Verilog HDL三种描述方式

时间:2024-11-11 10:12:22

一.数据流建模方式

在组合逻辑电路中,数据不会存储,因此输入信号经过电路变为输出信号类似于数据流动。可以通过连续赋值语句这种特性进行建模,这种建模方式通常被称为数据流建模。

 连续赋值语句只能用来对连线型变量进行驱动,它可以采取显式连续赋值语句和隐式连续赋值语句两种赋值方式。

1.显式连续赋值语句

由两条语句构成:

格式:
<连续型变量类型><位宽><变量名>;            // 对连线型变量进行类型说明

assign #<延迟><变量名>=赋值语句;       // 对这个连线型变量进行连续赋值的赋值语句

2.隐式连续赋值语句

格式:
<连续型变量类型><赋值驱动强度><位宽>#<延迟><变量名>=赋值语句;

注意:


a. 连续型变量类型默认为wire

b.位宽默认1位;

c.驱动强度只存在于隐式连续赋值语句,默认strong1,strong0),若两个驱动强度以高驱动为主

d.延迟#(delay1,delay2,delay3)分别为上升沿,下降沿,变为高阻态的延迟;

e.连续赋值语句不能出现在过程块里。

f.多个连续赋值语句之间是并行关系,因此与位置顺序无关。
 

 

二.行为级建模

电路外部行为的角度对其进行描述,因此行为级建模是从一个层次很高的抽象角度来表示电路的。行为建模也可以用来生成仿真测试信号。

2.1  过程语句  

2.1.1.initial过程语句
initial过程语句的语法格式为:

  initial
      begin
         语句1;
         语句2;
          ...
      语句n;
  end

 

a.通常用于仿真模块中对激励向量描述,或用于给寄存器变量赋初值。

b.仿真时从模拟0时刻开始执行,而且只执行一次。

c.若多个initial过程块,则同时从0时刻开始并行执行的。

 

2.1.2.always过程语句

语法格式是:
always@(<敏感事件列表>)
      语句块;

a.相对于initial过程语句,always过程语句只要满足always后面的敏感事件列表,就执行语句块。

和negedge描述信号的上升沿和下降沿。
 

注意;

a.在过程语句(initial和always)中,被赋值信号必须定义为“reg”类型。

b.组合电路进行描述时,需要把全部的输入信号列入敏感信号列表

c.时序电路进行描述时,需要把时间信号和部分输入信号列入敏感信号列表。

 

2.2  语句块

当语句数超过一条时,需要采用语句块。
 语句块由块标识符begin-endfork-join界定的一组行为描述语句。

 

2.2.1. 串行语句块

begin-end,可以用于可综合电路程序和仿真测试程序。

        a. 串行语句块中的每条语句逐条执行。延迟时间相对于前一条语句执行结束时间。

b. 串行语句块的起始执行时间就是串行语句块中第一条语句开始执行的时间;串行语句块的结束时间就是块中最后一条语句执行结束的时间。

2.2.2. 并行语句块

fork--join,只能用于仿真测试程序

并行语句块的特点:
a. 块内语句是同时执行的,即程序流程控制一进入到该并行语句块,块内语句则同时开始执行。

b.块内每条语句的延迟时间是相对于程序流程控制进入到块内的仿真时间的。

 

1) 采用串行语句块:
 

module wave_tb1;
    reg wave;
   parameter T=10;
   initial
       begin
      wave=0;
      #T  wave=1;
      #T  wave=0;
          #T  wave=1;
      #T  wave=0;
      #T  wave=1;
      end
endmodule

(2) 采用并行语句块:

module wave_tb2;
reg wave;
parameter T=10;
initial
           fork 
          wave=0;
          #T  wave=1;
          #(2*T)  wave=0;
          #(3*T)  wave=1;
          #(4*T)  wave=0;
          #(5*T)  wave=1;
        join
endmodule

2.3  过程赋值语句

过程块中的赋值语句称为过程赋值语句。
        过程性赋值是在initial语句或always语句内的赋值,它只能对寄存器数据类型的变量赋值。

过程赋值语句有阻塞赋值语句和非阻塞赋值语句两种。
2.3.1. 阻塞赋值语句
阻塞赋值语句的操作符号为“=”,其语法格式是:
变量
=表达式;

例如: b = a;
当一个语句块中有多条阻塞赋值语句时,如果前面的赋值语句没有完成,则后面的语句就不能被执行,仿佛被阻塞了一样,因此称为阻塞赋值方式。

阻塞赋值语句的特点:
(1) 在串行语句块中,顺序执行;在并行语句块中,同时执行,没有先后之分。

(2) 执行阻塞赋值语句的顺序是,先计算等号右端表达式的值,然后立刻将计算的值赋给左边的变量,与仿真时间无关。

 

2.3.2. 非阻塞赋值语句
非阻塞赋值语句的操作符号为“<=”,其语法格式是:
变量
<=表达式;

例如:b<= a;
如果在一个语句块中有多条非阻塞赋值语句,则后面语句的执行不会受到前面语句的限制,因此称为非阻塞赋值方式。

非阻塞赋值语句的特点:
(1) 在串行语句块中,执行没有先后之分,各条语句并行执行。

(2) 执行非阻塞赋值语句的顺序是,先计算右端表达式的值,然后等待延迟时间的结束,再将计算的值赋给左边的变量

一般情况下组合逻辑电路用阻塞赋值,时序逻辑电路用非阻塞赋值。

例题:

a.程序1

module block1(din,clk,out1,out2);
    input din,clk;
    output out1,out2;
    reg out1,out2;
    always@(posedge clk)   	
        begin
                out1=din;       
                out2=out1;	
        end
endmodule

 程序1的电路结构:

b.程序2

module non_block1(din,clk,out1,out2);
    input din,clk;
    output out1,out2;
    reg out1,out2;        
    always@(posedge clk)    
        begin	    
            out1<=din;	    
            out2<=out1;	 
        end
endmodule

注意:此处的out2为out1上一阶段的值,不是此处out1的值。 

程序2的电路结构 :

 

c.将程序(2)改为阻塞赋值语句

module block2(din,clk,out1,out2);  		
    input din,clk;    	
    output out1,out2;    	
    reg out1,out2;   		
    always@(posedge clk)    		
    begin				
        out2=out1;                                               
        out1=din;			
    end            
endmodule

2.4  过程连续赋值语句(了解)

过程连续赋值语句可以在always和initial过程语句中对连线型和寄存器型变量类型进行赋值操作。
过程连续赋值不能够对寄存器型变量进行位操作,只能整体赋值。

在Verilog HDL中,过程连续赋值语句有两种类型:赋值、重新赋值语句(assign、deassign)和强制、释放语句(force、release)。


a.赋值语句和重新赋值语句
assign和deassign语句构成了一类过程性连续赋值语句,只能用于对寄存器类型变量的连续赋值操作.

b.强制语句和释放语句
force和release语句与assign和deassign语句类似,也是一种过程连续赋值语句。能对寄存器类型变量和线网类型数据进行操作。主要用于仿真测试,便于对某种信号进行临时性的赋值和测试。
注意:连续赋值语句为数据流建模,分为显式和隐式连续赋值。

2.5  条件分支语句

 Verilog HDL的条件分支语句有两种:if条件分支语句case条件分支语句

1.if条件分支语句

  条件语句只能在initialalways语句引导的语句块(begin-end)中使用,模块的其它部分都不能使用。

注意:

若为0xz,则按“假”处理;若为1,则按“真”处理,执行指定的语句块。
例如:
            if(a)    等价于  if(a==1)
  if(!a)    等价于  if(a!=1)
 

2.case条件分支语句 


相对于if语句只有两个分支而言,case语句是一种可实现多路分支选择控制的语句。

一般的,case语句多用于多条件译码电路设计,如描述译码器、数据选择器、状态机及微处理器的指令译码等。

当用case语句对控制表达式和其后的值进行比较时,必须是一种全等比较,必须保证两者的对应位全等。

真值表:

注意:

     (1)default选项相当于if-else语句中的else部分,一般要有。
(2) case语句的所有表达式的值的位宽必须相等。

例题:用case语句描述BCD数码管译码。

module BCD_decoder(out,in);
    output[6:0]out;
    input[3:0]in;
    reg [6:0]out;
    always@(in)
    begin  
        case(in)	 
            4'd0:out=7'b1111110;       //4'd0表示4位十进制,数值为0       
            4'd1:out=7'b0110000;	 
            4'd2:out=7'b1101101;	 
            4'd3:out=7'b1111001;  
            4'd4:out=7'b0110011;	 
            4'd5:out=7'b1011011;            
            4'd6:out=7'b1011111;
            4'd7:out=7'b1110000;
            4'd8:out=7'b1111111;
            4'd9:out=7'b1111011;           
            default:out=7'bx;
        endcase 
endendmodule

除了case分支语句以外,还有casezcasex这两种功能类似的条件分支语句.其真值表:

casez语句中,有一位的值是z,即认为这一位的比较结果永远是真。

而在casex语句中,则把这种处理方式进一步扩展到对x的处理,即如果比较的双方(控制表达式与值项)有一边的某一位的值是zx,那么这一位的比较就不予考虑。

 

2.6  循环语句

Verilog HDL中规定了四种循环语句,分别是forever、repeat、while和for循环语句。多用于测试仿真程序设计。

1.forever循环语句

forever语句的语法格式是:
                              forever 语句或语句块;

 

forever循环语句连续不断地执行后面的语句或语句块,常用来产生周期性的波形,作为仿真激励信号。

always语句可独立写在程序中。

forever在于不能独立写在程序中。一般用在initial过程语句中,如果在forever语句中没有加入时延控制,forever语句将在0时延后无限循环下去。

//用 forever语句产生时钟信号。
module forever_tb;	
    reg clock;	
    initial	        
    begin		    
        clock=0;		    
        forever #50	clock=~clock;	        
     end
endmodule

 

2.repeat循环语句 

语法格式是:
         
repeat(循环次数表达式)
              语句或语句块(循环体)

其中,“循环次数表达式”用于指定循环次数,它必须是一个常数、一个变量或者一个信号。
        如果循环次数是变量或者信号,则循环次数是循环开始执行时变量或者信号的值,而不是循环执行期间的值。

//使用repeat循环语句产生固定周期数的时钟信号。 
module repeat_tb;	
    reg clock;	
    initial	        
     begin		   
         clock=0;		   
         repeat(8)  	clock=~clock;	         
     end 
endmodule

3.while循环语句 

语法格式是:
        
while(条件表达式)   语句或语句块;

其中,“条件表达式”表示循环体得以继续重复执行时必须满足的条件,它常常是一个逻辑表达式。在每一次执行循环体之前都要对这个条件表达式是否成立进行判断。

 //使用while语句产生时钟信号。
module while_tb;
	reg clock;
	initial
		begin	
   		    clock=0;	   
		    while(1)	
             #50	clock=~clock; 
    	end
endmodule

4.for循环语句

语法格式是:
    for(循环变量赋初值;循环结束条件;循环变量增值)                     
                          语句块;

for语句的执行过程是:先给“循环变量赋初值”,然后判断“循环结束条件”,若其值为真,则执行for循环语句中指定的语句块,然后进行“循环变量增值”操作,这一过程进行到循环结束条件满足时,for循环语句结束。

//使用for语句产生时钟信号。
module for_clk;
reg clk;
integer i;
initial
       begin
           clk=0;
           for(i=0;i>=0;i=i+1)  
               #50 clk=~clk;      
        end
endmodule

循环语句也可以用于可综合电路的设计,当采用循环语句进行计算和赋值的描述时,可以综合得到逻辑电路。

 

3 . 结 构 化 建 模

结构描述方式就是将硬件电路描述成一个分级子模块系统,通过逐层调用这些子模块构成功能复杂的数字逻辑电路和系统的一种描述方式。
结构描述方式的描述目标是电路的层次结构,组成硬件电路的各层功能单元将被描述成各个级别的子模块。

根据所调用子模块的不同抽象级别,可以将模块的结构描述方式分成如下三类


(1) 模块级建模:通过调用由用户设计生成的低级子模块来对硬件电路结构进行说明,这种情况下的模块由低级模块的实例组成。

(2) 门级建模:通过调用Verilog HDL内部的基本门级元件来对硬件电路的结构进行说明,这种情况下的模块由基本门级元件的实例组成。

(3) 开关级建模:通过调用Verilog HDL内部的基本开关元件来对硬件电路的结构进行说明,这种情况下的模块由基本开关级元件的实例组成。

3.1  模块级建模

 

如果当前模块不再被其它模块所调用,那么这个模块一定是所谓的顶层模块在对一个硬件系统的描述中,必定有而且只能有一个顶层模块

1.模块调用方式
模块调用的基本语法格式是:
 
     模块名<参数值列表> 实例名(端口名列表);

  //一个简单的模块调用的例子。
module and_2(a,b,c); 			//2输入与门模块	  
     input a,b;	   
     output c;		 
     assign c=a&b;
endmodule



module logic(in1,in2,q);			//顶层模块	   
    input in1,in2;	   
    output q;	   
    and_2	 U1(in1,in2,q);	//模块的调用
endmodule

2.模块端口对应方式
1) 端口位置对应方式
端口位置对应方式是被调用的模块按照一定的顺序出现在端口连接表中的一种模块调用方式,其语法格式是:
模块名<参数值列表> 实例名(<信号名1>,<信号名2>,…,<信号名n>);

2) 端口名对应方式
语法格式如下:
 
 模块名 <参数值列表> 实例名(.端口名1<信号名1>,.端口名2<信号名2>,…,.端口名n<信号名n>);

3.2  门级建模

3.3 开关级建模