Files
rebbarb/docs/IceBreaker FPGA/rgb_status_panel.md
T

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.