FT600Q开发笔记

  • 为什么选择FT600Q
  • 电路图
  • FT600Q引脚
  • 时序
  • FPGA接收数据
  • 时序
  • 说明
  • FPGA发送时序
  • 时序
  • 说明
  • FT600中止(abort)接收
  • FT600中止(abort)发送
  • Verilog代码
  • 运行结果
  • FPGA接收
  • FPGA发送
  • FT600中止接收
  • FT600中止发送
  • 接收速率
  • 发送速率

为什么选择FT600Q

为将SDR中FPGA处理后的IQ数据发送到PC,可以通过网络或者USB的方式。FPGA处理后的数据为24位,抽取后,10MHz的采样率,两个通道数据传输率将达到(24/8) x 2 x 10MHz=60MB/s。至少千兆网和USB 3.0才能满足速度要求。
选择FT600Q作为USB 3.0的接口芯片的一个因素是FT600Q为QFN封装,便于焊接;另一个因素是手头的FPGA开发板只有40pin的扩展插槽,无法支持32位的USB接口。
FT600Q支持多种模式,各种模式有不同的通道数量。这里全部都使用245模式。

FT600Q引脚

CLK:输出时钟到bus master[FPGA]
TXE_N:输出,发送缓冲区空(4KB),FPGA可以将数据发送到FT600
RXF_N:输出,接收缓冲区满(4KB),FPGA可以接收数据
OE_N:输入,输出使能,FPGA告知FT600可以输出数据
WR_N:输入,写使能,FPGA告知向FT600写入数据
RD_N:输入,读使能,FPGA告知从FT600读取数据
BE[3:0]:(FT600是BE[1:0]),字节有效信号,在FPGA的读周期中,FT600告知哪些字节是有效的;在FPGA的写周期中,FPGA告知FT600哪些字节是有效的。通常在写入的最后一个周期中,如果数据的字节长度没有对齐,那么有些字节就是无效的。

时序

这个代码主要实现与FTDI提供的FT600DataStreamerDemoApp进行通信。

FPGA接收数据

时序

FT芯片支持虚拟化吗_FT芯片支持虚拟化吗

说明

PC端调用FT_WritePipe函数,向FT600发送数据
FT600拉低RXF_N引脚,告知FPGA缓冲区有数据
FPGA拉低OE_N,告知FT600可以使用数据线
FPGA拉低RD_N,告知FT600开始发送数据
FT600缓冲区空,FT600拉高RXF_N
FPGA拉高OE_N和RD_N,停止接收

FPGA发送时序

时序

FT芯片支持虚拟化吗_usb_02

说明

PC端调用FT_ReadPipi函数,告知FT600准备接收数据
FT600拉低TXE_N,告知FPGA缓冲区空,可以开始发送
FPGA拉低WR_N,告知FT600开始发送数据
FT600在缓冲区满后,拉高TXE_N,告知FPGA停止发送
FPGA拉高WR_N,停止发送

FT600中止(abort)接收

当PC端要中止接收时要通过OOB(Out-Of-Band)信号告知FPGA中止。FT600的stream demo中,接收中止OOB信号是通过GPIO[0]发送的。

FT芯片支持虚拟化吗_ide_03

  1. 调用FT_ReadPipe
  2. FT_ReadPipe超时
  3. 应用初始化abort recovery
  4. 应用通过FT_WriteGPIO发出read side OOB信号给FPGA
  5. FPGA读到read side OOB signal
  6. 如果TXE_N被拉低,说明FT600还能接收数据,此时FGPA应进行非对齐写,这样可以使FT600中的会话关闭。
  7. 应用等待10毫秒(依赖于FGPA的设计)
  8. 应用调用FT_AbortPipe并返回
  9. 应用停止read side OOB信号
  10. pipe为空
  11. 应用可以继续传输数据

FT600中止(abort)发送

中止发送也是通过OOB发送给FPGA。FT600的stream demo中,接收中止OOB信号是通过GPIO[1]发送的。

FT芯片支持虚拟化吗_ide_04

  1. 应用调用FT_WritePipe
  2. FT_WritePipe超时
  3. 应用初始化abort recovery
  4. 应用通过FT_WriteGPIO发出write side OOB信号给FPGA
  5. 应用等待10毫秒(或更长时间)
  6. FPGA检测到write side OOB信号
  7. FPGA从FT600中读取数据,直到RXF_N被拉高
  8. 应用定时器到
  9. 应用调用FT_AbortPipe
  10. 应用停止write side OOB信号
  11. 管道变空,完成abort recovery

Verilog代码

//FT600 stream
//BG6RDF

`timescale 1ns / 1ns

module FT600_stream
(
    //PL时钟
    input                  pl_clk,          //PL端时钟
    // 复位
    input                  rst_n,           //外部复位
    output reg             ft600_reset_n,   //ft600复位

    // FIFO interface    
    input                  ft600_clk,
    inout [15:0]           ft600_data,
    inout [1:0]            ft600_be,
    input                  ft600_rxf_n,
    input                  ft600_txe_n,
    output                 ft600_oe_n,
    output                 ft600_wr_n,
    output                 ft600_rd_n,
    output reg             ft600_wakeup_n,
    input [1:0]            ft600_gpio,
    output                 ft600_siwu_n,
    
    output                 led0,
    output                 led1
);

reg ft600_wakeup_reg;
reg led0_reg;
reg led1_reg;

assign led0=led0_reg;
assign led1=led1_reg;

//从host来,退出读操作
wire r_oob;
//从host来,退出写操作
wire w_oob;

assign ft600_siwu_n = 1'b1;             // pull-up

wire [15:0] rd_data;
wire [15:0] wr_data;
wire [1:0] be_rd;
reg [1:0] be_wr;
reg [2:0] ft600_state;                  //状态

localparam  FT600_IDLE=0,
            FT600_READ=1,
            FT600_READ_ABORT=2,
            FT600_WRITE=3,
            FT600_WRITE_ABORT=4;

assign rd_data  =  ft600_data;
assign ft600_data  =  (ft600_state==FT600_WRITE || ft600_state==FT600_WRITE_ABORT) ? wr_data : 16'bz;
assign be_rd    =   ft600_be;
assign ft600_be =  (ft600_state==FT600_WRITE || ft600_state==FT600_WRITE_ABORT) ? be_wr : 2'bz;// write data dir

ila_ft600 ila_ft600_inst
(
    .clk(ft600_clk) ,
    .probe0(ft600_clk),
    .probe1(ft600_data),
    .probe2(ft600_be),
    .probe3(ft600_rxf_n),
    .probe4(ft600_txe_n),
    .probe5(ft600_oe_n),
    .probe6(ft600_wr_n),
    .probe7(ft600_siwu_n),
    .probe8(ft600_rd_n),
    .probe9(ft600_wakeup_n),
    .probe10(ft600_gpio),
    .probe11(rd_data),
    .probe12(wr_data),
    .probe13(be_rd),
    .probe14(be_wr),
    .probe15(led0),
    .probe16(led1),
    .probe17(ft600_state),
    .probe18(r_oob),
    .probe19(w_oob),
    .probe20(ft600_reset_n)
);

reg [15:0] wr_cnt;
assign wr_data = 16'hffff - wr_cnt;

//写计数器
always @(posedge ft600_clk)
begin
    if(!ft600_reset_n)begin
        wr_cnt <= 16'd0;
    end
    else 
    if(!ft600_wr_n) 
    begin
        wr_cnt <= wr_cnt + 1'b1;
    end
end

//写信号
assign ft600_wr_n=!((ft600_state==FT600_WRITE || ft600_state==FT600_WRITE_ABORT) && !ft600_txe_n);
//读信号,读状态且有数据可读
assign ft600_rd_n=!((ft600_state==FT600_READ || ft600_state==FT600_READ_ABORT) && (!ft600_rxf_n));
//在空闲态或读状态,且有数据可读时,允许FT600控制数据线
assign ft600_oe_n=!((ft600_state==FT600_IDLE || ft600_state==FT600_READ || ft600_state==FT600_READ_ABORT) && !ft600_rxf_n);

//定时器
localparam DELAY_5MS_CNT=(5*100000-1);
reg[31:0] delay_timer;

//状态机
always @(posedge ft600_clk)begin
    if(!ft600_reset_n)
    begin
        ft600_state <= FT600_IDLE;
    end
    else 
    begin
        if (r_oob)
        begin
            be_wr<=2'b01;
            delay_timer <= DELAY_5MS_CNT;
            ft600_state<=FT600_WRITE_ABORT;
        end
        else if (w_oob)
        begin
            delay_timer <= DELAY_5MS_CNT;
            ft600_state<=FT600_READ_ABORT;
        end
        else
        begin
            case(ft600_state)
            FT600_IDLE:
            begin
                be_wr <= 2'b11;
                if((!ft600_rxf_n))
                    ft600_state  <= FT600_READ;         //有数据可读
                else if(!ft600_txe_n)
                    ft600_state  <= FT600_WRITE;        //可写入数据
                else
                    ft600_state <= FT600_IDLE;
            end
            FT600_READ:
                if (ft600_rxf_n)
                    ft600_state  <= FT600_IDLE;
            FT600_READ_ABORT:
                if (delay_timer==32'b0)
                    ft600_state <= FT600_IDLE;
                else
                begin
                    delay_timer=delay_timer-1'b1;
                end
            FT600_WRITE:
                if(ft600_txe_n)
                    ft600_state  <= FT600_IDLE;
            FT600_WRITE_ABORT:
                if (delay_timer==32'b0)
                    ft600_state <= FT600_IDLE;
                else
                begin
                    delay_timer=delay_timer-1'b1;
                end
            endcase
        end           
    end
end

//延时复位
localparam RST_CYCLE=100;
reg [19:0] rst_cnt;

always @(posedge pl_clk)
begin
    if (!rst_n)
    begin
        ft600_reset_n<=0;
        rst_cnt<=0;
    end
    else
    begin
        if (rst_cnt<RST_CYCLE)
            rst_cnt<=rst_cnt+1;
        else
        begin
            ft600_reset_n<=1;
        end
    end
end

//定时唤醒FT600,确保DEBUG_HUB有时钟
localparam WAKEUP_CYCLE=8'd100;
reg [7:0] wakeup_cnt;
always @(posedge pl_clk)
begin
    if (!rst_n)
    begin
       ft600_wakeup_n<=1'b1;
       wakeup_cnt<=0;
    end
    else
    begin
       if (wakeup_cnt==WAKEUP_CYCLE)
       begin
            wakeup_cnt<=0;
            ft600_wakeup_n<=!ft600_wakeup_n;
       end
       else
            wakeup_cnt<=wakeup_cnt+1;
    end
end

//LED
reg [1:0] ft600_gpio_p1;
reg [1:0] ft600_gpio_p2;

assign r_oob=(ft600_gpio_p2[0]==1'b0 && ft600_gpio_p1[0]==1'b1) ? 1'b1 : 1'b0;
assign w_oob=(ft600_gpio_p2[1]==1'b0 && ft600_gpio_p1[1]==1'b1) ? 1'b1 : 1'b0;

//OOB
always @(posedge ft600_clk)
begin
    if (!ft600_reset_n)
    begin
        led0_reg<=1;
        led1_reg<=1;
        ft600_gpio_p1<=2'b11;
        ft600_gpio_p2<=2'b11;
    end
    else
    begin
        ft600_gpio_p2<=ft600_gpio_p1;
        ft600_gpio_p1<=ft600_gpio;
        
        if (ft600_gpio_p2[0]==1'b0 && ft600_gpio_p1[0]==1'b1)
            led0_reg<=1'b0;
        if (ft600_gpio_p2[1]==1'b0 && ft600_gpio_p1[1]==1'b1)
            led1_reg<=1'b0;
    end
end

endmodule

运行结果

FPGA接收

FT芯片支持虚拟化吗_usb_05

FPGA发送

FT芯片支持虚拟化吗_ide_06

FT600中止接收

FT芯片支持虚拟化吗_usb_07

FT600中止发送

FT芯片支持虚拟化吗_usb_08

接收速率

FT芯片支持虚拟化吗_fpga_09

发送速率

FT芯片支持虚拟化吗_fpga_10