开发者

Faking register reads in C

开发者 https://www.devze.com 2023-03-23 11:45 出处:网络
I have an existing code base for an embedded system with no operating system. I\'m trying to get it to build with the x86 gcc instead of just the cross compiler, as a first step toward being able to u

I have an existing code base for an embedded system with no operating system. I'm trying to get it to build with the x86 gcc instead of just the cross compiler, as a first step toward being able to unit test on the host. Unfortunately there are direct register reads and writes all over the place, instead of using the provided abstraction layer. One option of course is to fix that; replace the direct register access with calls to the GPIO access functions. I'm hoping to get up to speed faster by introducing a macro (used in the x86 build only) to redefine pieces of the code like

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

to something like:

myvar = (myGpiocIdrReadFunc() >> 6) & 0x03;   // Read bits 6 and 7
myGpiodClrWriteFunc() &= 0xFFFFFF0开发者_JAVA百科F;

GPIOx's are #defined as pointers to a physical address. Of course if I try to read and write directly to addresses on my PC with an executable built with x86, that would be access violation. Unfortunately if I try something like this:

#define GPIOC->IDR { myGpiocIdrReadFunc() }

the compiler doesn't like that "->", saying "missing whitespace after the macro name."

If you've solved this kind of problem before, how did you do it?


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

gpio_t c;
gpio_t d;

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

Just keep adding registers like IDR to the struct as the compiler screams at you.

EDIT I edited the answer after I saw your comment that you actually want to simulate the values, this way you can, but bear in mind that you need to initialized them like in your hardware.


It seems you're after literally replacing that string in the source. Why don't you just do that with a non-interactive editor like sed? You probably have a build system which could, depending on the architecture, do the replacement or not.


In case the solution from hexa isn't quite sufficient, here is a second option, which allows you to actually simulate the behaviour of the registers being referred to (including side effects etc). As a downside, this requires C++ (and that your existing C code can be compiled as C++, which is often a difficult requirement), and that the structs don't use native types directly but some alias instead.

You could define a class that implements operator=, operator int, and any other access functions you might need (&= etc). If the fields in the struct GPIOC points to are of a type you can change, you can then simply change that type to refer to your register class.

Something roughly like this:

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

(Note that the above is somewhat simplified: both IDR and CRL would behave identically in this case, which is probably not the case with the hardware.)


You seem to have your #define the wrong way round. Try "#define MyGpiocIdrReadFunc() x86ReadFunc()" for the macro on your test system. Of course you'll need to replace all the reads from GPIOC->IDR with MyGpiocIdrReadFunc(). You'll also need to implement that function. Which is probably about as much work as fixing the code to use the abstraction layer you mentioned.


I don't think it will be easy.

Note that the SFRs are usually volatile, and many arithmetic operations are atomic. So, the code

GPIOD->CRL &= 0xFFFFFF0F;

won't be equivalent to

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

Also note that there could be a busy waiting loop for an interrupt, and even interrupt handlers.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号