1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
use rust_hdl_core::prelude::*;

use crate::{dff::DFF, dff_setup};

/// A [Strobe] generates a periodic pulse train, with a single clock-cycle wide pulse
/// at the prescribed frequency.  The argument [N] of the generic [Strobe<N>] is used
/// to size the counter that stores the internal delay value.  Unfortunately, Rust const
/// generics are currently not good enough to compute [N] on the fly.  However, a compile
/// time assert ensures that the number of clock cycles between pulses does not overflow
/// the [N]-bit wide register inside the [Strobe].
#[derive(Clone, Debug, LogicBlock)]
pub struct Strobe<const N: usize> {
    /// Set this to true to enable the pulse train.
    pub enable: Signal<In, Bit>,
    /// This is the strobing signal - it will fire for 1 clock cycle such that the strobe frequency is generated.
    pub strobe: Signal<Out, Bit>,
    /// The clock that drives the [Strobe].  All signals are synchronous to this clock.
    pub clock: Signal<In, Clock>,
    threshold: Constant<Bits<N>>,
    counter: DFF<Bits<N>>,
}

impl<const N: usize> Strobe<N> {
    /// Generate a [Strobe] widget that can be used in a RustHDL circuit.
    ///
    /// # Arguments
    ///
    /// * `frequency`: The frequency (in Hz) of the clock signal driving the circuit.
    /// * `strobe_freq_hz`: The desired frequency in Hz of the output strobe.  Note that
    /// the strobe frequency will be rounded to something that can be obtained by dividing
    /// the input clock by an integer.  As such, it may not produce exactly the desired
    /// frequency, unless `frequency`/`strobe_freq_hz` is an integer.
    ///
    /// returns: Strobe<{ N }>
    ///
    /// # Examples
    ///
    /// See [BlinkExample] for an example.
    pub fn new(frequency: u64, strobe_freq_hz: f64) -> Self {
        let clock_duration_femto = freq_hz_to_period_femto(frequency as f64);
        let strobe_interval_femto = freq_hz_to_period_femto(strobe_freq_hz);
        let interval = strobe_interval_femto / clock_duration_femto;
        let threshold = interval.round() as u64;
        assert!((threshold as u128) < (1_u128 << (N as u128)));
        assert!(threshold > 2);
        Self {
            enable: Signal::default(),
            strobe: Signal::default(),
            clock: Signal::default(),
            threshold: Constant::new(threshold.into()),
            counter: Default::default(),
        }
    }
}

impl<const N: usize> Logic for Strobe<N> {
    #[hdl_gen]
    fn update(&mut self) {
        // Connect the counter clock to my clock
        dff_setup!(self, clock, counter);
        if self.enable.val() {
            self.counter.d.next = self.counter.q.val() + 1;
        }
        self.strobe.next = self.enable.val() & (self.counter.q.val() == self.threshold.val());
        if self.strobe.val() {
            self.counter.d.next = 1.into();
        }
    }
}