Hello World — In Three Days!

Hello world is one of the simplest programs you can write. Your language may vary, but Hello World is a “one liner” where the computer says “Hello world”. I first did it in ZX Basic. You might have done it in C or Python first.

10 PRINT "HELLO WORLD"

How can this take three days to get fully debugged and working?

Hello World isn’t only your first program (at home, high school, or wherever you started), it is probably the first program you will run on any new IDE, compiler or language you encounter. In the embedded world, it might even blink an LED rather than print to a screen, depending on what resources you have.

My adventure ultimately had the same C code running on a Windows PC, a Linux PC, two different embedded Linux systems, and CI (Continuous Integration) automated testing.

The code was written in an IDE called Devin. The compilers used were GCC and AMD Vitis. The Devin + GCC toolchain had to be installed for my new-ish PC. There were options of using the msys64 or Mingw64 variants of the GNU toolset. I also had to install a make tool. Vitis, an IDE with cross compiler, was installed on a virgin Ubuntu VM and a Win 11 VM.

To add to the mix, I wanted my Hello World output to be more informative; I added a print of OS name and time to the output.

The Windows PC

Microsoft’s Visual Studio Code (VSCode) has been my go-to IDE for several years now. It’s a good platform, with hooks into source control systems, integration to Python and C running on x64 architectures. With GitHub integration came Co-Pilot as an AI coding assistant. Later we used a Claude plug-in as our AI coding agent.

VSCode is also available for macOS and Linux, so I used it everywhere.

Recently we started evaluating and using Windsurf, which has now become Devin Desktop; this is a fork of VSCode, and brings agentic AI into the workflow. As a derivative of VSCode, there is a different marketplace for plugins and extensions, carrying a subset of the VSCode extensions, and some are forks, rather than the flavour I know and trust.

The first day involved installing the tools and then trying to match my old PC’s VSCode extensions with the Devin equivalents, and getting a build that would run from the command line. The default terminal in Devin is Windows PowerShell, but GitBash is another command line option.

Spanning the two terminal shells was my first big time-sink. GitBash I normally associate with Mingw64 tooling. The PowerShell environment wanted to use msys64. What I really wanted was the Microsoft C/C++ extensions that VSCode has, but which are still absent from Devin.

Long story short — I settled on using GitBash and msys64. But still, half a day, easily, to troubleshoot and set up.

One thing that slowed me down was Devin’s AI trying to help me. It was trying to run the tools and compile my code from PowerShell, and was getting stuck.

I ended up building and running my code by launching GitBash outside Devin and working much the way I would under Linux.

Moving to Ubuntu

Since I had a new VM, I was at a clean slate. I.T. had installed Git for me, but nothing much else.

I didn’t even install Devin at this point. I just made sure my Hello World and supporting Makefile were in a repo I could clone to the new machine.

GCC and cmake were quickly brought up, and the OS dependency hooks showed we were correctly identifying a Linux build.

I got Devin installed, but was only using it as an editor and AI agent. All the build and run I was doing from the command line.

Though the work was trivial, it still took some time to physically do the job.

Vitis on Linux

This is one of those tools that takes hours to download and install. You may be wondering why — a C compiler and code editor could be downloaded in a couple of minutes.

I’m not just in need of a cross compiler to an AMD SoC with an ARM core; I’m actually in need of a fuller toolchain, as this SoC has an FPGA embedded. The toolset necessarily is much larger.

After a few minutes setting up the installer, you start the process and go get lunch. It will take hours.

I came back to find the installer at the licensing tool. I set that up, thinking I was pretty much done. Twenty minutes later I looked to see where we’d got to, only to find the installer had hung.

I had let the machine sleep, and my remote desktop had disconnected. I thought I’d broken something. I had to get out of the hung installer, so I hit Cancel.

Cancel was the only option, and the wrong one. Cancelling the installation causes a very swift clean-up process — deleting everything that had been installed.

I had to repeat the previous three-hour install. This time making sure I had every option selected perfectly.

It hung at the same point again. So I did some Googling, and polled the AI tools for suggestions; I also managed to find the error message at the end of the install log: libncurses was missing.

I went back and double-checked the pre-requisites for the installer; this library had not been called out. Without libncurses, the installer was failing to write out a log of the installation — and silently hanging rather than telling you.

There is good news here. A quick apt install libncurses5 and I could spawn the last step of the installer again, picking up where it left off.

I recently blogged about a tool so obvious that “everyone already has that” — something we forget is not part of the OS and may not be there. This was the same lesson wearing different clothes.

Almost a day lost to tools, again.

Compiling for the AMD Target

Running Hello World on Linux is trivial; the libraries are all bundled together for GCC. If you have ever used GCC as a cross compiler to your embedded controller, you will know that you have to pick the right GCC tool — this brings CPU architecture into the compile. You will also probably have used a HAL to help you access UARTs to provide a console interface, and allow printf to display your greeting.

For an embedded Linux cross compile you need the equivalent. For a Vitis Linux app, you need to create a platform project and then an application project. You can do this in the GUI IDE they provide, which is fine for local builds and day-to-day development. I wanted the CLI workflow, which relies on scripting.

Older versions of Vitis used TCL-based scripts for this, but the last few years have seen a shift to Python-based build scripts. Python is not my strongest language, and the command set (or API) for driving a Vitis build is new to me. This is the age of AI-assisted coding, so I leant on Devin’s SWE model to help me.

The SWE model managed to get the script written, but the compiler reported success while producing no .elf file. I spent some time making sure build output was going to directories we both had write access to. I also let the agent fix the script several times.

During this debug, the agent started to spin — getting into a loop where it was going to close out the problem before coming back to me. Code, compile, check, repeat. It kept failing. After about ten minutes of this, I stopped it, switched model to Claude Opus, and tried again.

Claude Opus is a premium model, costing more per session, but in this case it came back almost immediately with: the file is there, but we were looking in the wrong place. I found the .elf, used SCP (secure copy over SSH) to move it onto the target, and it ran first time.

I then let Claude clean up the scripts — the SWE agent had done the usual: adding band-aids where they weren’t needed, and not removing the failed attempts underneath. About two hours, all told, for what turned out to be a search-path problem.

The Other AMD Device

I have an outgoing and an incoming AMD-based platform, and I want to share code between them going forward. If I tweak something in a common library for the new platform, I want to check it still works on the old. So I actually have two versions of embedded Linux, targeting two different ARM core types.

There are ways to structure a repo for this, and ways to drive Vitis command-line builds so you can swap targets — and if necessary, which Vitis compiler version you use. I had to find a meaningful way to manage several SYSROOT and XSA sources, building for both legacy and current.

I also know the codebase will eventually need to build for at least three concurrent targets: deployed, SW integration, and HW development. Before I could declare Hello World done, I had at least one more target to build for, and one more layer of complexity to add to the Vitis build scripting.

With some small JSON files to hold configuration, and a few extensions to the Python, I got that last cross compile running. Another two or three hours, with AI coding following my floor plan.

CI as Another Valid Target

As you can see, this is not really about writing a Hello World program. It’s about creating the foundations for a new project’s build and test environment.

Automated testing is as critical as anything else. I wanted to make sure I had a test runner that would work as a GitHub Action.

I had ci.yaml from another project that would run unit tests. I just had to clean it up and port it to the new repo. A couple of pushes and pulls later, Hello World was green in CI. Compared to the earlier steps, a casual afternoons work.

Documenting

I had been documenting as I went, but notes that evolve tend to have a journal format. I needed to turn that around into an orderly set of how-to pages.

To give you an idea of the scope: this blog post took 90 minutes of initial text entry — fixing typos as I went, but no real editing. Stream of consciousness. For informal storytelling, that works fine. Cleaning it up properly will take a few more hours.

For the READMEs, I let Claude do the first pass, then proof-read. The final Git PR was 10 files and several hundred lines: Makefile, Vitis Python scripts, a JSON file creation tool, sample JSON, README.md, ci-tests.yaml.

Three Days Was Kinda Quick

Yes, three days is slow for getting to Hello World.

A simple Hello World in Python, including tools install, would probably take me under an hour. For an Arduino, likewise.

Using Atmel Studio or STM32CubeMX would take about a day or two, maybe three. After you install the tools and download the packages for your CPU, you would need to set up your BSP — at least configuring one UART for console output, and maybe even making sure your clock tree is properly configured. With an off-the-shelf dev board it would probably be quicker than for a full custom board.

I have seen it take several weeks get a toolchain up and running with everything working. For me, GCC on Windows took way too long, but the Vitis bring-up, with AI assistance, was a lot quicker than I expected. The TCL version took me several days to half-understand, and the GUI set up of a Vitis project can trip you up — I missed one step in the manual once and it gave me limping builds for a week.

Hello World is a very powerful tool that I use on most of my projects. I use it to learn the tools and prove the flows. Prove your tools before you write your novel code — that way you will only be debugging one thing at a time.


Leave a comment