CSE370 Laboratory Assignment 8

Counters and Finite State Machines


Distributed: Nov. 21, 2006
Due: Beginning of next lab.


Objectives

In this laboratory assignment you will use Verilog to design sequential circuits and implement them using PALs. We will start with a simple LFSR like the one you designed in Lab #4, then a counter, and then finally a state machine that implements a simple reaction-time game. In all cases, we'll only use our 22V10 PAL (see map).  This lab will focus on the sequential logic capabilities of the PAL.

 

It is strongly suggested that you go through the three modules before the lab section and simulate them in Active-HDL before synthesis and wiring. The files are in the course Labs folder, along with some CTL files that you might find useful. You might also extend the game finite state machine in the third part and try it out in Active-HDL.  You do not need a test fixture to test these modules.  Instead you can use the Stimulator feature: clock for the clk, and function (a type of stimulator where you specify time intervals when the signal is 1 or 0) for the other inputs.


The Quick-Draw Game

You will implement a circuit for a game that allows two players to test their reaction times against each other. Here's how it works: A start button is pressed to begin the game.  After an unpredictable amount of time, a light turns on.  The first player to press their stop button after the light comes on wins, and the circuit turns on their win light.  If there is a tie, both lights are turned on. Pressing the start button causes the game to start over.

 

You will build this circuit out of three components: an LFSR that implements a random number generator, a counter and a finite state machine.  You should design these components, wire them up into a circuit that implements the game, and test it using our test fixture.  You should try to do this first part (design) before coming to lab, and use lab to iron out any problems you have and build/test the circuit.

LFSR

The first circuit we'll design is an 8-bit LFSR that we will use as a random number generator.  The file lrsr8.v contains the Verilog file for an 8-bit LFSR, which is also shown below.  The 8-bit register called rand is implemented in the always @(posedge clk) block. The assignments in this block only happen on the rising edge of the clock, which is exactly how registers operate.  The if statement implements a synchronous reset (we only use synchronous resets).  If reset is asserted when the clock ticks, then the register is set to a given value, in this case 1. (0 doesn't work for an LFSR, remember?)  Otherwise, the register is set to its input value, which is named nxt_rand. The value of nxt_rand is computed by the combinational logic described in the assign statements.

 


module lfsr8 (clk, reset, rand);

   input clk, reset;

   output [7:0] rand;

   reg [7:0] rand;      // Required for always block assignment

   wire [7:0] nxt_rand;

  

   // These assigns implement the combinational logic for the

   // register inputs

   assign     nxt_rand[0] = rand[7] ^ rand[6] ^ rand[5] ^ rand[0];

   assign     nxt_rand[7:1] = rand[6:0];

  

   // This always block implements the "rand" register

   always @(posedge clk) begin

      if (reset) begin

        rand <= 1;

      end else begin

        rand <= nxt_rand;

      end

   end

 

endmodule


 

Compile this program into a PAL.  You can use the pin assignment file lfsr8.ctl to specify which pins you want to use for which signals. You can use a CTL file like this to control pin assignment when you compile PALs.  To tell the tool to use the pin assignment file (.CTL extension), in the Synthesis Options window (step 7 & 8 of the PAL Tutorial) on the General tab click the check box "Use Custom CTL file." just like in previous labs.  Next Click on the Browse button and point the program at your CTL file. Click OK.

 

After you've compiled your circuit, look at the synthesis report and study the equations for the flip-flop inputs.  Are they what you expected?

Connect your LFSR to the clock and reset switches, and the output to the LEDs.  Run your LFSR and verify that it is working properly.  Don’t take your circuit apart!  You will use it in the next two steps.

Counter

In this part, you will implement a simple 8-bit counter using a PAL. The Verilog file count8.v  implements this counter.  Note that this is an unusual counter: It has an 8-bit counter internally, but has only one output, tc.  This stands for "terminal count" and is asserted when the count is at its maximum value. There is a load signal, which loads the counter with the datain value.

 

Notice the slightly different style of Verilog used in this code. There are two kinds of always blocks: register blocks that use the always @(posedge clk), and combinational blocks, that do not use posedge.  In this file, both types of always blocks are used, one for the register count, and one for the combinational logic.  Note that the <= operator is used for assignment in the register always block, and that the normal= operator is used for combinational logic.  Why is that?  Also note that any variable assigned in an always block must be declared "reg".  This does not mean register!  Only variables assigned to in an always @(posedge clk) block are registers.

 

Combinational always blocks have another requirement: all the inputs used in the block must be listed in the always header, in this case, (count or datain or load).  If you forget and leave out a signal, it is an error.  You would think the compiler could figure this out (and new ones do) but the Warp compiler doesn't, and so you are stuck with this constraint.

 

Always blocks are more general than assign statements in that you can use multiple statements, including if, case, etc. to describe the combinational function you want to compute.  The one restriction is that if you assign a value to a variable in the always block, then you must assign a value to that variable for any execution of the block.  In this case, it would be an error to leave out the first assignment: nxt_count = count; because otherwise nxt_count would not be assigned when both (load==0) and (count==0).


module count8 (clk, reset, datain, load, tc);

   input clk, reset;

   input [7:0] datain;  // Value to be loaded into the counter

   input load;          // Load initializes the counter to datain

   output tc;           // "Terminal Count", i.e. highest count value

   reg    tc;           // Declaration needed for always block

  

   reg [7:0] count, nxt_count;

  

   // This block implements a simple register

   always @(posedge clk) begin

        if (reset) count <= 0;

        else       count <= nxt_count;

   end

 

   // This block implements the logic for the next count value and

   // for the tc output

   always @(count or datain or load) begin

      nxt_count = count;      // Default is to do nothing

      if (load) nxt_count = datain;

      else if (count != 0) nxt_count = count + 1;

      tc = (count == 255);

   end

endmodule // count8


 

To test this circuit, connect the datain input to the output of the LFSR.  When you assert the load input, the counter should count up to zero and then stop. It should also assert the tc output when it reaches 255.
At this point, show your work to the TA to get checked off.  Don’t disconnect your circuit!

The Quick-Draw Game

Now you will implement a finite state machine for a game that allows two players to test their reaction times against each other. Again, here's how it works: Each player has a "stop" button and a "win" light.  A third "start" button is pressed to begin the game.  After an unpredictable amount of time, a "start" light turns on. Each player tries to be the first to press their stop button after this light turns on.  The first player to press their stop button after the light comes on wins and the circuit turns on their win light.  If there is a tie, both lights are turned on. Pressing the start button causes the game to start over.

 

You will use the LFSR and counter to implement the unpredictable amount of time.  This should already be connected in the previous step. The LFSR cycles endlessly and when the start button is pressed, the current value is loaded into the counter and the counter is enabled.  When the counter reaches the end (TC==1), the light is turned on, and the state machine waits for the first button to be pushed.

 

The Verilog file game.v contains an implementation of the finite state machine for this game, but only for one player. Note how the state machine is described: Again, there is one always block for the register, this time the state register, and one always block for the combinational logic, in this case the next state function and output function.  This second always block lists all the inputs that are used in this block.

 

You must extend it to work for two players.  Here are some things to think about:

  • The machine should only react to the first button.
  • What should it do if a player tries to get a head start and pushes the button before the light goes on?
  • How many states do you need?  How many name bits do you need for the state register?

 


module game (clk, reset, startButton, loadCnt, tc, startLight,

           stopA, winA, stopB, winB);

   input clk, reset;

   input startButton;   // Button to start the game

   output loadCnt;      // Load the counter

   output startLight;   // Turn on the start light

   input tc;            // Terminal count from counter

   input  stopA, stopB; // Player stop buttons

   output winA, winB;   // Player Win lights

 

   // Reg declarations for always block assignments

   reg loadCnt;     

   reg startLight;  

   reg winA, winB;

  

   // State encoding

   parameter IDLE=0,    // Reset state

             START=1,   // Start the game

             WAIT=2,    // Wait for stop button

             AWIN=3;    // Player A wins!

  

   // The state register

   reg [1:0] state, nxt_state;

  

   // This block implements a simple state register

   always @(posedge clk) begin

      if (reset) state <= IDLE;

      else       state <= nxt_state;

   end

 

   // This block implements the next state function

   // and the output function

   always @(state or startButton or tc) begin

      // Defaults

      loadCnt = 0;

      startLight = 0;

      winA = 0;

      winB = 0;

      case (state)

      // Reset state

      IDLE: begin

         if (startButton) begin

            loadCnt = 1;

            nxt_state = START;

         end else begin

            nxt_state = IDLE;

         end

      end

      // Counter has been loaded; wait for it to finish

      START: begin

         if (tc) nxt_state = WAIT;

         else nxt_state = START;

      end

      // Counter has finished, turn on the start light and

      // wait for stop button

      WAIT: begin

         startLight = 1;

         if (stopA) nxt_state = AWIN;

         else nxt_state = WAIT;

      end

      // Stop button has been pushed; turn on win light

      AWIN: begin

         winA = 1;

         if (startButton) begin

            nxt_state = START;

            loadCnt = 1;

         end else begin

            nxt_state = AWIN;

         end

      end

      endcase // case(state)

   end // always @ (*)

endmodule // count8


Constructing and Testing Your Game

Wire up your game comprising the 3 PALs using LEDs and buttons.  Hand in a copy of your Verilog and schematics. Demo your circuit to a TA and get it checked off.


Comments to: cse370-webmaster@cs.washington.edu