initial commit

This commit is contained in:
John Bintz 2024-07-06 14:51:29 -04:00
commit 59f7d5db2e
26 changed files with 2191 additions and 0 deletions

71
Go_Board_Constraints.pcf Normal file
View File

@ -0,0 +1,71 @@
# ##############################################################################
# iCEcube PCF
# Version: 2014.12.27052
# File Generated: Apr 27 2015 09:46:33
# Family & Device: iCE40HX1K
# Package: VQ100
# ##############################################################################
### Main FPGA Clock
set_io i_Clk 15
### LED Pins:
set_io o_LED_1 56
set_io o_LED_2 57
set_io o_LED_3 59
set_io o_LED_4 60
## Push-Button Switches
set_io i_Switch_1 53
set_io i_Switch_2 51
set_io i_Switch_3 54
set_io i_Switch_4 52
### 7 Segment Outputs
set_io o_Segment1_A 3
set_io o_Segment1_B 4
set_io o_Segment1_C 93
set_io o_Segment1_D 91
set_io o_Segment1_E 90
set_io o_Segment1_F 1
set_io o_Segment1_G 2
set_io o_Segment2_A 100
set_io o_Segment2_B 99
set_io o_Segment2_C 97
set_io o_Segment2_D 95
set_io o_Segment2_E 94
set_io o_Segment2_F 8
set_io o_Segment2_G 96
## UART Outputs
set_io i_UART_RX 73
set_io o_UART_TX 74
## VGA Outputs
set_io o_VGA_HSync 26
set_io o_VGA_VSync 27
set_io o_VGA_Red_0 36
set_io o_VGA_Red_1 37
set_io o_VGA_Red_2 40
set_io o_VGA_Grn_0 29
set_io o_VGA_Grn_1 30
set_io o_VGA_Grn_2 33
set_io o_VGA_Blu_0 28
set_io o_VGA_Blu_1 41
set_io o_VGA_Blu_2 42
## PMOD Signals
set_io io_PMOD_1 65
set_io io_PMOD_2 64
set_io io_PMOD_3 63
set_io io_PMOD_4 62
set_io io_PMOD_7 78
set_io io_PMOD_8 79
set_io io_PMOD_9 80
set_io io_PMOD_10 81

23
Makefile Normal file
View File

@ -0,0 +1,23 @@
CONSTRAINTS = ../Go_Board_Constraints.pcf
LED_TOGGLE = pong_runner.bin
install: $(LED_TOGGLE)
iceprog $(LED_TOGGLE)
.PHONY: test
test:
iverilog -o test uart_rx_test.v uart_rx.v
vvp test
gtkwave test.vcd
.SUFFIXES: .v .json .bitstream .bin
.v.json:
yosys -q -p "hierarchy -top PongRunner; synth_ice40 -json $*.json" $*.v vga_sync_pulse_generator.v vga_current_beam_position.v vga_add_porches_to_output.v uart_rx.v uart_tx.v pong.v pong_ball.v pong_paddle.v debounce_filter.v
.json.bitstream:
#nextpnr-ice40 --hx1k --freq 25 --pcf $(CONSTRAINTS) --json $*.json --package vq100 --asc $*.bitstream
nextpnr-ice40 --hx1k --freq 25 --pcf $(CONSTRAINTS) --json $*.json --package vq100 --asc $*.bitstream
.bitstream.bin:
icepack $*.bitstream $*.bin

28
README.md Normal file
View File

@ -0,0 +1,28 @@
# Go Board code reworking
This code is a reworking of the code for the [Go Board](https://nandland.com/the-go-board/)
FPGA beginner's board.
The original code comes from two sources:
* [Nandland Go Board tutorials](https://nandland.com/download-and-install-the-fpga-tools-and-drivers/)
* [Getting Started with FPGAs repo](https://github.com/nandland/getting-started-with-fpgas/)
Where possible I tried to:
* use more consistent variable and constant names
* fix missing inferred wires (the Pong project had a lot of these)
* avoid run-on lines of code
* avoid one-liner conditional bodies, opting for `begin...end` blocks everywhere
All of these can be built with [Yosys](https://github.com/YosysHQ/yosys)
and tested with [Icarus Verilog](https://github.com/steveicarus/iverilog). The `Makefile` is one I've been dragging along across all projects. It can fire off both a build-and-install to the Go Board, as well as run the test suite and open up [GTKWave](https://gtkwave.sourceforge.net/)
so you can inspect the signals. You do not need Lattive iCECube2 and you will have a hard time
getting it to run on modern Linux anyway!
`sipo_shift_register_test.sv` shows how to create your own `assert` for Icarus Verilog,
taken from an idea from here: https://stackoverflow.com/a/13906120
Have fun.
John

44
binary_to_7_segment.v Normal file
View File

@ -0,0 +1,44 @@
module Binary_to_7_Segment(
input i_Clk,
input [$clog2(15)-1:0] i_Number,
output o_SegA,
output o_SegB,
output o_SegC,
output o_SegD,
output o_SegE,
output o_SegF,
output o_SegG
);
reg [6:0] r_HexEncoding;
always @(posedge i_Clk) begin
case (i_Number)
0: r_HexEncoding <= 7'b1111110;
1: r_HexEncoding <= 7'b0110000;
2: r_HexEncoding <= 7'b1101101;
3: r_HexEncoding <= 7'b1111001;
4: r_HexEncoding <= 7'b0110011;
5: r_HexEncoding <= 7'b1011011;
6: r_HexEncoding <= 7'b1011111;
7: r_HexEncoding <= 7'b1110000;
8: r_HexEncoding <= 7'b1111111;
9: r_HexEncoding <= 7'b1111011;
10: r_HexEncoding <= 7'b1110111;
11: r_HexEncoding <= 7'b0011111;
12: r_HexEncoding <= 7'b1001110;
13: r_HexEncoding <= 7'b0111101;
14: r_HexEncoding <= 7'b1001111;
15: r_HexEncoding <= 7'b1000111;
default: r_HexEncoding <= 7'b0000000;
endcase
end
assign o_SegA = r_HexEncoding[6];
assign o_SegB = r_HexEncoding[5];
assign o_SegC = r_HexEncoding[4];
assign o_SegD = r_HexEncoding[3];
assign o_SegE = r_HexEncoding[2];
assign o_SegF = r_HexEncoding[1];
assign o_SegG = r_HexEncoding[0];
endmodule

22
count_and_toggle.v Normal file
View File

@ -0,0 +1,22 @@
module Count_And_Toggle (
input i_Clk,
input i_Enable,
output reg o_Toggle
);
parameter COUNT_LIMIT = 10;
reg [$clog2(COUNT_LIMIT - 1):0] r_Counter;
always @(posedge i_Clk) begin
if (i_Enable == 1) begin
if (r_Counter == COUNT_LIMIT - 1) begin
o_Toggle <= !o_Toggle;
r_Counter <= 0;
end else begin
r_Counter <= r_Counter + 1;
end
end else begin
o_Toggle <= 0;
end
end
endmodule

30
debounce_filter.v Normal file
View File

@ -0,0 +1,30 @@
module Debounce_Filter
#(parameter DEBOUNCE_LIMIT = 20) (
input i_Clk,
input i_Bouncy,
output o_Debounced
);
reg [$clog2(DEBOUNCE_LIMIT)-1:0] r_Count = 0;
reg r_State = 1'b0;
always @(posedge i_Clk)
begin
if (i_Bouncy !== r_State && r_Count < DEBOUNCE_LIMIT - 1)
begin
r_Count <= r_Count + 1;
end
else if (r_Count == DEBOUNCE_LIMIT - 1)
begin
r_State <= i_Bouncy;
r_Count <= 0;
end
else
begin
r_Count <= 0;
end
end
assign o_Debounced = r_State;
endmodule

19
debounce_project_top.v Normal file
View File

@ -0,0 +1,19 @@
module Debounce_Project_Top(
input i_Clk,
input i_Switch_1,
output o_LED_1
);
wire w_Debounced_Switch;
Debounce_Filter #(.DEBOUNCE_LIMIT(250000)) Debounce_Inst (
.i_Clk(i_Clk),
.i_Bouncy(i_Switch_1),
.o_Debounced(w_Debounced_Switch)
);
LED_Toggle_Project LED_Toggle_Inst (
.i_Clk(i_Clk),
.i_Switch_1(w_Debounced_Switch),
.o_LED_1(o_LED_1)
);
endmodule

14
demux_1_to_4.v Normal file
View File

@ -0,0 +1,14 @@
module Demux_1_to_4(
input i_Data,
input i_Sel0,
input i_Sel1,
output o_Data0,
output o_Data1,
output o_Data2,
output o_Data3
);
assign o_Data0 = i_Data & !i_Sel0 & !i_Sel1;
assign o_Data1 = i_Data & i_Sel0 & !i_Sel1;
assign o_Data2 = i_Data & !i_Sel0 & i_Sel1;
assign o_Data3 = i_Data & i_Sel0 & i_Sel1;
endmodule

180
fifo.v Normal file
View File

@ -0,0 +1,180 @@
module FIFO(
input i_Clock,
input i_writeDataReady_Flash,
input [WIDTH_BITS-1:0] i_writeData,
input [$clog2(DEPTH_WORDS)-1:0] i_almostFullLevel,
output o_almostFull,
output o_Full,
input i_doRead_Flash,
output reg o_readDataReady,
output reg [WIDTH_BITS-1:0] o_readData,
input [$clog2(DEPTH_WORDS)-1:0] i_almostEmptyLevel,
output o_almostEmpty,
output o_Empty,
input i_Reset_Low
);
parameter WIDTH_BITS = 8;
parameter DEPTH_WORDS = 16;
reg [WIDTH_BITS-1:0] r_RAM [DEPTH_WORDS-1:0];
reg [$clog2(DEPTH_WORDS)-1:0] r_writeAddress;
reg [$clog2(DEPTH_WORDS)-1:0] r_readAddress;
reg [$clog2(DEPTH_WORDS)-1:0] r_Count;
always @(posedge i_Clock or negedge i_Reset_Low) begin
if (~i_Reset_Low) begin
r_writeAddress <= 0;
r_readAddress <= 0;
r_Count <= 0;
end else begin
if (i_writeDataReady_Flash) begin
r_RAM[r_writeAddress] <= i_writeData;
if (r_writeAddress == DEPTH_WORDS - 1) begin
r_writeAddress <= 0;
end else begin
r_writeAddress <= r_writeAddress + 1;
end
end
if (i_doRead_Flash) begin
o_readData <= r_RAM[r_readAddress];
o_readDataReady <= i_doRead_Flash;
if (r_readAddress == DEPTH_WORDS - 1) begin
r_readAddress <= 0;
end else begin
r_readAddress <= r_readAddress + 1;
end
end
if (i_doRead_Flash & ~i_writeDataReady_Flash) begin
if (r_Count != 0) begin
r_Count <= r_Count - 1;
end
end else if (i_writeDataReady_Flash & ~i_doRead_Flash) begin
if (r_Count != DEPTH_WORDS) begin
r_Count <= r_Count + 1;
end
end
end
end
assign o_Full = (
r_Count == DEPTH_WORDS
) || (
r_Count == DEPTH_WORDS-1 &&
i_writeDataReady_Flash &&
!i_doRead_Flash
);
assign o_Empty = (r_Count == 0);
assign o_almostFull = (r_Count > DEPTH_WORDS - i_almostFullLevel);
assign o_almostEmpty = (r_Count < i_almostEmptyLevel);
endmodule
module TestFIFO();
reg r_Clk = 0;
always #1 r_Clk <= !r_Clk;
reg r_Reset_Low = 0;
reg r_writeDataReady_Flash = 0;
reg [7:0] r_writeData = 50;
reg [3:0] r_almostFullLevel = 4;
wire w_almostFull;
wire w_Full;
reg r_doReadFlash = 0;
wire w_readDataReady;
wire [7:0] w_readData;
reg [3:0] r_almostEmptyLevel = 4;
wire w_almostEmpty;
wire w_Empty;
FIFO myFifo(
.i_Clock(r_Clk),
.i_Reset_Low(r_Reset_Low),
.i_writeDataReady_Flash(r_writeDataReady_Flash),
.i_writeData(r_writeData),
.i_almostFullLevel(r_almostFullLevel),
.o_almostFull(w_almostFull),
.o_Full(w_Full),
.i_doRead_Flash(r_doReadFlash),
.o_readDataReady(w_readDataReady),
.o_readData(w_readData),
.i_almostEmptyLevel(r_almostEmptyLevel),
.o_almostEmpty(w_almostEmpty),
.o_Empty(w_Empty)
);
initial begin
$dumpfile("test.vcd");
$dumpvars;
#2;
r_Reset_Low <= 1;
#2;
r_writeDataReady_Flash <= 1;
#2;
r_writeDataReady_Flash <= 0;
#2;
r_writeData = 69;
r_writeDataReady_Flash <= 1;
#2;
r_writeDataReady_Flash <= 0;
#2;
r_writeDataReady_Flash <= 1;
#2;
r_writeDataReady_Flash <= 0;
#2;
r_writeDataReady_Flash <= 1;
#2;
r_writeDataReady_Flash <= 0;
#2;
r_writeDataReady_Flash <= 1;
#2;
r_writeDataReady_Flash <= 0;
#2;
r_writeDataReady_Flash <= 1;
#2;
r_writeDataReady_Flash <= 0;
#2;
r_doReadFlash <= 1;
#2;
r_doReadFlash <= 0;
#2;
r_doReadFlash <= 1;
#2;
r_doReadFlash <= 0;
#2;
$finish;
end
endmodule

20
led_toggle.v Normal file
View File

@ -0,0 +1,20 @@
module LED_Toggle_Project(
input i_Clk,
input i_Switch_1,
output o_LED_1
);
reg r_LED_1 = 1'b0;
reg r_Switch_1 = 1'b0;
always @(posedge i_Clk)
begin
r_Switch_1 <= i_Switch_1;
if (i_Switch_1 == 1'b0 && r_Switch_1 == 1'b1)
begin
r_LED_1 <= ~r_LED_1;
end
end
assign o_LED_1 = r_LED_1;
endmodule

18
lfsr_22.v Normal file
View File

@ -0,0 +1,18 @@
module LFSR(
input i_Clk,
output [SIZE-1:0] o_LFSR_Data,
output o_LFSR_Done
);
parameter SIZE = 22;
reg [SIZE-1:0] r_LFSR;
wire w_XNOR;
always @(posedge i_Clk) begin
r_LFSR <= {r_LFSR[SIZE-2:0], w_XNOR};
end
assign w_XNOR = r_LFSR[SIZE-1] ^~ r_LFSR[SIZE-2];
assign o_LFSR_Done = (r_LFSR == 0);
assign o_LFSR_Data = r_LFSR;
endmodule

181
pong.v Normal file
View File

@ -0,0 +1,181 @@
module Pong(
input i_Clk,
input i_HSync,
input i_VSync,
input i_GameStart,
input i_Player1_Up,
input i_Player1_Down,
input i_Player2_Up,
input i_Player2_Down,
output reg o_HSync,
output reg o_VSync,
output [2:0] o_Red,
output [2:0] o_Green,
output [2:0] o_Blue
);
parameter TOTAL_COLUMNS = 800;
parameter TOTAL_ROWS = 525;
parameter ACTIVE_COLUMNS = 640;
parameter ACTIVE_ROWS = 480;
localparam SCREEN_WIDTH_CELLS = 40;
localparam SCREEN_HEIGHT_CELLS = 30;
localparam SCORE_LIMIT = 9;
localparam PADDLE_HEIGHT = 6;
localparam PADDLE_PLAYER1_X = 0;
localparam PADDLE_PLAYER2_X = SCREEN_WIDTH_CELLS - 1;
localparam STATE_IDLE = 0;
localparam STATE_RUNNING = 1;
localparam STATE_P1_WINNER = 2;
localparam STATE_P2_WINNER = 3;
localparam STATE_CLEANUP = 4;
reg [2:0] r_currentState = STATE_IDLE;
wire w_HSync, w_VSync;
wire [9:0] w_X, w_Y;
wire w_P1_DoDrawPaddle, w_P2_DoDrawPaddle;
wire [5:0] w_P1_PaddleY, w_P2_PaddleY;
wire w_DoDrawBall, w_DrawAny;
wire [5:0] w_BallX, w_BallY;
wire w_IsGameActive;
reg [3:0] r_P1_Score = 0;
reg [3:0] r_P2_Score = 0;
wire [5:0] w_CellX, w_CellY;
VGA_Current_Beam_Position #(
.TOTAL_COLUMNS(TOTAL_COLUMNS),
.TOTAL_ROWS(TOTAL_ROWS)
) CurrentPosition (
.i_Clk(i_Clk),
.i_HSync(i_HSync),
.i_VSync(i_VSync),
.o_HSync(w_HSync),
.o_VSync(w_VSync),
.o_X(w_X),
.o_Y(w_Y)
);
always @(posedge i_Clk) begin
o_HSync <= w_HSync;
o_VSync <= w_VSync;
end
// right shift 4
assign w_CellX = w_X[9:4];
assign w_CellY = w_Y[9:4];
// paddles
PongPaddle #(
.X_POS_CELLS(PADDLE_PLAYER1_X),
.SCREEN_HEIGHT_CELLS(SCREEN_HEIGHT_CELLS),
.PADDLE_HEIGHT_CELLS(PADDLE_HEIGHT)
) P1_Paddle (
.i_Clk(i_Clk),
.i_CellX(w_CellX),
.i_CellY(w_CellY),
.i_PaddleUp(i_Player1_Up),
.i_PaddleDown(i_Player1_Down),
.o_DoDrawPaddle(w_P1_DoDrawPaddle),
.o_PaddleY(w_P1_PaddleY)
);
PongPaddle #(
.X_POS_CELLS(PADDLE_PLAYER2_X),
.SCREEN_HEIGHT_CELLS(SCREEN_HEIGHT_CELLS),
.PADDLE_HEIGHT_CELLS(PADDLE_HEIGHT)
) P2_Paddle (
.i_Clk(i_Clk),
.i_CellX(w_CellX),
.i_CellY(w_CellY),
.i_PaddleUp(i_Player2_Up),
.i_PaddleDown(i_Player2_Down),
.o_DoDrawPaddle(w_P2_DoDrawPaddle),
.o_PaddleY(w_P2_PaddleY)
);
PongBall #(
.SCREEN_WIDTH_CELLS(SCREEN_WIDTH_CELLS),
.SCREEN_HEIGHT_CELLS(SCREEN_HEIGHT_CELLS)
) Ball (
.i_Clk(i_Clk),
.i_IsGameActive(w_IsGameActive),
.i_CellX(w_CellX),
.i_CellY(w_CellY),
.o_DoDrawBall(w_DoDrawBall),
.o_BallX(w_BallX),
.o_BallY(w_BallY)
);
always @(posedge i_Clk) begin
case (r_currentState)
STATE_IDLE:
begin
if (i_GameStart) begin
r_currentState <= STATE_RUNNING;
end
end
STATE_RUNNING:
begin
if (
w_BallX == 0 && (
w_BallY < w_P1_PaddleY ||
w_BallY > w_P1_PaddleY + PADDLE_HEIGHT
)
) begin
r_currentState <= STATE_P1_WINNER;
end else if (
w_BallX == SCREEN_WIDTH_CELLS - 1 && (
w_BallY < w_P2_PaddleY ||
w_BallY > w_P2_PaddleY + PADDLE_HEIGHT
)
) begin
r_currentState <= STATE_P2_WINNER;
end
end
STATE_P1_WINNER:
begin
if (r_P1_Score == SCORE_LIMIT - 1) begin
r_P1_Score <= 0;
end else begin
r_P1_Score <= r_P1_Score + 1;
r_currentState <= STATE_CLEANUP;
end
end
STATE_P2_WINNER:
begin
if (r_P2_Score == SCORE_LIMIT - 1) begin
r_P2_Score <= 0;
end else begin
r_P2_Score <= r_P2_Score + 1;
r_currentState <= STATE_CLEANUP;
end
end
STATE_CLEANUP:
begin
r_currentState <= STATE_IDLE;
end
endcase
end
assign w_IsGameActive = (r_currentState == STATE_RUNNING) ? 1 : 0;
assign w_DrawAny = w_DoDrawBall | w_P1_DoDrawPaddle | w_P2_DoDrawPaddle;
assign o_Red = w_DrawAny ? 7 : 0;
assign o_Green = w_DrawAny ? 7 : 0;
assign o_Blue = w_DrawAny ? 7 : 0;
endmodule

70
pong_ball.v Normal file
View File

@ -0,0 +1,70 @@
module PongBall (
input i_Clk,
input i_IsGameActive,
input [5:0] i_CellX,
input [5:0] i_CellY,
output reg o_DoDrawBall,
output reg [5:0] o_BallX,
output reg [5:0] o_BallY
);
parameter SCREEN_WIDTH_CELLS = 40;
parameter SCREEN_HEIGHT_CELLS = 30;
parameter CLOCKS_PER_SECOND = 25000000;
parameter BALL_MOVE_TIME_MS = 50;
parameter BALL_SPEED = 1250000;
reg [$clog2(BALL_SPEED)-1:0] r_ballMoveCount;
reg [5:0] r_PrevBallX;
reg [5:0] r_PrevBallY;
always @(posedge i_Clk) begin
if (!i_IsGameActive) begin
o_BallX <= SCREEN_WIDTH_CELLS / 2;
o_BallY <= SCREEN_HEIGHT_CELLS / 2;
r_PrevBallX <= SCREEN_WIDTH_CELLS / 2 + 1;
r_PrevBallY <= SCREEN_HEIGHT_CELLS / 2 - 2;
end else begin
if (r_ballMoveCount < BALL_SPEED) begin
r_ballMoveCount <= r_ballMoveCount + 1;
end else begin
r_ballMoveCount <= 0;
r_PrevBallX <= o_BallX;
r_PrevBallY <= o_BallY;
if (
// at right edge, travelling right
(r_PrevBallX < o_BallX && o_BallX == SCREEN_WIDTH_CELLS - 1) ||
// not at left edge
(o_BallX < r_PrevBallX && o_BallX != 0)
) begin
o_BallX <= o_BallX - 1;
end else begin
o_BallX <= o_BallX + 1;
end
if (
// at bottom edge, travelling up
(r_PrevBallY < o_BallY && o_BallY == SCREEN_HEIGHT_CELLS - 1) ||
// not at left edge
(o_BallY < r_PrevBallY && o_BallY != 0)
) begin
o_BallY <= o_BallY - 1;
end else begin
o_BallY <= o_BallY + 1;
end
end
end
end
always @(posedge i_Clk) begin
if (i_CellX == o_BallX && i_CellY == o_BallY) begin
o_DoDrawBall <= 1;
end else begin
o_DoDrawBall <= 0;
end
end
endmodule

59
pong_paddle.v Normal file
View File

@ -0,0 +1,59 @@
/**
* Paddles maintain their own location on the screen
*/
module PongPaddle (
input i_Clk,
input [5:0] i_CellX,
input [5:0] i_CellY,
input i_PaddleUp,
input i_PaddleDown,
output reg o_DoDrawPaddle,
output reg [5:0] o_PaddleY
);
parameter X_POS_CELLS = 0;
parameter PADDLE_HEIGHT_CELLS = 6;
parameter SCREEN_HEIGHT_CELLS = 30;
parameter CLOCKS_PER_SECOND = 25000000;
parameter PADDLE_MOVE_TIME_MS = 50;
// omg i can't get this
parameter integer PADDLE_SPEED = 1250000;
reg [$clog2(PADDLE_SPEED)-1:0] r_paddleMoveCount;
wire w_onlyOneButtonDown = i_PaddleUp ^ i_PaddleDown;
always @(posedge i_Clk) begin
// handle end of move delay
if (w_onlyOneButtonDown) begin
if (r_paddleMoveCount == PADDLE_SPEED) begin
r_paddleMoveCount <= 0;
end else begin
r_paddleMoveCount <= r_paddleMoveCount + 1;
end
end
if (
i_PaddleUp &&
r_paddleMoveCount == PADDLE_SPEED &&
o_PaddleY > 0
) begin
o_PaddleY <= o_PaddleY - 1;
end else if (
i_PaddleDown &&
r_paddleMoveCount == PADDLE_SPEED &&
o_PaddleY < SCREEN_HEIGHT_CELLS - PADDLE_HEIGHT_CELLS - 1
) begin
o_PaddleY <= o_PaddleY + 1;
end
if (
i_CellX == X_POS_CELLS &&
i_CellY >= o_PaddleY &&
i_CellY <= o_PaddleY + PADDLE_HEIGHT_CELLS) begin
o_DoDrawPaddle <= 1;
end else begin
o_DoDrawPaddle <= 0;
end
end
endmodule

142
pong_runner.v Normal file
View File

@ -0,0 +1,142 @@
module PongRunner (
input i_Clk,
input i_UART_RX,
input i_Switch_1,
input i_Switch_2,
input i_Switch_3,
input i_Switch_4,
output o_VGA_HSync,
output o_VGA_VSync,
output o_VGA_Red_0,
output o_VGA_Red_1,
output o_VGA_Red_2,
output o_VGA_Grn_0,
output o_VGA_Grn_1,
output o_VGA_Grn_2,
output o_VGA_Blu_0,
output o_VGA_Blu_1,
output o_VGA_Blu_2
);
localparam TOTAL_COLUMNS = 800;
localparam TOTAL_ROWS = 525;
localparam ACTIVE_COLUMNS = 640;
localparam ACTIVE_ROWS = 480;
localparam VIDEO_WIDTH = 3;
wire [VIDEO_WIDTH-1:0] w_Red_FromPong, w_Red_FromPorchSync;
wire [VIDEO_WIDTH-1:0] w_Green_FromPong, w_Green_FromPorchSync;
wire [VIDEO_WIDTH-1:0] w_Blue_FromPong, w_Blue_FromPorchSync;
wire w_dataReady;
wire w_HSync_Start, w_VSync_Start;
wire w_HSync_FromPong, w_VSync_FromPong;
wire w_HSync_FromPorchSync, w_VSync_FromPorchSync;
wire w_Switch_1_Debounced,
w_Switch_2_Debounced,
w_Switch_3_Debounced,
w_Switch_4_Debounced;
UART_RX #(
.CLOCKS_PER_SECOND(25000000),
.BAUD_RATE(115200)
) MyUART_RX (
.i_Clk(i_Clk),
.i_ReceiveBit(i_UART_RX),
.o_DataReady(w_dataReady),
.o_DataByte()
);
VGA_Sync_Pulse_Generator #(
.TOTAL_COLUMNS(TOTAL_COLUMNS),
.TOTAL_ROWS(TOTAL_ROWS),
.ACTIVE_COLUMNS(ACTIVE_COLUMNS),
.ACTIVE_ROWS(ACTIVE_ROWS)
) SyncGenerator (
.i_Clk(i_Clk),
.o_HSync(w_HSync_Start),
.o_VSync(w_VSync_Start),
.o_rawX(),
.o_rawY()
);
Debounce_Filter #(
.DEBOUNCE_LIMIT(25000000 / 25000)
) Debounce_Switch_1 (
.i_Clk(i_Clk),
.i_Bouncy(i_Switch_1),
.o_Debounced(w_Switch_1_Debounced)
);
Debounce_Filter #(
.DEBOUNCE_LIMIT(25000000 / 25000)
) Debounce_Switch_2 (
.i_Clk(i_Clk),
.i_Bouncy(i_Switch_2),
.o_Debounced(w_Switch_2_Debounced)
);
Debounce_Filter #(
.DEBOUNCE_LIMIT(25000000 / 25000)
) Debounce_Switch_3 (
.i_Clk(i_Clk),
.i_Bouncy(i_Switch_3),
.o_Debounced(w_Switch_3_Debounced)
);
Debounce_Filter #(
.DEBOUNCE_LIMIT(25000000 / 25000)
) Debounce_Switch_4 (
.i_Clk(i_Clk),
.i_Bouncy(i_Switch_4),
.o_Debounced(w_Switch_4_Debounced)
);
Pong #() MyPong (
.i_Clk(i_Clk),
.i_HSync(w_HSync_Start),
.i_VSync(w_VSync_Start),
.i_GameStart(w_dataReady),
.i_Player1_Up(w_Switch_1_Debounced),
.i_Player1_Down(w_Switch_2_Debounced),
.i_Player2_Up(w_Switch_3_Debounced),
.i_Player2_Down(w_Switch_4_Debounced),
.o_HSync(w_HSync_FromPong),
.o_VSync(w_VSync_FromPong),
.o_Red(w_Red_FromPong),
.o_Green(w_Green_FromPong),
.o_Blue(w_Blue_FromPong)
);
VGA_Add_Porches_To_Output #() PorchOutput (
.i_Clk(i_Clk),
.i_HSync(w_HSync_FromPong),
.i_VSync(w_VSync_FromPong),
.i_Red(w_Red_FromPong),
.i_Green(w_Green_FromPong),
.i_Blue(w_Blue_FromPong),
.o_HSync(w_HSync_FromPorchSync),
.o_VSync(w_VSync_FromPorchSync),
.o_Red(w_Red_FromPorchSync),
.o_Green(w_Green_FromPorchSync),
.o_Blue(w_Blue_FromPorchSync)
);
assign o_VGA_HSync = w_HSync_FromPorchSync;
assign o_VGA_VSync = w_VSync_FromPorchSync;
assign o_VGA_Red_0 = w_Red_FromPorchSync[0];
assign o_VGA_Red_1 = w_Red_FromPorchSync[1];
assign o_VGA_Red_2 = w_Red_FromPorchSync[2];
assign o_VGA_Grn_0 = w_Green_FromPorchSync[0];
assign o_VGA_Grn_1 = w_Green_FromPorchSync[1];
assign o_VGA_Grn_2 = w_Green_FromPorchSync[2];
assign o_VGA_Blu_0 = w_Blue_FromPorchSync[0];
assign o_VGA_Blu_1 = w_Blue_FromPorchSync[1];
assign o_VGA_Blu_2 = w_Blue_FromPorchSync[2];
endmodule

77
ram.v Normal file
View File

@ -0,0 +1,77 @@
module RAM(
input i_writeClock,
input [$clog2(DEPTH_WORDS - 1):0] i_writeAddress,
input [WIDTH_BITS-1:0] i_writeData,
input i_writeDataValid,
input i_readClock,
input [$clog2(DEPTH_WORDS - 1):0] i_readAddress,
input i_doRead,
output reg o_dataValid,
output reg [WIDTH_BITS-1:0] o_readData
);
parameter WIDTH_BITS = 8;
parameter DEPTH_WORDS = 16;
reg [WIDTH_BITS-1:0] r_Memory[DEPTH_WORDS-1:0];
always @(posedge i_writeClock) begin
if (i_writeDataValid) begin
r_Memory[i_writeAddress] <= i_writeData;
end
end
always @(posedge i_readClock) begin
o_readData <= r_Memory[i_readAddress];
o_dataValid <= i_doRead;
end
endmodule
module TestRAM();
reg r_Clk = 0;
always #2 r_Clk <= !r_Clk;
reg [4:0] r_writeAddress = 1;
reg [7:0] r_writeData = 50;
reg r_writeDataValid = 0;
reg [4:0] r_readAddress = 1;
wire [7:0] w_readData;
reg r_doRead = 0;
wire w_dataValid;
RAM myRam (
.i_writeClock(r_Clk),
.i_writeAddress(r_writeAddress),
.i_writeData(r_writeData),
.i_writeDataValid(r_writeDataValid),
.i_readClock(r_Clk),
.i_readAddress(r_readAddress),
.i_doRead(r_doRead),
.o_dataValid(w_dataValid),
.o_readData(w_readData)
);
initial begin
$dumpfile("test.vcd");
$dumpvars;
#2;
r_writeDataValid <= 1;
#2;
r_writeDataValid <= 0;
#2;
r_doRead <= 1;
#2;
$finish;
end
endmodule

103
simon.v Normal file
View File

@ -0,0 +1,103 @@
module Simon(
input i_Clk,
input i_Switch_1,
input i_Switch_2,
input i_Switch_3,
input i_Switch_4,
output o_LED_1,
output o_LED_2,
output o_LED_3,
output o_LED_4,
output o_Segment2_A,
output o_Segment2_B,
output o_Segment2_C,
output o_Segment2_D,
output o_Segment2_E,
output o_Segment2_F,
output o_Segment2_G
);
localparam GAME_LIMIT = 7;
localparam CLKS_PER_SECOND = 25000000;
localparam integer DEBOUNCE_LIMIT = CLKS_PER_SECOND * 0.001;
wire w_Switch_1, w_Switch_2, w_Switch_3, w_Switch_4;
wire [3:0] w_Score;
wire w_Segment2_A,
w_Segment2_B,
w_Segment2_C,
w_Segment2_D,
w_Segment2_E,
w_Segment2_F,
w_Segment2_G;
Debounce_Filter #(
.DEBOUNCE_LIMIT(DEBOUNCE_LIMIT)
) Debounce_Switch_1 (
.i_Clk(i_Clk),
.i_Bouncy(i_Switch_1),
.o_Debounced(w_Switch_1)
);
Debounce_Filter #(
.DEBOUNCE_LIMIT(DEBOUNCE_LIMIT)
) Debounce_Switch_2 (
.i_Clk(i_Clk),
.i_Bouncy(i_Switch_2),
.o_Debounced(w_Switch_2)
);
Debounce_Filter #(
.DEBOUNCE_LIMIT(DEBOUNCE_LIMIT)
) Debounce_Switch_3 (
.i_Clk(i_Clk),
.i_Bouncy(i_Switch_3),
.o_Debounced(w_Switch_3)
);
Debounce_Filter #(
.DEBOUNCE_LIMIT(DEBOUNCE_LIMIT)
) Debounce_Switch_4 (
.i_Clk(i_Clk),
.i_Bouncy(i_Switch_4),
.o_Debounced(w_Switch_4)
);
Simon_Game #(
.CLKS_PER_SECOND(CLKS_PER_SECOND),
.GAME_LIMIT(GAME_LIMIT)
) Simon_Game_Instance (
.i_Clk(i_Clk),
.i_Switch_1(w_Switch_1),
.i_Switch_2(w_Switch_2),
.i_Switch_3(w_Switch_3),
.i_Switch_4(w_Switch_4),
.o_Score(w_Score),
.o_LED_1(o_LED_1),
.o_LED_2(o_LED_2),
.o_LED_3(o_LED_3),
.o_LED_4(o_LED_4)
);
Binary_to_7_Segment Scoreboard (
.i_Clk(i_Clk),
.i_Number(w_Score),
.o_SegA(w_Segment2_A),
.o_SegB(w_Segment2_B),
.o_SegC(w_Segment2_C),
.o_SegD(w_Segment2_D),
.o_SegE(w_Segment2_E),
.o_SegF(w_Segment2_F),
.o_SegG(w_Segment2_G)
);
assign o_Segment2_A = !w_Segment2_A;
assign o_Segment2_B = !w_Segment2_B;
assign o_Segment2_C = !w_Segment2_C;
assign o_Segment2_D = !w_Segment2_D;
assign o_Segment2_E = !w_Segment2_E;
assign o_Segment2_F = !w_Segment2_F;
assign o_Segment2_G = !w_Segment2_G;
endmodule

208
simon_game.v Normal file
View File

@ -0,0 +1,208 @@
module Simon_Game (
input i_Clk,
input i_Switch_1,
input i_Switch_2,
input i_Switch_3,
input i_Switch_4,
output reg [3:0] o_Score,
output o_LED_1,
output o_LED_2,
output o_LED_3,
output o_LED_4
);
parameter CLKS_PER_SECOND = 25000000;
parameter GAME_LIMIT = 6;
localparam START = 3'd0;
localparam PATTERN_OFF = 3'd1;
localparam PATTERN_SHOW = 3'd2;
localparam WAIT_PLAYER = 3'd3;
localparam INCREMENT_SCORE = 3'd4;
localparam PLAYER_LOST = 3'd5;
localparam PLAYER_WON = 3'd6;
reg [2:0] r_currentState;
// count and toggle results
wire w_IncomingDelay;
reg r_DelayCompleted;
wire w_EnableCounter;
// user input handling
reg r_Switch_1, r_Switch_2, r_Switch_3, r_Switch_4;
reg r_Button_notDown;
reg [1:0] r_currentButtonDown;
// game pattern & construction
reg [1:0] r_LEDPattern [0:10];
wire [21:0] w_LFSR_Data;
reg [$clog2(GAME_LIMIT)-1:0] r_currentLEDPattern;
always @(posedge i_Clk) begin
if (i_Switch_1 & i_Switch_2) begin
r_currentState <= START;
end else begin
case (r_currentState)
START:
begin
if (!i_Switch_1 & !i_Switch_2 & r_Button_notDown) begin
o_Score <= 0;
r_currentLEDPattern <= 0;
r_currentState <= PATTERN_OFF;
end
end
PATTERN_OFF:
begin
if (!w_IncomingDelay & r_DelayCompleted) begin
r_currentState <= PATTERN_SHOW;
end
end
PATTERN_SHOW:
begin
if (!w_IncomingDelay & r_DelayCompleted) begin
// this limits the number of
if (o_Score == r_currentLEDPattern) begin
r_currentLEDPattern <= 0;
r_currentState <= WAIT_PLAYER;
end else begin
r_currentLEDPattern <= r_currentLEDPattern + 1;
r_currentState <= PATTERN_OFF;
end
end
end
WAIT_PLAYER:
begin
if (r_Button_notDown) begin
if (
r_LEDPattern[r_currentLEDPattern] == r_currentButtonDown &&
o_Score == r_currentLEDPattern
) begin
r_currentLEDPattern <= 0;
r_currentState <= INCREMENT_SCORE;
end else if (
r_LEDPattern[r_currentLEDPattern] != r_currentButtonDown
) begin
r_currentState <= PLAYER_LOST;
end else begin
r_currentLEDPattern <= r_currentLEDPattern + 1;
end
end
end
INCREMENT_SCORE:
begin
o_Score <= o_Score + 1;
if (o_Score == GAME_LIMIT - 1) begin
r_currentState <= PLAYER_WON;
end else begin
r_currentState <= PATTERN_OFF;
end
end
PLAYER_WON:
begin
o_Score <= 4'hA;
end
PLAYER_LOST:
begin
o_Score <= 4'hF;
end
default:
begin
r_currentState <= START;
end
endcase
end
end
always @(posedge i_Clk) begin
if (r_currentState == START) begin
r_LEDPattern[0] <= w_LFSR_Data[1:0];
r_LEDPattern[1] <= w_LFSR_Data[3:2];
r_LEDPattern[2] <= w_LFSR_Data[5:4];
r_LEDPattern[3] <= w_LFSR_Data[7:6];
r_LEDPattern[4] <= w_LFSR_Data[9:8];
r_LEDPattern[5] <= w_LFSR_Data[11:10];
r_LEDPattern[6] <= w_LFSR_Data[13:12];
r_LEDPattern[7] <= w_LFSR_Data[15:14];
r_LEDPattern[8] <= w_LFSR_Data[17:16];
r_LEDPattern[9] <= w_LFSR_Data[19:18];
r_LEDPattern[10] <= w_LFSR_Data[21:20];
end
end
assign o_LED_1 = (
(r_currentState == PATTERN_SHOW && r_LEDPattern[r_currentLEDPattern] == 0) ?
1 :
i_Switch_1
);
assign o_LED_2 = (
(r_currentState == PATTERN_SHOW && r_LEDPattern[r_currentLEDPattern] == 1) ?
1 :
i_Switch_2
);
assign o_LED_3 = (
(r_currentState == PATTERN_SHOW && r_LEDPattern[r_currentLEDPattern] == 2) ?
1 :
i_Switch_3
);
assign o_LED_4 = (
(r_currentState == PATTERN_SHOW && r_LEDPattern[r_currentLEDPattern] == 3) ?
1 :
i_Switch_4
);
always @(posedge i_Clk) begin
r_DelayCompleted <= w_IncomingDelay;
r_Switch_1 <= i_Switch_1;
r_Switch_2 <= i_Switch_2;
r_Switch_3 <= i_Switch_3;
r_Switch_4 <= i_Switch_4;
if (r_Switch_1 && !i_Switch_1) begin
r_Button_notDown <= 1;
r_currentButtonDown <= 0;
end else if (r_Switch_2 && !i_Switch_2) begin
r_Button_notDown <= 1;
r_currentButtonDown <= 1;
end else if (r_Switch_3 && !i_Switch_3) begin
r_Button_notDown <= 1;
r_currentButtonDown <= 2;
end else if (r_Switch_4 && !i_Switch_4) begin
r_Button_notDown <= 1;
r_currentButtonDown <= 3;
end else begin
r_Button_notDown <= 0;
r_currentButtonDown <= 0;
end
end
assign w_EnableCounter = (r_currentState == PATTERN_SHOW || r_currentState == PATTERN_OFF);
Count_And_Toggle #(
.COUNT_LIMIT(CLKS_PER_SECOND/4)
) Counter (
.i_Clk(i_Clk),
.i_Enable(w_EnableCounter),
.o_Toggle(w_IncomingDelay)
);
LFSR #(
.SIZE(22)
) MyLFSR (
.i_Clk(i_Clk),
.o_LFSR_Data(w_LFSR_Data),
.o_LFSR_Done()
);
endmodule

104
sipo_shift_register_test.sv Normal file
View File

@ -0,0 +1,104 @@
module SiPo_ShiftRegister_4(
input i_Clk,
input i_Latch,
input i_Data,
input i_Reset,
output o_Data0,
output o_Data1,
output o_Data2,
output o_Data3
);
reg [3:0] r_Data;
reg [3:0] r_Incoming;
reg [2:0] r_Pos;
reg r_didLatch;
always @ (posedge i_Clk & i_Reset) begin
r_Data <= 0;
r_Incoming <= 0;
r_Pos <= 0;
r_didLatch <= 0;
end
always @ (posedge i_Clk) begin
if (i_Latch & !r_didLatch) begin
r_Incoming[0] <= i_Data;
r_Incoming[3:1] <= r_Incoming[2:0];
r_Pos <= r_Pos + 1;
r_didLatch <= 1;
end
if (r_didLatch & !i_Latch) begin
r_didLatch <= 0;
if (r_Pos == 4) begin
r_Data[3:0] <= r_Incoming[3:0];
r_Incoming <= 0;
r_Pos <= 0;
end
end
end
assign o_Data0 = r_Data[0];
assign o_Data1 = r_Data[1];
assign o_Data2 = r_Data[2];
assign o_Data3 = r_Data[3];
endmodule
module Test_SIPO();
task assert(input condition);
if (!condition) $error;
endtask
reg r_Data = 1'b0, r_Clk = 1'b0, r_Latch = 1'b0, r_Reset = 1'b0;
wire w_Data0, w_Data1, w_Data2, w_Data3;
always #2 r_Clk <= !r_Clk;
SiPo_ShiftRegister_4 UUT(
.i_Clk(r_Clk),
.i_Data(r_Data),
.i_Latch(r_Latch),
.i_Reset(r_Reset),
.o_Data0(w_Data0),
.o_Data1(w_Data1),
.o_Data2(w_Data2),
.o_Data3(w_Data3)
);
initial begin
$dumpfile("test.vcd");
$dumpvars;
r_Data <= 1'b1;
r_Reset <= 1'b1;
@(posedge r_Clk);
r_Reset <= 1'b0;
@(posedge r_Clk);
r_Latch <= 1'b1;
@(posedge r_Clk);
r_Latch <= 1'b0;
@(posedge r_Clk);
r_Data <= 0;
r_Latch <= 1'b1;
@(posedge r_Clk);
r_Latch <= 1'b0;
@(posedge r_Clk);
r_Latch <= 1'b1;
@(posedge r_Clk);
r_Latch <= 1'b0;
@(posedge r_Clk);
r_Data <= 1;
r_Latch <= 1'b1;
@(posedge r_Clk);
r_Latch <= 1'b0;
@(posedge r_Clk);
#5;
$finish();
end
endmodule

100
uart_rx.v Normal file
View File

@ -0,0 +1,100 @@
module UART_RX (
input i_Clk,
input i_ReceiveBit,
output o_DataReady,
output [7:0] o_DataByte
);
parameter CLOCKS_PER_SECOND = 25000000;
parameter BAUD_RATE = 115200;
localparam integer CLOCKS_PER_BIT = CLOCKS_PER_SECOND / BAUD_RATE;
localparam integer HALF_CLOCKS_PER_BIT = (CLOCKS_PER_BIT - 1) / 2;
localparam IDLE = 0;
localparam VERIFY_START_BIT = 1;
localparam READ_DATA_BITS = 2;
localparam WAIT_FOR_STOP_BIT = 3;
localparam CLEANUP = 4;
// golang-style: lowercase is private, uppercase is public
reg [$clog2(CLEANUP)-1:0] r_currentState = IDLE;
reg r_dataReady = 0;
reg [$clog2(CLOCKS_PER_BIT)-1:0] r_clockCount = 0;
reg [$clog2(8)-1:0] r_bitIndex = 0;
reg [7:0] r_receivedByte = 0;
always @(posedge i_Clk) begin
case (r_currentState)
IDLE:
begin
r_dataReady <= 0;
r_clockCount <= 0;
r_bitIndex <= 0;
if (i_ReceiveBit == 0) begin
r_currentState <= VERIFY_START_BIT;
end
end
// Check the middle of the start bit to make sure it's still low
VERIFY_START_BIT:
begin
if (r_clockCount == HALF_CLOCKS_PER_BIT) begin
// still low, let's go
if (i_ReceiveBit == 0) begin
r_clockCount <= 0;
r_currentState <= READ_DATA_BITS;
end else begin
r_currentState <= IDLE;
end
end else begin
r_clockCount <= r_clockCount + 1;
end
end
// note that we are still in the halfway points of the signal at this
// point. we are sampling the very middles.
READ_DATA_BITS:
begin
if (r_clockCount < CLOCKS_PER_BIT - 1) begin
r_clockCount <= r_clockCount + 1;
end else begin
r_clockCount <= 0;
r_receivedByte[r_bitIndex] <= i_ReceiveBit;
if (r_bitIndex == 7) begin
r_bitIndex <= 0;
r_currentState <= WAIT_FOR_STOP_BIT;
end else begin
r_bitIndex <= r_bitIndex + 1;
end
end
end
WAIT_FOR_STOP_BIT:
begin
if (r_clockCount < CLOCKS_PER_BIT - 1) begin
r_clockCount <= r_clockCount + 1;
end else begin
r_dataReady <= 1;
r_clockCount <= 0;
r_currentState <= CLEANUP;
end
end
CLEANUP:
begin
r_currentState <= IDLE;
r_dataReady <= 0;
end
default:
begin
r_currentState <= IDLE;
end
endcase
end
assign o_DataByte = r_receivedByte;
assign o_DataReady = r_dataReady;
endmodule

98
uart_tx.v Normal file
View File

@ -0,0 +1,98 @@
module UART_TX (
input i_Reset_Low,
input i_Clk,
input i_TransmitReady,
input [7:0] i_TransmitByte,
output reg o_Active,
output reg o_Output,
output reg o_Done
);
parameter CLOCKS_PER_SECOND = 25000000;
parameter BAUD_RATE = 115200;
localparam integer CLOCKS_PER_BIT = CLOCKS_PER_SECOND / BAUD_RATE;
localparam IDLE = 0;
localparam SEND_START_BIT = 1;
localparam SEND_DATA_BITS = 2;
localparam SEND_STOP_BIT = 3;
reg [$clog2(SEND_STOP_BIT)-1:0] r_currentState = IDLE;
reg [$clog2(CLOCKS_PER_BIT)-1:0] r_clockCount = 0;
reg [$clog2(8)-1:0] r_bitIndex = 0;
reg [7:0] r_transmitData;
always @(posedge i_Clk or negedge i_Reset_Low) begin
if (!i_Reset_Low) begin
r_currentState <= IDLE;
end else begin
o_Done <= 0;
case (r_currentState)
IDLE:
begin
// Drive line high for idle
o_Output <= 1;
r_clockCount <= 0;
r_bitIndex <= 0;
if (i_TransmitReady) begin
o_Active <= 1;
r_transmitData <= i_TransmitByte;
r_currentState <= SEND_START_BIT;
end
end
SEND_START_BIT:
begin
o_Output <= 0;
if (r_clockCount < CLOCKS_PER_BIT - 1) begin
r_clockCount <= r_clockCount + 1;
end else begin
r_clockCount <= 0;
r_currentState <= SEND_DATA_BITS;
end
end
SEND_DATA_BITS:
begin
o_Output <= r_transmitData[r_bitIndex];
if (r_clockCount < CLOCKS_PER_BIT - 1) begin
r_clockCount <= r_clockCount + 1;
end else begin
r_clockCount <= 0;
if (r_bitIndex == 7) begin
r_bitIndex <= 0;
r_currentState <= SEND_STOP_BIT;
end else begin
r_bitIndex <= r_bitIndex + 1;
end
end
end
SEND_STOP_BIT:
begin
o_Output <= 1;
if (r_clockCount < CLOCKS_PER_BIT - 1) begin
r_clockCount <= r_clockCount + 1;
end else begin
o_Done <= 1;
r_clockCount <= 0;
r_currentState <= IDLE;
o_Output <= 0;
end
end
default:
begin
r_currentState <= IDLE;
end
endcase
end
end
endmodule

View File

@ -0,0 +1,91 @@
/**
* The output is buffered as the front/back porches are added, so watch your
* timing for other stuff.
*/
module VGA_Add_Porches_To_Output(
input i_Clk,
input i_HSync,
input i_VSync,
input [VIDEO_WIDTH-1:0] i_Red,
input [VIDEO_WIDTH-1:0] i_Green,
input [VIDEO_WIDTH-1:0] i_Blue,
output reg o_HSync,
output reg o_VSync,
output reg [VIDEO_WIDTH-1:0] o_Red,
output reg [VIDEO_WIDTH-1:0] o_Green,
output reg [VIDEO_WIDTH-1:0] o_Blue
);
parameter VIDEO_WIDTH = 3;
parameter TOTAL_COLUMNS = 800;
parameter TOTAL_ROWS = 525;
parameter ACTIVE_COLUMNS = 640;
parameter ACTIVE_ROWS = 480;
//parameter FRONT_PORCH_COUNT_X = 18;
parameter FRONT_PORCH_COUNT_X = 15;
//parameter FRONT_PORCH_COUNT_Y = 10;
parameter FRONT_PORCH_COUNT_Y = 10;
//parameter BACK_PORCH_COUNT_X = 50;
parameter BACK_PORCH_COUNT_X = 53;
//parameter BACK_PORCH_COUNT_Y = 33;
parameter BACK_PORCH_COUNT_Y = 33;
wire w_HSync, w_VSync;
wire [$clog2(TOTAL_COLUMNS)-1:0] w_X;
wire [$clog2(TOTAL_ROWS)-1:0] w_Y;
reg [VIDEO_WIDTH-1:0] r_Red = 0;
reg [VIDEO_WIDTH-1:0] r_Green = 0;
reg [VIDEO_WIDTH-1:0] r_Blue = 0;
VGA_Current_Beam_Position #(
.TOTAL_COLUMNS(TOTAL_COLUMNS),
.TOTAL_ROWS(TOTAL_ROWS)
) CurrentPosition (
.i_Clk(i_Clk),
.i_HSync(i_HSync),
.i_VSync(i_VSync),
.o_HSync(w_HSync),
.o_VSync(w_VSync),
.o_X(w_X),
.o_Y(w_Y)
);
// https://web.mit.edu/6.111/www/s2004/NEWKIT/vga.shtml
always @(posedge i_Clk) begin
if (
(w_X < FRONT_PORCH_COUNT_X + ACTIVE_COLUMNS) ||
(w_X > TOTAL_COLUMNS - BACK_PORCH_COUNT_X - 1)
) begin
o_HSync <= 1;
end else begin
o_HSync <= w_HSync;
end
if (
(w_Y < FRONT_PORCH_COUNT_Y + ACTIVE_ROWS) ||
(w_Y > TOTAL_ROWS - BACK_PORCH_COUNT_Y - 1)
) begin
o_VSync <= 1;
end else begin
o_VSync <= w_VSync;
end
end
// The process of going through Current Beam Position and then
// the porch alignment above delays the video signal by two clock ticks.
// Do the same input -> wire -> output as above.
always @(posedge i_Clk) begin
r_Red <= i_Red;
r_Green <= i_Green;
r_Blue <= i_Blue;
o_Red <= r_Red;
o_Green <= r_Green;
o_Blue <= r_Blue;
end
endmodule

View File

@ -0,0 +1,47 @@
// Keys off of HSync and VSync signals to get the actual electron
// beam position
module VGA_Current_Beam_Position(
input i_Clk,
input i_HSync,
input i_VSync,
output reg o_HSync = 0,
output reg o_VSync = 0,
output reg [$clog2(TOTAL_COLUMNS)-1:0] o_X,
output reg [$clog2(TOTAL_ROWS)-1:0] o_Y
);
parameter TOTAL_COLUMNS = 800;
parameter TOTAL_ROWS = 525;
wire w_frameStart;
// forward these flip-flop style. use these instead of what
// comes out of sync pulse generator!
always @(posedge i_Clk) begin
o_VSync <= i_VSync;
o_HSync <= i_HSync;
end
always @(posedge i_Clk) begin
if (w_frameStart == 1) begin
o_X <= 0;
o_Y <= 0;
end else begin
if (o_X == TOTAL_COLUMNS - 1) begin
o_X <= 0;
if (o_Y == TOTAL_ROWS - 1) begin
o_Y <= 0;
end else begin
o_Y <= o_Y + 1;
end
end else begin
o_X <= o_X + 1;
end
end
end
// rising vsync == new frame
assign w_frameStart = (~o_VSync & i_VSync);
endmodule

189
vga_pattern_generator.v Normal file
View File

@ -0,0 +1,189 @@
module VGA_Pattern_Generator(
input i_Clk,
input [3:0] i_Pattern,
input i_HSync,
input i_VSync,
output reg o_HSync = 0,
output reg o_VSync = 0,
output reg [VIDEO_WIDTH-1:0] o_Red,
output reg [VIDEO_WIDTH-1:0] o_Green,
output reg [VIDEO_WIDTH-1:0] o_Blue
);
// Go Board only supports 3 bits per channel. That's even less than the
// Amiga!
parameter VIDEO_WIDTH = 3;
parameter TOTAL_COLUMNS = 800;
parameter TOTAL_ROWS = 525;
parameter ACTIVE_COLUMNS = 640;
parameter ACTIVE_ROWS = 480;
localparam COLOR_BAR_WIDTH = ACTIVE_COLUMNS / 8;
wire w_HSync, w_VSync;
wire [VIDEO_WIDTH-1:0] w_Red[0:15];
wire [VIDEO_WIDTH-1:0] w_Green[0:15];
wire [VIDEO_WIDTH-1:0] w_Blue[0:15];
wire [$clog2(TOTAL_COLUMNS)-1:0] w_X;
wire [$clog2(TOTAL_ROWS)-1:0] w_Y;
VGA_Current_Beam_Position #(
.TOTAL_COLUMNS(TOTAL_COLUMNS),
.TOTAL_ROWS(TOTAL_ROWS)
) CurrentPosition (
.i_Clk(i_Clk),
.i_HSync(i_HSync),
.i_VSync(i_VSync),
.o_HSync(w_HSync),
.o_VSync(w_VSync),
.o_X(w_X),
.o_Y(w_Y)
);
// all these pattern operations take one clock tick
// so delay sync signals by that much
always @(posedge i_Clk) begin
o_HSync <= w_HSync;
o_VSync <= w_VSync;
end
// none more black
assign w_Red[0] = 0;
assign w_Green[0] = 0;
assign w_Blue[0] = 0;
// red
assign w_Red[1] = (
(w_X < ACTIVE_COLUMNS && w_Y < ACTIVE_ROWS) ?
{VIDEO_WIDTH{1'b1}} :
0
);
assign w_Green[1] = 0;
assign w_Blue[1] = 0;
// green
assign w_Red[2] = 0;
assign w_Green[2] = (
(w_X < ACTIVE_COLUMNS && w_Y < ACTIVE_ROWS) ?
{VIDEO_WIDTH{1'b1}} :
0
);
assign w_Blue[2] = 0;
// blue
assign w_Red[3] = 0;
assign w_Green[3] = 0;
assign w_Blue[3] = (
(w_X < ACTIVE_COLUMNS && w_Y < ACTIVE_ROWS) ?
{VIDEO_WIDTH{1'b1}} :
0
);
// checkerboard
// cell size is 32 pixels
assign w_Red[4] = (w_X[5] ^ w_Y[5]) ? {VIDEO_WIDTH{1'b1}} : 0;
assign w_Green[4] = w_Red[4];
assign w_Blue[4] = w_Red[4];
wire [2:0] w_colorBarSelect;
// color bars
// yes this is the hardware way to do this b/c multi/div
assign w_colorBarSelect =
w_X < COLOR_BAR_WIDTH ? 0 :
w_X < COLOR_BAR_WIDTH * 2 ? 1 :
w_X < COLOR_BAR_WIDTH * 3 ? 2 :
w_X < COLOR_BAR_WIDTH * 4 ? 3 :
w_X < COLOR_BAR_WIDTH * 5 ? 4 :
w_X < COLOR_BAR_WIDTH * 6 ? 5 :
w_X < COLOR_BAR_WIDTH * 7 ? 6 :7;
assign w_Red[5] = (
w_colorBarSelect == 4 ||
w_colorBarSelect == 5 ||
w_colorBarSelect == 6 ||
w_colorBarSelect == 7
) ? {VIDEO_WIDTH{1'b1}} : 0;
assign w_Green[5] = (
w_colorBarSelect == 2 ||
w_colorBarSelect == 3 ||
w_colorBarSelect == 6 ||
w_colorBarSelect == 7
) ? {VIDEO_WIDTH{1'b1}} : 0;
assign w_Blue[5] = (
w_colorBarSelect == 1 ||
w_colorBarSelect == 3 ||
w_colorBarSelect == 5 ||
w_colorBarSelect == 7
) ? {VIDEO_WIDTH{1'b1}} : 0;
// border
localparam BORDER_WIDTH = 2;
assign w_Red[6] = (
w_X < BORDER_WIDTH || w_X >= ACTIVE_COLUMNS - BORDER_WIDTH ||
w_Y < BORDER_WIDTH || w_Y >= ACTIVE_ROWS - BORDER_WIDTH
) ? {VIDEO_WIDTH{1'b1}} : 0;
assign w_Green[6] = 0;
assign w_Blue[6] = 0;
always @(posedge i_Clk) begin
case (i_Pattern)
0:
begin
o_Red <= w_Red[0];
o_Green <= w_Green[0];
o_Blue <= w_Blue[0];
end
1:
begin
o_Red <= w_Red[1];
o_Green <= w_Green[1];
o_Blue <= w_Blue[1];
end
2:
begin
o_Red <= w_Red[2];
o_Green <= w_Green[2];
o_Blue <= w_Blue[2];
end
3:
begin
o_Red <= w_Red[3];
o_Green <= w_Green[3];
o_Blue <= w_Blue[3];
end
4:
begin
o_Red <= w_Red[4];
o_Green <= w_Green[4];
o_Blue <= w_Blue[4];
end
5:
begin
o_Red <= w_Red[5];
o_Green <= w_Green[5];
o_Blue <= w_Blue[5];
end
6:
begin
o_Red <= w_Red[6];
o_Green <= w_Green[6];
o_Blue <= w_Blue[6];
end
default:
begin
o_Red <= w_Red[0];
o_Green <= w_Green[0];
o_Blue <= w_Blue[0];
end
endcase
end
endmodule

View File

@ -0,0 +1,34 @@
// Remember, this "magically works" on the Go Board.
// If you're using a different clock speed, you need to
// do a different thing here.
module VGA_Sync_Pulse_Generator (
input i_Clk,
output o_HSync,
output o_VSync,
output reg [$clog2(TOTAL_COLUMNS)-1:0] o_rawX,
output reg [$clog2(TOTAL_ROWS)-1:0] o_rawY
);
// remember overscan
parameter ACTIVE_COLUMNS = 640;
parameter ACTIVE_ROWS = 480;
parameter TOTAL_COLUMNS = 800;
parameter TOTAL_ROWS = 525;
always @(posedge i_Clk) begin
if (o_rawX == TOTAL_COLUMNS - 1) begin
o_rawX <= 0;
if (o_rawY == TOTAL_ROWS - 1) begin
o_rawY <= 0;
end else begin
o_rawY <= o_rawY + 1;
end
end else begin
o_rawX <= o_rawX + 1;
end
end
assign o_HSync = (o_rawX < ACTIVE_COLUMNS) ? 1 : 0;
assign o_VSync = (o_rawY < ACTIVE_ROWS) ? 1 : 0;
endmodule

219
vga_tester.v Normal file
View File

@ -0,0 +1,219 @@
module VGA_Tester(
input i_Clk,
output o_VGA_HSync,
output o_VGA_VSync,
output o_VGA_Red_0,
output o_VGA_Red_1,
output o_VGA_Red_2,
output o_VGA_Grn_0,
output o_VGA_Grn_1,
output o_VGA_Grn_2,
output o_VGA_Blu_0,
output o_VGA_Blu_1,
output o_VGA_Blu_2,
input i_UART_RX,
output o_UART_TX,
output o_Segment1_A,
output o_Segment1_B,
output o_Segment1_C,
output o_Segment1_D,
output o_Segment1_E,
output o_Segment1_F,
output o_Segment1_G,
output o_Segment2_A,
output o_Segment2_B,
output o_Segment2_C,
output o_Segment2_D,
output o_Segment2_E,
output o_Segment2_F,
output o_Segment2_G
);
localparam TOTAL_COLUMNS = 800;
localparam TOTAL_ROWS = 525;
localparam ACTIVE_COLUMNS = 640;
localparam ACTIVE_ROWS = 480;
localparam VIDEO_WIDTH = 3;
// vga
wire w_HSync_Start, w_VSync_Start;
wire w_HSync_FromTestPattern, w_VSync_FromTestPattern;
wire w_HSync_FromPorchSync, w_VSync_FromPorchSync;
wire [VIDEO_WIDTH-1:0] w_Red_FromTestPattern;
wire [VIDEO_WIDTH-1:0] w_Red_FromPorchSync;
wire [VIDEO_WIDTH-1:0] w_Green_FromTestPattern;
wire [VIDEO_WIDTH-1:0] w_Green_FromPorchSync;
wire [VIDEO_WIDTH-1:0] w_Blue_FromTestPattern;
wire [VIDEO_WIDTH-1:0] w_Blue_FromPorchSync;
reg [3:0] r_currentPattern = 6;
// uart
wire w_dataReady;
wire [7:0] w_dataByte;
wire w_transmitActive, w_transmitSerial;
wire w_segment1_A, w_segment2_A;
wire w_segment1_B, w_segment2_B;
wire w_segment1_C, w_segment2_C;
wire w_segment1_D, w_segment2_D;
wire w_segment1_E, w_segment2_E;
wire w_segment1_F, w_segment2_F;
wire w_segment1_G, w_segment2_G;
reg r_resetTransmit = 1;
// accept incoming data
UART_RX #(
.CLOCKS_PER_SECOND(25000000),
.BAUD_RATE(115200)
) MyUART_RX (
.i_Clk(i_Clk),
.i_ReceiveBit(i_UART_RX),
.o_DataReady(w_dataReady),
.o_DataByte(w_dataByte)
);
UART_TX #(
.CLOCKS_PER_SECOND(25000000),
.BAUD_RATE(115200)
) MyUART_TX (
.i_Clk(i_Clk),
.i_Reset_Low(r_resetTransmit),
.i_TransmitReady(w_dataReady),
.i_TransmitByte(w_dataByte),
.o_Active(w_transmitActive),
.o_Output(w_transmitSerial),
.o_Done()
);
assign o_UART_TX = w_transmitActive ? w_transmitSerial : 1;
Binary_to_7_Segment Segment_1 (
.i_Clk(i_Clk),
.i_Number(w_dataByte[7:4]),
.o_SegA(w_segment1_A),
.o_SegB(w_segment1_B),
.o_SegC(w_segment1_C),
.o_SegD(w_segment1_D),
.o_SegE(w_segment1_E),
.o_SegF(w_segment1_F),
.o_SegG(w_segment1_G)
);
assign o_Segment1_A = !w_segment1_A;
assign o_Segment1_B = !w_segment1_B;
assign o_Segment1_C = !w_segment1_C;
assign o_Segment1_D = !w_segment1_D;
assign o_Segment1_E = !w_segment1_E;
assign o_Segment1_F = !w_segment1_F;
assign o_Segment1_G = !w_segment1_G;
Binary_to_7_Segment Segment_2 (
.i_Clk(i_Clk),
.i_Number(w_dataByte[3:0]),
.o_SegA(w_segment2_A),
.o_SegB(w_segment2_B),
.o_SegC(w_segment2_C),
.o_SegD(w_segment2_D),
.o_SegE(w_segment2_E),
.o_SegF(w_segment2_F),
.o_SegG(w_segment2_G)
);
assign o_Segment2_A = !w_segment2_A;
assign o_Segment2_B = !w_segment2_B;
assign o_Segment2_C = !w_segment2_C;
assign o_Segment2_D = !w_segment2_D;
assign o_Segment2_E = !w_segment2_E;
assign o_Segment2_F = !w_segment2_F;
assign o_Segment2_G = !w_segment2_G;
always @(posedge i_Clk) begin
if (w_dataReady) begin
r_currentPattern <= w_dataByte[3:0];
end
end
// show the video
VGA_Sync_Pulse_Generator #(
.TOTAL_COLUMNS(TOTAL_COLUMNS),
.TOTAL_ROWS(TOTAL_ROWS),
.ACTIVE_COLUMNS(ACTIVE_COLUMNS),
.ACTIVE_ROWS(ACTIVE_ROWS)
) SyncGenerator (
.i_Clk(i_Clk),
.o_HSync(w_HSync_Start),
.o_VSync(w_VSync_Start),
.o_rawX(),
.o_rawY()
);
VGA_Pattern_Generator #(
.VIDEO_WIDTH(VIDEO_WIDTH),
.TOTAL_COLUMNS(TOTAL_COLUMNS),
.TOTAL_ROWS(TOTAL_ROWS),
.ACTIVE_COLUMNS(ACTIVE_COLUMNS),
.ACTIVE_ROWS(ACTIVE_ROWS)
) PatternGenerator (
.i_Clk(i_Clk),
.i_Pattern(r_currentPattern),
.i_HSync(w_HSync_Start),
.i_VSync(w_VSync_Start),
.o_HSync(w_HSync_FromTestPattern),
.i_Clk(i_Clk),
.i_Pattern(r_currentPattern),
.i_HSync(w_HSync_Start),
.i_VSync(w_VSync_Start),
.o_HSync(w_HSync_FromTestPattern),
.o_VSync(w_VSync_FromTestPattern),
.o_Red(w_Red_FromTestPattern),
.o_Green(w_Green_FromTestPattern),
.o_Blue(w_Blue_FromTestPattern)
);
VGA_Add_Porches_To_Output #(
.VIDEO_WIDTH(VIDEO_WIDTH),
.TOTAL_COLUMNS(TOTAL_COLUMNS),
.TOTAL_ROWS(TOTAL_ROWS),
.ACTIVE_COLUMNS(ACTIVE_COLUMNS),
.ACTIVE_ROWS(ACTIVE_ROWS)
) PorchBuilder (
.i_Clk(i_Clk),
.i_HSync(w_HSync_FromTestPattern),
.i_VSync(w_VSync_FromTestPattern),
.i_Red(w_Red_FromTestPattern),
.i_Green(w_Green_FromTestPattern),
.i_Blue(w_Blue_FromTestPattern),
.o_HSync(w_HSync_FromPorchSync),
.o_VSync(w_VSync_FromPorchSync),
.o_Red(w_Red_FromPorchSync),
.o_Green(w_Green_FromPorchSync),
.o_Blue(w_Blue_FromPorchSync)
);
assign o_VGA_HSync = w_HSync_FromPorchSync;
assign o_VGA_VSync = w_VSync_FromPorchSync;
assign o_VGA_Red_0 = w_Red_FromPorchSync[0];
assign o_VGA_Red_1 = w_Red_FromPorchSync[1];
assign o_VGA_Red_2 = w_Red_FromPorchSync[2];
assign o_VGA_Grn_0 = w_Green_FromPorchSync[0];
assign o_VGA_Grn_1 = w_Green_FromPorchSync[1];
assign o_VGA_Grn_2 = w_Green_FromPorchSync[2];
assign o_VGA_Blu_0 = w_Blue_FromPorchSync[0];
assign o_VGA_Blu_1 = w_Blue_FromPorchSync[1];
assign o_VGA_Blu_2 = w_Blue_FromPorchSync[2];
endmodule