Add integrated UART UDP bring-up shell (socket 3) + capture critical-path fix
An interactive UART command shell drives a UDP send/receive test on the W5100's socket 3, running ALONGSIDE the live BBA (socket-0 MACRAW) with EXI keeping bus priority. MACRAW+UDP coexistence is W5100S-datasheet confirmed (S4.6 + "4 independent SOCKETs"). Now the default flash build (--console selects the old event-log console). New / changed gateware: - uart_shell.py (new): rebbarb> shell over FT2232H channel B. Commands: help; udp unicast <ip> [msg]; udp broadcast [msg]. After each send it waits (bounded, else "timeout") for a reply and prints "rx <payload>". Line buffer + message ROM live in block RAM with a sequential parser (LC-efficient); 1-deep RX holding reg keeps pastes intact. 8 sim tests. - w5100_parallel_master.py: configurable UDP socket (default 3) with UDP send AND receive (IP-stack init, runtime dest IP, WIZnet UDP RX header + payload). Gated by enable_udp_test so the MACRAW path is unchanged when off. Tests U1-U4 + MACRAW T1-T5. - exi_capture.py: CAPTURE-DOMAIN CRITICAL-PATH FIX. The TX byte-FIFO read-enable was gated by its own gray-coded ready (r_en = ... | (flushing & r_rdy)), forming a consume_ptr -> gray -> r_rdy -> flush -> r_en -> consume_ptr loop that capped capture_clk. Replaced the r_rdy-based "drain until empty" flush with a fixed-length drain counter (FIFO is only tx_depth deep), removing the pointer feedback from r_en. Path 24.3 -> 19.6 ns; flush behavior preserved. - bba_top.py: wire shell <-> W5100 UDP (send + rx); shell additive. - synth.py: shell default build; env-var UDP network config; documents a reverted PNR-timing-priority experiment. Timing (--seeds 8, default shell build, 67% LC): capture closes on 4/8 seeds (best seed 4 = 58.36 MHz, +8%), clk passes on all. This is BETTER than the pre-shell 2/8 baseline because the flush fix improved the capture domain intrinsically. Flash build/seed4/top.bin. Bring-up caveats (unchanged): W5100 socket register addresses / UDP header format are datasheet-derived (confirm on hardware); UDP_SRC_IP / subnet / gateway must match the LAN for unicast ARP. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+49
-3
@@ -24,6 +24,7 @@ from exi_bba.w5500_spi_master import W5500SPIMaster
|
||||
from exi_bba.w5100_parallel_master import W5100ParallelMaster
|
||||
from exi_bba.status_panel import StatusPanel
|
||||
from exi_bba.uart_console import UARTConsole
|
||||
from exi_bba.uart_shell import UARTShell
|
||||
|
||||
from amaranth.lib.cdc import FFSynchronizer
|
||||
|
||||
@@ -48,7 +49,11 @@ class BBATop(Elaboratable):
|
||||
"""
|
||||
|
||||
def __init__(self, eth="w5100", reset_cycles=24000,
|
||||
status_panel=False, uart_console=False):
|
||||
status_panel=False, uart_console=False, uart_shell=False,
|
||||
udp_socket=3, udp_src_ip="192.168.1.123",
|
||||
udp_subnet="255.255.255.0", udp_gateway="192.168.1.1",
|
||||
udp_dst_ip="192.168.1.100",
|
||||
udp_src_port=40000, udp_dst_port=6464):
|
||||
# Ethernet back-end: "w5100" (indirect parallel bus, reaches the EXI
|
||||
# ceiling) or "w5500" (SPI, ~12 Mbit/s). Both expose the identical
|
||||
# tx/rx/init/par interface, so only the physical pins differ.
|
||||
@@ -64,6 +69,19 @@ class BBATop(Elaboratable):
|
||||
# uart_rx ← FT2232H Channel B (net UART_TXD, pin 18) — net names are
|
||||
# FTDI-perspective, so this is not a naming mismatch, see synth.py.
|
||||
self._uart_console = uart_console
|
||||
# Optional interactive UART command shell (bring-up UDP test on the
|
||||
# W5100's socket 1). Shares the UART pins with the console, so only one
|
||||
# may be enabled; the UDP path needs the W5100 back-end. See uart_shell.
|
||||
self._uart_shell = uart_shell
|
||||
if uart_console and uart_shell:
|
||||
raise ValueError("uart_console and uart_shell share the UART pins; "
|
||||
"enable only one")
|
||||
if uart_shell and eth != "w5100":
|
||||
raise ValueError("uart_shell UDP test requires eth='w5100'")
|
||||
self._udp_cfg = dict(udp_socket=udp_socket, src_ip=udp_src_ip,
|
||||
subnet=udp_subnet, gateway=udp_gateway,
|
||||
dst_ip=udp_dst_ip, src_port=udp_src_port,
|
||||
dst_port=udp_dst_port)
|
||||
|
||||
# EXI (GC side)
|
||||
self.exi_clk = Signal(init=1)
|
||||
@@ -99,7 +117,7 @@ class BBATop(Elaboratable):
|
||||
self.panel_led = Signal(5) # to onboard LEDs (see StatusPanel)
|
||||
self.panel_btn = Signal(3) # from onboard button(s)
|
||||
|
||||
if uart_console:
|
||||
if uart_console or uart_shell:
|
||||
self.uart_tx = Signal(init=1) # FPGA → PC (FT2232H Channel B)
|
||||
self.uart_rx = Signal(init=1) # PC → FPGA
|
||||
|
||||
@@ -161,7 +179,9 @@ class BBATop(Elaboratable):
|
||||
drain = TXFrameDrain()
|
||||
eth = (W5500SPIMaster(reset_cycles=self._reset_cycles)
|
||||
if self._eth == "w5500"
|
||||
else W5100ParallelMaster(reset_cycles=self._reset_cycles))
|
||||
else W5100ParallelMaster(reset_cycles=self._reset_cycles,
|
||||
enable_udp_test=self._uart_shell,
|
||||
**self._udp_cfg))
|
||||
|
||||
m.submodules.cap = cap
|
||||
m.submodules.reg = reg
|
||||
@@ -335,6 +355,32 @@ class BBATop(Elaboratable):
|
||||
console.uart_rx .eq(self.uart_rx),
|
||||
]
|
||||
|
||||
if self._uart_shell:
|
||||
# Interactive command shell driving the W5100 socket-1 UDP test.
|
||||
# `eth` is the W5100 master built with enable_udp_test above.
|
||||
shell = UARTShell()
|
||||
m.submodules.shell = shell
|
||||
m.d.comb += [
|
||||
self.uart_tx .eq(shell.uart_tx),
|
||||
shell.uart_rx .eq(self.uart_rx),
|
||||
# send
|
||||
eth.udp_send_req .eq(shell.udp_send_req),
|
||||
eth.udp_dst_ip .eq(shell.udp_dst_ip),
|
||||
eth.udp_pl_data .eq(shell.udp_pl_data),
|
||||
eth.udp_pl_valid .eq(shell.udp_pl_valid),
|
||||
eth.udp_pl_last .eq(shell.udp_pl_last),
|
||||
shell.udp_pl_ready .eq(eth.udp_pl_ready),
|
||||
shell.udp_test_busy .eq(eth.udp_test_busy),
|
||||
# receive (reply)
|
||||
eth.udp_rx_req .eq(shell.udp_rx_req),
|
||||
shell.udp_rx_busy .eq(eth.udp_rx_busy),
|
||||
shell.udp_rx_none .eq(eth.udp_rx_none),
|
||||
shell.udp_rx_data .eq(eth.udp_rx_data),
|
||||
shell.udp_rx_valid .eq(eth.udp_rx_valid),
|
||||
shell.udp_rx_eof .eq(eth.udp_rx_eof),
|
||||
eth.udp_rx_ready .eq(shell.udp_rx_ready),
|
||||
]
|
||||
|
||||
if need_ready:
|
||||
with m.If(eth.init_done):
|
||||
m.d.sync += ready.eq(1)
|
||||
|
||||
Reference in New Issue
Block a user