opt_clean: Better memory handling.
authorMarcelina Kościelnicka <mwk@0x04.net>
Thu, 8 Oct 2020 11:33:47 +0000 (13:33 +0200)
committerMarcelina Kościelnicka <mwk@0x04.net>
Thu, 8 Oct 2020 16:05:51 +0000 (18:05 +0200)
Previously, `$memwr` and `$meminit` cells were always preserved (along
with the memory itself).  With this change, they are instead part of the
main cell mark-and-sweep pass: a memory (and its `$meminit` and `$memwr`
cells) is only preserved iff any associated `$memrd` cell needs to be
preserved.

passes/opt/opt_clean.cc
tests/opt/opt_clean_mem.ys [new file with mode: 0644]

index bd9856e81c89f7bd03e861c518c1390af243f446..883374cf6ae682da82ef13bf0b2fe49be5a792cc 100644 (file)
@@ -55,7 +55,7 @@ struct keep_cache_t
                if (!module->get_bool_attribute(ID::keep)) {
                    bool found_keep = false;
                    for (auto cell : module->cells())
-                       if (query(cell, true /* ignore_specify_mem */)) {
+                       if (query(cell, true /* ignore_specify */)) {
                            found_keep = true;
                            break;
                        }
@@ -70,12 +70,12 @@ struct keep_cache_t
                return cache[module];
        }
 
-       bool query(Cell *cell, bool ignore_specify_mem = false)
+       bool query(Cell *cell, bool ignore_specify = false)
        {
                if (cell->type.in(ID($assert), ID($assume), ID($live), ID($fair), ID($cover)))
                        return true;
 
-               if (!ignore_specify_mem && cell->type.in(ID($memwr), ID($meminit), ID($specify2), ID($specify3), ID($specrule)))
+               if (!ignore_specify && cell->type.in(ID($specify2), ID($specify3), ID($specrule)))
                        return true;
 
                if (cell->has_keep_attr())
@@ -95,6 +95,8 @@ int count_rm_cells, count_rm_wires;
 void rmunused_module_cells(Module *module, bool verbose)
 {
        SigMap sigmap(module);
+       dict<IdString, pool<Cell*>> mem2cells;
+       pool<IdString> mem_unused;
        pool<Cell*> queue, unused;
        pool<SigBit> used_raw_bits;
        dict<SigBit, pool<Cell*>> wire2driver;
@@ -108,6 +110,17 @@ void rmunused_module_cells(Module *module, bool verbose)
                }
        }
 
+       for (auto &it : module->memories) {
+               mem_unused.insert(it.first);
+       }
+
+       for (Cell *cell : module->cells()) {
+               if (cell->type.in(ID($memwr), ID($meminit))) {
+                       IdString mem_id = cell->getParam(ID::MEMID).decode_string();
+                       mem2cells[mem_id].insert(cell);
+               }
+       }
+
        for (auto &it : module->cells_) {
                Cell *cell = it.second;
                for (auto &it2 : cell->connections()) {
@@ -145,17 +158,33 @@ void rmunused_module_cells(Module *module, bool verbose)
        while (!queue.empty())
        {
                pool<SigBit> bits;
-               for (auto cell : queue)
-               for (auto &it : cell->connections())
-                       if (!ct_all.cell_known(cell->type) || ct_all.cell_input(cell->type, it.first))
-                               for (auto bit : sigmap(it.second))
-                                       bits.insert(bit);
+               pool<IdString> mems;
+               for (auto cell : queue) {
+                       for (auto &it : cell->connections())
+                               if (!ct_all.cell_known(cell->type) || ct_all.cell_input(cell->type, it.first))
+                                       for (auto bit : sigmap(it.second))
+                                               bits.insert(bit);
+
+                       if (cell->type == ID($memrd)) {
+                               IdString mem_id = cell->getParam(ID::MEMID).decode_string();
+                               if (mem_unused.count(mem_id)) {
+                                       mem_unused.erase(mem_id);
+                                       mems.insert(mem_id);
+                               }
+                       }
+               }
 
                queue.clear();
+
                for (auto bit : bits)
                for (auto c : wire2driver[bit])
                        if (unused.count(c))
                                queue.insert(c), unused.erase(c);
+
+               for (auto mem : mems)
+               for (auto c : mem2cells[mem])
+                       if (unused.count(c))
+                               queue.insert(c), unused.erase(c);
        }
 
        unused.sort(RTLIL::sort_by_name_id<RTLIL::Cell>());
@@ -168,6 +197,14 @@ void rmunused_module_cells(Module *module, bool verbose)
                count_rm_cells++;
        }
 
+       for (auto it : mem_unused)
+       {
+               if (verbose)
+                       log_debug("  removing unused memory `%s'.\n", it.c_str());
+               delete module->memories.at(it);
+               module->memories.erase(it);
+       }
+
        for (auto &it : module->cells_) {
                Cell *cell = it.second;
                for (auto &it2 : cell->connections()) {
diff --git a/tests/opt/opt_clean_mem.ys b/tests/opt/opt_clean_mem.ys
new file mode 100644 (file)
index 0000000..b35b158
--- /dev/null
@@ -0,0 +1,49 @@
+read_verilog <<EOT
+module top(...);
+
+input [7:0] wa;
+input [7:0] ra1;
+input [7:0] ra2;
+input [7:0] wd;
+input clk;
+wire [7:0] rd1;
+wire [7:0] rd2;
+
+reg [7:0] mem[0:7];
+
+always @(posedge clk)
+       mem[wa] <= wd;
+assign rd1 = mem[ra1];
+assign rd2 = mem[ra2];
+
+initial mem[8'h12] = 8'h34;
+
+endmodule
+EOT
+
+proc
+memory_dff
+
+select -assert-count 2 t:$memrd
+select -assert-count 1 t:$memwr
+select -assert-count 1 t:$meminit
+design -save orig
+
+opt_clean
+select -assert-none t:$memrd
+select -assert-none t:$memwr
+select -assert-none t:$meminit
+
+design -load orig
+expose top/rd1
+opt_clean
+select -assert-count 1 t:$memrd
+select -assert-count 1 t:$memwr
+select -assert-count 1 t:$meminit
+
+design -load orig
+expose top/rd1 top/rd2
+opt_clean
+select -assert-count 2 t:$memrd
+select -assert-count 1 t:$memwr
+select -assert-count 1 t:$meminit