0

I have a chunk (1024 bytes) of shared memory between two processes for which I have an address pointing to. I want to copy some data to this shared memory, and read it on the other process. Coming from a C background, it seems easiest to map a record to this address, and then write to the record, but it does not seem to be copying correctly.

Currently, I am trying to convert the pointer to a pointer-to-record type using an Unchecked Conversion, and copy to the record, but I am seeing differences in the data when I compare the original payload with the one received in the second process.

Is this the proper way of doing this?:

type Payload_Array_Type is array (1..255) of Integer_32;

type Common_Buffer_Type is
  record
    Size : Integer_32;
    Payload : Payload_Array_Type;
  end record;

type Common_Buffer_Ptr_Type is access Common_Buffer_Type;

function Convert_Common_Memory_Ptr is new Unchecked_Conversion (
    Source => System.Address,
    Target => Common_Buffer_Ptr_Type);

Common_Memory_Ptr : System.Address;

procedure Copy_To_Common_Buffer
(
    Size : Integer_32;
    Payload : Payload_Array_Type
) is
    Common_Buffer_Ptr : Common_Buffer_Ptr_Type;
begin
    Common_Buffer_Ptr := Convert_Common_Memory_Ptr(Common_Memory_Ptr);
    Common_Buffer_Ptr.Size := Size;
    Common_Buffer_Ptr.Payload(1..255) := Payload(1..255);    
end Copy_To_Common_Buffer;
Casey Kuball
  • 7,717
  • 5
  • 38
  • 70
  • 2
    Could you turn this fragment into a complete example which demonstrates the problem? A few comments : (1) you declare `Common_Memory_Pointer` but never initialise it so it could point anywhere or nowhere. (2)There should be no need for Unchecked_Conversion in basic programming like this (3) Why assign separate components to a record when you could just assign another record? (4) If it's shared between two Ada tasks it should probably be a protected object. –  Jul 18 '15 at 18:45
  • 1
    @Brian, it’s shared between two _processes_. Darthfett, `System.Address_To_Access_Conversions` would be more “proper”, but probably won’t work any better. When you obtain the address from whatever mapping mechanism you’re using, it’s usually simpler to declare it in Ada as an access-to-your-record-type straight away, rather than bothering with conversions. – Simon Wright Jul 18 '15 at 18:58
  • @Simon, it's not clear to me if those *processes* are both Ada tasks, under a single environment task - or separate executables independently launched from the OS? If the latter, then (a) protected objects won't help. (b) probably need to use representation aspects to specify the precise memory layout in the same way in both executables. Unless you can guarantee they are both built with same compiler, flags, etc. –  Jul 18 '15 at 19:13
  • @BrianDrummond I really wish I could, but I'm working on a legacy codebase for an embedded system. I'm still new to Ada, so I lack the experience to write the code to create processes and such. The two processes are both Ada processes, built with the same compiler (likely also flags). – Casey Kuball Jul 18 '15 at 19:41
  • (1) Is there an associated representation clause? (2) Is the other process also written in Ada? (3) Is this other process on the same architecture? aka, is it an endian(ness) problem? – NWS Jul 18 '15 at 19:46
  • @NWS There is no representation clause on the record, although the individual elements have something like `for Integer_32'Size use 32`. Other process is also Ada, same architecture and compiler, etc. – Casey Kuball Jul 18 '15 at 19:51
  • OK. ... for an embedded system. might be worth saying which compiler, CPU if you can ... add info to question, there are too many comments and I'm not helping that any! Also ... (1) you can specify offsets within a record as well as component size, to lock down the layout (in case of padding differences). (2) is there any pattern or anything you can say about the "differences"? (3) if you can't make it a protected object, declare it with `pragma volatile` so its reader actually reads it and doesn't just assume its contents are unchanged. –  Jul 18 '15 at 20:30

1 Answers1

2

I would try to do it this way:

procedure Payload is 

   type Payload_Array_Type is array (1..255) of Integer_32;

   type Common_Buffer_Type is record
      Size : Integer_32;
      Payload : Payload_Array_Type;
   end record;
   for Common_Buffer_Type use record -- representation clause should be common to both processes
      Size    at 0 range 0 .. 31;
      Payload at 0 range 32 .. 1023;
   end record;
   for Common_Buffer_Type'Size use 1024; -- check this is also used in the other process.

   type Common_Buffer_Ptr_Type is access Common_Buffer_Type;

   Common_Memory_Ptr : System.Address; -- assuming this is where the shared object resides with a real address, possibly make it constant

   procedure Copy_To_Common_Buffer (Size    : in Integer_32;
                    Payload : in Payload_Array_Type) is
      Common_Buffer : Common_Buffer_Type;
      for Common_Buffer'Address use Common_Memory_Ptr; -- address overlay
   begin
      Common_Buffer := (Size => Size,
            Payload => Payload);
   end Copy_To_Common_Buffer;

begin

   Copy_To_Common_Buffer (9,(others => 876));

end Payload;

The type definitions should be common to the two processes, and note I've used a representation clause to specify where the components go.

I've used an address overlay to specify the location of where I'm writing, and written the whole record in one go.

Also look up usage of pragma volatile as @Brian Drummond suggests.

Marc C
  • 8,664
  • 1
  • 24
  • 29
NWS
  • 3,080
  • 1
  • 19
  • 34
  • Going to accept this, as this looks like the best way to do it. The problem I had ended up being some sort of hardware issue, as the whole 32-bit word had to be written at once, or else the data would not be written correctly. Appreciated this at the time, but must have forgotten to accept/upvote! – Casey Kuball Jan 16 '16 at 05:48