【问题标题】:Read Write to Memory space读取写入内存空间
【发布时间】:2019-10-09 11:46:29
【问题描述】:

我正在尝试将带符号的双精度数写入内存并回读相同的数字,回读是多余的,因为它只是在触发 PL(可编程逻辑)FPGA Fabric 之前验证内存中是否有正确的数据访问这些数据并执行任务。

我将文件读入双精度(联合的一部分),然后通过无符号长整数(联合的一部分)写入内存。但是写入内存之前和我读出之后的数据是错误的,它只是最后一个字节。 (详见代码和cmets)

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


我在写入内存或从内存读取时犯了什么错误,我希望将这个完整的 64 位写入内存。或者我应该手动将每个字节写入一个内存字节,内存字(32位)不是可寻址的吗?或者如果不是,我可以让它成为一个可寻址的词吗?

此外,这是在 Zynq 和 Petalinux 上完成的

请帮忙:)

【问题讨论】:

  • 愚蠢的问题,但sizeof(double) == sizeof(unsigned long) 在您的系统上吗?
  • 是的,它们都是 8 个字节,大小为双 8,大小为 UL 8

标签: c fpga zynq petalinux


【解决方案1】:

你的问题在这里:

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

您将(void *) weights 转换为(unsigned char *),然后将conv.lw 的值存储在该指针位置。
但是通过进行这种类型转换,您已经明确告诉您的编译器您只想编写一个 unsigned char,因此它非常高兴地使用 conv.lw 的最低有效字节来实现。
同样,当您读回它时,您再次将weights 转换为(unsigned char *),因此您只是从该位置读取一个单字节。

如果你做了类似的事情:

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

您将写入和读取conv.lw 的所有字节。

还有一些原因导致您尝试做的事情不可移植,包括:unsigned long 在 32 位架构上通常只有 4 个字节,并且取消引用从其他类型转换的指针是(在至少有时)未定义的行为。

【讨论】:

  • 如果他们要将所有字节写成一个对象,他们还不如写一个double。如果由于对齐或其他问题而无法正常工作,则它们应该在double 和内存之间仅使用memcpy。无论哪种方式,他们都不需要联合或unsigned long *
  • @EricPostpischil - 是的。我只是试图保持原始代码的精神......当然可以更好地表达意图。
  • 非常感谢你的兄弟!!,我太傻了,错过了我正在写它的类型。
【解决方案2】:

赋值*((unsigned char *) (weights+0x00)) = conv.lw; 只写入一个unsigned char,而SysMem= *((unsigned char *) (weights+0x00)); 只读取一个unsigned char

要写入和读取double,请使用:

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

或:

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

第一种方法要求weights 与您的平台适当对齐(看起来就像在您的示例代码中一样)。这段代码不应该直接出现别名问题,因为conv.dw 是作为其正确类型 (double) 访问的,而weights 实际上是一个动态分配的对象(假设您获取其值的代码是正确的,并且可能是您的编译器支持这一点),因此通过在weights 写入double 到那里有效地创建double

如果weights 中的地址在您的进程中可以正确访问,则第二种方法应该可以正常工作。

在任何一种情况下,都不需要联合。 double 可以直接使用,无需使用unsigned long 对其进行别名。

【讨论】:

  • Eric Postpischil,非常感谢您解决了我的问题!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-24
相关资源
最近更新 更多