I'm writing code in ISO C90, which forbids mixed declarations and code.
So I have something like this:
int function(int something)
{
int foo, ret;
int bar = function_to_get_bar();
some_typedef_t foobar = get_this_guy(something);
spin_lock_irqsave(lock);
/*
Here is code that does a lot of things
*/
spin_unlock_irqrestore(lock);
return ret;
}
The question is if hardware or software interrupt happens, in which place it can interrupt my function, can it happen also in the middle of variable declaration?
Why I ask this is because I need that this function is not interrupted by interrupts. I want to use spin_lock_irqsave() to ensure that, but I was wondering if interrupt could interrupt my function in variable declarations?
Interrupts are highly hardware platform specific. But there is no "variable declaration" in the code the processor runs. The variables are just places in memory (or in registers, if the compiler so chooses) that are predetermined.
If you mean assigning to variables then yes, in general interrupts can happen. If you need function_to_get_bar() not to be interrupted and spin_lock_irqsave(lock); guarantees it won't, then just move the assignment inside it.
int function(int something)
{
int foo, ret;
int bar; // This is declaration, just for the compiler
some_typedef_t foobar;
spin_lock_irqsave(lock);
bar = function_to_get_bar(); // This is assignment, will actually run some code on the CPU
foobar = get_this_guy(something);
/*
Here is code that does a lot of things
*/
spin_unlock_irqrestore(lock);
return ret;
}
Related
Suppose having the following code elements working on a fifo buffer:
static uint_fast32_t buffer_start;
static uint_fast32_t buffer_end;
static mutex_t buffer_guard;
(...)
void buffer_write(uint8_t* data, uint_fast32_t len)
{
uint_fast32_t pos;
mutex_lock(buffer_guard);
pos = buffer_end;
buffer_end = buffer_end + len;
(...) /* Wrap around buffer_end, fill in data */
mutex_unlock(buffer_guard);
}
bool buffer_isempty(void)
{
bool ret;
mutex_lock(buffer_guard);
ret = (buffer_start == buffer_end);
mutex_unlock(buffer_guard);
return ret;
}
This code might be running on an embedded system, with a RTOS, with the buffer_write() and buffer_isempty() functions called from different threads. The compiler has no means to know that the mutex_lock() and mutex_unlock() functions provided by the RTOS are working with a critical sections.
As the code is above, due to buffer_end being a static variable (local to the compilation unit), the compiler might choose to reorder accesses to it around function calls (at least as far as I understand the C standard, this seems possible to happen). So potentially the code performing buffer_end = buffer_end + len line have a chance to end up before the call to mutex_lock().
Using volatile on these variables (like static volatile uint_fast32_t buffer_end;) seems to resolve this as then they would be constrained by sequence points (which a mutex_lock() call is, due to being a function call).
Is my understanding right on these?
Is there a more appropriate means (than using volatile) of dealing with this type of problem?
Is there a way to add an identifier that the compiler would replace with multiple lines of code?
I read up on macros and inline functions but am getting no where.
I need to write an Interrupt Service Routine and not call any functions for speed.
Trouble is I have several cases where I need to use a function so currently I just repeat all several lines in many places.
for example:
void ISR()
{
int a = 1;
int b = 2;
int c = 3;
// do some stuff here ...
int a = 1;
int b = 2;
int c = 3;
// do more stuff here ...
int a = 1;
int b = 2;
int c = 3;
}
The function is many pages and I need the code to be more readable.
I basically agree with everyone else's reservations with regards to using macros for this. But, to answer your question, Multiline macros can be created with a backslash.
#define INIT_VARS \
int a = 1; \
int b = 2; \
int c = 3;
#define RESET_VARS \
a = 1; \
b = 2; \
c = 3;
void ISR()
{
INIT_VARS
// do some stuff here ...
RESET_VARS
// do more stuff here ...
RESET_VARS
}
You can use inline function that will be rather integrated into place where it is called in source instead of really being called (note that behavior of this depends on several things like compiler support and optimizations setup or using -fno-inline flag feature). GCC documentation on inline functions.
For completeness - other way would be defining // do some stuff here... as pre-processor macro which again gets inserted in place where called; this time by preprocessor - so no type safety, harder to debug and also to read. Usual good rule of thumb is to not write a macro for something that can be done with function.
You are correct - it is recommended that you not place function calls in an ISR. It's not that you cannot do it, but it can be a memory burden depending on the type of call. The primary reason is for timing. ISRs should be quick in and out. You shouldn't be doing a lot of extended work inside them.
That said, here's how you can actually use inline functions.
// In main.c
#include static_defs.h
//...
void ISR() {
inline_func();
// ...
inline_func();
}
// In static_defs.h
static inline void inline_func(void) __attribute__((always_inline));
// ... Further down in file
static inline void inline_func(void) {
// do stuff
}
The compiler will basically just paste the "do stuff" code into the ISR multiple times, but as I said before, if it's a complex function, it's probably not a good idea to do it multiple times in a single ISR, inlined or not. It might be better to set a flag of some sort and do it in your main loop so that other interrupts can do their job, too. Then, you can use a normal function to save program memory space. That depends on what you are really doing and when/why it needs done.
If you are actually setting variables and returning values, that's fine too, although, setting multiple variables would be done by passing/returning a structure or using a pointer to a structure that describes all of the relevant variables.
If you'd prefer to use macros (I wouldn't, because function-like macros should be avoided), here's an example of that:
#define RESET_VARS() do { \
a = 1; \
b = 2; \
c = 3; \
while (0)
//...
void ISR() {
uint8_t a=1, b=2, c=3;
RESET_VARS();
// ...
RESET_VARS();
}
Also, you said it was a hypothetical, but it's recommended to use the bit-width typedefs found in <stdint.h> (automatically included when you include <io.h> such as uint8_t rather than int. On an 8-bit MCU with AVR-GCC, an int is a 16-bit signed variable, which will require (at least) 2 clock cycles for every operation that would have taken one with an 8-bit variable.
I'm using microcontroller to make some ADC measurements. I have an issue when I try to compile following code using -O2 optimization, MCU freezes when PrintVal() function is present in code. I did some debugging and it turns out that when I add -fno-inline compiler flag, the code will run fine even with PrintVal() function.
Here is some background:
AdcIsr.c contains interrupt that is executed when ADC finishes it's job. This file also contains ISRInit() function that initializes variable that will hold value after conversion. In main loop will wait for interrupt and only then access AdcMeas.value.
AdcIsr.c
static volatile uin16_t* isrVarPtr = NULL;
ISR()
{
uint8_t tmp = readAdc();
*isrVarPtr = tmp;
}
void ISRInit(volatile uint16_t *var)
{
isrVarPtr = var;
}
AdcMeas.c
typedef struct{
uint8_t id;
volatile uint16_t value;
}AdcMeas_t;
static AdcMeas_t AdcMeas = {0};
const AdcMeas_t* AdcMeasGetStructPtr()
{
return &AdcMeas;
}
main.c
void PrintVal(const AdcMeas_t* data)
{
printf("AdcMeas %d value: %d\r\n", data->id, data->value);
}
void StartMeasurement()
{
...
AdcOn();
...
}
int main()
{
ISRInit(AdcMeasGetStructPtr()->value);
while(1)
{
StartMeasurement();
WaitForISR();
PrintVal(AdcMeasGetStructPtr());
DelayMs(1000);
}
}
Questions:
Is there something wrong with usage of const AdcMeas_t* data as argument of the PrintVal() function? I understand that AdcMeas.value may change inside interrupt and PrintVal() may be outdated.
AdcMeas contains a 'generic getter'. Is this a good practice to use this sort of function to allow read-only access to static structure? or should I implement AdcMeasGetId() and AdcMeasGetValue functions (note that this struct has only 2 members, what if it has 8 members)?
I know this code is a bit dumb (waiting for interrupt in while loop), this is just an example.
Some bugs:
You have no header files, neither library include or your own ones. This means that everything is hopelessly broken until you fix that. You cannot do multiple file projects in C without header files.
*isrVarPtr = tmp; Here you write to a variable without protection from race conditions. If the main program reads this variable in several steps, you risk getting incorrect data. You need to protect against race conditions or guarantee atomic access.
const AdcMeasGetStructPtr() is gibberish and there is no way that the return &AdcMeas; inside it would compile with a conforming C compiler.
If you have an old but conforming C90 compiler, the return type will get treated as int. Otherwise, if you have a modern C compiler, not even the function definition will compiler. So it would seem that something is very wrong with your compiler, which is a greater concern than this bug.
Declaring the typedef struct in the C file and then returning a pointer to it doesn't make any sense. You need to re-design this module. You could have a getter function returning an instance to a private struct, if there is only ever going to be 1 instance of it (singleton). However, as mentioned, it needs to handle race conditions.
Stylistic concerns:
Empty parenthesis () in a function declaration is almost always wrong in C. This is obsolete style and means "accept any parameter". C++ is different here.
int main() doesn't make any sense at all in a microcontroller system. You should use some implementation-defined form suitable for freestanding programs. The most commonly supported form is void main (void).
DelayMs(1000); is highly questionable code in any embedded system. There should never be a reason why you'd want to hang up your MCU being useless, with max current consumption, for a whole second.
Overall it seems you would benefit from a "continuous conversion" ADC. ADCs that support continuous conversion just dump their latest read in the data register and you can pick it up with polling whenever you need it. Catching all ADC interrupts is really just for hard realtime systems, signal processing and similar.
Let x and y be variables that are shared between main code and interrupt code.
My idea of volatile is that it is only and always needed for hardware variables and interrupt variables that are also used in main code.
Every usage of x and y in the main code is guaranteed to be atomic by disabling interrupts.
Do x and y really need to be volatile, or is it enough to put a memory barrier before using them to force reloading the variables from RAM?
A)
volatile bool x;
volatile int y[100];
int main(void)
{
while (true) {
disable_interrupts();
if (x)
work(y);
x = false;
enable_interrupts();
}
}
B)
bool x;
int y[100];
int main(void)
{
while (true) {
memory_barrier();
disable_interrupts();
if (x)
work(y);
x = false;
enable_interrupts();
}
}
The objectives are:
To let the compiler optimize work().
Be able to use standard library functions such as memcpy() (those aren't made to be used with volatile variables).
Edit: add interrupt example
interrupts.c:
extern volatile? int x;
extern volatile? int y;
void interrupt(void)
{
x = true;
REGY1 = y[7];
y[23] = REGY2;
}
Memory barriers instead of volatile are fine. Linux kernel developers prefer it that way
There are a few things to watch out for.
Move the barrier after disabling interrupts. Interrupts tend to happen at the worst times.
You need a second memory barrier before enabling interrupts, for variables that are written in the main program, and read in the interupt handler.
Disabling interrupts is not enough in a multiprocessor/multicore system, it doesn't prevent another core from running.
Needless to say, interrupts should not be disabled for extended periods of time, as it can prevent some hardware drivers from functioning.
A structure TsMyStruct is given as parameter to some functions :
typedef struct
{
uint16_t inc1;
uint16_t inc2;
}TsMyStruct;
void func1(TsMyStruct* myStruct)
{
myStruct->inc1 += 1;
}
void func2(TsMyStruct* myStruct)
{
myStruct->inc1 += 2;
myStruct->inc2 += 3;
}
func1 is called under non-interrupt context and func2 is called under interrupt context. Call stack of func2 has an interrupt vector as origin. C compiler does not know func2 can be called (but code isn't considered as "unused" code as linker needs it in interrupt vector table memory section), so some code reading myStruct->inc2 outside func2 can be possibly optimized preventing myStruct->inc2 to be reloaded from ram. It is true for C basic types, but is it true for inc2 structure member or some array...? Is it true for function parameters?
As a general rule, can I say "every memory zone (of basic type? or not?) modified in interrupt context and read elsewhere must be declared as volatile"?
Yes, any memory that is used both inside and outside of an interrupt handler should be volatile, including structs and arrays, and pointers passed as function parameters. Assuming that you are targeting a single-core device, you do not need additional synchronization.
Still, you have to consider that func1 could be interrupted anywhere, which may lead to inconsistent results if you're not careful. For instance, consider this:
void func1(volatile TsMyStruct* myStruct)
{
myStruct->inc1 += 1;
if (myStruct->inc1 == 4)
{
print(myStruct->inc1); // assume "print" exists
}
}
void func2(volatile TsMyStruct* myStruct)
{
myStruct->inc1 += 2;
myStruct->inc2 += 3;
}
Since interrupts are asynchronous, this could print numbers different from 4. That would happen, for instance, if func1 is interrupted after the check but before the print call.
no. volatile is not enough. You have both to set an optimization barrier for the compiler (which can be volatile) and for the processor. E.g. when a CPU core writes data, this can go into some cache and won't be visible for another core.
Usually, you need some locking in your code (spin locks, or mutex). Such functions contain usually an optimization barrier so you do not need an volatile.
Your code is racy, with proper locking it would look like
void func1(TsMyStruct* myStruct)
{
lock();
myStruct->inc1 += 1;
unlock();
}
void func2(TsMyStruct* myStruct)
{
lock();
myStruct->inc1 += 2;
unlock();
myStruct->inc1 += 3;
}
and the lock() + unlock() functions contain optimization barriers (e.g. __asm__ __volatile__("" ::: "memory") or just a call to a global function) which will cause the compiler to reload myStruct.
For nitpicking: lock() and unlock() are expected to do the right thing (e.g. disable irqs). Real world implementations would be e.g. spin_lock_irqsave() + spin_lock_irqrestore() in linux.