Day one experience with Intel SoC-FPGA device

I got myself a Terasic DE10-Nano board. I love it and I hate it. I'm super excited and completely tired. "Why?" you might ask. While Terasic is just a Taiwanese hardware producer, nothig to be worried, the SoC that are included on their boards are designed by Altera (currently Intel, as Altera was bought by Intel in late December 2015). This means we are getting into proprietary software territory, most all of resources are available in Intel's knowledge base and pretty much nowhere else, well... with one tiny exception, which, as it will turn out later, is a godsend. Intel's knowledge base itself is fine, you can easily find what you are looking for (including dozens of revisions of the same document), as long as you look for it on your own, because all of the knowledge base links lead to the main page somehow. So what's the problem with Intel? As a beginner you are basically forced by manuals to install gigabytes of proprietary bloatware, including software you don't have a license for and software you don't plan to ever use. However, the fun begins way before you get to that.

Oh by the way, I picked this board mostly because after I'm done toying with it, it's gonna be repurposed to run MiSTer FPGA software. For those who don't know, that's a huge open source project focused on recreating retro computers, game consoles, arcade machines and some other classic projects, like Game of Life. There's dozens of retro hardware recreated using Altera's Cyclone V capabilities. The project is using DE10-Nano as a base, as you've probably guessed already. Check out their wiki if you want to learn more!

First steps

As soon as I got the board and some extra cables to my ever growing collection out of the box, I plugged it into the wall and thankfully, it was working fine. The excitement started to arise, as at this point I was sure, this little baby is staying here with me and I can bid the store, that I got it from, farewell. Next step - plugging the UART connection to the PC, as according to starting guide, there were some demos waiting for me on the filesystem. Aaaaaand, they were. However, I had to access it using Ethernet-over-USB. The drivers were there, right next to instructions that were present on the little storage device that the board has presented as.

One quick driver installation later, I still had... wait... I still had an unknown device present in the Device Manager. Another quick driver installation later, I noticed it was not successful. Short trivia insert: the drivers (and the board itself) is dating back to March 2017, Windows 10 was exactly 2 years old at that time. As it turned out after some googling, Windows 10 has a very nice feature regarding driver installation - they have to be signed by the vendor, so that you (well, system) can verify it wasn't tampered with. That's honestly, a very good security feature, unfortunately, Windows isn't kind enough to tell you where the problem is, it just fails to install unsigned drivers. So, what's the solution? Reboot the system with driver signature checking temporarily disabled. I can't find any details on when the enforced driver signature checking was introduced, so I'm gonna believe it was there since the beginning - March 2015. Good job on preparing the drivers, Terasic! The RNDIS driver that is supplied by vendor is from XP/Vista era. At least it still works.

Ok, I've finally got the access to the board's HPS (hard processor system) shell! And self-hosted website. Yep, they even made a website to showcase some more maths-related demos. In the preinstalled filesystem there are some basic examples utilizing some on-board I/O (both connected directly to HPS and FPGA) and the FPGA itself, showing how blazingly fast it can be in simple tasks compared to your good old ARM Cortex A9. The first impression is very good in overall. Imagination starts to sparkle up as you see how some basic stuff works in practice.

The SDK

Enough of the funsies, it's time to continue setting things up. Next up, the offishyal CD from the producer (aka the aforementioned godsend). I'm glad they at least realized barely anyone has a spinny whinney disc drive these days (I'm honestly surprised after the Vista drivers incident) and you need to get the CD dump from their website. After making a dummy account of course! By the way, it looks like the site might go down any day (either that or it will survive the apocalypse, you never know with those straight outta 90s websites), so I made a backup of the disc here. I'll make it public if it actually goes down, lol. So what's on the disc - everything. Literally everything: datasheets, more complex demos, manuals, schematics, even an FPGA project generator for this specific board. Thank you Terasic, very cool! Let's dig in!

Would you look at that fancy setup! It's from the manual.

Time to bring the bloatware to the table. The first pack - Intel SoC Embedded Design Suite - ain't that bad, most of the stuff looks like it can be useful in one way or another, especially hps interfaces and hwlib. However, you are expected to get Cygwin. That's very unconvenient for me (and possibly for many other people), because, well, I do have a pseudo-linux subsystem set up already. And it's not Cygwin. Fortunately, while the manuals might "force" you to do it their way, scripts themselves are ready for alternatives (probably by accident). In the end I've managed to set everything up using MSYS2 and I bet it's also possible to do with WSL, without using Cygwin at all. There is one thing that you need to get on your own - linaro arm gcc-based bare metal (eabi) toolchain along with newlib (which is yet another libc implementation). Heck, you even get to build newlib yourself. Not gonna lie, I was sure it ain't gonna go smoothly at all. I was in a "pirate" environment after all, god knows how deep the Cygwin dependencies went. Yet again, fortunately, there was only one minor issue on the way, I just had to throw the ancient version of GNU make that was included with the package away. Ok, so I got a bare metal toolchain along with libc to link with. I'm sure it will take months until I get to actually build anything using it. A linux toolchain would be hella useful though. Yeah... a linux toolchain.

Tough luck! There is no mention of any gnueabi toolchains anywhere in any Intel documentation :) I guess ARM must've payed up, because according to those docs and manuals, armcc and Arm Development Studio are the only way to develop and deploy linux applications on the board. That's bullshit though and anyone who knows anything about cross-compilation knows that. I find it especially offensive given the fact, that Arm DS is the only piece of software that requires a license. License that doesn't have to be included with each board kit and is hella expensive. They could've mentioned at least one word about alternatives...

Anyway, one quick toolchain download later (latest arm-linux-gnueabihf from Linaro) I managed to build a boy's proudest Hello World program. This is literally the most excited I've ever been running a Hello World. Even though there was no reason for it not to work, I expected to see "unsupported file format" (or something like this) message anyway. Things will probably get spicier once I get to actually utilize hwlib. It's provided as source files, not static libraries to link to, so it miiiight not be a problem, maybe. As long as it's valid code and the hardcoded memory addresses in header files did not change over the years, it's got to "just work". No expensive Arm's bloat required here.

Next step, FPGA suite. A whopping 1.6GB of download (and another 1.3GB of board specific stuff). Quartus Prime is what they call it. It's also a paid software, but there is a free version for home users like me. I chuckled a little when I noticed it wants to install itself to intelFPGA_lite instead of intelFPGA, but ok, let's pretend someone other than devs have all the versions installed, sure. Hang on a second... The shell script for SoC EDS environment setup is looking for Quartus Prime in "../quartus" location. If by default it wants to install to a different root dir... Oh my god. Oh. No. Good job, Intel! You just gave your support some extra work! I feel sorry for all those students and beginners that fell for this bait. Remember kids, always thoroughly read scripts that are modifying/setting environmental variables! Oh by the way, the whole Quartus Prime suite (with Cyclone V data) totals up to 10GB. :)

I checked the basic examples included with the disc and then I made my very first FPGA project thanks to the User Guide. Oh wait, there's one more thing... I had to reboot PC with driver signature checking disabled once again to install another driver. This time it was for JTAG connector. I cursed a little bit, done so and then finally got to checking out the examples and making my first project. I can say I understand it, but it's totally gonna get way too complicated to comprehend very soon. So I'm gonna focus on developing stuff for HPS for now and then slowly start to utilize FPGA in my projects. Apart from demos, the vendor's CD also included a gateway to my first project - Golden Hardware Reference Design utilizing AXI HPS to (and from) FPGA bridge. This is the point where I really appreciate the presence of manuals and user guides. Anyway, remaining demos will have to wait a bit as I finally get to write something on my own!

Damn I just realized, this article is focusing on stuff that has barely anything to do with what I was doing the most on the first day - which was after all, reading guides and getting through demos/examples. It's about time to shift focus from technical difficulties, arduous setup and complaints to actual FUN!

Hello breadboard!

I'd like to use pins from Arduino expansion header, any of GPIO headers would be fine, too, but I have plenty of male-male connection cables, so let's go with Arduino. Module definition will need some changes to throw in some extra pins. I mirrored the ones from project generated using supplied System Builder (it's quite easy though, just define them and that's it).


      module DE10_NANO_SoC_GHRD(

	 //////////// ARDUINO //////////
	 inout    [15: 0]	  ARDUINO_IO,
	 inout               ARDUINO_RESET_N,
      

Next up is booting up Platform Designer. It's upgrading the system files to Quartus version 20.1, back from 16.0, it's slowly getting scary. In fact, that might be a good moment to do a clean build with the whole project updated to 20.1, just to be sure it's still working. Few minutes later, to my personal surprise, it still works. Back to "designing" - from Library->Processors and Peripherals->Peripherals I'm adding another PIO, calling it arduino_pio. Clock and Reset inputs are taken from clk_0, then Avalon Memory Mapped Slave is from fpga_only_master and mm_bridge_0. Basically mirrored all others PIO configuration. Can't go wrong with that, am I right? Register's width is gonna be 16 bits long, with InOut direction. Here's how it looks like. And the address base might be set to 0, why not, it's free anyway.

If I'm correct and didn't make any mistakes, generating HDL should now be possible. A moment of dread later it turns I need WSL. One quick MSYS injection and path mangling later... it turns out I don't need WSL. ¯\_(ツ)_/¯ Code generation goes brrrr and after a coffee break it's probably ready for deployment. Few more things though, first of all, gotta export the pins to soc system and, well, assign them in Pin Planner. The latter is obvious and the needed code for the export is apparently this:


soc_system u0(
        ...

        .hps_0_hps_io_hps_io_gpio_inst_GPIO61(HPS_GSENSOR_INT),      //                               .hps_io_gpio_inst_GPIO61
        //FPGA Partion
        .arduino_pio_external_connection_in_port(ARDUINO_IO),        //arduino_pio_external_connection.in_port
        .arduino_pio_external_connection_out_port(ARDUINO_IO),       //                               .out_port
        .led_pio_external_connection_export(fpga_led_internal),      //    led_pio_external_connection.export

        ...
      

Pin assignment has got to be the most chill part of this whole process. Reference table on one screen, manual labor on the other. And what a beautiful visual feedback. Someday I will understand what I'm looking at, but it looks cool.

Yes, the reset pin is assigned, but currently unused, I believe I won't need that. It's time compile the whole design! I can see no warnings related to my changes, so I'm gonna assume it's going to actually work somehow. Moment of truth has arrived. Can I add some pins to the layout on my own or am I just wandering in the dark. Another dreadful moment arises, as I program the board. Thankfully, a heartbeat LED starts to signal everything is fine, or in other words, I didn't fry the board. Yet.

Alright let's build a basic system - the classic LED test. I just want to see if I actually have any control over the pins from Arduino expansion header, so I'll use the HPS-FPGA LED example as a codebase, just throw in powering up/down one of the pins. Here's what I'm gonna use for this system: a tiny lil' breadboard, some male-male cables, a 330 Ohm resistor and an LED light of course. I could do without the resistor, as power output is limited to 16mA, so there is around 210 Ohm resistence coming from the board itself, which, at 3.3V output, powers the red LED at around 5-8 mA*. That would be completely safe, but from what I remember, nowadays, even 1-2 mA is enough to power the LED light, hence 330 Ohm resistor.

Physics aside, this is how the setup looks like. Before assembly on the left and after assembly on the right. Let's say such simple system doesn't need any schematics.

As you can see, it actually works! Unfortunately, it doesn't play Goa Trance [yet ;)], but it does what the other L.E.D.LIGHT should do. And how do I know it isn't just a random luck? The first port of Arduino header (ARDUINO_IO[0]) is outputting high signal while the LED "strafe" sample program is running, and low after it's done. This concludes the very first of my own projects. Everything I'll need in the nearest future is set up. I have a design ready that forwards I/O from Arduino Uno R3 expansion header to the device's HPS (aka ARM system), I am able to build code for ARM bare metal, ARM linux, NIOS 2, and finally, I can successfully compile the FPGA design.

Now that all the hate I built up during the initial setup and configuration is gone, there is only place left for the love. Next up - a C++ framework/library for I/O interactions, I know embedded software development is all about speeeeeeeeeed (and being basically very smol), but I like my code clean and readable. And since gcc (GNU Compiler Collection, not to be mistaken with GNU C Compiler :D) includes g++, why the hell no. I ain't gonna be writing time critical code, and even if I do, I have plenty of unutilized FPGA logic to offload it to. A natural next step to accompany me along the coding and design process is gonna be a 16x2 LED Display. Shee you nexzd tahm!

Appendix: Re-shelling the shells you don't want to shell with.

First of all, you'll need whatever base-devel metapackage is called in your subsystem. In case of MSYS it's just msys/base-devel (yeah, I'm gonna use msys shell, not mingw64, as all the toolchains are provided externally anyway). Modification of shell launching scripts/env setups will require patient analysis of what you actually need and what you don't need. Without all the comments my Embedded_Command_Shell.bat looks like this:


@ set _SOCEDS_ROOT=%~dp0
@ set _SOCEDS_ROOT=%_SOCEDS_ROOT:\=/%

:run_soceds_command_shell

@ C:\msys64\msys2_shell.cmd -here "%_SOCEDS_ROOT%embedded_command_shell.sh" "%*"
      

Next up is embedded_comand_shell.sh. They did pretty good job sowing off Cygwin stuff, so the only change I needed is changing running command in place from using exec to running it in a bash shell:


if [ -n "$*" ]; then
    bash -c "$@"
else
      

Lastly for SoC EDS there is the env setup (done in env.sh). I commented out appending NIOS 2 to env as it has its own shell and has its own make which doesn't cooperate too nicely. Speaking of make this env also has its own one, so it's gotta go for the same reason (if you can't find it, it's one of the last prepended paths). Other than that, I added path to new linaro toolchain (the arm-linux-gnueabihf one - armcc replacement) and exported path to hwlib root, for easier access from CMake.

Then the same treatment has to be done for NIOS 2 EDS. Leaving and modifying exact same lines in Nios II Command Shell.bat. nios2_command_shell.sh required some more commenting out, as this time it's expecting WSL (hey, if you have it, and there's a chance you do, you don't have to do anything!), not Cygwin, so WSL checks natually have to go. WSLENV export has to be replaced with PATH:


# export WSLENV=QUARTUS_ROOTDIR/p:SOPC_KIT_NIOS2/p:PATH/p
export PATH=$QUARTUS_ROOTDIR:$SOPC_KIT_NIOS2:$PATH
      

And at last, the same change has to be done for running shell with command directly (exec -> bash -c). If you haven't figured it out yet, that's why msys shell is started with -here parameter and any dangling parameters are passed to it. There is one more thing you have to do outside of the scripts, in your msys root folder, create a mnt directory and create a symbolic link to drives (note the plural) with your temp folder and the whole intelFPGA suite, called c where c is the drive letter of course. It's necessary because makefiles generated by Quartus have paths prepared for WSL (/mnt/c/intelFPGA/ and so on)

That's it. You're good to go. It's all MSYS now. Similar steps can be done for any other subsystem (though I'm not sure if any other than Cygwin and WSL exist...). Oh, yeah, you could just use Linux like a sane person. Even VM would be fine. Actually, yeah, don't bother with this on Windows, it's only gonna make you suffer.