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.cc153
1 files changed, 141 insertions, 12 deletions
diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc
index a7f9cf382..24478f2ee 100644
--- a/passes/memory/memory_bram.cc
+++ b/passes/memory/memory_bram.cc
@@ -68,6 +68,10 @@ struct rules_t
if (groups != GetSize(transp)) log_error("Bram %s variant %d has %d groups but only %d entries in 'transp'.\n", log_id(name), variant, groups, GetSize(transp));
if (groups != GetSize(clocks)) log_error("Bram %s variant %d has %d groups but only %d entries in 'clocks'.\n", log_id(name), variant, groups, GetSize(clocks));
if (groups != GetSize(clkpol)) log_error("Bram %s variant %d has %d groups but only %d entries in 'clkpol'.\n", log_id(name), variant, groups, GetSize(clkpol));
+
+ int group = 0;
+ for (auto e : enable)
+ if (e > dbits) log_error("Bram %s variant %d group %d has %d enable bits but only %d dbits.\n", log_id(name), variant, group, e, dbits);
}
vector<portinfo_t> make_portinfos() const
@@ -130,6 +134,7 @@ struct rules_t
dict<string, int> min_limits, max_limits;
bool or_next_if_better, make_transp, make_outreg;
char shuffle_enable;
+ vector<vector<std::tuple<bool,IdString,Const>>> attributes;
};
dict<IdString, vector<bram_t>> brams;
@@ -323,6 +328,20 @@ struct rules_t
continue;
}
+ if (GetSize(tokens) >= 2 && tokens[0] == "attribute") {
+ data.attributes.emplace_back();
+ for (int idx = 1; idx < GetSize(tokens); idx++) {
+ size_t c1 = tokens[idx][0] == '!' ? 1 : 0;
+ size_t c2 = tokens[idx].find("=");
+ bool exists = (c1 == 0);
+ IdString key = RTLIL::escape_id(tokens[idx].substr(c1, c2));
+ Const val = c2 != std::string::npos ? tokens[idx].substr(c2+1) : RTLIL::Const(1);
+
+ data.attributes.back().emplace_back(exists, key, val);
+ }
+ continue;
+ }
+
syntax_error();
}
}
@@ -472,8 +491,12 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
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);
@@ -489,6 +512,10 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
}
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]);
}
@@ -499,6 +526,10 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
}
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);
+ }
shuffle_map.push_back(-1);
}
}
@@ -522,10 +553,15 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
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]);
+ }
}
// 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++)
{
bool clken = wr_clken[cell_port_i] == State::S1;
@@ -535,7 +571,7 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
pair<SigBit, bool> clkdom(clksig, clkpol);
if (!clken)
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~");
@@ -623,6 +659,8 @@ grow_read_ports:;
pi.sig_addr = SigSpec();
pi.sig_data = SigSpec();
pi.sig_en = SigSpec();
+ pi.make_outreg = false;
+ pi.make_transp = false;
}
new_portinfos.push_back(pi);
if (pi.dupidx == dup_count-1) {
@@ -700,7 +738,13 @@ grow_read_ports:;
if (read_transp.count(pi.transp) && read_transp.at(pi.transp) != transp) {
if (match.make_transp && wr_ports <= 1) {
pi.make_transp = true;
- enable_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;
@@ -719,7 +763,8 @@ grow_read_ports:;
if (clken) {
clock_domains[pi.clocks] = clkdom;
clock_polarities[pi.clkpol] = clkdom.second;
- read_transp[pi.transp] = transp;
+ 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;
@@ -783,6 +828,43 @@ grow_read_ports:;
return false;
}
+ for (const auto &sums : match.attributes) {
+ bool found = false;
+ for (const auto &term : sums) {
+ 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()) {
+ if (exists)
+ continue;
+ found = true;
+ break;
+ }
+ else if (!exists)
+ continue;
+ if (it->second != value)
+ continue;
+ found = true;
+ break;
+ }
+ if (!found) {
+ std::stringstream ss;
+ bool exists = std::get<0>(sums.front());
+ if (!exists)
+ ss << "!";
+ IdString key = std::get<1>(sums.front());
+ ss << log_id(key);
+ const Const &value = std::get<2>(sums.front());
+ if (exists && value != Const(1))
+ ss << "=\"" << value.decode_string() << "\"";
+
+ log(" Rule for bram type %s rejected: requirement 'attribute %s ...' not met.\n",
+ log_id(match.name), ss.str().c_str());
+ return false;
+ }
+ }
+
if (mode == 1)
return true;
}
@@ -895,17 +977,18 @@ grow_read_ports:;
} else {
SigSpec bram_dout = module->addWire(NEW_ID, bram.dbits);
c->setPort(stringf("\\%sDATA", pf), bram_dout);
-
- if (pi.make_outreg) {
+ 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;
- }
-
- if (pi.make_transp)
- {
+ } 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),
@@ -931,6 +1014,8 @@ grow_read_ports:;
SigSpec addr_ok_q = addr_ok;
if ((pi.clocks || pi.make_outreg) && !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);
}
@@ -1067,6 +1152,43 @@ void handle_cell(Cell *cell, const rules_t &rules)
goto next_match_rule;
}
+ for (const auto &sums : match.attributes) {
+ bool found = false;
+ for (const auto &term : sums) {
+ 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()) {
+ if (exists)
+ continue;
+ found = true;
+ break;
+ }
+ else if (!exists)
+ continue;
+ if (it->second != value)
+ continue;
+ found = true;
+ break;
+ }
+ if (!found) {
+ std::stringstream ss;
+ bool exists = std::get<0>(sums.front());
+ if (!exists)
+ ss << "!";
+ IdString key = std::get<1>(sums.front());
+ ss << log_id(key);
+ const Const &value = std::get<2>(sums.front());
+ if (exists && value != Const(1))
+ ss << "=\"" << value.decode_string() << "\"";
+
+ log(" Rule for bram type %s (variant %d) rejected: requirement 'attribute %s ...' not met.\n",
+ log_id(bram.name), bram.variant, ss.str().c_str());
+ goto next_match_rule;
+ }
+ }
+
log(" Rule #%d for bram type %s (variant %d) accepted.\n", i+1, log_id(bram.name), bram.variant);
if (or_next_if_better || !best_rule_cache.empty())
@@ -1120,7 +1242,7 @@ void handle_cell(Cell *cell, const rules_t &rules)
struct MemoryBramPass : public Pass {
MemoryBramPass() : Pass("memory_bram", "map memories to block rams") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -1192,6 +1314,13 @@ struct MemoryBramPass : public Pass {
log(" dcells ....... number of cells in 'data-direction'\n");
log(" cells ........ total number of cells (acells*dcells*dups)\n");
log("\n");
+ log("A match containing the command 'attribute' followed by a list of space\n");
+ log("separated 'name[=string_value]' values requires that the memory contains any\n");
+ log("one of the given attribute name and string values (where specified), or name\n");
+ log("and integer 1 value (if no string_value given, since Verilog will interpret\n");
+ log("'(* attr *)' as '(* attr=1 *)').\n");
+ log("A name prefixed with '!' indicates that the attribute must not exist.\n");
+ log("\n");
log("The interface for the created bram instances is derived from the bram\n");
log("description. Use 'techmap' to convert the created bram instances into\n");
log("instances of the actual bram cells of your target architecture.\n");
@@ -1210,7 +1339,7 @@ struct MemoryBramPass : public Pass {
log("the data bits to accommodate the enable pattern of port A.\n");
log("\n");
}
- virtual void execute(vector<string> args, Design *design)
+ void execute(vector<string> args, Design *design) YS_OVERRIDE
{
rules_t rules;