参考书籍:
《手把手教你学FPGA设计:基于大道至简的至简设计法》
百度网盘链接:https://pan.baidu.com/s/12knuxzb4Z6z_8gR-By-l-A
提取码:mwzd
自己实现的书中的模块和相应的testbench测试:https://github.com/lishengxie/verilog-learning.git
计数器
设计规则
- 计数器需要考虑三要素:初值、加一条件和结束值(通常依次考虑);
- 计数初值必须为0;
- 使用某个计数值时必须同时满足加1条件;例如加1条件为
add_cnt
且add_cnt && cnt==4
时表示计数到第五个,而add_cnt==0 && cnt==4
不表示计数到第五个; - 计数条件必须同时满足加一条件,且结束值必须是
x-1
的形式; - 当从计数器取某个数时,assign形式必须为:
(加一条件) && (cnt==计数值-1)
; - 结束后计数值需要回到0;
- 需要限定范围时,推荐使用
>=
和<
两种符号,尽量不要使用大于或者小于等于两种符号; - 计数器设计时,先写计数器的always段,条件用名字代替,随后使用assign语句依次写出加一条件和结束条件;由此可以得出计数器的模板写法如下所示
// 中文词语方便理解,实际使用需要更改为相应的变量名
always @(posedge clk or negedge rst_n) begin
if(rst_n==1'b0)begin
cnt <= 0;
end
else if(加一条件)begin
if(结束条件)
cnt <= 0;
else
cnt <= cnt + 1;
end
end
assign 加一条件 = xxxxxx;
assign 结束条件 = (加一条件) && (cnt==计数值-1);
- 加一条件必须与计数器严格对齐,其他信号一律向计数器对齐;例如,现在需要输出两个信号
dout0
和dout1
,dout0
在计数到6时拉高,dout1
在计数到7时拉高,因此dout0
变1的条件为add_cnt && cnt==6-1
,dout1
变1的条件有两种写法
dout0 == 1
add_cnt && cnt==7-1
第一种写法是间接向计数器对齐,是非常不好的方法,建议使用第二种直接向计数器对齐; - 加一条件统一前缀为
add_
,结束条件统一前缀为end_
; - 暂不使用减1计数器。
状态机
设计规则
- 使用四段式写法
第一段为同步时序的always模块,用于格式化描述次态迁移到现态寄存器;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
第二段为组合逻辑的always块,用于描述状态转移条件判断,以三个状态的状态机IDLE->S1->S2->IDLE为例;
always @(*) begin
case(state_c)
IDLE: begin
if(idle2s1_start) begin
state_n = S1;
end
else begin
state_n = state_c;
end
end
S1: begin
if(s12s2_start) begin
state_n = S2;
end
else begin
state_n = state_c;
end
end
S2: begin
if(s22_idle_start) begin
state_n = S2;
end
else begin
state_n = state_c;
end
end
default: begin
state_n = IDLE;
end
endcase
end
第三段定义转移条件,注意条件一定要加上现态。
assign idle2s1_start = state_c == IDLE && xxxx;
assign s12s2_start = state_c == S1 && xxxx;
assign s22idle_start = state_c == S2 && xxxx;
第四段设计输出信号,每一个输出信号使用一个always块
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
out1 <= 1'b0
end
else if(state_c==S1)begin
out <= 1'b1;
end
else begin
out <= 1'b0;
end
end
- 四段式状态机第一段写法可以保持不变;
- 第二段中的状态转移条件用信号名表示,无需写出具体的转移条件;
- 用assign形式将状态转移条件写成xx2xx_start的形式;
- 状态转移条件,用assign产生变化条件时必须加上当前状态;
- 状态保持不变使用state_n = state_c,因为如果在组合逻辑中使用state_n = state_n,只有锁存器才能有保持电路,而锁存器在数字电路中通常是不希望出现的。
FIFO
设计规则
- 使用Show-ahead模式。FIFO有两种使用模式,分别是Normal和Show-ahead模式,其中Normal模式指先有读使能,之后FIFO才输出这个数据;而Show-ahead模式指FIFO先输出数据,遇到读使能后FIFO更新输出数据。两种模式只有读数据时存在区别,使用Show-ahead模式的好处在于读请求信号读数据同时有效,可以当做有效数据使用
- 读、写隔离,读控制和写控制是相互独立的,相互之间除了用FIFO交流信息外,不能有任何信息传递。
- 读使能必须判断空状态,并且用组合逻辑产生。原因在于使用Show-ahead模式当FIFO为空时,如果使用时序逻辑产生读使能,会出现在FIFO为空的情况下读数据的操作,读操作会出错。
- 处理报文时将指示信号和数据一起存入FIFO,这样做的好处是可以将报文数据、报文头、报文尾的指示信号和数据一起原封不动地送入下游模块。
- 读写时钟不同时,必须使用异步FIFO。
VScode verilog使用笔记
编写&添加Verilog代码段
- 编写代码段
* 顶部菜单栏 文件(File) -> 首选项(Preference) -> 用户代码段(User Snippets)
* 选择verilog.json打开,添加代码段模板,以编写的计数器模板为例,如下所示
2. 插入代码段
Ctrl+Shift+P进入命令输入,输入Insert Snippet命令选择对应的代码段插入。
数字电路相关
三态门电路/inout端口
- 当sio_out_en=0 时,此时sio_d作为输出口,sio_d输出sio_out的值;
- 当sio_out_en=1 时,此时sio_d作为输入口,sio_din输出sio_d的输入值;
inout_sio_d;
assign sio_d = sio_out_en ? Sio_out : 1'bz;
assign sio_din = sio_d;
评论 (0)