lib: use DelayLine in ECP5_RAM
Cleans up the code nicely, and still produces the correct logic.
This commit is contained in:
parent
f1e705fd31
commit
5df41d4b94
|
@ -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 *)
|
||||
|
|
|
@ -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 *)
|
||||
|
|
101
lib/ECP5_RAM.bsv
101
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
|
||||
|
|
4
tasks.py
4
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
|
||||
"""))
|
||||
|
|
Loading…
Reference in New Issue