Timers, PWM and the RGB LED
The little computer of part 2 talks to the world through a handful of devices: eight LEDs, a button, four digits, a timer and a tone. This part gives it more, one page at a time, each a small piece of gateware sitting on the bus, with a few registers the CPU reads and writes. By the end it reads buttons and a knob, talks over a serial line and MIDI, drives a screen and plays music through a sound chip of its own, all on the UPduino’s FPGA.
This first page is about how a device joins the bus at all, and about the first new one: a light whose brightness the CPU can set, by switching it on and off very fast.
1 · A device is a few registers on the bus
Section titled “1 · A device is a few registers on the bus”From the CPU’s side, a device is nothing but addresses. Writing 8000 lights the LEDs; reading $8001 says whether the button is held. The CPU does exactly what it does for memory, and something on the bus decides that this address is a device’s.
That something is address decoding, as on Memory and buses: a little logic that looks at the address and says which part of the computer it belongs to. The memory map gives the I/O space, $8000–$BFFF, to devices. Part 2’s devices take the first sixteen addresses; this part’s go above them, each with a block of its own:
| Addresses | Device | Page |
|---|---|---|
| $8000–$800F | part 2’s: LEDs, button, digits, timer 1, tone | Memory and buses, Jumps, the stack and interrupts |
| $8010–$8012 | the RGB LED | this one |
| $8020–$8023 | buttons and a knob | Buttons and knobs |
| $8030–$8034 | a serial port | A serial port |
| $8040–$8044 | MIDI | MIDI |
| $8050–$8053 | SPI | SPI |
| $8060–$8062 | I²C | I²C |
| $8070–$8072 | the one-bit DAC (the audio out) | A one-bit DAC |
| $8100–$8112, $9000–$A3FF | video | Video out |
| $8400–$841C | the sound chip | A sound chip |
In the gateware, machine.v (the computer of part 2) decodes its own sixteen addresses and passes everything
else in the I/O space out on an expansion bus: the address, the byte being written, whether it’s a
write, and the clock enable (the CPU takes a step on one clock in twelve, so from the board’s 12 MHz it runs
at 1 MHz, and everything on the bus acts on that same clock). board.v decodes each device’s block from
there:
wire s_rgb = ext && addr[15:4] == 12'h801; // $8010–$801F: the RGB LEDwire s_btn = ext && addr[15:4] == 12'h802; // $8020–$802F: buttons and the knobEach device receives its decoded line as an input called sel (short for “selected”), which is high while the
CPU is talking to it. The low bits of the address bus are passed to the device too, and say which of its own
registers is being addressed. A device needs three things:
- Registers the CPU writes: flip-flops that take the data bus’s byte on a clock where
ce,seland the write signal are all high. - A way to be read: a multiplexer that puts the chosen register’s byte out. The bus collects every
device’s byte and passes on the one whose
selis high. (On Memory and buses that was tri-state outputs on shared wires. Inside an FPGA there are no tri-state wires, so it’s a multiplexer: the same idea, the choosing done by logic instead.) - Its job, whatever it does with those registers, all the time, whether the CPU is looking or not.
Here’s the RGB LED’s gateware, exactly as it goes on the board. Its three registers are the brightness of red, green and blue; its job is pulse-width modulation, the subject of the next section.
rgb.v, simulated gate by gate. To write a register, as the CPU would: set a to 0 (red), d to a small
level such as 3, sel, we and ce to 1, and click Clock; then set sel, we and ce back to 0 and
keep clocking. red is on while count is below the level, so for the first 3 counts of every 256. And
q is always what a read of register a would give back.
2 · Pulse-width modulation
Section titled “2 · Pulse-width modulation”An FPGA pin is either high or low: there’s nothing in between. So how does a digital circuit dim a light?
It switches it fully on and fully off, so fast that the eye can’t follow, and varies how long it’s on. A counter runs round from 0 to 255; the output is on while the counter is below the level, and off from there to the end. The level is how many of every 256 clocks the light is on. That’s pulse-width modulation (PWM): the information is in the width of the pulses.
The counter (top) goes round 0 to 255 over and over, each step a clock of 12 MHz; the output (bottom) is on while it’s below the level. Move the level and the pulses widen and narrow; the dashed line is the average, the share of the time the output is on. The square at the bottom right is that average, as the eye would see it. A period is 256 clocks, 21.33 µs: 46,875 of them a second.
At 46,875 periods a second, an eye sees only the average. So does almost anything slow: a motor turns at a speed set by the average voltage; a speaker cone can’t follow 47 kHz and moves with the average; and the beeper of part 2 had a DUTY register for the same reason. To get the average out as a real voltage, put the output through a resistor and a capacitor (a low-pass filter): that’s a crude digital-to-analogue converter, and a page of its own later, A one-bit DAC.
3 · Even steps to the eye
Section titled “3 · Even steps to the eye”Here’s a catch. The PWM’s level sets an amount of light: level 128 is half the light of level 255. But the eye doesn’t see amounts of light evenly. It’s far more sensitive to differences between dim lights than between bright ones: going from 1% to 2% of the light looks like a big step; from 90% to 91%, nothing at all.
Top: eight steps even in the PWM’s level (0, 36, 73 … 255): the steps look bunched up at the bright end, and the dim end jumps. Bottom: eight steps even to the eye, each level 255 × (n / 7)^2.2, rounded: 0, 4, 16, 40, 74, 122, 182 and 255. Each swatch is drawn as the light that level would give, so these are what the LED would look like.
So to fade a light smoothly, the levels have to grow slowly at first and quickly later. A good match to the
eye is the level going as the step to the power 2.2, a curve called gamma. (Screens use the same curve,
for the same eye: a pixel’s number isn’t the amount of light either. That’s why the swatches above are drawn
by converting each level to a screen number first, n^(1/2.2).) A program does it with a table: 32 steps,
worked out once, read with LDA table,X.
4 · The RGB LED
Section titled “4 · The RGB LED”The UPduino has an RGB LED: three LEDs, red, green and blue, in one package, so close together that from a step back they’re a single light whose colour is their mixture. Three PWM outputs, one each, give it any colour: the RGB LED device above, three counters’ worth of comparing and one shared counter.
The UP5K drives that LED in a way most FPGAs can’t: three of its pins have a constant-current LED driver
behind them, which sinks a set current through each LED when its PWM signal is on. So the LED needs no
resistors, and its brightness doesn’t change with the supply voltage. board.v connects the three PWM
outputs to it.
The computer with the RGB LED at $8010–$8012. Mix a colour writes three levels and stops: change them
and run again. Fade up and down fades all three together through a table of 32 gamma steps, one every 20
ms, timed by polling timer 1; change gamma,X to linear,X to see steps even in the duty instead. Colour
wheel moves round the colours on timer interrupts, 100 a second: each colour follows the same wave, a third
of the way round from the others. The LED is drawn the way the levels’ light would look.
Notice what the CPU does in the colour wheel: almost nothing. Between interrupts it sits in a loop that jumps to itself, and each interrupt is a few dozen instructions. The PWM, 46,875 periods a second for each colour, is all in the gateware.
5 · Timers
Section titled “5 · Timers”The colour wheel and the fade both use timer 1, from Jumps, the stack and interrupts.
A timer is the simplest peripheral of all: a counter that counts clock cycles down from a period the CPU sets,
and when it runs out sets a flag, starts again and, if asked, interrupts. In machine.v it’s this (the
register writes left out):
wire t1_out = t1_run && t1_period != 16'd0 && t1_count <= 16'd1; // runs out this cyclealways @(posedge clk) if (ce) begin if (t1_out) // start again from the period t1_count <= t1_count + t1_period - 16'd1; else if (t1_run && t1_period != 16'd0) t1_count <= t1_count - 16'd1; if (t1_out) t1_flag <= 1'b1; // seen by reading T1STAT, or as an interruptendA timer is how a program knows how long anything took, or makes anything happen on time. Without one, it counts its own instructions in a delay loop, as part 2’s first programs did, and the count goes wrong the moment the loop changes or an interrupt arrives. A timer counts the clock itself, whatever the CPU is doing.
And it’s worth seeing what having PWM in gateware saves. Here’s PWM done by the CPU instead, with timer 1 interrupting every 100 cycles, the handler counting round 16 steps and switching LED 0:
PWM in software: LED 0 on a quarter of the time, switched by a timer interrupt every 100 cycles. The main program does nothing but count its loops (at $0002–$0003, in the memory view). Measured on this emulator, it gets 51% of the CPU’s time: the other 49% goes on the interrupts. And this PWM has only 16 steps, at 625 periods a second, slow enough to flicker.
Half the CPU, for one light, done badly. The gateware’s PWM does three, with 256 steps each at 46,875 periods a second, and costs the CPU nothing: 89 logic cells of the FPGA. That’s the case for peripherals in a nutshell: anything that has to happen often and on time is cheaper as a little circuit than as a program.
Summary
Section titled “Summary”- A device is registers at addresses: flip-flops the CPU writes, a multiplexer for reading them back, and the job it does with them. Address decoding gives each device its block of the I/O space.
- PWM dims a light (or slows a motor, or makes a voltage) by switching it fully on and off, quickly, for a share of the time: a counter compared with a level.
- The eye sees brightness unevenly, so even-looking steps need levels that grow as a power: gamma.
- The UP5K’s RGB LED has its own current drivers; three PWMs mix any colour.
- A timer counts clock cycles, so a program can do things on time without counting instructions; and work that has to happen often and on time is cheaper in gateware than in software.
Further reading
Section titled “Further reading”- Charles Poynton, Digital Video and HD: Algorithms and Interfaces: the chapter on gamma, the clearest account of why light and numbers differ.
- Lattice Semiconductor’s iCE40 LED driver usage guide: the UP5K’s LED driver and its current settings.