Skip to content

Commit 7bfa358

Browse files
committed
BiDf: Introduce fanin
This is a useful operation for servicing requests from a number of sources with a single sink.
1 parent 7faf4a7 commit 7bfa358

File tree

2 files changed

+79
-0
lines changed

2 files changed

+79
-0
lines changed

clash-protocols/src/Protocols/BiDf.hs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ module Protocols.BiDf (
1313
loopback,
1414
-- * Mapping
1515
dimap,
16+
-- * Fan-in
17+
fanin
1618
) where
1719

1820
import Prelude ()
@@ -100,3 +102,40 @@ dimap f g = circuit $ \biDf -> do
100102
resp' <- Df.map g -< resp
101103
(biDf', resp) <- fromDfs -< req'
102104
idC -< biDf'
105+
106+
-- | Merge a number of 'BiDf's, preferring requests from the last channel.
107+
fanin
108+
:: forall n dom req resp.
109+
( KnownNat n
110+
, 1 <= n
111+
, NFDataX req
112+
, NFDataX resp
113+
, HiddenClockResetEnable dom
114+
)
115+
=> Circuit (Vec n (BiDf dom req resp)) (BiDf dom req resp)
116+
fanin = fromSignals $ \(upFwds, (reqAck, respData)) ->
117+
let reqDatas :: Vec n (Signal dom (Df.Data req))
118+
reqDatas = map fst upFwds
119+
respAcks :: Vec n (Signal dom Ack)
120+
respAcks = map snd upFwds
121+
122+
((reqAcks, respAck), (respDatas, reqData)) =
123+
toSignals fanin' ((reqDatas, respData), (respAcks, reqAck))
124+
in (zip reqAcks respDatas, (reqData, respAck))
125+
where
126+
fanin'
127+
:: Circuit (Vec n (Df dom req), Df dom resp)
128+
(Vec n (Df dom resp), Df dom req)
129+
fanin' = circuit $ \(reqs, resp) -> do
130+
[fwd0, fwd1]
131+
<- Df.fanout
132+
<| Df.roundrobinCollect @n Df.Parallel
133+
<| Df.unbundleVec
134+
<| Df.map (zip indicesI)
135+
<| Df.bundleVec
136+
-< reqs
137+
138+
activeN <- Df.map fst -< fwd1
139+
resps <- Df.route <| Df.zip -< (activeN, resp)
140+
req <- Df.map snd -< fwd0
141+
idC -< (resps, req)

clash-protocols/tests/Tests/Protocols/BiDf.hs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,46 @@ prop_loopback_id =
5151
BiDf.loopback id -< biDf
5252
idC -< resp
5353

54+
-- | Test that 'BiDf.fanin' on a single 'BiDf' channel behaves as an identity.
55+
prop_fanin_id :: Property
56+
prop_fanin_id =
57+
idWithModelSingleDomain @System defExpectOptions gen (\_ _ _ -> id) (exposeClockResetEnable impl)
58+
where
59+
gen :: Gen [Int]
60+
gen = Gen.list (Range.linear 0 10) (Gen.integral (Range.linear 0 100))
61+
62+
impl :: forall dom a. (HiddenClockResetEnable dom, NFDataX a)
63+
=> Circuit (Df dom a) (Df dom a)
64+
impl = circuit $ \req -> do
65+
(biDf, resp) <- BiDf.fromDfs -< req
66+
BiDf.loopback id <| BiDf.fanin @1 -< [biDf]
67+
idC -< resp
68+
69+
-- | Test that 'BiDf.fanin' on a number of 'BiDf' channels behaves as an
70+
-- identity on each channel.
71+
prop_fanin :: Property
72+
prop_fanin =
73+
idWithModelSingleDomain @System expectOpts gen (\_ _ _ -> id) (exposeClockResetEnable impl)
74+
where
75+
expectOpts = defExpectOptions
76+
77+
gen :: Gen (Vec 10 [Int])
78+
gen = genVec @Gen @10 $ Gen.list (Range.linear 0 10) (Gen.integral (Range.linear 0 100))
79+
80+
impl :: forall dom. (HiddenClockResetEnable dom)
81+
=> Circuit (Vec 10 (Df dom Int)) (Vec 10 (Df dom Int))
82+
impl = circuit $ \reqs -> do
83+
(biDfs, resps) <- unbundleC <| repeatC BiDf.fromDfs -< reqs
84+
BiDf.loopback id <| BiDf.fanin @10 -< biDfs
85+
idC -< resps
86+
87+
unbundleC :: forall n a b. Circuit (Vec n (a, b)) (Vec n a, Vec n b)
88+
unbundleC = fromSignals $ \(fwd, (bwdA, bwdB)) ->
89+
let fwdA :: Vec n (Fwd a)
90+
fwdB :: Vec n (Fwd b)
91+
(fwdA, fwdB) = Vector.unzip fwd
92+
in (Vector.zip bwdA bwdB, (fwdA, fwdB))
93+
5494
tests :: TestTree
5595
tests =
5696
$(testGroupGenerator)

0 commit comments

Comments
 (0)