PS/2 and JTAG UART Interfaces Updated: February 2014 1. Introduction This document presents an overview of the tools and two IP cores from Altera. In this lesson, we will discuss two interfaces: PS2 JTAG UART 2. Design of an Embedded System on an FPGA Device: A Review Let us quickly review the design procedure to interface with a new hardware device. We designed and implemented a hardware controller to interface with a device at the physical hardware level in lab 1 (PS/2 keyboard controller). At system and application level, we integrated the hardware controller into an embedded system (nios_system) and developed a software application to interface with the device in lab 2. Labs 1 and 2 have illustrated the concept of developing an interfacing core that communicates with I/O devices. The same concept can be applied to most peripherals and can be summarized as: i. Developing a hardware controller in hardware description language. This step requires knowledge of o Input and output signals o Timing requirement ii. o Communication protocol, etc. Developing a wrapper interface so that the hardware controller can be integrated into a Nios system in Qsys. This step can be done by o Using a PIO core (as in lab 2) or o Using a custom core for the controller (built-in or developed). iii. Developing a software application (or drivers) to utilize the device. Application can be written in o Assembly programming language, or o C programming language. The first step (step i) is a time consuming task. Fortunately, Altera provides a set of IP cores that are ready to be integrated into a Nios system. So, instead of developing an 1 I/O interfacing core from scratch for every device, we will use the provided IP cores (if available). This lesson mainly focuses steps ii and iii above. Specifically, we will learn how to utilize two pre-built IP cores from Altera in our applications. 3. DE2-70 Media Computer Before we develop any application in C, we need to develop the hardware system. This task is done using Qsys tool as you have seen in the tutorial and lab 2. To save time from creating, generating, port mapping, and compiling a system every time we have a new project, we will use a provided system from Altera University Program (AUP). This system is known as the DE2-70 Media Computer which includes most of the standard I/O devices that can be used to perform different experiments in this class. 3.1. System Overview The block diagram of the DE2-70 Media Computer is shown in Fig. 1. The main components include the Altera Nios-II processor, SRAM, SDRAM and on-chip memory for program and data storage, an audio-in/out port, a video-out port with both pixel and character buffers, a PS/2 serial port, a 16×2 character display, parallel ports connected to switches and lights, a timer module, and an RS 232 serial port. Fig. 1. Block diagram of the DE2-70 Media Computer [1]. 2 A screenshot of the DE2-70 Media Computer in Qsys is shown in Fig. 2. Fig. 2. Screen shot of the DE2-70 Media Computer in Qsys. Question: Will your C program from assignment 2 (the tutorial) work with this system? No. You will need to modify the base addresses. Note that this system has been verified to work correctly. You will not need to generate, port map, or compile the system. 3.4 PS2 Core Let’s first look at the PS2 core in the DE2_70 Media Computer since we have completed 2 labs dealing with a PS2 keyboard. The PS2 Core handles the timing of the PS2 Serial Data Transmission Protocol. A driver function or an application can communicate with 3 the device by reading from and writing to its data and control registers. The core comes with a 256-word First-In-First-Out (FIFO) buffer for storing data received from a PS2 device. A block diagram illustrating connections in an embedded system is shown in the figure below. Note that the processor does not access to the FIFO buffer or the controller directly. Altera FPGA PS2 Core Registers FIFO Data NIOS-II Processor PS2 connector Controller Control Fig. 3. Interfacing a Nios-II processor with a PS2 Core. Data and Control Registers Device drivers or user applications communicate with the PS2 Core through two 32-bit registers as shown in Table 1. Table 1. PS2 core register map [2]. (1) Reserved. Read values are undefined. Write zero. The fields in the data register are explained in Table 2. The PS2 Data register is both readable and writable. When bit 15, RVALID, is 1, reading from this register provides the data at the head of the FIFO in the Data field, and the number of entries in the FIFO (including this read) in the RAVAIL field. When RVALID is 1, reading from the PS2 4 Data register decrements this field by 1. Writing to the PS/2 Data register can be used to send a command in the Data field to the PS2 device. Table 2. Data register of the PS/2 core [2]. The fields in the control register are explained in Table 3. The PS2 control register can be used to enable interrupts from the PS2 port by setting the RE field to the value 1. When RE field is set, the PS2 port generates an interrupt when RAVAIL > 0. While the interrupt is pending the field RI will be set to 1, and it can be cleared by emptying the PS2 port FIFO. It means that will need to read all data in the FIFO buffer before exiting the interrupt service routine (clear the interrupt). The CE field in the PS/2 control register is used to indicate that an error has occurred when sending a command to a PS2 device. In this class, we will mainly deal with receiving data from a PS2 device. Table 3. Control register of the PS/2 core [2]. Question: What register should a user program communicate with when polling method is used? Data register 5 Example: Complete the C program below to read valid data from a PS2 device and display the data on the red LEDs of the DE2 Media Computer. Use polling method. #define LEDR_BASE_ADR #define PS2_KB_BASE_ADR 0x10000000 0x10000100 //Main function int main(void) { volatile int *LEDR_ptr volatile int *PS2_KB_ptr int ps2; = (int *) LEDR_BASE_ADR; // LEDR ip core = (int *) PS2_KB_BASE_ADR; // PS2_KB ip core // temporary storage while(1) // loop forever { ps2 = *(PS2_KB_ptr); if ( ((ps2 & 0xFFFF0000) >> 16) > 0x0) // check RAVAIL field *(LEDR_ptr) = (ps2 & 0x000000FF); // output data to red leds } return 0; } Question: Why do we need to use the ps2 variable in the previous example? In other words can we replace the while loop with while(1) // loop forever { if ( ((*PS2_KB_ptr & 0xFFFF0000) >> 16) > 0x0) *(LEDR_ptr) = (*PS2_KB_ptr & 0x000000FF); } *PS2_KB_ptr indicates a read operation two read operations are performed by the if statement one byte is lost every time the if statement is executed. PS2 Core Interrupt PS2 core setup in Qsys: o This control register is automatically generated when a PS2 core is added to the system. PS2 core setup in C program: o Bit 0 (RE bit) in the control register is set to 1. Nios-II processor setup in C program: o Set the interrupt enable bit for the PS2 core IRQ level in register 3 (ienable register) of the Nios-II processor to 1. 6 o Set the global interrupt enable bit in register 0 (status register) of the NiosII processor to 1. Setup the interrupt service routine (ISR) in C program: o Include the “exception.h” file o In the interrupt_handler function, check register 4 (ipending register) and call the appropriate ISR function. o Develop an ISR function to deal with an interrupt event and to clear the interrupt. Setup the Altera Monitor Program: o Reserve space for the exception and reset functions at the top of the memory space. Example: Complete the C program below to read valid data from a PS2 device and display the data on the red LEDs of the DE2 Media Computer. Use interrupt method. #include "exception.h" #define LEDR_BASE_ADR 0x10000000 #define PS2_KB_BASE_ADR 0x10000100 //Main function int main(void) { int *PS2_KB_ptr = (int *) PS2_KB_BASE_ADR ; // PS2_KB ip core // PS2 Core Setup for interrupt *(PS2_KB_ptr + 1) = 0x01; // set RE = 1 to enable interrupt // Enable individual components and global interrupt bit (Nios side) __builtin_wrctl(3, 0x80); //Enable IRQ7 level __builtin_wrctl(0, 1); //Write 1 into status register while(1) // loop forever {} // do nothing, wait for interrupt return 0; } // interrupt handler function --> handle all interrupt events void interrupt_handler(void) { int ipending; ipending = __builtin_rdctl(4); //Read the ipending register if ((ipending & 0x80) > 0) ps2_kb_isr(); //If irq7 is high, run ps2_kb_isr() return; } 7 // ps2 keyboard interrupt service routine void ps2_kb_isr(void) { int *PS2_KB_ptr = (int *) PS2_KB_BASE_ADR ; int *LEDR_ptr = (int *) LEDR_BASE_ADR; *LEDR_ptr = (*PS2_KB_ptr & 0x000000FF); // PS2_KB ip core // output data to red leds return; } Question: Why don’t we need to use the ps2 variable in the interrupt example? Because we don’t have to check for valid data in the data register only one read operation is needed. 3.4 JTAG UART Core A simple and commonly used scheme for transferring data between a processor and an I/O device is known as the Universal Asynchronous Receiver Transmitter (UART). A UART interface (circuit) is placed between the processor and the I/O device. It handles data one 8bit character (ASCII code) at a time. We will use a JTAG UART core (named JTAG_UART in the DE2 Media Computer) to establish a connection between a Nios II processor and the host computer connected to the DE2_70 board. Fig. 2 shows a block diagram of the JTAG UART circuit. On one side the JTAG UART connects to the Avalon switch fabric, which interconnects the Nios II processor, the memory chips, and the I/O interfaces. On the other side it connects to the host computer via the JTAG port using an USB-Blaster cable. The JTAG UART core contains two registers: Data and Control, which are accessed by the processor as memory locations. The core also contains two 64-character FIFOs that serve as storage buffers, one for queuing up the data to be transmitted to the host and the other for queuing up the data received from the host. A block diagram illustrating connections in an embedded system is shown in the figure below. Note that the processor does not access to the FIFO buffer or the controller directly. 8 Nios-II processor Fig. 4. Block diagram for JTAG UART circuit [3]. Data and Control Registers Device drivers or user applications communicate with the JTAG UART core through two 32-bit registers as shown in the table below. Table 4. JTAG UART core register map Reserved space: Read values are undefined. Write zero. The fields in the data register are explained in Table 5. When bit 15, RVALID, is 1, reading from this register provides the character (ASCII code) at the head of the FIFO in the Data field, and the number of entries in the FIFO (after this read) in the RAVAIL 9 field. When RVALID is 1, reading from the PS2 data register decrements this field by 1. Writing to the data register stores the ASCII value in the write FIFO. If the write FIFO is full, the character is lost. Table 5. JTAG UART data register bits [3]. The fields in the control register are explained in Table 6. The JTAG UART control register can be used to enable read or write interrupts from the JTAG UART port by setting the RE (for Read interrupt) or WE (for Write interrupt) field to the value 1. Table 6. JTAG UART control register bits [3]. When RE field is set, the JTAG UART port generates an interrupt whenever the read FIFO is nearly full. The nearly full threshold value is specified at system generation time in Qsys and cannot be changed by the C program. The read threshold value (rtv) is set to 8 in the DE2-70 Media Computer as shown in Fig. 5. The read interrupt condition is set whenever the read FIFO has rtv or fewer empty spaces remaining. The read interrupt condition is also set if there is at least one character in the read FIFO and no more characters are expected. While the interrupt is pending the field RI will be set to 1, and it can be cleared by reading characters from the data register. 10 The JTAG UART port also can assert a write interrupt whenever the write FIFO is nearly empty. The write threshold value (wtv) is specified at system generation time in Qsys and cannot be changed by the C program. The wtv is set to 8 in the DE2-70 Media Computer as shown in Fig. 5. The write interrupt condition is set whenever there are wtv or fewer characters in the write FIFO. If it has no characters remaining to send, the application program should disable the write interrupt. While the interrupt is pending the field WI will be set to 1, and it is cleared by writing characters to the data register to fill the write FIFO beyond the wtv. Fig. 5. Write threshold value (wtv) and read threshold value (rtv) setup. We will mostly deal with the polling method for the JTAG UART port in the DE2-70 Media Computer. Using JTAG UART Core in Altera Monitor Program The JTAG_UART core can be used to display text messages (i.e. for troubleshooting) in the terminal window of the Altera Monitor Program. The terminal window can also be used to and to read in ASCII characters from the host computer via the JTAG_UART core. To establish this type of communication, you will need to set the Terminal device as JTAG_UART under the System Settings tab as shown in Fig. 6. The output from the JTAG UART core will be displayed in the terminal window. While the C 11 program is running, you will need to click inside the terminal window to read input characters from the JTAG UART core. An example is shown in Fig. 7. Fig. 6. JTAG UART setup in Altera Monitor Program. Fig. 7. An example using JTAG UART core in the Altera Monitor Program. 12 Example: Complete the put_jtag function in the C program below to display the character c in the terminal using the JTAG UART core. Your function should check for WSPACE field. Print/Output c only if WSPACE > 0 (available spaces in the write FIFO). See example program below. Example: Complete the while loop in the C program below to read in a character c in the terminal using the JTAG UART core. Use polling technique to check for valid input. See example program below. //Base Addresses from DE2-70 Media Computer #define JTAG_UART_BASE_ADR 0x10001000 /* function prototypes */ void put_jtag(volatile int *, char); //Main function int main(void) { volatile int * JTAG_UART_ptr = (int *) JTAG_UART_BASE_ADR; //base address int i, data; char text_string[] = "\n EC463 JTAG UART example: \n> \0"; /* print a text string */ for (i = 0; text_string[i] != 0; i++) put_jtag (JTAG_UART_ptr, text_string[i]); while(1)//main loop { data = *(JTAG_UART_ptr); // read the JTAG_UART data register if (data & 0x00008000) // check RVALID bit { data = data & 0x000000FF; // extract data byte put_jtag (JTAG_UART_ptr, (char)data ); // output data byte } } return 0; } //Subroutine to send a character to the JTAG UART void put_jtag( volatile int * JTAG_UART_ptr, char c ) { if (*(JTAG_UART_ptr + 1) & 0xFFFF0000) // if WSPACE > 0, *(JTAG_UART_ptr) = c; // then print the character, else ignore } 13 4. Exercise Use an interrupt method to interface with a PS2 keyboard via the PS2 core of the DE2-70 Media Computer. Convert the make code of the numeric (0 – 9), ‘-‘, ‘+’, and enter keys to ASCII codes and display them in the Altera Monitor Program terminal. Other scan codes should be converted ASCII value of ‘E’. 5. References: [1]. Altera, “Media Computer System for the Altera DE-70 Board,” for Quartus II v.13.0, May 2013. Altera, “PS/2 Core for Altera DE-Series Boards,” for Quartus II v.13.0, May 2012. Altera, “Chapter 6: JTAG UART Core,” in the Embedded Peripheral IP User Guide, June 2011. [2]. [3]. 14
© Copyright 2024