memory: Introduce $meminit_v2 cell, with EN input.
authorMarcelina Kościelnicka <mwk@0x04.net>
Fri, 21 May 2021 00:26:52 +0000 (02:26 +0200)
committerMarcelina Kościelnicka <mwk@0x04.net>
Wed, 28 Jul 2021 21:18:38 +0000 (23:18 +0200)
CHANGELOG
kernel/celltypes.h
kernel/mem.cc
kernel/mem.h
kernel/rtlil.cc
manual/CHAPTER_CellLib.tex
passes/opt/opt_clean.cc
passes/opt/wreduce.cc
passes/sat/sim.cc
techlibs/common/simlib.v

index 6dcd05de665d7cf04ea34bd4d9d1ada680a27aa4..6948ff441aa4f880ce5801a65cf780b702acd4ec 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -70,6 +70,7 @@ Yosys 0.9 .. Yosys 0.9-dev
     - Added "dfflegalize" pass
     - 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)
 
 Yosys 0.8 .. Yosys 0.9
 ----------------------
index 2918b90395d10e7f777212a5e4b7bb528ab4adc6..2ce7978a46392c56a2124d16b5c0d19416718830 100644 (file)
@@ -157,6 +157,7 @@ struct CellTypes
                setup_type(ID($memrd), {ID::CLK, ID::EN, ID::ADDR}, {ID::DATA});
                setup_type(ID($memwr), {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($fsm), {ID::CLK, ID::ARST, ID::CTRL_IN}, {ID::CTRL_OUT});
index e95118d4c66a3e54c885ee1ca85c526f4c26d4c6..a3b244eab8b0d5d804abb38cbb2bfc12fbd9419b 100644 (file)
@@ -263,8 +263,11 @@ void Mem::emit() {
                }
                idx = 0;
                for (auto &init : inits) {
+                       bool v2 = !init.en.is_fully_ones();
                        if (!init.cell)
-                               init.cell = module->addCell(NEW_ID, ID($meminit));
+                               init.cell = module->addCell(NEW_ID, v2 ? ID($meminit_v2) : ID($meminit));
+                       else
+                               init.cell->type = v2 ? ID($meminit_v2) : ID($meminit);
                        init.cell->attributes = init.attributes;
                        init.cell->parameters[ID::MEMID] = memid.str();
                        init.cell->parameters[ID::ABITS] = GetSize(init.addr);
@@ -273,6 +276,10 @@ void Mem::emit() {
                        init.cell->parameters[ID::PRIORITY] = idx++;
                        init.cell->setPort(ID::ADDR, init.addr);
                        init.cell->setPort(ID::DATA, init.data);
+                       if (v2)
+                               init.cell->setPort(ID::EN, init.en);
+                       else
+                               init.cell->unsetPort(ID::EN);
                }
        }
 }
@@ -289,6 +296,14 @@ void Mem::coalesce_inits() {
        for (auto &init : inits) {
                if (init.removed)
                        continue;
+               bool valid = false;
+               for (auto bit : init.en)
+                       if (bit == State::S1)
+                               valid = true;
+               if (!valid) {
+                       init.removed = true;
+                       continue;
+               }
                int addr = init.addr.as_int();
                int addr_e = addr + GetSize(init.data) / width;
                auto it_e = chunks.upper_bound(addr_e);
@@ -335,6 +350,13 @@ void Mem::coalesce_inits() {
                int caddr_e = chunks[caddr];
                auto &chunk_inits = it.second;
                if (GetSize(chunk_inits) == 1) {
+                       auto &init = inits[chunk_inits[0]];
+                       if (!init.en.is_fully_ones()) {
+                               for (int i = 0; i < GetSize(init.data); i++)
+                                       if (init.en[i % width] != State::S1)
+                                               init.data[i] = State::Sx;
+                               init.en = Const(State::S1, width);
+                       }
                        continue;
                }
                Const cdata(State::Sx, (caddr_e - caddr) * width);
@@ -344,12 +366,14 @@ void Mem::coalesce_inits() {
                        log_assert(offset >= 0);
                        log_assert(offset + GetSize(init.data) <= GetSize(cdata));
                        for (int i = 0; i < GetSize(init.data); i++)
-                               cdata.bits[i+offset] = init.data.bits[i];
+                               if (init.en[i % width] == State::S1)
+                                       cdata.bits[i+offset] = init.data.bits[i];
                        init.removed = true;
                }
                MemInit new_init;
                new_init.addr = caddr;
                new_init.data = cdata;
+               new_init.en = Const(State::S1, width);
                inits.push_back(new_init);
        }
 }
@@ -361,7 +385,7 @@ Const Mem::get_init_data() const {
                        continue;
                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))
+                       if (0 <= i+offset && i+offset < GetSize(init_data) && init.en[i % width] == State::S1)
                                init_data.bits[i+offset] = init.data.bits[i];
        }
        return init_data;
@@ -432,7 +456,7 @@ namespace {
                                        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))
+                               else if (cell->type.in(ID($meminit), ID($meminit_v2)))
                                        inits[cell->parameters.at(ID::MEMID).decode_string()].insert(cell);
                        }
                }
@@ -507,6 +531,14 @@ namespace {
                                        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();
+                               if (cell->type == ID($meminit_v2)) {
+                                       auto en = cell->getPort(ID::EN);
+                                       if (!en.is_fully_const())
+                                               log_error("Non-constant enable %s in memory initialization %s.\n", log_signal(en), log_id(cell));
+                                       init.en = en.as_const();
+                               } else {
+                                       init.en = RTLIL::Const(State::S1, mem->width);
+                               }
                                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; });
@@ -558,6 +590,7 @@ namespace {
                                        MemInit minit;
                                        minit.addr = res.start_offset + pos;
                                        minit.data = init.extract(pos * res.width, (epos - pos) * res.width, State::Sx);
+                                       minit.en = RTLIL::Const(State::S1, res.width);
                                        res.inits.push_back(minit);
                                        pos = epos;
                                }
index 62403e00c52090c3ef927bd91e01e5b03440a7c4..24c2d64c8dc69f7ae446932f3f2d47efd2ba15a5 100644 (file)
@@ -69,6 +69,7 @@ struct MemInit : RTLIL::AttrObject {
        Cell *cell;
        Const addr;
        Const data;
+       Const en;
        MemInit() : removed(false), cell(nullptr) {}
 };
 
@@ -101,7 +102,8 @@ struct Mem : RTLIL::AttrObject {
        // address ranges, they are combined into one, with the higher-priority
        // one's data overwriting the other.  Running this results in
        // an inits list equivalent to the original, in which all entries
-       // cover disjoint (and non-touching) address ranges.
+       // cover disjoint (and non-touching) address ranges, and all enable
+       // masks are all-1.
        void coalesce_inits();
 
        // Checks consistency of this memory and all its ports/inits, using
index 21ee15ac548a6cd23ba16ee31d8f204ec0ad2573..bd6b3ad055812d80903269d49b5246b25557176a 100644 (file)
@@ -1414,6 +1414,16 @@ namespace {
                                return;
                        }
 
+                       if (cell->type == ID($meminit_v2)) {
+                               param(ID::MEMID);
+                               param(ID::PRIORITY);
+                               port(ID::ADDR, param(ID::ABITS));
+                               port(ID::DATA, param(ID::WIDTH) * param(ID::WORDS));
+                               port(ID::EN, param(ID::WIDTH));
+                               check_expected();
+                               return;
+                       }
+
                        if (cell->type == ID($mem)) {
                                param(ID::MEMID);
                                param(ID::SIZE);
@@ -3177,7 +3187,7 @@ 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));
+       return type.in(ID($memwr), ID($memrd), ID($meminit), ID($meminit_v2));
 }
 
 bool RTLIL::Cell::is_mem_cell() const
index d4572a88a29bea6a2851b349d1d12b9716f668e8..08901debb1a9d0d7d1c05a1e3c29f9a76cbafc65 100644 (file)
@@ -338,14 +338,14 @@ 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}
+Memories are either represented using RTLIL::Memory objects, {\tt \$memrd}, {\tt \$memwr}, and {\tt \$meminit\_v2}
 cells, or by {\tt \$mem} 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)
 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} cells,
+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
@@ -401,8 +401,9 @@ edge if this parameter is {\tt 1'b0}.
 The cell with the higher integer value in this parameter wins a write conflict.
 \end{itemize}
 
-The {\tt \$meminit} cells have an address input \B{ADDR} and a data input \B{DATA}, with the width
-of the \B{DATA} port equal to \B{WIDTH} parameter times \B{WORDS} parameter. Both of the inputs
+The {\tt \$meminit\_v2} cells have an address input \B{ADDR}, a data input \B{DATA}, with the width
+of the \B{DATA} port equal to \B{WIDTH} parameter times \B{WORDS} parameter, and a bit enable mask input
+\B{EN} with width equal to \B{WIDTH} parameter. All three of the inputs
 must resolve to a constant for synthesis to succeed.
 
 \begin{itemize}
@@ -497,7 +498,7 @@ 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} cells
+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\_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.
index 699fd4f80ca0278ad49f61687d6e2b633d05f9e1..c3a0928ef3b17b73dffde9d71cbdb4d106638c45 100644 (file)
@@ -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))) {
+               if (cell->type.in(ID($memwr), ID($meminit), ID($meminit_v2))) {
                        IdString mem_id = cell->getParam(ID::MEMID).decode_string();
                        mem2cells[mem_id].insert(cell);
                }
index 0cdabcc1a881a4941c1e6a3744a79ba456dc48af..f6bf8b51a79abdc19cc837ea0608f58aad277ec9 100644 (file)
@@ -558,7 +558,7 @@ struct WreducePass : public Pass {
                                        }
                                }
 
-                               if (!opt_memx && c->type.in(ID($memrd), ID($memwr), ID($meminit))) {
+                               if (!opt_memx && c->type.in(ID($memrd), ID($memwr), 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) {
index 630e1aaa1ac5971169babfdc4d40ff66c532a9b2..4e158da62922e29ac142dbf8db4c44e9341d7f84 100644 (file)
@@ -559,6 +559,7 @@ struct SimInstance
                        MemInit minit;
                        minit.addr = mem.mem->start_offset;
                        minit.data = mem.data;
+                       minit.en = Const(State::S1, mem.mem->width);
                        mem.mem->inits.push_back(minit);
                        mem.mem->emit();
                }
index 42a355c2dd8e6d2a1953b2b01aa8dd9ea16d295d..ad654c8a43bdb69878ad8c701ce7d19e7071aadd 100644 (file)
@@ -2233,6 +2233,30 @@ endmodule
 
 // --------------------------------------------------------
 
+module \$meminit_v2 (ADDR, DATA, EN);
+
+parameter MEMID = "";
+parameter ABITS = 8;
+parameter WIDTH = 8;
+parameter WORDS = 1;
+
+parameter PRIORITY = 0;
+
+input [ABITS-1:0] ADDR;
+input [WORDS*WIDTH-1:0] DATA;
+input [WIDTH-1:0] EN;
+
+initial begin
+       if (MEMID != "") begin
+               $display("ERROR: Found non-simulatable instance of $meminit_v2!");
+               $finish;
+       end
+end
+
+endmodule
+
+// --------------------------------------------------------
+
 module \$mem (RD_CLK, RD_EN, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA);
 
 parameter MEMID = "";