Skip to content
This repository has been archived by the owner on Sep 6, 2024. It is now read-only.

Commit

Permalink
111 implement arp top level module (#138)
Browse files Browse the repository at this point in the history
arp top level component
  • Loading branch information
t-wallet authored May 30, 2024
1 parent fe25f93 commit a226c67
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 12 deletions.
2 changes: 2 additions & 0 deletions clash-eth.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,11 @@ library
import: common-options
hs-source-dirs: src
exposed-modules:
Clash.Cores.Ethernet.Arp
Clash.Cores.Ethernet.Arp.ArpManager
Clash.Cores.Ethernet.Arp.ArpTable
Clash.Cores.Ethernet.Arp.ArpTypes
Clash.Cores.Ethernet.Examples.ArpStack
Clash.Cores.Ethernet.Examples.EchoStack
Clash.Cores.Ethernet.Examples.RxStack
Clash.Cores.Ethernet.Examples.TxStack
Expand Down
72 changes: 72 additions & 0 deletions src/Clash/Cores/Ethernet/Arp.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{-|
Module : Clash.Cores.Ethernet.Arp
Description : Provides a fully functional ARP stack.
-}

{-# language FlexibleContexts #-}
{-# OPTIONS -fplugin=Protocols.Plugin #-}

module Clash.Cores.Ethernet.Arp where

import Clash.Prelude

import Protocols
import Protocols.Df qualified as Df
import Protocols.Extra.PacketStream

import Clash.Cores.Ethernet.Arp.ArpManager
import Clash.Cores.Ethernet.Arp.ArpTable
import Clash.Cores.Ethernet.Arp.ArpTypes
import Clash.Cores.Ethernet.IP.IPv4Types
import Clash.Cores.Ethernet.Mac.EthernetTypes


-- | A fully functional ARP stack which handles ARP lookups from client circuits.
-- Maintains a single-entry ARP table which the client circuit can query via the
-- `ArpLookup` input. If the client-supplied IPv4 address is not found in the table,
-- it transmits an ARP request for this specific address. The circuit will assert
-- backpressure until either a reply has been received, or a timeout occurs. The
-- maximum number of seconds the stack will wait for a reply to this request is
-- configurable. The timeout (in seconds) of ARP table entries is configurable as well.
-- All timeouts may be up to a second inaccurate.
--
-- Moreover, it takes in an Ethernet stream with the ARP
-- etherType (0x0806), and updates the ARP table upon receiving a valid ARP
-- reply or gratitious ARP request. Gratitious ARP replies are ignored for now.
-- If a normal ARP request is received, it transmits a reply.
--
-- Does not support Proxy ARP.
arpC
:: forall
(dom :: Domain)
(maxAgeSeconds :: Nat)
(maxWaitSeconds :: Nat)
(dataWidth :: Nat)
. HiddenClockResetEnable dom
=> KnownNat dataWidth
=> KnownNat (DomainPeriod dom)
=> DomainPeriod dom <= 5 * 10^11
=> 1 <= DomainPeriod dom
=> 1 <= maxAgeSeconds
=> 1 <= maxWaitSeconds
=> 1 <= dataWidth
=> SNat maxAgeSeconds
-- ^ ARP entries will expire after this many seconds
-> SNat maxWaitSeconds
-- ^ The maximum amount of seconds we wait for an incoming ARP reply
-- if the lookup IPv4 address was not found in our ARP table
-> Signal dom MacAddress
-- ^ Our MAC address
-> Signal dom IPv4Address
-- ^ Our IPv4 address
-> Circuit (PacketStream dom dataWidth EthernetHeader, ArpLookup dom)
(PacketStream dom dataWidth EthernetHeader)
arpC maxAge maxWait ourMacS ourIPv4S =
-- TODO waiting for an ARP reply in seconds is too coarse.
-- Make this timer less coarse, e.g. milliseconds
circuit $ \(ethStream, lookupIn) -> do
(entry, replyOut) <- arpReceiverC ourIPv4S -< ethStream
(lookupOut, requestOut) <- arpManagerC maxWait -< lookupIn
() <- arpTable maxAge -< (lookupOut, entry)
arpPktOut <- Df.roundrobinCollect Df.Skip -< [replyOut, requestOut]
arpTransmitterC ourMacS ourIPv4S -< arpPktOut
10 changes: 7 additions & 3 deletions src/Clash/Cores/Ethernet/Arp/ArpManager.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
Module : Clash.Cores.Ethernet.Arp.ArpManager
Description : Provides an ARP manager which handles ARP lookups from client circuits.
-}
module Clash.Cores.Ethernet.Arp.ArpManager where
module Clash.Cores.Ethernet.Arp.ArpManager
( arpManagerC
, arpReceiverC
, arpTransmitterC
) where

import Clash.Prelude
import Clash.Signal.Extra
Expand Down Expand Up @@ -107,7 +111,7 @@ arpManagerC SNat = fromSignals ckt
mealyB arpManagerT (AwaitLookup @maxWaitSeconds) (lookupIPv4S, arpResponseInS, ackInS, secondTimer)

-- | Transmits ARP packets upon request.
arpTransmitter
arpTransmitterC
:: forall (dom :: Domain)
(dataWidth :: Nat)
. HiddenClockResetEnable dom
Expand All @@ -118,7 +122,7 @@ arpTransmitter
-> Signal dom IPv4Address
-- ^ Our IPv4 address
-> Circuit (Df dom ArpLite) (PacketStream dom dataWidth EthernetHeader)
arpTransmitter ourMacS ourIPv4S = fromSignals bundleWithSrc |> packetizeFromDfC toEthernetHdr constructArpPkt
arpTransmitterC ourMacS ourIPv4S = fromSignals bundleWithSrc |> packetizeFromDfC toEthernetHdr constructArpPkt
where
bundleWithSrc (fwdIn, bwdIn) = (bwdIn, go <$> bundle (ourMacS, ourIPv4S, fwdIn))
go (ourMac, ourIPv4, maybeArpLite) = maybeArpLite >>= \arpLite -> Df.Data (ourMac, ourIPv4, arpLite)
Expand Down
64 changes: 64 additions & 0 deletions src/Clash/Cores/Ethernet/Examples/ArpStack.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{-|
Module : Clash.Cores.Ethernet.Examples.ArpStack
Description : Fully functional ARP stack.
-}

{-# language FlexibleContexts #-}

module Clash.Cores.Ethernet.Examples.ArpStack
( arpStackC
) where

import Clash.Prelude

import Clash.Cores.Crc
import Clash.Cores.Crc.Catalog
import Clash.Cores.Ethernet.Arp
import Clash.Cores.Ethernet.Arp.ArpTypes
import Clash.Cores.Ethernet.Examples.RxStack
import Clash.Cores.Ethernet.Examples.TxStack
import Clash.Cores.Ethernet.IP.IPv4Types
import Clash.Cores.Ethernet.Mac.EthernetTypes

import Protocols
import Protocols.Extra.PacketStream
import Protocols.Extra.PacketStream.Routing


-- | TODO replace this by the IPv4 -> Ethernet stream transformer
constArpLookup :: Circuit () (ArpLookup dom)
constArpLookup = Circuit $ \((), _bwdIn) -> ((), fwdOut)
where
fwdOut = pure (Just (IPv4Address (192 :> 168 :> 1 :> 254 :> Nil)))

-- | Fully functional ARP stack.
arpStackC
:: forall
(dom :: Domain)
(domEthRx :: Domain)
(domEthTx :: Domain)
. KnownDomain dom
=> KnownDomain domEthRx
=> KnownDomain domEthTx
=> KnownNat (DomainPeriod dom)
=> DomainPeriod dom <= 5 * 10^11
=> 1 <= DomainPeriod dom
=> HardwareCrc Crc32_ethernet 8 4
=> HiddenClockResetEnable dom
=> Clock domEthRx
-> Reset domEthRx
-> Enable domEthRx
-> Clock domEthTx
-> Reset domEthTx
-> Enable domEthTx
-> Signal dom MacAddress
-> Signal dom IPv4Address
-> Circuit (PacketStream domEthRx 1 ()) (PacketStream domEthTx 1 ())
arpStackC rxClk rxRst rxEn txClk txRst txEn ourMacS ourIPv4S =
circuit $ \stream -> do
ethStream <- rxStack @4 rxClk rxRst rxEn ourMacS -< stream
[arpStream] <- packetDispatcherC (singleton $ \hdr -> _etherType hdr == arpEtherType) -< ethStream
lookupIn <- constArpLookup -< ()
arpOtp <- arpC d10 d5 ourMacS ourIPv4S -< (arpStream, lookupIn)
ethOtp <- packetArbiterC RoundRobin -< [arpOtp]
txStack txClk txRst txEn -< ethOtp
4 changes: 2 additions & 2 deletions src/Clash/Cores/Ethernet/Examples/TxStack.hs
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,5 @@ txStack ethClk ethRst ethEn = ckt
|> fcsInserterC
|> preambleInserterC
|> asyncFifoC d4 hasClock hasReset hasEnable ethClk ethRst ethEn
|> (exposeClockResetEnable downConverterC ethClk ethRst ethEn)
|> (exposeClockResetEnable interpacketGapInserterC ethClk ethRst ethEn) d12
|> exposeClockResetEnable downConverterC ethClk ethRst ethEn
|> exposeClockResetEnable interpacketGapInserterC ethClk ethRst ethEn d12
20 changes: 14 additions & 6 deletions src/Clash/Lattice/ECP5/Colorlight/TopEntity.hs
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,21 @@ module Clash.Lattice.ECP5.Colorlight.TopEntity
) where

import Clash.Annotations.TH
import Clash.Cores.Crc ( deriveHardwareCrc )
import Clash.Cores.Crc.Catalog ( Crc32_ethernet )

import Clash.Explicit.Prelude
import Clash.Prelude ( exposeClockResetEnable )

import Protocols ( toSignals, (|>) )

import Clash.Cores.Ethernet.Examples.EchoStack ( echoStackC )
import Clash.Cores.Crc ( deriveHardwareCrc )
import Clash.Cores.Crc.Catalog ( Crc32_ethernet )
import Clash.Cores.Ethernet.Examples.ArpStack
import Clash.Cores.Ethernet.IP.IPv4Types ( IPv4Address(IPv4Address) )
import Clash.Cores.Ethernet.Mac.EthernetTypes ( MacAddress(MacAddress) )
import Clash.Lattice.ECP5.Colorlight.CRG
import Clash.Lattice.ECP5.Prims
import Clash.Lattice.ECP5.RGMII ( RGMIIRXChannel(..), RGMIITXChannel(..), rgmiiTxC, unsafeRgmiiRxC )

import Protocols ( toSignals, (|>) )

import Data.Proxy ( Proxy(Proxy) )


Expand Down Expand Up @@ -80,9 +83,14 @@ topEntity clk25 uartRxBit _dq_in _mdio_in eth0_rx _eth1_rx =
ethRxEn = enableGen @DomEth0
ethTxEn = enableGen @DomEthTx

-- Replace this with your FPGA's MAC address
ourMac = MacAddress (0x00 :> 0xE0 :> 0x6C :> 0x38 :> 0xCF :> 0xF0 :> Nil)
-- Hardcoded IPv4
ourIPv4 = IPv4Address (192 :> 168 :> 1 :> 123 :> Nil)

phyStack
= exposeClockResetEnable (unsafeRgmiiRxC @DomEth0 @DomDDREth0 (delayg d80) iddrx1f) ethRxClk ethRxRst ethRxEn
|> exposeClockResetEnable (echoStackC ethRxClk ethRxRst ethRxEn ethTxClk ethTxRst ethTxEn) clk50 rst50 en50
|> exposeClockResetEnable (arpStackC ethRxClk ethRxRst ethRxEn ethTxClk ethTxRst ethTxEn (pure ourMac) (pure ourIPv4)) clk50 rst50 en50
|> exposeClockResetEnable (rgmiiTxC @DomEthTx @DomDDREth0 (delayg d0) oddrx1f) ethTxClk ethTxRst ethTxEn

uartTxBit = uartRxBit
Expand Down
2 changes: 1 addition & 1 deletion tests/Test/Cores/Ethernet/Arp/ArpManager.hs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ arpTransmitterPropertyGenerator C.SNat =
defExpectOptions
(Gen.list (Range.linear 1 100) genArpLite)
(C.exposeClockResetEnable model)
(C.exposeClockResetEnable @C.System (arpTransmitter (pure ourMac) (pure ourIP)))
(C.exposeClockResetEnable @C.System (arpTransmitterC (pure ourMac) (pure ourIP)))
(===)
where
model :: [ArpLite] -> [PacketStreamM2S dataWidth EthernetHeader]
Expand Down

0 comments on commit a226c67

Please sign in to comment.