2021-09-23
A few months ago I bought a couple of the ridiculously cheap DECA boards from Arrow – they’re sadly sold out now – but $37 bought you a MAX10 FPGA with 50,000 LEs, some DDR3 RAM, i2s audio, an HDMI port, USB and network ports, and a couple of GPIO headers. (It also bought you all the blue LEDs – I highly recommend not looking directly at the board when you power it up for the first time!)
I’m not the only one who’s been interested by this board – a bunch of MiST and MiSTer cores have already been ported to a DECA-based reference platform which involves a MiSTer-style SDRAM module, PS/2 keyboard, DB9 joystick and VGA video on the GPIO headers.
There is now an open-source DDR3 memory controller which has DECA as a main supported target.
And there is a project which caught my attention recently, which turns the DECA board into an external USB2 soundcard.
Yes, you read that correctly.
Naturally I wanted to try this out, so I cloned the repo on my own machine. Hmmm… there seems to be lots of Python involved. I’m not familiar with Python, but by now it’s a well-established, mature language with reliable, well-thought-out packaging and dependency management, right?
Finished laughing yet? Good – here’s exactly how smoothly it went:
(All snarkiness aside, this is a great project and I highly recommend checking it out – the roadblocks I encountered were mostly related to python – and probably my lack of familiarity with it – rather than the project itself. My intention here is partly to document and share the workarounds I needed to use, and – I’ll admit – partly to vent frustrations with the papercuts sustained along the way!)
[Edit: The author, Hans Baier, has made binaries available, so you can skip this entire setup-and-build process if you wish – see https://github.com/hansfbaier/deca-usb2-audio-interface/releases. Alternatively, if you want to laugh at me fumbling my way through getting the python environment set up – or you’ve just encountered the same hurdles I did and are looking for workarounds – read on!]
Since my last post, I’m “auditioning” Linux distros, and this week’s candidate is stock Ubuntu LTS – mainstream enough that I shouldn’t be too far off the road-well-trodden.
So inside in the repo is a gateware directory which contains the various parts of the project written in nmigen (which is Python based), and an “initialize-python-environment.sh” script. Well it’s a bad plan to run a random script from the internet without looking at it first, so…
!/bin/bash
python3 -m venv venv
. venv/bin/activate
pip3 install -r requirements.tx
OK that’s simple enough – let’s run it:
ERROR: Command errored out with exit status 1:
command: /home/amr/deca-usb2-audio-interface/gateware/venv/bin/python3 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-o1r7mv_x/usb-protocol/setup.py'"'"'; file='"'"'/tmp/pip-install-o1r7mv_x/usb-protocol/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(file);code=f.read().replace('"'"'\r\n'"'"',2'h0: (* src = "/home/amr/deca-usb2-audio-interface/gateware/venv/lib/python3.8/site-packages/nmigen_library/utils/timer.py:42" *) casez (1'h0) endcase
'"'"'\n'"'"');f.close();exec(compile(code, file, '"'"'exec'"'"'))' bdist_wheel -d /tmp/pip-wheel-_ekod9ko
cwd: /tmp/pip-install-o1r7mv_x/usb-protocol/
Complete output (32 lines):
WARNING: The wheel package is not available.
WARNING: The wheel package is not available.
WARNING: The wheel package is not available.
WARNING: The wheel package is not available.
/tmp/pip-install-o1r7mv_x/usb-protocol/.eggs/setuptools_scm-6.3.2-py3.8.egg/setuptools_scm/integration.py:16: RuntimeWarning:
ERROR: setuptools==44.0.0 is used in combination with setuptools_scm>=6.x
Your build configuration is incomplete and previously worked by accident!
This happens as setuptools is unable to replace itself when a activated build dependency
requires a more recent setuptools version
(it does not respect "setuptools>X" in setup_requires).
setuptools>=31 is required for setup.cfg metadata support
setuptools>=42 is required for pyproject.toml configuration support
Suggested workarounds if applicable:
preinstalling build dependencies like setuptools_scm before running setup.py
installing setuptools_scm using the system package manager to ensure consistency
migrating from the deprecated setup_requires mechanism to pep517/518
and using a pyproject.toml to declare build dependencies
which are reliably pre-installed before running the build tools
warnings.warn(
usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] …]
or: setup.py --help [cmd1 cmd2 …]
or: setup.py --help-commands
or: setup.py cmd --help
error: invalid command 'bdist_wheel'
ERROR: Failed building wheel for usb-protocol
Oh ${deity}, this brings back memories of tangling with GNU autotools in the mid 2000s. Googling the error message finds references mostly to Python 2.7 projects – so this problem, whatever it is, has existed for a while – but eventually I figured out that the problem here is that the script is using the systemwide setuptools provided by Ubuntu, and it’s too old. Once I realised that pip could download the latest version on demand, I installed it manually into the venv, like so:
python3 -m venv venv
. venv/bin/activate
pip3 install setuptools --upgrade
Problem solved? Well… no. When I run the next command from the script:
pip3 install -r requirements.txt
I get this:
ERROR: Command errored out with exit status 1:
command: /home/amr/deca-usb2-audio-interface/gateware/venv/bin/python3 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-vvse3nje/usb-protocol/setup.py'"'"'; file='"'"'/tmp/pip-install-vvse3nje/usb-protocol/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(file);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, file, '"'"'exec'"'"'))' bdist_wheel -d /tmp/pip-wheel-56xus5wz
cwd: /tmp/pip-install-vvse3nje/usb-protocol/
Complete output (10 lines):
WARNING: The wheel package is not available.
WARNING: The wheel package is not available.
WARNING: The wheel package is not available.
WARNING: The wheel package is not available.
usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] …]
or: setup.py --help [cmd1 cmd2 …]
or: setup.py --help-commands
or: setup.py cmd --help
error: invalid command 'bdist_wheel'
ERROR: Failed building wheel for usb-protocol
So I try again, manually installing wheel as well as setuptools, and…
The installation runs to completion! Time to celebrate? Well… no, not quite yet…
Rather than build the project directly to a binary, I wanted to use the nmigen scripts to create a project I could open in the Quartus IDE, so I could get a feel for what kind of code nmigen generates, and get an overview of how the project hangs together. That’s easy enough:
python3 deca_usb2_audio_interface.py --dry-run --keep-files
If all goes as it should, we now have a “build” directory, which contains a .qsf file and some verilog files. No .qpf though, which I’ll need in order to load the project into the Quartus IDE. Easy enough to create one, however – they’re really simple – the single line PROJECT_REVISION = “top” is sufficient. So I load this into Quartus Prime Lite 18 and have a go at building it:
Error (10170): Verilog HDL syntax error at top.v(3434) near text: "endcase"; expecting an operand.
Hmmmm…. here’s the construct it’s complaining about:
2'h0: (* src = "/home/amr/deca-usb2-audio-interface/gateware/venv/lib/python3.8/site-packages/nmigen_library/utils/timer.py:42" *)
casez (1'h0)
endcase
Hmmm – it seems to object to the empty case statement (maybe a newer version of Quartus will tolerate that? Or is it a bug in either the project or one of its dependencies? I don’t know at this stage.) It’s easily fixed with the judicial insertion of “default: ;” – and *then* the project builds, and works!
If I upload the bitstream into the FPGA and then connect the (non-USB-Blaster) USB port to the computer, dmesg tells me this:
[ 1688.006570] usb 1-2: new high-speed USB device number 3 using ehci-pci
[ 1688.163306] usb 1-2: New USB device found, idVendor=1209, idProduct=4711, bcdDevice= 0.01
[ 1688.163310] usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 1688.163312] usb 1-2: Product: DECAface
[ 1688.163315] usb 1-2: Manufacturer: OpenAudioGear
[ 1688.163317] usb 1-2: SerialNumber: 4711
[ 1688.338914] mc: Linux media interface: v0.10
[ 1688.382469] usbcore: registered new interface driver snd-usb-audio
… and sure enough, I can play audio through the DECA, as a USB soundcard!
Very impressive – all the more so for being only around 2,500 logic elements in size. And the main part of the nmigen-based project is under 500 lines of code. Nmigen, the luna USB project and other related projects are certainly things I want to explore in the near future.
Nice to see people interested in the project!
If you had contacted me beforehand, maybe
we could have ironed out some of the wrinkles.
I actually supply binary builds of the files in the releases section for convenience:
https://github.com/hansfbaier/deca-usb2-audio-interface/releases
I must admit most of the errors you mentioned do not show up on my machine.
For the USB core, I recommend using my fork of the LUNA core, because it has a lot of additions/fixes necessary for audio/MIDI and isochronous endpoints.
Also note there is nmigen-library, which is a great collection of useful cores for nmigen.
Thanks for commenting – and for pointing out the releases. I’d been curious about nmigen and related technologies for a while, so this was an ideal way to dip a toe in the water – that’s why I wanted to go through the whole process from start to finish.
You’re right, I should have got in touch when I hit roadblocks, but didn’t want to bother you if the problems were just due to my own Python-noobishness (which I think they were…)
Thanks for sharing the project – it’s very cool!
I use Quartus version 20.1.1 Build 720. With that it compiles without problems.