【问题标题】:C Recursive Macros for table offsets表偏移的 C 递归宏
【发布时间】:2021-08-17 08:19:12
【问题描述】:

我们需要为外部存储器创建一个静态地址表。 存储结构的大小是已知且固定的。但是,这些结构的地址必须从内存段的开头开始。 我们打算通过使用宏来做到这一点,因为在编译时让表格随着表格元素的变化而变化。

实现看起来像这样:

#define BLOC (0xFF)
#define FINAL_ADR(N) (ADR_##N + SIZE_##N)
#define GET_ADR(N) (FINAL_ADR(N) + (BLOC - (FINAL_ADR(N) & BLOC)))

#define ADR_0 (0x0)
#define SIZE_0 (5)

#define ADR_1 GET_ADR(0)
#define SIZE_1 (300)

#define ADR_2 GET_ADR(1)
#define SIZE_2 (130)
...

const struct {
   uint32_t addr, size;
} mem[] = {
  {.addr = ADR_0, .size = SIZE_0},
  {.addr = ADR_1, .size = SIZE_1},
  {.addr = ADR_2, .size = SIZE_2},
};

上面的代码尝试根据元素的前一个地址和大小来分配一个地址。

  • 元素 0 从位置 0x00 开始,占用 5 个字节(1 个部分)。
  • 元素 1 应从位置 0xFF 开始并占用 300 字节(2 个部分)。
  • 元素 2 应从位置 0x2FD 开始并占用 130 个字节(1 个部分)。

如果我们尝试实现上面的宏,编译器就会失败。因为宏在元素 2 处引用自己:

ADR_2 = ((GET_ADR(1)+ SIZE_1) + (0xFF- ((GET_ADR(1)+ SIZE_1) & 0xFF)))

是否可以绕过这个间接自引用宏?有没有更好的方法来做到这一点?

【问题讨论】:

  • IMO 这毫无意义。
  • 我不确定您要做什么,但我的印象是编写一个生成表的外部程序并在构建过程中包含该程序的执行听起来更容易。
  • 我们有一些应用程序数据需要存储在外部存储器中。这些结构的大小是已知的并且是固定的。但是,这些结构的地址必须从内存部分的开头开始。上面的代码('GET_ADR')试图做的是从最后一个开始计算结构的起始地址。
  • 为此使用链接器脚本,不必自己计算

标签: c macros embedded


【解决方案1】:

您可以滥用枚举来获取此信息。

#include <stdint.h>

#define BLOC (0xFF)
#define FINAL_ADR(N) (ADR_##N + SIZE_##N)
#define GET_ADR(N) (FINAL_ADR(N) + (BLOC + 1 - (FINAL_ADR(N) & BLOC)))

enum {
  ADR_0 = 0x0,        SIZE_0 = 5,
  ADR_1 = GET_ADR(0), SIZE_1 = 300,
  ADR_2 = GET_ADR(1), SIZE_2 = 130,
};

const struct {
  uint32_t addr, size;
} mem[] = {
  {.addr = ADR_0, .size = SIZE_0},
  {.addr = ADR_1, .size = SIZE_1},
  {.addr = ADR_2, .size = SIZE_2},
};

编译为汇编源代码(用于 x86_64):

        .text
        .section  .rdata,"dr"
        .globl    mem          # @mem
        .p2align  4
mem:
        .long     0            # 0x0
        .long     5            # 0x5
        .long     256          # 0x100
        .long     300          # 0x12c
        .long     768          # 0x300
        .long     130          # 0x82

哦,您的阻塞公式需要额外的 + 1 才能达到 0x100 的下一个倍数。

【讨论】:

    【解决方案2】:

    这不是您在嵌入式系统中内存映射变量的方式。

    所有具有自定义内存映射的变量都在链接描述文件中配置,而不是在源代码中。您在链接描述文件中创建段(有时也称为段),您可以在其中指定每个段的起始地址和大小。

    然后在源代码中,您使用一些特定于编译器的非标准扩展来在您创建的段内分配变量。 gcc 示例:

    struct element0 stuff __attribute__ (section ("ELEMENT_0")) = { ... };
    

    现在如果代码的其他部分需要知道这个变量的地址,那么可以用&amp;stuff获取。

    如果您的外部存储器是由 SPI 等访问的串行存储器,而不是存储器映射,那么只需使用(命名的)数字常量制作一个硬编码查找表。不需要所有这些复杂的、不可读的宏。优秀的程序员写的代码尽可能简单,而不是尽可能复杂。

    【讨论】:

      猜你喜欢
      • 2016-07-28
      • 1970-01-01
      • 1970-01-01
      • 2012-11-16
      • 1970-01-01
      • 2019-03-31
      • 2020-05-25
      • 1970-01-01
      相关资源
      最近更新 更多