基于Verilog语言的可维护性设计技术
【注】本文内容主体部分直接翻译参考文献[1]较多内容,因此本文不用于任何商业目的,也不会发表在任何学术刊物上,仅供实验室内部交流和IC设计爱好者交流之用。
“曲意而使人喜,不若直节而使人忌;无善而致人誉,不如无恶而致人毁” ——《菜根谭》
【摘要】 本文以VerilogHDL为例,从可重用性、代码可扩展性、可读性、变量本地化、参数(parameter)和宏(`define)的对比以及封装子程序的角度探讨了可维护性设计应遵守的几条基本原则。
【关键词】设计重用; 可维护性设计; 本地化; 参数; 宏
前言
随着集成电路制造技术的发展,对设计提出了更多的挑战,随着设计复杂度的增加,又提出了片上系统(SoC)的概念。为了加速设计收敛,设计重用、可测性设计、可验证性设计和可维护性设计得到了更多重视。本文以VerilogHDL为例,对可维护性设计进行了初步探讨。
1、设计重用与可维护性设计 设计重用是一个很大的概念,严格来讲,可验证性设计和可维护性设计都在设计重用之列。可维护性设计的目的本身就是便于设计重用,便于让后来人读懂前人所写的代码,但设计重用包括的内容更广泛。 设计重用讲的是设计总体风格而不是设计的细节,“it is about forests, rather than trees”。
最重要的概念:本地化(locality)——一定要尽量使问题本地化,这样有利于问题的排除。对于大的设计尤为重要。
三条基本原则
尽量采用全同步设计,将输入和输出寄存,这样有利于将时序优化本地化。
采用bottom-up验证方法,保证每个子模块的功能是正确的,然后再进行整体验证。
花时间设计一个好的设计规范,力图使这样的规范具有好的体系结构和模块划分,这样有利于本地化的有效实现。
一个好的设计应该包括:完备的文档、好风格的代码、详尽的注释、良好易用的验证环境和鲁棒的脚本。 影响设计重用的障碍本质上说是管理和企业文化;开发和管理内部IP资源是最大的重用挑战。 关于设计重用这里不再讨论,否则就有喧宾夺主之嫌了。
2、关于代码可扩展性的一点讨论 代码可扩展性实际上也包括在设计重用之内。代码可扩展性或称可扩展性设计要求便于向将来新的设计过渡。比如64位的PCI标准出来的时候,现行的32位PCI接口设计模块可以通过比较简单的改动而变为64位,而不需要一切从头再来。一般设计中通过定义参数和宏来便于修改。
典型例子就是总线: `define WORD 16 `define DWORD 32 reg [`WORD-1:0] intruction reg [`DWORD-1:0] data_bus,addr_bus;
当然代码可扩展性不仅仅包括参数和宏的使用,如下例: module tri_buf(in,out, ena); parameter WIDTH=8; input [WIDTH-1:0] in; output[WIDTH-1:0] out; input ena; assign out= ena ? in : ’bz; endmodule
这是一段有潜在问题的代码,Verilog将高位扩展为0来匹配输出,所以当WIDTH>32时,上述代码是有问题的,仿真的时候甚至都看不出来,因此不利于可扩展性。象这样的问题可以用Verilog lint等工具来检查。
3、可读性最重要 可读性最重要。许多设计者有个习惯:追求尽量用短的代码来完成同样的功能,国内许多考试中也要求用少于多少行的代码完成某某功能(很不好的倾向)。
对于HDL,我不推荐这样的风格,因为综合工具会帮助你完成优化工作。
我的建议是:同样的代码可长可短,不要为了减少行数而影响可读性。如下例: Module rrarb(request,grant,reset,clk); Input [1:0] request; Output[1:0]grant; Input reset; Input clk; Wire winner; Reg last_winner; Reg [1:0] grant; Wire[1:0] next_grant;
Assign next_grant[0]=~reset&(request[0] & (~request[1]|last_winner)); Assugn next_grant[1]=~reset& (request[1] &(~request[0]| ~last_winner)); Assign winner=~reset & ~next_grant[0] &(last_winner | next_grant[1]);
Always @(posedge clk)Begin Last_winner=winner; Grant=next_grant; End
endmodule
当request[1:0]=2’b00,时,last_winner会发生什么变化?上面的代码可读性较之下面的代码差很多。
Module rrarb(request,grant,reset,clk); Input [1:0] request; Input reset; Input clk;
Output[1:0]grant;
Wire winner; Reg last_winner; Reg [1:0] grant;
Always @(posedge clk)Begin If(reset) begin Grant<=2’b00; Last_winner<=0; End Else begin Grant<=2’b00; If(request!=2’b00) begin:find_winner Reg winner;//模块内的局部变量 Case(request) 2’b01:winner<=0; 2’b10:winner<=1; 2’b11:if (last_winner==1’b0) winner<=1; else winner<=0; default:winner<=0; endcase grant[winner] <=1’b1; last_winner<=winner; end end endmodule
上述两个代码的综合结果差不多。而且上面第一段代码在always块内采用阻塞赋值的方法也存在潜在问题。
4、要有好的注释风格 要有好的注释风格。减少注释的行数跟节约代码的行数一样,我不推荐。像代码有好坏一样,注释也有好坏之分。比如: //increment addr addr<=addr+1;
上例中的注释就是一句废话,因为地址加1是不言自明的。这样的注释反而会影响可读性。好的注释应该给出该段代码的使用目的,它能够给不熟悉这段代码的人以信息。如下例:
//In burst mode,the bytes are written in consecutive addresses.
//Need to access the next address to verify that the next byte was properly saved. Addr<=addr+1;
关于如何注释我在设计重用里面讲过,这里不再重复,请参考[2]。
5、应尽量将声明本地化 这种办法有点类似于C++中封装的概念,应尽量保持变量的可见性在必要的范围内,不要产生不必要的交互。 如下例: integer I;
always begin for(I=0;I<32;I=I+1) begin … end end
always begin for(I=15;I>=0;I=I-1) begin … end end
对于两段代码来讲,I是全局的,他们存在的的不必要的交互会产生不可预料的结果。
在verilog里面,你可以将I定义到always块里面,使I只对当前块可见。如下例:
always begin:block1 integer I; for(I=0;I<32;I=I+1) begin … end end
always begin:block2 integer I; for(I=15;I>=0;I=I-1) begin … end end
在Verilog里面其它使变量本地化的方法是采用function 和task。Function和task中定义的变量只对其内部可见。 Task send; Input [7:0] data; Reg parity; Begin … end endtask
function [31:0] average; input [31:0] val1; input[31:0] val2; reg [32:0] sum; begin sum=val1+val2; average=sum/2; end endfunction;
另外,还要提一句关于`define VS parameter。
一般设计中都要有一个头文件,里面用`define定义了一些宏。而模块中又有可能用parameter定义一些参数。
两者的区别是一个是全局的,一个是本地的。设计者可以根据需要进行定义。定义成本地参数可以避免同其他模块的变量名字冲突。定义成全局的宏则可以在整个设计的各个文件中使用。千万不可为了方便全部定义成宏。
6.子程序封装 封装有利于程序的可维护性。如果程序中多次用到一段代码,应根据实际情况尽量将它定义为function、task或module。这样做可以减少代码长度,提高可维护性和可验证性。下面以testbench为例说明。
通常一个SoC设计中,需要首先设计和验证各个子模块,然后才进行系统级验证,因此要多次用到testbench。每个testbench中都要初始化时钟、进行系统复位等。这些多次用到的代码不如写在单独的一个文件(不妨称作basic.v)中,各个testbench只要使用include将他们包括进来就可以直接使用了。 下面是我在最近的863项目中写的basic.v,去掉一些注释后的内容,可以直接copy使用。 要求将时钟定义为clk,复位管脚定义为rst,高有效。
//start of basic.v event ENDSIM; // CYCLE monitor and end-of-simulation checker. task monitor_cycles; input max_cycles; integer max_cycles; integer cycles; begin cycles = 0; fork // Count cycles. forever begin @(posedge clk); cycles = cycles + 1; end // Watch for max cycles. If we detect max cycles then throw our testbench ENDSIM event. // begin wait (cycles == max_cycles); $display ("MAXIMUM CYCLES EXCEEDED!"); ->ENDSIM; end join end endtask // Reset task reset; begin rst = 1; #200; rst = 0; $display ("End RESET."); end endtask // Drive the clock input task drive_clock; begin clk = 0; forever begin #(`CLKLO) clk = 1; #(`CLKHI) clk = 0; end end endtask
// ************* BASIC CONFIDENCE Test Tasks ************** // // BASIC CONFIDENCE Test. // // This task will fork off all the other necessary tasks to cause reset, drive the clock, etc. etc. // // task clk_rst_gen_and_stop_at; input cycle_num; begin $display ("designed by Chenxi. Email:chenxiee@mails.tsinghua.edu.cn"); fork // Capture data capture_data; // Run the clock drive_clock; // Do a reset reset; begin monitor_cycles(cycle_num); end // Catch end of simulation event due to max number of cycles or pattern from PIC code. begin @(ENDSIM); // Catch the event. $display ("End of simulation signalled. Killing simulation in a moment."); #0; // Let anything else see this event... $stop; end join end endtask //end of basiv .v
在testbench中可以直接例化使用。
//somemodule_tb.v module somemodule_tb; `include “basic.v” initial begin // ** This is our top-level "Basic Confidence" test. … //generatr rst,clk,and stop at 5000 cycles clk_rst_gen_and_stop_at(5000); … end … endmodule //End of somemodule_tb.v
【注意】basic.v不能够单独编译,直接编译testbench就可以了。上述方法也在可综合的代码中使用。 参考文献[1]推荐的另外一种办法是将上述basic.v用模块封装起来。如下例:
module basic; integer warnings; integer errors; initial begin warnings=0; errors=0; end task warning; input[80*8:1] msg; begin $write(“Warning at %t: %s”,$time,msg); warnings=warnings+1; end endtask task terminate; begin $write(“Simulation Completed\n”); $stop; end … endmodule
调用办法:
module somemodule_tb; initial begin … if(…) basic.warning(”Unexpected response detected”); … basic.terminate; end endmodule
7、结论
本文VerilogHDL为例,从可重用性与可维护性的关系、代码可扩展性设计、尽量增加可读性和使变量本地化、参数(parameter)和宏(`define)的对比以及封装子程序的角度探讨了可维护性设计应遵守的几条基本原则。作者的HDL coding经验不多,权当抛砖引玉。 “公平正论不可犯手,一犯手则玷污终身”,“小处不渗漏,暗处不欺隐,末路不怠荒,才是真正英雄”,因此一定要依照以上原则进行可维护性设计!,不要试图降低师弟妹们对你的崇拜程度。 欢迎大家提出宝贵意见。联系方式: QQ:51559222 参考文献 [1]Janick Bergeron,WRITING TESTBGENCHES –Functional Verification of HDL Models, England:KLUWER ACADEMIC PUBLISHERS [2] 陈曦 设计重用 中国:清华大学 [3] 陈曦的设计文件 basic.v
本文来自:我爱研发网(52RD.com) - R&D大本营 详细出处:http://www.52rd.com/Blog/Archive_Thread.asp?SID=417
基于Verilog语言的可维护性设计技术的更多相关文章
- 基于Verilog HDL整数乘法器设计与仿真验证
基于Verilog HDL整数乘法器设计与仿真验证 1.预备知识 整数分为短整数,中整数,长整数,本文只涉及到短整数.短整数:占用一个字节空间,8位,其中最高位为符号位(最高位为1表示为负数,最高位为 ...
- 【iCore、iCore2 双核心板】EPCS 实验(SPI Flash)(基于Verilog语言)
_____________________________________ 深入交流QQ群: A: 204255896(1000人超级群,可加入) B: 165201798(500人超级群,满员) C ...
- 基于Verilog HDL 的数字电压表设计
本次实验是在“基于Verilog HDL的ADC0809CCN数据采样”实验上进一步改进,利用ADC0809采集到的8位数据,进行BCD编码,以供查表方式相加进行显示,本次实验用三位数码管. ADC0 ...
- 基于Verilog语言的FIR滤波【程序和理解】
一直想找一个简单.清晰.明了的fir滤波器的设计,终于找到了一个可以应用的,和大家分享一下,有助于FPGA新手入门. 1.说道fir滤波器,滤波系数肯定是最重要的,因为后面程序中涉及到滤波系数问题,所 ...
- 【iCore2双核心板】SRAM 读写实验(基于Verilog语言)
_____________________________________ 深入交流QQ群: A: 204255896(1000人超级群,可加入) B: 165201798(500人超级群,满员) C ...
- 基于Verilog的奇数偶数小数分频器设计
今天呢,由泡泡鱼工作室发布的微信公共号“硬件为王”(微信号:king_hardware)正式上线啦,关注有惊喜哦.在这个普天同庆的美好日子里,小编脑洞大开,决定写一首诗赞美一下我们背后伟大的团队,虽然 ...
- 基于Java语言开发jt808、jt809技术文章精华索引
很多技术开发人员喜欢追逐最新的技术,如Node.js, go等语言,这些语言只是解决了某一个方面,如只是擅长异步高并发等等,却在企业管理后台开发方面提供的支持非常不够,造成项目团队技术选项失败,开发后 ...
- 基于Verilog HDL 各种实验
菜鸟做的的小实验链接汇总: 1.基于Verilog HDL 的数字时钟设计 2.乘法器 3.触发器(基本的SR触发器.同步触发器.D触发器) 4.基于Verilog HDL的ADC ...
- 基于MATLAB2016b图形化设计自动生成Verilog语言的积分模块及其应用
在电力电子变流器设备中,常常需要计算发电量,由于电力电子变流器设备一般是高频变流设备,所以发电量的计算几乎时实时功率的积分,此时就会用到一个积分模块.发电量计算的公式如下:Q=∫P. FPGA由于其并 ...
随机推荐
- Linux学习之计算机基础理论
一.描述计算机的组成及其功能. 计算机系统是由硬件系统(hardware)和软件系统(software system)两部分组成. 硬件系统: 从硬件基本结构上来讲,计算机是由运算器.控制器.存储器. ...
- mariadb数据库的链接查询和表格设计
链接查询 练习准备: --创建学生表 create table students ( id int unsigned not null auto_increment primary key, name ...
- Python 语言中经常有疑惑的地方
*)关于for循环中range(2),i到底是从0还是1开始.特别是在用数组的长度作为range的参数的时候经常会犯糊涂 #首先 >>> for i in range(5): ... ...
- 紫书 习题 11-16 UVa 1669(树形dp)
想了很久, 以为是网络流最大流, 后来建模建不出来, 无奈. 后来看了 https://blog.csdn.net/hao_zong_yin/article/details/79441180 感觉思路 ...
- 你必须了解的RecyclerView的五大开源项目-解决上拉加载、下拉刷新和添加Header、Footer等问题
前段时间做项目由于采用的MD设计,所以必须要使用RecyclerView全面代替ListView.但是开发中遇到了需要实现RecyclerView上拉加载.下拉刷新和添加Header以及Footer等 ...
- 编写SDR SDRAM页突发模式控制器的注意点-下篇
本来是没打算写这些的,但是后面逐渐发现点问题,所以决定再写一个下篇来补充说明一下. 图一 细心的网友会发现上篇末尾的打印是有点问题的,因为我的数据产生器产生的是1-200,1-200,1-200,1- ...
- php中file_get_contents如何读取大容量文件
php中file_get_contents如何读取大容量文件 一.总结 一句话总结:使用file_get_contents()进行分段读取,file_get_contents()函数可以分段读取 1. ...
- lightoj--1214--Large Division(大数取余)
Large Division Time Limit: 1000MS Memory Limit: 32768KB 64bit IO Format: %lld & %llu Submit ...
- 在Ubuntu下使用命令删除目录
在Ubuntu命令行中用命令删除目录,现在在Linux系统中删除目录大致会用两个,rm和rmdir,rm命令删除目录很简单,不过很多人还是比较习惯用rmdir命令,如果操作的目录非空时就有点麻烦.这时 ...
- AppManager类,管理Activity和App
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); / ...