1

This might be a nobrainer but as a novice i can not get my head around it.

I have a function returning a fixed size array. I am trying to convert this array into a record of the same size

The function signature is like this:

type Control is new UInt8_Array (1 .. 2);
function readControl (Command : Control) return Control;

I am trying to get the two bytes (UInt8) into the record Contro_Status_Bytes with the following definition:

 type Control_Status_MSB is
  record
     RES_UP     : Boolean;
     QMAX_UP    : Boolean;
     BCA        : Boolean;
     CCA        : Boolean;
     CALMODE    : Boolean;
     SS         : Boolean;
     WDRESET    : Boolean;
     SHUTDOWNEN : Boolean;
  end record;

for Control_Status_MSB use
  record
     RES_UP     at 0 range 0 .. 0;
     QMAX_UP    at 0 range 1 .. 1;
     BCA        at 0 range 2 .. 2;
     CCA        at 0 range 3 .. 3;
     CALMODE    at 0 range 4 .. 4;
     SS         at 0 range 5 .. 5;
     WDRESET    at 0 range 6 .. 6;
     SHUTDOWNEN at 0 range 7 .. 7;
  end record;


type Control_Status_LSB is
  record
     VOK       : Boolean;
     RUP_DIS   : Boolean;
     LDMD      : Boolean;
     SLEEP     : Boolean;
     HIBERNATE : Boolean;
     INITCOMP  : Boolean;
  end record;

for Control_Status_LSB use
  record
     VOK at 0 range 1 .. 1;
  end record;

type Control_Status_Bytes is
  record
     HighByte : Control_Status_MSB;
     LowByte  : Control_Status_LSB;
  end record;

I think it must be possible to convert the array to the record and vice versa without an unchecked conversion. But currently i am missing something.

Update: This might be an valid answer/way to do that i came up after reading @Simons answer.

function readControl (Command : Control) return Control_Status_Bytes is
  CSB : Control_Status_Bytes;
begin
  -- do stuff return UInt8_Array of size 2 as response
  CSB.HighByte := response'First;
  CSB.LowByte  := response'Last;
  return CSB;
end readControl;
Dreanaught
  • 71
  • 1
  • 10
  • I also suggest to add the `Bit_Order` property for the `Control_Status_Bytes`. By default it is `Low_Order_First`, but in general it is up to the compiler to decide. Thus, you may get a different result depending on the target platform. – NeoSer Jan 28 '18 at 07:24

3 Answers3

3

Unchecked conversion is the usual way.

But for I/O ports and peripheral registers in MCUs (Atmel AVR, MSP430 etc) which can be addressed either as numbers, or arrays of booleans (or potentially, records) there's a hack ...

   p1in                   : constant unsigned_8;     --  Port 1 Input 
   Pragma Volatile(p1in);
   Pragma Import(Ada, p1in);      -- see ARM C.6 (13)
   For p1in'Address use   16#20#;

   p1in_bits              : constant Byte;           --  Port 1 Input  Bits
   Pragma Volatile(p1in_bits);
   Pragma Import(Ada, p1in_bits);
   For p1in_bits'Address use   16#20#;

This maps the inputs from I/O port 1 to the same address, viewed either as an 8 bit Unsigned or as a Byte (an array of 8 booleans).

The equivalent in your case would be something like

For Control_Status_Record'Address use Control_Status_Array`Address;

Note you probably need to attach "pragma volatile" to both views, as here, so that changes to one view aren't lost because the other view is cached in a register.

All in all, I recommend Unchecked_Conversion over this approach. It's designed for the job and avoids messing with Volatile.

1

It has to depend on what happens inside readControl, but couldn't you make it return the type you want directly?

function readControl (Command : Control) return Control_Status_Bytes;

(I expect that Command actually has some structure too?).

By the way, you only define the position of one component (VOK) in Control_Status_LSB, which leaves the rest up to the compiler.

Simon Wright
  • 25,108
  • 2
  • 35
  • 62
  • You are right `Control_Status_LSB` misses some componets. If i follow your advice the problem arises inside `readControl`. The `UInt8_Array` of fixed size 2 has to be converted to the record type (also containing 2 UInt8 ). I would love to do it like [here](https://en.wikibooks.org/w/index.php?title=Ada_Programming/Type_System&section=22#Checked_conversion_for_non-numeric_types) but i cannot use `UInt8_Array` in the for clause. Unfortunately a `record` is required – Dreanaught Jan 26 '18 at 09:05
  • This is the way. Accessors divorce the internal representation from the user's view. If you change your mind on the internal representation, you simply adjust the accessors to suit. –  Jan 26 '18 at 10:42
  • OK, UC it is, then. – Simon Wright Jan 26 '18 at 11:36
1

The hint from @Simon Wright pointed me in the right direction.

This is what is use now and it works:

function convert (ResponseArray : Control) return Control_Status_Bytes is
   Result : Control_Status_Bytes with
     Import, Convention => Ada, Address => ResponseArray'Address;
begin
   return Result;
end convert;
Dreanaught
  • 71
  • 1
  • 10
  • This is not guaranteed to work. You should rather use `Unchecked_Conversion`. – Jacob Sparre Andersen Jan 27 '18 at 18:46
  • I am curious as to what problem can arise, that makes it stop working. By the way, Ada 2012 is used – Dreanaught Jan 28 '18 at 16:56
  • I can't remember the list of reasons that it may not work, but Randy Brukardt (the editor of the standard) explains it regularly on the `comp.lang.ada` newsgroup. Basically you should only expect address clauses to work for interfacing purposes (hardware ports, etc.). – Jacob Sparre Andersen Jan 28 '18 at 17:44