Sunday, April 12, 2020

More on FOMU Circuit Python in WSL.


After the previous pains of getting Circuit Python to just build for the FOMU, it is time to move on to more interesting things.... actually putting it on the FOMU!

Back to my first notes on this topic, the dfu-util is needed. There are a variety of sources of this app. Unfortunately the only one I was able to get working was from the ad-spam-laden sourceforge (it's really a shame what that site has become). Fortunately there's a ULX3S dfu-util repo with a precompiled binary. No ads.  (If you, too are tired of so may ads, check out my pi-hole blog).

At this early stage of the game, there's really quite limited documentation available. One observation from the Makefile is that during the (by default relatively quiet) build of Circuit Python... the only thing happening there is just that: the RISC-V (more specifically VexRiscv) compile of "C" code into a binary DFU file. This means that the FOMU needs to have been pre-configured: make the FPGA a soft CPU.

Fortunately, I had already loaded foboot onto my FOMU. Specifically, I took the shortcut of using a pre-built version of hacker-updater-v2.0.3.dfu as noted here. To do something using with another FPGA, say... the ULX3S... clearly I'm going to have to get control of building that bitstream.

But for now, let's run with that. The FOMU is configured as a soft CPU, which we learned from the Circuit Python text file is a VexRisc. The "only" thing needed is some code for it to run: Circuit Python.

There's an apt-get install version of dfu-util for Ubuntu, but it does not work in WSL. It does not "see" the FOMU

gojimmypi:~
$ which dfu-util
/usr/bin/dfu-util
gojimmypi:~
$ sudo dfu-util -l
[sudo] password for gojimmypi:
dfu-util 0.9

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2016 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/

gojimmypi:~
There's another one that came with the FOMU Toolchain mentioned in my previous blog, but that one does not work either, giving instead this error message:
gojimmypi:~
$ /opt/fomu-toolchain-linux_x86_64-v1.5.6/bin/dfu-util -l
dfu-util 0.9

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2019 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/

unable to initialize libusb: LIBUSB_ERROR_OTHER
gojimmypi:~
Of course, I'm doing this in WSL (not WSL2).. and there are known problems with native USB drivers not working in WSL. Given the libusb error, I'm pretty sure neither of those linux versions of dfu-util will ever work. However, one of the amazing things about WSL: Native Windows and DOS apps can be launched from the command prompt! The DOS version does work!
WORKSPACE=/mnt/c/workspace
cd $WORKSPACE
git clone https://github.com/ulx3s/dfu-util.git
$WORKSPACE/dfu-util/bin-win64/dfu-util.exe -l
This should identify the FOMU installed:
dfu-util 0.9

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2016 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/

Found DFU: [1209:5bf0] ver=0101, devnum=12, cfg=1, intf=0, path="1-1.3", alt=0, name="Fomu Hacker running DFU Bootloader v2.0.3", serial="UNKNOWN"
Note that I have DFU Bootloader v2.0.3 installed on the FOMU as described in my prior blog. In particular, see tinyusb issues #293 for some oddities that I encountered when using older versions of the bootloader. See also  WSL Issue 2185 and 3745 regarding native USB drivers in WSL.

If you need to make:
make BOARD=fomu clean
make BOARD=fomu
The send the firmware to the device:
cd $WORKSPACE/circuitpython/ports/litex
$WORKSPACE/dfu-util/bin-win64/dfu-util.exe  -D ./build-fomu/firmware.dfu
Note that this is a "soft" program of the FPGA. The firmware.dfu can't be sent to the FOMU again until power cycle. In fact, after a power cycle, it *needs* to be sent again. Normally I might say something about preventing corruption to be careful to to the USB eject. But after removing the device, it is no longer a Circuit Python.

For reference, to put foboot in place:

WORKSPACE=/mnt/c/workspace
cd $WORKSPACE
mkdir -p fomu
cd fomu
wget https://github.com/im-tomu/foboot/blob/master/releases/v2.0.3/hacker-updater-v2.0.3.dfu

# for WSL, we need the DOS executable with usblib support:
$WORKSPACE/dfu-util/bin-win64/dfu-util.exe -D hacker-updater-v2.0.3.dfu

# for regular linux:
# dfu-util -D hacker-updater-v2.0.3.dfu

The thing is: we don't have a bootloader for the ULX3S. It does not show up as a device for dfu-util. Recall we use the ujprog to upload FPGA bitstreams to the FPGA, and the litex_term to upload the firwmware.bin file.

As noted in a prior blog, I have a soft CPU script that shows how this is done on the ULX3S:

WORKSPACE=/mnt/c/workspace

cd $WORKSPACE/litex-boards/litex_boards/targets/soc_basesoc_ulx3s/gateware

$WORKSPACE/ulx3s-examples/bin/ujprog.exe top.bit

cd $WORKSPACE/circuitpython/ports/litex/build-ulx3s

litex_term --serial-boot --kernel firmware.bin /dev/ttyS15

Unfortunately, I've spent a bunch of time chasing toolchain and memory problems. After updating everything to solve a problem where Migen was not creating a top.v file (which as it turns out, was even not necessary to solve the problem I was seeing), LiteX is now complaining that memtest is failing.

My specific memory chip on my ULX3S is a AS4C32M16SB. I did at least learn how to do a memory check:

git clone https://github.com/emard/ulx3s-bin.git
cd $WORKSPACE/ulx3s-bin/fpga/memtest/memtest-85k

# be sure to select the correct bit file 32MB or 64MB for your specific memory chip
$WORKSPACE/ulx3s-examples/bin/ujprog.exe ulx3s_85f_memtest_32MB_190MHz_300deg.bit 

 A good memory test looks like this on an HDMI monitor:


The top line is not used. The middle line is the number of memory tests completed. The red bottom line is the number of errors.

A bad memory test, say when a 64MB test is loaded for a 32MB chip, looks like this:\\


The 64MB test blinks onboard LEDs like this. The 32MB test does not.

In the meantime, there are some really quite talented folks that do this stuff rather easily. Perhaps I'll have more luck next time.

See also: Adafruit Circuit Python Issues 2604: Fomu port of CircuitPython



Saturday, April 11, 2020

Circuit Python on the FOMU in WSL (plus the pains of building your own RISC-V toolchain)


I started down the road of learning FPGA Circuit Python last month, with the (admittedly overly-ambitious) goal of getting Circuit Python working on the ULX3S. I was inspired to revisit this today when I saw Adafruit Blog - CircuitPython 5.2.0 Released - that specifically mentioned LiteX for Circuit Python!! In this case, for the FOMU.

TL;DR - WSL is by default NOT Case Sensitive. Use fsutil.exe in a DOS admin window to change this for each directory. To compile, riscv64-unknown-elf-gcc is needed, but the RISC-V GitHub GNU Repo does not work here to build Circuit Python. Use the FOMU toolchain. The make parameter should be lower case: make BOARD=fomu

If you are interested in Circuit Python, check out this CircuitBrains Deluxe on Crowd Supply.

See Adafruit instructions on building Circuit Python. All of the downloads for Circuit Python are available online. Well, all but of course the FOMU that I am most interested in. Hopefully it will be there soon, so for now we are doing this the hard way. (no fun if it is too easy, right?)

I could not find any documentation on the FOMU build, so I had to dig and search for keywords. The initial part of fetching from GitHub is pretty standard, so we'll start there:

git clone https://github.com/adafruit/circuitpython.git
cd circuitpython
git submodule sync
git submodule update --init
make -C mpy-cross
When doing the git submodule update --init in DOS (I sometimes do that, since my git clones are in C:\workspace - even though I compile in WSL from /mnt/c/workspace/)... I saw these errors:
git submodule update --init
Submodule 'extmod/ulab' (https://github.com/v923z/micropython-ulab/) registered for path 'extmod/ulab'
...etc
Submodule path 'lib/nrfutil': checked out '9e7dfb28a5c6f3d7a19340971b32e0c2b4128ecf'
Submodule path 'lib/tinyusb': checked out '1f95f439e11f519e69d75a4a8b7b9f28eaf5060e'
error: unable to create file tests/decomp-bad-inputs/00/id:000000,sig:11,src:000000,op:flip1,pos:10: Invalid argument
...etc
error: unable to create file tests/decomp-bad-inputs/00/id:000012,sig:11,src:000000,op:arith8,pos:11,val:+6: Invalid argument
error: unable to create file tests/decomp-bad-inputs/00/id:000013,sig:11,src:000000,op:arith8,pos:12,val:-9: Invalid argument
error: unable to create file tests/decomp-bad-inputs/00/id:000014,sig:11,src:000000,op:havoc,rep:16: Invalid argument
error: unable to create file tests/decomp-bad-inputs/00/id:000015,sig:11,src:000000,op:havoc,rep:2: Invalid argument
... many, many more similar errors
When doing the git submodule update --init from WSL just a few moments later, I did not see those errors. Perhaps there's some issue with Windows file names. (edit: I learned later this is likely a case sensitivity issue, see below).

For the FOMU, there's a Makefile in ./ports/litex
cd ./ports/litex
make BOARD=fomu
Edit: note that a case insensitive directory setting may allow BOARD=FOMU to work. ymmv. The safest one to use is a lower-case: BOARD=fomu.

If you see an error like this:
xargs: riscv64-unknown-elf-gcc: No such file or directory
../../py/mkrules.mk:81: recipe for target 'build-FOMU/genhdr/qstr.i.last' failed
make: *** [build-FOMU/genhdr/qstr.i.last] Error 127
make: *** Deleting file 'build-FOMU/genhdr/qstr.i.last'
Then try riscv64-unknown-elf-gcc --version to see if this RISC-V compiler is installed (perhaps you need to install the compiler). sadly, this is a different version of the compiler than is used in my ULX3S toolchain builder, that uses rv32i installed to /opt/riscv32i. Of course it does. Sometimes I wonder if this is all a hard drive manufacturer conspiracy to use as much disk space as possible. ;)

If like me you don't have the right compiler installed, make sure you have plenty of disk space:
This process will start by downloading about 200 MiB of upstream sources, then will patch, build, and install the toolchain. If a local cache of the upstream sources exists in $(DISTDIR), it will be used; the default location is /var/cache/distfiles. Your computer will need about 8 GiB of disk space to complete the process.

Here's what I did in WSL:
sudo apt-get install autoconf automake autotools-dev curl python3 libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev
git clone --recursive https://github.com/riscv/riscv-gnu-toolchain  
cd riscv-gnu-toolchain  
./configure --prefix=/opt/riscv
sudo mkdir /opt/riscv
sudo chown $USER /opt/riscv
if [ "$(echo $PATH | grep  /opt/riscv/bin)" == "" ]; then export PATH=$PATH:/opt/riscv/bin; fi
make
You may wish to put the export PATH=$PATH:/opt/riscv/bin in your ./bash.rc file.

Do NOT run make linux if you want riscv64-unknown-elf-gcc files! The linux option creates riscv64-unknown-linux-gnu files, not elf!.

My first attempt at install failed. After hours of downloading and building, the errors started here (and yes, I also incorrectly specified linux):

mv -f /mnt/c/workspace/riscv-gnu-toolchain/build-glibc-linux-rv64imafdc-lp64d/elf/rtld-libc.aT /mnt/c/workspace/riscv-gnu-toolchain/build-glibc-linux-rv64imafdc-lp64d/elf/rtld-libc.a
make[4]: Leaving directory '/mnt/c/workspace/riscv-gnu-toolchain/riscv-glibc/elf'
riscv64-unknown-linux-gnu-gcc    -nostdlib -nostartfiles -r -o /mnt/c/workspace/riscv-gnu-toolchain/build-glibc-linux-rv64imafdc-lp64d/elf/librtld.os '-Wl,-(' /mnt/c/workspace/riscv-gnu-toolchain/build-glibc-linux-rv64imafdc-lp64d/elf/dl-allobjs.os /mnt/c/workspace/riscv-gnu-toolchain/build-glibc-linux-rv64imafdc-lp64d/elf/rtld-libc.a -lgcc '-Wl,-)' \
          -Wl,-Map,/mnt/c/workspace/riscv-gnu-toolchain/build-glibc-linux-rv64imafdc-lp64d/elf/librtld.os.map
riscv64-unknown-linux-gnu-gcc    -nostdlib -nostartfiles -shared -o /mnt/c/workspace/riscv-gnu-toolchain/build-glibc-linux-rv64imafdc-lp64d/elf/ld.so.new               \
          -Wl,-z,combreloc -Wl,-z,relro -Wl,--hash-style=both -Wl,-z,defs       \
          /mnt/c/workspace/riscv-gnu-toolchain/build-glibc-linux-rv64imafdc-lp64d/elf/librtld.os -Wl,--version-script=/mnt/c/workspace/riscv-gnu-toolchain/build-glibc-linux-rv64imafdc-lp64d/ld.map            \
          -Wl,-soname=ld-linux-riscv64-lp64d.so.1                       \
          -Wl,-defsym=_begin=0
/opt/riscv/lib/gcc/riscv64-unknown-linux-gnu/9.2.0/../../../../riscv64-unknown-linux-gnu/bin/ld: /mnt/c/workspace/riscv-gnu-toolchain/build-glibc-linux-rv64imafdc-lp64d/elf/librtld.os: in function `process_envvars':
/mnt/c/workspace/riscv-gnu-toolchain/riscv-glibc/elf/rtld.c:2484: undefined reference to `__GI___open64_nocancel'
/opt/riscv/lib/gcc/riscv64-unknown-linux-gnu/9.2.0/../../../../riscv64-unknown-linux-gnu/bin/ld: /mnt/c/workspace/riscv-gnu-toolchain/build-glibc-linux-rv64imafdc-lp64d/elf/librtld.os: in function `dl_main':
/mnt/c/workspace/riscv-gnu-toolchain/riscv-glibc/elf/rtld.c:1317: undefined reference to `__access'
/opt/riscv/lib/gcc/riscv64-unknown-linux-gnu/9.2.0/../../../../riscv64-unknown-linux-gnu/bin/ld: /mnt/c/workspace/riscv-gnu-toolchain/riscv-glibc/elf/rtld.c:2103: undefined reference to `__access'
/opt/riscv/lib/gcc/riscv64-unknown-linux-gnu/9.2.0/../../../../riscv64-unknown-linux-gnu/bin/ld: /mnt/c/workspace/riscv-gnu-toolchain/riscv-glibc/elf/rtld.c:1564: undefined reference to `__GI___close_nocancel'
and eventually culminated here:

/opt/riscv/lib/gcc/riscv64-unknown-linux-gnu/9.2.0/../../../../riscv64-unknown-linux-gnu/bin/ld: final link failed: bad value
collect2: error: ld returned 1 exit status
Makefile:496: recipe for target '/mnt/c/workspace/riscv-gnu-toolchain/build-glibc-linux-rv64imafdc-lp64d/elf/ld.so' failed
make[3]: *** [/mnt/c/workspace/riscv-gnu-toolchain/build-glibc-linux-rv64imafdc-lp64d/elf/ld.so] Error 1
make[3]: Leaving directory '/mnt/c/workspace/riscv-gnu-toolchain/riscv-glibc/elf'
Makefile:258: recipe for target 'elf/subdir_lib' failed
make[2]: *** [elf/subdir_lib] Error 2
make[2]: Leaving directory '/mnt/c/workspace/riscv-gnu-toolchain/riscv-glibc'
Makefile:9: recipe for target 'all' failed
make[1]: *** [all] Error 2
make[1]: Leaving directory '/mnt/c/workspace/riscv-gnu-toolchain/build-glibc-linux-rv64imafdc-lp64d'
Makefile:234: recipe for target 'stamps/build-glibc-linux-rv64imafdc-lp64d' failed
make: *** [stamps/build-glibc-linux-rv64imafdc-lp64d] Error 2
I was about to open a GitHub issue, and then thought - hm... perhaps I just read the rest of the README, eh? I know, I know, actually RTFM'ing takes away all the fun, but it is rather embarrassing when someone tells you to read the fine manual in response to asking for help. Sure enough, I found this:
If building a linux toolchain on a MacOS system, or on a Windows system using the Linux subsystem or cygwin, you must ensure that the filesystem is case-sensitive. A build on a case-insensitive filesystem will fail when building glibc because *.os and *.oS files will clobber each other during the build eventually resulting in confusing link errors. @tnt aka smunaut has a RISC-V USB project in the works.
In order to install RISC-V for WSL I found this article that noted after Windows 10 version 17093 there's a command fsutil.exe for setting case sensitivity:

fsutil.exe file setCaseSensitiveInfo riscv-gnu-toolchain enable

Note this needs to be run in a DOS Window, not WSL, and with Administrative permissions:


BUT! Note the command is only for the specified directory, and the attribute is NOT inherited  (and certainly does not get applied to each existing sub-directory). So we need to iterate through all sub-directories individually. Yes, I'm sure many readers are rolling their eyes at this point, asking why I am doing this in WSL and not a "real" Linux machine or even a VM; what am I doing in Windows? I ask myself that sometimes, too. Well, I just think WSL is just too cool! Plus, I suppose that I like the challenge. So anyway, here's what I did to set case sensitivity:

cd \workspace\riscv-gnu-toolchain\
for /f %f in ('dir /ad /s /b') do fsutil.exe file setCaseSensitiveInfo %f enable
There are many directories. Be patient...

As a reminder, that command is meant to be executed interactively. If placed into a batch file, the percent symbols need to be doubled up.

Now that everything in the riscv-gnu-toolchain directory is case sensitive, I ran make again:

cd /mnt/c/workspace/riscv-gnu-toolchain
make
This does a full rebuild, taking again a very long time. Did I mention being patient?

If you can run /opt/riscv/bin/riscv64-unknown-elf-gcc --version but see a lot of errors when doing a make BOARD=fomu like:
/opt/riscv/lib/gcc/riscv64-unknown-elf/9.2.0/include/stdint.h:9:16: fatal error: stdint.h: No such file or directory
That means the RISC-V make either failed... or you didn't wait until it finished. (for me, it was the latter; did I mention being patient?) Once the RISC-V make is complete, the FOMU code can be built:
cd /mnt/c/workspace/circuitpython/ports/litex
make BOARD=FOMU

Which resulted in this output:

$ make BOARD=FOMU
Use make V=1, make V=2 or set BUILD_VERBOSE similarly in your environment to increase build verbosity.
QSTR updated
make: msgfmt: Command not found
../../py/py.mk:338: recipe for target 'build-FOMU/genhdr/en_US.mo' failed
make: *** [build-FOMU/genhdr/en_US.mo] Error 127

so ok, a quick google yields something useful on stackoverflow

apt-get install gettext

and so even after than, I saw even more errors like this:
/opt/riscv/lib/gcc/riscv64-unknown-elf/9.2.0/../../../../riscv64-unknown-elf/bin/ld: build-FOMU/lib/libm/math.o: in function `floorf':
/mnt/c/workspace/circuitpython/ports/litex/../../lib/libm/math.c:790: undefined reference to `__addsf3'
/opt/riscv/lib/gcc/riscv64-unknown-elf/9.2.0/../../../../riscv64-unknown-elf/bin/ld: /mnt/c/workspace/circuitpython/ports/litex/../../lib/libm/math.c:795: undefined reference to `__addsf3'
/opt/riscv/lib/gcc/riscv64-unknown-elf/9.2.0/../../../../riscv64-unknown-elf/bin/ld: build-FOMU/lib/libm/math.o:/mnt/c/workspace/circuitpython/ports/litex/../../lib/libm/math.c:817: more undefined references to `__addsf3' follow
/opt/riscv/lib/gcc/riscv64-unknown-elf/9.2.0/../../../../riscv64-unknown-elf/bin/ld: build-FOMU/firmware.elf(.text): relocation ".L15+0x0 (type R_RISCV_RVC_JUMP)" goes out of range
/opt/riscv/lib/gcc/riscv64-unknown-elf/9.2.0/../../../../riscv64-unknown-elf/bin/ld: /opt/riscv/lib/gcc/riscv64-unknown-elf/9.2.0/libgcc.a(divsf3.o): file class ELFCLASS64 incompatible with ELFCLASS32
/opt/riscv/lib/gcc/riscv64-unknown-elf/9.2.0/../../../../riscv64-unknown-elf/bin/ld: final link failed: file in wrong format
collect2: error: ld returned 1 exit status
Makefile:177: recipe for target 'build-FOMU/firmware.elf' failed
make: *** [build-FOMU/firmware.elf] Error 1
So I did another one of these, this time for the circuitpython directory:

for /f %f in ('dir /ad /s /b') do fsutil.exe file setCaseSensitiveInfo %f enable

and things got even worse:
$ make BOARD=FOMU
Makefile:30: *** Invalid BOARD specified.  Stop.
So I tried to clean it, same error:
$ make BOARD=FOMU clean
Makefile:30: *** Invalid BOARD specified.  Stop.
Turns out that was case sensitive, so then it is:
make BOARD=fomu clean
make BOARD=fomu
Ok, at the point, readers must really, really wonder why I use Windows. I know, I know. But I'm persistent.

It still failed. I gave up and asked for help.
I didn't get an answer to "should I use the FOMU toolchain instead of the RISC-V, so I thought I'd try it:

# fetch and extract
wget https://github.com/im-tomu/fomu-toolchain/releases/download/v1.5.6/fomu-toolchain-linux_x86_64-v1.5.6.tar.gz 
tar -xzf fomu-toolchain-linux_x86_64-v1.5.6.tar.gz
sudo mv fomu-toolchain-linux_x86_64-v1.5.6 /opt/fomu-toolchain-linux_x86_64-v1.5.6

# set the path
if [ "$(echo $PATH | grep fomu-toolchain-linux_x86_64-v1.5.6)" == "" ]; then export PATH=$PATH:/opt/fomu-toolchain-linux_x86_64-v1.5.6/bin; fi

# verify it is in the path:
which riscv64-unknown-elf-gcc
riscv64-unknown-elf-gcc --version

cd /mnt/c/workspace/circuitpython/ports/litex
make BOARD=fomu  clean

And... TADA! A successful Circuit Python build for the FPGA FOMU!! Whew.

gojimmypi:/mnt/c/workspace/circuitpython/ports/litex
$ make BOARD=fomu
Use make V=1, make V=2 or set BUILD_VERBOSE similarly in your environment to increase build verbosity.
/opt/fomu-toolchain-linux_x86_64-v1.5.6/bin/../lib/gcc/riscv64-unknown-elf/8.3.0/../../../../riscv64-unknown-elf/bin/ld: warning: section `.bss' type changed to PROGBITS

739752 bytes free in flash firmware space out of 1048576 bytes (1024.0kB).
116920 bytes free in ram for stack and heap out of 131072 bytes (128.0kB).

Create build-fomu/firmware.dfu
python3 ../../tools/dfu.py -b build-fomu/firmware.bin -D 0x1209:0x5bf0 "build-fomu/firmware.dfu"

Other notes (thanks @pdp7): Using Python for creating hardware

Migen Step by Step Tutorial

LiteX is a fork of MiSoC

MiSoc and LiteX are both based on Migen


Find gojimmypi at gojimmypi.github.io

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