首页 > 其他分享 >RTL difftest搭建

RTL difftest搭建

时间:2024-12-05 18:43:35浏览次数:7  
标签:__ RTL nemu dut 寄存器 difftest REF 搭建

  difftest在测试集中可以起到十分重要的作用,可以快速找到发生问题的指令和pc寄存器地址。在nemu作为dut,参考其他模拟器(比如spike)的功能中,大部分代码已经完成,我们只需要完成寄存器的比对即可。但在RTL中重新实现这一功能或者类似功能时,我们需要完成更多函数,但大体的框架已经完成,我们可以参考nemu中已有的difftest功能去补全函数。

  讲义中已经提示我们需要完成哪些函数:

// 在DUT host memory的`buf`和REF guest memory的`addr`之间拷贝`n`字节,
// `direction`指定拷贝的方向, `DIFFTEST_TO_DUT`表示往DUT拷贝, `DIFFTEST_TO_REF`表示往REF拷贝
void difftest_memcpy(paddr_t addr, void *buf, size_t n, bool direction);
// `direction`为`DIFFTEST_TO_DUT`时, 获取REF的寄存器状态到`dut`;
// `direction`为`DIFFTEST_TO_REF`时, 设置REF的寄存器状态为`dut`;
void difftest_regcpy(void *dut, bool direction);
// 让REF执行`n`条指令
void difftest_exec(uint64_t n);
// 初始化REF的DiffTest功能
void difftest_init();

  我们要完成的ref.c的函数。我们可以以spike作为参考,spike的代码位于/nemu/tools/spike-diff/difftest.cc 。但是这只是一部分,difftest.cc只是提供了工具函数,主要的逻辑则位于dut.c中。除过ref.c,我们还需要修改npc的函数。其实这二者是对应的:

difftest.cc

ref.c
dut.c main.cpp
  仔细阅读dut.c的代码,我们可以发现,他的基本思路就是在开始前执行一次memcpy和regcpy,将dut的img和寄存器值复制到ref中:
 ref_difftest_memcpy(RESET_VECTOR, guest_to_host(RESET_VECTOR), img_size, DIFFTEST_TO_REF);
 ref_difftest_regcpy(&cpu, DIFFTEST_TO_REF);

  随后,在正常情况下,dut每执行一条指令,就暂停下来让ref也执行一条指令,然后对比二者的寄存器值是否一一对应。样例dut.c中多了很多逻辑,因为有的指令可能需要跳过,我们的npc与nemu对比时暂无此情况,不需要进行处理。

------------------------------

  在清楚思路以后,我们可以仿照difftest.cc,完成ref.c的三个函数:

  首先需要注意,在nemu为dut时,DIFFTEST_TO_REF是从nemu到spike,我们实现时,DIFFTEST_TO_REF就是从npc到nemu了。反之也是同理。
regcpy的基本功能就是根据方向,决定是把nemu的寄存器值给npc,还是把npc的值给nemu。内部交换值是使用memcpy()还是直接cpu.gpr=xxx均可。我自己是选择的memcpy。

  difftest_memcpy同样。根据方向,使用paddr_write或者paddr_read()即可。注意:传入的dut是void*,我自己是转换word_t *后用下标赋值的,这里隐含了小端序。如果你的传值不一样,最好小心这一点,可以在传值后原地检查一次nemu的内存,一方面可以看赋值是否成功,一方面可以检查值是否正确。比如0x0000 0413这样一条指令,其实0x00位是13,不是00,从左到右内存地址递减。小端序的内存最低字节在最低位。不过c语言内部会处理,我们自己做RTL时选择什么内存模型会有影响。

--------------------------------

  npc的main里面除了调用这几个函数,还需要自己实现两个函数:get_regs,负责获取当前的通用寄存器和pc值。check_regs,负责对比寄存器值。

  关于如何获取npc里通用寄存器的值,有两种主要的方法:一种是在reg元件里利用DPI-C机制声明函数给C++调用,一种是根据编译出的头文件直接查找寄存器的名字调用。声明DPI-C函数的代码如下:

 export "DPI-C" function get_reg_value;  
function int get_reg_value(input int index); 
  begin
    if (index < 2**ADDR_WIDTH) begin
      get_reg_value = rf[index]; 
    end 
    else begin
      $display("wrong reg index, %0d",index);
      get_reg_value = -1; 
    end
  end
  endfunction

同时npc的C++部分也要先声明再使用。此外有很多注意事项:
1. 声明时需要extern "C";

2.verilog的function参数不能出现output,并且用export声明函数时不要写参数。

3.verilog的function和c++侧的参数类型声明要对应:

Verilog byte 对应 C char
Verilog int 对应 C int
Verilog shortint 对应 C short
Verilog longint 对应 C long long
Verilog real 对应 C double
Verilog bit 和 logic 对应 C int(通常用于单个位)

有时候有/无符号和logic/bit等的对应会造成额外麻烦。需要注意的是,Verilog中的byte是一个8位的有符号整数,与C语言中的char在大多数平台上是等价的(也是8位有符号整数)

  我最终没有选择这种方法,因为使用时编译器反复报错,不管是否使用return ,怎么修改函数声明,或者是修改内部函数,都在出问题。最终选择了方法二。

-------------------------------------------
方法二只需要根据编译出的头文件寻找元件名:在我们编译出的obj文件里,会有Vtop.h  Vtop__dpi.h和Vtop___xxxroot.h。第一个头文件表示顶层元件的一些功能性函数接口,比如eval()  以及DPI-C函数。 第二个是dpi-c机制的接口。第三个root.h就是从顶层元件开始所有连接元件内变量的标记,reg型和wire型都有:

 如图,变量名中间的DOT就相当于verilog语言里的"." ,通过这个我们就可以通过顶层元件访问到连接的元件,一层层向内。我的通用寄存器是顶层元件->执行器->寄存器,所以我的寄存器变量名就叫  "top__DOT__exec_unit__DOT__RegFile__DOT__rf',访问方法也很简单,像数组一样直接下标就可以访问。但是,在C++元件里要访问,首先需要引用头文件root.h,其次需要用top访问,讲义里也提到,对于我而言,想要访问这个变量,就需要写成:

top->rootp->top__DOT__exec_unit__DOT__RegFile__DOT__rf

这个寄存器变量不能直接用top->访问,中间还需要rootp。只要查阅了两个头文件就可以看明白。

--------------------

  在搞清楚怎么获取寄存器的值以后,剩下的事就很简单了。在开始前初始化,把npc的代码img和寄存器状态给nemu。在每个时钟周期后:

获取npc寄存器值
获取nemu寄存器值
对比

  不过还有一点需要注意:如果你的寄存器使用了时序逻辑+非阻塞赋值,那么寄存器赋值会晚一个时钟周期,在对比时需要注意。在这里我没有写出让nemu运行一条指令这个函数应该在哪里,因为个人情况可能不同。

标签:__,RTL,nemu,dut,寄存器,difftest,REF,搭建
From: https://www.cnblogs.com/namezhyp/p/18589155

相关文章

  • 上千人挑战,用通义灵码从 0 开始打造一款 App 爆火 | 第二课:搭建本机服务
    通义灵码携手科技博主@玺哥超carry打造全网第一个完整的、面向普通人的自然语言编程教程。完全使用AI,再配合简单易懂的方法,只要你会打字,就能真正做出一个完整的应用。上节课我们完成了从0开始打造一款App的前端代码的生成,超过1000多人参与体验互动,反响非常好!本节课我......
  • 上千人挑战,用通义灵码从 0 开始打造一款 App 爆火 | 第二课:搭建本机服务
    通义灵码携手科技博主@玺哥超carry打造全网第一个完整的、面向普通人的自然语言编程教程。完全使用AI,再配合简单易懂的方法,只要你会打字,就能真正做出一个完整的应用。上节课我们完成了从0开始打造一款App的前端代码的生成,超过1000多人参与体验互动,反响非常好!本节课我......
  • 上千人挑战,用通义灵码从 0 开始打造一款 App 爆火 | 第二课:搭建本机服务
    通义灵码携手科技博主@玺哥超carry打造全网第一个完整的、面向普通人的自然语言编程教程。完全使用AI,再配合简单易懂的方法,只要你会打字,就能真正做出一个完整的应用。上节课我们完成了从0开始打造一款App的前端代码的生成,超过1000多人参与体验互动,反响非常好!本节课我......
  • vsftp搭建虚拟用户模式
    一、CentOS1.安装vsftp以及相关依赖yum-yinstallvsftpd*pam*db4*·vsftpd:ftp软件·pam:认证模块·DB4:支持文件数据库关闭selinuxsetenforce0#暂时关闭sed-ri's#(SELINUX=)enforcing#\1disabled#'/etc/selinux/config#永久关闭grep^SELINUX=/etc/selinu......
  • 超简单!动手搭建‘仲景’中医药大模型,体验线上中医问诊
    得益于大模型技术的发展,中医的传承与发展形式也得到了创新,目前已经有多个中医药大语言模型公开亮相。国内的首个中医药大语言模型是由复旦大学和同济大学联合开发的“仲景”中医药大模型(CMLM-ZhongJing)。仲景中医大语言模型融入了多项创新技术,称得上是一个真正意义上......
  • OSG开发笔记(三十七):OSG基于windows平台msvc2017x64编译器官方稳定版本OSG3.4.1搭建环境
    前言  自行编译的osg版本插件比较多,如果对版本没有特定要求,但是对环境编译器有特定要求,可以反向融合编译器符合要求的osg版本。 OSG下载过程  osg官网:http://www.osgchina.org        由于我们不使用osgQt模块,下载了也无所谓,反正不用,这里是osg3.6.4......
  • hexo+github搭建个人博客
    一、环境准备1.安装Node.js直接到官网上下载安装即可Node.js(Node.js版本需不低于10.13,建议使用Node.js12.0及以上版本)Node自带npmnpm换源(选择一个即可)#淘宝npmconfigsetregistryhttps://registry.npmmirror.com#阿里云npmconfigsetreg......
  • Docker:Docker搭建Jenkins并共用宿主机Docker部署服务(五)跨服务器远程部署后端微服务多
    前言继续完成跨服务器远程部署微服务多模块,Jenkins的搭建与插件安装可以观看上一篇文章:https://www.cnblogs.com/nhdlb/p/18561435配置SSH远程服务器连接这里需要安装SSH连接的插件,可以观看上一篇文章进行安装。开始配置SSH连接保存!!新建视图方便将整个项目的前端和后......
  • Microi吾码|.NET、VUE快速搭建项目,低代码便捷开发教程
    Microi吾码|VUE快速搭建项目,低代码便捷开发教程一、摘要二、Microi吾码介绍2.1功能介绍2.2团队介绍2.3上线项目案例三、VUE中使用Microi吾码3.1前期了解3.2创建第一个低代码应用3.3接口API使用说明3.4引擎界面可视化配置,生成API3.5最后咱们来看看平台效果四、......
  • 如何搭建一个简单的区块链网络
    区块链技术的独特之处在于其去中心化的特性,它依赖多个节点共同维护一个不可篡改的分布式账本。作为程序员,理解区块链的底层原理和搭建过程,对于深入掌握这项技术至关重要。在本文中,我们将从实践角度出发,逐步指导如何搭建一个简单的区块链网络,包括节点配置、数据存储、共识算法......