diff --git a/fourmolu.yaml b/fourmolu.yaml new file mode 100644 index 0000000..4142c1a --- /dev/null +++ b/fourmolu.yaml @@ -0,0 +1,2 @@ +indentation: 2 +column-limit: 90 diff --git a/nix/sources.json b/nix/sources.json index 3a79b06..bd88e56 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -18,10 +18,10 @@ "homepage": null, "owner": "clash-lang", "repo": "clash-protocols", - "rev": "dac1bc4faf192843163248c6e4952b84337e3363", - "sha256": "0ilvsxy27yhm7d0qlmd6zxmr28f569iwzparbsgilbf7c0i9457x", + "rev": "d6f6bf622af5d2e9383f1d2c209239537f86afc0", + "sha256": "1dxxws8ab82cv2r7xlidgz29r03l078pd22jyk1xv0fir03gg0cj", "type": "tarball", - "url": "https://github.com/clash-lang/clash-protocols/archive/dac1bc4faf192843163248c6e4952b84337e3363.tar.gz", + "url": "https://github.com/clash-lang/clash-protocols/archive/d6f6bf622af5d2e9383f1d2c209239537f86afc0.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "doctest-parallel": { diff --git a/src/Clash/Cores/Ethernet/Mac.hs b/src/Clash/Cores/Ethernet/Mac.hs index a6780c6..de2734a 100644 --- a/src/Clash/Cores/Ethernet/Mac.hs +++ b/src/Clash/Cores/Ethernet/Mac.hs @@ -9,14 +9,19 @@ and link-layer. module Clash.Cores.Ethernet.Mac ( -- * Data types and constants module Clash.Cores.Ethernet.Mac.EthernetTypes, - -- * Frame check sequence + + -- * Handling the Frame Check Sequence module Clash.Cores.Ethernet.Mac.FrameCheckSequence, + -- * Interpacket gap module Clash.Cores.Ethernet.Mac.InterpacketGapInserter, - -- * MAC header + + -- * (De)packetizing MAC headers module Clash.Cores.Ethernet.Mac.MacPacketizers, + -- * Padding module Clash.Cores.Ethernet.Mac.PaddingInserter, + -- * Preamble module Clash.Cores.Ethernet.Mac.Preamble, ) where diff --git a/src/Clash/Cores/Ethernet/Mac/FrameCheckSequence.hs b/src/Clash/Cores/Ethernet/Mac/FrameCheckSequence.hs index 96fc6d0..934a371 100644 --- a/src/Clash/Cores/Ethernet/Mac/FrameCheckSequence.hs +++ b/src/Clash/Cores/Ethernet/Mac/FrameCheckSequence.hs @@ -2,9 +2,12 @@ {-# LANGUAGE RecordWildCards #-} {-# OPTIONS_HADDOCK hide #-} -{-| -Module : Clash.Cores.Ethernet.Mac.FrameCheckSequence -Description : Provides circuits to insert, validate and strip the FCS of an Ethernet frame. +{- | + Copyright : (C) 2024, QBayLogic B.V. + License : BSD2 (see the file LICENSE) + Maintainer : QBayLogic B.V. + +Provides circuits to insert, validate and strip the FCS of Ethernet frames. -} module Clash.Cores.Ethernet.Mac.FrameCheckSequence ( fcsInserterC, @@ -12,21 +15,16 @@ module Clash.Cores.Ethernet.Mac.FrameCheckSequence ( fcsStripperC, ) where --- crc -import Clash.Cores.Crc (crcEngine, crcValidator, HardwareCrc) -import Clash.Cores.Crc.Catalog (Crc32_ethernet(..)) - --- vector -import Clash.Sized.Vector.Extra (appendVec) +import Clash.Cores.Crc (HardwareCrc, crcEngine, crcValidator) +import Clash.Cores.Crc.Catalog (Crc32_ethernet (..)) --- prelude import Clash.Prelude --- maybe +import Clash.Sized.Vector.Extra (appendVec) + import Data.Maybe -import Data.Maybe.Extra +import Data.Maybe.Extra (toMaybe) --- protocols import Protocols import Protocols.PacketStream @@ -40,106 +38,110 @@ toCrcInput :: PacketStreamM2S dataWidth () -> Maybe (Bool, Index dataWidth, Vec dataWidth (BitVector 8)) toCrcInput en isFirst PacketStreamM2S{..} = - toMaybe en (isFirst, fromMaybe maxBound _last, _data) - -fcsInserterT - :: forall dataWidth - . KnownNat dataWidth - => 1 <= dataWidth - => FcsInserterState dataWidth - -> ( Vec 4 (BitVector 8) - , Maybe (PacketStreamM2S dataWidth ()) - , PacketStreamS2M) - -> ( FcsInserterState dataWidth - , ( Maybe (PacketStreamM2S dataWidth ()) - , Bool)) -fcsInserterT (FcsCopy Nothing) ( _, fwdIn, _) = (FcsCopy fwdIn, (Nothing, True)) - -fcsInserterT st@(FcsCopy (Just cache@(PacketStreamM2S{..}))) (ethCrcBytes, fwdIn, PacketStreamS2M readyIn) - = (nextSt, (Just fwdOut, readyIn)) - where - (combined, leftover) = splitAtI $ appendVec (fromJust _last) _data ethCrcBytes - - nextLast i = case compareSNat d5 (SNat @dataWidth) of - SNatLE -> toMaybe (i < natToNum @(dataWidth - 4)) $ i + 4 - _ -> Nothing - - insertCrc = nextLast <$> _last - - fwdOut = case insertCrc of - Just l -> cache { _data = combined, _last = l } - Nothing -> cache - - nextStIfReady = if maybe True isJust insertCrc - then FcsCopy fwdIn - else FcsInsert - { _aborted = _abort - , _cachedFwd = fwdIn - -- Since we know we are in a case where we are not transmitting the entire CRC out - -- it's guaranteed that dataWidth - 4 <= lastIdx <= dataWidth - 1 - -- This means we don't need to look at entire state space of the index. - -- Only the last 2 bits matter. But since dataWidth might not be 4 byte - -- aligned we need to wrapping subtract Mod dataWidth 4 to align the index. - -- Normally wrapping subtract is relatively expensive but since 4 - -- is a power of two we get it for free. But it means we have to do - -- arithmetic with BitVector/Unsigned type and not index. - -- - -- We could go even further beyond and just pass through the last 2 bits without - -- correction and handle that in `FcsInsert`. - , _valid = unpack $ resize (pack $ fromJustX _last) - natToNum @(Mod dataWidth 4) - , _cachedCrc = leftover - } - - nextSt = if readyIn then nextStIfReady else st - -fcsInserterT st@(FcsInsert{..}) (_, _, PacketStreamS2M readyIn) = (nextSt, (Just dataOut, False)) - where - finished = _valid <= natToNum @(Min (dataWidth - 1) 3) - (outBytes, nextBytes) = splitAtI $ _cachedCrc ++ repeat 0 - dataOut = PacketStreamM2S - { _data = outBytes - , _last = toMaybe finished $ resize _valid - , _meta = () - , _abort = _aborted - } - - nextStIfReady = - if finished - then FcsCopy _cachedFwd - else st - { _valid = _valid - natToNum @dataWidth - , _cachedCrc = nextBytes - } - - nextSt = if readyIn then nextStIfReady else st + toMaybe + (en && _last /= Just 0) + (isFirst, maybe maxBound (resize . (\i -> i - 1)) _last, _data) --- | States of the FcsInserter +-- | State of 'fcsInserterT'. data FcsInserterState dataWidth = FcsCopy - { _cachedFwd :: Maybe (PacketStreamM2S dataWidth ()) } + {_cachedFwd :: Maybe (PacketStreamM2S dataWidth ())} | FcsInsert { _aborted :: Bool , _cachedFwd :: Maybe (PacketStreamM2S dataWidth ()) - , _valid :: Index 4 + , _valid :: Index 5 -- ^ how many bytes of _cachedCrc are valid , _cachedCrc :: Vec 4 (BitVector 8) } deriving (Show, Generic, NFDataX) --- | fcsInserter -fcsInserter - :: forall (dataWidth :: Nat) (dom :: Domain) - . HiddenClockResetEnable dom - => KnownNat dataWidth - => HardwareCrc Crc32_ethernet 8 dataWidth - => ( Signal dom (Maybe (PacketStreamM2S dataWidth ())) - , Signal dom PacketStreamS2M - ) - -> ( Signal dom PacketStreamS2M - , Signal dom (Maybe (PacketStreamM2S dataWidth ())) - ) -fcsInserter (fwdIn, bwdIn) = (bwdOut, fwdOut) - where +-- | State transition function of 'fcsInserterC'. +fcsInserterT :: + forall dataWidth. + (KnownNat dataWidth) => + (1 <= dataWidth) => + FcsInserterState dataWidth -> + ( Vec 4 (BitVector 8) + , Maybe (PacketStreamM2S dataWidth ()) + , PacketStreamS2M + ) -> + ( FcsInserterState dataWidth + , ( Maybe (PacketStreamM2S dataWidth ()) + , Bool + ) + ) +fcsInserterT (FcsCopy Nothing) (_, fwdIn, _) = (FcsCopy fwdIn, (Nothing, True)) +fcsInserterT st@(FcsCopy (Just cache@(PacketStreamM2S{..}))) (ethCrcBytes, fwdIn, bwdIn) = + (nextStOut, (Just fwdOut, _ready bwdIn)) + where + (combined, leftover) = splitAtI $ appendVec (fromJust _last) _data ethCrcBytes + + nextLast :: Index (dataWidth + 1) -> Maybe (Index (dataWidth + 1)) + nextLast i = case compareSNat d4 (SNat @dataWidth) of + SNatLE -> toMaybe (i <= natToNum @(dataWidth - 4)) (i + 4) + _ -> Nothing + + insertCrc = nextLast <$> _last + + fwdOut = case insertCrc of + Just l -> cache{_data = combined, _last = l} + Nothing -> cache + + nextSt = + if maybe True isJust insertCrc + then FcsCopy fwdIn + else + FcsInsert + { _aborted = _abort + , _cachedFwd = fwdIn + , _valid = 4 - resize (maxBound - fromJustX _last) + , _cachedCrc = leftover + } + + nextStOut = if _ready bwdIn then nextSt else st +fcsInserterT st@(FcsInsert{..}) (_, _, bwdIn) = (nextStOut, (Just dataOut, False)) + where + finished = _valid <= natToNum @(Min dataWidth 4) + (outBytes, nextBytes) = splitAtI $ _cachedCrc ++ repeat 0x00 + dataOut = + PacketStreamM2S + { _data = outBytes + , _last = toMaybe finished (resize _valid) + , _meta = () + , _abort = _aborted + } + + nextSt = + if finished + then FcsCopy _cachedFwd + else + st + { _valid = _valid - natToNum @dataWidth + , _cachedCrc = nextBytes + } + + nextStOut = if _ready bwdIn then nextSt else st + +{- | +Computes the Ethernet CRC ('Crc32_ethernet') over each packet in the stream +and appends this CRC to the corresponding packet in the output stream. + +__NB__: does not support zero-byte packets. Feeding a zero-byte packet to this +component will result in /undefined behaviour/. +-} +fcsInserterC :: + forall (dataWidth :: Nat) (dom :: Domain). + (HiddenClockResetEnable dom) => + (HardwareCrc Crc32_ethernet 8 dataWidth) => + (KnownNat dataWidth) => + -- | Ethernet FCS inserter circuit + Circuit + (PacketStream dom dataWidth ()) + (PacketStream dom dataWidth ()) +fcsInserterC = forceResetSanity |> fromSignals go + where + go (fwdIn, bwdIn) = (bwdOut, fwdOut) + where fwdInX = fromJustX <$> fwdIn transferOccured = ready .&&. isJust <$> fwdIn crcIn = liftA3 toCrcInput transferOccured isFirst fwdInX @@ -152,21 +154,6 @@ fcsInserter (fwdIn, bwdIn) = (bwdOut, fwdOut) (fwdOut, ready) = mealyB fcsInserterT (FcsCopy Nothing) (ethCrcBytes, fwdIn, bwdIn) -{- | -Computes the Ethernet CRC (4 bytes) of each packet in the input stream and -appends this CRC to the corresponding packet in the output stream. --} -fcsInserterC - :: forall (dataWidth :: Nat) (dom :: Domain) - . KnownDomain dom - => KnownNat dataWidth - => HiddenClockResetEnable dom - => HardwareCrc Crc32_ethernet 8 dataWidth - => Circuit - (PacketStream dom dataWidth ()) - (PacketStream dom dataWidth ()) -fcsInserterC = forceResetSanity |> fromSignals fcsInserter - -- | State of 'fcsValidatorT'. newtype FcsValidatorState dataWidth = FcsValidatorState { _cachedFwd :: Maybe (PacketStreamM2S dataWidth ()) @@ -176,6 +163,7 @@ newtype FcsValidatorState dataWidth = FcsValidatorState -- | State transition function of 'fcsValidator'. fcsValidatorT :: forall (dataWidth :: Nat). + (KnownNat dataWidth) => FcsValidatorState dataWidth -> ( Bool , Maybe (PacketStreamM2S dataWidth ()) @@ -204,7 +192,6 @@ fcsValidatorT st@FcsValidatorState{..} (valid, fwdIn, bwdIn) = fcsValidator :: forall (dataWidth :: Nat) (dom :: Domain). (HiddenClockResetEnable dom) => - (KnownNat dataWidth) => (HardwareCrc Crc32_ethernet 8 dataWidth) => ( Signal dom (Maybe (PacketStreamM2S dataWidth ())) , Signal dom PacketStreamS2M @@ -233,12 +220,16 @@ did not match the last 4 bytes of the stream. __NB__: does not remove the FCS field (last 4 bytes of the stream). Use 'fcsStripperC' for that. + +__NB__: does not support zero-byte packets. Feeding a zero-byte packet to this +component will result in /undefined behaviour/. -} fcsValidatorC :: forall (dataWidth :: Nat) (dom :: Domain). (HiddenClockResetEnable dom) => (KnownNat dataWidth) => (HardwareCrc Crc32_ethernet 8 dataWidth) => + -- | Ethernet FCS validator circuit Circuit (PacketStream dom dataWidth ()) (PacketStream dom dataWidth ()) @@ -255,6 +246,7 @@ fcsStripperC :: (HiddenClockResetEnable dom) => (KnownNat dataWidth) => (1 <= dataWidth) => + -- | Ethernet FCS stripper circuit Circuit (PacketStream dom dataWidth ()) (PacketStream dom dataWidth ()) diff --git a/src/Clash/Cores/Ethernet/Mac/PaddingInserter.hs b/src/Clash/Cores/Ethernet/Mac/PaddingInserter.hs index d369641..9dc05c6 100644 --- a/src/Clash/Cores/Ethernet/Mac/PaddingInserter.hs +++ b/src/Clash/Cores/Ethernet/Mac/PaddingInserter.hs @@ -1,97 +1,122 @@ +{-# LANGUAGE AllowAmbiguousTypes #-} {-# OPTIONS_HADDOCK hide #-} -{-| -Module : Clash.Cores.Ethernet.Mac.PaddingInserter -Description : Provides paddingInserterC for padding ethernet frames to a customizable amount of bytes. +{- | + Copyright : (C) 2024, QBayLogic B.V. + License : BSD2 (see the file LICENSE) + Maintainer : QBayLogic B.V. + +Provides a component which pads packets with null bytes, to a customizable +minimum packet size. -} -module Clash.Cores.Ethernet.Mac.PaddingInserter - ( paddingInserterC - ) where +module Clash.Cores.Ethernet.Mac.PaddingInserter ( + paddingInserterC, +) where import Clash.Prelude -import Protocols ( Circuit, fromSignals ) -import Protocols.PacketStream +import Control.Monad (guard) -import Control.Monad ( guard ) -import Data.Maybe ( isJust ) -import Data.Maybe.Extra ( toMaybe ) +import Data.Maybe (isJust) +import Data.Maybe.Extra (toMaybe) +import Data.Type.Equality ((:~:) (Refl)) +import Protocols (Circuit, fromSignals) +import Protocols.PacketStream --- | State of the paddingInserter circuit. --- Counts up to @ceil(padBytes / dataWidth)@ packets, which is --- the amount of packets needed to fill @padBytes@ bytes. +{- | +State of 'paddingInserterT'. +Counts up to @ceil(padBytes / dataWidth)@ transfers per packet, which is +the amount of transfers needed to fill @padBytes@ bytes. +-} data PaddingInserterState (dataWidth :: Nat) (padBytes :: Nat) - = Filling { count :: Index (DivRU padBytes dataWidth) } + = Filling (Index (DivRU padBytes dataWidth)) | Full - | Padding { count :: Index (DivRU padBytes dataWidth) } - deriving (Eq, Show, Generic, NFDataX) + | Padding (Index (DivRU padBytes dataWidth)) + deriving (Generic, NFDataX, Show, ShowX) + +-- | Computes the size of the last fragment of a padded packet. +lastSize :: + forall (dataWidth :: Nat) (padBytes :: Nat). + (KnownNat dataWidth) => + (KnownNat padBytes) => + (1 <= dataWidth) => + Index (dataWidth + 1) +lastSize = case sameNat d0 (SNat @(padBytes `Mod` dataWidth)) of + Just Refl -> maxBound + _ -> natToNum @(padBytes `Mod` dataWidth) + +paddingInserterT :: + forall (dataWidth :: Nat) (padBytes :: Nat). + (KnownNat dataWidth) => + (KnownNat padBytes) => + (1 <= dataWidth) => + (1 <= padBytes) => + PaddingInserterState dataWidth padBytes -> + ( Maybe (PacketStreamM2S dataWidth ()) + , PacketStreamS2M + ) -> + ( PaddingInserterState dataWidth padBytes + , ( PacketStreamS2M + , Maybe (PacketStreamM2S dataWidth ()) + ) + ) +-- If state is Filling, forward the input from sink with updated _last +paddingInserterT st@(Filling i) (Just fwdIn, bwdIn) = (nextStOut, (bwdIn, Just fwdOut)) + where + done = i == maxBound + -- If we are not done filling, then set _last to @Nothing@. Otherwise, set + -- _last to the maximum of the index that would reach the minimum packet + -- size, and the _last of the input transfer. + fwdOut = + fwdIn + { _last = guard done >> max (lastSize @dataWidth @padBytes) <$> _last fwdIn + } -paddingInserter - :: forall (dataWidth :: Nat) (padBytes :: Nat) (dom :: Domain) - . HiddenClockResetEnable dom - => 1 <= dataWidth - => 1 <= padBytes - => KnownNat dataWidth - => KnownNat padBytes - => SNat padBytes - -> ( Signal dom (Maybe (PacketStreamM2S dataWidth ())) - , Signal dom PacketStreamS2M) - -- ^ Input packet stream from the source - -- Input backpressure from the sink - -> ( Signal dom PacketStreamS2M - , Signal dom (Maybe (PacketStreamM2S dataWidth ()))) - -- ^ Output backpressure to the source - -- Output packet stream to the sink -paddingInserter _ = mealyB go (Filling 0) - where - padding = PacketStreamM2S {_data = repeat 0, _last = Nothing, _meta = (), _abort = False} - lastIdx = natToNum @((padBytes - 1) `Mod` dataWidth) - go - :: PaddingInserterState dataWidth padBytes - -> (Maybe (PacketStreamM2S dataWidth ()), PacketStreamS2M) - -> (PaddingInserterState dataWidth padBytes, (PacketStreamS2M, Maybe (PacketStreamM2S dataWidth ()))) - -- If state is Full, forward the input from sink - go Full (Nothing, bwd) = (Full, (bwd, Nothing)) - go Full (Just fwd, bwd@(PacketStreamS2M inReady)) = (if inReady && isJust (_last fwd) then Filling 0 else Full, (bwd, Just fwd)) + nextSt = case (done, _last fwdIn) of + (True, Nothing) -> Full + (True, Just _) -> Filling 0 + (False, Nothing) -> Filling (i + 1) + (False, Just _) -> Padding (i + 1) - -- If state is Padding, send out null-bytes to source and backpressure to sink - go st@(Padding i) (_, PacketStreamS2M inReady) = (if inReady then st' else st, (PacketStreamS2M False, Just fwdOut)) - where - done = i == maxBound - st' = if done then Filling 0 else Padding (i + 1) - fwdOut = padding {_last = toMaybe done lastIdx} + nextStOut = if _ready bwdIn then nextSt else st - -- If state is Filling, forward the input from sink with updated _last - go (Filling i) (Nothing, bwd) = (Filling i, (bwd, Nothing)) - go st@(Filling i) (Just fwdIn, bwd@(PacketStreamS2M inReady)) = (if inReady then st' else st, (bwd, Just fwdOut)) - where - done = i == maxBound - next = i + 1 - st' = case (done, _last fwdIn) of - (True, Nothing) -> Full - (True, Just _ ) -> Filling 0 - (False, Nothing) -> Filling next - (False, Just _ ) -> Padding next - -- If i < maxBound, then set _last to Nothing - -- Otherwise, set _last to the maximum of the - -- index that would reach the minimum frame size, - -- and the _last of fwdIn - fwdOut = fwdIn {_last = guard done >> max lastIdx <$> _last fwdIn} +-- If state is Full, forward the input from sink +paddingInserterT Full (Just fwdIn, bwdIn) = (nextStOut, (bwdIn, Just fwdIn)) + where + nextStOut = if _ready bwdIn && isJust (_last fwdIn) then Filling 0 else Full + +-- If state is Padding, send out null-bytes to source and backpressure to sink +paddingInserterT st@(Padding i) (_, bwdIn) = (nextStOut, (PacketStreamS2M False, Just fwdOut)) + where + done = i == maxBound + fwdOut = + PacketStreamM2S + { _data = repeat 0 + , _last = toMaybe done (lastSize @dataWidth @padBytes) + , _meta = () + , _abort = False + } + nextSt = if done then Filling 0 else Padding (i + 1) + nextStOut = if _ready bwdIn then nextSt else st +paddingInserterT st (Nothing, bwdIn) = (st, (bwdIn, Nothing)) {- | -Pads ethernet frames to a minimum of @padBytes@ bytes. -Requires that all invalid bytes are set to 0x00, otherwise -Sends bytes the same clock cycle as they are received. +Pads packets with null bytes (@0x00@) to a minimum of @padBytes@ bytes. Provides +zero latency: transmits bytes the same clock cycle as they are received. Only +runs at full throughput for packets that do not need padding. For packets that +do need padding, it will assert backpressure in order to append the padding to +the packet. + +__NB__: requires that all invalid bytes are set to @0x00@. -} -paddingInserterC - :: forall (dataWidth :: Nat) (padBytes :: Nat) (dom :: Domain) - . HiddenClockResetEnable dom - => 1 <= dataWidth - => 1 <= padBytes - => KnownNat dataWidth - => KnownNat padBytes - -- | The minimum size out output packets - => SNat padBytes - -> Circuit (PacketStream dom dataWidth ()) (PacketStream dom dataWidth ()) -paddingInserterC padBytes = fromSignals (paddingInserter padBytes) +paddingInserterC :: + forall (dataWidth :: Nat) (padBytes :: Nat) (dom :: Domain). + (HiddenClockResetEnable dom) => + (KnownNat dataWidth) => + (1 <= dataWidth) => + (1 <= padBytes) => + -- | The minimum size of output packets + SNat padBytes -> + Circuit (PacketStream dom dataWidth ()) (PacketStream dom dataWidth ()) +paddingInserterC SNat = fromSignals (mealyB (paddingInserterT @_ @padBytes) (Filling 0)) diff --git a/src/Clash/Sized/Vector/Extra.hs b/src/Clash/Sized/Vector/Extra.hs index 9e53c17..bc84af6 100644 --- a/src/Clash/Sized/Vector/Extra.hs +++ b/src/Clash/Sized/Vector/Extra.hs @@ -46,7 +46,7 @@ appendVec :: forall n m a . KnownNat n => Num a - => Index n + => Index (n + 1) -> Vec n a -> Vec m a -> Vec (n + m) a @@ -59,7 +59,7 @@ appendVec valid xs ys = results !! valid extra :: Vec (n - (l + 1)) a extra = repeat 0 _ -> error "appendVec: Absurd" - results = smap (\s _ -> go s) xs + results = (ys ++ repeat 0) :> smap (\s _ -> go s) xs -- | Fold a vector of @n@ elements into a single element using a binary function. -- | Between every "layer" of the fold, there is a register diff --git a/test/Test/Cores/Ethernet/Arp/ArpManager.hs b/test/Test/Cores/Ethernet/Arp/ArpManager.hs index 5b97a90..716bf80 100644 --- a/test/Test/Cores/Ethernet/Arp/ArpManager.hs +++ b/test/Test/Cores/Ethernet/Arp/ArpManager.hs @@ -76,7 +76,7 @@ arpReceiverPropertyGenerator SNat = idWithModelSingleDomain @System defExpectOptions{eoStopAfterEmpty = 1000} - (genPackets (Range.linear 1 5) Abort genPkt) + (genPackets 1 5 genPkt) (exposeClockResetEnable model) (exposeClockResetEnable @System (arpReceiverC $ pure ourIPv4)) where @@ -89,10 +89,10 @@ arpReceiverPropertyGenerator SNat = <*> Gen.constant (if gratuitous then spa else ourIPv4) <*> genArpOperation - genPkt am = + genPkt = Gen.choice [ -- Random packet - genValidPacket (pure ()) (Range.linear 0 20) am + genValidPacket defPacketOptions (pure ()) (Range.linear 0 20) , -- Valid ARP reply/request do arpPkt <- genArpPacket False diff --git a/test/Test/Cores/Ethernet/IP/EthernetStream.hs b/test/Test/Cores/Ethernet/IP/EthernetStream.hs index b4c8897..3481932 100644 --- a/test/Test/Cores/Ethernet/IP/EthernetStream.hs +++ b/test/Test/Cores/Ethernet/IP/EthernetStream.hs @@ -86,7 +86,7 @@ ethernetStreamTest SNat arpResponse = idWithModelSingleDomain @System defExpectOptions - (genPackets (Range.linear 1 10) Abort (genValidPacket genIPv4Addr (Range.linear 0 10))) + (genPackets 1 10 (genValidPacket defPacketOptions genIPv4Addr (Range.linear 0 10))) (exposeClockResetEnable (model arpResponse)) (exposeClockResetEnable (testCircuit @_ @dataWidth arpResponse)) diff --git a/test/Test/Cores/Ethernet/IP/IPPacketizers.hs b/test/Test/Cores/Ethernet/IP/IPPacketizers.hs index d96a15f..502fce3 100644 --- a/test/Test/Cores/Ethernet/IP/IPPacketizers.hs +++ b/test/Test/Cores/Ethernet/IP/IPPacketizers.hs @@ -36,7 +36,7 @@ testIPPacketizer SNat = idWithModelSingleDomain @System defExpectOptions{eoSampleMax = 400, eoStopAfterEmpty = 400} - (genPackets (Range.linear 1 4) Abort (genValidPacket genIPv4Header (Range.linear 0 30))) + (genPackets 1 4 (genValidPacket defPacketOptions genIPv4Header (Range.linear 0 30))) (exposeClockResetEnable (packetizerModel _ipv4Destination id . setChecksums)) (exposeClockResetEnable (ipPacketizerC @_ @dataWidth)) where @@ -54,26 +54,26 @@ testIPDepacketizer SNat = idWithModelSingleDomain @System defExpectOptions{eoStopAfterEmpty = 400} - (genPackets (Range.linear 1 10) Abort genPkt) + (genPackets 1 10 genPkt) (exposeClockResetEnable model) (exposeClockResetEnable (ipDepacketizerC @_ @dataWidth)) where - validPkt = genValidPacket genEthernetHeader (Range.linear 0 10) - genPkt am = + validPkt = genValidPacket defPacketOptions genEthernetHeader (Range.linear 0 10) + genPkt = Gen.choice [ -- Random packet: extremely high chance to get aborted. - validPkt am + validPkt , -- Packet with valid header: should not get aborted. do hdr <- genIPv4Header packetizerModel id (const hdr{_ipv4Checksum = pureInternetChecksum (bitCoerce hdr :: Vec 10 (BitVector 16))}) - <$> validPkt am + <$> validPkt , -- Packet with valid header apart from (most likely) the checksum. do hdr <- genIPv4Header - packetizerModel id (const hdr{_ipv4Checksum = 0xABCD}) <$> validPkt am + packetizerModel id (const hdr{_ipv4Checksum = 0xABCD}) <$> validPkt ] model fragments = L.concat $ L.zipWith setAbort packets aborts diff --git a/test/Test/Cores/Ethernet/Icmp.hs b/test/Test/Cores/Ethernet/Icmp.hs index 6723365..a2089d0 100644 --- a/test/Test/Cores/Ethernet/Icmp.hs +++ b/test/Test/Cores/Ethernet/Icmp.hs @@ -40,12 +40,12 @@ icmpResponderPropertyGenerator SNat = idWithModelSingleDomain @System defExpectOptions - (genPackets (Range.linear 1 5) Abort genValidIcmpRequestPacket) + (genPackets 1 5 genValidIcmpRequestPacket) (exposeClockResetEnable (L.concatMap model . chunkByPacket)) (exposeClockResetEnable (icmpEchoResponderC $ pure ourIPv4)) where - genValidIcmpRequestPacket am = do - dat <- genValidPacket (genIPv4HeaderLite ourIPv4) (Range.linear 0 10) am + genValidIcmpRequestPacket = do + dat <- genValidPacket defPacketOptions (genIPv4HeaderLite ourIPv4) (Range.linear 0 10) let checksum = calculateChecksum (packetizerModel id (const (IcmpHeader 8 0 0)) dat) pure $ packetizerModel id (const $ IcmpHeader 8 0 checksum) dat diff --git a/test/Test/Cores/Ethernet/Mac/FrameCheckSequence.hs b/test/Test/Cores/Ethernet/Mac/FrameCheckSequence.hs index 4e6b1e7..0deab9d 100644 --- a/test/Test/Cores/Ethernet/Mac/FrameCheckSequence.hs +++ b/test/Test/Cores/Ethernet/Mac/FrameCheckSequence.hs @@ -1,8 +1,6 @@ {-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE NumericUnderscores #-} {-# OPTIONS_GHC -Wno-orphans #-} -{-# OPTIONS_GHC -fplugin Protocols.Plugin #-} module Test.Cores.Ethernet.Mac.FrameCheckSequence ( tests, @@ -31,18 +29,18 @@ import Test.Tasty.Hedgehog.Extra (testProperty) import Test.Tasty.TH (testGroupGenerator) $(deriveHardwareCrc Crc32_ethernet d8 d1) -$(deriveHardwareCrc Crc32_ethernet d8 d2) $(deriveHardwareCrc Crc32_ethernet d8 d3) $(deriveHardwareCrc Crc32_ethernet d8 d4) $(deriveHardwareCrc Crc32_ethernet d8 d7) -$(deriveHardwareCrc Crc32_ethernet d8 d8) packetToCrcInp :: (KnownNat dataWidth) => (1 <= dataWidth) => [PacketStreamM2S dataWidth ()] -> [BitVector 8] -packetToCrcInp packet = head . _data <$> (chopPacket =<< packet) +packetToCrcInp packet = go (chopPacket =<< packet) + where + go = L.concatMap (\p -> [head (_data p) | _last p /= Just 0]) insertCrc :: forall (dataWidth :: Nat). @@ -55,17 +53,20 @@ insertCrc = upConvert . go . downConvert go :: [PacketStreamM2S 1 ()] -> [PacketStreamM2S 1 ()] go pkt = pkt'' where - crcInp = head . _data <$> pkt + lastfmnt = L.last pkt + trailingZero = _last lastfmnt == Just 0 + + crcInp' = head . _data <$> pkt + crcInp = if trailingZero then L.init crcInp' else crcInp' softwareCrc = mkSoftwareCrc Crc32_ethernet d8 crc = digest $ L.foldl' feed softwareCrc crcInp crc' = singleton . v2bv <$> (toList . reverse . unconcat d8 . bv2v $ crc) - lastfmnt = L.last pkt + pkt' = L.init pkt - L.++ [lastfmnt{_last = Nothing}] + L.++ ([lastfmnt{_last = Nothing} | not trailingZero]) L.++ fmap (\dat -> lastfmnt{_data = dat, _last = Nothing}) crc' - pkt'' = L.init pkt' L.++ [(L.last pkt'){_last = Just 0}] - + pkt'' = L.init pkt' L.++ [(L.last pkt'){_last = Just 1}] validateCrc :: forall (dataWidth :: Nat). @@ -86,74 +87,77 @@ validateCrc packet = L.init packet L.++ [lastPacketSetAbort] } -- | Test the FCS inserter -fcsinserterTest :: +fcsInserterTest :: forall dataWidth. (1 <= dataWidth) => (HardwareCrc Crc32_ethernet 8 dataWidth) => SNat dataWidth -> Property -fcsinserterTest SNat = +fcsInserterTest SNat = idWithModelSingleDomain @System defExpectOptions - (genPackets (Range.linear 1 4) Abort (genValidPacket (pure ()) (Range.linear 0 20))) + (genPackets 1 8 genPkt) (exposeClockResetEnable modelInsert) (exposeClockResetEnable (fcsInserterC @dataWidth)) where + pktOpts = defPacketOptions{poAllowEmptyPackets = False} + genPkt = genValidPacket pktOpts (pure ()) (Range.linear 1 20) modelInsert packets = L.concatMap insertCrc (chunkByPacket packets) -- | Test the FCS validator -fcsvalidatorTest :: +fcsValidatorTest :: forall dataWidth. (1 <= dataWidth) => (HardwareCrc Crc32_ethernet 8 dataWidth) => SNat dataWidth -> Property -fcsvalidatorTest SNat = +fcsValidatorTest SNat = idWithModelSingleDomain @System defExpectOptions - (genPackets (Range.linear 1 4) Abort genPkt) + (genPackets 1 8 genPkt) (exposeClockResetEnable modelValidate) (exposeClockResetEnable (fcsValidatorC @dataWidth)) where - genPkt am = + pktOpts = defPacketOptions{poAllowEmptyPackets = False} + genPkt = Gen.choice [ -- Random packet - genValidPacket (pure ()) (Range.linear 0 20) am + genValidPacket pktOpts (pure ()) (Range.linear 0 20) , -- Packet with valid CRC - insertCrc <$> genValidPacket (pure ()) (Range.linear 0 20) am + insertCrc <$> genValidPacket pktOpts (pure ()) (Range.linear 0 20) ] modelValidate packets = validateCrc =<< chunkByPacket packets -prop_fcsinserter_d1 :: Property -prop_fcsinserter_d1 = fcsinserterTest d1 +prop_fcs_inserter_d1 :: Property +prop_fcs_inserter_d1 = fcsInserterTest d1 -prop_fcsinserter_d2 :: Property -prop_fcsinserter_d2 = fcsinserterTest d2 +prop_fcs_inserter_d3 :: Property +prop_fcs_inserter_d3 = fcsInserterTest d3 -prop_fcsinserter_d4 :: Property -prop_fcsinserter_d4 = fcsinserterTest d4 +prop_fcs_inserter_d4 :: Property +prop_fcs_inserter_d4 = fcsInserterTest d4 -prop_fcsinserter_d8 :: Property -prop_fcsinserter_d8 = fcsinserterTest d8 +prop_fcs_inserter_d7 :: Property +prop_fcs_inserter_d7 = fcsInserterTest d7 -prop_fcsvalidator_d1 :: Property -prop_fcsvalidator_d1 = fcsvalidatorTest d1 +prop_fcs_validator_d1 :: Property +prop_fcs_validator_d1 = fcsValidatorTest d1 -prop_fcsvalidator_d3 :: Property -prop_fcsvalidator_d3 = fcsvalidatorTest d3 +prop_fcs_validator_d3 :: Property +prop_fcs_validator_d3 = fcsValidatorTest d3 -prop_fcsvalidator_d4 :: Property -prop_fcsvalidator_d4 = fcsvalidatorTest d4 +prop_fcs_validator_d4 :: Property +prop_fcs_validator_d4 = fcsValidatorTest d4 -prop_fcsvalidator_d7 :: Property -prop_fcsvalidator_d7 = fcsvalidatorTest d7 +prop_fcs_validator_d7 :: Property +prop_fcs_validator_d7 = fcsValidatorTest d7 tests :: TestTree tests = - localOption (mkTimeout 20_000_000 {- 20 seconds -}) $ - localOption + localOption (mkTimeout 20_000_000 {- 20 seconds -}) + $ localOption (HedgehogTestLimit (Just 1_000)) $(testGroupGenerator) diff --git a/test/Test/Cores/Ethernet/Mac/InterpacketGapInserter.hs b/test/Test/Cores/Ethernet/Mac/InterpacketGapInserter.hs index dd5f1bb..667b66f 100644 --- a/test/Test/Cores/Ethernet/Mac/InterpacketGapInserter.hs +++ b/test/Test/Cores/Ethernet/Mac/InterpacketGapInserter.hs @@ -32,7 +32,7 @@ prop_interpacket_gap_inserter_id = idWithModelSingleDomain @System defExpectOptions - (genPackets (Range.linear 1 10) Abort (genValidPacket (pure ()) (Range.linear 0 20))) + (genPackets 1 10 (genValidPacket defPacketOptions (pure ()) (Range.linear 0 20))) (exposeClockResetEnable id) (exposeClockResetEnable (interpacketGapInserterC d12)) diff --git a/test/Test/Cores/Ethernet/Mac/PaddingInserter.hs b/test/Test/Cores/Ethernet/Mac/PaddingInserter.hs index 6d547c2..621eb98 100644 --- a/test/Test/Cores/Ethernet/Mac/PaddingInserter.hs +++ b/test/Test/Cores/Ethernet/Mac/PaddingInserter.hs @@ -31,7 +31,7 @@ paddingInserterModel :: [PacketStreamM2S dataWidth ()] paddingInserterModel padBytes fragments = L.concatMap - (upConvert . fullPackets . insertPadding) + (upConvert . insertPadding) (chunkByPacket $ downConvert fragments) where padding = @@ -42,11 +42,13 @@ paddingInserterModel padBytes fragments = , _abort = False } - insertPadding xs = - L.init xs - L.++ ( (L.last xs){_last = Nothing} - : L.replicate (max 0 (padBytes - L.length xs)) padding - ) + insertPadding xs + | n < 0 = xs + | n > 0 = + fullPackets $ L.init xs L.++ ((L.last xs){_last = Nothing} : L.replicate n padding) + | otherwise = fullPackets xs + where + n = padBytes - L.length xs -- | Test the padding inserter. paddingInserterTest :: @@ -61,29 +63,29 @@ paddingInserterTest SNat padBytes = idWithModelSingleDomain @System defExpectOptions - (genPackets (Range.linear 1 10) Abort (genValidPacket (pure ()) (Range.linear 0 10))) + (genPackets 1 10 (genValidPacket defPacketOptions (pure ()) (Range.linear 0 10))) (exposeClockResetEnable (paddingInserterModel $ natToNum @padBytes)) (exposeClockResetEnable (paddingInserterC @dataWidth padBytes)) -- | dataWidth ~ padBytes -prop_paddinginserter_d1 :: Property -prop_paddinginserter_d1 = paddingInserterTest d1 d1 +prop_padding_inserter1_d1 :: Property +prop_padding_inserter1_d1 = paddingInserterTest d1 d1 -- | dataWidth % padBytes ~ 0 -prop_paddinginserter_d2 :: Property -prop_paddinginserter_d2 = paddingInserterTest d2 d26 +prop_padding_inserter26_d2 :: Property +prop_padding_inserter26_d2 = paddingInserterTest d2 d26 -- | dataWidth % padBytes > 0 -prop_paddinginserter_d7 :: Property -prop_paddinginserter_d7 = paddingInserterTest d7 d26 +prop_padding_inserter26_d7 :: Property +prop_padding_inserter26_d7 = paddingInserterTest d7 d26 -- | dataWidth > padBytes -prop_paddinginserter_d20 :: Property -prop_paddinginserter_d20 = paddingInserterTest d20 d10 +prop_padding_inserter10_d20 :: Property +prop_padding_inserter10_d20 = paddingInserterTest d20 d10 tests :: TestTree tests = - localOption (mkTimeout 20_000_000 {- 20 seconds -}) $ - localOption + localOption (mkTimeout 20_000_000 {- 20 seconds -}) + $ localOption (HedgehogTestLimit (Just 1_000)) $(testGroupGenerator) diff --git a/test/Test/Cores/Ethernet/Mac/Preamble.hs b/test/Test/Cores/Ethernet/Mac/Preamble.hs index 0d9f824..a0db1a7 100644 --- a/test/Test/Cores/Ethernet/Mac/Preamble.hs +++ b/test/Test/Cores/Ethernet/Mac/Preamble.hs @@ -36,17 +36,17 @@ prop_preamble_stripper = idWithModelSingleDomain @System defExpectOptions{eoStopAfterEmpty = 1000} - (genPackets (Range.linear 1 10) Abort genPkt) + (genPackets 1 10 genPkt) (exposeClockResetEnable preambleStripperModel) (exposeClockResetEnable preambleStripperC) where - genPkt am = + genPkt = Gen.choice [ -- Random valid packet - genValidPacket (pure ()) (Range.linear 0 20) am + genValidPacket defPacketOptions (pure ()) (Range.linear 0 20) , -- Valid packet with SFD set somewhere do - packet <- genValidPacket (pure ()) (Range.linear 0 20) am + packet <- genValidPacket defPacketOptions (pure ()) (Range.linear 0 20) idx <- Gen.int (Range.linear 0 (L.length packet - 1)) pure $ L.zipWith