前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >源码系列:基于 FPGA 的 USB2.0 设计

源码系列:基于 FPGA 的 USB2.0 设计

作者头像
FPGA技术江湖
发布2020-12-29 16:46:43
1.2K0
发布2020-12-29 16:46:43
举报
文章被收录于专栏:FPGA技术江湖FPGA技术江湖

大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分。大侠可以关注FPGA技术江湖,在“闯荡江湖”、"行侠仗义"栏里获取其他感兴趣的资源,或者一起煮酒言欢。

今天给大侠带来基于FPGA的USB2.0设计,附源码,获取源码,请在“FPGA技术江湖”公众号内回复“ USB2.0设计源码”,可获取源码文件。话不多说,上货。

2019年9月4日,USB-IF终于正式公布USB 4规范。它引入了Intel此前捐献给USB推广组织的Thunderbolt雷电协议规范,双链路运行(Two-lane),传输带宽因此提升,与雷电3持平,都是40Gbps。需要注意的是,你想要体验最高传输速度,就必须使用经过认证的全新数据线。USB4保留了良好的兼容性,可向下兼容USB 3.2/3.1/3.0、雷电3。除此之外,USB4将只有USB Type-C一种接口,并支持多种数据、显示协议,包括DisplayPort,可以一起充分利用高速带宽,也支持USB PD供电。

比较遗憾的是,USB4的发布时间暂未公布。值得注意的是,此次发布的USB4是规范,而并非USB4.0。在此之前,USB Implementers Forum(USB-IF)计划取消USB 3.0/3.1命名,统一划归为USB 3.2。其中USB 3.0更名USB 3.2 Gen 1(5Gbps),USB 3.1更名USB 3.2 Gen 2(10Gbps),USB 3.2更名为USB 3.2 Gen 2x2(20Gbps)。

以上就是关于USB标准以及命名的讯息,而今天我们要做设计的是USB2.0,USB2.0的设备是按照2.0的电源标准设计的,设备所需的电流小于500mA,而USB3.0接口可以提供更大的电流,所以用在USB3.0接口是安全的,这就是标准向下兼容的道理。反之,USB3.0的设备,是否能够在USB2.0接口使用,就要查看设备的说明书。通常的电源都是电压源,设备与电源的电压必须相等,而电源的输出电流要大于设备的电流,即电源功率要大于设备功率。

现在大部分USB设备(比如USB接口的鼠标、键盘、闪存、U盘等等)都是采用了USB通用驱动,而你的系统有USB通用驱动的话(比如XP就内建了USB通用驱动)就能用。而有些USB设备是需要特殊驱动的,比如某些手机,连接到电脑的USB口,是需要安装驱动才能使用的。下面我们一起动手做一做,了解一下如何设计。

设计原理

USB(Universal Serial Bus2.0,通用串行总线)是一种应用在计算机领域的新型接口技术。USB接口具有传输速度更快,支持热插拔以及连接多个设备的特点。目前已经在各类外部设备中广泛的被采用。USB接口有三种:USB1.1,USB2.0和USB3.2。理论上USB1.1的传输速度可以达到12Mbps,而USB2.0则可以达到速度480Mbps,并且可以向下兼容USB1.1。

本次设计我们选择一款开发设备,一块廉价的开发板,其中的USB芯片是Cypress的FX2LP系列中的CY7C68013A代,详细的介绍大家可以去Cypress的官网查询。下面简述一下设计思路。

FX2的设计架构如下图,内嵌480MBit/s的收发器,锁相环PLL,串行接口引擎SIE——集成了整个USB 2.0协议的物理层。为适应USB 2.0的480MBit/s的速率,FIFO端点可配置成2,3,4个缓冲区。配置用的是“软配置”——USB固件可由USB总线下载,片上不需集成ROM。拥有四个FIFO接口,可工作在内部或外部时钟下。端点和FIFO接口的应用使外部逻辑和USB总线可高速连接。

基于FX2LP的USB开发,包括三部分:固件程序、驱动、上位机软件。

固件程序我们在kiil中写出来,然后配置到我们的芯片中,固件的开发对于FPGA工程师来说是不用写的,是其他工程师配置好芯片后我们直接拿来使用,其主要的配置过程如下图。

先上电复位,然后初始化寄存器变量,然后调用配置函数,打开中断后,判断是否接受到了配置包,如果接收到了就调用TD_POLL()函数,这个是函数是不停的执行扫描端点等。然后判断芯片是否挂起,如果挂起就叫醒芯片,如果没有就一直调用TD_POLL函数,这样就完成所需要的配置。

这里的项目设计要求是要把FX2配置成从FIFO的模式, 配置为单片机工作时钟24M,端点2输出,字节1024,端点6输入,字节1024,信号全设置为低电平有效等。模块驱动时钟配置成内部输出时钟,也就是让FX2给项目设计当作时钟源,输出一个最大的配置时钟48M的时钟。FX2的数据存储区叫端点,有512、1024字节两个存储大小之分。

从FIFO说明

当有一个与FX2芯片相连的外部逻辑只需要利用FX2做为一个USB 2.0接口而实现与主机的高速通讯,而它本身又能够提供满足Slave FIFO要求的传输时序,可以做为Slave FIFO主控制器时,即可考虑用此传输方式。

Slave FIFO传输的示意图如下:

在这种方式下,FX2内嵌的8051固件的功能只是配置Slave FIFO相关的寄存器以及控制FX2何时工作在Slave FIFO模式下。一旦8051固件将相关的寄存器配置完毕,且使自身工作在Slave FIFO模式下后,外部逻辑(如FPGA)即可按照Slave FIFO的传输时序,高速与主机进行通讯,而在通讯过程中不需要8051固件的参与。

FX2系列的有3种封装方式,我们我的开发板用的是56引脚的封装方式的电路图,其电路图如下所示:

端口介绍

IFCLK:FX2输出的时钟,可做为通讯的同步时钟。

SLCS:FIFO的片选信号,外部逻辑控制,当SLCS输出高时,不可进行数据传输。

SLOE:FIFO输出使能,外部逻辑控制,当SLOE无效时,数据线不输出有效数据。

SLRD:FIFO读信号,外部逻辑控制,同步读时,FIFO指针在SLRD有效时的每个IFCLK的上升沿递增。

SLWR:FIFO写信号,外部逻辑控制,同步写时,在SLWR有效时的每个IFCLK的上升沿时数据被写入,FIFO指针递增。

FD[15:0]:数据线。

FIFOADR[1:0]:选择四个FIFO端点的地址线,外部逻辑控制。

FLAG A,B,C端点的空满标志位。

开发驱动大家可以在网上找,然后根据自己系统装上合适的驱动。上位机软件用的是官方的开发工具,只有如下的安装包,然后安装第一个和第二个就好了。

设计代码

总模块usb:

代码语言:javascript
复制
module  usb(pi_clk, clk, pi_rst_n, pi_usb_flagb, pi_usb_flagc, pio_usb_data, 
  po_usb_oe_n, po_usb_wr_n, po_usb_address, po_usb_rd_n, led);

  input clk;
  input pi_clk;
  input pi_rst_n;
  input pi_usb_flagb;          
  input pi_usb_flagc;          
  inout [15:0] pio_usb_data;

  output  po_usb_oe_n;          
  output  po_usb_wr_n;          
  output  po_usb_rd_n;          
  output  [1:0] po_usb_address;  
  output led;
  wire po_rst_n;

  delay delay_dut(
    .pi_clk(pi_clk),
    .pi_rst_n(pi_rst_n), 
    .po_rst_n(po_rst_n)
  );

  usb_wr wr_dut(
    .pi_clk(pi_clk),
    .pi_rst_n(po_rst_n),
    .pi_usb_flagb(pi_usb_flagb), 
    .pi_usb_flagc(pi_usb_flagc), 
    .pio_usb_data(pio_usb_data), 
    .po_usb_oe_n(po_usb_oe_n), 
    .po_usb_wr_n(po_usb_wr_n),
    .po_usb_address(po_usb_address),
    .po_usb_rd_n(po_usb_rd_n)
  );

  my_pll  my_pll_inst (
    .areset ( ~ pi_rst_n ),
    .inclk0 ( clk ),
    .c0 ( c0_sig ),
    .locked ( locked_sig )
  );
  
endmodule

读模块usb_rd:

代码语言:javascript
复制
module usb_rd(pi_clk, pi_rst_n, pi_usb_flagb, pi_usb_flagc, pio_usb_data, 
  po_usb_oe_n, po_usb_rd_n, po_usb_address, po_usb_wr_n, led);

  input pi_clk;
  input pi_rst_n;
  input pi_usb_flagb;              //端点2标志信号
  input pi_usb_flagc;                    //端点6标志信号
  inout [15:0]  pio_usb_data;            //输入输出端口

  output reg po_usb_oe_n;                //读标志信号
  output reg po_usb_rd_n;                //写使能
  output reg po_usb_wr_n;                //读使能
  output reg [1:0] po_usb_address;       //端点地址选择
  output reg led;

  reg [15:0] temp_data;
  reg [9:0] count;
  reg [2:0] state;

  assign pio_usb_data = (state == 10) ? 1 : 16'hzzzz;    //读数据,可以一直释放数据总线的控制权

  always @ (posedge pi_clk or negedge pi_rst_n)
    if(!pi_rst_n)
      begin
        state <= 0;
        po_usb_oe_n <= 1;
        po_usb_rd_n  <= 1;
        count <= 0;
        po_usb_wr_n <= 1;
        temp_data <= 0;
      end
    else
      case (state)  
        0  :  state <= 1;

        1  :  begin
              po_usb_address <= 2'b00;  //地址指向端点2
              state <= 2;          
            end

        2  :  if(!pi_usb_flagb)      //判断端点2已经满
              begin
                po_usb_rd_n  <= 0;
                state <= 3;
                po_usb_oe_n <= 0;
              end
            else
              state <= 2;

        3  :  begin  
            if(count < 512 - 1)      //接收1024字节的数据
              begin
                count <= count + 1'b1;
              end
            else
              begin
                count <= 0;
                state <= 4;
              end
            if (count == 2)
              begin
                temp_data <= pio_usb_data;
              end
            end

        4  :  begin
              po_usb_rd_n  <= 1;
              po_usb_oe_n <= 1;
              state <= 0;
            end

        default:  state <= 0;
      endcase

  always @ (*)
    if(!pi_rst_n)
      led <= 1;
    else if (temp_data == 16'h33ff)
      led <= 0;

endmodule

写模块 usb_wr:

代码语言:javascript
复制
module usb_wr(pi_clk, pi_rst_n, pi_usb_flagb, pi_usb_flagc, pio_usb_data, 
  po_usb_oe_n, po_usb_wr_n, po_usb_address, po_usb_rd_n);

  input pi_clk;
  input pi_rst_n;
  input pi_usb_flagb;            //端点2标志信号
  input pi_usb_flagc;            //端点6标志信号
  inout [15:0] pio_usb_data;            //输入输出端口

  output reg po_usb_oe_n;          //读标志信号
  output reg po_usb_wr_n;          //写使能
  output reg po_usb_rd_n;          //读使能
  output reg [1:0] po_usb_address;    //端点地址选择

  reg [15:0] temp_data;
  reg [2:0] state;

  //在状态的3,拿回数据总线控制全,给写入数据
  assign pio_usb_data = (state == 3) ? temp_data : 16'hzzzz;

  always @ (posedge pi_clk or negedge pi_rst_n)
    if(!pi_rst_n)
      begin
        state <= 0;
        po_usb_oe_n <= 1;
        po_usb_wr_n  <= 1;
        temp_data <= 0;
        po_usb_rd_n <= 1;
      end
    else
      case (state)  
        0  :  state <= 1;

        1  :  begin
              po_usb_address <= 2'b10;  //地址指向端点6
              state <= 2;          
            end

        2  :  if(!pi_usb_flagc)      //判断端点6已经空
              begin
                po_usb_wr_n  <= 0;
                state <= 3;
              end
            else
              state <= 2;

        3  :  if(temp_data < 512 - 1)        //发送1024字节的数据
              temp_data <= temp_data + 1'b1;
            else
              begin
                temp_data <= 0;
                state <= 4;
              end

        4  :  begin
              po_usb_wr_n  <= 1;
              state <= 0;
            end

        default:  state <= 0;
      endcase

endmodule

延时模块 delay:

代码语言:javascript
复制
module delay(pi_clk, pi_rst_n, po_rst_n);

  input pi_clk;
  input pi_rst_n;

  output reg po_rst_n;

  parameter T1ms = 50000;
  reg [15:0] count;

  always @ (posedge pi_clk or negedge pi_rst_n)
    if(!pi_rst_n)
      begin
        count <= 0;
        po_rst_n <= 0;
      end
    else
      begin
        if(count < T1ms - 1)  
          count <= count + 1;
        else
          begin
            count <= count;
            po_rst_n <= 1;
          end
      end

endmodule

上位机测试

安装好驱动和下载的上位机软件,然后在下面的界面中,点击”LGEEPROM”按钮,下载我们写好的的.IIC固件。

然后在下面的页面中会出现先选择other endpt xfers选项中会出现4个端点,然后选择写入的端点或者读的端点执行读写操作。

写的端点是6端点,选择这个端点,写入端点是1024个字节,这里设置的是512字节,也就是写入2次就可以写满了,如下图,和我们编写的代码中写入数据值是一样的。

读操作也就是要读端点2,我们先要给端点一个数,然后才能读我们的端点,我们写入图中显示的数,因为我们设计的是读出的数,如果第三个数位33ff ,就让我们的灯亮,值得一说的是,我们上位机显示的时候是把低位显示到了前面,高位显示到了后面,我们一个包是1024字节,后面的数自动补零,读出数据后可以看到我们的led灯亮,验证出我们的设计正确。

以上就是我们的基于FPGA的usb2.0设计了,希望能给各位大侠起到参考学习作用,此次设计到此结束,有缘再见,告辞。

END

后续会持续更新,带来Vivado、 ISE、Quartus II 、candence等安装相关设计教程,学习资源、项目资源、好文推荐等,希望大侠持续关注。

大侠们,江湖偌大,继续闯荡,愿一切安好,有缘再见!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-03-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FPGA技术江湖 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据保险箱
数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档