vh文件
definitions.vh
// The width of the screen in pixels
`define PIXEL_WIDTH 640
// The height of the screen in pixels
`define PIXEL_HEIGHT 480
// Used for VGA horizontal and vertical sync
`define HSYNC_FRONT_PORCH 16
`define HSYNC_PULSE_WIDTH 96
`define HSYNC_BACK_PORCH 48
`define VSYNC_FRONT_PORCH 10
`define VSYNC_PULSE_WIDTH 2
`define VSYNC_BACK_PORCH 33
// How many pixels wide/high each block is
`define BLOCK_SIZE 20
// How many blocks wide the game board is
`define BLOCKS_WIDE 10
// How many blocks high the game board is
`define BLOCKS_HIGH 22
// Width of the game board in pixels
`define BOARD_WIDTH (`BLOCKS_WIDE * `BLOCK_SIZE)
// Starting x pixel for the game board
`define BOARD_X (((`PIXEL_WIDTH - `BOARD_WIDTH) / 2) - 1)
// Height of the game board in pixels
`define BOARD_HEIGHT (`BLOCKS_HIGH * `BLOCK_SIZE)
// Starting y pixel for the game board
`define BOARD_Y (((`PIXEL_HEIGHT - `BOARD_HEIGHT) / 2) - 1)
// The number of bits used to store a block position
`define BITS_BLK_POS 8
// The number of bits used to store an X position
`define BITS_X_POS 4
// The number of bits used to store a Y position
`define BITS_Y_POS 5
// The number of bits used to store a rotation
`define BITS_ROT 2
// The number of bits used to store how wide / long a block is (max of decimal 4)
`define BITS_BLK_SIZE 3
// The number of bits for the score. The score goes up to 10000
`define BITS_SCORE 14
// The number of bits used to store each block
`define BITS_PER_BLOCK 3
// The type of each block
`define EMPTY_BLOCK 3'b000
`define I_BLOCK 3'b001
`define O_BLOCK 3'b010
`define T_BLOCK 3'b011
`define S_BLOCK 3'b100
`define Z_BLOCK 3'b101
`define J_BLOCK 3'b110
`define L_BLOCK 3'b111
// Color mapping
`define WHITE 8'b11111111
`define BLACK 8'b00000000
`define GRAY 8'b10100100
`define CYAN 8'b11110000
`define YELLOW 8'b00111111
`define PURPLE 8'b11000111
`define GREEN 8'b00111000
`define RED 8'b00000111
`define BLUE 8'b11000000
`define ORANGE 8'b00011111
// Error value
`define ERR_BLK_POS 8'b11111111
// Modes
`define MODE_BITS 3
`define MODE_PLAY 0
`define MODE_DROP 1
`define MODE_PAUSE 2
`define MODE_IDLE 3
`define MODE_SHIFT 4
// The maximum value of the drop timer
`define DROP_TIMER_MAX 10000
calc_cur_blk.v
`include "definitions.vh"
module calc_cur_blk(
input wire [`BITS_PER_BLOCK-1:0] piece,
input wire [3:0] pos_x,
input wire [4:0] pos_y,
input wire [1:0] rot,
output reg [7:0] blk_1,
output reg [7:0] blk_2,
output reg [7:0] blk_3,
output reg [7:0] blk_4,
output reg [2:0] width,
output reg [2:0] height
);
always @ (*) begin
case (piece)
`EMPTY_BLOCK: begin
blk_1 = `ERR_BLK_POS;
blk_2 = `ERR_BLK_POS;
blk_3 = `ERR_BLK_POS;
blk_4 = `ERR_BLK_POS;
width = 0;
height = 0;
end
`I_BLOCK: begin
if (rot == 0 || rot == 2) begin
blk_1 = (pos_y * `BLOCKS_WIDE) + pos_x;
blk_2 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x;
blk_3 = ((pos_y + 2) * `BLOCKS_WIDE) + pos_x;
blk_4 = ((pos_y + 3) * `BLOCKS_WIDE) + pos_x;
width = 1;
height = 4;
end else begin
blk_1 = (pos_y * `BLOCKS_WIDE) + pos_x;
blk_2 = (pos_y * `BLOCKS_WIDE) + pos_x + 1;
blk_3 = (pos_y * `BLOCKS_WIDE) + pos_x + 2;
blk_4 = (pos_y * `BLOCKS_WIDE) + pos_x + 3;
width = 4;
height = 1;
end
end
`O_BLOCK: begin
blk_1 = (pos_y * `BLOCKS_WIDE) + pos_x;
blk_2 = (pos_y * `BLOCKS_WIDE) + pos_x + 1;
blk_3 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x;
blk_4 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x + 1;
width = 2;
height = 2;
end
`T_BLOCK: begin
case (rot)
0: begin
blk_1 = (pos_y * `BLOCKS_WIDE) + pos_x + 1;
blk_2 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x;
blk_3 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x + 1;
blk_4 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x + 2;
width = 3;
height = 2;
end
1: begin
blk_1 = (pos_y * `BLOCKS_WIDE) + pos_x;
blk_2 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x;
blk_3 = ((pos_y + 2) * `BLOCKS_WIDE) + pos_x;
blk_4 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x + 1;
width = 2;
height = 3;
end
2: begin
blk_1 = (pos_y * `BLOCKS_WIDE) + pos_x;
blk_2 = (pos_y * `BLOCKS_WIDE) + pos_x + 1;
blk_3 = (pos_y * `BLOCKS_WIDE) + pos_x + 2;
blk_4 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x + 1;
width = 3;
height = 2;
end
3: begin
blk_1 = (pos_y * `BLOCKS_WIDE) + pos_x + 1;
blk_2 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x + 1;
blk_3 = ((pos_y + 2) * `BLOCKS_WIDE) + pos_x + 1;
blk_4 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x;
width = 2;
height = 3;
end
endcase
end
`S_BLOCK: begin
if (rot == 0 || rot == 2) begin
blk_1 = (pos_y * `BLOCKS_WIDE) + pos_x + 1;
blk_2 = (pos_y * `BLOCKS_WIDE) + pos_x + 2;
blk_3 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x;
blk_4 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x + 1;
width = 3;
height = 2;
end else begin
blk_1 = (pos_y * `BLOCKS_WIDE) + pos_x;
blk_2 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x;
blk_3 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x + 1;
blk_4 = ((pos_y + 2) * `BLOCKS_WIDE) + pos_x + 1;
width = 2;
height = 3;
end
end
`Z_BLOCK: begin
if (rot == 0 || rot == 2) begin
blk_1 = (pos_y * `BLOCKS_WIDE) + pos_x;
blk_2 = (pos_y * `BLOCKS_WIDE) + pos_x + 1;
blk_3 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x + 1;
blk_4 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x + 2;
width = 3;
height = 2;
end else begin
blk_1 = (pos_y * `BLOCKS_WIDE) + pos_x + 1;
blk_2 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x;
blk_3 = ((pos_y + 2) * `BLOCKS_WIDE) + pos_x;
blk_4 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x + 1;
width = 2;
height = 3;
end
end
`J_BLOCK: begin
case (rot)
0: begin
blk_1 = (pos_y * `BLOCKS_WIDE) + pos_x + 1;
blk_2 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x + 1;
blk_3 = ((pos_y + 2) * `BLOCKS_WIDE) + pos_x + 1;
blk_4 = ((pos_y + 2) * `BLOCKS_WIDE) + pos_x;
width = 2;
height = 3;
end
1: begin
blk_1 = (pos_y * `BLOCKS_WIDE) + pos_x;
blk_2 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x;
blk_3 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x + 1;
blk_4 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x + 2;
width = 3;
height = 2;
end
2: begin
blk_1 = (pos_y * `BLOCKS_WIDE) + pos_x;
blk_2 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x;
blk_3 = ((pos_y + 2) * `BLOCKS_WIDE) + pos_x;
blk_4 = (pos_y * `BLOCKS_WIDE) + pos_x + 1;
width = 2;
height = 3;
end
3: begin
blk_1 = (pos_y * `BLOCKS_WIDE) + pos_x;
blk_2 = (pos_y * `BLOCKS_WIDE) + pos_x + 1;
blk_3 = (pos_y * `BLOCKS_WIDE) + pos_x + 2;
blk_4 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x + 2;
width = 3;
height = 2;
end
endcase
end
`L_BLOCK: begin
case (rot)
0: begin
blk_1 = (pos_y * `BLOCKS_WIDE) + pos_x;
blk_2 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x;
blk_3 = ((pos_y + 2) * `BLOCKS_WIDE) + pos_x;
blk_4 = ((pos_y + 2) * `BLOCKS_WIDE) + pos_x + 1;
width = 2;
height = 3;
end
1: begin
blk_1 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x;
blk_2 = (pos_y * `BLOCKS_WIDE) + pos_x;
blk_3 = (pos_y * `BLOCKS_WIDE) + pos_x + 1;
blk_4 = (pos_y * `BLOCKS_WIDE) + pos_x + 2;
width = 3;
height = 2;
end
2: begin
blk_1 = (pos_y * `BLOCKS_WIDE) + pos_x + 1;
blk_2 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x + 1;
blk_3 = ((pos_y + 2) * `BLOCKS_WIDE) + pos_x + 1;
blk_4 = (pos_y * `BLOCKS_WIDE) + pos_x;
width = 2;
height = 3;
end
3: begin
blk_1 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x;
blk_2 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x + 1;
blk_3 = ((pos_y + 1) * `BLOCKS_WIDE) + pos_x + 2;
blk_4 = (pos_y * `BLOCKS_WIDE) + pos_x + 2;
width = 3;
height = 2;
end
endcase
end
endcase
end
endmodule
calc_test_pos_rot.v
`include "definitions.vh"
module calc_test_pos_rot(
input wire [`MODE_BITS-1:0] mode,
input wire game_clk_rst,
input wire game_clk,
input wire btn_left_en,
input wire btn_right_en,
input wire btn_rotate_en,
input wire btn_down_en,
input wire btn_drop_en,
input wire [`BITS_X_POS-1:0] cur_pos_x,
input wire [`BITS_Y_POS-1:0] cur_pos_y,
input wire [`BITS_ROT-1:0] cur_rot,
output reg [`BITS_X_POS-1:0] test_pos_x,
output reg [`BITS_Y_POS-1:0] test_pos_y,
output reg [`BITS_ROT-1:0] test_rot
);
always @ (*) begin
if (mode == `MODE_PLAY) begin
if (game_clk) begin
test_pos_x = cur_pos_x;
test_pos_y = cur_pos_y + 1; // move down
test_rot = cur_rot;
end else if (btn_left_en) begin
test_pos_x = cur_pos_x - 1; // move left
test_pos_y = cur_pos_y;
test_rot = cur_rot;
end else if (btn_right_en) begin
test_pos_x = cur_pos_x + 1; // move right
test_pos_y = cur_pos_y;
test_rot = cur_rot;
end else if (btn_rotate_en) begin
test_pos_x = cur_pos_x;
test_pos_y = cur_pos_y;
test_rot = cur_rot + 1; // rotate
end else if (btn_down_en) begin
test_pos_x = cur_pos_x;
test_pos_y = cur_pos_y + 1; // move down
test_rot = cur_rot;
end else if (btn_drop_en) begin
// do nothing, we set to drop mode
test_pos_x = cur_pos_x;
test_pos_y = cur_pos_y;
test_rot = cur_rot;
end else begin
// do nothing, the block isn't moving this cycle
test_pos_x = cur_pos_x;
test_pos_y = cur_pos_y;
test_rot = cur_rot;
end
end else if (mode == `MODE_DROP) begin
if (game_clk_rst) begin
// do nothing, we set to play mode
test_pos_x = cur_pos_x;
test_pos_y = cur_pos_y;
test_rot = cur_rot;
end else begin
test_pos_x = cur_pos_x;
test_pos_y = cur_pos_y + 1; // move down
test_rot = cur_rot;
end
end else begin
// Other mode, do nothing
test_pos_x = cur_pos_x;
test_pos_y = cur_pos_y;
test_rot = cur_rot;
end
end
endmodule
complete_row.v
`include "definitions.vh"
module complete_row(
input wire clk,
input wire pause,
input wire [(`BLOCKS_WIDE*`BLOCKS_HIGH)-1:0] fallen_pieces,
output reg [`BITS_Y_POS-1:0] row,
output wire enabled
);
initial begin
row = 0;
end
// Enabled is high when the current row is complete (all 1s)
assign enabled = &fallen_pieces[row*`BLOCKS_WIDE +: `BLOCKS_WIDE];
// Increment the row under test when the clock goes high
always @ (posedge clk) begin
if (!pause) begin
if (row == `BLOCKS_HIGH - 1) begin
row <= 0;
end else begin
row <= row + 1;
end
end
end
endmodule
debouncer.v
module debouncer(
input wire raw,
input wire clk,
output wire enabled,
output wire disabled
);
reg debounced;
reg debounced_prev;
reg [15:0] counter;
initial begin
debounced = 0;
debounced_prev = 0;
counter = 0;
end
always @ (posedge clk) begin
// 200 Hz
if (counter == 12500) begin
counter <= 0;
debounced <= raw;
end else begin
counter <= counter + 1;
end
// Update previous
debounced_prev <= debounced;
end
assign enabled = debounced && !debounced_prev;
assign disabled = !debounced && debounced_prev;
endmodule
game_clock.v
module game_clock(
input wire clk,
input wire rst,
input wire pause,
output reg game_clk
);
reg [31:0] counter;
always @ (posedge clk) begin
if (!pause) begin
if (rst) begin
counter <= 0;
game_clk <= 0;
end else begin
if (counter == 25000000) begin // 1 Hz
counter <= 0;
game_clk <= 1;
end else begin
counter <= counter + 1;
game_clk <= 0;
end
end
end
end
endmodule
randomizer.v
module randomizer(
input wire clk,
output reg [2:0] random
);
initial begin
random = 1;
end
// Cycle through values at 100 MHz and select one
// at user input, which is effectively random.
always @ (posedge clk) begin
if (random == 7) begin
random <= 1;
end else begin
random <= random + 1;
end
end
endmodule
seg_display.v
`include "definitions.vh"
module seg_display(
input wire clk,
input wire [3:0] score_1, // 1's place
input wire [3:0] score_2, // 10's place
input wire [3:0] score_3, // 100's place
input wire [3:0] score_4, // 1000's place
output reg [7:0] seg,
output reg [3:0] an
);
// Divide the clock so we get a seg_clk that goes
// at a couple hundred Hz for displaying the next digit
reg [17:0] counter;
reg seg_clk;
always @ (posedge clk) begin
if (counter == 50000) begin
counter <= 0;
seg_clk <= 1;
end else begin
counter <= counter + 1;
seg_clk <= 0;
end
end
// Which digit we are currently displaying
reg [1:0] digit;
initial begin
digit = 0;
end
always @ (posedge seg_clk) begin
digit <= digit + 1;
if (digit == 0) begin
an <= 4'b0111;
display_digit(score_4);
end else if (digit == 1) begin
an <= 4'b1011;
display_digit(score_3);
end else if (digit == 2) begin
an <= 4'b1101;
display_digit(score_2);
end else begin
an <= 4'b1110;
display_digit(score_1);
end
end
task display_digit;
input [3:0] d;
begin
case (d)
0: seg <= 8'b11000000;
1: seg <= 8'b11111001;
2: seg <= 8'b10100100;
3: seg <= 8'b10110000;
4: seg <= 8'b10011001;
5: seg <= 8'b10010010;
6: seg <= 8'b10000010;
7: seg <= 8'b11111000;
8: seg <= 8'b10000000;
default: seg <= 8'b10010000;
endcase
end
endtask
endmodule // seg_display
tetris.v
`include "definitions.vh"
module tetris(
input wire clk_too_fast,
input wire btn_drop,
input wire btn_rotate,
input wire btn_left,
input wire btn_right,
input wire btn_down,
input wire sw_pause,
input wire sw_rst,
output wire [7:0] rgb,
/*
//////////////////////
input wire sw_red4,
input wire sw_green4,
input wire sw_blue3,
input wire sw_blue4,
//////////////////////
output wire red4,
output wire green4,
output wire blue3,
output wire blue4,
*/
//////////////////////
output wire hsync,
output wire vsync,
output wire [7:0] seg,
output wire [3:0] an
);
//////////////////////
/* assign red4=sw_red4;
assign green4=sw_green4;
assign blue3=sw_blue3;
assign blue4=sw_blue4;*/
/////////////////////
// Divides the clock into 25 MHz
reg clk_count;
reg clk;
initial begin
clk_count = 0;
clk = 0;
end
always @ (posedge clk_too_fast) begin
clk_count <= ~clk_count;
if (clk_count) begin
clk <= ~clk;
end
end
// Increments once per cycle to a maximum value. If this is
// not yet at the maximum value, we cannot go into drop mode.
reg [31:0] drop_timer;
initial begin
drop_timer = 0;
end
// This signal random_piece rotates between the types
// of pieces at 100 MHz, and is selected based on user input,
// making it effectively random.
wire [`BITS_PER_BLOCK-1:0] random_piece;
randomizer randomizer_ (
.clk(clk),
.random(random_piece)
);
// The enable signals for the five buttons, after
// they have gone through the debouncer. Should only be high
// for one cycle for each button press.
wire btn_drop_en;
wire btn_rotate_en;
wire btn_left_en;
wire btn_right_en;
wire btn_down_en;
// Debounce all of the input signals
debouncer debouncer_btn_drop_ (
.raw(btn_drop),
.clk(clk),
.enabled(btn_drop_en)
);
debouncer debouncer_btn_rotate_ (
.raw(btn_rotate),
.clk(clk),
.enabled(btn_rotate_en)
);
debouncer debouncer_btn_left_ (
.raw(btn_left),
.clk(clk),
.enabled(btn_left_en)
);
debouncer debouncer_btn_right_ (
.raw(btn_right),
.clk(clk),
.enabled(btn_right_en)
);
debouncer debouncer_btn_down_ (
.raw(btn_down),
.clk(clk),
.enabled(btn_down_en)
);
// Sets up wires for the pause and reset switch enable
// and disable signals, and debounces the asynchronous input.
wire sw_pause_en;
wire sw_pause_dis;
wire sw_rst_en;
wire sw_rst_dis;
debouncer debouncer_sw_pause_ (
.raw(sw_pause),
.clk(clk),
.enabled(sw_pause_en),
.disabled(sw_pause_dis)
);
debouncer debouncer_sw_rst_ (
.raw(sw_rst),
.clk(clk),
.enabled(sw_rst_en),
.disabled(sw_rst_dis)
);
// A memory bank for storing 1 bit for each board position.
// If the fallen_pieces memory is 1, there is a block still that
// has not been removed from play. This is used to draw the board
// and to test for intersection with the falling piece.
reg [(`BLOCKS_WIDE*`BLOCKS_HIGH)-1:0] fallen_pieces;
// What type of piece the current falling tetromino is. The types
// are defined in definitions.vh.
reg [`BITS_PER_BLOCK-1:0] cur_piece;
// The x position of the falling piece.
reg [`BITS_X_POS-1:0] cur_pos_x;
// The y position of the falling piece.
reg [`BITS_Y_POS-1:0] cur_pos_y;
// The current rotation of the falling piece (0 == 0 degrees, 1 == 90 degrees, etc)
reg [`BITS_ROT-1:0] cur_rot;
// The four flattened locations of the current falling tetromino. Used to
// test for intersection, or add to fallen_pieces, etc.
wire [`BITS_BLK_POS-1:0] cur_blk_1;
wire [`BITS_BLK_POS-1:0] cur_blk_2;
wire [`BITS_BLK_POS-1:0] cur_blk_3;
wire [`BITS_BLK_POS-1:0] cur_blk_4;
// The width and height of the current shape of the tetromino, based on its
// type and rotation.
wire [`BITS_BLK_SIZE-1:0] cur_width;
wire [`BITS_BLK_SIZE-1:0] cur_height;
// Use a calc_cur_blk module to get the values of the wires above from
// the current position, type, and rotation of the falling tetromino.
calc_cur_blk calc_cur_blk_ (
.piece(cur_piece),
.pos_x(cur_pos_x),
.pos_y(cur_pos_y),
.rot(cur_rot),
.blk_1(cur_blk_1),
.blk_2(cur_blk_2),
.blk_3(cur_blk_3),
.blk_4(cur_blk_4),
.width(cur_width),
.height(cur_height)
);
// The VGA controller. We give it the type of tetromino (cur_piece)
// so that it knows the right color, and the four positions on the
// board that it covers. We also pass in fallen_pieces so that it can
// display the fallen tetromino squares in monochrome.
vga_display display_ (
.clk(clk),
.cur_piece(cur_piece),
.cur_blk_1(cur_blk_1),
.cur_blk_2(cur_blk_2),
.cur_blk_3(cur_blk_3),
.cur_blk_4(cur_blk_4),
.fallen_pieces(fallen_pieces),
.rgb(rgb),
.hsync(hsync),
.vsync(vsync)
);
// The mode, used for finite state machine things. We also
// need to store the old mode occasionally, like when we're paused.
reg [`MODE_BITS-1:0] mode;
reg [`MODE_BITS-1:0] old_mode;
// The game clock
wire game_clk;
// The game clock reset
reg game_clk_rst;
// This module outputs the game clock, which is when the clock
// that determines when the tetromino falls by itself.
game_clock game_clock_ (
.clk(clk),
.rst(game_clk_rst),
.pause(mode != `MODE_PLAY),
.game_clk(game_clk)
);
// Set up some variables to test for intersection or off-screen-ness
// of the current piece if the user's current action were to be
// followed through. For example, if the user presses the left button,
// we test where the current piece would be if it was moved one to the
// left, i.e. x = x - 1.
wire [`BITS_X_POS-1:0] test_pos_x;
wire [`BITS_Y_POS-1:0] test_pos_y;
wire [`BITS_ROT-1:0] test_rot;
// Combinational logic to determine what position/rotation we are testing.
// This has been hoisted out into a module so that the code is shorter.
calc_test_pos_rot calc_test_pos_rot_ (
.mode(mode),
.game_clk_rst(game_clk_rst),
.game_clk(game_clk),
.btn_left_en(btn_left_en),
.btn_right_en(btn_right_en),
.btn_rotate_en(btn_rotate_en),
.btn_down_en(btn_down_en),
.btn_drop_en(btn_drop_en),
.cur_pos_x(cur_pos_x),
.cur_pos_y(cur_pos_y),
.cur_rot(cur_rot),
.test_pos_x(test_pos_x),
.test_pos_y(test_pos_y),
.test_rot(test_rot)
);
// Set up the outputs for the calc_test_blk module
wire [`BITS_BLK_POS-1:0] test_blk_1;
wire [`BITS_BLK_POS-1:0] test_blk_2;
wire [`BITS_BLK_POS-1:0] test_blk_3;
wire [`BITS_BLK_POS-1:0] test_blk_4;
wire [`BITS_BLK_SIZE-1:0] test_width;
wire [`BITS_BLK_SIZE-1:0] test_height;
calc_cur_blk calc_test_block_ (
.piece(cur_piece),
.pos_x(test_pos_x),
.pos_y(test_pos_y),
.rot(test_rot),
.blk_1(test_blk_1),
.blk_2(test_blk_2),
.blk_3(test_blk_3),
.blk_4(test_blk_4),
.width(test_width),
.height(test_height)
);
// This function checks whether its input block positions intersect
// with any fallen pieces.
function intersects_fallen_pieces;
input wire [7:0] blk1;
input wire [7:0] blk2;
input wire [7:0] blk3;
input wire [7:0] blk4;
begin
intersects_fallen_pieces = fallen_pieces[blk1] ||
fallen_pieces[blk2] ||
fallen_pieces[blk3] ||
fallen_pieces[blk4];
end
endfunction
// This signal goes high when the test positions/rotations intersect with
// fallen blocks.
wire test_intersects = intersects_fallen_pieces(test_blk_1, test_blk_2, test_blk_3, test_blk_4);
// If the falling piece can be moved left, moves it left
task move_left;
begin
if (cur_pos_x > 0 && !test_intersects) begin
cur_pos_x <= cur_pos_x - 1;
end
end
endtask
// If the falling piece can be moved right, moves it right
task move_right;
begin
if (cur_pos_x + cur_width < `BLOCKS_WIDE && !test_intersects) begin
cur_pos_x <= cur_pos_x + 1;
end
end
endtask
// Rotates the current block if it would not cause any part of the
// block to go off screen and would not intersect with any fallen blocks.
task rotate;
begin
if (cur_pos_x + test_width <= `BLOCKS_WIDE &&
cur_pos_y + test_height <= `BLOCKS_HIGH &&
!test_intersects) begin
cur_rot <= cur_rot + 1;
end
end
endtask
// Adds the current block to fallen_pieces
task add_to_fallen_pieces;
begin
fallen_pieces[cur_blk_1] <= 1;
fallen_pieces[cur_blk_2] <= 1;
fallen_pieces[cur_blk_3] <= 1;
fallen_pieces[cur_blk_4] <= 1;
end
endtask
// Adds the given blocks to fallen_pieces, and
// chooses a new block for the user that appears
// at the top of the screen.
task get_new_block;
begin
// Reset the drop timer, can't drop until this is high enough
drop_timer <= 0;
// Choose a new block for the user
cur_piece <= random_piece;
cur_pos_x <= (`BLOCKS_WIDE / 2) - 1;
cur_pos_y <= 0;
cur_rot <= 0;
// reset the game timer so the user has a full
// cycle before the block falls
game_clk_rst <= 1;
end
endtask
// Moves the current piece down one, getting a new block if
// the piece would go off the board or intersect with another block.
task move_down;
begin
if (cur_pos_y + cur_height < `BLOCKS_HIGH && !test_intersects) begin
cur_pos_y <= cur_pos_y + 1;
end else begin
add_to_fallen_pieces();
get_new_block();
end
end
endtask
// Sets the mode to MODE_DROP, in which the current block will not respond
// to user input and it will move down at one cycle per second until it hits
// a block or the bottom of the board.
task drop_to_bottom;
begin
mode <= `MODE_DROP;
end
endtask
// The score register, increased by one when the user
// completes a row.
reg [3:0] score_1; // 1's place
reg [3:0] score_2; // 10's place
reg [3:0] score_3; // 100's place
reg [3:0] score_4; // 1000's place
// The 7-segment display module, which outputs the score
seg_display score_display_ (
.clk(clk),
.score_1(score_1),
.score_2(score_2),
.score_3(score_3),
.score_4(score_4),
.an(an),
.seg(seg)
);
// The module that determines which row, if any, is complete
// and needs to be removed and the score incremented
wire [`BITS_Y_POS-1:0] remove_row_y;
wire remove_row_en;
complete_row complete_row_ (
.clk(clk),
.pause(mode != `MODE_PLAY),
.fallen_pieces(fallen_pieces),
.row(remove_row_y),
.enabled(remove_row_en)
);
// This task removes the completed row from fallen_pieces
// and increments the score
reg [`BITS_Y_POS-1:0] shifting_row;
task remove_row;
begin
// Shift away remove_row_y
mode <= `MODE_SHIFT;
shifting_row <= remove_row_y;
// Increment the score
if (score_1 == 9) begin
if (score_2 == 9) begin
if (score_3 == 9) begin
if (score_4 != 9) begin
score_4 <= score_4 + 1;
score_3 <= 0;
score_2 <= 0;
score_1 <= 0;
end
end else begin
score_3 <= score_3 + 1;
score_2 <= 0;
score_1 <= 0;
end
end else begin
score_2 <= score_2 + 1;
score_1 <= 0;
end
end else begin
score_1 <= score_1 + 1;
end
end
endtask
// Initialize any registers we need
initial begin
mode = `MODE_IDLE;
fallen_pieces = 0;
cur_piece = `EMPTY_BLOCK;
cur_pos_x = 0;
cur_pos_y = 0;
cur_rot = 0;
score_1 = 0;
score_2 = 0;
score_3 = 0;
score_4 = 0;
end
// Starts a new game after a button is pressed in the MODE_IDLE state
task start_game;
begin
mode <= `MODE_PLAY;
fallen_pieces <= 0;
score_1 <= 0;
score_2 <= 0;
score_3 <= 0;
score_4 <= 0;
get_new_block();
end
endtask
// Determine if the game is over because the current position
// intersects with a fallen block
wire game_over = cur_pos_y == 0 && intersects_fallen_pieces(cur_blk_1, cur_blk_2, cur_blk_3, cur_blk_4);
// Main game logic
always @ (posedge clk) begin
if (drop_timer < `DROP_TIMER_MAX) begin
drop_timer <= drop_timer + 1;
end
game_clk_rst <= 0;
if (mode == `MODE_IDLE && (sw_rst_en || sw_rst_dis)) begin
// We are in idle mode and the user has requested to start the game
start_game();
end else if (sw_rst_en || sw_rst_dis || game_over) begin
// We hit the reset switch or the game ended by itself,
// go into idle mode where we wait for the user to press a button
mode <= `MODE_IDLE;
add_to_fallen_pieces();
cur_piece <= `EMPTY_BLOCK;
end else if ((sw_pause_en || sw_pause_dis) && mode == `MODE_PLAY) begin
// If we switch on pause, save the old mode and enter
// the pause mode.
mode <= `MODE_PAUSE;
old_mode <= mode;
end else if ((sw_pause_en || sw_pause_dis) && mode == `MODE_PAUSE) begin
// If we switch off pause, enter the old mode
mode <= old_mode;
end else if (mode == `MODE_PLAY) begin
// Normal gameplay
if (game_clk) begin
move_down();
end else if (btn_left_en) begin
move_left();
end else if (btn_right_en) begin
move_right();
end else if (btn_rotate_en) begin
rotate();
end else if (btn_down_en) begin
move_down();
end else if (btn_drop_en && drop_timer == `DROP_TIMER_MAX) begin
drop_to_bottom();
end else if (remove_row_en) begin
remove_row();
end
end else if (mode == `MODE_DROP) begin
// We are dropping the block until we hit respawn
// at the top
if (game_clk_rst && !sw_pause_en) begin
mode <= `MODE_PLAY;
end else begin
move_down();
end
end else if (mode == `MODE_SHIFT) begin
// We are shifting the row above shifting_row
// into shifting_row's position
if (shifting_row == 0) begin
fallen_pieces[0 +: `BLOCKS_WIDE] <= 0;
mode <= `MODE_PLAY;
end else begin
fallen_pieces[shifting_row*`BLOCKS_WIDE +: `BLOCKS_WIDE] <= fallen_pieces[(shifting_row - 1)*`BLOCKS_WIDE +: `BLOCKS_WIDE];
shifting_row <= shifting_row - 1;
end
end
end
endmodule
vga_display.v
`include "definitions.vh"
module vga_display(
input wire clk,
input wire [`BITS_PER_BLOCK-1:0] cur_piece,
input wire [`BITS_BLK_POS-1:0] cur_blk_1,
input wire [`BITS_BLK_POS-1:0] cur_blk_2,
input wire [`BITS_BLK_POS-1:0] cur_blk_3,
input wire [`BITS_BLK_POS-1:0] cur_blk_4,
input wire [(`BLOCKS_WIDE*`BLOCKS_HIGH)-1:0] fallen_pieces,
output reg [7:0] rgb,
output wire hsync,
output wire vsync
);
reg [9:0] counter_x = 0;
reg [9:0] counter_y = 0;
assign hsync = ~(counter_x >= (`PIXEL_WIDTH + `HSYNC_FRONT_PORCH) &&
counter_x < (`PIXEL_WIDTH + `HSYNC_FRONT_PORCH + `HSYNC_PULSE_WIDTH));
assign vsync = ~(counter_y >= (`PIXEL_HEIGHT + `VSYNC_FRONT_PORCH) &&
counter_y < (`PIXEL_HEIGHT + `VSYNC_FRONT_PORCH + `VSYNC_PULSE_WIDTH));
// Combinational logic to select the current pixel
wire [9:0] cur_blk_index = ((counter_x-`BOARD_X)/`BLOCK_SIZE) + (((counter_y-`BOARD_Y)/`BLOCK_SIZE)*`BLOCKS_WIDE);
reg [2:0] cur_vid_mem;
always @ (*) begin
// Check if we're within the drawing space
if (counter_x >= `BOARD_X && counter_y >= `BOARD_Y &&
counter_x <= `BOARD_X + `BOARD_WIDTH && counter_y <= `BOARD_Y + `BOARD_HEIGHT) begin
if (counter_x == `BOARD_X || counter_x == `BOARD_X + `BOARD_WIDTH ||
counter_y == `BOARD_Y || counter_y == `BOARD_Y + `BOARD_HEIGHT) begin
// We're at the edge of the board, paint it white
rgb = `WHITE;
end else begin
if (cur_blk_index == cur_blk_1 ||
cur_blk_index == cur_blk_2 ||
cur_blk_index == cur_blk_3 ||
cur_blk_index == cur_blk_4) begin
case (cur_piece)
`EMPTY_BLOCK: rgb = `GRAY;
`I_BLOCK: rgb = `CYAN;
`O_BLOCK: rgb = `YELLOW;
`T_BLOCK: rgb = `PURPLE;
`S_BLOCK: rgb = `GREEN;
`Z_BLOCK: rgb = `RED;
`J_BLOCK: rgb = `BLUE;
`L_BLOCK: rgb = `ORANGE;
endcase
end else begin
rgb = fallen_pieces[cur_blk_index] ? `WHITE : `GRAY;
end
end
end else begin
// Outside the board
rgb = `BLACK;
end
end
always @ (posedge clk) begin
if (counter_x >= `PIXEL_WIDTH + `HSYNC_FRONT_PORCH + `HSYNC_PULSE_WIDTH + `HSYNC_BACK_PORCH) begin
counter_x <= 0;
if (counter_y >= `PIXEL_HEIGHT + `VSYNC_FRONT_PORCH + `VSYNC_PULSE_WIDTH + `VSYNC_BACK_PORCH) begin
counter_y <= 0;
end else begin
counter_y <= counter_y + 1;
end
end else begin
counter_x <= counter_x + 1;
end
end
endmodule
为了能够用键盘进行游戏,需要编写按键的控制代码,这里为了简单,用了ps2键盘实现
ps2scan.v
`timescale 1ns / 1ps
module ps2scan (
input wire clk,
input wire rst_n,
input wire ps2_sclk,
input wire ps2_sda,
output reg [7:0] out_data
);
//端口信号:模块的输入输出接口
//input clk;//系统时钟
//input rst_n;//低电平复位
//input ps2_sclk;//ps2时钟信号(ps2设备自动产生,大约10KHz左右)
//input ps2_sda;//ps2数据信号
//output reg [7:0] out_data; //键值数据(采集ps2的一帧信号中间的8位有效位)
//在发送时序中,数据在ps2_sclk的下降沿采集信号,以下为检测下降沿
reg in1,in2;
wire en;
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
in1 <= 1'b0;
in2 <= 1'b0;
end
else
begin
in1 <= ps2_sclk;
in2 <= in1;
end
end
assign en = (~in1) & in2; //当出现ps2_sclk下降沿后拉高一个时钟,以进行数据的采集
//采集ps2的一帧信号中间的8位有效位:每检测一个下降沿算一位数据位,在中间8位采集有效数据
reg[7:0] temp_data;
reg[3:0] num;
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
num <= 4'd0;
temp_data <= 8'd0;
end
else if(en)
case (num)
4'd0: num <= num+1'b1; //开始位
4'd1: begin
num <= num+1'b1;
temp_data[0] <= ps2_sda; //bit0
end
4'd2: begin
num <= num+1'b1;
temp_data[1] <= ps2_sda; //bit1
end
4'd3: begin
num <= num+1'b1;
temp_data[2] <= ps2_sda; //bit2
end
4'd4: begin
num <= num+1'b1;
temp_data[3] <= ps2_sda; //bit3
end
4'd5: begin
num <= num+1'b1;
temp_data[4] <= ps2_sda; //bit4
end
4'd6: begin
num <= num+1'b1;
temp_data[5] <= ps2_sda; //bit5
end
4'd7: begin
num <= num+1'b1;
temp_data[6] <= ps2_sda; //bit6
end
4'd8: begin
num <= num+1'b1;
temp_data[7] <= ps2_sda; //bit7
end
4'd9: num <= num+1'b1; //结束位
4'd10: num <= 4'd0;
default: num<=4'd0;
endcase
end
//判断是否有键按下:根据通码、断码的特性判断
reg key;
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
key <= 1'b0;
else if(num==4'd10)
begin
if(temp_data == 8'hf0)
key <= 1'b1;
else
begin
if(!key)
out_data <= temp_data;
else
key <= 1'b0;
end
end
end
endmodule
ps2_top.v
`timescale 1ns / 1ps
module ps2_top (
input wire clk,
input wire rst_n,
input wire ps2_sclk,
input wire ps2_sda,
output wire btn_drop,
output wire btn_rotate,
output wire btn_left,
output wire btn_right,
output wire btn_down
);
//外部接口
//input clk; //系统时钟50MHz
//input rst_n; //低电平复位
//input ps2_sclk; //ps2时钟
//input ps2_sda; //ps2数据
//output [2:0] sel; //数码管位选
//output [7:0] seg; //数码管段选
wire[7:0] out_data, tx_out;
/*****键盘扫描模块*****/
ps2scan ps2scan_inst(
.clk(clk),
.rst_n(rst_n),
.ps2_sclk(ps2_sclk),
.ps2_sda(ps2_sda),
.out_data(out_data)
);
ASCII ASCII_inst(
.out_data(out_data),
.tx_out(tx_out),
.btn_drop_wire(btn_drop),
.btn_rotate_wire(btn_rotate),
.btn_left_wire(btn_left),
.btn_right_wire(btn_right),
.btn_down_wire(btn_down)
);
/*
seg_num seg_num_inst(
.num(tx_out),
.clk(clk),
.rst_n(rst_n),
.sel(sel),
.seg(seg)
);
*/
endmodule
ASCII.v
`timescale 1ns / 1ps
module ASCII(
input wire [7:0] out_data,
output reg [7:0] tx_out,
output wire btn_drop_wire,
output wire btn_rotate_wire,
output wire btn_left_wire,
output wire btn_right_wire,
output wire btn_down_wire
);
//?????????????é??????????????
//input [7:0] out_data; //?ü?????¨?è?ü??
//output reg [7:0] tx_out;//?¨??ASCII??×??????ó????
//?¨???é??±í??·?????????ASCII?????ü??×???????????????
always@(*)
case (out_data)
8'h1c: tx_out <= 8'h41; //A
8'h32: tx_out <= 8'h42; //B
8'h21: tx_out <= 8'h43; //C
8'h23: tx_out <= 8'h44; //D
8'h24: tx_out <= 8'h45; //E
8'h2b: tx_out <= 8'h46; //F
8'h34: tx_out <= 8'h47; //G
8'h33: tx_out <= 8'h48; //H
8'h43: tx_out <= 8'h49; //I
8'h3b: tx_out <= 8'h4a; //J
8'h42: tx_out <= 8'h4b; //K
8'h4b: tx_out <= 8'h4c; //L
8'h3a: tx_out <= 8'h4d; //M
8'h31: tx_out <= 8'h4e; //N
8'h44: tx_out <= 8'h4f; //O
8'h4d: tx_out <= 8'h50; //P
8'h15: tx_out <= 8'h51; //Q
8'h2d: tx_out <= 8'h52; //R
8'h1b: tx_out <= 8'h53; //S
8'h2c: tx_out <= 8'h54; //T
8'h3c: tx_out <= 8'h55; //U
8'h2a: tx_out <= 8'h56; //V
8'h1d: tx_out <= 8'h57; //W
8'h22: tx_out <= 8'h58; //X
8'h35: tx_out <= 8'h59; //Y
8'h1a: tx_out <= 8'h5a; //Z
default: tx_out <= 8'h00;
endcase
reg btn_drop;
reg btn_rotate;
reg btn_left;
reg btn_right;
reg btn_down;
assign btn_drop_wire = btn_drop;
assign btn_rotate_wire = btn_rotate;
assign btn_left_wire = btn_left;
assign btn_right_wire = btn_right;
assign btn_down_wire = btn_down;
always @(*)
begin
if(out_data==8'h1d)
begin
btn_drop = 1'b0;
btn_rotate = 1'b1;
btn_left = 1'b0;
btn_right = 1'b0;
btn_down = 1'b0;
end
else if(out_data==8'h1c)
begin
btn_drop = 1'b0;
btn_rotate = 1'b0;
btn_left = 1'b1;
btn_right = 1'b0;
btn_down = 1'b0;
end
else if(out_data==8'h1b)
begin
btn_drop = 1'b0;
btn_rotate = 1'b0;
btn_left = 1'b0;
btn_right = 1'b0;
btn_down = 1'b1;
end
else if(out_data==8'h23)
begin
btn_drop = 1'b0;
btn_rotate = 1'b0;
btn_left = 1'b0;
btn_right = 1'b1;
btn_down = 1'b0;
end
else if(out_data==8'h2b)
begin
btn_drop = 1'b1;
btn_rotate = 1'b0;
btn_left = 1'b0;
btn_right = 1'b0;
btn_down = 1'b0;
end
end
endmodule
seg_num.v
`timescale 1ns / 1ps
module seg_num (
input wire clk,
input wire rst_n,
input wire [7:0] num,
output reg [3:0] sel,
output reg [7:0] seg
);
reg [16:0]number=17'd0;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
number<=17'd0;
else
begin
if(number<40000)
number<=number+1'b1;
else
number<=0;
end
end
always@(posedge clk)
begin
if(number>20000)
begin
sel=4'b1101;
case(num[7:4])
4'd0:seg[7:0]<=8'b1100_0000;
4'd1:seg[7:0]<=8'b1111_1001;
4'd2:seg[7:0]<=8'b1010_0100;
4'd3:seg[7:0]<=8'b1011_0000;
4'd4:seg[7:0]<=8'b1001_1001;
4'd5:seg[7:0]<=8'b1001_0010;
4'd6:seg[7:0]<=8'b1000_0010;
4'd7:seg[7:0]<=8'b1111_1000;
4'd8:seg[7:0]<=8'b1000_0000;
4'd9:seg[7:0]<=8'b1001_0000;
4'd10:seg[7:0]<=8'b1000_1000;
4'd11:seg[7:0]<=8'b1000_0011;
4'd12:seg[7:0]<=8'b1010_0111;
4'd13:seg[7:0]<=8'b1010_0001;
4'd14:seg[7:0]<=8'b1000_0110;
4'd15:seg[7:0]<=8'b1000_1110;
default:seg[7:0]<=8'b1111_0111;
endcase
end
else
begin
sel=4'b1110;
case(num[3:0])
4'd0:seg[7:0]<=8'b1100_0000;
4'd1:seg[7:0]<=8'b1111_1001;
4'd2:seg[7:0]<=8'b1010_0100;
4'd3:seg[7:0]<=8'b1011_0000;
4'd4:seg[7:0]<=8'b1001_1001;
4'd5:seg[7:0]<=8'b1001_0010;
4'd6:seg[7:0]<=8'b1000_0010;
4'd7:seg[7:0]<=8'b1111_1000;
4'd8:seg[7:0]<=8'b1000_0000;
4'd9:seg[7:0]<=8'b1001_0000;
4'd10:seg[7:0]<=8'b1000_1000;
4'd11:seg[7:0]<=8'b1000_0011;
4'd12:seg[7:0]<=8'b1010_0111;
4'd13:seg[7:0]<=8'b1010_0001;
4'd14:seg[7:0]<=8'b1000_0110;
4'd15:seg[7:0]<=8'b1000_1110;
default:seg[7:0]<=8'b1111_0111;
endcase
end
end
endmodule