07-从计数器到可控线性序列机

设计功能

使led每隔10ms进行8个状态的亮灭,亮灭的状态可以由用户控制,亮灭的时间长短也可以有用户控制。

代码

设计文件

 1 module counter_led_4(
 2     Clk,
 3     Reset_n,
 4     Ctrl, //LED状态控制
 5     Time, //LED状态变化的时间
 6     led
 7 );
 8     input Clk;
 9     input Reset_n;
10     input [7:0]Ctrl;
11     input [31:0]Time;
12     reg EN;
13     output reg led;
14     
15     reg [18:0]counter0;//EN信号定时
16     reg [31:0]counter;
17     reg [2:0]counter2;
18     //EN信号10ms周期定时
19     always@(posedge Clk or negedge Reset_n)begin
20     if(!Reset_n)
21         counter0 <= 0;
22     else if(counter0 == 5000000-1) //10ms周期
23         counter0 <= 0;
24     else
25         counter0 <= counter0 + 1'd1;
26     end
27    //EN逻辑功能:EN保持高电平时LED工作。     
28    always@(posedge Clk or negedge Reset_n)begin
29     if(!Reset_n)
30         EN <= 0;
31     else if(counter0 == 0)
32         EN <= 1;
33     //else if(counter2 == 7) //有问题的写法
34     else if((counter2 == 7)&&(counter == Time -1))
35         EN <= 0;
36     end
37     //LED状态变化的时间,例如10us改变一次状态。
38     //tb中更改Time的值来实现。
39     always@(posedge Clk or negedge Reset_n)begin
40     if(!Reset_n)
41         counter <= 0;
42     else if(EN)begin
43         if(counter == Time -1) //10us定时
44             counter <= 0;
45         else
46             counter <= counter + 1'd1;
47         end
48     else
49         counter <= 0; //不在EN信号有效电平(高电平)期间,计数值一律清零。
50     end
51     //每隔10us 赋予LED不同的值   
52     always@(posedge Clk or negedge Reset_n)begin
53         if(!Reset_n)
54             counter2 <= 0;
55         else if(EN)begin
56             if(counter == Time -1)//计时10usled控制信号的值+1。
57                 counter2 <= counter2 + 1'd1;
58             end
59         else
60             counter2 <= 0;//不在EN信号有效电平(高电平)期间,计数值一律清零。
61         end
62     //每隔10us 赋予LED不同的值   
63     always@(posedge Clk or negedge Reset_n)begin
64         if(!Reset_n)
65             led <= 0;
66         else case (counter2)
67             0:led <= Ctrl[7];
68             1:led <= Ctrl[6];
69             2:led <= Ctrl[5];
70             3:led <= Ctrl[4];
71             4:led <= Ctrl[3];
72             5:led <= Ctrl[2];
73             6:led <= Ctrl[1];
74             7:led <= Ctrl[0];
75             default:led <= led;
76                 endcase
77         end  
78             
79 endmodule 

测试文件

 1 `timescale 1ns / 1ns
 2 module counter_led_tb;
 3     reg s_Clk;
 4     reg s_Reset_n;
 5     reg [7:0]s_Ctrl;
 6     reg [31:0]s_Time;
 7     wire s_led;
 8     
 9 counter_led_4 counter_led_inst0(
10     .Clk(s_Clk),
11     .Reset_n(s_Reset_n),
12     .Ctrl(s_Ctrl),
13     .Time(s_Time),
14     .led(s_led)
15 );
16     initial s_Clk = 1;
17     always #10 s_Clk = ~s_Clk;
18     initial begin
19         s_Reset_n = 0;
20         s_Ctrl = 0;
21         s_Time = 0;
22         #201;
23         s_Reset_n = 1;
24         #201;
25         s_Ctrl = 8'b1011_0010;
26         s_Time = 500;
27         #20000;
28         s_Ctrl = 8'b1010_0110;
29         #2000000000;
30         end
31 
32 endmodule

仿真结果

正确的仿真结果

仿真结果中,led的亮灭状态与预期结果相同。

有问题的仿真分析

仿真结果中看不到LED的位7的状态,问题出在写成了设计文件第33行else if(counter2 == 7) //有问题的写法,这样的写法有问题,在仿真中led位7的状态不可见,放大时间轴其实可以看到led位7的状态,看不到的原因是只显示了两个时钟周期,时间太短了。

为什么会这样呢?

当counter == 499时,counter此时的值为6(不是7,因为这里是499其实时498+1变成499的,实际上在这个时钟上升沿进行采样判断时,counter的值是498,这是一个极小的时间,ns级别,所以counter2在进行判断的时候,并不符合499的条件,这其中涉及硬件内部结构,D触发器传递从输入到输出是需要一定时间的)。

接着到下一个时钟上升沿时,counter == 499,被清零,counter2判断符合条件计数值+1变成7,这时EN的值还是保持高电平,而不是低电平,原因和上面一样。

接着再到下一个时钟上升沿时,因为EN实际上在上升沿的瞬间还是高电平,所以counter还会+1,变成1,而counter2继续保持7。

最后再走一个时钟周期,这时候EN已经变成低电平了,counter、counter2都清零了。

那么如何解决这个问题?

上面分析到,位7只保持了两个时钟周期,而他需要保持多久?他需要保持10us,也就是500个时钟周期,所以可以利用counter,让他继续计数,计时满10us,才将EN拉低。修改代码为else if((counter2 == 7)&&(counter == Time -1))。

修改后的仿真后结果

当counter=499后,下一个时钟周期EN拉低,counter2和counter都清零了,注意这里的清零并不是由于EN起的作用(原理分析和前面所说的一样),而是由counter计数到499后,就会被自身规则清零,而couter2计数到7后,因为counter2是一个3位数据,加到7再加1就会溢出变成0。

posted @ 2022-06-16 08:45  #hua  阅读(63)  评论(0编辑  收藏  举报