From 52ace35a73839d661163206f123074368f751a09 Mon Sep 17 00:00:00 2001 From: Graham Edgecombe Date: Sat, 9 Dec 2017 21:39:20 +0000 Subject: [PATCH 1/9] Remove trailing comma from SB_IO_OD port list This isn't compatible with Icarus Verilog. --- techlibs/ice40/cells_sim.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/techlibs/ice40/cells_sim.v b/techlibs/ice40/cells_sim.v index b398c1886..2ace597b6 100644 --- a/techlibs/ice40/cells_sim.v +++ b/techlibs/ice40/cells_sim.v @@ -1193,7 +1193,7 @@ module SB_IO_OD ( input DOUT1, input DOUT0, output DIN1, - output DIN0, + output DIN0 ); parameter [5:0] PIN_TYPE = 6'b000000; parameter [0:0] NEG_TRIGGER = 1'b0; From f93e6637aab442e46786af23274b5ac94b63b74f Mon Sep 17 00:00:00 2001 From: Graham Edgecombe Date: Sat, 9 Dec 2017 21:49:07 +0000 Subject: [PATCH 2/9] Fix port names in SB_IO_OD --- techlibs/ice40/cells_sim.v | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/techlibs/ice40/cells_sim.v b/techlibs/ice40/cells_sim.v index 2ace597b6..3be5ed28e 100644 --- a/techlibs/ice40/cells_sim.v +++ b/techlibs/ice40/cells_sim.v @@ -1205,44 +1205,44 @@ module SB_IO_OD ( reg outena_q; generate if (!NEG_TRIGGER) begin - always @(posedge INPUT_CLK) if (CLOCK_ENABLE) din_q_0 <= PACKAGE_PIN; - always @(negedge INPUT_CLK) if (CLOCK_ENABLE) din_q_1 <= PACKAGE_PIN; - always @(posedge OUTPUT_CLK) if (CLOCK_ENABLE) dout_q_0 <= D_OUT_0; - always @(negedge OUTPUT_CLK) if (CLOCK_ENABLE) dout_q_1 <= D_OUT_1; - always @(posedge OUTPUT_CLK) if (CLOCK_ENABLE) outena_q <= OUTPUT_ENABLE; + always @(posedge INPUTCLK) if (CLOCKENABLE) din_q_0 <= PACKAGEPIN; + always @(negedge INPUTCLK) if (CLOCKENABLE) din_q_1 <= PACKAGEPIN; + always @(posedge OUTPUTCLK) if (CLOCKENABLE) dout_q_0 <= DOUT0; + always @(negedge OUTPUTCLK) if (CLOCKENABLE) dout_q_1 <= DOUT1; + always @(posedge OUTPUTCLK) if (CLOCKENABLE) outena_q <= OUTPUTENABLE; end else begin - always @(negedge INPUT_CLK) if (CLOCK_ENABLE) din_q_0 <= PACKAGE_PIN; - always @(posedge INPUT_CLK) if (CLOCK_ENABLE) din_q_1 <= PACKAGE_PIN; - always @(negedge OUTPUT_CLK) if (CLOCK_ENABLE) dout_q_0 <= D_OUT_0; - always @(posedge OUTPUT_CLK) if (CLOCK_ENABLE) dout_q_1 <= D_OUT_1; - always @(negedge OUTPUT_CLK) if (CLOCK_ENABLE) outena_q <= OUTPUT_ENABLE; + always @(negedge INPUTCLK) if (CLOCKENABLE) din_q_0 <= PACKAGEPIN; + always @(posedge INPUTCLK) if (CLOCKENABLE) din_q_1 <= PACKAGEPIN; + always @(negedge OUTPUTCLK) if (CLOCKENABLE) dout_q_0 <= DOUT0; + always @(posedge OUTPUTCLK) if (CLOCKENABLE) dout_q_1 <= DOUT1; + always @(negedge OUTPUTCLK) if (CLOCKENABLE) outena_q <= OUTPUTENABLE; end endgenerate always @* begin - if (!PIN_TYPE[1] || !LATCH_INPUT_VALUE) - din_0 = PIN_TYPE[0] ? PACKAGE_PIN : din_q_0; + if (!PIN_TYPE[1] || !LATCHINPUTVALUE) + din_0 = PIN_TYPE[0] ? PACKAGEPIN : din_q_0; din_1 = din_q_1; end // work around simulation glitches on dout in DDR mode reg outclk_delayed_1; reg outclk_delayed_2; - always @* outclk_delayed_1 <= OUTPUT_CLK; + always @* outclk_delayed_1 <= OUTPUTCLK; always @* outclk_delayed_2 <= outclk_delayed_1; always @* begin if (PIN_TYPE[3]) - dout = PIN_TYPE[2] ? !dout_q_0 : D_OUT_0; + dout = PIN_TYPE[2] ? !dout_q_0 : DOUT0; else dout = (outclk_delayed_2 ^ NEG_TRIGGER) || PIN_TYPE[2] ? dout_q_0 : dout_q_1; end - assign D_IN_0 = din_0, D_IN_1 = din_1; + assign DIN0 = din_0, DIN1 = din_1; generate - if (PIN_TYPE[5:4] == 2'b01) assign PACKAGE_PIN = dout ? 1'bz : 1'b0; - if (PIN_TYPE[5:4] == 2'b10) assign PACKAGE_PIN = OUTPUT_ENABLE ? (dout ? 1'bz : 1'b0) : 1'bz; - if (PIN_TYPE[5:4] == 2'b11) assign PACKAGE_PIN = outena_q ? (dout ? 1'bz : 1'b0) : 1'bz; + if (PIN_TYPE[5:4] == 2'b01) assign PACKAGEPIN = dout ? 1'bz : 1'b0; + if (PIN_TYPE[5:4] == 2'b10) assign PACKAGEPIN = OUTPUTENABLE ? (dout ? 1'bz : 1'b0) : 1'bz; + if (PIN_TYPE[5:4] == 2'b11) assign PACKAGEPIN = outena_q ? (dout ? 1'bz : 1'b0) : 1'bz; endgenerate `endif endmodule From ca2adc30c9f3736ccd9e381bbca43326e1f54a69 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 12 Dec 2017 17:13:27 +0100 Subject: [PATCH 3/9] Add warnings for driver-driver conflicts between FFs (and other cells) and constants --- passes/opt/opt_clean.cc | 9 ++++++++- passes/proc/proc_dff.cc | 5 +++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index 2d2ffa9a1..25d462ada 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -91,9 +91,16 @@ void rmunused_module_cells(Module *module, bool verbose) Cell *cell = it.second; for (auto &it2 : cell->connections()) { if (!ct_all.cell_known(cell->type) || ct_all.cell_output(cell->type, it2.first)) - for (auto bit : sigmap(it2.second)) + for (auto raw_bit : it2.second) { + if (raw_bit.wire == nullptr) + continue; + auto bit = sigmap(raw_bit); + if (bit.wire == nullptr) + log_warning("Driver-driver conflict for %s between cell %s.%s and constant %s in %s: Resolved using constant.\n", + log_signal(raw_bit), log_id(cell), log_id(it2.first), log_signal(bit), log_id(module)); if (bit.wire != nullptr) wire2driver[bit].insert(cell); + } } if (keep_cache.query(cell)) queue.insert(cell); diff --git a/passes/proc/proc_dff.cc b/passes/proc/proc_dff.cc index 98653dc6b..f732baa17 100644 --- a/passes/proc/proc_dff.cc +++ b/passes/proc/proc_dff.cc @@ -322,6 +322,7 @@ void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) } } + SigSpec sig_q = sig; ce.assign_map.apply(insig); ce.assign_map.apply(rstval); ce.assign_map.apply(sig); @@ -350,13 +351,13 @@ void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) else if (!rstval.is_fully_const() && !ce.eval(rstval)) { log_warning("Async reset value `%s' is not constant!\n", log_signal(rstval)); - gen_dffsr(mod, insig, rstval, sig, + gen_dffsr(mod, insig, rstval, sig_q, sync_edge->type == RTLIL::SyncType::STp, sync_level && sync_level->type == RTLIL::SyncType::ST1, sync_edge->signal, sync_level->signal, proc); } else - gen_dff(mod, insig, rstval.as_const(), sig, + gen_dff(mod, insig, rstval.as_const(), sig_q, sync_edge && sync_edge->type == RTLIL::SyncType::STp, sync_level && sync_level->type == RTLIL::SyncType::ST1, sync_edge ? sync_edge->signal : SigSpec(), From 88182e46d7897625f2889bf1e681b7e0e4f7b966 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Wed, 13 Dec 2017 19:14:34 +0100 Subject: [PATCH 4/9] Check for memories in clk2fflogic --- passes/sat/clk2fflogic.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/passes/sat/clk2fflogic.cc b/passes/sat/clk2fflogic.cc index ef6d5dd72..fbd71fe58 100644 --- a/passes/sat/clk2fflogic.cc +++ b/passes/sat/clk2fflogic.cc @@ -72,6 +72,11 @@ struct Clk2fflogicPass : public Pass { for (auto cell : vector(module->selected_cells())) { + if (cell->type.in("$mem")) + { + log_error("Currently there is no support for memories in clk2fflogic. Run memory_map first to convert memories to logic.\n"); + } + if (cell->type.in("$dlatch")) { bool enpol = cell->parameters["\\EN_POLARITY"].as_bool(); From 7350f7692a559fbde534c61e8bcd6ece93d0a3d7 Mon Sep 17 00:00:00 2001 From: Kevin Kiningham Date: Wed, 13 Dec 2017 13:27:52 -0800 Subject: [PATCH 5/9] Use quote includes for yosys.h --- kernel/celltypes.h | 2 +- kernel/cost.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/celltypes.h b/kernel/celltypes.h index 9f775cde7..5218b5363 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -20,7 +20,7 @@ #ifndef CELLTYPES_H #define CELLTYPES_H -#include +#include "kernel/yosys.h" YOSYS_NAMESPACE_BEGIN diff --git a/kernel/cost.h b/kernel/cost.h index 84fd6cd6d..e795b571b 100644 --- a/kernel/cost.h +++ b/kernel/cost.h @@ -20,7 +20,7 @@ #ifndef COST_H #define COST_H -#include +#include "kernel/yosys.h" YOSYS_NAMESPACE_BEGIN From 96ad6888496f4cd34bbf461ce26f97f598bf898c Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 12 Dec 2017 21:48:31 +0100 Subject: [PATCH 6/9] Add SigSpec::is_fully_ones() --- kernel/rtlil.cc | 15 +++++++++++++++ kernel/rtlil.h | 1 + 2 files changed, 16 insertions(+) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 8c3d2962c..7dc7107c1 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -3353,6 +3353,21 @@ bool RTLIL::SigSpec::is_fully_zero() const return true; } +bool RTLIL::SigSpec::is_fully_ones() const +{ + cover("kernel.rtlil.sigspec.is_fully_ones"); + + pack(); + for (auto it = chunks_.begin(); it != chunks_.end(); it++) { + if (it->width > 0 && it->wire != NULL) + return false; + for (size_t i = 0; i < it->data.size(); i++) + if (it->data[i] != RTLIL::State::S1) + return false; + } + return true; +} + bool RTLIL::SigSpec::is_fully_def() const { cover("kernel.rtlil.sigspec.is_fully_def"); diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 6ce9b6748..b33cb53a3 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -704,6 +704,7 @@ public: bool is_fully_const() const; bool is_fully_zero() const; + bool is_fully_ones() const; bool is_fully_def() const; bool is_fully_undef() const; bool has_const() const; From 76afff7ef67265b296455649c9bc6f0196aa5390 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Thu, 14 Dec 2017 02:06:39 +0100 Subject: [PATCH 7/9] Add RTLIL::Const::is_fully_ones() --- kernel/rtlil.cc | 11 +++++++++++ kernel/rtlil.h | 1 + 2 files changed, 12 insertions(+) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 7dc7107c1..3e873054f 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -172,6 +172,17 @@ bool RTLIL::Const::is_fully_zero() const return true; } +bool RTLIL::Const::is_fully_ones() const +{ + cover("kernel.rtlil.const.is_fully_ones"); + + for (auto bit : bits) + if (bit != RTLIL::State::S1) + return false; + + return true; +} + bool RTLIL::Const::is_fully_def() const { cover("kernel.rtlil.const.is_fully_def"); diff --git a/kernel/rtlil.h b/kernel/rtlil.h index b33cb53a3..fc29e1e65 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -480,6 +480,7 @@ struct RTLIL::Const inline const RTLIL::State &operator[](int index) const { return bits.at(index); } bool is_fully_zero() const; + bool is_fully_ones() const; bool is_fully_def() const; bool is_fully_undef() const; From 2625da6440362246205f0c36649d240c4ca0ee0f Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Thu, 14 Dec 2017 02:07:10 +0100 Subject: [PATCH 8/9] Add smt2 back-end support for async write memories --- backends/smt2/smt2.cc | 67 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 14 deletions(-) diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc index 8daa52eb3..5c3ce3753 100644 --- a/backends/smt2/smt2.cc +++ b/backends/smt2/smt2.cc @@ -556,15 +556,28 @@ struct Smt2Worker decls.push_back(stringf("; yosys-smt2-memory %s %d %d %d %d\n", get_id(cell), abits, width, rd_ports, wr_ports)); + bool async_read = false; + if (!cell->getParam("\\WR_CLK_ENABLE").is_fully_ones()) { + if (!cell->getParam("\\WR_CLK_ENABLE").is_fully_zero()) + log_error("Memory %s.%s has mixed clocked/nonclocked write ports. This is not supported by \"write_smt2\".\n", log_id(cell), log_id(module)); + async_read = true; + } + + string memstate; + if (async_read) { + memstate = stringf("%s#%d#final", get_id(module), arrayid); + } else { + memstate = stringf("%s#%d#0", get_id(module), arrayid); + } + if (statebv) { int mem_size = cell->getParam("\\SIZE").as_int(); int mem_offset = cell->getParam("\\OFFSET").as_int(); - makebits(stringf("%s#%d#0", get_id(module), arrayid), width*mem_size, get_id(cell)); - - decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (_ BitVec %d) (|%s#%d#0| state))\n", - get_id(module), get_id(cell), get_id(module), width*mem_size, get_id(module), arrayid)); + makebits(memstate, width*mem_size, get_id(cell)); + decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (_ BitVec %d) (|%s| state))\n", + get_id(module), get_id(cell), get_id(module), width*mem_size, memstate.c_str())); for (int i = 0; i < rd_ports; i++) { @@ -584,9 +597,9 @@ struct Smt2Worker read_expr += "0"; for (int k = 0; k < mem_size; k++) - read_expr = stringf("(ite (= (|%s_m:R%dA %s| state) #b%s) ((_ extract %d %d) (|%s#%d#0| state))\n %s)", + read_expr = stringf("(ite (= (|%s_m:R%dA %s| state) #b%s) ((_ extract %d %d) (|%s| state))\n %s)", get_id(module), i, get_id(cell), Const(k+mem_offset, abits).as_string().c_str(), - width*(k+1)-1, width*k, get_id(module), arrayid, read_expr.c_str()); + width*(k+1)-1, width*k, memstate.c_str(), read_expr.c_str()); decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d)\n %s) ; %s\n", get_id(module), idcounter, get_id(module), width, read_expr.c_str(), log_signal(data_sig))); @@ -600,14 +613,14 @@ struct Smt2Worker else { if (statedt) - dtmembers.push_back(stringf(" (|%s#%d#0| (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", - get_id(module), arrayid, abits, width, get_id(cell))); + dtmembers.push_back(stringf(" (|%s| (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", + memstate.c_str(), abits, width, get_id(cell))); else - decls.push_back(stringf("(declare-fun |%s#%d#0| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", - get_id(module), arrayid, get_id(module), abits, width, get_id(cell))); + decls.push_back(stringf("(declare-fun |%s| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", + memstate.c_str(), get_id(module), abits, width, get_id(cell))); - decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) (|%s#%d#0| state))\n", - get_id(module), get_id(cell), get_id(module), abits, width, get_id(module), arrayid)); + decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) (|%s| state))\n", + get_id(module), get_id(cell), get_id(module), abits, width, memstate.c_str())); for (int i = 0; i < rd_ports; i++) { @@ -622,8 +635,8 @@ struct Smt2Worker decls.push_back(stringf("(define-fun |%s_m:R%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", get_id(module), i, get_id(cell), get_id(module), abits, addr.c_str(), log_signal(addr_sig))); - decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) (select (|%s#%d#0| state) (|%s_m:R%dA %s| state))) ; %s\n", - get_id(module), idcounter, get_id(module), width, get_id(module), arrayid, get_id(module), i, get_id(cell), log_signal(data_sig))); + decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) (select (|%s| state) (|%s_m:R%dA %s| state))) ; %s\n", + get_id(module), idcounter, get_id(module), width, memstate.c_str(), get_id(module), i, get_id(cell), log_signal(data_sig))); decls.push_back(stringf("(define-fun |%s_m:R%dD %s| ((state |%s_s|)) (_ BitVec %d) (|%s#%d| state))\n", get_id(module), i, get_id(cell), get_id(module), width, get_id(module), idcounter)); @@ -869,11 +882,25 @@ struct Smt2Worker int width = cell->getParam("\\WIDTH").as_int(); int wr_ports = cell->getParam("\\WR_PORTS").as_int(); + bool async_read = false; + string initial_memstate, final_memstate; + + if (!cell->getParam("\\WR_CLK_ENABLE").is_fully_ones()) { + log_assert(cell->getParam("\\WR_CLK_ENABLE").is_fully_zero()); + async_read = true; + initial_memstate = stringf("%s#%d#0", get_id(module), arrayid); + final_memstate = stringf("%s#%d#final", get_id(module), arrayid); + } + if (statebv) { int mem_size = cell->getParam("\\SIZE").as_int(); int mem_offset = cell->getParam("\\OFFSET").as_int(); + if (async_read) { + makebits(final_memstate, width*mem_size, get_id(cell)); + } + for (int i = 0; i < wr_ports; i++) { SigSpec addr_sig = cell->getPort("\\WR_ADDR").extract(abits*i, abits); @@ -912,6 +939,15 @@ struct Smt2Worker } else { + if (async_read) { + if (statedt) + dtmembers.push_back(stringf(" (|%s| (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", + initial_memstate.c_str(), abits, width, get_id(cell))); + else + decls.push_back(stringf("(declare-fun |%s| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", + initial_memstate.c_str(), get_id(module), abits, width, get_id(cell))); + } + for (int i = 0; i < wr_ports; i++) { SigSpec addr_sig = cell->getPort("\\WR_ADDR").extract(abits*i, abits); @@ -948,6 +984,9 @@ struct Smt2Worker std::string expr_q = stringf("(|%s#%d#0| next_state)", get_id(module), arrayid); trans.push_back(stringf(" (= %s %s) ; %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell))); + if (async_read) + hier.push_back(stringf(" (= %s (|%s| state)) ; %s\n", expr_d.c_str(), final_memstate.c_str(), get_id(cell))); + Const init_data = cell->getParam("\\INIT"); int memsize = cell->getParam("\\SIZE").as_int(); From 590e6961cb2ff352c2c01d5fcf6307db95d37129 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Thu, 14 Dec 2017 02:07:31 +0100 Subject: [PATCH 9/9] Add clk2fflogic memory support --- passes/sat/clk2fflogic.cc | 78 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/passes/sat/clk2fflogic.cc b/passes/sat/clk2fflogic.cc index fbd71fe58..d334cf7d9 100644 --- a/passes/sat/clk2fflogic.cc +++ b/passes/sat/clk2fflogic.cc @@ -74,7 +74,83 @@ struct Clk2fflogicPass : public Pass { { if (cell->type.in("$mem")) { - log_error("Currently there is no support for memories in clk2fflogic. Run memory_map first to convert memories to logic.\n"); + int abits = cell->getParam("\\ABITS").as_int(); + int width = cell->getParam("\\WIDTH").as_int(); + int rd_ports = cell->getParam("\\RD_PORTS").as_int(); + int wr_ports = cell->getParam("\\WR_PORTS").as_int(); + + for (int i = 0; i < rd_ports; i++) { + if (cell->getParam("\\RD_CLK_ENABLE").extract(i).as_bool()) + log_error("Read port %d of memory %s.%s is clocked. This is not supported by \"clk2fflogic\"! " + "Call \"memory\" with -nordff to avoid this error.\n", i, log_id(cell), log_id(module)); + } + + Const wr_clk_en_param = cell->getParam("\\WR_CLK_ENABLE"); + Const wr_clk_pol_param = cell->getParam("\\WR_CLK_POLARITY"); + + SigSpec wr_clk_port = cell->getPort("\\WR_CLK"); + SigSpec wr_en_port = cell->getPort("\\WR_EN"); + SigSpec wr_addr_port = cell->getPort("\\WR_ADDR"); + SigSpec wr_data_port = cell->getPort("\\WR_DATA"); + + for (int wport = 0; wport < wr_ports; wport++) + { + bool clken = wr_clk_en_param[wport] == State::S1; + bool clkpol = wr_clk_pol_param[wport] == State::S1; + + if (!clken) + continue; + + SigBit clk = wr_clk_port[wport]; + SigSpec en = wr_en_port.extract(wport*width, width); + SigSpec addr = wr_addr_port.extract(wport*abits, abits); + SigSpec data = wr_data_port.extract(wport*width, width); + + log("Modifying write port %d on memory %s.%s: CLK=%s, A=%s, D=%s\n", + wport, log_id(module), log_id(cell), log_signal(clk), + log_signal(addr), log_signal(data)); + + Wire *past_clk = module->addWire(NEW_ID); + past_clk->attributes["\\init"] = clkpol ? State::S1 : State::S0; + module->addFf(NEW_ID, clk, past_clk); + + SigSpec clock_edge_pattern; + + if (clkpol) { + clock_edge_pattern.append_bit(State::S0); + clock_edge_pattern.append_bit(State::S1); + } else { + clock_edge_pattern.append_bit(State::S1); + clock_edge_pattern.append_bit(State::S0); + } + + SigSpec clock_edge = module->Eqx(NEW_ID, {clk, SigSpec(past_clk)}, clock_edge_pattern); + + SigSpec en_q = module->addWire(NEW_ID, GetSize(addr)); + module->addFf(NEW_ID, en, en_q); + + SigSpec addr_q = module->addWire(NEW_ID, GetSize(addr)); + module->addFf(NEW_ID, addr, addr_q); + + SigSpec data_q = module->addWire(NEW_ID, GetSize(data)); + module->addFf(NEW_ID, data, data_q); + + wr_clk_port[wport] = State::S0; + wr_en_port.replace(wport*width, module->Mux(NEW_ID, Const(0, GetSize(en_q)), en_q, clock_edge)); + wr_addr_port.replace(wport*abits, addr_q); + wr_data_port.replace(wport*width, data_q); + + wr_clk_en_param[wport] = State::S0; + wr_clk_pol_param[wport] = State::S0; + } + + cell->setParam("\\WR_CLK_ENABLE", wr_clk_en_param); + cell->setParam("\\WR_CLK_POLARITY", wr_clk_pol_param); + + cell->setPort("\\WR_CLK", wr_clk_port); + cell->setPort("\\WR_EN", wr_en_port); + cell->setPort("\\WR_ADDR", wr_addr_port); + cell->setPort("\\WR_DATA", wr_data_port); } if (cell->type.in("$dlatch"))