ethernet: use new bridge in EthernetInterface

This commit is contained in:
Fischer Moseley 2026-01-02 13:19:50 -07:00
parent 6563446724
commit 226d1afd9a
6 changed files with 130 additions and 203 deletions

View File

@ -4,8 +4,7 @@ from random import getrandbits
from amaranth import *
from amaranth.hdl import IOPort
from manta.ethernet.sink_bridge import UDPSinkBridge
from manta.ethernet.source_bridge import UDPSourceBridge
from manta.ethernet.bridge import EthernetBridge
from manta.utils import *
@ -517,21 +516,20 @@ class EthernetInterface(Elaboratable):
if platform:
platform.add_file("liteeth.v", self.generate_liteeth_core())
m.submodules.source_bridge = source_bridge = UDPSourceBridge()
m.submodules.sink_bridge = sink_bridge = UDPSinkBridge()
m.submodules.bridge = bridge = EthernetBridge()
m.d.comb += source_bridge.data_i.eq(self._source_data)
m.d.comb += source_bridge.last_i.eq(self._source_last)
m.d.comb += self._source_ready.eq(source_bridge.ready_o)
m.d.comb += source_bridge.valid_i.eq(self._source_valid)
m.d.comb += bridge.data_i.eq(self._source_data)
m.d.comb += bridge.last_i.eq(self._source_last)
m.d.comb += self._source_ready.eq(bridge.ready_o)
m.d.comb += bridge.valid_i.eq(self._source_valid)
m.d.comb += self._sink_data.eq(sink_bridge.data_o)
m.d.comb += self._sink_last.eq(sink_bridge.last_o)
m.d.comb += sink_bridge.ready_i.eq(self._sink_ready)
m.d.comb += self._sink_valid.eq(sink_bridge.valid_o)
m.d.comb += self._sink_data.eq(bridge.data_o)
m.d.comb += self._sink_last.eq(bridge.last_o)
m.d.comb += bridge.ready_i.eq(self._sink_ready)
m.d.comb += self._sink_valid.eq(bridge.valid_o)
m.d.comb += sink_bridge.bus_i.eq(self.bus_i)
m.d.comb += self.bus_o.eq(source_bridge.bus_o)
m.d.comb += bridge.bus_i.eq(self.bus_i)
m.d.comb += self.bus_o.eq(bridge.bus_o)
return m

View File

@ -1,19 +1,8 @@
# Test with 32-bit data/valid/ready/last interface for input, and one for output
from amaranth import *
from amaranth.lib.enum import IntEnum
from manta.utils import *
class MessageTypes(IntEnum, shape=unsigned(3)):
READ_REQUEST = 0
WRITE_REQUEST = 1
READ_RESPONSE = 2
WRITE_RESPONSE = 3
NACK = 4
class EthernetBridge(Elaboratable):
def __init__(self):
self.data_i = Signal(32)
@ -116,7 +105,7 @@ class EthernetBridge(Elaboratable):
with m.State("WRITE_WAIT_FOR_ADDR"):
with m.If(self.valid_i):
m.d.sync += self.bus_i.addr.eq(self.data_i)
m.d.sync += self.bus_o.addr.eq(self.data_i)
m.next = "WRITE_FIRST"
# Don't want to increment address on the first write,
@ -177,104 +166,3 @@ class EthernetBridge(Elaboratable):
m.next = "IDLE"
return m
# Actual testing below!
ether_bridge = EthernetBridge()
from random import randint
async def send_bytes(ctx, bytes):
ctx.set(ether_bridge.ready_i, 1)
ctx.set(ether_bridge.valid_i, 1)
for i, byte in enumerate(bytes):
ctx.set(ether_bridge.data_i, byte)
ctx.set(ether_bridge.last_i, i == len(bytes) - 1)
while not ctx.get(ether_bridge.ready_o):
await ctx.tick()
await ctx.tick()
ctx.set(ether_bridge.data_i, 0)
ctx.set(ether_bridge.last_i, 0)
ctx.set(ether_bridge.valid_i, 0)
await ctx.tick()
async def send_bytes_sporadic(ctx, bytes):
ctx.set(ether_bridge.ready_i, 1)
ctx.set(ether_bridge.valid_i, 1)
for i, byte in enumerate(bytes):
if randint(0, 1):
ctx.set(ether_bridge.valid_i, 0)
for _ in range(0, randint(1, 4)):
await ctx.tick()
ctx.set(ether_bridge.valid_i, 1)
ctx.set(ether_bridge.data_i, byte)
ctx.set(ether_bridge.last_i, i == len(bytes) - 1)
while not ctx.get(ether_bridge.ready_o):
await ctx.tick()
await ctx.tick()
ctx.set(ether_bridge.data_i, 0)
ctx.set(ether_bridge.last_i, 0)
ctx.set(ether_bridge.valid_i, 0)
await ctx.tick()
# - type: 3 bits
# - seq_num: 13 bits
# - length (only if read request): 7 bits
async def send_write_request(ctx, seq_num, addr, write_data):
await send_bytes_sporadic(
ctx, [(seq_num << 3) | MessageTypes.WRITE_REQUEST, addr] + write_data
)
async def send_read_request(ctx, seq_num, addr, read_length):
await send_bytes_sporadic(
ctx, [(read_length << 16) | (seq_num << 3) | MessageTypes.READ_REQUEST, addr]
)
@simulate(ether_bridge)
async def test_ether_bridge(ctx):
await ctx.tick()
await ctx.tick()
await ctx.tick()
# Send a read request with a bad sequence number
# await send_read_request(ctx, seq_num=1, addr=0, read_length=1)
# await ctx.tick()
# await send_read_request(ctx, seq_num=1, addr=1, read_length=1)
# await ctx.tick()
# await send_write_request(ctx, seq_num=0, addr=0x1234_5678, write_data=[0x0000_0000, 0x1111_1111, 0x2222_2222])
# ctx.tick()
await send_write_request(
ctx,
seq_num=0,
addr=0x1234_5678,
write_data=[0x0000_0000, 0x1111_1111, 0x2222_2222, 0x3333_3333],
)
# await send_write_request(ctx, seq_num=4, addr=0x1234_5678, write_data=[0x0000_0000, 0x1111_1111, 0x2222_2222])
# await send_read_request(ctx, seq_num=0, addr=0x1234_5678, read_length=10)
# await send_bytes(ctx, [0x0123_4567])
# await send_bytes(ctx, [0x0123_4567, 0x89AB_CDEF])
# await send_bytes(ctx, [0x0123_4567, 0x89AB_CDEF, 0x0123_4567])
# await send_bytes(ctx, [0x0123_4567, 0x89AB_CDEF, 0x0123_4567, 0x89AB_CDEF])
ctx.tick()
for _ in range(20):
await ctx.tick()

View File

@ -1,32 +0,0 @@
from amaranth import *
from manta.utils import *
class UDPSinkBridge(Elaboratable):
"""
A module for bridging Manta's internal bus to an AXI stream of UDP packet
data. Connects to the LiteEth core's "sink" port.
"""
def __init__(self):
self.bus_i = Signal(InternalBus())
self.data_o = Signal(32)
self.last_o = Signal()
self.ready_i = Signal()
self.valid_o = Signal()
def elaborate(self, platform):
m = Module()
m.d.sync += self.data_o.eq(0)
m.d.sync += self.last_o.eq(0)
m.d.sync += self.valid_o.eq(0)
with m.If((self.bus_i.valid) & (~self.bus_i.rw)):
m.d.sync += self.data_o.eq(self.bus_i.data)
m.d.sync += self.last_o.eq(self.bus_i.last)
m.d.sync += self.valid_o.eq(1)
return m

View File

@ -1,43 +0,0 @@
from amaranth import *
from manta.utils import *
class UDPSourceBridge(Elaboratable):
"""
A module for bridging the AXI-stream of incoming UDP packet data to Manta's
internal bus. Connects to the LiteEth core's "source" port.
"""
def __init__(self):
self.bus_o = Signal(InternalBus())
self.data_i = Signal(32)
self.last_i = Signal()
self.ready_o = Signal()
self.valid_i = Signal()
def elaborate(self, platform):
m = Module()
state = Signal() # Can either be 0, for read/write, or 1, for data
rw_buf = Signal().like(self.bus_o.rw)
# Can always take more data
m.d.sync += self.ready_o.eq(1)
m.d.sync += self.bus_o.eq(0)
with m.If(self.valid_i):
m.d.sync += state.eq(~state)
with m.If(state == 0):
m.d.sync += rw_buf.eq(self.data_i)
with m.Else():
m.d.sync += self.bus_o.addr.eq(self.data_i[:16])
m.d.sync += self.bus_o.data.eq(self.data_i[16:])
m.d.sync += self.bus_o.rw.eq(rw_buf)
m.d.sync += self.bus_o.valid.eq(1)
m.d.sync += self.bus_o.last.eq(self.last_i)
return m

View File

@ -3,8 +3,9 @@ from abc import ABC, abstractmethod
from pathlib import Path
from random import sample
from amaranth import Elaboratable
from amaranth import Elaboratable, unsigned
from amaranth.lib import data
from amaranth.lib.enum import IntEnum
from amaranth.sim import Simulator
@ -102,6 +103,14 @@ class InternalBus(data.StructLayout):
)
class MessageTypes(IntEnum, shape=unsigned(3)):
READ_REQUEST = 0
WRITE_REQUEST = 1
READ_RESPONSE = 2
WRITE_RESPONSE = 3
NACK = 4
def warn(message):
"""
Prints a warning to the user's terminal. Originally the warn() method

View File

@ -0,0 +1,107 @@
# Test with 32-bit data/valid/ready/last interface for input, and one for output
from amaranth import *
from amaranth.lib.enum import IntEnum
from manta.ethernet import EthernetBridge
from manta.utils import *
# Actual testing below!
ether_bridge = EthernetBridge()
from random import randint
async def send_bytes(ctx, bytes):
ctx.set(ether_bridge.ready_i, 1)
ctx.set(ether_bridge.valid_i, 1)
for i, byte in enumerate(bytes):
ctx.set(ether_bridge.data_i, byte)
ctx.set(ether_bridge.last_i, i == len(bytes) - 1)
while not ctx.get(ether_bridge.ready_o):
await ctx.tick()
await ctx.tick()
ctx.set(ether_bridge.data_i, 0)
ctx.set(ether_bridge.last_i, 0)
ctx.set(ether_bridge.valid_i, 0)
await ctx.tick()
async def send_bytes_sporadic(ctx, bytes):
ctx.set(ether_bridge.ready_i, 1)
ctx.set(ether_bridge.valid_i, 1)
for i, byte in enumerate(bytes):
if randint(0, 1):
ctx.set(ether_bridge.valid_i, 0)
for _ in range(0, randint(1, 4)):
await ctx.tick()
ctx.set(ether_bridge.valid_i, 1)
ctx.set(ether_bridge.data_i, byte)
ctx.set(ether_bridge.last_i, i == len(bytes) - 1)
while not ctx.get(ether_bridge.ready_o):
await ctx.tick()
await ctx.tick()
ctx.set(ether_bridge.data_i, 0)
ctx.set(ether_bridge.last_i, 0)
ctx.set(ether_bridge.valid_i, 0)
await ctx.tick()
# - type: 3 bits
# - seq_num: 13 bits
# - length (only if read request): 7 bits
async def send_write_request(ctx, seq_num, addr, write_data):
await send_bytes_sporadic(
ctx, [(seq_num << 3) | MessageTypes.WRITE_REQUEST, addr] + write_data
)
async def send_read_request(ctx, seq_num, addr, read_length):
await send_bytes_sporadic(
ctx, [(read_length << 16) | (seq_num << 3) | MessageTypes.READ_REQUEST, addr]
)
@simulate(ether_bridge)
async def test_ether_bridge(ctx):
await ctx.tick()
await ctx.tick()
await ctx.tick()
# Send a read request with a bad sequence number
# await send_read_request(ctx, seq_num=1, addr=0, read_length=1)
# await ctx.tick()
# await send_read_request(ctx, seq_num=1, addr=1, read_length=1)
# await ctx.tick()
# await send_write_request(ctx, seq_num=0, addr=0x1234_5678, write_data=[0x0000_0000, 0x1111_1111, 0x2222_2222])
# ctx.tick()
await send_write_request(
ctx,
seq_num=0,
addr=0x1234_5678,
write_data=[0x0000_0000, 0x1111_1111, 0x2222_2222, 0x3333_3333],
)
# await send_write_request(ctx, seq_num=4, addr=0x1234_5678, write_data=[0x0000_0000, 0x1111_1111, 0x2222_2222])
# await send_read_request(ctx, seq_num=0, addr=0x1234_5678, read_length=10)
# await send_bytes(ctx, [0x0123_4567])
# await send_bytes(ctx, [0x0123_4567, 0x89AB_CDEF])
# await send_bytes(ctx, [0x0123_4567, 0x89AB_CDEF, 0x0123_4567])
# await send_bytes(ctx, [0x0123_4567, 0x89AB_CDEF, 0x0123_4567, 0x89AB_CDEF])
ctx.tick()
for _ in range(20):
await ctx.tick()