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