Ultra96でMIPI信号をリードする(2)

手っ取り早くシミュレーションしてISERDESE3を理解しよう。

ざっくり動かすとこんな感じになる。

波形

CLK_P/Nは内部の差動クロックといえば良いのかな?

CLKDIVはCLK_P/Nの2分周、DIN_P/NはCLK_P/Nと同じ周波数で、MIPIをリードするためにDDR想定です。

そうすると、DOUTにパラレルデータ、DOUT_CLKにCLK_P/Nの4分周が出てくる。

もし、SDRの場合は偶数ビットか奇数ビットのどちらかにデータが現れるらしい。

SDRを使う場合はデータシートをご覧くだされ。

ISERDESE5のDOUTで気をつけなければいけないのはどこでデータがロックするかなんだけど、それはわからないのでISERDESE3の後段でビットアライメントを合わせる回路が必要に成る。

あとはREFCLK_FREQUENCYに注意かな?

ISERDESE3のRTL

module csi_rx_lane_phy(
  input RST_N,

  input CLK_P,
  input CLK_N,

  input CLKDIV,

  input DIN_P,
  input DIN_N,

  output DOUT_CLK,
  output [7:0] DOUT
);

wire in_data;
wire [7:0] serdes_data;
IBUFDS
#(
  .DIFF_TERM("TRUE"),
  .IBUF_LOW_PWR("FALSE"),
  .IOSTANDARD("DEFAULT")
)
u_IBUFDS
(
  .I(DIN_P),
  .IB(DIN_N),
  .O(in_data)
);

IDELAYE3 #(
  .CASCADE("NONE"),
  .DELAY_FORMAT("TIME"),
  .DELAY_SRC("IDATAIN"),
  .DELAY_TYPE("FIXED"),
  .DELAY_VALUE(0),
  .IS_CLK_INVERTED(0),
  .IS_RST_INVERTED(0),
  .LOOPBACK("FALSE"),
  .REFCLK_FREQUENCY(500.0),
  .SIM_DEVICE("ULTRASCALE_PLUS"),
  .SIM_VERSION(2.0),
  .UPDATE_MODE("ASYNC")
)
u_IDELAYE3(
  .CASC_OUT(),
  .CNTVALUEOUT(),
  .DATAOUT(in_delayed),

  .CASC_IN(),
  .CASC_RETURN(),
  .CE(),
  .CLK(CLKDIV),
  .CNTVALUEIN(),
  .DATAIN(),
  .EN_VTC(),
  .IDATAIN(in_data),
  .INC(),
  .LOAD(),
  .RST(~RST_N)
);

wire serdes_clk, fifo_empty;

ISERDESE3 #(
  .DATA_WIDTH(8),
  .DDR_CLK_EDGE("OPPOSITE_EDGE"),
  .FIFO_ENABLE("FALSE"),
  .FIFO_SYNC_MODE("FALSE"),
  .IDDR_MODE("FALSE"),
  .IS_CLK_B_INVERTED(0),
  .IS_CLK_INVERTED(0),
  .IS_RST_INVERTED(0),
  .SIM_DEVICE("ULTRASCALE_PLUS"),
  .SIM_VERSION(2.0)
)
u_ISERDESE3 (
  .FIFO_EMPTY(fifo_empty),
  .INTERNAL_DIVCLK(serdes_clk),
  .Q(serdes_data),

  .CLK(CLK_P),
  .CLKDIV(CLKDIV),
  .CLK_B(CLK_N),
  .D(in_data),
  .FIFO_RD_CLK(),
  .FIFO_RD_EN(),
  .RST(~RST_N)
);

assign DOUT = serdes_data;
assign DOUT_CLK = serdes_clk;

endmodule

テストベンチ

`timescale 1ns / 1ps

module tb_csi_rx_lane_phy;

reg RST_N;
reg CLK_P;
wire CLK_N;
reg CLKDIV;
reg DIN_P;
wire DIN_N;
wire DOUT_CLK;
wire [7:0] DOUT;

initial begin
  CLK_P    = 0;
  CLKDIV = 0;
  RST_N    = 0;
  #100;
  RST_N    = 1;
end

always begin
  #(2)  CLK_P <= ~CLK_P;
end

always begin
  #(4)  CLKDIV <= ~CLKDIV;
end

assign CLK_N = ~CLK_P;

initial begin
  #100000;

  $finish();
end

always begin
  wait(RST_N);
  #(1) DIN_P = 1;
  #(2) DIN_P = 0;
  #(2) DIN_P = 1;
  #(2) DIN_P = 0;
  #(2) DIN_P = 1;
  #(2) DIN_P = 0;
  #(2) DIN_P = 0;
  #(2) DIN_P = 1;

  #(2) DIN_P = 1;
  #(2) DIN_P = 1;
  #(2) DIN_P = 1;
  #(2) DIN_P = 0;
  #(2) DIN_P = 1;
  #(2) DIN_P = 1;
  #(2) DIN_P = 0;
  #(2) DIN_P = 1;

  #(2) DIN_P = 0;
  #(2) DIN_P = 1;
  #(2) DIN_P = 0;
  #(2) DIN_P = 1;
  #(2) DIN_P = 0;
  #(2) DIN_P = 1;
  #(2) DIN_P = 0;
  #(2) DIN_P = 1;

  #(2) DIN_P = 0;
  #(2) DIN_P = 1;
  #(2) DIN_P = 0;
  #(2) DIN_P = 1;
  #(2) DIN_P = 0;
  #(2) DIN_P = 1;
  #(2) DIN_P = 0;
  #(2) DIN_P = 1;

  #(1);
end

assign DIN_N = ~DIN_P;

csi_rx_lane_phy u_csi_rx_lane_phy(
  .RST_N(RST_N),

  .CLK_P(CLK_P),
  .CLK_N(CLK_N),

  .CLKDIV(CLKDIV),

  .DIN_P(DIN_P),
  .DIN_N(DIN_N),

  .DOUT_CLK(DOUT_CLK),
  .DOUT(DOUT)
);

endmodule