Skip to content

Commit e3a0397

Browse files
committed
add ftq_top test
1 parent 0c723d8 commit e3a0397

19 files changed

Lines changed: 1655 additions & 1 deletion

scripts/build_ut_frontend_ftq_top.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def build(cfg):
1919
# export ftq.sv
2020
if not os.path.exists(get_root_dir(f"dut/{module_name}")):
2121
info("Exporting Ftq.sv")
22-
s,out,err = exe_cmd(f'picker export --cp_lib false {rtl_files[0]} --tname {module_name}\
22+
s,out,err = exe_cmd(f'picker export --rw 1 --cp_lib false {rtl_files[0]} --tname {module_name}\
2323
--lang python --tdir {get_root_dir("dut")}/ -w {module_name}.fst -c --fs ' + ' '.join(rtl_files))
2424
assert s, f"Failed to export Ftq.sv: %s\n%s" % (out, err)
2525

ut_frontend/ftq/ftq_top/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
这是对香山RISC-V处理器中FtqTop模块的验证代码。FtqTop是指令取指目标队列模块,负责管理处理器前端的指令流。
2+
3+
验证内容
4+
5+
验证覆盖了FtqTop模块的7个主要功能:
6+
向IFU发送取指目标
7+
接收并处理IFU预译码信息
8+
响应后端重定向
9+
响应IFU重定向
10+
向后端发送取指目标
11+
响应重定向并更新内部状态
12+
冲刷指针和状态队列
13+
14+
15+
测试环境
16+
17+
操作系统:Ubuntu 22.04
18+
Python版本:3.10.12
19+
使用工具:Picker 0.9.0, Verilator 5.027, pytest 8.4.0
20+
21+
22+
测试用例
23+
24+
共有7个测试文件,对应不同的功能点:
25+
test_ftq_top3.py:测试取指目标发送功能
26+
test_ftq_top4.py:测试预译码处理功能
27+
test_ftq_top5.py:测试后端重定向响应
28+
test_ftq_top6.py:测试IFU重定向响应
29+
test_ftq_top7.py:测试向后端发送目标
30+
test_ftq_top8.py:测试状态更新
31+
test_ftq_top9.py:测试冲刷逻辑
32+
33+
运行方式:make run CASE=数字(3-9)
34+
35+
36+
所有测试用例均通过,行覆盖率达到76.2%,模块功能符合设计预期。

ut_frontend/ftq/ftq_top/__init__.py

Whitespace-only changes.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from toffee import Env # 导入基类 Env
2+
3+
# 相对导入 FtqBundle 和 FtqAgent(从同级 bundle/ 和 agent/)
4+
from .ftq_bundle import FtqBundle
5+
from .ftq_agent import FtqAgent
6+
7+
8+
9+
class FtqEnv(Env):
10+
def __init__(self, ftq_bundle, dut=None): # 接收 bundle 和 dut
11+
super().__init__()
12+
self.ftq_agent = FtqAgent(ftq_bundle) # 设置 agent
13+
self.dut = dut # 存储 dut 作为实例属性

ut_frontend/ftq/ftq_top/env/ftq_agent.py

Lines changed: 545 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
from toffee import *
2+
3+
class IfuPdSlotBundle(Bundle):
4+
brType = Signal()
5+
isCall = Signal()
6+
isRet = Signal()
7+
valid = Signal()
8+
9+
class RobCommitBundle(Bundle):
10+
valid = Signal()
11+
bits_commitType = Signal()
12+
bits_ftqIdx_flag = Signal()
13+
bits_ftqIdx_value = Signal()
14+
bits_ftqOffset = Signal()
15+
16+
class LastStageFtbEntryBundle(Bundle):
17+
valid = Signal()
18+
isJalr = Signal()
19+
isCall = Signal()
20+
isRet = Signal()
21+
brSlots_0_valid = Signal()
22+
brSlots_0_offset = Signal()
23+
tailSlot_valid = Signal()
24+
tailSlot_offset = Signal()
25+
tailSlot_sharing = Signal()
26+
27+
28+
class ToIfuBundle(Bundle):
29+
req_ready = Signal()
30+
req_valid = Signal()
31+
32+
33+
34+
class ToICacheBundle(Bundle):
35+
req_valid = Signal()
36+
req_bits_readValid_0 = Signal()
37+
req_bits_readValid_1 = Signal()
38+
req_bits_readValid_2 = Signal()
39+
req_bits_readValid_3 = Signal()
40+
req_bits_readValid_4 = Signal()
41+
req_bits_pcMemRead_0_startAddr = Signal()
42+
req_bits_pcMemRead_1_startAddr = Signal()
43+
req_bits_pcMemRead_2_startAddr = Signal()
44+
req_bits_pcMemRead_3_startAddr = Signal()
45+
req_bits_pcMemRead_4_startAddr = Signal()
46+
req_bits_pcMemRead_0_nextlineStart = Signal()
47+
req_bits_pcMemRead_1_nextlineStart = Signal()
48+
req_bits_pcMemRead_2_nextlineStart = Signal()
49+
req_bits_pcMemRead_3_nextlineStart = Signal()
50+
req_bits_pcMemRead_4_nextlineStart = Signal()
51+
52+
class ToPrefetchBundle(Bundle):
53+
req_ready = Signal()
54+
req_valid = Signal()
55+
56+
flushFromBpu_s2_valid = Signal()
57+
flushFromBpu_s2_bits_flag = Signal()
58+
flushFromBpu_s2_bits_value = Signal()
59+
flushFromBpu_s3_valid = Signal()
60+
flushFromBpu_s3_bits_flag = Signal()
61+
flushFromBpu_s3_bits_value = Signal()
62+
63+
64+
65+
class FromBpuBundle(Bundle):
66+
67+
resp_valid = Signal()
68+
resp_ready = Signal()
69+
resp_bits_s1_pc_3 = Signal()
70+
resp_bits_s1_full_pred_3_fallThroughErr = Signal()
71+
72+
resp_bits_s2_valid_3 = Signal()
73+
resp_bits_s2_hasRedirect_3 = Signal()
74+
resp_bits_s2_pc_3 = Signal()
75+
resp_bits_s2_ftq_idx_value = Signal()
76+
resp_bits_s2_ftq_idx_flag = Signal()
77+
resp_bits_s2_full_pred_3_fallThroughErr = Signal()
78+
resp_bits_s2_full_pred_3_hit = Signal()
79+
80+
resp_bits_s3_valid_3 = Signal()
81+
resp_bits_s3_hasRedirect_3 = Signal()
82+
resp_bits_s3_pc_3 = Signal()
83+
resp_bits_s3_ftq_idx_value = Signal()
84+
resp_bits_s3_ftq_idx_flag = Signal()
85+
resp_bits_s3_full_pred_3_fallThroughErr = Signal()
86+
87+
88+
last_stage_ftb_entry = LastStageFtbEntryBundle.from_prefix("resp_bits_last_stage_ftb_entry_")
89+
90+
class FromBackendBundle(Bundle):
91+
redirect_valid = Signal()
92+
redirect_bits_ftqIdx_value = Signal()
93+
redirect_bits_ftqIdx_flag = Signal()
94+
redirect_bits_ftqOffset = Signal()
95+
redirect_bits_cfiUpdate_target = Signal()
96+
redirect_bits_cfiUpdate_taken = Signal()
97+
redirect_bits_cfiUpdate_isMisPred = Signal()
98+
99+
redirect_bits_level = Signal()
100+
redirect_bits_debugIsCtrl = Signal()
101+
redirect_bits_debugIsMemVio = Signal()
102+
103+
ftqIdxSelOH_bits = Signal()
104+
ftqIdxAhead_0_valid = Signal()
105+
ftqIdxAhead_0_bits_value = Signal()
106+
107+
rob_commits_0 = RobCommitBundle.from_prefix("rob_commits_0_")
108+
rob_commits_1 = RobCommitBundle.from_prefix("rob_commits_1_")
109+
rob_commits_2 = RobCommitBundle.from_prefix("rob_commits_2_")
110+
rob_commits_3 = RobCommitBundle.from_prefix("rob_commits_3_")
111+
rob_commits_4 = RobCommitBundle.from_prefix("rob_commits_4_")
112+
rob_commits_5 = RobCommitBundle.from_prefix("rob_commits_5_")
113+
rob_commits_6 = RobCommitBundle.from_prefix("rob_commits_6_")
114+
rob_commits_7 = RobCommitBundle.from_prefix("rob_commits_7_")
115+
116+
class FromIfuBundle(Bundle):
117+
pdWb_bits_target = Signal()
118+
pdWb_bits_cfiOffset_valid = Signal()
119+
pdWb_bits_misOffset_valid = Signal()
120+
pdWb_bits_ftqIdx_value = Signal()
121+
pdWb_bits_ftqIdx_flag = Signal()
122+
pdWb_bits_misOffset_bits = Signal()
123+
pdWb_valid = Signal()
124+
125+
126+
127+
128+
129+
130+
131+
pdWb_bits_pd_0 = IfuPdSlotBundle.from_prefix("pdWb_bits_pd_0_")
132+
pdWb_bits_pd_1 = IfuPdSlotBundle.from_prefix("pdWb_bits_pd_1_")
133+
pdWb_bits_pd_2 = IfuPdSlotBundle.from_prefix("pdWb_bits_pd_2_")
134+
pdWb_bits_pd_3 = IfuPdSlotBundle.from_prefix("pdWb_bits_pd_3_")
135+
pdWb_bits_pd_4 = IfuPdSlotBundle.from_prefix("pdWb_bits_pd_4_")
136+
pdWb_bits_pd_5 = IfuPdSlotBundle.from_prefix("pdWb_bits_pd_5_")
137+
pdWb_bits_pd_6 = IfuPdSlotBundle.from_prefix("pdWb_bits_pd_6_")
138+
pdWb_bits_pd_7 = IfuPdSlotBundle.from_prefix("pdWb_bits_pd_7_")
139+
pdWb_bits_pd_8 = IfuPdSlotBundle.from_prefix("pdWb_bits_pd_8_")
140+
pdWb_bits_pd_9 = IfuPdSlotBundle.from_prefix("pdWb_bits_pd_9_")
141+
pdWb_bits_pd_10 = IfuPdSlotBundle.from_prefix("pdWb_bits_pd_10_")
142+
pdWb_bits_pd_11 = IfuPdSlotBundle.from_prefix("pdWb_bits_pd_11_")
143+
pdWb_bits_pd_12 = IfuPdSlotBundle.from_prefix("pdWb_bits_pd_12_")
144+
pdWb_bits_pd_13 = IfuPdSlotBundle.from_prefix("pdWb_bits_pd_13_")
145+
pdWb_bits_pd_14 = IfuPdSlotBundle.from_prefix("pdWb_bits_pd_14_")
146+
pdWb_bits_pd_15 = IfuPdSlotBundle.from_prefix("pdWb_bits_pd_15_")
147+
148+
pdWb_bits_pc_0 = Signal()
149+
pdWb_bits_pc_1 = Signal()
150+
pdWb_bits_pc_2 = Signal()
151+
pdWb_bits_pc_3 = Signal()
152+
pdWb_bits_pc_4 = Signal()
153+
pdWb_bits_pc_5 = Signal()
154+
pdWb_bits_pc_6 = Signal()
155+
pdWb_bits_pc_7 = Signal()
156+
pdWb_bits_pc_8 = Signal()
157+
pdWb_bits_pc_9 = Signal()
158+
pdWb_bits_pc_10 = Signal()
159+
pdWb_bits_pc_11 = Signal()
160+
pdWb_bits_pc_12 = Signal()
161+
pdWb_bits_pc_13 = Signal()
162+
pdWb_bits_pc_14 = Signal()
163+
pdWb_bits_pc_15 = Signal()
164+
165+
class FtqBundle(Bundle):
166+
167+
168+
fromBackend = FromBackendBundle.from_prefix("fromBackend_")
169+
fromIfu = FromIfuBundle.from_prefix("fromIfu_")
170+
fromBpu = FromBpuBundle.from_prefix("fromBpu_")
171+
toIfu = ToIfuBundle.from_prefix("toIfu_")
172+
toICache = ToICacheBundle.from_prefix("toICache_")
173+
toPrefetch = ToPrefetchBundle.from_prefix("toPrefetch_")
174+

ut_frontend/ftq/ftq_top/ref/__init__.py

Whitespace-only changes.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
from collections import namedtuple
2+
import random
3+
4+
# --- 数据结构定义 ---
5+
BpuPacket = namedtuple('BpuPacket', ['pc', 'fallThruError'])
6+
FtqPointer = namedtuple('FtqPointer', ['value', 'flag'])
7+
FTQ_SIZE = 64
8+
# --- 最终版参考模型 ---
9+
10+
def get_random_ptr_before_bpu(bpu_ptr: FtqPointer) -> FtqPointer:
11+
steps_to_go_back = random.randint(1, FTQ_SIZE - 1)
12+
new_value = bpu_ptr.value
13+
new_flag = bpu_ptr.flag
14+
for _ in range(steps_to_go_back):
15+
if new_value == 0:
16+
new_value = FTQ_SIZE - 1
17+
new_flag = not new_flag
18+
else:
19+
new_value -= 1
20+
21+
return FtqPointer(new_value, new_flag)
22+
23+
class FtqAccurateRef:
24+
"""参考模型,所有指针计算和逻辑判断直接内联执行"""
25+
26+
27+
def __init__(self, ftq_size=64):
28+
self.FTQ_SIZE = ftq_size
29+
self.bpu_ptr = FtqPointer(0, False)
30+
self.ifu_ptr = FtqPointer(0, False)
31+
self.mem = {}
32+
33+
def enqueue(self, data_packet):
34+
if FtqPointer(
35+
(self.bpu_ptr.value + 1) % self.FTQ_SIZE,
36+
self.bpu_ptr.flag if self.bpu_ptr.value != self.FTQ_SIZE - 1 else not self.bpu_ptr.flag
37+
) == self.ifu_ptr:
38+
return False
39+
40+
self.mem[self.bpu_ptr.value] = data_packet
41+
self.bpu_ptr = FtqPointer(
42+
(self.bpu_ptr.value + 1) % self.FTQ_SIZE,
43+
self.bpu_ptr.flag if self.bpu_ptr.value != self.FTQ_SIZE - 1 else not self.bpu_ptr.flag
44+
)
45+
return True
46+
47+
def dequeue(self):
48+
if self.bpu_ptr == self.ifu_ptr:
49+
return None
50+
51+
data = self.mem[self.ifu_ptr.value]
52+
self.ifu_ptr = FtqPointer(
53+
(self.ifu_ptr.value + 1) % self.FTQ_SIZE,
54+
self.ifu_ptr.flag if self.ifu_ptr.value != self.FTQ_SIZE - 1 else not self.ifu_ptr.flag
55+
)
56+
57+
58+
return data
59+
60+
61+
62+
def redirect(self, redirect_idx, redirect_flag, redirect_packet):
63+
self.mem[redirect_idx] = redirect_packet
64+
65+
self.bpu_ptr = FtqPointer(
66+
(redirect_idx + 1) % self.FTQ_SIZE,
67+
bool(redirect_flag) if redirect_idx != self.FTQ_SIZE - 1 else not bool(redirect_flag)
68+
)
69+
70+
if (((bool(redirect_flag) == self.ifu_ptr.flag) and (redirect_idx <= self.ifu_ptr.value)) or \
71+
((bool(redirect_flag) != self.ifu_ptr.flag) and (redirect_idx > self.ifu_ptr.value))):
72+
self.ifu_ptr = FtqPointer(redirect_idx, bool(redirect_flag))
73+
# 假设 FtqPointer 是一个已定义的类
74+
# 假设 self.mem = {}

ut_frontend/ftq/ftq_top/test/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)