gary/lib/Strobe.bsv

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