For now, pretend all memory is of one type. Ignore 'cache'. Read a book (you're going to have to) to
understand the basic operation of a computer. You'll learn a lot just going for an old (1980's book) on
computing. To learn the usefulness of cache and modern computing concepts, you can then follow up with
another book - a book by Hennesey and Patterson will help, but it's not a beginners book, so read an
old 1980's computing textbook first. It should be a general book, not microcontroller-specific. I'm thinking
something written by Rodney Zaks era.
In the meantime, here is some info to get you thinking about how a computer makes use of memory
to execute a program. Notice I won't actually refer to cache at all, and only mention ROM and RAM in passing.
A C program, when compiled, produces code which will eventually fit into ROM inside the microcontroller. Any constants are stored
there, too. Any variables, however, are generally kept on a stack (Note that nowadays, many microcontrollers have
lots of registers, so these are used for variable storage too - and it can be faster. Anyway, I'll ignore it for the
purpose of this discussion). A stack is an area of memory (RAM) which is used by the microcontroller to keep track of
subroutine nesting. It generally grows downwards in memory (upward in the diagram below, because it has been rotated
through 180 degrees); starting at a high address and working it’s way down. The diagram below shows the stacking structure
in the target microcontroller, before and after a subroutine call. (Each small rectangle "pigeon hole" is one byte).
In order to remember where it left off, each time a subroutine call is made the microcontroller will push
the current value of the program counter (PC) onto the stack, followed by the condition code register
(CCR). The PC is 24-bit wide in this example microcontroller, so it occupies three bytes of stack space. The CCR is 1 byte
in this example, so PC and CCR is a total of 4 bytes. When the subroutine exits, the microcontroller will pop off the CCR and
PC from the stack, and hence it will know where to continue running from. A specific register (known as the stack pointer or SP)
stores the location of the end of the stack.
The stack can also be used to store temporary data, as long as the stack pointer is returned to its original
value before any return from a subroutine. Otherwise if the stack pointer is modified, the processor will
pop off the wrong contents for the program counter and CCR, and will begin execution at that address
– generally resulting in a system crash.
Look at this next image, which shows an example C program (a very short program) and the
assembler output (it will be in ROM eventually, but that doesn't matter). The exact microcontroller version doesn't matter either
for the purposes of this discussion.
Looking at the assembler output, it can be seen that the first thing that the compiler does is to store all registers onto the stack using
a series of PUSH.L statements (shown in a dashed box) (The '.L' just means long word for this microcontroller,
i.e. 4 bytes). This is so that the calling function does not get it’s registers corrupted.
Next, there is a SUBS.L statement which is subtracting 4 from the stack pointer. Remembering that the
stack grows backwards, this is equivalent to increasing the size of the stack by 4 bytes. The purpose of
this is to reserve memory for the integer variable ‘i', as specified in the source code.
The next line is a quick way of setting a register to equal zero; this is the first parameter of the for
loop.
Throughout the rest of the program, the assembler code refers to the stack each time it wants to read or
modify the variable.
Finally, four is added to the value of the stack pointer (ADDS.L #4,SP). This is in order to satisfy the
condition indicated earlier, where a function must leave the stack pointer at exactly the same point that
it was on entry. Before the routine exits, the original contents of the registers are restored through a
series of POP.L statements.