docs: update docs at 03_add_test/02_build_env; code style: remove some useless commented out code at rvc_expande

This commit is contained in:
cjh 2024-12-13 10:51:32 +08:00
parent c54c0c2e59
commit d09f78a58c
7 changed files with 165 additions and 17 deletions

View File

@ -29,7 +29,7 @@ ut_backend/ctrl_block/decode
模块(例如`decode`)中的代码目录结构由贡献者自行决定,但需要满足 python 规范,且逻辑和命名合理。
## 编写测试环境
## 编写测试环境:一般思路
在 UT 验证模块的测试环境中,目标是完成以下工作:
@ -38,7 +38,7 @@ ut_backend/ctrl_block/decode
3. 定义必要 fixture 提供给测试用例
4. 在合理时刻统计覆盖率
decode 环境中的 RVCExpander 为例(`ut_backend/ctrl_block/decode/env/decode_wrapper.py`
IFU 环境中的 RVCExpander 为例(`ut_frontend/ifu/rvc_expander/classical_version/env/rvc_expander_wrapper.py`
### 1. DUT 封装
@ -52,8 +52,9 @@ class RVCExpander(toffee.Bundle):
self.io = toffee.Bundle.from_prefix("io_", self.dut) # 通过 Bundle 使用前缀关联引脚
self.bind(self.dut) # 把 Bundle 与 DUT 进行绑定
def expand(self, instr):
def expand(self, instr, fsIsOff):
self.io["in"].value = instr # 给DUT引脚赋值
self.io["fsIsOff"].value = fsIsOff # 给DUT引脚赋值
self.dut.RefreshComb() # 推动组合电路
self.cover_group.sample() # 调用sample对功能覆盖率进行统计
return self.io["out_bits"].value, self.io["ill"].value # 返回结果 和 是否是非法指令
@ -122,6 +123,8 @@ def rvc_expander(request):
os.makedirs(coverage_dir, exist_ok=True) # 目标目录不存在则创建目录
expander = RVCExpander(g, coverage_filename=coverage_file, waveform_filename=wave_file)
# 创建RVCExpander
expander.dut.io_in.AsImmWrite() # 设置io_in引脚的写入时机为立即写入
expander.dut.io_fsIsOff.AsImmWrite() # 设置io_fsIsOff引脚的写入时机为立即写入
init_rvc_expander_funcov(expander, g) # 初始化功能检查点
yield expander # 返回创建好的 RVCExpander 给 Test Case
expander.dut.Finish() # Tests Case运行完成后结束DUT
@ -183,3 +186,153 @@ def test_rvc_expand_16bit_full(rvc_expander, start, end):
- 需要初始化功能检查点(功能检查点可以独立成一个模块)
- 需要进行覆盖率统计
- 需要有说明文档
## toffee框架支持的测试环境
使用python语言进行的测试可以通过引入我们的开源测试框架[toffee](https://github.com/XS-MLVP/toffee)来得到更好的支持。
toffee的官方教程可以参考[这里](https://open-verify.cc/mlvp/docs/mlvp/)。
### bundle快捷DUT封装
toffee通过Bundle实现了对DUT的绑定。toffee提供了多种建立Bundle与DUT绑定的方法。相关代码
#### 手动绑定
toffee框架下用于支持绑定引脚的最底层类是Signal其通过命名匹配的方式和DUT中的各个引脚进行绑定。相关代码参照`ut_frontend/ifu/rvc_expander/toffee_version`。
以最简单的RVCExpander为例其io引脚形如
``` verilog
module RVCExpander(
input [31:0] io_in,
input io_fsIsOff,
output [31:0] io_out_bits,
output io_ill
);
```
一共四个信号io\_in, io\_fsIsOff, io\_out\_bits, io\_ill。我们可以抽取共同的前缀比如\"io_\"不过由于in在python中有其他含义其不能直接作为变量名虽然可以使用setattr 和getattr方法来规避这个问题但是出于代码简洁的考虑我们只选取\"io\"作为前缀将后续部分作为引脚名定义在对应的Bundle类中
```python
class RVCExpanderIOBundle(Bundle):
_in, _fsIsOff ,_out_bits,_ill = Signals(4)
```
然后在更高一级的Env或者Bundle中采取from\_prefix的方式完成前缀的绑定
```python
self.agent = RVCExpanderAgent(RVCExpanderIOBundle.from_prefix("io").bind(dut))
```
#### 自动定义Bundle
实际上Bundle类的定义也不一定需要写明可以仅仅通过前缀绑定
```python
self.io = toffee.Bundle.from_prefix("io_", self.dut) # 通过 Bundle 使用前缀关联引脚
self.bind(self.dut)
```
如果Bundle的from_prefix方法传入dut其将根据前缀和DUT的引脚名自动生成引脚的定义而在访问的时候使用dict访问的思路即可
```python
self.io["in"].value = instr
self.io["fsIsOff"].value = False
```
#### Bundle代码生成
toffee框架的[scripts](https://github.com/XS-MLVP/toffee/tree/master/scripts)提供了两个脚本。
bundle\_code\_gen\.py脚本主要提供了三个方法
```python
def gen_bundle_code_from_dict(bundle_name: str, dut, dict: dict, max_width: int = 120)
def gen_bundle_code_from_prefix(bundle_name: str, dut, prefix: str = "", max_width: int = 120):
def gen_bundle_code_from_regex(bundle_name: str, dut, regex: str, max_width: int = 120):
```
通过传入dut和生成规则包括dict、prefix、regex三种自动生成对应的bundle代码。
而bundle\_code\_intel\_gen\.py则解析picker生成的signals\.json文件自动生成层次化的bundle代码。可以直接在命令行调用
```bash
python bundle_code_intel_gen.py [signal] [target]
```
如发现自动生成脚本存在bug欢迎提issue以便我们修正。
### Agent驱动方法
如果说Bundle是将DUT的数据职责进行抽象的话那么Agent则是将DUT的行为职责封装为一个个接口。简单地说Agent通过封装多个对外开放的方法将多组IO操作抽象为一个具体的行为
```python
class RVCExpanderAgent(Agent):
def __init__(self, bundle:RVCExpanderIOBundle):
super().__init__(bundle)
self.bundle = bundle
@driver_method()
async def expand(self, instr, fsIsOff): # 传入参数RVC指令和fs.status使能情况
self.bundle._in.value = instr # 引脚赋值
self.bundle._fsIsOff.value = fsIsOff # 引脚赋值
await self.bundle.step() # 推动时钟
return self.bundle._out_bits.value, # 返回值:扩展后指令
self.bundle._ill.value # 返回值:指令合法校验
```
譬如RVCExpander的指令扩展功能接收输入的指令可能为RVI指令也可能为RVC指令和CSR对fs\.status的使能情况。我们将这个功能抽象为expand方法提供除self以外的两个参数。同时指令扩展最终将会返回传入指令对应的RVI指令和该指令是否合法的判断对应地该方法也返回这两个值。
### Env测试环境
```python
class RVCExpanderEnv(Env):
def __init__(self, dut:DUTRVCExpander):
super().__init__()
dut.io_in.xdata.AsImmWrite()
dut.io_fsIsOff.xdata.AsImmWrite() # 设置引脚写入时机
self.agent = RVCExpanderAgent(RVCExpanderIOBundle.from_prefix("io").bind(dut)) # 补全前缀绑定DUT
```
### 覆盖率定义
定义覆盖率组的方式和前述方式类似,这里就不再赘述了。
### 测试套件定义
测试套件的定义略有不同:
```python
@toffee_test.fixture
async def rvc_expander(toffee_request: toffee_test.ToffeeRequest):
import asyncio
version_check()
dut = toffee_request.create_dut(DUTRVCExpander)
start_clock(dut)
init_rvc_expander_funcov(dut, gr)
toffee_request.add_cov_groups([gr])
expander = RVCExpanderEnv(dut)
yield expander
cur_loop = asyncio.get_event_loop()
for task in asyncio.all_tasks(cur_loop):
if task.get_name() == "__clock_loop":
task.cancel()
try:
await task
except asyncio.CancelledError:
break
```
由于toffee提供了更强大的测试覆盖率管理功能因此不需要手动设置行覆盖率。同时由于toffee的时钟机制建议在套件代码最后额外检查任务是否全部结束。

View File

@ -93,9 +93,9 @@ class RVCExpander(toffee.Bundle):
self.io = toffee.Bundle.from_prefix("io_", self.dut)
self.bind(self.dut)
def expand(self, instr):
def expand(self, instr, fsIsOff):
self.io["in"].value = instr
self.io["fsIsOff"].value = False
self.io["fsIsOff"].value = fsIsOff
self.dut.RefreshComb()
self.cover_group.sample()
return self.io["out_bits"].value, self.io["ill"].value
@ -118,6 +118,7 @@ def rvc_expander(request):
os.makedirs(coverage_dir, exist_ok=True)
expander = RVCExpander(g, coverage_filename=coverage_file, waveform_filename=wave_file)
expander.dut.io_in.AsImmWrite()
expander.dut.io_fsIsOff.AsImmWrite()
init_rvc_expander_funcov(expander, g)
yield expander
expander.dut.Finish()

View File

@ -19,7 +19,7 @@ from tools.disasm import disasmbly, libdisasm
from comm import TAG_LONG_TIME_RUN, TAG_SMOKE, TAG_RARELY_USED, debug
def rvc_expand(rvc_expander, ref_insts, is_32bit=False):
def rvc_expand(rvc_expander, ref_insts, is_32bit=False, fsIsOff=False):
"""compare the RVC expand result with the reference
Args:
@ -29,7 +29,7 @@ def rvc_expand(rvc_expander, ref_insts, is_32bit=False):
find_error = 0
for insn in ref_insts:
insn_disasm = disasmbly(insn)
value, instr_ex = rvc_expander.expand(insn)
value, instr_ex = rvc_expander.expand(insn, fsIsOff)
if is_32bit:
assert value == insn, "RVC expand error, 32bit instruction need to be the same"
if (insn_disasm == "unknown") and (instr_ex == 0):

View File

@ -5,8 +5,8 @@ class RVCExpanderAgent(Agent):
def __init__(self, bundle:RVCExpanderIOBundle):
super().__init__(bundle)
self.bundle = bundle
# @driver_method()
@driver_method()
async def expand(self, instr, fsIsOff):
self.bundle._in.value = instr
self.bundle._fsIsOff.value = fsIsOff

View File

@ -9,6 +9,4 @@ class RVCExpanderEnv(Env):
super().__init__()
dut.io_in.xdata.AsImmWrite()
dut.io_fsIsOff.xdata.AsImmWrite()
# start_clock(dut)
self.agent = RVCExpanderAgent(RVCExpanderIOBundle.from_prefix("io").bind(dut))
self.agent = RVCExpanderAgent(RVCExpanderIOBundle.from_prefix("io").bind(dut))

View File

@ -1,3 +1,4 @@
# currently no use
def ref_rvc_expander(opcode, fsIsOff):
op_str = bin(opcode)[2:].zfill(32)
lowest2 = opcode[-2:]

View File

@ -68,11 +68,6 @@ def init_rvc_expander_funcov(dut:DUTRVCExpander, g: fc.CovGroup):
async def rvc_expander(toffee_request: toffee_test.ToffeeRequest):
import asyncio
version_check()
# fname = request.node.name
# wave_file = get_out_dir("decoder/rvc_expander_%s.fst" % fname)
# coverage_file = get_out_dir("decoder/rvc_expander_%s.dat" % fname)
# coverage_dir = os.path.dirname(coverage_file)
# os.makedirs(coverage_dir, exist_ok=True)
dut = toffee_request.create_dut(DUTRVCExpander)
start_clock(dut)
init_rvc_expander_funcov(dut, gr)