Skip to content

Commit 96b012a

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 241c6c1 commit 96b012a

File tree

2 files changed

+89
-1
lines changed

2 files changed

+89
-1
lines changed

clash-protocols/src/Protocols/BiDf.hs

Lines changed: 37 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,38 @@ 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+
<| repeatWithIndexC (\i -> Df.map (\x -> (i,x)))
134+
-< reqs
135+
136+
activeN <- Df.map fst -< fwd1
137+
resps <- Df.route <| Df.zip -< (activeN, resp)
138+
req <- Df.map snd -< fwd0
139+
idC -< (resps, req)

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

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33

44
module Tests.Protocols.BiDf (tests) where
55

6+
import Prelude as P
7+
import qualified Data.List as L
8+
69
-- clash-prelude
7-
import Clash.Prelude
10+
import Clash.Prelude as C
811
import qualified Clash.Sized.Vector as Vector
912
import Clash.Hedgehog.Sized.Vector
1013

@@ -51,6 +54,54 @@ prop_loopback_id =
5154
BiDf.loopback id -< biDf
5255
idC -< resp
5356

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

0 commit comments

Comments
 (0)