经常遇见两个人在Debug的时候,第一句话往往是“你的代码风格挺好的”或者“你这代码好乱啊,我都看不懂”之类的话。其实,Verilog编码风格一直是一个令人反感的话题,因为没有完美的编码风格,也不能说一个人的编码风格不好,这好像就在说他字写得丑、人长得不好看一样,伤害到他内心的自尊。
其实每个人都有属于自己的代码风格,阅读与自己风格相似的代码总是更容易理解,心情也十分开心,而一旦遇见一个和自己风格迥异的代码段,往往都没有看下去的欲望,满脑的不舒服。但是有一点需要注意的是,每个人写的代码,不仅仅是自己的,也是世界的,重要的是自己的代码属于“靠谱的”,而不是晦涩难懂、凌乱的。
Verilog同其他语言一样,有一些基本的要求,例如代码整洁度、可读性、可修改性、可维护性以及重复移植性等。除此之外,还需兼顾逻辑功能正确、仿真速度快、综合结果优等更高要求。
对于一个开发小组或公司部门,应该共同制定一套每个开发者都认同的编码风格,共同遵守、编写最简洁的代码风格。
选择有意义的信号和变量名,对这些进行简化统一是非常重要的,命名包括信号、变量、状态等含义。
下面列举一些通用规则:
(1)使用有意义、有效的名字。
在特定代码段,例如for和generate,没必要将指针单独列举,直接使用i、j这类就好。
(2)使用缩写,统一缩写名称。
(3)最右侧下划线表示低电平有效,高电平有效信号避免使用下划线。
(4)首字符大写,其余小写。
(5)避免使用保留字。
(6)后缀非常重要。
常用后缀如下:
后缀 | 意义 |
---|---|
_Clk | 时钟信号 |
_Rstn | 低电平复位 |
_n | 低电平有效 |
_n | 低电平有效 |
_a | 异步信号 |
_r | 寄存器输出 |
_t | 临时信号 |
_next | 锁存前信号 |
_z | 连接到三态输出 |
_f | 下降沿有效 |
常用缩写如下:
全称 | 缩写 | 中文含义 |
---|---|---|
acknowledge | ack | 应答 |
address | adr | 地址 |
arbiter | arb | 仲裁 |
clock | clk | 时钟 |
control | ctrl | 控制信号 |
count | cnt | 计数器 |
check | chk | 校验 |
decode | de | 译码 |
encode | encd | 编码 |
data in | din | 数据输入 |
data out | dout | 数据输出 |
decrease | dec | 减 |
enable | en | 使能 |
error | err | 错误 |
frame | frm | 帧 |
generate | gen | 生成 |
grant | gnt | 申请通过 |
increase | inc | 加 |
input | in | 输入 |
length | len | 长度(帧长) |
output | out | 输出 |
pointer | ptr | 指针 |
rd enable | ren | 读使能 |
read | rd | 读(操作) |
ready | rdy | 应答信号准备好 |
receive | rx | 接收 |
transmit | tx | 发送 |
valid | vld | 有效信号 |
request | req | 请求 |
reset | rst | 复位 |
write | wr | 写操作 |
(1)看重设计文档,设计之前要把设计思路、数据通路、项目细节等描述清楚,做到项目可控、可实现,从易到难,逐次递进。
(2)设计时尽量使用之前的IP,使用IP开发简单可靠。
(3)单个文件单个模块,文件命名和模块名相同。
(4)顶层模块只包含各个模块的例化连接。
(5)目录结构要做到合理有效。
(6)避免造成竞争冲突。
(7)避免实例化具体门级电路。
(8)模块内部避免三态电路。
(9)注意EDA工具的综合指令,不同综合工具对代码内的综合指令处理方式不同,综合指令放到单独的脚本,避免污染设计源文件。
(10)避免使用锁存器Latch
(11)注意复位信号造成的亚稳态,推荐异步复位同步释放。
(12)所有寄存器要同时复位。
(13)谨慎使用门控时钟,门控时钟用不好会引起毛刺,造成时序问题。
(14)避免使用模块内部的时钟信号,所有时钟在测试时要来源于外部的可控时钟。
(15)避免使用模块内部的复位信号,测试的时候复位来源于外部可控信号。
(16)当个模块尽量使用一个时钟,跨时钟域要单独处理。
(17)避免同时使用上升沿和下降沿。
(18)注意多周期路径和假路径,便于做STA分析。
(19)设计时要预留测试的模式,便于后期DFT。
(20)组合逻辑敏感列表直接写always@(*)。
(21)区分阻塞赋值和非阻塞赋值,不要混用。
(22)状态机电路要合理,推荐学习三段式状态机。
(23)注意case和if else 综合出的电路区别。
(24)begin不另起行。
(25)模块实例化要按照端口名字连接。
(26)无用的信号及时删除,在完成debug后,删除所有debug模块。
(27)避免使用全局变量,localparam。
(28)注意可综合语句,tb语法不要放到设计源文件。
(29)常数声明单独放到一个文件中。
(30)parameter位置靠前。
(31)标识符区分大小写。
(32)信号按功能集中划分区域排序。
(33)组合逻辑用 “=”。
(34)时序逻辑用"<=“。
(35)#delay 在综合会忽略。
(36)不要在多个语句块中,对同一个变量赋值。
(37)一个always块,只描述一个电路。
(38)区别function和task。
(39)尽量避免直接使用乘法、除法取余数等。
(40)输入信号是wire,输出可以wire或reg。
(41)尽量使用同步设计。
(42)不对时钟信号做判断。
(43)避免过长代码,分行写最好。
(44)多利用tab和空格对齐。
(45)模块例化、连线时注意位宽。
(46)例化时检查所有端口。
这里将本人的编码风格展现出来,欢迎相互交流。
代码头关键信息包含,谁写的?是什么?什么时候写的?完成到哪了?是做什么的?
// Copyright (c) 2014-2022 All rights reserved
// -----------------------------------------------------------------------------
// Author : [email protected]
// File : cnn_mnist.v
// Create : 2022-09-15 17:06:16
// Revise : 2022-09-15 17:06:16
// Editor : HFUT Integrated Circuit Design & Research Center
// Verdion: V1.2
// Description: CNN 顶层文件
// -----------------------------------------------------------------------------
// 所有注释部分的代码仅供debug
能看懂就行
module cnn_mnist(
input sys_clk_p ,
input sys_clk_n ,
input rst_n ,
input rx_pin ,
//uart module
output wire tx_pin ,
output wire tx_pin_stm32 ,
//hdmi display module
output hdmi_clk ,
output[23:0] hdmi_d ,
output hdmi_de ,
output hdmi_hs ,
output hdmi_vs ,
output hdmi_nreset ,
inout hdmi_scl ,
inout hdmi_sda
);
always @(posedge clk_50m or negedge rst_n) begin
if(~rst_n) begin
wr_ram <= 1'b1 ;
end
else if (addra == 'd784)begin
wr_ram <= 1'b0 ;
end
else begin
wr_ram <= wr_ram ;
end
end
hdmi_top inst_hdmi_top
(
.clk_148mhz (clk_148mhz ),
.clk_100mhz (clk_100mhz ),
.pll_locked (pll_locked ),
.decision (decision ), //4bit
.rst_n (rst_n ),
.valid_out_6 (valid_out_6 ),
.clk_ram (clk_50m ),
.wr_ram (wr_ram ),
.wr_addr_ram (addra ), //10bit
.din_ram (ram_data ), //ram_data rom_data//8bit
.hdmi_clk (hdmi_clk ),
.hdmi_d (hdmi_d ), //24bit
.hdmi_de (hdmi_de ),
.hdmi_hs (hdmi_hs ),
.hdmi_vs (hdmi_vs ),
.hdmi_nreset (hdmi_nreset ),
.hdmi_scl (hdmi_scl ),
.hdmi_sda (hdmi_sda )
);