# 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()`: ```python 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.