Files
retroDE_ps2/rtl/platform/platform_video_stub.sv
thejayman77 ec82764bef Initial commit: retroDE_ps2 — first-of-its-kind PS2 GS FPGA core (DE25-Nano / Agilex 5)
RTL (GS rasterizer, EE core stub, platform bridge, LPDDR4B path), sim regression
(272 TBs), docs, and tooling. Copyrighted PS2 content (BIOS, game code, GS dumps,
and all dump-derived textures/traces) is excluded via .gitignore and stays local.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 20:10:50 -04:00

164 lines
5.4 KiB
Systemverilog

// retroDE_ps2 — platform_video_stub
//
// Smallest retroDE-facing video adapter needed for Milestone A. Accepts a
// flat pixel source (bg_{r,g,b}) from gs_stub and generates a free-running
// VGA-style raster with configurable timing. Wave 1 produces a flood-fill
// frame at the current BGCOLOR — enough to prove the platform video path
// end-to-end without waiting for real GS/PCRTC behavior.
//
// Contract refs:
// docs/stub_module_plan.md (Wave 1, item 5)
// docs/contracts/platform.md
//
// Default timing is VGA 640x480 @ 25.175 MHz pixel clock. Testbenches
// typically override to tiny values (e.g. 16x8 with minimal porches) to
// keep simulation turnaround short.
//
// Replacement path: this module remains as the platform adaptation layer
// while the upstream pixel source evolves from gs_stub to fuller GS/PCRTC
// output.
//
// Trace payload schema:
// PLAT MODE arg0=frame_number arg1=pixels_per_frame arg2=- arg3=-
// emitted once per frame on vsync rising edge, so testbenches can count
// frames without sampling raw video signals.
`timescale 1ns/1ps
module platform_video_stub
import trace_pkg::*;
#(
// Horizontal timing (in pixel clocks)
parameter int H_ACTIVE = 640,
parameter int H_FRONT = 16,
parameter int H_SYNC = 96,
parameter int H_BACK = 48,
// Vertical timing (in line counts)
parameter int V_ACTIVE = 480,
parameter int V_FRONT = 10,
parameter int V_SYNC = 2,
parameter int V_BACK = 33,
// Sync polarity. VGA 640x480 is active-low on both.
parameter bit HSYNC_ACTIVE_LOW = 1'b1,
parameter bit VSYNC_ACTIVE_LOW = 1'b1
) (
input logic clk, // pixel clock
input logic rst_n,
// Pixel source from gs_stub
input logic [7:0] bg_r,
input logic [7:0] bg_g,
input logic [7:0] bg_b,
// Platform-facing video
output logic hsync,
output logic vsync,
output logic de,
output logic [7:0] r,
output logic [7:0] g,
output logic [7:0] b,
// Trace
output logic ev_valid,
output subsys_e ev_subsys,
output event_e ev_event,
output logic [63:0] ev_arg0,
output logic [63:0] ev_arg1,
output logic [63:0] ev_arg2,
output logic [63:0] ev_arg3,
output logic [31:0] ev_flags
);
localparam int H_TOTAL = H_ACTIVE + H_FRONT + H_SYNC + H_BACK;
localparam int V_TOTAL = V_ACTIVE + V_FRONT + V_SYNC + V_BACK;
localparam int H_SYNC_START = H_ACTIVE + H_FRONT;
localparam int H_SYNC_END = H_SYNC_START + H_SYNC;
localparam int V_SYNC_START = V_ACTIVE + V_FRONT;
localparam int V_SYNC_END = V_SYNC_START + V_SYNC;
localparam int HCNT_W = $clog2(H_TOTAL);
localparam int VCNT_W = $clog2(V_TOTAL);
logic [HCNT_W-1:0] hcnt;
logic [VCNT_W-1:0] vcnt;
// ------------------------------------------------------------------
// Raster counters
// ------------------------------------------------------------------
logic end_of_line;
logic end_of_frame;
assign end_of_line = (hcnt == HCNT_W'(H_TOTAL - 1));
assign end_of_frame = end_of_line && (vcnt == VCNT_W'(V_TOTAL - 1));
always_ff @(posedge clk) begin
if (!rst_n) begin
hcnt <= '0;
vcnt <= '0;
end else if (end_of_line) begin
hcnt <= '0;
vcnt <= end_of_frame ? '0 : (vcnt + VCNT_W'(1));
end else begin
hcnt <= hcnt + HCNT_W'(1);
end
end
// ------------------------------------------------------------------
// Sync + data-enable + pixel colour
// ------------------------------------------------------------------
logic active_h;
logic active_v;
logic in_hsync;
logic in_vsync;
assign active_h = (hcnt < HCNT_W'(H_ACTIVE));
assign active_v = (vcnt < VCNT_W'(V_ACTIVE));
assign in_hsync = (hcnt >= HCNT_W'(H_SYNC_START)) && (hcnt < HCNT_W'(H_SYNC_END));
assign in_vsync = (vcnt >= VCNT_W'(V_SYNC_START)) && (vcnt < VCNT_W'(V_SYNC_END));
assign hsync = HSYNC_ACTIVE_LOW ? ~in_hsync : in_hsync;
assign vsync = VSYNC_ACTIVE_LOW ? ~in_vsync : in_vsync;
assign de = active_h && active_v;
assign r = de ? bg_r : 8'd0;
assign g = de ? bg_g : 8'd0;
assign b = de ? bg_b : 8'd0;
// ------------------------------------------------------------------
// Trace: one EV_MODE pulse per completed frame.
// ------------------------------------------------------------------
logic [31:0] frame_count;
always_ff @(posedge clk) begin
if (!rst_n) begin
frame_count <= 32'd0;
ev_valid <= 1'b0;
ev_subsys <= SUBSYS_PLAT;
ev_event <= EV_MODE;
ev_arg0 <= 64'd0;
ev_arg1 <= 64'd0;
ev_arg2 <= 64'd0;
ev_arg3 <= 64'd0;
ev_flags <= 32'd0;
end else if (end_of_frame) begin
frame_count <= frame_count + 32'd1;
ev_valid <= 1'b1;
ev_subsys <= SUBSYS_PLAT;
ev_event <= EV_MODE;
ev_arg0 <= {32'd0, frame_count};
ev_arg1 <= {{(64-32){1'b0}}, 32'(H_ACTIVE * V_ACTIVE)};
ev_arg2 <= 64'd0;
ev_arg3 <= 64'd0;
ev_flags <= 32'd0;
end else begin
ev_valid <= 1'b0;
end
end
endmodule : platform_video_stub