https://blog.adacore.com/ada-on-any-arm-cortex-m-device-in-just-a-couple-minutes AdaCore Blog Menu [ ] All Industry News & Trends Technology Product News Opinion linkedin twitter rss search [ ][ ] All Industry News & Trends Technology Product News Opinion linkedin twitter github rss Ada on any ARM Cortex-M device, in just a couple minutes by Fabien Chouteau - Jan 11, 2021 [iStock-866702538] In this blog post I want to present a new tool that allows one to very quickly and easily start Ada programming on any ARM Cortex-M or RISC-V microcontroller. To program a microcontroller with Ada, one must start with a run-time library. The run-time provides various Ada features (depending on the run-time profile), and compilation options. The run-time traditionally also provides board specific code that has to be adapted for each board. The solution I am presenting here removes the board specific parts of the run-time and provides a tool to generate them from a simple description of the hardware. startup-gen # The solution is focused on the Zero-FootPrint (ZFP) run-times. These run-times support one of the simplest subsets of Ada and do not, in particular, implement Ravenscar tasking. The Ravenscar run-times require much more board adaptations. They are, therefore, not covered by this solution. Starting with GNAT Pro 21.0 or GNAT Community 2020, in addition to pre-built run-times targeting specific microcontrollers and processors, GNAT for bareboard ARM and bareboard RISC-V includes pre-built generic ZFP run-time libraries that target specific Cortex-M and RISC-V cores. These generic run-times omit microcontroller specific startup code and linker scripts, enabling them to be provided separately without creating and building a new run-time. A new tool, startup-gen, is introduced to generate the missing startup code and linker scripts from a description of the hardware provided in the project file. You can get startup-gen packaged with GNAT Pro 21.0 or from the Alire package manager: $ alr get --build startup_gen Let's look at an example # For this example we will use the STM32F4-Discovery development board from STmicro. The board is equipped with an ARM Cortex-M4F microcontroller. Board specifications # To begin with, we need to know the specification of the board and microcontroller. We will need: * The name of the CPU core architecture (ARM Cortex-M4F in our case) * Base address and size of memory banks (flash, RAM, etc.) * The number of interrupts You can get the information from the vendor documentation or product page. Another way to get the required information is look in the XML-based package description (PDSC) files of a CMSIS pack. For instance in the STM32F4XX PDSC we can see: The project file # Given that board description we can then augment the GNAT project (gpr) file.The project file will require some specific fields: * The list of languages must contain ASM_CPP, because we will compile the startup code (crt0) written in assembly language. * The run-time should be set to zfp-cortex-m4f because we are using an ARM Cortex-M4F microcontroller. This is one of the generic ZFP run-time mentioned above. * The linker script must be specified as a linker option * The board specifications in a Device_ Configuration package Here is what the resulting project file looks like: project Hello is for Languages use ("Ada", "ASM_CPP"); -- ASM_CPP to compile the startup code for Source_Dirs use ("src"); for Object_Dir use "obj"; for Main use ("hello.adb"); for Target use "arm-eabi"; -- generic ZFP run-time compatible with our MCU for Runtime ("Ada") use "zfp-cortex-m4f"; package Linker is -- Linker script generated by startup-gen for Switches ("Ada") use ("-T", Project'Project_Dir & "/src/link.ld"); end Linker; package Device_Configuration is -- Name of the CPU core on the STM32F407 for CPU_Name use "ARM Cortex-M4F"; for Float_Handling use "hard"; -- Number of interrupt lines on the STM32F407 for Number_Of_Interrupts use "82"; -- List of memory banks on the STM32F407 for Memories use ("SRAM", "FLASH", "CCM"); -- Specify from which memory bank the program will load for Boot_Memory use "FLASH"; -- Specification of the SRAM for Mem_Kind ("SRAM") use "ram"; for Address ("SRAM") use "0x20000000"; for Size ("SRAM") use "128K"; -- Specification of the FLASH for Mem_Kind ("FLASH") use "rom"; for Address ("FLASH") use "0x08000000"; for Size ("FLASH") use "1024K"; -- Specification of the CCM RAM for Mem_Kind ("CCM") use "ram"; for Address ("CCM") use "0x10000000"; for Size ("CCM") use "64K"; end Device_Configuration; end Hello; Generate the linker script and startup code with startup-gen # Once the project file is ready we can use startup-gen to generate the linker script and startup code. To do this, use the following command line: $ startup-gen -P hello.gpr -l src/link.ld -s src/crt0.S This means that startup-gen will create a linker script in src/ link.ld and an assembly code file in src/crt0.S. Create the Ada application code # We need some Ada code to run on the board, so let's write a simple hello world in src/hello.adb: with Ada.Text_IO; procedure Hello is begin Ada.Text_IO.Put_Line ("Hello world!"); end Hello; Build # We can now build our project: $ gprbuild -P hello.gpr It is also possible to open this project in GNATstudio and build it from there. Run # We can now run the program, for example on GNATemulator: $ arm-eabi-gnatemu --board=STM32F4 obj/hello Scenario Variables # startup-gen supports the use of scenario variables in the input project file. These can be used in multiple ways, here are two examples: Select boot memory # project Prj is type Boot_Mem is ("flash", "sram"); Boot : Boot_Mem := external ("BOOT_MEM", "flash"); package Device_Configuration is for Memories use ("flash", "sram"); for Boot_Memory use Boot; -- [...] end Device_Configuration; end Prj; $ startup-gen -P prj.gpr -l link.ld -s crt0.S -XBOOT_MEM=flash $ startup-gen -P prj.gpr -l link.ld -s crt0.S -XBOOT_MEM=sram Select boards with different device configuration # project Prj is type Board_Kind is ("dev_board", "production_board"); Board : Board_Kind := external ("BOARD", "dev_board"); package Device_Configuration is for Memories use ("flash", "sram"); case Board is when "dev_board" => for Size ("sram") use "256K"; when "production_board" => for Size ("sram") use "128K"; end case; -- [...] end Device_Configuration; end Prj; $ startup-gen -P prj.gpr -l link.ld -s crt0.S -XBOARD=dev_board $ startup-gen -P prj.gpr -l link.ld -s crt0.S -XBOARD=production_board Conlusion # With this new tool we want to reduce the barrier of entry for Ada/ SPARK programming on microcontrollers by removing the run-time customization step. The code is available on GitHub: https:// github .com/ A d a C o r e / s t a r t u p-gen, don't hesitate to give us feedback or contribute. Posted in About Fabien Chouteau Fabien Chouteau Fabien joined AdaCore in 2010 after his engineering degree at the EPITA (Paris). He is involved in real-time, embedded and hardware simulation technology. Maker/DIYer in his spare time, his projects include electronics, music and woodworking. Please enable JavaScript to view the comments powered by Disqus. AdaCore provides tools and expertise for safe, secure, and high-reliability software development. Home Products Developers Training Customers Ada Answers Academia Support linkedin twitter github rss Copyright (c) 2021 AdaCore. All Rights Reserved Legal | Privacy Policy | Code of Conduct