先来看看VIDEO数据流到AXIS数据流的转换。
aclk是接口时钟,这个时钟和FV接口的时钟clk_video是同一个。
aresetn是接口复位,用来对模块内的寄存器复位。
首先需要延迟寄存,
always@(posedge aclk) begin
fval_r0 <= cl_fval;
fval_r1 <= fval_r0;
end
always@(posedge aclk) begin
lval_r0 <= cl_lval;
lval_r1 <= lval_r0;
lval_r2 <= lval_r1;
end
always@(posedge aclk) begin
cl_data_r0 <= cl_data;
cl_data_r1 <= cl_data_r0;
end
然后产生行计数。
reg [15:0] lval_cnt;
always@(posedge aclk) begin
if(~aresetn)
lval_cnt <= 'h0;
else if(fval_r1&~fval_r0) //帧信号下降沿,清零计数器
lval_cnt <= 'h0;
else if(lval_r1&~lval_r0) //行信号下降沿,计数器加1
lval_cnt <= lval_cnt + 1'b1;
end
然后产生AXI 帧起始信号m_axis_video_tuser,持续一个周期
always@(posedge aclk) begin
if(~aresetn)
m_axis_video_tuser <= 'h0;
else if((~lval_r2&lval_r1) && lval_cnt=='h0)
m_axis_video_tuser <= 'h1;
else
m_axis_video_tuser <= 'h0;
end
然后产生行结束信号m_axis_video_tlast,持续一个周期
always@(posedge aclk) begin
if(lval_r1&~lval_r0)
m_axis_video_tlast <= 'h1;
else
m_axis_video_tlast <= 'h0;
end
然后产生数据有效信号和数据m_axis_video_tvalid和m_axis_video_tdata
always@(posedge aclk) begin
s_axis_video_tdata <= cl_data_r1;
end
always@(posedge aclk) begin
s_axis_video_tvalid <= lval_r1;
end
可以看到,AXIS接口数据,相比原始数据,延迟了2个周期。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
再来看看AXIS数据流到VIDEO数据流的转换。
aclk是接口时钟,这个时钟和FV接口的时钟clk_video是同一个。
aresetn是接口复位,用来对模块内的寄存器复位。
READY信号是M侧需要使用的,这里可以一直拉高
assign s_axis_video_tready = 1'b1;
首先,生成帧有效信号,
首先需要产生行计数,
always@(posedge aclk) begin
if(~aresetn)
lval_cnt <= 'h0;
else if(s_axis_video_tuser)
lval_cnt <= 'h0;
else if(s_axis_video_tlast)
lval_cnt <= lval_cnt + 'h1;
end
用一个状态机来生成帧有效。
always @(posedge aclk)
begin
if(~aresetn)begin
state_fv<=2'd0;
end
else begin
case(state_fv)
2'd0:begin
if(fval_high_req)begin
state_fv<=2'd1;
end
end
2'd1:begin
if(lval_cnt==IMG_Y_SIZE)begin
state_fv<=2'd2;
end
end
2'd2:begin
if(fv_cnt_delay==3'd7)begin
state_fv<=2'd3;
end
end
2'd3:begin
state_fv<=2'd0;
end
endcase
end
end
在状态0中,如果检测到fval_high_req,则跳转到状态1,状态0为起始态,
在状态1中,如果检测到lval_cnt值为设定的行数,则跳转到状态2,状态1是帧传输态,
在状态2中,如果检测到fv_cnt_delay为设定的值3’d7,则跳转到状态3,状态2是帧后处理态,
在状态3中,无条件跳转回状态0。所以状态3是结束态。
来看看fval_high_req,
always@(posedge aclk) begin
if(s_axis_video_tuser)
fval_high_req <= 1'b1;
else if(fval_high_ack)
fval_high_req <= 1'b0;
end
当检测到s_axis_video_tuser为高时,fval_high_req 被拉高,如果检测到fval_high_ack为高,则fval_high_req拉低。
来看看fval_high_ack。
always @(posedge aclk)
begin
if(~aresetn)begin
fval_high_ack <= 1'b0;
end
else begin
fval_high_ack <= 1'b0;
case(state_fv)
2'd0:begin
if(fval_high_req)begin
fval_high_ack <= 1'b1;
end
end
endcase
end
end
可以看出,当状态跳转到1时,fval_high_ack 被拉高,否则,一律拉低。
所以,fval_high_ack只维持一周期的高电平。
fval_high_req和fval_high_ack形成互控握手。fval_high_req维持两个周期的高电平。
来看看fv的有效情况,
always @(posedge aclk)
begin
if(~aresetn)begin
cl_fval <= 1'h0;
end
else begin
case(state_fv)
2'd0:begin
if(fval_high_req)begin
cl_fval <= 1'h1;
end
end
2'd2:begin
if(fv_cnt_delay==3'd7)begin
cl_fval <= 1'h0;
end
end
endcase
end
end
在跳转到状态1的同时,cl_fval就被拉高了。
状态1是一帧的传输状态,所以整个状态1中,cl_fval都不会拉低。
状态2是一帧传输完后的收尾处理,这里是延时等待状态。脱离状态2的条件是fv_cnt_delay。
来看看fv_cnt_delay。
always @(posedge aclk)
begin
if(~aresetn)begin
fv_cnt_delay<=3'd0;
end
else begin
case(state_fv)
2'd1:begin
if(lval_cnt==IMG_Y_SIZE)begin
fv_cnt_delay<=3'd1;
end
end
2'd2:begin
if(fv_cnt_delay==3'd7)begin
fv_cnt_delay<=3'd0;
end
else begin
fv_cnt_delay<=fv_cnt_delay+3'd1;
end
end
endcase
end
end
在跳转到状态2时,fv_cnt_delay被设置为计数起始值1,之后,在状态2中持续计数,
达到设定值7时,跳转到状态3,且计数器检测到同样的条件,复位。
再生成行有效信号。
always@(posedge aclk) begin
cl_lval_reg<=s_axis_video_tvalid;
cl_lval_reg2<=cl_lval_reg;
cl_lval_reg3<=cl_lval_reg2;
cl_lval_reg4<=cl_lval_reg3;
cl_lval <= cl_lval_reg4;
end
再生成数据信号。
always@(posedge aclk) begin
cl_data_reg<=m_axis_video_tdata;
cl_data_reg2<=cl_data_reg;
cl_data_reg3<=cl_data_reg2;
cl_data_reg4<=cl_data_reg3;
cl_data <= cl_data_reg4;
end