67 lines
3.0 KiB
Markdown
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.
|