I'm working developing some embedded system with an 8051 at work. Today, all codes are written with IAR, which manage the different memories of the micro with some keywords like __xdata, __pdata, etc...
We're starting with unit testing using the framework Ceedling and I think that the best way of testing my units is making a native executable (http://www.throwtheswitch.org/build/which) and test in my linux and then, once my soft is done, compile it for the 8051.
My problem now is that I don't know how to map the different types of memories of the micro without using the IAR keywords. Does anyone have this problem?
You could consider using the C pre-compiler to define a set of 'declaration' macros, although this could obfuscate the code making it more difficult to maintain.
#ifndef UNIT_TEST
#define DECLARE_DATA_VAR(type,name) type __data name
#endif
Then you could define a similar macro for your unit test framework. The following assumes GCC, so that an output section can be specified to the linker to represent __data variables.
#ifdef UNIT_TEST
#define DECLARE_DATA_VAR(type,name) type name __attribute__((section ("__data")))
#endif
Then in your code you would have to replace standard variable declarations using the macros.
DECLARE_DATA_VAR(int,aNumber);
Caveat: If you do use the GCC __attribute__ to place a variable in a named section you must be careful about the section attributes, as the data and bss sections have different attributes and you will not be able to mix them. (Some architectures and compile tools have addition sections as well)! For example, the following may not be mutually compatible;
DECLARE_DATA_VAR(int,aNumber); // Will be placed in bss
DECLARE_DATA_VAR(int,aNumber) = 0; // May end up in bss or data (it's up to the compiler implementation).
DECLARE_DATA_VAR(int,aNumber) = 1; // Will be placed in data
Alternative: You should consider very carefully exactly what you are 'unit testing'. Think about encapsulating business functionality into functions that are independent of the underlying hardware implementation and providing a seperate hardware abstraction layer for all those parts that really do depend on the target and don't need to be unit tested (i.e. low level drivers).
You could end up writing your own 8051 simulator or spend more time abstracting your code for the sake of unit testing than you do adding business value to your software.
Related
I see the following code in some OS kernel. But I don't understand the way __section is used, and don't know what does this code mean.
#define KEEP_PAGER(sym) \
extern const unsigned long ____keep_pager_##sym; \
const unsigned long ____keep_pager_##sym \
__section("__keep_meta_vars_pager") = (unsigned long)&sym
It's specific linux kernel C macro definition wrapped around a GCC extension, specifying an atttribute to use for an object. It's a shorter way of writing the section attribute definition
Historically the linux kernel has been written specifically for building with the GCC compiler, and makes extensive use of low level extensions to do specific hardware operations and optimisations.
The section attribute specifically is used to determine the storage location of the object tagged with it. ELF binary format arranges the object file into named sections, and using the attribute like this allows the programmer to more precisely specify where the information for the tagged object will be placed in the target object
Over the years, there's been work put in to increasing the compatibility of these compiler extensions between different compilers, as well as making linux compilable with alternative compilers (if you look at the linux header file where the macro is defined you'll see that it is full of conditional directives for various compiler features). Macros like this can be a useful way to have a portable internal API for low level features across different compiler implementations.
Kernel and kernel driver C code is atypically concerned with direct specifics of physical hardware implementation, and needs to be explicit about the compiler binary output in a way that application level C code rarely will.
One example of why the linux kernel uses named sections is in the init handling - functions and data that are only used during bootup are grouped into one section of memory that can be easily released once startup is complete - you may be familiar with the boot message along the lines of 'freeing unused kernel memory:...' towards the end of the linux boot sequence
It is hard to tell what that __section is exactly without its definition, but it might be a variable "section" attribute. It is used to make compiler place variable into a section different from "bss" or "data". See GCC documentation for details.
I was working on an embedded program using C.
There are tons of hardware macros like
#ifdef HardwareA
do A
#endif
It's not readable, and hard to cover all the different paths with unit tests.
So, I decided to move the hardware related code to arch folders, and using macros in the makefile to decide which arch folder is linked. Like in the Linux kernel code.
But when I saw the Linux kernel, I noticed there are so many duplicates in the arch folders.
How do they make the changes to all related hardware when a bug was found in one hardware, but might affect all others?
I think doing this way will inevitably bring duplicates into the code base.
Does anyone have experience with this type of problem?
How to unit test on code which has lots of hardware macros?
Refactoring the code to move hardware macros off source code?
It sounds like you are replacing a function like this:
somefunc()
{
/* generic code ... */
#ifdef HardwareA
do A
#endif
/* more generic code ... */
}
with multiple implementations, one in each arch folder, like this:
somefunc()
{
/* generic code ... */
/* more generic code ... */
}
somefunc()
{
/* generic code ... */
do A
/* more generic code ... */
}
The duplication of the generic code is what you're worried about. Don't do that: instead, have one implementation of the function like this:
somefunc()
{
/* generic code ... */
do_A();
/* more generic code ... */
}
..and then implement do_A() in the arch folders: on Hardware A it has the code for that hardware, and on the other hardware, it is an empty function.
Don't be afraid of empty functions - if you make them inline functions defined in the arch header file, they'll be completely optimised out.
Linux tries to avoid code duplicated between multiple arch directories. You'll see the same functions implemented, but implemented differently. After all, all architectures need code for managing the page tables, but the details differ. So they all have the same functions, but with different definitions.
For some functions, there are CONFIG_GENERIC_* defined by the build system that replace unnecessary architecture hooks with generic versions as well (often no-ops). For example, an arch without a FPU doesn't need hooks to save/restore FPU state on context switch.
This kind of #ifdef hell is definitely to be avoided, but naturally you also want to avoid code duplication. I don't claim this will solve all your problems, but I think the single biggest step you can make it changing your #ifdefs from #ifdef HardwareX to #ifdef HAVE_FeatureY or #ifdef USE_FeatureZ. What this allows you to do is factor the knowledge of which hardware/OS/etc. targets have which features/interfaces out of all your source files and into a single header, which avoids things like:
#if defined(HardwareA) || (defined(HardwareB) && HardwareB_VersionMajor>4 || ...
rendering your sources unreadable.
I tend to move the hardware specific #defines into one header per platform, then select it in a "platform.h" file, which all source files include.
platform.h:
#if defined PLATFORM_X86_32BIT
#include "Platform_X86_32Bit.h"
#elsif defined PLATFORM_TI_2812
#include "Platform_TI_2812.h"
#else
#error "Project File must define a platform"
#endif
The architecture specific headers will contain 2 things.
1) Typedefs for all the common integer sizes, like typedef short int16_t; Note that c99 specifies a 'stdint.h' which has these predefined. (Never use a raw int in portable code).
2) Function headers or Macros for all the hardware specific behavior. By extracting all the dependencies to functions, the main body of code remains clean:
//example data receive function
HW_ReceiverPrepare();
HW_ReceiveBytes(buffer, bytesToFetch);
isGood = (Checksum(buffer+1, bytesToFetch-1) == buffer[0])
HW_ReceiverReset();
Then one platform specific header may provide the prototype to a complex HW_ReceiverPrepare() function, while another simply defines it away with #define HW_ReceiverPrepare()
This works very well in situations like the one described in your comment where the differences between platforms are usually one or two lines. Just encapsulate those lines as function/macro calls, and you can keep the code readable while minimizing duplication.
I'm working on a 'C' code base that was written specifically for one type of embedded processor. I've written generic 'psuedo object-oriented' code for things like LEDs, GPIO lines and ADCs (using structs, etc). I have also written a large amount of code that utilizes these 'objects' in a hardware/target agnostic manner.
We are now tossing another processor type into the mix, and I'd like to keep the current code structure so I can still make use of the higher level libraries. I do, however, need to provide different implementations for the lower level code (LEDs, GPIO, ADCs).
I know #includes in .C files are generally looked down upon, but in this case, is it appropriate? For example:
// led.c
#ifdef TARGET_AVR
#include "led_avr.c"
#elseifdef TARGET_PIC
#include "led_pic.c"
#else
#error "Unspecified Target"
#endif
If this is inappropriate, what is a better implementation?
Thanks!
Since the linker doesn't care what the name of a source file actually is (it only cares about exported symbols), you can change your linker command line for each target to name the appropriate implementation module (led_avr.c or led_pic.c).
A common way to manage multiple platform source files is to put each set of platform implementation files in their own directory, so you might have avr/led.c and pic/led.c (and avr/gpio.c and pic/gpio.c, etc).
It is good. You may use other tricks, like:
#ifdef PROC1
#define MULTI_CPU(a,b) (a)
#else
#define MULTI_CPU(a,b) (b)
#endif
The more common way to do that, instead of including a C file, is to change the build system (whatever it is) to compile or not compile those certain C files.
Is there any special C standard for microcontrollers?
I ask because so far when I programmed something under Windows OS, it doesn't matter which compiler I used. If I had a compiler for C99, I knew what I could do with it.
But recently I started to program in C for microcontrollers, and I was shocked, that even it's still C in its basics, like loops, variables creation and so, there is some syntax type I have never seen in C for desktop computers. And furthermore, the syntax is changing from version to version. I use AVR-GCC compiler, and in previous versions, you used a function for port I/O, now you can handle a port like a variable in the new version.
What defines what functions and how to have them to be implemented into the compiler and still have it be called C?
Is there any special C standard for microcontrollers?
No, there is the ISO C standard. Because many small devices have special architecture features that need to be supported, many compilers support language extensions. For example because an 8051 has bit addressable RAM, a _bit data type may be provided. It also has a Harvard architecture, so keywords are provided for specifying different memory address spaces which an address alone does not resolve since different instructions are required to address these spaces. Such extensions will be clearly indicated in the compiler documentation. Moreover, extensions in a conforming compiler should be prefixed with an underscore. However, many provide unadorned aliases for backward compatibility, and their use should be deprecated.
... when I programmed something under Windows OS, it doesn't matter which compiler I used.
Because the Windows API is standardized (by Microsoft), and it only runs on x86, so there is no architectural variation to consider. That said, you may still see FAR, and NEAR macros in APIs, and that is a throwback to 16-bit x86 with its segmented addressing, which also required compiler extensions to handle.
... that even it's still C in its basics, like loops, variables creation and so,
I am not sure what that means. A typical microcontroller application has no OS or a simple kernel, you should expect to see a lot more 'bare metal' or 'system-level' code, because there are no extensive OS APIs and device driver interfaces to do lots of work under the hood for you. All those library calls are just that; they are not part of the language; it is the same C language; jut put to different work.
... there is some syntax type I have never seen in C for desktop computers.
For example...?
And furthermore, the syntax is changing from version to version.
I doubt it. Again; for example...?
I use AVR-GCC compiler, and in previous versions, you used a function for port I/O, now you can handle a port like a variable in the new version.
That is not down to changes in the language or compiler, but more likely simple 'preprocessor magic'. On AVR, all I/O is memory mapped, so if for example you include the device support header, it may have a declaration such as:
#define PORTA (*((volatile char*)0x0100))
You can then write:
PORTA = 0xFF;
to write 0xFF to memory mapped the register at address 0x100. You could just take a look at the header file and see exactly how it does it.
The GCC documentation describes target specific variations; AVR is specifically dealt with here in section 6.36.8, and in 3.17.3. If you compare that with other targets supported by GCC, it has very few extensions, perhaps because the AVR architecture and instruction set were specifically designed for clean and efficient implementation of a C compiler without extensions.
What defines what functions and how to have them to be implemented into the compiler and still have it be called C?
It is important to realise that the C programming language is a distinct entity from its libraries, and that functions provided by libraries are no different from the ones you might write yourself - they are not part of the language - so it can be C with no library whatsoever. Ultimately, library functions are written using the same basic language elements. You cannot expect the level of abstraction present in, say, the Win32 API to exist in a library intended for a microcontroller. You can in most cases expect at least a subset of the C Standard Library to be implemented since it was designed as a systems level library with few target hardware dependencies.
I have been writing C and C++ for embedded and desktop systems for years and do not recognise the huge differences you seem to perceive, so can only assume that they are the result of a misunderstanding of what constitutes the C language. The following books may help.
C Programming Language (2nd Edition) by Brian W. Kernighan and Dennis M. Ritchie
Embedded C by Michael J. Pont
Embedded systems are weird and sometimes have exceptions to "standard" C.
From system to system you will have different ways to do things like declare interrupts, or define what variables live in different segments of memory, or run "intrinsics" (pseudo-functions that map directly to assembly code), or execute inline assembly code.
But the basics of control flow (for/if/while/switch/case) and variable and function declarations should be the same across the board.
and in previous versions, you used function for Port I/O, now you can handle Port like variable in new version.
That's not part of the C language; that's part of a device support library. That's something each manufacturer will have to document.
The C language assumes a von Neumann architecture (one address space for all code and data) which not all architectures actually have, but most desktop/server class machines do have (or at least present with the aid of the OS). To get around this without making horrible programs, the C compiler (with help from the linker) often support some extensions that aid in making use of multiple address spaces efficiently. All of this could be hidden from the programmer, but it would often slow down and inflate programs and data.
As far as how you access device registers -- on different desktop/server class machines this is very different as well, but since programs written to run under common modern OSes for these machines (Mac OS X, Windows, BSDs, or Linux) don't normally access hardware directly, this isn't an issue. There is OS code that has to deal with these issues, though. This is usually done through defining macros and/or functions that are implemented differently on different architectures or even have multiple versions on a single system so that a driver could work for a particular device (such an Ethernet chip) whether it were on a PCI card or a USB dongle (possibly plugged into a USB card plugged into a PCI slot), or directly mapped into the processor's address space.
Additionally, the C standard library makes more assumptions than the compiler (and language proper) about the system that hosts the programs that use it (the C standard library). These things just don't make sense when there isn't a general purpose OS or filesystem. fopen makes no sense on a system without a filesystem, and even printf might not be easily definable.
As far as what AVR-GCC and its libraries do -- there are lots of stuff that goes into how this is done. The AVR is a Harvard architecture with memory mapped device control registers, special function registers, and general purpose registers (memory addresses 0-31), and a different address space for code and constant data. This already falls outside of what standard C assumes. Some of the registers (general, special, and device control) are accessible via special instructions for things like flipping single bits and read/writing to some multi-byte registers (a multi-instruction operation) implicitly blocks interrupts for the next instruction (so that the second half of the operation can happen). These are things that desktop C programs don't have to know anything about, and since AVR-GCC comes from regular GCC, it didn't initially understand all of these things either. That meant that the compiler wouldn't always use the best instructions to access control registers, so:
*(DEVICE_REG_ADDR) |= 1; // Set BIT0 of control register REG
would have turned into:
temp_reg = *DEVICE_REG_ADDR;
temp_reg |= 1;
*DEVICE_REG_ADDR = temp_reg;
because AVR generally has to have things in its general purpose registers to do bit operations on them, though for some memory locations this isn't true. AVR-GCC had to be altered to recognize that when the address of a variable used in certain operations is known at compile time and lies within a certain range, it can use different instructions to preform these operations. Prior to this, AVR-GCC just provided you with some macros (that looked like functions) that had inline assembly to do this (and use the single instruction inplemenations that GCC now uses). If they no longer provide the macro versions of these operations then that's probably a bad choice since it breaks old code, but allowing you to access these registers as though they were normal variables once the ability to do so efficiently and atomically was implemented is good.
I have never seen a C compiler for a microcontroller which did not have some controller-specific extensions. Some compilers are much closer to meeting ANSI standards than others, but for many microcontrollers there are tradeoffs between performance and ANSI compliance.
On many 8-bit microcontrollers, and even some 16-bit ones, accessing variables on a stack frame is slow. Some compilers will always allocate automatic variables on a run-time stack despite the extra code required to do so, some will allocate automatic variables at compile time (allowing variables that are never live simultaneously to overlap), and some allow the behavior to be controlled with a command-line options or #pragma directives. When coding for such machines, I sometimes like to #define a macro called "auto" which gets redefined to "static" if it will help things work faster.
Some compilers have a variety of storage classes for memory. You may be able to improve performance greatly by declaring things to be of suitable storage classes. For example, an 8051-based system might have 96 bytes of "data" memory, 224 bytes of "idata" memory which overlaps the first 96 bytes, and 4K of "xdata" memory.
Variables in "data" memory may be accessed directly.
Variables in "idata" memory may only be accessed by loading their address into a one-byte pointer register. There is no extra overhead accessing them in cases where that would be necessary anyway, so idata memory is great for arrays. If array q is stored in idata memory, a reference to q[i] will be just as fast as if it were in data memory, though a reference to q[0] will be slower (in data memory, the compiler could pre-compute the address and access it without a pointer register; in idata memory that is not possible).
Variables in xdata memory are far slower to access than those in other types, but there's a lot more xdata memory available.
If one tells an 8051 compiler to put everything in "data" by default, one will "run out of memory" if one's variables total more than 96 bytes and one hasn't instructed the compiler to put anything elsewhere. If one puts everything in "xdata" by default, one can use a lot more memory without hitting a limit, but everything will run slower. The best is to place frequently-used variables that will be directly accessed in "data", frequently-used variables and arrays that are indirectly accessed in "idata", and infrequently-used variables and arrays in "xdata".
The vast majority of the standard C language is common with microcontrollers. Interrupts do tend to have slightly different conventions, although not always.
Treating ports like variables is a result of the fact that the registers are mapped to locations in memory on most microcontrollers, so by writing to the appropriate memory location (defined as a variable with a preset location in memory), you set the value on that port.
As previous contributors have said, there is no standard as such, mainly due to different architectures.
Having said that, Dynamic C (sold by Rabbit Semiconductor) is described as "C with real-time extensions". As far as I know, the compiler only targets Rabbit processors, but there are useful additional keywords (for example, costate, cofunc, and waitfor), some real peculiarities (for example, #use mylib.lib instead of #include mylib.h - and no linker), and several omissions from ANSI C (for example, no file-scope static variables).
It's still described as 'C' though.
Wiring has a C-based language syntax. Perhaps you might want to see what makes it as such.
I'm working on an embedded C project that depends on some external HW. I wish to stub out the code accessing these parts, so I can simulate the system without using any HW. Until now I have used some macros but this forces me to change a little on my production code, which I would like to avoid.
Example:
stub.h
#ifdef _STUB_HW
#define STUB_HW(name) Stub_##name
#else /*_STUB_HW*/
#define STUB_HW(name) name
#endif /*_STUB_HW*/
my_hw.c
WORD STUB_HW(clear_RX_TX)()
{ /* clear my rx/tx buffer on target HW */ }
test_my_hw.c
#ifdef _STUB_HW
WORD clear_RX_TX()
{ /* simulate clear rx/tx buffer on target HW */ }
With this code I can turn on/off the stubbing with the preprocessor tag _STUB_HW
Is there a way to acomplish this without having to change my prod code, and avoiding a lot of ifdefs. And I won't mix prod and test code in the same file if I can avoid it. I don't care how the test code looks as long as I can keep as much as possible out of the production code.
Edit:
Would be nice if it was posible to select/rename functions without replacing the whole file. Like take all functions starting on nRF_## and giving then a new name and then inserting test_nRF_## to nRF_## if it is posible
I just make two files ActualDriver.c and StubDriver.c containing exactly the same function names. By making two builds linking the production code against the different objects there is no naming conflicts. This way the production code contains no testing or conditional code.
As Gerhard said, use a common header file "driver.h" and separate hardware layer implementation files containing the actual and stubbed functions.
In eclipse, I have two targets and I "exclude from build" the driver.c file that is not to be used and make sure the proper one is included in the build. Eclipse then generates the makefile at build time.
Another issue to point out is to ensure you are defining fixed size integers so your code behaves the same from an overflow perspective. (Although from your code sample I can see you are doing that.)
I agree with the above. The standard solution to this is to define an opaque abstracted set of function calls that are the "driver" to the hw, and then call that in the main program. Then provide two different driver implementations, one for hw, one for sw. The sw variant will simulate the IO effect of the hw in some appropriate way.
Note that if the goal is at a lower level, i.e., writing code where each hardware access is to be simulated rather than entire functions, it might be a bit tricker. But here, different "write_to_memory" and "read_from_memory" functions (or macros, if speed on target is essential) could be defined.
There is no need in either case to change the names of functions, just have two different batch files, make files, or IDE build targets (depending on what tools you are using).
Finally, in many cases a better technical solution is to go for a full-blown target system simulator, such as Qemu, Simics, SystemC, CoWare, VaST, or similar. This lets you run the same code all the time, and instead you build a model of the hardware that works like the actual hardware from the perspective of the software. It does take a much larger up-front investment, but for many projects it is well worth the effort. It basically gets rid of the nasty issue of having different builds for target and host, and makes sure you always use your cross-compiler with deployment build options. Note that many embedded compiler suites come with some basic such simulation ability built in.