# 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.
