《VLSI仿真与验证》课程实验:TinyCore交叉编译验证思路

一、前言:

        本实验是对一个基于RISC-V指令集的CPU核fwrisc进行功能验证。在宋宇鲲老师的《Verilog语言与FPGA实现》的课程实验中我们已经写过一个只有5条指令的8位CPU,当时在编写测试平台的时候花时间思考编写存储在ROM中程序对应的机器码耗费了我大量的时间,我当时是采用人脑编译器的方式来直接编写机器码,这种方式极其容易出错导致程序崩溃且低效,本次实验通过汇编程序交叉编译的方式解决了当时困扰我的问题。

        首先编写基于RISC-V RV32I的.S汇编程序,然后使用交叉编译器将其转换为.obj目标文件,通过link连接dll动态链接库生成.elf可执行文件,再反汇编生成.dump反汇编文件,然后从反汇编文件中提取出机器码存储在对应文件夹下的ram.hex文件中,并且将生成的ram.hex文件添加到program_list.dat文件中进行用来仿真。最后运行仿真,观察构造的test case的覆盖率情况,并根据反馈调整test case以尽可能的达到最大覆盖。另外还可以使用socre.sh脚本观察自己编写测试用例的综合得分情况。

二、实验材料的学习与准备

TinyCore概述

        TinyCore由多周期RISC-V处理器fwrisc和一个UART模块ti_uart组成。在验证的过程中可以使用UART总线功能模型与fwrisc通讯来进行交叉验证,从而避免重复查看波形,因此效率更高。

RV32I指令集

        RV32I是RISC-V基础整数指令集,一共包含47条指令,本实验需要验证除了fence、fence.i指令外的其他45条指令,其类型与格式如下表所示:

类型

名称

格式

类型

名称

格式

类型

名称

格式

Shifts

sll

R

Logical

and

R

Environment

ebreak

I

slli

I

andi

I

CSR

csrrw

I

srl

R

Compare

slt

R

csrrs

I

srli

I

slti

I

csrrc

I

sra

R

sltu

R

csrrwi

I

srai

I

sltiu

I

csrrsi

I

Arithmetic

add

R

Branches

beq

B

csrrci

I

addi

I

bne

B

Loads

lb

I

sub

R

blt

B

lh

I

lui

U

bge

B

lbu

I

auipc

U

bltu

B

lhu

I

Logical

xor

R

bgeu

B

lw

I

xori

I

Jump&Link

jal

J

Stores

sb

S

or

R

jalr

I

sh

S

ori

I

Environment

ecall

I

sw

S

         其中六种指令格式如下图所示:

         所有指令长度都为32位,支持32位寻址空间,在使用的过程中需要注意指令格式与操作数的位数以防止出错。

fwrisc寄存器

fwrisc一共使用了32个32位整数寄存器,在实验中也需要使用到32个不同的寄存器来验证各个寄存器功能的正确性,其常用功能及对应编号如下所示:

         特殊的是x0寄存器的数值恒为0,因此对x0寄存器赋值是无效的,也可以直接使用x0寄存器中的零来实现一些特殊的功能。例如RV32I中为了尽量简化硬件设计而没有常见的nop指令,着可以通过addi x0,x0,0指令来替代。这体现了精简指令集为降低硬件成本可以通过其他方式来实现与复杂指令相同功能的巧妙设计。

UART寄存器

        UART一共有5个寄存器,其名称与对应的功能如下所示:

        在本次实验中,可以对相应的寄存器进行配置来实现UART与fwrisc的通讯,将调试信息输出到终端上显示,避免重复查看波形,从而提高了仿真验证的效率。

三、编写汇编程序

        通过针对每一条需要验证的指令以及每一种可能的指令执行结果编写相应的汇编程序,来达到尽可能高的覆盖率。代码覆盖率有四个评价指标,分别是:语句覆盖率(line)、条件覆盖率(cond,即condition)、翻转覆盖率(tgl,即toggle)和状态机覆盖率(fsm,即finite state machine);验证计划中的检查点有六个方面,分别是:验证的指令数(Instruction)、验证的fwrisc寄存器数(Register)、验证的UART寄存器数(UART_reg)、验证的异常数(Exception)、验证的跳转与分支数(j&b,即jump and branch)、验证的加载与存储数(ldst,即load and store)。

语句覆盖率

        语句覆盖率是指在仿真的过程中执行的代码的行数与测试平台总的代码行数的比值。语句覆盖率越高,只能说明仿真执行的越完备,并不意味着所验证的功能正确,因此语句覆盖是最弱的逻辑覆盖。实际上有些语句没有执行是正常现象。比如在跳转类型指令的验证验证中,不满足条件的语句将不会被执行,因此不能仅凭语句覆盖率来判断验证的进度。

        本实验尽量保证代码覆盖率高,但不追求100%。一般来说不需要过多的关注line指标,其覆盖率都在90%以上。

条件覆盖率

        条件覆盖率是指在仿真过程中条件判断语句被触发的条件数目与所有可能数的比值。导致条件判断语句被触发的条件可能不知一个,然而仅从外部只能看到条件被触发而不清楚具体是满足哪一种条件而被触发,因此条件覆盖率对于验证的完备性具有一定的指导意义,在验证过程中尽可能的保证所有可能的触发条件语句的情况被验证。

        本实验需要保证条件覆盖率尽可能高,当然只要指令覆盖的足够多,cond指标自然而然地就比较客观。

翻转覆盖率

        翻转覆盖率是指DUT每个模块的信号在仿真过程中被翻转的次数与所有可能翻转的总数的比值。1位信号即可能从0到1被翻转,也有可能从1到0被翻转,n位信号的组合则有多达2^n种翻转情况,因此翻转覆盖率也是评价验证完备性的一个重要指标。

        本实验中翻转覆盖率应当尽可能高,测试用例越多,翻转的种类越多,因此要保证tgl指标高,则需要编写尽可能多的有效测试用例。

状态机覆盖率

        状态机覆盖率是指DUT状态机在仿真过程中被验证的状态跳转的数目与所有可能的状态跳转总数的比值。虽然有限状态机的每一种状态都可能被测试到,但是不能确定这个状态是从哪个其他状态跳转过来的,因此状态机覆盖率对于验证的完备性有很大的作用,应当尽可能的完全覆盖。

本实验中的状态机覆盖率指的是fwrisc状态机在仿真中被覆盖到的状态转移数占总转移可能数的比值。与上面几种指标类似,指令覆盖率越大、有效的测试用例的数量越多,fsm自然就越高。

验证的指令数

        在第二章中已经梳理了需要验证的45条指令,这里不再赘述。

        在本实验中针对需要验证的指令编写汇编程序,由于一条指令可能有多种操作数的可能,因此为了保证更高的覆盖率,就需要针对不同的输入操作数的组合对同一条指令编写.S汇编程序。例如,加法指令add的操作数不仅可以是正整数,也可是是负整数,而要验证add指令的完备性和正确性,需要针对两种输入的操作数的可能编写不同的汇编程序。在这里可以使用UART总线功能模型来辅助我们进行正确性的检查,为了避免重复观察波形来检查功能的正确性,可以使用UART与fwrisc进行通讯,将仿真信息以文本的形式显示在终端,这大大提高了验证效率,同时使用UART来进行文本的显示使用到的UART寄存器也能提高UART_reg指标。

验证的fwrisc寄存器数

        在第二章中已经梳理了需要验证的32个fwrisc寄存器,这里不再赘述。

        在编写汇编程序的过程中,应尽量使用所有的fwrisc寄存器,已验证寄存器是否功能正常。需要注意的是x0寄存器恒为0,对其赋值将被舍弃,因此可以用来实现一些特定的功能,起到替代复杂指令集中某些指令的效果。其他x1到x31寄存器虽然有约定俗成的用法,比如x2寄存器为sp,即stack pointer,但是本质上都是通用寄存器,可以在汇编程序中通用。

验证的UART寄存器数

在第二章中已经梳理了需要验证的5个UART寄存器,这里不再赘述。

TinyCore中UART与fwrisc的通讯是通过使用li、sw等指令配置相应的数据、控制来发送和接收的。一般来说在一个汇编程序中配置了五个寄存器实现了打印信息,UART_reg就达到了100%。

验证的异常数

        在ecall和ebreak指令执行的时候会触发异常,ecall是引发环境调用异常来请求执行环境,ebreak则是抛出断点异常请求调试器。然而通过调用这两条指令仅实现了Exception的50%覆盖。另外两种异常情况是寻址时地址边界未对齐而触发的。在对内存进行读写的时候目标地址不是需要访问的数据单元字节数的整数倍的时候就会产生地址不对齐。因此可以使la地址加载指令加载的地址与lh或lw内存读取指令地址不对齐来产生这两种类型的Exception,这样就能够实现100%覆盖率。

验证的跳转与分支数

        j&b表征了验证的跳转和分支数占所有可能的跳转分支的比值。一般而言跳转的验证在Instruction达到100%的时候就已经将所有的跳转可能验证完毕了。然而对于分支的验证则是通过覆盖所有条件跳转分支指令所有可能的情况来实现的,例如beq指令在相等的时候跳转到相应的代码段,在不相等的时候继续当前的代码段,此外跳转情况还可以分为向前跳转和向后跳转,所有情况都需要进行验证才能达到更到的j&b,因此在验证分支跳转指令的时候既需要验证条件满足的情况(文件后缀命名为_t)和条件不满足的情况(文件后缀命名为_f),也需要验证向前跳转(文件后缀命名为_fwd)和向后跳转(文件后缀命名为_back),这样就能得到较高的j&b。

验证的加载与存储数

        ldst表征了仿真验证的加载和存储指令与所有可能的指令的比值。对于RV32I而言一共有8条指令。只要在地址对齐的情况下对这8条指令进行了验证,就可以达到ldst指标100%。

四、最终结果

1、运行仿真,观察UART打印在终端的信息已验证功能的正确性:

以last_lb.S为例,在汇编程序中数据段为:,加载指令为:,通过UART依次发送代码段从低到高最低字节和次低字节,其仿真结果如下所示:

 

        可以看到仿真结果正确。同理,观察其他所有终端显示是否与预期相同,就可以用来验证指令功能的正确性,而不用反复查看波形。

2、观察6个检查点和4个代码覆盖率情况:

 

         6个检查点除了j&b之外都达到了100%,而j&b没有达到100%原因可能是条件满足的跳转、条件不满足的跳转、向前跳转、向后跳转所有可能的情况没有完全覆盖到。

 

         4个覆盖率中语句覆盖、条件覆盖都在指令覆盖达到100%后顺其自然的达到了90%以上,翻转覆盖率在编写了大量测试用例之后有了进一步的提高,状态机覆盖率只有70.97%,有待进一步提高。

3、最终得分:

修改权重之前:

修改权重之后:

         多利要求增加覆盖率权重来评判,因此修改后得分有所下降。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值