From 5df41d4b94f346f598629d66849c2c7ec0e0e12d Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 13 Aug 2024 20:53:47 -0700 Subject: [PATCH] lib: use DelayLine in ECP5_RAM Cleans up the code nicely, and still produces the correct logic. --- experiments/primitive_ram/Top.bsv | 4 +- lib/DelayLine.bsv | 1 + lib/ECP5_RAM.bsv | 101 ++++++++++++++++-------------- tasks.py | 4 +- 4 files changed, 59 insertions(+), 51 deletions(-) diff --git a/experiments/primitive_ram/Top.bsv b/experiments/primitive_ram/Top.bsv index 8af2445..bda0029 100644 --- a/experiments/primitive_ram/Top.bsv +++ b/experiments/primitive_ram/Top.bsv @@ -5,8 +5,8 @@ import ECP5_RAM::*; //(* always_enabled *) interface Top; interface EBRPort#(Bit#(12), Bit#(4)) ram1; - //interface EBRPort#(Bit#(14), Bit#(1)) ram2; - interface EBRPort#(void, void) ram2; + interface EBRPort#(Bit#(14), Bit#(1)) ram2; + //interface EBRPort#(void, void) ram2; endinterface (* synthesize *) diff --git a/lib/DelayLine.bsv b/lib/DelayLine.bsv index 302c556..5ec4afe 100644 --- a/lib/DelayLine.bsv +++ b/lib/DelayLine.bsv @@ -14,6 +14,7 @@ import List::*; // implicit condition, ready() can poll the delay line's output // without blocking. interface DelayLine#(type value); + (* always_ready *) method Action _write (value v); method value _read(); (* always_ready *) diff --git a/lib/ECP5_RAM.bsv b/lib/ECP5_RAM.bsv index c9ff54c..1390360 100644 --- a/lib/ECP5_RAM.bsv +++ b/lib/ECP5_RAM.bsv @@ -6,6 +6,8 @@ import Printf::*; import ToString::*; import StmtFSM::*; +import DelayLine::*; + export EBRWriteMode(..); export EBRPortConfig(..); export EBRPort(..); @@ -494,62 +496,65 @@ module mkEBR#(EBRPortConfig cfgA, let mem <- mkEBRCore(cfgA, cfgB); - WriteOnly#(Bool) portA_start_op = ?; - ReadOnly#(Bool) portA_op_complete = ?; - WriteOnly#(Bool) portB_start_op = ?; - ReadOnly#(Bool) portB_op_complete = ?; + DelayLine#(void) latencyA <- mkDelayLine(rcfgA.operation_latency, clocked_by(rcfgA.clk), reset_by(rcfgA.rstN)); + DelayLine#(void) latencyB <- mkDelayLine(rcfgB.operation_latency, clocked_by(rcfgB.clk), reset_by(rcfgB.rstN)); - // TODO: this variable-depth register chain should be pulled into a - // separate "delay line" module. - if (!rcfgA.enabled) begin - portA_start_op = discardingWriteOnly; - portA_op_complete = constToReadOnly(False); - end - else if (rcfgA.register_output) begin - let syncA1 <- mkDReg(False, clocked_by(rcfgA.clk), reset_by(rcfgA.rstN)); - let syncA2 <- mkReg(False, clocked_by(rcfgA.clk), reset_by(rcfgA.rstN)); - portA_start_op = regToWriteOnly(syncA1); - portA_op_complete = regToReadOnly(syncA2); + // WriteOnly#(Bool) portA_start_op = ?; + // ReadOnly#(Bool) portA_op_complete = ?; + // WriteOnly#(Bool) portB_start_op = ?; + // ReadOnly#(Bool) portB_op_complete = ?; - (* no_implicit_conditions, fire_when_enabled *) - rule syncA1_to_syncA2; - syncA2 <= syncA1; - endrule - end - else begin - let syncA <- mkDReg(False, clocked_by(rcfgA.clk), reset_by(rcfgA.rstN)); - portA_start_op = regToWriteOnly(syncA); - portA_op_complete = regToReadOnly(syncA); - end + // // TODO: this variable-depth register chain should be pulled into a + // // separate "delay line" module. + // if (!rcfgA.enabled) begin + // portA_start_op = discardingWriteOnly; + // portA_op_complete = constToReadOnly(False); + // end + // else if (rcfgA.register_output) begin + // let syncA1 <- mkDReg(False, clocked_by(rcfgA.clk), reset_by(rcfgA.rstN)); + // let syncA2 <- mkReg(False, clocked_by(rcfgA.clk), reset_by(rcfgA.rstN)); + // portA_start_op = regToWriteOnly(syncA1); + // portA_op_complete = regToReadOnly(syncA2); - if (!rcfgB.enabled) begin - portB_start_op = discardingWriteOnly; - portB_op_complete = constToReadOnly(False); - end - else if (rcfgB.register_output) begin - let syncB1 <- mkDReg(False, clocked_by(rcfgB.clk), reset_by(rcfgB.rstN)); - let syncB2 <- mkReg(False, clocked_by(rcfgB.clk), reset_by(rcfgB.rstN)); - portB_start_op = regToWriteOnly(syncB1); - portB_op_complete = regToReadOnly(syncB2); + // (* no_implicit_conditions, fire_when_enabled *) + // rule syncA1_to_syncA2; + // syncA2 <= syncA1; + // endrule + // end + // else begin + // let syncA <- mkDReg(False, clocked_by(rcfgA.clk), reset_by(rcfgA.rstN)); + // portA_start_op = regToWriteOnly(syncA); + // portA_op_complete = regToReadOnly(syncA); + // end - (* no_implicit_conditions, fire_when_enabled *) - rule syncB1_to_syncB2; - syncB2 <= syncB1; - endrule - end - else begin - let syncB1 <- mkDReg(False, clocked_by(rcfgB.clk), reset_by(rcfgB.rstN)); - portB_start_op = regToWriteOnly(syncB1); - portB_op_complete = regToReadOnly(syncB1); - end + // if (!rcfgB.enabled) begin + // portB_start_op = discardingWriteOnly; + // portB_op_complete = constToReadOnly(False); + // end + // else if (rcfgB.register_output) begin + // let syncB1 <- mkDReg(False, clocked_by(rcfgB.clk), reset_by(rcfgB.rstN)); + // let syncB2 <- mkReg(False, clocked_by(rcfgB.clk), reset_by(rcfgB.rstN)); + // portB_start_op = regToWriteOnly(syncB1); + // portB_op_complete = regToReadOnly(syncB2); + + // (* no_implicit_conditions, fire_when_enabled *) + // rule syncB1_to_syncB2; + // syncB2 <= syncB1; + // endrule + // end + // else begin + // let syncB1 <- mkDReg(False, clocked_by(rcfgB.clk), reset_by(rcfgB.rstN)); + // portB_start_op = regToWriteOnly(syncB1); + // portB_op_complete = regToReadOnly(syncB1); + // end interface EBRPort portA; method Action put(UInt#(3) chip_select, Bool write, addr_a address, data_a datain); mem.portA.put(chip_select, write, address, datain); if (rcfgA.write_outputs_data || !write) - portA_start_op <= True; + latencyA <= ?; endmethod - method data_a read() if (rcfgA.enabled && portA_op_complete == True); + method data_a read() if (rcfgA.enabled && latencyA.ready); return mem.portA.read(); endmethod endinterface @@ -558,9 +563,9 @@ module mkEBR#(EBRPortConfig cfgA, method Action put(UInt#(3) chip_select, Bool write, addr_b address, data_b datain); mem.portB.put(chip_select, write, address, datain); if (rcfgB.write_outputs_data || !write) - portB_start_op <= True; + latencyB <= ?; endmethod - method data_b read() if (rcfgB.enabled && portB_op_complete == True); + method data_b read() if (rcfgB.enabled && latencyB.ready); return mem.portB.read(); endmethod endinterface diff --git a/tasks.py b/tasks.py index ddd3720..c1328f7 100644 --- a/tasks.py +++ b/tasks.py @@ -89,7 +89,7 @@ def build(c, target="."): for target in expand_build_target(target): out_info, out_verilog, out_bsc = ensure_build_dirs(target, "info", "verilog", "bsc") print(f"Building {target}") - c.run(f"bsc -aggressive-conditions -check-assert -u -verilog -info-dir {out_info} -vdir {out_verilog} -bdir {out_bsc} -p {target.parent}:lib:%/Libraries -show-module-use -show-compiles {target}") + c.run(f"bsc -aggressive-conditions -check-assert -remove-dollar -remove-empty-rules -remove-false-rules -remove-starved-rules -remove-unused-modules -show-method-conf -show-method-bvi -u -verilog -info-dir {out_info} -vdir {out_verilog} -bdir {out_bsc} -p {target.parent}:lib:%/Libraries -show-module-use -show-compiles {target}") module_name = Path(f"mk{target.stem}") verilog_main_file = out_verilog / module_name.with_suffix(".v") @@ -132,7 +132,9 @@ def synth(c, target): hierarchy -top {module_name} synth_ecp5 -top {module_name} -run :map_ram write_verilog -sv {yosys_preprocessed} + opt_clean -purge synth_ecp5 -run map_ram: -json {yosys_json} + opt_clean -purge write_verilog -sv {yosys_compiled} tee -o {yosys_report} stat """))