Verilog学习笔记(03)

Posted 高山流水123a s d

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Verilog学习笔记(03)相关的知识,希望对你有一定的参考价值。

文章目录


参考:Verilog数字VLSI设计教程
硬件描述语言Verilog
Verilog HDL数字设计与综合
Verilog HDL 数字集成电路高级程序设计

4. 测试仿真结构

对于一个典型的电路测试,一般会有一个信号源对它的目标电路进行激励,然后通过测试设备,对波形进行观察,然后确定电路的正确与否

下图为测试仿真的示意图:

流程图如下:

testbench的结构模板:

module 仿真模块名;
//数据类型声明
激励信号为reg型,显示信号为wire型

//实例化待测模块
<模块名><实例名><(端口列表)>;

//测试激励定义
always和initial过程块
function和task结构
if-elsecase等控制语句
//输出响应

endmodule

测试激励描述方式
(1)直接编辑测试激励波形。在测试环境中,用人工的方式去改变信号的电平,产生我们要求的向量,但效率较低
(2)用Verilog产生测试激励,效率相对较高
(3)利用Verilog从文本文件中读取数据。对于大规模集成电路以及可靠性要求较高的测试环境中,通常会使用

4.1 信号初始化

一般来说,testbench中信号初始化采用initial过程块来完成

4.2 延迟控制

1.延迟语句

(1)外部时间控制方式

initial #5 a=b;
等效于
initial 
begin 
	#5;
	a=b;
end

(2)内部时间控制方式

initial a=#5 b;
等效于
initial
begin
	temp=b;
	#5;
	a=temp;
end
2.事件语句
@(<事件表达式>);
@(<事件表达式>)行为语句;

例如

initial
begin
	#10;
	@(posedge en) in=~in;
end
3.等待语句
wait(<条件表达式>) 行为语句;

例如:

always #5 cnt=cnt+1'b1;
initial
wait(cnt==4'b1111)
$display($time,"cnt=%b",cnt);

4.3 initial和always过程块的使用

1. initial过程块

例:

`timescale 1ns/1ns
module wave_initial;
reg a; 
initial
begin
        a=1'b0;
        #5 a=1'b1; 
        #5 a=1'b0;
        #10 a=1'b1;
end
endmodule
2. always过程块

例:

`timescale 1ns/1ns
module wave_always;
reg clk; 
initial clk=1'b0;
always  #5 clk=~clk;
endmodule

4.4 串行与并行语句块产生测试信号

1. 串行语句块产生测试信号

例:

`timescale 1ns/1ns 
module serial_wave;
reg a;
initial
begin
          a=1'b0;
      #10 a=1'b1;
      #20 a=1'b0;
      #20 a=1'b1;
      #30 a=1'b0;
      #20 a=1'b1;
      #10 a=1'b0;
end
endmodule
2. 并行语句块产生测试信号
`timescale 1ns/1ns
module parallel_wave;
reg a;
initial
fork
          a=1'b0;
      #10 a=1'b1;
      #30 a=1'b0;
      #50 a=1'b1;
      #80 a=1'b0;
      #100 a=1'b1;
      #110 a=1'b0;
join
endmodule

因此,产生相同的一个信号,既可以用串行语句相对延迟产生,也可以用并行语句绝对延迟时间产生

3. 阻塞与非阻塞描述方式产生测试信号

例:

外部事件控制的阻塞方式

`timescale 1ns/1ns
module blocking_out;
reg a;
initial
begin
           a=1'b0;
       #5  a=1'b1;
       #10 a=1'b0;
       #5  a=1'b1;
       #20 a=1'b0;
       #5  a=1'b1;
end
endmodule

内部事件控制的阻塞方式

module blocking_inner;
reg a;
initial
begin
       a=	 1'b0;
       a=#5  1'b1;
       a=#10 1'b0;
       a=#5  1'b1;
       a=#20 1'b0;
       a=#5  1'b1;
end
endmodule

外部事件控制的非阻塞方式

`timescale 1ns/1ns
module nonblocking_out;
reg a;
initial
begin
       a<=1'b0;
       #5 a<=1'b1;
       #10 a<=1'b0;
       #5 a<=1'b1;
       #20 a<=1'b0;
       #5 a<=1'b1;
end
endmodule

内部事件控制的非阻塞方式

`timescale 1ns/1ns
module non_blocking_inner;
reg a;
initial
begin
       a<=   1'b0;
       a<=#5 1'b1;
       a<=#15 1'b0;
       a<=#20 1'b1;
       a<=#40 1'b0;
       a<=#45 1'b1;
end
endmodule

在modelsim仿真,波形如下:

4.5 任务和函数

使用函数可以让我们在编写程序时能够以较少的语句将内容描述清楚
可以实现代码复用,高效率编程

1. 任务(Task)
task<任务名>;
<端口类型声明>;
<局部变量声明>;
begin
<语句1>;
<语句2>;
      ...
<语句n>;
end
endtask

任务的调用格式

<任务名>(端口1,端口2,,端口n);

注意事项:
(1)任务的定义和调用必须在一个module模块内。
(2)端口的顺序与端口的类型说明顺序必须一致
(3)任务的调用是在always语句或initial语句中进行的
(4)任务可以调用任务,而任务不能被函数调用
(5)任务不返回任何值,且任务是不可综合的,它只能用于仿真。

任务的方式可以极大地减少程序设计

2.函数
function<返回值类型或位宽><函数名>;
<输入变量及类型声明语句>;
<局部变量声明>;
begin
行为语句1;
行为语句2;
     ...
行为语句n;
end
endfunction

函数的调用格式

<函数名>(输入表达式1,(输入表达式2)...(输入表达式n);

函数的调用方式不能单独存在,它一定是调用的过程中会被当做一个返回值去赋给某一个信号
注意事项:
(1)函数定义只能在模块中完成,不能出现在过程块中。
(2)函数至少要有一个输入端口,但不能包含输出和双向端口。
(3)在函数结构中,不能使用任何形式的时间控制语句(#、wait等),也不能使用disable中止语句。
(4)函数定义结构体中不能出现过程块语句,函数调用既能出现在过程块中,也能出现在assign连续赋值语句中,函数的调用只能作为一个操作数出现在调用语句中。
(5)函数内部可以调用函数,但不能调用过程。

4.6 典型测试向量的测试方法

1.任意波形信号的产生
`timescale 1ns/1ns 
module single_seq;
reg [2:0]in;
initial
begin
       in=3'b000;
       #10 in=3'b100;
       #20 in=3'b101;
       #10 in=3'b010;
       #30 in=3'b011;
end
endmodule
`timescale 1ns/1ns 
module mult_seq;
reg [2:0]in;
always
begin
          in=0;
       #6 in=5'b01010; 
       #3 in=5'b11110;
       #6 in=5'b10100;
       #5;
end
endmodule

重复5次产生不规则序列1、3、4、13、15、16、22。

`timescale 1ns/1ns 
module data_signal;
reg [4:0] data;
initial
begin
data=5'b00000;
repeat(5)
begin
       #10 data=5'b00001;
       #10 data=5'b00011;
       #10 data=5'b00100;
       #10 data=5'b01101;
       #10 data=5'b01111;
       #10 data=5'b10000;
       #10 data=5'b10110;
end
end
endmodule

initial语句产生普通时钟信号

`timescale 1ns/1ns 
module clk1;
reg clk;
initial
begin
clk=1'b0;
forever #50 clk=~clk;
end
endmodule

always语句产生普通时钟信号

`timescale 1ns/1ns 
module clk2;
reg clk;
initialclk=1'b0;
always #50 clk=~clk;
endmodule

自定义占空比的时钟信号产生。

`timescale 1ns/1ns 
module duty_cycle_clk;
parameter high=4,low=16;
reg clk;
always
begin
      clk=1'b0;
      #low;
      clk=1'b1;
      #high;
end
endmodule

具有相位偏移的时钟信号产生。

`timescale 1ns/1ns 
module shift_clk;
parameter high=4,low=16,shift=2;
reg clka;
wire clkb;
always
begin
clka=1'b0;
      #low;
clka=1'b1;
      #high;
end
assign #shift clkb=clka;
endmodule

固定数目的时钟信号产生。

`timescale 1ns/1ns 
module fix_num_clk;
reg clk;
parameter n=5;
initial
begin
clk=1'b0;
repeat(2n)
         #5 clk=~clk;
end
endmodule

产生与时钟同步的“10010111”循环序列测试信号。

`timescale 1ns/1ns 
module tb_circuit;
reg clk;
reg[7:0] q;
wire signal_tb;
initial
begin
	  clk=1'b0;
      q=8'b10010111;
end
always #5 clk=~clk;
always @(posedge clk) q<=q[6:0],q[7];
assign signal_tb=q[7];
endmodule

总线信号的产生

`timescale 1ns/1ns 
module bus_wr_tb;
reg clk;
reg cs;
reg wr;
reg [31:0] addr;
reg [31:0] data;
initial
begin
cs=1'b1; wr=1'b1;
    #30;
    bus_wr(32'h1100008a, 32'h11113000);
    bus_wr(32'h1100009a, 32'h11113001);
    bus_wr(32'h110000aa, 32'h11113002);
    bus_wr(32'h110000ba, 32'h11113003);
    bus_wr(32'h110000ca, 32'h11113004);
    addr=32'bx; data=32'bx;
  end
initial clk=1;
always #5 clk=~clk;
task bus_wr;
input [31:0] ADDR;
input [31:0] DATA;
begin
    cs=1'b0; wr=1'b0;
    addr=ADDR;
    data=DATA;
    #30 cs=1'b1; wr=1'b1;
 end
 endtask
endmodule

以上是关于Verilog学习笔记(03)的主要内容,如果未能解决你的问题,请参考以下文章

verilog波形仿真

nc_verilog 仿真波形中如何显示状态名

verilog中为啥锁存器要在电平触发方式下生成

verilog怎么编写可调PWM波形?

verilog hdl全加器的小问题(quartus)

verilog编写可调PWM波形