MIPI——Mobile Industry Processor Interface
DSI用于显示屏,CSI用于摄像头。在某些场合想用FPGA模拟摄像头,可以用Xilinx FPGA MIPI CSI-2 Transmitter Subsystem0。
MIPI协议,网上资料很多,此处不做赘述,此处仅对Xilinx FPGA MIPI CSI-2 Transmitter Subsystem做主要说明。
MIPI CSI-2 Transmitter Subsystem这个IP核,内部包括MIPI D-PHY和MIPI CSI-2 TX Controller。
此处配置接口为AXI4S,CSI lane选择1,输入像素数目为1,Line buffer深度为2048。
如果整个工程仅用一个接口,那么此处选择inlcude shared logic in core。
管脚根据你的硬件设计进行指定,此处数据管脚和时钟管脚,仅HP BANK可以用,绘制原理图之前需要提前看一下某些管脚是否可以用。
由于IP不自动example工程,所以自己搭建测试。
里面有个DATA_TYPE特别恶心,Xilinx的文档没提各个对应关系,让查MIPI官方文档,MIPI官方又要注册账号。
我这里产生图像是128*32的RGB888图像数据,用于仿真。
它的s_axis_tuser对应的Line number和Frame number可以设置为0,内核会自动匹配检测,但是后级MIPI接收端可能不知道图像的行场大小。
module tb_mipi_core ;
//------------------------------------------------------------
parameter C_M_AXI_ADDR_WIDTH = 8;
reg dphy_clk_200M ;
//s_axi
wire s_axi_awready ;
wire s_axi_awvalid ;
wire [C_M_AXI_ADDR_WIDTH-1:0] s_axi_awaddr ;
wire s_axi_arready ;
wire s_axi_arvalid ;
wire [C_M_AXI_ADDR_WIDTH-1:0] s_axi_araddr ;
wire s_axi_wready ;
wire s_axi_wvalid ;
wire [31:0] s_axi_wdata ;
wire [3:0] s_axi_wstrb ;
wire s_axi_rready ;
wire s_axi_rvalid ;
wire [31:0] s_axi_rdata ;
wire [1:0] s_axi_rresp ;
wire s_axi_bready ;
wire s_axi_bvalid ;
wire [1:0] s_axi_bresp ;
//s_axis
reg s_axis_aclk ;
reg s_axis_aresetn ;
// reg s_axis_tvalid ;
wire s_axis_tvalid ;
wire s_axis_tready ;
reg [47:0] s_axis_tdata ;
// reg s_axis_tlast ;
wire s_axis_tlast ;
// reg [95:0] s_axis_tuser ;
wire [95:0] s_axis_tuser ;
reg [5:0] s_axis_tkeep ;
reg [1:0] s_axis_tdest ;
wire txclkesc_out ;
wire mmcm_lock_out ;
wire clkoutphy_out ;
wire [0:0] mipi_phy_if_data_p ;
wire [0:0] mipi_phy_if_data_n ;
wire mipi_phy_if_clk_p ;
wire mipi_phy_if_clk_n ;
wire txbyteclkhs ;
wire interrupt ;
wire system_rst_out ;
wire pll_lock_out ;
wire s_axi_init_rdy;
//------------------------------------------------------------
initial begin
dphy_clk_200M = 0;
s_axis_aresetn = 0;
s_axis_tdest = 0;
// s_axi_awvalid = 0;
// s_axi_wdata = 0;
// s_axi_arvalid = 0;
// s_axi_araddr = 0;
// s_axi_rready = 0 ;
// s_axi_bready = 0 ;
// s_axi_wstrb = 0 ;
// s_axi_awaddr = 0;
// s_axi_wvalid = 0 ;
s_axis_aclk = 0;
s_axis_tdata = 0;
// s_axis_tlast = 0;
// s_axis_tuser = 0;
// s_axis_tvalid = 0;
s_axis_tkeep = 5'h1f;
#200;
s_axis_aresetn = 1;
end
always #2.5 dphy_clk_200M = ~dphy_clk_200M;
always #5 s_axis_aclk = ~s_axis_aclk;
reg [10:0] cnt_h = 0;
reg [10:0] cnt_v = 0;
// 128*32
always @(posedge s_axis_aclk)
if(s_axis_aresetn && s_axi_init_rdy==1)
begin
if(s_axis_tready)
if(cnt_h==200)
cnt_h <= 40;
else
cnt_h <= cnt_h + 1;
if(cnt_h==200)
if(cnt_v >= 31)
cnt_v <= 0;
else
cnt_v <= cnt_v + 1;
end
//----------------------------------------------------------------------------------
// assign s_axis_tvalid = 0;
assign s_axis_tvalid = (cnt_h >= 73) ? 1:0;
assign s_axis_tuser[0] = (cnt_v==0 && cnt_h==73)? 1 : 0 ;
assign s_axis_tlast = (cnt_h==200)? 1 : 0 ;
//-------------------------------------------------------
assign s_axis_tuser[95:64] = 0; //reserved
assign s_axis_tuser[15:7] = 0; //reserved
assign s_axis_tuser[63:48] = 128; //Word count ---- 128*32
assign s_axis_tuser[47:32] = 0; //Line number
assign s_axis_tuser[31:16] = 0; //Frame number
assign s_axis_tuser[6:1] = 6'h24; //Data type --- RGB888
// s_axis_tuser[0] = 0; //Frame start
always @(posedge s_axis_aclk)
if(s_axis_tvalid && s_axis_tready) begin
s_axis_tdata[41:34] <= s_axis_tdata[41:34] + 1;
s_axis_tdata[27:20] <= s_axis_tdata[27:20] + 1;
s_axis_tdata[13:6] <= s_axis_tdata[13:6] + 1;
end
//------------------------------------------------------------
mipi_csi2_tx_subsystem_0
DUT (
.s_axi_awready (s_axi_awready ) ,
.s_axis_aresetn (s_axis_aresetn ) ,
.txclkesc_out (txclkesc_out ) ,
.mmcm_lock_out (mmcm_lock_out ) ,
.s_axis_tdest (s_axis_tdest ) ,
.clkoutphy_out (clkoutphy_out ) ,
.mipi_phy_if_data_n (mipi_phy_if_data_n ) ,
.mipi_phy_if_clk_n (mipi_phy_if_clk_n ) ,
.s_axi_arready (s_axi_arready ) ,
// .s_axi_arprot (0 ) ,
.s_axi_awvalid (s_axi_awvalid ) ,
.s_axis_tdata (s_axis_tdata ) ,
.s_axi_wdata (s_axi_wdata ) ,
.dphy_clk_200M (dphy_clk_200M ) ,
.mipi_phy_if_data_p (mipi_phy_if_data_p ) ,
.mipi_phy_if_clk_p (mipi_phy_if_clk_p ) ,
.txbyteclkhs (txbyteclkhs ) ,
.interrupt (interrupt ) ,
.s_axis_tlast (s_axis_tlast ) ,
.s_axi_arvalid (s_axi_arvalid ) ,
.s_axis_tready (s_axis_tready ) ,
.s_axi_rresp (s_axi_rresp ) ,
.s_axi_bresp (s_axi_bresp ) ,
.s_axi_wready (s_axi_wready ) ,
// .s_axi_awprot (0 ) ,
.s_axis_tuser (s_axis_tuser ) ,
.s_axi_araddr (s_axi_araddr ) ,
.s_axis_aclk (s_axis_aclk ) ,
.s_axis_tvalid (s_axis_tvalid ) ,
.s_axi_rready (s_axi_rready ) ,
.s_axi_bready (s_axi_bready ) ,
.s_axi_wvalid (s_axi_wvalid ) ,
.system_rst_out (system_rst_out ) ,
.s_axi_rvalid (s_axi_rvalid ) ,
.s_axi_bvalid (s_axi_bvalid ) ,
.s_axi_wstrb (s_axi_wstrb ) ,
.s_axi_awaddr (s_axi_awaddr ) ,
.s_axis_tkeep (s_axis_tkeep ) ,
.s_axi_rdata (s_axi_rdata ) ,
.pll_lock_out (pll_lock_out ) );
//-------------------------------------------------------------
//-------------------------------------------------------------
//-------------------------------------------------------------
axi_lite_sm
#(
.C_M_AXI_ADDR_WIDTH (C_M_AXI_ADDR_WIDTH)
)
lite_sm_u
(
// -- System Signals
.s_axi_aclk (s_axis_aclk),
.aresetn (s_axis_aresetn),
// -- Master Interface Write Address
.s_axi_awaddr (s_axi_awaddr),
.s_axi_awvalid (s_axi_awvalid),
.s_axi_awready (s_axi_awready),
// -- Master Interface Write Data
.s_axi_wdata (s_axi_wdata),
.s_axi_wstrb (s_axi_wstrb),
.s_axi_wvalid (s_axi_wvalid),
.s_axi_wready (s_axi_wready),
// -- Master Interface Write Response
.s_axi_bresp (s_axi_bresp),
.s_axi_bvalid (s_axi_bvalid),
.s_axi_bready (s_axi_bready),
// -- Master Interface Read Address
.s_axi_araddr (s_axi_araddr),
.s_axi_arvalid (s_axi_arvalid),
.s_axi_arready (s_axi_arready),
// -- Master Interface Read Data
.s_axi_rdata (s_axi_rdata),
.s_axi_rresp (s_axi_rresp),
.s_axi_rvalid (s_axi_rvalid),
.s_axi_rready (s_axi_rready),
.s_axi_init_rdy(s_axi_init_rdy)
);
endmodule
原来以为不对其做任何配置,直接输入AXIS的数据,这个IP应该就能够正常运行,但是仿真发现其根本没有反应。所以后面通过AXI-LITE总线对其做寄存器配置。
根据手册说明,仅对配置寄存器(Core Configuration options)做配置就可以了。
register_write(0,32'h12); //reset Controller
register_write(0,32'h11); // Enables the core to receive and process packets
// wait Controller ready
while(s_axi_rdata_r[2]==0)
begin
register_read(32'h0);
repeat(10) @(posedge s_axi_aclk);
end
//----
send AXIS stream data to core
根据手册说明,物理层默认初始化时间是1ms,等内核就绪后,发送AXIS数据流,它就应该能正常工作,但是观察输出的时钟和数据,始终不变化。并且内核AXIS接口在接收几包数据后,内核的ready信号一直为0,不能再继续接收数据。
并且输出的差分时钟和数据信号一直不变。
后面折腾半天,最终找到问题原因。
register_write(0,32'h12); //reset Controller
// wait Controller ready
while(s_axi_rdata_r[2]==0)
begin
register_read(32'h0);
repeat(10) @(posedge s_axi_aclk);
end
register_write(0,32'h11); // Enables the core to receive and process packets
//----
send AXIS stream data to core
将之前代码顺序进行调整,仿真竟然通过。也就是在使能内核之前,必须先等内核就绪。