aboutsummaryrefslogtreecommitdiffstats
path: root/passes/memory/memory_bram.cc
diff options
context:
space:
mode:
Diffstat (limited to 'passes/memory/memory_bram.cc')
-rw-r--r--passes/memory/memory_bram.cc560
1 files changed, 249 insertions, 311 deletions
diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc
index 3cb0728b7..b1f45d5fc 100644
--- a/passes/memory/memory_bram.cc
+++ b/passes/memory/memory_bram.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -18,6 +18,8 @@
*/
#include "kernel/yosys.h"
+#include "kernel/mem.h"
+#include "kernel/ffinit.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -28,9 +30,6 @@ struct rules_t
int group, index, dupidx;
int wrmode, enable, transp, clocks, clkpol;
- SigBit sig_clock;
- SigSpec sig_addr, sig_data, sig_en;
- bool effective_clkpol;
bool make_transp;
bool make_outreg;
int mapped_port;
@@ -91,7 +90,6 @@ struct rules_t
pi.mapped_port = -1;
pi.make_transp = false;
pi.make_outreg = false;
- pi.effective_clkpol = false;
portinfos.push_back(pi);
}
return portinfos;
@@ -400,17 +398,13 @@ struct rules_t
}
};
-bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, const rules_t::match_t &match, dict<string, int> &match_properties, int mode)
+bool replace_memory(Mem &mem, const rules_t &rules, FfInitVals *initvals, const rules_t::bram_t &bram, const rules_t::match_t &match, dict<string, int> &match_properties, int mode)
{
- Module *module = cell->module;
+ Module *module = mem.module;
auto portinfos = bram.make_portinfos();
int dup_count = 1;
- pair<SigBit, bool> make_transp_clk;
- bool enable_make_transp = false;
- int make_transp_enbits = 0;
-
dict<int, pair<SigBit, bool>> clock_domains;
dict<int, bool> clock_polarities;
dict<int, bool> read_transp;
@@ -437,124 +431,33 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
log(" Mapping to bram type %s (variant %d):\n", log_id(bram.name), bram.variant);
// bram.dump_config();
- int mem_size = cell->getParam(ID::SIZE).as_int();
- int mem_abits = cell->getParam(ID::ABITS).as_int();
- int mem_width = cell->getParam(ID::WIDTH).as_int();
- // int mem_offset = cell->getParam(ID::OFFSET).as_int();
-
- bool cell_init = !SigSpec(cell->getParam(ID::INIT)).is_fully_undef();
- vector<Const> initdata;
-
- if (cell_init) {
- Const initparam = cell->getParam(ID::INIT);
- initdata.reserve(mem_size);
- for (int i=0; i < mem_size; i++)
- initdata.push_back(initparam.extract(mem_width*i, mem_width, State::Sx));
- }
-
- int wr_ports = cell->getParam(ID::WR_PORTS).as_int();
- auto wr_clken = SigSpec(cell->getParam(ID::WR_CLK_ENABLE));
- auto wr_clkpol = SigSpec(cell->getParam(ID::WR_CLK_POLARITY));
- wr_clken.extend_u0(wr_ports);
- wr_clkpol.extend_u0(wr_ports);
-
- SigSpec wr_en = cell->getPort(ID::WR_EN);
- SigSpec wr_clk = cell->getPort(ID::WR_CLK);
- SigSpec wr_data = cell->getPort(ID::WR_DATA);
- SigSpec wr_addr = cell->getPort(ID::WR_ADDR);
-
- int rd_ports = cell->getParam(ID::RD_PORTS).as_int();
- auto rd_clken = SigSpec(cell->getParam(ID::RD_CLK_ENABLE));
- auto rd_clkpol = SigSpec(cell->getParam(ID::RD_CLK_POLARITY));
- auto rd_transp = SigSpec(cell->getParam(ID::RD_TRANSPARENT));
- rd_clken.extend_u0(rd_ports);
- rd_clkpol.extend_u0(rd_ports);
- rd_transp.extend_u0(rd_ports);
-
- SigSpec rd_en = cell->getPort(ID::RD_EN);
- SigSpec rd_clk = cell->getPort(ID::RD_CLK);
- SigSpec rd_data = cell->getPort(ID::RD_DATA);
- SigSpec rd_addr = cell->getPort(ID::RD_ADDR);
-
- if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0 && wr_ports > 0)
+ std::vector<int> shuffle_map;
+ if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0 && !mem.wr_ports.empty())
{
int bucket_size = bram.dbits / portinfos.at(match.shuffle_enable - 'A').enable;
log(" Shuffle bit order to accommodate enable buckets of size %d..\n", bucket_size);
- // extract unshuffled data/enable bits
-
- std::vector<SigSpec> old_wr_en;
- std::vector<SigSpec> old_wr_data;
- std::vector<SigSpec> old_rd_data;
-
- for (int i = 0; i < wr_ports; i++) {
- old_wr_en.push_back(wr_en.extract(i*mem_width, mem_width));
- old_wr_data.push_back(wr_data.extract(i*mem_width, mem_width));
- }
-
- for (int i = 0; i < rd_ports; i++)
- old_rd_data.push_back(rd_data.extract(i*mem_width, mem_width));
-
// analyze enable structure
std::vector<SigSpec> en_order;
dict<SigSpec, vector<int>> bits_wr_en;
- for (int i = 0; i < mem_width; i++) {
+ for (int i = 0; i < mem.width; i++) {
SigSpec sig;
- for (int j = 0; j < wr_ports; j++)
- sig.append(old_wr_en[j][i]);
+ for (auto &port : mem.wr_ports)
+ sig.append(port.en[i]);
if (bits_wr_en.count(sig) == 0)
en_order.push_back(sig);
bits_wr_en[sig].push_back(i);
}
- // re-create memory ports
-
- std::vector<SigSpec> new_wr_en(GetSize(old_wr_en));
- std::vector<SigSpec> new_wr_data(GetSize(old_wr_data));
- std::vector<SigSpec> new_rd_data(GetSize(old_rd_data));
- std::vector<std::vector<State>> new_initdata;
- std::vector<int> shuffle_map;
-
- if (cell_init)
- new_initdata.resize(mem_size);
-
for (auto &it : en_order)
{
- auto &bits = bits_wr_en.at(it);
- int buckets = (GetSize(bits) + bucket_size - 1) / bucket_size;
- int fillbits = buckets*bucket_size - GetSize(bits);
- SigBit fillbit;
-
- for (int i = 0; i < GetSize(bits); i++) {
- for (int j = 0; j < wr_ports; j++) {
- new_wr_en[j].append(old_wr_en[j][bits[i]]);
- new_wr_data[j].append(old_wr_data[j][bits[i]]);
- fillbit = old_wr_en[j][bits[i]];
- }
- for (int j = 0; j < rd_ports; j++)
- new_rd_data[j].append(old_rd_data[j][bits[i]]);
- if (cell_init) {
- for (int j = 0; j < mem_size; j++)
- new_initdata[j].push_back(initdata[j][bits[i]]);
- }
- shuffle_map.push_back(bits[i]);
- }
+ for (auto bit : bits_wr_en.at(it))
+ shuffle_map.push_back(bit);
- for (int i = 0; i < fillbits; i++) {
- for (int j = 0; j < wr_ports; j++) {
- new_wr_en[j].append(fillbit);
- new_wr_data[j].append(State::S0);
- }
- for (int j = 0; j < rd_ports; j++)
- new_rd_data[j].append(State::Sx);
- if (cell_init) {
- for (int j = 0; j < mem_size; j++)
- new_initdata[j].push_back(State::Sx);
- }
+ while (GetSize(shuffle_map) % bucket_size)
shuffle_map.push_back(-1);
- }
}
log(" Results of bit order shuffling:");
@@ -563,53 +466,38 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
log("\n");
// update mem_*, wr_*, and rd_* variables
-
- mem_width = GetSize(new_wr_en.front());
- wr_en = SigSpec(0, wr_ports * mem_width);
- wr_data = SigSpec(0, wr_ports * mem_width);
- rd_data = SigSpec(0, rd_ports * mem_width);
-
- for (int i = 0; i < wr_ports; i++) {
- wr_en.replace(i*mem_width, new_wr_en[i]);
- wr_data.replace(i*mem_width, new_wr_data[i]);
- }
-
- for (int i = 0; i < rd_ports; i++)
- rd_data.replace(i*mem_width, new_rd_data[i]);
-
- if (cell_init) {
- for (int i = 0; i < mem_size; i++)
- initdata[i] = Const(new_initdata[i]);
- }
+ } else {
+ for (int i = 0; i < mem.width; i++)
+ shuffle_map.push_back(i);
}
+ // Align width up to dbits.
+ while (GetSize(shuffle_map) % bram.dbits)
+ shuffle_map.push_back(-1);
+
// assign write ports
pair<SigBit, bool> wr_clkdom;
- for (int cell_port_i = 0, bram_port_i = 0; cell_port_i < wr_ports; cell_port_i++)
+ for (int cell_port_i = 0, bram_port_i = 0; cell_port_i < GetSize(mem.wr_ports); cell_port_i++)
{
- bool clken = wr_clken[cell_port_i] == State::S1;
- bool clkpol = wr_clkpol[cell_port_i] == State::S1;
- SigBit clksig = wr_clk[cell_port_i];
+ auto &port = mem.wr_ports[cell_port_i];
- pair<SigBit, bool> clkdom(clksig, clkpol);
- if (!clken)
+ pair<SigBit, bool> clkdom(port.clk, port.clk_polarity);
+ if (!port.clk_enable)
clkdom = pair<SigBit, bool>(State::S1, false);
wr_clkdom = clkdom;
log(" Write port #%d is in clock domain %s%s.\n",
cell_port_i, clkdom.second ? "" : "!",
- clken ? log_signal(clkdom.first) : "~async~");
+ port.clk_enable ? log_signal(clkdom.first) : "~async~");
for (; bram_port_i < GetSize(portinfos); bram_port_i++)
{
auto &pi = portinfos[bram_port_i];
- make_transp_enbits = pi.enable;
- make_transp_clk = clkdom;
if (pi.wrmode != 1)
skip_bram_wport:
continue;
- if (clken) {
+ if (port.clk_enable) {
if (pi.clocks == 0) {
log(" Bram port %c%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1);
goto skip_bram_wport;
@@ -618,7 +506,7 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
log(" Bram port %c%d is in a different clock domain.\n", pi.group + 'A', pi.index + 1);
goto skip_bram_wport;
}
- if (clock_polarities.count(pi.clkpol) && clock_polarities.at(pi.clkpol) != clkpol) {
+ if (clock_polarities.count(pi.clkpol) && clock_polarities.at(pi.clkpol) != port.clk_polarity) {
log(" Bram port %c%d has incompatible clock polarity.\n", pi.group + 'A', pi.index + 1);
goto skip_bram_wport;
}
@@ -629,33 +517,36 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
}
}
- SigSpec sig_en;
- SigBit last_en_bit = State::S1;
- for (int i = 0; i < mem_width; i++) {
- if (pi.enable && i % (bram.dbits / pi.enable) == 0) {
- last_en_bit = wr_en[i + cell_port_i*mem_width];
- sig_en.append(last_en_bit);
- }
- if (last_en_bit != wr_en[i + cell_port_i*mem_width]) {
- log(" Bram port %c%d has incompatible enable structure.\n", pi.group + 'A', pi.index + 1);
- goto skip_bram_wport;
+ // We need to check enable compatibility of this port, as well as all
+ // ports that have priority over this one (because they will be involved
+ // in emulate_priority logic).
+
+ for (int i = 0; i < GetSize(mem.wr_ports); i++) {
+ auto &oport = mem.wr_ports[i];
+ if (i != cell_port_i && !oport.priority_mask[cell_port_i])
+ continue;
+ SigBit last_en_bit = State::S1;
+ for (int j = 0; j < GetSize(shuffle_map); j++) {
+ if (shuffle_map[j] == -1)
+ continue;
+ SigBit en_bit = oport.en[shuffle_map[j]];
+ if (pi.enable && j % (bram.dbits / pi.enable) == 0)
+ last_en_bit = en_bit;
+ if (last_en_bit != en_bit) {
+ log(" Bram port %c%d has incompatible enable structure.\n", pi.group + 'A', pi.index + 1);
+ goto skip_bram_wport;
+ }
}
}
log(" Mapped to bram port %c%d.\n", pi.group + 'A', pi.index + 1);
pi.mapped_port = cell_port_i;
- if (clken) {
+ if (port.clk_enable) {
clock_domains[pi.clocks] = clkdom;
clock_polarities[pi.clkpol] = clkdom.second;
- pi.sig_clock = clkdom.first;
- pi.effective_clkpol = clkdom.second;
}
- pi.sig_en = sig_en;
- pi.sig_addr = wr_addr.extract(cell_port_i*mem_abits, mem_abits);
- pi.sig_data = wr_data.extract(cell_port_i*mem_width, mem_width);
-
bram_port_i++;
goto mapped_wr_port;
}
@@ -678,10 +569,6 @@ grow_read_ports:;
for (auto &pi : portinfos) {
if (pi.wrmode == 0) {
pi.mapped_port = -1;
- pi.sig_clock = SigBit();
- pi.sig_addr = SigSpec();
- pi.sig_data = SigSpec();
- pi.sig_en = SigSpec();
pi.make_outreg = false;
pi.make_transp = false;
}
@@ -710,23 +597,27 @@ grow_read_ports:;
// assign read ports
- for (int cell_port_i = 0; cell_port_i < rd_ports; cell_port_i++)
+ for (int cell_port_i = 0; cell_port_i < GetSize(mem.rd_ports); cell_port_i++)
{
- bool clken = rd_clken[cell_port_i] == State::S1;
- bool clkpol = rd_clkpol[cell_port_i] == State::S1;
- bool transp = rd_transp[cell_port_i] == State::S1;
- SigBit clksig = rd_clk[cell_port_i];
-
- if (wr_ports == 0)
- transp = false;
+ auto &port = mem.rd_ports[cell_port_i];
+ bool transp = false;
+ bool non_transp = false;
+
+ if (port.clk_enable) {
+ for (int i = 0; i < GetSize(mem.wr_ports); i++)
+ if (port.transparency_mask[i])
+ transp = true;
+ else if (!port.collision_x_mask[i])
+ non_transp = true;
+ }
- pair<SigBit, bool> clkdom(clksig, clkpol);
- if (!clken)
+ pair<SigBit, bool> clkdom(port.clk, port.clk_polarity);
+ if (!port.clk_enable)
clkdom = pair<SigBit, bool>(State::S1, false);
log(" Read port #%d is in clock domain %s%s.\n",
cell_port_i, clkdom.second ? "" : "!",
- clken ? log_signal(clkdom.first) : "~async~");
+ port.clk_enable ? log_signal(clkdom.first) : "~async~");
for (int bram_port_i = 0; bram_port_i < GetSize(portinfos); bram_port_i++)
{
@@ -736,7 +627,7 @@ grow_read_ports:;
skip_bram_rport:
continue;
- if (clken) {
+ if (port.clk_enable) {
if (pi.clocks == 0) {
if (match.make_outreg) {
pi.make_outreg = true;
@@ -749,25 +640,17 @@ grow_read_ports:;
log(" Bram port %c%d.%d is in a different clock domain.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
goto skip_bram_rport;
}
- if (clock_polarities.count(pi.clkpol) && clock_polarities.at(pi.clkpol) != clkpol) {
+ if (clock_polarities.count(pi.clkpol) && clock_polarities.at(pi.clkpol) != port.clk_polarity) {
log(" Bram port %c%d.%d has incompatible clock polarity.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
goto skip_bram_rport;
}
- if (rd_en[cell_port_i] != State::S1 && pi.enable == 0) {
- log(" Bram port %c%d.%d has no read enable input.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
+ if (non_transp && read_transp.count(pi.transp) && read_transp.at(pi.transp)) {
+ log(" Bram port %c%d.%d has incompatible read transparency.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
goto skip_bram_rport;
}
- skip_bram_rport_clkcheck:
- if (read_transp.count(pi.transp) && read_transp.at(pi.transp) != transp) {
- if (match.make_transp && wr_ports <= 1) {
+ if (transp && (non_transp || (read_transp.count(pi.transp) && !read_transp.at(pi.transp)))) {
+ if (match.make_transp) {
pi.make_transp = true;
- if (pi.clocks != 0) {
- if (wr_ports == 1 && wr_clkdom != clkdom) {
- log(" Bram port %c%d.%d cannot have soft transparency logic added as read and write clock domains differ.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
- goto skip_bram_rport;
- }
- enable_make_transp = true;
- }
} else {
log(" Bram port %c%d.%d has incompatible read transparency.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
goto skip_bram_rport;
@@ -780,22 +663,19 @@ grow_read_ports:;
}
}
+ skip_bram_rport_clkcheck:
log(" Mapped to bram port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
pi.mapped_port = cell_port_i;
- if (clken) {
+ if (pi.clocks) {
clock_domains[pi.clocks] = clkdom;
clock_polarities[pi.clkpol] = clkdom.second;
- if (!pi.make_transp)
- read_transp[pi.transp] = transp;
- pi.sig_clock = clkdom.first;
- pi.sig_en = rd_en[cell_port_i];
- pi.effective_clkpol = clkdom.second;
+ if (non_transp)
+ read_transp[pi.transp] = false;
+ if (transp && !pi.make_transp)
+ read_transp[pi.transp] = true;
}
- pi.sig_addr = rd_addr.extract(cell_port_i*mem_abits, mem_abits);
- pi.sig_data = rd_data.extract(cell_port_i*mem_width, mem_width);
-
if (grow_read_ports_cursor < cell_port_i) {
grow_read_ports_cursor = cell_port_i;
try_growing_more_read_ports = true;
@@ -815,17 +695,19 @@ grow_read_ports:;
// update properties and re-check conditions
+ int dcells = GetSize(shuffle_map) / bram.dbits;
+ int acells = (mem.size + (1 << bram.abits) - 1) / (1 << bram.abits);
if (mode <= 1)
{
match_properties["dups"] = dup_count;
match_properties["waste"] = match_properties["dups"] * match_properties["bwaste"];
- int cells = ((mem_width + bram.dbits - 1) / bram.dbits) * ((mem_size + (1 << bram.abits) - 1) / (1 << bram.abits));
+ int cells = dcells * acells;
match_properties["efficiency"] = (100 * match_properties["bits"]) / (dup_count * cells * bram.dbits * (1 << bram.abits));
- match_properties["dcells"] = ((mem_width + bram.dbits - 1) / bram.dbits);
- match_properties["acells"] = ((mem_size + (1 << bram.abits) - 1) / (1 << bram.abits));
- match_properties["cells"] = match_properties["dcells"] * match_properties["acells"] * match_properties["dups"];
+ match_properties["dcells"] = dcells;
+ match_properties["acells"] = acells;
+ match_properties["cells"] = cells * dup_count;
log(" Updated properties: dups=%d waste=%d efficiency=%d\n",
match_properties["dups"], match_properties["waste"], match_properties["efficiency"]);
@@ -857,8 +739,8 @@ grow_read_ports:;
bool exists = std::get<0>(term);
IdString key = std::get<1>(term);
const Const &value = std::get<2>(term);
- auto it = cell->attributes.find(key);
- if (it == cell->attributes.end()) {
+ auto it = mem.attributes.find(key);
+ if (it == mem.attributes.end()) {
if (exists)
continue;
found = true;
@@ -892,6 +774,90 @@ grow_read_ports:;
return true;
}
+ // At this point we are commited to replacing the RAM, and can mutate mem.
+
+ // Apply make_outreg and make_transp where necessary.
+ for (auto &pi : portinfos) {
+ if (pi.mapped_port == -1 || pi.wrmode)
+ continue;
+ auto &port = mem.rd_ports[pi.mapped_port];
+ if (pi.make_outreg) {
+ mem.extract_rdff(pi.mapped_port, initvals);
+ } else if (port.clk_enable) {
+ if (!pi.enable && port.en != State::S1)
+ mem.emulate_rden(pi.mapped_port, initvals);
+ else
+ mem.emulate_reset(pi.mapped_port, true, true, true, initvals);
+ }
+ if (pi.make_transp) {
+ for (int i = 0; i < GetSize(mem.wr_ports); i++)
+ if (port.transparency_mask[i])
+ mem.emulate_transparency(i, pi.mapped_port, initvals);
+ }
+ }
+
+ // We don't really support priorities, emulate them.
+ for (int i = 0; i < GetSize(mem.wr_ports); i++)
+ for (int j = 0; j < i; j++)
+ mem.emulate_priority(j, i, initvals);
+
+ // Swizzle the init data. Do this before changing mem.width, so that get_init_data works.
+ bool cell_init = !mem.inits.empty();
+ vector<Const> initdata;
+ if (cell_init) {
+ Const initparam = mem.get_init_data();
+ initdata.reserve(mem.size);
+ for (int i = 0; i < mem.size; i++) {
+ std::vector<State> val;
+ for (auto idx : shuffle_map) {
+ if (idx == -1)
+ val.push_back(State::Sx);
+ else
+ val.push_back(initparam[mem.width * i + idx]);
+ }
+ initdata.push_back(Const(val));
+ }
+ }
+
+ // Now the big swizzle.
+ mem.width = GetSize(shuffle_map);
+
+ // Swizzle write ports.
+ for (auto &port : mem.wr_ports) {
+ SigSpec new_en, new_data;
+ SigBit en_bit = State::S1;
+ for (auto idx : shuffle_map) {
+ if (idx == -1) {
+ new_data.append(State::Sx);
+ } else {
+ new_data.append(port.data[idx]);
+ en_bit = port.en[idx];
+ }
+ new_en.append(en_bit);
+ }
+ port.en = new_en;
+ port.data = new_data;
+ }
+
+ // Swizzle read ports.
+ for (auto &port : mem.rd_ports) {
+ SigSpec new_data = module->addWire(NEW_ID, mem.width);
+ Const new_init_value = Const(State::Sx, mem.width);
+ Const new_arst_value = Const(State::Sx, mem.width);
+ Const new_srst_value = Const(State::Sx, mem.width);
+ for (int i = 0; i < mem.width; i++)
+ if (shuffle_map[i] != -1) {
+ module->connect(port.data[shuffle_map[i]], new_data[i]);
+ new_init_value[i] = port.init_value[shuffle_map[i]];
+ new_arst_value[i] = port.arst_value[shuffle_map[i]];
+ new_srst_value[i] = port.srst_value[shuffle_map[i]];
+ }
+ port.data = new_data;
+ port.init_value = new_init_value;
+ port.arst_value = new_arst_value;
+ port.srst_value = new_srst_value;
+ }
+
// prepare variant parameters
dict<IdString, Const> variant_params;
@@ -902,42 +868,28 @@ grow_read_ports:;
dict<SigSpec, pair<SigSpec, SigSpec>> dout_cache;
- for (int grid_d = 0; grid_d*bram.dbits < mem_width; grid_d++)
+ for (int grid_d = 0; grid_d < dcells; grid_d++)
{
- SigSpec mktr_wraddr, mktr_wrdata, mktr_wrdata_q;
- vector<SigSpec> mktr_wren;
-
- if (enable_make_transp) {
- mktr_wraddr = module->addWire(NEW_ID, bram.abits);
- mktr_wrdata = module->addWire(NEW_ID, bram.dbits);
- mktr_wrdata_q = module->addWire(NEW_ID, bram.dbits);
- module->addDff(NEW_ID, make_transp_clk.first, mktr_wrdata, mktr_wrdata_q, make_transp_clk.second);
- for (int grid_a = 0; grid_a*(1 << bram.abits) < mem_size; grid_a++)
- mktr_wren.push_back(module->addWire(NEW_ID, make_transp_enbits));
- }
-
- for (int grid_a = 0; grid_a*(1 << bram.abits) < mem_size; grid_a++)
+ for (int grid_a = 0; grid_a < acells; grid_a++)
for (int dupidx = 0; dupidx < dup_count; dupidx++)
{
- Cell *c = module->addCell(module->uniquify(stringf("%s.%d.%d.%d", cell->name.c_str(), grid_d, grid_a, dupidx)), bram.name);
+ Cell *c = module->addCell(module->uniquify(stringf("%s.%d.%d.%d", mem.memid.c_str(), grid_d, grid_a, dupidx)), bram.name);
log(" Creating %s cell at grid position <%d %d %d>: %s\n", log_id(bram.name), grid_d, grid_a, dupidx, log_id(c));
for (auto &vp : variant_params)
c->setParam(vp.first, vp.second);
if (cell_init) {
- int init_offset = grid_a*(1 << bram.abits);
+ int init_offset = grid_a*(1 << bram.abits) - mem.start_offset;
int init_shift = grid_d*bram.dbits;
int init_size = (1 << bram.abits);
Const initparam(State::Sx, init_size*bram.dbits);
- for (int i = 0; i < init_size; i++) {
- State padding = State::Sx;
+ for (int i = 0; i < init_size; i++)
for (int j = 0; j < bram.dbits; j++)
- if (init_offset+i < GetSize(initdata) && init_shift+j < GetSize(initdata[init_offset+i]))
+ if (init_offset+i < GetSize(initdata) && init_offset+i >= 0)
initparam[i*bram.dbits+j] = initdata[init_offset+i][init_shift+j];
else
- initparam[i*bram.dbits+j] = padding;
- }
+ initparam[i*bram.dbits+j] = State::Sx;
c->setParam(ID::INIT, initparam);
}
@@ -949,101 +901,84 @@ grow_read_ports:;
string prefix = stringf("%c%d", pi.group + 'A', pi.index + 1);
const char *pf = prefix.c_str();
- if (pi.clocks && (!c->hasPort(stringf("\\CLK%d", (pi.clocks-1) % clocks_max + 1)) || pi.sig_clock.wire)) {
- c->setPort(stringf("\\CLK%d", (pi.clocks-1) % clocks_max + 1), pi.sig_clock);
- if (pi.clkpol > 1 && pi.sig_clock.wire)
- c->setParam(stringf("\\CLKPOL%d", (pi.clkpol-1) % clkpol_max + 1), clock_polarities.at(pi.clkpol));
- if (pi.transp > 1 && pi.sig_clock.wire)
- c->setParam(stringf("\\TRANSP%d", (pi.transp-1) % transp_max + 1), read_transp.at(pi.transp));
- }
+ if (pi.clocks && clock_domains.count(pi.clocks))
+ c->setPort(stringf("\\CLK%d", (pi.clocks-1) % clocks_max + 1), clock_domains.at(pi.clocks).first);
+ if (pi.clkpol > 1 && clock_polarities.count(pi.clkpol))
+ c->setParam(stringf("\\CLKPOL%d", (pi.clkpol-1) % clkpol_max + 1), clock_polarities.at(pi.clkpol));
+ if (pi.transp > 1 && read_transp.count(pi.transp))
+ c->setParam(stringf("\\TRANSP%d", (pi.transp-1) % transp_max + 1), read_transp.at(pi.transp));
SigSpec addr_ok;
- if (GetSize(pi.sig_addr) > bram.abits) {
- SigSpec extra_addr = pi.sig_addr.extract(bram.abits, GetSize(pi.sig_addr) - bram.abits);
+ SigSpec sig_addr;
+ if (pi.mapped_port >= 0) {
+ if (pi.wrmode == 1)
+ sig_addr = mem.wr_ports[pi.mapped_port].addr;
+ else
+ sig_addr = mem.rd_ports[pi.mapped_port].addr;
+ }
+
+ if (GetSize(sig_addr) > bram.abits) {
+ SigSpec extra_addr = sig_addr.extract(bram.abits, GetSize(sig_addr) - bram.abits);
SigSpec extra_addr_sel = SigSpec(grid_a, GetSize(extra_addr));
addr_ok = module->Eq(NEW_ID, extra_addr, extra_addr_sel);
}
- if (pi.enable)
- {
- SigSpec sig_en = pi.sig_en;
+ sig_addr.extend_u0(bram.abits);
+ c->setPort(stringf("\\%sADDR", pf), sig_addr);
- if (pi.wrmode == 1) {
- sig_en.extend_u0((grid_d+1) * pi.enable);
- sig_en = sig_en.extract(grid_d * pi.enable, pi.enable);
+ if (pi.wrmode == 1) {
+ if (pi.mapped_port == -1)
+ {
+ if (pi.enable)
+ c->setPort(stringf("\\%sEN", pf), Const(State::S0, pi.enable));
+ continue;
}
- if (!addr_ok.empty())
- sig_en = module->Mux(NEW_ID, SigSpec(0, GetSize(sig_en)), sig_en, addr_ok);
-
- c->setPort(stringf("\\%sEN", pf), sig_en);
-
- if (pi.wrmode == 1 && enable_make_transp)
- module->connect(mktr_wren[grid_a], sig_en);
- }
+ auto &port = mem.wr_ports[pi.mapped_port];
+ SigSpec sig_data = port.data.extract(grid_d * bram.dbits, bram.dbits);
+ c->setPort(stringf("\\%sDATA", pf), sig_data);
- SigSpec sig_addr = pi.sig_addr;
- sig_addr.extend_u0(bram.abits);
- c->setPort(stringf("\\%sADDR", pf), sig_addr);
+ if (pi.enable)
+ {
+ SigSpec sig_en;
+ int stride = bram.dbits / pi.enable;
+ for (int i = 0; i < pi.enable; i++)
+ sig_en.append(port.en[stride * i + grid_d * bram.dbits]);
- if (pi.wrmode == 1 && enable_make_transp && grid_a == 0)
- module->connect(mktr_wraddr, sig_addr);
+ if (!addr_ok.empty())
+ sig_en = module->Mux(NEW_ID, SigSpec(0, GetSize(sig_en)), sig_en, addr_ok);
- SigSpec sig_data = pi.sig_data;
- sig_data.extend_u0((grid_d+1) * bram.dbits);
- sig_data = sig_data.extract(grid_d * bram.dbits, bram.dbits);
+ c->setPort(stringf("\\%sEN", pf), sig_en);
- if (pi.wrmode == 1) {
- c->setPort(stringf("\\%sDATA", pf), sig_data);
- if (enable_make_transp && grid_a == 0)
- module->connect(mktr_wrdata, sig_data);
+ }
} else {
- SigSpec bram_dout = module->addWire(NEW_ID, bram.dbits);
- c->setPort(stringf("\\%sDATA", pf), bram_dout);
- if (pi.make_outreg && pi.make_transp) {
- log(" Moving output register to address for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
- SigSpec sig_addr_q = module->addWire(NEW_ID, bram.abits);
- module->addDff(NEW_ID, pi.sig_clock, sig_addr, sig_addr_q, pi.effective_clkpol);
- c->setPort(stringf("\\%sADDR", pf), sig_addr_q);
- } else if (pi.make_outreg) {
- SigSpec bram_dout_q = module->addWire(NEW_ID, bram.dbits);
- if (!pi.sig_en.empty())
- bram_dout = module->Mux(NEW_ID, bram_dout_q, bram_dout, pi.sig_en);
- module->addDff(NEW_ID, pi.sig_clock, bram_dout, bram_dout_q, pi.effective_clkpol);
- bram_dout = bram_dout_q;
- } else if (pi.make_transp) {
- log(" Adding extra logic for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
-
- SigSpec transp_en_d = module->Mux(NEW_ID, SigSpec(0, make_transp_enbits),
- mktr_wren[grid_a], module->Eq(NEW_ID, mktr_wraddr, sig_addr));
-
- SigSpec transp_en_q = module->addWire(NEW_ID, make_transp_enbits);
- module->addDff(NEW_ID, make_transp_clk.first, transp_en_d, transp_en_q, make_transp_clk.second);
-
- for (int i = 0; i < make_transp_enbits; i++) {
- int en_width = bram.dbits / make_transp_enbits;
- SigSpec orig_bram_dout = bram_dout.extract(i * en_width, en_width);
- SigSpec bypass_dout = mktr_wrdata_q.extract(i * en_width, en_width);
- bram_dout.replace(i * en_width, module->Mux(NEW_ID, orig_bram_dout, bypass_dout, transp_en_q[i]));
- }
+ if (pi.mapped_port == -1)
+ {
+ if (pi.enable)
+ c->setPort(stringf("\\%sEN", pf), State::S0);
+ continue;
}
+ auto &port = mem.rd_ports[pi.mapped_port];
+ SigSpec sig_data = port.data.extract(grid_d * bram.dbits, bram.dbits);
- for (int i = bram.dbits-1; i >= 0; i--)
- if (sig_data[i].wire == nullptr) {
- sig_data.remove(i);
- bram_dout.remove(i);
- }
+ SigSpec bram_dout = module->addWire(NEW_ID, bram.dbits);
+ c->setPort(stringf("\\%sDATA", pf), bram_dout);
SigSpec addr_ok_q = addr_ok;
- if ((pi.clocks || pi.make_outreg) && !addr_ok.empty()) {
+ if (port.clk_enable && !addr_ok.empty()) {
addr_ok_q = module->addWire(NEW_ID);
- if (!pi.sig_en.empty())
- addr_ok = module->Mux(NEW_ID, addr_ok_q, addr_ok, pi.sig_en);
- module->addDff(NEW_ID, pi.sig_clock, addr_ok, addr_ok_q, pi.effective_clkpol);
+ module->addDffe(NEW_ID, port.clk, port.en, addr_ok, addr_ok_q, port.clk_polarity);
}
dout_cache[sig_data].first.append(addr_ok_q);
dout_cache[sig_data].second.append(bram_dout);
+
+ if (pi.enable) {
+ SigSpec sig_en = port.en;
+ if (!addr_ok.empty())
+ sig_en = module->And(NEW_ID, sig_en, addr_ok);
+ c->setPort(stringf("\\%sEN", pf), sig_en);
+ }
}
}
}
@@ -1063,22 +998,23 @@ grow_read_ports:;
}
}
- module->remove(cell);
+ mem.remove();
return true;
}
-void handle_cell(Cell *cell, const rules_t &rules)
+void handle_memory(Mem &mem, const rules_t &rules, FfInitVals *initvals)
{
- log("Processing %s.%s:\n", log_id(cell->module), log_id(cell));
+ log("Processing %s.%s:\n", log_id(mem.module), log_id(mem.memid));
+ mem.narrow();
- bool cell_init = !SigSpec(cell->getParam(ID::INIT)).is_fully_undef();
+ bool cell_init = !mem.inits.empty();
dict<string, int> match_properties;
- match_properties["words"] = cell->getParam(ID::SIZE).as_int();
- match_properties["abits"] = cell->getParam(ID::ABITS).as_int();
- match_properties["dbits"] = cell->getParam(ID::WIDTH).as_int();
- match_properties["wports"] = cell->getParam(ID::WR_PORTS).as_int();
- match_properties["rports"] = cell->getParam(ID::RD_PORTS).as_int();
+ match_properties["words"] = mem.size;
+ match_properties["abits"] = ceil_log2(mem.size);
+ match_properties["dbits"] = mem.width;
+ match_properties["wports"] = GetSize(mem.wr_ports);
+ match_properties["rports"] = GetSize(mem.rd_ports);
match_properties["bits"] = match_properties["words"] * match_properties["dbits"];
match_properties["ports"] = match_properties["wports"] + match_properties["rports"];
@@ -1181,8 +1117,8 @@ void handle_cell(Cell *cell, const rules_t &rules)
bool exists = std::get<0>(term);
IdString key = std::get<1>(term);
const Const &value = std::get<2>(term);
- auto it = cell->attributes.find(key);
- if (it == cell->attributes.end()) {
+ auto it = mem.attributes.find(key);
+ if (it == mem.attributes.end()) {
if (exists)
continue;
found = true;
@@ -1219,7 +1155,7 @@ void handle_cell(Cell *cell, const rules_t &rules)
if (or_next_if_better && i+1 == GetSize(rules.matches) && vi+1 == GetSize(rules.brams.at(match.name)))
log_error("Found 'or_next_if_better' in last match rule.\n");
- if (!replace_cell(cell, rules, bram, match, match_properties, 1)) {
+ if (!replace_memory(mem, rules, initvals, bram, match, match_properties, 1)) {
log(" Mapping to bram type %s failed.\n", log_id(match.name));
failed_brams.insert(pair<IdString, int>(bram.name, bram.variant));
goto next_match_rule;
@@ -1246,12 +1182,12 @@ void handle_cell(Cell *cell, const rules_t &rules)
best_rule_cache.clear();
auto &best_bram = rules.brams.at(rules.matches.at(best_rule.first).name).at(best_rule.second);
- if (!replace_cell(cell, rules, best_bram, rules.matches.at(best_rule.first), match_properties, 2))
+ if (!replace_memory(mem, rules, initvals, best_bram, rules.matches.at(best_rule.first), match_properties, 2))
log_error("Mapping to bram type %s (variant %d) after pre-selection failed.\n", log_id(best_bram.name), best_bram.variant);
return;
}
- if (!replace_cell(cell, rules, bram, match, match_properties, 0)) {
+ if (!replace_memory(mem, rules, initvals, bram, match, match_properties, 0)) {
log(" Mapping to bram type %s failed.\n", log_id(match.name));
failed_brams.insert(pair<IdString, int>(bram.name, bram.variant));
goto next_match_rule;
@@ -1383,10 +1319,12 @@ struct MemoryBramPass : public Pass {
}
extra_args(args, argidx, design);
- for (auto mod : design->selected_modules())
- for (auto cell : mod->selected_cells())
- if (cell->type == ID($mem))
- handle_cell(cell, rules);
+ for (auto mod : design->selected_modules()) {
+ SigMap sigmap(mod);
+ FfInitVals initvals(&sigmap, mod);
+ for (auto &mem : Mem::get_selected_memories(mod))
+ handle_memory(mem, rules, &initvals);
+ }
}
} MemoryBramPass;