story · 2020年09月27日

verilog重点解析(13题)

1、连续赋值和过程赋值之间有什么区别?
1.jpg

2、initial和always中的赋值有什么区别?

initial和always中的赋值都是过程赋值。
8eb52b8ef3471322e035263c877c52b.png

3、阻塞和非阻塞赋值之间有什么区别?

阻塞和非阻塞赋值都是过程赋值。

3.jpg

4、如何使用连续赋值建模双向nets?

assign语句构成一个连续赋值。 RHS变化立即影响LHS。 然而,LHS的任何变化都不会影响RHS。

例如,在以下声明,rhs的更改将更新到lhs,但反之不会。
4.png

System Verilog引入了一个关键字alias,能定义双向nets。

例如,在以下代码中,rhs的任何变化都会更新到lhs,反之亦然。
20914927f9df54451da6883d47092f2.png

如果上述alias换成assign,则输出如下:

lhs = 2 , rhs = 2

lhs = c , rhs = z

然而,由于定义了alias,输出如下:

lhs = 2 , rhs = 2

lhs = c, rhs = c

在上面的示例中,对net任何一侧的更改都会更新到另一侧。

5、task和function之间有什么区别?

Verilog中的task和function都可以实现常用功能,有助于代码的清晰和可维护,避免在不同位置复制大量代码。 本质上,task和function都提供了在模块中不同位置重用相同代码段的“子程序”机制。

但是,task和function在以下方面有所不同:

5.jpg

6、静态task和动态task有何不同?

动态task在关键字task和名称之间有automatic关键字。 动态task在每个task调用期间,自动分配变量内存空间,即每次调用都不会覆盖这些值。没有automatic关键字,变量是静态分配的,这意味着这些变量在不同的task调用之间共享,因此可以被覆盖。

以下示例说明了关键字automatic的效果。 这是一个不可综合的代码。

6.jpg
7.png

在上面的示例中,my\_value是task中的局部变量。 每当调用此task时,输入in\_value在5个仿真时间单位之后赋值给局部变量。 在initial begin中,有一个fork-join,它启动两个并行进程,分别在仿真时间单位#1和#2之后开始。 第1个进程赋值2给my\_value,第2个进程赋值3给my\_value。 假设没有automatic关键字,使用上面的代码运行仿真,会输出以下内容:
8.png

没有automatic关键字的事件序列如下:

1. 从仿真时间0开始启动fork-join两个进程。

2.第一个进程在#1之后调用modify\_value,并赋值局部变量my\_value为2.  此时 t = 1。

3.第二个进程在#2之后调用modify\_value,并赋值局部变量my\_value为3.  此时t = 2。

请注意现在赋值给局部变量my\_value的值被3覆盖。

4.再经过4个时间单位,即在t = 1 + 5 = 6时,第1个task调用$display。由于最新值现在是“3”, my\_value显示“3”,而不是 “2”。

类似地,对于第二个过程,即t =2 + 5 = 7,第2个task调用$display。由于最新值仍为“3”,因此此处my\_value显示“3”。

现在,在task和task名称之间使用关键字automatic,仿真输出一下内容:

30baeab7bea99459fd022c95faa9d46.png

按照上述相同的步骤,这次,由于存在关键字automatic,变量不会被其他进程覆盖。

下表总结了动态task和静态task之间的差异:
640.jpg

7、如何覆盖automatic task中的变量?

默认情况下,module中的所有变量都是静态的。 但是, task/function中的变量都可以定义为static或automatic。

以下示例组合了static或者automatic的task/function和其变量:

1、task/function和其变量都没有定义为automatic

在Verilog-1995中,task/function和其变量都是隐式静态的。 变量仅分配一次内存,多次调用将覆盖其变量。

2、static task/function

System Verilog引入了关键字static。 当task/function被明确定义为static,它的变量只分配一次内存,并且多次调用将覆盖其变量。 、

3、automatic task/function

从Verilog-2001开始,当task/function定义为automatic,其变量也是隐式automatic的。 因此,在多次调用task/function时,变量每次都会分配内存并不会覆盖。

4、static  task/function和automatic变量

SystemVerilog还允许在静态task/function中使用automatic变量。 那些没有automatic定义的变量会保持隐含的静态。 这在变量需要在task/function调用之前初始化,并且自动分配内存的情况下很有用

5、automatic task/function和static变量

SystemVerilog还允许automatic task/function中使用静态变量。没有static声明的那些变量将保留隐式automatic。 这在静态变量需要为每次调用更新变量值的场景中很有用

8、如何没有返回值地调用function?

在Verilog 2001之前,任何function调用都必须返回一个值,调用function的代码必须接收返回值。 例如,以下是语法错误:

9.png

上面例子中的一行是语法错误,因为调用了my\_funct,却没有返回值。 只有task可以在没有返回值的情况下调用。

SystemVerilog引入了void来支持没有返回值的function调用。 这使得function调用类似于task调用。

以下示例说明void的function调用:
99.png

上面的例子显示了结果:

int\_result = 7

同时:

1、function不能使用#,@等结构

2、function不能使用非阻塞赋值。

3、function返回值默认为1比特位宽

9、如何在例化时修改模块的parameter值?

如果Verilog模块使用parameter,有两种方法可以修改它值。

1)按顺序列表:

在此方法中,parameters的修改顺序和模块中声明它们的顺序一样。 例如,

parameter\_list模块包含两个参数,即width和depth,已在模块中分配默认值。 并且在example\_parameter\_list模块中实例化,并且这些parameter在不同的实例化中被不同的值覆盖。
79d5a05de70732aba9dbf3af3eadfb8.png

使用上述方法的限制是:

parameter修改值必须被按顺序修改覆盖。 例如,在上面代码中,U2实例化parameter\_list,不能跳过width 和depth直接修改num\_buses

我们有两种方法克服这种限制:

1、  在声明模块内的parameter时,将后面例化时需要改变的parameter声明在不需要改变的parameter之前。 例如,在上面的代码中,U0和U1实例化了parameter\_list .num\_buses不需要更改,所以最后声明,分配给它的是默认值。

2、  在模块实例化时,为所有的parameter分配值,包括不需要修改的parameter。 在U2实例化中,虽然只有num\_buses参数需要改变,但width and depth仍然需要分配模块中定义的相同的默认值。

2)按名称指定:

这是Verilog-2001开始提供的一项新功能,可以通过显式指定parameter来更好地修改模块的parameter。 这样,parameter值就链接到它的名字,而不是声明时的位置顺序。

使用与上面相同的模块parameter\_list,

下面的示例显示了按名称指定的相同的parameter修改覆盖。
11.png

请注意,显式地按parameter名称指定修改方式,括号中的值是修改的值。 在在U2实例化中,只需指定depth即可,无需为width指定任何修改值。

3)使用 defparam:

在此方法中,模块中的parameter根据其层次结构名称访问。 在下面的示例中,低层次模块parameter\_list在example\_defparam模块中实例化。 但是width和depth的值使用defparam修改。

12.png

以下总结了使用defparam方法的优点:

1、修改parameter值是不需要遵循parameter声明顺序

2、可以修改特定parameter,而不是重新指定所有parameters

3、通过对defparam进行分组,可以帮助进行代码维护



10、如果阻止模块例化时parameters不被改变

如果需要阻止模块中的特定parameters被改变,应该使用localparam,而不是parameter。 localparam在Verilog-2001中引入。在以下示例中,localparam用于声明num\_bits,因此尝试改变它会给出Error。

13.png

请注意,由于width和depth是使用parameter指定的,它们可以在实例化时被改变。

通常,localparam定义本地化标识符,其值来自常规parameters。



11、使用\`define和parameter有什么区别?

\`define和parameter都可以在设计中用来指定常量。 例如:
14.png
15.jpg

12、什么是派生parameters?

当一个或多个parameters用于定义另一个parameters时,则结果是派生parameters。 派生parameters可以是parameter或localparam。 在以下示例中,有两个parameter,width和 depth,,用于定义第三个parameter,num\_bits。

在这种情况下,num\_bits的值为32。

22.png

使用派生parameters可以增强RTL代码的可重用性。



13、层次化设计当中连接Ports的方法有哪些?各自的优缺点是什么?

1)按顺序连接

在此方法中,模块实例化中的端口顺序应与模块声明中的端口顺序相同。 例如,在下面的代码中,upper模块实例化lower模块,并且端口是隐式连接的,也就是说,按顺序连接的。

23.png

2)按名称连接

在此方法中,可以通过port名称,在实例化模块时显式地完成port之间的连接。如下所示,按名称连接连接进行连接port。

24.png

按名称连接提高了port连接的可读性, 和port声明的顺序不再相关,因为它们是显示连接的。

请注意,两种类型的模块port连接不能混合,也就是说,下面示例的连接方式不正确:

25.png

3)按Interface连接

SystemVerilog引入了一个interface结构,将一束nets和variables封装到一个组中。使用Interface,有助于创造可维护的代码。 对Interface定义的更改会影响所有实例化interface的模块。 下面是使用Interface结构的示例:

26.png

在上面的例子中,all\_ins是实例化的Interface,指定in1到in3端口的ports连接。



本文转载自公众号:芯片数字实验室
原文链接:https://mp.weixin.qq.com/s/ypJ24VPXA4tbKbpeDiv02w
未经作者同意,请勿转载!

推荐阅读

想了解更多内容,欢迎关注芯片数字实验室专栏,由于工具,你可以专注在更重要的事情上。
推荐阅读
关注数
12275
内容数
199
前瞻性的眼光,和持之以恒的学习~
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息