Finished the board including the connector and mousebites
This commit is contained in:
+3
-1
@@ -60,7 +60,9 @@ class BBATop(Elaboratable):
|
||||
# iCEbreaker — see synth.py). panel_led bit order matches StatusPanel.
|
||||
self._status_panel = status_panel
|
||||
# Optional UART debug console (8N1 115200, sync domain).
|
||||
# uart_tx → FT2232H Channel B pin 9; uart_rx ← pin 6.
|
||||
# uart_tx → FT2232H Channel B (net UART_RXD, pin 19);
|
||||
# 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
|
||||
|
||||
# EXI (GC side)
|
||||
|
||||
+88
-56
@@ -1,4 +1,4 @@
|
||||
"""Synthesis script for BBATop → iCEbreaker (iCE40UP5K SG48).
|
||||
"""Synthesis script for BBATop → re-bba-rb interposer board (iCE40UP5K SG48).
|
||||
|
||||
Run from workspace root:
|
||||
python -m exi_bba.synth # synthesize only
|
||||
@@ -20,19 +20,43 @@ from exi_bba.bba_top import BBATop
|
||||
|
||||
|
||||
# ── Platform definition ───────────────────────────────────────────────────
|
||||
# Pin assignments use the iCEbreaker PMOD connectors as placeholders.
|
||||
# Replace with actual SP1-interposer pin numbers once PCB is finalised.
|
||||
# Real re-bba-rb board pin map, pulled directly from the schematic netlist
|
||||
# (U9 = iCE40UP5K-SG48ITR) — not iCEbreaker PMOD placeholders.
|
||||
#
|
||||
# PMOD1A (J2): pins 4 2 47 45 / 3 48 46 44 (top/bottom)
|
||||
# PMOD1B (J3): pins 43 38 34 31 / 42 36 32 28
|
||||
# PMOD2 (J4): pins 27 25 21 19 / 26 23 20 18
|
||||
# EXI (GC side) : CLK=44(G6) MOSI=4(IOB_8a) MISO=3(IOB_9b) CS=45 INT=46
|
||||
# W5100 bus (IOT bank, indirect parallel):
|
||||
# A0=42 A1=38 D0=34 D1=32 D2=31 D3=28 D4=27 D5=26 D6=25 D7=23
|
||||
# CS_N=43 RD_N=37 WR_N=36 RST_N=2 (net /ethernet/ETH_RST, NOT on the IOT
|
||||
# bank — separate pin)
|
||||
# Board ties the W5100's upper address lines A[14:2] to 0 (only A[1:0]
|
||||
# wired); DATA[7:0] is bidirectional (SB_IO tristate, shared output-enable).
|
||||
#
|
||||
# EXI : CLK=4 MOSI=2 MISO=47 CS_N=45 INT_N=3 (PMOD1A)
|
||||
# W5100 : indirect parallel bus — 15 pins across PMOD1B + PMOD2.
|
||||
# ADDR[1:0]=43 38 DATA[7:0]=34 31 42 36 32 28 27 25
|
||||
# CS_N=21 RD_N=19 WR_N=26 INT_N=23 RST_N=20 (pin 18 free)
|
||||
# Board: tie the W5100's upper address lines A[14:2] to 0 (only A[1:0] wired);
|
||||
# DATA[7:0] is bidirectional (SB_IO tristate, single shared output-enable).
|
||||
# INT_N=13 — MOVED off the schematic's original pin 35: pin 35 physically
|
||||
# conflicts with the iCE40UP5K's PLL hard macro (nextpnr: "PLL bel
|
||||
# 'X12/Y31/pll_3' cannot be used... conflicts with input... on pin 35"),
|
||||
# and this design needs the PLL for the 54 MHz capture-domain clock. Pin 35
|
||||
# cannot be used as regular I/O at all while the PLL is instantiated,
|
||||
# regardless of what drives it — this is a fixed silicon constraint, not a
|
||||
# routing choice. Pin 13 was picked because it's the ONLY spare GPIO on this
|
||||
# exact SG48 package/board combo: the 5k-sg48 package has 39 usable I/O pins
|
||||
# total (per icestorm's icebox.py pin database), and every other one is
|
||||
# already assigned to a real signal on this board — pin 13 shows up in the
|
||||
# schematic netlist as an unrouted U9 pad, nothing else was available.
|
||||
# Board is still in design, so PIN 13 ON THE SCHEMATIC/PCB MUST BE REWORKED
|
||||
# to carry ETH_INT instead of pin 35 before layout is final.
|
||||
#
|
||||
# Debug (J4 header, unpopulated): DBG0=9 DBG1=10 DBG2=11 DBG3=12 DBG4=6
|
||||
# UART (FT2232H channel B): net UART_TXD is the FTDI's OUTPUT, so it is the
|
||||
# FPGA's RX input, and vice versa for UART_RXD — FPGA RX=18(UART_TXD net),
|
||||
# FPGA TX=19(UART_RXD net). Do not swap by "TXD means transmit" instinct.
|
||||
# Status LEDs: D6(green,heartbeat)=LED_G=pin47, D7(red,EXI-activity)=LED_R=
|
||||
# pin48 — both wired ACTIVE-HIGH (LED anode toward the FPGA pin via its
|
||||
# series resistor, cathode to GND), unlike the iCEbreaker's own onboard
|
||||
# LEDs which are active-low. No physical button exists on this board (the
|
||||
# iCEbreaker's BTN_N was dev-board-only); panel_btn is tied idle instead.
|
||||
# RGB status LED (D11=red/rx, D12=green/tx, D13=yellow/ready) is on the
|
||||
# iCE40UP5K's dedicated SB_RGBA_DRV pins 39/40/41 — fixed by the chip
|
||||
# package on any board, not board-specific, so no resource needed here.
|
||||
|
||||
class IceBreakerPlatform(LatticeICE40Platform):
|
||||
device = "iCE40UP5K"
|
||||
@@ -45,43 +69,38 @@ class IceBreakerPlatform(LatticeICE40Platform):
|
||||
Clock(12e6),
|
||||
Attrs(GLOBAL=True, IO_STANDARD="SB_LVCMOS")),
|
||||
|
||||
# EXI interface (GC side, SPI Mode 3) — PMOD1A FPGA pins
|
||||
# EXI interface (GC side, SPI Mode 3)
|
||||
Resource("exi", 0,
|
||||
Subsignal("clk", Pins("4", dir="i")),
|
||||
Subsignal("mosi", Pins("2", dir="i")),
|
||||
Subsignal("miso", Pins("47", dir="o")),
|
||||
Subsignal("clk", Pins("44", dir="i")),
|
||||
Subsignal("mosi", Pins("4", dir="i")),
|
||||
Subsignal("miso", Pins("3", dir="o")),
|
||||
Subsignal("cs_n", Pins("45", dir="i")),
|
||||
Subsignal("int_n", Pins("3", dir="o")),
|
||||
Subsignal("int_n", Pins("46", dir="o")),
|
||||
Attrs(IO_STANDARD="SB_LVCMOS")),
|
||||
|
||||
# W5100 indirect parallel bus — PMOD1B + PMOD2 FPGA pins
|
||||
# W5100 indirect parallel bus
|
||||
Resource("w5100", 0,
|
||||
Subsignal("addr", Pins("43 38", dir="o")),
|
||||
Subsignal("data", Pins("34 31 42 36 32 28 27 25", dir="io")),
|
||||
Subsignal("cs_n", Pins("21", dir="o")),
|
||||
Subsignal("rd_n", Pins("19", dir="o")),
|
||||
Subsignal("wr_n", Pins("26", dir="o")),
|
||||
Subsignal("int_n", Pins("23", dir="i")),
|
||||
Subsignal("rst_n", Pins("18", dir="o")),
|
||||
Subsignal("addr", Pins("42 38", dir="o")),
|
||||
Subsignal("data", Pins("34 32 31 28 27 26 25 23", dir="io")),
|
||||
Subsignal("cs_n", Pins("43", dir="o")),
|
||||
Subsignal("rd_n", Pins("37", dir="o")),
|
||||
Subsignal("wr_n", Pins("36", dir="o")),
|
||||
Subsignal("int_n", Pins("13", dir="i")),
|
||||
Subsignal("rst_n", Pins("2", dir="o")),
|
||||
Attrs(IO_STANDARD="SB_LVCMOS")),
|
||||
|
||||
# Bring-up status panel → iCEbreaker ONBOARD parts (dedicated pins, not
|
||||
# on any PMOD, so they coexist with EXI + W5100). LEDR/LEDG are
|
||||
# active-low discrete LEDs; BTN_N is the user button.
|
||||
# RGB LED (pins 39/40/41) is driven via SB_RGBA_DRV — not declared here
|
||||
# as a platform resource (see BBATopSynth.elaborate, status_panel block).
|
||||
Resource("ledr", 0, Pins("11", dir="o"), Attrs(IO_STANDARD="SB_LVCMOS")),
|
||||
Resource("ledg", 0, Pins("37", dir="o"), Attrs(IO_STANDARD="SB_LVCMOS")),
|
||||
Resource("btn", 0, Pins("10", dir="i"), Attrs(IO_STANDARD="SB_LVCMOS")),
|
||||
# Bring-up status panel: D6/D7 discrete LEDs (active-high on this
|
||||
# board). RGB (pins 39/40/41) is driven via SB_RGBA_DRV — not
|
||||
# declared here as a platform resource (see BBATopSynth.elaborate).
|
||||
# No onboard button on this board (see note above).
|
||||
Resource("ledr", 0, Pins("48", dir="o"), Attrs(IO_STANDARD="SB_LVCMOS")),
|
||||
Resource("ledg", 0, Pins("47", dir="o"), Attrs(IO_STANDARD="SB_LVCMOS")),
|
||||
|
||||
# UART debug console → iCEbreaker FT2232H Channel B (onboard USB-UART).
|
||||
# No external hardware needed — the FT2232H is already on the board.
|
||||
# UART debug console → FT2232H Channel B.
|
||||
# On the PC: open the second USB serial port at 115200 8N1.
|
||||
# pin 9 = FPGA TX → FT2232H Channel B RX (FPGA drives)
|
||||
# pin 6 = FPGA RX ← FT2232H Channel B TX (FPGA reads)
|
||||
Resource("uart", 0,
|
||||
Subsignal("tx", Pins("9", dir="o")),
|
||||
Subsignal("rx", Pins("6", dir="i")),
|
||||
Subsignal("tx", Pins("19", dir="o")),
|
||||
Subsignal("rx", Pins("18", dir="i")),
|
||||
Attrs(IO_STANDARD="SB_LVCMOS")),
|
||||
]
|
||||
|
||||
@@ -124,23 +143,23 @@ class BBATopSynth(BBATop):
|
||||
w5100.rst_n.o .eq(self.w5100_rst_n),
|
||||
]
|
||||
|
||||
# ── Bring-up status panel → onboard LEDs / button ──────────────
|
||||
# ── Bring-up status panel → onboard LEDs ────────────────────────
|
||||
# All 5 panel LEDs mapped:
|
||||
# LEDG (pin 37) = led[0] heartbeat
|
||||
# LEDR (pin 11) = led[1] EXI activity
|
||||
# LEDG (pin 47) = led[0] heartbeat
|
||||
# LEDR (pin 48) = led[1] EXI activity
|
||||
# RGB (pins 39/40/41) = led[2] rx / led[3] tx / led[4] ready
|
||||
# The one onboard button → panel btn[1] (manual re-init).
|
||||
# No physical button on this board — panel_btn tied idle/released.
|
||||
if self._status_panel:
|
||||
ledr = platform.request("ledr", 0)
|
||||
ledg = platform.request("ledg", 0)
|
||||
btn = platform.request("btn", 0)
|
||||
led = self.panel_led
|
||||
|
||||
m.d.comb += [
|
||||
ledg.o.eq(~led[0]), # heartbeat (active-low LED)
|
||||
ledr.o.eq(~led[1]), # EXI activity (active-low LED)
|
||||
# btn[0]/[2] held released (active-low idle = 1)
|
||||
self.panel_btn.eq(Cat(C(1, 1), btn.i, C(1, 1))),
|
||||
ledg.o.eq(led[0]), # heartbeat (active-high LED)
|
||||
ledr.o.eq(led[1]), # EXI activity (active-high LED)
|
||||
# all 3 bits idle/released (active-low idle = 1) — no
|
||||
# physical button exists on this board to read
|
||||
self.panel_btn.eq(C(0b111, 3)),
|
||||
]
|
||||
|
||||
# iCEbreaker RGB LED has no series resistors — must use
|
||||
@@ -205,26 +224,39 @@ if __name__ == "__main__":
|
||||
# versions; treat as non-fatal timing failure.
|
||||
print(f" [seed {seed}] build exception (timing?): {exc}")
|
||||
|
||||
# Parse fmax from nextpnr log in build/top.tim (if present)
|
||||
# Parse fmax from nextpnr log in build/top.tim (if present). Domain
|
||||
# names are 'clk' (exi/sync, 24 MHz target) and 'capture_clk'
|
||||
# (54 MHz target, the tighter constraint) — NOT 'exi', which never
|
||||
# matched any real log line (this regex silently reported 0.0 MHz
|
||||
# for every seed until fixed).
|
||||
import glob, re
|
||||
tim_files = glob.glob("build/top.tim") + glob.glob("build/*.tim")
|
||||
fmax_exi = 0.0
|
||||
fmax_clk = 0.0
|
||||
fmax_capture = 0.0
|
||||
for tf in tim_files:
|
||||
try:
|
||||
with open(tf) as f:
|
||||
for line in f:
|
||||
m_ = re.search(
|
||||
r"Max frequency.*exi.*?:\s*([\d.]+)\s*MHz", line)
|
||||
r"Max frequency for clock\s+'(\w+)':\s*([\d.]+)\s*MHz", line)
|
||||
if m_:
|
||||
fmax_exi = float(m_.group(1))
|
||||
domain, freq = m_.group(1), float(m_.group(2))
|
||||
if domain == "clk":
|
||||
fmax_clk = freq
|
||||
elif domain == "capture_clk":
|
||||
fmax_capture = freq
|
||||
except OSError:
|
||||
pass
|
||||
print(f" [seed {seed}] exi fmax extracted: {fmax_exi:.1f} MHz")
|
||||
if fmax_exi > best_fmax:
|
||||
best_fmax = fmax_exi
|
||||
print(f" [seed {seed}] clk fmax: {fmax_clk:.2f} MHz (target 24) "
|
||||
f"capture_clk fmax: {fmax_capture:.2f} MHz (target 54.02)")
|
||||
# capture_clk is the binding constraint (tighter target, historically
|
||||
# the one that swings ±20% with seed) — rank seeds by it.
|
||||
if fmax_capture > best_fmax:
|
||||
best_fmax = fmax_capture
|
||||
best_seed = seed
|
||||
|
||||
print(f"\nBest seed: {best_seed} exi fmax: {best_fmax:.1f} MHz")
|
||||
print(f"\nBest seed: {best_seed} capture_clk fmax: {best_fmax:.2f} MHz "
|
||||
f"(target 54.02) — {'PASS' if best_fmax >= 54.02 else 'FAIL'}")
|
||||
|
||||
if do_flash:
|
||||
print(f"\nFlashing with seed {best_seed}...")
|
||||
|
||||
Reference in New Issue
Block a user