一.阻塞与非阻塞:
1.阻塞赋值的执行可以认为是只有一个步骤的操作,即计算RHS的值并更新LHS,此时不允许任何其他语句的干扰。符号=
2.非阻塞赋值是由时钟节拍决定,在时钟上升到来时,执行赋值语句右边,然后将begin-end之间的所有赋值语句同时赋值到赋值语句的左边。
assign语句与always的区别
assign语句使用时不能带时钟。
always语句可以带时钟,也可以不带时钟。在always不带时钟时,逻辑功能和assign完全 一致,都是只产生组合逻辑。比较简单的组合逻辑推荐使用assign语句,比较复杂的组合逻辑 推荐使用always语句。
latch是指锁存器
一种对脉冲电平敏感的存储单元电路。锁存器和寄存器都是基本存 储单元,锁存器是电平触发的存储器,寄存器是边沿触发的存储器。两者的基本功能是一样的, 都可以存储数据。锁存器是组合逻辑产生的,而寄存器是在时序电路中使用,由时钟触发产生的。
latch的主要危害是会产生毛刺(glitch),在设计中,应尽量避免latch的使用。
代码里面出现latch的两个原因是在组合逻辑中,if或者case语句不完整的描述,比如if缺少else分支,case缺少default分支,导致代码在综合过程中出现了latch。解决办法就是if必须带else分支,case必须带default分支。
需要注意下,只有不带时钟的always语句if或者case语句不完整才会产生latch,带时钟的语句if或者case语句不完整描述不会产生latch。
状态机
Mealy
状态机:组合逻辑的输出不仅取决于当前状态,还取决于输入状态。
Moore
状态机:组合逻辑的输出只取决于当前状态。
三段式状态机:
一段式:整个状态机写到一个always模块里面,在该模块中既描述状态转移,又描述状态的输入和输出。不推荐采用这种状态机,因为从代码风格方面来讲,一般都会要求把组合逻辑和时序逻辑分开;从代码维护和升级来说,组合逻辑和时序逻辑混合在一起不利于代码维护和 修改,也不利于约束。
二段式:用两个always模块来描述状态机,其中一个always模块采用同步时序描述状态转 移;另一个模块采用组合逻辑判断状态转移条件,描述状态转移规律以及输出。不同于一段式状态机的是,它需要定义两个状态,现态和次态,然后通过现态和次态的转换来实现时序逻辑。
三段式:在两个always模块描述方法基础上,使用三个always模块,一个always模块采用同步时序描述状态转移,一个always采用组合逻辑判断状态转移条件,描述状态转移规律,另一个always模块描述状态输出(可以用组合电路输出,也可以时序电路输出)。
三段式状态机的基本格式是:
第一个always语句实现同步状态跳转;
第二个always语句采用组合逻辑判断状态转移条件;
第三个always语句描述状态输出(可以用组合电路输出,也可以时序电路输出)。
在开始编写状态机代码之前,一般先画出状态跳转图,这样在编写代码时思路会比较清晰
下面以一个7分频为例
1 module divider7_fsm (
2 //系统时钟与复位
3 input sys_clk ,
4 input sys_rst_n ,
5
6 //输出时钟
7 output reg clk_divide_7
8 );
9
10 //parameter define
11 parameter S0 = 7'b0000001; //独热码定义方式
12 parameter S1 = 7'b0000010;
13 parameter S2 = 7'b0000100;
14 parameter S3 = 7'b0001000;
15 parameter S4 = 7'b0010000;
16 parameter S5 = 7'b0100000;
17 parameter S6 = 7'b1000000;
18
19 //reg define
20 reg [6:0] curr_st ; //当前状态
21 reg [6:0] next_st ; //下一个状态
22
23 //*****************************************************
24 //** main code
25 //*****************************************************
26
27 //状态机的第一段采用同步时序描述状态转移
28 always @(posedge sys_clk or negedge sys_rst_n) begin
29 if (!sys_rst_n)
30 curr_st <= S0;
31 else
32 curr_st <= next_st;
33 end
34
35 //状态机的第二段采用组合逻辑判断状态转移条件
36 always @(*) begin
37 case (curr_st)
38 S0: next_st = S1;
39 S1: next_st = S2;
40 S2: next_st = S3;
41 S3: next_st = S4;
42 S4: next_st = S5;
43 S5: next_st = S6;
44 S6: next_st = S0;
45 default: next_st = S0;
46 endcase
47 end
48
49 //状态机的第三段描述状态输出(这里采用时序电路输出)
50 always @(posedge sys_clk or negedge sys_rst_n) begin
51 if (!sys_rst_n)
52 clk_divide_7 <= 1'b0;
53 else if ((curr_st == S0) | (curr_st == S1) | (curr_st == S2) | (curr_st == S3))
54 clk_divide_7 <= 1'b0;
55 else if ((curr_st == S4) | (curr_st == S5) | (curr_st == S6))
56 clk_divide_7 <= 1'b1;
57 else
58 ;
59 end
60
61 endmodule
模块化设计
一般采用自顶向下的设计方式
在顶层TOP文件中 可以
例化子模块
上图左边为子模块的定义、各种输入输出接口
上图右侧对应的就是顶层模块的例化 分别包含子模块名 例化模块名(可随意书写,我们一般命名为 u_+子模块名) 子模块端口需要与子模块的函数单口一致,后面的例化模块端口与TOP层的端口定义一致,也就是进行对应连接
参数的例化,参数的例化是在模块例化的基础上,增加了对参数的信号定义,如下图所示:
在对参数进行例化时,在模块名的后面加上“#”,表示后面跟着的是参数列表。计时模块定义 的 MAX_NUM 和 顶 层 模 块 的 TIME_SHOW 都 是 等 于 25000_000 , 当 在 顶 层 模 块 定 义 TIME_SHOW=12500_000时,那么子模块的MAX_NUM的值实际上是也等于12500_000。当然即使子模块包含参数,在做模块的例化时也可以不添加对参数的例化,这样的话,子模块的参数值等 于该模块内部实际定义的值。
Verilog语法中的localparam代表的意思同样是参数定义,用法和parameter基本一致,区别在于parameter定义的参数可以做例化,而localparam定义的参数是指本地参数,上层模块不可以对localparam定义的参数做例化