The CAT Board is part of a Raspberry Pi-based hand-held FPGA programming system.
To make the experience fit your profile, pick a username and tell us what interests you.
We found and based on your interests.
Over the past few weeks, I've done two things related to his project:
When you're dealing with FPGAs having several hundred or more pins, most of which are interchangeable, it can be perplexing when trying to choose the correct pin to connect to some other peripheral. You want to choose one that's close to where it will be connected, but you also need to make sure it's the right type (don't connect an FPGA VCC pin to an HDMI connector data pin), and it's in the right I/O bank. I wrote the PadPainter plugin for KiCad to highlight pads of ICs that meet a set of conditions such as the electrical function (input, output, bidirectional, etc.) and the bank (or unit) to which its assigned. This tool quickly shows me the pins of the FPGA which are candidates for connection to other peripherals. If you're dealing with large FPGAs or SOCs, this tool may also be useful to you. You can read more about it here.
I decided to replace the SATA connectors on the original CAT Board with an HDMI port. Because of its location, the normal right-angle HDMI connector would be blocked by the USB ports of the Raspberry Pi. So I decided to use a vertical connector.
Finding vertical HDMI connectors wasn't easy. Digikey and Mouser didn't have any. I finally found some on Aliexpress, but I had to piece together the footprint dimensions from several sources. Obviously, I couldn't really trust my footprint so I built a small prototype board using several hole sizes and trace/space widths:
The PCB showed my footprint would work with the vertical HDMI connector:
Even so, I decided not to pursue this any further. The choice of suppliers for the connector was too limited and sketchy, and the assembly process looked like bent pins would be a problem.
Instead, I'll use the more common right-angle HDMI connector and bring it off the same side of the PCB that's used by the Raspberry Pi GPIO connector. To make room, I'll need to replace the through-hole GPIO socket with an SMT version.
In my last log, I had the SKiDL-generated netlist imported into an RPi HAT template. The problem with that is PCBNEW initially places all the parts pretty much any way it wants. That means you have to pick through a pile of parts to find which ones go with which. As an example, I highlighted (in white) the SDRAM chip and its bypass caps:
My solution for this problem was two-fold:
With those two items in place, the relationships between the parts becomes much clearer. Here's the placement of the SDRAM and its bypass caps now:
After that, I was able to create an initial, rough placement for the CAT Board:
I still have to design the +3.3V and +1.2V voltage regulator circuits. After those are done, I can begin the detailed placing and routing.
It's been a few weeks since my last log post. Since then, I've done the following:
With regard to the last item on the list, here are the components and the HAT PCB boundary:
And here it is with the air-wires:
So I've got my crap. I've got my bag. Now it's a question of whether I can get all my crap in my bag.
The current state of the CAT Board is stored in the skidlized branch of the CAT Board repository.
The first step in this resurrection was to convert the schematic-based CAT Board design into a SKiDL program. That was actually pretty easy. First, I generated a netlist from within EESCHEMA. Then I ran the following program to convert it into a SKiDL program:
netlist_to_skidl -i cat.net -o cat-flat.py
While the conversion was easy, it's not exactly what I want. The resulting code is flat: just a list of component instantiations and nets to interconnect them without any hierarchical structure. Making changes to that is no easier than hacking on the actual netlist file.
The actual hierarchy for the CAT Board is shown below. It isn't a deep tree: just a bunch of peripherals attached to a central FPGA. But my goal is to make a parameterized SKiDL module for each peripheral. Then I can build not only the CAT Board by interconnecting those modules, but reuse them in other designs as part of a circuit library. A simple example would be an LED module that instantiates multiple LEDs and current-limiting resistors based upon the width of the bus entering the module.
So why did I bother making the flat version of the SKiDL code? Some of the motivation was to show the use of the netlist_to_skidl utility since it's an easy way to generate SKiDL for those used to working with schematics. In addition, the flat code also includes the detailed templates of the components used in the CAT Board. I can reuse those in my parameterized version.
My last log post for this project was over 1-1/2 years ago, but I'm returning to it now with the following objectives:
Rebuilding the CAT Board using SKiDL will guide me in adding features to make the language more expressive and concise. The remaining changes will improve the capabilities of the board while possibly compacting the circuitry enough so I can get all the components on a single side of the PCB.
Also, I took advantage of a StickerMule special to get 50 vinyl stickers for just $9. It'd be a shame to have these and no new board to go with 'em.
I learned about a new FOSS project a few weeks ago: apio. It's stated mission is:
Experimental open source micro-ecosystem for open FPGAs. Based on platformio. Apio is a multiplatform toolbox, with static pre-built packages, project configuration tools and easy commands to verify, synthesize, simulate and upload your verilog designs.
That sounded pretty good to me. It was easy to install on my RPi3:
pip install apioThat just installs the apio "shell". In order to get all the synthesis, simulation, and board tools, I used the command:
apio install --all
That automatically downloads and installs some system utilities, the Icestorm FPGA toolchain, the iverilog simulator, and some design examples for several types of iCE40 FPGA boards. Once that was completed, I could check to see what boards were supported:
pi@raspberrypi:~ $ apio boards --list
Supported boards:
--------------------------------------------------------------------------------
Board FPGA Type Size Pack
--------------------------------------------------------------------------------
icoboard iCE40-HX8K-CT256 hx 8k ct256
icezum iCE40-HX1K-TQ144 hx 1k tq144
icestick iCE40-HX1K-TQ144 hx 1k tq144
go-board iCE40-HX1K-VQ100 hx 1k vq100
iCE40-HX8K iCE40-HX8K-CT256 hx 8k ct256
Use `apio init --board <boardname>` for creating a new apio proyect for that board
What!?!? No CAT Board in the list!?! I needed to fix that situation. I poked around in the apio Python code and found a likely file at apio/resources/boards.json. Inside that file, I pretty much copied the existing entry for the icoboard with a few modifications: "Cat-board": {
"fpga": "iCE40-HX8K-CT256",
"prog": "litterbox",
"check": {
"arch": "linux_armv7l"
}
}
The only thing I changed (besides the board identifier) was the "prog" entry to list the litterbox utility that uploads bitstreams from the RPi3 to the FPGA on the CAT Board.I also had to modify the following section of the SConstruct file in the same directory:
# -- Upload the bitstream into FPGA
upload_cmd = ''
if PROG == 'ftdi':
upload_cmd = 'iceprog{0} -d i:0x0403:0x6010:{1} $SOURCE'.format(
EXT, DEVICE)
elif PROG == 'gpio':
# Icoboard + RPI2: sram
upload_cmd = 'export WIRINGPI_GPIOMEM=1; icoprog -p < $SOURCE'
elif PROG == 'litterbox':
# Cat Board + RPI2,3
upload_cmd = 'sudo litterbox -c $SOURCE'
This just adds the command for uploading a bitstream using the litterbox utility.
Finally, after a little more poking around, I found the apio/managers/scons.py file and added the following code to its upload function:
# -- Litterbox
elif programmer == 'litterbox':
# Cat Board + RPI2,3
# Device argument is ignored
if device and device != -1:
click.secho(
'Info: ignore device argument {0}'.format(device),
fg='yellow')
# Check architecture
arch = self.resources.boards[board]['check']['arch']
current_arch = util.get_systype()
if arch != current_arch:
# Incorrect architecture
click.secho(
'Error: incorrect architecture: RPI2 or RPI3 required',
fg='red')
return 1
This does a sanity check to make sure the upload process for the CAT Board is occurring on an RPi2 or RPi3.
Now when I regenerate the board list, I can see the CAT Board:
pi@raspberrypi:~ $ apio boards --list
Supported boards:
--------------------------------------------------------------------------------
Board FPGA Type Size Pack
--------------------------------------------------------------------------------
icoboard iCE40-HX8K-CT256 hx 8k ct256
icezum iCE40-HX1K-TQ144 hx 1k tq144
Cat-board iCE40-HX8K-CT256 hx 8k ct256
icestick iCE40-HX1K-TQ144 hx 1k tq144
go-board iCE40-HX1K-VQ100 hx 1k vq100
iCE40-HX8K iCE40-HX8K-CT256 hx 8k ct256
Use `apio init --board <boardname>` for creating a new apio proyect for that board
Once my modifications were complete, I went into the directory for the LED blinker and used the following command to indicate that this design was intended for the CAT Board:
apio init --board Cat-board
This creates an apio.ini file with a single line of text indicating the board...
Read more »I've been away from this project for a few months (OK, four months) building things like a new tool for designing electronics. One of the things I haven't discussed here is the time it takes to download a bitstream to the FPGA on the CAT Board.
As shown in previous logs, the FPGA is configured through one of the hardware SPI ports of the RPi. I've never considered SPI a very fast way of transferring data, so I initially set the port bit rate at 1 Mbps. That was good enough to get the FPGA going within a couple of seconds and there was no reason to push it and possibly cause errors while I debugged the board.
But once the board was working reliably, I revisited the SPI bit-rate setting. I figured there was no harm in upping it to 5 Mbps just to see what happens. I went into the litterbox.py script and changed it to:
self.spi.speed = 5000000
Then I ran the command to load the FPGA with the bitstream for the LED blinker:sudo litterbox -c blinky.bin
The download to the FPGA completed more quickly than before and the LED started blinking. Success!
Then I started pushing for more: 10 Mbps, 20 Mbps, 50 Mbps, no problem; 100 Mbps, 150 Mbps, still five-by-five; 200 Mbps, complete and utter failure.
OK, I hadn't expected to get even close to 200 Mbps. With a little trial and error, I finally found the maximum speed I could use was 199,999,999 bps. The reason for that becomes clear later.
Now, was I actually transferring bits at 200 Mbps, or was the software making a promise that the hardware couldn't keep? To test that, I wrote some code to time the transmission of a 10 MByte payload and compute the effective bit-rate while I also observed the maximum SPI clock frequency and duty cycle with an oscilloscope:
spi.speed (Mbps) | Actual Speed (Mbps) | Fmax (MHz) | Duty Cycle (%) |
4 | 1.8 | 2.5 | --- |
10 | 3.3 | 6.25 | 54.0 |
25 | 8.8 | 25 | 36.1 |
50 | 10.7 | 50 | 21.7 |
67 | 11.3 | 67 | 17.9 |
100 | 12.0 | 100 | 12.1 |
150 | 12.0 | 100 | 12.1 |
200 | 12.8 | 200 | 6.4 |
As can be seen, the actual transmission speeds are quite a bit lower than the speed setting. The reason for that is the overhead in the python-spi module that copies and converts the individual 4096-byte packets of the payload before sending them to the SPI driver. Even though each packet gets transmitted at a high clock speed, there's a significant "dead time" (2.3 ms) while the software readies the next packet. As the raw speed increases, the packet transmission time decreases and the dead time (which stays constant) consumes a larger percentage of the time to send the full payload. That's why the duty cycle decreases as the speed setting increases.
To decrease the overhead, I modified the python-spi code as follows:
After these two changes, setting spi.speed to 100 Mbps resulted in an actual transmission speed of 65 Mbps (an increase of 540%).
There's no reason to set the spi.speed to a value greater than 100 Mbps. The table indicates the RPi is generating the SPI clock by dividing a master 200 MHz clock by an integer. Any setting between 100 and 199 Mbps will result in an SPI clock of 100 MHz, and going to 200 Mbps has already proven too fast for sending an FPGA configuration bitstream. (The iCE40HX datasheet also shows the SPI clock in slave mode should not exceed 25 MHz, so getting to 100 MHz is really pushing it already.)
A transfer rate of 65 Mbps opens up some interesting possibilities. That means there is an 8 MByte/second channel between the CAT Board FPGA and the RPi that uses only a few pins of the GPIO connector. I have some Xilinx-centric VHDL modules and a Python library that provide a printf-like debug interface for FPGA designs through the JTAG port. I can modify these to use the SPI port so the CAT Board + RPi will have the same capabilities. I'll be working on that next. I think. Maybe.
In my previous post, I showed how to blink an LED on the CAT Board using Verilog. Now I'll do the same thing using MyHDL, a hardware description language based on Python.
I'll assume a starting point of a Raspberry Pi running the Raspbian OS with the yosys, arachne-pnr and icestorm FPGA tools installed. If you're following along and haven't already got that, read the previous post. The stuff I describe here won't work without it.
Before you can use MyHDL, you have to install it. Installing the latest release is as simple as:
sudo pip install myhdl
But I like to use the development version because that's where all the new features are. That's installed like this:
cd /opt
sudo git clone https://github.com/jandecaluwe/myhdl.git
cd myhdl
sudo python setup.py install
Blinking an LED with Verilog wasn't hard, and doing it in MyHDL isn't either. Here's the source code that's stored in a Python file called blinky.py.
from myhdl import *
# Define the Blinky module.
@block
def blinky(clk_i, led_o):
cnt = Signal(intbv(0, 0, 50000000)) # Counter from 0 to 49999999.
tgl = Signal(bool(0)) # Toggle flag drives the LED.
# Sequential block triggered on every rising edge of the clock.
@always_seq(clk_i.posedge, reset=None)
def toggle_led():
if cnt == cnt.max-1: # When the counter reaches its max value...
tgl.next = ~tgl # Toggle the flag...
led_o.next = tgl # Output the flag to the LED...
cnt.next = 0 # Reset the counter.
else: # Counter hasn't reached max so just keep incrementing.
cnt.next = cnt + 1
# Return a reference to the Blinky logic.
return toggle_led
# Define the connections to Blinky.
clk_i = Signal(bool(0))
led_o = Signal(bool(0))
# Create an instantiation of Blinky.
top = blinky(clk_i, led_o)
# Output Verilog code for Blinky.
top.convert(hdl='Verilog')
The blinky.py file containing the MyHDL code shown above is executed as follows:
python blinky.py
This translates the MyHDL code into Verilog that's stored in the blinky.v file. Then this Verilog file can be compiled into a bitstream using the yosys, arachne-pnr and icepack tools just like in the previous post. Once the bitstream is downloaded to the CAT Board, the LED will blink.
Why bother doing this in MyHDL? It just adds another step, but does it add any value?
In this case, no, it doesn't. Because this design is so simple, the MyHDL code isn't any more compact or expressive than the original Verilog. The value of MyHDL is experienced when doing design exploration, i.e. trying various approaches to solving a problem. Then all the features of the Python ecosystem can be used with MyHDL to come up with a solution. I showed a few examples of this here and here.
A fundamental rite of passage is getting your embedded system to blink an LED. In this post, I'll show you how to take a Raspberry Pi 3 fresh out of the box and get an LED blinking on the CAT Board.
(If you're already experienced with the RPi, then a lot of this post will be redundant for you. However, I've found it useful to document processes so I can repeat them reliably at a later date when the details have grown fuzzy.)
The first thing to do is install an operating system (OS) on the RPi:
I'm going to use the RPi by connecting to it over a network SSH connection from my PC:
ip=169.254.68.2
You can do everything over the wired Ethernet, but I prefer the convenience of using my local wireless network.
network={ ssid="XESS" psk="whatever_my_wireless_password_is" }
Sharing files between my PC and the RPi lets me use a familiar editor to write source that can then be dropped onto the RPi.
$ sudo apt-get update
$ sudo apt-get install samba
$ sudo apt-get install samba-common-bin
[pi] path=/home/pi writeable = yes browseable = yes only guest = no create mask = 0777 directory mask = 0777 public = yesAlso, change the name of the workgroup to whatever is being used for the Windows PC:
workgroup = XESS
sudo service smbd start
Before the RPi can be used to program the CAT Board FPGA, a few of its configuration settings need to be adjusted:
sudo raspi-config
sudo raspi-config --expand-rootfs
There are five software...
Read more »One of the reasons for respinning the CAT Board PCB was to get the SPI flash chip connected correctly to the Lattice FPGA. The flash+FPGA+Raspberry Pi interconnection is complicated because it has to operate in three different modes:
All three modes have to share a single SPI bus between all the devices while using a minimum of RPi GPIO signals and extra circuitry.
Mode 3 is the easiest: when the FPGA powers up (or is reset), it checks to see if the SPI CS line is pulled high and, if so, becomes an SPI master and reads its configuration bitstream from the flash. The RPi GPIO signals are hidden behind the series resistors and can't interfere.
Mode 2 is slightly more difficult. When the RPi is storing a bitstream by sending it to the flash chip's SI input, the SPI CS line is pulled low. But that also enables the SPI interface of the FPGA which could lead to interference if the FPGA's SDO output becomes active. To prevent this, the RPi asserts the reset pin of the FPGA so its SPI port can't turn on. Problem solved.
Mode 1 is the most difficult. The RPi pulls the SPI CS line low as it removes the reset from the FPGA. This places the FPGA in slave mode so the RPi can send a bitstream to the FPGA's SPI port. But this also enables the flash chip's SPI port, which means the flash's SO output could interfere with the bitstream data. Unfortunately, there's no reset pin on the flash to keep it quiet. However, the flash does have a deep power-down mode that is entered by sending a specific command to the flash. Once in this state, it will not respond to anything until it receives another specific command to wake up. The RPi can then transfer the bitstream to the FPGA. During the transfer, there's no chance the wake-up command will be sent accidentally to the flash's SI input because 1) neither the RPi or FPGA will be driving that signal line during the configuration process, and 2) the flash only executes a command once its CS input goes high but the SPI CS line is held low for the entire duration of the bitstream transfer. So the flash will stay quiet and the RPi can send the bitstream to the FPGA in peace.
Another complication of the shared SPI bus is that the roles of the RPi's MOSI and MISO pins are reversed in modes 1 and 2. In mode 1, the RPI's MOSI output pin drives the SDI input of the FPGA and the FPGA's SDO output drives the RPI's MISO input. That allows the hardware SPI port of the RPi to be used for the SPI transactions. But in mode 2, the RPi's MOSI pin acts as an input to receive data from the flash's SO output and the RPI's MISO pin has to drive the SI input pin of the flash. That precludes the use of the RPI's SPI hardware and the SPI transfers to/from the flash have to be done using bit banging.
I searched for a ready-made SPI bit-banger program but nothing great popped up. So I just wrote one in Python using the RPi.GPIO library. It consists of a class for handling individual pin I/O, another class for SPI transactions, and a final class that handles most of the commands for the serial flash chip.
To test the code, I first tried the command to read the device ID from the flash. The manufacturer and device IDs should have been 0x1F and 0x8401, respectively. Instead, I got 0xFF and 0xFFFF. That's OK; nothing ever works the first time.
I probed with an oscilloscope to make sure the RPi was driving the correct pins of the flash. No problem there except that the SO pin was always high (naturally).
Next, I pulled out my old HP LogicDart and sampled the waveforms on the CS, SCK and SI pins. Once again, no problems:...
Read more »
Create an account to leave a comment. Already have an account? Log In.
No, I really don't have any plans for selling this. Lattice sells $22 iCE40 boards and Olimex has one for $25. Even though these use a small HX1K FPGA, they kind of set the price point of what people are expecting. Earlier this year, I calculated the part cost of the CAT Board with an HX8K at $17. Even if I could sell it at $50, I'd make almost nothing and still not many people would buy it.
It's open source, though, so feel free to build your own.
Hi Dave, very cool project. I have actually been working on similar project off and on for a while with a Xilinx FPGA. Glad to see that I am not the only one who had the idea of pairing the pi with FPGA. I haven't posted anything yet but I might in the future.
Just a quick note based on an earlier comment where you mention the SPI as being slow. I'm using this library (http://www.airspayce.com/mikem/bcm2835/index.html) to control the SPI interface. My very rough calculations put the transfer rate at about 3 Mbit/s when I'm loading my bit file, and it can probably be pushed a bit faster. I can share some code if you're interested.
Hi, Mike. Thanks for the reply. I actually sell the StickIt!-MB (http://www.xess.com/shop/product/stickit-mb-4_0/) that mates one of my XuLA Xilinx FPGA boards to an RPi. The only problem with that is I can't run the Xilinx programming tools on the RPi. Have you been able to do that?
In regards to the slow SPI, that's a problem only when I program the flash by bit-banging to arbitrary GPIO pins using Python. When I use the actual hardware SPI of the BCM2835 to program the FPGA directly, it's incredibly fast. I looked at the library you mentioned and it appears to only provide an interface to the hardware SPI port. Is the library also able to bit-bang to any set of GPIO pins?
Damn nice project! Funny name, just happen to have finished a etherCAT project :P (8 layer pcb from multi-circuit-boards.eu -> 5 boards 150 euro without stelcil ).
[this comment has been deleted]
Hi, Brosnan.
I used a hot air gun (actually, it's a paint remover) because that's what I have.
A few years ago, I made a video about how to solder BGAs. It's not that hard to do.
Hi, Sami. The CAT Board isn't available for sale at this time. I'm still considering whether that would be worthwhile.
The main limitation of the CAT Board is the slow interface to the RPi through the GPIO connector.
Hi,
I would like to get and test one of your CAT FPGA board for Raspberry Pi 2 B. How can I order one from you?
Have you found any limitations in your CAT board?
Best regards,
Sami
Become a member to follow this project and never miss any updates
Do you have an update on whether you will be selling these and pricepoint?