实验2、时序逻辑

教程

1、触发器与锁存器

时序逻辑部分最重要的知识点就是flip-flop与latch的不同,为了强化这部分的理解,我们直接用实验看一下它们的不同点。请逐行研读一下下面的代码,并且基于“实验1”中的仿真与FPGA部署方法,运行下面的Testbench。

请重点观察:

  • 给到D端口的数据什么时候会变?是哪些信号让他们发生了变化?

  • Q端的数据怎么样才能保持住(不受D端信号变化的影响)?

  • 注意“边缘触发”/“电平触发”/“同步重置”(RESET)/“异步重置”(RESET)的区别

现在有3种时序逻辑器件:a)同步RESET的DFF,b)异步RESET的DFF,和c)D-Latch,你可以预测出来三个Q端输出在下面输入的行为么?

_static/assets/lab2_q.png

先自己试着预测画一个波形,下面我们来动手实验验证一下你的预测对不对吧!

先写代码,把三种DFF/latch写入同一个文件dff.v里:

a) 同步RESET的DFF:

module dff_sync_reset (
data   , // Data Input
clk    , // Clock Input
reset  , // Reset input
q        // Q output
);

input data, clk, reset ; //Input Ports

output reg q;//Output Ports

always @ ( posedge clk)
if (reset) begin
  q <= 1'b0;
end  else begin
  q <= data;
end

endmodule

b) 异步RESET的DFF:

module dff_async_reset (
data   , // Data Input
clk    , // Clock Input
reset  , // Reset input
q        // Q output
);

input data, clk, reset ; //Input Ports

output reg q;//Output Ports

always @ ( posedge clk or posedge reset)
if (reset) begin
  q <= 1'b0;
end  else begin
  q <= data;
end

endmodule

c) D(ata)型锁存器 (D-Latch):

module dlatch_reset (
data   , // Data Input
en     , // LatchInput
reset  , // Reset input
q        // Q output
);

input data, en, reset ; //Input Ports

output reg q; //Output Ports

always @ ( en or reset or data)
if (reset) begin
  q <= 1'b0;
end else if (en) begin
  q <= data;
end

endmodule

再写比较三者不同的统一Testbench:

`timescale 1ns/1ns
`include "dff.v"

module dff_tb;

reg d,clk,rst;
wire q1,q2,q3;

initial begin
    $dumpfile("wave.vcd");
    $dumpvars(0);
    $monitor("time=%4t, d=%2b, clk=%2b, rst=%2b, q1=%2b, q2=%2b, q3=%2b",$time,d,clk,rst,q1,q2,q3);
end

initial clk = 0;
always #5 clk = ~clk;

initial begin
    rst = 0;
    #1 rst = 1;
    #10 rst = 0;
    #25 rst = 1;
    #1 rst = 0;
end

initial begin
    d = 0;
    #10 d = 1;
    #7 d = 0;
    #1 d = 1;
    #3 d = 0;
    #1 d = 1;
    #1 d = 0;
    #10 d = 1;
    #10 $finish;
end

dff_sync_reset dut1(
.data   (d)   ,// Data Input
.clk    (clk) ,// Clock Input
.reset  (rst) ,// Reset input
.q      (q1)    // Q output
);


dff_async_reset dut2(
.data   (d)   ,// Data Input
.clk    (clk) ,// Clock Input
.reset  (rst) ,// Reset input
.q      (q2)    // Q output
);

dlatch_reset dut3(
.data (d)   , // Data Input
.en (clk)     , // LatchInput
.reset (rst)  , // Reset input
.q (q3)        // Q output
);

endmodule

用iverilog快速验证一下:

cd [到当前存放dff.v和dff_tb.v的文件夹]
iverilog -o wave dff_tb.v
vvp -n wave

如果到此运行顺利的话,当前文件夹会生成一个新的wave.vcd(正如dff_tb.v里面写的$dumpfile(“wave.vcd”)所写)。然后用gtkwave打开这个波形:

gtkwave wave.vcd

就可以得到这个。你之前预测的结果是正确的么?

2、普通计数器

计数器是Verilog里最常见的时序逻辑,其代码(一种实现方式)如下:

module counter    (
out     ,  // Output of the counter
enable  ,  // enable for counter
clk     ,  // clock Input
reset      // reset Input
);

output reg [7:0] out;//Output Ports

input enable, clk, reset;//Input Ports

always @(posedge clk)
if (reset) begin
  out <= 8'b0 ;
end else if (enable) begin
  out <= out + 1;
end

endmodule 

思考一下,如out已经达到8’hFF,它会如何变化?先预测一下,下面我们用iverilog或者FPGA验证一下结果。

1)用Iverilog:

先写Testbench:

`timescale 1ns/1ns
`include "counter.v"

module counter_tb;

reg en,clk,rst;
wire [7:0] out;

initial clk = 0;
always #5 clk = ~clk;

initial begin
    $monitor("time=%0t, en=%0b, rst=%0b, out=%0d", $time, en, rst, out);
    $dumpfile("wave.vcd");
    $dumpvars(0);
end

initial begin
    en=1;
    rst = 1;
    #10 rst = 0;
    #5000 $finish;
end

counter dut(
.out(out)     ,  // Output of the counter
.enable(en)  ,  // enable for counter
.clk(clk)     ,  // clock Input
.reset(rst)      // reset Input
);

endmodule

然后:

iverilog -o wave counter_tb.v
vvp -n wave
gtkwave wave.vcd 

验证结果。

2)用FPGA来点Fancy的实验吧:


练习

Note

[问题1] 请简要回答上面三者:(a) 同步RESET的DFF、(b)异步RESET的DFF、(c)D-Latch在行为上的不同点。

1、上下计数器

上下计数器是指计数器可以实现向上数(12345…)与向下数(54321…)的计算数,被计数信号从clk端输入。向下向下数的模式需要设置一个模式控制端口“up_down”:

up_down=0: 向上数
up_down=1: 向下数

Note

[问题2] 用Verilog HDL设计上下计数器,并写testbench验证。

2、格雷码计数器

格雷码(Gray Code),又称为反射码、单增码、循环码等,是一种二进制编码方式,最早由美国计算机科学家达尔文·格雷(Darwin Gray)在1959年提出。格雷码的特点是在相邻的两个数值之间,只有一个二进制位不同,这种特性使得格雷码在硬件中进行数值切换时,可以最小化错误发生的概率,从而减少开关动作的次数,提高电子设备的可靠性和稳定性。

格雷码基础公式:https://baike.baidu.com/item/格雷码/6510858

4位格雷码

4位自然二进制码

0000

0000

0001

0001

0011

0010

0010

0011

0110

0100

0111

0101

0101

0110

0100

0111

1100

1000

1101

1001

1111

1010

1110

1011

1010

1100

1011

1101

1001

1110

1000

1111

Note

[问题3] 用Verilog HDL设计一个16bit格雷码计数器,并写testbench验证。

  • 提示:不要用查找表一一对应写!