Created
May 21, 2024 11:12
-
-
Save urish/421920c1361b1d3dd132630b4ee84063 to your computer and use it in GitHub Desktop.
Matt's VGA Clock for the VGA Clock Playground
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2024 Uri Shaked | |
* SPDX-License-Identifier: Apache-2.0 | |
*/ | |
`default_nettype none | |
module tt_um_vga_example( | |
input wire [7:0] ui_in, // Dedicated inputs | |
output wire [7:0] uo_out, // Dedicated outputs | |
input wire [7:0] uio_in, // IOs: Input path | |
output wire [7:0] uio_out, // IOs: Output path | |
output wire [7:0] uio_oe, // IOs: Enable path (active high: 0=input, 1=output) | |
input wire ena, // always 1 when the design is powered, so you can ignore it | |
input wire clk, // clock | |
input wire rst_n // reset_n - low to reset | |
); | |
assign uio_out = 8'b0; | |
assign uio_oe = 8'b0; | |
wire [1:0] R; | |
wire [1:0] G; | |
wire [1:0] B; | |
wire hsync, vsync; | |
wire vga_clock_pmod = ui_in[3]; | |
// this input switches between Tiny VGA and VGA Clock PMOD | |
// https://github.com/mole99/tiny-vga | |
// https://github.com/TinyTapeout/tt-vga-clock-pmod | |
assign uo_out[0] = vga_clock_pmod ? hsync : R[1]; | |
assign uo_out[1] = vga_clock_pmod ? vsync : G[1]; | |
assign uo_out[2] = vga_clock_pmod ? B[0] : B[1]; | |
assign uo_out[3] = vga_clock_pmod ? B[1] : vsync; | |
assign uo_out[4] = vga_clock_pmod ? G[0] : R[0]; | |
assign uo_out[5] = vga_clock_pmod ? G[1] : G[0]; | |
assign uo_out[6] = vga_clock_pmod ? R[0] : B[0]; | |
assign uo_out[7] = vga_clock_pmod ? R[1] : hsync; | |
vga_clock vga_clock ( | |
.clk (clk), | |
.reset_n (rst_n), | |
// inputs | |
.adj_hrs (ui_in[0]), | |
.adj_min (ui_in[1]), | |
.adj_sec (ui_in[2]), | |
// outputs | |
.hsync (hsync), | |
.vsync (vsync), | |
.rrggbb ({R,G,B}) | |
); | |
endmodule | |
module vga_clock ( | |
input wire clk, | |
input wire reset_n, | |
input wire adj_hrs, | |
input wire adj_min, | |
input wire adj_sec, | |
output wire hsync, | |
output wire vsync, | |
output wire [5:0] rrggbb | |
); | |
wire reset = !reset_n; | |
reg [3:0] sec_u; | |
reg [2:0] sec_d; | |
reg [3:0] min_u; | |
reg [2:0] min_d; | |
reg [3:0] hrs_u; | |
reg [1:0] hrs_d; | |
reg [25:0] sec_counter; | |
always @(posedge px_clk) begin | |
if(reset) begin | |
sec_u <= 0; | |
sec_d <= 0; | |
min_u <= 0; | |
min_d <= 0; | |
hrs_u <= 0; | |
hrs_d <= 0; | |
sec_counter <= 0; | |
color_offset <= 0; | |
end else begin | |
if(sec_u == 10) begin | |
sec_u <= 0; | |
sec_d <= sec_d + 1; | |
end | |
if(sec_d == 6) begin | |
sec_d <= 0; | |
min_u <= min_u + 1; | |
color_offset <= color_offset + 1; | |
end | |
if(min_u == 10) begin | |
min_u <= 0; | |
min_d <= min_d + 1; | |
end | |
if(min_d == 6) begin | |
min_d <= 0; | |
hrs_u <= hrs_u + 1; | |
end | |
if(hrs_u == 10) begin | |
hrs_u <= 0; | |
hrs_d <= hrs_d + 1; | |
end | |
if(hrs_d == 2 && hrs_u == 4) begin | |
hrs_u <= 0; | |
hrs_d <= 0; | |
end | |
// second counter | |
sec_counter <= sec_counter + 1; | |
if(sec_counter + 1 == 31_500_000) begin | |
sec_u <= sec_u + 1; | |
sec_counter <= 0; | |
end | |
// adjustment buttons | |
if(adj_sec_pulse) | |
sec_u <= sec_u + 1; | |
if(adj_min_pulse) begin | |
min_u <= min_u + 1; | |
color_offset <= color_offset + 1; | |
end | |
if(adj_hrs_pulse) | |
hrs_u <= hrs_u + 1; | |
end | |
end | |
wire adj_sec_pulse, adj_min_pulse, adj_hrs_pulse; | |
// want button_clk_en to be about 10ms | |
// frame rate is 70hz is 15ms | |
wire but_clk_en = y_px == 0 && x_px == 0; | |
localparam MAX_BUT_RATE = 16; | |
localparam DEC_COUNT = 1; | |
localparam MIN_COUNT = 2; | |
button_pulse #(.MIN_COUNT(MIN_COUNT), .DEC_COUNT(DEC_COUNT), .MAX_COUNT(MAX_BUT_RATE)) | |
pulse_sec (.clk(px_clk), .clk_en(but_clk_en), .button(adj_sec), .pulse(adj_sec_pulse), .reset(reset)); | |
button_pulse #(.MIN_COUNT(MIN_COUNT), .DEC_COUNT(DEC_COUNT), .MAX_COUNT(MAX_BUT_RATE)) | |
pulse_min (.clk(px_clk), .clk_en(but_clk_en), .button(adj_min), .pulse(adj_min_pulse), .reset(reset)); | |
button_pulse #(.MIN_COUNT(MIN_COUNT), .DEC_COUNT(DEC_COUNT), .MAX_COUNT(MAX_BUT_RATE)) | |
pulse_hrs (.clk(px_clk), .clk_en(but_clk_en), .button(adj_hrs), .pulse(adj_hrs_pulse), .reset(reset)); | |
// these are in blocks | |
localparam OFFSET_Y_BLK = 0; | |
localparam OFFSET_X_BLK = 1; | |
localparam NUM_CHARS = 8; | |
localparam FONT_W = 4; | |
localparam FONT_H = 5; | |
localparam COLON = 10; | |
localparam BLANK = 11; | |
localparam COL_INDEX_W = $clog2(FONT_W); | |
wire [9:0] x_px; // X position for actual pixel. | |
wire [9:0] y_px; // Y position for actual pixel. | |
// blocks are 16 x 16 px. total width = 8 * blocks of 4 = 512. | |
/* verilator lint_off WIDTH */ | |
wire [5:0] x_block = (x_px -64) >> 4; | |
wire [5:0] y_block = (y_px -200) >> 4; | |
/* verilator lint_on WIDTH */ | |
reg [5:0] x_block_q; | |
reg [5:0] y_block_q; | |
// reg [5:0] x_block = 0; | |
// reg [5:0] y_block = 0; | |
wire activevideo; | |
wire px_clk; | |
assign px_clk = clk; | |
hvsync_generator hvsync_gen( | |
.clk(px_clk), | |
.reset(~reset_n), | |
.hsync(hsync), | |
.vsync(vsync), | |
.display_on(activevideo), | |
.hpos(x_px), | |
.vpos(y_px) | |
); | |
wire [FONT_W-1:0] font_out; | |
wire [5:0] font_addr; | |
fontROM #(.data_width(FONT_W)) font_0 (.clk(px_clk), .addr(font_addr), .dout(font_out)); | |
wire [5:0] digit_index; | |
wire [5:0] color; | |
reg [3:0] color_offset; | |
wire [3:0] number; | |
wire [COL_INDEX_W-1:0] col_index; | |
reg [COL_INDEX_W-1:0] col_index_q; | |
initial begin | |
$display(FONT_W); | |
$display(COL_INDEX_W); | |
end | |
digit #(.FONT_W(FONT_W), .FONT_H(FONT_H), .NUM_BLOCKS(NUM_CHARS*FONT_W)) digit_0 (.clk(px_clk), .x_block(x_block), .number(number), .digit_index(digit_index), .col_index(col_index), .color(color), .color_offset(color_offset)); | |
/* verilator lint_off WIDTH */ | |
assign number = x_block < FONT_W * 1 ? hrs_d : | |
x_block < FONT_W * 2 ? hrs_u : | |
x_block < FONT_W * 3 ? COLON : | |
x_block < FONT_W * 4 ? min_d : | |
x_block < FONT_W * 5 ? min_u : | |
x_block < FONT_W * 6 ? COLON : | |
x_block < FONT_W * 7 ? sec_d : | |
x_block < FONT_W * 8 ? sec_u : | |
BLANK; | |
/* verilator lint_on WIDTH */ | |
assign rrggbb = activevideo && draw ? color : 6'b0; | |
assign font_addr = digit_index + y_block; | |
reg draw; | |
always @(posedge px_clk) begin | |
if(reset) | |
draw <= 0; | |
x_block_q <= x_block; | |
y_block_q <= y_block; | |
col_index_q <= col_index; | |
if(x_block_q < FONT_W * NUM_CHARS && y_block_q < FONT_H) | |
draw <= font_out[(FONT_W - 1) - col_index_q]; | |
else | |
draw <= 0; | |
end | |
endmodule | |
module button_pulse | |
#( | |
parameter MAX_COUNT = 8, // max wait before issue next pulse | |
parameter DEC_COUNT = 2, // every pulse, decrement comparitor by this amount | |
parameter MIN_COUNT = 1 // until reaches this wait time | |
)( | |
input wire clk, | |
input wire clk_en, | |
input wire button, | |
input wire reset, | |
output wire pulse | |
); | |
reg [$clog2(MAX_COUNT-1):0] comp; | |
reg [$clog2(MAX_COUNT-1):0] count; | |
assign pulse = (clk_en && button && count == 0); | |
always @(posedge clk) | |
if(reset) begin | |
comp <= MAX_COUNT - 1; | |
count <= 0; | |
end else | |
if(clk_en) begin | |
if(button) | |
count <= count + 1; | |
// if button is held, increase pulse rate by reducing comp | |
if(count == 0 && comp > (MIN_COUNT + DEC_COUNT)) begin | |
comp <= comp - DEC_COUNT; | |
end | |
// reset counter | |
if(count == comp) | |
count <= 0; | |
// if button is released, set count and comp to default | |
if(!button) begin | |
count <= 0; | |
comp <= MAX_COUNT - 1; | |
end | |
end | |
/* | |
`ifdef FORMAL | |
default clocking @(posedge clk); endclocking | |
default disable iff (!clk_en); | |
cover property (##1 $rose(pulse)); | |
cover property (comp < MAX_COUNT - 1); | |
assert property (button |=> comp <= $past(comp)); | |
assert property (button && count != comp |=> count >= $past(count)); | |
assert property (button && count == 0 |-> pulse); | |
assert property (pulse |=> !pulse); | |
`endif | |
*/ | |
endmodule | |
`default_nettype none | |
module digit | |
#( | |
parameter DIGIT_INDEX_FILE = "digit_index.hex", | |
parameter COL_INDEX_FILE = "col_index.hex", | |
parameter COLOR_INDEX_FILE = "color.hex", | |
parameter FONT_W = 3, | |
parameter FONT_H = 5, | |
parameter NUM_BLOCKS = 20 | |
) | |
( | |
input wire clk, | |
input wire [5:0] x_block, | |
// input wire [5:0] y_block, | |
input wire [3:0] number, // the number to display: [0->9: ] | |
input wire [3:0] color_offset, // shift through the colours | |
output reg [5:0] digit_index, | |
output reg [5:0] color, | |
output reg [COL_INDEX_W-1:0] col_index | |
); | |
localparam COL_INDEX_W = $clog2(FONT_W); | |
reg [5:0] digit_index_mem [0:11]; | |
reg [COL_INDEX_W-1:0] col_index_mem [0:NUM_BLOCKS]; | |
reg [5:0] color_index_mem [0:7]; | |
initial begin | |
digit_index_mem[0] = 8'h00; | |
digit_index_mem[1] = 8'h05; | |
digit_index_mem[2] = 8'h0a; | |
digit_index_mem[3] = 8'h0f; | |
digit_index_mem[4] = 8'h14; | |
digit_index_mem[5] = 8'h19; | |
digit_index_mem[6] = 8'h1e; | |
digit_index_mem[7] = 8'h23; | |
digit_index_mem[8] = 8'h28; | |
digit_index_mem[9] = 8'h2d; | |
digit_index_mem[10] = 8'h32; | |
digit_index_mem[11] = 8'h37; | |
col_index_mem[0] = 8'h00; | |
col_index_mem[1] = 8'h01; | |
col_index_mem[2] = 8'h02; | |
col_index_mem[3] = 8'h03; | |
col_index_mem[4] = 8'h00; | |
col_index_mem[5] = 8'h01; | |
col_index_mem[6] = 8'h02; | |
col_index_mem[7] = 8'h03; | |
col_index_mem[8] = 8'h00; | |
col_index_mem[9] = 8'h01; | |
col_index_mem[10] = 8'h02; | |
col_index_mem[11] = 8'h03; | |
col_index_mem[12] = 8'h00; | |
col_index_mem[13] = 8'h01; | |
col_index_mem[14] = 8'h02; | |
col_index_mem[15] = 8'h03; | |
col_index_mem[16] = 8'h00; | |
col_index_mem[17] = 8'h01; | |
col_index_mem[18] = 8'h02; | |
col_index_mem[19] = 8'h03; | |
col_index_mem[20] = 8'h00; | |
col_index_mem[21] = 8'h01; | |
col_index_mem[22] = 8'h02; | |
col_index_mem[23] = 8'h03; | |
col_index_mem[24] = 8'h00; | |
col_index_mem[25] = 8'h01; | |
col_index_mem[26] = 8'h02; | |
col_index_mem[27] = 8'h03; | |
col_index_mem[28] = 8'h00; | |
col_index_mem[29] = 8'h01; | |
col_index_mem[30] = 8'h02; | |
col_index_mem[31] = 8'h03; | |
color_index_mem[0] = 6'b110000; | |
color_index_mem[1] = 6'b111000; | |
color_index_mem[2] = 6'b111100; | |
color_index_mem[3] = 6'b001000; | |
color_index_mem[4] = 6'b000011; | |
color_index_mem[5] = 6'b100010; | |
color_index_mem[6] = 6'b010010; | |
color_index_mem[7] = 6'b110000; | |
end | |
wire [3:0] char = x_block[5:2]; | |
always @(posedge clk) begin | |
digit_index <= digit_index_mem[number]; | |
col_index <= col_index_mem[x_block < NUM_BLOCKS ? x_block : NUM_BLOCKS-1]; | |
color <= color_index_mem[char + color_offset]; | |
end | |
endmodule | |
////////////////////////////////////////////////////////////////////////////////// | |
// Company: Ridotech | |
// Engineer: Juan Manuel Rico | |
// | |
// Create Date: 21:30:38 26/04/2018 | |
// Module Name: fontROM | |
// | |
// Description: Font ROM for numbers (16x19 bits for numbers 0 to 9). | |
// | |
// Dependencies: | |
// | |
// Revision: | |
// Revision 0.01 - File Created | |
// | |
// Additional Comments: | |
// | |
//----------------------------------------------------------------------------- | |
//-- GPL license | |
//----------------------------------------------------------------------------- | |
module fontROM | |
#( | |
parameter FONT_FILE = "font.list", | |
parameter addr_width = 6, | |
parameter data_width = 4 | |
) | |
( | |
input wire clk, | |
input wire [addr_width-1:0] addr, | |
output reg [data_width-1:0] dout | |
); | |
reg [data_width-1:0] mem [(1 << addr_width)-1:0]; | |
initial begin | |
mem[0] = 4'b1110; | |
mem[1] = 4'b1010; | |
mem[2] = 4'b1010; | |
mem[3] = 4'b1010; | |
mem[4] = 4'b1110; | |
mem[5] = 4'b1100; | |
mem[6] = 4'b0100; | |
mem[7] = 4'b0100; | |
mem[8] = 4'b0100; | |
mem[9] = 4'b1110; | |
mem[10] = 4'b1110; | |
mem[11] = 4'b0010; | |
mem[12] = 4'b1110; | |
mem[13] = 4'b1000; | |
mem[14] = 4'b1110; | |
mem[15] = 4'b1110; | |
mem[16] = 4'b0010; | |
mem[17] = 4'b1110; | |
mem[18] = 4'b0010; | |
mem[19] = 4'b1110; | |
mem[20] = 4'b1010; | |
mem[21] = 4'b1010; | |
mem[22] = 4'b1110; | |
mem[23] = 4'b0010; | |
mem[24] = 4'b0010; | |
mem[25] = 4'b1110; | |
mem[26] = 4'b1000; | |
mem[27] = 4'b1110; | |
mem[28] = 4'b0010; | |
mem[29] = 4'b1110; | |
mem[30] = 4'b1000; | |
mem[31] = 4'b1000; | |
mem[32] = 4'b1110; | |
mem[33] = 4'b1010; | |
mem[34] = 4'b1110; | |
mem[35] = 4'b1110; | |
mem[36] = 4'b0010; | |
mem[37] = 4'b0100; | |
mem[38] = 4'b1000; | |
mem[39] = 4'b1000; | |
mem[40] = 4'b1110; | |
mem[41] = 4'b1010; | |
mem[42] = 4'b1110; | |
mem[43] = 4'b1010; | |
mem[44] = 4'b1110; | |
mem[45] = 4'b1110; | |
mem[46] = 4'b1010; | |
mem[47] = 4'b1110; | |
mem[48] = 4'b0010; | |
mem[49] = 4'b0010; | |
mem[50] = 4'b0000; | |
mem[51] = 4'b0100; | |
mem[52] = 4'b0000; | |
mem[53] = 4'b0100; | |
mem[54] = 4'b0000; | |
mem[55] = 4'b0000; | |
mem[56] = 4'b0000; | |
mem[57] = 4'b0000; | |
mem[58] = 4'b0000; | |
mem[59] = 4'b0000; | |
end | |
always @(posedge clk) | |
begin | |
dout <= mem[addr]; | |
end | |
endmodule |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment