Harry
Harry

Reputation: 33

Read Write to Memory space

I am trying to write a Signed Double number to memory and read back the same, reading back is redundant as it is just to verify if the correct data is in the memory before I trigger the PL (programmable logic) FPGA Fabric to access this data and perform a task.

I read a file into a double (part of a union) and then write to memory through the unsigned long (part of a union). But the data before writing into memory and after I read out is wrong, it is just the last byte. (see the code and comments for details)

union floatpun {
    double dw;
    unsigned long lw;
};

void *lookup_slave_by_phy_addr(const int fd, const off_t target, const size_t mapsize)
{
  void *map_base, *virt_addr;

  /* Map one page */
  map_base = mmap(0, mapsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, target & ~(mapsize-1));
  if (map_base == (void *) -1) { FATAL; }
  printf("Memory mapped at address %p.\n", map_base);
  fflush(stdout);

  virt_addr = map_base + (target & (mapsize-1));
  return virt_addr;
}


int main(int argc, char *argv[])
{  
  union floatpun conv;

  FILE *fp;
  fp = fopen("/media/card/numbers.txt", "r");
  fscanf(fp,"%lf",&conv.dw);  // 0.009101592004299160 Reads this number from the file, which is correct as expected.
  printf("The value Read from the File is %lx \n",conv.lw); // Prints 3f 82 a3 da ff ff ff fe, which is also correct.
  fclose(fp);

  int fd;
  if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) { FATAL; }
  printf("/dev/mem opened.\n");
  fflush(stdout);

  void *weights;
  // Map systemMemory master phy address range 0000 0008 7000 0000 -> 0000 0008 7FFF FFFF
  weights       = lookup_slave_by_phy_addr(fd,strtoul("0x0000000870000000",0,0),MAP_SIZE_256M);
    *((unsigned char *) (weights+0x00))  = conv.lw;   // Writing into the mempory
    SysMem= *((unsigned char *) (weights+0x00));    // Reading it out from the memory
    printf("Read %lx\n", SysMem);                   // When this is printed I get only FE, the last byte of the data read from the file, I have read the following 0x01, 02 03 also, which are all junk data like 0x69 0x95 0x9A

  close(fd);
  return 0;
}


What mistake am I doing in writing to memory or reading from the memory, I want this complete 64 bit written to the memory. Or should I manually write each byte to a byte of memory, arent the memory word (32 Bit) addressable? or if not can I make it a word addressable?

Also, this is being done on Zynq with Petalinux

Please help :)

Upvotes: 2

Views: 1128

Answers (2)

Eric Postpischil
Eric Postpischil

Reputation: 223633

The assignment *((unsigned char *) (weights+0x00)) = conv.lw; writes only a single unsigned char, and SysMem= *((unsigned char *) (weights+0x00)); reads only a single unsigned char.

To write and read a double, use either:

* (double *) weights = conv.dw;
double x = * (double *) weights;
printf("%a\n", x); // Or use %g or %f.

or:

memcpy(weights, conv.dw, sizeof conv.dw);
double x;
memcpy(x, weights, sizeof x);
printf("%a\n", x); // Or use %g or %f.

The first method requires that weights be suitably aligned for your platform (it looks like it would be in your example code). There should not be aliasing issues directly from this code since conv.dw is accessed as its proper type (double), and weights is effectively a dynamically allocated object (providing your code for getting its value is correct, and presumably your compiler supports this), so a double is effectively created at weights by writing a double to there.

The second method should work regardless of alignment, provided the address in weights is properly accessible in your process.

In either case, there is no need for the union. The double can be used directly, without aliasing it with an unsigned long.

Upvotes: 0

brhans
brhans

Reputation: 402

Your problem is here:

    *((unsigned char *) (weights+0x00))  = conv.lw;   // Writing into the mempory
    SysMem= *((unsigned char *) (weights+0x00));    // Reading it out from the memory

You're casting your (void *) weights as a (unsigned char *), then storing the value of conv.lw at that pointer location.
But by doing that type-cast, you've explicitly told your compiler that you only want to write a single unsigned char, so it quite happily does that with the least-significant byte of conv.lw.
Similarly, when you read it back, you again cast weights as an (unsigned char *) and so you're only reading a singe byte form that location.

If you instead did something like:

    *((unsigned long *) (weights+0x00))  = conv.lw;   // Writing into the mempory
    SysMem= *((unsigned long *) (weights+0x00));    // Reading it out from the memory

you'd be writing and reading all the bytes of conv.lw.

There are also a few reasons which make what you're trying to do non-portable, including: unsigned long is only typically only 4 bytes on a 32-bit architecture, and dereferencing pointers cast from other types is (at least sometimes) undefined behavior.

Upvotes: 1

Related Questions