Implementing Your Own Combinators
The HazardFlow HDL standard library provides some primitive combinators, however we need to implement our custom combinators sometimes for specific logic. In fact, the HazardFlow HDL standard library is also implemented in the same way as what we will introduce in this section.
We introduced the masked_merge combinator specification in the Tutorial section,
but we did not give out the concrete implementation.
The masked_merge combinator is a custom combinator which is not provided by the HazardFlow HDL standard library,
thus we need to implement its logic by ourselves.
Implementation
To define a custom combinator for interface type [Vr<P>; N], we need to define a custom trait first.
/// Masked merge trait
trait MaskedMergeExt<P: Copy + Default, const N: usize>: Interface
where [(); clog2(N)]:
{
    /// Hazard type
    type EH: Hazard;
    /// Fair Mux
    fn masked_merge(self) -> I<Self::EH, { Dep::Demanding }>;
}
- The custom trait's name is 
MaskedMergeExt.- It specifies the general payload type 
Pneed to be constrained by theCopyandDefaulttraits. const N: usizespecifies the number of ingress interfaces.- The 
MaskedMergeExtneed to extend theInterface trait, since we need toimplthis trait later for Implementing our custom combinator. where [(); clog2(N)]is telling the HazardFlow HDL compiler thatclog2(N)is a constant.
 - It specifies the general payload type 
 - We define the egress hazard as 
Hazardtype, which is a hazard protocol with a given payload, resolver, and thereadyfunction. fn masked_merge(self) -> I<Self::EH, { Dep::Demanding }>defines the combinator's namemasked_mergeand specifies the egress hazard isEH.
We can define the combinational logic now.
impl<P: Copy + Default, const N: usize> MaskedMergeExt<P, N> for [Vr<P>; N]
where [(); clog2(N)]:
{
    type EH = VrH<(P, U<{ clog2(N) }>), Array<bool, N>>;
    fn masked_merge(self) -> I<Self::EH, { Dep::Demanding }> {
        unsafe {
            self.fsm::<I<VrH<(P, U<{ clog2(N) }>), Array<bool, N>>, { Dep::Demanding }>, ()>((), |ip, er, s| {
                if !er.ready {
                    let ir = Ready::new(false, ()).repeat();
                    let ep = None;
                    return (ep, ir, s);
                }
                let ep_idx = ip.zip(er.inner).find_idx(|(p, selected)| p.is_some() && !selected);
                let ep = if let Some(idx) = ep_idx { Some((ip[idx].unwrap(), idx)) } else { None };
                let ir = Ready::invalid().repeat::<N>().set_cond(ep.is_some(), ep_idx.unwrap(), Ready::valid(()));
                (ep, ir, s)
            })
        }
    }
}
- We 
implthe custom traitMaskedMergeExtfor the compound interface type[Vr<P>; N]. - We define the egress hazard as 
VrH<(P, U<{ clog2(N) }>), Array<bool, N>>- The egress interface is a valid-ready interface with valid-ready hazard.
 U<{ clog2(N) }>is the bit representation of the index of the ingress interfaces.- The inner resolver is 
Array<bool, N>which indicates the index of the ingress interfaces are present in the current queue. 
 unsafecode block is necessary for Implementing your own custom combinator, sincefsmfunction need to be in theunsafecode block.- The 
fsmfunction specifies the egress interface type isI<VrH<(P, U<{ clog2(N) }>), Array<bool, N>. - The 
fsmfunction takes two inputs and returns the egress interface.- The initial state of the combinator.
 - An anonymous function takes three inputs.
- The ingress payload 
ip. The type isArray<HOption<P>, N>, which is all the ingress interfaces' payload. - The egress resolver 
er. The type isReady<Array<bool, N>>, which is a ready signal with an array that indicates the index of the ingress interfaces present in the current queue. - The initial state 
s. The type is simply a(), since we do not have a state in this combinator. 
 - The ingress payload 
 - The anonymous function returns a tuple including:
- The egress payload 
ep. The type isHOption<(P, Array<bool, _>)>, which is the actual data we want to transfer and also the index of the ingress interface. Note that the bit representation of an unsigned integer is an array ofbool. - The ingress resolver 
ip. The type isArray<Ready<()>, N>, which is an array of ready signals with sizeN. It is a collection of the ready signal for each ingress interface. 
 - The egress payload 
 
 - If the egress resolver ready signal is 
false, which means the egress interface is not ready to transfer the payload, we are not transferring any payload.- We set the ingress resolver's ready signal as 
false - We set the egress payload as 
None. 
 - We set the ingress resolver's ready signal as 
 - If the egress resolver ready signal is 
true, which means the egress interface is ready to transfer the payload.- We find the first index of the ingress interfaces whose payload is 
Some(P)and also not in the current queue.zipfunction zips two arrays and returns a new array whose element is a tuple of both elements from the two arrays.find_idxfinds the index of first element that satisfies given condition.
 - We set the egress payload as a tuple containing the selected ingress interface's actual payload and its index.
 - We set the selected ingress resolver ready signal as 
trueand the rest of the ingress interfaces' ready signal asfalse. 
 - We find the first index of the ingress interfaces whose payload is