【至简案例系列】基于USB3.0的数据通信设计-一位研三学生学习FPGA的点滴感悟
基于USB3.0的数据通信作者:小Q 作为一个即将毕业但由于疫情不能回校的研三学生的独白。。。。。。真正和FPGA打交道应该是刚读研一的时候,不知道市场上什么资料好,可能和刚开始学习FPGA的同学们一样,主要心态是想快速掌握这门技术,经过一番调查选择了潘老师的《手把手教你学FPGA设计》,终于被大道至简的至简设计法所打败了。
1. 为什么大道至简好用且方便
由于书中讲解详细且区域划分鲜明,过多赘述不必多说,就像潘老师所说过那样,学好FPGA最重要就是状态机和计数器,记住,这是重点,要考的!!!其实刚开始我没认为这两项多么重要,哈哈哈,但是随着工程代码量的不断增大以及对时序等要求的不断增强,愈发觉得基础才能决定上层建筑。而反观三段式或四段式状态机代码清晰,结构分明,你所需要做的只是添砖加瓦,也就是你自己的状态,因为地基已经为你搭好了。再说计数器,应该说没有什么说的了,抬手就写,因为代码放在那了啊,只需要添加你的加一和结束条件就可以了啊!!!现在想想当初的我为什么要特立独行!!!导致在仿真阶段浪费好多时间。
2. USB3.0数据通信
最近写的代码(也不是最近了,大约在过年回家之前)是关于USB3.0数据通信的,这里多说几句,板子前期是购买的,后来自己设计的。代码部分是卖家提供(具体名称不方便透露,因为我不是挑事的人,哈哈哈),但是真的是他的代码太繁琐以及太不易搞懂且移植性太差了,但比较好的是我自己写也不浪费什么事情,我将USB3.0的状态分为读,写,空闲三个,然后根据三段式顺利写好属于自己的代码。源代码在下面,哈哈哈,如果可以的话请不要乱发或者帮我打码,毕竟毕业论文里还在用。并且家里电脑真的是什么软件都没安装,我就用记事本复制过来的。
2.1. USB3.0控制模块参考代码
信号类型位宽意义
clk输入1时钟信号
pclk_in输入1USB芯片复位信号
rst_n输入1复位信号,低电平有效
flag_a输入1USB芯片标志位,控制写状态
flag_b输入1USB芯片标志位,控制写状态
flag_c输入1USB芯片标志位,控制读状态
flag_d输入1USB芯片标志位,控制读状态
pclk输出1USB芯片复位信号
slcs输出1USB芯片片选信号
sloe输出1USB芯片控制信号
slrd输出1USB芯片读状态选择信号
slwr输出1USB芯片写状态选择信号
pktend输出1USB芯片数据包信号
fifo_addr输出2USB芯片数据读取方向控制信号
usb_data双向32USB3.0数据
cmd_flag输出1USB传输数据标志位
cmd_data输出32USB传输数据
moduleUSB_command( input clk, input pclk_in, input rst_n, //usb input flag_a, input flag_b, input flag_c, input flag_d,outputwirepclk ,outputwireslcs ,outputreg sloe ,outputregslrd ,outputregslwr ,outputwirepktend ,outputreg [ 1: 0] fifo_addr ,inoutwire usb_data ,outputregcmd_flag ,outputwire cmd_data ); parameter IDLE = 4'b0001 ;parameter WRITE = 4'b0010 ;parameter READ = 4'b0100 ;reg [ 3: 0] state_c /*synthesis preserve*/;reg [ 3: 0] state_n /*synthesis preserve*/;//cntreg cnt ;wire add_cnt;wire end_cnt; reg rd_cnt ;reg rd_data_len ;regwr_trig ;//写触发 assignslcs = 1'b0;assignpclk = pclk_in;assignpktend = 1'b1;assignusb_data = (!slwr) ? usb_write_data : 32'dz;wireusb_write_data ; //state_calways@(posedgeclk or negedgerst_n)begin if(!rst_n)beginstate_c<= IDLE; end else beginstate_c<= state_n; endend //state_nalways@(*)begin case(state_c)IDLE:beginif(flag_a&&flag_b&&wr_trig)beginstate_n = WRITE; end else if(flag_c&&flag_d)beginstate_n = READ; end else beginstate_n = state_c; end endWRITE:begin if(flag_b == 1'b0)begin //写满state_n = IDLE; end else beginstate_n = state_c; end endREAD:begin if(flag_d == 1'b0)begin //读空state_n = IDLE; end else beginstate_n = state_c; end enddefault:beginstate_n = IDLE; endendcaseend //fifo_addralways@(posedgeclk or negedgerst_n)beginif(rst_n == 1'b0)beginfifo_addr<=2'b00; sloe <=1'b1; end else if(state_c==READ)beginfifo_addr<=2'b11; sloe <=1'b0; end else beginfifo_addr<=2'b00; sloe <=1'b1; endend //slwr 输出信号,用时序逻辑好点always@(posedgeclk or negedgerst_n)beginif(rst_n == 1'b0)beginslwr<=1'b1; end else if(state_c==WRITE)beginslwr<=1'b0; end else beginslwr<=1'b1; endend //slrdalways@(posedgeclk or negedgerst_n)beginif(rst_n == 1'b0)beginslrd<=1'b1; end else if(state_c==READ)beginslrd<=1'b0; end else beginslrd<=1'b1; endend //rd_cntalways@(posedgeclk or negedgerst_n)beginif(rst_n == 1'b0)beginrd_cnt<=16'd0; end else if(slrd)beginrd_cnt<=16'd0; end else beginrd_cnt<=rd_cnt + 1'b1; endend //rd_data_lenalways@(posedgeclk or negedgerst_n)beginif(rst_n == 1'b0)beginrd_data_len<=16'd0; end else if(rd_cnt == 16'd3)beginrd_data_len<=usb_data + 16'd3; endend //cmd_flagalways@(posedgeclk or negedgerst_n)beginif(rst_n == 1'b0)begincmd_flag<=1'b0; end else if(rd_cnt == 16'd3)begincmd_flag<=1'b1; end else if(rd_cnt == rd_data_len)begincmd_flag<=1'b0; endend assigncmd_data = usb_data ;//将cmd_flag与cmd_data配合使用 //cntalways @(posedgeclk or negedgerst_n)begin if(!rst_n)begincnt<= 0; end else if(add_cnt)begin if(end_cnt)cnt<= 0; else cnt<= cnt + 1; end else begincnt<= 0; endend assignadd_cnt = state_c==WRITE; assignend_cnt = add_cnt&&cnt == 4096-1; //测试用的计数数据 //wr_trigalways@(posedgeclk or negedgerst_n)beginif(rst_n == 1'b0)beginwr_trig<=1'b0; end else if(cmd_flag&&cmd_data == 32'h11111111)beginwr_trig<=1'b1; end //else if(slwr == 1'b0)begin //开始写的时候,将wr_trig拉低 //wr_trig<=1'b0; //endend //fifo 读请求regfifo_rdeq;always@(posedgeclk or negedgerst_n)beginif(rst_n == 1'b0)beginfifo_rdeq<=1'b0; end else if(flag_a )begin fifo_rdeq<=1'b1; end else fifo_rdeq<= 1'b1;end //fifo 写请求regfifo_wdeq;always@(posedgeclk or negedgerst_n)beginif(rst_n == 1'b0)beginfifo_wdeq<=1'b0; end else if(flag_c)beginfifo_wdeq<=1'b1; end else fifo_wdeq<= 1'b1;end //FIFO fifo fifo_inst ( .clock (clk), .data ( cnt ), .rdreq ( fifo_rdeq ), .wrreq ( fifo_wdeq ), .q ( usb_write_data ) ); Endmodule
2.2. 明德扬的signaltap教程及仿真验证
这里还得感谢明德扬的signaltap教程,因为USB通信的数据线为32根,因此调试阶段使用modelsim真的不太好用,所以根据明德扬所讲的signaltap教程,我很容易的实现了抓图,分析出代码问题,并且我自己的体会,signaltap真的就像现实的示波器啊!!!好处自己去钻研吧。下面是抓图,通过抓图分析数据简直不要太快!!
3. 结论
最后,祝福明德扬越来越好,大道至简,受益匪浅。愿桃李不言下自成蹊,终其教育,无外乎传道授业解惑,古有孔丘弟三千,愿今师者百树人。
页:
[1]