Skip to content

Commit 7faf4a7

Browse files
committed
Introduce BiDf protocol
This introduces a new protocol, `BiDf`, which captures the common pattern of a request `Df` channel paired with a `Df` for returning corresponding responses.
1 parent 64a0a58 commit 7faf4a7

File tree

4 files changed

+163
-1
lines changed

4 files changed

+163
-1
lines changed

clash-protocols/clash-protocols.cabal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ library
144144
Protocols.Axi4.WriteAddress
145145
Protocols.Axi4.WriteData
146146
Protocols.Axi4.WriteResponse
147+
Protocols.BiDf
147148
Protocols.Df
148149
Protocols.DfConv
149150
Protocols.Hedgehog
@@ -175,6 +176,7 @@ test-suite unittests
175176
main-is: unittests.hs
176177
other-modules:
177178
Tests.Protocols
179+
Tests.Protocols.BiDf
178180
Tests.Protocols.Df
179181
Tests.Protocols.DfConv
180182
Tests.Protocols.Avalon
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
{-# OPTIONS_GHC -fplugin Protocols.Plugin #-}
2+
3+
-- | Bi-directional request/response-style 'Df' channels.
4+
module Protocols.BiDf (
5+
BiDf,
6+
-- * Conversion
7+
fromDfs,
8+
toDfs,
9+
fromBiDf,
10+
toBiDf,
11+
-- * Trivial combinators
12+
void,
13+
loopback,
14+
-- * Mapping
15+
dimap,
16+
) where
17+
18+
import Prelude ()
19+
20+
import Clash.Prelude
21+
22+
import Protocols
23+
import qualified Protocols.Df as Df
24+
25+
-- | A 'Protocol' allowing requests to be passed downstream, with corresponding
26+
-- responses being passed back upstream. Responses are provided in the order that
27+
-- their corresponding requests were submitted.
28+
--
29+
-- *Correctness conditions*
30+
--
31+
-- - The response channel must not produce a value before the request channel
32+
-- has produced a value.
33+
--
34+
-- - Each request must be paired with exactly one response.
35+
--
36+
-- - Responses must be issued in the order that their corresponding requests arrived.
37+
--
38+
-- - Both the request and response channels must obey usual 'Df' correctness
39+
-- conditions.
40+
--
41+
-- - There must not be a combinational path from the request channel to the
42+
-- response channel.
43+
--
44+
type BiDf dom req resp =
45+
(Df dom req, Reverse (Df dom resp))
46+
47+
-- | Convert a circuit of 'Df's to a 'BiDf' circuit.
48+
toBiDf
49+
:: Circuit (Df dom req) (Df dom resp)
50+
-> Circuit (BiDf dom req resp) ()
51+
toBiDf c = circuit $ \bidf -> do
52+
resp <- c -< req
53+
req <- toDfs -< (bidf, resp)
54+
idC -< ()
55+
56+
-- | Convert a 'BiDf' circuit to a circuit of 'Df's.
57+
fromBiDf
58+
:: Circuit (BiDf dom req resp) ()
59+
-> Circuit (Df dom req) (Df dom resp)
60+
fromBiDf c = circuit $ \req -> do
61+
(biDf, resp) <- fromDfs -< req
62+
c -< biDf
63+
idC -< resp
64+
65+
-- | Convert a pair of a request and response 'Df`s into a 'BiDf'.
66+
toDfs :: Circuit (BiDf dom req resp, Df dom resp) (Df dom req)
67+
toDfs = fromSignals $ \(~((reqData, respAck), respData), reqAck) ->
68+
(((reqAck, respData), respAck), reqData)
69+
70+
-- | Convert a 'BiDf' into a pair of request and response 'Df`s.
71+
fromDfs :: Circuit (Df dom req) (BiDf dom req resp, Df dom resp)
72+
fromDfs = fromSignals $ \(reqData, ~((reqAck, respData), respAck)) ->
73+
(reqAck, ((reqData, respAck), respData))
74+
75+
-- | Ignore all requests, never providing responses.
76+
void :: (HiddenClockResetEnable dom) => Circuit (BiDf dom req resp') ()
77+
void = circuit $ \biDf -> do
78+
req <- toDfs -< (biDf, resp)
79+
resp <- Df.empty -< ()
80+
Df.void -< req
81+
82+
-- | Return mapped requests as responses.
83+
loopback
84+
:: (HiddenClockResetEnable dom, NFDataX req)
85+
=> (req -> resp)
86+
-> Circuit (BiDf dom req resp) ()
87+
loopback f = circuit $ \biDf -> do
88+
req <- toDfs -< (biDf, resp)
89+
resp <- Df.map f <| Df.registerFwd -< req
90+
idC -< ()
91+
92+
-- | Map both requests and responses.
93+
dimap
94+
:: (req -> req')
95+
-> (resp -> resp')
96+
-> Circuit (BiDf dom req resp') (BiDf dom req' resp)
97+
dimap f g = circuit $ \biDf -> do
98+
req <- toDfs -< (biDf, resp')
99+
req' <- Df.map f -< req
100+
resp' <- Df.map g -< resp
101+
(biDf', resp) <- fromDfs -< req'
102+
idC -< biDf'

clash-protocols/tests/Tests/Protocols.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module Tests.Protocols (tests, main) where
33
import Test.Tasty
44
import qualified Tests.Protocols.Avalon
55
import qualified Tests.Protocols.Axi4
6+
import qualified Tests.Protocols.BiDf
67
import qualified Tests.Protocols.Df
78
import qualified Tests.Protocols.DfConv
89
import qualified Tests.Protocols.Wishbone
@@ -11,7 +12,8 @@ tests :: TestTree
1112
tests =
1213
testGroup
1314
"Protocols"
14-
[ Tests.Protocols.Df.tests
15+
[ Tests.Protocols.BiDf.tests
16+
, Tests.Protocols.Df.tests
1517
, Tests.Protocols.DfConv.tests
1618
, Tests.Protocols.Avalon.tests
1719
, Tests.Protocols.Axi4.tests
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
{-# OPTIONS_GHC -fplugin Protocols.Plugin #-}
2+
{-# LANGUAGE TemplateHaskell #-}
3+
4+
module Tests.Protocols.BiDf (tests) where
5+
6+
-- clash-prelude
7+
import Clash.Prelude
8+
import qualified Clash.Sized.Vector as Vector
9+
import Clash.Hedgehog.Sized.Vector
10+
11+
-- clash-protocols
12+
import Protocols
13+
import Protocols.Hedgehog
14+
import Protocols.BiDf as BiDf
15+
16+
-- hedgehog
17+
import Hedgehog
18+
import qualified Hedgehog.Gen as Gen
19+
import qualified Hedgehog.Range as Range
20+
21+
-- tasty
22+
import Test.Tasty
23+
import Test.Tasty.Hedgehog.Extra (testProperty)
24+
import Test.Tasty.TH (testGroupGenerator)
25+
26+
-- | Ensure that 'BiDf.toDfs' composed with 'BiDf.fromDfs' behaves as an
27+
-- identity.
28+
prop_toDfs_fromDfs_id :: Property
29+
prop_toDfs_fromDfs_id =
30+
idWithModelSingleDomain @System defExpectOptions gen (\_ _ _ -> id) (exposeClockResetEnable impl)
31+
where
32+
gen :: Gen [Int]
33+
gen = Gen.list (Range.linear 0 10) (Gen.integral (Range.linear 0 100))
34+
35+
impl :: forall dom a. (HiddenClockResetEnable dom, NFDataX a)
36+
=> Circuit (Df dom a) (Df dom a)
37+
impl = BiDf.toDfs <| BiDf.fromDfs
38+
39+
-- | Ensure that 'BiDf.loopback' behaves as an identity.
40+
prop_loopback_id :: Property
41+
prop_loopback_id =
42+
idWithModelSingleDomain @System defExpectOptions gen (\_ _ _ -> id) (exposeClockResetEnable impl)
43+
where
44+
gen :: Gen [Int]
45+
gen = Gen.list (Range.linear 0 10) (Gen.integral (Range.linear 0 100))
46+
47+
impl :: forall dom a. (HiddenClockResetEnable dom, NFDataX a)
48+
=> Circuit (Df dom a) (Df dom a)
49+
impl = circuit $ \req -> do
50+
(biDf, resp) <- BiDf.fromDfs -< req
51+
BiDf.loopback id -< biDf
52+
idC -< resp
53+
54+
tests :: TestTree
55+
tests =
56+
$(testGroupGenerator)

0 commit comments

Comments
 (0)