Dealing with FAR pointers in code library for multiple embedded platforms
I'm trying to maintain a library of code,开发者_开发问答 with samples, for multiple embedded platforms. I need to support the concept of "far" (non 16-bit) pointers for some function parameters.
I thought I had a good solution with defining the macro FAR
to be __far
on some platforms and nothing on platforms with 32-bit pointers (embedded Linux, Win32, etc.). With that macro, I could easily define pointers as somestruct_t FAR *foo
.
But then I started working with Freescale processors, and their compiler requires the FAR
to go between the asterisk and variable name. (somestruct_t * __far foo
).
The best solution I've come up with to handle this case is to define a macro FARPTR
as either __far *
, * __far
or just *
depending on the platform. This allows for somestruct_t FARPTR foo
.
Are there cleaner solutions out there? In particular, I don't like that there isn't a *
visible to someone reading that code. I'm also worried that I will run into problems when it comes to function declarations. Get a load of this syntax from the Freescale compiler help:
int __far *f(); // __far function returning a pointer to int
int * __far f(); // Function returning a __far pointer to int
int __near * __far f(); // __near function returning a __far pointer to int
That last one kills me -- a qualifier inside of the return type indicates a near function?! And I've recently learned that adding the __near
isn't enough to actually compile a function to near memory -- I need to wrap it in pragmas.
So, has anyone seen a nicer solution than my FARPTR
macro idea?
The freescale processor's usage is more consistent with standard type qualifiers such as const
, that placement causes it to refer to the pointer not the data being pointed to. That said, since "far data" rather than a "far pointer" would be meaningless, you'd have thought that it would not matter, but greater consistency presumably makes for a simpler compiler parser.
You could use something kludgey such as:
#if defined __SOME_ARCHITECTURE__
#define DECLARE_FARPTR( type, identifier ) type __far * identifier
#if defined __SOME_OTHER_ARRCHITECTURE__
#define DECLARE_FARPTR( type, identifier ) type * __far identifier
#else
#define DECLARE_FARPTR( type, identifier )
#endif
Then your declarations would look like:
DECLARE_FARPTR( somestruct_t, foo ) ;
or in a function prototype taking a pointer parameter:
void fn( DECLARE_FARPTR( somestruct_t, foo ) ) ;
or a function returning a far pointer:
DECLARE_FARPTR( somestruct_t, fn( void ) ) ;
As you can see it quickly gets hard to read and a declarative function-like macro is generally a thing best avoided.
I don't have a specific better solution for you. However, having dealt with the same problem on several occasions, I recommend a review of the AUTOSAR Specification of Compiler Abstraction (PDF).
It includes a detailed approach to dealing with multiple compilers.
To add to Clifford's post, although function-like macros are probably a bad idea, you could use a macro to create typedefs:
#ifdef SOME_ARCH
#define DEF_FAR_PTR(type, farptr_typename) typedef type __far *farptr_typename;
#elsif SOME_OTHER_ARCH
#define DEF_FAR_PTR(type, farptr_typename) typedef type * __far farptr_typename;
#else
#define DEF_FAR_PTR(type, farptr_typename) typedef type * farptr_typename;
#endif
I also work with Freescale and my own solution to this is either of the following:
1) Replace far pointers with plain integers when possible. This doesn't work in every situation, but is particularly useful when dealing with function pointers, for example when writing interrupt vector tables.
2) Instead of using far pointers, write code that is identical to what the compiler will produce during a far pointer access. Here is an example for a Freescale MCU:
unsigned char readFromPage (unsigned char page, const unsigned char* address)
{
unsigned char value;
unsigned char tmp = PPAGE;
SAFE_DisableInterrupts;
PPAGE = page;
value = *address;
PPAGE = tmp;
SAFE_EnableInterrupts;
return value;
}
(this code may be inlined depending on the case)
3) Use #pragmas as far as possible.
As a result, I don't have a single far or near pointer in my code, it is portable (as portable as hardware-related code gets), and I can check it with static analysers that only understand standard C.
It might just be cleaner to define two FAR macros: one for before the * and another for after the *.
精彩评论