作者:MiS603开发团队
日期:20150911
公司:南京米联电子科技有限公司
论坛:www.osrc.cn
EAT博客:http://blog.chinaaet.com/whilebreak
博客园:http://www.cnblogs.com/milinker/
MiS603开发板 第十一章 CY7C68013A Slave FIFO回传输
CY7C68013A提供了强大和灵活的外部接口通信方式,有Slave FIFO 和GPIP方式两种方式。由于本教程采用FPGA+CY7C68013A的方案,因此采用Slave FIFO方式进行传输,本节开始详细讲解SlaveFIFO 传输方式。
11.1 应用场合分析
USB2.0接口是一种非常方便使用的高速通信接口,所有电脑都带USB接口,并且理论带宽在480Mb/S,适合很多数据传输场合,比如数据采集,视频图像传输。
11.2 Slave FIFO硬件结构
下图,描述了CY7C68013A的Slave FIFO的硬件结构:
1)、SlaveFIFO具备4个端点,分别为EP2/EP4/EP6/EP8
2)、时钟具备内部时钟48MHZ/30MHZ 两种选择,或者采用外部时钟5~48MHZ输入
3)、外部端口通过一些控制信号实现通信时序的控制
4)、数据端口最大可以配置为16bit位宽
下图描述了Slave FIFO模式下CY7C68013A和外部设备的接线图,可以看出来,EXT Master就是FPGA
11.3 Slave FIFO 时序分析
下图描述了异步通信时序,和同步通信时序,如果采用异步时序只要信号SLRD 和 SLWR既可以,但是异步时序需要考虑采样和被采样周期,也就是说当CY7C68013A工作在48MHZ的最高速度下,是无法采样到48MHZ的异步信号的,所以速度上,异步时序没有同步时序快。从下图中可以比较异步传输和同步传输的区别。异步传输,完全由SLRD和SLWR控制,而同步传输则在SLRD或SLWR有效状态时,读写数据与IFCLK保持同步。本节的例子也是采用了同步时序。
在进行具体的同步时序分析之前。先来了解下,CY7C68013A各个引脚对应的功能。
IFCLK:FX2输出的时钟,可作为通信的同步时钟。
FLAGA/FLGAB/FLAGC/FLAGD:CY7C68013A内部FIFO的状态信息,如满、空标志等。这些状态信息的功能,可通过后续编程来实现。
SLCS:FIFO片选使能信号,由外部逻辑控制,低电平有效。
SLOE:FIFO输出使能信号,当其无效时,数据线上的数据无效。
SLRD:FIFO读信号,采用同步时序传输时,在SLRD有效状态情况下,数据随着IFCLK时钟从FIFO读出。
SLWR:FIFO写信号,采用同步时序传输时,在SLWR有效状态情况下,数据随着IFCLK时钟写入FIFO。
PKTEND:包结束信号。正常情况下,向CY7C68013A内部写入信号,当写入FIFO端点的字节数等于预先设定的数据包大小时,数据自动打包进行传输。当数据写入,小于设定设定的数据包大小时候,只需在数据传输完成后,定义此信号,则自动将数据打包传输。
FIFOADR:地址控制端,选择CY7C68013A内部的四个FIFO。其中FIFOADR=2’b00时,对应端点2,即内部FIFO对应的地址。FIFOADR=2’b01,对应端点4,FIFOADR=2’b10对应端点6,FIFOADR=2’b11对应端口8。
FD:数据线。可配置16bit位宽或8bit位宽。
下面分别分析下,同步传输模式下读写时序。
1.同步传输SLAVE FIFO写模式:
如上图的状态机(该状态机从官方手册中截图,详细参考官方FX2+TechRefManual手册)。其标准写时序为:
?初始状态:当写事件发生时,进入状态1;
?状态1:该状态确定FIFOADR指向的地址,选择端点号,完成后,进入状态2;
?状态2:根据所选择的FIFO满、空标志位,确定是否将数据写入,若FIFO满状态,在该状态等待,直至满状态无效,进入状态3;
?状态3:保持写信号SLWR有效,将待写入数据送入数据线上,持续一个IFCLK时钟,进入状态4;
?状态4:若继续写入数据,进入状态2,否则写入完成,进入空闲状态。
默认情况下,FULL,EMPTY,SLWR,PKTEND均为低电平有效,其对应的几种时序图如下所示:
情况一:FIFO中无数据,为空时写入数据
上图中,选择端点8,单个周期后,判断FLAGB始终为高,FLGAC始终为低,即端点8中无数据,此时数据可写入。在SLWR低电平有效时,IFCLK上升沿写入数据N。写入之后,FLGAC拉高,表示端点中非空。此时FLAGB依然无效,即表示数据可继续写入,SLWR再次拉低后,IFCLK上升沿写入数据N+1。
情况二:FIFO中非空,且FIFO非满时,数据写入
从上可以看出,此时端点8中,FLGAB时钟为高电平,无效状态,即非空状态,FLGAC同样无效,即端点中数据未写满。持续写入,当写入512个字节后,达到预先设定的数据包字节数,CY7C68013A将数据自动打包传输。该过程由硬件自动完成,外部逻辑按照上述时序,写入字节后,FX2硬件自动将数据打包,实现高速传输。
情况二:FIFO中数据写满
同样从上可以看出,当数据写满后,FLAGB满标志位低电平有效,数据未达到设定的数据包情况下,可通过拉低PKTEND信号来进行打包传输。但FLGAB持续低电平,写信号再次有效时,数据无法写入。
2.同步传输SLAVE FIFO读模式:
既然同步传输写存在固定的模式,当然读也要按照对应的时序进行。其状态转移图如下所示:
从其官方提供的标准时序可以看出,包含以下过程:
?初始状态:当读事件发生时,进入状态1;
?状态1:该状态确定FIFOADR指向的地址,选择端点号,完成后,进入状态2;
?状态2:当端点中数据位空时,保持该状态,当端点中数据非空时,进入状态3;
?状态3:保持写信号SLRD有效,从数据线上采集数据,持续一个IFCLK时钟,数据采集完成后,FIFO内部读指针自动加1,进入状态4;
?状态4:若继续读取数据,进入状态2,否则写入完成,进入空闲状态。
同样,各个状态信号或控制信号,默认低电平有效,其官方提供的几种读时序图如下所示:
情况一:FIFO中非空,正常读操作
从图中可以看出状态2时,选择端点2FIFO,FLAGC持续高电平,表示端点2中的FIFO有数据,可正常读写,当SLOE有效时,使能SLRD,在IFCLK上升沿时,读出FD总线上的数据,FX2对应FIFO内部指针自动累加,以为下一次读操作做准备。
情况二:FIFO中为空,无数据
当连续进行读操作时,FLGAC有效状态时,此时将SLOE和SLRD设置为无效状态,等待FIFO中有数据才能再次读操作,此时数据总线保持高阻态。
11.4 Slave FIFO回传实验
通过前几节对Slave fifo的详细介绍,这节来完成个slave fifo的回传实验。通过上位机发送数据,CY7C68013将数据传输至FPGA中的FIFO。FPGA将FIFO中的数据一方面通过CY7C68013返回至68013的FIFO中,上位机可抓取该FIFO中数据。另一部分将FIFO中的数据,通过串口传输至调试助手,比较两次回传的数据。
11.5 USB 固件源码分析
CYPRESS公司提供的完整开发包里,包含了固件的编程框架,并提供了很多可直接应用的固件程序。用户可直接使用,或者在原有框架基础上添加修改少量的代码,即可完成固件的开发。框架内部提供了钩子函数,用户在钩子函数中添加自己的代码,完成编程。
固件复位上电时,先初始化一些全局变量,接着调用初始化钩子函数TD_Init(),开中断,等待设备重枚举完成,最后进入while(1)循环语句,执行任务调度。下文中,先简单介绍下固件开发的框架,之后再结合具体的源码,分析下固件开发过程中,关键寄存器的配置。
?固件总体框架分析
打开米联MIS603开发板的固件工程文件,从中可以看出其左边的工程项目如下图所示。从图中可以看出,工程中主要包含fw.c和slave.c文件。
?fw.c文件是整个USB的根本,USB协议方面都在这里面完成,包括上电枚举,重枚举,唤醒以及调用程序等。这个工程文件由CYPRESS公司提供的开发包里面提供,如非必要,尽量不要修改这里面内容。
?slave.c文件即用户自己代码的书写文件,通常这里面包含了传输数据需要配置的寄存器,时钟,断点等。当然这个程序也不是所有都需要用户自己来写的,可以拷贝官方提供的固件开发工程,再从中进行修改即可。用户可从安装的CYPRESS开发包里面找到,这个路径为:D:\Cypress\USB\CY3684_EZ-USB_FX2LP_DVK\1.0\Firmware。该文件夹是CYPRESS提供的不同应用场合的固件开发例程。用户只需要修改其中官方的即可。
至于其他调用的文件,可以从下表中看出:
EZUSB.LIB
EZUSB库文件
EZUSB.h
EZUSB头文件
fx2regs.h
FX2寄存器头文件
fx2.h
预定义,宏及函数声明
fw.c
固件框架源文件
dscr.a51
USB描述符列表,用户可修改
USBJmpTb.OBJ
中断跳转函数目标文件
syncdly.h
同步延时,定义了程序短延时函数SYNCDELAY
intrins.h
C51一些数据类型及函数定义
intr.c
外部中断处理文件
当然,可能工程中并不需要上述所有文件。在打开固件程序后,点击编译,很多人可能并不能立即编译无误,这是由于工程文件的路径变化,导致无法编译所有文件。这里需要修改的路径包含如下两个窗口。在调整好这些路径后,再次点击编译,确保编译无误,所有文件均参与工程编译中。
编译完成后,其编译结果如下所示:
在了解上述开发流程后,继续需要了解的函数包括:
?void TD_Init():此函数在USB启动后只调用一次。此函数主要添加USB数据传输的初始化代码,也就是传输之前要配置的。通过68013内部寄存器,配置好时钟、FIFO、中断等。
?void TD_Poll():用户调度程序,USB在空闲时反复执行该函数,通常将反复执行的代码放着里面。通常直接采用默认方式。
至于其他函数,在本例程中显得不是很重要,这里不在细细解释,请参考官方文档查阅。
?USB传输寄存器配置
CY7C68013实际上就是一个51核的单片机,说起单片机自然对其操作,就涉及到寄存器配置了。因此在有效传输数据之前,对寄存器进行配置,是重要的一步。
1、设置时钟寄存器CPUCS=0x10;其中CPUCS寄存器如下所示:
CPUCS
?PORTCSTB --- 1表示读写端口C时产生RD#和WR#信号,0不产生读写信号,默认为0。
?CLKSPD1,CLKSPD0 --- CPU时钟设置。如下表所示:
?CLKINV --- 时钟状态反转
?CLKOE --- 时钟使能
2、PINFLAGSAB/CD(E602:E603):FLAGx引脚配置寄存器
FLAGA,FLAGB,FLAGC,FLAGD反映FIFO状态选择。每个脚有编址/固定两种模式:如设为编址模式,则它们都反映FIFOADR[1:0]脚当前所指端点的状态,其中,FLAGA反映“可编程极限”,FLAGB反映“满”标志,FLAGC反映“空”标志,FLAGD不存在;如设为固定模式,它们均可任意设置成反映任意端点的任意标志,而不受限于FIFOADR[1:0]脚当前所指端点的状态。
Slave fifo模式中,用引脚FLAGA~FLAGD来定义用端点FIFO的状态,并可灵活编程来实现FLAGx设置,见下表所示。
说明:
1. PF表示FIFO编程状态,EF表示FIFO已空,FF表示FIFO已满
2. 0000为索引模式,其它为固定模式
3、PORTACFG:端口A配置
置1使能端口A复用引脚,虽然SLCS出现在PORTACFG.6的位置上,当IFCFG1:0=11时,PORTA.7复用为SLCS,FLAGD也出现在PORTA.7引脚上,当PORTACFG.7置位时,PORTA.7复用为FLAGD输出,当PORTACFG.6和PORTACFG.7均为1,则PORTA.7复用为FLAGD。所以PORTACFG7:6=01时,PORTA.7复用为SLCS。
4、接口配置寄存器IFCONFIG
IFCONFIG
?IFCLKSRC --- 0外部时钟源,1内部时钟源。
?3048MHZ --- 0 IFCLK时钟为30M,1 IFCLK时钟为48M。
?IFCLKOE --- IFCLK时钟输出使能,0关闭,1打开。
?IFCLKOL ---IFCLK信号反正,0不反转,1反转
?ASYNC --- GPIF同步或异步操作,0同步,1异步
?GSTATE --- GPIF状态输出使能,0关闭,1使能,引脚PE0 PE1 PE2和GPIF状态 GSTATE0,GSTATE1,GSTATE2。
?IFCFG0,IFCFG1 --- 模式设置,决定了端口引脚功能。
5、EP2CFG/ EP4CFG/ EP6CFG/ EP8CFG端点2,4,6,8配置寄存器
?VALID --- 0端点无效,1端点有效。
?DIR --- 端点方向,0=OUT方向,1=IN方向,默认端点2,4位IN,端点6,8为OUT。
?TYPE1,TYPE0 --- 端点类型,如下表所示。
?SIZE --- 缓冲区大小(仅端口2和端点6),0=512字节,1=1024字节
?BUF1,BUF0 --- 端点缓冲区个数(仅端口2和端点6)。
6、端点2/4/6/8 FIFO配置寄存器
EP2FIFOCFG/EP4FIFOCFG/EP6FIFOCFG/EP8FIFOCFG
?INFM1 --- 1表示IN端点满减1。
?OEP1 --- 1表示OUT端点空加1。
?AUTOOUT --- 1表示数据自动提交OUT端点FIFO,0表示手动。
?AUTOIN --- 1表示IN端点FIFO数据自动呈交SIE,0表示手动。
?ZEROLENIN --- 1表示使能零长度IN端点数据包,0表示非使能。
?WORDWIED --- 数据宽度,0表示8bit,1表示16bit。
其他重要的寄存器大家可以参考官方文档。当然不想看中文的,推荐大家一本书《USB开发手册》,这本书详细地描述了USB68013所有的使用方法。
重要的程序源码如下所示:
void TD_Init( void )
{ // Called once at startup
CPUCS = 0x10; // CLKSPD[1:0]=10, for 48MHz operation, output CLKOUT
PINFLAGSAB = 0x08; // FLAGD - EP2EF
SYNCDELAY;
PINFLAGSCD = 0xE0; // FLAGA - EP6FF
SYNCDELAY;
PORTACFG |= 0x80;
IFCONFIG = 0xE3; // for async? for sync?
EP2CFG = 0xA0; //out 512 bytes, 4x, bulk
SYNCDELAY;
EP6CFG = 0xE0; // in 512 bytes, 4x, bulk
SYNCDELAY;
EP4CFG = 0x02; //clear valid bit
SYNCDELAY;
EP8CFG = 0x02; //clear valid bit
SYNCDELAY;
SYNCDELAY;
FIFORESET = 0x80; // activate NAK-ALL to avoid race conditions
SYNCDELAY; // see TRM section 15.14
FIFORESET = 0x02; // reset, FIFO 2
SYNCDELAY; //
FIFORESET = 0x04; // reset, FIFO 4
SYNCDELAY; //
FIFORESET = 0x06; // reset, FIFO 6
SYNCDELAY; //
FIFORESET = 0x08; // reset, FIFO 8
SYNCDELAY; //
FIFORESET = 0x00; // deactivate NAK-ALL
// handle the case where we were already in AUTO mode...
// ...for example: back to back firmware downloads...
SYNCDELAY; //
EP2FIFOCFG = 0x00; // AUTOOUT=0, WORDWIDE=1
// core needs to see AUTOOUT=0 to AUTOOUT=1 switch to arm endp‘s
SYNCDELAY; //
EP2FIFOCFG = 0x10; // AUTOOUT=1, WORDWIDE=1
SYNCDELAY; //
EP6FIFOCFG = 0x4C; // AUTOIN=1, ZEROLENIN=1, WORDWIDE=1
SYNCDELAY;
}
11.6 FPGA源码分析
module USB_FPGA(
input ifclk_i,
inout [7:0] fdata_b,
output[1:0] faddr_o,
output reg slrd_o ,
output reg slwr_o,
output reg sloe_o,
input flagd_i,
input flaga_i
);
parameter READ1=0;
parameter READ2=1;
parameter WRITE1=2;
parameter WRITE2=3;
reg [1:0] usb_s=0;
wire empty;
wire [7:0] usb_tx;
//双向端口数据流的切换
assign fdata_b= ((usb_s==WRITE1)||(usb_s==WRITE2)) ? usb_tx : 8‘hzz;
//CY7C68013A EP2和EP6端口切换
assign faddr_o =((usb_s==READ1) ||(usb_s==READ2) ) ? 2‘b00 : 2‘b10;
//88888888888888888888888888888888888888888888888888888888888888888888888888888888888
//读写控制逻辑
always @(*)begin
if(flaga_i&&(usb_s == READ2)) begin//读USB FIFO
slrd_o <= 1‘b0;
slwr_o <= 1‘b1;
sloe_o <= 1‘b0;
end
else if(flagd_i&&(usb_s == WRITE2)) begin//写USB FIFO
slrd_o <= 1‘b1;
slwr_o <= 1‘b0;
sloe_o <= 1‘b0;
end
else begin//停止读写
slrd_o <= 1‘b1;
slwr_o <= 1‘b1;
sloe_o <= 1‘b0;
end
end
wire sys_clk;
reg [9:0]usb_cnt;
assign sys_clk=~ifclk_i;
//888888888888888888888888888888888888888888888888888888888888888888
//读写状态机
always @(posedge sys_clk)begin
case(usb_s)
READ1: begin //此状态1个时钟周期切换传输端口到读
usb_s <= READ2;
end
READ2: begin //此状态切换端口
if(flaga_i);//USB FIFO非空有数据则一直读
else if(usb_cnt>511)//如果FPGA FIFO非空即进入写准备
usb_s <= WRITE1;
end
WRITE1:begin//此状态1个时钟周期切换传输端口到写
usb_s <= WRITE2;
end
WRITE2:begin
if(flagd_i);//USB FIFO非满则从FPGA FIFO读数据写
if(empty) usb_s <= READ1; //直到读空FIFO
end
endcase
end
//888888888888888888888888888888888888888888888888888888888888888888
//测试收发数据数量
(*KEEP = "TRUE" *)wire[9:0] usb_cnt_dg;
assign usb_cnt_dg=usb_cnt;
always @(posedge sys_clk)begin
if(usb_s==READ2&&flaga_i)usb_cnt<=usb_cnt+1‘b1;
if(usb_s==WRITE2&&flagd_i)usb_cnt<=usb_cnt-1‘b1;
end
//888888888888888888888888888888888888888888888888888888888888888888
//内部延迟复位
reg [9:0] cnt=0;
always @(posedge sys_clk)begin
if(!cnt[9])cnt<=cnt+1;
end
wire rst;
assign rst =!cnt[9];
wire full;
wire [7:0] usb_rx;
assign usb_rx = fdata_b;
//888888888888888888888888888888888888888888888888888888888888888888
//FIFO
fifo fifo0 (
.clk(sys_clk), // input clk
.rst(rst), // input rst
.din(usb_rx), // input [7 : 0] din
.wr_en((~slrd_o)&&(~full)), // input wr_en
.rd_en(~slwr_o), // input rd_en
.dout(usb_tx), // output [7 : 0] dout
.full(full), // output full
//.empty(empty) // output empty
.almost_empty(empty)
);
endmodule
以上设计关键是在于状态机设计
READ1:此状态一个时钟周期用来切换端口方向和把CY7C68013A端口切换到EP6以便让FPGA可以读取CY7C68013A的数据。
READ2:数据端口和CY7C68013A端口已经切换到EP6,当flaga_i表示FIFO非空,有数据,就开始读取数据保存到FPGA的FIFO.当读到512个数据的时候,切换到写状态机
WRITE1:此状态一个时钟周期用来切换端口方向和把CY7C68013A端口切换到EP2以便让FPGA可以写数据给CY7C68013A。
WRITE2:只要FPGA的FIFO有数据,就放EP2端口写数据,这样一次写入刚才读入的512 btytes的数据。
11.7 Chipcope 逻辑分析仪在线调试
设置好Chipscope环境,这里我们抓取信号slwr,slrd,flaga,flagd,usb_tx信号,选择持续触发模式,slwr上升沿触发,从上位机发送数据,预先发送几个ABCD,再发送单个字节A,从chipscope中抓取的信号,如下图所示,与发送数据保持一致。
11.8 测试结果
将USB固件下载完成后,断电再上电之后,打开cyconsole执行文件后,可以看出固件已经下载完成。选择端点2OUT通道,采用Bulk Trans可以看出,已经能够向68013里写入字节,选用断点6IN通道,却无法读取刚刚发送的字节。断电重启后,再下载slave fifo传输模式的FPGA对应编译文件。选择端点2OUT通道后,发送512个字节的A字符,连续发送几次后,再选用6IN通道,读取刚刚发送完的字符A。可以看出slave fifo回传数据正常。传输结果如下所示:
11.9 小结
Slave FIFO对于USB68013的应用来说是非常重要的一个例子。实现了数据从PC至开发板,开发板至PC机的环路传输。对于大数据量的传输,slave fifo在满足高效率传输的同时,能够保证数据无丢失,是68013应用最广泛的一种传输方式。初学者可以先从此例子学起,在理解改传输模式后,进行后续更复杂的传输模式。