lib/Strobe: add a Strobe module to generate synchronization pulses
This commit is contained in:
parent
b46d70fa07
commit
4013be675e
|
@ -0,0 +1,57 @@
|
||||||
|
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
|
|
@ -0,0 +1,53 @@
|
||||||
|
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