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>
237 lines
8.1 KiB
Verilog
Executable File
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
|