跳转至

RTL 概念与常用 RTL 建模

[!note] 本章与第五章 [[RTL设计与编码指导]]和第六章[[如何写好状态机]]可作为一个整体

RTL 和综合的概念

  • RTL (Register Transfer Level): 不关注寄存器和组合逻辑的细节, 通过描述寄存器到寄存器之间的逻辑功能描述电路的 HDL 层次
    • 这里的细节指的是不关注使用了多少逻辑门, 逻辑门之间的拓扑结构等
    • 特性: RTL 级描述是可综合的描述层次
  • 综合 (Synthesis): 将 HDL 语言, 原理图等设计输入翻译成门级连接 (网表), 并根据设计目标与要求 (约束条件) 优化所生成的逻辑连接, 输出门级网表文件
    • RTL 级综合指将 RTL 级源代码翻译并优化为门级网表 (可以理解为软件中的汇编, 将 C 代码汇编为汇编)

RTL 级的基本要素和设计步骤

  • 典型的 RTL 设计包含:
    • 时钟域描述
    • 时序逻辑描述 (寄存器描述)
    • 组合逻辑描述
  • 作者推荐的设计步骤
    1. 功能定义与模块划分
    2. 定义所有模块的接口
    3. 设计的时钟域
    4. 考虑设计的关键路径
    5. 顶层设计
    6. FSM 设计
    7. 时序逻辑设计
    8. 组合逻辑设计

常用 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 应该使用 alwaysif...elsecase 语句

存储器建模

  • 定义方法: 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}

同步复位和异步复位

  • 复位的目的:
    • 仿真时候使电路进入初始状态或其他预知状态
    • 对于综合实现的真实电路, 通过复位电路进入初始状态或其他预知状态
  • 同步复位:
    • 当复位信号变化时候, 并不立即生效, 只有当有效时钟沿采样到已变化的复位信号后, 才对所有寄存器复位
    • 要点: 很多目标器件的触发器本身并不包含同步复位端口, 因此同步复位会被实现为如下的结构: image.png
    • 优点:
      • 利于基于周期机制的仿真器进行仿真
      • 可以设计 100% 的同步时序电路, 利于时序分析, 其综合结果的频率往往较高
      • 仅在时钟的有效沿生效, 可以有效避免因复位电路毛刺造成的亚稳态和错误, 增强电路的稳定性
    • 缺点:
      • 由于很多目标器件的触发器不包含同步复位端口, 因此使用同步复位会增加更多的逻辑资源
      • 必须保证复位信号的有效时间足够长, 才能保证所有触发器都有效的复位
  • 异步复位:
    • 当复位信号有效沿到达时候, 无论时钟沿是否有效, 复位立即发挥功能
    • 多数目标器件都包含异步复位端口, 会被直接接到触发器的异步复位端口 image.png
    • 优点:
      • 节约逻辑资源
      • 设计简单
      • 对于大多数 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 建模:
      always @(*) begin
          casex({sel0, sel1, sel2, sel3})
              4'b1xxx: z = d;
              4'bx1xx: z = c;
              4'bxx1x: z = b;
              4'bxxx1: z = a;
          endcase
      end
      
    • 使用单个 if 建模:
      always @(*) begin
          z = 0;
          if (sel3)
              z = d;
          eles if (sel2)
              z = c;
          else if (sel1)
              z = b;
          else
              z = a;
      end
      
    • 使用多个 if 建模
      always @(*) begin
          z = 0;
          if (sel0) z = a;
          if (sel1) z = b;
          if (sel2) z = c;
          if (sel3) z = d;
      end
      
  • 综合结果:
    • 单个 ifcase: 不会综合处级联结构, 没有优先级 image.png
    • 多个 if: 综合出级联结构, 具有优先级, 距离输出最近的优先级最高, 即对于上面的代码, sel3d 的优先级是最高的 image.png

可综合的 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