kernel/mem: Add read-first semantic emulation code.
authorMarcelina Kościelnicka <mwk@0x04.net>
Thu, 27 Jan 2022 22:26:56 +0000 (23:26 +0100)
committerMarcelina Kościelnicka <mwk@0x04.net>
Fri, 28 Jan 2022 07:48:33 +0000 (08:48 +0100)
kernel/mem.cc
kernel/mem.h

index 92fe1051d65163cd787bd971a3cbaf050a7fa60b..059f8f934612e7431720e29f512015eca3710653 100644 (file)
@@ -1560,3 +1560,107 @@ void Mem::emulate_rd_srst_over_ce(int idx) {
        port.ce_over_srst = true;
        port.en = module->Or(NEW_ID, port.en, port.srst);
 }
+
+bool Mem::emulate_read_first_ok() {
+       if (wr_ports.empty())
+               return false;
+       SigSpec clk = wr_ports[0].clk;
+       bool clk_polarity = wr_ports[0].clk_polarity;
+       for (auto &port: wr_ports) {
+               if (!port.clk_enable)
+                       return false;
+               if (port.clk != clk)
+                       return false;
+               if (port.clk_polarity != clk_polarity)
+                       return false;
+       }
+       bool found_read_first = false;
+       for (auto &port: rd_ports) {
+               if (!port.clk_enable)
+                       return false;
+               if (port.clk != clk)
+                       return false;
+               if (port.clk_polarity != clk_polarity)
+                       return false;
+               // No point doing this operation if there is no read-first relationship
+               // in the first place.
+               for (int j = 0; j < GetSize(wr_ports); j++)
+                       if (!port.transparency_mask[j] && !port.collision_x_mask[j])
+                               found_read_first = true;
+       }
+       return found_read_first;
+}
+
+void Mem::emulate_read_first(FfInitVals *initvals) {
+       log_assert(emulate_read_first_ok());
+       for (int i = 0; i < GetSize(rd_ports); i++)
+               for (int j = 0; j < GetSize(wr_ports); j++)
+                       if (rd_ports[i].transparency_mask[j])
+                               emulate_transparency(j, i, initvals);
+       for (int i = 0; i < GetSize(rd_ports); i++)
+               for (int j = 0; j < GetSize(wr_ports); j++) {
+                       log_assert(!rd_ports[i].transparency_mask[j]);
+                       rd_ports[i].collision_x_mask[j] = false;
+                       rd_ports[i].transparency_mask[j] = true;
+               }
+       for (auto &port: wr_ports) {
+               Wire *new_data = module->addWire(NEW_ID, GetSize(port.data));
+               Wire *new_addr = module->addWire(NEW_ID, GetSize(port.addr));
+               auto compressed = port.compress_en();
+               Wire *new_en = module->addWire(NEW_ID, GetSize(compressed.first));
+               FfData ff_data(module, initvals, NEW_ID);
+               FfData ff_addr(module, initvals, NEW_ID);
+               FfData ff_en(module, initvals, NEW_ID);
+               ff_data.width = GetSize(port.data);
+               ff_data.has_clk = true;
+               ff_data.sig_clk = port.clk;
+               ff_data.pol_clk = port.clk_polarity;
+               ff_data.sig_d = port.data;
+               ff_data.sig_q = new_data;;
+               ff_data.val_init = Const(State::Sx, ff_data.width);
+               ff_data.emit();
+               ff_addr.width = GetSize(port.addr);
+               ff_addr.has_clk = true;
+               ff_addr.sig_clk = port.clk;
+               ff_addr.pol_clk = port.clk_polarity;
+               ff_addr.sig_d = port.addr;
+               ff_addr.sig_q = new_addr;;
+               ff_addr.val_init = Const(State::Sx, ff_addr.width);
+               ff_addr.emit();
+               ff_en.width = GetSize(compressed.first);
+               ff_en.has_clk = true;
+               ff_en.sig_clk = port.clk;
+               ff_en.pol_clk = port.clk_polarity;
+               ff_en.sig_d = compressed.first;
+               ff_en.sig_q = new_en;;
+               ff_en.val_init = Const(State::S0, ff_en.width);
+               ff_en.emit();
+               port.data = new_data;
+               port.addr = new_addr;
+               port.en = port.decompress_en(compressed.second, new_en);
+       }
+}
+
+std::pair<SigSpec, std::vector<int>> MemWr::compress_en() {
+       SigSpec sig = en[0];
+       std::vector<int> swizzle;
+       SigBit prev_bit = en[0];
+       int idx = 0;
+       for (auto &bit: en) {
+               if (bit != prev_bit) {
+                       sig.append(bit);
+                       prev_bit = bit;
+                       idx++;
+               }
+               swizzle.push_back(idx);
+       }
+       log_assert(idx + 1 == GetSize(sig));
+       return {sig, swizzle};
+}
+
+SigSpec MemWr::decompress_en(const std::vector<int> &swizzle, SigSpec sig) {
+       SigSpec res;
+       for (int i: swizzle)
+               res.append(sig[i]);
+       return res;
+}
index 4d0a1d702601e336b659904f893204957964bd84..ae87b1285f79b555003232bdd01bb0960b831694 100644 (file)
@@ -74,6 +74,9 @@ struct MemWr : RTLIL::AttrObject {
                        res[i] = State(sub >> i & 1);
                return res;
        }
+
+       std::pair<SigSpec, std::vector<int>> compress_en();
+       SigSpec decompress_en(const std::vector<int> &swizzle, SigSpec sig);
 };
 
 struct MemInit : RTLIL::AttrObject {
@@ -209,6 +212,15 @@ struct Mem : RTLIL::AttrObject {
        // emulation logic.
        void emulate_rd_srst_over_ce(int idx);
 
+       // Returns true iff emulate_read_first makes sense to call.
+       bool emulate_read_first_ok();
+
+       // Emulates all read-first read-write port relationships in terms of
+       // all-transparent ports, by delaying all write ports by one cycle.
+       // This can only be used when all read ports and all write ports are
+       // in the same clock domain.
+       void emulate_read_first(FfInitVals *initvals);
+
        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) {}
 };