mirror of https://github.com/YosysHQ/yosys.git
Add v2 memory cells.
This commit is contained in:
parent
b96eb888cc
commit
fd79217763
|
@ -71,6 +71,11 @@ Yosys 0.9 .. Yosys 0.9-dev
|
|||
- Added "_TECHMAP_CELLNAME_" parameter for "techmap" pass
|
||||
- Merged "dffsr2dff", "opt_rmdff", "dff2dffe", "dff2dffs", "peepopt.dffmux" passes into a new "opt_dff" pass
|
||||
- Added $meminit_v2 cells (with support for write mask)
|
||||
- Added $mem_v2, $memrd_v2, $memwr_v2, with the following features:
|
||||
- write priority masks, per write/write port pair
|
||||
- transparency and undefined collision behavior masks, per read/write port pair
|
||||
- read port reset and initialization
|
||||
- wide ports (accessing a naturally aligned power-of-two number of memory cells)
|
||||
|
||||
Yosys 0.8 .. Yosys 0.9
|
||||
----------------------
|
||||
|
|
|
@ -155,10 +155,13 @@ struct CellTypes
|
|||
setup_internals_ff();
|
||||
|
||||
setup_type(ID($memrd), {ID::CLK, ID::EN, ID::ADDR}, {ID::DATA});
|
||||
setup_type(ID($memrd_v2), {ID::CLK, ID::EN, ID::ARST, ID::SRST, ID::ADDR}, {ID::DATA});
|
||||
setup_type(ID($memwr), {ID::CLK, ID::EN, ID::ADDR, ID::DATA}, pool<RTLIL::IdString>());
|
||||
setup_type(ID($memwr_v2), {ID::CLK, ID::EN, ID::ADDR, ID::DATA}, pool<RTLIL::IdString>());
|
||||
setup_type(ID($meminit), {ID::ADDR, ID::DATA}, pool<RTLIL::IdString>());
|
||||
setup_type(ID($meminit_v2), {ID::ADDR, ID::DATA, ID::EN}, pool<RTLIL::IdString>());
|
||||
setup_type(ID($mem), {ID::RD_CLK, ID::RD_EN, ID::RD_ADDR, ID::WR_CLK, ID::WR_EN, ID::WR_ADDR, ID::WR_DATA}, {ID::RD_DATA});
|
||||
setup_type(ID($mem_v2), {ID::RD_CLK, ID::RD_EN, ID::RD_ARST, ID::RD_SRST, ID::RD_ADDR, ID::WR_CLK, ID::WR_EN, ID::WR_ADDR, ID::WR_DATA}, {ID::RD_DATA});
|
||||
|
||||
setup_type(ID($fsm), {ID::CLK, ID::ARST, ID::CTRL_IN}, {ID::CTRL_OUT});
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ X(bugpoint_keep)
|
|||
X(B_WIDTH)
|
||||
X(C)
|
||||
X(cells_not_processed)
|
||||
X(CE_OVER_SRST)
|
||||
X(CFG_ABITS)
|
||||
X(CFG_DBITS)
|
||||
X(CFG_INIT)
|
||||
|
@ -46,6 +47,7 @@ X(CLK_POLARITY)
|
|||
X(CLR)
|
||||
X(CLR_POLARITY)
|
||||
X(CO)
|
||||
X(COLLISION_X_MASK)
|
||||
X(CONFIG)
|
||||
X(CONFIG_WIDTH)
|
||||
X(CTRL_IN)
|
||||
|
@ -95,6 +97,7 @@ X(hdlname)
|
|||
X(hierconn)
|
||||
X(I)
|
||||
X(INIT)
|
||||
X(INIT_VALUE)
|
||||
X(init)
|
||||
X(initial_top)
|
||||
X(interface_modport)
|
||||
|
@ -133,18 +136,29 @@ X(onehot)
|
|||
X(P)
|
||||
X(parallel_case)
|
||||
X(parameter)
|
||||
X(PORTID)
|
||||
X(PRIORITY)
|
||||
X(PRIORITY_MASK)
|
||||
X(Q)
|
||||
X(qwp_position)
|
||||
X(R)
|
||||
X(RD_ADDR)
|
||||
X(RD_ARST)
|
||||
X(RD_ARST_VALUE)
|
||||
X(RD_CE_OVER_SRST)
|
||||
X(RD_CLK)
|
||||
X(RD_CLK_ENABLE)
|
||||
X(RD_CLK_POLARITY)
|
||||
X(RD_COLLISION_X_MASK)
|
||||
X(RD_DATA)
|
||||
X(RD_EN)
|
||||
X(RD_INIT_VALUE)
|
||||
X(RD_PORTS)
|
||||
X(RD_SRST)
|
||||
X(RD_SRST_VALUE)
|
||||
X(RD_TRANSPARENCY_MASK)
|
||||
X(RD_TRANSPARENT)
|
||||
X(RD_WIDE_CONTINUATION)
|
||||
X(reg)
|
||||
X(S)
|
||||
X(SET)
|
||||
|
@ -195,6 +209,7 @@ X(T_LIMIT_TYP)
|
|||
X(to_delete)
|
||||
X(top)
|
||||
X(TRANS_NUM)
|
||||
X(TRANSPARENCY_MASK)
|
||||
X(TRANSPARENT)
|
||||
X(TRANS_TABLE)
|
||||
X(T_RISE_MAX)
|
||||
|
@ -220,6 +235,8 @@ X(WR_CLK_POLARITY)
|
|||
X(WR_DATA)
|
||||
X(WR_EN)
|
||||
X(WR_PORTS)
|
||||
X(WR_PRIORITY_MASK)
|
||||
X(WR_WIDE_CONTINUATION)
|
||||
X(X)
|
||||
X(Y)
|
||||
X(Y_WIDTH)
|
||||
|
|
359
kernel/mem.cc
359
kernel/mem.cc
|
@ -123,42 +123,31 @@ void Mem::emit() {
|
|||
if (!cell) {
|
||||
if (memid.empty())
|
||||
memid = NEW_ID;
|
||||
cell = module->addCell(memid, ID($mem));
|
||||
cell = module->addCell(memid, ID($mem_v2));
|
||||
}
|
||||
cell->type = ID($mem_v2);
|
||||
cell->attributes = attributes;
|
||||
cell->parameters[ID::MEMID] = Const(memid.str());
|
||||
cell->parameters[ID::WIDTH] = Const(width);
|
||||
cell->parameters[ID::OFFSET] = Const(start_offset);
|
||||
cell->parameters[ID::SIZE] = Const(size);
|
||||
Const rd_wide_continuation, rd_clk_enable, rd_clk_polarity, rd_transparent;
|
||||
Const wr_wide_continuation, wr_clk_enable, wr_clk_polarity;
|
||||
Const rd_wide_continuation, rd_clk_enable, rd_clk_polarity, rd_transparency_mask, rd_collision_x_mask;
|
||||
Const wr_wide_continuation, wr_clk_enable, wr_clk_polarity, wr_priority_mask;
|
||||
Const rd_ce_over_srst, rd_arst_value, rd_srst_value, rd_init_value;
|
||||
SigSpec rd_clk, rd_en, rd_addr, rd_data;
|
||||
SigSpec wr_clk, wr_en, wr_addr, wr_data;
|
||||
SigSpec rd_arst, rd_srst;
|
||||
int abits = 0;
|
||||
for (auto &port : rd_ports)
|
||||
abits = std::max(abits, GetSize(port.addr));
|
||||
for (auto &port : wr_ports)
|
||||
abits = std::max(abits, GetSize(port.addr));
|
||||
cell->parameters[ID::ABITS] = Const(abits);
|
||||
std::vector<int> wr_port_xlat;
|
||||
for (int i = 0; i < GetSize(wr_ports); i++)
|
||||
for (int j = 0; j < (1 << wr_ports[i].wide_log2); j++)
|
||||
wr_port_xlat.push_back(i);
|
||||
for (auto &port : rd_ports) {
|
||||
// TODO: remove
|
||||
log_assert(port.arst == State::S0);
|
||||
log_assert(port.srst == State::S0);
|
||||
log_assert(port.init_value == Const(State::Sx, width << port.wide_log2));
|
||||
bool transparent = false;
|
||||
bool non_transparent = false;
|
||||
if (port.clk_enable) {
|
||||
for (int i = 0; i < GetSize(wr_ports); i++) {
|
||||
auto &oport = wr_ports[i];
|
||||
if (oport.clk_enable && oport.clk == port.clk && oport.clk_polarity == port.clk_polarity) {
|
||||
if (port.transparency_mask[i])
|
||||
transparent = true;
|
||||
else if (!port.collision_x_mask[i])
|
||||
non_transparent = true;
|
||||
}
|
||||
}
|
||||
log_assert(!transparent || !non_transparent);
|
||||
}
|
||||
if (port.cell) {
|
||||
module->remove(port.cell);
|
||||
port.cell = nullptr;
|
||||
|
@ -168,28 +157,55 @@ void Mem::emit() {
|
|||
rd_wide_continuation.bits.push_back(State(sub != 0));
|
||||
rd_clk_enable.bits.push_back(State(port.clk_enable));
|
||||
rd_clk_polarity.bits.push_back(State(port.clk_polarity));
|
||||
rd_transparent.bits.push_back(State(transparent));
|
||||
rd_ce_over_srst.bits.push_back(State(port.ce_over_srst));
|
||||
rd_clk.append(port.clk);
|
||||
rd_arst.append(port.arst);
|
||||
rd_srst.append(port.srst);
|
||||
rd_en.append(port.en);
|
||||
SigSpec addr = port.sub_addr(sub);
|
||||
addr.extend_u0(abits, false);
|
||||
rd_addr.append(addr);
|
||||
log_assert(GetSize(addr) == abits);
|
||||
for (auto idx : wr_port_xlat) {
|
||||
rd_transparency_mask.bits.push_back(State(bool(port.transparency_mask[idx])));
|
||||
rd_collision_x_mask.bits.push_back(State(bool(port.collision_x_mask[idx])));
|
||||
}
|
||||
}
|
||||
rd_data.append(port.data);
|
||||
for (auto &bit : port.arst_value)
|
||||
rd_arst_value.bits.push_back(bit);
|
||||
for (auto &bit : port.srst_value)
|
||||
rd_srst_value.bits.push_back(bit);
|
||||
for (auto &bit : port.init_value)
|
||||
rd_init_value.bits.push_back(bit);
|
||||
}
|
||||
if (rd_ports.empty()) {
|
||||
rd_wide_continuation = State::S0;
|
||||
rd_clk_enable = State::S0;
|
||||
rd_clk_polarity = State::S0;
|
||||
rd_transparent = State::S0;
|
||||
rd_ce_over_srst = State::S0;
|
||||
rd_arst_value = State::S0;
|
||||
rd_srst_value = State::S0;
|
||||
rd_init_value = State::S0;
|
||||
}
|
||||
if (rd_ports.empty() || wr_ports.empty()) {
|
||||
rd_transparency_mask = State::S0;
|
||||
rd_collision_x_mask = State::S0;
|
||||
}
|
||||
cell->parameters[ID::RD_PORTS] = Const(GetSize(rd_clk));
|
||||
cell->parameters[ID::RD_CLK_ENABLE] = rd_clk_enable;
|
||||
cell->parameters[ID::RD_CLK_POLARITY] = rd_clk_polarity;
|
||||
cell->parameters[ID::RD_TRANSPARENT] = rd_transparent;
|
||||
cell->parameters[ID::RD_TRANSPARENCY_MASK] = rd_transparency_mask;
|
||||
cell->parameters[ID::RD_COLLISION_X_MASK] = rd_collision_x_mask;
|
||||
cell->parameters[ID::RD_WIDE_CONTINUATION] = rd_wide_continuation;
|
||||
cell->parameters[ID::RD_CE_OVER_SRST] = rd_ce_over_srst;
|
||||
cell->parameters[ID::RD_ARST_VALUE] = rd_arst_value;
|
||||
cell->parameters[ID::RD_SRST_VALUE] = rd_srst_value;
|
||||
cell->parameters[ID::RD_INIT_VALUE] = rd_init_value;
|
||||
cell->setPort(ID::RD_CLK, rd_clk);
|
||||
cell->setPort(ID::RD_EN, rd_en);
|
||||
cell->setPort(ID::RD_ARST, rd_arst);
|
||||
cell->setPort(ID::RD_SRST, rd_srst);
|
||||
cell->setPort(ID::RD_ADDR, rd_addr);
|
||||
cell->setPort(ID::RD_DATA, rd_data);
|
||||
for (auto &port : wr_ports) {
|
||||
|
@ -203,6 +219,8 @@ void Mem::emit() {
|
|||
wr_clk_enable.bits.push_back(State(port.clk_enable));
|
||||
wr_clk_polarity.bits.push_back(State(port.clk_polarity));
|
||||
wr_clk.append(port.clk);
|
||||
for (auto idx : wr_port_xlat)
|
||||
wr_priority_mask.bits.push_back(State(bool(port.priority_mask[idx])));
|
||||
SigSpec addr = port.sub_addr(sub);
|
||||
addr.extend_u0(abits, false);
|
||||
wr_addr.append(addr);
|
||||
|
@ -215,10 +233,13 @@ void Mem::emit() {
|
|||
wr_wide_continuation = State::S0;
|
||||
wr_clk_enable = State::S0;
|
||||
wr_clk_polarity = State::S0;
|
||||
wr_priority_mask = State::S0;
|
||||
}
|
||||
cell->parameters[ID::WR_PORTS] = Const(GetSize(wr_clk));
|
||||
cell->parameters[ID::WR_CLK_ENABLE] = wr_clk_enable;
|
||||
cell->parameters[ID::WR_CLK_POLARITY] = wr_clk_polarity;
|
||||
cell->parameters[ID::WR_PRIORITY_MASK] = wr_priority_mask;
|
||||
cell->parameters[ID::WR_WIDE_CONTINUATION] = wr_wide_continuation;
|
||||
cell->setPort(ID::WR_CLK, wr_clk);
|
||||
cell->setPort(ID::WR_EN, wr_en);
|
||||
cell->setPort(ID::WR_ADDR, wr_addr);
|
||||
|
@ -247,49 +268,44 @@ void Mem::emit() {
|
|||
mem->size = size;
|
||||
mem->attributes = attributes;
|
||||
for (auto &port : rd_ports) {
|
||||
// TODO: remove
|
||||
log_assert(port.arst == State::S0);
|
||||
log_assert(port.srst == State::S0);
|
||||
log_assert(port.init_value == Const(State::Sx, width << port.wide_log2));
|
||||
bool transparent = false;
|
||||
bool non_transparent = false;
|
||||
if (port.clk_enable) {
|
||||
for (int i = 0; i < GetSize(wr_ports); i++) {
|
||||
auto &oport = wr_ports[i];
|
||||
if (oport.clk_enable && oport.clk == port.clk && oport.clk_polarity == port.clk_polarity) {
|
||||
if (port.transparency_mask[i])
|
||||
transparent = true;
|
||||
else if (!port.collision_x_mask[i])
|
||||
non_transparent = true;
|
||||
}
|
||||
}
|
||||
log_assert(!transparent || !non_transparent);
|
||||
}
|
||||
if (!port.cell)
|
||||
port.cell = module->addCell(NEW_ID, ID($memrd));
|
||||
port.cell = module->addCell(NEW_ID, ID($memrd_v2));
|
||||
port.cell->type = ID($memrd_v2);
|
||||
port.cell->attributes = port.attributes;
|
||||
port.cell->parameters[ID::MEMID] = memid.str();
|
||||
port.cell->parameters[ID::ABITS] = GetSize(port.addr);
|
||||
port.cell->parameters[ID::WIDTH] = width << port.wide_log2;
|
||||
port.cell->parameters[ID::CLK_ENABLE] = port.clk_enable;
|
||||
port.cell->parameters[ID::CLK_POLARITY] = port.clk_polarity;
|
||||
port.cell->parameters[ID::TRANSPARENT] = transparent;
|
||||
port.cell->parameters[ID::CE_OVER_SRST] = port.ce_over_srst;
|
||||
port.cell->parameters[ID::ARST_VALUE] = port.arst_value;
|
||||
port.cell->parameters[ID::SRST_VALUE] = port.srst_value;
|
||||
port.cell->parameters[ID::INIT_VALUE] = port.init_value;
|
||||
port.cell->parameters[ID::TRANSPARENCY_MASK] = port.transparency_mask;
|
||||
port.cell->parameters[ID::COLLISION_X_MASK] = port.collision_x_mask;
|
||||
port.cell->parameters.erase(ID::TRANSPARENT);
|
||||
port.cell->setPort(ID::CLK, port.clk);
|
||||
port.cell->setPort(ID::EN, port.en);
|
||||
port.cell->setPort(ID::ARST, port.arst);
|
||||
port.cell->setPort(ID::SRST, port.srst);
|
||||
port.cell->setPort(ID::ADDR, port.addr);
|
||||
port.cell->setPort(ID::DATA, port.data);
|
||||
}
|
||||
int idx = 0;
|
||||
for (auto &port : wr_ports) {
|
||||
if (!port.cell)
|
||||
port.cell = module->addCell(NEW_ID, ID($memwr));
|
||||
port.cell = module->addCell(NEW_ID, ID($memwr_v2));
|
||||
port.cell->type = ID($memwr_v2);
|
||||
port.cell->attributes = port.attributes;
|
||||
if (port.cell->parameters.count(ID::PRIORITY))
|
||||
port.cell->parameters.erase(ID::PRIORITY);
|
||||
port.cell->parameters[ID::MEMID] = memid.str();
|
||||
port.cell->parameters[ID::ABITS] = GetSize(port.addr);
|
||||
port.cell->parameters[ID::WIDTH] = width << port.wide_log2;
|
||||
port.cell->parameters[ID::CLK_ENABLE] = port.clk_enable;
|
||||
port.cell->parameters[ID::CLK_POLARITY] = port.clk_polarity;
|
||||
port.cell->parameters[ID::PRIORITY] = idx++;
|
||||
port.cell->parameters[ID::PORTID] = idx++;
|
||||
port.cell->parameters[ID::PRIORITY_MASK] = port.priority_mask;
|
||||
port.cell->setPort(ID::CLK, port.clk);
|
||||
port.cell->setPort(ID::EN, port.en);
|
||||
port.cell->setPort(ID::ADDR, port.addr);
|
||||
|
@ -497,9 +513,9 @@ namespace {
|
|||
dict<IdString, pool<Cell *>> inits;
|
||||
MemIndex (Module *module) {
|
||||
for (auto cell: module->cells()) {
|
||||
if (cell->type == ID($memwr))
|
||||
if (cell->type.in(ID($memwr), ID($memwr_v2)))
|
||||
wr_ports[cell->parameters.at(ID::MEMID).decode_string()].insert(cell);
|
||||
else if (cell->type == ID($memrd))
|
||||
else if (cell->type.in(ID($memrd), ID($memrd_v2)))
|
||||
rd_ports[cell->parameters.at(ID::MEMID).decode_string()].insert(cell);
|
||||
else if (cell->type.in(ID($meminit), ID($meminit_v2)))
|
||||
inits[cell->parameters.at(ID::MEMID).decode_string()].insert(cell);
|
||||
|
@ -513,33 +529,45 @@ namespace {
|
|||
res.mem = mem;
|
||||
res.attributes = mem->attributes;
|
||||
std::vector<bool> rd_transparent;
|
||||
std::vector<int> wr_portid;
|
||||
if (index.rd_ports.count(mem->name)) {
|
||||
for (auto cell : index.rd_ports.at(mem->name)) {
|
||||
MemRd mrd;
|
||||
bool is_compat = cell->type == ID($memrd);
|
||||
mrd.cell = cell;
|
||||
mrd.attributes = cell->attributes;
|
||||
mrd.clk_enable = cell->parameters.at(ID::CLK_ENABLE).as_bool();
|
||||
mrd.clk_polarity = cell->parameters.at(ID::CLK_POLARITY).as_bool();
|
||||
bool transparent = cell->parameters.at(ID::TRANSPARENT).as_bool();
|
||||
mrd.clk = cell->getPort(ID::CLK);
|
||||
mrd.en = cell->getPort(ID::EN);
|
||||
mrd.addr = cell->getPort(ID::ADDR);
|
||||
mrd.data = cell->getPort(ID::DATA);
|
||||
mrd.wide_log2 = ceil_log2(GetSize(mrd.data) / mem->width);
|
||||
mrd.ce_over_srst = false;
|
||||
mrd.arst_value = Const(State::Sx, mem->width << mrd.wide_log2);
|
||||
mrd.srst_value = Const(State::Sx, mem->width << mrd.wide_log2);
|
||||
mrd.init_value = Const(State::Sx, mem->width << mrd.wide_log2);
|
||||
mrd.srst = State::S0;
|
||||
mrd.arst = State::S0;
|
||||
if (!mrd.clk_enable) {
|
||||
// Fix some patterns that we'll allow for backwards compatibility,
|
||||
// but don't want to see moving forwards: async transparent
|
||||
// ports (inherently meaningless) and async ports without
|
||||
// const 1 tied to EN bit (which may mean a latch in the future).
|
||||
transparent = false;
|
||||
if (mrd.en == State::Sx)
|
||||
mrd.en = State::S1;
|
||||
bool transparent = false;
|
||||
if (is_compat) {
|
||||
transparent = cell->parameters.at(ID::TRANSPARENT).as_bool();
|
||||
mrd.ce_over_srst = false;
|
||||
mrd.arst_value = Const(State::Sx, mem->width << mrd.wide_log2);
|
||||
mrd.srst_value = Const(State::Sx, mem->width << mrd.wide_log2);
|
||||
mrd.init_value = Const(State::Sx, mem->width << mrd.wide_log2);
|
||||
mrd.srst = State::S0;
|
||||
mrd.arst = State::S0;
|
||||
if (!mrd.clk_enable) {
|
||||
// Fix some patterns that we'll allow for backwards compatibility,
|
||||
// but don't want to see moving forwards: async transparent
|
||||
// ports (inherently meaningless) and async ports without
|
||||
// const 1 tied to EN bit (which may mean a latch in the future).
|
||||
transparent = false;
|
||||
if (mrd.en == State::Sx)
|
||||
mrd.en = State::S1;
|
||||
}
|
||||
} else {
|
||||
mrd.ce_over_srst = cell->parameters.at(ID::CE_OVER_SRST).as_bool();
|
||||
mrd.arst_value = cell->parameters.at(ID::ARST_VALUE);
|
||||
mrd.srst_value = cell->parameters.at(ID::SRST_VALUE);
|
||||
mrd.init_value = cell->parameters.at(ID::INIT_VALUE);
|
||||
mrd.arst = cell->getPort(ID::ARST);
|
||||
mrd.srst = cell->getPort(ID::SRST);
|
||||
}
|
||||
res.rd_ports.push_back(mrd);
|
||||
rd_transparent.push_back(transparent);
|
||||
|
@ -549,6 +577,7 @@ namespace {
|
|||
std::vector<std::pair<int, MemWr>> ports;
|
||||
for (auto cell : index.wr_ports.at(mem->name)) {
|
||||
MemWr mwr;
|
||||
bool is_compat = cell->type == ID($memwr);
|
||||
mwr.cell = cell;
|
||||
mwr.attributes = cell->attributes;
|
||||
mwr.clk_enable = cell->parameters.at(ID::CLK_ENABLE).as_bool();
|
||||
|
@ -558,11 +587,36 @@ namespace {
|
|||
mwr.addr = cell->getPort(ID::ADDR);
|
||||
mwr.data = cell->getPort(ID::DATA);
|
||||
mwr.wide_log2 = ceil_log2(GetSize(mwr.data) / mem->width);
|
||||
ports.push_back(std::make_pair(cell->parameters.at(ID::PRIORITY).as_int(), mwr));
|
||||
ports.push_back(std::make_pair(cell->parameters.at(is_compat ? ID::PRIORITY : ID::PORTID).as_int(), mwr));
|
||||
}
|
||||
std::sort(ports.begin(), ports.end(), [](const std::pair<int, MemWr> &a, const std::pair<int, MemWr> &b) { return a.first < b.first; });
|
||||
for (auto &it : ports)
|
||||
for (auto &it : ports) {
|
||||
res.wr_ports.push_back(it.second);
|
||||
wr_portid.push_back(it.first);
|
||||
}
|
||||
for (int i = 0; i < GetSize(res.wr_ports); i++) {
|
||||
auto &port = res.wr_ports[i];
|
||||
bool is_compat = port.cell->type == ID($memwr);
|
||||
if (is_compat) {
|
||||
port.priority_mask.resize(GetSize(res.wr_ports));
|
||||
for (int j = 0; j < i; j++) {
|
||||
auto &oport = res.wr_ports[j];
|
||||
if (port.clk_enable != oport.clk_enable)
|
||||
continue;
|
||||
if (port.clk_enable && port.clk != oport.clk)
|
||||
continue;
|
||||
if (port.clk_enable && port.clk_polarity != oport.clk_polarity)
|
||||
continue;
|
||||
port.priority_mask[j] = true;
|
||||
}
|
||||
} else {
|
||||
Const orig_prio_mask = port.cell->parameters.at(ID::PRIORITY_MASK);
|
||||
for (int orig_portid : wr_portid) {
|
||||
bool has_prio = orig_portid < GetSize(orig_prio_mask) && orig_prio_mask[orig_portid] == State::S1;
|
||||
port.priority_mask.push_back(has_prio);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (index.inits.count(mem->name)) {
|
||||
std::vector<std::pair<int, MemInit>> inits;
|
||||
|
@ -592,37 +646,33 @@ namespace {
|
|||
for (auto &it : inits)
|
||||
res.inits.push_back(it.second);
|
||||
}
|
||||
for (int i = 0; i < GetSize(res.wr_ports); i++) {
|
||||
auto &port = res.wr_ports[i];
|
||||
port.priority_mask.resize(GetSize(res.wr_ports));
|
||||
for (int j = 0; j < i; j++) {
|
||||
auto &oport = res.wr_ports[j];
|
||||
if (port.clk_enable != oport.clk_enable)
|
||||
continue;
|
||||
if (port.clk_enable && port.clk != oport.clk)
|
||||
continue;
|
||||
if (port.clk_enable && port.clk_polarity != oport.clk_polarity)
|
||||
continue;
|
||||
port.priority_mask[j] = true;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < GetSize(res.rd_ports); i++) {
|
||||
auto &port = res.rd_ports[i];
|
||||
port.transparency_mask.resize(GetSize(res.wr_ports));
|
||||
port.collision_x_mask.resize(GetSize(res.wr_ports));
|
||||
if (!rd_transparent[i])
|
||||
continue;
|
||||
if (!port.clk_enable)
|
||||
continue;
|
||||
for (int j = 0; j < GetSize(res.wr_ports); j++) {
|
||||
auto &wport = res.wr_ports[j];
|
||||
if (!wport.clk_enable)
|
||||
bool is_compat = port.cell->type == ID($memrd);
|
||||
if (is_compat) {
|
||||
port.transparency_mask.resize(GetSize(res.wr_ports));
|
||||
port.collision_x_mask.resize(GetSize(res.wr_ports));
|
||||
if (!rd_transparent[i])
|
||||
continue;
|
||||
if (port.clk != wport.clk)
|
||||
if (!port.clk_enable)
|
||||
continue;
|
||||
if (port.clk_polarity != wport.clk_polarity)
|
||||
continue;
|
||||
port.transparency_mask[j] = true;
|
||||
for (int j = 0; j < GetSize(res.wr_ports); j++) {
|
||||
auto &wport = res.wr_ports[j];
|
||||
if (!wport.clk_enable)
|
||||
continue;
|
||||
if (port.clk != wport.clk)
|
||||
continue;
|
||||
if (port.clk_polarity != wport.clk_polarity)
|
||||
continue;
|
||||
port.transparency_mask[j] = true;
|
||||
}
|
||||
} else {
|
||||
Const orig_trans_mask = port.cell->parameters.at(ID::TRANSPARENCY_MASK);
|
||||
Const orig_cx_mask = port.cell->parameters.at(ID::COLLISION_X_MASK);
|
||||
for (int orig_portid : wr_portid) {
|
||||
port.transparency_mask.push_back(orig_portid < GetSize(orig_trans_mask) && orig_trans_mask[orig_portid] == State::S1);
|
||||
port.collision_x_mask.push_back(orig_portid < GetSize(orig_cx_mask) && orig_cx_mask[orig_portid] == State::S1);
|
||||
}
|
||||
}
|
||||
}
|
||||
res.check();
|
||||
|
@ -635,6 +685,7 @@ namespace {
|
|||
cell->parameters.at(ID::OFFSET).as_int(),
|
||||
cell->parameters.at(ID::SIZE).as_int()
|
||||
);
|
||||
bool is_compat = cell->type == ID($mem);
|
||||
int abits = cell->parameters.at(ID::ABITS).as_int();
|
||||
res.packed = true;
|
||||
res.cell = cell;
|
||||
|
@ -662,65 +713,103 @@ namespace {
|
|||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < cell->parameters.at(ID::RD_PORTS).as_int(); i++) {
|
||||
int n_rd_ports = cell->parameters.at(ID::RD_PORTS).as_int();
|
||||
int n_wr_ports = cell->parameters.at(ID::WR_PORTS).as_int();
|
||||
Const rd_wide_continuation = is_compat ? Const(State::S0, n_rd_ports) : cell->parameters.at(ID::RD_WIDE_CONTINUATION);
|
||||
Const wr_wide_continuation = is_compat ? Const(State::S0, n_wr_ports) : cell->parameters.at(ID::WR_WIDE_CONTINUATION);
|
||||
for (int i = 0, ni; i < n_rd_ports; i = ni) {
|
||||
ni = i + 1;
|
||||
while (ni < n_rd_ports && rd_wide_continuation[ni] == State::S1)
|
||||
ni++;
|
||||
MemRd mrd;
|
||||
mrd.wide_log2 = 0;
|
||||
mrd.wide_log2 = ceil_log2(ni - i);
|
||||
log_assert(ni - i == (1 << mrd.wide_log2));
|
||||
mrd.clk_enable = cell->parameters.at(ID::RD_CLK_ENABLE).extract(i, 1).as_bool();
|
||||
mrd.clk_polarity = cell->parameters.at(ID::RD_CLK_POLARITY).extract(i, 1).as_bool();
|
||||
mrd.clk = cell->getPort(ID::RD_CLK).extract(i, 1);
|
||||
mrd.en = cell->getPort(ID::RD_EN).extract(i, 1);
|
||||
mrd.addr = cell->getPort(ID::RD_ADDR).extract(i * abits, abits);
|
||||
mrd.data = cell->getPort(ID::RD_DATA).extract(i * res.width, res.width);
|
||||
mrd.ce_over_srst = false;
|
||||
mrd.arst_value = Const(State::Sx, res.width << mrd.wide_log2);
|
||||
mrd.srst_value = Const(State::Sx, res.width << mrd.wide_log2);
|
||||
mrd.init_value = Const(State::Sx, res.width << mrd.wide_log2);
|
||||
mrd.srst = State::S0;
|
||||
mrd.arst = State::S0;
|
||||
mrd.data = cell->getPort(ID::RD_DATA).extract(i * res.width, (ni - i) * res.width);
|
||||
if (is_compat) {
|
||||
mrd.ce_over_srst = false;
|
||||
mrd.arst_value = Const(State::Sx, res.width << mrd.wide_log2);
|
||||
mrd.srst_value = Const(State::Sx, res.width << mrd.wide_log2);
|
||||
mrd.init_value = Const(State::Sx, res.width << mrd.wide_log2);
|
||||
mrd.arst = State::S0;
|
||||
mrd.srst = State::S0;
|
||||
} else {
|
||||
mrd.ce_over_srst = cell->parameters.at(ID::RD_CE_OVER_SRST).extract(i, 1).as_bool();
|
||||
mrd.arst_value = cell->parameters.at(ID::RD_ARST_VALUE).extract(i * res.width, (ni - i) * res.width);
|
||||
mrd.srst_value = cell->parameters.at(ID::RD_SRST_VALUE).extract(i * res.width, (ni - i) * res.width);
|
||||
mrd.init_value = cell->parameters.at(ID::RD_INIT_VALUE).extract(i * res.width, (ni - i) * res.width);
|
||||
mrd.arst = cell->getPort(ID::RD_ARST).extract(i, 1);
|
||||
mrd.srst = cell->getPort(ID::RD_SRST).extract(i, 1);
|
||||
}
|
||||
if (!is_compat) {
|
||||
Const transparency_mask = cell->parameters.at(ID::RD_TRANSPARENCY_MASK).extract(i * n_wr_ports, n_wr_ports);
|
||||
Const collision_x_mask = cell->parameters.at(ID::RD_COLLISION_X_MASK).extract(i * n_wr_ports, n_wr_ports);
|
||||
for (int j = 0; j < n_wr_ports; j++)
|
||||
if (wr_wide_continuation[j] != State::S1) {
|
||||
mrd.transparency_mask.push_back(transparency_mask[j] == State::S1);
|
||||
mrd.collision_x_mask.push_back(collision_x_mask[j] == State::S1);
|
||||
}
|
||||
}
|
||||
res.rd_ports.push_back(mrd);
|
||||
}
|
||||
for (int i = 0; i < cell->parameters.at(ID::WR_PORTS).as_int(); i++) {
|
||||
for (int i = 0, ni; i < n_wr_ports; i = ni) {
|
||||
ni = i + 1;
|
||||
while (ni < n_wr_ports && wr_wide_continuation[ni] == State::S1)
|
||||
ni++;
|
||||
MemWr mwr;
|
||||
mwr.wide_log2 = 0;
|
||||
mwr.wide_log2 = ceil_log2(ni - i);
|
||||
log_assert(ni - i == (1 << mwr.wide_log2));
|
||||
mwr.clk_enable = cell->parameters.at(ID::WR_CLK_ENABLE).extract(i, 1).as_bool();
|
||||
mwr.clk_polarity = cell->parameters.at(ID::WR_CLK_POLARITY).extract(i, 1).as_bool();
|
||||
mwr.clk = cell->getPort(ID::WR_CLK).extract(i, 1);
|
||||
mwr.en = cell->getPort(ID::WR_EN).extract(i * res.width, res.width);
|
||||
mwr.en = cell->getPort(ID::WR_EN).extract(i * res.width, (ni - i) * res.width);
|
||||
mwr.addr = cell->getPort(ID::WR_ADDR).extract(i * abits, abits);
|
||||
mwr.data = cell->getPort(ID::WR_DATA).extract(i * res.width, res.width);
|
||||
mwr.data = cell->getPort(ID::WR_DATA).extract(i * res.width, (ni - i) * res.width);
|
||||
if (!is_compat) {
|
||||
Const priority_mask = cell->parameters.at(ID::WR_PRIORITY_MASK).extract(i * n_wr_ports, n_wr_ports);
|
||||
for (int j = 0; j < n_wr_ports; j++)
|
||||
if (wr_wide_continuation[j] != State::S1)
|
||||
mwr.priority_mask.push_back(priority_mask[j] == State::S1);
|
||||
}
|
||||
res.wr_ports.push_back(mwr);
|
||||
}
|
||||
for (int i = 0; i < GetSize(res.wr_ports); i++) {
|
||||
auto &port = res.wr_ports[i];
|
||||
port.priority_mask.resize(GetSize(res.wr_ports));
|
||||
for (int j = 0; j < i; j++) {
|
||||
auto &oport = res.wr_ports[j];
|
||||
if (port.clk_enable != oport.clk_enable)
|
||||
continue;
|
||||
if (port.clk_enable && port.clk != oport.clk)
|
||||
continue;
|
||||
if (port.clk_enable && port.clk_polarity != oport.clk_polarity)
|
||||
continue;
|
||||
port.priority_mask[j] = true;
|
||||
if (is_compat) {
|
||||
for (int i = 0; i < GetSize(res.wr_ports); i++) {
|
||||
auto &port = res.wr_ports[i];
|
||||
port.priority_mask.resize(GetSize(res.wr_ports));
|
||||
for (int j = 0; j < i; j++) {
|
||||
auto &oport = res.wr_ports[j];
|
||||
if (port.clk_enable != oport.clk_enable)
|
||||
continue;
|
||||
if (port.clk_enable && port.clk != oport.clk)
|
||||
continue;
|
||||
if (port.clk_enable && port.clk_polarity != oport.clk_polarity)
|
||||
continue;
|
||||
port.priority_mask[j] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < GetSize(res.rd_ports); i++) {
|
||||
auto &port = res.rd_ports[i];
|
||||
port.transparency_mask.resize(GetSize(res.wr_ports));
|
||||
port.collision_x_mask.resize(GetSize(res.wr_ports));
|
||||
if (!cell->parameters.at(ID::RD_TRANSPARENT).extract(i, 1).as_bool())
|
||||
continue;
|
||||
if (!port.clk_enable)
|
||||
continue;
|
||||
for (int j = 0; j < GetSize(res.wr_ports); j++) {
|
||||
auto &wport = res.wr_ports[j];
|
||||
if (!wport.clk_enable)
|
||||
for (int i = 0; i < GetSize(res.rd_ports); i++) {
|
||||
auto &port = res.rd_ports[i];
|
||||
port.transparency_mask.resize(GetSize(res.wr_ports));
|
||||
port.collision_x_mask.resize(GetSize(res.wr_ports));
|
||||
if (!cell->parameters.at(ID::RD_TRANSPARENT).extract(i, 1).as_bool())
|
||||
continue;
|
||||
if (port.clk != wport.clk)
|
||||
if (!port.clk_enable)
|
||||
continue;
|
||||
if (port.clk_polarity != wport.clk_polarity)
|
||||
continue;
|
||||
port.transparency_mask[j] = true;
|
||||
for (int j = 0; j < GetSize(res.wr_ports); j++) {
|
||||
auto &wport = res.wr_ports[j];
|
||||
if (!wport.clk_enable)
|
||||
continue;
|
||||
if (port.clk != wport.clk)
|
||||
continue;
|
||||
if (port.clk_polarity != wport.clk_polarity)
|
||||
continue;
|
||||
port.transparency_mask[j] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
res.check();
|
||||
|
@ -736,7 +825,7 @@ std::vector<Mem> Mem::get_all_memories(Module *module) {
|
|||
res.push_back(mem_from_memory(module, it.second, index));
|
||||
}
|
||||
for (auto cell: module->cells()) {
|
||||
if (cell->type == ID($mem))
|
||||
if (cell->type.in(ID($mem), ID($mem_v2)))
|
||||
res.push_back(mem_from_cell(cell));
|
||||
}
|
||||
return res;
|
||||
|
@ -750,7 +839,7 @@ std::vector<Mem> Mem::get_selected_memories(Module *module) {
|
|||
res.push_back(mem_from_memory(module, it.second, index));
|
||||
}
|
||||
for (auto cell: module->selected_cells()) {
|
||||
if (cell->type == ID($mem))
|
||||
if (cell->type.in(ID($mem), ID($mem_v2)))
|
||||
res.push_back(mem_from_cell(cell));
|
||||
}
|
||||
return res;
|
||||
|
|
|
@ -1392,6 +1392,26 @@ namespace {
|
|||
return;
|
||||
}
|
||||
|
||||
if (cell->type == ID($memrd_v2)) {
|
||||
param(ID::MEMID);
|
||||
param_bool(ID::CLK_ENABLE);
|
||||
param_bool(ID::CLK_POLARITY);
|
||||
param(ID::TRANSPARENCY_MASK);
|
||||
param(ID::COLLISION_X_MASK);
|
||||
param_bool(ID::CE_OVER_SRST);
|
||||
param_bits(ID::ARST_VALUE, param(ID::WIDTH));
|
||||
param_bits(ID::SRST_VALUE, param(ID::WIDTH));
|
||||
param_bits(ID::INIT_VALUE, param(ID::WIDTH));
|
||||
port(ID::CLK, 1);
|
||||
port(ID::EN, 1);
|
||||
port(ID::ARST, 1);
|
||||
port(ID::SRST, 1);
|
||||
port(ID::ADDR, param(ID::ABITS));
|
||||
port(ID::DATA, param(ID::WIDTH));
|
||||
check_expected();
|
||||
return;
|
||||
}
|
||||
|
||||
if (cell->type == ID($memwr)) {
|
||||
param(ID::MEMID);
|
||||
param_bool(ID::CLK_ENABLE);
|
||||
|
@ -1405,6 +1425,20 @@ namespace {
|
|||
return;
|
||||
}
|
||||
|
||||
if (cell->type == ID($memwr_v2)) {
|
||||
param(ID::MEMID);
|
||||
param_bool(ID::CLK_ENABLE);
|
||||
param_bool(ID::CLK_POLARITY);
|
||||
param(ID::PORTID);
|
||||
param(ID::PRIORITY_MASK);
|
||||
port(ID::CLK, 1);
|
||||
port(ID::EN, param(ID::WIDTH));
|
||||
port(ID::ADDR, param(ID::ABITS));
|
||||
port(ID::DATA, param(ID::WIDTH));
|
||||
check_expected();
|
||||
return;
|
||||
}
|
||||
|
||||
if (cell->type == ID($meminit)) {
|
||||
param(ID::MEMID);
|
||||
param(ID::PRIORITY);
|
||||
|
@ -1446,6 +1480,38 @@ namespace {
|
|||
return;
|
||||
}
|
||||
|
||||
if (cell->type == ID($mem_v2)) {
|
||||
param(ID::MEMID);
|
||||
param(ID::SIZE);
|
||||
param(ID::OFFSET);
|
||||
param(ID::INIT);
|
||||
param_bits(ID::RD_CLK_ENABLE, max(1, param(ID::RD_PORTS)));
|
||||
param_bits(ID::RD_CLK_POLARITY, max(1, param(ID::RD_PORTS)));
|
||||
param_bits(ID::RD_TRANSPARENCY_MASK, max(1, param(ID::RD_PORTS) * param(ID::WR_PORTS)));
|
||||
param_bits(ID::RD_COLLISION_X_MASK, max(1, param(ID::RD_PORTS) * param(ID::WR_PORTS)));
|
||||
param_bits(ID::RD_WIDE_CONTINUATION, max(1, param(ID::RD_PORTS)));
|
||||
param_bits(ID::RD_CE_OVER_SRST, max(1, param(ID::RD_PORTS)));
|
||||
param_bits(ID::RD_ARST_VALUE, param(ID::RD_PORTS) * param(ID::WIDTH));
|
||||
param_bits(ID::RD_SRST_VALUE, param(ID::RD_PORTS) * param(ID::WIDTH));
|
||||
param_bits(ID::RD_INIT_VALUE, param(ID::RD_PORTS) * param(ID::WIDTH));
|
||||
param_bits(ID::WR_CLK_ENABLE, max(1, param(ID::WR_PORTS)));
|
||||
param_bits(ID::WR_CLK_POLARITY, max(1, param(ID::WR_PORTS)));
|
||||
param_bits(ID::WR_WIDE_CONTINUATION, max(1, param(ID::WR_PORTS)));
|
||||
param_bits(ID::WR_PRIORITY_MASK, max(1, param(ID::WR_PORTS) * param(ID::WR_PORTS)));
|
||||
port(ID::RD_CLK, param(ID::RD_PORTS));
|
||||
port(ID::RD_EN, param(ID::RD_PORTS));
|
||||
port(ID::RD_ARST, param(ID::RD_PORTS));
|
||||
port(ID::RD_SRST, param(ID::RD_PORTS));
|
||||
port(ID::RD_ADDR, param(ID::RD_PORTS) * param(ID::ABITS));
|
||||
port(ID::RD_DATA, param(ID::RD_PORTS) * param(ID::WIDTH));
|
||||
port(ID::WR_CLK, param(ID::WR_PORTS));
|
||||
port(ID::WR_EN, param(ID::WR_PORTS) * param(ID::WIDTH));
|
||||
port(ID::WR_ADDR, param(ID::WR_PORTS) * param(ID::ABITS));
|
||||
port(ID::WR_DATA, param(ID::WR_PORTS) * param(ID::WIDTH));
|
||||
check_expected();
|
||||
return;
|
||||
}
|
||||
|
||||
if (cell->type == ID($tribuf)) {
|
||||
port(ID::A, param(ID::WIDTH));
|
||||
port(ID::Y, param(ID::WIDTH));
|
||||
|
@ -3187,12 +3253,12 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed)
|
|||
|
||||
bool RTLIL::Cell::has_memid() const
|
||||
{
|
||||
return type.in(ID($memwr), ID($memrd), ID($meminit), ID($meminit_v2));
|
||||
return type.in(ID($memwr), ID($memwr_v2), ID($memrd), ID($memrd_v2), ID($meminit), ID($meminit_v2));
|
||||
}
|
||||
|
||||
bool RTLIL::Cell::is_mem_cell() const
|
||||
{
|
||||
return type == ID($mem) || has_memid();
|
||||
return type.in(ID($mem), ID($mem_v2)) || has_memid();
|
||||
}
|
||||
|
||||
RTLIL::SigChunk::SigChunk()
|
||||
|
|
|
@ -338,19 +338,19 @@ In addition to {\tt \$dlatch} ports and parameters, they also have multi-bit
|
|||
\subsection{Memories}
|
||||
\label{sec:memcells}
|
||||
|
||||
Memories are either represented using RTLIL::Memory objects, {\tt \$memrd}, {\tt \$memwr}, and {\tt \$meminit\_v2}
|
||||
cells, or by {\tt \$mem} cells alone.
|
||||
Memories are either represented using RTLIL::Memory objects, {\tt \$memrd\_v2}, {\tt \$memwr\_v2}, and {\tt \$meminit\_v2}
|
||||
cells, or by {\tt \$mem\_v2} cells alone.
|
||||
|
||||
In the first alternative the RTLIL::Memory objects hold the general metadata for the memory (bit width,
|
||||
size in number of words, etc.) and for each port a {\tt \$memrd} (read port) or {\tt \$memwr} (write port)
|
||||
size in number of words, etc.) and for each port a {\tt \$memrd\_v2} (read port) or {\tt \$memwr\_v2} (write port)
|
||||
cell is created. Having individual cells for read and write ports has the advantage that they can be
|
||||
consolidated using resource sharing passes. In some cases this drastically reduces the number of required
|
||||
ports on the memory cell. In this alternative, memory initialization data is represented by {\tt \$meminit\_v2} cells,
|
||||
which allow delaying constant folding for initialization addresses and data until after the frontend finishes.
|
||||
|
||||
The {\tt \$memrd} cells have a clock input \B{CLK}, an enable input \B{EN}, an
|
||||
address input \B{ADDR}, and a data output \B{DATA}. They also have the
|
||||
following parameters:
|
||||
The {\tt \$memrd\_v2} cells have a clock input \B{CLK}, an enable input \B{EN}, an
|
||||
address input \B{ADDR}, a data output \B{DATA}, an asynchronous reset input \B{ARST},
|
||||
and a synchronous reset input \B{SRST}. They also have the following parameters:
|
||||
|
||||
\begin{itemize}
|
||||
\item \B{MEMID} \\
|
||||
|
@ -360,7 +360,9 @@ The name of the RTLIL::Memory object that is associated with this read port.
|
|||
The number of address bits (width of the \B{ADDR} input port).
|
||||
|
||||
\item \B{WIDTH} \\
|
||||
The number of data bits (width of the \B{DATA} output port).
|
||||
The number of data bits (width of the \B{DATA} output port). Note that this may be a power-of-two
|
||||
multiple of the underlying memory's width -- such ports are called wide ports and access an aligned
|
||||
group of cells at once. In this case, the corresponding low bits of \B{ADDR} must be tied to 0.
|
||||
|
||||
\item \B{CLK\_ENABLE} \\
|
||||
When this parameter is non-zero, the clock is used. Otherwise this read port is asynchronous and
|
||||
|
@ -370,12 +372,37 @@ the \B{CLK} input is not used.
|
|||
Clock is active on the positive edge if this parameter has the value {\tt 1'b1} and on the negative
|
||||
edge if this parameter is {\tt 1'b0}.
|
||||
|
||||
\item \B{TRANSPARENT} \\
|
||||
If this parameter is set to {\tt 1'b1}, a read and write to the same address in the same cycle will
|
||||
return the new value. Otherwise the old value is returned.
|
||||
\item \B{TRANSPARENCY\_MASK} \\
|
||||
This parameter is a bitmask of write ports that this read port is transparent with. The bits
|
||||
of this parameter are indexed by the write port's \B{PORTID} parameter. Transparency can only be
|
||||
enabled between synchronous ports sharing a clock domain. When transparency is enabled for a given
|
||||
port pair, a read and write to the same address in the same cycle will return the new value.
|
||||
Otherwise the old value is returned.
|
||||
|
||||
\item \B{COLLISION\_X\_MASK} \\
|
||||
This parameter is a bitmask of write ports that have undefined collision behavior with this port.
|
||||
The bits of this parameter are indexed by the write port's \B{PORTID} parameter. This behavior can only be
|
||||
enabled between synchronous ports sharing a clock domain. When undefined collision is enabled for a given
|
||||
port pair, a read and write to the same address in the same cycle will return the undefined (all-X) value.
|
||||
This option is exclusive (for a given port pair) with the transparency option.
|
||||
|
||||
\item \B{ARST\_VALUE} \\
|
||||
Whenever the \B{ARST} input is asserted, the data output will be reset to this value.
|
||||
Only used for synchronous ports.
|
||||
|
||||
\item \B{SRST\_VALUE} \\
|
||||
Whenever the \B{SRST} input is synchronously asserted, the data output will be reset to this value.
|
||||
Only used for synchronous ports.
|
||||
|
||||
\item \B{INIT\_VALUE} \\
|
||||
The initial value of the data output, for synchronous ports.
|
||||
|
||||
\item \B{CE\_OVER\_SRST} \\
|
||||
If this parameter is non-zero, the \B{SRST} input is only recognized when \B{EN} is true.
|
||||
Otherwise, \B{SRST} is recognized regardless of \B{EN}.
|
||||
\end{itemize}
|
||||
|
||||
The {\tt \$memwr} cells have a clock input \B{CLK}, an enable input \B{EN} (one
|
||||
The {\tt \$memwr\_v2} cells have a clock input \B{CLK}, an enable input \B{EN} (one
|
||||
enable bit for each data bit), an address input \B{ADDR} and a data input
|
||||
\B{DATA}. They also have the following parameters:
|
||||
|
||||
|
@ -387,7 +414,9 @@ The name of the RTLIL::Memory object that is associated with this write port.
|
|||
The number of address bits (width of the \B{ADDR} input port).
|
||||
|
||||
\item \B{WIDTH} \\
|
||||
The number of data bits (width of the \B{DATA} output port).
|
||||
The number of data bits (width of the \B{DATA} output port). Like with {\tt \$memrd\_v2} cells,
|
||||
the width is allowed to be any power-of-two multiple of memory width, with the corresponding
|
||||
restriction on address.
|
||||
|
||||
\item \B{CLK\_ENABLE} \\
|
||||
When this parameter is non-zero, the clock is used. Otherwise this write port is asynchronous and
|
||||
|
@ -397,8 +426,15 @@ the \B{CLK} input is not used.
|
|||
Clock is active on positive edge if this parameter has the value {\tt 1'b1} and on the negative
|
||||
edge if this parameter is {\tt 1'b0}.
|
||||
|
||||
\item \B{PRIORITY} \\
|
||||
The cell with the higher integer value in this parameter wins a write conflict.
|
||||
\item \B{PORTID} \\
|
||||
An identifier for this write port, used to index write port bit mask parameters.
|
||||
|
||||
\item \B{PRIORITY\_MASK} \\
|
||||
This parameter is a bitmask of write ports that this write port has priority over in case of writing
|
||||
to the same address. The bits of this parameter are indexed by the other write port's \B{PORTID} parameter.
|
||||
Write ports can only have priority over write ports with lower port ID. When two ports write to the same
|
||||
address and neither has priority over the other, the result is undefined. Priority can only be set between
|
||||
two synchronous ports sharing the same clock domain.
|
||||
\end{itemize}
|
||||
|
||||
The {\tt \$meminit\_v2} cells have an address input \B{ADDR}, a data input \B{DATA}, with the width
|
||||
|
@ -424,17 +460,17 @@ The cell with the higher integer value in this parameter wins an initialization
|
|||
\end{itemize}
|
||||
|
||||
The HDL frontend models a memory using RTLIL::Memory objects and asynchronous
|
||||
{\tt \$memrd} and {\tt \$memwr} cells. The {\tt memory} pass (i.e.~its various sub-passes) migrates
|
||||
{\tt \$dff} cells into the {\tt \$memrd} and {\tt \$memwr} cells making them synchronous, then
|
||||
converts them to a single {\tt \$mem} cell and (optionally) maps this cell type
|
||||
{\tt \$memrd\_v2} and {\tt \$memwr\_v2} cells. The {\tt memory} pass (i.e.~its various sub-passes) migrates
|
||||
{\tt \$dff} cells into the {\tt \$memrd\_v2} and {\tt \$memwr\_v2} cells making them synchronous, then
|
||||
converts them to a single {\tt \$mem\_v2} cell and (optionally) maps this cell type
|
||||
to {\tt \$dff} cells for the individual words and multiplexer-based address decoders for the read and
|
||||
write interfaces. When the last step is disabled or not possible, a {\tt \$mem} cell is left in the design.
|
||||
write interfaces. When the last step is disabled or not possible, a {\tt \$mem\_v2} cell is left in the design.
|
||||
|
||||
The {\tt \$mem} cell provides the following parameters:
|
||||
The {\tt \$mem\_v2} cell provides the following parameters:
|
||||
|
||||
\begin{itemize}
|
||||
\item \B{MEMID} \\
|
||||
The name of the original RTLIL::Memory object that became this {\tt \$mem} cell.
|
||||
The name of the original RTLIL::Memory object that became this {\tt \$mem\_v2} cell.
|
||||
|
||||
\item \B{SIZE} \\
|
||||
The number of words in the memory.
|
||||
|
@ -451,26 +487,56 @@ The initial memory contents.
|
|||
\item \B{RD\_PORTS} \\
|
||||
The number of read ports on this memory cell.
|
||||
|
||||
\item \B{RD\_WIDE\_CONTINUATION} \\
|
||||
This parameter is \B{RD\_PORTS} bits wide, containing a bitmask of ``wide continuation'' read ports.
|
||||
Such ports are used to represent the extra data bits of wide ports in the combined cell, and must
|
||||
have all control signals identical with the preceding port, except for address, which must have
|
||||
the proper sub-cell address encoded in the low bits.
|
||||
|
||||
\item \B{RD\_CLK\_ENABLE} \\
|
||||
This parameter is \B{RD\_PORTS} bits wide, containing a clock enable bit for each read port.
|
||||
|
||||
\item \B{RD\_CLK\_POLARITY} \\
|
||||
This parameter is \B{RD\_PORTS} bits wide, containing a clock polarity bit for each read port.
|
||||
|
||||
\item \B{RD\_TRANSPARENT} \\
|
||||
This parameter is \B{RD\_PORTS} bits wide, containing a transparent bit for each read port.
|
||||
\item \B{RD\_TRANSPARENCY\_MASK} \\
|
||||
This parameter is \B{RD\_PORTS*WR\_PORTS} bits wide, containing a concatenation of all
|
||||
\B{TRANSPARENCY\_MASK} values of the original {\tt \$memrd\_v2} cells.
|
||||
|
||||
\item \B{RD\_COLLISION\_X\_MASK} \\
|
||||
This parameter is \B{RD\_PORTS*WR\_PORTS} bits wide, containing a concatenation of all
|
||||
\B{COLLISION\_X\_MASK} values of the original {\tt \$memrd\_v2} cells.
|
||||
|
||||
\item \B{RD\_CE\_OVER\_SRST} \\
|
||||
This parameter is \B{RD\_PORTS} bits wide, determining relative synchronous reset and enable priority for each read port.
|
||||
|
||||
\item \B{RD\_INIT\_VALUE} \\
|
||||
This parameter is \B{RD\_PORTS*WIDTH} bits wide, containing the initial value for each synchronous read port.
|
||||
|
||||
\item \B{RD\_ARST\_VALUE} \\
|
||||
This parameter is \B{RD\_PORTS*WIDTH} bits wide, containing the asynchronous reset value for each synchronous read port.
|
||||
|
||||
\item \B{RD\_SRST\_VALUE} \\
|
||||
This parameter is \B{RD\_PORTS*WIDTH} bits wide, containing the synchronous reset value for each synchronous read port.
|
||||
|
||||
\item \B{WR\_PORTS} \\
|
||||
The number of write ports on this memory cell.
|
||||
|
||||
\item \B{WR\_WIDE\_CONTINUATION} \\
|
||||
This parameter is \B{WR\_PORTS} bits wide, containing a bitmask of ``wide continuation'' write ports.
|
||||
|
||||
\item \B{WR\_CLK\_ENABLE} \\
|
||||
This parameter is \B{WR\_PORTS} bits wide, containing a clock enable bit for each write port.
|
||||
|
||||
\item \B{WR\_CLK\_POLARITY} \\
|
||||
This parameter is \B{WR\_PORTS} bits wide, containing a clock polarity bit for each write port.
|
||||
|
||||
\item \B{WR\_PRIORITY\_MASK} \\
|
||||
This parameter is \B{WR\_PORTS*WR\_PORTS} bits wide, containing a concatenation of all
|
||||
\B{PRIORITY\_MASK} values of the original {\tt \$memwr\_v2} cells.
|
||||
\end{itemize}
|
||||
|
||||
The {\tt \$mem} cell has the following ports:
|
||||
The {\tt \$mem\_v2} cell has the following ports:
|
||||
|
||||
\begin{itemize}
|
||||
\item \B{RD\_CLK} \\
|
||||
|
@ -485,6 +551,12 @@ This input is \B{RD\_PORTS}*\B{ABITS} bits wide, containing all address signals
|
|||
\item \B{RD\_DATA} \\
|
||||
This input is \B{RD\_PORTS}*\B{WIDTH} bits wide, containing all data signals for the read ports.
|
||||
|
||||
\item \B{RD\_ARST} \\
|
||||
This input is \B{RD\_PORTS} bits wide, containing all asynchronous reset signals for the read ports.
|
||||
|
||||
\item \B{RD\_SRST} \\
|
||||
This input is \B{RD\_PORTS} bits wide, containing all synchronous reset signals for the read ports.
|
||||
|
||||
\item \B{WR\_CLK} \\
|
||||
This input is \B{WR\_PORTS} bits wide, containing all clock signals for the write ports.
|
||||
|
||||
|
@ -498,11 +570,11 @@ This input is \B{WR\_PORTS}*\B{ABITS} bits wide, containing all address signals
|
|||
This input is \B{WR\_PORTS}*\B{WIDTH} bits wide, containing all data signals for the write ports.
|
||||
\end{itemize}
|
||||
|
||||
The {\tt memory\_collect} pass can be used to convert discrete {\tt \$memrd}, {\tt \$memwr}, and {\tt \$meminit\_v2} cells
|
||||
belonging to the same memory to a single {\tt \$mem} cell, whereas the {\tt memory\_unpack} pass performs the inverse operation.
|
||||
The {\tt memory\_collect} pass can be used to convert discrete {\tt \$memrd\_v2}, {\tt \$memwr\_v2}, and {\tt \$meminit\_v2} cells
|
||||
belonging to the same memory to a single {\tt \$mem\_v2} cell, whereas the {\tt memory\_unpack} pass performs the inverse operation.
|
||||
The {\tt memory\_dff} pass can combine asynchronous memory ports that are fed by or feeding registers into synchronous memory ports.
|
||||
The {\tt memory\_bram} pass can be used to recognize {\tt \$mem} cells that can be implemented with a block RAM resource on an FPGA.
|
||||
The {\tt memory\_map} pass can be used to implement {\tt \$mem} cells as basic logic: word-wide DFFs and address decoders.
|
||||
The {\tt memory\_bram} pass can be used to recognize {\tt \$mem\_v2} cells that can be implemented with a block RAM resource on an FPGA.
|
||||
The {\tt memory\_map} pass can be used to implement {\tt \$mem\_v2} cells as basic logic: word-wide DFFs and address decoders.
|
||||
|
||||
\subsection{Finite State Machines}
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ struct TorderPass : public Pass {
|
|||
if (!noautostop && yosys_celltypes.cell_known(cell->type)) {
|
||||
if (conn.first.in(ID::Q, ID::CTRL_OUT, ID::RD_DATA))
|
||||
continue;
|
||||
if (cell->type == ID($memrd) && conn.first == ID::DATA)
|
||||
if (cell->type.in(ID($memrd), ID($memrd_v2)) && conn.first == ID::DATA)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ void rmunused_module_cells(Module *module, bool verbose)
|
|||
}
|
||||
|
||||
for (Cell *cell : module->cells()) {
|
||||
if (cell->type.in(ID($memwr), ID($meminit), ID($meminit_v2))) {
|
||||
if (cell->type.in(ID($memwr), ID($memwr_v2), ID($meminit), ID($meminit_v2))) {
|
||||
IdString mem_id = cell->getParam(ID::MEMID).decode_string();
|
||||
mem2cells[mem_id].insert(cell);
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ void rmunused_module_cells(Module *module, bool verbose)
|
|||
for (auto bit : sigmap(it.second))
|
||||
bits.insert(bit);
|
||||
|
||||
if (cell->type == ID($memrd)) {
|
||||
if (cell->type.in(ID($memrd), ID($memrd_v2))) {
|
||||
IdString mem_id = cell->getParam(ID::MEMID).decode_string();
|
||||
if (mem_unused.count(mem_id)) {
|
||||
mem_unused.erase(mem_id);
|
||||
|
|
|
@ -441,7 +441,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
|
||||
if (!noclkinv)
|
||||
{
|
||||
if (cell->type.in(ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($sdff), ID($sdffe), ID($sdffce), ID($fsm), ID($memrd), ID($memwr)))
|
||||
if (cell->type.in(ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($sdff), ID($sdffe), ID($sdffce), ID($fsm), ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2)))
|
||||
handle_polarity_inv(cell, ID::CLK, ID::CLK_POLARITY, assign_map, invert_map);
|
||||
|
||||
if (cell->type.in(ID($sr), ID($dffsr), ID($dffsre), ID($dlatchsr))) {
|
||||
|
|
|
@ -254,9 +254,9 @@ struct OptReduceWorker
|
|||
SigPool mem_wren_sigs;
|
||||
for (auto &cell_it : module->cells_) {
|
||||
RTLIL::Cell *cell = cell_it.second;
|
||||
if (cell->type == ID($mem))
|
||||
if (cell->type.in(ID($mem), ID($mem_v2)))
|
||||
mem_wren_sigs.add(assign_map(cell->getPort(ID::WR_EN)));
|
||||
if (cell->type == ID($memwr))
|
||||
if (cell->type.in(ID($memwr), ID($memwr_v2)))
|
||||
mem_wren_sigs.add(assign_map(cell->getPort(ID::EN)));
|
||||
}
|
||||
for (auto &cell_it : module->cells_) {
|
||||
|
|
|
@ -366,7 +366,7 @@ struct ShareWorker
|
|||
continue;
|
||||
}
|
||||
|
||||
if (cell->type == ID($memrd)) {
|
||||
if (cell->type.in(ID($memrd), ID($memrd_v2))) {
|
||||
if (cell->parameters.at(ID::CLK_ENABLE).as_bool())
|
||||
continue;
|
||||
if (config.opt_aggressive || !modwalker.sigmap(cell->getPort(ID::ADDR)).is_fully_const())
|
||||
|
@ -399,11 +399,14 @@ struct ShareWorker
|
|||
if (c1->type != c2->type)
|
||||
return false;
|
||||
|
||||
if (c1->type == ID($memrd))
|
||||
if (c1->type.in(ID($memrd), ID($memrd_v2)))
|
||||
{
|
||||
if (c1->parameters.at(ID::MEMID).decode_string() != c2->parameters.at(ID::MEMID).decode_string())
|
||||
return false;
|
||||
|
||||
if (c1->parameters.at(ID::WIDTH) != c2->parameters.at(ID::WIDTH))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -703,7 +706,7 @@ struct ShareWorker
|
|||
return supercell;
|
||||
}
|
||||
|
||||
if (c1->type == ID($memrd))
|
||||
if (c1->type.in(ID($memrd), ID($memrd_v2)))
|
||||
{
|
||||
RTLIL::Cell *supercell = module->addCell(NEW_ID, c1);
|
||||
RTLIL::SigSpec addr1 = c1->getPort(ID::ADDR);
|
||||
|
|
|
@ -558,7 +558,7 @@ struct WreducePass : public Pass {
|
|||
}
|
||||
}
|
||||
|
||||
if (!opt_memx && c->type.in(ID($memrd), ID($memwr), ID($meminit), ID($meminit_v2))) {
|
||||
if (!opt_memx && c->type.in(ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2), ID($meminit), ID($meminit_v2))) {
|
||||
IdString memid = c->getParam(ID::MEMID).decode_string();
|
||||
RTLIL::Memory *mem = module->memories.at(memid);
|
||||
if (mem->start_offset >= 0) {
|
||||
|
|
|
@ -74,6 +74,7 @@ public:
|
|||
param_int(ID::CTRL_IN_WIDTH)
|
||||
param_int(ID::CTRL_OUT_WIDTH)
|
||||
param_int(ID::OFFSET)
|
||||
param_int(ID::PORTID)
|
||||
param_int(ID::PRIORITY)
|
||||
param_int(ID::RD_PORTS)
|
||||
param_int(ID::SIZE)
|
||||
|
|
|
@ -2182,6 +2182,34 @@ end
|
|||
|
||||
endmodule
|
||||
|
||||
module \$memrd_v2 (CLK, EN, ARST, SRST, ADDR, DATA);
|
||||
|
||||
parameter MEMID = "";
|
||||
parameter ABITS = 8;
|
||||
parameter WIDTH = 8;
|
||||
|
||||
parameter CLK_ENABLE = 0;
|
||||
parameter CLK_POLARITY = 0;
|
||||
parameter TRANSPARENCY_MASK = 0;
|
||||
parameter COLLISION_X_MASK = 0;
|
||||
parameter ARST_VALUE = 0;
|
||||
parameter SRST_VALUE = 0;
|
||||
parameter INIT_VALUE = 0;
|
||||
parameter CE_OVER_SRST = 0;
|
||||
|
||||
input CLK, EN, ARST, SRST;
|
||||
input [ABITS-1:0] ADDR;
|
||||
output [WIDTH-1:0] DATA;
|
||||
|
||||
initial begin
|
||||
if (MEMID != "") begin
|
||||
$display("ERROR: Found non-simulatable instance of $memrd_v2!");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
||||
module \$memwr (CLK, EN, ADDR, DATA);
|
||||
|
@ -2208,6 +2236,31 @@ end
|
|||
|
||||
endmodule
|
||||
|
||||
module \$memwr_v2 (CLK, EN, ADDR, DATA);
|
||||
|
||||
parameter MEMID = "";
|
||||
parameter ABITS = 8;
|
||||
parameter WIDTH = 8;
|
||||
|
||||
parameter CLK_ENABLE = 0;
|
||||
parameter CLK_POLARITY = 0;
|
||||
parameter PORTID = 0;
|
||||
parameter PRIORITY_MASK = 0;
|
||||
|
||||
input CLK;
|
||||
input [WIDTH-1:0] EN;
|
||||
input [ABITS-1:0] ADDR;
|
||||
input [WIDTH-1:0] DATA;
|
||||
|
||||
initial begin
|
||||
if (MEMID != "") begin
|
||||
$display("ERROR: Found non-simulatable instance of $memwr_v2!");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
||||
module \$meminit (ADDR, DATA);
|
||||
|
@ -2344,6 +2397,122 @@ end
|
|||
|
||||
endmodule
|
||||
|
||||
module \$mem_v2 (RD_CLK, RD_EN, RD_ARST, RD_SRST, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA);
|
||||
|
||||
parameter MEMID = "";
|
||||
parameter signed SIZE = 4;
|
||||
parameter signed OFFSET = 0;
|
||||
parameter signed ABITS = 2;
|
||||
parameter signed WIDTH = 8;
|
||||
parameter signed INIT = 1'bx;
|
||||
|
||||
parameter signed RD_PORTS = 1;
|
||||
parameter RD_CLK_ENABLE = 1'b1;
|
||||
parameter RD_CLK_POLARITY = 1'b1;
|
||||
parameter RD_TRANSPARENCY_MASK = 1'b0;
|
||||
parameter RD_COLLISION_X_MASK = 1'b0;
|
||||
parameter RD_WIDE_CONTINUATION = 1'b0;
|
||||
parameter RD_CE_OVER_SRST = 1'b0;
|
||||
parameter RD_ARST_VALUE = 1'b0;
|
||||
parameter RD_SRST_VALUE = 1'b0;
|
||||
parameter RD_INIT_VALUE = 1'b0;
|
||||
|
||||
parameter signed WR_PORTS = 1;
|
||||
parameter WR_CLK_ENABLE = 1'b1;
|
||||
parameter WR_CLK_POLARITY = 1'b1;
|
||||
parameter WR_PRIORITY_MASK = 1'b0;
|
||||
parameter WR_WIDE_CONTINUATION = 1'b0;
|
||||
|
||||
input [RD_PORTS-1:0] RD_CLK;
|
||||
input [RD_PORTS-1:0] RD_EN;
|
||||
input [RD_PORTS-1:0] RD_ARST;
|
||||
input [RD_PORTS-1:0] RD_SRST;
|
||||
input [RD_PORTS*ABITS-1:0] RD_ADDR;
|
||||
output reg [RD_PORTS*WIDTH-1:0] RD_DATA;
|
||||
|
||||
input [WR_PORTS-1:0] WR_CLK;
|
||||
input [WR_PORTS*WIDTH-1:0] WR_EN;
|
||||
input [WR_PORTS*ABITS-1:0] WR_ADDR;
|
||||
input [WR_PORTS*WIDTH-1:0] WR_DATA;
|
||||
|
||||
reg [WIDTH-1:0] memory [SIZE-1:0];
|
||||
|
||||
integer i, j, k;
|
||||
reg [WR_PORTS-1:0] LAST_WR_CLK;
|
||||
reg [RD_PORTS-1:0] LAST_RD_CLK;
|
||||
|
||||
function port_active;
|
||||
input clk_enable;
|
||||
input clk_polarity;
|
||||
input last_clk;
|
||||
input this_clk;
|
||||
begin
|
||||
casez ({clk_enable, clk_polarity, last_clk, this_clk})
|
||||
4'b0???: port_active = 1;
|
||||
4'b1101: port_active = 1;
|
||||
4'b1010: port_active = 1;
|
||||
default: port_active = 0;
|
||||
endcase
|
||||
end
|
||||
endfunction
|
||||
|
||||
initial begin
|
||||
for (i = 0; i < SIZE; i = i+1)
|
||||
memory[i] = INIT >>> (i*WIDTH);
|
||||
RD_DATA = RD_INIT_VALUE;
|
||||
end
|
||||
|
||||
always @(RD_CLK, RD_ARST, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA) begin
|
||||
`ifdef SIMLIB_MEMDELAY
|
||||
#`SIMLIB_MEMDELAY;
|
||||
`endif
|
||||
for (i = 0; i < RD_PORTS; i = i+1) begin
|
||||
if (RD_CLK_ENABLE[i] && RD_EN[i] && port_active(RD_CLK_ENABLE[i], RD_CLK_POLARITY[i], LAST_RD_CLK[i], RD_CLK[i])) begin
|
||||
// $display("Read from %s: addr=%b data=%b", MEMID, RD_ADDR[i*ABITS +: ABITS], memory[RD_ADDR[i*ABITS +: ABITS] - OFFSET]);
|
||||
RD_DATA[i*WIDTH +: WIDTH] <= memory[RD_ADDR[i*ABITS +: ABITS] - OFFSET];
|
||||
|
||||
for (j = 0; j < WR_PORTS; j = j+1) begin
|
||||
if (RD_TRANSPARENCY_MASK[i*WR_PORTS + j] && port_active(WR_CLK_ENABLE[j], WR_CLK_POLARITY[j], LAST_WR_CLK[j], WR_CLK[j]) && RD_ADDR[i*ABITS +: ABITS] == WR_ADDR[j*ABITS +: ABITS])
|
||||
for (k = 0; k < WIDTH; k = k+1)
|
||||
if (WR_EN[j*WIDTH+k])
|
||||
RD_DATA[i*WIDTH+k] <= WR_DATA[j*WIDTH+k];
|
||||
if (RD_COLLISION_X_MASK[i*WR_PORTS + j] && port_active(WR_CLK_ENABLE[j], WR_CLK_POLARITY[j], LAST_WR_CLK[j], WR_CLK[j]) && RD_ADDR[i*ABITS +: ABITS] == WR_ADDR[j*ABITS +: ABITS])
|
||||
for (k = 0; k < WIDTH; k = k+1)
|
||||
if (WR_EN[j*WIDTH+k])
|
||||
RD_DATA[i*WIDTH+k] <= 1'bx;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for (i = 0; i < WR_PORTS; i = i+1) begin
|
||||
if (port_active(WR_CLK_ENABLE[i], WR_CLK_POLARITY[i], LAST_WR_CLK[i], WR_CLK[i]))
|
||||
for (j = 0; j < WIDTH; j = j+1)
|
||||
if (WR_EN[i*WIDTH+j]) begin
|
||||
// $display("Write to %s: addr=%b data=%b", MEMID, WR_ADDR[i*ABITS +: ABITS], WR_DATA[i*WIDTH+j]);
|
||||
memory[WR_ADDR[i*ABITS +: ABITS] - OFFSET][j] = WR_DATA[i*WIDTH+j];
|
||||
end
|
||||
end
|
||||
|
||||
for (i = 0; i < RD_PORTS; i = i+1) begin
|
||||
if (!RD_CLK_ENABLE[i]) begin
|
||||
// $display("Combinatorial read from %s: addr=%b data=%b", MEMID, RD_ADDR[i*ABITS +: ABITS], memory[RD_ADDR[i*ABITS +: ABITS] - OFFSET]);
|
||||
RD_DATA[i*WIDTH +: WIDTH] <= memory[RD_ADDR[i*ABITS +: ABITS] - OFFSET];
|
||||
end
|
||||
end
|
||||
|
||||
for (i = 0; i < RD_PORTS; i = i+1) begin
|
||||
if (RD_SRST[i] && port_active(RD_CLK_ENABLE[i], RD_CLK_POLARITY[i], LAST_RD_CLK[i], RD_CLK[i]) && (RD_EN[i] || !RD_CE_OVER_SRST[i]))
|
||||
RD_DATA[i*WIDTH +: WIDTH] <= RD_SRST_VALUE[i*WIDTH +: WIDTH];
|
||||
if (RD_ARST[i])
|
||||
RD_DATA[i*WIDTH +: WIDTH] <= RD_ARST_VALUE[i*WIDTH +: WIDTH];
|
||||
end
|
||||
|
||||
LAST_RD_CLK <= RD_CLK;
|
||||
LAST_WR_CLK <= WR_CLK;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`endif
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
|
|
@ -50,25 +50,25 @@ design -reset; read_verilog ../common/blockram.v
|
|||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 36 sync_ram_sdp
|
||||
setattr -set syn_romstyle "ebr" m:memory
|
||||
synth_ecp5 -top sync_ram_sdp; cd sync_ram_sdp
|
||||
select -assert-count 1 t:$mem # requested BROM but this is a RAM
|
||||
select -assert-count 1 t:$mem_v2 # requested BROM but this is a RAM
|
||||
|
||||
design -reset; read_verilog ../common/blockram.v
|
||||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 36 sync_ram_sdp
|
||||
setattr -set rom_block 1 m:memory
|
||||
synth_ecp5 -top sync_ram_sdp; cd sync_ram_sdp
|
||||
select -assert-count 1 t:$mem # requested BROM but this is a RAM
|
||||
select -assert-count 1 t:$mem_v2 # requested BROM but this is a RAM
|
||||
|
||||
design -reset; read_verilog ../common/blockram.v
|
||||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 36 sync_ram_sdp
|
||||
setattr -set syn_ramstyle "block_ram" m:memory
|
||||
synth_ecp5 -top sync_ram_sdp -nobram; cd sync_ram_sdp
|
||||
select -assert-count 1 t:$mem # requested BRAM but BRAM is disabled
|
||||
select -assert-count 1 t:$mem_v2 # requested BRAM but BRAM is disabled
|
||||
|
||||
design -reset; read_verilog ../common/blockram.v
|
||||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 36 sync_ram_sdp
|
||||
setattr -set ram_block 1 m:memory
|
||||
synth_ecp5 -top sync_ram_sdp -nobram; cd sync_ram_sdp
|
||||
select -assert-count 1 t:$mem # requested BRAM but BRAM is disabled
|
||||
select -assert-count 1 t:$mem_v2 # requested BRAM but BRAM is disabled
|
||||
|
||||
# RAM bits <= 18K; Data width <= 18; Address width <= 10: -> DP16KD
|
||||
|
||||
|
@ -141,25 +141,25 @@ design -reset; read_verilog ../common/blockram.v
|
|||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 18 sync_ram_sdp
|
||||
setattr -set syn_romstyle "ebr" m:memory
|
||||
synth_ecp5 -top sync_ram_sdp; cd sync_ram_sdp
|
||||
select -assert-count 1 t:$mem # requested BROM but this is a RAM
|
||||
select -assert-count 1 t:$mem_v2 # requested BROM but this is a RAM
|
||||
|
||||
design -reset; read_verilog ../common/blockram.v
|
||||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 18 sync_ram_sdp
|
||||
setattr -set rom_block 1 m:memory
|
||||
synth_ecp5 -top sync_ram_sdp; cd sync_ram_sdp
|
||||
select -assert-count 1 t:$mem # requested BROM but this is a RAM
|
||||
select -assert-count 1 t:$mem_v2 # requested BROM but this is a RAM
|
||||
|
||||
design -reset; read_verilog ../common/blockram.v
|
||||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 18 sync_ram_sdp
|
||||
setattr -set syn_ramstyle "block_ram" m:memory
|
||||
synth_ecp5 -top sync_ram_sdp -nobram; cd sync_ram_sdp
|
||||
select -assert-count 1 t:$mem # requested BRAM but BRAM is disabled
|
||||
select -assert-count 1 t:$mem_v2 # requested BRAM but BRAM is disabled
|
||||
|
||||
design -reset; read_verilog ../common/blockram.v
|
||||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 18 sync_ram_sdp
|
||||
setattr -set ram_block 1 m:memory
|
||||
synth_ecp5 -top sync_ram_sdp -nobram; cd sync_ram_sdp
|
||||
select -assert-count 1 t:$mem # requested BRAM but BRAM is disabled
|
||||
select -assert-count 1 t:$mem_v2 # requested BRAM but BRAM is disabled
|
||||
|
||||
# RAM bits <= 64; Data width <= 4; Address width <= 4: -> DPR16X4
|
||||
|
||||
|
@ -194,7 +194,7 @@ design -reset; read_verilog ../common/blockram.v
|
|||
chparam -set ADDRESS_WIDTH 4 -set DATA_WIDTH 4 sync_ram_sdp
|
||||
setattr -set syn_ramstyle "distributed" m:memory
|
||||
synth_ecp5 -top sync_ram_sdp -nolutram; cd sync_ram_sdp
|
||||
select -assert-count 1 t:$mem # requested LUTRAM but LUTRAM is disabled
|
||||
select -assert-count 1 t:$mem_v2 # requested LUTRAM but LUTRAM is disabled
|
||||
|
||||
# ================================ ROM ================================
|
||||
# ROM bits <= 18K; Data width <= 36; Address width <= 9: -> PDPW16KD
|
||||
|
@ -242,25 +242,25 @@ design -reset; read_verilog ../common/blockrom.v
|
|||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 36 sync_rom
|
||||
setattr -set syn_ramstyle "block_ram" m:memory
|
||||
synth_ecp5 -top sync_rom; cd sync_rom
|
||||
select -assert-count 1 t:$mem # requested BRAM but this is a ROM
|
||||
select -assert-count 1 t:$mem_v2 # requested BRAM but this is a ROM
|
||||
|
||||
design -reset; read_verilog ../common/blockrom.v
|
||||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 36 sync_rom
|
||||
setattr -set ram_block 1 m:memory
|
||||
synth_ecp5 -top sync_rom; cd sync_rom
|
||||
select -assert-count 1 t:$mem # requested BRAM but this is a ROM
|
||||
select -assert-count 1 t:$mem_v2 # requested BRAM but this is a ROM
|
||||
|
||||
design -reset; read_verilog ../common/blockrom.v
|
||||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 36 sync_rom
|
||||
setattr -set syn_ramstyle "block_rom" m:memory
|
||||
synth_ecp5 -top sync_rom -nobram; cd sync_rom
|
||||
select -assert-count 1 t:$mem # requested BROM but BRAM is disabled
|
||||
select -assert-count 1 t:$mem_v2 # requested BROM but BRAM is disabled
|
||||
|
||||
design -reset; read_verilog ../common/blockrom.v
|
||||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 36 sync_rom
|
||||
setattr -set rom_block 1 m:memory
|
||||
synth_ecp5 -top sync_rom -nobram; cd sync_rom
|
||||
select -assert-count 1 t:$mem # requested BROM but BRAM is disabled
|
||||
select -assert-count 1 t:$mem_v2 # requested BROM but BRAM is disabled
|
||||
|
||||
# ROM bits <= 18K; Data width <= 18; Address width <= 10: -> DP16KD
|
||||
|
||||
|
@ -307,22 +307,22 @@ design -reset; read_verilog ../common/blockrom.v
|
|||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 18 sync_rom
|
||||
setattr -set syn_ramstyle "block_ram" m:memory
|
||||
synth_ecp5 -top sync_rom; cd sync_rom
|
||||
select -assert-count 1 t:$mem # requested BRAM but this is a ROM
|
||||
select -assert-count 1 t:$mem_v2 # requested BRAM but this is a ROM
|
||||
|
||||
design -reset; read_verilog ../common/blockrom.v
|
||||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 18 sync_rom
|
||||
setattr -set ram_block 1 m:memory
|
||||
synth_ecp5 -top sync_rom; cd sync_rom
|
||||
select -assert-count 1 t:$mem # requested BRAM but this is a ROM
|
||||
select -assert-count 1 t:$mem_v2 # requested BRAM but this is a ROM
|
||||
|
||||
design -reset; read_verilog ../common/blockrom.v
|
||||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 18 sync_rom
|
||||
setattr -set syn_ramstyle "block_rom" m:memory
|
||||
synth_ecp5 -top sync_rom -nobram; cd sync_rom
|
||||
select -assert-count 1 t:$mem # requested BROM but BRAM is disabled
|
||||
select -assert-count 1 t:$mem_v2 # requested BROM but BRAM is disabled
|
||||
|
||||
design -reset; read_verilog ../common/blockrom.v
|
||||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 18 sync_rom
|
||||
setattr -set rom_block 1 m:memory
|
||||
synth_ecp5 -top sync_rom -nobram; cd sync_rom
|
||||
select -assert-count 1 t:$mem # requested BROM but BRAM is disabled
|
||||
select -assert-count 1 t:$mem_v2 # requested BROM but BRAM is disabled
|
||||
|
|
|
@ -65,25 +65,25 @@ design -reset; read_verilog ../common/blockram.v
|
|||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 8 sync_ram_sdp
|
||||
setattr -set syn_romstyle "ebr" m:memory
|
||||
synth_ice40 -top sync_ram_sdp; cd sync_ram_sdp
|
||||
select -assert-count 1 t:$mem # requested BROM but this is a RAM
|
||||
select -assert-count 1 t:$mem_v2 # requested BROM but this is a RAM
|
||||
|
||||
design -reset; read_verilog ../common/blockram.v
|
||||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 8 sync_ram_sdp
|
||||
setattr -set rom_block 1 m:memory
|
||||
synth_ice40 -top sync_ram_sdp; cd sync_ram_sdp
|
||||
select -assert-count 1 t:$mem # requested BROM but this is a RAM
|
||||
select -assert-count 1 t:$mem_v2 # requested BROM but this is a RAM
|
||||
|
||||
design -reset; read_verilog ../common/blockram.v
|
||||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 8 sync_ram_sdp
|
||||
setattr -set syn_ramstyle "block_ram" m:memory
|
||||
synth_ice40 -top sync_ram_sdp -nobram; cd sync_ram_sdp
|
||||
select -assert-count 1 t:$mem # requested BRAM but BRAM is disabled
|
||||
select -assert-count 1 t:$mem_v2 # requested BRAM but BRAM is disabled
|
||||
|
||||
design -reset; read_verilog ../common/blockram.v
|
||||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 8 sync_ram_sdp
|
||||
setattr -set ram_block 1 m:memory
|
||||
synth_ice40 -top sync_ram_sdp -nobram; cd sync_ram_sdp
|
||||
select -assert-count 1 t:$mem # requested BRAM but BRAM is disabled
|
||||
select -assert-count 1 t:$mem_v2 # requested BRAM but BRAM is disabled
|
||||
|
||||
# ================================ ROM ================================
|
||||
# ROM bits <= 4K; Data width <= 16; Address width <= 11: -> SB_RAM40_4K
|
||||
|
@ -146,22 +146,22 @@ design -reset; read_verilog ../common/blockrom.v
|
|||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 8 sync_rom
|
||||
setattr -set syn_ramstyle "block_ram" m:memory
|
||||
synth_ice40 -top sync_rom; cd sync_rom
|
||||
select -assert-count 1 t:$mem # requested BRAM but this is a ROM
|
||||
select -assert-count 1 t:$mem_v2 # requested BRAM but this is a ROM
|
||||
|
||||
design -reset; read_verilog ../common/blockrom.v
|
||||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 8 sync_rom
|
||||
setattr -set ram_block 1 m:memory
|
||||
synth_ice40 -top sync_rom; cd sync_rom
|
||||
select -assert-count 1 t:$mem # requested BRAM but this is a ROM
|
||||
select -assert-count 1 t:$mem_v2 # requested BRAM but this is a ROM
|
||||
|
||||
design -reset; read_verilog ../common/blockrom.v
|
||||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 8 sync_rom
|
||||
setattr -set syn_romstyle "ebr" m:memory
|
||||
synth_ice40 -top sync_rom -nobram; cd sync_rom
|
||||
select -assert-count 1 t:$mem # requested BROM but BRAM is disabled
|
||||
select -assert-count 1 t:$mem_v2 # requested BROM but BRAM is disabled
|
||||
|
||||
design -reset; read_verilog ../common/blockrom.v
|
||||
chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 8 sync_rom
|
||||
setattr -set rom_block 1 m:memory
|
||||
synth_ice40 -top sync_rom -nobram; cd sync_rom
|
||||
select -assert-count 1 t:$mem # requested BROM but BRAM is disabled
|
||||
select -assert-count 1 t:$mem_v2 # requested BROM but BRAM is disabled
|
||||
|
|
|
@ -18,7 +18,7 @@ ${MAKE:-make} -f ../tools/autotest.mk SEED="$seed" EXTRA_FLAGS="$abcopt" *.v
|
|||
|
||||
for f in `egrep -l 'expect-(wr-ports|rd-ports|rd-clk)' *.v`; do
|
||||
echo -n "Testing expectations for $f .."
|
||||
../../yosys -qp "proc; opt; memory -nomap;; dump -outfile ${f%.v}.dmp t:\$mem" $f
|
||||
../../yosys -qp "proc; opt; memory -nomap;; dump -outfile ${f%.v}.dmp t:\$mem_v2" $f
|
||||
if grep -q expect-wr-ports $f; then
|
||||
grep -q "parameter \\\\WR_PORTS $(gawk '/expect-wr-ports/ { print $3; }' $f)\$" ${f%.v}.dmp ||
|
||||
{ echo " ERROR: Unexpected number of write ports."; false; }
|
||||
|
|
|
@ -31,4 +31,4 @@ proc
|
|||
opt
|
||||
select -assert-count 2 t:$memwr
|
||||
opt_mem
|
||||
select -assert-count 1 t:$memwr
|
||||
select -assert-count 1 t:$memwr_v2
|
||||
|
|
|
@ -37,7 +37,7 @@ design -save preopt
|
|||
|
||||
design -load start
|
||||
opt_mem_feedback
|
||||
select -assert-count 1 t:$memrd
|
||||
select -assert-count 1 t:$memrd_v2
|
||||
memory_map
|
||||
design -save postopt
|
||||
|
||||
|
@ -182,7 +182,7 @@ design -save preopt
|
|||
|
||||
design -load start
|
||||
opt_mem_feedback
|
||||
select -assert-count 1 t:$memrd
|
||||
select -assert-count 1 t:$memrd_v2
|
||||
memory_map
|
||||
design -save postopt
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
read_verilog -sv logic_rom.sv
|
||||
prep -top top
|
||||
select -assert-count 1 t:$mem r:SIZE=16 %i r:WIDTH=8 %i
|
||||
select -assert-count 1 t:$mem_v2 r:SIZE=16 %i r:WIDTH=8 %i
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
read_verilog -sv typedef_memory.sv
|
||||
prep -top top
|
||||
select -assert-count 1 t:$mem r:SIZE=16 %i r:WIDTH=4 %i
|
||||
select -assert-count 1 t:$mem_v2 r:SIZE=16 %i r:WIDTH=4 %i
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
read_verilog -sv typedef_memory_2.sv
|
||||
prep -top top
|
||||
dump
|
||||
select -assert-count 1 t:$mem r:SIZE=16 %i r:WIDTH=4 %i
|
||||
select -assert-count 1 t:$mem_v2 r:SIZE=16 %i r:WIDTH=4 %i
|
||||
|
|
Loading…
Reference in New Issue