Extending the ZPUino

In previous posts, I've
introduced the ZPUino softcore processor
and shown how to build the base instance for an FPGA.
But the real advantage of the ZPUino is being able to extend it by attaching new peripherals
to the core processor in the VHDL description.
The best way to learn something is by doing it, so in this post I'll show how to integrate a
simple RGB LED driver peripheral into the ZPUino.

The ZPUino architecture incorporates 16 slots that can be populated with peripherals.
A particular slot is activated when the ZPUino core processor accesses an I/O address within the
range of addresses assigned to that slot.
Then the peripheral in that slot can pass data back-and-forth with the processor through a
Wishbone interface
that consists of parallel address and data buses along with control signals and
a handshake synchronization protocol.

All the signals are part of the Wishbone interface except for id which outputs a peripheral-specific code
that the ZPUino processor can use to associate a peripheral performing a specific function
(e.g., a UART) with software drivers for that function.

I constructed the RGB peripheral according to the following block diagram:

The Wishbone interface allows the processor to read or write a single 32-bit register at address 0.
Each of the lower three bytes of this register controls the duty-cycle of a
pulse-width modulator (PWM).
Varying the duty cycles of the PWMs will change the intensities of the red, green and blue components
which changes the overall color of the RGB LED.
For the excruciating operational details, check out the VHDL for the PWM
and the RGB peripheral.

Next, I instantiated the RGB peripheral in the top-level ZPUino module as follows:

The led_r, led_g, and led_b output signals have to be attached to pins of the ZPUino
in order to connect to the physical RGB LED. I could have made these pin assignments
statically, but the ZPUino provides a much more flexible solution with its
Peripheral Pin Select (PPS) mechanism. The PPS is basically a collection of
multiplexers that allow any one of N outputs to be attached to any one of M
pins under software control. (There is an equivalent PPS block for redirecting the inputs.)
So with the PPS, I can assign the ZPUino pins that output the RGB PWM signals at run-time.

Within the top-level ZPUino VHDL module, I attached the RGB peripheral outputs to
the output PPS as follows:

In order to make room in the PPS for the new outputs, I bumped-up the PPSCOUNT_OUT constant in the
zpuino_config.vhd
file:

-- Set this to the max. number of output pps on the system
constant PPSCOUNT_OUT: integer := 10; -- Increased to handle RGB LED drivers.

After making these changes, I rebuilt the ZPUino FPGA bitstream with the newly-added RGB peripheral.
Now it's just a matter of writing the software for accessing the new peripheral.
I'll demonstrate this by writing an
example program
that ramps-up and then fades an RGB LED over a sequence of colors.

The program starts (as most do) with a set of definitions.
This is where I tell the program about the decisions I made in the VHDL code:
the location of the RGB peripheral (slot 9), the address offset of the color register (0),
and the position of the red, green and blue outputs going into the PPS block (7, 8, and 9).
I also specify the ZPUino pins that the RGB LED will be connected to (5, 4 and 3).

Next, I use these definitions in the setup() routine.
The ZPUino pins driving the RGB LED are enabled as outputs,
and these outputs are driven by the multiplexers from the PPS.
Then each multiplexer is programmed to output one of the PWM signals
from the RGB peripheral.

Then there's the little set_rgb_color() subroutine that accepts a 24-bit color value
and writes it to the color register within the RGB peripheral.
The IO_SLOT(RGB_SLOT) macro-call generates the base address for the RGB peripheral.
Then the REGISTER(...) macro-call adds the offset of the color register
to the base address to generate the final address where the new color value is written.

The meat of the example program is contained in the brighten_then_fade() subroutine that ramps the RGB LED
up to its maximum color intensity and then down to zero.
It accepts a color_increment argument that contains either a 0 or a 1
in each of its eight-bit fields.
For example, the value 0x000101 would change both the red and green
fields while leaving the blue component alone.
Starting from zero, this argument is added to the RGB color until it reaches
its maximum intensity (0x00FFFF for the example value of the color_increment).
Then the argument is subtracted from the color until it again reaches zero.

Comments

Robert Pflaum
3 years, 10 months ago

Dave,

I have been following this series on ZPUINO 2.0 . However the source code I have does not appear to be the same as you describe. I do not see the ID output in the instantiation. I used the link you provided earlier in this series to download the source. It appears to be the source for SOURCE 1.0 not 2.0. What have I done wrong?

If you enter anything in this field your comment will be treated as spam

Dave Vandenbout
3 years, 10 months ago

Hi, Robert. I got bit by this same problem. Before you download the source, make sure to select the correct branch in the drop-down list in the upper left of the webpage. For the RGB LED example, that would be "RGB_LED_Example". If you want one of the previous implementations, then the "zpuino2_xula2_usb_uart" would also contain the ZPUino 2.0 code.