RAT MCU BASED NES CONTROLLER DRIVER
by tstarr-1 in Circuits > Electronics
785 Views, 4 Favorites, 0 Comments
RAT MCU BASED NES CONTROLLER DRIVER
Authors: Ezzeddeen Gazali and Tyler Starr
Introduction
For our Final Project we were tasked with implementing some complex function using the RAT MCU that we have been assembling all quarter. We chose to implement a RAT assembly program which reads user inputs from an original NES controller, sends the inputs to a computer, and displays inputs and the time on a VGA display. The inputs sent to the computer is then interpreted by a python script which presses certain keys on the keyboard depending on which button was pressed on the NES controller. The RAT MCU that our program runs on adheres to the instruction set found in the RAT assembler manual and architecture.
OPERATING INSTRUCTIONS
- Connect the NES controller to the JC PMOD pins as shown in the figures above.
- Connect a VGA monitor to the VGA port on the Basys 3 board.
- Connect a micro USB cable between the Basys 3 board and the computer.
- Run the provided UART_KEY_PRESS.py script on the computer.
-
Load the provided .bit file in the attached file to the Basys 3 Board.
*NOTE: The python script requires python and three dependencies (serial, binascii, PyKeyboard).
You can now open a game or Emulator of your choice and use the NES controller to press keys on your computer. The controller will press the W, A, S, D, N, M, K and L keys on your computer as shown in the figure above so bind your keys in the game accordingly. Simultaneously pressing the start and select buttons on the controller will pause the timer. To reset the timer, press the start, select and B buttons on the controller. Switch U18 on the Basys 3 board will restart the entire program.
SUMMARY OF DEVELOPMENT
RAT MCU/WRAPPER:
The MCU used to implement the NES_CONTROLLER was designed to fulfill the RAT instruction set which is detailed in the RAT assembler manual. Additionally, the RAT MCU follows the architecture given in class and doesn’t use tri-state outputs.
CONTROLLER INPUT (NES_CONTROLLER_READER.vhd):
The RAT MCU simply reads an eight bit word which contains the inputs. The hardware which reads the controller is implemented as a finite state machine and emulates the polling mechanism which the original NES employs. The timing diagram shows this polling behavior. The NES_CONTROLLER_READER sets LATCH high for 12µs, signalling the shift register inside the NES controller to latch the current inputs of the controller and output the value for the ‘A’ button on the data line. Then the NES_CONTROLLER_READER pulses the Clock signal at 8.33kHz with a 50% duty cycle. On each rising edge of the Clock signal the data line outputs the value of the next button, the sequence of this can be seen the given timing diagram.
INPUT DESCRIPTIONS:
CLK : 100MHz clock from Basys 3 Board.
DATA : DATA from serialized output on NES controller.
RST : Reset module.
OUTPUT DESCRIPTIONS:
CLK_OUT : Clock that signals NES controller to change which input is on the DATA line.
DATA_OUT : Eight bit word indicating which buttons are held down and which aren’t.
LATCH_OUT : Signals NES controller to latch the current user input to its internal shift register.
UART (UART_TX_CTRL.vhd):
The UART module used was obtained through the Basys 3 demo project which can be found here. This module is used as an output for the MCU and sends the inputs from the NES_CONTROLLER_READER over the inbuilt UART port on the Basys 3 board. The UART module sends the values of the inputs in an eight bit word in the following sequence: A, B, SELECT, START, UP, DOWN, LEFT, and RIGHT.
INPUT DESCRIPTIONS:
CLK : 100MHz clock from Basys 3 Board.
DATA : Data from MCU to be transmitted over UART.
SEND : Enable UART module to send data.
OUTPUT DESCRIPTIONS:
UART_TX : Serialized output of DATA.
PYTHON INTERPRETER (UART_KEY_PRESS.py):
The serial input that the UART module provides is interpreted by a python program which runs on the PC. It reads in the eight bit word containing the input data and either presses or releases keys on the PC accordingly. Python was chosen because it has some robust input emulation modules which are easy to use, as well as cross platform compatibility. We chose to emulate keyboard presses rather than emulate a HID device to increase compatibility with emulators and decrease the amount of configuration required to use our controller driver.
VGA(static_VGA_wrapper.vhd):
The VGA file was provided by the instructor and was obtained from Polylearn. The file contains a VGA driver, clock divider and a wrapper to connect the modules. The VGA file was used to display a timer and the NES Controller design on the the VGA display. The wrapper takes in a 100 MHz clock signal and divides it to a 50 MHz clock (50% duty cycle) which informs the refresh rate of the individual pixels. The VGA module is driven by defining an X value, Y value, and a color value. The values are then processed and eventually sent to a VGA display by setting the enable high.
INPUT DESCRIPTIONS:
CLK_100 : 100 MHz clock input from the Basys Board
ROW : X offset for pixel to be displayed
COLUMN : Y offset for pixel to be displayed
COLOR_8BIT_sig : color of pixel to be displayed. (RRRGGGBB)
W_ENABLE : drive the pixel info to the monitor when asserted.
OUTPUT DESCRIPTIONS:
R_OUT : 3 bit color output to the monitor for RED
G_OUT : 3 bit color output to the monitor for GREEN
B_OUT : 2 bit color output to the monitor for BLUE
HSYNC : Horizontal synchronization of the monitor
VSYNC : Vertical synchronization of the monitor
TIMER (TIMER.vhd):
The timer is implemented as a resettable, and “pausable” counter; however, it doesn’t simply count in binary. The outputs for the counter are split into two BCD outputs which correspond to the four digits in a sixty minute timer. Then the BCD outputs are manipulated to output the four digits that make up the timer. For example, when counting the seconds, the ones place counts from 0-9 then resets to 0. Resetting the ones place to 0 causes an increment in the tens place of the seconds. The same occurs for the ones and tens place of minutes. The MCU has the capability to read the BCD representation of the time using the “IN” instruction and reset or pause the timer through the “OUT” instruction.
INPUT DESCRIPTIONS:
CLK : 100MHz clock from Basys 3 Board.
PAUSE : Pause/Unpause timer when PAUSE transitions from low to high.
RST : Reset time to zero minutes and zero seconds.
OUTPUT DESCRIPTIONS:
MINUTES : MSB four bits contain BCD representation of the tens of minutes,
LSB four bits contain BCD representation of minutes.
SECONDS : MSB four bits contain BCD representation of the tens of seconds,
LSB four bits contain BCD representation of seconds.
R_NUMBER(pseudo_random.vhd):
This module is a slightly modified version of the pseudo random number generator given in the example “ETCH-A-SKETCH” project. The module is used to produce random numbers which the MCU reads in. These random numbers change the color of a digit on the timer every time it gets updated. The modification made to the module ensures that numbers which would correspond to very dark colored numbers aren’t generated.
INPUT DESCRIPTIONS:
CLK : 100MHz clock from Basys 3 Board.
OUTPUT DESCRIPTIONS:
PSUEDO_RAND_NUM : a random 8 bit number used to set the color of the timer
The RAT Program(NES Controller Driver.asm):
The RAT program which ties the above elements together is outlined at a high level in the two flow charts shown. The user input is constantly polled and updated. Alternatively, the timer is interrupt driven and only updates the digits representing the timer on screen when the timer has changed. A more detailed look into how specific operations are performed can be further explored through the commented code found in the Source section.
SOURCE CODE
The source code for the project can be found in this link to GitHub. As per the request of our professor we did not provide the source code for the RAT MCU; however, we provided a .bit file that can be programmed with vivado if you wish to demo the project.
VIDEO DEMO
CONCLUSION
Through this project we have learned heaps about the construction of MCUs and the thought processes behind assembly level programming. Additionally, we have learned to dissect and compartmentalize a problem to create a circuit that performs fairly advanced functions. We have learned a lot about analyzing signals required by a circuit to function properly and creating the hardware that can provide those signals to produce something useful. We hope we have provided some insight into our problem solving process and look forward to tackling larger and more complicated problems.