Make with Ada

Programming Competition   June 20 - September 30, 2016

Bluetooth Beacons

Having created an initial RTS for the NRF51822, I want to play with the 2.4GHz radio. Initial low hanging fruit is iBeacon, followed by Eddystone-EID.

Project Lead Shawn Nock     Location London, Canada
Log Updates 10

Project Log

In which armv7-m (lm3s) is much like armv6-m (nRF51822)

I had some initial luck copying the vast majority of the zfp-lm3s RTS and modifying it for the nRF51.

Linker File Changes

They have different memory maps, but luckily the nRF51 map is very simple: ~~~ SEARCH_DIR(.) GROUP(-lgcc)

MEMORY { FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 256k RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 16k }

OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")

ENTRY(Reset_Handler)

SECTIONS { .text : { KEEP((.Vectors)) *(.text)

    KEEP(*(.init))
    KEEP(*(.fini))

    /* .ctors */
    *crtbegin.o(.ctors)
    *crtbegin?.o(.ctors)
    *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
    *(SORT(.ctors.*))
    *(.ctors)

    /* .dtors */
    *crtbegin.o(.dtors)
    *crtbegin?.o(.dtors)
    *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
    *(SORT(.dtors.*))
    *(.dtors)

    *(.rodata*)

    *(.eh_frame*)
    . = ALIGN(4);
} > FLASH


.ARM.extab : 
{
    *(.ARM.extab* .gnu.linkonce.armextab.*)
    . = ALIGN(4);
} > FLASH

__exidx_start = .;
.ARM.exidx :
{
    *(.ARM.exidx* .gnu.linkonce.armexidx.*)
    . = ALIGN(4);
} > FLASH
__exidx_end = .;

__etext = .;

.data : AT (__etext)
{
    __data_start__ = .;
    *(vtable)
    *(.data*)

    . = ALIGN(4);
    /* preinit data */
    PROVIDE_HIDDEN (__preinit_array_start = .);
    *(.preinit_array)
    PROVIDE_HIDDEN (__preinit_array_end = .);

    . = ALIGN(4);
    /* init data */
    PROVIDE_HIDDEN (__init_array_start = .);
    *(SORT(.init_array.*))
    *(.init_array)
    PROVIDE_HIDDEN (__init_array_end = .);


    . = ALIGN(4);
    /* finit data */
    PROVIDE_HIDDEN (__fini_array_start = .);
    *(SORT(.fini_array.*))
    *(.fini_array)
    PROVIDE_HIDDEN (__fini_array_end = .);

    *(.jcr)
    . = ALIGN(4);
    /* All data end */
    __data_end__ = .;

} > RAM

.bss :
{
    . = ALIGN(4);
    __bss_start__ = .;
    *(.bss*)
    *(COMMON)
    . = ALIGN(4);
    __bss_end__ = .;
} > RAM

.heap (COPY):
{
    __end__ = .;
    end = __end__;
    *(.heap*)
    __HeapLimit = .;
} > RAM

/* .stack_dummy section doesn't contains any symbols. It is only
 * used for linker to calculate size of stack sections, and assign
 * values to stack symbols later */
.stack_dummy (COPY):
{
    *(.stack*)
} > RAM

/* Set stack top to end of RAM, and stack limit move down by
 * size of stack_dummy section */
__StackTop = ORIGIN(RAM) + LENGTH(RAM);
__StackLimit = __StackTop - SIZEOF(.stack_dummy);
PROVIDE(__stack = __StackTop);

/* Check if data + heap + stack exceeds RAM limit */
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack")

} ~~~

Posted Jul 05, 2016 at 03:37

In which Shawn cannot use a WebUI

Well, that first update was crap. Hopefully we are allowed to edit these down before the contest is over.

Posted Jul 05, 2016 at 03:44

More lm3s / nrf51 differences

RTS changes, continued

I copied the initialization code from the Nordic nRF SDK v10.0; it is permissively licensed. Most of the non-memory map changes in the linker file are differing symbol names (__heap_start vs. HeapStart).

The assembly to bootstrap the microcontrollers are quite similar. The nRF has a manipulation to ensure that all the RAM banks are powered at reset and no PLL machinery. Otherwise, it's just the standard:

  • Point us to the top of stack
  • Show us where our vectors live
  • Copy in the bss segment from flash
  • Branch to the main routine

Initial Success

I used the linker file to define some registers and was able to turn on one of the boards LEDs.

Next time

  1. A Delay routine
Posted Jul 05, 2016 at 03:45

GNAT 2016 and nrf51822: Blinking like you mean it

Having ported a zero footprint profile RTS to nrf51 (from the lm3s one available in the GNAT 2016 distribution); it was time to write some code.

Like many before me, I chose to blink some lights. You can follow along here: https://github.com/nocko/Nrf51LedDemo_Ada. You can blink LEDs by spinning in a tight do nothing loop, but that's no fun.

As it turns out, to blink the LED efficiently, you need to write a few peripheral drivers. GPIO (duh), but also RTC (so you can sleep the uController), NVIC (interrupt controller) and support for the sleep mechanisms as well.

The sleep mechanism on Cortex-m0 is really easy, it's an instruction (WFI or WFE), we can implement this in Ada with inline assembly: ada procedure WFI is use System.Machine_Code; begin Asm (Template => "wfi", Volatile => True); return; end WFI;

Efficient (Power) delay routine

Initialization requires starting the 32kHz xtal oscillator, configuring the RTC prescaler and finally configuring the RTC to interrupt on match of the the CC0 register (and enabling interrupt in the NVIC).

ada ... -- Start the 32kHz xtal oscillator if EVENTS_LFCLKSTARTED /= 1 then LFCLKSRC.SRC := Xtal; TASKS_LFCLKSTART := 1; loop -- Waiting for the LF Oscillator to start exit when EVENTS_LFCLKSTARTED = 1; end loop; end if; ... -- Initialize (clear) the RTC peripheral, enable interrupt on CC0 PRESCALER := Delay_Timer_Prescaler; TASKS_STOP := 1; TASKS_CLEAR := 1; INTENSET.COMPARE.Arr (0) := Set; Interrupts.Enable (RTC1_IRQ);

The delay routine has two parts; set the RTC to interrupt after the specified time and finally wait-for and detect the interrupt (while saving as much power as possible).

ada procedure Delay_MS (Milliseconds : Natural) is TASKS_START : nrf51.Word renames nrf51.RTC.RTC1_Periph.TASKS_START; CC0 : UInt24 renames nrf51.RTC.RTC1_Periph.CC (0).COMPARE; begin CC0 := MS_To_Ticks (Milliseconds); -- Set a flag (to be cleared by the IRQHandler) Delay_Active := True; -- Start the RTC TASKS_START := 1; loop -- Keep sleeping until the IRQHandler indicates that our time has come WFI; exit when Delay_Active = False; end loop; end Delay_MS;

RTC IRQHandler

The ISR is simple. Clear the flag set above so Delay_MS will return; then stop the RTC, ack the interrupt and clear the RTC peripheral compare registers.

ada procedure RTC1_IRQHandler is ... begin -- Clear the flag so the main execution path continues Delay_Active := False; -- Stop and reset RTC and acknowledge the interrupt TASKS_STOP := 1; TASKS_CLEAR := 1; EVENTS_COMPARE (0) := 0; end RTC1_IRQHandler;

Does it work?

It doesn't work on GNAT 2016, and we'd not have known if we used a busy loop to blink our LED. In our next exciting instalment; we'll figure out why and fix it.

Posted Aug 30, 2016 at 05:33

Hosting Build Log on my Blog

The Make with Ada site's support for markdown is incomplete (please enable editing and code blocks). For easier reading, I am posting them at my blog as well.

https://nocko.se/blog https://nocko.se/blog/gnat-2016-and-nrf51822-blinking-poorly/ https://nocko.se/blog/building-gnat-gpl-2016/

Posted Aug 30, 2016 at 06:26

ZFP RTS Availability

I've worked on the zfp RTS for nrf51 and it's mostly complete. It currently lives here: https://github.com/nocko/zfp-nrf51 and is licensed GPLv2 or later.

I am planning to submit a pull request against https://github.com/AdaCore/embedded-runtimes; but currently I include a driver for the interrupt handler in the RTS (where as Adacore puts similar drivers in https://github.com/AdaCore/AdaDriversLibrary. I'll have to coordinate with Adacore for a good solution.

Posted Sep 15, 2016 at 06:32

GNAT 2016 support for arm-v6m

I've updated my blog with a post about troubleshooting bad instructions in GNAT 2016 output for nrf51 and a fix (that requires rebuilding GNAT 2016):

https://nocko.se/blog/gnat-2016s-multilib-isnt-multi-enough/

Posted Sep 15, 2016 at 06:37

I've updated my blog with a new post about the ZFP runtime for nrf51: https://nocko.se/blog/zero-footprint-profile-runtime-system-ada-for-nrf51/

Posted Sep 22, 2016 at 05:59

Ada iBeacon

I've written a BLE iBeacon demo app in Ada. It's licensed GPLv2+. The code is available here: https://github.com/nocko/Nrf51IbeaconAda/.

I've written a blog post with some explanation and additional annotation: https://nocko.se/blog/nrf51-ibeacon-in-ada/

Posted Sep 28, 2016 at 06:54

Final Project Update

I posted a few thoughts on my Make with Ada experience: https://nocko.se/blog/make-with-ada-redux/.

Thank you for putting together what has been a very positive experience for me.

Shawn

Posted Sep 29, 2016 at 10:40

Bluetooth Beacons

Having created an initial RTS for the NRF51822, I want to play with the 2.4GHz radio. Initial low hanging fruit is iBeacon, followed by Eddystone-EID.

Project Lead Shawn Nock     Location London, Canada
Log Updates 10

Project Log

In which armv7-m (lm3s) is much like armv6-m (nRF51822)

I had some initial luck copying the vast majority of the zfp-lm3s RTS and modifying it for the nRF51.

Linker File Changes

They have different memory maps, but luckily the nRF51 map is very simple: ~~~ SEARCH_DIR(.) GROUP(-lgcc)

MEMORY { FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 256k RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 16k }

OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")

ENTRY(Reset_Handler)

SECTIONS { .text : { KEEP((.Vectors)) *(.text)

    KEEP(*(.init))
    KEEP(*(.fini))

    /* .ctors */
    *crtbegin.o(.ctors)
    *crtbegin?.o(.ctors)
    *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
    *(SORT(.ctors.*))
    *(.ctors)

    /* .dtors */
    *crtbegin.o(.dtors)
    *crtbegin?.o(.dtors)
    *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
    *(SORT(.dtors.*))
    *(.dtors)

    *(.rodata*)

    *(.eh_frame*)
    . = ALIGN(4);
} > FLASH


.ARM.extab : 
{
    *(.ARM.extab* .gnu.linkonce.armextab.*)
    . = ALIGN(4);
} > FLASH

__exidx_start = .;
.ARM.exidx :
{
    *(.ARM.exidx* .gnu.linkonce.armexidx.*)
    . = ALIGN(4);
} > FLASH
__exidx_end = .;

__etext = .;

.data : AT (__etext)
{
    __data_start__ = .;
    *(vtable)
    *(.data*)

    . = ALIGN(4);
    /* preinit data */
    PROVIDE_HIDDEN (__preinit_array_start = .);
    *(.preinit_array)
    PROVIDE_HIDDEN (__preinit_array_end = .);

    . = ALIGN(4);
    /* init data */
    PROVIDE_HIDDEN (__init_array_start = .);
    *(SORT(.init_array.*))
    *(.init_array)
    PROVIDE_HIDDEN (__init_array_end = .);


    . = ALIGN(4);
    /* finit data */
    PROVIDE_HIDDEN (__fini_array_start = .);
    *(SORT(.fini_array.*))
    *(.fini_array)
    PROVIDE_HIDDEN (__fini_array_end = .);

    *(.jcr)
    . = ALIGN(4);
    /* All data end */
    __data_end__ = .;

} > RAM

.bss :
{
    . = ALIGN(4);
    __bss_start__ = .;
    *(.bss*)
    *(COMMON)
    . = ALIGN(4);
    __bss_end__ = .;
} > RAM

.heap (COPY):
{
    __end__ = .;
    end = __end__;
    *(.heap*)
    __HeapLimit = .;
} > RAM

/* .stack_dummy section doesn't contains any symbols. It is only
 * used for linker to calculate size of stack sections, and assign
 * values to stack symbols later */
.stack_dummy (COPY):
{
    *(.stack*)
} > RAM

/* Set stack top to end of RAM, and stack limit move down by
 * size of stack_dummy section */
__StackTop = ORIGIN(RAM) + LENGTH(RAM);
__StackLimit = __StackTop - SIZEOF(.stack_dummy);
PROVIDE(__stack = __StackTop);

/* Check if data + heap + stack exceeds RAM limit */
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack")

} ~~~

Posted Jul 05, 2016 at 03:37

In which Shawn cannot use a WebUI

Well, that first update was crap. Hopefully we are allowed to edit these down before the contest is over.

Posted Jul 05, 2016 at 03:44

More lm3s / nrf51 differences

RTS changes, continued

I copied the initialization code from the Nordic nRF SDK v10.0; it is permissively licensed. Most of the non-memory map changes in the linker file are differing symbol names (__heap_start vs. HeapStart).

The assembly to bootstrap the microcontrollers are quite similar. The nRF has a manipulation to ensure that all the RAM banks are powered at reset and no PLL machinery. Otherwise, it's just the standard:

  • Point us to the top of stack
  • Show us where our vectors live
  • Copy in the bss segment from flash
  • Branch to the main routine

Initial Success

I used the linker file to define some registers and was able to turn on one of the boards LEDs.

Next time

  1. A Delay routine
Posted Jul 05, 2016 at 03:45

GNAT 2016 and nrf51822: Blinking like you mean it

Having ported a zero footprint profile RTS to nrf51 (from the lm3s one available in the GNAT 2016 distribution); it was time to write some code.

Like many before me, I chose to blink some lights. You can follow along here: https://github.com/nocko/Nrf51LedDemo_Ada. You can blink LEDs by spinning in a tight do nothing loop, but that's no fun.

As it turns out, to blink the LED efficiently, you need to write a few peripheral drivers. GPIO (duh), but also RTC (so you can sleep the uController), NVIC (interrupt controller) and support for the sleep mechanisms as well.

The sleep mechanism on Cortex-m0 is really easy, it's an instruction (WFI or WFE), we can implement this in Ada with inline assembly: ada procedure WFI is use System.Machine_Code; begin Asm (Template => "wfi", Volatile => True); return; end WFI;

Efficient (Power) delay routine

Initialization requires starting the 32kHz xtal oscillator, configuring the RTC prescaler and finally configuring the RTC to interrupt on match of the the CC0 register (and enabling interrupt in the NVIC).

ada ... -- Start the 32kHz xtal oscillator if EVENTS_LFCLKSTARTED /= 1 then LFCLKSRC.SRC := Xtal; TASKS_LFCLKSTART := 1; loop -- Waiting for the LF Oscillator to start exit when EVENTS_LFCLKSTARTED = 1; end loop; end if; ... -- Initialize (clear) the RTC peripheral, enable interrupt on CC0 PRESCALER := Delay_Timer_Prescaler; TASKS_STOP := 1; TASKS_CLEAR := 1; INTENSET.COMPARE.Arr (0) := Set; Interrupts.Enable (RTC1_IRQ);

The delay routine has two parts; set the RTC to interrupt after the specified time and finally wait-for and detect the interrupt (while saving as much power as possible).

ada procedure Delay_MS (Milliseconds : Natural) is TASKS_START : nrf51.Word renames nrf51.RTC.RTC1_Periph.TASKS_START; CC0 : UInt24 renames nrf51.RTC.RTC1_Periph.CC (0).COMPARE; begin CC0 := MS_To_Ticks (Milliseconds); -- Set a flag (to be cleared by the IRQHandler) Delay_Active := True; -- Start the RTC TASKS_START := 1; loop -- Keep sleeping until the IRQHandler indicates that our time has come WFI; exit when Delay_Active = False; end loop; end Delay_MS;

RTC IRQHandler

The ISR is simple. Clear the flag set above so Delay_MS will return; then stop the RTC, ack the interrupt and clear the RTC peripheral compare registers.

ada procedure RTC1_IRQHandler is ... begin -- Clear the flag so the main execution path continues Delay_Active := False; -- Stop and reset RTC and acknowledge the interrupt TASKS_STOP := 1; TASKS_CLEAR := 1; EVENTS_COMPARE (0) := 0; end RTC1_IRQHandler;

Does it work?

It doesn't work on GNAT 2016, and we'd not have known if we used a busy loop to blink our LED. In our next exciting instalment; we'll figure out why and fix it.

Posted Aug 30, 2016 at 05:33

Hosting Build Log on my Blog

The Make with Ada site's support for markdown is incomplete (please enable editing and code blocks). For easier reading, I am posting them at my blog as well.

https://nocko.se/blog https://nocko.se/blog/gnat-2016-and-nrf51822-blinking-poorly/ https://nocko.se/blog/building-gnat-gpl-2016/

Posted Aug 30, 2016 at 06:26

ZFP RTS Availability

I've worked on the zfp RTS for nrf51 and it's mostly complete. It currently lives here: https://github.com/nocko/zfp-nrf51 and is licensed GPLv2 or later.

I am planning to submit a pull request against https://github.com/AdaCore/embedded-runtimes; but currently I include a driver for the interrupt handler in the RTS (where as Adacore puts similar drivers in https://github.com/AdaCore/AdaDriversLibrary. I'll have to coordinate with Adacore for a good solution.

Posted Sep 15, 2016 at 06:32

GNAT 2016 support for arm-v6m

I've updated my blog with a post about troubleshooting bad instructions in GNAT 2016 output for nrf51 and a fix (that requires rebuilding GNAT 2016):

https://nocko.se/blog/gnat-2016s-multilib-isnt-multi-enough/

Posted Sep 15, 2016 at 06:37

I've updated my blog with a new post about the ZFP runtime for nrf51: https://nocko.se/blog/zero-footprint-profile-runtime-system-ada-for-nrf51/

Posted Sep 22, 2016 at 05:59

Ada iBeacon

I've written a BLE iBeacon demo app in Ada. It's licensed GPLv2+. The code is available here: https://github.com/nocko/Nrf51IbeaconAda/.

I've written a blog post with some explanation and additional annotation: https://nocko.se/blog/nrf51-ibeacon-in-ada/

Posted Sep 28, 2016 at 06:54

Final Project Update

I posted a few thoughts on my Make with Ada experience: https://nocko.se/blog/make-with-ada-redux/.

Thank you for putting together what has been a very positive experience for me.

Shawn

Posted Sep 29, 2016 at 10:40