Sale!

ECSE 324 Lab 1-3 Solutions

Original price was: $90.00.Current price is: $75.00.

Download Details:

  • Name: ECSE-324-Lab1-3-Solutions-u05ugp.zip
  • Type: zip
  • Size: 1.26 MB

Category:

Description

Rate this product

ECSE 324 Lab 1: Basic Assembly Language Programming Solved

Part 1: Calling all functions!
In this part, your task is to implement a number of different relatively straightforward functions.
Assume that main in the C code below begins at _start in your assembly program. All programs
should terminate with an infinite loop, as in exercise (5) of Lab 0.
Subroutine calling convention
It is important to carefully respect subroutine calling conventions in order to prevent call stack
corruption. The convention which we will use for calling a subroutine in ARM assembly is as
follows.
The caller (the code that is calling a function) must:
● Move arguments into R0 through R3. (If more than four arguments are required, the
caller should PUSH the arguments onto the stack.)
● Call the subroutine func using BL func.
The callee (the code in the function that is called) must:
● Move the return value into R0.
● Ensure that the state of the processor is restored to what it was before the subroutine
call by POPing arguments off of the stack.
● Use BX LR to return to the calling code.
Note that the state of the processor can be saved and restored by pushing R4 through LR onto
the stack at the beginning of the subroutine and popping R4 through LR off the stack at the end
of the subroutine.
For an example of how to perform a function call in assembly consider the implementation of
vector dot product. Note that this code is an expansion of the example presented in 2-isa.pdf.
.global _start // define entry point
// initialize memory
n: .word 6 // the length of our vectors
vecA: .word 5,3,-6,19,8,12 // initialization for vector A
vecB: .word 2,14,-3,2,-5,36 // initialization for vector B
vecC: .word 19,-1,-37,-26,35,4 // initialization for vector C
result: .space 8 // uninitialized space for the results

_start: // execution begins here!
LDR A1, =vecA // put the address of A in A1
LDR A2, =vecB // put the address of B in A2
LDR A3, n // put n in A3
BL dotp // call dotp function
LDR V1, =result // put the address of result in V1
STR A1, [V1] // put the answer (0x1f4, #500) in result
LDR A1, =vecA // put the address of A in A1
LDR A2, =vecC // put the address of C in A2
LDR A3, n // put n in A3
BL dotp // call dotp function
STR A1, [V1, #4] // put the answer (0x94, #148) in result+4
stop:
B stop
// calculate the dot product of two vectors
// pre– A1: address of vector a
// A2: address of vector b
// A3: length of vectors
// post- A1: result
dotp:
PUSH {V1-V3} // push any Vn that we use
MOV V1, #0 // V1 will accumulate the product
dotpLoop:
LDR V2, [A1], #4 // get vectorA[i] and post-increment
LDR V3, [A2], #4 // get vectorB[i] and post-increment
MLA V1, V2, V3, V1 // V1 += V2*V3
SUBS A3, A3, #1 // i– and set condition flags
BGT dotpLoop
MOV A1, V1 // put our result in A1 to return it
POP {V1-V3} // pop any Vn that we pushed
BX LR // return
Questions? There’s a channel for that in Teams.

Exponential function
You’ll start with a function that calculates xn using a loop as follows in the C code below. Note
that you need not explicitly implement a function called main.
int exp(int x, int n) {
int result = 1;
for (int i=0; i<n; i++)
result = result * x;
return result;
} // exp
int main(int argc, char* argv[]) {
int a, b;
a = exp(2, 10); // 1024
b = exp(-5, 5); // -3125
} // main
Note! Your implementation of exp must match the function prototype above: use R0 to pass x;
use R1 to pass n. Return the result in R0.
Factorial function
Now you will implement a function that calculates n! using recursion. A recursive function is a
function that uses the stack to call itself until a base case is reached. The C below describes a
recursive function that calculates the factorial function for a given positive integer n:
int fact(int n) {
if (n < 2)
// base case
return 1;
else
return n * fact(n−1);
}
int main(int argc, char* argv[]) {
int a, b;
a = fact(5); // 120

b = fact(10); // 3628800
} // main
For example, fact(4) is computed as follows:
5 * fact(4) = 5 * 4 * fact(3) = 5 * 4 * 3 * fact(2) = 5 * 4 * 3 * 2 * fact(1) = 5 * 4 * 3 * 2 * 1 = 120.
Note! Your implementation of fact must match the function prototype above: use R0 to pass n.
Return the result in R0.
Exponentiation by Squaring
Now that we have seen how recursion works, we can implement exponentiation by squaring, a
more efficient technique for calculating xn
.
int exp(int x, int n) {
int result = 1;
// base cases
if (n == 0)
return 1;
if (n == 1)
return x;
// check if n is even or odd
if (n & 1)
// n is odd
return x * exp(x * x, n >> 1);
else
// n is even
return exp(x * x, n >> 1);
} // exp
int main(int argc, char *argv[]) {
int a, b;
a = exp(2, 10); // 1024
b = exp(-5, 5); // -3125
} // main

Note! Your implementation of exp must match the function prototype above: use R0 to pass x;
use R1 to pass n. Return the result in R0.
Exercises
1. Write an assembly program (part1-exp.s) that computes xn for any integer x and any
positive integer n. exp must be implemented as a function that is called by your program
more than once.
2. Write an assembly program (part1-fact.s) that uses recursive function calls to
calculate n!. fact must be implemented as a function that is called by your program more
than once.
3. Write an assembly program (part1-expbysq.s) that uses recursive function calls to
compute xn for any integer x and any positive integer n using exponentiation by squaring.
exp must be implemented as a function that is called by your program more than once.
Part 2: Sort this out quickly
In this part, your task is to implement the quicksort algorithm. Quicksort is a recursive algorithm;
you will additionally implement a helper function that swaps elements in an array.
/* swap two elements in the array */
void swap(int *array, int a, int b) {
int temp;
temp = array[a];
array[a] = array[b];
array[b] = temp;
} // swap
/* main quicksort algorithm */
void quicksort(int *array, int start, int end) {
int i, j, pivot, temp;
if (start < end) {
pivot = start;
i = start;
j = end;
while (i < j) {
// move i right until we find a number greater than the pivot
while (array[i] <= array[pivot] && i < end)
i++;

// move j left until we find a number smaller than the pivot
while (array[j] > array[pivot])
j–;
// swap the elements at these positions
// unless they are already relatively sorted
if (i < j)
swap(array, i, j);
} // while
// swap pivot and element j
swap(array, pivot, j);
// recurse on the subarrays before and after element j
quicksort(array, start, j-1);
quicksort(array, j+1, end);
} // if
} // quicksort
/* program initializes an array and calls quicksort */
int main(int argc, char* argv[]) {
// initialization
int length = 10;
int numbers[10] = {68, -22, -31, 75, -10, -61, 39, 92, 94, -55};
int *ptr = &numbers[0];
quicksort(ptr, 0, length-1);
// output: numbers = {-61, -55, -31, -22, -10, 39, 68, 75, 92, 94}
} // main
Note! Your implementation of quicksort must match the function prototype above: use R0 to
pass a pointer to an array; use R1 to pass the lowest index to be sorted; use R2 to pass the
highest index to be sorted.
Exercise
1. Write an assembly program (part2.s) that implements the quicksort algorithm to sort
the given array in ascending order. quicksort must be implemented as a function that is
called by your program.

Deliverables
Your demo is limited to 5 minutes. It is useful to highlight that your software computes correct
partial and final answers; draw our attention to the registers and memory contents at appropriate
points to demonstrate that your software operates as expected.
Your demo will be graded by assessing, for each algorithm, the correctness of the observed
behavior, and the correctness of your description of that behavior.
In your report, for each algorithm, describe:
● Your approach (e.g., how you used subroutines, including passing arguments and
returning results, how you used the stack, etc)
● Challenges you faced, if any, and your solutions
● Shortcomings, possible improvements, etc
Your report is limited to four pages, total (no smaller than 10 pt font, no narrower than 1”
margins, no cover page). It will be graded by assessing, for each algorithm, your report’s clarity,
organization, and technical content.
Grading
Your demo and report are equally weighted. The breakdown for the demo and report are as
follows:
Demo
● 10% Part 1A: Exponential function
● 20% Part 1B: Factorial function
● 30% Part 1C: Exponentiation by squares
● 40% Part 2: Quicksort
Each section will be graded for (a) clarity, (b) technical content, and (c) correct execution:
● 1pt clarity: the demo is clear and easy to follow
● 1pt technical content: correct terms are used to describe your software
● 3pt correctness: given an input, the correct output is clearly demonstrated
Report
● 10% Part 1A: Exponential function
● 20% Part 1B: Factorial function
● 30% Part 1C: Exponentiation by squares
● 40% Part 2: Quicksort

Each section will be graded for: (a) clarity, (b) organization, and (c) technical content:
● 1pt clarity: grammar, syntax, word choice
● 1pt organization: clear narrative flow from problem description, approach, testing,
challenges, etc.
● 3pt technical content: appropriate use of terms, description of proposed
approach, description of testing and results, etc.
Submission
Please submit, on MyCourses, your source code, and report, in a single .zip archive, using the
following file name conventions:
● Code: part1-exp.s, part1-fact.s, part1-expbysq.s, part2.s
● Report: StudentID_FullName_Lab1_report.pdf

 ECSE 324: Computer Organization Lab 2: I/O Solved

Part 1: Basic I/O
For this part, it is necessary to refer to sections 2.9.1 – 2.9.4 (pp. 7 – 9) and 3.4.1 (p. 14)
in the DE1-SoC Computer Manual.
Brief overview
The hardware setup of the DE1-SoC’s I/O components is fairly straightforward. The
system has designated addresses in memory that are connected to hardware circuits on
the FPGA through parallel ports, and these hardware circuits, in turn, interface with the
physical I/O components. In most cases, the FPGA hardware simply maps the I/O
terminals to the memory address designated to it. There are several parallel ports
implemented in the FPGA that support input, output, and bidirectional transfers of data
between the ARM A9 processor and I/O peripherals. For instance, the state of the slider
switches is available to the FPGA on a bus of 10 wires which carry either a logical ’0’ or
’1’. The state of the slider switches is then stored in the memory address reserved for
the slider switches (0xFF200040 in this case).
It is useful to have slightly more sophisticated FPGA hardware in some cases. For
instance, in the case of the push-buttons, in addition to knowing the state of the button,
it is also helpful to know whether a falling edge is detected, signaling a keypress. This
can be achieved with a simple edge detection circuit in the FPGA.
Getting Started: Drivers for slider switches and LEDs
Access to the memory addresses designated for I/O interfaces is best facilitated by
what are called device drivers. Drivers are subroutines (drivers) that ease the process of
reading from and writing to I/O interface addresses, thereby manipulating the state of
and data associated with a given peripheral. When writing drivers, it is critical that you
follow the subroutine calling conventions presented in this course.
1- Slider Switches: Create a new subroutine labeled read_slider_switches_ASM, which
reads the value from the memory location designated for the slider switches data
(SW_ADDR) and stores it R0, and then returns. Remember to use the subroutine
calling convention, and save processor state (by pushing and popping registers) if
needed! If there are fewer than four 32-bit arguments or return values, use registers,
rather than the stack, for communication between caller and callee.

2- LEDs: Create a new subroutine labeled write_LEDs_ASM. The write_LEDs_ASM
subroutine writes the value in R0 to the LEDs memory location (LED_ADDR), and then
branches to the address contained in the LR.
To help you get started, the code for the slider switches and LEDs drivers have been
provided below. Use them as templates for writing future drivers.
// Slider Switches Driver
// returns the state of slider switches in R0
// post- A1: slide switch state
.equ SW_ADDR, 0xFF200040
read_slider_switches_ASM:
LDR A2, =SW_ADDR // load the address of slider switch state
LDR A1, [A2] // read slider switch state
BX LR
// LEDs Driver
// writes the state of LEDs (On/Off state) in A1 to the LEDs’ memory
location
// pre– A1: data to write to LED state
.equ LED_ADDR, 0xFF200000
write_LEDs_ASM:
LDR A2, =LED_ADDR // load the address of the LEDs’ state
STR A1, [A2] // update LED state with the contents of A1
BX LR
To acquaint yourself with using memory-mapped I/O, write an application that makes
use of your read_slider_switches_ASM and the write_LEDs_ASM subroutines to turn
on/off the LEDs. Write an infinite loop that calls read_slider_switches_ASM and
write_LEDs_ASM in order. Compile and Run your project, and then change the state of
the switches in the online simulator to turn on/off the corresponding LEDs. Note that
both the Switches and the LEDs panels are located on the top corner of your screen.
The figure below demonstrates the result of activating slider switches 0, 4, 5 and 9.
3-10)
More Advanced Drivers: Drivers for HEX displays and push-buttons
Now that the basic structure of the drivers has been introduced, we can write more
advanced drivers i.e., HEX displays and push-buttons drivers.
1- HEX displays: There are six HEX displays (HEX0 to HEX5) on the DE1-SoC
Computer board. You are required to write three subroutines to implement the functions
listed below to control the HEX displays.
Note! The emulator will complain about word-aligned accesses if you use STRB to write
to some HEX displays. This does not indicate an error has occurred. You may turn off
Device-specific warnings in the settings, but be careful, as this may hide other warnings
worth observing. If you leave this Device-specific warnings enabled, simply continue the
execution of your code after the emulator-inserted breakpoint.
HEX_clear_ASM: The subroutine will turn off all the segments of the HEX displays
passed in the argument. It receives the HEX display indices through register A1 as an
argument.

HEX_flood_ASM: The subroutine will turn on all the segments of the HEX displays
passed in the argument. It receives the HEX display indices through register A1 as an
argument.
HEX_write_ASM: The subroutine receives HEX display indices and an integer value in
0-15 to display. These are passed in registers A1 and A2, respectively. Based on the
second argument (A2), the subroutine will display the corresponding hexadecimal digit
(0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F) on the display(s).
The subroutines should check the first argument to determine which displays,
HEX0-HEX5, have been selected, and write the value of the second argument to each.
A loop may be useful here! The HEX display indices can be encoded based on a
one-hot encoding scheme, e.g.,:
HEX0 = 0x00000001
HEX1 = 0x00000002
HEX2 = 0x00000004
HEX3 = 0x00000008
HEX4 = 0x00000010
HEX5 = 0x00000020
For example, you may pass 0x0000000C to the HEX_flood_ASM subroutine to turn on
all the segments of HEX2 and HEX3 displays:
mov R0, #0x0000000C
BL HEX_flood_ASM
2- Pushbuttons: There are four pushbuttons (PB0 to PB3) on the DE1-SoC Computer
board. You are required to write seven subroutines to implement the functions listed
below to control the pushbuttons:
read_PB_data_ASM: The subroutine returns the indices of the pressed pushbuttons
(the keys from the pushbuttons Data register). The indices are encoded based on a
one-hot encoding scheme: -10)

PB0 = 0x00000001
PB1 = 0x00000002
PB2 = 0x00000004
PB3 = 0x00000008
PB_data_is_pressed_ASM: The subroutine receives a pushbutton index as an
argument. Then, it returns 0x00000001 if the corresponding pushbutton is pressed.
read_PB_edgecp_ASM: The subroutine returns the indices of the pushbuttons that
have been pressed and then released (the edge bits from the pushbuttons’ Edgecapture
register).
PB_edgecp_is_pressed_ASM: The subroutine receives a pushbutton index as an
argument. Then, it returns 0x00000001 if the corresponding pushbutton has been
pressed and released.
PB_clear_edgecp_ASM: The subroutine clears the pushbutton Edgecapture register.
You can read the edgecapture register and write what you just read back to the
edgecapture register to clear it.
enable_PB_INT_ASM: The subroutine receives pushbutton indices as an argument.
Then, it enables the interrupt function for the corresponding pushbuttons by setting the
interrupt mask bits to ‘1’.
disable_PB_INT_ASM: The subroutine receives pushbutton indices as an argument.
Then, it disables the interrupt function for the corresponding pushbuttons by setting the
interrupt mask bits to ‘0’.
Part 1 Deliverable: Write an application that uses the appropriate drivers created so far
to perform the following functions.
● As before, the state of the slider switches will be mapped directly to the LEDs.
● Additionally, the state of the last four slider switches SW3-SW0 (SW3
corresponds to the most significant bit) will be used to set the value of a number
from 0-15. This number will be displayed on a HEX display when the
corresponding pushbutton is pressed. For example, pressing PB0 will result in
the number being displayed on HEX0. When the pushbutton is released, the
value displayed on the HEX display should remain unchanged, even if you
change the state of the slider switches. Since there are no pushbuttons to
correspond to HEX4 and HEX5, you must turn on all the segments of the HEX4
and HEX5 displays.
● Finally, asserting slider switch SW9 should clear all the HEX displays. Figure
below demonstrates the result of activating slider switches 0 and 3 and pressing
pushbutton 0 (PB0). Remember, you have to release the pushbuttons to see the
results as the Edgecapture register is updated once the pushbuttons are
released (unchecked).
)
Part 2: Timers
For this part, it is necessary to refer to sections 2.4.1 (p. 3) and 3.1 (p. 14) in the
DE1-SoC Computer Manual.
Brief overview
Timers are simply hardware counters that are used to measure time and/or synchronize
events. They run on a known clock frequency that is programmable in some cases.
Timers are usually (but not always) down counters, and by programming the start value,
the time-out event (when the counter reaches zero) occurs at fixed time intervals.
ARM A9 Private Timer drivers
There is one ARM A9 private timer available on the DE1-SoC board. The timer uses a
clock frequency of 200 MHz. You need to configure the timer before using it. To
configure the timer, you need to pass three arguments to the “configuration subroutine”.
The arguments are:
1- Load value: The ARM A9 private timer is a down counter and requires an initial count
value. Use A1 to pass this argument.
2- Configuration bits: Use A2 to pass this argument. Read sections 2.4.1 (p. 3) and 3.1
(p. 14) in the De1-SoC Computer Manual carefully to learn how to handle the
configuration bits. The configuration bits are stored in the Control register of the timer.
You are required to write three subroutines to implement the functions listed below to
control the timers:
ARM_TIM_config_ASM: The subroutine is used to configure the timer. Use the
arguments discussed above to configure the timer.
ARM_TIM_read_INT_ASM: The subroutine returns the “F” value (0x00000000 or
0x00000001) from the ARM A9 private timer Interrupt status register.
ARM_TIM_clear_INT_ASM: The subroutine clears the “F” value in the ARM A9 private
timer Interrupt status register. The F bit can be cleared to 0 by writing a 0x00000001
into the Interrupt status register.
To test the functionality of your subroutines, write assembly that uses the ARM A9
private timer. Use the timer to count from 0 to 15 and show the count value on the HEX
display (HEX0). You must increase the count value by 1 every time the “F” value is
asserted (“F” becomes ‘1’). The count value must be reset when it reaches 15 (1, 2, 3,
…, E, F, 0, 1, …). The counter should be able to count in increments of 1 second.
Remember, you must clear the timer interrupt status register each time the timer sets
the “F” bit in the interrupt status register to 1 by calling the ARM_TIM_clear_INT_ASM
subroutine.
Creating an application: Polling-based stopwatch
Part 2 Deliverable: Create a simple stopwatch using the ARM A9 private timer,
pushbuttons, and HEX displays. The stopwatch should be able to count in increments of
100 milliseconds. Use the ARM A9 private timer to count time. Display milliseconds on
HEX0, seconds on HEX2-1, minutes on HEX3-4, and hours on HEX5.
PB0, PB1, and PB2 will be used to start, stop and reset the stopwatch, respectively. Use
an endless loop to poll the pushbutton edgecapture register and the “F” bit from the
ARM A9 private timer interrupt status register.
Note! The emulator is not real-time capable (1 second of simulation of your stopwatch
may take much more than 1 second of wall-clock time). You will not be able to compare
the passage of wall-clock time with your stopwatch’s behavior to determine whether or
not it is functioning correctly.
)
Part 3: Interrupts
For this part, it is necessary to refer to section 3 (pp. 13-17) in the DE1-SoC Computer
Manual. Furthermore, detailed information about the interrupt drivers is provided in the
“Using the ARM Generic Interrupt Controller” document available here.
Brief Overview
Interrupts are hardware or software signals that are sent to the processor to indicate
that an event has occurred that needs immediate attention. When the processor
receives an interrupt, it pauses the code currently executing, handles the interrupt by
executing code defined in an Interrupt Service Routine (ISR), and then resumes normal
execution.
Apart from ensuring that high priority events are given immediate attention, interrupts
also help the processor to utilize resources more efficiently. Consider the polling
application from the previous section, where the processor periodically checked the
pushbuttons for a keypress event. Asynchronous events such as this, if assigned an
interrupt, can free the processor to do other work between events.
ARM Generic Interrupt Controller
The ARM generic interrupt controller (GIC) is a part of the ARM A9 MPCORE
processor. The GIC is connected to the IRQ interrupt signals of all I/O peripheral
devices that are capable of generating interrupts. Most of these devices are normally
external to the A9 MPCORE, and some are internal peripherals (such as timers). The
GIC included with the A9 MPCORE processor in the Altera Cyclone V SoC family
handles up to 255 interrupt sources. When a peripheral device sends its IRQ signal to
the GIC, then the GIC can forward a corresponding IRQ signal to one or both of the A9
cores. Software code that is running on the A9 core can then query the GIC to
determine which peripheral device caused the interrupt, and take appropriate action.
The ARM Cortex-A9 has several main modes of operation and the operating mode of
the processor is indicated in the current processor status register CPSR. In this Lab, we
only use IRQ mode. A Cortex-A9 processor enters IRQ mode in response to receiving
an IRQ signal from the GIC. Before such interrupts can be used, software code has to
perform a number of steps:

1. Ensure that IRQ interrupts are disabled in the A9 processor by setting the IRQ
disable bit in the CPSR to 1.
2. Configure the GIC. Interrupts for each I/O peripheral device that is connected to
the GIC are identified by a unique interrupt ID.
3. Configure each I/O peripheral device so that it can send IRQ interrupt requests to
the GIC.
4. Enable IRQ interrupts in the A9 processor, by setting the IRQ disable bit in the
CPSR to 0.
An example assembly program that demonstrates use of interrupts follows. The
program responds to interrupts from the pushbutton KEY port in the FPGA. The
interrupt service routine for the pushbutton KEYs indicates which KEY has been
pressed on the HEX0 display. You can use this code as a template when using
interrupts in the ARM Cortex-A9 processor.
First, you need to add the following lines at the beginning of your assembly code to
initialize the exception vector table. Within the table, one word is allocated to each of the
various exception types. This word contains branch instructions to the address of the
relevant exception handlers.
.section .vectors, “ax”
B _start // reset vector
B SERVICE_UND // undefined instruction vector
B SERVICE_SVC // software interrupt vector
B SERVICE_ABT_INST // aborted prefetch vector
B SERVICE_ABT_DATA // aborted data vector
.word 0 // unused vector
B SERVICE_IRQ // IRQ interrupt vector
B SERVICE_FIQ // FIQ interrupt vector
Then, add the following to configure the interrupt routine. Note that the processor’s
modes have their own stack pointers and link registers (see Figure 3 in “Using the ARM
Generic Interrupt Controller”). At a minimum, you must assign initial values to the stack
pointers of any execution modes that are used by your application. In our case, when an
interrupt occurs, the processor enters IRQ mode. Therefore, we must assign an initial
value to the IRQ mode stack pointer. Usually, interrupts are expected to be executed as
fast as possible. As a result, on-chip memories are used in IRQ mode.

The following code illustrates how to set the stack to the A9 on-chip memory in IRQ
mode:
.text
.global _start
_start:
/* Set up stack pointers for IRQ and SVC processor modes */
MOV R1, #0b11010010 // interrupts masked, MODE = IRQ
MSR CPSR_c, R1 // change to IRQ mode
LDR SP, =0xFFFFFFFF – 3 // set IRQ stack to A9 onchip memory
/* Change to SVC (supervisor) mode with interrupts disabled */
MOV R1, #0b11010011 // interrupts masked, MODE = SVC
MSR CPSR, R1 // change to supervisor mode
LDR SP, =0x3FFFFFFF – 3 // set SVC stack to top of DDR3 memory
BL CONFIG_GIC // configure the ARM GIC
// To DO: write to the pushbutton KEY interrupt mask register
// Or, you can call enable_PB_INT_ASM subroutine from previous
task
// to enable interrupt for ARM A9 private timer, use
ARM_TIM_config_ASM subroutine
LDR R0, =0xFF200050 // pushbutton KEY base address
MOV R1, #0xF // set interrupt mask bits
STR R1, [R0, #0x8] // interrupt mask register (base + 8)
// enable IRQ interrupts in the processor
MOV R0, #0b01010011 // IRQ unmasked, MODE = SVC
MSR CPSR_c, R0
IDLE:
B IDLE // This is where you write your objective task

Then, you need to define the exception service routines using the following:
/*— Undefined instructions ————————————–*/
SERVICE_UND:
B SERVICE_UND
/*— Software interrupts —————————————-*/
SERVICE_SVC:
B SERVICE_SVC
/*— Aborted data reads ——————————————*/
SERVICE_ABT_DATA:
B SERVICE_ABT_DATA
/*— Aborted instruction fetch ———————————–*/
SERVICE_ABT_INST:
B SERVICE_ABT_INST
/*— IRQ ———————————————————*/
SERVICE_IRQ:
PUSH {R0-R7, LR}
/* Read the ICCIAR from the CPU Interface */
LDR R4, =0xFFFEC100
LDR R5, [R4, #0x0C] // read from ICCIAR
/* To Do: Check which interrupt has occurred (check interrupt IDs)
Then call the corresponding ISR
If the ID is not recognized, branch to UNEXPECTED
See the assembly example provided in the De1-SoC Computer_Manual
on page 46 */
Pushbutton_check:
CMP R5, #73
UNEXPECTED:
BNE UNEXPECTED // if not recognized, stop here
BL KEY_ISR
EXIT_IRQ:
/* Write to the End of Interrupt Register (ICCEOIR) */
STR R5, [R4, #0x10] // write to ICCEOIR
POP {R0-R7, LR}
SUBS PC, LR, #4
/*— FIQ ———————————————————*/
SERVICE_FIQ:
B SERVICE_FIQ
0)
Then you are required to add the following to configure the Generic Interrupt Controller
(GIC):
CONFIG_GIC:
PUSH {LR}
/* To configure the FPGA KEYS interrupt (ID 73):
* 1. set the target to cpu0 in the ICDIPTRn register
* 2. enable the interrupt in the ICDISERn register */
/* CONFIG_INTERRUPT (int_ID (R0), CPU_target (R1)); */
/* To Do: you can configure different interrupts
by passing their IDs to R0 and repeating the next 3 lines */
MOV R0, #73 // KEY port (Interrupt ID = 73)
MOV R1, #1 // this field is a bit-mask; bit 0 targets
cpu0
BL CONFIG_INTERRUPT
/* configure the GIC CPU Interface */
LDR R0, =0xFFFEC100 // base address of CPU Interface
/* Set Interrupt Priority Mask Register (ICCPMR) */
LDR R1, =0xFFFF // enable interrupts of all priorities
levels
STR R1, [R0, #0x04]
/* Set the enable bit in the CPU Interface Control Register (ICCICR).
* This allows interrupts to be forwarded to the CPU(s) */
MOV R1, #1
STR R1, [R0]
/* Set the enable bit in the Distributor Control Register (ICDDCR).
* This enables forwarding of interrupts to the CPU Interface(s) */
LDR R0, =0xFFFED000
STR R1, [R0]
POP {PC}
/*
* Configure registers in the GIC for an individual Interrupt ID
* We configure only the Interrupt Set Enable Registers (ICDISERn) and
* Interrupt Processor Target Registers (ICDIPTRn). The default
(reset)
* values are used for other registers in the GIC
* Arguments: R0 = Interrupt ID, N
* R1 = CPU target
*/
CONFIG_INTERRUPT:
PUSH {R4-R5, LR}
/* Configure Interrupt Set-Enable Registers (ICDISERn).
* reg_offset = (integer_div(N / 32) * 4
* value = 1 << (N mod 32) */
LSR R4, R0, #3 // calculate reg_offset
BIC R4, R4, #3 // R4 = reg_offset
LDR R2, =0xFFFED100
ADD R4, R2, R4 // R4 = address of ICDISER
AND R2, R0, #0x1F // N mod 32
MOV R5, #1 // enable
LSL R2, R5, R2 // R2 = value
/* Using the register address in R4 and the value in R2 set the
* correct bit in the GIC register */
LDR R3, [R4] // read current register value
ORR R3, R3, R2 // set the enable bit
STR R3, [R4] // store the new register value
/* Configure Interrupt Processor Targets Register (ICDIPTRn)
* reg_offset = integer_div(N / 4) * 4
* index = N mod 4 */
BIC R4, R0, #3 // R4 = reg_offset
LDR R2, =0xFFFED800
ADD R4, R2, R4 // R4 = word address of ICDIPTR
AND R2, R0, #0x3 // N mod 4
ADD R4, R2, R4 // R4 = byte address in ICDIPTR
/* Using register address in R4 and the value in R2 write to
* (only) the appropriate byte */
STRB R1, [R4]
POP {R4-R5, PC}
2
Rev 1.2 (2022-03-10)
Then use the pushbutton Interrupt Service Routine (ISR) given below. This routine
checks which KEY has been pressed and writes corresponding index to the HEX0
display:
KEY_ISR:
LDR R0, =0xFF200050 // base address of pushbutton KEY port
LDR R1, [R0, #0xC] // read edge capture register
MOV R2, #0xF
STR R2, [R0, #0xC] // clear the interrupt
LDR R0, =0xFF200020 // based address of HEX display
CHECK_KEY0:
MOV R3, #0x1
ANDS R3, R3, R1 // check for KEY0
BEQ CHECK_KEY1
MOV R2, #0b00111111
STR R2, [R0] // display “0”
B END_KEY_ISR
CHECK_KEY1:
MOV R3, #0x2
ANDS R3, R3, R1 // check for KEY1
BEQ CHECK_KEY2
MOV R2, #0b00000110
STR R2, [R0] // display “1”
B END_KEY_ISR
CHECK_KEY2:
MOV R3, #0x4
ANDS R3, R3, R1 // check for KEY2
BEQ IS_KEY3
MOV R2, #0b01011011
STR R2, [R0] // display “2”
B END_KEY_ISR
IS_KEY3:
MOV R2, #0b01001111
STR R2, [R0] // display “3”
END_KEY_ISR:
BX LR

Interrupt-based Stopwatch
Before attempting this section, get familiarized with the relevant documentation sections
provided in the introduction.
Part 3 Deliverable: Modify the stopwatch application from the previous section to use
interrupts. In particular, enable interrupts for the ARM A9 private timer (ID: 29) used to
count time for the stopwatch. Also enable interrupts for the pushbuttons (ID: 73), and
determine which key was pressed when a pushbutton interrupt is received.
In summary, you need to modify some parts of the given template to perform this task:
● _start: activate the interrupts for pushbuttons and the ARM A9 private timer
by calling the subroutines you wrote in the previous tasks. (Call the
enable_PB_INT_ASM and ARM_TIM_config_ASM subroutines.)
● IDLE: You will implement the stopwatch’s operation here.
● SERVICE_IRQ: Modify this part so that the IRQ handler checks both the ARM
A9 private timer and pushbutton interrupts and calls the corresponding
interrupt service routine (ISR). Hint: The given template only checks the
pushbutton interrupt and calls its ISR (KEY_ISR). Use labels KEY_ISR and
ARM_TIM_ISR for pushbuttons and ARM A9 private timer interrupt service
routines, respectively.
● CONFIG_GIC: The given CONFIG_GIC subroutine only configures the
pushbutton interrupts. You must modify this subroutine to configure the ARM
A9 private timer and pushbutton interrupts by passing the required interrupt
IDs.
● KEY_ISR: The given pushbutton interrupt service routine (KEY_ISR)
performs unnecessary functions that are not required for this task. You must
modify this part to only perform the following functions: 1- write the content of
the pushbutton edgecapture register to the PB_int_flag memory and 2- clear
the interrupts. In your main code (see IDLE), you may read the PB_int_flag
memory to determine which pushbutton was pressed. Place the following
code at the top of your program to designate the memory location:
PB_int_flag:
.word 0x0
)
● ARM_TIM_ISR: You must write this subroutine from the scratch and add it to
your code. The subroutine writes the value ‘1’ into the tim_int_flag memory
when an interrupt is received. Then it clears the interrupt. In your main code
(see IDLE), you may read the tim_int_flag memory to determine whether the
timer interrupt has occurred. Use the following code to designate the memory
location:
tim_int_flag:
.word 0x0
Make sure you have read and understood the user manual before attempting this task.
For instance, you may need to refer to the user manual to understand how to clear the
interrupts for different interfaces (i.e., ARM A9 private timer and pushbuttons)
0)
Deliverables
Your demo is limited to 5 minutes. It is useful to highlight that your software computes correct
partial and final answers; draw our attention to the registers and memory contents at appropriate
points to demonstrate that your software operates as expected.
Your demo will be graded by assessing, for each algorithm, the correctness of the observed
behavior, and the correctness of your description of that behavior.
In your report, for each software deliverable, describe:
● Your approach (e.g., how you used subroutines, the stack, etc)
● Challenges you faced, if any, and your solutions
● Shortcomings, possible improvements, etc
Your report is limited to four pages, total (no smaller than 10 pt font, no narrower than 1”
margins). It will be graded by assessing, for each software deliverable, your report’s clarity,
organization, and technical content.
Grading
Your demo and report are equally weighted. The breakdown for the demo and report are as
follows:
Demo
● 20% Part 1: I/O routine library and test code
● 40% Part 2: Polling-based stopwatch
● 40% Part 3: Interrupt-based stopwatch
Each section will be graded for (a) clarity, (b) technical content, and (c) correct execution:
● 1pt clarity: the demo is clear and easy to follow
● 1pt technical content: correct terms are used to describe your software
● 3pt correctness: given an input, the correct output is clearly demonstrated
Report
● 20% Part 1: I/O routine library and test code
● 40% Part 2: Polling-based stopwatch
● 40% Part 3: Interrupt-based stopwatch
Each section will be graded for: (a) clarity, (b) organization, and (c) technical content:
● 1pt clarity: grammar, syntax, word choice
● 1pt organization: clear narrative flow from problem description, approach, testing,
challenges, etc.
● 3pt technical content: appropriate use of terms, description of proposed
approach, description of testing and results, etc.
Submission
Please submit, on MyCourses, your source code, and report in a single .zip archive, using the
following file name conventions:
● Code: part1.s, part2.s, part3.s
● Report: StudentID_FullName_Lab1_report.pdf

ECSE 324 Lab 3: Keyboard and VGA Solved

In this lab we will use the high level I/O capabilities of the DE1-SoC simulator to
1. display pixels and characters using the VGA controller, and
2. accept keyboard input via the PS/2 port.
For each of these topics, we will create a driver. We will test the drivers both individually and in
tandem by means of test applications.
Part 1: Drawing things with VGA
The DE1-SoC computer has a built-in VGA controller that can render pixels, characters or a
combination of both. The authoritative resource on these matters is Sections 4.2.1 and 4.2.4 of
the DE1-SoC Computer Manual. This section of the lab provides a quick overview that should
suffice for the purpose of completing this lab.
To render pixels, the VGA controller continuously reads the pixel buffer, a region in memory
starting at 0xc8000000 that contains the color value of every pixel on the screen. Colors are
encoded as 16-bit integers that reserve 5 bits for the red channel, 6 bits for the green channel
and 5 bits for the blue channel. That is, every 16-bit color is encoded like so:
15 … 11 10 … 5 4 … 0
Red Green Blue
The pixel buffer is 320 pixels wide and 240 pixels high. Individual pixel colors can be accessed
at 0xc8000000 | (y << 10) | (x << 1), where x and y are valid x and y coordinates.
As previously hinted, we can also render characters. To do so, we will use the character buffer,
which is analogous to the pixel buffer, but for characters. The device’s VGA controller
continuously reads the character buffer and renders its contents as characters in a built-in font.
The character buffer itself is a buffer of byte-sized ASCII characters at 0xc9000000. The buffer
has a width of 80 characters and a height of 60 characters. An individual character can be
accessed at 0xc9000000 | (y << 7) | x.

Task: Create a VGA driver
To provide a slightly higher-level layer over the primitive functionality offered by the pixel and
character buffers, we will create a driver. That is, a set of functions that can be used to control
the screen.
To help get you started, we created an application that uses such functions to draw a testing
screen. Your job is to create a set of driver functions to support the application. Download vga.s
and augment it with the following four functions:
● VGA_draw_point_ASM draws a point on the screen with the color as indicated in the
third argument, by accessing only the pixel buffer memory. Hint: This subroutine should
only access the pixel buffer memory.
● VGA_clear_pixelbuff_ASM clears (sets to 0) all the valid memory locations in the
pixel buffer. It takes no arguments and returns nothing. Hint: You can implement this
function by calling VGA_draw_point_ASM with a color value of zero for every valid
location on the screen.
● VGA_write_char_ASM writes the ASCII code passed in the third argument (r2) to the
screen at the (x, y) coordinates given in the first two arguments (r0 and r1). Essentially,
the subroutine will store the value of the third argument at the address calculated with
the first two arguments. The subroutine should check that the coordinates supplied are
valid, i.e., x in [0, 79] and y in [0, 59]. Hint: This subroutine should only access the
character buffer memory.
● VGA_clear_charbuff_ASM clears (sets to 0) all the valid memory locations in the
character buffer. It takes no arguments and returns nothing. Hint: You can implement this
function by calling VGA_write_char_ASM with a character value of zero for every valid
location on the screen.
Their C prototypes are as follows:
void VGA_draw_point_ASM(int x, int y, short c);
void VGA_clear_pixelbuff_ASM();
void VGA_write_char_ASM(int x, int y, char c);
void VGA_clear_charbuff_ASM();
Notes:
● Use suffixes B and H with the assembly memory access instructions in order to
read/modify bytes/half-words in memory.

● You must follow the conventions taught in class. If you do not, then the testing code in
the next section will be unlikely to work.
Testing the VGA driver
To test your VGA driver, run your finished assembly file. You can inspect the VGA output visually
using the VGA pixel buffer tab under the Devices panel of the simulator.
If you implemented your driver correctly, compiling and running the program will draw the
following image.

Part 2. Reading keyboard input
For the purpose of this lab, here’s a high level description of the PS/2 keyboard protocol. For a
more comprehensive resource, see Section 4.5 (pp. 45-46) of the DE1-SoC Computer Manual.
The PS/2 bus provides data about keystroke events by sending hexadecimal numbers called
scan codes, which for this lab will vary from 1-3 bytes in length. When a key on the PS/2
keyboard is pressed, a unique scan code called the make code is sent, and when the key is
released, another scan code called the break code is sent. The scan code set used in this lab is
summarized by the table below. (Originally taken from Baruch Zoltan Francisc’s page on PS/2
scan codes.)
KEY MAKE BREAK KEY MAKE BREAK KEY MAKE BREAK
A 1C F0,1C 9 46 F0,46 [ 54 FO,54
B 32 F0,32 \` 0E F0,0E INSERT E0,70 E0,F0,7
0
C 21 F0,21 – 4E F0,4E HOME E0,6C E0,F0,6
C
D 23 F0,23 = 55 FO,55 PG UP E0,7D E0,F0,7
D
E 24 F0,24 \ 5D F0,5D DELETE E0,71 E0,F0,7
1
F 2B F0,2B BKSP 66 F0,66 END E0,69 E0,F0,6
9
G 34 F0,34 SPACE 29 F0,29 PG DN E0,7A E0,F0,7
A
H 33 F0,33 TAB 0D F0,0D U
ARROW E0,75 E0,F0,7
5
I 43 F0,43 CAPS 58 F0,58 L
ARROW E0,6B E0,F0,6
B
J 3B F0,3B L SHFT 12 FO,12 D
ARROW E0,72 E0,F0,7
2
K 42 F0,42 L CTRL 14 FO,14 R
ARROW E0,74 E0,F0,7
4
L 4B F0,4B L GUI E0,1F E0,F0,1
F NUM 77 F0,77
M 3A F0,3A L ALT 11 F0,11 KP / E0,4A E0,F0,4
A
N 31 F0,31 R SHFT 59 F0,59 KP * 7C F0,7C

O 44 F0,44 R CTRL E0,14 E0,F0,1
4 KP – 7B F0,7B
P 4D F0,4D R GUI E0,27 E0,F0,2
7 KP + 79 F0,79
Q 15 F0,15 R ALT E0,11 E0,F0,1
1 KP EN E0,5A E0,F0,5
A
R 2D F0,2D APPS E0,2F E0,F0,2
F KP . 71 F0,71
S 1B F0,1B ENTER 5A F0,5A KP 0 70 F0,70
T 2C F0,2C ESC 76 F0,76 KP 1 69 F0,69
U 3C F0,3C F1 05 F0,05 KP 2 72 F0,72
V 2A F0,2A F2 06 F0,06 KP 3 7A F0,7A
W 1D F0,1D F3 04 F0,04 KP 4 6B F0,6B
X 22 F0,22 F4 0C F0,0C KP 5 73 F0,73
Y 35 F0,35 F5 03 F0,03 KP 6 74 F0,74
Z 1A F0,1A F6 0B F0,0B KP 7 6C F0,6C
0 45 F0,45 F7 83 F0,83 KP 8 75 F0,75
1 16 F0,16 F8 0A F0,0A KP 9 7D F0,7D
2 1E F0,1E F9 01 F0,01 ] 5B F0,5B
3 26 F0,26 F10 09 F0,09 ; 4C F0,4C
4 25 F0,25 F11 78 F0,78 ‘ 52 F0,52
5 2E F0,2E F12 07 F0,07 , 41 F0,41
6 36 F0,36 PRNT
SCRN
E0,12,
E0,7C
E0,F0,
7C,E0,
F0,12
. 49 F0,49
7 3D F0,3D SCROL
L 7E F0,7E / 4A F0,4A
8 3E F0,3E PAUSE
E1,14,7
7,
E1,F0,1
4,
F0,77
Two other parameters involved are the typematic delay and the typematic rate. When a key is
pressed, the corresponding make code is sent, and if the key is held down, the same make code
is repeatedly sent at a constant rate after an initial delay. The initial delay ensures that briefly
pressing a key will not register as more than one keystroke. The make code will stop being sent
only if the key is released or another key is pressed. The initial delay between the first and
second make code is called the typematic delay, and the rate at which the make code is sent
after this is called the typematic rate. The typematic delay can range from 0.25 seconds to 1.00
second and the typematic rate can range from 2.0 cps (characters per second) to 30.0 cps, with
default values of 500 ms and 10.9 cps respectively.
Task: Create a PS/2 driver
The DE1-SoC receives keyboard input from a memory-mapped PS/2 data register at address
0xff200100. Said register has an RVALID bit that states whether or not the current contents of
the register represent a new value from the keyboard. The RVALID bit can be accessed by
shifting the data register 15 bits to the right and extracting the lowest bit, i.e.,
RVALID = ((*(volatile int *)0xff200100) >> 15) & 0x1. When RVALID is true,
the low eight bits of the PS/2 data register correspond to a byte of keyboard data.
The hardware knows when you read a value from the memory-mapped PS/2 data register and
will automatically present the next code when you read the data register again.
For more details, see Section 4.5 (pp. 45-46) of the DE1-SoC Computer Manual.
Download ps2.s. This assembly file implements a program that reads keystrokes from the
keyboard and writes the PS/2 codes to the VGA screen using the character buffer. Copy your
VGA driver into ps2.s. Then implement a function that adheres to the following specifications:
● Name: read_PS2_data_ASM
● Input argument (r0): A memory address in which the data that is read from the PS/2
keyboard will be stored (pointer argument).
● Output argument (r0): Integer that denotes whether the data read is valid or not.
● Description: The subroutine will check the RVALID bit in the PS/2 Data register. If it is
valid, then the data from the same register should be stored at the address in the pointer
argument, and the subroutine should return 1 to denote valid data. If the RVALID bit is
not set, then the subroutine should simply return 0.
read_PS2_data_ASM’s C declaration is as follows:
int read_PS2_data_ASM(char *data);
Testing the PS/2 driver
To verify that the PS/2 driver is working correctly, you can type into the simulator’s PS/2
keyboard device and verify that the bytes showing up on the screen correspond to the codes
you might expect from the table in this section’s introduction.
If you implemented your PS/2 and VGA drivers correctly, then the program will print make and
break codes whenever you type in the simulator’s keyboard input device. Make sure to use the
keyboard device that says ff200100.
Note: If you did not manage to implement a working VGA driver, then you can still get credit for
the PS/2 driver by replacing write_byte with the implementation below. It will write PS/2
codes to memory address 0xfff0. Delete all calls to VGA driver functions and delete the
write_hex_digit function to ensure that your code still compiles.
write_byte:
push {r3, r4, lr}
ldr r4, =0xfff0
and r3, r3, #0xff
str r3, [r4]
pop {r3, r4, pc}
03-24)
Part 3. Putting everything together: Vexillology
We will now create an application that paints a gallery of flags. The user can use keyboard keys
to navigate through flags. Pressing the D key will prompt the application to show the next flag.
Similarly, pressing the A key will prompt the application to show the previous flag. Pressing A
when at the first flag or D when at the last flag cycles to the last or first flag, respectively.
Download flags.s. This file implements an input loop that reads keystrokes from the keyboard,
tests if they are A or D key presses, and cycles flags accordingly. It also implements a function
that draws the flag of Texas.
Your task is twofold:
1. Include your VGA and PS/2 driver functions.
2. Implement flag painting logic for two other flags by rewriting draw_real_life_flag
and draw_imaginary_flag . One flag must be a real-life flag and another flag can be
a flag you designed yourself.
Hint: The starter code includes the draw_rectangle and draw_star functions. You can use
these functions to draw any flag that consists of rectangles and stars. This encompasses many
flags, from the flags of the US to France and even China.
draw_rectangle draws a rectangle. It takes five arguments. The first four arguments are
stored in registers r0 through r3. The fifth argument is stored on the stack at address [sp].
draw_rectangle’s signature is:
/**
* Draws a rectangle.
* @param x The x coordinate of the top left corner of the rectangle.
* @param y The y coordinate of the top left corner of the rectangle.
* @param width The width of the rectangle.
* @param height The height of the rectangle.
* @param c The color with which to fill the rectangle.
*/
void draw_rectangle(int x, int y, int width, int height, int c);
draw_star draws a star. It takes four arguments, stored in registers r0 through r3. Its signature is:
/**
* Draws a star.
* @param x_center The x coordinate at which the star is centered.
* @param y_center The y coordinate at which the star is centered.
* @param radius The star’s radius.
* @param c The star’s color.
*/
void draw_star(int x_center, int y_center, int radius, int c);
Hint: The flag of Texas as drawn by the starter code should look like the image below. If it
doesn’t, then your VGA driver might be faulty.

Deliverables
Your demo is limited to 5 minutes. It is useful to highlight that your software computes correct
partial and final answers; draw our attention to the registers and memory contents at appropriate
points to demonstrate that your software operates as expected.
Your demo will be graded by assessing, for each software deliverable, the correctness of the
observed behavior, and the correctness of your description of that behavior.
In your report, for each software deliverable, describe:
● Describe your approach (e.g., how you used subroutines, the stack, etc)
● Challenges you faced, if any, and your solutions
● Shortcomings, possible improvements, etc
Note that you need not describe the operation of the given libraries, only how you made use of
the provided functions.
Your report is limited to four pages, total (no smaller than 10 pt font, no narrower than 1”
margins). It will be graded by assessing, for each software deliverable, your report’s clarity,
organization, and technical content.
Grading
Your demo and report are equally weighted. The breakdown for the demo and report are as
follows:
Demo
● 30% Part 1: VGA driver
● 30% Part 2: PS/2 driver
● 40% Part 3: Vexillology application
Each section will be graded for (a) clarity, (b) technical content, and (c) correct execution:
● 1pt clarity: the demo is clear and easy to follow
● 1pt technical content: correct terms are used to describe your software
● 3pt correctness: given an input, the correct output is clearly demonstrated
Report
● 30% Part 1: VGA driver
● 30% Part 2: PS/2 driver
● 40% Part 3: Vexillology application
© 4)
Each section will be graded for: (a) clarity, (b) organization, and (c) technical content:
● 1pt clarity: grammar, syntax, word choice
● 1pt organization: clear narrative flow from problem description, approach, testing,
challenges, etc.
● 3pt technical content: appropriate use of terms, description of proposed
approach, description of testing and results, etc.
Submission
Please submit, on MyCourses, your source code and report in a single .zip archive, using the
following file name conventions:
● Code: part1.s, part2.s, part3.s
● Report: StudentID_FullName_Lab1_report.pdf