Compare commits

..

16 Commits

Author SHA1 Message Date
mingyu shi 4204b942e7 final 2025-04-29 20:48:06 +08:00
mingyu shi 56962b77dd Merge remote-tracking branch 'upstream/main' 2025-03-23 19:32:34 +08:00
mingyu shi a5c34f6cfb update 2025-03-20 21:19:18 +08:00
mingyu shi f35f96a3ba Merge remote-tracking branch 'upstream/main' 2025-03-07 21:44:47 +08:00
mingyu shi 708c440ce3 delete some files 2025-02-28 15:40:27 +08:00
mingyu shi 502d823fc7 fix 2025-02-28 15:32:36 +08:00
mingyu shi 15fea20a5f fix 2025-02-28 15:31:25 +08:00
mingyu shi f68fd0ad55 Merge remote-tracking branch 'upstream/main' 2025-02-27 20:07:43 +08:00
mingyu shi 6b4e2f0680 优化细节 2025-02-27 20:06:30 +08:00
mingyu shi a7f82fe823 Merge remote-tracking branch 'upstream/main' 2025-02-26 23:20:09 +08:00
mingyu shi 380febef36 update 2025-02-24 00:36:31 +08:00
mingyu shi 2a701680c8 predecode 2025-02-22 22:27:39 +08:00
mingyu shi 23fcd1f1ba Merge remote-tracking branch 'upstream/main' 2025-02-22 20:08:19 +08:00
mingyu shi 554bbfcf43 predecode 2025-02-22 20:06:35 +08:00
mingyu shi 663d857694 f3predecoder 2025-02-20 22:48:28 +08:00
mingyu shi 5a01b8b026 test 2025-02-20 11:41:54 +08:00
388 changed files with 1062 additions and 43739 deletions

6
.gitignore vendored
View File

@ -15,9 +15,6 @@ rtl/*
documents/static/data/*
!documents/static/data/README.txt
# Auto-generated mapping file
.dirmap.autogen
# C extensions
*.so
@ -174,3 +171,6 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
.vscode/
.metals/

View File

@ -12,12 +12,6 @@ space:= $(empty) $(empty)
PROCESSED_DUTS := $(subst $(comma),$(space),$(strip $(DUTS)))
TIMESTAMP := $(shell date +'%Y-%m-%d %H:%M:%S,%3N')
CURDIR := $(abspath .)
INFO_PREFIX := [$(TIMESTAMP),$(CURDIR)/Makefile,INFO]
WARN_PREFIX := [$(TIMESTAMP),$(CURDIR)/Makefile,Warning]
all: rtl dut test_all
update_python_deps:
@ -42,41 +36,39 @@ check_all_dut:
test: check_dut
@python3 run.py --config $(CFG) $(KV) -- $(REPORT) -vs $(target) $(args)
check_dut: generate_dirmap
check_dut:
@if [ -n "$(target)" ]; then \
for t in $(target); do \
CLEANED_TARGET=$$(echo "$$t" | sed 's/\/$$//'); \
grep ".* --> .* --> $$CLEANED_TARGET$$" .dirmap.autogen | while read -r MATCHED_LINE; do \
MATCHED_LINE=$$(grep ".* --> .* --> $$CLEANED_TARGET" dir_map.f | head -1); \
if [ -n "$$MATCHED_LINE" ]; then \
DUT_NAME=$$(echo "$$MATCHED_LINE" | awk -F' --> ' '{print $$1}'); \
DUT_DIR=dut/$$(echo "$$MATCHED_LINE" | awk -F' --> ' '{print $$2}'); \
if [ ! -d "$$DUT_DIR" ]; then \
echo "$(INFO_PREFIX) Building missing DUT for target $$t: $$DUT_NAME"; \
$(MAKE) dut DUTS="$$DUT_NAME" NO_GEN_DIRMAP=1; \
echo "Building missing DUT for target $$t: $$DUT_NAME"; \
$(MAKE) dut DUTS="$$DUT_NAME"; \
fi; \
done; \
else \
echo "No DUT mapping found for target: $$t, skipping..." >&2; \
fi; \
done; \
fi
@rm -f .dirmap.autogen
dut: rtl $(if $(NO_GEN_DIRMAP),,generate_dirmap)
dut: rtl
@if [ "$(PROCESSED_DUTS)" = "*" ]; then \
$(MAKE) clean_dut; \
else \
for d in $(PROCESSED_DUTS); do \
dir=$$(awk -F' --> ' -v dut="$$d" '$$1 == dut {print $$2; exit}' .dirmap.autogen); \
dir=$$(awk -F' --> ' -v dut="$$d" '$$1 == dut {print $$2; exit}' dir_map.f); \
if [ -z "$$dir" ]; then \
echo "$(WARN_PREFIX) No mapping found for DUT: $$d in .dirmap.autogen, skipping deletion" >&2; \
echo "No mapping found for '$$d' in dir_map.f, skipping deletion" >&2; \
continue; \
fi; \
echo "$(INFO_PREFIX) Cleaning dut/$$dir"; \
echo "Cleaning dut/$$dir"; \
rm -rf "dut/$$dir"; \
done; \
fi
@python3 run.py --config $(CFG) --build $(DUTS) $(args)
@if [ -z "$(NO_GEN_DIRMAP)" ]; then rm -f .dirmap.autogen; fi
generate_dirmap:
@python3 -c "from comm.functions import generate_dirmap; generate_dirmap()"
rtl:
@python3 run.py --config $(CFG) --download-rtl $(args)

View File

@ -14,7 +14,6 @@
import os
import sys
import time
import base64
import re
@ -488,74 +487,3 @@ def get_all_rtl_files(top_module, cfg):
get_rtl_helper(top_module)
return list(module_path_map.values())
def generate_dirmap(scripts_dir="../scripts", output_file="../.dirmap.autogen"):
script_root = os.path.abspath(os.path.dirname(__file__))
scripts_dir = os.path.join(script_root, scripts_dir)
output_path = os.path.abspath(os.path.join(script_root, output_file))
script_files = [
f for f in os.listdir(scripts_dir)
if f.startswith("build_ut_") and f.endswith(".py")
]
with open(output_path, "w") as f_out:
for script_file in script_files:
dut_name = re.search(r"build_ut_(.*)\.py", script_file).group(1)
script_path = os.path.join(scripts_dir, script_file)
module_name = f"build_ut_{dut_name}"
spec = importlib.util.spec_from_file_location(module_name, script_path)
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)
if not hasattr(module, "get_metadata"):
warning(f"{script_file} has no get_metadata() function, skipping")
continue
try:
metadata = module.get_metadata()
dut_dir = metadata.get("dut_dir")
test_targets = metadata.get("test_targets", [])
except Exception as e:
warning(f"Failed to get metadata from {script_file}: {str(e)}")
continue
if not dut_dir or not test_targets:
warning(f"{script_file} has invalid metadata (missing dut_dir or test_targets)")
continue
for target in test_targets:
f_out.write(f"{dut_name} --> {dut_dir} --> {target}\n")
def extract_signals(verilog_file, output_file):
# 定义匹配 wire 和 reg 的正则表达式
signal_pattern = re.compile(r'\b(wire|reg)\b\s*(\[[^\]]+\])?\s*([\w, ]+)(;|=)')
extracted_signals = []
# 读取 sv 文件内容
with open(verilog_file, 'r') as file:
lines = file.readlines()
# 逐行解析
for line in lines:
match = signal_pattern.search(line)
if match:
signal_type = match.group(1) # wire or reg
width = match.group(2) if match.group(2) else "" # [8:0] or empty
names = match.group(3) # 信号名
if signal_type == "reg":
signal_type = "logic"
# 分解信号名并格式化
if width=="" :
for name in names.split(','):
extracted_signals.append(f" - \"{signal_type} {name.strip()}\"")
else :
for name in names.split(','):
extracted_signals.append(f" - \"{signal_type} {width.strip()} {name.strip()}\"")
# 写入到 yaml 文件
basename = os.path.basename(verilog_file)
filename = os.path.splitext(basename)[0]
with open(output_file, 'w') as file:
file.write(filename + ':\n')
for signal in extracted_signals:
file.write(signal + '\n')

View File

@ -48,31 +48,7 @@ children:
priority: high
- name: "icache"
desc: "指令缓存 (Instruction Cache)"
children:
- name: "iprefetchpipe"
desc: "预取指模块"
meta:
doc_url: "https://open-verify.cc/UnityChipForXiangShan/docs/98_ut/01_frontend/04_icache/01_iprefetchpipe/"
- name: "mainpipe"
desc: "取指模块"
meta:
doc_url: "https://open-verify.cc/UnityChipForXiangShan/docs/98_ut/01_frontend/04_icache/02_mainpipe/"
- name: "waylookup"
desc: "元数据缓冲队列"
meta:
doc_url: "https://open-verify.cc/UnityChipForXiangShan/docs/98_ut/01_frontend/04_icache/03_waylookup/"
- name: "missunit"
desc: "缺失处理单元"
meta:
doc_url: "https://open-verify.cc/UnityChipForXiangShan/docs/98_ut/01_frontend/04_icache/04_missunit/"
- name: "ctrlunit"
desc: "控制单元"
meta:
doc_url: "https://open-verify.cc/UnityChipForXiangShan/docs/98_ut/01_frontend/04_icache/05_ctrlunit/"
- name: "icache"
desc: "icache顶层模块"
meta:
doc_url: "https://open-verify.cc/UnityChipForXiangShan/docs/98_ut/01_frontend/04_icache/06_icache/"
priority: high
- name: "ifu"
desc: "指令单元 (Instruction Fetch Unit)"
children:
@ -205,16 +181,23 @@ children:
desc: "Load/Store队列"
priority: critical
children:
- name: "virtual_load_queue"
desc: "虚拟Load队列"
- name: "rar_queue"
desc: "RAR队列"
- name: "raw_queue"
desc: "RAW队列"
- name: "replay_queue"
desc: "重发队列"
- name: "uncache_queue"
desc: "非缓存队列"
- name: "load_queue"
desc: "Load队列"
- name: "load_queue"
desc: "Load队列"
children:
- name: "virtual_load_queue"
desc: "虚拟Load队列"
- name: "rar_queue"
desc: "RAR队列"
- name: "raw_queue"
desc: "RAW队列"
- name: "replay_queue"
desc: "重发队列"
- name: "uncache_queue"
desc: "非缓存队列"
- name: "exception_queue"
desc: "异常队列"
- name: "store_queue"
desc: "Store队列"
- name: "dtlb"

15
dir_map.f Normal file
View File

@ -0,0 +1,15 @@
backend_ctrl_block_decode --> DecodeStage --> ut_backend/ctrl_block/decode
frontend_bpu_ittage --> ITTage --> ut_frontend/bpu/ittage
frontend_bpu_tagesc --> Tage_SC --> ut_frontend/bpu/tagesc
frontend_ifu_f3predecoder --> F3Predecoder --> ut_frontend/ifu/f3predecoder
frontend_ifu_frontend_trigger --> FrontendTrigger --> ut_frontend/ifu/frontend_trigger
frontend_ifu_pred_checker --> PredChecker --> ut_frontend/ifu/pred_checker
frontend_ifu_predecode --> PreDecode --> ut_frontend/ifu/predecode
frontend_ifu_rvc_expander --> RVCExpander --> ut_frontend/ifu/rvc_expander
frontend_ifu_top --> NewIFU -->
frontend_itlb --> TLB --> ut_frontend/itlb/classical_version
frontend_itlb --> TLB --> ut_frontend/itlb/toffee_version
frontend_tlb_fa --> TLBFA --> ut_frontend/itlb/submodules/TLBFA
frontend_tlb_nonblock --> TLBNonBlock --> ut_frontend/itlb/submodules/TLBNonBlock
frontend_tlb_storage_wrapper --> TlbStorageWrapper --> ut_frontend/itlb/submodules/TlbStorageWrapper
frontend_tlbuffer --> TLBuffer --> ut_frontend/itlb/submodules/TLBuffer

View File

@ -4,7 +4,7 @@
init:
curl -sL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
sudo pip3 install hugo==0.145.0
sudo pip3 install hugo==0.124.1
sudo add-apt-repository ppa:longsleep/golang-backports
sudo apt update
sudo apt install golang-go

View File

@ -1,36 +0,0 @@
---
title: Target Verification Units
linkTitle: Target Verification Units
#menu: {main: {weight: 20}}
weight: 12
---
<script src="../../../js/echarts.min.js"></script>
<script src="../../../js/chart_datatree.js"></script>
<script>
function update_dut_charts(data_url){
show_datatree_chart("datatree_chart", data_url)
}
</script>
<br>
<div id="datatree_chart" style="width: 90%;height:800px;"></div>
<div style="text-align: center; width: 100%;">
{{<list-report baseurl="../../../data/reports" label="Current Version:" detail="View Test Report" id="dut" onchange="update_dut_charts">}}
</div>
<br>
In the above chart, there are a total of <em id="em_id_report_dut_total">-</em> modules. By default, modules are gray. When the number of test cases in a module exceeds <em id="em_id_report_dut_min_light">-</em>, the module is fully lit. Currently, <em id="em_id_report_dut_lighted">-</em> modules are fully lit, and <em id="em_id_report_dut_lighted_no">-</em> modules are yet to be lit.
#### Overview of General Processor Modules
High-performance processors are the core of modern computing devices. They usually consist of three main parts: the frontend, the backend, and the memory subsystem. These parts work together to ensure the processor can efficiently execute complex computational tasks.
- **Frontend**: The frontend, also known as the instruction fetch and decode stage, is responsible for fetching instructions from memory and decoding them into a format the processor can understand. This stage is critical to processor performance because it directly affects how quickly the processor can start executing instructions. The frontend typically includes an instruction cache, branch predictor, and instruction decoder. The instruction cache stores recently accessed instructions to reduce accesses to main memory, thus improving speed. The branch predictor tries to predict conditional branches in the program to fetch and decode subsequent instructions in advance, reducing the time spent waiting for branch results.
- **Backend**: The backend, also known as the execution stage, is where the processor actually executes instructions. This stage includes the Arithmetic Logic Unit (ALU), Floating Point Unit (FPU), and various execution units. These units handle arithmetic operations, logic operations, data transfers, and other processor operations. The backend design is usually very complex because it needs to support multiple instruction set architectures (ISA) and optimize performance. To improve efficiency, modern processors often use superscalar architectures, meaning they can execute multiple instructions simultaneously.
- **Memory Subsystem**: The memory subsystem is the bridge between the processor and memory. It includes data caches, memory controllers, and cache coherence protocols. Data caches store data frequently accessed by the processor to reduce accesses to main memory. The memory controller manages data transfers between the processor and memory. Cache coherence protocols ensure that in multiprocessor systems, all processors see a consistent memory state.
Designing high-performance processors requires balancing these three parts to achieve optimal performance. This often involves complex microarchitecture design and pipeline optimization.

View File

@ -1,96 +0,0 @@
---
title: Prepare Verification Environment
linkTitle: Prepare Verification Environment
#menu: {main: {weight: 20}}
weight: 13
---
#### Basic Environment Requirements
This project uses the `Python` programming language for UT verification, with [picker](https://github.com/XS-MLVP/picker) and [toffee](https://github.com/XS-MLVP/toffee) as the main tools and test frameworks. **Environment requirements** are as follows:
1. Linux operating system. It is recommended to install Ubuntu 22.04 under WSL2.
1. Python. Python 3.11 is recommended.
1. picker. Install the latest version as instructed in the [Quick Start](https://open-verify.cc/mlvp/docs/quick-start/installer/).
1. toffee. It will be installed automatically later. You can also manually install the latest version as instructed in the [Quick Start](https://open-verify.cc/mlvp/docs/mlvp/quick-start/).
1. lcov. Used for report generation in the test stage. Install via package manager: `sudo apt install lcov`
**After environment setup**, clone the repository:
```bash
git clone https://github.com/XS-MLVP/UnityChipForXiangShan.git
cd UnityChipForXiangShan
pip3 install -r requirements.txt # Install python dependencies (e.g., toffee)
```
#### Download RTL Code
By default, download from the repository [https://github.com/XS-MLVP/UnityChipXiangShanRTLs](https://github.com/XS-MLVP/UnityChipXiangShanRTLs). Users can also generate RTL by compiling according to the XiangShan documentation.
```bash
make rtl # This command downloads the latest rtl code, unpacks it to the rtl directory, and creates a symlink
```
You can specify the rtl version to download with the following command:
```bash
make rtl args="rtl.version='openxiangshan-kmh-fad7803d-24120901'"
```
All RTL download packages can be found at [UnityChipXiangShanRTLs](https://github.com/XS-MLVP/UnityChipXiangShanRTLs).
The naming convention for RTL archives is: `name-microarchitecture-GitTag-date.tar.gz`, for example, `openxiangshan-kmh-97e37a2237-24092701.tar.gz`. When used, the repository code will filter out the git tag and suffix, so the version accessed via cfg.rtl.version is: `openxiangshan-kmh-24092701`. The directory structure inside the archive is:
```bash
openxiangshan-kmh-97e37a2237-24092701.tar.gz
└── rtl # directory
|-- *.sv # all sv files
`-- *.v # all v files
```
#### Compile DUT
The purpose of this process is to package the RTL into a Python module using the picker tool. You can specify the DUT to be packaged via the make command, or package all DUTs at once.
If you want to package a specific dut yourself, you need to create a script named build_ut_<name>.py in the scripts directory. This script must implement a build method, which will be called automatically during packaging. There is also a line_coverage_files method for specifying files used for line coverage reference.
Picker's packaging supports adding internal signals; See the --internal parameter of picker and pass a custom yaml.
```bash
# Calls the build method in scripts/build_ut_<name>.py to create the Python DUT to be verified
make dut DUTS=<name> # If there are multiple DUTS, separate them with commas. Wildcards are supported. The default value is "*", which compiles all DUTs.
# Example:
make dut DUTS=backend_ctrl_block_decode
```
For example, after running `make dut DUTS=backend_ctrl_block_decode`, the corresponding Python package will be generated in the dut directory:
```
dut/
├── __init__.py
├── DecodeStage
├── Predecode
└── RVCExpander
```
After conversion, you can import the corresponding DUT in your test case code, for example:
```python
from dut.PreDecode import DUTPreDecode
dut = DUTPreDecode()
```
#### Edit Configuration
When running rtl, dut, test, and other commands, the default configuration is used from configs/_default.yaml.
Of course, you can also use a custom configuration as follows:
```bash
# Specify a custom CFG file
make CFG=path/to/your_cfg.yaml
```
Similarly, you can specify key-value pairs directly on the command line. Currently, only the test-related stage supports command-line configuration key-value pairs:
```bash
# Specify KV, pass command-line arguments, separate key-value pairs with spaces
make test KV="log.term-level='debug' test.skip-tags=['RARELY_USED']"
```

View File

@ -1,34 +0,0 @@
---
title: Run Tests
linkTitle: Run Tests
#menu: {main: {weight: 20}}
weight: 14
---
This project uses the PyTest testing framework for verification. When running tests, the PyTest framework automatically searches for all `test_*.py` files and executes all test cases that start with `test_`.
```bash
# Run all test cases in ut_* directories
make test_all
# Run test cases in the specified directory
make test target=<dir>
# For example, run all test cases in the ut_backend/ctrl_block/decode directory
make test target=ut_backend/ctrl_block/decode
```
You can pass Pytest runtime parameters via the `args` parameter, such as enabling the x-dist plugin for multi-core execution:
```bash
make test args="-n 4" # Use 4 processes
make test args="-n auto" # Let the framework automatically choose the number of processes
```
*Note: x-dist can run tests concurrently on multiple nodes. See its [documentation](https://pytest-xdist.readthedocs.io/en/stable/remote.html) for details.
After running, an HTML version of the test report will be generated by default in the `out/report` directory. The HTML file can be opened directly in a browser (it is recommended to install the `Open In Default Browser` plugin in VS Code IDE).
Running tests mainly completes the following three parts:
1. Run Test Cases as required, which can be configured via options in `cfg.tests`
1. Collect test results and output test reports. The toffee-report tool automatically generates (a total test report, merging results of all tests)
1. Further data statistics on the test report as needed (`cfg.doc_result.disable = True`)

View File

@ -1,128 +0,0 @@
---
title: Add Compilation Script
linkTitle: Add Compilation Script
#menu: {main: {weight: 20}}
weight: 2
---
## Script Target
Write a compilation file for the corresponding RTL in the `scripts` directory using Python (e.g., `build_ut_frontend_ifu_rvc_expander.py`).
The goal of this script is to provide RTL-to-Python DUT compilation, target coverage files, and custom functionality.
## Creation Process
### Determine File Name
Select the UT to be verified in [XiangShan Kunming Lake DUT Verification Progress](). If it is not available or needs further refinement, you can manually add it by editing `configs/dutree/xiangshan-kmh.yaml`.
For example, if we want to verify the `rvc_expander` module under the `ifu` module in the frontend, we need to add the corresponding part to `configs/dutree/xiangshan-kmh.yaml` (this module already exists in the YAML file; this is just an example):
```yaml
name: "kmh_dut"
desc: "All Kunming Lake DUTs"
children:
- name: "frontend"
desc: "Frontend Module"
children:
- name: "ifu"
desc: "Instruction Fetch Unit"
children:
- name: "rvc_expander"
desc: "RVC Instruction Expander"
```
The naming format for the script file is as follows:
```bash
scripts/build_<top_module>_<sub_module>_..._<target_module>.py
```
Currently, the project includes four top-level modules:
1. ut_frontend (Frontend)
1. ut_backend (Backend)
1. ut_mem_block (Memory Access)
1. ut_misc (Miscellaneous)
Submodules do not have the `ut_` prefix (the top-level directories have this prefix to distinguish them from other directories).
For example, if the target DUT to be verified is the `rvc_expander` module:
This module belongs to the frontend, so the top-level module is `ut_frontend`. Its submodule is `ifu`, and the target module is `rvc_expander`.
From the previously opened `yaml` file, we can also see that the `children` of `frontend` is `ifu`, and the `children` of `ifu` is `rvc_expander`.
Thus, the script name to be created is `build_ut_frontend_ifu_rvc_expander.py`.
### Write the `build(cfg) -> bool` Function
The `build` function is defined as follows:
```python
def build(cfg) -> bool:
"""Compile DUT
Args:
cfg: Runtime configuration, which can be used to access configuration items, e.g., cfg.rtl.version
Return:
Returns True or False, indicating whether the function achieved its intended goal
"""
```
The `build` function is called during `make dut`. Its main purpose is to convert the target RTL into a Python module. Other necessary processes, such as compiling dependencies, can also be added. For example, in `build_ut_frontend_ifu_rvc_expander.py`, the function primarily performs RTL checks, DUT checks, RTL compilation, and disasm dependency compilation:
```python
import os
from comm import warning, info
def build(cfg):
# Import related dependencies
from toffee_test.markers import match_version
from comm import is_all_file_exist, get_rtl_dir, exe_cmd, get_root_dir
# Check RTL version (an empty version parameter means all versions are supported)
if not match_version(cfg.rtl.version, "openxiangshan-kmh-*"):
warning("ifu frontend rvc expander: %s" % f"Unsupported RTL version {cfg.rtl.version}")
return False
# Check if the target file exists in the current RTL
f = is_all_file_exist(["rtl/RVCExpander.sv"], get_rtl_dir(cfg=cfg))
assert f is True, f"File {f} not found"
# If the DUT does not contain RVCExpander, use picker to package it into Python
if not os.path.exists(get_root_dir("dut/RVCExpander")):
info("Exporting RVCExpander.sv")
s, out, err = exe_cmd(f'picker export --cp_lib false {get_rtl_dir("rtl/RVCExpander.sv", cfg=cfg)} --lang python --tdir {get_root_dir("dut")}/ -w rvc.fst -c')
assert s, "Failed to export RVCExpander.sv: %s\n%s" % (out, err)
# If disasm/build does not exist in tools, compile disasm
if not os.path.exists(get_root_dir("tools/disasm/build")):
info("Building disasm")
s, _, _ = exe_cmd("make -C %s" % get_root_dir("tools/disasm"))
assert s, "Failed to build disasm"
# Compilation successful
return True
def line_coverage_files(cfg):
return ["RVCExpander.v"]
```
For details on how to use `picker`, refer to its [documentation](https://github.com/XS-MLVP/picker/blob/master/README.zh.md) and [usage guide](https://open-verify.cc/mlvp/docs/env_usage/picker_usage/).
In the `scripts` directory, you can create subdirectories to store files needed for UT verification. For example, the `rvc_expander` module creates a `scripts/frontend_ifu_rvc_expander` directory, where `rtl_file.f` specifies the input RTL file, and `line_coverage.ignore` stores lines of code to be ignored in coverage statistics. Custom directory names should be reasonable and should indicate the module and file they belong to.
### Write the `line_coverage_files(cfg) -> list[str]` Function
The `line_coverage_files` function is defined as follows:
```python
def line_coverage_files(cfg) -> list[str]:
"""Specify files to be covered
Args:
cfg: Runtime configuration, which can be used to access configuration items, e.g., cfg.rtl.version
Return:
Returns the names of RTL files targeted for line coverage statistics
"""
```
In the `build_ut_frontend_ifu_rvc_expander.py` file, the `line_coverage_files` function is defined as follows:
```python
def line_coverage_files(cfg):
return ["RVCExpander.v"]
```
This indicates that the module focuses on coverage for the `RVCExpander.v` file. If you want to enable test result processing, set `disable=False` under `doc-result` in `configs/_default.yaml` (the default parameter is `False`, meaning it is enabled). If you do not enable test result processing (`disable=True`), the above function will not be called.

View File

@ -1,350 +0,0 @@
---
title: Build Test Environment
linkTitle: Build Test Environment
#menu: {main: {weight: 20}}
weight: 3
---
## Determine Directory Structure
The directory structure of the Unit Test (UT) should match its naming convention. For example, `frontend.ifu.rvc_expander` should be located in the `ut_frontend/ifu/rvc_expander` directory, and each directory level must include an `__init__.py` file to enable Python imports.
**The file for this chapter is `your_module_wrapper.py`** (if your module is `rvc_expander`, the file would be `rvc_expander_wrapper.py`).
A wrapper is essentially a layer of abstraction that encapsulates the methods needed for testing into APIs decoupled from the DUT. These APIs are then used in test cases.
\*Note: Decoupling ensures that test cases are independent of the DUT, allowing them to be written and debugged without needing to know the DUT's implementation details. For more information, refer to [Decoupling Verification Code from the DUT](https://open-verify.cc/mlvp/docs/mlvp/canonical_env/#%E5%B0%86%E9%AA%8C%E8%AF%81%E4%BB%A3%E7%A0%81%E4%B8%8Edut%E8%BF%9B%E8%A1%8C%E8%A7%A3%E8%80%A6).
This file should be placed in the `ut_frontend_or_backend/top_module/your_module/env` directory. For example, if `rvc_expander` belongs to the frontend, its top-level directory should be `ut_frontend`. The next-level directory would be `ifu`, followed by `rvc_expander`. Since we are **building the test environment**, an additional `env` directory is created. The full path would be: `ut_frontend_or_backend/top_module/your_module/env`.
```shell
ut_frontend/ifu/rvc_expander
├── classical_version
│   ├── env
│   │   ├── __init__.py
│   │   └── rvc_expander_wrapper.py
│   ├── __init__.py
│   └── test_rvc_expander.py
├── __init__.py
├── README.md
└── toffee_version
├── agent
│   └── __init__.py
├── bundle
│   └── __init__.py
├── env
│   ├── __init__.py
│   └── ref_rvc_expand.py
├── __init__.py
└── test
├── __init__.py
├── rvc_expander_fixture.py
└── test_rvc.py
```
In the `rvc_expander` directory, there are two versions: `classical_version` (traditional) and `toffee_version` (using Toffee).
The traditional version uses the `pytest` framework for testing, while the Toffee version leverages more features of the Toffee framework.
In general, **the traditional version is sufficient for most cases**, and the Toffee version is only needed when the traditional version cannot meet the requirements.
When building the test environment, **choose one version**.
The directory structure within a module (e.g., `rvc_expander`) is determined by the contributor. You do **not** need to create additional `classical_version` or `toffee_version` directories, but the structure must comply with Python standards and be logically and consistently named.
## Env Requirements
- Perform RTL version checks.
- The APIs provided by Env must be independent of pins and timing.
- The APIs provided by Env must be stable and should not undergo arbitrary changes in interfaces or return values.
- Define necessary fixtures.
- Initialize functional checkpoints (functional checkpoints can be independent modules).
- Perform coverage statistics.
- Include documentation.
## Building the Test Environment: Traditional Version
In the test environment for the UT verification module, the goal is to accomplish the following:
1. Encapsulate DUT functionality to provide stable APIs for testing.
2. Define functional coverage.
3. Define necessary fixtures for test cases.
4. Collect coverage statistics at appropriate times.
Taking the RVCExpander in the IFU environment as an example (`ut_frontend/ifu/rvc_expander/classical_version/env/rvc_expander_wrapper.py`):
### 1. DUT Encapsulation
The following content is located in `ut_frontend/ifu/rvc_expander/classical_version/env/rvc_expander_wrapper.py`.
```python
class RVCExpander(toffee.Bundle):
def __init__(self, cover_group, **kwargs):
super().__init__()
self.cover_group = cover_group
self.dut = DUTRVCExpander(**kwargs) # Create DUT
self.io = toffee.Bundle.from_prefix("io_", self.dut) # Bind pins using Bundle and prefix
self.bind(self.dut) # Bind Bundle to DUT
def expand(self, instr, fsIsOff):
self.io["in"].value = instr # Assign value to DUT pin
self.io["fsIsOff"].value = fsIsOff # Assign value to DUT pin
self.dut.RefreshComb() # Trigger combinational logic
self.cover_group.sample() # Collect functional coverage statistics
return self.io["out_bits"].value, self.io["ill"].value # Return result and illegal instruction flag
def stat(self): # Get current state
return {
"instr": self.io["in"].value, # Input instruction
"decode": self.io["out_bits"].value, # Decoded result
"illegal": self.io["ill"].value != 0, # Whether the input is illegal
}
```
In the example above, `class RVCExpander` encapsulates `DUTRVCExpander` and provides two APIs:
- `expand(instr: int, fsIsOff: bool) -> (int, int)`: Accepts an input instruction `instr` for decoding and returns `(result, illegal instruction flag)`. If the illegal instruction flag is non-zero, the input instruction is illegal.
- `stat() -> dict(instr, decode, illegal)`: Returns the current state, including the input instruction, decoded result, and illegal instruction flag.
These APIs **abstract away the DUT's pins**, exposing only general functionality to external programs.
### 2. Define Functional Coverage
Define functional coverage in the environment whenever possible. If necessary, coverage can also be defined in test cases. For details on defining functional coverage with Toffee, refer to [What is Functional Coverage](http://localhost:1313/docs/03_add_test/05_cover_func/). To establish a clear relationship between functional checkpoints and test cases, functional coverage definitions should be linked to test cases (reverse marking).
The following content is located in `ut_frontend/ifu/rvc_expander/classical_version/env/rvc_expander_wrapper.py`.
```python
import toffee.funcov as fc
# Create a functional coverage group
g = fc.CovGroup(UT_FCOV("../../../CLASSIC"))
def init_rvc_expander_funcov(expander, g: fc.CovGroup):
"""Add watch points to the RVCExpander module to collect functional coverage information"""
# 1. Add point RVC_EXPAND_RET to check expander return value:
# - bin ERROR: The instruction is not illegal
# - bin SUCCE: The instruction is not expanded
g.add_watch_point(expander, {
"ERROR": lambda x: x.stat()["illegal"] == False,
"SUCCE": lambda x: x.stat()["illegal"] != False,
}, name="RVC_EXPAND_RET")
...
# 5. Reverse mark functional coverage to the checkpoint
def _M(name):
# Get the module name
return module_name_with(name, "../../test_rv_decode")
# - Mark RVC_EXPAND_RET
g.mark_function("RVC_EXPAND_RET", _M(["test_rvc_expand_16bit_full",
"test_rvc_expand_32bit_full",
"test_rvc_expand_32bit_randomN"]), bin_name=["ERROR", "SUCCE"])
...
```
In the code above, a functional checkpoint named `RVC_EXPAND_RET` is added to check whether the `RVCExpander` module can return illegal instructions. The checkpoint requires both `ERROR` and `SUCCE` conditions to be met, meaning the `illegal` field in `stat()` must have both `True` and `False` values. After defining the checkpoint, the `mark_function` method is used to link it to the relevant test cases.
### 3. Define Necessary Fixtures
The following content is located in `ut_frontend/ifu/rvc_expander/classical_version/env/rvc_expander_wrapper.py`.
```python
version_check = get_version_checker("openxiangshan-kmh-*") # Specify the required RTL version
@pytest.fixture()
def rvc_expander(request):
version_check() # Perform version check
fname = request.node.name # Get the name of the test case using this fixture
wave_file = get_out_dir("decoder/rvc_expander_%s.fst" % fname) # Set waveform file path
coverage_file = get_out_dir("decoder/rvc_expander_%s.dat" % fname) # Set code coverage file path
coverage_dir = os.path.dirname(coverage_file)
os.makedirs(coverage_dir, exist_ok=True) # Create directory if it doesn't exist
expander = RVCExpander(g, coverage_filename=coverage_file, waveform_filename=wave_file)
# Create RVCExpander
expander.dut.io_in.AsImmWrite() # Set immediate write timing for io_in pin
expander.dut.io_fsIsOff.AsImmWrite() # Set immediate write timing for io_fsIsOff pin
init_rvc_expander_funcov(expander, g) # Initialize functional checkpoints
yield expander # Return the created RVCExpander to the test case
expander.dut.Finish() # End DUT after the test case is executed
set_line_coverage(request, coverage_file) # Report code coverage file to toffee-report
set_func_coverage(request, g) # Report functional coverage data to toffee-report
g.clear() # Clear functional coverage statistics
```
This fixture accomplishes the following:
1. Performs RTL version checks. If the version does not meet the `"openxiangshan-kmh-*"` requirement, the test case using this fixture is skipped.
2. Creates the DUT and specifies the paths for waveform and code coverage files (the paths include the name of the test case using the fixture: `fname`).
3. Calls `init_rvc_expander_funcov` to add functional coverage points.
4. Ends the DUT and processes code and functional coverage (sending them to `toffee-report` for processing).
5. Clears functional coverage statistics.
\*Note: In PyTest, before executing a test case like `test_A(rvc_expander, ...)`, (**rvc_expander is the method name we defined when we used the fixure decorator**), the part of `rvc_expander(request)` before the `yield` keyword will be automatically called and executed (which is equivalent to initialization). and then `rvc_expander` will be returned to call the `test_A` case via `yield` (**the object returned by yield is the method name we defined in our fixture of the test case**). After the execution of the case is completed, then continue to execute the part of the `fixture` after the `field` keyword. For example: refer to the following code of statistical coverage, the penultimate line of `rvc_expand(rvc_expander, generate_rvc_instructions(start, end))`, where `rvc_expander` is the name of the method that we defined in the `fixture`, that is, the `yield` return object.
### 4. Collect Coverage Statistics
The following content is located in `ut_frontend/ifu/rvc_expander/classical_version/test_rvc_expander.py`.
```python
N = 10
T = 1 << 16
@pytest.mark.toffee_tags(TAG_LONG_TIME_RUN)
@pytest.mark.parametrize("start,end",
[(r * (T // N), (r + 1) * (T // N) if r < N - 1 else T) for r in range(N)])
def test_rvc_expand_16bit_full(rvc_expander, start, end):
"""Test the RVC expand function with a full compressed instruction set
Description:
Perform an expand check on 16-bit compressed instructions within the range from 'start' to 'end'.
"""
# Add checkpoint: RVC_EXPAND_RANGE to check expander input range.
# When run to here, the range[start, end] is covered
covered = -1
g.add_watch_point(rvc_expander, {
"RANGE[%d-%d]" % (start, end): lambda _: covered == end
}, name="RVC_EXPAND_ALL_16B", dynamic_bin=True)
# Reverse mark function to the checkpoint
g.mark_function("RVC_EXPAND_ALL_16B", test_rvc_expand_16bit_full, bin_name="RANGE[%d-%d]" % (start, end))
# Drive the expander and check the result
rvc_expand(rvc_expander, generate_rvc_instructions(start, end))
# When go to here, the range[start, end] is covered
covered = end
g.sample() # Sample coverage
```
After defining coverage, it must be collected in the test cases. In the code above, a functional checkpoint `rvc_expander` is added in the test case using `add_watch_point`. The checkpoint is then marked and sampled. Coverage sampling triggers a callback function to evaluate the `bins` defined in `add_watch_point`. If any `bins`'s condition evaluates to `True`, it is counted as a `pass`.
## Building the Test Environment: Toffee Version
Testing with Python can be enhanced by using our open-source testing framework [Toffee](https://github.com/XS-MLVP/toffee).
The official Toffee tutorial can be found [here](https://open-verify.cc/mlvp/docs/mlvp/).
### Bundle: Quick DUT Encapsulation
Toffee uses Bundles to bind to DUTs. It provides multiple methods for establishing Bundle-to-DUT bindings. Relevant code can be found in `ut_frontend/ifu/rvc_expander/toffee_version/bundle`.
#### Manual Binding
In the Toffee framework, the lowest-level class supporting pin binding is `Signal`, which binds to DUT pins using name matching. For example, consider the simplest RVCExpander with the following I/O pins:
```verilog
module RVCExpander(
input [31:0] io_in,
input io_fsIsOff,
output [31:0] io_out_bits,
output io_ill
);
```
There are four signals: `io_in`, `io_fsIsOff`, `io_out_bits`, and `io_ill`. A common prefix, such as `io_`, can be extracted (note that `in` cannot be used directly as a variable name in Python). The remaining parts can be defined as pin names in the corresponding Bundle class:
```python
class RVCExpanderIOBundle(Bundle):
_in, _fsIsOff, _out_bits, _ill = Signals(4)
```
In a higher-level Env or Bundle, the `from_prefix` method can be used to complete the prefix binding:
```python
self.agent = RVCExpanderAgent(RVCExpanderIOBundle.from_prefix("io").bind(dut))
```
#### Automatic Bundle Definition
The Bundle class definition can also be omitted by using prefix binding:
```python
self.io = toffee.Bundle.from_prefix("io_", self.dut) # Bind pins using Bundle and prefix
self.bind(self.dut)
```
If the `from_prefix` method is passed a DUT, it automatically generates pin definitions based on the prefix and DUT pin names. Accessing the pins can then be done using a dictionary-like approach:
```python
self.io["in"].value = instr
self.io["fsIsOff"].value = False
```
#### Bundle Code Generation
The Toffee framework's [scripts](https://github.com/XS-MLVP/toffee/tree/master/scripts) provide two scripts.
The `bundle_code_gen.py` script offers three methods:
```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)
```
These methods generate Bundle code by passing in a DUT and generation rules (dict, prefix, or regex).
The `bundle_code_intel_gen.py` script parses the `signals.json` file generated by Picker to automatically generate hierarchical Bundle code. It can be invoked from the command line:
```bash
python bundle_code_intel_gen.py [signal] [target]
```
If you encounter bugs in the auto-generation scripts, feel free to submit an issue for us to fix.
### Agent: Driving Methods
If Bundles abstract the data responsibilities of a DUT, Agents encapsulate its behavioral responsibilities into interfaces. Simply put, an Agent provides multiple methods that abstract groups of I/O operations into specific behaviors:
```python
class RVCExpanderAgent(Agent):
def __init__(self, bundle: RVCExpanderIOBundle):
super().__init__(bundle)
self.bundle = bundle
@driver_method()
async def expand(self, instr, fsIsOff): # Accepts RVC instruction and fs.status enable flag
self.bundle._in.value = instr # Assign value to pin
self.bundle._fsIsOff.value = fsIsOff # Assign value to pin
await self.bundle.step() # Trigger clock
return self.bundle._out_bits.value, # Return expanded instruction
self.bundle._ill.value # Return legality check
```
For example, the RVCExpander's instruction expansion function accepts an input instruction (which could be an RVI or RVC instruction) and the CSR's enable flag for `fs.status`. This functionality is abstracted into the `expand` method, which takes two parameters in addition to `self`. The method returns the corresponding RVI instruction and a legality check for the input instruction.
### Env: Test Environment
```python
class RVCExpanderEnv(Env):
def __init__(self, dut: DUTRVCExpander):
super().__init__()
dut.io_in.xdata.AsImmWrite()
dut.io_fsIsOff.xdata.AsImmWrite() # Set pin write timing
self.agent = RVCExpanderAgent(RVCExpanderIOBundle.from_prefix("io").bind(dut)) # Complete prefix and bind DUT
```
### Coverage Definition
The method for defining coverage groups is similar to the one described earlier and will not be repeated here.
### Test Suite Definition
The definition of test suites differs slightly:
```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
```
Due to Toffee's more powerful coverage management features, manual line coverage settings are not needed. Additionally, because of Toffee's clock mechanism, it is recommended to check if all tasks have ended at the end of the suite code.

View File

@ -1,148 +0,0 @@
---
title: Add Test Cases
linkTitle: Add Test Cases
#menu: {main: {weight: 20}}
weight: 4
---
## Naming Requirements
All test case files should be named in the format `test_*.py`, where `*` is replaced with the test target (e.g., `test_rvc_expander.py`). All test cases should also start with the `test_` prefix. The test case names must have clear and meaningful descriptions.
Examples of naming:
```python
def test_a(): # Not acceptable, as "a" does not indicate the test target
pass
def test_rvc_expand_16bit_full(): # Acceptable, as the name indicates the test content
pass
```
## Using Assert
Each test case must use `assert` to determine whether the test passes.
`pytest` relies on the results of `assert` statements, so these statements must ensure correctness.
The following content is located in `ut_frontend/ifu/rvc_expander/classical_version/test_rvc_expander.py`:
```python
def rvc_expand(rvc_expander, ref_insts, is_32bit=False, fsIsOff=False):
"""Compare the RVC expand result with the reference
Args:
rvc_expander (wrapper): the fixture of the RVC expander
ref_insts (list[int]): the reference instruction list
"""
find_error = 0
for insn in ref_insts:
insn_disasm = disasmbly(insn)
value, instr_ex = rvc_expander.expand(insn, fsIsOff)
if is_32bit:
assert value == insn, "RVC expand error, 32-bit instruction must remain unchanged"
if (insn_disasm == "unknown") and (instr_ex == 0):
debug(f"Found bad instruction: {insn}, ref: 1, dut: 0")
find_error += 1
elif (insn_disasm != "unknown") and (instr_ex == 1):
if (instr_filter(insn_disasm) != 1):
debug(f"Found bad instruction: {insn}, disasm: {insn_disasm}, ref: 0, dut: 1")
find_error += 1
assert find_error == 0, f"RVC expand error ({find_error} errors)"
```
## Writing Comments
Each test case must include necessary explanations and comments, adhering to the [Python Docstring Conventions](https://peps.python.org/pep-0257/).
Example format for test case documentation:
```python
def test_<name>(a: type_a, b: type_b):
"""Test abstract
Args:
a (type_a): Description of argument a.
b (type_b): Description of argument b.
Detailed test description here (if needed).
"""
...
```
## Test Case Management
To facilitate test case management, use the `@pytest.mark.toffee_tags` tag feature provided by `toffee-test`. Refer to the [Other](https://open-verify.cc/UnityChipForXiangShan/docs/98_others/) section of this site and the [toffee-test documentation](https://github.com/XS-MLVP/toffee-test/blob/master/README_zh.md#%E7%AE%A1%E7%90%86%E6%B5%8B%E8%AF%95%E7%94%A8%E4%BE%8B%E8%B5%84%E6%BA%90).
## Reference Test Cases
If many test cases share the same operations, the common parts can be extracted into a utility function. For example, in RVCExpander verification, the comparison of compressed instruction expansion with the reference model (`disasm`) can be encapsulated into the following function:
The following content is located in `ut_frontend/ifu/rvc_expander/classical_version/test_rvc_expander.py`:
```python
def rvc_expand(rvc_expander, ref_insts, is_32bit=False, fsIsOff=False):
"""Compare the RVC expand result with the reference
Args:
rvc_expander (wrapper): the fixture of the RVC expander
ref_insts (list[int]): the reference instruction list
"""
find_error = 0
for insn in ref_insts:
insn_disasm = disasmbly(insn)
value, instr_ex = rvc_expander.expand(insn, fsIsOff)
if is_32bit:
assert value == insn, "RVC expand error, 32-bit instruction must remain unchanged"
if (insn_disasm == "unknown") and (instr_ex == 0):
debug(f"Found bad instruction: {insn}, ref: 1, dut: 0")
find_error += 1
elif (insn_disasm != "unknown") and (instr_ex == 1):
if (instr_filter(insn_disasm) != 1):
debug(f"Found bad instruction: {insn}, disasm: {insn_disasm}, ref: 0, dut: 1")
find_error += 1
assert find_error == 0, f"RVC expand error ({find_error} errors)"
```
The above utility function includes `assert` statements, so the test cases calling this function can also rely on these assertions to determine the results.
During test case development, debugging is often required. To quickly set up the verification environment, "smoke tests" can be written for debugging. For example, a smoke test for expanding 16-bit compressed instructions in RVCExpander is as follows:
```python
@pytest.mark.toffee_tags(TAG_SMOKE)
def test_rvc_expand_16bit_smoke(rvc_expander):
"""Test the RVC expand function with 1 compressed instruction"""
rvc_expand(rvc_expander, generate_rvc_instructions(start=100, end=101))
```
For easier management, the above test case is tagged with the `SMOKE` label using `toffee_tags`. Its input parameter is `rvc_expander`, which will automatically invoke the corresponding `fixture` with the same name during runtime.
The goal of testing 16-bit compressed instructions in RVCExpander is to traverse all 2^16 compressed instructions and verify that all cases match the reference model (`disasm`). If a single test is used for traversal, it would take a significant amount of time. To address this, we can use `pytest`'s `parametrize` feature to configure test parameters and execute them in parallel using the `pytest-xdist` plugin:
The following content is located in `ut_frontend/ifu/rvc_expander/classical_version/test_rvc_expander.py`:
```python
N = 10
T = 1 << 16
@pytest.mark.toffee_tags(TAG_LONG_TIME_RUN)
@pytest.mark.parametrize("start,end",
[(r * (T // N), (r + 1) * (T // N) if r < N - 1 else T) for r in range(N)])
def test_rvc_expand_16bit_full(rvc_expander, start, end):
"""Test the RVC expand function with a full compressed instruction set
Description:
Perform an expand check on 16-bit compressed instructions within the range from 'start' to 'end'.
"""
# Add checkpoint: RVC_EXPAND_RANGE to check expander input range.
# When run to here, the range [start, end] is covered
g.add_watch_point(rvc_expander, {
"RANGE[%d-%d]" % (start, end): lambda _: True
}, name="RVC_EXPAND_ALL_16B").sample()
# Reverse mark function to the checkpoint
g.mark_function("RVC_EXPAND_ALL_16B", test_rvc_expand_16bit_full, bin_name="RANGE[%d-%d]" % (start, end))
# Drive the expander and check the result
rvc_expand(rvc_expander, generate_rvc_instructions(start, end))
```
In the above test case, the parameters `start` and `end` are defined to specify the range of compressed instructions. These parameters are grouped and assigned using the `@pytest.mark.parametrize` decorator. The variable `N` specifies the number of groups for the target data, with a default of 10 groups. During runtime, the test case `test_rvc_expand_16bit_full` will expand into 10 test cases, such as `test_rvc_expand_16bit_full[0-6553]` to `test_rvc_expand_16bit_full[58977-65536]`.

View File

@ -1,131 +0,0 @@
---
title: Code Coverage
linkTitle: Code Coverage
#menu: {main: {weight: 20}}
weight: 5
---
Code coverage is a metric that measures which parts of the tested code have been executed and which parts have not. By analyzing code coverage, the effectiveness and thoroughness of testing can be evaluated.
Code coverage includes:
- **Line Coverage**: The number of lines executed in the tested code. This is the simplest metric, and the goal is usually 100%.
- **Branch Coverage**: Whether each branch of every control structure has been executed. For example, in an `if` statement, have both the `true` and `false` branches been executed?
- **FSM Coverage**: Whether all states of a finite state machine have been reached.
- **Toggle Coverage**: Tracks the toggling of signals in the tested code, ensuring that every circuit node has both `0 -> 1` and `1 -> 0` transitions.
- **Path Coverage**: Examines the coverage of paths. In `always` or `initial` blocks, `if ... else` and `case` statements can create various data paths in the circuit structure.
\* The primary simulator used in this project is Verilator, with a focus on **line coverage**. Verilator supports coverage statistics, so when building the DUT, the `-c` option must be added to the compilation options to enable coverage statistics.
## Relevant Locations in This Project
To enable coverage, the `-c` option must be added during compilation (when using the `picker` command). Refer to the [Picker Parameter Explanation](https://github.com/XS-MLVP/picker/blob/master/README.zh.md#%E5%8F%82%E6%95%B0%E8%A7%A3%E9%87%8A). Additionally, the line coverage function must be implemented and enabled in the test files to generate coverage statistics during Toffee testing.
In conjunction with the above description, code coverage will be involved when compiling, writing and enabling line coverage functions and tests in this project:
### Adding Compilation Scripts
[Write the `build(cfg) -> bool` Function](01_build_script.md#write-the-buildcfg---bool-function)
```python
# Omitted earlier code
if not os.path.exists(get_root_dir("dut/RVCExpander")):
info("Exporting RVCExpander.sv")
s, out, err = exe_cmd(f'picker export --cp_lib false {get_rtl_dir("rtl/RVCExpander.sv", cfg=cfg)
} --lang python --tdir {get_root_dir("dut")}/ -w rvc.fst -c')
assert s, "Failed to export RVCExpander.sv: %s\n%s" % (out, err)
# Omitted later code
```
In the line `s, out, err=...`, the `picker` command is used with the `-c` option to enable code coverage.
[Set Target Coverage Files (`line_coverage_files` Function)](01_build_script.md#write-the-line_coverage_filescfg---liststr-function)
Write the `line_coverage_files(cfg) -> list[str]` function as needed, and enable test result processing (`doc_result.disable = False`) to ensure it is invoked.
### Building the Test Environment
[Define Necessary Fixtures](02_build_env.md#3-define-necessary-fixtures)
```python
set_line_coverage(request, coverage_file) # Pass the generated code coverage file to toffee-report
```
Use the `toffee-test.set_line_coverage` function to pass the coverage file to Toffee-Test, enabling it to collect data for generating reports with line coverage.
## Ignoring Specific Statistics
Sometimes, certain parts of the code may need to be excluded from coverage statistics. For example, some parts may not need to be tested, or it may be normal for certain parts to remain uncovered. Ignoring these parts can help optimize coverage reports or assist in debugging. Our framework supports two methods for ignoring coverage:
### 1. Using Verilator to Specify Ignored Sections
#### Using `verilator_coverage_off/on` Directives
Verilator supports ignoring specific code sections from coverage statistics using comment directives. For example:
```verilog
// *verilator coverage_off*
// Code section to ignore
...
// *verilator coverage_on*
```
Example:
```verilog
module example;
always @(posedge clk) begin
// *verilator coverage_off*
if (debug_signal) begin
$display("This is for debugging only");
end
// *verilator coverage_on*
if (enable) begin
do_something();
end
end
endmodule
```
In the above example, the `debug_signal` section will not be included in coverage statistics, while the `enable` section will still be counted.
For more ways to ignore coverage in Verilator, refer to the [Verilator Documentation](https://veripool.org/guide/latest/exe_verilator.html#configuration-files).
### 2. Using Toffee to Specify Filters
```python
def set_line_coverage(request, datfile, ignore=[]):
"""Pass
Args:
request (pytest.Request): Pytest's default fixture.
datfile (string): The coverage file generated by the DUT.
ignore (list[str]): Coverage filter files or directories.
"""
```
The `ignore` parameter can specify content to be filtered out from the coverage file. For example:
```python
...
set_line_coverage(request, coverage_file,
get_root_dir("scripts/frontend_ifu_rvc_expander"))
```
During coverage statistics, the `line_coverage.ignore` file in the `scripts/frontend_ifu_rvc_expander` directory will be searched, and its wildcard patterns will be used for filtering.
```ignore
# Line coverage ignore file
# Ignore Top file
*/RVCExpander_top*%
```
The above file indicates that files containing the keyword `RVCExpander_top` will be ignored during coverage statistics (the corresponding data is collected but excluded from the final report).
## Viewing Statistics Results
After completing all the steps, including preparing the test environment ([Download RTL Code](../01_verfiy_env.md#download-rtl-code), [Compile DUT](../01_verfiy_env.md#compile-dut), [Edit Configuration](../01_verfiy_env.md#edit-configuration)), and adding tests ([Add Compilation Scripts](01_build_script.md), [Build Test Environment](02_build_env.md), [Add Test Cases](03_add_test.md)):
Now, [Run Tests](../02_run_test.md). Afterward, an HTML version of the test report will be generated in the `out/report` directory by default.
You can also view the statistics results by selecting the corresponding test report (named by test time) under "Current Version" in the [Progress Overview](https://open-verify.cc/UnityChipForXiangShan/docs/) section and clicking the link on the right.

View File

@ -1,157 +0,0 @@
---
title: Functional Coverage
linkTitle: Functional Coverage
#menu: {main: {weight: 20}}
weight: 6
---
Functional Coverage is a **user-defined** metric used to measure the proportion of design specifications executed during verification. Functional coverage focuses on whether the features and functionalities of the design have been covered by the test cases.
Mapping refers to associating functional points with test cases. This allows you to see which test cases correspond to each functional point during statistics, making it easier to identify which functional points have more test cases and which have fewer. This helps optimize test cases in the later stages.
## Relevant Locations in This Project
Functional coverage must be defined before it can be collected, primarily during the process of building the test environment.
In [Building the Test Environment](https://open-verify.cc/UnityChipForXiangShan/docs/03_add_test/02_build_env/):
- [Define Functional Coverage](02_build_env.md#2-define-functional-coverage): Create functional coverage groups, add watch points, and map them.
- [Define Necessary Fixtures](02_build_env.md#3-define-necessary-fixtures): Pass the collected results to `toffee-report`.
- [Collect Coverage](02_build_env.md#4-collect-coverage): Add watch points and mappings.
Other:
- Functional points can also be written in each test case for use in test cases.
## Functional Coverage Workflow
### Specify Group Name
The test report matches the Group name with the DUT name. Use `comm.UT_FCOV` to obtain the DUT prefix. For example, in the Python module `ut_frontend/ifu/rvc_expander/classical_version/env/rvc_expander_wrapper.py`, the following call is made:
```python
from comm import UT_FCOV
# Module name: ut_frontend.ifu.rvc_expander.classical_version.env.rvc_expander_wrapper
# Remove classical_version and the parent module env, rvc_expander_wrapper using ../../../
# UT_FCOV will automatically remove the prefix ut_
g = fc.CovGroup(UT_FCOV("../../../CLASSIC"))
# name = UT_FCOV("../../../CLASSIC")
```
The value of `name` is `frontend.ifu.rvc_expander.CLASSIC`. When collecting the final results, the longest prefix will be matched to the target UT (i.e., matched to the `frontend.ifu.rvc_expander` module).
### Create Coverage Group
Use `toffee`'s `funcov` to create a coverage group.
```python
import toffee.funcov as fc
# Use the GROUP name specified above
g = fc.CovGroup(name)
```
These two steps can also be combined into one: `g = fc.CovGroup(UT_FCOV("../../../CLASSIC"))`.
The created `g` object represents a functional coverage group, which can be used to provide watch points and mappings.
### Add Watch Points and Mappings
Inside each test case, you can use `add_watch_point` (or its alias `add_cover_point`, which is identical) to add watch points and `mark_function` to add mappings.
A watch point is triggered when the signal meets the conditions defined in the watch point, and its name (i.e., the functional point) will be recorded in the functional coverage.
A mapping associates functional points with test cases, allowing you to see which test cases correspond to each functional point during statistics.
The location of the watch point depends on the actual situation. Generally, adding watch points outside the test case is acceptable. However, sometimes more flexibility is required.
1. Outside the test case (in `decode_wrapper.py`):
```python
def init_rvc_expander_funcov(expander, g: fc.CovGroup):
"""Add watch points to the RVCExpander module to collect functional coverage information"""
# 1. Add point RVC_EXPAND_RET to check expander return value:
# - bin ERROR: The instruction is not illegal
# - bin SUCCE: The instruction is not expanded
g.add_watch_point(expander, {
"ERROR": lambda x: x.stat()["ilegal"] == False,
"SUCCE": lambda x: x.stat()["ilegal"] != False,
}, name="RVC_EXPAND_RET")
# 5. Reverse mark function coverage to the check point
def _M(name):
# Get the module name
return module_name_with(name, "../../test_rv_decode")
# - mark RVC_EXPAND_RET
g.mark_function("RVC_EXPAND_RET", _M(["test_rvc_expand_16bit_full",
"test_rvc_expand_32bit_full",
"test_rvc_expand_32bit_randomN"]), bin_name=["ERROR", "SUCCE"])
# The End
return None
```
In this example, the first `g.add_watch_point` is placed outside the test case because it is not directly related to the existing test cases. Placing it outside the test case is more convenient. Once the conditions in the `bins` of the `add_watch_point` method are triggered, the `toffee-test` framework will collect the corresponding functional points.
2. Inside the test case (in `test_rvc_expander.py`):
```python
N = 10
T = 1 << 32
@pytest.mark.toffee_tags([TAG_LONG_TIME_RUN, TAG_RARELY_USED])
@pytest.mark.parametrize("start,end",
[(r * (T // N), (r + 1) * (T // N) if r < N - 1 else T) for r in range(N)])
def test_rvc_expand_32bit_full(rvc_expander, start, end):
"""Test the RVC expand function with a full 32-bit instruction set
Description:
Randomly generate N 32-bit instructions for each check, and repeat the process K times.
"""
# Add check point: RVC_EXPAND_ALL_32B to check instr bits.
covered = -1
g.add_watch_point(rvc_expander, {"RANGE[%d-%d]" % (start, end): lambda _: covered == end},
name="RVC_EXPAND_ALL_32B", dynamic_bin=True)
# Reverse mark function to the check point
g.mark_function("RVC_EXPAND_ALL_32B", test_rvc_expand_32bit_full)
# Drive the expander and check the result
rvc_expand(rvc_expander, list([_ for _ in range(start, end)]))
# When reaching here, the range [start, end] is covered
covered = end
g.sample()
```
In this example, the watch point is inside the test case because `start` and `end` are determined by `pytest.mark.parametrize`. Since the values are not fixed, the watch point needs to be added inside the test case.
### Sampling
At the end of the previous example, we called `g.sample()`. This function notifies `toffee-test` that the `bins` in `add_watch_point` have been executed. If the conditions are met, the watch point is recorded as a pass.
There is also an automatic sampling option. During the test environment setup, you can add `StepRis(lambda x: g.sample())` in the fixture definition. This will automatically sample at the rising edge of each clock cycle.
The following content is from `ut_backend/ctrl_block/decode/env/decode_wrapper.py`:
```python
@pytest.fixture()
def decoder(request):
# Before test
init_rv_decoder_funcov(g)
func_name = request.node.name
# If the output directory does not exist, create it
output_dir_path = get_out_dir("decoder/log")
os.makedirs(output_dir_path, exist_ok=True)
decoder = Decode(DUTDecodeStage(
waveform_filename=get_out_dir("decoder/decode_%s.fst" % func_name),
coverage_filename=get_out_dir("decoder/decode_%s.dat" % func_name),
))
decoder.dut.InitClock("clock")
decoder.dut.StepRis(lambda x: g.sample())
yield decoder
# After test
decoder.dut.Finish()
coverage_file = get_out_dir("decoder/decode_%s.dat" % func_name)
if not os.path.exists(coverage_file):
raise FileNotFoundError(f"File not found: {coverage_file}")
set_line_coverage(request, coverage_file, get_root_dir("scripts/backend_ctrlblock_decode"))
set_func_coverage(request, g)
g.clear()
```
As shown above, we call `g.sample()` before `yield`, enabling automatic sampling at the rising edge of each clock cycle.
The `StepRis` function executes the passed function at the rising edge of each clock cycle. For more details, refer to the [Picker Usage Guide](https://open-verify.cc/mlvp/docs/env_usage/picker_usage/).

View File

@ -1,26 +0,0 @@
---
title: Add Test
linkTitle: Add Test
#menu: {main: {weight: 20}}
weight: 15
---
To add a brand-new DUT test case, the following three steps need to be completed (this section uses the `rvc_expander` under the frontend `ifu` as an example):
1. **Add a compilation script**: Write a compilation file for the corresponding `rtl` in the `scripts` directory using `python` (e.g., `build_ut_frontend_ifu_rvc_expander.py`).
1. **Build the test environment**: Create the target test UT directory in the appropriate location (e.g., `ut_frontend/ifu/rvc_expander`). If necessary, add the basic tools required for the DUT test in modules such as `tools` or `comm`.
1. **Add test cases**: Add test cases in the UT directory following the [PyTest specification](https://docs.pytest.org/en/stable/).
If you are adding content to an existing DUT test, simply follow the original directory structure.
For information on how to perform Python chip verification using the picker and toffee libraries, refer to: [https://open-verify.cc/mlvp/docs](https://open-verify.cc/mlvp/docs)
When testing, you also need to pay attention to the following:
1. **UT Module Description**: Add a `README.md` file in the top-level folder of the added module to provide an explanation. For specific formats and requirements, refer to the [template](https://open-verify.cc/UnityChipForXiangShan/docs/10_template_ut_readme/).
1. **Code Coverage**: Code coverage is an important metric for chip verification. Generally, all code of the target DUT needs to be covered.
1. **Functional Coverage**: Functional coverage indicates how much of the target functionality has been verified. It usually needs to reach 100%.
In subsequent documentation, we will continue to use the `rvc_expander` module as an example to explain the above process in detail.
\*Note: Directory or file names should be reasonable so that their specific meaning can be inferred from the naming.

View File

@ -1,22 +0,0 @@
---
title: How to Participate in This Project
linkTitle: How to Participate in This Project
#menu: {main: {weight: 20}}
weight: 18
---
### How to Submit a Bug
Submit according to the ISSUE template and mark the corresponding labels (bug, bug level, etc.).
The maintainer of the corresponding module will check and modify the labels and XiangShan branch as needed.
### How to Submit Documentation
Documentation for this repository should be submitted via PR to this repository. DUT documentation should be submitted in the repository at [UnityChipForXiangShan/documents/content/zh-cn/docs/98_UT](https://github.com/XS-MLVP/UnityChipForXiangShan/tree/main/documents/content/zh-cn/docs/98_UT).
This project welcomes anyone to participate via [`ISSUE`](https://github.com/XS-MLVP/UnityChipForXiangShan/issues), [`DISCUSS`](https://github.com/XS-MLVP/env-xs-ov-00-bpu/discussions), [`Fork`](https://github.com/XS-MLVP/UnityChipForXiangShan/fork), or [`PR`](https://github.com/XS-MLVP/env-xs-ov-00-bpu/pulls).
WanZhongYiXin QQ Group:
<image src="600480230.jpg" alter="600480230" width=300px />

View File

@ -1,96 +0,0 @@
---
title: Template-PR
linkTitle: Template-PR
#menu: {main: {weight: 20}}
weight: 19
---
```markdown
# Description
Please include a summary of the changes and the related issue.
Please also include relevant motivation and context.
List any dependencies that are required for this change.
Fixes # (issue)
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
# How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce.
Please also list any relevant details for your test configuration
- [ ] Test A
- [x] Test B
**Test Configuration**:
* Firmware version:
* Hardware:
* Toolchain:
* SDK:
# Checklist:
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream modules
```
The rendered effect is as follows:
# Description
Please include a summary of the changes and the related issue. Please also include relevant motivation
and context. List any dependencies that are required for this change.
Fixes # (issue)
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
# How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce.
Please also list any relevant details for your test configuration
- [ ] Test A
- [x] Test B
**Test Configuration**:
* Firmware version:
* Hardware:
* Toolchain:
* SDK:
# Checklist:
- [ ] My code follows the style guidelines of this project
- [ ] I have added the appropriate labels
- [ ] I have performed a self-review of my code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream modules

View File

@ -1,87 +0,0 @@
---
title: Template-ISSUE
linkTitle: Template-ISSUE
#menu: {main: {weight: 20}}
weight: 20
---
```markdown
## Description
A brief description of the issue.
## Steps to Reproduce
1. Describe the first step
2. Describe the second step
3. Describe the third step
4. ...
## Expected Result
Describe what you expected to happen.
## Actual Result
Describe what actually happened.
## Screenshots
If applicable, add screenshots to help explain your problem.
## Environment
- OS: [e.g. Windows 10, macOS 10.15, Ubuntu 20.04]
- Browser: [e.g. Chrome 86, Firefox 82, Safari 14]
- Version: [e.g. 1.0.0]
## Additional Information
Add any other context about the problem here.
```
The rendered effect is as follows:
## Description
A brief description of the issue.
## Steps to Reproduce
1. Describe the first step
2. Describe the second step
3. Describe the third step
4. ...
## Expected Result
Describe what you expected to happen.
## Actual Result
Describe what actually happened.
## Screenshots
If applicable, add screenshots to help explain your problem.
## Environment
- OS: [e.g. Windows 10, macOS 10.15, Ubuntu 20.04]
- Browser: [e.g. Chrome 86, Firefox 82, Safari 14]
- Version: [e.g. 1.0.0]
## Additional Information
Add any other context about the problem here.
## Checklist
- [ ] I have searched the existing issues
- [ ] I have added the appropriate labels
- [ ] I have reproduced the issue with the latest version
- [ ] I have provided a detailed description of the bug
- [ ] I have provided steps to reproduce the issue
- [ ] I have included screenshots (if applicable)
- [ ] I have provided the environment details (OS, version, etc.)

View File

@ -1,133 +0,0 @@
---
title: Template-UT-README
linkTitle: Template-UT-README
#menu: {main: {weight: 20}}
weight: 21
---
```markdown
# Module Name
## Test Objectives
<Description of test objectives and methods>
## Test Environment
<Description of test environment and dependencies>
## Function Check
<Describe the target functions to be tested and the corresponding checking methods>
|No.|Module|Function Description|Checkpoint Description|Check Identifier|Check Item|
|-|-|-|-|-|-|
|-|-|-|-|-|-|
## Verification Interface
<Description of the interface>
## Test Case Description
#### Test Case 1
|Step|Operation|Expected Result|Covered Function Point|
|-|-|-|-|
|-|-|-|-|
#### Test Case 2
|Step|Operation|Expected Result|Covered Function Point|
|-|-|-|-|
|-|-|-|-|
## Directory Structure
<Description of the directory structure for this module>
## Checklist
- [ ] This document meets the specified [template]() requirements
- [ ] The API provided by Env does not contain any DUT pins or timing information
- [ ] The API of Env remains stable (total [ X ])
- [ ] Supported RTL versions in Env have been checked (supported versions [ X ])
- [ ] Function points (total [ X ]) are consistent with the [design document]()
- [ ] Checkpoints (total [ X ]) cover all function points
- [ ] The input of checkpoints does not depend on any DUT pins, only on the standard API of Env
- [ ] All test cases (total [ X ]) are mapped to function checkpoints
- [ ] All test cases use assert for result checking
- [ ] All DUTs or corresponding wrappers are created via fixture
- [ ] RTL version is checked in the above fixtures
- [ ] The fixture for creating DUT or corresponding wrapper performs function and code line coverage statistics
- [ ] Filtering requirements are checked when setting code line coverage
The rendered effect is as follows:
# Module Name
## Test Objectives
<Description of test objectives and methods>
## Test Environment
<Description of test environment and dependencies>
## Function Check
<Describe the target functions to be tested and the corresponding checking methods>
|No.|Module|Function Description|Checkpoint Description|Check Identifier|Check Item|
|-|-|-|-|-|-|
|-|-|-|-|-|-|
## Verification Interface
<Description of the interface>
## Test Case Description
#### Test Case 1
|Step|Operation|Expected Result|Covered Function Point|
|-|-|-|-|
|-|-|-|-|
#### Test Case 2
|Step|Operation|Expected Result|Covered Function Point|
|-|-|-|-|
|-|-|-|-|
## Directory Structure
<Description of the directory structure for this module>
## Checklist
- [ ] This document meets the specified [template]() requirements
- [ ] The API provided by Env does not contain any DUT pins or timing information
- [ ] The API of Env remains stable (total [ X ])
- [ ] Supported RTL versions in Env have been checked (supported versions [ X ])
- [ ] Function points (total [ X ]) are consistent with the [design document]()
- [ ] Checkpoints (total [ X ]) cover all function points
- [ ] The input of checkpoints does not depend on any DUT pins, only on the standard API of Env
- [ ] All test cases (total [ X ]) are mapped to function checkpoints
- [ ] All test cases use assert for result checking
- [ ] All DUTs or corresponding wrappers are created via fixture
- [ ] RTL version is checked in the above fixtures
- [ ] The fixture for creating DUT or corresponding wrapper performs function and code line coverage statistics
- [ ] Filtering requirements are checked when setting code line coverage

View File

@ -1,230 +0,0 @@
---
title: Common APIs
linkTitle: Common APIs
#menu: {main: {weight: 99}}
weight: 95
---
## comm Module
The comm module provides some commonly used APIs, which can be called in the following ways:
```python
# import all
from comm import *
# or direct import functions you need
from comm import function_you_need
# or access from module
import comm
comm.function_you_need()
```
### cfg Submodule
#### get_config(cfg=None)
Get the current Config configuration
- Input: If cfg is not empty, return cfg. Otherwise, automatically get the global Config via toffee.
- Return: Config object
```python
import comm
cfg = comm.get_config()
print(cfg.rtl.version)
```
#### cfg_as_str(cfg: CfgObject)
Convert the config object to a string type
- Input: Config object
- Return: Encoded Config object
```python
import comm
cfg_str = comm.cfg_as_str(comm.get_config())
```
#### cfg_from_str(cfg_str)
Restore the Config object from a string
- Input: Encoded Config object
- Return: Config object
```python
import comm
cfg = comm.cfg_from_str(cfg_str)
```
#### dump_cfg(cfg: CfgObject = None, cfg_file=None)
Save the config object to a file
- Input:
- cfg: the config to save
- cfg_file: target file
```python
import comm
cfg = comm.get_config()
comm.dump_cfg(cfg, "config.yaml")
```
### functions Submodule
#### get_log_dir(subdir="", cfg=None)
Get the log directory
- Input:
- subdir: subdirectory
- cfg: config file
- Output: log directory
```python
import comm
my_log = comm.get_log_dir("my_log")
print(my_log) # /workspace/UnityChipForXiangShan/out/log/my_log
```
#### get_out_dir(subdir="", cfg=None)
Get the output directory
- Input:
- subdir: subdirectory
- cfg: config file
- Output: output directory
#### get_rtl_dir(subdir="", cfg=None)
Get the RTL directory
- Input:
- subdir: subdirectory
- cfg: config file
- Output: RTL directory
#### get_root_dir(subdir="")
Get the root directory:
- Input: subdirectory under the root directory
- Output: root directory of the current repository
#### is_all_file_exist(files_to_check, dir)
Check whether all files exist in the specified directory
- Input:
- files_to_check: list of files to check
- dir: target directory
- Output: whether all exist; returns False if any file does not exist
#### time_format(seconds=None, fmt="%Y%m%d-%H%M%S")
Format time
- Input:
- seconds: time to format, None means current time
- fmt: time format
- Return: formatted time string
```python
import comm
import time
print(time_format(time.time())) # 20241202-083726
```
#### base64_encode(input_str)
Base64 encode:
- Input: string to encode
- Output: encoded string
```python
import comm
print(comm.base64_encode("test")) # dGVzdA==
```
#### base64_decode(base64_str)
Base64 decode:
- Input: base64 encoded string
- Output: decoded original string
```python
import comm
print(comm.base64_decode("dGVzdA==")) # test
```
#### exe_cmd(cmd, no_log=False)
Execute an OS command:
- Input:
- cmd: OS command to execute
- no_log: whether to return command line output
- Output: success, stdout, stderr
- success: whether the command executed successfully
- command standard output string (forced to empty if no_log=True)
- command standard error string (forced to empty if no_log=True)
```python
import comm
su, st, er = exe_cmd("pwd")
print(st)
```
#### get_git_commit()
Get the current repository git commit hash
#### get_git_branch()
Get the current repository git branch name
#### UT_FCOV(group, ignore_prefix="ut_")
Get function coverage group
- Input:
- group: group name
- ignore_prefix: prefix to remove
- Output: coverage group name with module prefix
For example, called in `ut_backend/ctrl_block/decode/env/decode_wrapper.py`:
```python
print(UT_FCOV("../../INT"))
# out
backend.ctrl_block.decode.INT
```
#### get_version_checker(target_version)
Get version check function
- Input: target version string
- Output: check function
The returned check function is usually used for version checking in fixtures.
```python
import comm
import pytest
checker = comm.get_version_checker("openxiangshan-kmh-24092701+")
@pytest.fixture
def fixture():
checker()
...
```
#### module_name_with(names, prefix=None)
Add a module prefix to names
- Input:
- names: list of strings to add prefix to
- prefix: module prefix
- Return: list of strings with prefix added
For example, called in a/b/c/d/e.py:
```python
import comm
print(comm.module_name_with(["X", "Y"], "../../x"))
# out
["a.b.c.x.X", "a.b.c.x.Y"]
```
#### `get_all_rtl_files(top_module, cfg)`
Get a list of all RTL files (`.v` or `.sv`) that the module named `top_module` depends on, and ensure that the first element of the list is the absolute path of the file where `top_module` is located. All RTL files are located in the `UnityChipForXiangShan/rtl/rtl` directory.
- Input:
- `top_module`: module name, type `str`
- `cfg`: config info, type `CfgObject`
- Output:
- Returns a list of strings, each string is the absolute path of an RTL file that the module depends on. The first element of the list is the path of the file where `top_module` is located.
Suppose `top_module` is `"ALU"`, and its dependent RTL files include `ALU.sv`, `adder.v`, and `multiplier.v`:
```python
paths = get_all_rtl_files("ALU", cfg)
"""
Possible contents of paths:
[
"/path/to/UnityChipForXiangShan/rtl/rtl/ALU.sv",
"/path/to/UnityChipForXiangShan/rtl/rtl/adder.v",
"/path/to/UnityChipForXiangShan/rtl/rtl/multiplier.v"
]
"""
```

View File

@ -1,189 +0,0 @@
---
title: Others
linkTitle: Others
#menu: {main: {weight: 99}}
weight: 96
---
## Test Case Management
If test cases are closely related to the target RTL version, changes in RTL may render previous test cases unsuitable. In addition, different scenarios have different requirements, such as not running time-consuming cases when verifying the test environment. Therefore, test cases need to be managed so that users can skip certain cases in specific scenarios. To achieve this, we use `pytest.mark.toffee_tags` to tag and version each test case. Then, in the configuration file, you can set which tags to skip or which tags to run.
```python
@pytest.mark.toffee_tags("my_tag", "version1 < version13")
def test_case_1():
...
```
For example, the above `test_case_1` is tagged with `my_tag` and supports versions from `version1` to `version13`. Therefore, you can specify `test.skip-tags=["my_tag"]` in the configuration file to skip this case during execution.
The parameters for `pytest.mark.toffee_tags` are as follows:
```python
@pytest.mark.toffee_tags(
tag: Optional[list, str] = [] # Case tag
version: Optional[list, str] = [], # RTL version requirement for the case
skip: callable = None, # Custom skip logic, skip(tag, version, item): (skip, reason)
)
```
The `tag` parameter of `toffee_tags` supports both `str` and `list[str]` types. The `version` parameter can also be `str` or `list[str]`. If it is a list, it matches exactly; if it is a string, the matching rules are as follows:
1. `name-number1 < name-number2:` means the version must be between `number1` and `number2` (inclusive, `number` can be a decimal, e.g., `1.11`)
2. `name-number1+`: means version `number1` and later
3. `name-number1-`: means version `number1` and earlier
If none of the above, and there is a `*` or `?`, it is treated as a wildcard. Other cases are exact matches.
Predefined tags can be found in `comm/constants.py`, for example:
```python
# Predefined tags for test cases
TAG_LONG_TIME_RUN = "LONG_TIME_RUN" # Long-running
TAG_SMOKE = "SMOKE" # Smoke test
TAG_RARELY_USED = "RARELY_USED" # Rarely used
TAG_REGRESSION = "REGRESSION" # Regression test
TAG_PERFORMANCE = "PERFORMANCE" # Performance test
TAG_STABILITY = "STABILITY" # Stability test
TAG_SECURITY = "SECURITY" # Security test
TAG_COMPATIBILITY = "COMPATIBILITY" # Compatibility test
TAG_OTHER = "OTHER" # Other
TAG_CI = "CI" # Continuous integration test
TAG_DEBUG = "DEBUG" # Debug test
TAG_DEMO = "DEMO" # Demo
```
In the default configuration (`config/_default.yaml`), tests marked with `LONG_TIME_RUN`, `REGRESSION`, `RARELY_USED`, and `CI` are filtered out.
You can use `@pytest.mark.toffee_tags` to add tags to each case, or define the following variables in a module to add tags to all test cases in the module:
```python
toffee_tags_default_tag = [] # Corresponds to the tag parameter
toffee_tags_default_version = [] # Corresponds to the version parameter
toffee_tags_default_skip = None # Corresponds to the skip parameter
```
*Note: The version number in this environment will automatically filter out git tags. For example, if the downloaded RTL is named `openxiangshan-kmh-97e37a2237-24092701.tar.gz`, its version number in this project is `openxiangshan-kmh-24092701`, which can be obtained via `cfg.rtl.version` or `comm.get_config().rtl.version`.
## Version Checking
In addition to using the `toffee_tags` tag for automatic version checking, you can also actively check versions via `get_version_checker`. A unit test usually consists of a test environment (Test Env) and test cases (Test Case). The Env encapsulates RTL pins and functions, then provides a stable API to the Case, so version checking is needed in the Env to determine whether to skip all test cases using this environment. For example, in Env:
```python
...
from comm import get_version_checker
version_check = get_version_checker("openxiangshan-kmh-*") # Get RTL version checker, same as the version parameter in toffee_tags
@pytest.fixture()
def my_fixture(request):
version_check() # Actively check in the fixture
....
yield dut
...
```
In the above example, the Env actively performs version checking in the fixture named `my_fixture`. Therefore, every time the test case calls it, version checking is performed, and if the check fails, the case will be skipped.
## Repository Directory Structure
```bash
UnityChipForXiangShan
├── LICENSE # Open source license
├── Makefile # Main Makefile
├── README.en.md # English readme
├── README.zh.md # Chinese readme
├── __init__.py # Python module file, allows importing UnityChipForXiangShan as a module
├── pytest.ini # PyTest configuration file
├── comm # Common components: logs, functions, configs, etc.
├── configs # Configuration files directory
├── documents # Documentation
├── dut # DUT generation directory
├── out # Output directory for logs, reports, etc.
├── requirements.txt # Python dependencies
├── rtl # RTL cache
├── run.py # Main Python entry file
├── scripts # DUT compilation scripts
├── tools # Common tool modules
├── ut_backend # Backend test cases
├── ut_frontend # Frontend test cases
├── ut_mem_block # Memory access test cases
└── ut_misc # Other test cases
```
## Configuration File Description
Default configuration and explanation:
```yaml
# Default configuration file
# Configuration loading order: _default.yaml -> user-specified *.yaml -> command line parameters eg: log.term-level='debug'
# RTL configuration
rtl:
# RTL download address, all *.gz.tar files from this address are treated as target RTL
base-url: https://<your_rtl_download_address>
# RTL version to download, e.g., openxiangshan-kmh-97e37a2237-24092701
version: latest
# Directory to store RTL, relative to the current config file path
cache-dir: "../rtl"
# Test case configuration (tag and case support wildcards)
test:
# Skip tags, all test cases with these tags will be skipped
skip-tags: ["LONG_TIME_RUN", "RARELY_USED", "REGRESSION", "CI"]
# Target tags, only test cases with these tags will be executed (skip-tags overrides run-tags)
run-tags: []
# Skipped test cases, all test cases (or module names) with these names will be skipped.
skip-cases: []
# Target test cases, only test cases (or module names) with these names will be executed (skip-cases overrides run-cases).
run-cases: []
# Skip exceptions, all test cases that throw these exceptions will be skipped
skip-exceptions: []
# Output configuration
output:
# Output directory, relative to the current config file path
out-dir: "../out"
# Test report configuration
report:
# Report generation directory, relative to output.out-dir
report-dir: "report"
# Report name, supports variable substitution: %{host} hostname, %{pid} process ID, %{time} current time
report-name: "%{host}-%{pid}-%{time}/index.html"
# Report content
information:
# Report title
title: "XiangShan KMH Test Report"
# Report user information
user:
name: "User"
email: "User@example.email.com"
# Target line coverage, e.g., 90 means 90%
line_grate: 99
# Other information to display, key is the title, value is the content
meta:
Version: "1.0"
# Log configuration
log:
# Root output level
root-level: "debug"
# Terminal output level
term-level: "info"
# File log output directory
file-dir: "log"
# File log name, supports variable substitution: %{host} hostname, %{pid} process ID, %{time} current time
file-name: "%{host}-%{pid}-%{time}.log"
# File log output level
file-level: "info"
# Test result configuration (this data is used to populate statistics charts in documents, original data comes from toffee-test generated reports)
# After running the tests, you can view the results via `make doc`
doc-result:
# Whether to enable test result post-processing
disable: False
# Organizational structure configuration of target DUT
dutree: "%{root}/configs/dutree/xiangshan-kmh.yaml"
# Result name, will be saved to the output report directory
result-name: "ut_data_progress.json"
# Symlink to the created test report for hugo
report-link: "%{root}/documents/static/data/reports"
```
You can add custom parameters in the above configuration file, get global config info via `cfg = comm.get_config()`, and then access via `cfg.your_key`. The `cfg` info is read-only and cannot be modified by default.

View File

@ -1,41 +0,0 @@
---
title: Required Specifications
linkTitle: Required Specifications
#menu: {main: {weight: 99}}
weight: 97
---
In order to facilitate the integration of everyone's contributions, it is necessary to adopt the same "specifications" in coding, environment, and documentation.
### Environment Requirements
- **python:** When coding in Python, use the standard library as much as possible, and use general syntax compatible with most Python 3 versions (try to be compatible with Python 3.6 - Python 3.12). Do not use syntax that is too old or too new.
- **Operating System:** Ubuntu 22.04 is recommended. On Windows, it is recommended to use the WSL2 environment.
- **hugo:** Recommended version is 0.124.1 (older versions do not support symlinks)
- **Minimal dependencies:** Try to minimize the use of third-party C++/C libraries.
- **picker:** It is recommended to install the picker tool and xspcomm library via wheel.
### Test Cases
- **Code Style:** It is recommended to follow the [PEP 8 standard](https://peps.python.org/pep-0008/)
- **Build Scripts:** The naming of build scripts must follow the DUT naming structure, otherwise verification results cannot be collected correctly. For example, the build file for the `backend.ctrl_block.decode` UT in the scripts directory should be named `build_ut_backend_ctrl_block_decode.py` (with the fixed prefix `build_ut_`, and dots `.` replaced by underscores `_`). The script should implement the `build(cfg) -> bool` and `line_coverage_files(cfg) -> list[str]` methods. `build` is used to compile the DUT into a Python module, and `line_coverage_files` is used to return the files for code line coverage statistics.
- **Test Case Tags:** If a test case cannot be version-agnostic, it needs to be marked with `pytest.mark.toffee_tags` to indicate the supported versions.
- **Test Case Abstraction:** The input of the test case should not contain specific DUT pins or other strongly coupled content. Only functions encapsulated on top of the DUT can be called. For example, for an adder, the DUT's target function should be encapsulated as `dut_wrapper.add(a: int, b: int) -> int, bool`, and in the test_case, only `sum, c = add(a, b)` should be called for testing.
- **Coverage Abstraction:** When writing functional coverage, the input of the checkpoint function should also not include DUT pins.
- **Environment Abstraction:** For a verification, it is usually divided into two parts: Test Case and Env (everything except the test case is called Env, which includes DUT, drivers, monitors, etc.). The Env should provide abstract functional interfaces to the outside and should not expose too many details.
- **Test Documentation:** In the verification environment of each DUT, a `README.md` should be provided to explain the environment, such as the interfaces provided by Env to Case, directory structure, etc.
### PR Writing
- **Title:** Concise and clear, able to summarize the main content of the PR.
- **Detailed Description:** Clearly explain the purpose of the PR, the changes made, and relevant background information. If solving an existing issue, provide a link (e.g., Issue).
- **Related Issues:** Link related issues in the description, such as `Fixes #123`, so that the related issue is closed when the PR is merged.
- **Testing:** Testing is required, and the test results should be described.
- **Documentation:** Any documentation involved in the PR should be updated accordingly.
- **Decomposition:** If the PR involves many changes, consider splitting it into multiple PRs.
- **Checklist:** Check whether compilation passes, code style is reasonable, tests pass, necessary comments are present, etc.
- **Template:** Please refer to the provided PR template [reference link](08_pr_template/).
### ISSUE Writing
Same requirements as above.

View File

@ -1,31 +0,0 @@
---
title: Maintainers
linkTitle: Maintainers
#menu: {main: {weight: 99}}
weight: 99
---
When submitting an issue, pull request, or discussion, specifying the maintainer of the corresponding module can help you get a quicker response. The current maintainers are listed below (in alphabetical order):
**Verification Tools:**
- picker: [Makiras](https://github.com/Makiras), [SFangYy](https://github.com/SFangYy), [yaozhicheng](https://github.com/yaozhicheng)
- toffee/toffee-test: [Miical](https://github.com/Miical), [yaozhicheng](https://github.com/yaozhicheng)
<!-- <script src="../../js/echarts.min.js"></script> -->
<script>
function update_maintainers(data_url){
updateMaintainers(data_url)
}
</script>
<div style="text-align: center; width: 100%;">
{{<list-report baseurl="../../../data/reports" label="Current Version:" id="maintainers" onchange="update_maintainers">}}
</div>
<br>
{{<maintainers>}}
*Other maintainers will be updated continuously.
If you are interested in this project, you are welcome to apply to become a maintainer.

View File

@ -1,87 +1,8 @@
---
title: Progress Overview
linkTitle: Progress Overview
title: XiangShan UT
linkTitle: XiangShan UT
#menu: {main: {weight: 20}}
weight: 10
weight: 20
---
<script src="../../js/echarts.min.js"></script>
<script src="../../js/chart_meta.js"></script>
<script>
function update_charts(data_url){
show_meta_chart("meta_chart", data_url)
updateDUTestStatus(data_url)
}
</script>
This project aims to perform unit testing (Unit Test, UT) verification of the [XiangShan Processor](https://github.com/OpenXiangShan/XiangShan) Kunming Lake architecture through open-source crowdsourcing. The chart below shows the verification status of each module in the XiangShan Kunming Lake architecture.
<div id="meta_chart" style="width: 100%;height:400px;"></div>
<div style="text-align: center; width: 100%;">
{{<list-report baseurl="../../data/reports" label="Current Version:" detail="View Test Report" id="index" onchange="update_charts">}}
</div>
<br>
Overall statistics are as follows:
<table>
<ol>
<tr>
<td>Total Cases:</td>
<td style="text-align: left; font-weight: bold;"><em id="em_id_report_cases_toal">-</em></td>
<td>Passed Cases:</td>
<td style="text-align: left; font-weight: bold;"><em id="em_id_report_cases_pass">-</em></td>
<td>Passed Rate:</td>
<td style="text-align: left; font-weight: bold;"><em id="em_id_report_cases_prate">-</em></td>
</tr>
<tr>
<td>Failed Cases:</td>
<td style="text-align: left; font-weight: bold;"><em id="em_id_report_cases_fail">-</em></td>
<td>Skipped Cases:</td>
<td style="text-align: left; font-weight: bold;"><em id="em_id_report_cases_skip">-</em></td>
<td>Skip Rate:</td>
<td style="text-align: left; font-weight: bold;"><em id="em_id_report_cases_srate">-</em></td>
</tr>
<tr>
<td>Function Coverage:</td>
<td style="text-align: left; font-weight: bold;"><em id="em_id_report_function_total">-</em></td>
<td>Covered Functions:</td>
<td style="text-align: left; font-weight: bold;"><em id="em_id_report_function_cover">-</em></td>
<td>Covered Rate:</td>
<td style="text-align: left; font-weight: bold;"><em id="em_id_report_function_rate">-</em></td>
</tr>
<tr>
<td>Total Lines:</td>
<td style="text-align: left; font-weight: bold;"><em id="em_id_report_line_total">-</em></td>
<td>Covered Lines:</td>
<td style="text-align: left; font-weight: bold;"><em id="em_id_report_line_cover">-</em></td>
<td>Covered Rate:</td>
<td style="text-align: left; font-weight: bold;"><em id="em_id_report_line_rate">-</em></td>
</tr>
</ol>
</table>
*The total number of lines will continue to increase as DUTs are added, so: the total line coverage is not the final coverage.
Other quick links:
- **[DUT Documentation & Functions](https://open-verify.cc/UnityChipForXiangShan/docs/98_ut/)**
- **[Pending Bug List](https://github.com/XS-MLVP/UnityChipForXiangShan/labels/bug%20need%20to%20confirm)**
- **[Confirmed Bug List](https://github.com/XS-MLVP/UnityChipForXiangShan/labels/bug%20confirmed)**
- **[Fixed Bug List](https://github.com/XS-MLVP/UnityChipForXiangShan/labels/bug%20fixed)**
- **[Ongoing Task List](https://open-verify.cc/crowdsourcing/kunming_lake)**
- **[Completed Task List](https://open-verify.cc/crowdsourcing/kunming_lake)**
<br>
<div style="text-align: center; width: 100%;">
<h4 id="testmap">XiangShan Kunming Lake DUT Verification Progress</h4>
</div>
<br>
{{<list-dut-test-status>}}
<div style="text-align: center; width: 100%;">
<br>
Note: The statistics in this document are automatically generated based on test results.<br>
Data auto-update date: <em id="em_id_report_date">1970-01-01 00:00:00</em>
</div>
TBD

View File

@ -0,0 +1,8 @@
---
title: FTB
linkTitle: FTB
#menu: {main: {weight: 20}}
weight: 20
---
TBD

View File

@ -0,0 +1,8 @@
---
title: TAGE
linkTitle: TAGE
#menu: {main: {weight: 20}}
weight: 20
---
TBD

View File

@ -0,0 +1,8 @@
---
title: ITTAGE
linkTitle: ITTAGE
#menu: {main: {weight: 20}}
weight: 20
---
TBD

View File

@ -0,0 +1,10 @@
---
title: 分支预测器Branch Prediction Unit, BPU
linkTitle: BPU
#menu: {main: {weight: 20}}
weight: 20
---
{{% pageinfo %}}
什么是BPU
{{% /pageinfo %}}

View File

@ -12,13 +12,14 @@ weight: 13
1. Linux操作系统。建议WSL2下安装Ubuntu22.04。
1. Python。建议Python3.11。
1. picker。按照[快速开始](https://open-verify.cc/mlvp/docs/quick-start/installer/)中的提示安装最新版本。
1. toffee。将在后面自动安装。也可按照[快速开始](https://open-verify.cc/mlvp/docs/mlvp/quick-start/)中的提示手动安装最新版本。
1. lcov。用于后续test阶段报告生成。使用包管理器即可下载`sudo apt install lcov`
**环境配置完成**后clone仓库
```bash
git clone https://github.com/XS-MLVP/UnityChipForXiangShan.git
cd UnityChipForXiangShan
pip3 install -r requirements.txt # 安装 python 依赖(例如 toffee
pip3 install -r requirements.txt # 安装python依赖例如 toffee
```
#### 下载RTL代码
@ -26,10 +27,10 @@ pip3 install -r requirements.txt # 安装 python 依赖(例如 toffee
默认从仓库[https://github.com/XS-MLVP/UnityChipXiangShanRTLs](https://github.com/XS-MLVP/UnityChipXiangShanRTLs)中下载。用户也可以自行按照XiangShan文档编译生成RTL。
```bash
make rtl # 该命令下载最新的 rtl 代码,解压至 rtl 目录,并创建软链
make rtl # 该命下载最新的rtl代码并解压至rtl目录并创建软连
```
可以用以下命令指定下载的 rtl 版本:
可以用以下命令指定下载的rtl版本
```bash
make rtl args="rtl.version=\'openxiangshan-kmh-fad7803d-24120901\'"
@ -37,7 +38,7 @@ make rtl args="rtl.version=\'openxiangshan-kmh-fad7803d-24120901\'"
所有RTL下载包请在[UnityChipXiangShanRTLs](https://github.com/XS-MLVP/UnityChipXiangShanRTLs)中查看。
RTL压缩包的命名规范为`名称-微架构-Git标记-日期编号.tar.gz`,例如`openxiangshan-kmh-97e37a2237-24092701.tar.gz`。在使用时,仓库代码会过滤掉 git 标记和后缀,例如通过 cfg.rtl.version 访问到的版本号为:`openxiangshan-kmh-24092701`。压缩包内的目录结构为:
RTL压缩包的命名规范为`名称-微架构-Git标记-日期编号.tar.gz`,例如`openxiangshan-kmh-97e37a2237-24092701.tar.gz`。在使用时仓库代码会过滤掉git标记和后缀例如通过 cfg.rtl.version 访问到的版本号为:`openxiangshan-kmh-24092701`。压缩包内的目录结构为:
```bash
openxiangshan-kmh-97e37a2237-24092701.tar.gz

View File

@ -123,8 +123,8 @@ def init_rvc_expander_funcov(expander, g: fc.CovGroup):
# - bin ERROR. The instruction is not illegal
# - bin SUCCE. The instruction is not expanded
g.add_watch_point(expander, {
"ERROR": lambda x: x.stat()["illegal"] == False,
"SUCCE": lambda x: x.stat()["illegal"] != False,
"ERROR": lambda x: x.stat()["ilegal"] == False,
"SUCCE": lambda x: x.stat()["ilegal"] != False,
}, name = "RVC_EXPAND_RET")
...
# 5. Reverse mark function coverage to the check point
@ -139,7 +139,7 @@ def init_rvc_expander_funcov(expander, g: fc.CovGroup):
...
```
在上述代码中添加了名为`RVC_EXPAND_RET`的功能检查点来检查`RVCExpander`模块是否具有返回非法指令的能力。需要满足`ERROR`和`SUCCE`两个条件,即`stat()`中的`illegal`需要有`True`也需要有`False`值。在定义完检查点后,通过`mark_function`方法,对会覆盖到该检查的测试用例进行了标记。
在上述代码中添加了名为`RVC_EXPAND_RET`的功能检查点来检查`RVCExpander`模块是否具有返回非法指令的能力。需要满足`ERROR`和`SUCCE`两个条件,即`stat()`中的`ileage`需要有`True`也需要有`False`值。在定义完检查点后,通过`mark_function`方法,对会覆盖到该检查的测试用例进行了标记。
### 3. 定义必要fixture
@ -221,7 +221,7 @@ toffee的官方教程可以参考[这里](https://open-verify.cc/mlvp/docs/mlvp/
### bundle快捷DUT封装
toffee通过Bundle实现了对DUT的绑定。toffee提供了多种建立Bundle与DUT绑定的方法。相关代码参照`ut_frontend/ifu/rvc_expander/toffee_version/bundle`。
toffee通过Bundle实现了对DUT的绑定。toffee提供了多种建立Bundle与DUT绑定的方法。相关代码
#### 手动绑定

View File

@ -1,54 +1,40 @@
---
title: 文档模板
linkTitle: 文档模板
title: FIFO文档模板
linkTitle: FIFO文档模板
weight: 2
# draft: true
draft: true
---
以下是一份验证文档的完整模板(请一定同提交的验证报告区分开来)
以下是一份验证文档的完整模板
```markdown
# 验证文档各部分说明
## 文档概述【必填项】
在该部分对整个文档进行简约描述,例如内容概述,待验证模块的基本功能、特殊需求、特定规格、目标读者、知识前置等。目的是通过对该部分,读者便了解是否具有其感兴趣的内容。例如本文档是对验证文档的编写要求进行描述,便于多文档协作,规范验证的数据输入,特定数据标签等。
## 术语说明 【必填项】 列出术语和关键概念解释,方便读者参考
优先解释模块专有缩写如TLB FIFO等如果有缩写请用`缩写(全称)的方式填在表格的“名称”栏目中`
## 术语说明 \[必填项\] 列出术语和关键概念解释,方便读者参考
优先解释模块专有缩写如TLB FIFO等
对容易混淆的概念请务必明确(如虚拟地址和物理地址等)
| 称 | 定义 |
| ------- | ---|
| 缩写1FULL_NAME_1 | 描述1 |
| 缩写2FULL_NAME_2 | 描述2 |
| 概念名1 | 描述3 |
| 缩写 | 全称 | 定义 |
| -- | ----- | ---|
| 缩写1 | FULL_NAME_1 | 描述1 |
| 缩写2 | FULL_NAME_2 | 描述2 |
| 缩写3 | FULL_NAME_3 | 描述3 |
## 前置知识【可选项】
在阅读文档或进行验证之前建议掌握一些关键前置知识以便更深入理解相关内容。例如在撰写LoadStoreQueueLSQ文档时讲述RAWRead After Write违例有助于理解操作之间的依赖关系。在撰写Icache或L2Cache文档时介绍缓存层级、替换策略和一致性模型等基本概念也有助于读者理解。如果涉及复杂算法也应对其进行简要描述。
基本要求:
1. 该部分内容应简洁,易于理解。如篇幅较长,可将内容移至附录。
2. 针对较为复杂的内容,可以通过图像、伪代码和案例进行解释,以降低理解难度。
## 整体框图 【可选项】 若模块含多个子模块或复杂数据流,需提供框图辅助说明
## 整体框图 \[可选项\] 若模块含多个子模块或复杂数据流,需提供框图辅助说明
可使用Visio/Draw.io等工具绘制导出为PNG/SVG格式
需标注关键信号流向;
框图中子模块命名需与“子模块列表”章节严格一致。
## 流水级示意图 【可选项】 若为复杂流水线型模块,需说明各级流水功能与时序关系
## 流水级示意图 \[可选项\] 若为复杂流水线型模块,需说明各级流水功能与时序关系
可使用Visio/Draw.io等工具绘制导出为PNG/SVG格式
涉及到的模块名称需要保持一致性
重要数据除了列出名称以外,还需要标明位宽等信息
## 子模块列表 【可选项】 若模块由多个子模块组成,需在此列出
## 子模块列表 \[可选项\] 若模块由多个子模块组成,需在此列出
以下是IFU top文档中的一个示例
@ -61,7 +47,7 @@ weight: 2
<mrs-functions>
## 模块功能说明 【必填项】 需按功能树形式逐级分解,每个功能点需对应后续测试点。
## 模块功能说明 \[必填项\] 需按功能树形式逐级分解,每个功能点需对应后续测试点。
请用<mrs-functions></functions>包裹整个“模块功能说明”部分。
@ -89,7 +75,7 @@ weight: 2
</mrs-functions>
## 常量说明 【可选项】 需列出模块中所有可配置参数及其物理意义
## 常量说明 \[可选项\] 需列出模块中所有可配置参数及其物理意义
| 常量名 | 常量值 | 解释 |
@ -99,7 +85,7 @@ weight: 2
| 常量3 | 16 | 常量3解释 |
## 接口说明 【必填项】 详细解释各种接口的含义、来源
## 接口说明 \[必填项\] 详细解释各种接口的含义、来源
信号按功能(如时钟复位、数据输入、控制信号等)或来源(其他模块)分组;
@ -127,7 +113,7 @@ weight: 2
...
## 接口时序 【可选项】 对复杂接口,提供波形图的案例
## 接口时序 \[可选项\] 对复杂接口,提供波形图的案例
### 案例1
@ -137,7 +123,7 @@ weight: 2
请在这里填充时序案例2
## 测试点总表 (【必填项】针对细分的测试点,列出表格)
## 测试点总表 (\[必填项\] 针对细分的测试点,列出表格)
实际使用下面的表格时,请用有意义的英文大写的功能名称和测试点名称替换下面表格中的名称
@ -155,8 +141,5 @@ weight: 2
</mrs-testpoints>
## 附录【可选项】
此部分用于存放正文的补充内容,以便进行扩展和详细说明,旨在使文档格式更加清晰,排版更加合理。
```

View File

@ -58,10 +58,6 @@ endmodule
# FIFO 模块验证文档
## 文档概述
本文档描述FIFO的功能并根据功能给出测试点参考方便测试的参与者理解测试需求编写相关测试用例。
## 术语说明
| 缩写 | 全称 | 定义 |

View File

@ -24,15 +24,15 @@ draft: false
术语说明标题请用二号标题格式(两个#)。
**【必填项】** 该部分需要列出术语和关键概念解释,方便读者参考。
1. 优先解释模块专有缩写如TLB FIFO等,且用`缩写(全名)`的格式填写在“名称”一栏中。
1. 优先解释模块专有缩写如TLB FIFO等
2. 对容易混淆的概念请务必明确(如虚拟地址和物理地址等)
3. 示例格式如下:
| 称 | 定义 |
| ------- | ---|
| TLBTranslation Lookaside Buffer | 地址转换的缓存单元,用于加速虚拟地址到物理的转换 |
| FIFOFirst In First Out | 先进先出队列 |
| 写回 | 发生在Cache替换时如果被替换块为脏块需要将缓存行写回对应内存位置 |
| 缩写 | 全称 | 定义 |
| -- | ----- | ---|
| TLB | Translation Lookaside Buffer | 地址转换的缓存单元,用于加速虚拟地址到物理的转换 |
| FIFO | First In First Out | 先进先出队列 |
| QoS | Quality of Service | 服务质量,用于总线仲裁中优先级控制机制 |
如果有其他补充情况请在此说明例如上述命名描述仅针对香山处理器不代表RISC-V标准或者其他处理器。
@ -126,7 +126,7 @@ ADD R4, R3, R5 ; 使用 R3 的值进行计算
**编写规则:**
1. 请使用 `<mrs-functions>``</mrs-functions>` 标签包裹整个“模块功能说明”部分;
1. 请使用 <mrs-functions></mrs-functions> 标签包裹整个“模块功能说明”部分;
2. 采用 X.Y.Z 多级编号(如 1.2.3 表示主功能 1 → 子功能 2 → 测试点 3且可进一步细分
3. 多级编号的标题格式按照级别增加例如“1. 读FIFO操作”应为三号标题格式 “1.1. 常规读取”应为四号标题格式;
4. 功能描述应清晰列出输入条件、处理过程和输出结果。
@ -291,4 +291,3 @@ rd', rs1'和rs2'寄存器受限于16位指令的位宽限制这几个寄
其中nzuimm\[5\:4\|9\:6\|2\|3\]的含义是:
···
下方展示了模板和两个验证案例:

View File

@ -82,9 +82,9 @@ out\_pd每条指令的预译码信息在F3Predecoder分析得到的是brTy
| 1\.3 | CFI指令类型判定 | JAL判定 | 对传入的JAL指令应该判定为类型2 |
| 1\.4 | CFI指令类型判定 | JALR判定 | 对传入的JALR指令应该判定为类型3 |
| 2\.1 | ret、call判定 | 非CFI和BR不判定 | 对传入的非CFI和BR指令都不应判定为call或者ret |
| 2\.2\.1\.1 | ret、call判定 | RVI\.JAL判定call | 对传入的RVC\.JAL指令当rd设置为1或5应当判定该指令为call |
| 2\.2\.1\.2 | ret、call判定 | RVI\.JAL例外 | 对传入的RVC\.JAL指令当rd设置为1和5之外的值不应当判定该指令为call或ret |
| 2\.2\.2 | ret、call判定 | RVC\.JAL不判定 | 对传入的RVI\.JAL指令无论什么情况都不能判定为call或ret |
| 2\.2\.1\.1 | ret、call判定 | RVC\.JAL判定call | 对传入的RVC\.JAL指令当rd设置为1或5应当判定该指令为call |
| 2\.2\.1\.2 | ret、call判定 | RVC\.JAL例外 | 对传入的RVC\.JAL指令当rd设置为1和5之外的值不应当判定该指令为call或ret |
| 2\.2\.2 | ret、call判定 | RVI\.JAL不判定 | 对传入的RVI\.JAL指令无论什么情况都不能判定为call或ret |
| 2\.3\.1\.1 | ret、call判定 | RVI\.JALR和rd为link | 传入RVI\.JALR指令并且rd为1或5无论其他取值都应判定为call |
| 2\.3\.1\.2 | ret、call判定 | RVI\.JALR且仅rs为link | 传入RVI\.JALR指令rd不为1和5rs为1或5应判定为ret |
| 2\.3\.1\.3 | ret、call判定 | RVI\.JALR无link | 对传入的JALR指令若rd和rs均不为link则不应判定为ret和cal |

View File

@ -26,13 +26,6 @@ RVI指令永远判断为合法。
对于RVC指令的判定详细内容参阅20240411的RISCV手册的26\.8节表格列出的指令条件。
## 常量说明
| 常量名 | 常量值 | 解释 |
| ---- | ---- | ---- |
| XLEN | 64 | 通用寄存器位宽决定指令扩展时使用rv32还是rv64还是rv128 |
| fLen | 64 | 香山支持d扩展故为64 |
## RVCExpander接口说明
### 输入接口
@ -299,8 +292,6 @@ addi的格式形如\| imm\[11\:0\] \| rs1 \| 000 \| rd \| 0010011 \|
lui指令的格式形如 \| imm[31:12] \| rd \| 0110111 \|
当立即数为0时这一字段reserved
当rd为0时为hint也可当作cli进行译码。
当rd为2时为addi16sp指令

View File

@ -148,8 +148,8 @@ PredChecker会对传入的预测块进行JALR预测错误预检查并修正指
| 3\.2\.2| 预测的跳转并非第一条 | 预测块中存在JALR指令但是BPU预测信息取的跳转指令在第一条JALR指令之后检查PredChecker是否能检测出JALR预测错误。 |
### 功能点4 更新指令有效范围向量和预测跳转的指令
PredChecker在检查出Jal/Ret/Jalr指令预测错误时,需要重新生成指令有效范围向量,
有效范围截取到Jal/Ret/Jalr指令的位置之后的bit全部置为0。
PredChecker在检查出Jal/Ret指令预测错误时需要重新生成指令有效范围向量
有效范围截取到Jal/Ret指令的位置之后的bit全部置为0。
同时还需要根据每条指令的预译码信息和BPU的预测信息修复预测跳转的结果。
所以,根据功能要求,我们可以划分出三类情况,分别是预测的有效范围和取用的跳转指令正确的情况,
@ -158,7 +158,7 @@ PredChecker在检查出Jal/Ret/Jalr指令预测错误时需要重新生成指
| 序号 | 名称 | 描述 |
|---------|-------------------------|---------------------------------------------------------------|
| 4\.1 | 有效范围无误 | 不存在任何错误的情况下PredChecker应当保留之前的预测结果。 |
| 4\.2 | RET、JAL、JALR预测错误引起的范围偏大 | 如果检测到了JAL、RET、JALR类的预测错误PredChecker应该将有效指令的范围修正为预测块开始至第一条跳转指令。同时应该将预测跳转的指令位置修正为预测块中的第一条跳转指令。 |
| 4\.2 | RET和JAL预测错误引起的范围偏大 | 如果检测到了JAL或RET类的预测错误PredChecker应该将有效指令的范围修正为预测块开始至第一条跳转指令。同时应该将预测跳转的指令位置修正为预测块中的第一条跳转指令。 |
| 4\.3 | 非CFI和无效指令引起的预测范围偏小 | 如果出现了非控制流指令和无效指令的误预测,不应该将预测跳转的指令重新修正到预测块中第一条跳转指令,因为后续会直接冲刷并重新从重定向的位置取指令,如果这里修正的话,会导致下一预测块传入重复的指令 |

View File

@ -4,7 +4,7 @@ linkTitle: IFU
weight: 12
---
**本文档参考[香山IFU设计文档](https://github.com/OpenXiangShan/XiangShan-Design-Doc/blob/master/docs/frontend/IFU/index.md)写成**
**本文档参考[香山IFU设计文档](https://github.com/OpenXiangShan/XiangShan-Design-Doc/blob/master/docs/frontend/IFU/IFU.md)写成**
本文档撰写的内容截至[c670557]
@ -14,25 +14,19 @@ weight: 12
<div class="ifu-ctx">
## 文档概述
本文档描述IFU的功能并根据功能给出测试点参考方便测试的参与者理解测试需求编写相关测试用例。
为方便验证参与者本文档中还额外给出了整体框图和流水级的示意图以及各个rtl接口的详细说明。此外本文档还给出了两个时序示例。
## 术语说明
| 名称 | 描述 |
| ------------------------------- | ------------------------------------------ |
| RVCRISC-V Compressed Instructions | RISC-V 手册"C"扩展规定的 16 位长度压缩指令 |
| RVIRISC-V Integer Instructions | RISC-V 手册规定的 32 位基本整型指令 |
| IFUInstruction Fetch Unit | 取指令单元 |
| FTQFetch Target Queue | 取指目标队列 |
| ICacheL1 Instruction Cache | 一级指令缓存 |
| IBufferInstruction Buffer | 指令缓冲 |
| CFIControl Flow Instruction | 控制流指令 |
| ITLBInstruction Translation Lookaside Buffer | 指令地址转译后备缓冲器 |
| InstrUncacheInstruction Ucache Module | 指令 MMIO 取指处理单元 |
| 缩写 | 全称 | 描述 |
| ------------ | ---------------------------------------- | ------------------------------------------ |
| RVC | RISC-V Compressed Instructions | RISC-V 手册"C"扩展规定的 16 位长度压缩指令 |
| RVI | RISC-V Integer Instructions | RISC-V 手册规定的 32 位基本整型指令 |
| IFU | Instruction Fetch Unit | 取指令单元 |
| FTQ | Fetch Target Queue | 取指目标队列 |
| ICache | L1 Instruction Cache | 一级指令缓存 |
| IBuffer | Instruction Buffer | 指令缓冲 |
| CFI | Control Flow Instruction | 控制流指令 |
| ITLB | Instruction Translation Lookaside Buffer | 指令地址转译后备缓冲器 |
| InstrUncache | Instruction Ucache Module | 指令 MMIO 取指处理单元 |
## 整体框图
@ -415,7 +409,7 @@ PredChecker还需要负责生成跳转和顺序目标。
| 序号 | 功能名称 | 测试点名称 | 描述 |
|------|---------|-------------|---------------------------------|
| 5\.8 | IFU_PREDCHECK_TARGETS | TARGETS | 随机提供译码信息,检测生成的跳转目标和顺序目标。 |
| 5\.8\.1| IFU_PREDCHECK_TARGETS | TARGETS | 随机提供译码信息,检测生成的跳转目标和顺序目标。 |
### 6. 前端重定向WB阶段
@ -454,13 +448,21 @@ PredChecker还需要负责生成跳转和顺序目标。
#### 7.1. 跨预测块32位指令处理
如果发现当前预测块的最后两个字节是一条RVI指令的开始则设置一个标识f3\_lastHalf\_valid告诉接下来的预测块含有后半条指令。
我们没有办法直接观察到这个标识,但是可以通过下一预测块的开始向量的首位来判断。
我们没有办法直接观察到这个标识,但是可以通过下一预测块来判定:
| 序号 | 功能名称 | 测试点名称 | 描述 |
|------|---------|-------------|---------------------------------|
| 7\.1\.1 | IFU_CROSS_BLOCK | NORMAL | 连续传入两个预测块其中有一条32位指令跨两个预测块后一个预测块的指令开始向量的首位应该为False |
#### 7.2. 跨预测块指令误判
但是,如果这一判断出现问题(比如当前预测块存在跳转),则需要进行流水线冲刷。
这一功能需要PredChecker子模块“配合”仅仅通过外部IO的修改很难触发这个防御机制实现起来比较麻烦但是还是列举一个测试点见后文总表
这一功能需要PredChecker子模块“配合”仅仅通过外部IO的修改很难触发这个防御机制实现起来比较麻烦但是还是列举一个测试点
| 序号 | 功能名称 | 测试点名称 | 描述 |
|------|---------|-------------|---------------------------------|
| 7\.2\.1 | IFU_CROSS_BLOCK | ERROR | 当IFU根据PredChecker修复的指令有效范围错判了跨预测块指令时需要将F3以外的流水级全部冲刷 |
### 8. 将指令码和前端信息送入IBufferF3流水级
@ -954,7 +956,7 @@ tdata包括下列成员
| 序号 | 功能名称 | 测试点名称 | 描述 |
| ----- |-----------------|---------------------|------------------------------------|
| 1 | [IFU_RCV_REQ](#1-接收ftq取指令请求f0流水级) | READY | IFU接收FTQ请求后设置ready |
| 1\.1 | [IFU_RCV_REQ](#1-接收ftq取指令请求f0流水级) | READY | IFU接收FTQ请求后设置ready |
| 2\.1\.1| [IFU_F1_INFOS](#21-f1流水级计算信息和切分指针) | PC | IFU接收FTQ请求后在F1流水级生成PC |
| 2\.1\.2| IFU_F1_INFOS | CUT_PTR | IFU接收FTQ请求后在F1流水级生成后续切取缓存行的指针 |
| 2\.2\.1 | [IFU_F2_INFOS](#22-f2流水级获取指令信息) | EXCP_VEC | IFU接收ICache内容后会根据ICache的结果生成属于每个指令的异常向量 |
@ -1016,15 +1018,15 @@ tdata包括下列成员
| 5\.7\.1\.1 | [IFU_PREDCHECK_TARGET_MISS](#57-目标地址预测错误检查) | NOP | 构造不存在跳转指令并且未预测跳转的预测信息作输入测试PredChecker是否会错检目标地址预测错误 |
| 5\.7\.1\.2 | IFU_PREDCHECK_TARGET_MISS | CORRECT | 构造存在有效跳转指令并且正确预测跳转的预测信息作为输入测试PredChecker是否会错检目标地址预测错误 |
| 5\.7\.2 | IFU_PREDCHECK_TARGET_CHECK | ERROR | 构造存在有效跳转指令的预测块和预测跳转但跳转目标计算错误的预测信息作为输入测试PredChecker能否检出目标地址预测错误 |
| 5\.8 | [IFU_PREDCHECK_TARGETS](#58-生成跳转和顺序目标) | TARGETS | 随机提供译码信息,检测生成的跳转目标和顺序目标。 |
| 5\.8\.1| [IFU_PREDCHECK_TARGETS](#58-生成跳转和顺序目标) | TARGETS | 随机提供译码信息,检测生成的跳转目标和顺序目标。 |
| 6\.1\.1 | [IFU_REDIRECT](#61-预测错误重定向) | JAL | 预测请求中存在JAL预测错误需要冲刷流水线 |
| 6\.1\.2 | IFU_REDIRECT | RET | 预测请求中存在RET预测错误需要冲刷流水线 |
| 6\.1\.3 | IFU_REDIRECT | JALR | 预测请求中存在JALR预测错误需要冲刷流水线 |
| 6\.1\.4 | IFU_REDIRECT | NON_CFI | 预测请求中存在非CFI预测错误需要冲刷流水线 |
| 6\.1\.5 | IFU_REDIRECT | INVALID | 预测请求中存在无效指令预测错误,需要冲刷流水线 |
| 6\.1\.6 | IFU_REDIRECT | TARGET_FAULT | 预测请求中存在跳转目标错误,需要冲刷流水线 |
| 7\.1 | [IFU_CROSS_BLOCK](#71-跨预测块32位指令处理) | NORMAL | 连续传入两个预测块其中有一条32位指令跨两个预测块后一个预测块的指令开始向量的首位应该为False |
| 7\.2 | IFU_CROSS_BLOCK | ERROR | 当IFU根据PredChecker修复的指令有效范围错判了跨预测块指令时需要将F3以外的流水级全部冲刷 |
| 7\.1\.1 | [IFU_CROSS_BLOCK](#71-跨预测块32位指令处理) | NORMAL | 连续传入两个预测块其中有一条32位指令跨两个预测块后一个预测块的指令开始向量的首位应该为False |
| 7\.2\.1 | IFU_CROSS_BLOCK | ERROR | 当IFU根据PredChecker修复的指令有效范围错判了跨预测块指令时需要将F3以外的流水级全部冲刷 |
| 8\.1\.1 | [IFU_TO_IBUFFER](#81-传送指令码和前端信息) | INSTRS | IFU向IBuffer传送扩展后的指令码 |
| 8\.1\.2 | IFU_TO_IBUFFER | EXCP | IFU向IBuffer传送每个指令的异常信息 |
| 8\.1\.3 | IFU_TO_IBUFFER | PD_INFO | IFU向IBuffer传递每个指令的预译码信息 |

View File

@ -0,0 +1,40 @@
---
title: 环境配置
linkTitle: 环境配置
weight: 12
---
## 推荐使用WSL2+Ubuntu22.04+GTKWave
我们推荐Windows10/11用户通过WSL2进行开发在此给出通过此方法进行环境配置的教程集锦仅供参考。如环境安装过程中出现任何问题欢迎在QQ群群号<b>976081653</b>)中提出,我们将尽力帮助解决。此页面将收集大家提出的所有环境配置相关问题并提供解决方案,欢迎随时向我们提问!
## 1、在Windows下安装WSL2Ubuntu22.04
参考资源:
--- 微软官方教程:[如何使用 WSL 在 Windows 上安装 Linux](https://learn.microsoft.com/zh-cn/windows/wsl/install)
--- 其它资源:[安装WSL2和Ubuntu22.04版本](https://blog.csdn.net/HHHBan/article/details/126843786)
## 2、打开WSL换源
推荐使用清华源:[清华大学开源软件镜像站-Ubuntu软件仓库](https://mirrors.tuna.tsinghua.edu.cn/help/ubuntu/)
## 3、配置验证环境
请参照[开放验证平台学习资源-快速开始-搭建验证环境](https://open-verify.cc/mlvp/docs/quick-start/installer/)配置picker环境。
## 4、使用 GTKWave
使用[重庆大学硬件综合设计实验文档-Windows原生GTKWave](https://co.ccslab.cn/tips/win-gtkwave/)给出的方法可以通过在WSL中输入 `gtkwave.exe wave.fst` 打开在Windows下安装的GTKWave。请注意gtkwave在使用中需要进入fst文件所在文件夹否则会出现无法
initialize的情况。
```bash
cd out
gtkwave.exe {test_name}.fst
cd ..
```
## 5、使用VSCode插件Live Server查看验证报告
成功安装插件Live Server后打开文件列表定位到 `/out/report/2025*-itlb-doc-*/index.html` 右键并选择 `Open With Live Server`,之后在浏览器中打开提示的端口(默认为`//localhost:5500`)即可。

View File

@ -58,8 +58,8 @@ TLB 应当正常接收来自 IFU 与 ICache 的取指令请求,查找自身页
| No. | 名称 | 说明 |
|--------|-------------------|------------------------|
| 1.1 | 接收来自 ICache 请求requestor0、1 | ITLB 根据请求查找自身缓存 TLBuffer返回 hit/miss 结果 |
| 1.2 | 接收来自 IFU 请求requestor2 | 注意此处为阻塞式访问,每次访问后若 miss 应当 reset 后再次访问 |
| 1.1 | 接收来自 IFU 请求requestor0、1 | ITLB 根据请求查找自身缓存 TLBuffer返回 hit/miss 结果 |
| 1.2 | 接收来自 ICache 请求requestor2 | 注意此处为阻塞式访问,每次访问后若 miss 应当 reset 后再次访问 |
| 1.3 | 接收条件判断requestor0、1 | valid 信号 |
| 1.4 | 接受条件判断requestor2 | valid-ready 信号 |

View File

@ -1,223 +0,0 @@
---
title: 果壳Cache文档案例
linkTitle: 果壳Cache文档案例
weight: 10
---
本文档将以[果壳L1Cache](https://github.com/OSCPU/NutShell/blob/fc12171d929e7e589fab9f794ab63ce12e6c594e/src/main/scala/nutcore/mem/Cache.scala)作为案例,展示一个具有相当复杂度的模块的验证说明文档例子(请一定同提交的验证报告区分开来)。
# 果壳L1Cache验证文档
## 文档概述
本文档针对NutShell L1Cache的验证需求撰写通过对其功能进行描述并依据功能给出参考测试点从而帮助验证人员编制测试用例。
果壳NutShell是一款由5位中国科学院大学本科生设计的基于RISC-V RV64开放指令集的顺序单发射处理器([NutShell·Github](https://github.com/OSCPU/NutShell)), 隶属于国科大与计算所“一生一芯”项目。而果壳CacheNutShell Cache是其缓存模块采用可定制化设计L1 Cache和L2 Cache采用相同的模板生成只需要调整参数具体来说L1 Cache指令Cache和数据Cache大小为32KBL2 Cache大小为128KB, 在整体结构上果壳Cache采用三级流水的结构。
本次验证的目标是L1 Cache即一级缓存。
## 术语说明
| 名称 | 定义 |
| ------- | ---|
| MMIOMemory-Mapped Input/Output | 内存映射IO |
| 写回 | Cache需要进行替换时会将脏替换块写回内存 |
|关键字优先方案 | 缺失发生时系统会优先获取CPU所需要的当前指令或数据所对应的字 |
## 前置知识
### Cache的层次结构
Cache有三种主要的组织方式直接映射Direct-MappedCache、组相连Set-AssociativeCache和全相连Fully-AssociativeCache。对于物理内存中的一个数据如果在Cache中只有一个位置可以存放它这就是直接映射Cache如果有多个位置可以存放这个数据这就是组相连Cache如果Cache中的任何位置都可以存放这个数据这就是全相连Cache。
直接映射Cache和全相连Cache实际上是组相连Cache的两种特殊情况。现代处理器中的Cache通常属于这三种方式中的一种。例如翻译后备缓冲区TLB和Victim Cache多采用全相连结构而普通的指令缓存I-Cache和数据缓存D-Cache则采用组相连结构。当处理器需要执行一个指令时它会首先查找该指令是否在I-Cache中。如果在则直接从I-Cache中读取指令并执行如果不在则需要从内存中读取指令到I-Cache中再执行。与I-Cache类似当处理器需要读取或写入数据时会首先查找D-Cache。如果数据在D-Cache中则直接读取或写入如果不在则需要从内存中加载数据到D-Cache中。与I-Cache不同的是D-Cache需要考虑数据的一致性和写回策略。为了保证数据的一致性当数据在D-Cache中被修改后需要同步更新到内存中。
![composition](composition.png)
### Cache的写入
在执行写数据时如果只是向D-Cache中写入数据而不改变其下级存储器中的数据就会导致D-Cache和下级存储器对于同一地址的数据不一致non-consistent。为了保持一致性一般Cache在写命中状态下采用两种写入方式
1写通Write Through数据写入D-Cache的同时也写入其下级存储器。然而由于下级存储器的访问时间较长而存储指令的频率较高频繁地向这种较慢的存储器中写入数据会降低处理器的执行效率。
2写回Write Back数据写入D-Cache后只是在Cache line上做一个标记并不立即将数据写入更下级的存储器。只有当Cache中这个被标记的line要被替换时才将其写入下级存储器。这种方式能够减少向较慢存储器写入数据的频率从而获得更好的性能。然而这种方式会导致D-Cache和下级存储器中许多地址的数据不一致给存储器的一致性管理带来一定的负担。
D-Cache处理写缺失一般有两种策略
1**非写分配Non-Write Allocate**直接将数据写入下级存储器而不将其写入D-Cache。这意味着当发生写缺失时数据会直接写入到下级存储器而不会经过D-Cache。
2**写分配Write Allocate**在发生写缺失时会先将相应地址的整个数据块从下级存储器中读取到D-Cache中然后再将要写入的数据合并到这个数据块中最终将整个数据块写回到D-Cache中。这样做的好处是可以在D-Cache中进行更多的操作但同时也增加了对内存的访问次数和延迟。
写通Write Through和非写分配Non-Write Allocate将数据直接写入下级存储器而写回Write Back和写分配Write Allocate则会将数据写入到D-Cache中。通常情况下D-Cache的写策略搭配为写通+非写分配或写回+写分配。
**写通示意图**
<div style="text-align:center">
<img src="Write-through_with_no-write-allocation.png" alt="write-through" width="400" />
<b>写通示意图</b>
</div>
<div style="text-align:center">
<img src="Write-back_with_write-allocation.png" alt="write-back" width="400" />
<b>写回示意图</b>
</div>
### 替换策略
读写D-Cache发生缺失时需要从对应的Cache Set中找到一个cache行来存放从下级存储器中读出的数据如果此时这个Cache Set内的所有Cache行都已经被占用了那么就需要替换掉其中一个如何从这些有效的Cache行找到一个并替换它这就是替换策略本节介绍几种最常用的替换策略。
**近期最少使用法**会选择最近被使用次数最少的Cache行因此这个算法需要追踪每个Cache行的使用情况这需要为每个Cache行都设置一个年龄age部分每当一个Cache行被访问时它对应的年龄部分就会增加或者减少其他Cache行的年龄值这样当进行替换时年龄值最小的那个Cache行就是被使用次数最少的了会选择它进行替换。
**随机替换算法**硬件实现简单这种方法发生缺失的频率会更高一些但是随着Cache容量的增大这个差距是越来越小的。在实际的设计中很难实现严格的随机一般采用一种称为时钟算法clock algorithm的方法实现近似的随机它的工作原理本质上是一个时钟计数器计数器的宽度由Cache的路的个数决定当要替换时就根据这个计数器选择相应的行进行替换。这种方法硬件复杂度较低也不会损失较多的性能因此是一种折中的方法。
## 整体框图和流水级
以下是L1Cache的整体框图和流水级示意
![Cache](Cache.png)
## 子模块列表
以下是NutShell L1Cache的一些子模块
| 子模块 | 描述 |
| -------------------- | ------------------- |
| s1 | 缓存阶段1 |
| s2 | 缓存阶段2 |
| s3 | 缓存阶段3 |
| metaArray | 以数组形式存储元数据 |
| dataArray | 以数组形式存储缓存数据 |
| arb | 总线仲裁器 |
上下游通信总线采用SimpleBus总线包含了req和resp两个通路其中req通路的cmd信号表明请求的操作类型可以通过检查该信号获得访问类型。SimpleBus总线共有七种操作类型由于NutShell文档未涉及probe和prefetch操作在验证中只出现五种操作read、write、readBurst、writeBurst、writeLast前两种为字读写后三种为Burst读写即一次可以操作多个字。
<mrs-functions>
## 模块功能说明
Cache的功能是降低访存的时间开销其功能本质上和内存是一致的。也就是说不论是向Cache存数还是取数其都应该和直接向内存存取的数是一样的。
因此Cache的基础读写功能将成为我们的第一个功能点。
进一步访问Cache的地址空间分为MMIO和内存。其中访问MMIO的地址空间时Cache一定会Miss然后将请求转发到MMIO端口上。而访问内存的地址空间时Cache则会根据该地址所在的Cache Line是否在Cache中而触发Hit或者Miss。Hit则直接返回响应Miss则会将请求转发到内存端口。如果被替换的受害者行之前被写过是dirty的则要先将受害者行写回write-back内存否则直接从内存加载缺失的Cache Line重填refill回Cache。
### 1. 内存备份
Cache的功能本质上和内存是一致的所以不管向Cache存或取数据本质上都应该和从内存存取的数一样。
据此我们为这一功能点安排了一个测试点即Cache应当为内存的备份。在实际测试过程中必须同时考虑读写两方面的一致性。
### 2. MMIO
Cache会根据地址所在的区间判断是否发生MMIO请求。
#### 2.1. MMIO读写
如果发生MMIO请求则会将请求转发到MMIO的端口上而不会发生Cache行的读写。此外MMIO请求不是Burst请求每次只会写入或读出一个地址的数据而不是一个Cache行的数据。因此在MMIO端口上不应当观测到Burst的请求类型。
据此,我们可以设计下述两个测试点:
| 序号 | 功能名称 | 测试点名称 | 描述 |
| ----- |-----------------|---------------------|------------------------------------|
| 2\.1\.1 | CACHE_MMIO_RW | FORWARD | Cache接收到MMIO空间的请求时不应发生读写而是直接转发给MMIO端口 |
| 2\.1\.2 | CACHE_MMIO_RW | NO_BURST | Cache接收到MMIO空间的请求时MMIO端口接收到的Cache请求不应为BURST类型 |
### 2.2. MMIO阻塞
NutShell手册指出在检测出MMIO请求后会阻塞流水线。
因此我们将设计这一测试点当MMIO请求发出后应当检查流水线是否阻塞。
### 3. Cache命中
NutShell的Cache采用写回策略因此在写命中时需要标记脏块后续发生缓存行替换时再将对应的缓存行写回内存。
同时,因为采用写回方式,所以,即使写命中也不需要同内存进行交互,因此收到回复的周期数更少。
#### 3.1. 写命中
由于果壳Cache采用写回策略因此在发生写命中时需要标记脏位后续还要写回内存中。据此可以设置一个测试点。
#### 3.2. 命中时序
命中发生时,即使是写命中,也无需写回或者重填,因此,回复的时间会更短一些。
以下是本功能点的所有测试点:
| 序号 | 功能名称 | 测试点名称 | 描述 |
| ----- |-----------------|----------------|----------------|
| 3\.2\.1 | CACHE_HIT | WRITE | Cache写命中时应设置脏位 |
| 3\.2\.2 | CACHE_HIT | SHORTER | Cache写命中时回复的周期应该更少 |
### 4. Cache缺失
为了创造Cache Miss的测试环境首先需要通过一系列的Load操作先将Cache填满。后续需要触发Cache Miss时只需要访问上述Load覆盖范围之外的地址即可。
#### 4.1. 缺失通用行为
发生Cache Miss时会阻塞流水线同时NutShell Cache重填时采用**关键字优先方案**即缺失发生时系统会优先获取CPU所需要的当前指令或数据所对应的字。因此Cache向内存请求数据时发出的首个地址应当是向Cache发出请求时的地址。例如假设向Cache发出0x1000地址的读请求此时发生Cache MissCache会向内存发出读请求这个请求的首地址应当是0x1000。显然Cache缺失时回复的时间会更长。
从而,我们可以划分如下的测试点:
| 序号 | 功能名称 | 测试点名称 | 描述 |
| ----- |-----------------|----------------|----------------|
| 4\.1\.1 | CACHE_MISS_COMMON | BLOCK | 发生缺失时,也会阻塞流水线 |
| 4\.1\.2 | CACHE_MISS_COMMON | CRITICAL_WORD | Cache缺失时Cache发出请求的首个地址应当是向Cache请求的地址 |
| 4\.1\.3 | CACHE_MISS_COMMON | LONGER | Cache缺失时回复的时间会更长 |
#### 4.2. 脏块写回
当需要替换的Cache块是脏块时首先会进行写回的操作。
在进行测试时我们首先需要创建脏块的环境由于NutShell Cache采用**随机替换**的策略因此我们考虑将整个Cache都设置成脏块。操作也是简单的在上述的Load的基础上只需要在每个CacheLine的起始地址进行一次Store操作即可。
#### 4.3. 干净块不写回
当需要替换的Cache块是干净的时不会写回这个Cache块。
</mrs-functions>
## 常量说明
| 常量名 | 常量值 | 解释 |
| ---- | ---- | ---- |
| 缓存行大小 | 64 | 以字节为单位的缓存行大小 |
| L1Cache大小 | 32 | L1Cache的总容量单位为千字节 |
## 接口说明
|信号|说明|
| --- | --- |
| clock<br>reset | 时钟<br>复位信号|
| io\_flush<br>io\_empty <br> io\_in\_* |<br><br> 请求总线信号(req \& resp) |
| io\_out\_mem\_* <br> io\_mmio\_* <br> io\_out\_coh\_* <br>victim\_way\_mask | cache向内存请求的总线信号<br>cache向MMIO请求的总线信号 <br> 一致性相关的信号 <br> 受害者相关信号即被替换的cache块相关信息 |
## 测试点总表
实际使用下面的表格时,请用有意义的英文大写的功能名称和测试点名称替换下面表格中的名称
<mrs-testpoints>
| 序号 | 功能名称 | 测试点名称 | 描述 |
| ----- |-----------------|---------------------|------------------------------------|
| 1 | CACHE_BACKUP | BACKUP | 对Cache的存取应该同对内存的存取一致 |
| 2\.1\.1 | CACHE_MMIO_RW | FORWARD | Cache接收到MMIO空间的请求时不应发生读写而是直接转发给MMIO端口 |
| 2\.1\.2 | CACHE_MMIO_RW | NO_BURST || 1\.2\.2 | FUNCTION_1_2 | TESTPOINT_Y | 功能1\.2的测试点Y使用时请替换为您的测试点的输入输出和判断方法 | Cache接收到MMIO空间的请求时MMIO端口接收到的Cache请求不应为BURST类型 |
| 2\.2 | CACHE_MMIO | BLOCK | MMIO请求发生时应当阻塞流水线 |
| 3\.1 | CACHE_HIT | WRITE | Cache写命中时应设置脏位 |
| 3\.2 | CACHE_HIT | SHORTER | Cache写命中时回复的周期应该更少 |
| 4\.1\.1 | CACHE_MISS_COMMON | BLOCK | 发生缺失时,也会阻塞流水线 |
| 4\.1\.2 | CACHE_MISS_COMMON | CRITICAL_WORD | Cache缺失时Cache发出请求的首个地址应当是向Cache请求的地址 |
| 4\.1\.3 | CACHE_MISS_COMMON | LONGER | Cache缺失时回复的时间会更长 |
| 4\.2 | CACHE_MISS | DIRTY | Cache缺失时Cache发出请求的首个地址应当是向Cache请求的地址 |
| 4\.3 | CACHE_MISS | CLEAN | Cache缺失时回复的时间会更长 |
</mrs-testpoints>

View File

@ -1,98 +0,0 @@
---
title: 环境配置
linkTitle: 环境配置
weight: 12
---
## WSL2+Ubuntu22.04+GTKWaveWindows用户推荐使用
我们推荐 Windows10/11 用户通过 WSL2 进行开发在此给出通过此方法进行环境配置的教程集锦仅供参考。如环境安装过程中出现任何问题欢迎在QQ群群号<b>976081653</b>)中提出,我们将尽力帮助解决。此页面将收集大家提出的所有环境配置相关问题并提供解决方案,欢迎随时向我们提问!
### 1、在 Windows 下安装 WSL2Ubuntu22.04
参考资源:
--- 微软官方教程:[如何使用 WSL 在 Windows 上安装 Linux](https://learn.microsoft.com/zh-cn/windows/wsl/install)
--- 其它资源:[安装WSL2和Ubuntu22.04版本](https://blog.csdn.net/HHHBan/article/details/126843786)
### 2、打开 WSL换源
推荐使用清华源:[清华大学开源软件镜像站-Ubuntu软件仓库](https://mirrors.tuna.tsinghua.edu.cn/help/ubuntu/)
### 3、配置验证环境
请参照[开放验证平台学习资源-快速开始-搭建验证环境](https://open-verify.cc/mlvp/docs/quick-start/installer/)配置环境。
以下是示例方法:
```bash
# 基本工具包
cd ~ && sudo apt-get update
sudo apt-get install -y build-essential cmake git wget curl lcov autoconf flex bison libgoogle-perftools-dev gcc python3.11 python3.11-dev python3.11-distutils python3-pip python-is-python3
rm -rf /var/lib/apt/lists/*
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1
curl -sS https://bootstrap.pypa.io/get-pip.py | python3.11
# verilator
git clone https://github.com/verilator/verilator.git
cd verilator
git checkout v4.218 # 4.218为最低需求版本,可自行查看并选择新版本
autoconf && ./configure && make -j$(nproc) && make install
cd .. && rm -rf verilator
# verible
curl -sS https://github.com/chipsalliance/verible/releases/download/v0.0-3946-g851d3ff4/verible-v0.0-3946-g851d3ff4-linux-static-x86_64.tar.gz -o /tmp/
tar -zxvf /tmp/verible-v0.0-3946-g851d3ff4-linux-static-x86_64.tar.gz -C /tmp/
copy /tmp/verible-v0.0-3946-g851d3ff4/bin/verible-* /usr/local/bin/
sudo chmod +x /usr/local/bin/verible-*
rm /tmp/verible-*
# pcre2
curl -sS https://github.com/PCRE2Project/pcre2/releases/download/pcre2-10.45/pcre2-10.45.tar.gz -o /tmp/
tar -zxvf /tmp/pcre2-10.45.tar.gz -C /tmp/
cd /tmp/pcre2-10.45
./configure --prefix=/usr/local && make -j$(nproc) && make install
rm -rf /tmp/pcre2* && cd ~
# swig
# 注意不要使用 apt install swig将会下载不符合最低要求的版本 4.0.2
curl -sS http://prdownloads.sourceforge.net/swig/swig-4.3.0.tar.gz -o /tmp/
tar -zxvf /tmp/swig-4.3.0.tar.gz -C /tmp/
cd /tmp/swig-4.3.0
./configure --prefix=/usr/local && make -j$(nproc) && make install
rm -rf /tmp/swig* && cd ~
# 更新本地包
apt-get update && apt-get -y upgrade
# picker
git clone https://github.com/XS-MLVP/picker.git --depth=1
cd picker
make init && make && make install
cd .. && rm -rf picker
# UnityChipForXiangShan
git clone https://github.com/XS-MLVP/UnityChipForXiangShan.git
cd UnityChipForXiangShan
pip3 install --no-cache-dir -r requirements.txt
```
### 4、使用 GTKWave 查看波形文件
使用[重庆大学硬件综合设计实验文档-Windows原生GTKWave](https://co.ccslab.cn/tips/win-gtkwave/)给出的方法可以通过在WSL中输入 `gtkwave.exe wave.fst` 打开在 Windows 下安装的 GTKWave。请注意gtkwave在使用中需要进入 fst 文件所在文件夹,否则会出现无法
initialize 的情况。
```bash
gtkwave.exe /out/{test_name}.fst
```
### 5、使用 VSCode 插件 Live Server 查看验证报告
成功安装插件Live Server后打开文件列表定位到 `/out/report/2025*-itlb-doc-*/index.html` 右键并选择 `Open With Live Server`,之后在浏览器中打开提示的端口(默认为`//localhost:5500`)即可。
## docker一键部署方案MAC用户可用
我们提供了 MAC 可用的 docker 环境,已在 Docker Hub 发布,名称为 `unitychip-env`。安装 Docker Desktop 后在命令行使用以下命令即可获取并打开开发环境。需下载约 500MB 的镜像,展开后约占用 1GB 空间。
```bash
docker search unitychip-env
docker pull dingjunbi/unitychip-env && docker run unitychip-env
cd UnityChipForXiangShan && git pull
```
[Docker Hub使用文档](https://docs.docker.com/docker-hub/)
[Dockerdocker 拉取镜像及查看pull下来的image在哪里](https://blog.csdn.net/sj349781478/article/details/105267887/)

View File

@ -1,142 +0,0 @@
---
title: FTQ顶层
linkTitle: 01_FTQ顶层
weight: 12
---
# 简述
在FTQ概述中我们已经知道了FTQ的作用就是多个模块交互的中转站大致了解了它接受其他模块的哪些信息它如何接受并存储这些信息在FTQ中并如何把这些存储信息传递给需要的模块。
下面我们来具体了解一下FTQ与其他模块的交互接口我们会对这种交互有一个更具体的认识。
# IO一览
## 模块间IO
- **fromBpu接受BPU预测结果的接口BpuToFtqIO**
- **fromIfu接受IFU预译码写回的接口IfuToFtqIO**
- **fromBackend接受后端执行结果和commit信号的接口CtrlToFtqIO**
- **toBpu向BPU发送训练信息和重定向信息的接口FtqToBpuIO**
- **toIfu向IFU发送取值目标和重定向信息的接口FtqToIfuIO**
- toICache向ICache发送取值目标的接口FtqToICacheIO
- **toBackend向后端发送取值目标的接口FtqToCtrlIO**
- toPrefetch向Prefetch发送取值目标的接口FtqToPrefetchIO
- mmio
## 其他
上述是主要的IO接口此外还有一些用于性能统计的IO接口比如对BPU预测正确和错误结果次数进行统计并进行转发的IO, 还有转发BPU各预测器预测信息的IO。
# [BpuToFtqIO](https://open-verify.cc/xs-bpu/docs/ports/02_global_ports/)
# IfuToFtqIO
我们知道从IFU我们会得到预译码信息和重定向信息而后者其实也是从预译码信息中生成。所以从IFU到FTQ的接口主要就是用来传递预译码信息的
- pdWbIFU向FTQ写回某个FTQ项的预译码信息
- 接口类型:**PredecodeWritebackBundle**
- 信号列表:
- pc一个分支预测块覆盖的预测范围内的所有pc
- 接口类型Vec(PredictWidth, UInt(VAddrBits.W))
- pd预测范围内所有指令的预译码信息
- 接口类型Vec(PredictWidth, new PreDecodeInfo)
- PreDecodeInfo每条指令的预译码信息
- 接口类型PreDecodeInfo
- 信号列表:
- valid预译码有效信号
- 接口类型Bool
- isRVC是RVC指令
- 接口类型Bool
- brType跳转指令类型
- 接口类型UInt(2.W)
- 说明根据brType的值判断跳转指令类型
- b01对应分支指令
- b10对应jal
- b11对应jalr
- b00对应非控制流指令
- isCall是Call指令
- 接口类型Bool
- isRet是Ret指令
- 接口类型Bool
- ftqIdxFTQ项的索引标记写回到哪个FTQ项
- 接口类型FtqPtr
- ftqOffset由BPU预测结果得到的在该指令块中指令控制流指令的位置指令控制流指令就是实际发生跳转的指令
- 接口类型UInt(log2Ceil(PredictWidth).W)
- misOffset预译码发现发生预测错误的指令在指令块中的位置
- 接口类型ValidUndirectioned(UInt(log2Ceil(PredictWidth).W))
- 说明它的valid信号拉高表示该信号有效也就说明存在预测错误会引发重定向
- cfiOffset由预译码结果得到的在该指令块中指令控制流指令的位置指令控制流指令就是实际发生跳转的指令
- 接口类型ValidUndirectioned(UInt(log2Ceil(PredictWidth).W))
- target该指令块的目标地址
- 接口类型UInt(VAddrBits.W)
- 说明:所谓目标地址,即在指令块中有控制流指令时,控制流指令的地址,在没有控制流指令时,指令块顺序执行,该指令块最后一条指令的下一条指令
- jalTargetjal指令的跳转地址
- 接口类型UInt(VAddrBits.W)
- instrRange有效指令范围
- 接口类型Vec(PredictWidth, Bool())
- 说明:表示该条指令是不是在这个预测块的有效指令范围内(第一条有效跳转指令之前的指令)
# CtrlToFtqIO
后端控制块向FTQ发送指令提交信息后端执行结果的接口。
- rob_commits一个提交宽度内的RobCommitInfo信息。
- 接口类型Vec(CommitWidth, Valid(new RobCommitInfo))
- 详情链接RobCommitInfo
- redirect后端提供重定向信息的接口。
- 接口类型Valid(new Redirect)
- 详情链接Redirect
- ftqIdxAhead提前重定向的FTQ指针将要重定向的FTQ项的指针提前发送
- 接口类型: Vec(BackendRedirectNum, Valid(new FtqPtr))
- 说明:虽然有三个接口,但实际上只用到了第一个接口,后面两个弃用了
- ftqIdxSelOH独热码本来是依靠该信号从提前重定向ftqIdxAhead中选择一个但现在只有一个接口了独热码也只有一位了。
- 接口类型Valid(UInt((BackendRedirectNum).W))
- 说明为了实现提前一拍读出在ftq中存储的重定向数据减少redirect损失后端会向ftq提前一拍相对正式的后端redirect信号传送ftqIdxAhead信号和ftqIdxSelOH信号。
# [FtqToBpuIO](https://open-verify.cc/xs-bpu/docs/ports/02_global_ports/)
# FtqToICacheIO
FTQ向IFU发送取值目标ICache是指令缓存如果取值目标在ICache中命中由ICache将指令发给IFU
- reqFTQ向ICache发送取值目标的请求
- 接口类型Decoupled(new FtqToICacheRequestBundle)
- 信号列表:
- pcMemReadFTQ针对ICache发送的取值目标ICache通过5个端口同时读取取指目标
- 接口类型Vec(5, new FtqICacheInfo)
- FtqICacheInfo: FTQ针对ICache发送的取值目标
- 信号列表:
- ftqIdx指令块在FTQ中的位置索引
- 接口类型FtqPtr
- startAddr预测块起始地址
- 接口类型UInt(VAddrBits.W)
- nextlineStart起始地址所在cacheline的下一个cacheline的开始地址
- 接口类型UInt(VAddrBits.W)
- 说明通过startAddr(blockOffBits - 1)这一位也就是块内偏移地址的最高位可以判断该预读取pc地址是位于cacheline的前半块还是后半块若是前半块由于取值块大小为cacheline大小的一半不会发生跨cacheline行
- readValid: 对应5个pcMemRead是否有效
- backendException是否有后端异常
# FtqToCtrlIO
FTQ向后端控制模块转发PC后端将这些pc存储在本地之后直接在本地读取这些pc
**写入后端pc mem**
- pc_mem_wenFTQ向后端pc存储单元pc_mem写使能信号
- 接口类型Output(Bool())
- pc_mem_waddr写入地址
- 接口类型Output(UInt(log2Ceil(FtqSize).W))
- pc_mem_wdata写入数据是一个指令块的取值目标
- 接口类型Output(new Ftq_RF_Components)详见FTQ子队列相关介绍
**写入最新目标**
- newest_entry_en是否启用
- 接口类型Output(Bool())
- newest_entry_target最新指令块的跳转目标
- 接口类型Output(UInt(VAddrBits.W))
- newest_entry_ptr最新指令块的索引值
- 接口类型: Output(new FtqPtr)
# FtqToPrefetchIO
- reqFTQ向Prefetch发送取值目标的请求
- 接口类型FtqICacheInfo
- flushFromBPU: 来自BPU的冲刷信息
- 接口类型BpuFlushInfo
- 信号列表:
- s2 BPU预测结果重定向注意这种重定向是BPU自己产生的与其他类型要做区分发生在s2阶段时此阶段的分支预测块的索引
- 接口类型Valid(new FtqPtr)
- 说明valid信号有效时说明此时s2流水级分支预测结果与其s1阶段预测结果不一致产生s2阶段重定向
- s3BPU预测结果重定向注意这种重定向是BPU自己产生的与其他类型要做区分发生在s3阶段时此阶段的分支预测块的索引
- 接口类型Valid(new FtqPtr)
- 说明与s2类似
- 说明发生预测结果重定向的时候预取单元和IFU都可能会被冲刷比如如果发生s2阶段重定向FTQ会比较发给IFU req接口中的ftqIdx和s2阶段预测结果的ftqIdx如果s2阶段的ftqIdx不在req的ftqIdx之后这意味着s2阶段产生的预测结果重定向之前的错误预测结果s1阶段预测结果被发给IFU进行取指了为了消除这种错误需要向IFU发送s2阶段flush信号。
- backendException后端执行发生的异常
- 接口类型UInt(ExceptionType.width.W)
- 说明:表示后端执行时发生异常的类型,有这样几种类型的异常:
```scala
def none: UInt = "b00".U(width.W)
def pf: UInt = "b01".U(width.W) // instruction page fault
def gpf: UInt = "b10".U(width.W) // instruction guest page fault
def af: UInt = "b11".U(width.W) // instruction access fault
```

View File

@ -1,292 +0,0 @@
---
title: FTQ子队列
linkTitle: 02_FTQ子队列
weight: 12
---
## 文档概述
***请注意:从本篇开始,就涉及待验证的功能点和测试点了***
在之前的介绍中我们采用FTQ项这个术语描述描述FTQ队列中的每一个元素实际上这只是一种便于抽象的说法。
实际上的FTQ队列是由好多个子队列共同构成的一些子队列维护一类信息另一些子队列维护另一类信息相同ftqIdx索引的子队列信息共同构成一个完整的FTQ项。
为什么要把它们分开成多个子队列呢因为某些模块只需要FTQ项中的某一些信息比如IFU想要取值目标它只需要专门存储取值目标的子队列提供的信息就行了。另外在我们更改FTQ项的内容时也只需要写入需要更新的子队列比如IFU预译码写回时只需要写回专门存储预译码信息的队列了。
下面来介绍一些FTQ的主要子队列以及它们内部存储的数据结构。此外FTQ还有一些存储中间状态的更小的队列
## 术语说明
| 名称 | 定义 |
| -------------------------------------------------------- | ------------------------------------------------------ |
| [FTB项](https://open-verify.cc/xs-bpu/docs/ports/00_ftb/) | 分支预测结果的基本组成项,包含对预测块中分支指令和跳转指令的预测 |
| 取指目标 | 一个预测块内包含的所有指令PC当然它不是直接发送所有PC而是发送部分信号接收方可由该信号推出所有PC |
## 子模块列表
| 子模块 | 描述 |
| -------------------- | --------------------------- |
| ftq_redirect_mem<br> | 重定向存储子队列,存储来自分支预测结果的重定向信息 |
| ftq_pd_mem | 预译码存储子队列存储来自IFU的对指令块的预译码信息 |
| ftb_entry_mem | FTB项存储子队列存储自分支预测结果中的ftb项 |
| ftq_pc_mem | 取指目标子队列,存储来自分支预测结果的取指目标 |
## 模块功能说明
### 1. ftq_redirect_mem存储重定向信息
ftq_redirect_mem是香山ftq的一个子队列。它记录了重定向需要的一些信息帮助重定向回正确状态这些信息来自于BPU分支预测中的RAS预测器以及顶层的分支历史指针如果想要了解可以参考BPU的RAS子文档了解如何通过这些信息回溯到之前的状态。
它是一个寄存器堆由64FtqSize个表项Ftq_Redirect_SRAMEntry构成。支持同步读写操作。有3个读端口和1个写端口每个读端口负责与不同的模块交互。
#### 1.1 ftq_redirect_mem读操作
- 读操作:
- 输入:
- 需要使能ren这是一个向量可指定任意读端口可读
- 对应接口ren
- 从任意读端口中输入要读取的元素在ftq_redirect_mem中的地址这是一个从0到ftqsize-1的索引
- 对应接口raddr
- 输出:
- 从发起输入的读端口对应的读出端口中读出Ftq_Redirect_SRAMEntry。
- 对应接口rdata
#### 1.2 ftq_redirect_mem写操作
- 写操作
- 输入:
- 需要使能wen可指定写端口可写
- 对应接口wen
- 向写端口中输入要写入的元素在ftq_redirect_mem中的地址这是一个从0到ftqsize-1的索引
- 对应接口waddr
- 向wdata中写入Ftq_Redirect_SRAMEntry
- 对应接口wdata
- 多端口读:可以从多个读端口读取结果
*每个子队列的读写基本都是类似的,后面不再赘述*
### Ftq_Redirect_SRAMEntry
ftq_redirect_mem存储的表项。继承自SpeculativeInfo存储RAS预测器相关重定向信息根据这些信息回溯到之前的状态
- sc_disagree统计分支指令在sc预测器中预测是否发生错误
- 接口类型Some(Vec(numBr, Bool()))
- 说明Option 类型表明这个值可能不存在在非FPGA平台才有否则为none
- 信号列表:
- SpeculativeInfo推测信息帮助BPU在发生重定向的时候回归正常的状态
- 接口列表:
- histPtr重定向请求需要恢复的全局历史指针可参见BPU顶层文档了解详情
- 接口类型CGHPtr
- 说明以下都属于RAS重定向信息,可参见BPU文档了解如何利用这些信息进行重定向
- ssp重定向请求指令对应的 RAS 推测栈栈顶在提交栈位置的指针
- 接口类型UInt(log2Up(RasSize).W)
- sctr重定向请求指令对应的 RAS 推测栈栈顶递归计数 Counter
- 接口类型RasCtrSize.W
- TOSW重定向请求指令对应的 RAS 推测栈(队列)写指针
- 接口类型RASPtr
- TOSR重定向请求指令对应的 RAS 推测栈(队列)读指针
- 接口类型RASPtr
- NOS重定向请求指令对应的 RAS 推测栈(队列)读指针
- 接口类型RASPtr
- topAddr
- 接口类型UInt(VAddrBits.W)
| 序号 | 功能名称 | 测试点名称 | 描述 |
| ---- | ---------------- | ----- | ------------------------------------ |
| 1\.1 | FTQ_REDIRECT_MEM | WRITE | 向单端口输入wenwaddr决定是否写入以及写入地址写入wdata |
| 1\.2 | FTQ_REDIRECT_MEM | READ | 向多端口中输入renraddr决定是否读以及读取地址从rdata读取 |
### 2. ftq_pd_mem存储预译码信息
由64FtqSize个表项Ftq_pd_Entry构成。支持同步读写操作。有2个读端口和1个写端口。具有读写使能信号。
存储来自IFU预译码的写回信息它是一个寄存器堆由64FtqSize个表项Ftq_pd_Entry构成。有2个读端口和1个写端口。
ftq_pd_mem直接接收来自IfuToFtqIO的信号从中获取Ftq_pd_Entry表示一个指令块对应的预译码信息表项。读取时获取预测块内某条指令的预测信息
#### Ftq_pd_Entry
- brMask一个指令预测宽度内16条rvc指令的指令块中哪些指令是分支指令
- 接口类型Vec(PredictWidth, Bool())
- jmpInfojump信息其值对应不同的jmp指令类型表示指令块内jmp指令类型
- 接口类型ValidUndirectioned(Vec(3, Bool()))
- 说明  jumpinfo有效的时候第0位是0表示jal指令第0位是1表示jalr指令第1位是1表示call指令第二位是1表示ret指令。
- jmpOffsetjmp指令在指令预测块中的偏移地址
- 接口类型: UInt(log2Ceil(PredictWidth).W)
- rvcMask一个预测块内的指令16条rvc指令哪些是rvc指令
- 接口类型Vec(PredictWidth, Bool())
### 2.1 ftq_pd_mem写操作
#### **PredecodeWritebackBundleIfuToFtqIO如何写入ftq_pd_mem的一条Ftq_pd_Entry**
Ftq_pd_Entry项的写入是通过PredecodeWritebackBundle这个接口进行写入的其实也就是IfuToFtqIO
*从fromPdWb接口中接收信号生成表项*
- brmaskPredecodeWritebackBundle有一个预测块内的所有指令的预译码信息当一条指令的预译码信息有效(valid)且是分支指令is_br时, bool序列对应位置的指令被判定为分支指令
- jumpInfo
- valid预测块内存在一条指令其预译码信息有效valid且是jmp指令isJal或者isJalrjumpInfo有效
- bits预测块内的第一条有效跳转指令的info它是一个三位序列从低到高拉高对应该指令被预译码为是isJalrisCallisRet
- jmpOffset预测块内第一条有效jmp跳转指令的偏移
- rvcMask原封不动接受同名信号
- jalTarget原封不动接收同名信号
### 2.2 ftq_pd_mem写操作
#### **ftq_pd_mem的一条Ftq_pd_Entry如何以PreDecodeInfoto pd的形式输出**
PreDecodeInfo是一个Ftq_pd_Entry中的一条指令的预译码需要输入offset指定该预译码指令在预测块内的偏移
- valid直接set为1
- isRVC设置为rvcMask bool序列中对应偏移的值
- isBr设置为brMask bool序列中对应偏移的值
- isJalr输入的偏移量等于jumpOffset且jumpInfo有效并指明该指令type是isJalrjmpInfo.valid && jmpInfo.bits(0)
| 序号 | 功能名称 | 测试点名称 | 描述 |
| ---- | ---------- | ----- | ------------------------------------ |
| 2\.1 | FTQ_PD_MEM | WRITE | 向单端口输入wenwaddr决定是否写入以及写入地址写入wdata |
| 2\.2 | FTQ_PD_MEM | READ | 向多端口中输入renraddr决定是否读以及读取地址从rdata读取 |
### 3. ftb_entry_mem存储FTB项
有两个读端口一个写端口FtqSize个表项存储的数据项为FTBEntry_FtqMemFTBEntry_FtqMem与FTBEntry基本上是一致的。
#### FTBEntry_FtqMem
- brSlots分支指令槽
- 接口类型Vec(numBrSlot, new FtbSlot_FtqMem)
- FtbSlot_FtqMem
- 信号列表:
- offset给分支指令在相对于指令块起始地址的偏移
- 接口类型UInt(log2Ceil(PredictWidth).W)
- sharing对于tailSlot来说启用sharing表示把这个slot让给分支指令来被预测
- 接口类型Bool
- valid预测槽有效
- 接口类型Bool
- 说明当slot有效时我们才能说这条指令是br指令还是jmp指令
- tailSlot跳转指令槽
- 接口类型FtbSlot_FtqMem
- FTBEntry_partFTBEntry_FtqMem的父类存储部分FTB信息记录跳转指令的类型
- 信号列表:
- isCall接口类型Bool
- isRet接口类型Bool
- isJalr接口类型Bool
#### 3.1 ftb_entry_mem读操作
除了读出FTB项之外顶层还可以从FTBEntry_FtqMem获取以下有效信息在这里我们不需要验证以下内容但是在验证顶层的时候我们会用到以下内容在此处提一下此外以下内容并不会生成具体的信号接口而是产生相应的判断逻辑
- jmpValid预测块中jmp指令有效
- 说明当tailslot有效且不分享给分支指令时jmp有效
- getBrRecordedVec三维向量对于三个slot
- 说明接收一个offset偏移如果命中有效分支slot或者sharing拉高的tailslot对应slot的向量元素拉高。
- brIsSaved给定offset的指令是否是分支指令
- 说明采用slot预测结果来说明是不是分支指令前提需要信号有效
- getBrMaskByOffset
- 说明在给定offset范围内的三个slot中的指令是否是有效分支指令用一个三位maks表示
- newBrCanNotInsert能否插入新的brSlot
- 说明给定offset超过有效tailSlot对应的offset时不能插入新的brSlot
| 序号 | 功能名称 | 测试点名称 | 描述 |
| ---- | ------------- | ----- | ------------------------------------ |
| 3\.1 | FTQ_ENTRY_MEM | WRITE | 向单端口输入wenwaddr决定是否写入以及写入地址写入wdata |
| 3\.2 | FTQ_ENTRY_MEM | READ | 向多端口中输入renraddr决定是否读以及读取地址从rdata读取 |
### 4. ftq_pc_mem存储取指目标
pc存储子队列。存储项为Ftq_RF_Components用来读取取指信息取值信息交给IFU进行取指。
#### Ftq_RF_Components
**信号含义**
- startAddr: 预测块的起始地址
- nexLineAddr: 预测块下一个缓存行的起始地址
- startAddr加上64个字节一个缓存行的大小是64字节
- isNextMask: 一个预测宽度内的16条指令各自是否属于下一个预测块(在最新版本rtl中已被编译优化掉)
- 通过计算某条指令相对于预测块起始地址的偏移量每条指令两个字节得到偏移地址该偏移地址的第4位从0开始为1表示该指令属于下一个预测块。
- 进一步说其实也就可以根据它判断该指令是否在预测块跨缓存行的时候判断该指令是否属于下一个cacheline了
- fallThruError :预测出的下一个顺序取指地址是否存在错误
##### 4.1 ftq_pc_mem写操作
**信息获取:上述信息都可以从一个单流水级分支预测结果 (BranchPredictionBundle)中获取**。
获取方式startAddr直接获取BranchPredictonBundle中的pcfallThruError直接获取BranchPredictionBundle中的fallThruError。
##### 4.2 ftq_pc_mem读操作
**多端口读**ftq_pc_mem的每个读端口的读地址被直接连到各个FTQ指针的写入信号这样做的目的是可以及时的读取从pc存储子队列读出的项一定是此时FTQ指针指向的项
##### 读写时机
**写入时机**BPU流水级的S1阶段创建新的预测entry时写入
**读出时机** 读数据每个时钟周期都会存进Reg。如果IFU不需要从bypass中读取数据Reg数据直连给Icache和IFU如果IFU不需要从bypass中读取数据Reg数据直连给Icache和IFU
| 序号 | 功能名称 | 测试点名称 | 描述 |
| ---- | ---------- | ----- | ------------------------------------ |
| 4\.1 | FTQ_PC_MEM | WRITE | 向单端口输入wenwaddr决定是否写入以及写入地址写入wdata |
| 4\.2 | FTQ_PC_MEM | READ | 向多端口中输入renraddr决定是否读以及读取地址从rdata读取 |
### 5. ftq_meta_1r_sram存储meta信息
存储的数据为Ftq_1R_SRAMEntry同样有FtqSize项
Ftq_1R_SRAMEntry接口列表
- meta分支预测的meta数据
- ftb_entry分支预测的FTB项
**写入时机**:在 BPU的s3阶段接收信息因为对于一个指令预测块只有在其s3阶段才能获取完整的mata信息同样被接收的还有最后阶段ftqentry信息
| 序号 | 功能名称 | 测试点名称 | 描述 |
| ---- | ---------------- | ----- | ------------------------------------ |
| 5\.1 | FTQ_META_1R_SRAM | WRITE | 向单端口输入wenwaddr决定是否写入以及写入地址写入wdata |
| 5\.2 | FTQ_META_1R_SRAM | READ | 向多端口中输入renraddr决定是否读以及读取地址从rdata读取 |
## 接口说明
### Ftq_Redirect_SRAMEntry
ftq_redirect_mem存储的表项。继承自SpeculativeInfo存储RAS预测器相关重定向信息根据这些信息回溯到之前的状态
- sc_disagree统计分支指令在sc预测器中预测是否发生错误
- 接口类型Some(Vec(numBr, Bool()))
- 说明Option 类型表明这个值可能不存在在非FPGA平台才有否则为none
- 信号列表:
- SpeculativeInfo推测信息帮助BPU在发生重定向的时候回归正常的状态
- 接口列表:
- histPtr重定向请求需要恢复的全局历史指针可参见BPU顶层文档了解详情
- 接口类型CGHPtr
- 说明以下都属于RAS重定向信息,可参见BPU文档了解如何利用这些信息进行重定向
- ssp重定向请求指令对应的 RAS 推测栈栈顶在提交栈位置的指针
- 接口类型UInt(log2Up(RasSize).W)
- sctr重定向请求指令对应的 RAS 推测栈栈顶递归计数 Counter
- 接口类型RasCtrSize.W
- TOSW重定向请求指令对应的 RAS 推测栈(队列)写指针
- 接口类型RASPtr
- TOSR重定向请求指令对应的 RAS 推测栈(队列)读指针
- 接口类型RASPtr
- NOS重定向请求指令对应的 RAS 推测栈(队列)读指针
- 接口类型RASPtr
- topAddr
- 接口类型UInt(VAddrBits.W)
### Ftq_pd_Entry
- brMask一个指令预测宽度内16条rvc指令的指令块中哪些指令是分支指令
- 接口类型Vec(PredictWidth, Bool())
- jmpInfojump信息其值对应不同的jmp指令类型表示指令块内jmp指令类型
- 接口类型ValidUndirectioned(Vec(3, Bool()))
- 说明  jumpinfo有效的时候第0位是0表示jal指令第0位是1表示jalr指令第1位是1表示call指令第二位是1表示ret指令。
- jmpOffsetjmp指令在指令预测块中的偏移地址
- 接口类型: UInt(log2Ceil(PredictWidth).W)
- rvcMask一个预测块内的指令16条rvc指令哪些是rvc指令
- 接口类型Vec(PredictWidth, Bool())
## 测试点总表
| 序号 | 功能名称 | 测试点名称 | 描述 |
| ---- | ---------------- | ----- | ------------------------------------ |
| 1\.1 | FTQ_REDIRECT_MEM | WRITE | 向单端口输入wenwaddr决定是否写入以及写入地址写入wdata |
| 1\.2 | FTQ_REDIRECT_MEM | READ | 向多端口中输入renraddr决定是否读以及读取地址从rdata读取 |
| 2\.1 | FTQ_PD_MEM | WRITE | 向单端口输入wenwaddr决定是否写入以及写入地址写入wdata |
| 2\.2 | FTQ_PD_MEM | READ | 向多端口中输入renraddr决定是否读以及读取地址从rdata读取 |
| 3\.1 | FTQ_ENTRY_MEM | WRITE | 向单端口输入wenwaddr决定是否写入以及写入地址写入wdata |
| 3\.2 | FTQ_ENTRY_MEM | READ | 向多端口中输入renraddr决定是否读以及读取地址从rdata读取 |
| 4\.1 | FTQ_PC_MEM | WRITE | 向单端口输入wenwaddr决定是否写入以及写入地址写入wdata |
| 4\.2 | FTQ_PC_MEM | READ | 向多端口中输入renraddr决定是否读以及读取地址从rdata读取 |
| 5\.1 | FTQ_META_1R_SRAM | WRITE | 向单端口输入wenwaddr决定是否写入以及写入地址写入wdata |
| 5\.2 | FTQ_META_1R_SRAM | READ | 向多端口中输入renraddr决定是否读以及读取地址从rdata读取 |
## 附录
***虽然列在附录,但实际上这段内容依然十分重要,当你需要的时候请一定要查看。***
### 其余状态子队列
上述存储结构是FTQ中比较核心的存储结构实际上还有一些子队列用来存储一些状态信息也同样都是存储ftqsize个64元素。主要有以下
update_target记录每个FTQ项的跳转目标跳转目标有两种一种是当该FTQ项对应的分支预测结果中指明的该分支预测块中执行跳转的分支指令将要跳转到的地址另一种则是分支预测块中不发生跳转跳转目标为分支预测块中指令顺序执行的下一条指令地址。
- 此外与之配套的还有newest_entry_targetnewest_entry_ptr用来指示新写入的跳转目标地址和它对应的指令预测块或者说FTQ项的在FTQ中的位置同时有辅助信号newest_entry_target_modified和newest_entry_ptr_modified用来标识该地址的FTQ项跳转地址是否被修改。
写入时机上一个周期的bpu_in_fire有效的时候或者说相对于bpu_in_fire有效时延迟一个周期写入。
newest_entry_ptrnewest_entry_target这几个内部信号表明我们当前最新的有效FTQ项。BPU新的写入重定向等等都会对最新FTQ项进行新的安排在相应的文档中对其生成方式做具体的描述。
cfiIndex_vec记录每个FTQ项的发生跳转的指令cficontrol flow instruction指令在其分支预测块中的位置
写入时机相对于bpu_in_fire有效时延迟一个周期写入。
mispredict_vec记录每个FTQ项的分支预测结果是否有误初始化为false
pred_stage记录每个FTQ项的分支预测结果是来自于哪个阶段
写入时机相对于bpu_in_fire有效时延迟一个周期写入。
pred_s1_cycle记录每个FTQ项的分支预测结果对应的s1阶段的分支预测结果生成的时间cycle数
写入时机相对于bpu_in_fire有效时延迟两个周期写入。
commitStateQueueReg记录每个FTQ项中对应的分支预测块中每条指令一般是16条rvc指令对应一个预测宽度的提交状态提交状态有c_empty c_toCommit c_committed c_flushed依次用从0开始的从小到大的枚举量表示初始化为c_empty状态
写入时机相对于bpu_in_fire有效时延迟一个周期写入。
entry_fetch_status记录每个FTQ项的分支预测结果是否被送到ifu中该状态由两个枚举量f_to_send f_sent来表示, 初始化为f_sent状态。
写入时机上一个周期的bpu_in_fire有效的时候相对于bpu_in_fire有效时延迟一个周期写入。
写入数据写入f_to_send
entry_hit_status记录每个FTQ项拿到的分支预测结果是否是ftb entry hit的即生成该分支预测结果的时候是否是从FTB ( [预测结果生成hit](https://open-verify.cc/xs-bpu/docs/modules/03_ftb/))(非必须了解)中读取到了对应的记录表项。初始化为not_hit状态。
写入时机当来自BPU的全局分支预测信息中s2阶段的分支预测结果有效时写入s2阶段分支预测结果中指名的hit状态因为FTB预测器是分支预测s2阶段开始生效的在此时判断预测项是否在FTB缓存中命中
newest_entry_ptrnewest_entry_target这几个内部信号它们不是队列但是它们很重要表明我们当前应该关注的最新的FTQ项及对应的跳转目标。BPU新的写入重定向等等都会对最新FTQ项进行新的安排在涉及到修改该信号的相应的文档中对其生成方式做具体的描述。

View File

@ -1,192 +0,0 @@
---
title: FTQ接收BPU分支预测结果
linkTitle: 03_FTQ接收BPU分支预测结果
weight: 12
---
## 文档概述
BPU会将分支预测结果和meta数据发给FTQ。
- 从分支预测结果中我们可以提取出分支预测块对应的取值目标比如一个不跨缓存行且所有指令均为RVC指令的分支预测块对应的取值目标是从分支预测块起始地址开始的以2B为间隔的连续16条指令。
- meta信息则存储了各个预测器相关的预测信息由于BPU预测有三个流水级每个流水级都有相应的预测器所以只有到s3阶段才有可能收集到所有预测器的预测信息直到此时FTQ才接受到完整的meta这些信息会在该分支预测块的全部指令被后端提交时交给BPU进行训练
- FTBEntry严格来说它其实也是meta的一部分但是因为更新的时候ftb_entry需要在原来的基础上继续修改为了不重新读一遍ftb另外给它存储一个副本。
## 术语说明
| 名称 | 定义 |
| ---------------------------- | ----------- |
| BPU (Branch Prediction Unit) | 分支预测单元 |
| FTQ (Fetch Target Queue) | 采集目标队列 |
| IFU (Instruction Fetch Unit) | 指令采集单元 |
| RAS (Return Address Stack) | 返回地址堆 |
| FTQ Entry | FTQ队列中的单个表项 |
## 模块功能说明
### 1. 新的预测块进队条件
#### **1.1 成功接收数据**
##### 1.1.1 FTQ准备好接收信号
- FTQ准备好接收信号
     当FTQ队列中元素小于FtqSize或者可以提交指令块canCommit拉高说明可以提交指令块在后面的文档: FTQ向BPU发送更新信息中介绍怎么判断是否可以提交指令块的时候来自BPU的新的指令预测块可以进入FTQ队列队列准备好接收新的预测块fromBpu的resp接口ready信号拉高。
##### 1.1.2 BPU准备好要发送的信号
- BPU准备好要发送的信号
     当BPU发往FTQ的接口vaid信号拉高表示发送信号准备好
满足以上两个条件时,fromBpu的resp接口fire表示接口数据被成功发送到FTQ中。
#### **1.2 允许BPU入队allowBpuIn**
- 重定向发生时会回滚到之前的状态新发送的BPU预测信息自然就不需要了。**允许BPU入队**时不能发生重定向
##### 1.2.1 后端重定向发生
1. 后端重定向发生:
- 标志接收后端写回信息的接口fromBackend的重定向接口redirect有效则该周期不允许入队如果没有发生真实提前重定向realAhdValid(参见FTQ接收后端重定向一文),则下一个周期也不允许入队。
##### 1.2.2 IFU重定向发生
2. IFU重定向发生
- 标志IFU重定向信息生成的两个周期均不许入队参见FTQ接收IFU重定向一文了解IFU重定向信息的生成
只要避免上述两种重定向出现的情况就可以允许BPU入队,即可以把发送到FTQ的数据写入FTQ项
#### 1.3 以BPU预测结果重定向的方式入队
上述的BPU入队方式是一个全新的预测块进队即BPU分支预测的s1阶段结果入队此时未发生预测结果重定向。
当BPU发生预测结果重定向时只要**允许BPU入队allowBpuIn**也可以看作预测结果入队不过这种入队是覆写队列中已有的FTQ项没有写入新的指令块。
- BPU预测结果发生重定向的具体标志fromBpu的resp接口的s2s2阶段的预测信息有效且s2的hasRedirect拉高表示在s2阶段发生了重定向s3阶段重定向是一样的。
***综合两种形式的BPU入队这里称之为广义BPU入队方便区分记为bpu_in_fire该信号拉高表明发生广义BPU入队。***
### 2. 写入FTQ项
之前已经说明过了FTQ项只是一个抽象的概念FTQ有很多个子队列组成它们的项共同构成一个FTQ项所以向FTQ中写入FTQ项实际上就是就是把BPU的预测信息写到对应的FTQ子队列中。
FTQ主要获取以下信息作为bpu_in_resp
- bpu_in_respBPU交给FTQ的resp详见BPU文档resp中含有s1,s2,s3三个阶段的指令预测信息bpu_in_resp将获取其中某一阶段预测信息selectedResp作为其值。未发生重定向时使用s1作为预测结果s2或者s3发生重定向信息时优先s3的预测信息作为selectedResp。某阶段发生重定向的标志与上文讲述的一样一样。
从selectedRespbpu_in_resp我们还可以获取以下目标信息帮助我们写入子队列ftq_idx帮助我们索引写入子队列的地址
#### 2.1 写入FTQ子队列
##### 2.1.1 写入ftq_pc_mem
- ftq_pc_mem: 来自BPU的selectedResp预测信息被写入ftq_pc_mem, 该存储结构有ftqsize个表项对应队列中的所有ftq表项每个存储元素可以推出对应的ftq表项中每条指令的pc地址
接收信号列表:
- wen接收bpu_in_fire作为写使能信号
- waddr接收selectedResp的ftq_idx
- wdataselectedResp的相应信号
##### 2.1.2 写入ftq_redirect_mem
- ftq_redirect_mem: 在BPU的s3也就是最终阶段接收信息因为重定向信息只有在s3阶段才能得到。里面存储了RAS重定向相关的信息帮助BPU进行重定向。
接收信号列表:
- wen从BPUfromBpu回应resp的lastStage有效信号
- waddr从BPU回应的lastStage的ftq_idx.value
- wdata从BPU回应的last_stage_spec_info
##### 2.1.3 写入ftq_meta_1r_sram
- ftq_meta_1r_sram在 BPU的s3阶段接收信息同样是因为对于一个指令预测块只有在其s3阶段才能获取完整的mata信息同样被接收的还有最后阶段ftqentry信息
接收信号列表:
- wen从BPUfromBpu回应resp的lastStage有效信号
- waddr从BPU回应的lastStage的ftq_idx的value
- wdata
- meta从BPU回应的last_stage_meta
- ftb_entry从BPU回应的last_stage_ftb_entry
##### 2.1.4 写入ftb_entry_mem
- ftb_entry_mem虽然ftq_meta_1r_sram中存储有最后阶段ftbentry但此处出于更高效率读取专门把它存在ftb_entry_mem中。
接收信号列表:
- wen从BPUfromBpu回应resp的lastStage有效信号
- waddr从BPU回应的lastStage的ftq_idx的value字段
- wdata从BPU回应的last_stage_ftb_entry
从中可以看到FTQ虽然名字上听起来是一个队列**实际上内部却是由数个队列组成**他们共同构成了FTQ这个大队列
#### 2.2 写入状态队列
上述存储结构是FTQ中比较核心的存储结构实际上还有一些子队列用来存储一些状态信息也同样都是存储ftqsize个64元素需要被写入写入时机是在发生bpu_in_fire的下一个周期或者再下一个周期 。主要有以下:
##### 2.2.1 写入update_target
update_target记录每个FTQ项的跳转目标跳转目标有两种一种是当该FTQ项对应的分支预测结果中指明的该分支预测块中执行跳转的分支指令将要跳转到的地址另一种则是分支预测块中不发生跳转跳转目标为分支预测块中指令顺序执行的下一条指令地址。
- 此外与之配套的还有newest_entry_targetnewest_entry_ptr用来指示bpu_in_resp推出的跳转目标地址表示下一次预测时开始的目标地址和它对应的bpu_in_resp指令预测块在FTQ中的位置。
- 同时有辅助信号newest_entry_target_modified和newest_entry_ptr_modified用来标识该这两个字段是否被修改。
- 写入时机相对于bpu_in_fire有效时延迟一个周期写入。
- 写入地址bpu_in_resp记录的要写入FTQ的地址
- 写入数据bpu_in_resp.getTarget
##### 2.2.2 写入cfiIndex_vec
cfiIndex_vec记录每个FTQ项的发生跳转的指令cficontrol flow instruction指令在其分支预测块中的位置
- 写入时机相对于bpu_in_fire有效时延迟一个周期写入。
- 写入地址bpu_in_resp记录的要写入FTQ的地址
- 写入数据bpu_in_resp推断出的跳转目标
##### 2.2.3 写入mispredict_vec
mispredict_vec记录每个FTQ项的所有指令的预测结果是否有误初始化为false
- 写入时机相对于bpu_in_fire有效时延迟两个周期写入。
- 写入地址bpu_in_resp记录的要写入FTQ的地址
- 写入数据将该指令块的所有预测结果对应的值设置为false
##### 2.2.4 写入pred_stage
pred_stage记录每个FTQ项的分支预测结果是来自于哪个阶段
- 写入时机相对于bpu_in_fire有效时延迟一个周期写入。
- 写入地址bpu_in_resp记录的要写入FTQ的地址
##### 写入pred_s1_cycle不需要测试
pred_s1_cycle记录每个FTQ项的分支预测结果对应的s1阶段的分支预测结果生成的时间cycle数
- 写入时机相对于bpu_in_fire有效时延迟两个周期写入。
- 写入地址bpu_in_resp记录的要写入FTQ的地址
##### 2.2.5 写入commitStateQueueReg
commitStateQueueReg记录每个FTQ项中对应的分支预测块中每条指令一般是16条rvc指令对应一个预测宽度的提交状态提交状态有c_empty c_toCommit c_committed c_flushed依次用从小到大的枚举量表示初始化为c_empty状态
- 写入时机相对于bpu_in_fire有效时延迟一个周期写入。
- 写入数据写入c_empty
- 写入地址bpu_in_resp记录的要写入FTQ的地址
##### 2.2.6 写入entry_fetch_status
entry_fetch_status记录每个FTQ项的分支预测结果是否被送到ifu中该状态由两个枚举量f_to_send f_sent来表示, 初始化为f_sent状态。
- 写入时机相对于bpu_in_fire有效时延迟一个周期写入。
- 写入数据写入f_to_send
- 写入地址bpu_in_resp记录的要写入FTQ的地址
##### 2.2.7 写入entry_hit_status
entry_hit_status记录每个FTQ项拿到的分支预测结果是否是ftb entry hit的即生成该分支预测结果的时候是否是从ftb中读取到了对应的记录表项。初始化为not_hit状态。
- 写入时机当来自BPU的全局分支预测信息中s2阶段的分支预测结果有效时写入s2阶段分支预测结果中指名的hit状态
- 写入地址bpu_in_resp记录的要写入FTQ的地址
- 写入数据f_to_send
注:之所以延迟时钟周期写入,是为了缩短关键路径,以及帮助减少扇出
### **3 转发分支预测重定向**
#### 3.1 转发给IFU
- s2以及s3阶段的预测重定向信息通过FTQ与Ifu的接口toIfu的flushFromBpu发送给IFU当完整分支预测结果中的s2阶段分支预测结果发生预测结果重定向时flushFromBpu.s2.valid拉高flushFromBpu.s2.bits接收s2阶段分支预测结果中指明的该分支预测结果在FTQ中的位置ftq_idx。
#### 3.2 **转发给预取**
- 该重定向信号同样会通过toPrefetch.flushFromBpu接口以相同的方式传递给Prefetch
s3阶段向IFU以及Prefetch的重定向传递与s2阶段的重定向信号传递一样。该阶段的重定向信号传递会覆盖可能的s2阶段重定向信号传递结果
### 4 修正FTQ指针
此外分支预测结果重定向也会影响ifuPtr与pfPtr两个指针信号的写入信号。
#### 4.1 正常修改
- 正常情况下allowToIfu条件和allowToBpu一样同时BPU向Ifu发送FTQ项的io接口toIfu.req发生fire的时候ifuPtr寄存器中写入ifuPtr+1。同样发生修改的还有pfPtr当allowToIfu同时BPU向Prefetch发送FTQ项的io接口totoPrefetch.req发生fire的时候。
#### 4.2 发生重定向时修改
- 而如果是发生重定向的时候比如s2阶段预测结果发生重定向此时若ifuPtr不在s2阶段预测结果中指明的ftq_idx之前ifuPtr写入该ftq_idxpfPtr_write同样如此
**bpuptr**
由FTQ交给BPU用于指示新的指令预测块应该放到FTQ队列中的位置上述存储结构ftq_pc_memftq_redirect_memftq_meta_1r_sramftb_entry_mem基本上也是通过与该指针相关的信号得知信息应该存储的addrbpuptr交给BPUBPU基于此获知每个阶段预测结果的ftq_idx
bpuptr寄存器的输出值直接连到FTQ发往BPU的接口toBpu的enq_ptr字段中当然再次之前bpuptr的值会根据实际情况修改。
在enq from bpu的过程中正常情况下发生enq的时候也就是新的预测块进队时bpuptr+1BPU将要向FTQ中写入的位置前进一位
但是如果发生重定向的时候比如如果s2阶段预测结果发生重定向bpuptr被更新为s2阶段分支预测结果的ftq_idx+1表示BPU将要向FTQ中写入的位置为s2阶段预测结果在FTQ中位置的后一位因为此时新的全局预测结果会基于s2的预测结果展开下一轮预测即以s2分支预测块的下一块展开预测自然会被写入该结果会覆盖enq_fire发生时的结果此外s3阶段的分支预测重定向时会覆盖可能的s2阶段重定向修改的bpuptr
其他的ftq指针也是类似的用于指示写入FTQ的地址
## 接口说明
FTQ接收BPU分支预测结果工程中涉及到的IO接口如下在FTQ顶层IO一文中有详细说明
| 接口 | 作用 |
| ----------- | ---------------------------- |
| fromBackend | 根据是否有重定向确认是否允许BPU预测结果入队 |
| fromBPU | 接收BPU预测结果 |
| toIfu | 发送更新的IFU指针转发BPU预测结果重定向 |
| toPrefetch | 发送更新的Prefetch指针转发BPU预测结果重定向 |
| toBpu | 发送更新的BPU指针 |
## 测试点总表
| 序号 | 功能名称 | 测试点名称 | 描述 |
| ------- | --------------------- | ----------------- | --------------------------------------------------------------------------- |
| 1\.1\.1 | BPU_IN_RECEIVE | FTQ_READY | 当FTQ队列中元素小于FtqSize或者可以提交指令块的时候队列准备好接收新的预测块 |
| 1\.1\.2 | BPU_IN_RECEIVE | BPU_VALID | BPU准备好要发送的信号 |
| 1\.2\.1 | BPU_IN_ALLOW | BACKEND | 接收后端写回信息的接口fromBackend的重定向接口redirect有效则该周期不允许入队如果没有发生真实提前重定向则下一个周期也不允许入队 |
| 1\.2\.2 | BPU_IN_ALLOW | IFU | IFU重定向信息生成的两个周期均不许入队 |
| 1\.3\.1 | BPU_IN_BY_REDIRECT | REDIRECT | 当BPU发生预测结果重定向时只要**允许BPU入队allowBpuIn**,也可以看作预测结果入队 |
| 2\.1\.1 | WRITE_FTQ_SUBQUEUE | FTQ_PC | 根据BPU预测结果写入ftq_pc_mem |
| 2\.1\.2 | WRITE_FTQ_SUBQUEUE | FTQ_REDIRECT | 根据BPU预测结果写入ftq_redirect_mem |
| 2\.1\.3 | WRITE_FTQ_SUBQUEUE | FTQ_MATA | 根据BPU预测结果写入ftq_meta_1r_sram |
| 2\.1\.4 | WRITE_FTQ_SUBQUEUE | FTQ_ENTRY | 根据BPU预测结果写入ftb_entry_mem |
| 2\.2\.1 | WRITE_FTQ_STATEQUEUE | UPDATED_TARGET | 根据BPU预测结果写入update_target |
| 2\.2\.2 | WRITE_FTQ_STATEQUEUE | CFIINDEX | 根据BPU预测结果写入cfiIndex_vec |
| 2\.2\.3 | WRITE_FTQ_STATEQUEUE | MISPREDICT | 根据BPU预测结果写入mispredict_vec |
| 2\.2\.4 | WRITE_FTQ_STATEQUEUE | PRED_STAGE | 根据BPU预测结果写入pred_stage |
| 2\.2\.5 | WRITE_FTQ_STATEQUEUE | COMMITSTATE | 根据BPU预测结果写入commitStateQueueReg |
| 2\.2\.6 | WRITE_FTQ_STATEQUEUE | ENTRY_FETCH_STATU | 根据BPU预测结果写入entry_fetch_status |
| 2\.2\.7 | WRITE_FTQ_STATEQUEUE | ENTRY_HIT_STATU | 根据BPU预测结果写入entry_hit_status |
| 3\.1 | TRANSFER_BPU_REDIRECT | IFU | 转发分支预测重定向给IFU |
| 3\.2 | TRANSFER_BPU_REDIRECT | PREFETCH | 转发分支预测重定向给PREFETCH |
| 4\.1 | UPDATE_FTQ_PTR | NORMAL | 正常情况下修改FTQ指针 |
| 4\.2 | UPDATE_FTQ_PTR | REDIRECT | 发生重定向时修改FTQ指针 |

View File

@ -1,127 +0,0 @@
---
title: FTQ向IFU发送取指目标
linkTitle: 04_FTQ向IFU发送取指目标
weight: 12
---
## 文档概述
IFU需要取FTQ中的项进行取指令操作同时也会简单地对指令进行解析并写回错误的指令
FTQ发送给IFU的信号同时也需发送给ICache一份ICache是指令缓存帮助快速读取指令。
## 术语说明
- ifuPtr该寄存器信号指示了当前FTQ中需要读取的项的指针。直接发送给io.toIfu.req接口的ftqIdx。
- entry_is_to_sendentry_fetch_status存储每个FTQ项的发送状态初始化并默认为当前ifuptr指向的项对应的发送状态后续可能因为旁路逻辑等改变
- entry_ftq_offset: 从cfiIndex_vec中初始化并默认为当前ifuptr指向项的跳转指令在预测块中的偏移后续可能因为旁路逻辑等改变
- entry_next本次取指结束后下一次取值的开始地址
- pc_mem_ifu_ptr_rdata获取ifuptr指向FTQ项的取指信息从ftq_pc_mem的读取接口ifuPtr_rdata中获取
- pc_mem_ifu_plus1_rdata获取ifuptr+1指向FTQ项的pc相关信息从ftq_pc_mem的读取接口ifuPtrPlus1_rdata中
- copied_ifu_plus1_to_send多个相同的复制信号entry_fetch_status中指向ifuPtrPlus1的项是f_to_send状态或者上一周期bpu_in_fire,同时旁路bpu指针bpu_in_bypass_ptr等于ifuptr+1时信号copied_ifu_plus1_to_send在一周期后拉高
- copied_ifu_ptr_to_send同理只是把ifuptr+1改成了ifuptr
## 模块功能说明
### 1. 获取取指目标信息
获取取指目标有两个来源一个是BPU写入信息时直接将取指目标旁路出来一种则是从存储取指目标的队列ftq_pc_mem中读取。使用前一种方式的前提是刚好ifuPtr指向的读取项刚好就是旁路指针信号bpu_in_resp_ptrBPU入队时写入项的ftqIdx
- 旁路逻辑pc信号在被写入存储子队列时就被旁路一份写入信号ftq_pc_mem.io.wdata在bpu_in_fire信号拉高时被旁路到旁路信号寄存器bpu_in_bypass_buf中。同时被旁路的还有指针信号bpu_in_resp_ptr在同样的条件下被旁路到寄存器bpu_in_bypass_ptr中
- 读取ftq_pc_mem: 存储pc相关的取指目标该存储队列有多个读接口对所有ftqptr的写入信号比如ifuPtr_write, ifuPtrPlus1_write等被直接连接到存储队列的读取接口这样在ftqPtr寄存器正式被更新时就可以同时直接从对应的读取接口中返回对应指针的读取结果比如ftq_pc_mem.io.ifuPtr_rdata
#### 1.1 准备发往ICache的取指目标
有以下三种情况,分别对应**测试点1.1.11.1.21.1.3**
1. 旁路生效即旁路bpu指针等于ifuptr且上一周期bpu输入有效结果last_cycle_bpu_in表示上一周期bpu_in_fire有效也就相当于该旁路指针是有效的此时直接向toICache接口输入旁路pc信息bpu_in_bypass_buf
2. 不满足情况1但是上一周期发生ifu_fire即FTQ发往IFU的接口发生fire成功传输信号此toICache中被写入pc存储子队列ftq_pc_mem中ifuptr+1对应项的结果这是因为此时发生了ifu_fire新的ifuptr还未来得及更新即加1所以直接从后一项中获取新的发送数据
3. 前两种情况都不满足此时toICache接口中被写入pc存储队列中ifuptr对应项的结果
#### 1.2 提前一周期准备发往Prefetch的取指目标
有以下三种情况,分别对应**测试点1.2.11.2.21.2.3**
同样有三种情况:
1. bpu有信号写入bpu_in_fire同时bpu_in_resp_ptr等于pfptr的写入信号pfptr_write, 此时pfptr_write还没有正式被写入pfptr中读取bpu向pc存储队列的写入信号wdata下一周期写入ToPrefetch
     *xxxptr_write是相应FTQptr寄存器的write信号连接到寄存器的写端口寄存器在时钟上升沿成功写入write信号*
2. 不满足情况1且由bpu到prefetch的接口发生fire即bpu向预取单元成功发送信号pc存储单元的pfPtrPlus1_rdata下一周期写入ToPrefetch接口选择指针加1对应项的原因与toICache类似。
3. 不满足以上两种情况pc存储单元的pfPtr_rdata在下一周期被写入ToPrefetch接口
#### 1.3 设置下一个发送的指令块的起始地址
有以下三种情况,分别对应**测试点1.3.11.3.21.3.3**
**targetentry_next_addr旁路逻辑**
有三种情况:
1. 上一周期bpu写入信号且旁路指针等于ifuptr
- toIfu写入旁路pc信息bpu_in_bypass_buf
- entry_is_to_send :拉高
- entry_next_addr bpu预测结果中跳转地址last_cycle_bpu_target
- entry_ftq_offset bpu预测结果中跳转指令在预测块中的偏移last_cycle_cfiIndex
2. 不满足情况1bpu到ifu的接口发生fire信号成功写入
- toIfu写入pc存储队列的读出信号ifuPtrPlus1_rdata这同样是因为ifuptr还没来得及更改所以直接使用ifuptr+1对应项的rdata
- entry_is_to_send 发送状态队列中ifuPtrPlus1对应项为f_to_send或者在上一周期bpu有写入时旁路bpu指针等于ifuptr加1entry_is_to_send拉高。
- entry_next_addr
- 如果上一周期bpu有写入且bpu旁路指针等于ifuptr+1写入bpu旁路pc信号的startAddr字段而这个项的pc信息还没有写入正在pc旁路信号中这是因为ifuptr+1对应下一个指令预测块它的起始地址实际上就是ifuptr对应指令的预测块的跳转目标。
- 如果不满足该条件,
1. ifuptr等于newest_entry_ptr: 使用newest_entry_target作为entry_next_addrnewest_entry_ptrnewest_entry_target这几个内部信号表明我们当前队列中最新的有效的FTQ项。如之前所说BPU新的写入重定向等等都会对最新FTQ项进行新的安排在相应的文档中对其生成方式做具体的描述。
2. 不满足条件1使用pc存储队列的ifuPtrPlus2_rdata.startAddr
3. 不满足情况12
- toIfu写入pc存储队列的读出信号ifuPtr_rdata
- entry_is_to_send 发送状态队列中ifuPtr对应项为f_to_send或者在上一周期bpu有写入时旁路bpu指针等于ifuptr
- entry_next_addr
- 如果上一周期bpu有写入且bpu旁路指针等于ifuptr+1写入bpu旁路pc信号的startAddr字段。
- 如果不满足该条件,
         1. ifuptr等于newest_entry_ptr: 使用newest_entry_target作为entry_next_addr。
         2. 不满足上面的条件1使用pc存储队列的ifuPtrPlus1_rdata.startAddr为什么条件2和条件3一个使用ifuPtrPlus2_rdata.startAddr作为entry_next_addr 一个使用ifuPtrPlus1_rdata.startAddr作为这也是出于时序的考虑
因为要获得实际上的ifuptr+1对应项的start值作为结果而因为第一处那里因为ifuptr还没来得及更新加1同步到当前实际的ifuptr所以要加2来达到实际上的ifuptr+1对应的值而第二处的ifuptr已经更新了所以只用加1就行了。
### 2. 发送取指信息
#### 2.1 发送取指目标
##### 2.1.1 发送给IFU
**toIfu接口的req接口**
FTQ通过该接口向IFU发送取指信号
- valid要发送的FTQ项处于将发送状态entry_is_to_send且ifuptr不等于bpuptr
- nextStartAddr递交最终的entry_next_addr
- ftqOffset递交最终的entry_ftq_offset
- toIfu递交pc信息
##### 2.1.2 发送给ICache
**toICache的req接口**
FTQ通过该接口向ICache发送取指信号
- validFTQ项处于将发送状态entry_is_to_send且ifuptr不等于bpuptr
- readValidICache的有多个read接口readVlid是一个向量表示这几个read接口是否有效readVlid中的每个元素的写入值与valid一样
- pcMemRead同样是一个向量对应readVlid向量的ICache的多个pc信号read接口从toIfu接口中将pc信息结果写入向量中各接口接口的ftqIdx字段被写入ifuPtr
- backendException后端出现异常同时后端pc错误指针等于ifuPtr
#### 2.1.3 发送给Prefetch
**toPrefetch的req接口**
- valid传给预取模块的项的状体toPrefetchEntryToSend为1toPrefetchEntryToSend会玩一个周期存储nextCycleToPrefetchEntryToSend的值且pfptr不等于bpuptr
- toPrefetch递交pc
- ftqIdx字段被设置为pfptr寄存器的值
- backendException在后端pc错误指针等于pfptr的时候传入后端异常信号否则传入无异常信号
#### 2.2 错误命中
**错误命中falsehit**
当发往Ifu的pc接口toIfu中发生fallThruError预测块的fall through地址小于预测的起始地址时且hit状态队列entry_hit_status中ifuPtr对应项显示命中的话进行如下判断
当发往ifu的接口toIfu的req接口发生fire且bpu的预测结果不发生满足以下条件的重定向: s2或者s3的重定向的预测块对应的FTQ项索引号ftq_idx等于ifuptr, 此时hit状态队列中ifuptr对应项被设置为false_hit。
#### 2.3 BPU冲刷
**bpu向ifu的req请求的flush**
发往ifu的flushfrombpu(来自bpu的冲刷)接口中记录有s2s3阶段的指针如果其中一条指针不大于发往ifu的req接口的ftqIdx的时候表示应该被冲刷掉req信号即冲刷掉新的发送给FTQ的预测信息。
#### 2.4 更新发送状态
**成功发送:**
发往ifu的req接口发生fire且req不被来自bpu的flush给冲刷掉时
entry_fetch_status状态队列中ifuptr对应项的发送状态置为f_sent。表示该ftq项被成功发送 了
## 接口说明
| 顶层IO | 子接口 | 作用 |
| ---------- | ------------ | -------------- |
| toIFU | req | 发送取指目标 |
| toIFU | flushfrombpu | 冲刷掉发送给IFU的取指目标 |
| toICache | req | 发送取指目标 |
| toPrefetch | req | 发送取指目标 |
## 测试点总表
| 序号 | 功能名称 | 测试点名称 | 描述 |
| ------- | ------------------- | ----------------- | ------------------------------------------------------------------------------------------- |
| 1\.1\.1 | GET_PC_FOR_ICACHE | COND1 | 旁路生效即旁路bpu指针等于ifuptr且上一周期bpu输入有效结果有效直接向toICache接口输入旁路pc信息bpu_in_bypass_buf |
| 1\.1\.2 | GET_PC_FOR_ICACHE | COND2 | 不满足情况1但是上一周期发生ifu_fire成功传输信号此时toICache中被写入pc存储子队列ftq_pc_mem中ifuptr+1对应项的结果 |
| 1\.1\.3 | GET_PC_FOR_ICACHE | COND3 | 前两种情况都不满足此时toICache中被写入pc存储队列中ifuptr对应项的结果 |
| 1\.2\.1 | GET_PC_FOR_PREFETCH | COND1 | bpu有信号写入同时bpu_in_resp_ptr等于pfptr的写入信号pfptr_write, 读取bpu向pc存储队列的写入信号wdata下一周期写入ToPrefetch |
| 1\.2\.2 | GET_PC_FOR_PREFETCH | COND2 | 不满足情况1且由bpu到prefetch的接口发生fire即bpu向预取单元成功发送信号pc存储单元的pfPtrPlus1_rdata下一周期写入ToPrefetch接口 |
| 1\.2\.3 | GET_PC_FOR_PREFETCH | COND3 | 不满足以上两种情况pc存储单元的pfPtr_rdata在下一周期被写入ToPrefetch接口 |
| 1\.3\.1 | SET_NEXT_ADDR | COND1 | 上一周期bpu写入信号且旁路指针等于ifuptr时设置下一个发送的指令块的起始地址 |
| 1\.3\.2 | SET_NEXT_ADDR | COND2 | 不满足情况1bpu到ifu的接口发生fire时设置下一个发送的指令块的起始地址 |
| 1\.3\.3 | SET_NEXT_ADDR | COND3 | 不满足情况12时设置下一个发送的指令块的起始地址 |
| 2\.1\.1 | SEND_PC | IFU | 向IFU发送取指目标 |
| 2\.1\.2 | SEND_PC | ICACHE | 向ICache发送取指目标 |
| 2\.1\.3 | SEND_PC | PREFETCH | 向Prefetch发送取指目标 |
| 2\.2 | FALSE_HIT | FALSE_HIT | 当发往Ifu的pc接口toIfu中发生fallThruError且FTB项命中时判断是否是错误命中 |
| 2\.3 | FLUSH_FROM_BPU | FLUSH_FROM_BPU | 发往ifu的flushfrombpu(来自bpu的冲刷)接口中的s2s3阶段的指针其中一条指针不大于发往ifu的req接口的ftqIdx的时候应该冲刷掉新的发送给FTQ的预测信息 |
| 2\.4 | UPDATE_SEND_STATU | UPDATE_SEND_STATU | 发往ifu的req接口发生fire且req不被来自bpu的flush给冲刷掉时<br>entry_fetch_status状态队列中ifuptr对应项的发送状态置为f_sent |

View File

@ -1,88 +0,0 @@
---
title: IFU向FTQ写回预译码信息
linkTitle: 05_IFU向FTQ写回预译码信息
weight: 12
---
## 文档概述
IFU获取来自BPU的预测信息之后会执行预译码并将FTQ项写回FTQ中去。我们会比对FTQ中原BPU预测项和预译码的结果判断是否有预测错误
### 基本流程
预译码写回ftq_pd_mem
- FTQ从pdWb接口中获取IFU的写回信息FTQ首先将预译码写回信息写回到ftq_pd_mem,
更新提交状态队列commitStateQueue
- 然后根据写回信息中指令的有效情况更新提交状态队列commitStateQueue。
比对错误:
- 同时从ftb_entry_mem读出ifu_Wb_idx所指的FTB项将该FTB项的预测结果与预译码写回结果进行对比看两者对分支的预测结果是否有所不同。
综合错误:
- 之后就综合根据预译码信息可能得到的错误有前面说的比对BPU的预测结果和预译码结果得到的错误也有直接根据预译码得到的错误预测信息。根据错误预测结果更新命中状态队列。
更新写回指针
- 最后如果IFU成功写回ifu_Wb_idx更新加1。
## 术语说明
| 名称 | 定义 |
| -------- | ------------------------- |
| 预译码 | IFU会对取指目标进预译码之后写回FTQ |
| ifuWbPtr | IFU写回指针知识IFU预译码要写入FTQ的位置 |
## 模块功能说明
### 1. 预译码写回ftq_pd_mem
写回有效预译码信息pdWb有效时写有效
写回地址pdWb的ftqIdx的value
写回值解析整个pdWb的结果
### 2. 更新提交状态队列
当预译码信息pdWb有效时相当于写回有效此时根据预译码信息中每条指令的有效情况和该指令是否在有效范围内判断指令的提交状态是否可以修改若可以修改则将提交状态队列写回项中的指令状态修改
#### 详细信号表示
pdWb有效时ifu_wb_valid拉高。
此时对于预译码信息中每一条指令的预译码结果pd做判断
如果预译码结果valid且指令在有效范围内根据insrtRange的bool数组指示则提交状态队列commitStateQueue中写回项中的指令状态修改为c_toCommit表示可以提交这是因为只有在FTQ项被预译码写回后才能根据后端提交信息提交该FTQ项之后会把预译码信息一并发往更新通道。
### 3. 比对预测结果与预译码结果
从ftb存储队列ftb_entry_mem中的读取ifu写回指针ifuwbptr的对应项
- pdWb有效的时候读有效读取地址为预译码信息中指示的ftqIdx。
当命中状态队列指示待比对项ftb命中且回写有效时读取出FTB存储队列中对应的项与预译码信息进行比对当BPU预测的FTB项指示指令是有效分支指令而预译码信息中则指示不是有效分支指令时发生分支预测错误当BPU预测的FTB项指示指令是有效jmp指令而预译码信息中则指示不是有效jmp指令时发生跳转预测错误
#### 详细信号表示:
ifu_wb_valid回写有效时ftb_entry_mem回写指针对应读使能端口ren有效读取地址为ifu_wb_idx预测译码信息中指示的ftqIdx的value值。
回写项命中且回写有效hit_pd_valid信号有效此时读取ftb存储队列中的FTB项读出brSlots与tailSlot并进行比对
#### 3.1 判断是否有分支预测错误br_false_hit
##### 测试点3.1.1和3.1.2对应以下两种条件导致的br_false_hit
- 判断是否有分支预测错误br_false_hit
1. brSlots的任意一项有效同时在预译码信息中不满足这一项对应的pd有效且isBr字段拉高表明是分支指令
2. taiSlot有效且sharing字段拉高表明该slot为分支slot同时在预译码信息中不满足这一项对应的pd有效且isBr字段拉高表明是分支指令
满足任意条件可判断发生分支预测错误br_false_hit该信号拉高
#### 3.2 判断是否发生jmp预测错误jal_false_hit
- 判断是否发生jmp预测错误jal_false_hit
- 预测结果中必须指明指令预测有效且其中isJal拉高表面是jal指令或者指明是isjalr指令
### 4. 预译码错误
直接从预测结果中获取错误预测相关信息如果回写项ftb命中且missoffset字段有效表明有错误预测的指令hit_pd_mispred信号拉高表示预译码结果中直接指明有预测错误的指令。
### 5. 综合错误
综合比对预测结果与预译码结果得到的错误信息与预译码错误直接获得的预测错误任意一种发生时has_false_hit拉高表示有预测错误此时命中状态队列entry_hit_status中写回项的状态置为h_false_hit
### 6. 更新写回指针
ifu_wb_valid拉高表示写回有效将ifuWbPtr更新为原值加1。
## 接口说明
| 顶层IO | 子接口 |
| ------- | ---- |
| fromIfu | pdWb |
## 测试点总表
| 序号 | 功能名称 | 测试点名称 | 描述 |
| ------- | ------------------ | ------------------ | ----------------------------------------------------------------------------------------------------------------- |
| 1 | WB_PD | WB_PD | 向ftq_pd_mem中写回预译码信息 |
| 2 | UPDATE_COMMITSTATE | UPDATE_COMMITSTATE | 当预译码信息pdWb有效时根据预译码信息中每条指令的有效情况和该指令是否在有效范围内判断指令的提交状态是否可以修改若可以修改则将提交状态队列写回项中的指令状态修改 |
| 3\.1\.1 | BR_FALSE_HIT | COND1 | brSlots的任意一项有效同时在预译码信息中不满足这一项对应的pd有效且isBr字段拉高 |
| 3\.1\.2 | BR_FALSE_HIT | COND2 | taiSlot有效且sharing字段拉高表明该slot为分支slot同时在预译码信息中不满足这一项对应的pd有效且isBr字段拉高 |
| 3\.2 | JAL_FALSE_HIT | JAL_FALSE_HIT | 指令预测有效且其中isJal拉高或者指明是isjalr指令 |
| 4 | PD_MISS | PD_MISS | 如果回写项ftb命中且missoffset字段有效表明有错误预测的指令hit_pd_mispred信号拉高 |
| 5 | FALSE_HIT | FALSE_HIT | 综合比对预测结果与预译码结果得到的错误信息与预译码错误直接获得的预测错误任意一种发生时has_false_hit拉高表示有预测错误此时命中状态队列entry_hit_status中写回项的状态置为h_false_hit |
| 6 | UPDATE_IFU_WB_PTR | UPDATE_IFU_WB_PTR | ifu_wb_valid拉高将ifuWbPtr更新为原值加1 |

View File

@ -1,89 +0,0 @@
---
title: FTQ接收后端重定向
linkTitle: 06_FTQ接收后端重定向
weight: 12
---
## 文档概述
FTQ重定向信息有两个来源分别是IFU 和 后端。两者的 重定向接口大致相似,但重定向的过程有一定区别。
对于重定向后端有提前重定向机制为了实现提前一拍读出在ftq中存储的重定向数据减少redirect损失后端会向ftq提前一拍相对正式的后端redirect信号传送ftqIdxAhead信号和ftqIdxSelOH信号。ftqIdxSelOH信号出现的原因是早期版本要读多个ftqIdxAhead信号以独热码的形式选其中一路作为最终确认的提前索引值但现在只需要从一个端口获取ftqIdx信号了ftqIdxAhead只能确认这一个端口了。
## 术语说明
| 名称 | 定义 |
| ----------- | --------------------------------------------------- |
| sc_disagree | 统计SC预测错误用的性能计数器中需要用到的值SC预测器是BPU子预测器TAGE-SC预测器的一个部分 |
## 模块功能说明
### 1. 接收后端重定向信号
### 时序
#### 1.1 提前重定向
第一个周期:
- 后端重定向写回时首先会从后端到FTQ的IO接口CtrltoFtqIO看ftqIdx是不是有效信号且此时后端正式重定向信号redirect无效(因为提前重定向会比正式重定向提前一拍,所以此时正式重定向无效)这时提前重定向信号aheadValid有效, 将使用提前获取的重定向ftqIdx
#### 1.2 真实提前重定向
第二个周期:
- 如果此时后端正式重定向信号有效了且ftqIdxSelOH拉高说明在正式重定向阶段成功对ftqIdxAhead信号进行选中同时上一周期重定向信号aheadValid是有效的则真实提前重定向信号realAhdValid拉高在此时读取
#### 1.3 存储后端重定向信号
第三个周期:
- 该周期会把来自后端的重定向信息的存储一份在寄存器backendRedirectReg中具体的来说当上一个周期后端重定向有效时将后端重定向bits字段存储实际内容被写入寄存器的bits字段。
- 而实际决定信号是否有效的valid字段决定该信号是否有效则在上一周期真实提前重定向信号有效表示确实使用了提前重定向的ftqIdx进行重定向的情况下被写入false因为提前重定向发生时我们直接使用当前的后端重定向信号交给FTQ就可以了。而不需要多保存一个周期。
- 真实提前重定向信号无效时则由上一周期后端正式重定向的有效值决定只有信号有效时我们才需要把它存下来之后交给FTQ。
### 2. 选择重定向信号
**信号抉择**
是提前获取后端重定向信息还是延迟一个周期从寄存器内读取?
真实重定向有效时直接将后端重定向信息传递给FTQ否则取重定向寄存器内的信号作为重定向信息传递给FTQ相当于晚一个周期发送重定向信息。最后被选择的重定向信息作为**后端重定向结果fromBackendRedirect**发送给FTQ
接下来讲讲后端重定向在这三个周期到底通过ftqIdx到底读了哪些FTQ子队列中的信息以及怎么使用它们。
### 3. 整合子队列信号
#### 3.1 读取子队列
接下来讲讲后端重定向在这三个周期到底通过ftqIdx到底读了哪些FTQ子队列中的信息以及怎么使用它们。
**后端重定向读取的子队列:**
- ftq_redirect_memFTQ会根据后端重定向提供的ftqIdx读出ftq_Redirect_SRAMEntry借助它提供的信息重定向到之前的状态。
- ftq_entry_mem读出重定向指令块对应的FTB项
- ftq_pd_mem读出重定向指令块的预译码信息
#### 3.1.1 发生提前重定向时,读取子队列需要两个周期
#### 3.1.2 未发生提前重定向时,读取子队列需要三个周期
**读子队列时序:**
第一个周期:
- 提前重定向信号有效时将子队列的读端口读有效信号拉高输入ftqIdxAhead的value字段作为读地址发起读取请求。
第二个周期:
- case1. 如果第一周期的提前重定向无效而现在正式重定向有效则在此时才拉高读有效信号使用正式重定向接口的ftqIdx作为读取地址发起读取请求。
- case2. 真实提前重定向有效了,此时因为前一个周期已经发起读取请求,此时可以直接从子队列的读端口读出了
第三个周期
- 真实提前重定向无效,但至少前一个周期正式重定向发起的读取请求能保证在当前周期从子队列中读出。
#### 3.2 将子队列信息整合到后端重定向信号
**处理读取信息**
*FTQ会将从子队列中读出的信息整合到fromBackendRedirect中。*
具体来说:
- 重定向redirect接口的CfiUpdateInfo接口直接接收ftq_Redirect_SRAMEntry中的同名信号。
- 利用fromBackendRedirect中指示的ftqOffset读取指令块预译码信息中实际跳转指令的预译码信息该ftqOffset为后端执行过后确定的控制流指令在指令块内的偏移。
- 得到的预译码信息被直接连接到CfiUpdateInfo接口的pd接口中
- 对于读出的指令块对应的FTB项我们可以从中得知实际执行时得到的跳转指令是否在FTB项被预测为跳转指令或者是被预测为jmp指令如果是则cfiUpdateInfo的br_hit接口或者jr_hit接口被拉高表示对应的分支预测结果正确了。
- 具体来说通过发送ftqOffsetftb项以brIsSaved的方式判断是否br_hit判断是否jr_hit的方式也是类似的r_ftb_entry.isJalr && r_ftb_entry.tailSlot.offset === r_ftqOffset
- 在CfiUpdateInfo接口设置为br_hit的时候还会根据这条发生跳转的分支指令是哪个槽从ftq_Redirect_SRAMEntry重定向接口的sc_disagree统计SC预测错误用的性能计数器中获取对应值最后整合到后端重定向接口中如果没有br_hit对应计数器的两个值都为0
## 接口说明
| 顶层IO | 功能 |
| ----------- | --------- |
| fromBackend | 接收后端重定向信息 |
## 测试点总表
| 序号 | 功能名称 | 测试点名称 | 描述 |
| ----- | ------------------------- | ------------------- | --------------------------------------------------------------------------------------------------- |
| 1.1 | RECERIVE_BACKEND_REDIRECT | REDIRECT_AHEAD | 后端重定向写回时首先会从后端到FTQ的IO接口CtrltoFtqIO看ftqIdx是不是有效信号且此时后端正式重定向信号redirect无效这时提前重定向信号aheadValid有效 |
| 1.2 | RECERIVE_BACKEND_REDIRECT | REAL_REDIRECT_AHEAD | 如果此时后端正式重定向信号有效了且ftqIdxSelOH拉高同时上一周期重定向信号aheadValid是有效的则真实提前重定向信号realAhdValid拉高 |
| 1.3 | RECERIVE_BACKEND_REDIRECT | STORE_REDIRECT | 后端真实重定向无效时写入寄存器 |
| 2 | CHOOSE_AHEAD | CHOOSE_AHEAD | 真实重定向有效时直接将后端重定向信息传递给FTQ否则取重定向寄存器内的信号作为重定向信息传递给FTQ |
| 3.1.1 | READ_FTQ_SUBQUEUE | READ_AHEAD | 发生提前重定向时,读取子队列需要两个周期 |
| 3.1.2 | READ_FTQ_SUBQUEUE | READ_NO_AHEAD | 未发生提前重定向时,读取子队列需要三个周期 |
| 3.2 | ADD_SUBQUEUE_INFO | ADD_SUBQUEUE_INFO | 将子队列信息整合到后端重定向信号 |

View File

@ -1,72 +0,0 @@
---
title: FTQ接收IFU重定向
linkTitle: 07_FTQ接收IFU重定向
weight: 12
---
## 文档概述
除了后端IFU也会发送重定向相关消息和后端不同IFU的重定向信息来自于预译码写回信息。相同的是它们都是通过BranchPredictionRedirect的接口传递重定向信息。
## 术语说明
| 名称 | 定义 |
| ---------------- | -------------------------------------------------------------------------- |
| RedirectLevel | 重定向等级,重定向请求是否包括本位置,低表示在本位置后重定向,高表示在本位置重定向。它在之后决定了由重定向导致的冲刷信号是否会影响到发生重定向的指令 |
## 模块功能说明
### 1. IFU重定向信号生成
#### 流程
IFU重定向是通过这个BranchPredictionRedirect接口传递的下面来讲述IFU重定向怎么生成IFU的BranchPredictionRedirect内相应信号的这个过程需要两个周期
信号列表:
**第一个周期**
#### 1.1 IFU 重定向触发条件
- valid当预译码写回pdWb有效且pdWb的missOffset字段有效表明存在预测错误的指令同时后端冲刷信号backendFlush无效时valid信号有效。
#### 1.2 IFU生成重定向信号
- ftqIdx接收pdWb指定的ftqIdx
- ftqOffset接收pdWb的missOffset的bits字段
- levelRedirectLevel.flushAfter将重定向等级设置为flushAfter
- BTBMissBubbletrue
- debugIsMemViofalse
- debugIsCtrlfalse
- cfiUpdate
信号列表:
- pcpdWb中记录的指令块中所有指令pc中missOffset对应的pc
- pdpdWb中记录的指令块中所有指令的pd中missOffset对应的pd
- predTaken从cfiIndex_vec子队列中读取pdWb中ftqIdx索引的项是否valid有效说明指令块内被预测为有控制流指令。
- targetpdWb中的target
- takenpdWb中cfiOffset的valid字段有效时表明预译码认为指令块中存在指令控制流指令
- isMisPredpdWb中missOffset的valid字段有效时表明预译码认为指令块中存在预测错误的指令
**第二个周期:**
该周期进行的信号生成是在第一周期valid字段有效的情况下才继续的
- cifUpdate
信号列表:
- 重定向RAS相关信号通过ftqIdx索引从 ftq_redirect_mem读出ftq_Redirect_SRAMEntry把其中的所有信号直接传递给cfiUpdate的同名信号中。
- target已在第一周期写入cfiUpdate的pd有效且isRet字段拉高指明发生预测错误的指令本是一条Ret指令此时将target设置为cfiUpdate的topAddr帮助回到发生错误之前的状态。
### 2. 重定向结果生效
两个周期生成完整的重定向信息后IFU重定向信息才有效有可能被FTQ采取完整的**IFU重定向结果记为ifuRedirectToBpu**
### 3. IFU 冲刷信号 (`ifuFlush`)
**指令流控制信号**
ifuFlush来自IFU的冲刷信号主要是由IFU重定向造成的生成IFU重定向信息的两个周期内该信号都拉高
- 标志IFU重定向信息产生接口BranchPredictionRedirect中valid有效表示开始生成重定向信息该周期以及下一个周期ifuFlush拉高
## 接口说明
| 顶层IO | 作用 |
| ------- | ------------- |
| fromIFU | 接收来自IFU的预译码信息 |
## 接口时序
## 测试点总表
| 序号 | 功能名称 | 测试点名称 | 描述 |
| ---- | ------------------- | ---------------------- | ------------------------------------------------------------------------------ |
| 1\.1 | IFU_REDIRECT | IFU_REDIRECT_GRN_VALID | 当预译码写回pdWb有效且pdWb的missOffset字段有效表明存在预测错误的指令同时后端冲刷信号backendFlush无效时valid信号有效 |
| 1\.2 | IFU_REDIRECT | IFU_REDIRECT_GEN | 允许生成IFU重定向时在两周期内生成具体信号 |
| 2 | IFU_REDIRECT_TO_BPU | IFU_REDIRECT_TO_BPU | IFU重定向生成后IFU重定向结果生效 |
| 3 | IFU_FLUSH | IFU_FLUSH | 生成IFU重定向信息的两个周期内ifuFlush信号都拉高 |
</mrs-testpoints>

View File

@ -1,53 +0,0 @@
---
title: FTQ向后端发送取指目标
linkTitle: 08_FTQ向后端发送取指目标
weight: 12
---
## 文档概述
pc取值目标会发给后端pc mem让他自己进行存储之后从自己的pc mem取指此外最新的FTQ项和对应的跳转目标也会发给后端。
怎样算是一个最新的FTQ项BPU最新发送的预测块可以是最新的FTQ项其次重定向发生时需要回滚到发生错误预测之前的状态从指定的FTQ项开始重新开始预测预译码等等这也可以是被更新的最新的FTQ项。
## 术语说明
| 名称 | 定义 |
| --- | --- |
| 暂无 | 暂无 |
## 模块功能说明
#### 流程
#### 1.发送取值目标到pc mem
- 发送时机bpu_in_fire即BPU向前端发送有效预测信息或者重定向信息的时候。以此为基础之后的第二个周期进行发送通过将toBackend接口的pc_mem_wen设置为true的方式指明开始发送
- 接口信号列表:
- pc_mem_wen设置为true
- pc_mem_waddr接收bpu_in_fire那个周期BPU发送的ftqIdx
- pc_mem_wdata接收bpu_in_fire那个周期FTQ读取的ftq_pc_mem中的取指目标
#### 2.更新最新的FTQ项
- 发送时机:
- 最新的FTQ项可能是由BPU写入最新预测信息造成的**发送取值目标到pc mem**也是因为BPU写入最新预测信息才写入的如果是这种情况造成的更新FTQ项和写入pc mem的时机是一致的。
- 此外发生重定向时也会进行状态回滚更新FTQ项标志是后端接口fromBackend的重定向redirect信号有效或者写入BPU的接口toBPU的redirctFromIFU拉高说明当前有来自IFU的重定向
- *注释可忽略IFU重定向信号生成有两个周期可以认为第一个周期预译码信息中missoffset有效说明IFU重定向发生也可以认为第二个周期redirctFromIFU拉高说明重定向发生此处取后者。*
- 同样是向toBackend中写入
- 接口信号列表:
- newest_entry_en前面说的发送时机到来时再延迟一个周期达到真正的写入时机这时才拉高信号
- newest_entry_ptr发送时机到来时的newest_entry_ptr在真正的写入时机写入
- newest_entry_target发送时机到来时的newest_entry_target
newest_entry_ptrnewest_entry_target这几个都是同名的内部信号如之前所说BPU新的写入重定向等等都会对最新FTQ项进行新的安排在相应的文档中对其生成方式做具体的描述。
## 接口说明
| 顶层IO | 作用 |
| --------- | --------------- |
| toBackend | 发送取指令目标,让后端进行储存 |
## 测试点总表
| 序号 | 功能名称 | 测试点名称 | 描述 |
| --- | ------------------ | ------------- | ------------- |
| 1 | SEND_PC_TO_BACKEND | SEND_PC | 发送取值目标到pc mem |
| 2 | SEND_PC_TO_BACKEND | UPDATE_NEWEST | 更新最新的FTQ项 |
</mrs-testpoints>

View File

@ -1,79 +0,0 @@
---
title: 执行单元修改FTQ状态队列
linkTitle: 09_执行单元修改FTQ状态队列
weight: 12
---
## 文档概述
后端的写回信息,包括重定向信息和更新信息,实际上都是执行之后,由实际执行单元根据结果发回的
## 术语说明
| 名称 | 定义 |
| ------------- | --------------------------------------------- |
| cfiIndex_vec | 控制流指令索引队列,记录每个指令块中控制流指令的索引 |
| update_target | 更新目标队列,记录每个指令块的跳转目标 |
| FTQ最新项 | BPU新的写入重定向等等都会对最新FTQ项进行新的安排表明我们当前关注的最新FTQ项。 |
## 模块功能说明
### 1. 由后端的写回信号修改FTQ状态
#### 1.1 修改FTQ状态队列
从后端写回FTQ接口fromBackend中的redirect接口中我们可以读出validftqPtrftqOffset后端实际执行时确认的控制流指令的偏移takenmispred字段依靠它们来判断如何修改FTQ的状态队列和相关的变量
**后端执行单元写回时被修改的队列**
#### 1.1.1 修改cfiIndex_vec
- cfiIndex_vec
修改方式执行写回修改队列中ftqPtr那一项
- validfromBackend中的redirect接口中valid有效taken有效且ftqOffset小于或者等于cfiIndex_vec中ftqPtr那一项指定的偏移这说明重定向发生实际执行结果判断ftqPtr索引的指令块确实会发生跳转且实际执行跳转的指令在被预测为发生跳转的指令之前或等于它。所以这时指令块是会发生跳转的控制流索引队列的ftqPtr项valid
- bitsfromBackend中的redirect接口中valid有效taken有效且ftqOffset小于cfiIndex_vec中ftqPtr那一项指定的偏移偏移量被更新为更小值ftqOffset。
#### 1.1.2 修改update_target
- update_target
- ftqPtr索引项的跳转目标修改为fromBackend的redirect接口中的cifUpdate中指定的target
#### 1.1.3 修改mispredict_vec
- mispredict_vec
- 如果该重定向指令是来自后端的重定向指令, ftqPtr索引项的ftqOffset偏移指令被设置为fromBackend的redirect接口中的cifUpdate中指定的isMisPred
#### 1.2 修改FTQ最新项
- newest_entry_target
- 被修改为重定向接口中cfiUpdate指定的target
- 辅助信号newest_entry_target_modified被指定为true
- newest_entry_ptr
- 修改为重定向接口指定的ftqIdx
- 辅助信号newest_entry_ptr_modified被指定为true
### 2. 由IFU的写回信号修改FTQ状态
IFU既然也能和后端一样生成重定向信息那么他也能在产生重定向信息的时候修改这些状态队列和FTQ最新项区别
- 但是由于IFU没有真的执行所以它的预译码结果并不能作为决定指令块是不是真的被错误预测了所以它不能修改mispredict_vec的状态
- 其次后端重定向优先级永远高于IFU重定向两者同时发生时只采用后端重定向。
所以这个部分也有以下测试点:
#### 2.1.1 修改cfiIndex_vec
#### 2.1.2 修改update_target
#### 2.2 修改FTQ最新项
## 常量说明
| 常量名 | 常量值 | 解释 |
| --- | --- | ----- |
| 常量1 | 64 | 常量1解释 |
| 常量2 | 8 | 常量2解释 |
| 常量3 | 16 | 常量3解释 |
## 接口说明
| 顶层IO | 子接口 | |
| ----------- | -------- | --- |
| fromBackend | redirect | |
## 测试点总表
实际使用下面的表格时,请用有意义的英文大写的功能名称和测试点名称替换下面表格中的名称
| 序号 | 功能名称 | 测试点名称 | 描述 |
| ------- | ------------------------------ | ------------------------------ | ------------------------- |
| 1\.1\.1 | BACKEDN_REDIRECT_UPDATE_STATE | UPDATE_CFIINDEXVEC | 后端重定向修改cfiinedex状态队列 |
| 1\.1\.2 | BACKEDN_REDIRECT_UPDATE_STATE | UPDATE_UPDATE_TARGET | 后端重定向修改update_target状态队列 |
| 1\.1\.3 | BACKEDN_REDIRECT_UPDATE_STATE | UPDATE_MISPREDICTVEC | 后端重定向修改mispredict状态队列 |
| 1\.2 | BACKEDN_REDIRECT_UPDATE_NEWEST | BACKEDN_REDIRECT_UPDATE_NEWEST | 后端重定向修改FTQ最新项 |
| 2\.1\.1 | IFU_REDIRECT_UPDATE_STATE | UPDATE_CFIINDEXVEC | IFU重定向修改cfiinedex状态队列 |
| 2\.1\.2 | IFU_REDIRECT_UPDATE_STATE | UPDATE_UPDATE_TARGET | IFU重定向修改update_target状态队列 |
| 2\.2 | IFU_REDIRECT_UPDATE_NEWEST | IFU_REDIRECT_UPDATE_NEWEST | IFU重定向修改FTQ最新项 |

View File

@ -1,82 +0,0 @@
---
title: 冲刷指针和状态队列
linkTitle: 10_冲刷指针和状态队列
weight: 12
---
## 文档概述
之前讲了后端和IFU重定向写回会修改一些状态队列。此外FtqPtr也是一种比较重要的维护信息。由后端或者IFU引起的重定向需要恢复各种类型用来索引FTQ项的FtqPtr。而当重定向是由后端发起的时候还要修改提交状态队列说明指令已经被执行。
## 术语说明
| 名称 | 定义 |
| ----- | ------------------------------------ |
| FTQ指针 | 用来索引FTQ项有不同类型的FTQ指针比如bpuPtrifuPtr |
| flush | 冲刷发生时需要重置FTQ指针以及重置其他状态 |
| 融合指令 | 一条指令可以和其他指令融合,形成融合指令 |
## 模块功能说明
### 1. 冲刷FTQ指针及提交状态队列
#### 流程
后端和IFU的重定向信号都会冲刷指针更具体的来说
#### 1.1 冲刷条件
- 后端写回接口fromBackend有效或者IFU重定向有效当预译码写回pdWb有效且pdWb的missOffset字段有效表明存在预测错误的指令同时后端冲刷信号backendFlush无效参考从IFU重定向的第一个周期重定向valid值有效条件
#### 1.2 冲刷指针
第一个周期:
- 冲刷指针确认后端和IFU的重定向信号可能冲刷指针时从两个重定向来源的redirect接口读出重定向信息包括ftqIdxftqOffset重定向等级RedirectLevel。有两个来源时优先后端的重定向信息。
冲刷指针列表:
- bpuPtrftqIdx+1
- ifuPtrftqIdx+1
- ifuWbPtrftqIdx+1
- pfPtrftqIdx+1
*注:只是在当前周期向指针寄存器写入更新信息,实际生效是在下一个周期。*
这样一来,所有类型指针当前指向的都是发生重定向的指令块的下一项了,我们从这一项开始重新进行分支预测,预译码,等等。
#### 1.3 冲刷提交状态队列
第二个周期:
如果上一个周期的重定向来源是后端FTQ会进一步更改提交状态队列
- 提交状态队列中对于重定向的指令块通过ftqIdx索引位于ftqOffset后面的指令的状态被设置为c_empty
- 对于正好处于ftqOffset的指令判断RedirectLevel低表示在本位置后flush高表示在本位置flush所以level为高时对于的指令提交状态被设置为flush。
### 2 转发到顶层IO
实际上在发生重定向的时候还涉及一些将重定向信息通过FTQ顶层IO接口转发给其他模块的操作比如ICache需要flush信号取进行冲刷IFU也需要后端的重定向信号对它进行重定向具体来说
在**流程**的第一个周期:
#### 2.1 flush转发到icacheFlush
- flush信号顶层IO转发icacheFlush
- 确认后端和IFU的重定向信号可能冲刷指针时拉高FTQ顶层IO接口中的icacheFlush信号把重定向产生的flush信号转发给ICache
#### 2.2 重定向信号转发到IFU
- 重定向信号顶层IO转发toIFU
- redirect
- bits接收来自后端的重定向信号
- valid后端的重定向信号有效时有效保持有效直到下个周期依然有效
### 3 重排序缓冲区提交
其实除了后端重定向会更新提交状态队列最直接的更新提交状态队列的方式是通过FTQ顶层IO中frombackend里提供的提交信息rob_commits告知我们哪些指令需要被提交。
rob_commits的valid字段有效可以根据其中信息对指令进行提交修改状态队列。对于被执行的指令是如何提交的如何对应地修改提交状态队列有两种情况
#### 3.1 提交普通指令
- 对于普通指令根据rob_commits的ftqIdx和ftqOffset索引提交状态队列中的某条指令将对应的提交状态设置为c_commited
### 3.2 提交融合指令
- 对于融合指令根据提交类型commitType对被索引的指令和另一与之融合的指令进行提交将对应的提交状态设置为c_commited
1. commitType = 4同时把被索引指令的下一条指令设为c_commited
2. commitType = 5同时把被索引指令的之后的第二条指令设为c_commited
3. commitType = 6同时把被指令块的下一个指令块的第0条指令设为c_commited
4. commitType = 7同时把被指令块的下一个指令块的第1条指令设为c_commited
## 接口说明
| 顶层IO | 作用 |
| ----------- | ----------------- |
| fromBackend | 接收后端重定向和指令提交 |
| fromIfu | 接收IFU重定向 |
| icacheFlush | 将flush信号转发到icache |
| toIFU | 将后端重定向转发到IFU |
## 测试点总表
| 序号 | 功能名称 | 测试点名称 | 描述 |
| ---- | ---------------------------- | ------------------ | ------------------------------------------------------------------------- |
| 1.1 | FLUSH_FTQPTR_AND_COMMITSTATE | FLUSH_COND | 后端写回接口fromBackend有效或者IFU重定向有效时进行冲刷 |
| 1\.2 | FLUSH_FTQPTR_AND_COMMITSTATE | FLUSH_FTQ_PTR | 优先采用后端重定向信息冲刷FTQ指针 |
| 1\.3 | FLUSH_FTQPTR_AND_COMMITSTATE | FLUSH_COMMIT_STATE | 发生后端重定向时,进一步修改提交状态队列 |
| 2\.1 | TRANSFER_TO_TOP | FLUSH | 后端和IFU的重定向信号可能冲刷指针拉高FTQ顶层IO接口中的icacheFlush信号 |
| 2\.2 | TRANSFER_TO_TOP | IFU | 将重定向信号转发到IFU |
| 3\.1 | COMMIT_BY_ROB | NORMAL | 对于普通指令根据rob_commits的ftqIdx和ftqOffset索引提交状态队列中的某条指令将对应的提交状态设置为c_commited |
| 3\.2 | COMMIT_BY_ROB | FUSION | 对于融合指令根据提交类型commitType对被索引的指令和另一与之融合的指令进行提交将对应的提交状态设置为c_commited |

View File

@ -1,241 +0,0 @@
---
title: FTQ向BPU发送更新与重定向信息
linkTitle: 11_FTQ向BPU发送更新与重定向信息
weight: 12
---
## 文档概述
FTQ将已提交指令的更新信息发往BPU进行训练同时转发重定向信息。
## 术语说明
| 名称 | 定义 |
| --- | --- |
| 暂无 | 暂无 |
## 模块功能说明
### 1. 转发重定向
向toBPU接口进行转发
#### 1.1 IFU重定向结果有效
- redirctFromIFUIFU重定向结果有效时拉高该信号注意IFU重定向有效的时机有两种说法因为IFU重定向结果生成需要两个周期此处取后者IFU重定向生成过程的第二个周期有效也是IFU生成完整重定向结果的周期
#### 1.2 选择后端重定向或者IFU重定向
- redirect如果后端重定向结果fromBackendRedirect有效选用fromBackendRedirect否则选用IFU重定向结果ifuRedirectToBpu
### 2 BPU更新暂停
BPU的更新需要两个周期故需要三种状态去表明我们当前的更新状态更新的第一个周期第二个周期更新完成。
当发生更新的时候会暂停FTQ对指令块的提交以及发送更新信息。
### 3 提交指令块
FTQ需要对当前comPtr指向的当前提交指令块进行判断是否能够提交。
这个过程比较复杂。
由于 香山V2版本 的后端会在 ROB 中重新压缩 FTQ entry因此并不能保证提交一个 entry 中的每条指令,甚至不能保证每一个 entry 都有指令提交。
**判断一个 entry 是否被提交有如下几种可能**
- robCommPtr 在 commPtr 之后ptr更大。也就是说后端已经开始提交之后 entry 的指令,在 robCommPtr 指向的 entry 之前的 entry 一定都已经提交完成
- commitStateQueue 中的某个指令块内最后一条有效范围内指令被提交。FTQ项中该指令被提交意味着这FTQ项内的指令已经全部被提交
在此以外,还必须要考虑到,后端存在 flush itself 的 redirect 请求这意味着这条指令自身也需要重新执行这包括异常、load replay 等情况。在这种情况下这一FTQ项不应当被提交以更新 BPU否则会导致 BPU 准确率显著下降。
#### 3.1 canCommit
具体来看判断commPtr指向的指令块能否提交如果可以提交记为canCommit。
canCommit的设置条件如下
#### 3.1.1 COND1
- 当commPtr不等于ifuWbPtr且没有因为BPU更新而暂停同时robCommPtr在commPtr之后。之所以要求commPtr不等于ifuWbPtr是因为前面说过了必须先预译码写回FTQ项才能提交
#### 3.1.2 COND2
- commitStateQueue 中commPtr对应指令块有指令处于c_toCommit 或c_committed状态。且指令块中最后一条处于c_toCommit 或c_committed状态的指令是c_committed的。
这两种情况下canCommit拉高说明可以提交该指令块
### 3.2 canMoveCommPtr
#### 3.2.1 提交指令块更新提交指针
在commPtr指向的指令块如果能提交那么我们自然可以移动CommPtr指向下一个FTQ项了。
#### 3.2.2 指令冲刷更新提交指针
但除此之外commitStateQueue 中commPtr对应指令块的第一条指令被后端重定向冲刷掉了时这表明该指令需要重新执行这一FTQ项不应被提交但是却可以更新CommPtr指针因为该指令块内已经没有可以提交的指令了。
- CanMoveCommPtr时commPtr指针更新加1一周期后成功写入
### 3.3 robCommPtr更新
有几种情况
#### 3.3.1 COND1
- 当来自后端接口fromBackend的rob_commits信息中有信息有效时取最后一条有效交割信息的ftqIdx作为robCommPtr
#### 3.3.2 COND2
- 不满足情况1选取commPtr, robCommPtr中较大的那个
### 3.4 mmio提交
发往mmioCommitRead接口
- mmioLastCommit
#### 3.4.1 COND1
- 当commPtr比来自mmioCommitRead接口的mmioFtqPtr大时
#### 3.4.2 COND2
- 或者两者正好相等且commPtr指向的指令块中有c_toCommit 或c_committed状态的指令最后一条处于c_toCommit 或c_committed状态的指令是c_committed的
在这两种情况下mmioLastCommit信号在下一个周期被拉高
### 4 发送BPU更新信息
FTQ需要从FTQ子队列中读取提交项的预测信息重定向信息meta信息用这些信息来对BPU发送更新信息。
当canCommit时可以提交commPtr指向的指令块时从ftq_pd_memftq_redirect_mem,ftq_meta_1r_sram_mem这些子队列以及一些小的状态队列中读出对应指令块的相应信息这些信息需要专门花一个周期才能读取到。具体来说
- 从预译码信息子队列ftq_pd_mem中读取提交提交指令块commptr所指的预译码信息
- 从取指目标子队列ftq_pc_mem中读取取指信息
- 从分支预测重定向信息子队列ftq_redirect_mem中读取提交指令块的重定向信息。
- 从预测阶段状态队列中读取提交块来自BPU的哪个预测阶段
- 从meta信息子队列ftq_meta_1r_sram中读取提交指令块的meta和相应的ftb_entry。
- 从提交状态队列commitStateQueueReg中读取提交状态并确认指令块中哪些指令为c_committed,用bool数组表示
- 从控制流索引状态队列cfiIndex_vec中读取指令控制流指令在块中索引
- 结合错误预测状态队列mispredict_vec和提交状态队列信息确认指令块中的提交错误指令。(即提交状态指示为c_commited 同时错误预测指示为预测错误)
- 从表项命中状态队列entry_hit_status中读取提交指令块是否命中
根据相关信息进行判断:
- 获取提交块的目标如果commPtr等于newest_entry_ptr则取newest_entry_target_modified拉高时记录下的newest_entry_target否则取ftq_pc_mem.io.commPtrPlus1_rdata.startAddr获取到的提交块目标将会被用来辅助新FTB项的生成
#### 4.1 将子队列读取信息发向更新通道
整合完上述信息后FTQ会向toBpu的update接口发送更新请求具体如下
- validcanCommit 且 指令块满足命中或者存在cfi指令valid接口有效表明可以发送更新请求
- bits
- false_hit提交块命中状态指示为h_false_hit时该信号拉高
- pc提交块的取指信息中的startAddr
- meta提交块的meta
- cfi_idx提交块中cfi指令的index
- full_target提交块的目标
- from_stage提交块来自哪个预测阶段
- spec_info提交块的meta
- pred_hit提交块的命中状态为hit或者false_hit
另外被更新的FTB表项也会**同时**被转发到更新接口但是新的FTB表项生成方式相对复杂下一节专门展开叙述
#### 4.2 修正FTB项
更新结果会基于旧的FTB项进行更新然后直接转发给更新接口。你可能需要先阅读[FTB项相关文档](https://open-verify.cc/xs-bpu/docs/ports/00_ftb/)了解FTB项的结构和相关信号生成方式
commit表项的相关信息会被发送给一个名为FTBEntryGen的接口经过一系列组合电路处理输出更新后的FTB表项信息。
为了更新FTB项提交项如下信息会被读取
- 取值目标中的起始地址 startAddr
- meta中旧FTB项 old_entry
- 包含FTQ项内32Byte内所有分支指令的预译码信息 pd
- 此FTQ项内有效指令的真实跳转结果 cfiIndex包括是否跳转以及跳转指令相对startAddr的偏移
- 此FTQ项内分支指令如跳转的跳转地址执行结果
- 预测时FTB是否真正命中旧FTB项是否有效
- 对应FTQ项内所有可能指令的误预测 mask
接下来介绍如何通过这些信息更新FTB。
FTB项生成逻辑
##### 4.2.1 **情况1FTB未命中则创建一个新的FTB项**
*我们会根据预译码信息进行判断预译码会告诉我们指令块中cfi指令是否是br指令jmp指令信息以及是哪种类型的jmp指令*
1) 无条件跳转指令处理:
- 不论是否被执行都一定会被写入新FTB项的tailSlot
- 如果最终FTQ项内跳转的指令是条件分支指令写入新FTB项的第一个brSlot目前也只有这一个对应的strongbias被设置为1作为初始化
2) pftAddr设置
- 存在无条件跳转指令时:以无条件跳转指令的结束地址设置
- 无无条件跳转指令时以startAddr+取指宽度32B设置
- 特殊情况当4Byte宽度的无条件跳转指令起始地址位于startAddr+30时虽然结束地址超出取指宽度范围仍按startAddr+32设置
3) carry位根据pftAddr的条件同时设置
4) 设置分支类型标志:
- isJalr、isCall、isRet按照无条件跳转指令的类型设置
- 特殊标志当且仅当4Byte宽度的无条件跳转指令起始地址位于startAddr+30时置last_may_be_rvi_call位
*详细信号说明*
- cfiIndex有效说明指令块存在跳转指令且pd的brmask指明该指令是br指令。则判断控制流指令是br指令
- pd的jmpinfo有效且cifIndx有效。则进一步根据jmpinfo判断是那种类型的jmp指令
1. 第零位为0jal
2. 第零位为1jalr
3. 第一位为1call
4. 第二位为1ret
- 判断最后一条指令是否是rvi4byte的jmp指令jmpinfo有效pd中jmpOffset等于15且pd的rvcMask指明最后一条指令不是rvc指令
- 判断cfi指令是否是jal指令cfiindx = jmpOffset且根据之前的判断确认jmp指令是jal指令
- 判断cfi指令是jalr指令也是同理的。
- FTB生成valid被初始化为true
- brslot在判断控制流指令是br指令时进行填充
- valid初始化为true
- offsetcfiindx
- lower和stat根据startaddr和提交块指定的target计算
- 对应的strongbias被初始化为true
- tailslotpd的jmpinfo有效时进行填充
- valid根据之前的判断确认jmp指令是jal指令或者是jalr指令时valid有效
- offsetpd的jmpoffset
- lower和stat根据startaddr和target计算如果cfi指令是jalr指令使用提交块指定的target否则用pd预测的jalTarget
- 对应的strongbias根据之前的判断确认jmp指令是jalr指令时拉高。strongbias是针对于BPU的ittage预测器的该预测器基于一些统计信息工作strongbias用来指向指令跳转偏好的强弱其中jal指令不需要记录strongbias。
- pftAddr上方介绍已经够详细了
- carry上方介绍已经足够
- isJalr/isCall/isRet
- last_may_be_rvi_call
#### 4.2.2 情况2FTB命中修改旧的FTB项
##### 4.2.2.1 插入brslot的FTB项
*在原来的基础上改动即可比如插入新的slot注意只针对新的brslot*
1. **修改条件**首先根据oldftbentry判断在旧entry中cfi指令是否被记录为br指令如果不是**则说明这是一个新的br指令**。
2. 接着从旧FTB中判断哪些slot可以被插入slot
- brslot如果旧FTB的brslot无效表示该slot空闲此时可以在此位置插入新的brslot此外如果新slot在旧slot之前新的br指令在旧slotbr指令之前执行或者说在指令块之前的位置即使不空也能插入
- tailslot当不能在brslot插入时才考虑tailslot同样在该slot空闲或者新slot在旧slot之前可以插入此位置
3. 插入slot
1. brslot能插入时则在这里插入不能的时候把对应的strongbias拉低因为这说明新slot一定在旧slot之后如果不想要详细了解ittage的原理可以不用理解原因
2. tailslot能插入时则在这里插入不能的时候如果新slot在旧slot之后把对应的strongbias拉低如果不在之后当原brslot有效即不空闲则用插入前的brslot代替该tailslot。对应的strongbias维持不变。
*注tailslot不能插入且新slot在其之前其实就已经说明brslot一定是可以插入的所以才有后面的替代*
***pftaddr***
出现新的br指令同时旧的FTB项内没有空闲的slot这说明确实发生了在FTB项内确实发生了FTB项的替换pftaddr也需要做相应的调整。
- 如果没有能插入的位置使用新的br指令的偏移作为pftaddr对应的偏移因为此时新br指令一定在两个slot之后。否则使用旧FTB项的最后一个slot的offset。将ptfoffset结合startAddr得到最后的pftAddrcarry也进行相应的设置。
- last_may_be_rvi_callisCallisRet isJalr全部置false。
##### 4.2.2.2 修改jmp target的FTB项
**修改条件****当cfi指令是一个jalr指令**且旧的tailslot对应的是一个jump的指令但tailslot指示的target与提交项指示的target不同时说明需要对跳转目标进行修改。
- 根据正确的跳转目标对lower和stat进行修改
- 两位strongbias设置成0
##### 4.2.2.3 修改bias的FTB项
**当cfi指令就是原FTB项的条件跳转指令**,只需要根据跳转情况设置跳转的强弱
- brslot旧的brslot有发生跳转时bias在原bias拉高发生跳转的cfiindex等于该slot的offsetbrslot有效时保持拉高其余情况拉低。
- tailslot旧的brslot没有跳转而tailslot有分支指令且发生跳转把brslot的bias置为falsetailslot保持bias的方式与上面的brslot一致。
**修改条件**当旧的bias拉高且对应的旧的FTB项中的slot中有分支指令同时修改后的bias拉低。任何一个slot出现这种情况都需要进行修改。
最后需要抉择出一个修改的FTB项
- 如果cfi是一个新的分支指令我们采用插入新的slot的FTB项。
- 如果是cfi是一个jalr指令且跳转目标发生修改我们采用修改jmp跳转目标的FTB项
- 如cfi指令就是原FTB项的条件跳转指令采用修改bias的FTB项
#### 4.3 发送新FTB项及相关信号
此时根据是否hit我们已经得到更新后的FTB项了在这个基础上我们继续更新一些相关信号以发送到FTQ更新接口。
- new_br_insert_pos使用之前我们判断的FTB项中可插入位置的bool数组
- taken_mask根据cfi指令在更新后FTB项的位置判断只有分支指令才做此计算若是jmp指令置为0。
- jump_taken: cfi指令在更新后FTB项的taislot且jmpValid。
- mispred_mask的最后一项更新后的FTB项jumpValid且预译码推断的jmp指令在提交项的错误预测信息中指示错误。
- **mispred_mask** 预测块内预测错误的掩码。第一、二位分别代表两个条件分支指令是否预测错误,第三位指示无条件跳转指令是否预测错误。
- 接口类型:`Vec(numBr+1, Bool())`
- old_entry如果hit且FTB项不做任何修改即不满足上述三种修改FTB项的条件拉高该信号说明更新后的FTB项是旧的FTB项。
##### 发送处理后的更新信息
此时我们就可以向BPU发送处理好的更新信息了下面是update的接口接收的信号
- ftb_entry更新后的FTB项
- new_br_insert_pos上一小节已述
- mispred_mask上一小节已述
- old_entry上一小节已述
- br_taken_mask: 上一小节已述
- br_committed根据提交项的提交状态信息判断新FTB项中的有效分支指令是否已经提交
- jmp_taken上一小节已述
## 接口说明
| 顶层IO | | 作用 |
| ------------- | --- | ------------------- |
| toBpu | | 向BPU发送重定向信息与更新信息 |
| fromBackend | | 获取指令交割信息,判断指令块是否被提交 |
| mmioCommiRead | | 发送mmio指令的提交信息 |
## 测试点总表 (【必填项】针对细分的测试点,列出表格)
实际使用下面的表格时,请用有意义的英文大写的功能名称和测试点名称替换下面表格中的名称
| 序号 | 功能名称 | 测试点名称 | 描述 |
| ---------- | -------------------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| 1\.1 | TRANSFER_REDIRECT | REDIRECT_FROM_FLUSH | IFU重定向结果有效时拉高该信号 |
| 1\.2 | TRANSFER_REDIRECT | CHOOSE_REDIRECT | 如果后端重定向结果fromBackendRedirect有效选用fromBackendRedirect否则选用IFU重定向结果ifuRedirectToBpu |
| 2 | UPDATE_STALL | UPDATE_STALL | 当发生BPU的更新时候会暂停FTQ对指令块的提交以及发送更新信息 |
| 3\.1\.1 | CAN_COMMIT_ENTRY | COND1 | 当commPtr不等于ifuWbPtr且没有因为BPU更新而暂停同时robCommPtr在commPtr之后,canCommit拉高 |
| 3\.1\.2 | CAN_COMMIT_ENTRY | COND2 | commitStateQueue 中commPtr对应指令块有指令处于c_toCommit 或c_committed状态。且指令块中最后一条处于c_toCommit 或c_committed状态的指令是c_committed的,canCommit拉高 |
| 3\.2\.1 | MOVECOMMPTR | BY_ROB_COMMIT | 在commPtr指向的指令块如果能提交,可以移动CommPtr |
| 3\.2\.2 | MOVECOMMPTR | BY_FLUSH | commitStateQueue 中commPtr对应指令块的第一条指令被后端重定向冲刷掉,可以移动CommPtr |
| 3\.3.1 | UPDATE_ROB_COMM_PTR | COND1 | 当来自后端接口fromBackend的rob_commits信息中有信息有效时取最后一条有效交割信息的ftqIdx作为robCommPtr |
| 3\.3.2 | UPDATE_ROB_COMM_PTR | COND2 | 不满足情况1选取commPtr, robCommPtr中较大的那个 |
| 3\.4\.1 | MMIO_LAST_COMMIT | COND1 | 当commPtr比来自mmioCommitRead接口的mmioFtqPtr大时,mmioLastCommit信号在下一个周期被拉高 |
| 3\.4\.2 | MMIO_LAST_COMMIT | COND2 | 两者正好相等且commPtr指向的指令块中有c_toCommit 或c_committed状态的指令最后一条处于c_toCommit 或c_committed状态的指令是c_committed的,mmioLastCommit信号在下一个周期被拉高 |
| 4\.1 | SEND_UPDATE_TO_BPU | SEND_SUBQUEUE_INFO_TO_UPDATE | 将提交项的子队列读取信息发向更新通道 |
| 4\.2.1 | UPDATE_FTB_ENTRY | CREATE_NEW | FTB未命中创建一个新的FTB项 |
| 4\.2\.2\.1 | CREATE_NEW_FTB_ENTRY | INSERT | FTB未命中创建一个新的FTB项,在原来的基础上改动即可插入新的slot |
| 4\.2\.2\.2 | CREATE_NEW_FTB_ENTRY | jmp target | FTB未命中创建一个新的FTB项,在原来的基础上改动即可,**当cfi指令是一个jalr指令**且旧的tailslot对应的是一个jump的指令但tailslot指示的target与提交项指示的target不同时说明需要对跳转目标进行修改 |
| 4\.2\.2\.3 | CREATE_NEW_FTB_ENTRY | bias | FTB未命中创建一个新的FTB项,在原来的基础上改动即可,**当cfi指令就是原FTB项的条件跳转指令**,只需要根据跳转情况设置跳转的强弱 |
| 4\.3 | SEND_UPDATE_TO_BPU | SEND_NEW_FTB_RELATED | 根据是否hit我们已经得到更新后的FTB项了在这个基础上我们继续更新一些相关信号以发送到FTQ更新接口。 |

View File

@ -1,70 +0,0 @@
---
title: FTQ概述
linkTitle: FTQ
weight: 12
---
下文包括所有的FTQ文档中会提到一些关于BPU和IFU的相关知识详情需要去查看对应的文档:
- [BPU文档链接](https://open-verify.cc/xs-bpu/docs/)
- [IFU文档链接](https://open-verify.cc/UnityChipForXiangShan/docs/98_ut/01_frontend/01_ifu/)
*hint建议先从BPU基础设计中着重理解以下概念*
1. 什么是分支预测?
2. 什么是分支预测块?一个有帮助的链接:[预测块](https://docs.xiangshan.cc/zh-cn/latest/frontend/bp/#pred-block)
3. (可选)什么是重定向,什么是预测结果重定向?
4. (可选)分支预测的流水级
# 简介
FTQ 是分支预测和取指单元之间的缓冲队列它的主要职能是**暂存 BPU 预测的取指目标**,并根据这些取指目标**给 IFU 发送取指请求**。它的另一重要职能是**暂存 BPU 各个预测器的预测信息**在指令提交后把这些信息送回 BPU 用作预测器的训练因此它需要**维护指令从预测到提交的完整的生命周期**。另外后端将存储来自FTQ的取指目标PC便于自身读取。
![[Pasted image 20250222103931.png]]
# 模块之间的中转站
从上图FTQ很大程度上相当于一个中转站中间人的角色一方面它承担着BPU和IFU之间的交互这通常是因为BPU预测的速度快于IFU取值执行所以使用FTQ作为缓冲。另一方面它承担着后端与前端的交互比如把前端将要执行的pc交给后端去执行。
显然FTQ的 中转远不止这么多下面更具体地讨论一下FTQ怎么中转各个前端或后端模块的信息的。
## BPU和FTQ
BPUtoFTQBPU会将分支预测结果和meta数据发给FTQ。
- 从分支预测结果中我们可以提取出分支预测块对应的取值目标比如一个不跨缓存行且所有指令均为RVC指令的分支预测块对应的取值目标是从分支预测块起始地址开始的以2B为间隔的连续16条指令。
- meta信息则存储了各个预测器相关的预测信息由于BPU预测有三个流水级每个流水级都有相应的预测器所以只有到s3阶段才有可能收集到所有预测器的预测信息直到此时FTQ才接受到完整的meta这些信息会在该分支预测块的全部指令被后端提交时交给BPU进行训练
- FTBEntry严格来说它其实也是meta的一部分但是因为更新的时候ftb_entry需要在原来的基础上继续修改为了不重新读一遍ftb另外给它存储一个副本。
FTQtoBPUFTQ会将带元数据的训练信息和重定向信息发回给BPU
- [请参照BPU文档链接](https://open-verify.cc/xs-bpu/docs/ports/02_global_ports/) **BPU 模块整体对外接口 (PredirectIO)**
## FTQ和IFU
FTQtoIFUFTQ会将存储的取值目标发往IFU进行取值译码和把后端的重定向信息也移交给IFU
- 取值目标同时也发给:
- toICache同样的取值目标会被发给指令缓存单元看对应的指令是否在缓存单元内存在如果有会被直接发送给IFU加速取值效率
- toPrefetch: prefetch是ICache的一个组件负责预取功能
- 转发后端重定向:
- 后端重定向不仅需要转发给BPU帮助其回到正确状态也同时需要转发给IFU帮助其回到正确状态
IFUtoFTQIFU将预译码信息和重定向信息写回FTQ
- 预译码信息:包含分支预测块对应的预测宽度内所有指令的预译码信息
***预测宽度一个指令块预测块覆盖的指令范围香山中是16条rvc指令***
- 重定向信息其实也是根据预译码信息得到的当预译码信息中指出预测块内某一条指令预测出错时写回IFU重定向信息
## 后端和FTQ
FTQ到后端FTQ会将存储的取值目标发往后端后端存储 PC后在本地即可进行读取取指目标。
- 除了IFU预测块的取值目标也会发给后端但这里有一点区别IFU空闲时才能从FTQ中获取取值目标但是后端会一直取得最新的预测块的取指目标
后端到FTQ后端重定向和指令commit
- 后端重定向与更新:后端是实际执行指令的单元,通过后端的执行结果,才能确认一条指令是否执行错误,产生重定向,同时,在发生重定向时,根据后端实际执行结果生成更新信息。
- 指令commit当一个分支预测块内的所有指令都被执行在后端提交这标志着FTQ队列中这个分支预测块对应的FTQ项已经结束了它的生命周期可以从队列中移除了这时候我们就可以把它的更新信息发给FTQ了。
# FTQ指针
FTQ的全名叫取值目标队列队列中的一个项叫做FTQ项BPU写入预测结果时是写入队列中哪个位置IFU又是从哪个队列取FTQ项这时候我们需要一个FTQ指针去索引FTQ项而由于和不同模块的交互需要索引不同的FTQ项因此有以下类型的FTQ指针下面由指令生命周期为例大致介绍这些指针
## 指令在 FTQ 中的生存周期
指令以[预测块](https://docs.xiangshan.cc/zh-cn/latest/frontend/bp/#pred-block)为单位 BPU 预测后便送进 FTQ直到指令所在的[预测块](https://docs.xiangshan.cc/zh-cn/latest/frontend/bp/#pred-block)中的所有指令全部在后端提交完成FTQ 才会在存储结构中完全释放该[预测块](https://docs.xiangshan.cc/zh-cn/latest/frontend/bp/#pred-block)所对应的项。这个过程中发生的事如下:
1. 预测块从 BPU 发出进入 FTQ`bpuPtr` 指针加一初始化对应 FTQ 项的各种状态把各种预测信息写入存储结构如果预测块来自 BPU 覆盖预测逻辑则恢复 `bpuPtr` 和 `ifuPtr`
2. FTQ  IFU 发出取指请求`ifuPtr` 指针加一,等待预译码信息写回
3. IFU 写回预译码信息`ifuWbPtr` 指针加一如果预译码检测出了预测错误则给 BPU 发送相应的重定向请求恢复 `bpuPtr` 和 `ifuPtr`
4. 指令进入后端执行如果后端检测出了误预测则通知 FTQ IFU  BPU 发送重定向请求恢复 `bpuPtr`、`ifuPtr` 和 `ifuWbPtr`
5. 指令在后端提交通知 FTQ FTQ 项中所有的有效指令都已提交`commPtr` 指针加一从存储结构中读出相应的信息送给 BPU 进行训练
预测块 `n` 内指令的生存周期会涉及到 FTQ 中的 `bpuPtr`、`ifuPtr`、`ifuWbPtr` 和 `commPtr` 四个指针,当 `bpuPtr` 开始指向 `n+1` 时,预测块内的指令进入生存周期,当 `commPtr` 指向 `n+1` 后,预测块内的指令完成生存周期。
## 循环队列
FTQ队列实际上是一个循环队列所有类型的FTQ指针都是同一类型ftqPtr的value字段用来表示索引flag字段则用来表示循环轮数flag只有一位进入新的循环时flag位翻转。
这样,我们就可以在一个有限的队列空间内不断更新新的项,以及正确进行比较,判断哪个项在队列中更靠前或者更靠后。

View File

@ -1,424 +0,0 @@
---
title: IPrefetchPipe
linkTitle: IPrefetchPipe
weight: 12
---
<div class="icache-ctx">
</div>
## IPrefetchPipe
IPrefetchPipe 为预取的流水线,三级流水设计,负责预取请求的过滤。
<div>
<center>
<img src="../iprefetchpipe_structure.png"
alt="IPrefetchPipe模块结构示意图"
style="zoom:100%"/>
<br>
IPrefetchPipe结构示意图
</center>
</div>
<br>
1. 接收预取请求s0 阶段):
- 从 FTQ 或后端接收预取请求。
- 发送读请求到 ITLB 和 MetaArray 缓存元数据模块。
2. 地址转换和缓存检查s1 阶段):
- 接收 ITLB 的地址转换结果,处理可能的缺失和重发。
- 从缓存元数据中读取标签和有效位,检查是否命中。
- 进行 PMP 权限检查,合并异常信息。
- 根据情况决定是否发送请求到 WayLookup 模块。
3. 未命中请求处理s2 阶段):
- 检查与 missUnit 的交互,更新命中状态。
- 对于无异常的未命中请求,向 missUnit 发送请求以获取数据。
- 控制流水线的推进和刷新,处理可能的阻塞和异常。
### S0 流水级
在 S0 流水级,接收来自 FTQ 的预取请求,向 MetaArray 和 ITLB 发送请求。
- 接收预取请求:从 FTQ 或后端接收预取请求提取预取请求的虚拟地址、FTQ 索引、是否为软件预取、是否跨缓存行信、虚拟组索引s0_req_vSetIdx和后端的异常信息。
- 发送请求到 ITLB将虚拟地址发送到 ITLB 进行地址转换。
- 发送请求到缓存元数据Meta SRAM将请求发送到缓存的元数据存储器以便在后续阶段读取缓存标签和有效位。
### S1 流水级
软件预取 enqway 持续一拍...
- 接收 ITLB 的响应:从 ITLB 接收地址转换的结果,包括物理地址 paddr、异常类型(`af`/`pf`)和特殊情况(`pbmt.nc`/`pbmt.io`)。
- 接收缓存元数据的响应并检查缓存命中:从缓存元数据存储器 MetaArray 读取缓存标签 `tag` 和有效位,检查预取地址是否在缓存中已存在,命中结果存入 `waymask` 中。
- 权限检查:使用 PMP 对物理地址进行权限检查,确保预取操作的合法性。
- 异常处理和合并合并来自后端、ITLB、PMP 的异常信息,准备在后续阶段处理。
- 发送请求到 WayLookup 模块:当条件满足时,将元数据(命中信息 `waymask`、ITLB 信息 `paddr`/`af`/`pf`)发送到 WayLookup 模块,以便进行后续的缓存访问。
- 状态机转换:根据当前状态和条件,更新下一个状态。
- 状态机初始状态为 `idle`,当 S1 流水级进入新的请求时,首先判断 ITLB 是否缺失,如果缺失,就进入 `itlbResend`;如果 ITLB 命中但命中信息未入队 WayLookup就进入 `enqWay`;如果 ITLB 命中且 WayLookup 入队但 S2 请求未处理完,就进入 `enterS2`
- 在 `itlbResend` 状态,重发 ITLB 请求,此时占用 ITLB 端口,直至请求回填完成,在回填完成的当拍向 MetaArray 再次发送读请求,回填期间可能发生新的写入,如果 MetaArray 繁忙(正在被写入),就进入`metaResend`,否则进入 `enqWay`
- 在 `metaResend` 状态,重发 MetaArray 读请求,发送成功后进入 `enqWay`
- 在 `enqWay` 状态,尝试将元数据入队 WayLookup如果 WayLookup 队列已满,就阻塞至 WayLookup 入队成功,另外在 MSHR 发生新的写入时禁止入队,主要是为了防止写入的信息与命中信息所冲突,需要对命中信息进行更新。当成功入队 WayLookup 或者是软件预取时,如果 S2 空闲,就直接进入 `idle`,否则进入 `enterS2`
- 在 `enterS2` 状态,尝试将请求流入下一流水级,流入后进入 `idle`
<div> <!--块级封装-->
<center> <!--将图片和文字居中-->
<img src="../iprefetchpipe_fsm.png"
alt="IPrefetchPipe状态机"
style="zoom:100%"/>
<br> <!--换行-->
IPrefetchPipe S1状态机 <!--标题-->
</center>
</div>
<br>
### S2 流水级
- 监控 missUnit 的请求:更新 MSHR 的匹配状态。综合该请求的命中结果、ITLB 异常、PMP 异常、meta 损坏,判断是否需要预取,只有不存在异常时才进行预取。
- 发送请求到 missUnit因为同一个预测块可能对应两个 cacheline所以通过 Arbiter 依次将请求发送至 MissUnit。
### 命中信息的更新
在 S1 流水级中得到命中信息后,距离命中信息真正在 MainPipe 中被使用要经过两个阶段,分别是在 IPrefetchPipe 中等待入队 WayLookup 阶段和在 WayLookup 中等待出队阶段,在等待期间可能会发生 MSHR 对 Meta/DataArray 的更新,因此需要对 MSHR 的响应进行监听,分为两种情况:
1. 请求在 MetaArray 中未命中,监听到 MSHR 将该请求对应的 cacheline 写入了 SRAM需要将命中信息更新为命中状态。
2. 请求在 MetaArray 中已经命中,监听到同样的位置发生了其它 cacheline 的写入,原有数据被覆盖,需要将命中信息更新为缺失状态。
为了防止更新逻辑的延迟引入到 DataArray 的访问路径上,在 MSHR 发生新的写入时禁止入队 WayLookup在下一拍入队。
### 刷新机制
在 IPrefetch 中如果收到后端重定向、IFU 预译码、fencei 带来的刷新,就冲刷整个流水线
IPrefetchPipe 模块中的刷新信号主要来自以下两个方面:
1. 全局刷新信号:由系统的其他模块发出的全局刷新信号,如怀疑流水线中存在错误数据或需要清除流水线时触发。
- io.flush模块输入的全局刷新信号。当系统需要清除所有流水线阶段的数据时该信号被置为高。
2. 来自分支预测单元BPU的刷新信号当分支预测错误或需要更新预测信息时BPU 会发出刷新信号。
- io.flushFromBpu包含来自 BPU 的刷新信息,指示哪些指令需要被刷新。
## IPrefetchPipe 的功能点和测试点
### 接收预取请求
从 FTQ 接收预取请求,请求可能有效( io.req.valid 为高),可能无效; IPrefetchPipe 可能处于空闲( io.req.ready 为高),可能处于非空闲状态。
只有在请求有效且 IPrefetchPipe 处于空闲状态时,预取请求才会被接收(这里暂不考虑 s0 的刷新信号 s0_flush ,默认其为低)。
预取请求分为不同类型,包括硬件预取请求 (isSoftPrefetch = false)和软件预取请求 (isSoftPrefetch = true)。
cacheline 也分为单 cacheline 和双 cacheline。
1. 硬件预取请求:
预取请求为硬件 (isSoftPrefetch = false)
1. 预取请求可以继续:
- 当预取请求有效且 IPrefetchPipe 处于空闲状态时,预取请求应该被接收。
- s0_fire 信号在没有 s0 的刷新信号( s0_flush 为低)时,应该被置为高。
2. 预取请求被拒绝--预取请求无效时:
- 当预取请求无效时,预取请求应该被拒绝。
- s0_fire 信号应该被置为低。
3. 预取请求被拒绝--IPrefetchPipe 非空闲时:
- 当 IPrefetchPipe 非空闲时,预取请求应该被拒绝。
- s0_fire 信号应该被置为低。
4. 预取请求被拒绝--预取请求无效且 IPrefetchPipe 非空闲时:
- 当预取请求无效且 IPrefetchPipe 非空闲时,预取请求应该被拒绝。
- s0_fire 信号应该被置为低。
5. 预取请求有效且为单 cacheline 时:
- 当预取请求有效且为单 cacheline 时,预取请求应该被接收。
- s0_fire 为高s0_doubleline 应该被置低false
6. 预取请求有效且为双 cacheline 时:
- 当预取请求有效且为双 cacheline 时,预取请求应该被接收。
- s0_fire 为高s0_doubleline 应该被置高true
2. 软件预取请求:
预取请求为软件 (isSoftPrefetch = true)
1. 软件预取请求可以继续:
- 当预取请求有效且 IPrefetchPipe 处于空闲状态时,软件预取请求应该被接收。
- s0_fire 信号在没有 s0 的刷新信号( s0_flush 为低)时,应该被置为高。
2. 软件预取请求被拒绝--预取请求无效时:
- 当预取请求无效时,软件预取请求应该被拒绝。
- s0_fire 信号应该被置为低。
3. 软件预取请求被拒绝--IPrefetchPipe 非空闲时:
- 当 IPrefetchPipe 非空闲时,软件预取请求应该被拒绝。
- s0_fire 信号应该被置为低。
4. 软件预取请求被拒绝--预取请求无效且 IPrefetchPipe 非空闲时:
- 当预取请求无效且 IPrefetchPipe 非空闲时,软件预取请求应该被拒绝。
- s0_fire 信号应该被置为低。
5. 软件预取请求有效且为单 cacheline 时:
- 当软件预取请求有效且为单 cacheline 时,软件预取请求应该被接收。
- s0_fire 为高s0_doubleline 应该被置低false
6. 软件预取请求有效且为双 cacheline 时:
- 当软件预取请求有效且为双 cacheline 时,软件预取请求应该被接收。
- s0_fire 为高s0_doubleline 应该被置高true
### 接收来自 ITLB 的响应并处理结果
接收 ITLB 的响应,完成虚拟地址到物理地址的转换。
当 ITLB 发生缺失miss保存请求信息等待 ITLB 完成后再继续处理。
1. 地址转换完成:
- 根据 ITLB 的响应接收物理地址paddr并完成地址转换。
- 处理 ITLB 响应可能在不同周期到达的情况,管理有效信号和数据保持机制,确保正确使用物理地址。
1. 当 ITLB 正常返回物理地址时:
- ITLB 在一个周期内成功返回物理地址 paddrs1_valid 为高。
- 确认 s1 阶段正确接收到 paddr。
2. 当 ITLB 发生 TLB 缺失,需要重试时:
- fromITLB(PortNumber).bits.miss 为高,表示对应通道的 ITLB 发生了 TLB 缺失,需要重发。
- 重发完成后后续步骤继续进行fromITLB(PortNumber).bits.miss 为低。
2. 处理 ITLB 异常:
- 根据 ITLB 的异常信息处理可能的异常。pf 缺页、pgf 虚拟机缺页、af 访问错误。
1. 当 ITLB 发生页错误异常时:
- s1_itlb_exception 返回的页错误。
- iTLB 返回的物理地址有效fromITLB(PortNumber).bits.miss 为低s1_itlb_exception 指示页错误 pf。
2. 当 ITLB 发生虚拟机页错误异常时:
- s1_itlb_exception 返回的虚拟机页错误。
- iTLB 返回的物理地址有效fromITLB(PortNumber).bits.miss 为低s1_itlb_exception 指示虚拟机页错误 pgf。
3. 当 ITLB 发生访问错误异常时:
- s1_itlb_exception 返回的访问错误。
- iTLB 返回的物理地址有效fromITLB(PortNumber).bits.miss 为低s1_itlb_exception 指示访问错误 af。
3. 处理虚拟机物理地址(用于虚拟化):
- 在虚拟化环境下处理虚拟机物理地址gpaddr确定访问是否针对二级虚拟机的非叶子页表项isForVSnonLeafPTE
1. 发生虚拟机页错误异常返回虚拟机物理地址gpaddr
- 发生 pgf 后,需要返回对应的 gpaddr。
- 只有一个通道发生 pgf 时,返回对应通道的 gpaddr 即可;多个通道发生 pgf 时,返回第一个通道的 gpaddr。
2. 当访问二级虚拟机的非叶子页表项时:
- 发生 gpf 后,如果是访问二级虚拟机的非叶子页表项时,需要返回对应的 gpaddr。
- 只有一个通道发生 pgf 时,返回对应通道的 gpaddr 即可;多个通道发生 pgf 时,返回第一个通道的 gpaddr。
4. 返回基于页面的内存类型 pbmt 信息:
- TLB 有效时,返回 pbmt 信息。
### 接收来自 IMeta缓存元数据的响应并检查缓存命中
从 Meta SRAM 中读取缓存标签和有效位。
将物理地址的标签部分与缓存元数据中的标签比较,确定是否命中。
1. 缓存标签比较和有效位检查:
- 从物理地址中提取物理标签ptag将其与缓存元数据中的标签进行比较检查所有缓存路Way。检查有效位确保只考虑有效的缓存行。
1. 缓存未命中(标签不匹配或有效位为假):
- 当标签不匹配或者标签匹配,但是有效位为假时,表示缓存未命中。
- s1_meta_ptags(PortNumber)(nWays) 不等于 ptags(PortNumber) 或者它们相等,但是对应的 s1_meta_valids 为低时,总之返回的 waymasks 为全 0。
2. 单路缓存命中(标签匹配且有效位为真):
- 当标签匹配,且有效位为真时,表示缓存命中。
- waymasks 对应的位为 1。
### PMP物理内存保护权限检查
对物理地址进行 PMP 权限检查,确保预取操作的合法性。
处理 PMP 返回的异常和 MMIO 信息
1. 访问被允许的内存区域
- itlb 返回的物理地址在 PMP 允许的范围内。
- s1_pmp_exception(i) 为 none。
2. 访问被禁止的内存区域
- s1_req_paddr(i) 对应的地址在 PMP 禁止的范围内。
- s1_pmp_exception(i) 为 af。
3. 访问 MMIO 区域
- itlb 返回的物理地址在 MMIO 区域。
- s1_pmp_mmio 为高。
### 异常处理和合并
backend 优先级最高merge 方法里的异常越靠前优先级越高
合并来自后端、ITLB、PMP 的异常信息,按照优先级确定最终的异常类型。
1. 仅 ITLB 产生异常
- s1_itlb_exception(i) 为非零s1_pmp_exception(i) 为零。
- s1_exception_out(i) 正确包含 ITLB 异常。
2. 仅 PMP 产生异常
- s1_itlb_exception(i) 为零s1_pmp_exception(i) 为非零。
- s1_exception_out(i) 正确包含 PMP 异常。
3. 仅 后端 产生异常
- s1_itlb_exception(i) 为零s1_pmp_exception(i) 为零。
- s1_exception_out(i) 正确包含 后端 异常。
4. ITLB 和 PMP 都产生异常
- s1_itlb_exception(i) 和 s1_pmp_exception(i) 都为非零。
- s1_exception_out(i) 包含 ITLB 异常(优先级更高)。
5. ITLB 和 后端 都产生异常
- s1_itlb_exception(i) 和 s1_backendException(i) 都为非零。
- s1_exception_out(i) 包含 后端 异常(优先级更高)。
6. PMP 和 后端 都产生异常
- s1_pmp_exception(i) 和 s1_backendException(i) 都为非零。
- s1_exception_out(i) 包含 后端 异常(优先级更高)。
7. ITLB、PMP 和 后端 都产生异常
- s1_itlb_exception(i)、s1_pmp_exception(i) 和 s1_backendException(i) 都为非零。
- s1_exception_out(i) 包含 后端 异常(优先级更高)。
8. 无任何异常
- s1_itlb_exception(i)、s1_pmp_exception(i)、s1_backendException(i) 都为零。
- s1_exception_out(i) 指示无异常。
### 发送请求到 WayLookup 模块
当条件满足时,将请求发送到 WayLookup 模块,以进行后续的缓存访问。
1. 正常发送请求到 WayLookup
- toWayLookup.valid 为高toWayLookup.ready 为高s1_isSoftPrefetch 为假。
- 请求成功发送包含正确的地址、标签、waymask 和异常信息。
2. WayLookup 无法接收请求
- toWayLookup.valid 为高toWayLookup.ready 为假。
- 状态机等待 WayLookup 准备好,不会错误地推进。
3. 软件预取请求不发送到 WayLookup
- s1_isSoftPrefetch 为真。
- toWayLookup.valid 为假,不会发送预取请求到 WayLookup。
### 状态机控制和请求处理流程
使用状态机管理 s1 阶段的请求处理流程。
包括处理 ITLB 重发、Meta 重发、进入 WayLookup、等待 s2 准备等状态
1. 初始为 m_idle 状态:
1. 正常流程推进,保持 m_idle 状态
- s1_valid 为高itlb_finish 为真toWayLookup.fire 为真s2_ready 为真。
- 状态机保持在 m_idle 状态s1 阶段顺利推进。
2. ITLB 未完成,需要重发
- s1_valid 为高itlb_finish 为假。
- 状态机进入 m_itlbResend 状态,等待 ITLB 完成。
3. ITLB 完成WayLookup 未命中
- s1_valid 为高itlb_finish 为真toWayLookup.fire 为假。
- 状态机进入 m_enqWay 状态,等待 WayLookup 入队。
2. 初始为 m_itlbResend 状态:
1. ITLB 命中, MetaArray 空闲,需要 WayLookup 入队
- itlb_finish 为假toMeta.ready 为真。
- 状态机进入 m_enqWay 状态,等待 WayLookup 入队。
2. ITLB 命中, MetaArray 繁忙,等待 MetaArray 读请求
- itlb_finish 为假toMeta.ready 为假。
- 状态机进入 m_metaResend 状态MetaArray 读请求
3. 初始为 m_metaResend 状态:
1. MetaArray 空闲 ,需要 WayLookup 入队
- toMeta.ready 为真。
- 状态机进入 m_enqWay 状态,等待 WayLookup 入队。
4. 初始为 m_enqWay 状态:
1. WayLookup 入队完成或者为软件预取, S2 空闲, 重新进入空闲状态
- toWayLookup.fire 或 s1_isSoftPrefetch 为真s2_ready 为假。
- 状态机进入空闲状态 m_idle。
2. WayLookup 入队完成或者为软件预取, S2 繁忙,需要 enterS2 状态
- toWayLookup.fire 或 s1_isSoftPrefetch 为真s2_ready 为真。
- 状态机进入 m_enterS2 状态,等待 s2 阶段准备好。
5. 初始为 m_enterS2 状态:
1. s2 阶段准备好,请求进入下流水级,流入后进入 m_idle 状态
- s2_ready 为真。
- 状态机进入空闲状态 m_idle。
### 监控 missUnit 的请求
检查 missUnit 的响应,更新缓存的命中状态和 MSHR 的匹配状态。
1. 请求与 MSHR 匹配且有效:
- s2_req_vSetIdx 和 s2_req_ptags 与 fromMSHR 中的数据匹配,且 fromMSHR.valid 为高fromMSHR.bits.corrupt 为假。
- s2_MSHR_match(PortNumber) 为真, s2_MSHR_hits(PortNumber) 应保持为真
2. 请求在 SRAM 中命中:
- s2_waymasks(PortNumber) 中有一位为高,表示在缓存中命中。
- s2_SRAM_hits(PortNumber) 为真,s2_hits(PortNumber) 应为真。
3. 请求未命中 MSHR 和 SRAM
- 请求未匹配 MSHR且 s2_waymasks(PortNumber) 为空。
- s2_MSHR_hits(PortNumber)、s2_SRAM_hits(PortNumber) 均为假, s2_hits(PortNumber) 为假。
### 发送请求到 missUnit
对于未命中的预取请求,向 missUnit 发送请求,以获取缺失的数据。
1. 确定需要发送给 missUnit 的请求
- 根据命中状态、异常信息、MMIO 信息等,确定哪些请求需要发送到 missUnit即 s2_miss
1. 请求未命中且无异常,需要发送到 missUnit
- s2_hits(PortNumber) 为假(未命中缓存)s2_exception 无异常s2_mmio 为假(不是 MMIO 或不可缓存的内存)。
- s2_miss(PortNumber) 为真,表示需要发送请求到 missUnit。
2. 请求命中或有异常,不需要发送到 missUnit
- s2_hits(i) 为真(已命中)或者 s2_exception 有异常 或者 s2_mmio 为真MMIO 访问)。
- s2_miss(i) 为假,不会发送请求到 missUnit。
3. 双行预取时,处理第二个请求的条件:
- s2_doubleline 为真,处理第二个请求。
- 如果第一个请求有异常或 MMIOs2_miss(1) 应为假,后续请求被取消或处理。
2. 避免发送重复请求,发送请求到 missUnit
- 使用寄存器 has_send 记录每个端口是否已发送请求,避免重复发送。
- 将需要发送的请求通过仲裁器 toMSHRArbiter 发送到 missUnit。
1. 在 s1_real_fire 时,复位 has_send
- s1_real_fire 为高。
- has_send(PortNumber) 应被复位为假,表示新的请求周期开始。
2. 当请求成功发送时,更新 has_send
- toMSHRArbiter.io.in(PortNumber).fire 为高(请求已发送)。
- has_send(PortNumber) 被设置为真,表示该端口已发送请求。
3. 避免重复发送请求:
- 同一请求周期内has_send(PortNumber) 为真s2_miss(PortNumber) 为真。
- toMSHRArbiter.io.in(PortNumber).valid 为假,不会再次发送请求。
4. 正确发送需要的请求到 missUnit
- s2_valid 为高s2_miss(i) 为真has_send(i) 为假。
- toMSHRArbiter.io.in(i).valid 为高,请求被成功发送。
5. 仲裁器正确仲裁多个请求:
- 多个端口同时需要发送请求。
- 仲裁器按照优先级或设计要求选择请求发送到 missUnit,未被选中的请求在下个周期继续尝试发送。
### 刷新机制
- io.flush: 全局刷新信号,当该信号为高时,所有请求都需要刷新。
- from_bpu_s0_flush当请求不是软件预取!s0_isSoftPrefetch, 软件预取请求是由特定的指令触发的,与指令流中的分支预测无关。因此,在处理刷新信号时,对于软件预取请求,通常不受来自 BPU 的刷新信号影响。),且 BPU 指示需要在 Stage 2 或 Stage 3 刷新的请求,由于该请求尚未进入 s1 阶段,因此在 s0 阶段也需要刷新。
- s0_flush综合考虑全局刷新信号、来自 BPU 的刷新信号,以及 s1 阶段的刷新信号
- from_bpu_s1_flush当 s1 阶段的请求有效且不是软件预取,且 BPU 指示在 Stage 3 需要刷新,则在 s1 阶段需要刷新。
- io.itlbFlushPipe当 s1 阶段需要刷新时,该信号用于通知 ITLB 刷新其流水线,以保持一致性。
- s1_flush综合考虑全局刷新信号和来自 BPU 的刷新信号。
- s2_flush用于控制 s2 阶段是否需要刷新。
1. 发生全局刷新
- io.flush 为高。
- s0_flush、s1_flush、s2_flush 分别为高,所有阶段的请求被正确清除。
2. 来自 BPU 的刷新
- io.flushFromBpu.shouldFlushByStageX 为真X 为 2 或 3且请求不是软件预取。
- 对应阶段的 from_bpu_sX_flush 为高sX_flush 为高,阶段请求被刷新。
3. 刷新时状态机复位
- s1_flush 为高。
- 状态机 state 被重置为 m_idle 状态。
4. ITLB 管道同步刷新
- s1_flush 为高。
- io.itlbFlushPipe 为高ITLB 被同步刷新。

View File

@ -1,362 +0,0 @@
---
title: MainPipe
linkTitle: MainPipe
weight: 12
---
<div class="icache-ctx">
</div>
## MainPipe
MainPipe 为 ICache 的主流水,为三级流水设计,负责从 DataArray 中读取数据pmp 检查,缺失处理,并且将结果返回给 IFU。
<div>
<center>
<img src="../mainpipe_structure.png"
alt="MainPipe结构示意图"
style="zoom:100%"/>
<br>
MainPipe结构示意图
</center>
</div>
<br>
1. 从 WayLookup 获取信息,访问 DataArray 单路S0 阶段)
在 S0 流水级,从 WayLookup 获取元数据,包括路命中信息和 ITLB 查询结果,访问 DataArray 的单路,如果 DataArray 正在被写或 WayLookup 中没有有效表项流水线就会阻塞。每次重定向后FTQ 中同一个请求被同时发送到 MainPipe 和 IPrefetchPipe 中MainPipe 始终需要等待 IPrefetchPipe 将请求的查询信息写入 WayLookup 后才能向下走,导致了 1 拍重定向延迟,当预取超过取指时,该延迟就会被覆盖。
- 接收并解析来自 FTQ 的取指请求,提取必要的请求信息,如虚拟地址、缓存组索引、块内偏移、是否为双行读、后端的异常信息。
- 从 WayLookup 模块获取缓存命中信息和 TLB 信息,包括 waymask、物理标签、虚拟机物理地址、是否为叶节点、 ITLB 异常、ITLB 的 PBMT 信息、缓存元数据的校验码。
- 访问 DataArray 的单路,如果 DataArray 正在被写或 WayLookup 中没有有效表项,流水线就会阻塞。
- 每次重定向后FTQ 中同一个请求被同时发送到 MainPipe 和 IPrefetchPipe 中MainPipe 始终需要等待 IPrefetchPipe 将请求的查询信息写入 WayLookup 后才能向下走,导致了 1 拍重定向延迟,当预取超过取指时,该延迟就会被覆盖。
2. 接收上一个阶段的信息并进行数据暂存、PMP 检查、从 DataArray 获取读响应异常合并、替换策略更新以及监控 MissUnitS1 阶段)
- 寄存并延迟 S0 阶段信息
- 从 S0 获取的地址、tag、命中方式waymask、TLB 异常标志、下一拍要用的数据等,都会在 S1 寄存一拍,保证在流水线停顿时也能维持正确值。
- Meta ECC 检查
- 对 S0 读出的 meta 和其校验码ECC/Parity进行比对判断是否发生错误。如果关闭 parity 功能,则跳过该检查。
- 更新 replacer
- 对确定命中的访问请求进行“touch”更新标记最近使用过的 way以便后续替换算法正确运行。
- PMP 检查
- 根据 S0 得到的物理地址paddr在 S1 对其进行 PMP 检查,判断是否拥有执行权限、是否为 MMIO 等。在当拍收到响应,将结果寄存到下一流水级进行处理。
- 需要指出IPrefetchPipe s1 流水级也会进行 PMP 检查,和此处的检查实际上是完全一样的,分别检查只是为了优化时序(避免 `ITLB(reg) -> ITLB.resp -> PMP.req -> PMP.resp -> WayLookup.write -> bypass -> WayLookup.read -> MainPipe s1(reg)` 的超长组合逻辑路径)。
- 异常合并
- 将 ITLB 与 PMP 异常进行优先级合并,产生最终的异常标记。
- 选择数据来源MSHR 或 SRAM
- 接收 DataArray 返回的 data 和 code 并寄存,同时监听 MSHR 的响应,当 DataArray 和 MSHR 的响应同时有效时,后者的优先级更高。当 MSHR 已在填充一些数据,如果当前请求与 MSHR 命中,可以在 S1 阶段直接选用 MSHR 的数据,而不必依赖 SRAM 读出的结果。
3. 监控 MissUnit在 ECC 校验、异常处理和缺失处理之后,将最终的数据、异常信息传递给 IFU完成取指流程S2 阶段)
- ECC 校验
- DataArray ECC 校验,对 S1 流水级寄存的 code 进行校验,生成 data 是否损坏信号 s2_data_corrupt。如果校验出错就将错误报告给 BEU。
- MetaArray ECC 校验IPrefetchPipe 读出 MetaArray 的数据后会直接进行校验,并将校验结果随命中信息一起入队 WayLookup 并随 MainPipe 流水到达 S2 级meta_corrupt 信号),在此处随 DataArray 的 ECC 校验结果一起报告给 BEU。
- 监控 MissUnit 响应端口
- 检查当前 S2 阶段的请求是否与 MSHR 中的条目匹配,命中时寄存 MSHR 响应的数据,为了时序在下一拍才将数据发送到 IFU。
- 更新 Data 和其是否来自 MSHR 的信息。
- 更新 s2_hits 和处理异常。
- 处理 L2 Cache 的 Corrupt 标志。
- 缺失处理,发送 Miss 请求到 MSHR
- 计算是否需要重新获取Refetch
- 通过是否命中、ECC 错误、正确跨行、是否异常和是否属于 MMIO 区域来发送 Miss 请求。
- 设置 Arbiter 合并多个端口的 Miss 请求,确保一次只处理一个请求,同时有避免重复请求的设置。
- 判断 Fetch 是否完成。
- 生成 L2 Cache 的异常标记,再将当前 S2 阶段的异常(包括 ITLB、PMP与 L2 Cache 的异常进行合并。
- 响应 IFU
- 将最终的数据、异常信息传递给 IFU完成取指流程。
- 根据请求是否为跨行,决定如何处理双行数据。
- 报告 TileLink 的 Corrupt 错误
- 对于每个端口,如果在当前周期 s2_fire 时检测到来自 L2 Cache 的数据 corrupt 错误,就将错误报告给 BEU。
## MainPipe 的功能点和测试点
### 访问 DataArray 的单路
根据从 WayLookup 获取信息,包括路命中信息和 ITLB 查询结果还有 DataArray 当前的情况,决定是否需要从 DataArray 中读取数据。
1. 访问 DataArray 的单路
- 当 WayLookup 中的信息表明路命中时ITLB 查询成功,并且 DataArray 当前没有写时MainPipe 会向 DataArray 发送读取请求,以获取数据。
- s0_hits 为高一路命中s0_itlb_exception 信号为零ITLB 查询成功toData.last.ready 为高DataArray 没有正在进行的写操作)。
- toData.valid 信号为高,表示 MainPipe 向 DataArray 发出了读取请求。
2. 不访问 DataArrayWay 未命中) ==会访问,但是返回数据无效==
- 当 WayLookup 中的信息表明路未命中时MainPipe 不会向 DataArray 发送读取请求。
- s0_hits 为低表示缓存未命中
- toData.valid 信号为低,表示 MainPipe 未向 DataArray 发出读取请求。
3. 不访问 DataArrayITLB 查询失败)==会访问,但是返回数据无效==
- 当 ITLB 查询失败时MainPipe 不会向 DataArray 发送读取请求。
- s0_itlb_exception 信号不为零ITLB 查询失败)。
- toData.valid 信号为低,表示 MainPipe 未向 DataArray 发出读取请求。
4. 不访问 DataArrayDataArray 正在进行写操作)
- 当 DataArray 正在进行写操作时MainPipe 不会向 DataArray 发送读取请求。
- toData.last.ready 信号为低,表示 DataArray 正在进行写操作。
- toData.valid 信号为低,表示 MainPipe 未向 DataArray 发出读取请求。
### Meta ECC 校验
将物理地址的标签部分与对应的 Meta 进行 ECC 校验,以确保 Meta 的完整性。
1. 无 ECC 错误
- 当 waymask 全为 0没有命中则 hit_num 为 0 或 waymask 有一位为 1一路命中hit_num 为 1 且 ECC 对比通过encodeMetaECC(meta) == code
- s1_meta_corrupt 为假。
2. 单路命中的 ECC 错误
- 当 waymask 有一位为 1一路命中ECC 对比失败encodeMetaECC(meta) != code
- s1_data_corrupt(i) io.errors(i).valid io.errors(i).bits.report_to_beu io.errors(i).bits.source.data 为 true。
3. 多路命中
> hit multi-way, must be an ECC failure
- 当 waymask 有两位及以上为 1多路命中视为 ECC 错误。
- s1_data_corrupt(i) io.errors(i).valid io.errors(i).bits.report_to_beu io.errors(i).bits.source.data 为 true。
4. ECC 功能关闭
- 当奇偶校验关闭时ecc_enable 为低),强制清除 s1_meta_corrupt 信号置位。
- 不管是否发生 ECC 错误s1_meta_corrupt 都为假。
### PMP 检查
- 将 S1 的物理地址 s1_req_paddr(i) 和指令 TlbCmd.exec 发往 PMP判断取指是否合法。
- 防止非法地址,区分普通内存和 MMIO 内存。
1. 没有异常
- s1_pmp_exception 为全零,表示没有 PMP 异常。
2. 通道 0 有 PMP 异常
- s1_pmp_exception(0) 为真,表示通道 0 有 PMP 异常。
3. 通道 1 有 PMP 异常
- s1_pmp_exception(1) 为真,表示通道 1 有 PMP 异常。
4. 通道 0 和通道 1 都有 PMP 异常
- s1_pmp_exception(0) 和 s1_pmp_exception(1) 都为真,表示通道 0 和通道 1 都有 PMP 异常。
5. 没有映射到 MMIO 区域
- s1_pmp_mmio0 和 s1_pmp_mergemmio1 都为假,表示没有映射到 MMIO 区域。
6. 通道 0 映射到了 MMIO 区域
- s1_pmp_mmio0 为真,表示映射到了 MMIO 区域。
7. 通道 1 映射到了 MMIO 区域
- s1_pmp_mmio1 为真,表示映射到了 MMIO 区域。
8. 通道 0 和通道 1 都映射到了 MMIO 区域
- s1_pmp_mmio0 和 s1_pmp_mmio1 都为真,表示通道 0 和通道 1 都映射到了 MMIO 区域。
### 异常合并
- 将 s1_itlbmergeption 与 s1_pmp_exception 合并生成 s1_exception_out。
- ITLB 异常通常优先于 PMP 异常。merge
1. 没有异常
- s1_exception_out 为全零,表示没有异常。
2. 只有 ITLB 异常
- s1_exception_out 和 s1_itlb_exception 一致
3. 只有 PMP 异常
- s1_exception_out 和 s1_pmp_exception 一致
4. ITLB 与 PMP 异常同时出现
> itlb has the highest priority, pmp next
- s1_exception_out 和 s1_itlb_exception 一致
### MSHR 匹配和数据选择
- 检查当前的请求是否与 MSHR 中正在处理的缺失请求匹配。
- 判断 缓存组索引相同(s1_req_vSetIdx(i) == fromMSHR.bits.vSetIdx) ,物理标签相同 (s1_req_ptags(i) == fromMSHR.bits.blkPaddr);若匹配 MSHR 有效且没有错误fromMSHR.valid && !fromMSHR.bits.corrupt则优先使用 MSHR 中的数据
- 避免重复访问 Data SRAM提升性能当 MSHR 中已有重填结果时,可立即命中。
1. 命中 MSHR
- MSHR 中已有正确数据时S1 阶段能直接拿到
- s1_MSHR_hits(i) 为 true 时s1_datas(i) 为 s1_bankMSHRHit(i)s1_data_is_from_MSHR(i) 为 true
2. 未命中 MSHR
- MSHR 中存放的地址与当前请求不同,那么应该读取 SRAM 的数据
- s1_MSHR_hits(i) 为 true 时s1_datas(i) 为 fromData.datas(i)s1_data_is_from_MSHR(i) 为 false
3. MSHR 数据 corrupt
- fromMSHR.bits.corrupt = true那么 MSHR 将不匹配,应该读取 SRAM 的数据
- s1_datas(i) 为 fromData.datas(i)s1_data_is_from_MSHR(i) 为 false
### Data ECC 校验
在 S2 阶段,对从 S1 或 MSHR 获得的数据(如 s2_datas进行 ECC 校验:
- 若 ECC 校验失败,则标记 s2_data_corrupt(i) = true。
- 若数据来自 MSHR则不重复进行 ECC 校验(或忽略 corrupt
1. 无 ECC 错误
- s2_bank 全部没有损坏bank 也选对了对应的端口和 bank数据不来自 MSHR
- s2_data_corrupt(i) 为 false没有 ECC 错误。
2. 单 Bank ECC 错误
- s2_bank_corrupt(bank) 有一个为 true ,即对应的 bank 有损坏;同时 bank 也选对了对应的端口和 bank数据不来自 MSHR
- s2_data_corrupt(i) io.errors(i).valid io.errors(i).bits.report_to_beu io.errors(i).bits.source.data 为 true。
3. 多 Bank ECC 错误
- s2_bank_corrupt(bank) 有两个或以上为 true,即对应的 bank 有损坏;同时 bank 也选对了对应的端口和 bank数据不来自 MSHR
- s2_data_corrupt(i) io.errors(i).valid io.errors(i).bits.report_to_beu io.errors(i).bits.source.data 为 true。
4. ECC 功能关闭
- 当奇偶校验关闭时ecc_enable 为低),强制清除 s2_data_corrupt 信号置位。
- 不管是否发生 ECC 错误s2_data_corrupt 都为假。
### 冲刷 MetaArray
Meta 或者 Data ECC 校验错误时,会冲刷 MetaArray为重取做准备。
1. 只有 Meta ECC 校验错误
> if is meta corrupt, clear all way (since waymask may be unreliable)
- 当 s1_meta_corrupt 为真时MetaArray 的所有路都会被冲刷。
- toMetaFlush(i).valid 为真toMetaFlush(i).bits.waymask 对应端口的所有路置位。
2. 只有 Data ECC 校验错误
> if is data corrupt, only clear the way that has error
- 当 s2_data_corrupt 为真时,只有对应路会被冲刷。
- toMetaFlush(i).valid 为真toMetaFlush(i).bits.waymask 对应端口的对应路置位。
3. 同时有 Meta ECC 校验错误和 Data ECC 校验错误
- 处理 Meta ECC 的优先级更高, 将 MetaArray 的所有路冲刷。
- toMetaFlush(i).valid 为真toMetaFlush(i).bits.waymask 对应端口的所有路置位。
### 监控 MSHR 匹配与数据更新
- 判断是否命中 MSHR
- 根据 MSHR 是否命中和 s1 阶段是否发射来更新 s2 的数据s2 的命中状态和 l2 是否损坏
1. MSHR 命中(匹配且本阶段有效)
- MSHR 的 vSetIdx / blkPaddr 与 S2 请求一致, fromMSHR.valid 有效s2_valid 也有效
- s2_MSHR_matchs2_MSHR_hits 为高s2_bankMSHRHit 对应 bank 为高
- s1_fire 无效时s2_datas 更新为 MSHR 的数据,将 s2_data_is_from_MSHR 对应位置位s2_hits 置位,清除 s2_data_corruptl2 的 corrupt 更新为 fromMSHR.bits.corrupt
- s1_fire 有效时s2_datas 为 s1_datas 的数据,将 s2_data_is_from_MSHR 对应位置为 s1 的 s1_data_is_from_MSHRs2_hits 置为 s1_hits清除 s2_data_corruptl2 的 corrupt 为 false
2. MSHR 未命中
- MSHR 的 vSetIdx / blkPaddr 与 S2 请求一致, fromMSHR.valid 有效s2_valid 也有效,至少有一个未达成
- s2_MSHR_hits(i) = falseS2 不会更新 s2_datas继续保持原先 SRAM 数据或进入 Miss 流程。
### Miss 请求发送逻辑和合并异常
- 通过计算 s2_should_fetch(i) 判断是否需要向 MSHR 发送 Miss 请求:
- 当出现未命中 (!s2_hits(i)) 或 ECC 错误(s2_meta_corrupt(i) || s2_data_corrupt(i)) 时,需要请求重新获取。
- 若端口存在异常或处于 MMIO 区域,则不发送 Miss 请求。
- 使用 Arbiter 将多个端口的请求合并后发送至 MSHR。
- 通过 s2_has_send(i) 避免重复请求。
- 将 S2 阶段已有的 ITLB/PMP 异常s2_exception与 L2 Cache 报告的 s2_l2_corrupt(i)(封装后为 s2_l2_exception(i))进行合并。
1. 未发生 Miss
- 当 s2_hits(i) 为高s2 已经命中s2 的 meta 和 data 都没有错误s2 异常,处于 mmio 区域
- 以上条件至少满足一个时s2_should_fetch(i) 为低,表示不发送 Miss 请求。
2. 单口 Miss
- 当出现未命中 (!s2_hits(i)) 或 ECC 错误(s2_meta_corrupt(i) || s2_data_corrupt(i)),端口不存在异常且未处于 MMIO 区域时,会向 MSHR 发送 Miss 请求。
- toMSHRArbiter.io.in(i).valid = true Arbiter 只发送一条 Miss 请求。
3. 双口都需要 Miss
- 同上,但是两个端口都满足 s2_should_fetch 为高的条件。
- toMSHRArbiter.io.in(0).valid、toMSHRArbiter.io.in(1).valid 均为 trueArbiter 根据仲裁顺序依次发出请求。
4. 重复请求屏蔽
- 当 s1_fire 为高,表示可以进入 s2 阶段,那么 s2 还没有发送 s2_has_send(i) := false.B
- 如果已经有请求发送了,那么对应的 toMSHRArbiter.io.in(i).fire 为高表示对应的请求可以发送s2_has_send(i) := true。
- 此时再次发送toMSHRArbiter.io.in(i).valid 为低,表示发送失败。
5. 仅 ITLB/PMP 异常
- S1 阶段已记录了 ITLB 或 PMP 异常L2 corrupt = false。
- 2_exception_out 仅保留 ITLB/PMP 异常标记,无新增 AF 异常。
6. 仅 L2 异常
- S2 阶段 s2_l2_corrupt(i) = true且无 ITLB/PMP 异常。
- s2_exception_out(i) 表示 L2 访问错误(AF)。
7. ITLB + L2 同时出现
- 同时触发 ITLB 异常和 L2 corrupt。
- s2_exception_out 优先保留 ITLB 异常类型,不被 L2 覆盖。
8. s2 阶段取指完成
- s2_should_fetch 的所有端口都为低,表示需要取指,那么取指完成
- s2_fetch_finish 为高
### 响应 IFU
- 若当前周期 S2 成功发射s2_fire = true且数据获取完毕s2_fetch_finish则把数据、异常信息、物理地址等打包到 toIFU.bits 输出。
- 若为双行请求s2_doubleline = true也会向 IFU 发送第二路的信息(地址、异常)。
1. 正常命中并返回
- 不存在任何异常或 Misss2 命中s2 阶段取指完成,外部的 respStall 停止信号也为低 。
- toIFU.valid = truetoIFU.bits.data 为正确的 Cacheline 数据toIFU.bits.exception、pmp_mmio、itlb_pbmt = none。
2. 异常返回
- 设置 ITLB、PMP、或 L2 corrupt 异常。
- toIFU.bits.exception(i) = 对应异常类型pmp_mmio、itlb_pbmt 根据是否有对应的异常设置为 true。
3. 跨行取指
- s2_doubleline = true同时检查第一路、第二路返回情况。
- toIFU.bits.doubleline = true。
- 若第二路正常toIFU.bits.exception(1) = none若第二路异常则 exception(1) 标记相应类型。
- pmp_mmio、itlb_pbmt 类似。
4. RespStall
- 外部 io.respStall = true导致 S2 阶段无法发射到 IFU。
- s2_fire = falsetoIFU.valid 也不拉高S2 保持原状态等待下一拍(或直到 respStall 解除)。
### L2 Corrupt 报告
- 当检测到 L2 Cache 返回的 corrupt 标记时s2_l2_corrupt(i) = true在 S2 完成发射后额外向外部错误接口 io.errors(i) 报告。
- 与 Data ECC 或 Meta ECC 不同L2 corrupt 由 L2 自己报告给 BEU这里不需要再次报告给 beu。
1. L2 Corrupt 单路
- s2 阶段准备完成可以发射s2_fire 为高s2_MSHR_hits(0)和 fromMSHR.bits.corrupt 为高
- s2_l2_corrupt(0) = trueio.errors(0).valid = trueio.errors(0).bits.source.l2 = true。
2. 双路同时 corrupt
- 端口 0 和端口 1 都从 L2 corrupt 数据中获取。
- s2_l2_corrupt 均为 true发射后分别报告到 io.errors(0) 和 io.errors(1)。
### 刷新机制
- io.flush外部的全局刷新信号它用于指示整个流水线需要被冲刷清空
- s0_flush S0 阶段内部的刷新信号,它由 io.flush 传递而来,用于控制 S0 阶段的刷新操作。
- s1_flush S1 阶段内部的刷新信号,它由 io.flush 传递而来,用于控制 S1 阶段的刷新操作。
- s2_flush S2 阶段内部的刷新信号,它由 io.flush 传递而来,用于控制 S2 阶段的刷新操作。
1. 全局刷新
- io.flush 被激活时流水线的各个阶段S0, S1 和 S2都能正确响应并执行刷新操作。
- io.flush = true。
- s0_flush, s1_flush, s2_flush = true。
2. S0 阶段刷新
- s0_flush = true。
- s0_fire = false。
3. S1 阶段刷新
- s1_flush = true。
- s1_valid s1_fire = false。
4. S2 阶段刷新
- s2_flush = true。
- s2_valid toMSHRArbiter.io.in(i).valid s2_fire = false

View File

@ -1,202 +0,0 @@
---
title: WayLookup
linkTitle: WayLookup
weight: 12
---
<div class="icache-ctx">
</div>
## WayLookup
<div>
<center>
<img src="../waylookup_structure_rw.png"
alt="WayLookup 读写结构"
style="zoom:100%"/>
<br>
WayLookup 读写结构
</center>
</div>
<br>
<div>
<center>
<img src="../waylookup_structure_update.png"
alt="WayLookup 更新结构"
style="zoom:100%"/>
<br>
WayLookup 更新结构
</center>
</div>
<br>
- 内部是 FIFO 环形队列结构。暂存 IPrefetchPipe 查询 MetaArray 和 ITLB 得到的元数据,以备 MainPipe 使用。同时监听 MSHR 写入 SRAM 的 cacheline对命中信息进行更新。
- 通过 readPtr 和 writePtr 来管理读写位置。当有 flush 信号时读写指针都会被重置。当写入数据时写指针递增读取时读指针递增。需要处理队列的空和满的情况empty 是读指针等于写指针,而 full 则是两者的值相同且标志位不同。
- 处理 GPF 的部分,有一个 gpf_entry 寄存器,存储 GPF 的相关信息。当写入的数据包含 GPF 异常时,需要将信息存入 gpf_entry并记录当前的写指针位置到 gpfPtr。当读取的时候如果当前读指针的位置与 gpfPtr 匹配,并且 gpf_entry 有效,那么就将 GPF 信息一并输出。
- IPrefetchPipe 向其写入 WayLookupInfo 信息(包括 vSetIdxwaymaskptagitlb_exceptionitlb_pbmtmeta_codesgpaddrisForVSnonLeafPTE
- 写入前,需要考虑队列是否已满,以及是否有 GPF 阻塞。如果有 GPF 信息待读取且未被处理,则写入需要等待,防止覆盖 GPF 信息。写入时,如果数据中包含 GPF 异常,就将信息存入 gpf_entry并更新 gpfPtr。
- MainPipe 从其读出 WayLookupInfo 信息。
- 在读取上有两种情况当队列为空但有写请求时可以直接将写的数据旁路bypass给读端口否则就从 entries 数组中读取对应读指针的数据。同时,如果当前读的位置存在 GPF 信息,就将 GPF 信息一起输出,并在读取后清除有效位。
- 允许 bypass当队列为空但有写请求时可以直接将写的数据旁路给读端口为了不将更新逻辑的延迟引入到 DataArray 的访问路径上,在 MSHR 有新的写入时禁止出队MainPipe 的 S0 流水级也需要访问 DataArray当 MSHR 有新的写入时无法向下走,所以该措施并不会带来额外影响。
- MissUnit 向其写入命中信息。
- 若是命中则将 waymask 更新 ICacheMissResp 信息(包括 blkPaddrvSetIdxwaymaskdatacorrupt且 meta_codes 也更新,否则 waymask 清零。更新逻辑与 IPrefetchPipe 中相同,见 [IPrefetchPipe 子模块文档中的“命中信息的更新”](./01_iprefetchpipe.md#命中信息的更新)一节。
### GPaddr 省面积机制
由于 `gpaddr` 仅在 guest page fault 发生时有用,并且每次发生 gpf 后前端实际上工作在错误路径上,后端保证会送一个 redirectWayLookup flush到前端无论是发生 gpf 前就已经预测错误/发生异常中断导致的;还是 gpf 本身导致的),因此在 WayLookup 中只需存储 reset/flush 后第一个 gpf 有效时的 gpaddr。对双行请求只需存储第一个有 gpf 的行的 `gpaddr。`
在实现上,把 gpf 相关信号(目前只有 `gpaddr`)与其它信号(`paddr`etc.)拆成两个 bundle其它信号实例化 nWayLookupSize 个gpf 相关只实例化一个寄存器。同时另用一个 `gpfPtr` 指针。总计可以节省$(\text{nWayLookupSize}\times2-1)\times \text{GPAddrBits} - \log_2{(\text{nWayLookupSize})} - 1$bit 的寄存器。
当 prefetch 向 WayLookup 写入时,若有 gpf 发生,且 WayLookup 中没有已经存在的 gpf则将 gpf/gpaddr 写入 `gpf_entry` 寄存器,同时将 `gpfPtr` 设置为此时的 `writePtr。`
当 MainPipe 从 WayLookup 读取时,若 bypass则仍然直接将 prefetch 入队的数据出队;否则,若 `readPtr === gpfPtr`,则读出 gpf_entry否则读出全 0。
需要指出:
1. 考虑双行请求,`gpaddr` 只需要存一份(若第一行发生 gpf则第二行肯定也在错误路径上不必存储但 gpf 信号本身仍然需要存两份,因为 ifu 需要判断是否是跨行异常。
2. `readPtr===gpfPtr` 这一条件可能导致 flush 来的比较慢时 `readPtr` 转了一圈再次与 `gpfPtr` 相等,从而错误地再次读出 gpf但如前所述此时工作在错误路径上因此即使再次读出 gpf 也无所谓。
3. 需要注意一个特殊情况:一个跨页的取指块,其 32B 在前一页且无异常,后 2B 在后一页且发生 gpf若前 32B 正好是 16 条 RVC 压缩指令,则 IFU 会将后 2B 及对应的异常信息丢弃,此时可能导致下一个取指块的 `gpaddr` 丢失。需要在 WayLookup 中已有一个未被 MainPipe 取走的 gpf 及相关信息时阻塞 WayLookup 的入队(即 IPrefetchPipe s1 流水级),见 PR#3719。
## WayLookup 的功能点和测试点
### 刷新操作
- 接收到全局刷新刷新信号 io.flush 后,读、写指针和 GPF 信息都被重置。
1. 刷新读指针
- io.flush 为高时,重置读指针。
- readPtr.value 为 0 readPtr.flag 为 false。
2. 刷新写指针
- io.flush 为高时,重置写指针。
- writePtr.value 为 0 writePtr.flag 为 false。
3. 刷新 GPF 信息
- io.flush 为高时,重置 GPF 信息。
- gpf_entry.valid 为 0 gpf_entry.bits 为 0。
### 读写指针更新
- 读写信号握手完毕之后io.read.fire/io.write.fire 为高),对应指针加一。
- 因为是在环形队列上,所以超过队列大小后,指针会回到队列头部。
1. 读指针更新
- 当 io.read.fire 为高时,读指针加一。
- readPtr.value 加一。
- 如果 readPtr.value 超过环形队列的大小readPtr.flag 会翻转。
2. 写指针更新
- 当 io.write.fire 为高时,写指针加一。
- writePtr.value 加一。
- 如果 writePtr.value 超过环形队列的大小writePtr.flag 会翻转。
### 更新操作
- MissUnit 处理完 Cache miss 后,向 WayLookup 写入命中信息,也就是 update 操作。
- 情况分为两种:
- 命中:更新 waymask 和 meta_codes。
- 未命中:重置 waymask。
1. 命中更新
- MissUnit 返回的更新信息和 WayLookup 的信息相同时,更新 waymask 和 meta_codes。
- vset_same 和 ptag_same 为真。
- waymask 和 meta_codes 更新。
- hits 对应位为高。
2. 未命中更新
- vset_same 和 way_same 为真。
- waymask 清零。
- hit 对应位为高。
3. 不更新
- 其他情况下不更新。
- vset_same 为假或者 ptag_same 和 way_same 都为假。
- hits 对应位为低。
### 读操作
- 读操作会根据读指针从环形队列中读取信息。
- 如果达成了绕过条件,优先绕过。
1. Bypass 读
- 队列为空,并且 io.write.valid 写有效时,可以直接读取,而不经过队列。
- empty 和 io.write.valid 都为真。
- io.read.bits = io.write.bits
2. 读信号无效
- 队列为空readPtr === writePtr且写信号 io.write.valid 为低。
- io.read.valid 为低,读信号无效。
3. 正常读
- 未达成绕过条件empty 和 io.write.valid 至少有一个为假)且 io.read.valid 为高。
- 从环形队列中读取信息。
- io.read.bits.entry = entries(readPtr.value)
4. gpf 命中
- io.read.valid 为高,可以读。
- 当 gpf_hits 为高时,从 GPF 队列中读取信息。
- io.read.bits.gpf = gpf_entry.bits
5. gpf 命中且被读取
- io.read.valid 为高,可以读。
> also clear gpf_entry.valid when it's read
- 当 gpf 命中且被读取其时io.read.fire 为高gpf_entry.valid 会被置为 0。
6. gpf 未命中
- io.read.valid 为高,可以读。
- io.read.bits.gpf 清零。
### 写操作
- 写操作会根据写指针从环形队列中读取信息。
- 如果有 gpf 停止,就会停止写。
1. gpf 停止
> if there is a valid gpf to be read, we should stall write
- gpf 队列数据有效,并且没有被读取或者没有命中,就会产生 gpf 停止,此时写操作会被停止。
- gpf_entry.valid && !(io.read.fire && gpf_hit) 为高时写操作会被停止io.write.ready 为低)。
2. 写就绪无效
- 当队列为满((readPtr.value === writePtr.value) && (readPtr.flag ^ writePtr.flag))或者 gpf 停止时,写操作会被停止。
- io.write.ready 为低)
3. 正常写
- 当 io.write.valid 为高时(没满且没有 gpf 停止),写操作会被执行。
- 正常握手完毕 io.write.fire 为高。
- 写信息会被写入环形队列。
- entries(writePtr.value) = io.write.bits.entry。
有 ITLB 异常的写
- 前面与正常写相同,只不过当写信息中存在 ITLB 异常时,会更新 gpf 队列和 gpf 指针。
- 此时如果已经被绕过直接读取了,那么就不需要存储它了。
- 4. 被绕过直接读取了
- can_bypass 和 io.read.fire 都为高。
- gpf_entry.valid 为 false。
- gpf_entry.bits = io.write.bits.gpf
- gpfPtr = writePtr
- 5. 没有被绕过直接读取
- can_bypass 为低。
- gpf_entry.valid 为 true。
- gpf_entry.bits = io.write.bits.gpf
- gpfPtr = writePtr

View File

@ -1,268 +0,0 @@
---
title: MissUnit
linkTitle: MissUnit
weight: 12
---
<div class="icache-ctx">
</div>
## 子模块FIFO
- 一个先入先出的循环队列,目前仅在 MissUnit 中有使用,作为优先队列 priorityFIFO。
- 按照在 MissUnit 中的实例化pipe 是默认值 falsehasflush 是 true。
- 队列的指针都是环形的分为入队指针写指针ent_ptr和出队指针读指针deq_ptr记录读和写的位置。
- 两个指针都有对应的 flag 位当指针超过队列大小时flag 位会翻转,用以判断是否已经循环。
- 在入队、出队对应的 firevalid && ready 信号有效时,移动对应的指针。
## FIFO 的功能点和测试点
### 入队操作
1. 队未满,正常入队
- 当队列未满,且空位不小于一时,可以正常入队,如果从零号位开始入队到最大容量,入队指针的 flag 不会翻转。
- io.enq.fire 为高有效regFiles(enq_ptr.value) = io.enq.bitsenq_ptr.value+1 入队指针移动,入队指针标记位不翻转。
- 重复以上操作至队满。
2. 队未满,入队后标记位翻转
- 当队未满,但是空位却是靠近队尾时,入队一位后就到达了队头,入队指针的 flag 会翻转。
- 队列的容量为 10入队指针指向 9队未满。此时如果 io.enq.fire 为高,则 regFiles(9) = io.enq.bitsenq_ptr.value+1循环队列加完后 enq_ptr.value=0入队指针移动入队指针标记位翻转。
3. 队满,入队就绪信号为低,无法入队
- 当队满时,(enq_ptr.value === deq_ptr.value) && (enq_ptr.flag ^ deq_ptr.flag) 为高io.enq.ready 为低io.enq.fire 为低无效。
- 此时入队,入队指针的 value 和 flag 不变。
### 出队操作
1. 队非空,正常出队
- 当队列非空时,可以正常出队,如果出队指针不经过最大容量位置,出队指针的 flag 不会翻转。
- io.deq.fire 为高有效io.deq.bits = regFiles(deq_ptr.value)deq_ptr.value+1 出队指针移动,出队指针标记位不翻转。
2. 队非空,出队后标记位翻转
- 当队非空,但是出队指针是靠近队尾时,出队一位后就到达了队头,出队指针的 flag 会翻转。
- 队列的容量为 10出队指针指向 9队非空。此时如果 io.deq.fire 为高,则 io.deq.bits = regFiles(9)deq_ptr.value+1循环队列加完后 deq_ptr.value=0出队指针移动出队指针标记位翻转。
3. 队空,出队有效信号为低,无法出队
- 当队空时enq_ptr === deq_ptr 为高io.deq.valid 为低io.deq.fire 为低无效。
- 此时出队,出队指针的 value 和 flag 不变。
### 刷新清空操作
1. flush 清空
- 当刷新信号有效时,重置出队和入队的指针和标记位,清空队列。
- 当 flush 为高时deq_ptr.value=0enq_ptr.value=0deq_ptr.flag=falseenq_ptr.flag=falseempty=true,full=false。
## MissUnit
<div>
<center>
<img src="../missunit_structure.png"
alt="MissUnit 结构"
style="zoom:100%"/>
<br>
MissUnit 结构
</center>
</div>
<br>
- 接收并管理多个 Miss 请求
- 处理来自 Fetch 和 Prefetch 的 Miss 请求。
- 将这些请求分派给适当数量的 MSHR 进行排队和状态管理。
- 管理 MSHR
- ICacheMissUnit 使用多个 MSHR 来跟踪和管理未完成的缓存未命中请求。为了防止 flush 时取指 MSHR 不能完全释放,设置取指 MSHR 的数量为 4预取 MSHR 的数量为 10。采用数据和地址分离的设计方法所有的 MSHR 共用一组数据寄存器,在 MSHR 只存储请求的地址信息、状态等信息。
- 接收来自 MainPipe 的取指请求和来自 IPrfetchPipe 的预取请求,取指请求只能被分配到 fetchMSHR预取请求只能分配到 prefetchMSHR入队时采用低 index 优先的分配方式。
- 在入队的同时对 MSHR 进行查询,如果请求已经在 MSHR 中存在,就丢弃该请求,对外接口仍表现 fire只是不入队到 MSHR 中。==在入队时向 Replacer 请求写入 waymask==。当请求完成后MSHR 会被释放,以便处理新的请求。
- 通过 TileLink 协议与 L2 缓存进行通信发送获取缓存块的请求mem_acquire并接收 L2 缓存的响应mem_grant
- 当到 L2 的总线空闲时,选择 MSHR 表现进行处理,整体 fetchMSHR 的优先级高于 prefetchMSHR只有没有需要处理的 fetchMSHR才会处理 prefetchMSHR。
- 对于 fetchMSHR采用低 index 优先的优先级策略,因为同时最多只有两个请求需要处理,并且只有当两个请求都处理完成时才能向下走,所有 fetchMSHR 之间的优先级并不重要。
- 对于 prefetchMSHR考虑到预取请求之间具有时间顺序采用先到先得的优先级策略在入队时通过一个 FIFO 记录入队顺序,处理时按照入队顺序进行处理。
- 通过状态机与 Tilelink 的 D 通道进行交互,到 L2 的带宽为 32byte需要分 2 次传输,并且不同的请求不会发生交织,所以只需要一组寄存器来存储数据。
- 当一次传输完成时,根据传输的 id 选出对应的 MSHR从 MSHR 中读取地址、掩码等信息,将相关信息写入 SRAM同时将 MSHR 释放。
- 向 MetaArray 和 DataArray 发送写请求,向 MainPipe 发送响应
- 当数据传回后MissUnit 根据相应的替换策略信息victim way将新数据写回 ICache 的 SRAM(Meta/Data) 。
- 同时向取指端或预取端返回“Miss 已完成”的响应包括写入了哪一路way、实际数据以及可能的校验信息如 corrupt 标记等)。
- 处理特殊情况(如 flush、fencei、数据损坏等
- 遇到 Flush 或 fence.i 等指令时MissUnit 可以终止或跳过某些 Miss 请求的写回,从而保证不在无效或过期的情况下写入缓存。
- 数据若出现 corrupt部分拍损坏也会在写回或发给前端时进行特殊处理或标记。
过程:
1. fetch_req 和 prefetch_req 分别先经过 DeMultiplexer (Demux),把请求分发给对应数量的 MSHR。fetch 的 MSHR 和 prefetch 的 MSHR 分成两组,分别处理取指和预取请求。
2. 每个 MSHR 内部会记录当前 Miss 请求的地址、索引、是否已经发出 acquire 等状态。当有其它相同的 miss 请求进来时,可以直接 “ hit MSHR ” 而不用重复创建新的请求。
3. 对于 fetchMSHR采用低 index 优先的优先级策略;对于 prefetchMSHR采用先到先得的优先级策略在入队 prefetchMSHR 前通过一个 priorityFIFO.记录入队顺序,处理时按照入队顺序进行处理。
4. fetchMSHR 发出的请求与 prefetchArb 选出的 prefetchMSHR 通过 acquireArb 合并后,通过 mem_acquire 发送给下一级或外部存储。
5. mem_grant 表示对这一条 Miss 请求的返回数据。需要分多个 beat 收集,直到收满一个 Cacheline。
6. 收集完 Cacheline 数据后,会根据对应 MSHR 的信息向 metaArray 和 dataArray 发起写操作 (meta_write, data_write),同时向取指端 (fetch_resp) 发送补全后的数据和标记 (waymask 等)。
7. 如果发生 flush 或 fencei在未发出请求前请求会被无效化请求被发出后会持续阻止新请求进入已经发出的访问最终会将返回过程走完但收到的响应并不会回复给 MainPipe 和 IPrefetchPipe也不会写给 MetaArray 和 DataArray。
Demultiplexer 类
grant:选择第一个 ready能写的 mshr写进去第 0 到 n 个端口,前面有 ready 的。比如 grant=seq(false,true),grant(1)为 true表示 1 端口前面有一个 ready 的端口0 端口))
io.out(i).valid:前 i-1 个 mshr 没有 ready 的,输入的写有效。
io.in.ready := grant.last || io.out.last.ready给 MissUnit 的 ready 信号有一个有效,那么 MissUnit 给 MSHR 的 ready 信号就有效。
## MissUnit 的功能点和测试点
### 处理取指缺失请求
处理来自 MainPipe 的取指单元的缓存缺失请求,将缺失请求分发到多个 Fetch MSHR 中的一个,避免重复请求。
低索引的请求优先处理。
1. 接受新的取指请求
- 当新的 fetch miss 与 MSHR 中的已有请求不重复时(通过 io.fetch_req.bits.blkPaddr / vSetIdx 给出具体地址MissUnit 会将请求分配到一个空闲的 Fetch MSHR 中。
- 当有新的取指缺失请求到达时io.fetch_req.valid 为高),且没有命中已有的 MSHRfetchHit 为低io.fetch_req.ready 应为高,表示可以接受请求。
- io.fetch_req.fire 成功握手后,该 MSHR 处于 valid = true 状态,并记录地址。
2. 处理已有的取指请求
- 当已有取指缺失请求到达时io.fetch_req.valid 为高),且命中已有的 MSHRfetchHit 为高io.fetch_req.ready 应为高,虽然不接受请求,但是表现出来为已经接收请求。
- fetchDemux.io.in.valid 应为低fetchDemux.io.in.fire 为低,表示没有新的请求被分发到 MSHR。
3. 低索引的请求优先进入 MSHR
- Fetch 的请求会通过 fetchDemux 分配到多个 Fetch MSHRfetchDemux 的实现中,低索引的 MSHR 会优先被分配请求。
- 当取指请求有多个 io.out(i).read 时,选择其中的第一个,也就是低索引的写入 MSHRio.chose 为对应的索引。
### 处理预取缺失请求
与 Fetch Miss 类似,但走另一些 MSHRPrefetch MSHR
1. 接受新的预取请求
- 当新的 prefetch miss 与 MSHR 中的已有请求不重复时(通过 io.prefetch_req.bits.blkPaddr / vSetIdx 给出具体地址MissUnit 会将请求分配到一个空闲的 Prefetch MSHR 中。
- 当有新的预取缺失请求到达时io.prefetch_req.valid 为高),且没有命中已有的 MSHRprefetchHit 为低io.prefetch_req.ready 应为高,表示可以接受请求。
- io.prefetch_req.fire 成功握手后,该 MSHR 处于 valid = true 状态,并记录地址。
2. 处理已有的预取请求
- 当已有预取缺失请求到达时io.prefetch_req.valid 为高),且命中已有的 MSHRprefetchHit 为高io.prefetch_req.ready 应为高,虽然不接受请求,但是表现出来为已经接收请求。
- prefetchDemux.io.in.valid 应为低prefetchDemux.io.in.fire 为低,表示请求被接受但未分发到新的 MSHR。
3. 低索引的请求优先进入 MSHR
- Prefetch 的请求会通过 prefetchDemux 分配到多个 Prefetch MSHRprefetchDemux 的实现中,低索引的 MSHR 会优先被分配请求。
- 当取指请求有多个 io.out(i).read 时,选择其中的第一个,也就是低索引的写入 MSHRio.chose 为对应的索引。
4. 先进入 MSHR 的优先进入 prefetchArb
- 从 prefetchDemux 离开后,请求的编号会进入 priorityFIFOpriorityFIFO 会根据进入队列的顺序排序,先进入队列的请求会先进入 prefetchArb。
- prefetchDemux.io.in.fire 为高,并且 prefetchDemux.io.chosen 有数据时,将其编号写入 priorityFIFO。
- 在 priorityFIFO 中有多个编号时,出队的顺序和入队顺序一致。
- 检查 priorityFIFO.io.deq.bit 中的数据即可。
### MSHR 管理与查找
1. MSHR 查找命中逻辑
- 当新的请求到来时,能够正确查找所有 MSHR判断请求是否命中已有 MSHR。
- 当新的请求(取指或预取)到来时,系统遍历所有 MSHR根据所有 MSHR 的查找信号 allMSHRs(i).io.lookUps(j).hit检查请求是否已经存在于某个 MSHR 中。
- 如果命中,则对应的 fetchHit 或 prefetchHit 为高。
- 对于 prefetchHit 为高,还有一种情况:预取的物理块地址和组索引与取指的相等((io.prefetch_req.bits.blkPaddr === io.fetch_req.bits.blkPaddr) && (io.prefetch_req.bits.vSetIdx === io.fetch_req.bits.vSetIdx))并且有取指请求 io.fetch_req.valid 有效时,也算命中
2. MSHR 状态的更新与释放
- 当请求完成后也就是来自内存总线的响应完成D 通道接收完所有节拍MSHR 能够正确地释放(清除其有效位),以便接收新的请求。
- TileLink D 通道返回的 source ID ,即 io.mem_grant.bits.source。
- 无效化信号 allMSHRs(i).io.invalid 为高,对应的 MSHR 的有效位 allMSHRs(i).valid 变为低
### acquireArb 仲裁
预取和取指的 acquire 都会发送给 acquireArbacquireArb 会选择一个 acquire 发送给 mem_acquire。
acquireArb 使用 chisel 自带的 Arbiter 实现,Arbiter 使用固定优先级仲裁,优先级从编号 0 开始,编号越小优先级越高。
1. acquireArb 仲裁
- acquireArb 会选择一个 acquire 发送给 mem_acquire。
- 当有多个 MSHR 同时发出请求时acquireArb 会根据优先级进行仲裁,选择优先级最高的 MSHR 发送请求。
- 取指请求总是在 0-3 号,预取请求直接在最后一号,所以取指请求优先级高于预取请求。
- 当取指 acquire 和预取 acquire 同时发出时fetchMSHRs(i).io.acquire 和 prefetchMSHRs(i).io.acquire 都有效,仲裁结果 acquireArb.io.out 应该和 fetchMSHRs(i).io.acquire 一致。
### Grant 数据接收与 Refill
在收到 TileLink D 通道数据时收集整行
- 累计 beat 数readBeatCnt直到完成一整行 (last_fire)
- 记录 corrupt 标志
- 将完成的请求映射回对应的 MSHR (id_r = mem_grant.bits.source)
1. 正常完整 Grant 流程readBeatCnt 为 0 时
- readBeatCnt 初始为 0refillCycles - 1 也为 0。
- io.mem_grant.valid 为高(因为 io.mem_grant.ready 默认为高,所以 io.mem_grant.fire 为高只需要 io.mem_grant.valid 为高)且 io.mem_grant.bits.opcpde(0)为高。
- 此时 respDataReg(0)= io.mem_grant.bits.data
- readBeatCnt 加一为 1。
2. 正常完整 Grant 流程readBeatCnt 为 1 时
- io.mem_grant.valid 为高且 io.mem_grant.bits.opcpde(0)为高。
- 此时 respDataReg(1)= io.mem_grant.bits.data
- readBeatCnt 重置回 0。
- last_fire 为高。
- 下一拍 last_fire_r 为高id_r=io.mem_grant.bits.source。
3. 正常完整 Grant 流程last_fire_r 为高
- last_fire_r 为高,并且 id_r 为 0-13 中的一个。
- 对应的 fetchMSHRs 或者 prefetchMSHRs 会被无效,也就是 fetchMSHRs_i 或 prefetchMSHRs_i-4 的 io_invalid 会被置高。
4. Grant 带有 corrupt 标志
- io.mem_grant.valid 为高且 io.mem_grant.bits.opcpde(0)为高io.mem_grant.bits.corrupt 为高,则 corrupt_r 应为高。
- 如果 io.mem_grant.valid 为高且 io.mem_grant.bits.opcpde(0)为高io.mem_grant.bits.corrupt 为高中有一个不满足,且此时 last_fire_r 为高,则 corrupt_r 重置为低。
### 替换策略更新 (Replacer)
MissUnit 在发出 Acquire 请求时,还会将本次选中的 victim way 对应的索引告诉 io.victim让替换策略更新其记录替换策略采用 PLRU
只有当 Acquire 真正“fire”时才说明成功替换replacer 需要更新状态
1. 正常替换更新
- 当 io.mem.acquire.ready & acquireArb.io.out.valid 同时为高,也就是 acquireArb.io.out.fir 为高时io.victim.vSetIdx.valid 也为高。
- io.victim.vSetIdx.bits = 当前 MSHR 请求的 acquireArb.io.out.bits.vSetIdx。
2. 生成 waymask
- 根据从 L2 返回的 mshr_resp 中 mshr_resp.bits.way 生成 waymask 信息。
- 返回的 mshr_resp.bits.way 有 16 位通过独热码生成一位掩码信息waymask 表示其中哪一路被替换。
- 生成的 waymask 应该和 mshr_resp.bits.way 一致。
### 写回 SRAM (Meta / Data)
在一条 Miss Request refill 完成时,将新得到的 Cache line 写到 ICache。
生成 io.meta_write 和 io.data_write 的请求,带上 waymask, tag, idx, data 。
生成 io.meta_write.valid 和 io.data_write.valid 信号。
1. 生成 io.meta_write.valid 和 io.data_write.valid 信号
- 当 grant 传输完成后,经过一拍后,即 last_fire_r 为高,且从 TileLink 返回的 mshr_resp 中的 mshr_resp.valid 为高。
- 并且此时没有硬件刷新信号和软件刷新信号,也就是 io.flush 和 io.fencei 为低。 在等待 l2 响应的过程中,没有刷新信号
- 也没有数据 corrupt即 corrupt_r 为低。
- 那么 io.meta_write.valid 和 io.data_write.valid 均为高。
2. 正常写 SRAM
- io.meta_write.bits 的 virIdx、phyTag、waymask、bankIdx、poison 应该正常更新
- io.data_write.bits 的 virIdx、data、waymask、bankIdx、poison 应该正常更新
### 向 mainPipe/prefetchPipe 发出 Miss 完成响应fetch_resp
在完成 refill 后无论是否要真正写阵列都会向取指端发送“Miss 请求完成”
更新 io.fetch_resp.valid 和 fetch_resp.bits。
1. 正常 Miss 完成响应
- 当 grant 传输完成后,经过一拍后,即 last_fire_r 为高,且从 TileLink 返回的 mshr_resp 中的 mshr_resp.valid 为高。
- 无论此时是否有硬件刷新信号和软件刷新信号, io.fetch_resp.valid 都为高,说明可向取指端发送响应。
- io.fetch_resp.bits 中的数据更新:
- io.fetch_resp.bits.blkPaddr = mshr_resp.bits.blkPaddr
- io.fetch_resp.bits.vSetIdx = mshr_resp.bits.vSetIdx
- io.fetch_resp.bits.waymask = waymask
- io.fetch_resp.bits.data = respDataReg.asUInt
- io.fetch_resp.bits.corrupt = corrupt_r
### 处理 flush / fencei
一旦收到 io.flush 或 io.fencei 时,对未发射的请求可立即取消,对已经发射的请求在拿到数据后也不写 SRAM。
1. MSHR 未发射前 fencei
- 如果 MSHR 还没有通过 io.acquire.fire 发出请求,就应立即取消该 MSHRmshr_resp.valid= false既不发出请求也不要写 SRAM。
- 当 io.fencei 为高时fetchMSHRs 和 prefetchMSHRs 的 io.req.ready 和 io.acquire.valid 均为低,表示请求不发射。
2. MSHR 未发射前 flush
- 由于 fetchMSHRs 的 io.flush 被直接设置为 false所以 io.flush 对 fetchMSHRs 无效,但是对 prefetchMSHRs 有效。
- 当 io.flush 为高时,只能发射 fetchMSHRs 的请求。
3. MSHR 已发射后 flush/fencei
- 已经发射了请求,之后再有刷新信号,那么等数据回来了但不写 SRAM。
- 在发射后io.flush/io.fencei 为高时,等待数据回来,但是写 SRAM 的信号write_sram_valid、io.meta_write.valid 和 io.data_write.valid 均为低,表示不写 SRAM。
- 对于 response fetch 无影响。

View File

@ -1,162 +0,0 @@
---
title: CtrlUnit
linkTitle: CtrlUnit
weight: 12
---
<div class="icache-ctx">
</div>
## CtrlUnit
目前 CtrlUnit 主要负责 ECC 校验使能/错误注入等功能。
RegField 案例类和伴生对象的作用RegReadFn 和 RegWriteFn 案例类和伴生对象的作用。
通过两个控制寄存器 CSReccctrl 和 ecciaddr来实现错误注入。
在 eccctrlBundle 中,定义 eccctrl 的 ierror、istatus、itarget、inject、enable 域的初始值。
在 ecciaddrBundle 中,定义 ecciaddr 的 paddr 域的初始值。
### mmio-mapped CSR
CtrlUnit 内实现了一组 mmio-mapped CSR连接在 tilelink 总线上,地址可由参数 `cacheCtrlAddressOpt` 配置,默认地址为`0x38022080`。总大小为 128B。
当参数 `cacheCtrlAddressOpt``None`CtrlUnit **不会实例化**。此时 ECC 校验使能**默认开启**,软件不可控制关闭;软件不可控制错误注入。
目前实现的 CSR 如下:
```plain
64 10 7 4 2 1 0
0x00 eccctrl | WARL | ierror | istatus | itarget | inject | enable |
64 PAddrBits-1 0
0x08 ecciaddr | WARL | paddr |
```
| CSR | field | desp |
| ------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| eccctrl | enable | ECC 错误校验使能,原 sfetchctl(0) 。 <br>注:即使不使能,在 icache 重填时仍会计算 parity可能会有额外功耗但如果不计算则在未使能转换成使能时需要冲刷 icache否则读出的 parity 有 50%概率是错的)。 |
| eccctrl | inject | ECC 错误注入使能,写 1 即使能,读恒 0 |
| eccctrl | itarget | ECC 错误注入目标 <br>0: metaArray<br>1: rsvd<br>2: dataArray<br>3: rsvd |
| eccctrl | istatus | ECC 错误注入状态read-only<br>0: idle注入控制器闲置<br>1: working收到注入请求注入中<br>2: injected注入完成等待触发<br>3: rsvd<br>4: rsvd<br>5: rsvd<br>6: rsvd<br>7: error注入出错 |
| eccctrl | ierror | ECC 错误原因read-only<br>0: ECC 未使能 (i.e. !eccctrl.enable) <br>1: inject 目标 SRAM 无效 (i.e. eccctrl.itarget==rsvd) <br>2: inject 目标地址 (i.e. ecciaddr.paddr) 不在 ICache 中<br>3: rsvd <br>4: rsvd <br>5: rsvd <br>6: rsvd <br>7: rsvd |
| ecciaddr | paddr | ECC 错误注入物理地址 |
| RERI standard | | RERI 手册还要求了错误计数等寄存器,用于软件获取 RAS controller 状态,参考手册,可能需要与 dcache、L2cache 统一在后端实现icache 像现在给 BEU 送 error 一样送给后端。<br>即:暂时不需要在 icache 实现,但要把错误计数等机制所需的接口准备出来 |
### 错误校验使能
CtrlUnit 的 `eccctrl.enable` 位直接连接到 MainPipe控制 ECC 校验使能。当该位为 0 时ICache 不会进行 ECC 校验。但仍会在重填时计算校验码并存储,这可能会有少量的额外功耗;如果不计算,则在未使能转换成使能时需要冲刷 ICache否则读出的 parity code 可能是错的)。
### 错误注入使能
CtrlUnit 内部使用一个状态机控制错误注入过程,其 status (注意:与 `eccctrl.istatus` 不同)有:
- idle注入控制器闲置
- readMetaReq发送读取 metaArray 请求
- readMetaResp接收读取 metaArray 响应
- writeMeta写入 metaArray
- writeData写入 dataArray
当软件向 `eccctrl.inject` 写入 1 时,进行以下简单检查,检查通过时状态机进入 `readMetaReq` 状态:
- 若 `eccctrl.enable` 为 0报错 `eccctrl.ierror=0`
- 若 `eccctrl.itarget` 为 rsvd(1/3),报错 `eccctrl.ierror=1`
`readMetaReq` 状态下CtrlUnit 向 MetaArray 发送 `ecciaddr.paddr` 地址对应的 set 读取的请求,等待握手。握手后转移到 `readMetaResp` 状态。
`readMetaResp` 状态下CtrlUnit 接收到 MetaArray 的响应,检查 `ecciaddr.paddr` 地址对应的 ptag 是否命中,若未命中则报错 `eccctrl.ierror=2`。否则,根据 `eccctrl.itarget` 进入 `writeMeta``writeData` 状态。
`writeMeta``writeData` 状态下CtrlUnit 向 MetaArray/DataArray 写入任意数据,同时拉高 `poison` 位,写入完成后状态机进入 `idle` 状态。
ICache 顶层中实现了一个 Mux当 CtrlUnit 的状态机不为 `idle` 时,将 MetaArray/DataArray 的读写口连接到 CtrlUnit而非 MainPipe/IPrefetchPipe/MissUnit。当状态机 `idle` 时反之。
状态机和错误注入流程
- `is_idle`:模块处于空闲状态,等待错误注入的触发。只有当 `eccctrl.istatus``working` 时,状态机才会转移到 `is_readMetaReq` 状态,准备读取元数据。
- `is_readMetaReq`:发送读取元数据请求。通过接口 `metaRead` 向缓存发送读取请求。当握手成功后状态会转移到 `is_readMetaResp`
- `is_readMetaResp`:接收元数据响应并验证。如果未命中,则会设置错误状态。没命中会转移状态到`is_idle`,并设置错误错误注入状态和错误原因;如果找到有效的缓存行并且标签匹配,根据错误注入目标来判断是向元数据还是数据阵列写入错误。
- `is_writeMeta`:写入带 poison 标记的数据完成注入。当握手成功后,错误注入状态设置为 injected注入完成等待触发,状态转移到`is_idle`。
- `is_writeData`:写入带 poison 标记的数据完成注入。当握手成功后,向数据阵列写入错误数据,错误注入状态设置为 injected注入完成等待触发,状态转移到`is_idle`。
寄存器和接口映射
- eccctrl控制 ECC 启用、错误注入状态等。寄存器通过 eccctrlRegField 进行映射。
- ecciaddr指定错误注入的物理地址。通过 ecciaddrRegField 映射。
- 通过寄存器描述符RegFieldDesc和寄存器字段RegField定义了寄存器的这些是寄存器的描述信息和读写逻辑。
- 通过 node.regmap这两个寄存器被映射到指定的地址偏移。eccctrl 寄存器被映射到 params.eccctrlOffset 地址ecciaddr 寄存器被映射到 params.ecciaddrOffset 地址。
- `node.regmap` 使得这两个寄存器可以通过外部的 TileLink 接口进行访问,外部模块可以读写这些寄存器以控制 ECC 和错误注入功能。
## CtrlUnit 的功能点和测试点
### ECC 启用/禁用
控制 eccctrl.enable 字段来启用或禁用 ECC 功能。外部系统可以通过写寄存器 eccctrl 来控制 ECC 是否启用。
- 通过寄存器写入控制信号 enable当 enable 为 true 时ECC 功能启用;为 false 时ECC 功能禁用。
1. 启用 ECC
- 向 eccctrl.enable 寄存器写入 true验证模块内部 eccctrl.enable 设置为 true并确保后续的错误注入操作能够成功进行。此测试确保 eccctrl.enable 写操作被执行。
- 确保 eccctrl.enable 被正确设置为 true并触发 eccctrlRegWriteFn 中的写操作逻辑。
2. 禁用 ECC
- 向 eccctrl.enable 寄存器写入 false验证模块内部 eccctrl.enable 设置为 false并确保在后续的错误注入过程中ECC 功能被禁用,不允许进行错误注入。此测试确保 eccctrl.enable 写操作被正确设置为 false。
- 验证禁用 ECC 时 eccctrl.enable 为 false并触发 eccctrlRegWriteFn 中的错误处理分支。x.istatus = eccctrlInjStatus.error 和 x.ierror = eccctrlInjError.notEnabled
### 状态机转换
根据状态机的状态,验证错误注入的流程是否正确。
1. is_idle 状态
- 初始为 is_idle 状态。
- 当 eccctrl.istatus 为 working 时,验证此时的状态为 is_readMetaReq。
2. is_readMetaReq 状态
- 当握手成功后io.metaRead.ready 和 io.metaRead.valid 都为高),验证此时的状态为 is_readMetaResp。
3. is_readMetaResp 状态
- 未命中
- 当 waymask 全零的时候,表示没有命中,会进入 is_idle 状态,并且设置错误错误注入状态和错误原因。
- 验证此时的状态为 is_idle eccctrl.istatus = error 和 eccctrl.ierror = notFound。
- 命中
- 当 waymask 不全零的时候,表示命中,会根据错误注入目标来判断是向元数据还是数据阵列写入错误。
- 当 eccctrl.itarget=metaArray 时,验证此时的状态为 is_writeMeta ;当 eccctrl.itarget=metaArray 时,验证此时的状态为 is_writeData。
4. is_writeMeta 状态
- RegWriteFn
- 此状态进入后io.dataWrite.valid 会为高
- x.itarget = req.itarget
- 当 req.inject 为高并且 x.istatus = idle 时:
- 如果 ecc 的 req.enable = false则验证 x.istatus = error 且 x.ierror = notEnabled
- 否则,如果 req.itarget = metaArray 和 dataArray则验证 x.istatus = error 且 x.ierror = targetInvalid
- 如果都不满足,则验证 x.istatus = working
- 状态转换
- 当 io.metaWrite.fire 为高, 验证下一个状态为 is_idle并且 eccctrl.istatus = injected。
5. is_writeData 状态
- RegWriteFn
- 此状态进入后io.dataWrite.valid 会为高
- res.inject = false
- 当 ready 为高,且 x.istatus = injected 或 x.istatus = error 时,验证 x.istatus = idle 和 x.ierror = notEnabled
- 状态转换
- 当 io.dataWrite.fire 为高, 验证下一个状态为 is_idle并且 eccctrl.istatus = injected。
### 寄存器映射和外部访问
通过 TileLink 总线将寄存器映射到特定地址,使外部模块可以读写 ECC 控制寄存器和注入地址寄存器。
- 使用 TLRegisterNode 实现寄存器的映射,使得外部系统可以通过地址访问寄存器。寄存器的读写操作通过 TileLink 协议进行。
1. 外部读取和写入 ECC 控制寄存器
- 验证外部模块可以通过 TileLink 协议正确读取和写入 eccctrl 和 ecciaddr 寄存器,并对模块内部的状态产生影响,确保读写操作完全覆盖。
2. 外部模块触发错误注入
- 通过外部模块经 TileLink 总线向 eccctrl.inject 寄存器写入 true触发错误注入验证内部状态是否按 RegWriteFn 内部过程执行。

View File

@ -1,178 +0,0 @@
---
title: ICache
linkTitle: ICache
weight: 12
---
<div class="icache-ctx">
</div>
## ICache
各种组合数据的宽度以 system verilog/verilog 中的为准。
- IPrefetchPipe 接收来自 FTQ 的预取请求,然后向 MetaArray 和 ITLB 发送请求,再从 ITLB 的响应得到 paddr之后与 MetaArray 返回的 tag 进行比较得到命中信息把命中信息、MetaArray ECC 校验信息和 ITLB 信息一并写入 WayLookup同时进行 PMP 检查。未命中就将信息发送给 MissUnit 处理MissUnit 通过 TileLink 总线向 L2Cache 发起请求,获取数据后返回给 MetaArray 和 IPrefetchPipe。之后会判断是否 Miss如果 Miss 则把预取请求发送到 MissUnit它会通过 TileLink 向 L2 做预取指。
- MainPipe 接收来自 FTQ 的取指请求,然后从 WayLookup 获取路命中信息和 ITLB 查询结果,再访问 DataArray。命中后向 replacer 发送 touch 请求replacer 采用 PLRU 替换策略,接收到 MainPipe 的命中更新,向 MissUnit 提供写入的 waymask。同时进行 PMP 检查,接收 DataArry 返回的数据。对 DataArray 做 ECC 校验,根据 DataArry 和 MetaArry 的校验结果MetaArray 的校验结果来自 Waylookup判断是否将错误报告给总线beu。之后如果 DataArry 没有命中,将信息发往 MissUnit 处理。MissUnit 通过 TileLink 总线向 L2Cache 发起请求,获取数据后返回给 DataArray 和 MainPipe。之后就可以将数据返回给 IFU。
- MetaArray 存储缓存行的标签Tag和 ECC 校验码
- 使用双 Bank SRAM 结构支持双线访问Double-Line每个 Bank 存储部分元数据。
- 标签包含物理地址的高位,用于地址匹配。
- 支持标签 ECC 校验,检测和纠正存储错误。
- valid_array 记录每个 Way 的有效状态Flush 操作会清零
- DataArray 存储实际的指令数据块。
- 数据按 Bank 划分为八个,每个 Bank 宽度为 64 位,支持多 Bank 并行访问。
- 数据 ECC 校验,分段生成校验码,增强错误检测能力。
- 支持双线访问,根据地址偏移选择 Bank单周期可读取 32 字节数据。
- 冲刷信号有三种ftqPrefetch.flushFromBpuitlbFlushPipe模块外部的 fencei 和 flush 信号。
- ftqPrefetch.flushFromBpu通过 FTQ 来自的 BPU 刷新信号,用于控制预取请求的冲刷。
- itlbFlushPipeITLB 的冲刷信号itlb 在收到该信号时会冲刷 gpf 缓存。
- fencei:刷新 MetaArray清除所有路的 valid_array 清零missUnit 中所有 MSHR 置无效。
- flush:mainPipe 和 prefetchPipe 所有流水级直接置无效wayLookup 读写指针复位gpf_entry 直接置无效,missUnit 中所有 MSHR 置无效。
### Replacer
采用 PLRU 更新算法,考虑到每次取指可能访问连续的 doubleline对于奇地址和偶地址设置两个 replacer在进行 touch 和 victim 时根据地址的奇偶分别更新 replacer。
<div>
<center>
<img src="../plru.png"
alt="PLRU 算法示意"
style="zoom:100%"/>
<br>
PLRU 算法示意
</center>
</div>
<br>
#### touch
Replacer 具有两个 touch 端口,用以支持双行,根据 touch 的地址奇偶分配到对应的 replacer 进行更新。
#### victim
Replacer 只有一个 victim 端口,因为同时只有一个 MSHR 会写入 SRAM同样根据地址的奇偶从对应的 replacer 获取 waymask。并且在下一拍再进行 touch 操作更新 replacer。
## ICache 的功能点和测试点
### FTQ 预取请求处理
接收来自 FTQ 的预取请求,经 IPrefetchPipe 请求过滤(查询 ITLB 地址,是否命中 MetaArryPMP 检查),(有异常则由 MissUnit 处理)后进入 WayLookup。
1. 预取地址命中,无异常
- io.ftqPrefetch.req.bits 的 startAddr 和 nextlineStart 在正常地址范围内itlb 命中无异常itlb 查询到的地址与 MetaArry 的 ptag 匹配pmp 检查通过。
- 如果没有监听到 MSHR 同样的位置发生了其它 cacheline 的写入,那么验证 wayLookup.io.write 的内容应该命中的取指数据。
- 如果监听到 MSHR 同样的位置发生了其它 cacheline 的写入,那么验证 wayLookup.io.write 的内容应该是未命中的取指数据。
2. 预取地址未命中,无异常
- io.ftqPrefetch.req.bits 的 startAddr 和 nextlineStart 在正常地址范围内itlb 命中无异常itlb 查询到的地址与 MetaArry 的 ptag 不匹配pmp 检查通过。
- 如果监听到 MSHR 将该请求对应的 cacheline 写入了 SRAM那么验证 wayLookup.io.write 的内容应该命中的取指数据。
- 如果监听到 MSHR 没有将该请求对应的 cacheline 写入了 SRAM那么验证 wayLookup.io.write 的内容应该未命中的取指数据。
3. 预取地址 TLB 异常,无其他异常
- io.ftqPrefetch.req.bits 的 startAddr 和 nextlineStart 在正常地址范围内itlb 异常。
- 验证 wayLookup.io.write 的 itlb_exception 内容中其有对应的异常类型编号pf:01;gpf:10;af:11
4. 预取地址 PMP 异常,无其他异常
- io.ftqPrefetch.req.bits 的 startAddr 和 nextlineStart 在正常地址范围内itlb 命中无异常itlb 查询到的地址与 MetaArry 的 ptag 匹配pmp 检查未通过。
- 验证 wayLookup.io.write 的 tlb_pbmt 内容中其有对应的异常类型编号nc:01;io:10
### FTQ 取指请求处理
io.fetch.resp <> mainPipe.io.fetch.resp 发送回 IFU 的数据是在 io.fetch.resp。
接收来自 FTQ 的取指请求,从 WayLookup 获取路命中信息和 ITLB 查询结果,再访问 DataArray监控 MSHR 的响应。更新 replacer做 pmp 检查。后做 DataArray 和 MetaArray 的 ECC 校验。最后将数据发送给 IFU。
1. 取指请求命中,无异常
- io.fetch.req.bits.pcMemRead 的 0-4 的 startAddr 和 nextlineStart 在正常地址范围内,从 WayLookup 获取信息命中pmp 检查正常DataArray 和 MetaArray 的 ECC 校验正常。
- 验证 replacer.io.touch 的 vSetIdx 和 way 和 ftq 的 fetch 一致missUnit.io.victim 的 vSetIdx 和 way 是按照制定的算法生成的。
- 验证 io.fetch.resp 的数据应该是取指的数据。
2. 取指请求未命中MSHR 返回的响应命中,无异常
- io.fetch.req.bits.pcMemRead 的 0-4 的 startAddr 和 nextlineStart 在正常地址范围内,从 WayLookup 获取信息未命中pmp 检查正常DataArray 和 MetaArray 的 ECC 校验正常。
- 请求在 MSHR 返回的响应命中。
- 验证 missUnit.io.victim 的 vSetIdx 和 way 是按照制定的算法生成的。
- 验证 io.fetch.resp 的数据应该是取指的数据。
3. 取指请求命中,ECC 校验错误,无其他异常
- io.fetch.req.bits.pcMemRead 的 0-4 的 startAddr 和 nextlineStart 在正常地址范围内,从 WayLookup 获取信息命中pmp 检查正常DataArray 或 MetaArray 的 ECC 校验错误。
- 验证 io.error.valid 为高,且 io.error.bits 内容为对应的错误源和错误类型。
- 先刷 MetaArray 的 ValidArray,给 MissUnit 发请求,由其在 L2 重填,阻塞至数据返回。
- 验证 replacer.io.touch 的 vSetIdx 和 way 和 ftq 的 fetch 一致missUnit.io.victim 的 vSetIdx 和 way 是按照制定的算法生成的。
- 验证 io.fetch.resp 的数据应该是取指的数据。
4. 取指请求未命中,但是 exception 非 0af、gpf、pf无其他异常
- io.fetch.req.bits.pcMemRead 的 0-4 的 startAddr 和 nextlineStart 在正常地址范围内,从 WayLookup 获取信息命中pmp 检查未通过DataArray 和 MetaArray 的 ECC 校验正常。
- 验证 io.fetch.resp 为对应的错误源和错误类型。
- 验证 io.fetch.resp 的数据无效,里面有异常类型。
5. 取指请求未命中,通过 WayLookup 中读取到的预取过来的 itlb 中返回 pbmt。
- 有 itlb_pbmt 和 pmp_mmio 时,他们合成 s1_mmio传递到 s2_mmio,生成 s2_miss,有特殊情况就不会取指。
- io.fetch.req.bits.pcMemRead 的 0-4 的 startAddr 和 nextlineStart 在正常地址范围内,从 WayLookup 获取信息命中pmp 检查通过DataArray 和 MetaArray 的 ECC 校验正常。
- 验证 io.fetch.resp 为对应的错误源和错误类型。
- 验证 io.fetch.resp 的数据无效,里面有特殊情况类型类型。
6. 取指请求未命中pmp 返回 mmio ,处理同 5。
### MetaArray 功能
在 IPrefetchPipe 的 S0接收来自 IPrefetchPipe 的读请求 read返回对应路和组的响应 readResp。
在 miss 的时候MissUnit 会将会应的数据写入 write 到 MetaArray。
MetaArray 主要存储了每个 Cache 行的标签和 ECC 校验码。
1. 元数据写入操作(对应的 Set 已满): ICacheMetaArray 应当能够正确地将元数据(标签和有效位)写入到指定的 Set 和 Way 。
- 从 MissUnit 返回的请求都是未命中的请求(已命中不会向 MissUnit 请求,那么 MissUnit 自然也不会向 MetaArray 写入)。
- 发送一个写请求 write 到 ICacheMetaArrayICacheReplacer 根据 PLRU 替换策略指定 way替换路被写入 waymask最后指定 virIdx、phyTag、waymask、bankIdx、poison。
- 写入操作后,发起一个对相同虚拟索引的读请求。验证 readResp 的 metas 和 codes 分别包含写入的 ptag 和 ecc code并且对于写入的路readResp.entryValid 信号被置为有效。
2. 元数据读取操作 (命中): 当一个读请求在 ICacheMetaArray 中命中时(存在有效的条目),它应该返回正确的元数据(标签和有效位)。
- 首先,向特定的虚拟索引(组和路)写入元数据(参照上面的写入操作)。然后,向相同的虚拟索引发送一个读请求。
- 验证 readResp.metas 包含之前写入的物理标签并且对于相应的路readResp.entryValid 信号被置为有效。
3. 元数据读取操作 (未命中): 当读取一个尚未被写入的地址时ICacheMetaArray 应当指示未命中(条目无效)。
- 向 ICacheMetaArray 发送一个读请求,请求的虚拟索引在复位后从未被写入过。
- 验证对于任何路readResp.entryValid 信号都没有被置为有效。 对应的 readResp.metas 和 codes 的内容是 DontCare 也就是 0。
4. 独立的缓存组刷新:在第 i 个端口是有效的刷新请求,并且该请求的 waymask 指定了当前正在处理的第 w 路时,应该使第 i 个端口的条目无效。
- 先向 ICacheMetaArray 写入指定一个或多个端口的元数据,然后再给对应的端口的路发送刷新请求 io.flush其包含虚拟索引 virIdx 和路掩码 waymask。
- 验证 valid_array 对应的路中的 virIdx 被置为无效io.readResp.entryValid 对应路的对应端口为无效。
5. 全部刷新操作: ICacheMetaArray 应当能够在接收到全部刷新请求时,使所有条目无效。
- 先向多个不同的虚拟索引写入元数据。然后置位 io.flushAll 信号。
- 验证步骤: 在 io.flushAll 信号置位后发起对所有之前写入过的虚拟索引的读请求。验证在所有的读取响应中对于任何路readResp.entryValid 信号都没有被置为有效。
### DataArray 功能
与 MetaArray 类似,在 MainPipe 的 S0接收来自 MainPipe 的读请求 read返回对应路和组的响应 readResp。
在 miss 的时候MissUnit 会将会应的数据写入 write 到 DataArray。
DataArray 主要存储了每个 Cache 行的标签和 ECC 校验码。
1. 数据写入操作(对应的 Set 已满): ICacheDataArray 应当能够正确地将数据写入到指定的 Set (组)、Way (路) 和数据 Bank (存储体)。
- 发送一个写请求 write 到 ICacheDataArrayICacheReplacer 根据 PLRU 替换策略指定 way替换路被写入 waymask最终指定虚拟索引、数据、路掩码、存储体索引 bankIdx 和毒化位。写入的数据模式应跨越多个数据存储体。
- 写入操作后,发起一个对相同虚拟索引和块偏移量的读请求。验证 readResp.datas 与写入的数据相匹配。
2. 数据读取操作 (命中): 当一个读请求命中时(相应的元数据有效),它应该从相应的组、路和数据存储体返回正确的数据。
- 首先,向特定的虚拟索引和块偏移量写入数据。然后,向相同的虚拟索引和块偏移量发送一个读请求。使用不同的块偏移量进行测试,以覆盖存储体的选择逻辑。
- 验证 readResp.datas 包含之前写入的数据。
3. 数据读取操作 (未命中): 当读取一个尚未被写入的地址时ICacheDataArray 的输出应该是默认值或无关值。
- 向 ICacheDataArray 发送一个读请求,请求的虚拟索引在复位后从未被写入过。
- 验证 readResp.datas 为 0。

View File

@ -1,176 +0,0 @@
---
title: LoadQueueRAR
linkTitle: LoadQueueRAR
weight: 12
---
**本文档参考[香山LSQ设计文档](https://github.com/OpenXiangShan/XiangShan-Design-Doc/tree/master/docs/memblock/LSU/LSQ)写成**
本文档撰写的内容截至[ca892e73]
请注意,本文档撰写的测试点仅供参考,如能补充更多测试点,最终获得的奖励可能更高!
# LoadQueueRAR 简介
LoadQueueRAR用于保存已经完成的load指令的用于load to load违例检测的信息。
多核环境下会出现load to load违例。单核环境下相同地址的load乱序执行本来是不关心的但是如果两个load之间有另外一个核做了相同地址的store并且本身这个核的两个load做了乱序调度就有可能导致新的load没有看到store更新的结果但是旧的load看到了出现了顺序错误。
多核环境下的load-load违例有一个特征当前DCache一定会收到L2 cache发来的Probe请求使得DCache主动释放掉这个数据副本这时DCache会通知load queue将相同地址的load queue中已经完成访存的项做一个release标记。后续发往流水线的load指令会查询load queue中在它之后相同地址的load指令如果存在release标记就发生了load-load违例。
## 术语说明
| 名称 | 描述 |
| ------------------------------- | ------------------------------------------ |
| L2Cache | 二级高速缓存 |
| DCache | 数据缓存 |
| ROB | 重排序缓冲区 |
| CAM | 内容可寻址存储器 |
| FTQ | 取指目标队列 |
## ld-ld违例
多核环境下可能会出现load to load违例在单核环境中相同地址的load乱序执行通常不被关注因为它们在同一核内执行不会影响其他核的状态也不会被其他核的操作影响。但是当两个load操作之间有另一个核对相同地址进行了store操作情况就变得复杂。
考虑以下指令序列:
```
load1core1
storecore2
load2core1
```
指令的实际执行顺序为:
```
load2core1
storecore2
load1core1
```
由于指令的乱序执行,可能导致以下情况:旧的 load1 指令在执行时读取到了 store 修改后的新数据,而新的 load2 指令却读取到了未被修改的旧数据。这种执行顺序的变化会导致数据的不一致性,进而引发访存错误。
因此,在多核环境中,正确处理指令的执行顺序和内存一致性是至关重要的,以确保所有核都能看到一致的内存状态。
## 整体框图
<div>
<center>
<img src="../LoadQueueRAR_structure.svg"
alt="LoadQueueRAR结构示意图"
style="zoom:100%"/>
<br>
图1LoadQueueRAR结构示意图<br><br>
</center>
</div>
LoadQueueRAR最多能够存储72条指令为了同VirtualLoadQueue的大小保持一致每条指令占用一个条目。每个条目包含指令的物理地址paddr、与指令相关的信息uop、以及标记为已释放released和已分配allocated的状态寄存器。
该模块通过 FreeList 子模块管理 entry 资源FreeList 中存储的是 entry 的编号。当一条指令满足入队条件时FreeList 会为其分配一个 entry 编号,并将该指令存放在相应的 entry 中。指令出队时,需要释放所占用的 entry 资源,并将条目编号重新放回 FreeList 中,以供后续指令使用。
PaddrModule 的实现基于内容可寻址存储器CAM其深度为 72数据宽度为 48。CAM 为每条流水线提供一个写端口其中物理地址paddr作为写数据wdata条目编号作为写地址waddr。此外CAM 还为每条流水线提供了一个地址查询端口releaseViolationMdata并为数据缓存DCache提供另一个地址查询端口releaseMdata
<mrs-functions>
## 模块功能说明
### 功能1发生ld-ld违例的指令请求入队
当query到达load流水线的s2时判断是否满足入队条件如果在当前load指令之前有未完成的load指令,且当前指令没有被flush时当前load可以入队。
具体入队条件如下:
1. 指令的入队请求必须有效,具体通过检查 `query.req.valid` 是否等于 1。如果该条件满足系统将继续处理指令的入队。
2. 指令必须确认尚未写回到重排序缓冲区ROB。这一条件通过比较指令在 VirtualLoadQueue 中的写回指针与该指令分配的 `lqIdx` 来验证。指令只有在到达 VirtualLoadQueue 的队头,并且其地址和数据均已准备好后,才能被写回到 ROB。这一机制确保了指令执行的顺序性和数据的有效性。
3. 指令不能处于冲刷状态。为此,系统需要比较重定向指针所指向的指令与该指令的 `robIdx`、`ftqidx`及 FTQ 内的偏移(`ftqoffset`)。如果两者不相同,则说明该指令可以安全入队,从而避免潜在的冲突和数据不一致性。
在 LoadQueueRAR 指令成功入队后,系统会执行一系列响应操作,以确保指令被正确管理和处理。具体的入队响应操作如下:
1. 拉高 allocated 寄存器。系统将指令的 `allocated` 寄存器设置为高电平。这一操作的目的是明确标识该指令已成功分配到 LoadQueueRAR 中。通过将 `allocated` 寄存器拉高,后续的处理逻辑能够迅速识别出该指令的状态,从而避免对未分配指令的误操作。
2. 写入指令相关信息到 uop。指令的相关信息将被写入到微操作`uop`)中。这些信息包括指令的类型、目标寄存器、操作数等关键信息。将这些信息存储在 `uop` 中,确保后续的执行阶段能够准确获取和使用这些数据,从而执行相应的操作。这一过程对于指令的正确执行至关重要。
3. 物理地址写入 PaddrModule。指令的物理地址将被写入到 PaddrModule 中。这一操作的主要目的是为后续的地址查询和管理提供支持。
4. 检测 Release 的 Valid 信号。系统将检测 `release` 的有效信号是否被拉高。如果该信号有效,将进一步比较物理地址是否相同。如果物理地址一致,则对应条目的 `released` 信号将被设置为高电平,可以用于后续操作。
### 功能2检测ld-ld违例条件
在 Load 指令的处理过程中,为了确保数据的一致性和正确性,系统需要检测潜在的 Load-Load 违例。当 load 到达流水线的 s2 时会检查RAR队列中是否存在与当前load指令物理地址相同且比当前指令年轻的load指令如果这些 load 已经拿到了数据并且被标记了release说明发生 load - load 违例被标记release的指令需要从取指重发。 该检测过程主要涉及将查询指令的物理地址和相关信息与队列中存储的指令进行对比。具体流程如下:
1. 对比 ROB 索引。通过对比两条指令的robidx识别队列中是否存在比查询指令更年轻的指令。
2. 物理地址匹配。检查这两条指令的物理地址是否相同。这一对比通过 `releaseViolationMmask(w)(i)` 来进行,以确定两条指令是否访问了相同的内存位置。
3. 检查 Released 标记。如果该条指令的 `released` 寄存器被拉高,表明该指令已被标记为释放,说明它可以被重新使用。
一旦检测到 Load-Load 违例,系统将在下一个时钟周期内将 `resp.rep_rm_fetch` 信号拉高,以通知其他组件发生了违例。触发 Load-Load 违例的 Load 指令将被标记为需要重新从取指阶段执行。重定向请求将在这些指令到达 ROB 队列的尾部时发出,确保指令能够在合适的时机得到正确的处理。
该过程分为两个时钟周期进行:
- 第一拍进行条件匹配对比物理地址和指令状态得到mask。
- 第二拍生成是否发生违例的响应信号(`resp.rep_rm_fetch` )。
由于 Load-Load 违例出现的频率相对较低,因此系统会选择在指令到达 ROB 的头部时才进行处理。这种处理方式类似于异常处理,确保系统能够在合适的时机对潜在的违例情况进行响应。
### 功能3released寄存器更新
released寄存器需要更新的三种情况
1. missQueue模块的replace_req在mainpipe流水线的s3栈发起release释放dcache块release信号在下一拍进入loadqueue。
2. probeQueue模块的probe_req在mainpipe流水线的s3栈发起release释放dcache块release信号在下一拍进入loadqueue。
3. atomicsUnit模块的请求在mainpipe流水线的s3栈发生miss时需要释放dcache块release信号在下一拍进入loadQueue。
release信号的到达时机可以分为以下两种情况
1. 指令入队时到达。如果查询指令传来的paddr的高42位信号与paddr的高位信号相同并且该指令能够成功入队将对应entry的released寄存器信号拉高
2. 指令入队后到达。如果paddrmodule中存放的paddr的高42位信号与paddr的高位信号相同,将对应的released寄存器信号拉高
值得注意的是dcache release 信号在更新 load queue 中 `released` 状态位时, 会与正常 load 流水线中的 load-load 违例检查争用 load paddr cam 端口. release 信号更新 load queue 有更高的优先级. 如果争用不到资源, 流水线中的 load 指令将立刻被从保留站重发.
### 功能4指令的出队
Load指令的出队需要满足以下条件其中之一
1. 当比队列entry中存放的指令更老的指令已经全部写回到ROB时该指令可以出队。
2. 当这条指令需要被冲刷时,通常是出现数据依赖性问题、预测错误、异常或错误的情况下,迫使系统强制性地移除该指令,以保证处理器能够恢复到一个稳定的状态。
出队执行的操作:
1. 将指令对应的 `allocated` 寄存器设置为低电平。这一操作的目的是标识该指令不再占用 LoadQueueRAR 的资源,从而为后续指令的入队和处理腾出空间。
2. 将entry对应的`free`掩码拉高,表示该条目已被释放并可供后续使用。
在load流水线的s3阶段可以向队列发送revoke信号撤销上一拍的请求。如果指令当前周期的revoke信号拉高revoke ==1并且在上一个周期已经入队需要执行撤销操作
1. 该entry对应的allocated寄存器清零
2. 该entry对应的free掩码拉高
</mrs-functions>
## 接口说明
| | name | I/O | width | Description |
| ---------------------- | ---------------------------------------- | ------------------- | ------------------ | ------------------------------------------------ |
| redirect | | | | |
| io.redirect.valid | input | 1 | 后端重定向的有效位 | |
| | io.redirect.bits.robIdx.flag | input | 1 | 后端重定向的flag用于在循环列表中判断先后 |
| | io.redirect.bits.robIdx.value | input | 8 | 后端重定向的位置value |
| | io.redirect.bits.level | | | |
| input | 1 | 后端重定向的level | | |
| 1b0冲刷之后的指令 | | | | |
| 1b1冲刷这条指令本身 | | | | |
| vecFeedback | io.vecFeedback_0/1.valid | input | 1 | 来自两条流水线的向量反馈信息有效位 |
| | io.vecFeedback_0/1.bits | input | 17 | 来自两条流水线的向量反馈信息 |
| query | io.query_0/1/2.req.ready | output | 1 | 能否接收3条数据通路中load违例检查请求 |
| | io.query_0/1/2.req.valid | input | 1 | 3条数据通路中load违例检查有效位 |
| | io.query_0/1/2.req.bits.uop.robIdx.flag | input | 1 | 3条数据通路中load违例检查uop在rob中的flag |
| | io.query_0/1/2.req.bits.uop.robIdx.value | input | 8 | 3条数据通路中load违例检查uop在rob中的value |
| | io.query_0/1/2.req.bits.uop.lqIdx.flag | input | 1 | 3条数据通路中load违例检查uop在LoadQueue中的flag |
| | io.query_0/1/2.req.bits.uop.lqIdx.value | input | 7 | 3条数据通路中load违例检查uop在LoadQueue中的value |
| | io.query_0/1/2.req.bits.paddr | input | 48 | 3条数据通路中load违例检查的物理地址 |
| | io.query_0/1/2.req.bits.data.valid | input | 1 | 3条数据通路中load违例检查data的有效 |
| | io.query_0/1/2.resp.valid | output | 1 | 3条数据通路中load违例检查响应的有效位 |
| | io.query_0/1/2.resp.bits.rep.frm.fetch | output | 1 | 3条数据通路中load违例检查的响应 |
| | io.query_0/1/2.revoke | input | 1 | 3条数据通路中load违例检查的撤销 |
| release | io.release.valid | input | 1 | Dcache释放块有效位 |
| | io.release.bits.paddr | input | 48 | Dcache释放块的物理地址 |
| ldwbptr | io.ldWbPtr.flag | input | 1 | VirtualLoadQueue中writeback的flag |
| | io.ldWbPtr.value | input | 7 | VirtualLoadQueue中writeback的位置value |
| Lqfull | io.lqFull | output | 1 | 表示loadqueue RAR满了 |
| performance | io.perf_0/1_value | output | 6 | 性能计数器 |

View File

@ -1,123 +0,0 @@
---
title: LoadQueueRAW
linkTitle: LoadQueueRAW
weight: 12
---
**本文档参考[香山LSQ设计文档](https://github.com/OpenXiangShan/XiangShan-Design-Doc/tree/master/docs/memblock/LSU/LSQ)写成**
本文档撰写的内容截至[ca892e73]
请注意,本文档撰写的测试点仅供参考,如能补充更多测试点,最终获得的奖励可能更高!
# LoadQueueRAW 简介
LoadQueueRAW是用于处理store-load违例的。由于load和store在流水线中都是乱序执行会经常出现load越过了更老的相同地址的store即这条load本应该前递store的数据但是由于store地址或者数据没有准备好导致这条load没有前递到store的数据就已经提交后续使用这条load结果的指令也都发生了错误于是产生store to load forwarding违例。
当store address通过STA保留站发射出来进入store流水线时会去查询LQRAW中在这条store后面的所有已经完成访存的相同地址的load以及load流水线中正在进行的在该条store之后的相同地址的load一旦发现有就发生了store to load forwarding违例可能有多个load发生了违例需要找到离store最近的load也就是最老的违例的load然后给RedirectGenerator部件发送重定向请求冲刷最老的违例的load及之后的所有指令。
当store流水线执行cbo zero指令时也需要进行store-load违例检查。
## st-ld违例
在现代处理器中Load 和 Store 指令通常采用乱序执行的方式进行处理。这种执行策略旨在提高处理器的并行性和整体性能。然而,由于 Load 和 Store 指令在流水线中的乱序执行,常常会出现 Load 指令越过更早的相同地址的 Store 指令的情况。这意味着Load 指令本应通过前递forwarding机制从 Store 指令获取数据,但由于 Store 指令的地址或数据尚未准备好,导致 Load 指令未能成功前递到 Store 的数据,而 Store 指令已被提交。由此,后续依赖于该 Load 指令结果的指令可能会出现错误,这就是 st-ld 违例。
考虑以下伪代码示例:
```
ST R1, 0(R2) ; 将 R1 的值存储到 R2 指向的内存地址
LD R3, 0(R2) ; 从 R2 指向的内存地址加载值到 R3
ADD R4, R3, R5 ; 使用 R3 的值进行计算
```
假设在这个过程中Store 指令由于某种原因(如缓存未命中)未能及时完成,而 Load 指令已经执行并读取了旧的数据(例如,从内存中读取到的值为 `0`。此时Load 指令并未获得 Store 指令更新后的值,导致后续计算的数据错误。
通过上述例子,可以清楚地看到 Store-to-Load 违例如何在乱序执行的环境中导致数据一致性问题。这种问题强调了在指令调度和执行过程中,确保正确的数据流动的重要性。现代处理器通过多种机制来检测和解决这种违例,以维护程序的正确性和稳定性。
## 整体框图
<div>
<center>
<img src="../LoadQueueRAW_structure.svg"
alt="LoadQueueRAW结构示意图"
style="zoom:100%"/>
<br>
图1LoadQueueRAW结构示意图<br><br>
</center>
</div>
LoadQueueRAW最多能够存储64条指令通过FreeList子模块管理空闲资源。FreeList 中存储的是 entry 的编号。当一条指令满足入队条件时FreeList 会为其分配一个 entry 编号,并将该指令存放在相应的 entry 中。指令出队时,需要释放所占用的 entry 资源,并将条目编号重新放回 FreeList 中以供后续指令使用。Load指令在s2阶段在 LoadQueueRAR 中查询 store-to-load 违例在s3阶段返回响应。
<mrs-functions>
## 模块功能说明
### 功能1发生st-ld违例的指令请求入队
当query到达load流水线的s2时判断是否满足入队条件如果在当前load指令之前有地址未准备好的store指令且当前指令没有被flush时当前load可以入队。具体流程如下
1. 判断入队条件:检查在当前 Load 指令之前是否存在未准备好的 Store 指令。如果存在这样的 Store 指令,并且当前 Load 指令尚未被冲刷flush则当前 Load 指令可以入队。
2. 分配 Entry 和 Index在 Freelist 中,系统将获得一个可分配的 Entry 及其对应的 Index以便为 Load 指令分配资源。
3. 保存物理地址:在 PaddrModule 中将入队的 Load 指令的物理地址保存到对应的 Entry。这一操作确保在后续访问中能够正确引用该地址。
4. 保存掩码信息:在 MaskModule 中,系统将入队的 Load 指令的掩码信息保存到对应的 Entry。掩码信息用于后续的地址匹配和数据访问。
5. 写入uop将 Load 指令的uop信息写入到相应的结构中以完成入队过程。
### 功能2检测st-ld违例条件
在 Store 指令到达 Store 流水线的 s1 阶段时,系统会进行 Store-to-Load 违例检查。此时Store 指令需要与 Load Queue 中已经完成访存的 Load 指令,以及在 Load 流水线 s1 和 s2 阶段正在访存的 Load 指令进行比较。这些 Load 指令可能尚未通过前递forwarding机制获取 Store 指令执行的结果。
具体的违例检查流程如下:
1. 物理地址匹配:在第一拍中,系统将进行物理地址匹配,并检查条件。此时,将匹配在当前 Store 指令之后的所有新的 Load 指令。如果这些 Load 指令已经成功获取了数据(`datavalid`),或者由于缓存未命中正在等待数据回填(`dcache miss`),则可以确定这些 Load 指令不会将数据前递给当前的 Store 指令。
2. 匹配 Load 指令在第二拍中Store 流水线中的 Store 指令根据匹配结果中的掩码mask在 Load Queue 的 RAWRead After Write结构中查找所有匹配的 Load 指令。Load Queue 中共有 32 项这些项将被平分为4组。每组从 8 项中选出一个最老的 Load 指令,最多可得到 4 个候选最老的 Load 指令。
3. 选择最老的 Load在第三拍中从上述 4 个候选最老的 Load 指令中,系统将选出一个最老的 Load 指令,作为最终的目标。
4. 处理违例情况:在第四拍中,如果在两条 Store 流水线中均发生了 Store-to-Load 违例,系统将从各自的 Queue 中匹配到的最老 Load 指令中选出一个更老的 Load 指令以产生回滚请求并发送给重定向模块Redirect。此时违例的条件包括
- Load 和 Store 的地址相同。
- Load 指令比 Store 指令年轻。
- Load 指令已经成功获取了数据。
### 功能3指令的出队
Load指令的出队需要满足以下条件其中之一
1. 当比队列entry中存放的指令更老的指令已经全部写回到ROB时该指令可以出队。
2. 当这条指令需要被冲刷时,通常是出现数据依赖性问题、预测错误、异常或错误的情况下,迫使系统强制性地移除该指令,以保证处理器能够恢复到一个稳定的状态。
出队执行的操作:
1. 将指令对应的 `allocated` 寄存器设置为低电平。这一操作的目的是标识该指令不再占用 LoadQueueRAR 的资源,从而为后续指令的入队和处理腾出空间。
2. 将entry对应的`free`掩码拉高,表示该条目已被释放并可供后续使用。
在load流水线的s3阶段可以向队列发送revoke信号撤销上一拍的请求。如果指令当前周期的revoke信号拉高revoke ==1并且在上一个周期已经入队需要执行撤销操作
1. 该entry对应的allocated寄存器清零
2. 该entry对应的free掩码拉高
</mrs-functions>
## 接口说明
| | name | I/O | width | Description |
| ---------------- | ---------------------------------------- | ------ | ----- | ------------------------------------------------------------ |
| redirect | io.redirect.valid | input | 1 | 后端重定向的有效位 |
| | io.redirect.bits.robIdx.flag | input | 1 | 后端重定向的flag用于在循环列表中判断先后 |
| | io.redirect.bits.robIdx.value | input | 8 | 后端重定向的位置value |
| | io.redirect.bits.level | input | 1 | 后端重定向的level1b0冲刷之后的指令1b1冲刷这条指令本身 |
| vecFeedback | io.vecFeedback_0/1.valid | input | 1 | 来自两条流水线的向量反馈信息有效位 |
| | io.vecFeedback_0/1.bits | input | 17 | 来自两条流水线的向量反馈信息 |
| query | io.query_0/1/2.req.ready | output | 1 | 能否接收3条数据通路中load违例检查请求 |
| | io.query_0/1/2.req.valid | input | 1 | 3条数据通路中load违例检查有效位 |
| | io.query_0/1/2.req.bits.uop.robIdx.flag | input | 1 | 3条数据通路中load违例检查uop在rob中的flag |
| | io.query_0/1/2.req.bits.uop.robIdx.value | input | 8 | 3条数据通路中load违例检查uop在rob中的value |
| | io.query_0/1/2.req.bits.uop.lqIdx.flag | input | 1 | 3条数据通路中load违例检查uop在LoadQueue中的flag |
| | io.query_0/1/2.req.bits.uop.lqIdx.value | input | 7 | 3条数据通路中load违例检查uop在LoadQueue中的value |
| | io.query_0/1/2.req.bits.paddr | input | 48 | 3条数据通路中load违例检查的物理地址 |
| | io.query_0/1/2.req.bits.data.valid | input | 1 | 3条数据通路中load违例检查data的有效 |
| | io.query_0/1/2.revoke | input | 1 | 3条数据通路中load违例检查的撤销 |
| storeIn | storeIn_0/1.bits | input | 84 | 两条store流水线store指令相关信息 |
| | storeIn_0/1.valid | input | 1 | 两条store流水线store指令相关信息有效位 |
| rollback | rollback_0/1.valid | output | 1 | 两条store流水线回滚信息的有效性 |
| | rollback_0/1.bits | output | 31 | 两条store流水线回滚信息 |
| stAddrReadySqPtr | stAddrReadySqPtr | input | 7 | 指向 store 队列中已准备好的地址条目 |
| stIssuePtr | stIssuePtr | input | 7 | 指向 store 队列中准备发射执行的指令条目 |
| lqFull | lqFull | output | 1 | 判断队列是否满 |

View File

@ -1,176 +0,0 @@
---
title: LoadQueueReplay
linkTitle: LoadQueueReplay
weight: 12
---
**本文档参考[香山LSQ设计文档](https://github.com/OpenXiangShan/XiangShan-Design-Doc/tree/master/docs/memblock/LSU/LSQ)写成**
本文档撰写的内容截至[ca892e73]
请注意,本文档撰写的测试点仅供参考,如能补充更多测试点,最终获得的奖励可能更高!
# LoadQueueReplay 简介
LoadQueueReplay 模块是现代处理器架构中用于处理 Load 指令重发的重要组成部分。它负责管理因各种原因而需要重发的 Load 指令,确保指令执行的正确性和高效性。
## 整体框图
<div>
<center>
<img src="../LoadQueueReplay_structure.png"
alt="LoadQueueReplay结构示意图"
style="zoom:100%"/>
<br>
图1LoadQueueReplay结构示意图<br><br>
</center>
</div>
LoadQueueReplay 最多存放72条指令涉及多个状态和存储的信息。其关键组成部分如下
- **Allocated**
- 表示某个 Load 重发队列项是否已经被分配,反映该项的有效性。
- **Scheduled**
- 指示某个 Load 重发队列项是否已被调度,意味着该项已经被选出,并将被发送至 Load Unit 进行重发。
- **Uop**
- 该队列项对应的 Load 指令执行信息包括微操作uop
- **Cause**:指示该 Load 指令重发的原因,主要包括以下几种情况:
- **C_MA**:存储-加载st-ld违反重新执行。
- **C_TM**TLB翻译后备页表缺失。
- **C_FF**:存储-加载转发。
- **C_DR**数据缓存dcache需要重发。
- **C_DM**:数据缓存缺失。
- **C_WF**:路径预测失败。
- **C_BC**:数据缓存路径冲突。
- **C_RAR**:读取-读取RAR队列无法接收。
- **C_RAW**:读取-写入RAW队列无法接收。
- **C_NK**:存储-加载违反。
- **Blocking**:指示该 Load 指令因等待条件而被阻塞,不能被调度重发。阻塞的原因和解除阻塞的条件包括:
- **C_MA**:存储指令的地址就绪。
- **C_TM**TLB 回填完毕,并发送 Hint 信号。
- **C_FF**:存储指令的数据就绪。
- **C_DM**:数据缓存回填完毕。
- **C_RAR**RAR 队列未满,且 Load 指令比 Load Queue 的写回项更老。
- **C_RAW**RAW 队列未满,且 Load 指令比 Store Queue 中所有地址准备好的项都更老。
LoadReplayQueue 通过 FreeList 管理队列的空闲状态。FreeList 的大小等于 LoadReplayQueue 的项数分配宽度为3Load Unit 的数量),释放宽度为 4。同时Free List 可以反馈 Load Replay Queue 的空余项数量以及是否已满的信息。除了FreeListLoadQueueReplay还包含两个子模块AgeDetector 和 LqVAddrModule其中 AgeDetector 用于寻找一系列load replay queue项中最早入队的一项。
例如昆明湖V1的Load宽度为2则会将load replay queue分为两半从偶数项和奇数项中分别挑选一项最老的进行重发。LqVAddrModule 用于保存load replay queue项数个虚拟地址读口和写口的数量均为Load的宽度LoadUnit的数量
## LoadQueueReplay 存储信息
| Field | Description |
|-----------------------|-------------|
| allocated | 是否已经被分配,也代表是否该项是否有效。 |
| scheduled | 是否已经被调度代表该项已经被选出已经或即将被发送至LoadUnit进行重发。 |
| uop | load指令执行包括的uop信息。 |
| vecReplay | 向量load指令相关信息。 |
| vaddrModule | Load指令的虚拟地址。 |
| cause | 某load replay queue项对应load指令重发的原因包括<br>- C_MA(位0): store-load预测违例<br>- C_TM(位1): tlb miss<br>- C_FF(位2): store-to-load-forwarding store数据为准备好导致失败<br>- C_DR(位3): 出现DCache miss但是无法分配MSHR<br>- C_DM(位4): 出现DCache miss<br>- C_WF(位5): 路预测器预测错误<br>- C_BC(位6): Bank冲突<br>- C_RAR(位7): LoadQueueRAR没有空间接受指令<br>- C_RAR(位8): LoadQueueRAW没有空间接受指令<br>- C_NK(位9): LoadUnit监测到store-to-load-forwarding违例<br>- C_MF(位10): LoadMisalignBuffer没用空间接受指令 |
| blocking | Load指令正在被阻塞。 |
| strict | 访存依赖预测器判断指令是否需要等待它之前的所有store指令执行完毕进入调度阶段。 |
| blockSqIdx | 与load指令有相关性的store指令的StoreQueue Index。 |
| missMSHRId | load指令的dcache miss请求接受ID。 |
| tlbHintId | load指令的tlb miss请求接受ID。 |
| replacementUpdated | DCache的替换算法是否已经更新。 |
| replayCarry | DCache的路预测器预测信息。 |
| missDbUpdated | ChiselDB中Miss相关情况更新。 |
| dataInLastBeatReg | Load指令需要的数据在两笔回填请求的最后一笔。 |
# 功能简介
<mrs-functions>
## 模块功能说明
### 功能1需要重发的指令请求入队
根据是否满足以下条件以及freelist是否可以分配的空闲槽位决定能否直接入队
1. `enq_X.valid` 信号有效。
2. 即将入队的项不需要重定向。
3. 该项被标记为需要重发(`enq.bits.rep_info.need_rep`)。
4. 没有异常。在入队时,必须确保没有异常发生。如果当前指令处于异常状态,入队操作应被禁止,以防止无效指令的执行。
### 功能2指令重发解锁
LoadQueueReplay 中的指令出队分三拍:
在重发过程中根据重发的原因和当前条件解锁相应项。在不满足解锁条件时将会被阻塞无法参与重发仲裁。其中C_BCDcache 块冲突、C_NKoad_unit 在 S1、S2 阶段发生 store-load 违例、C_DRDcache miss 且 MSHR 满、C_WF路径预测失败**无需条件即可立即重发**。
其他重发原因和对应的解锁条件如下:
1. C_MAstore_load 预测违例已经被分配入队并且store准备好了相应的地址具体如下
1. store unit 的地址信号有效,且 `sqIdx` 与被阻塞的 `Idx` 相同store 地址未发生 TLB miss。
2. 被阻塞的 `SeqIdx` 在 store_queue 发送的 `stAddrReadySqPtr` 之前。
3. 非严格阻塞,且阻塞的 `SeqIdx``stAddrReadyVec` 的向量组内。
4. store queue 为空,无未处理项。
2. C_TMTLB Missresp信号有效并且输入的id号等于Tlb Hint的id号或者replay_all信号有效。
3. C_FFstore_load 数据前递失败):因为数据前递失败导致指令重发的释放条件有下面四条:
1. store unit 的数据信号有效,且 `sqIdx` 与被阻塞的 `Idx` 相同。
2. 被阻塞的 `SqIdx` 在 store_queue 发送的 `stDataReadySqPtr` 之前。
3. 阻塞的 `SeqIdx``stDataReadyVec` 的向量组内。
4. store queue 为空,无未处理项。
4. C_DMDcache MissDcache 的信号有效,且 `tl_d_channel.mshrid` 与阻塞的 `missMSHRId` 相同。
5. C_RARRAR queue 没有回应RAR 未满,或 `lqIdx``ldWbPtr` 之前。
6. C_RAWRAW 没有回应RAW 未满,或 `lqIdx``stAddrReadySqPtr` 之前。
### 功能3指令重发优先级
LoadQueueReplay有3种选择调度方式
1. 根据入队年龄
LoadQueueReplay使用3个年龄矩阵(每一个Bank对应一个年龄矩阵),来记录入队的时间。年龄矩阵会从已经准备好可以重发的指令中,选择一个入队时间最长的指令调度重发。
2. 根据Load指令的年龄
LoadQueuReplay可以根据LqPtr判断靠近最老的load指令重发判断宽度为OldestSelectStride=4。
3. DCache数据相关的load指令优先调度
- LoadQueueReply首先调度因L2 Hint调度的重发当dcache miss后需要继续查询下级缓存L2 Cache。在L2 Cache回填前的2或3拍L2 Cache会提前给LoadQueueReplay唤醒信号称为L2 Hint当收到L2 Hint后LoadQueueReplay可以更早地唤醒这条因dcache miss而阻塞的Load指令进行重发。
- 如果不存在L2 Hint情况会将其余Load Replay的原因分为高优先级和低优先级。高优先级包括因dcache缺失或st-ld forward导致的重发而将其他原因归纳为低优先级。如果能够从LoadQueueReplay中找出一条满足重发条件的Load指令有效、未被调度、且不被阻塞等待唤醒则选择该Load指令重发否则按照入队顺序通过AgeDetector模块寻找一系列load replay queue项中最早入队的一项进行重发。
### 功能4指令重发逻辑
1. Load_unit s3过来的请求根据enq.bits.isLoadReplay判断是否是已经从replay_queue出队的序列如果是已经出队的序列根据是否needReplay和有异常做下一步的判断如果有异常或者不需要重发则释放这个槽位并从agedetector里面把该项出队如果需要重发则将这个项对应的scheduled位置为false来参与后续的出队仲裁竞争。
2. 从freelist中选出发给load unit的有效项项数为load unit的宽度即有几条load unit的流水线根据优先级来进行出队。
3. 第0拍将数据传递给第1拍由s0_can_go控制当s0_can_go为1时才能将0拍得到的数据发给第一拍s0_can_go有效的条件是s0被重定向或者s1_can_go为1。
4. 第一拍从vaddr内部取出需要的虚拟地址发给下一拍流水线。 ColdCouter的值在0到12之间上一拍没有被阻塞并且整个过程没有发生重定向的时候向load unit发送请求。
5. 发送给下一拍流水线的数据受s1_can_go控制s1_can_go为1的条件是:
- ColdCouter的值在0到12之间 且 上一拍完成操作(未被阻塞)或者不需要发送数据两者之一。
- 发生数据的重定向。
6. 第二拍将收到第一拍的数据发送给对应的load unit,获取仲裁权限,完成重发指令的任务。
</mrs-functions>
## 接口说明
| name | I/O | description |
| ----------------------- | ------ | ------------------------------------------------------------ |
| redirect | input | 后端重定向相关信息 |
| vecFeedback | input | 来自两条流水线的向量反馈信息 |
| enq | input | 表示外部模块希望将 load 指令传递给当前模块,来自 load 指令流水线的 s3 级 |
| storeAddrIn | input | 在一个时钟周期内接收多条 store 指令的地址信息,用于判断指令存储的地址是否已经准备好 |
| storeDataIn | input | 在一个时钟周期内接收多条 store 指令的数据信息,用于判断指令存储的数据是否已准备好 |
| replay | output | 用于处理 load 指令的重发请求,每个元素对应一个重发接口 |
| tl_d_channel | input | 用于接收来自数据缓存Dcache的信息在处理 load 指令时会使用该端口进行数据转发 |
| stAddrReadySqPtr | input | 指向当前准备好地址的 store 指令 |
| stAddrReadyVec | input | 向量中对应 store 指令的地址是否已经准备好 |
| stDataReadySqPtr | input | 指向当前准备好数据的 store 指令 |
| stDataReadyVec | input | 向量中对应 store 指令的数据是否已经准备好 |
| sqEmpty | input | 当前 store 队列是否为空 |
| lqFull | output | 当前 load 队列是否已满 |
| ldWbPtr | input | 指向当前写回的load指令 |
| rarFull | input | rar 队列是否已满 |
| rawFull | input | raw 队列是否已满 |
| l2_hint | input | 当 dcache miss 后,需要继续查询下级缓存 L2 Cache。在 L2 Cache 回填前的 2 或 3 拍L2 Cache 会提前给 LoadQueueReplay 唤醒信号,称为 L2 Hint |
| tlb_hint | input | 作用类似于 l2_hint接收当前的 TLB 提示信息 |
| tlbReplayDelayCycleCtrl | input | 控制 TLB 重发的延迟周期 |

View File

@ -1,180 +0,0 @@
---
title: LoadQueueUncache
linkTitle: LoadQueueUncache
weight: 12
---
**本文档参考[香山LSQ设计文档](https://github.com/OpenXiangShan/XiangShan-Design-Doc/tree/master/docs/memblock/LSU/LSQ)写成**
本文档撰写的内容截至[66e9b546]
请注意,本文档撰写的测试点仅供参考,如能补充更多测试点,最终获得的奖励可能更高!
# LoadQueueUncache 简介
LoadQueueUncache 和 Uncache 模块,对于 uncache load 访问请求来说,起到一个从 LoadUnit 流水线到总线访问的中间站作用。其中 Uncache 模块,作为靠近总线的一方,主要用于处理 uncache 访问到总线的请求和响应。LoadQueueUncache 作为靠近流水线的一方,需要承担以下责任:
1. 接收 LoadUnit 流水线传过来的 uncache load 请求。
2. 选择已准备好 uncache 访问的 uncache load 请求 发送到 Uncache Buffer。
3. 接收来自 Uncache Buffer 的处理完的 uncache load 请求。
4. 将处理完的 uncache load 请求 返回给 LoadUnit。
LoadQueueUncache 结构上,目前有 4 项项数可配UncacheEntry每一项独立负责一个请求并利用一组状态寄存器控制其具体处理流程有一个 FreeList管理各项分配和回收的情况。而 LoadQueueUncache 主要是协同 4 项的新项分配、请求选择、响应分派、出队等统筹逻辑。
## 整体框图
<div>
<center>
<img src="../LoadQueueUncache_structure.svg"
alt="LoadQueueUncache结构示意图"
style="zoom:100%"/>
<br>
图1LoadQueueUncache结构示意图
</center>
</div>
UnCacheBuffer 最多存放4条指令除了 FreeList 之外,另一个重要的子模块是 UncacheEntry管理每个Uncahce请求负责发起Uncache写回Uncache数据。每个Entry内维护一个用于发起Uncache请求的状态机状态机的状态转换图如下
<div>
<center>
<img src="../UncacheEntry.png"
alt="UncacheEntry结构示意图"
style="zoom:100%"/>
<br>
图2UncacheEntry状态转换图
</center>
</div>
- s_idl:该项还未发起一个MMIO请求。
- s_req:向uncache模块发起MMIO请求等待请求被接收。
- s_resp:等待uncache模块的MMIO响应。
- s_wait:等待将MMIO结果写回流水线。
# 功能简介
<mrs-functions>
## 模块功能说明
### 功能1Uncache指令请求入队
LoadQueueUncache 负责接收来自 LoadUnit 0、1、2 三个模块的请求,这些请求可以是 MMIO 请求,也可以是 NC 请求。
1. 首先,系统会根据请求的 robIdx 按照时间顺序从最老到最新对请求进行排序以确保最早的请求能优先分配到空闲项避免特殊情况下因老项回滚rollback而导致死锁。
2. 进入入队处理的条件是:请求没有重发、没有异常,并且系统会根据 FreeList 中可分配的空闲项依次为请求分配项。
3. 当 LoadQueueUncache 达到容量上限,且仍有请求未分配到项时,系统会从这些未分配的请求中选择最早的请求进行 rollback。
UncacheBuffer 的入队分为 s1 和 s2 两个阶段:
s1
- **请求收集**:通过 `io.req.map(_.bits)` 收集所有请求的内容,形成 `s1_req` 向量。
- **有效性标记**:通过 `io.req.map(_.valid)` 收集所有请求的有效性,形成 `s1_valid` 向量。
s2
执行入队操作,主要分为以下几步:
- 使用 `RegEnable`**s1** 阶段的请求 `s1_req` 注册到 `s2_req`,确保在请求有效时保持其状态。
- 通过以下条件生成`s2_valid`向量,判断每个请求是否有效:
1. `RegNext(s1_valid(i))`:确保请求在 **s1** 阶段有效。
2. `!s2_req(i).uop.robIdx.needFlush(RegNext(io.redirect))`:确保请求的 ROB 索引不需要因重定向而被刷新。
3. `!s2_req(i).uop.robIdx.needFlush(io.redirect)`:确保请求的 ROB 索引不需要因当前重定向而被刷新。
- 检查每个请求是否需要重发,结果存储在 `s2_need_replay` 向量中。
- 在 **s2** 阶段,使用 `s2_enqueue` 向量来决定哪些请求成功入队。入队条件包括:
- `s2_valid(w)`:请求在 **s2** 阶段有效。
- `!s2_has_exception(w)`:请求没有异常。
- `!s2_need_replay(w)`:请求不需要重发。
- `s2_req(w).mmio`:请求是一个内存映射 IOMMIO请求。
- 通过 `enqValidVec``enqIndexVec` 的有效管理确保每个加载请求在满足有效性和可分配条件时能够正确地申请和分配FreeList槽位。
### 功能2Uncache指令的出队
1. 当一个项完成 Uncache 访问操作并返回给 LoadUnit ,或被 redirect 刷新时,则该项出队并释放 FreeList 中该项的标志。
具体流程如下:
- 计算`freeMaskVec`掩码,用于标记每个槽位的释放状态,指示相应槽位是否可用。
- 如果当前条目被选择 (`e.io.select`) 且其输出信号有效 (`e.io.ldout.fire`),则对应槽位的释放状态被标记为 `true`,表示该槽位可用。
- 如果接收到刷新信号 (`e.io.flush`),同样将对应槽位的释放状态设置为 `true`
2. 同一拍可能有多个项出队。返回给 LoadUnit 的请求,会在第一拍中选出,第二拍返回。
3. 其中,可供处理 uncache 返回请求的 LoadUnit 端口是预先设定的。当前MMIO 只返回到 LoadUnit 2NC 可返回到 LoadUnit 1\2。在多个端口返回的情况下利用 uncache entry id 与端口数的余数,来指定每个项可以返回到的 LoadUnit 端口,并从该端口的候选项中选择一个项进行返回。
### 功能3Uncache交互逻辑
1. 发送 req
第一拍先从当前已准备好 uncache 访问中选择一个,第二拍将其发送给 Uncache Buffer。发送的请求中会标记选中项的 id称为 mid 。其中是否被成功接收,可根据 req.ready 判断。
2. 接收 idResp
如果发送的请求被 Uncache Buffer 接收,那么会在接收的下一拍收到 Uncache 的 idResp。该响应中包含 mid 和 Uncache Buffer 为该请求分配 entry id称为 sid。LoadQueueUncache 利用 mid 找到内部对应的项,并将 sid 存储在该项中。
3. 接收 resp
待 Uncache Buffer 完成该请求的总线访问后,会将访问结果返回给 LoadQueueUncache。该响应中包含 sid。考虑到 Uncache Buffer 的合并特性(详细入队合并逻辑见 Uncache一个 sid 可能对应 LoadQueueUncache 的多个项。LoadQueueUncache 利用 sid 找到内部所有相关项,并将访问结果传递给这些项。
### 功能4Uncache回滚检测
freelist 没有空闲表现导致 MMIO Load 进入 UncacheBuffer 失败时需要进行 rollback
时需要根据 robidx 选择不能入队的 MMIO 中最老的指令进行 rollback。整个流程分为以下几个周期
- Cycle 0进行 uncache 请求入队。
- Cycle 1选择最旧的 uncache 加载请求。
- Cycle 2发出重定向请求。
- 从 load 流水线中选择最旧的 load 请求。
- 根据检测到的拒绝情况准备重定向请求。
- 如果重定向请求有效,则发出请求。
使用 `selectOldestRedirect` 函数来选择最旧的重定向请求,具体步骤如下:
- 比较向量生成:
- 创建一个比较向量 `compareVec`,用于判断请求的顺序,比较每个请求的 ROB 索引。
- 生成独热编码结果:
- `resultOnehot` 向量根据有效性和比较结果生成,标记出最旧的可重定向请求。
</mrs-functions>
## 接口说明
| name | I/O | description |
| ----------- | ------ | ------------------------------------------------------------ |
| redirect | input | 后端重定向相关信息 |
| req | input | 接收写入请求 |
| ldout | output | 写回 MMIO 数据接口,输出 MemExuOutput 类型的数据,处理与 MMIO 的写回操作 |
| ld_raw_data | output | 读取原始数据输出接口 |
| rob | input | 接收来自 ROB 的信号或数据 |
| uncache | output | 发送数据或信号给 uncache 模块 |
| rollback | output | 当 uncache 缓存满时,从前端进行回滚 |

View File

@ -1,120 +0,0 @@
---
title: VirtualLoadQueue
linkTitle: VirtualLoadQueue
weight: 12
---
**本文档参考[香山LSQ设计文档](https://github.com/OpenXiangShan/XiangShan-Design-Doc/tree/master/docs/memblock/LSU/LSQ)写成**
本文档撰写的内容截至[ca892e73]
请注意,本文档撰写的测试点仅供参考,如能补充更多测试点,最终获得的奖励可能更高!
# VirtualLoadQueue 简介
Virtualloadqueue是一个队列用于存储所有load指令的微操作(MicroOp)并维护这些load指令之间的顺序它的功能类似于重排序缓冲区Reorder Buffer, ROB但专注于load指令的管理。其主要功能是跟踪Load指令执行状态以确保在并发执行的环境中加载操作能够正确、有序地完成。
## 整体框图
<div>
<center>
<img src="../VirtualLoadQueue_structure.png"
alt="VirtualLoadQueue结构示意图"
style="zoom:100%"/>
<br>
图1VirtualLoadQueue结构示意图
</center>
</div>
Virtualloadqueue最多可以存放72条指令dispatch阶段最多支持6条指令同时入队最多支持8条指令出队。Virtualloadqueue对于每一个 entry 中的 load 指令都有若干状态位来标识这个 load 处于什么状态:
**allocated**该项是否分配了load用于确定load指令的生命周期。
**isvec**该指令是否是向量load指令。
**committed**: 该项是否提交。
# 功能简介
<mrs-functions>
## 模块功能说明
### 功能1load指令请求入队
在调度阶段保留站通过入队enq总线向VirtualLoadQueue发起入队请求最多支持六组并发请求。成功入队的条件包括以下几点
1. StoreQueue 和 LoadQueue 有预留空间确保LoadQueue有足够的容量来接收新的加载指令以避免队列溢出。确保StoreQueu有预留空间则是基于数据一致性和避免指令阻塞的考虑因为store指令入队阻塞可能会导致load指令无法正确读取或forward到数据。
2. 入队请求有效:入队请求必须是合法的,确保指令在调度过程中可以被正确处理。
3. 指令未被冲刷:确保指令在入队时没有被系统标记为无效或被撤销。
成功入队之后,系统会执行以下操作:
1. 将指令的lqidx作为索引将对应的allocated寄存器置1bits信息写入uop寄存器。
2. 计算新的lqidx值作为enq_resp传送给保留站。
### 功能2接收load流水线写回的数据
在 load 流水线的s3阶段load unit会将指令执行的信息通过总线 ldin 写回到 VirtualLoadQueue。具体写回信息包括
1. 是否发生了异常以及异常类型
2. dcache是否命中
3. tlb是否命中
4. 是否为mmio指令
5. 是否为软件预取或者硬件预取
6. 是否需要重发以及重发的原因
7. 写uop的使能信号
写回需要满足的条件如下:
1. ldin 总线的 valid 信号需要拉高,表明当前正在进行有效的数据传输。
2. 指令不应需要重发(即 `need_rep` 信号为 0否则将影响写回的正常进行。
在满足写回条件后,系统将生成相应的写回响应,具体包括以下几个方面:
1. 如果在执行过程中发生了异常、TLB命中或软件预取操作`addrvalid` 信号将被置为 1表示地址信息有效。
2. 如果在执行过程中发生了异常、MMIO操作、DCACHE命中并且不需要重发或是软件预取操作`datavalid` 信号将被置为 1表示数据有效。
3. 指令在流水线的 S3 阶段有效(注意:不能是硬件预取指令)。当 `ldin` 总线的写使能信号 `data_wen_dup` 拉高时将更新队列中的uop信息以确保指令的状态及时反映。
系统将`addrvalid`和`datavalid`分开进行处理是考虑到在一些情况下地址可以被重用而数据可能需要重新请求如dcache miss/mmio/软件预取等)。分开标识可以减少流水线停顿,允许处理器在地址有效时继续执行其他指令,而不必等待数据有效性确认,从而优化整体性能。
### 功能3load指令的出队(提交)
1. 出队时机当被分配的entriesallocated为高到达队头同时allocated与committed都为1时表示可以出队如果是向量load需要每个元素都committed。
</mrs-functions>
## 接口说明
| | name | I/O | width | description |
| ----------- | -------------------------------------------------- | ------ | ----- | ------------------------------------------------------------ |
| redirect | io.redirect.valid | input | 1 | 后端重定向有效位 |
| | io.redirect.bits.robIdx.flag | input | 1 | 后端重定向相关信息 |
| | io.redirect.bits.robIdx.value | input | 8 | 后端重定向相关信息 |
| | io.redirect.bits.level | input | 1 | 后端重定向相关信息 |
| enq | io.enq.canAccept | output | 1 | Lq能否接收派遣指令 |
| | io.enq.sqcanAccept | input | 1 | sq能否接收派遣至零 |
| | io.enq.needAlloc_0~5 | input | 1 | |
| | io.enq.req_0~5.valid | input | 1 | 入队请求的有效信号 |
| | io.enq.req_0~5.bits.robIdx.flag | input | 1 | 入队请求ROB指针的flag |
| | io.enq.req_0~5.bits.robIdx.value | input | 8 | 入队请求ROB指针的value |
| | io.enq.req_0~5.bits.lqIdx.value | input | 7 | 入队请求lqidx的value |
| | io.enq.req_0~5.bits.numLsElem | input | 5 | 1. 向量寄存器的总位宽为128位每个向量元素的大小为8位因此每个向量寄存器可以存储16个numLsElem表示向量寄存器中元素的个数因此位宽为5。 2. 如果是标量值零numLsElem的值恒为5b1 3. 如果是向量指令每个端口的numLsElem的最大值为[16 2 2 2 2 2] |
| ldin | io.ldin_0/1/2.valid | input | 1 | load写回到loadqueue的信息有效 |
| | io.ldin_0/1/2.bits.uop.cf.exceptionVec_3/4/5/13/21 | input | 1 | Load写回到流水线的指令发生异常 |
| | io.ldin_0/1/2.bits.uop.robIdx_flag | input | 1 | load写回lq指令的rob指针的flag |
| | io.ldin_0/1/2.bits.uop.robIdx_value | input | 8 | load写回lq指令的rob指针的value |
| | io.ldin_0/1/2.bits.uop.lqIdx.value | input | 7 | load写回lq指令的lq指针的value |
| | io.ldin_0/1/2.bits.miss | input | 1 | Load写回到Lq的指令发生cacheMiss |
| | io.ldin_0/1.bits.tlbMiss | input | 1 | Load写回到Lq的指令发生tlbMiss |
| | io.ldin_0/1/2.bits.mmio | input | 1 | Load写回到Lq的指令是MMIO指令 |
| | io.ldin_0/1/2.bits.isPrefetch | input | 1 | 指令为预取操作,预取分为软件预取和硬件预取 |
| | io.ldin_0/1/2.bits.isHWPrefetch | input | 1 | 指令为硬件预取 |
| | io.ldin_0/1/2.bits.dcacheRequireReplay | input | 1 | Load写回到Lq的指令需要replay |
| | io.ldin_0/1/2.bits.rep.info.cause_0~9 | input | 1 | Load写回到Lq的指令需要replay的原因 =0st-ld violention predirect =1tlb miss =2st-ld forward =3dcache replay =4dcache miss =5wpu predict fail =6dcache bank conflict =7RAR queue nack =8RAW queue nack =9st-ld violention |
| | io.ldin_0/1/2.bits.data_wen_dup_1 | input | 1 | uop信息的写入使能信号 |
| ldWbPtr | io.ldWbPtr.flag | output | 1 | writeback指针的flag |
| | io.ldWbPtr.value | output | 7 | writeback指针的value |
| lqEmpty | io.lqEmpty | output | 1 | Lq是否空 |
| lqDeq | io.lqDeq | output | 3 | 出队表项数量 |
| lqCancelCnt | io.lqCancelCnt | output | 7 | 后端发生重定向时取消的load数量 |

View File

@ -1,314 +0,0 @@
---
title: StoreQueue
linkTitle: StoreQueue
weight: 12
---
**本文档参考[香山LSQ设计文档](https://github.com/OpenXiangShan/XiangShan-Design-Doc/tree/master/docs/memblock/LSU/LSQ)写成**
本文档撰写的内容截至[ca892e73]
请注意,本文档撰写的测试点仅供参考,如能补充更多测试点,最终获得的奖励可能更高!
# StoreQueue 简介
StoreQueue是一个队列用来装所有的 store 指令,功能如下:
- 在跟踪 store 指令的执行状态
- 存储 store 的数据,跟踪数据的状态(是否到达)
- 为load提供查询接口让load可以forward相同地址的store
- 负责 MMIO store和NonCacheable store的执行
- 将被 ROB 提交的 store 写到 sbuffer 中
- 维护地址和数据就绪指针用于LoadQueueRAW的释放和LoadQueueReplay的唤醒
store进行了地址与数据分离发射的优化即 StoreUnit 是 store 的地址发射出来走的流水线StdExeUnit 是 store 的数据发射出来走的流水线是两个不同的保留站store 的数据就绪了就可以发射到 StdExeUnitstore 的地址就绪了就可以发射到 StoreUnit。
## 整体框图
<div>
<center>
<img src="../StoreQueue_structure.svg"
alt="StoreQueue结构示意图"
style="zoom:100%"/>
<br>
图1StoreQueue结构示意图<br><br>
</center>
</div>
StoreQueue最多可以存放64条指令**store queue 中重要的状态位有:**
- allocatedRS在storeQueue队列有空闲时会设置这个entry的allocated状态开始记录这条store 的生命周期。同时发射到StoreUnit/ StdExeUnit 2条流水。当这条store指令被提交到Sbuffer时allocated状态被清除。
- addrvalid在StoreUnit的S1更新表示是否已经经过了地址转换得到物理地址用于 load forward 检查时的 cam 比较。
- datavalid在StdExeUnit 的S1更新表示store 的数据是否已经被发射出来,是否已经可用
- committed在store 是否已经被 ROB commit 了
- pending在StoreUnit的S2更新在这条 store 是否是 MMIO 空间的 store主要是用于控制 MMIO 的状态机
- mmio在StoreUnit的S2更新这条 store 是否是 MMIO 空间的 store主要是用于控制对 sbuffer 的写
## 非对齐store指令
StoreQueue支持处理非对齐的Store指令每一个非对齐的Store指令占用一项并在写入dataBuffer对地址和数据对齐后写入。
## 向量store指令
如图2所示StoreQueue会给向量store指令预分配一些项。
StoreQueue通过vecMbCommit控制向量store的提交
1. 针对每个 store从反馈向量 fbk 中获取相应的信息。
2. 判断该 store 是否符合提交条件valid 且标记为 commit 或 flush并且检查该 store 是否与 uop(i) 对应的指令匹配(通过 robIdx 和 uopIdx。只有当满足所有条件时才会将该 store 标记为提交。判断VecStorePipelineWidth内是否有指令满足条件满足则判断该向量store提交否则不提交。
3. 特殊情况处理(跨页 store 指令):
在特殊情况下(当 store 跨页且 storeMisalignBuffer 中有相同的 uop如果该 store 符合条件`io.maControl.toStoreQueue.withSameUop`,会强制将 vecMbCommit设置为 true表示该 store 无论如何都已提交。
<div>
<center>
<img src="../StoreQueue_Vector.svg"
alt="向量store指令示意图"
style="zoom:100%"/>
<br>
图2向量store指令<br><br>
</center>
</div>
# 功能简介
<mrs-functions>
## 模块功能说明
### 功能1store指令请求入队
1. StoreQueue 每次最多会有 2 个 entry 入队,通过入队指针 enqPtrExt 控制。在 dispatch 阶段最多可以分配2个 entry指针每次右移 1 位或 2 位。
2. 通过比较入队指针 enqPtrExt 和出队指针 deqPtrExt 得出已经在队列中有效 entry。只有空闲的 entry 大于需要请求入队的指令时才会分配 entry 入队。
3. 入队时设置 entry 的状态位 allocated 为 true其他状态位都为 false。
### 功能2指令的出队
1. StoreQueue 每次最多会有2个 entry 出队释放,通过输出指针 deqPtrExt 控制,每次指针右移一位或 2 位。
2. STQ 出队的触发信号是isbuffer(i).fire延后一拍的信号因为 sbuffer 的写动作要用 2 拍完成,在 sbuffer 写完成之前 entry 不释放可以继续 forward 数据。
### 功能3从store的地址流水线写回结果
store 的地址从保留站发出来后会经过 StoreUnit 流水线通过lsq/lsq_replenish总线接口在S1/S2把地址信息更新到store queue 中:
1. 在store流水线s1阶段获得 DTLB hit/miss 的信息, 以及指令的虚拟地址vaddr和物理地址paddr
2. 在store流水线s2阶段获得 mmio/pmp 信息以及是否是mmio地址空间操作等信息
### 功能4接收 store 的数据到STQ 的Datamodule
store 的数据是从与地址不同的保留站发出来的后经过`StdExeUnit`流水线,通过`storeDataIn`接口在S0/S1把数据写到对应的entry的`datamodule`里:
1. S0给`datamodule`发写请求
2. S1写入数据到`datamodule`同时更新 entry 的`datavalid`属性为True接收 store 的mask到STQ 的`Datamodule`
store 的地址从保留站发出来之后会经过`StoreUnit`流水线,`s0_mask_out`在S0把地址中的mask信息更新到对应entry的`datamodule`里。
### 功能5为 load 提供 forward 查询
1. load 需要查询 store queue 来找到在它之前相同地址的与它最近的那个 store 的数据。
- 查询总线(`io.forwrd.sqIdx`) 和 StoreQueue 的出栈指针比较,找出所有比 load 指令老的 storeQueue 中的 entry。以 flag 相同或不同分为2种情况
(1)same flag-> older Store范围是 (tail, sqIdx)如图3(a)所示
(2)different flags-> older Store范围是(tail, VirtualLoadQueueSize) +(0, sqIdx)如图3(b)所示
<div>
<center>
<img src="../StoreQueue_Forward_Mask.svg"
alt="StoreQueue前递范围生成"
style="zoom:100%"/>
<br>
图3StoreQueue前递范围生成<br><br>
</center>
</div>
2. 查询总线用va 和pa同时查询如果发现物理地址匹配但是虚拟地址不匹配或者虚拟地址匹配但是物理地址不匹配的情况就需要将那条 load 设置为 replayInst等 load 到 ROB head 后replay。
3. 如果只发现一笔 entry 匹配且数据准备好,则直接 forward
4. 如果只发现一笔 entry 匹配且数据没有准备好,就需要让保留站负责重发
5. 如果发现多笔匹配,则选择最老的一笔 store forwardStoreQueue以1字节为单位采用树形数据选择逻辑,如图4
<div>
<center>
<img src="../StoreQueue_Forward.svg"
alt="StoreQueue前递数据选择"
style="zoom:100%"/>
<br>
图4StoreQueue前递数据选择<br><br>
</center>
</div>
6. store 指令能被 load forward的条件
- allocated这条 store 还在 store queue 内,还没有写到 sbuffer
- datavalid这条 store 的数据已经就绪
- addrvalid这条 store 已经完成了虚实地址转换,得到了物理地址
7. SSID (Store-Set-ID) 标记了之前 load 预测执行失败历史信息,如果当前 load 命中之前历史中的SSID会等之前所有 older 的 store 都执行完如果没有命中就只会等pa相同的 older Store 执行完成。
### 功能6MMIO与NonCacheable Store指令
- **MMIO Store指令执行**:
1. MMIO 空间的 store 也只能等它到达 ROB 的 head 时才能执行,但是跟 load 稍微有些不同store 到达 ROB 的 head 时,它不一定位于 store queue 的尾部,有可能有的 store 已经提交,但是还在 store queue 中没有写入到 sbuffer需要等待这些 store 写到 sbuffer 之后,才能让这条 MMIO 的 store 去执行。
2. 利用一个状态机去控制MMIO的store执行
- s_idle空闲状态接收到MMIO的store请求后进入到s_req状态;
- s_req给MMIO通道发请求请求被MMIO通道接受后进入s_resp状态;
- s_respMMIO通道返回响应接收后记录是否产生异常并进入到 s_wb 状态
- s_wb将结果转化为内部信号写回给 ROB成功后,如果有异常则进入s_idle, 否则进入到 s_wait 状态
- s_wait等待 ROB 将这条 store 指令提交,提交后重新回到 s_idle 状态
- **NonCacheable Store指令执行**
1. NonCacheable空间的store指令需要等待上一个NonCacheable Store指令提交之后才能从StoreQueue按序发送请求
2. 利用一个状态机去控制NonCacheable的store执行
- nc_idle空闲状态接收到NonCacheable的store请求后进入到nc_req状态;
- nc_req给NonCacheable通道发请求请求被NonCachable通道接受后, 如果启用uncacheOutstanding功能则进入nc_idle否则进入nc_resp状态;
- nc_resp接受NonCacheable通道返回响应并进入到nc_idle状态
### 功能7store指令提交以及写入SBuffer
StoreQueue采用提前提交的方式进行提交。
- **提前提交规则**:
1. 检查进入提交阶段的条件
(1)指令有效。
(2)指令的ROB对头指针不超过待提交指针。
(3)指令不需要取消。
(4)指令不等待Store操作完成或者是向量指令
2. 如果是CommitGroup的第一条指令, 则
(1)检查MMIO状态: 没有MMIO操作或者有MMIO操作并且MMIO store以及提交。
(2)如果是向量指令需满足vecMbCommit条件。
3. 如果不是CommitGroup的第一条指令
(1)提交状态依赖于前一条指令的提交状态。
(2)如果是向量指令需满足vecMbCommit条件。
提交之后可以按顺序写到 sbuffer, 先将这些 store 写到 dataBuffer 中dataBuffer 是一个两项的缓冲区01通道用来处理从大项数 store queue 中的读出延迟。只有0通道可以编写未对齐的指令,同时为了简化设计,即使两个端口出现异常,但仍然只有一个未对齐出队。
- **写入sbuffer的过程**
1. 写入有效信号生成
2. 0通道指令存在非对齐且跨越16字节边界时
(1) 0通道的指令已分配和提交
(2) dataBuffer的01通道能同时接受指令
(3) 0通道的指令不是向量指令并且地址和数据有效或者是向量且vsMergeBuffer以及提交。
(4) 没有跨越4K页表或者跨越4K页表但是可以被出队,并且1如果是0通道允许有异常的数据写入; 2如果是1通道不允许有异常的数据写入。
(5) 之前的指令没有NonCacheable指令如果是第一条指令自身不能是Noncacheable指令
3. 否则,需要满足:
(1) 指令已分配和提交。
(2) 不是向量且地址和数据有效或者是向量且vsMergeBuffer以及提交。
(3) 之前的指令没有NonCacheable和MMIO指令如果是第一条指令自身不能是Noncacheable和MMIO指令。
(4) 如果未对齐store则不能跨越16字节边界且地址和数据有效或有异常
- **地址和数据生成**
1. 地址拆分为高低两部分:
(1) 低位地址8字节对齐地址
(2) 高位地址低位地址加上8偏移量
2. 数据拆分为高低两部分:
(1) 跨16字节边界数据原始数据左移地址低4位偏移量包含的字节数
(2) 低位数据跨16字节边界数据的低128位
(3) 高位数据跨16字节边界数据的高128位
3. 写入选择逻辑:
如果dataBuffer能接受非对齐指令写入,通道0的指令是非对齐并且跨越了16字节边界则检查
(1) 是否跨4K页表同时跨4K页表且可以出队: 通道0使用低位地址和低位数据写入dataBuffer; 通道1使用StoreMisaligBuffer的物理地址和高位数据写入dataBuffer
(2) 否则: 通道0使用低位地址和低位数据写入dataBuffer; 通道1使用高位地址和高位数据写入dataBuffer
(3) 如果通道指令没有跨越16字节并且非对齐则使用16字节对齐地址和对齐数据写入dataBuffer
(4) 否则将原始数据和地址写给dataBuffer
### 功能8强制刷新sbuffer
StoreQueue采用双阈值的方法控制强制刷新Sbuffer上阈值和下阈值。
1. 当StoreQueue的有效项数大于上阈值时 StoreQueue强制刷新Sbuffer
2. 直到StoreQueue的有效项数小于下阈值时停止刷新Sbuffer。
</mrs-functions>
## 接口说明
| name | description |
| ------------------ | ------------------------------------------------------------ |
| enq | 接收来自外部模块的信息,包含入队请求、控制信号等 |
| brqRedirect | 分支重定向信号 |
| vecFeedback | 向量反馈信息 |
| storeAddrIn | store指令的地址 |
| storeAddrInRe | store指令的地址用于处理MMIO 和异常情况 |
| storeDataIn | store指令的数据 |
| storeMaskIn | 传递store掩码从保留站RS发送到 Store QueueSQ。store掩码通常用于指示哪些字节在store操作中是有效的。 |
| sbuffer | 存储已提交的 Store 请求到sbuffer |
| uncacheOutstanding | 指示是否有未完成的uncached请求 |
| cmoOpReg | 发送缓存管理操作请求 |
| cmoOpResp | 接收缓存管理操作的响应 |
| mmioStout | 写回uncache的存储操作的结果 |
| forward | 查询forwarding信息 |
| rob | 接收来自 ROB 的信号或数据 |
| uncache | 发送数据或信号给 uncache 模块 |
| flushSbuffer | 冲刷sbuffer缓冲区 |
| sqEmpty | 标识store queue为空 |
| stAddrReadySqPtr | 指向当前准备好地址的 store 指令 |
| stAddrReadyVec | 向量中对应 store 指令的地址是否已经准备好 |
| stDataReadySqPtr | 指向当前准备好数据的 store 指令 |
| stDataReadyVec | 向量中对应 store 指令的数据是否已经准备好 |
| stIssuePtr | 跟踪当前发出的store请求 |
| sqCancelCnt | 指示在store queue中可以被取消的请求数量 |
| sqDeq | 当前store queue中出队的请求位置 |
| force_write | 是否强制写入存储操作 |
| maControl | 与存储管理缓冲区MA进行控制信号的交互 |

View File

@ -1,11 +0,0 @@
---
title: LSQ
linkTitle: LSQ
weight: 12
---
**本文档参考[香山LSQ设计文档](https://github.com/OpenXiangShan/XiangShan-Design-Doc/tree/master/docs/memblock/LSU/LSQ)写成**
本文档撰写的内容截至[ca892e73]
请注意,本文档撰写的测试点仅供参考,如能补充更多测试点,最终获得的奖励可能更高!

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 569 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 580 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 266 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 512 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 411 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 169 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 373 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 MiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 63 KiB

Some files were not shown because too many files have changed in this diff Show More