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
//! Merge.

use super::*;

/// Extension trait for `merge` and `cmerge`.
// Semantically `ED` should be an associated constant instead of a const parameter, but the associated constant version
// doesn't compile.
pub trait MergeExt<const N: usize, const ED: Dep>: Interface
where [(); clog2(N)]:
{
    /// Hazard specification of egress interface.
    type EH: Hazard;

    /// A variation of [`cmerge`](merge) that does not output a control signal that indicates which interface is
    /// selected. See [`cmerge`](merge) for more information.
    fn merge(self) -> I<Self::EH, ED> {
        self.cmerge().into_inner()
    }

    /// Control merge.
    fn cmerge(self) -> I<SelH<Self::EH, N>, ED>;
}

impl<const N: usize, P: Copy, R: Copy, const D: Dep> MergeExt<N, D> for [I<ValidH<P, R>, D>; N]
where [(); clog2(N)]:
{
    type EH = ValidH<P, R>;

    /// Merges `N` `ValidH` hazard interfaces with a control signal that outputs which interface is selected.
    ///
    /// - Payloads: Selects the first ingress interface with a valid payload and outputs that payload.
    /// - Resolver: Duplicated to multiple interfaces.
    ///
    /// | Interface | Ingress                | Egress                      |
    /// | :-------: | ---------------------- | --------------------------- |
    /// |  **Fwd**  | `Array<HOption<P>, N>` | `(HOption<P>, BoundedU<N>)` |
    /// |  **Bwd**  | `Array<R, N>`          | `R`                         |
    fn cmerge(self) -> I<SelH<ValidH<P, R>, N>, D> {
        unsafe {
            self.fsm::<I<SelH<ValidH<P, R>, N>, D>, ()>((), |ip, er, s| {
                let sel = ip.find_idx(|ele| ele.is_some());
                let ep = sel.map(|sel| (ip[sel].unwrap(), BoundedU::new(sel)));
                let ir = er.repeat();
                (ep, ir, s)
            })
        }
    }
}

impl<const N: usize, H: Hazard, const D: Dep> MergeExt<N, { Dep::Demanding }> for [I<AndH<H>, D>; N]
where [(); clog2(N)]:
{
    type EH = AndH<H>;

    /// Merges `N` `AndH<H>` hazard interfaces with a control signal that outputs which interface is selected.
    ///
    /// - Payloads: Selects the first ingress interface on which a transfer can happen
    ///     (`ip[sel].is_some_and(|p| AndH::<H>::ready(p, er))`), and outputs the selected interface's payload.
    /// - Resolver: If the index of the selected interface is `sel`, the ingress ready signals for the interfaces
    ///     `0..=sel` is true, and `(sel + 1)..N` is false. This is to ensure that a transfer happens only on the
    ///     interface `sel`. The inner value `H::R` of the resolver is duplicated to multiple interfaces.
    ///
    /// | Interface | Ingress                   | Egress                         |
    /// | :-------: | ------------------------- | ------------------------------ |
    /// |  **Fwd**  | `Array<HOption<H::P>, N>` | `(HOption<H::P>, BoundedU<N>)` |
    /// |  **Bwd**  | `Array<Ready<H::R>, N>`   | `Ready<H::R>`                  |
    fn cmerge(self) -> I<SelH<AndH<H>, N>, { Dep::Demanding }> {
        // TODO: Write safety comments
        unsafe {
            self.fsm::<I<SelH<AndH<H>, N>, { Dep::Demanding }>, ()>((), |ip, er, s| {
                // Logic for ingress hazard calculation
                //
                // `ir[i].ready` is true if and only if forall j < i, ingress[j] is not transferrable.
                //
                // NOTE: We have to give `er.repeat()`, not `Ready::invalid().repeat()`.
                // This is because the ingress interface can be demanding, which should see the `er` and set the valid bit.
                let (ir, sel) = ip.enumerate().fold((er.repeat::<N>(), None), |(acc_ir, xferred_idx), (idx, ele)| {
                    if xferred_idx.is_some() {
                        // If there exists `j < i` such that `ingress[j]` is transferrable, then `ingress[i]` is not transferrable.
                        //
                        // NOTE: We do not use `Ready::invalid()` because the ingress interface can be demanding, which might see the `er` in the ready function which should not receive don't-care value as `inner`.
                        // TODO: After changing the `Ready::invalid` api to receive `inner` as parameter, we should change this to `Ready::invalid`.
                        (acc_ir.set(idx, Ready::new(false, er.inner)), xferred_idx)
                    } else {
                        let xferred_sel = ele.and_then(|ele| if AndH::<H>::ready(ele, er) { Some(idx) } else { None });
                        (acc_ir, xferred_sel)
                    }
                });

                // We can safely unwrap `sel` since it is guaranteed to be `Some` by the above logic.
                //
                // If `sel` is `Some`, then `ip[sel]` is guaranteed to be `Some`.
                (sel.map(|sel| (ip[sel].unwrap(), BoundedU::new(sel))), ir, s)
            })
        }
    }
}