opt_mem: Remove constant-value bit lanes.
authorMarcelina Kościelnicka <mwk@0x04.net>
Fri, 6 May 2022 21:29:16 +0000 (23:29 +0200)
committerMarcelina Kościelnicka <mwk@0x04.net>
Sat, 7 May 2022 21:13:16 +0000 (23:13 +0200)
passes/opt/opt_mem.cc
tests/techmap/.gitignore
tests/techmap/mem_simple_4x1_runtest.sh

index edadf2c7b114b2e4656ebb2469d659f880c36afd..885b6f97d8366ef23f45a1231f9ee333fafe5a12 100644 (file)
@@ -20,6 +20,7 @@
 #include "kernel/yosys.h"
 #include "kernel/sigtools.h"
 #include "kernel/mem.h"
+#include "kernel/ff.h"
 
 USING_YOSYS_NAMESPACE
 PRIVATE_NAMESPACE_BEGIN
@@ -54,31 +55,160 @@ struct OptMemPass : public Pass {
                        SigMap sigmap(module);
                        FfInitVals initvals(&sigmap, module);
                        for (auto &mem : Mem::get_selected_memories(module)) {
+                               std::vector<bool> always_0(mem.width, true);
+                               std::vector<bool> always_1(mem.width, true);
                                bool changed = false;
                                for (auto &port : mem.wr_ports) {
                                        if (port.en.is_fully_zero()) {
                                                port.removed = true;
                                                changed = true;
                                                total_count++;
+                                       } else {
+                                               for (int sub = 0; sub < (1 << port.wide_log2); sub++) {
+                                                       for (int i = 0; i < mem.width; i++) {
+                                                               int bit = sub * mem.width + i;
+                                                               if (port.en[bit] != State::S0) {
+                                                                       if (port.data[bit] != State::Sx && port.data[bit] != State::S0) {
+                                                                               always_0[i] = false;
+                                                                       }
+                                                                       if (port.data[bit] != State::Sx && port.data[bit] != State::S1) {
+                                                                               always_1[i] = false;
+                                                                       }
+                                                               } else {
+                                                                       if (port.data[bit] != State::Sx) {
+                                                                               port.data[bit] = State::Sx;
+                                                                               changed = true;
+                                                                               total_count++;
+                                                                       }
+                                                               }
+                                                       }
+                                               }
                                        }
                                }
-                               if (changed) {
-                                       mem.emit();
+                               for (auto &init : mem.inits) {
+                                       for (int i = 0; i < GetSize(init.data); i++) {
+                                               State bit = init.data.bits[i];
+                                               int lane = i % mem.width;
+                                               if (bit != State::Sx && bit != State::S0) {
+                                                       always_0[lane] = false;
+                                               }
+                                               if (bit != State::Sx && bit != State::S1) {
+                                                       always_1[lane] = false;
+                                               }
+                                       }
                                }
-
-                               if (mem.wr_ports.empty() && mem.inits.empty()) {
-                                       // The whole memory array will contain
-                                       // only State::Sx, but the embedded read
-                                       // registers could have reset or init values.
-                                       // They will probably be optimized away by
-                                       // opt_dff later.
-                                       for (int i = 0; i < GetSize(mem.rd_ports); i++) {
-                                               mem.extract_rdff(i, &initvals);
-                                               auto &port = mem.rd_ports[i];
-                                               module->connect(port.data, Const(State::Sx, GetSize(port.data)));
+                               std::vector<int> swizzle;
+                               for (int i = 0; i < mem.width; i++) {
+                                       if (!always_0[i] && !always_1[i]) {
+                                               swizzle.push_back(i);
+                                               continue;
                                        }
+                                       State bit;
+                                       if (!always_0[i]) {
+                                               log("%s.%s: removing const-1 lane %d\n", log_id(module->name), log_id(mem.memid), i);
+                                               bit = State::S1;
+                                       } else if (!always_1[i]) {
+                                               log("%s.%s: removing const-0 lane %d\n", log_id(module->name), log_id(mem.memid), i);
+                                               bit = State::S0;
+                                       } else {
+                                               log("%s.%s: removing const-x lane %d\n", log_id(module->name), log_id(mem.memid), i);
+                                               bit = State::Sx;
+                                       }
+                                       // Reconnect read port data.
+                                       for (auto &port: mem.rd_ports) {
+                                               for (int sub = 0; sub < (1 << port.wide_log2); sub++) {
+                                                       int bidx = sub * mem.width + i;
+                                                       if (!port.clk_enable) {
+                                                               module->connect(port.data[bidx], bit);
+                                                       } else {
+                                                               // The FF will most likely be redundant, but it's up to opt_dff to deal with this.
+                                                               FfData ff(module, &initvals, NEW_ID);
+                                                               ff.width = 1;
+                                                               ff.has_clk = true;
+                                                               ff.sig_clk = port.clk;
+                                                               ff.pol_clk = port.clk_polarity;
+                                                               if (port.en != State::S1) {
+                                                                       ff.has_ce = true;
+                                                                       ff.pol_ce = true;
+                                                                       ff.sig_ce = port.en;
+                                                               }
+                                                               if (port.arst != State::S0) {
+                                                                       ff.has_arst = true;
+                                                                       ff.pol_arst = true;
+                                                                       ff.sig_arst = port.arst;
+                                                                       ff.val_arst = port.arst_value[bidx];
+                                                               }
+                                                               if (port.srst != State::S0) {
+                                                                       ff.has_srst = true;
+                                                                       ff.pol_srst = true;
+                                                                       ff.sig_srst = port.srst;
+                                                                       ff.val_srst = port.srst_value[bidx];
+                                                               }
+                                                               ff.sig_d = bit;
+                                                               ff.sig_q = port.data[bidx];
+                                                               ff.val_init = port.init_value[bidx];
+                                                               ff.emit();
+                                                       }
+                                               }
+                                       }
+                               }
+                               if (GetSize(swizzle) == 0) {
                                        mem.remove();
                                        total_count++;
+                                       continue;
+                               }
+                               if (GetSize(swizzle) != mem.width) {
+                                       for (auto &port: mem.wr_ports) {
+                                               SigSpec new_data;
+                                               SigSpec new_en;
+                                               for (int sub = 0; sub < (1 << port.wide_log2); sub++) {
+                                                       for (auto i: swizzle) {
+                                                               new_data.append(port.data[sub * mem.width + i]);
+                                                               new_en.append(port.en[sub * mem.width + i]);
+                                                       }
+                                               }
+                                               port.data = new_data;
+                                               port.en = new_en;
+                                       }
+                                       for (auto &port: mem.rd_ports) {
+                                               SigSpec new_data;
+                                               Const new_init;
+                                               Const new_arst;
+                                               Const new_srst;
+                                               for (int sub = 0; sub < (1 << port.wide_log2); sub++) {
+                                                       for (auto i: swizzle) {
+                                                               int bidx = sub * mem.width + i;
+                                                               new_data.append(port.data[bidx]);
+                                                               new_init.bits.push_back(port.init_value.bits[bidx]);
+                                                               new_arst.bits.push_back(port.arst_value.bits[bidx]);
+                                                               new_srst.bits.push_back(port.srst_value.bits[bidx]);
+                                                       }
+                                               }
+                                               port.data = new_data;
+                                               port.init_value = new_init;
+                                               port.arst_value = new_arst;
+                                               port.srst_value = new_srst;
+                                       }
+                                       for (auto &init: mem.inits) {
+                                               Const new_data;
+                                               Const new_en;
+                                               for (int s = 0; s < GetSize(init.data); s += mem.width) {
+                                                       for (auto i: swizzle) {
+                                                               new_data.bits.push_back(init.data.bits[s + i]);
+                                                       }
+                                               }
+                                               for (auto i: swizzle) {
+                                                       new_en.bits.push_back(init.en.bits[i]);
+                                               }
+                                               init.data = new_data;
+                                               init.en = new_en;
+                                       }
+                                       mem.width = GetSize(swizzle);
+                                       changed = true;
+                                       total_count++;
+                               }
+                               if (changed) {
+                                       mem.emit();
                                }
                        }
                }
index cfed22fc56c7465cbe88a40659c3808ce7e96c2d..56c9ba8f9acb71c0d0e21018f3def83ba21a6326 100644 (file)
@@ -1,2 +1,3 @@
 *.log
+*.out
 /*.mk
index b486de5c703228925f5ccb9feb9f48017147be87..5b5838b9da4cf570b74f652a6cac571bb7a864e1 100644 (file)
@@ -1,17 +1,3 @@
 #!/bin/bash
 
-set -e
-
-../../yosys -b 'verilog -noattr' -o mem_simple_4x1_synth.v -p 'read_verilog mem_simple_4x1_uut.v; proc; opt; memory -nomap; techmap -map mem_simple_4x1_map.v;; techmap; opt; abc;; stat'
-
-iverilog -o mem_simple_4x1_gold_tb mem_simple_4x1_tb.v mem_simple_4x1_uut.v
-iverilog -o mem_simple_4x1_gate_tb mem_simple_4x1_tb.v mem_simple_4x1_synth.v mem_simple_4x1_cells.v
-
-./mem_simple_4x1_gold_tb > mem_simple_4x1_gold_tb.out
-./mem_simple_4x1_gate_tb > mem_simple_4x1_gate_tb.out
-
-diff -u mem_simple_4x1_gold_tb.out mem_simple_4x1_gate_tb.out
-rm -f mem_simple_4x1_synth.v mem_simple_4x1_tb.vcd
-rm -f mem_simple_4x1_{gold,gate}_tb{,.out}
-: OK
-
+exec ../tools/autotest.sh -G -j $@ -p 'proc; opt; memory -nomap; techmap -map ../mem_simple_4x1_map.v;; techmap; opt; abc;; stat' mem_simple_4x1_uut.v