gary/lib/PinSync.bsv

52 lines
1.8 KiB
Plaintext
Raw Normal View History

package PinSync;
(* always_ready, always_enabled *)
interface PinSync#(type val);
method Action _write(val a);
method val _read();
endinterface
// 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, PinSync#(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