« 新しい物件を探しに、本郷をお散歩 | トップページ | MITOUJTAGオンラインが完成に近づく »

2015.11.01

Kintex-7 PCI ExpressのTLP受信回路

Kintex-7でPCI Expressの回路を作っています。

まず、CoreGeneratorでPCI Expressのコアを生成すると、下の図のような構造のファイルが作られます。

Pcie1_2

これらのファイルはCoreGenのXCOファイルをプロジェクトに追加しているときには見えません。XCOを外して、\ipcore_dir\pcie_7x_v1_9\example_design ディレクトリの中にあるファイルをプロジェクトに追加すると、見えるようになります。

生成されたコアは、pcie_7x_v1_9というブロックと、pcie_app_7xという2つの大きなブロックに分けられ、pcie_7x_v1_9にはEndPoint BlockやGTXなど、人間が触る必要がない部分が入っています。

ユーザが作るべき回路はpcie_appに入っています。CoreGenで自動生成すると簡単なPIOのサンプルが作られているので、これをもとに改良していくことにします。

今回は受信回路の動作を解析します。受信回路はPIO_RX_ENGINE.vhd です。

ホストパソコンからMemory Writeを行ってみたときの波形をMITOUJTAGでキャプチャしたものを下図に示します。

Pcie2

XILINXのコアは、受信したデータm_axis_rx_tdataに乗せ、m_axis_rx_tvalidとm_axis_rx_tlastを使ってデータの有無とパケットの終端を示してきます。

XILINX 7シリーズのPCIeコアはデータバスが64bitです。m_axis_rx_tdataからは40000001 0000000F F0600000 00010203という4DWのデータが出てきていることがわかります。

パケットの先頭を示すフラグはないので、PIO_RX_ENGINE.vhdの中ではin_packet_qという信号と、sopという信号を作ってパケットの先頭の位置を割り出しています。

in_packet_qはm_axis_rx_tvalidでアサートされ、m_axis_rx_tlastでデアサートされます。もちろん、ユーザ回路が準備可能であることを示すtready_intも絡んできます。

sopが'1'のとき、パケットの先頭であり、このときのデータバスの[31:0]の値を見て、パケットの種類を判別しています。ステートマシンのコード(state_decode)も0→2→8→0と動いています。上の波形では40000001なので、長さ1のメモリライトとして解釈しています。

しかし、次の場合はどうでしょう。これは長さ64バイトのCombied Writeを発行したときの波形です。メモリライトなのですが、先頭の4DWは40000010となっていて、長さフィールドが0x10です。

Pcie3

この場合、ステートマシンが反応していません。

その原因は、下記のコードにあります。

if ((sop = '1') and (m_axis_rx_tvalid = '1') and (m_axis_rx_tready_int = '1')) then
  case (m_axis_rx_tdata(30 downto 24)) is
    when RX_MEM_RD32_FMT_TYPE =>
      m_axis_rx_tready_int <= '0' after TCQ;
      if (m_axis_rx_tdata(9 downto 0) = "0000000001") then
        state        <= PIO_64_RX_MEM_RD32_DW1DW2 after TCQ;
      else
        state        <= PIO_64_RX_RST_STATE after TCQ;
      end if;

    when RX_MEM_WR32_FMT_TYPE =>
      m_axis_rx_tready_int <= '0' after TCQ;
      if (m_axis_rx_tdata(9 downto 0) = "0000000001") then
        state        <= PIO_64_RX_MEM_WR32_DW1DW2 after TCQ;
      else
        state        <= PIO_64_RX_RST_STATE after TCQ;
      end if;

先頭のデータの[9:0]は長さフィールドで、これが1の場合しかステートマシンが反応しないようになっています。これゆえ、Combined Writeのときのように4バイトを超えるデータを書き込もうとしたときに対応できないのです。

つまり、書き込みパケットが4バイトに制限されるから、ものすごく遅い。PCI Expressの速度のメリットがないコアであるといえるでしょう。

どうすればよいかというと、まず、最初の条件のところから長さに関する制約を取り払います。そして、長さフィールドをremainという新たに作った信号に代入します。

    when RX_MEM_WR32_FMT_TYPE =>
      m_axis_rx_tready_int <= '0';
      remain <= m_axis_rx_tdata(9 downto 0);
      state        <= PIO_64_RX_MEM_WR32_DW1DW2;

そして、ステートがPIO_64_RX_MEM_WR32_DW1DW2に遷移したら、次のような内部ステートfstateを作ってここにとどまります。

最初の1回目のときには書き込みアドレスをフェッチする。二回目以降は書き込みデータをフェッチする。3回目以降は書き込みアドレスをインクリメントしていきます。

when PIO_64_RX_MEM_WR32_DW1DW2 =>
    if (m_axis_rx_tvalid = '1') then
      m_axis_rx_tready_int <= not m_axis_rx_tready_int;
      if(fstate = "00") then
          wr_addr_p        <= region_select(1 downto 0) & m_axis_rx_tdata(10 downto 2);
          fstate <= "01";
      else
          fstate <= "10";
          if(m_axis_rx_tready_int = '1') then
              wr_data_p        <= m_axis_rx_tdata(63 downto 32);
          else
              wr_data_p        <= m_axis_rx_tdata(31 downto 0);
          end if;
          remain <= remain - 1;
          wr_en_p          <= '1';
          if(fstate = "10") then
              wr_addr_p(8 downto 0) <= wr_addr_p(8 downto 0) + 1;
          end if;
          if(remain = 1) then
              state            <= PIO_64_RX_WAIT_STATE;
          end if;
      end if;
    else
      state<= PIO_64_RX_MEM_WR32_DW1DW2;
    end if;

また、データバスm_axis_rx_tdataは64ビットですが、ユーザ回路には32bitで渡すので、上と下とに分けて受け取らなければなりません。この回路では、データを受信するためのこのステートでm_axis_rx_tready_intをバタバタと動かして、AXIバスに2サイクルに1回だけデータを送るようにさせています。

Pcie4

このようにして、長いデータも受信することができるようになりました。

PCI ExpressのGen1でつないだ場合、170MB/secくらい出ているようです。

Pcie5

|

« 新しい物件を探しに、本郷をお散歩 | トップページ | MITOUJTAGオンラインが完成に近づく »

コメント

コメントを書く



(ウェブ上には掲載しません)




« 新しい物件を探しに、本郷をお散歩 | トップページ | MITOUJTAGオンラインが完成に近づく »