Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ethernet: support zero-byte transfers. #19

Merged
merged 5 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions fourmolu.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
indentation: 2
column-limit: 90
6 changes: 3 additions & 3 deletions nix/sources.json
Original file line number Diff line number Diff line change
Expand Up @@ -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/<owner>/<repo>/archive/<rev>.tar.gz"
},
"doctest-parallel": {
Expand Down
9 changes: 7 additions & 2 deletions src/Clash/Cores/Ethernet/Mac.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
228 changes: 110 additions & 118 deletions src/Clash/Cores/Ethernet/Mac/FrameCheckSequence.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,29 @@
{-# 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. <[email protected]>

Provides circuits to insert, validate and strip the FCS of Ethernet frames.
-}
module Clash.Cores.Ethernet.Mac.FrameCheckSequence (
fcsInserterC,
fcsValidatorC,
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

Expand All @@ -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
Expand All @@ -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 ())
Expand All @@ -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 ())
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 ())
Expand All @@ -255,6 +246,7 @@ fcsStripperC ::
(HiddenClockResetEnable dom) =>
(KnownNat dataWidth) =>
(1 <= dataWidth) =>
-- | Ethernet FCS stripper circuit
Circuit
(PacketStream dom dataWidth ())
(PacketStream dom dataWidth ())
Expand Down
Loading