0% found this document useful (0 votes)
46 views

Multi-Threading 5. Heap Memory: Static Automatic External

The document discusses various topics related to variables and memory in embedded systems including code optimization, context switching, multi-threading, interrupts, heap memory, variable types (variable, constant, literal), storage classes (static, automatic, external), scope of variables, variable declarations of different data types, variable storage classes and modifiers (auto, extern, static, register, volatile, const, unsigned, signed). It provides examples of static global and local variables and describes automatic variables. The document also discusses extern storage class, volatile modifier, proper uses of volatile variables and examples of volatile peripheral registers and variables modified by interrupts.

Uploaded by

Munish Kaundal
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
46 views

Multi-Threading 5. Heap Memory: Static Automatic External

The document discusses various topics related to variables and memory in embedded systems including code optimization, context switching, multi-threading, interrupts, heap memory, variable types (variable, constant, literal), storage classes (static, automatic, external), scope of variables, variable declarations of different data types, variable storage classes and modifiers (auto, extern, static, register, volatile, const, unsigned, signed). It provides examples of static global and local variables and describes automatic variables. The document also discusses extern storage class, volatile modifier, proper uses of volatile variables and examples of volatile peripheral registers and variables modified by interrupts.

Uploaded by

Munish Kaundal
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 8

1.

Code Optimization
variable in the memory can be changed only by the compiler whenever the code
is executed
2. Context Switching
A context switch is the computing process of storing and restoring state (context) of a CPU
so that execution can be resumed from the same point at a later time. This enables multiple
processes to share a single CPU. Thecontext switch is an essential feature of a multitasking
operating system.
3. Multi-Threading
4. An interrupt is a hardware-requested software action.
5. Heap memory
a memory which is not optimised...... and its not other program that can access...
the example is the RTC of the computer.... a variable that can be accessed by not
the program but by interrupts , hardware ports....
6. A variable is a named object that resides in RAM memory and is capable of being examined
and modified
7. A constant is a named object that resides in memory (usually in ROM) and is only capable
of being examined
8. A literal is the direct specification of a number character or string. The difference between
a literal and a constant is that constants are given names so that they can be accessed more
than once.
9. The term storage class refers to the method by which an object is assigned space in
memory. The ImageCraft and Metrowerks compilers recognize three storage classes--
static, automatic, and external. In this document we will use the term global variable to
mean a regular static variable that can be accessed by all other functions. Similarly we will
use the term local variable to mean an automatic variable that can be accessed only by the
function that created it. As we will see in the following sections there are other possibilities
like a static global and static local.
In an embedded system we normally wish to place all variables in RAM and constants in
ROM.
10. A static global is very similar to a regular global. In both cases, the variable is defined in RAM
permanently. The assembly language access is identical. The only difference is the scope.
The static global can only be accessed within the file where it is defined.
A static local is similar to the static global. Just as with the other statics, the variable is
defined in RAM permanently. The assembly language code generated by the compiler that
accesses the variable is identical. The only difference is the scope. The static local can only
be accessed within the function where it is defined.
11. Automatic variables, on the other hand, do not have fixed memory locations. They are
dynamically allocated when the block in which they are defined is entered, and they are
discarded upon leaving that block. Specifically, they are allocated on the 6811/6812 stack by
subtracting a value (one for characters, two for integers and four for long integers) from the
stack pointer register (SP). Since automatic objects exist only within blocks, they can only be
declared locally. Automatic variables can only be referenced (read or write) by the function
that created it. In this way, the information is protected or local to the function.
When a local variable is created it has no dependable initial value. It must be set to an initial
value by means of an assignment operation. C provides for automatic variables to be
initialized in their declarations, like globals. It does this by generating "hidden" code that
assigns values automatically after variables are allocated space.
12. The scope of a variable is the portion of the program from which it can be referenced. We
might say that a variable's scope is the part of the program that "knows" or "sees" the
variable. As we shall see, different rules determine the scopes of global and local objects.
When a variable is declared globally (outside of a function) its scope is the part of the source
file that follows the declaration--any function following the declaration can refer to it.
Functions that precede the declaration cannot refer to it. Most C compilers would issue an
error message in that case.
13. Declarations:-
Declaration Comment Range
unsigned char uc; 8-bit unsigned number 0 to +255
char c1,c2,c3; three 8-bit signed numbers -128 to +127
unsigned int ui; 16-bit unsigned number 0 to +65535
int i1,i2; two 16-bit signed numbers -32768 to +32767
unsigned short us; 16-bit unsigned number 0 to +65535
short s1,s2; two 16-bit signed numbers -32768 to +32767
long l1,l2,l3,l4; four signed 32 bit integers -2147483648L to 2147483647L
float f1,f2; two 32-bit floating numbers not recommended
double d1,d2; two 64-bit floating numbers not recommended

14. Variable storage classes
Class Comment
auto automatic, allocated on the stack
extern defined in some other program file
static permanently allocated
register
attempt to implement an automatic using a register instead of on the
stack
Variable modifiers
Modifier Comment
volatile can change value by means other than the current program
const
fixed value, defined in the source code and can not be changed during
execution
unsigned range starts with 0 includes only positive values
signed range includes both negative and positive values

15. Extern :- Objects that are defined outside of the present source module have the external
storage class. This means that, although the compiler knows what they are
(signed/unsigned, 8-bit 16-bit 32-bit etc.), it has no idea where they are. It simply refers to
them by name without reserving space for them. Then when the linker brings together the
object modules, it resolves these "pending" references by finding the external objects and
inserting their addresses into the instructions that refer to them. The compiler knows an
external variable by the keyword extern that must precede its declaration.
Only global declarations can be designated extern and only globals in other modules can be
referenced as external.
16.
17. The volatile modifier disables the optimization, forcing the program to fetch a new value
from the variable each time the variable is accessed.
18. Volatile variable
1. Volatile is a global variable.
2. takes 1 byte of instruction
3. Compiler cannot optimise the variable.
4. type casting is not allowed.
5. Value can be modified by ports, Hardware, interrupts,serial ports only.
6. Permanent memory location is allocated for it.

Volatile modifiers cannot be optimised by the compiler, During linking process the
code is allocated physical memory in the internal memory. so during the link
process( generation of .lnk file during compilation) these variables are placed in
the heap instead of main memory. Compiler cannot modify the variable until
unless a copy is copied to RAM for execution. So the compiler allocates a different
memory location for the variable. These are called un-optimised location. the
variables in this location are not dependent on the compiler to change the value.
instead interrupt, ports, serial, hardware are given permission to access these
variables when ever they raise a request.
When a memory is optimised, then the life of that variable is limited only till the
function executes then it is destroyed, but volatile are not destroyed, but keep
value in it till there is any change done by external entities. Whenever these
variables are accessed only the last updated value is seen in the register.

Uploading a example after this. the best example is RTC(Real Time Clock). in the
PC. even when the PC is shut down , and later restarted, the clock updates the
latest current time.

eg:
volatile char time;
void update(void)
{
time =time+1;
}

void main()
{
time=0;
while(time<100);
}


without volatile modifier the compiler looks at this as 2 different statements.
1. time =0;
2. while(time<100);

since time =0; 0<100, so the loop always stay in the same line till the condition
is true.
Time never reach 100. So when memory is optimised then it can be changed only
through execution of explicit statement which modify the value. in the above case
it does not change.

if we use volatile modifier, this disables optimisation, forcing the program to fetch
a new value from the variable every time variable is accessed.

hope this answers your question. if not then i can still continue my explaination in
my next post. let me know if you got anything from this.

Always remember you cannot simulate the condition of volatile in c51 or any
compiler as Heap memory cannot be simulated in keil. it can be seen only on
hardware.

Proper use of volatile
A variable should be declared volatile whenever its value could change unexpectedly. In practice, only
three types of variables could change:
1. Memory-mapped peripheral registers
2. Global variables modified by an interrupt service routine
3. Global variables accessed by multiple tasks within a multi-threaded application
We'll talk about each of these cases in the sections that follow.
Peripheral registers
Embedded systems contain real hardware, usually with sophisticated peripherals. These peripherals
contain registers whose values may change asynchronously to the program flow. As a very simple
example, consider an 8-bit status register that is memory mapped at address 0x1234. It is required
that you poll the status register until it becomes non-zero. The naive and incorrect implementation is
as follows:
uint8_t * pReg = (uint8_t *) 0x1234;

// Wait for register to become non-zero
while (*pReg == 0) { } // Do something else
This will almost certainly fail as soon as you turn compiler optimization on, since the compiler will
generate assembly language that looks something like this:
mov ptr, #0x1234 mov a, @ptr

loop:
bz loop
The rationale of the optimizer is quite simple: having already read the variable's value into the
accumulator (on the second line of assembly), there is no need to reread it, since the value will
always be the same. Thus, in the third line, we end up with an infinite loop. To force the compiler to do
what we want, we modify the declaration to:
uint8_t volatile * pReg = (uint8_t volatile *) 0x1234;
The assembly language now looks like this:
mov ptr, #0x1234

loop:
mov a, @ptr
bz loop
The desired behavior is achieved.
Subtler problems tend to arise with registers that have special properties. For instance, a lot of
peripherals contain registers that are cleared simply by reading them. Extra (or fewer) reads than you
are intending can cause quite unexpected results in these cases.
Interrupt service routines
Interrupt service routines often set variables that are tested in mainline code. For example, a serial
port interrupt may test each received character to see if it is an ETX character (presumably signifying
the end of a message). If the character is an ETX, the ISR might set a global flag. An incorrect
implementation of this might be:
int etx_rcvd = FALSE;

void main()
{
...
while (!ext_rcvd)
{
// Wait
}
...
}

interrupt void rx_isr(void)
{
...
if (ETX == rx_char)
{
etx_rcvd = TRUE;
}
...
}
With compiler optimization turned off, this code might work. However, any half decent optimizer will
"break" the code. The problem is that the compiler has no idea that etx_rcvd can be changed within
an ISR. As far as the compiler is concerned, the expression !ext_rcvd is always true, and, therefore,
you can never exit the while loop. Consequently, all the code after the while loop may simply be
removed by the optimizer. If you are lucky, your compiler will warn you about this. If you are unlucky
(or you haven't yet learned to take compiler warnings seriously), your code will fail miserably.
Naturally, the blame will be placed on a "lousy optimizer."
The solution is to declare the variable etx_rcvd to be volatile. Then all of your problems (well, some of
them anyway) will disappear.
Multi-threaded applications
Despite the presence of queues, pipes, and other scheduler-aware communications mechanisms in
real-time operating systems, it is still fairly common for two tasks to exchange information via a shared
memory location (that is, a global). Even as you add a preemptive scheduler to your code, your
compiler has no idea what a context switch is or when one might occur. Thus, another task modifying
a shared global is conceptually identical to the problem of interrupt service routines discussed
previously. So all shared global variables should be declared volatile. For example, this is asking for
trouble:
int cntr;

void task1(void)
{
cntr = 0;

while (cntr == 0)
{
sleep(1);
}
...
}

void task2(void)
{
...
cntr++;
sleep(10);
...
}
This code will likely fail once the compiler's optimizer is enabled. Declaring cntr to be volatile is the
proper way to solve the problem.
Final thoughts
Some compilers allow you to implicitly declare all variables as volatile. Resist this temptation, since it
is essentially a substitute for thought. It also leads to potentially less efficient code.
Also, resist the temptation to blame the optimizer or turn it off. Modern optimizers are so good that I
cannot remember the last time I came across an optimization bug. In contrast, I come across failures
by programmers to use volatile with depressing frequency.
If you are given a piece of flaky code to "fix," perform a grep for volatile. If grep comes up empty, the
examples given here are probably good places to start looking for problems.



Volatile keyword is applied to a variable during declaration so that the compiler cannot apply any
optimizations on variable, whose value can change without being detected by the compiler. i.e. value
can be changed outside the scope of current code at any time.

In other words, volatile keyword tells the compiler to access the actual variable's storage for each
reference during repeated references to the variable. (eg. caching the value to a register is
forbidden).
Syntax:

Applying volatile to a struct or union, makes the entire contents of the struct/union as volatile.
One can also apply the volatile keyword to the individual members of the struct/union.

Three situations to use volatile keyword:
1. Memory or I/O mapped peripheral registers
Value of such registers may change asynchronously during program execution.
For ex:
polling a peripheral's status register until its value becomes 0.
Since, the value is already read into accumulator, it is not re-read because there is no way compiler
could know that value could change outside the current code scope.
Hence we end up with an infinite loop.
To overcome this, use:
Here, we see that value is read from actual storage into accumulator on every access.

2. Interrupy service routine (ISR)
In ISR code, we often encounter situation wherein we set the value of some variable and its value is
tested in some other process.
For ex:
The above code may work with compiler optimization turned OFF, but would break when turned ON.
Without use of volatile, the value of status variable is not re-read as compiler has no idea that it could
change in some ISR. As a result code would stuck in the while loop forever.
Solution is to use volatile int status = false;

3. Globals in a multi-threaded application
One of the ways, to exchange information between threads is through global variables. Threads are
asynchronous in nature. When global variable is changed in one thread, the other thread
should be able to fetch the new updated value. But, with optimization turned ON, Compiler may read
the global variable and place the value in register for current thread context.
For ex:


Lets ignore the synchronization issue between the above thread and concentrate on the potential
issue due to not using volatile.
Here, with compiler optimization turned ON, the changes in global variable count in thread2 is not
reflected while evaluating while(count == 0) condition in thread1 because the assembly code
generated in this case would result in value of count stored into some processor register and would
not be accessed everytime as expected. Solution is to use volatile int count.

You might also like