维护Ada中记录成员的固定内存地址

时间:2018-12-01 23:55:09

标签: avr ada atmega gnat gnat-gps

我三天前安装了GNAT-GPS和AVR-ELF来玩。我眨眼就想去玩一些。我没有非VHDL Ada经验。

这是我在C语言中工作的场景:

我进行了设置,以便使用GPIO typedef,我可以参考设置GPIO引脚所需的所有信息(即引脚编号,引脚寄存器地址,dd寄存器地址和端口寄存器地址)。然后,我对LED0做同样的事情,以便在逻辑上可以将LED0连接到GPIO15,而GPIO15本身又连接到AVR微控制器的PB1。

我尝试在Ada中做同样的事情。我觉得我可能正在用Ada编写C。随便让我知道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;

这是为了让我编写以下功能:

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

如果使用此功能,我发现LED正确初始化:

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

如果我还能得到的话甚至更好

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

很抱歉,这个问题很长;希望它能使事情变得清晰起来。

4 个答案:

答案 0 :(得分:0)

通过两种类型的分配,您无法真正获得想要的东西。所要做的只是复制当前值,而不是寄存器地址。这是一个选项:

创建一个与您的gpio_t类型相似的类型,但使其与微控制器的寄存器映射完全匹配。这意味着您不会在其中存储引脚号,并且需要包括所有周围的寄存器。这是我从另一个文件中找到的另一个Micro的示例,但希望可以作为示例

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. 一种从引脚计算寄存器地址和掩码,并使用引脚作为参数的函数调用中的本地声明变量的address属性上使用其地址的方法。

您不能真正使单个记录组件的寻址方式有所不同(在C和C ++中也是如此)。

答案 1 :(得分:0)

经过一番思考,我决定继续执行我在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中找到的。我想在Ada做同样的事情。同样,如果我在阿达(Ada)讲C,请原谅我。随时提出更多的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复合类型并且不需要手动取消引用地址/指针的其他方法。

答案 2 :(得分:0)

好的,在看完您的示例后,我在Ada中提出了类似的解决方案。就是说,我不太在乎此处的公开访问类型。我会保留以前的答案,因为我觉得直接使用记录是整体上更好的方法,但是为了具体回答您的问题,这是我使用手工运行时在GNAT GPL 2017中测试的一个示例(对于另一个芯片,但这足够了验证编译)。尝试在非嵌入式GNAT版本中进行编译会导致编译器崩溃(我认为是因为地址对Windows不利)。希望这能提供一个更适合您个人需求的示例

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;

答案 3 :(得分:0)

经过一番尝试之后,在此在线编译器(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;

修改后,我将其烧录到微控制器中。

然后指示灯闪烁。

我将继续使用它。