pong-verilog/vga.v
2025-11-05 07:26:15 +01:00

295 lines
7.0 KiB
Verilog

`timescale 1ns/1ps
module vga #(
parameter COLS = 1024, // oszlopok száma a képernyőn
parameter ROWS = 768, // sorok száma a képernyőn
parameter H_FRONT_PORCH = 24,
parameter H_SYNC_PULSE = 136,
parameter H_BACK_PORCH = 160,
parameter V_FRONT_PORCH = 3,
parameter V_SYNC_PULSE = 6,
parameter V_BACK_PORCH = 29,
parameter H_SYNC_NEG = 1'b1,
parameter V_SYNC_NEG = 1'b1,
parameter OVERSCAN = 1,
// színek (BGR)
parameter COL_POD1 = 3'b001,
parameter COL_POD2 = 3'b010,
parameter COL_BALL = 3'b011,
parameter COL_BG = 3'b000,
// platformok tulajdonságai
parameter POD_WIDTH = COLS / 6,
parameter POD_HEIGHT = ROWS / 32,
parameter POD_TOP = ROWS - 50,
// középső fal tulajdonságai
parameter WALL_HALF_WIDTH = 4,
parameter WALL_HEIGHT = ROWS / 3,
// bithosszok
parameter N_COLS = $clog2(H_FULL),
parameter N_ROWS = $clog2(V_FULL)
)(
input wire pclk, // pixel órajel (65MHz)
input wire sclk, // lassú órajel (1MHz)
input wire rst, // reset
output wire hsync, // vízszintes szinkron
output wire vsync, // függőleges szinkron
output wire [2:0] rgb, // színjelek
input wire [N_COLS - 1:0] pod1x, // első játékos helye
input wire [N_COLS - 1:0] pod2x, // második játékos helye
input wire [N_COLS - 1:0] ballx, // a labda X koordinátája
input wire [N_ROWS - 1:0] bally // a labda Y koordinátája
);
localparam H_FULL = COLS + H_FRONT_PORCH + H_SYNC_PULSE + H_BACK_PORCH;
localparam V_FULL = ROWS + V_FRONT_PORCH + V_SYNC_PULSE + V_BACK_PORCH;
localparam BALL_RADIUS = 12;
localparam [0:25*25-1] BALL_GLYPH = {
25'b0000000000000000000000000,
25'b0000000001111111100000000,
25'b0000001111111111111000000,
25'b0000011100111111111100000,
25'b0000110000011111111110000,
25'b0001110000011111111111000,
25'b0011100000111111111111100,
25'b0011000001111111111111110,
25'b0111000011111111111111110,
25'b0110000011111111111111110,
25'b0110000111111111111111110,
25'b0111111111111111111111111,
25'b0111111111111111111111111,
25'b0111111111111111111111111,
25'b0111111111111111111111111,
25'b0111111111111111111111110,
25'b0111111111111111111111110,
25'b0011111111111111111111110,
25'b0011111111111111111111100,
25'b0001111111111111111111100,
25'b0000111111111111111111000,
25'b0000111111111111111110000,
25'b0000001111111111111100000,
25'b0000000111111111110000000,
25'b0000000000111110000000000
};
// -----------
reg [N_COLS - 1:0] col; // oszlopszámláló
reg [N_ROWS - 1:0] row; // sorszámláló
// oszlopszámláló
always @(posedge pclk)
begin
col <= (rst || (col == (H_FULL - 1))) ? 0 : (col + 1'b1);
end
wire row_ce = col == (H_FULL - 1); // oszlopszámláló engedélyezés
// sorszámláló
always @(posedge pclk)
begin
if (rst)
begin
row <= 0;
end
else if (row_ce)
begin
row <= (row == (V_FULL - 1)) ? 0 : (row + 1'b1);
end
end
wire new_frame = row_ce && (row == (V_FULL - 1));
// kijazolási állapotok
localparam VIS = 0; // látható terület
localparam NV_FP = 1; // nem látható, front porch
localparam NV_SYNC = 2; // nem látható, szinkronjel
localparam NV_BP = 3; // nem látható, back porch
reg [1:0] h_state; // vízszintes kirajzolási állapot
reg [1:0] v_state; // függőleges kirajzolási állapot
always @(posedge pclk)
begin
if (rst)
begin
h_state = VIS;
v_state = VIS;
end
else
begin
h_state <= next_h_state;
if (row_ce)
begin
v_state <= next_v_state;
end
end
end
reg [1:0] next_h_state; // következő vízszintes állapot
reg [1:0] next_v_state; // következő függőleges állapot
always @(*)
begin
case (h_state)
VIS : next_h_state <= (col == (COLS - 1)) ? NV_FP : VIS;
NV_FP : next_h_state <= (col == (COLS + H_FRONT_PORCH - 1)) ? NV_SYNC : NV_FP;
NV_SYNC : next_h_state <= (col == (COLS + H_FRONT_PORCH + H_SYNC_PULSE - 1)) ? NV_BP : NV_SYNC;
NV_BP : next_h_state <= (col == (H_FULL - 1)) ? VIS : NV_BP;
endcase
end
always @(*)
begin
case (v_state)
VIS : next_v_state <= (row == (ROWS - 1)) ? NV_FP : VIS;
NV_FP : next_v_state <= (row == (ROWS + V_FRONT_PORCH - 1)) ? NV_SYNC : NV_FP;
NV_SYNC : next_v_state <= (row == (ROWS + V_FRONT_PORCH + V_SYNC_PULSE - 1)) ? NV_BP : NV_SYNC;
NV_BP : next_v_state <= (row == (V_FULL - 1)) ? VIS : NV_BP;
endcase
end
// vízszintes overscan
reg h_ovs_visible;
always @(posedge pclk)
begin
h_ovs_visible <= (rst) ? 1'b0 : next_h_ovs_visible;
end
reg next_h_ovs_visible;
always @(*)
begin
case (h_ovs_visible)
1'b0 : next_h_ovs_visible <= (col == OVERSCAN) ? 1'b1 : 1'b0;
1'b1 : next_h_ovs_visible <= (col == (COLS - OVERSCAN - 1)) ? 1'b0 : 1'b1;
endcase
end
// függőleges overscan
reg v_ovs_visible;
always @(posedge pclk)
begin
if (rst)
begin
v_ovs_visible <= 1'b0;
end
else
begin if (row_ce)
v_ovs_visible <= next_v_ovs_visible;
end
end
reg next_v_ovs_visible;
always @(*)
begin
case (v_ovs_visible)
1'b0 : next_v_ovs_visible <= (row == OVERSCAN) ? 1'b1 : 1'b0;
1'b1 : next_v_ovs_visible <= (row == (ROWS - OVERSCAN - 1)) ? 1'b0 : 1'b1;
endcase
end
// szinkronjelek
assign hsync = H_SYNC_NEG ? (h_state != NV_SYNC) : (h_state == NV_SYNC);
assign vsync = V_SYNC_NEG ? (v_state != NV_SYNC) : (v_state == NV_SYNC);
// kijelzés
reg [N_COLS - 1:0] pod1x_;
reg [N_COLS - 1:0] pod2x_;
reg [N_COLS - 1:0] ballx_;
reg [N_ROWS - 1:0] bally_;
// bemeneti adatok átvétele
always @(posedge pclk)
begin
if (rst)
begin
pod1x_ <= 10;
pod2x_ <= 400;
ballx_ <= 200;
bally_ <= 200;
end
else if (new_frame)
begin
pod1x_ <= pod1x;
pod2x_ <= pod2x;
ballx_ <= ballx;
bally_ <= bally;
end
end
reg pod_area;
// ütők kirajzolási magasságának felismerése
always @(posedge pclk)
begin
if (rst)
begin
pod_area <= 1'b0;
end
else
begin if (row_ce)
pod_area <= next_pod_area;
end
end
reg next_pod_area;
always @(*)
begin
case (pod_area)
1'b0: next_pod_area <= (row == (POD_TOP - 1)) ? 1'b1 : 1'b0;
1'b1: next_pod_area <= (row == (POD_TOP + POD_HEIGHT - 1)) ? 1'b0 : 1'b1;
endcase
end
// teljes játéktér kirajzolása
reg [2:0] color;
always @(posedge pclk)
begin
// háttérszín
color <= COL_BG;
// labda kirajzolása
if ((col > ballx_ - BALL_RADIUS - 1) && (col < ballx_ + BALL_RADIUS + 1) &&
(row > bally_ - BALL_RADIUS - 1) && (row < bally_ + BALL_RADIUS + 1 ))
begin
reg [N_COLS:0] rbx;
reg [N_ROWS:0] rby;
rbx = col - (ballx_ - BALL_RADIUS);
rby = row - (bally_ - BALL_RADIUS);
color <= BALL_GLYPH[rby * 25 + rbx] ? COL_BALL : 3'b000;
end
// fal kirajzolása
if ((col > (COLS / 2) - WALL_HALF_WIDTH - 1) && (col < (COLS / 2) + WALL_HALF_WIDTH - 1) &&
(row > (ROWS - WALL_HEIGHT)))
begin
color <= 3'b111;
end
// ütők kirajzolása
if (pod_area)
begin
if ((col > pod1x_) && (col < pod1x_ + POD_WIDTH))
begin
color <= COL_POD1;
end
else if ((col > pod2x_) && (col < pod2x_ + POD_WIDTH))
begin
color <= COL_POD2;
end
end
end
//wire [2:0] color = (pod_area) ? COL_POD1 : COL_BG;
wire visible = h_ovs_visible && v_ovs_visible; // épp látható területen vagyunk?
assign rgb = visible ? color : 3'b000; // színinformáció kiadása
endmodule