[X]关闭

USB3.0方案FT601Q测速

文档创建者:uisrc
浏览次数:6740
最后更新:2023-01-04
    Setp by Step FT601Q的驱动支持同步模式245模式,异步模式245模式,以及同步模式Stream方式,和异步模式Stream方式,我们这里以比较简单的同步模式245模式来实现对USB3.0 FT601Q方案测速,当然这种测速的速度肯定比不上Stream模式的正确。如果有时间我们也会给出Stream的模式方案。对于FPGA工程师上位机要写的比较好确实比较难,但是软件工程师,这个都是小意思。
这个例子中我们还简化了USB接口函数的设计,更加方便使用。


1、USB3.0 FPGA 程序设计还是用上一篇文章的就可以了
module ft60x_top(
// system control
input                  Rstn_i,//fpga reset
output                 USBSS_EN,//power enable   
// FIFO interface     
input                  CLK_i,
inout [31:0]           DATA_io,
inout [3:0]            BE_io,
input                  RXF_N_i,   // ACK_N
input                  TXE_N_i,
output                 OE_N_o,
output                 WR_N_o,    // REQ_N
output                 SIWU_N_o,
output                 RD_N_o,
output                 WAKEUP_o,
output [1:0]           GPIO_o,
// led
output reg            [3:0]LED,
input                 [3:0]BTN
);

assign USBSS_EN = 1'b1;   
assign WAKEUP_o = 1'b1;
assign GPIO_o   = 2'b00;   
assign SIWU_N_o = 1'b0;

wire rstn;
(*mark_debug = "true"*) wire            RXF_N_i;   // ACK_N
(*mark_debug = "true"*) wire            TXE_N_i;
(*mark_debug = "true"*) reg             OE_N_o;
(*mark_debug = "true"*) reg             WR_N_o;    // REQ_N
(*mark_debug = "true"*) reg             RD_N_o;

(*mark_debug = "true"*) (* KEEP = "TRUE" *)wire [31:0] rd_data;
(*mark_debug = "true"*) (* KEEP = "TRUE" *)wire [31:0] wr_data;
(*mark_debug = "true"*) wire [3 :0] BE_RD;
(*mark_debug = "true"*) wire [3 :0] BE_WR;
(*mark_debug = "true"*) reg [1:0] USB_S;

wire data_rd_valid,data_wr_valid;
assign data_rd_valid = (RD_N_o==1'b0)&&(RXF_N_i==1'b0);
assign data_wr_valid = (WR_N_o==1'b0)&&(TXE_N_i==1'b0);
//read or write flag
assign rd_data  =  data_rd_valid ? DATA_io : 32'd0;//read data dir
assign DATA_io  =  data_wr_valid ? wr_data : 32'bz;// write data dir
assign BE_RD    =  data_rd_valid ? BE_io   : 4'd0; //read data dir
assign BE_io    =  data_wr_valid ? BE_WR   : 4'bz;// write data dir
assign BE_WR    =  4'b1111;

assign wr_data = {8'h00,8'haa,8'hff,4'h0,BTN};

(*mark_debug = "true"*) (* KEEP = "TRUE" *) reg [15:0]rd_cnt;

always @(posedge CLK_i)begin
    if(!rstn)begin
        rd_cnt <= 16'd0;
    end
    else if(data_rd_valid) begin
        rd_cnt <= rd_cnt + 1'b1;
    end
    else begin
       rd_cnt  <= 16'd0;
    end
end


always @(posedge CLK_i)begin
    if(!rstn)begin
        LED <= 4'b0000;
    end
    else if(data_rd_valid) begin
        LED <= rd_data[3:0];
    end
end


always @(posedge CLK_i)begin
    if(!rstn)begin
        USB_S <= 2'd0;
        OE_N_o <= 1'b1;
        RD_N_o <= 1'b1;
        WR_N_o <= 1'b1;
    end
    else begin
        case(USB_S)
        0:begin
            OE_N_o <= 1'b1;
            RD_N_o <= 1'b1;
            WR_N_o <= 1'b1;
            if((!RXF_N_i)) begin
                USB_S  <= 2'd1;
                OE_N_o <= 1'b0;   
            end
            else if(!TXE_N_i)begin
                USB_S  <= 2'd2;
            end
        end
        1:begin
            RD_N_o <= 1'b0;   
            if(RXF_N_i) begin
                USB_S  <= 2'd0;
                RD_N_o <= 1'b1;
                OE_N_o <= 1'b1;      
            end
        end
        2:begin
            WR_N_o <= 1'b0;
            if(TXE_N_i) begin
                USB_S  <= 2'd0;
                WR_N_o <= 1'b1;
             end
        end
        3:begin
            USB_S <= 2'd0;
        end
        endcase                 
    end
end


Delay_rst #(
    .num(20'hffff0)
)
Delay_rst_inst
(
    .clk_i(CLK_i),
    .rstn_i(Rstn_i),
    .rst_o(rstn)
);

2、上位机分析
2.1 USB设备接口设计
和前面的教程使用的方法不一样,我们现在重新编写了更加简易的函数调用接口。而且大家可以看到这个函数和我们之前学习的PCIE课程中的函数非常类似,这样我们只要稍加修改就可以把我们之前写的PCIE上位机应用到USB上位机中了。
Usb_fun.c
#ifdef __cplusplus
extern "C" {
#endif
#include <Windows.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <strsafe.h>
#include <stdint.h>
#include <SetupAPI.h>
#include <INITGUID.H>
#include <WinIoCtl.h>
//#include <AtlBase.h>
#include <io.h>
#include "FTD3XX.h"

//#pragma comment(lib, "../pcie_speed/FTD3XX.lib")

DEFINE_GUID(GUID_DEVINTERFACE_FOR_FTDI_DEVICES,
     0x36fc9e60, 0xc465, 0x11cf, 0x80, 0x56, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00);

static unsigned char *h2c_align_mem_tmp;
static unsigned char *c2h_align_mem_tmp;

static int cnt1=0;
static int cnt2=0;

static FT_HANDLE ftHandle;
static FT_STATUS ftStatus;


static int verbose_msg(const char* const fmt, ...) {

    int ret = 0;
    va_list args;
    if (1) {
        va_start(args, fmt);
        ret = vprintf(fmt, args);
        va_end(args);
    }
    return ret;

}
static BYTE* allocate_buffer(size_t size, size_t alignment) {

    if(size == 0) {
        size = 4;
    }

    if (alignment == 0) {
        SYSTEM_INFO sys_info;
        GetSystemInfo(&sys_info);
        alignment = sys_info.dwPageSize;
        //printf("alignment = %d\n",alignment);
    }
    verbose_msg("Allocating host-side buffer of size %d, aligned to %d bytes\n", size, alignment);
    return (BYTE*)_aligned_malloc(size, alignment);

}

static void open_devices( int dev )
{
    //FT_INVALID_HANDLE
        USHORT uwVID = 0;
        USHORT uwPID = 0;

        GUID DeviceGUID[2] = { 0 };
        memcpy(&DeviceGUID[0], &GUID_DEVINTERFACE_FOR_FTDI_DEVICES, sizeof(GUID));
        ftStatus = FT_Create(&DeviceGUID[0], FT_OPEN_BY_GUID, &ftHandle);

        if (FT_FAILED(ftStatus))
        {
            verbose_msg("open fail");
            return ;
        }

        ftStatus = FT_GetVIDPID(ftHandle, &uwVID, &uwPID);
        if (FT_FAILED(ftStatus))
        {
            FT_Close(ftHandle);
        }
}

static BOOL read_device( UCHAR* buff, ULONG *length )
{
    if (ftStatus != FT_OK)
    {
        return FALSE;
    }
    unsigned long readLen;
    ftStatus = FT_ReadPipe(ftHandle, 0x82, buff, *length, &readLen, NULL);

    BOOL result = TRUE;
    if (FT_FAILED(ftStatus) || readLen != *length)
    {
        result = FALSE;
    }
    *length = readLen;
    return result;
}

static BOOL write_device( UCHAR *data, ULONG *length )
{
    if (ftStatus != FT_OK)
    {
        return FALSE;
    }
    unsigned long writenLen;

    ftStatus = FT_WritePipe(ftHandle, 0x02, data, *length, &writenLen, NULL);

    BOOL result = TRUE;
    if (FT_FAILED(ftStatus) || writenLen != *length)
    {
        result = FALSE;
    }
    return result;
}


int usb_init()
{
    int res = 1;
    open_devices(0);
    h2c_align_mem_tmp = allocate_buffer(0x800000,4096);
    c2h_align_mem_tmp = allocate_buffer(0x800000,4096);
    if(NULL == h2c_align_mem_tmp || NULL == c2h_align_mem_tmp)
        return 0;
    return res;
}
void usb_deinit()
{
    FT_Close(ftHandle);
}
unsigned int h2c_transfer(unsigned int size)
{
    double bd=0;
    double time_sec;
    LARGE_INTEGER start;
    LARGE_INTEGER stop;
    LARGE_INTEGER freq;

    QueryPerformanceFrequency(&freq);
    QueryPerformanceCounter(&start);

    write_device(h2c_align_mem_tmp,&size);
    QueryPerformanceCounter(&stop);
    time_sec = (unsigned long long)(stop.QuadPart - start.QuadPart) / (double)freq.QuadPart;
    bd = ((double)size)/(double)(time_sec)/1024.0/1024.0;

    return (unsigned int)bd;
}
unsigned int c2h_transfer(unsigned int size)
{
    double bd=0;
    double time_sec;
    LARGE_INTEGER start;
    LARGE_INTEGER stop;
    LARGE_INTEGER freq;

    QueryPerformanceFrequency(&freq);
    QueryPerformanceCounter(&start);
    read_device(c2h_align_mem_tmp,&size);
    QueryPerformanceCounter(&stop);
    time_sec = (unsigned long long)(stop.QuadPart - start.QuadPart) / (double)freq.QuadPart;
    bd = ((double)size)/(double)(time_sec)/1024.0/1024.0;

    return (unsigned int)bd;
}


#ifdef __cplusplus
}
#endif

2.2 为了设计绚丽的测试码表控件我们用到了以下代码,这个码表测速也是我们PCIE中用到的程序
myspeed.c
#include "myspeed.h"
#include <QPainter>
#include <qmath.h>
mySpeed::mySpeed(QWidget *parent): QWidget(parent)
{
    m_background = Qt::black;
    m_foreground = Qt::green;

    m_startAngle=60;
    m_endAngle=60;
    m_scaleMajor=10;
    m_minValue=0;
    m_maxValue=1000;
    m_scaleMajor = 10;//分度
    m_scaleMinor = 10;
    m_units = "MB/S";
    m_title = "Speed USB";
    m_precision = 2;
    m_value=0;

    m_updateTimer = new QTimer(this);
    m_updateTimer->setInterval(50);//
    connect(m_updateTimer,SIGNAL(timeout()),this,SLOT(UpdateAngle()));
      m_updateTimer->start();//

     //setWindowFlags(Qt::FramelessWindowHint);//
     //setAttribute(Qt::WA_TranslucentBackground);//
     resize(400,400);
}

mySpeed::~mySpeed()
{

}

void mySpeed::drawCrown(QPainter *painter)
{
    painter->save();
    int radius =100;
    QLinearGradient lg1(0,-radius,0,radius);
    lg1.setColorAt(0.2,Qt::white);
    lg1.setColorAt(1,Qt::black);
    painter->setBrush(lg1);
    painter->drawEllipse(-100, -100, 200 ,200);
    painter->restore();
}

void mySpeed::drawBackground(QPainter *painter)
{
    painter->save();
    painter->setBrush(m_background);
    painter->drawEllipse(-90, -90, 180 ,180);
    painter->restore();
}

void mySpeed::drawScaleNum(QPainter *painter)
{
    painter->save();
    painter->setPen(m_foreground);
    double startRad = (360 - m_startAngle - 90) * (3.14 / 180);
    double deltaRad = (360 - m_startAngle - m_endAngle) * (3.14 / 180) / m_scaleMajor;
    double sina,cosa;
    int x, y;
    QFontMetricsF fm(this->font());
    double w, h, tmpVal;
    QString str;

    for (int i = 0; i <= m_scaleMajor; i++)
    {
        sina = sin(startRad - i * deltaRad);
        cosa = cos(startRad - i * deltaRad);

       tmpVal = 1.0 * i *((m_maxValue - m_minValue) / m_scaleMajor) + m_minValue;

        str = QString( "%1" ).arg(tmpVal);
        w = fm.size(Qt::TextSingleLine,str).width();
        h = fm.size(Qt::TextSingleLine,str).height();
        x = 82 * cosa - w / 2;
        y = -82 * sina + h / 4;
        painter->drawText(x, y, str);
    }
    painter->restore();
}

void mySpeed::drawScale(QPainter *painter)
{
    painter->save();
    painter->rotate(m_startAngle);
    int steps = (m_scaleMajor * m_scaleMinor);
    double angleStep = (360.0 - m_startAngle - m_endAngle) / steps;
    painter->setPen(m_foreground);
    QPen pen = painter->pen();
    for (int i = 0; i <= steps; i++)
    {
        if (i % m_scaleMinor == 0)
        {
            pen.setWidth(1);
            painter->setPen(pen);
            painter->drawLine(0, 62, 0, 72);
        }
        else
        {
            pen.setWidth(0);
            painter->setPen(pen);
            painter->drawLine(0, 67, 0, 72);
        }
        painter->rotate(angleStep);
    }
    painter->restore();
}

void mySpeed::drawTitle(QPainter *painter)
{
    painter->save();
    painter->setPen(m_foreground);
    //painter->setBrush(m_foreground);
    QString str(m_title);
    QFontMetricsF fm(this->font());
    double w = fm.size(Qt::TextSingleLine,str).width();
    painter->drawText(-w / 2, -30, str);
    painter->restore();
}
void mySpeed::drawNumericValue(QPainter *painter)
{
    QString str  =  QString("%1 %2").arg(m_value, 0, 'f', m_precision).arg(m_units);
    QFontMetricsF fm(font());
    double w = fm.size(Qt::TextSingleLine,str).width();
    painter->setPen(m_foreground);
    painter->drawText(-w / 2, 42, str);
}

void mySpeed::drawIndicator(QPainter *painter)
{
    painter->save();
    QPolygon pts;
    pts.setPoints(3, -2,0, 2,0, 0,60);     /* (-2,0)/(2,0)/(0,60) *///

    painter->rotate(m_startAngle);
    double degRotate =  (360.0 - m_startAngle - m_endAngle)/(m_maxValue - m_minValue)*(m_value - m_minValue);


    painter->rotate(degRotate);  //
    QRadialGradient haloGradient(0, 0, 60, 0, 0);  //
    haloGradient.setColorAt(0, QColor(60,60,60));
    haloGradient.setColorAt(1, QColor(160,160,160)); //
    painter->setPen(Qt::white); //
    painter->setBrush(haloGradient);//
    painter->drawConvexPolygon(pts); //
    painter->restore();

    //画中心点
    QColor niceBlue(150, 150, 200);
    QConicalGradient coneGradient(0, 0, -90.0);  //
    coneGradient.setColorAt(0.0, Qt::darkGray);
    coneGradient.setColorAt(0.2, niceBlue);
    coneGradient.setColorAt(0.5, Qt::white);
    coneGradient.setColorAt(1.0, Qt::darkGray);
    painter->setPen(Qt::NoPen);  //
    painter->setBrush(coneGradient);
    painter->drawEllipse(-5, -5, 10, 10);
}


void mySpeed::paintEvent(QPaintEvent *)
{
    QPainter painter (this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.translate(width()/2,height()/2);
    int side = qMin(width(),height());
    painter.scale(side /200.0,side /200.0);
    drawCrown(&painter);
    drawBackground(&painter);
    drawScaleNum(&painter);

    drawScale(&painter);
    drawTitle(&painter);

    drawNumericValue(&painter);
    drawIndicator(&painter);

}

void mySpeed::UpdateAngle()
{
  //m_value += 1;
  //if(m_value > 1000)
  //{
  //   m_value = 0;
  //}

  //m_angle = ((m_angle + 1) % 360);
  update();//
}



2.3 主程序中,我们会定时调用测试函数进行测速,并且把测试的值通过码表程序显示
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "usb_fun.h"

MainWindow::MainWindow(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::MainWindow)
{
    //init ui
    QGridLayout *pUserLayout = new QGridLayout(this);
    for(int i=0;i<2;i++)
    {
        ppmySpeed = new mySpeed;
        i ? ppmySpeed->m_title = "Read Speed" : ppmySpeed->m_title = "Write Speed" ;
        pUserLayout->addWidget(ppmySpeed, 0, i);
     }
    ui->setupUi(this);
    this->setWindowTitle(tr("MSXBO USB3.0 Speed(V1.01)"));
    //init usb
    if(usb_init()<0)
    {
        QMessageBox::information(this,"ERROR","usb init error");
    }
    isc2h_start = false;
    ish2c_start = false;

    c2h_transfer_size = 0x80000;
    h2c_transfer_size = 0x80000;

    m_QTimer_c2h = new QTimer(this);
    m_QTimer_h2c = new QTimer(this);
    connect(m_QTimer_c2h,SIGNAL(timeout()),this,SLOT(c2h_transfer_one_time()));
    connect(m_QTimer_h2c,SIGNAL(timeout()),this,SLOT(h2c_transfer_one_time()));

}

MainWindow::~MainWindow()
{

    usb_deinit();

    delete ui;
}
void MainWindow::c2h_transfer_one_time()
{
    unsigned int bd=0;
    bd = c2h_transfer(c2h_transfer_size);
    ppmySpeed[1]->m_value=bd;
    //QString str = QString("%1").arg(bd);
    //str += "MB/s";
    //ui->lineEdit_c2h->setText(str);
}
void MainWindow::h2c_transfer_one_time()
{
    //transfer h2c
    unsigned int bd=0;
    bd = h2c_transfer(h2c_transfer_size);
    ppmySpeed[0]->m_value=bd;
    //QString str = QString("%1").arg(bd);
    //str += "MB/s";
    //ui->lineEdit_h2c->setText(str);
}

void MainWindow::on_pushButton_h2c_start_clicked()
{
    if(false==ish2c_start)
    {
        ish2c_start = true;
        m_QTimer_h2c->start(100);
        ui->pushButton_h2c_start->setText("STOP");
    }
    else
    {
        ish2c_start = false;
        m_QTimer_h2c->stop();
        ui->pushButton_h2c_start->setText("START");
    }
}

void MainWindow::on_pushButton_c2h_start_clicked()
{
    if(false==isc2h_start)
    {
        isc2h_start = true;
        m_QTimer_c2h->start(100);
        ui->pushButton_c2h_start->setText("STOP");
    }
    else
    {
        isc2h_start = false;
        m_QTimer_c2h->stop();
        ui->pushButton_c2h_start->setText("START");
    }
}

3、测速结果
USB3.0方案FT601Q测速-1.jpg
4、思考
USB3.0和PCIE2.0的串行传输方式非常类似,速度也非常接近,但是FT601Q 没有同时双向处理的能力,我们的程序为什么可以同时进行读写呢?显示不可能同时读写的,实际上你看到的感觉读和写同时在进行实际上,对于FT601Q和FPGA的通信来说,他们一个时刻只能是写或者读,所以如果同时读写,速度至少要降低一半的。我们这里测速只对单次读,和单次写进行了测速。具体的可以去看看代码,理解代码才是王道。


发表评论已发布 3

I'mpossible

发表于 2020-4-1 16:26:02 | 显示全部楼层

感谢分享
回复

使用道具 举报

I'mpossible

发表于 2020-4-1 16:26:02 | 显示全部楼层

感谢分享
回复

使用道具 举报

wx_nRUa4

发表于 2023-1-4 15:38:23 | 显示全部楼层

感谢分享
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则