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.
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.
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.
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!
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:
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
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.