Files

67 lines
3.0 KiB
Markdown

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