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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//! Interface.

use hazardflow_macro::magic;

use super::*;
use crate::prelude::*;

/// Interface trait.
#[must_use]
pub trait Interface: Sized {
    /// Forward signal.
    type Fwd: Copy;

    /// Backward signal.
    type Bwd: Copy;

    /// A generic FSM combinator.
    ///
    /// We assume that the function `f` is combinational logic. The returned egress payload and ingress resolver are
    /// immediately propagated, and the state is updated to the returned next state from the next cycle.
    ///
    /// # Safety
    ///
    /// When using this combinator, you need to guarantee that it satisfies the specification of the interface's
    /// protocol.
    ///
    /// In particular, for a hazard interface [`I<H, D>`], you must follow the specification described in the "Safety"
    /// section of [`I::fsm`].
    ///
    /// # How it is compiled?
    ///
    /// In the HazardFlow compiler, the fsm function is not actually executed (which would lead to a panic).
    ///
    /// Instead, the HazardFlow compiler captures the [High-level IR](https://rustc-dev-guide.rust-lang.org/hir.html)
    /// generated by the Rust compiler and extracts information about the ingress/egress/state types (`Self`, `E`, `S`)
    /// and the arguments (`init_state`, `f`) of the fsm function.
    ///
    /// Using this information, the HazardFlow compiler generates the corresponding Verilog code.
    ///
    /// # Type parameters
    ///
    /// - `Self`: The ingress interface type.
    /// - `E`: The egress interface type.
    /// - `S`: The state type.
    ///
    /// # Parameters
    ///
    /// - `self`: The ingress interface.
    /// - `init_state`: The initial state.
    ///     - Whenever `rst` signal is turned on, the state will be initialized to this value.
    ///     - For example, set `init_state` as `None` for a state with an `Option<_>` type.
    /// - `f`: Output calculation and state transition logic. If `let (ep, ir, s_next) = f(ip, er, s)`,
    ///     - `ip`: The ingress payload.
    ///     - `er`: The egress resolver.
    ///     - `s`: The current state.
    ///     - `ep`: The egress payload.
    ///     - `ir`: The ingress resolver.
    ///     - `s_next`: The next state.
    #[magic(interface::fsm)]
    unsafe fn fsm<E: Interface, S: Copy>(
        self,
        _init_state: S,
        _f: impl Fn(Self::Fwd, E::Bwd, S) -> (E::Fwd, Self::Bwd, S),
    ) -> E {
        compiler_magic!()
    }

    /// Combines the module to the given interface and returns the egress interface.
    fn comb<E: Interface>(self, m: impl FnOnce(Self) -> E) -> E {
        m(self)
    }
}

impl Interface for () {
    type Bwd = ();
    type Fwd = ();
}

impl<If: Interface, const N: usize> Interface for [If; N] {
    type Bwd = Array<If::Bwd, N>;
    type Fwd = Array<If::Fwd, N>;
}

macro_rules! impl_interface_tuple {
    ($($a:ident)+) => {
        impl<$($a: Interface,)+> Interface for ($($a,)+) {
            type Fwd = ($($a::Fwd,)+);
            type Bwd = ($($a::Bwd,)+);
        }
    }
}

impl_interface_tuple! { If1 }
impl_interface_tuple! { If1 If2 }
impl_interface_tuple! { If1 If2 If3 }
impl_interface_tuple! { If1 If2 If3 If4 }
impl_interface_tuple! { If1 If2 If3 If4 If5 }
impl_interface_tuple! { If1 If2 If3 If4 If5 If6 }
impl_interface_tuple! { If1 If2 If3 If4 If5 If6 If7 }
impl_interface_tuple! { If1 If2 If3 If4 If5 If6 If7 If8 }
impl_interface_tuple! { If1 If2 If3 If4 If5 If6 If7 If8 If9 }
impl_interface_tuple! { If1 If2 If3 If4 If5 If6 If7 If8 If9 If10 }
impl_interface_tuple! { If1 If2 If3 If4 If5 If6 If7 If8 If9 If10 If11 }
impl_interface_tuple! { If1 If2 If3 If4 If5 If6 If7 If8 If9 If10 If11 If12 }

/// Applies the function `f` to the provided interfaces `is`.
///
/// NOTE: The function `f` must be a function pointer (type `fn`). It `f` is not a function pointer, it may cause internal
///       compile error.
#[macro_export]
macro_rules! array_map {
    ($is: ident, $f: expr) => {{
        let m = seq(from_fn(move |i, x: ()| ($f(i), x)));
        let (os, _) = m($is, ());
        os
    }};
}