Add frontend.bpu.ittage test code (#41)

This commit is contained in:
Frank OU 2025-02-06 14:22:10 +08:00 committed by GitHub
parent d7e91e08f8
commit 8e09f7c86a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 1344 additions and 2 deletions

View File

@ -17,8 +17,34 @@ import os
def build(cfg):
return False
from tempfile import NamedTemporaryFile
from toffee_test.markers import match_version
from comm import error, info, get_root_dir, exe_cmd, get_all_rtl_files
# check version
if not match_version(cfg.rtl.version, "openxiangshan-kmh-*"):
error(f"frontend_bpu_ittage: Unsupported RTL version {cfg.rtl.version}")
return False
# find source files for ITTage
rtl_files = get_all_rtl_files("ITTage", cfg=cfg)
assert rtl_files, "Cannot find RTL files of Frontend.BPU.ITTage"
internal_signals_path = os.path.join(get_root_dir("ut_frontend/bpu/ittage/internal.yaml"))
assert os.path.exists(internal_signals_path), "Cannot find internal signal files"
# export ITTage.sv
if not os.path.exists(get_root_dir("dut/ITTage")):
info("Exporting ITTage.sv")
with NamedTemporaryFile("w+", encoding="utf-8", suffix=".txt") as filelist:
filelist.write("\n".join(rtl_files))
filelist.flush()
s, _, err = exe_cmd(
f"picker export --cp_lib false {rtl_files[0]} --fs {filelist.name} --lang python --tdir "
f"{get_root_dir('dut')}/ -w ITTage.fst -c --internal={internal_signals_path}")
assert s, err
return True
def line_coverage_files(cfg):
return []
return ["ITTage.v"]

View File

48
ut_frontend/bpu/ittage/env/bundle.py vendored Normal file
View File

@ -0,0 +1,48 @@
__all__ = ['UpdateBundle', 'InBundle', 'OutBundle', 'PipelineCtrl']
from toffee import Bundle, Signals, Signal
class PipelineCtrl(Bundle):
s0_fire_0, s0_fire_1, s0_fire_2, s0_fire_3 = Signals(4)
s1_fire_0, s1_fire_1, s1_fire_2, s1_fire_3 = Signals(4)
s2_fire_0, s2_fire_1, s2_fire_2, s2_fire_3 = Signals(4)
class FullPred(Bundle):
jalr_target = Signal()
class FoldedHist(Bundle):
[hist_14_folded_hist, hist_13_folded_hist , hist_12_folded_hist , hist_10_folded_hist,
hist_6_folded_hist , hist_4_folded_hist , hist_3_folded_hist , hist_2_folded_hist] = Signals(8)
class FTBEntry(Bundle):
tailSlot_offset, tailSlot_sharing, tailSlot_valid, isRet, isJalr = Signals(5)
class CFI(Bundle):
valid, bits = Signals(2)
# io_update_
class UpdateBundle(Bundle):
valid, bits_pc, bits_jmp_taken, bits_mispred_mask_2, bits_meta, bits_full_target = Signals(6)
bits_ghist = Signal()
ftb_entry = FTBEntry.from_prefix("bits_ftb_entry_")
# folded_hist= FoldedHist.from_prefix("bits_spec_info_folded_hist_")
cfi = CFI.from_prefix("bits_cfi_idx_")
# io_in_
class InBundle(Bundle):
bits_s0_pc_3 = Signal()
folded_hist = FoldedHist.from_prefix("bits_s1_folded_hist_3_")
pred0 = FullPred.from_prefix("bits_resp_in_0_s2_full_pred_0_")
pred1 = FullPred.from_prefix("bits_resp_in_0_s2_full_pred_1_")
pred2 = FullPred.from_prefix("bits_resp_in_0_s2_full_pred_2_")
pred3 = FullPred.from_prefix("bits_resp_in_0_s2_full_pred_3_")
# io_out_
class OutBundle(Bundle):
last_stage_meta = Signal()
pred1 = FullPred.from_prefix("s3_full_pred_1_")
pred0 = FullPred.from_prefix("s3_full_pred_0_")
pred2 = FullPred.from_prefix("s3_full_pred_2_")
pred3 = FullPred.from_prefix("s3_full_pred_3_")

View File

@ -0,0 +1,26 @@
__all__ = ["GlobalHistory"]
GLOBAL_HISTORY_LEN = 256
GLOBAL_HISTORY_MASK = (1 << GLOBAL_HISTORY_LEN) - 1
class GlobalHistory:
def __init__(self, init_val: int = 0, gh_len: int = GLOBAL_HISTORY_LEN):
self.value = init_val
self._len = gh_len
def update(self, taken: bool) -> None:
g = self.value
self.value = (g << 1) | taken & GLOBAL_HISTORY_MASK
def get_fh(self, folded_len: int, hist_len: int) -> int:
if folded_len == 0:
return 0
res = 0
g = self.value & ((1 << hist_len) - 1)
mask = (1 << folded_len) - 1
for _ in range(0, min(self._len, hist_len), folded_len):
res ^= g & mask
g >>= folded_len
return res

View File

@ -0,0 +1,134 @@
import os.path
import random
from datetime import datetime
import toffee
from toffee import ClockCycles
from dut.ITTage import DUTITTage
from ..util.common import get_folded_hist
from .bundle import *
__all__ = ['ITTageWrapper']
class ITTageWrapper:
def __init__(self, dut: DUTITTage):
# Create DUT
self.dut = dut
self.dut.InitClock("clock")
self.xclock = self.dut.xclock # for callback function
# Connect Bundle
self.update_bundle = UpdateBundle.from_prefix("io_update_").set_name("ittage_update").bind(self.dut)
self.in_bundle = InBundle.from_prefix("io_in_").set_name("ittage_in").bind(self.dut)
self.out_bundle = OutBundle.from_prefix("io_out_").set_name("ittage_out").bind(self.dut)
self.pipeline_ctrl = PipelineCtrl.from_prefix("io_").set_name("ittage_pipeline_ctrl").bind(self.dut)
# Reset
self.reset()
def finalize(self):
self.dut.Finish()
async def __rst_async__(self):
self.dut.reset.value = 1
await ClockCycles(self.dut, 10)
self.dut.reset.value = 0
def __set_fire__(self, stage):
for i in range(0, 4):
getattr(self.pipeline_ctrl, f"s{stage}_fire_{i}").value = 1
def __unset_fire__(self, stage):
for i in range(0, 4):
getattr(self.pipeline_ctrl, f"s{stage}_fire_{i}").value = 0
async def __predict_async__(self, fh, pc, use_dummy_fh=False):
# stage0
self.__set_fire__(0)
self.in_bundle.bits_s0_pc_3.value = pc
if use_dummy_fh:
self.in_bundle.folded_hist.set_all(0)
else:
self.in_bundle.folded_hist.assign(get_folded_hist(fh))
await ClockCycles(self.dut, 1)
self.__unset_fire__(0)
# stage1
self.__set_fire__(1)
await ClockCycles(self.dut, 1)
self.__unset_fire__(1)
# stage2
self.__set_fire__(2)
await ClockCycles(self.dut, 1)
self.__unset_fire__(2)
# stage3
await ClockCycles(self.dut, 1)
return self.out_bundle.as_dict()
async def __update_async__(self, update_req):
self.update_bundle.assign(update_req)
self.update_bundle.valid.value = 1
await ClockCycles(self.dut, 1)
self.update_bundle.valid.value = 0
await ClockCycles(self.dut, 3)
return None
def predict(self, fh, pc, use_dummy_fh=False):
# stage0
self.__set_fire__(0)
self.in_bundle.bits_s0_pc_3.value = pc
if use_dummy_fh:
self.in_bundle.folded_hist.set_all(0)
else:
self.in_bundle.folded_hist.assign(get_folded_hist(fh))
self.xclock.Step(1)
self.__unset_fire__(0)
# stage1
self.__set_fire__(1)
self.xclock.Step(1)
self.__unset_fire__(1)
# stage2
self.__set_fire__(2)
self.xclock.Step(1)
self.__unset_fire__(2)
# stage3
self.xclock.Step(1)
return self.out_bundle.as_dict()
def predict_async(self, fh, pc, use_dummy_fh=False):
return toffee.create_task(self.__predict_async__(fh, pc, use_dummy_fh))
def update(self, update_req):
self.update_bundle.assign(update_req)
self.update_bundle.valid.value = 1
self.xclock.Step(1)
self.update_bundle.valid.value = 0
self.xclock.Step(1)
pass
def update_async(self, update_req):
return toffee.create_task(self.__update_async__(update_req))
def reset(self):
self.dut.reset.value = 1
self.xclock.Step(1)
self.dut.reset.value = 0
while self.dut.io_s1_ready.value == 0:
self.xclock.Step(1)
self.xclock.Step(10)
def reset_async(self):
return toffee.create_task(self.__rst_async__())

View File

@ -0,0 +1,62 @@
ITTage:
- "logic s3_altProvided"
- "logic [1:0] s3_altProviderCtr"
- "logic [40:0] s3_altProviderTarget"
- "logic [2:0] s3_altProvider"
- "logic s3_provided"
- "logic [1:0] s3_providerCtr"
- "logic s3_providerU"
- "logic [2:0] s3_provider"
- "logic [40:0] s3_providerTarget"
- "logic [7:0] tickCtr"
- "logic updateValid"
- tables_0:
- "wire io_resp_valid"
- "wire io_update_valid"
- "wire io_update_alloc"
- "wire io_update_correct"
- "wire [1:0] io_update_oldCtr"
- "wire [1:0] update_wdata_ctr"
- "wire io_update_reset_u"
- "wire io_update_uValid"
- "wire io_update_u"
- tables_1:
- "wire io_resp_valid"
- "wire io_update_valid"
- "wire io_update_alloc"
- "wire io_update_correct"
- "wire [1:0] io_update_oldCtr"
- "wire [1:0] update_wdata_ctr"
- "wire io_update_reset_u"
- "wire io_update_uValid"
- "wire io_update_u"
- tables_2:
- "wire io_resp_valid"
- "wire io_update_valid"
- "wire io_update_alloc"
- "wire io_update_correct"
- "wire [1:0] io_update_oldCtr"
- "wire [1:0] update_wdata_ctr"
- "wire io_update_reset_u"
- "wire io_update_uValid"
- "wire io_update_u"
- tables_3:
- "wire io_resp_valid"
- "wire io_update_valid"
- "wire io_update_alloc"
- "wire io_update_correct"
- "wire [1:0] io_update_oldCtr"
- "wire [1:0] update_wdata_ctr"
- "wire io_update_reset_u"
- "wire io_update_uValid"
- "wire io_update_u"
- tables_4:
- "wire io_resp_valid"
- "wire io_update_valid"
- "wire io_update_alloc"
- "wire io_update_correct"
- "wire [1:0] io_update_oldCtr"
- "wire [1:0] update_wdata_ctr"
- "wire io_update_reset_u"
- "wire io_update_uValid"
- "wire io_update_u"

View File

View File

@ -0,0 +1,116 @@
"""
Define all the coverage group calculation here
Author: yzcc
"""
from comm import UT_FCOV
from .chk_pred import *
from .chk_train import *
from ..env.ittage_wrapper import ITTageWrapper
def get_cov_grp_of_alt_pred(dut: DUTITTage) -> CovGroup:
grp = CovGroup(UT_FCOV("../UT_ITTage"))
bins = {}
for i in range(4):
bins[f"alt_use_table_{i}"] = is_alt_from_table_i(i)
grp.add_watch_point(dut, bins, name="ITTAGE Alt Pred altProvider from Tn")
return grp
def get_cov_grp_of_longest_and_alt_train(dut: ITTageWrapper) -> CovGroup:
grp = CovGroup(UT_FCOV("../UT_ITTage"))
bins_as_provider = {}
bins_as_alt = {}
for i in range(5):
# 第i个表被作为主预测更新
bins_as_provider["_".join([f"table{i + 1}", "update", "as", "provider"])] = is_update_table_as_provider(i)
# 第i个表被作为替代预测更新
if i != 4:
bins_as_alt["_".join([f"table{i + 1}", "update", "as", "alt"])] = is_update_table_as_alt(i)
grp.add_watch_point(dut, bins_as_provider, name="ITTAGE Longest and Alt Train Update as Provider")
grp.add_watch_point(dut, bins_as_alt, name="ITTAGE Longest and Alt Train Update as Alt Provider")
return grp
def get_cov_grp_of_main_pred(dut: DUTITTage) -> CovGroup:
grp = CovGroup(UT_FCOV("../UT_ITTage"))
bins = {}
for i in range(5):
bins[f"T{i}_hit"] = is_hit_table_i(i)
grp.add_watch_point(dut, bins, name="ITTAGE Main Pred Hit Tn")
return grp
def get_cov_grp_of_other_pred(dut: DUTITTage) -> CovGroup:
grp = CovGroup(UT_FCOV("../UT_ITTage"))
# 是否命中多个表
grp.add_watch_point(dut, {"hit_multi": is_hit_multi_table()}, name="ITTAGE Other Pred Hit Multi Table")
# 是否一个表都没命中
grp.add_watch_point(dut, {"hit_no": is_hit_no_table()}, name="ITTAGE Other Pred Hit No Table")
# Res src
# 替代预测来自ftb
bins_src = {}
bins_src["src_from_main"] = is_src_from_main()
bins_src["src_from_alt"] = is_src_from_alt()
bins_src["src_from_ftb"] = is_src_from_ftb()
grp.add_watch_point(dut, bins_src, name="ITTAGE Other Pred Result Source")
return grp
def get_cov_grp_of_other_train(dut: DUTITTage) -> CovGroup:
grp = CovGroup(UT_FCOV("../UT_ITTage"))
# tickCtr -> reset
grp.add_watch_point(dut, {"reset_us": is_reset_us}, name="ITTAGE Other Train Reset Useful Bit")
# 更新最长表的时候,更新/不更新替代预测
bins_update = {}
for update_alt in range(2):
s = ["update", "provider", "and", "alt"] if update_alt else ["only", "update", "provider"]
bins_update["_".join(s)] = is_update_table_as_provider(update_alt)
grp.add_watch_point(dut, bins_update, name="ITTAGE Other Train Update Entry")
# 申请新表项成功/失败
bins_alloc = {}
for success in range(2):
s = "succeed" if success else "fail"
bins_alloc["allocate_" + s] = is_allocate_succeed_or_fail(success)
grp.add_watch_point(dut, bins_alloc, name="ITTAGE Other Train Allocate Entry")
return grp
def get_cov_grp_of_train_saturation(dut: DUTITTage) -> CovGroup:
grp = CovGroup(UT_FCOV("../UT_ITTage"))
for up_or_down in range(2):
saturating_status = "up" if up_or_down else "down"
bins = {}
for i in range(5):
bins[f"T{i}_{saturating_status}_saturate"] = is_table_ctr_saturing(i, up_or_down)
grp.add_watch_point(dut, bins, name=f"ITTAGE Saturation Train {saturating_status} saturing")
return grp
def get_cov_grp_of_us_train(dut: DUTITTage) -> CovGroup:
grp = CovGroup(UT_FCOV("../UT_ITTage"))
bins_set = {}
bins_clr = {}
for i in range(5):
bins_set[f"T{i} us set"] = is_us_set_or_clear(1, i)
bins_clr[f"T{i} us clear"] = is_us_set_or_clear(0, i)
grp.add_watch_point(dut, bins_set, name="ITTAGE Us Train Tn us set")
grp.add_watch_point(dut, bins_clr, name="ITTAGE Us Train Tn us clear")
return grp

View File

@ -0,0 +1,86 @@
"""
Checkpoint for prediction
"""
from dut.ITTage import DUTITTage
def is_hit_table_i(i: int):
def hit_table_i(dut: DUTITTage):
tn_hit = getattr(dut, f"ITTage_tables_{i}_io_resp_valid").value
return tn_hit
return hit_table_i
def is_hit_multi_table():
def hit_multi_table(dut: DUTITTage):
tn_hit = 0
for i in range(5):
tn_hit += is_hit_table_i(i)(dut)
return tn_hit > 1
return hit_multi_table
def is_hit_no_table():
def hit_no_table(dut: DUTITTage):
tn_hit = 0
for i in range(5):
tn_hit += is_hit_table_i(i)(dut)
return tn_hit == 0
return hit_no_table
def is_alt_from_ftb():
def alt_from_ftb(dut: DUTITTage):
provided = dut.ITTage_s3_provided.value
alt_provided = dut.ITTage_s3_altProvided.value
return alt_provided == 0 and provided == 0
return alt_from_ftb
def is_alt_from_table_i(i: int):
def alt_from_table_i(dut: DUTITTage):
alt_provided = dut.ITTage_s3_altProvided.value
alt_provider = dut.ITTage_s3_altProvider.value
return alt_provided and alt_provider == i
return alt_from_table_i
# final prediction target
def is_src_from_main():
def src_from_main(dut: DUTITTage):
provided = dut.ITTage_s3_provided.value
provider_ctr = dut.ITTage_s3_providerCtr.value
provider_target = dut.ITTage_s3_providerTarget.value
pred_target = dut.io_out_s3_full_pred_3_jalr_target.value
return provided > 0 and provider_ctr > 0 and provider_target == pred_target
return src_from_main
def is_src_from_alt():
def src_from_alt(dut: DUTITTage):
provided = dut.ITTage_s3_provided.value
provider_ctr = dut.ITTage_s3_providerCtr.value
alt_provided = dut.ITTage_s3_altProvided.value
alt_target = dut.ITTage_s3_altProviderTarget.value
pred_target = dut.io_out_s3_full_pred_3_jalr_target.value
return provided > 0 and provider_ctr == 0 and alt_provided > 0 and alt_target == pred_target
return src_from_alt
def is_src_from_ftb():
def src_from_ftb(dut: DUTITTage):
provided = dut.ITTage_s3_provided.value
alt_provided = dut.ITTage_s3_altProvided.value
ftb_target = dut.io_in_bits_resp_in_0_s2_full_pred_3_jalr_target.value
pred_target = dut.io_out_s3_full_pred_3_jalr_target.value
return provided == 0 and alt_provided == 0 and ftb_target == pred_target
return src_from_ftb

View File

@ -0,0 +1,165 @@
"""
Checkpoint for train
"""
from toffee import CovGroup
from ..util.meta_parser import MetaParser
from dut.ITTage import DUTITTage
def is_table_ctr_saturing(i: int, up_or_down: int):
sat_value = 0b11 if up_or_down else 0
def table_ctr_saturing(dut: DUTITTage):
alloc = getattr(dut, f"ITTage_tables_{i}_io_update_alloc").value
correct = getattr(dut, f"ITTage_tables_{i}_io_update_correct").value
valid = getattr(dut, f"ITTage_tables_{i}_io_update_valid").value and not alloc and up_or_down == correct
old_ctr = getattr(dut, f"ITTage_tables_{i}_io_update_oldCtr").value
new_ctr = getattr(dut, f"ITTage_tables_{i}_update_wdata_ctr").value
return valid and old_ctr == sat_value and new_ctr == sat_value
return table_ctr_saturing
def is_update_provider(update_alt: int):
def update_provider(dut: DUTITTage):
valid = dut.io_update_valid.value and dut.ITTage_updateValid.value
provided = dut.ITTage_s3_provided.value
alt_provided = dut.ITTage_s3_altProvided.value
if valid and provided:
return update_alt == alt_provided
return False
return update_provider
def is_update_table_as_provider(i: int):
def update_table_as_provider(dut: DUTITTage):
valid = dut.io_update_valid.value and dut.ITTage_updateValid.value
provided = dut.ITTage_s3_provided.value
provider = dut.ITTage_s3_provider.value
return valid and provided and (provider == i)
return update_table_as_provider
def is_update_table_as_alt(i: int):
def update_table_as_alt(dut: DUTITTage):
valid = dut.io_update_valid.value and dut.ITTage_updateValid.value
alt_provided = dut.ITTage_s3_altProvided.value
alt_provider = dut.ITTage_s3_altProvider.value
return valid and alt_provided and (alt_provider == i)
return update_table_as_alt
def is_reset_us(dut: DUTITTage):
valid = dut.io_update_valid.value
tick_ctr = dut.ITTage_tickCtr.value
reset = sum([getattr(dut, f"ITTage_tables_{i}_io_update_reset_u").value for i in range(5)])
assert reset in {0, 5}, "Number of reset tables must be 5"
if valid and (reset == 5):
# assert tick_ctr == 0xff
return True
return False
def is_allocate_succeed_or_fail(success_or_fail: int):
def allocate_succeed_or_fail(dut: DUTITTage):
valid = dut.io_update_valid.value and dut.ITTage_updateValid.value
allocate_valid = MetaParser(dut.io_update_bits_meta.value).allocate_valid
return valid and (allocate_valid == success_or_fail)
return allocate_succeed_or_fail
def is_us_set_or_clear(set_or_clear: int, i: int):
def us_set_or_clear(dut: DUTITTage):
meta_wrap = MetaParser(dut.io_update_bits_meta.value)
altDiffer = meta_wrap.altDiffers
providerU = meta_wrap.providerU
provider = meta_wrap.provider
misPred = dut.io_update_bits_mispred_mask_2.value
set_value = (not misPred) if altDiffer else providerU
valid = dut.io_update_valid.value and meta_wrap.provided
return valid and set_value == set_or_clear and provider == i
return us_set_or_clear
def is_set_us_correct(dut: DUTITTage):
meta_parser = MetaParser(dut.io_update_bits_meta.value)
provided = meta_parser.provided
provider = meta_parser.provider
provider_ctr = meta_parser.providerCtr
provider_target = meta_parser.providerTarget
use_provider = provided and provider_ctr != 0
real_target = dut.io_update_bits_full_target.value
provider_correct = provider_target == real_target
valid = dut.io_update_valid.value and dut.ITTage_updateValid.value
set_us_true = (getattr(dut, f"ITTage_tables_{provider}_io_update_uValid").value and
getattr(dut, f"ITTage_tables_{provider}_io_update_u").value)
if valid and set_us_true:
# 香山ITTAGE对于us置1的基本逻辑是只要
return use_provider and provider_correct
return False
def _get_cov_grp_of_pred(dut: DUTITTage) -> CovGroup:
grp = CovGroup("ITTAGE Train")
# 第i个表计数器达到上/下饱和
for i in range(5):
for up_or_down in range(2):
saturing_status = "up" if up_or_down else "down"
grp.add_watch_point(
dut,
{"_".join([f"table{i + 1}", saturing_status, "saturing"]): is_table_ctr_saturing(i, up_or_down)},
name=" ".join([f"Table{i + 1}", "is", saturing_status.capitalize(), "Saturing"]),
)
for i in range(5):
# 第i个表被作为主预测更新
grp.add_watch_point(
dut,
{"_".join([f"table{i + 1}", "update", "as", "provider"]): is_update_table_as_provider(i)},
name=" ".join([f"Table{i + 1}", "Update", "as", "Provider"]),
)
# 第i个表被作为替代预测更新
grp.add_watch_point(
dut,
{"_".join([f"table{i + 1}", "update", "as", "alt"]): is_update_table_as_alt(i)},
name=" ".join([f"Table{i + 1}", "Update", "as", "Alt"]),
)
# 更新最长表的时候,更新/不更新替代预测
for update_alt in range(2):
s = ["update", "provider", "and", "alt"] if update_alt else ["only", "update", "provider"]
grp.add_watch_point(
dut,
{"_".join(s): is_update_table_as_provider(update_alt)},
name=" ".join([x.capitalize() for x in s])
)
# tickCtr达到最大值触发重置useful位
grp.add_watch_point(dut, {"reset_us": is_reset_us}, name="Reset Useful Bit")
# 申请新表项成功/失败
for success in range(2):
s = "succeed" if success else "fail"
grp.add_watch_point(
dut,
{"allocate_" + s: is_allocate_succeed_or_fail(success)},
name="Allocate " + s.capitalize()
)
# 在正确的条件下us被置为1
grp.add_watch_point(dut, {"us_set_1": is_set_us_correct}, name="Provider us bit Set 1")
return grp

View File

@ -0,0 +1,73 @@
"""
Test for allocate entry
Author: yzcc
"""
import toffee_test
from dut.ITTage import DUTITTage
from ..util.common import UpdateReq
from ..util.meta_parser import MetaParser
from .chk_helper import get_cov_grp_of_other_train, get_cov_grp_of_us_train
from ..env.ittage_wrapper import ITTageWrapper
def test_alloc_train(ittage_wrapper: ITTageWrapper):
pins = ittage_wrapper
# Start
pins.xclock.Step(1000)
pc = 0x114514
dut_output = pins.predict(0, pc, True)
meta_wrap = MetaParser(dut_output["last_stage_meta"])
pred_target = dut_output["pred0"]["jalr_target"]
meta_wrap.allocate_valid = 0
meta_wrap.allocate_bits = 0
for i in range(1000):
req = UpdateReq(pc, meta_wrap.value, pred_target + 0x4, 0, 1)
pins.update(req.asdict())
# Check
dut_output = pins.predict(0, pc, True)
meta_wrap = MetaParser(dut_output["last_stage_meta"])
pred_target = dut_output["pred0"]["jalr_target"]
assert meta_wrap.provided == 0 and meta_wrap.altProvided == 0 and pred_target == 0
fake_meta = MetaParser(0)
# provider is unconfident and providing an incorrect target
fake_meta.provided = 1
fake_meta.provider = 3
fake_meta.providerCtr = 0
fake_meta.providerTarget = 0x114514
# altProvider is unconfident and providing a correct target
fake_meta.altProvided = 1
fake_meta.altProvider = 1
fake_meta.altProviderCtr = 0
# altDiffers = s3_finalAltPred != s3_tageTaken_dup(3)
# = Mux(altProvided, altProviderInfo.ctr(ITTageCtrBits-1), True) != True
fake_meta.altDiffers = 1
fake_meta.altTargetCtr = 0x1919810
pins.update(UpdateReq(0x123, fake_meta.meta, 0x1919810, 1, 0).asdict())
assert not (pins.dut.ITTage_tables_3_io_update_valid.value
and pins.dut.ITTage_tables_3_io_update_u.value
and pins.dut.ITTage_tables_3_io_update_uValid.value), "provider shouldn't be set useful"
@toffee_test.fixture
async def ittage_wrapper(toffee_request: toffee_test.ToffeeRequest):
dut: DUTITTage = toffee_request.create_dut(DUTITTage, "clock")
wrapper = ITTageWrapper(dut)
toffee_request.add_cov_groups([
get_cov_grp_of_other_train(dut),
get_cov_grp_of_us_train(dut)
])
yield wrapper

View File

@ -0,0 +1,74 @@
"""
Test for alternative prediction
Author: yzcc
"""
import toffee_test
from random import randint
from toffee import CovGroup
from dut.ITTage import DUTITTage
from ..util.common import UpdateReq
from ..util.meta_parser import MetaParser
from .chk_helper import get_cov_grp_of_alt_pred
from ..env.ittage_wrapper import ITTageWrapper
def test_alt_pred(ittage_wrapper: ITTageWrapper):
pins = ittage_wrapper
# Start
pins.xclock.Step(1000)
# Test: use T1~T4 as alt_pred
for tab in range(4):
pc = tab * 4
# Alloc Longest
dut_output = pins.predict(0, pc, True)
meta_wrap = MetaParser(dut_output["last_stage_meta"])
pred_target = dut_output["pred0"]["jalr_target"]
meta_wrap.allocate_bits = 4
req = UpdateReq(pc, meta_wrap.value, pred_target + randint(1, 0x114514), 0, 1)
pins.update(req.asdict())
pins.xclock.Step(10)
# Alloc Second Longest
meta_wrap.allocate_bits = tab
req = UpdateReq(pc, meta_wrap.value, pred_target + randint(1, 0x114514), 0, 1)
pins.update(req.asdict())
pins.xclock.Step(10)
# Set longest to unconfident
dut_output = pins.predict(0, pc, True)
meta_wrap = MetaParser(dut_output["last_stage_meta"])
pred_target = dut_output["pred0"]["jalr_target"]
meta_wrap.provided = 1
meta_wrap.provider = 4
meta_wrap.providerCtr = 0b01
req = UpdateReq(pc, meta_wrap.value, pred_target + 0x1, 0, 1)
pins.update(req.asdict())
pins.xclock.Step(10)
pins.update(req.asdict())
pins.xclock.Step(10)
# Pred and Check
dut_output = pins.predict(0, pc, True)
meta_wrap = MetaParser(dut_output["last_stage_meta"])
assert meta_wrap.altProvided == 1 and meta_wrap.providerCtr == 0, "should use alternative pred"
pins.xclock.Step(100)
@toffee_test.fixture
async def ittage_wrapper(toffee_request: toffee_test.ToffeeRequest):
dut: DUTITTage = toffee_request.create_dut(DUTITTage, "clock")
wrapper = ITTageWrapper(dut)
toffee_request.add_cov_groups([
get_cov_grp_of_alt_pred(dut)
])
yield wrapper

View File

@ -0,0 +1,48 @@
"""
Test the main prediction func
Author: yzcc
"""
import asyncio
import toffee_test
from random import randint
from dut.ITTage import DUTITTage
from ..util.common import UpdateReq
from ..util.meta_parser import MetaParser
from .chk_helper import get_cov_grp_of_main_pred
from ..env.ittage_wrapper import ITTageWrapper
@toffee_test.testcase
async def test_main_pred(ittage_wrapper: ITTageWrapper):
pins = ittage_wrapper
# Start
pins.reset()
for tab in range(5):
pc = tab
dut_output = pins.predict(0, pc, True)
meta_wrap = MetaParser(dut_output["last_stage_meta"])
pred_target = dut_output["pred0"]["jalr_target"]
meta_wrap.allocate_bits = tab
req = UpdateReq(pc, meta_wrap.value, pred_target + randint(1, 0x114514), 0, 1)
pins.update(req.asdict())
# Check
dut_output = pins.predict(0, pc, True)
meta_wrap = MetaParser(dut_output["last_stage_meta"])
assert meta_wrap.provided == 1 and meta_wrap.provider == tab, f"T{tab} should be hit"
@toffee_test.fixture
async def ittage_wrapper(toffee_request: toffee_test.ToffeeRequest):
dut: DUTITTage = toffee_request.create_dut(DUTITTage, "clock")
wrapper = ITTageWrapper(dut)
toffee_request.add_cov_groups([
get_cov_grp_of_main_pred(dut)
])
yield wrapper

View File

@ -0,0 +1,52 @@
"""
Random Test
Author: yzcc
"""
import random
import toffee_test
from toffee import CovGroup
from dut.ITTage import DUTITTage
from ..util.common import UpdateReq
from .chk_helper import get_cov_grp_of_main_pred, get_cov_grp_of_us_train, get_cov_grp_of_other_train
from ..env.ittage_wrapper import ITTageWrapper
def test_random(ittage_wrapper: ITTageWrapper):
pins = ittage_wrapper
# Start Test
pins.reset()
random.seed(19260817)
pc = 0x114514
for ite in range(10000):
pc += random.randint(0, 0x3) * 2
# predict
dut_output = pins.predict(0, pc, True)
meta = dut_output["last_stage_meta"]
pred_target = dut_output["pred0"]["jalr_target"]
# update
if random.randint(0, 1):
req = UpdateReq(pc, meta, pred_target, 0, 0)
else:
req = UpdateReq(pc, meta, pred_target + 0x1, 0, 1)
pins.update(req.asdict())
@toffee_test.fixture
async def ittage_wrapper(toffee_request: toffee_test.ToffeeRequest):
dut: DUTITTage = toffee_request.create_dut(DUTITTage, "clock")
wrapper = ITTageWrapper(dut)
toffee_request.add_cov_groups([
get_cov_grp_of_main_pred(dut),
get_cov_grp_of_us_train(dut),
get_cov_grp_of_other_train(dut)
])
yield wrapper

View File

@ -0,0 +1,27 @@
import toffee_test
from toffee import CovGroup
from dut.ITTage import DUTITTage
from .chk_helper import get_cov_grp_of_main_pred
from ..env.ittage_wrapper import ITTageWrapper
def test_reset(ittage_wrapper: ITTageWrapper):
pins = ittage_wrapper
dut_output = pins.predict(0, 0x123, True)
for i in range(4):
assert dut_output[f"pred{i}"]["jalr_target"] == 0
@toffee_test.fixture
async def ittage_wrapper(toffee_request: toffee_test.ToffeeRequest):
dut: DUTITTage = toffee_request.create_dut(DUTITTage, "clock")
wrapper = ITTageWrapper(dut)
toffee_request.add_cov_groups([
get_cov_grp_of_main_pred(dut),
])
yield wrapper

View File

@ -0,0 +1,75 @@
"""
Test the saturation of the training
Author: yzcc
"""
import toffee_test
from dut.ITTage import DUTITTage
from ..util.common import UpdateReq
from ..util.meta_parser import MetaParser
from .chk_helper import get_cov_grp_of_train_saturation, get_cov_grp_of_other_train
from ..env.ittage_wrapper import ITTageWrapper
def test_saturation_train(ittage_wrapper: ITTageWrapper):
pins = ittage_wrapper
# Start
pins.xclock.Step(1000)
pc = None
for tab in range(5):
# Up Saturating
pc = tab
dut_output = pins.predict(0, pc, True)
meta_wrap = MetaParser(dut_output["last_stage_meta"])
pred_target = dut_output["pred0"]["jalr_target"]
meta_wrap.provided = 1
meta_wrap.provider = tab
meta_wrap.providerCtr = 0b11 # up bound
req = UpdateReq(pc, meta_wrap.value, pred_target, 0, 0)
pins.update(req.asdict())
pins.xclock.Step(10)
# Check
dut_output = pins.predict(0, pc, True)
meta_wrap = MetaParser(dut_output["last_stage_meta"])
assert meta_wrap.provided == 1 and meta_wrap.provider == tab and meta_wrap.providerCtr == 0b11, f"T{tab} up saturation fails"
# Down Saturating
pc = tab + 47
dut_output = pins.predict(0, pc, True)
meta_wrap = MetaParser(dut_output["last_stage_meta"])
pred_target = dut_output["pred0"]["jalr_target"]
meta_wrap.allocate_valid = 0
meta_wrap.provided = 1
meta_wrap.provider = tab
meta_wrap.providerCtr = 0b00
req = UpdateReq(pc, meta_wrap.value, pred_target + 1, 0, 1)
pins.update(req.asdict())
pins.xclock.Step(10)
# Check
dut_output = pins.predict(0, pc, True)
meta_wrap = MetaParser(dut_output["last_stage_meta"])
assert meta_wrap.provided == 1 and meta_wrap.provider == tab and meta_wrap.providerCtr == 0b00, f"T{tab} down saturation fails"
@toffee_test.fixture
async def ittage_wrapper(toffee_request: toffee_test.ToffeeRequest):
dut: DUTITTage = toffee_request.create_dut(DUTITTage, "clock")
wrapper = ITTageWrapper(dut)
toffee_request.add_cov_groups([
get_cov_grp_of_train_saturation(dut),
get_cov_grp_of_other_train(dut),
])
yield wrapper

View File

@ -0,0 +1,96 @@
"""
Src of prediction from:
1. Main prediction
2. Alt prediction
3. FTB prediction
Author: yzcc
"""
import toffee_test
from toffee import CovGroup
from ..util.common import UpdateReq
from ..util.meta_parser import MetaParser
from .chk_helper import get_cov_grp_of_other_pred
from ..env.ittage_wrapper import ITTageWrapper
from dut.ITTage import DUTITTage
def test_src_pred(ittage_wrapper: ITTageWrapper):
pins = ittage_wrapper
# Start
pins.xclock.Step(1000)
pc = 0x0
# Test: Use Main Pred - 2
dut_output = pins.predict(0, pc, True) # get meta, alloc
meta_wrap = MetaParser(dut_output["last_stage_meta"])
meta_wrap.allocate_bits = 4 # update
req = UpdateReq(pc, meta_wrap.value, 0x114514, 0, 1)
pins.update(req.asdict())
pins.xclock.Step(10)
dut_output = pins.predict(0, pc, True)
assert dut_output["pred0"]["jalr_target"] == 0x114514, "Target should from main prediction"
# Test: Use Main Pred - 1
fake_meta = MetaParser(0)
# except provider is unconfident
fake_meta.provided = 1
fake_meta.provider = 3
fake_meta.providerCtr = 0
fake_meta.providerTarget = 0x1919810
# except altProvider is invalid
fake_meta.altProvided = 0
fake_meta.altProvidedCtr = 0x114514
pins.update(UpdateReq(0x123, fake_meta.meta, 0xFF000, 1, 1).asdict())
pins.in_bundle.pred3.jalr_target.value = 0xF # T0 Target
pins.predict(1, 0x123)
dut_output = pins.out_bundle.as_dict()
assert dut_output['pred0']['jalr_target'] in {0xF, 0xFF000}, "Predict Target is not from known sources"
pins.in_bundle.pred3.jalr_target.value = 0
# Test: Use Alt Pred
# Alloc Second Longest
meta_wrap.allocate_bits = 1
req = UpdateReq(pc, meta_wrap.value, 0x1919810, 0, 1)
pins.update(req.asdict())
pins.xclock.Step(10)
# Set longest to unconfident
dut_output = pins.predict(0, pc, True)
meta_wrap = MetaParser(dut_output["last_stage_meta"])
pred_target = dut_output["pred0"]["jalr_target"]
meta_wrap.provided = 1
meta_wrap.provider = 4
meta_wrap.providerCtr = 0b01
req = UpdateReq(pc, meta_wrap.value, pred_target + 0x1, 0, 1)
pins.update(req.asdict())
pins.xclock.Step(10)
pins.update(req.asdict())
pins.xclock.Step(10)
dut_output = pins.predict(0, pc, True)
assert dut_output["pred0"]["jalr_target"] == 0x1919810, "Target should from alt prediction"
# Test: Use FTB Prerd
pc = 0x114
dut_output = pins.predict(0, pc, True)
assert dut_output["pred0"]["jalr_target"] == 0x0, "Target should from ftb"
@toffee_test.fixture
async def ittage_wrapper(toffee_request: toffee_test.ToffeeRequest):
dut: DUTITTage = toffee_request.create_dut(DUTITTage, "clock")
wrapper = ITTageWrapper(dut)
toffee_request.add_cov_groups([
get_cov_grp_of_other_pred(dut)
])
yield wrapper

View File

View File

@ -0,0 +1,97 @@
__all__ = ['gen_dummy_update_req', 'UpdateReq', 'get_folded_hist']
from ..env.global_history import GlobalHistory
def gen_dummy_update_req(pc, meta):
update_req = {}
update_req["valid"] = 1
update_req["bits_pc"] = pc
update_req["ftb_entry"] = {}
update_req["ftb_entry"]["tailSlot_offset"] = 0
update_req["ftb_entry"]["tailSlot_sharing"] = 0
update_req["ftb_entry"]["tailSlot_valid"] = 1
update_req["ftb_entry"]["isRet"] = 0
update_req["ftb_entry"]["isJalr"] = 1
update_req["cfi"] = {}
update_req["cfi"]["valid"] = 1
update_req["cfi"]["bits"] = 0
update_req["bits_jmp_taken"] = 1
update_req["bits_mispred_mask_2"] = 1
update_req["bits_meta"] = meta
return update_req
def get_folded_hist(hist: int) -> dict:
ghv = GlobalHistory(hist)
return {
"hist_14_folded_hist": ghv.get_fh(8, 8),
"hist_13_folded_hist": ghv.get_fh(9, 13),
"hist_12_folded_hist": ghv.get_fh(4, 4),
"hist_10_folded_hist": ghv.get_fh(9, 32),
"hist_6_folded_hist": ghv.get_fh(9, 16),
"hist_4_folded_hist": ghv.get_fh(8, 13),
"hist_3_folded_hist": ghv.get_fh(8, 32),
"hist_2_folded_hist": ghv.get_fh(8, 16),
}
class FtbEntry:
def __init__(self):
self.tailSlot_offset = 0
self.tailSlot_sharing = 0
self.tailSlot_valid = 1
self.isRet = 0
self.isJalr = 1
class CfiReq:
def __init__(self):
self.valid = 1
self.bits = 0
class UpdateReq:
def __init__(self, pc: int, meta: int, full_target: int, hist: int, mis_pred: int):
self.valid = 1
self.bits_pc = pc
self.ftb_entry = FtbEntry()
self.cfi = CfiReq()
self.bits_jmp_taken = 1
self.bits_mispred_mask_2 = mis_pred
self.bits_meta = meta
self.hist = hist
self.bits_full_target = full_target
def asdict(self) -> dict:
update_req = {}
update_req["valid"] = self.valid
update_req["bits_pc"] = self.bits_pc
update_req["ftb_entry"] = {}
update_req["ftb_entry"]["tailSlot_offset"] = self.ftb_entry.tailSlot_offset
update_req["ftb_entry"]["tailSlot_sharing"] = self.ftb_entry.tailSlot_sharing
update_req["ftb_entry"]["tailSlot_valid"] = self.ftb_entry.tailSlot_valid
update_req["ftb_entry"]["isRet"] = self.ftb_entry.isRet
update_req["ftb_entry"]["isJalr"] = self.ftb_entry.isJalr
update_req["cfi"] = {}
update_req["cfi"]["valid"] = self.cfi.valid
update_req["cfi"]["bits"] = self.cfi.bits
update_req["bits_jmp_taken"] = self.bits_jmp_taken
update_req["bits_mispred_mask_2"] = self.bits_mispred_mask_2
update_req["bits_meta"] = self.bits_meta
update_req["bits_full_target"] = self.bits_full_target
# update_req["folded_hist"] = get_folded_hist(self.hist)
update_req["bits_ghist"] = self.hist
return update_req

View File

@ -0,0 +1,137 @@
__all__ = ['MetaParser']
class MetaParser:
def __init__(self, meta: int) -> None:
self.value = meta
pass
def bits(self, high, low):
assert low <= high
mask = (1 << (high + 1)) - 1
return (self.value & mask) >> low
def bit(self, bit):
return (self.value >> bit) & 1
@property
def meta(self) -> int:
return self.value
@property
def provided(self) -> int:
return self.bit(117)
@provided.setter
def provided(self, value: int):
mask = ~(1 << 117)
self.value = (self.value & mask) | (value << 117)
@property
def provider(self):
"""The provider property."""
return self.bits(116, 114)
@provider.setter
def provider(self, value):
mask = ~(0b111 << 114)
self.value = (self.value & mask) | (value << 114)
@property
def altProvided(self):
"""The altProvided property."""
return self.bit(113)
@altProvided.setter
def altProvided(self, value):
mask = ~(1 << 113)
self.value = (self.value & mask) | (value << 113)
@property
def altProvider(self):
"""The altProvider property."""
return self.bits(112, 110)
@altProvider.setter
def altProvider(self, value):
mask = ~(0b111 << 110)
self.value = (self.value & mask) | (value << 110)
@property
def altDiffers(self):
"""The altDiffers property."""
return self.bit(109)
@altDiffers.setter
def altDiffers(self, value):
mask = ~(1 << 109)
self.value = (self.value & mask) | (value << 109)
@property
def providerU(self):
"""The providerU property."""
return self.bit(108)
@providerU.setter
def providerU(self, value):
mask = ~(1 << 108)
self.value = (self.value & mask) | (value << 108)
@property
def providerCtr(self):
"""The providerCtr property."""
return self.bits(107, 106)
@providerCtr.setter
def providerCtr(self, value):
mask = ~(0b11 << 106)
self.value = (self.value & mask) | (value << 106)
@property
def altProviderCtr(self):
"""The altProviderCtr property."""
return self.bits(105, 104)
@altProviderCtr.setter
def altProviderCtr(self, value):
mask = ~(0b11 << 104)
self.value = (self.value & mask) | (value << 104)
@property
def allocate_valid(self):
"""The allocate_valid property."""
return self.bit(103)
@allocate_valid.setter
def allocate_valid(self, value):
mask = ~(1 << 103)
self.value = (self.value & mask) | (value << 103)
@property
def allocate_bits(self):
"""The allocate_bits property."""
return self.bits(102, 100)
@allocate_bits.setter
def allocate_bits(self, value):
mask = ~(0b111 << 100)
self.value = (self.value & mask) | (value << 100)
@property
def providerTarget(self):
"""The providerTarget property."""
return self.bits(95, 50)
@providerTarget.setter
def providerTarget(self, value):
mask = ~(0X3FFFFFFFFFFFF << 50)
self.value = (self.value & mask) | (value << 50)
@property
def altProviderTarget(self):
"""The altProviderTarget property."""
return self.bits(49, 0)
@altProviderTarget.setter
def altProviderTarget(self, value):
mask = ~(0X3FFFFFFFFFFFF << 0)
self.value = (self.value & mask) | (value << 0)