lib: use DelayLine in ECP5_RAM

Cleans up the code nicely, and still produces the correct logic.
This commit is contained in:
David Anderson 2024-08-13 20:53:47 -07:00
parent f1e705fd31
commit 5df41d4b94
4 changed files with 59 additions and 51 deletions

View File

@ -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 *)

View File

@ -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 *)

View File

@ -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

View File

@ -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
"""))