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 *)
|
//(* always_enabled *)
|
||||||
interface Top;
|
interface Top;
|
||||||
interface EBRPort#(Bit#(12), Bit#(4)) ram1;
|
interface EBRPort#(Bit#(12), Bit#(4)) ram1;
|
||||||
//interface EBRPort#(Bit#(14), Bit#(1)) ram2;
|
interface EBRPort#(Bit#(14), Bit#(1)) ram2;
|
||||||
interface EBRPort#(void, void) ram2;
|
//interface EBRPort#(void, void) ram2;
|
||||||
endinterface
|
endinterface
|
||||||
|
|
||||||
(* synthesize *)
|
(* synthesize *)
|
||||||
|
|
|
@ -14,6 +14,7 @@ import List::*;
|
||||||
// implicit condition, ready() can poll the delay line's output
|
// implicit condition, ready() can poll the delay line's output
|
||||||
// without blocking.
|
// without blocking.
|
||||||
interface DelayLine#(type value);
|
interface DelayLine#(type value);
|
||||||
|
(* always_ready *)
|
||||||
method Action _write (value v);
|
method Action _write (value v);
|
||||||
method value _read();
|
method value _read();
|
||||||
(* always_ready *)
|
(* always_ready *)
|
||||||
|
|
101
lib/ECP5_RAM.bsv
101
lib/ECP5_RAM.bsv
|
@ -6,6 +6,8 @@ import Printf::*;
|
||||||
import ToString::*;
|
import ToString::*;
|
||||||
import StmtFSM::*;
|
import StmtFSM::*;
|
||||||
|
|
||||||
|
import DelayLine::*;
|
||||||
|
|
||||||
export EBRWriteMode(..);
|
export EBRWriteMode(..);
|
||||||
export EBRPortConfig(..);
|
export EBRPortConfig(..);
|
||||||
export EBRPort(..);
|
export EBRPort(..);
|
||||||
|
@ -494,62 +496,65 @@ module mkEBR#(EBRPortConfig cfgA,
|
||||||
|
|
||||||
let mem <- mkEBRCore(cfgA, cfgB);
|
let mem <- mkEBRCore(cfgA, cfgB);
|
||||||
|
|
||||||
WriteOnly#(Bool) portA_start_op = ?;
|
DelayLine#(void) latencyA <- mkDelayLine(rcfgA.operation_latency, clocked_by(rcfgA.clk), reset_by(rcfgA.rstN));
|
||||||
ReadOnly#(Bool) portA_op_complete = ?;
|
DelayLine#(void) latencyB <- mkDelayLine(rcfgB.operation_latency, clocked_by(rcfgB.clk), reset_by(rcfgB.rstN));
|
||||||
WriteOnly#(Bool) portB_start_op = ?;
|
|
||||||
ReadOnly#(Bool) portB_op_complete = ?;
|
|
||||||
|
|
||||||
// TODO: this variable-depth register chain should be pulled into a
|
// WriteOnly#(Bool) portA_start_op = ?;
|
||||||
// separate "delay line" module.
|
// ReadOnly#(Bool) portA_op_complete = ?;
|
||||||
if (!rcfgA.enabled) begin
|
// WriteOnly#(Bool) portB_start_op = ?;
|
||||||
portA_start_op = discardingWriteOnly;
|
// ReadOnly#(Bool) portB_op_complete = ?;
|
||||||
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);
|
|
||||||
|
|
||||||
(* no_implicit_conditions, fire_when_enabled *)
|
// // TODO: this variable-depth register chain should be pulled into a
|
||||||
rule syncA1_to_syncA2;
|
// // separate "delay line" module.
|
||||||
syncA2 <= syncA1;
|
// if (!rcfgA.enabled) begin
|
||||||
endrule
|
// portA_start_op = discardingWriteOnly;
|
||||||
end
|
// portA_op_complete = constToReadOnly(False);
|
||||||
else begin
|
// end
|
||||||
let syncA <- mkDReg(False, clocked_by(rcfgA.clk), reset_by(rcfgA.rstN));
|
// else if (rcfgA.register_output) begin
|
||||||
portA_start_op = regToWriteOnly(syncA);
|
// let syncA1 <- mkDReg(False, clocked_by(rcfgA.clk), reset_by(rcfgA.rstN));
|
||||||
portA_op_complete = regToReadOnly(syncA);
|
// let syncA2 <- mkReg(False, clocked_by(rcfgA.clk), reset_by(rcfgA.rstN));
|
||||||
end
|
// portA_start_op = regToWriteOnly(syncA1);
|
||||||
|
// portA_op_complete = regToReadOnly(syncA2);
|
||||||
|
|
||||||
if (!rcfgB.enabled) begin
|
// (* no_implicit_conditions, fire_when_enabled *)
|
||||||
portB_start_op = discardingWriteOnly;
|
// rule syncA1_to_syncA2;
|
||||||
portB_op_complete = constToReadOnly(False);
|
// syncA2 <= syncA1;
|
||||||
end
|
// endrule
|
||||||
else if (rcfgB.register_output) begin
|
// end
|
||||||
let syncB1 <- mkDReg(False, clocked_by(rcfgB.clk), reset_by(rcfgB.rstN));
|
// else begin
|
||||||
let syncB2 <- mkReg(False, clocked_by(rcfgB.clk), reset_by(rcfgB.rstN));
|
// let syncA <- mkDReg(False, clocked_by(rcfgA.clk), reset_by(rcfgA.rstN));
|
||||||
portB_start_op = regToWriteOnly(syncB1);
|
// portA_start_op = regToWriteOnly(syncA);
|
||||||
portB_op_complete = regToReadOnly(syncB2);
|
// portA_op_complete = regToReadOnly(syncA);
|
||||||
|
// end
|
||||||
|
|
||||||
(* no_implicit_conditions, fire_when_enabled *)
|
// if (!rcfgB.enabled) begin
|
||||||
rule syncB1_to_syncB2;
|
// portB_start_op = discardingWriteOnly;
|
||||||
syncB2 <= syncB1;
|
// portB_op_complete = constToReadOnly(False);
|
||||||
endrule
|
// end
|
||||||
end
|
// else if (rcfgB.register_output) begin
|
||||||
else begin
|
// let syncB1 <- mkDReg(False, clocked_by(rcfgB.clk), reset_by(rcfgB.rstN));
|
||||||
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_start_op = regToWriteOnly(syncB1);
|
||||||
portB_op_complete = regToReadOnly(syncB1);
|
// portB_op_complete = regToReadOnly(syncB2);
|
||||||
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
|
||||||
|
|
||||||
interface EBRPort portA;
|
interface EBRPort portA;
|
||||||
method Action put(UInt#(3) chip_select, Bool write, addr_a address, data_a datain);
|
method Action put(UInt#(3) chip_select, Bool write, addr_a address, data_a datain);
|
||||||
mem.portA.put(chip_select, write, address, datain);
|
mem.portA.put(chip_select, write, address, datain);
|
||||||
if (rcfgA.write_outputs_data || !write)
|
if (rcfgA.write_outputs_data || !write)
|
||||||
portA_start_op <= True;
|
latencyA <= ?;
|
||||||
endmethod
|
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();
|
return mem.portA.read();
|
||||||
endmethod
|
endmethod
|
||||||
endinterface
|
endinterface
|
||||||
|
@ -558,9 +563,9 @@ module mkEBR#(EBRPortConfig cfgA,
|
||||||
method Action put(UInt#(3) chip_select, Bool write, addr_b address, data_b datain);
|
method Action put(UInt#(3) chip_select, Bool write, addr_b address, data_b datain);
|
||||||
mem.portB.put(chip_select, write, address, datain);
|
mem.portB.put(chip_select, write, address, datain);
|
||||||
if (rcfgB.write_outputs_data || !write)
|
if (rcfgB.write_outputs_data || !write)
|
||||||
portB_start_op <= True;
|
latencyB <= ?;
|
||||||
endmethod
|
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();
|
return mem.portB.read();
|
||||||
endmethod
|
endmethod
|
||||||
endinterface
|
endinterface
|
||||||
|
|
4
tasks.py
4
tasks.py
|
@ -89,7 +89,7 @@ def build(c, target="."):
|
||||||
for target in expand_build_target(target):
|
for target in expand_build_target(target):
|
||||||
out_info, out_verilog, out_bsc = ensure_build_dirs(target, "info", "verilog", "bsc")
|
out_info, out_verilog, out_bsc = ensure_build_dirs(target, "info", "verilog", "bsc")
|
||||||
print(f"Building {target}")
|
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}")
|
module_name = Path(f"mk{target.stem}")
|
||||||
verilog_main_file = out_verilog / module_name.with_suffix(".v")
|
verilog_main_file = out_verilog / module_name.with_suffix(".v")
|
||||||
|
@ -132,7 +132,9 @@ def synth(c, target):
|
||||||
hierarchy -top {module_name}
|
hierarchy -top {module_name}
|
||||||
synth_ecp5 -top {module_name} -run :map_ram
|
synth_ecp5 -top {module_name} -run :map_ram
|
||||||
write_verilog -sv {yosys_preprocessed}
|
write_verilog -sv {yosys_preprocessed}
|
||||||
|
opt_clean -purge
|
||||||
synth_ecp5 -run map_ram: -json {yosys_json}
|
synth_ecp5 -run map_ram: -json {yosys_json}
|
||||||
|
opt_clean -purge
|
||||||
write_verilog -sv {yosys_compiled}
|
write_verilog -sv {yosys_compiled}
|
||||||
tee -o {yosys_report} stat
|
tee -o {yosys_report} stat
|
||||||
"""))
|
"""))
|
||||||
|
|
Loading…
Reference in New Issue