FPGA影象處理系列-video訊號轉Avalon-ST模組
阿新 • • 發佈:2019-01-07
年前到手一塊Altera的SoCKit開發板,跑了跑友晶的VIP例程。也是為了練手,自己寫了個video input format detection模組(此模組官方有提供IP)。功能為:將輸入的包含RGB data, hsync,vsync,data_valid訊號的video視訊流轉換成Avalon-ST匯流排。
核心就是一個非同步fifo和fifo的控制器,注意avalon-ST的時鐘一定要大於video時鐘。寫入端的寫使能為data_valid,然後讀出端構建一個狀態機,產生Avalon-ST的包。Avalon-ST匯流排和X家的AXI4-stream類似,都是適合視訊流傳輸的匯流排。可參考官方的Avalon specification。關於Avalon-ST的視訊包格式,可以參考Altera的Video and Image Processing Suite User Guide文件的第二章Interface,一個包包含兩部分,控制包和資料包,控制包含影象的大小,以24bit寬度的匯流排為例,控制包以0x00000f作為起始標誌;資料包就是影象的資料了,以0x000000作為起始標誌。fifo讀取端,通過檢測fifo的空狀態,在狀態STATE_DATA_PKT_DAT期間來產生fifo的讀使能訊號。直接上原始碼:
module convertor ( clk_video, reset_n_video, video_data, video_hsync, video_vsync, video_valid, clk_st, reset_n_st, // streaming source source_data, source_valid, source_ready, source_sop, source_eop ); input clk_video; input reset_n_video; input [23:0] video_data; input video_hsync; input video_vsync; input video_valid; input clk_st; input reset_n_st; // streaming source output [23:0] source_data; output source_valid; input source_ready; output source_sop; output source_eop; parameter WIDTH = 640; parameter HEIGHT = 480; localparam CTRL_PKT_NUM = 3; localparam CTRL_PKT_HEADER = 24'd15; localparam DATA_PKT_HEADER = 24'd0; wire [10 :0] rdusedw_sig; wire [23 :0] q_sig; wire rdreq_sig; wire [15:0] w_LOG_WIDTH = WIDTH; wire [15:0] w_LOG_HEIGHT = HEIGHT; /////////////////////// reg [2:0] dout_data_type; reg [2:0] ctrl_data_type; reg [11:0] dot_cnt; reg [11:0] line_cnt; wire rdempty_sig; assign source_data = (dout_data_type[2:0] == 3'd0) ? q_sig: (dout_data_type[2:0] == 3'd1) ? CTRL_PKT_HEADER: (dout_data_type[2:0] == 3'd2) ? { 4'b0, w_LOG_WIDTH[ 7: 4], 4'b0, w_LOG_WIDTH[11: 8], 4'b0, w_LOG_WIDTH[15:12] }: (dout_data_type[2:0] == 3'd3) ? { 4'b0, w_LOG_HEIGHT[11: 8], 4'b0, w_LOG_HEIGHT[15:12], 4'b0, w_LOG_WIDTH[ 3: 0] }: (dout_data_type[2:0] == 3'd4) ? { 4'b0, 4'b0, 4'b0, w_LOG_HEIGHT[ 3: 0], 4'b0, w_LOG_HEIGHT[ 7: 4] }: (dout_data_type[2:0] == 3'd5) ? DATA_PKT_HEADER: q_sig; assign source_valid = source_ready & (dout_data_type[2:0] >= 3'd1 && dout_data_type[2:0] <= 3'd5 ) | source_ready & rdreq_sig; assign rdreq_sig = source_ready & (dout_data_type[2:0] == 3'd0) & !rdempty_sig; /////////////////////// // State Machine //reg out; reg [1:0] pkt_state; localparam STATE_CTRL_PKT_SOP = 0; localparam STATE_CTRL_PKT_DAT = 1; localparam STATE_DATA_PKT_SOP = 2; localparam STATE_DATA_PKT_DAT = 3; wire ctrl_pkt_sop = (pkt_state == STATE_CTRL_PKT_SOP ) ? 1 : 0 ; wire ctrl_pkt_eop = ((pkt_state == STATE_CTRL_PKT_DAT) & (dot_cnt==(CTRL_PKT_NUM-1)) ) ? 1 : 0 ; wire data_pkt_sop = (pkt_state == STATE_DATA_PKT_SOP ) ? 1 : 0 ; wire data_pkt_eop = ((pkt_state == STATE_DATA_PKT_DAT) & (dot_cnt==(WIDTH-1)) & (line_cnt==(HEIGHT-1)) ) ? 1 : 0 ; always @ (posedge clk_st) begin if (!reset_n_st) pkt_state <= STATE_CTRL_PKT_SOP; else case (pkt_state) // state transitions STATE_CTRL_PKT_SOP: if (source_ready) pkt_state <= STATE_CTRL_PKT_DAT; STATE_CTRL_PKT_DAT: if (source_ready & ctrl_pkt_eop) pkt_state <= STATE_DATA_PKT_SOP; STATE_DATA_PKT_SOP: if (source_ready) pkt_state <= STATE_DATA_PKT_DAT; STATE_DATA_PKT_DAT: if (source_ready & data_pkt_eop) pkt_state <= STATE_CTRL_PKT_SOP; default : pkt_state = STATE_CTRL_PKT_DAT; endcase end // sop and eop signals assign source_sop = (ctrl_pkt_sop | data_pkt_sop) ; assign source_eop = (ctrl_pkt_eop | data_pkt_eop) ; ///////////////////////// // dot and line counter always @(posedge clk_st) begin if (!reset_n_st) begin dot_cnt <= 0; end else begin if (source_ready) if ((pkt_state == STATE_DATA_PKT_DAT) ) begin if(rdreq_sig) if ( dot_cnt < (WIDTH-1) ) dot_cnt <= dot_cnt + 11'd1; else dot_cnt <= 0; end else if ((pkt_state == STATE_CTRL_PKT_DAT) )begin // control packet if ( dot_cnt < (CTRL_PKT_NUM-1) ) dot_cnt <= dot_cnt + 11'd1; else dot_cnt <= 0; end end end always @(posedge clk_st) begin if (!reset_n_st) begin line_cnt <= 0; end else begin if (source_ready)begin if (pkt_state == STATE_DATA_PKT_DAT) begin if(rdreq_sig) if ( dot_cnt == (WIDTH-1) ) begin if ( line_cnt < (HEIGHT-1) ) line_cnt <= line_cnt + 11'd1; else line_cnt <= 0; end end else line_cnt <= 0; end end end /////////////////////// // Making Final Output Data always @(pkt_state or ctrl_data_type ) begin case (pkt_state) STATE_CTRL_PKT_SOP: dout_data_type = {3'd1};//CTRL_PKT_HEADER; STATE_CTRL_PKT_DAT: dout_data_type = {ctrl_data_type};//ctrl_data_type; STATE_DATA_PKT_SOP: dout_data_type = {3'd5};//DATA_PKT_HEADER; default: dout_data_type = {3'd0};//image_data; endcase end always @(dot_cnt[3:0]) begin case (dot_cnt[3:0]) 0 : ctrl_data_type = 3'd2;//{ 4'b0, w_LOG_WIDTH[ 7: 4], 4'b0, w_LOG_WIDTH[11: 8], 4'b0, w_LOG_WIDTH[15:12] }; 1 : ctrl_data_type = 3'd3;//{ 4'b0, w_LOG_HEIGHT[11: 8], 4'b0, w_LOG_HEIGHT[15:12], 4'b0, w_LOG_WIDTH[ 3: 0] }; 2 : ctrl_data_type = 3'd4;//{ 4'b0, 4'b0, 4'b0, w_LOG_HEIGHT[ 3: 0], 4'b0, w_LOG_HEIGHT[ 7: 4] }; default : ctrl_data_type = 3'd0;//24'bx; endcase end //////// output data path -- fifo ////////// //////////////////////////////////////////////////// reg[1:0] video_vsync_r; always @(posedge clk_video ) if(!reset_n_video) video_vsync_r <= 2'd0; else video_vsync_r <= {video_vsync_r[0],video_vsync}; wire fifo_clr = ~video_vsync_r[1] & video_vsync_r[0]; // pixel_fifo pixel_fifo_inst ( .aclr (fifo_clr), .data ( video_data ), .rdclk ( clk_st ), .rdreq ( rdreq_sig ), .wrclk ( clk_video ), .wrreq ( video_valid ), .q ( q_sig ), .rdempty ( rdempty_sig ), .rdfull ( rdfull_sig ), .rdusedw ( rdusedw_sig ), .wrempty ( wrempty_sig ), .wrfull ( wrfull_sig ), .wrusedw ( wrusedw_sig ) ); endmodule