diff options
Diffstat (limited to 'backends')
| -rw-r--r-- | backends/aiger/aiger.cc | 12 | ||||
| -rw-r--r-- | backends/aiger/xaiger.cc | 193 | ||||
| -rw-r--r-- | backends/btor/btor.cc | 14 | ||||
| -rw-r--r-- | backends/btor/test_cells.sh | 2 | ||||
| -rw-r--r-- | backends/cxxrtl/cxxrtl.cc | 139 | ||||
| -rw-r--r-- | backends/firrtl/firrtl.cc | 68 | ||||
| -rw-r--r-- | backends/smt2/smt2.cc | 10 | ||||
| -rw-r--r-- | backends/smt2/smtbmc.py | 11 | ||||
| -rw-r--r-- | backends/smt2/smtio.py | 37 | ||||
| -rw-r--r-- | backends/smv/smv.cc | 5 | ||||
| -rw-r--r-- | backends/smv/test_cells.sh | 4 | ||||
| -rw-r--r-- | backends/verilog/verilog_backend.cc | 89 | 
12 files changed, 379 insertions, 205 deletions
| diff --git a/backends/aiger/aiger.cc b/backends/aiger/aiger.cc index cac32a8da..e5a41b5c5 100644 --- a/backends/aiger/aiger.cc +++ b/backends/aiger/aiger.cc @@ -629,30 +629,30 @@ struct AigerWriter  				int a = aig_map.at(sig[i]);  				if (verbose_map) -					wire_lines[a] += stringf("wire %d %d %s\n", a, i, log_id(wire)); +					wire_lines[a] += stringf("wire %d %d %s\n", a, wire->start_offset+i, log_id(wire));  				if (wire->port_input) {  					log_assert((a & 1) == 0); -					input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, i, log_id(wire)); +					input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, wire->start_offset+i, log_id(wire));  				}  				if (wire->port_output) {  					int o = ordered_outputs.at(sig[i]); -					output_lines[o] += stringf("output %d %d %s\n", o, i, log_id(wire)); +					output_lines[o] += stringf("output %d %d %s\n", o, wire->start_offset+i, log_id(wire));  				}  				if (init_inputs.count(sig[i])) {  					int a = init_inputs.at(sig[i]);  					log_assert((a & 1) == 0); -					init_lines[a] += stringf("init %d %d %s\n", (a >> 1)-1, i, log_id(wire)); +					init_lines[a] += stringf("init %d %d %s\n", (a >> 1)-1, wire->start_offset+i, log_id(wire));  				}  				if (ordered_latches.count(sig[i])) {  					int l = ordered_latches.at(sig[i]);  					if (zinit_mode && (aig_latchinit.at(l) == 1)) -						latch_lines[l] += stringf("invlatch %d %d %s\n", l, i, log_id(wire)); +						latch_lines[l] += stringf("invlatch %d %d %s\n", l, wire->start_offset+i, log_id(wire));  					else -						latch_lines[l] += stringf("latch %d %d %s\n", l, i, log_id(wire)); +						latch_lines[l] += stringf("latch %d %d %s\n", l, wire->start_offset+i, log_id(wire));  				}  			}  		} diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc index 3c7c745fe..4a8f54c4d 100644 --- a/backends/aiger/xaiger.cc +++ b/backends/aiger/xaiger.cc @@ -76,9 +76,11 @@ void aiger_encode(std::ostream &f, int x)  struct XAigerWriter  { +	Design *design;  	Module *module;  	SigMap sigmap; +	dict<SigBit, State> init_map;  	pool<SigBit> input_bits, output_bits;  	dict<SigBit, SigBit> not_map, alias_map;  	dict<SigBit, pair<SigBit, SigBit>> and_map; @@ -137,7 +139,7 @@ struct XAigerWriter  		return a;  	} -	XAigerWriter(Module *module, bool holes_mode=false) : module(module), sigmap(module) +	XAigerWriter(Module *module, bool dff_mode) : design(module->design), module(module), sigmap(module)  	{  		pool<SigBit> undriven_bits;  		pool<SigBit> unused_bits; @@ -154,10 +156,11 @@ struct XAigerWriter  		// promote keep wires  		for (auto wire : module->wires()) -			if (wire->get_bool_attribute(ID::keep)) +			if (wire->get_bool_attribute(ID::keep) || wire->get_bool_attribute(ID::abc9_keep))  				sigmap.add(wire); -		for (auto wire : module->wires()) +		for (auto wire : module->wires()) { +			auto it = wire->attributes.find(ID::init);  			for (int i = 0; i < GetSize(wire); i++)  			{  				SigBit wirebit(wire, i); @@ -174,17 +177,27 @@ struct XAigerWriter  				undriven_bits.insert(bit);  				unused_bits.insert(bit); -				bool scc = wire->attributes.count(ID::abc9_scc); -				if (wire->port_input || scc) +				bool keep = wire->get_bool_attribute(ID::abc9_keep); +				if (wire->port_input || keep)  					input_bits.insert(bit); -				bool keep = wire->get_bool_attribute(ID::keep); -				if (wire->port_output || keep || scc) { +				keep = keep || wire->get_bool_attribute(ID::keep); +				if (wire->port_output || keep) {  					if (bit != wirebit)  						alias_map[wirebit] = bit;  					output_bits.insert(wirebit);  				} + +				if (it != wire->attributes.end()) { +					auto s = it->second[i]; +					if (s != State::Sx) { +						auto r = init_map.insert(std::make_pair(bit, it->second[i])); +						if (!r.second && r.first->second != it->second[i]) +							log_error("Bit '%s' has a conflicting (* init *) value.\n", log_signal(bit)); +					} +				}  			} +		}  		TimingInfo timing; @@ -212,10 +225,7 @@ struct XAigerWriter  					continue;  				} -				if (cell->type == ID($__ABC9_FF_) && -						// The presence of an abc9_mergeability attribute indicates -						//   that we do want to pass this flop to ABC -						cell->attributes.count(ID::abc9_mergeability)) +				if (dff_mode && cell->type.in(ID($_DFF_N_), ID($_DFF_P_)) && !cell->get_bool_attribute(ID::abc9_keep))  				{  					SigBit D = sigmap(cell->getPort(ID::D).as_bit());  					SigBit Q = sigmap(cell->getPort(ID::Q).as_bit()); @@ -231,31 +241,29 @@ struct XAigerWriter  					continue;  			} -			RTLIL::Module* inst_module = module->design->module(cell->type); -			if (inst_module) { -				IdString derived_type = inst_module->derive(module->design, cell->parameters); -				inst_module = module->design->module(derived_type); -				log_assert(inst_module); - +			RTLIL::Module* inst_module = design->module(cell->type); +			if (inst_module && inst_module->get_blackbox_attribute()) {  				bool abc9_flop = false; -				if (!cell->has_keep_attr()) { -					auto it = cell->attributes.find(ID::abc9_box_seq); -					if (it != cell->attributes.end()) { -						int abc9_box_seq = it->second.as_int(); -						if (GetSize(box_list) <= abc9_box_seq) -							box_list.resize(abc9_box_seq+1); -						box_list[abc9_box_seq] = cell; -						// Only flop boxes may have arrival times -						//   (all others are combinatorial) -						abc9_flop = inst_module->get_bool_attribute(ID::abc9_flop); -						if (!abc9_flop) -							continue; -					} + +				auto it = cell->attributes.find(ID::abc9_box_seq); +				if (it != cell->attributes.end()) { +					log_assert(!cell->has_keep_attr()); +					log_assert(cell->parameters.empty()); +					int abc9_box_seq = it->second.as_int(); +					if (GetSize(box_list) <= abc9_box_seq) +						box_list.resize(abc9_box_seq+1); +					box_list[abc9_box_seq] = cell; +					// Only flop boxes may have arrival times +					//   (all others are combinatorial) +					log_assert(cell->parameters.empty()); +					abc9_flop = inst_module->get_bool_attribute(ID::abc9_flop); +					if (!abc9_flop) +						continue;  				} -				if (!timing.count(derived_type)) +				if (!timing.count(inst_module->name))  					timing.setup_module(inst_module); -				auto &t = timing.at(derived_type).arrival; +				auto &t = timing.at(inst_module->name).arrival;  				for (const auto &conn : cell->connections()) {  					auto port_wire = inst_module->wire(conn.first);  					if (!port_wire->port_output) @@ -269,7 +277,7 @@ struct XAigerWriter  #ifndef NDEBUG  						if (ys_debug(1)) {  							static std::set<std::tuple<IdString,IdString,int>> seen; -							if (seen.emplace(derived_type, conn.first, i).second) log("%s.%s[%d] abc9_arrival = %d\n", +							if (seen.emplace(inst_module->name, conn.first, i).second) log("%s.%s[%d] abc9_arrival = %d\n",  									log_id(cell->type), log_id(conn.first), i, d);  						}  #endif @@ -280,10 +288,6 @@ struct XAigerWriter  				if (abc9_flop)  					continue;  			} -			else { -				if (cell->type == ID($__ABC9_DELAY)) -					log_error("Cell type '%s' not recognised. Check that '+/abc9_model.v' has been read.\n", cell->type.c_str()); -			}  			bool cell_known = inst_module || cell->known();  			for (const auto &c : cell->connections()) { @@ -317,9 +321,9 @@ struct XAigerWriter  		for (auto cell : box_list) {  			log_assert(cell); -			RTLIL::Module* box_module = module->design->module(cell->type); +			RTLIL::Module* box_module = design->module(cell->type);  			log_assert(box_module); -			log_assert(box_module->attributes.count(ID::abc9_box_id) || box_module->get_bool_attribute(ID::abc9_flop)); +			log_assert(box_module->has_attribute(ID::abc9_box_id));  			auto r = box_ports.insert(cell->type);  			if (r.second) { @@ -383,27 +387,6 @@ struct XAigerWriter  						undriven_bits.erase(O);  					}  			} - -			// Connect <cell>.abc9_ff.Q (inserted by abc9_map.v) as the last input to the flop box -			if (box_module->get_bool_attribute(ID::abc9_flop)) { -				SigSpec rhs = module->wire(stringf("%s.abc9_ff.Q", cell->name.c_str())); -				if (rhs.empty()) -					log_error("'%s.abc9_ff.Q' is not a wire present in module '%s'.\n", log_id(cell), log_id(module)); - -				for (auto b : rhs) { -					SigBit I = sigmap(b); -					if (b == RTLIL::Sx) -						b = State::S0; -					else if (I != b) { -						if (I == RTLIL::Sx) -							alias_map[b] = State::S0; -						else -							alias_map[b] = I; -					} -					co_bits.emplace_back(b); -					unused_bits.erase(I); -				} -			}  		}  		for (auto bit : input_bits) @@ -419,16 +402,14 @@ struct XAigerWriter  			undriven_bits.erase(bit);  		} -		if (holes_mode) { -			struct sort_by_port_id { -				bool operator()(const RTLIL::SigBit& a, const RTLIL::SigBit& b) const { -					return a.wire->port_id < b.wire->port_id || -					    (a.wire->port_id == b.wire->port_id && a.offset < b.offset); -				} -			}; -			input_bits.sort(sort_by_port_id()); -			output_bits.sort(sort_by_port_id()); -		} +		struct sort_by_port_id { +			bool operator()(const RTLIL::SigBit& a, const RTLIL::SigBit& b) const { +				return a.wire->port_id < b.wire->port_id || +				    (a.wire->port_id == b.wire->port_id && a.offset < b.offset); +			} +		}; +		input_bits.sort(sort_by_port_id()); +		output_bits.sort(sort_by_port_id());  		aig_map[State::S0] = 0;  		aig_map[State::S1] = 1; @@ -589,17 +570,14 @@ struct XAigerWriter  			int box_count = 0;  			for (auto cell : box_list) {  				log_assert(cell); +				log_assert(cell->parameters.empty()); -				RTLIL::Module* box_module = module->design->module(cell->type); -				log_assert(box_module); - -				IdString derived_type = box_module->derive(box_module->design, cell->parameters); -				box_module = box_module->design->module(derived_type); -				log_assert(box_module); - -				auto r = cell_cache.insert(derived_type); +				auto r = cell_cache.insert(cell->type);  				auto &v = r.first->second;  				if (r.second) { +					RTLIL::Module* box_module = design->module(cell->type); +					log_assert(box_module); +  					int box_inputs = 0, box_outputs = 0;  					for (auto port_name : box_module->ports) {  						RTLIL::Wire *w = box_module->wire(port_name); @@ -610,11 +588,6 @@ struct XAigerWriter  							box_outputs += GetSize(w);  					} -					// For flops only, create an extra 1-bit input that drives a new wire -					//   called "<cell>.abc9_ff.Q" that is used below -					if (box_module->get_bool_attribute(ID::abc9_flop)) -						box_inputs++; -  					std::get<0>(v) = box_inputs;  					std::get<1>(v) = box_outputs;  					std::get<2>(v) = box_module->attributes.at(ID::abc9_box_id).as_int(); @@ -635,23 +608,27 @@ struct XAigerWriter  			auto write_s_buffer = std::bind(write_buffer, std::ref(s_buffer), std::placeholders::_1);  			write_s_buffer(ff_bits.size()); +			dict<SigSpec, int> clk_to_mergeability;  			for (const auto &i : ff_bits) {  				const SigBit &d = i.first;  				const Cell *cell = i.second; -				int mergeability = cell->attributes.at(ID::abc9_mergeability).as_int(); +				SigSpec clk_and_pol{sigmap(cell->getPort(ID::C)), cell->type[6] == 'P' ? State::S1 : State::S0}; +				auto r = clk_to_mergeability.insert(std::make_pair(clk_and_pol, clk_to_mergeability.size()+1)); +				int mergeability = r.first->second;  				log_assert(mergeability > 0);  				write_r_buffer(mergeability); -				Const init = cell->attributes.at(ID::abc9_init, State::Sx); -				log_assert(GetSize(init) == 1); +				SigBit Q = sigmap(cell->getPort(ID::Q)); +				State init = init_map.at(Q, State::Sx); +				log_debug("Cell '%s' (type %s) has (* init *) value '%s'.\n", log_id(cell), log_id(cell->type), log_signal(init));  				if (init == State::S1)  					write_s_buffer(1);  				else if (init == State::S0)  					write_s_buffer(0);  				else {  					log_assert(init == State::Sx); -					write_s_buffer(0); +					write_s_buffer(2);  				}  				// Use arrival time from output of flop box @@ -671,10 +648,16 @@ struct XAigerWriter  			f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));  			f.write(buffer_str.data(), buffer_str.size()); -			RTLIL::Module *holes_module = module->design->module(stringf("%s$holes", module->name.c_str())); +			RTLIL::Design *holes_design; +			auto it = saved_designs.find("$abc9_holes"); +			if (it != saved_designs.end()) +				holes_design = it->second; +			else +				holes_design = nullptr; +			RTLIL::Module *holes_module = holes_design ? holes_design->module(module->name) : nullptr;  			if (holes_module) {  				std::stringstream a_buffer; -				XAigerWriter writer(holes_module, true /* holes_mode */); +				XAigerWriter writer(holes_module, false /* dff_mode */);  				writer.write_aiger(a_buffer, false /*ascii_mode*/);  				f << "a"; @@ -704,10 +687,10 @@ struct XAigerWriter  		f << stringf("Generated by %s\n", yosys_version_str); -		module->design->scratchpad_set_int("write_xaiger.num_ands", and_map.size()); -		module->design->scratchpad_set_int("write_xaiger.num_wires", aig_map.size()); -		module->design->scratchpad_set_int("write_xaiger.num_inputs", input_bits.size()); -		module->design->scratchpad_set_int("write_xaiger.num_outputs", output_bits.size()); +		design->scratchpad_set_int("write_xaiger.num_ands", and_map.size()); +		design->scratchpad_set_int("write_xaiger.num_wires", aig_map.size()); +		design->scratchpad_set_int("write_xaiger.num_inputs", input_bits.size()); +		design->scratchpad_set_int("write_xaiger.num_outputs", output_bits.size());  	}  	void write_map(std::ostream &f) @@ -725,13 +708,12 @@ struct XAigerWriter  				if (input_bits.count(b)) {  					int a = aig_map.at(b);  					log_assert((a & 1) == 0); -					input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, i, log_id(wire)); +					input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, wire->start_offset+i, log_id(wire));  				}  				if (output_bits.count(b)) {  					int o = ordered_outputs.at(b); -					int init = 2; -					output_lines[o] += stringf("output %d %d %s %d\n", o - GetSize(co_bits), i, log_id(wire), init); +					output_lines[o] += stringf("output %d %d %s\n", o - GetSize(co_bits), wire->start_offset+i, log_id(wire));  					continue;  				}  			} @@ -762,10 +744,10 @@ struct XAigerBackend : public Backend {  		log("    write_xaiger [options] [filename]\n");  		log("\n");  		log("Write the top module (according to the (* top *) attribute or if only one module\n"); -		log("is currently selected) to an XAIGER file. Any non $_NOT_, $_AND_, $_ABC9_FF_, or"); -		log("non (* abc9_box_id *) cells will be converted into psuedo-inputs and\n"); -		log("pseudo-outputs. Whitebox contents will be taken from the '<module-name>$holes'\n"); -		log("module, if it exists.\n"); +		log("is currently selected) to an XAIGER file. Any non $_NOT_, $_AND_, (optionally\n"); +		log("$_DFF_N_, $_DFF_P_), or non (* abc9_box *) cells will be converted into psuedo-\n"); +		log("inputs and pseudo-outputs. Whitebox contents will be taken from the equivalent\n"); +		log("module in the '$abc9_holes' design, if it exists.\n");  		log("\n");  		log("    -ascii\n");  		log("        write ASCII version of AIGER format\n"); @@ -773,10 +755,13 @@ struct XAigerBackend : public Backend {  		log("    -map <filename>\n");  		log("        write an extra file with port and box symbols\n");  		log("\n"); +		log("    -dff\n"); +		log("        write $_DFF_[NP]_ cells\n"); +		log("\n");  	}  	void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE  	{ -		bool ascii_mode = false; +		bool ascii_mode = false, dff_mode = false;  		std::string map_filename;  		log_header(design, "Executing XAIGER backend.\n"); @@ -792,6 +777,10 @@ struct XAigerBackend : public Backend {  				map_filename = args[++argidx];  				continue;  			} +			if (args[argidx] == "-dff") { +				dff_mode = true; +				continue; +			}  			break;  		}  		extra_args(f, filename, args, argidx, !ascii_mode); @@ -809,7 +798,7 @@ struct XAigerBackend : public Backend {  		if (!top_module->memories.empty())  			log_error("Found unmapped memories in module %s: unmapped memories are not supported in XAIGER backend!\n", log_id(top_module)); -		XAigerWriter writer(top_module); +		XAigerWriter writer(top_module, dff_mode);  		writer.write_aiger(*f, ascii_mode);  		if (!map_filename.empty()) { diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc index 14c8484e8..2816d3246 100644 --- a/backends/btor/btor.cc +++ b/backends/btor/btor.cc @@ -266,20 +266,26 @@ struct BtorWorker  			goto okay;  		} -		if (cell->type.in(ID($div), ID($mod))) +		if (cell->type.in(ID($div), ID($mod), ID($modfloor)))  		{ +			bool a_signed = cell->hasParam(ID::A_SIGNED) ? cell->getParam(ID::A_SIGNED).as_bool() : false; +			bool b_signed = cell->hasParam(ID::B_SIGNED) ? cell->getParam(ID::B_SIGNED).as_bool() : false; +  			string btor_op;  			if (cell->type == ID($div)) btor_op = "div"; +			// "rem" = truncating modulo  			if (cell->type == ID($mod)) btor_op = "rem"; +			// "mod" = flooring modulo +			if (cell->type == ID($modfloor)) { +				// "umod" doesn't exist because it's the same as "urem" +				btor_op = a_signed || b_signed ? "mod" : "rem"; +			}  			log_assert(!btor_op.empty());  			int width = GetSize(cell->getPort(ID::Y));  			width = std::max(width, GetSize(cell->getPort(ID::A)));  			width = std::max(width, GetSize(cell->getPort(ID::B))); -			bool a_signed = cell->hasParam(ID::A_SIGNED) ? cell->getParam(ID::A_SIGNED).as_bool() : false; -			bool b_signed = cell->hasParam(ID::B_SIGNED) ? cell->getParam(ID::B_SIGNED).as_bool() : false; -  			int nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed);  			int nid_b = get_sig_nid(cell->getPort(ID::B), width, b_signed); diff --git a/backends/btor/test_cells.sh b/backends/btor/test_cells.sh index e0f1a0514..3f077201a 100644 --- a/backends/btor/test_cells.sh +++ b/backends/btor/test_cells.sh @@ -6,7 +6,7 @@ rm -rf test_cells.tmp  mkdir -p test_cells.tmp  cd test_cells.tmp -../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$sop /$macc /$mul /$div /$mod' +../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$sop /$macc /$mul /$div /$mod /$divfloor /$modfloor'  for fn in test_*.il; do  	../../../yosys -p " diff --git a/backends/cxxrtl/cxxrtl.cc b/backends/cxxrtl/cxxrtl.cc index 237700b29..0cceecbba 100644 --- a/backends/cxxrtl/cxxrtl.cc +++ b/backends/cxxrtl/cxxrtl.cc @@ -212,14 +212,14 @@ bool is_ff_cell(RTLIL::IdString type)  bool is_internal_cell(RTLIL::IdString type)  { -	return type[0] == '$' && !type.begins_with("$paramod\\"); +	return type[0] == '$' && !type.begins_with("$paramod");  }  bool is_cxxrtl_blackbox_cell(const RTLIL::Cell *cell)  {  	RTLIL::Module *cell_module = cell->module->design->module(cell->type);  	log_assert(cell_module != nullptr); -	return cell_module->get_bool_attribute(ID(cxxrtl.blackbox)); +	return cell_module->get_bool_attribute(ID(cxxrtl_blackbox));  }  enum class CxxrtlPortType { @@ -231,14 +231,14 @@ enum class CxxrtlPortType {  CxxrtlPortType cxxrtl_port_type(const RTLIL::Cell *cell, RTLIL::IdString port)  {  	RTLIL::Module *cell_module = cell->module->design->module(cell->type); -	if (cell_module == nullptr || !cell_module->get_bool_attribute(ID(cxxrtl.blackbox))) +	if (cell_module == nullptr || !cell_module->get_bool_attribute(ID(cxxrtl_blackbox)))  		return CxxrtlPortType::UNKNOWN;  	RTLIL::Wire *cell_output_wire = cell_module->wire(port);  	log_assert(cell_output_wire != nullptr); -	bool is_comb = cell_output_wire->get_bool_attribute(ID(cxxrtl.comb)); -	bool is_sync = cell_output_wire->get_bool_attribute(ID(cxxrtl.sync)); +	bool is_comb = cell_output_wire->get_bool_attribute(ID(cxxrtl_comb)); +	bool is_sync = cell_output_wire->get_bool_attribute(ID(cxxrtl_sync));  	if (is_comb && is_sync) -		log_cmd_error("Port `%s.%s' is marked as both `cxxrtl.comb` and `cxxrtl.sync`.\n", +		log_cmd_error("Port `%s.%s' is marked as both `cxxrtl_comb` and `cxxrtl_sync`.\n",  		              log_id(cell_module), log_signal(cell_output_wire));  	else if (is_comb)  		return CxxrtlPortType::COMB; @@ -513,7 +513,6 @@ struct CxxrtlWorker {  	bool elide_public = false;  	bool localize_internal = false;  	bool localize_public = false; -	bool run_opt_clean_purge = false;  	bool run_proc_flatten = false;  	bool max_opt_level = false; @@ -606,7 +605,7 @@ struct CxxrtlWorker {  	std::string mangle(const RTLIL::Module *module)  	{ -		return mangle_module_name(module->name, /*is_blackbox=*/module->get_bool_attribute(ID(cxxrtl.blackbox))); +		return mangle_module_name(module->name, /*is_blackbox=*/module->get_bool_attribute(ID(cxxrtl_blackbox)));  	}  	std::string mangle(const RTLIL::Memory *memory) @@ -634,19 +633,19 @@ struct CxxrtlWorker {  	std::vector<std::string> template_param_names(const RTLIL::Module *module)  	{ -		if (!module->has_attribute(ID(cxxrtl.template))) +		if (!module->has_attribute(ID(cxxrtl_template)))  			return {}; -		if (module->attributes.at(ID(cxxrtl.template)).flags != RTLIL::CONST_FLAG_STRING) -			log_cmd_error("Attribute `cxxrtl.template' of module `%s' is not a string.\n", log_id(module)); +		if (module->attributes.at(ID(cxxrtl_template)).flags != RTLIL::CONST_FLAG_STRING) +			log_cmd_error("Attribute `cxxrtl_template' of module `%s' is not a string.\n", log_id(module)); -		std::vector<std::string> param_names = split_by(module->get_string_attribute(ID(cxxrtl.template)), " \t"); +		std::vector<std::string> param_names = split_by(module->get_string_attribute(ID(cxxrtl_template)), " \t");  		for (const auto ¶m_name : param_names) {  			// Various lowercase prefixes (p_, i_, cell_, ...) are used for member variables, so require  			// parameters to start with an uppercase letter to avoid name conflicts. (This is the convention  			// in both Verilog and C++, anyway.)  			if (!isupper(param_name[0])) -				log_cmd_error("Attribute `cxxrtl.template' of module `%s' includes a parameter `%s', " +				log_cmd_error("Attribute `cxxrtl_template' of module `%s' includes a parameter `%s', "  				              "which does not start with an uppercase letter.\n",  				              log_id(module), param_name.c_str());  		} @@ -677,7 +676,7 @@ struct CxxrtlWorker {  	{  		RTLIL::Module *cell_module = cell->module->design->module(cell->type);  		log_assert(cell_module != nullptr); -		if (!cell_module->get_bool_attribute(ID(cxxrtl.blackbox))) +		if (!cell_module->get_bool_attribute(ID(cxxrtl_blackbox)))  			return "";  		std::vector<std::string> param_names = template_param_names(cell_module); @@ -726,12 +725,13 @@ struct CxxrtlWorker {  	void dump_const_init(const RTLIL::Const &data, int width, int offset = 0, bool fixed_width = false)  	{ +		const int CHUNK_SIZE = 32;  		f << "{";  		while (width > 0) { -			const int CHUNK_SIZE = 32; -			uint32_t chunk = data.extract(offset, width > CHUNK_SIZE ? CHUNK_SIZE : width).as_int(); +			int chunk_width = min(width, CHUNK_SIZE); +			uint32_t chunk = data.extract(offset, chunk_width).as_int();  			if (fixed_width) -				f << stringf("0x%08xu", chunk); +				f << stringf("0x%.*xu", (3 + chunk_width) / 4, chunk);  			else  				f << stringf("%#xu", chunk);  			if (width > CHUNK_SIZE) @@ -1418,8 +1418,8 @@ struct CxxrtlWorker {  			f << indent << "value<" << wire->width << "> " << mangle(wire) << ";\n";  		} else {  			std::string width; -			if (wire->module->has_attribute(ID(cxxrtl.blackbox)) && wire->has_attribute(ID(cxxrtl.width))) { -				width = wire->get_string_attribute(ID(cxxrtl.width)); +			if (wire->module->has_attribute(ID(cxxrtl_blackbox)) && wire->has_attribute(ID(cxxrtl_width))) { +				width = wire->get_string_attribute(ID(cxxrtl_width));  			} else {  				width = std::to_string(wire->width);  			} @@ -1521,7 +1521,7 @@ struct CxxrtlWorker {  	{  		inc_indent();  			f << indent << "bool converged = " << (eval_converges.at(module) ? "true" : "false") << ";\n"; -			if (!module->get_bool_attribute(ID(cxxrtl.blackbox))) { +			if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {  				for (auto wire : module->wires()) {  					if (edge_wires[wire]) {  						for (auto edge_type : edge_types) { @@ -1573,10 +1573,10 @@ struct CxxrtlWorker {  						f << indent << "prev_" << mangle(wire) << " = " << mangle(wire) << ";\n";  					continue;  				} -				if (!module->get_bool_attribute(ID(cxxrtl.blackbox)) || wire->port_id != 0) +				if (!module->get_bool_attribute(ID(cxxrtl_blackbox)) || wire->port_id != 0)  					f << indent << "changed |= " << mangle(wire) << ".commit();\n";  			} -			if (!module->get_bool_attribute(ID(cxxrtl.blackbox))) { +			if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {  				for (auto memory : module->memories) {  					if (!writable_memories[memory.second])  						continue; @@ -1623,8 +1623,8 @@ struct CxxrtlWorker {  	void dump_module_intf(RTLIL::Module *module)  	{  		dump_attrs(module); -		if (module->get_bool_attribute(ID(cxxrtl.blackbox))) { -			if (module->has_attribute(ID(cxxrtl.template))) +		if (module->get_bool_attribute(ID(cxxrtl_blackbox))) { +			if (module->has_attribute(ID(cxxrtl_template)))  				f << indent << "template" << template_params(module, /*is_decl=*/true) << "\n";  			f << indent << "struct " << mangle(module) << " : public module {\n";  			inc_indent(); @@ -1686,7 +1686,7 @@ struct CxxrtlWorker {  					dump_attrs(cell);  					RTLIL::Module *cell_module = module->design->module(cell->type);  					log_assert(cell_module != nullptr); -					if (cell_module->get_bool_attribute(ID(cxxrtl.blackbox))) { +					if (cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) {  						f << indent << "std::unique_ptr<" << mangle(cell_module) << template_args(cell) << "> ";  						f << mangle(cell) << " = " << mangle(cell_module) << template_args(cell);  						f << "::create(" << escape_cxx_string(cell->name.str()) << ", "; @@ -1711,7 +1711,7 @@ struct CxxrtlWorker {  	void dump_module_impl(RTLIL::Module *module)  	{ -		if (module->get_bool_attribute(ID(cxxrtl.blackbox))) +		if (module->get_bool_attribute(ID(cxxrtl_blackbox)))  			return;  		f << indent << "bool " << mangle(module) << "::eval() {\n";  		dump_eval_method(module); @@ -1730,9 +1730,9 @@ struct CxxrtlWorker {  		for (auto module : design->modules()) {  			if (!design->selected_module(module))  				continue; -			if (module->get_bool_attribute(ID(cxxrtl.blackbox))) +			if (module->get_bool_attribute(ID(cxxrtl_blackbox)))  				modules.push_back(module); // cxxrtl blackboxes first -			if (module->get_blackbox_attribute() || module->get_bool_attribute(ID(cxxrtl.blackbox))) +			if (module->get_blackbox_attribute() || module->get_bool_attribute(ID(cxxrtl_blackbox)))  				continue;  			topo_design.node(module); @@ -1821,16 +1821,16 @@ struct CxxrtlWorker {  			SigMap &sigmap = sigmaps[module];  			sigmap.set(module); -			if (module->get_bool_attribute(ID(cxxrtl.blackbox))) { +			if (module->get_bool_attribute(ID(cxxrtl_blackbox))) {  				for (auto port : module->ports) {  					RTLIL::Wire *wire = module->wire(port); -					if (wire->has_attribute(ID(cxxrtl.edge))) { -						RTLIL::Const edge_attr = wire->attributes[ID(cxxrtl.edge)]; +					if (wire->has_attribute(ID(cxxrtl_edge))) { +						RTLIL::Const edge_attr = wire->attributes[ID(cxxrtl_edge)];  						if (!(edge_attr.flags & RTLIL::CONST_FLAG_STRING) || (int)edge_attr.decode_string().size() != GetSize(wire)) -							log_cmd_error("Attribute `cxxrtl.edge' of port `%s.%s' is not a string with one character per bit.\n", +							log_cmd_error("Attribute `cxxrtl_edge' of port `%s.%s' is not a string with one character per bit.\n",  							              log_id(module), log_signal(wire)); -						std::string edges = wire->get_string_attribute(ID(cxxrtl.edge)); +						std::string edges = wire->get_string_attribute(ID(cxxrtl_edge));  						for (int i = 0; i < GetSize(wire); i++) {  							RTLIL::SigSpec wire_sig = wire;  							switch (edges[i]) { @@ -1839,7 +1839,7 @@ struct CxxrtlWorker {  								case 'n': register_edge_signal(sigmap, wire_sig[i], RTLIL::STn); break;  								case 'a': register_edge_signal(sigmap, wire_sig[i], RTLIL::STe); break;  								default: -									log_cmd_error("Attribute `cxxrtl.edge' of port `%s.%s' contains specifiers " +									log_cmd_error("Attribute `cxxrtl_edge' of port `%s.%s' contains specifiers "  									              "other than '-', 'p', 'n', or 'a'.\n",  										log_id(module), log_signal(wire));  							} @@ -1868,12 +1868,12 @@ struct CxxrtlWorker {  				RTLIL::Module *cell_module = design->module(cell->type);  				if (cell_module &&  				    cell_module->get_blackbox_attribute() && -				    !cell_module->get_bool_attribute(ID(cxxrtl.blackbox))) +				    !cell_module->get_bool_attribute(ID(cxxrtl_blackbox)))  					log_cmd_error("External blackbox cell `%s' is not marked as a CXXRTL blackbox.\n", log_id(cell->type));  				if (cell_module && -				    cell_module->get_bool_attribute(ID(cxxrtl.blackbox)) && -				    cell_module->get_bool_attribute(ID(cxxrtl.template))) +				    cell_module->get_bool_attribute(ID(cxxrtl_blackbox)) && +				    cell_module->get_bool_attribute(ID(cxxrtl_template)))  					blackbox_specializations[cell_module].insert(template_args(cell));  				FlowGraph::Node *node = flow.add_node(cell); @@ -1942,13 +1942,13 @@ struct CxxrtlWorker {  						case RTLIL::STa:  							break; +						case RTLIL::STg: +							log_cmd_error("Global clock is not supported.\n"); +  						// Handling of init-type sync rules is delegated to the `proc_init` pass, so we can use the wire  						// attribute regardless of input.  						case RTLIL::STi:  							log_assert(false); - -						case RTLIL::STg: -							log_cmd_error("Global clock is not supported.\n");  					}  			} @@ -2008,6 +2008,7 @@ struct CxxrtlWorker {  				log("Module `%s' contains feedback arcs through wires:\n", log_id(module));  				for (auto wire : feedback_wires)  					log("  %s\n", log_id(wire)); +				log("\n");  			}  			for (auto wire : module->wires()) { @@ -2039,20 +2040,20 @@ struct CxxrtlWorker {  				log("Module `%s' contains buffered combinatorial wires:\n", log_id(module));  				for (auto wire : buffered_wires)  					log("  %s\n", log_id(wire)); +				log("\n");  			}  			eval_converges[module] = feedback_wires.empty() && buffered_wires.empty();  		}  		if (has_feedback_arcs || has_buffered_wires) {  			// Although both non-feedback buffered combinatorial wires and apparent feedback wires may be eliminated -			// by optimizing the design, if after `opt_clean -purge` there are any feedback wires remaining, it is very +			// by optimizing the design, if after `proc; flatten` there are any feedback wires remaining, it is very  			// likely that these feedback wires are indicative of a true logic loop, so they get emphasized in the message.  			const char *why_pessimistic = nullptr;  			if (has_feedback_arcs)  				why_pessimistic = "feedback wires";  			else if (has_buffered_wires)  				why_pessimistic = "buffered combinatorial wires"; -			log("\n");  			log_warning("Design contains %s, which require delta cycles during evaluation.\n", why_pessimistic);  			if (!max_opt_level)  				log("Increasing the optimization level may eliminate %s from the design.\n", why_pessimistic); @@ -2064,7 +2065,7 @@ struct CxxrtlWorker {  		has_sync_init = has_packed_mem = false;  		for (auto module : design->modules()) { -			if (module->get_blackbox_attribute() && !module->has_attribute(ID(cxxrtl.blackbox))) +			if (module->get_blackbox_attribute() && !module->has_attribute(ID(cxxrtl_blackbox)))  				continue;  			if (!design->selected_whole_module(module)) @@ -2086,34 +2087,39 @@ struct CxxrtlWorker {  	void prepare_design(RTLIL::Design *design)  	{ +		bool did_anything = false;  		bool has_sync_init, has_packed_mem;  		log_push();  		check_design(design, has_sync_init, has_packed_mem);  		if (run_proc_flatten) {  			Pass::call(design, "proc");  			Pass::call(design, "flatten"); +			did_anything = true;  		} else if (has_sync_init) {  			// We're only interested in proc_init, but it depends on proc_prune and proc_clean, so call those  			// in case they weren't already. (This allows `yosys foo.v -o foo.cc` to work.)  			Pass::call(design, "proc_prune");  			Pass::call(design, "proc_clean");  			Pass::call(design, "proc_init"); +			did_anything = true;  		} -		if (has_packed_mem) +		if (has_packed_mem) {  			Pass::call(design, "memory_unpack"); +			did_anything = true; +		}  		// Recheck the design if it was modified.  		if (has_sync_init || has_packed_mem)  			check_design(design, has_sync_init, has_packed_mem);  		log_assert(!(has_sync_init || has_packed_mem)); -		if (run_opt_clean_purge) -			Pass::call(design, "opt_clean -purge");  		log_pop(); +		if (did_anything) +			log_spacer();  		analyze_design(design);  	}  };  struct CxxrtlBackend : public Backend { -	static const int DEFAULT_OPT_LEVEL = 6; +	static const int DEFAULT_OPT_LEVEL = 5;  	CxxrtlBackend() : Backend("cxxrtl", "convert design to C++ RTL simulation") { }  	void help() YS_OVERRIDE @@ -2155,12 +2161,12 @@ struct CxxrtlBackend : public Backend {  		log("For example, the following Verilog code defines a CXXRTL black box interface for\n");  		log("a synchronous debug sink:\n");  		log("\n"); -		log("    (* cxxrtl.blackbox *)\n"); +		log("    (* cxxrtl_blackbox *)\n");  		log("    module debug(...);\n"); -		log("      (* cxxrtl.edge = \"p\" *) input clk;\n"); +		log("      (* cxxrtl_edge = \"p\" *) input clk;\n");  		log("      input en;\n");  		log("      input [7:0] i_data;\n"); -		log("      (* cxxrtl.sync *) output [7:0] o_data;\n"); +		log("      (* cxxrtl_sync *) output [7:0] o_data;\n");  		log("    endmodule\n");  		log("\n");  		log("For this HDL interface, this backend will generate the following C++ interface:\n"); @@ -2205,13 +2211,13 @@ struct CxxrtlBackend : public Backend {  		log("port widths. For example, the following Verilog code defines a CXXRTL black box\n");  		log("interface for a configurable width debug sink:\n");  		log("\n"); -		log("    (* cxxrtl.blackbox, cxxrtl.template = \"WIDTH\" *)\n"); +		log("    (* cxxrtl_blackbox, cxxrtl_template = \"WIDTH\" *)\n");  		log("    module debug(...);\n");  		log("      parameter WIDTH = 8;\n"); -		log("      (* cxxrtl.edge = \"p\" *) input clk;\n"); +		log("      (* cxxrtl_edge = \"p\" *) input clk;\n");  		log("      input en;\n"); -		log("      (* cxxrtl.width = \"WIDTH\" *) input [WIDTH - 1:0] i_data;\n"); -		log("      (* cxxrtl.width = \"WIDTH\" *) output [WIDTH - 1:0] o_data;\n"); +		log("      (* cxxrtl_width = \"WIDTH\" *) input [WIDTH - 1:0] i_data;\n"); +		log("      (* cxxrtl_width = \"WIDTH\" *) output [WIDTH - 1:0] o_data;\n");  		log("    endmodule\n");  		log("\n");  		log("For this parametric HDL interface, this backend will generate the following C++\n"); @@ -2245,27 +2251,27 @@ struct CxxrtlBackend : public Backend {  		log("\n");  		log("The following attributes are recognized by this backend:\n");  		log("\n"); -		log("    cxxrtl.blackbox\n"); +		log("    cxxrtl_blackbox\n");  		log("        only valid on modules. if specified, the module contents are ignored,\n");  		log("        and the generated code includes only the module interface and a factory\n");  		log("        function, which will be called to instantiate the module.\n");  		log("\n"); -		log("    cxxrtl.edge\n"); +		log("    cxxrtl_edge\n");  		log("        only valid on inputs of black boxes. must be one of \"p\", \"n\", \"a\".\n");  		log("        if specified on signal `clk`, the generated code includes edge detectors\n");  		log("        `posedge_p_clk()` (if \"p\"), `negedge_p_clk()` (if \"n\"), or both (if\n");  		log("        \"a\"), simplifying implementation of clocked black boxes.\n");  		log("\n"); -		log("    cxxrtl.template\n"); +		log("    cxxrtl_template\n");  		log("        only valid on black boxes. must contain a space separated sequence of\n");  		log("        identifiers that have a corresponding black box parameters. for each\n");  		log("        of them, the generated code includes a `size_t` template parameter.\n");  		log("\n"); -		log("    cxxrtl.width\n"); +		log("    cxxrtl_width\n");  		log("        only valid on ports of black boxes. must be a constant expression, which\n");  		log("        is directly inserted into generated code.\n");  		log("\n"); -		log("    cxxrtl.comb, cxxrtl.sync\n"); +		log("    cxxrtl_comb, cxxrtl_sync\n");  		log("        only valid on outputs of black boxes. if specified, indicates that every\n");  		log("        bit of the output port is driven, correspondingly, by combinatorial or\n");  		log("        synchronous logic. this knowledge is used for scheduling optimizations.\n"); @@ -2305,10 +2311,7 @@ struct CxxrtlBackend : public Backend {  		log("        like -O3, and localize public wires not marked (*keep*) if possible.\n");  		log("\n");  		log("    -O5\n"); -		log("        like -O4, and run `opt_clean -purge` first.\n"); -		log("\n"); -		log("    -O6\n"); -		log("        like -O5, and run `proc; flatten` first.\n"); +		log("        like -O4, and run `proc; flatten` first.\n");  		log("\n");  	}  	void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE @@ -2342,19 +2345,23 @@ struct CxxrtlBackend : public Backend {  		extra_args(f, filename, args, argidx);  		switch (opt_level) { -			case 6: +			// the highest level here must match DEFAULT_OPT_LEVEL +			case 5:  				worker.max_opt_level = true;  				worker.run_proc_flatten = true; -			case 5: -				worker.run_opt_clean_purge = true; +				YS_FALLTHROUGH  			case 4:  				worker.localize_public = true; +				YS_FALLTHROUGH  			case 3:  				worker.elide_public = true; +				YS_FALLTHROUGH  			case 2:  				worker.localize_internal = true; +				YS_FALLTHROUGH  			case 1:  				worker.elide_internal = true; +				YS_FALLTHROUGH  			case 0:  				break;  			default: diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc index 40d05a036..b1d8500bb 100644 --- a/backends/firrtl/firrtl.cc +++ b/backends/firrtl/firrtl.cc @@ -343,7 +343,7 @@ struct FirrtlWorker  				switch (dir) {  					case FD_INOUT:  						log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", cell_type.c_str(), log_signal(it->second)); -						/* FALLTHRU */ +						YS_FALLTHROUGH  					case FD_OUT:  						sourceExpr = firstName;  						sinkExpr = secondExpr; @@ -351,7 +351,7 @@ struct FirrtlWorker  						break;  					case FD_NODIRECTION:  						log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", cell_type.c_str(), log_signal(it->second)); -						/* FALLTHRU */ +						YS_FALLTHROUGH  					case FD_IN:  						sourceExpr = secondExpr;  						sinkExpr = firstName; @@ -392,7 +392,34 @@ struct FirrtlWorker  		return result;  	} -	void run() +	void emit_extmodule() +	{ +		std::string moduleFileinfo = getFileinfo(module); +		f << stringf("  extmodule %s: %s\n", make_id(module->name), moduleFileinfo.c_str()); +		vector<std::string> port_decls; + +		for (auto wire : module->wires()) +		{ +			const auto wireName = make_id(wire->name); +			std::string wireFileinfo = getFileinfo(wire); + +			if (wire->port_input && wire->port_output) +			{ +				log_error("Module port %s.%s is inout!\n", log_id(module), log_id(wire)); +			} +			port_decls.push_back(stringf("    %s %s: UInt<%d> %s\n", wire->port_input ? "input" : "output", +					wireName, wire->width, wireFileinfo.c_str())); +		} + +		for (auto &str : port_decls) +		{ +			f << str; +		} + +		f << stringf("\n"); +	} + +	void emit_module()  	{  		std::string moduleFileinfo = getFileinfo(module);  		f << stringf("  module %s: %s\n", make_id(module->name), moduleFileinfo.c_str()); @@ -446,7 +473,7 @@ struct FirrtlWorker  			string y_id = make_id(cell->name);  			std::string cellFileinfo = getFileinfo(cell); -			if (cell->type.in(ID($not), ID($logic_not), ID($neg), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_bool), ID($reduce_xnor))) +			if (cell->type.in(ID($not), ID($logic_not), ID($_NOT_), ID($neg), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_bool), ID($reduce_xnor)))  			{  				string a_expr = make_expr(cell->getPort(ID::A));  				wire_decls.push_back(stringf("    wire %s: UInt<%d> %s\n", y_id.c_str(), y_width, cellFileinfo.c_str())); @@ -462,7 +489,7 @@ struct FirrtlWorker  				// Assume the FIRRTL width is a single bit.  				firrtl_width = 1; -				if (cell->type == ID($not)) primop = "not"; +				if (cell->type.in(ID($not), ID($_NOT_))) primop = "not";  				else if (cell->type == ID($neg)) {  					primop = "neg";  					firrtl_is_signed = true;	// Result of "neg" is signed (an SInt). @@ -494,7 +521,7 @@ struct FirrtlWorker  				continue;  			} -			if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($xor), ID($xnor), ID($and), ID($or), ID($eq), ID($eqx), +			if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($xor), ID($_XOR_), ID($xnor), ID($and), ID($_AND_), ID($or), ID($_OR_), ID($eq), ID($eqx),                                          ID($gt), ID($ge), ID($lt), ID($le), ID($ne), ID($nex), ID($shr), ID($sshr), ID($sshl), ID($shl),                                          ID($logic_and), ID($logic_or), ID($pow)))  			{ @@ -524,7 +551,7 @@ struct FirrtlWorker  				// For the arithmetic ops, expand operand widths to result widths befor performing the operation.  				// This corresponds (according to iverilog) to what verilog compilers implement. -				if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($xor), ID($xnor), ID($and), ID($or))) +				if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($xor), ID($_XOR_), ID($xnor), ID($and), ID($_AND_), ID($or), ID($_OR_)))  				{  					if (a_width < y_width) {  						a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); @@ -558,19 +585,20 @@ struct FirrtlWorker  					firrtl_is_signed = a_signed | b_signed;  					firrtl_width = a_width;  				} else if (cell->type == ID($mod)) { +					// "rem" = truncating modulo  					primop = "rem";  					firrtl_width = min(a_width, b_width); -				} else if (cell->type == ID($and)) { +				} else if (cell->type.in(ID($and), ID($_AND_))) {  					primop = "and";  					always_uint = true;  					firrtl_width = max(a_width, b_width);  				} -				else if (cell->type == ID($or) ) { +				else if (cell->type.in(ID($or), ID($_OR_))) {  					primop =  "or";  					always_uint = true;  					firrtl_width = max(a_width, b_width);  				} -				else if (cell->type == ID($xor)) { +				else if (cell->type.in(ID($xor), ID($_XOR_))) {  					primop = "xor";  					always_uint = true;  					firrtl_width = max(a_width, b_width); @@ -694,7 +722,8 @@ struct FirrtlWorker  					}  				} -				if (!cell->parameters.at(ID::B_SIGNED).as_bool()) { +				auto it = cell->parameters.find(ID::B_SIGNED); +				if (it == cell->parameters.end() || !it->second.as_bool()) {  					b_expr = "asUInt(" + b_expr + ")";  				} @@ -723,9 +752,10 @@ struct FirrtlWorker  				continue;  			} -			if (cell->type.in(ID($mux))) +			if (cell->type.in(ID($mux), ID($_MUX_)))  			{ -				int width = cell->parameters.at(ID::WIDTH).as_int(); +				auto it = cell->parameters.find(ID::WIDTH); +				int width = it == cell->parameters.end()? 1 : it->second.as_int();  				string a_expr = make_expr(cell->getPort(ID::A));  				string b_expr = make_expr(cell->getPort(ID::B));  				string s_expr = make_expr(cell->getPort(ID::S)); @@ -1076,6 +1106,18 @@ struct FirrtlWorker  		for (auto str : wire_exprs)  			f << str; + +		f << stringf("\n"); +	} + +	void run() +	{ +		// Blackboxes should be emitted as `extmodule`s in firrtl. Only ports are +		// emitted in such a case. +		if (module->get_blackbox_attribute()) +			emit_extmodule(); +		else +			emit_module();  	}  }; diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc index 3e67e55f2..26f17bcb3 100644 --- a/backends/smt2/smt2.cc +++ b/backends/smt2/smt2.cc @@ -590,7 +590,17 @@ struct Smt2Worker  			if (cell->type == ID($sub)) return export_bvop(cell, "(bvsub A B)");  			if (cell->type == ID($mul)) return export_bvop(cell, "(bvmul A B)");  			if (cell->type == ID($div)) return export_bvop(cell, "(bvUdiv A B)", 'd'); +			// "rem" = truncating modulo  			if (cell->type == ID($mod)) return export_bvop(cell, "(bvUrem A B)", 'd'); +			// "mod" = flooring modulo +			if (cell->type == ID($modfloor)) { +				// bvumod doesn't exist because it's the same as bvurem +				if (cell->getParam(ID::A_SIGNED).as_bool()) { +					return export_bvop(cell, "(bvsmod A B)", 'd'); +				} else { +					return export_bvop(cell, "(bvurem A B)", 'd'); +				} +			}  			if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool)) &&  					2*GetSize(cell->getPort(ID::A).chunks()) < GetSize(cell->getPort(ID::A))) { diff --git a/backends/smt2/smtbmc.py b/backends/smt2/smtbmc.py index d3015b066..03f001bfd 100644 --- a/backends/smt2/smtbmc.py +++ b/backends/smt2/smtbmc.py @@ -1511,7 +1511,7 @@ else:  # not tempind, covermode                              smt_assert_consequent(get_constr_expr(constr_assumes, i))                          print_msg("Re-solving with appended steps..")                          if smt_check_sat() == "unsat": -                            print("%s Cannot appended steps without violating assumptions!" % smt.timestamp()) +                            print("%s Cannot append steps without violating assumptions!" % smt.timestamp())                              retstatus = "FAILED"                              break                      print_anyconsts(step) @@ -1548,7 +1548,7 @@ else:  # not tempind, covermode                          break                      smt_pop() -                if not retstatus: +                if retstatus == "FAILED" or retstatus == "PREUNSAT":                      break          else:  # gentrace @@ -1557,8 +1557,9 @@ else:  # not tempind, covermode                  smt_assert(get_constr_expr(constr_asserts, i))              print_msg("Solving for step %d.." % (last_check_step)) -            if smt_check_sat() != "sat": -                print("%s No solution found!" % smt.timestamp()) +            status = smt_check_sat() +            if status != "sat": +                print("%s No solution found! (%s)" % (smt.timestamp(), status))                  retstatus = "FAILED"                  break @@ -1568,7 +1569,7 @@ else:  # not tempind, covermode          step += step_size -    if gentrace and retstatus: +    if gentrace and retstatus == "PASSED":          print_anyconsts(0)          write_trace(0, num_steps, '%') diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py index 69f59df79..9f7c8c6d9 100644 --- a/backends/smt2/smtio.py +++ b/backends/smt2/smtio.py @@ -121,6 +121,7 @@ class SmtIo:          self.logic_bv = True          self.logic_dt = False          self.forall = False +        self.timeout = 0          self.produce_models = True          self.smt2cache = [list()]          self.p = None @@ -135,6 +136,7 @@ class SmtIo:              self.debug_file = opts.debug_file              self.dummy_file = opts.dummy_file              self.timeinfo = opts.timeinfo +            self.timeout = opts.timeout              self.unroll = opts.unroll              self.noincr = opts.noincr              self.info_stmts = opts.info_stmts @@ -147,6 +149,7 @@ class SmtIo:              self.debug_file = None              self.dummy_file = None              self.timeinfo = os.name != "nt" +            self.timeout = 0              self.unroll = False              self.noincr = False              self.info_stmts = list() @@ -172,22 +175,32 @@ class SmtIo:              self.unroll = False          if self.solver == "yices": -            if self.noincr: +            if self.noincr or self.forall:                  self.popen_vargs = ['yices-smt2'] + self.solver_opts              else:                  self.popen_vargs = ['yices-smt2', '--incremental'] + self.solver_opts +            if self.timeout != 0: +                self.popen_vargs.append('-t') +                self.popen_vargs.append('%d' % self.timeout);          if self.solver == "z3":              self.popen_vargs = ['z3', '-smt2', '-in'] + self.solver_opts +            if self.timeout != 0: +                self.popen_vargs.append('-T:%d' % self.timeout);          if self.solver == "cvc4":              if self.noincr:                  self.popen_vargs = ['cvc4', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts              else:                  self.popen_vargs = ['cvc4', '--incremental', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts +            if self.timeout != 0: +                self.popen_vargs.append('--tlimit=%d000' % self.timeout);          if self.solver == "mathsat":              self.popen_vargs = ['mathsat'] + self.solver_opts +            if self.timeout != 0: +                print('timeout option is not supported for mathsat.') +                sys.exit(1)          if self.solver == "boolector":              if self.noincr: @@ -195,6 +208,9 @@ class SmtIo:              else:                  self.popen_vargs = ['boolector', '--smt2', '-i'] + self.solver_opts              self.unroll = True +            if self.timeout != 0: +                print('timeout option is not supported for boolector.') +                sys.exit(1)          if self.solver == "abc":              if len(self.solver_opts) > 0: @@ -204,6 +220,9 @@ class SmtIo:              self.logic_ax = False              self.unroll = True              self.noincr = True +            if self.timeout != 0: +                print('timeout option is not supported for abc.') +                sys.exit(1)          if self.solver == "dummy":              assert self.dummy_file is not None @@ -232,12 +251,16 @@ class SmtIo:              if self.logic_uf: self.logic += "UF"              if self.logic_bv: self.logic += "BV"              if self.logic_dt: self.logic = "ALL" +            if self.solver == "yices" and self.forall: self.logic = "BV"          self.setup_done = True          for stmt in self.info_stmts:              self.write(stmt) +        if self.forall and self.solver == "yices": +            self.write("(set-option :yices-ef-max-iters 1000000000)") +          if self.produce_models:              self.write("(set-option :produce-models true)") @@ -706,7 +729,7 @@ class SmtIo:          if self.forall:              result = self.read() -            while result not in ["sat", "unsat", "unknown"]: +            while result not in ["sat", "unsat", "unknown", "timeout", "interrupted", ""]:                  print("%s %s: %s" % (self.timestamp(), self.solver, result))                  result = self.read()          else: @@ -717,7 +740,7 @@ class SmtIo:              print("(check-sat)", file=self.debug_file)              self.debug_file.flush() -        if result not in ["sat", "unsat"]: +        if result not in ["sat", "unsat", "unknown", "timeout", "interrupted"]:              if result == "":                  print("%s Unexpected EOF response from solver." % (self.timestamp()), flush=True)              else: @@ -927,7 +950,7 @@ class SmtIo:  class SmtOpts:      def __init__(self):          self.shortopts = "s:S:v" -        self.longopts = ["unroll", "noincr", "noprogress", "dump-smt2=", "logic=", "dummy=", "info=", "nocomments"] +        self.longopts = ["unroll", "noincr", "noprogress", "timeout=", "dump-smt2=", "logic=", "dummy=", "info=", "nocomments"]          self.solver = "yices"          self.solver_opts = list()          self.debug_print = False @@ -936,6 +959,7 @@ class SmtOpts:          self.unroll = False          self.noincr = False          self.timeinfo = os.name != "nt" +        self.timeout = 0          self.logic = None          self.info_stmts = list()          self.nocomments = False @@ -945,6 +969,8 @@ class SmtOpts:              self.solver = a          elif o == "-S":              self.solver_opts.append(a) +        elif o == "--timeout": +            self.timeout = int(a)          elif o == "-v":              self.debug_print = True          elif o == "--unroll": @@ -976,6 +1002,9 @@ class SmtOpts:      -S <opt>          pass <opt> as command line argument to the solver +    --timeout <value> +        set the solver timeout to the specified value (in seconds). +      --logic <smt2_logic>          use the specified SMT2 logic (e.g. QF_AUFBV) diff --git a/backends/smv/smv.cc b/backends/smv/smv.cc index 7113ebc97..2fc7099f4 100644 --- a/backends/smv/smv.cc +++ b/backends/smv/smv.cc @@ -358,7 +358,8 @@ struct SmvWorker  				continue;  			} -			if (cell->type.in(ID($div), ID($mod))) +			// SMV has a "mod" operator, but its semantics don't seem to be well-defined - to be safe, don't generate it at all +			if (cell->type.in(ID($div)/*, ID($mod), ID($modfloor)*/))  			{  				int width_y = GetSize(cell->getPort(ID::Y));  				int width = max(width_y, GetSize(cell->getPort(ID::A))); @@ -366,7 +367,7 @@ struct SmvWorker  				string expr_a, expr_b, op;  				if (cell->type == ID($div))  op = "/"; -				if (cell->type == ID($mod))  op = "mod"; +				//if (cell->type == ID($mod))  op = "mod";  				if (cell->getParam(ID::A_SIGNED).as_bool())  				{ diff --git a/backends/smv/test_cells.sh b/backends/smv/test_cells.sh index 63de465c0..145b9c33b 100644 --- a/backends/smv/test_cells.sh +++ b/backends/smv/test_cells.sh @@ -7,8 +7,8 @@ mkdir -p test_cells.tmp  cd test_cells.tmp  # don't test $mul to reduce runtime -# don't test $div and $mod to reduce runtime and avoid "div by zero" message -../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$macc /$mul /$div /$mod' +# don't test $div/$mod/$divfloor/$modfloor to reduce runtime and avoid "div by zero" message +../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$macc /$mul /$div /$mod /$divfloor /$modfloor'  cat > template.txt << "EOT"  %module main diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 11b2ae10f..4f44a053a 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -740,6 +740,95 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)  #undef HANDLE_UNIOP  #undef HANDLE_BINOP +	if (cell->type == ID($divfloor)) +	{ +		// wire [MAXLEN+1:0] _0_, _1_, _2_; +		// assign _0_ = $signed(A); +		// assign _1_ = $signed(B); +		// assign _2_ = (A[-1] == B[-1]) || A == 0 ? _0_ : $signed(_0_ - (B[-1] ? _1_ + 1 : _1_ - 1)); +		// assign Y = $signed(_2_) / $signed(_1_); + +		if (cell->getParam(ID::A_SIGNED).as_bool() && cell->getParam(ID::B_SIGNED).as_bool()) { +			SigSpec sig_a = cell->getPort(ID::A); +			SigSpec sig_b = cell->getPort(ID::B); + +			std::string buf_a = next_auto_id(); +			std::string buf_b = next_auto_id(); +			std::string buf_num = next_auto_id(); +			int size_a = GetSize(sig_a); +			int size_b = GetSize(sig_b); +			int size_y = GetSize(cell->getPort(ID::Y)); +			int size_max = std::max(size_a, std::max(size_b, size_y)); + +			// intentionally one wider than maximum width +			f << stringf("%s" "wire [%d:0] %s, %s, %s;\n", indent.c_str(), size_max, buf_a.c_str(), buf_b.c_str(), buf_num.c_str()); +			f << stringf("%s" "assign %s = ", indent.c_str(), buf_a.c_str()); +			dump_cell_expr_port(f, cell, "A", true); +			f << stringf(";\n"); +			f << stringf("%s" "assign %s = ", indent.c_str(), buf_b.c_str()); +			dump_cell_expr_port(f, cell, "B", true); +			f << stringf(";\n"); + +			f << stringf("%s" "assign %s = ", indent.c_str(), buf_num.c_str()); +			f << stringf("("); +			dump_sigspec(f, sig_a.extract(sig_a.size()-1)); +			f << stringf(" == "); +			dump_sigspec(f, sig_b.extract(sig_b.size()-1)); +			f << stringf(") || "); +			dump_sigspec(f, sig_a); +			f << stringf(" == 0 ? %s : ", buf_a.c_str()); +			f << stringf("$signed(%s - (", buf_a.c_str()); +			dump_sigspec(f, sig_b.extract(sig_b.size()-1)); +			f << stringf(" ? %s + 1 : %s - 1));\n", buf_b.c_str(), buf_b.c_str()); + + +			f << stringf("%s" "assign ", indent.c_str()); +			dump_sigspec(f, cell->getPort(ID::Y)); +			f << stringf(" = $signed(%s) / ", buf_num.c_str()); +			dump_attributes(f, "", cell->attributes, ' '); +			f << stringf("$signed(%s);\n", buf_b.c_str()); +			return true; +		} else { +			// same as truncating division +			dump_cell_expr_binop(f, indent, cell, "/"); +			return true; +		} +	} + +	if (cell->type == ID($modfloor)) +	{ +		// wire truncated = $signed(A) % $signed(B); +		// assign Y = (A[-1] == B[-1]) || truncated == 0 ? truncated : $signed(B) + $signed(truncated); + +		if (cell->getParam(ID::A_SIGNED).as_bool() && cell->getParam(ID::B_SIGNED).as_bool()) { +			SigSpec sig_a = cell->getPort(ID::A); +			SigSpec sig_b = cell->getPort(ID::B); + +			std::string temp_id = next_auto_id(); +			f << stringf("%s" "wire [%d:0] %s = ", indent.c_str(), GetSize(cell->getPort(ID::A))-1, temp_id.c_str()); +			dump_cell_expr_port(f, cell, "A", true); +			f << stringf(" %% "); +			dump_attributes(f, "", cell->attributes, ' '); +			dump_cell_expr_port(f, cell, "B", true); +			f << stringf(";\n"); + +			f << stringf("%s" "assign ", indent.c_str()); +			dump_sigspec(f, cell->getPort(ID::Y)); +			f << stringf(" = ("); +			dump_sigspec(f, sig_a.extract(sig_a.size()-1)); +			f << stringf(" == "); +			dump_sigspec(f, sig_b.extract(sig_b.size()-1)); +			f << stringf(") || %s == 0 ? %s : ", temp_id.c_str(), temp_id.c_str()); +			dump_cell_expr_port(f, cell, "B", true); +			f << stringf(" + $signed(%s);\n", temp_id.c_str()); +			return true; +		} else { +			// same as truncating modulo +			dump_cell_expr_binop(f, indent, cell, "%"); +			return true; +		} +	} +  	if (cell->type == ID($shift))  	{  		f << stringf("%s" "assign ", indent.c_str()); | 
