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
//! Valid-ready protocol.

use super::hazard::*;
use super::interface::*;
use super::valid::*;
use crate::prelude::*;
use crate::std::*;

/// Hazard for valid-ready hazard interface.
///
/// - `Hazard::P` = `P`
/// - `Hazard::R` = `Ready<R>`
pub type VrH<P, R = ()> = AndH<ValidH<P, R>>;

/// Valid-ready interface.
///
/// - `Interface::Fwd` = `HOption<P>`
/// - `Interface::Bwd` = `Ready<()>`
pub type Vr<P, const D: Dep = { Dep::Helpful }> = I<VrH<P>, D>;

/// Attaches a ready signal to the module `m`'s egress interface.
///
/// The returned module's ingress ready signal is calculated as "`m`'s ingress ready signal" AND "attached ready
/// signal".
///
/// The returned module `attach_ready(m)` looks like the following:
///
/// ```text
///   (Ingress)                                  (Egress)
///                                   +-----+
/// HOption<P1> --------------------->|     |--> HOption<P2>
///          R1 <---------------------|  m  |<-- R2
///                +-----+            |     |
///                |     |<-- bool <--|     |
///        bool <--| AND |            +-----+
///                |     |<--------------------- bool
///                +-----+
/// ```
pub fn attach_ready<P1: Copy, R1: Copy, P2: Copy, R2: Copy>(
    m: impl FnOnce(I<VrH<P1, R1>, { Dep::Helpful }>) -> I<ValidH<P2, R2>, { Dep::Helpful }>,
) -> impl FnOnce(I<VrH<P1, R1>, { Dep::Helpful }>) -> I<VrH<P2, R2>, { Dep::Helpful }> {
    |i: I<VrH<P1, R1>, { Dep::Helpful }>| -> I<VrH<P2, R2>, { Dep::Helpful }> {
        // `dummy` is used for additional ready signal.
        let (i, dummy) = unsafe {
            Interface::fsm::<(I<VrH<P1, R1>, { Dep::Helpful }>, I<ValidH<(), bool>, { Dep::Helpful }>), ()>(
                i,
                (),
                |ip, (er1, er2), s| ((ip, None), Ready::new(er1.ready & er2, er1.inner), s),
            )
        };

        unsafe {
            (i.comb(m), dummy)
                .fsm::<I<VrH<P2, R2>, { Dep::Helpful }>, ()>((), |(ip, _), er, s| (ip, (er.inner, er.ready), s))
        }
    }
}

/// Attaches an additional resolver to the valid-ready circuit `m`.
///
/// The returned module `attach_resolver(m)` looks like the following:
///
/// ```text
///  (Ingress)               (Egress)
///               +-----+
/// HOption<P> -->|  m  |--> HOption<EP>
///       bool <--|     |<-- bool
///               +-----+
///          R <------------ R
/// ```
pub fn attach_resolver<P: Copy, EP: Copy, R: Copy>(
    m: impl FnOnce(Vr<P>) -> Vr<EP>,
) -> impl FnOnce(I<VrH<P, R>, { Dep::Helpful }>) -> I<VrH<EP, R>, { Dep::Helpful }> {
    |i: I<VrH<P, R>, { Dep::Helpful }>| -> I<VrH<EP, R>, { Dep::Helpful }> {
        // Always transferred to the first interface, `dummy` is used for additional resolver.
        let (i, dummy) = i.map(|p| (p, BoundedU::new(0.into_u()))).map_resolver_inner::<((), R)>(|(_, r)| r).branch();

        // Always transferred from the first interface.
        (i.comb(m), dummy.map(|_| unsafe { x::<EP>() }))
            .mux(Valid::constant(0.into_u()))
            .map_resolver_inner::<R>(|r| ((), r))
    }
}

/// Attaches an additional payload to the valid-ready circuit `m`.
///
/// The returned module `attach_payload(m)` looks like the following:
///
/// ```text
///        (Ingress)                                                    (Egress)
///                    +--> AP -----------------------------> AP --+
///                    |                  +-----+                  |
/// HOption<(P, AP)> --+--> HOption<P> -->|  m  |--> HOption<EP> --+--> HOption<(EP, AP)>
///             bool <--------------------|     |<--------------------- bool
///                                       +-----+
/// ```
///
/// NOTE: Current implementation considered only when `m` returns its egress at the same cycle as takes its ingress.
// TODO: Consider when `m` takes one or multi cycles.
pub fn attach_payload<P: Copy, EP: Copy, AP: Copy>(
    m: impl FnOnce(Vr<P>) -> Vr<EP>,
) -> impl FnOnce(Vr<(P, AP)>) -> Vr<(EP, AP)> {
    |i: Vr<(P, AP)>| -> Vr<(EP, AP)> {
        // `dummy` is used for additional payload.
        let (i, dummy) = i.lfork_uni();

        let i = i.map(|(p, _)| p);
        let dummy = dummy.map(|(_, ap)| ap); // TODO: Add `reg_fwd` with flow property to consider `m` takes one or multi cycles.

        (i.comb(m), dummy).join()
    }
}