Skip to content

Instantly share code, notes, and snippets.

@nickfox-taterli
Created January 11, 2024 09:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save nickfox-taterli/f8d622b43cc97b7e37ae137f05e9a203 to your computer and use it in GitHub Desktop.
Save nickfox-taterli/f8d622b43cc97b7e37ae137f05e9a203 to your computer and use it in GitHub Desktop.
Zynq HDMI Output
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/01/08 18:22:57
// Design Name:
// Module Name: ser_10to1
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module ser_10to1(
input reset,
input clk,
input clk_5x,
input [9:0] d,
output out
);
wire shift1;
wire shift2;
OSERDESE2 #(
.DATA_RATE_OQ("DDR"), // DDR, SDR
.DATA_RATE_TQ("SDR"), // DDR, BUF, SDR
.DATA_WIDTH(10), // Parallel data width (2-8,10,14)
.SERDES_MODE("MASTER"), // MASTER, SLAVE
.TBYTE_CTL("FALSE"), // Enable tristate byte operation (FALSE, TRUE)
.TBYTE_SRC("FALSE"), // Tristate byte source (FALSE, TRUE)
.TRISTATE_WIDTH(1)
)
OSERDESE2_M (
.CLK(clk_5x), // 1-bit input: High speed clock
.CLKDIV(clk), // 1-bit input: Divided clock
.OQ(out),
// D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
.D1(d[0]),
.D2(d[1]),
.D3(d[2]),
.D4(d[3]),
.D5(d[4]),
.D6(d[5]),
.D7(d[6]),
.D8(d[7]),
.RST(reset), // 1-bit input: Reset
.SHIFTIN1(shift1),
.SHIFTIN2(shift2),
// T1 - T4: 1-bit (each) input: Parallel 3-state inputs
.T1(0),
.T2(0),
.T3(0),
.T4(0),
.TBYTEIN(0), // 1-bit input: Byte group tristate
.TCE(0) // 1-bit input: 3-state clock enable
);
OSERDESE2 #(
.DATA_RATE_OQ("DDR"), // DDR, SDR
.DATA_RATE_TQ("SDR"), // DDR, BUF, SDR
.DATA_WIDTH(10), // Parallel data width (2-8,10,14)
.SERDES_MODE("SLAVE"), // MASTER, SLAVE
.TBYTE_CTL("FALSE"), // Enable tristate byte operation (FALSE, TRUE)
.TBYTE_SRC("FALSE"), // Tristate byte source (FALSE, TRUE)
.TRISTATE_WIDTH(1)
)
OSERDESE2_S (
.CLK(clk_5x), // 1-bit input: High speed clock
.CLKDIV(clk), // 1-bit input: Divided clock
// D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
.D1(0),
.D2(0),
.D3(d[8]),
.D4(d[9]),
.D5(0),
.D6(0),
.D7(0),
.D8(0),
.RST(reset), // 1-bit input: Reset
.SHIFTOUT1(shift1),
.SHIFTOUT2(shift2),
// T1 - T4: 1-bit (each) input: Parallel 3-state inputs
.T1(0),
.T2(0),
.T3(0),
.T4(0),
.TBYTEIN(0), // 1-bit input: Byte group tristate
.TCE(0) // 1-bit input: 3-state clock enable
);
endmodule
// TMDS 编码
module tmds_encoder(
input clk,
input resetn,
input [7:0] din,
input c0,
input c1,
input de,
output reg [9:0] q_out
);
localparam CTRLTOKEN0 = 10'b1101010100;
localparam CTRLTOKEN1 = 10'b0010101011;
localparam CTRLTOKEN2 = 10'b0101010100;
localparam CTRLTOKEN3 = 10'b1010101011;
reg [7:0] din_r; // 锁存输入的数据
reg [1:0] de_r,c0_r,c1_r; // 锁存输入的控制信号
reg [3:0] n1d,n1q_m,n0q_m; // 统计数据中的1个数,输出中间寄存器中的1和0个数.
reg [5:0] cnt; // 记录上一次传输时候1比0多了多少个,初始发送时会被控制信号设置为0.
reg [8:0] q_m_r; // 缓存中间变量
wire [8:0] q_m; // 中间变量
wire condition1;
wire condition2;
wire condition3;
//统计待编码输入数据中1的个数,最多8个1,所以位宽为4.
always@(posedge clk)
begin
if(!resetn)
begin
n1d <= 4'd0;
end
else if(de)
begin
n1d <= din[0] + din[1] + din[2] + din[3] + din[4] + din[5] + din[6] + din[7];
end
else begin // DE为低时候传输的是控制字符,硬编码的,没有统计必要.
n1d <= 4'd0;
end
end
// 前面统计数据时候打了一拍,这里也要打一拍来同步.
always@(posedge clk)begin
din_r <= din;
de_r <= {de_r[0],de};
c0_r <= {c0_r[0],c0};
c1_r <= {c1_r[0],c1};
q_m_r <= q_m;
end
//判断条件1:输入数据1的个数多余4或者1的个数等于4并且最低位为0时拉高,其余时间拉低.
assign condition1 = ((n1d > 4'd4) || ((n1d == 4'd4) && (~din_r[0])));
//对输入的信号进行XOR/XNOR运算.
assign q_m[0] = din_r[0];
assign q_m[1] = condition1 ? ~((q_m[0] ^ din_r[1])) : (q_m[0] ^ din_r[1]);
assign q_m[2] = condition1 ? ~((q_m[1] ^ din_r[2])) : (q_m[1] ^ din_r[2]);
assign q_m[3] = condition1 ? ~((q_m[2] ^ din_r[3])) : (q_m[2] ^ din_r[3]);
assign q_m[4] = condition1 ? ~((q_m[3] ^ din_r[4])) : (q_m[3] ^ din_r[4]);
assign q_m[5] = condition1 ? ~((q_m[4] ^ din_r[5])) : (q_m[4] ^ din_r[5]);
assign q_m[6] = condition1 ? ~((q_m[5] ^ din_r[6])) : (q_m[5] ^ din_r[6]);
assign q_m[7] = condition1 ? ~((q_m[6] ^ din_r[7])) : (q_m[6] ^ din_r[7]);
assign q_m[8] = ~condition1;
always@(posedge clk)
begin
if(!resetn)
begin
n1q_m <= 4'd0;
n0q_m <= 4'd0;
end
else if(de_r[0])begin // 实际DE有效时统计,否则就是前面说的硬编码的.
n1q_m <= q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7];
n0q_m <= 4'd8 - (q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]);
end
else begin//输入数据无效时清零。
n1q_m <= 4'd0;
n0q_m <= 4'd0;
end
end
//判断条件2:一行已编码数据(上一次传输会更新Cnt)中1的个数等于0的个数或者本次编码数据中1的个数等于0的个数.
assign condition2 = ((cnt == 6'd0) || (n1q_m == n0q_m));
//判断条件3:已编码数据中1的多余0并且本次编码中间数据1的个数也多与0的个数或者已编码数据中0的个数较多并且此次编码中0的个数也比较多时拉高,其余时间拉低,为什么判断bit5,因为对于我们看,他就是符号位.
assign condition3 = (((~cnt[5]) && (n1q_m > n0q_m)) || (cnt[5] && (n1q_m < n0q_m)));
always@(posedge clk)
begin
if(!resetn)
begin
cnt <= 6'd0;
q_out <= 10'd0;
end
else if(de_r[1]) //又打了一拍之后,de_r[1]就是原来的de.
begin
q_out[8] <= q_m_r[8]; //第8位为编码方式位,直接输出即可.
if(condition2)
begin
q_out[9] <= ~q_m_r[8];
q_out[7:0] <= q_m_r[8] ? q_m_r[7:0] : ~q_m_r[7:0];
// 按照规范更新Cnt.
cnt <= q_m_r[8] ? (cnt + n1q_m - n0q_m) : (cnt + n0q_m - n1q_m);
end
else if(condition3)
begin
q_out[9] <= 1'b1;
q_out[7:0] <= ~q_m_r[7:0];
// 按照规范更新Cnt.
cnt <= cnt + {q_m_r[8],1'b0} + n0q_m - n1q_m;
end
else
begin
q_out[9] <= 1'b0;
q_out[7:0] <= q_m_r[7:0];
// 按照规范更新Cnt.
cnt <= cnt - {~q_m_r[8],1'b0} + n1q_m - n0q_m;
end
end
else
begin
// DE = 0时,需要设置Cnt = 0.
cnt <= 6'd0;
case ({c1_r[1],c0_r[1]})
2'b00 : q_out <= CTRLTOKEN0;
2'b01 : q_out <= CTRLTOKEN1;
2'b10 : q_out <= CTRLTOKEN2;
2'b11 : q_out <= CTRLTOKEN3;
endcase
end
end
endmodule
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/01/08 14:37:31
// Design Name:
// Module Name: top
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module top(
input clk,
input resetn,
output tmds_clk_p,
output tmds_clk_n,
output [2:0] tmds_data_p,
output [2:0] tmds_data_n
);
wire hs;
wire vs;
wire de;
wire [11:0] active_x;
wire [11:0] active_y;
wire [9:0] tmds_blue;
wire [9:0] tmds_green;
wire [9:0] tmds_red;
wire clk_pclk;
wire clk_pclk_5x;
wire clk_locked;
wire [2:0] tmds_data;
wire tmds_clk;
wire reset0_n = resetn & clk_locked;
reg [24:0] color;
// Color Gen
always @(posedge clk_pclk)
begin
if(active_x < 300 && active_y < 300)
color <= 24'h0000ff;
else if(active_x < 600 && active_y < 600)
color <= 24'h00ff00;
else if(active_x < 1000 && active_y < 1000)
color <= 24'hff0000;
else
color <= color;
end
clk_wiz_0 clk_wiz_inst(
.clk_out1(clk_pclk),
.clk_out2(clk_pclk_5x),
.reset(~resetn),
.locked(clk_locked),
.clk_in1(clk)
);
tmds_encoder tmds_encoder_blue(
.clk(clk_pclk),
.resetn(reset0_n),
.din(color[7:0]),
.c0(hs),
.c1(vs),
.de(de),
.q_out(tmds_blue)
);
tmds_encoder tmds_encoder_green(
.clk(clk_pclk),
.resetn(reset0_n),
.din(color[15:8]),
.c0(0),
.c1(0),
.de(de),
.q_out(tmds_green)
);
tmds_encoder tmds_encoder_red(
.clk(clk_pclk),
.resetn(reset0_n),
.din(color[23:16]),
.c0(0),
.c1(0),
.de(de),
.q_out(tmds_red)
);
// VGA ʱ������
vga_timing vga_timing_inst(
.clk(clk_pclk),
.resetn(reset0_n),
.hs(hs),
.vs(vs),
.de(de),
.active_x(active_x),
.active_y(active_y)
);
ser_10to1 ser_blue(
.reset(!reset0_n),
.clk(clk_pclk),
.clk_5x(clk_pclk_5x),
.d(tmds_blue),
.out(tmds_data[0])
);
ser_10to1 ser_green(
.reset(!reset0_n),
.clk(clk_pclk),
.clk_5x(clk_pclk_5x),
.d(tmds_green),
.out(tmds_data[1])
);
ser_10to1 ser_red(
.reset(!reset0_n),
.clk(clk_pclk),
.clk_5x(clk_pclk_5x),
.d(tmds_red),
.out(tmds_data[2])
);
ser_10to1 ser_clk(
.reset(!reset0_n),
.clk(clk_pclk),
.clk_5x(clk_pclk_5x),
.d(10'b1111100000),
.out(tmds_clk)
);
OBUFDS #(
.IOSTANDARD("TMDS_33")
) tmds_diff_0 (
.O(tmds_data_p[0]), // Diff_p output (connect directly to top-level port)
.OB(tmds_data_n[0]), // Diff_n output (connect directly to top-level port)
.I(tmds_data[0]) // Buffer input
);
OBUFDS #(
.IOSTANDARD("TMDS_33")
) tmds_diff_1 (
.O(tmds_data_p[1]), // Diff_p output (connect directly to top-level port)
.OB(tmds_data_n[1]), // Diff_n output (connect directly to top-level port)
.I(tmds_data[1]) // Buffer input
);
OBUFDS #(
.IOSTANDARD("TMDS_33")
) tmds_diff_2 (
.O(tmds_data_p[2]), // Diff_p output (connect directly to top-level port)
.OB(tmds_data_n[2]), // Diff_n output (connect directly to top-level port)
.I(tmds_data[2]) // Buffer input
);
OBUFDS #(
.IOSTANDARD("TMDS_33")
) tmds_diff_clk (
.O(tmds_clk_p), // Diff_p output (connect directly to top-level port)
.OB(tmds_clk_n), // Diff_n output (connect directly to top-level port)
.I(tmds_clk) // Buffer input
);
endmodule
// VGA 时序生成
module vga_timing#(
parameter integer H_ACTIVE = 16'd2560,
parameter integer H_FP = 16'd8,
parameter integer H_SYNC = 16'd32,
parameter integer H_BP = 16'd40,
parameter integer V_ACTIVE = 16'd1440,
parameter integer V_FP = 16'd17,
parameter integer V_SYNC = 16'd8,
parameter integer V_BP = 16'd6
)(
input clk,
input resetn,
output hs,
output vs,
output de,
output reg [11:0] active_x, // Video Position (Early 1 PCLK)
output reg [11:0] active_y
);
parameter integer H_TOTAL = H_ACTIVE + H_FP + H_SYNC + H_BP;
parameter integer V_TOTAL = V_ACTIVE + V_FP + V_SYNC + V_BP;
wire h_active;
wire v_active;
reg [11:0] h_cnt;
reg [11:0] v_cnt;
assign de = h_active & v_active;
// Horizontal Counter
always @ (posedge clk or negedge resetn)
begin
if(!resetn)
h_cnt <= 0;
else if(h_cnt == H_TOTAL - 1)
h_cnt <= 0;
else
h_cnt <= h_cnt + 1;
end
// Vertical Counter
always @ (posedge clk or negedge resetn)
begin
if(!resetn)
v_cnt <= 0;
else if(h_cnt == H_FP - 1) // Horizontal Sync Time
if(v_cnt == V_TOTAL - 1)
v_cnt <= 0;
else
v_cnt <= v_cnt + 1;
else
v_cnt <= v_cnt;
end
// Position Counter
always @ (posedge clk)
begin
if(h_active)
active_x <= h_cnt - H_SYNC - H_BP;
else
active_x <= 0;
end
always @ (posedge clk)
begin
if(v_active)
active_y <= v_cnt - V_SYNC - V_BP;
else
active_y <= active_y;
end
// Horizontal Active
assign h_active = ((h_cnt >= H_SYNC + H_BP) && (h_cnt < H_SYNC + H_BP + H_ACTIVE))?1'b1:1'b0;
// Vertical Active
assign v_active = ((v_cnt >= V_SYNC + V_BP) && (v_cnt < V_SYNC + V_BP + V_ACTIVE))?1'b1:1'b0;
// HS Generate
assign hs = (h_cnt > H_SYNC)?1'b0:1'b1;
// VS Generate
assign vs = (v_cnt > V_SYNC)?1'b0:1'b1;
endmodule
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment