Sunday, February 17, 2019

Notes on ulx3s FPGA: Yosys, Verilog, VHDL, vhdl2vl (Convert VHDL to Verilog)

I want to better understand my new ULX3S, and I was hoping to use FOSS FPGA tools. Here I document some things I've recently learned:

Some key points (from ):
  • the standard JTAG programming stuff only programs FPGA SRAM; So rebooting the board will "recover" it 
  • In general you have to go through a fairly tedious process involving Diamond Deployment Tool to create a SVF file to program flash, and this will be very slow Otherwise, you are just programming SRAM (update: this is no longer the case; see ujprog -j flash file.bit
-- @daveshah1
  • I guess the ulx3s-passthru bitstream is by default shipped to you, when you have OLED and ESP32 in box then it makes sense to load the board with passthru.
  • Other boards without OLED and/or ESP32 are probably shipped with f32c with FAT filesystem on config flash to run self-test application at power up.
  • You can erase config flash and ESP32 with whatever you want and flash back to "factory default" from my ulx3s-bin repository.
  • passthru is old source and it needs updated makefile like in prjtrellis-dvi. Yes it tries to find diamond.
  • Passthru is very simple and it could be ported done using opensource tools only. I'm using vhd2vl. For most simple vhdl examples it works great.
  • It can't convert if VHDL source is complex/advanced and has functions and packages.
-- @emard
I case you are wondering about that second USB port (the left one, when facing them):
don't touch the second USB port - it's wired directly into FPGA and thus won't even enumerate on the PC as a serial port unless you do some serious coding at FPGA side (see TinyFPGA Bootloader project - it's pretty much all about it) ... instead, use USB1 which is wired through FT231 chip (see info here)
-- @reostat 
My favorite claims / features:
  • ULX3S is unbrickable
-- @emard
  • There is no realistic chance of irreparable damage to the ULX3S in any case
-- @daveshah1

The term "iCEStorm Toolchain" does not mean Yosys, arache-pnr, nextpnr, Trellis; just iCEStorm.

Yosys, arache-pnr, nextpnr, Trellis supports only Verilog, not VHDL

I have a ULX3S 12K. The Blinky from DoctorWkt expects a 45F chip. Even when editing the original Makefile to instead use the --12 option, nextpnr-ecp5 failed:
Yosys 0.8+148 (git sha1 e112d2fb, clang 6.0.0-1ubuntu2 -fPIC -Os)
Time spent: 54% 11x read_verilog (0 sec), 9% 6x techmap (0 sec), ...
nextpnr-ecp5 --12k --json blinky.json --basecfg ulx3s_empty.config \
--lpf ulx3s_v20.lpf \
--textcfg ulx3s_out.config
unrecognised option '--12k'
Makefile:74: recipe for target 'ulx3s_out.config' failed
make: * [ulx3s_out.config] Error 255
In fact, things were looking pretty bleak, as the nextpnr-ecp5 does not even list the 12F:
nextpnr-ecp5 -- Next Generation Place and Route (git sha1 4c73061)

General options:
  -h [ --help ]             show help
  -v [ --verbose ]          verbose output
  -q [ --quiet ]            quiet mode, only errors and warnings displayed
  -l [ --log ] arg          log file, all log messages are written to this file
                            regardless of -q
  --debug                   debug output
  -f [ --force ]            keep running after errors
  --gui                     start gui
  --run arg                 python file to execute instead of default flow
  --pre-pack arg            python file to run before packing
  --pre-place arg           python file to run before placement
  --pre-route arg           python file to run before routing
  --post-route arg          python file to run after routing
  --json arg                JSON design file to ingest
  --seed arg                seed value for random number generator
  -r [ --randomize-seed ]   randomize seed value for random number generator
  --slack_redist_iter arg   number of iterations between slack redistribution
  --cstrweight arg          placer weighting for relative constraint
  --pack-only               pack design only without placement or routing
  --ignore-loops            ignore combinational loops in timing analysis
  -V [ --version ]          show version
  --test                    check architecture database integrity
  --freq arg                set target frequency for design in MHz
  --no-tmdriv               disable timing-driven placement
  --save arg                project file to write
  --load arg                project file to read

Architecture specific options:
  --25k                     set device type to LFE5U-25F
  --45k                     set device type to LFE5U-45F
  --85k                     set device type to LFE5U-85F
  --um-25k                  set device type to LFE5UM-25F
  --um-45k                  set device type to LFE5UM-45F
  --um-85k                  set device type to LFE5UM-85F
  --um5g-25k                set device type to LFE5UM5G-25F
  --um5g-45k                set device type to LFE5UM5G-45F
  --um5g-85k                set device type to LFE5UM5G-85F
  --package arg             select device package (defaults to CABGA381)
  --speed arg               select device speedgrade (6, 7 or 8)
  --basecfg arg             base chip configuration in Trellis text format
  --override-basecfg arg    base chip configuration in Trellis text format
  --textcfg arg             textual configuration in Trellis format to write
  --lpf arg                 LPF pin constraint file(s)
@daveshah1 to the rescue once again!
pass --25k to nextpnr, remove the basecfg if your nextpnr is yesterday or today's build otherwise point it to a 25k basecfg, and then pass --idcode 0x21111043 to ecppack
-- @daveshah1

I'm not sure I would have ever guessed the 25F, and certainly not the --idcode 0x21111043 part. I pulled the latest SymbiFlow/prjtrellis and nextpnr now (git sha1 4c73061), cleaned and rebuilt everything with my new Makefile, and success to build!
[... snip ..]
Info: [ 79471,  79958) |
Info: [ 79958,  80445) |***
Info: [ 80445,  80932) |*
Info: [ 80932,  81419) |***
Info: [ 81419,  81906) |********
337 warnings, 0 errors
ecppack ulx3s_out.config ulx3s.bit --idcode 0x21111043
Now recall I am doing this all in WSL Ubuntu, so there are no native USB devices - thus I am forced to use the Windows version of ujprog to actually upload the code to the ULX3S board.

The amazing thing here - is that Windows applications can be run from within WSL!

This seems to completely circumvent the "No Native USB Devices" in WSL. For example, here's the same app compiled for linux, not finding the JTAG device:

I'm using the very latest FTDI code in my fork of the f32c tools (I created this PR #9). I still encountered some problems with the syntax of the ujprog. I opened issue #10, which was promptly closed - regarding the expected operation. Bottom line is this is the syntax that works:
C:\workspace-git\f32c_tools\ujprog>ujprog ulx3s.bit
ULX2S / ULX3S JTAG programmer v 3.0.92 (built Feb 13 2019 12:27:20)
Using USB cable: ULX3S FPGA 12K v3.0.3
Programming: 100%
Completed in 18.66 seconds.
And I learned a few more things in the close comment:
JTAG doesn't work in COM mode, so no wonder that uploading a bitstream can't work that way, especially not when using the -a modifier, which tells the ujprog to send the file as a stream of bytes.
-- gornjas
From gitter:
ujprog note: supported targets are either -j sram or -j flash
-- @emard
So, ya - I get that all this stuff is obvious to the developers & subject matter experts; However, I struggled to learn each of these things when seeing it all for the first time and relatively little documentation. Although there's no README in the f32c/tools/ujprog, I created one in my fork that hopefully will help others.

As mentioned above: in order to program the ESP32, the FPGA needs to be configured in "Pass-Through" mode. @emard's ulx3s-passthru is written in VHDL.

There's a tool called vhdl2vl that can convert some VHDL to Verilog. Yosys has a VDHL reader plugin based on vhdl2vl.
git clone
cd yosys-plugins/vhdl

# mkdir -p /usr/local/share/yosys/plugins
# cp /usr/local/share/yosys/plugins/
sudo make install
then run yosys, and from the yosys> prompt:
plugin  -i vhdl
plugin  -l
This will add a new read_vhdl command to yosys.

There is more documentation on yosys here.

If the plugin is listed and working, typing read_vhdl in yosys will show help:
1. Executing VHDL frontend.

Syntax error in command `read_vhdl':

    read_vhdl [options] [filename]

Load modules from a VHDL file to the current design.

        dump abstract syntax tree (before simplification)

        dump abstract syntax tree (after simplification)

        do not include hex memory addresses in dump (easier to diff dumps)

        dump ast as VHDL code (after simplification)

        enable parser debug output

        usually latches are synthesized into logic loops
        this option prohibits this and sets the output to 'x'
        in what would be the latches hold condition

        this behavior can also be achieved by setting the
        'nolatches' attribute on the respective module or
        always block.

        under certain conditions memories are converted to registers
        early during simplification to ensure correct handling of
        complex corner cases. this option disables this behavior.

        this can also be achieved by setting the 'nomem2reg'
        attribute on the respective module or register.

        This is potentially dangerous. Usually the front-end has good
        reasons for converting an array to a list of registers.
        Prohibiting this step will likely result in incorrect synthesis

        always convert memories to registers. this can also be
        achieved by setting the 'mem2reg' attribute on the respective
        module or register.

        do not infer $meminit cells and instead convert initialized
        memories to registers directly in the front-end.

        dump VHDL code after pre-processor

        do not run the pre-processor

        disable DPI-C support

        only create empty blackbox modules. This implies -DBLACKBOX.

        don't perform basic optimizations (such as const folding) in the
        high-level front-end.

        interpret cell types starting with '$' as internal cell types

        ignore re-definitions of modules. (the default behavior is to
        create an error message if the existing module is not a black box
        module, and overwrite the existing module otherwise.)

        overwrite existing modules with the same name

        only read the abstract syntax tree and defer actual compilation
        to a later 'hierarchy' command. Useful in cases where the default
        parameters of modules yield invalid or not synthesizable code.

        make the default of `default_nettype be "none" instead of "wire".

    -setattr <attribute_name>
        set the specified attribute (to the value 1) on all loaded modules

        define the preprocessor symbol 'name' and set its optional value

        add 'dir' to the directories which are used when searching include

The command 'vhdl_defaults' can be used to register default options for
subsequent calls to 'read_vhdl'.

Note that the VHDL frontend does a pretty good job of processing valid
VHDL input, but has not very good error reporting. It generally is
recommended to use a simulator for checking the syntax of the code, rather
than to rely on read_vhdl for that.
The yosys VHDL plugin reads a VDHL file like this:
yosys> read_vhdl ulx3s_v20_passthru_wifi.vhd

3. Executing VHDL frontend.
Parsing VHDL input from `ulx3s_v20_passthru_wifi.vhd' to AST representation.
ERROR: NOT IMPLEMENTED: ulx3s_v20_passthru_wifi.vhd:16 (vhdl_parser.y:1583)
There is probably a different message if it was successful.

There's also the vhd2vl noted on the yosys README.
git clone
cd vhd2vl/src
./vhd2vl ../../ulx3s-passthru/rtl/ulx3s_v20_passthru_wifi.vhd
With a bit of help info:
./vhd2vl --help
Usage: vhd2vl [--debug] [--quiet] [--std 1995|2001] source_file.vhd > target_file.v
   or  vhd2vl [--debug] [--quiet] [--std 1995|2001] source_file.vhd target_file.v
That also had limited success:

// File ../../ulx3s-passthru/rtl/ulx3s_v20_passthru_wifi.vhd translated with vhd2vl v3.0 VHDL to Verilog RTL translator
// vhd2vl settings:
//  * Verilog Module Declaration Style: 2001

// vhd2vl is Free (libre) Software:
//   Copyright (C) 2001 Vincenzo Liguori - Ocean Logic Pty Ltd
//   Modifications Copyright (C) 2006 Mark Gonzales - PMC Sierra Inc
//   Modifications (C) 2010 Shankar Giri
//   Modifications Copyright (C) 2002-2017 Larry Doolittle
//   Modifications (C) 2017 Rodrigo A. Melo
//   vhd2vl comes with ABSOLUTELY NO WARRANTY.  Always check the resulting
//   Verilog for correctness, ideally with a formal verification tool.
//   You are welcome to redistribute vhd2vl under certain conditions.
//   See the license (GPLv2) file included with the source for details.

// The result of translation follows.  Its copyright status should be
// considered unchanged from the original VHDL.

WARNING (line 70): port default initialization ignored.
WARNING (line 70): port default initialization ignored.
WARNING (line 70): port default initialization ignored.
WARNING (line 70): port default initialization ignored.
WARNING (line 70): port default initialization ignored.
syntax error, unexpected GENERATE at "generate" in line 145.

So the reality is that I probably won't be converting VHDL to Verilog anytime soon. However, writing my own Verilog passthrough app should be an excellent beginner FPGA to write.

No comments:

Post a Comment

comments are welcome, but I prefer not to allow links to promotions or other unrelated services.

Find gojimmypi at

I'm currently working on my new blog home at After implementing a variety of features such as dark mode , syntax hi...