Search code examples
cfpgazynqpetalinux

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


Solution

  • 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.