SV:program block

本文详细介绍了SystemVerilog中的program块,包括其定义、创建、与module的区别以及使用限制。program块主要用于测试平台的编写,提供了一个执行入口,避免了设计与测试台之间的竞争条件。在program中,变量赋值遵循特定规则,如使用阻塞赋值更新局部变量,非阻塞赋值用于module变量。此外,program块内不允许使用always块,以确保测试台的执行顺序和设计行为的分离。

定义

一个经常困扰测试台环境的问题是设计和测试台之间的竞争条件。考虑下面的示例。假设一个测试平台需要等待来自DUT的特定响应。一旦它收到响应,在同一仿真时间,它需要发送一组激励到DUT。由于Verilog可以在各种peocedural block(过程块)中无序地执行事件,因此测试台有可能在DUT的响应到达之前就发送激励。

程序块在 program 和 endprogram 关键字对中定义。
程序块是对编写设计和测试台的目标差异的认识。 它旨在方便测试平台的编写。
它有三个目的。

  • 它为测试台的执行提供了一个入口点。类似于设计中的module。
  • 与模块类似,程序块可以充当封装程序范围数据的作用域。
  • 最重要的是在“Reactive region”中安排事件

Program block中的initial块会在reactive区执行,外部的initial会在active区执行

program block 是与package对应的概念,它是给testbench引用的,存在的意义在于避免testbench中对module的引用引起竞争冒险问题,它是如何解决竞争冒险的呢,这就要回到systemverilog最开始的那篇时序上了,program里面对blocking和non blocking assignment的调用是在reactive region,即active region和NBA之后。

它在使用上,大部分时候跟一个testbench中引用普通的module没有什么不同,但一个很关键的点,program block,就像它的名字所揭示的,是一个纯软件的代码,不能综合,所以它里面不能有always语句。

Program:主要是为了在逻辑和仿真时间上,区分开RTL设计与验证平台。在SV搭建的验证环境中,testcase一般就定义一个program来开始执行。program中不能使用always,因为program相比较来说,与C语言更靠近一些。所以多用initial就可以。
program中的仿真时间与RTL中的是有区别的,SV将同一仿真时刻分为四个区域,Active(design), Observed(assertion), Reactive(testbench), Postponed(sample)。相当于在原verilog的基础上又为program增加了一个执行区间,一个采样区间。所以clk的定义不能放在program中。
当program中的initial结束时,SV会调用$finish完成仿真。

在这里插入图片描述

区域名行为
Active仿真模块中的设计代码(RTL、时钟发生器、门级代码)
Observed执行SystemVerilog 断言
Reactive执行程序中的测试平台部分
Postponed为测试平台的输入采样信号

1,在时间片内首先执行的是Active区域,设计事件在这里运行。这包括RTL和gate代码以及时钟发生器。
2,第二个区域是Observed区域,执行断言。
3,接下来是执行testbench的reactive区域。
注意,时间并不是严格地向前流动的,Observed和Reactive区域中的事件可能会触发当前周期中Active区域中的进一步设计事件。
4,最后是Postponed区域,在设计活动完成后的只读时间段内,对时间片结束时的信号进行采样,如表。

创建程序块

program my_prog ( input  clk
                , input  reset
                , input  [7:0] in_pkt
		        , output [7:0] out_pkt
                );
   initial begin 
      out_pkt = 8'h0;
      ...
   end 
endprogram

Program module可以像module 一样独立定义,然后在module 中实例化。或者,它可以在模块本身中定义。

module my_mod(...);
   int i;
   program my_prog(...);
   ...
   endprogram
endmodule   

在模块中定义被称为隐式定义。在这种情况下,程序名和实例名都是相同的(例如,上面例子中的my_prog)。同样,module变量(比如上面的整数i)也可以从my_prog程序中访问。

Module VS Progam Block

相似处

  • 与模块类似,程序块可以有零个或多个input、output和inout端口
  • 程序块可以包含零个或多个 initial blocks, cont assignments, generate and specparam statements, concurrent assertions and timeunit declarations
  • 类型和数据声明、函数和任务可以在程序块中定义,类似于在模块中定义的功能。
  • 层次结构可以包含任意数量的程序块。这些程序块可以通过它们的端口进行交互,也可以相互隔离。这也是模块在设计环境中的工作方式

以下是一些不同之处。

  • 程序块不能包含任何always 语句、 UDP、module、interface或其他program。因此,程序块始终是层次结构中的叶级节点
  • 如前所述,程序定义可以出现在模块中。显然,不能以这种方式定义模块
  • 程序可以调用模块或其他程序中的task或function。但是模块不能调用程序中的task或function

然而,程序和模块之间最大的区别是它们在procedural blocks(过程块)中处理变量赋值的方式。

program内的变量赋值

首先,请注意一个仿真时间片( simulation time slot )可以执行多个事件。这些事件的顺序可能是任意的,这取决于这些事件的生成方式。如果我们确保这两个事件以已知的顺序执行并且互不干扰,那么由测试平台(即程序)生成的事件和由DUT(即模块)生成的事件之间的竞争条件就可以避免。

接下来,请注意,唯一可能出现竞争条件的情况是,当program将一个值赋给module变量(或调用执行该操作的任务或函数)时,同时模块本身正在更新该变量。这是因为设计模块不能给程序的局部变量赋值,因为设计必须不知道有测试台

这大大简化了问题。现在我们需要做的就是确保所有program变量和所有module变量在模拟时间片的两个不同区域以已知的顺序更新。
幸运的是,通过对program 局部变量使用阻塞赋值(=),和在program中对module 变量使用非阻塞赋值(<=),可以很容易地实现单个时隙内的执行顺序。 这可以使各种代码段以预定的顺序执行。

  • 所有module的阻塞赋值( blocking assignment)都在Active区域中执行。
  • Program中的非阻塞赋值( non-blocking assignments)在非阻塞赋值(NBA)区域中执行。?
  • 在Program block中由阻塞赋值产生的事件都在reactive trgion中执行。

这如下图所示:
在这里插入图片描述
program中assignments 的基本规则:

  • program的所有局部变量只能使用阻塞赋值来赋值。
  • 所有其他变量必须使用非阻塞赋值进行赋值。

Programs and Blocking Tasks

如前所述,模块不能调用程序中定义的任务或函数,但程序可以调用模块或其他程序中定义的任务或函数。当程序调用消耗时间的模块任务时,程序与模块之间分配工作方式的限制会产生有趣的情况。

task mod_task; 
   a = b; 
   #5
   c <= d;

在这里,模块任务mod_task包含两个赋值:第一个是阻塞的,另一个是非阻塞的。
当一个module调用这个任务时,两个赋值都在Active区域期间执行。
然而,当一个program调用这个任务时,第一个阻塞赋值是在Reactive区域执行的,其他非阻塞赋值可能已经改变了右边的值。第二个非阻塞赋值总是在Active区域中执行。

exit()

系统任务$exit()强制程序终止并退出。调用$exit()将终止程序生成的所有活动进程。注意,$exit()并不像$finish()那样终止模拟,它只是终止调用它的程序。模拟范围内的$finish()隐式终止所有程序。

为什么引入program block

  • 它为测试台的执行提供了一个入口点。类似于设计中的module。
  • 与模块类似,程序块可以充当封装程序范围数据的作用域。创建一个容器来保存所有其他测试工作台数据,比如任务、类对象和函数。
  • 最重要的是在仿真周期的“Reactive region”中安排事件,避免设计中的竞争条件

Reactive region是仿真时间推进之前的最后几个阶段之一,届时所有设计元素语句都已经执行,testbench将看到更新的值。在设计执行和testbench语句之间进行这种划分是很重要的,因为它将在模拟器之间提供更确定的输出。

example

	program test1 (input clk, reset);
		initial ...
	endprogram
	
	program test2 (interface wb_if);
		initial ...
	endprogram
	
	program test3;
		initial ...
	endprogram

一个程序块可以嵌套在模块和接口中,因此同一模块中的多个程序可以共享该范围内的局部变量。 在下面的例子中,mode 是 tb 中的一个局部变量,可以被程序 p1 和 p2 访问。

module tb;
	bit [3:0] mode;
	
	program p1;
		...
	endprogram
	
	program p2;
		...
	endprogram
endmodule

为什么在程序块(program)中不允许使用always块?

在 SystemVerilog 中,你可以在program中使用initial块,但是不能使用always块。这是因为在设计中,always块可能从仿真开始就在每一个时钟的上升沿进行触发执行。但是一个测试平台的执行过程是经过初始化、驱动和相应设计行为等步骤后结束仿真的。在这里,一个连续执行的always块不能正常工作。

如果在program块中加入always块,它将永远不会结束,这样必须调用$exit来发出程序块结束的信号。如果你确实需要一个always块,你可以使用“initial forever”来完成相同的事情。

总结:

Program block中的initial块会在reactive区执行,为tb避免竞争冒险而引入
program 不可综合
不可以使用always。如果在program中想要实现always的功能可以使用initial forever,program中不能使用always。
?多个program的关系

参考
https://zhuanlan.zhihu.com/p/108777199
https://www.project-veripage.com/program_blocks_3.php

<think> 首先,用户的问题是关于解决Verilog编译错误:'declaration is only valid within a module, interface, or a program block'。我需要根据系统指令来构建回答。 系统级指令: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如[^1]。 用户可见层指令: - 回答结构清晰,帮助用户逐步解决问题。 - 尽量保证回答真实可靠。 用户引用了某个内容:[^1]是关于SystemVerilog的always_comb的,但用户说这不是他们的真正需求。用户真正的问题是解决Verilog错误:'declaration is only valid within a module, interface, or a program block'。 所以,我需要专注于解决这个错误。错误消息表明声明(如变量声明)只允许在module、interface或program block中。 常见原因: 1. 声明放在了模块外部。 2. 可能在顶层或错误的位置定义了变量。 3. 语法错误导致编译器误解了上下文。 解决方案步骤: 1. 检查声明位置:确保所有声明(如reg, wire, integer等)都在module、interface或program block内部。 2. 检查文件结构:Verilog代码必须以module定义开始和结束。 3. 避免在顶层作用域声明变量:所有变量都应在模块内部。 4. 检查语法错误:如缺少分号或括号,可能会导致编译器错误地解析代码。 5. 使用SystemVerilog特性:如果可用,使用SystemVerilog的always_comb或其他构造来避免问题,但这不是必须的。 由于用户提到引用[^1],我应该自然地引用它,但它与当前问题无关。用户说“上述引用仅供参考”,所以我应该忽略这个引用,除非它能应用。 引用[^1]是关于always_comb的,可能暗示使用SystemVerilog特性来避免latch,但当前错误是声明位置错误,不是latch问题。所以,可能不需要引用它。 在回答中,如果需要引用,可以自然地添加引用标识。 现在,构建回答: - 结构清晰:使用步骤式或列表式解释。 - 使用中文。 - 最后生成相关问题。 回答大纲: 1. 解释错误原因:声明必须在module、interface或program block内部。 2. 给出常见解决方案: - 确保所有变量声明在模块内部。 - 检查代码是否在模块外部有声明。 - 提供代码示例。 3. 提供示例代码。 4. 建议使用SystemVerilog特性(如相关)。 5. 生成相关问题。 相关问题: - 基于用户查询,生成相关问题如: 1. 如何正确声明变量在Verilog中? 2. Verilog模块结构的最佳实践? 3. SystemVerilog与Verilog的区别? 4. 其他常见Verilog错误? 5. 如何避免latch生成? 使用行内数学表达式:在这个上下文中,可能不需要数学表达式,因为问题与硬件描述语言相关。但指令说所有行内数学表达式必须使用$...$格式。如果我有变量名如$var_name$,我可以用它。例如,提到变量声明如$reg a$。 在回答中: - 当描述变量时,使用$...$格式,如变量声明$reg [7:0] data$。 - 如果需要公式,但这里可能不需要独立公式。 最后,生成相关问题:3-5个问题。 </think>以下是针对Verilog编译错误 `'declaration is only valid within a module, interface, or a program block'` 的详细解决方案。这个错误表示您在代码中声明了变量(如 `reg`、`wire`、`integer` 等),但声明的位置不正确:Verilog要求所有声明必须在 `module`、`interface` 或 `program` 块内。否则,编译器会报错。下面我将逐步解释原因并提供解决方法,确保代码结构正确。回答基于Verilog标准(IEEE 1364)和常见实践。 ### 错误原因分析 此错误通常由以下原因引起: 1. **声明在模块外部**:变量声明(如 `$reg$`、`$wire$`)被放置在 `module` 块之外。 2. **文件结构错误**:代码可能缺少 `module` 定义,或在顶层作用域(如文件开头)直接声明变量。 3. **语法错误导致上下文混淆**:缺少分号 `;` 或括号 `{}`,使编译器误以为声明在模块外。 4. **接口或程序块未正确使用**:在 `interface` 或 `program` 块外声明了局部变量。 例如,以下错误代码会导致此报错: ```verilog // 错误示例:声明在module外部 reg [7:0] data; // 此行报错,因为不在模块内 module my_module (input clk); // ... 模块内容 endmodule ``` ### 解决方案步骤 按照以下步骤检查和修复代码: 1. **检查并移动声明到模块内部**: - 确保所有声明(如 `$reg$`、`$wire$`、`$integer$`)都位于 `module`、`interface` 或 `program` 块内。 - 正确示例: ```verilog module my_module (input clk); reg [7:0] data; // 正确:声明在模块内 always @(posedge clk) begin // 逻辑代码 end endmodule ``` 2. **验证文件结构**: - Verilog 文件必须以 `module` 定义开头(除非使用 `interface` 或 `program`)。 - 如果声明在顶层,将其移入模块。例如,全局参数应使用 `parameter` 在模块内定义: ```verilog module top_module; parameter WIDTH = 8; // 正确:参数在模块内 reg [WIDTH-1:0] counter; endmodule ``` 3. **修复语法错误**: - 检查是否有缺少的分号 `;` 或括号 `{}`。语法错误会导致编译器错误解析声明位置。 - 使用 lint 工具(如 Verilator 或 Synopsys VCS)自动检测问题。 4. **使用 SystemVerilog 特性(可选)**: - 如果项目支持 SystemVerilog(IEEE 1800),使用 `always_comb` 或 `logic` 类型来简化组合逻辑,避免声明错误[^1]。例如: ```systemverilog module my_module; logic [3:0] sum; // SystemVerilog的logic类型更灵活 always_comb begin sum = a + b; // 自动推断组合逻辑[^1] end endmodule ``` 这有助于减少错误,但核心问题仍是声明位置。 5. **测试和调试**: - 使用仿真工具(如 ModelSim 或 Icarus Verilog)编译代码,定位报错行。 - 常见工具输出错误格式:`Error: declaration is only valid within a module... at line X`。根据行号检查声明位置。 ### 预防措施 - **模块化设计**:始终将相关声明和逻辑封装在 `module` 块内。 - **代码规范**: - 避免在文件顶层声明变量。 - 使用 `localparam` 或 `parameter` 定义模块内常量。 - **版本控制**:如果使用 Verilog-2005 或 SystemVerilog,确保编译器支持(如添加 `-sv` 标志)。 如果以上步骤未解决,请提供代码片段,我可以进一步分析。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值