我是靠谱客的博主 专一啤酒,这篇文章主要介绍EDA设计(verilog)—— 连续数检测电路,现在分享给大家,希望可以做个参考。

题目描述:设计一个4 连续0 或者4 个连续1 的序列检测FSM(有限状态机),定义一个长序列,在七段管上分别显示检测的4 个连续0 和4 个连续1 的个数。显示连续1 和连续0 的个数在七段管上的显示,分别用函数和任务实现。

目录

  • 题目分析
  • 第一次尝试(只有函数时,逻辑最简单)
  • 有趣的尝试(十位使用函数,个位使用任务)
  • 最终定解


题目分析

分析一:如何去实现这个这个功能?
     对于连续数字的检测,根据题目描述,我们可以使用有限状态机来表示,那么我们可以对其进行抽象成如下的表示图:
在这里插入图片描述
在这张图中,我们分别使用后了9个状态来表示输入的状态,用带有数字的箭头表示状态之间的转移。从状态0 到 状态 9 分别表示为

状态值状态意义
0初始化状态,即模块遇到状态错误或者重置时的初始化状态,在正常工作时是不会进入到初始化状态的
1连续输入一个1
2连续输入两个1
3连续输入三个1
4连续输入四个或四个以上1
5连续输入一个0
6连续输入两个0
7连续输入三个0
8连续输入四个或者四以上个0

当状态进行到4或者8的时候就可以有信号输出了。


分析二:如何显示出当前有几个1 或者 0?
     显然我们需要定义一个数值变量来记录下0 和 1的个数,再对这个数进行分解,输出到七段管上,当然,标准一点的写法可以写成若干10位计数器的串联,对于这道题来说,用取模10 除以10 这样的分数方法当然也可以完成。最后就是调用七段管了。


分析三:函数和任务??
     这道题甲方又提出了令人费解的需求,这两个不是差不多吗?写一个调用多次就挺好,还非要搞俩个。在增加代码复杂度的同时,又明显劣化了性能。(这道题显然使用函数会好做一些)可谓一石二鸟。但没办法,只好先分析一下函数和任务的主要不同。

函数
对于一个函数来说他的写法是这样的

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module test(a, b, out); input[1:0] a; // 时钟信号 input[1:0] b; output[2:0] out; function[2:0] my_add; input[2:0] a; input[2:0] b; my_add = a + b; endfunction assign out = my_add(a, b); endmodule

可以看到对于函数来说,在其内部自己的函数名就是返回值,我们会将结果直接赋予他,它的使用有以下注意点:

注意点
(1)函数定义只能在模块中完成,不能出现在过程块中;
(2)函数至少要有一个输入端口;不能包含输出端口和双向端口;
(3) 在函数结构中, 不能使用任何形式的时间控制语句 (#、 wait 等)
(4)函数结构中不能使用 disable中止语句;
(5)函数定义结构体中不能出现过程块语句(always 语句) ;
(6)函数内部可以调用函数,但不能调用任务。
(7)函数调用语句不能单独作为一条语句出现,只能作为赋值语句的右端操作数。

任务

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module test(a, b, out); input[1:0] a; // 时钟信号 input[1:0] b; output reg[2:0] out; // 将数值变成七段管中的明暗状态 task my_add; input[1:0] a; input[1:0] b; output[2:0] c; c = a + b; endtask always @(a,b) begin my_add(a, b, out); end endmodule

可以看出,对于一个任务来说,我们的输出输出都是在任务内部定义的,在调用的时候,我们要注意的是传入变量的顺序应该和变量在内部定义的顺序一致,任务还有以下需要注意以下内容。

注意点
(1)在第一行“task”语句中不能列出端口名称,而应该统一在任务体内部定义
(2)任务的输入、输出端口和双向端口数量不受限制,甚至可以没有输入、输出以及双向端口。
(3)在任务定义的描述语句中,可以使用出现不可综合操作符合语句。
(4)在任务中可以调用其他的任务或函数,也可以调用自身。
(5)在任务定义结构内不能出现 initial和 always过程块。
(6)在任务定义中可以出现“disable 中止语句” ,任务会被中断,程序流程将返回到调用任务的地方继续向下执行。(非综合)
(7)任务调用语句只能出现在过程块( initial和 always)内;
(8)可综合任务只能实现组合逻辑,也就是说调用可综合任务的时间为“0” 。出现不可综合代码是调用时间可不为“0”


第一次尝试(只有函数时,逻辑最简单)

综合代码

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
module fsm(clk,clr,sign,out,qout,tout1, tout2, num, pn); input clk; // 时钟信号 input clr; // 复位信号 input sign; // 输入信号 output reg out; // 输出信号 output reg[3:0] qout; // 状态型号,由于寄存器状态一个有9种, 所以长度为4 output[6:0] tout1; output[6:0] tout2; // 进行连续计数的变量 output reg [7:0] num; output reg pn; // 输出信号的正负 // 函数:将数值变成七段管中的明暗状态 function[6:0] dig2tube; input[3:0] in; case(in) 0: dig2tube = 8'b0000001; 1: dig2tube = 8'b0011111; 2: dig2tube = 8'b0010010; 3: dig2tube = 8'b0000110; 4: dig2tube = 8'b1001100; 5: dig2tube = 8'b0100100; 6: dig2tube = 8'b0100000; 7: dig2tube = 8'b0001111; 8: dig2tube = 8'b0000000; 9: dig2tube = 8'b0000100; endcase endfunction //此过程定义状态转换,转化过程见状态转移图 always @(posedge clk or posedge clr) begin pn <= sign; // 表示现在输出num是在计数0还是在计数1 if(clr) qout<=0; //异步复位 else begin // 每个状态对于信号的高低都有两种不同的走向 if(sign)begin // 如果是 1 case(qout) // 根据当前状态修改qout(状态)和 num 计数 4'b0000: begin qout<= 4'b0001; num <=1; end // 0 4'b0001: begin qout<= 4'b0010; num <=2; end // 1 4'b0010: begin qout<= 4'b0011; num <=3; end // 2 4'b0011: begin qout<= 4'b0100; num <=4; end // 3 4'b0100: begin qout<= 4'b0100; num <=num + 1; end // 4 4'b0101: begin qout<= 4'b0001; num <=1; end // 5 4'b0110: begin qout<= 4'b0001; num <=1; end // 6 4'b0111: begin qout<= 4'b0001; num <=1; end // 7 4'b1000: begin qout<= 4'b0001; num <=1; end // 8 default: begin qout<= 0; num <=0; end // 默认 endcase end else begin // 如果信号是 0 case(qout) // 根据当前状态修改qout(状态)和 num 计数 4'b0000: begin qout<= 4'b0101; num <=1; end // 0 4'b0001: begin qout<= 4'b0101; num <=1; end // 1 4'b0010: begin qout<= 4'b0101; num <=1; end // 2 4'b0011: begin qout<= 4'b0101; num <=1; end // 3 4'b0100: begin qout<= 4'b0101; num <=1; end // 4 4'b0101: begin qout<= 4'b0110; num <=2; end // 5 4'b0110: begin qout<= 4'b0111; num <=3; end // 6 4'b0111: begin qout<= 4'b1000; num <=4; end // 7 4'b1000: begin qout<= 4'b1000; num <=num + 1; end // 8 default: begin qout<= 0; num <=0; end // 默认 endcase end end end // 此过程产生输出逻辑 always @(qout) begin case(qout) 4'b1000: out=1'b1; 4'b0100: out=1'b1; default: out=1'b0; endcase end // 将寄存器里的数值通过函数赋值到输出上 assign tout1 = dig2tube(num/10%10); assign tout2 = dig2tube(num%10); endmodule

测试代码

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
`timescale 1 ps/ 1 ps module fsm_vlg_tst(); reg clk; reg clr; reg sign; wire out; wire pn; wire [3:0] qout; wire [6:0] tout1; wire [6:0] tout2; wire [7:0] num; fsm i1 ( .clk(clk), .clr(clr), .out(out), .pn(pn), .qout(qout), .sign(sign), .num(num), .tout1(tout1), .tout2(tout2) ); function[3:0] tube2dig; input[6:0] in; case(in) 8'b0000001: tube2dig = 0; 8'b0011111: tube2dig = 1; 8'b0010010: tube2dig = 2; 8'b0000110: tube2dig = 3; 8'b1001100: tube2dig = 4; 8'b0100100: tube2dig = 5; 8'b0100000: tube2dig = 6; 8'b0001111: tube2dig = 7; 8'b0000000: tube2dig = 8; 8'b0000100: tube2dig = 9; endcase endfunction initial begin clr = 0;clk=0; sign =0; #2 clr = 1; #2 clr =0; #116 sign = 1; #200 $stop; end always begin #5 clk =~ clk; end always @(num) begin // 这里延时一秒是由于 out 是reg 类型, 当变化的瞬间, out 输出的是上一时刻的,所以要延时一下 #1 $display("sign=%d -- num=%d -- out=%d -- tout1=%d -- tout2=%d", sign, num, out, tube2dig(tout1), tube2dig(tout2)); end endmodule

效果

在这里插入图片描述
在这里插入图片描述



有趣的尝试(十位使用函数,个位使用任务)

综合代码

没有修改的地方已注释。可以看到,由于task要求在过程块里使用,所以说我们必须把输出也给改成reg 类型。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
//module fsm(clk,clr,sign,out,qout,tout1, tout2, num, pn); // // input clk; // 时钟信号 // input clr; // 复位信号 // input sign; // 输入信号 // output reg out; // 输出信号 // output reg[3:0] qout; // 状态型号,由于寄存器状态一个有9种, 所以长度为4 // output[6:0] tout1; output reg [6:0] tout2; // 进行连续计数的变量 // output reg [7:0] num; // output reg pn; // 输出信号的正负 // // // // 函数:将数值变成七段管中的明暗状态 // function[6:0] dig2tube; // input[3:0] in; // case(in) // 0: dig2tube = 8'b0000001; // 1: dig2tube = 8'b0011111; // 2: dig2tube = 8'b0010010; // 3: dig2tube = 8'b0000110; // 4: dig2tube = 8'b1001100; // 5: dig2tube = 8'b0100100; // 6: dig2tube = 8'b0100000; // 7: dig2tube = 8'b0001111; // 8: dig2tube = 8'b0000000; // 9: dig2tube = 8'b0000100; // endcase // endfunction // // //此过程定义状态转换,转化过程见状态转移图 // always @(posedge clk or posedge clr) begin // pn <= sign; // if(clr) qout<=0; //异步复位 // else begin // 每个状态对于信号的高低都有两种不同的走向 // if(sign)begin // 如果是 1 // case(qout) // 根据当前状态修改qout(状态)和 num 计数 // 4'b0000: begin qout<= 4'b0001; num <=1; end // 0 // 4'b0001: begin qout<= 4'b0010; num <=2; end // 1 // 4'b0010: begin qout<= 4'b0011; num <=3; end // 2 // 4'b0011: begin qout<= 4'b0100; num <=4; end // 3 // 4'b0100: begin qout<= 4'b0100; num <=num + 1; end // 4 // 4'b0101: begin qout<= 4'b0001; num <=1; end // 5 // 4'b0110: begin qout<= 4'b0001; num <=1; end // 6 // 4'b0111: begin qout<= 4'b0001; num <=1; end // 7 // 4'b1000: begin qout<= 4'b0001; num <=1; end // 8 // default: begin qout<= 0; num <=0; end // 默认 // endcase // end else begin // 如果信号是 0 // case(qout) // 根据当前状态修改qout(状态)和 num 计数 // 4'b0000: begin qout<= 4'b0101; num <=1; end // 0 // 4'b0001: begin qout<= 4'b0101; num <=1; end // 1 // 4'b0010: begin qout<= 4'b0101; num <=1; end // 2 // 4'b0011: begin qout<= 4'b0101; num <=1; end // 3 // 4'b0100: begin qout<= 4'b0101; num <=1; end // 4 // 4'b0101: begin qout<= 4'b0110; num <=2; end // 5 // 4'b0110: begin qout<= 4'b0111; num <=3; end // 6 // 4'b0111: begin qout<= 4'b1000; num <=4; end // 7 // 4'b1000: begin qout<= 4'b1000; num <=num + 1; end // 8 // default: begin qout<= 0; num <=0; end // 默认 // endcase // end // end dig2tube2(num%10, tout2); // end // 此过程产生输出逻辑 // always @(qout) begin // case(qout) // 4'b1000: out=1'b1; // 4'b0100: out=1'b1; // default: out=1'b0; // endcase // end // 将寄存器里的数值通过函数赋值到输出上 assign tout1 = dig2tube(num/10%10); // 任务:将数值变成七段管中的明暗状态 task dig2tube2; input[3:0] _in; output[6:0] _out; case(_in) 0: _out = 8'b0000001; 1: _out = 8'b0011111; 2: _out = 8'b0010010; 3: _out = 8'b0000110; 4: _out = 8'b1001100; 5: _out = 8'b0100100; 6: _out = 8'b0100000; 7: _out = 8'b0001111; 8: _out = 8'b0000000; 9: _out = 8'b0000100; endcase endtask //endmodule

出现的问题

在这里插入图片描述
可以看到,由于加了一个reg 把out2 放到了always 里直接导致了out2 与其他变量不同步。

问题的原因

     出现这个问题的本质是由于输出和输出之间经过了两次寄存器,一次是num, 一次是out2本身,使得他发生变化要比经过一次寄存器的生效慢,现在要解决的这个问题就要让输入信号和out2 之间进过的寄存器减少。



最终定解

     经过了两次测试我们认识到了实现函数和任务时的一些基本注意点,之后就要完成甲方这不合理的请求了。为了让管子的输出同步,在使用task的时候只能使用一次寄存器,即在本题中,我们直接使用最后的七段管,去除了中间的计数器。这样带来了代码的复杂性。即我们需要将七段管的值变回数值用于计算。而在使用函数时,则不用。

综合代码

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
module fsm(clk,clr,sign,out,qout,num1, tubep1, tubep2, tuben1, tuben2); input clk; // 时钟信号 input clr; // 复位信号 input sign; // 输入信号 output reg out; // 输出信号 output reg[3:0] qout; // 状态型号,由于寄存器状态一个有9种, 所以长度为4 output reg [7:0] num1; // 用于统计 output [6:0] tubep1; // 用于输出1 的计数 output [6:0] tubep2; // 用于输出1 的计数 output reg [6:0] tuben1; // 用于输出0 的计数 output reg [6:0] tuben2; // 用于输出0 的计数 // output reg pn; // 由于这时候有两个输出,无需考虑正负 // 函数:将数值变成七段管中的明暗状态 function[6:0] dig2tube; input[3:0] in; case(in) 0: dig2tube = 8'b0000001; 1: dig2tube = 8'b0011111; 2: dig2tube = 8'b0010010; 3: dig2tube = 8'b0000110; 4: dig2tube = 8'b1001100; 5: dig2tube = 8'b0100100; 6: dig2tube = 8'b0100000; 7: dig2tube = 8'b0001111; 8: dig2tube = 8'b0000000; 9: dig2tube = 8'b0000100; endcase endfunction // 函数:将七段管的明暗状态 变成 数值 function[3:0] tube2dig; input[6:0] in; case(in) 8'b0000001: tube2dig = 0; 8'b0011111: tube2dig = 1; 8'b0010010: tube2dig = 2; 8'b0000110: tube2dig = 3; 8'b1001100: tube2dig = 4; 8'b0100100: tube2dig = 5; 8'b0100000: tube2dig = 6; 8'b0001111: tube2dig = 7; 8'b0000000: tube2dig = 8; 8'b0000100: tube2dig = 9; endcase endfunction // 任务:将数值变成七段管中的明暗状态 task dig2tube2; input[3:0] _in; output[6:0] _out; case(_in) 0: _out = 8'b0000001; 1: _out = 8'b0011111; 2: _out = 8'b0010010; 3: _out = 8'b0000110; 4: _out = 8'b1001100; 5: _out = 8'b0100100; 6: _out = 8'b0100000; 7: _out = 8'b0001111; 8: _out = 8'b0000000; 9: _out = 8'b0000100; endcase endtask //此过程定义状态转换,转化过程见状态转移图 always @(posedge clk or posedge clr) begin if(clr) qout<=0; //异步复位 else begin // 每个状态对于信号的高低都有两种不同的走向 if(sign)begin // 如果是 1 tuben1 <= dig2tube(0); tuben2 <= dig2tube(0); case(qout) // 根据当前状态修改qout(状态)和 num 计数 4'b0000: begin qout<= 4'b0001; end // 0 4'b0001: begin qout<= 4'b0010; end // 1 4'b0010: begin qout<= 4'b0011; end // 2 4'b0011: begin qout<= 4'b0100; num1 <= 1; end // 3 4'b0100: begin qout<= 4'b0100; num1 <=num1 + 1; end // 4 4'b0101: begin qout<= 4'b0001; end // 5 4'b0110: begin qout<= 4'b0001; end // 6 4'b0111: begin qout<= 4'b0001; end // 7 4'b1000: begin qout<= 4'b0001; end // 8 default: begin qout<= 0; end // 默认 endcase end else begin // 如果信号是 0 num1 <= 0; case(qout) // 根据当前状态修改qout(状态)和 num 计数 4'b0000: begin qout<= 4'b0101; end // 0 4'b0001: begin qout<= 4'b0101; end // 1 4'b0010: begin qout<= 4'b0101; end // 2 4'b0011: begin qout<= 4'b0101; end // 3 4'b0100: begin qout<= 4'b0101; end // 4 4'b0101: begin qout<= 4'b0110; end // 5 4'b0110: begin qout<= 4'b0111; end // 6 4'b0111: begin qout<= 4'b1000; end // 7 4'b1000: begin qout<= 4'b1000; end // 8 default: begin qout<= 0; end // 默认 endcase if(qout>=7) begin // 如果是从 5 - 8状态转变过来的,就让管子里的数字增加 // 开始增加数值 if(tube2dig(tuben2) == 9) begin // 如果个位是9 dig2tube2( 4'b0000 , tuben2); // 个位变为0 if(tube2dig(tuben1) == 9) begin // 如果是 99 dig2tube2( 4'b0000 , tuben1); // 十位也变为 0 end else begin dig2tube2( tube2dig(tuben1) + 1 , tuben1); // 如果十位不是9 各位 是9,个位自增, 十位不变 end end else begin dig2tube2( tube2dig(tuben2) + 1 , tuben2); // 如果个位不是9, 考虑进位,直接个位自增 end // 数值增加结束 end else begin dig2tube2(4'b0000, tuben2); dig2tube2(4'b0000, tuben1); end end end end // 此过程产生输出逻辑 always @(qout) begin case(qout) 4'b1000: out=1'b1; 4'b0100: out=1'b1; default: out=1'b0; endcase end // 将num1寄存器里的数值通过函数赋值到 assign tubep1 = dig2tube(num1/10%10); assign tubep2 = dig2tube(num1%10); endmodule

测试代码

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
`timescale 1 ps/ 1 ps module fsm_vlg_tst(); reg clk; reg clr; reg sign; wire [7:0] num1; wire out; wire [3:0] qout; wire [6:0] tuben1; wire [6:0] tuben2; wire [6:0] tubep1; wire [6:0] tubep2; fsm i1 ( .clk(clk), .clr(clr), .num1(num1), .out(out), .qout(qout), .sign(sign), .tuben1(tuben1), .tuben2(tuben2), .tubep1(tubep1), .tubep2(tubep2) ); // 函数:将七段管的明暗状态 变成 数值 function[3:0] tube2dig; input[6:0] in; case(in) 8'b0000001: tube2dig = 0; 8'b0011111: tube2dig = 1; 8'b0010010: tube2dig = 2; 8'b0000110: tube2dig = 3; 8'b1001100: tube2dig = 4; 8'b0100100: tube2dig = 5; 8'b0100000: tube2dig = 6; 8'b0001111: tube2dig = 7; 8'b0000000: tube2dig = 8; 8'b0000100: tube2dig = 9; endcase endfunction initial begin clr = 0;clk=0; sign =0; #2 clr = 1; #2 clr =0; #146 sign = 1; #4; #146 sign = 0; #4; #200 $stop; end always begin #5 clk =~ clk; end always @(posedge clk) begin // 这里延时一秒是由于 out 是reg 类型, 当变化的瞬间, out 输出的是上一时刻的,所以要延时一下 #1 $display("sign:%d -- out:%d -- the num of continuous low:%d%d -- the num of continuous high:%d%d", sign, out, tube2dig(tuben1), tube2dig(tuben2), tube2dig(tubep1), tube2dig(tubep2)); end endmodule

效果图

文本输出
在这里插入图片描述

波形图整体
在这里插入图片描述
变化细节
在这里插入图片描述

最后

以上就是专一啤酒最近收集整理的关于EDA设计(verilog)—— 连续数检测电路的全部内容,更多相关EDA设计(verilog)——内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(62)

评论列表共有 0 条评论

立即
投稿
返回
顶部