搜索
bottom↓
回复: 0

【正点原子FPGA连载】第四十二章HDMI输入输出环回实验

[复制链接]

出0入234汤圆

发表于 2020-12-7 16:53:20 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2021-1-23 15:35 编辑

1)实验平台:正点原子达芬奇FPGA开发板
2)购买链接:https://detail.tmall.com/item.htm?id=624335496505
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz_dafenqi.html
4) 正点原子官方B站:https://space.bilibili.com/394620890
5)对正点原子FPGA感兴趣的同学可以加群讨论:
905624739 点击加入: QQ群头像.png

100846rel79a9p4uelap24.jpg

100846f1ce1fg14zbg0va4.png

第四十二章HDMI输入输出环回实验



在HDMI彩条显示实验中,我们成功地在HDMI显示器上显示出了彩条的图像。本章我们将使用FPGA开发板实现对HDMI的输入源解码,再通过编码输出到HDMI显示器上实时显示,实现HDMI输入输出环回的功能。
本章包括以下几个部分:
4242.1简介
42.2实验任务
42.3硬件设计
42.4程序设计
42.5下载验证


42.1
简介
HDMI包含三种独立的通信通道::TMDS、DDC和可选的CEC通道。
TMDS通道用于传送所有的音频和视频数据,还有辅助数据,包括用来描述有效的音频和视频数据流的AVI和音频信息帧。
HDMI源端通过DDC通道用来读取E-EDID数据架构从而获得HDMI接收端的接收能力和接收特性。这样HDMI源端只会发送那些接收端支持的视频格式。除此之外,HDMI接收端还通过DDC通道探测到信息帧,进而正确的接收音视频信息。
CEC是一个可选的功能,用来定义一些高级的用户功能例如单键播放,单键刻录及刻录时间控制等。
由于本次实验只是使用HDMI接口来显示图像,不需要传输音频,因此我们只需要实现DVI接口和DDC通道的驱动逻辑即可,不需要CEC功能。不过在此之前我们还需要简单地了解一下TMDS视频传输协议。在前文的HDMI彩条实验已经介绍过HDMI的编码原理,下面介绍下解码原理。
42561.png

图 42.1.1 DVI解码器示意图

DVI解码器需要在一系列的串行数据中确定数据边界的位置,一旦数据通道中的边界字符被确定,解码器将依此同步串行数据流,并且将TMDS通道传输的数据流解码。TMDS数据流提供一个周期性的尾接提示给同步解码器。当在视频数据的数据周期和数据岛周期处理TMDS字时,则包含5次或更少的转换,当在控制周期处理TMDS字时,则包含7次或更多的转换。这些字符跳转次数的不同将会被用于接收设备的同步边界。当边界被确定后,将串并转换器转换后的并行数据进行8B/10B的解码,得到像素数据和相关控制信号。
EDID数据结构:
EDID(Extended Display Identification Data,扩展显示标识数据)是VESA的一个简单规范。它定义了一种数据格式,其中包括有关监视器及其性能的参数,包括生产厂家信息、显示器的名称和序列号、支持的所有分辨率清单、颜色设置、厂商预设值等信息。
DDC就是一个有两根符合I2C协议的数据线(SCL,SDA)组成的数据通道。HDMI源端通过这个DDC通道访问HDMI接收端的EEPROM,从而获得EDID信息,也就是获得了一些关于接收端的信息,然后HDMI发送端再根据这些信息决定发送的视频格式,从而达到最好的显示效果。
EDID的扩展数据由256 bytes组成,其中前面128 bytes是基本的数据结构,它的数据如下图所示:
表格 42.1.1 EDID的基本数据结构
4211.png

在地址7Eh处,当数据为00h时,只有128byte的基础数据结构,如果为01h时,那么基础数据结构就为256byte。当使用VGA和DVI时,将数值设为00h,使用HDMI时,数值设为01h。
表格 42.1.2 EDID扩展数据结构
地址空间(+80H)        值        数据内容描述        
4212.png

HDMI协议与DVI协议在很多方面都是相同的,包括物理连接(TMDS)、有效视频编码算法以及控制字符的定义等。相比于DVI,HDMI在视频的消隐期会传输更多的数据,包括音频数据和附加数据。4-bit音频和附加数据将通过TERC4编码机制转换成10-bit TERC4字符,然后在绿色和红色通道上传输。
42.2实验任务
本节实验任务是使用达芬奇开发板将HDMI_A输入进来的数据流进行解码再编码后,通过HDMI_B的输出接口输出,用以驱动HDMI显示器。
42.3硬件设计
422463.png

图 42.3.1 HDMI接口A原理图1

图 42.3.1是达芬奇HDMI接口A原理图的一部分,其中HDMI的三个数据通道HDMI_D[2:0]和一个时钟通道HDMI_CLK通过热插拔保护芯片与TMDS差分引脚相连。HDMI接口B的原理图与HDMI接口A是一样的,都是可以作为输入或输出的接口。
HDMI_CEC指的是用户电气控制(Consumer Electronics Control),它用于HDMI连接线上的设备之间进行信息交换。当一个设备的状态发生变化时,CEC可以使用远程控制或自动改变设置来命令连接的关联设备的状态发生相应的变化。例如,如果用户放置一张碟片到蓝光播放器并开始播放,那么高清电视机将会自动打开电源,设置正确的视频输入格式和打开环绕声设备等等,这种关联通信提供了一个更好的客户体验。
HDMI_HPD指的是热拔插检测(Hot Plug Detect),当视频设备与接收设备通过HDMI连接时,接收设备将HPD置为高电平,通知发送设备。当发送设备检测到HPD为低电平时,表明断开连接。
422988.png

图 42.3.2 HDMI接口A原理图2

在图 42.3.2中,HDMI_OUT_EN信号用于设置HDMI接口的输入输出模式,当其为高电平时作为输出端,此时由达芬奇开发板输出HDMI接口的5V电源。同时图 42.3.1中HDMI_HPD将作为输入信号使用;反之,当HDMI_OUT_EN为低电平时,开发板上的HDMI接口作为输入端,接口上的5V电源将由外部连接设备提供。同时HDMI_HPD将输出高电平,用于指示HDMI连接状态。
423297.png

图 42.3.3 HDMI接口A原理图3

在图 42.3.3中,HDMI_SCL_LS和HDMI_SDA_LS是HDMI接口的显示数据通道(DDC,Display Data Channel),用于HDMI 发送端和接收端之间交换一些配置信息,通过I2C协议通信。发送端通过 DDC通道,读取接收端保存在 EEPROM中的EDID数据,获取接收端的信息,确认接收端终端显示的设置和功能,决定跟接收端之间以什么格式传输音/视频数据。
本次实验使用以下信号,各信号的管脚分配如下表所示:
表 42.3.1  HDMI输入输出环回实验管脚分配
信号名        方向        管脚        端口说明        电平标准
4231.png

HDMI输入输出环回实验XDC约束文件如下:
############## NET - IOSTANDARD #################################
set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]
set_property BITSTREAM.CONFIG.UNUSEDPIN PULLUP [current_design]
#############SPI Configurate Setting##############################
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]
set_property CONFIG_MODE SPIx4 [current_design]
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
############## clock and reset define##############################
create_clock -period 20.000 [get_ports sys_clk]
set_property IOSTANDARD LVCMOS33 [get_ports sys_clk]
set_property PACKAGE_PIN R4 [get_ports sys_clk]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
set_property PACKAGE_PIN U2 [get_ports rst_n]

#####################HDMI_IN_A#######################################
create_clock -period 6.000 -name clk_hdmi_in_p -waveform {0.000 3.000}
set_property PACKAGE_PIN D22 [get_ports hdmi_ddc_scl_io]
set_property PACKAGE_PIN D21 [get_ports hdmi_ddc_sda_io]
set_property PACKAGE_PIN E22 [get_ports hdmi_in_hpd]
set_property PACKAGE_PIN C18 [get_ports clk_hdmi_in_p]
set_property PACKAGE_PIN C22 [get_ports {data_hdmi_in_p[0]}]
set_property PACKAGE_PIN B21 [get_ports {data_hdmi_in_p[1]}]
set_property PACKAGE_PIN B20 [get_ports {data_hdmi_in_p[2]}]
set_property PACKAGE_PIN E21 [get_ports hdmi_in_oen]
set_property IOSTANDARD LVCMOS33 [get_ports hdmi_ddc_scl_io]
set_property IOSTANDARD LVCMOS33 [get_ports hdmi_ddc_sda_io]
set_property IOSTANDARD TMDS_33 [get_ports clk_hdmi_in_n]
set_property IOSTANDARD TMDS_33 [get_ports clk_hdmi_in_p]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_in_n[0]}]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_in_p[0]}]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_in_n[1]}]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_in_p[1]}]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_in_n[2]}]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_in_p[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports hdmi_in_oen]
set_property IOSTANDARD LVCMOS33 [get_ports hdmi_in_hpd]
######################HDMI_OUT_B#######################################
set_property PACKAGE_PIN B17 [get_ports clk_hdmi_out_p]
set_property PACKAGE_PIN A18 [get_ports {data_hdmi_out_p[0]}]
set_property PACKAGE_PIN A15 [get_ports {data_hdmi_out_p[1]}]
set_property PACKAGE_PIN A13 [get_ports {data_hdmi_out_p[2]}]
set_property PACKAGE_PIN C20 [get_ports hdmi_out_oen]
set_property IOSTANDARD TMDS_33 [get_ports clk_hdmi_out_n]
set_property IOSTANDARD TMDS_33 [get_ports clk_hdmi_out_p]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_out_n[0]}]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_out_p[0]}]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_out_n[1]}]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_out_p[1]}]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_out_n[2]}]
set_property IOSTANDARD TMDS_33 [get_ports {data_hdmi_out_p[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports hdmi_out_oen]

42.4程序设计
由于本次实验只需要HDMI接口的视频数据部分,因此将其当成DVI接口进行驱动。我们只需要实现图像的接受和发送功能,由此得出本次实验的系统框图如下所示:
427546.png

图 42.4.1 顶层系统框图

由上图可知,时钟模块(mmcm)为HDMI解码模块和EDID读取模块提供驱动时钟。HDMI解码模块负责将HDMI输入源输入的串行差分信号转化为串行单端信号,再进行串并转换,最后再进行8B/10B解码,将解码后的时钟、控制信号和数据传给HDMI编码模块。HDMI编码模块与解码模块是相反的操作,先进行8B/10B编码,再进行并串转换,最后再将串行单端信号转为串行差分信号。EDID读取模块负责与输入源进行通信,告诉输入源监视器所需要的性能的参数,包括生产厂家信息、显示器的名称和序列号、支持的所有分辨率清单、颜色设置、厂商预设值等信息。
顶层模块的原理图如下图所示:
427890.png

图 42.4.2 顶层模块原理图

FPGA顶层模块(hdmi_loop)例化了以下四个模块:时钟模块(mmcm)、HDMI解码模块(hdmi_rx)、HDMI编码模块(dvi_transmitter_top)和EDID读取模块(i2c_edid)。
时钟模块(mmcm):时钟模块通过调用MMCM IP核实现,共输出2个时钟,频率为10Mhz和200Mhz。 10Mhz的时钟负责对输入进来的iic信号和热插拔信号(hdmi_in_hpd)进行采样,200Mhz是HDMI解码模块的参考时钟。
HDMI解码封装层模块(hdmi_rx):HDMI解码模块负责对输入源的输入数据进行解析,将解析后的时钟、数据和控制信号传给HDMI编码模块。
HDMI编码模块(dvi_transmitter_top):HDMI编码模块负责将HDMI解码模块传出的时钟、数据和控制信号重新编码,并输出串行差分数据用以驱动HDMI显示器。有关HDMI编码模块的详细介绍请大家参考“HDMI彩条显示实验”章节。
EDID读取模块(i2c_edid):负责与输入源进行通信,将监视器的性能参数传给输入源。
顶层模块的代码如下:
  1. 1  module hdmi_loop(
  2. 2      input               sys_clk,               //50M系统时钟
  3. 3      input               rst_n,                 //系统复位,低有效
  4. 4      //hdmi in
  5. 5      input               hdmi_ddc_scl_io,       //IIC时钟
  6. 6      inout               hdmi_ddc_sda_io,       //IIC数据
  7. 7      output              hdmi_in_hpd,           //热插拔信号
  8. 8      output [0:0]        hdmi_in_oen,           //输入输出切换信号
  9. 9      input               clk_hdmi_in_n,         //输入差分时钟
  10. 10     input               clk_hdmi_in_p,         //输入差分时钟
  11. 11     input  [2:0]        data_hdmi_in_n,        //输入差分数据
  12. 12     input  [2:0]        data_hdmi_in_p,        //输入差分数据
  13. 13     //hdmi out
  14. 14     output [0:0]        hdmi_out_oen,          //输入输出切换信号
  15. 15     output              clk_hdmi_out_n,        //输出差分时钟
  16. 16     output              clk_hdmi_out_p,        //输出差分时钟
  17. 17     output [2:0]        data_hdmi_out_n,       //输出差分数据
  18. 18     output [2:0]        data_hdmi_out_p        //输出差分数据
  19. 19 );
  20. 20
  21. 21 //wire define
  22. 22 wire        clk_10m;           //10m时钟
  23. 23 wire        clk_200m;          //200m时钟
  24. 24 wire        rx_rst;            //复位信号,高有效
  25. 25 wire        pixel_clk;         //像素时钟
  26. 26 wire        pixel_clk_5x;      //5倍像素时钟
  27. 27 wire        video_hs;          //行信号
  28. 28 wire        video_vs;          //场信号
  29. 29 wire        video_de;          //数据有效使能
  30. 30 wire        hdmi_in_oen;       //输入输出切换信号
  31. 31 wire        hdmi_in_hpd;       //热插拔信号
  32. 32 wire        hdmi_out_oen;      //输入输出切换信号
  33. 33 wire [23:0] video_rgb;         //像素数据
  34. 34
  35. 35 //*****************************************************
  36. 36 //**                    main code
  37. 37 //*****************************************************      
  38. 38 //时钟模块
  39. 39 mmcm u_mmcm(
  40. 40 .clk_out1           (clk_10m),  // output clk_out1
  41. 41 .clk_out2           (clk_200m),  // output clk_out1
  42. 42 .locked             (       ),  // output locked
  43. 43 .clk_in1            (sys_clk)   // input clk_in1
  44. 44 );      
  45. 45     
  46. 46 //读edid模块   
  47. 47 i2c_edid u_i2c_edid (
  48. 48  .clk(clk_10m),
  49. 49  .rst(~rst_n),
  50. 50  .scl(hdmi_ddc_scl_io),
  51. 51  .sda(hdmi_ddc_sda_io)
  52. 52 );   
  53. 53     
  54. 54 //hdmi解码模块   
  55. 55 hdmi_rx u_hdmi_rx(
  56. 56     .clk_10m       (clk_10m),
  57. 57     .clk_200m      (clk_200m),
  58. 58     //input
  59. 59     .tmdsclk_p     (clk_hdmi_in_p),     
  60. 60     .tmdsclk_n     (clk_hdmi_in_n),      
  61. 61     .blue_p        (data_hdmi_in_p[0]),
  62. 62     .green_p       (data_hdmi_in_p[1]),  
  63. 63     .red_p         (data_hdmi_in_p[2]),  
  64. 64     .blue_n        (data_hdmi_in_n[0]),  
  65. 65     .green_n       (data_hdmi_in_n[1]),
  66. 66     .red_n         (data_hdmi_in_n[2]),
  67. 67     .rst_n         (rst_n),              
  68. 68     //output      
  69. 69     .reset         (rx_rst),            
  70. 70     .pclk          (pixel_clk),         
  71. 71     .pclkx5        (pixel_clk_5x),      
  72. 72     .hsync         (video_hs),         
  73. 73     .vsync         (video_vs),         
  74. 74     .de            (video_de),         
  75. 75     .rgb_data      (video_rgb),         
  76. 76     .hdmi_in_en    (hdmi_in_oen),   
  77. 77     .hdmi_in_hpd   (hdmi_in_hpd)  
  78. 78   );      
  79. 79   
  80. 80  //hdmi编码模块      
  81. 81 dvi_transmitter_top u_rgb2dvi_0(
  82. 82     .pclk           (pixel_clk),
  83. 83     .pclk_x5        (pixel_clk_5x),
  84. 84     .reset_n        (~rx_rst),
  85. 85                 
  86. 86     .video_din      (video_rgb),
  87. 87     .video_hsync    (video_hs),
  88. 88     .video_vsync    (video_vs),
  89. 89     .video_de       (video_de),
  90. 90                 
  91. 91     .tmds_clk_p     (clk_hdmi_out_p),
  92. 92     .tmds_clk_n     (clk_hdmi_out_n),
  93. 93     .tmds_data_p    (data_hdmi_out_p),
  94. 94     .tmds_data_n    (data_hdmi_out_n),
  95. 95     .tmds_oen       (hdmi_out_oen)
  96. 96     );  
  97. 97 endmodule
复制代码

在代码的39行至44行,添加了一个MMCM的IP,产生了一个10M时钟和200M时钟,10M时钟用以驱动EDID读取模块和hdmi解码模块,200M时钟作为hdmi解码模块的参考时钟。
在代码的47行至53行,例化了一个EDID的读取模块。本次实验中的IIC是作为从机来使用的,所以用10M时钟来做iic时钟和数据的采样时钟,不需要再对10M时钟进行分频了。
在代码的55行至78行,例化了一个HDMI解码封装层模块。在代码的70和71行,定义了2个时钟信号,一个是像素时钟,一个是5倍的像素的时钟。这里用5倍像素时钟而不用10倍像素时钟的原因是在编码模块里的OSERDESE2原语采用的是DDR模式,而不是SDR模式。在代码的76行,这个信号的作用是告诉外设这个接口是输入还是输出。在代码的77行,这个信号的作用是告诉外设这个接口是否连接上了。
在代码的81行至96行,例化了一个hdmi编码模块。
EDID读取模块的代码如下:
  1. 1   module i2c_edid
  2. 2   (
  3. 3       input wire clk,
  4. 4       input wire rst,
  5. 5       input wire scl,
  6. 6       inout wire sda
  7. 7   );
  8. 8   
  9. 9   //parameter define
  10. 10  parameter   EDID_IDLE     = 3'b000;
  11. 11  parameter   EDID_ADDR     = 3'b001;
  12. 12  parameter   EDID_ADDR_ACK = 3'b010;
  13. 13  parameter   EDID_ADDR_ACK2= 3'b011;
  14. 14  parameter   EDID_DATA     = 3'b100;
  15. 15  parameter   EDID_DATA_ACK = 3'b101;
  16. 16  parameter   EDID_DATA_ACK2= 3'b110;
  17. 17  
  18. 18  //reg define
  19. 19  reg         hiz ;
  20. 20  reg         sda_out;
  21. 21  reg [4:0]   count;
  22. 22  reg [15:0]  rdata;
  23. 23  reg [7:0]   addr;
  24. 24  reg [7:0]   data ;
  25. 25  reg [2:0]   edid_state;
  26. 26  reg [3:0]   scl_data ;
  27. 27  reg [3:0]   sda_data ;
  28. 28  
  29. 29  //wire define
  30. 30  wire [7:0]  dout;
  31. 31  wire        scl_high;
  32. 32  wire        scl_negedge;
  33. 33  wire        scl_posedge;
  34. 34  
  35. 35  //*****************************************************
  36. 36  //**                    main code
  37. 37  //*****************************************************
  38. 38      
  39. 39  //取IIC时钟的上升沿
  40. 40  assign scl_posedge = (scl_data == 4'b0111) ? 1'b1 : 1'b0;
  41. 41  //取IIC时钟的下降沿
  42. 42  assign scl_negedge = (scl_data == 4'b1000) ? 1'b1 : 1'b0;
  43. 43  //IIC时钟的全高电平
  44. 44  assign scl_high    = (scl_data == 4'b1111) ? 1'b1 : 1'b0;
  45. 45  //IIC数据的三态输出
  46. 46  assign sda = hiz ? 1'hz : sda_out;
  47. 47  
  48. 48  //存储edid信息
  49. 49  edid_rom edid_rom_0 (
  50. 50      .addra(addr[7:0]),
  51. 51      .clka(clk),
  52. 52      .douta(dout)
  53. 53  );
  54. 54  
  55. 55  always @(posedge clk) begin
  56. 56      if (rst) begin
  57. 57          hiz <= 1'b1;
  58. 58          sda_out <= 1'b0;
  59. 59          count <= 5'd0;
  60. 60          rdata <= 24'h0;
  61. 61          addr <= 8'h0;
  62. 62          data <= 8'h0;
  63. 63          scl_data <= 4'h00;
  64. 64          sda_data <= 4'h00;
  65. 65      end
  66. 66  else begin
  67. 67          scl_data <= {scl_data[2:0], scl};
  68. 68          sda_data <= {sda_data[2:0], sda};
  69. 69  
  70. 70          if (sda_data == 4'b1000 && scl_high) begin         // iic开始的标志
  71. 71              count <= 5'd0;
  72. 72              hiz <= 1'b1;
  73. 73              sda_out <= 1'b0;
  74. 74              edid_state <= EDID_ADDR;
  75. 75          end
  76. 76      else if (sda_data == 4'b0111 && scl_high) begin // iic结束的标志
  77. 77              edid_state <= EDID_IDLE;
  78. 78          end
  79. 79      else
  80. 80          case (edid_state)
  81. 81              EDID_IDLE: begin
  82. 82                  hiz <= 1'b1;
  83. 83                  sda_out <= 1'b0;        
  84. 84              end
  85. 85              EDID_ADDR: begin
  86. 86                  if (scl_posedge) begin
  87. 87                      count <= count + 5'd1;
  88. 88                      rdata  <= {rdata[14:0], sda};
  89. 89                      if (count[2:0] == 3'd7) begin      //器件地址写完
  90. 90                          edid_state <= EDID_ADDR_ACK;               
  91. 91                          if (count == 5'd15)            //字地址写完   
  92. 92                              addr <= {rdata[6:0],sda};  //将字地址赋给ROM地址
  93. 93                          else
  94. 94                              addr <= addr;      
  95. 95                      end
  96. 96                  end
  97. 97              end
  98. 98              EDID_ADDR_ACK: begin
  99. 99                  if (scl_negedge) begin
  100. 100                     hiz <= 1'b0;
  101. 101                     sda_out <= 1'b0;
  102. 102                     if (count == 5'd8 && rdata [0] == 1'b1) begin //判断是否是读操作
  103. 103                         data <= dout;
  104. 104                         edid_state <= EDID_DATA;
  105. 105                     end
  106. 106                 else begin
  107. 107                         edid_state <= EDID_ADDR_ACK2;
  108. 108                     end
  109. 109                 end
  110. 110             end
  111. 111             EDID_ADDR_ACK2: begin
  112. 112                 if (scl_negedge) begin
  113. 113                     hiz <= 1'b1;          //释放总线
  114. 114                     edid_state <= EDID_ADDR;
  115. 115                 end
  116. 116             end
  117. 117             EDID_DATA: begin
  118. 118                 if (scl_negedge) begin
  119. 119                     count <= count + 5'd1;
  120. 120                     hiz <= 1'b0;
  121. 121                     sda_out <= data[7];            
  122. 122                     data <= {data[6:0], 1'b0};     //数据移位
  123. 123                     if (count[2:0] == 3'd7) begin  //一个数据读完
  124. 124                         addr <= addr + 8'h1;       //rom地址加1
  125. 125                         edid_state <= EDID_DATA_ACK;
  126. 126                     end
  127. 127                 end
  128. 128             end
  129. 129             EDID_DATA_ACK: begin
  130. 130                 if (scl_negedge) begin
  131. 131                     data <= dout;
  132. 132                     hiz <= 1'b1;       //释放总线
  133. 133                     sda_out <= 1'b0;
  134. 134                     edid_state <= EDID_DATA_ACK2;
  135. 135                 end
  136. 136             end
  137. 137             EDID_DATA_ACK2: begin
  138. 138                 if (scl_posedge) begin
  139. 139                     if (sda)  //数据为1,读操作结束,为0,未结束
  140. 140                         edid_state <= EDID_IDLE;  
  141. 141                     else
  142. 142                         edid_state <= EDID_DATA;
  143. 143                 end
  144. 144             end
  145. 145         endcase
  146. 146     end
  147. 147 end
  148. 148
  149. 149 endmodule
复制代码

代码的40行至44行,是对IIC时钟变化的定义。
代码的46行,是对双向引脚SDA进行一个三态处理,hiz为1时,释放总线,否则就输出数据。
代码的49行至53行,是对存放EDID信息的ROM的一个例化,其中初始化所用的coe文件是从网上搜索得到的。。
代码的69行至143行,是对IIC时序的一个状态跳转代码,其流程图如下所示:
4217562.png

图 42.4.3 IIC时序的状态跳转图

代码的69行至73行,是IIC时序的开始信号到来时对信号的初始化操作。
代码的74行至75行,是IIC时序的结束信号到来时,将状态跳转到空闲状态。
代码的78行至80行,当状态处于空闲状态时,释放数据总线,使总线处于高阻状态。
代码的82行至94行,是主机向从机写入器件地址和寄存器地址。本次实验中FPGA作为从机,并且与主机是一对一的关系,所以可以不需要识别器件地址和寄存器地址。当信号count在0~7时,主机写入的是器件地址,信号count在8~f时,写入的是寄存器地址。如下图所示:
4217901.png

图 42.4.4 IIC时序图1

代码的95行至106行,判断器件地址的第8位是否为高,为高则表明是读操作,为低则表示是写操作。因为本次的IIC时序采用的是随机读时序,即在时序中有个虚拟写操作,如下图所示。当第8个时钟下降沿到来时,拉低数据总线,以此响应主机。
4218103.png


图 42.4.5 IIC时序图2

代码的107行至112行,当SCL的下降沿到来时,表示从机应答完成,所以要释放总线。
代码的113行至124行,是主机从从机读取数据的操作。当一个字节读完后,主机需要给从机一个应答信号。
代码的125行至132行,此时需要主机回复一个应答信号,所以要释放数据总线。
代码的133行至140行,是用来判别主机的应答信号是高电平还是低电平,如果是高电平,则表示此次读操作已经结束,为低电平则表示读操作还未结束。
HDMI解码封装层模块的代码如下:
  1. 1  module hdmi_rx(
  2. 2    input  wire        clk_10m,          // 10M时钟     
  3. 3    input  wire        clk_200m,         // 200M参考时钟                                 
  4. 4    input  wire        tmdsclk_p,        // HDMI输入差分时钟
  5. 5    input  wire        tmdsclk_n,        // HDMI输入差分时钟
  6. 6    input  wire        blue_p,           // HDMI输入蓝色差分数据
  7. 7    input  wire        green_p,          // HDMI输入绿色差分数据
  8. 8    input  wire        red_p,            // HDMI输入红色差分数据
  9. 9    input  wire        blue_n,           // HDMI输入蓝色差分数据
  10. 10   input  wire        green_n,          // HDMI输入绿色差分数据
  11. 11   input  wire        red_n,            // HDMI输入红色差分数据
  12. 12   input  wire        rst_n,            // 复位信号,低有效
  13. 13                                       
  14. 14   output wire        reset,            // RX端复位信号
  15. 15   output wire        pclk,             // 像素点采样时钟
  16. 16   output wire        pclkx5,           // 像素点5倍采样时钟
  17. 17   output wire        hsync,            // 行信号
  18. 18   output wire        vsync,            // 场信号
  19. 19   output wire        de,               // 数据使能
  20. 20   output wire [23:0] rgb_data,         // 像素数据
  21. 21   output wire        hdmi_in_en,       // 输入输出使能信号,0代表输入,1代表输出
  22. 22   output reg         hdmi_in_hpd       // 热插拔信号
  23. 23     );
  24. 24   
  25. 25   
  26. 26 //wire define  
  27. 27 wire [7:0] red;         //红色像素数据
  28. 28 wire [7:0] green;       //绿色像素数据
  29. 29 wire [7:0] blue;        //蓝色像素数据
  30. 30   
  31. 31 //*****************************************************
  32. 32 //**                    main code
  33. 33 //*****************************************************  
  34. 34
  35. 35 assign rgb_data = {red,green,blue};      
  36. 36 assign hdmi_in_en = 1'b0;
  37. 37
  38. 38 //热插拔信号
  39. 39 always@(posedge clk_10m or negedge rst_n)begin
  40. 40     if( rst_n == 1'b0)
  41. 41         hdmi_in_hpd <= 1'b0;
  42. 42     else
  43. 43         hdmi_in_hpd <= 1'b1;
  44. 44 end
  45. 45
  46. 46 //hdmi解码模块
  47. 47 dvi_decoder u_dvi_decoder(
  48. 48     //input
  49. 49     .clk_200m      (clk_200m),
  50. 50     .tmdsclk_p     (tmdsclk_p),      // tmds clock
  51. 51     .tmdsclk_n     (tmdsclk_n),      // tmds clock
  52. 52     .blue_p        (blue_p),         // Blue data in
  53. 53     .green_p       (green_p),        // Green data in
  54. 54     .red_p         (red_p    ),      // Red data in
  55. 55     .blue_n        (blue_n   ),      // Blue data in
  56. 56     .green_n       (green_n  ),      // Green data in
  57. 57     .red_n         (red_n    ),      // Red data in
  58. 58     .exrst_n       (rst_n),          // external reset input, e.g. reset button
  59. 59     //output      
  60. 60     .reset         (reset),          // rx reset
  61. 61     .pclk          (pclk),           // double rate pixel clock
  62. 62     .pclkx5        (pclkx5),         // 10x pixel as IOCLK
  63. 63     .hsync         (hsync),          // hsync data
  64. 64     .vsync         (vsync),          // vsync data
  65. 65     .de            (de),             // data enable
  66. 66     .red           (red),            // pixel data out
  67. 67     .green         (green),          // pixel data out
  68. 68     .blue          (blue)            // pixel data out
  69. 69
  70. 70   );    // pixel data out      
  71. 71        
  72. 72 endmodule
复制代码

代码第35行是将8bit的红绿蓝的信号拼接成24bit的数据信号。
代码第36行是将输入输出使能信号赋0,表示这个接口是作为输入口使用。
代码第39行至44行,是对热插拔信号进行赋值,为0表示设备未连接,1表示设备连接上。
代码第47行至70行,是对hdmi解码模块的例化。
HDMI解码模块的代码如下:
  1. 1   module dvi_decoder (
  2. 2     input  wire clk_200m,
  3. 3     input  wire tmdsclk_p,      // tmds clock
  4. 4     input  wire tmdsclk_n,      // tmds clock
  5. 5     input  wire blue_p,         // Blue data in
  6. 6     input  wire green_p,        // Green data in
  7. 7     input  wire red_p,          // Red data in
  8. 8     input  wire blue_n,         // Blue data in
  9. 9     input  wire green_n,        // Green data in
  10. 10    input  wire red_n,          // Red data in
  11. 11    input  wire exrst_n,        // external reset input, e.g. reset button
  12. 12  
  13. 13    output reg  reset,          // rx reset
  14. 14    output wire pclk,           // regenerated pixel clock
  15. 15    output wire pclkx5,         // 5x pixel as IOCLK  
  16. 16    output wire hsync,          // hsync data
  17. 17    output wire vsync,          // vsync data
  18. 18    output wire de,             // data enable  
  19. 19    output wire [7:0] red,      // pixel data out
  20. 20    output wire [7:0] green,    // pixel data out
  21. 21    output wire [7:0] blue      // pixel data out  
  22. 22   
  23. 23    );   
  24. 24  
  25. 25  //parameter define   
  26. 26  parameter kCtlTknCount = 128; //检测到控制字符的最低持续个数
  27. 27  parameter kTimeoutMs = 50;    //未检测到控制字符的最大时间间隔
  28. 28  
  29. 29  //wire define      
  30. 30  wire de_b, de_g, de_r;
  31. 31  wire blue_rdy, green_rdy, red_rdy;  //数据准备好信号
  32. 32  wire blue_vld, green_vld, red_vld;  //数据有效信号
  33. 33  
  34. 34  //*****************************************************
  35. 35  //**                    main code
  36. 36  //*****************************************************  
  37. 37  
  38. 38  assign de = de_b;
  39. 39  
  40. 40  //RX端的复位信号
  41. 41  always @(posedge pclk ) begin
  42. 42      if (!exrst_n )
  43. 43          reset <= 1'b1;
  44. 44      else         
  45. 45          reset <= ~pll_lckd;      
  46. 46  end
  47. 47  
  48. 48  //HDMI时钟模块
  49. 49   tmds_clock u_tmds_clock(
  50. 50  
  51. 51       .tmds_clk_p (tmdsclk_p),
  52. 52       .tmds_clk_n (tmdsclk_n),
  53. 53  
  54. 54       .pixelclk   (pclk),
  55. 55       .serialclk  (pclkx5),
  56. 56       .alocked    (pll_lckd)
  57. 57      );
  58. 58   
  59. 59  //HDMI红色数据解码模块
  60. 60  tmds_decoder #(
  61. 61    .kCtlTknCount (kCtlTknCount),  //检测到控制字符的最低持续个数
  62. 62    .kTimeoutMs (kTimeoutMs),      //未检测到控制字符的最大时间间隔
  63. 63    .kRefClkFrqMHz (200)           //参考时钟频率
  64. 64  ) u_tmds_decoder_0(
  65. 65  
  66. 66      .arst           (reset),
  67. 67      .pixelclk       (pclk),
  68. 68      .serialclk      (pclkx5),
  69. 69      .refclk         (clk_200m),
  70. 70      .prst           (~exrst_n),
  71. 71      .sdatain_p      (red_p),
  72. 72      .sdatain_n      (red_n),   
  73. 73      .potherchrdy    ({blue_rdy,green_rdy}),
  74. 74      .potherchvld    ({blue_vld,green_vld}),   
  75. 75      .palignerr      (),
  76. 76      .pc0            (),
  77. 77      .pc1            (),   
  78. 78      .pmerdy         (red_rdy),
  79. 79      .pmevld         (red_vld),   
  80. 80      .pvde           (de_r),
  81. 81      .pdatain        (red),      
  82. 82      .peyesize       ()   
  83. 83  );
  84. 84  
  85. 85  //HDMI蓝色数据解码模块
  86. 86  tmds_decoder #(
  87. 87    .kCtlTknCount (kCtlTknCount),  //检测到控制字符的最低持续个数
  88. 88    .kTimeoutMs (kTimeoutMs),      //未检测到控制字符的最大时间间隔
  89. 89    .kRefClkFrqMHz (200)           //参考时钟频率
  90. 90  ) u_tmds_decoder_1(
  91. 91  
  92. 92      .arst           (reset),
  93. 93      .pixelclk       (pclk),
  94. 94      .serialclk      (pclkx5),
  95. 95      .refclk         (clk_200m),
  96. 96      .prst           (~exrst_n),
  97. 97      .sdatain_p      (blue_p),
  98. 98      .sdatain_n      (blue_n),   
  99. 99      .potherchrdy    ({red_rdy,green_rdy}),
  100. 100     .potherchvld    ({red_vld,green_vld}),   
  101. 101     .palignerr      (),
  102. 102     .pc0            (hsync),
  103. 103     .pc1            (vsync),   
  104. 104     .pmerdy         (blue_rdy),
  105. 105     .pmevld         (blue_vld),   
  106. 106     .pvde           (de_b),
  107. 107     .pdatain        (blue),        
  108. 108     .peyesize       ()   
  109. 109 );
  110. 110
  111. 111 //HDMI绿色数据解码模块
  112. 112 tmds_decoder #(
  113. 113   .kCtlTknCount (kCtlTknCount),     //检测到控制字符的最低持续个数
  114. 114   .kTimeoutMs (kTimeoutMs),         //未检测到控制字符的最大时间间隔
  115. 115   .kRefClkFrqMHz (200)              //参考时钟频率
  116. 116 ) u_tmds_decoder_2(
  117. 117
  118. 118     .arst           (reset),
  119. 119     .pixelclk       (pclk),
  120. 120     .serialclk      (pclkx5),
  121. 121     .refclk         (clk_200m),
  122. 122     .prst           (~exrst_n),
  123. 123     .sdatain_p      (green_p),
  124. 124     .sdatain_n      (green_n),   
  125. 125     .potherchrdy    ({red_rdy,blue_rdy}),
  126. 126     .potherchvld    ({red_vld,blue_vld}),   
  127. 127     .palignerr      (),
  128. 128     .pc0            (),
  129. 129     .pc1            (),   
  130. 130     .pmerdy         (green_rdy),
  131. 131     .pmevld         (green_vld),   
  132. 132     .pvde           (de_g),
  133. 133     .pdatain        (green),      
  134. 134     .peyesize       ()   
  135. 135 );
  136. 136
  137. 137 endmodule
复制代码

代码第26行,定义了一个参数kCtlTknCount,这个参数的用意是用来判断控制字符的最低持续个数,这样做是为了防止误识别。
代码第27行,定义了一个参数kTimeoutMs,这个参数的用意是当在一定时间内未检测到控制字符,则对相应的信号进行复位处理。
代码第41行至46行,是对RX端的复位信号进行赋值。这里将RX端进来的时钟信号进入MMCM后产生的lock信号取反,是为了当时钟信号不稳定或者没有的时候对RX端的信号进行复位。
代码第48行至57行,是对HDMI时钟模块的例化。
代码第60行至135行,是对HDMI数据解码模块的例化,其中只有蓝色差分对解出的行场信号是有效的,其他2个差分对解出的不是行场信号。
HDMI时钟模块的代码如下:
  1. 1   module tmds_clock(
  2. 2   
  3. 3        input      tmds_clk_p,
  4. 4        input      tmds_clk_n,
  5. 5   
  6. 6   
  7. 7        output     pixelclk,
  8. 8        output     serialclk,
  9. 9        output     alocked     
  10. 10      );
  11. 11  
  12. 12  //wire define  
  13. 13  wire        clk_in_hdmi_clk;
  14. 14  wire        pixelclk;     
  15. 15  wire        serialclk;
  16. 16  wire        alocked;
  17. 17  wire        clkout1b_unused, clkout2_unused, clkout2b_unused;
  18. 18  wire        clkout3_unused, clkout3b_unused, clkout4_unused;
  19. 19  wire        clkout5_unused, clkout6_unused,drdy_unused, psdone_unused;
  20. 20  wire        clkfbstopped_unused, clkinstopped_unused, clkfboutb_unused;
  21. 21  wire        clkout0b_unused, clkout1_unused ;
  22. 22  wire [15:0] do_unused;
  23. 23  wire        clkfbout_hdmi_clk;
  24. 24  wire        clk_out_5x_hdmi_clk;
  25. 25  
  26. 26  //*****************************************************
  27. 27  //**                    main code
  28. 28  //*****************************************************
  29. 29   
  30. 30    IBUFDS  # (
  31. 31      .DIFF_TERM     ("FALSE"),
  32. 32      .IBUF_LOW_PWR  ("TRUE"),
  33. 33      .IOSTANDARD    ("TMDS_33")   
  34. 34    ) u_IBUFDS(
  35. 35      .O    (clk_in_hdmi_clk),
  36. 36      .I    (tmds_clk_p),
  37. 37      .IB   (tmds_clk_n)     
  38. 38    );
  39. 39  
  40. 40      
  41. 41    MMCME2_ADV                                                                 
  42. 42    #(.BANDWIDTH            ("OPTIMIZED"),
  43. 43      .CLKOUT4_CASCADE      ("FALSE"),
  44. 44      .COMPENSATION         ("ZHOLD"),
  45. 45      .STARTUP_WAIT         ("FALSE"),
  46. 46      .DIVCLK_DIVIDE        (1),
  47. 47      .CLKFBOUT_MULT_F      (5.000),
  48. 48      .CLKFBOUT_PHASE       (0.000),
  49. 49      .CLKFBOUT_USE_FINE_PS ("FALSE"),
  50. 50      .CLKOUT0_DIVIDE_F     (1.000),
  51. 51      .CLKOUT0_PHASE        (0.000),
  52. 52      .CLKOUT0_DUTY_CYCLE   (0.500),
  53. 53      .CLKOUT0_USE_FINE_PS  ("FALSE"),
  54. 54      .CLKOUT1_DIVIDE       (5),
  55. 55      .CLKOUT1_PHASE        (0.000),
  56. 56      .CLKOUT1_DUTY_CYCLE   (0.500),
  57. 57      .CLKOUT1_USE_FINE_PS  ("FALSE"),
  58. 58      .CLKIN1_PERIOD        (6.667))   
  59. 59                                       
  60. 60    mmcm_adv_inst                                                         
  61. 61      // Output clocks                                                   
  62. 62     (                                                                    
  63. 63      .CLKFBOUT            (clkfbout_hdmi_clk),                           
  64. 64      .CLKFBOUTB           (clkfboutb_unused),                           
  65. 65      .CLKOUT0             (clk_out_5x_hdmi_clk),                        
  66. 66      .CLKOUT0B            (clkout0b_unused),                             
  67. 67      .CLKOUT1             (clk_out_1x_hdmi_clk),                              
  68. 68      .CLKOUT1B            (clkout1b_unused),                             
  69. 69      .CLKOUT2             (clkout2_unused),                              
  70. 70      .CLKOUT2B            (clkout2b_unused),                             
  71. 71      .CLKOUT3             (clkout3_unused),                              
  72. 72      .CLKOUT3B            (clkout3b_unused),                             
  73. 73      .CLKOUT4             (clkout4_unused),                              
  74. 74      .CLKOUT5             (clkout5_unused),                              
  75. 75      .CLKOUT6             (clkout6_unused),                              
  76. 76       // Input clock control                                             
  77. 77      .CLKFBIN             (clkfbout_hdmi_clk),                           
  78. 78      .CLKIN1              (clk_in_hdmi_clk),                             
  79. 79      .CLKIN2              (1'b0),                                       
  80. 80       // Tied to always select the primary input clock                  
  81. 81      .CLKINSEL            (1'b1),                                       
  82. 82      // Ports for dynamic reconfiguration                                
  83. 83      .DADDR               (7'h0),                                       
  84. 84      .DCLK                (1'b0),                                       
  85. 85      .DEN                 (1'b0),                                       
  86. 86      .DI                  (16'h0),                                       
  87. 87      .DO                  (do_unused),                                   
  88. 88      .DRDY                (drdy_unused),                                 
  89. 89      .DWE                 (1'b0),                                       
  90. 90      // Ports for dynamic phase shift                                    
  91. 91      .PSCLK               (1'b0),                                       
  92. 92      .PSEN                (1'b0),                                       
  93. 93      .PSINCDEC            (1'b0),                                       
  94. 94      .PSDONE              (psdone_unused),                              
  95. 95      // Other control and status signals                                 
  96. 96      .LOCKED              (alocked),                                
  97. 97      .CLKINSTOPPED        (clkinstopped_unused),                        
  98. 98      .CLKFBSTOPPED        (clkfbstopped_unused),                        
  99. 99      .PWRDWN              (1'b0),                                       
  100. 100     .RST                 (0));                           
  101. 101  
  102. 102 // 5x fast serial clock
  103. 103 BUFG u_BUFG(
  104. 104       .O (serialclk), // 1-bit output: Clock output (connect to I/O clock loads).
  105. 105       .I (clk_out_5x_hdmi_clk)  // 1-bit input: Clock input (connect to an IBUF or BUFMR).
  106. 106    );   
  107. 107   
  108. 108 BUFG u_BUFG_0(
  109. 109       .O (pixelclk), // 1-bit output: Clock output (connect to I/O clock loads).
  110. 110       .I (clk_out_1x_hdmi_clk)  // 1-bit input: Clock input (connect to an IBUF or BUFMR).
  111. 111    );      
  112. 112  
  113. 113  
  114. 114 endmodule
复制代码

代码第30行至38行,是通过IBUFDS将差分时钟转换为单端时钟。
代码第41行至100行,是通过MMCM产生一个和输入时钟同频的和5倍频的时钟,以提供给其他模块使用。代码第58行设置了一个输入时钟的周期,即频率为150MHz,所以本实验最大支持的分辨率为1080P。
代码第103行至111行,是将MMCM产生的时钟加入全局时钟网络,以减小延迟和抖动。
本次实验的HDMI数据解码模块包含以下四个模块,框图如下:
4232216.png

图 42.4.6 HDMI数据解码模块框图

串并转化模块模块:将进来的串行差分数据转化为串行单端数据,然后把串行单端数据转化为10bit的并行数据。
字对齐校准模块:将10bit的并行数据与4个控制字符做对比,如果连续在16个延迟值都能检测到控制字符,则认为已经找到延迟值的范围,此时取中间的延迟值做为最终的值,表示校准已经完成。
数据同步模块:将红绿蓝三组的数据进行同步,防止图像的颜色出现偏差。
8b/10b解码模块:将并行的10bit数据转化为8bit数据,同时产生行场信号和数据有效使能。
由系统框图可知,HDMI数据解码模块主要例化了以下四个模块:串并转化模块模块(selectio_1_10)、字对齐校准模块(phasealign)、数据同步模块(channelbond)和8b/10b解码模块(decoder)。各模块端口及信号连接如下图所示:
4232642.png

图 42.4.7 HDMI数据解码模块原理图

HDMI数据解码模块将输入的串行差分信号转化为并行数据,再通过字对齐校准模块确定延迟值,然后通过数据同步模块同步三组颜色数据,最后经过8b/10b解码模块输出8bit颜色数据和行场信号和数据有效使能。
HDMI数据解码模块的代码如下:
  1. 1   module tmds_decoder #(   
  2. 2     parameter kCtlTknCount = 128,    //检测到控制字符的最低持续个数
  3. 3     parameter kTimeoutMs = 50,       //未检测到控制字符的最大时间间隔
  4. 4     parameter kRefClkFrqMHz = 200    //参考时钟频率
  5. 5   )(
  6. 6       input              arst        ,   
  7. 7       input              pixelclk    ,   //TMDS clock x1 (CLKDIV)
  8. 8       input              serialclk   ,   //TMDS clock x5 (CLK)
  9. 9       input              refclk      ,   //200 MHz reference clock
  10. 10      input              prst        ,   //Synchronous reset to restart lock procedure
  11. 11      input              sdatain_p   ,   //TMDS data channel positive
  12. 12      input              sdatain_n   ,   //TMDS data channel negative
  13. 13      input   [1:0]      potherchrdy ,   //另外2组数据同步完成信号
  14. 14      input   [1:0]      potherchvld ,   //另外2组数据校准完成信号
  15. 15      output             palignerr   ,   //校准错误信号
  16. 16      output             pc0         ,   //控制信号
  17. 17      output             pc1         ,   //控制信号
  18. 18      output             pmerdy      ,   //数据同步完成信号
  19. 19      output             pmevld      ,   //数据校准完成信号
  20. 20      output             pvde        ,   //数据有效使能
  21. 21      output   [7:0]     pdatain     ,   //解码后8bit颜色数据
  22. 22      output   [4:0]     peyesize        //连续监测到控制字符的延迟值的次数
  23. 23  
  24. 24      );
  25. 25      
  26. 26  //wire define
  27. 27  wire                            pc0;           //控制信号
  28. 28  wire                            pc1;           //控制信号
  29. 29  wire                            pvde;          //数据有效使能
  30. 30  wire  [7:0]                     pdatain;       //解码后的8bit颜色数据
  31. 31  wire  [4:0]                     peyesize;      //连续监测到控制字符的延迟值的次数
  32. 32  wire  [9:0]                     pdatainbnd;    //同步后的10bit并行数据
  33. 33  wire  [9:0]                     pdatainraw;    //转化后的10bit并行数据
  34. 34  wire                            pmerdy_int;    //数据同步完成信号
  35. 35  wire                            paligned;      //数据校准完成信号
  36. 36  wire                            palignerr_int; //校准错误信号
  37. 37  wire                            pidly_ld;      //IDELAYE控制信号
  38. 38  wire                            pidly_ce;      //IDELAYE控制信号
  39. 39  wire                            pidly_inc;     //IDELAYE控制信号
  40. 40  wire  [4:0]                     pidly_cnt;     //IDELAYE延迟值
  41. 41  wire                            pbitslip;      //字对齐移动信号
  42. 42  
  43. 43  //*****************************************************
  44. 44  //**                    main code
  45. 45  //*****************************************************
  46. 46  
  47. 47  assign  palignerr = palignerr_int;  //校准错误信号
  48. 48  assign  pmevld = paligned;          //数据校准完成信号
  49. 49  assign  pmerdy = pmerdy_int;        //数据同步完成信号
  50. 50  
  51. 51  //8b/10b解码模块  
  52. 52  decoder u_decoder(
  53. 53      .pixelclk    (pixelclk),
  54. 54      .pdatainbnd  (pdatainbnd),
  55. 55      .potherchrdy (potherchrdy),
  56. 56      .pmerdy_int  (pmerdy_int),
  57. 57  
  58. 58      .pc0         (pc0),
  59. 59      .pc1         (pc1),
  60. 60      .pvde        (pvde),   
  61. 61      .pdatain     (pdatain)
  62. 62      );  
  63. 63  
  64. 64  //串并转化模块模块
  65. 65   selectio_1_10 #(
  66. 66       .SYS_W (1),
  67. 67       .DEV_W (10)     
  68. 68   )u_selectio_1_10(
  69. 69      .clk_div_in                (pixelclk),
  70. 70      .clk_in                    (serialclk),   
  71. 71      .data_in_from_pins_p       (sdatain_p),
  72. 72      .data_in_from_pins_n       (sdatain_n),
  73. 73      .ref_clock                 (refclk),
  74. 74      //Encoded parallel data (raw)
  75. 75      .data_in_to_device         (pdatainraw),   
  76. 76      //Control for phase alignment
  77. 77      .bitslip                   (pbitslip),  
  78. 78      .in_delay_ld               (pidly_ld),
  79. 79      .in_delay_data_ce          (pidly_ce),
  80. 80      .in_delay_data_inc         (pidly_inc),
  81. 81      .in_delay_data_cnt         (pidly_cnt),
  82. 82      .io_reset                  (arst)   
  83. 83   
  84. 84   );   
  85. 85               
  86. 86  //字对齐校准模块   
  87. 87   phasealign #(
  88. 88       .kTimeoutMs (kTimeoutMs),   
  89. 89       .kRefClkFrqMHz (kRefClkFrqMHz),  
  90. 90       .kCtlTknCount (kCtlTknCount)   
  91. 91   )u_phasealign(
  92. 92      .prst       (prst),
  93. 93      .arst       (arst),
  94. 94      .pixelclk   (pixelclk),
  95. 95      .refclk     (refclk),
  96. 96      .pdata      (pdatainraw),
  97. 97      .pidly_ld   (pidly_ld),
  98. 98      .pidly_ce   (pidly_ce),
  99. 99      .pidly_inc  (pidly_inc),
  100. 100     .pidly_cnt  (pidly_cnt),
  101. 101     .pbitslip   (pbitslip),
  102. 102     .paligned   (paligned),
  103. 103     .perror     (palignerr_int),
  104. 104     .peyesize   (peyesize)
  105. 105  );
  106. 106
  107. 107 //数据同步模块
  108. 108 channelbond u_channelbond(
  109. 109   .clk           (pixelclk),
  110. 110   .rawdata       (pdatainraw),
  111. 111   .iamvld        (paligned),
  112. 112   .other_ch0_vld (potherchvld[0]),
  113. 113   .other_ch1_vld (potherchvld[1]),
  114. 114   .other_ch0_rdy (potherchrdy[0]),
  115. 115   .other_ch1_rdy (potherchrdy[1]),
  116. 116   .iamrdy        (pmerdy_int),
  117. 117   .sdata         (pdatainbnd)
  118. 118 );
  119. 119   
  120. 120 endmodule
复制代码

代码第52行至62行,是对8b/10b解码模块的例化。该模块主要是将并行的10bit数据转化为8bit数据,同时产生行场信号和数据有效使能。
代码第65行至84行,是对串并转化模块模块的例化。该模块主要是将进来的串行差分数据转化为串行单端数据,然后把串行单端数据转化为10bit的并行数据。其中参数SYS_W表示差分对个数,这里设为1表示只有1对差分对;参数DEV_W表示并串的比例,这里设为10表示并串比为10 :1。
代码第87行至105行,是对字对齐校准模块的例化。该模块主要是将10bit的并行数据与4个控制字符做对比,如果连续在16个延迟值都能检测到控制字符,则认为已经找到延迟值的范围,此时取中间的延迟值做为最终的值,表示校准已经完成。
代码第108行至118行,是对数据同步模块的例化。该模块主要是将红绿蓝三组的数据进行同步,防止图像的颜色出现偏差。
串并转化模块模块的代码如下:
  1. 1   module selectio_1_10
  2. 2      // width of the data for the system
  3. 3    #(parameter SYS_W = 1,
  4. 4      // width of the data for the device
  5. 5      parameter DEV_W = 10)
  6. 6    (
  7. 7     // From the system into the device
  8. 8     input  [SYS_W-1:0]   data_in_from_pins_p,   //差分数据输入
  9. 9     input  [SYS_W-1:0]   data_in_from_pins_n,
  10. 10    output [DEV_W-1:0]   data_in_to_device,     //10bit并行数据输出
  11. 11    input                in_delay_ld,           //加载寄存器的延迟值
  12. 12    input  [SYS_W -1 :0] in_delay_data_ce,      //调整延迟值的有效使能
  13. 13    input  [SYS_W -1 :0] in_delay_data_inc,     //增减延迟值
  14. 14   
  15. 15    input                ref_clock,             //200M参考时钟
  16. 16    input  [SYS_W-1:0]   bitslip,               //字对齐调整信号
  17. 17    output  [4:0]        in_delay_data_cnt,     //当前延迟值
  18. 18                                    
  19. 19    input                clk_in,                //5倍像素时钟
  20. 20    input                clk_div_in,            //1倍像素时钟
  21. 21    input                io_reset               //io的复位
  22. 22    );   
  23. 23   
  24. 24    localparam         num_serial_bits = DEV_W/SYS_W;
  25. 25  
  26. 26  
  27. 27    wire [SYS_W-1:0]  data_in_from_pins_int;
  28. 28    wire [SYS_W-1:0]  data_in_from_pins_delay;
  29. 29    wire [SYS_W-1:0]  delay_data_busy;
  30. 30    wire [SYS_W-1:0]  in_delay_ce;
  31. 31    wire [SYS_W-1:0]  in_delay_inc_dec;
  32. 32    wire              ref_clock_bufg;
  33. 33    wire [SYS_W-1:0]  iserdes_q[0:13];   
  34. 34   
  35. 35    assign in_delay_ce = { in_delay_data_ce[0]};
  36. 36    assign in_delay_inc_dec = { in_delay_data_inc[0]};
  37. 37  
  38. 38    // We have multiple bits- step over every bit, instantiating the required elements
  39. 39    genvar pin_count;
  40. 40    genvar slice_count;
  41. 41    generate for (pin_count = 0; pin_count < SYS_W; pin_count = pin_count + 1) begin: pins
  42. 42      // Instantiate the buffers
  43. 43      ////------------------------------
  44. 44      // Instantiate a buffer for every bit of the data bus
  45. 45      IBUFDS
  46. 46        #(.DIFF_TERM  ("FALSE"),             // Differential termination
  47. 47          .IOSTANDARD ("TMDS_33"))
  48. 48       ibufds_inst
  49. 49         (.I          (data_in_from_pins_p  [pin_count]),
  50. 50          .IB         (data_in_from_pins_n  [pin_count]),
  51. 51          .O          (data_in_from_pins_int[pin_count]));
  52. 52  
  53. 53      // Instantiate the delay primitive
  54. 54      ////-------------------------------
  55. 55  
  56. 56       (* IODELAY_GROUP = "selectio_wiz_0_group" *)
  57. 57       IDELAYE2
  58. 58         # (
  59. 59           .CINVCTRL_SEL           ("FALSE"),          // TRUE, FALSE
  60. 60           .DELAY_SRC              ("IDATAIN"),        // IDATAIN, DATAIN
  61. 61           .HIGH_PERFORMANCE_MODE  ("FALSE"),          // TRUE, FALSE
  62. 62           .IDELAY_TYPE            ("VARIABLE"),       // FIXED, VARIABLE, or VAR_LOADABLE
  63. 63           .IDELAY_VALUE           (0),                // 0 to 31
  64. 64           .REFCLK_FREQUENCY       (200.0),
  65. 65           .PIPE_SEL               ("FALSE"),
  66. 66           .SIGNAL_PATTERN         ("DATA"))           // CLOCK, DATA
  67. 67         idelaye2_bus
  68. 68             (
  69. 69           .DATAOUT                (data_in_from_pins_delay[pin_count]),
  70. 70           .DATAIN                 (1'b0),                              
  71. 71           .C                      (clk_div_in),
  72. 72           .CE                     (in_delay_ce[pin_count]),
  73. 73           .INC                    (in_delay_inc_dec[pin_count]),
  74. 74           .IDATAIN                (data_in_from_pins_int  [pin_count]),
  75. 75           .LD                     (in_delay_ld),
  76. 76           .REGRST                 (io_reset),
  77. 77           .LDPIPEEN               (1'b0),
  78. 78           .CNTVALUEIN             (5'b00000),
  79. 79           .CNTVALUEOUT            (in_delay_data_cnt),
  80. 80           .CINVCTRL               (1'b0)
  81. 81           );
  82. 82  
  83. 83       // local wire only for use in this generate loop
  84. 84       wire cascade_shift;
  85. 85       wire [SYS_W-1:0] icascade1;
  86. 86       wire [SYS_W-1:0] icascade2;
  87. 87       wire clk_in_int_inv;
  88. 88  
  89. 89       assign clk_in_int_inv = ~ clk_in;
  90. 90  
  91. 91       // declare the iserdes
  92. 92       ISERDESE2
  93. 93         # (
  94. 94           .DATA_RATE         ("DDR"),
  95. 95           .DATA_WIDTH        (10),
  96. 96           .INTERFACE_TYPE    ("NETWORKING"),
  97. 97           .DYN_CLKDIV_INV_EN ("FALSE"),
  98. 98           .DYN_CLK_INV_EN    ("FALSE"),
  99. 99           .NUM_CE            (2),
  100. 100          .OFB_USED          ("FALSE"),
  101. 101          .IOBDELAY          ("IFD"),                              
  102. 102          .SERDES_MODE       ("MASTER"))
  103. 103        iserdese2_master (
  104. 104          .Q1                (iserdes_q[0][pin_count]),
  105. 105          .Q2                (iserdes_q[1][pin_count]),
  106. 106          .Q3                (iserdes_q[2][pin_count]),
  107. 107          .Q4                (iserdes_q[3][pin_count]),
  108. 108          .Q5                (iserdes_q[4][pin_count]),
  109. 109          .Q6                (iserdes_q[5][pin_count]),
  110. 110          .Q7                (iserdes_q[6][pin_count]),
  111. 111          .Q8                (iserdes_q[7][pin_count]),
  112. 112          .SHIFTOUT1         (icascade1[pin_count]),               
  113. 113          .SHIFTOUT2         (icascade2[pin_count]),               
  114. 114          .BITSLIP           (bitslip[pin_count]),                 
  115. 115                                                                  
  116. 116          .CE1               (1'b1),                       
  117. 117          .CE2               (1'b1),                       
  118. 118          .CLK               (clk_in),                             
  119. 119          .CLKB              (clk_in_int_inv),                     
  120. 120          .CLKDIV            (clk_div_in),                        
  121. 121          .CLKDIVP           (1'b0),
  122. 122          .D                 (1'b0),                              
  123. 123          .DDLY              (data_in_from_pins_delay[pin_count]),
  124. 124          .RST               (io_reset),                           
  125. 125          .SHIFTIN1          (1'b0),
  126. 126          .SHIFTIN2          (1'b0),
  127. 127     // unused connections
  128. 128          .DYNCLKDIVSEL      (1'b0),
  129. 129          .DYNCLKSEL         (1'b0),
  130. 130          .OFB               (1'b0),
  131. 131          .OCLK              (1'b0),
  132. 132          .OCLKB             (1'b0),
  133. 133          .O                 ());                                 
  134. 134
  135. 135      ISERDESE2
  136. 136        # (
  137. 137          .DATA_RATE         ("DDR"),
  138. 138          .DATA_WIDTH        (10),
  139. 139          .INTERFACE_TYPE    ("NETWORKING"),
  140. 140          .DYN_CLKDIV_INV_EN ("FALSE"),
  141. 141          .DYN_CLK_INV_EN    ("FALSE"),
  142. 142          .NUM_CE            (2),
  143. 143          .OFB_USED          ("FALSE"),
  144. 144          .IOBDELAY          ("IFD"),               
  145. 145          .SERDES_MODE       ("SLAVE"))
  146. 146        iserdese2_slave (
  147. 147          .Q1                (),
  148. 148          .Q2                (),
  149. 149          .Q3                (iserdes_q[8][pin_count]),
  150. 150          .Q4                (iserdes_q[9][pin_count]),
  151. 151          .Q5                (iserdes_q[10][pin_count]),
  152. 152          .Q6                (iserdes_q[11][pin_count]),
  153. 153          .Q7                (iserdes_q[12][pin_count]),
  154. 154          .Q8                (iserdes_q[13][pin_count]),
  155. 155          .SHIFTOUT1         (),
  156. 156          .SHIFTOUT2         (),
  157. 157          .SHIFTIN1          (icascade1[pin_count]),  
  158. 158          .SHIFTIN2          (icascade2[pin_count]),  
  159. 159          .BITSLIP           (bitslip[pin_count]),   
  160. 160                                                      
  161. 161          .CE1               (1'b1),         
  162. 162          .CE2               (1'b1),         
  163. 163          .CLK               (clk_in),               
  164. 164          .CLKB              (clk_in_int_inv),        
  165. 165          .CLKDIV            (clk_div_in),            
  166. 166          .CLKDIVP           (1'b0),
  167. 167          .D                 (1'b0),                  
  168. 168          .DDLY              (1'b0),
  169. 169          .RST               (io_reset),              
  170. 170    // unused connections
  171. 171          .DYNCLKDIVSEL      (1'b0),
  172. 172          .DYNCLKSEL         (1'b0),
  173. 173          .OFB               (1'b0),
  174. 174          .OCLK              (1'b0),
  175. 175          .OCLKB             (1'b0),
  176. 176          .O                 ());              
  177. 177      ////---------------------------------------------------------
  178. 178      for (slice_count = 0; slice_count < num_serial_bits;
  179. 179           slice_count = slice_count + 1) begin: in_slices
  180. 180         assign data_in_to_device[slice_count] =
  181. 181           iserdes_q[num_serial_bits-slice_count-1];
  182. 182      end
  183. 183   end
  184. 184   endgenerate
  185. 185   
  186. 186 // IDELAYCTRL is needed for calibration
  187. 187 (* IODELAY_GROUP = "selectio_wiz_0_group" *)
  188. 188   IDELAYCTRL
  189. 189     delayctrl (
  190. 190      .RDY    (delay_locked),
  191. 191      .REFCLK (ref_clock),
  192. 192      .RST    (io_reset));
  193. 193
  194. 194 endmodule
复制代码

串并转化模块模块主要是将进来的串行差分数据转化为并行数据,其运用了xilinx提供的底层原语ISERDESE、IDELAYE、IDELAYCTRL和IBUFDS。
IBUFDS原语的作用是将进来的差分信号转化为单端信号。
IDELAYCTRL原语与IDELAYE是绑定的,使用了IDELAYE原语就必须要用IDELAYCTRL原语。IDELAYE原语的作用是将进来的信号进行延时,然后输出给其他原语使用。
下面来讲解IDELAYE原语中几个参数的作用。
(1)CINVCTRL_SEL:切换时钟C的极性,TRUE表示切换,FALSE表示不切换;
(2)DELAY_SRC:选择串行数据的输入方式,选择IDATAIN就从IDATAIN接口输入,选择DATAIN就从DATAIN接口输入;
(3)HIGH_PERFORMANCE_MODE:选择性能模式,TRUE表示减小输出抖动,FALSE表示不减小输出抖动;
(4)IDELAY_TYPE:选择延迟模式,FIXED表示设定一个固定的延迟值,VARIABLE是可以实时调整延迟值,VAR_LOADABLE即可以实时调整延迟值,还可以将CNTVALUEIN接口的值作为延迟值,VAR_LOAD_PIPE即可以实时调整延迟值,也可以存储CNTVALUEIN的值;
(5)IDELAY_VALUE:固定延迟值,只在FIXED模式有效;
(6)REFCLK_FREQUENCY:参考时钟频率,本次实验设为200MHz;
(7)PIPE_SEL:选择pipeline模式,只有在VAR_LOAD_PIPE中可以选择TRUE,其他模式都选FALSE;
(8)SIGNAL_PATTERN:输出信号的类型,CLOCK表示需要延迟的是时钟信号,DATA表示需要延迟的是数据信号。
本次实验中选择的是VARIABLE模式,即实时调整延迟值,而延迟值的变化与下面几个参数有关,如下图所示:
4247389.png

图 42.4.8 延时值变化表

ISERDESE原语的作用是将串行数据转化为并行数据,其示意图如下所示:
4247487.png

图 42.4.9 ISERDESE示意图

通过示意图可以发现一个ISERDESE最多能转化8bit的并行数据,而本次实验需要转化10bit的并行数据,所以需要2个ISERDESE级联,其级联的示意图如下所示:
4247637.png

图 42.4.10 ISERDESE级联示意图

这里有一个地方大家需要注意,xilinx的ISERDESE最多只能级联2个,转化的最大并行数据的位宽为14位。
看到这里大家可能有个疑惑,那就是ISERDESE是怎么知道数据的高低位从而生成并行数据。说到这里就要说下字对齐调整信号(bitslip),这个信号就是调整数据的高低位顺序的,调整的示意图如下所示:
4247861.png

图 42.4.11 字对齐调整示意图

从上图可以看出当bitslip每拉高一次,输出的并行数据的顺序就变化一次,因此在这所有组合的并行数据中必然有大家所需要的并行数据。
字对齐校准模块的代码如下:
  1. 1   module phasealign #(
  2. 2       parameter kTimeoutMs = 50,
  3. 3       parameter kRefClkFrqMHz = 200,
  4. 4       parameter kCtlTknCount = 128  
  5. 5   )(
  6. 6   
  7. 7       input                           prst          ,
  8. 8       input                           arst          ,
  9. 9       input                           refclk        ,
  10. 10      input                           pixelclk      ,
  11. 11      output                          pbitslip      ,
  12. 12      input   [9:0]                   pdata         ,
  13. 13      output                          pidly_ld      ,
  14. 14      output                          pidly_ce      ,
  15. 15      output                          pidly_inc     ,
  16. 16      input   [4:0]                   pidly_cnt     ,
  17. 17      output                          paligned      ,
  18. 18      output                          perror        ,
  19. 19      output  [4:0]                   peyesize   
  20. 20      );
  21. 21      
  22. 22  //parameter define  
  23. 此处省略一段代码。
  24. 300 always@(*)begin
  25. 301     case(pstate)
  26. 302         ResetSt:    begin
  27. 303                         pStateNxt <= IdleSt;                       
  28. 304                     end
  29. 305         IdleSt:     begin
  30. 306                         if( pblank_begin)
  31. 307                             pStateNxt <= TokenSt;
  32. 308                         else if(ptimeoutovf)
  33. 309                             pStateNxt <= JtrZoneSt;                                               
  34. 310                         else
  35. 311                             pStateNxt <= IdleSt;
  36. 312                     end
  37. 313         TokenSt:    begin
  38. 314                         if( !ptkn_flagq)
  39. 315                             pStateNxt <= IdleSt;
  40. 316                         else if(pctltkn_ovf)
  41. 317                             pStateNxt <= EyeOpenSt;                                               
  42. 318                         else
  43. 319                             pStateNxt <= TokenSt;                          
  44. 320                     end
  45. 321         JtrZoneSt:  begin
  46. 322                         if(pfoundeye_flag)
  47. 323                             pStateNxt <= DlyDecSt;
  48. 324                         else
  49. 325                             pStateNxt <= DlyIncSt;
  50. 326                     end                    
  51. 327         EyeOpenSt:  begin
  52. 328                         if(peyeopen_cnt == kEyeOpenCntEnough)
  53. 329                             pStateNxt <= JtrZoneSt;
  54. 330                         else
  55. 331                             pStateNxt <= DlyIncSt;
  56. 332                     end                    
  57. 333         DlyIncSt:   begin
  58. 334                         pStateNxt <= DlyTstOvfSt;   
  59. 335                     end
  60. 336         DlyTstOvfSt:begin
  61. 337                         if(pdelaywait_ovf)
  62. 338                             if(pdelay_ovf)
  63. 339                                 pStateNxt <= AlignErrorSt;
  64. 340                             else
  65. 341                                 pStateNxt <= IdleSt;
  66. 342                         else
  67. 343                             pStateNxt <= DlyTstOvfSt;                        
  68. 344                     end                     
  69. 345         DlyDecSt:   begin
  70. 346                         pStateNxt <= DlyTstCenterSt;   
  71. 347                     end
  72. 348         DlyTstCenterSt:begin
  73. 349                         if(pdelaywait_ovf)
  74. 350                             if(pdelay_center)
  75. 351                                 pStateNxt <= AlignedSt;
  76. 352                             else
  77. 353                                 pStateNxt <= DlyDecSt;
  78. 354                         else
  79. 355                             pStateNxt <= DlyTstCenterSt;                        
  80. 356                     end
  81. 357         AlignedSt:  begin
  82. 358                         pStateNxt <= AlignedSt;   
  83. 359                     end     
  84. 360         AlignErrorSt:  begin
  85. 361                         pStateNxt <= AlignErrorSt;   
  86. 362                     end   
  87. 363         default:begin
  88. 364                         pStateNxt <= IdleSt;
  89. 365                                       
  90. 366                 end                 
  91. 367     endcase   
  92. 368 end
  93. 369
  94. 370  syncbase #(
  95. 371      .kResetTo (0)   
  96. 372  )u_syncbaseovf(
  97. 373     .areset     (arst),
  98. 374     .inclk      (refclk),
  99. 375     .iin        (rtimeoutovf),
  100. 376     .outclk     (pixelclk),
  101. 377     .oout       (ptimeoutovf)
  102. 378  );  
  103. 379         
  104. 380  syncbase #(
  105. 381      .kResetTo (1)
  106. 382  )u_syncbaserst(
  107. 383     .areset     (arst),
  108. 384     .inclk      (pixelclk),
  109. 385     .iin        (ptimeoutrst),
  110. 386     .outclk     (refclk),
  111. 387     .oout       (rtimeoutrst)
  112. 388  );                                                                     
  113. 389                                                                           
  114. 390 endmodule        
复制代码

                                                                    
字对齐校准模块的主要作用就是确定IDELAYE延迟值,确保ISERDESE解出正确的10bit数据。下面是字对齐校准模块的状态跳转流程图:
4252853.png

图 42.4.12 状态跳转流程图

(1)ResetSt:对所有的寄存器复位,使其处于初值的状态;
(2)IdleSt:信号rtimeout_cnt开始计数,如果在信号rtimeout_cnt未达到最大的时间间隔(kTimeoutEnd)就检测到控制字符,就将状态跳转到TokenSt,否则就跳转到状态JtrZoneSt;
(3)TokenSt:如果在128个时钟周期内检测到信号ptkn_flagq为低,表示在128个时钟周期内没有检测到128个控制字符,则认为此次得延时值是不稳定的,就将状态跳转到IdleSt,否则就认为此次得延时值是稳定的,状态跳转到EyeOpenSt,同时对计数器pctltkn_cnt和rtimeout_cnt进行清零;
(4)JtrZoneSt:若找到了延时区间,则下一步将状态跳转到DlyDecSt,用以确定延时区间的中间延时值,否则就认为还没有找到延时区间,将状态跳转到DlyIncSt,用以重新查找延时区间;
(5)EyeOpenSt:如果连续16次都能连续监测到128个控制字符,则说明已经确定了延时区间的范围,将状态跳转到JtrZoneSt,否则将状态跳转到DlyIncSt;
(6)DlyIncSt:之前的延时值不能完全正确的解出控制字符,所以在pidly_inc为高时拉高信号pidly_ce,使得延时值加一,直接跳转到状态DlyTstOvfSt;
(7)DlyTstOvfSt:如果信号pidly_cnt的值不为0,说明这一次的32次延时值还没有测完,将状态跳转到IdleSt,如果测试完了,将状态跳转到AlignErrorSt,表明此次的字对齐是不对的;
(8)DlyDecSt:说明延时区间已经找到了,接下来就是找到延时区间的中间延时值,之前的延时值是区间的最大值,所以将此延时值减8就是区间的中间延时值,所以在此状态当pidly_inc为低时拉高信号pidly_ce,使得延时值减一,然后将状态跳转到DlyTstCenterSt;
(9)DlyTstCenterSt:当延时值为中间延时值时,将状态跳转到AlignedSt,表明延时值已经确定,状态不需要跳转了,否则将状态跳转到DlyDecSt,使得延时值继续减一,循坏往返8次就可以得到中间延时值;
(10)AlignedSt:表明延时值已经确定,状态不需要跳转了;
(11)AlignErrorSt:表明此次的字对齐的顺序是错误的,等到信号pbitslip拉高后,将状态复位到ResetSt。
代码370行至388行的作用是起到信号跨时钟域同步的作用。波形如下图所示:
4253952.png

图 42.4.13 信号跨时钟域同步

数据同步模块的代码如下:
  1. 1   module channelbond (
  2. 2     input  wire       clk,
  3. 3     input  wire [9:0] rawdata,
  4. 4     input  wire       iamvld,
  5. 5     input  wire       other_ch0_vld,
  6. 6     input  wire       other_ch1_vld,
  7. 7     input  wire       other_ch0_rdy,
  8. 8     input  wire       other_ch1_rdy,
  9. 9     output reg        iamrdy,
  10. 10    output reg [9:0]  sdata
  11. 11  );
  12. 12  
  13. 13  //parameter define
  14. 14  parameter CTRLTOKEN0 = 10'b1101010100;
  15. 15  parameter CTRLTOKEN1 = 10'b0010101011;
  16. 16  parameter CTRLTOKEN2 = 10'b0101010100;
  17. 17  parameter CTRLTOKEN3 = 10'b1010101011;
  18. 18  
  19. 19  //reg define
  20. 20  reg [3:0] wa;                  //写ram地址
  21. 21  reg [3:0] ra;                  //读ram地址
  22. 22  reg       we;                  //写ram使能
  23. 23  reg       rcvd_ctkn;           //控制字符使能
  24. 24  reg       rcvd_ctkn_d0;        
  25. 25  reg       rcvd_ctkn_pos;       //控制字符上升沿
  26. 26  reg       skip_line;           //跳过当前行使能
  27. 27  reg       rawdata_vld_d0;      
  28. 28  reg       rawdata_vld_pos;     //数据全部校准完成使能上升沿
  29. 29  reg       ra_en;               //读使能
  30. 30                                 
  31. 31  //wire define                  
  32. 32  wire      rawdata_vld;         //数据全部校准完成使能  
  33. 33  wire [9:0]dpfo_dout;           //ram输出数据
  34. 34  wire      next_rcvd_ctkn_pos;  //同步信号准备好信号
  35. 35   
  36. 36  //*****************************************************
  37. 37  //**                    main code
  38. 38  //*****************************************************   
  39. 39   
  40. 40  assign rawdata_vld = other_ch0_vld & other_ch1_vld & iamvld;
  41. 41  assign next_rcvd_ctkn_pos = skip_line & rcvd_ctkn_pos;
  42. 42  
  43. 43  
  44. 44  ////////////////////////////////////////////////////////
  45. 45  // FIFO Write Control Logic
  46. 46  ////////////////////////////////////////////////////////
  47. 47  always @ (posedge clk) begin
  48. 48    we <=#1 rawdata_vld;
  49. 49  end
  50. 50  
  51. 51  always @ (posedge clk) begin
  52. 52    if(rawdata_vld)
  53. 53      wa <=#1 wa + 1'b1;
  54. 54    else
  55. 55      wa <=#1 4'h0;
  56. 56  end
  57. 57  
  58. 58  DRAM16XN #(.data_width(10))
  59. 59  cbfifo_i (
  60. 60         .DATA_IN(rawdata),
  61. 61         .ADDRESS(wa),
  62. 62         .ADDRESS_DP(ra),
  63. 63         .WRITE_EN(we),
  64. 64         .CLK(clk),
  65. 65         .O_DATA_OUT(),
  66. 66         .O_DATA_OUT_DP(dpfo_dout));
  67. 67  
  68. 68  always @ (posedge clk) begin
  69. 69    sdata <=#1 dpfo_dout;
  70. 70  end
  71. 71  
  72. 72  ////////////////////////////////////////////////////////
  73. 73  // FIFO read Control Logic
  74. 74  ////////////////////////////////////////////////////////
  75. 75  
  76. 76  ////////////////////////////////
  77. 77  // Use blank period beginning
  78. 78  // as a speical marker to
  79. 79  // align all channel together
  80. 80  ////////////////////////////////
  81. 81  
  82. 82  always @ (posedge clk) begin
  83. 83    rcvd_ctkn <=#1 ((sdata == CTRLTOKEN0) || (sdata == CTRLTOKEN1)
  84. 84                 || (sdata == CTRLTOKEN2) || (sdata == CTRLTOKEN3));
  85. 85    rcvd_ctkn_d0 <=#1 rcvd_ctkn;
  86. 86    rcvd_ctkn_pos <=#1 !rcvd_ctkn_d0 & rcvd_ctkn;
  87. 87  end
  88. 88  
  89. 89  /////////////////////////////
  90. 90  //skip the current line
  91. 91  /////////////////////////////
  92. 92  
  93. 93  always @ (posedge clk) begin
  94. 94    if(!rawdata_vld)
  95. 95      skip_line <=#1 1'b0;
  96. 96    else if(rcvd_ctkn_pos)
  97. 97      skip_line <=#1 1'b1;
  98. 98  end
  99. 99  
  100. 100 //////////////////////////////
  101. 101 //Declare my own readiness
  102. 102 //////////////////////////////
  103. 103 always @ (posedge clk) begin
  104. 104   if(!rawdata_vld)
  105. 105     iamrdy <=#1 1'b0;
  106. 106   else if(next_rcvd_ctkn_pos)
  107. 107     iamrdy <=#1 1'b1;
  108. 108 end
  109. 109
  110. 110 always @ (posedge clk) begin
  111. 111   rawdata_vld_d0 <=#1 rawdata_vld;
  112. 112   rawdata_vld_pos <=#1 rawdata_vld & !rawdata_vld_d0;
  113. 113 end
  114. 114
  115. 115 //////////////////////////////////////////////////////////////////////////////////////
  116. 116 // 1. FIFO flow through first when all channels are found valid(phase aligned)
  117. 117 // 2. When the speical marker on my channel is found, the fifo read is hold
  118. 118 // 3. Until the same markers are found across all three channels, the fifo read resumes
  119. 119 ///////////////////////////////////////////////////////////////////////////////////////
  120. 120
  121. 121 always @ (posedge clk) begin
  122. 122   if(rawdata_vld_pos || (other_ch0_rdy & other_ch1_rdy & iamrdy))
  123. 123     ra_en <=#1 1'b1;
  124. 124   else if(next_rcvd_ctkn_pos && !(other_ch0_rdy & other_ch1_rdy & iamrdy))
  125. 125     ra_en <=#1 1'b0;
  126. 126 end
  127. 127
  128. 128 /////////////////////////////////////////
  129. 129 //FIFO Read Address Counter
  130. 130 /////////////////////////////////////////
  131. 131 always @ (posedge clk) begin
  132. 132   if(!rawdata_vld)
  133. 133     ra <=#1 4'h0;
  134. 134   else if(ra_en)
  135. 135     ra <=#1 ra + 1'b1;
  136. 136 end
  137. 137
  138. 138 endmodule
复制代码

代码第40行信号rawdata_vld表示RGB三组数据已经全部校准完成。
代码第41行信号next_rcvd_ctkn_pos表示本组数据已经同步完成好了。
代码第47行至56行,是对ram的写使能和写地址进行操作。
代码第58行至66行,是对ram模块的例化。
代码第68行至70行,是对ram输出的数据进行打拍处理,以减小信号的扇出。
代码第82行至87行,当数据为控制字符时,拉高使能并同时获取使能的上升沿。
代码第93行至98行,当校准完成后,检测到控制字符的上升沿就拉高信号skip_line。
代码第103行至108行,当校准完成后,下一次的控制字符的上升沿就拉高信号iamrdy,表明本组数据已经同步完成。
代码第110行至113行,对信号rawdata_vld进行打拍并同时获取信号rawdata_vld的上升沿。
代码第121行至126行,当校准完成使能的上升沿到来或者3组数据全部同步完成则拉高ram的读使能,当3组数据没有全部同步完成则在控制字符的上升沿拉低ram的读使能。
代码第131行至136行,当校准未完成时,读地址一直为0,否则当读使能为高时,读地址依次累加。
8b/10b解码模块的代码如下:
  1. 1  module decoder(
  2. 2   input           pixelclk,
  3. 3   input   [9:0]   pdatainbnd,
  4. 4   input   [1:0]   potherchrdy,
  5. 5   input           pmerdy_int,
  6. 6   
  7. 7   output          pc0,   
  8. 8   output          pc1,   
  9. 9   output          pvde,   
  10. 10  output  [7:0]   pdatain
  11. 11     );
  12. 12
  13. 13 //parameter define   
  14. 14 parameter CTRLTOKEN0 = 10'b1101010100;
  15. 15 parameter CTRLTOKEN1 = 10'b0010101011;
  16. 16 parameter CTRLTOKEN2 = 10'b0101010100;
  17. 17 parameter CTRLTOKEN3 = 10'b1010101011;
  18. 18
  19. 19 //wire define
  20. 20 wire [7:0]   pdatain8b;
  21. 21
  22. 22 //reg define
  23. 23 reg  [7:0]   pdatain =0;
  24. 24 reg          pc0 =0;
  25. 25 reg          pc1 =0;
  26. 26 reg          pvde=0;
  27. 27
  28. 28 //*****************************************************
  29. 29 //**                    main code
  30. 30 //*****************************************************
  31. 31
  32. 32 assign pdatain8b = (pdatainbnd[9]) ? ~pdatainbnd[7:0] : pdatainbnd[7:0];               
  33. 33                                                                                          
  34. 34 always @ (posedge pixelclk) begin                                                      
  35. 35     if(pmerdy_int && (potherchrdy == 2'b11)) begin                                       
  36. 36         case (pdatainbnd)                                                                  
  37. 37             CTRLTOKEN0: begin                                                                 
  38. 38                 pc0 <=#1 1'b0;                                                                  
  39. 39                 pc1 <=#1 1'b0;                                                                  
  40. 40                 pvde <=#1 1'b0;                                                                 
  41. 41             end                                                                              
  42. 42                                                                                               
  43. 43             CTRLTOKEN1: begin                                                                 
  44. 44                 pc0 <=#1 1'b1;                                                                  
  45. 45                 pc1 <=#1 1'b0;                                                                  
  46. 46                 pvde <=#1 1'b0;                                                                 
  47. 47             end                                                                              
  48. 48                                                                                               
  49. 49             CTRLTOKEN2: begin                                                                 
  50. 50                 pc0 <=#1 1'b0;                                                                  
  51. 51                 pc1 <=#1 1'b1;                                                                  
  52. 52                 pvde <=#1 1'b0;                                                                 
  53. 53             end                                                                              
  54. 54                                                                                               
  55. 55             CTRLTOKEN3: begin                                                                 
  56. 56                 pc0 <=#1 1'b1;                                                                  
  57. 57                 pc1 <=#1 1'b1;                                                                  
  58. 58                 pvde <=#1 1'b0;                                                                 
  59. 59             end                                                                              
  60. 60                                                                                               
  61. 61             default: begin                                                                    
  62. 62                 pdatain[0] <=#1 pdatain8b[0];                                                                              
  63. 63                 pdatain[1] <=#1 (pdatainbnd[8]) ? (pdatain8b[1] ^ pdatain8b[0])
  64. 64                                               : (pdatain8b[1] ~^ pdatain8b[0]);           
  65. 65                 pdatain[2] <=#1 (pdatainbnd[8]) ? (pdatain8b[2] ^ pdatain8b[1])
  66. 66                                               : (pdatain8b[2] ~^ pdatain8b[1]);           
  67. 67                 pdatain[3] <=#1 (pdatainbnd[8]) ? (pdatain8b[3] ^ pdatain8b[2])
  68. 68                                                 : (pdatain8b[3] ~^ pdatain8b[2]);
  69. 69                 pdatain[4] <=#1 (pdatainbnd[8]) ? (pdatain8b[4] ^ pdatain8b[3])
  70. 70                                               : (pdatain8b[4] ~^ pdatain8b[3]);           
  71. 71                 pdatain[5] <=#1 (pdatainbnd[8]) ? (pdatain8b[5] ^ pdatain8b[4])
  72. 72                                                 : (pdatain8b[5] ~^ pdatain8b[4]);           
  73. 73                 pdatain[6] <=#1 (pdatainbnd[8]) ? (pdatain8b[6] ^ pdatain8b[5])
  74. 74                                                 : (pdatain8b[6] ~^ pdatain8b[5]);           
  75. 75                 pdatain[7] <=#1 (pdatainbnd[8]) ? (pdatain8b[7] ^ pdatain8b[6])
  76. 76                                                 : (pdatain8b[7] ~^ pdatain8b[6]);           
  77. 77                                                                                             
  78. 78                 pvde <=#1 1'b1;                                                               
  79. 79             end                                                                              
  80. 80         endcase                                                                                                                                                                                      
  81. 81     end
  82. 82     else begin                                                                       
  83. 83         pc0 <= 1'b0;                                                                       
  84. 84         pc1 <= 1'b0;                                                                       
  85. 85         pvde <= 1'b0;
  86. 86         pdatain <= 8'h0;
  87. 87     end
  88. 88 end  
  89. 89
  90. 90 endmodule
复制代码

TMDS解码的原理流程图如下图所示:
4265021.png

图 42.4.14 解码流程图

当3组颜色数据同步完成后,将输入的10bit数据先与4个控制字符做比较。如果与控制信号相同则产生pc0和pc1这2个控制信号,否则就进行8b/10b的解码过程,具体的解码过程如上图所示。
42.5下载验证
编译完工程之后我们就可以开始下载程序了。将HDMI电缆一端连接到开发板的HDMI_A插座一端连接到电脑主机,并将HDMI电缆一端连接到开发板上的HDMI_B插座另一端连接到显示器。将下载器一端连电脑,另一端与开发板上的JTAG端口连接,连接电源线并打开电源开关。
4265314.png

图 42.5.1 达芬奇开发板硬件连接

接下来我们下载程序,验证HDMI输入输出环回功能。下载完成后观察HDMI显示器显示的图案如下图所示,说明HDMI输入输出环回程序下载验证成功。
4265451.png

图 42.5.2 HDMI实时显示图像



阿莫论坛20周年了!感谢大家的支持与爱护!!

如果想吃一顿饺子,就得从冰箱里取出肉,剁馅儿,倒面粉、揉面、醒面,擀成皮儿,下锅……
一整个繁琐流程,就是为了出锅时那一嘴滚烫流油的热饺子。

如果这个过程,禁不住饿,零食下肚了,饺子出锅时也就不香了……《非诚勿扰3》
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-3-29 00:03

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表