Verilog实验报告 下载本文

2014-2015-2-G02A3050-1

电子电路设计训练(数字EDA部分)

实验报告

( 2015 年5 月 20 日)

教学班

学号 姓名 组长 签名 成绩

自动化科学与电气工程学院

目 录

目 录 .............................................................................................................................................. 1 实验一、简单组合逻辑和简单时序逻辑 ....................................................................................... 1

1.1 实验任务1——简单组合逻辑 ................................................................................. 1

1.1.1 实验要求 ......................................................................................................... 1 1.1.2 模块的核心逻辑设计 ..................................................................................... 1 1.1.3 测试程序的核心逻辑设计 ............................................................................. 1 1.1.4 仿真实验关键结果及其解释 ......................................................................... 2 1.2 实验任务2——简单时序逻辑 ................................................................................. 3

1.2.1 实验要求 ......................................................................................................... 3 1.2.2 模块的核心逻辑设计 ..................................................................................... 3 1.2.3 测试程序的核心逻辑设计 ............................................................................. 3 1.2.4 仿真实验关键结果及其解释 ......................................................................... 4 1.3 实验小结 .................................................................................................................... 4

实验二、条件语句和always过程块 .............................................................................................. 5

2.1 实验任务1——利用条件语句实现计数分频时序电路 ......................................... 5

2.1.1 实验要求 ......................................................................................................... 5 2.1.2 模块的核心逻辑设计 ..................................................................................... 5 2.1.3 测试程序的核心逻辑设计 ............................................................................. 6 2.1.4 仿真实验关键结果及其解释 ......................................................................... 7 2.2 实验任务2——用always块实现较复杂的组合逻辑电路 .................................... 8

2.2.1 实验要求 ......................................................................................................... 8 2.2.2 模块的核心逻辑设计 ..................................................................................... 8 2.2.3 测试程序的核心逻辑设计 ............................................................................. 9 2.2.4 仿真实验关键结果及其解释 ....................................................................... 10 2.3 实验小结 .................................................................................................................. 11

实验三、赋值、函数和任务 ......................................................................................................... 12

3.1 实验任务1——阻塞赋值与非阻塞赋值的区别 ................................................... 12

3.1.1 实验要求 ....................................................................................................... 12 3.1.2 模块的核心逻辑设计 ................................................................................... 12 3.1.3 测试程序的核心逻辑设计 ........................................................................... 13 3.1.4 仿真实验关键结果及其解释 ....................................................................... 14 3.2 实验任务2——在Verilog HDL中使用函数 .......................................................... 16

3.2.1 实验要求 ....................................................................................................... 16 3.2.2 模块的核心逻辑设计 ................................................................................... 16 3.2.3 测试程序的核心逻辑设计 ........................................................................... 18 3.2.4 仿真实验关键结果及其解释 ....................................................................... 19 3.3 实验任务3——在Verilog HDL中使用任务 .......................................................... 20

3.3.1 实验要求 ....................................................................................................... 20 3.3.2 模块的核心逻辑设计 ................................................................................... 20 3.2.3 测试程序的核心逻辑设计 ........................................................................... 21

3.2.4 仿真实验关键结果及其解释 ....................................................................... 22 3.3 实验小结 .................................................................................................................. 22

实验四、有限状态机 ..................................................................................................................... 23

4.1 实验任务1——基于状态机的串行数据检测器 ................................................... 23

4.1.1 实验要求 ....................................................................................................... 23 4.1.2 模块的核心逻辑设计 ................................................................................... 23 4.1.3 测试程序的核心逻辑设计 ........................................................................... 25 4.1.4 仿真实验关键结果及其解释 ....................................................................... 26 4.2 实验任务2——楼梯灯 ........................................................................................... 26

4.2.1 实验要求 ....................................................................................................... 26 4.2.2 模块的核心逻辑设计 ................................................................................... 27 4.2.3 测试程序的核心逻辑设计 ........................................................................... 31 4.2.4 仿真实验关键结果及其解释 ....................................................................... 32 4.3 实验小结 .................................................................................................................. 34

实验一、简单组合逻辑和简单时序逻辑

1.1 实验任务1——简单组合逻辑

1.1.1 实验要求

(1)设计一个两位数据比较器,比较两个数据a和b。若两数据相同,则给出结果1,否则给出结果0。

(2)设计一个字节(8位)的比较器,比较两个字节a[7:0]和b[7:0]的大小。若a大于b,则输出高电平,否则输出低电平。

1.1.2 模块的核心逻辑设计

(1)两位数据比较器

assign equal=(a==b)?1:0; //用连续赋值语句assign对结果equal赋值,a=b时,equal输出为1,否则为0

(2)字节数据比较器

assign res=(a>b)?1:0; //用连续语句assign对结果equal赋值,a>b时equal输出为1,否则输出为0

1.1.3 测试程序的核心逻辑设计

(1)两位数据比较器

always #50 clock=~clock; //产生周期性跳变的时钟,50个时间单位跳变一次 always@(negedge clock) //always后的语句表示时序控制,每次时钟下降沿时刻产生不同的a和b

begin

a={$random}%2;

b={$random}%2; //每次随机产生a和b end

1

initial

begin #100000000 $stop; end //系统任务,暂停仿真以观察波形 (2)字节数据比较器 a={$random}%6;

b={$random}%6; //a和b从0~255共256个数中随机产生,即可生成8位字节数据

1.1.4 仿真实验关键结果及其解释

(1)两位数据比较器

图 1两位数据比较器波形图

如图1所示,a和b相同时equal输出为高电平,否则输出低电平。 (2)字节数据比较器

图 2 字节数据比较器波形图

2

如图2所示,a>b时,res输出高电平,否则res输出低电平。

1.2 实验任务2——简单时序逻辑

1.2.1 实验要求

设计一个分频器,将时钟波形二分频。

1.2.2 模块的核心逻辑设计

always@(posedge clk_in) //always语句后表示时序控制,每次clk_in时钟上升沿时刻进行动作

begin

if(! reset) clk_out=0; //reset信号为低电平时,输出清零

else clk_out=~clk_out; //reset为高电平时,输出时钟clk_out在输入时钟clk_in的上升沿时刻翻转

end

1.2.3 测试程序的核心逻辑设计

always #`clk_cycle clk=~clk; //产生输入时钟 initial begin clk=0; reset=1;

#10 reset=0; //reset给低电平,输出清零 #110 reset=1; //reset复位

#100000 $stop; //系统任务,暂停仿真以便观察波形 end

3

1.2.4 仿真实验关键结果及其解释

图 3 二分频器的波形图

如图3所示,输入时钟clk被二分频输出。

1.3 实验小结

通过实验一,我掌握了如下内容: 1)assign连续赋值语句的使用。 2)always, initial块的使用。 3)reg, wire等数据类型的适用范围 4)调用被测试模块的方法

4

实验二、条件语句和always过程块

2.1 实验任务1——利用条件语句实现计数分频时序电路

2.1.1 实验要求

(1)设计20分频计数器,将10MHz的时钟分频为500kHz的时钟。 (2)利用10MHz的时钟,设计一个给定单周期形状的周期波形。

2.1.2 模块的核心逻辑设计

(1)20分频计数器 begin

if(j==9) //对计数器进行判断,计十个数翻转一次,则一个周期计20个数,即实现20分频 begin

j<=0; //输出时钟翻转的同时计数器置零 F500K<=~F500K; end else

j<=j+1; //若还没计到十个数,继续计数 end

(2)给定单周期形状的波形 begin

if(j<=20)

begin

FDIV<=0;

j<=j+1; //前20个输入时钟周期,计数器计数,但输出不跳变 end

else if((j>20)&&(j<=30))

5

begin

FDIV<=1;

j<=j+1; //中间10个时钟周期输出跳变成高电平,保持计数 end

else if((j>30)&&(j<=50))

begin FDIV<=0;

j<=j+1; //后20个时钟周期输出跳变成低电平,保持计数

end else

j<=0; //计数器清零

end

2.1.3 测试程序的核心逻辑设计

(1)20分频计数器

always #`clk_cycle F10M_clk=~F10M_clk; //产生输入的10MHz时钟

initial

begin end

RESET=1; F10M_clk=0;

#100 RESET=0; //reset给低电平,输出清零 #100 RESET=1; //reset复位

#10000 $stop; //系统任务,暂停仿真以便观察波形

(2)给定单周期形状的波形 begin

RESET=1; F10M_clk=0; #100 RESET=0;

6

#100 RESET=1; #100000 $stop;

end //与(1)一致

2.1.4 仿真实验关键结果及其解释

(1)20分频计数器

图 4 20分频计数器波形图

如图4所示,10MHz的时钟F10M被20分频成500kHz的时钟F500k。 (2)给定单周期形状的波形

图 5 给定单周期形状的波形图

如图5所示,生成了题目要求形状的周期波形图。

7

2.2 实验任务2——用always块实现较复杂的组合逻辑电路

2.2.1 实验要求

(1)设计一个指令译码电路,对输入数据执行相应的操作,包括加、减、与、或和求反。

(2)运用always块设计一个8路数据选择器。要求:每路输入数据与输出数据均为4位2进制数,当选择开关(至少3位)或输入数据发生变化时,输出数据也相应变化。

2.2.2 模块的核心逻辑设计

(1)指令译码电路

always@(opcode or a or b) //电平敏感的always块,当输入数据a,b或控制信号opcode变化时,输出发生变化

begin

case(opcode)

`plus: out=a+b; //控制信号为'plus时,输出等于a+b `minus: out=a-b; //控制信号为'minus时,输出等于a-b `band: out=a&b; //控制信号为'band时,输出等于a&b `bor: out=a|b; //控制信号为'bor时,输出等于a|b `unegate:out=~a; //控制信号为'unegate时,输出等于~a default: out=8'hx; //未收到指令时,输出任意态 endcase

(2)8路数据选择器

always@(ctl or a0 or a1 or a2 or a3 or a4 or a5 or a6 or a7) //电平敏感模块,控制信号ctl或输入a0~a7变化时,输出发生变化

begin case(ctl)

`ctl0: out=a0;

8

`ctl1: out=a1; `ctl2: out=a2; `ctl3: out=a3; `ctl4: out=a4; `ctl5: out=a5; `ctl6: out=a6;

`ctl7: out=a7; //控制端为ctl0~ctl7对应输出a0~a7 default: out=4'dx; //未收到指令时,输出任意态 endcase

2.2.3 测试程序的核心逻辑设计

(1)指令译码电路 begin

a={$random}%6; //从0~255共256个数中随机生成一个数作为输入a b={$random}%6; //从0~255共256个数中随机生成一个数作为输入b opcode=3'h0; //控制信号设为初值0,即'plus,求和 repeat(times) //repeat循环语句使控制及输入信号重复变化

begin

#100 a={$random}%6;

b={$random}%6;

opcode=opcode+1; //每一时钟到来时,输入a,b改变一随机数,

控制信号+1

end

#100 $stop; //系统任务,暂停仿真以观察输出波形 end

(2)8路数据选择器 begin

a0={$random}; a1={$random};

9

a2={$random}; a3={$random}; a4={$random}; a5={$random}; a6={$random};

a7={$random}; //从0~15中随机生成输入a0~a7 ctl=3'd0; //控制端置ctl0

repeat(times) //repeat语句重复改变输入 begin

#100 a0={$random}; a1={$random}; a2={$random}; a3={$random}; a4={$random}; a5={$random}; a6={$random};

a7={$random}; //随机生成a0~a7 ctl=ctl+1; //控制端每次加1 end #100 $stop; end

2.2.4 仿真实验关键结果及其解释

(1)指令译码电路

10

图 6 指令译码电路波形

指令译码电路输出波形如图所示。控制信号opcode为0时,输出为a+b;控制信号opcode为1时,输出为a-b;......以此类推。

(2)8路数据选择器

图 7 8选1数据选择器波形图

8路数据选择器输出波形如图7所示,控制端ctl为0~7时对应输出a0~a7。

2.3 实验小结

通过实验二,我掌握了如下内容: 1)if...else条件语句的使用。 2)case条件语句的使用

11

实验三、赋值、函数和任务

3.1 实验任务1——阻塞赋值与非阻塞赋值的区别

3.1.1 实验要求

本实验中两个模块\和\分别采用阻塞赋值和非阻塞赋值语句,从实验结果比较他们的区别。

3.1.2 模块的核心逻辑设计

(1)阻塞赋值 always@(posedge clk) begin b=a;

c=b; //阻塞赋值,a赋给b,b赋给c

$display(\//在Transcript窗口中显示赋值后a,b,c的值

end

(2)非阻塞赋值 always@(posedge clk) begin b<=a;

c<=b; //非阻塞赋值,a赋给b,b赋给c

$display(\b=%d, c=%d.\ //在Transcript窗口中显示赋值后a,b,c的值

end

(3)改变阻塞赋值程序的写法,再比较二者的区别(测试程序仅改变调用的阻塞赋值模块)

always@(posedge clk)

12

begin c=b;

b=a; //改变阻塞赋值的顺序,b赋给c,a赋给b $display(\ end

(3)再改变阻塞赋值程序的写法,比较二者的区别(测试程序仅改变调用的阻塞赋值模块)

always@(posedge clk) b=a; always@(posedge clk) c=b;

3.1.3 测试程序的核心逻辑设计

将阻塞与非阻塞赋值模块用同一测试程序测试,比较其输出的不同。 begin a=4'h3;

$display(\ #100 a=4'h7;

$display(\ #100 a=4'hf;

$display(\ #100 a=4'ha;

$display(\ #100 a=4'h2;

$display(\

#100 $display(\ //每隔100个时间单位改变一次输入a的值,并显示输出

$stop; end

blocking blocking(clk,a,b1,c1); //阻塞赋值输出用b1,c1表示

non_blocking non_blocking(clk,a,b2,c2); //非阻塞赋值输出用b2,c2表示

13

3.1.4 仿真实验关键结果及其解释

(1)阻塞赋值写法1

图 8 阻塞与非阻塞赋值比较波形图

图 9 阻塞与非阻塞赋值输出结果

如图8所示,b1,c1为阻塞赋值输出,输入a的值改变时,输出b的值随之改变,同时b的值赋给c,即赋值语句执行完后输出b, c值立即改变,然后块才结束。b2,c2为非阻塞赋值输出,赋值语句之后输出b, c的值并不立即改变,而是在块结束后才进行赋值操作,当下一时钟上升沿到来时,上一个a值才赋给b,同时上一b值赋给c。以上即阻塞与非阻塞赋值的区别。

a和b的输出结果如图9所示。 (2)阻塞赋值写法2

14

图 10 阻塞赋值程序变形1波形图

图 11 阻塞赋值程序变形1输出结果

改变阻塞赋值程序后的波形图如图10所示,可见将b=a, c=b的顺序调换之后,阻塞赋值程序先将上一次时钟上升沿时b的值赋给c,再将这一次时钟上升沿时a的值赋给b,即b与a同时变化但c的值是上一个b值。在波形图上看,由于输入a的变化时刻对应的是时钟下降沿,而输出要在下一时钟上升沿才能显示,故阻塞与非阻塞输出c在波形图上看是一致的,实际上在时钟下降沿a发生变化时,阻塞输出c的值已经发生变化。输出结果如图11

(3)阻塞赋值写法3

15

图 12 阻塞赋值程序变形2波形图

图 13 阻塞赋值程序变形2输出结果

阻塞赋值程序变形2的输出波形与结果如图12、图13所示。由于两个阻塞操作用同一个时钟沿触发,执行顺序是不确定的。

3.2 实验任务2——在Verilog HDL中使用函数

3.2.1 实验要求

(1)设计程序实现函数调用

(2)设计一个带控制端的逻辑运算电路,分别完成正整数的平方、立方和最大数为5的阶乘运算。

3.2.2 模块的核心逻辑设计

(1)设计程序实现函数调用

16

always@(posedge clk) //clk上升沿触发同步运算 begin if(!reset)

result<=0; //reset为低时复位 else begin

result<=n*factorial(n)/((n*2)+1); //调用factorial函数,verilog在整数除法运算结果中不考虑余数

end end

function[31:0] factorial; //函数定义,返回一个32位的数 input[3:0] operand; //输入一个4位操作数 reg[3:0] index; //函数内部计数用中间变量 begin

factorial=operand?1:0; //操作数为0时函数输出为0,否则为1 for(index=2;index<=operand;index=index+1) end endfunction

(2)带控制端的逻辑运算电路 always@(posedge clk) begin if(!reset) result<=0; else begin

if(sel==0) result<=n*n; //控制端输入sel=0时,执行平方操作 else if(sel==1) result<=n*n*n; //控制端输入sel=1时,执行立方操作 else if(sel==2&&n<=5) result<=factorial(n); //控制端输入sel=2且

17

factorial=index*factorial; //表示阶乘的迭代运算

输入n小于等于5时,计算n!

else result<=factorial(5); //否则计算5! end end

function[31:0] factorial; //函数定义,返回一个32位的数 input[3:0] operand; //输入一个4位操作数 reg[3:0] index; //函数内部计数用中间变量 begin

factorial=operand?1:0; //操作数为0时函数输出为0,否则为1

for(index=2;index<=operand;index=index+1)

factorial=index*factorial; //表示阶乘的迭代运算 end

3.2.3 测试程序的核心逻辑设计

(1)设计程序实现函数调用 initial begin

clk=0; n=0; reset=1;

#100 reset=0; //产生复位信号的负跳变沿 #100 reset=1; //复位信号恢复高电平后输入n

for(i=0;i<=15;i=i+1) begin

#200 n=i; //用循环结构,每隔200个时钟周期改变一次输入n的值

end

#100 $stop;

end

(2)带控制端的逻辑运算电路

18

begin clk=0; n=0; reset=1;

#100 reset=0; //产生复位信号的负跳变沿 #100 reset=1; //复位信号恢复高电平后输入n for(i=0;i<=15;i=i+1) begin

#200 sel={$random}%3;

n={$random}; //用循环结构,每隔200个时钟周期改变一

次控制端sel和输入n的值,n从0~15中随机生成

end #100 $stop; end

3.2.4 仿真实验关键结果及其解释

(1)设计程序实现函数调用

图 14 函数调用输出结果波形图

调用函数实现的运算输出结果波形如图14所示。通过调用函数实现了

result?n?n!的结果输出。(由于输入n, i为4位二进制数,计算机默认为补码2n?1形式,若以十进制显示,9~15将显示负值,为避免混乱,输入用二进制显示)。

19

(2)带控制端的逻辑运算电路

图 15 带控制端的逻辑运算电路输出结果波形图

逻辑运算电路输出结果波形如图15所示。从图中可见,输入n=4,控制端sel=2,输出result=4!=24;输入n=11,控制端sel=1,输出result?113?1331;输入n=13,控制端sel=2,输出result=5!=120;输入n=4,控制端sel=0,输出

result?42?16。

3.3 实验任务3——在Verilog HDL中使用任务

3.3.1 实验要求

使用任务设计4个4位并行输入数的排序组合逻辑。

3.3.2 模块的核心逻辑设计

always@(a or b or c or d) begin

{va,vb,vc,vd}={a,b,c,d}; rank2(va,vb); rank2(va,vc); rank2(va,vd); rank2(vb,vc);

20

rank2(vb,vd); rank2(vc,vd);

{ra,rb,rc,rd}={va,vb,vc,vd}; //用选择排序法排序 end

task rank2; //执行排序算法的任务 inout[3:0] x,y; reg[3:0] tmp; if(x>y) begin tmp=x; x=y;

y=tmp; //x与y变量内容互换,要求顺序执行,采用阻塞赋值方式 end endtask

(实验指导书上采用快速排序算法,我对快速排序不熟悉,故采用选择排序算法)

3.2.3 测试程序的核心逻辑设计

begin

a=0;b=0;c=0;d=0; repeat(50) begin

#100 a={$random}; b={$random}; c={$random};

d={$random}; //随机生成参与排序的数a,b,c,d end

21

3.2.4 仿真实验关键结果及其解释

图 16 使用任务进行排序输出波形

使用任务进行排序得到的输出波形如图16所示。输出以16进制显示,可见排序功能实现正确。

3.3 实验小结

通过实验三,我掌握了如下内容:

(1)深入理解了阻塞与非阻塞赋值的区别。

(2)掌握了在Verilog HDL中使用函数的方法,进一步熟悉了if...else和case分支结构的使用。

(3)掌握了用repeat语句实现for循环结构的方法。

(4)掌握了在Verilog HDL中使用任务的方法,回顾了排序算法。

22

实验四、有限状态机

4.1 实验任务1——基于状态机的串行数据检测器

4.1.1 实验要求

设计一个串行数据监测器。要求是:连续4个或4个以上为1时输出1,其他情况下输出0。

4.1.2 模块的核心逻辑设计

always@(posedge clk) if(!rst) state<=Q0; else

state<=nextstate; //复位端为0时输出状态置零,复位端为1时输出状态始终向下一状态变化

always@(state or x) case(state) Q0: if(x==1) nextstate=Q1;

else

nextstate=Q0; Q1: if(x==1) nextstate=Q2; else

nextstate=Q0; Q2: if(x==1) nextstate=Q3;

23

else

nextstate=Q0; Q3: if(x==1) nextstate=Q4; else

nextstate=Q0; Q4: if(x==1)

nextstate=Q4; else

nextstate=Q0;

default nextstate=Q0;

endcase //状态转移的条件判断部分,转移逻辑见状态图 always@(state or rst or x) if(!rst) Y=0; else

if(state==Q4&&x==1) Y=1; else

Y=0; //输出的条件判断部分,输出逻辑见状态图 状态图如图17所示:

24

图 17 \状态机状态图

4.1.3 测试程序的核心逻辑设计

always@(posedge clk)

data={data[22:0],data[23]}; initial begin clk=0; rst=1; #5 rst=0; #30 rst=1;

data='b1011_1110_0010_1111_1110; //待检测的数字序列 #500 $stop; end

25

4.1.4 仿真实验关键结果及其解释

图 18 \序列检测输出波形1

图 19 \序列检测输出波形2

图18、图19为“1111”序列检测的输出波形。由输出波形可见,当检测到第一段“1111”后输出Y翻转,输出为1,当再次输入0时输出复位为0;监测到第二段“1111”后输出Y又变为1,且当输入在“1111”后保持为1时,输出也保持为1。

4.2 实验任务2——楼梯灯

4.2.1 实验要求

楼下到楼上依次有3个感应灯:灯1、灯2、灯3。当行人上下楼梯时,各个灯感应到后自动点亮,若在8s内感应信号消失,则点亮8s,若感应信号存在

26

时间超过8s,则感应信号消失4s后灯自动关闭。

基本任务:

(1)做出如上逻辑电路并仿真。

(2)设感应信号是电平信号,考虑去抖情况,对于感应信号到达存在毛刺(小于0.5s),设计合适逻辑并剔除。

扩展任务:

(3)若为节约能源,下一个灯点亮的同时将自动关闭上一个灯,作出如上逻辑设计并仿真(仅考虑一个人的情况)。

(4)考虑存在多个人上下楼梯的情况,比如:

行人1已经从灯1到达灯2,灯2受感应自动点亮,但此时行人2刚上楼梯到达灯1的位置,则灯1和灯2都须点亮。

更加复杂一点,如果行人2是下楼梯刚到达灯3位置,作出如上逻辑设计并仿真。

4.2.2 模块的核心逻辑设计

(1)基本任务 对一盏灯进行建模

always @ ( posedge clock or negedge reset_n) begin if ( ! reset_n ) begin state <= OFF ; end

else begin // reset信号为0时,关灯;reset信号为1时,开始执行 case ( state ) OFF: begin

count <= 0 ; state <= nstate ; end

ON: begin

if ( nstate == LONG ) count <= 0 ;

27

else count <= count + 1 ; state <= nstate ; end

LONG: begin

if ( nstate == LONG ) count <=0 ; state <= nstate ; end

DELAY_LONG: begin

if ( nstate == DELAY_LONG ) count = count + 1 ; state <= nstate ; end

DELAY_SHORT: begin

if ( nstate == DELAY_SHORT ) count <= count + 1 ; else count <= 0 ; state <= nstate ; end

default : state <= OFF ;

endcase //二段式中的第一段,给出各状态跳变到下一状态的条件 end end

always @ ( state or switch or count ) begin case ( state )

OFF:

if ( switch ) nstate = ON ; ON: if ( switch )

if ( count < NUM_WAIT-1 ) nstate = ON ; else nstate = LONG ; else nstate = DELAY_SHORT ;

28

LONG:

if ( switch ) nstate = LONG ;

else nstate = DELAY_LONG ;

DELAY_LONG: if ( switch ) nstate = ON ;

else if ( tolerance < NUM_DELAY-1 ) nstate = DELAY_LONG ; else if ( count < NUM_DELAY-1 ) nstate = DELAY_LONG ; else nstate = OFF ;

DELAY_SHORT:

if ( switch ) nstate = ON ;

else if ( count < NUM_WAIT-1 ) nstate = DELAY_SHORT ; else nstate = OFF ; default: nstate = OFF;

endcase

end //二段式中的第二段,判断各状态时满足条件的下一状态是哪个状态

assign light = ( state == OFF )? 0 : 1 ; //light为输出变量,表示灯的亮灭,OFF为0时灯亮,OFF为1时灯灭

(2)去抖任务

assign filtered = ( state == INACTIVE ) ? 0 : 1 ; assign out = filtered || in ; ACTIVE :

if ( !in ) begin state <= PENDING ; count <= 0 ; end PENDING : begin

if ( in ) state <= ACTIVE ; else if ( count < NUM_JITTER-1) count <= count + 1 ;

else if(count >=NUM_JITTER-1&&(!in)) state=INACTIVE;

29

else state <= INACTIVE ;

end //老师的程序只做了当原输出状态为1(亮灯)时的去抖,方法时当原输出状态为1且有0输入时增加一个PENDING状态,判断输入信号的持续时间是否大于0.5s,若小于0.5s,则认为是ACTIVE状态,即忽略扰动,否则认为是INACTIVE状态,输出变化。而输出是由filtered变量和输入in共同控制的,filtered==ACTIVE,通过assign连续赋值语句,使输出为in||filtered,从而实现原状态为1时的防抖。我尝试了设计原状态为0的防抖,但效果不佳,代码如下:

always @ ( posedge clock ) begin if(state==INACTIVE) out<=0; else if(state==ACTIVE) out<=1; else out<=0; end ...

INACTIVE : if ( in ) begin

state <= PENDING1 ; count<=0; end ...

PENDING1:begin

if(!in) state<=INACTIVE; else if(count < NUM_JITTER-1) count <= count + 1 ;

else if(count >=NUM_JITTER-1&&(in)) state=ACTIVE; else state<=ACTIVE;

end //我尝试在INACTIVE状态下增加一个等待判断状态PENDING1,且输出采用always块给寄存器变量赋值的形式,但效果不佳,见结果分析部分

30

4.2.3 测试程序的核心逻辑设计

(1)基本任务 仅测试一盏灯:

initial begin // 短开关信号测试 switch = 0 ; #100 switch = 1 ;

#20 switch = 0 ; //第一个开关信号持续2s #100 switch = 1 ; //10s后第二个开关信号到来 #30 switch = 0 ; //第二个开关信号持续3s #40 switch = 1 ; // 4s后又一开关信号到来 #60 switch = 0 ;

//长开关信号测试 #50 switch = 1 ;

#99.5 switch = 0 ; // 第一个开关信号持续9.95s #50.5 switch = 1 ; // 5.05s后第二个开关信号到来 #90 switch = 0 ; //第二个开关信号持续9s #30 switch = 1 ; // 3s后第二个开关信号到来 #10 switch = 0 ; //10s后开关信号结束 //SUM = 330 end

三盏灯的测试只需在测试程序中调用3个单盏灯测试程序,且开关信号分为switch[1]~switch[3],表示灯的亮灭的输出变量分为light[1]~light[3]。

(2)防抖任务 initial begin clock = 0 ; in = 0 ; #10 in = 1 ; #50 in = 0 ; #3 in = 1 ;

31

#12 in = 0 ; #20 in = 1 ; #10 in = 0 ; #8 in = 1 ; #2 in = 0 ; #8 in=1; #3 in=0;

#50 $stop ; end

4.2.4 仿真实验关键结果及其解释

(1)基本任务

图 20 开关信号持续时间小于8s的情况

图 21 开关信号持续时间大于8s的情况

32

只对一盏灯进行控制和测试时,开关信号持续时间小于或大于8s的情况分别如图20、图21所示。图20中,红色光标线处开始有一开关信号,持续时间2s,对应亮灯信号则在开关信号到来时跳变成1,持续8s后跳变回0。图21中,红色光标处开始的开关信号持续时间为9.95s,则对应亮灯信号在开关信号消失后再持续4s才跳变回0。

(2)去抖任务

图 22用连续赋值的方法设计的去抖程序

图 23 用always块赋值方法

对老师的程序稍作改变的结果如图22所示,显然,由于输出为filtered||in,故当有in=1抖动输入时输出依然会跳变,只是随抖动结束输出也跳变回0。

增加一个状态并用always块赋值得到的结果如图23。由图中可见,这种方法对原输入状态为0时的防抖功能实现较好,但反而对原状态为1时效果不好,不仅没有实现防抖,在真正输入跳变时输出的跟随也变慢了。

由于时间不足,我没能深入研究产生上述现象的原因并找出解决办法,也没

33

有做任务(3)、(4),望老师见谅。

4.3 实验小结

通过实验4,我学习了有限状态机的使用,尝试用有限状态机解决比较复杂的楼梯灯问题。但由于时间不足,我只是在理解的基础上采用了老师给的程序,想对其作出改进但并不成功。如果日后有时间的话,我会再研究一下这个问题,希望能够找到解决方法。

34