************** *REQUIREMENTS* ************** 1) The Assembly program needs to receive the incoming x values from the C program and store them into the array (defined in the C program) in a cyclical fashion, with a reset feature 2) Use the values in the array (henceforth referred to as circular buffer, CB) and the mean value (provided by C program) to compute the variance and return it to the C program ***************** *CIRCULAR BUFFER* ***************** - C program stores x values in an array: int x[12]={2,4,2,4,2,4,10,10,10,10,10,2}; - C program defines another array for CB: int buffer[5]={0,0,0,0,0}; - C program calls asm_streaming(x[i],3,reset,buffer)), where reset is 0 or 1 - The 4 arguments are passed into the assembly program through the first 4 general purpose registers: r0=x[i], r1=mean, r2=reset, r3=buffer Note: # by passing in the name of the buffer, we are passing in the address of the first element of the buffer, i.e. pointer to array # you can assume that the 4 function arguments have been magically loaded into the registers and ready for you to use immediately at the start of your Assembly program ******************** *WHAT WE NEED TO DO* ******************** - In our Assembly program, we need to maintain a circular buffer, which means that the incoming x value must be inserted into the buffer in a cyclical fashion: fill up the buffer from start till the end and once you reach the end, the next incoming x value will overwrite the first value, and so on... - In order to help us do this, we can define some variables: ~ N is the maximum number of elements in the buffer (5 in our case) ~ CBcnt is the number of elements in the buffer, in the range of 1 to N (referenced as n in the variance formula) ~ CBptr is the address of the last inserted element ~ CBind will hold the array index of the last inserted element, in the range of 0 to N-1 - We have to manage 4 cases: ~ Case 1: *reset* The incoming x value must be written to the start of CB and writing resumes from there. Contents of CB (if any) to be preserved. ~ Case 2: *CBcnt < N* The buffer is not full and we simply insert the incoming x value to the next empty array index, i.e. next word location ~ Case 3: *CBcnt == N && CBind == N-1* The buffer is full as the most recent x value was written at the last array location. The incoming x value would therefore have to overwrite the first array element. ~ Case 4: *CBcnt == N && CBind < N-1* The buffer is full but the most recent x value was not written to the last array location. We can therefore overwrite the next array index, i.e. next word location. - At every time step, the incoming x value is stored according to the above and the variance is calculated using all the array values (and thereafter returned to the C program via R0) *********** *IMPORTANT* *********** 1) "Callee functions must preserve R4-R11 and LR, because these registers are not allowed to be corrupted by the callee function." Taken from: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.100748_0606_00_en/lmi1470147220260.html 2) All conditional instructions must be inside an IT block (Branch is the only exception to this rule) 3) An IT block can have up to a maximum of 4 conditional instructions; to run more than 4 conditional instructions, split them into 2 consecutive IT blocks *************** *CODE SNIPPETS* *************** 1) Defining a static variable (local commons): .lcomm CBcnt 4 //Allocate 4 bytes (1 word) in static RAM and use CBcnt to refer to this address ******* 0x100 * * ******* CBcnt: 0x104 * 3 * ******* 0x108 * * ******* 2) Difference between loading an address vs loading a value stored at an address @ METHOD A) LDR R4, =CBcnt //Load R4 with the memory address referred to by CBcnt, i.e. R4 = 0x104 LDR R5, [R4] //Load R5 with the value stored at the memory location given by R4, i.e. R5 = 3 @ METHOD B) LDR R5, CBcnt //Load R5 with the value stored at the location referred to by CBcnt, i.e. R5 = 3 SO WHAT METHOD TO USE? First of all, recognise that the effect of running the 2 instructions in A is equivalent to running that single instruction in B. Use method B if you only want to read the value from memory and don't intend to change it later. Use method A if you know that you'll be changing the value in memory later. In order to change the value in memory, you obviously need to know the address of that location, so that you can store (STR) from a register into the memory location. Note that unlike LDR, you cannot use a label in a STR instruction. The required memory address must be in a register. 3) Incrementing CBcnt by 1 LDR R4, =CBcnt // R4 = 0x104 LDR R5, [R4] // R5 = value at 0x104 = 3 ADD R5, #1 // (R5 + 1) -> R5 STR R5, [R4] // value at 0x104 = R5 = 4 4) Conditional branch CMP R1, R2 // Compare R1 and R2 BEQ loop // Branch to the section of code labelled as loop if R1==R2 ****** *TIPS* ****** - Don't overwrite R3!!! Because it will always point to the start of the buffer, it is very useful to use it in conjunction with an offset (or a separate index variable)