ec82764bef
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>
164 lines
5.4 KiB
Systemverilog
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
|