ECSE 487 Experiment 1 Lab Report Michael Dang’ana 110234458 Michael.dangana@mail.mcgill.ca 29-Jan-08 TABLE OF CONTENTS 1. Methodology/implementation notes ............................................... Error! Bookmark not defined. 2. Performance testing method and results .................................................................................. 14 References....................................................................................................................................... 16 Appendix A: Source Code ................................................................................................................. 16 1. Lab Description Design Problem Part 1 The goal is to design an 8-bit 4-function barrel shifter, able to perform arithmetic right shifts, logical left and right shifts and rotations. Two versions of the barrel shifter are desired here: behavioural and structural. The behavioural model uses mainly signals and variables and the process block, while the structural model uses combinational logic components and storage elements (registers). Design Problem Part 2 The goal is to design an 8-bit 4-function pipelined barrel shifter, able to perform arithmetic right shifts, logical left and right shifts and rotations. The version desired here is pipelined. Like the structural model it uses combinational logic components and storage elements (registers). It could also be made behaviorally. However, parallelism is employed to allow for issuance of multiple shift operations simultaneously. This maximizes throughput in the design. A structural approach has been taken due to the relative ease of adapting the structural design of part 1 to a pipelined model. This ease is there because of the similarity between the structural model explained in the instructions and the pipelined model required in Part 2. Parallelism is achieved by copying a single pipeline and allowing for issuance of inputs in every clock cycle as opposed to once every 3 clock cycles in the structural model, and once every 8 cycles in the behavioural model. 2. Behavioural Shift Register a) Methodology Implementing this barrel shift register involved using multiplexers to decide the shift function required and others to determine the relevant output bits. Each clock cycle involved the execution of either 1 or 0 shifts, depending on the shift amount specified as input. Multiple single shifts then add up to any arbitrary shift (0 up to 7) and a counter is maintained to keep track of the number of shifts executed. This counter is present in VHDL as signals and not instantiated as a block 3 bit counter (count) Sinput Clock num (shift amount) S (shift function) soutput Figure 1 b) Source Code See Appendix A c) Simulation Results See vector file in Appendix B The circuit was tested for all possible inputs using a visual basic for applications (VBA – part of MS office) generated vector file. The generating code is in Appendix B. Outputs were compared in MS excel 2003 to vector generated outputs created in VBA. A simple comparison was done to determine the values where the circuit output differs from the expected output. An auto-filter was used to select the error values and the necessary bug fixes were done. The circuit outputs were generated by the test bench which read values from the input vector file and run these through the circuit, collected the output and put it in the output vector file. Figure 2 below shows the simulation waveform. In it, 8 cycles after each input value is read the output value is available. For an arithmetic right shift function (ss = 01) and a shift amount of four (nums = 010), the following input/output pairs are shown: (datas = 11010111, data_out = 01011100) and (datas = 11011000, data_out = 01100000). Figure 2: behavioural shifter simulation waveform d) Test bench and testing software code See Appendix A for the test bench and Appendix B for the testing software. Also, attached is the verification excel file containing the testing software functions and macros. e) Synthesis See figure 1 above for the RTL schematic diagram. See Appendix A for the test bench and Appendix B for the testing software. Also, attached is the verification excel file containing the testing software functions and macros. Maximum clock speed and critical path Maximum clock speed is 91.988 MHz (10.871ns period) and the critical path has a slack of 0.87. This means that the critical path has a latency that is 0.87ns higher than the input clock period of 10 ns. As expected, the critical path is that through the counter (which has the most complex internal logic cells like flip flops on each input and output, etc). Optimization could be made by implementing this circuit with a simpler block. Instead of using a count process, the shift amount signal (nums) could be decremented in each clock cycle using a basic adder. The counter is avoided altogether. This would reduce the number of logic cells used, lowering latency and circuit area. Logic Cells Below is a breakdown of the logic cells used in this circuit: Total accumulated area : Number of Dffs or Latches : 23 Number of Function Generators : Number of MUX CARRYs : Number of gates : 16 9 16 Number of accumulated instances : 82 Below is the schematic view of the circuit. Compared to the RTL view in figure 2 above, the lookup tables (LUTs) implement the multiplexers, the D flip flops remaining present, and so is the counter. Smaller logic cells like inverters and ports are also present, which each add their own delays and latencies (like setup time, hold time, propagation delay, etc). In general multiplexers are described by the ‘if’ statements in the VHDL code, D flip flops are the data and control signals (sinput, S, snum), the counter is the constantly incremented count variable and the function generators are for the various ‘1’ and ‘0’ bit signals used inside the code during arithmetic and logical shifts. The critical path and its optimization are discussed in the ‘Maximum clock speed and critical path’ section above. Figure 3: Schematic view of behavioural shift register LUT = lookup table FDE_x = D flip flops S_int = S, shift function num = shift amount clk = clock sinput = 8 bit input soutput = 8 bit output f) Summary of resources and performance (throughput and latency) For information on logic cell resources kindly see the ‘logic cells’ of section e above. Latency is also described in the ‘critical path’ above. Initial edge separation: 10.00 Source clock delay: 1.40 Dest clock delay: + ----------- 1.40 Edge separation: Setup constraint: 10.00 - 1.17 ----------Data required time: Data arrival time: 8.83 - 9.70 ----------Slack (VIOLATED): -0.87 These are the delay/latency summary metrics for the behavioural shifter. Delays are in nanoseconds. Clock width (edge separation) was 10 ns during synthesis. The generated slack was 0.87 ns mainly due to the critical path latency created by the counter, and also the setup times at input ports and around the flip flops and multiplexers. The throughput of the circuit is 1 full execution every 8 clock cycles. This is because every cycle a single shift is done so a full 8 cycles is needed to execute the largest shift possible (a shift amount of 111). As we shall soon see, this can be overcome by pipelining. 3. Pure Combinational Shift Register a) Methodology This barrel shift register involved using multiplexers to decide the shift function required and others to determine the relevant output bits. Each clock cycle involved the execution of either 1 or 0 shifts, depending on the shift amount specified as input. Multiple single shifts then add up to any arbitrary shift (0 up to 7) and a counter is maintained to keep track of the number of shifts executed. This counter is present in VHDL as signals and not instantiated as a block 3 bit counter (count) Sinput Clock num (shift amount) S (shift function) soutput Figure 1 b) Source Code See Appendix A c) Simulation Results See vector file in Appendix B The circuit was tested for all possible inputs using a visual basic for applications (VBA – part of MS office) generated vector file. The generating code is in Appendix B. Outputs were compared in MS excel 2003 to vector generated outputs created in VBA. A simple comparison was done to determine the values where the circuit output differs from the expected output. An auto-filter was used to select the error values and the necessary bug fixes were done. The circuit outputs were generated by the test bench which read values from the input vector file and run these through the circuit, collected the output and put it in the output vector file. Figure 2 below shows the simulation waveform. In it, 8 cycles after each input value is read the output value is available. For an arithmetic right shift function (ss = 01) and a shift amount of four (nums = 010), the following input/output pairs are shown: (datas = 11010111, data_out = 01011100) and (datas = 11011000, data_out = 01100000). Figure 2: behavioural shifter simulation waveform d) Test bench and testing software code See Appendix A for the test bench and Appendix B for the testing software. Also, attached is the verification excel file containing the testing software functions and macros. e) Synthesis See figure 1 above for the RTL schematic diagram. See Appendix A for the test bench and Appendix B for the testing software. Also, attached is the verification excel file containing the testing software functions and macros. Maximum clock speed and critical path Maximum clock speed is 91.988 MHz (10.871ns period) and the critical path has a slack of 0.87. This means that the critical path has a latency that is 0.87ns higher than the input clock period of 10 ns. As expected, the critical path is that through the counter (which has the most complex internal logic cells like flip flops on each input and output, etc). Optimization could be made by implementing this circuit with a simpler block. Instead of using a count process, the shift amount signal (nums) could be decremented in each clock cycle using a basic adder. The counter is avoided altogether. This would reduce the number of logic cells used, lowering latency and circuit area. Logic Cells Below is a breakdown of the logic cells used in this circuit: Total accumulated area : Number of Dffs or Latches : 23 Number of Function Generators : Number of MUX CARRYs : Number of gates : 16 9 16 Number of accumulated instances : 82 Below is the schematic view of the circuit. Compared to the RTL view in figure 2 above, the lookup tables (LUTs) implement the multiplexers, the D flip flops remaining present, and so is the counter. Smaller logic cells like inverters and ports are also present, which each add their own delays and latencies (like setup time, hold time, propagation delay, etc). In general multiplexers are described by the ‘if’ statements in the VHDL code, D flip flops are the data and control signals (sinput, S, snum), the counter is the constantly incremented count variable and the function generators are for the various ‘1’ and ‘0’ bit signals used inside the code during arithmetic and logical shifts. The critical path and its optimization are discussed in the ‘Maximum clock speed and critical path’ section above. Figure 3: Schematic view of behavioural shift register LUT = lookup table FDE_x = D flip flops S_int = S, shift function num = shift amount clk = clock sinput = 8 bit input soutput = 8 bit output f) Summary of resources and performance (throughput and latency) For information on logic cell resources kindly see the ‘logic cells’ of section e above. Latency is also described in the ‘critical path’ above. Initial edge separation: 10.00 Source clock delay: 1.40 Dest clock delay: + ----------- 1.40 Edge separation: Setup constraint: 10.00 - 1.17 ----------Data required time: Data arrival time: 8.83 - 9.70 ----------Slack (VIOLATED): -0.87 These are the delay/latency summary metrics for the behavioural shifter. Delays are in nanoseconds. Clock width (edge separation) was 10 ns during synthesis. The generated slack was 0.87 ns mainly due to the critical path latency created by the counter, and also the setup times at input ports and around the flip flops and multiplexers. The throughput of the circuit is 1 full execution every 8 clock cycles. This is because every cycle a single shift is done so a full 8 cycles is needed to execute the largest shift possible (a shift amount of 111). As we shall soon see, this can be overcome by pipelining. 1. 2. Hardware o Game, USART, Flash and Timer modules See Lab Notes to assignment 2. Modified game module to poll for keypad device interrupts instead of USART interrupts for user input. See main.c in Appendix A. o LCD display module See Lab Notes to assignment 1. Also available in segmentDisplay.c in Appendix A. o Keypad module Enables user input. Keypad is connected according to the schematic on the MSP430 keypad device manual2. Columns are connected to P1.3, P1.4 and P1.5 respectively while the rows are as specified in the manual. There are no diodes. The table below has the control register values at initialization. The only values to change through the operation of the application are the last three: P1IES, P1IFG and P1IE. CONTROL P1SEL P3SEL P1OUT P1DIR P3DIR P3OUT P1IES P1IFG P1IE Value = 0x00 &=~(BIT0 + BIT1 + BIT2 + BIT3) = 0x00 &= ~(BIT3 + BIT4 + BIT5) |= 0x0f = 0x0f = 0x00 = 0x00 = (BIT3 + BIT4 + BIT5) Description Selects Port 1 for IO Selects Port 3 for IO Clears port 1 of charge Sets port 1 as an input port Sets port 3 as an output port Sets pins 3.0 - 3.3 (rows) to high Enables low-high (edge-select) interrupts Clears interrupt flags on port 1 Enables interrupts on pins 1.3, 1.4 and 1.5 Table 1 Port 1 interrupts are turned off during the key press and key release events (P1IE = 0x00) and turned on when each event is complete (see last row of table 1). The interrupt service routine simply asserts the key event flag and turns off interrupts. Polling of the flag is done by the playgame function (as shown above). 3. User interface User input is done via the keypad device while output is on the terminal emulator and LCD display device. Menus are used because user input is simple and requires only single character responses. At any time while the application is running, player scores can be toggled on the hex display (2 digit values ranging from 0 to 99) using the ‘*’ and ‘0’ keys of the keypad (where ‘*’ stands for player X and ‘0’ stands for player O). These values are retrieved from the flash memory device. At the end of each game, users are given the option to start a new game, reset the score or quit the game, for which the inputs are now ‘2’, ‘1’ and ‘3’ respectively. These changed from ‘r’, ‘n’ and ‘q’ used in lab 2 because of the limited key variety on the keypad. 3. Performance testing method and results For testing on game, USART, flash and timer modules, see lab 2 performance and testing section. The game module was retested for seamless coupling with the keypad device because in lab 2 the keyboard was used for input. Re-executed test cases TC1, TC2 and TC4 had the same results as in lab 2 (PASS). Refer to Lab 2 notes for these. TC3 was re-executed as part of the keypad module test suite. The test case identifiers used in this lab have no link to those used in lab 2. Module: keypad ID: Test case title TC1: Prolonged key press Test case title Purpose Test if pressing a key for a long duration will cause multiple key inputs Board drawn and awaiting input n/a Purpose Steps 1. Press and hold a key for a minute Steps Results PASS; only one key is read Results Notes n/a Notes ID: Test case title Purpose TC3: Keypad overflow Try pressing multiple keys at the same time to see if input occurs and which key is read ID: Test case title Purpose Prerequisites Board drawn and awaiting input n/a 1. Press multiple keys at the same time Prerequisites PASS; none of the keys is accepted. Results Prerequisites Test data Test data Steps Results Prerequisites Test data Test data Steps TC2: Key pressed before another is released Test if key input can occur while a key is pressed and held down Board drawn and awaiting input n/a 1. Press and hold a key 2. Press many other keys one at a time 3. Release held key PASS; Only one key input occurs: that of step 1 n/a TC4: Debounce delays Run under different debounce delays to determine the lowest acceptable delay Board drawn and awaiting input n/a 1. Input key with bounces lasting 2 ms (using timer A to bounce P1.3, P1.4 or P1.5) 2. Check to see if key is correctly read 3. Repeat steps 1-2 10 times with bounces increasing by 2 (i.e. 2ms, 4ms, etc) until bounces last 20ms. 4. Adjust the debounce period and repeat steps 13 until there are no input errors. PASS; No key input errors detected for 20ms debounce Notes and Questions If one key is pressed slightly ahead of the other, it will be read Notes and Questions Test case did not run well however because oscilloscope readings of the pin values were not successful when timer A was running. TC5: Toggle player scores during a game using * and 0 keys Test if hex display works at all times during a game Test case title Board drawn and awaiting input n/a Prerequisites TC6: Toggle player scores after a game when waiting for user input Test if hex display works at all times when waiting for a user response Game menu displayed and awaiting user response n/a 1. Press the * and 0 keys, alternate between them PASS; Correct player scores are displayed on the LCD n/a Steps Module: Segment (LCD hex) display ID: Test case title Purpose Prerequisites Test data Steps Results Notes Purpose Test data Results Notes 1. Press the * and 0 keys, alternate between them PASS; Correct player scores are displayed on the LCD n/a References 1. Lab 3 instructions (McGill Webct > Microprocessors > Home Page > Experiment Assignments > Lab 3 > LabExperiment3_Fall2007) 2. http://www.cs.mcgill.ca/~mdanga2/courses/ecse426/a3/keypadDoc.pdf > page 2 Appendix A: Source Code /************************************************************* ***************** File: main.c Author: Theodore Herman & Michael Dang'ana Date: 24 Oct 2007 Turns off watchdog timer and Oscillator faults Initializes MSP430P140 keypad and USART devices and the game module Enables interrupts and runs the game ************************************************************** ***************/ #include <msp430x14x.h> #include <usartio.h> #include <keypad.h> #include <game.h> void main(void) { unsigned int i; //Utility counter /* Stop watchdog */ WDTCTL = WDTPW + WDTHOLD; /* Turn on the XT2 oscillator and stabilize it*/ BCSCTL1 &= ~XT2OFF; /* Clock stabilization loop */ do { IFG1 &= ~OFIFG; // Clear OSCFault flag for (i = 0xFF; i > 0; i--); // Time for flag to set } while ((IFG1 & OFIFG) != 0); // OSCFault flag still set? /* * Choose the MCLK source, SMCLK source, divider for SMCLK, * Select XT2 for MCLK, select XT2 for SMCLK */ BCSCTL2 = SELS + SELM_2; // use the 8MHz clock // initialize the USART, the board (tic tac toe game), and enable interrupts initKeypad(); initUsart(); initBoard(); _EINT(); // play the game playGame(); while(1); /* this is end of program, to keep it from starting over */ } /************************************************************* ***************** File: game.h Author: Theodore Herman & Michael Dang'ana Date: 05 Oct 2007 Defines the functions and constants for the tic tac toe game. It assumes that I/O is done through the USART0 and that the number of X wins and O wins are stored in the flash memory ************************************************************** ***************/ #ifndef __game #define __game #define GAME_STARTED (0) // a game state denoting a game that has started but not completed #define GAME_FINISHED (1) // a game state denoting a completed game, waiting for user input #define GAME_EXIT (2) // a game state denoting the user wants to quit the game #define BUFFER_EMPTY (0) // denotes that the USART buffer is empty #define BUFFER_FULL (1) // denotes that the USART buffer is full #define CHAR_NEW_GAME '2' // the character the user must press to start a new game #define CHAR_RESET_SCORE '1' // the character the user must press to reset the score #define CHAR_QUIT '3' // the character the user must press to quit the game Input: char xter the location of the next letter that the user wants to place Output: Places the X or O on the board if xter is a valid position */ void placeLetter(char xter); /* #define FLASH_PW_ADDR (0xF000) // the address in flash memory to store the "password" #define FLASH_PW_VAL (55) // the "password" value #define FLASH_X_WINS_ADDR (0xF001) // the address to store the number of X wins #define FLASH_O_WINS_ADDR (0xF002) // the address to store the number of O wins #define INPUT_DELAY_MS 250 // the delay in milliseconds in between placement of the character (X or O) before the move is executed #define CLEAR_BOARD_DELAY_MS 2000 // the delay in milliseconds after the user inputs the control character before the screen is reset /* Input: none Output: handles high-level game control, calling other game functions to perform lower-level functionality */ void playGame(void); /* Input: none Output: Draws the current state of the board onto the USART */ void drawBoard(void); /* Input: none Output: Initializes the actual board array, game state, and flash memory (if it has not yet been initialized) */ void initBoard(void); /* Input: none Returns: char the winner of the current game (either 'X' or 'O') or must return '' if the game has not finished or must return 'T' if the game is a tie */ char getWinner(void); /* Input: char winner the winner of the game Output: A string of text indicating who won the game, and the total win count for X and O. Also changes the state of the game to finished, and displays a screen for the user to choose their action (new game, reset score, quit game) */ void declareWinner(char winner); /* Input: none Returns: 1 if the user wishes to exit the game, 0 otherwise */ int exitGame(void); /* Input: none Output: takes the character stored in the buffer and passes it to the appropriate function, depending on the game state Returns: 0 if the buffer was empty and had nothing to do, 1 if the buffer was full and the character was passed to a function */ int emptyTheBuffer(void); /* Input: char input the character entered by the user Output: Depending on the validity of input, applies a delay, then sets the game in the appropriate state */ void handleEndOfGameSelection(char input); #endif /************************************************************* ***************** File: game.c Author: Theodore Herman & Michael Dang'ana Date: 05 Oct 2007 Implementation of the Tic Tac Toe functions Augmented to use McGumps keypad as input ************************************************************** ***************/ #include <msp430x14x.h> #include <game.h> #include <usartio.h> #include <timer.h> #include <flashio.h> #include <keypad.h> #include <segmentDisplay.h> /*Board spot values, input buffer and LCD flag to indicate which player is currently on the hex display*/ char board[3][3], inputBuffer, hexkey; /*Player variable to show which player made the last move played, Game state variable to show the state the game is in, and Buffer state variable to show the state of the buffer*/ int player, gameState, bufferState; void updateHexDisplay(char control); /* Input: none Output: handles high-level game control, calling other game functions to perform lower-level functionality */ void playGame(void) { char winner, key; // print instructions printNewLines(15); prints("Welcome to Tic Tac Toe v2.0\r\n"); prints("Use the number keypad on the McGumps board to play\r\n"); prints("Number positions match board positions\r\n"); prints("O goes first\r\n"); hexkey = '0'; updateHexDisplay(hexkey); //initTimerA(); //Testing debounce function //timerA_delay(2); //Testing debounce function updateHexDisplay(hexkey); while(!getNewKeyEvent()); //SetBouncesNum(10); //Testing debounce function key = readkey(); if (key == '*' || key == '0') { hexkey = key; updateHexDisplay(hexkey); key = '\0'; } } while (key == '\0'); // if the game is over, pass the character to the end of game function if (gameState == GAME_FINISHED) handleEndOfGameSelection(key); else placeLetter(key); if(exitGame()) // check to see if the user has requested to exit the game break; } // user quit the game, print a goodbye screen prints("Goodbye!"); } // play the actual game while(1) { //Wait until user decides to quit, reset or restart if (gameState != GAME_FINISHED) { drawBoard(); // draw the board winner = getWinner(); // check if a winner exists if (winner != '') declareWinner(winner); // if it does, declare the winner } do { /* Input: char xter the location of the next letter that the user wants to place Output: Places the X or O on the board if xter is a valid position */ void placeLetter(char xter) { // determine the position that the user wants to go int pos = xter - '1'; } // validate the position if(xter >= '1' && xter <= '9' && board[pos / 3][pos % 3] == ' ') { // toggle the player and place the character on the board player ^= 0x01; if (player==0) board[pos / 3][pos % 3] = 'X'; else board[pos / 3][pos % 3] = 'O'; } if (gameState != GAME_FINISHED) printNewLines(10); } // output the board structure if (count1 != 2) { for (count2=0; count2<=4; count2++) sendXter('-'); printNewLines(1); } } } } void drawBoard(void) { int count1, count2; // loop variables for (count1=0; count1<3; count1++) { for (count2=0; count2<3; count2++) { // output the character at the given board position sendXter(board[count1][count2]); // output a separator if necessary if (count2<2) sendXter('|'); else { sendXter('\r'); sendXter('\n'); void initBoard(void) { int count1, count2; for (count1=0; count1<3; count1++) for (count2=0; count2<3; count2++) board[count1][count2] = ' '; // initialize the board to ' ' characters player = 0; // the first player is O // check to see if the flash memory has been initialized if (flashread((char *)FLASH_PW_ADDR) != FLASH_PW_VAL) { // if not, erase the segment, then write FLASH_PW_VAL into FLASH_PW_ADDR and 0 into the X and O win count flasherase((char *)FLASH_PW_ADDR); flashwrite(FLASH_PW_VAL, (char *)FLASH_PW_ADDR); flashwrite(0, (char *)FLASH_X_WINS_ADDR); flashwrite(0, (char *)FLASH_O_WINS_ADDR); } gameState = GAME_STARTED; { } // assume tie and then check to see if any space is still available winner = 'T'; for(count = 0; count < 3; count++) for(count2 = 0; count2 < 3; count2++) if(board[count][count2] == ' ') winner = ''; char getWinner(void) { int count, count2; // loop counter variables char winner = ''; // assume no winner // Check horizontal winner for (count=0; count<3 && winner==''; count++) if (board[count][0] != ' ' && board[count][0]==board[count][1] && board[count][1]==board[count][2]) winner = board[count][0]; // Check vertical winner for (count=0; count<3 && winner==''; count++) if (board[0][count] != ' ' && board[0][count]==board[1][count] && board[1][count]==board[2][count]) winner = board[0][count]; // Check diagonal winner if (board[0][0] != ' ' && board[0][0]==board[1][1] && board[1][1]==board[2][2]) winner = board[0][0]; if (board[0][2] != ' ' && board[0][2]==board[1][1] && board[1][1]==board[2][0]) winner = board[0][2]; // if no winner yet, check to see if the game is a tie if(winner == '') } return winner; } void declareWinner(char winner) { char xwins, owins; // the number of X wins and O wins, respectively gameState = GAME_FINISHED; // get the number of X wins and O wins from the flash memory xwins = flashread((char *)FLASH_X_WINS_ADDR); owins = flashread((char *)FLASH_O_WINS_ADDR); // increment the winner's score, and store it back in flash memory flasherase((char *)FLASH_PW_ADDR); if (winner == 'X') xwins++; else if(winner == 'O') owins++; flashwrite(FLASH_PW_VAL, (char *)FLASH_PW_ADDR); flashwrite(xwins, (char *)FLASH_X_WINS_ADDR); flashwrite(owins, (char *)FLASH_O_WINS_ADDR); // display the winner of the game, or a tie message, if it's a tie if(winner == 'X' || winner == 'O') { sendXter(winner); prints(" wins the game!"); } else if(winner == 'T') { prints("Tie game!"); } // display the total number of wins for each player prints("\r\n\r\nNumber of X wins: "); sendInt(xwins); prints("\r\nNumber of O wins: "); sendInt(owins); // print out an instructional message on the user's available actions prints("\r\nChoose one of the following:\r\n"); prints("1\t-\tReset the score (and start a new game)\r\n"); prints("2\t-\tNew game\r\n"); prints("3\t-\tQuit\r\n"); } int emptyTheBuffer(void) { if (bufferState == BUFFER_FULL) { if (gameState == GAME_STARTED) { // if the game has started but not ended, wait INPUT_DELAY_MS milliseconds // and then place the letter on the board softwareDelay(INPUT_DELAY_MS); placeLetter(inputBuffer); } else if (gameState == GAME_FINISHED) { // if the game is over, pass the character to the end of game function handleEndOfGameSelection(inputBuffer); } bufferState = BUFFER_EMPTY; return 1; } else return 0; } int exitGame(void) { if(gameState == GAME_EXIT) return 1; else return 0; } void handleEndOfGameSelection(char input) { switch(input) { case CHAR_NEW_GAME: prints("Starting new game...\r\n"); softwareDelay(CLEAR_BOARD_DELAY_MS); printNewLines(10); initBoard(); break; case CHAR_RESET_SCORE: prints("Game score reset...\r\n"); flasherase((char *)FLASH_PW_ADDR); softwareDelay(CLEAR_BOARD_DELAY_MS); printNewLines(10); initBoard(); break; case CHAR_QUIT: prints("Quitting game...\r\n"); softwareDelay(CLEAR_BOARD_DELAY_MS); printNewLines(15); gameState = GAME_EXIT; break; } } void updateHexDisplay(char control) { char xwins, owins; // the number of X wins and O wins, respectively // get the number of X wins and O wins from the flash memory xwins = flashread((char *)FLASH_X_WINS_ADDR); owins = flashread((char *)FLASH_O_WINS_ADDR); //Modify wins to display as decimal numbers on hex display xwins = xwins + 6 * ((int)xwins / 10); owins = owins + 6 * ((int)owins / 10); /* Set P1.0 and P5 as output ports */ P5DIR = 0xFF; //Display wins on hex display if (control == '*') display_num(xwins); if (control == '0') display_num(owins); } /* This function simulates key press bounces to ease testing of the debounce delay */ void simulateKeyBounce(int interval_ms) { } /************************************************************* ***************** File: keypad.h Authors: Theodore Herman & Michael Dang'ana Date: 20 Oct 2007 Purpuse: The Keypad module allows for manipulation of the MSP430 Keypad device . The three functions (initKeypad, readkey and getNewKeyEvent) allow for initialization and use of the MSP430 keypad device Function code is available in keypad.c ************************************************************** ***************/ #ifndef __keypad #define __keypad #define FALSE (0) //Boolean 'false' defined as zero #define TRUE (1) //Boolean 'true' defined as one #define DEBOUNCE_DELAY_MS (10) //Debounce delay in milliseconds /* Initializes the keypad device Assumes PORT1 is connecting to the columns, PORT3 is connected to the rows */ void initKeypad(void); /* Reads a pressed key based on row and column values Returns the char value of the key */ char readkey(void); /* Returns the value of the 'keyevent' variable Allows for key event polling */ int getNewKeyEvent(void); #endif /************************************************************* ***************** File: keypad.c Authors: Theodore Herman & Michael Dang'ana Date: 20 Oct 2007 Purpuse: The Keypad module allows for manipulation of the MSP430 Keypad device . The three functions (initKeypad, readkey and getNewKeyEvent) allow for initialization and use of the MSP430 keypad device ************************************************************** ***************/ #include <keypad.h> #include <cross_studio_io.h> #include <msp430x14x.h> #include <timer.h> int keyevent = FALSE; //Indicates that a key event (key press or release) has occured int keypress = TRUE; //First keyevent should be a key press char key = '\0'; //Value of last key pressed /* Initializes the keypad device Assumes PORT1 is connecting to the columns, PORT3 is connected to the rows */ void initKeypad(void) { // Set P1 as input port and P3 as output port P1SEL = 0x00; //Select Port 1 for IO P3SEL &= ~(BIT0 + BIT1 + BIT2 + BIT3); //Select Port 3 for IO /*P1DIR = 0x0f; //Set port 1 as an output port P1OUT = 0x00; //Clear port 1 of charge*/ P1DIR &= ~(BIT3 + BIT4 + BIT5); //Set port 1 as an input port P3DIR |= 0x0f; //Set port 3 as an output port P3OUT |= 0x0f; //Set pins 3.0 - 3.3 (rows) to high /*Enable low-high (edge-select) interrupts and interrupt flags on port 1 & Enable interrupts on port 1*/ P1IES = 0x00; P1IFG = 0x00; P1IE = (BIT3 + BIT4 + BIT5); // bits 1,2,3 can cause interrupts on port 1 } /* Reads a pressed key based on row and column values Returns the char value of the key */ char readkey(void) { int col, row, i, keymask, identifier; key = '\0'; keyevent = FALSE; //Turn off key-press flag softwareDelay(DEBOUNCE_DELAY_MS);//Debounce for DEBOUNCE_DELAY_MS milliseconds if (keypress) { keypress = FALSE; //Next event expected should be a key release //Determine the column value col = P1IN; col &= BIT3 + BIT4 + BIT5; // clear all other bits on P1 since they're not relevant //Determine the row value /* Check for Port 1 inputs when row = 1 */ row = -1; for (i=0; i<4; i++) { keymask = 1 << i; //Rotate keymask P3OUT = keymask; //send to row inputs. Only 1 row asserted if (P1IN & (BIT3 + BIT4 + BIT5)) row = i; //check if Port 1 bit is asserted to see if } // it is the right row //Row and column values ascertained. Translate to key character identifier = ((row+1) << 8) + col; switch (identifier) { case 800: key = '1'; break; case 776: key = '2'; break; case 784: key = '3'; break; case 1056: key = '4'; break; case 1032: key = '5'; break; case 1040: key = '6'; break; case 288: key = '7'; break; case 264: key = '8'; break; case 272: key = '9'; break; case 544: key = '*'; break; case 520: key = '0'; break; case 528: key = '#'; } /*Enable high-low (edge-select) interrupt for key release and interrupt flags on port 1 & Enable interrupts on port 1*/ //debug_printf("%c",key); P1IES = (BIT3 + BIT4 + BIT5); P1IFG = (BIT3 + BIT4 + BIT5); P1IE = (BIT3 + BIT4 + BIT5); //P1IFG = 0x00; //P1IE = 0x07; } else { //Key release keypress = TRUE; //Next event expected should be a key press /*Enable low-high (edge-select) interrupts and interrupt flags on port 1 & Enable interrupts on port 1*/ P1IES = 0x00; P1IFG = 0x00; P1IE = (BIT3 + BIT4 + BIT5); } P3OUT |= 0x0f; //Drive rows to high return key; } /* Returns the value of the 'keyevent' variable Allows for key event polling */ int getNewKeyEvent(void) { if(keyevent == TRUE) return TRUE; else return FALSE; } /* Returns the value of the 'keyevent' variable Allows for key event polling */ void p1isr(void) __interrupt[PORT1_VECTOR] { keyevent = TRUE; //Set key press interrupt flag //P1IFG = 0x00; //Clear Port 1 interrupt flags P1IE = 0x00; //Turn off interrupts }