最近测试有些进展,但也碰到了许多令人尴尬的问题。
但问题不大,吸取经验教训才能进步。
说回到这次碰到的问题。片上做的i2c接口实测时发现读取出现问题,体验了一波从实测追溯到仿真的过程。具体来说:
-
如果有一套fpga代码有一套asic代码,版本管理做好,确保一致性
-
fpga验证pass不能代表asic代码就没问题,该仿真的地方得仿真覆盖,不能跳过
-
具体来说的流程:设计文档编写->RTL编写->RTL仿真验证->spyglass检查->FPGA验证->dc综合->dc网表fm检查->dc网表仿真验证->后端pnr->后端网表fm检查->后端网表验证仿真->LVS检查,如果出了问题就是倒过来查验一遍
-
涉及数模接口的部分必须混仿覆盖到
之前一直偷懒,跳过spyglass,fm和dc网表仿真的环节,当然这次是RTL上就出了问题但是没有验证覆盖到。
总体而言,小心驶得万年船。贴个更新后的组内规范上来:
数字前端规范 v1.3
编写人:袁易扬
联系方式:[email protected]
文档版本 | 编写日期 | 说明 |
---|---|---|
v1.0 | 2023.05.19 | 初次发布 |
v1.1 | 2023.05.20 | 勘误分频器模板说明 |
v1.2 | 2023.05.26 | 勘误工具版本 |
v1.3 | 2024.07.24 | 细化各条目说明,设计模板文档独立 |
1. 工具链
数字前端工具链:
编译工具:
Synopsys VCS 2018
仿真工具:
Synopsys Verdi 2018
综合工具:
Synopsys Design Compiler 2018
代码检查工具:
Synopsys Spyglass 2016
一致性校验工具:
Fomality 2018
虚拟机下载链接:https://pan.baidu.com/s/19F-tL6-NoM2vmmUlBXE2rw?pwd=asbg
工具链安装包下载:https://mp.weixin.qq.com/s/nHSd1UkOcXe8qaaZtPpJTQ
建议无经验新生直接安装虚拟机,有一定的linux系统使用经验的可安装物理机。
Ubuntu20.04上安装完全套工具链,使用正常。约一个月linux办公环境使用体验 - sasasatori - 博客园 (cnblogs.com)
2. 数字总流程
设计文档编写->RTL编写->RTL仿真验证->spyglass检查->FPGA验证->dc综合->dc网表fm检查->dc网表仿真验证->后端pnr->后端网表fm检查->后端网表验证仿真->LVS检查
3. 文档规范
原始文档建议使用markdown进行编辑,markdown编写软件推荐使用Typora(可以下载免费版,也可以购买付费版),也可以使用vscode安装markdown插件后作为markdown编辑器使用。
设计文档格式可参考《设计文档模板》;
验证文档格式可参考《验证文档模板》;
测试文档格式可参考《测试文档模板》‘
此外文档需要注明以下信息:
- 文档作者
- 作者联系方式
- 文档版本
- 文档编写日期
若文档存在历史版本,需要说明各版本修改信息。
4. 数字编码规范
主要参考华为内部编码标准。以下规范均只涉及可综合设计部分,对于testbench以及第三方ip不应用以下规范。
4.1 设计风格
- 低电平有效的信号,信号名后缀"_n"
- 模块名统一使用小写+下划线方式风格,例如"test_module"
- 模块例化使用u_xx表示,需要多次例化的模块使用序号表示(0、1、2),例如"u_test_0"
- 使用降序定义向量位宽,最低位为0,例如"wire [3:0] vector"
- 采用小写字母定义wire,reg和input/output
- 采用大写字母定义参数,参数名小于20个字母,例如"parameter PARAM = 2'b00"
- 时钟信号应前缀"clk",复位信号应前缀"rst"
- 对于一个时钟上产生分频时钟应后缀分频比例,例如"clk_128",表示clk信号分频128倍后产生的时钟
- 代码中不能使用VHDL,Verilog或SystemVerilog的保留字
- 在进行模块声明时,按照如下顺序定义端口信号:输入、输出,例如:
module test(
input a,
input b,
output c
);
- 不要书写空的模块,每个模块至少有一个输入和一个输出
- 时钟事件必须要以边沿触发的形式书写,即"posedge <clk_name>"或"negedge <clk_name>"
- 异步复位,高电平有效使用"if(<asynch_reset>)",低电平有效用"if(!<asynch_reset>)"
- 代码中给出必要的注释
- 每个文件应包含一个文件头,包含作者,日期等基本信息,即
//--------------------------------------------------------------------
// ___ _____ ______ _______ ________ ___ _________
// |\ \|\ _ \ _ \|\ ___ \ |\ __ \|\ \|\___ ___\
// \ \ \ \ \\\__\ \ \ \ __/| \ \ \|\ /\ \ \|___ \ \_|
// \ \ \ \ \\|__| \ \ \ \_|/__ \ \ __ \ \ \ \ \ \
// \ \ \ \ \ \ \ \ \ \_|\ \ __\ \ \|\ \ \ \ \ \ \
// \ \__\ \__\ \ \__\ \_______\\__\ \_______\ \__\ \ \__\
// \|__|\|__| \|__|\|_______\|__|\|_______|\|__| \|__|
//
// COPYRIGHT 2023, ALL RIGHTS RESERVED
// Institute of Microelectronics, Chinese Academy of Sciences
// Beijing Institude of Technology
//
// Filename:
// Author:
// Date:
//
// Project:
// Description:
//--------------------------------------------------------------------
- 每个文件只包含一个模块
- 模块名与模块名保持一致,例如模块命名为"module test",则文件名必须为"test.v"
- 模块名或变量要使用缩写时请参考缩写规范:(382条消息) 【精】Verilog语言缩写规范_verilog 常用缩写_heartdreamplus的博客-CSDN博客
4.2 设计可靠性
- 同步时序逻辑的always block中有且只有一个时钟信号,并且所有在同一个边沿动作(如上升沿)
- 采用同步设计,避免使用异步逻辑(全局异步复位除外)
- 避免将时钟信号作为数据信号输入
- 不要在时钟路径上添加任何buffer(即让原始的时钟信号穿过任何逻辑再输出到其他模块作为时钟)
- 不要在复位路径上添加任何buffer(即让原始的复位信号穿过任何逻辑再输出到其他模块作为复位)
- 在顶层模块中,时钟信号必须可见
- 不要采用向量的方式定义一组时钟
- 建议使用单一的全局同步复位电路或者单一的全局异步复位电路
- 不使用不可综合的语法
- 不使用不可综合的运算符
- 避免使用inout端口
- 避免在多个逻辑块中驱动同一个信号
- 时序always块禁止使用电平驱动
- 数据位宽要匹配
- 时序逻辑块统一使用非阻塞赋值(<=)
- 组合逻辑块统一使用阻塞赋值(=)
- 避免产生latch
- 避免产生异步数据环路
4.3 其他规则
基本模板:
强调:所有文件使用UTF-8格式进行编码!
为了便于markdown表格与verilog的自动转换,模块端口声明请采用方向声明与类型声明分离的写法,为了简洁,input端口不声明端口类型,缺省为wire。output端口单独进行reg类型的声明。
优化数字前端工作流的小脚本 - sasasatori - 博客园 (cnblogs.com)
积极使用参数化设计以提高模块的重用性。
在模块内部进行声明时按照模块端口->参数->内部信号的顺序进行声明。
例子:
module my_module # //例化参数
(
DATA_WIDTH = 32,
ADDR_WIDTH = 5
)
( //模块端口
input clk, // 时钟
input rst_n, // 复位
input [ADDR_WIDTH-1:0] inst_addr, // 指令地址
output [DATA_WIDTH-1:0] inst_dout // 指令数据
);
// 端口reg
reg [DATA_WIDTH-1:0] inst_dout;
// 参数
parameter DEPTH = 32;
// 内部信号
reg [DATA_WIDTH-1:0] sram [DEPTH-1:0];
// 逻辑块
always @ (posedge clk or negedge rst_n) begin
...
end
// assign语句
assign ...
endmodule
以下常用模块参照模板格式进行编码:
- FSM模板
使用三段式状态机,输出逻辑使用时序逻辑
module fsm(
input clk,
input rst_n,
input [1:0] in,
output [1:0] out
);
reg [1:0] out;
parameter IDLE = 2'b00;
parameter STATE1 = 2'b01;
parameter STATE2 = 2'b10;
reg [1:0] current_state, next_state;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
current_state <= IDLE;
end else begin
current_state <= next_state;
end
end
always @ (*) begin
case(current_state)
IDLE: begin
if(in == 2'b01) begin
next_state = STATE1;
end else begin
next_state = IDLE;
end
end
STATE1: begin
if(in == 2'b10) begin
next_state = STATE2;
end else begin
next_state = STATE1;
end
end
STATE2: begin
if(in == 2'b01) begin
next_state = IDLE;
end else begin
next_state = STATE2;
end
end
default: next_state = IDLE;
endcase
end
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
out <= 2'b00;
end else begin
case(current_state)
IDLE: out <= 2'b00;
STATE1: out <= 2'b01;
STATE2: out <= 2'b10;
default: out <= 2'b00;
endcase
end
end
endmodule
- regfile
写端口使用时序逻辑,读端口使用组合逻辑,采用参数化设计以便控制位宽
module regfile #(
parameter ADDR_WIDTH = 5,
parameter DATA_WIDTH = 32,
parameter DEPTH = 32
) (
input clk,
input rst_n,
input wren,
input [ADDR_WIDTH-1:0] raddr1,
input [ADDR_WIDTH-1:0] raddr2,
input [ADDR_WIDTH-1:0] waddr,
input [DATA_WIDTH-1:0] wdata,
output [DATA_WIDTH-1:0] rdata1,
output [DATA_WIDTH-1:0] rdata2
);
output reg [DATA_WIDTH-1:0] rdata1;
output reg [DATA_WIDTH-1:0] rdata2;
reg [DATA_WIDTH-1:0] regs [DEPTH-1:0];
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
for(int i=0; i<DEPTH; i=i+1) begin
regs[i] <= 0;
end
end else if(wren) begin
regs[waddr] <= wdata;
end
end
assign rdata1 = regs[raddr1];
assign rdata2 = regs[raddr2];
endmodule
- 译码逻辑
针对简单的译码逻辑可以使用case语句进行生成
module decoder(
input [1:0] in,
output [3:0] out
);
reg [3:0] out;
always @ (*) begin
case(in)
2'b00: out = 4'b0001;
2'b01: out = 4'b0010;
2'b10: out = 4'b0100;
2'b11: out = 4'b1000;
endcase
end
endmodule
针对复杂译码逻辑(如cpu的指令译码)为了避免综合时产生latch,强烈建议使用wire和assign的纯组合语法,例如:
module decoder(
input [1:0] in,
output[3:0] out
);
assign out[0] = (in == 2'b00) ? 1'b1 : 1'b0;
assign out[1] = (in == 2'b01) ? 1'b1 : 1'b0;
assign out[2] = (in == 2'b10) ? 1'b1 : 1'b0;
assign out[3] = (in == 2'b11) ? 1'b1 : 1'b0;
endmodule
- 分频逻辑
采用参数化设计
module clk_divider
#(
parameter DIV = 10
)
(
input clk,
input rst_n,
output out
);
reg out;
reg [7:0] cnt;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= 0;
out <= 0;
end else if (cnt == DIV - 1) begin
cnt <= 0;
out <= ~out;
end else begin
cnt <= cnt + 1;
end
end
endmodule
5. 基本工程结构
所有路径描述务必均使用相对路径,避免出现任何工程文件夹外的路径。
数字前端示例工程结构:
.
|-- doc
| `-- xxx.pdf
|-- lib
| `-- xxx
|-- output
| |-- netlist
| `-- gds
|-- prj
| `|-- makefile
| -- filelist.f
|-- src
| |-- rtl
| `-- sdc
|-- tb
| |-- data
| `-- tb_xxx.v
`-- work
|-- dc
|-- fm
|-- spyglass
|-- vcs
`-- verdi
6. 推荐阅读资料
Verilog中可综合与不可综合的语句 - 知乎 (zhihu.com)
综合工具-DesignCompiler学习教程 - 知乎 (zhihu.com)
使用 Design Compiler 评估 RTL 设计 - 知乎 (zhihu.com)
DC report_timing 报告分析(STA)_北方爷们的博客-CSDN博客
标签:__,--,Flow,WIDTH,模块,反思,input,IC,out From: https://www.cnblogs.com/sasasatori/p/18325095