module vga_pong_controller ( // Clock e Reset input wire clk, // 100 MHz clock input wire reset, // Avalon Memory-Mapped Slave Interface input wire [4:0] avs_address, input wire avs_read, input wire avs_write, input wire [31:0] avs_writedata, output reg [31:0] avs_readdata, // VGA Output Signals output wire [7:0] VGA_R, output wire [7:0] VGA_G, output wire [7:0] VGA_B, output wire VGA_HS, output wire VGA_VS, output wire VGA_BLANK_N, output wire VGA_SYNC_N, output wire VGA_CLK ); // ============================================================================ // Timing Parameters per 800x600 @ 60Hz (Pixel Clock = 40 MHz) // ============================================================================ localparam H_DISPLAY = 800; localparam H_FRONT = 40; localparam H_SYNC = 128; localparam H_BACK = 88; localparam H_TOTAL = H_DISPLAY + H_FRONT + H_SYNC + H_BACK; // 1056 localparam V_DISPLAY = 600; localparam V_FRONT = 1; localparam V_SYNC = 4; localparam V_BACK = 23; localparam V_TOTAL = V_DISPLAY + V_FRONT + V_SYNC + V_BACK; // 628 // ============================================================================ // Parametri di gioco fissi // ============================================================================ localparam PADDLE_WIDTH = 10; // Larghezza racchette localparam PADDLE_HEIGHT = 80; // Altezza racchette localparam BALL_SIZE = 10; // Dimensione palla (quadrata) localparam PADDLE_OFFSET = 30; // Distanza dal bordo // ============================================================================ // PLL per generazione Clock 40 MHz (da 100 MHz) // Nota: Il PLL deve essere configurato in Quartus Prime // ============================================================================ wire pixel_clk; wire pll_locked; vga_pll pll_inst ( .refclk(clk), // 100 MHz input .rst(reset), .outclk_0(pixel_clk), // 40 MHz output .locked(pll_locked) ); assign VGA_CLK = pixel_clk; // ============================================================================ // Registri Configurabili via Avalon MM // ============================================================================ // Address Map: // 0x00: Paddle Left Y position (10 bit) // 0x04: Paddle Right Y position (10 bit) // 0x08: Ball X position (10 bit) // 0x0C: Ball Y position (10 bit) // 0x10: Paddle Left Color (RGB 24-bit) // 0x14: Paddle Right Color (RGB 24-bit) // 0x18: Ball Color (RGB 24-bit) // 0x1C: Background Color (RGB 24-bit) reg [9:0] paddle_left_y; // Posizione Y racchetta sinistra reg [9:0] paddle_right_y; // Posizione Y racchetta destra reg [9:0] ball_x; // Posizione X palla reg [9:0] ball_y; // Posizione Y palla reg [7:0] paddle_left_r, paddle_left_g, paddle_left_b; reg [7:0] paddle_right_r, paddle_right_g, paddle_right_b; reg [7:0] ball_r, ball_g, ball_b; reg [7:0] bg_r, bg_g, bg_b; // Posizioni X fisse delle racchette wire [9:0] paddle_left_x = PADDLE_OFFSET; wire [9:0] paddle_right_x = H_DISPLAY - PADDLE_OFFSET - PADDLE_WIDTH; // Avalon MM Write always @(posedge clk or posedge reset) begin if (reset) begin // Posizioni iniziali paddle_left_y <= 10'd260; // Centro schermo - metà altezza racchetta paddle_right_y <= 10'd260; ball_x <= 10'd395; // Centro schermo ball_y <= 10'd295; // Colori iniziali paddle_left_r <= 8'hFF; paddle_left_g <= 8'hFF; paddle_left_b <= 8'hFF; paddle_right_r <= 8'hFF; paddle_right_g <= 8'hFF; paddle_right_b <= 8'hFF; ball_r <= 8'hFF; ball_g <= 8'hFF; ball_b <= 8'h00; bg_r <= 8'h00; bg_g <= 8'h00; bg_b <= 8'h00; end else if (avs_write) begin case (avs_address) 5'h00: paddle_left_y <= avs_writedata[9:0]; 5'h01: paddle_right_y <= avs_writedata[9:0]; 5'h02: ball_x <= avs_writedata[9:0]; 5'h03: ball_y <= avs_writedata[9:0]; 5'h04: begin paddle_left_b <= avs_writedata[7:0]; paddle_left_g <= avs_writedata[15:8]; paddle_left_r <= avs_writedata[23:16]; end 5'h05: begin paddle_right_b <= avs_writedata[7:0]; paddle_right_g <= avs_writedata[15:8]; paddle_right_r <= avs_writedata[23:16]; end 5'h06: begin ball_b <= avs_writedata[7:0]; ball_g <= avs_writedata[15:8]; ball_r <= avs_writedata[23:16]; end 5'h07: begin bg_b <= avs_writedata[7:0]; bg_g <= avs_writedata[15:8]; bg_r <= avs_writedata[23:16]; end endcase end end // Avalon MM Read always @(*) begin case (avs_address) 5'h00: avs_readdata = {22'd0, paddle_left_y}; 5'h01: avs_readdata = {22'd0, paddle_right_y}; 5'h02: avs_readdata = {22'd0, ball_x}; 5'h03: avs_readdata = {22'd0, ball_y}; 5'h04: avs_readdata = {8'd0, paddle_left_r, paddle_left_g, paddle_left_b}; 5'h05: avs_readdata = {8'd0, paddle_right_r, paddle_right_g, paddle_right_b}; 5'h06: avs_readdata = {8'd0, ball_r, ball_g, ball_b}; 5'h07: avs_readdata = {8'd0, bg_r, bg_g, bg_b}; default: avs_readdata = 32'd0; endcase end // ============================================================================ // Contatori VGA // ============================================================================ reg [10:0] h_count; reg [9:0] v_count; always @(posedge pixel_clk or posedge reset) begin if (reset) begin h_count <= 11'd0; v_count <= 10'd0; end else begin if (h_count == H_TOTAL - 1) begin h_count <= 11'd0; if (v_count == V_TOTAL - 1) v_count <= 10'd0; else v_count <= v_count + 1'b1; end else begin h_count <= h_count + 1'b1; end end end // ============================================================================ // Generazione Segnali di Sincronizzazione // ============================================================================ wire h_sync_region = (h_count >= (H_DISPLAY + H_FRONT)) && (h_count < (H_DISPLAY + H_FRONT + H_SYNC)); wire v_sync_region = (v_count >= (V_DISPLAY + V_FRONT)) && (v_count < (V_DISPLAY + V_FRONT + V_SYNC)); assign VGA_HS = ~h_sync_region; // Negative polarity assign VGA_VS = ~v_sync_region; // Negative polarity // ============================================================================ // Area Visibile // ============================================================================ wire display_area = (h_count < H_DISPLAY) && (v_count < V_DISPLAY); assign VGA_BLANK_N = display_area; assign VGA_SYNC_N = 1'b0; // ============================================================================ // Coordinate Pixel Correnti // ============================================================================ wire [9:0] pixel_x = h_count[9:0]; wire [9:0] pixel_y = v_count[9:0]; // ============================================================================ // Logica di Rendering // ============================================================================ // Racchetta sinistra wire in_paddle_left_x = (pixel_x >= paddle_left_x) && (pixel_x < (paddle_left_x + PADDLE_WIDTH)); wire in_paddle_left_y = (pixel_y >= paddle_left_y) && (pixel_y < (paddle_left_y + PADDLE_HEIGHT)); wire draw_paddle_left = in_paddle_left_x && in_paddle_left_y; // Racchetta destra wire in_paddle_right_x = (pixel_x >= paddle_right_x) && (pixel_x < (paddle_right_x + PADDLE_WIDTH)); wire in_paddle_right_y = (pixel_y >= paddle_right_y) && (pixel_y < (paddle_right_y + PADDLE_HEIGHT)); wire draw_paddle_right = in_paddle_right_x && in_paddle_right_y; // Palla wire in_ball_x = (pixel_x >= ball_x) && (pixel_x < (ball_x + BALL_SIZE)); wire in_ball_y = (pixel_y >= ball_y) && (pixel_y < (ball_y + BALL_SIZE)); wire draw_ball = in_ball_x && in_ball_y; // Linea centrale tratteggiata (opzionale) localparam CENTER_LINE_X = H_DISPLAY / 2; localparam DASH_LENGTH = 20; wire [9:0] dash_pattern = pixel_y % (DASH_LENGTH * 2); wire draw_center_line = (pixel_x >= CENTER_LINE_X - 1) && (pixel_x <= CENTER_LINE_X + 1) && (dash_pattern < DASH_LENGTH); // ============================================================================ // Output RGB con priorità di rendering // ============================================================================ reg [7:0] vga_r_reg, vga_g_reg, vga_b_reg; always @(posedge pixel_clk) begin if (display_area) begin // Priorità: Palla > Racchette > Linea centrale > Sfondo if (draw_ball) begin vga_r_reg <= ball_r; vga_g_reg <= ball_g; vga_b_reg <= ball_b; end else if (draw_paddle_left) begin vga_r_reg <= paddle_left_r; vga_g_reg <= paddle_left_g; vga_b_reg <= paddle_left_b; end else if (draw_paddle_right) begin vga_r_reg <= paddle_right_r; vga_g_reg <= paddle_right_g; vga_b_reg <= paddle_right_b; end else if (draw_center_line) begin vga_r_reg <= 8'h80; // Grigio per linea centrale vga_g_reg <= 8'h80; vga_b_reg <= 8'h80; end else begin vga_r_reg <= bg_r; vga_g_reg <= bg_g; vga_b_reg <= bg_b; end end else begin vga_r_reg <= 8'h00; vga_g_reg <= 8'h00; vga_b_reg <= 8'h00; end end assign VGA_R = vga_r_reg; assign VGA_G = vga_g_reg; assign VGA_B = vga_b_reg; endmodule