mirror of https://github.com/YosysHQ/yosys.git
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
|
@ -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…
Reference in New Issue