Compare commits
No commits in common. "2ff58b51d2c1aa9815602b8f511eec3179787490" and "b46d70fa070847d237c6c4285abd1a3dfe1d72b1" have entirely different histories.
2ff58b51d2
...
b46d70fa07
|
@ -1,45 +0,0 @@
|
||||||
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
|
|
|
@ -1,50 +0,0 @@
|
||||||
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
|
|
|
@ -1,57 +0,0 @@
|
||||||
package Strobe;
|
|
||||||
|
|
||||||
import Real::*;
|
|
||||||
import Printf::*;
|
|
||||||
|
|
||||||
// A Strobe provides a synchronization signal to other modules, when
|
|
||||||
// an event happens at a cadence other than the module clock.
|
|
||||||
(* always_ready *)
|
|
||||||
interface Strobe;
|
|
||||||
method Bool _read();
|
|
||||||
// reset resets the strobe cycle, starting with a strobe on the
|
|
||||||
// cycle following reset.
|
|
||||||
method Action reset();
|
|
||||||
endinterface
|
|
||||||
|
|
||||||
// mkStrobe returns a Strobe that triggers at the given
|
|
||||||
// target_frequency, assuming mkStrobe is being clocked at the given
|
|
||||||
// higher clock_frequency.
|
|
||||||
module mkStrobe(Integer clock_frequency, Integer target_frequency, Strobe ifc);
|
|
||||||
if (target_frequency > clock_frequency)
|
|
||||||
error("mkStrobe target_frequency must be less than clock_frequency");
|
|
||||||
let strobe_every = round(fromInteger(clock_frequency)/fromInteger(target_frequency));
|
|
||||||
|
|
||||||
// Because we're using integer counters to divide frequencies,
|
|
||||||
// unless the clock and target frequencies divide cleanly we'll end
|
|
||||||
// up with a small amount of error.
|
|
||||||
//
|
|
||||||
// Strobes like this tend to be used for relatively short
|
|
||||||
// operations before some other synchronization event happens
|
|
||||||
// (e.g. sending one byte on UART), so we can allow a small amount
|
|
||||||
// of frequency error. For now, the target frequency error is fixed
|
|
||||||
// at <=0.1%.
|
|
||||||
Real actual_frequency = fromInteger(clock_frequency)/fromInteger(strobe_every);
|
|
||||||
Real frequency_error_pct = abs(fromInteger(target_frequency)-actual_frequency) / fromInteger(target_frequency) * 100;
|
|
||||||
if (frequency_error_pct > 0.1)
|
|
||||||
error(sprintf("mkStrobe actual frequency is %0f, %0f%% error vs. requested %0d. Your clock_frequency and target_frequency are probably too near each other.", actual_frequency, frequency_error_pct, target_frequency));
|
|
||||||
|
|
||||||
Reg#(UInt#(32)) cnt[2] <- mkCReg(2, 0);
|
|
||||||
|
|
||||||
(* no_implicit_conditions, fire_when_enabled *)
|
|
||||||
rule increment;
|
|
||||||
if (cnt[0] == fromInteger(strobe_every-1))
|
|
||||||
cnt[0] <= 0;
|
|
||||||
else
|
|
||||||
cnt[0] <= cnt[0]+1;
|
|
||||||
endrule
|
|
||||||
|
|
||||||
method Bool _read();
|
|
||||||
return cnt[0] == 0;
|
|
||||||
endmethod
|
|
||||||
|
|
||||||
method Action reset();
|
|
||||||
cnt[1] <= 0;
|
|
||||||
endmethod
|
|
||||||
endmodule
|
|
||||||
|
|
||||||
endpackage
|
|
|
@ -1,53 +0,0 @@
|
||||||
package Strobe_Test;
|
|
||||||
|
|
||||||
import Assert::*;
|
|
||||||
import StmtFSM::*;
|
|
||||||
|
|
||||||
import Strobe::*;
|
|
||||||
import Testing::*;
|
|
||||||
|
|
||||||
module mkTB();
|
|
||||||
let testflags <- mkTestFlags();
|
|
||||||
let cycles <- mkCycleCounter();
|
|
||||||
|
|
||||||
// For this test, we assume we're clocked at 25MHz, and want a
|
|
||||||
// 115_200bps strobe for a serial port. That translates to a strobe
|
|
||||||
// every 217 cycles.
|
|
||||||
let dut <- mkStrobe(25_000_000, 115_200);
|
|
||||||
let want_pulse_every = 217;
|
|
||||||
|
|
||||||
function Action check_dut(Bool want);
|
|
||||||
return action
|
|
||||||
if (testflags.verbose)
|
|
||||||
$display("%0d (%0d): strobe = %0d, want %0d", cycles.all, cycles, dut, want);
|
|
||||||
dynamicAssert(dut == want, "incorrect strobe state");
|
|
||||||
endaction;
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function Stmt check_one_cycle();
|
|
||||||
return seq
|
|
||||||
action
|
|
||||||
check_dut(True);
|
|
||||||
cycles.reset();
|
|
||||||
endaction
|
|
||||||
while (cycles < want_pulse_every)
|
|
||||||
check_dut(False);
|
|
||||||
endseq;
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
runTest(2000,
|
|
||||||
mkTest("Strobe", seq
|
|
||||||
dut.reset();
|
|
||||||
repeat(3) check_one_cycle();
|
|
||||||
|
|
||||||
// Reset should actually reset
|
|
||||||
repeat(10) noAction;
|
|
||||||
par
|
|
||||||
check_dut(False);
|
|
||||||
dut.reset();
|
|
||||||
endpar
|
|
||||||
repeat(3) check_one_cycle();
|
|
||||||
endseq));
|
|
||||||
endmodule
|
|
||||||
|
|
||||||
endpackage
|
|
Loading…
Reference in New Issue