第43讲:HDMI显示器驱动设计与验证
理论部分
设计与实现
实验目标:通过HDMI插显示屏显示彩条
encode
`timescale 1ns/1ns module encode ( input wire sys_clk , //时钟信号 input wire sys_rst_n , //复位信号,低有效 input wire [7:0] data_in , //输入8bit待编码数据 input wire c0 , //控制信号c0 input wire c1 , //控制信号c1 input wire de , //使能信号 output reg [9:0] data_out //输出编码后的10bit数据 ); //parameter define parameter DATA_OUT0 = 10'b1101010100, DATA_OUT1 = 10'b0010101011, DATA_OUT2 = 10'b0101010100, DATA_OUT3 = 10'b1010101011; //wire define wire condition_1 ; //条件1 wire condition_2 ; //条件2 wire condition_3 ; //条件3 wire [8:0] q_m ; //第一阶段转换后的9bit数据 //reg define reg [3:0] data_in_n1 ; //待编码数据中1的个数 reg [7:0] data_in_reg ; //待编码数据打一拍 reg [3:0] q_m_n1 ; //转换后9bit数据中1的个数 reg [3:0] q_m_n0 ; //转换后9bit数据中0的个数 reg [4:0] cnt ; //视差计数器,0-1个数差别,最高位为符号位 reg de_reg1 ; //使能信号打一拍 reg de_reg2 ; //使能信号打两拍 reg c0_reg1 ; //控制信号c0打一拍 reg c0_reg2 ; //控制信号c0打两拍 reg c1_reg1 ; //控制信号c1打一拍 reg c1_reg2 ; //控制信号c1打两拍 reg [8:0] q_m_reg ; //q_m信号打一拍 //data_in_n1:待编码数据中1的个数 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) data_in_n1 <= 4'd0; else data_in_n1 <= data_in[0] + data_in[1] + data_in[2] + data_in[3] + data_in[4] + data_in[5] + data_in[6] + data_in[7]; //data_in_reg:待编码数据打一拍 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) data_in_reg <= 8'b0; else data_in_reg <= data_in; //condition_1:条件1 assign condition_1 = ((data_in_n1 > 4'd4) || ((data_in_n1 == 4'd4) && (data_in_reg[0] == 1'b0))); //q_m:第一阶段转换后的9bit数据 assign q_m[0] = data_in_reg[0]; assign q_m[1] = (condition_1) ? (q_m[0] ^~ data_in_reg[1]) : (q_m[0] ^ data_in_reg[1]); assign q_m[2] = (condition_1) ? (q_m[1] ^~ data_in_reg[2]) : (q_m[1] ^ data_in_reg[2]); assign q_m[3] = (condition_1) ? (q_m[2] ^~ data_in_reg[3]) : (q_m[2] ^ data_in_reg[3]); assign q_m[4] = (condition_1) ? (q_m[3] ^~ data_in_reg[4]) : (q_m[3] ^ data_in_reg[4]); assign q_m[5] = (condition_1) ? (q_m[4] ^~ data_in_reg[5]) : (q_m[4] ^ data_in_reg[5]); assign q_m[6] = (condition_1) ? (q_m[5] ^~ data_in_reg[6]) : (q_m[5] ^ data_in_reg[6]); assign q_m[7] = (condition_1) ? (q_m[6] ^~ data_in_reg[7]) : (q_m[6] ^ data_in_reg[7]); assign q_m[8] = (condition_1) ? 1'b0 : 1'b1; //q_m_n1:转换后9bit数据中1的个数 //q_m_n0:转换后9bit数据中0的个数 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) begin q_m_n1 <= 4'd0; q_m_n0 <= 4'd0; end else begin q_m_n1 <= q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]; q_m_n0 <= 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 //condition_2:条件2 assign condition_2 = ((cnt == 5'd0) || (q_m_n1 == q_m_n0)); //condition_3:条件3 assign condition_3 = (((~cnt[4] == 1'b1) && (q_m_n1 > q_m_n0)) || ((cnt[4] == 1'b1) && (q_m_n0 > q_m_n1))); //数据打拍,为了各数据同步 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) begin de_reg1 <= 1'b0; de_reg2 <= 1'b0; c0_reg1 <= 1'b0; c0_reg2 <= 1'b0; c1_reg1 <= 1'b0; c1_reg2 <= 1'b0; q_m_reg <= 9'b0; end else begin de_reg1 <= de; de_reg2 <= de_reg1; c0_reg1 <= c0; c0_reg2 <= c0_reg1; c1_reg1 <= c1; c1_reg2 <= c1_reg1; q_m_reg <= q_m; end //data_out:输出编码后的10bit数据 //cnt:视差计数器,0-1个数差别,最高位为符号位 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) begin data_out <= 10'b0; cnt <= 5'b0; end else begin if(de_reg2 == 1'b1) begin if(condition_2 == 1'b1) begin data_out[9] <= ~q_m_reg[8]; data_out[8] <= q_m_reg[8]; data_out[7:0] <= (q_m_reg[8]) ? q_m_reg[7:0] : ~q_m_reg[7:0]; cnt <= (~q_m_reg[8]) ? (cnt + q_m_n0 - q_m_n1) : (cnt + q_m_n1 - q_m_n0); end else begin if(condition_3 == 1'b1) begin data_out[9] <= 1'b1; data_out[8] <= q_m_reg[8]; data_out[7:0] <= ~q_m_reg[7:0]; cnt <= cnt + {q_m_reg[8], 1'b0} + (q_m_n0 - q_m_n1); end else begin data_out[9] <= 1'b0; data_out[8] <= q_m_reg[8]; data_out[7:0] <= q_m_reg[7:0]; cnt <= cnt - {~q_m_reg[8], 1'b0} + (q_m_n1 - q_m_n0); end end end else begin case ({c1_reg2, c0_reg2}) 2'b00: data_out <= DATA_OUT0; 2'b01: data_out <= DATA_OUT1; 2'b10: data_out <= DATA_OUT2; default:data_out <= DATA_OUT3; endcase cnt <= 5'b0; end end endmodule
par_to_ser
`timescale 1ns/1ns module par_to_ser ( input wire clk_5x , //输入系统时钟 input wire [9:0] par_data , //输入并行数据 output wire ser_data_p , //输出串行差分数据 output wire ser_data_n //输出串行差分数据 ); //wire define wire [4:0] data_rise = {par_data[8],par_data[6],par_data[4],par_data[2],par_data[0]}; wire [4:0] data_fall = {par_data[9],par_data[7],par_data[5],par_data[3],par_data[1]}; //reg define reg [4:0] data_rise_s = 0; reg [4:0] data_fall_s = 0; reg [2:0] cnt = 0; always @ (posedge clk_5x) begin cnt <= cnt[2] ? 3'd0 : cnt + 3'd1; data_rise_s <= cnt[2] ? data_rise : data_rise_s[4:1]; data_fall_s <= cnt[2] ? data_fall : data_fall_s[4:1]; end //------------- ddio_out_inst0 ------------- ddio_out ddio_out_inst0 ( .datain_h (data_rise_s[0] ), .datain_l (data_fall_s[0] ), .outclock (~clk_5x ), .dataout (ser_data_p ) ); //------------- ddio_out_inst1 ------------- ddio_out ddio_out_inst1 ( .datain_h (~data_rise_s[0]), .datain_l (~data_fall_s[0]), .outclock (~clk_5x ), .dataout (ser_data_n ) ); endmodule
vga_ctrl
`timescale 1ns/1ns module vga_ctrl ( input wire vga_clk , //输入工作时钟,频率25MHz input wire sys_rst_n , //输入复位信号,低电平有效 input wire [15:0] pix_data , //输入像素点色彩信息 output wire [11:0] pix_x , //输出VGA有效显示区域像素点X轴坐标 output wire [11:0] pix_y , //输出VGA有效显示区域像素点Y轴坐标 output wire hsync , //输出行同步信号 output wire vsync , //输出场同步信号 output wire rgb_valid , output wire [15:0] rgb //输出像素点色彩信息 ); //parameter define parameter H_SYNC = 10'd96 , //行同步 H_BACK = 10'd40 , //行时序后沿 H_LEFT = 10'd8 , //行时序左边框 H_VALID = 10'd640 , //行有效数据 H_RIGHT = 10'd8 , //行时序右边框 H_FRONT = 10'd8 , //行时序前沿 H_TOTAL = 10'd800 ; //行扫描周期 parameter V_SYNC = 10'd2 , //场同步 V_BACK = 10'd25 , //场时序后沿 V_TOP = 10'd8 , //场时序上边框 V_VALID = 10'd480 , //场有效数据 V_BOTTOM = 10'd8 , //场时序下边框 V_FRONT = 10'd2 , //场时序前沿 V_TOTAL = 10'd525 ; //场扫描周期 //wire define wire pix_data_req ; //像素点色彩信息请求信号 //reg define reg [11:0] cnt_h ; //行同步信号计数器 reg [11:0] cnt_v ; //场同步信号计数器 //cnt_h:行同步信号计数器 always@(posedge vga_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_h <= 12'd0 ; else if(cnt_h == H_TOTAL - 1'd1) cnt_h <= 12'd0 ; else cnt_h <= cnt_h + 1'd1 ; //hsync:行同步信号 assign hsync = (cnt_h <= H_SYNC - 1'd1) ? 1'b1 : 1'b0 ; //cnt_v:场同步信号计数器 always@(posedge vga_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_v <= 12'd0 ; else if((cnt_v == V_TOTAL - 1'd1) && (cnt_h == H_TOTAL-1'd1)) cnt_v <= 12'd0 ; else if(cnt_h == H_TOTAL - 1'd1) cnt_v <= cnt_v + 1'd1 ; else cnt_v <= cnt_v ; //vsync:场同步信号 assign vsync = (cnt_v <= V_SYNC - 1'd1) ? 1'b1 : 1'b0 ; //rgb_valid:VGA有效显示区域 assign rgb_valid = (((cnt_h >= H_SYNC + H_BACK + H_LEFT) && (cnt_h < H_SYNC + H_BACK + H_LEFT + H_VALID)) &&((cnt_v >= V_SYNC + V_BACK + V_TOP) && (cnt_v < V_SYNC + V_BACK + V_TOP + V_VALID))) ? 1'b1 : 1'b0; //pix_data_req:像素点色彩信息请求信号,超前rgb_valid信号一个时钟周期 assign pix_data_req = (((cnt_h >= H_SYNC + H_BACK + H_LEFT - 1'b1) && (cnt_h < H_SYNC + H_BACK + H_LEFT + H_VALID - 1'b1)) &&((cnt_v >= V_SYNC + V_BACK + V_TOP) && (cnt_v < V_SYNC + V_BACK + V_TOP + V_VALID))) ? 1'b1 : 1'b0; //pix_x,pix_y:VGA有效显示区域像素点坐标 assign pix_x = (pix_data_req == 1'b1) ? (cnt_h - (H_SYNC + H_BACK + H_LEFT - 1'b1)) : 12'hfff; assign pix_y = (pix_data_req == 1'b1) ? (cnt_v - (V_SYNC + V_BACK + V_TOP)) : 12'hfff; //rgb:输出像素点色彩信息 assign rgb = (rgb_valid == 1'b1) ? pix_data : 16'b0 ; endmodule
vga_pic
`timescale 1ns/1ns module vga_pic ( input wire vga_clk , //输入工作时钟,频率25MHz input wire sys_rst_n , //输入复位信号,低电平有效 input wire [11:0] pix_x , //输入VGA有效显示区域像素点X轴坐标 input wire [11:0] pix_y , //输入VGA有效显示区域像素点Y轴坐标 output reg [15:0] pix_data //输出像素点色彩信息 ); //parameter define parameter H_VALID = 12'd640 , //行有效数据 V_VALID = 12'd480 ; //场有效数据 parameter RED = 16'hF800, //红色 ORANGE = 16'hFC00, //橙色 YELLOW = 16'hFFE0, //黄色 GREEN = 16'h07E0, //绿色 CYAN = 16'h07FF, //青色 BLUE = 16'h001F, //蓝色 PURPPLE = 16'hF81F, //紫色 BLACK = 16'h0000, //黑色 WHITE = 16'hFFFF, //白色 GRAY = 16'hD69A; //灰色 //pix_data:输出像素点色彩信息,根据当前像素点坐标指定当前像素点颜色数据 always@(posedge vga_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) pix_data <= 16'd0; else if((pix_x >= 0) && (pix_x < (H_VALID/10)*1)) pix_data <= RED; else if((pix_x >= (H_VALID/10)*1) && (pix_x < (H_VALID/10)*2)) pix_data <= ORANGE; else if((pix_x >= (H_VALID/10)*2) && (pix_x < (H_VALID/10)*3)) pix_data <= YELLOW; else if((pix_x >= (H_VALID/10)*3) && (pix_x < (H_VALID/10)*4)) pix_data <= GREEN; else if((pix_x >= (H_VALID/10)*4) && (pix_x < (H_VALID/10)*5)) pix_data <= CYAN; else if((pix_x >= (H_VALID/10)*5) && (pix_x < (H_VALID/10)*6)) pix_data <= BLUE; else if((pix_x >= (H_VALID/10)*6) && (pix_x < (H_VALID/10)*7)) pix_data <= PURPPLE; else if((pix_x >= (H_VALID/10)*7) && (pix_x < (H_VALID/10)*8)) pix_data <= BLACK; else if((pix_x >= (H_VALID/10)*8) && (pix_x < (H_VALID/10)*9)) pix_data <= WHITE; else if((pix_x >= (H_VALID/10)*9) && (pix_x < H_VALID)) pix_data <= GRAY; else pix_data <= BLACK; endmodule
hdmi_ctrl
`timescale 1ns/1ns module hdmi_ctrl ( input wire clk_1x , //输入系统时钟 input wire clk_5x , //输入5倍系统时钟 input wire sys_rst_n , //复位信号,低有效 input wire [7:0] rgb_blue , //蓝色分量 input wire [7:0] rgb_green , //绿色分量 input wire [7:0] rgb_red , //红色分量 input wire hsync , //行同步信号 input wire vsync , //场同步信号 input wire de , //使能信号 output wire hdmi_clk_p , output wire hdmi_clk_n , //时钟差分信号 output wire hdmi_r_p , output wire hdmi_r_n , //红色分量差分信号 output wire hdmi_g_p , output wire hdmi_g_n , //绿色分量差分信号 output wire hdmi_b_p , output wire hdmi_b_n //蓝色分量差分信号 ); wire [9:0] red ; //8b转10b后的红色分量 wire [9:0] green ; //8b转10b后的绿色分量 wire [9:0] blue ; //8b转10b后的蓝色分量 //------------- encode_inst0 ------------- encode encode_inst0 ( .sys_clk (clk_1x ), .sys_rst_n (sys_rst_n ), .data_in (rgb_blue ), .c0 (hsync ), .c1 (vsync ), .de (de ), .data_out (blue ) ); //------------- encode_inst1 ------------- encode encode_inst1 ( .sys_clk (clk_1x ), .sys_rst_n (sys_rst_n ), .data_in (rgb_green ), .c0 (hsync ), .c1 (vsync ), .de (de ), .data_out (green ) ); //------------- encode_inst2 ------------- encode encode_inst2 ( .sys_clk (clk_1x ), .sys_rst_n (sys_rst_n ), .data_in (rgb_red ), .c0 (hsync ), .c1 (vsync ), .de (de ), .data_out (red ) ); //------------- par_to_ser_inst0 ------------- par_to_ser par_to_ser_inst0 ( .clk_5x (clk_5x ), .par_data (blue ), .ser_data_p (hdmi_b_p ), .ser_data_n (hdmi_b_n ) ); //------------- par_to_ser_inst1 ------------- par_to_ser par_to_ser_inst1 ( .clk_5x (clk_5x ), .par_data (green ), .ser_data_p (hdmi_g_p ), .ser_data_n (hdmi_g_n ) ); //------------- par_to_ser_inst2 ------------- par_to_ser par_to_ser_inst2 ( .clk_5x (clk_5x ), .par_data (red ), .ser_data_p (hdmi_r_p ), .ser_data_n (hdmi_r_n ) ); //------------- par_to_ser_inst3 ------------- par_to_ser par_to_ser_inst3 ( .clk_5x (clk_5x ), .par_data (10'b1111100000), .ser_data_p (hdmi_clk_p ), .ser_data_n (hdmi_clk_n ) ); endmodule
hdmi_colorbar
`timescale 1ns/1ns module hdmi_colorbar ( input wire sys_clk , //输入工作时钟,频率50MHz input wire sys_rst_n , //输入复位信号,低电平有效 output wire ddc_scl , output wire ddc_sda , output wire tmds_clk_p , output wire tmds_clk_n , //HDMI时钟差分信号 output wire [2:0] tmds_data_p , output wire [2:0] tmds_data_n //HDMI图像差分信号 ); //wire define wire vga_clk ; //VGA工作时钟,频率25MHz wire clk_5x ; wire locked ; //PLL locked信号 wire rst_n ; //VGA模块复位信号 wire [11:0] pix_x ; //VGA有效显示区域X轴坐标 wire [11:0] pix_y ; //VGA有效显示区域Y轴坐标 wire [15:0] pix_data; //VGA像素点色彩信息 wire hsync ; //输出行同步信号 wire vsync ; //输出场同步信号 wire [15:0] rgb ; //输出像素信息 wire rgb_valid; //rst_n:VGA模块复位信号 assign rst_n = (sys_rst_n & locked); assign ddc_scl = 1'b1; assign ddc_sda = 1'b1; //------------- clk_gen_inst ------------- clk_gen clk_gen_inst ( .areset (~sys_rst_n ), //输入复位信号,高电平有效,1bit .inclk0 (sys_clk ), //输入50MHz晶振时钟,1bit .c0 (vga_clk ), //输出VGA工作时钟,频率25Mhz,1bit .c1 (clk_5x ), .locked (locked ) //输出pll locked信号,1bit ); //------------- vga_ctrl_inst ------------- vga_ctrl vga_ctrl_inst ( .vga_clk (vga_clk ), //输入工作时钟,频率25MHz,1bit .sys_rst_n (rst_n ), //输入复位信号,低电平有效,1bit .pix_data (pix_data ), //输入像素点色彩信息,16bit .pix_x (pix_x ), //输出VGA有效显示区域像素点X轴坐标,10bit .pix_y (pix_y ), //输出VGA有效显示区域像素点Y轴坐标,10bit .hsync (hsync ), //输出行同步信号,1bit .vsync (vsync ), //输出场同步信号,1bit .rgb_valid (rgb_valid ), .rgb (rgb ) //输出像素点色彩信息,16bit ); //------------- vga_pic_inst ------------- vga_pic vga_pic_inst ( .vga_clk (vga_clk ), //输入工作时钟,频率25MHz,1bit .sys_rst_n (rst_n ), //输入复位信号,低电平有效,1bit .pix_x (pix_x ), //输入VGA有效显示区域像素点X轴坐标,10bit .pix_y (pix_y ), //输入VGA有效显示区域像素点Y轴坐标,10bit .pix_data (pix_data ) //输出像素点色彩信息,16bit ); //------------- hdmi_ctrl_inst ------------- hdmi_ctrl hdmi_ctrl_inst ( .clk_1x (vga_clk ), //输入系统时钟 .clk_5x (clk_5x ), //输入5倍系统时钟 .sys_rst_n (rst_n ), //复位信号,低有效 .rgb_blue ({rgb[4:0],3'b0} ), //蓝色分量 .rgb_green ({rgb[10:5],2'b0} ), //绿色分量 .rgb_red ({rgb[15:11],3'b0} ), //红色分量 .hsync (hsync ), //行同步信号 .vsync (vsync ), //场同步信号 .de (rgb_valid ), //使能信号 .hdmi_clk_p (tmds_clk_p ), .hdmi_clk_n (tmds_clk_n ), //时钟差分信号 .hdmi_r_p (tmds_data_p[2] ), .hdmi_r_n (tmds_data_n[2] ), //红色分量差分信号 .hdmi_g_p (tmds_data_p[1] ), .hdmi_g_n (tmds_data_n[1] ), //绿色分量差分信号 .hdmi_b_p (tmds_data_p[0] ), .hdmi_b_n (tmds_data_n[0] ) //蓝色分量差分信号 ); endmodule
tb_hdmi_colorbar
`timescale 1ns/1ns module tb_hdmi_colorbar(); //wire define wire ddc_scl ; wire ddc_sda ; wire tmds_clk_p ; wire tmds_clk_n ; wire [2:0] tmds_data_p ; wire [2:0] tmds_data_n ; //reg define reg sys_clk ; reg sys_rst_n ; //sys_clk,sys_rst_n初始赋值 initial begin sys_clk = 1'b1; sys_rst_n <= 1'b0; #200 sys_rst_n <= 1'b1; end //sys_clk:产生时钟 always #10 sys_clk = ~sys_clk ; //------------- hdmi_colorbar_inst ------------- hdmi_colorbar hdmi_colorbar_inst ( .sys_clk (sys_clk ), //输入工作时钟,频率50MHz .sys_rst_n (sys_rst_n ), //输入复位信号,低电平有效 .ddc_scl (ddc_scl ), .ddc_sda (ddc_sda ), .tmds_clk_p (tmds_clk_p ), .tmds_clk_n (tmds_clk_n ), //HDMI时钟差分信号 .tmds_data_p (tmds_data_p), .tmds_data_n (tmds_data_n) //HDMI图像差分信号 ); endmodule