Bootstrapping a project for a new dev board


I’ve been following along with the AdaCore blog posts regarding how to get started with a new board and GNAT. I’ve used svd2ada, and I’m about to use startup_gen and then well, I’ll see what isn’t working and iterate until I start to understand anything.

I do wonder however, do I need startup_gen if the CMSIS pack for my target chip has startup code and linker scripts? Should I be using the things that come with the keil packs in general?

The pdsc file has what appears to be a lot of important information and the blog doesn’t really dip into anything like what to make of the sections etc.

The hardware I’m trying to get going with does have nuttx support and zephyr support and examples, so I do think I could also dig around in the various repos if I needed more info. I’ve not yet really done this sort of thing.

Sorry, the board is MR-CANHUBK344 | NXP MR-CANHUBK344: CAN-FD to T1 ETH. and is built with the S32K344 Arm Cortex-M7 lockstep cpu which is intended for general safety critical automotive use.
I’m using this hobbyist oriented board as a way to test working with Ada/Spark in a somewhat friendlier platform as an alternative to the Infineon Tricore chip we have been using otherwise.

Even if I manage to get an LED blinking… it feels like there is still a mountain to climb after. Is it common that folks tend to use the driver libraries that arm provides, or do they piece together support for things using Ada by porting things?


I feel an enormous sense of accomplishment, thanks to all the excellent community resources out there! :smiley:

(gdb) info frame
Stack level 0, frame at 0x20401008:
 pc = 0x4003d4 in sue_gateway (/home/chipc/workspaces/kvs/projects/sue/gateway_mrcanhub/src/sue_gateway.adb:1); saved pc = 0x4003e4
 source language ada.
 Arglist at 0x20401008, args:
 Locals at 0x20401008, Previous frame's sp is 0x20401008
(gdb) l
1       procedure Sue_Gateway is
2       begin
3          loop
4             null;
5          end loop;
6       end Sue_Gateway;
(gdb) n

Breakpoint 1, sue_gateway () at /home/chipc/workspaces/kvs/projects/sue/gateway_mrcanhub/src/sue_gateway.adb:1
1       procedure Sue_Gateway is

Now it’s time to start climbing the rest of the mountain! :mountain: :person_climbing:


There are basically two approaches to bringing up a new chip:

  1. Link against the vendor’s SDK/HAL/CMSIS library and trust that they wrote their C code in a reasonable manner.
  2. Read the datasheet and write drivers that poke at registers in Ada.

Arguments for option 1:

  • The vendor knows more about their chips than you do and can account for undocumented behavior
  • You might be able to get tech support if it doesn’t work the way you expect
  • Less code for you to write/maintain/debug

Arguments against option 1:

  • Eww, C
  • Can’t take advantage of Ada/SPARK verification tools
  • You have to figure out how to get gprbuild to either compile the vendor’s sources or link objects generated by their build system
  • You might be forced to use the vendor’s RTOS, which may require writing a new Ada runtime

Arguments for option 2:

  • You’ll actually know what the code does and can avoid things like recursion and unbounded stack/heap allocations
  • You can target SPARK verification if that’s important to you
  • Can implement whatever abstract interface you want, like the Ada_Drivers_Library HAL so that you can take advantage of existing drivers written in Ada

Arguments against option 2:

  • It’s a lot more work
  • You’re relying on the vendor’s datasheets and SVD definitions to be accurate
  • Unlikely to be supported by the vendor

No, but it can make some things easier. startup_gen is just going to generate a linker script and startup assembly from some templates. These are guaranteed to have all the right hooks for things like GNAT exceptions. However, your chip might need special initialization before passing control to the Ada runtime and startup_gen can’t account for that.

I find that it’s useful to run through the startup_gen process just to see what it thinks you should do, then modify the vendor’s startup code to add the GNAT-specific stuff.

It sounds like you’re well on the way to a blinking LED, which is always a good place to start… Most applications can be broadly described as reading inputs and toggling pins with precise timing.

Usually the first things I try to get working are:

  1. Toggle a pin
  2. Configure the system clock (XOSC, PLLs, etc)
  3. Emit a character from the UART
  4. Configure a timer for delays

All of these things are much easier with a hardware debugger, which it looks like you’ve already got working. Yay for gdb!

Once you’ve got those basics going, you can either build general purpose drivers for all the peripherals in the chip or you can just implement a minimal HAL for just the operations your application needs.

General purpose drivers are nice if you intend to use the same chip for multiple applications, but it’s hard to build good abstractions if you don’t know what the drivers will be used for.

An application specific HAL is usually less work and less code to maintain, but is likely to contain leaky abstractions. If you do end up reusing the same platform in the future, you’ll probably copy/paste this code and modify it for the new application, and now you have two codebases to maintain.