Compare commits
No commits in common. "f1e705fd31097305884b6c7f164d6e27ca7d7737" and "27da4958d27f3ab2c0e44c9518dc9080847b2ba5" have entirely different histories.
f1e705fd31
...
27da4958d2
|
@ -1,98 +0,0 @@
|
||||||
package DelayLine;
|
|
||||||
|
|
||||||
import List::*;
|
|
||||||
|
|
||||||
// A DelayLine mirrors writes back as reads after a number of delay
|
|
||||||
// cycles. Delay lines are pipelined and multiple values can be "in
|
|
||||||
// flight" at once.
|
|
||||||
//
|
|
||||||
// Reads block until a value is available, but the DelayLine doesn't
|
|
||||||
// stall if there are no readers - the value is available for reading
|
|
||||||
// after the set delay, and is lost if not read by anything.
|
|
||||||
//
|
|
||||||
// For callers that need to check for a value without tripping over an
|
|
||||||
// implicit condition, ready() can poll the delay line's output
|
|
||||||
// without blocking.
|
|
||||||
interface DelayLine#(type value);
|
|
||||||
method Action _write (value v);
|
|
||||||
method value _read();
|
|
||||||
(* always_ready *)
|
|
||||||
method Bool ready();
|
|
||||||
endinterface
|
|
||||||
|
|
||||||
// mkDelayLine constructs a DelayLine with delay_cycles of latency
|
|
||||||
// between writes and reads. delay_cycles may be zero, with the result
|
|
||||||
// being Wire/RWire like scheduling semantics.
|
|
||||||
module mkDelayLine(Integer delay_cycles, DelayLine#(a) ifc)
|
|
||||||
provisos (Bits#(a, a_sz));
|
|
||||||
|
|
||||||
// In most cases a DelayLine is a pipeline of registers through
|
|
||||||
// which values flow, and so it inherits the scheduling properties
|
|
||||||
// of registers: all reads from the delay line happen before any
|
|
||||||
// writes take effect.
|
|
||||||
//
|
|
||||||
// The exception is when the caller asks for zero cycles of delay:
|
|
||||||
// in that case, a write must be visible to reads within the same
|
|
||||||
// cycle, and thus the write to the delay line must execute before
|
|
||||||
// any reads.
|
|
||||||
//
|
|
||||||
// So, handle a delay of 0 cycles specially and implement it as an
|
|
||||||
// RWire, which has write-before-read behavior. Otherwise, build a
|
|
||||||
// pipeline of registers with read-before-write behavior.
|
|
||||||
//
|
|
||||||
// You might wonder why even bother allowing a DelayLine with zero
|
|
||||||
// delay. It's because doing so is handy in parameterized
|
|
||||||
// modules. Say for example you're wrapping a blackbox module that
|
|
||||||
// has optional input and output registers. Depending on the
|
|
||||||
// parameters, the in->out delay could be 0 cycles (pure
|
|
||||||
// combinatorial circuit), 1 cycle (input or output register) or 2
|
|
||||||
// cycles (input and output register). It's very handy to be able
|
|
||||||
// to plop down a DelayLine regardless of the requested
|
|
||||||
// configuration, and have it gracefully go all the way to zero
|
|
||||||
// latency.
|
|
||||||
if (delay_cycles == 0) begin
|
|
||||||
RWire#(a) w <- mkRWire();
|
|
||||||
|
|
||||||
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
|
|
||||||
end
|
|
||||||
else begin
|
|
||||||
RWire#(a) inputVal <- mkRWire;
|
|
||||||
|
|
||||||
// Note that in rules and modules, for loops get unrolled
|
|
||||||
// statically at compile time. We're not specifying a circuit
|
|
||||||
// that runs a loop in hardware here, this is shorthand for
|
|
||||||
// "splat out N registers and wire them together in a line".
|
|
||||||
List#(Reg#(Maybe#(a))) delay = Nil;
|
|
||||||
for (Integer i = 0; i < delay_cycles; i = i+1) begin
|
|
||||||
let r <- mkReg(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
|
|
||||||
|
|
||||||
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
|
|
||||||
end
|
|
||||||
endmodule
|
|
||||||
|
|
||||||
endpackage
|
|
|
@ -1,85 +0,0 @@
|
||||||
package DelayLine_Test;
|
|
||||||
|
|
||||||
import Assert::*;
|
|
||||||
import StmtFSM::*;
|
|
||||||
import Testing::*;
|
|
||||||
import Printf::*;
|
|
||||||
import List::*;
|
|
||||||
|
|
||||||
import DelayLine::*;
|
|
||||||
|
|
||||||
module mkTB();
|
|
||||||
let cycles <- mkCycleCounter();
|
|
||||||
|
|
||||||
function Stmt testDelayLine(DelayLine#(Int#(8)) delay, Bit#(32) wantDelay);
|
|
||||||
seq
|
|
||||||
action
|
|
||||||
delay <= 42;
|
|
||||||
cycles.reset();
|
|
||||||
$display(" write cycle: %0d", cycles.all);
|
|
||||||
endaction
|
|
||||||
|
|
||||||
repeat (wantDelay-1)
|
|
||||||
action
|
|
||||||
if (delay.ready) begin
|
|
||||||
$display("delay line ready after %0d cycles, want %0d (on cycle %0d)", cycles, wantDelay, cycles.all);
|
|
||||||
$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 != wantDelay) begin
|
|
||||||
$display("delay line became ready after %0d cycles, want %0d (on cycle %0d)", cycles, wantDelay, cycles.all);
|
|
||||||
$finish;
|
|
||||||
end
|
|
||||||
$display(" read cycle: %0d", cycles.all);
|
|
||||||
endaction
|
|
||||||
endpar
|
|
||||||
|
|
||||||
dynamicAssert(delay.ready == False, "delay line still ready after value yield");
|
|
||||||
endseq;
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
let delay0 <- mkDelayLine(0);
|
|
||||||
let delay1 <- mkDelayLine(1);
|
|
||||||
let delay2 <- mkDelayLine(2);
|
|
||||||
let delay3 <- mkDelayLine(3);
|
|
||||||
let delay4 <- mkDelayLine(4);
|
|
||||||
|
|
||||||
let test0 = seq
|
|
||||||
dynamicAssert(delay0.ready == False, "delay line ready before put");
|
|
||||||
|
|
||||||
par
|
|
||||||
action
|
|
||||||
delay0 <= 42;
|
|
||||||
$display(" write cycle: %0d", cycles.all);
|
|
||||||
endaction
|
|
||||||
action
|
|
||||||
dynamicAssert(delay0.ready == True, "delay line not ready on same cycle");
|
|
||||||
$display(" read cycle: %0d", cycles.all);
|
|
||||||
endaction
|
|
||||||
dynamicAssert(delay0 == 42, "delay line has wrong value");
|
|
||||||
endpar
|
|
||||||
|
|
||||||
dynamicAssert(delay0.ready == False, "delay line ready without write");
|
|
||||||
endseq;
|
|
||||||
|
|
||||||
|
|
||||||
runTest(100,
|
|
||||||
mkTest("DelayLine", seq
|
|
||||||
mkTest("DelayLine/0", test0);
|
|
||||||
mkTest("DelayLine/1", testDelayLine(delay1, 1));
|
|
||||||
mkTest("DelayLine/2", testDelayLine(delay2, 2));
|
|
||||||
mkTest("DelayLine/3", testDelayLine(delay3, 3));
|
|
||||||
mkTest("DelayLine/4", testDelayLine(delay4, 4));
|
|
||||||
endseq));
|
|
||||||
endmodule
|
|
||||||
|
|
||||||
endpackage
|
|
|
@ -2,65 +2,22 @@ package Testing;
|
||||||
|
|
||||||
import Assert::*;
|
import Assert::*;
|
||||||
import StmtFSM::*;
|
import StmtFSM::*;
|
||||||
import Cntrs::*;
|
|
||||||
|
|
||||||
// A CycleCounter keeps a count of total elapsed cycles in a
|
module mkTestCycleLimit#(Integer max_cycles)();
|
||||||
// simulation, as well as cycles since the last reset.
|
Reg#(UInt#(64)) c <- mkReg(fromInteger(max_cycles));
|
||||||
interface CycleCounter;
|
|
||||||
// _read returns the number of cycles since the last time reset()
|
|
||||||
// was called.
|
|
||||||
method Bit#(32) _read();
|
|
||||||
// reset resets the cycle counter. The counter will read 1 on the
|
|
||||||
// cycle following the call to reset.
|
|
||||||
method Action reset();
|
|
||||||
|
|
||||||
// all returns the number of cycles elapsed since simulation start,
|
rule count;
|
||||||
// for use in $display and the like.
|
dynamicAssert(c > 0, "FAIL: test timed out");
|
||||||
method Bit#(32) all();
|
c <= c-1;
|
||||||
endinterface
|
|
||||||
|
|
||||||
module mkCycleCounter(CycleCounter);
|
|
||||||
let total <- mkCount(0);
|
|
||||||
let cnt <- mkCount(0);
|
|
||||||
|
|
||||||
(* no_implicit_conditions, fire_when_enabled *)
|
|
||||||
rule count_up;
|
|
||||||
cnt.incr(1);
|
|
||||||
total.incr(1);
|
|
||||||
endrule
|
endrule
|
||||||
|
|
||||||
method _read = cnt._read;
|
|
||||||
method Action reset();
|
|
||||||
cnt.update(0);
|
|
||||||
endmethod
|
|
||||||
method all = total._read;
|
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
// mkTest runs the given test, printing status text before and after
|
module mkTest#(String name, RStmt#(Bit#(0)) test)();
|
||||||
// the run. Tests can be nested.
|
|
||||||
function Stmt mkTest(String name, Stmt test);
|
|
||||||
seq
|
|
||||||
$display("RUN %s", name);
|
|
||||||
test;
|
|
||||||
$display("OK %s", name);
|
|
||||||
endseq;
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
// runTest runs the given test with a timeout.
|
|
||||||
module runTest(Integer cycle_limit, Stmt test, Empty ifc);
|
|
||||||
Bit#(32) max = fromInteger(cycle_limit);
|
|
||||||
let cnt <- mkCount(0);
|
|
||||||
|
|
||||||
(* no_implicit_conditions, fire_when_enabled *)
|
|
||||||
rule cycle_deadline;
|
|
||||||
dynamicAssert(cnt < max, "Test timed out");
|
|
||||||
cnt.incr(1);
|
|
||||||
endrule
|
|
||||||
|
|
||||||
mkAutoFSM(seq
|
mkAutoFSM(seq
|
||||||
$display("Running test with %0d cycle timeout", max);
|
$display("RUN ", name);
|
||||||
test;
|
test;
|
||||||
endseq);
|
$display("OK ", name);
|
||||||
|
endseq);
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
endpackage
|
endpackage
|
||||||
|
|
22
tasks.py
22
tasks.py
|
@ -92,18 +92,13 @@ 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_main_file = out_verilog / module_name.with_suffix(".v")
|
verilog_files.append(out_verilog / module_name.with_suffix(".v"))
|
||||||
if verilog_main_file.is_file():
|
with open(out_verilog / module_name.with_suffix(".use")) as f:
|
||||||
verilog_files.append(verilog_main_file)
|
verilog_files.extend(find_verilog_modules(c, f.read().splitlines()))
|
||||||
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:")
|
||||||
print("\nVerilog files for synthesis:")
|
for v in verilog_files:
|
||||||
for v in verilog_files:
|
print(" "+str(v))
|
||||||
print(" "+str(v))
|
|
||||||
|
|
||||||
return verilog_files
|
return verilog_files
|
||||||
|
|
||||||
|
@ -115,9 +110,6 @@ 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"
|
||||||
|
@ -167,7 +159,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