Thursday, November 25, 2010

Translating C Constructs to MSP430 Assembly Code

Function and its Parameters

The sample program is:
When a function is called, some housekeeping is normally done which appears as the function's prologue in the assembly code (this would not be true if the function is declared with the attribute "naked").

The procedure is:
1. The current value of r4 (used as frame pointer in MSP430 family) is pushed
    into the stack. The stack pointer (r1) automatically gets decremented by 2.
2. r1 gets decremented again by an offset, thus allocating a stack frame. The
    offset by which r1 gets decremented depends on the number of local variables
    in the called function.
    Here, r1 gets decremented by 4, since there are two local variables for fun(), a
    and b.
3. The current value of the stack pointer r1 is copied into r4 (the register r4 thus
    indicates the frame pointer for the currently executing function).
    All further manipulations of the local variables will be with reference to the
    frame pointer r4.
4. After the body of the called function is executed, the same offset as above is
    added back to the stack pointer r1, thus deallocating the stack frame.
5. The current value of r1 is popped into r4, thus retrieving the previous stack
    frame. The stack pointer r1 gets auto-incremented by 2.


The assembler directives __FrameSize and __FrameOffset gives the size and offset of the frame allocated for the function fun().

Pointers

The sample program is:

On generating the corresponding assembly code:

The code can be traced as follows:
1. The number 10 is stored in the memory address which is at  an offset of 2 bytes
    from the location pointed to by the frame pointer r4.
2. The memory address of 10, i.e. the value of (r4 + 2), is stored in the memory
    location pointed to by r4.
3. The data in the location pointed to by the register r4 is safely interpreted as
    another memory address, and the number 20 is stored in this particular address.

And you thought "pointers" were magic !!!

Variables

The most simple case would be:

But on still expecting the prologue and epilogue:

The number 100 is stored in a memory location addressed with reference to the frame pointer r4.
The variable i is local to the function main() so no extra work.

Static Variables

The demonstration will be like:

Wondering how the assembly code would look like:

The static integer 200 is stored in a similar way as above, but to a different address. Also this address (label i.1194) is located in the .data section, instead of the usual .text section.

All global and static variables (which have their lifetime as long as the whole program), are stored in the .data section.

Pointers to Functions

Considering the sample code:

A simple pointer to a function accepting void and returning void is created. It is assigned the memory address of the function fun(). Then this pointer to a function f is called.

Awaiting the assembly code:
                                   

The pointer f is local to the function main(). Hence, a stack frame of size 2 is allocated, as expected. The value of f, i.e. the memory address of function fun(), is stored in the location pointed to by the frame pointer r4. This address is then simply passed to the call instruction.

The Arsenal Of An Embedded System Programmer

When confronting any new microcontroller or microprocessor or practicallly, a development board, there are some things to keep in mind before diving into the possibly arduous debugging session you are going to have with the system.

Bits and more bits ...
The Rules
1. Never fully trust anything written before you, anywhere you may see it.
2. If you are forced to behave otherwise, go back to Rule 1.

The Programmer's Model
    You can't possibly know everything about the interconnections, circuitry and other design specifications of the chip under concern and also the development board, when you work on it for the first time. But then, these factors aren't really much of a concern. What you primarily need is something else.

    Even if you don't know how the chip is built, you must know how you can control it, and also the features available at the higher level. You need to have a model of your own for the chip, called the Programmer's Model. Through this model, you must be aware of the following:

1. Homework
    Identify the manufacturer, family, type of device (value line, low/high density,
    etc ..), and architecture (von Neumann, Harvard, etc ..).
    Get the Family Manual, Device Specific Manual, and any other pdf that you may
    find useful.
    The manufacturer may also publish an errata sheet, which may become useful
    in some rare cases.

2. Instruction set
    Know whether the instructions are 16 or 32 bit. Be familiar with some common
    instructions.
    Understand the different addressing modes provided in the device.
    Are the instructions aligned by  2 or 4?
    Does it suite more to a RISC or CISC style?  

3. The Memory Map
    Which are the memory areas for flash, RAM, interrupt vectors, peripheral
    registers and special function registers (SFRs)?
    Where is the starting location of stack stored?

4. Registers
    Which are the General Purpose Registers, Program Counter, Status Register and
    Special Function Registers?

5. The Vector Table
    Where is the interrupt vector table present? Which interrupts do the vectors
    represent in the table?
    Which is the reset vector?

6. The Modules
    Know the inbuilt modules in the package (ADC, Timer, USART, etc ..).
    All the Control Word Registers needed and how to manipulate them, will be
    usually specified in the Family Manual.

7. Modes of Operation
    In some systems, the processor by itself may operate in different modes.
    Also know about the various low power modes normally available for the system
    as a whole.

8. The Runtime Framework
    C is the normal choice for embedded programming.
    If interested, learn the device specific startup functions that are called before
    main().
    You may also write a simple linker script.

9. Potential Bugs
    Always keep an eye out for them. Unless it is something like a heisenbug for
    example, it can be traced down. The time taken depends.

The ARM Cortex-M3

The ARM Architecture

The ARM is a 32-bit reduced instruction set computer (RISC) instruction set architecture (ISA) developed by ARM Holdings. It was known as the Advanced RISC Machine, and before that as the Acorn RISC Machine. The ARM architecture is the most widely used 32-bit ISA in terms of numbers produced. They were originally conceived as a processor for desktop personal computers by Acorn Computers, a market now dominated by the x86 family used by IBM PC compatible and AppleMacintosh computers.

The ARM Cortex-M3

The ARM Cortex family is a new generation of processors that has a standard CPU and system architecture. Unlike other ARM CPUs, the Cortex family is a complete processor core in itself.

It comes in three series:
A series: For high end applications, using complex OS and user
               applications. It supports ARM, Thumb and  Thumb-2
               instruction sets.
R series: They follow more of a RT system profile. They too
               supports ARM, Thumb and Thumb-2 instruction sets.
M series: For microcontroller applications, and other
               cost-sensitive projects. It supports only Thumb-2
               instruction set.

There is a relative performance level for all these devices, ranging from 1-8. The highest level for M series is 3.

The ARM Cortex-M3 provides the entire heart of a microcontroller, including timer, memory map, interrupt system, etc.
It has a Harvard Architecture, with about 4 GB total address space.

Operating Modes

In privileged mode, the CPU has access to the full instruction set.
In unprivileged mode, xPSR related functions and access to most registers in the Cortex processor control space are disabled.

Fig 1. The Cortex-M3 operating modes
Both the Thread and Handler modes execute in privileged mode.

Programmer's Model

The Cortex CPU RISC processor has a load/store architecture. To perform data processing operations, operands must be loaded into a central register file, and the data operations are performed on these registers, and the result stored back to memory.

Fig 2. The load/store architecture of Cortex-M3

Register File

There are sixteen 32-bit registers in the processor register file, with an extra 32-bit xPSR (Program Status Register).
Fig 3. The Cortex-M3 register file and xPSR
The Link Register (LR) stores the return address of each procedure call.
There are two stacks, main stack and process stack, to support the two operating modes. Register R15 is the Program Counter (PC).

Memory Map

The memory map for the code area, SRAM area, and the peripheral devices are shown below.
Fig 4. A portion of the Cortex-M3 memory map

Features

1. Unaligned memory access - The Cortex-M3 can make unaligned memory access, which ensures that SRAM is efficiently used.
2. Bit Banding - By this technique, direct bit manipulation can be performed on sections of peripheral and SRAM memory spaces, without the need for any special instructions (normal bit manipulations require READ, MODIFY, WRITE which is expensive in terms of number of cycles).
3. Nested Vector Interrupt Controller (NVIC) - It is a standard unit within the Cortex core, thus making the process of porting the code to different microcontrollers easier. It is designed to support nested interrupts and there are 16 levels of priority. 
By the interrupt preemption technique, high priority interrupts can preempt low priority ones. By the tail chaining technique, successive interrupts can be added to the tail queue, thus reducing the latency in handling those interrupts.