diff options
author | Marcelina Kościelnicka <mwk@0x04.net> | 2020-10-17 22:19:34 +0200 |
---|---|---|
committer | Marcelina Kościelnicka <mwk@0x04.net> | 2020-10-21 17:51:20 +0200 |
commit | 8720482ebdb4045c04636ea349ccea416bd78d17 (patch) | |
tree | f2e27e7d90af6b4667818990049906749bf9d285 | |
parent | c76d533e07fe866253cdb4ed581028d7be8289e1 (diff) | |
download | yosys-8720482ebdb4045c04636ea349ccea416bd78d17.tar.gz yosys-8720482ebdb4045c04636ea349ccea416bd78d17.tar.bz2 yosys-8720482ebdb4045c04636ea349ccea416bd78d17.zip |
Add new helper structures to represent memories.
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | kernel/mem.cc | 436 | ||||
-rw-r--r-- | kernel/mem.h | 78 |
3 files changed, 516 insertions, 1 deletions
@@ -586,6 +586,7 @@ $(eval $(call add_include_file,kernel/utils.h)) $(eval $(call add_include_file,kernel/satgen.h)) $(eval $(call add_include_file,kernel/ff.h)) $(eval $(call add_include_file,kernel/ffinit.h)) +$(eval $(call add_include_file,kernel/mem.h)) $(eval $(call add_include_file,libs/ezsat/ezsat.h)) $(eval $(call add_include_file,libs/ezsat/ezminisat.h)) $(eval $(call add_include_file,libs/sha1/sha1.h)) @@ -601,7 +602,7 @@ $(eval $(call add_include_file,backends/cxxrtl/cxxrtl_vcd_capi.cc)) $(eval $(call add_include_file,backends/cxxrtl/cxxrtl_vcd_capi.h)) OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o -OBJS += kernel/cellaigs.o kernel/celledges.o kernel/satgen.o +OBJS += kernel/cellaigs.o kernel/celledges.o kernel/satgen.o kernel/mem.o kernel/log.o: CXXFLAGS += -DYOSYS_SRC='"$(YOSYS_SRC)"' kernel/yosys.o: CXXFLAGS += -DYOSYS_DATDIR='"$(DATDIR)"' -DYOSYS_PROGRAM_PREFIX='"$(PROGRAM_PREFIX)"' diff --git a/kernel/mem.cc b/kernel/mem.cc new file mode 100644 index 000000000..0301a913c --- /dev/null +++ b/kernel/mem.cc @@ -0,0 +1,436 @@ +/* + * 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" + +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<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); + 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); + 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); + } + 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> 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; +} + +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; +} diff --git a/kernel/mem.h b/kernel/mem.h new file mode 100644 index 000000000..6d727e71d --- /dev/null +++ b/kernel/mem.h @@ -0,0 +1,78 @@ +/* + * 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. + * + */ + +#ifndef MEM_H +#define MEM_H + +#include "kernel/yosys.h" + +YOSYS_NAMESPACE_BEGIN + +struct MemRd { + dict<IdString, Const> attributes; + Cell *cell; + bool clk_enable, clk_polarity; + bool transparent; + SigSpec clk, en, addr, data; + MemRd() : cell(nullptr) {} +}; + +struct MemWr { + dict<IdString, Const> attributes; + Cell *cell; + bool clk_enable, clk_polarity; + SigSpec clk, en, addr, data; + MemWr() : cell(nullptr) {} +}; + +struct MemInit { + dict<IdString, Const> attributes; + Cell *cell; + Const addr; + Const data; + MemInit() : cell(nullptr) {} +}; + +struct Mem { + Module *module; + IdString memid; + dict<IdString, Const> attributes; + bool packed; + RTLIL::Memory *mem; + Cell *cell; + int width, start_offset, size; + std::vector<MemInit> inits; + std::vector<MemRd> rd_ports; + std::vector<MemWr> wr_ports; + + void remove(); + void emit(); + void remove_wr_port(int idx); + void remove_rd_port(int idx); + void clear_inits(); + Const get_init_data() const; + static std::vector<Mem> get_all_memories(Module *module); + static std::vector<Mem> get_selected_memories(Module *module); + Cell *extract_rdff(int idx); + Mem(Module *module, IdString memid, int width, int start_offset, int size) : module(module), memid(memid), packed(false), mem(nullptr), cell(nullptr), width(width), start_offset(start_offset), size(size) {} +}; + +YOSYS_NAMESPACE_END + +#endif |