2020-10-17 15:19:34 -05:00
|
|
|
/*
|
|
|
|
* yosys -- Yosys Open SYnthesis Suite
|
|
|
|
*
|
|
|
|
* Copyright (C) 2020 Marcelina Kościelnicka <mwk@0x04.net>
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "kernel/mem.h"
|
2021-05-22 10:18:59 -05:00
|
|
|
#include "kernel/ff.h"
|
2020-10-17 15:19:34 -05:00
|
|
|
|
|
|
|
USING_YOSYS_NAMESPACE
|
|
|
|
|
|
|
|
void Mem::remove() {
|
|
|
|
if (cell) {
|
|
|
|
module->remove(cell);
|
|
|
|
cell = nullptr;
|
|
|
|
}
|
|
|
|
if (mem) {
|
|
|
|
module->memories.erase(mem->name);
|
|
|
|
delete mem;
|
|
|
|
mem = nullptr;
|
|
|
|
}
|
|
|
|
for (auto &port : rd_ports) {
|
|
|
|
if (port.cell) {
|
|
|
|
module->remove(port.cell);
|
|
|
|
port.cell = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (auto &port : wr_ports) {
|
|
|
|
if (port.cell) {
|
|
|
|
module->remove(port.cell);
|
|
|
|
port.cell = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (auto &init : inits) {
|
|
|
|
if (init.cell) {
|
|
|
|
module->remove(init.cell);
|
|
|
|
init.cell = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Mem::emit() {
|
2021-05-22 09:36:50 -05:00
|
|
|
check();
|
2021-05-22 09:10:18 -05:00
|
|
|
std::vector<int> rd_left;
|
|
|
|
for (int i = 0; i < GetSize(rd_ports); i++) {
|
|
|
|
auto &port = rd_ports[i];
|
|
|
|
if (port.removed) {
|
|
|
|
if (port.cell) {
|
|
|
|
module->remove(port.cell);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
rd_left.push_back(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
std::vector<int> wr_left;
|
|
|
|
for (int i = 0; i < GetSize(wr_ports); i++) {
|
|
|
|
auto &port = wr_ports[i];
|
|
|
|
if (port.removed) {
|
|
|
|
if (port.cell) {
|
|
|
|
module->remove(port.cell);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
wr_left.push_back(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (int i = 0; i < GetSize(rd_left); i++)
|
|
|
|
if (i != rd_left[i])
|
|
|
|
std::swap(rd_ports[i], rd_ports[rd_left[i]]);
|
|
|
|
rd_ports.resize(GetSize(rd_left));
|
|
|
|
for (int i = 0; i < GetSize(wr_left); i++)
|
|
|
|
if (i != wr_left[i])
|
|
|
|
std::swap(wr_ports[i], wr_ports[wr_left[i]]);
|
|
|
|
wr_ports.resize(GetSize(wr_left));
|
|
|
|
|
|
|
|
// for future: handle transparency mask here
|
2021-05-22 10:00:20 -05:00
|
|
|
|
|
|
|
for (auto &port : wr_ports) {
|
|
|
|
for (int i = 0; i < GetSize(wr_left); i++)
|
|
|
|
port.priority_mask[i] = port.priority_mask[wr_left[i]];
|
|
|
|
port.priority_mask.resize(GetSize(wr_left));
|
|
|
|
}
|
2021-05-22 09:10:18 -05:00
|
|
|
|
2020-10-17 15:19:34 -05:00
|
|
|
if (packed) {
|
|
|
|
if (mem) {
|
|
|
|
module->memories.erase(mem->name);
|
|
|
|
delete mem;
|
|
|
|
mem = nullptr;
|
|
|
|
}
|
|
|
|
if (!cell) {
|
|
|
|
if (memid.empty())
|
|
|
|
memid = NEW_ID;
|
|
|
|
cell = module->addCell(memid, ID($mem));
|
|
|
|
}
|
|
|
|
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);
|
2021-05-24 17:49:52 -05:00
|
|
|
Const rd_wide_continuation, rd_clk_enable, rd_clk_polarity, rd_transparent;
|
|
|
|
Const wr_wide_continuation, wr_clk_enable, wr_clk_polarity;
|
2020-10-17 15:19:34 -05:00
|
|
|
SigSpec rd_clk, rd_en, rd_addr, rd_data;
|
|
|
|
SigSpec wr_clk, wr_en, wr_addr, wr_data;
|
|
|
|
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);
|
|
|
|
for (auto &port : rd_ports) {
|
2021-05-22 10:18:59 -05:00
|
|
|
// 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));
|
2020-10-17 15:19:34 -05:00
|
|
|
if (port.cell) {
|
|
|
|
module->remove(port.cell);
|
|
|
|
port.cell = nullptr;
|
|
|
|
}
|
2021-05-24 17:49:52 -05:00
|
|
|
for (int sub = 0; sub < (1 << port.wide_log2); sub++)
|
|
|
|
{
|
|
|
|
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(port.transparent));
|
|
|
|
rd_clk.append(port.clk);
|
|
|
|
rd_en.append(port.en);
|
|
|
|
SigSpec addr = port.addr;
|
|
|
|
addr.extend_u0(abits, false);
|
|
|
|
for (int i = 0; i < port.wide_log2; i++)
|
|
|
|
addr[i] = State(sub >> i & 1);
|
|
|
|
rd_addr.append(addr);
|
|
|
|
log_assert(GetSize(addr) == abits);
|
|
|
|
}
|
2020-10-17 15:19:34 -05:00
|
|
|
rd_data.append(port.data);
|
|
|
|
}
|
|
|
|
if (rd_ports.empty()) {
|
2021-05-24 17:49:52 -05:00
|
|
|
rd_wide_continuation = State::S0;
|
2020-10-17 15:19:34 -05:00
|
|
|
rd_clk_enable = State::S0;
|
|
|
|
rd_clk_polarity = State::S0;
|
|
|
|
rd_transparent = State::S0;
|
|
|
|
}
|
2021-05-24 17:49:52 -05:00
|
|
|
cell->parameters[ID::RD_PORTS] = Const(GetSize(rd_clk));
|
2020-10-17 15:19:34 -05:00
|
|
|
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->setPort(ID::RD_CLK, rd_clk);
|
|
|
|
cell->setPort(ID::RD_EN, rd_en);
|
|
|
|
cell->setPort(ID::RD_ADDR, rd_addr);
|
|
|
|
cell->setPort(ID::RD_DATA, rd_data);
|
|
|
|
for (auto &port : wr_ports) {
|
|
|
|
if (port.cell) {
|
|
|
|
module->remove(port.cell);
|
|
|
|
port.cell = nullptr;
|
|
|
|
}
|
2021-05-24 17:49:52 -05:00
|
|
|
for (int sub = 0; sub < (1 << port.wide_log2); sub++)
|
|
|
|
{
|
|
|
|
wr_wide_continuation.bits.push_back(State(sub != 0));
|
|
|
|
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);
|
|
|
|
SigSpec addr = port.addr;
|
|
|
|
addr.extend_u0(abits, false);
|
|
|
|
for (int i = 0; i < port.wide_log2; i++)
|
|
|
|
addr[i] = State(sub >> i & 1);
|
|
|
|
wr_addr.append(addr);
|
|
|
|
log_assert(GetSize(addr) == abits);
|
|
|
|
}
|
2020-10-17 15:19:34 -05:00
|
|
|
wr_en.append(port.en);
|
|
|
|
wr_data.append(port.data);
|
|
|
|
}
|
|
|
|
if (wr_ports.empty()) {
|
2021-05-24 17:49:52 -05:00
|
|
|
wr_wide_continuation = State::S0;
|
2020-10-17 15:19:34 -05:00
|
|
|
wr_clk_enable = State::S0;
|
|
|
|
wr_clk_polarity = State::S0;
|
|
|
|
}
|
2021-05-24 17:49:52 -05:00
|
|
|
cell->parameters[ID::WR_PORTS] = Const(GetSize(wr_clk));
|
2020-10-17 15:19:34 -05:00
|
|
|
cell->parameters[ID::WR_CLK_ENABLE] = wr_clk_enable;
|
|
|
|
cell->parameters[ID::WR_CLK_POLARITY] = wr_clk_polarity;
|
|
|
|
cell->setPort(ID::WR_CLK, wr_clk);
|
|
|
|
cell->setPort(ID::WR_EN, wr_en);
|
|
|
|
cell->setPort(ID::WR_ADDR, wr_addr);
|
|
|
|
cell->setPort(ID::WR_DATA, wr_data);
|
|
|
|
for (auto &init : inits) {
|
|
|
|
if (init.cell) {
|
|
|
|
module->remove(init.cell);
|
|
|
|
init.cell = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cell->parameters[ID::INIT] = get_init_data();
|
|
|
|
} else {
|
|
|
|
if (cell) {
|
|
|
|
module->remove(cell);
|
|
|
|
cell = nullptr;
|
|
|
|
}
|
|
|
|
if (!mem) {
|
|
|
|
if (memid.empty())
|
|
|
|
memid = NEW_ID;
|
|
|
|
mem = new RTLIL::Memory;
|
|
|
|
mem->name = memid;
|
|
|
|
module->memories[memid] = mem;
|
|
|
|
}
|
|
|
|
mem->width = width;
|
|
|
|
mem->start_offset = start_offset;
|
|
|
|
mem->size = size;
|
|
|
|
for (auto &port : rd_ports) {
|
2021-05-22 10:18:59 -05:00
|
|
|
// 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));
|
2020-10-17 15:19:34 -05:00
|
|
|
if (!port.cell)
|
|
|
|
port.cell = module->addCell(NEW_ID, ID($memrd));
|
|
|
|
port.cell->parameters[ID::MEMID] = memid.str();
|
|
|
|
port.cell->parameters[ID::ABITS] = GetSize(port.addr);
|
2021-05-22 09:48:46 -05:00
|
|
|
port.cell->parameters[ID::WIDTH] = width << port.wide_log2;
|
2020-10-17 15:19:34 -05:00
|
|
|
port.cell->parameters[ID::CLK_ENABLE] = port.clk_enable;
|
|
|
|
port.cell->parameters[ID::CLK_POLARITY] = port.clk_polarity;
|
|
|
|
port.cell->parameters[ID::TRANSPARENT] = port.transparent;
|
|
|
|
port.cell->setPort(ID::CLK, port.clk);
|
|
|
|
port.cell->setPort(ID::EN, port.en);
|
|
|
|
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->parameters[ID::MEMID] = memid.str();
|
|
|
|
port.cell->parameters[ID::ABITS] = GetSize(port.addr);
|
2021-05-22 09:48:46 -05:00
|
|
|
port.cell->parameters[ID::WIDTH] = width << port.wide_log2;
|
2020-10-17 15:19:34 -05:00
|
|
|
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->setPort(ID::CLK, port.clk);
|
|
|
|
port.cell->setPort(ID::EN, port.en);
|
|
|
|
port.cell->setPort(ID::ADDR, port.addr);
|
|
|
|
port.cell->setPort(ID::DATA, port.data);
|
|
|
|
}
|
|
|
|
idx = 0;
|
|
|
|
for (auto &init : inits) {
|
|
|
|
if (!init.cell)
|
|
|
|
init.cell = module->addCell(NEW_ID, ID($meminit));
|
|
|
|
init.cell->parameters[ID::MEMID] = memid.str();
|
|
|
|
init.cell->parameters[ID::ABITS] = GetSize(init.addr);
|
|
|
|
init.cell->parameters[ID::WIDTH] = width;
|
|
|
|
init.cell->parameters[ID::WORDS] = GetSize(init.data) / width;
|
|
|
|
init.cell->parameters[ID::PRIORITY] = idx++;
|
|
|
|
init.cell->setPort(ID::ADDR, init.addr);
|
|
|
|
init.cell->setPort(ID::DATA, init.data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Mem::clear_inits() {
|
|
|
|
for (auto &init : inits)
|
|
|
|
if (init.cell)
|
|
|
|
module->remove(init.cell);
|
|
|
|
inits.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
Const Mem::get_init_data() const {
|
|
|
|
Const init_data(State::Sx, width * size);
|
|
|
|
for (auto &init : inits) {
|
|
|
|
int offset = (init.addr.as_int() - start_offset) * width;
|
|
|
|
for (int i = 0; i < GetSize(init.data); i++)
|
|
|
|
if (0 <= i+offset && i+offset < GetSize(init_data))
|
|
|
|
init_data.bits[i+offset] = init.data.bits[i];
|
|
|
|
}
|
|
|
|
return init_data;
|
|
|
|
}
|
|
|
|
|
2021-05-22 09:36:50 -05:00
|
|
|
void Mem::check() {
|
2021-05-22 09:48:46 -05:00
|
|
|
int max_wide_log2 = 0;
|
2021-05-22 09:36:50 -05:00
|
|
|
for (auto &port : rd_ports) {
|
|
|
|
if (port.removed)
|
|
|
|
continue;
|
|
|
|
log_assert(GetSize(port.clk) == 1);
|
|
|
|
log_assert(GetSize(port.en) == 1);
|
2021-05-22 10:18:59 -05:00
|
|
|
log_assert(GetSize(port.arst) == 1);
|
|
|
|
log_assert(GetSize(port.srst) == 1);
|
2021-05-22 09:48:46 -05:00
|
|
|
log_assert(GetSize(port.data) == (width << port.wide_log2));
|
2021-05-22 10:18:59 -05:00
|
|
|
log_assert(GetSize(port.init_value) == (width << port.wide_log2));
|
|
|
|
log_assert(GetSize(port.arst_value) == (width << port.wide_log2));
|
|
|
|
log_assert(GetSize(port.srst_value) == (width << port.wide_log2));
|
2021-05-22 09:36:50 -05:00
|
|
|
if (!port.clk_enable) {
|
|
|
|
log_assert(!port.transparent);
|
2021-05-22 10:18:59 -05:00
|
|
|
log_assert(port.arst == State::S0);
|
|
|
|
log_assert(port.srst == State::S0);
|
2021-05-22 09:36:50 -05:00
|
|
|
}
|
2021-05-22 09:48:46 -05:00
|
|
|
for (int j = 0; j < port.wide_log2; j++) {
|
|
|
|
log_assert(port.addr[j] == State::S0);
|
|
|
|
}
|
|
|
|
max_wide_log2 = std::max(max_wide_log2, port.wide_log2);
|
2021-05-22 09:36:50 -05:00
|
|
|
}
|
|
|
|
for (int i = 0; i < GetSize(wr_ports); i++) {
|
|
|
|
auto &port = wr_ports[i];
|
|
|
|
if (port.removed)
|
|
|
|
continue;
|
|
|
|
log_assert(GetSize(port.clk) == 1);
|
2021-05-22 09:48:46 -05:00
|
|
|
log_assert(GetSize(port.en) == (width << port.wide_log2));
|
|
|
|
log_assert(GetSize(port.data) == (width << port.wide_log2));
|
|
|
|
for (int j = 0; j < port.wide_log2; j++) {
|
|
|
|
log_assert(port.addr[j] == State::S0);
|
|
|
|
}
|
|
|
|
max_wide_log2 = std::max(max_wide_log2, port.wide_log2);
|
2021-05-22 10:00:20 -05:00
|
|
|
log_assert(GetSize(port.priority_mask) == GetSize(wr_ports));
|
|
|
|
for (int j = 0; j < GetSize(wr_ports); j++) {
|
|
|
|
auto &wport = wr_ports[j];
|
|
|
|
if (port.priority_mask[j] && !wport.removed) {
|
|
|
|
log_assert(j < i);
|
|
|
|
log_assert(port.clk_enable == wport.clk_enable);
|
|
|
|
if (port.clk_enable) {
|
|
|
|
log_assert(port.clk == wport.clk);
|
|
|
|
log_assert(port.clk_polarity == wport.clk_polarity);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-05-22 09:36:50 -05:00
|
|
|
}
|
2021-05-22 09:48:46 -05:00
|
|
|
int mask = (1 << max_wide_log2) - 1;
|
|
|
|
log_assert(!(start_offset & mask));
|
|
|
|
log_assert(!(size & mask));
|
2021-05-22 09:36:50 -05:00
|
|
|
}
|
|
|
|
|
2020-10-17 15:19:34 -05:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
struct MemIndex {
|
|
|
|
dict<IdString, pool<Cell *>> rd_ports;
|
|
|
|
dict<IdString, pool<Cell *>> wr_ports;
|
|
|
|
dict<IdString, pool<Cell *>> inits;
|
|
|
|
MemIndex (Module *module) {
|
|
|
|
for (auto cell: module->cells()) {
|
|
|
|
if (cell->type == ID($memwr))
|
|
|
|
wr_ports[cell->parameters.at(ID::MEMID).decode_string()].insert(cell);
|
|
|
|
else if (cell->type == ID($memrd))
|
|
|
|
rd_ports[cell->parameters.at(ID::MEMID).decode_string()].insert(cell);
|
|
|
|
else if (cell->type == ID($meminit))
|
|
|
|
inits[cell->parameters.at(ID::MEMID).decode_string()].insert(cell);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Mem mem_from_memory(Module *module, RTLIL::Memory *mem, const MemIndex &index) {
|
|
|
|
Mem res(module, mem->name, mem->width, mem->start_offset, mem->size);
|
|
|
|
res.packed = false;
|
|
|
|
res.mem = mem;
|
|
|
|
res.attributes = mem->attributes;
|
|
|
|
if (index.rd_ports.count(mem->name)) {
|
|
|
|
for (auto cell : index.rd_ports.at(mem->name)) {
|
|
|
|
MemRd mrd;
|
|
|
|
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();
|
|
|
|
mrd.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);
|
2021-05-22 09:48:46 -05:00
|
|
|
mrd.wide_log2 = ceil_log2(GetSize(mrd.data) / mem->width);
|
2021-05-22 10:18:59 -05:00
|
|
|
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;
|
2020-10-17 15:19:34 -05:00
|
|
|
res.rd_ports.push_back(mrd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (index.wr_ports.count(mem->name)) {
|
|
|
|
std::vector<std::pair<int, MemWr>> ports;
|
|
|
|
for (auto cell : index.wr_ports.at(mem->name)) {
|
|
|
|
MemWr mwr;
|
|
|
|
mwr.cell = cell;
|
|
|
|
mwr.attributes = cell->attributes;
|
|
|
|
mwr.clk_enable = cell->parameters.at(ID::CLK_ENABLE).as_bool();
|
|
|
|
mwr.clk_polarity = cell->parameters.at(ID::CLK_POLARITY).as_bool();
|
|
|
|
mwr.clk = cell->getPort(ID::CLK);
|
|
|
|
mwr.en = cell->getPort(ID::EN);
|
|
|
|
mwr.addr = cell->getPort(ID::ADDR);
|
|
|
|
mwr.data = cell->getPort(ID::DATA);
|
2021-05-22 09:48:46 -05:00
|
|
|
mwr.wide_log2 = ceil_log2(GetSize(mwr.data) / mem->width);
|
2020-10-17 15:19:34 -05:00
|
|
|
ports.push_back(std::make_pair(cell->parameters.at(ID::PRIORITY).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)
|
|
|
|
res.wr_ports.push_back(it.second);
|
|
|
|
}
|
|
|
|
if (index.inits.count(mem->name)) {
|
|
|
|
std::vector<std::pair<int, MemInit>> inits;
|
|
|
|
for (auto cell : index.inits.at(mem->name)) {
|
|
|
|
MemInit init;
|
|
|
|
init.cell = cell;
|
|
|
|
init.attributes = cell->attributes;
|
|
|
|
auto addr = cell->getPort(ID::ADDR);
|
|
|
|
auto data = cell->getPort(ID::DATA);
|
|
|
|
if (!addr.is_fully_const())
|
|
|
|
log_error("Non-constant address %s in memory initialization %s.\n", log_signal(addr), log_id(cell));
|
|
|
|
if (!data.is_fully_const())
|
|
|
|
log_error("Non-constant data %s in memory initialization %s.\n", log_signal(data), log_id(cell));
|
|
|
|
init.addr = addr.as_const();
|
|
|
|
init.data = data.as_const();
|
|
|
|
inits.push_back(std::make_pair(cell->parameters.at(ID::PRIORITY).as_int(), init));
|
|
|
|
}
|
|
|
|
std::sort(inits.begin(), inits.end(), [](const std::pair<int, MemInit> &a, const std::pair<int, MemInit> &b) { return a.first < b.first; });
|
|
|
|
for (auto &it : inits)
|
|
|
|
res.inits.push_back(it.second);
|
|
|
|
}
|
2021-05-22 10:00:20 -05:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2021-05-22 09:36:50 -05:00
|
|
|
res.check();
|
2020-10-17 15:19:34 -05:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
Mem mem_from_cell(Cell *cell) {
|
|
|
|
Mem res(cell->module, cell->parameters.at(ID::MEMID).decode_string(),
|
|
|
|
cell->parameters.at(ID::WIDTH).as_int(),
|
|
|
|
cell->parameters.at(ID::OFFSET).as_int(),
|
|
|
|
cell->parameters.at(ID::SIZE).as_int()
|
|
|
|
);
|
|
|
|
int abits = cell->parameters.at(ID::ABITS).as_int();
|
|
|
|
res.packed = true;
|
|
|
|
res.cell = cell;
|
|
|
|
res.attributes = cell->attributes;
|
|
|
|
Const &init = cell->parameters.at(ID::INIT);
|
|
|
|
if (!init.is_fully_undef()) {
|
|
|
|
int pos = 0;
|
|
|
|
while (pos < res.size) {
|
|
|
|
Const word = init.extract(pos * res.width, res.width, State::Sx);
|
|
|
|
if (word.is_fully_undef()) {
|
|
|
|
pos++;
|
|
|
|
} else {
|
|
|
|
int epos;
|
|
|
|
for (epos = pos; epos < res.size; epos++) {
|
|
|
|
Const eword = init.extract(epos * res.width, res.width, State::Sx);
|
|
|
|
if (eword.is_fully_undef())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
MemInit minit;
|
|
|
|
minit.addr = res.start_offset + pos;
|
|
|
|
minit.data = init.extract(pos * res.width, (epos - pos) * res.width, State::Sx);
|
|
|
|
res.inits.push_back(minit);
|
|
|
|
pos = epos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (int i = 0; i < cell->parameters.at(ID::RD_PORTS).as_int(); i++) {
|
|
|
|
MemRd mrd;
|
2021-05-22 09:48:46 -05:00
|
|
|
mrd.wide_log2 = 0;
|
2020-10-17 15:19:34 -05:00
|
|
|
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.transparent = cell->parameters.at(ID::RD_TRANSPARENT).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);
|
2021-05-22 10:18:59 -05:00
|
|
|
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;
|
2020-10-17 15:19:34 -05:00
|
|
|
res.rd_ports.push_back(mrd);
|
|
|
|
}
|
|
|
|
for (int i = 0; i < cell->parameters.at(ID::WR_PORTS).as_int(); i++) {
|
|
|
|
MemWr mwr;
|
2021-05-22 09:48:46 -05:00
|
|
|
mwr.wide_log2 = 0;
|
2020-10-17 15:19:34 -05:00
|
|
|
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.addr = cell->getPort(ID::WR_ADDR).extract(i * abits, abits);
|
|
|
|
mwr.data = cell->getPort(ID::WR_DATA).extract(i * res.width, res.width);
|
|
|
|
res.wr_ports.push_back(mwr);
|
|
|
|
}
|
2021-05-22 10:00:20 -05:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2021-05-22 09:36:50 -05:00
|
|
|
res.check();
|
2020-10-17 15:19:34 -05:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<Mem> Mem::get_all_memories(Module *module) {
|
|
|
|
std::vector<Mem> res;
|
|
|
|
MemIndex index(module);
|
|
|
|
for (auto it: module->memories) {
|
|
|
|
res.push_back(mem_from_memory(module, it.second, index));
|
|
|
|
}
|
|
|
|
for (auto cell: module->cells()) {
|
|
|
|
if (cell->type == ID($mem))
|
|
|
|
res.push_back(mem_from_cell(cell));
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<Mem> Mem::get_selected_memories(Module *module) {
|
|
|
|
std::vector<Mem> res;
|
|
|
|
MemIndex index(module);
|
|
|
|
for (auto it: module->memories) {
|
|
|
|
if (module->design->selected(module, it.second))
|
|
|
|
res.push_back(mem_from_memory(module, it.second, index));
|
|
|
|
}
|
|
|
|
for (auto cell: module->selected_cells()) {
|
|
|
|
if (cell->type == ID($mem))
|
|
|
|
res.push_back(mem_from_cell(cell));
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2021-05-23 11:29:44 -05:00
|
|
|
Cell *Mem::extract_rdff(int idx, FfInitVals *initvals) {
|
2020-10-17 15:19:34 -05:00
|
|
|
MemRd &port = rd_ports[idx];
|
|
|
|
|
|
|
|
if (!port.clk_enable)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
Cell *c;
|
|
|
|
|
2021-05-25 13:42:34 -05:00
|
|
|
// There are two ways to handle rdff extraction when transparency is involved:
|
|
|
|
//
|
|
|
|
// - if all of the following conditions are true, put the FF on address input:
|
|
|
|
//
|
|
|
|
// - the port has no clock enable, no reset, and no initial value
|
|
|
|
// - the port is transparent wrt all write ports (implying they also share
|
|
|
|
// the clock domain)
|
|
|
|
//
|
|
|
|
// - otherwise, put the FF on the data output, and make bypass paths for
|
|
|
|
// all write ports wrt which this port is transparent
|
|
|
|
bool trans_use_addr = port.transparent;
|
|
|
|
|
|
|
|
// If there are no write ports at all, we could possibly use either way; do data
|
|
|
|
// FF in this case.
|
|
|
|
if (GetSize(wr_ports) == 0)
|
|
|
|
trans_use_addr = false;
|
2021-05-25 08:48:52 -05:00
|
|
|
|
2021-05-25 13:42:34 -05:00
|
|
|
if (port.en != State::S1 || port.srst != State::S0 || port.arst != State::S0 || !port.init_value.is_fully_undef())
|
|
|
|
trans_use_addr = false;
|
|
|
|
|
|
|
|
if (trans_use_addr)
|
|
|
|
{
|
2021-05-25 08:48:52 -05:00
|
|
|
// Do not put a register in front of constant address bits — this is both
|
|
|
|
// unnecessary and will break wide ports.
|
|
|
|
int width = 0;
|
|
|
|
for (int i = 0; i < GetSize(port.addr); i++)
|
|
|
|
if (port.addr[i].wire)
|
|
|
|
width++;
|
|
|
|
|
2021-05-25 15:39:50 -05:00
|
|
|
if (width)
|
|
|
|
{
|
2021-05-25 13:42:34 -05:00
|
|
|
SigSpec sig_q = module->addWire(stringf("$%s$rdreg[%d]$q", memid.c_str(), idx), width);
|
|
|
|
SigSpec sig_d;
|
2021-05-25 08:48:52 -05:00
|
|
|
|
2021-05-25 13:42:34 -05:00
|
|
|
int pos = 0;
|
|
|
|
for (int i = 0; i < GetSize(port.addr); i++)
|
|
|
|
if (port.addr[i].wire) {
|
|
|
|
sig_d.append(port.addr[i]);
|
|
|
|
port.addr[i] = sig_q[pos++];
|
|
|
|
}
|
2021-05-25 08:48:52 -05:00
|
|
|
|
2021-05-25 13:42:34 -05:00
|
|
|
c = module->addDff(stringf("$%s$rdreg[%d]", memid.c_str(), idx), port.clk, sig_d, sig_q, port.clk_polarity);
|
2021-05-25 15:39:50 -05:00
|
|
|
} else {
|
|
|
|
c = nullptr;
|
2021-05-25 13:42:34 -05:00
|
|
|
}
|
2020-10-17 15:19:34 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-05-22 10:18:59 -05:00
|
|
|
log_assert(port.arst == State::S0 || port.srst == State::S0);
|
|
|
|
|
2021-05-25 13:42:34 -05:00
|
|
|
SigSpec async_d = module->addWire(stringf("$%s$rdreg[%d]$d", memid.c_str(), idx), GetSize(port.data));
|
|
|
|
SigSpec sig_d = async_d;
|
|
|
|
|
|
|
|
for (int i = 0; i < GetSize(wr_ports); i++) {
|
|
|
|
auto &wport = wr_ports[i];
|
|
|
|
if (port.transparent) {
|
|
|
|
log_assert(wport.clk_enable);
|
|
|
|
log_assert(wport.clk == port.clk);
|
|
|
|
log_assert(wport.clk_enable == port.clk_enable);
|
|
|
|
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;
|
|
|
|
for (int j = min_wide_log2; j < max_wide_log2; j++)
|
|
|
|
if (wide_write)
|
|
|
|
waddr[j] = State(sub >> j & 1);
|
|
|
|
else
|
|
|
|
raddr[j] = State(sub >> j & 1);
|
|
|
|
SigSpec addr_eq;
|
|
|
|
if (raddr != waddr)
|
|
|
|
addr_eq = module->Eq(stringf("$%s$rdtransen[%d][%d][%d]$d", memid.c_str(), idx, i, sub), raddr, waddr);
|
|
|
|
int pos = 0;
|
|
|
|
int ewidth = 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 * width] == wport.en[pos + wsub * width])
|
|
|
|
epos++;
|
|
|
|
SigSpec cur = sig_d.extract(pos + rsub * width, epos-pos);
|
|
|
|
SigSpec other = wport.data.extract(pos + wsub * width, epos-pos);
|
|
|
|
SigSpec cond;
|
|
|
|
if (raddr != waddr)
|
|
|
|
cond = module->And(stringf("$%s$rdtransgate[%d][%d][%d][%d]$d", memid.c_str(), idx, i, sub, pos), wport.en[pos + wsub * width], addr_eq);
|
|
|
|
else
|
|
|
|
cond = wport.en[pos + wsub * width];
|
|
|
|
SigSpec merged = module->Mux(stringf("$%s$rdtransmux[%d][%d][%d][%d]$d", memid.c_str(), idx, i, sub, pos), cur, other, cond);
|
|
|
|
sig_d.replace(pos + rsub * width, merged);
|
|
|
|
pos = epos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-22 10:18:59 -05:00
|
|
|
IdString name = stringf("$%s$rdreg[%d]", memid.c_str(), idx);
|
|
|
|
FfData ff(initvals);
|
|
|
|
ff.width = GetSize(port.data);
|
|
|
|
ff.has_clk = true;
|
|
|
|
ff.sig_clk = port.clk;
|
|
|
|
ff.pol_clk = port.clk_polarity;
|
|
|
|
if (port.en != State::S1) {
|
|
|
|
ff.has_en = true;
|
|
|
|
ff.pol_en = true;
|
|
|
|
ff.sig_en = port.en;
|
|
|
|
}
|
|
|
|
if (port.arst != State::S0) {
|
|
|
|
ff.has_arst = true;
|
|
|
|
ff.pol_arst = true;
|
|
|
|
ff.sig_arst = port.arst;
|
|
|
|
ff.val_arst = port.arst_value;
|
|
|
|
}
|
|
|
|
if (port.srst != State::S0) {
|
|
|
|
ff.has_srst = true;
|
|
|
|
ff.pol_srst = true;
|
|
|
|
ff.sig_srst = port.srst;
|
|
|
|
ff.val_srst = port.srst_value;
|
|
|
|
ff.ce_over_srst = ff.has_en && port.ce_over_srst;
|
|
|
|
}
|
|
|
|
ff.sig_d = sig_d;
|
|
|
|
ff.sig_q = port.data;
|
|
|
|
ff.val_init = port.init_value;
|
2021-05-25 13:42:34 -05:00
|
|
|
port.data = async_d;
|
2021-05-22 10:18:59 -05:00
|
|
|
c = ff.emit(module, name);
|
2020-10-17 15:19:34 -05:00
|
|
|
}
|
|
|
|
|
2021-05-25 13:42:34 -05:00
|
|
|
log("Extracted %s FF from read port %d of %s.%s: %s\n", trans_use_addr ? "addr" : "data",
|
2020-10-17 15:19:34 -05:00
|
|
|
idx, log_id(module), log_id(memid), log_id(c));
|
|
|
|
|
|
|
|
port.en = State::S1;
|
|
|
|
port.clk = State::S0;
|
2021-05-22 10:18:59 -05:00
|
|
|
port.arst = State::S0;
|
|
|
|
port.srst = State::S0;
|
2020-10-17 15:19:34 -05:00
|
|
|
port.clk_enable = false;
|
|
|
|
port.clk_polarity = true;
|
2021-05-22 09:36:50 -05:00
|
|
|
port.transparent = false;
|
2021-05-22 10:18:59 -05:00
|
|
|
port.ce_over_srst = false;
|
|
|
|
port.arst_value = Const(State::Sx, GetSize(port.data));
|
|
|
|
port.srst_value = Const(State::Sx, GetSize(port.data));
|
|
|
|
port.init_value = Const(State::Sx, GetSize(port.data));
|
2020-10-17 15:19:34 -05:00
|
|
|
|
|
|
|
return c;
|
|
|
|
}
|
2021-05-24 17:58:17 -05:00
|
|
|
|
|
|
|
void Mem::narrow() {
|
|
|
|
std::vector<MemRd> new_rd_ports;
|
|
|
|
std::vector<MemWr> new_wr_ports;
|
|
|
|
std::vector<std::pair<int, int>> new_rd_map;
|
|
|
|
std::vector<std::pair<int, int>> new_wr_map;
|
|
|
|
for (int i = 0; i < GetSize(rd_ports); i++) {
|
|
|
|
auto &port = rd_ports[i];
|
|
|
|
for (int sub = 0; sub < (1 << port.wide_log2); sub++) {
|
|
|
|
new_rd_map.push_back(std::make_pair(i, sub));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (int i = 0; i < GetSize(wr_ports); i++) {
|
|
|
|
auto &port = wr_ports[i];
|
|
|
|
for (int sub = 0; sub < (1 << port.wide_log2); sub++) {
|
|
|
|
new_wr_map.push_back(std::make_pair(i, sub));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (auto &it : new_rd_map) {
|
|
|
|
MemRd &orig = rd_ports[it.first];
|
|
|
|
MemRd port = orig;
|
|
|
|
if (it.second != 0)
|
|
|
|
port.cell = nullptr;
|
|
|
|
if (port.wide_log2) {
|
|
|
|
port.data = port.data.extract(it.second * width, width);
|
2021-05-22 10:18:59 -05:00
|
|
|
port.init_value = port.init_value.extract(it.second * width, width);
|
|
|
|
port.arst_value = port.arst_value.extract(it.second * width, width);
|
|
|
|
port.srst_value = port.srst_value.extract(it.second * width, width);
|
2021-05-24 17:58:17 -05:00
|
|
|
for (int i = 0; i < port.wide_log2; i++)
|
|
|
|
port.addr[i] = State(it.second >> i & 1);
|
|
|
|
port.wide_log2 = 0;
|
|
|
|
}
|
|
|
|
new_rd_ports.push_back(port);
|
|
|
|
}
|
|
|
|
for (auto &it : new_wr_map) {
|
|
|
|
MemWr &orig = wr_ports[it.first];
|
|
|
|
MemWr port = orig;
|
|
|
|
if (it.second != 0)
|
|
|
|
port.cell = nullptr;
|
|
|
|
if (port.wide_log2) {
|
|
|
|
port.data = port.data.extract(it.second * width, width);
|
|
|
|
port.en = port.en.extract(it.second * width, width);
|
|
|
|
for (int i = 0; i < port.wide_log2; i++)
|
|
|
|
port.addr[i] = State(it.second >> i & 1);
|
|
|
|
port.wide_log2 = 0;
|
|
|
|
}
|
|
|
|
port.priority_mask.clear();
|
|
|
|
for (auto &it2 : new_wr_map)
|
|
|
|
port.priority_mask.push_back(orig.priority_mask[it2.first]);
|
|
|
|
new_wr_ports.push_back(port);
|
|
|
|
}
|
|
|
|
std::swap(rd_ports, new_rd_ports);
|
|
|
|
std::swap(wr_ports, new_wr_ports);
|
|
|
|
}
|
2021-05-24 19:56:35 -05:00
|
|
|
|
|
|
|
void Mem::emulate_priority(int idx1, int idx2)
|
|
|
|
{
|
|
|
|
auto &port1 = wr_ports[idx1];
|
|
|
|
auto &port2 = wr_ports[idx2];
|
|
|
|
if (!port2.priority_mask[idx1])
|
|
|
|
return;
|
|
|
|
int min_wide_log2 = std::min(port1.wide_log2, port2.wide_log2);
|
|
|
|
int max_wide_log2 = std::max(port1.wide_log2, port2.wide_log2);
|
|
|
|
bool wide1 = port1.wide_log2 > port2.wide_log2;
|
|
|
|
for (int sub = 0; sub < (1 << max_wide_log2); sub += (1 << min_wide_log2)) {
|
|
|
|
SigSpec addr1 = port1.addr;
|
|
|
|
SigSpec addr2 = port2.addr;
|
|
|
|
for (int j = min_wide_log2; j < max_wide_log2; j++)
|
|
|
|
if (wide1)
|
|
|
|
addr1[j] = State(sub >> j & 1);
|
|
|
|
else
|
|
|
|
addr2[j] = State(sub >> j & 1);
|
|
|
|
SigSpec addr_eq = module->Eq(NEW_ID, addr1, addr2);
|
|
|
|
int ewidth = width << min_wide_log2;
|
|
|
|
int sub1 = wide1 ? sub : 0;
|
|
|
|
int sub2 = wide1 ? 0 : sub;
|
|
|
|
dict<std::pair<SigBit, SigBit>, SigBit> cache;
|
|
|
|
for (int pos = 0; pos < ewidth; pos++) {
|
|
|
|
SigBit &en1 = port1.en[pos + sub1 * width];
|
|
|
|
SigBit &en2 = port2.en[pos + sub2 * width];
|
|
|
|
std::pair<SigBit, SigBit> key(en1, en2);
|
|
|
|
if (cache.count(key)) {
|
|
|
|
en1 = cache[key];
|
|
|
|
} else {
|
|
|
|
SigBit active2 = module->And(NEW_ID, addr_eq, en2);
|
|
|
|
SigBit nactive2 = module->Not(NEW_ID, active2);
|
|
|
|
en1 = cache[key] = module->And(NEW_ID, en1, nactive2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
port2.priority_mask[idx1] = false;
|
|
|
|
}
|