Add new helper structures to represent memories.
authorMarcelina Kościelnicka <mwk@0x04.net>
Sat, 17 Oct 2020 20:19:34 +0000 (22:19 +0200)
committerMarcelina Kościelnicka <mwk@0x04.net>
Wed, 21 Oct 2020 15:51:20 +0000 (17:51 +0200)
Makefile
kernel/mem.cc [new file with mode: 0644]
kernel/mem.h [new file with mode: 0644]

index 2a9cf2eb5de255da068f28895d7a8485a1603c5e..9378115c5379f8eeeb1e59d29ab2fc3e058590e5 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -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 (file)
index 0000000..0301a91
--- /dev/null
@@ -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 (file)
index 0000000..6d727e7
--- /dev/null
@@ -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