RTL 概念与常用 RTL 建模
[!note] 本章与第五章 [[RTL设计与编码指导]]和第六章[[如何写好状态机]]可作为一个整体
RTL 和综合的概念
- RTL (Register Transfer Level): 不关注寄存器和组合逻辑的细节, 通过描述寄存器到寄存器之间的逻辑功能描述电路的 HDL 层次
- 这里的细节指的是不关注使用了多少逻辑门, 逻辑门之间的拓扑结构等
- 特性: RTL 级描述是可综合的描述层次
- 综合 (Synthesis): 将 HDL 语言, 原理图等设计输入翻译成门级连接 (网表), 并根据设计目标与要求 (约束条件) 优化所生成的逻辑连接, 输出门级网表文件
- RTL 级综合指将 RTL 级源代码翻译并优化为门级网表 (可以理解为软件中的汇编, 将 C 代码汇编为汇编)
RTL 级的基本要素和设计步骤
- 典型的 RTL 设计包含:
- 时钟域描述
- 时序逻辑描述 (寄存器描述)
- 组合逻辑描述
- 作者推荐的设计步骤
- 功能定义与模块划分
- 定义所有模块的接口
- 设计的时钟域
- 考虑设计的关键路径
- 顶层设计
- FSM 设计
- 时序逻辑设计
- 组合逻辑设计
常用 RTL 级建模
非阻塞赋值, 阻塞赋值, 连续赋值
- 建议:
- 对于时序逻辑, 统一使用非阻塞赋值
<=
- 对于
always
模块的敏感表为电平敏感信号的组合逻辑, 统一使用阻塞赋值=
- 对于
assign
关键字描述的组合逻辑, 统一使用=
, 变量被定义为wire
型信号
- 对于时序逻辑, 统一使用非阻塞赋值
寄存器电路建模
- 注意:
- 寄存器信号声明:
reg
- 时钟输入: 在时钟的正/负沿进行处理
- 异步复位
- 同步复位
[!question] 同时使用时钟上升或下降沿的问题 不建议同时使用时钟的上下沿, 因为 PLD (Programmable Logi Device, 主要指 FPGA 和 CPLD) 的 PLL/DLL 和一些时钟电路往往只能对时钟的一个沿保证非常好的指标, 而另一个沿的抖动, 偏斜, 斜率等待指标不见得非常优化, 同时使用会造成一定的性能恶化. 推荐的做法是, 将原时钟通过 PLL/DLL 倍频, 然后使用倍频时钟的单沿进行操作
- 寄存器信号声明:
组合逻辑建模
- RTL 级常用组合逻辑的建模:
- 使用
always
- 同上面提到的, 即使信号被定义为
reg
型, 最终综合实现的结果并不是寄存器, 而是组合逻辑, 定义为reg
仅仅是语法上的需要
- 同上面提到的, 即使信号被定义为
assign
关键字描述- 信号一般被定义为
wire
类型 - 可以直接赋值, 也可以使用
?
, 对于简单的组合逻辑使用assign
和?
的组合会更加清晰
- 信号一般被定义为
- 使用
双向端口与三态信号建模
- 双向总线应该在顶层模块定义为三态信号, 禁止在顶层以外的其他子层定义双向端口
[!note] 早期的 EDA 软件和器件支持在子模块定义双向总线, 实例化三态信号, 其实从理论上讲任何子模块中定义的三态信号都可以迁移到顶层.
mux 建模
- 简单的 mux 可以使用
assign
和?
- 复杂的 mux 应该使用
always
和if...else
或case
语句
存储器建模
- 定义方法:
reg [datawidth] MemoryName [addresswidth]
- 例如:
reg [7:0] RAM8x64 [0:63]
, 定义了一个数据位宽为 8bit, 地址为 64 位的 RAM
- 例如:
- 在使用存储单元时候, 不能直接操作存储器的某地址的某位, 先将存储单元赋值给某个寄存器, 然后再对该寄存器的某位进行相关操作:
时钟分频
[!note] reference 5.3 Verilog 时钟分频 | 菜鸟教程
偶数分频
- 循环计数: 在计数周期达到分频系数中间数值
N/2
时候进行时钟翻转 - 分频后时钟占空比: 50%
串/并转换建模
reg [7:0] pal_out;
always @(posedge clk or negedge rst)
if (!rst)
pal_out <= 8'b0;
else
pal_out <= {pal_out, srl_in}
同步复位和异步复位
- 复位的目的:
- 仿真时候使电路进入初始状态或其他预知状态
- 对于综合实现的真实电路, 通过复位电路进入初始状态或其他预知状态
- 同步复位:
- 当复位信号变化时候, 并不立即生效, 只有当有效时钟沿采样到已变化的复位信号后, 才对所有寄存器复位
- 要点: 很多目标器件的触发器本身并不包含同步复位端口, 因此同步复位会被实现为如下的结构:
- 优点:
- 利于基于周期机制的仿真器进行仿真
- 可以设计 100% 的同步时序电路, 利于时序分析, 其综合结果的频率往往较高
- 仅在时钟的有效沿生效, 可以有效避免因复位电路毛刺造成的亚稳态和错误, 增强电路的稳定性
- 缺点:
- 由于很多目标器件的触发器不包含同步复位端口, 因此使用同步复位会增加更多的逻辑资源
- 必须保证复位信号的有效时间足够长, 才能保证所有触发器都有效的复位
- 异步复位:
- 当复位信号有效沿到达时候, 无论时钟沿是否有效, 复位立即发挥功能
- 多数目标器件都包含异步复位端口, 会被直接接到触发器的异步复位端口
- 优点:
- 节约逻辑资源
- 设计简单
- 对于大多数 FPGA, 都有专用的全局异步复位(GST, Global Set Reset) , 使用 GSR 资源, 异步复位到达所有寄存器的 skew 最小
- 缺点:
- 毛刺问题
- 推荐的复位电路设计: 异步复位, 同步释放
reg reset_reg; always @(posedge clk) reset_reg <= rst_ always @(posedge clk or negedge reset_reg) if (!reset_reg) // reset else // do something others
- 使用时钟将外部输入的异步复位信号寄存一个节拍后, 再送到触发器异步复位端口 -> 好处: 做 STA 分析时候, 时序工具会自动检查同步后的异步复位信号和时钟的到达/撤销时间关系, 如果因布线造成的 skew 导致该到达/撤销时间不能满足, STA 工具会上报该路径, 帮助设计者进一步分析问题
用 case 和 if... else 建模
- 一般来说:
case
是并行的, 没有优先级if...else
可以建模无优先级的判断结构if...if...if...
可以建模具有优先级的判断结构
-
优先级结构会消耗组合逻辑资源, 若非设计需要, 应该使用无优先级的判断结构
[!note] 综合工具与优先级结构 目前综合工具的优化能力越来越强, 大多数情况下可以将不必要的优先级树优化掉, 所以综合结果是否具有优先级, 很大程度上是取决于综合工具.
-
使用不同的方式进行建模:
- 使用 case 建模:
- 使用单个
if
建模: - 使用多个
if
建模
- 综合结果:
- 单个
if
与case
: 不会综合处级联结构, 没有优先级 - 多个
if
: 综合出级联结构, 具有优先级, 距离输出最近的优先级最高, 即对于上面的代码,sel3
和d
的优先级是最高的
- 单个
可综合的 Verilog 语法子集
- 可综合:
- 模块声明:
module...endmodule;
- 端口声明:
input
,output
,inout
- 信号类型:
wire
,reg
,tri
等,integer
用于在for
语句中索引 - 参数定义:
parameter
- 运算操作符: 各种逻辑操作, 移位操作, 算数操作符
- 比较判断:
case...endcase
,if...else if...else
- 连续赋值:
assign
,<cond> ? <value1> : <value2>
always
模块: 建模时序和组合逻辑- 语法分隔符:
begin...end
- 任务定义:
task...endtask
- 循环语句:
for
- 模块声明: