什么样的Verilog代码风格是好的风格?

写代码是给别人和多年后的自己看的。 关于verilog代码设计的一些风格和方法之前也写过一些verilog有什么奇技淫巧?
模块化设计
把所有的代码都写到一个模块里,也不是一个好的风格。 积累自己的小ip,在芯片设计阶段,就将功能模块划分仔细,划分清楚,可复用的功能做成一个可参数化ip。搭建起你的数字积木。
对齐
把编辑器设置成tap自动替换成2/4个空格。 用空格对齐代码,提高代码观赏性。
//case0 input clk; input rst_n; inout in; output [6:0] out; //case1 input clk ; input rst_n ; inout in ; output [6:0] out ;连分号也要对齐的对齐狂魔,大可不必,徒增功耗。 关于提高verilog代码编写效率的gvim插件本订阅号之前也分享过,i/o端口按如上所示风格编写好后,直接可以生成端口列表。直接写assign和always块语句,也可以直接生成定义。省去手动定义的麻烦。  
括号
用括号将表达式的条件括起来,让层次关系更清晰,一些情况不括起来功能上也没有问题,但是会引起阅读者的歧义和可读性差。善用括号,避免阅读歧义和工具理解歧义。
assign flag = cnt == 2'd1 && (mode != 2'd1 && (|v_shift) || cnt2 < 8'd15);assign flag = (cnt == 2'd1) && ((mode != 2'd1) && (|v_shift) || (cnt2 < 8'd15));  
能少写则少写
always @(posedge clk or negedge rst_n) begin if(!rst_n) begin out <= 0; end else if(en)begin out <= in; end else begin out <= out; endendalways @(posedge clk or negedge rst_n) begin if(!rst_n) out <= 0; else if(en) out <= in;end只有单行语句可以省略去begin-end,一个always块只对一个变量操作。else分支如果是保持的话,可以省略。还有begin的位置...,能少写一行算一行。 注意,在组合逻辑中else分支不写会产生锁存器,写成了保持就是组合逻辑环,这一点要和时序逻辑分开。时序逻辑中else分支不写代表保持。 always @(*)begin case(in[1:0]) 2'd0 : data[1:0] = 2'd0; 2'd1 : data[1:0] = 2'd1; 2'd2 : data[1:0] = 2'd2; default : data[1:0] = 2'd3; endcase endcase语句也一样,不写default分支会产生锁存器,如果case中的所有情况都达到,就可以不用写default分支,但在asic设计中可能工具会报lint,所以这样的写法是最完美的。  
fsm
状态机采用三段式
小tips,在写状态机时,将第二段需要保持状态循环的状态写法,写成这样的写法,可以省去很多else的分支。 万物基于状态机——状态机大法好
assign语句慎用
assign语句+三目运算符/与或门逻辑慎用
assign data_out[5:0] = data_vld0 ? data0[5:0] : data_vld1 ? data1[5:0] : data_vld2 ? data2[5:0] : data_vld3 ? data3[5:0] : 6'b0; assign data_out[5:0] = ({6{data_vld0}} & data0[5:0]) | ({6{data_vld1}} & data1[5:0]) | ({6{data_vld2}} & data2[5:0]) | ({6{data_vld0}} & data3[5:0]);这两种写法一个带有优先级另一个不带优先级,本身没什么问题。不过覆盖率的expression中会将这个表达式中的所有条件满足与否列出来。 其实很多情况是不可能出现的,比如这几个vld同时1,data为0的情况组合,但是这就需要designer一个个去waive掉,然后写出原因,选择的语句少了还好,但是如果非常多,waive起来工作量还是很大的,徒增功耗。 这样的问题可在开发阶段就避免。 always @(*)begin if(data_vld0) data_out[5:0] = data0[5:0]; else if(data_vld1) data_out[5:0] = data1[5:0]; else if(data_vld2) data_out[5:0] = data2[5:0]; else if(data_vld3) data_out[5:0] = data3[5:0]; else data_out[5:0] = 6'd0; end写成这样,所有条件跑到就可以了。并行的语句用case。用与门和或门做的表达可能会省一些cell,但是比起后来waive时漫长的体力活,省这一毛两毛的干啥。 看过一本书上的写法,将所有的组合逻辑都用assign做,把d触发器都做成ip,直接需要打拍的时候再调用。这样的思想很好,准确的将组合逻辑和时序逻辑分开,从代码到电路。 作者提到另一个原因是由于if-else和case不能传播不定态,有的eda工具有x态传播选项,可以强行传播,一般也需要license,但并不是所有的eda工具都有这个功能。 但是我个人(浅薄的)认为这样的风格不适合所有人。比如上面提到的覆盖率问题,还有代码的可读性问题,assign式的写法可读性会变差。 直接在声明wire时就给变量赋值这样的写法,连spyglass都过不了。还是先声明再用吧。 关于工具的license问题,看看公司怎么给解决。  
乘法器分时复用
一个设计要考虑ppa最优,就要考虑乘法器的数量多少以及复用能不能最大化,追求最好的设计是整个数据通路中乘法器空闲不下来。 乘法器调用方法,一般是在乘法器的输入保证寄存器输入,结果输出到各个复用模块时打一拍再使用。可以做成在进行完乘法运算后,就打拍,这样消耗的寄存器会少很多。画个图意思一下(单bit)。
修改前
修改后
修改完后的寄存器省了很多,但是乘法器的输出寄存器负载会变大,不过后端综合时约束了max_fan_out工具会自动插buffer和复制寄存器,经过实测还是会节省很多面积,把一些优化工作可以交给工具去做,了解它,信任它,使用它。
数据位宽写全
assign dout = din; always @(posedge clk or negedge rst_n)begin if(!rst_n) dout[255:0] <= 'd0; else dout[255:0] <= din; end写成这样 assign dout[3:0] = din[3:0]; always @(posedge clk or negedge rst_n)begin if(!rst_n) dout[255:0] <= 256'd0; else dout[255:0] <= din; end写代码时将数据位宽写全,方便编辑器插件自动生成定义,而且写上位宽再后续阅读代码时能一眼看变量的位宽,提高阅读效率。 寄存器赋初始值 'd0 'h0等等,功能应该没有问题,这样写工具会默认是最大32bit??lint检查工具可能会报出来,最后还是得修改,不如一次到位。  
良好的代码风格
写了这么多,总结一起,其实关于代码风格的问题,通过多看书,多看一些代码风格的写法,很多书上都有关于一些常见的风格阐述,多写代码多积累,最后形成自己的代码风格习惯,另外一般公司都会有代码规范的文档,到时候参加工作以后,一定要记得多读几遍。


DPTV-3D/dx/ix引脚功能及参数资料
Soundbar平台:支持从音频源到扬声器全程处理
怎样选择教学投影幕?
软通动力亮相华为伙伴暨开发者大会
目前数字隔离器领域的大趋势
什么样的Verilog代码风格是好的风格?
高德红外与国网湖北检修公司推动主网数字化转型”战略合作协议签署仪式
一种简洁、可拓展的RTOS任务初始化设计
iphone13运行内存会更新吗
骨传导耳机是什么?科普骨传导耳机究竟是智商税还是运动佳品
乐视超级手机1拆解,看看手机内部做工如何
滴滴上市恐怕成“梦局”
商业的本质是企业要创造独特的价值
地平线余凯亚布力谈AI:人工智能时代,让计算从云端走向终端
亚马逊CPU销售排行榜曝光,AMD竞争激烈,TOP5中除了第一的Core i7-8700K其他都是AMD的
需要了解linux设备驱动中的阻塞与非阻塞等问题
怎么解决HMI-Board在调试4bitSDHI挂载文件系统失败的问题呢?
采用LM3409P沟道MosFET设计的调光控制LED驱动器电路
松下PTZ一体化摄像机的创新应用
齿轮减速电机的单相和双相之间,有什么区别?