opt_mem: Remove constant-value bit lanes.

This commit is contained in:
Marcelina Kościelnicka 2022-05-06 23:29:16 +02:00
parent 048170d376
commit 77b1dfd8c3
3 changed files with 147 additions and 30 deletions

View File

@ -20,6 +20,7 @@
#include "kernel/yosys.h" #include "kernel/yosys.h"
#include "kernel/sigtools.h" #include "kernel/sigtools.h"
#include "kernel/mem.h" #include "kernel/mem.h"
#include "kernel/ff.h"
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN PRIVATE_NAMESPACE_BEGIN
@ -54,32 +55,161 @@ struct OptMemPass : public Pass {
SigMap sigmap(module); SigMap sigmap(module);
FfInitVals initvals(&sigmap, module); FfInitVals initvals(&sigmap, module);
for (auto &mem : Mem::get_selected_memories(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; bool changed = false;
for (auto &port : mem.wr_ports) { for (auto &port : mem.wr_ports) {
if (port.en.is_fully_zero()) { if (port.en.is_fully_zero()) {
port.removed = true; port.removed = true;
changed = true; changed = true;
total_count++; 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<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) { if (changed) {
mem.emit(); 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++;
}
} }
} }

View File

@ -1,2 +1,3 @@
*.log *.log
*.out
/*.mk /*.mk

View File

@ -1,17 +1,3 @@
#!/bin/bash #!/bin/bash
set -e 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
../../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