diff options
Diffstat (limited to 'backends')
| -rw-r--r-- | backends/firrtl/firrtl.cc | 352 | 
1 files changed, 294 insertions, 58 deletions
| diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc index 9739a7a9f..44c3397da 100644 --- a/backends/firrtl/firrtl.cc +++ b/backends/firrtl/firrtl.cc @@ -102,6 +102,260 @@ const char *make_id(IdString id)  	return namecache.at(id).c_str();  } +std::string dump_const_string(const RTLIL::Const &data) +{ +	std::string res_str; + +	std::string str = data.decode_string(); +	for (size_t i = 0; i < str.size(); i++) +	{ +		if (str[i] == '\n') +			res_str += "\\n"; +		else if (str[i] == '\t') +			res_str += "\\t"; +		else if (str[i] < 32) +			res_str += stringf("\\%03o", str[i]); +		else if (str[i] == '"') +			res_str += "\\\""; +		else if (str[i] == '\\') +			res_str += "\\\\"; +		else +			res_str += str[i]; +	} + +	return res_str; +} + +std::string dump_const(const RTLIL::Const &data) +{ +	std::string res_str; + +	// // For debugging purposes to find out how Yosys encodes flags. +	// res_str += stringf("flags_%x --> ", data.flags); + +	// Real-valued parameter. +	if (data.flags & RTLIL::CONST_FLAG_REAL) +	{ +		// Yosys stores real values as strings, so we call the string dumping code. +		res_str += dump_const_string(data); +	} +	// String parameter. +	else if (data.flags & RTLIL::CONST_FLAG_STRING) +	{ +		res_str += "\""; +		res_str += dump_const_string(data); +		res_str += "\""; +	} +	// Numeric (non-real) parameter. +	else +	{ +		int width = data.bits.size(); + +		// If a standard 32-bit int, then emit standard int value like "56" or +		// "-56". Firrtl supports negative-valued int literals. +		// +		//    SignedInt +		//      : ( '+' | '-' ) PosInt +		//      ; +		if (width <= 32) +		{ +			int32_t int_val = 0; + +			for (int i = 0; i < width; i++) +			{ +				switch (data.bits[i]) +				{ +					case State::S0:                      break; +					case State::S1: int_val |= (1 << i); break; +					default: +						log_error("Unexpected int value\n"); +						break; +				} +			} + +			res_str += stringf("%d", int_val); +		} +		else +		{ +			// If value is larger than 32 bits, then emit a binary representation of +			// the number as integers are not large enough to contain the result. +			// There is a caveat to this approach though: +			// +			// Note that parameter may be defined as having a fixed width as follows: +			// +			//     parameter signed [26:0] test_signed; +			//     parameter        [26:0] test_unsigned; +			//     parameter signed [40:0] test_signed_large; +			// +			// However, if you assign a value on the RHS without specifying the +			// precision, then yosys considers the value you used as an int and +			// assigns it a width of 32 bits regardless of the type of the parameter. +			// +			//     defparam <inst_name> .test_signed = 49;           (width = 32, though should be 27 based on definition) +			//     defparam <inst_name> .test_unsigned = 40'd35;     (width = 40, though should be 27 based on definition) +			//     defparam <inst_name> .test_signed_large = 40'd12; (width = 40) +			// +			// We therefore may lose the precision of the original verilog literal if +			// it was written without its bitwidth specifier. + +			// Emit binary prefix for string. +			res_str += "\"b"; + +			// Emit bits. +			for (int i = width - 1; i >= 0; i--) +			{ +				log_assert(i < width); +				switch (data.bits[i]) +				{ +					case State::S0: res_str += "0"; break; +					case State::S1: res_str += "1"; break; +					case State::Sx: res_str += "x"; break; +					case State::Sz: res_str += "z"; break; +					case State::Sa: res_str += "-"; break; +					case State::Sm: res_str += "m"; break; +				} +			} + +			res_str += "\""; +		} +	} + +	return res_str; +} + +std::string extmodule_name(RTLIL::Cell *cell, RTLIL::Module *mod_instance) +{ +	// Since we are creating a custom extmodule for every cell that instantiates +	// this blackbox, we need to create a custom name for it. We just use the +	// name of the blackbox itself followed by the name of the cell. +	const std::string cell_name = std::string(make_id(cell->name)); +	const std::string blackbox_name = std::string(make_id(mod_instance->name)); +	const std::string extmodule_name = blackbox_name + "_" + cell_name; +	return extmodule_name; +} + +/** + * Emits a parameterized extmodule. Instance parameters are obtained from + * ''cell'' as it represents the instantiation of the blackbox defined by + * ''mod_instance'' and therefore contains all its instance parameters. + */ +void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream &f) +{ +	const std::string indent = "    "; + +	const std::string blackbox_name = std::string(make_id(mod_instance->name)); +	const std::string exported_name = extmodule_name(cell, mod_instance); + +	// We use the cell's fileinfo for this extmodule as its parameters come from +	// the cell and not from the module itself (the module contains default +	// parameters, not the instance-specific ones we're using to emit the +	// extmodule). +	const std::string extmoduleFileinfo = getFileinfo(cell); + +	// Emit extmodule header. +	f << stringf("  extmodule %s: %s\n", exported_name.c_str(), extmoduleFileinfo.c_str()); + +	// Emit extmodule ports. +	for (auto wire : mod_instance->wires()) +	{ +		const auto wireName = make_id(wire->name); +		const std::string wireFileinfo = getFileinfo(wire); + +		if (wire->port_input && wire->port_output) +		{ +			log_error("Module port %s.%s is inout!\n", log_id(mod_instance), log_id(wire)); +		} + +		const std::string portDecl = stringf("%s%s %s: UInt<%d> %s\n", +			indent.c_str(), +			wire->port_input ? "input" : "output", +			wireName, +			wire->width, +			wireFileinfo.c_str() +		); + +		f << portDecl; +	} + +	// Emit extmodule "defname" field. This is the name of the verilog blackbox +	// that is used when verilog is emitted, so we use the name of mod_instance +	// here. +	f << stringf("%sdefname = %s\n", indent.c_str(), blackbox_name.c_str()); + +	// Emit extmodule generic parameters. +	for (const auto &p : cell->parameters) +	{ +		const RTLIL::IdString p_id = p.first; +		const RTLIL::Const p_value = p.second; + +		std::string param_name(p_id.c_str()); +		const std::string param_value = dump_const(p_value); + +		// Remove backslashes from parameters as these come from the internal RTLIL +		// naming scheme, but should not exist in the emitted firrtl blackboxes. +		// When firrtl is converted to verilog and given to downstream synthesis +		// tools, these tools expect to find blackbox names and parameters as they +		// were originally defined, i.e. without the extra RTLIL naming conventions. +		param_name.erase( +			std::remove(param_name.begin(), param_name.end(), '\\'), +			param_name.end() +		); + +		f << stringf("%sparameter %s = %s\n", indent.c_str(), param_name.c_str(), param_value.c_str()); +	} + +	f << "\n"; +} + +/** + * Emits extmodules for every instantiated blackbox in the design. + * + * RTLIL stores instance parameters at the cell's instantiation location. + * However, firrtl does not support module parameterization (everything is + * already elaborated). Firrtl instead supports external modules (extmodule), + * i.e. blackboxes that are defined by verilog and which have no body in + * firrtl itself other than the declaration of the blackboxes ports and + * parameters. + * + * Furthermore, firrtl does not support parameterization (even of extmodules) + * at a module's instantiation location and users must instead declare + * different extmodules with different instance parameters in the extmodule + * definition itself. + * + * This function goes through the design to identify all RTLIL blackboxes + * and emit parameterized extmodules with a unique name for each of them. The + * name that's given to the extmodule is + * + *     <blackbox_name>_<instance_name> + * + * Beware that it is therefore necessary for users to replace "parameterized" + * instances in the RTLIL sense with these custom extmodules for the firrtl to + * be valid. + */ +void emit_elaborated_extmodules(RTLIL::Design *design, std::ostream &f) +{ +	for (auto module : design->modules()) +	{ +		for (auto cell : module->cells()) +		{ +			// Is this cell a module instance? +			bool cellIsModuleInstance = cell->type[0] != '$'; + +			if (cellIsModuleInstance) +			{ +				// Find the module corresponding to this instance. +				auto modInstance = design->module(cell->type); +				bool modIsBlackbox = modInstance->get_blackbox_attribute(); + +				if (modIsBlackbox) +				{ +					emit_extmodule(cell, modInstance, f); +				} +			} +		} +	} +} +  struct FirrtlWorker  {  	Module *module; @@ -328,8 +582,16 @@ struct FirrtlWorker  			log_warning("No instance for %s.%s\n", cell_type.c_str(), cell_name.c_str());  			return;  		} + +		// If the instance is that of a blackbox, use the modified extmodule name +		// that contains per-instance parameterizations. These instances were +		// emitted earlier in the firrtl backend. +		const std::string instanceName = instModule->get_blackbox_attribute() ? +			extmodule_name(cell, instModule) : +			instanceOf; +  		std::string cellFileinfo = getFileinfo(cell); -		wire_exprs.push_back(stringf("%s" "inst %s%s of %s %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), instanceOf.c_str(), cellFileinfo.c_str())); +		wire_exprs.push_back(stringf("%s" "inst %s%s of %s %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), instanceName.c_str(), cellFileinfo.c_str()));  		for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) {  			if (it->second.size() > 0) { @@ -392,33 +654,6 @@ struct FirrtlWorker  		return result;  	} -	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); @@ -440,12 +675,12 @@ struct FirrtlWorker  			{  				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", +				port_decls.push_back(stringf("%s%s %s: UInt<%d> %s\n", indent.c_str(), wire->port_input ? "input" : "output",  						wireName, wire->width, wireFileinfo.c_str()));  			}  			else  			{ -				wire_decls.push_back(stringf("    wire %s: UInt<%d> %s\n", wireName, wire->width, wireFileinfo.c_str())); +				wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), wireName, wire->width, wireFileinfo.c_str()));  			}  		} @@ -476,7 +711,7 @@ struct FirrtlWorker  			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())); +				wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), y_width, cellFileinfo.c_str()));  				if (a_signed) {  					a_expr = "asSInt(" + a_expr + ")"; @@ -516,7 +751,7 @@ struct FirrtlWorker  				if ((firrtl_is_signed && !always_uint))  					expr = stringf("asUInt(%s)", expr.c_str()); -				cell_exprs.push_back(stringf("    %s <= %s %s\n", y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); +				cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str()));  				register_reverse_wire_map(y_id, cell->getPort(ID::Y));  				continue; @@ -528,7 +763,7 @@ struct FirrtlWorker  				string a_expr = make_expr(cell->getPort(ID::A));  				string b_expr = make_expr(cell->getPort(ID::B));  				std::string cellFileinfo = getFileinfo(cell); -				wire_decls.push_back(stringf("    wire %s: UInt<%d> %s\n", y_id.c_str(), y_width, cellFileinfo.c_str())); +				wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), y_width, cellFileinfo.c_str()));  				if (a_signed) {  					a_expr = "asSInt(" + a_expr + ")"; @@ -746,7 +981,7 @@ struct FirrtlWorker  				if ((firrtl_is_signed && !always_uint))  					expr = stringf("asUInt(%s)", expr.c_str()); -				cell_exprs.push_back(stringf("    %s <= %s %s\n", y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); +				cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str()));  				register_reverse_wire_map(y_id, cell->getPort(ID::Y));  				continue; @@ -759,11 +994,11 @@ struct FirrtlWorker  				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)); -				wire_decls.push_back(stringf("    wire %s: UInt<%d> %s\n", y_id.c_str(), width, cellFileinfo.c_str())); +				wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), width, cellFileinfo.c_str()));  				string expr = stringf("mux(%s, %s, %s)", s_expr.c_str(), b_expr.c_str(), a_expr.c_str()); -				cell_exprs.push_back(stringf("    %s <= %s %s\n", y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); +				cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str()));  				register_reverse_wire_map(y_id, cell->getPort(ID::Y));  				continue; @@ -902,9 +1137,9 @@ struct FirrtlWorker  				string expr = make_expr(cell->getPort(ID::D));  				string clk_expr = "asClock(" + make_expr(cell->getPort(ID::CLK)) + ")"; -				wire_decls.push_back(stringf("    reg %s: UInt<%d>, %s %s\n", y_id.c_str(), width, clk_expr.c_str(), cellFileinfo.c_str())); +				wire_decls.push_back(stringf("%sreg %s: UInt<%d>, %s %s\n", indent.c_str(), y_id.c_str(), width, clk_expr.c_str(), cellFileinfo.c_str())); -				cell_exprs.push_back(stringf("    %s <= %s %s\n", y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); +				cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str()));  				register_reverse_wire_map(y_id, cell->getPort(ID::Q));  				continue; @@ -923,7 +1158,7 @@ struct FirrtlWorker  				string a_expr = make_expr(cell->getPort(ID::A));  				// Get the initial bit selector  				string b_expr = make_expr(cell->getPort(ID::B)); -				wire_decls.push_back(stringf("    wire %s: UInt<%d>\n", y_id.c_str(), y_width)); +				wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width));  				if (cell->getParam(ID::B_SIGNED).as_bool()) {  					// Use validif to constrain the selection (test the sign bit) @@ -933,7 +1168,7 @@ struct FirrtlWorker  				}  				string expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_expr.c_str()); -				cell_exprs.push_back(stringf("    %s <= %s\n", y_id.c_str(), expr.c_str())); +				cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), expr.c_str()));  				register_reverse_wire_map(y_id, cell->getPort(ID::Y));  				continue;  			} @@ -945,7 +1180,7 @@ struct FirrtlWorker  				string b_expr = make_expr(cell->getPort(ID::B));  				auto b_string = b_expr.c_str();  				string expr; -				wire_decls.push_back(stringf("    wire %s: UInt<%d>\n", y_id.c_str(), y_width)); +				wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width));  				if (cell->getParam(ID::B_SIGNED).as_bool()) {  					// We generate a left or right shift based on the sign of b. @@ -959,7 +1194,7 @@ struct FirrtlWorker  				} else {  					expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_string);  				} -				cell_exprs.push_back(stringf("    %s <= %s\n", y_id.c_str(), expr.c_str())); +				cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), expr.c_str()));  				register_reverse_wire_map(y_id, cell->getPort(ID::Y));  				continue;  			} @@ -972,8 +1207,8 @@ struct FirrtlWorker  				if (a_width < y_width) {  					a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);  				} -				wire_decls.push_back(stringf("    wire %s: UInt<%d>\n", y_id.c_str(), y_width)); -				cell_exprs.push_back(stringf("    %s <= %s\n", y_id.c_str(), a_expr.c_str())); +				wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width)); +				cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), a_expr.c_str()));  				register_reverse_wire_map(y_id, cell->getPort(ID::Y));  				continue;  			} @@ -986,8 +1221,8 @@ struct FirrtlWorker  			int y_width =  GetSize(conn.first);  			string expr = make_expr(conn.second); -			wire_decls.push_back(stringf("    wire %s: UInt<%d>\n", y_id.c_str(), y_width)); -			cell_exprs.push_back(stringf("    %s <= %s\n", y_id.c_str(), expr.c_str())); +			wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width)); +			cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), expr.c_str()));  			register_reverse_wire_map(y_id, conn.first);  		} @@ -1053,13 +1288,13 @@ struct FirrtlWorker  			if (is_valid) {  				if (make_unconn_id) { -					wire_decls.push_back(stringf("    wire %s: UInt<1> %s\n", unconn_id.c_str(), wireFileinfo.c_str())); +					wire_decls.push_back(stringf("%swire %s: UInt<1> %s\n", indent.c_str(), unconn_id.c_str(), wireFileinfo.c_str()));  					// `invalid` is a firrtl construction for simulation so we will not  					// tag it with a @[fileinfo] tag as it doesn't directly correspond to  					// a specific line of verilog code. -					wire_decls.push_back(stringf("    %s is invalid\n", unconn_id.c_str())); +					wire_decls.push_back(stringf("%s%s is invalid\n", indent.c_str(), unconn_id.c_str()));  				} -				wire_exprs.push_back(stringf("    %s <= %s %s\n", make_id(wire->name), expr.c_str(), wireFileinfo.c_str())); +				wire_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), make_id(wire->name), expr.c_str(), wireFileinfo.c_str()));  			} else {  				if (make_unconn_id) {  					unconn_id.clear(); @@ -1067,7 +1302,7 @@ struct FirrtlWorker  				// `invalid` is a firrtl construction for simulation so we will not  				// tag it with a @[fileinfo] tag as it doesn't directly correspond to  				// a specific line of verilog code. -				wire_decls.push_back(stringf("    %s is invalid\n", make_id(wire->name))); +				wire_decls.push_back(stringf("%s%s is invalid\n", indent.c_str(), make_id(wire->name)));  			}  		} @@ -1112,12 +1347,7 @@ struct FirrtlWorker  	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(); +		emit_module();  	}  }; @@ -1180,10 +1410,16 @@ struct FirrtlBackend : public Backend {  		std::string circuitFileinfo = getFileinfo(top);  		*f << stringf("circuit %s: %s\n", make_id(top->name), circuitFileinfo.c_str()); +		emit_elaborated_extmodules(design, *f); + +		// Emit non-blackbox modules.  		for (auto module : design->modules())  		{ -			FirrtlWorker worker(module, *f, design); -			worker.run(); +			if (!module->get_blackbox_attribute()) +			{ +				FirrtlWorker worker(module, *f, design); +				worker.run(); +			}  		}  		namecache.clear(); | 
