From 5e4c6915c96f21c4cf647bcdf5687f431a7df9ed Mon Sep 17 00:00:00 2001 From: =?utf8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Thu, 27 Jan 2022 16:08:33 +0100 Subject: [PATCH] kernel/mem: Add functions to emulate read port enable/init/reset signals. --- kernel/mem.cc | 208 ++++++++++++++++++++++++++++++++++++++++++++++++++ kernel/mem.h | 18 +++++ 2 files changed, 226 insertions(+) diff --git a/kernel/mem.cc b/kernel/mem.cc index 96168ff76..92fe1051d 100644 --- a/kernel/mem.cc +++ b/kernel/mem.cc @@ -1352,3 +1352,211 @@ void Mem::widen_wr_port(int idx, int wide_log2) { port.wide_log2 = wide_log2; } } + +void Mem::emulate_rden(int idx, FfInitVals *initvals) { + auto &port = rd_ports[idx]; + log_assert(port.clk_enable); + emulate_rd_ce_over_srst(idx); + Wire *new_data = module->addWire(NEW_ID, GetSize(port.data)); + Wire *prev_data = module->addWire(NEW_ID, GetSize(port.data)); + Wire *sel = module->addWire(NEW_ID); + FfData ff_sel(module, initvals, NEW_ID); + FfData ff_data(module, initvals, NEW_ID); + ff_sel.width = 1; + ff_sel.has_clk = true; + ff_sel.sig_clk = port.clk; + ff_sel.pol_clk = port.clk_polarity; + ff_sel.sig_d = port.en; + ff_sel.sig_q = sel; + 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 = prev_data; + if (!port.init_value.is_fully_undef()) { + ff_sel.val_init = State::S0; + ff_data.val_init = port.init_value; + port.init_value = Const(State::Sx, GetSize(port.data)); + } else { + ff_sel.val_init = State::Sx; + ff_data.val_init = Const(State::Sx, GetSize(port.data)); + } + if (port.arst != State::S0) { + ff_sel.has_arst = true; + ff_sel.val_arst = State::S0; + ff_sel.sig_arst = port.arst; + ff_sel.pol_arst = true; + ff_data.has_arst = true; + ff_data.val_arst = port.arst_value; + ff_data.sig_arst = port.arst; + ff_data.pol_arst = true; + port.arst = State::S0; + } + if (port.srst != State::S0) { + log_assert(!port.ce_over_srst); + ff_sel.has_srst = true; + ff_sel.val_srst = State::S0; + ff_sel.sig_srst = port.srst; + ff_sel.pol_srst = true; + ff_sel.ce_over_srst = false; + ff_data.has_srst = true; + ff_data.val_srst = port.srst_value; + ff_data.sig_srst = port.srst; + ff_data.pol_srst = true; + ff_data.ce_over_srst = false; + port.srst = State::S0; + } + ff_sel.emit(); + ff_data.emit(); + module->addMux(NEW_ID, prev_data, new_data, sel, port.data); + port.data = new_data; + port.en = State::S1; +} + +void Mem::emulate_reset(int idx, bool emu_init, bool emu_arst, bool emu_srst, FfInitVals *initvals) { + auto &port = rd_ports[idx]; + if (emu_init && !port.init_value.is_fully_undef()) { + Wire *sel = module->addWire(NEW_ID); + FfData ff_sel(module, initvals, NEW_ID); + Wire *new_data = module->addWire(NEW_ID, GetSize(port.data)); + ff_sel.width = 1; + ff_sel.has_clk = true; + ff_sel.sig_clk = port.clk; + ff_sel.pol_clk = port.clk_polarity; + ff_sel.sig_d = State::S1; + ff_sel.sig_q = sel; + ff_sel.val_init = State::S0; + if (port.en != State::S1) { + ff_sel.has_ce = true; + ff_sel.sig_ce = port.en; + ff_sel.pol_ce = true; + ff_sel.ce_over_srst = port.ce_over_srst; + } + if (port.arst != State::S0) { + ff_sel.has_arst = true; + ff_sel.sig_arst = port.arst; + ff_sel.pol_arst = true; + if (emu_arst && port.arst_value == port.init_value) { + // If we're going to emulate async reset anyway, and the reset + // value is the same as init value, reuse the same mux. + ff_sel.val_arst = State::S0; + port.arst = State::S0; + } else { + ff_sel.val_arst = State::S1; + } + } + if (port.srst != State::S0) { + ff_sel.has_srst = true; + ff_sel.sig_srst = port.srst; + ff_sel.pol_srst = true; + if (emu_srst && port.srst_value == port.init_value) { + ff_sel.val_srst = State::S0; + port.srst = State::S0; + } else { + ff_sel.val_srst = State::S1; + } + } + ff_sel.emit(); + module->addMux(NEW_ID, port.init_value, new_data, sel, port.data); + port.data = new_data; + port.init_value = Const(State::Sx, GetSize(port.data)); + } + if (emu_arst && port.arst != State::S0) { + Wire *sel = module->addWire(NEW_ID); + FfData ff_sel(module, initvals, NEW_ID); + Wire *new_data = module->addWire(NEW_ID, GetSize(port.data)); + ff_sel.width = 1; + ff_sel.has_clk = true; + ff_sel.sig_clk = port.clk; + ff_sel.pol_clk = port.clk_polarity; + ff_sel.sig_d = State::S1; + ff_sel.sig_q = sel; + if (port.init_value.is_fully_undef()) + ff_sel.val_init = State::Sx; + else + ff_sel.val_init = State::S1; + if (port.en != State::S1) { + ff_sel.has_ce = true; + ff_sel.sig_ce = port.en; + ff_sel.pol_ce = true; + ff_sel.ce_over_srst = port.ce_over_srst; + } + ff_sel.has_arst = true; + ff_sel.sig_arst = port.arst; + ff_sel.pol_arst = true; + ff_sel.val_arst = State::S0; + if (port.srst != State::S0) { + ff_sel.has_srst = true; + ff_sel.sig_srst = port.srst; + ff_sel.pol_srst = true; + if (emu_srst && port.srst_value == port.arst_value) { + ff_sel.val_srst = State::S0; + port.srst = State::S0; + } else { + ff_sel.val_srst = State::S1; + } + } + ff_sel.emit(); + module->addMux(NEW_ID, port.arst_value, new_data, sel, port.data); + port.data = new_data; + port.arst = State::S0; + } + if (emu_srst && port.srst != State::S0) { + Wire *sel = module->addWire(NEW_ID); + FfData ff_sel(module, initvals, NEW_ID); + Wire *new_data = module->addWire(NEW_ID, GetSize(port.data)); + ff_sel.width = 1; + ff_sel.has_clk = true; + ff_sel.sig_clk = port.clk; + ff_sel.pol_clk = port.clk_polarity; + ff_sel.sig_d = State::S1; + ff_sel.sig_q = sel; + if (port.init_value.is_fully_undef()) + ff_sel.val_init = State::Sx; + else + ff_sel.val_init = State::S1; + if (port.en != State::S1) { + ff_sel.has_ce = true; + ff_sel.sig_ce = port.en; + ff_sel.pol_ce = true; + ff_sel.ce_over_srst = port.ce_over_srst; + } + ff_sel.has_srst = true; + ff_sel.sig_srst = port.srst; + ff_sel.pol_srst = true; + ff_sel.val_srst = State::S0; + if (port.arst != State::S0) { + ff_sel.has_arst = true; + ff_sel.sig_arst = port.arst; + ff_sel.pol_arst = true; + ff_sel.val_arst = State::S1; + } + ff_sel.emit(); + module->addMux(NEW_ID, port.srst_value, new_data, sel, port.data); + port.data = new_data; + port.srst = State::S0; + } +} + +void Mem::emulate_rd_ce_over_srst(int idx) { + auto &port = rd_ports[idx]; + log_assert(port.clk_enable); + if (port.en == State::S1 || port.srst == State::S0 || !port.ce_over_srst) { + port.ce_over_srst = false; + return; + } + port.ce_over_srst = false; + port.srst = module->And(NEW_ID, port.en, port.srst); +} + +void Mem::emulate_rd_srst_over_ce(int idx) { + auto &port = rd_ports[idx]; + log_assert(port.clk_enable); + if (port.en == State::S1 || port.srst == State::S0 || port.ce_over_srst) { + port.ce_over_srst = true; + return; + } + port.ce_over_srst = true; + port.en = module->Or(NEW_ID, port.en, port.srst); +} diff --git a/kernel/mem.h b/kernel/mem.h index 87a148beb..4d0a1d702 100644 --- a/kernel/mem.h +++ b/kernel/mem.h @@ -191,6 +191,24 @@ struct Mem : RTLIL::AttrObject { // original address. void widen_wr_port(int idx, int wide_log2); + // Emulates a sync read port's enable functionality in soft logic, + // changing the actual read port's enable to be always-on. + void emulate_rden(int idx, FfInitVals *initvals); + + // Emulates a sync read port's initial/reset value functionality in + // soft logic, removing it from the actual read port. + void emulate_reset(int idx, bool emu_init, bool emu_arst, bool emu_srst, FfInitVals *initvals); + + // Given a read port with ce_over_srst set, converts it to a port + // with ce_over_srst unset without changing its behavior by adding + // emulation logic. + void emulate_rd_ce_over_srst(int idx); + + // Given a read port with ce_over_srst unset, converts it to a port + // with ce_over_srst set without changing its behavior by adding + // emulation logic. + void emulate_rd_srst_over_ce(int idx); + 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) {} }; -- 2.30.2