数字前端生存指南—RTL

电子说

1.3w人已加入

描述

1.为什么是RTL

在数字前端领域,RTL几乎与“设计代码”概念相同。RTL的英文全称为Register Transfer Level,中文一般翻译为寄存器传输级,原本是HDL建模的一种层次。以Verilog HDL为例,其建模层次包括系统级、算法级、RTL级、门级和开关级,其中:

*系统级和算法级:描述层次较高,较多的忽略细节时序,设计精度低,但开发容易,设计难度低
*门级和开关级:描述层次较低,设计难度很大,但设计精细度很高
*RTL级:设计难度和设计精度的trade off,同时也是EDA工具能较好处理的最高层级

这里举出一个乘加器的RTL例子,代码如下所示:

reg [7:0] a_reg;
reg [7:0] b_reg;
reg [15:0] mul_reg;
reg [15:0] c_reg;
reg [16:0] result_reg;




always @ (posedge clk)
    mul_reg <= a_reg * b_reg;


always @ (posedge clk)
    result_reg <= c_reg + mul_reg;

这段代码中涉及5个寄存器,以及寄存器之间的关系,例如mul_reg就等于a_reg和b_reg的乘积,RTL级的描述就是以寄存器为基点(例如a_regb_regmul_reg),描述寄存器之间的关系(例如mul_reg=a_reg*b_reg)的建模层次。

2.RTL拆解

2.1.R(Reg)

RTL首先为R,即寄存器,在RTL级中的寄存器为一种理想寄存器,其具有以下几种特征:

*存储:寄存器具有存储特性,在不复位和赋值的情况下会一直保持当前的值
*延迟:由于寄存器是边沿敏感器件,仅能在时钟上升沿(一般不使用下降沿)可以被赋值,因此输入的数据延迟一个cycle才能在输出上体现
*时钟:寄存器具有时钟输入端口,赋值的条件时时钟边沿+复位无效+有效信号(若有)
*复位:寄存器可以具有同步或异步复位端口,也可以没有复位端口,复位是优先级最高的赋值端口,会将寄存器输出变为复位值,且异步复位是立即有效的

对于Reg,可以分为以下几种类型:

*存储:用于保存当前的值供下次调用,使用了寄存器的存储特性,为了满足功能要求添加,在更上层的建模中可见
*打拍:用于打断组合逻辑路径,为了满足时序要求添加,删除后(匹配控制路径)对功能无影响,在更上层的建模中不可见
*状态:用于保存状态,以自身状态为下一状态的输入,常见为状态机、隐式状态机和计数器几种,是一条控制路径或子控制类路径的起点

根据Reg处于数据路径还是控制路径,常见的形态如下表所示:

乘加器

可以使用下面的例子展示,这里的例子是一个以累加器结尾的流水线,上方是控制路径,下方是数据路径。控制路径的起点是分类为状态的寄存器,形式为状态机,后续逐级对状态信息打拍,这里的目的是为了匹配流水线的延迟,因此分类为打拍,最后将状态存储供后续使用,是为了功能添加,因此分类为存储。数据路径为流水线,添加流水线的目的是提高运行速率,为了匹配时序目标,因此第一、第二、第三级流水线的分类均为打拍,而最后一级寄存器是累加寄存器,添加的目的是为了保持数据,满足累加的功能要求,因此分类为存储:

乘加器

最后,根据不同类型的寄存器,设计时需要考虑的问题都相对固定,首先分析寄存器是否有必要存在和基本的形式,分析的角度如下所示:

*状态寄存器:当前设计是集中式控制还是分布式控制,若为集中式控制,状态寄存器是否需要移动到控制模块中;若为分布式控制,状态寄存器的形态为计数器还是显式状态机。
*打拍寄存器:逻辑路径是否过长,若过长,则添加在哪个点既能合理的分割逻辑路径,又能使用较少bit的寄存器
*存储寄存器:存储的访问方式,容量需求如何,是否需要灵活同时读写,是否可以用memory代替

随后对于寄存器,有一些基本的设计要素,是每个寄存器都要考虑的,包括:

*工作在哪个时钟域下,和输入寄存器之间是否存在跨时钟域的关系
*是否需要异步和同步复位信号,若需要,位于哪个复位域(和其他哪些寄存器能一起复位)
*是否需要用前清零和用后清零
*什么是否需要对其进行刷新,刷新是否需要有效信号(是否考虑自动门控)

对其复位、自动门控和复位的处理如下表所示,其中是否添加异步/同步复位和自动门控优先级主要与位置有关,复位处理主要与分类有关:

乘加器

2.2.T(Transfer)

Transfer用于描述组合逻辑,即寄存器之间的连接关系,必须依附于寄存器(或端口)存在。Transfer可以用函数表示,描述其依附的Reg的值如何进行更新。以一个累加器为例,其代码如下所示:

input [7:0] din;
input din_vld;
reg [7:0] dout;
wire dout_transfer;


assign dout_transfer = dout + din;


always @ (posedge clk or negedge rst_b) begin
    if(~rst_b) begin
        dout <= 8'b0;
    end else if(din_vld) begin
        dout <= dout_transfer;
    end
end

其中dout是数据路径上的存储寄存器,对应的transfer的函数表达式如下所示:

乘加器

Transfer又可以分为两类,分别是有状态和无状态:

*有状态Transfer:其依附的寄存器的值作为transfor的输入,如上面的例子就是一个有状态的Transfor,一般来说,有状态的Transfor只会依附在状态寄存器、存储寄存器(累运算)上
*无状态Transfor:其依附的寄存器的值和transfor的输入无关,一般依附在打拍寄存器和一部分存储寄存器(纯存储)上

这两种类型的Transfer的例子如下图所示:

乘加器

和Reg不同,Transfer的功能和基本实现模式由设计人员通过RTL代码描述,而具体而细节的实现方式一般由综合器确定(也可以由设计人员手工指定),综合器主要会对Transfer进行优化,Transfer对应的变量可能在综合后被重命名,按原有名称在网表中可能无法找到。

2.3.L(Level)

Level就是层级,这里对R和T做一个总结,以R(和Port)为节点,以T为边,可以构建一个有向、有环、带自环边的图,如下图所示:

乘加器

图中的节点分为寄存器、端口和常数值(一般忽略),而边分为控制线和数据线,控制线负责管理刷新时刻,而数据线负责生成刷新值。按另一种说法,如果将一个寄存器的功能用一个或多个不包括分支的函数表示,则函数中的输入值就都是数据线,其他信号就是控制线,

RTL代码实质就是描述一张图的关系,如下一段代码:

input din_vld
reg cnt_en;reg [7:0] cnt;output dout_vld;
always @ (posedge clk or negedge rst_b) begin if(~rst_b) begin cnt_en <= 1'b0; end else if(cnt == 8'd200) begin cnt_en <= 1'b0; // 数据线f(x) = 0 end else if(din_vld) begin cnt_en <= 1'b1; // 数据线f(x) = 1 end
always @ (posedge clk or negedge rst_b) begin if(~rst_b) begin cnt <= 8'b0; end else if(din_vld) begin // 控制线 cnt <= 8'b0; // 数据线f(x) = 0 end else if(cnt_en) begin cnt <= cnt + 1'b1; // 数据线f(x) = cnt + 1 end
assign dout_vld = (cnt > 8'd128); // 数据线f(x) = 0 和 f(x) = 1


这段代码描述了一个计数器生成控制信号的过程,可以转换为如下所示的图。其中红线为控制线,黑线为数据线,忽略了常数值:

乘加器

3.RTL和Reg/Wire的关系

RTL中的Reg和Transfer与Verilog中常用的Reg和Wire的关系是一个老生常谈的话题,这里直接给出结论:

*Verilog中的Reg类型可以建模RTL中的Reg(时序逻辑)和Transfer(组合逻辑),但RTL中的Reg只能用Verilog中的Reg类型建模
*Verilog中的Wire类型只能用来建模RTL中的Transfer(组合逻辑),但RTL中的Transfer(组合逻辑)既可以用Verilog中的Wire类型建模,也可以用Verilog中的Reg类型建模

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分