17 September 2012
National University of Singapore
EE2024 PROGRAMMING FOR COMPUTER INTERFACES
A Light-Activated Shock Detector - Part I
A Light-Activated Shock Detector - Part I
Project Report
Abstract
The aim of this project is to implement a night-time shock detector which can either operate on its own in "detect mode", or as part of a larger system in "co-operative mode". This is achieved by integrating C Programming and Thumb-32 bit Assembly language to instruct an ARM-Cortex M3 Microprocessor chip to control various peripherals such as a light sensor and an accelerometer. This report focuses mainly on how thumb-32 assembly language was used to implement a sub-program which counts the mean and variance for the last 6 values parsed in from the "main C program". For more information about how C programming was used to implement "Detect Mode" and "Co-operative Mode", please refer to part 2 of the project here. This project was carried out in August 2012 - November 2012 as a group of 2 team-mates.
Contents
1 Introduction
- Introduction
2 Implementation
of the Circular Buffer
- 2.1 Overview
- 2.2 When ‘Reset’ is True
- 2.3 When ‘Reset’ is False
3 Calculation of
Variance and Max Values
- 3.1 Finding Variance
- 3.2 Finding the maximum value within the circular buffer
- 3.3 Storing the “max” value from register to memory
4 Conclusion
- Conclusion
Appendix
- Appendix A: Actual Code for Assessment
1 Introduction
In
this project, we have implemented an assembly language code which calculates
the variance and the maximum values of up to ‘N’ inputs parsed in from a main C
program. In order to do so, we have split the project into two main components.
Firstly, the implementation of a circular buffer to store the last ‘N’ inputs
parsed in, and secondly, the calculation of the variance and maximum values of those
inputs. Within this report, we will be using the project-specified value of ‘N’
= 6 for all illustrations provided.
2 Implementation of the Circular Buffer
2.1 Overview
We
have implemented the buffer for this project using the “.lcomm” command to a
reserve block of ‘N + 2’ memory locations. ‘N’ locations are allocated for
storing the last ‘N’ inputs parsed in from the main C program, and the two
additional locations store firstly, the address of where the input from the
current cycle is being stored (CBptr), and secondly the count of the number of
values currently stored within the buffer (CBcnt). Figure 1 shows the actual
memory address locations in use when we run our program and a description of
their contents.
Memory Address
|
Contents
|
Remarks
|
0x10000204
|
The address of the buffer location that is currently being used to
store the ‘x’ input. (One of the addresses highlighted in blue)
|
CBptr
|
0x10000208
|
The number of inputs currently stored in the Buffer
|
CBcnt
|
0x1000020c
|
The last 6 inputs of 'x' parsed in from the main C program
|
Buffer
|
0x10000210
|
||
0x10000214
|
||
0x10000218
|
||
0x1000021c
|
||
0x10000220
|
Figure
1. Table of Contents of the Circular Buffer for ‘N’ = 6
2.2
When ‘Reset’ is True
‘Reset’
is triggered when the main C program parses the value ‘1’ as the 3rd
argument. This can be verified by checking the content of the register R2,
using the “CMP” command to compare the value of R2 with ‘1’, then branching to
the “reset_true” label within our code if it is true, and to the “reset_false”
label instead if it is false.
CBptr
needs to be updated to store the address of the first memory location within
the circular buffer when ‘Reset’ is triggered. This is done by loading two
registers, R4 and R5, with the addresses of CBptr and the first memory location
of the circular buffer respectively. The contents of R5 are then stored to the
memory location which is pointed to by the address stored in R4.
Since
the main C program parses in values of ‘x’, ‘’, and ‘int* max’ together with ‘Reset’,
there will always be at least one input which needs to be stored in the
circular buffer every time the program is run. Thus, CBcnt needs to be reset to
‘1’ and not ‘0’ when ‘Reset’ is triggered. This can be done by loading two
registers, R4 and R5, with the addresses of CBcnt and the number ‘1’
respectively. The contents of R5 are then stored to the memory location which
is pointed to by the address stored in R4.
The
contents of the memory locations within the circular buffer need to be set to
zero when ‘Reset’ is triggered. This can be done by loading three registers,
R4, R5 and R6, with the values of ‘N’, ‘0’ and the address of the first memory
location within the circular buffer respectively. Using R4 as the “count”
register, we created a loop which stores the contents of R5 into the memory
location which is pointed to by the address stored in R6 ‘N’ times. At the end
of every loop, R6 is incremented by ‘4’ so that the next time the loop is run,
‘0’ will be stored in the subsequent memory location within the circular buffer,
and R4 is decremented by ‘1’ and compared with ‘0’ to see if the loop needs to
be run again.
An
unconditional branch instruction to the “variance” label is then as carried out as the program should skip the "reset-false" segment of instructions if "reset" is true.
2.3 When ‘Reset’
is False
If
the content of the register R2 is ‘0’, the program branches to the “reset_false”
label which uses IT block instructions to execute conditional updates on the
values stored in CBcnt and CBptr.
The
value of CBptr should be incremented by ‘4’ unless it is currently stored with
the value of the last memory location of the circular buffer. Thus, our program
conditionally updates the contents of CBptr by comparing the value stored
within CBptr with the address of the first memory location within the circular
buffer incremented by 4(N – 1). If they are equal, the address of the first
memory location with the circular buffer will be stored inside CBptr, and the
value of CBptr will be incremented by ‘4’ otherwise. Figure 2 shows the update
cycle of CBptr.
Figure
2. CBptr update cycle for ‘N’ = 6
The
value of CBcnt is then updated after comparing the content of CBcnt with the
value of ‘N’. If they are equal, then CBcnt need not be updated, and CBcnt is
incremented by ‘1’ otherwise.
3 Calculation of Variance and Max Values
3.1 Finding
Variance
Given
that the formula for calculating variance is σ2 = [Σ(xi - x̄)2] / n, we have coded our program to store the
value of the current ‘x’ parsed in from the main c program inside the circular
buffer, extract and arithmetically manipulate the contents stored within the
circular buffer one by one in order to obtain a cumulative sum of
before finally dividing the result by the
value stored within CBcnt to get the result for variance.
The
value of ‘x’ is parsed in from the main C program into register R0. In order to
store it within the circular buffer, we first load register R4 with the address
of CBptr before loading register R5 with the contents of the memory location
pointed to by the address in R4. Since R5 now holds the address location within
the circular buffer which the current value of ‘x’ should be stored, we can
then store the contents of R0 into the memory location pointed to by the
address in R5.
Figure
3 displays detailed explanations of the code showing how the program utilizes a
loop to perform a cumulative summation for
,
before dividing the result by the value of CBcnt to obtain the final result for
variance.
Instruction
|
Comments
|
LDR R4, =CBcnt
|
R4 = address of CBcnt
|
LDR R5, [R4]
|
R5 = value of CBcnt
|
LDR R6, =Buffer
|
R6 = address of the first memory
location within the circular buffer
|
MOV R8, #0
|
Initialize R8 to '0'
|
loop_sum: LDR R7, [R6]
|
R7 = xi
|
SUB R7, R7, R1
|
R7 = xi - x̄
|
MLA R8, R7, R7, R8
|
R8 =
|
CMP R5, #1
|
Check if R5 is '1', if not then
update R6 and R5, branch back to the "loopsum" label and continue loading values from
subsequent locations within the circular buffer, calculate the (xi - x̄)2 for the current value loaded
from the circular buffer and update the value of R8. Thus, R8 will be equal
to Σ(xi - x̄)2 when the loop ends.
|
ITTT NE
|
|
ADDNE R6, R6, #4
|
|
SUBNE R5, R5, #1
|
|
BNE loop_sum
|
Figure
3. Code segment for cumulative summation of (xi - x̄)2
R8
is then divided by the value stored within CBcnt to obtain the variance.
Following which, the value of R0 is replaced with the value in R8 so that the
variance will be returned to the main C program when the “BX LR” instruction is
given.
3.2
Finding the maximum value within the circular buffer
The
number of values currently stored within the circular buffer can be obtained
from the value stored within CBcnt. Taking that value as ‘n’, the program
utilizes a loop to perform ‘n – 1’ comparisons between the values stored inside
the circular buffer in order to determine the maximum. The program also checks
first to see if ‘n’ is ‘1’ and if so, skips the loop and branches to the
“storemax” label instead. Figure 4 shows the exact coding instructions which
carry out this operation with detailed explanations.
Instruction
|
Comments
|
LDR R4, =Buffer
|
R4 = address of first memory location
within circular buffer
|
LDR R5, = [R4]
|
R5 = content of first memory location
within circular buffer
|
LDR R6, = CBcnt
|
R6 = address of CBcnt
|
LDR R7, [R6]
|
R7 = value stored in CBcnt = 'n'
|
SUB R7, R7, #1
|
n = n - 1
|
MOV R8, #0
|
Set R8 to '0'
|
CMP R7, R8
|
If R8 = '0', R5 is maximum. Skip the
loop and branch to "storemax" label.
|
BEQ store_max
|
|
loop_max: ADD R4, R4, #4
|
Increment R4 to the next address
within circular buffer
|
LDR R9, [R4]
|
R9 = content of subsequent memory
location in circular buffer
|
CMP R9, R5
|
Compare R9 and R5. The greater value
will be stored inside R5
|
IT GT
|
|
MOVGT R5, R9
|
|
SUB R7, R7, #1
|
Update the value of R7, the
"count" register, compare R7 with '0' and loop back to
"loopmax" label if not equal. R5 will be the maximum value within
the circular buffer when the loop ends.
|
CMP R7, R8
|
|
BNE loop_max
|
3.3 Storing the
“max” value from register to memory
Since
the ‘int* max’ address location was parsed into register R3 by the main C
program, we can store the maximum value the program has determined into that
address location. The address of the C variable “max” is 0x10007fc8 which
was determined by putting a breakpoint at the beginning of the assembly
language function and reading the value of R3 when the debugging process paused
at that breakpoint. This method of parsing address values as arguments from the
main C program to assembly language functions can be used, in general, to
return multiple values from assembly language functions to the main C program.
4 Conclusion
Through
this project, we have become more familiar with assembly language programming.
More importantly, we have learnt the importance of using high level programming
languages such as C or C++ in conjunction with assembly languages such as CISC
or RISC. They each have their pros and cons and it is crucial to be fluent in
both types in order for us to be able to code and execute programs with the desired
efficiency and performance
Appendix
Appendix A:
Actual Code for Assessment
1 @directives
2 .syntax unified
3 .cpu cortex-m3
4 .thumb
5 .align 2
6 .global asm_variance
7 .thumb_func
8
9 asm_variance:
10
11 PUSH {R4-R9}
12
13 N:
14 .word 6
15
16 .lcomm CBptr 4
17 .lcomm CBcnt 4
18 .lcomm Buffer 4
19
20 @checking R2 to see if 'Reset' has been triggered
21 CMP R2, #1
22 BEQ reset_true
23 B reset_false
24
25 reset_true:
26 @makes CBptr point back to location of 'Buffer'
27 LDR R4, =Buffer
28 LDR R5, =CBptr
29 STR R4, [R5]
30
31 @resets CBcnt to one
32 LDR R4, =CBcnt
33 MOV R5, #1
34 STR R5, [R4]
35
36 @flushes values in buffer to 0
37 LDR R4, =Buffer
38 MOV R5, #0
39 LDR R6, N
40
41 flush:
42 STR R5, [R4]
43 SUB R6, R6, #1
44 ADD R4, R4, #4
45 CMP R6, #0
46 ITT NE
47 ADDNE R4, R4, #4
48 BNE flush
49
50 B variance @skip 'reset-false' section if reset is '1'
51
52 reset_false:
53 @updating the value of CBcnt
54 LDR R4, =CBcnt
55 LDR R5, [R4]
56 LDR R6, N
57 CMP R5, R6
58 IT NE
59 ADDNE R5, R5, #1
60 STR R5, [R4]
61
62 @updating the value of CBptr
63 LDR R4, =CBptr
64 LDR R5, [R4]
65 LDR R6, =Buffer
66 LDR R7, N
67 SUB R7, R7, #1 @R7 = N-1
68 LSL R7, R7, #2 @R7 = 4*(N-1)
69 ADD R7, R7, R6 @R7 = last address of Buffer
70 CMP R5, R7 @Compare CBptr value with last address of Buffer
71 ITE EQ
72 MOVEQ R5, R6
73 ADDNE R5, R5, #4
74 STR R5, [R4]
75
76 variance:
77 @storing 'x' inside the buffer
78 LDR
R4, =CBptr
79 LDR R5, [R4]
80 STR R0, [R5]
81
82 @calculating summation of (x-xbar)^2
83 LDR R4, =CBcnt
84 LDR R5, [R4] @R5 = value of count
85 LDR R6, =Buffer
86 MOV R8, #0 @initialize R8 to zero
87
88 loop_sum:
89 LDR R7, [R6]
90 SUB R7, R7, R1
91 MLA R8, R7, R7, R8
92 CMP R5, #1
93 ITTT NE
94 ADDNE R6, R6, #4
95 SUBNE R5, R5, #1
96 BNE loop_sum
97
98 LDR R4, =CBcnt
99 LDR R5, [R4]
100 SDIV R8, R8, R5
101
102 MOV R0, R8
103
104 @computing max
value of items stored in Buffer
105 LDR R4, =Buffer
106 LDR R5, [R4]
107 LDR R6, =CBcnt
108 LDR R7, [R6]
109 SUB R7, R7, #1 @only need to compare N-1 times
110 MOV R8, #0
111 CMP R7, R8
112 BEQ store_max
113
114 loop_max:
115 ADD R4, R4, #4
116 LDR R9, [R4]
117 CMP R9, R5
118 IT GT
119 MOVGT R5, R9 @R5 stores max upon loop completion
120 SUB R7, R7, #1
121 CMP R7, R8
122 BNE loop_max
123
124 store_max:
125 MOV R8, R3
126 STR R5, [R8]
127
128 POP {R4-R9}
129
130 BX LR
No comments:
Post a Comment