实战篇---USB2.0的Verilog实现

目录

1. USB接口什么情况下使用?

2. USB2.0 数据传输速率可以多快?

3. USB传输协议是怎么样的?

4. FPGA与CY7C68013通信,实现与IC内部FIFO读写功能。  

 1)控制信号介绍

2) FPGA对FIFO异步读

3) FPGA对FIFO异步写

4)硬件连接图

5)Verilog代码 

6)调试

总结



1. USB接口什么情况下使用?

       第一个问题,作为PC与外设的串行传输,有UART为什么还出现USB呢?目前USB运用的比较广泛,我们常见的USB2.0,3.0版本,数据传输的速率可以达到很高,2.0版本的可以达到400M,3.0版本的可以达到G以上。USB因为低电平以及差分信号达到了高速率传输的要求,但对于PC和手机等设备应用,需要接口统一、简单且功能复用,因此在串口的基础上出现新的标准,比串口具有更多的特性。其主要特性包括:使用方便、支持热插拔、连接灵活、独立供电等优点,可以连接键盘、鼠标、大容量存储设备等多种外设。需要注意的是,虽然USB是在UART的基础上出现的,但是是另一种新的传输标准和协议,所以速度是另一个级别的,不能简单的与UART比较传输速率。

2. USB2.0 数据传输速率可以多快?

        目前EZ-USB的传输有全速模式和高速模式,全速模式可以达到12M,高速模式可以达到480M.

3. USB传输协议是怎么样的?

         其实FPGA与USB IC CY7C通讯并不涉及到USB协议,FPGA对其读写,只是将其看成FIFO。USB协议在PC与CY7C通信时会使用到,因此在PC上需要安装USB驱动,通讯过程需要遵循协议标准。下面简单介绍下USB协议中,数据是如何传输的。

        USB传输数据是由一个一个的包(package)传输的,每个包再细分成一个一个域。包的具体构成及操作(如同步、位填充、校验等)被USB串行引擎隐藏。每个包中都有一个8bit的域,用来表示正在传输的包的类型,叫做PID (package ID)。USB2.0协议一共规定了16个包,分为四个大类,如下所示:

        PID类型                         PID名字
        令牌类(Token)           IN         OUT      SOF      SETUP
        数据类(Data)             DATA0  DATA1  DATA2  DATAM
        握手类(HandShake)  ACK     NAK      STALL   NYET
        特殊类(Special)         PRE     ERR      SPLIT   PING
        一次USB传输事务,以令牌包开始,然后数据包最后可能会有握手包结束。事务可能会有如下4种传输模式,控制传输、同步传输、中断传输和批量传输。如下,是一包的数据构成,payload是实际传输的数据。 

4. FPGA与CY7C68013通信,实现与IC内部FIFO读写功能。  

     在CY7C的固件中已经配置好了CY7C的工作模式、速率以及哪个作为输入存储哪个作为输出。在之后的内容中再具体介绍这部分。   

      而FPGA与CY7C之间的通讯,如果只是实现将FIFO EP2的数据通过FPGA转到EP6,那么只需要如下操作:FPGA作为主设备,CY7C作为从设备,通过控制FIFO_ADDR切换需要访问哪个 ENDPOINT,再通过FLAG知道endpoint是空,非空,满还是非满状态来对FIFO进行读写。

        在手册第二节中介绍到:In Slave (S) mode, the FX2LP accepts either an internally derived clock or externally supplied clock (IFCLK, max frequency 48 MHz) and SLCS#, SLRD, SLWR, SLOE, PKTEND signals from external logic. When using an external IFCLK, the external clock must be present before switching to the external clock with the IFCLKSRC bit. Each endpoint can individually be selected for byte or word operation by an internal configuration bit and a Slave FIFO Output Enable signal SLOE enables data of the selected width. External logic must ensure that the output enable signal is inactive when writing data to a slave FIFO. The slave interface can also operate asynchronously, where the SLRD and SLWR signals act directly as strobes, rather than a clock qualifier as in synchronous mode. The signals SLRD, SLWR, SLOE and PKTEND are gated by the signal SLCS#.   

        可以看出作为从设备,CY7C可以使用FPGA提供的时钟进行同步通讯也可以使用自身的时钟进行同步通讯,如果使用外部提供的时钟时,最大频率为48MHZ。通讯时FPGA输出 SLCS, SLRD, SLWR, SLOE, PKTEND这些信号给CY7C进行控制。通过内部配置位以及SLOE输出使能信号,可以选择对每个终端是按字节或者按字进行操作,并且当FPGA往FIFO中写数据时,SLOE必须时无效的。

 1)控制信号介绍

        SLCS:gates all other slave FIFO enable/strobes, 当输出高时,不可进行数据传输。   

        SLOE: Input-only output enable with programmable polarity for the slave FIFO.   FPGA输出的,控制对FIFO读数据使能的信号。IC 输出使能,低电平有效。即在FPGA读的时候需要使其置低,写的时候拉高。

       FIFOADR:选择是哪个endpoint,如下图,EP0,EP1是控制信号buffer,EP2,EP4,EP6,EP8是可以被用户访问的,地址分别对应00,01,10,11。具体使用哪个,作为输入还是输出,需要固件编写。

        FLAGX是对应endpoint的空满标志。

        SLRD: FIFO读信号。同步读时,FIFO指针在SLRD有效时的每个IFCLK的上升沿递增;异步读时,FIFO读指针在SLRD的每个有效到无效的跳变沿时递增;

        SLWR:FIFO写信号。同步写时,在SLWR有效时的每个IFCLK的上升沿时数据被写入,FIFO指针递增;异步写时,在SLWR的每个有效到无效的跳变沿时数据被写入,FIFO写指针递增。

2) FPGA对FIFO异步读

        上面是异步读时的时序图:可以看出SLRD信号的低电平有效时间最少为50ns,FIFO输出数据到FLAG输出有70ns的延迟,因此读数据到写请求输出前的判断必须要大于70ns,否则可能造成误判。而我们使用的FPGA时钟周期是20ns,在每一个状态中可能需要停留几个时钟周期。

3) FPGA对FIFO异步写

        

4)硬件连接图

          连接关系如下,FPGA通过控制信号以及读取到的EP FLAG来判断是进行读还是写; 

        

    

5)Verilog代码 

/*
	designer:yang yang
	data:20210907
	file: if endpoint EP2  is not empty and EP6 is not full, read the 16bit data from EP2 and send to EP6
	
*/

module USB_test(
	input clk,          //fpga clock 50mhz
	input rst_n,        // fpga reset input
	input USB_FLAGA,    //EP2 fifo empty indication 1:not empty; 0:empty
	input USB_FLAGB,    //EP4 fifo empty indication 1:not empty; 0:empty
	input USB_FLAGC,    //EP6 fifo full indication 1:not fll; 0:full
	output reg [1:0]USB_ADDR,  //FIFO ADDRESS
	output reg USB_SLCS,      //chipset select
	output reg USB_SLOE,      //data output enable
	output reg USB_SLWR,      // wirte indication   =low active
	output reg USB_SLRD,      //read indication     =low active
	inout [15:0]USB_FD    // usb data
	);
reg [15:0] data_reg;
reg access_req;
reg bus_busy;
reg usb_fd_en;  // inout enable,when en=1, as output, otherwise en=0,as input
reg [4:0] usb_state;
reg [4:0]i;
//reg [15:0] usb_wr_data_reg;
assign USB_FD = usb_fd_en?data_reg:16'bz;
parameter usb_idle=5'd0,
			 usb_rd_req=5'd1,
			 usb_rd_data=5'd2,
			 usb_rd_over=5'd4,
			 usb_wr_req=5'd8,
			 usb_wr_over=5'd16;
	// usb read/write request
always@(posedge clk or negedge rst_n) begin
	if(~rst_n) access_req<=1'b0;
	else if(USB_FLAGA & USB_FLAGC & (bus_busy==1'b0))
		access_req<=1'b1;
	else access_req<=1'b0;
	end
	// generate usb read and write command
always@(posedge clk or negedge rst_n) begin
	if(~rst_n) begin
		usb_state<=usb_idle;
		USB_ADDR<=2'b00;
		USB_SLCS<=1'b0;  
		USB_SLOE<=1'b1;
		USB_SLRD<=1'b1;
		USB_SLWR<=1'b1;
		usb_fd_en<=1'b0;  // write data output enable  
		//usb_wr_data_reg<=16'd0;
		end
	else begin
		case(usb_state)
			usb_idle: begin
				USB_ADDR<=2'b00;
				usb_fd_en<=1'b0;
				i<=5'd0;
				if(access_req) begin
					usb_state<=usb_rd_req;
					bus_busy<=1'b1;
					end
				else begin
					usb_state<=usb_idle;
					bus_busy<=1'b0;
					end
				end
			usb_rd_req:begin
				if(i==5'd2)begin
					USB_SLRD<=1'b1;
					USB_SLOE<=1'b0;
                    i<=i+1'b1;
					end
				else if(i==5'd8) begin
					USB_SLRD<=1'b0;
					USB_SLOE<=1'b0;
					usb_state<=usb_rd_data;
					i<=5'd0;
					end
				else i<=i+1'b1;
				end
			usb_rd_data:begin
				if(i==5'd8)begin
					USB_SLRD<=1'b1;
					USB_SLOE<=1'b0;
					data_reg<=USB_FD;
					usb_state<=usb_rd_over;
					i<=5'd0;
					end
				else i<=i+1'b1;
				end
			usb_rd_over:begin
				if(i==5'd4)begin
					USB_SLRD<=1'b1;
					USB_SLOE<=1'b1;
					USB_ADDR<=2'b10;
					usb_state<=usb_wr_req;
					i<=5'd0;
					end
				else i<=i+1'b1;
				end
			usb_wr_req:begin
				if(i==5'd8)begin
					USB_SLWR<=1'b1;
					usb_state<=usb_wr_over;
					i<=5'd0;
					end
				else begin
					USB_SLWR<=1'b0;
					usb_fd_en<=1'b1;
					i<=i+1'b1;
					end
				end
			usb_wr_over:begin
				if(i==5'd4)begin
					usb_fd_en<=1'b0;
					bus_busy<=1'b0;
					i<=0;
					usb_state<=usb_idle;
					end
				else i<=i+1'b1;	
				end
			default: usb_state<=usb_idle;
		endcase
		end
	end	
endmodule

  

6)调试

        在实际调试中,成功下载驱动后,发现无法找到调试USB的软件,不知道通过什么软件可以实现连接IC与对USB中的EP块进行读写操作。重新查看安装教程后发现,驱动和上位机调试软件是一起的。如下:

想要的软件:

 实际软件的位置:

 可以看到软件名是cyconsole而不是EZ-USB,怪不得我怎么都搜不到。

        那么代码具体运行的效果如何,通过signal tap的波形发现,通过上位机可以从PC将数据写到EP2中,并且FPGA可以从EP2读到正确的数据并执行写EP6的操作,但是从上位机中没办法读到EP6中的数据值。。同时FLAGC一直显示为高电平.......如下图

        对比数据手册的时序要求,发现,在读的过程中,读时序的各个阶段时间都大于要求所需要的时间,而在写EP6的过程中,SLWR写信号由低电平变为高电平的时间要求是70ns,而代码中是4个时钟,小于数据手册的要求值,将其改为8个时钟后,重新读写,正常。 

总结

        FPGA对USB的读写,如果只是单纯的异步读写比较简单,读取的协议相比串口的通信过程更简单。由于数据手册中对异步的时序要求,读一个数据最少需要100ns,写数据需要120ns。因此读写速率可以达到差不多10M.这个速度相对于本身的优势来说并不高,因此,下篇博文继续提高FPGA对USB的读写速率。

       

  • 8
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值