Starting from:
$30

$24

Lab 3: Basic I/O, Timers and Interrupts solution

Introduction




This lab introduces the basic I/O capabilities of the DE1-SoC computer - the slider switches, push-buttons, LEDs and 7-Segment displays. After writing assembly drivers that interface with the I/O components, timers and interrupts are used to demonstrate polling and interrupt based applications written in C.









1 Creating the Project in the Altera Monitor Program




IMPORTANT: The project is structured as outlined below to introduce concepts that are used in writing well organized code. Furthermore, drivers for configuring the Generic Interrupt Controller (GIC) will be provided in the latter part of this lab, and the driver code relies on this project structure. The code will not compile if the project is not organized as described!




First, create a new folder named GXX Lab3, where GXX is the corresponding group number. Within this folder, create a new folder named drivers. Finally, within the drivers folder, create three folders: asm, src and inc. The final folder structure is shown in Figure 1.







GXX Lab3







drivers







asm







inc







src







Figure 1: The project folder structure




Create a new file main.c and save it in the GXX Lab3 folder.







Next, open the Altera Monitor Program and create a new project. Select the created folder GXX Lab3 as the project directory, name the project GXX Lab3, set the architecture to ARM Cortex-A9 and click ‘Next’.







When asked to select a system, select De1-SoC Computer from the drop-down menu and click ‘Next’.




Set the program type as C Program and click ‘Next’.




In the next menu, add main.c to the source files.




In the System Parameters menu, ensure that the board is detected in the ‘Host connection’ dia-logue box and click ‘Next’.




Finally, in the memory settings menu, change the Linker Section Presents from ‘Basic’ to ’Excep-tions’ and click ‘Finish’.








2 Basic I/O




For this part, it is necessary to refer to sections 2.5.6 - 2.5.10 (pp. 8 - 10) and 3.4 (pp. 20 - 21) in the De1-SoC Computer Manual.




Brief overview




The hardware setup of the I/O components is fairly simple to understand. The ARM cores have designated addresses in memory that are connected to hardware circuits on the FPGA, and these hardware circuits in turn interface with the physical I/O components.




In the case of most of the basic I/O, the FPGA hardware can be as simple as a direct mapping from the I/O terminals to the memory address designated to it. For instance, the state of the slider switches is available to the FPGA on bus of 10 wires which carry either a logical ’0’ or ’1’. This bus can be directly passed as ’write-data’ to the memory address reserved for the slider switches (0xFF200040 in this case).




It is useful to have slightly more sophisticated FPGA hardware. 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, signalling a keypress. This can be achieved by a simple edge detection cir-cuit in the FPGA.




The FPGA hardware to interface with the I/O is part of the De1-SoC computer, and is loaded when the .sof file is flashed onto the board. This section will deal with writing assembly code to control the I/O interact by reading from and writing to memory.







Getting started: Drivers for slider switches and LEDs




Slider switches:




Create a new assembly file called slider switches.s in the GXX Lab3/drivers/asm directory. Create a new subroutine labelled read slider switches ASM, which will read the value at the memory location designated for the slider switches data into the R0 register, and then branch to the link register. Make the subroutine visible to other files in the project by using the .global assembler directive. Remember to use the ARM function calling convention, and save the context if needed!.




Next, create a new header file called slider switches.h in the GXX Lab3/drivers/inc directory. The header file will provide the C function declaration for the slider switches assembly driver. Declare the function as extern int read slider switches ASM(), and make use of preprocessor directives to avoid recursive inclusion of the header file.




To help get started, code for the slider switches driver has been provided in Figure 2. Use this as a template for writing future driver code.




LEDs:




Create a new assembly file called LEDs.s in the GXX Lab3/drivers/asm directory. Create two subroutines - read LEDs ASM and write LEDs ASM. Again, export both subroutines using the




.global assembler directive




Similar to the slider switches driver, the read LEDs ASM subroutine will load the value at the LEDs memory location into R0 and then branch to LR. The write LEDs ASM subroutine will store the value in R0 at the LEDs memory location, and then branch to LR.




Create a new header file called LEDs.h in the GXX Lab3/drivers/inc directory. Provide function declarations for both the subroutines. The function declaration will not be the exact same as in the slider switches; one of these functions will have to accept an argument!







(a) Assembly file (b) Header file




Figure 2: Code for the slider switches driver




Putting it together:




Fill in the main.c file in the GXX Lab3 directory. The main function will include the header files for both the drivers, and will send the switches state to the LEDs in an infinite while loop. The code for this file is shown in Figure 3.











































Figure 3: Code for the main.c file




Next, open the project settings and add all the driver files to the project. Compile and load the project onto the De1-SoC computer, and run the code. The LED lights should now turn on and off when the corresponding slider switch is toggled.




Slightly more advanced: Drivers for HEX displays and push-buttons




Now that the basic structure of the drivers has been introduced, custom data types in C will be used to write drivers that are more readable and easier to implement. In particular, the following two drivers will focus on using enumerations in C.




HEX displays:




As in the previous parts, create two files HEX displays.s and HEX displays.h and place them in the correct folders.




The code for the header file is provided in Figure 4. Notice the new datatype HEX t defined in the form of an enumeration, where each display is given a unique value based on a one-hot encoding scheme. This will be useful when writing to multiple displays in the same function call.




Write the assembly code to implement the three functions listed in the header file. The HEX t argument can be treated as an integer in the assembly code. The subroutine should check the argument for all the displays HEX0-HEX5, and write to whichever ones have been asserted. A loop may be useful here!




HEX clear ASM will turn off all the segments of all the HEX displays passed in the argument. Similarly, HEX flood ASM will turn on all the segments. The final function HEX write ASM






4
















































Figure 4: Code for the HEX displays.h file










takes a second argument val, which is a number between 0-15. Based on this number, 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).




A sample program is shown in Figure 5 to demonstrate how multiple displays can be controlled in the same function call. Since the value for each display is based on a one-hot encoding scheme, the logical OR of all the displays will assert the bits for all the displays.


































Figure 5: Sample program that uses the HEX displays driver




Pushbuttons:




Create two files pushbuttons.s and pushbuttons.h and place them in the correct folders.




Write the assembly code to implement the functionality described in the header file, as shown in Figure 6




Putting it together:




Modify the main.c file to create an application that uses all of the drivers created so far. 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 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 KEY0 will result in the number being displayed on HEX0. Since there are no pushbuttons to correspond to HEX4 and HEX5, switch on all the segments of these two displays. Finally, asserting slider switch SW9 should clear all the HEX displays.



3 Timers




For this part, it is necessary to refer to sections 2.4.2 (p. 4) and 3.2 (p. 20) in the De1-SoC Computer Manual.







Brief introduction




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 (by using a phase-locked loop). 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.







HPS timer drivers




For this section, the drivers have been provided. Download the two files HPS TIM.s and HPS TIM.h from MyCourses and place them in the correct folders. The code for the header file is shown in Figure 7.







































































































Figure 7: Code for the HPS TIM.h file







This driver uses a new concept in C - structures. A structure is a composite datatype that allows the grouping of several variables to be accessed by a single pointer. They are similar to arrays, ex-cept that the individual elements of a structure can be of different datatypes! This driver will help demonstrate how structures can be useful by modifying multiple parameters easily.






7



Notice how the first subroutine HPS TIM config ASM takes a struct pointer as an argument. The reason for this is that if a struct is passed directly to a function, the compiler unpacks the struct elements at compile time and passes them as individual arguments to the function. Since in most cases the number of arguments will be greater than the number of argument registers, the compiler will place the extra arguments on the stack. This is perfectly fine if all the code is handled by the compiler, but since this lab requires handwritten assembly drivers, it causes the programmer a lot of extra overhead when retrieving the arguments in the assembly subroutine. By passing a struct pointer, the individual elements can be easily accessed at the corresponding offset from the base address passed in the pointer. For instance, the timeout element can be accessed in the assembly subroutine via a load instruction from the address in R0 offset by 0x4.







Notice how the timeout struct element is given in microseconds. This hides the hardware specific details of the timer from the C programmer. Since all of the HPS timers do not run on the same clock frequency, the subroutine calculates the correct load value for the corresponding timer in order to achieve the desired timeout value. Similarly, the last three struct elements should be set to 1 to be enabled, and 0 to be disabled.




The second subroutine HPS TIM read INT ASM supports multiple timer instances passed in the argument. The return value is an integer that should be interpreted as a 4-bit one-hot encoded number, with bit 0 corresponding to the S-bit of TIM0, and so on. Thus this function can be used to read the S-bit of just one timer, or can also be used to read the S-bit of all four timers, using just a single function call in both cases. Similarly, the other two subroutines also support multiple timer instances passed in the argument.







A sample program that uses the HPS timer driver is shown in Figure 8. Notice how all four HPS timers are configured to have a 1 second timeout in the same function call. The program will count from 0-15 on all four HEX displays at the same rate of 1 second. It is important to remember that the configuration values in the struct are implemented at a level of abstraction above the hardware, with the aim of providing a better hardware interface to the C programmer. How these values are then used in the assembly driver is governed by the hardware documentation (De1-SoC computer manual). Recreating this sample program will prove to be a useful exercise before attempting the application in the next section.




Creating an application: Stopwatch!




Create a simple stopwatch using the HPS timers, pushbuttons, and HEX displays. The stopwatch should be able to count in increments of 10 milliseconds. Use a single HPS timer to count time. Display milliseconds on HEX1-0, seconds on HEX3-2, and minutes on HEX5-4.




PB0, PB1, and PB2 will be used to start, stop and reset the stopwatch respectively. Use another HPS timer set at a faster timeout value (5 milliseconds or less) to poll the pushbutton edgecapture register.









Figure 8: Sample program that uses the HPS timer driver

































9
4 Interrupts




For this part, it is necessary to refer to section 3 (pp. 19-32) in the De1-SoC Computer Manual. Additional information about the interrupt drivers that are provided can be found in ’Using the ARM Generic Interrupt Controller’ which is available on myCourses.







Brief introduction




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 current code execution, 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 processors time and use it only when required.




Using the interrupt drivers




Download the following files from myCourses:




int setup.c int setup.h ISRs.s




ISRs.h




address map arm.h







Within the GXX Lab3/drivers/ directory, place C files in the src, header files in the inc, and as-sembly files in the asm directories. Only the ISRs.s and ISRs.h files will need to be modified in applications. Do not modify the other files







Before attempting this section, get familiarized with the relevant documentation sections pro-vided in the introduction. To demonstrate how to use the drivers, a simple interrupt based applica-tion using HPS TIM0 is shown.




Note: Ensure that in the memory settings menu in the project settings, the Linker Section Presets has been changed from ‘Basic’ to ’Exceptions’!




To begin, the code for the main.c file is shown in Figure 9. The int setup() function is the only thing needed to configure the interrupt controller and enable the desired interrupt IDs. It takes two arguments: an integer whose value denotes the number of interrupt IDs to enable, and an integer array containing these IDs. In this example, the only interrupt ID enabled is 199, corresponding to HPS TIM0.







After enabling interrupts for the desired IDs, the hardware devices themselves have to be pro-grammed to generate interrupts. This is done in the code above via the HPS timer driver. Instruc-tions for enabling interrupts from the different hardware devices can be found in the documentation.













10
































































































Figure 9: Interrupts example: The main.c file







Now that HPS TIM0 is able to send interrupts, ISR code is needed to handle the interrupt events. Notice how in the while loop of the main program, the value of hps tim0 int flag is checked to see if an interrupt has occurred. The ISR code is responsible for writing to this flag, and also for clearing the interrupt status in HPS TIM0.







When interrupts from a device are enabled and an interrupt is received, the processor halts code execution and branches to the appropriate subroutine in the ISRs.s file. This is where the ISR code should be written. Figure 10 shows the ISR code for HPS TIM0. In the ISR, the interrupt status of the timer is cleared, and the interrupt flag is asserted.




Finally, in order for the main program to use the interrupt flag, it is declared in the ISRs.h file as shown in Figure 11.




IMPORTANT: When ISR code is being executed, the processor has halted normal execution. Lengthy ISR code will cause the application to freeze. ISR code should be as lightweight as possible!







Interrupt based stopwatch!




Modify the stopwatch application from the previous section to use interrupts. In particular, enable interrupts for the HPR timer used to count time for the stopwatch. Also enable interrupts for the pushbuttons, and determine which key was pressed when a pushbutton interrupt is received. There is no need for the second HPS timer that was used to poll the pushbuttons in the previous section.







11




































































































































Figure 10: Interrupts example: The ISR assembly code
































































12






























































































































Figure 11: Interrupts example: Flag declaration in ISRs.h






































































13
5 Grading




The TA will ask to see the following deliverables during the demo (the corresponding portion of your grade for each is indicated in brackets):




Slider switches and LEDs program (10%) Entire basic I/O program (15%)




Polling based stopwatch (30%) Interrupt based stopwatch (25%)




Full marks are awarded for a deliverable only if the program functions correctly and the TA’s ques-tions are answered satisfactorily.




A portion of the grade is reserved for answering questions about the code, which is awarded in-dividually to group members. All members of your group should be able to answer any questions the TA has about any part of the deliverables, whether or not you wrote the particular part of the code the TA asks about. Full marks are awarded for a deliverable only if the program functions correctly and the TA’s questions are answered satisfactorily.




Finally, the remaining 20% of the grade for this Lab will go towards a report. Write up a short (3-4) page report that gives a brief description of each part completed, the approach taken, and the challenges faced, if any. Please don’t include the entire code in the body of the report. Save the space for elaborating on possible improvements you made or could have made to the program.




Your final submission should be a single compressed folder that contains your report and all the code files, correctly organized (.c, .h and .s).




This Lab will run for five weeks, from February 19th to March 23rd, along with Lab 4. There will be no lab sessions during reading week, March 5th to March 9th. You should demo your code within the other active weeks within your assigned lab period. The report for Lab 3 is due by 11:59 pm, March 30th.


















































































14

More products