
你已经了解RTL代码的仿真过程及其原理
本次课内容:
RTL代码是从逻辑上描述电路的行为
具体地, 晶圆厂需要的是一个GDSII(Graphic Design System II)格式的版图文件
(3, 4)的位置有一个与门(4, 2)和(0, 2)之间有一根走线
要将RTL代码转换成类似的GDS版图, 则需要一系列EDA工具进行多个阶段的处理
布局阶段确定每一个门电路的坐标布线阶段确定如何通过线网将门电路连接起来通常, 晶圆厂会提供一个工艺设计套件(Process Design Kit, 简称PDK)
PDK中的标准单元库(standard cell library)包含了该工艺所支持的逻辑单元, 称为标准单元(standard cell)
人工将RTL代码转换为标准单元是很繁琐的
综合器的输入是RTL代码, 输出是标准单元的网表(netlist)
网表所描述的电路(包括标准单元及其连接关系), 应该和RTL代码所描述的电路在逻辑上等价
EDA工具会在后续阶段基于网表进行物理设计环节
“一生一芯”提供了一个基于开源EDA的综合和评估项目yosys-sta
综合包含较多阶段(以yosys为例)
$ yosys counter.v
yosys读入counter.v, 并进行解析,
然后将其转换为抽象语法树(AST)
;细化阶段的工作包括
hierarchy命令需要指定一个顶层模块
猜想: 在yosys的细化阶段中还进行了类似C语言编译中语义分析的工作
一些尝试:
posedge clk改为posedge countmymodule abc(clk, rst);
上述两种错误都符合Verilog的语法, 因此无法在解析文件阶段发现
autoidx 3
attribute \hdlname "counter"
attribute \top 1
attribute \src "counter.v:2.1-12.10"
module \counter
wire width 2 $0\count[1:0]
wire width 2 $add$counter.v:10$2_Y
wire input 1 \clk
wire width 2 output 4 \count
wire input 3 \en
wire input 2 \rst
cell $add $add$counter.v:10$2
parameter \A_SIGNED 0
parameter \A_WIDTH 2
parameter \B_SIGNED 0
parameter \B_WIDTH 2
parameter \Y_WIDTH 2
connect \A \count
connect \B 2'01
connect \Y $add$counter.v:10$2_Y
end
process $proc$counter.v:8$1
assign $0\count[1:0] \count
switch \rst
case 1'1
assign $0\count[1:0] 2'00
case
switch \en
case 1'1
assign $0\count[1:0] $add$counter.v:10$2_Y
case
end
end
sync posedge \clk
update \count $0\count[1:0]
end
end
attribute用于标识一些属性
attribute \src "counter.v:10.27-10.39"用于标识相应的元素在源文件中的位置wire width 2 $0\count[1:0]表示定义一个位宽为2的信号,
其名称为$0\count[1:0] ($, \,
[,
:和]这些字符都是名称的一部分)cell $add $add$counter.v:10$2表示实例化一个类型为$add的单元,
其名称为$add$counter.v:10$2
parameter表示
parameter \A_WIDTH 2表示单元的端口A的位宽为2parameter \A_SIGNED 0表示单元的端口A是无符号的connect表示
connect \Y $add$counter.v:10$2_Y表示单元的端口Y与信号$add$counter.v:10$2_Y相连process表示一个行为描述过程, 其中
assign表示信号的赋值switch-case表示根据信号的值进行条件性赋值sync表示当条件满足时对信号进行更新
RTLIL的语法虽然和Verilog不同, 但也是在描述硬件, 而且更接近网表
process和Verilog代码中的always存在对应关系+被$add取代$add这些单元, 属于yosys的内部单元库(internal
cell library)
将RTLIL中的拓扑关系通过结构图的方式进行可视化:
yosys> show
基于设计的粗粒度表示进行处理
字级单元(word-level
cells)来描述设计$为前缀
$neg, $not,
$reduce_or等$add, $eq, $mul,
$shift等$bmux, $mux,
$pmux等$dff, $dffe,
$adff等粗粒度综合的工作主要包含:
procoptfsmmemory基于设计的细粒度表示进行处理
门级单元(gate-level
cells)来描述设计
$_XXX_的形式命名
$_AND_, $_NOR_,
$_XOR_等$_AOI3_, $_MUX4_,
$_OAI4_等$_DFF_P_, $_DFF_NP0_,
$_DFFE_PP1N_等$_DLATCH_P_,
$_DLATCHSR_PPP_等
粗粒度综合的工作主要包含:
techmapsplitnets -portsopt -full工艺映射 = 从工艺无关的电路表示映射到具体工艺的实现
标准单元库的示例(来源于yosys手册)
library(demo) {
cell(BUF) {
area: 6;
pin(A) { direction: input; }
pin(Y) { direction: output;
function: "A"; }
}
cell(NOT) {
area: 3;
pin(A) { direction: input; }
pin(Y) { direction: output;
function: "A'"; }
}
cell(NAND) {
area: 4;
pin(A) { direction: input; }
pin(B) { direction: input; }
pin(Y) { direction: output;
function: "(A*B)'"; }
}
cell(NOR) {
area: 4;
pin(A) { direction: input; }
pin(B) { direction: input; }
pin(Y) { direction: output;
function: "(A+B)'"; }
}
cell(DFF) {
area: 18;
ff(IQ, IQN) { clocked_on: C;
next_state: D; }
pin(C) { direction: input;
clock: true; }
pin(D) { direction: input; }
pin(Q) { direction: output;
function: "IQ"; }
}
}
对时序逻辑单元进行工艺映射:
门级单元$_SDFFE_PP0P_被替换成DFF和一些$_MUX_
DFF就是标准单元库中的标准单元$_SDFFE_PP0P_功能完全相同的标准单元
$_SDFFE_PP0P_ =
带高有效同步复位信号和高有效使能信号的正边沿D触发器高有效同步复位和高有效使能将网表写入到文件:
输出统计报告:
目前的RTLIL还存在process这样的过程描述(结构图中的PROC节点)
proc命令是一条宏命令, 包含一系列子命令:
proc_dlatch -
将过程描述中的锁存器转换为D锁存器类型的单元proc_dff -
将过程描述中的触发器转换为D触发器类型的单元proc_memwr -
将过程描述中的存储器写操作转换为$memwr单元proc_clean - 移除空分支和空的过程描述opt_expr -keepdc - 进行表达式相关的优化
一些子命令的行为和C语言的编译优化技术类似:
proc_clean, proc_rmdead,
proc_prune, opt_expr
剩余的关键工作:
switch-case部分转换为$mux单元sync描述转换为D锁存器类型或D触发器类型的单元和编译优化类似, 综合器一般也提供优化的功能
opt命令是一条宏命令, 包含一系列子命令:
opt_expr - 常量合并和简单表达式改写opt_merge -nomux - 合并相同的单元,
但不合并选择器类型的单元opt_muxtree - 移除嵌套选择器中的不可达分支opt_reduce - 简化多输入的选择器, 与门和或门opt_merge - 合并相同的单元opt_share - 合并输入相同, 类型相同,
且不会同时激活的单元opt_dff - D触发器的常量优化和时钟复位信号合并opt_clean - 移除无用的单元和线网opt_expr - 常量合并和简单表达式改写为了方便理解, 我们用Verilog代码来呈现优化前后的语义
opt_expr) - 在一些表达式中,
若某输入为特定的常量, 或表达式符合某种特殊样式, 可对其进行简化// 优化前 | 优化后
wire a, b, c, x, y, z; | wire a, b, c, x, y, z;
// ...... | // ......
assign x = a != a; | assign x = 1'0;
assign y = b | x; | assign y = b;
assign z = c == x; | assign z = ~c;
opt_muxtree) -
在嵌套的选择器中, 某些分支因条件冲突而不可达, 可将其移除// 优化前 | 优化后
wire a, b, c, d, x; | wire a, b, c, d, x;
// ...... | // ......
assign x = a ? (a ? b : c) : d; | assign x = a ? b : d;
opt_reduce) -
对于多输入的选择器, 与门和或门, 它们中有一些输入可能相同,
可以对这些输入进行消除或合并opt_dff) -
若D触发器的数据输入端为常量, 则可将其替换为常量,
从而移除相应的D触发器单元// 优化前 | 优化后
reg [31:0] r; | wire [31:0] r;
// ...... | // ......
always @ (posedge clk) | assign r = 32'hdeadbeef;
r <= 32'hdeadbeef; |
opt_clean) -
如果某些单元和线网不影响模块的输出, 可将其移除其他优化技术包括位宽削减, 窥孔优化等, 可查阅yosys文档或相关资料
例: 用有限状态机(Finite State Machine,
FSM)识别输入中的连续3个1
| 含义 | 输入0 |
输入1 |
|
|---|---|---|---|
S0 |
初始状态 | S0/0 |
S1/0 |
S1 |
识别了1个1 |
S0/0 |
S2/0 |
S2 |
识别了2个1 |
S0/0 |
S2/1 |
对于更复杂的FSM, 存在可以优化的机会:
思路:
识别并优化FSM:
fsm命令是一条宏命令, 包含一系列子命令:
fsm_detect - FSM检测, 根据一定的规则在RTLIL中识别出FSM,
并用特殊属性标记相关单元fsm_extract - FSM抽取,
将标记的相关单元用$fsm单元替代, 并解析出状态转移表fsm_opt - FSM优化, 根据状态转移表对FSM进行优化
fsm_recode - FSM重编码,
使用独热码对状态信号进行重新编码fsm_map - 单元映射,
将处理后的$fsm单元映射回字级单元一个示例
module top (
input clk, reset, x,
output y
);
reg [1:0] s;
always @(posedge clk) begin
if (reset) s <= 2'b0;
else begin
case (s)
2'b00: s <= (x ? 2'b01 : 2'b00);
2'b01: s <= (x ? 2'b10 : 2'b00);
2'b10: s <= (x ? 2'b11 : 2'b00);
2'b11: s <= (x ? 2'b11 : 2'b00);
endcase
end
end
assign y = ((s == 2'b10) || (s == 2'b11)) && x;
endmodule另一种需要特殊处理的单元是存储器
识别并优化存储器:
memory命令是一条宏命令, 包含一系列子命令:
FPGA侧重灵活的可编程特性
ASIC追求更高的性能
64x64的存储器单元 - 总面积较小,
但读延迟可能较高32x64的存储器单元进行拼接 - 总面积较大,
但读延迟可能较优
将来再深入讨论存储器的问题