/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2020 Marcelina Koƛcielnicka * * 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" 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() { 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); cell->parameters[ID::RD_PORTS] = Const(GetSize(rd_ports)); cell->parameters[ID::WR_PORTS] = Const(GetSize(wr_ports)); Const rd_clk_enable, rd_clk_polarity, rd_transparent; Const wr_clk_enable, wr_clk_polarity; 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) { if (port.cell) { module->remove(port.cell); port.cell = nullptr; } 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); log_assert(GetSize(port.clk) == 1); rd_en.append(port.en); log_assert(GetSize(port.en) == 1); SigSpec addr = port.addr; addr.extend_u0(abits, false); rd_addr.append(addr); log_assert(GetSize(addr) == abits); rd_data.append(port.data); log_assert(GetSize(port.data) == width); } if (rd_ports.empty()) { rd_clk_enable = State::S0; rd_clk_polarity = State::S0; rd_transparent = State::S0; } 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; } 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); log_assert(GetSize(port.clk) == 1); wr_en.append(port.en); log_assert(GetSize(port.en) == width); SigSpec addr = port.addr; addr.extend_u0(abits, false); wr_addr.append(addr); log_assert(GetSize(addr) == abits); wr_data.append(port.data); log_assert(GetSize(port.data) == width); } if (wr_ports.empty()) { wr_clk_enable = State::S0; wr_clk_polarity = State::S0; } 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) { 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); port.cell->parameters[ID::WIDTH] = width; 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); port.cell->parameters[ID::WIDTH] = width; 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::remove_wr_port(int idx) { if (wr_ports[idx].cell) { module->remove(wr_ports[idx].cell); } wr_ports.erase(wr_ports.begin() + idx); } void Mem::remove_rd_port(int idx) { if (rd_ports[idx].cell) { module->remove(rd_ports[idx].cell); } rd_ports.erase(rd_ports.begin() + idx); } 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; } namespace { struct MemIndex { dict> rd_ports; dict> wr_ports; dict> 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); res.rd_ports.push_back(mrd); } } if (index.wr_ports.count(mem->name)) { std::vector> 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); ports.push_back(std::make_pair(cell->parameters.at(ID::PRIORITY).as_int(), mwr)); } std::sort(ports.begin(), ports.end(), [](const std::pair &a, const std::pair &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> 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 &a, const std::pair &b) { return a.first < b.first; }); for (auto &it : inits) res.inits.push_back(it.second); } 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; 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); res.rd_ports.push_back(mrd); } for (int i = 0; i < cell->parameters.at(ID::WR_PORTS).as_int(); i++) { MemWr mwr; 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); } return res; } } std::vector Mem::get_all_memories(Module *module) { std::vector 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::get_selected_memories(Module *module) { std::vector 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; } Cell *Mem::extract_rdff(int idx) { MemRd &port = rd_ports[idx]; if (!port.clk_enable) return nullptr; Cell *c; if (port.transparent) { SigSpec sig_q = module->addWire(stringf("%s$rdreg[%d]$q", memid.c_str(), idx), GetSize(port.addr)); SigSpec sig_d = port.addr; port.addr = sig_q; c = module->addDffe(stringf("%s$rdreg[%d]", memid.c_str(), idx), port.clk, port.en, sig_d, sig_q, port.clk_polarity, true); } else { SigSpec sig_d = module->addWire(stringf("%s$rdreg[%d]$d", memid.c_str(), idx), width); SigSpec sig_q = port.data; port.data = sig_d; c = module->addDffe(stringf("%s$rdreg[%d]", memid.c_str(), idx), port.clk, port.en, sig_d, sig_q, port.clk_polarity, true); } log("Extracted %s FF from read port %d of %s.%s: %s\n", port.transparent ? "addr" : "data", idx, log_id(module), log_id(memid), log_id(c)); port.en = State::S1; port.clk = State::S0; port.clk_enable = false; port.clk_polarity = true; return c; }