diff --git a/hazardflow-designs/src/std/combinators/mux.rs b/hazardflow-designs/src/std/combinators/mux.rs index 4fc3307..841a1ea 100644 --- a/hazardflow-designs/src/std/combinators/mux.rs +++ b/hazardflow-designs/src/std/combinators/mux.rs @@ -72,3 +72,38 @@ where [(); clog2(N)]: } } } + +impl MuxExt<2> for (I, D>, I, D>) { + type E = I, D>; + + /// Muxes two `VrH` hazard interfaces based on `cntl`. + /// + /// `cntl` selects which ingress interface to connect to the egress interface. + /// + /// - Payloads: Outputs the payload of the interface selected by `cntl`. + /// - Resolver: The selected interface's resolver follows the egress resolver. All the other resolvers are invalid. + /// + /// | Interface | Ingress | Egress | + /// | :-------: | -------------------------- | ----------------- | + /// | **Fwd** | `(HOption

, HOption

)` | `HOption

` | + /// | **Bwd** | `(Ready, Ready)` | `Ready<(R1, R2)>` | + fn mux(self, cntl: Valid>) -> I, D> { + unsafe { + (self, cntl).fsm::, D>, ()>((), |((ip1, ip2), sel), er, s| { + let ep = sel.and_then(|sel| if sel == 0.into_u() { ip1 } else { ip2 }); + let ir1 = if sel.filter(|p| p == 0.into_u()).is_some() { + er.map(|r| r.0) + } else { + Ready::new(false, er.inner.0) + }; + let ir2 = if sel.filter(|p| p == 1.into_u()).is_some() { + er.map(|r| r.1) + } else { + Ready::new(false, er.inner.1) + }; + + (ep, ((ir1, ir2), ()), s) + }) + } + } +} diff --git a/hazardflow-designs/src/std/valid_ready/mod.rs b/hazardflow-designs/src/std/valid_ready/mod.rs index df0b3a7..319f4ca 100644 --- a/hazardflow-designs/src/std/valid_ready/mod.rs +++ b/hazardflow-designs/src/std/valid_ready/mod.rs @@ -36,21 +36,23 @@ pub type Vr = I, D>; /// | |<--------------------- bool /// +-----+ /// ``` -pub fn attach_ready( - m: impl FnOnce(I, D1>) -> I, D2>, -) -> impl FnOnce(I, D1>) -> I, D2> { - |i: I, D1>| -> I, D2> { - let (i, ready) = unsafe { - Interface::fsm::<(I, D1>, I, { Dep::Helpful }>), ()>( +pub fn attach_ready( + m: impl FnOnce(I, { Dep::Helpful }>) -> I, { Dep::Helpful }>, +) -> impl FnOnce(I, { Dep::Helpful }>) -> I, { Dep::Helpful }> { + |i: I, { Dep::Helpful }>| -> I, { Dep::Helpful }> { + // `dummy` is used for additional ready signal. + let (i, dummy) = unsafe { + Interface::fsm::<(I, { Dep::Helpful }>, I, { Dep::Helpful }>), ()>( i, (), |ip, (er1, er2), s| ((ip, None), Ready::new(er1.ready & er2, er1.inner), s), ) }; - let e = i.comb(m); - - unsafe { (e, ready).fsm::, D2>, ()>((), |(ip, _), er, s| (ip, (er.inner, er.ready), s)) } + unsafe { + (i.comb(m), dummy) + .fsm::, { Dep::Helpful }>, ()>((), |(ip, _), er, s| (ip, (er.inner, er.ready), s)) + } } } @@ -66,29 +68,17 @@ pub fn attach_ready( - m: impl FnOnce(Vr) -> Vr, -) -> impl FnOnce(I, D>) -> I, ED> { - |i: I, D>| -> I, ED> { - // TODO: Need to consider `m` need multi-cycle - let (payload, hazard) = unsafe { - Interface::fsm::<(Vr, I, { Dep::Helpful }>), ()>(i, (), |ip, (er1, er2), s| { - let ir = Ready::new(er1.ready, er2); - // let ep1 = ip.filter(|ip| ReadyH::::ready(ip, ir)); - let ep1 = ip; - let ep2 = Some(()); - ((ep1, ep2), ir, s) - }) - }; +pub fn attach_resolver( + m: impl FnOnce(Vr

) -> Vr, +) -> impl FnOnce(I, { Dep::Helpful }>) -> I, { Dep::Helpful }> { + |i: I, { Dep::Helpful }>| -> I, { 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(); - unsafe { - (payload.comb(m), hazard).fsm::, ED>, ()>((), |(ip1, _), er, s| { - let ir1 = er.map(|_| ()); - let ir2 = er.inner; - let ep = ip1; - (ep, (ir1, ir2), s) - }) - } + // Always transferred from the first interface. + (i.comb(m), dummy.map(|_| unsafe { x::() })) + .mux(Valid::constant(0.into_u())) + .map_resolver_inner::(|r| ((), r)) } } @@ -104,31 +94,19 @@ pub fn attach_resolver( /// bool <--------------------| |<--------------------- bool /// +-----+ /// ``` -pub fn attach_payload( - m: impl FnOnce(Vr) -> Vr, -) -> impl FnOnce(Vr<(P, AP), D>) -> Vr<(EP, AP), ED> { - |i: Vr<(P, AP), D>| -> Vr<(EP, AP), ED> { - // TODO: Need to consider `m` need multi-cycle - let (i1, i2) = unsafe { - Interface::fsm::<(Vr, Vr), ()>(i, (), |ip, (er1, _er2), s| { - let ep1 = ip.map(|(p, _)| p); - let ep2 = ip.map(|(_, ap)| ap); - // let ir = Ready::new(er1.ready & er2.ready, ()); - let ir = er1; - ((ep1, ep2), ir, s) - }) - }; +/// +/// 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( + m: impl FnOnce(Vr

) -> Vr, +) -> 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 e1 = i1.comb(m); - let e2 = i2; // TODO: We should be add `reg_fwd` which have flow property. + 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. - unsafe { - (e1, e2).fsm::, ()>((), |(ip1, ip2), er, s| { - let ep = ip1.zip(ip2); - let ir1 = er; - let ir2 = er; - (ep, (ir1, ir2), s) - }) - } + (i.comb(m), dummy).join() } }