mipi数据流处理

发布时间 2023-04-14 22:28:09作者: VincentZJ

总体概览

配置摄像头 将摄像头配置为RAW10输出格式
图像数据串转并 将从两个lane通道接收的串行数据转换为byte类型数据
字节对齐 由于应用层原始数据打包后是byte形式,在传输时又转换成bit形式,在进行图像的逆过程时,需要保证一个byte数据的各个bit在是原来的byte数据
通道对齐 lane0和lane1数据到达FPGA的时刻存在偏差,要保证恢复后的数据相邻的两个byte是原始数据的相邻byte
解析出原始图像数据 一个长包包含一行数据,但是还包含包头等非图像数据,需要去掉
字节转像素 将解析好的图像数据转换成能够通过hdmi显示的像素数据

摄像头配置

OV5640寄存器配置

  时钟配置

    

  首先,采用的是720P的图像模式,帧率是30帧,我们知道图像实际传输的时候分辨率会比我们用到的有效分辨率高,比如我们这里720P传输的时候并不是1280720,而是1892740,这样算下来每一帧图像总共有42002400个像素,图像帧率是30帧,这样每秒钟就有42002400*30=42002400个像素需要传输,假如每个像素有两个Byte,也就是16bit,这样每秒钟就是42002400*16=672038400个比特,而我们是两个通道传输,又是DDR模式,所以相应的时钟还要除以通道数目,再除以2(DDR模式),所以最终的时钟速率是168009600, 也就是168MHz。

Register Name Value Description
0x3035 PLL Control1 0x21 System clock divided by 2, MIPI divided by 2
0x3036 PLL Control2 0x54 PLL multiplier by 0x54 = 84
0x3037 PLL Control3 0x13 PLL predivider is 31

  这样我们根据实际参考时钟为24M来计算,首先24M经过3分频变为8M,然后经过84倍的倍频变为672M,在经过系统分频2变为336MHz,之后是MIPI的2分频,最终变为168MHz。

寄存器太复杂,太多了,对寄存器的配置理解的还是不够通透,时钟计算不太准确,自己的理解。根据手册配置能够满足时钟的要求。例如 1024×768×30帧/秒的图像采集,计算疫苗产生的像素点为1024×768×30=23,592,960,采用RGB565的格式 传输图像信息,一共16bit的数据,需要2个时钟周期才能完成一个像素点数据的传输,所以需要传输的数据23,592,960×2=47,185,920‬,约为47MHZ,个人认为大于该时钟就完全可以完成数据的传输

IIC时序

MIPI 串转并

  OV5640配置 1080P 30Fps  MIPI LANE DATA RATE = 420Mhz

  mipi ddr mode mipiclk=210Mhz

  mipi 数据传输 低位优先

  1.数据传入包含MIPI_LANE[1:0] MIPI_CLK

    使用IBUFDS_DPHY原语进行数据接入,并实现差分转单端操作

    使用BUFG将转单端的时钟接入全局时钟,并使用BUFGCE_DIV原语生成串转并需要的4分频时钟信号

    ISERDESE3原语,采用DDR模式,将传入时钟和分频时钟接入,输出并行8bit信号

    

MIPI_PHY数据接收和串转并操作
 module mipi_phy_gen(
	//
	input			resetn			,
	input			mipi_clk_p		,
	input			mipi_clk_n		,
	input	[1:0]	mipi_data_p		,
	input	[1:0]	mipi_data_n		,
	
	//
	output 			mipi_byte_clk	,
	output	[7:0]	lane0_byte_data	,
	output	[7:0]	lane1_byte_data
    );
	
	
//clock

wire			clk_in_int;
wire 			clk_div;

wire			clk_in_int_bufg;

 IBUFDS_DPHY #(
      .DIFF_TERM("TRUE"),             // Differential termination
      .IOSTANDARD("MIPI_DPHY_DCI"),         // I/O standard
      .SIM_DEVICE("ULTRASCALE_PLUS")  // Set the device version (ULTRASCALE_PLUS, ULTRASCALE_PLUS_ES1,
                                      // ULTRASCALE_PLUS_ES2)
   )
   IBUFDS_DPHY_inst_uclk (
      .HSRX_O(clk_in_int_bufg),             // 1-bit output: HS RX output
      .LPRX_O_N(),         // 1-bit output: LP RX output (Slave)
      .LPRX_O_P(),         // 1-bit output: LP RX output (Master)
      .HSRX_DISABLE(1'b0), // 1-bit input: Disable control for HS mode
      .I(mipi_clk_p),                       // 1-bit input: Data input0 PAD
      .IB(mipi_clk_n),                     // 1-bit input: Data input1 PAD
      .LPRX_DISABLE(1'b1)  // 1-bit input: Disable control for LP mode
   );

BUFG BUFG_inst (
        .O                              (clk_in_int                      ),  // 1-bit output: Clock output
        .I                              (clk_in_int_bufg                          )   // 1-bit input: Clock input
    );


	 
 BUFGCE_DIV #(
      .BUFGCE_DIVIDE(4),      // 1-8
      // Programmable Inversion Attributes: Specifies built-in programmable inversion on specific pins
      .IS_CE_INVERTED(1'b0),  // Optional inversion for CE
      .IS_CLR_INVERTED(1'b0), // Optional inversion for CLR
      .IS_I_INVERTED(1'b0)    // Optional inversion for I
   )
   BUFGCE_DIV_inst (
      .O(clk_div),     // 1-bit output: Buffer
      .CE(1'b1),   // 1-bit input: Buffer enable
      .CLR(1'b0), // 1-bit input: Asynchronous clear
      .I(clk_in_int_bufg)      // 1-bit input: Buffer
   );
	 
assign mipi_byte_clk = clk_div;

//data
wire	[1:0]	data_in_from_pins_int;
wire	[7:0]	lane_byte_data[0:1];
assign lane0_byte_data = lane_byte_data[0];
assign lane1_byte_data = lane_byte_data[1];
genvar i;
generate
	for(i=0;i<2;i=i+1)begin:gen
		IBUFDS_DPHY #(
			.DIFF_TERM("TRUE"),             // Differential termination
			.IOSTANDARD("MIPI_DPHY_DCI"),         // I/O standard
			.SIM_DEVICE("ULTRASCALE_PLUS")  // Set the device version (ULTRASCALE_PLUS, ULTRASCALE_PLUS_ES1,
											// ULTRASCALE_PLUS_ES2)
		)
		IBUFDS_DPHY_inst_u0 (
			.HSRX_O(data_in_from_pins_int[i]),             // 1-bit output: HS RX output
			.LPRX_O_N(),         // 1-bit output: LP RX output (Slave)
			.LPRX_O_P(),         // 1-bit output: LP RX output (Master)
			.HSRX_DISABLE(1'b0), // 1-bit input: Disable control for HS mode
			.I(mipi_data_p[i]),                       // 1-bit input: Data input0 PAD
			.IB(mipi_data_n[i]),                     // 1-bit input: Data input1 PAD
			.LPRX_DISABLE(1'b1)  // 1-bit input: Disable control for LP mode
		);
		
		
		ISERDESE3 #(
			.DATA_WIDTH(8),                 // Parallel data width (4,8)
			.FIFO_ENABLE("FALSE"),          // Enables the use of the FIFO
			.FIFO_SYNC_MODE("FALSE"),       // Always set to FALSE. TRUE is reserved for later use.
			.IS_CLK_B_INVERTED(1'b1),       // Optional inversion for CLK_B
			.IS_CLK_INVERTED(1'b0),         // Optional inversion for CLK
			.IS_RST_INVERTED(1'b0),         // Optional inversion for RST
			.SIM_DEVICE("ULTRASCALE_PLUS")  // Set the device version for simulation functionality (ULTRASCALE,
											// ULTRASCALE_PLUS, ULTRASCALE_PLUS_ES1, ULTRASCALE_PLUS_ES2)
		)
		ISERDESE3_u0 (
			.FIFO_EMPTY(),           // 1-bit output: FIFO empty flag
			.INTERNAL_DIVCLK(), // 1-bit output: Internally divided down clock used when FIFO is
												// disabled (do not connect)
		
			.Q(lane_byte_data[i]),                             // 8-bit registered output
			.CLK(clk_in_int),                         // 1-bit input: High-speed clock
			.CLKDIV(clk_div),                   // 1-bit input: Divided Clock
			.CLK_B(clk_in_int),                     // 1-bit input: Inversion of High-speed clock CLK
			.D(data_in_from_pins_int[i]),                             // 1-bit input: Serial Data Input
			.FIFO_RD_CLK(),         // 1-bit input: FIFO read clock
			.FIFO_RD_EN(),           // 1-bit input: Enables reading the FIFO when asserted
			.RST(1'b0)                          // 1-bit input: Asynchronous Reset
		);//set_property DATA_RATE SDR|DDR [get_ports port_name]
	end	

endgenerate
endmodule

MIPI 通道对齐

  SOT = 8'hB8; 帧头也是同步头

  

DI SOT(B8)

LPS(low power state)

8bit 10111000 0000 0000

 

 {byte_data,byte_data_r1};  16bit data,在其中寻找B8,确定数据偏移量;但是B8不一定是SOT,因为数据中也可能存在B8;所以,找到B8之后,计算ECC结果是否等于接收到的ECC,就能确定B8是否代表SOT;如果是SOT,利用偏移位置将拼接的16bit数据对应的数据进行输出;通道对齐模块只是进行寻找B8,验证是否是SOT在数据包解析模块进行

 

数据对齐
 module byte_align(
	input			clk				,
	input			resetn			,
	input	[7:0]	lane_data		,
	output reg[7:0]	mipi_byte_data	,
	output reg		mipi_byte_vld  	,
	input			re_find
);  

//================define==========
localparam SYNC  = 8'hb8;
reg [2:0]	offset;

reg [7:0]	lane_data_r1;


wire [15:0]	data_16bit;
reg [15:0]	data_16bit_r1;

//===================main code=====
always@(posedge clk)begin
	lane_data_r1 <= lane_data;
	data_16bit_r1<= data_16bit;
end

assign data_16bit = {lane_data,lane_data_r1};

always@(posedge clk or negedge resetn)begin
	if(!resetn)begin
		offset <= 'd0;
		mipi_byte_vld <= 1'b0;
	end
	else if(re_find)begin
		offset <= 'd0;
		mipi_byte_vld <= 1'b0;
	end
	else if(mipi_byte_vld == 1'b0)begin
		if(data_16bit[7:0] == SYNC)begin
			offset <= 'd0;
			mipi_byte_vld <= 1'b1;
		end
		else if(data_16bit[8:1] == SYNC)begin
			offset <= 'd1;
			mipi_byte_vld <= 1'b1;
		end
		else if(data_16bit[9:2] == SYNC)begin
			offset <= 'd2;
			mipi_byte_vld <= 1'b1;
		end
		else if(data_16bit[10:3] == SYNC)begin
			offset <= 'd3;
			mipi_byte_vld <= 1'b1;
		end
		else if(data_16bit[11:4] == SYNC)begin
			offset <= 'd4;
			mipi_byte_vld <= 1'b1;
		end
		else if(data_16bit[12:5] == SYNC)begin
			offset <= 'd5;
			mipi_byte_vld <= 1'b1;
		end
		else if(data_16bit[13:6] == SYNC)begin
			offset <= 'd6;
			mipi_byte_vld <= 1'b1;
		end
		else if(data_16bit[14:7] == SYNC)begin
			offset <= 'd7;
			mipi_byte_vld <= 1'b1;
		end
	end
end

//assign mipi_byte_data data_16bit_r1[offset+7:offset];]
always@(*)begin
	case(offset)
		0:	mipi_byte_data = data_16bit_r1[7:0];
		1:	mipi_byte_data = data_16bit_r1[8:1];
		2:	mipi_byte_data = data_16bit_r1[9:2];
		3:	mipi_byte_data = data_16bit_r1[10:3];
		4:	mipi_byte_data = data_16bit_r1[11:4];
		5:	mipi_byte_data = data_16bit_r1[12:5];
		6:	mipi_byte_data = data_16bit_r1[13:6];
		7:	mipi_byte_data = data_16bit_r1[14:7];
		default:	mipi_byte_data = data_16bit_r1[7:0];
	endcase
end

endmodule

 通道对齐

 SOT在摄像头MIPI端是同时传输的,如果两个lane的同步头在FPGA内部传输相差超过1个时钟周期,就意味着源端发送2进制数据超过8bit,也就是超过MIPI传输数据的4个时钟周期(20ns),两个同步头的传输时不会发生这种情景,所以这种情况对应的一定是数据部分,不是同步头。

   两个lane的byte_vld相差不超过一个时钟周期,谁快谁打拍,需做同步处理,谁快谁打拍,同时进入就同时输出。

   如果在两个时钟周期以上,就一定不是SOT,反馈给byte_align,模块重新查找同步头。

 

通道对齐
 module lane_align(
	//sys
	input				clk			,
	input				rst_n		,
	//input
	input		[7:0]	lane0_byte_data	,
	input		[7:0]	lane1_byte_data,
	input				lane0_byte_vld	,
	input				lane1_byte_vld	,
	//output
	output	reg 		word_vld,
	output	reg [15:0]	word_data,
	input				invalid_start
);
//=========================================
//================define===================
//=========================================
localparam LANE0_FIRST = 2'b01;
localparam LANE1_FIRST = 2'b10;
localparam NONE_FIRST  = 2'b11;

wire 			lane_vld_or;
reg 			lane_vld_or_r1;
wire 			lane_vld_or_pos;
reg 			lane_vld_or_pos_r1;
reg 	[7:0]	lane0_byte_data_r1;
reg		[7:0]	lane1_byte_data_r1;
reg 			lane0_byte_vld_r1;
reg 			lane1_byte_vld_r1;

reg 	[1:0]	tap;

wire 			lane_vld_and;

//-----------------------------------------
assign lane_vld_or = lane0_byte_vld || lane1_byte_vld;
assign lane_vld_or_pos = lane_vld_or && !lane_vld_or_r1;
assign lane_vld_and = lane0_byte_vld && lane1_byte_vld;

always@(posedge clk)begin
	lane0_byte_data_r1 	<= lane0_byte_data;
	lane1_byte_data_r1 	<= lane1_byte_data;
	lane_vld_or_r1		<= lane_vld_or;
	lane_vld_or_pos_r1	<= lane_vld_or_pos;
	lane0_byte_vld_r1   <= lane0_byte_vld;
	lane1_byte_vld_r1   <= lane1_byte_vld;
end

always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		tap <= NONE_FIRST;
	else if(lane_vld_or_pos)
		tap <= {lane1_byte_vld,lane0_byte_vld};
end
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		invalid_start <= 1'b0;
	else if(lane_vld_or_pos_r1 && lane_vld_and == 1'b0)
		invalid_start <= 1'b1;
	else
		invalid_start <= 1'b0;
end

always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		word_data <= 'd0;
		word_vld <= 1'b0;
	end
	else begin
		case(tap)
			LANE0_FIRST:begin
				word_data <= {lane1_byte_data,lane0_byte_data_r1};
				word_vld <= lane1_byte_vld;
			end
			LANE1_FIRST:begin
				word_data <= {lane1_byte_data_r1,lane0_byte_data};
				word_vld <= lane0_byte_vld;
			end
			NONE_FIRST:begin
				word_data <= {lane1_byte_data_r1,lane0_byte_data_r1};
				word_vld <= lane1_byte_vld_r1;
			end
			default:begin
				word_data <= {lane1_byte_data_r1,lane0_byte_data_r1};
				word_vld <= lane1_byte_vld_r1;
			end
		endcase
	end
		
end
endmodule