Saturday, 6 April 2013

[17 September 2012] A Light-Activated Shock Detector - Part I

17 September 2012
National University of Singapore
EE2024 PROGRAMMING FOR COMPUTER INTERFACES
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 - )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
MLA R8, R7, R7, R8
R8 =  (xi - )+ previous value of 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 - )2 for the current value loaded from the circular buffer and update the value of R8. Thus, R8 will be equal to Σ(xi - )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 - )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