I was looking at The predefined profiles in order to understand the difference between Ada runtimes and profiles. I would like to ask a few basic questions:
Am I correct in thinking that a particular Ada runtime could support multiple profiles like Ravenscar and Jorvik but most of the time each runtime is designed for one specific profile?
What about ZFP runtime, does it support any profiles, or profiles are more about restricting Ada tasking, hence it is not relevant to ZFP runtime which does not support any tasking?
I would like to understand how ZFP runtime is integrated into Ada so I could implement my own version of this runtime. The link above refers to a list of predefined packages for ZFP, so is it the case of simply copying those packages from GNAT full runtime or is there more to implementing a generic ZFP? Is there any documentation that describes how this should be done?
Iāve been looking at the existing open source projects:
However Iām trying to find documentation or howto guides that would allow me to understand how ZFP runtime should be designed. Something similar to https://wiki.osdev.org/Ada_Bare_Bones but maybe more up to date.
WHY do you need one more runtime? What you want to achieve?
ZFP is just a minimal subset of runtimeās packages. It is almost cross-platform. There is no need to develop anything, just take it and use.
Ravenscar/Jorvik is a set of restrictions applicable to much more complete runtime, mostly to tasking part.
light-tasking and embedded runtimes includes bare-board schedulers, like, for example, FreeRTOS. They provide particular implementation of runtime to run on particular platform. Both supports restricted set of runtime only.
full runtime is expected to run on top of more complicated OS, using features provided by OS (scheduling, memory management). Application might restrict use of language features when necessary.
Iām trying to achieve the equivalent of freestanding C implementation, i.e. absolutely minimal Ada runtime. This is less to do with machine resources and more to do with learning how to implement a custom runtime.
The Ada/SPARC bare_runtime seems to be more than just ZFP and contains a lot of functionality which I may not need at this time.
If I build a freestanding C cross-compiler, it also includes stdbool.h, stddef.h, stdint.h, etc., which is enough to get started with bare metal programming. However, a similar Ada cross-compiler is missing this functionality and requires a runtime. I donāt need dynamic memory allocation or tasking, but at the very least I need packages for fixed width integers and interfacing with the C language. Without a ZFP runtime, compiling Ada code fails with:
Iām experimenting with Ada in the context of developing a small bootloader for SPARC V9. The open firmware provides various interfaces for reading and writing from/to the console and allocating memory buffers. I would like to implement Ada bindings for the firmware interfaces, which would then allow me to implement the bootloader mostly in Ada.
Do you need to be able just to compile code or want to use gprbuild?
For the first one just add system.ads to your sources. Look for formal definition in RM and for examples in bb-runtimes. It is enough to compile simple code using gcc. Compiler will complain about missing support units when necessary.
PS. Freestanding C compiler includes libgcc, it is like ZFP for Ada.
and various other resources. Iām trying to uncover a bit more information on how runtimes are designed and which packages are mandatory for ZFP. There are bits of info here and there in GNAT documentation, but it is missing a lot of details.
Yes, exactly.
With a full Ada runtime, the profiles are guaranteed to work; the reverse is not true.
For example, having a runtime that does not have tasking can be used by a program with pragma Restrictions (No_Tasking); as well as a full runtime, while trying to compile a program that uses tasking would be an error ā remember: āprofilesā are just collections of sets of pragma restrictions with appropriate default values.
I donāt need gprbuild for my own runtime, as I can use a simple Makefile. The idea is to start with absolute minimum like system.ads plus a few other files, then over time extend it and add more functionality.
This is sadly something that isnāt documented well. The Ada RM only tells you āwhatā the Runtime has to do but not āhowā to do it. (For some of specifics there are multiple ways to do it and gnat is opinionated in that regard)
Be warned! All Ada programs need a runtime and the one you are using (without explicitly stating) is already in your compiler. (Think of libc but for ada. Itās called ālibadaā and included in gnat)
The ironclad kernel has itās own rts that you can adapt (Itās even in Ada/SPARK!) Commit. <format> can be used to further edit the. Itās quite small and also a good starting point and it uses gprbuild which can be integrated later on to other ada compilation steps.
Iām actually looking into writing a guide on how to implement your own runtime! Itās not going to explain how to implement everything but at least the basics so that people like you can build on top of it.
ZFP does not support any profiles āper seā, itās just the bare minimum to make Ada programs working, but you donāt get OS, no I/O, nothing. ZFP implements (but this not the correct term, see later) basic functionalities to handle machine addresses, asm language and few other things.
Itās a waste of time to re-implement a ZFP, at least in GNAT. You will end up writing exactly whatās already been written. But you could do it, since itās very instructive.
ZFP is composed of a set of files that are almost entirely definitions of types/objects and āintrinsicsā interfaces to the underlying handling logic of the compiler. A well-designed ZFP, when compiled, produces virtually no code at all (hence the term āzeroā), because the compiler generates on the fly (by means of inline instructions) the high-level constructs. As an example, an Unchecked_Conversion is virtually an empty thing when the size of the objects match, and the bit pattern of the original object is taken as the bit pattern of the destination object. No library code.
Many RTSes claim to be ZFP, but technically this is not always true. In SweetAda the ZFP produces a nearly zero-sized library, except for few bytes that handle the initialization procedures and elaboration flags of some types, mostly present in Interfaces.C.Extensions, yet there are no library functions callable. So if you wipe out this unit, you have a zero-footprint runtime for real.
I looked at the source code some time ago and got a bit confused by separate zfs, sfp, and mfp subdirectories, but I think now I understand they are different runtimes, which share some code from the common subdirectory.
With Ada there appears to be no formal definition of what a runtime should be, so for example a small footprint runtime could contain anything you like.
The ācommonā directory contains absolute necessary files, imported nearly always. The zfp picks up those files plus some others (in the zfp own directory). The same for sfp, which is much larger. The configuration.in located in the chosen runtime is processed by the Makefile and files are eventually filtered/edited for parameter adjustments, and you are free to pick up whatever you want. If you want to create your own runtime, I suggest to start with a copy of zfp (obviously with another name) and add (or delete) files at your will. mfp is just sfp + finalization (not working) just to import some external libraries that trigger references about entities during the bind phase.
Pay attention that parameters come also from cpu ātargetsā directories, together with the system.ads, and override the previous ones.
Then you choose the runtime and the profile in the platform configuration.in, naming it appropriately.
The āprofileā is another story, it imposes restrictions on the whole source code. The gnat.adc.in is a template that lets you to associate restrictions-vs-runtimes, configurable as well. Obviously your runtime name shall go in the comment field in order to be recognized and trigger an activation. You can even choose a zfp runtime with sfp restrictions, or whatever you want. The fact that a runtime has the same name of a set of restrictions, is just because it turns out to be simple to associate their functionalities.
If you want to experiment on some files, keep a backup of your changes before cloning again (or play dirty games with git), because of course they will be deleted.