Omar and Lorraine
Omar and Lorraine

Reputation: 1266

How to put a variable at a specific memory location in SDCC

For low-level programming, sometimes it's necessary to say, at a given memory location, this is where my address is. For this post, the example is the PIR1 register in the PIC16F886 and related microcontrollers. It's always found at address 0x000C.

I've taken this approach:

#define pir1 (*(uint8_t*)0xc)

now I can assign to the variable with something like pir1 |= 0x40 (okay, I'd use a #defined constant instead of magic numbers but you get my drift). This compiles just fine on GCC, with no warnings even when I use -Wextra -Wall. To check my assumptions, GCC spits out the following x86_64:

movl    $12, %eax
movb    $64, (%rax)

Exactly what I wanted (okay, I'm not sure why it's eax one moment and rax another, that's probably yet another stupid x86 quirk, but irrelevant since I want PIC14 code anyway)

Now, to target the PIC14, I'm actually using the SDCC compiler. I am invoking it like this

sdcc --std-c99 -mpic14 -p16f886 --use-non-free source.c

The above code starting with #define gives the following warning:

source.c:412: warning 88: cast of LITERAL value to 'generic' pointer
from type 'const-int literal'
to type 'unsigned-char generic* fixed'     

I've tried doing this instead:

__code __at (0xc) uint8_t PIR1;

but that results in an error message

error 33: Attempt to assign value to a constant variable (=)

when I try to make an assignment.

So my question is if I'm missing an idiomatic way to do this in C? Why is SDCC warning me? Is there's some particular SDCC specific funk I'm ignoring here?

Upvotes: 2

Views: 1615

Answers (2)

phuclv
phuclv

Reputation: 41962

Regarding the warning it's probably a bug in the compiler. And as said, you must use (volatile uint8_t*)0xc for memory-mapped access instead

The standard way for absolute addressing in SDCC is via compiler extensions, but you're doing it wrongly. __code __at (0xc) uint8_t PIR1; puts the variable in the code section which isn't writable. That's why you see the error error 33: Attempt to assign value to a constant variable (=). Remember PIC14 uses Harvard architecture. If the address is in RAM then use

volatile __data __at (0xc) uint8_t PIR1;

If it's in xdata then use

volatile __xdata __at (0xc) uint8_t PIR1;

See Absolute Addressing in the documentation

Demo on Godbolt

Upvotes: 4

Lundin
Lundin

Reputation: 214780

"warning 88: cast of LITERAL value to 'generic' pointer" appears to be a false positive. There is nothing wrong syntax-wise or language-wise with your macro. The C standard explicitly allows such conversions, see C17 6.3.2.3/5:

An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.

(Alignment & traps are of no concern in this case.)

Embedded systems compilers in general and PIC compilers in particular, have a bad reputation of non-compliance to standard C. So you either have to figure out how to disable the broken warning or consider using a different compiler.

Unrelated to the warning, you also have a severe bug, namely the missing volatile. I would recommend to study this: How to access a hardware register from firmware?

Upvotes: 1

Related Questions