Verilog 基础(2)

上一篇总结包括逻辑值、进制表示、数据类型、关键字和运算符,接下来总结内容包括:变量赋值、条件语句、循环语句和结构说明语句。所有知识点仅代表个人理解,若有误,欢迎指教。上一篇地址:Verilog基础知识(1)

赋值语句

在verilog中的赋值主要包括:过程赋值语句持续赋值语句

持续赋值语句

持续赋值语句是 Verilog 数据流建模的基本语句。针对的数据类型主要是标量线网类型wire和向量线网类型wire[n-1:0]assign 为关键词,任何已经声明 wire 变量的连续赋值语句都是以assign 开头。

  1. 显示持续赋值语句
  • <net_declaration><range><name>;
  • assign #<delay><name>= Assignment expression;
    net_declaration:连线型变量类型
    range:变量位宽,指明了变量数据类型的宽度,格式为[msb:lab],缺省为1位。
    delay:延时量,这一项是可选的。
    例:
    assign y = m|n;
    assign #(3, 2, 4)c = a&b;
    
  1. 隐式持续赋值语句
  • <net_declaration><drive_strength><range>#<delay><name>=Assignment expression;
    drive_strength :(赋值驱动强度)是可选的,只能在“隐式连续赋值语句”格式中得到指定。它用来对连线型变量受到的驱动强度进行指定。例如:wire (weak0,strong1) out=in1&in2;

    隐式赋值就是在声明变量时赋初值。

  1. 持续赋值语句注意点
  • 赋值目标只能是线网类型(wire) ;
  • 在连续赋值中,只要赋值语句右边表达式任何一个变量有变化,表达式立即被计算,计算的结果立即赋给左边信号(若没有定义延时量);
  • 连续赋值语句不能出现在过程块中。
  • 多个连续赋值语句之间是并行语句,因此与位置顺序无关。
  • 连续赋值语句中的延时具有硬件电路中惯性延时的特性,任何小于其延时的信号变化脉冲都将被滤除掉,不会体现在输出端口上。

过程赋值语句

过程性赋值是在 initialalways 语句块里的赋值,赋值对象是寄存器、整数、实数等类型。这些变量在被赋值后,其值将保持不变,直到重新被赋予新值。
连续性赋值总是处于激活状态,任何操作数的改变都会影响表达式的结果;过程赋值只有在语句执行的时候,才会起作用。
Verilog 过程赋值包括 2 种语句:阻塞赋值非阻塞赋值

阻塞赋值

阻塞赋值属于顺序执行,即下一条语句执行前,当前语句一定会执行完毕。阻塞赋值语句使用等号 = 作为赋值符。
例:

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

上述程序综合结果为:
阻塞赋值

非阻塞赋值

非阻塞赋值属于并行执行语句,即下一条语句的执行和当前语句的执行是同时进行的,它不会阻塞位于同一个语句块中后面语句的执行。非阻塞赋值语句使用小于等于号 <= 作为赋值符。
例:

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

上述程序综合结果为:
非阻塞赋值

条件语句

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

if 条件语句

if条件语句就是判断所给的条件是否满足,然后根据判断的结果来确定下一步的操作。

  1. 形式1

    if(条件表达式) 语句块;
    
  2. 形式2

    if(条件表达式) 
    	语句块1;
    else
     	语句块2;
    
  3. 形式3

    if(条件表达式1)
    	语句块1;
    else if(条件表达式2)
    	语句块2;
    	........
    	........
    else if(条件表达式i)
    	语句块i;
    else
    	语句块n;
    
  4. 形式4:if 语句中嵌套if 语句

    if(条件表达式1)
    	if(条件表达式2)	//内嵌的if语句
    		语句块1;
    	else
    		语句块2;
    else
    	if(条件表达式3)	//内嵌的if语句
    		语句块3;
    	else
    		语句块4;
    

case 条件分支语句

相对于if语句只有两个分支而言,case语句是一种可实现多路分文选择控制的语句,比if-else条件语句显得更为万便和直观。一般,case语句多用于多条件译码电路设计。

case语句的语法格式是:

case(控制表达式)1:语句块12:语句块2
	........
	值n:语句块n
	default:语句块n+1
endcase

真值表:

case01xz
01000
10100
x0010
z0001

例: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'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
	end
endmodule

BCD数码管译码
case语句注意事项:

  1. 1到值n之间必须各不相同,一旦判断到与某值相同并执行相应语句块后,case语句的执行便结束;
  2. 如果某几个连续排列的值项执行的是同一条语句,则这几个值项间可用逗号相隔,而将语句放在这几个值项的最后一个中;
  3. default选项相当于if-else语句中的else部分,可依据需要用或者不用,当前面已经列出了敏感表达式的所有可能值,则default可以省略;
  4. case语句的所有表达式的值的位宽必须相等,只有这样控制表达式和分支表达式才能进行对应位的比较。
  5. 在使用case语句时,应包含所有状态,如果没包含全,那么缺省项必须写,否则将产生锁存器,这在同步时序电路设计中是不允许的。

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

  1. casez:用来处理不考虑高阻值z的比较过程。

    casez01xz
    01001
    10101
    x0011
    z1111
  2. casex:将高阻值z和不定值x视为不关心的情况。

    casex01xz
    01011
    10111
    x1111
    z1111

循环语句

  • Verilog 循环语句有 4 种类型,分别是 whileforrepeat,和 forever 循环。循环语句只能在 alwaysinitial 块中使用,但可以包含延迟表达式。

forever 循环语句

  • 关键字forever所引导的循环语句表示永久循环。在永久循环中不包含任何条件表达式,只执行无限的循环,直到遇到系统任务$finish为止。如果需要从forever循环中退出,则可以使用disable语句。必须写在initial语句块中
  • 例:用forever语句产生时钟信号
    module forever _tb;
    	reg clock;
    	initial begin
    		clock = 0;
    		forever #50 clock=~clock;	//产生周期为100的时钟信号
    	end
    endmodule
    

repeat 循环语句

  • 关键字repeat所引导的循环语句表示执行固定次数的循环。
  • 其语法格式是:
    repeat(循环次数表达式)
    语句或语句块(循环体);
  • repeat 循环的次数必须是一个常量、变量或信号。如果循环次数是变量信号,则循环次数是开始执行 repeat 循环时变量信号的值。即便执行期间,循环次数代表的变量信号值发生了变化,repeat 执行次数也不会改变。
  • 例:使用repeat循环语句产生固定周期数时钟信号
    module repeat _tb;
    	reg clock;
    	initial begin
    		clock = 0;
    		repeat(8) #50 clock=~clock;		//产生8个周期的时钟信号
    	end
    endmodule
    

while 循环语句

  • 关键字while所引导的循环语句表示的是一种条件循环
  • while语句根据条件表达式的真假来确定循环体的执行,当指定的条件表达式为真时才会重复执行循环体,否则就不执行循环体。
  • 语法格式:while(条件表达式) 语句或语句块;
  • 例:
    module while_tb;
    	reg [3:0]    counter ;
    	initial begin
    		counter = 'b0 ;
    		while (counter<=10) begin
    			#10 ;
    			counter = counter + 1'b1 ;	//counter 执行了 11 次
    		end
    	end
    endmodule
    

for 循环语句

  • 关键字for所要引导的循环语句也表示一种条件循环,只有在指定的条件表达式成立时才进行循环。
  • 语法格式是:for(循环变量赋初值;循环结束条件;循环变量增值) 语句块;
  • 例:
    module for_tb;
    	integer      i ;
    	reg [3:0]    counter2 ;
    	initial begin
    	    counter2 = 'b0 ;
    	    for (i=0; i<=10; i=i+1) begin
    	        #10 ;
    	        counter2 = counter2 + 1'b1 ;
    	    end
    	end
    endmodule
    
  • 注:循环变量增值,i = i + 1 不能像 C 语言那样写成 i++ 的形式,i = i -1 也不能写成 i – 的形式。

结构说明语句

该部分主要介绍initial语句、always语句、任务task和函数function

initial 语句

  • initial 语句从 0 时刻开始执行,只执行一次,多个 initial 块之间是相互独立的。

  • 如果 initial 块内包含多个语句,需要使用关键字 beginend 组成一个块语句。

  • 如果 initial 块内只要一条语句,关键字 beginend 可使用也可不使用。

  • initial 理论上来讲是不可综合的,多用于初始化、信号检测等。

  • 语法格式:

    initial
       begin
       	语句1;
       	语句2;
       	.....
       	语句n;
       end
    
  • 例:用initial过程语句对变量A,B,C进行赋值

    module initial_tb1;
    	reg A,B,C;
    	initial begin
    		A=0;B=1;C=0;
    		#100 A=1;B=0;
    		#100 A=0;C=1;
    		#100 B=1;
    		#100 B=0;C=0;
    	end
    endmodule
    

always 语句

  • initial 语句相反,always语句块的触发状态是一直存在的,只要满足always后面的敏感事件列表,就执行过程块。

  • always 语句块从 0 时刻开始执行其中的行为语句;当执行完最后一条语句后,便再次执行语句块中的第一条语句,如此循环反复。

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

    • 敏感事件可以时电平信号,也可以是时钟边沿信号;可以有一个信号,也可以有多个信号,当出现多个信号时中间用关键字or连接。
    • 当敏感信号较多时,可以采用*代替所有的敏感信号。
  • 例:4选1数据选择器

    module mux4_1(out,in0,in1 ,in2,in3,sel);
    	output out;
    	input in0,in1,in2,in3;
    	input[1:0] sel;
    	reg out;									//被赋值信号定义为“reg”类型
    	always @( in0 or in1 or in2 or in3 or sel)	//敏感信号列表
    		case(sel)
    			2'b00: out=in0;
    			2'b01: out=in1;
    			2'b10: out=in2;
    			2'b11: out=in3;
    			default: out=2'bx;
    		endcase
    endmodule
    

注:

  • 在信号定义形式方面,无论是对时序逻辑还是组合逻辑描述,Verilog 要求在过程语句( initialalways)中,被赋值信号必须定义为reg类型;

  • 采用always对组合电路进行描述时,作为输入的全部信号需要列入敏感信号列表;

  • 采用always对时序电路进行描述时,需要把时间信号和部分输入信号列入敏感信号列表。应当注意的是,不同的敏感事件列表会产生不同的电路形式。

function 语句

  • 函数function只能在模块中定义,位置任意,并在模块的任何地方引用,作用范围也局限于此模块。函数主要有以下几个特点:

    • 不含有任何延迟、时序或时序控制逻辑
    • 至少有一个输入变量
    • 只有一个返回值,且没有输出
    • 不含有非阻塞赋值语句
    • 函数可以调用其他函数,但是不能调用任务
  • 函数的声明语法:

    function [range-1:0]     function_id ;
    	input_declaration ;
    	other_declaration ;
    	procedural_statement ;
    endfunction
    
    • 函数在声明时,会隐式的声明一个宽度为range、名字为function_id的寄存器变量,函数的返回值通过这个变量进行传递。

    • 当该寄存器变量没有指定位宽时,默认位宽为 1

    • 函数通过指明函数名与输入变量进行调用。函数结束时,返回值被传递到调用处。

  • 例:

    function [N-1:0]     data_rvs ;
    	input     [N-1:0] data_in ;
    	parameter         MASK = 32'h3 ;
    	integer           k ;
    	begin
    		for(k=0; k<N; k=k+1) begin
    			data_rvs[N-k-1]  = data_in[k] ;  
    		end
    	end
    endfunction
    
    //调用函数形式
    input [N-1:0]     a;		//为函数准备参数
    reg [N-1:0]       b_temp;	//定义变量接收函数返回结果
    
    b_temp =  data_rvs(a);		//调用函数
    

task 语句

  • 任务可以在模块中任意位置定义,并在模块内任意位置引用,作用范围也局限于此模块。

  • 模块内子程序出现下面任意一个条件时,则必须使用任务而不能使用函数。

    • 子程序中包含时序控制逻辑,例如延迟,事件控制等
    • 没有输入变量
    • 没有输出或输出端的数量大于 1
  • 任务声明格式:

    task <automatic> task_id;	//<automatic>为可选项
        port_declaration ;
        procedural_statement ;
    endtask
    
    • 任务中使用关键字 inputoutputinout 对端口进行声明。inputinout型端口将变量从任务外部传递到内部,outputinout 型端口将任务执行完毕时的结果传回到外部。
    • 进行任务的逻辑设计时,可以把 input 声明的端口变量看做wire 型,把 output 声明的端口变量看做 reg 型。但是不需要用 regoutput 端口再次说明。
  • 例:

    task xor_oper_iner;
        input [N-1:0]   numa;
        input [N-1:0]   numb;
        output [N-1:0]  numco ;		//无需再注明 reg 类型
        #3  numco = numa ^ numb ;	//不用assign,因为输出默认是reg
    endtask
    
  • 任务调用:task_id(input1, input2, …,outpu1, output2, …);

    • 任务调用时,端口必须按顺序对应。

    • 输入端连接的模块内信号可以是 wire 型,也可以是 reg 型。输出端连接的模块内信号要求一定是 reg 型。

task和function的区别

task和function的区别

  • function需要在一个单位仿真时间内完成,而task中可以包含时间控制的命令,因此#@,wait等语句都不能出现在function中;
  • function不能调用task,但是task可以调用function
  • function至少需要一个input端口,同时不能出现outputinout端口,task没有这个要求;
  • function需要有返回值,task可以没有返回值;
  • function中不能使用非阻塞赋值。
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值