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

Commit

Permalink
76 ip loopback hardware test (#144)
Browse files Browse the repository at this point in the history
Write IP lookback hardware test
  • Loading branch information
Akribes authored Jun 1, 2024
1 parent a226c67 commit e85d0e6
Show file tree
Hide file tree
Showing 15 changed files with 308 additions and 157 deletions.
4 changes: 2 additions & 2 deletions clash-eth.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ library
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
Clash.Cores.Ethernet.Examples.RxStacks
Clash.Cores.Ethernet.Examples.TxStacks
Clash.Cores.Ethernet.IP.InternetChecksum
Clash.Cores.Ethernet.IP.IPPacketizers
Clash.Cores.Ethernet.IP.IPv4Types
Expand Down
19 changes: 0 additions & 19 deletions python_tests/test_eth_uart_echo.py

This file was deleted.

66 changes: 66 additions & 0 deletions python_tests/test_ip_echo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import unittest
import util
import os
import socket
import struct
import random

IFNAME = os.environ['IFNAME']
DEV = os.environ['DEV']

def open_socket():
s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(3))
s.bind((IFNAME, 0))
return s

src_ip = '192.168.1.0'
dst_ip = '192.168.1.123'
src_mac = b"\x8c\x8c\xaa\xc8\x2b\xee" # hardcoded in echo stack
dst_mac = b"\x00\x00\x00\xff\xff\xff"

class TestEthUartEcho(unittest.TestCase):
def _test(self, dst_ip):
"""
Sends five IP packets and tests if they are sent back
"""
with open_socket() as s:
s.settimeout(5)

for _ in range(5):
data = os.urandom(random.randint(0,25))
payload = struct.pack("!HHHH", 1337, 1337, len(data) + 8, 0) + data
ip_header = create_ip_header(src_ip, dst_ip, len(payload))
packet = dst_mac + src_mac + b"\x08\x00" + ip_header + payload

s.send(packet)

response = s.recv(2**16 - 1)
total_length = struct.unpack_from("!H", response, 16)[0]
response = response[:total_length + 14]

expected_ip_header = create_ip_header(dst_ip, src_ip, len(payload))
expected = src_mac + dst_mac + b"\x08\x00" + expected_ip_header + payload

self.assertEqual(expected, response)

def testWithIp(self):
self._test(dst_ip)

def testWithBroadcast(self):
self._test("192.168.1.255")

def create_ip_header(src_ip, dst_ip, payload_length):
header = bytearray(struct.pack('!BBHHHBBH4s4s',
(4 << 4) + 5, # version, ihl
0,
20 + payload_length, # total length
0,
0,
64, # TTL. Set to 64 so Wireshark thinks this is a totally normal UDP packet
0, # Protocol. 0, because we can't set this in ipLitePacketizerC
0, # checksum
socket.inet_aton(src_ip),
socket.inet_aton(dst_ip)))
checksum = util.internet_checksum(header)
header[10:12] = struct.pack("!H", checksum)
return bytes(header)
18 changes: 0 additions & 18 deletions python_tests/test_uart_to_eth.py

This file was deleted.

11 changes: 11 additions & 0 deletions python_tests/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,14 @@ def make_uart_packet(data: bytes):
if data == b'':
return b''
return struct.pack("<H", len(data) - 1) + data

def internet_checksum(data):
if len(data) % 2 != 0:
data += b'\x00'

s = sum(struct.unpack("!%dH" % (len(data) // 2), data))
s = (s >> 16) + (s & 0xffff)
s += s >> 16
s = ~s & 0xffff

return s
8 changes: 4 additions & 4 deletions src/Clash/Cores/Ethernet/Examples/ArpStack.hs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ 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.Examples.RxStacks
import Clash.Cores.Ethernet.Examples.TxStacks
import Clash.Cores.Ethernet.IP.IPv4Types
import Clash.Cores.Ethernet.Mac.EthernetTypes

Expand Down Expand Up @@ -56,9 +56,9 @@ arpStackC
-> 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
ethStream <- macRxStack @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
macTxStack txClk txRst txEn -< ethOtp
31 changes: 15 additions & 16 deletions src/Clash/Cores/Ethernet/Examples/EchoStack.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,29 @@ Module : Clash.Cores.Ethernet.Examples.EchoStack
Description : Simple Ethernet echo stack.
-}
module Clash.Cores.Ethernet.Examples.EchoStack
( echoStackC
( ipEchoStackC
) where

-- import prelude
import Clash.Prelude

-- import ethernet
import Clash.Cores.Ethernet.Examples.RxStack ( rxStack )
import Clash.Cores.Ethernet.Examples.TxStack ( txStack )
import Clash.Cores.Ethernet.Mac.EthernetTypes ( EthernetHeader(..), MacAddress(..) )
import Clash.Cores.Ethernet.Examples.RxStacks ( ipRxStack )
import Clash.Cores.Ethernet.Examples.TxStacks ( ipTxStack )
import Clash.Cores.Ethernet.Mac.EthernetTypes ( MacAddress(..) )

-- import protocols
import Protocols ( Circuit, (|>) )
import Protocols
import Protocols.Extra.PacketStream
import Protocols.Extra.PacketStream.PacketBuffer ( packetBufferC )

import Clash.Cores.Crc ( HardwareCrc )
import Clash.Cores.Crc.Catalog ( Crc32_ethernet )

import Clash.Cores.Ethernet.IP.IPv4Types

myMac :: MacAddress
myMac = MacAddress $ 0x00 :> 0x00 :> 0x00 :> 0xff :> 0xff :> 0xff :> Nil

-- | Processes ethernet frames and echoes them back
echoStackC
-- | Processes IP packets and echoes them back
ipEchoStackC
:: forall
(dom :: Domain)
(domEthRx :: Domain)
Expand All @@ -46,12 +44,13 @@ echoStackC
-> Clock domEthTx
-> Reset domEthTx
-> Enable domEthTx
-> Signal dom MacAddress
-> Signal dom (IPv4Address, IPv4Address)
-> Circuit (PacketStream domEthRx 1 ()) (PacketStream domEthTx 1 ())
echoStackC rxClk rxRst rxEn txClk txRst txEn = ckt
ipEchoStackC rxClk rxRst rxEn txClk txRst txEn mac ip = ckt
where
swapMac hdr@EthernetHeader {..} = hdr { _macSrc = _macDst, _macDst = _macSrc}
ckt = rxStack @4 rxClk rxRst rxEn (pure myMac)
swapIp hdr@IPv4HeaderLite {..} = hdr { _ipv4lSource = _ipv4lDestination, _ipv4lDestination = _ipv4lSource}
ckt = ipRxStack @4 rxClk rxRst rxEn mac ip
|> packetBufferC d10 d4
|> mapMeta swapMac
|> txStack txClk txRst txEn

|> mapMeta swapIp
|> ipTxStack @4 txClk txRst txEn mac
51 changes: 0 additions & 51 deletions src/Clash/Cores/Ethernet/Examples/RxStack.hs

This file was deleted.

76 changes: 76 additions & 0 deletions src/Clash/Cores/Ethernet/Examples/RxStacks.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
{-# language FlexibleContexts #-}

{-|
Module : Clash.Cores.Ethernet.Examples.RxStacks
Description : Provides the entire receive stack as a circuit.
-}
module Clash.Cores.Ethernet.Examples.RxStacks
( macRxStack
, ipRxStack
) where

import Clash.Cores.Crc
import Clash.Cores.Crc.Catalog
import Clash.Prelude

import Protocols
import Protocols.Extra.PacketStream
import Protocols.Extra.PacketStream.AsyncFIFO ( asyncFifoC )
import Protocols.Extra.PacketStream.Converters ( upConverterC )
import Protocols.Extra.PacketStream.Routing ( packetDispatcherC )

import Clash.Cores.Ethernet.IP.IPPacketizers
import Clash.Cores.Ethernet.IP.IPv4Types
import Clash.Cores.Ethernet.Mac.EthernetTypes
import Clash.Cores.Ethernet.Mac.FrameCheckSequence ( fcsValidatorC )
import Clash.Cores.Ethernet.Mac.MacPacketizers ( macDepacketizerC )
import Clash.Cores.Ethernet.Mac.Preamble ( preambleStripperC )

-- | Processes received ethernet frames
macRxStack
:: forall (dataWidth :: Nat) (dom :: Domain) (domEth :: Domain)
. ( HiddenClockResetEnable dom
, KnownDomain domEth
, HardwareCrc Crc32_ethernet 8 dataWidth
, KnownNat dataWidth
, 1 <= dataWidth
)
=> Clock domEth
-> Reset domEth
-> Enable domEth
-> Signal dom MacAddress
-> Circuit (PacketStream domEth 1 ()) (PacketStream dom dataWidth EthernetHeader)
macRxStack ethClk ethRst ethEn macAddressS =
upConverterC'
|> asyncFifoC'
|> preambleStripperC
|> fcsValidatorC
|> macDepacketizerC
|> filterMetaS (isForMyMac <$> macAddressS)
where
upConverterC' = exposeClockResetEnable upConverterC ethClk ethRst ethEn
asyncFifoC' = asyncFifoC d4 ethClk ethRst ethEn hasClock hasReset hasEnable
isForMyMac myMac (_macDst -> to) = to == myMac || to == broadcastMac

-- | Processes received IP packets
ipRxStack
:: forall (dataWidth :: Nat) (dom :: Domain) (domEth :: Domain)
. ( HiddenClockResetEnable dom
, KnownDomain domEth
, HardwareCrc Crc32_ethernet 8 dataWidth
, KnownNat dataWidth
, 1 <= dataWidth
)
=> Clock domEth
-> Reset domEth
-> Enable domEth
-> Signal dom MacAddress
-> Signal dom (IPv4Address, IPv4Address)
-> Circuit (PacketStream domEth 1 ()) (PacketStream dom dataWidth IPv4HeaderLite)
ipRxStack ethClk ethRst ethEn macAddressS ipS = circuit $ \raw -> do
ethernetFrames <- macRxStack ethClk ethRst ethEn macAddressS -< raw
[ip] <- packetDispatcherC (isIpv4 :> Nil) -< ethernetFrames
ipDepacketizerLiteC |> filterMetaS (isForMyIp <$> ipS) -< ip
where
isIpv4 = (== 0x0800) . _etherType
isForMyIp (ip, subnet) (_ipv4lDestination -> to) = to == ip || to == ipv4Broadcast ip subnet
Loading

0 comments on commit e85d0e6

Please sign in to comment.