Skip to content

Adding support for new devices

Simon Schubert edited this page Feb 3, 2019 · 1 revision

These instructions were originally posted to reddit here and here but moved here so others could contribute.

I've been asked what steps are necessary to add support for other chips (such as iCE40 UltraLite) to Project IceStorm. I've offered to give some guidance. I might as well do it in form of posts here, so everyone can read along.

icefuzz/icecube.sh is a shell script that runs the Lattice iCEcube tools to synthesize a given design. The design must be a single Verilog file and the top module must be named "top". For example:

$ cat demo.v 
module top(input A, B, C, D, output X, Y, Z);
  assign X = |{A, B, C, D};
  assign Y = &{A, B, C, D};
  assign Z = ^{A, B, C, D};
endmodule

$ ICEDEV=ul1k-cm36a bash icefuzz/icecube.sh demo.v

I've already added support for ICEDEV=ul1k-cm36a and ICEDEV=ul1k-swg16 to the script some time ago. It currently support this two iCE40 UltraLite devices as well as all HX/LP 1K, 4K, and 8K devices.

The first step is adding support for all chips (and package variations) that you want to add support for to this script. There is a big "case" statement for all supported $ICEDEV values, setting $iCEPACKAGE and $iCE40DEV. Then there is another "case" statement for all $iCE40DEV values setting the variables $icetech, $libfile, and $devfile.

The easiest way to figure out what values to use for this variables is running the iCEcube GUI, synthesizing for the new device you want to add support for (select LSE as synthesis tool), and for a device that already is supported by IceStorm, and simply comparing the log messages for this two cases.

When the target device is not yet supported by icepack/iceunpack, you will get the following error message at the end of the icecube.sh output:

+ icefuzz/../icepack/iceunpack demo.bin demo.asc
Error: Failed to detect chip type.

This is OK. Simply ignore it for now. Adding support to icepack/iceunpack for the new chip will be part 2 of this series.

Running icecube.sh on demo.v will produce the following output files:

File Description
demo.tmp/ Working directory for synthesis
demo.bin Generated iCE40 bit-stream
demo.asc IceStorm ASCII version of bit-stream (when iceunpack support is available)
demo.glb Debug output written by the iCEcube "bitmap" tool
demo.psb Placement constraint file generated by iCEcube tools
demo.rpt Timing report generated by iCEcube "sbtimer" tool
demo.sdf Timing edges (SDF format) generated by iCEcube "sbtimer" tool
demo.vsb Timing netlist generated by iCEcube "sbtimer" tool

The most interesting file for us to look at is "demo.glb", the debug output generated by Lattice's "bitmap" tool. It contains blocks of text like this one:

LogicTile_8_10

 (11 6)  (323 182)  (323 182)  routing T_6_11.sp4_h_r_11 <X> T_6_11.sp4_v_t_40
 (13 6)  (325 182)  (325 182)  routing T_6_11.sp4_h_r_11 <X> T_6_11.sp4_v_t_40
 (12 7)  (324 183)  (324 183)  routing T_6_11.sp4_h_r_11 <X> T_6_11.sp4_v_t_40

This tells us that bits (11 6), (13 6), and (12 7) are set in the LogicTile 8 10. Note that the bit (11 6) corresponds to the bit B6[11] in IceStorm notation.

The 2nd pair of numbers (I don't know why this is included twice) are the coordinates of that same bit, but not relative to the tile but to the start of the CRAM bank the tile is in. The rest of the line is a brief description of what the bit does.

If you haven't already you should carefully read the stuff in the "Where is the Documentation?" section on http://www.clifford.at/icestorm/, and the documents linked from there. You should also browse the "Bit Docs" for LOGIC and IO tiles a little bit to get a better understanding and feeling for what the individual bits in the iCE40 tiles do.

Now we can use Lattice iCEcube easily and in a script-based fashion to generate bitstreams and additional output files for a given verilog source file. Now we will be using the information in this .glb file to add support for new devices to icepack/iceunpack.

The next step is adding support for the new part to icepack/iceunpack. First you should make sure you have carefully read the bitstream file format documentation on the IceStorm website.

Running iceunpack demo.bin demo.asc on a binary bit-stream file for the new chip will likely produce Error: Failed to detect chip type. now. It's time to start hacking on icepack.cc to add support for the new chip type. Chip types are detected based on the CRAM size. Run the iceunpack command again, but this time with the -v option (use -vv for increased verbosity). This time the command will (among other things) print the size of the CRAM segments.

Go through icepack.cc and add a new device type to the existing ones ("1k" and "8k", searching for those strings in icepack.cc will get you to all the relevant places). Start by adding detection for your chip (see the end of FpgaConfig::read_bits). For some of the information (like size of the chip in rows and columns of tiles) you might need to consult the device datasheet or the floorplanner view of the iCEcube GUI. Compare the values for already supported devices with their datasheet values and iCEcube floorplanner view if it is hard to make sense of the parameters.

Note that FpgaConfig::chip_cols() describes the left half of the device. The right half is a mirrored image of the left one. 18, 54, and 42 are the CRAM widths of IO Tiles, LOGIC tiles and RAM tiles respectively.

As soon as iceunpack is able to at least load the binary bit-stream it can come handy to use the various options that write netpbm images as output files. I'd recommend to first familiarize yourself with those options and the images they create using bit-streams for already supported devices, and then use them to analyze the files for the new chip type. (The colorful checkerboard-like image on the IceStorm doc page for the bit-stream format was created using this options.)

Above I briefly discussed the .glb files created by icecube.sh. The script icefuzz/glbcheck.py can be used to compare a .glb dump file with an ASCII file generated by iceunpack. This should help a lot with debugging problems with icepack.cc.

Verification is key! Make sure to set up a small set of examples and write tiny scripts that help you track the mismatches between the generated .asc and .glb using glbcheck.py. Usually when I get stuck in a reverse engineering project, as soon as I have implemented a way to measure my progress, I quickly start making progress again.

Luckily, the icefuzz directory contains a number of python scripts which can generate sample Verilog. You can run these auto-generated files through iCEcube by running make. This defaults to the 1k device but you can add additional devices to the Makefile and fuzzconfig.py file. (Note, @tannewt is changing the way to select the device to make ICEDEVICE=5k for example.)

In fuzzconfig.py you'll need to provide a list of pins in pins, the number of ram blocks in num_ramb40 and the list of global buffer pins in gpins. Global buffer pins are those that can be used to provide high drive signals through the whole array for signals such as clock and reset. The list of pins is most easily found in the Package view within iCEcube2. This view also marks pisn as GBIN which go into gpins. The number of ram blocks can be counted off of the Floor Planner view in iCEcube2.

Clone this wiki locally