6 Library Organization
This chapter is based on the projects:
- 04a_crc
- 00_toolkit
6.1 Projectlet Goals
Goals of this projectlet are:
- Replicate the crc project
- Evolve an architecture of a toolkit that can be the platform for all other projects. In fact this layer should be able to serve as the starting point for a fullblown application with a few cooperating applications.
- Expectation is for the addition of standard features such as application health annunciation through an LED, a log, a terminal like command line interface to be included transparently in the application without major efforts - excepting hardware dependencies.
6.2 Organization of the codebase
We take a breather now to revise our code organization for enabling quick composition of new projects. In particular an approach to a library of modules to support common functions began to evolve in the previous projects. Based on our experiments, in this projectlet, the design of a toolkit is attempted. In particular an application oriented library layer toolkit is introduced to shield applications somewhat from having to reengineer many standard support functions.
In particular the earlier project crc layered on top of such a toolkit simply includes the library in its project file and application itself is very simple problem oriented code. The toolkit is included as:
File : crc.gpr
0002 | with "../00_toolkit/toolkit.gpr";
0003 | with "config/crc_config.gpr";
0004 | with "../common.gpr" ;
0005 |
0006 | project Crc is
0007 |
0008 | for Target use "arm-eabi" ;
0009 | for Runtime ("Ada") use "embedded-stm32f4" ;
As an aside, the above fragment illustrates, an abstract project file common.gpr is designed for inclusion into other projects - in order to standardize compiler switches and the like:
File : crc.gpr
0019 | package Compiler is
0020 | for Default_Switches ("Ada") use common.Compiler_Switches ;
0021 | end Compiler;
6.3 Simplified application
The toolkit does not have a great impact on the application code as shown:
File : crc.adb
0039 | loop
0040 | while wordend < test'Last and test(wordend) /= ' ' loop
0041 | wordend := wordend + 1 ;
0042 | end loop ;
0043 | crc_value := toolkit.crc.Calculate( test(wordbegin)'Address , wordend - wordbegin ) ;
0044 | if wordend = test'Last then
0045 | toolkit.logs.logger.Put_Line( test(wordbegin .. wordend) &
0046 | " is: " & hex.Image(crc_value)
0047 | , source => myname ) ;
0048 | else
0049 | toolkit.logs.logger.Put_Line( test(wordbegin .. wordend - 1) &
0050 | " is: " & hex.Image(crc_value)
0051 | , source => myname ) ;
0052 | end if ;
0053 |
0054 | if wordend >= test'Last then
0055 | crc_value := toolkit.crc.Calculate( test(test'First)'Address , test'Length ) ;
0056 | toolkit.logs.logger.Put_Line ( "CRC(" & test & ") is: " & hex.Image(crc_value)
0057 | , level => toolkit.logs.WARNING
0058 | , source => myname ) ;
0059 | wordbegin := test'First ;
0060 | else
0061 | wordbegin := wordend + 1 ;
0062 | end if ;
0063 | wordend := wordbegin ;
0064 | delay 2.0 ;
0065 | end loop ;The logs should now be a bit more fanciful, with automatic timestamps, logging level support and a way to identify the source of a log line. The entire log channel now can support multiple generator.
The library is incorporated in the application as:
File : crc.adb
0008 | with hex ;
0009 | with toolkit.led ;
0010 | pragma Elaborate( toolkit.led ) ;
0011 |
0012 | with toolkit.services;
0013 | pragma Elaborate( toolkit.services ) ;
0014 |
0015 | with toolkit.rtclsi ;
0016 | with toolkit.logs ;
0017 | with toolkit.crc ;All of this has a very familiar feel though the target environment is an embedded bare metal platform. It will be a challenge to use an entirely different board without changing the application code - which will be addressed in the next book in this series.
6.3.1 Example run
When this application is run, several features are illustrated - an LED blinks, the logs are richer, the log level (I for informational and W for warning) as described in toolkit.logs and source (Crc) are clearly identified:
17:50:16 -W- [Crc...] : CRC(The quick brown fox jumps over the lazy dog) is: 1ce6cfb1
17:50:18 -I- [Crc...] : The is: 0cbeb623
17:50:20 -I- [Crc...] : quick is: 35243e17
17:50:22 -I- [Crc...] : brown is: 5b50750a
17:50:24 -I- [Crc...] : fox is: f3342c74
17:50:26 -I- [Crc...] : jumps is: 3eb7d624
17:50:28 -I- [Crc...] : over is: fcfca4eb
17:50:30 -I- [Crc...] : the is: 949d00c3
17:50:33 -I- [Crc...] : lazy is: 59f8a6e2
17:50:35 -I- [Crc...] : dog is: a4c70a72
17:50:35 -W- [Crc...] : CRC(The quick brown fox jumps over the lazy dog) is: 1ce6cfb1
17:50:37 -I- [Crc...] : The is: 0cbeb623
17:50:39 -I- [Crc...] : quick is: 35243e17
17:50:41 -I- [Crc...] : brown is: 5b50750a
17:50:43 -I- [Crc...] : fox is: f3342c74
17:50:45 -I- [Crc...] : jumps is: 3eb7d624
In addition, with very little additional actions, a terminal is automatically created and is operational. A session that exercises the CRC from the terminal:
led - [blue|green|red|orange|ext] toggles the led
crc - hex string or quoted string to compute and display CRC
cli> crc aabbccdd
19354eb8
eb863705 "AdaCore"
CRC of c
[ 305419896, 3735928559] is: d92b271b
Calculated as a block is: d92b271b
CRC of
[ 1, 2, 3, 4] is: 955ae3fd
Calculated as a block is: 955ae3fd
CRC of 1 bytes is: c3c5c0cc
CRC of 2 bytes is: 63375ec3
CRC of 3 bytes is: 615eab4a
CRC of 4 bytes is: 1dabe74f
CRC of 5 bytes is: ba237be3
CRC of 6 bytes is: 5ff5c445
CRC of 7 bytes is: 5afe83d0
CRC of 8 bytes is: a3141bda
cli>
Of course, the commands are implemented as part of the toolkit but in later projects a more application oriented command line support will be illustred.
6.4 Architecture of the library
A library for the embedded environment is quite similar to one for general purpose environments. In this effort, the library consists of several functions imported from the ADL such as serial_io and its supporting functions such as message_buffers. These are included in the ADL as examples and not as part of the library itself.
The toolkit from the earlier work contributes a package such as hex but customized for the embedded platform.
Beyond that all the disparate support packages from earlier projects are replicated here under the package toolkit in the process making them adaptible to applications easily. All the support that may be hardware related are all collected under a package tree. To summarize the library features the following package tree:
| Package | Function |
|---|---|
| toolkit | toplevel |
| toolkit.button | Pushbuttons |
| toolkit.cli | command language |
| toolkit.cli.cmd | commands supported |
| toolkit.crc | CRC |
| toolkit.led | LEDs |
| toolkit.led.health | App health |
| toolkit.logs | Text logs |
| toolkit.rng | Random numbers |
| toolkit.rtclsi | Realtime clock |
| toolkit.services | a way to initiate services |
The application crc is an example of applying this as is the button projectlet to follow.
The library project is specified similar to the ones for general purpose platforms:
File : toolkit.gpr
0002 | with "../../adl/boards/stm32f407_discovery/stm32f407_discovery_full.gpr";
0003 | with "config/toolkit_config.gpr";
0004 | with "../common.gpr" ;
0005 | project Toolkit is
0006 |
0007 | for Target use "arm-eabi" ;
0008 | for Runtime ("Ada") use "embedded-stm32f4" ;
0009 |
0010 | for Library_Name use "Toolkit";
0011 | for Library_Version use Project'Library_Name & ".so." & Toolkit_Config.Crate_Version;
0012 |
0013 | type Library_Type_Type is ("relocatable", "static", "static-pic");
0014 | Library_Type : Library_Type_Type :=
0015 | external ("TOOLKIT_LIBRARY_TYPE", external ("LIBRARY_TYPE", "static"));
0016 | for Library_Kind use Library_Type;
Things to note in this project file are:
- The inclusion of the ADL which is done in this one location and does not have to be repeated in each project.
- By default a static library is created
- The library name is specified as Toolkit resulting in libToolkit.a
- A method to have common switches is used - if needed
Most devices (medical or otherwise) will feature multiple low level application modules, of the same architecture and will benefit from this approach of separating the library. The conventional notion of hardware abstraction layer HAL is extremely helpful in isolating low level details but the toolkit layer goes further.
6.4.1 Example: Log service
In particular standard constructs such as tasks, protected objects can be implemented in this layer to help designers to focus on the application. The logging support provided by the toolkit toolkit.logs is a pattern that will be replicated many times. From the application’s standpoint, a protected object is the destination and source as needed:
File : toolkit-logs.ads
0016 | procedure Put_Line (This : String) ;
0017 |
0018 | protected Logger
0019 |
0020 | with Priority => System.Priority'Last is
0021 |
0022 | entry Put_Line( this : String ;
0023 | level : message_level_type := INFORMATIONAL ;
0024 | source : Source_Name_Type := Default_Source_Name ) ;
0025 |
0026 | entry Get( msg : in out Message_Buffers.Message ;
0027 | level : out message_level_type ;
0028 | source : out Source_Name_Type ) ;
0029 |
0030 | private
0031 | initialized : Boolean := False ;In the above, the application uses the Put_Line to request a log line but this request is processed only when exclusive access can be granted to the requestor. Only the actual log text generator needs to retrieve these requests using the Get interface.
The log text generator itself is a task:
File : toolkit-logs.adb
0101 | task body Service_Type is
0102 | msg : Message_Buffers.Message (Physical_Size => 160); -- arbitrary size
0103 | ml : message_level_type ;
0104 | src : Source_Name_Type ;
0105 | begin
0106 | loop
0107 | Logger.Get( msg , ml , src);
0108 | Put_Line( rtclsi.Get_Time & " " & Image(ml) & " [" & src & "] : " & msg.Content );
0109 | end loop ;
0110 | end Service_Type;This task has exclusive control of the hardware and leverages the non blocking interfaces to the serial_io ports. The specifics of the GPIO pins, serial discipline (e.g. 8 bits, 1 stop bit, 115200 baud) are all hidden from the application. In fact the actual text that is generated contains the applications text but additional information such as timestamps are added transparently.
A particular enhancement that will be useful but not implemented is the addition of a CRC to the message. For a log text, it may be considered an overhead but in later projectlets, when we apply this pattern to binary data - including sensor feedback and setpoints, such data protection will be mandatory.