proc_rom: Add special handling of const-0 address bits.
authorMarcelina Kościelnicka <mwk@0x04.net>
Wed, 18 May 2022 06:18:13 +0000 (08:18 +0200)
committerMarcelina Kościelnicka <mwk@0x04.net>
Wed, 18 May 2022 15:32:30 +0000 (17:32 +0200)
passes/proc/proc_rom.cc
tests/proc/proc_rom.ys

index 4fd611518976ff6a2dab25c32b8e6553427f441b..b83466ce75a2919df96fa1f11aa77b77df9af973 100644 (file)
@@ -46,10 +46,6 @@ struct RomWorker
                        log_debug("rejecting switch: no cases\n");
                        return;
                }
-               if (GetSize(sw->signal) > 30) {
-                       log_debug("rejecting switch: address too wide\n");
-                       return;
-               }
 
                // A switch can be converted into ROM when:
                //
@@ -70,9 +66,15 @@ struct RomWorker
                        }
                }
 
+               int swsigbits = 0;
+               for (int i = 0; i < GetSize(sw->signal); i++)
+                       if (sw->signal[i] != State::S0)
+                               swsigbits = i + 1;
+
                dict<int, Const> vals;
                Const default_val;
                bool got_default = false;
+               int maxaddr = 0;
                for (auto cs : sw->cases) {
                        if (!cs->switches.empty()) {
                                log_debug("rejecting switch: has nested switches\n");
@@ -105,10 +107,21 @@ struct RomWorker
                                        log_debug("rejecting switch: case value has undef bits\n");
                                        return;
                                }
-                               int a = addr.as_int();
+                               Const c = addr.as_const();
+                               while (GetSize(c) && c.bits.back() == State::S0)
+                                       c.bits.pop_back();
+                               if (GetSize(c) > swsigbits)
+                                       continue;
+                               if (GetSize(c) > 30) {
+                                       log_debug("rejecting switch: address too large\n");
+                                       return;
+                               }
+                               int a = c.as_int();
                                if (vals.count(a))
                                        continue;
                                vals[a] = val;
+                               if (a > maxaddr)
+                                       maxaddr = a;
                        }
                        if (cs->compare.empty()) {
                                default_val = val;
@@ -116,8 +129,8 @@ struct RomWorker
                                break;
                        }
                }
-               int total = 1 << GetSize(sw->signal);
-               if (!got_default && GetSize(vals) != total) {
+               int abits = ceil_log2(maxaddr + 1);
+               if (!got_default && (swsigbits > 30 || GetSize(vals) != (1 << swsigbits))) {
                        log_debug("rejecting switch: not all values are covered\n");
                        return;
                }
@@ -127,18 +140,18 @@ struct RomWorker
                        log_debug("rejecting switch: not enough values\n");
                        return;
                }
-               if (total / GetSize(vals) > 4) {
+               if ((1 << abits) / GetSize(vals) > 4) {
                        log_debug("rejecting switch: not enough density\n");
                        return;
                }
 
                // Ok, let's do it.
                SigSpec rdata = module->addWire(NEW_ID, GetSize(lhs));
-               Mem mem(module, NEW_ID, GetSize(lhs), 0, total);
+               Mem mem(module, NEW_ID, GetSize(lhs), 0, 1 << abits);
                mem.attributes = sw->attributes;
 
                Const init_data;
-               for (int i = 0; i < total; i++) {
+               for (int i = 0; i < mem.size; i++) {
                        auto it = vals.find(i);
                        if (it == vals.end()) {
                                log_assert(got_default);
@@ -157,7 +170,7 @@ struct RomWorker
                mem.inits.push_back(std::move(init));
 
                MemRd rd;
-               rd.addr = sw->signal;
+               rd.addr = sw->signal.extract(0, abits);
                rd.data = rdata;
                rd.init_value = Const(State::Sx, GetSize(lhs));
                rd.arst_value = Const(State::Sx, GetSize(lhs));
@@ -168,10 +181,22 @@ struct RomWorker
                for (auto cs: sw->cases)
                        delete cs;
                sw->cases.clear();
-               sw->signal = SigSpec();
-               RTLIL::CaseRule *cs = new RTLIL::CaseRule;
-               cs->actions.push_back(SigSig(lhs, rdata));
-               sw->cases.push_back(cs);
+               sw->signal = sw->signal.extract(0, swsigbits);
+               if (abits == GetSize(sw->signal)) {
+                       sw->signal = SigSpec();
+                       RTLIL::CaseRule *cs = new RTLIL::CaseRule;
+                       cs->actions.push_back(SigSig(lhs, rdata));
+                       sw->cases.push_back(cs);
+               } else {
+                       sw->signal = sw->signal.extract_end(abits);
+                       RTLIL::CaseRule *cs = new RTLIL::CaseRule;
+                       cs->compare.push_back(Const(State::S0, GetSize(sw->signal)));
+                       cs->actions.push_back(SigSig(lhs, rdata));
+                       sw->cases.push_back(cs);
+                       RTLIL::CaseRule *cs2 = new RTLIL::CaseRule;
+                       cs2->actions.push_back(SigSig(lhs, default_val));
+                       sw->cases.push_back(cs2);
+               }
 
                count += 1;
        }
index c854e732fedadbd122f3ee357ce25571eed00055..0ef2e2c61190cdd9aeb64823c3cc15a9d07b8fc1 100644 (file)
@@ -33,6 +33,7 @@ hierarchy -auto-top
 
 design -save orig
 proc
+select -assert-count 1 t:$memrd_v2
 memory
 opt_dff
 design -stash postopt
@@ -41,3 +42,148 @@ proc -norom
 design -stash preopt
 
 equiv_opt -assert -run prepare: dummy
+
+
+
+design -reset
+
+read_verilog << EOT
+
+module top(input [3:0] a, input en, output [7:0] d);
+
+always @*
+       if (en)
+               case(a)
+                       4'h0: d <= 8'h12;
+                       4'h1: d <= 8'h34;
+                       4'h2: d <= 8'h56;
+                       4'h3: d <= 8'h78;
+                       4'h4: d <= 8'h9a;
+                       4'h5: d <= 8'hbc;
+                       4'h6: d <= 8'hde;
+                       4'h7: d <= 8'hff;
+                       4'h8: d <= 8'h61;
+                       4'h9: d <= 8'h49;
+                       4'ha: d <= 8'h36;
+                       4'hb: d <= 8'h81;
+                       4'hc: d <= 8'h8c;
+                       default: d <= 8'h11;
+               endcase
+       else
+               d <= 0;
+
+endmodule
+
+EOT
+
+hierarchy -auto-top
+
+design -save orig
+proc
+select -assert-count 1 t:$memrd_v2
+memory
+opt_dff
+design -stash postopt
+design -load orig
+proc -norom
+design -stash preopt
+
+equiv_opt -assert -run prepare: dummy
+
+
+
+design -reset
+
+read_verilog << EOT
+
+module top(input [31:0] a, input en, output [7:0] d);
+
+always @*
+       if (en)
+               case(a)
+                       0: d <= 8'h12;
+                       1: d <= 8'h34;
+                       2: d <= 8'h56;
+                       3: d <= 8'h78;
+                       4: d <= 8'h9a;
+                       5: d <= 8'hbc;
+                       6: d <= 8'hde;
+                       7: d <= 8'hff;
+                       8: d <= 8'h61;
+                       9: d <= 8'h49;
+                       10: d <= 8'h36;
+                       11: d <= 8'h81;
+                       12: d <= 8'h8c;
+                       default: d <= 8'h11;
+               endcase
+       else
+               d <= 0;
+
+endmodule
+
+EOT
+
+hierarchy -auto-top
+
+design -save orig
+proc
+select -assert-count 1 t:$memrd_v2
+memory
+opt_dff
+design -stash postopt
+design -load orig
+proc -norom
+design -stash preopt
+
+equiv_opt -assert -run prepare: dummy
+
+
+design -reset
+
+read_verilog << EOT
+
+module top(input [3:0] a, input en, output [7:0] d);
+
+always @*
+       if (en)
+               case(a)
+                       'h0: d <= 8'h12;
+                       'h1: d <= 8'h34;
+                       'h2: d <= 8'h56;
+                       'h3: d <= 8'h78;
+                       'h4: d <= 8'h9a;
+                       'h5: d <= 8'hbc;
+                       'h6: d <= 8'hde;
+                       'h7: d <= 8'hff;
+                       'h8: d <= 8'h61;
+                       'h9: d <= 8'h49;
+                       'ha: d <= 8'h36;
+                       'hb: d <= 8'h81;
+                       'hc: d <= 8'h8c;
+                       'hd: d <= 8'ha9;
+                       'he: d <= 8'h99;
+                       'hf: d <= 8'h51;
+               endcase
+       else
+               d <= 0;
+
+endmodule
+
+EOT
+
+hierarchy -auto-top
+
+design -save orig
+proc
+select -assert-count 1 t:$memrd_v2
+memory
+opt_dff
+design -stash postopt
+design -load orig
+proc -norom
+design -stash preopt
+
+equiv_opt -assert -run prepare: dummy
+
+
+