How to use command-line flow (IceStorm or Cloud Synth)

Hi all,

I’m going to briefly describe how to flash the device using our command-line flasher in conjunction with cloud-synthesis or a local IceStorm installation. (You can also use the flasher tool with the official iCECube IDE). This tutorial should work for any operating system that supports Node.js and npm.

First, you need to ensure that Node.js and npm are installed. On Debian systems, you can run sudo apt get install nodejs.

Then you need to install the webfpga utility with npm install -g webfpga-cli. Once you have that tool installed, can view the usage by simply typing in webfpga into your commannd-line prompt.

$ webfpga
Usage: webfpga [options] [command]

Options:
  -V, --version              output the version number
  -h, --help                 output usage information

Commands:
  synth <top.v> [others...]  synthesize one or more verilog source files
  flash <bitstream.bin>      flash webfpga device
  help [cmd]                 display help for [cmd]

Cloud Synthesis + Flashing

# blinky.v
module fpga_top(output reg WF_LED, input wire WF_CLK);
    reg [24:0] counter;

    // blink LED every 1000 ms
    always @ (posedge WF_CLK) begin
        if (counter == 8000000) begin
            WF_LED   <= ~WF_LED;
            counter  <= 'b0;
        end else begin
            counter  <= counter + 'b1;
        end
    end
endmodule
$ webfpga synth blinky.v 
Synthesizing source files:
  - blinky.v

{ id:
   '20a2cf13aea4c148a68877788727df962b4e86c4c91ce2f6cda84cc556b5d87b',
  cached: false }
Subscribed to stream: 20a2cf13aea4c148a68877788727df962b4e86c4c91ce2f6cda84cc556b5d87b
synthesis-engine has received the request...
Saving blinky.v...
Synthesizing...
Detected modules for file '/tmp/synthesis_worker.2f4137ba-1373-4cdc-ba0b-5c5797455511.20190811-19-17e4xy0/blinky.v':
found the top-level module
====> fpga_top (file /tmp/synthesis_worker.2f4137ba-1373-4cdc-ba0b-5c5797455511.20190811-19-17e4xy0/blinky.v) <====
{
"top_level": "fpga_top",
"io_map": {},
"top_level_found": true,
"error": false,
"pcf_contents": "set_io WF_LED 31nset_io WF_CLK 35nset_io WF_BUTTON 42nset_io WF_NEO 32nset_io WF_CPU1 11nset_io WF_CPU2 12nset_io WF_CPU3 13nset_io WF_CPU4 10n"
}
Top level verilog module is fpga_top.
Synthesis:Verilog to EDIF: LUT4s:32( 0.61%)
                           FLOPs:24( 0.45%)
                           IOs  :2( 5.13%)
                           CLOCK:1.000     FREQ:  MHz
                                       target: 24 MHz
EDIF Parser: Successful.
PLACER:Run time up to 2:00 minutes. Compute intensive.
5280
Placement of Design:       LUT4s:32( 0.61%)
                           FLOPs:24( 0.45%)
                           IOs:  2( 5.13%)
                           CLOCK:fpga_top|WF_CLK     FREQ: 114.12 MHz
                                       target: 1.00 MHz

Packer: DRC check was successful. Now doing packing.
Packer: Packing was successful. Logic cells used: 32 

Routing of Design: Successful. Nets:57 Iterations:2.
Writing of Design Netlist: Successful.
 
Static Timing of Design: 
Timing completed. See timing report file.
Clock: fpga_top|WF_CLK  | Frequency: 82.16 MHz  | Target: 1.00 MHz  | 
 
Bit File Generation: Successful.
header  : E+Sun 11 Aug 2019 09:21:13 PM UTC+shastaplus
header2 : E+Sun_11_Aug_2019_09:21:13_PM_UTC+shastaplus
synthesis complete!

(By default, the tool saves the bitstream as bitstream.bin, but you can set a custom output filename with the -o flag.)

$ webfpga flash bitstream.bin
Connected to board.
Cascadia WebUSB FPGA Programmer - Auburn Ventures, LLC

Finding and checking for programmer...
--RECV AT: Hi (pass)
--RECV API: C_WEBUSB+ (pass)
--RECV APR: 000921 (pass)
Found programmer.
Checking for FPGA module and its flash configuration...
--RECV APWE: wren (pass)
--RECV AMQ: SA016WHEe (pass)
--RECV AMBE: DONE (pass)
--RECV AMQ: SA016WHEE (pass)

flashing module...
--RECV AMW: OK (pass)
....................|
....................|
..............
Done.

Offline IceStorm Synthesis + Flashing

This example repository will fade the on-board RGB LED.

This assumes that you have the IceStorm flow installed. You need yosys, arachne-pnr, and icepack. Read the Makefile for more details. You also need to have the NodeJS/npm installed to run the webfpga CLI utility.

$ git clone https://github.com/webfpga/webfpga_icestorm_examples
$ cd webfpga_icestorm_examples
$ make
$ make flash

Open-Source + Self-Hosted Cloud (IceStorm)

I’m not sure whether it’s because I’m running the “testing” branch of Debian, or whether the npm packages are outdated, but I can’t seem to establish a functional install.

If I install nodejs and npm (using apt) and then use npm to install webfpga, I end up with what appears to be a dummy version:

[email protected]:~$ sudo npm install -g webfpga
npm WARN npm npm does not support Node.js v10.15.2
npm WARN npm You should probably upgrade to a newer version of node as we
npm WARN npm can't make any promises that npm will work with this version.
npm WARN npm Supported releases of Node.js are the latest release of 4, 6, 7, 8, 9.
npm WARN npm You can find the latest version at https://nodejs.org/
/usr/local/bin/webfpga -> /usr/local/lib/node_modules/webfpga/app.js
+ [email protected]
added 1 package from 1 contributor in 0.91s
[email protected]:~$ webfpga --help
error: You probably meant to install 'webfpga-cli'

npm install -g webfpga-cli

and if I try doing what it suggests:

[email protected]:~$ sudo npm install -g webfpga-cli
npm WARN npm npm does not support Node.js v10.15.2
npm WARN npm You should probably upgrade to a newer version of node as we
npm WARN npm can't make any promises that npm will work with this version.
npm WARN npm Supported releases of Node.js are the latest release of 4, 6, 7, 8, 9.
npm WARN npm You can find the latest version at https://nodejs.org/
npm WARN deprecated [email protected]: this package has been deprecated
npm ERR! path /usr/local/lib/node_modules/webfpga-cli/bin/webfpga
npm ERR! code ENOENT
npm ERR! errno -2
npm ERR! syscall chmod
npm ERR! enoent ENOENT: no such file or directory, chmod '/usr/local/lib/node_modules/webfpga-cli/bin/webfpga'
npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! enoent 

npm ERR! A complete log of this run can be found in:
npm ERR!     /root/.npm/_logs/2019-08-12T00_27_15_133Z-debug.log

Okay, I’ve re-published the tool. Try running npm uninstall -g webfpga-cli then npm install -g webfpga-cli.

You should be running version 0.1.2 to be compatible with the latest firmware:

$ webfpga --version
0.1.2

Different failure this time

[email protected]:~$ sudo npm install -g webfpga-cli
npm WARN npm npm does not support Node.js v10.15.2
npm WARN npm You should probably upgrade to a newer version of node as we
npm WARN npm can't make any promises that npm will work with this version.
npm WARN npm Supported releases of Node.js are the latest release of 4, 6, 7, 8, 9.
npm WARN npm You can find the latest version at https://nodejs.org/
/usr/local/bin/webfpga -> /usr/local/lib/node_modules/webfpga-cli/bin/webfpga

> [email protected] install /usr/local/lib/node_modules/webfpga-cli/node_modules/usb
> prebuild-install --verbose || node-gyp rebuild

prebuild-install info begin Prebuild-install version 5.3.0
prebuild-install WARN install EACCES: permission denied, access '/root/.npm'
gyp ERR! configure error 
gyp ERR! stack Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/webfpga-cli/node_modules/usb/build'
gyp ERR! System Linux 4.19.0-5-amd64
gyp ERR! command "/usr/bin/node" "/usr/bin/node-gyp" "rebuild"
gyp ERR! cwd /usr/local/lib/node_modules/webfpga-cli/node_modules/usb
gyp ERR! node -v v10.15.2
gyp ERR! node-gyp -v v3.8.0
gyp ERR! not ok 
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] install: `prebuild-install --verbose || node-gyp rebuild`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the [email protected] install script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

Ah, I will have to double-check the permissions in my package. For now, you should be able to run everything with npx webfpga-cli.

For example,

$ npx webfpga-cli synth blinky.v

And some further struggles (e.g. trying to do an npm install of usb itself) continue to have problems. I have a feeling that my system is trapped in a twistly little maze of node.js and npm dependencies, all different and perhaps incompatible.

I wonder… given how complex node.js is (and all that it seems to require to install it and use it), how difficult would it be to port the flashing API to run on top of something like Python and its USB interface library? When I was playing around yesterday I found that it took about five lines of Python to successfully access the device and claim the interface (although I haven’t tried actually sending anything to it).

I looked on github and found your “webfpga-api” repository and I’ve looked at board.js - it appears that this might be fairly straightforward to convert to Python. Is there any formal documentation of the host-to-microcontroller protocol available?

OK, that seems to work - I can get to “npx webfpga-cli help” and get something meaningful. I’ll pick up from there - thanks!

Okay, I just spun up a Debian virtual machine, and I’m getting the same permission errors as you… but not on Arch Linux. To me, it looks like an issue with the usb package, as npm install -g usb yields the same error. Odd…


Yeah, I originally attempted to write the flashing utility in Go… but then I decided to use Node.js so that I wouldn’t have to re-implement both the WebUSB protocol and our own protocol stack. I am literally using the same JavaScript code that runs on the website. But yes, I agree, Node.js/npm is definitely overkill.

In my opinion, board.js has our protocol well-documented, so if someone has the time, they could whip up an implementation on a more system-oriented language. I’ll write up a decent README describing the overarching steps needed. Bluntly, we use a series of AT commands to ensure the firmware is compatible, then set the flash to write, then transfer the compressed bitstream (which the MCU decompresses).

Finding and checking for programmer...
--RECV AT: Hi (pass)
--RECV API: C_WEBUSB+ (pass)
--RECV APR: 000921 (pass)
Found programmer.
Checking for FPGA module and its flash configuration...
--RECV APWE: wren (pass)
--RECV AMQ: SA016WHEe (pass)
--RECV AMBE: DONE (pass)
--RECV AMQ: SA016WHEE (pass)

When I get the time, I’ll look into transitioning into Python or such.

OK, thanks - it looked pretty straight-forward from what I could see, and I think it’ll translate to Python cleanly. I may give it a stab in my Copious Spare Time (cue horse laugning in the distance).

Oh, for what it’s worth, “npx webfpga-cli synth blinky.v” works fine, and synthesizes the design and delivers the bitstream. However, I cannot flash from the command line (but if I fire up Chromium afterwards and use the Web page, I can synthesize and flash).

[email protected]:~/fpga$ npx webfpga-cli flash bitstream.bin
prebuild-install info begin Prebuild-install version 5.3.0
prebuild-install info looking for cached prebuild @ /home/dplatt/.npm/_prebuilds/f78b17-usb-v1.6.0-node-v64-linux-x64.tar.gz
prebuild-install info found cached prebuild 
prebuild-install info unpacking @ /home/dplatt/.npm/_prebuilds/f78b17-usb-v1.6.0-node-v64-linux-x64.tar.gz
prebuild-install info unpack resolved to /home/dplatt/.npm/_npx/12619/lib/node_modules/webfpga-cli/node_modules/usb/build/Release/usb_bindings.node
prebuild-install info unpack required /home/dplatt/.npm/_npx/12619/lib/node_modules/webfpga-cli/node_modules/usb/build/Release/usb_bindings.node successfully
prebuild-install info install Successfully installed prebuilt binary!
Connected to board.
Cascadia WebUSB FPGA Programmer - Auburn Ventures, LLC


Finding and checking for programmer...
error: board failed verification
ReferenceError: TextDecoder is not defined
    at device.controlTransferIn.then.result (/home/dplatt/.npm/_npx/12619/lib/node_modules/webfpga-cli/lib/webfpga-api/lib/board.js:108:22)

I’m not sure why the Web-Javascript flasher validates the programmer OK but the command-line version doesn’t.

Huh, yeah it looks like TextDecoder isn’t supported on your version of Node.js… What version are you running? It’s supported in the standard library (https://nodejs.org/api/all.html#util_class_util_textdecoder).

I’m guessing that’s the only reason why the flasher fails.

Debian Testing seems to be on version 10.15.2. It may be that I need to pull in another library for it that isn’t part of the normal Debian dependencies… I’ll check around about that.

My first crude bit of Python has just gotten back a “Hi” from an AT command… this bodes well!

Further yet:

[email protected]:/src/webfpga-flasher$ ./flash.py
Config 1
Interface 0,0
Endpoint 129
AT: Hi

API: C_WEBUSB+

APR: 010105

Found programmer.
Checking for FPGA module and its flash configuration…
APWE: wren

AMQ: SA016WHEe

1 Like

Ah, okay after some digging, I found this GitHub issue (https://github.com/nodejs/node/issues/20365). It seems to show that v10 of Node.js doesn’t include TextDecoder by default (located in the util standard library). I’ll look into that.

The difficult part about making the same JS code run on both Node.js and the browser, is running require().


But… you’ve already made good progress on the Python code! I doubt that the communication protocol will change much, so having separate implementations shouldn’t be an issue. I’ll document the AT commands for everyone.

It doesn’t really matter what language it’s done in, as long as it’s reasonably cross-platform – and Python fits that bill just fine.

Just updated the original post with IceStorm code.

Yee-hah!

I’ve successfully flashed the compressed bitstream for blinky.v using my new Python 3 flasher, overwriting a copy of the blinky-with-switch that I had flashed via the web-site interface. It started blinking away once per second, just as it should.

[email protected]:/src/webfpga-flasher$ ./flash.py bitstream.bin 
Config 1
	Interface 0,0
		Endpoint 129
AT: Hi
API: C_WEBUSB+
APR: 010105
Found programmer.
Checking for FPGA module and its flash configuration...
APWE: wren
AMQ: SA016WHEe
AMBE: DONE
AMQ: SA016WHEE
AMW: OK
AMWD: 0  3e  0  3e
AMWD: 1  40  0  40
AMWD: 2  40  0  40
AMWD: 3  3f  7  3f
AMWD: 4  3d  c  3d
AMWD: 5  40  11  40
AMWD: 6  40  16  40
AMWD: 7  40  21  40
AMWD: 8  40  33  40
AMWD: 9  40  45  40
AMWD: 10  3e  50  3e
AMWD: 11  40  67  40
AMWD: 12  40  6c  40
AMWD: 13  40  71  40
AMWD: 14  40  72  40
AMWD: 15  3e  78  3e
AMWD: 16  3e  7e  3e
AMWD: 17  40  83  40
AMWD: 18  40  88  40
AMWD: 19  40  91  40
AMWD: 20  40  a4  40
AMWD: 21  40  a9  40
AMWD: 22  40  ad  40
AMWD: 23  40  b2  40
AMWD: 24  40  b7  40
AMWD: 25  3e  bb  3e
AMWD: 26  40  c0  40
AMWD: 27  40  c5  40
AMWD: 28  40  d3  40
AMWD: 29  40  e1  40
AMWD: 30  40  f2  40
AMWD: 31  3d  111  3d
AMWD: 32  40  118  40
AMWD: 33  40  11c  40
AMWD: 34  40  11f  40
AMWD: 35  3f  121  3f
AMWD: 36  40  125  40
AMWD: 37  3e  127  3e
AMWD: 38  3f  129  3f
AMWD: 39  3d  12a  3d
AMWD: 40  40  12b  40
AMWD: 41  40  12d  40
AMWD: 42  40  12e  40
AMWD: 43  3f  12f  3f
AMWD: 44  40  131  40
AMWD: 45  3f  132  3f
AMWD: 46  40  134  40
AMWD: 47  40  138  40
AMWD: 48  40  14f  40
AMWD: 49  40  154  40
AMWD: 50  40  159  40
AMWD: 51  3f  165  3f
AMWD: 52  3e  178  3e
Hit the last-block marker?
AMWD: 53  ae  18c  2e
1 Like

Hey, that’s awesome! Before we create more extensive documentation for all of the AT commands, here’s what they briefly stand for:

  AT: Hi          | Simple firmware communication test
 API: C_WEBUSB+   | Programmer Type
 APR: 010105      | Programmer Firmware Revision ID
APWE: wren        | Flash Write Enable
 AMQ: SA016WHEe   | Flash Device Type (will document string encoding later...)
AMBE: DONE        | Flash Block Erase
 AMQ: SA016WHEE   | Flash Device Type
 AMW: OK          | Set Programmer into "Write Mode". Ready to receive data
AMWD: ...         | Send data record (up to 64 bytes) of compressed data

Normally, we just output a single dot character for each AMWD ACK. So on screen here, each dot represents 64 bytes:
flash dots

Just whipped this up last night. You should be able to clone the repository and immediately run the Python synthesis.py script in ./client/. The only the dependency is websocket-client, which you can grab with pip.

Try navigating to ./client/ and running the example blinky. The LED should change rate depending if the main user button is held down or not. IceStorm synthesis speeds are pretty snappy on our backend. I’m really pleased.


icestorm-server

This is a fully open-source and self-hosted implementation of the WebFPGA interface and synthesis flow. The FPGA bitstream is generated on a remote server and returned to the client.

For more information about WebFPGA, check out https://webfpga.io.


Command-line Client

The command-line client is written in Python and transmits Verilog source files and an optional pinmap.pcf file to the backend. The backend responds with real-time logs and the final, compressed bitstream.

By default, it is configured to send jobs to ws://icestorm.webfpga.io:2019. You can change that by modifying the SERVER= string in synthesis.py.

$ cd client
$ pip install websocket-client
$ ./synthesis.py fpga_top blinky.v pinmap.pcf
$ webfpga flash bitstream.bin

Server

The server is written in Node.js and is a WebSocket wrapper around a Makefile that runs the IceStorm flow.

To get started, make sure you have IceStorm installed. (This includes yosys, etc…)

$ cd server
$ npm install
$ node app.js

Docker

Or… you can host icestorm-server via Docker. This is by far the easiest method to up and running. For example, if you are running MacOS, all you need is Python and Docker to get started with the WebFPGA Standalone Board. (That is, if you want to go the fully open-source and self-hosted route. Remember, you can always navigate to the official Web IDE (https://beta.webfpga.io) to get started without any install or software dependencies.)

$ docker run -it --rm -p 2019:2019 webfpga/icestorm-server:latest

This is great progress, Ryan!

I synced up my copy of the icestorm examples and ran a local synthesis (I have the top-of-tree versions of yosys, nextpnr, etc. installed). I then used my latest-and-greatest Python3 flasher to program the .cbin file. It programmed fine and the LED is now color-changing as I assume it should.

I’ve got the Python3 flasher just about ready to share. It now has a choice of two different flashing-progress indicators… one is a dot-per-packet, 20-dots-per-line version modeled after your Web version, and another is a “byte count and spinner” single-line display. Currently you have to modify the source code to select the one you want… I may eventually add a command-line option.

How do you want to handle contributions to the open-source stuff? Can you set up a repo that we can check out, and then push new workbranches into and send you a pull request? or what…?

1 Like

I’ve created a GitHub repository here: https://github.com/webfpga/cli. I can add you as a collaborator right now, or we can work off of PRs. I think we’re pretty close to migrating away from the basic Node.js tool completely – with your new flasher utility and my client code. The flasher is definitely the most important piece though, because without it, people can’t fully go offline. The Node.js tool works, but Python is definitely simpler and offers better cross-platform reliability.

Ideally, users will be able to run ‘pip install webfpga’ to grab the command-line tool. I’ve created a pip package here: https://pypi.org/project/webfpga/.

The next thing to do is re-implement our bitstream compression utility in Python. Right now it’s about 600 lines of messy C code :frowning: It saves a lot of time when flashing because small logic (such as blinky) is typically over 90% zeroes, so our utility compresses them into small sub-64k frames that can be expanded by the MCU before they hit the flash chip. Oftentimes, we see 8-15x speed improvements. I’ll attach the original C source code within a day or two; I’m just going to do a couple of passes to get the code cruft down.

I would like the command-line interface to look something similar to the old tool. I’ve never created a proper Python command-line package before so I’ll have to do some research on how to set it up.

Usage: webfpga [options] [command]

Options:
  -V, --version              output the version number
  -h, --help                 output usage information

Commands:
  synth <top.v> [others...]  synthesize one or more verilog source files
  flash <bitstream.bin>      flash webfpga device
  help [cmd]                 display help for [cmd]

Let me know! This is exciting progress!