Summary
This project was done for my EGEC 446 class, Advanced Digital Design using Verilog HDL. Some features include a random seed using a linear feedback shift register, as well as multiple state machines to keep track of game and display logic.

Code
top_game_system.v
`timescale 1ns / 1ps
`include "constants.vh"
module top_game_system(
input clk,
input jump,
input reset,
output [6:0] sseg_out,
output [7:0] an_out
);
wire sclk;
// gets sent to LCD
wire [23:0] display_game_state;
wire [2:0] player_state;
wire [23:0] shifted_state;
// get value of slow clock to fit in other logic
clock_divider clock_divider_module(.clk(clk), .reset(reset), .sclk(sclk));
// grab value of player
input_handler input_handler_module(
.sclk(sclk),
.reset(reset),
.button(jump),
.player_state(player_state)
);
// get value of shifted state
game_shift game_shift_module(
.sclk(sclk),
.reset(reset),
.shifted_game_state(shifted_state)
);
// pass value of display game state
collision collision_module(
.sclk(sclk),
.reset(reset),
.shifted_display_state(shifted_state),
.player_state(player_state),
.combined_state(display_game_state)
);
// use display_game_state to update physical 7-segment
seven_segment_top seven_segment_top_module(
.clk(clk),
.display_game_state(display_game_state),
.sseg_out(sseg_out),
.an_out(an_out));
endmodule
clock_divider.v
module clock_divider(
`include "constants.vh"
input clk,
input reset,
output reg sclk
);
parameter CLOCK_DIVIDER_MAX_COUNT = 32'd49999999; // 1s period
parameter CLOCK_DIVIDER_MIN_COUNT = 32'd12499999; // 250 ms period
reg [31:0] speed_upper_bound;
reg [31:0] count;
always@(posedge clk or posedge reset) begin
if (reset) begin
count <= 32'd0;
sclk <= 1'b0;
speed_upper_bound <= CLOCK_DIVIDER_MAX_COUNT;
end else begin
if (count >= speed_upper_bound) begin // 100_000_000 per clock, divide by
count <= 32'd0;
sclk <= ~sclk;
speed_upper_bound <= (speed_upper_bound < CLOCK_DIVIDER_MIN_COUNT)?
CLOCK_DIVIDER_MIN_COUNT : ((speed_upper_bound > CLOCK_DIVIDER_MAX_COUNT)? CLOCK_DIVIDER_MAX_COUNT : speed_upper_bound - 32'd450000);
end else begin
count <= count + 1;
end
end
end
endmodule
input_handler.v
`timescale 1ns / 1ps
`include "constants.vh"
module input_handler(
input wire sclk,
input wire reset,
input wire button,
output wire [2:0] player_state
);
reg [2:0] curr;
reg [2:0] next;
// At each clock cycle assign the new state
always @(posedge sclk or posedge reset) begin
if (reset) begin
curr <= `PLAYER_FLOOR_STATE;
end else
curr <= next;
end
// Combinational part that selects next state based on input and current state
always @(*) begin
case(curr)
`PLAYER_FLOOR_STATE: begin
if (button) begin
next = `PLAYER_AIR_STATE;
end else
next = `PLAYER_FLOOR_STATE;
end
`PLAYER_AIR_STATE: begin
next = `PLAYER_FLOOR_STATE;
end
default: next = `PLAYER_FLOOR_STATE;
endcase
end
assign player_state = curr;
endmodule
game_shift.v
`timescale 1ns / 1ps
`include "constants.vh"
module game_shift(
input sclk,
input reset,
output reg [23:0] shifted_game_state
);
wire [2:0] LFSR_out;
// MUST NOT SUPPLY TWO OBSTACLES CONSECUTIVELY
wire [2:0] shift_in;
LFSR_3 RNG(.CLK(sclk), .RST(reset), .LFSR_out(LFSR_out));
// generate LFSR output into either an obstacle or a box
assign shift_in = (((LFSR_out % 2) == 0) && (shifted_game_state[2:0] != `OBSTACLE_STATE))
? `OBSTACLE_STATE : `FLOOR_STATE;
// override player state with floor, shift everything left
always@(posedge sclk or posedge reset) begin
if (reset)
shifted_game_state <= `SHIFT_RESET_STATE;
else
shifted_game_state <= {shifted_game_state[20:0], shift_in};
end
endmodule
LFSR_3.v
`timescale 1ns / 1ps
module LFSR_3 (
input wire CLK,
input wire RST,
output wire [2:0] LFSR_out
);
parameter [2:0] SEED = 3'b101;
reg [2:0] LFSR;
always @(posedge CLK or posedge RST) begin
if (RST)
LFSR <= SEED;
else
LFSR <= {LFSR[1:0], LFSR[2] ^ LFSR[0]};
end
assign LFSR_out = LFSR;
endmodule
collision.v
`timescale 1ns / 1ps
`include "constants.vh"
module collision(
input sclk,
input reset,
input wire [23:0] shifted_display_state,
input wire [2:0] player_state,
output reg [23:0] combined_state
);
reg game_over;
always @(posedge sclk or posedge reset) begin
if (reset) begin
combined_state <= `RESET_STATE;
game_over <= 1'b0;
end else if (game_over) begin
combined_state <= `GAME_OVER;
end else if (shifted_display_state[14:12] == `OBSTACLE_STATE) begin
if (player_state == `PLAYER_AIR_STATE) begin
combined_state <= {shifted_display_state[23:15], `PLAYER_BOX_JUMP_STATE, shifted_display_state[11:0]};
end else begin
combined_state <= `GAME_OVER;
game_over <= 1'b1;
end
end else begin
combined_state <= {shifted_display_state[23:15], player_state, shifted_display_state[11:0]};
end
end
endmodule
seven_segment_top.v
`timescale 1ns / 1ps
`include "constants.vh"
module seven_segment_top(
input clk,
input [23:0] display_game_state,
output [6:0] sseg_out,
output reg [7:0] an_out
);
// hold value of timer before switching to next
reg [16:0] an_count;
reg [2:0] an_select;
reg [2:0] display_select;
sseg_decoder SS0(.state(display_select), .sseg_out(sseg_out));
// 100 MHz / 100000 = 1 ms multiplexing
always@(posedge clk) begin
if (an_count >= 32'd99999) begin
an_count <= 0;
an_select <= an_select + 1'b1;
end else
an_count <= an_count + 1'b1;
end
// display game state for each segment
always@(an_select) begin
case(an_select)
3'b000: begin
an_out <= 8'b11111110;
display_select <= display_game_state[2:0];
end
3'b001: begin
an_out <= 8'b11111101;
display_select <= display_game_state[5:3];
end
3'b010: begin
an_out <= 8'b11111011;
display_select <= display_game_state[8:6];
end
3'b011: begin
an_out <= 8'b11110111;
display_select <= display_game_state[11:9];
end
3'b100: begin
an_out <= 8'b11101111;
display_select <= display_game_state[14:12];
end
3'b101: begin
an_out <= 8'b11011111;
display_select <= display_game_state[17:15];
end
3'b110: begin
an_out <= 8'b10111111;
display_select <= display_game_state[20:18];
end
3'b111: begin
an_out <= 8'b01111111;
display_select <= display_game_state[23:21];
end
endcase
end
endmodule
sseg_decoder.v
`timescale 1ns / 1ps
`include "constants.vh"
module sseg_decoder(
input [2:0] state,
output reg [6:0] sseg_out
);
parameter sseg_floor = 7'b1110111;
parameter sseg_obstacle = 7'b1100010;
parameter sseg_player_floor = 7'b1110011;
parameter sseg_player_air = 7'b1010111;
parameter sseg_player_box_jump = 7'b1000010;
parameter sseg_game_over = 7'b0000000;
always @(state) begin
case (state) // display individual character
`FLOOR_STATE : sseg_out = sseg_floor;
`OBSTACLE_STATE : sseg_out = sseg_obstacle;
`PLAYER_AIR_STATE : sseg_out = sseg_player_air;
`PLAYER_FLOOR_STATE : sseg_out = sseg_player_floor;
`PLAYER_BOX_JUMP_STATE : sseg_out = sseg_player_box_jump;
`GAME_OVER_STATE : sseg_out = sseg_game_over;
//switch off 7 segment character
default : sseg_out = 7'b1111111;
endcase
end
endmodule
constants.vh
`ifndef CONSTANTS_VH
`define CONSTANTS_VH
`define FLOOR_STATE 3'b000
`define OBSTACLE_STATE 3'b001
`define PLAYER_AIR_STATE 3'b010
`define PLAYER_FLOOR_STATE 3'b011
`define GAME_OVER_STATE 3'b100
`define PLAYER_BOX_JUMP_STATE 3'b111
`define RESET_STATE 24'b000_000_000_011_000_000_000_000
`define SHIFT_RESET_STATE 24'b000_000_000_000_000_000_000_000
`define GAME_OVER 24'b100_100_100_100_100_100_100_100
`endif
collision_tb.v
`timescale 1ns / 1ps
module collision_tb(
);
reg sclk;
reg reset;
reg button;
wire [2:0] player_state;
wire [23:0] shifted_game_state;
wire [23:0] combined_state;
input_handler player(.sclk(sclk), .reset(reset), .button(button), .player_state(player_state));
game_shift shift(.sclk(sclk), .reset(reset), .shifted_game_state(shifted_game_state));
collision col(.sclk(sclk), .reset(reset), .shifted_display_state(shifted_game_state),
.player_state(player_state), .combined_state(combined_state));
always begin
sclk = 1'b0; #10;
sclk = 1'b1; #10;
end
initial begin
reset = 1'b1; button = 1'b0; #30;
reset = 1'b0; button = 1'b0; #30;
button = 1'b1; #50;
button = 1'b0; #100;
button = 1'b1; #50;
button = 1'b0; #50;
button = 1'b1; #50;
end
endmodule
