* 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
SigSpec sig_addr, sig_data, sig_en;
bool effective_clkpol;
bool make_transp;
+ bool make_outreg;
int mapped_port;
};
pi.clkpol = clkpol[i];
pi.mapped_port = -1;
pi.make_transp = false;
+ pi.make_outreg = false;
pi.effective_clkpol = false;
portinfos.push_back(pi);
}
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];
+ variant_params[stringf("\\CFG_WRMODE_%c", 'A' + i)] = wrmode[i];
if (enable[i] != other.enable[i])
- variant_params[stringf("\\CFG_ENABLE_%c", 'A' + i)] = enable[1];
+ variant_params[stringf("\\CFG_ENABLE_%c", 'A' + i)] = enable[i];
if (transp[i] != other.transp[i])
- variant_params[stringf("\\CFG_TRANSP_%c", 'A' + i)] = transp[1];
+ variant_params[stringf("\\CFG_TRANSP_%c", 'A' + i)] = transp[i];
if (clocks[i] != other.clocks[i])
- variant_params[stringf("\\CFG_CLOCKS_%c", 'A' + i)] = clocks[1];
+ variant_params[stringf("\\CFG_CLOCKS_%c", 'A' + i)] = clocks[i];
if (clkpol[i] != other.clkpol[i])
- variant_params[stringf("\\CFG_CLKPOL_%c", 'A' + i)] = clkpol[1];
+ variant_params[stringf("\\CFG_CLKPOL_%c", 'A' + i)] = clkpol[i];
}
}
};
struct match_t {
IdString name;
dict<string, int> min_limits, max_limits;
- bool or_next_if_better, make_transp;
+ bool or_next_if_better, make_transp, make_outreg;
char shuffle_enable;
};
data.name = RTLIL::escape_id(tokens[1]);
data.or_next_if_better = false;
data.make_transp = false;
+ data.make_outreg = false;
data.shuffle_enable = 0;
while (next_line())
continue;
}
+ if (GetSize(tokens) == 1 && tokens[0] == "make_outreg") {
+ data.make_transp = true;
+ data.make_outreg = true;
+ continue;
+ }
+
if (GetSize(tokens) == 1 && tokens[0] == "or_next_if_better") {
data.or_next_if_better = true;
continue;
void parse(string filename)
{
- if (filename.substr(0, 2) == "+/")
- filename = proc_share_dirname() + filename.substr(1);
-
+ rewrite_filename(filename);
infile.open(filename);
linecount = 0;
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);
+ clocks_max = max(clocks_max, pi.clocks);
+ clkpol_max = max(clkpol_max, pi.clkpol);
+ transp_max = max(transp_max, pi.transp);
}
log(" Mapping to bram type %s (variant %d):\n", log_id(bram.name), bram.variant);
int mem_width = cell->getParam("\\WIDTH").as_int();
// int mem_offset = cell->getParam("\\OFFSET").as_int();
+ bool cell_init = !SigSpec(cell->getParam("\\INIT")).is_fully_undef();
+ vector<Const> initdata;
+
+ if (cell_init) {
+ Const initparam = cell->getParam("\\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("\\WR_PORTS").as_int();
auto wr_clken = SigSpec(cell->getParam("\\WR_CLK_ENABLE"));
auto wr_clkpol = SigSpec(cell->getParam("\\WR_CLK_POLARITY"));
rd_clkpol.extend_u0(rd_ports);
rd_transp.extend_u0(rd_ports);
+ SigSpec rd_en = cell->getPort("\\RD_EN");
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)
+ 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)
{
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);
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);
}
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 (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);
}
}
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;
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~");
mapped_wr_port:;
}
- // houskeeping stuff for growing more read ports and restarting read port assignments
+ // housekeeping stuff for growing more read ports and restarting read port assignments
int grow_read_ports_cursor = -1;
bool try_growing_more_read_ports = false;
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) {
bool transp = rd_transp[cell_port_i] == State::S1;
SigBit clksig = rd_clk[cell_port_i];
+ if (wr_ports == 0)
+ transp = false;
+
pair<SigBit, bool> clkdom(clksig, clkpol);
if (!clken)
clkdom = pair<SigBit, bool>(State::S1, false);
if (clken) {
if (pi.clocks == 0) {
+ if (match.make_outreg) {
+ pi.make_outreg = true;
+ goto skip_bram_rport_clkcheck;
+ }
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(" 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);
+ 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) {
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 transparancy.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
+ log(" Bram port %c%d.%d has incompatible read transparency.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
goto skip_bram_rport;
}
}
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;
}
for (auto &vp : variant_params)
c->setParam(vp.first, vp.second);
+ if (cell_init) {
+ int init_offset = grid_a*(1 << bram.abits);
+ 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 j = 0; j < bram.dbits; j++)
+ if (init_offset+i < GetSize(initdata) && init_shift+j < GetSize(initdata[init_offset+i]))
+ initparam[i*bram.dbits+j] = initdata[init_offset+i][init_shift+j];
+ else
+ initparam[i*bram.dbits+j] = padding;
+ }
+ c->setParam("\\INIT", initparam);
+ }
+
for (auto &pi : portinfos)
{
if (pi.dupidx != dupidx)
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 (pi.wrmode == 1) {
+ 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);
} else {
SigSpec bram_dout = module->addWire(NEW_ID, bram.dbits);
c->setPort(stringf("\\%sDATA", pf), bram_dout);
-
- if (pi.make_transp)
- {
+ 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),
}
SigSpec addr_ok_q = addr_ok;
- if (pi.clocks && !addr_ok.empty()) {
+ 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);
}
{
log("Processing %s.%s:\n", log_id(cell->module), log_id(cell));
+ bool cell_init = !SigSpec(cell->getParam("\\INIT")).is_fully_undef();
+
dict<string, int> match_properties;
match_properties["words"] = cell->getParam("\\SIZE").as_int();
match_properties["abits"] = cell->getParam("\\ABITS").as_int();
log("\n");
pool<pair<IdString, int>> failed_brams;
- dict<pair<int, int>, std::tuple<int, int, int>> best_rule_cache;
+ dict<pair<int, int>, tuple<int, int, int>> best_rule_cache;
for (int i = 0; i < GetSize(rules.matches); i++)
{
log(" Metrics for %s: awaste=%d dwaste=%d bwaste=%d waste=%d efficiency=%d\n",
log_id(match.name), awaste, dwaste, bwaste, waste, efficiency);
+ if (cell_init && bram.init == 0) {
+ log(" Rule #%d for bram type %s (variant %d) rejected: cannot be initialized.\n",
+ i+1, log_id(bram.name), bram.variant);
+ goto next_match_rule;
+ }
+
for (auto it : match.min_limits) {
if (it.first == "waste" || it.first == "dups" || it.first == "acells" || it.first == "dcells" || it.first == "cells")
continue;
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;
}
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;
+ best_rule_cache[pair<int, int>(i, vi)] = tuple<int, int, int>(match_properties["efficiency"], -match_properties["cells"], -match_properties["acells"]);
next_match_rule:
if (or_next_if_better || best_rule_cache.empty())
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");
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(" 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(" enable 4 1 # number of enable bits\n");
+ log(" transp 0 2 # transparent (for read ports)\n");
log(" clocks 1 2 # clock configuration\n");
log(" clkpol 2 2 # clock polarity configuration\n");
log(" endbram\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("of the bram. Verilog configuration 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(" 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("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");
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 'make_outreg' will add external flip-flops\n");
+ log("to implement synchronous read ports, 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)
+ void execute(vector<string> args, Design *design) YS_OVERRIDE
{
rules_t rules;
- log_header("Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n");
+ log_header(design, "Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {