mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +00:00 
			
		
		
		
	cxxrtl: Convert to Mem helpers.
This *only* does conversion, but doesn't add any new functionality — support for memory read port init/reset is still upcoming.
This commit is contained in:
		
							parent
							
								
									7f12820b26
								
							
						
					
					
						commit
						d5c9595668
					
				
					 1 changed files with 276 additions and 206 deletions
				
			
		|  | @ -216,7 +216,7 @@ bool is_internal_cell(RTLIL::IdString type) | |||
| 
 | ||||
| bool is_effectful_cell(RTLIL::IdString type) | ||||
| { | ||||
| 	return type == ID($memwr) || type.isPublic(); | ||||
| 	return type.isPublic(); | ||||
| } | ||||
| 
 | ||||
| bool is_cxxrtl_blackbox_cell(const RTLIL::Cell *cell) | ||||
|  | @ -274,12 +274,16 @@ struct FlowGraph { | |||
| 			CELL_EVAL, | ||||
| 			PROCESS_SYNC, | ||||
| 			PROCESS_CASE, | ||||
| 			MEM_RDPORT, | ||||
| 			MEM_WRPORTS, | ||||
| 		}; | ||||
| 
 | ||||
| 		Type type; | ||||
| 		RTLIL::SigSig connect = {}; | ||||
| 		const RTLIL::Cell *cell = NULL; | ||||
| 		const RTLIL::Process *process = NULL; | ||||
| 		const RTLIL::Cell *cell = nullptr; | ||||
| 		const RTLIL::Process *process = nullptr; | ||||
| 		const Mem *mem = nullptr; | ||||
| 		int portidx; | ||||
| 	}; | ||||
| 
 | ||||
| 	std::vector<Node*> nodes; | ||||
|  | @ -414,7 +418,7 @@ struct FlowGraph { | |||
| 			if (cell->output(conn.first)) { | ||||
| 				if (is_inlinable_cell(cell->type)) | ||||
| 					add_defs(node, conn.second, /*is_ff=*/false, /*inlinable=*/true); | ||||
| 				else if (is_ff_cell(cell->type) || (cell->type == ID($memrd) && cell->getParam(ID::CLK_ENABLE).as_bool())) | ||||
| 				else if (is_ff_cell(cell->type)) | ||||
| 					add_defs(node, conn.second, /*is_ff=*/true,  /*inlinable=*/false); | ||||
| 				else if (is_internal_cell(cell->type)) | ||||
| 					add_defs(node, conn.second, /*is_ff=*/false, /*inlinable=*/false); | ||||
|  | @ -502,6 +506,49 @@ struct FlowGraph { | |||
| 		add_case_rule_defs_uses(node, &process->root_case); | ||||
| 		return node; | ||||
| 	} | ||||
| 
 | ||||
| 	// Memories
 | ||||
| 	void add_node(const Mem *mem) { | ||||
| 		for (int i = 0; i < GetSize(mem->rd_ports); i++) { | ||||
| 			auto &port = mem->rd_ports[i]; | ||||
| 			Node *node = new Node; | ||||
| 			node->type = Node::Type::MEM_RDPORT; | ||||
| 			node->mem = mem; | ||||
| 			node->portidx = i; | ||||
| 			nodes.push_back(node); | ||||
| 			add_defs(node, port.data, /*is_ff=*/port.clk_enable, /*inlinable=*/false); | ||||
| 			add_uses(node, port.clk); | ||||
| 			add_uses(node, port.en); | ||||
| 			add_uses(node, port.arst); | ||||
| 			add_uses(node, port.srst); | ||||
| 			add_uses(node, port.addr); | ||||
| 			if (port.transparent && port.clk_enable) { | ||||
| 				// Our implementation of transparent read ports reads en, addr and data from every write port
 | ||||
| 				// in the same domain.
 | ||||
| 				for (auto &wrport : mem->wr_ports) { | ||||
| 					if (wrport.clk_enable && wrport.clk == port.clk && wrport.clk_polarity == port.clk_polarity) { | ||||
| 						add_uses(node, wrport.en); | ||||
| 						add_uses(node, wrport.addr); | ||||
| 						add_uses(node, wrport.data); | ||||
| 					} | ||||
| 				} | ||||
| 				// Also we read the address twice in this case (prevent inlining).
 | ||||
| 				add_uses(node, port.addr); | ||||
| 			} | ||||
| 		} | ||||
| 		if (!mem->wr_ports.empty()) { | ||||
| 			Node *node = new Node; | ||||
| 			node->type = Node::Type::MEM_WRPORTS; | ||||
| 			node->mem = mem; | ||||
| 			nodes.push_back(node); | ||||
| 			for (auto &port : mem->wr_ports) { | ||||
| 				add_uses(node, port.clk); | ||||
| 				add_uses(node, port.en); | ||||
| 				add_uses(node, port.addr); | ||||
| 				add_uses(node, port.data); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| std::vector<std::string> split_by(const std::string &str, const std::string &sep) | ||||
|  | @ -637,10 +684,9 @@ struct CxxrtlWorker { | |||
| 	int temporary = 0; | ||||
| 
 | ||||
| 	dict<const RTLIL::Module*, SigMap> sigmaps; | ||||
| 	dict<const RTLIL::Module*, std::vector<Mem>> mod_memories; | ||||
| 	pool<const RTLIL::Wire*> edge_wires; | ||||
| 	dict<RTLIL::SigBit, RTLIL::SyncType> edge_types; | ||||
| 	pool<const RTLIL::Memory*> writable_memories; | ||||
| 	dict<const RTLIL::Cell*, pool<const RTLIL::Cell*>> transparent_for; | ||||
| 	dict<const RTLIL::Module*, std::vector<FlowGraph::Node>> schedule, debug_schedule; | ||||
| 	dict<const RTLIL::Wire*, WireType> wire_types, debug_wire_types; | ||||
| 	dict<RTLIL::SigBit, bool> bit_has_state; | ||||
|  | @ -724,9 +770,9 @@ struct CxxrtlWorker { | |||
| 		return mangle_module_name(module->name, /*is_blackbox=*/module->get_bool_attribute(ID(cxxrtl_blackbox))); | ||||
| 	} | ||||
| 
 | ||||
| 	std::string mangle(const RTLIL::Memory *memory) | ||||
| 	std::string mangle(const Mem *mem) | ||||
| 	{ | ||||
| 		return mangle_memory_name(memory->name); | ||||
| 		return mangle_memory_name(mem->memid); | ||||
| 	} | ||||
| 
 | ||||
| 	std::string mangle(const RTLIL::Cell *cell) | ||||
|  | @ -1216,114 +1262,6 @@ struct CxxrtlWorker { | |||
| 				dump_sigspec_rhs(cell->getPort(ID::CLR)); | ||||
| 				f << (cell->getParam(ID::CLR_POLARITY).as_bool() ? "" : ".bit_not()") << ");\n"; | ||||
| 			} | ||||
| 		// Memory ports
 | ||||
| 		} else if (cell->type.in(ID($memrd), ID($memwr))) { | ||||
| 			if (cell->getParam(ID::CLK_ENABLE).as_bool()) { | ||||
| 				log_assert(!for_debug); | ||||
| 				RTLIL::SigBit clk_bit = cell->getPort(ID::CLK)[0]; | ||||
| 				clk_bit = sigmaps[clk_bit.wire->module](clk_bit); | ||||
| 				if (clk_bit.wire) { | ||||
| 					f << indent << "if (" << (cell->getParam(ID::CLK_POLARITY).as_bool() ? "posedge_" : "negedge_") | ||||
| 					            << mangle(clk_bit) << ") {\n"; | ||||
| 				} else { | ||||
| 					f << indent << "if (false) {\n"; | ||||
| 				} | ||||
| 				inc_indent(); | ||||
| 			} | ||||
| 			RTLIL::Memory *memory = cell->module->memories[cell->getParam(ID::MEMID).decode_string()]; | ||||
| 			std::string valid_index_temp = fresh_temporary(); | ||||
| 			f << indent << "auto " << valid_index_temp << " = memory_index("; | ||||
| 			// Almost all non-elidable cells cannot appear in debug_eval(), but $memrd is an exception; asynchronous
 | ||||
| 			// memory read ports can.
 | ||||
| 			dump_sigspec_rhs(cell->getPort(ID::ADDR), for_debug); | ||||
| 			f << ", " << memory->start_offset << ", " << memory->size << ");\n"; | ||||
| 			if (cell->type == ID($memrd)) { | ||||
| 				bool has_enable = cell->getParam(ID::CLK_ENABLE).as_bool() && !cell->getPort(ID::EN).is_fully_ones(); | ||||
| 				if (has_enable) { | ||||
| 					f << indent << "if ("; | ||||
| 					dump_sigspec_rhs(cell->getPort(ID::EN)); | ||||
| 					f << ") {\n"; | ||||
| 					inc_indent(); | ||||
| 				} | ||||
| 				// The generated code has two bounds checks; one in an assertion, and another that guards the read.
 | ||||
| 				// This is done so that the code does not invoke undefined behavior under any conditions, but nevertheless
 | ||||
| 				// loudly crashes if an illegal condition is encountered. The assert may be turned off with -DCXXRTL_NDEBUG
 | ||||
| 				// not only for release builds, but also to make sure the simulator (which is presumably embedded in some
 | ||||
| 				// larger program) will never crash the code that calls into it.
 | ||||
| 				//
 | ||||
| 				// If assertions are disabled, out of bounds reads are defined to return zero.
 | ||||
| 				f << indent << "CXXRTL_ASSERT(" << valid_index_temp << ".valid && \"out of bounds read\");\n"; | ||||
| 				f << indent << "if(" << valid_index_temp << ".valid) {\n"; | ||||
| 				inc_indent(); | ||||
| 					if (writable_memories[memory]) { | ||||
| 						std::string lhs_temp = fresh_temporary(); | ||||
| 						f << indent << "value<" << memory->width << "> " << lhs_temp << " = " | ||||
| 						            << mangle(memory) << "[" << valid_index_temp << ".index];\n"; | ||||
| 						std::vector<const RTLIL::Cell*> memwr_cells(transparent_for[cell].begin(), transparent_for[cell].end()); | ||||
| 						if (!memwr_cells.empty()) { | ||||
| 							std::string addr_temp = fresh_temporary(); | ||||
| 							f << indent << "const value<" << cell->getPort(ID::ADDR).size() << "> &" << addr_temp << " = "; | ||||
| 							dump_sigspec_rhs(cell->getPort(ID::ADDR)); | ||||
| 							f << ";\n"; | ||||
| 							std::sort(memwr_cells.begin(), memwr_cells.end(), | ||||
| 								[](const RTLIL::Cell *a, const RTLIL::Cell *b) { | ||||
| 									return a->getParam(ID::PRIORITY).as_int() < b->getParam(ID::PRIORITY).as_int(); | ||||
| 								}); | ||||
| 							for (auto memwr_cell : memwr_cells) { | ||||
| 								f << indent << "if (" << addr_temp << " == "; | ||||
| 								dump_sigspec_rhs(memwr_cell->getPort(ID::ADDR)); | ||||
| 								f << ") {\n"; | ||||
| 								inc_indent(); | ||||
| 									f << indent << lhs_temp << " = " << lhs_temp; | ||||
| 									f << ".update("; | ||||
| 									dump_sigspec_rhs(memwr_cell->getPort(ID::DATA)); | ||||
| 									f << ", "; | ||||
| 									dump_sigspec_rhs(memwr_cell->getPort(ID::EN)); | ||||
| 									f << ");\n"; | ||||
| 								dec_indent(); | ||||
| 								f << indent << "}\n"; | ||||
| 							} | ||||
| 						} | ||||
| 						f << indent; | ||||
| 						dump_sigspec_lhs(cell->getPort(ID::DATA)); | ||||
| 						f << " = " << lhs_temp << ";\n"; | ||||
| 					} else { | ||||
| 						f << indent; | ||||
| 						dump_sigspec_lhs(cell->getPort(ID::DATA)); | ||||
| 						f << " = " << mangle(memory) << "[" << valid_index_temp << ".index];\n"; | ||||
| 					} | ||||
| 				dec_indent(); | ||||
| 				f << indent << "} else {\n"; | ||||
| 				inc_indent(); | ||||
| 					f << indent; | ||||
| 					dump_sigspec_lhs(cell->getPort(ID::DATA)); | ||||
| 					f << " = value<" << memory->width << "> {};\n"; | ||||
| 				dec_indent(); | ||||
| 				f << indent << "}\n"; | ||||
| 				if (has_enable) { | ||||
| 					dec_indent(); | ||||
| 					f << indent << "}\n"; | ||||
| 				} | ||||
| 			} else /*if (cell->type == ID($memwr))*/ { | ||||
| 				log_assert(writable_memories[memory]); | ||||
| 				// See above for rationale of having both the assert and the condition.
 | ||||
| 				//
 | ||||
| 				// If assertions are disabled, out of bounds writes are defined to do nothing.
 | ||||
| 				f << indent << "CXXRTL_ASSERT(" << valid_index_temp << ".valid && \"out of bounds write\");\n"; | ||||
| 				f << indent << "if (" << valid_index_temp << ".valid) {\n"; | ||||
| 				inc_indent(); | ||||
| 					f << indent << mangle(memory) << ".update(" << valid_index_temp << ".index, "; | ||||
| 					dump_sigspec_rhs(cell->getPort(ID::DATA)); | ||||
| 					f << ", "; | ||||
| 					dump_sigspec_rhs(cell->getPort(ID::EN)); | ||||
| 					f << ", " << cell->getParam(ID::PRIORITY).as_int() << ");\n"; | ||||
| 				dec_indent(); | ||||
| 				f << indent << "}\n"; | ||||
| 			} | ||||
| 			if (cell->getParam(ID::CLK_ENABLE).as_bool()) { | ||||
| 				dec_indent(); | ||||
| 				f << indent << "}\n"; | ||||
| 			} | ||||
| 		// Internal cells
 | ||||
| 		} else if (is_internal_cell(cell->type)) { | ||||
| 			log_cmd_error("Unsupported internal cell `%s'.\n", cell->type.c_str()); | ||||
|  | @ -1567,6 +1505,161 @@ struct CxxrtlWorker { | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	void dump_mem_rdport(const Mem *mem, int portidx, bool for_debug = false) | ||||
| 	{ | ||||
| 		auto &port = mem->rd_ports[portidx]; | ||||
| 		dump_attrs(&port); | ||||
| 		f << indent << "// memory " << mem->memid.str() << " read port " << portidx << "\n"; | ||||
| 		if (port.clk_enable) { | ||||
| 			log_assert(!for_debug); | ||||
| 			RTLIL::SigBit clk_bit = port.clk[0]; | ||||
| 			clk_bit = sigmaps[clk_bit.wire->module](clk_bit); | ||||
| 			if (clk_bit.wire) { | ||||
| 				f << indent << "if (" << (port.clk_polarity ? "posedge_" : "negedge_") | ||||
| 					    << mangle(clk_bit) << ") {\n"; | ||||
| 			} else { | ||||
| 				f << indent << "if (false) {\n"; | ||||
| 			} | ||||
| 			inc_indent(); | ||||
| 		} | ||||
| 		std::vector<const RTLIL::Cell*> inlined_cells_addr; | ||||
| 		collect_sigspec_rhs(port.addr, for_debug, inlined_cells_addr); | ||||
| 		if (!inlined_cells_addr.empty()) | ||||
| 			dump_inlined_cells(inlined_cells_addr); | ||||
| 		std::string valid_index_temp = fresh_temporary(); | ||||
| 		f << indent << "auto " << valid_index_temp << " = memory_index("; | ||||
| 		// Almost all non-elidable cells cannot appear in debug_eval(), but $memrd is an exception; asynchronous
 | ||||
| 		// memory read ports can.
 | ||||
| 		dump_sigspec_rhs(port.addr, for_debug); | ||||
| 		f << ", " << mem->start_offset << ", " << mem->size << ");\n"; | ||||
| 		bool has_enable = port.clk_enable && !port.en.is_fully_ones(); | ||||
| 		if (has_enable) { | ||||
| 			std::vector<const RTLIL::Cell*> inlined_cells_en; | ||||
| 			collect_sigspec_rhs(port.en, for_debug, inlined_cells_en); | ||||
| 			if (!inlined_cells_en.empty()) | ||||
| 				dump_inlined_cells(inlined_cells_en); | ||||
| 			f << indent << "if ("; | ||||
| 			dump_sigspec_rhs(port.en); | ||||
| 			f << ") {\n"; | ||||
| 			inc_indent(); | ||||
| 		} | ||||
| 		// The generated code has two bounds checks; one in an assertion, and another that guards the read.
 | ||||
| 		// This is done so that the code does not invoke undefined behavior under any conditions, but nevertheless
 | ||||
| 		// loudly crashes if an illegal condition is encountered. The assert may be turned off with -DCXXRTL_NDEBUG
 | ||||
| 		// not only for release builds, but also to make sure the simulator (which is presumably embedded in some
 | ||||
| 		// larger program) will never crash the code that calls into it.
 | ||||
| 		//
 | ||||
| 		// If assertions are disabled, out of bounds reads are defined to return zero.
 | ||||
| 		f << indent << "CXXRTL_ASSERT(" << valid_index_temp << ".valid && \"out of bounds read\");\n"; | ||||
| 		f << indent << "if(" << valid_index_temp << ".valid) {\n"; | ||||
| 		inc_indent(); | ||||
| 			if (!mem->wr_ports.empty()) { | ||||
| 				std::string lhs_temp = fresh_temporary(); | ||||
| 				f << indent << "value<" << mem->width << "> " << lhs_temp << " = " | ||||
| 					    << mangle(mem) << "[" << valid_index_temp << ".index];\n"; | ||||
| 				if (port.transparent && port.clk_enable) { | ||||
| 					std::string addr_temp = fresh_temporary(); | ||||
| 					f << indent << "const value<" << port.addr.size() << "> &" << addr_temp << " = "; | ||||
| 					dump_sigspec_rhs(port.addr); | ||||
| 					f << ";\n"; | ||||
| 					for (auto &wrport : mem->wr_ports) { | ||||
| 						if (!wrport.clk_enable) | ||||
| 							continue; | ||||
| 						if (wrport.clk != port.clk) | ||||
| 							continue; | ||||
| 						if (wrport.clk_polarity != port.clk_polarity) | ||||
| 							continue; | ||||
| 						f << indent << "if (" << addr_temp << " == "; | ||||
| 						dump_sigspec_rhs(wrport.addr); | ||||
| 						f << ") {\n"; | ||||
| 						inc_indent(); | ||||
| 							f << indent << lhs_temp << " = " << lhs_temp; | ||||
| 							f << ".update("; | ||||
| 							dump_sigspec_rhs(wrport.data); | ||||
| 							f << ", "; | ||||
| 							dump_sigspec_rhs(wrport.en); | ||||
| 							f << ");\n"; | ||||
| 						dec_indent(); | ||||
| 						f << indent << "}\n"; | ||||
| 					} | ||||
| 				} | ||||
| 				f << indent; | ||||
| 				dump_sigspec_lhs(port.data); | ||||
| 				f << " = " << lhs_temp << ";\n"; | ||||
| 			} else { | ||||
| 				f << indent; | ||||
| 				dump_sigspec_lhs(port.data); | ||||
| 				f << " = " << mangle(mem) << "[" << valid_index_temp << ".index];\n"; | ||||
| 			} | ||||
| 		dec_indent(); | ||||
| 		f << indent << "} else {\n"; | ||||
| 		inc_indent(); | ||||
| 			f << indent; | ||||
| 			dump_sigspec_lhs(port.data); | ||||
| 			f << " = value<" << mem->width << "> {};\n"; | ||||
| 		dec_indent(); | ||||
| 		f << indent << "}\n"; | ||||
| 		if (has_enable) { | ||||
| 			dec_indent(); | ||||
| 			f << indent << "}\n"; | ||||
| 		} | ||||
| 		if (port.clk_enable) { | ||||
| 			dec_indent(); | ||||
| 			f << indent << "}\n"; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	void dump_mem_wrports(const Mem *mem, bool for_debug = false) | ||||
| 	{ | ||||
| 		log_assert(!for_debug); | ||||
| 		for (int portidx = 0; portidx < GetSize(mem->wr_ports); portidx++) { | ||||
| 			auto &port = mem->wr_ports[portidx]; | ||||
| 			dump_attrs(&port); | ||||
| 			f << indent << "// memory " << mem->memid.str() << " write port " << portidx << "\n"; | ||||
| 			if (port.clk_enable) { | ||||
| 				RTLIL::SigBit clk_bit = port.clk[0]; | ||||
| 				clk_bit = sigmaps[clk_bit.wire->module](clk_bit); | ||||
| 				if (clk_bit.wire) { | ||||
| 					f << indent << "if (" << (port.clk_polarity ? "posedge_" : "negedge_") | ||||
| 					            << mangle(clk_bit) << ") {\n"; | ||||
| 				} else { | ||||
| 					f << indent << "if (false) {\n"; | ||||
| 				} | ||||
| 				inc_indent(); | ||||
| 			} | ||||
| 			std::vector<const RTLIL::Cell*> inlined_cells_addr; | ||||
| 			collect_sigspec_rhs(port.addr, for_debug, inlined_cells_addr); | ||||
| 			if (!inlined_cells_addr.empty()) | ||||
| 				dump_inlined_cells(inlined_cells_addr); | ||||
| 			std::string valid_index_temp = fresh_temporary(); | ||||
| 			f << indent << "auto " << valid_index_temp << " = memory_index("; | ||||
| 			dump_sigspec_rhs(port.addr); | ||||
| 			f << ", " << mem->start_offset << ", " << mem->size << ");\n"; | ||||
| 			// See above for rationale of having both the assert and the condition.
 | ||||
| 			//
 | ||||
| 			// If assertions are disabled, out of bounds writes are defined to do nothing.
 | ||||
| 			f << indent << "CXXRTL_ASSERT(" << valid_index_temp << ".valid && \"out of bounds write\");\n"; | ||||
| 			f << indent << "if (" << valid_index_temp << ".valid) {\n"; | ||||
| 			inc_indent(); | ||||
| 				std::vector<const RTLIL::Cell*> inlined_cells; | ||||
| 				collect_sigspec_rhs(port.data, for_debug, inlined_cells); | ||||
| 				collect_sigspec_rhs(port.en, for_debug, inlined_cells); | ||||
| 				if (!inlined_cells.empty()) | ||||
| 					dump_inlined_cells(inlined_cells); | ||||
| 				f << indent << mangle(mem) << ".update(" << valid_index_temp << ".index, "; | ||||
| 				dump_sigspec_rhs(port.data); | ||||
| 				f << ", "; | ||||
| 				dump_sigspec_rhs(port.en); | ||||
| 				f << ", " << portidx << ");\n"; | ||||
| 			dec_indent(); | ||||
| 			f << indent << "}\n"; | ||||
| 			if (port.clk_enable) { | ||||
| 				dec_indent(); | ||||
| 				f << indent << "}\n"; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	void dump_wire(const RTLIL::Wire *wire, bool is_local) | ||||
| 	{ | ||||
| 		const auto &wire_type = wire_types[wire]; | ||||
|  | @ -1650,41 +1743,28 @@ struct CxxrtlWorker { | |||
| 		f << "value<" << wire->width << "> " << mangle(wire) << ";\n"; | ||||
| 	} | ||||
| 
 | ||||
| 	void dump_memory(RTLIL::Module *module, const RTLIL::Memory *memory) | ||||
| 	void dump_memory(Mem *mem) | ||||
| 	{ | ||||
| 		vector<const RTLIL::Cell*> init_cells; | ||||
| 		for (auto cell : module->cells()) | ||||
| 			if (cell->type == ID($meminit) && cell->getParam(ID::MEMID).decode_string() == memory->name.str()) | ||||
| 				init_cells.push_back(cell); | ||||
| 
 | ||||
| 		std::sort(init_cells.begin(), init_cells.end(), [](const RTLIL::Cell *a, const RTLIL::Cell *b) { | ||||
| 			int a_addr = a->getPort(ID::ADDR).as_int(), b_addr = b->getPort(ID::ADDR).as_int(); | ||||
| 			int a_prio = a->getParam(ID::PRIORITY).as_int(), b_prio = b->getParam(ID::PRIORITY).as_int(); | ||||
| 			return a_prio > b_prio || (a_prio == b_prio && a_addr < b_addr); | ||||
| 		}); | ||||
| 
 | ||||
| 		dump_attrs(memory); | ||||
| 		f << indent << "memory<" << memory->width << "> " << mangle(memory) | ||||
| 		            << " { " << memory->size << "u"; | ||||
| 		if (init_cells.empty()) { | ||||
| 		dump_attrs(mem); | ||||
| 		f << indent << "memory<" << mem->width << "> " << mangle(mem) | ||||
| 		            << " { " << mem->size << "u"; | ||||
| 		if (!GetSize(mem->inits)) { | ||||
| 			f << " };\n"; | ||||
| 		} else { | ||||
| 			f << ",\n"; | ||||
| 			inc_indent(); | ||||
| 				for (auto cell : init_cells) { | ||||
| 					dump_attrs(cell); | ||||
| 					RTLIL::Const data = cell->getPort(ID::DATA).as_const(); | ||||
| 					size_t width = cell->getParam(ID::WIDTH).as_int(); | ||||
| 					size_t words = cell->getParam(ID::WORDS).as_int(); | ||||
| 					f << indent << "memory<" << memory->width << ">::init<" << words << "> { " | ||||
| 					            << stringf("%#x", cell->getPort(ID::ADDR).as_int()) << ", {"; | ||||
| 				for (auto &init : mem->inits) { | ||||
| 					dump_attrs(&init); | ||||
| 					int words = GetSize(init.data) / mem->width; | ||||
| 					f << indent << "memory<" << mem->width << ">::init<" << words << "> { " | ||||
| 						    << stringf("%#x", init.addr.as_int()) << ", {"; | ||||
| 					inc_indent(); | ||||
| 						for (size_t n = 0; n < words; n++) { | ||||
| 						for (int n = 0; n < words; n++) { | ||||
| 							if (n % 4 == 0) | ||||
| 								f << "\n" << indent; | ||||
| 							else | ||||
| 								f << " "; | ||||
| 							dump_const(data, width, n * width, /*fixed_width=*/true); | ||||
| 							dump_const(init.data, mem->width, n * mem->width, /*fixed_width=*/true); | ||||
| 							f << ","; | ||||
| 						} | ||||
| 					dec_indent(); | ||||
|  | @ -1735,6 +1815,12 @@ struct CxxrtlWorker { | |||
| 						case FlowGraph::Node::Type::PROCESS_SYNC: | ||||
| 							dump_process_syncs(node.process); | ||||
| 							break; | ||||
| 						case FlowGraph::Node::Type::MEM_RDPORT: | ||||
| 							dump_mem_rdport(node.mem, node.portidx); | ||||
| 							break; | ||||
| 						case FlowGraph::Node::Type::MEM_WRPORTS: | ||||
| 							dump_mem_wrports(node.mem); | ||||
| 							break; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|  | @ -1764,6 +1850,12 @@ struct CxxrtlWorker { | |||
| 					case FlowGraph::Node::Type::PROCESS_SYNC: | ||||
| 						dump_process_syncs(node.process, /*for_debug=*/true); | ||||
| 						break; | ||||
| 					case FlowGraph::Node::Type::MEM_RDPORT: | ||||
| 						dump_mem_rdport(node.mem, node.portidx, /*for_debug=*/true); | ||||
| 						break; | ||||
| 					case FlowGraph::Node::Type::MEM_WRPORTS: | ||||
| 						dump_mem_wrports(node.mem, /*for_debug=*/true); | ||||
| 						break; | ||||
| 					default: | ||||
| 						log_abort(); | ||||
| 				} | ||||
|  | @ -1783,10 +1875,10 @@ struct CxxrtlWorker { | |||
| 					f << indent << "if (" << mangle(wire) << ".commit()) changed = true;\n"; | ||||
| 			} | ||||
| 			if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) { | ||||
| 				for (auto memory : module->memories) { | ||||
| 					if (!writable_memories[memory.second]) | ||||
| 				for (auto &mem : mod_memories[module]) { | ||||
| 					if (!GetSize(mem.wr_ports)) | ||||
| 						continue; | ||||
| 					f << indent << "if (" << mangle(memory.second) << ".commit()) changed = true;\n"; | ||||
| 					f << indent << "if (" << mangle(&mem) << ".commit()) changed = true;\n"; | ||||
| 				} | ||||
| 				for (auto cell : module->cells()) { | ||||
| 					if (is_internal_cell(cell->type)) | ||||
|  | @ -1928,12 +2020,12 @@ struct CxxrtlWorker { | |||
| 				} | ||||
| 			} | ||||
| 			if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) { | ||||
| 				for (auto &memory_it : module->memories) { | ||||
| 					if (!memory_it.first.isPublic()) | ||||
| 				for (auto &mem : mod_memories[module]) { | ||||
| 					if (!mem.memid.isPublic()) | ||||
| 						continue; | ||||
| 					f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(memory_it.second)); | ||||
| 					f << ", debug_item(" << mangle(memory_it.second) << ", "; | ||||
| 					f << memory_it.second->start_offset << "));\n"; | ||||
| 					f << indent << "items.add(path + " << escape_cxx_string(mem.packed ? get_hdl_name(mem.cell) : get_hdl_name(mem.mem)); | ||||
| 					f << ", debug_item(" << mangle(&mem) << ", "; | ||||
| 					f << mem.start_offset << "));\n"; | ||||
| 				} | ||||
| 				for (auto cell : module->cells()) { | ||||
| 					if (is_internal_cell(cell->type)) | ||||
|  | @ -2048,8 +2140,8 @@ struct CxxrtlWorker { | |||
| 				for (auto wire : module->wires()) | ||||
| 					dump_debug_wire(wire, /*is_local=*/false); | ||||
| 				bool has_memories = false; | ||||
| 				for (auto memory : module->memories) { | ||||
| 					dump_memory(module, memory.second); | ||||
| 				for (auto &mem : mod_memories[module]) { | ||||
| 					dump_memory(&mem); | ||||
| 					has_memories = true; | ||||
| 				} | ||||
| 				if (has_memories) | ||||
|  | @ -2313,6 +2405,11 @@ struct CxxrtlWorker { | |||
| 			SigMap &sigmap = sigmaps[module]; | ||||
| 			sigmap.set(module); | ||||
| 
 | ||||
| 			std::vector<Mem> &memories = mod_memories[module]; | ||||
| 			memories = Mem::get_all_memories(module); | ||||
| 			for (auto &mem : memories) | ||||
| 				mem.narrow(); | ||||
| 
 | ||||
| 			if (module->get_bool_attribute(ID(cxxrtl_blackbox))) { | ||||
| 				for (auto port : module->ports) { | ||||
| 					RTLIL::Wire *wire = module->wire(port); | ||||
|  | @ -2357,13 +2454,13 @@ struct CxxrtlWorker { | |||
| 			for (auto conn : module->connections()) | ||||
| 				flow.add_node(conn); | ||||
| 
 | ||||
| 			dict<const RTLIL::Cell*, FlowGraph::Node*> memrw_cell_nodes; | ||||
| 			dict<std::pair<RTLIL::SigBit, const RTLIL::Memory*>, | ||||
| 			     pool<const RTLIL::Cell*>> memwr_per_domain; | ||||
| 			for (auto cell : module->cells()) { | ||||
| 				if (!cell->known()) | ||||
| 					log_cmd_error("Unknown cell `%s'.\n", log_id(cell->type)); | ||||
| 
 | ||||
| 				if (cell->is_mem_cell()) | ||||
| 					continue; | ||||
| 
 | ||||
| 				RTLIL::Module *cell_module = design->module(cell->type); | ||||
| 				if (cell_module && | ||||
| 				    cell_module->get_blackbox_attribute() && | ||||
|  | @ -2375,7 +2472,7 @@ struct CxxrtlWorker { | |||
| 				    cell_module->get_bool_attribute(ID(cxxrtl_template))) | ||||
| 					blackbox_specializations[cell_module].insert(template_args(cell)); | ||||
| 
 | ||||
| 				FlowGraph::Node *node = flow.add_node(cell); | ||||
| 				flow.add_node(cell); | ||||
| 
 | ||||
| 				// Various DFF cells are treated like posedge/negedge processes, see above for details.
 | ||||
| 				if (cell->type.in(ID($dff), ID($dffe), ID($adff), ID($adffe), ID($dffsr), ID($dffsre), ID($sdff), ID($sdffe), ID($sdffce))) { | ||||
|  | @ -2383,43 +2480,23 @@ struct CxxrtlWorker { | |||
| 						register_edge_signal(sigmap, cell->getPort(ID::CLK), | ||||
| 							cell->parameters[ID::CLK_POLARITY].as_bool() ? RTLIL::STp : RTLIL::STn); | ||||
| 				} | ||||
| 				// Similar for memory port cells.
 | ||||
| 				if (cell->type.in(ID($memrd), ID($memwr))) { | ||||
| 					if (cell->getParam(ID::CLK_ENABLE).as_bool()) { | ||||
| 						if (is_valid_clock(cell->getPort(ID::CLK))) | ||||
| 							register_edge_signal(sigmap, cell->getPort(ID::CLK), | ||||
| 								cell->parameters[ID::CLK_POLARITY].as_bool() ? RTLIL::STp : RTLIL::STn); | ||||
| 			} | ||||
| 					memrw_cell_nodes[cell] = node; | ||||
| 				} | ||||
| 				// Optimize access to read-only memories.
 | ||||
| 				if (cell->type == ID($memwr)) | ||||
| 					writable_memories.insert(module->memories[cell->getParam(ID::MEMID).decode_string()]); | ||||
| 				// Collect groups of memory write ports in the same domain.
 | ||||
| 				if (cell->type == ID($memwr) && cell->getParam(ID::CLK_ENABLE).as_bool() && is_valid_clock(cell->getPort(ID::CLK))) { | ||||
| 					RTLIL::SigBit clk_bit = sigmap(cell->getPort(ID::CLK))[0]; | ||||
| 					const RTLIL::Memory *memory = module->memories[cell->getParam(ID::MEMID).decode_string()]; | ||||
| 					memwr_per_domain[{clk_bit, memory}].insert(cell); | ||||
| 				} | ||||
| 				// Handling of packed memories is delegated to the `memory_unpack` pass, so we can rely on the presence
 | ||||
| 				// of RTLIL memory objects and $memrd/$memwr/$meminit cells.
 | ||||
| 				if (cell->type.in(ID($mem))) | ||||
| 					log_assert(false); | ||||
| 			} | ||||
| 			for (auto cell : module->cells()) { | ||||
| 				// Collect groups of memory write ports read by every transparent read port.
 | ||||
| 				if (cell->type == ID($memrd) && cell->getParam(ID::CLK_ENABLE).as_bool() && is_valid_clock(cell->getPort(ID::CLK)) && | ||||
| 				    cell->getParam(ID::TRANSPARENT).as_bool()) { | ||||
| 					RTLIL::SigBit clk_bit = sigmap(cell->getPort(ID::CLK))[0]; | ||||
| 					const RTLIL::Memory *memory = module->memories[cell->getParam(ID::MEMID).decode_string()]; | ||||
| 					for (auto memwr_cell : memwr_per_domain[{clk_bit, memory}]) { | ||||
| 						transparent_for[cell].insert(memwr_cell); | ||||
| 						// Our implementation of transparent $memrd cells reads \EN, \ADDR and \DATA from every $memwr cell
 | ||||
| 						// in the same domain, which isn't directly visible in the netlist. Add these uses explicitly.
 | ||||
| 						flow.add_uses(memrw_cell_nodes[cell], memwr_cell->getPort(ID::EN)); | ||||
| 						flow.add_uses(memrw_cell_nodes[cell], memwr_cell->getPort(ID::ADDR)); | ||||
| 						flow.add_uses(memrw_cell_nodes[cell], memwr_cell->getPort(ID::DATA)); | ||||
| 
 | ||||
| 			for (auto &mem : memories) { | ||||
| 				flow.add_node(&mem); | ||||
| 
 | ||||
| 				// Clocked memory cells are treated like posedge/negedge processes as well.
 | ||||
| 				for (auto &port : mem.rd_ports) { | ||||
| 					if (port.clk_enable) | ||||
| 						if (is_valid_clock(port.clk)) | ||||
| 							register_edge_signal(sigmap, port.clk, | ||||
| 								port.clk_polarity ? RTLIL::STp : RTLIL::STn); | ||||
| 				} | ||||
| 				for (auto &port : mem.wr_ports) { | ||||
| 					if (port.clk_enable) | ||||
| 						if (is_valid_clock(port.clk)) | ||||
| 							register_edge_signal(sigmap, port.clk, | ||||
| 								port.clk_polarity ? RTLIL::STp : RTLIL::STn); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
|  | @ -2518,6 +2595,8 @@ struct CxxrtlWorker { | |||
| 			for (auto node : flow.nodes) { | ||||
| 				if (node->type == FlowGraph::Node::Type::CELL_EVAL && is_effectful_cell(node->cell->type)) | ||||
| 					worklist.insert(node); // node has effects
 | ||||
| 				else if (node->type == FlowGraph::Node::Type::MEM_WRPORTS) | ||||
| 					worklist.insert(node); // node is memory write
 | ||||
| 				else if (flow.node_sync_defs.count(node)) | ||||
| 					worklist.insert(node); // node is a flip-flop
 | ||||
| 				else if (flow.node_comb_defs.count(node)) { | ||||
|  | @ -2747,9 +2826,9 @@ struct CxxrtlWorker { | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	void check_design(RTLIL::Design *design, bool &has_top, bool &has_sync_init, bool &has_packed_mem) | ||||
| 	void check_design(RTLIL::Design *design, bool &has_top, bool &has_sync_init) | ||||
| 	{ | ||||
| 		has_sync_init = has_packed_mem = has_top = false; | ||||
| 		has_sync_init = has_top = false; | ||||
| 
 | ||||
| 		for (auto module : design->modules()) { | ||||
| 			if (module->get_blackbox_attribute() && !module->has_attribute(ID(cxxrtl_blackbox))) | ||||
|  | @ -2768,20 +2847,15 @@ struct CxxrtlWorker { | |||
| 				for (auto sync : proc.second->syncs) | ||||
| 					if (sync->type == RTLIL::STi) | ||||
| 						has_sync_init = true; | ||||
| 
 | ||||
| 			// The Mem constructor also checks for well-formedness of $meminit cells, if any.
 | ||||
| 			for (auto &mem : Mem::get_all_memories(module)) | ||||
| 				if (mem.packed) | ||||
| 					has_packed_mem = true; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	void prepare_design(RTLIL::Design *design) | ||||
| 	{ | ||||
| 		bool did_anything = false; | ||||
| 		bool has_top, has_sync_init, has_packed_mem; | ||||
| 		bool has_top, has_sync_init; | ||||
| 		log_push(); | ||||
| 		check_design(design, has_top, has_sync_init, has_packed_mem); | ||||
| 		check_design(design, has_top, has_sync_init); | ||||
| 		if (run_hierarchy && !has_top) { | ||||
| 			Pass::call(design, "hierarchy -auto-top"); | ||||
| 			did_anything = true; | ||||
|  | @ -2801,14 +2875,10 @@ struct CxxrtlWorker { | |||
| 			Pass::call(design, "proc_init"); | ||||
| 			did_anything = true; | ||||
| 		} | ||||
| 		if (has_packed_mem) { | ||||
| 			Pass::call(design, "memory_unpack"); | ||||
| 			did_anything = true; | ||||
| 		} | ||||
| 		// Recheck the design if it was modified.
 | ||||
| 		if (did_anything) | ||||
| 			check_design(design, has_top, has_sync_init, has_packed_mem); | ||||
| 		log_assert(!has_sync_init && !has_packed_mem); | ||||
| 			check_design(design, has_top, has_sync_init); | ||||
| 		log_assert(!has_sync_init); | ||||
| 		log_pop(); | ||||
| 		if (did_anything) | ||||
| 			log_spacer(); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue