Verilog的常用testbench模板分享
楼主在初学verilog的时候就一直对testbench该怎么写感到困惑,之后的学习过程中也陆陆续续地看过一些testbench文件,其中有一些其实相当于就在testbench里重写了一下要验证地模块,个人感觉这有点”鸡生蛋“和”蛋生鸡“那味儿了。虽说在testbench里写不用考虑能否综合的问题,可以用一些更为方便的写法,但是终归还是用的Verilog体系内的语法,描述待测试模块预期的功能时很有可能会犯类似的错误,因此楼主觉得这种testbench的写法应付比较简单的模块可能还行,比较简单直接,但是应对复杂点的模块则出错的可能性偏大,不太可取。
除了前述testbench的写法外,遍历穷举应该是常见的testbench写法之一,即借由高级语言穷举生成所有的输入和对应的输出,来比对模块的输出以达到验证的目的,因此本文就分享一个适用于这种穷举遍历思路的testbench模板,下面就借由一个原码输入原码输出的带饱和处理的减法器模块进行这一模板的写法说明。
原码减法器
选取被减数AAA和减数BBB的数据位宽为444,输出结果resresres的数据位宽也为4,设计框图如下
设计说明如下:
- 输入为原码(Sign-magnitude,SM)表示,而加减法一般习惯用二的补码(2’s compliment,2’s),因此先将AAA和BBB转换为补码形式,其中BBB为被减数,可以考虑直接对(-B)进行补码转换,即上图中的红色减号,此时后续”减法“则转为加法进行;
- 二的补码加法比较简单,直接相加即可,本设计并不需要用到进位,因此可以只在模块内声明一下作为连线即可;
- 最后再进行溢出时的饱和处理,即溢出时符号位不变,幅值取最大值。具体到溢出的判断,本设计就不考虑最高位进位和次高位进位的溢出判断了,而是直接用输入和输出的符号位作为判断依据,比较直观。
Verilog模块
代码如下所示
/*** @function:* compute res = A - B and get sign-magnitude representation result* @input:* A, B -> sign-magnitude representation* @output:* res -> sign-magnitude representation
**/
module SmSub #(parameter WIDTH = 4
) (input [(WIDTH - 1) : 0] A,input [(WIDTH - 1) : 0] B,output [(WIDTH - 1) : 0] res
);wire [(WIDTH - 1) : 0] tmpA; // magnitude part of Awire [(WIDTH - 1) : 0] complA; // 2's complement representation of Awire [(WIDTH - 1) : 0] complB; // 2's complement representation of Bwire [(WIDTH - 1) : 0] complRes; // 2's complement representation of resreg [(WIDTH - 1) : 0] tmpRes; // for negative resultwire carry;reg [(WIDTH - 1) : 0] satcomplRes; // saturated complReswire [(WIDTH - 1) : 0] negMax; // maximum negative value using 2's complement representation// sign-magnitude -> 2's complement for Aassign tmpA = {1'b0, A[(WIDTH - 2):0]};assign complA = (1'b1 == A[(WIDTH - 1)]) ? ((~tmpA[(WIDTH - 1):0]) + 1) : A[(WIDTH - 1):0];// sign-magnitude -> 2's complement for (-B)assign complB = (1'b1 == B[(WIDTH - 1)]) ? {1'b0, B[(WIDTH - 2):0]} : ((~B[(WIDTH - 1):0]) + 1);// get resultassign {carry, complRes} = complA + complB;// overflow processalways @(*) begincase ({complA[(WIDTH - 1)], complB[(WIDTH - 1)], complRes[(WIDTH - 1)]})// postive spillover3'b001: beginsatcomplRes = {1'b0, {(WIDTH - 1){1'b1}}};end// negtive spillover3'b110: beginsatcomplRes = {1'b1, {(WIDTH - 1){1'b1}}};end// no spilloverdefault: begin// 2's complement -> sign-magnitude for complRestmpRes = (~complRes[(WIDTH - 1):0]) + 1;satcomplRes = (1'b1 == complRes[(WIDTH - 1)]) ? {1'b1, tmpRes[(WIDTH - 2):0]} : complRes[(WIDTH - 1):0];endendcaseend// handle special case N'b1000…000assign negMax = {1'b1, {(WIDTH - 1){1'b0}}};assign res = (negMax[(WIDTH - 1):0] == satcomplRes[(WIDTH - 1):0]) ? {1'b1, {(WIDTH - 1){1'b1}}} : satcomplRes[(WIDTH - 1):0];endmodule
matlab生成数据
matlab代码如下所示,即穷举所有输入的情况,一共24×24=2562^4\times 2^4=25624×24=256种,算出对应的输出,借由dec2bin()
函数转为二进制形式写入到result.txt
文件中,以供testbench读入。
注:下列代码整体风格其实与C很像,之所以不用C而是用matlab只是图matlab里有现成的十进制转二进制函数罢了……
dataWidth = 4;
fileName = "../result.txt";
fileID = fopen(fileName, "w");dataNum = power(2, dataWidth);
magnitudeMax = power(2, dataWidth - 1) - 1;
result = zeros(dataNum, dataNum);
for i = 1 : dataNumfor j = 1 : dataNum% convert to sign-magnitude representationif (i <= power(2, dataWidth - 1)) tmpI = i - 1;elsetmpI = power(2, dataWidth - 1) + 1 - i;endif (j <= power(2, dataWidth - 1)) tmpJ = j - 1;elsetmpJ = power(2, dataWidth - 1) + 1 - j;endtmpRes = tmpI - tmpJ;% saturated the results and convert to binary representationif tmpRes < 0if abs(tmpRes) >= magnitudeMax % saturatedfprintf(fileID, "%s\n", dec2bin(magnitudeMax + power(2, dataWidth - 1), dataWidth)); % sign-magnitude representationelsefprintf(fileID, "%s\n", dec2bin(abs(tmpRes) + power(2, dataWidth - 1), dataWidth));endelseif tmpRes >= magnitudeMax % saturatedfprintf(fileID, "%s\n", dec2bin(magnitudeMax, dataWidth));elsefprintf(fileID, "%s\n", dec2bin(tmpRes, dataWidth));endendend
endfclose(fileID);
testbench编写
本文的重头戏来了,先放代码
`timescale 1 ns/ 10 ps
`define period 10module SmSub_tb();
/********** 1. ports and signal declaration **********/reg [3:0] A;reg [3:0] B;wire [3:0] res;reg [3:0] refResMem[0:(16 * 16 - 1)];integer i;integer j;integer tmpIdx;integer errCnt;/********** 2. module instantiation and assignment **********/SmSub #( .WIDTH(4) ) uut0 (.A(A),.B(B),.res(res));/********** 3. Initialization **********/initial beginerrCnt = 0;$readmemb("./sim/result.txt", refResMem);for (i = 0; i < 16; i = i + 1) beginfor (j = 0; j < 16; j = j + 1) beginA = i; B = j;#(`period / 2) checkOutputs;#(`period / 2);endendif (errCnt) begin$display("***************** Total Errors: %d *****************\n", errCnt);endelse begin$display("***************** No Errors! *****************\n");end#(`period*10) $stop;end /********** 4. check part **********/task checkOutputs;begintmpIdx = (i * 16) + j;if (res != refResMem[tmpIdx]) beginerror;endendendtasktask error;beginerrCnt = errCnt + 1;$display("ERROR AT %d: expected -> %h, get -> %h", tmpIdx, refResMem[tmpIdx], res);endendtaskendmodule
说明如下:
- 第一部分就是testbench都有的端口和接下来要用的信号声明;
- 第二部分是测试模块的实例化,较为复杂点的模块可能还需要在testbench里额外添加一些连线;
- 第三部分为初始化,先将错误计数变量初始化为0,再用系统函数
$readmemb
/$readmemh
将前一步用matlab生成的参考输出读入,而考虑到本模块中的输入比较有规律,所以输入用整型变量i
和j
产生,若数据量较大或不规律,可以参照前一步,用高级语言生成对应的输入文件读入。再之后,每产生一个输入,就调用对应的checkOutputs
任务进行输出对比,如果存在错误则调用error
任务进行错误处理即打印出错信息到终端,并对错误进行计数。最后则是检查错误个数,打印对应的输出信息,并调用$stop
中止仿真。 - 第四部分即输出对比函数和错误处理函数,本设计并不复杂,所以只是简单的比对仅有的一个输出,错误处理也只是单纯地打印,并不算复杂;
总结
这一模板是楼主学Verilog以来感觉比较好用的一个,当然了,这种遍历穷举的验证方式在实际工程中还是有不小局限性的,输入比特数较大时往往就只能随机生成众多输入情况的一部分进行验证了,最终还是要借助于系统的验证方法学。尽管如此,个人感觉这一模板还是对初学Verilog的人来说还是比较友好的!
Verilog的常用testbench模板分享相关推荐
- 米联客资料笔记FPGA篇EDA先锋工作室官方DOC常用TestBench模板Vivado基本使用
文章目录 背景 一.米联客verilog篇笔记 1.为什么要推出vivado 2.状态机,软核的理解 3.always @的含义与 @() 4.条件运算符 5.阻塞逻辑和非阻塞逻辑混用 二.xilin ...
- Vivado中Testbench模板(自用)
1.背景 从ISE切换到Vivado后,一直不习惯该软件的仿真方式.因为有个大麻烦,软件无法自动生成testbench用例.即使网上有VScode搭配插件进行自动生成Testbench ...
- 关于使用ModelSim中编写testbench模板问题
对于初学者来说写Testbench测试文件还是比较困难的,但Modelsim和quartus ii都提供了模板,下面就如何使用Modelsim提供的模板进行操作. Modelsim提供了很多Testb ...
- Jetbrains系列软件常用快捷键/模板/调试
Jetbrains开发软件常用快捷键/模板/调试 一.Jetbrains常用快捷键 Ctrl+Alt+L:代码的格式 Ctrl+F:打开的文件中查找 两次Shift:调出快速搜索框 Ctrl+Shif ...
- 08常用的模板标签和过滤器
技术交流QQ群:1027579432,欢迎你的加入! 本教程来源于B站杨仕航Django2.0开发视频教程,如需转载,必须注明来源! 1.继续搭建blog [外链图片转存失败,源站可能有防盗链机制,建 ...
- Makefile常用万能模板(包括静态链接库、动态链接库、可执行文件)
from: Makefile常用万能模板(包括静态链接库.动态链接库.可执行文件) 本文把makefile 分成了三份:生成可执行文件的makefile,生成静态链接库的makefile,生成动态链接 ...
- 常用jQuery代码分享
为什么80%的码农都做不了架构师?>>> 常用jQuery代码分享 1.查看浏览器信息 navigator.userAgent 检索浏览器信息包括哪些字符 例如: if (na ...
- Django 3.2.5博客开发教程:一些常用的模板使用方法
一.django static文件的引入方式 1.在django project中创建 static文件夹 2.settings.py中配置要在 STATIC_URL = '/static/' 下边 ...
- 简单可行性报告模板_项目可行性报告模板分享!第三章主要内容
项目可行性报告模板分享!第三章主要内容如下: 第三章 市场分析与建设规模 市场分析在可行性研究中的重要地位在于,任何一个项目,其生产规模的确定.技术的选择.投资估算甚至厂址的选择,都必须在市场需求情况 ...
最新文章
- 在批处理脚本所在目录下打开cmd
- python的read_csv_python,pd.read_csv成熟导入
- 是否应该频繁升级小米的系统?
- javascript中alert函数的替代方案,一个自定义的对话框的方法
- Node.js ES6 模块化的基本语法-默认导出 与 默认导入
- sql查询初学者指南_面向初学者SQL Server查询执行计划–类型和选项
- 《Windows 8 权威指南》——1.3 引入全新内核休眠模式,实现“瞬间开机”
- Flutter Provider框架实现简单的购物车
- android scrollview 动态添加,使用Scrollview和LinearLayout动态添加布局
- CAUSALITY FOR MACHINE LEARNING
- Joey Logano 嘉年华正式开始,和 Joey Logano 一起竞赛吧
- 今天开机发现,在光标左边多了一个长方形的带有箭头的,怎么去掉?
- 阿里云上海云栖大会上宣布多款核心云计算产品降价 最高降幅达50%
- 手机网站点击手机号码直接拨号
- ODOO开发教程之图表
- VUE3 reactive与toRefs函数
- xadmin报cannot import name ‘DEFAULT_FORMATS‘ from ‘import_export.admin‘/cannot import name ‘SKIP_ADMI
- linux 终端分屏命令vsp(转)
- window10登录界面进不了,怎么办
- android+sony+动态背景音乐,音乐流媒体时代,索尼ZX505给你不一样的感动