aboutsummaryrefslogtreecommitdiffstats
path: root/passes/memory
diff options
context:
space:
mode:
Diffstat (limited to 'passes/memory')
-rw-r--r--passes/memory/Makefile.inc1
-rw-r--r--passes/memory/memory.cc17
-rw-r--r--passes/memory/memory_bram.cc1174
-rw-r--r--passes/memory/memory_collect.cc130
-rw-r--r--passes/memory/memory_dff.cc16
-rw-r--r--passes/memory/memory_map.cc32
-rw-r--r--passes/memory/memory_share.cc33
-rw-r--r--passes/memory/memory_unpack.cc8
8 files changed, 1337 insertions, 74 deletions
diff --git a/passes/memory/Makefile.inc b/passes/memory/Makefile.inc
index 026c5ff85..aeff225d8 100644
--- a/passes/memory/Makefile.inc
+++ b/passes/memory/Makefile.inc
@@ -4,5 +4,6 @@ OBJS += passes/memory/memory_dff.o
OBJS += passes/memory/memory_share.o
OBJS += passes/memory/memory_collect.o
OBJS += passes/memory/memory_unpack.o
+OBJS += passes/memory/memory_bram.o
OBJS += passes/memory/memory_map.o
diff --git a/passes/memory/memory.cc b/passes/memory/memory.cc
index fc3095535..866efae77 100644
--- a/passes/memory/memory.cc
+++ b/passes/memory/memory.cc
@@ -22,13 +22,16 @@
#include <stdlib.h>
#include <stdio.h>
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
struct MemoryPass : public Pass {
MemoryPass() : Pass("memory", "translate memories to basic cells") { }
virtual void help()
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
- log(" memory [-nomap] [selection]\n");
+ log(" memory [-nomap] [-bram <bram_rules>] [selection]\n");
log("\n");
log("This pass calls all the other memory_* passes in a useful order:\n");
log("\n");
@@ -37,7 +40,8 @@ struct MemoryPass : public Pass {
log(" memory_share\n");
log(" opt_clean\n");
log(" memory_collect\n");
- log(" memory_map (skipped if called with -nomap)\n");
+ log(" memory_bram -rules <bram_rules> (when called with -bram)\n");
+ log(" memory_map (skipped if called with -nomap)\n");
log("\n");
log("This converts memories to word-wide DFFs and address decoders\n");
log("or multiport memory blocks if called with the -nomap option.\n");
@@ -46,6 +50,7 @@ struct MemoryPass : public Pass {
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
bool flag_nomap = false;
+ string memory_bram_opts;
log_header("Executing MEMORY pass.\n");
log_push();
@@ -56,6 +61,10 @@ struct MemoryPass : public Pass {
flag_nomap = true;
continue;
}
+ if (argidx+1 < args.size() && args[argidx] == "-bram") {
+ memory_bram_opts += " -rules " + args[++argidx];
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
@@ -66,6 +75,9 @@ struct MemoryPass : public Pass {
Pass::call(design, "opt_clean");
Pass::call(design, "memory_collect");
+ if (!memory_bram_opts.empty())
+ Pass::call(design, "memory_bram" + memory_bram_opts);
+
if (!flag_nomap)
Pass::call(design, "memory_map");
@@ -73,3 +85,4 @@ struct MemoryPass : public Pass {
}
} MemoryPass;
+PRIVATE_NAMESPACE_END
diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc
new file mode 100644
index 000000000..958cc88b4
--- /dev/null
+++ b/passes/memory/memory_bram.cc
@@ -0,0 +1,1174 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct rules_t
+{
+ struct portinfo_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;
+ int mapped_port;
+ };
+
+ struct bram_t {
+ IdString name;
+ int variant;
+
+ int groups, abits, dbits, init;
+ vector<int> ports, wrmode, enable, transp, clocks, clkpol;
+
+ void dump_config() const
+ {
+ log(" bram %s # variant %d\n", log_id(name), variant);
+ log(" init %d\n", init);
+ log(" abits %d\n", abits);
+ log(" dbits %d\n", dbits);
+ log(" groups %d\n", groups);
+
+ log(" ports "); for (int v : ports) log("%4d", v); log("\n");
+ log(" wrmode"); for (int v : wrmode) log("%4d", v); log("\n");
+ log(" enable"); for (int v : enable) log("%4d", v); log("\n");
+ log(" transp"); for (int v : transp) log("%4d", v); log("\n");
+ log(" clocks"); for (int v : clocks) log("%4d", v); log("\n");
+ log(" clkpol"); for (int v : clkpol) log("%4d", v); log("\n");
+ log(" endbram\n");
+ }
+
+ void check_vectors() const
+ {
+ if (groups != GetSize(ports)) log_error("Bram %s variant %d has %d groups but only %d entries in 'ports'.\n", log_id(name), variant, groups, GetSize(ports));
+ if (groups != GetSize(wrmode)) log_error("Bram %s variant %d has %d groups but only %d entries in 'wrmode'.\n", log_id(name), variant, groups, GetSize(wrmode));
+ if (groups != GetSize(enable)) log_error("Bram %s variant %d has %d groups but only %d entries in 'enable'.\n", log_id(name), variant, groups, GetSize(enable));
+ 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));
+ }
+
+ vector<portinfo_t> make_portinfos() const
+ {
+ vector<portinfo_t> portinfos;
+ for (int i = 0; i < groups; i++)
+ for (int j = 0; j < ports[i]; j++) {
+ portinfo_t pi;
+ pi.group = i;
+ pi.index = j;
+ pi.dupidx = 0;
+ pi.wrmode = wrmode[i];
+ pi.enable = enable[i];
+ pi.transp = transp[i];
+ pi.clocks = clocks[i];
+ pi.clkpol = clkpol[i];
+ pi.mapped_port = -1;
+ pi.make_transp = false;
+ pi.effective_clkpol = false;
+ portinfos.push_back(pi);
+ }
+ return portinfos;
+ }
+
+ void find_variant_params(dict<IdString, Const> &variant_params, const bram_t &other) const
+ {
+ log_assert(name == other.name);
+
+ if (groups != other.groups)
+ log_error("Bram %s variants %d and %d have different values for 'groups'.\n", log_id(name), variant, other.variant);
+
+ if (abits != other.abits)
+ variant_params["\\CFG_ABITS"] = abits;
+ if (dbits != other.dbits)
+ variant_params["\\CFG_DBITS"] = dbits;
+ if (init != other.init)
+ variant_params["\\CFG_INIT"] = init;
+
+ for (int i = 0; i < groups; i++)
+ {
+ if (ports[i] != other.ports[i])
+ log_error("Bram %s variants %d and %d have different number of %c-ports.\n", log_id(name), variant, other.variant, 'A'+i);
+ if (wrmode[i] != other.wrmode[i])
+ variant_params[stringf("\\CFG_WRMODE_%c", 'A' + i)] = wrmode[1];
+ if (enable[i] != other.enable[i])
+ variant_params[stringf("\\CFG_ENABLE_%c", 'A' + i)] = enable[1];
+ if (transp[i] != other.transp[i])
+ variant_params[stringf("\\CFG_TRANSP_%c", 'A' + i)] = transp[1];
+ if (clocks[i] != other.clocks[i])
+ variant_params[stringf("\\CFG_CLOCKS_%c", 'A' + i)] = clocks[1];
+ if (clkpol[i] != other.clkpol[i])
+ variant_params[stringf("\\CFG_CLKPOL_%c", 'A' + i)] = clkpol[1];
+ }
+ }
+ };
+
+ struct match_t {
+ IdString name;
+ dict<string, int> min_limits, max_limits;
+ bool or_next_if_better, make_transp;
+ char shuffle_enable;
+ };
+
+ dict<IdString, vector<bram_t>> brams;
+ vector<match_t> matches;
+
+ std::ifstream infile;
+ vector<string> tokens;
+ vector<string> labels;
+ int linecount;
+
+ void syntax_error()
+ {
+ if (tokens.empty())
+ log_error("Unexpected end of rules file in line %d.\n", linecount);
+ log_error("Syntax error in rules file line %d.\n", linecount);
+ }
+
+ bool next_line()
+ {
+ string line;
+ while (std::getline(infile, line)) {
+ tokens.clear();
+ labels.clear();
+ linecount++;
+ for (string tok = next_token(line); !tok.empty(); tok = next_token(line)) {
+ if (tok[0] == '@') {
+ labels.push_back(tok.substr(1));
+ continue;
+ }
+ if (tok[0] == '#')
+ break;
+ tokens.push_back(tok);
+ }
+ if (!tokens.empty())
+ return true;
+ }
+ return false;
+ }
+
+ bool parse_single_int(const char *stmt, int &value)
+ {
+ if (GetSize(tokens) == 2 && tokens[0] == stmt) {
+ value = atoi(tokens[1].c_str());
+ return true;
+ }
+ return false;
+ }
+
+ bool parse_int_vect(const char *stmt, vector<int> &value)
+ {
+ if (GetSize(tokens) >= 2 && tokens[0] == stmt) {
+ value.resize(GetSize(tokens)-1);
+ for (int i = 1; i < GetSize(tokens); i++)
+ value[i-1] = atoi(tokens[i].c_str());
+ return true;
+ }
+ return false;
+ }
+
+ void parse_bram()
+ {
+ IdString bram_name = RTLIL::escape_id(tokens[1]);
+
+ if (GetSize(tokens) != 2)
+ syntax_error();
+
+ vector<vector<string>> lines_nolabels;
+ std::map<string, vector<vector<string>>> lines_labels;
+
+ while (next_line())
+ {
+ if (GetSize(tokens) == 1 && tokens[0] == "endbram")
+ break;
+ if (labels.empty())
+ lines_nolabels.push_back(tokens);
+ for (auto lab : labels)
+ lines_labels[lab].push_back(tokens);
+ }
+
+ std::map<string, vector<vector<string>>> variant_lines;
+
+ if (lines_labels.empty())
+ variant_lines[""] = lines_nolabels;
+ for (auto &it : lines_labels) {
+ variant_lines[it.first] = lines_nolabels;
+ variant_lines[it.first].insert(variant_lines[it.first].end(), it.second.begin(), it.second.end());
+ }
+
+ for (auto &it : variant_lines)
+ {
+ bram_t data;
+ data.name = bram_name;
+ data.variant = GetSize(brams[data.name]) + 1;
+ data.groups = 0;
+ data.abits = 0;
+ data.dbits = 0;
+ data.init = 0;
+
+ for (auto &line_tokens : it.second)
+ {
+ tokens = line_tokens;
+
+ if (parse_single_int("groups", data.groups))
+ continue;
+
+ if (parse_single_int("abits", data.abits))
+ continue;
+
+ if (parse_single_int("dbits", data.dbits))
+ continue;
+
+ if (parse_single_int("init", data.init))
+ continue;
+
+ if (parse_int_vect("ports", data.ports))
+ continue;
+
+ if (parse_int_vect("wrmode", data.wrmode))
+ continue;
+
+ if (parse_int_vect("enable", data.enable))
+ continue;
+
+ if (parse_int_vect("transp", data.transp))
+ continue;
+
+ if (parse_int_vect("clocks", data.clocks))
+ continue;
+
+ if (parse_int_vect("clkpol", data.clkpol))
+ continue;
+
+ syntax_error();
+ }
+
+ data.check_vectors();
+ brams[data.name].push_back(data);
+ }
+ }
+
+ void parse_match()
+ {
+ if (GetSize(tokens) != 2)
+ syntax_error();
+
+ match_t data;
+ data.name = RTLIL::escape_id(tokens[1]);
+ data.or_next_if_better = false;
+ data.make_transp = false;
+ data.shuffle_enable = 0;
+
+ while (next_line())
+ {
+ if (!labels.empty())
+ syntax_error();
+
+ if (GetSize(tokens) == 1 && tokens[0] == "endmatch") {
+ matches.push_back(data);
+ break;
+ }
+
+ if (GetSize(tokens) == 3 && tokens[0] == "min") {
+ data.min_limits[tokens[1]] = atoi(tokens[2].c_str());
+ continue;
+ }
+
+ if (GetSize(tokens) == 3 && tokens[0] == "max") {
+ data.max_limits[tokens[1]] = atoi(tokens[2].c_str());
+ continue;
+ }
+
+ if (GetSize(tokens) == 2 && tokens[0] == "shuffle_enable" && GetSize(tokens[1]) == 1 && 'A' <= tokens[1][0] && tokens[1][0] <= 'Z') {
+ data.shuffle_enable = tokens[1][0];
+ continue;
+ }
+
+ if (GetSize(tokens) == 1 && tokens[0] == "make_transp") {
+ data.make_transp = true;
+ continue;
+ }
+
+ if (GetSize(tokens) == 1 && tokens[0] == "or_next_if_better") {
+ data.or_next_if_better = true;
+ continue;
+ }
+
+ syntax_error();
+ }
+ }
+
+ void parse(string filename)
+ {
+ if (filename.substr(0, 2) == "+/")
+ filename = proc_share_dirname() + filename.substr(1);
+
+ infile.open(filename);
+ linecount = 0;
+
+ if (infile.fail())
+ log_error("Can't open rules file `%s'.\n", filename.c_str());
+
+ while (next_line())
+ {
+ if (!labels.empty())
+ syntax_error();
+
+ if (tokens[0] == "bram") {
+ parse_bram();
+ continue;
+ }
+
+ if (tokens[0] == "match") {
+ parse_match();
+ continue;
+ }
+
+ syntax_error();
+ }
+
+ infile.close();
+ }
+};
+
+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)
+{
+ Module *module = cell->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;
+ pool<int> clocks_wr_ports;
+ pool<int> clkpol_wr_ports;
+ int clocks_max = 0;
+ int clkpol_max = 0;
+ int transp_max = 0;
+
+ clock_polarities[0] = false;
+ clock_polarities[1] = true;
+
+ for (auto &pi : portinfos) {
+ if (pi.wrmode) {
+ clocks_wr_ports.insert(pi.clocks);
+ if (pi.clkpol > 1)
+ clkpol_wr_ports.insert(pi.clkpol);
+ }
+ clocks_max = std::max(clocks_max, pi.clocks);
+ clkpol_max = std::max(clkpol_max, pi.clkpol);
+ transp_max = std::max(transp_max, pi.transp);
+ }
+
+ log(" Mapping to bram type %s (variant %d):\n", log_id(bram.name), bram.variant);
+ // bram.dump_config();
+
+ int mem_size = cell->getParam("\\SIZE").as_int();
+ int mem_abits = cell->getParam("\\ABITS").as_int();
+ int mem_width = cell->getParam("\\WIDTH").as_int();
+ // int mem_offset = cell->getParam("\\OFFSET").as_int();
+
+ int wr_ports = cell->getParam("\\WR_PORTS").as_int();
+ auto wr_clken = SigSpec(cell->getParam("\\WR_CLK_ENABLE"));
+ auto wr_clkpol = SigSpec(cell->getParam("\\WR_CLK_POLARITY"));
+ wr_clken.extend_u0(wr_ports);
+ wr_clkpol.extend_u0(wr_ports);
+
+ SigSpec wr_en = cell->getPort("\\WR_EN");
+ SigSpec wr_clk = cell->getPort("\\WR_CLK");
+ SigSpec wr_data = cell->getPort("\\WR_DATA");
+ SigSpec wr_addr = cell->getPort("\\WR_ADDR");
+
+ int rd_ports = cell->getParam("\\RD_PORTS").as_int();
+ auto rd_clken = SigSpec(cell->getParam("\\RD_CLK_ENABLE"));
+ auto rd_clkpol = SigSpec(cell->getParam("\\RD_CLK_POLARITY"));
+ auto rd_transp = SigSpec(cell->getParam("\\RD_TRANSPARENT"));
+ rd_clken.extend_u0(rd_ports);
+ rd_clkpol.extend_u0(rd_ports);
+ rd_transp.extend_u0(rd_ports);
+
+ SigSpec rd_clk = cell->getPort("\\RD_CLK");
+ SigSpec rd_data = cell->getPort("\\RD_DATA");
+ SigSpec rd_addr = cell->getPort("\\RD_ADDR");
+
+ if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0)
+ {
+ 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++) {
+ SigSpec sig;
+ for (int j = 0; j < wr_ports; j++)
+ sig.append(old_wr_en[j][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<int> shuffle_map;
+
+ 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]]);
+ shuffle_map.push_back(bits[i]);
+ }
+
+ 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);
+ shuffle_map.push_back(-1);
+ }
+ }
+
+ log(" Results of bit order shuffling:");
+ for (int v : shuffle_map)
+ log(" %d", v);
+ 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]);
+ }
+
+ // assign write ports
+
+ 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;
+ bool clkpol = wr_clkpol[cell_port_i] == State::S1;
+ SigBit clksig = wr_clk[cell_port_i];
+
+ pair<SigBit, bool> clkdom(clksig, clkpol);
+ if (!clken)
+ clkdom = pair<SigBit, bool>(State::S1, false);
+
+ log(" Write port #%d is in clock domain %s%s.\n",
+ cell_port_i, clkdom.second ? "" : "!",
+ clken ? 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 (pi.clocks == 0) {
+ log(" Bram port %c%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1);
+ goto skip_bram_wport;
+ }
+ if (clock_domains.count(pi.clocks) && clock_domains.at(pi.clocks) != clkdom) {
+ 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) {
+ log(" Bram port %c%d has incompatible clock polarity.\n", pi.group + 'A', pi.index + 1);
+ goto skip_bram_wport;
+ }
+ } else {
+ if (pi.clocks != 0) {
+ log(" Bram port %c%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1);
+ goto skip_bram_wport;
+ }
+ }
+
+ 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;
+ }
+ }
+
+ log(" Mapped to bram port %c%d.\n", pi.group + 'A', pi.index + 1);
+ pi.mapped_port = cell_port_i;
+
+ if (clken) {
+ 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;
+ }
+
+ log(" Failed to map write port #%d.\n", cell_port_i);
+ return false;
+ mapped_wr_port:;
+ }
+
+ // houskeeping stuff for growing more read ports and restarting read port assignments
+
+ int grow_read_ports_cursor = -1;
+ bool try_growing_more_read_ports = false;
+ auto backup_clock_domains = clock_domains;
+ auto backup_clock_polarities = clock_polarities;
+
+ if (0) {
+grow_read_ports:;
+ vector<rules_t::portinfo_t> new_portinfos;
+ 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();
+ }
+ new_portinfos.push_back(pi);
+ if (pi.dupidx == dup_count-1) {
+ if (pi.clocks && !clocks_wr_ports[pi.clocks])
+ pi.clocks += clocks_max;
+ if (pi.clkpol > 1 && !clkpol_wr_ports[pi.clkpol])
+ pi.clkpol += clkpol_max;
+ if (pi.transp > 1)
+ pi.transp += transp_max;
+ pi.dupidx++;
+ new_portinfos.push_back(pi);
+ }
+ }
+ try_growing_more_read_ports = false;
+ portinfos.swap(new_portinfos);
+ clock_domains = backup_clock_domains;
+ clock_polarities = backup_clock_polarities;
+ dup_count++;
+ }
+
+ read_transp.clear();
+ read_transp[0] = false;
+ read_transp[1] = true;
+
+ // assign read ports
+
+ for (int cell_port_i = 0; cell_port_i < 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];
+
+ pair<SigBit, bool> clkdom(clksig, clkpol);
+ if (!clken)
+ 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~");
+
+ for (int bram_port_i = 0; bram_port_i < GetSize(portinfos); bram_port_i++)
+ {
+ auto &pi = portinfos[bram_port_i];
+
+ if (pi.wrmode != 0 || pi.mapped_port >= 0)
+ skip_bram_rport:
+ continue;
+
+ if (clken) {
+ if (pi.clocks == 0) {
+ log(" Bram port %c%d.%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
+ goto skip_bram_rport;
+ }
+ if (clock_domains.count(pi.clocks) && clock_domains.at(pi.clocks) != clkdom) {
+ 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) {
+ 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 (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;
+ } else {
+ log(" Bram port %c%d.%d has incompatible read transparancy.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
+ goto skip_bram_rport;
+ }
+ }
+ } else {
+ if (pi.clocks != 0) {
+ log(" Bram port %c%d.%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
+ goto skip_bram_rport;
+ }
+ }
+
+ 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) {
+ clock_domains[pi.clocks] = clkdom;
+ clock_polarities[pi.clkpol] = clkdom.second;
+ read_transp[pi.transp] = transp;
+ pi.sig_clock = clkdom.first;
+ pi.effective_clkpol = clkdom.second;
+ }
+
+ 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;
+ }
+
+ goto mapped_rd_port;
+ }
+
+ log(" Failed to map read port #%d.\n", cell_port_i);
+ if (try_growing_more_read_ports) {
+ log(" Growing more read ports by duplicating bram cells.\n");
+ goto grow_read_ports;
+ }
+ return false;
+ mapped_rd_port:;
+ }
+
+ // update properties and re-check conditions
+
+ 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));
+ 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"];
+
+ log(" Updated properties: dups=%d waste=%d efficiency=%d\n",
+ match_properties["dups"], match_properties["waste"], match_properties["efficiency"]);
+
+ for (auto it : match.min_limits) {
+ if (!match_properties.count(it.first))
+ log_error("Unknown property '%s' in match rule for bram type %s.\n",
+ it.first.c_str(), log_id(match.name));
+ if (match_properties[it.first] >= it.second)
+ continue;
+ log(" Rule for bram type %s rejected: requirement 'min %s %d' not met.\n",
+ log_id(match.name), it.first.c_str(), it.second);
+ return false;
+ }
+ for (auto it : match.max_limits) {
+ if (!match_properties.count(it.first))
+ log_error("Unknown property '%s' in match rule for bram type %s.\n",
+ it.first.c_str(), log_id(match.name));
+ if (match_properties[it.first] <= it.second)
+ continue;
+ log(" Rule for bram type %s rejected: requirement 'max %s %d' not met.\n",
+ log_id(match.name), it.first.c_str(), it.second);
+ return false;
+ }
+
+ if (mode == 1)
+ return true;
+ }
+
+ // prepare variant parameters
+
+ dict<IdString, Const> variant_params;
+ for (auto &other_bram : rules.brams.at(bram.name))
+ bram.find_variant_params(variant_params, other_bram);
+
+ // actually replace that memory cell
+
+ dict<SigSpec, pair<SigSpec, SigSpec>> dout_cache;
+
+ for (int grid_d = 0; grid_d*bram.dbits < mem_width; 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 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);
+ 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);
+
+ for (auto &pi : portinfos)
+ {
+ if (pi.dupidx != dupidx)
+ continue;
+
+ 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));
+ }
+
+ 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 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_en.extend_u0((grid_d+1) * pi.enable);
+ sig_en = sig_en.extract(grid_d * pi.enable, pi.enable);
+
+ 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);
+ }
+
+ SigSpec sig_addr = pi.sig_addr;
+ sig_addr.extend_u0(bram.abits);
+ c->setPort(stringf("\\%sADDR", pf), sig_addr);
+
+ if (pi.wrmode == 1 && enable_make_transp && grid_a == 0)
+ module->connect(mktr_wraddr, sig_addr);
+
+ 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);
+
+ 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_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]));
+ }
+ }
+
+ for (int i = bram.dbits-1; i >= 0; i--)
+ if (sig_data[i].wire == nullptr) {
+ sig_data.remove(i);
+ bram_dout.remove(i);
+ }
+
+ SigSpec addr_ok_q = addr_ok;
+ if (pi.clocks && !addr_ok.empty()) {
+ addr_ok_q = module->addWire(NEW_ID);
+ module->addDff(NEW_ID, pi.sig_clock, addr_ok, addr_ok_q, pi.effective_clkpol);
+ }
+
+ dout_cache[sig_data].first.append(addr_ok_q);
+ dout_cache[sig_data].second.append(bram_dout);
+ }
+ }
+ }
+ }
+
+ for (auto &it : dout_cache)
+ {
+ if (it.second.first.empty())
+ {
+ log_assert(GetSize(it.first) == GetSize(it.second.second));
+ module->connect(it.first, it.second.second);
+ }
+ else
+ {
+ log_assert(GetSize(it.first)*GetSize(it.second.first) == GetSize(it.second.second));
+ module->addPmux(NEW_ID, SigSpec(State::Sx, GetSize(it.first)), it.second.second, it.second.first, it.first);
+ }
+ }
+
+ module->remove(cell);
+ return true;
+}
+
+void handle_cell(Cell *cell, const rules_t &rules)
+{
+ log("Processing %s.%s:\n", log_id(cell->module), log_id(cell));
+
+ if (!SigSpec(cell->getParam("\\INIT")).is_fully_undef()) {
+ log(" initialized memories are not supported yet.");
+ return;
+ }
+
+ dict<string, int> match_properties;
+ match_properties["words"] = cell->getParam("\\SIZE").as_int();
+ match_properties["abits"] = cell->getParam("\\ABITS").as_int();
+ match_properties["dbits"] = cell->getParam("\\WIDTH").as_int();
+ match_properties["wports"] = cell->getParam("\\WR_PORTS").as_int();
+ match_properties["rports"] = cell->getParam("\\RD_PORTS").as_int();
+ match_properties["bits"] = match_properties["words"] * match_properties["dbits"];
+ match_properties["ports"] = match_properties["wports"] + match_properties["rports"];
+
+ log(" Properties:");
+ for (auto &it : match_properties)
+ log(" %s=%d", it.first.c_str(), it.second);
+ log("\n");
+
+ pool<pair<IdString, int>> failed_brams;
+ dict<pair<int, int>, std::tuple<int, int, int>> best_rule_cache;
+
+ for (int i = 0; i < GetSize(rules.matches); i++)
+ {
+ auto &match = rules.matches.at(i);
+
+ if (!rules.brams.count(rules.matches[i].name))
+ log_error("No bram description for resource %s found!\n", log_id(rules.matches[i].name));
+
+ for (int vi = 0; vi < GetSize(rules.brams.at(match.name)); vi++)
+ {
+ auto &bram = rules.brams.at(match.name).at(vi);
+ bool or_next_if_better = match.or_next_if_better || vi+1 < GetSize(rules.brams.at(match.name));
+
+ if (failed_brams.count(pair<IdString, int>(bram.name, bram.variant)))
+ continue;
+
+ int avail_rd_ports = 0;
+ int avail_wr_ports = 0;
+ for (int j = 0; j < bram.groups; j++) {
+ if (GetSize(bram.wrmode) < j || bram.wrmode.at(j) == 0)
+ avail_rd_ports += GetSize(bram.ports) < j ? bram.ports.at(j) : 0;
+ if (GetSize(bram.wrmode) < j || bram.wrmode.at(j) != 0)
+ avail_wr_ports += GetSize(bram.ports) < j ? bram.ports.at(j) : 0;
+ }
+
+ log(" Checking rule #%d for bram type %s (variant %d):\n", i+1, log_id(bram.name), bram.variant);
+ log(" Bram geometry: abits=%d dbits=%d wports=%d rports=%d\n", bram.abits, bram.dbits, avail_wr_ports, avail_rd_ports);
+
+ int dups = avail_rd_ports ? (match_properties["rports"] + avail_rd_ports - 1) / avail_rd_ports : 1;
+ match_properties["dups"] = dups;
+
+ log(" Estimated number of duplicates for more read ports: dups=%d\n", match_properties["dups"]);
+
+ int aover = match_properties["words"] % (1 << bram.abits);
+ int awaste = aover ? (1 << bram.abits) - aover : 0;
+ match_properties["awaste"] = awaste;
+
+ int dover = match_properties["dbits"] % bram.dbits;
+ int dwaste = dover ? bram.dbits - dover : 0;
+ match_properties["dwaste"] = dwaste;
+
+ int bwaste = awaste * bram.dbits + dwaste * (1 << bram.abits) - awaste * dwaste;
+ match_properties["bwaste"] = bwaste;
+
+ int waste = match_properties["dups"] * bwaste;
+ match_properties["waste"] = waste;
+
+ int cells = ((match_properties["dbits"] + bram.dbits - 1) / bram.dbits) * ((match_properties["words"] + (1 << bram.abits) - 1) / (1 << bram.abits));
+ int efficiency = (100 * match_properties["bits"]) / (dups * cells * bram.dbits * (1 << bram.abits));
+ match_properties["efficiency"] = efficiency;
+
+ log(" Metrics for %s: awaste=%d dwaste=%d bwaste=%d waste=%d efficiency=%d\n",
+ log_id(match.name), awaste, dwaste, bwaste, waste, efficiency);
+
+ for (auto it : match.min_limits) {
+ if (it.first == "waste" || it.first == "dups" || it.first == "acells" || it.first == "dcells" || it.first == "cells")
+ continue;
+ if (!match_properties.count(it.first))
+ log_error("Unknown property '%s' in match rule for bram type %s.\n",
+ it.first.c_str(), log_id(match.name));
+ if (match_properties[it.first] >= it.second)
+ continue;
+ log(" Rule #%d for bram type %s (variant %d) rejected: requirement 'min %s %d' not met.\n",
+ i+1, log_id(bram.name), bram.variant, it.first.c_str(), it.second);
+ goto next_match_rule;
+ }
+ for (auto it : match.max_limits) {
+ if (it.first == "acells" || it.first == "dcells" || it.first == "cells")
+ continue;
+ if (!match_properties.count(it.first))
+ log_error("Unknown property '%s' in match rule for bram type %s.\n",
+ it.first.c_str(), log_id(match.name));
+ if (match_properties[it.first] <= it.second)
+ continue;
+ log(" Rule #%d for bram type %s (variant %d) rejected: requirement 'max %s %d' not met.\n",
+ i+1, log_id(bram.name), bram.variant, it.first.c_str(), it.second);
+ 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())
+ {
+ 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)) {
+ 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;
+ }
+
+ log(" Storing for later selection.\n");
+ best_rule_cache[pair<int, int>(i, vi)] = std::tuple<int, int, int>(match_properties["efficiency"], -match_properties["cells"], -match_properties["acells"]);
+
+ if (or_next_if_better)
+ goto next_match_rule;
+
+ next_match_rule:
+ if (or_next_if_better || best_rule_cache.empty())
+ continue;
+
+ log(" Selecting best of %d rules:\n", GetSize(best_rule_cache));
+ pair<int, int> best_rule = best_rule_cache.begin()->first;
+
+ for (auto &it : best_rule_cache) {
+ if (it.second > best_rule_cache[best_rule])
+ best_rule = it.first;
+ log(" Efficiency for rule %d.%d: efficiency=%d, cells=%d, acells=%d\n", it.first.first+1, it.first.second+1,
+ std::get<0>(it.second), -std::get<1>(it.second), -std::get<2>(it.second));
+ }
+
+ log(" Selected rule %d.%d with efficiency %d.\n", best_rule.first+1, best_rule.second+1, std::get<0>(best_rule_cache[best_rule]));
+ 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))
+ 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)) {
+ 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;
+ }
+ return;
+ }
+ }
+
+ log(" No acceptable bram resources found.\n");
+}
+
+struct MemoryBramPass : public Pass {
+ MemoryBramPass() : Pass("memory_bram", "map memories to block rams") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" memory_bram -rules <rule_file> [selection]\n");
+ log("\n");
+ log("This pass converts the multi-port $mem memory cells into block ram instances.\n");
+ log("The given rules file describes the available resources and how they should be\n");
+ log("used.\n");
+ log("\n");
+ log("The rules file contains a set of block ram description and a sequence of match\n");
+ log("rules. A block ram description looks like this:\n");
+ log("\n");
+ log(" bram RAMB1024X32 # name of BRAM cell\n");
+ // log(" init 1 # set to '1' if BRAM can be initialized\n");
+ log(" abits 10 # number of address bits\n");
+ log(" dbits 32 # number of data bits\n");
+ log(" groups 2 # number of port groups\n");
+ log(" ports 1 1 # number of ports in each group\n");
+ log(" wrmode 1 0 # set to '1' if this groups is write ports\n");
+ log(" enable 4 0 # number of enable bits (for write ports)\n");
+ log(" transp 0 2 # transparatent (for read ports)\n");
+ log(" clocks 1 2 # clock configuration\n");
+ log(" clkpol 2 2 # clock polarity configuration\n");
+ log(" endbram\n");
+ log("\n");
+ log("For the option 'transp' the value 0 means non-transparent, 1 means transparent\n");
+ log("and a value greater than 1 means configurable. All groups with the same\n");
+ log("value greater than 1 share the same configuration bit.\n");
+ log("\n");
+ log("For the option 'clocks' the value 0 means non-clocked, and a value greater\n");
+ log("than 0 means clocked. All groups with the same value share the same clock\n");
+ log("signal.\n");
+ log("\n");
+ log("For the option 'clkpol' the value 0 means negative edge, 1 means positive edge\n");
+ log("and a value greater than 1 means configurable. All groups with the same value\n");
+ log("greater than 1 share the same configuration bit.\n");
+ log("\n");
+ log("Using the same bram name in different bram blocks will create different variants\n");
+ log("of the bram. Verilog configration parameters for the bram are created as needed.\n");
+ log("\n");
+ log("It is also possible to create variants by repeating statements in the bram block\n");
+ log("and appending '@<label>' to the individual statements.\n");
+ log("\n");
+ log("A match rule looks like this:\n");
+ log("\n");
+ log(" match RAMB1024X32\n");
+ log(" max waste 16384 # only use this bram if <= 16k ram bits are unused\n");
+ log(" min efficiency 80 # only use this bram if efficiency is at least 80%%\n");
+ log(" endmatch\n");
+ log("\n");
+ log("It is possible to match against the following values with min/max rules:\n");
+ log("\n");
+ log(" words ........ number of words in memory in design\n");
+ log(" abits ........ number of address bits on memory in design\n");
+ log(" dbits ........ number of data bits on memory in design\n");
+ log(" wports ....... number of write ports on memory in design\n");
+ log(" rports ....... number of read ports on memory in design\n");
+ log(" ports ........ number of ports on memory in design\n");
+ log(" bits ......... number of bits in memory in design\n");
+ log(" dups .......... number of duplications for more read ports\n");
+ log("\n");
+ log(" awaste ....... number of unused address slots for this match\n");
+ log(" dwaste ....... number of unused data bits for this match\n");
+ log(" bwaste ....... number of unused bram bits for this match\n");
+ log(" waste ........ total number of unused bram bits (bwaste*dups)\n");
+ log(" efficiency ... total percentage of used and non-duplicated bits\n");
+ log("\n");
+ log(" acells ....... number of cells in 'address-direction'\n");
+ log(" dcells ....... number of cells in 'data-direction'\n");
+ log(" cells ........ total number of cells (acells*dcells*dups)\n");
+ log("\n");
+ log("The interface for the created bram instances is dervived 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");
+ log("\n");
+ log("A match containing the command 'or_next_if_better' is only used if it\n");
+ log("has a higher efficiency than the next match (and the one after that if\n");
+ log("the next also has 'or_next_if_better' set, and so forth).\n");
+ log("\n");
+ log("A match containing the command 'make_transp' will add external circuitry\n");
+ log("to simulate 'transparent read', if necessary.\n");
+ log("\n");
+ log("A match containing the command 'shuffle_enable A' will re-organize\n");
+ log("the data bits to accommodate the enable pattern of port A.\n");
+ log("\n");
+ }
+ virtual void execute(vector<string> args, Design *design)
+ {
+ rules_t rules;
+
+ log_header("Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-rules" && argidx+1 < args.size()) {
+ rules.parse(args[++argidx]);
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto mod : design->selected_modules())
+ for (auto cell : mod->selected_cells())
+ if (cell->type == "$mem")
+ handle_cell(cell, rules);
+ }
+} MemoryBramPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/memory/memory_collect.cc b/passes/memory/memory_collect.cc
index 9c670f00f..96d0ada03 100644
--- a/passes/memory/memory_collect.cc
+++ b/passes/memory/memory_collect.cc
@@ -17,13 +17,13 @@
*
*/
-#include "kernel/register.h"
-#include "kernel/log.h"
-#include <sstream>
-#include <algorithm>
-#include <stdlib.h>
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
-static bool memcells_cmp(RTLIL::Cell *a, RTLIL::Cell *b)
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+bool memcells_cmp(RTLIL::Cell *a, RTLIL::Cell *b)
{
if (a->type == "$memrd" && b->type == "$memrd")
return a->name < b->name;
@@ -32,14 +32,15 @@ static bool memcells_cmp(RTLIL::Cell *a, RTLIL::Cell *b)
return a->parameters.at("\\PRIORITY").as_int() < b->parameters.at("\\PRIORITY").as_int();
}
-static void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory)
+void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory)
{
- log("Collecting $memrd and $memwr for memory `%s' in module `%s':\n",
+ log("Collecting $memrd, $memwr and $meminit for memory `%s' in module `%s':\n",
memory->name.c_str(), module->name.c_str());
int addr_bits = 0;
- while ((1 << addr_bits) < memory->size)
- addr_bits++;
+
+ Const init_data(State::Sx, memory->size * memory->width);
+ SigMap sigmap(module);
int wr_ports = 0;
RTLIL::SigSpec sig_wr_clk;
@@ -57,37 +58,64 @@ static void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory)
RTLIL::SigSpec sig_rd_addr;
RTLIL::SigSpec sig_rd_data;
- std::vector<RTLIL::Cell*> del_cells;
std::vector<RTLIL::Cell*> memcells;
for (auto &cell_it : module->cells_) {
RTLIL::Cell *cell = cell_it.second;
- if ((cell->type == "$memwr" || cell->type == "$memrd") && memory->name == cell->parameters["\\MEMID"].decode_string())
+ if (cell->type.in("$memrd", "$memwr", "$meminit") && memory->name == cell->parameters["\\MEMID"].decode_string()) {
+ addr_bits = std::max(addr_bits, cell->getParam("\\ABITS").as_int());
memcells.push_back(cell);
+ }
+ }
+
+ if (memcells.empty()) {
+ log(" no cells found. removing memory.\n");
+ return;
}
std::sort(memcells.begin(), memcells.end(), memcells_cmp);
for (auto cell : memcells)
{
- if (cell->type == "$memwr" && memory->name == cell->parameters["\\MEMID"].decode_string())
+ log(" %s (%s)\n", log_id(cell), log_id(cell->type));
+
+ if (cell->type == "$meminit")
{
- wr_ports++;
- del_cells.push_back(cell);
-
- RTLIL::SigSpec clk = cell->getPort("\\CLK");
- RTLIL::SigSpec clk_enable = RTLIL::SigSpec(cell->parameters["\\CLK_ENABLE"]);
- RTLIL::SigSpec clk_polarity = RTLIL::SigSpec(cell->parameters["\\CLK_POLARITY"]);
- RTLIL::SigSpec addr = cell->getPort("\\ADDR");
- RTLIL::SigSpec data = cell->getPort("\\DATA");
- RTLIL::SigSpec en = cell->getPort("\\EN");
-
- clk.extend(1, false);
- clk_enable.extend(1, false);
- clk_polarity.extend(1, false);
- addr.extend(addr_bits, false);
- data.extend(memory->width, false);
- en.extend(memory->width, false);
+ SigSpec addr = sigmap(cell->getPort("\\ADDR"));
+ SigSpec data = sigmap(cell->getPort("\\DATA"));
+
+ if (!addr.is_fully_const())
+ log_error("Non-constant address %s in memory initialization %s.\n", log_signal(addr), log_id(cell));
+ if (!data.is_fully_const())
+ log_error("Non-constant data %s in memory initialization %s.\n", log_signal(data), log_id(cell));
+
+ int offset = (addr.as_int() - memory->start_offset) * memory->width;
+
+ if (offset < 0 || offset + GetSize(data) > GetSize(init_data))
+ log_warning("Address %s in memory initialization %s is out-of-bounds.\n", log_signal(addr), log_id(cell));
+
+ for (int i = 0; i < GetSize(data); i++)
+ if (0 <= i+offset && i+offset < GetSize(init_data))
+ init_data.bits[i+offset] = data[i].data;
+
+ continue;
+ }
+
+ if (cell->type == "$memwr")
+ {
+ SigSpec clk = sigmap(cell->getPort("\\CLK"));
+ SigSpec clk_enable = RTLIL::SigSpec(cell->parameters["\\CLK_ENABLE"]);
+ SigSpec clk_polarity = RTLIL::SigSpec(cell->parameters["\\CLK_POLARITY"]);
+ SigSpec addr = sigmap(cell->getPort("\\ADDR"));
+ SigSpec data = sigmap(cell->getPort("\\DATA"));
+ SigSpec en = sigmap(cell->getPort("\\EN"));
+
+ clk.extend_u0(1, false);
+ clk_enable.extend_u0(1, false);
+ clk_polarity.extend_u0(1, false);
+ addr.extend_u0(addr_bits, false);
+ data.extend_u0(memory->width, false);
+ en.extend_u0(memory->width, false);
sig_wr_clk.append(clk);
sig_wr_clk_enable.append(clk_enable);
@@ -95,26 +123,26 @@ static void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory)
sig_wr_addr.append(addr);
sig_wr_data.append(data);
sig_wr_en.append(en);
+
+ wr_ports++;
+ continue;
}
- if (cell->type == "$memrd" && memory->name == cell->parameters["\\MEMID"].decode_string())
+ if (cell->type == "$memrd")
{
- rd_ports++;
- del_cells.push_back(cell);
-
- RTLIL::SigSpec clk = cell->getPort("\\CLK");
- RTLIL::SigSpec clk_enable = RTLIL::SigSpec(cell->parameters["\\CLK_ENABLE"]);
- RTLIL::SigSpec clk_polarity = RTLIL::SigSpec(cell->parameters["\\CLK_POLARITY"]);
- RTLIL::SigSpec transparent = RTLIL::SigSpec(cell->parameters["\\TRANSPARENT"]);
- RTLIL::SigSpec addr = cell->getPort("\\ADDR");
- RTLIL::SigSpec data = cell->getPort("\\DATA");
-
- clk.extend(1, false);
- clk_enable.extend(1, false);
- clk_polarity.extend(1, false);
- transparent.extend(1, false);
- addr.extend(addr_bits, false);
- data.extend(memory->width, false);
+ SigSpec clk = sigmap(cell->getPort("\\CLK"));
+ SigSpec clk_enable = RTLIL::SigSpec(cell->parameters["\\CLK_ENABLE"]);
+ SigSpec clk_polarity = RTLIL::SigSpec(cell->parameters["\\CLK_POLARITY"]);
+ SigSpec transparent = RTLIL::SigSpec(cell->parameters["\\TRANSPARENT"]);
+ SigSpec addr = sigmap(cell->getPort("\\ADDR"));
+ SigSpec data = sigmap(cell->getPort("\\DATA"));
+
+ clk.extend_u0(1, false);
+ clk_enable.extend_u0(1, false);
+ clk_polarity.extend_u0(1, false);
+ transparent.extend_u0(1, false);
+ addr.extend_u0(addr_bits, false);
+ data.extend_u0(memory->width, false);
sig_rd_clk.append(clk);
sig_rd_clk_enable.append(clk_enable);
@@ -122,6 +150,9 @@ static void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory)
sig_rd_transparent.append(transparent);
sig_rd_addr.append(addr);
sig_rd_data.append(data);
+
+ rd_ports++;
+ continue;
}
}
@@ -135,6 +166,10 @@ static void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory)
mem->parameters["\\SIZE"] = RTLIL::Const(memory->size);
mem->parameters["\\ABITS"] = RTLIL::Const(addr_bits);
+ while (GetSize(init_data) > 1 && init_data.bits.back() == State::Sx && init_data.bits[GetSize(init_data)-2] == State::Sx)
+ init_data.bits.pop_back();
+ mem->parameters["\\INIT"] = init_data;
+
log_assert(sig_wr_clk.size() == wr_ports);
log_assert(sig_wr_clk_enable.size() == wr_ports && sig_wr_clk_enable.is_fully_const());
log_assert(sig_wr_clk_polarity.size() == wr_ports && sig_wr_clk_polarity.is_fully_const());
@@ -166,7 +201,7 @@ static void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory)
mem->setPort("\\RD_ADDR", sig_rd_addr);
mem->setPort("\\RD_DATA", sig_rd_data);
- for (auto c : del_cells)
+ for (auto c : memcells)
module->remove(c);
}
@@ -205,3 +240,4 @@ struct MemoryCollectPass : public Pass {
}
} MemoryCollectPass;
+PRIVATE_NAMESPACE_END
diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc
index 302ab3abf..d3cc681a2 100644
--- a/passes/memory/memory_dff.cc
+++ b/passes/memory/memory_dff.cc
@@ -22,13 +22,16 @@
#include <stdlib.h>
#include <sstream>
-static void normalize_sig(RTLIL::Module *module, RTLIL::SigSpec &sig)
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+void normalize_sig(RTLIL::Module *module, RTLIL::SigSpec &sig)
{
for (auto &conn : module->connections())
sig.replace(conn.first, conn.second);
}
-static bool find_sig_before_dff(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff_cells, RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, bool after = false)
+bool find_sig_before_dff(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff_cells, RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, bool after = false)
{
normalize_sig(module, sig);
@@ -66,7 +69,7 @@ static bool find_sig_before_dff(RTLIL::Module *module, std::vector<RTLIL::Cell*>
return true;
}
-static void handle_wr_cell(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff_cells, RTLIL::Cell *cell)
+void handle_wr_cell(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff_cells, RTLIL::Cell *cell)
{
log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str());
@@ -105,7 +108,7 @@ static void handle_wr_cell(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff
log("no (compatible) $dff found.\n");
}
-static void disconnect_dff(RTLIL::Module *module, RTLIL::SigSpec sig)
+void disconnect_dff(RTLIL::Module *module, RTLIL::SigSpec sig)
{
normalize_sig(module, sig);
sig.sort_and_unify();
@@ -123,7 +126,7 @@ static void disconnect_dff(RTLIL::Module *module, RTLIL::SigSpec sig)
}
}
-static void handle_rd_cell(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff_cells, RTLIL::Cell *cell)
+void handle_rd_cell(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff_cells, RTLIL::Cell *cell)
{
log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str());
@@ -161,7 +164,7 @@ static void handle_rd_cell(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff
log("no (compatible) $dff found.\n");
}
-static void handle_module(RTLIL::Module *module, bool flag_wr_only)
+void handle_module(RTLIL::Module *module, bool flag_wr_only)
{
std::vector<RTLIL::Cell*> dff_cells;
@@ -216,3 +219,4 @@ struct MemoryDffPass : public Pass {
}
} MemoryDffPass;
+PRIVATE_NAMESPACE_END
diff --git a/passes/memory/memory_map.cc b/passes/memory/memory_map.cc
index eecb6f35d..41c4a7b12 100644
--- a/passes/memory/memory_map.cc
+++ b/passes/memory/memory_map.cc
@@ -23,6 +23,9 @@
#include <set>
#include <stdlib.h>
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
struct MemoryMapWorker
{
RTLIL::Design *design;
@@ -55,21 +58,21 @@ struct MemoryMapWorker
RTLIL::Wire *addr_decode(RTLIL::SigSpec addr_sig, RTLIL::SigSpec addr_val)
{
std::pair<RTLIL::SigSpec, RTLIL::SigSpec> key(addr_sig, addr_val);
- log_assert(SIZE(addr_sig) == SIZE(addr_val));
+ log_assert(GetSize(addr_sig) == GetSize(addr_val));
if (decoder_cache.count(key) == 0) {
- if (SIZE(addr_sig) < 2) {
+ if (GetSize(addr_sig) < 2) {
decoder_cache[key] = module->Eq(NEW_ID, addr_sig, addr_val);
} else {
- int split_at = SIZE(addr_sig) / 2;
+ int split_at = GetSize(addr_sig) / 2;
RTLIL::SigBit left_eq = addr_decode(addr_sig.extract(0, split_at), addr_val.extract(0, split_at));
- RTLIL::SigBit right_eq = addr_decode(addr_sig.extract(split_at, SIZE(addr_sig) - split_at), addr_val.extract(split_at, SIZE(addr_val) - split_at));
+ RTLIL::SigBit right_eq = addr_decode(addr_sig.extract(split_at, GetSize(addr_sig) - split_at), addr_val.extract(split_at, GetSize(addr_val) - split_at));
decoder_cache[key] = module->And(NEW_ID, left_eq, right_eq);
}
}
RTLIL::SigBit bit = decoder_cache.at(key);
- log_assert(bit.wire != nullptr && SIZE(bit.wire) == 1);
+ log_assert(bit.wire != nullptr && GetSize(bit.wire) == 1);
return bit.wire;
}
@@ -77,11 +80,15 @@ struct MemoryMapWorker
{
std::set<int> static_ports;
std::map<int, RTLIL::SigSpec> static_cells_map;
+
int mem_size = cell->parameters["\\SIZE"].as_int();
int mem_width = cell->parameters["\\WIDTH"].as_int();
int mem_offset = cell->parameters["\\OFFSET"].as_int();
int mem_abits = cell->parameters["\\ABITS"].as_int();
+ SigSpec init_data = cell->getParam("\\INIT");
+ init_data.extend_u0(mem_size*mem_width, true);
+
// delete unused memory cell
if (cell->parameters["\\RD_PORTS"].as_int() == 0 && cell->parameters["\\WR_PORTS"].as_int() == 0) {
module->remove(cell);
@@ -107,7 +114,7 @@ struct MemoryMapWorker
// FIXME: Actually we should check for wr_en.is_fully_const() also and
// create a $adff cell with this ports wr_en input as reset pin when wr_en
// is not a simple static 1.
- static_cells_map[wr_addr.as_int()] = wr_data;
+ static_cells_map[wr_addr.as_int() - mem_offset] = wr_data;
static_ports.insert(i);
continue;
}
@@ -162,7 +169,10 @@ struct MemoryMapWorker
w_out_name = genid(cell->name, "", i, "$q");
RTLIL::Wire *w_out = module->addWire(w_out_name, mem_width);
- w_out->start_offset = mem_offset;
+ SigSpec w_init = init_data.extract(i*mem_width, mem_width);
+
+ if (!w_init.is_fully_undef())
+ w_out->attributes["\\init"] = w_init.as_const();
data_reg_out.push_back(RTLIL::SigSpec(w_out));
c->setPort("\\Q", data_reg_out.back());
@@ -177,6 +187,9 @@ struct MemoryMapWorker
{
RTLIL::SigSpec rd_addr = cell->getPort("\\RD_ADDR").extract(i*mem_abits, mem_abits);
+ if (mem_offset)
+ rd_addr = module->Sub(NEW_ID, rd_addr, SigSpec(mem_offset, GetSize(rd_addr)));
+
std::vector<RTLIL::SigSpec> rd_signals;
rd_signals.push_back(cell->getPort("\\RD_DATA").extract(i*mem_width, mem_width));
@@ -253,6 +266,10 @@ struct MemoryMapWorker
RTLIL::SigSpec wr_addr = cell->getPort("\\WR_ADDR").extract(j*mem_abits, mem_abits);
RTLIL::SigSpec wr_data = cell->getPort("\\WR_DATA").extract(j*mem_width, mem_width);
RTLIL::SigSpec wr_en = cell->getPort("\\WR_EN").extract(j*mem_width, mem_width);
+
+ if (mem_offset)
+ wr_addr = module->Sub(NEW_ID, wr_addr, SigSpec(mem_offset, GetSize(wr_addr)));
+
RTLIL::Wire *w_seladdr = addr_decode(wr_addr, RTLIL::SigSpec(i, mem_abits));
int wr_offset = 0;
@@ -339,3 +356,4 @@ struct MemoryMapPass : public Pass {
}
} MemoryMapPass;
+PRIVATE_NAMESPACE_END
diff --git a/passes/memory/memory_share.cc b/passes/memory/memory_share.cc
index 3ae0cd2c7..3d241433a 100644
--- a/passes/memory/memory_share.cc
+++ b/passes/memory/memory_share.cc
@@ -22,9 +22,10 @@
#include "kernel/sigtools.h"
#include "kernel/modtools.h"
+USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-static bool memcells_cmp(RTLIL::Cell *a, RTLIL::Cell *b)
+bool memcells_cmp(RTLIL::Cell *a, RTLIL::Cell *b)
{
if (a->type == "$memrd" && b->type == "$memrd")
return a->name < b->name;
@@ -488,8 +489,8 @@ struct MemoryShareWorker
if (wr_ports.size() <= 1)
return;
- ezDefaultSAT ez;
- SatGen satgen(&ez, &modwalker.sigmap);
+ ezSatPtr ez;
+ SatGen satgen(ez.get(), &modwalker.sigmap);
// find list of considered ports and port pairs
@@ -543,6 +544,7 @@ struct MemoryShareWorker
// create SAT representation of common input cone of all considered EN signals
+ pool<Wire*> one_hot_wires;
std::set<RTLIL::Cell*> sat_cells;
std::set<RTLIL::SigBit> bits_queue;
std::map<int, int> port_to_sat_variable;
@@ -551,7 +553,7 @@ struct MemoryShareWorker
if (considered_port_pairs.count(i) || considered_port_pairs.count(i+1))
{
RTLIL::SigSpec sig = modwalker.sigmap(wr_ports[i]->getPort("\\EN"));
- port_to_sat_variable[i] = ez.expression(ez.OpOr, satgen.importSigSpec(sig));
+ port_to_sat_variable[i] = ez->expression(ez->OpOr, satgen.importSigSpec(sig));
std::vector<RTLIL::SigBit> bits = sig;
bits_queue.insert(bits.begin(), bits.end());
@@ -559,24 +561,36 @@ struct MemoryShareWorker
while (!bits_queue.empty())
{
- std::set<ModWalker::PortBit> portbits;
+ for (auto bit : bits_queue)
+ if (bit.wire && bit.wire->get_bool_attribute("\\onehot"))
+ one_hot_wires.insert(bit.wire);
+
+ pool<ModWalker::PortBit> portbits;
modwalker.get_drivers(portbits, bits_queue);
bits_queue.clear();
for (auto &pbit : portbits)
if (sat_cells.count(pbit.cell) == 0 && cone_ct.cell_known(pbit.cell->type)) {
- std::set<RTLIL::SigBit> &cell_inputs = modwalker.cell_inputs[pbit.cell];
+ pool<RTLIL::SigBit> &cell_inputs = modwalker.cell_inputs[pbit.cell];
bits_queue.insert(cell_inputs.begin(), cell_inputs.end());
sat_cells.insert(pbit.cell);
}
}
+ for (auto wire : one_hot_wires) {
+ log(" Adding one-hot constraint for wire %s.\n", log_id(wire));
+ vector<int> ez_wire_bits = satgen.importSigSpec(wire);
+ for (int i : ez_wire_bits)
+ for (int j : ez_wire_bits)
+ if (i != j) ez->assume(ez->NOT(i), j);
+ }
+
log(" Common input cone for all EN signals: %d cells.\n", int(sat_cells.size()));
for (auto cell : sat_cells)
satgen.importCell(cell);
- log(" Size of unconstrained SAT problem: %d variables, %d clauses\n", ez.numCnfVariables(), ez.numCnfClauses());
+ log(" Size of unconstrained SAT problem: %d variables, %d clauses\n", ez->numCnfVariables(), ez->numCnfClauses());
// merge subsequent ports if possible
@@ -585,13 +599,13 @@ struct MemoryShareWorker
if (!considered_port_pairs.count(i))
continue;
- if (ez.solve(port_to_sat_variable.at(i-1), port_to_sat_variable.at(i))) {
+ if (ez->solve(port_to_sat_variable.at(i-1), port_to_sat_variable.at(i))) {
log(" According to SAT solver sharing of port %d with port %d is not possible.\n", i-1, i);
continue;
}
log(" Merging port %d into port %d.\n", i-1, i);
- port_to_sat_variable.at(i) = ez.OR(port_to_sat_variable.at(i-1), port_to_sat_variable.at(i));
+ port_to_sat_variable.at(i) = ez->OR(port_to_sat_variable.at(i-1), port_to_sat_variable.at(i));
RTLIL::SigSpec last_addr = wr_ports[i-1]->getPort("\\ADDR");
RTLIL::SigSpec last_data = wr_ports[i-1]->getPort("\\DATA");
@@ -741,4 +755,3 @@ struct MemorySharePass : public Pass {
} MemorySharePass;
PRIVATE_NAMESPACE_END
-
diff --git a/passes/memory/memory_unpack.cc b/passes/memory/memory_unpack.cc
index 5a4c4eac9..e650facb4 100644
--- a/passes/memory/memory_unpack.cc
+++ b/passes/memory/memory_unpack.cc
@@ -23,7 +23,10 @@
#include <algorithm>
#include <stdlib.h>
-static void handle_memory(RTLIL::Module *module, RTLIL::Cell *memory)
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+void handle_memory(RTLIL::Module *module, RTLIL::Cell *memory)
{
log("Creating $memrd and $memwr for memory `%s' in module `%s':\n",
memory->name.c_str(), module->name.c_str());
@@ -76,7 +79,7 @@ static void handle_memory(RTLIL::Module *module, RTLIL::Cell *memory)
module->remove(memory);
}
-static void handle_module(RTLIL::Design *design, RTLIL::Module *module)
+void handle_module(RTLIL::Design *design, RTLIL::Module *module)
{
std::vector<RTLIL::IdString> memcells;
for (auto &cell_it : module->cells_)
@@ -107,3 +110,4 @@ struct MemoryUnpackPass : public Pass {
}
} MemoryUnpackPass;
+PRIVATE_NAMESPACE_END