I unboxed the Terasic DE10-Nano! What a great bit of kit for a bargain price of $130! Nice going Terasic – you build some amazing stuff. I couldn’t buy the components for that price! Here it is, plugged into my development box.
The cables are (anti-clockwise from 11), 5V/2A power from the power brick, USB Blaster connection, Ethernet plugged direct into my dev box (no crossover or switch), FTDI UART console.
There are two images available from the Terasic site, an Ubuntu image with full LXDE GUI, or a console image. I started with the Ubuntu image, just because it had a great ControlPanel App to show linkage to the DE10.
I wanted to use the DE10 initially as a proven hardware platform for my CPC2.0. To do so, I’ll need to be able to upload an RBF image to the FPGA from the on-die ARM processor. I know this is possible from my time using the Terasic DE0-Nano-SOC with the Atlas Linux distribution. However, it proved to be quite tricky with this image.
The Linux Kernel provided in the LXDE image is very tightly bound to the FPGA image and there are multiple busses and dependencies in the image. If the FPGA is not initialized before the kernel boots, it will hang on boot. If you try to program a new image using the USB-Blaster while Linux is running, the kernel will hang mid-process.
I tried starting up and unbinding the drivers in the SYSFS:
#!/bin/bash echo -n "sopc@0:leds" > /sys/bus/platform/drivers/leds-gpio/unbind echo -n "sopc@0:fpgabridge@0" > /sys/bus/platform/drivers/altera_hps2fpga_bridge/unbind echo -n "sopc@0:fpgabridge@1" > /sys/bus/platform/drivers/altera_hps2fpga_bridge/unbind echo -n "sopc@0:fpgabridge@2" > /sys/bus/platform/drivers/altera_hps2fpga_bridge/unbind echo -n "sopc@0:fpgabridge@3" > /sys/bus/platform/drivers/altera_fpga2sdram_bridge/unbind
But this didn’t stop the kernel hanging. I booted the system in single user mode from the u-boot command line (using printenv and setenv commands on the mmcboot variable). Still no joy.
I learned a lot about what a device tree is, how to uncompile one, how to compile one, how u-boot works and how to reference the RBF, Device Tree Binary, and zImage kernel in u-boot.
I learned how to do user-space IO in Linux (yes, it’s possible without writing a kernel module). I also managed to connect my DE10-Nano to the internet without needing to wire into a switch or use a complicated USB WIFI dongle (with the associated driver issues). So let’s cover networking first.
Connecting the DE10-Nano to the internet
Why is this useful? Because you can use the APT software repositories to get the tools you need, such as sharutils (for uuencode). On the LXDE image, SSH is enabled by default, so as soon as you get the network set up, you can SSH in as root, no password required.
Add this to /etc/rc.local:
#!/bin/bash systemctl stop NetworkManager ifconfig eth0 10.0.0.2 route add default gw 10.0.0.1 echo "nameserver 18.104.22.168" > /etc/resolv.conf
You’ll need to add a firewall rule to forward the traffic to the internet through your PC (assuming you’re not using a wired connection, otherwise you’d just plug in the ethernet cable directly).
So on your host/gateway PC, issue these commands as root:
#!/bin/bash echo -n 1 > /proc/sys/net/ipv4/ip_forward /sbin/iptables -t nat -I POSTROUTING 1 -o wlp2s0 -j MASQUERADE /sbin/iptables -I FORWARD 1 -i enp4s0 -o wlp2s0 -m state --state RELATED,ESTABLISHED -j ACCEPT /sbin/iptables -I FORWARD 1 -i enp4s0 -o wlp2s0 -j ACCEPT ifconfig enp4s0 10.0.0.1
In my set-up, wlp2s0 is my wifi adapter and enp3s0 is my wired ethernet. The DE10-Nano is connected directly into the wired port, and no crossover is required. Traffic from the DE10 is routed to the PC, that in turn forwards this to the internet across it’s gateway connection.
There’s no clear guidance that I could find on the best way to do this. Obviously a kernel module is preferred for production code, but during development a module build would just be slow. There is something called UIO that uses the device tree binary to define the driver for an address as ‘uio-driver’. This makes a device available /dev/uioX where X is a number 0 upwards. This allows read/write on this device, including interrupt handling. I didn’t need anything near this complicated, so I looked for alternatives.
It seems mmap is the tool. Use this code to access the FPGA manager. The example code access the FPGA manager and issues a reset by toggling the nConfig pin on the FPGA. I’ll expand this code to upload an RBF file to program the FPGA. This will give what I need to manage fast uploads to the FPGA.
Device Tree Compiler and Booting
This is a complex area and one I’ll go into another time, but it’s worth recording just a few details of my research. If you’ve always wondered how Linux knows where to find the hardware on your board, without it being hard coded in the kernel or modules, then look no further than the device tree binary (DTB). This file is a compiled binary version of the source device tree source (DTS) file. This source file identifies what address the IO lives at and what driver is required to support it. This is critical for start-up items like the console. It’s possible to reverse engineer a DTB binary back to source, edit the source, and recompile back to a binary. Very useful for updating existing builds.
Install the device-tree-compiler package from Ubuntu/Fedora. This gives the dtc command. Issue this to reverse engineer the binary:
dtc -I dtb -O dts soc_system.dtb > soc_system.dts
You can edit the resulting DTS file and use the reverse to turn it back to a DTB.
dtc -I dts -O dtb soc_system.dts > soc_system.dtb
The device tree specification is stored at www.devicetree.org.
I’ll be looking to load the CPC2 RTL into the DE10-Nano in my next post.
Successful Linux Image?
So did I find a kernel not dependent upon the FPGA image? Yes, the console image provide on the Terasic site would allow independent updating of the kernel. I’ll use this one until I have more time to figure out the LXDE image.