一天一个设计实例-万字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