Download as pdf or txt
Download as pdf or txt
You are on page 1of 77

PROGRAMMING

LANGUAGES
High-level Languages
Object-Oriented vs. Structured Languages
Assembly Language
Outline

• Assembly Language
• Structure Programming, c-programming language
• Object Oriented Programming
• Object-based Perspective of Structured Language
• Mixed C & Assembly Projects
• Modeling time-ordered behavior using State Machines
Introduction

• CPU is a programmable machine that is designed to


implement a specific instruction set
• CPU Instruction Set Architecture is designed to fulfill all
the programming requirements
– Data movement to/from memory, a.k.a. load/store
instructions
– Arithmetic and logic data manipulation
– Program flow & control, e.g. function call, loops, and
branches
– Input / Output operations to bring data from
external sources to memory and move data from
memory to external devices
Assembly Language

• Instruction Set is CPU specific and very hard to use


directly
• The target of a CPU program is a machine code program
that is not readable
• Assembly languages were devised to make readable
form of machine code
– Instruction OPCODE is represented by symbols with
abbreviated meanings
– Memory Addresses are replaced by labels, which
could also have meaningful names
Assembly Language

• Each line of assembly code represents a machine


instruction: Op-Code + Operand
• Each line of assembly code maps directly into a memory
location since the instruction size is predefines, the
corresponding memory address can be calculated from
top to bottom
• Each line of assembly code is divided into 3 sections
1. Label: optionally used to refer to the physical
memory address of line
2. Opcode/Operands: the actual program content
3. Comments: usually preceded by ‘;’ and is used to
explain the intent behind the instruction to help others
understand the developer intent
Assembler Directives

• Assembler Directives pseudo-instructions are not a


machine instruction, and hence is not translated into a
machine-coded instruction by the assembler.
• A pseudo-instruction is rather an instruction that
provides the assembler with some type of information
about a particular instruction in the assembly code.
Assembly Language Example
Operand Field

• Operand can be:


– Name of a register
– Numeric or symbolic constants
– Labels (translates into memory address)
– Algebraic expressions evaluated by the assembler
– Examples:
 MOVB A,DATA1 ; A register, symbolic name
 MOVB A,DATA1+1 ; expression DATA1+1
Assembler Macros

• Collects frequently used instructions into a single


statement (macro)
– Macro definition: In the source code, two assembler
directives used to indicate start and end of macro,
for example DEF_MACRO and END_MACRO
– Macro invocation: In the source code, macro label
used in the operation code field
– Macro expansion: Assembler expands the macro
name into the full code specified in the macro
definition
Macro definition

Add_B_To_C DEF_MACRO
MOV A,C
ADD A,B
MOV C,A
END_MACRO

10
Macro Invocation

op_code operand
op_code operand
Add_B_To_C
op_code operand
op_code operand

11
Macro Expansion

op_code operand
op_code operand
Add_B_To_C
MOV A,C ; Assembler inserts
ADD A,B ; these three
MOV C,A ; lines
op_code operand
op_code operand

12
Macros Versus Subroutines

• Similar since both allow the reuse of code segments


– But with subroutine, code is included only once
– Macros are expanded “in-line” at each macro
invocation – makes the program longer
• Subroutine requires a call or jump-to-subroutine (macro
does not), thus subroutines are a little slower
• Macros expand to code, so the program takes more
memory space
• Both macros and subroutines make program more
modular and easier to read
– Changes only need to be made in one place
– Hides details of the segment code – only need to
know what it does, not how it does it
Two Pass Assemblers

• Assembler makes two passes during processing of the


code
1. First pass is used to create a symbol table
 Symbol table provides the values for symbols
2. Second pass generates the machine code using the
values found in the symbol table
Cross Assemblers & Native Assemblers

• Cross Assembler: Assembler running on a computer to


compile for a different processor
– For example, using an assembler on a PC running an
Intel processor to compile programs for the
MC68HC12
• Native Assembler: Assembler creates code for the local
processor
– Part of system compilers
The Code Location Problem

• Microcontrollers has different memory types and


regions:
– SRAM
– DDR
– Flash
– ROM
• Code is written to reside in different regions
• Assemblers will use a linker file to specify the address
map of the available regions
– Address Map identify the start address and size of
the memory region
Relocatable Assemblers

• Relocatable assembler: Creates object files that does


not resolve addresses
• Linker: Combines object files to create single executable
program (resolves addresses at linking time when
creating binary)
The Embedded Tool Chain
• Most embedded systems are created on a different
system (the host) than they execute on (the target)
• This has a number of effects on the developer and the
tools used in the creation of embedded systems
• cross-compiler is a compiler that runs on the host but
creates executable code that runs on the target
• linker is a program that combines a set of executable
codes together into an executable for a target.
• loader is a tool that loads the object image output from
the linking step into the memory of the target
environment
– As an alternative to loading the software image on a target platform, many
developers use simulators for the target that execute on their host
development systems
The Embedded Tool Chain

• debuggers – tools that give us a great measure over the


execution of the software, including the ability to step
into (execute line by line) or step over (execute in
entirety) functions, set breakpoints, and to examine and
modify variables. These debuggers may work over
standard serial and network links or over JTAG ports
• Modern-day Integrated Development Environments
(IDEs) link together most or all of the tools in the
embedded tool chain to facilitate and automate the
development process.
• The latest industry trend is to host the IDEs in the Eclipse platform4 because of
the power of the integrated environment and the availability of third-party and
open-source plug-ins.
High-Level Programming Languages

• Assembly language is mapped directly on the CPU


instruction set – i.e. it’s processor dependent
• Assembly programs are hardly portable
• There is a need to hide the details of the CPU/System
specifics and to write applications focusing on the data
rather than the hardware components of the system
• High-level programming languages are created to serve
that purpose
– All the developer to focus on the data processing
– Wrap hardware components with abstract software
application programing interface
Structured Programming

• Historically the first attempts of high-level languages


were structured in nature
• Structured Programming is a disciplined form of
software development that emphasizes two separate
and distinct aspects:
– Functions or Procedures form the foundation of
behavioral programming
– Data Structures: builds complex data structures
from more primitive elements, ultimately basing
them on the basic types provided by the computer
language
 may be homogeneous collections, such as arrays, or
 heterogeneous ones, as with C-structs
Structured Language

• A structured language is a language that directly


supports structured programming.
• C is clearly a structured language.
– C is by far the most prevalent language for creating
embedded systems and C is an inherently structured
language.
– It has all the earmarks – functions, variables, and so
forth.
Object-oriented Programming

• Object-oriented programming is based on an the notion


that rather than have two separate taxonomies
(procedures, data), the two are combined in one entity.
• Together both data (stored in attributes) and
procedures (also known as operations or methods) that
operate on that data, define a class.
– Beneficial because they tend to be inherently tightly
bound anyway
– The encapsulation give way to inheritance
• An object is an instance of a class. This makes an object equivalent
to, but more powerful than, a variable in a structured language
because the object provides both the values held in its attributes and
the operations that manipulate them
Object-oriented Programming

• Encapsulation: is the combination of data


structure/attributes and functions/method in the same
entity called the class
• Inheritance: is the ability to extend classes and reuse all
their functionality and extend the functionality or
override it with new implementation code
• Isomorphism: is the ability to use a base (abstract)
class in algorithm/application development and then use
variants of implementations without changing the
original code
Object-based Perspective

• An interesting question arises: can object-oriented


designs be developed with a structured language, such
as C?
• Using object-based perspective in the programming we
can use a structured language like c to develop OO
designed systems
– (object-oriented without sub-classing)
• The creation of object-based or object-oriented
programs in C is pretty straightforward
Object-based Perspective - Classes

• A class is really nothing more than a C struct, but what


is special about it is that it contains two different kinds
of features:
1. data (attributes) and
2. behavioral (operations)
• Use the file as the encapsulating boundary;
– public variables and functions can be made visible in
the header file,
– the implementation file holds the function bodies and
private variables and functions
• a class is represented as a pair of files and its implementation uses
some features (variables or functions) of the class (file)
Classes
Class Example – header file

#ifndef __SENSOR_H__
#define __SENSOR_H__

typedef struct { int filterFrequency; int updateFrequency; int value; } Sensor;


int getFilterFrequency (const Sensor* const me);
void setFilterFrequency (Sensor* const me, int p_filterFrequency);
int getUpdateFrequency (const Sensor* const me);
void setUpdateFrequency (Sensor* const me, int p_updateFrequency);
int getValue (const Sensor* const me);
Sensor * Sensor_Create (void);
void Sensor_Destroy (Sensor* const me);
Class Example - usage
#include "Sensor.h"
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char* argv[]) {
Sensor * p_Sensor0, * p_Sensor1;
p_Sensor0 = Sensor_Create();
p_Sensor1 = Sensor_Create();
/* do stuff with the sensors ere */
p_Sensor0->value = 99; p_Sensor1->value = -1;
printf("Sensor0 value is %d\n", getValue(p_Sensor0));
printf("Sensor1 value is %d\n", Sensor_getValue(p_Sensor1));
/* done with sensors */
Sensor_Destroy(p_Sensor0); Sensor_Destroy(p_Sensor1); return 0;
}
Polymorphism & Virtual Functions

• Polymorphism allows for the same function name to


represent one function in one context and another
function in a different context
• When either the static or dynamic context of an element
changes, the appropriate operation can be called
• The standard way to do this in C is to use conditional
statements such as if or switch
– quickly becomes unwieldy when many different
contexts are available
– all possible contexts must be known when the
original function is written
– code must be modified to allow a new context
Polymorphism Example

• Consider a sensor in which the actual interface may


differ for the getValue function:
– in one context may require accessing a memory-
mapped sensor, waiting for a while and then reading
a different address to acquire the value
– A different context may use a port-mapped sensor
– We would like that the client of the sensor doesn’t
know how the interface to the sensor works – make
application development simple
Polymorphism Example – standard way

int getValue (Sensor *me) {


int j, *r, *w; /* read and write addresses */
switch (me->type) {
case MEMORYMAPPED:
w=(int*)WRITEADDR; /* address to write to sensor */
*w = WRITEMASK; /* sensor command to force a read */
for (j=0;j<100;j++) { /* wait loop */ };
r=(int *)READADDR; /* address of returned value */
me->value = *r; break;
case PORTMAPPED:
me->value = inp(SENSORPORT);
break;
} /* end switch */
return me->value; }
Polymorphism Example – elegant way

#ifndef __SENSOR_H__
#define __SENSOR_H__

typedef struct { int filterFrequency, updateFrequency, value, type;


/* function pointer specific implementation */
int (*getValue) (const Sensor* const me);
} Sensor;
int getFilterFrequency (const Sensor* const me);
void setFilterFrequency (Sensor* const me, int p_filterFrequency);
int Sensor_getUpdateFrequency (const Sensor* const me);
void setUpdateFrequency (Sensor* const me, int p_updateFrequency);
int getValue_typ1 (const Sensor* const me);
int getValue_typ2 (const Sensor* const me);
Sensor * Sensor_Create (int type);
void Sensor_Destroy (Sensor* const me);
Polymorphism Example – elegant way

• Create function set the type-specific function pointer:


Sensor * Sensor_Create (int type){
// allocate struct in memory (heap)
Sensor *s = (Sensor*) malloc ( sizeof(Sensor) );
s->type = type; /* set the type value */
switch (type) {
// assign function pointer based on type selected
case TYPE1: get_value = getValue_typ1(this) ; break;
case TYPE2: get_value = getValue_typ2 (this); break;
default: /* error unsupported type! */
}
}
Class Extension - Inheritance

• By extension we mean that the child class will have


new features, such as new attributes or operations
– Done by making the extending struct own the
original struct

Two different relations are shown in the figure.


1) The line with the closed arrowhead is the generalization relation; the line points to
the more base class.
2) 2) The other line is known as composition. The composition is relation that implies
strong ownership, and the responsibility for creation and destruction of the instance
of the class at the other end of the relation. The open arrowhead depicts navigation
(the owner has a pointer to the part in this case, but not the other way around). The
name near the arrowhead is the name of the pointer within the CachedQueue class.
The number near the arrowhead is the multiplicity – the number of instances needed
to play the role (in this case, a single instance is needed).
Mixed C & Assembly Projects

• In most situations, application code will be written in C


or other high-level language
– Therefore it is not necessary for software developers
to know the details of the instruction set
• It is useful to have general overview of what
instructions are available and assembly language
Syntax
– Can be very useful in debugging
Mixed C & Assembly Projects

• For small projects it is possible to write the whole


project in assembly, however it is rare because:
– It is harder to develop in assembly
– Device drivers provided by vendors are in C
– Bugs are harder to find & debug
– The assembly code is not portable
Mixed C & Assembly Projects

• There are some situations you need to use assembly


language for some parts of a project:
– To allow direct manipulation of stack memory, e.g. in
context switching in OS
– To optimize for maximum speed/performance or
minimum program size
– To reuse assembly code from old projects
– To learn about processor architecture
Mixed C & Assembly Projects

• There are several ways to add assembly code to a c


project:
– Write functions in assembly and call them from C
– Assembly code could be instruction sequences
inserted inside C using inline assemble
– Use C-wrappers of assembly code provided by
vendor
The Assembly Language Syntax - ARM

• For Arm assembly the following instruction formatting is


used:
label
mnemonic operand1, operand2, … ; Comments
– Label: reference an address
– Mnemonic: is the name of the instruction, followed
by a number of operands
 For data processing, the first operand is the destination
 For memory read, the first operand is the register destination
 For memory write, the first operand is the register source
ASSEMBLY FUNCTIONS

ARM
Passing Arguments C ↔ ASM

• To make C & Assembly work together we need to pass


function arguments and return value
• General purpose registers are used for passing fuction
arguments
Procedure Call Standard - ARM

• Register Usage in Function Call: a function should retain


the values of GPReg R4-R11, R13, R14. If used in the
function, they must be pushed to stack at the start,
then poped from stack at the end
• Parameter and Result Passing: R0-R3 for input, R0 for
result
• Stack Alignment: if assembly function is calling C
function the it should insure that the stack pointer (SP)
points to a double word (64b aligned), i.e. the SP
address should be divisible by 8
Procedure Call Standard - ARM
Compiler Directive
Structure of Assembly Function

Function Code
Structure of Assembly Function
Calling C from Assembly
C Function
Assembly Code
Calling Assembly from C
Assembly Code
C Code
STATE MACHINES

Modeling time-ordered behavior


Time-ordered behavior

• Time-ordered behavior is system functionality where


outputs depend on the order in which input events
occur
– Example: A toll booth may raise a toll gate when the
booth operator presses a button, then keep the gate
up as long as a car is detected by sensor near the
gate
Time-ordered behavior

• Most programming languages, e.g. C, was not designed


for time-ordered behavior.
– C uses a sequential instructions computation model,
wherein statements (instructions) in a list are
executed sequentially (one after another) until the
list's end is reached.
– A sequential instructions model is good for capturing
algorithms that transform given input data into
output data, known as data processing behavior
– The sequential instructions model is poorly-suited for
capturing time-ordered behavior
Adding delays – timely behavior

C program: count carousel turns


int start_sensor = 0;
void main() {
int numTurns = 0;
while (1) {/* loop forever */
while (! start_sensor ); /* wait for start */
numTurns = numTurns + 1;
while (start_sensor ); /* wait for sensor reset */
}
}
Adding delays – timely behavior

C program: count carousel turns + reset button


int start_sensor = 0; Int reset_button = 0;
void main() {
int numTurns = 0;
while (1) {/* loop forever */
while (! start_sensor & !reset_button); /* wait */
if (reset_button) numTurns = 0;
else numTurns = numTurns + 1;
while (start_sensor ); /* wait for sensor reset */
}
}
State Machines

• As timely behavior involve more events/triggers,


programming such behavior becomes harder to
understand/capture/debug/modify
• A state machine is a computation model intended for
capturing time-ordered behavior
• Common features of state machines are:
– a set of inputs and outputs,
– a set of states with actions,
– a set of transitions with conditions, and
– an initial state.
• A drawing of a state machine is called a state diagram.
State Diagram of Carousel Examples
State Diagram of Carousel
Implementing State Machines in C

• Because microprocessors typically have C compilers but


not SM compilers, implementing an SM in C is necessary
• Using a standard method for implementing an SM to C
enhances the readability and correctness of the
resulting C code
Implementing State Machines in C

1) Use enumeration for state names/values


enum States { start, s0, s1 } State;

2) Implement the Tick Function


Two switch/case blocks:
a) State transitions
b) State actions
Implementing State Machines in C

void TickFct() {
switch(State) { // Transitions
case start: State = s0; break; // Initial transition
case s0: if (!A0) { State = s0; } else if (A0) { State = s1; } break;
case s1: if (!A0) { State = s0; } else if (A0) { State = s1; } break;
default: State = start; break;
} // Transitions
switch(State) { // State actions
case s0: break; void main() {
case s1: B0 = A1; break; B = 0x00; // Initialize outputs
default: break; State = start; // Indicates initial call
while(1) { TickFct(); }
} // State actions
}
}
Mealy & More State Machines

• The state machine model associates actions with states


only, known as a Moore-type state machine
• A Mealy-type state machine allows actions on
transitions too
• A Mealy-type SM can make some behaviors easier to
capture
Capturing State Machine Behavior

• Step 1: A first step is to list the system's basic states,


adding actions if known
Capturing State Machine Behavior

• Step 2: Add transitions to each state to achieve the


desired behavior.
Capturing State Machine Behavior

• Step 3: Mentally check the behavior of the captured


SM.
– may result in adding more transitions, or even more states
– for each state, check that exactly one transition's condition
will be true, modifying the conditions or adding transitions
if necessary
– determine whether the SM's behavior is as desired, by
mentally executing the SM and thinking what input
sequences might occur.
Capturing State Machine Behavior

• Step 3: Mentally check the behavior of the captured


SM.

timeout
Testing a State Machines

• Testing time-ordered behavior requires generating a


good set of input scenarios, where a scenario is a
sequence of inputs that should cause a particular
sequence of state.
• Such testing is in contrast to merely generating a good
set of input combinations for a system lacking internal
states, each input combination known as a test vector
• A testing process for time-ordered behavior may consist of:
1. Decide what scenarios to test, including normal cases as well as border cases.
2. Devise a sequence of test vectors to test each scenario.
• Goal: visit all states and test every transition
Testing a State Machines
Testing a State Machines

• When testing an SM, test vectors should ensure that


each state and each transition is executed at least once.
• If a state's action code has branches, then test vectors
should also ensure that every statement is executed at
least once.
• Even more ideally, every path through the SM would
also be tested, but achieving complete path coverage is
hard because huge numbers of paths may exist
• In testing terminology, black-box testing refers to
checking for correct outputs only. In white-box testing,
internal values of the system are also checked
Capture & Convert Process

• Two-step process that is common in disciplined


embedded programming:
1. Capture the desired behavior using a computation
model appropriate for the desired system behavior,
such as SMs.
2. Convert that captured behavior into an
implementation, such as C that will run on a
microprocessor.
• The conversion is typically very structured and
automatable
• All modifications are done by changing the SM
(capture), and then updating the C code (convert).
Time-interval behavior

• Time-interval behavior is system functionality where


events must be separated by specied intervals of real
time.
– A time interval may be related to an embedded
system's input or output
– Output example: repeatedly blink an LED on for 500
ms and off for 500 ms. 500 ms is a time interval
– Input example: A system that reads the value on the
input every 20 seconds and output the average of
the last three reads
Synchronous State Machines

• State Machine (SM) tick was assumed to take a small,


unknown, non-zero amount of time
• The tick rate can be set to a specific rate such as 500
ms, known as the SM's period.
• An SM with a specific period is a synchronous SM, or
synchSM for short.
• A blinking LED system, where the LED is on for 1000 ms
(1 sec) and off for 1000 ms, can be captured as a
synchronous state machine
Synchronous State Machines

• One common input time-interval scenario is to read


input values only at certain times, each read known as a
sample
• Another common input time-interval scenario is to
measure the time between two input events.
– example, consider computing the speed of a
marathon runner as he/she passes two sensors A0
and A1 that are separated by 10 feet
Choosing the Sampling Period

• Sometimes one system involves multiple, different time


intervals
– Choose the period as the greatest common divisor or
gcd of the required time intervals, and then use
multiple states (or counting within a state) to obtain
the actual desired interval
Microcontrollers with timers

• Microcontrollers come with one or more timers to


measure time intervals
• A timer is a hardware component that can be
programmed to tick at a user-specified rate
• When the timer ticks, the microcontroller hardware
interrupts the main program's execution
• Interrupt means to temporarily stop execution of the
main program and call a special C function known as an
interrupt service routine or ISR
• When that ISR function finishes executing, execution
resumes where it previously stopped in the main
program
Microcontrollers with timers

• Every time the hardware timer ticks, TimerISR() gets


called automatically by the microcontroller.
• The user can insert code into the ISR that should be
executed whenever the timer ticks.
• The user's own main code should never call TimerISR()
directly.
1. user should set timer period
2. user should enable timer interrupt
Converting a synchSM to C

• A synchSM can be translated to C code for a


microcontroller with a timer. The code is similar to that
for an SM, with the following additions:
1. A TimerISR() function that simply call TickFct
function of the state machine (transition/actions)
2. main() code that initializes the timer to tick at the
synchSM's tick rate
3. main() code that go in idle/spin loop ( while(1); ),
i.e. wait forever
State actions should never wait

• A state's statements should be written such that they


execute and reach their end, a feature known as run to
completion
• A key assumption in a synchSM is that every state's
statements always run to completion faster than the
synchSM's period. Actions are assumed to run in a small
but non-zero time
• Functions can be declared in a synchSM and called by
any state's statements, as long as functions always run
to completion.
• Functions thus serve as a programming convenience to
eliminate redundant code in different states, or to make a
state's actions more clear or less cluttered
References

• The Definitive Guide to ARM Cortex-M3 & Cortex-M4,


chapter 5: Instruction Set, chapter 20: assembly coding
• Design Patterns for Embedded Systems in C, An
Embedded Software Engineering Toolkit - Bruce Powel
Douglass, PhD, Chapter 1: OO or Structured – It’s Your
Choice
• Programming Embedded Systems – zyBooks, Frank
Vahid, Tony Givargis, Bailey Miller

You might also like