144 lines
5.2 KiB
Plaintext
144 lines
5.2 KiB
Plaintext
package Strobe;
|
|
|
|
import Real::*;
|
|
import Printf::*;
|
|
import DReg::*;
|
|
|
|
// 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 the reset.
|
|
method Action reset();
|
|
endinterface
|
|
|
|
module mkStrobeRaw(UInt#(sz) increment, UInt#(sz) max, Strobe ifc);
|
|
Reg#(UInt#(sz)) cnt <- mkReg(0);
|
|
PulseWire want_reset <- mkPulseWire();
|
|
Reg#(Bool) strobe_out <- mkDReg(False);
|
|
|
|
(* no_implicit_conditions, fire_when_enabled *)
|
|
rule increment;
|
|
if (want_reset) begin
|
|
cnt <= 0;
|
|
strobe_out <= True;
|
|
end
|
|
else begin
|
|
UInt#(TAdd#(sz, 1)) new_cnt = extend(cnt)+extend(increment);
|
|
if (new_cnt >= extend(max)) begin
|
|
new_cnt = new_cnt - extend(max);
|
|
strobe_out <= True;
|
|
end
|
|
cnt <= truncate(new_cnt);
|
|
end
|
|
endrule
|
|
|
|
method Bool _read = strobe_out._read;
|
|
method Action reset();
|
|
want_reset.send();
|
|
endmethod
|
|
endmodule
|
|
|
|
module mkStrobeRawOld(UInt#(sz) increment, UInt#(sz) max, Strobe ifc);
|
|
Reg#(UInt#(sz)) cnt[2] <- mkCReg(2, 0);
|
|
Reg#(Bool) pulse <- mkDReg(False);
|
|
PulseWire pw <- mkPulseWireOR();
|
|
|
|
(* no_implicit_conditions, fire_when_enabled *)
|
|
rule increment;
|
|
UInt#(TAdd#(sz, 1)) new_cnt = extend(cnt[0])+extend(increment);
|
|
if (new_cnt >= extend(max)) begin
|
|
cnt[0] <= truncate(new_cnt - extend(max));
|
|
pulse <= True;
|
|
end
|
|
else
|
|
cnt[0] <= truncate(new_cnt);
|
|
endrule
|
|
|
|
(* no_implicit_conditions, fire_when_enabled *)
|
|
rule strobe (pulse);
|
|
pw.send();
|
|
endrule
|
|
|
|
method _read = pw._read;
|
|
method Action reset();
|
|
cnt[1] <= increment;
|
|
pw.send();
|
|
endmethod
|
|
endmodule
|
|
|
|
module mkStrobeRawRec(UInt#(n) hack, Integer increment, Integer max, Strobe ifc);
|
|
let register_width = ceil(log2(fromInteger(max)));
|
|
if (valueOf(n) < register_width) begin
|
|
UInt#(TAdd#(n, 1)) hack2 = 0;
|
|
let _ret <- mkStrobeRawRec(hack2, increment, max);
|
|
return _ret;
|
|
end
|
|
else begin
|
|
UInt#(n) hack2 = fromInteger(increment);
|
|
let _ret <- mkStrobeRaw(hack2, fromInteger(max));
|
|
return _ret;
|
|
end
|
|
endmodule
|
|
|
|
// 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");
|
|
|
|
// This is how many main clock ticks there are per strobe at the
|
|
// target frequency. For all but the simplest cases, this will
|
|
// include fractional clock cycles due to the two frequencies not
|
|
// dividing evenly.
|
|
Real target_ratio = fromInteger(clock_frequency)/fromInteger(target_frequency);
|
|
|
|
// At this point we could round the target ratio and instantiate a
|
|
// counter for that amount, but the resulting strobe frequency can
|
|
// be quite drastically off target (empirically I've seen up to 5%
|
|
// without trying).
|
|
//
|
|
// We can improve on this by counting in larger increments modulo
|
|
// some maximum, and strobing on every overflow. This will make the
|
|
// strobe period vary slightly by the main clock's reckoning,
|
|
// jittering around the "true" strobe time as error accumulates and
|
|
// is then subtracted back out.
|
|
Real target_error = 1/1000; // 0.1%
|
|
|
|
Integer incr;
|
|
Integer max;
|
|
Real actual_error = 1;
|
|
for (incr=1; incr<=32 && actual_error > target_error; incr=incr+1) begin
|
|
Real mul_ratio = fromInteger(incr)*target_ratio;
|
|
max = round(mul_ratio);
|
|
actual_error = abs(mul_ratio - fromInteger(max))/fromInteger(max);
|
|
end
|
|
|
|
// Exited the loop, so either we found a good counter width to hit
|
|
// the requested error, or we maxed out on a 64-bit counter.
|
|
if (actual_error > target_error)
|
|
error(sprintf("cannot construct a strobe running at %0dHz from a main clock of %0dHz without exceeding the target frequency error of %0d%%", target_frequency, clock_frequency, target_error*100));
|
|
|
|
// The loop over-incremented by 1 after finding an acceptable error amount.
|
|
incr = incr-1;
|
|
|
|
// Debug messages, uncomment if you're wondering what the logic
|
|
// above decided for your main clock and target strobe rate.
|
|
Bool show_debug = True;
|
|
if (show_debug) begin
|
|
Real actual_ratio = fromInteger(max)/fromInteger(incr);
|
|
Real actual_freq_low = fromInteger(clock_frequency)/fromInteger(ceil(actual_ratio));
|
|
Real actual_freq_high = fromInteger(clock_frequency)/fromInteger(floor(actual_ratio));
|
|
Real actual_freq_avg = fromInteger(clock_frequency)/actual_ratio;
|
|
messageM(sprintf("Strobe at %0dHz given clock at %0dHz:\n count by %0d mod %0d\n strobing every %d to %d cycles\n effective strobe frequency %0.2fHz to %0.2fHz\n avg frequency %0.2fHz (%0.2f%% error)", target_frequency, clock_frequency, incr, max, floor(actual_ratio), ceil(actual_ratio), actual_freq_low, actual_freq_high, actual_freq_avg, actual_error*100));
|
|
end
|
|
|
|
let _ret <- mkStrobeRawRec(UInt#(1) ' (0), incr, max);
|
|
return _ret;
|
|
endmodule
|
|
|
|
endpackage
|