verilog学习_personal.md(计组实验)

Uncategorized
4.2k words

一、verilog学习

组合逻辑电路(Combinational Logic Circuit)和时序逻辑电路(Sequential Logic Circuit)是数字电路的两大基本类型,它们在数字系统和计算机硬件中扮演着重要的角色。

组合逻辑电路

组合逻辑电路的特点是输出仅仅依赖于当前的输入,与电路以前的状态无关。换句话说,组合逻辑电路不包含记忆元件,它没有内部状态。组合逻辑电路通常由逻辑门(如与门、或门、非门、异或门等)组成,这些逻辑门根据输入信号的组合产生输出信号。组合逻辑电路的设计相对简单,因为输出只由输入决定,不需要考虑电路的历史状态。

时序逻辑电路

时序逻辑电路的输出不仅取决于当前的输入信号,还取决于电路的当前状态,即电路以前的历史输入。时序逻辑电路包含记忆元件,如触发器(flip-flops)和寄存器,这些元件能够存储信息。时序逻辑电路通常用于实现计数器、存储器和复杂的决策电路等。由于需要考虑时间因素和电路的状态,时序逻辑电路的设计比组合逻辑电路更为复杂。

总结

  • 组合逻辑电路
    • 输出仅由当前输入决定。
    • 不包含记忆元件,没有内部状态。
    • 设计简单。
  • 时序逻辑电路
    • 输出由当前输入和电路的当前状态共同决定。
    • 包含记忆元件,能够存储信息。
    • 设计复杂,需要考虑时间因素和电路的历史状态。
      在实际应用中,复杂的数字系统通常是由组合逻辑电路和时序逻辑电路结合而成的,以实现各种功能。

二、D触发器

D触发器(D Flip-Flop)是一种常用的时序逻辑电路,它具有一个数据输入端D(Data),一个时钟输入端CLK(Clock),以及一个输出端Q。D触发器的基本功能是当有时钟脉冲作用时,输出端Q的状态会变得与数据输入端D的状态相同,即Q=D。这一转换发生在时钟脉冲的上升沿或下降沿,具体取决于D触发器的设计类型。

D触发器 D触发器

1、D触发器特性表
D触发器特性表

2、Verilog HDL语言描述D触发器
在Verilog HDL(硬件描述语言)中,描述一个基本的D触发器可以通过定义一个模块来实现。下面是一个简单的D触发器的Verilog代码示例,这个例子中,D触发器在时钟的上升沿将输入数据D传递到输出Q。

1
2
3
4
5
6
7
8
9
10
11
module d_flipflop (
input wire clk, // 时钟输入
input wire d, // 数据输入
output reg q // 数据输出
);

always @(posedge clk) begin // 在时钟的上升沿执行
q <= d; // 将D的值赋给Q
end

endmodule

这段代码中:

  • module d_flipflop 定义了一个名为d_flipflop的模块。
  • input wire clk, input wire d 分别声明了时钟信号clk和数据输入信号d为模块的输入。
  • output reg q 声明了数据输出信号q为模块的输出,并且使用reg类型变量,因为输出值需要被寄存。
  • always @(posedge clk) 是一个过程块,它指定以下代码块内的语句将在时钟信号的上升沿(posedge)执行。
  • q <= d; 这一行代码说明在每个时钟上升沿,输出q被赋值为当前的输入d的值。这里使用非阻塞赋值<=,这是为了在多驱动情况下的正确行为,尤其是在描述寄存器传输时。

3、测试代码
为了测试上述D触发器模块,我们可以编写一个简单的测试平台(testbench)来模拟时钟信号和数据输入,并观察输出端口q的行为。以下是使用Verilog HDL编写的测试代码示例:

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
module d_flipflop_testbench;

// 定义时间单位和时间精度
`timescale 1ns / 1ps

// 引入D触发器模块
reg clk;
reg d;
wire q;

d_flipflop dff (.clk(clk), .d(d), .q(q));

// 生成时钟信号
always #5 clk = ~clk; // 5ns周期的时钟信号

initial begin
$monitor($time, " - Clock: %b, Input D: %b, Output Q: %b", clk, d, q);

// 测试序列
#10 d = 0; // 等待10ns后设置d为0
#20 d = 1; // 再等待10ns后设置d为1
#30 d = 0; // 继续等待并改变d的值,观察q的响应

// 测试结束,$finish在仿真完成后停止仿真
#40 $finish;
end

endmodule

这段代码做了以下几件事:

  1. 引入了时间单位和时间精度定义,这有助于仿真时的时间尺度控制。
  2. 实例化了之前定义的d_flipflop模块,并连接了输入输出端口。
  3. 使用always块生成了一个5ns周期的时钟信号。
  4. initial块中,首先定义了一个监视器($monitor),用于打印仿真时间、时钟信号、输入数据d和输出数据q的状态,便于观察仿真结果。
  5. 随后,设置了一系列测试序列,改变了输入d的值,并通过延时观察输出q是否跟随d的变化。
  6. 最后,使用$finish指令在仿真达到预定时间后结束仿真。

通过这个测试平台,我们可以对D触发器的功能进行验证,确保它在不同的输入序列下能够正确地工作。(以上代码结合通义千问回答)

备用代码

1
2
3
4
5
6
7
8
9
10
11
module dff(clk,clr,rst,d,q);//clr清0,rst复位
input clk,clr,rst,d;
output q;
reg q;
always@(posedge clk or posedge clr)
begin
if(clr==1'b1)q<=1'b0;
else if(rst==1'b1)q<=1'b1;
else q<=d;
end
endmodule
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
module dff_t;
reg clk,rst,clr,d;
wire q;
initial
begin
clk=1'b0;
forever #10 clk=~clk;
end
initial
begin
clr=1'b0;
rst=1'b0; d=1'b0;
#10 rst=1'b1;clr=1'b0;d=1'b0;
#10 rst=1'b1;clr=1'b1;d=1'b1;
#10 rst=1'b0;clr=1'b0;d=1'b1;
#20 d=1'b0;
#20 d=1'b1;
end
dff U1(.clk(clk),.clr(clr),.rst(rst),.d(d),.q(q));
endmodule

工作原理

  1. 置位(Set)和复位(Reset):除了基本的数据输入外,一些D触发器还可能包含置位和复位输入端。当置位信号有效时,无论其他输入如何,输出Q会被强制为1;当复位信号有效时,Q会被强制为0。这些控制信号通常独立于时钟信号工作,并且优先级高于D输入。

  2. 边沿触发:D触发器属于边沿触发型触发器,这意味着数据仅在时钟信号的特定边沿(上升沿或下降沿)被采样并更新输出状态。这样设计可以防止在时钟信号的稳定期由于输入信号的波动而引起输出的不确定变化,保证了电路的稳定性和可靠性。

  3. 保持和禁止:在时钟脉冲的间隔期间,D触发器的输入通常需要保持稳定。这是因为触发器只在时钟脉冲的作用下改变状态,而在时钟的高电平或低电平稳态期间,输入信号的变化不会影响输出。

应用

D触发器在数字电路设计中极为重要,广泛应用于各种场合,包括但不限于:

  • 寄存器:一系列D触发器可以构成一个移位寄存器,用于数据的存储和移位。
  • 计数器:通过适当连接多个D触发器,可以实现不同进制的计数器。
  • 状态机:在复杂系统中,D触发器用于保存系统的当前状态,是实现状态机的基础。
  • 数据缓存:在数据传输过程中,D触发器可以作为缓冲,确保数据的同步和稳定性。

三、组合逻辑

1、assign语句实现组合逻辑

举例:assign out = (a&b)|c;
特点:
1、左值必须为wire类型;
2、只要任何输入发生变化,等号右边的表达式都会被立即重新计算,并将结果赋给左边的变量。

Testbench(测试平台)

编写Testbench的目的在于通过仿真来验证设计模块的功能正确性。作为一个独立的模块,Testbench主要由三个关键部分组成:

  1. 实例化被测模块
    在Testbench中,首先需要通过实例化来引入被测设计(DUT, Device Under Test)。实例化的语法通常是这样的:被测模块名 实例名(端口列表); 这一步骤建立了Testbench与DUT之间的连接桥梁。

  2. 向被测模块添加激励
    为了全面验证DUT的功能,Testbench需生成一系列的输入激励信号。这包括了各种可能的输入组合,覆盖正常操作、边界条件及异常情况,以确保DUT在所有预期情况下都能正确响应。

  3. 判断输出是否符合预期

    • 查看波形:通过仿真软件提供的波形查看器,直观比较DUT的输出信号与预期结果。这是最直接的验证方式,可以观察信号的时间关系和变化。
    • 使用断言和打印语句:除了波形观测外,还可以在Testbench中嵌入断言(Assertions)来自动检验某些条件是否满足,以及利用打印语句(如Verilog中的$display)输出关键变量的值,辅助分析DUT的行为是否符合设计规范。

2、always语句结合阻塞赋值语句

举例:

1
2
3
4
5
always @(*) //或者always @(a, b, c)        
begin
t = a&b;
out = t|c;
end
  1. always语句类似于一个循环,@()里面表示循环条件,叫做敏感信号列表,即括号里的任何一个信号发生时,循环执行一次。
  2. 对于组合逻辑,如例所示,敏感信号包括a,b,c,即等号右边所有的变量,可以用*表示。always语句中的赋值语句有两种,阻塞赋值语句和非阻塞赋值语句。阻塞赋值语句(=)按顺序执行,用于实现组合逻辑电路。
  3. always语句中的赋值语句的左值必须是reg类型。

3、时序逻辑实现方法

always语句结合非阻塞赋值语句
举例:

1
2
3
4
5
always  @(posedge clock)
begin
d_reg <= f_reg;
e_reg <= d_reg;
end

实现时序逻辑时,用非阻塞赋值语句。非阻塞赋值语句(<=)按并行执行。
(2024/5/17 3:30 编1)

Comments