Поддержание фиксированных адресов памяти для записи членов в Ada - PullRequest
0 голосов
/ 02 декабря 2018

Я установил GNAT-GPS и AVR-ELF 3 дня назад, чтобы поиграть.Я вздрогнул и подумал, что могу поиграть еще.У меня нет опыта работы не с VHDL Ada.

Вот сценарий, который я работаю в C:

Я настроил его так, чтобы с помощью typedef GPIO я мог обращаться ко всей необходимой информациинастроить вывод GPIO (т. е. номер контакта, адрес рег. контакта, адрес рег. dd и адрес рег. порта).Затем я делаю то же самое для, скажем, LED0, так что логически я могу подключить LED0 к GPIO15, который сам подключен к PB1 микроконтроллера AVR.

Я пытаюсь сделать то же самое в Ada.Я чувствую, что могу написать С на Аде;не стесняйтесь сообщить мне впоследствии, если есть лучший способ сделать это в Ada.

Я настроил регистры AVR для определенного контакта, чтобы подключиться к его короткому названию ссылки:

       -- 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;

Затем я устанавливаю ссылку на его короткое имя для подключения к номеру контакта пакета:

   -- 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;

Затем я определяю запись для хранения всех параметров контакта:

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

Thisпозволяет мне написать следующую функцию:

 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;

В будущем я буду искать ее как:

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;

Эта функция gpio_map используется для подключениявывод пакета gpio_t на номер вывода пакета:

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

Я считаю, что светодиод правильно инициализируется, если я использую эту функцию:

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

Но инициализируется неправильно, если я:

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

Это, однако, работает:

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

Мне ясно, что у меня есть

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

И что

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

Как мне сохранить фиксированные адреса памяти для записей членов, то есть получить

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

И еще лучше яf Я также могу получить

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

Извините за длинный вопрос;надеясь, что это поможет прояснить ситуацию.

Ответы [ 4 ]

0 голосов
/ 14 декабря 2018

Немного поиграв, в этом онлайн-компиляторе (https://www.tutorialspoint.com/compile_ada_online.php) у меня все заработало:


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;

Я изменил это для своей встроенной среды, но это не удалосьскомпилировать с жалобой:

undefined reference to `__gnat_last_chance_handler’

для строк «reg.io_reg: = value» и «return reg.io_reg».

Я обнаружил, что на самом деле ненужен обработчик last_chance_handler, если мои типы доступа были явно объявлены как «не нулевые».

Таким образом, обновленная программа стала:


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;

После этой модификации я скомпилировал еесжег его в микроконтроллере.

И светодиод мигает.

Я буду использовать это движение вперед.

0 голосов
/ 07 декабря 2018

Подумав немного, я решил продолжить то, что я уже делаю в C. Там у меня есть следующий typedef, определенный

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

И IO_REG_t сам по себе определен как

typedef struct {
    volatile uint8_t* io_reg;
} IO_REG_TypeDef_t;

Так что ключевые параметры для gpio явно указываются в typedef.Я думал сделать то же самое в Аде.Снова, прости меня, если я говорю на C на языке Ada;не стесняйтесь предлагать больше стандартных подходов Ada.

Я определяю компоненты булавки gpio:

   -- 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;

Определены функции чтения и записи регистра:

   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;

Тогдана этот раз определяется запись для перемещения по переменной pin, а вместо переменных для регистров pin, dd и port вместо них указываются их адреса:

   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;

Запись для данного вывода gpio собирается:

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

Определены процедуры, которые берут эту запись и устанавливают параметры вывода:

   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;

В каждой из этих процедур необходимы регистры, из-за отсутствия лучшего описания, разыменовывается вручную.

Следующая последовательность следует за ключевым словом begin:

 -- 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;

И светодиод мигает.

Это позволяет достичь того, что я хочу, но я открыт для другихподходы, которые используют составной gpio_t-подобный тип и не требуют разыменования вручную адреса / указателя.

0 голосов
/ 08 декабря 2018

Хорошо, посмотрев на ваш пример, я нашел похожее решение в Аде.Тем не менее, мне все равно, какие здесь типы доступа.Я оставлю свой предыдущий ответ, так как считаю, что использование записей напрямую - это лучший способ, но чтобы конкретно ответить на ваш вопрос, вот пример, который я тестировал в GNAT GPL 2017 с использованием среды выполнения, созданной вручную (для другого чипа, но этого было достаточнопроверить сборник).Попытка скомпилировать его в не встроенной версии GNAT привела к сбоям компилятора (я полагаю, потому что адреса были плохими для окон).Надеемся, что это дает пример, который лучше соответствует вашим личным требованиям

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;

предопределенный_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;
0 голосов
/ 02 декабря 2018

Вы не можете получить то, что хотите, назначив два типа.Все, что делает, это копирует текущие значения, а не адреса регистров.Вот вариант:

Создайте тип, аналогичный вашему типу gpio_t, но сделайте его точно таким же, как карта регистров для вашего микро.Это означает, что вы не будете хранить в нем пин-код, и вам нужно будет включить все окружающие регистры.Вот пример, который я нашел в другом файле для другого микро, но, надеюсь, послужит примером

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;

Тип записи должен быть ограничен, чтобы вы могли убедиться, что он передается по ссылке, а не по копии.

Примечание. Вместо этого вы можете использовать предложение для представления структуры байтов и битов структуры.Это будет зависеть от компилятора, который вы используете.

После того как регистры вашего микро выстроены в соответствии с таблицей данных, вы создаете переменную и сопоставляете ее с нужным адресом, как вы делали с отдельными переменными

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

Это сопоставит всю переменную записи с этим адресом.

После этого вам нужно изменить вызовы функций, чтобы они воспринимали всю запись как параметр, а не только регистр.В качестве примера:

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

Если вам нужно, чтобы вещи были более причудливыми, а правильные регистры и значение маски выбирались через пин-код, то вам нужно либо использовать

  1. CASEзаявления
  2. Массивы типов доступа / адресов (с использованием типа пин-кода в качестве индекса).
  3. Способ вычисления адреса и маски регистра из вывода и его использования в атрибуте адреса локально объявленной переменной внутри вызова функции с использованием вывода в качестве параметра.

Вы можетена самом деле отдельные компоненты записей не имеют адреса (это также верно для C и C ++).

...