lib/PinSync: add a pin synchronizer for async inputs
This commit is contained in:
parent
4013be675e
commit
2ff58b51d2
|
@ -0,0 +1,45 @@
|
||||||
|
package PinSync;
|
||||||
|
|
||||||
|
// mkPinSync builds a synchronizer for use with asynchronous inputs.
|
||||||
|
//
|
||||||
|
// You should only use this to capture asynchronous inputs coming from
|
||||||
|
// outside your design. For clock domain crossing within your design,
|
||||||
|
// use the dual-clocked synchronizers found in Bluespec's standard
|
||||||
|
// library.
|
||||||
|
//
|
||||||
|
// As the name suggests, mkPinSync is intended to be used to
|
||||||
|
// synchronize data coming into your design from an external pin, such
|
||||||
|
// as the RX line of a UART. Such signals do not run according to a
|
||||||
|
// known clock, so the regular stdlib synchronizers cannot be used as
|
||||||
|
// there's no "source" clock we can provide them.
|
||||||
|
//
|
||||||
|
// You can think of mkPinSync as the output end of a standard
|
||||||
|
// synchronizer, without the initial register that's clocked by the
|
||||||
|
// source domain. Conceptually, we assume that register exists outside
|
||||||
|
// our design and is driving the input of mkPinSync, so we just need
|
||||||
|
// the metastability mitigation within our own domain.
|
||||||
|
module mkPinSync(val init_value, Reg#(val) ifc)
|
||||||
|
provisos(Bits#(val, _));
|
||||||
|
|
||||||
|
Reg#(val) r1 <- mkReg(init_value);
|
||||||
|
Reg#(val) r2 <- mkReg(init_value);
|
||||||
|
|
||||||
|
// To break write+read conflicts. Without this, a rule that
|
||||||
|
// atomically reads the sync while also writing it fails to
|
||||||
|
// schedule vs. the 'every' rule below. This shouldn't really
|
||||||
|
// happen in real designs, but it's a convenient idiom in
|
||||||
|
// testing. The wire is free in terms of logic, so might as well
|
||||||
|
// make atomic read+write work.
|
||||||
|
Wire#(val) out <- mkBypassWire();
|
||||||
|
|
||||||
|
(* no_implicit_conditions, fire_when_enabled *)
|
||||||
|
rule every;
|
||||||
|
out <= r2;
|
||||||
|
r2 <= r1;
|
||||||
|
endrule
|
||||||
|
|
||||||
|
method _read = out._read;
|
||||||
|
method _write = r1._write;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
endpackage
|
|
@ -0,0 +1,50 @@
|
||||||
|
package PinSync_Test;
|
||||||
|
|
||||||
|
import Assert::*;
|
||||||
|
import StmtFSM::*;
|
||||||
|
|
||||||
|
import PinSync::*;
|
||||||
|
import Testing::*;
|
||||||
|
|
||||||
|
module mkTB();
|
||||||
|
let testflags <- mkTestFlags();
|
||||||
|
let cycles <- mkCycleCounter();
|
||||||
|
|
||||||
|
Reg#(UInt#(2)) dut <- mkPinSync(0);
|
||||||
|
|
||||||
|
function Action check_dut_val(UInt#(2) want_val);
|
||||||
|
return action
|
||||||
|
if (testflags.verbose)
|
||||||
|
$display("%0d: PinSync = %0d, want %0d", cycles.all, dut, want_val);
|
||||||
|
dynamicAssert(dut == want_val, "wrong value");
|
||||||
|
endaction;
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function Stmt check_sync(UInt#(2) starting_val, UInt#(2) want_val);
|
||||||
|
return seq
|
||||||
|
action
|
||||||
|
check_dut_val(starting_val);
|
||||||
|
dut <= want_val;
|
||||||
|
cycles.reset();
|
||||||
|
if (testflags.verbose)
|
||||||
|
$display("%0d: write(%0d)", cycles.all, want_val);
|
||||||
|
endaction
|
||||||
|
|
||||||
|
check_dut_val(starting_val);
|
||||||
|
|
||||||
|
action
|
||||||
|
check_dut_val(want_val);
|
||||||
|
dynamicAssert(cycles == 2, "synchronizer didn't sync at the right time");
|
||||||
|
endaction
|
||||||
|
endseq;
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
runTest(100,
|
||||||
|
mkTest("PinSync", seq
|
||||||
|
check_sync(0, 2);
|
||||||
|
check_sync(2, 3);
|
||||||
|
check_sync(3, 1);
|
||||||
|
endseq));
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
endpackage
|
Loading…
Reference in New Issue