[TEC] FT600の基本操作例(APIによる送受信) - Multi-Channel FIFO

  EDA-009は、FTDI社のUSB3.0 SuperSpeed FIFOブリッジIC "FT600"を搭載したUSB-FPGAボードです。(Altera Cyclone V搭載)
FT600とFPGA間でデータの送受信を行いましたので、ご参考に手順をお示しします。

USB FPGA FT600 CYCLONE V
 

 FT600は「Multi-Channel FIFO」と「245 Synchronous FIFO」という、ふたつの動作モードを選択できます。今回の実験ではMulti-Channel FIFOモード(以後、MultiChモード)を使用してFPGAとデータ通信してみました。
 通信条件などは下記の通りです。

  • Write/Read Pipe = 1
  • FIFO Clock = 100MHz
  • 受信はCallback割り込みを使用

FT600用API(D3XX)に関しては公式の資料を参考してください。 AN_379 - D3XX Programmers Guide
通信タイミング(プロトコル)についてはFT600のデータシートを参照してください。 F600/FT601 Series - SuperSpeed USB3.0 ICs


1.ハンドルの取得

 FT_Create()によりハンドルを取得します。今回はシリアルナンバーでOPENしました。(Flag = FT_OPEN_BY_SERIAL_NUMBER)。
問題がなければFT_OKが返ります。

FT_Create(serialNumber, FT_OPEN_BY_SERIAL_NUMBER, &this->ftHandle);

 デバイスのシリアルナンバーは下記のように取得します。

char serialNumber[16];
FT_ListDevices((PVOID)dwDeviceIndex, serialNumber, FT_LIST_BY_INDEX | FT_OPEN_BY_SERIAL_NUMBER);

 


2.動作モードの変更(チップ情報の書き換え)

 FT600のコンフィギュレーションを設定します。書き込みの前に、元となるデータを取得しておきます。

FT_60XCONFIGURATION chipConfig;
FT_GetChipConfiguration(ftHandle, &chipConfig);

 Multi-Chモードで動作させるための値を設定し書き戻します。

chipConfig.FIFOMode = CONFIGURATION_FIFO_MODE_600;
chipConfig.ChannelConfig = CONFIGURATION_CHANNEL_CONFIG_1;
chipConfig.FIFOClock = CONFIGURATION_FIFO_CLK_100;
chipConfig.OptionalFeatureSupport |= CONFIGURATION_OPTIONAL_FEATURE_ENABLENOTIFICATIONMESSAGE_INCH1;
chipConfig.OptionalFeatureSupport |= CONFIGURATION_OPTIONAL_FEATURE_DISABLEUNDERRUN_INCH1;
FT_SetChipConfiguration(ftHandle, &chipConfig);

 書き込み後はチップの再起動のため一度ポートを閉じます。

FT_Close(ftHandle);
WaitSec(5); //5秒待ち

 


3.Multi-Channel FIFO mode のProtocolについて

 Multi-Chモードでは送受信FIFOの状態がData[15:8]を使用して示されます。この時Data[7:0]はHi-Zです。

Data FIFO Channel
15 OUT 4
14 OUT 3
13 OUT 2
12 OUT 1
11 IN 4
10 IN 3
9 IN 2
8 IN 1

 今回の実験ではChannel = 1を使用していますので、OUT channel 1 FIFOにデータがある場合Data[12] = 0 となります。IN channel 1 FIFOが受信可能状態である場合Data[8] = 0となります。

 各FIFOのフラグを検出後、マスタ(FPGA)はWR信号を下げつつコマンドを応答します。コマンドはData[7:0]とBE[1:0]で返します。定義は下記の通りです。

Data[12] = 0 検出後にデータを取り出す場合(Master Read)、コマンドとしてBEに00bを、Data[7:0]に0001bを出力しWRを下げます。


(Data[31:16], BE[3:2]はFT600には在りません)

コマンド応答後、FT600よりRXF = 0 をもってデータが出力されます。TXEも上記のタイミングでFT600から出力されますので使用可能です。

Data[8] = 0 となっている場合にはコマンドBE=01b、Data[7:0]=0001b & WR = 0としてFPGAからデータを送信できます。 (Master Write)

 コマンド応答後、FT600よりRXF = 0 をもってデータが受信されます。TXEも上記のタイミングでFT600から出力されますので使用可能です。
なお、Data[8] = 0 が出力されるにはモード設定時に行った下記の設定が必要です。

chipConfig.OptionalFeatureSupport |= CONFIGURATION_OPTIONAL_FEATURE_ENABLENOTIFICATIONMESSAGE_INCH1;

 


3.データ送信 (FT600→FPGA)

 FT_WritePipe()よりデータを送信します。
 Write Pipe Channel 1より出力しますので第2引数のPipeID (Endpoint) には0x02を指定します。
 送信の完了を待ちますので第6引数のOverlappedにはNULLを指定しておきます。Overlapを使用すると非同期動作を行えます。つまりFT_WritePipe関数がデータの送信完了を待ちません。このときの返り値は"FT_IO_PENDING"となります。今回はNULLとしていますので送信完了まで関数から返りません。

FT_WritePipe(ftHandle, 0x02, buff, 10, &bytesTransferred, NULL);

 FPGA側での受信波形を示します。FT600から出力した5WordのデータをFPGAで正常に受信できています。


クリックして拡大

 FPGA受信用のHDLコード例を下記にお示しします。clk周波数 = FIFO clock x2 (立上同期)です。

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity H_FT600_MULTI_RX_V1 is

 port
 (
  clk    : in std_logic;
  reset  : in std_logic;
  act    : in std_logic;

  txe    : in std_logic;
  rxf    : in std_logic;
  wr     : out std_logic;
  data   : inout std_logic_vector(15 downto 0);
  be     : inout std_logic_vector(1 downto 0);
  data_rcv   : out std_logic_vector(15 downto 0);
  data_len   : out std_logic_vector(15 downto 0)
 );

end entity;

architecture rtl of H_FT600_MULTI_RX_V1 is

   -- Build an enumerated type for the state machine
   type state_type is (idle,s0,s1,s2,s3,s4,s5,s6,d0,d1,e0,e1);
   signal state : state_type;
    signal data_r : std_logic_vector(15 downto 0);
    signal rcv_len : std_logic_vector(15 downto 0);

begin

   process (clk, reset)
   begin
   if reset = '1' then
      state <= idle;
      wr <= '1';
        be <= (others=>'Z');
      data <= (others=>'Z');
        data_r <= X"0000";
        rcv_len <= X"0000";

   elsif (rising_edge(clk)) then
   
        data_rcv <= data_r;
        data_len <= rcv_len;
        
        case state is
            when idle =>
                if data(12) = '0' and act = '1' then
                    be <= "00";
                    data(7 downto 0) <= X"01";
                    wr <= '0';
                    rcv_len <= X"0000";
                    state <= s0;
                else
                    be <= (others=>'Z');
                    data <= (others=>'Z');
                    state <= idle;
                end if;
            ---------------------------------------------
            when s0 =>
                state <= s1;
            ---------------------------------------------
            when s1 =>
                state <= s2;
            ---------------------------------------------
            when s2 =>
                be <= (others=>'Z');
                data <= (others=>'Z');
                state <= s3;
            ---------------------------------------------
            when s3 =>
                state <= s4;
            ---------------------------------------------
            when s4 =>
                state <= s5;
            ---------------------------------------------
            when s5 =>
                state <= s6;
            ---------------------------------------------
            when s6 =>
                state <= d0;
            ---------------------------------------------
            when d0 =>
                state <= d1;
            ---------------------------------------------
            when d1 =>
                if rxf = '0' then
                    data_r <= data;
                    rcv_len <= rcv_len + 1;
                    state <= d0;
                else
                    state <= e0;
                end if;
            ---------------------------------------------
            when e0 =>
                state <= e1;
            ---------------------------------------------
            when e1 =>
                wr <= '1';
                state <= idle;
            ---------------------------------------------
        end case;
   ------------------------------------------
   end if;
   end process;

end rtl;

 


5.データの受信 (FPGA→FT600)

 FT_ReadPipe()によりデータを受信します。
 受信割り込みを使用しますのでHandle取得後にCallback関数を登録しておきます。

FT_SetNotificationCallback(this->ftHandle, NotificationCallback, &myContext);

 NotificationCallbackはFT_NOTIFICATION_CALLBACK型で下記のように定義されています。

typedef VOID(*FT_NOTIFICATION_CALLBACK)(PVOID pvCallbackContext, E_FT_NOTIFICATION_CALLBACK_TYPE eCallbackType, PVOID pvCallbackInfo);

 Callback関数内では引数を使用して受信用の関数を呼びます。ucEndpointNoには0x82が入っています。cntはグローバル定義のユーザコンテキストです(内容自由)。

enum _E_FT_NOTIFICATION_CALLBACK_TYPE type = eCallbackType;
if(type == E_FT_NOTIFICATION_CALLBACK_TYPE_DATA){
    FT_NOTIFICATION_CALLBACK_INFO_DATA *info = (FT_NOTIFICATION_CALLBACK_INFO_DATA*)pvCallbackInfo;
    ReadPipe(info->ucEndpointNo, cnt->buff, info->ulRecvNotificationLength);
}

 Callback関数から呼ぶ受信用関数は下記のようにしました。Overlapを使用し、受信完了を非同期で待っています。

int __fastcall TFt600::ReadPipe(unsigned char pipeId, BYTE *buff, int bytes)
{
    int result = 0;
    ULONG bytesRead;
    ULONG bytesToRead = bytes;
    OVERLAPPED overlappedRead = {0};

    if(FT_InitializeOverlapped(ftHandle, &overlappedRead) == FT_OK){

        ftStatus = FT_ReadPipe(ftHandle, pipeId, buff, bytesToRead, &bytesRead, &overlappedRead);

        if(ftStatus == FT_IO_PENDING){
            while(1){
                ftStatus = FT_GetOverlappedResult(ftHandle, &overlappedRead, &bytesRead, false);
                if(ftStatus == FT_IO_INCOMPLETE){
                    continue;
                }else if(ftStatus != FT_OK){
                    AddMemo(String().sprintf(_T("ReadPipe()::FT_GetOverlappedResult. (0x%X)"), ftStatus));
                    break;
                }else{ //ftStatus == FT_OK
                    result = 1;
                    break;
                }
            }
        }
        if(result != 0) return result;
        //----------------------------------------

        if(FT_ReleaseOverlapped(ftHandle, &overlappedRead) != FT_OK){
            AddMemo("Error, ReadPipe()::FT_ReleaseOverlapped");
            result = -1;
        }

    }else{
        AddMemo("Error, ReadPipe()::FT_InitializeOverlapped");
    }

    return result;
}

FPGA側での送信波形をお示しします。


クリックして拡大

 FPGA受信用のHDLコード例をお示しします。clk周波数 = FIFO clock x2 (立上同期)です。データはAsciiのインクリメントデータを出力しています。

library ieee;

use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity H_FT600_MULTI_TX_V2 is

 port
 (
  clk    : in std_logic;
  reset  : in std_logic;
  start    : in std_logic;
  sending    : out std_logic;

  txe    : in std_logic;
  rxf    : in std_logic;
  wr     : out std_logic;
  data   : inout std_logic_vector(15 downto 0);
  be     : inout std_logic_vector(1 downto 0);
  send_len   : in std_logic_vector(15 downto 0)
 );

end entity;

architecture rtl of H_FT600_MULTI_TX_V2 is

   -- Build an enumerated type for the state machine
   type state_type is (idle,s0,s1,s2,s3,s4,d0,d1,e0,e1);
   signal state : state_type;
    signal data_r : std_logic_vector(15 downto 0);
    signal send_len_r : std_logic_vector(15 downto 0);

begin

   process (clk, reset, rxf, start)
   begin
   if reset = '1' then
        sending <= '0';
      state <= idle;
      wr <= '1';
        be <= (others=>'Z');
      data <= (others=>'Z');
        data_r <= X"4141";
        send_len_r <= X"0000";

   elsif (rising_edge(clk)) then
   
        dbg_data <= data_r;
        
        case state is
            when idle =>
                if start = '0' then
                    send_len_r <= X"000D";
                    data_r <= X"4241";
                    sending <= '1';
                    state <= s0;
                else
                    sending <= '0';
                    state <= idle;
                end if;
            ---------------------------------------------
            when s0 =>
                if data(8) = '0' then
                    be <= "01";
                    data (15 downto 8) <= (others=>'Z');
                    data(7 downto 0) <= X"01";
                    wr <= '0';
                    state <= s1;
                else
                    be <= (others=>'Z');
                    data <= (others=>'Z');
                    wr <= '1';
                    state <= s0;
                end if;
            ---------------------------------------------
            when s1 =>
                state <= s2;
            ---------------------------------------------
            when s2 =>
                
                state <= s3;
            ---------------------------------------------
            when s3 =>
                if txe = '1' then
                    data <= (others=>'Z');
                    state <= s4;
                else
                    state <= s3;
                end if;
            ---------------------------------------------
            when s4 =>
                be <= "11";
                data <= data_r;
                state <= d0;
            ---------------------------------------------
            when d0 =>
                if rxf = '0' then
                    be <= "11";
                    data <= data_r;
                    data_r <= data_r + X"0202";
                    send_len_r <= send_len_r - 1;
                    state <= d1;
                else
                    be <= (others=>'Z');
                    data <= (others=>'Z');
                    wr <= '1';
                    state <= e0;    -- RX FIFO IS FULL
                end if;
            ---------------------------------------------
            when d1 =>
                if send_len_r /= X"0000" then
                    be <= "11";
                    data <= data_r;
                    state <= d0;
                else
                    be <= (others=>'Z');
                    data <= (others=>'Z');
                    wr <= '1';
                    state <= e0;
                end if;
            ---------------------------------------------
            when e0 =>
                if send_len_r /= X"0000" then
                    state <= s0;
                else
                    state <= e1;   -- DATA SEND COMPLETE
                    sending <= '0';
                end if;
            ---------------------------------------------
            when e1 =>
                if start = '1' then
                    state <= idle;
                else
                    state <= e1;
                end if;
            ---------------------------------------------
        end case;
   ------------------------------------------
   end if;
   end process;

end rtl;

 

お問い合わせについて

 ご不明な点や間違いなどありましたらご連絡ください。
 弊社では、FPGAそのものの使い方や開発ツールの使い方などは、サポートしておりませんので予めご了承ください。


[kw] 2018-04-26 TEC-FPGA EDA-009 FTDI USB3.0 FT600 Multi-Channel-FIFO


[]