mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-28 10:19:26 +00:00 
			
		
		
		
	add support for initializing registers and memories to the functional backend
This commit is contained in:
		
							parent
							
								
									bdb59ffc8e
								
							
						
					
					
						commit
						99effb6789
					
				
					 10 changed files with 418 additions and 282 deletions
				
			
		|  | @ -72,8 +72,8 @@ struct CxxStruct { | ||||||
| 	std::string name; | 	std::string name; | ||||||
| 	dict<IdString, CxxType> types; | 	dict<IdString, CxxType> types; | ||||||
| 	CxxScope<IdString> scope; | 	CxxScope<IdString> scope; | ||||||
|   CxxStruct(std::string name) | 	CxxStruct(std::string name) : name(name) | ||||||
|     : name(name) { | 	{ | ||||||
| 		scope.reserve("fn"); | 		scope.reserve("fn"); | ||||||
| 		scope.reserve("visit"); | 		scope.reserve("visit"); | ||||||
| 	} | 	} | ||||||
|  | @ -98,6 +98,19 @@ struct CxxStruct { | ||||||
| 	} | 	} | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | std::string cxx_const(RTLIL::Const const &value) { | ||||||
|  | 	std::stringstream ss; | ||||||
|  | 	ss << "Signal<" << value.size() << ">(" << std::hex << std::showbase; | ||||||
|  | 	if(value.size() > 32) ss << "{"; | ||||||
|  | 	for(int i = 0; i < value.size(); i += 32) { | ||||||
|  | 		if(i > 0) ss << ", "; | ||||||
|  | 		ss << value.extract(i, 32).as_int(); | ||||||
|  | 	} | ||||||
|  | 	if(value.size() > 32) ss << "}"; | ||||||
|  | 	ss << ")"; | ||||||
|  | 	return ss.str(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| template<class NodePrinter> struct CxxPrintVisitor : public FunctionalIR::AbstractVisitor<void> { | template<class NodePrinter> struct CxxPrintVisitor : public FunctionalIR::AbstractVisitor<void> { | ||||||
| 	using Node = FunctionalIR::Node; | 	using Node = FunctionalIR::Node; | ||||||
| 	CxxWriter &f; | 	CxxWriter &f; | ||||||
|  | @ -136,20 +149,7 @@ template<class NodePrinter> struct CxxPrintVisitor : public FunctionalIR::Abstra | ||||||
| 	void logical_shift_right(Node, Node a, Node b) override { print("{} >> {}", a, b); } | 	void logical_shift_right(Node, Node a, Node b) override { print("{} >> {}", a, b); } | ||||||
| 	void arithmetic_shift_right(Node, Node a, Node b) override { print("{}.arithmetic_shift_right({})", a, b); } | 	void arithmetic_shift_right(Node, Node a, Node b) override { print("{}.arithmetic_shift_right({})", a, b); } | ||||||
| 	void mux(Node, Node a, Node b, Node s) override { print("{2}.any() ? {1} : {0}", a, b, s); } | 	void mux(Node, Node a, Node b, Node s) override { print("{2}.any() ? {1} : {0}", a, b, s); } | ||||||
| 	void constant(Node, RTLIL::Const value) override { | 	void constant(Node, RTLIL::Const const & value) override { print("{}", cxx_const(value)); } | ||||||
| 		std::stringstream ss; |  | ||||||
| 		bool multiple = value.size() > 32; |  | ||||||
| 		ss << "Signal<" << value.size() << ">(" << std::hex << std::showbase; |  | ||||||
| 		if(multiple) ss << "{"; |  | ||||||
| 		while(value.size() > 32) { |  | ||||||
| 			ss << value.as_int() << ", "; |  | ||||||
| 			value = value.extract(32, value.size() - 32); |  | ||||||
| 		} |  | ||||||
| 		ss << value.as_int(); |  | ||||||
| 		if(multiple) ss << "}"; |  | ||||||
| 		ss << ")"; |  | ||||||
| 		print("{}", ss.str()); |  | ||||||
| 	} |  | ||||||
| 	void input(Node, IdString name) override { print("input.{}", input_struct[name]); } | 	void input(Node, IdString name) override { print("input.{}", input_struct[name]); } | ||||||
| 	void state(Node, IdString name) override { print("current_state.{}", state_struct[name]); } | 	void state(Node, IdString name) override { print("current_state.{}", state_struct[name]); } | ||||||
| 	void memory_read(Node, Node mem, Node addr) override { print("{}.read({})", mem, addr); } | 	void memory_read(Node, Node mem, Node addr) override { print("{}.read({})", mem, addr); } | ||||||
|  | @ -184,8 +184,24 @@ struct CxxModule { | ||||||
| 		output_struct.print(f); | 		output_struct.print(f); | ||||||
| 		state_struct.print(f); | 		state_struct.print(f); | ||||||
| 		f.print("\tstatic void eval(Inputs const &, Outputs &, State const &, State &);\n"); | 		f.print("\tstatic void eval(Inputs const &, Outputs &, State const &, State &);\n"); | ||||||
|  | 		f.print("\tstatic void initialize(State &);\n"); | ||||||
| 		f.print("}};\n\n"); | 		f.print("}};\n\n"); | ||||||
| 	} | 	} | ||||||
|  | 	void write_initial_def(CxxWriter &f) { | ||||||
|  | 		f.print("void {0}::initialize({0}::State &state)\n{{\n", module_name); | ||||||
|  | 		for (auto [name, sort] : ir.state()) { | ||||||
|  | 			if (sort.is_signal()) | ||||||
|  | 				f.print("\tstate.{} = {};\n", state_struct[name], cxx_const(ir.get_initial_state_signal(name))); | ||||||
|  | 			else if (sort.is_memory()) { | ||||||
|  | 				const auto &contents = ir.get_initial_state_memory(name); | ||||||
|  | 				f.print("\tstate.{}.fill({});\n", state_struct[name], cxx_const(contents.default_value())); | ||||||
|  | 				for(auto range : contents) | ||||||
|  | 					for(auto addr = range.base(); addr < range.limit(); addr++) | ||||||
|  | 						f.print("\tstate.{}[{}] = {};\n", state_struct[name], addr, cxx_const(range[addr])); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		f.print("}}\n\n"); | ||||||
|  | 	} | ||||||
| 	void write_eval_def(CxxWriter &f) { | 	void write_eval_def(CxxWriter &f) { | ||||||
| 		f.print("void {0}::eval({0}::Inputs const &input, {0}::Outputs &output, {0}::State const ¤t_state, {0}::State &next_state)\n{{\n", module_name); | 		f.print("void {0}::eval({0}::Inputs const &input, {0}::Outputs &output, {0}::State const ¤t_state, {0}::State &next_state)\n{{\n", module_name); | ||||||
| 		CxxScope<int> locals; | 		CxxScope<int> locals; | ||||||
|  | @ -204,7 +220,7 @@ struct CxxModule { | ||||||
| 			f.print("\tnext_state.{} = {};\n", state_struct[name], node_name(ir.get_state_next_node(name))); | 			f.print("\tnext_state.{} = {};\n", state_struct[name], node_name(ir.get_state_next_node(name))); | ||||||
| 		for (auto [name, sort] : ir.outputs()) | 		for (auto [name, sort] : ir.outputs()) | ||||||
| 			f.print("\toutput.{} = {};\n", output_struct[name], node_name(ir.get_output_node(name))); | 			f.print("\toutput.{} = {};\n", output_struct[name], node_name(ir.get_output_node(name))); | ||||||
| 		f.print("}}\n"); | 		f.print("}}\n\n"); | ||||||
| 	} | 	} | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -225,6 +241,7 @@ struct FunctionalCxxBackend : public Backend | ||||||
| 		mod.write_header(f); | 		mod.write_header(f); | ||||||
| 		mod.write_struct_def(f); | 		mod.write_struct_def(f); | ||||||
| 		mod.write_eval_def(f); | 		mod.write_eval_def(f); | ||||||
|  | 		mod.write_initial_def(f); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override | 	void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override | ||||||
|  |  | ||||||
|  | @ -410,6 +410,9 @@ public: | ||||||
|         ret._contents[addr.template as_numeric<size_t>()] = data; |         ret._contents[addr.template as_numeric<size_t>()] = data; | ||||||
|         return ret; |         return ret; | ||||||
|     } |     } | ||||||
|  |     // mutating methods for initializing a state
 | ||||||
|  |     void fill(Signal<d> data) { _contents.fill(data); } | ||||||
|  |     Signal<d> &operator[](Signal<a> addr) { return _contents[addr.template as_numeric<size_t>()]; } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -109,6 +109,13 @@ public: | ||||||
| 	} | 	} | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | std::string smt_const(RTLIL::Const const &c) { | ||||||
|  | 	std::string s = "#b"; | ||||||
|  | 	for(int i = c.size(); i-- > 0; ) | ||||||
|  | 		s += c[i] == State::S1 ? '1' : '0'; | ||||||
|  | 	return s; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| struct SmtPrintVisitor : public FunctionalIR::AbstractVisitor<SExpr> { | struct SmtPrintVisitor : public FunctionalIR::AbstractVisitor<SExpr> { | ||||||
| 	using Node = FunctionalIR::Node; | 	using Node = FunctionalIR::Node; | ||||||
| 	std::function<SExpr(Node)> n; | 	std::function<SExpr(Node)> n; | ||||||
|  | @ -117,13 +124,6 @@ struct SmtPrintVisitor : public FunctionalIR::AbstractVisitor<SExpr> { | ||||||
| 
 | 
 | ||||||
| 	SmtPrintVisitor(SmtStruct &input_struct, SmtStruct &state_struct) : input_struct(input_struct), state_struct(state_struct) {} | 	SmtPrintVisitor(SmtStruct &input_struct, SmtStruct &state_struct) : input_struct(input_struct), state_struct(state_struct) {} | ||||||
| 
 | 
 | ||||||
| 	std::string literal(RTLIL::Const c) { |  | ||||||
| 		std::string s = "#b"; |  | ||||||
| 		for(int i = c.size(); i-- > 0; ) |  | ||||||
| 			s += c[i] == State::S1 ? '1' : '0'; |  | ||||||
| 		return s; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	SExpr from_bool(SExpr &&arg) { | 	SExpr from_bool(SExpr &&arg) { | ||||||
| 		return list("ite", std::move(arg), "#b1", "#b0"); | 		return list("ite", std::move(arg), "#b1", "#b0"); | ||||||
| 	} | 	} | ||||||
|  | @ -149,8 +149,8 @@ struct SmtPrintVisitor : public FunctionalIR::AbstractVisitor<SExpr> { | ||||||
| 	SExpr bitwise_xor(Node, Node a, Node b) override { return list("bvxor", n(a), n(b)); } | 	SExpr bitwise_xor(Node, Node a, Node b) override { return list("bvxor", n(a), n(b)); } | ||||||
| 	SExpr bitwise_not(Node, Node a) override { return list("bvnot", n(a)); } | 	SExpr bitwise_not(Node, Node a) override { return list("bvnot", n(a)); } | ||||||
| 	SExpr unary_minus(Node, Node a) override { return list("bvneg", n(a)); } | 	SExpr unary_minus(Node, Node a) override { return list("bvneg", n(a)); } | ||||||
| 	SExpr reduce_and(Node, Node a) override { return from_bool(list("=", n(a), literal(RTLIL::Const(State::S1, a.width())))); } | 	SExpr reduce_and(Node, Node a) override { return from_bool(list("=", n(a), smt_const(RTLIL::Const(State::S1, a.width())))); } | ||||||
| 	SExpr reduce_or(Node, Node a) override { return from_bool(list("distinct", n(a), literal(RTLIL::Const(State::S0, a.width())))); } | 	SExpr reduce_or(Node, Node a) override { return from_bool(list("distinct", n(a), smt_const(RTLIL::Const(State::S0, a.width())))); } | ||||||
| 	SExpr reduce_xor(Node, Node a) override { | 	SExpr reduce_xor(Node, Node a) override { | ||||||
| 		vector<SExpr> s { "bvxor" }; | 		vector<SExpr> s { "bvxor" }; | ||||||
| 		for(int i = 0; i < a.width(); i++) | 		for(int i = 0; i < a.width(); i++) | ||||||
|  | @ -174,7 +174,7 @@ struct SmtPrintVisitor : public FunctionalIR::AbstractVisitor<SExpr> { | ||||||
| 	SExpr logical_shift_right(Node, Node a, Node b) override { return list("bvlshr", n(a), extend(n(b), b.width(), a.width())); } | 	SExpr logical_shift_right(Node, Node a, Node b) override { return list("bvlshr", n(a), extend(n(b), b.width(), a.width())); } | ||||||
| 	SExpr arithmetic_shift_right(Node, Node a, Node b) override { return list("bvashr", n(a), extend(n(b), b.width(), a.width())); } | 	SExpr arithmetic_shift_right(Node, Node a, Node b) override { return list("bvashr", n(a), extend(n(b), b.width(), a.width())); } | ||||||
| 	SExpr mux(Node, Node a, Node b, Node s) override { return list("ite", to_bool(n(s)), n(b), n(a)); } | 	SExpr mux(Node, Node a, Node b, Node s) override { return list("ite", to_bool(n(s)), n(b), n(a)); } | ||||||
| 	SExpr constant(Node, RTLIL::Const value) override { return literal(value); } | 	SExpr constant(Node, RTLIL::Const const &value) override { return smt_const(value); } | ||||||
| 	SExpr memory_read(Node, Node mem, Node addr) override { return list("select", n(mem), n(addr)); } | 	SExpr memory_read(Node, Node mem, Node addr) override { return list("select", n(mem), n(addr)); } | ||||||
| 	SExpr memory_write(Node, Node mem, Node addr, Node data) override { return list("store", n(mem), n(addr), n(data)); } | 	SExpr memory_write(Node, Node mem, Node addr, Node data) override { return list("store", n(mem), n(addr), n(data)); } | ||||||
| 
 | 
 | ||||||
|  | @ -199,6 +199,7 @@ struct SmtModule { | ||||||
| 		, output_struct(scope.unique_name(module->name.str() + "_Outputs"), scope) | 		, output_struct(scope.unique_name(module->name.str() + "_Outputs"), scope) | ||||||
| 		, state_struct(scope.unique_name(module->name.str() + "_State"), scope) | 		, state_struct(scope.unique_name(module->name.str() + "_State"), scope) | ||||||
| 	{ | 	{ | ||||||
|  | 		scope.reserve(name + "-initial"); | ||||||
| 		for (const auto &input : ir.inputs()) | 		for (const auto &input : ir.inputs()) | ||||||
| 			input_struct.insert(input.first, input.second); | 			input_struct.insert(input.first, input.second); | ||||||
| 		for (const auto &output : ir.outputs()) | 		for (const auto &output : ir.outputs()) | ||||||
|  | @ -207,18 +208,8 @@ struct SmtModule { | ||||||
| 			state_struct.insert(state.first, state.second); | 			state_struct.insert(state.first, state.second); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void write(std::ostream &out) | 	void write_eval(SExprWriter &w) | ||||||
| 	{ | 	{ | ||||||
| 		SExprWriter w(out); |  | ||||||
| 
 |  | ||||||
| 		input_struct.write_definition(w); |  | ||||||
| 		output_struct.write_definition(w); |  | ||||||
| 		state_struct.write_definition(w); |  | ||||||
| 
 |  | ||||||
| 		w << list("declare-datatypes", |  | ||||||
| 			list(list("Pair", 2)), |  | ||||||
| 			list(list("par", list("X", "Y"), list(list("pair", list("first", "X"), list("second", "Y")))))); |  | ||||||
| 
 |  | ||||||
| 		w.push(); | 		w.push(); | ||||||
| 		w.open(list("define-fun", name, | 		w.open(list("define-fun", name, | ||||||
| 			list(list("inputs", input_struct.name), | 			list(list("inputs", input_struct.name), | ||||||
|  | @ -245,6 +236,39 @@ struct SmtModule { | ||||||
| 		state_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.get_state_next_node(name)); }); | 		state_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.get_state_next_node(name)); }); | ||||||
| 		w.pop(); | 		w.pop(); | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	void write_initial(SExprWriter &w) | ||||||
|  | 	{ | ||||||
|  | 		std::string initial = name + "-initial"; | ||||||
|  | 		w << list("declare-const", initial, state_struct.name); | ||||||
|  | 		for (const auto &[name, sort] : ir.state()) { | ||||||
|  | 			if(sort.is_signal()) | ||||||
|  | 				w << list("assert", list("=", state_struct.access(initial, name), smt_const(ir.get_initial_state_signal(name)))); | ||||||
|  | 			else if(sort.is_memory()) { | ||||||
|  | 				auto contents = ir.get_initial_state_memory(name); | ||||||
|  | 				for(int i = 0; i < 1<<sort.addr_width(); i++) { | ||||||
|  | 					auto addr = smt_const(RTLIL::Const(i, sort.addr_width())); | ||||||
|  | 					w << list("assert", list("=", list("select", state_struct.access(initial, name), addr), smt_const(contents[i]))); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void write(std::ostream &out) | ||||||
|  | 	{     | ||||||
|  | 		SExprWriter w(out); | ||||||
|  | 
 | ||||||
|  | 		input_struct.write_definition(w); | ||||||
|  | 		output_struct.write_definition(w); | ||||||
|  | 		state_struct.write_definition(w); | ||||||
|  | 
 | ||||||
|  | 		w << list("declare-datatypes", | ||||||
|  | 			list(list("Pair", 2)), | ||||||
|  | 			list(list("par", list("X", "Y"), list(list("pair", list("first", "X"), list("second", "Y")))))); | ||||||
|  | 		 | ||||||
|  | 		write_eval(w); | ||||||
|  | 		write_initial(w); | ||||||
|  | 	} | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct FunctionalSmtBackend : public Backend { | struct FunctionalSmtBackend : public Backend { | ||||||
|  |  | ||||||
|  | @ -19,10 +19,100 @@ | ||||||
| 
 | 
 | ||||||
| #include "kernel/yosys.h" | #include "kernel/yosys.h" | ||||||
| #include "kernel/functionalir.h" | #include "kernel/functionalir.h" | ||||||
|  | #include <random> | ||||||
| 
 | 
 | ||||||
| USING_YOSYS_NAMESPACE | USING_YOSYS_NAMESPACE | ||||||
| PRIVATE_NAMESPACE_BEGIN | PRIVATE_NAMESPACE_BEGIN | ||||||
| 
 | 
 | ||||||
|  | struct MemContentsTest { | ||||||
|  | 	int addr_width, data_width; | ||||||
|  | 	MemContents state; | ||||||
|  | 	using addr_t = MemContents::addr_t; | ||||||
|  | 	std::map<addr_t, RTLIL::Const> reference; | ||||||
|  | 	MemContentsTest(int addr_width, int data_width) : addr_width(addr_width), data_width(data_width), state(addr_width, data_width, RTLIL::Const(State::S0, data_width))  {} | ||||||
|  | 	void check() { | ||||||
|  | 		state.check(); | ||||||
|  | 		for(auto addr = 0; addr < (1<<addr_width); addr++) { | ||||||
|  | 			auto it = reference.find(addr); | ||||||
|  | 			if(it != reference.end()) { | ||||||
|  | 				if(state.count_range(addr, addr + 1) != 1) goto error; | ||||||
|  | 				if(it->second != state[addr]) goto error; | ||||||
|  | 			} else { | ||||||
|  | 				if(state.count_range(addr, addr + 1) != 0) goto error; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		return; | ||||||
|  | 		error: | ||||||
|  | 		printf("FAIL\n"); | ||||||
|  | 		int digits = (data_width + 3) / 4; | ||||||
|  | 		 | ||||||
|  | 		for(auto addr = 0; addr < (1<<addr_width); addr++) { | ||||||
|  | 			if(addr % 8 == 0) printf("%.8x   ", addr); | ||||||
|  | 			auto it = reference.find(addr); | ||||||
|  | 			bool ref_def = it != reference.end(); | ||||||
|  | 			RTLIL::Const ref_value = ref_def ? it->second : state.default_value(); | ||||||
|  | 			std::string ref_string = stringf("%.*x", digits, ref_value.as_int()); | ||||||
|  | 			bool sta_def = state.count_range(addr, addr + 1) == 1; | ||||||
|  | 			RTLIL::Const sta_value = state[addr]; | ||||||
|  | 			std::string sta_string = stringf("%.*x", digits, sta_value.as_int()); | ||||||
|  | 			if(ref_def && sta_def) { | ||||||
|  | 				if(ref_value == sta_value) printf("%s%s", ref_string.c_str(), string(digits, ' ').c_str()); | ||||||
|  | 				else printf("%s%s", ref_string.c_str(), sta_string.c_str()); | ||||||
|  | 			} else if(ref_def) { | ||||||
|  | 				printf("%s%s", ref_string.c_str(), string(digits, 'M').c_str()); | ||||||
|  | 			} else if(sta_def) { | ||||||
|  | 				printf("%s%s", sta_string.c_str(), string(digits, 'X').c_str()); | ||||||
|  | 			} else { | ||||||
|  | 				printf("%s", string(2*digits, ' ').c_str()); | ||||||
|  | 			} | ||||||
|  | 			printf(" "); | ||||||
|  | 			if(addr % 8 == 7) printf("\n"); | ||||||
|  | 		} | ||||||
|  | 		printf("\n"); | ||||||
|  | 		//log_abort();
 | ||||||
|  | 	} | ||||||
|  | 	void clear_range(addr_t begin_addr, addr_t end_addr) { | ||||||
|  | 		for(auto addr = begin_addr; addr != end_addr; addr++) | ||||||
|  | 			reference.erase(addr); | ||||||
|  | 		state.clear_range(begin_addr, end_addr); | ||||||
|  | 		check(); | ||||||
|  | 	} | ||||||
|  | 	void insert_concatenated(addr_t addr, RTLIL::Const const &values) { | ||||||
|  | 		addr_t words = ((addr_t) values.size() + data_width - 1) / data_width; | ||||||
|  | 		for(addr_t i = 0; i < words; i++) { | ||||||
|  | 			reference.erase(addr + i); | ||||||
|  | 			reference.emplace(addr + i, values.extract(i * data_width, data_width)); | ||||||
|  | 		} | ||||||
|  | 		state.insert_concatenated(addr, values); | ||||||
|  | 		check(); | ||||||
|  | 	} | ||||||
|  | 	template<typename Rnd> void run(Rnd &rnd, int n) { | ||||||
|  | 		std::uniform_int_distribution<addr_t> addr_dist(0, (1<<addr_width) - 1); | ||||||
|  | 		std::poisson_distribution<addr_t> length_dist(10); | ||||||
|  | 		std::uniform_int_distribution<uint64_t> data_dist(0, ((uint64_t)1<<data_width) - 1); | ||||||
|  | 		while(n-- > 0) { | ||||||
|  | 			addr_t low = addr_dist(rnd); | ||||||
|  | 			//addr_t length = std::min((1<<addr_width) - low, length_dist(rnd));
 | ||||||
|  | 			//addr_t high = low + length - 1;
 | ||||||
|  | 			addr_t high = addr_dist(rnd); | ||||||
|  | 			if(low > high) std::swap(low, high); | ||||||
|  | 			if((rnd() & 7) == 0) { | ||||||
|  | 				log_debug("clear %.2x to %.2x\n", (int)low, (int)high); | ||||||
|  | 				clear_range(low, high + 1); | ||||||
|  | 			} else { | ||||||
|  | 				log_debug("insert %.2x to %.2x\n", (int)low, (int)high); | ||||||
|  | 				RTLIL::Const values; | ||||||
|  | 				for(addr_t addr = low; addr <= high; addr++) { | ||||||
|  | 					RTLIL::Const word(data_dist(rnd), data_width); | ||||||
|  | 					values.bits.insert(values.bits.end(), word.bits.begin(), word.bits.end()); | ||||||
|  | 				} | ||||||
|  | 				insert_concatenated(low, values); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| struct FunctionalTestGeneric : public Pass | struct FunctionalTestGeneric : public Pass | ||||||
| { | { | ||||||
| 	FunctionalTestGeneric() : Pass("test_generic", "test the generic compute graph") {} | 	FunctionalTestGeneric() : Pass("test_generic", "test the generic compute graph") {} | ||||||
|  | @ -40,6 +130,14 @@ struct FunctionalTestGeneric : public Pass | ||||||
| 		size_t argidx = 1; | 		size_t argidx = 1; | ||||||
| 		extra_args(args, argidx, design); | 		extra_args(args, argidx, design); | ||||||
| 
 | 
 | ||||||
|  | 		MemContentsTest test(8, 16); | ||||||
|  | 
 | ||||||
|  | 		std::random_device seed_dev; | ||||||
|  | 		std::mt19937 rnd(23); //seed_dev());
 | ||||||
|  | 		test.run(rnd, 1000); | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | 
 | ||||||
| 		for (auto module : design->selected_modules()) { | 		for (auto module : design->selected_modules()) { | ||||||
|             log("Dumping module `%s'.\n", module->name.c_str()); |             log("Dumping module `%s'.\n", module->name.c_str()); | ||||||
| 			auto fir = FunctionalIR::from_module(module); | 			auto fir = FunctionalIR::from_module(module); | ||||||
|  | @ -50,6 +148,7 @@ struct FunctionalTestGeneric : public Pass | ||||||
| 			for(auto [name, sort] : fir.state()) | 			for(auto [name, sort] : fir.state()) | ||||||
| 				std::cout << RTLIL::unescape_id(name) << " = " << RTLIL::unescape_id(fir.get_state_next_node(name).name()) << "\n"; | 				std::cout << RTLIL::unescape_id(name) << " = " << RTLIL::unescape_id(fir.get_state_next_node(name).name()) << "\n"; | ||||||
| 		} | 		} | ||||||
|  | */ | ||||||
| 	} | 	} | ||||||
| } FunctionalCxxBackend; | } FunctionalCxxBackend; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -19,6 +19,8 @@ | ||||||
| 
 | 
 | ||||||
| #include "kernel/functionalir.h" | #include "kernel/functionalir.h" | ||||||
| #include <optional> | #include <optional> | ||||||
|  | #include "ff.h" | ||||||
|  | #include "ffinit.h" | ||||||
| 
 | 
 | ||||||
| YOSYS_NAMESPACE_BEGIN | YOSYS_NAMESPACE_BEGIN | ||||||
| 
 | 
 | ||||||
|  | @ -70,7 +72,7 @@ struct PrintVisitor : FunctionalIR::DefaultVisitor<std::string> { | ||||||
| 	std::string slice(Node, Node a, int offset, int out_width) override { return "slice(" + np(a) + ", " + std::to_string(offset) + ", " + std::to_string(out_width) + ")"; } | 	std::string slice(Node, Node a, int offset, int out_width) override { return "slice(" + np(a) + ", " + std::to_string(offset) + ", " + std::to_string(out_width) + ")"; } | ||||||
| 	std::string zero_extend(Node, Node a, int out_width) override { return "zero_extend(" + np(a) + ", " + std::to_string(out_width) + ")"; } | 	std::string zero_extend(Node, Node a, int out_width) override { return "zero_extend(" + np(a) + ", " + std::to_string(out_width) + ")"; } | ||||||
| 	std::string sign_extend(Node, Node a, int out_width) override { return "sign_extend(" + np(a) + ", " + std::to_string(out_width) + ")"; } | 	std::string sign_extend(Node, Node a, int out_width) override { return "sign_extend(" + np(a) + ", " + std::to_string(out_width) + ")"; } | ||||||
| 	std::string constant(Node, RTLIL::Const value) override { return "constant(" + value.as_string() + ")"; } | 	std::string constant(Node, RTLIL::Const const& value) override { return "constant(" + value.as_string() + ")"; } | ||||||
| 	std::string input(Node, IdString name) override { return "input(" + name.str() + ")"; } | 	std::string input(Node, IdString name) override { return "input(" + name.str() + ")"; } | ||||||
| 	std::string state(Node, IdString name) override { return "state(" + name.str() + ")"; } | 	std::string state(Node, IdString name) override { return "state(" + name.str() + ")"; } | ||||||
| 	std::string default_handler(Node self) override { | 	std::string default_handler(Node self) override { | ||||||
|  | @ -407,6 +409,8 @@ class FunctionalIRConstruction { | ||||||
| 	CellSimplifier simplifier; | 	CellSimplifier simplifier; | ||||||
| 	vector<Mem> memories_vector; | 	vector<Mem> memories_vector; | ||||||
| 	dict<Cell*, Mem*> memories; | 	dict<Cell*, Mem*> memories; | ||||||
|  | 	SigMap sig_map; // TODO: this is only for FfInitVals, remove this once FfInitVals supports DriverMap
 | ||||||
|  | 	FfInitVals ff_initvals; | ||||||
| 
 | 
 | ||||||
| 	Node enqueue(DriveSpec const &spec) | 	Node enqueue(DriveSpec const &spec) | ||||||
| 	{ | 	{ | ||||||
|  | @ -438,8 +442,11 @@ class FunctionalIRConstruction { | ||||||
| 			return it->second; | 			return it->second; | ||||||
| 	} | 	} | ||||||
| public: | public: | ||||||
| 	FunctionalIRConstruction(FunctionalIR::Factory &f) : factory(f), simplifier(f) {} | 	FunctionalIRConstruction(Module *module, FunctionalIR::Factory &f) | ||||||
| 	void add_module(Module *module) | 		: factory(f) | ||||||
|  | 		, simplifier(f) | ||||||
|  | 		, sig_map(module) | ||||||
|  | 		, ff_initvals(&sig_map, module) | ||||||
| 	{ | 	{ | ||||||
| 		driver_map.add(module); | 		driver_map.add(module); | ||||||
| 		for (auto cell : module->cells()) { | 		for (auto cell : module->cells()) { | ||||||
|  | @ -447,9 +454,12 @@ public: | ||||||
| 				queue.emplace_back(cell); | 				queue.emplace_back(cell); | ||||||
| 		} | 		} | ||||||
| 		for (auto wire : module->wires()) { | 		for (auto wire : module->wires()) { | ||||||
|  | 			if (wire->port_input) | ||||||
|  | 				factory.add_input(wire->name, wire->width); | ||||||
| 			if (wire->port_output) { | 			if (wire->port_output) { | ||||||
| 				Node node = enqueue(DriveChunk(DriveChunkWire(wire, 0, wire->width))); | 				factory.add_output(wire->name, wire->width); | ||||||
| 				factory.declare_output(node, wire->name, wire->width); | 				Node value = enqueue(DriveChunk(DriveChunkWire(wire, 0, wire->width))); | ||||||
|  | 				factory.set_output(wire->name, value); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		memories_vector = Mem::get_all_memories(module); | 		memories_vector = Mem::get_all_memories(module); | ||||||
|  | @ -487,9 +497,9 @@ public: | ||||||
| 		// - Since wr port j can only have priority over wr port i if j > i, if we do writes in
 | 		// - Since wr port j can only have priority over wr port i if j > i, if we do writes in
 | ||||||
| 		//   ascending index order the result will obey the priorty relation.
 | 		//   ascending index order the result will obey the priorty relation.
 | ||||||
| 		vector<Node> read_results; | 		vector<Node> read_results; | ||||||
| 		int addr_width = ceil_log2(mem->size); | 		factory.add_state(mem->cell->name, FunctionalIR::Sort(ceil_log2(mem->size), mem->width)); | ||||||
| 		int data_width = mem->width; | 		factory.set_initial_state(mem->cell->name, MemContents(mem)); | ||||||
| 		Node node = factory.state_memory(mem->cell->name, addr_width, data_width); | 		Node node = factory.get_current_state(mem->cell->name); | ||||||
| 		for (size_t i = 0; i < mem->wr_ports.size(); i++) { | 		for (size_t i = 0; i < mem->wr_ports.size(); i++) { | ||||||
| 			const auto &wr = mem->wr_ports[i]; | 			const auto &wr = mem->wr_ports[i]; | ||||||
| 			if (wr.clk_enable) | 			if (wr.clk_enable) | ||||||
|  | @ -513,7 +523,7 @@ public: | ||||||
| 			Node addr = enqueue(driver_map(DriveSpec(rd.addr))); | 			Node addr = enqueue(driver_map(DriveSpec(rd.addr))); | ||||||
| 			read_results.push_back(factory.memory_read(node, addr)); | 			read_results.push_back(factory.memory_read(node, addr)); | ||||||
| 		} | 		} | ||||||
| 		factory.declare_state_memory(node, mem->cell->name, addr_width, data_width); | 		factory.set_next_state(mem->cell->name, node); | ||||||
| 		return concatenate_read_results(mem, read_results); | 		return concatenate_read_results(mem, read_results); | ||||||
| 	} | 	} | ||||||
| 	void process_cell(Cell *cell) | 	void process_cell(Cell *cell) | ||||||
|  | @ -527,6 +537,17 @@ public: | ||||||
| 			} | 			} | ||||||
| 			Node node = handle_memory(mem); | 			Node node = handle_memory(mem); | ||||||
| 			factory.update_pending(cell_outputs.at({cell, ID(RD_DATA)}), node); | 			factory.update_pending(cell_outputs.at({cell, ID(RD_DATA)}), node); | ||||||
|  | 		} else if (RTLIL::builtin_ff_cell_types().count(cell->type)) { | ||||||
|  | 			FfData ff(&ff_initvals, cell); | ||||||
|  | 			if (!ff.has_gclk) | ||||||
|  | 				log_error("The design contains a %s flip-flop at %s. This is not supported by the functional backend. " | ||||||
|  | 					"Call async2sync or clk2fflogic to avoid this error.\n", log_id(cell->type), log_id(cell)); | ||||||
|  | 			factory.add_state(ff.name, FunctionalIR::Sort(ff.width)); | ||||||
|  | 			Node q_value = factory.get_current_state(ff.name); | ||||||
|  | 			factory.suggest_name(q_value, ff.name); | ||||||
|  | 			factory.update_pending(cell_outputs.at({cell, ID(Q)}), q_value); | ||||||
|  | 			factory.set_next_state(ff.name, enqueue(ff.sig_d)); | ||||||
|  | 			factory.set_initial_state(ff.name, ff.val_init); | ||||||
| 		} else { | 		} else { | ||||||
| 			dict<IdString, Node> connections; | 			dict<IdString, Node> connections; | ||||||
| 			IdString output_name; // for the single output case
 | 			IdString output_name; // for the single output case
 | ||||||
|  | @ -572,7 +593,7 @@ public: | ||||||
| 					DriveChunkWire wire_chunk = chunk.wire(); | 					DriveChunkWire wire_chunk = chunk.wire(); | ||||||
| 					if (wire_chunk.is_whole()) { | 					if (wire_chunk.is_whole()) { | ||||||
| 						if (wire_chunk.wire->port_input) { | 						if (wire_chunk.wire->port_input) { | ||||||
| 							Node node = factory.input(wire_chunk.wire->name, wire_chunk.width); | 							Node node = factory.get_input(wire_chunk.wire->name); | ||||||
| 							factory.suggest_name(node, wire_chunk.wire->name); | 							factory.suggest_name(node, wire_chunk.wire->name); | ||||||
| 							factory.update_pending(pending, node); | 							factory.update_pending(pending, node); | ||||||
| 						} else { | 						} else { | ||||||
|  | @ -590,24 +611,8 @@ public: | ||||||
| 					DriveChunkPort port_chunk = chunk.port(); | 					DriveChunkPort port_chunk = chunk.port(); | ||||||
| 					if (port_chunk.is_whole()) { | 					if (port_chunk.is_whole()) { | ||||||
| 						if (driver_map.celltypes.cell_output(port_chunk.cell->type, port_chunk.port)) { | 						if (driver_map.celltypes.cell_output(port_chunk.cell->type, port_chunk.port)) { | ||||||
| 							if (port_chunk.cell->type.in(ID($dff), ID($ff))) |  | ||||||
| 							{ |  | ||||||
| 								Cell *cell = port_chunk.cell; |  | ||||||
| 								Node node = factory.state(cell->name, port_chunk.width); |  | ||||||
| 								factory.suggest_name(node, port_chunk.cell->name); |  | ||||||
| 								factory.update_pending(pending, node); |  | ||||||
| 								for (auto const &conn : cell->connections()) { |  | ||||||
| 									if (driver_map.celltypes.cell_input(cell->type, conn.first)) { |  | ||||||
| 										Node node = enqueue(DriveChunkPort(cell, conn)); |  | ||||||
| 										factory.declare_state(node, cell->name, port_chunk.width); |  | ||||||
| 									} |  | ||||||
| 								} |  | ||||||
| 							} |  | ||||||
| 							else |  | ||||||
| 							{ |  | ||||||
| 							Node node = enqueue_cell(port_chunk.cell, port_chunk.port); | 							Node node = enqueue_cell(port_chunk.cell, port_chunk.port); | ||||||
| 							factory.update_pending(pending, node); | 							factory.update_pending(pending, node); | ||||||
| 							} |  | ||||||
| 						} else { | 						} else { | ||||||
| 							DriveSpec driver = driver_map(DriveSpec(port_chunk)); | 							DriveSpec driver = driver_map(DriveSpec(port_chunk)); | ||||||
| 							factory.update_pending(pending, enqueue(driver)); | 							factory.update_pending(pending, enqueue(driver)); | ||||||
|  | @ -641,8 +646,7 @@ public: | ||||||
| FunctionalIR FunctionalIR::from_module(Module *module) { | FunctionalIR FunctionalIR::from_module(Module *module) { | ||||||
|     FunctionalIR ir; |     FunctionalIR ir; | ||||||
|     auto factory = ir.factory(); |     auto factory = ir.factory(); | ||||||
|     FunctionalIRConstruction ctor(factory); |     FunctionalIRConstruction ctor(module, factory); | ||||||
|     ctor.add_module(module); |  | ||||||
|     ctor.process_queue(); |     ctor.process_queue(); | ||||||
|     ir.topological_sort(); |     ir.topological_sort(); | ||||||
|     ir.forward_buf(); |     ir.forward_buf(); | ||||||
|  |  | ||||||
|  | @ -189,24 +189,11 @@ private: | ||||||
| 	// the bool is true for next state values
 | 	// the bool is true for next state values
 | ||||||
| 	using Graph = ComputeGraph<NodeData, Attr, IdString, std::pair<IdString, bool>>; | 	using Graph = ComputeGraph<NodeData, Attr, IdString, std::pair<IdString, bool>>; | ||||||
| 	Graph _graph; | 	Graph _graph; | ||||||
| 	dict<IdString, Sort> _inputs; | 	dict<IdString, Sort> _input_sorts; | ||||||
| 	dict<IdString, Sort> _outputs; | 	dict<IdString, Sort> _output_sorts; | ||||||
| 	dict<IdString, Sort> _state; | 	dict<IdString, Sort> _state_sorts; | ||||||
| 	void add_input(IdString name, Sort sort) { | 	dict<IdString, RTLIL::Const> _initial_state_signal; | ||||||
| 		auto [it, found] = _inputs.emplace(name, std::move(sort)); | 	dict<IdString, MemContents> _initial_state_memory; | ||||||
| 		if(found) |  | ||||||
| 			log_assert(it->second == sort); |  | ||||||
| 	} |  | ||||||
| 	void add_state(IdString name, Sort sort) { |  | ||||||
| 		auto [it, found] = _state.emplace(name, std::move(sort)); |  | ||||||
| 		if(found) |  | ||||||
| 			log_assert(it->second == sort); |  | ||||||
| 	} |  | ||||||
| 	void add_output(IdString name, Sort sort) { |  | ||||||
| 		auto [it, found] = _outputs.emplace(name, std::move(sort)); |  | ||||||
| 		if(found) |  | ||||||
| 			log_assert(it->second == sort); |  | ||||||
| 	} |  | ||||||
| public: | public: | ||||||
| 	class Factory; | 	class Factory; | ||||||
| 	// Node is an immutable reference to a FunctionalIR node
 | 	// Node is an immutable reference to a FunctionalIR node
 | ||||||
|  | @ -306,7 +293,7 @@ public: | ||||||
| 		virtual T logical_shift_right(Node self, Node a, Node b) = 0; | 		virtual T logical_shift_right(Node self, Node a, Node b) = 0; | ||||||
| 		virtual T arithmetic_shift_right(Node self, Node a, Node b) = 0; | 		virtual T arithmetic_shift_right(Node self, Node a, Node b) = 0; | ||||||
| 		virtual T mux(Node self, Node a, Node b, Node s) = 0; | 		virtual T mux(Node self, Node a, Node b, Node s) = 0; | ||||||
| 		virtual T constant(Node self, RTLIL::Const value) = 0; | 		virtual T constant(Node self, RTLIL::Const const & value) = 0; | ||||||
| 		virtual T input(Node self, IdString name) = 0; | 		virtual T input(Node self, IdString name) = 0; | ||||||
| 		virtual T state(Node self, IdString name) = 0; | 		virtual T state(Node self, IdString name) = 0; | ||||||
| 		virtual T memory_read(Node self, Node mem, Node addr) = 0; | 		virtual T memory_read(Node self, Node mem, Node addr) = 0; | ||||||
|  | @ -343,7 +330,7 @@ public: | ||||||
| 		T logical_shift_right(Node self, Node, Node) override { return default_handler(self); } | 		T logical_shift_right(Node self, Node, Node) override { return default_handler(self); } | ||||||
| 		T arithmetic_shift_right(Node self, Node, Node) override { return default_handler(self); } | 		T arithmetic_shift_right(Node self, Node, Node) override { return default_handler(self); } | ||||||
| 		T mux(Node self, Node, Node, Node) override { return default_handler(self); } | 		T mux(Node self, Node, Node, Node) override { return default_handler(self); } | ||||||
| 		T constant(Node self, RTLIL::Const) override { return default_handler(self); } | 		T constant(Node self, RTLIL::Const const &) override { return default_handler(self); } | ||||||
| 		T input(Node self, IdString) override { return default_handler(self); } | 		T input(Node self, IdString) override { return default_handler(self); } | ||||||
| 		T state(Node self, IdString) override { return default_handler(self); } | 		T state(Node self, IdString) override { return default_handler(self); } | ||||||
| 		T memory_read(Node self, Node, Node) override { return default_handler(self); } | 		T memory_read(Node self, Node, Node) override { return default_handler(self); } | ||||||
|  | @ -453,29 +440,40 @@ public: | ||||||
| 			log_assert(node.sort() == value.sort()); | 			log_assert(node.sort() == value.sort()); | ||||||
| 			mutate(node).append_arg(value._ref); | 			mutate(node).append_arg(value._ref); | ||||||
| 		} | 		} | ||||||
| 		Node input(IdString name, int width) { | 		void add_input(IdString name, int width) { | ||||||
| 			_ir.add_input(name, Sort(width)); | 			auto [it, inserted] = _ir._input_sorts.emplace(name, Sort(width)); | ||||||
| 			return add(NodeData(Fn::input, name), Sort(width), {}); | 			if (!inserted) log_error("input `%s` was re-defined", name.c_str()); | ||||||
| 		} | 		} | ||||||
| 		Node state(IdString name, int width) { | 		void add_output(IdString name, int width) { | ||||||
| 			_ir.add_state(name, Sort(width)); | 			auto [it, inserted] = _ir._output_sorts.emplace(name, Sort(width)); | ||||||
| 			return add(NodeData(Fn::state, name), Sort(width), {}); | 			if (!inserted) log_error("output `%s` was re-defined", name.c_str()); | ||||||
| 		} | 		} | ||||||
| 		Node state_memory(IdString name, int addr_width, int data_width) { | 		void add_state(IdString name, Sort sort) { | ||||||
| 			_ir.add_state(name, Sort(addr_width, data_width)); | 			auto [it, inserted] = _ir._state_sorts.emplace(name, sort); | ||||||
| 			return add(NodeData(Fn::state, name), Sort(addr_width, data_width), {}); | 			if (!inserted) log_error("state `%s` was re-defined", name.c_str()); | ||||||
| 		} | 		} | ||||||
| 		void declare_output(Node node, IdString name, int width) { | 		Node get_input(IdString name) { | ||||||
| 			_ir.add_output(name, Sort(width)); | 			return add(NodeData(Fn::input, name), Sort(_ir._input_sorts.at(name)), {}); | ||||||
| 			mutate(node).assign_key({name, false}); |  | ||||||
| 		} | 		} | ||||||
| 		void declare_state(Node node, IdString name, int width) { | 		Node get_current_state(IdString name) { | ||||||
| 			_ir.add_state(name, Sort(width)); | 			return add(NodeData(Fn::state, name), Sort(_ir._state_sorts.at(name)), {}); | ||||||
| 			mutate(node).assign_key({name, true}); |  | ||||||
| 		} | 		} | ||||||
| 		void declare_state_memory(Node node, IdString name, int addr_width, int data_width) { | 		void set_output(IdString output, Node value) { | ||||||
| 			_ir.add_state(name, Sort(addr_width, data_width)); | 			log_assert(_ir._output_sorts.at(output) == value.sort()); | ||||||
| 			mutate(node).assign_key({name, true}); | 			mutate(value).assign_key({output, false}); | ||||||
|  | 		} | ||||||
|  | 		void set_initial_state(IdString state, RTLIL::Const value) { | ||||||
|  | 			Sort &sort = _ir._state_sorts.at(state); | ||||||
|  | 			value.extu(sort.width()); | ||||||
|  | 			_ir._initial_state_signal.emplace(state, std::move(value)); | ||||||
|  | 		} | ||||||
|  | 		void set_initial_state(IdString state, MemContents value) { | ||||||
|  | 			log_assert(Sort(value.addr_width(), value.data_width()) == _ir._state_sorts.at(state)); | ||||||
|  | 			_ir._initial_state_memory.emplace(state, std::move(value)); | ||||||
|  | 		} | ||||||
|  | 		void set_next_state(IdString state, Node value) { | ||||||
|  | 			log_assert(_ir._state_sorts.at(state) == value.sort()); | ||||||
|  | 			mutate(value).assign_key({state, true}); | ||||||
| 		} | 		} | ||||||
| 		void suggest_name(Node node, IdString name) { | 		void suggest_name(Node node, IdString name) { | ||||||
| 			mutate(node).sparse_attr() = name; | 			mutate(node).sparse_attr() = name; | ||||||
|  | @ -487,9 +485,11 @@ public: | ||||||
| 	Node operator[](int i) { return Node(_graph[i]); } | 	Node operator[](int i) { return Node(_graph[i]); } | ||||||
| 	void topological_sort(); | 	void topological_sort(); | ||||||
| 	void forward_buf(); | 	void forward_buf(); | ||||||
| 	dict<IdString, Sort> inputs() const { return _inputs; } | 	dict<IdString, Sort> inputs() const { return _input_sorts; } | ||||||
| 	dict<IdString, Sort> outputs() const { return _outputs; } | 	dict<IdString, Sort> outputs() const { return _output_sorts; } | ||||||
| 	dict<IdString, Sort> state() const { return _state; } | 	dict<IdString, Sort> state() const { return _state_sorts; } | ||||||
|  | 	RTLIL::Const  const &get_initial_state_signal(IdString name) { return _initial_state_signal.at(name); } | ||||||
|  | 	MemContents const &get_initial_state_memory(IdString name) { return _initial_state_memory.at(name); } | ||||||
| 	Node get_output_node(IdString name) { return Node(_graph({name, false})); } | 	Node get_output_node(IdString name) { return Node(_graph({name, false})); } | ||||||
| 	Node get_state_next_node(IdString name) { return Node(_graph({name, true})); } | 	Node get_state_next_node(IdString name) { return Node(_graph({name, true})); } | ||||||
| 	class Iterator { | 	class Iterator { | ||||||
|  |  | ||||||
|  | @ -153,15 +153,17 @@ class FFCell(BaseCell): | ||||||
|         from test_functional import yosys_synth |         from test_functional import yosys_synth | ||||||
|         verilog_file = path.parent / 'verilog.v' |         verilog_file = path.parent / 'verilog.v' | ||||||
|         with open(verilog_file, 'w') as f: |         with open(verilog_file, 'w') as f: | ||||||
|             f.write(""" |             width = parameters['WIDTH'] | ||||||
|  |             f.write(f""" | ||||||
| module gold( | module gold( | ||||||
|     input wire clk, |     input wire clk, | ||||||
|     input wire [{0}:0] D, |     input wire [{width-1}:0] D, | ||||||
|     output reg [{0}:0] Q |     output reg [{width-1}:0] Q | ||||||
| ); | ); | ||||||
|  |     initial Q = {width}'b{("101" * width)[:width]}; | ||||||
|     always @(posedge clk) |     always @(posedge clk) | ||||||
|         Q <= D; |         Q <= D; | ||||||
| endmodule""".format(parameters['WIDTH'] - 1)) | endmodule""") | ||||||
|         yosys_synth(verilog_file, path) |         yosys_synth(verilog_file, path) | ||||||
| 
 | 
 | ||||||
| class MemCell(BaseCell): | class MemCell(BaseCell): | ||||||
|  | @ -180,6 +182,10 @@ module gold( | ||||||
|     output reg [{0}:0] RD |     output reg [{0}:0] RD | ||||||
| ); | ); | ||||||
|     reg [{0}:0] mem[0:{2}]; |     reg [{0}:0] mem[0:{2}]; | ||||||
|  |     integer i; | ||||||
|  |     initial | ||||||
|  |         for(i = 0; i <= {2}; i = i + 1) | ||||||
|  |             mem[i] = 9192 * (i + 1); | ||||||
|     always @(*) |     always @(*) | ||||||
|         RD = mem[RA]; |         RD = mem[RA]; | ||||||
|     always @(posedge clk) |     always @(posedge clk) | ||||||
|  | @ -211,8 +217,11 @@ module gold( | ||||||
|     output reg [{0}:0] RD1, |     output reg [{0}:0] RD1, | ||||||
|     output reg [{0}:0] RD2 |     output reg [{0}:0] RD2 | ||||||
| ); | ); | ||||||
|                     (*keep*) |  | ||||||
|     reg [{0}:0] mem[0:{2}]; |     reg [{0}:0] mem[0:{2}]; | ||||||
|  |     integer i; | ||||||
|  |     initial | ||||||
|  |         for(i = 0; i <= {2}; i = i + 1) | ||||||
|  |             mem[i] = 9192 * (i + 1); | ||||||
|     always @(*) |     always @(*) | ||||||
|         RD1 = mem[RA1]; |         RD1 = mem[RA1]; | ||||||
|     always @(*) |     always @(*) | ||||||
|  |  | ||||||
|  | @ -81,25 +81,6 @@ def simulate_smt_with_smtio(smt_file_path, vcd_path, smt_io, num_steps, rnd): | ||||||
|     parser.finish() |     parser.finish() | ||||||
|     assert smt_io.check_sat() == 'sat' |     assert smt_io.check_sat() == 'sat' | ||||||
| 
 | 
 | ||||||
|     def initial_state(states): |  | ||||||
|         mk_state_parts = [] |  | ||||||
|         rv = [] |  | ||||||
|         for name, width in states.items(): |  | ||||||
|             if isinstance(width, int): |  | ||||||
|                 binary_string = format(0, '0{}b'.format(width)) |  | ||||||
|                 mk_state_parts.append(f"#b{binary_string}") |  | ||||||
|             else: |  | ||||||
|                 binary_string = format(0, '0{}b'.format(width[1])) |  | ||||||
|                 rv.append(f"(declare-const test_state_initial_mem_{name} (Array (_ BitVec {width[0]}) (_ BitVec {width[1]})))") |  | ||||||
|                 rv.append(f"(assert (forall ((i (_ BitVec {width[0]}))) (= (select test_state_initial_mem_{name} i) #b{binary_string})))") |  | ||||||
|                 mk_state_parts.append(f"test_state_initial_mem_{name}") |  | ||||||
|         if len(states) == 0: |  | ||||||
|             mk_state_call = "gold_State" |  | ||||||
|         else: |  | ||||||
|             mk_state_call = "(gold_State {})".format(" ".join(mk_state_parts)) |  | ||||||
|         rv.append(f"(define-const test_state_step_n0 gold_State {mk_state_call})\n") |  | ||||||
|         return rv |  | ||||||
| 
 |  | ||||||
|     def set_step(inputs, step): |     def set_step(inputs, step): | ||||||
|         # This function assumes 'inputs' is a dictionary like {"A": 5, "B": 4} |         # This function assumes 'inputs' is a dictionary like {"A": 5, "B": 4} | ||||||
|         # and 'input_values' is a dictionary like {"A": 5, "B": 13} specifying the concrete values for each input. |         # and 'input_values' is a dictionary like {"A": 5, "B": 13} specifying the concrete values for each input. | ||||||
|  | @ -118,7 +99,7 @@ def simulate_smt_with_smtio(smt_file_path, vcd_path, smt_io, num_steps, rnd): | ||||||
|             f"(define-const test_state_step_n{step+1} gold_State (second test_results_step_n{step}))\n", |             f"(define-const test_state_step_n{step+1} gold_State (second test_results_step_n{step}))\n", | ||||||
|         ] |         ] | ||||||
| 
 | 
 | ||||||
|     smt_commands = initial_state(states) |     smt_commands = [f"(define-const test_state_step_n0 gold_State gold-initial)\n"] | ||||||
|     for step in range(num_steps): |     for step in range(num_steps): | ||||||
|         for step_command in set_step(inputs, step): |         for step_command in set_step(inputs, step): | ||||||
|             smt_commands.append(step_command) |             smt_commands.append(step_command) | ||||||
|  |  | ||||||
|  | @ -29,14 +29,14 @@ def yosys_synth(verilog_file, rtlil_file): | ||||||
| # simulate an rtlil file with yosys, comparing with a given vcd file, and writing out the yosys simulation results into a second vcd file | # simulate an rtlil file with yosys, comparing with a given vcd file, and writing out the yosys simulation results into a second vcd file | ||||||
| def yosys_sim(rtlil_file, vcd_reference_file, vcd_out_file, preprocessing = ""): | def yosys_sim(rtlil_file, vcd_reference_file, vcd_out_file, preprocessing = ""): | ||||||
|     try: |     try: | ||||||
|         yosys(f"read_rtlil {quote(rtlil_file)}; {preprocessing}; sim -r {quote(vcd_reference_file)} -scope gold -vcd {quote(vcd_out_file)} -timescale 1us -sim-gold") |         yosys(f"read_rtlil {quote(rtlil_file)}; {preprocessing}; sim -r {quote(vcd_reference_file)} -scope gold -vcd {quote(vcd_out_file)} -timescale 1us -sim-gold -fst-noinit") | ||||||
|     except: |     except: | ||||||
|         # if yosys sim fails it's probably because of a simulation mismatch |         # if yosys sim fails it's probably because of a simulation mismatch | ||||||
|         # since yosys sim aborts on simulation mismatch to generate vcd output |         # since yosys sim aborts on simulation mismatch to generate vcd output | ||||||
|         # we have to re-run with a different set of flags |         # we have to re-run with a different set of flags | ||||||
|         # on this run we ignore output and return code, we just want a best-effort attempt to get a vcd |         # on this run we ignore output and return code, we just want a best-effort attempt to get a vcd | ||||||
|         subprocess.run([base_path / 'yosys', '-Q', '-p', |         subprocess.run([base_path / 'yosys', '-Q', '-p', | ||||||
|             f'read_rtlil {quote(rtlil_file)}; sim -vcd {quote(vcd_out_file)} -a -r {quote(vcd_reference_file)} -scope gold -timescale 1us'], |             f'read_rtlil {quote(rtlil_file)}; sim -vcd {quote(vcd_out_file)} -a -r {quote(vcd_reference_file)} -scope gold -timescale 1us -fst-noinit'], | ||||||
|             capture_output=True, check=False) |             capture_output=True, check=False) | ||||||
|         raise |         raise | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -7,7 +7,11 @@ | ||||||
| 
 | 
 | ||||||
| #include "my_module_functional_cxx.cc" | #include "my_module_functional_cxx.cc" | ||||||
| 
 | 
 | ||||||
| std::string vcd_name_mangle(std::string name) { | class VcdFile { | ||||||
|  | 	std::ofstream &ofs; | ||||||
|  | 	std::string code_alloc = "!"; | ||||||
|  | 	std::unordered_map<std::string, std::string> codes; | ||||||
|  | 	std::string name_mangle(std::string name) { | ||||||
| 		std::string ret = name; | 		std::string ret = name; | ||||||
| 		bool escape = ret.empty() || !isalpha(ret[0]) && ret[0] != '_'; | 		bool escape = ret.empty() || !isalpha(ret[0]) && ret[0] != '_'; | ||||||
| 		for(size_t i = 0; i < ret.size(); i++) { | 		for(size_t i = 0; i < ret.size(); i++) { | ||||||
|  | @ -20,55 +24,76 @@ std::string vcd_name_mangle(std::string name) { | ||||||
| 		else | 		else | ||||||
| 			return ret; | 			return ret; | ||||||
| 	} | 	} | ||||||
| std::unordered_map<std::string, std::string> codes;  | 	std::string allocate_code() { | ||||||
| 
 | 		std::string ret = code_alloc; | ||||||
| struct DumpHeader { | 		for (size_t i = 0; i < code_alloc.size(); i++) | ||||||
|   std::ofstream &ofs; | 			if (code_alloc[i]++ == '~') | ||||||
|   std::string code = "!"; | 				code_alloc[i] = '!'; | ||||||
|   DumpHeader(std::ofstream &ofs) : ofs(ofs) {} |  | ||||||
|   void increment_code() { |  | ||||||
|     for(size_t i = 0; i < code.size(); i++) |  | ||||||
|       if(code[i]++ == '~') |  | ||||||
|         code[i] = '!'; |  | ||||||
| 			else | 			else | ||||||
|         return; | 				return ret; | ||||||
|     code.push_back('!'); | 		code_alloc.push_back('!'); | ||||||
|  | 		return ret; | ||||||
| 	} | 	} | ||||||
|   template <size_t n> | public: | ||||||
|   void operator()(const char *name, Signal<n> value) { | 	VcdFile(std::ofstream &ofs) : ofs(ofs) {} | ||||||
|     ofs << "$var wire " << n << " " << code << " " << vcd_name_mangle(name) << " $end\n"; | 	struct DumpHeader { | ||||||
|     codes[name] = code; | 		VcdFile *file; | ||||||
|     increment_code(); | 		explicit DumpHeader(VcdFile *file) : file(file) {} | ||||||
|   } | 		template <size_t n> void operator()(const char *name, Signal<n> value) | ||||||
|   template <size_t n, size_t m> | 		{ | ||||||
|   void operator()(const char *name, Memory<n, m> value) { | 			auto it = file->codes.find(name); | ||||||
|  | 			if(it == file->codes.end()) | ||||||
|  | 				it = file->codes.emplace(name, file->allocate_code()).first; | ||||||
|  | 			file->ofs << "$var wire " << n << " " << it->second << " " << file->name_mangle(name) << " $end\n"; | ||||||
| 		} | 		} | ||||||
|  | 		template <size_t n, size_t m> void operator()(const char *name, Memory<n, m> value) {} | ||||||
| 	}; | 	}; | ||||||
| 
 |  | ||||||
| 	struct Dump { | 	struct Dump { | ||||||
|   std::ofstream &ofs; | 		VcdFile *file; | ||||||
|   Dump(std::ofstream &ofs) : ofs(ofs) {} | 		explicit Dump(VcdFile *file) : file(file) {} | ||||||
|   template <size_t n> | 		template <size_t n> void operator()(const char *name, Signal<n> value) | ||||||
|   void operator()(const char *name, Signal<n> value) { | 		{ | ||||||
|     // Bit
 |  | ||||||
| 			if (n == 1) { | 			if (n == 1) { | ||||||
|       ofs << (value[0] ? '1' : '0'); | 				file->ofs << (value[0] ? '1' : '0'); | ||||||
|       ofs << codes[name] << "\n"; | 				file->ofs << file->codes.at(name) << "\n"; | ||||||
|       return; | 			} else { | ||||||
|     } | 				file->ofs << "b"; | ||||||
|     // vector (multi-bit) signals
 |  | ||||||
|     ofs << "b"; |  | ||||||
| 				for (size_t i = n; i-- > 0;) | 				for (size_t i = n; i-- > 0;) | ||||||
|       ofs << (value[i] ? '1' : '0'); | 					file->ofs << (value[i] ? '1' : '0'); | ||||||
|     ofs << " " << codes[name] << "\n"; | 				file->ofs << " " << file->codes.at(name) << "\n"; | ||||||
| 			} | 			} | ||||||
|   template <size_t n, size_t m> |  | ||||||
|   void operator()(const char *name, Memory<n, m> value) { |  | ||||||
| 		} | 		} | ||||||
|  | 		template <size_t n, size_t m> void operator()(const char *name, Memory<n, m> value) {} | ||||||
|  | 	}; | ||||||
|  | 	void begin_header() { | ||||||
|  | 		constexpr int number_timescale = 1; | ||||||
|  | 		const std::string units_timescale = "us"; | ||||||
|  | 		ofs << "$timescale " << number_timescale << " " << units_timescale << " $end\n"; | ||||||
|  | 		ofs << "$scope module gold $end\n"; | ||||||
|  | 	} | ||||||
|  | 	void end_header() { | ||||||
|  | 		ofs << "$enddefinitions $end\n$dumpvars\n"; | ||||||
|  | 	} | ||||||
|  | 	template<typename... Args> void header(Args ...args) { | ||||||
|  | 		begin_header(); | ||||||
|  | 		DumpHeader d(this); | ||||||
|  | 		(args.visit(d), ...); | ||||||
|  | 		end_header(); | ||||||
|  | 	} | ||||||
|  | 	void begin_data(int step) { | ||||||
|  | 		ofs << "#" << step << "\n"; | ||||||
|  | 	} | ||||||
|  | 	template<typename... Args> void data(int step, Args ...args) { | ||||||
|  | 		begin_data(step); | ||||||
|  | 		Dump d(this); | ||||||
|  | 		(args.visit(d), ...); | ||||||
|  | 	} | ||||||
|  | 	DumpHeader dump_header() { return DumpHeader(this); } | ||||||
|  | 	Dump dump() { return Dump(this); } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| template<size_t n> | template <size_t n> Signal<n> random_signal(std::mt19937 &gen) | ||||||
| Signal<n> random_signal(std::mt19937 &gen) { | { | ||||||
| 	std::uniform_int_distribution<uint32_t> dist; | 	std::uniform_int_distribution<uint32_t> dist; | ||||||
| 	std::array<uint32_t, (n + 31) / 32> words; | 	std::array<uint32_t, (n + 31) / 32> words; | ||||||
| 	for (auto &w : words) | 	for (auto &w : words) | ||||||
|  | @ -76,21 +101,11 @@ Signal<n> random_signal(std::mt19937 &gen) { | ||||||
| 	return Signal<n>::from_array(words); | 	return Signal<n>::from_array(words); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct Reset { |  | ||||||
|   template <size_t n> |  | ||||||
|   void operator()(const char *, Signal<n> &signal) { |  | ||||||
|     signal = 0; |  | ||||||
|   } |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct Randomize { | struct Randomize { | ||||||
| 	std::mt19937 &gen; | 	std::mt19937 &gen; | ||||||
| 	Randomize(std::mt19937 &gen) : gen(gen) {} | 	Randomize(std::mt19937 &gen) : gen(gen) {} | ||||||
| 
 | 
 | ||||||
|   template <size_t n> | 	template <size_t n> void operator()(const char *, Signal<n> &signal) { signal = random_signal<n>(gen); } | ||||||
|   void operator()(const char *, Signal<n> &signal) { |  | ||||||
|     signal = random_signal<n>(gen); |  | ||||||
|   } |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| int main(int argc, char **argv) | int main(int argc, char **argv) | ||||||
|  | @ -104,43 +119,27 @@ int main(int argc, char **argv) | ||||||
| 	const int steps = atoi(argv[2]); | 	const int steps = atoi(argv[2]); | ||||||
| 	const uint32_t seed = atoi(argv[3]); | 	const uint32_t seed = atoi(argv[3]); | ||||||
| 
 | 
 | ||||||
|   constexpr int number_timescale = 1; |  | ||||||
|   const std::string units_timescale = "us"; |  | ||||||
| 	gold::Inputs inputs; | 	gold::Inputs inputs; | ||||||
| 	gold::Outputs outputs; | 	gold::Outputs outputs; | ||||||
| 	gold::State state; | 	gold::State state; | ||||||
| 	gold::State next_state; | 	gold::State next_state; | ||||||
| 
 | 
 | ||||||
| 	std::ofstream vcd_file(functional_vcd_filename); | 	std::ofstream vcd_file(functional_vcd_filename); | ||||||
|  | 	VcdFile vcd(vcd_file); | ||||||
|  | 	vcd.header(inputs, outputs, state); | ||||||
| 
 | 
 | ||||||
|   vcd_file << "$timescale " << number_timescale << " " << units_timescale << " $end\n"; |  | ||||||
|   vcd_file << "$scope module gold $end\n"; |  | ||||||
|   { |  | ||||||
|     DumpHeader d(vcd_file); |  | ||||||
|     inputs.visit(d); |  | ||||||
|     outputs.visit(d); |  | ||||||
|     state.visit(d); |  | ||||||
|   } |  | ||||||
|   vcd_file << "$enddefinitions $end\n$dumpvars\n"; |  | ||||||
| 	std::mt19937 gen(seed); | 	std::mt19937 gen(seed); | ||||||
| 
 | 
 | ||||||
|   inputs.visit(Reset()); | 	gold::initialize(state); | ||||||
| 
 | 
 | ||||||
| 	for (int step = 0; step < steps; ++step) { | 	for (int step = 0; step < steps; ++step) { | ||||||
|     vcd_file << "#" << step << "\n"; | 		inputs.visit(Randomize(gen)); | ||||||
|  | 
 | ||||||
| 		gold::eval(inputs, outputs, state, next_state); | 		gold::eval(inputs, outputs, state, next_state); | ||||||
|     { | 		vcd.data(step, inputs, outputs, state); | ||||||
|       Dump d(vcd_file); |  | ||||||
|       inputs.visit(d); |  | ||||||
|       outputs.visit(d); |  | ||||||
|       state.visit(d); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
| 		state = next_state; | 		state = next_state; | ||||||
|     inputs.visit(Randomize(gen)); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|   vcd_file.close(); |  | ||||||
| 
 |  | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue