Building process (Part 2):
--------------------------------
in case the variable is used repeatedly in the code and its value is not changing during the code, The compiler optimizer has the option to store it in a General purpose register (GPR) or cache it in a cache memory if exists to reduce execution time.
Ex:
var =40;
while(var==40)
{
..
..
..
}
Here, the compiler will solve this line while(var==40) to
1- Read Var from its Virtual Memory Address (or from a GPR if cached in a GPR or cache memory)
2- Compare Var to 40
3- Jump if Equal.
is caching always useful?
*No , Not really!
let's say we have that code snippet:
-------------------------------------------
var =40;
while(var==40)
{
..
..
..
}
----
ISR()
{
var =10;
}
As we discussed earlier, the compiler will divide while condition to 3 steps. so, Let's imagine that while the CPU executes the read operation of var variable that is stored in a GPR ( as the compiler decided to cache it as it's used repeatedly in the code) , an ISR is fired modifying the value of var variable in RAM ,but CPU doesn't sense that change as var value is already cached in one of its GPRs! and that's a PROBLEM!
so we need to tell the compiler NEVER cache this variable as here we introduce volatile modifier.
Volatile Modifier: tells the compiler optimizer to NOT cache this variable. as its value may be changed in the future.
When to use volatile Modifier?
------------------------------------------
1) Any variable is defined inside an ISR should be defined as volatile.
2) Any Variable is changed by HW.
Example:-
---------------
Registers Addresses => #define PINA *((volatile u8*)0x31)
as PINA is an 8-bit register is modified by HardWare as PINA is updated when there's input voltage to the microcontroller.
Critical Section:
----------------------
A critical section is a code that can not be interrupted (executed atomically) ,as it is Critical. so that code should be optimized and small as possible.
when to use Critical Section?
Whenever there's a shared resource(Variable,...) we need to create a critical section to guarantee that resource will not change while using it.
*Shared Resource/Variable found between a Function and ISR or between tasks in an OS.
Note: Critical section will postpone the interrupt if happened ! so whenever we have a critical section in the code, we can calculate the total interrupt latency as the time of all critical sections in the code + interrupt latency itself in the worst case! (Architecture Design).
How to implement a Critical Section?
1) The simplest Method but a bad one: Disable Global Interrupt at the code beginning then Enable it at its end.
#define ENTER_CRITICAL_SECTION GIE=0 /*Disable Global Interrupt*/
#define EXIT_CRITICAL_SECTION GIE=1 /*Enable Global Interrupt*/
#define ENTER_CS ENTER_CRITICAL_SECTION
#define EXIT_CS EXIT_CRITICAL_SECTION
Example:
ENTER_CS
/*CODE*/
EXIT_CS
What if we have nested critical sections?
/*Critical Section(CS) 1*/
ENTER_CS /*GIE=0*/
/*Code1*/
ENTER_CS /*GIE=0*/
/*Code2*/
EXIT_CS /*GIE=1*/
/*Code1*/ => Global interrupt is ENABLED HERE! so this section is no longer a critical section!
EXIT_CS
so this methodology has a drawback! but we can overcome it will a little tweak:-
u8 counter=0;
#define ENTER_CS() do{\
GIE=0;\
counter++; } while(0)
#define EXIT_CS() do{\
counter--;\
if(counter==0) GIE=1;\
} while(0)
Another Methodology (A Better One) using assembly (so it depends on each microcontroller assembly language so it is implemented in MCAL layer[Global Interrupt Driver]) :-
/*At Critical section entry, We need to save the current state of GIE by pushing it into the stack then disable it. */
#define ENTER_CS() push GIE; \
GIE=0;
/*At Critical Section exit, We need to pop the last pushed value of GIE and that will guarantee the problem the existed with nested critical sections in the last method won't happen*/
#define EXIT_CS() pop GIE;
--------------------------------
in case the variable is used repeatedly in the code and its value is not changing during the code, The compiler optimizer has the option to store it in a General purpose register (GPR) or cache it in a cache memory if exists to reduce execution time.
Ex:
var =40;
while(var==40)
{
..
..
..
}
Here, the compiler will solve this line while(var==40) to
1- Read Var from its Virtual Memory Address (or from a GPR if cached in a GPR or cache memory)
2- Compare Var to 40
3- Jump if Equal.
is caching always useful?
*No , Not really!
let's say we have that code snippet:
-------------------------------------------
var =40;
while(var==40)
{
..
..
..
}
----
ISR()
{
var =10;
}
As we discussed earlier, the compiler will divide while condition to 3 steps. so, Let's imagine that while the CPU executes the read operation of var variable that is stored in a GPR ( as the compiler decided to cache it as it's used repeatedly in the code) , an ISR is fired modifying the value of var variable in RAM ,but CPU doesn't sense that change as var value is already cached in one of its GPRs! and that's a PROBLEM!
so we need to tell the compiler NEVER cache this variable as here we introduce volatile modifier.
Volatile Modifier: tells the compiler optimizer to NOT cache this variable. as its value may be changed in the future.
When to use volatile Modifier?
------------------------------------------
1) Any variable is defined inside an ISR should be defined as volatile.
2) Any Variable is changed by HW.
Example:-
---------------
Registers Addresses => #define PINA *((volatile u8*)0x31)
as PINA is an 8-bit register is modified by HardWare as PINA is updated when there's input voltage to the microcontroller.
It's a good practice to use volatile modifier whenever we define registers addresses.3) Shared variable in multi-threading programming (OS tasks).
Critical Section:
----------------------
A critical section is a code that can not be interrupted (executed atomically) ,as it is Critical. so that code should be optimized and small as possible.
when to use Critical Section?
Whenever there's a shared resource(Variable,...) we need to create a critical section to guarantee that resource will not change while using it.
*Shared Resource/Variable found between a Function and ISR or between tasks in an OS.
Note: Critical section will postpone the interrupt if happened ! so whenever we have a critical section in the code, we can calculate the total interrupt latency as the time of all critical sections in the code + interrupt latency itself in the worst case! (Architecture Design).
How to implement a Critical Section?
1) The simplest Method but a bad one: Disable Global Interrupt at the code beginning then Enable it at its end.
#define ENTER_CRITICAL_SECTION GIE=0 /*Disable Global Interrupt*/
#define EXIT_CRITICAL_SECTION GIE=1 /*Enable Global Interrupt*/
#define ENTER_CS ENTER_CRITICAL_SECTION
#define EXIT_CS EXIT_CRITICAL_SECTION
Example:
ENTER_CS
/*CODE*/
EXIT_CS
What if we have nested critical sections?
/*Critical Section(CS) 1*/
ENTER_CS /*GIE=0*/
/*Code1*/
ENTER_CS /*GIE=0*/
/*Code2*/
EXIT_CS /*GIE=1*/
/*Code1*/ => Global interrupt is ENABLED HERE! so this section is no longer a critical section!
EXIT_CS
so this methodology has a drawback! but we can overcome it will a little tweak:-
u8 counter=0;
#define ENTER_CS() do{\
GIE=0;\
counter++; } while(0)
#define EXIT_CS() do{\
counter--;\
if(counter==0) GIE=1;\
} while(0)
Another Methodology (A Better One) using assembly (so it depends on each microcontroller assembly language so it is implemented in MCAL layer[Global Interrupt Driver]) :-
/*At Critical section entry, We need to save the current state of GIE by pushing it into the stack then disable it. */
#define ENTER_CS() push GIE; \
GIE=0;
/*At Critical Section exit, We need to pop the last pushed value of GIE and that will guarantee the problem the existed with nested critical sections in the last method won't happen*/
#define EXIT_CS() pop GIE;
Writing critical sections should follow MISRA(Motor Industry Software Reliability Association) rules.
0 comments:
Post a Comment