Getting started with IceStorm/Verilog on the iCE40HX1K FPGA

Luke Weston
6 min readAug 10, 2018

--

Working with FPGAs used to be an annoying business with significant barriers to entry.

You’d be paying many hundreds of dollars for development boards from Altera or Xilinx, plus a separate JTAG probe from the PC, and the proprietary software toolchains for simulation and synthesis which were expensive, and dependent on Windows PCs.

I’ve recently been playing around with Clifford Wolf’s IceStorm toolchain, an open-source project for synthesis on Lattice Semiconductor iCE40 FPGAs.
Verilog synthesis, place and route, packing up the binary bitstream and deploying to the device are all implemented, and furthermore you can actually use these tools on OSX and Linux, as well as the whole free-as-in-freedom thing.

Lattice has also released the iCEstick development board for the iCE40HX1K FPGA, and the two tools go together really well.

Lattice iCEstick — a $35 all-in-one easy iCE40HX1K development board. (Yes, it’s a beautiful crystal-clear, perfectly lit, high-resolution photo, which is why I pinched it from Lattice’s website.)

It may not be the most powerful FPGA in the world, but sometimes good engineering means making a Ford for everyone, not building a Lamborghini for every problem.

You have 1280 logic cells, 96 I/O pins on a 144-pin package, some LEDs and expansion headers, power supplies implemented for you, oscillator, breakout headers, flash and USB interface to deploy the bitstream, on a well designed compact board which doesn’t require any hardware integration at all to get started.

(The IrDA transceiver is old school, like you can sync your FPGA board to your Palm Pilot or something. But it is a neat choice — you can easily deploy a simple, lightweight duplex serial link a short distance across the ether, without the complexities of RF communications.)

And you’ve got everything you need all nicely integrated for just AUD$35, comparable to a well designed and well supported Freetronics Eleven or genuine Arduino Uno price point. It’s a well designed hardware platform and a good deal.

Well, you have to take the iCEstick out of the packet, and the fairly wide “USB stick” form factor can be tricky on some machines, so a USB type-A male-to-female cable can be helpful to plug the board in. But that’s absolutely all the hardware integration you need to do. When you plug in to a powered USB port, the iCEstick will load its default demo bitstream, with a chaser pattern on the onboard LEDs.

We can get started on the metal by installing the IceStorm tools. On Ubuntu, this was pretty straightforward and worked for me. There are a handful of prerequisite tools to install first, if you don’t already have them.

sudo apt-get install make build-essential clang bison flex \ libreadline-dev gawk tcl-dev libffi-dev git mercurial graphviz \
xdot pkg-config python python3 libftdi-dev

Then install the IceStorm tools, plus yosysand arachne-pnr

git clone https://github.com/cliffordwolf/icestorm.git icestorm
cd icestorm
make -j$(nproc)
sudo make install
git clone https://github.com/cseed/arachne-pnr.git arachne-pnr
cd arachne-pnr
make -j$(nproc)
sudo make install
git clone https://github.com/cliffordwolf/yosys.git yosys
cd yosys
make -j$(nproc)
sudo make install

It takes a little while to install and build everything, but this is fairly painless.

For OSX, there’s a shell script here which makes the install process easier.
You’ll need to have homebrew installed.

Personally, using these install scripts on OSX I got an error during the Verilator build, but the important tools such as yosys and arachne-pnr were built successfully. Verilator is just a Verilog simulation tool, which is nice to have but is not a dealbreaker for getting started working on the metal.

If you’re on OSX and iceprog fails to talk to the board (and you’ve got it plugged in) you may need to unload the FT2232 driver, which looks something like this.

sudo kextunload -v -b com.FTDI.driver.FTDIUSBSerialDriver

Now that the tools are installed, we can actually write some HDL. At uni we did all our work with VHDL, but IceStorm only supports Verilog.
Oh well, I suppose we’re learning Verilog. (I think the iCEcube2 design software from Lattice supports VHDL, but it doesn’t run on OSX as far as I’m aware.)

Let’s get started with a really simple little Verilog example. We can start off with, say, blinking a LED. But you can’t just use a timer to blink the LED. What timer? You need to build the timer.

Given a breadboard and a generous stock of logic gates and flip-flops, how do you blink a LED? Think about your circuit design and implement that in HDL.
Analog circuits like relaxation oscillators, capacitors and 555s and such don’t count. This is a digital machine, remember.

The iCEstick does have a crystal oscillator onboard connected to one of the FPGA pins, providing a 12 MHz clock you can use.

You’re not writing a program that runs on a computer at all — you’re writing a description of a digital logic machine. HDLs are kind of similar in some ways to functional languages like Haskell, if you’ve worked with these before.

We’ll start by writing a file /blink/pins.pcf

pins.pcf is pretty self-explanatory. It’s a file which contains a map, specific to this board, that defines the pins of the FPGA chip.
This allows us to use human-readable definitions of what those pins are connected up to, such as the onboard LEDs, connection headers, and the oscillator.

set_io CLK_IN 21
set_io J3_10 44
set_io J3_9 45
set_io J3_8 47
set_io J3_7 48
set_io J3_6 56
set_io J3_5 60
set_io J3_4 61
set_io J3_3 62
set_io GLED5 95
set_io RLED4 96
set_io RLED3 97
set_io RLED2 98
set_io RLED1 99
set_io IR_TX 105
set_io IR_RX 106
set_io IR_SD 107
set_io J1_3 112
set_io J1_4 113
set_io J1_5 114
set_io J1_6 115
set_io J1_7 116
set_io J1_8 117
set_io J1_9 118
set_io J1_10 119

At the moment we won’t use the majority of these pins, but let’s put them all in anyway — they’ll be ignored if not used. I’ve left off the PMOD header pins and the pins exposed to the FT2232 which can provide serial comms to the PC from the FPGA user fabric.

We run yosys as follows:

yosys -q -p ‘synth_ice40 -top main -blif blink.blif’ blink.v

This generates a synthesised file blink.blif from our Verilog source code file blink.v

Now you’d run arachne-pnr, the place-and-router tool, which takes the file blink.blif and generates the ASCII bitstream file blink.txt as follows:

arachne-pnr -q -d 1k -o blink.txt -p blink.ucf blink.blif

icepack is used to convert the ASCII bitstream file into the binary bitstream file we need for the hardware configuration:

icepack blink.txt blink.bin

And finally, iceprog deploys the binary bitstream to the device:

iceprog blink.txt

But we don’t want to remember all the right syntax and we’re lazy, so let’s make a Makefile.

all: $(notdir $(CURDIR)).bin%.bin: %.txt
icepack $< $@
%.txt: %.blif
arachne-pnr -q -d 1k — post-place-blif $*.place.blif -o $@ -p $(notdir $(CURDIR)).pcf $<
%.blif: %.v
yosys -q -p ‘synth_ice40 -top main -blif $@’ $<
upload: $(notdir $(CURDIR)).bin
iceprog $<
clean:
rm -f *.bin *.txt *.blif *.rpt
.PRECIOUS: %.bin %.txt %.blif
.PHONY: all explain install clean

(Let’s just work with files that all have the same prefix, say blink, in a directory with the same name.)

Now we need some actual HDL, in the file blink.v

module main(input CLK_IN, output GLED5, output RLED1, output RLED2, output RLED3, output RLED4);localparam COUNTER_WIDTH = 24;
// 2^24 = 16 million or so, approx 0.75 Hz with 12 MHz clock.
reg [COUNTER_WIDTH-1:0] counter;
always @(posedge CLK_IN)
counter <= counter + 1;
assign GLED5 = counter[COUNTER_WIDTH — 1]; // MSB
assign RLED1 = counter[COUNTER_WIDTH — 2];
assign RLED2 = counter[COUNTER_WIDTH — 3];
assign RLED3 = counter[COUNTER_WIDTH — 4];
assign RLED4 = counter[COUNTER_WIDTH — 5];

endmodule

So, what does our blinking LED need to look like?
We could just build a “wire” from the clock oscillator pin to a LED pin, but you won’t be able to see a LED blinking at 12MHz. (Try it. Can an oscilloscope see it?)

We need to divide down the clock frequency, using a 24-bit counter. Just like a series of 24 cascaded flip-flops, clocked at the positive edge of the 12MHz clock. As we move up towards the MSB we can pick off lower-frequency outputs that can visibly blink a LED.

Now make, and the bitstream will hopefully be built without errors.
Ensure the hardware is plugged in before doing make upload.

The flash write should be successful, and the middle green LED should blink at 0.715 Hz. The four outer LEDs (connected to the four next most significant counter bits) should flash faster in the expected binary counting sequence.

See? (well, you can’t see the LEDs change state at all — but trust me, it works as expected.)

There are some more detailed resources and examples here, here, here and here.

--

--