Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
5 views19 pages

Module 3

This document covers the integration of C and Assembly language in programming, focusing on techniques such as inline and embedded assembly for performance optimization. It also discusses exception handling, including types of exceptions, interrupts, error conditions, and the processor's exception handling sequence. The document emphasizes the importance of adhering to standards like AAPCS when mixing C and assembly code.

Uploaded by

cheetah69757
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views19 pages

Module 3

This document covers the integration of C and Assembly language in programming, focusing on techniques such as inline and embedded assembly for performance optimization. It also discusses exception handling, including types of exceptions, interrupts, error conditions, and the processor's exception handling sequence. The document emphasizes the importance of adhering to standards like AAPCS when mixing C and assembly code.

Uploaded by

cheetah69757
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 19

Unit 3

Course – Computer Organization & Architecture


Mixing C and Assembly Language, Exception Handling

Mixing C and Assembly Language: Inline Assembler Embedded Assembler, Calling Between C
and Assembly.
Exception Handling: Interrupts, Error Conditions, Processor Exception Sequence, The Vector
Table, Exception Handlers, Exception Priorities, Procedures for Handling Exceptions.

Mixing C and Assembly Language

Many programmers are more comfortable writing in C, and for good reason: C is a mid-level
language (in comparison to Assembly, which is a low-level language), and spares the programmers
some of the details of the actual implementation.
However, there are some low-level tasks that either can be better implemented in assembly, or can
only be implemented in assembly language. Also, it is frequently useful for the programmer to look at
the assembly output of the C compiler, and hand-edit, or hand optimize the assembly code in ways
that the compiler cannot. Assembly is also useful for time-critical or real-time processes, because
unlike with high-level languages, there is no ambiguity about how the code will be compiled. The
timing can be strictly controlled, which is useful for writing simple device drivers. This section will
look at multiple techniques for mixing C and Assembly program development.

INLINE ASSEMBLER
The compiler will try to optimize code as much as possible

• Some applications, algorithms must be optimized by hand, especially in instances where data
is manipulated in ways that a compiler would normally not understand.
• If you’re writing an algorithm at a high level, it is possible to give the compiler some
assistance by indicating sections of code that should be regarded as important.
• One way is through a process called inlining, where the __inline keyword is placed in the C or
C++ code to notate a function that, when possible, should be placed in the assembly directly,
rather than being called as a subroutine.
• This potentially avoids some of the overhead associated with branching and returning.
• Some functions in your C or C++ code in assembly can be written using inline—this might be
placed in a function where you have called for inlining.
• Using the inline assembler is the easiest way to access instructions that are not supported by
the C compiler, for example, saturated math operations, coprocessor instructions, or accessing
the PSRs.

Inline Assembly Syntax

• The inline assembler is invoked with the __ asm keyword, which is followed by a list of
assembly instructions inside braces.

• Inline assembly refers to the ability to include assembly language instructions within a higher-
level programming language, typically for performance optimization or accessing hardware
features that aren't available through the higher-level language alone. In computer
organization and architecture, this feature is often used when programming in languages like
C or C++.

• The syntax for inline assembly can vary depending on the compiler being used and the
language in which the inline assembly is being embedded. However, I can provide a general
overview of the syntax commonly used in C and C++ with the GNU Compiler Collection
(GCC) as an example:

__asm__ ("assembly instructions");

__asm__: This is a keyword or a macro provided by the compiler to indicate that what follows is
assembly language code.

"assembly instructions": This is a string containing the actual assembly instructions. You write the
assembly instructions as you would in pure assembly language, but they are embedded within the
quotes.

Within the assembly instructions, you can also include placeholders for variables or values from the
surrounding C/C++ code using operand constraints and input/output operands. For example:

int a = 5, b = 10, result;

__asm__ ("add %1, %2, %0" : "=r"(result) : "r"(a), "r"(b));

In this example:

"add %1, %2, %0": This is the assembly instruction add, which adds the second and third operands
and stores the result in the first operand. %0, %1, and %2 are placeholders for the output and input
operands.

: "=r"(result): This is the output operand constraint, specifying that result will be stored in a register
(r), and the register number is assigned dynamically by the compiler.

: "r"(a), "r"(b): These are the input operand constraints, specifying that a and b will be stored in
registers and can be used as inputs for the assembly instruction.
Examples :-

__asm keyword, be sure to obey the following rules:

Multiple instructions on the same line, separate them with a semicolon.


Use of double quotes is valid, Enclose all the instructions within a single set of double quotes.

Instruction requires more than one line, specify the line continuation with the backslash character (\).
For the multiple line format, use of C or C++ comments anywhere in the inline assembly language
block. Embed comments in a line that contains multiple instructions cannot be possible.
The comma is used as a separator in assembly language, so C expressions with the comma operator
must be enclosed in parentheses to distinguish them, for example,

__asm

ADD x, y, (f(), z)

}
Register names in the inline assembler are treated as C or C++ variables. They do not necessarily
relate to the physical register of the same name. If not declared with the register as a C or C++
variable, then the compiler generates a warning.

Do not save and restore registers in the line assembler. The compiler will do it for self.

The inline assembler does not provide direct access to the physical registers.

If registers other than CPSR (current program status register) , APSR (Application Program Status
Register.), and SPSR (stored program status register) are read without being written to, an error
message is issue, for example int f(int x) {

__asm

STMFD sp!, {r0} // save r0-illegal:read before write

ADD r0, x, 1

EOR x, r0, x

LDMFD sp!, {r0} // restore r0 - not needed.

} return

x;

Restrictions on Inline Assembly Operations

• Inline assembler has some restrictions, but in general, it still does nearly everything
required to optimize your code.

• Restrictions mostly apply to the use of registers and the types of instructions allowed.

• For example, registers r0 through r3, sp, lr, and the NZCV flags in the CPSR/APSR must
be used with caution.
• Other C or C++ expressions might use these as temporary registers, and the flags could be
corrupted by the compiler when evaluating those expressions.

• Additionally, the following instructions are not supported in the inline assembler:

• BKPT, BX, BXJ, BLX, and SVC instructions

• LDR Rn, = expression pseudo-instruction

• LDRT, LDRBT, STRT, and STRBT instructions

• MUL, MLA, UMULL, UMLAL, SMULL, and SMLAL flag setting instructions

• MOV or MVN flag setting instructions where the second operand is a constant

• User mode LDM instructions

• ADR and ADRL pseudo-instructions

EMBEDDED ASSEMBLER

• Larger routine that requires optimizing by hand, use of embedded assembler rather than the
inline assembler will be an good idea.

• The embedded assembler allows you to declare assembly functions in C and C++ source
modules with full function prototypes, including arguments and a return value.

• Unlike functions written with the inline assembler, these functions cannot be inlined and will
always have the overhead associated with function calls.

• However, need not to have access to the full instruction set, so it is possible to insert Thumb
assembly functions in an ARM module.
To illustrate how the embedded assembler works, we can write a short routine that
copies a string from one memory location and stores it to another. Obviously a compiler
would do a pretty good job compiling such a function from C, but it’s simple enough to
write one just to illustrate the point.

#include <stdio.h > extern

void init_serial (void);

__asm void my_strcopy(const char *src, char *dst) loop

LDRB r2, [r0], #1

STRB r2, [r1], #1

CMP r2, #0

BNE loop

BX lr } int

main(void)
{ const char *a = “Just saying

hello!”; char b[24]; init_serial();

my_strcopy(a,b); printf(“Original

string: ‘%s’\n”, a); printf(“Copied

string: ‘%s’\n”, b); return 0;

The embedded assembler offers another advantage over the inline assembler in that you can access the
C preprocessor directly using the__cpp keyword. This allows access to constant expressions,
including the addresses of data or functions with external linkage. Example assembly instructions
might look like the following:

LDR r0, = __cpp(&some_variable)

LDR r1, = __cpp(some_function)

BL __cpp(some_function)

MOV r0, #__cpp(some_constant_expr)

Embedded Assembly Syntax

• Functions declared with __asm can have arguments and return a type. They are called from C
and C++ in the same way as normal C and C++ functions.

• The syntax of an embedded assembly function is:

__asm return-type function-name(parameter-list){

instruction instruction etc.

• The initial state of the embedded assembler (ARM or Thumb) is determined by the initial
state of the compiler, as specified on the command line.

• This means that if the compiler starts in ARM state, the embedded assembler uses __arm. If
the compiler starts in Thumb state, the embedded assembler uses __thumb.

• Changing the state of the embedded assembler within a function by using explicit ARM,
THUMB, or CODE16 directives in the embedded assembler function. Such a directive within
an __asm function does not affect the ARM or Thumb state of subsequent __asm functions.

• Note that argument names are permitted in the parameter list, but they cannot be used in the
body of the embedded assembly function.
• For example, the following function uses integer i in the body of the function, but this is not
valid in assembly:

__asm int f(int i)

ADD i, i, #1//error

Restrictions on Embedded Assembly Operations

• No return instructions are generated by the compiler for an __asm function. If required to
return from an __asm function, then need to include the return instructions, in assembly code,
in the body of the function.

• All calls between an __asm function and a normal C or C++ function must adhere to the
AAPCS rules (Arm Architecture (AAPCS) defines how subroutines can be written, compiled,
and assembled separately to work together) , even though there are no restrictions on the
assembly code that an __asm function can use.

CALLING BETWEEN C AND ASSEMBLY

• write functions in either C or assembly and then mixing them. In fact, it’s downright easy.

• Functions can be written in assembly and then called from either C or C++, and vice versa;
assembly routines can be called from C or C++ source code.
When using mixed language programming, needto ensure that your assembly routines follow
the AAPCS standard and your C code uses C calling conventions.
Exception Handling
An exception is any condition that needs to halt normal execution of the instructions.
Examples

⮚ Resetting ARM core

⮚ Failure of fetching instructions

⮚ Undefined instruction is encountered

⮚ Hardware Interrupt is executed (HWI)

⮚ Software Interrupt is executed (SWI)

Exceptions include events like an interrupt, and this can be any kind of interrupt, like someone
moving a mouse or pushing a button.
Technically, anything that breaks a program’s normal flow could be considered an exception, but it’s
worth detailing the different types, since some can be readily handled and others are unexpected and
can cause problems.

Hi

Interrupts
Interrupts are very common in microprocessor systems. They provide the ability for a device such as a
timer or a USB interface to poke the processor in the ribs and loudly announce that it wants attention.
These are the two interrupt lines going into the processor, with a low-priority interrupt called IRQ and
a high-priority interrupt called FIQ.
The interrupt will halt the normal processing routines in the ARM core to allow the interrupt request
to be serviced. The ARM core can handle up to five exceptions, however, our focus is on interrupt
handling from an IRQ or FIQ request.

Why Interrupts?
UART waiting for the processor to get the data.
There are roughly three ways the processor can handle this situation
⮚ The processor is always in a loop doing absolutely nothing except waiting for the character to
show up. (Time consuming & least efficient)
⮚ The processor occasionally checks the memory location to see if there is some new data there,
known as polling.
⮚ The processor can spend its time performing other functions, such as updating a display or
converting MP3 data to an analog waveform, while it waits for a slower device to complete its
task.

Error conditions
⮚ Exceptions in a system, do occur often enough that software needs to be sufficiently
robust to handle them.
⮚ The ARM cores recognize a few error conditions, some of which are easy to handle,
some of which are not.
⮚ If the design requires floating-point operations, but the processor does not support
floating-point in hardware, you could decide to use floating-point instructions and
emulate them in software.
⮚ The processor can then take the necessary actions to perform the operations anyway,
using only software to emulate the floating-point operation, appearing to the user as if
floating-point hardware were present.
⮚ Data and prefetch aborts are the exception types that often cause programmers the
most angst.
⮚ A prefetch abort occurs when the processor attempts to grab an instruction in memory
but something goes wrong
⮚ Certain memory regions may be configured as being readable but not writable, and an
attempt to write to such a region can cause a data abort. As with prefetch aborts, the
processor usually needs to be able to recover from some situations and often has
hardware to assist in the recovery.

Processor Exception Sequence


The ARM7TDMI processor has a defined sequence of events to start the handling and
recovery of the exception.
The following sequence begins for all the cases except for reset exception.
⮚ The CPSR is copied into SPSR_<mode>, where <mode> is the new mode into which
the processor is about to change.
⮚ The core will switch to ARM state if it was in Thumb state, as certain instructions do
not exist in Thumb that are needed to access the status registers. The core will also
change to the new exception mode, setting the least significant 5 bits in the CPSR
register. IRQ interrupts are also disabled automatically on entry to all exceptions. FIQ
interrupts are disabled on entry to reset and FIQ exceptions.
⮚ The return address is stored in LR_<mode>

⮚ The Program Counter changes to the appropriate vector address in memory.


⮚ The processor is responsible for all the actions.

⮚ The processor begins executing code from an exception handler

⮚ After the above step the processor should then return to the main code (whether or not
it returns to the instruction that caused the exception depends on the type of
exception).
⮚ The handler may restore the Program Counter to the address of the instruction after
the one that caused the exception.
⮚ Two things which has to be done o The CPSR must be restored from SPSR_<mode>,
where <mode> is the exception mode in which the processor currently operates.
o The PC must be restored from LR_<mode>. These
actions can only be done in ARM state.

Vector Table
For each type of exception, there is usually a dedicated block of code, called an exception
handler, that is responsible for acknowledging an exceptional condition and, more often than
not, fixing it. Not all exceptions need handlers, and in some deeply embedded systems, the
processor may not be able to recover.
The machine may be programmed to reset itself if something like (memory location that is
not physically present and address in the memory map does not point to a memory chip ) that
ever happens.
Larger applications, such as a cell phone, will have to deal with all exceptions and provide
robust methods to recover.
1. Reset exception
Reset exception is the highest priority exception and is always taken whenever it is
signaled.
It initializes the system including setting up memory and caches It
must set up stack pointers for all processor modes.
2. Data abort exception
It occurs only when the memory controller indicates that an invalid memory address It
occurs only when the current code attempts to read or write to memory without
correct access permission
FIQ exceptions can be raised only when data abort handler’s data abort exception are
not disabled
3. Fast Interrupt Request FIQ
FIQ exceptions occur when an external peripheral sets the FIQ pin to nFIQ It is of
highest priority and the cores disables FIQ and IRQ exceptions on entry into the
FIQ handler.
4. Interrupt Request IRQ
IRQ exceptions occur when an external peripheral sets the IRQ pin to nIRQ
It is of second highest priority
This will only enter if and only if an FIQ or Data Abort exception occurs
5. Prefetch Abort Exception
It occurs when the attempt to fetch instructions results in memory fault
This is raised in the execute stage of pipeline
6. Software Interrupt SWI
Occurs when SWI instruction is executed
The cpsr will be set to supervisor mode
The system uses nested SWI calls, the link register r14 and spsr must be stroed away
before branching to the nested SWI
7. Undefined Instruction Exception
This occurs when an instruction not in the ARM or Thumb instruction set reaches the
execute stage of the pipeline
Both SWI and UNDEFINED Instruction have same priority
The instruction being executed cannot be both SWI and UNDEFINED Instruction

Exception Handler
Exception Handler or Interrupt Service Routine (ISR) and is the function which runs when
the exception is triggered. The ARM hardware will automatically look up this function
pointer in the Vector Table when an exception is triggered and start executing the code.
⮚ The first thing it must do is copy the Current Program Status Register into a Saved
Program Status Register, and in particular the SPSR belonging to the new mode
associated with the exception.
⮚ The mode bits will be changed, further interrupts may be disabled, and the state will
change from Thumb state to ARM state if the processor was executing Thumb
instructions Since exceptions cause the code to jump to a new location.
⮚ Save the return address to the Link Register associated with the exception type.

⮚ Place the appropriate vector address of the exception mode in the Program Counter

This whole process of storing registers to external memory takes time, and again, depending
on the type of exception, may cause an unacceptable delay in processing an exception. To
prevent having to store data on the stack before handling the FIQ interrupts, there are also
five additional general-purpose registers that the handler can access.
Exception handlers can be intentionally short, or long, robust routines depending on how
much needs to be done for any given exception. At the end of all handlers, the programmer is
responsible for restoring the state of the machine and returning back to the original
instruction stream before the exception.
The instructions to do these operations only exist in the ARM instruction set, which was why
the processor had to switch from Thumb to ARM if it was executing Thumb code.

Exception Priorities
Exceptions must be prioritized in the event that multiple exceptions happen at the same time.
Exceptions are handled in turn before execution of the user program continues. The
Undefined Instruction and SWI exceptions are mutually exclusive because they are both
triggered by executing an instruction

Procedure for Exception Handling

⮚ Set up exception vectors

⮚ Initialize the memory system

⮚ Initialize all required processor mode stacks and registers

⮚ Initialize any critical I/O devices

⮚ Initialize any peripheral registers, control registers, or clocks, such as a phase-locked


loop (PLL)
⮚ Enable interrupts

⮚ Change processor mode and/or stat


Exception Handling
An exception is any condition that needs to halt normal execution of the instructions.
Examples

⮚ Resetting ARM core

⮚ Failure of fetching instructions

⮚ Undefined instruction is encountered

⮚ Hardware Interrupt is executed (HWI)

⮚ Software Interrupt is executed (SWI)

Exceptions include events like an interrupt, and this can be any kind of interrupt, like someone
moving a mouse or pushing a button.
Technically, anything that breaks a program’s normal flow could be considered an exception, but it’s
worth detailing the different types, since some can be readily handled and others are unexpected and
can cause problems.
Interrupts
Interrupts are very common in microprocessor systems. They provide the ability for a device such as a
timer or a USB interface to poke the processor in the ribs and loudly announce that it wants attention.
These are the two interrupt lines going into the processor, with a low-priority interrupt called IRQ and
a high-priority interrupt called FIQ.
The interrupt will halt the normal processing routines in the ARM core to allow the interrupt request
to be serviced. The ARM core can handle up to five exceptions, however, our focus is on interrupt
handling from an IRQ or FIQ request.

Why Interrupts?
UART waiting for the processor to get the data.
There are roughly three ways the processor can handle this situation
⮚ The processor is always in a loop doing absolutely nothing except waiting for the character to
show up. (Time consuming & least efficient)
⮚ The processor occasionally checks the memory location to see if there is some new data there,
known as polling.
⮚ The processor can spend its time performing other functions, such as updating a display or
converting MP3 data to an analog waveform, while it waits for a slower device to complete its
task.

Error conditions
⮚ Exceptions in a system, do occur often enough that software needs to be sufficiently
robust to handle them.
⮚ The ARM cores recognize a few error conditions, some of which are easy to handle,
some of which are not.
⮚ If the design requires floating-point operations, but the processor does not support
floating-point in hardware, you could decide to use floating-point instructions and
emulate them in software.
⮚ The processor can then take the necessary actions to perform the operations anyway,
using only software to emulate the floating-point operation, appearing to the user as if
floating-point hardware were present.
⮚ Data and prefetch aborts are the exception types that often cause programmers the
most angst.
⮚ A prefetch abort occurs when the processor attempts to grab an instruction in memory
but something goes wrong
⮚ Certain memory regions may be configured as being readable but not writable, and an
attempt to write to such a region can cause a data abort. As with prefetch aborts, the
processor usually needs to be able to recover from some situations and often has
hardware to assist in the recovery.

Processor Exception Sequence


The ARM7TDMI processor has a defined sequence of events to start the handling and
recovery of the exception.
The following sequence begins for all the cases except for reset exception.
⮚ The CPSR is copied into SPSR_<mode>, where <mode> is the new mode into which
the processor is about to change.
⮚ The core will switch to ARM state if it was in Thumb state, as certain instructions do
not exist in Thumb that are needed to access the status registers. The core will also
change to the new exception mode, setting the least significant 5 bits in the CPSR
register. IRQ interrupts are also disabled automatically on entry to all exceptions. FIQ
interrupts are disabled on entry to reset and FIQ exceptions.
⮚ The return address is stored in LR_<mode>

⮚ The Program Counter changes to the appropriate vector address in memory.

⮚ The processor is responsible for all the actions.

⮚ The processor begins executing code from an exception handler

⮚ After the above step the processor should then return to the main code (whether or not
it returns to the instruction that caused the exception depends on the type of
exception).
⮚ The handler may restore the Program Counter to the address of the instruction after
the one that caused the exception.
⮚ Two things which has to be done
o The CPSR must be restored from SPSR_<mode>, where <mode> is the
exception mode in which the processor currently operates.
o The PC must be restored from LR_<mode>.
These actions can only be done in ARM state.

Vector Table
For each type of exception, there is usually a dedicated block of code, called an exception
handler, that is responsible for acknowledging an exceptional condition and, more often than
not, fixing it. Not all exceptions need handlers, and in some deeply embedded systems, the
processor may not be able to recover.
The machine may be programmed to reset itself if something like (memory location that is
not physically present and address in the memory map does not point to a memory chip ) that
ever happens.
Larger applications, such as a cell phone, will have to deal with all exceptions and provide
robust methods to recover.
1. Reset exception: Reset exception is the highest priority exception and is always taken
whenever it is signaled. It initializes the system including setting up memory and
caches
It must set up stack pointers for all processor modes.
2. Data abort exception: It occurs only when the memory controller indicates that an
invalid memory address. It occurs only when the current code attempts to read or
write to memory without correct access permission .FIQ exceptions can be raised only
when data abort handler’s data abort exception are not disabled.
3. Fast Interrupt Request FIQ: FIQ exceptions occur when an external peripheral sets the
FIQ pin to nFIQ. It is of highest priority and the cores disables FIQ and IRQ
exceptions on entry into the FIQ handler.
4. Interrupt Request IRQ: IRQ exceptions occur when an external peripheral sets the
IRQ pin to nIRQ. It is of second highest priority. This will only enter if and only if an
FIQ or Data Abort exception occurs
5. Prefetch Abort Exception
It occurs when the attempt to fetch instructions results in memory fault, This is raised
in the execute stage of pipeline.
6. Software Interrupt SWI: Occurs when SWI instruction is executed, The cpsr will be
set to supervisor mode, The system uses nested SWI calls, the link register r14 and
spsr must be stroed away before branching to the nested SWI.
7. Undefined Instruction Exception : This occurs when an instruction not in the ARM or
Thumb instruction set reaches the execute stage of the pipeline. Both SWI and
UNDEFINED Instruction have same priority.
The instruction being executed cannot be both SWI and UNDEFINED Instruction

Exception Handler
Exception Handler or Interrupt Service Routine (ISR) and is the function which runs when
the exception is triggered. The ARM hardware will automatically look up this function
pointer in the Vector Table when an exception is triggered and start executing the code.
⮚ The first thing it must do is copy the Current Program Status Register into a Saved
Program Status Register, and in particular the SPSR belonging to the new mode
associated with the exception.
⮚ The mode bits will be changed, further interrupts may be disabled, and the state will
change from Thumb state to ARM state if the processor was executing Thumb
instructions Since exceptions cause the code to jump to a new location.
⮚ Save the return address to the Link Register associated with the exception type.

⮚ Place the appropriate vector address of the exception mode in the Program Counter

This whole process of storing registers to external memory takes time, and again, depending
on the type of exception, may cause an unacceptable delay in processing an exception. To
prevent having to store data on the stack before handling the FIQ interrupts, there are also
five additional general-purpose registers that the handler can access.
Exception handlers can be intentionally short, or long, robust routines depending on how
much needs to be done for any given exception. At the end of all handlers, the programmer is
responsible for restoring the state of the machine and returning back to the original
instruction stream before the exception.
The instructions to do these operations only exist in the ARM instruction set, which was why
the processor had to switch from Thumb to ARM if it was executing Thumb code.

Exception Priorities
Exceptions must be prioritized in the event that multiple exceptions happen at the same time.
Exception is handled in turn before execution of the user program continues. The Undefined
Instruction and SWI exceptions are mutually exclusive because they are both triggered by
executing an instruction

Procedure for Exception Handling

⮚ Set up exception vectors

⮚ Initialize the memory system

⮚ Initialize all required processor mode stacks and registers

⮚ Initialize any critical I/O devices

⮚ Initialize any peripheral registers, control registers, or clocks, such as a phase-locked


loop (PLL)
⮚ Enable interrupts

⮚ Change processor mode and/or stat

You might also like