伪造寄存器读取C

问题描述 投票:0回答:6

我有一个没有操作系统的嵌入式系统的现有代码库。我正在尝试使用 x86 gcc 而不仅仅是交叉编译器来构建它,作为能够在主机上进行单元测试的第一步。不幸的是,到处都有直接的寄存器读写,而不是使用提供的抽象层。当然,一种选择是解决这个问题;将直接寄存器访问替换为对 GPIO 访问函数的调用。我希望通过引入一个宏(仅在 x86 版本中使用)来重新定义代码片段,例如

来加快速度
myvar = (GPIOC->IDR >> 6) & 0x03;   // Read bits 6 and 7
GPIOD->CRL &= 0xFFFFFF0F;

类似:

myvar = (myGpiocIdrReadFunc() >> 6) & 0x03;   // Read bits 6 and 7
myGpiodClrWriteFunc() &= 0xFFFFFF0F;

GPIOx 被定义为指向物理地址的指针。当然,如果我尝试使用 x86 构建的可执行文件直接读取和写入 PC 上的地址,这将是访问冲突。不幸的是,如果我尝试这样的事情:

#define GPIOC->IDR { myGpiocIdrReadFunc() }

编译器不喜欢“->”,说“宏名称后面缺少空格。”

如果您以前解决过此类问题,您是如何做到的?

c macros embedded arm cpu-registers
6个回答
4
投票
typedef struct {
    int IDR;
    int CRL;
} gpio_t;

gpio_t c;
gpio_t d;

gpio_t * GPIOC = &c;
gpio_t * GPIOD = &d;

只要继续将

IDR
之类的寄存器添加到
struct
中,编译器就会对你尖叫。

编辑在看到您的评论后,我编辑了答案,您实际上想模拟这些值,这样就可以,但请记住,您需要像在硬件中一样初始化它们。


1
投票

看来您正在逐字替换源中的该字符串。为什么不使用像

sed
这样的非交互式编辑器来做到这一点呢?您可能有一个构建系统,可以根据架构进行替换或不进行替换。


1
投票

如果 hexa 的解决方案不够充分,这里有第二个选项,它允许您实际模拟所引用的寄存器的行为(包括副作用等)。缺点是,这需要 C++(并且现有的 C 代码可以编译为 C++,这通常是一个困难的要求),并且结构不直接使用本机类型,而是使用一些别名。

您可以定义一个实现

operator=
operator int
以及您可能需要的任何其他访问功能(
&=
等)的类。如果结构体
GPIOC
指向的字段属于您可以更改的类型,则您可以简单地更改该类型以引用您的寄存器类。

大致是这样的:

#ifdef USE_REAL_HW

typedef volatile uint32_t HW_REGISTER32;

#else // (USE_REAL_HW not defined)

template<class T>
class RegisterAbstractionClass {
public:
    const T operator= (const T value) {
        data = value;
        return value;
    };
    operator const T() {
        return data;
    };
    // Other operators...
protected:
    volatile T data;
};

typedef RegisterAbstractionClass<uint32_t> HW_REGISTER32;

#endif // (USE_REAL_HW)

typedef struct {
    HW_REGISTER32 IDR;
    HW_REGISTER32 CRL;
} gpio_t;

(请注意,上面的内容有些简化:在这种情况下,

IDR
CRL
的行为相同,这可能不是硬件的情况。)


0
投票

你的#define 似乎是错误的。在测试系统上尝试使用“#define MyGpiocIdrReadFunc() x86ReadFunc()”作为宏。当然,您需要将 GPIOC->IDR 的所有读取替换为 MyGpiocIdrReadFunc()。您还需要实现该功能。这可能与修复代码以使用您提到的抽象层一样多。


0
投票

据我所知,您无法使用宏替换

->
运算符。

这可能不是您正在寻找的解决方案,但这就是我处理模拟硬件外设的方式。

设置

考虑外围

GPIOC
。它在一些 HAL/CMSIS 文件中定义。

在我的 GPIO 驱动程序中,使用

#ifdef
检查,我将
GPIOC
替换为我自己的外设。我将
GPIOC
结构/外围设备称为“对象”,因为它可以帮助我更好地可视化它。

gpio.c

#ifdef _UNIT_TESTS_ENABLE_

 static GPIO_TypeDef *mock_GPIOC; // Creating a mock object pointer
 #undef GPIOC                     // Undefining the real object
 #define GPIOC mock_GPIOC         // Replacing real with mock

#endif // _UNIT_TESTS_ENABLE_

因此,当

gpio.c
中更下方的代码访问
GPIOC
时,它实际上正在访问
mock_GPIOC
。这很棒,因为
GPIOC
GPIO_TypeDef *
mock_GPIOC
也是。它们是同一类型。

要获取和设置指针

mock_GPIOC
,您可以创建这样的函数。也由
#ifdef
守护。

#ifdef _UNIT_TESTS_ENABLE_

GPIO_TypeDef *get_gpioc_object_ptr(void) { return GPIOC; }
void set_gpioc_object_ptr(GPIO_TypeDef *gpio_c) { GPIOC = gpio_c; }

#endif // _UNIT_TESTS_ENABLE_

当然,也在

.h
文件中公开这些函数。

使用

最后,要在测试中使用它,请创建一个新的

GPIO_TypeDef
对象并使用
set_gpioc_object_ptr()
传递其指针。然后运行目标函数,并断言任何内容。

void my_test_function(void)
{
    // Arrange
    GPIO_TypeDef *orig_gpioc = get_gpioc_object_ptr();

    GPIO_TypeDef mock_gpio =
    {
        .IDR = some_val,
        .CLR = some_other_val,
    };
    set_gpioc_object_ptr(&mock_gpio);


    // Act
    your_target_gpio_function();

    // Assert
    ASSERT(mock_gpio.IDR == compare_value);
    ASSERT(mock_gpio.CLR == other_compare_value);

    set_gpioc_object_ptr(orig_gpioc); // revert to old GPIOC pointer
}

-1
投票

我认为这并不容易。

请注意,SFR 通常是

volatile
,并且许多算术运算都是原子的。所以,代码

GPIOD->CRL &= 0xFFFFFF0F;

不等于

setter(CLR_reg, getter(CLR_reg) & 0xFFFFFF0F);

另请注意,可能存在中断的繁忙等待循环,甚至中断处理程序。

© www.soinside.com 2019 - 2024. All rights reserved.