作为一个即将毕业但由于疫情不能回校的研三学生的独白。。。。。。
真正和FPGA打交道应该是刚读研一的时候,不知道市场上什么资料好,可能和刚开始学习FPGA的同学们一样,主要心态是想快速掌握这门技术,经过一番调查选择了潘老师的《手把手教你学FPGA设计》,终于被大道至简的至简设计法所打败了。
一、为什么大道至简好用且方便
由于《手把手教你学FPGA设计》书中讲解详细且区域划分鲜明,至简设计法最大特点2个,设计模板化和思考步骤化。
设计模板化:
在设计上明德扬把一些功能模板规范成可复用的模板,我们只需要填入参数就能把设计做出来,极大地减少代码上不必要的错误,我们只需要专心做好设计;
思考步骤化:
至简设计法能帮助我们形成严谨的逻辑思维,做到设计有根据、思考有步骤。
过多赘述不必多说,就像潘老师所说过那样,学好FPGA最重要就是状态机和计数器,记住,这是重点,要考的!!!
其实刚开始我没认为这两项多么重要,哈哈哈,但是随着工程代码量的不断增大以及对时序等要求的不断增强,愈发觉得基础才能决定上层建筑。而反观三段式或四段式状态机代码清晰,结构分明,你所需要做的只是添砖加瓦,也就是你自己的状态,因为地基已经为你搭好了。再说计数器,应该说没有什么说的了,抬手就写,因为代码放在那了啊,只需要添加你的加一和结束条件就可以了啊!!!现在想想当初的我为什么要特立独行!!!导致在仿真阶段浪费好多时间。
二. USB3.0数据通信
最近写的代码(也不是最近了,大约在过年回家之前)是关于USB3.0数据通信的,这里多说几句,板子前期是购买的,后来自己设计的。代码部分是卖家提供(具体名称不方便透露,因为我不是挑事的人,哈哈哈),但是真的是他的代码太繁琐以及太不易搞懂且移植性太差了,但比较好的是我自己写也不浪费什么事情,我将USB3.0的状态分为读,写,空闲三个,然后根据三段式顺利写好属于自己的代码。源代码在下面,哈哈哈,如果可以的话请不要乱发或者帮我打码,毕竟毕业论文里还在用。并且家里电脑真的是什么软件都没安装,我就用记事本复制过来的。
2.1. USB3.0控制模块参考代码
|
信号
|
类型
|
位宽
|
意义
|
|
clk
|
输入
|
1
|
时钟信号
|
|
pclk_in
|
输入
|
1
|
USB芯片复位信号
|
|
rst_n
|
输入
|
1
|
复位信号,低电平有效
|
|
flag_a
|
输入
|
1
|
USB芯片标志位,控制写状态
|
|
flag_b
|
输入
|
1
|
USB芯片标志位,控制写状态
|
|
flag_c
|
输入
|
1
|
USB芯片标志位,控制读状态
|
|
flag_d
|
输入
|
1
|
USB芯片标志位,控制读状态
|
|
pclk
|
输出
|
1
|
USB芯片复位信号
|
|
slcs
|
输出
|
1
|
USB芯片片选信号
|
|
sloe
|
输出
|
1
|
USB芯片控制信号
|
|
slrd
|
输出
|
1
|
USB芯片读状态选择信号
|
|
slwr
|
输出
|
1
|
USB芯片写状态选择信号
|
|
pktend
|
输出
|
1
|
USB芯片数据包信号
|
|
fifo_addr
|
输出
|
2
|
USB芯片数据读取方向控制信号
|
|
usb_data
|
双向
|
32
|
USB3.0数据
|
|
cmd_flag
|
输出
|
1
|
USB传输数据标志位
|
|
cmd_data
|
输出
|
32
|
USB传输数据
|
|
module USB_command(
input clk ,
input pclk_in ,
input rst_n ,
//usb
input flag_a ,
input flag_b ,
input flag_c ,
input flag_d ,
output wirepclk ,
output wireslcs ,
output reg sloe ,
output regslrd ,
output regslwr ,
output wirepktend ,
output reg [ 1: 0] fifo_addr ,
inoutwire [31: 0] usb_data ,
output regcmd_flag ,
output wire [31: 0] 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*/ ;
//cnt
reg [31: 0] cnt ;
wire add_cnt ;
wire end_cnt ;
reg [15: 0] rd_cnt ;
reg [15: 0] rd_data_len ;
regwr_trig ;//写触发
assign slcs = 1'b0;
assign pclk = pclk_in;
assign pktend = 1'b1;
assign usb_data = (!slwr) ? usb_write_data : 32'dz;
wire[31:0] usb_write_data ;
//state_c
always@(posedgeclk or negedgerst_n)begin
if(!rst_n)begin
state_c<= IDLE;
end
else begin
state_c<= state_n;
end
end
//state_n
always@(*)begin
case(state_c)
IDLE:begin
if(flag_a&&flag_b&&wr_trig)begin
state_n = WRITE;
end
else if(flag_c&&flag_d)begin
state_n = READ;
end
else begin
state_n = state_c;
end
end
WRITE:begin
if(flag_b == 1'b0)begin //写满
state_n = IDLE;
end
else begin
state_n = state_c;
end
end
READ:begin
if(flag_d == 1'b0)begin //读空
state_n = IDLE;
end
else begin
state_n = state_c;
end
end
default:begin
state_n = IDLE;
end
endcase
end
//fifo_addr
always @(posedgeclk or negedgerst_n)begin
if(rst_n == 1'b0)begin
fifo_addr<= 2'b00;
sloe <= 1'b1;
end
else if(state_c==READ)begin
fifo_addr<= 2'b11;
sloe <= 1'b0;
end
else begin
fifo_addr<= 2'b00;
sloe <= 1'b1;
end
end
//slwr 输出信号,用时序逻辑好点
always @(posedgeclk or negedgerst_n)begin
if(rst_n == 1'b0)begin
slwr<= 1'b1;
end
else if(state_c==WRITE)begin
slwr<= 1'b0;
end
else begin
slwr<= 1'b1;
end
end
//slrd
always @(posedgeclk or negedgerst_n)begin
if(rst_n == 1'b0)begin
slrd<= 1'b1;
end
else if(state_c==READ)begin
slrd<= 1'b0;
end
else begin
slrd<= 1'b1;
end
end
//rd_cnt
always @(posedgeclk or negedgerst_n)begin
if(rst_n == 1'b0)begin
rd_cnt<= 16'd0;
end
else if(slrd)begin
rd_cnt<= 16'd0;
end
else begin
rd_cnt<= rd_cnt + 1'b1;
end
end
//rd_data_len
always @(posedgeclk or negedgerst_n)begin
if(rst_n == 1'b0)begin
rd_data_len<= 16'd0;
end
else if(rd_cnt == 16'd3)begin
rd_data_len<= usb_data + 16'd3;
end
end
//cmd_flag
always @(posedgeclk or negedgerst_n)begin
if(rst_n == 1'b0)begin
cmd_flag<= 1'b0;
end
else if(rd_cnt == 16'd3)begin
cmd_flag<= 1'b1;
end
else if(rd_cnt == rd_data_len)begin
cmd_flag<= 1'b0;
end
end
assign cmd_data = usb_data ; //将cmd_flag与cmd_data配合使用
//cnt
always @(posedgeclk or negedgerst_n)begin
if(!rst_n)begin
cnt<= 0;
end
else if(add_cnt)begin
if(end_cnt)
cnt<= 0;
else
cnt<= cnt + 1;
end
else begin
cnt<= 0;
end
end
assign add_cnt = state_c==WRITE;
assign end_cnt = add_cnt&&cnt == 4096-1; //测试用的计数数据
//wr_trig
always @(posedgeclk or negedgerst_n)begin
if(rst_n == 1'b0)begin
wr_trig<= 1'b0;
end
else if(cmd_flag&&cmd_data == 32'h11111111)begin
wr_trig<= 1'b1;
end
//else if(slwr == 1'b0)begin //开始写的时候,将wr_trig拉低
// wr_trig<= 1'b0;
//end
end
//fifo 读请求
regfifo_rdeq;
always @(posedgeclk or negedgerst_n)begin
if(rst_n == 1'b0)begin
fifo_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)begin
if(rst_n == 1'b0)begin
fifo_wdeq<= 1'b0;
end
else if(flag_c)begin
fifo_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真的就像现实的示波器啊!!!好处自己去钻研吧。下面是抓图,通过抓图分析数据简直不要太快!!

无论是FPGA初学者,还是已经工作的工程师,都推荐学习明德扬的至简设计法,一定会有大大的收获。最后,祝福明德扬越来越好,大道至简,受益匪浅。愿桃李不言下自成蹊,终其教育,无外乎传道授业解惑,古有孔丘弟三千,愿今师者百树人。








