kernel/mem: Add helpers for write port widening.
authorMarcelina Kościelnicka <mwk@0x04.net>
Wed, 26 May 2021 01:07:51 +0000 (03:07 +0200)
committerMarcelina Kościelnicka <mwk@0x04.net>
Thu, 27 May 2021 12:32:51 +0000 (14:32 +0200)
kernel/mem.cc
kernel/mem.h

index a7ee1f2d64320f5343deb4178904a778f4df7156..021e1991e5d22a50111fddb400ae249194161e81 100644 (file)
@@ -798,3 +798,49 @@ void Mem::prepare_wr_merge(int idx1, int idx2) {
                        oport.priority_mask[idx1] = true;
        }
 }
+
+void Mem::widen_prep(int wide_log2) {
+       // Make sure start_offset and size are aligned to the port width,
+       // adjust if necessary.
+       int mask = ((1 << wide_log2) - 1);
+       int delta = start_offset & mask;
+       start_offset -= delta;
+       size += delta;
+       if (size & mask) {
+               size |= mask;
+               size++;
+       }
+}
+
+void Mem::widen_wr_port(int idx, int wide_log2) {
+       widen_prep(wide_log2);
+       auto &port = wr_ports[idx];
+       log_assert(port.wide_log2 <= wide_log2);
+       if (port.wide_log2 < wide_log2) {
+               SigSpec new_data, new_en;
+               SigSpec addr_lo = port.addr.extract(0, wide_log2);
+               for (int sub = 0; sub < (1 << wide_log2); sub += (1 << port.wide_log2))
+               {
+                       Const cur_addr_lo(sub, wide_log2);
+                       if (addr_lo == cur_addr_lo) {
+                               // Always writes to this subword.
+                               new_data.append(port.data);
+                               new_en.append(port.en);
+                       } else if (addr_lo.is_fully_const()) {
+                               // Never writes to this subword.
+                               new_data.append(Const(State::Sx, GetSize(port.data)));
+                               new_en.append(Const(State::S0, GetSize(port.data)));
+                       } else {
+                               // May or may not write to this subword.
+                               new_data.append(port.data);
+                               SigSpec addr_eq = module->Eq(NEW_ID, addr_lo, cur_addr_lo);
+                               SigSpec en = module->Mux(NEW_ID, Const(State::S0, GetSize(port.data)), port.en, addr_eq);
+                               new_en.append(en);
+                       }
+               }
+               port.addr.replace(port.wide_log2, Const(State::S0, wide_log2 - port.wide_log2));
+               port.data = new_data;
+               port.en = new_en;
+               port.wide_log2 = wide_log2;
+       }
+}
index 82eb0f4880ca786eb42367d6cb1fac619db760ce..b4a9cb6954253eaf524820952308f7f8aef336da 100644 (file)
@@ -109,6 +109,17 @@ struct Mem {
        // is called.
        void prepare_wr_merge(int idx1, int idx2);
 
+       // Prepares the memory for widening a port to a given width.  This
+       // involves ensuring that start_offset and size are aligned to the
+       // target width.
+       void widen_prep(int wide_log2);
+
+       // Widens a write port up to a given width.  The newly port is
+       // equivalent to the original, made by replicating enable/data bits
+       // and masking enable bits with decoders on the low part of the
+       // original address.
+       void widen_wr_port(int idx, int wide_log2);
+
        Mem(Module *module, IdString memid, int width, int start_offset, int size) : module(module), memid(memid), packed(false), mem(nullptr), cell(nullptr), width(width), start_offset(start_offset), size(size) {}
 };