Merge pull request #6055 from qbojj/xilinx-transparent-memory

transparent memory verilog pattern
This commit is contained in:
nella 2026-08-04 15:14:57 +00:00 committed by GitHub
commit d5f1795249
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 242 additions and 191 deletions

View File

@ -602,211 +602,262 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem)
} }
} }
// Decide how to represent the transparency; same idea as Mem::extract_rdff. // Due to some synthesis tools being incompetent in detecting address collision pattern
bool trans_use_addr = true; // from verilog with strictly defined semantics and not inferring bram (see github issue #5082),
for (auto bit : port.transparency_mask) // memory cannot use:
if (!bit) // 1. e.g Vivado assumes non-transparent port
trans_use_addr = false; // assign r_data = mem[latched r_addr];
// 2. not recognised - synthesized to LUTRAM
// always @(...) begin
// if (rd_en) begin
// r_data <= mem[r_addr];
// if (wr_en && r_addr == w_addr) r_data <= w_data;
// end
// end
//
// As such, we need to implement transparency manually, outside of the always block.
if (GetSize(mem.wr_ports) == 0) // for clocked read ports make something like:
trans_use_addr = false; // reg [..] temp_id;
// always @(posedge clk)
// if (rd_en) temp_id <= array_reg[r_addr];
// assign r_data = temp_id;
std::string temp_id = next_auto_id();
lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", port.data.size() - 1, temp_id) );
if (port.en != State::S1 || port.srst != State::S0 || port.arst != State::S0 || !port.init_value.is_fully_undef()) bool has_transparency = false;
trans_use_addr = false; for (int i = 0; i < GetSize(mem.wr_ports); i++) {
if (!port.transparency_mask[i] && !port.collision_x_mask[i])
continue;
has_transparency = true;
}
if (!trans_use_addr) std::string bypass_valid_id;
{ std::string bypass_valid_reset_str;
// for clocked read ports make something like: std::string bypass_data_id;
// reg [..] temp_id; std::string bypass_data_reset_str;
// always @(posedge clk) if (has_transparency) {
// if (rd_en) temp_id <= array_reg[r_addr]; bypass_valid_id = next_auto_id();
// assign r_data = temp_id; lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", port.data.size() - 1, bypass_valid_id) );
std::string temp_id = next_auto_id();
lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", port.data.size() - 1, temp_id) );
bool has_indent = false; std::ostringstream os;
os << stringf("%s <= ", bypass_valid_id);
dump_sigspec(os, Const(State::S0, port.data.size()));
os << ";\n";
bypass_valid_reset_str = os.str();
if (port.arst != State::S0) { bypass_data_id = next_auto_id();
std::ostringstream os; lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", port.data.size() - 1, bypass_data_id) );
os << stringf("%s <= ", temp_id);
dump_sigspec(os, port.arst_value);
os << ";\n";
clk_to_arst_body[clk_domain_str].push_back(os.str());
}
if (port.srst != State::S0 && !port.ce_over_srst) { std::ostringstream os2;
std::ostringstream os; os2 << stringf("%s <= ", bypass_data_id);
os << stringf("if ("); dump_sigspec(os2, Const(State::Sx, port.data.size()));
dump_sigspec(os, port.srst); os2 << ";\n";
os << stringf(")\n"); bypass_data_reset_str = os2.str();
clk_to_lof_body[clk_domain_str].push_back(os.str()); }
std::ostringstream os2;
os2 << stringf("%s" "%s <= ", indent, temp_id);
dump_sigspec(os2, port.srst_value);
os2 << ";\n";
clk_to_lof_body[clk_domain_str].push_back(os2.str());
std::ostringstream os3;
if (port.en == State::S1) {
os3 << "else begin\n";
} else {
os3 << "else if (";
dump_sigspec(os3, port.en);
os3 << ") begin\n";
}
clk_to_lof_body[clk_domain_str].push_back(os3.str());
has_indent = true;
} else if (port.en != State::S1) {
std::ostringstream os;
os << stringf("if (");
dump_sigspec(os, port.en);
os << stringf(") begin\n");
clk_to_lof_body[clk_domain_str].push_back(os.str());
has_indent = true;
}
for (int sub = 0; sub < (1 << port.wide_log2); sub++) bool has_indent = false;
{
SigSpec addr = port.sub_addr(sub);
std::ostringstream os;
if (has_indent)
os << indent;
os << temp_id;
if (port.wide_log2)
os << stringf("[%d:%d]", (sub + 1) * mem.width - 1, sub * mem.width);
os << stringf(" <= %s[", mem_id);
dump_sigspec(os, addr);
os << stringf("];\n");
clk_to_lof_body[clk_domain_str].push_back(os.str());
}
for (int i = 0; i < GetSize(mem.wr_ports); i++) { if (port.arst != State::S0) {
auto &wport = mem.wr_ports[i]; std::ostringstream os;
if (!port.transparency_mask[i] && !port.collision_x_mask[i]) os << stringf("%s <= ", temp_id);
continue; dump_sigspec(os, port.arst_value);
int min_wide_log2 = std::min(port.wide_log2, wport.wide_log2); os << ";\n";
int max_wide_log2 = std::max(port.wide_log2, wport.wide_log2); clk_to_arst_body[clk_domain_str].push_back(os.str());
bool wide_write = wport.wide_log2 > port.wide_log2;
for (int sub = 0; sub < (1 << max_wide_log2); sub += (1 << min_wide_log2)) {
SigSpec raddr = port.addr;
SigSpec waddr = wport.addr;
if (wide_write)
waddr = wport.sub_addr(sub);
else
raddr = port.sub_addr(sub);
int pos = 0;
int ewidth = mem.width << min_wide_log2;
int wsub = wide_write ? sub : 0;
int rsub = wide_write ? 0 : sub;
while (pos < ewidth) {
int epos = pos;
while (epos < ewidth && wport.en[epos + wsub * mem.width] == wport.en[pos + wsub * mem.width])
epos++;
std::ostringstream os; if (has_transparency) {
if (has_indent) clk_to_arst_body[clk_domain_str].push_back(bypass_valid_reset_str);
os << indent; clk_to_arst_body[clk_domain_str].push_back(bypass_data_reset_str);
os << "if (";
dump_sigspec(os, wport.en[pos + wsub * mem.width]);
if (raddr != waddr) {
os << " && ";
dump_sigspec(os, raddr);
os << " == ";
dump_sigspec(os, waddr);
}
os << ")\n";
clk_to_lof_body[clk_domain_str].push_back(os.str());
std::ostringstream os2;
if (has_indent)
os2 << indent;
os2 << indent;
os2 << temp_id;
if (epos-pos != GetSize(port.data))
os2 << stringf("[%d:%d]", rsub * mem.width + epos-1, rsub * mem.width + pos);
os2 << " <= ";
if (port.transparency_mask[i])
dump_sigspec(os2, wport.data.extract(wsub * mem.width + pos, epos-pos));
else
dump_sigspec(os2, Const(State::Sx, epos - pos));
os2 << ";\n";
clk_to_lof_body[clk_domain_str].push_back(os2.str());
pos = epos;
}
}
}
if (port.srst != State::S0 && port.ce_over_srst)
{
std::ostringstream os;
if (has_indent)
os << indent;
os << stringf("if (");
dump_sigspec(os, port.srst);
os << stringf(")\n");
clk_to_lof_body[clk_domain_str].push_back(os.str());
std::ostringstream os2;
if (has_indent)
os2 << indent;
os2 << stringf("%s" "%s <= ", indent, temp_id);
dump_sigspec(os2, port.srst_value);
os2 << ";\n";
clk_to_lof_body[clk_domain_str].push_back(os2.str());
}
if (has_indent)
clk_to_lof_body[clk_domain_str].push_back("end\n");
if (!port.init_value.is_fully_undef())
{
std::ostringstream os;
dump_sigspec(os, port.init_value);
std::string line = stringf("initial %s = %s;\n", temp_id, os.str());
clk_to_lof_body[""].push_back(line);
}
{
std::ostringstream os;
dump_sigspec(os, port.data);
std::string line = stringf("assign %s = %s;\n", os.str(), temp_id);
clk_to_lof_body[""].push_back(line);
} }
} }
else
{ if (port.srst != State::S0 && !port.ce_over_srst) {
// for rd-transparent read-ports make something like: std::ostringstream os;
// reg [..] temp_id; os << stringf("if (");
// always @(posedge clk) dump_sigspec(os, port.srst);
// temp_id <= r_addr; os << stringf(") begin\n");
// assign r_data = array_reg[temp_id]; clk_to_lof_body[clk_domain_str].push_back(os.str());
std::string temp_id = next_auto_id();
lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", port.addr.size() - 1 - port.wide_log2, temp_id) ); std::ostringstream os2;
{ os2 << stringf("%s" "%s <= ", indent, temp_id);
std::ostringstream os; dump_sigspec(os2, port.srst_value);
dump_sigspec(os, port.addr.extract_end(port.wide_log2)); os2 << ";\n";
std::string line = stringf("%s <= %s;\n", temp_id, os.str()); clk_to_lof_body[clk_domain_str].push_back(os2.str());
clk_to_lof_body[clk_domain_str].push_back(line);
if (has_transparency) {
clk_to_lof_body[clk_domain_str].push_back(indent + bypass_valid_reset_str);
clk_to_lof_body[clk_domain_str].push_back(indent + bypass_data_reset_str);
} }
for (int sub = 0; sub < (1 << port.wide_log2); sub++) std::ostringstream os3;
{ if (port.en == State::S1) {
os3 << "end else begin\n";
} else {
os3 << "end else if (";
dump_sigspec(os3, port.en);
os3 << ") begin\n";
}
clk_to_lof_body[clk_domain_str].push_back(os3.str());
has_indent = true;
} else if (port.en != State::S1) {
std::ostringstream os;
os << stringf("if (");
dump_sigspec(os, port.en);
os << stringf(") begin\n");
clk_to_lof_body[clk_domain_str].push_back(os.str());
has_indent = true;
}
for (int sub = 0; sub < (1 << port.wide_log2); sub++)
{
SigSpec addr = port.sub_addr(sub);
std::ostringstream os;
if (has_indent)
os << indent;
os << temp_id;
if (port.wide_log2)
os << stringf("[%d:%d]", (sub + 1) * mem.width - 1, sub * mem.width);
os << stringf(" <= %s[", mem_id);
dump_sigspec(os, addr);
os << stringf("];\n");
clk_to_lof_body[clk_domain_str].push_back(os.str());
}
if (has_transparency) {
clk_to_lof_body[clk_domain_str].push_back((has_indent ? indent : "") + bypass_valid_reset_str);
clk_to_lof_body[clk_domain_str].push_back((has_indent ? indent : "") + bypass_data_reset_str);
}
for (int i = 0; i < GetSize(mem.wr_ports); i++) {
auto &wport = mem.wr_ports[i];
if (!port.transparency_mask[i] && !port.collision_x_mask[i])
continue;
int min_wide_log2 = std::min(port.wide_log2, wport.wide_log2);
int max_wide_log2 = std::max(port.wide_log2, wport.wide_log2);
bool wide_write = wport.wide_log2 > port.wide_log2;
for (int sub = 0; sub < (1 << max_wide_log2); sub += (1 << min_wide_log2)) {
SigSpec raddr = port.addr;
SigSpec waddr = wport.addr;
if (wide_write)
waddr = wport.sub_addr(sub);
else
raddr = port.sub_addr(sub);
int pos = 0;
int ewidth = mem.width << min_wide_log2;
int wsub = wide_write ? sub : 0;
int rsub = wide_write ? 0 : sub;
while (pos < ewidth) {
int epos = pos;
while (epos < ewidth && wport.en[epos + wsub * mem.width] == wport.en[pos + wsub * mem.width])
epos++;
std::ostringstream os;
if (has_indent)
os << indent;
os << "if (";
dump_sigspec(os, wport.en[pos + wsub * mem.width]);
if (raddr != waddr) {
os << " && ";
dump_sigspec(os, raddr);
os << " == ";
dump_sigspec(os, waddr);
}
os << ") begin\n";
clk_to_lof_body[clk_domain_str].push_back(os.str());
int sig_beg = rsub * mem.width + pos;
int sig_end = rsub * mem.width + epos;
std::ostringstream os2;
if (has_indent)
os2 << indent;
os2 << stringf("%s" "%s", indent, bypass_valid_id);
if (epos-pos != GetSize(port.data))
os2 << stringf("[%d:%d]", sig_end - 1, sig_beg);
os2 << " <= ";
dump_sigspec(os2, Const(State::S1, sig_end - sig_beg));
os2 << ";\n";
clk_to_lof_body[clk_domain_str].push_back(os2.str());
std::ostringstream os3;
if (has_indent)
os3 << indent;
os3 << stringf("%s" "%s", indent, bypass_data_id);
if (epos-pos != GetSize(port.data))
os3 << stringf("[%d:%d]", sig_end - 1, sig_beg);
os3 << " <= ";
if (port.transparency_mask[i])
dump_sigspec(os3, wport.data.extract(wsub * mem.width + pos, epos-pos));
else
dump_sigspec(os3, Const(State::Sx, epos - pos));
os3 << ";\n";
clk_to_lof_body[clk_domain_str].push_back(os3.str());
clk_to_lof_body[clk_domain_str].push_back(stringf("%s" "end\n", has_indent ? indent : ""));
pos = epos;
}
}
}
if (port.srst != State::S0 && port.ce_over_srst)
{
std::ostringstream os;
if (has_indent)
os << indent;
os << stringf("if (");
dump_sigspec(os, port.srst);
os << stringf(") begin\n");
clk_to_lof_body[clk_domain_str].push_back(os.str());
std::ostringstream os2;
if (has_indent)
os2 << indent;
os2 << stringf("%s" "%s <= ", indent, temp_id);
dump_sigspec(os2, port.srst_value);
os2 << ";\n";
clk_to_lof_body[clk_domain_str].push_back(os2.str());
if (has_transparency) {
clk_to_lof_body[clk_domain_str].push_back((has_indent ? indent : "") + indent + bypass_valid_reset_str);
clk_to_lof_body[clk_domain_str].push_back((has_indent ? indent : "") + indent + bypass_data_reset_str);
}
clk_to_lof_body[clk_domain_str].push_back((has_indent ? indent : "") + "end\n");
}
if (has_indent)
clk_to_lof_body[clk_domain_str].push_back("end\n");
if (!port.init_value.is_fully_undef())
{
std::ostringstream os;
os << stringf("initial %s = ", temp_id);
dump_sigspec(os, port.init_value);
os << ";\n";
clk_to_lof_body[""].push_back(os.str());
if (has_transparency) {
std::ostringstream os2;
os2 << stringf("initial %s = ", bypass_valid_id);
dump_sigspec(os2, Const(State::S0, port.data.size()));
os2 << ";\n";
clk_to_lof_body[""].push_back(os2.str());
}
}
{
if (!has_transparency) {
std::ostringstream os; std::ostringstream os;
os << "assign "; os << "assign ";
dump_sigspec(os, port.data.extract(sub * mem.width, mem.width)); dump_sigspec(os, port.data);
os << stringf(" = %s[", mem_id);; os << " = ";
if (port.wide_log2) { os << temp_id;
Const::Builder addr_lo_builder(port.wide_log2); os << ";\n";
for (int i = 0; i < port.wide_log2; i++) clk_to_lof_body[""].push_back(os.str());
addr_lo_builder.push_back(State(sub >> i & 1)); } else {
Const addr_lo = addr_lo_builder.build(); std::ostringstream os;
os << "{"; os << "assign ";
os << temp_id; dump_sigspec(os, port.data);
os << ", "; os << " = ";
dump_const(os, addr_lo); os << stringf("(%s & %s) | (~%s & %s)", bypass_valid_id, bypass_data_id, bypass_valid_id, temp_id);
os << "}"; os << ";\n";
} else {
os << temp_id;
}
os << "];\n";
clk_to_lof_body[""].push_back(os.str()); clk_to_lof_body[""].push_back(os.str());
} }
} }