数字门级电路可分为两大类:组合逻辑和时序逻辑。锁存器是组合逻辑和时序逻辑的一个交叉点,在后面会作为单独的主题处理。
组合逻辑描述了门级电路,其中逻辑块的输出直接反映到该块的输入值的组合,例如,双输入AND门的输出是两个输入的逻辑与。如果输入值发生变化,输出值将反映这一变化,组合逻辑的RTL模型需要反映这种门级行为,这意味着逻辑块的输出必须始终反映该逻辑块当前输入值的组合。
SystemVerilog有三种在可综合RTL级别表示组合逻辑的方法:连续赋值语句、always程序块和函数。接下来几篇文章将探讨每种编码风格,并推荐最佳实践编码风格。
连续赋值语句将表达式或操作结果驱动到网络或变量上,显式连续赋值语句是以assign关键字开始的语句。连续赋值语句的一个简单示例:
赋值的左边,即上面的sum;在上面的例子中,每当右边的值发生任何变化时,即在上面的例子中a或b发生变化时,sum就会更新。每当右边发生变化时,左边的这种持续更新行为就是组合逻辑行为的模型。
连续赋值语句允许在右侧发生更改和左侧更新之间指定传播延迟。然而,综合编译器预期RTL模型为零延迟,并且会忽略连续赋值语句中的延迟。这可能会导致经过延迟验证的设计与忽略延迟的综合实现之间不匹配。本系列文章只展示零延迟示例。
左侧类型。连续赋值语句的左侧可以是标量(1位)或向量,也可以是变量类型,也可以是用户定义的类型。左侧不能是未压缩的的结构体或未压缩的数组。
在连续赋值语句的左侧使用网络或变量之间有一个重要区别:
网络类型(如wire或tri)可以由多个源驱动,包括多个连续分配、多个模块或基本实例的输出或输入端口连接,或驱动的任意组合。
变量类型(如var或int)只能从单个源分配一个值,可以是:单个输入端口、单个连续赋值语句或任意数量的过程赋值(多个过程赋值被视为单个源;综合器要求多个过程赋值在同一个过程中)。
请注意,logic关键字推断出一种数据类型,但其本身不是网络或变量类型。当logic本身被使用时,一个变量被推断出来,并附带单个源赋值限制)。当使用logic关键字声明输出模块端口时,也会推断出一个变量。当使用logic关键字声明输入或inout模块端口时,将推断出具有多个驱动程序功能的wire类型。
最佳实践指南7-1 |
---|
在连续赋值的左侧使用变量,为防止无意中出现多个驱动,只有打算让一个信号有多个驱动时,才在左侧使用wire或tri。 |
仅当需要多个驱动时使用网络类型(如wire或tri),例如共享总线、三态总线或inout双向模块端口。
对于RTL建模,语义规则的一个重要优点是变量只能有一个来源。ASIC和FPGA设备中的大多数信号大多数为单源逻辑,但三态总线和双向端口除外。变量的单源限制有助于防止无意中的编码错误,如果对具有变量类型的同一信号进行多个连续赋值语句或连接,则多源编码错误将在仿真和综合中报告为编译或布线错误。
向量宽度不匹配。连续赋值语句的左侧可以是与右侧的信号或表达式结果不同宽度的向量大小。出现这种情况时,SystemVerilog会自动调整右侧的向量宽度,以匹配左侧的大小。如果右侧的向量宽度大于左侧,则右侧的最高有效位将被截断为左侧的大小。如果右侧是较小的向量宽度大小,则右侧值将向左扩展到左侧的大小。如果表达式或运算结果是无符号的,则左扩展将用0扩展。如果右侧表达式或运算结果是有符号的,则将使用符号扩展。
最佳实践指南7-2 |
---|
确保连续赋值语句和程序赋值的两侧向量宽度相同。避免左侧向量大小和右侧向量大小不匹配 。 |
在一些特例的情况下,赋值的右侧和左侧有不同大小的向量。这方面的一个例子是变量旋转操作(variable rotate operation)前面有介绍过,可以查看之前的文章。
连续赋值语句有两种形式:显式连续赋值语句和隐式连续赋值语句。显式连续赋值语句是用assign关键字声明的,如前面的代码段和示例所示。这种形式的连续赋值语句既可以赋值给网络类型,也可以赋值给变量类型。隐式连续赋值语句将网络类型的声明与连续赋值语句相结合。即使未使用assign关键字,这种形式的连续性质也是可以推断出来的。
推断网络声明赋值示例如下:
请注意,推断网络声明赋值语句与变量初始化语句不同,例如:
变量初始化只执行一次,而推断网络声明赋值是一个过程,每当右侧表达式上的值发生变化时,就会更新左侧网络。推断网络声明赋值语句是可综合的。
一个模块可以包含任意数量的连续赋值语句。每个连续赋值语句都是一个单独的过程,与其他连续赋值语句并行运行。所有连续赋值语句从仿真时间零点开始计算右侧运算,并运行到仿真结束。
一个模块中的多个过程分配可用于表示数据流行为,其中功能是用布尔方程建模的,布尔方程使用SystemVerilog操作符产生输出,而不是使用过程编程语句。在RTL模型中,数据流赋值表示数据在寄存器之间流动的组合逻辑。
下面的示例使用连续赋值语句来仿真通过加法器、乘法器和减法器的数据流。该数据流的结果在时钟每个正边缘被存储在寄存器中:
//`begin_keywords "1800-2012" // use SystemVerilog-2012 keywords
module dataflow
#(parameter N = 4) // bus size
(input logic clk, // scalar input
input logic [N-1:0] a, b, c, // scalable input size
input logic [ 1:0] factor, // fixed input size
output logic [N-1:0] out // scalable output size
);
timeunit 1ns; timeprecision 1ns;
logic [N-1:0] sum, diff, prod;
assign sum = a + b;
assign diff = prod - c;
assign prod = sum * factor;
always @(posedge clk)
out <= diff;
endmodule: dataflow
//`end_keywords
因为模块中的多个连续赋值语句并行运行,所以RTL源代码中赋值的顺序没有区别。这可以通过比较示例7-1中连续赋值语句的顺序和图7-1所示的综合结果中的数据流顺序看出来。RTL代码按加法、减法、乘法的顺序列出赋值语句,但操作的数据流是加法、乘法、减法。
一个模块可以包含连续赋值语句和always程序的组合。
下面的简单示例演示了一个带有双向数据总线的静态RAM。当从RAM读取数据时,数据总线作为输出端口被驱动——当不被读取时,数据总线被分配高阻态,以便其他设备可以驱动该总线,连续赋值语句用于仿真输出功能,以及always程序用于仿真输入功能(方便在时钟上升沿触发)。
数据总线是一个双向inout端口,必须是网络类型,如wire或tri,才能有多个驱动源。当数据总线是RAM的输出时,它可以由RAM驱动,当数据总线是RAM的输入时,它可以由其他模块驱动。只有连续赋值语句才能分配给网络数据类型。
每个连续赋值语句和每个always程序都是一个单独的并行过程,从仿真时间零点开始,在整个仿真过程中运行。模块中连续赋值语句和always程序的顺序并不重要,因为这些程序是并行运行的。
审核编辑 :李倩
全部0条评论
快来发表一下你的评论吧 !