0

I installed the GNAT-GPS and the AVR-ELF 3 days ago to play with. I got a blinky going and thought I might play around some more. I have no non-VHDL Ada experience.

Here's the scenario I have working in C:

I have it set up so that using a GPIO typedef, I can refer to all the information necessary to set up an GPIO pin (i.e. pin number, pin reg address, dd reg address and port reg address). Then I do the same for, say LED0, so that logically I can connect LED0 to GPIO15, which is itself connected to PB1 of the AVR microcontroller.

I try to do the same in Ada. I feel like I might be writing C in Ada; feel free to let me know afterwards if there's a better way to do this in Ada.

I set up the AVR registers for a particular pin to connect to its short name reference:

       -- PB1
   PB1_Port_reg : Unsigned_8;
   PB1_Dd_reg   : Unsigned_8;
   PB1_Pin_reg  : Unsigned_8;
   for PB1_Port_reg'Address use AVR.Atmega328p.PORTB'Address;
   for PB1_Dd_reg'Address use AVR.Atmega328p.DDRB'Address;
   for PB1_Pin_reg'Address use AVR.Atmega328p.PINB'Address;
   PB1_Pin : constant := 1;

Then I setup its short name reference to connect to its package pin number:

   -- ATmega328p DIP28 Pin15 is PB1
   Pin15_Port_reg : Unsigned_8;
   Pin15_Dd_reg   : Unsigned_8;
   Pin15_Pin_reg  : Unsigned_8;
   for Pin15_Port_reg'Address use PB1_Port_reg'Address;
   for Pin15_Dd_reg'Address use PB1_Dd_reg'Address;
   for Pin15_Pin_reg'Address use PB1_Pin_reg'Address;
   Pin15_Pin : constant := PB1_Pin;

Next I define a record to hold all the parameters for the pin together:

   type gpio_t is record
      pin   : Unsigned_8;
      pin_reg   : Unsigned_8;
      dd_reg    : Unsigned_8;
      port_reg  : Unsigned_8;
   end record;

This is to allow me to write the following function:

 procedure gpio_map (gpio_t_dest : in out gpio_t; gpio_t_pin, gpio_t_pin_reg, gpio_t_dd_reg, gpio_t_port_reg : in Unsigned_8) is

   begin
      gpio_t_dest.pin       := gpio_t_pin;
      gpio_t_dest.pin_reg   := gpio_t_pin_reg;
      gpio_t_dest.dd_reg    := gpio_t_dd_reg;
      gpio_t_dest.port_reg  := gpio_t_port_reg;     
   end gpio_map;

In the future, I'll be looking to have it as:

procedure gpio_map_future (gpio_t_dest : in out gpio_t; gpio_t_src : in gpio_t) is

       begin
          gpio_t_dest.pin       := gpio_t_src.pin;
          gpio_t_dest.pin_reg   := gpio_t_src.pin_reg;
          gpio_t_dest.dd_reg    := gpio_t_src.dd_reg;
          gpio_t_dest.port_reg  := gpio_t_src.port_reg;     
       end gpio_map;

This gpio_map function is used to connect a package pin gpio_t to a package pin number:

gpio_map(gpio15, Pin15_pin, Pin15_pin_reg, Pin15_dd_reg, Pin15_port_reg);

I find that the LED is correctly initialized if I use this function:

core_reg_write(Pin15_dd_reg, Shift_Left(1,Integer(Pin15_pin))); -- works

But is not correctly initialized if I do:

core_reg_write(gpio15.dd_reg, Shift_Left(1,Integer(gpio15.pin))); -- does not work

This, however, works:

core_reg_write(Pin15_dd_reg, Shift_Left(1,Integer(gpio15.pin))); -- works

It is clear to me that I have

Pin15_pin = 1 @ address (don't care - a variable)
Pin15_pin_reg = (don't care) @ address 0x23
Pin15_dd_reg = (0b00000000) @ address 0x24
Pin15_port_reg = (don't care) @ address 0x25

And that

gpio15.pin = 1 @ address (don't care, but not same as Pin15_pin address)
gpio15.pin_reg = (don't care) @ address IS NOT 0x23
gpio15.dd_reg = (don't care) @ address IS NOT 0x24
gpio15.port_reg = (don't care) @ address IS NOT 0x25

How do I maintain fixed memory addresses for record members, i.e., get

gpio15.pin_reg = (don't care) @ address 0x23
gpio15.dd_reg = (don't care) @ address 0x24
gpio15.port_reg = (don't care) @ address 0x25

And even better if I can also get

gpio15.pin = 1 @ address (same as Pin15_pin address)

Sorry for the long question; hoping it helped make it clear.

frank
  • 13
  • 3
  • What is the specification of Core_Reg_Write? Is it imported from C? – Jeffrey R. Carter Dec 02 '18 at 10:15
  • The specification of core_reg_write is: procedure core_reg_write (reg: in out Unsigned_8; value: in Unsigned_8); It's body is: procedure core_reg_write (reg: in out Unsigned_8; value: in Unsigned_8) is begin reg := value; end core_reg_write; The original function is C is: void core_reg_write (volatile uint8_t *reg, uint8_t value) { *reg = value; } I recognize that in C, reg is a pointer, but I felt since I was already able to modify its underlying address, I had sufficiently achieved the pointer effect needed to write the initialization to the dd register. – frank Dec 02 '18 at 13:22

4 Answers4

1

You can't really get what you want via assignment of the two types. All that does is copy the current values, not the register addresses. Here is an option:

Create a type similar to your gpio_t type but make it exactly match the register map for your micro. That means you won't be storing the pin number in it and you need to include all the surrounding registers. Here is an example I found from another file for a different micro, but hopefully serves as an example

type Register_Layout is limited record
      DIR      : Unsigned_32;
      DIRCLR   : Unsigned_32;
      DIRSET   : Unsigned_32;
      DIRTGL   : Unsigned_32;
      OUTVAL   : Unsigned_32;
      OUTCLR   : Unsigned_32;
      OUTSET   : Unsigned_32;
      OUTTGL   : Unsigned_32;
      INPUT    : Unsigned_32;
      CTRL     : Unsigned_32;
      WRCONFIG : Unsigned_32;
      EVCTRL   : Unsigned_32;
   end record
      with
         Pack,
         Volatile,
         Size => 12*32;

The record type should be limited so that you ensure it is passed by reference and not by copy.

Note: You can also use a representation clause to provide the byte and bit layout of the structure instead. It will depend on the compiler that you use.

Once you have your micro's registers laid out to match the datasheet, you then create a variable and map that to the address you want, just like you did with the individual variables

Register_B : Register_Layout with
      Address => System'To_Address(Some_Address),
      Volatile => True,
      Import => True;

This will map the entire record variable to that address.

After that, you need to modify your function calls to take the whole record as a parameter instead of the just the register. As an example:

Core_Reg_Write_DIR(Register_B, Shift_Left(1,Integer(PB1_Pin)));

If you need to have things be more fancy and have the right registers and mask value selected via pin, then you either need to use

  1. CASE statements
  2. Arrays of access types/addresses (using the pin type as the index).
  3. A way to calculate the register address and mask from the pin and use that on a locally declared variable's address attribute inside a function call using a pin as a parameter.

You can't really have individual record components addressed differently (this is true in C and C++ as well).

Jere
  • 3,124
  • 7
  • 15
  • Thanks for this. I spent sometime reviewing overlays thanks to your pointers. However that doesn’t do what I think I need. In C, I use a typedef to lug around all the key parameters for the GPIO, namely pin, pin reg, dd reg and port reg. I think I’m looking for an approach that supports this. – frank Dec 07 '18 at 05:28
  • The direct compliment to your C example which uses pointers is to use access types. See my comment on your answer above. – Jere Dec 07 '18 at 12:12
0

After a bit of thought, I decided to follow on what I already do in C. There, I have the following typedef defined

typedef struct {
    IO_REG_TypeDef_t portr;
    IO_REG_TypeDef_t ddr;
    IO_REG_TypeDef_t pinr;
    volatile uint8_t pin;
} GPIO_TypeDef_t;

And IO_REG_t is itself defined as

typedef struct {
    volatile uint8_t* io_reg;
} IO_REG_TypeDef_t;

So clearly the key parameters for the gpio are lugged around in a typedef. I thought to do the same in Ada. Again, forgive me if I am speaking C in Ada; feel free to suggest more Ada-standard approaches.

I define the gpio pin components:

   -- GPIO15 is PB1 on ATmega328p 28 DIP
   gpio15_pin_reg : Unsigned_8;
   for gpio15_pin_reg'Address use Atmega328p.PINB'Address;
   gpio15_dd_reg : Unsigned_8;
   for gpio15_dd_reg'Address use Atmega328p.DDRB'Address;
   gpio15_port_reg : Unsigned_8;
   for gpio15_port_reg'Address use Atmega328p.PORTB'Address;
   gpio15_pin : constant Unsigned_8 := 1;

Register read & write functions are defined:

   procedure core_reg_write (reg: in out Unsigned_8; value: in Unsigned_8) is
   begin
      reg := value;
   end core_reg_write;

   function core_reg_read (reg: in Unsigned_8) return Unsigned_8 is
      value : Unsigned_8;
   begin
      value := reg;
      return value;
   end core_reg_read;

Then a record is defined, this time, to lug around the pin variable and, instead of variables for the pin, dd and port registers, their addresses instead:

   type gpio_t is record
      pin       : Unsigned_8;
      pin_reg_addr  : System.Address;
      dd_reg_addr   : System.Address;
      port_reg_addr     : System.Address;
   end record;

The record for a given gpio pin is assembled:

   gpio15 : gpio_t := (gpio15_pin, gpio15_pin_reg'Address, gpio15_dd_reg'Address, gpio15_port_reg'Address);

Procedures that take this record and set parameters of the pin are defined:

   procedure gpio_output (gpio : in gpio_t) is
      dd_reg : Unsigned_8;
      for dd_reg'Address use gpio.dd_reg_addr;
   begin
      core_reg_write(dd_reg, core_reg_read(dd_reg) or shift_left(1,integer(gpio.pin)));
   end gpio_output;

   procedure gpio_hi (gpio : in gpio_t) is
      port_reg : Unsigned_8;
      for port_reg'Address use gpio.port_reg_addr;
   begin
      core_reg_write(port_reg, core_reg_read(port_reg) or shift_left(1,integer(gpio.pin)));
   end gpio_hi;

   procedure gpio_lo (gpio : in gpio_t) is
      port_reg : Unsigned_8;
      for port_reg'Address use gpio.port_reg_addr;
   begin
      core_reg_write(port_reg, core_reg_read(port_reg) and not shift_left(1,integer(gpio.pin)));
   end gpio_lo;

In each of these procedures, the required registers are, for lack of a better description, manually dereferenced.

The following sequence follows the begin keyword:

 -- Initialize
   gpio_output(gpio15); 

 For_loop_0:
   loop

    -- turn on
        gpio_hi(gpio15);

        -- loop
      Lazy_delay_1:
    for I in Unsigned_32 range 0 .. 100_000 loop
        null;
    end loop Lazy_delay_1;

    -- turn off
        gpio_lo(gpio15);

        -- loop
      Lazy_delay_2:
    for I in Unsigned_32 range 0 .. 100_000 loop
        null;
    end loop Lazy_delay_2;

   end loop For_loop_0;

And the led blinks.

This achieves what I want but I’m open to other approaches that take a composite gpio_t-like type and don’t require manual dereferencing of the address/pointer.

frank
  • 13
  • 3
  • I'm confused by your statement. In the C example, you would have been doing manual dereferencing as well since IO_REG_TypeDef_t is just a container for a pointer in C. If you mean using the Addresses, then just use Access types instead. You create an aliased variable of type Unsigned_8 (from the standard Interfaces package). You create an access type for it (type Unsigned_8_Access is access all Unsigned_8). Then you use that access type to pass around the register pointer (like you are doing in your C example). It would be the type of io_reg in your IO_REG_TypeDef_t struct/record. – Jere Dec 07 '18 at 12:11
  • If that doesn't make sense, I'll try and gen up an example when I get back from work later today. – Jere Dec 07 '18 at 12:32
  • Thanks for the example; it was pretty useful. I've accepted it. – frank Dec 14 '18 at 02:29
0

Ok, after looking at your example, I came up with a similar solution in Ada. That said, I don't really care for how exposed access types are here. I'll leave my previous answer since I feel using records directly is a better method overall, but to specifically answer your question, here is an example I tested out in GNAT GPL 2017 using a handmade runtime (for another chip, but it was enough to verify compilation). Trying to compile it in a non embedded version of GNAT met with compiler crashes (I am assuming because the addresses were bad for windows). Hopefully this gives an example that better fits your personal requirements

registers.ads

with Interfaces;

-- Basic Register type and functionality
package Registers with Pure is

   type Register is limited private;
   type Register_Access is access all Register with Storage_Size => 0;

   procedure Core_Reg_Write
      (Target : not null Register_Access;
       Value  : Interfaces.Unsigned_8)
      with Inline;

   function  Core_Reg_Read
      (Source : not null Register_Access) 
       return Interfaces.Unsigned_8
      with Inline;

private

   type Register is limited record
      Value : Interfaces.Unsigned_8;
   end record
      with Volatile, Size => 8;

end Registers;

registers.adb

package body Registers is

   procedure Core_Reg_Write
      (Target : not null Register_Access;
       Value  : Interfaces.Unsigned_8)
   is begin

      Target.Value := Value;

   end Core_Reg_Write;

   function  Core_Reg_Read
      (Source : not null Register_Access) 
       return Interfaces.Unsigned_8
   is begin

      return Source.Value;

   end Core_Reg_Read;

end Registers;

io_registers.ads

with Registers;

-- Specific Register types and functionality
package IO_Registers with Pure is

   -- Use different ones for each register to avoid accidental copy/paste
   -- errors.
   type Port_Register is new Registers.Register_Access;
   type DD_Register   is new Registers.Register_Access;
   type Pin_Register  is new Registers.Register_Access; 

   type Pin_Number is new Positive range 1 .. 8;      

   type GPIO_Register is record
      Port_Reg : Port_Register;
      DD_Reg   : DD_Register;
      Pin_Reg  : Pin_Register;
      Pin      : Pin_Number;
   end record;

end IO_Registers;

predefined_registers.ads

with Registers;
with System;

package Predefined_Registers is

   -- Fake addresses here, since I don't have your atmega package
   GPIO_15_Pin_Reg : aliased Registers.Register
      with 
         Address => System'To_Address(16#80000400#),
      Volatile,
      Convention => C,
      Import;

   GPIO_15_DD_Reg : aliased Registers.Register 
      with 
         Address => System'To_Address(16#80000401#),
      Volatile,
      Convention => C,
      Import;       

   GPIO_15_Port_Reg : aliased Registers.Register
      with 
         Address => System'To_Address(16#80000402#),
      Volatile,
      Convention => C,
      Import;

   GPIO_15_Pin : constant := 1;

end Predefined_Registers;

program.adb

with IO_Registers;
with Predefined_Registers;

procedure Program is
   GPIO_15 : IO_Registers.GPIO_Register :=
               (Port_Reg => Predefined_Registers.GPIO_15_Port_Reg'Access,
                Pin_Reg  => Predefined_Registers.GPIO_15_Pin_Reg'Access,
                DD_Reg   => Predefined_Registers.GPIO_15_DD_Reg'Access,
                Pin      => Predefined_Registers.GPIO_15_Pin);
begin
   -- Notice the use of IO_Registers for this call.  The new types were
   -- created there, so the corresponding ops were too
   IO_Registers.Core_Reg_Write(GPIO_15.Port_Reg,16#01#);
end Program;
Jere
  • 3,124
  • 7
  • 15
  • Thanks for this. Loaded this, switched compiler from ada2005 to ada2012, modified device addresses and had only 3 copies of 1 error (from Import) in package Predefine_Registers: missing Convention aspect for Export/Import Not sure of fix so just dropped “Import”. -- Fake addresses here, since I don't have your atmega package GPIO_15_Pin_Reg : aliased Registers.Register with --Address => System'To_Address(16#80000400#), Address => Atmega328p.PINB'Address, --Volatile, --Import; Volatile; And the led blinks. – frank Dec 14 '18 at 02:14
  • You want to keep the import because some compilers will try to "initialize" your register if not. To fix it, add the aspect Convention => C if your compiler requires a convention. – Jere Dec 15 '18 at 00:07
  • I updated the example, though I didn't have a compiler to double check with at this time. – Jere Dec 15 '18 at 00:13
  • Tested. Works. Thanks. – frank Dec 17 '18 at 14:46
0

After playing around a bit, in this online compiler (https://www.tutorialspoint.com/compile_ada_online.php) I got this working:


with Ada.Text_IO; use Ada.Text_IO;
with Interfaces; use Interfaces;
with System; use System;

procedure Hello is

    -- pseudo hardware registers, unknown addresses, known contents
    temp0 : interfaces.unsigned_8 := 2#00000101#; -- pinr
    temp1 : interfaces.unsigned_8 := 2#10000000#; -- ddr
    temp2 : interfaces.unsigned_8 := 2#10000000#; -- portr

    -- core
    type io_reg_t is limited record
        io_reg  :   interfaces.unsigned_8;
    end record;
    pragma volatile(io_reg_t); --  Verify relevance.

    -- processor
    gpio15_pinr : aliased io_reg_t;
    for gpio15_pinr'address use temp0'address;
    gpio15_ddr : aliased io_reg_t;
    for gpio15_ddr'address use temp1'address;    
    gpio15_portr : aliased io_reg_t;
    for gpio15_portr'address use temp2'address;
    gpio15_pin : constant interfaces.unsigned_8 := 1;

    procedure core_reg_write_old (reg: in out unsigned_8; value: in unsigned_8) is
    begin
      reg := value;
    end core_reg_write_old;

    procedure core_reg_write (reg: access io_reg_t; value: in unsigned_8) is
    begin
      reg.io_reg := value;
    end core_reg_write;

    function core_reg_read (reg: access io_reg_t) return Unsigned_8 is
    begin
      return reg.io_reg;
    end core_reg_read;    

    -- gpio
    type gpio_t is record
        pinr    :   access io_reg_t;
        ddr     :   access io_reg_t;
        portr   :   access io_reg_t;
        pin     :   interfaces.unsigned_8;
    end record;
    pragma volatile(gpio_t); -- Verify relevance.

    procedure gpio_output (gpio : in gpio_t) is
    begin
        core_reg_write(gpio.ddr,core_reg_read(gpio.ddr) or shift_left(1,integer(gpio.pin)));
    end gpio_output;

    procedure gpio_hi (gpio : in gpio_t) is
    begin
        core_reg_write(gpio.portr,core_reg_read(gpio.portr) or shift_left(1,integer(gpio.pin)));
    end gpio_hi;

    procedure gpio_lo (gpio : in gpio_t) is
    begin
        core_reg_write(gpio.portr,core_reg_read(gpio.portr) and not shift_left(1,integer(gpio.pin)));
    end gpio_lo; 

    gpio15 : gpio_t := (
        pinr    => gpio15_pinr'access,
        ddr     => gpio15_ddr'access,
        portr   => gpio15_portr'access,
        pin     => gpio15_pin
    );

    -- led
    type led_t is record
        gpio    :   gpio_t;
    end record;

    led0 : led_t := (gpio => gpio15);

    procedure led_init (led : in led_t) is
    begin
        gpio_output(led.gpio);
    end led_init;

    procedure led_on (led : in led_t) is
    begin
        gpio_hi(led.gpio);
    end led_on;

    procedure led_off (led : in led_t) is
    begin
        gpio_lo(led.gpio);
    end led_off;

begin
  put_line("Hello, world!");
  -- Does it match the original value of 5?
  put_line(gpio15.pinr.io_reg'Image);

  -- Does modification via variable alter the value returned?
  temp0 := 203;
  put_line(gpio15.pinr.io_reg'Image);

  -- Does modification via record alter the value returned?
  gpio15.pinr.io_reg := 89;
  put_line(gpio15.pinr.io_reg'Image);

  -- Writes value in temp2 (128) to temp0.
  core_reg_write_old(temp0,temp2);

  put_line(gpio15.pinr.io_reg'Image);
  put_line(gpio15.ddr.io_reg'Image);
  put_line(gpio15.portr.io_reg'Image);
  put_line(gpio15.pin'Image);

  -- Writes value of pin (1) to pinr via record.  
  --core_reg_write(gpio15.ddr,gpio15.pin);

  -- Writes 1 shifted value of pin times and or's that with ddr reg
  --gpio_output(gpio15);
  led_init(led0);

  put_line(gpio15.pinr.io_reg'Image);
  put_line(gpio15.ddr.io_reg'Image);
  put_line(gpio15.portr.io_reg'Image);
  put_line(gpio15.pin'Image);

  --gpio_hi(led0.gpio);  
  led_on(led0);

  put_line(gpio15.pinr.io_reg'Image);
  put_line(gpio15.ddr.io_reg'Image);
  put_line(gpio15.portr.io_reg'Image);
  put_line(gpio15.pin'Image);

  --gpio_lo(led0.gpio);   
  led_off(led0);

  put_line(gpio15.pinr.io_reg'Image);
  put_line(gpio15.ddr.io_reg'Image);
  put_line(gpio15.portr.io_reg'Image);
  put_line(gpio15.pin'Image);

end Hello;

I modified this for my embedded environment but it failed to compile, with the complaint:

undefined reference to `__gnat_last_chance_handler’

for the lines “reg.io_reg := value” and “return reg.io_reg”.

I found out that I actually didn’t need the last_chance_handler if my access types were explicitly declared to be “not null”.

So the updated program became:


with Interfaces; use Interfaces;
with System;
with Atmega328p;

procedure Main is

   -- core
    type io_reg_t is limited record
        io_reg  :   interfaces.unsigned_8;
    end record;
    pragma volatile(io_reg_t); -- Verify relevance.

    type dd_io_reg_t is new io_reg_t;

    -- Location?
    gpio15_pinr : aliased io_reg_t;
    for gpio15_pinr'address use Atmega328p.PINB'Address;
    gpio15_ddr : aliased io_reg_t;
    for gpio15_ddr'address use Atmega328p.DDRB'Address;    
    gpio15_portr : aliased io_reg_t;
    for gpio15_portr'address use Atmega328p.PORTB'Address;
    gpio15_pin : constant interfaces.unsigned_8 := 1; 

    procedure core_reg_write (reg: not null access io_reg_t; value: in interfaces.unsigned_8) is
    begin
        reg.io_reg := value;
    end core_reg_write;

    function core_reg_read (reg: not null access io_reg_t) return interfaces.unsigned_8 is
    begin
        return reg.io_reg;
    end core_reg_read;    

    -- gpio
    type gpio_t is record
        pinr    :   not null access io_reg_t;
        ddr     :   not null access io_reg_t;
        portr   :   not null access io_reg_t;
        pin     :   interfaces.unsigned_8;
    end record;
    pragma volatile(gpio_t); --  Verify relevance.

    -- gpio_output
    procedure gpio_output (gpio : in gpio_t) is
    begin
        core_reg_write(gpio.ddr,core_reg_read(gpio.ddr) or shift_left(1,integer(gpio.pin)));
    end gpio_output;

    procedure gpio_hi (gpio : in gpio_t) is
    begin
        core_reg_write(gpio.portr,core_reg_read(gpio.portr) or shift_left(1,integer(gpio.pin)));
    end gpio_hi;

    procedure gpio_lo (gpio : in gpio_t) is
    begin
        core_reg_write(gpio.portr,core_reg_read(gpio.portr) and not shift_left(1,integer(gpio.pin)));
    end gpio_lo; 

    gpio15 : gpio_t := (
        pinr    => gpio15_pinr'access,
        ddr     => gpio15_ddr'access,
        portr   => gpio15_portr'access,
        pin     => gpio15_pin
    );

    -- led
    type led_t is record
        gpio    :   gpio_t;
    end record;

    led0 : led_t := (gpio => gpio15);

    procedure led_init (led : in led_t) is
    begin
        gpio_output(led.gpio);
    end led_init;

    procedure led_on (led : in led_t) is
    begin
        gpio_hi(led.gpio);
    end led_on;

    procedure led_off (led : in led_t) is
    begin
        gpio_lo(led.gpio);
    end led_off;

begin

    -- Initialize
    -- Writes value of pin (1) to pinr via record.  
    --core_reg_write(gpio15.ddr,gpio15.pin);

    -- Writes 1 shifted value of pin times and or's that with ddr reg
    --gpio_output(gpio15);
    led_init(led0);

 For_loop_0:
   loop

        -- turn on
        --gpio_hi(led0.gpio);  
    led_on(led0);

        -- loop
      Lazy_delay_1:
    for i in interfaces.unsigned_32 range 0 .. 100_000 loop
        null;
    end loop Lazy_delay_1;

        -- turn off
        --gpio_lo(led0.gpio);   
    led_off(led0);

        -- loop
      Lazy_delay_2:
    for i in interfaces.unsigned_32 range 0 .. 100_000 loop
        null;
    end loop Lazy_delay_2;

   end loop For_loop_0;

end Main;

After this modification I compiled it burned it into the microcontroller.

And the led blinks.

I'll use this moving forward.

frank
  • 13
  • 3