Files
learnFPGAProject/RTL/DEVICES/GFX_hdmi.v
2025-11-27 04:28:54 +03:00

155 lines
5.0 KiB
Verilog

// Define one of:
// MODE_640x480, MODE_800x600, MODE_1024x768, MODE_1280x1024.
// ("physical mode" sent to the HDMI)
`include "TMDS_encoder.v"
// Generate HDMI signal from VGA signal
module GFX_hdmi(
input wire pixel_clk, // pixel clock
input wire pixel_clk_x5, // 5 times pixel clock freq (used by TMDS serializer)
// The TMDS serializers operate at (pixel_clock_freq * 10),
// but we use DDR mode, hence (pixel_clock_freq * 5).
input wire [7:0] R,
input wire [7:0] G,
input wire [7:0] B,
input wire hsync,
input wire vsync,
input wire draw_area,
output wire [3:0] gpdi_dp // HDMI signals, blue, green, red, clock
// dgpi_dn generated by pins (see, e.g., ulx3s.lpf)
);
// RGB TMDS encoding
// Generate 10-bits TMDS red,green,blue signals. Blue embeds HSync/VSync in its
// control part.
wire [9:0] TMDS_R, TMDS_G, TMDS_B;
TMDS_encoder encode_R(.clk(pixel_clk), .VD(R), .CD(2'b00) , .VDE(draw_area), .TMDS(TMDS_R));
TMDS_encoder encode_G(.clk(pixel_clk), .VD(G), .CD(2'b00) , .VDE(draw_area), .TMDS(TMDS_G));
TMDS_encoder encode_B(.clk(pixel_clk), .VD(B), .CD({vsync,hsync}), .VDE(draw_area), .TMDS(TMDS_B));
// Modulo-5 clock divider.
reg [4:0] TMDS_mod5=1;
wire TMDS_shift_load = TMDS_mod5[4];
always @(posedge pixel_clk_x5) TMDS_mod5 <= {TMDS_mod5[3:0],TMDS_mod5[4]};
// Shifters
// Every 5 clocks, we get a fresh R,G,B triplet from the TMDS encoders,
// else we shift.
reg [9:0] TMDS_shift_R=0, TMDS_shift_G=0, TMDS_shift_B=0;
always @(posedge pixel_clk_x5) begin
TMDS_shift_R <= TMDS_shift_load ? TMDS_R : {2'b00,TMDS_shift_R[9:2]};
TMDS_shift_G <= TMDS_shift_load ? TMDS_G : {2'b00,TMDS_shift_G[9:2]};
TMDS_shift_B <= TMDS_shift_load ? TMDS_B : {2'b00,TMDS_shift_B[9:2]};
end
// DDR serializers: they send D0 at the rising edge and D1 at the falling edge.
`ifndef BENCH_OR_LINT
`ifdef ULX3S
ODDRX1F ddr_R (.D0(TMDS_shift_R[0]), .D1(TMDS_shift_R[1]), .Q(gpdi_dp[2]), .SCLK(pixel_clk_x5), .RST(1'b0));
ODDRX1F ddr_G (.D0(TMDS_shift_G[0]), .D1(TMDS_shift_G[1]), .Q(gpdi_dp[1]), .SCLK(pixel_clk_x5), .RST(1'b0));
ODDRX1F ddr_B (.D0(TMDS_shift_B[0]), .D1(TMDS_shift_B[1]), .Q(gpdi_dp[0]), .SCLK(pixel_clk_x5), .RST(1'b0));
`endif
`endif
// The pixel clock is sent through the fourth differential pair.
assign gpdi_dp[3] = pixel_clk;
endmodule
/**************************************************************************************/
`ifdef BENCH_OR_LINT
module GFX_PLL(
input wire pclk, // the board's clock
output wire pixel_clk, // pixel clock
output wire pixel_clk_x5 // 5 times pixel clock freq (used by TMDS serializer)
);
assign pixel_clk = pclk;
assign pixel_clk_x5 = pclk;
endmodule
`else
`ifdef ULX3S
module GFX_PLL(
input wire pclk, // the board's clock
output wire pixel_clk, // pixel clock
output wire pixel_clk_x5 // 5 times pixel clock freq (used by TMDS serializer)
// The TMDS serializers operate at (pixel_clock_freq * 10),
// but we use DDR mode, hence (pixel_clock_freq * 5).
);
// The parameters of the PLL,
// They are found by using: ecppll -i 25 -o <5*pixel_clock> -f foobar.v
`ifdef MODE_640x480
localparam CLKI_DIV = 1;
localparam CLKOP_DIV = 5;
localparam CLKOP_CPHASE = 2;
localparam CLKOP_FPHASE = 0;
localparam CLKFB_DIV = 5;
`endif
`ifdef MODE_800x600
localparam CLKI_DIV = 1;
localparam CLKOP_DIV = 3;
localparam CLKOP_CPHASE = 1;
localparam CLKOP_FPHASE = 0;
localparam CLKFB_DIV = 8;
`endif
`ifdef MODE_1024x768
localparam CLKI_DIV = 1;
localparam CLKOP_DIV = 2;
localparam CLKOP_CPHASE = 1;
localparam CLKOP_FPHASE = 0;
localparam CLKFB_DIV = 13;
`endif
`ifdef MODE_1280x1024
localparam CLKI_DIV = 3;
localparam CLKOP_DIV = 1;
localparam CLKOP_CPHASE = 0;
localparam CLKOP_FPHASE = 0;
localparam CLKFB_DIV = 65;
`endif
// The PLL converts a 25 MHz clock into a (pixel_clock_freq * 5) clock
// The (half) TMDS serializer clock is generated on pin CLKOP.
// In addition, the pixel clock (at TMDS freq/5) is generated on
// pin CLKOS (hence CLKOS_DIV = 5*CLKOP_DIV).
(* ICP_CURRENT="12" *) (* LPF_RESISTOR="8" *) (* MFG_ENABLE_FILTEROPAMP="1" *) (* MFG_GMCREF_SEL="2" *)
EHXPLLL #(
.CLKI_DIV(CLKI_DIV),
.CLKOP_DIV(CLKOP_DIV),
.CLKOP_CPHASE(CLKOP_CPHASE),
.CLKOP_FPHASE(CLKOP_FPHASE),
.CLKOS_ENABLE("ENABLED"),
.CLKOS_DIV(5*CLKOP_DIV),
.CLKOS_CPHASE(CLKOP_CPHASE),
.CLKOS_FPHASE(CLKOP_FPHASE),
.CLKFB_DIV(CLKFB_DIV)
) pll_i (
.CLKI(pclk),
.CLKOP(pixel_clk_x5),
.CLKFB(pixel_clk_x5),
.CLKOS(pixel_clk),
.PHASESEL0(1'b0),
.PHASESEL1(1'b0),
.PHASEDIR(1'b1),
.PHASESTEP(1'b1),
.PHASELOADREG(1'b1),
.PLLWAKESYNC(1'b0),
.ENCLKOP(1'b0)
);
endmodule
`endif
`endif