一天一个设计实例-万字USB协议及FPGA+USB PHY通信实现

USB官方地址:https://www.usb.org/document-library/usb-20-specification

USB 即“Universal Serial Bus ”,中文名称为通用串行总线。。USB接口具有传输速度更快,支持热插拔以及连接多个设备的特点。目前已经在各类外部设备中广泛的被采用。目前USB接口有三种:USB1.1和USB2.0,以及近年来出现的USB3.0。理论上USB1.1的传输速度可以达到12Mbps,而USB2.0则可以达到速度480Mbps,并且可以向下兼容USB1.1。

主流版本区别:

速度对比

USB2.0高速规范把传输速度提高到了480Mbps,也就是60MByte/s,是USB 1.1设备的40倍

USB1.1,支持全速12Mbps和低速1.5Mbps。

USB2.0,兼容1.1,增加了高速480Mbps。

USB3.0,兼容2.0,增加了超速5Gbps。

硬件结构

USB采用四线电缆,其中两根是用来传送数据的串行通道,另两根为下游(Downstream)设备提供电源,对于高速且需要高带宽的外设,USB以全速12Mbps的传输数据;对于低速外设,USB则以1.5Mbps的传输速率来传输数据。USB总线会根据外设情况在两种传输模式中自动地动态转换。USB是基于令牌的总线。类似于令牌环网络或FDDI基于令牌的总线。USB主控制器广播令牌,总线上设备检测令牌中的地址是否与自身相符,通过接收或发送数据给主机来响应。USB通过支持悬挂/恢复操作来管理USB总线电源。USB系统采用级联星型拓扑,该拓扑由三个基本部分组成:主机(Host),集线器(Hub)和功能设备。

USB3.0 —— 也被认为是SuperSpeedUSB——为那些与PC或音频/高频设备相连接的各种设备提供了一个标准接口。

计算机内只有安装USB3.0相关的硬件设备后才可以使用USB3.0相关的功能!从键盘到高吞吐量磁盘驱动器,各种器件都能够采用这种低成本接口进行平稳运行的即插即用连接,用户基本不用花太多心思在上面。新的USB 3.0在保持与USB 2.0的兼容性的同时,还提供了下面的几项增强功能:

● 极大提高了带宽——高达5Gbps全双工(USB2.0则为480Mbps半双工)

● 实现了更好的电源管理

● 能够使主机为器件提供更多的功率,从而实现USB——充电电池、LED照明和迷你风扇等应用。

● 能够使主机更快地识别器件

● 新的协议使得数据处理的效率更高

传输速率

这款新的超高速接口的实际传输速率大约是3.2Gbps(即409.6MB/S)。理论上的最高速率是5.0Gbps(即640MB/S)。

数据传输

USB3.0 引入全双工数据传输。5根线路中2根用来发送数据,另2根用来接收数据,还有1根是地线。也就是说,USB 3.0可以同步全速地进行读写操作。以前的USB版本并不支持全双工数据传输。

USB 2.0基于半双工二线制总线,只能提供单向数据流传输,而USB 3.0采用了对偶单纯形四线制差分信号线,故而支持双向并发数据流传输。除此之外,USB 3.0还引入了新的电源管理机制,支持待机、休眠和暂停等状态。

电源

USB 3.0标准要求USB3.0接口供电能力为1A,而USB 2.0为0.5A。

1.1.1USB协议的简单介绍

在用户的角度,使用USB设备是非常方便的。但是,在开发人员的角度,可是谈USB色变。

这是因为,USB硬件接口非常简单,导致上层协议非常复杂。

图2‑60 USB通信协议

由上图所示,自下而上,USB硬件接口提供物理的通信链路,EHCI等主机控制器负责调度各端点的数据流,USB驱动协助操作系统和USB设备交换数据同时能烧写固件到USB设备,GUI则让用户轻松操纵USB设备,服务器云端数据库提供各种各样的USB驱动。

USB协议,是一个主从式的协议,所有的通信都由主机发起,设备不能发起。下图,展示了USB家族的主要成员。

图2‑61 USB家族成员

USB Host,即USB主机,一般是PC机。

USB Device,即USB设备,有USB键盘、U盘等,而USB Hub集线器是一种特殊的USB设备。

USB OTG,OTG即On-The-Go,同时具备USB主机和USB设备的功能,数码相机就带有OTG的功能。

以下,细说USB2.0的方方面面。

2.4.1.1 USB硬件接口

一、接插件

图2‑62 USB插座

由上图所示,USB座有很多种类型,主要分为:公/母、直插、贴片、单/双层、扁/方,等几种类型。

同时可以看到它只有四根线,分别是VBUS、DP(或D+)、DM(或D-)、GND。此外,还有屏蔽罩,一般屏蔽罩是通过一个100nF的电容连到GND的。

二、连接线

USB线,也分几种:公对公/公对母,带磁环,带屏蔽等。

注意:USB线不能太长,否则设备可能无法正确工作。

三、热插拔

热插拔。它能让你在不断电的情况下,插拔USB设备。

如果要支持热插拔,必须在VBUS、DP、DM三根线上,加上过压、过流保护,在未上电时DP和DM要保持高阻态。

另外,要利用金手指长短来控制上电顺序,插入时首先让电源线接通,让电源稳定之后,再接通信号线,拔出时顺序正好相反。这样有效防护接口被损坏。

图2‑63 USB接口“金手指”

四、上下拉电阻。

一般,在USB主机的DP、DM都接了下拉15K欧电阻。

对于低速设备,DM接了上拉1.5K欧电阻。

对于全速设备,DP接了上拉1.5K欧电阻。

对于高速设备,一接在主机的时候DP接了上拉1.5K欧电阻,后面经过握手协议之后,DP上断开1.5K欧电阻。

五、ESD防护。

一般支持USB接口的芯片ESD防护做得不够好,这个时候要另外加ESD芯片。如下图的USBLC6。

图2‑64 USB接口的保护电路

六、端接。

端接,目的是做阻抗匹配,特别是高速480Mbps对信号质量要求较高。

一般使用串联端接,在DP、DM上串联22欧电阻。

七、UTMI+和ULPI接口。

有一些不带USB接口的处理芯片,比如,51单片机、FPGA等。它们都不能直接处理USB总线上的DP、DM的差模信号。

所以,芯片厂商开发了一些USB PHY芯片,可以把DP、DM上的差模信号转成共模信号。

其中,成为业界标准的接口,有UTMI+和ULPI。

如下图所示,UTMI+接口一共有31根信号线。

图2‑65 UTMI+接口

如下图所示,ULPI接口一共有12根信号线。

图2‑66 ULPI接口

UTMI+和ULPI,在功能上是等价的。

UTMI+和ULPI区别在于,ULPI需要读写寄存器,而UTMI+只需直接拉高或拉低信号线。

如下图所示,ULPI是在UTMI+的基础上封装了一层。

图2‑67 ULPI和UTMI+关系

显然,UTMI+接口的信号线太多,占用大量的PCB空间,才需要推出ULPI接口。

市场上,大多数的USB PHY芯片都是ULPI接口。

八、检测USB设备速度的流程描述。

对于低速设备,在DM引脚上拉1.5K。

对于全速设备,在DP引脚上拉1.5K。

对于高速设备,则需要握手协议。

a、USB主机检测到全速设备,即:DP引脚上拉1.5K。

b、USB主机复位总线,即向总线发送SE0,此SE0持续时间不得小于2.5us。

c、高速设备检测到SE0持续时间不小于2.5us后,向总线发送Chirp K信号,此Chirp信号持续时间不小于1ms且不大于7ms。

d、总线回复到SE0状态。如果USB主机支持高速模式,则必须在Chipr K信号结束后100us内做出响应。

e、USB主机在Chipr K信号结束后100us内做出响应,向总线发送连续的Chirp K/J对,每个Chirp K信号或Chirp J信号的宽度不小于40us且不大于60us,每2个相邻的Chirp K和Chirp J信号之间的间隔不应大于2.5us。

f、高速设备在检测到连续的最少3对Chirp K/J对后,在500us内必须断开DP上的上拉电阻,并连接D+和D-上的对地高速端接电阻,完成高速握手,进入高速传输模式。

高速模式的握手协议,反应在USB3300的时序,如下图所示。

图2‑68 USB3300的时序

EHCI官方手册:https://www.intel.com/content/www/us/en/products/docs/io/universal-serial-bus/ehci-specification.html

USB2.0官方手册:https://www.usb.org/usb-charger-pd

USB设备类手册:https://www.usb.org/documents?search=&type%5B0%5D=55&items_per_page=50

USB3.0官方手册:http://usb3.com/

USB3.1官方手册:https://www.usb.org/documents

1.1.2USB2.0协议的FPGA应用

用FPGA实现USB协议的工作量很大,而且复杂度很高,一般应用时很少直接使用FPGA实现USB协议,所以本次应用USB时是利用USB2.0的PHY芯片Cypress厂家的CY7C68013芯片。下面在针对在使用过程中需要用到的几个概念进行解释,如下。

2.4.2.1 USB协议简要概念

USB2.0设备组成

每一个USB设备由一个或多个配置来控制其行为,使用多配置原因是对操作系统的支持;一个配置是由接口(Interface)组成;接口则是由管道(Pipe)组成;管道是和USB设备的端点(Endpoint)对应,端点都是输入输出成对的。在固件编程中,USB设备、配置、接口和管道都来描述符来报告其属性。

一个端点(Endpoint)建立一个管道。管道的端点总是成对出现,即In Endpoint和Out Endpoint。端点0默认为控制管道,其它端点可以配置成数据管道。一个具体的端点,只能工作在一种传输模式下。

In Endpoint:由device向Host发送数据的端点。

Out Endpoint:由Host向device发送数据的端点。

USB2.0传输速度

USB1.0和USB1.1版本中,只支持1.5Mb/s的低速(low-speed)模式和12Mb/s的全速模式。在USB2.0种,又加入了速度更快(480Mb/s)的高速模式。而USB3.0的最大传输带宽高达5.0Gbps(625MB/s)。

USB可扩展设备

USB1.1规定最多为4层,USB2.0规定最多为6层。理论上,一个USB主控制器最多可接127个设备,这是因为协议规定每个USB设备具有一个7 bit的地址(取值范围0~127),而地址0是保留给未初始化的设备使用。

USB传输类型

虽然USB定义了数据在总线上传输的基本单位是包,但是我们还不能随意地使用包来传输数据,必须按照一定的关系把这些不同的包组织成事务才能传输数据。

事务通常由两个或者三个包组成:令牌包,数据包和握手包。

令牌包用来启动一个事务,总是由主机发送。

数据包传送数据,可以从主机到设备,也可以从设备到主机,方向由令牌包来制定。

握手包的发送者通常为数据接收者,当数据接收正确后,发送握手包。设备也可以使用NAK握手包来表示数据还未准备好。

USB协议规定了4种传输类型:批量传输、等时传输(同步传输),中断传输和控制传输。其中,批量传输、等时传输、中断传输每传输一次数据都是一个事务;控制传输包括三个过程,建立过程和状态过程分别是一个事务,数据过程则可能包含多个事务。4种数据传输的相关特性(仅限USB1.1协议)如下表。

表2‑17 USB4种数据传输的相关特性

传输模式

中断传输

Interrupt

批量传输

Bulk

等待传输

ISO

控制传输

Control

传输速率/Mbps

12(1.5 低速)

12

12

1.5/12

数据的最大长度/Byte

1~64(1~8 低速)

8/16/32/64

1~1023

1~64(1~8 低速)

数据周期性

没有

没有

发送错误重传

应用设备

鼠标键盘

打印机

语音

-

可得到的最大宽度/Mbps

6.762(0.051 低速)

9.728

10.240

-

批量传输使用批量事务传输数据。一个批量事务由三个阶段:令牌包阶段,数据包阶段和握手包阶段。每个阶段都是一个独立的包。批量传输通常用于数据量大,对数据的实时性要求不高的场合。

USB2.0 数据帧

USB2.0和USB1.1规范的最大不同就是数据帧。在USB1.1规范中,USB数据采用每毫秒一个数据帧的方式进行数据传输,在毫秒数据帧的开始,USB主机首先产生帧开始(SOF)数据包,并传输当前数据帧号,后面是传输数据。对于USB2.0规范,为了支持480Mbps高速传输速度,USB2.0提出了微帧的概念,每毫秒数据帧又包含8个微帧。

USB2.0 端点缓冲区

表2‑18 USB2.0端点缓冲区

传输类型

USB1.1数据包大小

USB2.0数据包大小

控制传输

8,16,32,64

64

批量传输

8,16,32,64

512

中断传输

1~64

1024

等时传输

1023

1024

2.4.2.2  CY7C68013与FPGA

图2‑69 CY7C68013内部构造

CY7C68013特点:

(一)支持USB2.0,内部包括USB2.0收发器、串行接口引擎(SIE)以及增强型51内核;

(二)灵活配置,可“软配置”RAM,取代了传统51的RAM和ROM,程序可以通过以下方式下载:通过USB口下载;通过外部E2PROM装载;外界存储设备(仅128引脚支持)

(三)模式灵活,可设置为主从模式,主模式下可对外部FIFO、存储器、ATAn接口设备进行高速读写操作,从模式下外部主控器(例如DSP、MCU)可把GPIF端口当作FIFO进行高速读写操作。

(四)支持与外设通过并行8位或者16位总线传输

官方资料AN61345 提供了一个示例项目,用以通过从设备 FIFO 接口将 FX2LP 连接至 FPGA。示例实现中描述的接口为各个应用执行高速度的 USB 连接事项,如数据采集、工业控制和监控以及图像处理。

图2‑70 官方DEMO示例

可以通过两个不同的模式将 FX2LP 连接至 FPGA。这两个模式分别为通用可编程接口( GPIF)模式和从设备 FIFO模式。

官方硬件连接图如下:

图2‑71 官方推荐硬件连接图

表2‑19 硬件连接中引脚说明

引脚名称

说明

SLRD

SLRD 引脚应由主设备激活,用以从 FIFO 读取数据。

SLWR

SLWR 引脚应该由主设备激活,以将数据写入到 FIFO 内。

SLOE

是指 FIFO 输出驱动器的使能信号。

FIFOADR[1:0]

这些信号用于选择有效的端点。

FD[15:0]

16 位数据总线

FLAGA/FLAGB/FLAGC/FLAGD

FIFO 使用这些标志来表示各种状态(满、空、可编程)。

IFCLK

是指与从设备 FIFO 接口同步的时钟。在本应用笔记所提供的设计中,该时钟的频率被配置为 48 MHz,并由连接至 FX2LP 的 FPGA 生成。

CLKOUT

FX2LP 的 CLKOUT 引脚可以提供的时钟频率分别为 12、 24 或 48 MHz

在使用过程中或者硬件设计时尽量以官方说明为主,尤其一些引脚最好都连接上,这样在使用过程中以应对后期不同应用。

2.4.2.3  CY7C68013读写时序

Ø同步Slave FIFO写

同步Slave FIFO写的标准连接图如下:

图2‑72 同步Slave FIFO端口连接示意图

同步Slave FIFO写的标准时序如下:

IDLE:当写事件发生时,进状态1;

状态1:使FIFOADR[1:0]指向IN FIFO,进状态2;

状态2:如FIFO满,在本状态等待,否则进状态3;

状态3:驱动数据到数据线上,使SLWR有效,持续一个IFCLK周期,进状态4;

状态4:如需传输更多的数,进状态2,否则进状态IDLE。

状态跳转示意图如下:

图2‑73 同步Slave FIFO状态转移示意图

几种情况的时序图示意如下(FULL,EMPTY,SLWR,PKTEND均假定低有效):

图2‑74 同步Slave FIFO时序图

图示FIFO中本来没有数据,外部逻辑写入第一个数据时的情况。

图2‑75同步Slave FIFO写入第一个数据时序图

图示假定FX2设定包大小为512字节,外部逻辑向FIFO端点中写入的数据达512字节时的情况。此时FX2硬件自动将已写入的512字节打成一包准备进行传输,这个动作就和在普通传输中,FX2固件向FIFO端点中写入512字节后,把512这个数写入EPxBC中一样,只不过这个过程是由硬件自动完成的。在这里可以看出“FX2固件不参与数据传输过程”的含义了。外部逻辑只须按上面的时序图所示的时序向FIFO端点中一个一个字节(或字)地写数,写到一定数量,FX2硬件自动将数据打包传输,这一切均不需固件的参与,由此实现高速数据传输。

图2‑76 同步Slave FIFO端点被写满时的情况

下图是同步Slave FIFO写入时序:

图2‑77 同步Slave FIFO写入时序

逻辑时序设计中,数据应该在IFCLK上升沿写入。同时注意SLWR、DATA之间的时序关系。

Ø同步Slave FIFO读:

同步Slave FIFO读的标准连接图如下:

图2‑78 同步Slave FIFO读的标准连接图

同步Slave FIFO读的标准时序如下:

IDLE:当读事件发生时,进状态1;

状态1:使FIFOADR[1:0]指向OUT FIFO,进状态2;

状态2:使SLOE有效,如FIFO空,在本状态等待,否则进状态3;

状态3:从数据线上读数,使SLRD有效,持续一个IFCLK周期,以递增FIFO读指针,进状态4;

状态4:如需传输更多的数,进状态2,否则进状态IDLE。

状态跳转示意图如下:

图2‑79 同步Slave FIFO读状态跳转示意图

单个和突发读取时序:

图2‑80 同步SLAVE FIFO 同步读取序列和时序图

图2‑81 同步Slave FIFO同步事件序列图

从上图所示,FPGA应该在IFCLK上升沿处采集数据。

Ø异步Slave FIFO写:

异步Slave FIFO写的标准连接图如下:

图2‑82 异步Slave FIFO写的标准连接图

异步Slave FIFO写的标准时序如下:

IDLE:当写事件发生时,进状态1;

状态1:使FIFOADR[1:0]指向IN FIFO,进状态2;

状态2:如FIFO满,在本状态等待,否则进状态3;

状态3:驱动数据到数据线上,使SLWR有效,再无效,以使FIFO写指针递增,进状态4;

状态4:如需传输更多的数,进状态2,否则进状态IDLE。

状态跳转示意图如下:

图2‑83 异步Slave FIFO状态跳转示意图

几种情况的时序图示意如下(FULL,EMPTY,SLWR,PKTEND均假定低有效):

图2‑84 异步Slave FIFO几种情况的时序图示意

图示FIFO中本来没有数据,外部逻辑写入第一个数据时的情况。

图2‑85 Slave FIFO 异步写时序

数据必须在SLWR解除沿前Tsfd出现在总线上,当SLWR上升沿时,数据将被写进FIFO中,同时更新FIFO的指针。

Ø异步Slave FIFO读:

异步Slave FIFO读的标准连接图如下:

图2‑86 异步Slave FIFO读的标准连接图

异步Slave FIFO读的标准时序如下:

IDLE:当读事件发生时,进状态1;

状态1:使FIFOADR[1:0]指向OUT FIFO,进状态2;

状态2:如FIFO空,在本状态等待,否则进状态3;

状态3:使SLOE有效,使SLRD有效,从数据线上读数,再使SLRD无效,,以递增FIFO读指针,再使SLOE无效,进状态4;

状态4:如需传输更多的数,进状态2,否则进状态IDLE。

状态跳转示意图如下:

图2‑87 异步Slave FIFO读状态跳转示意图

几种情况的时序图示意如下(FULL,EMPTY,SLRD,SLOE均假定低有效):

图2‑88 异步Slave FIFO读几种情况的时序图示意

图示正常情况时的时序。

图2‑89 Slave FIFO异步读时序

Data总线在SLRD下降沿时被触发更新,有一定时间的延迟,所以采用异步读取的方式,应该在SLRD上升沿处采集数据。

Ø同步与异步读写的引脚差异

表2‑20 同步与异步读写的引脚差异

同步读

异步读

同步写

异步写

IFCLK

IFCLK

FIFOADR[1:0]

FIFOADR[1:0]

FIFOADR[1:0]

FIFOADR[1:0]

EMPTY

EMPTY

FULL

FULL

SLOE

SLOE

SLWR

SLWR

SLRD

SLRD

FD[15:0]

FD[15:0]

FD[15:0]

FD[15:0]

PKTEND

PKTEND

SLCS

SLCS

SLCS

SLCS

2.4.2.4  CY7C68013驱动及下载

CysuiteUSB_3_4_7软件下载链接:

http://www.waveshare.net/w/upload/7/79/CySuiteUSB_3_4_7_B204.7z

下载完成后解压安装,详见下图:

http://www.waveshare.net/study/data/attachment/portal/201609/22/113337ybl0lo6e8hjshuew.png

点击NEXT。

1、安装完CysuiteUSB_3_4_7,我们接入模块,在未加载驱动前,状态如下:

2、我们打开软件,看看可以怎么加载驱动:

3、这里面涉及到模块的VID和PID,具体怎么看呢,详见下图:

4、接着就是修改驱动文件上的VID和PID了,我们打开安装路径下的驱动目录,如下:

5、找到适合自己操作系统的驱动文件,比如我的是win7 64位,那么,进入../wlh/x64,以记事本方式打开cyusb.inf,然后修改替换并保存:

补充说明:把上图对应的操作系统前的“;”注释去掉,再保存

6、重新加载驱动文件

7、通常情况,驱动到这里加载就能正常识别了。But...,部分64位的操作系统有个强制数字签名比较烦球(比如我的这个机子上的就有这种情况),那么可能会有类似下面的情况出来:

8、那么大家伙可以重启按F8,临时禁用数字签名,不知道怎么禁用的同学可以百度下(jingyan.baidu.com/article/3d69c5518ca3fdf0cf02d7c9.html)

9、禁用后重启,识别正常,打开软件,正常识别!

http://www.waveshare.net/study/data/attachment/portal/201609/22/113337ybl0lo6e8hjshuew.png

关于 Win8 哈希值不在文件中的安装错误解决方案:

在 Win8-64 系统下安装 USB驱动出现了如下问题:

由于 Win8 强制要求驱动程序签名,而Cypress 的驱动未经过签名,因此驱动安装不成功。出现这种情况的,网上有很多解决方案,这里例举网友晴天/zt小猪在 Win8-64 位操作系统上测试验证的解决方案,如下:

1. 按快捷键 win+R 打开运行命令

2. (请先看完后面的再操作!!)运行输入 shutdown.exe /r /o /f /t 00

3. 点击确定

4. 系统将重启

5. 重启后点击疑难解答

6. 选择高级选项

7. 选择 windows 开机设置

8. 点击重启

9. 选择高级选项

10.选择“禁用强制驱动程序”(F7 键)

11. 然后装驱动就可以了

完成以上操作后, 再次安装 USB 驱动程序,成功的界面如下所示:

烧写模块固件:(说明此方法仅适用于擦除了EEPROM的模块,如何擦除详见用户手册)

把模块通过USB口连接到电脑。

打开软件CyConsole,Device选择模块,然后点击“Lg EEPROM”,

打开Slave_FIFO.iic文件,详细操作如下。

按模块上的RST按钮,查看电脑“设备管理器”中能否正确识别到,有则固件下载成功。

CY7C68013想要正常工作,就得给他编写好相应的固件,然后再固化到其内部,当然PC也是需要安装相应的驱动的,固件的编写主要是确定IN和OUT端点,以及一些标志信号。

固件只需要改这些参数,这里我都写好了,大家就不需要再改了,很容易看出我设置的时钟是48MHz,,然后设置

EP2为OUT端点,512字节,4缓冲,bulk (注意OUT,IN都是相对PC来说的,OUT表示PC--->cy7c68013a,IN则相反)

EP6为IN端点,512字节,4缓冲,bulk

flag_a 为EP2的EF,也就是空标志信号,为低时表示空,也就是没有数据过来,为高则表示有数据来了

flag_d 为EP6的FF,也就是满标志信号,为低时表示写满了,这时再去写就是无效写了,为高则表示没有写满,可以继续写。

写好的固件所在文件夹:固件源码什么的都在Firmware文件中

按照上诉步骤进行烧写就可以了,接下来就是FPGA端进行FIFO的读写。

2.4.2.5  FPGA驱动CY7C68013

驱动CY7C68013比较简单,因为大部分协议都由外围PHY完成,所以只需要对FIFO进行操作即可,详细的实现原理可以参考后面章节。

代码2‑33 FPGA驱动CY7C68013代码

1.//****************************************************************************//

2.//# @Author: 碎碎思

3.//# @Date:   2019-08-14 20:31:26

4.//# @Last Modified by:   zlk

5.//# @WeChat Official Account: OpenFPGA

6.//# @Last Modified time: 2019-08-14 20:43:45

7.//# Description:

8.//# @Modification History: 2019-08-14 20:43:45

9.//# Date                By             Version             Change Description:

10.//# ========================================================================= #

11.//# 2019-08-14 20:43:45

12.//# ========================================================================= #

13.//# |                                                                       | #

14.//# |                                OpenFPGA                               | #

15.//****************************************************************************//

16.//flag_a,flag_d都是默认低电平有效的,flag_a是ep2的EF信号,flag_d是ep6的FF信号

17.//flag_a为低时表示ep2里的FIFO为空,也就是没有数据过来,为高则表示非空,表示有数据过来了

18.//flag_d为低时表示ep6里的FIFO写满了,这时cypress芯片就会自己打包数据然后去发送pc,为高则表示没有写满,表示可以往ep6的FIFO里写数据

19.module  usb(

20.        input                   CLCOK                     ,

21.        input                   rst_n                   ,

22.        //usb interface

23.        input                   flag_d                  ,

24.        input                   flag_a                  ,

25.        output  reg             slwr                    ,

26.        output  reg             slrd                    ,

27.        output  reg             sloe                    ,

28.        output  wire            pktend                  ,

29.        output  wire            ifclk                   ,

30.        output  reg  [ 1: 0]    FIFO_addr               ,

31.        inout   wire [15: 0]    usb_data                ,

32.        //receive cmd from pc

33.        output  wire            cmd_flag                ,

34.        output  wire  [15: 0]   cmd_data

35.);

36.//=====================================================================\

37.// ********** Define Parameter and Internal Signals *************

38.//=====================================================================/

39.parameter   CNT_END     =       256                             ;

40.parameter   IDLE        =       3'b001                          ;

41.parameter   READ        =       3'b010                          ;

42.parameter   WRITE       =       3'b100                          ;

43.reg     [ 2: 0]                 state_c  /*synthesis noprune*/  ;

44.reg     [ 2: 0]                 state_n  /*synthesis noprune*/  ;

45.//cnt

46.reg     [ 7: 0]                 cnt                             ;

47.wire                            add_cnt                         ;

48.wire                            end_cnt                         ;

49.//======================================================================

50.// ***************      Main    Code    ****************

51.//======================================================================

52.assign  ifclk       =       ~CLCOK;

53.assign  pktend      =       1'b1;

54.assign  usb_data    =       (state_c[2] == 1'b1) ? {cnt,8'h00} : 16'hzzzz;//先发送低字节,然后再发送高字节

55.

56.//cnt  产生写usb数据,从0-255

57.always @(posedge CLCOK or negedge rst_n)begin

58.    if(!rst_n)begin

59.        cnt <= 0;

60.    end

61.    else if(add_cnt)begin

62.        if(end_cnt)

63.            cnt <= 0;

64.        else

65.            cnt <= cnt + 1;

66.    end

67.    else begin

68.        cnt <= 0;

69.    end

70.end

71.

72.assign  add_cnt     =       state_c[2];

73.assign  end_cnt     =       add_cnt && cnt == 256-1;

74.

75.//state_c

76.always@(posedge CLCOK or negedge rst_n)begin

77.    if(!rst_n)begin

78.        state_c <= IDLE;

79.    end

80.    else begin

81.        state_c <= state_n;

82.    end

83.end

84.

85.//state_n

86.always@(*)begin

87.    case(state_c)

88.        IDLE:begin

89.            if(flag_a == 1'b1)begin //ep2非空,说明有数据来了,进入读状态

90.                state_n = READ;

91.            end

92.            else if(flag_d == 1'b1)begin  //flag_d是EP6的FF,低有效,为高时表示该FIFO现在空闲,往ep6写数据

93.                state_n = WRITE;

94.            end

95.            else begin

96.                state_n = state_c;

97.            end

98.        end

99.        READ:begin

100.            if(flag_a == 1'b0)begin //flag_a位低,说明数据已经读完了,进入空闲状态

101.                state_n = IDLE;

102.            end

103.            else begin

104.                state_n = state_c;

105.            end

106.        end

107.        WRITE:begin

108.            if(flag_d == 1'b0)begin  //flag_d为低,说明写满了,进入空闲状态,这时cypress自己就会把这些数据打包好,然后发送给pc,

109.                state_n = IDLE;

110.            end

111.            else begin

112.                state_n = state_c;

113.            end

114.        end

115.        default:begin

116.            state_n = IDLE;

117.        end

118.    endcase

119.end

120.

121.//FIFO_addr,sloe

122.always  @(posedge CLCOK or negedge rst_n)begin

123.    if(rst_n == 1'b0)begin

124.        FIFO_addr   =   2'b10;

125.        sloe        =   1'b1;

126.    end

127.    else if(state_n[1])begin

128.        FIFO_addr   =   2'b00;

129.        sloe        =   1'b0;

130.    end

131.    else begin

132.        FIFO_addr   =   2'b10;

133.        sloe        =   1'b1;

134.    end

135.end

136.

137.//slwr

138.always  @(posedge CLCOK or negedge rst_n)begin

139.    if(rst_n==1'b0)begin

140.        slwr    <=  1'b1;

141.    end

142.    else if(state_n[2])begin

143.        slwr    <=  1'b0;

144.    end

145.    else begin

146.        slwr    <=  1'b1;

147.    end

148.end

149.

150.//slrd

151.always  @(posedge CLCOK or negedge rst_n)begin

152.    if(rst_n==1'b0)begin

153.        slrd    <=  1'b1;

154.    end

155.    else if(state_n[1])begin

156.        slrd    <=  1'b0;

157.    end

158.    else begin

159.        slrd    <=  1'b1;

160.    end

161.end

162.

163.//cmd_flag

164.assign  cmd_flag    =   state_c[1] && flag_a;

165.assign  cmd_data    =   usb_data;

166.

167.reg     [ 9: 0]                 cnt0    /*synthesis noprune*/   ;

168.wire                            add_cnt0                        ;

169.wire                            end_cnt0                        ;

170.//cnt0  用来标记读的数据的个数,注意数据是16位的

171.always @(posedge CLCOK or negedge rst_n)begin

172.    if(!rst_n)begin

173.        cnt0 <= 0;

174.    end

175.    else if(add_cnt0)begin

176.        if(end_cnt0)

177.            cnt0 <= 0;

178.        else

179.            cnt0 <= cnt0 + 1;

180.    end

181.    else begin

182.        cnt0 <= 0;

183.    end

184.end

185.

186.assign  add_cnt0        =       cmd_flag;

187.assign  end_cnt0        =       add_cnt0 && cnt0 == 1024-1;

188.

189.endmodule

接下来就是利用官方程序进行调试。

图2‑90 官方USB调试工具使用示例

下面给出写的signal tap 的调试截图

写是一次写512个字节数据,0-255,注意usb的FIFO是一次发送16位的,也就是2个字节。先发送低字节,然后再发送高字节,这我直接把低字节给赋值为0了

图2‑91 signal tap写调试设置

图2‑92 signal tap调试结果

后面局部放大图,注意只有在flag_d为高时,slwr为低才是有效写,否则就是无效写,因为当flag_d为低时,表示写满了,这时FIFO就会弃之后的写数据了。

PC端接受到的数据要2个字节一起读,因为usb是16位发送的,可以看出接受到的数据的确是0000-00FF

注意:pc接受数据按我标的编码顺序执行

图2‑93 上位机接收数据结果

下面轮到“读”出场了。

图2‑94 signal tap读调试设置

pc发送数据按1-->2-->3的步骤,可以看出我们发送了12 34 56 78 这4个字节。

图2‑95 USB上位机写操作

注意这里我是设置了cmd_flag标志信号的,只有cmd_flag为高时的cmd_data的数据才是有效的,也就是pc发送过来的数据。

下面针对数据格式简单解释一下。

Alter 的FIFO ip 是可以读写位宽不一致的,具体看下面的图:

由上图可以看出这个和USB是一样的格式,都是先发低字节,然后再发高字节。或者说先接受低字节,然后再接受高字节。

1.1.3利用上位机控制FPGA

暂空。

E

(0)

相关推荐