verilog初识-initial、always、task、function、递归函数automatic

本文讲述了Verilog中的initial、always、task和function的用法,涉及初始化、逻辑控制、任务和函数调用,是电路设计入门教程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

verilog初识-initial、always、task、function

1.initial

一个程序中可以有多个initial语句块,多个initial说明语句并行执行,一个initial语句块仅执行一次
initial多用于对变量的初始化。
如:(1)用initial块对存储器变量初始化

inital
	begin
		areg = 0;	//初始化寄存器变量areg
		for(index = 0; index < size; index = index + 1)
			meory[index] = 0;	//初始化memory变量
	end

(2)用initial语句生成激励波形

initial 
	begin
		inputs = b'0000;	//初始时刻inputs为0
		#10 inputs = b'1010;	//在第10个单位时间后改变inputs的值
		#10 inputs = b'0101;	//在第20个单位时间后改变inputs的值
	end

2.always

一个程序中可以有多个always语句块,每个always说明语句在仿真的开始同时执行;always语句不断重复地执行,直到仿真结束。always语句后紧跟的过程块是否运行,要看它的触发条件是否满足,一旦触发条件满足,则运行过程块,直至仿真过程结束。
其声明结构如下:
always (时序控制) 语句
always如果没有时序控制,会使仿真器产生死锁。如:
always areg = ~areg;
这条语句会生成一个0延迟的无限循环跳变过程,会导致仿真器发生死锁。
正确的使用方法如:
always #period areg = ~arge;
这条语句生产了一个周期位2*period的无限延续的波形信号,常用这种方法来描述时钟信号。
如:
reg [7:0] counter;
reg tick;
always @(posedge areg)
begin
tick = ~tick; //在每一个arge信号上升沿到来时,tick翻转,同时counter计数加1
counter = counter + 1;
end
always块的事件控制:

  • OR事件控制(其中“or”也可以使用“,”进行替换)
    • 使用电平触发的always语句块:
      如:描述一个异步复位的的触发器
      always @(reset or clock or d) //当reset、clock、d中任意一个信号位高电平时执行always语句块
      //等价于 always @(reset, clock, d)
      begin
      if(reset) q = 1’b0;//复位时,q值初始化为0
      else if(clock)
      q = d; //当clock信号为高时,锁存输入信号d
      end
    • 使用沿触发的always语句块
      如:
      always @(posedge clk, negedge reset)//此处,代替or
      begin
      if(!reset) q <= 1’b0;
      else q <= d; //在每个clk的上升沿到来时,锁存输入信号d
      end
    • @*@(*),表示对其后面语句块中所有输入变量的变化都是敏感的
      如:
      always @(*)等价于always @( a or b or c or d or e or f)
      begin
      out1 = a?b:c;
      out2 = d?e:f;
      end
  • 电平敏感时序控制
    使用关键字wait来表示等待电平敏感的条件为真
    如:
    always
    wait(count_en) #20 count=count+1; //当count_en为1时,延迟20个单位时间,count加1,否则count值保持不变

3.task

task的特点:

  • 任务可以定义自己的仿真时间;
  • 任务能启动其他任务和函数;
  • 任务可以没有或有多个任务类型的变量;
  • 任务不需要返回值。
    task说明语句的语法结构为:
    task 任务名;
    端口及数据类型声明语句;
    语句1;
    语句2;

    语句n;
    endtask
    任务的调用结构如下:
    任务名(端口1,端口2,…,端口n);
    如:
    (1)任务定义:
    task my_task;
    input a,b;
    inout c;
    output d,e;

    c = f1;
    d = f2;
    e = f3;
    endtask
    任务调用:my_task(v,w,x,y,z);含义为:任务启动时,将v、w、x变量赋值给a、b、c,任务结束时,将c、d、e的值赋给x、y、z。
    如:描述红黄绿交通灯行为:
module traffic_lights;	//定义模块名为traffic_lights
	reg clock,red,amber,green;	//定义变量
	parameter on = 1,off = 0, red_tics = 350, amber_tics = 30, green_tics = 200;	//定义参数

	initial red = off;//初始化变量
	initial amber = off;
	initial green = off;
	always
	begin
		red = on;
		light(red,red_tics);//调用任务light
		green = on;
		light(green,green_tics);
		amber = on;
		light(amber,amber_tics);
	end
	
	task light;
	output color;
	input [31:0] tics;
	begin
		repeat(tics);
		always @(posedge clock)//在clock上升沿到来时,灭灯
			color = off;
	end
	endtask
	
	always	#100 clock = ~clock;//产生时钟脉冲
endmodule

4.function

function的特点:

  • 函数只能与主模块共用同一个仿真时间单位;
  • 函数不能启动任务;
  • 函数至少要有一个输入变量;
  • 函数的目的是返回一个用于表达式的值。
    函数的语法结构为:
    function (返回值的类型或范围) 函数名;
    端口说明语句;
    变量类型说明语句;
    begin
    语句块

    end
    endfunction
    tips:
    “返回值的类型或范围”为可选项,默认时返回值为一位寄存器类型数据。
    如:
    function [7:0] getbyte;
    input [15:0] address;
    begin

    getbyte = result_expression;//将结果赋予函数的返回字节
    end
    endfunction
    函数的调用:
    函数名(表达式,…,表达式)
    如:
    word = control?{getbyte(msbyte),getbyte(lsbyte)}:0;//control为1时,将getbyte函数的高字节与低字节拼接后赋给word,否则对word清0
    如:阶乘函数的定义和调用
module tryfact;

	function [31:0] factorial;
		input [3:0] operand;
		reg [3:0] index;
		begin
		factorial = 1;//0和1的阶乘均为1
		for(index=2;index<=operand;index=index+1)
			factorial = index*factorial;//n的阶乘=n*(n-1)*(n-2)*...*1
		end
	endfunction
	
	reg [31:0] result;
	reg [3:0] n;
	initial
	begin
		result =1;
		for(n=2;n<=9;n=n+1)
		begin
			$display("partial result n=%d result =%d",n,result);
			result = n*factorial(n-1);//调用函数factorial
		end
		$display("finalresult=%d",result);
	end
endmodule
  • 递归函数(自动函数)
    verilog中的函数不能够进行递归调用,因为设计模块中若某函数在两个不同的地方被同时并发调用,由于这两个调用同时对同一块地址空间进行操作,那么计算结果将是不确定的。
    verilog中函数声明时使用关键字automatic,将函数变为自动或可递归的,即仿真器为每一次函数调用动态地分配新的地址空间,每一个函数调用对各自的地址空间进行操作。因此,自动函数中声明的局部变量不能通过层次名进行访问。而自动函数本身可以通过层次名进行调用。
    如:
module top;
...
function automatic integer factorial;
input [31:0] oper;
interger i;
begin
	if(oper >= 2) factorial=factorial(oper-1)*oper;
	else factorial = 1;
end
endfunction

integer result;
intial
begin
	result = factorial(4);
	$display("factorial of 4 is %d",result);
end

endmodule
<think>好的,我现在需要回答用户关于Verilogfunctiontask在递归操作及静态存储方面的区别的问题。首先,我需要回忆一下Verilog的基本知识,尤其是关于functiontask的特性,以及它们的存储机制。 根据用户提供的引用资料,Verilog早期的版本中,functiontask都是静态存储的,也就是说无论调用多少次,它们都使用同一个内存地址,无法保存每次调用的独立数据。这会影响递归操作,因为递归需要每次调用都有自己的存储空间来保存参数和局部变量。然而,SystemVerilog引入了动态生命期的支持,允许递归调用,但用户的问题可能集中在传统Verilog的情况。 接下来,我需要比较functiontask的不同之处。根据引用[3],函数不能包含延迟、事件或时序控制语句,而任务可以。此外,函数必须有至少一个输入,返回一个值,而任务可以有多个输入输出,不返回值。这些特性会影响它们在递归中的使用可能性。 关于递归,传统Verilog的静态存储意味着每次调用functiontask时,它们的局部变量会被覆盖,无法正确保存递归层次的数据,所以递归无法正常工作。而SystemVerilog可能通过自动或动态存储解决了这个问题,但用户的问题可能更关注传统Verilog的情况。 现在需要将这些信息结构化,分点说明functiontask在递归和存储上的区别,并引用提供的资料作为支持。需要确保使用正确的中文术语,并遵循用户给出的格式要求,比如行内数学表达式使用$...$,独立公式用$$...$$,不过这个问题可能不涉及数学公式,所以可能不需要使用。但需要检查是否有必要的地方加入。 另外,用户要求生成相关问题,我需要考虑与Verilogfunctiontask相关的其他问题,比如使用场景、优化方法、与SystemVerilog的区别等。 最后,确保回答中引用的部分正确标注引用标记,如[^1]、[^2]、[^3],并且在末尾列出相关参考文献。同时,结构要清晰,分点回答,帮助用户逐步理解。</think>关于Verilog中$function$和$task$在递归操作及静态存储的区别,以下从五个维度对比分析: ### 一、静态存储特性 1. **Verilog默认存储机制** Verilog中所有$function$和$task$默认采用**静态存储**,无论被调用多少次,其内部变量始终共享同一物理内存地址。这意味着: - 递归调用时,前一次调用的变量值会被后一次覆盖 - 无法实现真正的递归操作(如阶乘计算) ```verilog function automatic integer factorial; // SystemVerilog需加automatic修饰 input integer n; begin if(n <= 1) factorial = 1; else factorial = n * factorial(n-1); end endfunction ``` ### 二、递归支持差异 | 特性 | $function$ | $task$ | |-----------------|---------------------------------------------------------------------------|------------------------------------------------------------------------------| | **递归可行性** | 传统Verilog不可行,SystemVerilog需声明为`automatic` | 传统Verilog完全不可行 | | **变量独立性** | 静态存储导致多级调用互相干扰 | 同左 | | **应用场景** | 纯组合逻辑计算 | 包含时序控制的操作 | ### 三、参数传递机制 1. **$function$参数规则** - 至少需要1个输入参数 - 通过返回值传递结果 - 示例:`function [7:0] sum(input [3:0] a, b);` 2. **$task$参数规则** - 可包含输入/输出/双向参数 - 通过输出参数传递多个结果 - 示例:`task read_mem(output [31:0] data, input [15:0] addr);` ### 四、时序控制能力 $$ \text{时序控制支持度} = \begin{cases} \text{禁用} & \text{在} function \text{中} \\ \text{允许} & \text{在} task \text{中} \end{cases} $$ - $function$禁止包含`#delay`、`@(posedge clk)`等时序控制语句 - $task$可完整支持时序控制语句,常用于仿真场景 ### 五、SystemVerilog扩展 SystemVerilog通过`automatic`修饰符实现动态存储: ```systemverilog task automatic recursive_task; input [31:0] depth; begin if(depth > 0) recursive_task(depth-1); end endtask ``` 此时每次调用都会创建独立存储空间,支持递归操作。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值