摘要
最近在做cmos工业相机相关的工作。对于某些些cmos芯片,如线阵相机,同时会输出多个通道的像素数据。然而我们在实际使用过程中,这些数据往往使用串行的方式写进cpu内存,再进行下一步处理。这样以来,需要对cmos输出的像素信号进行缓存处理,根据后端模块的需要按顺序读出。
据此,编写了一个cmos像素有效信号的多通道缓存处理合并输出模块,并将此封装成IP备用。
本文档的格式按照工作中的IP说明编写,完整代码在文末给出。
?????????????????????????????
前言:通常的cmos芯片输出信号的格式
如上图,在帧有效并且行有效的时候代表着有效数据的输出。通过帧信号可以得知一帧何时开始,通过行信号可以得知一帧开始后何时输出数据有效。对这些信号加以参考和处理,可以正确的接收到像素数据。
1.功能概述
?? ?用于cmos传感器视频数据采集,将多通道的cmos像素有效信号缓存并合并输出
?? ?可进行1~16通道的整合输出
?? ?基于vivado 18.3软件设计
2.使用说明
?
图1.?IP 核端口示意图
表1.DVP_AXI stream IP 核端口列表
端口 ???????? / ? ?? ? 参数 | 类型 | 说明 | sys_clk | input | 输入时钟,可达100MHz | rst | input | 复位信号,高电平有效 | en | input | 使能信号输入,高电平有效 | cmos_signal | input | 单通道数据有效信号输入 | Out_channel | input | 多通道数据有效信号合并输出 | ?????????????????????????? Channels | / | 需要输出的通道数(1-16) | ?????????????????????????? Delay Line | / | 输出信号每行之间的延时参数(系统时钟为单位) | ?????????????????????????? Num Lines | / | cmos_signal信号每帧有效数据的行数 |
说明:
图2. 输入信号示意图
- 对输入信号cmos_signal的要求。
每帧的行数应该一致;
每行的行时间可以不一致,但要求差值不应过大,差值应小于100个时钟周期;
信号的“行间距”不能过大,应不大于“行时间”;
信号的“帧间距”应该足够大,应不小于(“行时间”X Channel+Delay Line)X?Num Lines。
- 对输入信号en的要求。
en信号应该在cmos_signal有效之前的开始拉高或者是在帧间隔时间段内拉高,不应在cmos_signal有效期间拉高,这样会使当前帧的多通道输出不完整。
- 对参数的要求。
Channels参数的范围在1到16之间,也就是此模块最大可输出16通道的信号,最小可输出1通道的信号。
Delay Line?参数的设置值应不小于相邻行的行时间差值。
Num Lines?参数设置值应该与实际输入cmos_signal信号的每帧的行数值一致,也就是在使用此模块前应事先确定输入信号的行数值。
3.功能仿真
编写测试模块代码,具体为在测试模块中例化IP模块,并生成相应形式的PWM波形作为IP核的信号输入。PWM波参考代码如下:
always@(posedge clk )
begin
if(rst)
r_pwm_signal <= 1'b0;
else begin
case(cnt1)
0:r_pwm_signal <= 1'b1;
512:r_pwm_signal <= 1'b0;
1024:r_pwm_signal <= 1'b1;
1524:r_pwm_signal <= 1'b0;
2024:r_pwm_signal <= 1'b1;
2624:r_pwm_signal <= 1'b0;
3224:r_pwm_signal <= 1'b1;
3736:r_pwm_signal <= 1'b0;
4248:r_pwm_signal <= 1'b1;
4760:r_pwm_signal <= 1'b0;
5272:r_pwm_signal <= 1'b1;
5772:r_pwm_signal <= 1'b0;
6272:r_pwm_signal <= 1'b1;
6872:r_pwm_signal <= 1'b0;
7472:r_pwm_signal <= 1'b1;
7984:r_pwm_signal <= 1'b0;
default:r_pwm_signal <= r_pwm_signal;
endcase
end
end
编写testbench进行仿真,参数设置Channel=16、Delay Line=100、Num Lines=8,仿真波形如下图。
?
图3. 功能仿真波形图
?
图4. 功能仿真波形图
可以看到,使能信号在帧间隔阶段拉高,在这之后多通道输出才开始进行。输出信号为16通道8行输出,Out_channel信号即为多通道信号的合并输出。输出信号的每行的行时间与输入信号一致,输信号的行间隔时间与Delay Line设置相符。
附,主要代码:
`timescale 1ns / 1ps
//
//
//
//
module multichannel_out(
input sys_clk,
input rst,
input en,
input cmos_signal,
output wire Out_channel
);
//参数定义
parameter NUM_lines=8; //每次(帧)的行数
parameter CHANNELS=16; //输出的通道数
parameter DELAY_line=50; //n个通道输出,每组之间的延时
parameter IDLE=2'b00; //等待状态
parameter WORK=2'b01; //空闲状态
//内部寄存器及连线定义
reg [10:0]counter=11'd0; //输入信号的高脉冲计数器
reg [10:0]r_counter=11'd0; //输入信号的高脉冲计数器打一拍
reg r_cmos_signal0=1'b0;
reg r_cmos_signal=1'b0; //输入信号寄存器
reg f_vaild_cmos_signal=1'b0; //输入信号下降沿标志,在每个下降沿时将counter存入fifo
wire [10:0]R_cnt; //fifo读出当前的像素行宽计数值
wire [10:0]R_cnt1;
reg [10:0]r_R_cnt=11'd0;
reg [1:0]state; //状态寄存器
reg [15:0]cnt=1; //代表着每一个多通道输出任务单元持续的时间,初始值改为1
reg [15:0]cnt1=1;
reg [7:0]cnt_line_vaild=8'd0; //一帧中每一行完成计数一次
reg fifo_rd_en; //fifo读使能
reg r_pluse_out1;
reg r_pluse_out2;
reg r_pluse_out3;
reg r_pluse_out4;
reg r_pluse_out5;
reg r_pluse_out6;
reg r_pluse_out7;
reg r_pluse_out8;
reg r_pluse_out9;
reg r_pluse_out10;
reg r_pluse_out11;
reg r_pluse_out12;
reg r_pluse_out13;
reg r_pluse_out14;
reg r_pluse_out15;
reg r_pluse_out16;
wire out_channel1;
/*****************************************************************************************
********************************** Logic Beginning ************************************
******************************************************************************************/
/******首先对输入信号高脉冲进行计数并缓存******/
always@(posedge sys_clk )begin
if (en) begin
r_cmos_signal0<=cmos_signal;
r_cmos_signal<=r_cmos_signal0; end
else begin
r_cmos_signal0<=1'b0;
r_cmos_signal<=1'b0; end
end
always@(posedge sys_clk )begin
if(rst)
counter<=11'd0;
else if (r_cmos_signal0)
counter<=counter+1'd1;
else
counter<=11'd0;
end
always@(posedge sys_clk )begin
r_counter<=counter;
end
//获得r_cmos_signal0信号的下降沿标志,用来使能fifo写入counter的计数值
always@(posedge sys_clk )begin //提取输入信号的下降沿
if(rst)
f_vaild_cmos_signal<=1'd0;
else if ({r_cmos_signal0,r_cmos_signal}==2'b01)
f_vaild_cmos_signal<=1'd1;
else
f_vaild_cmos_signal<=1'd0;
end
//例化fifo,将 r_counter缓存,并适时读出
fifo_generator_0 u_fifo_generator_0 (
.clk(sys_clk), // input wire clk
.rst(rst), // input wire rst
.din(r_counter), // input wire [10 : 0] din
.wr_en(f_vaild_cmos_signal), // input wire wr_en
.rd_en(fifo_rd_en), // input wire rd_en
.dout(R_cnt), // output wire [10 : 0] dout
.full(), // output wire full
.empty() // output wire empty
);
assign R_cnt1=(R_cnt==0)?1:R_cnt;
/******************************************状态转移******************************************/
always@(posedge sys_clk )begin //同步复位,高电平复位
if(rst) begin
state<=IDLE;
fifo_rd_en<=1'b0;end
else begin
case(state)
IDLE:
begin
if(f_vaild_cmos_signal)begin // 输入信号下降沿到来时,转换到工作状态
fifo_rd_en<=1'b1;
state<=WORK;end
else begin
fifo_rd_en<=1'b0;
state<=IDLE;end
end
WORK:begin
begin
if ((cnt_line_vaild==NUM_lines)) //一帧中的所有行都输出完成时,转换到空闲状态
state<=IDLE;
else
state<=state;
end
begin
if(cnt==(r_R_cnt*CHANNELS+DELAY_line))
fifo_rd_en<=1'b1;
else
fifo_rd_en<=1'b0; end
end
default: state<=IDLE;
endcase end
end
/******************************************与状态相关的逻辑******************************************/
always @(posedge sys_clk)begin //WORK状态时,将r_R_cnt1寄存
if(state==WORK)
r_R_cnt<=R_cnt1;
else
r_R_cnt<=11'd0;
end
always @(posedge sys_clk)begin //WORK状态时,将信号分通道输出
if (rst)begin
r_pluse_out1<=1'b0;
r_pluse_out2<=1'b0;
r_pluse_out3<=1'b0;
r_pluse_out4<=1'b0;
r_pluse_out5<=1'b0;
r_pluse_out6<=1'b0;
r_pluse_out7<=1'b0;
r_pluse_out8<=1'b0;
r_pluse_out9<=1'b0;
r_pluse_out10<=1'b0;
r_pluse_out11<=1'b0;
r_pluse_out12<=1'b0;
r_pluse_out13<=1'b0;
r_pluse_out14<=1'b0;
r_pluse_out15<=1'b0;
r_pluse_out16<=1'b0;
end
else if (state==WORK)
begin
if((cnt1>0)&(cnt1<=r_R_cnt))
begin
r_pluse_out1<=1'b1;
r_pluse_out2<=1'b0;
r_pluse_out3<=1'b0;
r_pluse_out4<=1'b0;
r_pluse_out5<=1'b0;
r_pluse_out6<=1'b0;
r_pluse_out7<=1'b0;
r_pluse_out8<=1'b0;
r_pluse_out9<=1'b0;
r_pluse_out10<=1'b0;
r_pluse_out11<=1'b0;
r_pluse_out12<=1'b0;
r_pluse_out13<=1'b0;
r_pluse_out14<=1'b0;
r_pluse_out15<=1'b0;
r_pluse_out16<=1'b0;
end
else if ((cnt1>r_R_cnt)&(cnt1<=r_R_cnt*2))
begin
r_pluse_out1<=1'b0;
r_pluse_out2<=1'b1;
r_pluse_out3<=1'b0;
r_pluse_out4<=1'b0;
r_pluse_out5<=1'b0;
r_pluse_out6<=1'b0;
r_pluse_out7<=1'b0;
r_pluse_out8<=1'b0;
r_pluse_out9<=1'b0;
r_pluse_out10<=1'b0;
r_pluse_out11<=1'b0;
r_pluse_out12<=1'b0;
r_pluse_out13<=1'b0;
r_pluse_out14<=1'b0;
r_pluse_out15<=1'b0;
r_pluse_out16<=1'b0;
end
else if ((cnt1>r_R_cnt*2)&(cnt1<=r_R_cnt*3))
begin
r_pluse_out1<=1'b0;
r_pluse_out2<=1'b0;
r_pluse_out3<=1'b1;
r_pluse_out4<=1'b0;
r_pluse_out5<=1'b0;
r_pluse_out6<=1'b0;
r_pluse_out7<=1'b0;
r_pluse_out8<=1'b0;
r_pluse_out9<=1'b0;
r_pluse_out10<=1'b0;
r_pluse_out11<=1'b0;
r_pluse_out12<=1'b0;
r_pluse_out13<=1'b0;
r_pluse_out14<=1'b0;
r_pluse_out15<=1'b0;
r_pluse_out16<=1'b0;
end
else if ((cnt1>r_R_cnt*3)&(cnt1<=r_R_cnt*4))
begin
r_pluse_out1<=1'b0;
r_pluse_out2<=1'b0;
r_pluse_out3<=1'b0;
r_pluse_out4<=1'b1;
r_pluse_out5<=1'b0;
r_pluse_out6<=1'b0;
r_pluse_out7<=1'b0;
r_pluse_out8<=1'b0;
r_pluse_out9<=1'b0;
r_pluse_out10<=1'b0;
r_pluse_out11<=1'b0;
r_pluse_out12<=1'b0;
r_pluse_out13<=1'b0;
r_pluse_out14<=1'b0;
r_pluse_out15<=1'b0;
r_pluse_out16<=1'b0;
end
else if ((cnt1>r_R_cnt*4)&(cnt1<=r_R_cnt*5))
begin
r_pluse_out1<=1'b0;
r_pluse_out2<=1'b0;
r_pluse_out3<=1'b0;
r_pluse_out4<=1'b0;
r_pluse_out5<=1'b1;
r_pluse_out6<=1'b0;
r_pluse_out7<=1'b0;
r_pluse_out8<=1'b0;
r_pluse_out9<=1'b0;
r_pluse_out10<=1'b0;
r_pluse_out11<=1'b0;
r_pluse_out12<=1'b0;
r_pluse_out13<=1'b0;
r_pluse_out14<=1'b0;
r_pluse_out15<=1'b0;
r_pluse_out16<=1'b0;
end
else if ((cnt1>r_R_cnt*5)&(cnt1<=r_R_cnt*6))
begin
r_pluse_out1<=1'b0;
r_pluse_out2<=1'b0;
r_pluse_out3<=1'b0;
r_pluse_out4<=1'b0;
r_pluse_out5<=1'b0;
r_pluse_out6<=1'b1;
r_pluse_out7<=1'b0;
r_pluse_out8<=1'b0;
r_pluse_out9<=1'b0;
r_pluse_out10<=1'b0;
r_pluse_out11<=1'b0;
r_pluse_out12<=1'b0;
r_pluse_out13<=1'b0;
r_pluse_out14<=1'b0;
r_pluse_out15<=1'b0;
r_pluse_out16<=1'b0;
end
else if ((cnt1>r_R_cnt*6)&(cnt1<=r_R_cnt*7))
begin
r_pluse_out1<=1'b0;
r_pluse_out2<=1'b0;
r_pluse_out3<=1'b0;
r_pluse_out4<=1'b0;
r_pluse_out5<=1'b0;
r_pluse_out6<=1'b0;
r_pluse_out7<=1'b1;
r_pluse_out8<=1'b0;
r_pluse_out9<=1'b0;
r_pluse_out10<=1'b0;
r_pluse_out11<=1'b0;
r_pluse_out12<=1'b0;
r_pluse_out13<=1'b0;
r_pluse_out14<=1'b0;
r_pluse_out15<=1'b0;
r_pluse_out16<=1'b0;
end
else if ((cnt1>r_R_cnt*7)&(cnt1<=r_R_cnt*8))
begin
r_pluse_out1<=1'b0;
r_pluse_out2<=1'b0;
r_pluse_out3<=1'b0;
r_pluse_out4<=1'b0;
r_pluse_out5<=1'b0;
r_pluse_out6<=1'b0;
r_pluse_out7<=1'b0;
r_pluse_out8<=1'b1;
r_pluse_out9<=1'b0;
r_pluse_out10<=1'b0;
r_pluse_out11<=1'b0;
r_pluse_out12<=1'b0;
r_pluse_out13<=1'b0;
r_pluse_out14<=1'b0;
r_pluse_out15<=1'b0;
r_pluse_out16<=1'b0;
end
else if ((cnt1>r_R_cnt*8)&(cnt1<=r_R_cnt*9))
begin
r_pluse_out1<=1'b0;
r_pluse_out2<=1'b0;
r_pluse_out3<=1'b0;
r_pluse_out4<=1'b0;
r_pluse_out5<=1'b0;
r_pluse_out6<=1'b0;
r_pluse_out7<=1'b0;
r_pluse_out8<=1'b0;
r_pluse_out9<=1'b1;
r_pluse_out10<=1'b0;
r_pluse_out11<=1'b0;
r_pluse_out12<=1'b0;
r_pluse_out13<=1'b0;
r_pluse_out14<=1'b0;
r_pluse_out15<=1'b0;
r_pluse_out16<=1'b0;
end
else if ((cnt1>r_R_cnt*9)&(cnt1<=r_R_cnt*10))
begin
r_pluse_out1<=1'b0;
r_pluse_out2<=1'b0;
r_pluse_out3<=1'b0;
r_pluse_out4<=1'b0;
r_pluse_out5<=1'b0;
r_pluse_out6<=1'b0;
r_pluse_out7<=1'b0;
r_pluse_out8<=1'b0;
r_pluse_out9<=1'b0;
r_pluse_out10<=1'b1;
r_pluse_out11<=1'b0;
r_pluse_out12<=1'b0;
r_pluse_out13<=1'b0;
r_pluse_out14<=1'b0;
r_pluse_out15<=1'b0;
r_pluse_out16<=1'b0;
end
else if ((cnt1>r_R_cnt*10)&(cnt1<=r_R_cnt*11))
begin
r_pluse_out1<=1'b0;
r_pluse_out2<=1'b0;
r_pluse_out3<=1'b0;
r_pluse_out4<=1'b0;
r_pluse_out5<=1'b0;
r_pluse_out6<=1'b0;
r_pluse_out7<=1'b0;
r_pluse_out8<=1'b0;
r_pluse_out9<=1'b0;
r_pluse_out10<=1'b0;
r_pluse_out11<=1'b1;
r_pluse_out12<=1'b0;
r_pluse_out13<=1'b0;
r_pluse_out14<=1'b0;
r_pluse_out15<=1'b0;
r_pluse_out16<=1'b0;
end
else if ((cnt1>r_R_cnt*11)&(cnt1<=r_R_cnt*12))
begin
r_pluse_out1<=1'b0;
r_pluse_out2<=1'b0;
r_pluse_out3<=1'b0;
r_pluse_out4<=1'b0;
r_pluse_out5<=1'b0;
r_pluse_out6<=1'b0;
r_pluse_out7<=1'b0;
r_pluse_out8<=1'b0;
r_pluse_out9<=1'b0;
r_pluse_out10<=1'b0;
r_pluse_out11<=1'b0;
r_pluse_out12<=1'b1;
r_pluse_out13<=1'b0;
r_pluse_out14<=1'b0;
r_pluse_out15<=1'b0;
r_pluse_out16<=1'b0;
end
else if ((cnt1>r_R_cnt*12)&(cnt1<=r_R_cnt*13))
begin
r_pluse_out1<=1'b0;
r_pluse_out2<=1'b0;
r_pluse_out3<=1'b0;
r_pluse_out4<=1'b0;
r_pluse_out5<=1'b0;
r_pluse_out6<=1'b0;
r_pluse_out7<=1'b0;
r_pluse_out8<=1'b0;
r_pluse_out9<=1'b0;
r_pluse_out10<=1'b0;
r_pluse_out11<=1'b0;
r_pluse_out12<=1'b0;
r_pluse_out13<=1'b1;
r_pluse_out14<=1'b0;
r_pluse_out15<=1'b0;
r_pluse_out16<=1'b0;
end
else if ((cnt1>r_R_cnt*13)&(cnt1<=r_R_cnt*14))
begin
r_pluse_out1<=1'b0;
r_pluse_out2<=1'b0;
r_pluse_out3<=1'b0;
r_pluse_out4<=1'b0;
r_pluse_out5<=1'b0;
r_pluse_out6<=1'b0;
r_pluse_out7<=1'b0;
r_pluse_out8<=1'b0;
r_pluse_out9<=1'b0;
r_pluse_out10<=1'b0;
r_pluse_out11<=1'b0;
r_pluse_out12<=1'b0;
r_pluse_out13<=1'b0;
r_pluse_out14<=1'b1;
r_pluse_out15<=1'b0;
r_pluse_out16<=1'b0;
end
else if ((cnt1>r_R_cnt*14)&(cnt1<=r_R_cnt*15))
begin
r_pluse_out1<=1'b0;
r_pluse_out2<=1'b0;
r_pluse_out3<=1'b0;
r_pluse_out4<=1'b0;
r_pluse_out5<=1'b0;
r_pluse_out6<=1'b0;
r_pluse_out7<=1'b0;
r_pluse_out8<=1'b0;
r_pluse_out9<=1'b0;
r_pluse_out10<=1'b0;
r_pluse_out11<=1'b0;
r_pluse_out12<=1'b0;
r_pluse_out13<=1'b0;
r_pluse_out14<=1'b0;
r_pluse_out15<=1'b1;
r_pluse_out16<=1'b0;
end
else if ((cnt1>r_R_cnt*15)&(cnt1<=r_R_cnt*16))
begin
r_pluse_out1<=1'b0;
r_pluse_out2<=1'b0;
r_pluse_out3<=1'b0;
r_pluse_out4<=1'b0;
r_pluse_out5<=1'b0;
r_pluse_out6<=1'b0;
r_pluse_out7<=1'b0;
r_pluse_out8<=1'b0;
r_pluse_out9<=1'b0;
r_pluse_out10<=1'b0;
r_pluse_out11<=1'b0;
r_pluse_out12<=1'b0;
r_pluse_out13<=1'b0;
r_pluse_out14<=1'b0;
r_pluse_out15<=1'b0;
r_pluse_out16<=1'b1;
end
else
begin
r_pluse_out1<=1'b0;
r_pluse_out2<=1'b0;
r_pluse_out3<=1'b0;
r_pluse_out4<=1'b0;
r_pluse_out5<=1'b0;
r_pluse_out6<=1'b0;
r_pluse_out7<=1'b0;
r_pluse_out8<=1'b0;
r_pluse_out9<=1'b0;
r_pluse_out10<=1'b0;
r_pluse_out11<=1'b0;
r_pluse_out12<=1'b0;
r_pluse_out13<=1'b0;
r_pluse_out14<=1'b0;
r_pluse_out15<=1'b0;
r_pluse_out16<=1'b0;
end
end
else begin
r_pluse_out1<=1'b0;
r_pluse_out2<=1'b0;
r_pluse_out3<=1'b0;
r_pluse_out4<=1'b0;
r_pluse_out5<=1'b0;
r_pluse_out6<=1'b0;
r_pluse_out7<=1'b0;
r_pluse_out8<=1'b0;
r_pluse_out9<=1'b0;
r_pluse_out10<=1'b0;
r_pluse_out11<=1'b0;
r_pluse_out12<=1'b0;
r_pluse_out13<=1'b0;
r_pluse_out14<=1'b0;
r_pluse_out15<=1'b0;
r_pluse_out16<=1'b0;
end
end
always@(posedge sys_clk )begin //当位于WORK状态时,计数,一行像素输出完成后再延时,
if (rst)
cnt<=16'd1;
else if(state==WORK)begin
if(cnt==(r_R_cnt*CHANNELS+DELAY_line))
cnt<=16'd1;
else
cnt<=cnt+1'd1;
end
else
cnt<=16'd1;
end
always@(posedge sys_clk )begin
cnt1<=cnt;
end
always@(posedge sys_clk )begin //每一行结束cnt_line_vaild加一
if (rst)
cnt_line_vaild<=8'd0;
else if (state==WORK) begin
if((cnt1==(r_R_cnt*CHANNELS)))begin
if (cnt_line_vaild==NUM_lines)
cnt_line_vaild<=8'd0;
else
cnt_line_vaild<=cnt_line_vaild+1'd1;end
else
cnt_line_vaild<=cnt_line_vaild;end
else
cnt_line_vaild<=0;
end
//多通道信号合并输出
assign out_channel1=r_pluse_out1||(r_pluse_out2&(CHANNELS>=2))||(r_pluse_out3&(CHANNELS>=3))||(r_pluse_out4&(CHANNELS>=4))||
(r_pluse_out5&(CHANNELS>=5))||(r_pluse_out6&(CHANNELS>=6))||(r_pluse_out7&(CHANNELS>=7))||(r_pluse_out8&(CHANNELS>=8))||
(r_pluse_out9&(CHANNELS>=9))||(r_pluse_out10&(CHANNELS>=10))||(r_pluse_out11&(CHANNELS>=11))||(r_pluse_out12&(CHANNELS>=12))||
(r_pluse_out13&(CHANNELS>=13))||(r_pluse_out14&(CHANNELS>=14))||(r_pluse_out15&(CHANNELS>=15))||(r_pluse_out16&(CHANNELS>=16));
assign Out_channel=(CHANNELS==1)?r_cmos_signal0:out_channel1;
endmodule
今天就分享到这啦!后续会进一步升级优化这些模块。目前个人各方面还不成熟,欢迎批评指正,一块讨论哦~
|