BT.656 NTSC制式彩条生成模块(verilog)
1.知识储备
隔行扫描是将一副图像分成两场扫描,第一场扫描第1,2,5,7...等奇数行,第二场扫描2,4,6,8...等偶数行,并把扫奇数行的场称为奇数场,扫偶数行的场称为偶数场,一幅图像经过两次扫描即可。由左到右的扫描称为行扫描,由上到下的扫描称为场扫描。
对于标准的8bit BT656(YUV4:2:2)标清制式来说,一帧图像的格式定义如下:
一行是由4个部分组成:行 = 结束码(EAV) + 水平消隐(Horizontal Vertical Blanking) + 起始码(SAV) + 有效数据(Active Video)
注意:实际的工程中彩条每一行像素点的排列并不是这样的,而是(SAV Code ->Active video->EAV Code->Blanking video)
2.代码分析
TPG端口说明:
reset_in:由SDI播出工程内部信号提供。
pclk_in:与播出时钟tx_usrclk对接。
data_out:输出图像数据。
tx_sd_ce:门控时钟(SD-SDI,5/6/5/6)
并且初识化像素位置pixels=1,行位置lines=1,即图像扫描的初始位置。
彩条中各种色彩值设定(忽略十进制的数字,部分有误),同时定义了用来存储当前颜色的寄存器cur_color
定义常数,总共有多少行,多少列(1716 ,525)
彩条颜色分配函数,通过像素点的位置来分配当前位置的颜色。
有效像素点的排列如下图所示:
pixels初始值为1,则
01:Cb
10:Y
11:Cr
00:Y
01:Cb
通过pixels[0],pixels[1:0]来分割cur_color,得到colorValue.
以下always块里面是行扫描和场扫描控制,当一行扫描完成后,pixels回到初始位置,line加1.
根据行来控制F,V的值:
每一行的像素排列,一定要注意顺序SAV(FF,00,00,XY),接下来的是Active video,然后是EAV(FF,00,00,XY),最后是Blanking video,用80,10填充。
视频数据的输出:
颜色数据是8位的,SDI工程接收的是10bit的数据,如果要对接,就需要进行低位填充,当数据是ff时,低2位填充11,当数据不是ff时,低2位填充00.
module tpg( reset_in, pclk_in, pclk_out, data_out, pixels_out, tx_sd_ce ); input reset_in; input pclk_in; input tx_sd_ce; output pclk_out; output [9:0]data_out; output [10:0]pixels_out; reg [10:0] pixels = 1; reg [9:0] lines = 1; reg [7:0] data; reg F, V; wire [10:0] pixels_w; `define V_TOTAL_SIZE 525 `define V_ACTIVE_SIZE 239 //* 240 line `define V_BLANK_SIZE 22 //* 22 line `define H_TOTAL_SIZE 1716 `define H_ACTIVE_SIZE 1444 //* 1444 pixel (4(SAV) + 720*2(Y+Cr/Cb)) `define H_BLANK_SIZE 272 //* 272 pixel (4(EAV) + 268) `define H_COUNT_ADD_ONE 11'b1 `define V_COUNT_ADD_ONE 10'b1 //* Y, Cb, Cr values for test pattern colours //* 75% amplitude, 100% saturated //* Values from "Video De-mystified" book, chapter 3 reg [23:0] white = {8'heb, 8'h80, 8'h80};//{8'd235, 8'd128, 8'd128}; reg [23:0] black = {8'h10, 8'h80, 8'h80};//{8'd16, 8'd128, 8'd128}; reg [23:0] blue = {8'h29, 8'hf0, 8'h6e};//{8'd35, 8'd212, 8'd114}; reg [23:0] green = {8'h91, 8'h36, 8'h22};//{8'd112, 8'd72, 8'd58}; reg [23:0] red = {8'h51, 8'h5a, 8'hf0};//{8'd65, 8'd100, 8'd212}; reg [23:0] cyan = {8'haa, 8'ha6, 8'h10};//{8'd131, 8'd156, 8'd44}; reg [23:0] yellow = {8'hd2, 8'h10, 8'h92};//{8'd162, 8'd44, 8'd142}; reg [23:0] magenta = {8'h6a, 8'hca, 8'hde};//{8'd84, 8'd184, 8'd198}; reg [23:0] cur_color; //产生图像数据,数据8个8个出 function [7:0] colorValue; input [10:0] pixels; begin if(452 > pixels) cur_color = white; else if(632 > pixels) cur_color = black; else if(812 > pixels) cur_color = blue; else if(992 > pixels) cur_color = green; else if(1172 > pixels) cur_color = red; else if(1352 > pixels) cur_color = cyan; else if(1532 > pixels) cur_color = yellow; else cur_color = magenta; //* Y if(pixels[0] == 1'h0) colorValue = cur_color[23:16]; //* Cr and Cb else if(pixels[1:0] == 2'h1) colorValue = cur_color[15:8]; //* Cb else colorValue = cur_color[7:0]; //* Cr end endfunction //复位,pixels和lines都归1,像素值逐渐增加,当一行的像素填充完后 //继续扫描下一行,即lines加1 always @ (posedge pclk_in) if(reset_in) begin pixels <= 1; lines <= 1; end else if(tx_sd_ce) begin if(pixels < `H_TOTAL_SIZE) pixels <= pixels + `H_COUNT_ADD_ONE; else begin pixels <= 1; if(lines < `V_TOTAL_SIZE) lines <= lines + `V_COUNT_ADD_ONE; else lines <= 1; end end //通过bt656标准,行结构的分布来确定F,V always @ (posedge pclk_in) if(reset_in) data <= 10'h00; else if(tx_sd_ce) begin //* even field is 1 if(lines<4 || lines>265) F <= 1; else F <= 0; //* vertical blank is 1 if(lines<20 || (lines>263 && lines<283)) V <= 1; else V <= 0; /* if(pixels <=1440) begin if( V== 1) data <= (pixels[0] == 1) ? 8'h80 : 8'h10; else data <= colorValue(pixels); end else if(pixels == 1441) data <= 8'hFF; else if(pixels == 1442) data <= 8'h00; else if(pixels == 1443) data <= 8'h00; else if(pixels == 1444) begin //* EAV is 1 //H <= 1; //data <= { 1'b1, F, V, H, (V ^ H), (F ^ H), (F ^V ), (F ^ V ^ H) }; data <= { 1'b1, F, V, 1'b1, (V ^ 1'b1), (F ^ 1'b1), (F ^ V ), (F ^ V ^ 1'b1) }; end else if(pixels <= 1712) data <= (pixels[0] == 1) ? 8'h80 : 8'h10; else if(pixels == 1713) data <= 8'hFF; else if(pixels == 1714) data <= 8'h00; else if(pixels == 1715) data <= 8'h00; else if(pixels == 1716) begin //* SAV is 0 //H <= 0; //data <= { 1'b1, F, V, H, (V ^ H), (F ^ H), (F ^V ), (F ^ V ^ H) }; data <= { 1'b1, F, V, 1'b0, (V ^ 1'b1), (F ^ 1'b1), (F ^ V ), (F ^ V ^ 1'b1) }; end end */ if(pixels == 1) data <= 8'hFF; else if(pixels == 2) data <= 8'h00; else if(pixels == 3) data <= 8'h00; else if(pixels == 4) begin //* EAV is 1 //换成sav //H <= 1; //data <= { 1'b1, F, V, H, (V ^ H), (F ^ H), (F ^V ), (F ^ V ^ H) }; data <= { 1'b1, F, V, 1'b0, (V ^ 1'b1), (F ^ 1'b1), (F ^ V ), (F ^ V ^ 1'b1) }; end else if(pixels <= 1444) //* horizontal blanking pixel begin if( V== 1) data <= (pixels[0] == 1) ? 8'h80 : 8'h10; else data <= colorValue(pixels); end else if(pixels == 1445) data <= 8'hFF; else if(pixels == 1446) data <= 8'h00; else if(pixels == 1447) data <= 8'h00; else if(pixels == 1448) begin //* SAV is 0 //换成eav //H <= 0; //data <= { 1'b1, F, V, H, (V ^ H), (F ^ H), (F ^V ), (F ^ V ^ H) }; data <= { 1'b1, F, V, 1'b1, (V ^ 1'b0), (F ^ 1'b0), (F ^ V ), (F ^ V ^ 1'b0) }; end else //* horizontal active pixel //* if vertical blank, fill with 0x80, 0x10 data <= (pixels[0] == 1) ? 8'h80 : 8'h10; end assign pclk_out = pclk_in; assign data_out = (data == 8'hff) ? {data, 2'b11} : {data, 2'b00}; assign pixels_out = pixels; assign pixels_w = pixels; endmodule