Files
retroDE_ps2/rtl/platform/I2C_HDMI_Config.v
T
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

237 lines
8.1 KiB
Verilog
Executable File

// ============================================================================
// I2C_HDMI_Config.v — ADV7513 HDMI transmitter configuration via I2C
// ============================================================================
//
// Derived from Terasic DE-series reference design (I2C_HDMI_Config.v).
// Original copyright belongs to Terasic Technologies Inc.; this file is
// distributed under the terms of the Terasic Reference Design license that
// ships with the DE25-Nano System CD (free use on Terasic hardware,
// copyright notice retained).
//
// retroDE modifications (2025-2026):
// - LUT_SIZE expanded to 38 entries
// - Audio configuration for I2S input @ 48 kHz, MCLK 12.288 MHz
// - HPD override (0xD6 = 0xC0) for monitors that misreport hot-plug
// - AVI InfoFrame configured for full-range RGB 444 output
// - Comments documenting each ADV7513 register write
//
// ============================================================================
`timescale 1ns/1ps
module I2C_HDMI_Config ( // Host Side
iCLK,
iRST_N,
// I2C Side
I2C_SCLK,
I2C_SDAT,
HDMI_TX_INT,
READY,
// Ch166: sticky NACK watchdog
ERROR
);
// Host Side
input iCLK;
input iRST_N;
// I2C Side: SCL is actively driven by the master; SDA is open-drain
// (master drives low / releases to 1'bz; slave drives ACK).
output I2C_SCLK;
inout I2C_SDAT;
input HDMI_TX_INT;
output READY ;
// Ch166: ERROR latches HIGH if the same LUT entry NACKs
// NACK_LIMIT consecutive times (chip absent, address wrong,
// bus shorted). Sticky until iRST_N. Cleared on reset.
output ERROR;
// Internal Registers/Wires
reg [15:0] mI2C_CLK_DIV;
reg [23:0] mI2C_DATA;
reg mI2C_CTRL_CLK;
reg mI2C_GO;
wire mI2C_END;
wire mI2C_ACK;
reg [15:0] LUT_DATA;
reg [5:0] LUT_INDEX;
reg [3:0] mSetup_ST;
reg READY ;
// Clock Setting
parameter CLK_Freq = 50000000; // 50 MHz
parameter I2C_Freq = 20000; // 20 KHz
// LUT Data Number
parameter LUT_SIZE = 38;
// Ch166 - NACK watchdog threshold (consecutive retries on the
// same LUT entry before ERROR latches). At I2C_Freq=20 kHz a
// full byte transaction is ~1.5 ms, so 16 retries ~= 24 ms before
// we declare the bus dead - generous enough for real-world bus
// settling but well short of a stuck-LED user complaint.
parameter NACK_LIMIT = 16;
///////////////////// I2C Control Clock ////////////////////////
always@(posedge iCLK or negedge iRST_N)
begin
if(!iRST_N)
begin
mI2C_CTRL_CLK <= 0;
mI2C_CLK_DIV <= 0;
end
else
begin
if( mI2C_CLK_DIV < (CLK_Freq/I2C_Freq) )
mI2C_CLK_DIV <= mI2C_CLK_DIV+1;
else
begin
mI2C_CLK_DIV <= 0;
mI2C_CTRL_CLK <= ~mI2C_CTRL_CLK;
end
end
end
////////////////////////////////////////////////////////////////////
I2C_Controller u0 ( .CLK(mI2C_CTRL_CLK), // Controller work clock
.CLK_EN(1'b1), // Advance every controller clock
.CLK_PHASE(mI2C_CTRL_CLK), // Phase for SCL generation
.I2C_SCLK(I2C_SCLK), // I2C CLOCK
.I2C_SDAT(I2C_SDAT), // I2C DATA
.I2C_DATA(mI2C_DATA), // DATA:[SLAVE_ADDR,SUB_ADDR,DATA]
.GO(mI2C_GO), // GO transfor
.END(mI2C_END), // END transfor
.W_R(1'b0), // Ch165 audit Low — tie retained-compat port off (always WRITE)
.ACK(mI2C_ACK), // ACK
.RESET(iRST_N) );
////////////////////////////////////////////////////////////////////
////////////////////// Config Control ////////////////////////////
always@(posedge mI2C_CTRL_CLK or negedge iRST_N)
begin
if(!iRST_N)
begin
READY <= 0;
LUT_INDEX <= 0;
mSetup_ST <= 0;
mI2C_GO <= 0;
end
else
begin
if(LUT_INDEX<LUT_SIZE)
begin
READY<=0;
case(mSetup_ST)
0: begin
mI2C_DATA <= {8'h72,LUT_DATA};
mI2C_GO <= 1;
mSetup_ST <= 1;
end
1: begin
if(mI2C_END)
begin
if(!mI2C_ACK)
mSetup_ST <= 2;
else
mSetup_ST <= 0;
mI2C_GO <= 0;
end
end
2: begin
LUT_INDEX <= LUT_INDEX+1;
mSetup_ST <= 0;
end
endcase
end
else
begin
READY<=1;
if(!HDMI_TX_INT)
begin
LUT_INDEX <= 0;
end
else
LUT_INDEX <= LUT_INDEX;
end
end
end
////////////////////////////////////////////////////////////////////
////////////////// Ch166 NACK watchdog (sticky) //////////////////
//
// Counts consecutive NACK retries on the *current* LUT entry.
// In the config FSM above, state 1 sees mI2C_END at the end of
// each I2C transaction; if mI2C_ACK is HIGH (slave didn't drive
// the ACK bit LOW), the FSM bounces back to state 0 and retries
// the same LUT_DATA. State 2 means the byte ACKed and LUT_INDEX
// is about to advance, so we clear the retry count there. Once
// the count hits NACK_LIMIT, ERROR latches HIGH (sticky until
// iRST_N) so the top level can surface a stuck bus on an LED.
reg [7:0] nack_retries;
reg error_latched;
always @(posedge mI2C_CTRL_CLK or negedge iRST_N)
begin
if (!iRST_N)
begin
nack_retries <= 0;
error_latched <= 1'b0;
end
else
begin
if (mSetup_ST == 1 && mI2C_END && mI2C_ACK)
begin
nack_retries <= nack_retries + 1;
if (nack_retries == NACK_LIMIT - 1)
error_latched <= 1'b1;
end
else if (mSetup_ST == 2)
begin
nack_retries <= 0;
end
end
end
assign ERROR = error_latched;
////////////////////////////////////////////////////////////////////
///////////////////// Config Data LUT //////////////////////////
always@(*)
begin
case(LUT_INDEX)
// Video Config Data
00 : LUT_DATA <= 16'h9803; //Must be set to 0x03 for proper operation
01 : LUT_DATA <= 16'hD6C0; //HPD override: force HPD always-high (bits[7:6]=11)
02 : LUT_DATA <= 16'h0100; //Set 'N' value at 6144
03 : LUT_DATA <= 16'h0218; //Set 'N' value at 6144
04 : LUT_DATA <= 16'h0300; //Set 'N' value at 6144
05 : LUT_DATA <= 16'h0a01; //MCLK ratio = 256x fs (12.288 MHz / 48 kHz)
06 : LUT_DATA <= 16'h0b2e; //MCLK Active
07 : LUT_DATA <= 16'h0cbc; //Serial Audio standard i2s, R0x0C[1:0] = '00
08 : LUT_DATA <= 16'h1402; //Audio Word Length 16 bit, stereo (2 channels)
09 : LUT_DATA <= 16'h1520; //Input 444 (RGB or YCrCb) with Separate Syncs, 48kHz fs
10 : LUT_DATA <= 16'h1630; //Output format 444, 24-bit input
11 : LUT_DATA <= 16'h1846; //Disable CSC
12 : LUT_DATA <= 16'h4080; //General control packet enable
13 : LUT_DATA <= 16'h4110; //Power down control
14 : LUT_DATA <= 16'h49A8; //Set dither mode - 12-to-10 bit
15 : LUT_DATA <= 16'h5510; //AVI InfoFrame byte 1: Y=RGB, A0=active fmt valid
16 : LUT_DATA <= 16'h5608; //AVI InfoFrame byte 2: active format aspect
17 : LUT_DATA <= 16'h5708; //AVI InfoFrame byte 3: Q=10 (full range RGB 0-255)
18 : LUT_DATA <= 16'h94C0; //INT enable 1: HPD + monitor sense only
19 : LUT_DATA <= 16'h9500; //INT enable 2: all disabled
20 : LUT_DATA <= 16'h96C0; //Clear HPD + monitor sense status (matches 0x94 enable mask)
21 : LUT_DATA <= 16'h7301; //Info frame Ch count = 2 (stereo)
22 : LUT_DATA <= 16'h7600; //Speaker allocation: FL+FR (stereo)
23 : LUT_DATA <= 16'h9803; //Must be set to 0x03 for proper operation
24 : LUT_DATA <= 16'h9902; //Must be set to Default Value
25 : LUT_DATA <= 16'h9ae0; //Must be set to 0b1110000
26 : LUT_DATA <= 16'h9c30; //PLL filter R1 value
27 : LUT_DATA <= 16'h9d61; //Set clock divide
28 : LUT_DATA <= 16'ha2a4; //Must be set to 0xA4 for proper operation
29 : LUT_DATA <= 16'ha3a4; //Must be set to 0xA4 for proper operation
30 : LUT_DATA <= 16'ha504; //Must be set to Default Value
31 : LUT_DATA <= 16'hab40; //Must be set to Default Value
32 : LUT_DATA <= 16'haf16; //Select HDMI mode
33 : LUT_DATA <= 16'hba60; //No clock delay
34 : LUT_DATA <= 16'hd1ff; //Must be set to Default Value
35 : LUT_DATA <= 16'hde10; //Must be set to Default for proper operation
36 : LUT_DATA <= 16'he460; //Must be set to Default Value
37 : LUT_DATA <= 16'hfa7d; //Nbr of times to look for good phase
default: LUT_DATA <= 16'h9803;
endcase
end
////////////////////////////////////////////////////////////////////
endmodule