lib: add a DelayLine module
A delay line takes a write and echoes it back N cycles later, with N fixed at compile time. It's a handy primitive to have when wrapping Verilog blackbox modules because the blackbox often specifies something like having 2 cycles of latency, and so you need to bubble the fact that a write occurred 2 cycles ago through to the output so that you can wire up the right implicit conditions.
This commit is contained in:
parent
27da4958d2
commit
85e27554ec
|
@ -0,0 +1,60 @@
|
||||||
|
package DelayLine;
|
||||||
|
|
||||||
|
import List::*;
|
||||||
|
|
||||||
|
interface DelayLine#(type value);
|
||||||
|
method Action _write (value v);
|
||||||
|
method value _read();
|
||||||
|
method Bool ready();
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
module mkDelayLine(Integer delay_cycles, DelayLine#(a) ifc)
|
||||||
|
provisos (Bits#(a, a_sz));
|
||||||
|
|
||||||
|
DelayLine#(a) ret = ?;
|
||||||
|
|
||||||
|
if (delay_cycles == 0) begin
|
||||||
|
RWire#(a) w <- mkRWire();
|
||||||
|
ret = (interface DelayLine;
|
||||||
|
method Action _write(a value);
|
||||||
|
w.wset(value);
|
||||||
|
endmethod
|
||||||
|
method a _read() if (w.wget matches tagged Valid .val);
|
||||||
|
return val;
|
||||||
|
endmethod
|
||||||
|
method Bool ready();
|
||||||
|
return isValid(w.wget);
|
||||||
|
endmethod
|
||||||
|
endinterface);
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
RWire#(a) inputVal <- mkRWire;
|
||||||
|
List#(Reg#(Maybe#(a))) delay = tagged Nil;
|
||||||
|
for (Integer i = 0; i < delay_cycles; i = i+1) begin
|
||||||
|
let r <- mkReg(tagged Invalid);
|
||||||
|
delay = cons(r, delay);
|
||||||
|
end
|
||||||
|
|
||||||
|
(* no_implicit_conditions, fire_when_enabled *)
|
||||||
|
rule pump_line;
|
||||||
|
delay[0] <= inputVal.wget();
|
||||||
|
for (Integer i = 0; i < delay_cycles-1; i = i+1)
|
||||||
|
delay[i+1] <= delay[i];
|
||||||
|
endrule
|
||||||
|
|
||||||
|
ret = (interface DelayLine;
|
||||||
|
method Action _write(a value);
|
||||||
|
inputVal.wset(value);
|
||||||
|
endmethod
|
||||||
|
method a _read() if (delay[delay_cycles-1] matches tagged Valid .val);
|
||||||
|
return val;
|
||||||
|
endmethod
|
||||||
|
method Bool ready();
|
||||||
|
return isValid(delay[delay_cycles-1]);
|
||||||
|
endmethod
|
||||||
|
endinterface);
|
||||||
|
end
|
||||||
|
return ret;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
endpackage
|
|
@ -0,0 +1,98 @@
|
||||||
|
package DelayLine_Test;
|
||||||
|
|
||||||
|
import Assert::*;
|
||||||
|
import StmtFSM::*;
|
||||||
|
import Testing::*;
|
||||||
|
import Printf::*;
|
||||||
|
import List::*;
|
||||||
|
|
||||||
|
import DelayLine::*;
|
||||||
|
|
||||||
|
module mkTB();
|
||||||
|
let cycles <- mkTestCycleLimiter(100);
|
||||||
|
|
||||||
|
function Stmt testDelayLine(DelayLine#(Int#(8)) delay, Bit#(32) wantDelay);
|
||||||
|
seq
|
||||||
|
$display(" RUN delay=%0d", wantDelay);
|
||||||
|
|
||||||
|
action
|
||||||
|
delay <= 42;
|
||||||
|
cycles.reset_count();
|
||||||
|
$display(" write cycle: %0d", cycles.cycles);
|
||||||
|
endaction
|
||||||
|
|
||||||
|
repeat (wantDelay-1)
|
||||||
|
action
|
||||||
|
if (delay.ready) begin
|
||||||
|
$display("delay line ready after %0d cycles, want %0d", cycles.count, wantDelay);
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endaction
|
||||||
|
|
||||||
|
// Check the value coming off the delay line and the timing
|
||||||
|
// separately, since the delay line read can be blocked by
|
||||||
|
// implicit conditions.
|
||||||
|
par
|
||||||
|
dynamicAssert(delay == 42, "delay output was wrong value");
|
||||||
|
|
||||||
|
action
|
||||||
|
dynamicAssert(delay.ready == True, "delay line not ready when expected");
|
||||||
|
if (cycles.count != wantDelay) begin
|
||||||
|
$display("delay line ready after %0d cycles, want %0d", cycles.count, wantDelay);
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
$display(" ready cycle: %0d", cycles.cycles);
|
||||||
|
endaction
|
||||||
|
endpar
|
||||||
|
|
||||||
|
dynamicAssert(delay.ready == False, "delay line still ready after value yield");
|
||||||
|
|
||||||
|
$display(" OK delay=%0d", wantDelay);
|
||||||
|
endseq;
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
let delay0 <- mkDelayLine(0);
|
||||||
|
let test0 = seq
|
||||||
|
$display(" RUN delay=0");
|
||||||
|
|
||||||
|
dynamicAssert(delay0.ready == False, "delay line ready before put");
|
||||||
|
|
||||||
|
par
|
||||||
|
action
|
||||||
|
delay0 <= 42;
|
||||||
|
$display(" write cycle: %0d", cycles.cycles);
|
||||||
|
endaction
|
||||||
|
action
|
||||||
|
dynamicAssert(delay0.ready == True, "delay line not ready on same cycle");
|
||||||
|
$display(" read cycle: %0d", cycles.cycles);
|
||||||
|
endaction
|
||||||
|
dynamicAssert(delay0 == 42, "delay line has wrong value");
|
||||||
|
endpar
|
||||||
|
|
||||||
|
dynamicAssert(delay0.ready == False, "delay line ready without write");
|
||||||
|
|
||||||
|
$display(" OK delay=0");
|
||||||
|
endseq;
|
||||||
|
|
||||||
|
let delay1 <- mkDelayLine(1);
|
||||||
|
let test1 = testDelayLine(delay1, 1);
|
||||||
|
|
||||||
|
let delay2 <- mkDelayLine(2);
|
||||||
|
let test2 = testDelayLine(delay2, 2);
|
||||||
|
|
||||||
|
let delay3 <- mkDelayLine(3);
|
||||||
|
let test3 = testDelayLine(delay3, 3);
|
||||||
|
|
||||||
|
let delay4 <- mkDelayLine(4);
|
||||||
|
let test4 = testDelayLine(delay4, 4);
|
||||||
|
|
||||||
|
mkTest("DelayLine", seq
|
||||||
|
test0;
|
||||||
|
test1;
|
||||||
|
test2;
|
||||||
|
test3;
|
||||||
|
test4;
|
||||||
|
endseq);
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
endpackage
|
|
@ -3,13 +3,31 @@ package Testing;
|
||||||
import Assert::*;
|
import Assert::*;
|
||||||
import StmtFSM::*;
|
import StmtFSM::*;
|
||||||
|
|
||||||
module mkTestCycleLimit#(Integer max_cycles)();
|
interface TestCycleLimiter;
|
||||||
Reg#(UInt#(64)) c <- mkReg(fromInteger(max_cycles));
|
method Bit#(32) cycles();
|
||||||
|
|
||||||
rule count;
|
method Bit#(32) count();
|
||||||
dynamicAssert(c > 0, "FAIL: test timed out");
|
method Action reset_count();
|
||||||
c <= c-1;
|
endinterface
|
||||||
|
|
||||||
|
module mkTestCycleLimiter#(Integer max_cycles)(TestCycleLimiter);
|
||||||
|
Bit#(32) max = fromInteger(max_cycles);
|
||||||
|
Reg#(Bit#(32)) cnt <- mkReg(0);
|
||||||
|
Reg#(Bit#(32)) lastReset <- mkReg(0);
|
||||||
|
|
||||||
|
(* no_implicit_conditions, fire_when_enabled *)
|
||||||
|
rule count_up;
|
||||||
|
dynamicAssert(cnt < max, "FAIL: test timed out");
|
||||||
|
cnt <= cnt+1;
|
||||||
endrule
|
endrule
|
||||||
|
|
||||||
|
method cycles = cnt._read;
|
||||||
|
method Bit#(32) count();
|
||||||
|
return cnt - lastReset;
|
||||||
|
endmethod
|
||||||
|
method Action reset_count();
|
||||||
|
lastReset <= cnt;
|
||||||
|
endmethod
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
module mkTest#(String name, RStmt#(Bit#(0)) test)();
|
module mkTest#(String name, RStmt#(Bit#(0)) test)();
|
||||||
|
|
12
tasks.py
12
tasks.py
|
@ -92,10 +92,15 @@ def build(c, 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 -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_files.append(out_verilog / module_name.with_suffix(".v"))
|
verilog_main_file = out_verilog / module_name.with_suffix(".v")
|
||||||
|
if verilog_main_file.is_file():
|
||||||
|
verilog_files.append(verilog_main_file)
|
||||||
|
use_file = out_verilog / module_name.with_suffix(".use")
|
||||||
|
if use_file.is_file():
|
||||||
with open(out_verilog / module_name.with_suffix(".use")) as f:
|
with open(out_verilog / module_name.with_suffix(".use")) as f:
|
||||||
verilog_files.extend(find_verilog_modules(c, f.read().splitlines()))
|
verilog_files.extend(find_verilog_modules(c, f.read().splitlines()))
|
||||||
|
|
||||||
|
if verilog_files:
|
||||||
print("\nVerilog files for synthesis:")
|
print("\nVerilog files for synthesis:")
|
||||||
for v in verilog_files:
|
for v in verilog_files:
|
||||||
print(" "+str(v))
|
print(" "+str(v))
|
||||||
|
@ -110,6 +115,9 @@ def synth(c, target):
|
||||||
|
|
||||||
module_name = Path(f"mk{target.stem}")
|
module_name = Path(f"mk{target.stem}")
|
||||||
verilog_files = build(c, target)
|
verilog_files = build(c, target)
|
||||||
|
if not verilog_files:
|
||||||
|
print("\nWARNING: bluespec compile didn't output any verilog files, did you (* synthesize *) something?")
|
||||||
|
return
|
||||||
|
|
||||||
phase("Logic synthesis")
|
phase("Logic synthesis")
|
||||||
yosys_script = out_yosys / "script.ys"
|
yosys_script = out_yosys / "script.ys"
|
||||||
|
@ -159,7 +167,7 @@ def synth(c, target):
|
||||||
print(f"Wrote bitstream to {bitstream}")
|
print(f"Wrote bitstream to {bitstream}")
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def test(c, target="."):
|
def test(c, target):
|
||||||
for target in expand_test_target(target):
|
for target in expand_test_target(target):
|
||||||
out_info, out_sim, out_bsc = ensure_build_dirs(target, "info", "sim", "bsc")
|
out_info, out_sim, out_bsc = ensure_build_dirs(target, "info", "sim", "bsc")
|
||||||
c.run(f"bsc -show-schedule -aggressive-conditions -check-assert -u -sim -info-dir {out_info} -simdir {out_sim} -bdir {out_bsc} -g mkTB -p {target.parent}:lib:%/Libraries {target}")
|
c.run(f"bsc -show-schedule -aggressive-conditions -check-assert -u -sim -info-dir {out_info} -simdir {out_sim} -bdir {out_bsc} -g mkTB -p {target.parent}:lib:%/Libraries {target}")
|
||||||
|
|
Loading…
Reference in New Issue