diff --git a/passes/opt/opt_mem.cc b/passes/opt/opt_mem.cc index edadf2c7b..885b6f97d 100644 --- a/passes/opt/opt_mem.cc +++ b/passes/opt/opt_mem.cc @@ -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,32 +55,161 @@ struct OptMemPass : public Pass { SigMap sigmap(module); FfInitVals initvals(&sigmap, module); for (auto &mem : Mem::get_selected_memories(module)) { + std::vector always_0(mem.width, true); + std::vector 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++; + } + } + } + } } } + 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; + } + } + } + std::vector 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(); } - - 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))); - } - mem.remove(); - total_count++; - } } } diff --git a/tests/techmap/.gitignore b/tests/techmap/.gitignore index cfed22fc5..56c9ba8f9 100644 --- a/tests/techmap/.gitignore +++ b/tests/techmap/.gitignore @@ -1,2 +1,3 @@ *.log +*.out /*.mk diff --git a/tests/techmap/mem_simple_4x1_runtest.sh b/tests/techmap/mem_simple_4x1_runtest.sh index b486de5c7..5b5838b9d 100644 --- a/tests/techmap/mem_simple_4x1_runtest.sh +++ b/tests/techmap/mem_simple_4x1_runtest.sh @@ -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