概述
本来打算第一个例子做一些简单的语法操作,但是感觉那样太浪费我和同学们的时间,所以这个例子,我们来写USB 3.0芯片FX600Q的驱动代码。本次使用的硬件环境与软件环境如下:
FPGA开发板:米联客MA7035FA 100T版本
软件环境:vivado2019.1
上位机软件:米联客开发的上位机软件
这里说明一下,USB3.0协议的速度是5Gbps与PCIE2,0速度相同。但是FX600Q的速度因为引脚的限制只能达到200MB/s,这里如果想全部利用USB3.0的速度,需要使用FX601Q芯片,两个芯片的时序一模一样,只是FX601Q比FX600Q数据引脚数目多一半。
FX600Q芯片介绍
这里关于FX600Q芯片的介绍,不作为主要重点。芯片的内部结构如下:
我们只需要理解芯片与FPGA相连的引脚即可,这里以MA7035FA为例,FPGA与芯片的连接图如下:
这里对上面的引脚及其作用进行解释:
USBSS_D:数据信号;
USBSS_BE:数据信号的字节使能信号,BE1对应高字节,BE0对应低字节,为1的情况下对应的数据位有效;
USBSS_TEX:学习USB芯片,最简单的一种方式就是把USB芯片看成FPGA与PC机之间的FIFO,TEX信号为0时代表该FIFO还有空间可以供写入;
USBSS_REX:为0时代表该USB所代表的FIFO还有数据可供读出;
USBSS_OE:数据输出的使能信号,一般在FPGA读USB芯片时为0,写USB芯片时为1;
USBSS_RD:读使能信号,0为有效;
USBSS_WR:写使能信号,0为有效;
USBSS_SIWU:为0时增加一个额外的拉力,一般恒为0;
USBSS_CLK:数据读出和写入的随路时钟;
USBSS_EN:芯片使能信号,一般默认为1;
USBSS_WAKEUP:芯片上电信号,一般默认为1;
GPIO:芯片配置信号,一般默认2’b00;
FX600Q的读写时序
把TX600Q当成一个FIFO来操作,那么他的读写时序就非常简单,FPGA读TX600Q芯片的时序如下:
FPGA写TX600Q芯片的时序如下:
FPGA驱动代码
由FX600Q的读写时序与引脚的定义可以很容易的写出代码,这里我使用的代码风格是参考的开源骚客的风格,个人感觉特别简练与整齐,这里使用的软件工具为sublime需要的可以进群里面自取:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : top.v
// Create Time : 2019-12-30 10:03:09
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module top(
input rst_n ,
output wire USBSS_EN ,
input sclk ,
inout [15:0] data ,
inout [ 1:0] be ,
input rxf_n ,
input txf_n ,
output reg oe_n ,
output reg wr_n ,
output wire siwu_n ,
output reg rd_n ,
output wire wakeup ,
output wire [ 1:0] gpio
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
parameter IDLE = 4'b0001 ;
parameter JUDGE = 4'b0010 ;
parameter READ = 4'b0100 ;
parameter WRITE = 4'b1000 ;
reg [ 3:0] state ;
wire [15:0] data_in ;
wire [15:0] data_out ;
//wire [ 1:0] be_in ;
//wire [ 1:0] be_out ;
wire fifo_wr ;
wire almost_full ;
wire fifo_rd ;
wire almost_empty ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
assign USBSS_EN = 1'b1;
assign wakeup = 1'b1;
assign siwu_n = 1'b0;
assign gpio = 2'b00;
assign fifo_wr = (rd_n == 1'b0) && (rxf_n == 1'b0);
assign data_in = (state == READ) ? data : 16'hzzzz;
assign fifo_rd = (wr_n == 1'b0) && (txf_n == 1'b0);
assign data = (state == WRITE) ? data_out : 16'hzzzz;
assign be = (state == WRITE) ? 2'b11 : 2'bzz;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
state <= IDLE;
else case(state)
IDLE : state <= JUDGE;
JUDGE : if(rxf_n == 1'b0 && almost_full == 1'b0)
state <= READ;
else if(txf_n == 1'b0 && almost_empty == 1'b0)
state <= WRITE;
else
state <= JUDGE;
WRITE : if(txf_n == 1'b1)
state <= JUDGE;
else
state <= WRITE;
READ : if(rxf_n == 1'b1)
state <= JUDGE;
else
state <= READ;
default : state <= IDLE;
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
oe_n <= 1'b1;
else if(state == READ && rxf_n == 1'b1)
oe_n <= 1'b1;
else if(state == READ)
oe_n <= 1'b0;
else
oe_n <= oe_n;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
rd_n <= 1'b1;
else if(state == READ && rxf_n == 1'b1)
rd_n <= 1'b1;
else if(state == READ && oe_n == 1'b0)
rd_n <= 1'b0;
else
rd_n <= rd_n;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
wr_n <= 1'b1;
else if(state == WRITE && txf_n == 1'b1)
wr_n <= 1'b1;
else if(state == WRITE)
wr_n <= 1'b0;
else
wr_n <= wr_n;
fifo_generator_0 fifo_generator_0_inst (
.clk (sclk ), // input wire clk
.srst (~rst_n ), // input wire srst
.din (data_in ), // input wire [15 : 0] din
.wr_en (fifo_wr ), // input wire wr_en
.rd_en (fifo_rd ), // input wire rd_en
.dout (data_out ), // output wire [15 : 0] dout
.full ( ), // output wire full
.almost_full (almost_full ), // output wire almost_full
.empty ( ), // output wire empty
.almost_empty (almost_empty ) // output wire almost_empty
);
endmodule
如果只有驱动代码,那么调试起来与读起来都很麻烦,所以这里给出tb测试代码,测试代码写的就比较草了,如下:
`timescale 1ns / 1ps
`define CLOCK 20
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : tb_top.v
// Create Time : 2019-12-30 11:35:06
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module tb_top;
reg rst_n ;
reg sclk ;
wire USBSS_EN ;
wire [15:0] data ;
wire [ 1:0] be ;
reg [15:0] data_in ;
reg rxf_n ;
reg txf_n ;
wire oe_n ;
wire wr_n ;
wire siwu_n ;
wire rd_n ;
wire wakeup ;
wire [ 1:0] gpio ;
initial begin
rst_n = 1'b0;
sclk <= 1'b0;
rxf_n = 1'b1;
txf_n = 1'b1;
#(50*`CLOCK)
rst_n = 1'b1;
#(50*`CLOCK)
rxf_n = 1'b0;
#(100*`CLOCK)
rxf_n = 1'b1;
#(100*`CLOCK)
txf_n = 1'b0;
#(100*`CLOCK)
txf_n = 1'b1;
end
always #(`CLOCK/2) sclk <= ~sclk;
assign data = (top_inst.state == 4'b0100) ? data_in : 16'hzzzz;
assign be = (top_inst.state == 4'b0100) ? 2'd3 : 2'bzz;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
data_in <= 'd0;
else if(top_inst.fifo_wr == 1'b1)
data_in <= data_in + 1'b1;
top top_inst(
.rst_n (rst_n ),
.USBSS_EN (USBSS_EN ),
.sclk (sclk ),
.data (data ),
.be (be ),
.rxf_n (rxf_n ),
.txf_n (txf_n ),
.oe_n (oe_n ),
.wr_n (wr_n ),
.siwu_n (siwu_n ),
.rd_n (rd_n ),
.wakeup (wakeup ),
.gpio (gpio )
);
endmodule
个人感觉自己的代码风格还是很清爽的,该模块的功能是回环测试功能,至于FIFO的定义,自己在vivado中补充就行了。上位机软件这里不做讲述,需要的可以参考米联客的配套上位机(包括源码),也可以进群自取。
参考文献
[1] 米联客MA7035FA开发板配套资料
结束语
对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群: