Understanding #define preprocessor directive macro syntax - c

The following code is taken from the LPC54618.h header file:
typedef struct {
//...structure elements
__IO uint32_t SDIOCLKSEL;
//...more elements
} SYSCON_Type;
#define SYSCON_BASE (0x40000000u)
#define SYSCON ((SYSCON_Type *)SYSCON_BASE)
As far as I can guess the meaning behind the line
#define SYSCON ((SYSCON_Type *)SYSCON_BASE)
I would assume that it creates a pointer named SYSCON that points to a variable of type SYSCON_Type which is stored at the address 0x40000000u. Is this really what happens? And is there any ressource that explains the syntax that is being used here (i.e. defining pointers inside macros)?
When I try to alter the value of SDIOCLKSEL directly, i.e.:
SYSCON->SDIOCLKSEL = some value;
I get an error:
error: expected ')'
error: expected parameter declarator
error: expected ')'
error: expected function body after function declarator
but if I use it inside a function, e.g.:
void foo(void)
{
SYSCON->SDIOCLKSEL = some value;
}
there is no error. Why is that? Why can't I write directly to the structure?
Any answer would be greatly appreciated!

#define SYSCON_BASE (0x40000000u)
This simply lists that at the physical address 0x40000000.
#define SYSCON ((SYSCON_Type *)SYSCON_BASE)
This converts the integer constant 0x40000000u to a pointer to struct by means of a cast. It doesn't actually allocate anything - the actual registers are already allocated as memory-mapped hardware.
Simply put, it says "at address 0x40000000 there's a hardware peripheral SYSCON" (whatever that is, some timer?). It's a common scenario that you have several hardware peripherals of the same type inside a MCU (many SPI, ADC etc), each with the same register layout, but found at different addresses. We can use the same struct type for each such peripheral, and also the same driver code.
The struct itself will have a memory map which corresponds 100% to the register layout. Here it is important to ensure that padding/alignment doesn't screw things up, but hopefully the MCU manufacturer have thought of that (don't take it for granted though).
Assuming SDIOCLKSEL has a register offset of 0x10, then when you type SYSCON->SDIOCLKSEL = some value;, you get machine code like this (pseudo assembler code):
LOAD 0x40000000 into index register X
LOAD 0x10 into register A
ADD A to X
MOVE some value into the address of X
(ARM got special instructions that can move etc based on an offset, so it may be fewer instructions in the actual machine code. Subsequent register accesses could keep "X" untouched and use that base address repeatedly, for effective code.)
The __IO qualifier is just code bloat hiding volatile.
The reason why you get an error when you try to "write directly into the structure" is simply that you can't execute code outside all functions, it has nothing to do with this struct.

it is very easy.
that it creates a pointer named SYSCON that points to a variable of
type SYSCON_Type which is stored at the address 0x40000000u. Is this
really what happens?
Yes and no. When you use the macro SYSCON
void foo(uint32_t value)
{
SYSCON->SDIOCLKSEL = value;
}
preprocessor converts into:
void foo(uint32_t value)
{
((SYSCON_Type *)0x40000000u)->SDIOCLKSEL = value;
}
which writes the 32bit unsigned value to the memory location at the address 0x40000000u + the offset of the struct member.
It is usually used to access the harware registers mapped in the memory address space.
You need to do it inside the function (as all code in the C language)

Related

Define a register as a macro in C?

I'm trying to solve a project in C (beginner - college level) and I have a small problem in a function .
I have two registers defined as macros :
#define register1 0x02004000
#define register2 0x02004200
I want to access to the content of the first register and put in it the content of the 2nd register.
I do the following :
*register1 = *register2 + 2
But it won't work because "#define" defines the registers as ints .
So how can I access the registers with the use of #define ?
(It might be a bad proposition but I'm still learning C :D )
You need to cast the constants to the appropriate pointer type.
So for example if each of these points to an int, you would do the following:
#define register1 ((int *)0x02004000)
#define register2 ((int *)0x02004200)
And technically, what you have aren't actually registers but memory addresses. Registers are part of the CPU and don't have an address.

Redefining a define

I'm reviewing some code and I stumbled across this:
In a header file we have this MAGIC_ADDRESS defined
#define ANOTHER_ADDRESS ((uint8_t*)0x40024000)
#define MAGIC_ADDRESS (ANOTHER_ADDRESS + 4u)
And then peppered throughout the code in various files we have things like this:
*(uint32_t*)MAGIC_ADDRESS = 0;
and
*(uint32_t*)MAGIC_ADDRESS = SOME_OTHER_DEFINE;
This compiles, apparently works, and it throws no linter errors. MAGIC_ADDRESS = 0; without the cast does not compile as I would expect.
So my questions are:
Why in the world would we ever want to do this rather than just making a uint32_t in the first place?
How does this actually work? I thought preprocessor defines were untouchable, how are we managing to cast one?
Why in the world would we ever want to do this rather than just making a uint32_t in the first place?
That's a fair question. One possibility is that ANOTHER_ADDRESS is used as a base address for more than one kind of data, but the code fragments presented do not show any reason why ANOTHER_ADDRESS should not be defined to expand to an expression of type uint32_t *. Note, however, that if that change were made then the definition of MAGIC_ADDRESS would need to be changed to (ANOTHER_ADDRESS + 1u).
How does this actually work? I thought preprocessor defines were untouchable, how are we managing to cast one?
Where an in-scope macro identifier appears in C source code, the macro's replacement text is substituted. Simplifying a bit, if the replacement text contains macro identifiers, too, then those are then replaced with their replacement text, etc.. Nowhere in your code fragments as a macro being cast, per se, but the fully-expanded result expresses some casts.
For example, this ...
*(uint32_t*)MAGIC_ADDRESS = 0;
... expands to ...
*(uint32_t*)(ANOTHER_ADDRESS + 4u) = 0;
... and then on to ...
*(uint32_t*)(((uint8_t*)0x40024000) + 4u) = 0;
. There are no casts of macros there, but there are (valid) casts of macros' replacement text.
It's not the cast that allows the assignment to work, it's the * dereferencing operator. The macro expands to a pointer constant, and you can't reassign a constant. But since it's a pointer you can assign to the memory it points to. So if you wrote
*MAGIC_ADDRESS = 0;
you wouldn't get an error.
The cast is necessary to assign to a 4-byte field at that address, rather than just a single byte, since the macro expands to a uint8_t*. Casting it to uint32_t* make it a 4-byte assignment.
#define ANOTHER_ADDRESS ((uint8_t*)0x40024000)
#define MAGIC_ADDRESS (ANOTHER_ADDRESS + 4u)
And then peppered throughout the code in various files we have things like this:
*(uint32_t*)MAGIC_ADDRESS = 0;
That's the problem - you don't want anything repetitive peppered throughout. Instead, this is what more-or-less idiomatic embedded C code would look like:
// Portable to compilers without void* arithmetic extension
#define BASE_ADDRESS ((uint8_t*)0x40024000)
#define REGISTER1 (*(uint32_t*)(ANOTHER_ADDRESS + 4u))
You can then write REGISTER1 = 42 or if (REGISTER1 != 42) etc. As you may imagine, this is normally used to for memory-mapped peripheral control registers.
If you're using gcc or clang, there's another layer of type safety available as an extension: you don't really want the compiler to allow *BASE_ADDRESS to compile, since presumably you only want to access registers - the *BASE_ADDRESS expression shouldn't pass a code review. And thus:
// gcc, clang, icc, and many others but not MSVC
#define BASE_ADDRESS ((void*)0x40024000)
#define REGISTER1 (*(uint32_t*)(ANOTHER_ADDRESS + 4u))
Arithmetic on void* is a gcc extension adopted most compilers that don't come from Microsoft, and it's handy: the *BASE_ADDRESS expression won't compile, and that's a good thing.
I imagine that the BASE_ADDRESS is the address of the battery-backed RAM on an STM32 MCU, in which case the "REGISTER" interpretation is incorrect, since all you want is to persist some application data, and you're using C, not assembly language, and there's this handy thing we call structures - absolutely use a structure instead of this ugly hack. The things beings stored in that non-volatile area aren't registers, they are just fields in a structure, and the structure itself is stored in a non-volatile fashion:
#define BKPSRAM_BASE_ ((void*)0x40024000)
#define nvstate (*(NVState*)BKPSRAM_BASE_)
enum NVLayout { NVVER_1 = 1, NVVER_2 = 2 };
struct {
// Note: This structure is persisted in NVRAM.
// Do not reorder the fields.
enum NVLayout layout;
// NVVER_1 fields
uint32_t value1;
uint32_t value2;
...
/* sometime later after a release */
// NVVER_2 fields
uint32_t valueA;
uint32_t valueB;
} typedef NVState;
Use:
if (nvstate.layout >= NVVER1) {
nvstate.value1 = ...;
if (nvstate.value2 != 42) ...
}
And here we come to the crux of the problem: your code review was focused on the minutiae, but you should have also divulged the big picture. If my big picture guess is correct - that it's all about sticking some data in a battery-backed RAM, then an actual data structure should be used, not macro hackery and manual offset management. Yuck.
And yes, you'll need that layout field for forward compatibility unless the entire NVRAM area is pre-initialized to zeroes, and you're OK with zeroes as default values.
This approach easily allows you to copy the NVRAM state, e.g. if you wanted to send it over the wire for diagnostic purposes - you don't have to worry about how much data is there, just use sizeof(NVState) for passing it to functions such as fwrite, and you can even use a working copy of that NV data - all without a single memcpy:
NVState wkstate = nvstate;
/* user manipulates the state here */
if (OK_pressed)
nvstate = wkstate;
else if (Cancel_pressed)
wkstate = nvstate;
If you need to assign values to a specific place in memory using MACROs
allows you to do so in a way that is relatively easy to read (and if you need to
use another address later - just change the macro definition)
The macro is translated by the preprocessor to a value. When you then de-reference
it you get access to the memory which you can read or write to. This has nothing to
do with the string that is used as a label by the preprocessor.
Both definitions are wrong I afraid (or at least not completely correct)
It should be defined as a pointer to volatile value if pointers are referencing hardware registers.
#define ANOTHER_POINTER ((volatile uint8_t*)0x40024000)
#define MAGIC_APOINTER (ANOTHER_ADDRESS + 4u)
I was defined as uint8_t * pointer because probably author wanted pointer arithmetic to be done on the byte level.

Pointer definition in preprocessor directive

I was going through a programming manual for one of the microcontrollers I came across and it had the preprocessor definition as follows:
#define SCICTL1A (volatile unsigned int *)0x7051
and a statement in the source file as follows:
*SCICTL1A = 0X0003;
My question is, what is the pointer variable here and what is it pointing to, (I have never come across pointer definitions in preprocessor directives before since I am a beginner to C programming) and what does the assignment statement do?
There is no variables here. The macro expands as text in place, so the 2nd excerpt becomes
*(volatile unsigned int *)0x7051 = 0X0003;
It casts the unsigned integer 0x7051 into a pointer to volatile unsigned integer, then references this in assignment. Essentially it stores 0x0003 into the unsigned integer-wide piece of memory that starts from address 0x7051 (or, however the integer-to-pointer conversion happens to work on your target platform)
volatile is required so that the compiler does not just optimize the assignment out - it must be strictly evaluated and considered a side effect (see as-if rule).
As for the actual reason why this is done - it is probably some memory-mapped device, check the microcontroller datasheets for more information.
There is no variable there. Only the pointer.
the *SCICTL1A = 0X0003; is replaced by the preprocessor by the:
*(volatile unsigned int *)0x7051 = 0x0003;
You just write the location with the address of the 0x07051. That does it mean depends on your implementation
I'm assuming you're using a TMS320F2803x Piccolo microcontroller: http://www.ti.com/lit/ds/sprs584l/sprs584l.pdf
According to this document, address 0x7051 is Control Register 1 for the Serial Communications Interface (SCI) Module.
According to this document, https://www.swarthmore.edu/NatSci/echeeve1/Ref/embedRes/DL/28069TechRefManual_spruh18d.pdf, you're able to do the following with this register:
SCICTL1 controls the receiver/transmitter enable, TXWAKE and SLEEP
functions, and the SCI software reset.

C Structs with three options in the magenta kernel

In the magenta kernel there is a paragraph in which struct got not only a type and name but one option more. I found in the references nothing to explain that syntax. So what is __CPU_ALIGN as argument in struct for and where do I find the syntax for it?
struct type name ???
#if WITH_SMP
/* a global state structure, aligned on cpu cache line to minimize aliasing */
struct mp_state mp __CPU_ALIGN = {
.hotplug_lock = MUTEX_INITIAL_VALUE(mp.hotplug_lock),
.ipi_task_lock = SPIN_LOCK_INITIAL_VALUE,
};
I know that __CPU_ALIGN itself is used to have aligned bytes for the CPU memory size.
It's a macro shorthand for the aligned attribute, which is a GCC extension.
The macro is defined as follows:
#define __CPU_ALIGN __ALIGNED(CACHE_LINE)
The macro __ALIGNED in turn is defined like this:
#define __ALIGNED(x) __attribute__((aligned(x)))
...which matches the syntax in the GCC documentation. (The value of CACHE_LINE depends on the architecture.)

gcc inline assembly using modifier "P" and constraint "p" over "m" in Linux kernel

I'm reading Linux kernel source code (3.12.5 x86_64) to understand how process descriptor is handled.
I found to get current process descriptor I could use current_thread_info() function, which is implemented as follows:
static inline struct thread_info *current_thread_info(void)
{
struct thread_info *ti;
ti = (void *)(this_cpu_read_stable(kernel_stack) +
KERNEL_STACK_OFFSET - THREAD_SIZE);
return ti;
}
Then I looked into this_cpu_read_stable():
#define this_cpu_read_stable(var) percpu_from_op("mov", var, "p" (&(var)))
#define percpu_from_op(op, var, constraint) \
({ \
typeof(var) pfo_ret__; \
switch (sizeof(var)) { \
...
case 8: \
asm(op "q "__percpu_arg(1)",%0" \
: "=r" (pfo_ret__) \
: constraint); \
break; \
default: __bad_percpu_size(); \
} \
pfo_ret__; \
})
#define __percpu_arg(x) __percpu_prefix "%P" #x
#ifdef CONFIG_SMP
#define __percpu_prefix "%%"__stringify(__percpu_seg)":"
#else
#define __percpu_prefix ""
#endif
#ifdef CONFIG_X86_64
#define __percpu_seg gs
#else
#define __percpu_seg fs
#endif
The expanded macro should be inline asm code like this:
asm("movq %%gs:%P1,%0" : "=r" (pfo_ret__) : "p"(&(kernel_stack)));
According to this post the input constraint used to be "m"(kernel_stack), which makes sense to me. But obviously to improve performance Linus changed the constraint to "p" and passed the address of variable:
It uses a "p" (&var) constraint instead of a "m" (var) one, to make gcc
think there is no actual "load" from memory. This obviously _only_ works
for percpu variables that are stable within a thread, but 'current' and
'kernel_stack' should be that way.
Also in post Tejun Heo made this comments:
Added the magical undocumented "P" modifier to UP __percpu_arg()
to force gcc to dereference the pointer value passed in via the
"p" input constraint. Without this, percpu_read_stable() returns
the address of the percpu variable. Also added comment explaining
the difference between percpu_read() and percpu_read_stable().
But my experiments with combining modifier "P" modifier and constraint "p(&var)" did not work. If segment register is not specified, "%P1" always returns the address of the variable. The pointer was not dereferenced. I have to use a bracket to dereference it, like "(%P1)". If segment register is specified, without bracket gcc won't even compile. My test code is as follows:
#include <stdio.h>
#define current(var) ({\
typeof(var) pfo_ret__;\
asm(\
"movq %%es:%P1, %0\n"\
: "=r"(pfo_ret__)\
: "p" (&(var))\
);\
pfo_ret__;\
})
int main () {
struct foo {
int field1;
int field2;
} a = {
.field1 = 100,
.field2 = 200,
};
struct foo *var = &a;
printf ("field1: %d\n", current(var)->field1);
printf ("field2: %d\n", current(var)->field2);
return 0;
}
Is there anything wrong with my code? Or do I need to append some options for gcc? Also when I used gcc -S to generate assembly code I didn't see optimization by using "p" over "m". Any answer or comments is much appreciated.
The reason why your example code doesn't work is because the "p" constraint is only of a very limited use in inline assembly. All inline assembly operands have the requirement that they be representable as an operand in assembly language. If the operand isn't representable than compiler makes it so by moving it to a register first and substituting that as the operand. The "p" constraint places an additional restriction: the operand must be a valid address. The problem is that a register isn't a valid address. A register can contain an address but a register is not itself an valid address.
That means the operand of the "p" constraint must be have a valid assembly representation as is and be a valid address. You're trying to use the address of a variable on the stack as the operand. While this is a valid address, it's not a valid operand. The stack variable itself has a valid representation (something like 8(%rbp)), but the address of the stack variable doesn't. (If it were representable it would be something like 8 + %rbp, but this isn't a legal operand.)
One of the few things that you can take the address of and use as an operand with the "p" constraint is a statically allocated variable. In this case it's a valid assembly operand, as it can be represented as an immediate value (eg. &kernel_stack can be represented as $kernel_stack). It's also a valid address and so satisfies the constraint.
So that's why Linux kernel macro works and you macro doesn't. You're trying to use it with stack variables, while the kernel only uses it with statically allocated variables.
Or at least what looks like a statically allocated variabvle to the compiler. In fact kernel_stack is actually allocated in a special section used for per CPU data. This section doesn't actually exist, instead it's used as a template to create a separate region of memory for each CPU. The offset of kernel_stack in this special section is used as the offset in each per CPU data region to store a separate kernel stack value for each CPU. The FS or GS segment register is used as the base of this region, each CPU using a different address as the base.
So that's why the Linux kernel use inline assembly to access what otherwise looks like a static variable. The macro is used to turn the static variable into a per CPU variable. If you're not trying to do something like this then you probably don't have anything to gain by copying from the kernel macro. You should probably be considering a different way to do what you're trying accomplish.
Now if you're thinking since Linus Torvalds has come with this optimization in the kernel to replace an "m" constraint with a "p" it must be a good idea to do this generally, you should be very aware how fragile this optimization is. What its trying to do is fool GCC into thinking that reference to kernel_stack doesn't actually access memory, so that it won't keep reloading the value every time it changes memory. The danger here is that if kernel_stack does change then the compiler will be fooled, and continue to use the old value. Linus knows when and how the per CPU variables are changed, and so can be confident that the macro is safe when used for its intended purpose in the kernel.
If you want eliminate redundant loads in your own code, I suggest using -fstrict-aliasing and/or the restrict keyword. That way you're not dependant on a fragile and non-portable inline assembly macros.

Resources