SPI程序的一些总结
1、电平敏感量触发/边沿触发敏感量问题(阻塞赋值与非阻塞赋值的问题):详细程序参看Verilog数字系统设计教程 夏宇闻 【第二版】第14章 深入理解阻塞和非阻塞赋值的不同
(1)、原则1:时序电路建模时,用非阻塞赋值
原则2:锁存器电路建模时,用非阻塞赋值
(2)、原则3:用always快描述组合逻辑时,应采用阻塞赋值语句
(3)、原则4:在同一个always快中描述时序和组合逻辑混合电路时,用非阻塞赋值
(4)、原则5:不要再同一个always快中同时使用阻塞和非阻塞赋值
(5)、原则6:严禁在多个always块中对同一个变量赋值
(6)、原则7:用$系统任务来显示,应该用非阻塞赋值的变量值
RHS——表示赋值等号右边的表达式或变量;LHS——表示赋值等号左边的表达式或变量;
1.1
阻塞赋值操作符(即=):阻塞赋值的执行可以认为是只有一个步骤的操作,即计算RHS并更新LHS,此时不允许有来自任何其他Verilog语句的干扰。所谓阻塞的概念是指在同一个always快中,其后面的赋值语句从概念上是在前一句赋值语句结束后在开始进行赋值的。
如果一个过程块中阻塞赋值的RHS变量正好是另一个过程快中阻塞赋值的LHS变量,这两个过程快又用同一个时钟沿触发。
参看例子中的y1,y2,若复位信号已从1到0,(1)若always块的又消失中沿比下面的always块的有效时钟沿早到几个皮秒(由时钟偏差造成的),则先计算y1=y2,此时y2的值为复位前的值1,则y1=1,当下一个always块执行的时候,y2=y1,此时y1=1,则y2=1;(2)同理可分析得到当下面的always块的有效时钟沿早到几个皮秒,则y1,y2都会取0,这说明这个Verilog模块是不稳定的,必定会产生冒险和竞争的情况。
1.2
非阻塞赋值操作符(即<=),非阻塞赋值的操作过程可以看做下面两个步骤:(1)在赋值开始时刻,计算非阻塞赋值RHS表达式;(2)在赋值结束时刻,更新非阻塞赋值LHS表达式。非阻塞赋值操作只能用于寄存器类型变量进行赋值,因此只能用在“initial”块和“always”块等过程快中,且不能用于连续赋值。
参看例子中的y3,y4,若复位信号已从1到0,无论哪一个always块的有效沿先到,两个always块中的非阻塞赋值都在赋值开始时刻计算RHS表达式,而在结束时刻才更新LHS表达式。实质上y1被赋值y2的值是有rst正跳变沿确定的,y2被赋值y1的值也是由正跳变沿去诶的那个的(若以后rst继续保持为0,时钟信号不断重复,则每次被赋值的y1和y2都是有上一个周期的时钟有效沿确定的)。从用户的角度看这两个非阻塞赋值好像是并行执行的。
例子1:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 `timescale 1ns/1ns
2 `define clk_cycle 5
3 module BlockAssignment;
4 reg clk;
5 reg rst;
6 reg y1;
7 reg y2;
8 reg y3;
9 reg y4;
10 always #`clk_cycle clk=~clk;
11 initial
12 begin
13 clk=0;
14 rst=0;
15 #10 rst=1;
16 #10 rst=0;
17 end
18
19 /* BlockAssignment */
20 always @(posedge clk or posedge rst)
21 begin
22 if(rst)
23 y1=0;
24 else
25 y1=y2;
26 end
27 always @(posedge clk or posedge rst)
28 begin
29 if(rst)
30 y2=1;
31 else
32 y2=y1;
33 end
34
35 /* NONBlockAssignment */
36 always @(posedge clk or posedge rst)
37 begin
38 if(rst)
39 y3<=0;
40 else
41 y3<=y4;
42 end
43 always @(posedge clk or posedge rst)
44 begin
45 if(rst)
46 y4<=1;
47 else
48 y4<=y3;
49 end
50
51 endmodule
仿真波形:(此处时序逻辑中阻塞赋值没有达到想要的逻辑关系,非阻塞可以达到预想的逻辑关系)
例子2:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 module a04(y,a,b,c,d);
2 output y;
3 input a,b,c,d;
4 reg y,temp1,temp2;
5 always@(a or b or c or d)
6 begin
7 temp1<=a&b;
8 temp2<=c&d;
9 y<=temp1|temp2;
10 end
11 endmodule
12 //由于非阻塞赋值语句在LHS更新前计算RHS的值,因此tmp1和tmp2仍是进入该
13
14 //always快是的值,而不是在该步仿真结束时将更新的值。输出y是刚进入always块
15
16 //时tmp1和tmp2的值,而不是在always块中经计算后得到的值。
17
18 module a04(y,a,b,c,d,tmp1,tmp2);
19 output y;
20 input a,b,c,d;
21 reg y,temp1,temp2;
22 always@(a or b or c or d)
23 begin
24 temp1<=a&b;
25 temp2<=c&d;
26 y<=temp1|temp2;
27 end
28 endmodule
29 //将tep1和tmp2加入敏感列表中后,现在输出的y是正确的,但是一个always块中有多次参数的传递,由此降低了仿真器的性能,只有在没有其他合理的方式的采用此方法
30 module a04(y,a,b,c,d);
31 output y;
32 input a,b,c,d;
33 reg y,temp1,temp2;
34 always@(a or b or c or d)
35 begin
36 temp1=a&b;
37 temp2=c&d;
38 y=temp1|temp2;
39 end
40 endmodule
41
42 //保证了经一次数据传输y的值是正确的,又提高了仿真效率
1.3 时序和组合的混合逻辑使用非阻塞赋值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 module nbex2(q,a,b,clk,rst_n);
2 output q;
3 input clk ,rst_n;
4 input a,b;
5 reg q;
6 always @(posedge ckl or negecge rst_n)
7 if(!rst_n)
8 q<=1'b0;//时序逻辑
9 else
10 q<=a^b;//异或,为组合逻辑
11 endmodule
12
13 /*采用两个always块实现以上逻辑也可以*/
14 module nbex2(q,a,b,clk,rst_n);
15 output q;
16 input clk ,rst_n;
17 input a,b;
18 reg q,y;
19 always @(a or b)
20 y=a^b;//组合逻辑
21 always @(posedge ckl or negecge rst_n)
22 if(!rst_n)
23 q<=1'b0;//时序逻辑
24 else
25 q<=y;//时序逻辑
26 endmodule
2、分频(倍频)问题
(1)、对clk进行二分频,得到control_pulse,在always(negedge clk)中检测control_pulse实现对control_pulse电平中间位置处采样,如图,同样也可作为门控时钟
(2)、锁相环倍频
(3)、奇数倍/偶数倍分频(来自互联网)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 `timescale 1ns/1ns
2 `define clk_cycle 5
3 `define N 11
4
5 module m1k;
6 // module m1k(rst_n,clk,k,clkk);
7 // input rst_n;
8 // input clk;
9 // input [3:0] k;//k分频
10 // reg clk1;
11 // reg clk2;
12
13 // output clkk;
14 // reg [3:0] k1;
15 // reg [3:0] k2;
16
17 reg rst_n;
18 reg clk;
19 reg [3:0] k;
20 //wire sys_clk;
21 wire clkk;
22 //wire clk_point;
23
24 reg clk1;
25 reg clk2;
26 //reg clk_point1;
27 //reg clk_point2;
28 //reg trigger_clk;
29 reg [3:0] k1;
30 reg [3:0] k2;
31 //reg [3:0] clk_point_count1;
32 //reg [3:0] clk_point_count2;
33 //assign sys_clk=clk;
34 initial
35 begin
36 rst_n=1;
37 clk=0;
38 k=11;
39 clk1=0;
40 clk2=0;
41 k1=0;
42 k2=0;
43 #20 rst_n=0;
44 #20 rst_n=1;
45 end
46 always #`clk_cycle clk=~clk;
47
48 always@(negedge rst_n or posedge clk)
49 begin
50 if(~rst_n)
51 begin
52 k1 <= 4'b0;
53 clk1 <= 1'b0;
54 end
55 else
56 begin
57 if(k1==(k-1))
58 clk1 <= ~clk1;
59 if(k1==((k>>1)-1'b1))//移位运算,此处相当于是k/2-1
60 clk1 <= ~clk1;
61
62 if(k1==k-1)
63 k1 <= 4'b0000;
64 else
65 k1 <= k1+4'b0001;
66
67
68 end
69
70 end
71
72 always@(negedge rst_n or negedge clk)
73 begin
74 if(~rst_n)
75 begin
76 k2 <= 4'b0;
77 clk2 <= 1'b0;
78 end
79 else
80 begin
81 if(k2 == k-1)
82 k2 <= 4'b0000;
83 else
84 k2 <= k2+4'b0001;
85 if(k2==k-1)
86 clk2 <= ~clk2;
87 if(k2==((k>>1)-1'b1))
88 clk2 <= ~clk2;
89 end
90
91 end
92 assign clkk = (k%2)?clk1&clk2:clk1;
93 endmodule
仿真波形如下:
3、门控时钟问题
4、状态机问题
用可综合的Verilog模块设计复杂的多输出状态机时常用的方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 module fsm(Clock, Reset, A, K2, K1); 2 input Clock, Reset, A; 3 output K2, K1; 4 reg K2, K1; 5 reg [1:0] state, nextState; 6 7 parameter 8 Idle = 2'b00, 9 Start = 2'b01, 10 Stop = 2'b10, 11 Clear = 2'b11; 12 //---每一个时钟沿产生一次可能的状态变化----- 13 14 always @(posedge Clock) 15 if (!Reset) 16 state <= Idle; 17 else 18 state <= nextState; 19 20 //-----产生下一状态的组合逻辑------- 21 always @(state or A)//由当前状态和当前输入决定下一个状态 22 case (state) 23 Idle: 24 if (A) 25 nextState = Start; 26 else 27 nextState = Idle; 28 Start: 29 if (!A) 30 nextState = Stop; 31 else 32 nextState = Start; 33 Stop: 34 if (A) 35 nextState = Clear; 36 else 37 nextState = Stop; 38 Clear: 39 if (!A) 40 nextState = Idle; 41 else 42 nextState = Clear; 43 default: 44 nextState = 2'bxx; 45 endcase 46 47 //----产生输出K1的组合逻辑-------- 48 always @(state or Reset or A)//由当前状态和当前输入决定K1的输出 49 if (!Reset) K1 = 0; 50 else 51 if (state == Clear && !A) //... 52 K1 = 1; 53 else 54 K1 = 0; 55 56 //----产生输出K2的组合逻辑-------- 57 always @(state or Reset or A)//由当前状态和当前输入决定K2的输出 58 if (!Reset) K2 = 0; 59 else 60 if (state == Stop && A) //... 61 K2 = 1; 62 else 63 K2 = 0; 64 65 endmodule
5、触发器问题
5.1 D触发器
5.1.1 边沿检测中用到的D触发器(来之CrazyBingo的博客)
5.1.11
正常工作,没有复位的情况下,工作流程如下:
(1)D触发器经过时钟clk的触发,输出trigger信号,保存了t0时刻的信号。
(2)同时由trigger通过非门输出信号,保留了当前时刻t1的触发信号
(3)经过与门输出信号pos_edge,neg_edge
a) 只有t0时刻为高,且t1时候为低的时候,与门输出高,此时为下降沿。
b) 只有to时候为低,且t1时候为高的时候,与门输出高,此时为上升沿。
当然,在复位的时刻,DFF被复位,无法检测触发信号。
5.1.22
一般为了防止触发信号的波动,加几级触发器,消除抖动,使得信号更稳定。
此例程中,相对于上图多了触发器。其用触发器对信号打慢两拍,使得触发信号然后在进行相关的处理;再来检测边沿的上升沿,下降沿。
(2)用verilog代码实现
edge_tech_design.v代码如下所示:
/*****************************************************
* Module Name : edge_tech_design.v
* Engineer : Crazy Bingo
* Target Device : EP2C8Q208C8
* Tool versions : Quartus II 11.0
* Create Date : 2011-6-25
* Revision : v1.0
* Description :
*****************************************************/
module edge_tech_design
(
input clk,
input rst_n,
input trigger,
output pos_edge,
output neg_edge
);
//Capture the rising_endge & falling_edge
reg trigger_r0,trigger_r1,trigger_r2;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
trigger_r0 <= 1'b0;
trigger_r1 <= 1'b0;
trigger_r2 <= 1'b0;
end
else
begin
trigger_r0 <= trigger;
trigger_r1 <= trigger_r0;
trigger_r2 <= trigger_r1;
end
end
assign pos_edge = trigger_r1 & ~trigger_r2;
assign neg_edge = ~trigger_r1 & trigger_r2;
endmodule
5.1.33没有十全十美的东西,也没有十全十美的电路、代码;本章节中所介绍的边沿检测技术亦如此。有如下缺陷:
(1)增大CLK信号可以增强边沿检测的效率,但不能滤去跳变的杂波。
(2)减少CLK可以有效滤去跳变的杂波,但不能及时检测到边沿跳变。
(3)增加DFF能更好的滤除杂波,寄存信号,但同时检测延时大。
6、算术运算问题
(1)、k=1010,k1=k>>1;k=1*2^3+0*2^2+1*2^1+0*2^0;k1=1*2^2+0*2^1+1*2^0;故由此分析,k1=k/2(当k为奇数的时候k1=k/2+1),即向右移动一位缩小两倍,同理,向左移动一位,扩大两倍
posted on 2012-02-28 13:59 lanlingshan 阅读(651) 评论(0) 编辑 收藏 举报