-
1▇▇▇▇ Get Started ▇▇▇▇
Reader note: If you would like to view this in the center of the screen, click on the View Instructions tab all the way at the bottom.
In the previous bootcamps, you built a simple Verilog demonstration consisting of an adder and a few flip flop-based circuits. The simulations work, so now it is time to put the design into a real FPGA and see if it works in the real world. The FPGA board we’ll use is the Lattice iCEstick, an inexpensive ($25) board that fits into a USB socket.
Like most vendors, Lattice lets you download free tools that will work with the iCEstick. However, they are often painful to install and the Lattice FPGA is one of the few that has a complete Open Source toolchain for Verilog so we're going to use that. The tools have instructions for installing under Linux, Windows, and the Mac. I will use Linux, but the process is essentially the same regardless of operating system.
Here's our general game plan:
- Install the tools
- Assemble our Verilog file and a constraint file -- you can download these from the Files section or Github or you can keep using the Verilog file you've used in previous bootcamps
- Run 3 programs to produce a bitstream for configuring the FPGA board
- Run a program to download the bitstream to the board
If you have your board and you've installed the IceStorm tools, you are ready to go on to the next step.
-
2▇▇▇▇ Basic Workflow ▇▇▇▇
FPGAs are actually very similar to memory devices. Some are nonvolatile and some require something (like a configuration memory chip or a CPU) to load them every time. Unlike a memory chip, though, writing bits to it doesn't allow you to read the bits back out (well, at least that's not the primary purpose). Instead, the bits configure the connections and options that wire up the circuit you've designed in Verilog.
The processes of taking your Verilog and creating a file that can be sent to the FPGA or a configuration memory is a workflow. Just like a C compiler converts C code to some kind of executable format, an FPGA toolchain converts Verilog code into a configuration bitstream that can be sent to the device to make it behave a certain way.
Don't forget, the bitstream isn't an executable. It just defines how the internal blocks of the FPGA connect together and possibly sets options for some of the blocks.
All FPGA toolchains have basically the same general steps, although some may merge them or split them up differently:
- Synthesize – convert Verilog into a simplified logic circuit
- Map – Identify parts of the synthesized design and map them to the blocks inside the FPGA
- Place – Allocate specific blocks inside the FPGA for the design
- Route – Make the connections between blocks required to form the circuits
- Configure – Send the bitstream to either the FPGA or a configuration device
-
3▇▇▇▇ Add Constraints ▇▇▇▇
Assuming your toolchain knows about the specific FPGA you are using, you might think you only need to feed it Verilog (this is analogous to a C compiler that knows about your CPU only needs C source code). However, there is one piece we didn't have to worry about in simulation that now becomes very important.
In our demo code, we had things like LEDs and switches. But that doesn't really mean anything to the tools and they will just assign them at random. In our case that would be bad because we have a board set up a particular way. Even if we were building our own board, once you have it set up one way you want it to stay that way. You might also have some pins on an FPGA that have special properties and you want to force it to use one of those pins.
In order to do that you have to provide the tools with constraints. In our case we want to say (in English) "this Verilog symbol maps to this pin on the FPGA." There are other kinds of constraints, too, that come into play when you are trying to optimize your FPGA design but for now, this is the main one we need.
Depending on your tools, there are many ways you might introduce a constraint: You could introduce with a special comment in Verilog or by using a graphical editor. Another common method is to provide a text file as an input to the tools, and that's what IceStorm expects. A PCF file has a simple format and can set constraints on pins.
Here's a snippet of our PCF file:
set_io LED3 97 # red set_io LED4 96 # red set_io LED5 95 # green
Every external pin (including the clock) you plan to use must be defined in the constraint file. Errors in the file can be bad too. For example, routing an output to a pin that is connected directly to ground could damage the FPGA, so be careful! The picture below shows the configuration with my PCF file (the center LED and the one closest to the FPGA chip just blink and are not marked in the picture).
Keep in mind, though, you could reroute signals any way that suited you. That is, just because LED1 in the Verilog is mapped to D1 on the board, doesn’t mean you couldn’t change your mind and route it to one of the pins on the PMOD connector instead. The name wouldn’t change, just the pin number in the PCF file.
I wrote these examples before the tools could ignore a constraint you didn't use, so I commented out the ones I don't use. I asked the developer to add a feature to make pin constraints optional and it was added. In the GitHub files (or the files in the file section) the icestickfull.pcf has the necessary attributes to work with the updated tools:
# LEDs set_io --warn-no-port LED3 97 # red set_io --warn-no-port LED4 96 # red set_io --warn-no-port LED5 95 # green
If you wanted to use a different compatible board, you would only need a different PCF file. In fact, [sjlongland] added a PCF for an ice40hx8k breakout board in the GitHub project.
-
4▇▇▇▇ Understanding Configuration ▇▇▇▇
Different FPGAs use different technology bases and that may affect how you program them. But it all starts with a bitstream (just a fancy name for a binary configuration file). For example, some devices have what amounts to nonvolatile memory and you program the chip like you might program an Arduino. Usually, the devices are reprogrammable, but sometimes they aren’t. Besides being simpler, devices with their own memory usually start up faster.
However, many FPGAs use a RAM-like memory structure. That means on each power cycle, something has to load the bitstream into the FPGA. This takes a little time. During development it is common to just load the FPGA directly using, for example, JTAG. However, for deployment a microprocessor or a serial EEPROM may feed the device (the FPGA usually has a special provision for reading the EEPROM).
The FPGA on the iCEstick is a bit odd. It is RAM-based. That means its look up tables and interconnections are lost when you power down. The chip can read an SPI configuration EEPROM or it can be an SPI slave. However, the chip also has a Non Volatile Configuration Memory (NVCM) inside. This serves the same purpose as an external EEPROM but it is only programmable once. Unless you want to dedicate your iCEstick to a single well-tested program, you don’t want to use the NVCM.
The USB interface on the board allows you to program the configuration memory on the iCEstick, so, again, you don’t really care about these details unless you plan to try to build the device into something.
-
5▇▇▇▇ Configure! ▇▇▇▇
For this step, you'll need the tools installed on your PC and two files. One file corresponds to the design.sv file you used in the previous bootcamps. Because we are not using System Verilog, I've called the file demo.v, but the contents are the same.
The other file you'll need is the PCF file that tells the compiler the names of the physical FPGA pins so you can access them from Verilog. Without this file the tools could assign pins at random. Best case is you would not have the right connections. Worst case is you'd short an FPGA output to some other device output and fry something. You can read more about that in the section above entitled "Add Constraints."
If you want to do all the steps without downloading files, you should start with your Verilog file you created in Bootcamp #2 (or paste it out from EDAPlayground) into a file named demo.v. Then create a PCF file (mine is named icestick.pcf). In practice, you'll probably start with one of the ones from GitHub (or the Files section of this bootcamp) and modify it as you need and -- in this case -- icestick.pcf doesn't require any changes.
If you use your file from the previous bootcamp's don't forget to put the delays back to their normal values. If you recall, we reduced them to shorten the simulation time.
The figure above has the "real life" lines commented out (//) and the simulation lines left in. For this step, you'll reverse this so the real life lines don't have // in front of them, but the simulation lines do. The file in the files section (and on GitHub) don't have the simulation lines in there at all and looks like this:
cnt1 <= cnt1 + 1; if (cnt1 == 0) if (cnt2 == 91) begin cnt2 <= 0; half_sec_pulse <= 1; end else cnt2 <= cnt2 + 1; else half_sec_pulse <= 0;
You can find both of these files in the Files section or on GitHub. Or you can download one of the PCF files and copy your work from the previous bootcamp into a file named demo.v. Of course, you can name these anything you like if you remember to use your names instead of demo.v and icestick.pcf.
Once you have the tools installed and your files prepared, here's the actual steps you need to do configuration.
There are four command lines you’ll need to program your design into the iCEstick. I’m assuming you have the file demo.v and you’ve changed the simulation-only numbers back to the proper numbers (we talked about this last time). The arachne-pnr tool generates an ASCII bitstream so there’s an extra step to convert it to a binary bitstream. Here are the four steps:
- Synthesis:
yosys -p "synth_ice40 -blif demo.blif" demo.v
- Place and route:
arachne-pnr -d 1k -p icestick.pcf demo.blif -o demo.txt
- Convert to binary:
icepack demo.txt demo.bin
- Configure on-board EEPROM:
iceprog demo.bin
You can manually type all this in, of course from a command prompt that knows where to find the tools (that is, the tool directory is on the path, for example). If you use Linux, bash under Windows, or Cygwin you can use the very simple build.sh script below. If you do type it into a shell prompt a step at a time, you do need to do it in order and fix any errors you note between steps.
Here's build.sh (which could use some error checking, so be careful):
#!/bin/bash # Parse some command line arguments while [ $# -gt 0 ] do case "$1" in -1k) PCF=icestick.pcf DEV=1k shift ;; -8k) PCF=ice40hx8k-bb.pcf DEV=8k shift ;; -S) ICEPROG_ARGS=-S shift ;; *) break ;; esac done # Build with open source tools if [ -z "$1" ] then echo Usage: build.sh [-1k] [-8k] [-S] main_name [other .v files] echo Example: ./build.sh demo library.v exit 1 fi set -e # exit if any tool errors MAIN=$1 shift echo Using yosys to synthesize design yosys -p "synth_ice40 -blif $MAIN.blif" $MAIN.v $@ echo Place and route with arachne-pnr arachne-pnr -d ${DEV} -p ${PCF} $MAIN.blif -o $MAIN.txt echo Converting ASCII output to bitstream icepack $MAIN.txt $MAIN.bin echo Sending bitstream to device iceprog ${ICEPROG_ARGS} $MAIN.bin
This script is deliberately simple, but there is a more sophisticated version that checks for errors and gives you many options available on GitHub. I suggest you stick with this one for now, though, so you can see exactly what's going on.
If you use the script, you can just pass the name of the .v file without the .v since we only have the one file:
./build.sh -1k demo
The -S switch allows you to program RAM instead of the EEPROM. This won't work on the iCEstick unless you make some mods to the board. According to the help from iceprog:
An unmodified iCEstick can only be programmed via the serial flash. Direct programming of the SRAM is not supported. For direct SRAM programming the flash chip and one zero ohm resistor must be desoldered and the FT2232H SI pin must be connected to the iCE SPI_SI pin, as shown in this picture: http://www.clifford.at/gallery/2014-elektronik/IMG_20141115_183838
You can find a video walkthrough of the steps required below.
If you choose to do things manually, here's an explanation of each command:
- Synthesis:
yosys -p "synth_ice40 -blif demo.blif" demo.v
The yosys command takes your demo.v file and creates the demo.blif file which is an intermediate format. The -p command just feeds yosys its own command line which is, of course, "synth_ice40 -blif demo.blif" which is necessary because yosys can actually do other FPGA families.
We won't cover it here, but you could ask yosys to output Verilog which would be "predigested" so you could do simulation closer to the device layer. That's generally not necessary.
If you think of the FPGA as bunch of building blocks, this step converts your design into a virtual schematic of building blocks that connect together. Why do I say schematic? Think about the relation of a schematic to a printed circuit board. Just having a schematic doesn't tell you the location of a resistor (or its traces) on the board. It just tells you the resistor exists and it logically connects to other things.
- Place and route:
arachne-pnr -d 1k -p icestick.pcf demo.blif -o demo.txt
The printed circuit board analogy would be even better if you imagine that you have a board with a bunch of components already on it, with no connections between them. Where yosys develops the schematic, arachne-pnr figures out which components you will actually use on the board and what traces it needs to draw to connect them together.
The -d option tells it what device we are using. The -p option specifies the constraint file. The demo.blif is the output from the previous step that the tool will read. Then the -o option specifies the output file.
- Convert to binary:
icepack demo.txt demo.bin
The output of arachne-pnr is just what the FPGA wants but in a text format while the FPGA expects binary. This simple tool just converts the text to binary and doesn't do any real processing.
- Configure on-board EEPROM:
iceprog demo.bin
This final step is what transfers the binary file created in the last step to the board's EEPROM that will configure the FPGA. Like icepack, it doesn't do any processing related to your design. It simply sends data to the board.
- Synthesis:
-
6▇▇▇▇ Engage! ▇▇▇▇
Once the board programs, it will immediately start operating. Remember, this isn’t a program. Once the circuitry is configured it will start doing what you meant it to do (if you are lucky, of course). In the picture below (and the video), you can see my board going through the paces. I have some buttons for the two adder inputs on one side and a reset button on the other. I also soldered some headers to the edge of the board.
If you are lazy, you can just use a switch or a jumper wire to connect the input pins to 3.3V or ground. It looks like the device pins will normally be low if you don’t connect anything to them (but I wouldn’t count on that in a real project). However, I did notice that without a resistor to pull them down (or a switch that positively connected to ground) there was a bit of delay as the pin’s voltage drooped. So in the picture, you’ll see I put the switches to +3.3V and some pull down resistors to ground. The value shouldn’t be critical and I just grabbed some 680 ohm resistors from a nearby breadboard, but that’s way overkill. A 10K would have been smarter and even higher would probably work.
-
7▇▇▇▇ Congratulations! ▇▇▇▇
If it works, congratulations! You’ve configured an FPGA using Verilog. There’s still a lot of details to learn, and certainly this is one of the simplest designs ever. However, sometimes just taking that first step into a new technology is the hardest and you’ve already got that behind you.
Although you can do just about anything with an FPGA, it isn’t always the best choice for a given project. Development tends to be harder than microcontroller development (duplicating this project on an Arduino would be trivial, for example). Also, most FPGAs are pretty power hungry (although the one we used is quite low power compared to some). Where FPGAs excel is when you need lots of parallel logic executing at once instead of the serial processing inherent in a CPU.
FPGAs get used a lot in digital signal processing and other number crunching (like Bitcoin mining) because being able to do many tasks all at once is attractive in those applications. Of course, it is possible to build an entire CPU out of an FPGA, and I personally enjoy exploring unusual CPU architectures with FPGAs.
Then again, just because you can make a radio with an IC doesn’t mean there isn’t some entertainment and educational value to building a radio with transistors, tubes, or even a galena crystal. So if you want to make your next robot use an FPGA instead of a CPU, don’t let me talk you out of it.
-
8▇▇▇▇ Next Steps ▇▇▇▇
If you want to get more experience with this hardware, you might enjoy a two part series on Hackaday that covers adding a UART and a PWM module to create a PWM peripheral. It is a good example of how you can keep adding more and more PWM modules and they all run in parallel so there's no change in performance. Contrast that to adding software-based PWM to a CPU where adding more channels is probably going to make all channels slower. Here's the first post in the series:
https://hackaday.com/2015/12/16/taking-the-pulse-width-modulation-of-an-fpga/
If you are interested in seeing a very practical application, both Jenny List and Bil Herd have written about using this board as a logic analyzer:
https://hackaday.com/2016/12/13/compiling-a-22-analyzer/
https://hackaday.com/2016/10/26/an-open-source-96-msps-logic-analyzer-for-22/
Although this project has been geared at the Lattice hardware, the skills you've learned can apply to just about any FPGA as long as you continue to use Verilog. There will be some differences, of course. Different FPGAs have different specialized resources and you'll use different tools and different ways to specify constraints. But in the end, it isn't that much different.
Another inexpensive FPGA board is from Intel. You can read more about it here:
https://hackaday.com/2018/01/27/arrows-30-fpga-board-reviewed/
There are also offerings from Xilinx and other vendors. Nearly all of them will have free (but not necessarily open) tools that can handle Verilog.
Most starter boards will hook up to USB like the one we used did. However, if you go to higher-end boards they will probably require you to buy a specific JTAG programmer to communicate with the board. You can learn more about JTAG: https://hackaday.com/2016/12/15/the-many-faces-of-jtag/
If you are using Linux or some form of bash shell on another OS, you might like to try the ice40flow script I prepared that does the steps you need to compile Verilog files easily using IceStorm.
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.
digi-key has it and in fact I am waiting for my shipment which is in the airport in HK.
No circuit diagram for the headboard it seemed. Need to figure that out?
Are you sure? yes | no
The Lattice IceStick doesn't appear to be available any more. Can you suggest an alternative device to follow along with?
Are you sure? yes | no