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 StmtFSM::*;
|
||||
|
||||
module mkTestCycleLimit#(Integer max_cycles)();
|
||||
Reg#(UInt#(64)) c <- mkReg(fromInteger(max_cycles));
|
||||
interface TestCycleLimiter;
|
||||
method Bit#(32) cycles();
|
||||
|
||||
rule count;
|
||||
dynamicAssert(c > 0, "FAIL: test timed out");
|
||||
c <= c-1;
|
||||
method Bit#(32) count();
|
||||
method Action reset_count();
|
||||
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
|
||||
|
||||
method cycles = cnt._read;
|
||||
method Bit#(32) count();
|
||||
return cnt - lastReset;
|
||||
endmethod
|
||||
method Action reset_count();
|
||||
lastReset <= cnt;
|
||||
endmethod
|
||||
endmodule
|
||||
|
||||
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}")
|
||||
|
||||
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:
|
||||
verilog_files.extend(find_verilog_modules(c, f.read().splitlines()))
|
||||
|
||||
if verilog_files:
|
||||
print("\nVerilog files for synthesis:")
|
||||
for v in verilog_files:
|
||||
print(" "+str(v))
|
||||
|
@ -110,6 +115,9 @@ def synth(c, target):
|
|||
|
||||
module_name = Path(f"mk{target.stem}")
|
||||
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")
|
||||
yosys_script = out_yosys / "script.ys"
|
||||
|
@ -159,7 +167,7 @@ def synth(c, target):
|
|||
print(f"Wrote bitstream to {bitstream}")
|
||||
|
||||
@task
|
||||
def test(c, target="."):
|
||||
def test(c, target):
|
||||
for target in expand_test_target(target):
|
||||
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}")
|
||||
|
|
Loading…
Reference in New Issue