kernel/mem: Add emulate_priority helper.
authorMarcelina Kościelnicka <mwk@0x04.net>
Tue, 25 May 2021 00:56:35 +0000 (02:56 +0200)
committerMarcelina Kościelnicka <mwk@0x04.net>
Tue, 25 May 2021 10:25:58 +0000 (12:25 +0200)
kernel/mem.cc
kernel/mem.h

index f2c8dd953b0b2cb204ece34a4e88a58201e7bacb..649515e0c9b66945dc444318cac22b04272719c5 100644 (file)
@@ -599,3 +599,41 @@ void Mem::narrow() {
        std::swap(rd_ports, new_rd_ports);
        std::swap(wr_ports, new_wr_ports);
 }
+
+void Mem::emulate_priority(int idx1, int idx2)
+{
+       auto &port1 = wr_ports[idx1];
+       auto &port2 = wr_ports[idx2];
+       if (!port2.priority_mask[idx1])
+               return;
+       int min_wide_log2 = std::min(port1.wide_log2, port2.wide_log2);
+       int max_wide_log2 = std::max(port1.wide_log2, port2.wide_log2);
+       bool wide1 = port1.wide_log2 > port2.wide_log2;
+       for (int sub = 0; sub < (1 << max_wide_log2); sub += (1 << min_wide_log2)) {
+               SigSpec addr1 = port1.addr;
+               SigSpec addr2 = port2.addr;
+               for (int j = min_wide_log2; j < max_wide_log2; j++)
+                       if (wide1)
+                               addr1[j] = State(sub >> j & 1);
+                       else
+                               addr2[j] = State(sub >> j & 1);
+               SigSpec addr_eq = module->Eq(NEW_ID, addr1, addr2);
+               int ewidth = width << min_wide_log2;
+               int sub1 = wide1 ? sub : 0;
+               int sub2 = wide1 ? 0 : sub;
+               dict<std::pair<SigBit, SigBit>, SigBit> cache;
+               for (int pos = 0; pos < ewidth; pos++) {
+                       SigBit &en1 = port1.en[pos + sub1 * width];
+                       SigBit &en2 = port2.en[pos + sub2 * width];
+                       std::pair<SigBit, SigBit> key(en1, en2);
+                       if (cache.count(key)) {
+                               en1 = cache[key];
+                       } else {
+                               SigBit active2 = module->And(NEW_ID, addr_eq, en2);
+                               SigBit nactive2 = module->Not(NEW_ID, active2);
+                               en1 = cache[key] = module->And(NEW_ID, en1, nactive2);
+                       }
+               }
+       }
+       port2.priority_mask[idx1] = false;
+}
index 214086ac47e649752fb8c621321dfb6e1ef4c532..08befebdbf40d065de66911f4f25ab2ee6cc0d86 100644 (file)
@@ -77,6 +77,12 @@ struct Mem {
        Cell *extract_rdff(int idx, FfInitVals *initvals);
        void narrow();
 
+       // If write port idx2 currently has priority over write port idx1,
+       // inserts extra logic on idx1's enable signal to disable writes
+       // when idx2 is writing to the same address, then removes the priority
+       // from the priority mask.
+       void emulate_priority(int idx1, int idx2);
+
        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) {}
 };