3.0 KiB
RGB LED Status Panel
Background
The StatusPanel module (exi_bba/status_panel.py) exposes 5 logical LED outputs:
| Index | Signal | Meaning |
|---|---|---|
led[0] |
heartbeat | ~1.4 Hz blink — clock alive, bitstream loaded |
led[1] |
exi_active | EXI transaction in progress (GC is talking) |
led[2] |
rx_act | Ethernet frame received |
led[3] |
tx_act | Ethernet frame transmitted |
led[4] |
ready | Ethernet initialisation complete |
The iCEbreaker board has two discrete LEDs (LEDR pin 11, LEDG pin 37) and one RGB LED (pins 39/40/41). Together they cover all 5 signals with no overlap.
Pin Mapping
| Physical LED | iCE40 pin | Signal | Colour |
|---|---|---|---|
| LEDG (discrete green) | 37 | led[0] heartbeat |
green |
| LEDR (discrete red) | 11 | led[1] EXI activity |
red |
| RGB element 0 | 41 | led[2] rx activity |
red |
| RGB element 1 | 40 | led[3] tx activity |
green |
| RGB element 2 | 39 | led[4] ready |
blue |
The RGB colour-to-element mapping (RGB0/RGB1/RGB2 → R/G/B) should be verified against the iCEbreaker schematic at hardware bring-up. It is a one-line change in synth.py if elements are swapped.
Implementation
The iCEbreaker RGB LED has no series current-limiting resistors on the board. It relies on the iCE40UP5K's on-chip SB_RGBA_DRV primitive, which is a dedicated open-drain current-source driver with built-in PWM support. The RGB pins (39/40/41) cannot be driven safely as regular LVCMOS outputs for this reason.
SB_RGBA_DRV outputs connect directly to the physical RGB pad locations — nextpnr places the primitive automatically without requiring PCF pin constraints. The platform resource system (platform.request()) is therefore not used for the RGB pins; only the two discrete LEDs are declared as platform resources.
The instance is added in exi_bba/synth.py inside the self._status_panel block of BBATopSynth.elaborate():
m.submodules.rgb_drv = Instance("SB_RGBA_DRV",
p_CURRENT_MODE="0b1", # half-current mode
p_RGB0_CURRENT="0b000001", # ~4 mA red
p_RGB1_CURRENT="0b000001", # ~4 mA green
p_RGB2_CURRENT="0b000001", # ~4 mA blue
i_CURREN=Const(1, 1),
i_RGBLEDEN=Const(1, 1),
i_RGB0PWM=led[2], # rx activity
i_RGB1PWM=led[3], # tx activity
i_RGB2PWM=led[4], # ready
o_RGB0=Signal(name="rgb_r"),
o_RGB1=Signal(name="rgb_g"),
o_RGB2=Signal(name="rgb_b"),
)
The output signals (rgb_r/g/b) are unconnected in the Amaranth netlist. yosys preserves SB_RGBA_DRV as a known iCE40 hardware primitive regardless, and nextpnr routes its outputs to the physical RGB pads.
Current is set to the lowest available setting (4 mA half-current) — visible as a status indicator without being distracting.
No Changes Required in StatusPanel
The StatusPanel module already outputs all 5 signals on self.led[0:5]. Only synth.py changed — adding the SB_RGBA_DRV instance and updating the comment block.