mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-20 11:57:12 -04:00
Merge branch 'master' into master
This commit is contained in:
commit
599cb5f261
2883 changed files with 796475 additions and 10300 deletions
|
@ -1,41 +1,54 @@
|
|||
parameters:
|
||||
ibex_configs: []
|
||||
name: Ibex RTL CI Steps
|
||||
description: Ibex RTL CI Steps
|
||||
|
||||
steps:
|
||||
- ${{ each config in parameters.ibex_configs }}:
|
||||
inputs:
|
||||
ibex_config:
|
||||
required: true
|
||||
description: Ibex configuration to run CI for
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
# ibex_config.py will exit with error code 1 on any error which will cause
|
||||
# the CI to fail if there's an issue with the configuration file or an
|
||||
# incorrect configuration name being used
|
||||
- bash: |
|
||||
set -e
|
||||
IBEX_CONFIG_OPTS=`./util/ibex_config.py ${{ config }} fusesoc_opts`
|
||||
- name: Test and display fusesoc config for ${{ inputs.ibex_config }}
|
||||
shell: bash
|
||||
run: |
|
||||
IBEX_CONFIG_OPTS=`./util/ibex_config.py ${{ inputs.ibex_config }} fusesoc_opts`
|
||||
echo $IBEX_CONFIG_OPTS
|
||||
echo "##vso[task.setvariable variable=ibex_config_opts]" $IBEX_CONFIG_OPTS
|
||||
displayName: Test and display fusesoc config for ${{ config }}
|
||||
echo "IBEX_CONFIG_OPTS=$IBEX_CONFIG_OPTS" >> $GITHUB_ENV
|
||||
|
||||
- bash: |
|
||||
- name: Lint Verilog source files with Verilator for ${{ inputs.ibex_config }}
|
||||
shell: bash
|
||||
run: |
|
||||
set +e
|
||||
fusesoc --cores-root . run --target=lint --tool=verilator lowrisc:ibex:ibex_top_tracing $IBEX_CONFIG_OPTS
|
||||
if [ $? != 0 ]; then
|
||||
echo -n "##vso[task.logissue type=error]"
|
||||
echo -n "::error::"
|
||||
echo "Verilog lint failed. Run 'fusesoc --cores-root . run --target=lint --tool=verilator lowrisc:ibex:ibex_top_tracing $IBEX_CONFIG_OPTS' to check and fix all errors."
|
||||
exit 1
|
||||
fi
|
||||
displayName: Lint Verilog source files with Verilator for ${{ config }}
|
||||
|
||||
- bash: |
|
||||
- name: Lint Verilog source files with Verible Verilog Lint for ${{ inputs.ibex_config }}
|
||||
shell: bash
|
||||
run: |
|
||||
set +e
|
||||
fusesoc --cores-root . run --target=lint --tool=veriblelint lowrisc:ibex:ibex_top_tracing $IBEX_CONFIG_OPTS
|
||||
if [ $? != 0 ]; then
|
||||
echo -n "##vso[task.logissue type=error]"
|
||||
echo -n "::error::"
|
||||
echo "Verilog lint failed. Run 'fusesoc --cores-root . run --target=lint --tool=veriblelint lowrisc:ibex:ibex_top_tracing $IBEX_CONFIG_OPTS' to check and fix all errors."
|
||||
exit 1
|
||||
fi
|
||||
displayName: Lint Verilog source files with Verible Verilog Lint for ${{ config }}
|
||||
|
||||
- bash: |
|
||||
- name: Run RISC-V Compliance test for Ibex RV32IMC for ${{ inputs.ibex_config }}
|
||||
shell: bash
|
||||
run: |
|
||||
set +e
|
||||
# Build simulation model of Ibex
|
||||
fusesoc --cores-root=. run --target=sim --setup --build lowrisc:ibex:ibex_riscv_compliance $IBEX_CONFIG_OPTS
|
||||
if [ $? != 0 ]; then
|
||||
echo -n "##vso[task.logissue type=error]"
|
||||
echo -n "::error::"
|
||||
echo "Unable to build Verilator model of Ibex for compliance testing."
|
||||
exit 1
|
||||
fi
|
||||
|
@ -49,14 +62,14 @@ steps:
|
|||
for isa in rv32i rv32im rv32imc rv32Zicsr rv32Zifencei; do
|
||||
make -C build/riscv-compliance RISCV_ISA=$isa 2>&1 | tee run.log
|
||||
if [ ${PIPESTATUS[0]} != 0 ]; then
|
||||
echo -n "##vso[task.logissue type=error]"
|
||||
echo -n "::error::"
|
||||
echo "The RISC-V compliance test suite failed for $isa"
|
||||
|
||||
# There's no easy way to get the test results in machine-readable
|
||||
# form to properly exclude known-failing tests. Going with an
|
||||
# approximate solution for now.
|
||||
if [ $isa == rv32i ] && grep -q 'FAIL: 4/48' run.log; then
|
||||
echo -n "##vso[task.logissue type=error]"
|
||||
echo -n "::error::"
|
||||
echo "Expected failure for rv32i, see lowrisc/ibex#100 more more information."
|
||||
else
|
||||
fail=1
|
||||
|
@ -64,41 +77,26 @@ steps:
|
|||
fi
|
||||
done
|
||||
exit $fail
|
||||
displayName: Run RISC-V Compliance test for Ibex RV32IMC for ${{ config }}
|
||||
|
||||
- bash: |
|
||||
- name: Run Verilator co-sim tests for for ${{ inputs.ibex_config }}
|
||||
shell: bash
|
||||
run: |
|
||||
source ci/setup-cosim.sh
|
||||
# Build simple system with co-simulation
|
||||
fusesoc --cores-root=. run --target=sim --setup --build lowrisc:ibex:ibex_simple_system_cosim $IBEX_CONFIG_OPTS
|
||||
|
||||
if [ $? != 0 ]; then
|
||||
echo -n "##vso[task.logissue type=error]"
|
||||
echo "Build Simple System with co-simulation failed. Run fusesoc --cores-root=. run --target=sim --setup --build lowrisc:ibex:ibex_simple_system_cosim $IBEX_CONFIG_OPTS to check and fix all errors."
|
||||
exit 1
|
||||
fi
|
||||
# Run directed tests against simple system co-simulation
|
||||
./ci/run-cosim-test.sh --skip-pass-check CoreMark examples/sw/benchmarks/coremark/coremark.elf
|
||||
|
||||
echo "Running CoreMark"
|
||||
|
||||
build/lowrisc_ibex_ibex_simple_system_cosim_0/sim-verilator/Vibex_simple_system --meminit=ram,examples/sw/benchmarks/coremark/coremark.elf
|
||||
if [ $? != 0 ]; then
|
||||
echo -n "##vso[task.logissue type=error]"
|
||||
echo "Running CoreMark failed co-simulation testing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "CoreMark succeeded"
|
||||
|
||||
# Run pmp_smoke_test if the config supports PMP
|
||||
if ./util/ibex_config.py ${{ config }} query_fields PMPEnable | grep -q 'PMPEnable=1'; then
|
||||
echo "Running pmp_smoke_test"
|
||||
build/lowrisc_ibex_ibex_simple_system_cosim_0/sim-verilator/Vibex_simple_system --meminit=ram,examples/sw/simple_system/pmp_smoke_test/pmp_smoke_test.elf
|
||||
if [ $? != 0 ]; then
|
||||
echo -n "##vso[task.logissue type=error]"
|
||||
echo "Running pmp_smoke_test failed co-simulation testing"
|
||||
exit 1
|
||||
fi
|
||||
echo "pmp_smoke_test succeeded"
|
||||
if ./util/ibex_config.py ${{ inputs.ibex_config }} query_fields PMPEnable | grep -q 'PMPEnable=1'; then
|
||||
./ci/run-cosim-test.sh --skip-pass-check pmp_smoke examples/sw/simple_system/pmp_smoke_test/pmp_smoke_test.elf
|
||||
else
|
||||
echo "PMP not supported on ${{ config }}, skipping pmp_smoke_test"
|
||||
echo "PMP not supported on ${{ inputs.ibex_config }}, skipping pmp_smoke_test"
|
||||
fi
|
||||
|
||||
if ./util/ibex_config.py ${{ inputs.ibex_config }} query_fields SecureIbex | grep -q 'SecureIbex=1'; then
|
||||
./ci/run-cosim-test.sh dit_test examples/sw/simple_system/dit_test/dit_test.elf
|
||||
./ci/run-cosim-test.sh dummy_instr_test examples/sw/simple_system/dummy_instr_test/dummy_instr_test.elf
|
||||
else
|
||||
echo "Security features not supported on ${{ inputs.ibex_config }}, skipping security feature tests"
|
||||
fi
|
||||
displayName: Run Verilator co-sim tests for for ${{ config }}
|
151
.github/workflows/ci.yml
vendored
Normal file
151
.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,151 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# GitHub Actions CI build configuration
|
||||
|
||||
name: Ibex CI
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
merge_group:
|
||||
types:
|
||||
- checks_requested
|
||||
pull_request:
|
||||
branches:
|
||||
- "*"
|
||||
|
||||
# Note: All tests run as part of one job to avoid copying intermediate build
|
||||
# artifacts around (e.g. Verilator and toolchain builds). Once more builds/tests
|
||||
# are added, we need to re-evaluate this decision to parallelize jobs and
|
||||
# improve end-to-end CI times.
|
||||
|
||||
jobs:
|
||||
lint_dv:
|
||||
name: Run quality checks (Lint and DV)
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# Fetch all history so that we can run git diff on the base branch
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup environment variables
|
||||
run: |
|
||||
# Filter out empty lines or comments
|
||||
grep -v '^\(#\|$\)' ci/vars.env >> $GITHUB_ENV
|
||||
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
ci/install-build-deps.sh
|
||||
|
||||
- name: Display environment
|
||||
run: |
|
||||
echo $PATH
|
||||
python3 --version
|
||||
echo -n "fusesoc "
|
||||
fusesoc --version
|
||||
verilator --version
|
||||
riscv32-unknown-elf-gcc --version
|
||||
verible-verilog-lint --version
|
||||
|
||||
# Verible format is experimental so only run on default config for now,
|
||||
# will eventually become part of the per-config CI
|
||||
- name: Format all source code with Verible format (experimental)
|
||||
run: |
|
||||
set +e
|
||||
fusesoc --cores-root . run --no-export --target=format --tool=veribleformat lowrisc:ibex:ibex_top_tracing
|
||||
if [ $? != 0 ]; then
|
||||
echo -n "::error::"
|
||||
echo "Verilog format with Verible failed. Run 'fusesoc --cores-root . run --no-export --target=format --tool=veribleformat lowrisc:ibex:ibex_top_tracing' to check and fix all errors."
|
||||
echo "This flow is currently experimental and failures can be ignored."
|
||||
fi
|
||||
# Show diff of what verilog_format would have changed, and then revert.
|
||||
git diff --no-pager
|
||||
git reset --hard HEAD
|
||||
continue-on-error: true
|
||||
|
||||
- name: Use clang-format to check C/C++ coding style
|
||||
# This check is not idempotent, but checks changes to a base branch.
|
||||
# Run it only on pull requests.
|
||||
if: github.event_name == 'pull_request'
|
||||
run: |
|
||||
set +e
|
||||
fork_origin=${{ github.event.pull_request.base.sha }}
|
||||
changed_files=$(git diff --name-only $fork_origin | grep -v '^vendor' | grep -E '\.(cpp|cc|c|h)$')
|
||||
test -z "$changed_files" || git diff -U0 $fork_origin $changed_files | clang-format-diff -p1 | tee clang-format-output
|
||||
if [ -s clang-format-output ]; then
|
||||
echo -n "::error::"
|
||||
echo "C/C++ lint failed. Use 'git clang-format' with appropriate options to reformat the changed code."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Build and run CSR testbench with Verilator
|
||||
run: |
|
||||
# Build and run CSR testbench, chosen Ibex configuration does not effect
|
||||
# this so doesn't need to be part of per-config CI
|
||||
fusesoc --cores-root=. run --target=sim --tool=verilator lowrisc:ibex:tb_cs_registers
|
||||
|
||||
- name: Get RISC-V Compliance test suite
|
||||
run: |
|
||||
cd build
|
||||
git clone https://github.com/riscv/riscv-compliance.git
|
||||
cd riscv-compliance
|
||||
git checkout "$RISCV_COMPLIANCE_GIT_VERSION"
|
||||
|
||||
- name: Build tests for verilator co-simulation
|
||||
run: |
|
||||
# Build CoreMark without performance counter dump for co-simulation testing
|
||||
make -C ./examples/sw/benchmarks/coremark SUPPRESS_PCOUNT_DUMP=1
|
||||
make -C ./examples/sw/simple_system/pmp_smoke_test
|
||||
make -C ./examples/sw/simple_system/dit_test
|
||||
make -C ./examples/sw/simple_system/dummy_instr_test
|
||||
|
||||
# Run Ibex RTL CI per supported configuration
|
||||
- name: Run Ibex RTL CI for small configuration
|
||||
uses: ./.github/actions/ibex-rtl-ci-steps
|
||||
with:
|
||||
ibex_config: small
|
||||
- name: Run Ibex RTL CI for opentitan configuration
|
||||
uses: ./.github/actions/ibex-rtl-ci-steps
|
||||
with:
|
||||
ibex_config: opentitan
|
||||
- name: Run Ibex RTL CI for maxperf configuration
|
||||
uses: ./.github/actions/ibex-rtl-ci-steps
|
||||
with:
|
||||
ibex_config: maxperf
|
||||
- name: Run Ibex RTL CI for maxperf-pmp-bmbalanced configuration
|
||||
uses: ./.github/actions/ibex-rtl-ci-steps
|
||||
with:
|
||||
ibex_config: maxperf-pmp-bmbalanced
|
||||
- name: Run Ibex RTL CI for maxperf-pmp-bmfull configuration
|
||||
uses: ./.github/actions/ibex-rtl-ci-steps
|
||||
with:
|
||||
ibex_config: maxperf-pmp-bmfull
|
||||
- name: Run Ibex RTL CI for experimental-branch-predictor configuration
|
||||
uses: ./.github/actions/ibex-rtl-ci-steps
|
||||
with:
|
||||
ibex_config: experimental-branch-predictor
|
||||
|
||||
# Run lint on simple system
|
||||
- name: Run Verilator lint on simple system
|
||||
run: |
|
||||
set +e
|
||||
fusesoc --cores-root . run --target=lint --tool=verilator lowrisc:ibex:ibex_simple_system
|
||||
if [ $? != 0 ]; then
|
||||
echo -n "::error::"
|
||||
echo "Verilog lint with Verilator failed. Run 'fusesoc --cores-root . run --target=lint --tool=verilator lowrisc:ibex:ibex_simple_system' to check and fix all errors."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Run Verible lint on simple system
|
||||
run: |
|
||||
set +e
|
||||
fusesoc --cores-root . run --target=lint --tool=veriblelint lowrisc:ibex:ibex_simple_system
|
||||
if [ $? != 0 ]; then
|
||||
echo -n "::error::"
|
||||
echo "Verilog lint with Verible failed. Run 'fusesoc --cores-root . run --target=lint --tool=veriblelint lowrisc:ibex:ibex_simple_system' to check and fix all errors."
|
||||
exit 1
|
||||
fi
|
38
.github/workflows/pr_lint.yml
vendored
Normal file
38
.github/workflows/pr_lint.yml
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# GitHub Action to run Verible linting on pull requests and add review comments.
|
||||
#
|
||||
# See https://github.com/chipsalliance/verible-linter-action.
|
||||
|
||||
name: pr-lint
|
||||
|
||||
# Triggers when there is any activity on a pull request, e.g. opened, updated.
|
||||
on:
|
||||
merge_group:
|
||||
types:
|
||||
- checks_requested
|
||||
pull_request:
|
||||
branches:
|
||||
- "*"
|
||||
|
||||
jobs:
|
||||
verible-lint:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
verible_config: "vendor/lowrisc_ip/lint/tools/veriblelint/lowrisc-styleguide.rules.verible_lint"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Display Verible config
|
||||
run: |
|
||||
echo "::group::Verible config"
|
||||
cat "$verible_config"
|
||||
echo "::endgroup::"
|
||||
- name: Run Verible linter action
|
||||
uses: chipsalliance/verible-linter-action@main
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
reviewdog_reporter: github-pr-check
|
||||
suggest_fixes: "false"
|
||||
config_file: ${{ env.verible_config }}
|
49
.github/workflows/pr_lint_review.yml
vendored
49
.github/workflows/pr_lint_review.yml
vendored
|
@ -1,49 +0,0 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
name: pr-lint-review
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["pr-trigger"]
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
review_triggered:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
# this workflow does not run in a PR context
|
||||
# download 'event.json' file from a PR-tiggered workflow
|
||||
# to mock the PR context and make a review
|
||||
- name: 'Download artifact'
|
||||
id: get-artifacts
|
||||
uses: actions/github-script@v3.1.0
|
||||
with:
|
||||
script: |
|
||||
var artifacts = await github.actions.listWorkflowRunArtifacts({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: ${{github.event.workflow_run.id }},
|
||||
});
|
||||
var matchArtifact = artifacts.data.artifacts.filter((artifact) => {
|
||||
return artifact.name == "event.json"
|
||||
})[0];
|
||||
var download = await github.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: matchArtifact.id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
var fs = require('fs');
|
||||
fs.writeFileSync('${{github.workspace}}/event.json.zip', Buffer.from(download.data));
|
||||
- run: |
|
||||
unzip event.json.zip
|
||||
- name: Run Verible linter action
|
||||
uses: chipsalliance/verible-linter-action@main
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
suggest_fixes: 'false'
|
||||
config_file: 'vendor/lowrisc_ip/lint/tools/veriblelint/lowrisc-styleguide.rules.verible_lint'
|
25
.github/workflows/pr_trigger.yml
vendored
25
.github/workflows/pr_trigger.yml
vendored
|
@ -1,25 +0,0 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
name: pr-trigger
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Copy event file
|
||||
run: cp "$GITHUB_EVENT_PATH" ./event.json
|
||||
|
||||
# If this workflow is triggered by a PR from a fork
|
||||
# it won't have sufficient access rights to make a review
|
||||
# so we just save the file needed to do the review
|
||||
# in a context with proper access rights
|
||||
- name: Upload event file as artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: event.json
|
||||
path: event.json
|
38
.github/workflows/private-ci.yml
vendored
Normal file
38
.github/workflows/private-ci.yml
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
name: Ibex Private CI
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
merge_group:
|
||||
types:
|
||||
- checks_requested
|
||||
pull_request_target:
|
||||
branches:
|
||||
- "*"
|
||||
|
||||
permissions:
|
||||
contents: write # For repository dispatch
|
||||
|
||||
jobs:
|
||||
trigger:
|
||||
name: Trigger Private CI
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Trigger Private CI
|
||||
run: |
|
||||
PAYLOAD='"target":"${{ github.repository_owner }}/lowrisc-private-ci/master/ibex-private-ci.yml","sha":"${{ github.event.pull_request.head.sha || github.sha }}"'
|
||||
if ${{ github.event_name == 'pull_request_target' }}; then
|
||||
PAYLOAD+=',"pull_request":${{ github.event.pull_request.number }}'
|
||||
fi
|
||||
curl -fL \
|
||||
-X POST \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${{ github.token }}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/${{ github.repository }}/dispatches \
|
||||
-d '{"event_type":"cross-repo-ci","client_payload":{'"$PAYLOAD"'}}'
|
20
.readthedocs.yml
Normal file
20
.readthedocs.yml
Normal file
|
@ -0,0 +1,20 @@
|
|||
# .readthedocs.yaml
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Set the version of Python and other tools you might need
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3.11"
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: doc/conf.py
|
||||
|
||||
python:
|
||||
install:
|
||||
- requirements: doc/requirements.txt
|
|
@ -5,7 +5,7 @@
|
|||
# Rules for svlint, a SystemVerilog linter commonly used in editors.
|
||||
# The configuration matches the lowRISC SystemVerilog style guide at
|
||||
# https://github.com/lowRISC/style-guides/blob/master/VerilogCodingStyle.md.
|
||||
# See https://github.com/dalance/svlint/blob/master/RULES.md for a list of rules.
|
||||
# See https://github.com/dalance/svlint/blob/master/MANUAL.md for a list of rules.
|
||||
|
||||
[option]
|
||||
exclude_paths = ["build.*", "sw/.*", ".sv.tpl$", "vendor/.*"]
|
||||
|
|
36
CREDITS.md
36
CREDITS.md
|
@ -13,34 +13,68 @@ in the form of source code, bug reports, testing, marketing, or any other form,
|
|||
please feel free to open a pull request to get your name added to this file.
|
||||
|
||||
- Alex Bradbury
|
||||
- Andreas Kurth
|
||||
- Andreas Traber
|
||||
- Antonio Pullini
|
||||
- Bryan Cantrill
|
||||
- Canberk Topal
|
||||
- Cathal Minnock
|
||||
- Daniel Mlynek
|
||||
- Dawid Zimonczyk
|
||||
- Eunchan Kim
|
||||
- Felix Yan
|
||||
- Flavian Solt
|
||||
- Florian Zaruba
|
||||
- Francesco Conti
|
||||
- Gary Guo
|
||||
- Germain Haugou
|
||||
- Greg Chadwick
|
||||
- Harry Callahan
|
||||
- Hai Hoang Dang
|
||||
- Henner Zeller
|
||||
- Hodjat Asghari Esfeden
|
||||
- Igor Loi
|
||||
- Ioannis Karageorgos
|
||||
- Markus Wegmann
|
||||
- Ivan Ribeiro
|
||||
- Karol Gugala
|
||||
- Leon Woestenberg
|
||||
- Luís Marques
|
||||
- Marek Pikuła
|
||||
- Markus Wegmann
|
||||
- Marno van der Maas
|
||||
- Matthias Baer
|
||||
- Mehmet Burak Aykenar
|
||||
- Michael Gautschi
|
||||
- Michael Gielda
|
||||
- Michael Munday
|
||||
- Michael Platzer
|
||||
- Michael Schaffner
|
||||
- Nils Graf
|
||||
- Noah Huesser
|
||||
- Noam Gallmann
|
||||
- Pasquale Davide Schiavone
|
||||
- Paul O'Keeffe
|
||||
- Philipp Wagner
|
||||
- Pirmin Vogel
|
||||
- Prajwala Puttappa
|
||||
- Rahul Behl
|
||||
- Rhys Thomas
|
||||
- Renzo Andri
|
||||
- Robert Schilling
|
||||
- Rupert Swarbick
|
||||
- Sam Elliott
|
||||
- Scott Johnson
|
||||
- Stefan Mach
|
||||
- Stefan Tauner
|
||||
- Stefan Wallentowitz
|
||||
- Sven Stucki
|
||||
- Tao Liu
|
||||
- Tobias Wölfel
|
||||
- Tom Roberts
|
||||
- Tudor Timi
|
||||
- Udi Jonnalagadda
|
||||
- Vladimir Rozic
|
||||
- Yuichi Sugiyama
|
||||
- Yusef Karim
|
||||
- Zachary Snow
|
||||
- Zeeshan Rafique
|
||||
|
|
10
NOTICE
Normal file
10
NOTICE
Normal file
|
@ -0,0 +1,10 @@
|
|||
The Ibex Project
|
||||
Copyright 2024 lowRISC contributors.
|
||||
|
||||
This product includes hardware and/or software developed as part of the
|
||||
Ibex(R) (https://github.com/lowRISC/ibex) and OpenTitan(R) projects.
|
||||
|
||||
Ibex was originally developed by the PULP team at ETH Zurich and University of
|
||||
Bologna under the name zero-riscy. Ibex verification, performance enhancement
|
||||
and security hardening have been supported by the OpenTitan project
|
||||
(https://www.opentitan.org).
|
15
README.md
15
README.md
|
@ -1,4 +1,7 @@
|
|||
[](https://dev.azure.com/lowrisc/ibex/_build/latest?definitionId=3&branchName=master)
|
||||
[Ibex OpenTitan configuration Nightly Regression](https://ibex.reports.lowrisc.org/opentitan/latest/report.html)
|
||||
<a href="https://ibex.reports.lowrisc.org/opentitan/latest/report.html">
|
||||
<img src="https://ibex.reports.lowrisc.org/opentitan/latest/summary.svg">
|
||||
</a>
|
||||
|
||||
# Ibex RISC-V Core
|
||||
|
||||
|
@ -9,8 +12,6 @@ seen multiple tape-outs. Ibex supports the Integer (I) or Embedded (E),
|
|||
Integer Multiplication and Division (M), Compressed (C), and B (Bit
|
||||
Manipulation) extensions.
|
||||
|
||||
The block diagram below shows the *small* parametrization with a 2-stage
|
||||
pipeline.
|
||||
<p align="center"><img src="doc/03_reference/images/blockdiagram.svg" width="650"></p>
|
||||
|
||||
Ibex was initially developed as part of the [PULP platform](https://www.pulp-platform.org)
|
||||
|
@ -31,7 +32,7 @@ These are configurations on which lowRISC is focusing for performance evaluation
|
|||
| Performance (CoreMark/MHz) | 0.904 | 2.47 | 3.13 | 3.13 |
|
||||
| Area - Yosys (kGE) | 16.85 | 26.60 | 32.48 | 66.02 |
|
||||
| Area - Commercial (estimated kGE) | ~15 | ~24 | ~30 | ~61 |
|
||||
| Verification status | Red | Green | Amber | Amber |
|
||||
| Verification status | Red | Green | Green | Green |
|
||||
|
||||
Notes:
|
||||
|
||||
|
@ -62,10 +63,10 @@ The Ibex repository includes [Simple System](examples/simple_system/README.md).
|
|||
This is an intentionally simple integration of Ibex with a basic system that targets simulation.
|
||||
It is intended to provide an easy way to get bare metal binaries running on Ibex in simulation.
|
||||
|
||||
A more complete example can be found in the [Ibex Super System repository](https://github.com/GregAC/ibex_super_system).
|
||||
A more complete example can be found in the [Ibex Demo System repository](https://github.com/lowrisc/ibex-demo-system).
|
||||
In particular it includes a integration of the [PULP RISC-V debug module](https://github.com/pulp-platform/riscv-dbg).
|
||||
It targets the [Arty A7 FPGA board from Digilent](https://digilent.com/shop/arty-a7-artix-7-fpga-development-board/) and supports debugging via OpenOCD and GDB over USB (no external JTAG probe required).
|
||||
The Ibex Super System is written by lowRISC but is not an official part of Ibex, nor officially supported by lowRISC.
|
||||
The Ibex Demo System is maintained by lowRISC but is not an official part of Ibex.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
@ -84,7 +85,7 @@ When contributing SystemVerilog source code, please try to be consistent and adh
|
|||
coding style guide](https://github.com/lowRISC/style-guides/blob/master/VerilogCodingStyle.md).
|
||||
|
||||
When contributing C or C++ source code, please try to adhere to [the OpenTitan C++ coding style
|
||||
guide](https://docs.opentitan.org/doc/rm/c_cpp_coding_style/).
|
||||
guide](https://opentitan.org/book/doc/contributing/style_guides/c_cpp_coding_style.html).
|
||||
All C and C++ code should be formatted with clang-format before committing.
|
||||
Either run `clang-format -i filename.cc` or `git clang-format` on added files.
|
||||
|
||||
|
|
8
SECURITY.md
Normal file
8
SECURITY.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Reporting Security Issues
|
||||
|
||||
The lowRISC team and Ibex community (including the OpenTitan partnership) take security issues seriously.
|
||||
|
||||
We appreciate all efforts to find security vulnerabilities in Ibex and ask that responsible disclosure is practiced should you discover a potential vulnerability.
|
||||
|
||||
As Ibex and in particular its secure configuration was developed as part of [OpenTitan](https://www.github.com/lowrisc/opentitan) contact [security@opentitan.org](mailto:security@opentitan.org) to report any security issues and do not open a public issue.
|
||||
[security@opentitan.org](mailto:security@opentitan.org) will advise on the coordinated vulnerability disclosure (CVD) procedure.
|
|
@ -1,126 +0,0 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Azure Pipelines CI build configuration
|
||||
# Documentation at https://aka.ms/yaml
|
||||
|
||||
variables:
|
||||
- template: ci/vars.yml
|
||||
|
||||
trigger:
|
||||
batch: true
|
||||
branches:
|
||||
include:
|
||||
- '*'
|
||||
tags:
|
||||
include:
|
||||
- '*'
|
||||
pr:
|
||||
branches:
|
||||
include:
|
||||
- '*'
|
||||
|
||||
# Note: All tests run as part of one job to avoid copying intermediate build
|
||||
# artifacts around (e.g. Verilator and toolchain builds). Once more builds/tests
|
||||
# are added, we need to re-evaluate this decision to parallelize jobs and
|
||||
# improve end-to-end CI times.
|
||||
|
||||
jobs:
|
||||
- job: lint_dv
|
||||
displayName: Run quality checks (Lint and DV)
|
||||
pool:
|
||||
vmImage: "ubuntu-20.04"
|
||||
steps:
|
||||
- bash: |
|
||||
ci/install-build-deps.sh
|
||||
displayName: Install build dependencies
|
||||
|
||||
- bash: |
|
||||
echo $PATH
|
||||
python3 --version
|
||||
echo -n "fusesoc "
|
||||
fusesoc --version
|
||||
verilator --version
|
||||
riscv32-unknown-elf-gcc --version
|
||||
verible-verilog-lint --version
|
||||
displayName: Display environment
|
||||
|
||||
# Verible format is experimental so only run on default config for now,
|
||||
# will eventually become part of the per-config CI
|
||||
- bash: |
|
||||
fusesoc --cores-root . run --no-export --target=format --tool=veribleformat lowrisc:ibex:ibex_top_tracing
|
||||
if [ $? != 0 ]; then
|
||||
echo -n "##vso[task.logissue type=error]"
|
||||
echo "Verilog format with Verible failed. Run 'fusesoc --cores-root . run --no-export --target=format --tool=veribleformat lowrisc:ibex:ibex_top_tracing' to check and fix all errors."
|
||||
echo "This flow is currently experimental and failures can be ignored."
|
||||
fi
|
||||
# Show diff of what verilog_format would have changed, and then revert.
|
||||
git diff
|
||||
git reset --hard HEAD
|
||||
continueOnError: true
|
||||
displayName: Format all source code with Verible format (experimental)
|
||||
|
||||
- bash: |
|
||||
fork_origin=$(git merge-base --fork-point origin/master)
|
||||
changed_files=$(git diff --name-only $fork_origin | grep -v '^vendor' | grep -E '\.(cpp|cc|c|h)$')
|
||||
test -z "$changed_files" || git diff -U0 $fork_origin $changed_files | clang-format-diff -p1 | tee clang-format-output
|
||||
if [ -s clang-format-output ]; then
|
||||
echo -n "##vso[task.logissue type=error]"
|
||||
echo "C/C++ lint failed. Use 'git clang-format' with appropriate options to reformat the changed code."
|
||||
exit 1
|
||||
fi
|
||||
# This check is not idempotent, but checks changes to a base branch.
|
||||
# Run it only on pull requests.
|
||||
condition: eq(variables['Build.Reason'], 'PullRequest')
|
||||
displayName: 'Use clang-format to check C/C++ coding style'
|
||||
|
||||
- bash: |
|
||||
# Build and run CSR testbench, chosen Ibex configuration does not effect
|
||||
# this so doesn't need to be part of per-config CI
|
||||
fusesoc --cores-root=. run --target=sim --tool=verilator lowrisc:ibex:tb_cs_registers
|
||||
displayName: Build and run CSR testbench with Verilator
|
||||
|
||||
- bash: |
|
||||
cd build
|
||||
git clone https://github.com/riscv/riscv-compliance.git
|
||||
cd riscv-compliance
|
||||
git checkout "$RISCV_COMPLIANCE_GIT_VERSION"
|
||||
displayName: Get RISC-V Compliance test suite
|
||||
|
||||
- bash: |
|
||||
# Build CoreMark without performance counter dump for co-simulation testing
|
||||
make -C ./examples/sw/benchmarks/coremark SUPPRESS_PCOUNT_DUMP=1
|
||||
make -C ./examples/sw/simple_system/pmp_smoke_test
|
||||
displayName: Build tests for verilator co-simulation
|
||||
|
||||
# Run Ibex RTL CI per supported configuration
|
||||
- template : ci/ibex-rtl-ci-steps.yml
|
||||
parameters:
|
||||
ibex_configs:
|
||||
# Note: Try to keep the list of configurations in sync with the one used
|
||||
# in Private CI.
|
||||
- small
|
||||
- experimental-maxperf-pmp
|
||||
- experimental-maxperf-pmp-bmfull
|
||||
- opentitan
|
||||
- experimental-branch-predictor
|
||||
|
||||
# Run lint on simple system
|
||||
- bash: |
|
||||
fusesoc --cores-root . run --target=lint --tool=verilator lowrisc:ibex:ibex_simple_system
|
||||
if [ $? != 0 ]; then
|
||||
echo -n "##vso[task.logissue type=error]"
|
||||
echo "Verilog lint with Verilator failed. Run 'fusesoc --cores-root . run --target=lint --tool=verilator lowrisc:ibex:ibex_simple_system' to check and fix all errors."
|
||||
exit 1
|
||||
fi
|
||||
displayName: Run Verilator lint on simple system
|
||||
|
||||
- bash: |
|
||||
fusesoc --cores-root . run --target=lint --tool=veriblelint lowrisc:ibex:ibex_simple_system
|
||||
if [ $? != 0 ]; then
|
||||
echo -n "##vso[task.logissue type=error]"
|
||||
echo "Verilog lint with Verible failed. Run 'fusesoc --cores-root . run --target=lint --tool=veriblelint lowrisc:ibex:ibex_simple_system' to check and fix all errors."
|
||||
exit 1
|
||||
fi
|
||||
displayName: Run Verible lint on simple system
|
|
@ -1,31 +0,0 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Private CI trigger. Used to run tooling that can't currently be shared
|
||||
# publicly.
|
||||
|
||||
trigger:
|
||||
batch: true
|
||||
branches:
|
||||
include:
|
||||
- '*'
|
||||
tags:
|
||||
include:
|
||||
- "*"
|
||||
pr:
|
||||
branches:
|
||||
include:
|
||||
- '*'
|
||||
|
||||
# The runner used for private CI enforces the use of the template below. All
|
||||
# build steps need to be placed into the template.
|
||||
resources:
|
||||
repositories:
|
||||
- repository: lowrisc-private-ci
|
||||
type: github
|
||||
endpoint: lowRISC
|
||||
name: lowrisc/lowrisc-private-ci
|
||||
|
||||
extends:
|
||||
template: jobs-ibex.yml@lowrisc-private-ci
|
|
@ -11,16 +11,20 @@ set -e
|
|||
[ -f /etc/os-release ] || (echo "/etc/os-release doesn't exist."; exit 1)
|
||||
. /etc/os-release
|
||||
|
||||
[ ! -z "$VERILATOR_VERSION" ] || (echo "VERILATOR_VERSION must be set."; exit 1)
|
||||
[ ! -z "$VERIBLE_VERSION" ] || (echo "VERIBLE_VERSION must be set."; exit 1)
|
||||
[ ! -z "$RISCV_TOOLCHAIN_TAR_VERSION" ] || (echo "RISCV_TOOLCHAIN_TAR_VERSION must be set."; exit 1)
|
||||
[ ! -z "$RISCV_TOOLCHAIN_TAR_VARIANT" ] || (echo "RISCV_TOOLCHAIN_TAR_VARIANT must be set."; exit 1)
|
||||
[ -n "$VERILATOR_VERSION" ] || (echo "VERILATOR_VERSION must be set."; exit 1)
|
||||
[ -n "$VERIBLE_VERSION" ] || (echo "VERIBLE_VERSION must be set."; exit 1)
|
||||
[ -n "$RISCV_TOOLCHAIN_TAR_VERSION" ] || (echo "RISCV_TOOLCHAIN_TAR_VERSION must be set."; exit 1)
|
||||
[ -n "$RISCV_TOOLCHAIN_TAR_VARIANT" ] || (echo "RISCV_TOOLCHAIN_TAR_VARIANT must be set."; exit 1)
|
||||
|
||||
SUDO_CMD=""
|
||||
if [ $(id -u) -ne 0 ]; then
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
SUDO_CMD="sudo "
|
||||
fi
|
||||
|
||||
if [ -z "$GITHUB_ACTIONS" ]; then
|
||||
GITHUB_PATH=/dev/null
|
||||
fi
|
||||
|
||||
case "$ID-$VERSION_ID" in
|
||||
ubuntu-16.04|ubuntu-18.04|ubuntu-20.04)
|
||||
# Curl must be available to get the repo key below.
|
||||
|
@ -49,19 +53,22 @@ case "$ID-$VERSION_ID" in
|
|||
libelf-dev \
|
||||
clang-format \
|
||||
wget \
|
||||
xz-utils
|
||||
xz-utils \
|
||||
libcairo2-dev
|
||||
|
||||
wget https://storage.googleapis.com/ibex-cosim-builds/ibex-cosim-$IBEX_COSIM_VERSION.tar.gz
|
||||
wget https://storage.googleapis.com/ibex-cosim-builds/ibex-cosim-"$IBEX_COSIM_VERSION".tar.gz
|
||||
$SUDO_CMD mkdir -p /tools/riscv-isa-sim
|
||||
$SUDO_CMD chmod 777 /tools/riscv-isa-sim
|
||||
$SUDO_CMD tar -C /tools/riscv-isa-sim -xvzf ibex-cosim-$IBEX_COSIM_VERSION.tar.gz --strip-components=1
|
||||
$SUDO_CMD tar -C /tools/riscv-isa-sim -xvzf ibex-cosim-"$IBEX_COSIM_VERSION".tar.gz --strip-components=1
|
||||
echo "##vso[task.prependpath]/tools/riscv-isa-sim/bin"
|
||||
echo "/tools/riscv-isa-sim/bin" >> $GITHUB_PATH
|
||||
|
||||
wget https://storage.googleapis.com/verilator-builds/verilator-$VERILATOR_VERSION.tar.gz
|
||||
wget https://storage.googleapis.com/verilator-builds/verilator-"$VERILATOR_VERSION".tar.gz
|
||||
$SUDO_CMD mkdir -p /tools/verilator
|
||||
$SUDO_CMD chmod 777 /tools/verilator
|
||||
$SUDO_CMD tar -C /tools/verilator -xvzf verilator-$VERILATOR_VERSION.tar.gz
|
||||
$SUDO_CMD tar -C /tools/verilator -xvzf verilator-"$VERILATOR_VERSION".tar.gz
|
||||
echo "##vso[task.prependpath]/tools/verilator/$VERILATOR_VERSION/bin"
|
||||
echo "/tools/verilator/$VERILATOR_VERSION/bin" >> $GITHUB_PATH
|
||||
# Python dependencies
|
||||
#
|
||||
# Updating pip and setuptools is required to have these tools properly
|
||||
|
@ -69,7 +76,7 @@ case "$ID-$VERSION_ID" in
|
|||
# an older version of a package must be used for a certain Python version.
|
||||
# If that information is not read, pip installs the latest version, which
|
||||
# then fails to run.
|
||||
$SUDO_CMD pip3 install -U pip setuptools
|
||||
$SUDO_CMD pip3 install -U pip "setuptools<66.0.0"
|
||||
|
||||
$SUDO_CMD pip3 install -r python-requirements.txt
|
||||
|
||||
|
@ -78,8 +85,9 @@ case "$ID-$VERSION_ID" in
|
|||
cd build/verible
|
||||
curl -Ls -o verible.tar.gz "https://github.com/google/verible/releases/download/$VERIBLE_VERSION/verible-$VERIBLE_VERSION-Ubuntu-$VERSION_ID-$VERSION_CODENAME-x86_64.tar.gz"
|
||||
$SUDO_CMD mkdir -p /tools/verible && $SUDO_CMD chmod 777 /tools/verible
|
||||
tar -C /tools/verible -xf verible.tar.gz --strip-components=1
|
||||
$SUDO_CMD tar -C /tools/verible -xf verible.tar.gz --strip-components=1
|
||||
echo "##vso[task.prependpath]/tools/verible/bin"
|
||||
echo "/tools/verible/bin" >> $GITHUB_PATH
|
||||
;;
|
||||
|
||||
*)
|
||||
|
@ -93,5 +101,6 @@ TOOLCHAIN_URL="https://github.com/lowRISC/lowrisc-toolchains/releases/download/$
|
|||
mkdir -p build/toolchain
|
||||
curl -Ls -o build/toolchain/rv32-toolchain.tar.xz "$TOOLCHAIN_URL"
|
||||
$SUDO_CMD mkdir -p /tools/riscv && $SUDO_CMD chmod 777 /tools/riscv
|
||||
tar -C /tools/riscv -xf build/toolchain/rv32-toolchain.tar.xz --strip-components=1
|
||||
$SUDO_CMD tar -C /tools/riscv -xf build/toolchain/rv32-toolchain.tar.xz --strip-components=1
|
||||
echo "##vso[task.prependpath]/tools/riscv/bin"
|
||||
echo "/tools/riscv/bin" >> $GITHUB_PATH
|
||||
|
|
54
ci/run-cosim-test.sh
Executable file
54
ci/run-cosim-test.sh
Executable file
|
@ -0,0 +1,54 @@
|
|||
#!/bin/bash
|
||||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Run an elf against simple system co-simulation and check the UART output for
|
||||
# reported pass/fail reporting as appropriate for use in GitHub Actions
|
||||
|
||||
SKIP_PASS_CHECK=0
|
||||
|
||||
if [ $# -eq 3 ]; then
|
||||
if [ $1 == "--skip-pass-check" ]; then
|
||||
SKIP_PASS_CHECK=1
|
||||
fi
|
||||
|
||||
TEST_NAME=$2
|
||||
TEST_ELF=$3
|
||||
elif [ $# -eq 2 ]; then
|
||||
TEST_NAME=$1
|
||||
TEST_ELF=$2
|
||||
else
|
||||
echo "Usage: $0 [--skip-pass-check] test_name test_elf"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Running $TEST_NAME with co-simulation"
|
||||
build/lowrisc_ibex_ibex_simple_system_cosim_0/sim-verilator/Vibex_simple_system --meminit=ram,$TEST_ELF
|
||||
if [ $? != 0 ]; then
|
||||
echo "##vso[task.logissue type=error]Running % failed co-simulation testing"
|
||||
echo "::error::Running % failed co-simulation testing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
grep 'FAILURE' ibex_simple_system.log
|
||||
if [ $? != 1 ]; then
|
||||
echo "##vso[task.logissue type=error]Failure seen in $TEST_NAME log"
|
||||
echo "::error::Failure seen in $TEST_NAME log"
|
||||
echo "Log contents:"
|
||||
cat ibex_simple_system.log
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $SKIP_PASS_CHECK != 1 ]; then
|
||||
grep 'PASS' ibex_simple_system.log
|
||||
if [ $? != 0 ]; then
|
||||
echo "##vso[task.logissue type=error]No pass seen in $TEST_NAME log"
|
||||
echo "::error::No pass seen in $TEST_NAME log"
|
||||
echo "Log contents:"
|
||||
cat ibex_simple_system.log
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "$TEST_NAME succeeded"
|
15
ci/vars.env
Normal file
15
ci/vars.env
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Pipeline variables, used by the public and private CI pipelines
|
||||
# Quote values to ensure they are parsed as string (version numbers might
|
||||
# end up as float otherwise).
|
||||
VERILATOR_VERSION=v4.210
|
||||
IBEX_COSIM_VERSION=39612f9
|
||||
RISCV_TOOLCHAIN_TAR_VERSION=20220210-1
|
||||
RISCV_TOOLCHAIN_TAR_VARIANT=lowrisc-toolchain-gcc-rv32imcb
|
||||
RISCV_COMPLIANCE_GIT_VERSION=844c6660ef3f0d9b96957991109dfd80cc4938e2
|
||||
VERIBLE_VERSION=v0.0-2135-gb534c1fe
|
||||
# lowRISC-internal version numbers of Ibex-specific Spike builds.
|
||||
SPIKE_IBEX_VERSION=20220817-git-eccdcb15c3e51b4f7906c7b42fb824f24a4338a2
|
16
ci/vars.yml
16
ci/vars.yml
|
@ -1,16 +0,0 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Pipeline variables, used by the public and private CI pipelines
|
||||
# Quote values to ensure they are parsed as string (version numbers might
|
||||
# end up as float otherwise).
|
||||
variables:
|
||||
VERILATOR_VERSION: "v4.104"
|
||||
IBEX_COSIM_VERSION: "0dc2de5d"
|
||||
RISCV_TOOLCHAIN_TAR_VERSION: "20220210-1"
|
||||
RISCV_TOOLCHAIN_TAR_VARIANT: "lowrisc-toolchain-gcc-rv32imcb"
|
||||
RISCV_COMPLIANCE_GIT_VERSION: "844c6660ef3f0d9b96957991109dfd80cc4938e2"
|
||||
VERIBLE_VERSION: "v0.0-2135-gb534c1fe"
|
||||
# lowRISC-internal version numbers of Ibex-specific Spike builds.
|
||||
SPIKE_IBEX_VERSION: "20220817-git-eccdcb15c3e51b4f7906c7b42fb824f24a4338a2"
|
|
@ -1,43 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Read an Azure Pipelines-compatible variables file, and convert it into
|
||||
# logging commands that Azure Pipelines understands, effectively setting the
|
||||
# variables at runtime.
|
||||
#
|
||||
# This script can be used as a workaround if variables cannot be included in the
|
||||
# Pipeline definition directly.
|
||||
#
|
||||
# See https://docs.microsoft.com/en-us/azure/devops/pipelines/scripts/logging-commands
|
||||
# for more information on logging commands.
|
||||
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
def vars_to_logging_cmd(vars_file):
|
||||
data = {}
|
||||
print(vars_file)
|
||||
with open(vars_file, 'r', encoding="utf-8") as fp:
|
||||
data = yaml.load(fp, Loader=yaml.SafeLoader)
|
||||
|
||||
if not (isinstance(data, dict) and 'variables' in data):
|
||||
print("YAML file wasn't a dictionary with a 'variables' key. Got: {}"
|
||||
.format(data))
|
||||
|
||||
print("Setting variables from {}".format(vars_file))
|
||||
for key, value in data['variables'].items():
|
||||
# Note: These lines won't show up in the Azure Pipelines output unless
|
||||
# "System Diagnostics" are enabled (go to the Azure Pipelines web UI,
|
||||
# click on "Run pipeline" to manually run a pipeline, and check "Enable
|
||||
# system diagnostics".)
|
||||
print("##vso[task.setvariable variable={}]{}".format(key, value))
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: {} VARS_FILE".format(sys.argv[0]))
|
||||
sys.exit(1)
|
||||
sys.exit(vars_to_logging_cmd(sys.argv[1]))
|
|
@ -9,7 +9,7 @@ It follows these specifications:
|
|||
Ibex implements the Machine ISA version 1.12.
|
||||
* `RISC-V External Debug Support, version 0.13.2 <https://content.riscv.org/wp-content/uploads/2019/03/riscv-debug-release.pdf>`_
|
||||
* `RISC-V Bit-Manipulation Extension, version 1.0.0 <https://github.com/riscv/riscv-bitmanip/releases/download/1.0.0/bitmanip-1.0.0-38-g865e7a7.pdf>`_ and `version 0.93 (draft from January 10, 2021) <https://github.com/riscv/riscv-bitmanip/blob/master/bitmanip-0.93.pdf>`_
|
||||
* `PMP Enhancements for memory access and execution prevention on Machine mode (Smepmp) version 0.9.3 <https://github.com/riscv/riscv-tee/blob/61455747230a26002d741f64879dd78cc9689323/Smepmp/Smepmp.pdf>`_
|
||||
* `PMP Enhancements for memory access and execution prevention on Machine mode (Smepmp) version 1.0 <https://github.com/riscv/riscv-tee/blob/191b563b08b31cc2974d604a3b670d8666a2e093/Smepmp/Smepmp.pdf>`_
|
||||
|
||||
Many features in the RISC-V specification are optional, and Ibex can be parametrized to enable or disable some of them.
|
||||
|
||||
|
@ -47,16 +47,17 @@ In addition, the following instruction set extensions are available.
|
|||
- 2.0
|
||||
- always enabled
|
||||
|
||||
Most content of the RISC-V privileged specification is optional.
|
||||
Ibex currently supports the following features according to the RISC-V Privileged Specification, version 1.11.
|
||||
* - **Smepmp** - PMP Enhancements for memory access and execution prevention on Machine mode
|
||||
- 1.0
|
||||
- always enabled in configurations with PMP see :ref:`PMP Enhancements<pmp-enhancements>`
|
||||
|
||||
Ibex currently supports the following features according to the RISC-V Privileged Specification, version 1.12.
|
||||
|
||||
* M-Mode and U-Mode
|
||||
* All CSRs listed in :ref:`cs-registers`
|
||||
* Performance counters as described in :ref:`performance-counters`
|
||||
* Vectorized trap handling as described at :ref:`exceptions-interrupts`
|
||||
|
||||
See :ref:`PMP Enhancements<pmp-enhancements>` for more information on Ibex's experimental and optional support for the PMP Enhancement proposal from the Trusted Execution Environment (TEE) working group.
|
||||
|
||||
.. rubric:: Footnotes
|
||||
|
||||
.. [#B_draft] Ibex fully implements the ratified version 1.0.0 of the RISC-V Bit-Manipulation Extension including the Zba, Zbb, Zbc and Zbs sub-extensions.
|
||||
|
|
|
@ -14,3 +14,4 @@ Read on for more information Ibex in general: what standards it implements, what
|
|||
compliance
|
||||
targets
|
||||
licensing
|
||||
verification_overview
|
||||
|
|
|
@ -7,8 +7,6 @@ ASIC Synthesis
|
|||
ASIC synthesis is supported for Ibex.
|
||||
The whole design is completely synchronous and uses positive-edge triggered flip-flops, except for the register file, which can be implemented either with latches or with flip-flops.
|
||||
See :ref:`register-file` for more details.
|
||||
The core occupies an area of roughly 24 kGE when using the latch-based register file and implementing the RV32IMC ISA, or 15 kGE when implementing the RV32EC ISA.
|
||||
|
||||
|
||||
FPGA Synthesis
|
||||
--------------
|
||||
|
|
22
doc/01_overview/verification_overview.rst
Normal file
22
doc/01_overview/verification_overview.rst
Normal file
|
@ -0,0 +1,22 @@
|
|||
Verification Overview
|
||||
=====================
|
||||
|
||||
Ibex is verified using a :ref:`UVM based testbench<verification>` that employs a :ref:`co-simulation methodology<cosim>` to cross-check Ibex execution against an ISS reference model (`Spike <https://github.com/lowRISC/riscv-isa-sim>`_).
|
||||
The testbench runs binaries built from source produced by the `RISC-DV <https://github.com/chipsalliance/riscv-dv>`_ random instruction generator.
|
||||
Additional stimulus is provided in the form of randomized memory timings, memory errors, interrupts and debug requests by the testbench.
|
||||
A comprehensive :ref:`testplan<testplan>` and :ref:`coverage plan<coverage-plan>` are implemented.
|
||||
|
||||
Verification Status
|
||||
-------------------
|
||||
|
||||
Ibex has a large number of parameters resulting in a large number of possible configurations.
|
||||
The configuration space is too large to fully verify the design for all possible parameter sets.
|
||||
To manage this complexity regressions runs and verification closure target a number of :ref:`supported configurations<ibex-config>`.
|
||||
|
||||
Current verification closure effort is focussed on the ``opentitan`` configuration and is the only configuration with nightly regression runs.
|
||||
Verification maturity is tracked via :ref:`verification_stages` that are `defined by the OpenTitan project <https://opentitan.org/book/doc/project_governance/development_stages.html#hardware-verification-stages-v>`_.
|
||||
Ibex has achieved **V2S** for the `opentitan` configuration, broadly this means verification is almost complete (over 90% code and functional coverage hit with over 90% regression pass rate with test plan and coverage plan fully implemented) but not yet closed.
|
||||
|
||||
Nightly regression results, including a coverage summary and details of test failures, for the ``opentitan`` Ibex configuration are published at https://ibex.reports.lowrisc.org/opentitan/latest/report.html. Below is a summary of these results:
|
||||
|
||||
.. image:: https://ibex.reports.lowrisc.org/opentitan/latest/summary.svg
|
43
doc/02_user/configuration.rst
Normal file
43
doc/02_user/configuration.rst
Normal file
|
@ -0,0 +1,43 @@
|
|||
.. _ibex-config:
|
||||
|
||||
Ibex Configurations
|
||||
===================
|
||||
|
||||
The ``ibex_top`` module has a large number of top-level parameters which configure the core (see :ref:`core-integration`).
|
||||
This gives rise to a huge number of possible Ibex core configurations.
|
||||
To manage this complexity a number of named configurations is provided in the :file:`ibex_configs.yml` file.
|
||||
A subset of these are 'supported configurations' which are the focus of verification and development activities.
|
||||
|
||||
Configuration Tool
|
||||
------------------
|
||||
|
||||
A tool :file:`util/ibex_config.py` is provided to work with the named configurations.
|
||||
This tool provides command line options to set Ibex parameters for various EDA tools for a named configuration.
|
||||
Various Ibex flows (e.g. the DV flow) use this tool internally and can be provided with a configuration name from :file:`util/ibex_config.py` to work with.
|
||||
|
||||
Here is an example of using the configuration tool to get the FuseSoC options required to build the ``opentitan`` configuration.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Request FuseSoC options required to build the 'opentitan' Ibex configuration.
|
||||
./util/ibex_config.py opentitan fusesoc_opts
|
||||
|
||||
# The output of the tool
|
||||
--RV32E=0 --RV32M=ibex_pkg::RV32MSingleCycle --RV32B=ibex_pkg::RV32BOTEarlGrey --RegFile=ibex_pkg::RegFileFF --BranchTargetALU=1 --WritebackStage=1 --ICache=1 --ICacheECC=1 --ICacheScramble=1 --BranchPredictor=0 --DbgTriggerEn=1 --SecureIbex=1 --PMPEnable=1 --PMPGranularity=0 --PMPNumRegions=16 --MHPMCounterNum=10 --MHPMCounterWidth=32
|
||||
|
||||
For further information about using the tool check the help provided on the command line.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Get help on using ibex_config.py
|
||||
./util/ibex_config.py -h
|
||||
|
||||
Supported Configurations
|
||||
------------------------
|
||||
|
||||
The current set of supported configurations are:
|
||||
|
||||
* ``small`` - RV32IMC with two stage pipeline and 3 cycle multiplier
|
||||
* ``opentitan`` - The configuration used by the `OpenTitan <www.opentitan.org>`_ project
|
||||
* ``maxperf`` - RV32IMC with three stage pipeline and single cycle multiplier, maximum performance (using stable features) configuration.
|
||||
* ``maxperf-pmp-bmbalanced`` - ``maxperf`` configuration with PMP and the 'balanced' bit-manipulation configuration (:ref:`core-integration` for details).
|
|
@ -3,16 +3,25 @@
|
|||
Examples
|
||||
========
|
||||
|
||||
To make use of Ibex it has to be integrated as described in :ref:`core-integration`.
|
||||
There are two examples that demonstrate Ibex usage.
|
||||
|
||||
FPGA
|
||||
----
|
||||
The first is 'Simple System' and is part of the Ibex repository.
|
||||
It demonstrates a minimal system connecting Ibex to some memory with a timer peripheral and is targeted at simulation.
|
||||
|
||||
A minimal example for the `Arty A7 <https://reference.digilentinc.com/reference/programmable-logic/arty-a7/start>`_ FPGA Development board is provided.
|
||||
In this example Ibex is directly linked to a SRAM memory instance.
|
||||
Four LEDs from the board are connected to the data bus and are updated each time when a word is written.
|
||||
The memory is separated into a instruction and data section.
|
||||
The instructions memory is initialized at synthesis time by reading the output from the software build.
|
||||
The software writes to the data section the complementary lower for bits of a word every second resulting in blinking LEDs.
|
||||
The second is the `'Ibex Demo System' <https://www.github.com/lowrisc/ibex-demo-system>`_ which is a separate repository.
|
||||
It is targeted at FPGA implementation and contains some extra peripherals along with a RISC-V debug module integration.
|
||||
|
||||
Simple System
|
||||
-------------
|
||||
|
||||
Simple system is built via FuseSoC.
|
||||
Verilator is the primary simulator it is designed for, though other simulators are also supported (such as VCS).
|
||||
Its aim is to make running a binary against Ibex RTL, obtaining an instruction trace, wave trace and any other simulation outputs as simple as possible along with demonstrating basic Ibex integration.
|
||||
See the `Simple System README <https://github.com/lowRISC/ibex/tree/master/examples/simple_system>`_ for more information.
|
||||
|
||||
There is an extended version of simple system which adds co-simulation checking.
|
||||
This cross-checks every instruction execution against a RISC-V ISS.
|
||||
It is the same co-simulation method used by our full DV environment but enables its use in a far simpler setup.
|
||||
The simple system co-simulation setup is compatible with Verilator (unlike our full DV environment).
|
||||
See :ref:`cosim` for more information.
|
||||
|
||||
Find the description of how to build and program the Arty board in ``examples/fpga/artya7/README.md``.
|
||||
|
|
|
@ -3,25 +3,16 @@
|
|||
Getting Started with Ibex
|
||||
=========================
|
||||
|
||||
This page discusses initial steps and requirements to start using Ibex in your design.
|
||||
The Ibex repository contains all the RTL needed to simulate and synthesize an Ibex core.
|
||||
`FuseSoC <https://github.com/olofk/fusesoc>`_ core files list the RTL files required to build Ibex (see :file:`ibex_core.core`).
|
||||
The core itself is contained in the :file:`rtl/` directory, though it utilizes some primitives found in the :file:`vendor/lowrisc_ip/` directory.
|
||||
These primitives come from the `OpenTitan <https://github.com/lowrisc/opentitan>`_ project but are copied into the Ibex repository so the RTL has no external dependencies.
|
||||
You may wish to replace these primitives with your own and some are only required for specific configurations.
|
||||
See :ref:`integration-prims` for more information.
|
||||
|
||||
Register File
|
||||
-------------
|
||||
There are several paths to follow depending on what you wish to accomplish:
|
||||
|
||||
Ibex comes with three different register file implementations that can be selected using the enumerated parameter ``RegFile`` defined in :file:`rtl/ibex_pkg.sv`.
|
||||
Depending on the target technology, either the flip-flop-based ("ibex_pkg::RegFileFF", default), the latch-based ("ibex_pkg::RegFileLatch") or an FPGA-targeted ("ibex_pkg::RegFileFPGA") implementation should be selected.
|
||||
For more information about the three register file implementations and their trade-offs, check out :ref:`register-file`.
|
||||
|
||||
Identification CSRs
|
||||
-------------------
|
||||
|
||||
The RISC-V Privileged Architecture specifies several read-only CSRs that identify the vendor and micro-architecture of a CPU.
|
||||
These are ``mvendorid``, ``marchid`` and ``mimpid``.
|
||||
The fixed, read-only values for these CSRs are defined in :file:`rtl/ibex_pkg.sv`.
|
||||
Implementers should carefully consider appropriate values for these registers.
|
||||
Ibex, as an open source implementation, has an assigned architecture ID (``marchid``) of 22.
|
||||
(Allocations are specified in `marchid.md of the riscv-isa-manual repository <https://github.com/riscv/riscv-isa-manual/blob/master/marchid.md>`_.)
|
||||
If significant changes are made to the micro-architecture a different architecture ID should be used.
|
||||
The vendor ID and implementation ID (``mvendorid`` and ``mimpid``) both read as 0 by default, meaning non-implemented.
|
||||
Implementers may wish to use other values here.
|
||||
Please see the RISC-V Privileged Architecture specification for more details on what these IDs represent and how they should be chosen.
|
||||
* See :ref:`examples` for a basic simulation setup running the core in isolation and a simple FPGA system.
|
||||
* See :ref:`verification` to begin working with the DV flow.
|
||||
* See :ref:`core-integration` to integrate the Ibex core into your own design.
|
||||
* See :ref:`integration-fusesoc-files` for information on how to get a complete RTL file listing to build Ibex for use outside of FuseSoC based flows.
|
||||
|
|
|
@ -10,5 +10,6 @@ It is aimed at hardware developers integrating Ibex into a design, and software
|
|||
|
||||
system_requirements
|
||||
getting_started
|
||||
configuration
|
||||
integration
|
||||
examples
|
||||
|
|
|
@ -7,7 +7,83 @@ The main module is named ``ibex_top`` and can be found in ``ibex_top.sv``.
|
|||
Note that the core logic is split-out from the register file and RAMs under ``ibex_top``.
|
||||
This is to facilitate a dual-core lockstep implementation (see :ref:`security`).
|
||||
|
||||
Below, the instantiation template is given and the parameters and interfaces are described.
|
||||
Register File
|
||||
-------------
|
||||
|
||||
Ibex comes with three different register file implementations that can be selected using the enumerated parameter ``RegFile`` defined in :file:`rtl/ibex_pkg.sv`.
|
||||
Depending on the target technology, either the flip-flop-based ("ibex_pkg::RegFileFF", default), the latch-based ("ibex_pkg::RegFileLatch") or an FPGA-targeted ("ibex_pkg::RegFileFPGA") implementation should be selected.
|
||||
For more information about the three register file implementations and their trade-offs, check out :ref:`register-file`.
|
||||
|
||||
Identification CSRs
|
||||
-------------------
|
||||
|
||||
The RISC-V Privileged Architecture specifies several read-only CSRs that identify the vendor and micro-architecture of a CPU.
|
||||
These are ``mvendorid``, ``marchid`` and ``mimpid``.
|
||||
The fixed, read-only values for these CSRs are defined in :file:`rtl/ibex_pkg.sv`.
|
||||
Implementers should carefully consider appropriate values for these registers.
|
||||
Ibex, as an open source implementation, has an assigned architecture ID (``marchid``) of 22.
|
||||
(Allocations are specified in `marchid.md of the riscv-isa-manual repository <https://github.com/riscv/riscv-isa-manual/blob/master/marchid.md>`_.)
|
||||
If significant changes are made to the micro-architecture a different architecture ID should be used.
|
||||
The vendor ID and implementation ID (``mvendorid`` and ``mimpid``) both read as 0 by default, meaning non-implemented.
|
||||
Implementers may wish to use other values here.
|
||||
Please see the RISC-V Privileged Architecture specification for more details on what these IDs represent and how they should be chosen.
|
||||
|
||||
.. _integration-prims:
|
||||
|
||||
Primitives
|
||||
----------
|
||||
|
||||
Ibex uses a number of primitive modules (that are held outside the :file:`rtl/` which contains the Ibex RTL).
|
||||
Full implementations of these primitives are provided in the Ibex repository but implementors may wish to provide their own implementations.
|
||||
Some of the primitives are only used for specific Ibex configurations so can be ignored/removed if you're not using one of those configurations.
|
||||
|
||||
The mandatory primitives (used by all configurations) are:
|
||||
* ``prim_buf`` - A buffer, used to ensure security critical logic isn't optimized out in synthesis (by applying suitable constraints to prim_buf).
|
||||
In configurations where ``SecureIbex == 0`` it must exist but can be implemented as a straight passthrough.
|
||||
* ``prim_clock_gating`` - A clock gate.
|
||||
|
||||
The configuration dependent primitives are:
|
||||
* ``prim_clock_mux2`` - A clock mux, used by the lockstep duplicate core.
|
||||
Required where ``SecureIbex == 1``.
|
||||
* ``prim_flop`` - A flip flop, used to ensure security critical logic isn't optimized out in synthesis (by applying suitable constraints to prim_flop).
|
||||
Required where ``SecureIbex == 1``.
|
||||
* ``prim_ram_1p`` - A single ported RAM.
|
||||
Required where ``ICache == 1``.
|
||||
* ``prim_ram_1p_scr`` - A single ported RAM which scrambles its contents with cryptographic primitives.
|
||||
Required where ``ICache == 1`` and ``SecureIbex == 1``.
|
||||
* ``prim_lfsr`` - Linear feedback shift register, used for pseudo random number generation for dummy instruction insertion.
|
||||
Required where ``SecureIbex == 1``.
|
||||
* ``prim_onehot_check`` - Checks a onehot signal is correct, for detecting fault injection attacks.
|
||||
Required where ``SecureIbex == 1``.
|
||||
* ``prim_secded_X`` - Various primitives to encode and decode SECDED (single error correct, double error detect) error detection and correction codes.
|
||||
Required where ``SecureIbex == 1``.
|
||||
|
||||
Primitives exclusively used by other primitives:
|
||||
* ``prim_present`` / ``prim_prince`` / ``prim_subst_perm`` - Cryptographic primitives used by ``prim_ram_1p_scr``.
|
||||
* ``prim_ram_1p_adv`` - Wrapper around ``prim_ram_1p`` that adds support for ECC, used by ``prim_ram_1p_scr``.
|
||||
|
||||
.. _integration-fusesoc-files:
|
||||
|
||||
RTL File List
|
||||
-------------
|
||||
|
||||
Ibex flows use `FuseSoC <https://github.com/olofk/fusesoc>`_ to gather needed RTL files and run builds.
|
||||
If you want to use Ibex without FuseSoC the following FuseSoC command will copy all the needed files into a build directory.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
fusesoc --cores-root . run --target=lint --setup --build-root ./build/ibex_out lowrisc:ibex:ibex_top
|
||||
|
||||
FuseSoC uses Python and it can be installed using pip.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip3 install -U -r python-requirements.txt
|
||||
|
||||
Ibex uses a `custom fork of FuseSoC <https://github.com/lowRISC/fusesoc/tree/ot>`_, so you must install it via this method rather than installing FuseSoC separately.
|
||||
|
||||
The RTL will be in :file:`./build/ibex_out/src` which is further divided into different sub-directories.
|
||||
A file list containing paths to all of the RTL files can be found in :file:`./build/ibex_out/ibex-verilator/lowrisc_ibex_ibex_top_0.1.vc`.
|
||||
|
||||
Instantiation Template
|
||||
----------------------
|
||||
|
@ -32,6 +108,8 @@ Instantiation Template
|
|||
.RndCnstLfsrSeed ( ibex_pkg::RndCnstLfsrSeedDefault ),
|
||||
.RndCnstLfsrPerm ( ibex_pkg::RndCnstLfsrPermDefault ),
|
||||
.DbgTriggerEn ( 0 ),
|
||||
.DmBaseAddr ( 32'h1A110000 ),
|
||||
.DmAddrMask ( 32'h00000FFF ),
|
||||
.DmHaltAddr ( 32'h1A110800 ),
|
||||
.DmExceptionAddr ( 32'h1A110808 )
|
||||
) u_top (
|
||||
|
@ -122,26 +200,23 @@ Parameters
|
|||
| | | | "ibex_pkg::RegFileFPGA": Register file for FPGA targets |
|
||||
| | | | "ibex_pkg::RegFileLatch": Latch-based register file for ASIC targets |
|
||||
+------------------------------+---------------------+------------+-----------------------------------------------------------------------+
|
||||
| ``BranchTargetALU`` | bit | 0 | *EXPERIMENTAL* - Enables branch target ALU removing a stall |
|
||||
| | | | cycle from taken branches |
|
||||
| ``BranchTargetALU`` | bit | 0 | Enables branch target ALU removing a stall cycle from taken branches |
|
||||
+------------------------------+---------------------+------------+-----------------------------------------------------------------------+
|
||||
| ``WritebackStage`` | bit | 0 | *EXPERIMENTAL* - Enables third pipeline stage (writeback) |
|
||||
| | | | improving performance of loads and stores |
|
||||
| ``WritebackStage`` | bit | 0 | Enables third pipeline stage (writeback) improving performance of |
|
||||
| | | | loads and stores |
|
||||
+------------------------------+---------------------+------------+-----------------------------------------------------------------------+
|
||||
| ``ICache`` | bit | 0 | *EXPERIMENTAL* Enable instruction cache instead of prefetch |
|
||||
| | | | buffer |
|
||||
| ``ICache`` | bit | 0 | Enable instruction cache instead of prefetch buffer |
|
||||
+------------------------------+---------------------+------------+-----------------------------------------------------------------------+
|
||||
| ``ICacheECC`` | bit | 0 | *EXPERIMENTAL* Enable SECDED ECC protection in ICache (if |
|
||||
| | | | ICache == 1) |
|
||||
| ``ICacheECC`` | bit | 0 | Enable SECDED ECC protection in ICache (if ICache == 1) |
|
||||
+------------------------------+---------------------+------------+-----------------------------------------------------------------------+
|
||||
| ``ICacheScramble`` | bit | 0 | *EXPERIMENTAL* Enabling this parameter replaces tag and data RAMs of |
|
||||
| | | | ICache with scrambling RAM primitives. |
|
||||
| ``ICacheScramble`` | bit | 0 | Enabling this parameter replaces tag and data RAMs of ICache with |
|
||||
| | | | scrambling RAM primitives. |
|
||||
+------------------------------+---------------------+------------+-----------------------------------------------------------------------+
|
||||
| ``BranchPrediction`` | bit | 0 | *EXPERIMENTAL* Enable Static branch prediction |
|
||||
+------------------------------+---------------------+------------+-----------------------------------------------------------------------+
|
||||
| ``SecureIbex`` | bit | 0 | *EXPERIMENTAL* Enable various additional features targeting |
|
||||
| | | | secure code execution. Note: SecureIbex == 1'b1 and |
|
||||
| | | | RV32M == ibex_pkg::RV32MNone is an illegal combination. |
|
||||
| ``SecureIbex`` | bit | 0 | Enable various additional features targeting secure code execution. |
|
||||
| | | | Note: SecureIbex == 1'b1 and RV32M == ibex_pkg::RV32MNone is an |
|
||||
| | | | illegal combination. |
|
||||
+------------------------------+---------------------+------------+-----------------------------------------------------------------------+
|
||||
| ``RndCnstLfsrSeed`` | lfsr_seed_t | see above | Set the starting seed of the LFSR used to generate dummy instructions |
|
||||
| | | | (only relevant when SecureIbex == 1'b1) |
|
||||
|
@ -151,6 +226,10 @@ Parameters
|
|||
+------------------------------+---------------------+------------+-----------------------------------------------------------------------+
|
||||
| ``DbgTriggerEn`` | bit | 0 | Enable debug trigger support (one trigger only) |
|
||||
+------------------------------+---------------------+------------+-----------------------------------------------------------------------+
|
||||
| ``DmBaseAddr`` | int | 0x1A110000 | Base address of the Debug Module |
|
||||
+------------------------------+---------------------+------------+-----------------------------------------------------------------------+
|
||||
| ``DmAddrMask`` | int | 0x1A110000 | Address mask of the Debug Module |
|
||||
+------------------------------+---------------------+------------+-----------------------------------------------------------------------+
|
||||
| ``DmHaltAddr`` | int | 0x1A110800 | Address to jump to when entering Debug Mode |
|
||||
+------------------------------+---------------------+------------+-----------------------------------------------------------------------+
|
||||
| ``DmExceptionAddr`` | int | 0x1A110808 | Address to jump to when an exception occurs while in Debug Mode |
|
||||
|
@ -215,7 +294,7 @@ Interfaces
|
|||
| | | | instructions in the ID/EX and WB |
|
||||
| | | | stages have finished. A multi-bit |
|
||||
| | | | encoding scheme is used. See |
|
||||
| | | | `FetchEnableOn` / `FetchEnableOff` in |
|
||||
| | | | `IbexMuBiOn` / `IbexMuBiOff` in |
|
||||
| | | | :file:`rtl/ibex_pkg.sv` |
|
||||
+----------------------------+-------------------------+-----+----------------------------------------+
|
||||
| ``core_sleep_o`` | 1 | out | Core in WFI with no outstanding data |
|
||||
|
|
|
@ -8,6 +8,7 @@ The following tools are known to work with the RTL code of Ibex.
|
|||
Please `file an issue <https://github.com/lowRISC/ibex/issues>`_ if you experience problems with any of the listed tools, or if you have successfully used a tool with Ibex which is not listed here.
|
||||
|
||||
- Synopsys Design Compiler
|
||||
- Cadence Genus
|
||||
- Xilinx Vivado, version |tool_requirements.vivado| and up.
|
||||
- Verilator, version |tool_requirements.verilator| and up.
|
||||
- Synopsys VCS, version at least |tool_requirements.vcs|.
|
||||
|
|
|
@ -16,16 +16,13 @@ Only VCS is supported as a simulator, though no VCS specific functionality is re
|
|||
To run the co-simulation system, a particular version of Spike is required (see the Setup and Usage section, below).
|
||||
|
||||
The RISC-V Formal Interface (RVFI) is used to provide information about retired instructions and instructions that produce synchronous traps for checking.
|
||||
The RVFI has been extended to provide interrupt and debug information and the value of the ``mcycle`` CSR.
|
||||
The RVFI has been extended to provide interrupt and debug information and the value of various CSRs that are harder to model (e.g. ``mcycle``).
|
||||
These extended signals have the prefix ``rvfi_ext``
|
||||
|
||||
The co-simulation system is EXPERIMENTAL.
|
||||
It is disabled by default in the UVM DV environment currently, however it is intended to become the primary checking method for the UVM testbench.
|
||||
|
||||
Setup and Usage
|
||||
---------------
|
||||
|
||||
Clone the `lowRISC fork of Spike <https://github.com/lowRISC/riscv-isa-sim>`_ and check out the ``ibex-cosim-v0.3`` tag.
|
||||
Clone the `lowRISC fork of Spike <https://github.com/lowRISC/riscv-isa-sim>`_ and check out the ``ibex-cosim-v0.5`` tag.
|
||||
Other, later, versions called ``ibex-cosim-v*`` may also work but there's no guarantee of backwards compatibility.
|
||||
Follow the Spike build instructions to build and install Spike.
|
||||
The ``--enable-commitlog`` and ``--enable-misaligned`` options must be passed to ``configure``.
|
||||
|
@ -136,7 +133,15 @@ The DV environment is responsible for determining when to call ``set_mip``, ``se
|
|||
|
||||
The state of the incoming interrupts and debug request is sampled when an instruction moves from IF to ID/EX.
|
||||
The sampled state is tracked with the rest of the RVFI pipeline and used to call ``set_mip``, ``set_debug_req`` and ``set_nmi`` when the instruction is output by the RVFI.
|
||||
See the comments in :file:`rtl/ibex_core.sv`, around the ``new_debug_req``, ``new_nmi`` and ``new_irq`` signals for further details.
|
||||
|
||||
A complication occurs when more than one interrupt or debug requests occur between individual instruction fetches.
|
||||
One interrupt or debug request may take priority over another when they all occur together but when they occur in time is important as well.
|
||||
If interrupt and debug request notification is associated exclusively with retired instructions the co-simulation system cannot correctly prioritise multiple interrupts and debug requests.
|
||||
To deal with this the RVFI can also signal an interrupt event not associated with an instruction by setting ``rvfi_ext_irq_valid`` without setting ``rvfi_valid``.
|
||||
When this is set the interrupt related RVFI signals are valid and provide the interrupt state.
|
||||
The RVFI is used in this way, as opposed to a separate notification interface, so the interrupt notifications are ordered relative to the retired instructions.
|
||||
|
||||
See the comments in :file:`rtl/ibex_core.sv`, around the ``new_debug_req``, ``new_nmi``, ``new_irq`` and ``rvfi_irq_valid`` signals for further details.
|
||||
|
||||
Memory Access Checking and Bus Errors
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
@ -60,12 +60,12 @@ Some categories are just a single instruction, which is named without further de
|
|||
* **CSRAccess** - Any instruction from Zicsr.
|
||||
* **EBreakDbg**/**EBreakExc** - An ``EBREAK`` instruction that either enters debug mode (Dbg) or causes an exception (Exc).
|
||||
Which occurs depends upon the setting of ``dcsr.ebreakm`` / ``dcsr.ebreaku`` combined with the privilege level of executed instruction.
|
||||
* ``ECALL``
|
||||
* ``MRET``
|
||||
* ``DRET``
|
||||
* ``WFI``
|
||||
* ``FENCE``
|
||||
* ``FENCE.I``
|
||||
* **ECall** - ``ECALL`` is an environment call used for escalation of privilege.
|
||||
* **MRet** - ``MRET`` return out of M-mode
|
||||
* **DRet** - ``DRET`` ruturn from debug mode.
|
||||
* **WFI** - wait for interrupt.
|
||||
* **Fence** - ``FENCE`` memory fence on the data side.
|
||||
* **FenceI** - ``FENCE.I`` instruction fence instruction.
|
||||
* **FetchError** - Any instruction that saw a fetch error.
|
||||
* **CompressedIllegal** - Any compressed instruction with an illegal encoding.
|
||||
* **UncompressedIllegal** - Any uncompressed instruction with an illegal encoding.
|
||||
|
@ -177,36 +177,35 @@ Each pipeline stage has some associated state.
|
|||
* Controller (within ID stage) state machine states
|
||||
|
||||
* ``cp_controller_fsm`` - Possible transitions between these states.
|
||||
Those marked with a '*' are of particular interest and should be crossed with instruction categories and other coverpoints as appropriate to fully explore the transitions.
|
||||
|
||||
* ``RESET`` -> ``BOOT_SET``
|
||||
* ``BOOT_SET`` -> ``FIRST_FETCH``
|
||||
* ``FIRST_FETCH`` -> ``DECODE``
|
||||
* ``FIRST_FETCH`` -> ``IRQ_TAKEN``
|
||||
* ``FIRST_FETCH`` -> ``DBG_TAKEN_IF``
|
||||
* ``DECODE`` -> ``FLUSH`` *
|
||||
* ``DECODE`` -> ``DBG_TAKEN_IF`` *
|
||||
* ``DECODE`` -> ``IRQ_TAKEN`` *
|
||||
* ``DECODE`` -> ``FLUSH``
|
||||
* ``DECODE`` -> ``DBG_TAKEN_IF``
|
||||
* ``DECODE`` -> ``IRQ_TAKEN``
|
||||
* ``IRQ_TAKEN`` -> ``DECODE``
|
||||
* ``DBG_TAKEN_IF`` -> ``DECODE``
|
||||
* ``DBG_TAKEN_ID`` -> ``DECODE``
|
||||
* ``FLUSH`` -> ``DECODE`` *
|
||||
* ``FLUSH`` -> ``DECODE``
|
||||
* ``FLUSH`` -> ``DBG_TAKEN_ID``
|
||||
* ``FLUSH`` -> ``WAIT_SLEEP``
|
||||
* ``FLUSH`` -> ``IRQ_TAKEN`` *
|
||||
* ``FLUSH`` -> ``DBG_TAKEN_IF`` *
|
||||
* ``FLUSH`` -> ``DBG_TAKEN_IF``
|
||||
* ``WAIT_SLEEP`` -> ``SLEEP``
|
||||
* ``SLEEP`` -> ``FIRST_FETCH``
|
||||
|
||||
Exceptions/Interrupts/Debug
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Exceptions, interrupts and debug entry can all cause control flow changes combined with CSR writes and privilege level changes and work quite similarly within the controller but not identically.
|
||||
Furthermore they can all occur together and must be appropriately prioritised (consider a instruction with hardware trigger point matching it, that causes some exception and an interrupt is raised the cycle it enters the ID/EX stage)
|
||||
Furthermore they can all occur together and must be appropriately prioritised (consider an instruction with hardware trigger point matching it, that causes some exception and an interrupt is raised the cycle it enters the ID/EX stage).
|
||||
|
||||
* Exception from instruction fetch error (covered by the **FetchError** instruction category).
|
||||
* ``pmp_iside_mode_cross`` - Exception from instruction PMP violation.
|
||||
* Exception from illegal instruction (covered by the illegal instruction categories).
|
||||
* ``cp_ls_error_exception`` - Exception from memory fetch error.
|
||||
* ``cp_ls_pmp_exception`` - Load store unit exception from PMP.
|
||||
* ``pmp_dside_mode_cross`` - Exception from memory access PMP violation.
|
||||
* Unaligned memory access
|
||||
|
||||
|
@ -222,11 +221,10 @@ Furthermore they can all occur together and must be appropriately prioritised (c
|
|||
|
||||
* ``cp_debug_req`` - External debug request.
|
||||
* ``cp_single_step_taken`` - Instruction executed when debug single step enabled.
|
||||
* ``cp_single_step_exception`` - Single step over an instruction that takes an exception.
|
||||
* ``cp_insn_trigger_enter_debug`` - Instruction matches hardware trigger point.
|
||||
|
||||
* ``cp_insn_trigger_exception`` - Instruction matching trigger point causes exception
|
||||
|
||||
* ``cp_debug_mode`` - Ibex operating in debug mode.
|
||||
* ``cp_debug_wakeup`` - Ibex wakes up after being halted from debug request.
|
||||
* ``irq_wfi_cross``, ``debug_wfi_cross`` - Debug and Interrupt whilst sleeping with WFI
|
||||
|
||||
* Cover with global interrupts enabled and disabled
|
||||
|
@ -266,11 +264,11 @@ PMP
|
|||
* ``cp_pmp_iside_region_override``, ``cp_pmp_iside2_region_override``, ``cp_pmp_dside_region_override`` - Higher priority entry allows access that lower priority entry prevents.
|
||||
* ``pmp_instr_edge_cross`` - Compressed instruction access (16-bit) passes PMP but 32-bit access at same address crosses PMP region boundary.
|
||||
|
||||
* Each field of mssecfg enabled/disabled with relevant functionality tested.
|
||||
* Each field of mssecfg enabled/disabled, as well as written to using a CSR write, with relevant functionality tested.
|
||||
|
||||
* RLB - rule locking bypass.
|
||||
|
||||
* ``cp_edit_locked_pmpcfg``,``cp_edit_locked_pmpaddr`` - Modify locked region with RLB set.
|
||||
* ``cp_edit_locked_pmpcfg``, ``cp_edit_locked_pmpaddr`` - Modify locked region with RLB set.
|
||||
* ``rlb_csr_cross`` - Try to enable RLB when RLB is disabled and locked regions present.
|
||||
|
||||
* MMWP - machine mode whitelist policy.
|
||||
|
@ -280,16 +278,21 @@ PMP
|
|||
|
||||
* MML - machine mode lockdown policy.
|
||||
|
||||
* ``rlb_csr_cross`` - Try to disable when enabled.
|
||||
* ``mml_sticky_cross`` - Try to disable when enabled.
|
||||
|
||||
* Access close to PMP region modification that allows/disallows that access.
|
||||
|
||||
* ``pmp_wr_exec_region`` - Explores behaviour around adding executable regions when MML is enabled.
|
||||
Cross of current region configuration with region configuration that is being written and RLB setting.
|
||||
It only considers regions that aren't currently executable with writes attempted to make them executable.
|
||||
Non MML configurations are not sampled.
|
||||
|
||||
CSRs
|
||||
^^^^
|
||||
Basic read/write functionality must be tested on all implemented CSRs.
|
||||
|
||||
* ``cp_csr_read_only`` - Read from CSR.
|
||||
* ``cp_csr_write`` - Write to CSR.
|
||||
* ``cp_csr_read_only`` - Read from CSR, there is also ``cp_csr_invalid_read_only`` for illegal CSRs.
|
||||
* ``cp_csr_write`` - Write to CSR, there is also ``cp_csr_invalid_write`` for illegal CSRs.
|
||||
|
||||
* Write to read only CSR.
|
||||
Covered by ensuring ``cp_csr_write`` is seen for read-only CSRs
|
||||
|
@ -300,17 +303,96 @@ Basic read/write functionality must be tested on all implemented CSRs.
|
|||
* Access to CSR disallowed due to privilege levels/debug mode
|
||||
Covered by ensuring within the crosses
|
||||
|
||||
* ``cp_ignored_csrs_ro``, ``cp_ignored_csrs_w`` - Read and write from/to an unimplemented CSR
|
||||
|
||||
CSRs addresses do not need to be crossed with the variety of CSR instructions as these all use the same basic read & write interface into ``ibex_cs_registers``.
|
||||
Coverage of the above points will be sampled at the ``ibex_cs_registers`` interface (as opposed to sampling CSR instructions).
|
||||
|
||||
Security Countermeasures
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
For more detail about each security countermeasure in Ibex see :ref:`security`
|
||||
|
||||
* ``cp_data_ind_timing`` - Enabling/Disabling "Data Independent Timing" feature.
|
||||
|
||||
* ``cp_data_ind_timing_instr`` - Executing each instruction category while data independent timing feature is enabled.
|
||||
|
||||
* ``cp_dummy_instr_en`` - Enabling/Disabling "Dummy Instruction Insertion" feature.
|
||||
|
||||
* ``cp_dummy_instr_mask`` - Frequency of injection for the dummy instructions.
|
||||
|
||||
* ``cp_dummy_instr_type`` - Type of the injected dummy instruction.
|
||||
|
||||
* ``cp_dummy_instr`` - Executing each instruction category while dummy instruction insertion feature is enabled.
|
||||
|
||||
* ``cp_dummy_instr_if_stage`` - The IF stage handles a dummy instruction.
|
||||
|
||||
* ``cp_dummy_instr_id_stage`` - The ID/EX stage handles a dummy instruction.
|
||||
|
||||
* ``cp_dummy_instr_wb_stage`` - The WB stage handles a dummy instruction.
|
||||
|
||||
* ``cp_rf_a_ecc_err``, ``cp_rf_b_ecc_err`` - Register file integrity (ECC) fault is seen for port A/B.
|
||||
|
||||
* ``cp_icache_ecc_err`` - ICache has seen an integrity (ECC) fault.
|
||||
|
||||
* ``cp_mem_load_ecc_err`` - An ECC error has been seen on a load response
|
||||
|
||||
* ``cp_mem_store_ecc_err`` - An ECC error has been seen on a store response
|
||||
|
||||
* ``cp_lockstep_err`` - Lockstep glitch fault seen.
|
||||
|
||||
* ``cp_rf_we_glitch_err`` - Register file write enable glitch fault seen.
|
||||
|
||||
* ``cp_pc_mismatch_err`` - PC mismatch error seen.
|
||||
|
||||
The :ref:`security features Ibex implements <security>` are given specific security countermeasure names in OpenTitan (see 'Security Countermeasures' in the `Comportability Definition and Specification <https://opentitan.org/book/doc/contributing/hw/comportability/index.html#security-countermeasures>`_ documentation section).
|
||||
The mapping between security countermeasures and coverpoints that demonstrate it being used is given below.
|
||||
|
||||
+--------------------------------+-------------------------------------------------------+
|
||||
| Security Countermeasure | Coverpoint(s) |
|
||||
+================================+=======================================================+
|
||||
| BUS.INTEGRITY | ``cp_mem_load_ecc_err`` ``cp_mem_store_ecc_err`` |
|
||||
+--------------------------------+-------------------------------------------------------+
|
||||
| SCRAMBLE.KEY.SIDELOAD | ``FENCE.I`` of ``cp_id_instr_category`` |
|
||||
+--------------------------------+-------------------------------------------------------+
|
||||
| CORE.DATA_REG_SW.SCA | ``cp_data_ind_timing`` ``cp_data_ind_timining_instr`` |
|
||||
+--------------------------------+-------------------------------------------------------+
|
||||
| PC.CTRL_FLOW.CONSISTENCY | ``cp_pc_mismatch_err`` |
|
||||
+--------------------------------+-------------------------------------------------------+
|
||||
| CTRL_FLOW.UNPREDICTABLE | ``cp_dummy_instr`` and related coverpoints |
|
||||
+--------------------------------+-------------------------------------------------------+
|
||||
| DATA_REG_SW.INTEGRITY | ``cp_rf_a_ecc_err`` ``cp_rf_b_ecc_err`` |
|
||||
+--------------------------------+-------------------------------------------------------+
|
||||
| DATA_REG_SW.GLITCH_DETECT | ``cp_rf_we_glitch_err`` |
|
||||
+--------------------------------+-------------------------------------------------------+
|
||||
| LOGIC.SHADOW | ``cp_lockstep_err`` |
|
||||
+--------------------------------+-------------------------------------------------------+
|
||||
| FETCH.CTRL.LC_GATED | ``cp_fetch_enable`` |
|
||||
+--------------------------------+-------------------------------------------------------+
|
||||
| EXCEPTION.CTRL_FLOW.LOCAL_ESC | ``cp_double_fault`` |
|
||||
+--------------------------------+-------------------------------------------------------+
|
||||
| EXCEPTION.CTRL_FLOW.GLOBAL_ESC | ``cp_double_fault`` |
|
||||
+--------------------------------+-------------------------------------------------------+
|
||||
| ICACHE.MEM.SCRAMBLE | ``FENCE.I`` of ``cp_id_instr_category`` |
|
||||
+--------------------------------+-------------------------------------------------------+
|
||||
| ICACHE.MEM.INTEGRITY | ``cp_icache_ecc_err`` |
|
||||
+--------------------------------+-------------------------------------------------------+
|
||||
|
||||
Memory Interface Behaviour
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Covering different scenarios around timing of memory requests and responses and
|
||||
related behaviour
|
||||
|
||||
* ``cp_dmem_response_latency``/``cp_imem_response_latency`` - Latency of response from request for dmem and imem.
|
||||
Separated into two bins ``single_cycle`` (immediate response after request) and ``multi_cycle`` (all other latencies).
|
||||
* ``dmem_req_gnt_valid``/``imem_req_gnt_rvalid`` - Request, grant and rvalid all seen in the same cycle for dmem and imem.
|
||||
This means a response is seen the same cycle a new request is being granted.
|
||||
|
||||
|
||||
Miscellaneous
|
||||
^^^^^^^^^^^^^
|
||||
Various points of interest do not fit into the categories above.
|
||||
|
||||
* ``instr_unstalled`` - Instruction unstalled - Cover the cycle an instruction is unstalled having just been stalled.
|
||||
* ``cp_icache_enable`` - Enabling/Disabling ICache.
|
||||
* ``cp_fetch_enable`` - Fetch enabled and disabled via top-level ``fetch_enable_i`` input.
|
||||
|
||||
Cross Coverage
|
||||
--------------
|
||||
|
@ -354,3 +436,11 @@ There must be a documented reason a particular bin is added to the illegal or ig
|
|||
* ``pmp_iside_priv_bits_cross``, ``pmp_iside2_priv_bits_cross``, ``pmp_dside_priv_bits_cross``, PMP regions x permissions x access fail/pass x privilege level
|
||||
|
||||
* Three crosses, one for each PMP channel (instruction, instruction 2 and data).
|
||||
|
||||
* ``dummy_instr_config_cross`` - Dummy Instruction Type x Dummy Instruction Insertion Frequency to explore all possible configurations.
|
||||
|
||||
* ``rf_ecc_err_cross`` - ECC Error on Port A x ECC Error on Port B to explore all possible combinations of reported ECC errors.
|
||||
|
||||
* ``debug_req_dummy_instr_{if,id,wb}_stage_cross`` - The IF, ID/EX, or WB stage handles a dummy instruction while a debug request arrives.
|
||||
|
||||
* ``irq_pending_dummy_instr_{if,id,wb}_stage_cross`` - The IF, ID/EX, or WB stage handles a dummy instruction while an IRQ is pending.
|
||||
|
|
|
@ -72,7 +72,7 @@ Ibex implements all the Control and Status Registers (CSRs) listed in the follow
|
|||
+---------+--------------------+--------+-----------------------------------------------+
|
||||
| 0x7B3 | ``dscratch1`` | RW | Debug Scratch Register 1 |
|
||||
+---------+--------------------+--------+-----------------------------------------------+
|
||||
| 0x7C0 | ``cpuctrl`` | WARL | CPU Control Register (Custom CSR) |
|
||||
| 0x7C0 | ``cpuctrlsts`` | WARL | CPU Control and Status Register (Custom CSR) |
|
||||
+---------+--------------------+--------+-----------------------------------------------+
|
||||
| 0x7C1 | ``secureseed`` | WARL | Security feature random seed (Custom CSR) |
|
||||
+---------+--------------------+--------+-----------------------------------------------+
|
||||
|
@ -113,7 +113,7 @@ Machine Status (mstatus)
|
|||
|
||||
CSR Address: ``0x300``
|
||||
|
||||
Reset Value: ``0x0000_1800``
|
||||
Reset Value: ``0x0000_0080``
|
||||
|
||||
+-------+-----+---------------------------------------------------------------------------------+
|
||||
| Bit# | R/W | Description |
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
Debug Support
|
||||
=============
|
||||
|
||||
Ibex offers support for execution-based debug according to the `RISC-V Debug Specification <https://riscv.org/specifications/debug-specification/>`_, version 0.13.
|
||||
Ibex offers support for execution-based debug according to the `RISC-V Debug Specification <https://github.com/riscv/riscv-debug-spec/blob/0.13-test-release/riscv-debug-spec.pdf>`_, version 0.13.
|
||||
|
||||
|
||||
.. note::
|
||||
|
@ -32,6 +32,10 @@ Parameters
|
|||
+---------------------+-----------------------------------------------------------------+
|
||||
| Parameter | Description |
|
||||
+=====================+=================================================================+
|
||||
| ``DmBaseAddr`` | Base address of the Debug Module |
|
||||
+---------------------+-----------------------------------------------------------------+
|
||||
| ``DmAddrMask`` | Address mask of the Debug Module |
|
||||
+---------------------+-----------------------------------------------------------------+
|
||||
| ``DmHaltAddr`` | Address to jump to when entering Debug Mode |
|
||||
+---------------------+-----------------------------------------------------------------+
|
||||
| ``DmExceptionAddr`` | Address to jump to when an exception occurs while in Debug Mode |
|
||||
|
|
|
@ -76,13 +76,13 @@ Internal interrupts are considered to be non-recoverable in general.
|
|||
Specific details of how an internal interrupt relates to the event that triggers it are listed below.
|
||||
Given these details it may be possible for software to recover from an internal interrupt under specific circumstances.
|
||||
|
||||
The possible ``mcause`` values for an internal interrupt as listed below:
|
||||
The possible ``mcause`` values for an internal interrupt are listed below:
|
||||
|
||||
+-------------+-------------------------------------------------------------------------------------------------------------+
|
||||
| ``mcause`` | Description |
|
||||
+-------------+-------------------------------------------------------------------------------------------------------------+
|
||||
| 0xFFFFFFE0 | Load integrity error internal interrupt. |
|
||||
| | Only generated when SecureIbex == 0. |
|
||||
| | Only generated when SecureIbex == 1. |
|
||||
| | ``mtval`` gives the faulting address. |
|
||||
| | The interrupt will be taken at most one instruction after the faulting load. |
|
||||
| | In particular a load or store immediately after a faulting load may execute before the interrupt is taken. |
|
||||
|
@ -125,6 +125,9 @@ Ibex can trigger an exception due to the following exception causes:
|
|||
|
||||
The illegal instruction exception, instruction access fault, LSU error exceptions and ECALL instruction exceptions cannot be disabled and are always active.
|
||||
|
||||
Note that Ibex cannot generated an 'instruction address misaligned' exception as all configurations implement the 'C' extension.
|
||||
Under the RISC-V architecture it is simply not possible to branch or otherwise start executing from a PC that isn't 16-bit aligned.
|
||||
So with 'C' implemented all possible PCs are appropriately aligned.
|
||||
|
||||
Nested Interrupt/Exception Handling
|
||||
-----------------------------------
|
||||
|
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 76 KiB |
File diff suppressed because it is too large
Load diff
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 118 KiB |
|
@ -22,6 +22,7 @@ It describes the design in detail, discusses the verification approach and the r
|
|||
debug
|
||||
tracer
|
||||
verification
|
||||
verification_stages
|
||||
cosim
|
||||
testplan
|
||||
coverage_plan
|
||||
|
|
|
@ -56,7 +56,7 @@ Arithmetic Logic Unit (ALU)
|
|||
---------------------------
|
||||
Source File: :file:`rtl/ibex_alu.sv`
|
||||
|
||||
The Arithmetic Logic Logic (ALU) is a purely combinational block that implements operations required for the Integer Computational Instructions and the comparison operations required for the Control Transfer Instructions in the RV32I RISC-V Specification.
|
||||
The Arithmetic Logic Unit (ALU) is a purely combinational block that implements operations required for the Integer Computational Instructions and the comparison operations required for the Control Transfer Instructions in the RV32I RISC-V Specification.
|
||||
Other blocks use the ALU for the following tasks:
|
||||
|
||||
* Mult/Div uses it to perform addition as part of the multiplication and division algorithms
|
||||
|
|
|
@ -26,7 +26,7 @@ See Multi- and Single-Cycle Instructions below for the details.
|
|||
Third Pipeline Stage
|
||||
--------------------
|
||||
Ibex can be configured to have a third pipeline stage (Writeback) which has major effects on performance and instruction behaviour.
|
||||
This feature is *EXPERIMENTAL* and the details of its impact are not yet documented here.
|
||||
The details of its impact are not yet documented here.
|
||||
All of the information presented below applies only to the two stage pipeline provided in the default configurations.
|
||||
|
||||
Multi- and Single-Cycle Instructions
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
Physical Memory Protection (PMP)
|
||||
================================
|
||||
|
||||
The Physical Memory Protection (PMP) unit implements region-based memory access checking in-accordance with the RISC-V Privileged Specification, version 1.11 and includes the Trusted Execution Environment (TEE) working group proposal `PMP Enhancements for memory access and execution prevention on Machine mode (Smepmp) version 0.9.3 <https://github.com/riscv/riscv-tee/blob/61455747230a26002d741f64879dd78cc9689323/Smepmp/Smepmp.pdf>`_.
|
||||
The Physical Memory Protection (PMP) unit implements region-based memory access checking in-accordance with the RISC-V Privileged Specification, version 1.12 and implements the `PMP Enhancements for memory access and execution prevention on Machine mode (Smepmp) version 1.0 <https://github.com/riscv/riscv-tee/blob/191b563b08b31cc2974d604a3b670d8666a2e093/Smepmp/Smepmp.pdf>`_ extension.
|
||||
The following configuration parameters are available to control PMP checking:
|
||||
|
||||
+----------------+---------------+----------------------------------------------------------+
|
||||
|
@ -36,9 +36,9 @@ When the granularity is greater than zero, NA4 mode is not available and will be
|
|||
PMP Enhancements
|
||||
----------------
|
||||
|
||||
These are described in more detail in `PMP Enhancements for memory access and execution prevention on Machine mode (Smepmp) version 0.9.3 <https://github.com/riscv/riscv-tee/blob/61455747230a26002d741f64879dd78cc9689323/Smepmp/Smepmp.pdf>`_.
|
||||
These are described in more detail in `PMP Enhancements for memory access and execution prevention on Machine mode (Smepmp) version 1.0 <https://github.com/riscv/riscv-tee/blob/191b563b08b31cc2974d604a3b670d8666a2e093/Smepmp/Smepmp.pdf>`_.
|
||||
If Ibex is configured to include PMP (PMPEnable is not zero) the PMP enhancements are always included.
|
||||
Use of the enhanced behavior is optional, if no writes to ``mseccfg`` occur PMP behavior will remain exactly as specified in the RISC-V privileged specification.
|
||||
Use of the enhanced behavior is optional, if no writes to ``mseccfg`` occur PMP behavior will remain exactly as if Smepmp was not implemented.
|
||||
The enhancements add:
|
||||
|
||||
* A new CSR ``mseccfg`` providing functionality to allow locked regions to be modified and to implement default deny for M-mode accesses.
|
||||
|
@ -50,5 +50,11 @@ Custom Reset Values
|
|||
|
||||
By default all PMP CSRs (include ``mseccfg``) are reset to 0.
|
||||
Some applications may want other reset values.
|
||||
Default reset values are defined in :file:`ibex_pmp_reset_default.svh`.
|
||||
An implementation can either modify this file or define ``IBEX_CUSTOM_PMP_RESET_VALUES`` and place a copy of :file:`ibex_pmp_result_default.svh` in a new file, :file:`ibex_pmp_reset.svh`, changing the values as required and adding the new file to the include path of whatever build flow is being used.
|
||||
Default reset values are defined in :file:`ibex_pkg.sv`.
|
||||
An implementation can either modify this file or pass custom reset values as a module parameter.
|
||||
|
||||
Debug Mode
|
||||
----------
|
||||
|
||||
In debug mode, the PMP allows all accesses to addresses of the Debug Module, as defined by the `DmBaseAddr` and `DmAddrMask` module parameters.
|
||||
This is mandated by the RISC-V Debug Specification (v1.0.0).
|
||||
|
|
|
@ -83,6 +83,22 @@ When Ibex is configured with the SecureIbex parameter, ECC checking is added to
|
|||
This can be useful to detect fault injection attacks since the register file covers a reasonably large area.
|
||||
No attempt is made to correct detected errors, but an internal major alert is signaled for the system to take action.
|
||||
|
||||
Register file write enable glitch detection
|
||||
-------------------------------------------
|
||||
|
||||
When Ibex is configured with the SecureIbex parameter, the write enable signal into the register file is checked to be one-hot.
|
||||
This can be useful to detect fault injection attacks.
|
||||
No attempt is made to correct detected errors, but an internal major alert is signaled for the system to take action.
|
||||
|
||||
Register file read addresses glitch detection
|
||||
-------------------------------------------
|
||||
|
||||
When Ibex is configured with the SecureIbex parameter, the read addresses provided to the register file are converted to one-hot encoded signals, and a one-hot encoded MUX is used to select the register to read from.
|
||||
By using one-hot encoding checkers, glitches in the one-hot encoded signals are detected.
|
||||
Bit-flips inside the plain read addresses before the one-hot conversion happens are detected by the dual core lockstep.
|
||||
This can be useful to detect fault injection attacks.
|
||||
No attempt is made to correct detected errors, but an internal major alert is signaled for the system to take action.
|
||||
|
||||
ICache ECC
|
||||
----------
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
.. _verification:
|
||||
|
||||
Verification
|
||||
============
|
||||
|
||||
|
@ -14,6 +16,14 @@ Overview
|
|||
This is a SV/UVM testbench for verification of the Ibex core, located in `dv/uvm/core_ibex`.
|
||||
At a high level, this testbench uses the open source `RISCV-DV random instruction generator <https://github.com/google/riscv-dv>`_ to generate compiled instruction binaries, loads them into a simple memory model, stimulates the Ibex core to run this program in memory, and then compares the core trace log against a golden model ISS trace log to check for correctness of execution.
|
||||
|
||||
Verification maturity is tracked via :ref:`verification_stages` that are `defined by the OpenTitan project <https://opentitan.org/book/doc/project_governance/development_stages.html#hardware-verification-stages-v>`_.
|
||||
|
||||
Ibex has achieved **V2S** for the ``opentitan`` configuration, broadly this means verification almost complete (over 90% code and functional coverage hit with over 90% regression pass rate with test plan and coverage plan fully implemented) but not yet closed.
|
||||
|
||||
Nightly regression results, including a coverage summary and details of test failures, for the ``opentitan`` Ibex configuration are published at https://ibex.reports.lowrisc.org/opentitan/latest/report.html. Below is a summary of these results:
|
||||
|
||||
.. image:: https://ibex.reports.lowrisc.org/opentitan/latest/summary.svg
|
||||
|
||||
Testbench Architecture
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -80,8 +90,7 @@ In order to run the co-simulation flow, you'll need:
|
|||
+ Some custom CSRs
|
||||
+ Custom NMI behavior
|
||||
|
||||
Ibex verification should work with the Spike version that is tagged as ``ibex-cosim-v0.3``.
|
||||
Other, later, versions called ``ibex-cosim-v*`` may also work but there's no guarantee of backwards compatibility.
|
||||
Ibex verification should work with the Spike version that is tagged as ``ibex-cosim-v0.5``.
|
||||
|
||||
Spike must be built with the ``--enable-commitlog`` and ``--enable-misaligned`` options.
|
||||
``--enable-commitlog`` is needed to produce log output to track the instructions that were executed.
|
||||
|
@ -109,7 +118,7 @@ Once these are installed, you need to set some environment variables to tell the
|
|||
.. _LRSpike: https://github.com/lowRISC/riscv-isa-sim
|
||||
.. _riscv-toolchain-source: https://github.com/riscv/riscv-gnu-toolchain
|
||||
.. _riscv-toolchain-releases: https://github.com/lowRISC/lowrisc-toolchains/releases
|
||||
.. _bitmanip-patches: https://github.com/lowRISC/lowrisc-toolchains#how-to-generate-the-bitmanip-patches
|
||||
.. _bitmanip-patches: https://github.com/lowRISC/lowrisc-toolchains#how-to-generate-the-bitmanip-patch
|
||||
.. _bitmanip: https://github.com/riscv/riscv-bitmanip
|
||||
|
||||
End-to-end RTL/ISS co-simulation flow
|
||||
|
@ -128,6 +137,7 @@ However, this checking model quickly falls apart once situations involving exter
|
|||
In order to provide support for these sorts of scenarios to verify if the core has entered the proper interrupt handler, entered Debug Mode properly, updated any CSRs correctly, and so on, the handshaking mechanism provided by the RISCV-DV instruction generator is heavily used, which effectively allows the core to send status information to the testbench during program execution for any analysis that is required to increase verification effectiveness.
|
||||
This mechanism is explained in detail at https://github.com/google/riscv-dv/blob/master/docs/source/handshake.rst.
|
||||
As a sidenote, the signature address that this testbench uses for the handshaking is ``0x8ffffffc``.
|
||||
|
||||
Additionally, as is mentioned in the RISCV-DV documentation of this handshake, a small set of API tasks are provided in `dv/uvm/core_ibex/tests/core_ibex_base_test.sv <https://github.com/lowRISC/ibex/blob/master/dv/uvm/core_ibex/tests/core_ibex_base_tests.sv>`_ to enable easy and efficient integration and usage of this mechanism in this test environment.
|
||||
To see how this handshake is used during real simulations, look in `dv/uvm/core_ibex/tests/core_ibex_test_lib.sv <https://github.com/lowRISC/ibex/blob/master/dv/uvm/core_ibex/tests/core_ibex_test_lib.sv>`_.
|
||||
As can be seen, this mechanism is extensively used to provide runtime verification for situations involving external debug requests, interrupt assertions, and memory faults.
|
||||
|
@ -135,6 +145,15 @@ To add another layer of correctness checking to the checking already provided by
|
|||
As a result, only the final values contained in every register at the end of the test are compared against each other, since any code executed in the debug ROM and trap handlers should not corrupt register state in the rest of the program.
|
||||
|
||||
The entirety of this flow is controlled by the Makefile found at `dv/uvm/core_ibex/Makefile <https://github.com/lowRISC/ibex/blob/master/dv/uvm/core_ibex/Makefile>`_; here is a list of frequently used commands:
|
||||
=======
|
||||
Additionally, as is mentioned in the RISCV-DV documentation of this handshake, a small set of API tasks are provided in `dv/uvm/core_ibex/tests/core_ibex_base_test.sv <https://github.com/lowRISC/ibex/blob/master/dv/uvm/core_ibex/tests/core_ibex_base_test.sv>`_ to enable easy and efficient integration and usage of this mechanism in this test environment.
|
||||
To see how this handshake is used during real simulations, look in `dv/uvm/core_ibex/tests/core_ibex_test_lib.sv <https://github.com/lowRISC/ibex/blob/master/dv/uvm/core_ibex/tests/core_ibex_test_lib.sv>`_.
|
||||
As can be seen, this mechanism is extensively used to provide runtime verification for situations involving external debug requests, interrupt assertions, and memory faults.
|
||||
To add another layer of correctness checking to the checking already provided by the handshake mechanism, a modified version of the trace log comparison is used, as comparing every register write performed during the entire simulation will lead to an incorrect result since the ISS trace log will not contain any execution information in the debug ROM or in any interrupt handler code.
|
||||
As a result, only the final values contained in every register at the end of the test are compared against each other, since any code executed in the debug ROM and trap handlers should not corrupt register state in the rest of the program.
|
||||
|
||||
The entirety of this flow is controlled by the Makefile found at `dv/uvm/core_ibex/Makefile <https://github.com/lowRISC/ibex/blob/master/dv/uvm/core_ibex/Makefile>`_; here is a list of frequently used commands:
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
|
|
224
doc/03_reference/verification_stages.rst
Normal file
224
doc/03_reference/verification_stages.rst
Normal file
|
@ -0,0 +1,224 @@
|
|||
.. _verification_stages:
|
||||
|
||||
Verification Stages
|
||||
===================
|
||||
|
||||
Ibex is being verified as part of the `OpenTitan <https://www.opentitan.org>`_ project and follows the `verification stages used in OpenTitan <https://opentitan.org/book/doc/project_governance/development_stages.html#hardware-verification-stages-v>`_.
|
||||
The current verification stage of the 'opentitan' configuration of Ibex is **V2S**.
|
||||
The full definition of V2S can be found at the `OpenTitan V2 <https://opentitan.org/book/doc/project_governance/checklist/index.html#v2>`_ and `OpenTitan V2S <https://opentitan.org/book/doc/project_governance/checklist/index.html#v2s>`_ checklists.
|
||||
Other Ibex configurations do not have a formal verification stage at present.
|
||||
|
||||
V1 Checklist
|
||||
------------
|
||||
|
||||
+---------------+------------------------------------+------------+-------------------------------------------------------+
|
||||
| Type | Item | Resolution | Notes |
|
||||
+===============+====================================+============+=======================================================+
|
||||
| Documentation | DV_DOC_DRAFT_COMPLETE | Waived | Plan created, but does not conform to other templates |
|
||||
+---------------+------------------------------------+------------+-------------------------------------------------------+
|
||||
| Documentation | TESTPLAN_COMPLETED | Done | |
|
||||
+---------------+------------------------------------+------------+-------------------------------------------------------+
|
||||
| Testbench | TB_TOP_CREATED | Done | |
|
||||
+---------------+------------------------------------+------------+-------------------------------------------------------+
|
||||
| Testbench | PRELIMINARY_ASSERTION_CHECKS_ADDED | N/A | |
|
||||
+---------------+------------------------------------+------------+-------------------------------------------------------+
|
||||
| Testbench | SIM_TB_ENV_CREATED | Done | |
|
||||
+---------------+------------------------------------+------------+-------------------------------------------------------+
|
||||
| Testbench | SIM_RAL_MODEL_GEN_AUTOMATED | N/A | |
|
||||
+---------------+------------------------------------+------------+-------------------------------------------------------+
|
||||
| Testbench | CSR_CHECK_GEN_AUTOMATED | N/A | |
|
||||
+---------------+------------------------------------+------------+-------------------------------------------------------+
|
||||
| Testbench | TB_GEN_AUTOMATED | N/A | |
|
||||
+---------------+------------------------------------+------------+-------------------------------------------------------+
|
||||
| Tests | SIM_SMOKE_TEST_PASSING | Done | |
|
||||
+---------------+------------------------------------+------------+-------------------------------------------------------+
|
||||
| Tests | SIM_CSR_MEM_TEST_SUITE_PASSING | Done | |
|
||||
+---------------+------------------------------------+------------+-------------------------------------------------------+
|
||||
| Tests | FPV_MAIN_ASSERTIONS_PROVEN | N/A | |
|
||||
+---------------+------------------------------------+------------+-------------------------------------------------------+
|
||||
| Tool Setup | SIM_ALT_TOOL_SETUP | Waived | waived for now, doesn’t follow standard tool flow |
|
||||
+---------------+------------------------------------+------------+-------------------------------------------------------+
|
||||
| Regression | SIM_SMOKE_REGRESSION_SETUP | Done | |
|
||||
+---------------+------------------------------------+------------+-------------------------------------------------------+
|
||||
| Regression | SIM_NIGHTLY_REGRESSION_SETUP | Done | |
|
||||
+---------------+------------------------------------+------------+-------------------------------------------------------+
|
||||
| Regression | FPV_REGRESSION_SETUP | N/A | |
|
||||
+---------------+------------------------------------+------------+-------------------------------------------------------+
|
||||
| Coverage | SIM_COVERAGE_MODEL_ADDED | Done | |
|
||||
+---------------+------------------------------------+------------+-------------------------------------------------------+
|
||||
| Code Quality | TB_LINT_SETUP | Waived | |
|
||||
+---------------+------------------------------------+------------+-------------------------------------------------------+
|
||||
| Integration | PRE_VERIFIED_SUB_MODULES_V1 | N/A | |
|
||||
+---------------+------------------------------------+------------+-------------------------------------------------------+
|
||||
| Review | DESIGN_SPEC_REVIEWED | Done | |
|
||||
+---------------+------------------------------------+------------+-------------------------------------------------------+
|
||||
| Review | TESTPLAN_REVIEWED | Waived | Not done, will be reviewed in V2 |
|
||||
+---------------+------------------------------------+------------+-------------------------------------------------------+
|
||||
| Review | STD_TEST_CATEGORIES_PLANNED | Done | different format than comportable modules |
|
||||
+---------------+------------------------------------+------------+-------------------------------------------------------+
|
||||
| Review | V2_CHECKLIST_SCOPED | Done | |
|
||||
+---------------+------------------------------------+------------+-------------------------------------------------------+
|
||||
|
||||
V2 Checklist
|
||||
------------
|
||||
|
||||
+---------------+-------------------------------------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Type | Item | Resolution | Notes |
|
||||
+===============+=====================================+============+======================================================================================================================================================================+
|
||||
| Documentation | DESIGN_DELTAS_CAPTURED_V2 | Complete | |
|
||||
+---------------+-------------------------------------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Testbench | FUNCTIONAL_COVERAGE_IMPLEMENTED | Complete | |
|
||||
+---------------+-------------------------------------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Testbench | ALL_INTERFACES_EXERCISED | Complete | |
|
||||
+---------------+-------------------------------------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Testbench | ALL_ASSERTION_CHECKS_ADDED | Complete | |
|
||||
+---------------+-------------------------------------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Testbench | SIM_TB_ENV_COMPLETED | Complete | |
|
||||
+---------------+-------------------------------------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Tests | SIM_ALL_TESTS_PASSING | Complete | Note the ``riscv_assorted_traps_interrupts_debug`` test sees many failures (but does have some seeds that pass). |
|
||||
| | | | The test attempts to generally combine many different stimuli and under OpenTitan classification would be considered a V3 test. |
|
||||
+---------------+-------------------------------------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Tests | FPV_ALL_ASSERTIONS_WRITTEN | N/A | No formal applied for non-security features in Ibex. |
|
||||
+---------------+-------------------------------------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Tests | FPV_ALL_ASSUMPTIONS_REVIEWED | N/A | No formal applied for non-security features in Ibex. |
|
||||
+---------------+-------------------------------------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Tests | SIM_FW_SIMULATED | N/A | No ROM or firmware present. |
|
||||
+---------------+-------------------------------------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Regression | SIM_NIGHTLY_REGRESSION_V2 | Complete | Regression run in GitHub Actions only accessible to OpenTitan members. |
|
||||
| | | | Publicly viewable reports on the `OpenTitan regression dashboard <https://reports.opentitan.org/hw/top_earlgrey/dv/summary/latest/report.html>`_ are planned for V3. |
|
||||
+---------------+-------------------------------------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Coverage | SIM_CODE_COVERAGE_V2 | Complete | Coverage results available in nightly regression run. |
|
||||
+---------------+-------------------------------------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Coverage | SIM_FUNCTIONAL_COVERAGE_V2 | Complete | Coverage results available in nightly regression run. |
|
||||
| | | | Note the average grade (the average of coverage % for each individual coverpoint and cross) is used for the 90% figure. |
|
||||
| | | | As the functional coverage contains some very large crosses a simple % of all bins hit biases too much toward these crosses. |
|
||||
+---------------+-------------------------------------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Coverage | FPV_CODE_COVERAGE_V2 | N/A | No formal applied for non-security features in Ibex. |
|
||||
+---------------+-------------------------------------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Coverage | FPV_COI_COVERAGE_V2 | N/A | No formal applied for non-security features in Ibex. |
|
||||
+---------------+-------------------------------------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Integration | PRE_VERIFIED_SUB_MODULES_V2 | Complete | ICache is verified in a seperate testbench. |
|
||||
+---------------+-------------------------------------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Issues | NO_HIGH_PRIORITY_ISSUES_PENDING | Complete | |
|
||||
+---------------+-------------------------------------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Issues | ALL_LOW_PRIORITY_ISSUES_ROOT_CAUSED | Complete | |
|
||||
+---------------+-------------------------------------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Review | DV_DOC_TESTPLAN_REVIEWED | Complete | |
|
||||
+---------------+-------------------------------------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Review | V3_CHECKLIST_SCOPED | Complete | |
|
||||
+---------------+-------------------------------------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
**PMP Testing Note**: A large number of iterations of ``pmp_full_random_test`` are required to meet coverage goals and timed out tests must be included in the coverage collection.
|
||||
This is because of the large cross bins for PMP that aim to explore the full space of possible behaviour.
|
||||
The current strategy of random generation is very inefficient at exploring this space.
|
||||
It is also complex to write a randomly generated test that can deal with all possible scenarios without hitting a double faulting or time out scenarios (e.g. consider a random configuration that gives you no executable regions and ePMP modes like machine mode lockdown and machine mode whitelist policy).
|
||||
Co-simulation checking is enabled when this test is run (as it is for all block level verification tests) so would detect any incorrect behaviour.
|
||||
From investigation we are confident the time-outs seen are simply badly performing tests (e.g. very slowly working its way through an instruction block with no execute permissions by attempting to execute one instruction, faulting, trying the next and getting the same result over and over).
|
||||
For future work we will explore more efficient strategies for exploring this space as well as employing formal methods to achieve full verification closure.
|
||||
|
||||
V2S Checklist
|
||||
-------------
|
||||
|
||||
+---------------+--------------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Type | Item | Resolution | Notes |
|
||||
+===============+==========================+============+======================================================================================================================================+
|
||||
| Documentation | SEC_CM_TESTPLAN_COMPLETE | Complete | The security counter measure to test mapping can be found below |
|
||||
+---------------+--------------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Tests | FPV_SEC_CM_VERIFIED | Complete | See the `OpenTitan FPV Results Dashboard <https://reports.opentitan.org/hw/top_earlgrey/formal/sec_cm/summary/latest/report.html>`_. |
|
||||
+---------------+--------------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Tests | SIM_SEC_CM_VERIFIED | Complete | |
|
||||
+---------------+--------------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Coverage | SIM_COVERAGE_REVIEWED | Complete | |
|
||||
+---------------+--------------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Review | SEC_CM_DV_REVIEWED | Complete | |
|
||||
+---------------+--------------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
Ibex SEC_CM Test Mapping
|
||||
------------------------
|
||||
|
||||
The :ref:`security features Ibex implements <security>` are given specific security countermeasure names in OpenTitan (see 'Security Countermeasures' in the `Comportability Definition and Specification <https://opentitan.org/book/doc/contributing/hw/comportability/index.html#security-countermeasures>`_ documentation section).
|
||||
Each countermeasure has a test that exercises it.
|
||||
The mapping between countermeasures and tests is given below
|
||||
|
||||
+--------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Security Countermeasure | Test |
|
||||
+================================+============================================================================================================================================================================================================================================================================================================+
|
||||
| BUS.INTEGRITY | ``riscv_mem_intg_error_test`` in Ibex DV. |
|
||||
| | The ``chip_sw_data_integrity`` OpenTitan top-level test will trigger integrity errors within the OpenTitan specific ``rv_core_ibex`` wrapper. |
|
||||
| | The TL-UL host adapter used in the OpenTitan specific ``rv_core_ibex`` is fully verified elsewhere in OpenTitan. |
|
||||
+--------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| SCRAMBLE.KEY.SIDELOAD | ``riscv_rand_instr_test`` in Ibex DV. |
|
||||
| | This test executes ``FENCE.I`` which rotates the scramble key. |
|
||||
| | The ``rv_core_ibex_icache_invalidate_test`` OpenTitan top-level test covers assertions within the OpenTitan specific ``rv_core_ibex`` wrapper that check that a ``FENCE.I`` results in an icache scramble key request and that the returned key is correctly supplied to the scrambling memory primitives. |
|
||||
+--------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| CORE.DATA_REG_SW.SCA | ``dit_test`` directed test run against simple system cosimulation. |
|
||||
| | The test runs functions that whose timing is data dependent with data independent timing disabled. |
|
||||
| | It passes where the runs with data independent timing enabled all execute in the same amount of time and the runs without it enabled take different amounts of time. |
|
||||
+--------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| PC.CTRL_FLOW.CONSISTENCY | ``riscv_pc_intg_test`` in Ibex DV. |
|
||||
+--------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| CTRL_FLOW.UNPREDICTABLE | ``dummy_instr_test`` directed test run against simple system cosimulation. |
|
||||
| | The test runs a function with dummy instructions disabled and enabled. |
|
||||
| | It passes where the runs without dummy instructions all have the same timing and runs with dummy instructions all have different timing. |
|
||||
+--------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| DATA_REG_SW.INTEGRITY | ``riscv_rf_intg_test`` in Ibex DV. |
|
||||
+--------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| DATA_REG_SW.GLITCH_DETECT | Covered by formal verification of security countermeasures within OpenTitan. |
|
||||
+--------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| LOGIC.SHADOW | ``chip_sw_rv_core_ibex_lockstep_glitch`` top-level test in OpenTitan |
|
||||
+--------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| FETCH.CTRL.LC_GATED | ``riscv_rand_instr_test`` in Ibex DV. |
|
||||
| | Fetch enable is randomly toggled in various tests and correct behaviour checked via an assertion. |
|
||||
+--------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| EXCEPTION.CTRL_FLOW.LOCAL_ESC | ``riscv_pmp_full_random_test`` in Ibex DV. |
|
||||
| | This test produces double faults, which are checked by an assertion. |
|
||||
| | ``chip_sw_rv_core_ibex_double_fault`` top-level test in OpenTitan demonstrates escalation on a double fault |
|
||||
+--------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| EXCEPTION.CTRL_FLOW.GLOBAL_ESC | ``riscv_pmp_full_random_test`` in Ibex DV. |
|
||||
| | This test produces double faults, which are checked by an assertion. |
|
||||
| | ``chip_sw_rv_core_ibex_double_fault`` top-level test in OpenTitan demonstrates escalation on a double fault |
|
||||
+--------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| ICACHE.MEM.SCRAMBLE | No explicit testing, the scrambling memory primitive is seperately verified within OpenTitan. |
|
||||
| | Assertions in the OpenTitan specific ``rv_core_ibex`` wrapper ensure a newly requested scramble key is correctly applied to the scrambling memories. |
|
||||
| | The ``rv_core_ibex_icache_invalidate_test`` OpenTitan top-level test covers assertions within the OpenTitan specific ``rv_core_ibex`` wrapper that check that a ``FENCE.I`` results in an icache scramble key request and that the returned key is correctly supplied to the scrambling memory primitives. |
|
||||
+--------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| ICACHE.MEM.INTEGRITY | ``riscv_icache_intg_test`` in Ibex DV. |
|
||||
+--------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
V3 Checklist
|
||||
------------
|
||||
|
||||
+---------------+--------------------------------+-------------+-------+
|
||||
| Type | Item | Resolution | Notes |
|
||||
+===============+================================+=============+=======+
|
||||
| Documentation | DESIGN_DELTAS_CAPTURED_V3 | Not Started | |
|
||||
+---------------+--------------------------------+-------------+-------+
|
||||
| Tests | X_PROP_ANALYSIS_COMPLETED | Not Started | |
|
||||
+---------------+--------------------------------+-------------+-------+
|
||||
| Tests | FPV_ASSERTIONS_PROVEN_AT_V3 | Not Started | |
|
||||
+---------------+--------------------------------+-------------+-------+
|
||||
| Regression | SIM_NIGHTLY_REGRESSION_AT_V3 | Not Started | |
|
||||
+---------------+--------------------------------+-------------+-------+
|
||||
| Coverage | SIM_CODE_COVERAGE_AT_100 | Not Started | |
|
||||
+---------------+--------------------------------+-------------+-------+
|
||||
| Coverage | SIM_FUNCTIONAL_COVERAGE_AT_100 | Not Started | |
|
||||
+---------------+--------------------------------+-------------+-------+
|
||||
| Coverage | FPV_CODE_COVERAGE_AT_100 | Not Started | |
|
||||
+---------------+--------------------------------+-------------+-------+
|
||||
| Coverage | FPV_COI_COVERAGE_AT_100 | Not Started | |
|
||||
+---------------+--------------------------------+-------------+-------+
|
||||
| Code Quality | ALL_TODOS_RESOLVED | Not Started | |
|
||||
+---------------+--------------------------------+-------------+-------+
|
||||
| Code Quality | NO_TOOL_WARNINGS_THROWN | Not Started | |
|
||||
+---------------+--------------------------------+-------------+-------+
|
||||
| Code Quality | TB_LINT_COMPLETE | Not Started | |
|
||||
+---------------+--------------------------------+-------------+-------+
|
||||
| Integration | PRE_VERIFIED_SUB_MODULES_V3 | Not Started | |
|
||||
+---------------+--------------------------------+-------------+-------+
|
||||
| Issues | NO_ISSUES_PENDING | Not Started | |
|
||||
+---------------+--------------------------------+-------------+-------+
|
||||
| Review | Reviewer(s) | Not Started | |
|
||||
+---------------+--------------------------------+-------------+-------+
|
||||
| Review | Signoff date | Not Started | |
|
||||
+---------------+--------------------------------+-------------+-------+
|
||||
|
|
@ -25,10 +25,8 @@ The concierge duties rotate between several core developers on a weekly basis.
|
|||
You can find today's concierge on duty in a `public calendar <https://calendar.google.com/calendar/embed?src=lowrisc.org_s0pdodkddnggdp40jusjij27h4%40group.calendar.google.com>`_.
|
||||
|
||||
* Greg Chadwick (`@GregAC <https://github.com/gregac>`_)
|
||||
* Tom Roberts (`@tomroberts-lowrisc <https://github.com/tomroberts-lowrisc>`_)
|
||||
* Rupert Swarbrick (`@rswarbrick <https://github.com/rswarbrick>`_)
|
||||
* Pirmin Vogel (`@vogelpi <https://github.com/vogelpi>`_)
|
||||
* Philipp Wagner (`@imphil <https://github.com/imphil>`_)
|
||||
|
||||
You can be Ibex Concierge, too.
|
||||
Please talk to any of the current concierges to discuss!
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
setuptools_scm
|
||||
# sphinxcontrib-wavedrom isn't yet ready for Sphinx 4. Relax this requirements
|
||||
# once https://github.com/bavovanachte/sphinx-wavedrom/pull/32 is released.
|
||||
sphinx~=3.0
|
||||
sphinx>=7.0
|
||||
sphinx_rtd_theme
|
||||
sphinxcontrib-wavedrom
|
||||
wavedrom>=1.9.0rc1
|
||||
|
|
|
@ -35,6 +35,10 @@ struct DSideAccessInfo {
|
|||
// `misaligned_first` set to true, there is no second half.
|
||||
bool misaligned_first;
|
||||
bool misaligned_second;
|
||||
|
||||
bool misaligned_first_saw_error;
|
||||
|
||||
bool m_mode_access;
|
||||
};
|
||||
|
||||
class Cosim {
|
||||
|
@ -72,7 +76,7 @@ class Cosim {
|
|||
//
|
||||
// Returns false if there are any errors; use `get_errors` to obtain details
|
||||
virtual bool step(uint32_t write_reg, uint32_t write_reg_data, uint32_t pc,
|
||||
bool sync_trap) = 0;
|
||||
bool sync_trap, bool suppress_reg_write) = 0;
|
||||
|
||||
// When more than one of `set_mip`, `set_nmi` or `set_debug_req` is called
|
||||
// before `step` which one takes effect is chosen by the co-simulator. Which
|
||||
|
@ -83,7 +87,7 @@ class Cosim {
|
|||
//
|
||||
// At the next call of `step`, the MIP value will take effect (i.e. if it's a
|
||||
// new interrupt that is enabled it will step straight to that handler).
|
||||
virtual void set_mip(uint32_t mip) = 0;
|
||||
virtual void set_mip(uint32_t pre_mip, uint32_t post_mip) = 0;
|
||||
|
||||
// Set the state of the NMI (non-maskable interrupt) line.
|
||||
//
|
||||
|
@ -95,6 +99,12 @@ class Cosim {
|
|||
// When an NMI is due to be taken that will occur at the next call of `step`.
|
||||
virtual void set_nmi(bool nmi) = 0;
|
||||
|
||||
// Set the state of the internal NMI (non-maskable interrupt) line.
|
||||
// Behaviour wise this is almost as same as external NMI case explained at
|
||||
// set_nmi method. Difference is that this one is a response from Ibex rather
|
||||
// than an input.
|
||||
virtual void set_nmi_int(bool nmi_int) = 0;
|
||||
|
||||
// Set the debug request.
|
||||
//
|
||||
// When set to true the core will enter debug mode at the next step
|
||||
|
|
|
@ -2,24 +2,30 @@
|
|||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "cosim_dpi.h"
|
||||
|
||||
#include <svdpi.h>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "cosim.h"
|
||||
#include "cosim_dpi.h"
|
||||
|
||||
int riscv_cosim_step(Cosim *cosim, const svBitVecVal *write_reg,
|
||||
const svBitVecVal *write_reg_data, const svBitVecVal *pc,
|
||||
svBit sync_trap) {
|
||||
svBit sync_trap, svBit suppress_reg_write) {
|
||||
assert(cosim);
|
||||
|
||||
return cosim->step(write_reg[0], write_reg_data[0], pc[0], sync_trap) ? 1 : 0;
|
||||
return cosim->step(write_reg[0], write_reg_data[0], pc[0], sync_trap,
|
||||
suppress_reg_write)
|
||||
? 1
|
||||
: 0;
|
||||
}
|
||||
|
||||
void riscv_cosim_set_mip(Cosim *cosim, const svBitVecVal *mip) {
|
||||
void riscv_cosim_set_mip(Cosim *cosim, const svBitVecVal *pre_mip,
|
||||
const svBitVecVal *post_mip) {
|
||||
assert(cosim);
|
||||
|
||||
cosim->set_mip(mip[0]);
|
||||
cosim->set_mip(pre_mip[0], post_mip[0]);
|
||||
}
|
||||
|
||||
void riscv_cosim_set_nmi(Cosim *cosim, svBit nmi) {
|
||||
|
@ -28,6 +34,11 @@ void riscv_cosim_set_nmi(Cosim *cosim, svBit nmi) {
|
|||
cosim->set_nmi(nmi);
|
||||
}
|
||||
|
||||
void riscv_cosim_set_nmi_int(Cosim *cosim, svBit nmi_int) {
|
||||
assert(cosim);
|
||||
|
||||
cosim->set_nmi_int(nmi_int);
|
||||
}
|
||||
void riscv_cosim_set_debug_req(Cosim *cosim, svBit debug_req) {
|
||||
assert(cosim);
|
||||
|
||||
|
@ -58,17 +69,21 @@ void riscv_cosim_notify_dside_access(Cosim *cosim, svBit store,
|
|||
svBitVecVal *addr, svBitVecVal *data,
|
||||
svBitVecVal *be, svBit error,
|
||||
svBit misaligned_first,
|
||||
svBit misaligned_second) {
|
||||
svBit misaligned_second,
|
||||
svBit misaligned_first_saw_error,
|
||||
svBit m_mode_access) {
|
||||
assert(cosim);
|
||||
|
||||
cosim->notify_dside_access(
|
||||
DSideAccessInfo{.store = store != 0,
|
||||
.data = data[0],
|
||||
.addr = addr[0],
|
||||
.be = be[0],
|
||||
.error = error != 0,
|
||||
.misaligned_first = misaligned_first != 0,
|
||||
.misaligned_second = misaligned_second != 0});
|
||||
cosim->notify_dside_access(DSideAccessInfo{
|
||||
.store = store != 0,
|
||||
.data = data[0],
|
||||
.addr = addr[0],
|
||||
.be = be[0],
|
||||
.error = error != 0,
|
||||
.misaligned_first = misaligned_first != 0,
|
||||
.misaligned_second = misaligned_second != 0,
|
||||
.misaligned_first_saw_error = misaligned_first_saw_error != 0,
|
||||
.m_mode_access = m_mode_access != 0});
|
||||
}
|
||||
|
||||
void riscv_cosim_set_iside_error(Cosim *cosim, svBitVecVal *addr) {
|
||||
|
|
|
@ -8,15 +8,19 @@
|
|||
#include <stdint.h>
|
||||
#include <svdpi.h>
|
||||
|
||||
#include "cosim.h"
|
||||
|
||||
// This adapts the C++ interface of the `Cosim` class to be used via DPI. See
|
||||
// the documentation in cosim.h for further details
|
||||
|
||||
extern "C" {
|
||||
int riscv_cosim_step(Cosim *cosim, const svBitVecVal *write_reg,
|
||||
const svBitVecVal *write_reg_data, const svBitVecVal *pc,
|
||||
svBit sync_trap);
|
||||
void riscv_cosim_set_mip(Cosim *cosim, const svBitVecVal *mip);
|
||||
svBit sync_trap, svBit suppress_reg_write);
|
||||
void riscv_cosim_set_mip(Cosim *cosim, const svBitVecVal *pre_mip,
|
||||
const svBitVecVal *post_mip);
|
||||
void riscv_cosim_set_nmi(Cosim *cosim, svBit nmi);
|
||||
void riscv_cosim_set_nmi_int(Cosim *cosim, svBit nmi_int);
|
||||
void riscv_cosim_set_debug_req(Cosim *cosim, svBit debug_req);
|
||||
void riscv_cosim_set_mcycle(Cosim *cosim, svBitVecVal *mcycle);
|
||||
void riscv_cosim_set_csr(Cosim *cosim, const int csr_id,
|
||||
|
@ -26,7 +30,9 @@ void riscv_cosim_notify_dside_access(Cosim *cosim, svBit store,
|
|||
svBitVecVal *addr, svBitVecVal *data,
|
||||
svBitVecVal *be, svBit error,
|
||||
svBit misaligned_first,
|
||||
svBit misaligned_second);
|
||||
svBit misaligned_second,
|
||||
svBit misaligned_first_saw_error,
|
||||
svBit m_mode_access);
|
||||
void riscv_cosim_set_iside_error(Cosim *cosim, svBitVecVal *addr);
|
||||
int riscv_cosim_get_num_errors(Cosim *cosim);
|
||||
const char *riscv_cosim_get_error(Cosim *cosim, int index);
|
||||
|
|
|
@ -11,9 +11,11 @@
|
|||
`define COSIM_DPI_SVH
|
||||
|
||||
import "DPI-C" function int riscv_cosim_step(chandle cosim_handle, bit [4:0] write_reg,
|
||||
bit [31:0] write_reg_data, bit [31:0] pc, bit sync_trap);
|
||||
import "DPI-C" function void riscv_cosim_set_mip(chandle cosim_handle, bit [31:0] mip);
|
||||
bit [31:0] write_reg_data, bit [31:0] pc, bit sync_trap, bit suppress_reg_write);
|
||||
import "DPI-C" function void riscv_cosim_set_mip(chandle cosim_handle, bit [31:0] pre_mip,
|
||||
bit [31:0] post_mip);
|
||||
import "DPI-C" function void riscv_cosim_set_nmi(chandle cosim_handle, bit nmi);
|
||||
import "DPI-C" function void riscv_cosim_set_nmi_int(chandle cosim_handle, bit nmi_int);
|
||||
import "DPI-C" function void riscv_cosim_set_debug_req(chandle cosim_handle, bit debug_req);
|
||||
import "DPI-C" function void riscv_cosim_set_mcycle(chandle cosim_handle, bit [63:0] mcycle);
|
||||
import "DPI-C" function void riscv_cosim_set_csr(chandle cosim_handle, int csr_id,
|
||||
|
@ -21,7 +23,7 @@ import "DPI-C" function void riscv_cosim_set_csr(chandle cosim_handle, int csr_i
|
|||
import "DPI-C" function void riscv_cosim_set_ic_scr_key_valid(chandle cosim_handle, bit valid);
|
||||
import "DPI-C" function void riscv_cosim_notify_dside_access(chandle cosim_handle, bit store,
|
||||
bit [31:0] addr, bit [31:0] data, bit [3:0] be, bit error, bit misaligned_first,
|
||||
bit misaligned_second);
|
||||
bit misaligned_second, bit misaligned_first_saw_error, bit m_mode_access);
|
||||
import "DPI-C" function int riscv_cosim_set_iside_error(chandle cosim_handle, bit [31:0] addr);
|
||||
import "DPI-C" function int riscv_cosim_get_num_errors(chandle cosim_handle);
|
||||
import "DPI-C" function string riscv_cosim_get_error(chandle cosim_handle, int index);
|
||||
|
|
|
@ -3,17 +3,19 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "spike_cosim.h"
|
||||
#include "riscv/config.h"
|
||||
#include "riscv/decode.h"
|
||||
#include "riscv/devices.h"
|
||||
#include "riscv/log_file.h"
|
||||
#include "riscv/processor.h"
|
||||
#include "riscv/simif.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "riscv/config.h"
|
||||
#include "riscv/decode.h"
|
||||
#include "riscv/devices.h"
|
||||
#include "riscv/log_file.h"
|
||||
#include "riscv/mmu.h"
|
||||
#include "riscv/processor.h"
|
||||
#include "riscv/simif.h"
|
||||
|
||||
// For a short time, we're going to support building against version
|
||||
// ibex-cosim-v0.2 (20a886c) and also ibex-cosim-v0.3 (9af9730). Unfortunately,
|
||||
// they've got different APIs and spike doesn't expose a version string.
|
||||
|
@ -48,10 +50,17 @@ SpikeCosim::SpikeCosim(const std::string &isa_string, uint32_t start_pc,
|
|||
std::make_unique<processor_t>(isa_string.c_str(), "MU", DEFAULT_VARCH,
|
||||
this, 0, false, log_file, std::cerr);
|
||||
#else
|
||||
isa_parser = std::make_unique<isa_parser_t>(isa_string.c_str(), "MU");
|
||||
|
||||
#ifdef COSIM_SIGSEGV_WORKAROUND
|
||||
isa_parser = new isa_parser_t(isa_string.c_str(), "MU");
|
||||
processor = std::make_unique<processor_t>(isa_parser, DEFAULT_VARCH, this, 0,
|
||||
false, log_file, std::cerr);
|
||||
#else
|
||||
isa_parser = std::make_unique<isa_parser_t>(isa_string.c_str(), "MU");
|
||||
processor = std::make_unique<processor_t>(
|
||||
isa_parser.get(), DEFAULT_VARCH, this, 0, false, log_file, std::cerr);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
processor->set_pmp_num(pmp_num_regions);
|
||||
|
@ -76,8 +85,9 @@ bool SpikeCosim::mmio_load(reg_t addr, size_t len, uint8_t *bytes) {
|
|||
bool dut_error = false;
|
||||
|
||||
// Incoming access may be an iside or dside access. Use PC to help determine
|
||||
// which.
|
||||
uint32_t pc = processor->get_state()->pc;
|
||||
// which. PC is 64 bits in spike, we only care about the bottom 32-bit so mask
|
||||
// off the top bits.
|
||||
uint64_t pc = processor->get_state()->pc & 0xffffffff;
|
||||
uint32_t aligned_addr = addr & 0xfffffffc;
|
||||
|
||||
if (pending_iside_error && (aligned_addr == pending_iside_err_addr)) {
|
||||
|
@ -85,17 +95,14 @@ bool SpikeCosim::mmio_load(reg_t addr, size_t len, uint8_t *bytes) {
|
|||
// assume it's an iside access and produce an error.
|
||||
pending_iside_error = false;
|
||||
dut_error = true;
|
||||
} else if (addr < pc || addr >= (pc + 8)) {
|
||||
} else {
|
||||
// Spike may attempt to access up to 8-bytes from the PC when fetching, so
|
||||
// only check as a dside access when it falls outside that range.
|
||||
// only check as a dside access when it falls outside that range
|
||||
bool in_iside_range = (addr >= pc && addr < pc + 8);
|
||||
|
||||
// Otherwise check if the aligned PC matches with the aligned address or an
|
||||
// incremented aligned PC (to capture the unaligned 4-byte instruction
|
||||
// case). Assume a successful iside access if either of these checks are
|
||||
// true, otherwise assume a dside access and check against DUT dside
|
||||
// accesses. If the RTL produced a bus error for the access, or the
|
||||
// checking failed produce a memory fault in spike.
|
||||
dut_error = (check_mem_access(false, addr, len, bytes) != kCheckMemOk);
|
||||
if (!in_iside_range) {
|
||||
dut_error = (check_mem_access(false, addr, len, bytes) != kCheckMemOk);
|
||||
}
|
||||
}
|
||||
|
||||
return !(bus_error || dut_error);
|
||||
|
@ -160,17 +167,47 @@ bool SpikeCosim::backdoor_read_mem(uint32_t addr, size_t len,
|
|||
// - If we catch a trap_t&, then the take_trap() fn updates the state of the
|
||||
// processor, and when we call step() again we start executing in the new
|
||||
// context of the trap (trap andler, new MSTATUS, debug rom, etc. etc.)
|
||||
bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data,
|
||||
uint32_t pc,
|
||||
bool sync_trap) {
|
||||
bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data, uint32_t pc,
|
||||
bool sync_trap, bool suppress_reg_write) {
|
||||
assert(write_reg < 32);
|
||||
|
||||
// The DUT has just produced an RVFI item
|
||||
// (parameters of this func is the data in the RVFI item).
|
||||
|
||||
// First check to see if this is an ebreak that should enter debug mode. These
|
||||
// need specific handling. When spike steps over them it'll immediately step
|
||||
// the next instruction (i.e. the first instruction of the debug handler) too.
|
||||
// In effect it treats it as a transition to debug mode that doesn't retire
|
||||
// any instruction so needs to execute the next instruction to step a single
|
||||
// time. To deal with this if it's a debug ebreak we skip the rest of this
|
||||
// function checking a few invariants on the debug ebreak first.
|
||||
if (pc_is_debug_ebreak(pc)) {
|
||||
return check_debug_ebreak(write_reg, pc, sync_trap);
|
||||
}
|
||||
|
||||
uint32_t initial_spike_pc;
|
||||
uint32_t suppressed_write_reg;
|
||||
uint32_t suppressed_write_reg_data;
|
||||
bool pending_sync_exception = false;
|
||||
|
||||
if (suppress_reg_write) {
|
||||
// If Ibex suppressed a register write (which occurs when a load gets data
|
||||
// with bad integrity) record the state of the destination register before
|
||||
// we do the stop, so we can restore it after the step (as spike won't
|
||||
// suppressed the register write).
|
||||
//
|
||||
// First check retired instruciton to ensure load suppression is correct
|
||||
if (!check_suppress_reg_write(write_reg, pc, suppressed_write_reg)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The check gives us the destination register the instruction would have
|
||||
// written to (write_reg will be 0 to indicate to write). Record the
|
||||
// contents of that register.
|
||||
suppressed_write_reg_data =
|
||||
processor->get_state()->XPR[suppressed_write_reg];
|
||||
}
|
||||
|
||||
// Before stepping Spike, record the current spike pc.
|
||||
// (If the current step causes a synchronous trap, it will be
|
||||
// recorded against the current pc)
|
||||
|
@ -200,7 +237,8 @@ bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data,
|
|||
|
||||
if (processor->get_state()->last_inst_pc == PC_INVALID) {
|
||||
if (!(processor->get_state()->mcause->read() & 0x80000000) ||
|
||||
processor->get_state()->debug_mode) { // (Async-Traps are disabled in debug mode)
|
||||
processor->get_state()
|
||||
->debug_mode) { // (Async-Traps are disabled in debug mode)
|
||||
// Spike encountered a synchronous trap
|
||||
pending_sync_exception = true;
|
||||
|
||||
|
@ -223,8 +261,8 @@ bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data,
|
|||
if (pending_sync_exception) {
|
||||
if (!sync_trap) {
|
||||
std::stringstream err_str;
|
||||
err_str << "Synchronous trap was expected at ISS PC: "
|
||||
<< std::hex << processor->get_state()->pc
|
||||
err_str << "Synchronous trap was expected at ISS PC: " << std::hex
|
||||
<< processor->get_state()->pc
|
||||
<< " but the DUT didn't report one at PC " << pc;
|
||||
errors.emplace_back(err_str.str());
|
||||
return false;
|
||||
|
@ -233,6 +271,9 @@ bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data,
|
|||
if (!check_sync_trap(write_reg, pc, initial_spike_pc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
handle_cpuctrl_exception_entry();
|
||||
|
||||
// This is all the checking possible when consider a
|
||||
// synchronously-trapping instruction that never retired.
|
||||
return true;
|
||||
|
@ -242,22 +283,31 @@ bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data,
|
|||
// We reached a retired instruction, so check spike and the dut behaved
|
||||
// consistently.
|
||||
|
||||
if (!sync_trap && pc_is_mret(pc) && nmi_mode) {
|
||||
// Do handling for recoverable NMI
|
||||
leave_nmi_mode();
|
||||
if (!sync_trap && pc_is_mret(pc)) {
|
||||
change_cpuctrlsts_sync_exc_seen(false);
|
||||
|
||||
if (nmi_mode) {
|
||||
// Do handling for recoverable NMI
|
||||
leave_nmi_mode();
|
||||
}
|
||||
}
|
||||
|
||||
if (pending_iside_error) {
|
||||
std::stringstream err_str;
|
||||
err_str << "DUT generated an iside error for address: "
|
||||
<< std::hex << pending_iside_err_addr
|
||||
<< " but the ISS didn't produce one";
|
||||
err_str << "DUT generated an iside error for address: " << std::hex
|
||||
<< pending_iside_err_addr << " but the ISS didn't produce one";
|
||||
errors.emplace_back(err_str.str());
|
||||
return false;
|
||||
}
|
||||
pending_iside_error = false;
|
||||
|
||||
if (!check_retired_instr(write_reg, write_reg_data, pc)) {
|
||||
if (suppress_reg_write) {
|
||||
// If we suppressed a register write restore the old register state now
|
||||
processor->get_state()->XPR.write(suppressed_write_reg,
|
||||
suppressed_write_reg_data);
|
||||
}
|
||||
|
||||
if (!check_retired_instr(write_reg, write_reg_data, pc, suppress_reg_write)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -266,8 +316,9 @@ bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool SpikeCosim::check_retired_instr(uint32_t write_reg, uint32_t write_reg_data,
|
||||
uint32_t dut_pc) {
|
||||
bool SpikeCosim::check_retired_instr(uint32_t write_reg,
|
||||
uint32_t write_reg_data, uint32_t dut_pc,
|
||||
bool suppress_reg_write) {
|
||||
// Check the retired instruction and all of its side-effects match those from
|
||||
// the DUT
|
||||
|
||||
|
@ -277,8 +328,8 @@ bool SpikeCosim::check_retired_instr(uint32_t write_reg, uint32_t write_reg_data
|
|||
if ((processor->get_state()->last_inst_pc & 0xffffffff) != dut_pc) {
|
||||
std::stringstream err_str;
|
||||
err_str << "PC mismatch, DUT retired : " << std::hex << dut_pc
|
||||
<< " , but the ISS retired: "
|
||||
<< std::hex << processor->get_state()->last_inst_pc;
|
||||
<< " , but the ISS retired: " << std::hex
|
||||
<< processor->get_state()->last_inst_pc;
|
||||
errors.emplace_back(err_str.str());
|
||||
return false;
|
||||
}
|
||||
|
@ -301,7 +352,8 @@ bool SpikeCosim::check_retired_instr(uint32_t write_reg, uint32_t write_reg_data
|
|||
// should never see more than one GPR write per step
|
||||
assert(!gpr_write_seen);
|
||||
|
||||
if (!check_gpr_write(reg_change, write_reg, write_reg_data)) {
|
||||
if (!suppress_reg_write &&
|
||||
!check_gpr_write(reg_change, write_reg, write_reg_data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -332,18 +384,17 @@ bool SpikeCosim::check_retired_instr(uint32_t write_reg, uint32_t write_reg_data
|
|||
return true;
|
||||
}
|
||||
|
||||
bool SpikeCosim::check_sync_trap(uint32_t write_reg,
|
||||
uint32_t dut_pc, uint32_t initial_spike_pc) {
|
||||
bool SpikeCosim::check_sync_trap(uint32_t write_reg, uint32_t dut_pc,
|
||||
uint32_t initial_spike_pc) {
|
||||
// Check if an synchronously-trapping instruction matches
|
||||
// between Spike and the DUT.
|
||||
|
||||
// Check that both spike and DUT trapped on the same pc
|
||||
if (initial_spike_pc != dut_pc) {
|
||||
std::stringstream err_str;
|
||||
err_str << "PC mismatch at synchronous trap, DUT at pc: "
|
||||
<< std::hex << dut_pc
|
||||
<< "while ISS pc is at : "
|
||||
<< std::hex << initial_spike_pc;
|
||||
err_str << "PC mismatch at synchronous trap, DUT at pc: " << std::hex
|
||||
<< dut_pc << "while ISS pc is at : " << std::hex
|
||||
<< initial_spike_pc;
|
||||
errors.emplace_back(err_str.str());
|
||||
return false;
|
||||
}
|
||||
|
@ -358,6 +409,18 @@ bool SpikeCosim::check_sync_trap(uint32_t write_reg,
|
|||
return false;
|
||||
}
|
||||
|
||||
if ((processor->get_state()->mcause->read() == 0x5) ||
|
||||
(processor->get_state()->mcause->read() == 0x7)) {
|
||||
// We have a load or store access fault, apply fixup for misaligned accesses
|
||||
misaligned_pmp_fixup();
|
||||
}
|
||||
|
||||
// If we see an internal NMI, that means we receive an extra memory intf item.
|
||||
// Deleting that is necessary since next Load/Store would fail otherwise.
|
||||
if (processor->get_state()->mcause->read() == 0xFFFFFFE0) {
|
||||
pending_dside_accesses.erase(pending_dside_accesses.begin());
|
||||
}
|
||||
|
||||
// Errors may have been generated outside of step() (e.g. in
|
||||
// check_mem_access()), return false if there are any.
|
||||
if (errors.size() != 0) {
|
||||
|
@ -407,6 +470,31 @@ bool SpikeCosim::check_gpr_write(const commit_log_reg_t::value_type ®_change,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool SpikeCosim::check_suppress_reg_write(uint32_t write_reg, uint32_t pc,
|
||||
uint32_t &suppressed_write_reg) {
|
||||
if (write_reg != 0) {
|
||||
std::stringstream err_str;
|
||||
err_str << "Instruction at " << std::hex << pc
|
||||
<< " indicated a suppressed register write but wrote to x"
|
||||
<< std::dec << write_reg;
|
||||
errors.emplace_back(err_str.str());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pc_is_load(pc, suppressed_write_reg)) {
|
||||
std::stringstream err_str;
|
||||
err_str << "Instruction at " << std::hex << pc
|
||||
<< " indicated a suppressed register write is it not a load"
|
||||
" only loads can suppress register writes";
|
||||
errors.emplace_back(err_str.str());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SpikeCosim::on_csr_write(const commit_log_reg_t::value_type ®_change) {
|
||||
int cosim_write_csr = (reg_change.first >> 4) & 0xfff;
|
||||
|
||||
|
@ -440,6 +528,37 @@ void SpikeCosim::leave_nmi_mode() {
|
|||
#endif
|
||||
}
|
||||
|
||||
void SpikeCosim::handle_cpuctrl_exception_entry() {
|
||||
if (!processor->get_state()->debug_mode) {
|
||||
bool old_sync_exc_seen = change_cpuctrlsts_sync_exc_seen(true);
|
||||
if (old_sync_exc_seen) {
|
||||
set_cpuctrlsts_double_fault_seen();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SpikeCosim::change_cpuctrlsts_sync_exc_seen(bool flag) {
|
||||
bool old_flag = false;
|
||||
uint32_t cpuctrlsts = processor->get_csr(CSR_CPUCTRLSTS);
|
||||
|
||||
// If sync_exc_seen (bit 6) is already set update old_flag to match
|
||||
if (cpuctrlsts & 0x40) {
|
||||
old_flag = true;
|
||||
}
|
||||
|
||||
cpuctrlsts = (cpuctrlsts & 0x1bf) | (flag ? 0x40 : 0);
|
||||
processor->put_csr(CSR_CPUCTRLSTS, cpuctrlsts);
|
||||
|
||||
return old_flag;
|
||||
}
|
||||
|
||||
void SpikeCosim::set_cpuctrlsts_double_fault_seen() {
|
||||
uint32_t cpuctrlsts = processor->get_csr(CSR_CPUCTRLSTS);
|
||||
// Set double_fault_seen (bit 7)
|
||||
cpuctrlsts = (cpuctrlsts & 0x17f) | 0x80;
|
||||
processor->put_csr(CSR_CPUCTRLSTS, cpuctrlsts);
|
||||
}
|
||||
|
||||
void SpikeCosim::initial_proc_setup(uint32_t start_pc, uint32_t start_mtvec,
|
||||
uint32_t mhpm_counter_num) {
|
||||
processor->get_state()->pc = start_pc;
|
||||
|
@ -462,12 +581,108 @@ void SpikeCosim::initial_proc_setup(uint32_t start_pc, uint32_t start_mtvec,
|
|||
}
|
||||
}
|
||||
|
||||
void SpikeCosim::set_mip(uint32_t mip) {
|
||||
processor->get_state()->mip->write_with_mask(0xffffffff, mip);
|
||||
void SpikeCosim::set_mip(uint32_t pre_mip, uint32_t post_mip) {
|
||||
uint32_t new_mip = pre_mip;
|
||||
uint32_t old_mip = processor->get_state()->mip->read();
|
||||
|
||||
processor->get_state()->mip->write_with_mask(0xffffffff, post_mip);
|
||||
processor->get_state()->mip->write_pre_val(pre_mip);
|
||||
|
||||
if (processor->get_state()->debug_mode ||
|
||||
(processor->halt_request == processor_t::HR_REGULAR) ||
|
||||
(!get_field(processor->get_csr(CSR_MSTATUS), MSTATUS_MIE) &&
|
||||
processor->get_state()->prv == PRV_M)) {
|
||||
// Return now if new MIP won't trigger an interrupt handler either because
|
||||
// we're in or heading to debug mode or interrupts are disabled.
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t old_enabled_irq = old_mip & processor->get_state()->mie->read();
|
||||
uint32_t new_enabled_irq = new_mip & processor->get_state()->mie->read();
|
||||
|
||||
// Check to see if new MIP will trigger an interrupt (which occurs when new
|
||||
// MIP produces an enabled interrupt for the first time).
|
||||
if ((old_enabled_irq == 0) && (new_enabled_irq != 0)) {
|
||||
// Early interrupt handle if the interrupt is triggered.
|
||||
early_interrupt_handle();
|
||||
}
|
||||
}
|
||||
|
||||
void SpikeCosim::early_interrupt_handle() {
|
||||
// Execute a spike step on the assumption an interrupt will occur so no new
|
||||
// instruction is executed just the state altered to reflect the interrupt.
|
||||
uint32_t initial_spike_pc = (processor->get_state()->pc & 0xffffffff);
|
||||
processor->step(1);
|
||||
|
||||
if (processor->get_state()->last_inst_pc != PC_INVALID) {
|
||||
std::stringstream err_str;
|
||||
err_str << "Attempted step for interrupt, expecting no instruction would "
|
||||
<< "be executed but saw one. PC before: " << std::hex
|
||||
<< initial_spike_pc
|
||||
<< " PC after: " << (processor->get_state()->pc & 0xffffffff);
|
||||
errors.emplace_back(err_str.str());
|
||||
}
|
||||
}
|
||||
|
||||
// Ibex splits misaligned accesses into two separate requests. They
|
||||
// independently undergo PMP access checks. It is possible for one to fail (so
|
||||
// no request produced for that half of the access) whilst the other successed
|
||||
// (producing a request for that half of the access).
|
||||
//
|
||||
// Spike splits misaligned accesses up into bytes and will apply PMP access
|
||||
// checks byte by byte in a linear order. As soon as a byte sees a PMP
|
||||
// permission failure the rest of the misaligned access is aborted.
|
||||
//
|
||||
// This results in mismatches as in some misaligned access cases Ibex will
|
||||
// produce a request and spike will not.
|
||||
//
|
||||
// This fixup detects this condition and removes the Ibex access from
|
||||
// pending_dside_accesses to avoid a mismatch. This removed access is checked
|
||||
// against PMP using the spike MMU to check spike agrees it passes PMP checks.
|
||||
//
|
||||
// There may be a better way to handle this (e.g. altering spike behaviour to
|
||||
// match Ibex) so for now a warning is generated in fixup cases so they can be
|
||||
// easily identified.
|
||||
void SpikeCosim::misaligned_pmp_fixup() {
|
||||
if (pending_dside_accesses.size() != 0) {
|
||||
auto &top_pending_access = pending_dside_accesses.front();
|
||||
auto &top_pending_access_info = top_pending_access.dut_access_info;
|
||||
|
||||
// If top access is the second half of a misaligned access where the first
|
||||
// half saw an error we have the PMP fixup case
|
||||
if (top_pending_access_info.misaligned_second &&
|
||||
top_pending_access_info.misaligned_first_saw_error) {
|
||||
mmu_t *mmu = processor->get_mmu();
|
||||
|
||||
// Check if the second half of the access (which Ibex produces a request
|
||||
// for and spike does not) passes PMP
|
||||
if (!mmu->pmp_ok(top_pending_access_info.addr, 4,
|
||||
top_pending_access_info.store ? STORE : LOAD,
|
||||
top_pending_access_info.m_mode_access ? PRV_M : PRV_U)) {
|
||||
// Raise an error if the second half shouldn't have passed PMP
|
||||
std::stringstream err_str;
|
||||
err_str << "Saw second half of a misaligned access which not have "
|
||||
<< "generated a memory request as it does not pass a PMP check,"
|
||||
<< " address: " << std::hex << top_pending_access_info.addr;
|
||||
errors.emplace_back(err_str.str());
|
||||
} else {
|
||||
// Output warning on stdout so we're aware which tests this is happening
|
||||
// in
|
||||
std::cout << "WARNING: Cosim dropping second half of misaligned access "
|
||||
<< "as first half saw an error and second half passed PMP "
|
||||
<< "check, address: " << std::hex
|
||||
<< top_pending_access_info.addr << std::endl;
|
||||
std::cout << std::dec;
|
||||
|
||||
pending_dside_accesses.erase(pending_dside_accesses.begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SpikeCosim::set_nmi(bool nmi) {
|
||||
if (nmi && !nmi_mode && !processor->get_state()->debug_mode) {
|
||||
if (nmi && !nmi_mode && !processor->get_state()->debug_mode &&
|
||||
processor->halt_request != processor_t::HR_REGULAR) {
|
||||
processor->get_state()->nmi = true;
|
||||
nmi_mode = true;
|
||||
|
||||
|
@ -477,6 +692,25 @@ void SpikeCosim::set_nmi(bool nmi) {
|
|||
mstack.mpie = get_field(processor->get_csr(CSR_MSTATUS), MSTATUS_MPIE);
|
||||
mstack.epc = processor->get_csr(CSR_MEPC);
|
||||
mstack.cause = processor->get_csr(CSR_MCAUSE);
|
||||
|
||||
early_interrupt_handle();
|
||||
}
|
||||
}
|
||||
|
||||
void SpikeCosim::set_nmi_int(bool nmi_int) {
|
||||
if (nmi_int && !nmi_mode && !processor->get_state()->debug_mode &&
|
||||
processor->halt_request != processor_t::HR_REGULAR) {
|
||||
processor->get_state()->nmi_int = true;
|
||||
nmi_mode = true;
|
||||
|
||||
// When NMI is set it is guaranteed NMI trap will be taken at the next step
|
||||
// so save CSR state for recoverable NMI to mstack now.
|
||||
mstack.mpp = get_field(processor->get_csr(CSR_MSTATUS), MSTATUS_MPP);
|
||||
mstack.mpie = get_field(processor->get_csr(CSR_MSTATUS), MSTATUS_MPIE);
|
||||
mstack.epc = processor->get_csr(CSR_MEPC);
|
||||
mstack.cause = processor->get_csr(CSR_MCAUSE);
|
||||
|
||||
early_interrupt_handle();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -568,7 +802,30 @@ void SpikeCosim::fixup_csr(int csr_num, uint32_t csr_val) {
|
|||
if (any_interrupt && int_interrupt) {
|
||||
new_val |= 0x7fffffe0;
|
||||
}
|
||||
#ifdef OLD_SPIKE
|
||||
processor->set_csr(csr_num, new_val);
|
||||
#else
|
||||
processor->put_csr(csr_num, new_val);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case CSR_MTVEC: {
|
||||
uint32_t mtvec_and_mask = 0xffffff00;
|
||||
uint32_t mtvec_or_mask = 0x1;
|
||||
|
||||
// For Ibex, mtvec.MODE is set to vectored and
|
||||
// mtvec.BASE must be 256-byte aligned
|
||||
reg_t new_val = (csr_val & mtvec_and_mask) | mtvec_or_mask;
|
||||
#ifdef OLD_SPIKE
|
||||
processor->set_csr(csr_num, new_val);
|
||||
#else
|
||||
processor->put_csr(csr_num, new_val);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case CSR_MISA: {
|
||||
// For Ibex, misa is hardwired
|
||||
reg_t new_val = 0x40901104;
|
||||
#ifdef OLD_SPIKE
|
||||
processor->set_csr(csr_num, new_val);
|
||||
#else
|
||||
|
@ -787,4 +1044,101 @@ bool SpikeCosim::pc_is_mret(uint32_t pc) {
|
|||
return insn == 0x30200073;
|
||||
}
|
||||
|
||||
bool SpikeCosim::pc_is_debug_ebreak(uint32_t pc) {
|
||||
uint32_t dcsr = processor->get_csr(CSR_DCSR);
|
||||
|
||||
// ebreak debug entry is controlled by the ebreakm (bit 15) and ebreaku (bit
|
||||
// 12) fields of DCSR. If the appropriate bit of the current privlege level
|
||||
// isn't set ebreak won't enter debug so return false.
|
||||
if ((processor->get_state()->prv == PRV_M) && ((dcsr & 0x1000) == 0) ||
|
||||
(processor->get_state()->prv == PRV_U) && ((dcsr & 0x8000) == 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// First check for 16-bit c.ebreak
|
||||
uint16_t insn_16;
|
||||
if (!backdoor_read_mem(pc, 2, reinterpret_cast<uint8_t *>(&insn_16))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (insn_16 == 0x9002) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Not a c.ebreak, check for 32 bit ebreak
|
||||
uint32_t insn_32;
|
||||
if (!backdoor_read_mem(pc, 4, reinterpret_cast<uint8_t *>(&insn_32))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return insn_32 == 0x00100073;
|
||||
}
|
||||
|
||||
bool SpikeCosim::check_debug_ebreak(uint32_t write_reg, uint32_t pc,
|
||||
bool sync_trap) {
|
||||
// A ebreak from the DUT should not write a register and will be reported as a
|
||||
// 'sync_trap' (though doesn't act like a trap in various respects).
|
||||
|
||||
if (write_reg != 0) {
|
||||
std::stringstream err_str;
|
||||
err_str << "DUT executed ebreak at " << std::hex << pc
|
||||
<< " but also wrote register x" << std::dec << write_reg
|
||||
<< " which was unexpected";
|
||||
errors.emplace_back(err_str.str());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sync_trap) {
|
||||
std::stringstream err_str;
|
||||
err_str << "DUT executed ebreak into debug at " << std::hex << pc
|
||||
<< " but indicated a synchronous trap, which was unexpected";
|
||||
errors.emplace_back(err_str.str());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SpikeCosim::pc_is_load(uint32_t pc, uint32_t &rd_out) {
|
||||
uint16_t insn_16;
|
||||
|
||||
if (!backdoor_read_mem(pc, 2, reinterpret_cast<uint8_t *>(&insn_16))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// C.LW
|
||||
if ((insn_16 & 0xE003) == 0x4000) {
|
||||
rd_out = ((insn_16 >> 2) & 0x7) + 8;
|
||||
return true;
|
||||
}
|
||||
|
||||
// C.LWSP
|
||||
if ((insn_16 & 0xE003) == 0x4002) {
|
||||
rd_out = (insn_16 >> 7) & 0x1F;
|
||||
return rd_out != 0;
|
||||
}
|
||||
|
||||
uint16_t insn_32;
|
||||
|
||||
if (!backdoor_read_mem(pc, 4, reinterpret_cast<uint8_t *>(&insn_32))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// LB/LH/LW/LBU/LHU
|
||||
if ((insn_32 & 0x7F) == 0x3) {
|
||||
uint32_t func = (insn_32 >> 12) & 0x7;
|
||||
if ((func == 0x3) || (func == 0x6) || (func == 0x7)) {
|
||||
// Not valid load encodings
|
||||
return false;
|
||||
}
|
||||
|
||||
rd_out = (insn_32 >> 7) & 0x1F;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int SpikeCosim::get_insn_cnt() { return insn_cnt; }
|
||||
|
|
|
@ -22,7 +22,17 @@
|
|||
|
||||
class SpikeCosim : public simif_t, public Cosim {
|
||||
private:
|
||||
// A sigsegv has been observed when deleting isa_parser_t instances under
|
||||
// Xcelium on CentOS 7. The root cause is unknown so for a workaround simply
|
||||
// use a raw pointer for isa_parser that never gets deleted. This produces a
|
||||
// minor memory leak but it is of little consequence as when SpikeCosim is
|
||||
// being deleted it is the end of simulation and the process will be
|
||||
// terminated shortly anyway.
|
||||
#ifdef COSIM_SIGSEGV_WORKAROUND
|
||||
isa_parser_t *isa_parser;
|
||||
#else
|
||||
std::unique_ptr<isa_parser_t> isa_parser;
|
||||
#endif
|
||||
std::unique_ptr<processor_t> processor;
|
||||
std::unique_ptr<log_file_t> log;
|
||||
bus_t bus;
|
||||
|
@ -61,17 +71,32 @@ class SpikeCosim : public simif_t, public Cosim {
|
|||
const uint8_t *bytes);
|
||||
|
||||
bool pc_is_mret(uint32_t pc);
|
||||
bool pc_is_load(uint32_t pc, uint32_t &rd_out);
|
||||
|
||||
bool pc_is_debug_ebreak(uint32_t pc);
|
||||
bool check_debug_ebreak(uint32_t write_reg, uint32_t pc, bool sync_trap);
|
||||
|
||||
bool check_gpr_write(const commit_log_reg_t::value_type ®_change,
|
||||
uint32_t write_reg, uint32_t write_reg_data);
|
||||
|
||||
bool check_suppress_reg_write(uint32_t write_reg, uint32_t pc,
|
||||
uint32_t &suppressed_write_reg);
|
||||
|
||||
void on_csr_write(const commit_log_reg_t::value_type ®_change);
|
||||
|
||||
void leave_nmi_mode();
|
||||
|
||||
bool change_cpuctrlsts_sync_exc_seen(bool flag);
|
||||
void set_cpuctrlsts_double_fault_seen();
|
||||
void handle_cpuctrl_exception_entry();
|
||||
|
||||
void initial_proc_setup(uint32_t start_pc, uint32_t start_mtvec,
|
||||
uint32_t mhpm_counter_num);
|
||||
|
||||
void early_interrupt_handle();
|
||||
|
||||
void misaligned_pmp_fixup();
|
||||
|
||||
unsigned int insn_cnt;
|
||||
|
||||
public:
|
||||
|
@ -94,14 +119,15 @@ class SpikeCosim : public simif_t, public Cosim {
|
|||
const uint8_t *data_in) override;
|
||||
bool backdoor_read_mem(uint32_t addr, size_t len, uint8_t *data_out) override;
|
||||
bool step(uint32_t write_reg, uint32_t write_reg_data, uint32_t pc,
|
||||
bool sync_trap) override;
|
||||
bool sync_trap, bool suppress_reg_write) override;
|
||||
|
||||
bool check_retired_instr(uint32_t write_reg, uint32_t write_reg_data,
|
||||
uint32_t pc);
|
||||
uint32_t dut_pc, bool suppress_reg_write);
|
||||
bool check_sync_trap(uint32_t write_reg, uint32_t pc,
|
||||
uint32_t initial_spike_pc);
|
||||
void set_mip(uint32_t mip) override;
|
||||
void set_mip(uint32_t pre_mip, uint32_t post_mip) override;
|
||||
void set_nmi(bool nmi) override;
|
||||
void set_nmi_int(bool nmi_int) override;
|
||||
void set_debug_req(bool debug_req) override;
|
||||
void set_mcycle(uint64_t mcycle) override;
|
||||
void set_csr(const int csr_num, const uint32_t new_val) override;
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
// 'rtl' directory), see verilator_waiver_rtl.vlt in the same
|
||||
// directory.
|
||||
//
|
||||
// See https://www.veripool.org/projects/verilator/wiki/Manual-verilator#CONFIGURATION-FILES
|
||||
// See https://verilator.org/guide/latest/exe_verilator.html#configuration-files
|
||||
// for documentation.
|
||||
//
|
||||
// Important: This file must included *before* any other Verilog file is read.
|
||||
|
|
|
@ -161,6 +161,6 @@ targets:
|
|||
- '--trace-structs'
|
||||
- '--trace-params'
|
||||
- '--trace-max-array 1024'
|
||||
- '-CFLAGS "-std=c++11 -Wall -DVM_TRACE_FMT_FST -DTOPLEVEL_NAME=ibex_riscv_compliance -g"'
|
||||
- '-CFLAGS "-std=c++14 -Wall -DVM_TRACE_FMT_FST -DTOPLEVEL_NAME=ibex_riscv_compliance -g"'
|
||||
- '-LDFLAGS "-pthread -lutil -lelf"'
|
||||
- "-Wall"
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
// 'rtl' directory), see verilator_waiver_rtl.vlt in the same
|
||||
// directory.
|
||||
//
|
||||
// See https://www.veripool.org/projects/verilator/wiki/Manual-verilator#CONFIGURATION-FILES
|
||||
// See https://verilator.org/guide/latest/exe_verilator.html#configuration-files
|
||||
// for documentation.
|
||||
//
|
||||
// Important: This file must included *before* any other Verilog file is read.
|
||||
|
|
|
@ -155,60 +155,62 @@ module ibex_riscv_compliance (
|
|||
.DbgTriggerEn (DbgTriggerEn ),
|
||||
.SecureIbex (SecureIbex ),
|
||||
.ICacheScramble (ICacheScramble ),
|
||||
.DmBaseAddr (32'h00000000 ),
|
||||
.DmAddrMask (32'h00000003 ),
|
||||
.DmHaltAddr (32'h00000000 ),
|
||||
.DmExceptionAddr (32'h00000000 )
|
||||
) u_top (
|
||||
.clk_i (clk_sys ),
|
||||
.rst_ni (rst_sys_n ),
|
||||
.clk_i (clk_sys ),
|
||||
.rst_ni (rst_sys_n ),
|
||||
|
||||
.test_en_i ('b0 ),
|
||||
.scan_rst_ni (1'b1 ),
|
||||
.ram_cfg_i ('b0 ),
|
||||
.test_en_i ('b0 ),
|
||||
.scan_rst_ni (1'b1 ),
|
||||
.ram_cfg_i ('b0 ),
|
||||
|
||||
.hart_id_i (32'b0 ),
|
||||
.hart_id_i (32'b0 ),
|
||||
// First instruction executed is at 0x0 + 0x80
|
||||
.boot_addr_i (32'h00000000 ),
|
||||
.boot_addr_i (32'h00000000 ),
|
||||
|
||||
.instr_req_o (host_req[CoreI] ),
|
||||
.instr_gnt_i (host_gnt[CoreI] ),
|
||||
.instr_rvalid_i (host_rvalid[CoreI] ),
|
||||
.instr_addr_o (host_addr[CoreI] ),
|
||||
.instr_rdata_i (host_rdata[CoreI] ),
|
||||
.instr_rdata_intg_i (ibex_instr_rdata_intg ),
|
||||
.instr_err_i (host_err[CoreI] ),
|
||||
.instr_req_o (host_req[CoreI] ),
|
||||
.instr_gnt_i (host_gnt[CoreI] ),
|
||||
.instr_rvalid_i (host_rvalid[CoreI] ),
|
||||
.instr_addr_o (host_addr[CoreI] ),
|
||||
.instr_rdata_i (host_rdata[CoreI] ),
|
||||
.instr_rdata_intg_i (ibex_instr_rdata_intg),
|
||||
.instr_err_i (host_err[CoreI] ),
|
||||
|
||||
.data_req_o (host_req[CoreD] ),
|
||||
.data_gnt_i (host_gnt[CoreD] ),
|
||||
.data_rvalid_i (host_rvalid[CoreD] ),
|
||||
.data_we_o (host_we[CoreD] ),
|
||||
.data_be_o (host_be[CoreD] ),
|
||||
.data_addr_o (host_addr[CoreD] ),
|
||||
.data_wdata_o (host_wdata[CoreD] ),
|
||||
.data_wdata_intg_o ( ),
|
||||
.data_rdata_i (host_rdata[CoreD] ),
|
||||
.data_rdata_intg_i (ibex_data_rdata_intg ),
|
||||
.data_err_i (host_err[CoreD] ),
|
||||
.data_req_o (host_req[CoreD] ),
|
||||
.data_gnt_i (host_gnt[CoreD] ),
|
||||
.data_rvalid_i (host_rvalid[CoreD] ),
|
||||
.data_we_o (host_we[CoreD] ),
|
||||
.data_be_o (host_be[CoreD] ),
|
||||
.data_addr_o (host_addr[CoreD] ),
|
||||
.data_wdata_o (host_wdata[CoreD] ),
|
||||
.data_wdata_intg_o ( ),
|
||||
.data_rdata_i (host_rdata[CoreD] ),
|
||||
.data_rdata_intg_i (ibex_data_rdata_intg ),
|
||||
.data_err_i (host_err[CoreD] ),
|
||||
|
||||
.irq_software_i (1'b0 ),
|
||||
.irq_timer_i (1'b0 ),
|
||||
.irq_external_i (1'b0 ),
|
||||
.irq_fast_i (15'b0 ),
|
||||
.irq_nm_i (1'b0 ),
|
||||
.irq_software_i (1'b0 ),
|
||||
.irq_timer_i (1'b0 ),
|
||||
.irq_external_i (1'b0 ),
|
||||
.irq_fast_i (15'b0 ),
|
||||
.irq_nm_i (1'b0 ),
|
||||
|
||||
.scramble_key_valid_i ('0 ),
|
||||
.scramble_key_i ('0 ),
|
||||
.scramble_nonce_i ('0 ),
|
||||
.scramble_req_o ( ),
|
||||
.scramble_key_valid_i ('0 ),
|
||||
.scramble_key_i ('0 ),
|
||||
.scramble_nonce_i ('0 ),
|
||||
.scramble_req_o ( ),
|
||||
|
||||
.debug_req_i ('b0 ),
|
||||
.crash_dump_o ( ),
|
||||
.double_fault_seen_o ( ),
|
||||
.debug_req_i ('b0 ),
|
||||
.crash_dump_o ( ),
|
||||
.double_fault_seen_o ( ),
|
||||
|
||||
.fetch_enable_i (ibex_pkg::FetchEnableOn),
|
||||
.alert_minor_o ( ),
|
||||
.alert_major_internal_o ( ),
|
||||
.alert_major_bus_o ( ),
|
||||
.core_sleep_o ( )
|
||||
.fetch_enable_i (ibex_pkg::IbexMuBiOn ),
|
||||
.alert_minor_o ( ),
|
||||
.alert_major_internal_o ( ),
|
||||
.alert_major_bus_o ( ),
|
||||
.core_sleep_o ( )
|
||||
);
|
||||
|
||||
// SRAM block for instruction and data storage
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
project: ibex
|
||||
|
||||
// These keys are expected by dvsim.py, so we have to set them to something.
|
||||
book: bogus.book.domain
|
||||
doc_server: bogus.doc.server
|
||||
results_server: bogus.results.server
|
||||
results_html_name: report.html
|
||||
|
@ -14,6 +15,10 @@
|
|||
scratch_path: "{scratch_base_path}/{branch}"
|
||||
tool_srcs_dir: "{scratch_path}/{tool}"
|
||||
|
||||
// Common DVSIM data structures
|
||||
build_pass_patterns: []
|
||||
build_fail_patterns: []
|
||||
|
||||
// The current design level
|
||||
design_level: "ip"
|
||||
}
|
||||
|
|
|
@ -2,11 +2,18 @@
|
|||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Top-Level Makefile
|
||||
###############################################################################
|
||||
|
||||
.SUFFIXES:
|
||||
export
|
||||
|
||||
# Explicitly ask for the bash shell
|
||||
SHELL := bash
|
||||
|
||||
# Build the 'all' target by default, override with e.g. GOAL=rtl_tb_compile
|
||||
GOAL ?= all
|
||||
|
||||
###############################################################################
|
||||
# CONFIGURATION KNOBS
|
||||
|
||||
|
@ -29,7 +36,8 @@ SIMULATOR := xlm
|
|||
ISS := spike
|
||||
# Test name (default: full regression)
|
||||
TEST := all
|
||||
TESTLIST := riscv_dv_extension/testlist.yaml
|
||||
RISCV-DV-TESTLIST := riscv_dv_extension/testlist.yaml
|
||||
DIRECTED-TESTLIST := directed_tests/directed_testlist.yaml
|
||||
# Verbose logging
|
||||
VERBOSE := 0
|
||||
# Number of iterations for each test, assign a non-empty value to override the
|
||||
|
@ -41,362 +49,56 @@ SIGNATURE_ADDR := 8ffffffc
|
|||
### Ibex top level parameters ###
|
||||
IBEX_CONFIG := opentitan
|
||||
|
||||
# Path to DUT used for coverage reports
|
||||
DUT_COV_RTL_PATH := "ibex_top"
|
||||
|
||||
export EXTRA_COSIM_CFLAGS ?=
|
||||
|
||||
ifeq ($(COSIM_SIGSEGV_WORKAROUND), 1)
|
||||
EXTRA_COSIM_CFLAGS += -DCOSIM_SIGSEGV_WORKAROUND
|
||||
endif
|
||||
|
||||
###############################################################################
|
||||
|
||||
all: collect_results $(if $(filter 1,$(COV)),merge_cov,)
|
||||
# Setup the necessary paths for all python scripts to find all other relevant modules.
|
||||
export PYTHONPATH := $(shell python3 -c 'from scripts.setup_imports import get_pythonpath; get_pythonpath()')
|
||||
|
||||
# Build Stages
|
||||
.PHONY: core_config
|
||||
.PHONY: instr_gen_build
|
||||
.PHONY: instr_gen_run
|
||||
.PHONY: instr_gen_compile
|
||||
.PHONY: rtl_tb_compile
|
||||
.PHONY: rtl_sim_run
|
||||
.PHONY: check_logs
|
||||
.PHONY: riscv_dv_fcov
|
||||
.PHONY: merge_cov
|
||||
.PHONY: collect_results
|
||||
# We run the 'create_metadata' step in this top-level makefile, so the sub-make
|
||||
# invocations can query the generated metadata objects. Since the targets/dependencies
|
||||
# are extracted from this metadata, it must be query-able in the makefile 'immediate' stage.
|
||||
.PHONY: run
|
||||
run:
|
||||
@env PYTHONPATH=$(PYTHONPATH) python3 ./scripts/metadata.py \
|
||||
--op "create_metadata" \
|
||||
--dir-metadata $(METADATA-DIR) \
|
||||
--dir-out $(OUT-DIR) \
|
||||
--args-list "\
|
||||
SEED=$(SEED) WAVES=$(WAVES) COV=$(COV) SIMULATOR=$(SIMULATOR) \
|
||||
ISS=$(ISS) TEST=$(TEST) VERBOSE=$(VERBOSE) ITERATIONS=$(ITERATIONS) \
|
||||
SIGNATURE_ADDR=$(SIGNATURE_ADDR) IBEX_CONFIG=$(IBEX_CONFIG) \
|
||||
DUT_COV_RTL_PATH=$(DUT_COV_RTL_PATH)"
|
||||
@$(MAKE) --file wrapper.mk --environment-overrides --no-print-directory $(GOAL)
|
||||
|
||||
###############################################################################
|
||||
|
||||
# This is the top-level output directory. Everything we generate goes in
|
||||
# here.
|
||||
OUT := out
|
||||
|
||||
# Derived directories from $(OUT), used for stuff that's built once or
|
||||
# stuff that gets run for each seed, respectively. Using OUT-DIR on
|
||||
# the way avoids ugly double slashes if $(OUT) happens to end in a /.
|
||||
OUT-DIR := $(dir $(OUT)/)
|
||||
BUILD-DIR := $(OUT-DIR)build
|
||||
RUN-DIR := $(OUT-DIR)run
|
||||
METADATA-DIR = $(OUT-DIR)metadata
|
||||
export OUT-DIR := $(dir $(OUT)/)
|
||||
export METADATA-DIR := $(OUT-DIR)metadata
|
||||
|
||||
# This is a list of directories that are automatically generated by some
|
||||
# targets. To ensure the directory has been built, add an order-only dependency
|
||||
# (with the pipe symbol before it) on the directory name and add the directory
|
||||
# to this list.
|
||||
gen-dirs := $(BUILD-DIR)
|
||||
$(gen-dirs): %:
|
||||
mkdir -p $@
|
||||
|
||||
###############################################################################
|
||||
# Environment variables
|
||||
|
||||
GEN_DIR := $(realpath ../../../vendor/google_riscv-dv)
|
||||
TOOLCHAIN := ${RISCV_TOOLCHAIN}
|
||||
EXT_DIR := riscv_dv_extension
|
||||
|
||||
export IBEX_ROOT := $(realpath ../../../)
|
||||
export PRJ_DIR := $(realpath ../../..)
|
||||
export LOWRISC_IP_DIR := $(realpath ${PRJ_DIR}/vendor/lowrisc_ip)
|
||||
|
||||
# Needed for tcl files that are used with Cadence tools.
|
||||
export dv_root := $(realpath ../../../vendor/lowrisc_ip/dv)
|
||||
export DUT_TOP := dut
|
||||
|
||||
# Setup the necessary paths for all python scripts to find all other relevant modules.
|
||||
PYTHONPATH := $(shell python3 -c 'from scripts.setup_imports import get_pythonpath; get_pythonpath()')
|
||||
# export PYTHONPATH := $(PYTHONPATH) ## Why doesn't this work?
|
||||
# riscv-dv extension directory
|
||||
export EXT_DIR := riscv_dv_extension
|
||||
|
||||
###############################################################################
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f $(EXT_DIR)/riscv_core_setting.sv
|
||||
rm -rf $(OUT-DIR)
|
||||
rm -f $(EXT_DIR)/riscv_core_setting.sv
|
||||
|
||||
###############################################################################
|
||||
# Setup the metadata for the regression, which can then be accessed by
|
||||
# all python scripts and testcases
|
||||
# It needs to run before anything else.
|
||||
new-metadata-file := $(shell env PYTHONPATH=$(PYTHONPATH) python3 ./scripts/metadata.py \
|
||||
--op "create_metadata" \
|
||||
--dir-metadata $(METADATA-DIR) \
|
||||
--dir-out $(OUT-DIR) \
|
||||
--args-list "\
|
||||
SEED=$(SEED) WAVES=$(WAVES) COV=$(COV) SIMULATOR=$(SIMULATOR) \
|
||||
ISS=$(ISS) TEST=$(TEST) VERBOSE=$(VERBOSE) ITERATIONS=$(ITERATIONS) \
|
||||
SIGNATURE_ADDR=$(SIGNATURE_ADDR) IBEX_CONFIG=$(IBEX_CONFIG)")
|
||||
|
||||
### TODO ##
|
||||
# Evaluate input variables to more-cleverly schedule partial-rebuilds
|
||||
# This allows us to use Make to handle build scheduling and to calculate rebuilds,
|
||||
# while keeping all the structured-data in the land of Python.
|
||||
define get-metadata-variable
|
||||
env PYTHONPATH=$(PYTHONPATH) python3 ./scripts/metadata.py \
|
||||
--op "print_field" \
|
||||
--dir-metadata $(METADATA-DIR) \
|
||||
--field $(1)
|
||||
endef
|
||||
define get-meta
|
||||
$(shell $(call get-metadata-variable, $(1)))
|
||||
endef
|
||||
# This is how you can get variables from the python metadata easily...
|
||||
testvar := $(call get-meta,"ibex_root")
|
||||
### TODO ###
|
||||
|
||||
###############################################################################
|
||||
# Here we express the different build artifacts that the Makefile uses to
|
||||
# establish the dependency tree, as well as which jobs depend on which
|
||||
# top-level configuration knobs when deciding what to rebuild.
|
||||
# Use build artifacts as targets where appropriate, otherwise use stamp-files.
|
||||
|
||||
tests-and-seeds := $(shell env PYTHONPATH=$(PYTHONPATH) python3 ./scripts/metadata.py \
|
||||
--op "tests_and_seeds" \
|
||||
--dir-metadata $(METADATA-DIR) $(new-metadata-file))
|
||||
ts-dirs = $(foreach ts,$(tests-and-seeds),$(RUN-DIR)/$(ts)/)
|
||||
test-asms = $(addsuffix test.S,$(ts-dirs))
|
||||
test-bins = $(addsuffix test.bin,$(ts-dirs))
|
||||
rtl-sim-logs = $(addsuffix $(rtl-sim-logfile),$(ts-dirs))
|
||||
comp-results = $(addsuffix trr.yaml,$(ts-dirs))
|
||||
rtl-sim-logfile := rtl_sim.log
|
||||
|
||||
###
|
||||
CORE-CONFIG-STAMP = $(METADATA-DIR)/core.config.stamp
|
||||
core_config: $(CORE-CONFIG-STAMP)
|
||||
core-config-var-deps := IBEX_CONFIG
|
||||
|
||||
INSTR-GEN-BUILD-STAMP = $(METADATA-DIR)/instr.gen.build.stamp
|
||||
instr_gen_build: $(METADATA-DIR)/instr.gen.build.stamp
|
||||
instr-gen-build-var-deps := SIMULATOR SIGNATURE_ADDR # Rebuild if these change
|
||||
|
||||
instr_gen_run: $(test-asms)
|
||||
|
||||
instr_gen_compile: $(test-bins)
|
||||
|
||||
TB-COMPILE-STAMP = $(METADATA-DIR)/tb.compile.stamp
|
||||
rtl_tb_compile: $(METADATA-DIR)/tb.compile.stamp
|
||||
rtl-tb-compile-var-deps := SIMULATOR COV WAVES # Rebuild if these change
|
||||
|
||||
rtl_sim_run: $(rtl-sim-logs)
|
||||
|
||||
check_logs: $(comp-results)
|
||||
|
||||
FCOV-STAMP = $(METADATA-DIR)/fcov.stamp
|
||||
riscv_dv_fcov: $(METADATA-DIR)/fcov.stamp
|
||||
|
||||
MERGE-COV-STAMP = $(METADATA-DIR)/merge.cov.stamp
|
||||
merge_cov: $(METADATA-DIR)/merge.cov.stamp
|
||||
|
||||
REGR-LOG-STAMP = $(METADATA-DIR)/regr.log.stamp
|
||||
collect_results: $(METADATA-DIR)/regr.log.stamp
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Other groups of files we may depend on are...
|
||||
|
||||
riscv-dv-files := $(shell find $(GEN_DIR) -type f)
|
||||
# A variable containing a file list for the riscv-dv vendored-in module.
|
||||
# Depending on these files gives a safe over-approximation that will ensure we
|
||||
# rebuild things if that module changes.
|
||||
|
||||
all-verilog = \
|
||||
$(shell find ../../../rtl -name '*.v' -o -name '*.sv' -o -name '*.svh') \
|
||||
$(shell find ../.. -name '*.v' -o -name '*.sv' -o -name '*.svh')
|
||||
all-cpp = \
|
||||
$(shell find ../.. -name '*.cc' -o -name '*.h')
|
||||
# The compiled ibex testbench (obviously!) also depends on the design and the
|
||||
# DV code. The clever way of doing this would be to look at a dependency
|
||||
# listing generated by the simulator as a side-effect of doing the compile (a
|
||||
# bit like using the -M flags with a C compiler). Unfortunately, that doesn't
|
||||
# look like it's particularly easy, so we'll just depend on every .v, .sv or
|
||||
# .svh file in the dv or rtl directories. Note that this variable is set with
|
||||
# '=', rather than ':='. This means that we don't bother running the find
|
||||
# commands unless we need the compiled testbench.
|
||||
|
||||
|
||||
###############################################################################
|
||||
###############################################################################
|
||||
# Build the Random Instruction Generator
|
||||
#
|
||||
###############################################################################
|
||||
include util.mk # VARIABLE DUMPING UTILS
|
||||
###############################################################################
|
||||
######## EXAMPLE OF VARIABLE DUMPING ############
|
||||
# This target depends on the vendored in code in $(GEN_DIR). It also depends on the
|
||||
# values of the following Makefile variables (we want to regenerate things if,
|
||||
# for example, the simulator changes).
|
||||
|
||||
# To achieve this variable tracking, we dump each of the variables to a Makefile
|
||||
# fragment and try to load it up the next time around. This done with the
|
||||
# utility function "dump-vars" at the end of the recipe.
|
||||
#
|
||||
# To create the dependency, we must do the following two things before each
|
||||
# target:
|
||||
#
|
||||
# First, load up the saved variable values from the last time around. If this
|
||||
# fails, it's no problem: we'll assume that the previous run either doesn't
|
||||
# exist or something went wrong.
|
||||
ig-build-vars-path := $(BUILD-DIR)/.instr_gen.vars.mk
|
||||
-include $(ig-build-vars-path)
|
||||
|
||||
# Next, compare the current variables to those we just loaded. This uses the
|
||||
# utility function "vars-prereq". It creates a variable which evaluates to the
|
||||
# (phony) FORCE if the two sets of variables do not match.
|
||||
#
|
||||
# Note that we define it with '=', not ':=', so we don't evaluate if we're not
|
||||
# trying to run the instr_gen_build target.
|
||||
instr-gen-build-vars-prereq = \
|
||||
$(call vars-prereq, \
|
||||
gen, \
|
||||
building instruction generator, \
|
||||
$(instr-gen-build-var-deps))
|
||||
|
||||
# Finally, $(instr-gen-build-vars-prereq) becomes a dependency of our target.
|
||||
################## END EXAMPLE ###################
|
||||
|
||||
core-config-vars-path := $(BUILD-DIR)/.cc.vars.mk
|
||||
-include $(core-config-vars-path)
|
||||
|
||||
core-config-var-prereq = $(call vars-prereq,gen,Generate core configuration file,$(core-config-var-deps))
|
||||
$(CORE-CONFIG-STAMP): \
|
||||
$(core-config-var-prereq) ./riscv_dv_extension/riscv_core_setting.tpl.sv \
|
||||
scripts/render_config_template.py \
|
||||
| $(BUILD-DIR)
|
||||
@echo Generating core configuration file
|
||||
$(verb)env PYTHONPATH=$(PYTHONPATH) \
|
||||
scripts/render_config_template.py \
|
||||
--dir-metadata $(METADATA-DIR) \
|
||||
$(EXT_DIR)/riscv_core_setting.tpl.sv > $(EXT_DIR)/riscv_core_setting.sv
|
||||
$(call dump-vars,$(core-config-vars-path),gen,$(core-config-var-deps))
|
||||
@touch $@
|
||||
|
||||
$(METADATA-DIR)/instr.gen.build.stamp: \
|
||||
$(instr-gen-build-vars-prereq) $(riscv-dv-files) $(CORE-CONFIG-STAMP) \
|
||||
scripts/build_instr_gen.py \
|
||||
| $(BUILD-DIR)
|
||||
@echo Building randomized test generator
|
||||
$(verb)env PYTHONPATH=$(PYTHONPATH) \
|
||||
scripts/build_instr_gen.py \
|
||||
--dir-metadata $(METADATA-DIR)
|
||||
$(call dump-vars,$(ig-build-vars-path),gen,$(instr-gen-build-var-deps))
|
||||
@touch $@
|
||||
|
||||
###############################################################################
|
||||
# Run the random instruction generator
|
||||
#
|
||||
# Make use of static-pattern rules
|
||||
# https://www.gnu.org/software/make/manual/html_node/Static-Usage.html#Static-Usage
|
||||
#
|
||||
# targets …: target-pattern: prereq-patterns …
|
||||
# recipe
|
||||
# …
|
||||
|
||||
$(test-asms): \
|
||||
$(RUN-DIR)/%/test.S: \
|
||||
$(INSTR-GEN-BUILD-STAMP) $(TESTLIST) scripts/run_instr_gen.py
|
||||
@echo Running randomized test generator to create assembly file $@
|
||||
$(verb)env PYTHONPATH=$(PYTHONPATH) \
|
||||
scripts/run_instr_gen.py \
|
||||
--dir-metadata $(METADATA-DIR) \
|
||||
--test-dot-seed $*
|
||||
|
||||
###############################################################################
|
||||
# Compile the generated assembly programs
|
||||
#
|
||||
# We don't explicitly track dependencies on the RISCV toolchain, so this
|
||||
# doesn't depend on anything more than the instr_gen stage did.
|
||||
#
|
||||
# Note that the compilation step generates a .o file and then uses
|
||||
# objcopy to create a .bin. The ISS run uses the .o and the RTL run
|
||||
# uses the .bin. In the Makefile, we just track the .bin to represent
|
||||
# both.
|
||||
|
||||
$(test-bins): \
|
||||
$(RUN-DIR)/%/test.bin: $(RUN-DIR)/%/test.S \
|
||||
scripts/compile_generated_test.py
|
||||
@echo Compiling generated test assembly to create binary at $@
|
||||
$(verb)env PYTHONPATH=$(PYTHONPATH) \
|
||||
scripts/compile_generated_test.py \
|
||||
--dir-metadata $(METADATA-DIR) \
|
||||
--test-dot-seed $*
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Compile ibex core TB
|
||||
#
|
||||
# Note that this doesn't depend on the seed: the DUT doesn't depend on which
|
||||
# test we're running!
|
||||
|
||||
tb-compile-vars-path := $(BUILD-DIR)/.tb.vars.mk
|
||||
-include $(tb-compile-vars-path)
|
||||
tb-compile-vars-prereq = $(call vars-prereq,comp,compiling TB,$(rtl-tb-compile-var-deps))
|
||||
|
||||
$(METADATA-DIR)/tb.compile.stamp: \
|
||||
$(tb-compile-vars-prereq) $(all-verilog) $(all-cpp) $(risc-dv-files) \
|
||||
scripts/compile_tb.py yaml/rtl_simulation.yaml \
|
||||
| $(BUILD-DIR)
|
||||
@echo Building RTL testbench
|
||||
$(verb)env PYTHONPATH=$(PYTHONPATH) \
|
||||
scripts/compile_tb.py \
|
||||
--dir-metadata $(METADATA-DIR)
|
||||
$(call dump-vars,$(tb-compile-vars-path),comp,$(rtl-tb-compile-var-deps))
|
||||
@touch $@
|
||||
|
||||
###############################################################################
|
||||
# Run ibex RTL simulation with randomly-generated program and uvm stimulus
|
||||
|
||||
$(rtl-sim-logs): \
|
||||
$(RUN-DIR)/%/$(rtl-sim-logfile): \
|
||||
$(TB-COMPILE-STAMP) $(RUN-DIR)/%/test.bin scripts/run_rtl.py
|
||||
@echo Running RTL simulation at $(@D)
|
||||
$(verb)env PYTHONPATH=$(PYTHONPATH) \
|
||||
scripts/run_rtl.py \
|
||||
--dir-metadata $(METADATA-DIR) \
|
||||
--test-dot-seed $*
|
||||
|
||||
###############################################################################
|
||||
# Gather RTL sim results, and parse logs for errors
|
||||
|
||||
$(comp-results): \
|
||||
$(RUN-DIR)/%/trr.yaml: \
|
||||
$(RUN-DIR)/%/$(rtl-sim-logfile) scripts/check_logs.py
|
||||
@echo Collecting simulation results and checking logs of testcase at $@
|
||||
$(verb)env PYTHONPATH=$(PYTHONPATH) \
|
||||
scripts/check_logs.py \
|
||||
--dir-metadata $(METADATA-DIR) \
|
||||
--test-dot-seed $*
|
||||
|
||||
###############################################################################
|
||||
# Generate RISCV-DV functional coverage
|
||||
# TODO(udi) - add B extension
|
||||
|
||||
$(METADATA-DIR)/fcov.stamp: $(comp-results) \
|
||||
scripts/get_fcov.py
|
||||
@echo Generating RISCV_DV functional coverage
|
||||
$(verb)env PYTHONPATH=$(PYTHONPATH) \
|
||||
scripts/get_fcov.py \
|
||||
--dir-metadata $(METADATA-DIR)
|
||||
@touch $@
|
||||
|
||||
###############################################################################
|
||||
# Merge all output coverage directories
|
||||
# Any coverage databases generated from the riscv_dv_fcov target will be merged
|
||||
# as well.
|
||||
|
||||
$(METADATA-DIR)/merge.cov.stamp: $(FCOV-STAMP) \
|
||||
scripts/merge_cov.py
|
||||
@echo Merging all recorded coverage data into a single report
|
||||
$(verb)env PYTHONPATH=$(PYTHONPATH) \
|
||||
scripts/merge_cov.py \
|
||||
--dir-metadata $(METADATA-DIR)
|
||||
@touch $@
|
||||
|
||||
###############################################################################
|
||||
# Generate the summarized regression log
|
||||
|
||||
$(METADATA-DIR)/regr.log.stamp: scripts/collect_results.py $(comp-results)
|
||||
@echo Collecting up results of tests into report regr.log
|
||||
$(verb)env PYTHONPATH=$(PYTHONPATH) \
|
||||
./scripts/collect_results.py \
|
||||
--dir-metadata $(METADATA-DIR)
|
||||
@touch $@
|
||||
|
||||
###############################################################################
|
||||
# Extras (for convenience)
|
||||
.PHONY: prettify
|
||||
prettify:
|
||||
@./scripts/prettify.sh
|
||||
|
||||
.PHONY: dump
|
||||
dump:
|
||||
@./scripts/objdump.sh
|
||||
|
|
6
dv/uvm/core_ibex/common/date.c
Normal file
6
dv/uvm/core_ibex/common/date.c
Normal file
|
@ -0,0 +1,6 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include <time.h>
|
||||
long int get_unix_timestamp() { return time(NULL); }
|
10
dv/uvm/core_ibex/common/date_dpi.svh
Normal file
10
dv/uvm/core_ibex/common/date_dpi.svh
Normal file
|
@ -0,0 +1,10 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
`ifndef DATE_DPI_SVH
|
||||
`define DATE_DPI_SVH
|
||||
|
||||
import "DPI-C" function longint get_unix_timestamp();
|
||||
|
||||
`endif
|
|
@ -44,4 +44,37 @@ class ibex_cosim_agent extends uvm_agent;
|
|||
function void write_mem_byte(bit [31:0] addr, bit [7:0] d);
|
||||
riscv_cosim_write_mem_byte(scoreboard.cosim_handle, addr, d);
|
||||
endfunction
|
||||
|
||||
function void write_mem_word(bit [31:0] addr, bit [DATA_WIDTH-1:0] d);
|
||||
for (int i = 0; i < DATA_WIDTH / 8; i++) begin
|
||||
write_mem_byte(addr + i, d[7:0]);
|
||||
d = d >> 8;
|
||||
end
|
||||
endfunction
|
||||
|
||||
// Backdoor-load the test binary file into the cosim memory model
|
||||
function void load_binary_to_mem(bit[31:0] base_addr, string bin);
|
||||
bit [7:0] r8;
|
||||
bit [31:0] addr = base_addr;
|
||||
int bin_fd;
|
||||
bin_fd = $fopen(bin,"rb");
|
||||
if (!bin_fd)
|
||||
`uvm_fatal(get_full_name(), $sformatf("Cannot open file %0s", bin))
|
||||
while ($fread(r8,bin_fd)) begin
|
||||
`uvm_info(`gfn, $sformatf("Init mem [0x%h] = 0x%0h", addr, r8), UVM_FULL)
|
||||
write_mem_byte(addr, r8);
|
||||
addr++;
|
||||
end
|
||||
endfunction
|
||||
|
||||
function void reset();
|
||||
scoreboard.rvfi_port.flush();
|
||||
scoreboard.dmem_port.flush();
|
||||
scoreboard.imem_port.flush();
|
||||
scoreboard.ifetch_port.flush();
|
||||
scoreboard.ifetch_pmp_port.flush();
|
||||
|
||||
scoreboard.reset_e.trigger();
|
||||
endfunction : reset
|
||||
|
||||
endclass : ibex_cosim_agent
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
package ibex_cosim_agent_pkg;
|
||||
import uvm_pkg::*;
|
||||
import ibex_mem_intf_agent_pkg::*;
|
||||
import ibex_mem_intf_pkg::*;
|
||||
|
||||
`include "uvm_macros.svh"
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
`include "cosim_dpi.svh"
|
||||
|
||||
class ibex_cosim_scoreboard extends uvm_scoreboard;
|
||||
import ibex_pkg::*;
|
||||
chandle cosim_handle;
|
||||
|
||||
core_ibex_cosim_cfg cfg;
|
||||
|
@ -17,7 +16,11 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
|
|||
uvm_tlm_analysis_fifo #(ibex_ifetch_seq_item) ifetch_port;
|
||||
uvm_tlm_analysis_fifo #(ibex_ifetch_pmp_seq_item) ifetch_pmp_port;
|
||||
|
||||
virtual core_ibex_instr_monitor_if instr_vif;
|
||||
virtual core_ibex_instr_monitor_if instr_vif;
|
||||
virtual core_ibex_dut_probe_if dut_vif;
|
||||
|
||||
uvm_event reset_e;
|
||||
uvm_event check_inserted_iside_error_e;
|
||||
|
||||
bit failed_iside_accesses [bit[31:0]];
|
||||
bit iside_pmp_failure [bit[31:0]];
|
||||
|
@ -34,12 +37,14 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
|
|||
function new(string name="", uvm_component parent=null);
|
||||
super.new(name, parent);
|
||||
|
||||
rvfi_port = new("rvfi_port", this);
|
||||
dmem_port = new("dmem_port", this);
|
||||
imem_port = new("imem_port", this);
|
||||
ifetch_port = new("ifetch_port", this);
|
||||
ifetch_pmp_port = new("ifetch_pmp_port", this);
|
||||
cosim_handle = null;
|
||||
rvfi_port = new("rvfi_port", this);
|
||||
dmem_port = new("dmem_port", this);
|
||||
imem_port = new("imem_port", this);
|
||||
ifetch_port = new("ifetch_port", this);
|
||||
ifetch_pmp_port = new("ifetch_pmp_port", this);
|
||||
cosim_handle = null;
|
||||
reset_e = new();
|
||||
check_inserted_iside_error_e = new();
|
||||
endfunction
|
||||
|
||||
function void build_phase(uvm_phase phase);
|
||||
|
@ -54,6 +59,11 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
|
|||
`uvm_fatal(`gfn, "Cannot get instr_monitor_if")
|
||||
end
|
||||
|
||||
if (!uvm_config_db#(virtual core_ibex_dut_probe_if)::get(null, "", "dut_if",
|
||||
dut_vif)) begin
|
||||
`uvm_fatal(`gfn, "Cannot get dut_probe_if")
|
||||
end
|
||||
|
||||
init_cosim();
|
||||
endfunction : build_phase
|
||||
|
||||
|
@ -77,29 +87,26 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
|
|||
endfunction
|
||||
|
||||
virtual task run_phase(uvm_phase phase);
|
||||
wait (instr_vif.instr_cb.reset === 1'b0);
|
||||
|
||||
forever begin
|
||||
fork begin: isolation_fork
|
||||
fork
|
||||
run_cosim_rvfi();
|
||||
run_cosim_dmem();
|
||||
run_cosim_imem_errors();
|
||||
if (cfg.probe_imem_for_errs) begin
|
||||
run_cosim_imem();
|
||||
end else begin
|
||||
fork
|
||||
run_cosim_ifetch();
|
||||
run_cosim_ifetch_pmp();
|
||||
join_any
|
||||
end
|
||||
|
||||
wait (instr_vif.instr_cb.reset === 1'b1);
|
||||
join_any
|
||||
disable fork;
|
||||
end join
|
||||
if (instr_vif.instr_cb.reset === 1'b1) handle_reset();
|
||||
end
|
||||
@(negedge instr_vif.reset)
|
||||
fork : isolation_fork
|
||||
run_cosim_rvfi();
|
||||
run_cosim_dmem();
|
||||
run_cosim_imem_errors();
|
||||
run_cosim_prune_imem_errors();
|
||||
if (cfg.probe_imem_for_errs) begin
|
||||
run_cosim_imem();
|
||||
end else begin
|
||||
fork
|
||||
run_cosim_ifetch();
|
||||
run_cosim_ifetch_pmp();
|
||||
join_any
|
||||
end
|
||||
join_none
|
||||
reset_e.wait_trigger();
|
||||
disable fork;
|
||||
handle_reset();
|
||||
end // forever
|
||||
endtask : run_phase
|
||||
|
||||
task run_cosim_rvfi();
|
||||
|
@ -108,34 +115,51 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
|
|||
forever begin
|
||||
rvfi_port.get(rvfi_instr);
|
||||
|
||||
// Remove entries from iside_error_queue where the instruction never reaches the RVFI
|
||||
// interface because it was flushed.
|
||||
while (iside_error_queue.size() > 0 && iside_error_queue[0].order < rvfi_instr.order) begin
|
||||
iside_error_queue.pop_front();
|
||||
if (rvfi_instr.irq_only) begin
|
||||
// RVFI item is only notifying about new interrupts, not a retired instruction, so provide
|
||||
// cosim with interrupt information and loop back to await the next item.
|
||||
riscv_cosim_set_nmi(cosim_handle, rvfi_instr.nmi);
|
||||
riscv_cosim_set_nmi_int(cosim_handle, rvfi_instr.nmi_int);
|
||||
riscv_cosim_set_mip(cosim_handle, rvfi_instr.pre_mip, rvfi_instr.pre_mip);
|
||||
|
||||
continue;
|
||||
end
|
||||
|
||||
// Check if the top of the iside_error_queue relates to the current RVFI instruction. If so
|
||||
// notify the cosim environment of an instruction error.
|
||||
if (iside_error_queue.size() !=0 && iside_error_queue[0].order == rvfi_instr.order) begin
|
||||
riscv_cosim_set_iside_error(cosim_handle, iside_error_queue[0].addr);
|
||||
iside_error_queue.pop_front();
|
||||
if (iside_error_queue.size() > 0) begin
|
||||
// Remove entries from iside_error_queue where the instruction never reaches the RVFI
|
||||
// interface because it was flushed.
|
||||
while (iside_error_queue.size() > 0 && iside_error_queue[0].order < rvfi_instr.order) begin
|
||||
iside_error_queue.pop_front();
|
||||
end
|
||||
|
||||
// Check if the top of the iside_error_queue relates to the current RVFI instruction. If so
|
||||
// notify the cosim environment of an instruction error.
|
||||
if (iside_error_queue.size() !=0 && iside_error_queue[0].order == rvfi_instr.order) begin
|
||||
riscv_cosim_set_iside_error(cosim_handle, iside_error_queue[0].addr);
|
||||
iside_error_queue.pop_front();
|
||||
end
|
||||
end
|
||||
|
||||
riscv_cosim_set_nmi(cosim_handle, rvfi_instr.nmi);
|
||||
riscv_cosim_set_mip(cosim_handle, rvfi_instr.mip);
|
||||
// Note these must be called in this order to ensure debug vs nmi vs normal interrupt are
|
||||
// handled with the correct priority when they occur together.
|
||||
riscv_cosim_set_debug_req(cosim_handle, rvfi_instr.debug_req);
|
||||
riscv_cosim_set_nmi(cosim_handle, rvfi_instr.nmi);
|
||||
riscv_cosim_set_nmi_int(cosim_handle, rvfi_instr.nmi_int);
|
||||
riscv_cosim_set_mip(cosim_handle, rvfi_instr.pre_mip, rvfi_instr.post_mip);
|
||||
riscv_cosim_set_mcycle(cosim_handle, rvfi_instr.mcycle);
|
||||
|
||||
// Set performance counters through a pseudo-backdoor write
|
||||
for (int i=0; i < 10; i++) begin
|
||||
riscv_cosim_set_csr(cosim_handle, CSR_MHPMCOUNTER3 + i, rvfi_instr.mhpmcounters[i]);
|
||||
riscv_cosim_set_csr(cosim_handle, CSR_MHPMCOUNTER3H + i, rvfi_instr.mhpmcountersh[i]);
|
||||
riscv_cosim_set_csr(cosim_handle,
|
||||
ibex_pkg::CSR_MHPMCOUNTER3 + i, rvfi_instr.mhpmcounters[i]);
|
||||
riscv_cosim_set_csr(cosim_handle,
|
||||
ibex_pkg::CSR_MHPMCOUNTER3H + i, rvfi_instr.mhpmcountersh[i]);
|
||||
end
|
||||
|
||||
riscv_cosim_set_ic_scr_key_valid(cosim_handle, rvfi_instr.ic_scr_key_valid);
|
||||
|
||||
if (!riscv_cosim_step(cosim_handle, rvfi_instr.rd_addr, rvfi_instr.rd_wdata, rvfi_instr.pc,
|
||||
rvfi_instr.trap)) begin
|
||||
rvfi_instr.trap, rvfi_instr.rf_wr_suppress)) begin
|
||||
// cosim instruction step doesn't match rvfi captured instruction, report a fatal error
|
||||
// with the details
|
||||
if (cfg.relax_cosim_check) begin
|
||||
|
@ -154,7 +178,8 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
|
|||
dmem_port.get(mem_op);
|
||||
// Notify the cosim of all dside accesses emitted by the RTL
|
||||
riscv_cosim_notify_dside_access(cosim_handle, mem_op.read_write == WRITE, mem_op.addr,
|
||||
mem_op.data, mem_op.be, mem_op.error, mem_op.misaligned_first, mem_op.misaligned_second);
|
||||
mem_op.data, mem_op.be, mem_op.error, mem_op.misaligned_first, mem_op.misaligned_second,
|
||||
mem_op.misaligned_first_saw_error, mem_op.m_mode_access);
|
||||
end
|
||||
endtask: run_cosim_dmem
|
||||
|
||||
|
@ -242,6 +267,16 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
|
|||
wait (instr_vif.instr_cb.valid_id &&
|
||||
instr_vif.instr_cb.instr_new_id &&
|
||||
latest_order != instr_vif.instr_cb.rvfi_order_id);
|
||||
|
||||
latest_order = instr_vif.instr_cb.rvfi_order_id;
|
||||
|
||||
if (dut_vif.dut_cb.wb_exception)
|
||||
// If an exception in writeback occurs the instruction in ID will be flushed and hence not
|
||||
// produce an iside error so skip the rest of the loop. A writeback exception may occur
|
||||
// after this cycle before the instruction in ID moves out of the ID stage. The
|
||||
// `run_cosim_prune_imem_errors` task deals with this case.
|
||||
continue;
|
||||
|
||||
// Determine if the instruction comes from an address that has seen an error that wasn't a PMP
|
||||
// error (the icache records both PMP errors and fetch errors with the same error bits). If a
|
||||
// fetch error was seen add the instruction order ID and address to iside_error_queue.
|
||||
|
@ -252,6 +287,7 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
|
|||
begin
|
||||
iside_error_queue.push_back('{order : instr_vif.instr_cb.rvfi_order_id,
|
||||
addr : aligned_addr});
|
||||
check_inserted_iside_error_e.trigger();
|
||||
end else if (!instr_vif.instr_cb.is_compressed_id &&
|
||||
(instr_vif.instr_cb.pc_id & 32'h3) != 0 &&
|
||||
failed_iside_accesses.exists(aligned_next_addr) &&
|
||||
|
@ -261,12 +297,36 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
|
|||
// side of the boundary
|
||||
iside_error_queue.push_back('{order : instr_vif.instr_cb.rvfi_order_id,
|
||||
addr : aligned_next_addr});
|
||||
check_inserted_iside_error_e.trigger();
|
||||
end
|
||||
|
||||
latest_order = instr_vif.instr_cb.rvfi_order_id;
|
||||
end
|
||||
endtask: run_cosim_imem_errors;
|
||||
|
||||
task run_cosim_prune_imem_errors();
|
||||
// Errors are added to the iside error queue the first cycle the instruction that sees the error
|
||||
// is in the ID stage. Cycles following this the writeback stage may cause an exception flushing
|
||||
// the ID stage so the iside error never occurs. When this happens we need to pop the new iside
|
||||
// error off the queue.
|
||||
forever begin
|
||||
// Wait until the `run_cosim_imem_errors` task notifies us it's added a error to the queue
|
||||
check_inserted_iside_error_e.wait_ptrigger();
|
||||
// Wait for the next clock
|
||||
@(instr_vif.instr_cb);
|
||||
// Wait for a new instruction or a writeback exception. When a new instruction has entered the
|
||||
// ID stage and we haven't seen a writeback exception we know the instruction associated with the
|
||||
// error just added to the queue isn't getting flushed.
|
||||
wait (instr_vif.instr_cb.instr_new_id || dut_vif.dut_cb.wb_exception);
|
||||
|
||||
if (!instr_vif.instr_cb.instr_new_id && dut_vif.dut_cb.wb_exception) begin
|
||||
// If we hit a writeback exception without seeing a new instruction then the newly added
|
||||
// error relates to an instruction just flushed from the ID stage so pop it from the
|
||||
// queue.
|
||||
iside_error_queue.pop_back();
|
||||
end
|
||||
end
|
||||
endtask: run_cosim_prune_imem_errors
|
||||
|
||||
function string get_cosim_error_str();
|
||||
string error = "Cosim mismatch ";
|
||||
for (int i = 0; i < riscv_cosim_get_num_errors(cosim_handle); ++i) begin
|
||||
|
@ -294,6 +354,5 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
|
|||
|
||||
task handle_reset();
|
||||
init_cosim();
|
||||
wait (instr_vif.instr_cb.reset === 1'b0);
|
||||
endtask
|
||||
endclass : ibex_cosim_scoreboard
|
||||
|
|
|
@ -27,18 +27,22 @@ class ibex_rvfi_monitor extends uvm_monitor;
|
|||
|
||||
forever begin
|
||||
// Wait for a retired instruction
|
||||
while(!vif.monitor_cb.valid) vif.wait_clks(1);
|
||||
while(!(vif.monitor_cb.valid || vif.monitor_cb.ext_irq_valid)) vif.wait_clks(1);
|
||||
|
||||
// Read instruction details from RVFI interface
|
||||
trans_collected = ibex_rvfi_seq_item::type_id::create("trans_collected");
|
||||
trans_collected.irq_only = !vif.monitor_cb.valid && vif.monitor_cb.ext_irq_valid;
|
||||
trans_collected.trap = vif.monitor_cb.trap;
|
||||
trans_collected.pc = vif.monitor_cb.pc_rdata;
|
||||
trans_collected.rd_addr = vif.monitor_cb.rd_addr;
|
||||
trans_collected.rd_wdata = vif.monitor_cb.rd_wdata;
|
||||
trans_collected.order = vif.monitor_cb.order;
|
||||
trans_collected.mip = vif.monitor_cb.ext_mip;
|
||||
trans_collected.pre_mip = vif.monitor_cb.ext_pre_mip;
|
||||
trans_collected.post_mip = vif.monitor_cb.ext_post_mip;
|
||||
trans_collected.nmi = vif.monitor_cb.ext_nmi;
|
||||
trans_collected.nmi_int = vif.monitor_cb.ext_nmi_int;
|
||||
trans_collected.debug_req = vif.monitor_cb.ext_debug_req;
|
||||
trans_collected.rf_wr_suppress = vif.monitor_cb.ext_rf_wr_suppress;
|
||||
trans_collected.mcycle = vif.monitor_cb.ext_mcycle;
|
||||
trans_collected.ic_scr_key_valid = vif.monitor_cb.ext_ic_scr_key_valid;
|
||||
|
||||
|
|
|
@ -3,14 +3,18 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class ibex_rvfi_seq_item extends uvm_sequence_item;
|
||||
bit irq_only;
|
||||
bit trap;
|
||||
bit [31:0] pc;
|
||||
bit [4:0] rd_addr;
|
||||
bit [31:0] rd_wdata;
|
||||
bit [63:0] order;
|
||||
bit [31:0] mip;
|
||||
bit [31:0] pre_mip;
|
||||
bit [31:0] post_mip;
|
||||
bit nmi;
|
||||
bit nmi_int;
|
||||
bit debug_req;
|
||||
bit rf_wr_suppress;
|
||||
bit [63:0] mcycle;
|
||||
|
||||
bit [31:0] mhpmcounters [10];
|
||||
|
@ -23,9 +27,12 @@ class ibex_rvfi_seq_item extends uvm_sequence_item;
|
|||
`uvm_field_int (rd_addr, UVM_DEFAULT)
|
||||
`uvm_field_int (rd_wdata, UVM_DEFAULT)
|
||||
`uvm_field_int (order, UVM_DEFAULT)
|
||||
`uvm_field_int (mip, UVM_DEFAULT)
|
||||
`uvm_field_int (pre_mip, UVM_DEFAULT)
|
||||
`uvm_field_int (post_mip, UVM_DEFAULT)
|
||||
`uvm_field_int (nmi, UVM_DEFAULT)
|
||||
`uvm_field_int (nmi_int, UVM_DEFAULT)
|
||||
`uvm_field_int (debug_req, UVM_DEFAULT)
|
||||
`uvm_field_int (rf_wr_suppress, UVM_DEFAULT)
|
||||
`uvm_field_int (mcycle, UVM_DEFAULT)
|
||||
`uvm_field_sarray_int (mhpmcounters, UVM_DEFAULT)
|
||||
`uvm_field_sarray_int (mhpmcountersh, UVM_DEFAULT)
|
||||
|
|
|
@ -24,6 +24,9 @@ interface ibex_mem_intf#(
|
|||
wire error;
|
||||
wire misaligned_first;
|
||||
wire misaligned_second;
|
||||
wire misaligned_first_saw_error;
|
||||
wire m_mode_access;
|
||||
wire spurious_response;
|
||||
|
||||
clocking request_driver_cb @(posedge clk);
|
||||
input reset;
|
||||
|
@ -38,6 +41,7 @@ interface ibex_mem_intf#(
|
|||
input rdata;
|
||||
input rintg;
|
||||
input error;
|
||||
input spurious_response;
|
||||
endclocking
|
||||
|
||||
clocking response_driver_cb @(posedge clk);
|
||||
|
@ -53,6 +57,7 @@ interface ibex_mem_intf#(
|
|||
output rdata;
|
||||
output rintg;
|
||||
output error;
|
||||
output spurious_response;
|
||||
endclocking
|
||||
|
||||
clocking monitor_cb @(posedge clk);
|
||||
|
@ -70,6 +75,9 @@ interface ibex_mem_intf#(
|
|||
input error;
|
||||
input misaligned_first;
|
||||
input misaligned_second;
|
||||
input misaligned_first_saw_error;
|
||||
input m_mode_access;
|
||||
input spurious_response;
|
||||
endclocking
|
||||
|
||||
task automatic wait_clks(input int num);
|
||||
|
|
|
@ -5,16 +5,11 @@
|
|||
package ibex_mem_intf_agent_pkg;
|
||||
|
||||
import uvm_pkg::*;
|
||||
import ibex_mem_intf_pkg::*;
|
||||
import mem_model_pkg::*;
|
||||
|
||||
parameter int DATA_WIDTH = 32;
|
||||
parameter int ADDR_WIDTH = 32;
|
||||
parameter int INTG_WIDTH = 7;
|
||||
|
||||
typedef enum { READ, WRITE } rw_e;
|
||||
import ibex_cosim_agent_pkg::*;
|
||||
|
||||
`include "uvm_macros.svh"
|
||||
`include "ibex_mem_intf_seq_item.sv"
|
||||
|
||||
typedef uvm_sequencer#(ibex_mem_intf_seq_item) ibex_mem_intf_request_sequencer;
|
||||
|
||||
|
@ -27,4 +22,8 @@ package ibex_mem_intf_agent_pkg;
|
|||
`include "ibex_mem_intf_request_driver.sv"
|
||||
`include "ibex_mem_intf_request_agent.sv"
|
||||
|
||||
// Re-export parameters from ibex_mem_intf_pkg so that other packages can access them through this
|
||||
// package.
|
||||
export ibex_mem_intf_pkg::*;
|
||||
|
||||
endpackage
|
||||
|
|
|
@ -10,18 +10,28 @@ class ibex_mem_intf_monitor extends uvm_monitor;
|
|||
|
||||
protected virtual ibex_mem_intf vif;
|
||||
|
||||
// The monitor tick event fires every clock cycle once any writes to
|
||||
// outstanding_access_port and addr_ph_ports have occurred.
|
||||
event monitor_tick;
|
||||
|
||||
mailbox #(ibex_mem_intf_seq_item) collect_response_queue;
|
||||
uvm_analysis_port#(ibex_mem_intf_seq_item) item_collected_port;
|
||||
uvm_analysis_port#(ibex_mem_intf_seq_item) addr_ph_port;
|
||||
// The number of outstanding accesses is written to this port every clock cycle
|
||||
uvm_analysis_port#(int) outstanding_accesses_port;
|
||||
|
||||
`uvm_component_utils(ibex_mem_intf_monitor)
|
||||
`uvm_component_new
|
||||
|
||||
int outstanding_accesses = 0;
|
||||
|
||||
function void build_phase(uvm_phase phase);
|
||||
super.build_phase(phase);
|
||||
item_collected_port = new("item_collected_port", this);
|
||||
addr_ph_port = new("addr_ph_port_monitor", this);
|
||||
collect_response_queue = new();
|
||||
outstanding_accesses_port = new("outstanding_accesses_port", this);
|
||||
|
||||
if(!uvm_config_db#(virtual ibex_mem_intf)::get(this, "", "vif", vif)) begin
|
||||
`uvm_fatal("NOVIF",{"virtual interface must be set for: ",get_full_name(),".vif"});
|
||||
end
|
||||
|
@ -30,13 +40,15 @@ class ibex_mem_intf_monitor extends uvm_monitor;
|
|||
virtual task run_phase(uvm_phase phase);
|
||||
wait (vif.monitor_cb.reset === 1'b0);
|
||||
forever begin
|
||||
fork : check_mem_intf
|
||||
collect_address_phase();
|
||||
collect_response_phase();
|
||||
wait (vif.monitor_cb.reset === 1'b1);
|
||||
join_any
|
||||
// Will only reach this point when mid-test reset is asserted
|
||||
disable fork;
|
||||
fork begin : isolation_fork
|
||||
fork : check_mem_intf
|
||||
collect_address_phase();
|
||||
collect_response_phase();
|
||||
wait (vif.monitor_cb.reset === 1'b1);
|
||||
join_any
|
||||
// Will only reach this point when mid-test reset is asserted
|
||||
disable fork;
|
||||
end join
|
||||
handle_reset();
|
||||
end
|
||||
endtask : run_phase
|
||||
|
@ -50,16 +62,32 @@ class ibex_mem_intf_monitor extends uvm_monitor;
|
|||
|
||||
virtual protected task collect_address_phase();
|
||||
ibex_mem_intf_seq_item trans_collected;
|
||||
|
||||
|
||||
forever begin
|
||||
@(vif.monitor_cb);
|
||||
|
||||
trans_collected = ibex_mem_intf_seq_item::type_id::create("trans_collected");
|
||||
while(!(vif.monitor_cb.request && vif.monitor_cb.grant)) vif.wait_clks(1);
|
||||
trans_collected.addr = vif.monitor_cb.addr;
|
||||
trans_collected.be = vif.monitor_cb.be;
|
||||
trans_collected.misaligned_first = vif.monitor_cb.misaligned_first;
|
||||
trans_collected.misaligned_second = vif.monitor_cb.misaligned_second;
|
||||
|
||||
while (!(vif.monitor_cb.request && vif.monitor_cb.grant)) begin
|
||||
if (vif.monitor_cb.rvalid && !vif.monitor_cb.spurious_response) begin
|
||||
outstanding_accesses--;
|
||||
end
|
||||
outstanding_accesses_port.write(outstanding_accesses);
|
||||
|
||||
-> monitor_tick;
|
||||
@(vif.monitor_cb);
|
||||
end
|
||||
|
||||
trans_collected.addr = vif.monitor_cb.addr;
|
||||
trans_collected.be = vif.monitor_cb.be;
|
||||
trans_collected.misaligned_first = vif.monitor_cb.misaligned_first;
|
||||
trans_collected.misaligned_second = vif.monitor_cb.misaligned_second;
|
||||
trans_collected.misaligned_first_saw_error = vif.monitor_cb.misaligned_first_saw_error;
|
||||
trans_collected.m_mode_access = vif.monitor_cb.m_mode_access;
|
||||
`uvm_info(get_full_name(), $sformatf("Detect request with address: %0x",
|
||||
trans_collected.addr), UVM_HIGH)
|
||||
if(vif.monitor_cb.we) begin
|
||||
if (vif.monitor_cb.we) begin
|
||||
trans_collected.read_write = WRITE;
|
||||
trans_collected.data = vif.monitor_cb.wdata;
|
||||
trans_collected.intg = vif.monitor_cb.wintg;
|
||||
|
@ -69,7 +97,14 @@ class ibex_mem_intf_monitor extends uvm_monitor;
|
|||
addr_ph_port.write(trans_collected);
|
||||
`uvm_info(get_full_name(),"Send through addr_ph_port", UVM_HIGH)
|
||||
collect_response_queue.put(trans_collected);
|
||||
vif.wait_clks(1);
|
||||
|
||||
outstanding_accesses++;
|
||||
if (vif.monitor_cb.rvalid && !vif.monitor_cb.spurious_response) begin
|
||||
outstanding_accesses--;
|
||||
end
|
||||
outstanding_accesses_port.write(outstanding_accesses);
|
||||
|
||||
-> monitor_tick;
|
||||
end
|
||||
endtask : collect_address_phase
|
||||
|
||||
|
@ -78,8 +113,8 @@ class ibex_mem_intf_monitor extends uvm_monitor;
|
|||
forever begin
|
||||
collect_response_queue.get(trans_collected);
|
||||
do
|
||||
vif.wait_clks(1);
|
||||
while(vif.monitor_cb.rvalid === 0);
|
||||
@(vif.monitor_cb);
|
||||
while(vif.monitor_cb.rvalid === 0 || vif.monitor_cb.spurious_response === 1);
|
||||
|
||||
if (trans_collected.read_write == READ) begin
|
||||
trans_collected.data = vif.monitor_cb.rdata;
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package ibex_mem_intf_pkg;
|
||||
|
||||
import uvm_pkg::*;
|
||||
|
||||
parameter int DATA_WIDTH = 32;
|
||||
parameter int ADDR_WIDTH = 32;
|
||||
parameter int INTG_WIDTH = 7;
|
||||
|
||||
typedef enum { READ, WRITE } rw_e;
|
||||
|
||||
`include "uvm_macros.svh"
|
||||
`include "ibex_mem_intf_seq_item.sv"
|
||||
|
||||
endpackage
|
|
@ -81,7 +81,7 @@ class ibex_mem_intf_request_driver extends uvm_driver #(ibex_mem_intf_seq_item);
|
|||
forever begin
|
||||
rdata_queue.get(tr);
|
||||
vif.wait_clks(1);
|
||||
while(vif.rvalid !== 1'b1) vif.wait_clks(1);
|
||||
while((vif.rvalid !== 1'b1 || vif.spurious_response === 1'b1)) vif.wait_clks(1);
|
||||
if(tr.read_write == READ)
|
||||
tr.data = vif.request_driver_cb.rdata;
|
||||
tr.intg = vif.request_driver_cb.rintg;
|
||||
|
|
|
@ -21,7 +21,10 @@ class ibex_mem_intf_response_agent extends uvm_agent;
|
|||
|
||||
super.build_phase(phase);
|
||||
monitor = ibex_mem_intf_monitor::type_id::create("monitor", this);
|
||||
cfg = ibex_mem_intf_response_agent_cfg::type_id::create("cfg", this);
|
||||
if (cfg == null)
|
||||
if(!uvm_config_db #(ibex_mem_intf_response_agent_cfg)::get(this, "", "cfg", cfg))
|
||||
`uvm_fatal(`gfn, "Could not locate mem_intf cfg object in uvm_config_db!")
|
||||
|
||||
if(get_is_active() == UVM_ACTIVE) begin
|
||||
driver = ibex_mem_intf_response_driver::type_id::create("driver", this);
|
||||
sequencer = ibex_mem_intf_response_sequencer::type_id::create("sequencer", this);
|
||||
|
@ -41,9 +44,12 @@ class ibex_mem_intf_response_agent extends uvm_agent;
|
|||
if(get_is_active() == UVM_ACTIVE) begin
|
||||
driver.seq_item_port.connect(sequencer.seq_item_export);
|
||||
monitor.addr_ph_port.connect(sequencer.addr_ph_port.analysis_export);
|
||||
monitor.outstanding_accesses_port.connect(sequencer.outstanding_accesses_imp);
|
||||
end
|
||||
driver.cfg = cfg;
|
||||
sequencer.cfg = cfg;
|
||||
|
||||
sequencer.monitor_tick = monitor.monitor_tick;
|
||||
endfunction : connect_phase
|
||||
|
||||
function void reset();
|
||||
|
|
|
@ -36,7 +36,11 @@ class ibex_mem_intf_response_agent_cfg extends uvm_object;
|
|||
// Default set to 50% for zero delay to be picked
|
||||
int unsigned zero_delay_pct = 50;
|
||||
|
||||
`uvm_object_new
|
||||
// CONTROL_KNOB : enable/disable to generation of bad integrity upon uninit accesses
|
||||
bit enable_bad_intg_on_uninit_access = 0;
|
||||
|
||||
int unsigned spurious_response_delay_min = 0;
|
||||
int unsigned spurious_response_delay_max = 100;
|
||||
|
||||
constraint zero_delays_c {
|
||||
zero_delays dist {1 :/ zero_delay_pct,
|
||||
|
@ -44,13 +48,20 @@ class ibex_mem_intf_response_agent_cfg extends uvm_object;
|
|||
}
|
||||
|
||||
`uvm_object_utils_begin(ibex_mem_intf_response_agent_cfg)
|
||||
`uvm_field_int(fixed_data_write_response, UVM_DEFAULT)
|
||||
`uvm_field_int(gnt_delay_min, UVM_DEFAULT)
|
||||
`uvm_field_int(gnt_delay_max, UVM_DEFAULT)
|
||||
`uvm_field_int(valid_delay_min, UVM_DEFAULT)
|
||||
`uvm_field_int(valid_delay_max, UVM_DEFAULT)
|
||||
`uvm_field_int(zero_delays, UVM_DEFAULT)
|
||||
`uvm_field_int(zero_delay_pct, UVM_DEFAULT)
|
||||
`uvm_field_int(fixed_data_write_response, UVM_DEFAULT)
|
||||
`uvm_field_int(gnt_delay_min, UVM_DEFAULT)
|
||||
`uvm_field_int(gnt_delay_max, UVM_DEFAULT)
|
||||
`uvm_field_int(valid_delay_min, UVM_DEFAULT)
|
||||
`uvm_field_int(valid_delay_max, UVM_DEFAULT)
|
||||
`uvm_field_int(zero_delays, UVM_DEFAULT)
|
||||
`uvm_field_int(zero_delay_pct, UVM_DEFAULT)
|
||||
`uvm_field_int(spurious_response_delay_min, UVM_DEFAULT)
|
||||
`uvm_field_int(spurious_response_delay_max, UVM_DEFAULT)
|
||||
|
||||
`uvm_object_utils_end
|
||||
|
||||
function new(string name = "");
|
||||
super.new(name);
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
|
|
@ -24,13 +24,15 @@ class ibex_mem_intf_response_driver extends uvm_driver #(ibex_mem_intf_seq_item)
|
|||
reset_signals();
|
||||
wait (cfg.vif.response_driver_cb.reset === 1'b0);
|
||||
forever begin
|
||||
fork : drive_stimulus
|
||||
send_grant();
|
||||
get_and_drive();
|
||||
wait (cfg.vif.response_driver_cb.reset === 1'b1);
|
||||
join_any
|
||||
// Will only be reached after mid-test reset
|
||||
disable fork;
|
||||
fork begin : isolation_fork
|
||||
fork : drive_stimulus
|
||||
send_grant();
|
||||
get_and_drive();
|
||||
wait (cfg.vif.response_driver_cb.reset === 1'b1);
|
||||
join_any
|
||||
// Will only be reached after mid-test reset
|
||||
disable fork;
|
||||
end join
|
||||
handle_reset();
|
||||
end
|
||||
endtask : run_phase
|
||||
|
@ -60,11 +62,12 @@ class ibex_mem_intf_response_driver extends uvm_driver #(ibex_mem_intf_seq_item)
|
|||
|
||||
virtual protected task get_and_drive();
|
||||
wait (cfg.vif.response_driver_cb.reset === 1'b0);
|
||||
|
||||
fork
|
||||
begin
|
||||
forever begin
|
||||
ibex_mem_intf_seq_item req, req_c;
|
||||
cfg.vif.wait_clks(1);
|
||||
@(cfg.vif.response_driver_cb);
|
||||
seq_item_port.get_next_item(req);
|
||||
$cast(req_c, req.clone());
|
||||
if(~cfg.vif.response_driver_cb.reset) begin
|
||||
|
@ -110,28 +113,44 @@ class ibex_mem_intf_response_driver extends uvm_driver #(ibex_mem_intf_seq_item)
|
|||
virtual protected task send_read_data();
|
||||
ibex_mem_intf_seq_item tr;
|
||||
forever begin
|
||||
cfg.vif.wait_clks(1);
|
||||
cfg.vif.response_driver_cb.rvalid <= 1'b0;
|
||||
cfg.vif.response_driver_cb.rdata <= 'x;
|
||||
cfg.vif.response_driver_cb.rintg <= 'x;
|
||||
cfg.vif.response_driver_cb.error <= 'x;
|
||||
@(cfg.vif.response_driver_cb);
|
||||
cfg.vif.response_driver_cb.rvalid <= 1'b0;
|
||||
cfg.vif.response_driver_cb.spurious_response <= 1'b0;
|
||||
cfg.vif.response_driver_cb.rdata <= 'x;
|
||||
cfg.vif.response_driver_cb.rintg <= 'x;
|
||||
cfg.vif.response_driver_cb.error <= 'x;
|
||||
|
||||
rdata_queue.get(tr);
|
||||
|
||||
`uvm_info(`gfn, $sformatf("Got response for addr %x", tr.addr), UVM_HIGH)
|
||||
|
||||
if(cfg.vif.response_driver_cb.reset) continue;
|
||||
cfg.vif.wait_clks(tr.rvalid_delay);
|
||||
|
||||
repeat (tr.rvalid_delay) @(cfg.vif.response_driver_cb);
|
||||
|
||||
if(~cfg.vif.response_driver_cb.reset) begin
|
||||
if (tr.spurious_response) begin
|
||||
`uvm_info(`gfn, $sformatf("Driving spurious response"), UVM_HIGH)
|
||||
end else begin
|
||||
`uvm_info(`gfn, $sformatf("Driving response for addr %x", tr.addr), UVM_HIGH)
|
||||
end
|
||||
|
||||
cfg.vif.response_driver_cb.rvalid <= 1'b1;
|
||||
cfg.vif.response_driver_cb.error <= tr.error;
|
||||
// A spurious response is not associated with any request. This is flagged in the vif
|
||||
// signals so other components in the testbench can ignore them if needed.
|
||||
cfg.vif.response_driver_cb.spurious_response <= tr.spurious_response;
|
||||
|
||||
if (tr.read_write == READ) begin
|
||||
cfg.vif.response_driver_cb.rdata <= tr.data;
|
||||
cfg.vif.response_driver_cb.rintg <= tr.intg;
|
||||
end else begin
|
||||
// rdata and intg fields aren't relevant to write responses
|
||||
if (cfg.fixed_data_write_response) begin
|
||||
// when fixed_data_write_response is set, set rdata to fixed value with correct matching
|
||||
// rintg field.
|
||||
cfg.vif.response_driver_cb.rdata <= 32'hffffffff;
|
||||
cfg.vif.response_driver_cb.rintg <=
|
||||
prim_secded_pkg::prim_secded_inv_39_32_enc(32'hffffffff)[38:32];
|
||||
// when fixed_data_write_response is set, sequence item is responsible for producing
|
||||
// fixed values so just copy them across here.
|
||||
cfg.vif.response_driver_cb.rdata <= tr.data;
|
||||
cfg.vif.response_driver_cb.rintg <= tr.intg;
|
||||
end else begin
|
||||
// when fixed_data_write_response is not set, drive the irrelevant fields to x.
|
||||
cfg.vif.response_driver_cb.rdata <= 'x;
|
||||
|
|
|
@ -10,20 +10,41 @@ class ibex_mem_intf_response_seq extends uvm_sequence #(ibex_mem_intf_seq_item);
|
|||
|
||||
ibex_mem_intf_seq_item item;
|
||||
mem_model m_mem;
|
||||
ibex_cosim_agent cosim_agent;
|
||||
bit enable_intg_error = 1'b0;
|
||||
bit enable_error = 1'b0;
|
||||
// Used to ensure that whenever inject_error() is called, the very next transaction will inject an
|
||||
// error, and that enable_error will not be flipped back to 0 immediately
|
||||
bit error_synch = 1'b1;
|
||||
bit is_dmem_seq = 1'b0;
|
||||
bit suppress_error_on_exc = 1'b0;
|
||||
bit enable_spurious_response = 1'b0;
|
||||
|
||||
|
||||
`uvm_object_utils(ibex_mem_intf_response_seq)
|
||||
`uvm_declare_p_sequencer(ibex_mem_intf_response_sequencer)
|
||||
`uvm_object_new
|
||||
|
||||
rand int unsigned spurious_response_delay_cycles;
|
||||
|
||||
constraint spurious_response_delay_cycles_c {
|
||||
spurious_response_delay_cycles inside {[p_sequencer.cfg.spurious_response_delay_min :
|
||||
p_sequencer.cfg.spurious_response_delay_max]};
|
||||
}
|
||||
|
||||
virtual task body();
|
||||
if(m_mem == null)
|
||||
`uvm_fatal(get_full_name(), "Cannot get memory model")
|
||||
virtual core_ibex_dut_probe_if ibex_dut_vif;
|
||||
|
||||
if (!uvm_config_db#(virtual core_ibex_dut_probe_if)::get(null, "", "dut_if",
|
||||
ibex_dut_vif)) begin
|
||||
`uvm_fatal(`gfn, "failed to get ibex dut_if from uvm_config_db")
|
||||
end
|
||||
|
||||
if (m_mem == null) `uvm_fatal(get_full_name(), "Cannot get memory model")
|
||||
`uvm_info(`gfn, $sformatf("is_dmem_seq: 0x%0x", is_dmem_seq), UVM_LOW)
|
||||
|
||||
`DV_CHECK_MEMBER_RANDOMIZE_FATAL(spurious_response_delay_cycles)
|
||||
|
||||
forever
|
||||
begin
|
||||
bit [ADDR_WIDTH-1:0] aligned_addr;
|
||||
|
@ -32,9 +53,54 @@ class ibex_mem_intf_response_seq extends uvm_sequence #(ibex_mem_intf_seq_item);
|
|||
bit [INTG_WIDTH-1:0] read_intg;
|
||||
bit data_was_uninitialized = 1'b0;
|
||||
|
||||
p_sequencer.addr_ph_port.get(item);
|
||||
if (enable_spurious_response) begin
|
||||
// When spurious responses are enabled we wake every monitor tick to decide whether to
|
||||
// insert a spurious response.
|
||||
while (1) begin
|
||||
@p_sequencer.monitor_tick;
|
||||
|
||||
if (p_sequencer.addr_ph_port.try_get(item)) begin
|
||||
// If we have a new request proceed as normal.
|
||||
break;
|
||||
end
|
||||
|
||||
if ((spurious_response_delay_cycles == 0)
|
||||
&& (p_sequencer.outstanding_accesses == 0)) begin
|
||||
|
||||
// If we've hit the time generate a new spurious responses and there's no outstanding
|
||||
// responses (we must only generate a spurious response when the interface is idle)
|
||||
// send one to the driver.
|
||||
req = ibex_mem_intf_seq_item::type_id::create("req");
|
||||
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(req, rvalid_delay == 0;)
|
||||
|
||||
req.spurious_response = 1'b1;
|
||||
{req.intg, req.data} = prim_secded_pkg::prim_secded_inv_39_32_enc(req.data);
|
||||
|
||||
`uvm_info(`gfn, $sformatf("Generated spurious response:\n%0s", req.sprint()), UVM_HIGH)
|
||||
start_item(req);
|
||||
finish_item(req);
|
||||
|
||||
`DV_CHECK_MEMBER_RANDOMIZE_FATAL(spurious_response_delay_cycles)
|
||||
end else if (spurious_response_delay_cycles > 0) begin
|
||||
spurious_response_delay_cycles = spurious_response_delay_cycles - 1;
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
// Without spurious responses just wait for the monitor to report a new request
|
||||
p_sequencer.addr_ph_port.get(item);
|
||||
end
|
||||
|
||||
aligned_addr = {item.addr[DATA_WIDTH-1:2], 2'b0};
|
||||
|
||||
req = ibex_mem_intf_seq_item::type_id::create("req");
|
||||
error_synch = 1'b0;
|
||||
if (suppress_error_on_exc &&
|
||||
(ibex_dut_vif.dut_cb.sync_exc_seen || ibex_dut_vif.dut_cb.irq_exc_seen)) begin
|
||||
enable_error = 1'b0;
|
||||
enable_intg_error = 1'b0;
|
||||
end
|
||||
|
||||
if (!req.randomize() with {
|
||||
addr == item.addr;
|
||||
read_write == item.read_write;
|
||||
|
@ -57,27 +123,29 @@ class ibex_mem_intf_response_seq extends uvm_sequence #(ibex_mem_intf_seq_item);
|
|||
}) begin
|
||||
`uvm_fatal(`gfn, "Cannot randomize response request")
|
||||
end
|
||||
enable_error = 1'b0;
|
||||
|
||||
error_synch = 1'b1;
|
||||
enable_error = 1'b0; // Disable after single inserted error.
|
||||
aligned_addr = {req.addr[DATA_WIDTH-1:2], 2'b0};
|
||||
// Do not inject any error to the handshake test_control_addr
|
||||
// TODO: Parametrize this. Until then, this needs to be changed manually.
|
||||
if (aligned_addr inside {32'h8ffffff8, 32'h8ffffffc}) begin
|
||||
req.error = 1'b0;
|
||||
enable_intg_error = 1'b0;
|
||||
end
|
||||
if (req.error) begin
|
||||
`DV_CHECK_STD_RANDOMIZE_FATAL(rand_data)
|
||||
req.data = rand_data;
|
||||
end else begin
|
||||
if(req.read_write == READ) begin : READ_block
|
||||
if (is_dmem_seq) begin
|
||||
for (int i = DATA_WIDTH / 8 - 1; i >= 0; i--) begin
|
||||
read_data = read_data << 8;
|
||||
if (req.be[i])
|
||||
read_data[7:0] = m_mem.read_byte(aligned_addr + i);
|
||||
end
|
||||
req.data = read_data;
|
||||
end else begin
|
||||
// Allow fetches from uninitialised IMEM: the core can run ahead of what's actually
|
||||
// initialised and we don't want to kill the simulation. When this happens, we set the
|
||||
// data_was_uninitialized flag and set req.data to zero.
|
||||
req.data = read(.addr(aligned_addr), .not_set(data_was_uninitialized));
|
||||
end
|
||||
end else if(item.read_write == READ) begin
|
||||
// Get data from memory_model, handle uninit memory accesses.
|
||||
req.data = read(aligned_addr, data_was_uninitialized);
|
||||
end else if(item.read_write == WRITE) begin
|
||||
// Update memory_model
|
||||
write(aligned_addr, item.data);
|
||||
if (p_sequencer.cfg.fixed_data_write_response) begin
|
||||
// When fixed_data_write_response is set drive data in store response to fixed
|
||||
// 32'hffffffff value. Integrity is calculated below.
|
||||
req.data = 32'hffffffff;
|
||||
end
|
||||
end
|
||||
// Add integrity bits
|
||||
|
@ -85,20 +153,15 @@ class ibex_mem_intf_response_seq extends uvm_sequence #(ibex_mem_intf_seq_item);
|
|||
|
||||
// If data_was_uninitialized is true then we want to force bad integrity bits: invert the
|
||||
// correct ones, which we know will break things for the codes we use.
|
||||
if (data_was_uninitialized) req.intg = ~req.intg;
|
||||
if ((p_sequencer.cfg.enable_bad_intg_on_uninit_access && data_was_uninitialized) || enable_intg_error) begin
|
||||
req.intg = ~req.intg;
|
||||
enable_intg_error = 1'b0;
|
||||
end
|
||||
|
||||
`uvm_info(get_full_name(), $sformatf("Response transfer:\n%0s", req.sprint()), UVM_HIGH)
|
||||
start_item(req);
|
||||
finish_item(req);
|
||||
if(item.read_write == WRITE) begin : WRITE_block
|
||||
bit [DATA_WIDTH-1:0] data;
|
||||
data = req.data;
|
||||
for (int i = 0; i < DATA_WIDTH / 8; i++) begin
|
||||
if (req.be[i])
|
||||
m_mem.write_byte(aligned_addr + i, data[7:0]);
|
||||
data = data >> 8;
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
endtask : body
|
||||
|
||||
|
@ -106,23 +169,71 @@ class ibex_mem_intf_response_seq extends uvm_sequence #(ibex_mem_intf_seq_item);
|
|||
this.enable_error = 1'b1;
|
||||
endfunction
|
||||
|
||||
virtual function void inject_intg_error();
|
||||
this.enable_intg_error = 1'b1;
|
||||
endfunction
|
||||
|
||||
virtual function bit get_error_synch();
|
||||
return this.error_synch;
|
||||
endfunction
|
||||
|
||||
// Read a word of DATA_WIDTH bits at addr.
|
||||
//
|
||||
// Return 0xabababab and write not_set = 1'b1 for addresses where there is no architectural value
|
||||
// (rather than generating an error).
|
||||
protected function logic [DATA_WIDTH-1:0] read(bit [ADDR_WIDTH-1:0] addr, output bit not_set);
|
||||
for (int i = 0; i < DATA_WIDTH / 8; i++) begin
|
||||
if (!m_mem.system_memory.exists(addr + i)) begin
|
||||
not_set = 1'b1;
|
||||
return {DATA_WIDTH/8{8'hab}};
|
||||
// Read a word of DATA_WIDTH bits from addr.
|
||||
// Handle reads fromm uninit memory as follows:
|
||||
// - DMEM : return a random value
|
||||
// - IMEM : return {2{C.unimp}}
|
||||
protected function logic [DATA_WIDTH-1:0] read(bit [ADDR_WIDTH-1:0] addr,
|
||||
output bit did_access_uninit_mem);
|
||||
logic [DATA_WIDTH-1:0] data = '0;
|
||||
bit [7:0] byte_data = '0;
|
||||
bit byte_is_uninit = 1'b0;
|
||||
for (int i = (DATA_WIDTH / 8) - 1; i >= 0 ; i--) begin
|
||||
data = data << 8;
|
||||
byte_data = read_byte(addr + i, byte_is_uninit);
|
||||
if (byte_is_uninit) begin
|
||||
did_access_uninit_mem = 1'b1;
|
||||
// If any byte of the access comes back as uninit, bork the whole access.
|
||||
if (is_dmem_seq) begin
|
||||
// DMEM
|
||||
`DV_CHECK_STD_RANDOMIZE_FATAL(byte_data)
|
||||
// Update mem_model(s) with the randomized data.
|
||||
`uvm_info(`gfn,
|
||||
$sformatf("Addr is uninit! DMEM seq, returning random data 0x%0h", data),
|
||||
UVM_MEDIUM)
|
||||
m_mem.write_byte(addr + i, byte_data); // Update UVM mem_model
|
||||
cosim_agent.write_mem_byte(addr + i, byte_data); // Update cosim mem_model
|
||||
end else begin
|
||||
// IMEM
|
||||
`uvm_info(`gfn,
|
||||
$sformatf("Addr is uninit! IMEM seq, returning 0x0000 (c.unimp)"),
|
||||
UVM_MEDIUM)
|
||||
return {2{16'h0000}}; // 2x C.unimp instructions
|
||||
end
|
||||
end
|
||||
data[7:0] = byte_data;
|
||||
end
|
||||
not_set = 1'b0;
|
||||
return m_mem.read(addr);
|
||||
return data;
|
||||
endfunction
|
||||
|
||||
// Write a word of DATA_WIDTH bits at addr.
|
||||
protected function void write(bit [ADDR_WIDTH-1:0] addr, bit [DATA_WIDTH-1:0] data);
|
||||
for (int i = 0; i < DATA_WIDTH / 8; i++) begin
|
||||
if (req.be[i])
|
||||
m_mem.write_byte(addr + i, data[7:0]);
|
||||
data = data >> 8;
|
||||
end
|
||||
endfunction
|
||||
|
||||
// Re-implement the read_byte function from mem_model.sv, but without the fatal assertion.
|
||||
function bit [7:0] read_byte(bit [ADDR_WIDTH-1:0] addr, output bit is_byte_uninit);
|
||||
bit [7:0] data = '0;
|
||||
if (!m_mem.addr_exists(addr)) begin
|
||||
`uvm_info(`gfn, $sformatf("Read from uninitialized addr 0x%0h", addr), UVM_MEDIUM)
|
||||
is_byte_uninit = 1'b1;
|
||||
end else begin
|
||||
data = m_mem.system_memory[addr];
|
||||
`uvm_info(`gfn, $sformatf("Read Mem : Addr[0x%0h], Data[0x%0h]", addr, data), UVM_HIGH)
|
||||
end
|
||||
return data;
|
||||
endfunction
|
||||
|
||||
endclass : ibex_mem_intf_response_seq
|
||||
|
|
|
@ -10,13 +10,18 @@ class ibex_mem_intf_response_sequencer extends uvm_sequencer #(ibex_mem_intf_seq
|
|||
|
||||
// TLM port to peek the address phase from the response monitor
|
||||
uvm_tlm_analysis_fifo #(ibex_mem_intf_seq_item) addr_ph_port;
|
||||
uvm_analysis_imp #(int, ibex_mem_intf_response_sequencer) outstanding_accesses_imp;
|
||||
ibex_mem_intf_response_agent_cfg cfg;
|
||||
|
||||
event monitor_tick = null;
|
||||
int outstanding_accesses = 0;
|
||||
|
||||
`uvm_component_utils(ibex_mem_intf_response_sequencer)
|
||||
|
||||
function new (string name, uvm_component parent);
|
||||
super.new(name, parent);
|
||||
addr_ph_port = new("addr_ph_port_sequencer", this);
|
||||
outstanding_accesses_imp = new("outstanding_access_imp", this);
|
||||
endfunction : new
|
||||
|
||||
// On reset, empty the tlm fifo
|
||||
|
@ -24,4 +29,8 @@ class ibex_mem_intf_response_sequencer extends uvm_sequencer #(ibex_mem_intf_seq
|
|||
addr_ph_port.flush();
|
||||
endfunction
|
||||
|
||||
function void write(int x);
|
||||
outstanding_accesses = x;
|
||||
endfunction
|
||||
|
||||
endclass : ibex_mem_intf_response_sequencer
|
||||
|
|
|
@ -17,20 +17,26 @@ class ibex_mem_intf_seq_item extends uvm_sequence_item;
|
|||
rand bit [3:0] req_delay;
|
||||
rand bit [5:0] rvalid_delay;
|
||||
rand bit error;
|
||||
bit spurious_response;
|
||||
bit misaligned_first;
|
||||
bit misaligned_second;
|
||||
bit misaligned_first_saw_error;
|
||||
bit m_mode_access;
|
||||
|
||||
`uvm_object_utils_begin(ibex_mem_intf_seq_item)
|
||||
`uvm_field_int (addr, UVM_DEFAULT)
|
||||
`uvm_field_enum (rw_e, read_write, UVM_DEFAULT)
|
||||
`uvm_field_int (be, UVM_DEFAULT)
|
||||
`uvm_field_int (data, UVM_DEFAULT)
|
||||
`uvm_field_int (intg, UVM_DEFAULT)
|
||||
`uvm_field_int (gnt_delay, UVM_DEFAULT)
|
||||
`uvm_field_int (rvalid_delay, UVM_DEFAULT)
|
||||
`uvm_field_int (error, UVM_DEFAULT)
|
||||
`uvm_field_int (misaligned_first, UVM_DEFAULT)
|
||||
`uvm_field_int (misaligned_second, UVM_DEFAULT)
|
||||
`uvm_field_int (addr, UVM_DEFAULT)
|
||||
`uvm_field_enum (rw_e, read_write, UVM_DEFAULT)
|
||||
`uvm_field_int (be, UVM_DEFAULT)
|
||||
`uvm_field_int (data, UVM_DEFAULT)
|
||||
`uvm_field_int (intg, UVM_DEFAULT)
|
||||
`uvm_field_int (gnt_delay, UVM_DEFAULT)
|
||||
`uvm_field_int (rvalid_delay, UVM_DEFAULT)
|
||||
`uvm_field_int (error, UVM_DEFAULT)
|
||||
`uvm_field_int (misaligned_first, UVM_DEFAULT)
|
||||
`uvm_field_int (misaligned_second, UVM_DEFAULT)
|
||||
`uvm_field_int (misaligned_first_saw_error, UVM_DEFAULT)
|
||||
`uvm_field_int (m_mode_access, UVM_DEFAULT)
|
||||
`uvm_field_int (spurious_response, UVM_DEFAULT)
|
||||
`uvm_object_utils_end
|
||||
|
||||
`uvm_object_new
|
||||
|
|
|
@ -24,12 +24,14 @@ class irq_monitor extends uvm_monitor;
|
|||
virtual task run_phase(uvm_phase phase);
|
||||
forever begin
|
||||
wait (vif.monitor_cb.reset === 1'b0);
|
||||
fork : monitor_irq
|
||||
collect_irq();
|
||||
wait (vif.monitor_cb.reset === 1'b1);
|
||||
join_any
|
||||
// Will only reach here on mid-test reset
|
||||
disable fork;
|
||||
fork begin : isolation_fork
|
||||
fork : monitor_irq
|
||||
collect_irq();
|
||||
wait (vif.monitor_cb.reset === 1'b1);
|
||||
join_any
|
||||
// Will only reach here on mid-test reset
|
||||
disable fork;
|
||||
end join
|
||||
end
|
||||
endtask : run_phase
|
||||
|
||||
|
|
|
@ -20,15 +20,17 @@ class irq_request_driver extends uvm_driver #(irq_seq_item);
|
|||
reset_signals();
|
||||
wait (vif.driver_cb.reset === 1'b0);
|
||||
forever begin
|
||||
fork : drive_irq
|
||||
// Setup a single get_REQ -> drive -> send_RSP long-running task.
|
||||
// This seq_item contains all signals on the interface at once.
|
||||
get_and_drive();
|
||||
wait (vif.driver_cb.reset === 1'b1);
|
||||
join_any
|
||||
// Will only reach here on mid-test reset
|
||||
disable fork;
|
||||
handle_reset();
|
||||
fork begin : isolation_fork
|
||||
fork : drive_irq
|
||||
// Setup a single get_REQ -> drive -> send_RSP long-running task.
|
||||
// This seq_item contains all signals on the interface at once.
|
||||
get_and_drive();
|
||||
wait (vif.driver_cb.reset === 1'b1);
|
||||
join_any
|
||||
// Will only reach here on mid-test reset
|
||||
disable fork;
|
||||
handle_reset();
|
||||
end join
|
||||
end
|
||||
endtask : run_phase
|
||||
|
||||
|
|
27
dv/uvm/core_ibex/common/prim/prim_and2.sv
Normal file
27
dv/uvm/core_ibex/common/prim/prim_and2.sv
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Abstract primitives wrapper.
|
||||
//
|
||||
// This file is a stop-gap until the DV file list is generated by FuseSoC.
|
||||
// Its contents are taken from the file which would be generated by FuseSoC.
|
||||
// https://github.com/lowRISC/ibex/issues/893
|
||||
|
||||
module prim_and2 #(
|
||||
parameter int Width = 1
|
||||
) (
|
||||
input [Width-1:0] in0_i,
|
||||
input [Width-1:0] in1_i,
|
||||
output logic [Width-1:0] out_o
|
||||
);
|
||||
|
||||
if (1) begin : gen_generic
|
||||
prim_generic_and2 #(
|
||||
.Width(Width)
|
||||
) u_impl_generic (
|
||||
.*
|
||||
);
|
||||
end
|
||||
|
||||
endmodule
|
18
dv/uvm/core_ibex/directed_tests/README.md
Normal file
18
dv/uvm/core_ibex/directed_tests/README.md
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Directed Tests
|
||||
|
||||
This directory contains the custom directed tests as well as scripts and headers for running directed tests vendored from various open source repositories.
|
||||
|
||||
Currently following open source test suites are vendored:
|
||||
- [riscv-tests](https://github.com/riscv-software-src/riscv-tests)
|
||||
- [riscv-arch-tests](https://github.com/riscv-non-isa/riscv-arch-test)
|
||||
- epmp-tests ([fork](https://github.com/lowRISC/riscv-isa-sim/tree/mseccfg_tests) from an opensource [repo](https://github.com/joxie/riscv-isa-sim))
|
||||
|
||||
## Generating test list
|
||||
|
||||
To generate a testlist containing all of the directed tests (custom + tests from vendored repos).
|
||||
|
||||
```
|
||||
python3 gen_testlist.py --add_tests riscv-tests,riscv-arch-tests,epmp-tests
|
||||
```
|
||||
|
||||
Please note that the custom directed tests needs to be added in the `gen_testlist.py` script and it needs to be run in order to update `directed_testlist.yaml`.
|
|
@ -0,0 +1,53 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
###############################################################################
|
||||
# Description:
|
||||
# Test to access misalign address between a PMP
|
||||
# and a non-PMP region. PMP region allows access
|
||||
# so the purpose is to check the implementation
|
||||
# behavior of multiple accesses.
|
||||
|
||||
#include "riscv_test.h"
|
||||
#include "test_macros.h"
|
||||
|
||||
RVTEST_RV64M
|
||||
RVTEST_CODE_BEGIN
|
||||
|
||||
# setting machine handler
|
||||
la t0, mtvec_handler
|
||||
csrw mtvec, t0
|
||||
|
||||
# setting the PMP region
|
||||
la t0, pmp_region_start
|
||||
srli t1, t0, PMP_SHIFT
|
||||
csrw pmpaddr0, t1
|
||||
la t1, pmp_region_end
|
||||
srli t1, t1, PMP_SHIFT
|
||||
csrw pmpaddr1, t1
|
||||
li t1, (PMP_L | PMP_TOR | PMP_R | PMP_W | PMP_X) << 8
|
||||
csrw pmpcfg0, t1
|
||||
|
||||
# access across the boundary between PMP and non-PMP
|
||||
lw t1, -2(t0)
|
||||
|
||||
j pass
|
||||
|
||||
TEST_PASSFAIL
|
||||
|
||||
.balign 256
|
||||
mtvec_handler:
|
||||
j fail
|
||||
|
||||
RVTEST_CODE_END
|
||||
|
||||
.data
|
||||
RVTEST_DATA_BEGIN
|
||||
|
||||
.balign 0x1000
|
||||
pmp_region_start: .zero 0x1000
|
||||
pmp_region_end:
|
||||
TEST_DATA
|
||||
|
||||
RVTEST_DATA_END
|
154
dv/uvm/core_ibex/directed_tests/custom_macros.h
Normal file
154
dv/uvm/core_ibex/directed_tests/custom_macros.h
Normal file
|
@ -0,0 +1,154 @@
|
|||
#Copyright lowRISC contributors.
|
||||
#Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
#SPDX - License - Identifier : Apache - 2.0
|
||||
|
||||
############################################################################## #
|
||||
|
||||
#Reset PMP CSRs
|
||||
#define RESET_PMP \
|
||||
csrw pmpcfg0, x0; \
|
||||
csrw pmpcfg1, x0; \
|
||||
csrw pmpcfg2, x0; \
|
||||
csrw pmpcfg3, x0; \
|
||||
csrw pmpaddr0, x0; \
|
||||
csrw pmpaddr1, x0; \
|
||||
csrw pmpaddr2, x0; \
|
||||
csrw pmpaddr3, x0; \
|
||||
csrw pmpaddr4, x0; \
|
||||
csrw pmpaddr5, x0; \
|
||||
csrw pmpaddr6, x0; \
|
||||
csrw pmpaddr7, x0; \
|
||||
csrw pmpaddr8, x0; \
|
||||
csrw pmpaddr9, x0; \
|
||||
csrw pmpaddr10, x0; \
|
||||
csrw pmpaddr11, x0; \
|
||||
csrw pmpaddr12, x0; \
|
||||
csrw pmpaddr13, x0; \
|
||||
csrw pmpaddr14, x0; \
|
||||
csrw pmpaddr15, x0; \
|
||||
csrw CSR_MSECCFG, x0;
|
||||
|
||||
#Calculate NAPOT addr by applying mask
|
||||
#define SET_NAPOT_ADDR(addr, gran) \
|
||||
li t0, gran >> 3; \
|
||||
not t2, t0; \
|
||||
la t1, addr; \
|
||||
srli t1, t1, 2; \
|
||||
and t1, t1, t2; \
|
||||
addi t0, t0, -1; \
|
||||
or t1, t1, t0;
|
||||
|
||||
#Set pmp configuration CSR depending upon region
|
||||
#define SET_PMP_CFG(pmp_cfg, pmp_region) \
|
||||
li t0, (8 * pmp_region) & 0x1f; \
|
||||
li t1, pmp_region; \
|
||||
li t2, 3; \
|
||||
bgt t1, t2, 1f; \
|
||||
li t1, pmp_cfg; \
|
||||
sll t1, t1, t0; \
|
||||
csrw pmpcfg0, t1; \
|
||||
j 4f; \
|
||||
1 : li t2, 7; \
|
||||
bgt t1, t2, 2f; \
|
||||
li t1, pmp_cfg; \
|
||||
sll t1, t1, t0; \
|
||||
csrw pmpcfg1, t1; \
|
||||
j 4f; \
|
||||
2 : li t2, 11; \
|
||||
bgt t1, t2, 3f; \
|
||||
li t1, pmp_cfg; \
|
||||
sll t1, t1, t0; \
|
||||
csrw pmpcfg2, t1; \
|
||||
j 4f; \
|
||||
3 : li t2, 15; \
|
||||
bgt t1, t2, 4f; \
|
||||
li t1, pmp_cfg; \
|
||||
sll t1, t1, t0; \
|
||||
csrw pmpcfg3, t1; \
|
||||
4:
|
||||
|
||||
#define SET_PMP_NAPOT(addr, gran, pmp_cfg, pmp_region) \
|
||||
SET_NAPOT_ADDR(addr, gran); \
|
||||
csrw pmpaddr##pmp_region, t1; \
|
||||
SET_PMP_CFG(pmp_cfg, pmp_region);
|
||||
|
||||
#define SET_PMP_TOR(addr_high, addr_low, pmp_cfg, pmp_region_high, \
|
||||
pmp_region_low) \
|
||||
la t0, addr_low; \
|
||||
srli t0, t0, 2; \
|
||||
csrw pmpaddr##pmp_region_low, t0; \
|
||||
la t0, addr_high; \
|
||||
srli t0, t0, 2; \
|
||||
csrw pmpaddr##pmp_region_high, t0; \
|
||||
SET_PMP_CFG(pmp_cfg, pmp_region_high);
|
||||
|
||||
#define SET_PMP_TOR_ADDR_FROM_REG(addr_high, addr_low, pmp_cfg, \
|
||||
pmp_region_high, pmp_region_low) \
|
||||
srli t0, addr_low, 2; \
|
||||
csrw pmpaddr##pmp_region_low, t0; \
|
||||
srli t0, addr_high, 2; \
|
||||
csrw pmpaddr##pmp_region_high, t0; \
|
||||
SET_PMP_CFG(pmp_cfg, pmp_region_high);
|
||||
|
||||
#define SKIP_PC \
|
||||
csrr t0, mepc; \
|
||||
lb t1, 0(t0); \
|
||||
li t2, 0x3; \
|
||||
and t1, t1, t2; \
|
||||
bne t1, t2, 1f; \
|
||||
addi t0, t0, 2; \
|
||||
1 : addi t0, t0, 2; \
|
||||
csrw mepc, t0;
|
||||
|
||||
#define RW_ACCESSES_IN_M_MODE(pmp_addr, gran) \
|
||||
la s0, pmp_addr; \
|
||||
lw s1, 0(s0); \
|
||||
sw s1, 0(s0); \
|
||||
li s1, gran / 2; \
|
||||
add s2, s0, s1; \
|
||||
lw s1, 0(s2); \
|
||||
sw s1, 0(s2); \
|
||||
li s1, gran - 4; \
|
||||
add s2, s0, s1; \
|
||||
lw s1, 0(s2); \
|
||||
sw s1, 0(s2);
|
||||
|
||||
// This assumes a PMP access failure on the read and write where the handler
|
||||
// jumps back to the failing access in M mode. If the access succeeds it remains
|
||||
// in U mode and the second SWITCH_TO_U_MODE will trap (due to attempting a
|
||||
// write to mstatus).
|
||||
#define RW_ACCESSES_IN_U_MODE(pmp_addr, gran) \
|
||||
la s0, pmp_addr; \
|
||||
SWITCH_TO_U_MODE_LABEL(1f); \
|
||||
1 : lw s1, 0(s0); \
|
||||
SWITCH_TO_U_MODE_LABEL(1f); \
|
||||
1 : sw s1, 0(s0);
|
||||
|
||||
#Accesses at start, mid and end of PMP range
|
||||
|
||||
#ifdef U_MODE
|
||||
#define RW_ACCESSES(pmp_addr, gran) RW_ACCESSES_IN_U_MODE(pmp_addr, gran)
|
||||
#else
|
||||
#define RW_ACCESSES(pmp_addr, gran) RW_ACCESSES_IN_M_MODE(pmp_addr, gran)
|
||||
#endif
|
||||
|
||||
#define SET_MSECCFG(val) \
|
||||
li t1, val; \
|
||||
csrs CSR_MSECCFG, t1;
|
||||
|
||||
#define SWITCH_TO_U_MODE_LABEL(label) \
|
||||
li t0, MSTATUS_MPP; \
|
||||
csrc mstatus, t0; \
|
||||
la t0, label; \
|
||||
csrw mepc, t0; \
|
||||
la a0, 1f; \
|
||||
mret; \
|
||||
1:
|
||||
|
||||
#define SWITCH_TO_U_MODE_REG(reg) \
|
||||
li t0, MSTATUS_MPP; \
|
||||
csrc mstatus, t0; \
|
||||
csrw mepc, reg; \
|
||||
la a0, 1f; \
|
||||
mret; \
|
||||
1:
|
6784
dv/uvm/core_ibex/directed_tests/directed_testlist.yaml
Normal file
6784
dv/uvm/core_ibex/directed_tests/directed_testlist.yaml
Normal file
File diff suppressed because it is too large
Load diff
31
dv/uvm/core_ibex/directed_tests/empty/empty.S
Normal file
31
dv/uvm/core_ibex/directed_tests/empty/empty.S
Normal file
|
@ -0,0 +1,31 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
###############################################################################
|
||||
# Description:
|
||||
# Empty test to check the sanity of directed test flow. It can be used as a
|
||||
# sample test.
|
||||
|
||||
#include "riscv_test.h"
|
||||
#include "test_macros.h"
|
||||
|
||||
RVTEST_RV64M
|
||||
RVTEST_CODE_BEGIN
|
||||
|
||||
j pass
|
||||
|
||||
RVTEST_CODE_END
|
||||
|
||||
pass:
|
||||
RVTEST_PASS
|
||||
|
||||
fail:
|
||||
RVTEST_FAIL
|
||||
|
||||
.data
|
||||
RVTEST_DATA_BEGIN
|
||||
|
||||
TEST_DATA
|
||||
|
||||
RVTEST_DATA_END
|
488
dv/uvm/core_ibex/directed_tests/gen_testlist.py
Normal file
488
dv/uvm/core_ibex/directed_tests/gen_testlist.py
Normal file
|
@ -0,0 +1,488 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generating testlists for following opensource test suites
|
||||
- riscv-tests
|
||||
- riscv-arch-tests
|
||||
- ePMP directed tests
|
||||
"""
|
||||
|
||||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import os
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
def add_configs_and_handwritten_directed_tests():
|
||||
testlist_string = '''# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
##########################################################
|
||||
|
||||
# This file is generated by gen_testlist.py script and largely copies
|
||||
# the formatting of the testlist.yaml used by riscv-dv, but only specifies
|
||||
# directed tests.
|
||||
#
|
||||
# - All paths are relative to THIS FILE.
|
||||
# - Each 'test' can specify a config by name to re-use common configuration
|
||||
# - If a test redefines a key already in the config, the test option takes priority.
|
||||
|
||||
##########################################################
|
||||
|
||||
- config: riscv-tests
|
||||
ld_script: link.ld
|
||||
includes: .
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
rtl_test: core_ibex_base_test
|
||||
rtl_params:
|
||||
PMPEnable: 1
|
||||
timeout_s: 300
|
||||
|
||||
- config: riscv-arch-tests
|
||||
ld_script: link.ld
|
||||
includes: .
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-arch-tests/riscv-test-suite/env/
|
||||
-I../../../vendor/riscv-isa-sim/arch_test_target/spike/
|
||||
rtl_test: core_ibex_base_test
|
||||
rtl_params:
|
||||
PMPEnable: 1
|
||||
timeout_s: 300
|
||||
|
||||
- config: epmp-tests
|
||||
ld_script: ../../../../vendor/riscv-isa-sim/tests/mseccfg/mseccfg_test.ld
|
||||
includes: .
|
||||
gcc_opts: -march=rv32imc -O2 -I . -I ./. -I ../softfloat -I ../riscv -fno-builtin-printf
|
||||
-fdata-sections -fno-section-anchors -DPRINTF_SUPPORTED=1
|
||||
../../../vendor/riscv-isa-sim/tests/mseccfg/crt.S
|
||||
../../../vendor/riscv-isa-sim/tests/mseccfg/syscalls.c
|
||||
-mcmodel=medany -static -nostdlib -nostartfiles -lm -lgcc
|
||||
-Wl,-M -Wl,-Map=link.log
|
||||
rtl_test: core_ibex_base_test
|
||||
rtl_params:
|
||||
PMPEnable: 1
|
||||
timeout_s: 300
|
||||
'''
|
||||
# Populate any handwritten directed tests below
|
||||
# and with suitable config
|
||||
available_directed_tests = '''
|
||||
# Custom directed tests
|
||||
|
||||
- test: empty
|
||||
desc: >
|
||||
Empty directed test
|
||||
iterations: 1
|
||||
test_srcs: empty/empty.S
|
||||
config: riscv-tests
|
||||
|
||||
- test: pmp_mseccfg_test_rlb1_l0_0_u0
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DPMP_REGION=4 -DRLB -DSET_PMP_L=0 -DSET_PMP_L_PREV=0
|
||||
|
||||
- test: pmp_mseccfg_test_rlb1_l0_1_u0
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DPMP_REGION=4 -DRLB -DSET_PMP_L=1 -DSET_PMP_L_PREV=0
|
||||
|
||||
- test: pmp_mseccfg_test_rlb1_l1_0_u0
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DPMP_REGION=4 -DRLB -DSET_PMP_L=0 -DSET_PMP_L_PREV=1
|
||||
|
||||
- test: pmp_mseccfg_test_rlb1_l1_1_u0
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DPMP_REGION=4 -DRLB -DSET_PMP_L=1 -DSET_PMP_L_PREV=1
|
||||
|
||||
- test: pmp_mseccfg_test_rlb0_l0_0_u0
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DPMP_REGION=4 -DSET_PMP_L=0 -DSET_PMP_L_PREV=0
|
||||
|
||||
- test: pmp_mseccfg_test_rlb0_l0_1_next_l1_u0
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DSET_PMP_L=1 -DSET_PMP_L_PREV=0 -DPMP_NEXT_L1
|
||||
|
||||
- test: pmp_mseccfg_test_rlb0_l0_1_next_l1_r1_u0
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DSET_PMP_L=1 -DSET_PMP_L_PREV=0 -DPMP_NEXT_L1_R1
|
||||
|
||||
- test: pmp_mseccfg_test_rlb0_l0_1_next_l1_w1_u0
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DSET_PMP_L=1 -DSET_PMP_L_PREV=0 -DPMP_NEXT_L1_W1
|
||||
|
||||
- test: pmp_mseccfg_test_rlb0_l0_1_next_l1_x1_u0
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DSET_PMP_L=1 -DSET_PMP_L_PREV=0 -DPMP_NEXT_L1_X1
|
||||
|
||||
- test: pmp_mseccfg_test_rlb0_l0_1_next_l1_r1_w1_u0
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DSET_PMP_L=1 -DSET_PMP_L_PREV=0 -DPMP_NEXT_L1_R1_W1
|
||||
|
||||
- test: pmp_mseccfg_test_rlb0_l0_1_next_l1_r1_x1_u0
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DSET_PMP_L=1 -DSET_PMP_L_PREV=0 -DPMP_NEXT_L1_R1_X1
|
||||
|
||||
- test: pmp_mseccfg_test_rlb0_l0_1_next_l1_w1_x1_u0
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DSET_PMP_L=1 -DSET_PMP_L_PREV=0 -DPMP_NEXT_L1_W1_X1
|
||||
|
||||
- test: pmp_mseccfg_test_rlb0_l0_1_next_l1_r1_w1_x1_u0
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DSET_PMP_L=1 -DSET_PMP_L_PREV=0 -DPMP_NEXT_L1_R1_W1_X1
|
||||
|
||||
- test: pmp_mseccfg_test_rlb1_l0_0_u1
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DPMP_REGION=4 -DRLB -DSET_PMP_L=0 -DSET_PMP_L_PREV=0 -DU_MODE
|
||||
|
||||
- test: pmp_mseccfg_test_rlb1_l0_1_u1
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DPMP_REGION=4 -DRLB -DSET_PMP_L=1 -DSET_PMP_L_PREV=0 -DU_MODE
|
||||
|
||||
- test: pmp_mseccfg_test_rlb1_l1_0_u1
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DPMP_REGION=4 -DRLB -DSET_PMP_L=0 -DSET_PMP_L_PREV=1 -DU_MODE
|
||||
|
||||
- test: pmp_mseccfg_test_rlb1_l1_1_u1
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DPMP_REGION=4 -DRLB -DSET_PMP_L=1 -DSET_PMP_L_PREV=1 -DU_MODE
|
||||
|
||||
- test: pmp_mseccfg_test_rlb0_l0_0_u1
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DPMP_REGION=4 -DSET_PMP_L=0 -DSET_PMP_L_PREV=0 -DU_MODE
|
||||
|
||||
- test: pmp_mseccfg_test_rlb0_l0_1_next_l1_u1
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DSET_PMP_L=1 -DSET_PMP_L_PREV=0 -DPMP_NEXT_L1 -DU_MODE
|
||||
|
||||
- test: pmp_mseccfg_test_rlb0_l0_1_next_l1_r1_u1
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DSET_PMP_L=1 -DSET_PMP_L_PREV=0 -DPMP_NEXT_L1_R1 -DU_MODE
|
||||
|
||||
- test: pmp_mseccfg_test_rlb0_l0_1_next_l1_w1_u1
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DSET_PMP_L=1 -DSET_PMP_L_PREV=0 -DPMP_NEXT_L1_W1 -DU_MODE
|
||||
|
||||
- test: pmp_mseccfg_test_rlb0_l0_1_next_l1_x1_u1
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DSET_PMP_L=1 -DSET_PMP_L_PREV=0 -DPMP_NEXT_L1_X1 -DU_MODE
|
||||
|
||||
- test: pmp_mseccfg_test_rlb0_l0_1_next_l1_r1_w1_u1
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DSET_PMP_L=1 -DSET_PMP_L_PREV=0 -DPMP_NEXT_L1_R1_W1 -DU_MODE
|
||||
|
||||
- test: pmp_mseccfg_test_rlb0_l0_1_next_l1_r1_x1_u1
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DSET_PMP_L=1 -DSET_PMP_L_PREV=0 -DPMP_NEXT_L1_R1_X1 -DU_MODE
|
||||
|
||||
- test: pmp_mseccfg_test_rlb0_l0_1_next_l1_w1_x1_u1
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DSET_PMP_L=1 -DSET_PMP_L_PREV=0 -DPMP_NEXT_L1_W1_X1 -DU_MODE
|
||||
|
||||
- test: pmp_mseccfg_test_rlb0_l0_1_next_l1_r1_w1_x1_u1
|
||||
desc: >
|
||||
mseccfg test
|
||||
iterations: 1
|
||||
test_srcs: pmp_mseccfg_test/pmp_mseccfg_test.S
|
||||
config: riscv-tests
|
||||
gcc_opts: -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
|
||||
-I../../../vendor/riscv-test-env/
|
||||
-I../../../vendor/riscv-test-env/p/
|
||||
-I../../../vendor/riscv-tests/isa/macros/scalar/
|
||||
-DSET_PMP_L=1 -DSET_PMP_L_PREV=0 -DPMP_NEXT_L1_R1_W1_X1 -DU_MODE
|
||||
|
||||
- test: access_pmp_overlap
|
||||
desc: >
|
||||
PMP access basic test
|
||||
iterations: 1
|
||||
test_srcs: access_pmp_overlap/access_pmp_overlap.S
|
||||
config: riscv-tests
|
||||
|
||||
- test: u_mode_exec_test
|
||||
desc: >
|
||||
PMP U mode exec test
|
||||
iterations: 1
|
||||
test_srcs: u_mode_exec_test/u_mode_exec_test.S
|
||||
config: riscv-tests
|
||||
'''
|
||||
testlist_string += available_directed_tests
|
||||
with open('directed_testlist.yaml', "a") as f:
|
||||
f.write(testlist_string)
|
||||
|
||||
def append_directed_testlist(tests, test_suite, test_suite_name, is_assembly):
|
||||
testlist_string = '''
|
||||
# Test-suite: {test_suite_name}
|
||||
'''.format(test_suite_name = test_suite_name)
|
||||
extension = '.S' if is_assembly else '.c'
|
||||
extension_grep = ' | egrep .S' if is_assembly else ' | egrep .c'
|
||||
|
||||
for test_group_name in tests:
|
||||
available_tests = os.popen('ls '+test_suite+test_group_name+extension_grep).read()
|
||||
available_testlist = []
|
||||
for test in available_tests.split('\n')[:-1]:
|
||||
available_testlist.append(test)
|
||||
for test_name_str in available_testlist:
|
||||
test_name = test_name_str.split(extension)[0]
|
||||
testlist_string = testlist_string + '''
|
||||
- test: {test_name}
|
||||
desc: >
|
||||
riscv test - {test_name}
|
||||
iterations: 1
|
||||
test_srcs: {test_suite}{test_group_name}/{test_name}{extension}
|
||||
config: {config}
|
||||
'''.format(test_name = test_name, test_group_name = test_group_name, test_suite = test_suite,
|
||||
config = test_suite_name, extension = extension)
|
||||
|
||||
with open('directed_testlist.yaml', "a") as f:
|
||||
f.write(testlist_string)
|
||||
|
||||
def list_tests(dir):
|
||||
testlist_str = os.popen('ls '+dir).read()
|
||||
testlist = []
|
||||
for test in testlist_str.split('\n')[:-1]:
|
||||
testlist.append(test)
|
||||
print(testlist)
|
||||
return testlist
|
||||
|
||||
def _main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--add_tests',
|
||||
type=str, required=True,
|
||||
help='''List test-suite name(s) from following:
|
||||
1) riscv-tests
|
||||
2) riscv-arch-tests
|
||||
3) epmp-tests
|
||||
|
||||
e.g. --add_tests=riscv-tests,epmp_tests
|
||||
''')
|
||||
|
||||
args = parser.parse_args()
|
||||
test_suite = args.add_tests
|
||||
test_suite_list = test_suite.split(',')
|
||||
|
||||
# remove any previous yaml file
|
||||
with open('directed_testlist.yaml','r+') as file:
|
||||
file.truncate(0)
|
||||
|
||||
# add headers and configs, also adding any handwritten directed tests
|
||||
add_configs_and_handwritten_directed_tests()
|
||||
|
||||
if 'riscv-tests' in test_suite_list or test_suite == 'all':
|
||||
isa_tests = {'rv32mi', 'rv32uc', 'rv32ui', 'rv32um'}
|
||||
append_directed_testlist(isa_tests, '../../../../vendor/riscv-tests/isa/', 'riscv-tests', 1)
|
||||
|
||||
if 'riscv-arch-tests' in test_suite_list or test_suite == 'all':
|
||||
arch_tests = {'rv32i_m/B/src', 'rv32i_m/C/src', 'rv32i_m/I/src', 'rv32i_m/M/src', 'rv32i_m/Zifencei/src'}
|
||||
append_directed_testlist(arch_tests, '../../../../vendor/riscv-arch-tests/riscv-test-suite/', 'riscv-arch-tests', 1)
|
||||
|
||||
if 'epmp-tests' in test_suite_list or test_suite == 'all':
|
||||
append_directed_testlist({'outputs'}, '../../../../vendor/riscv-isa-sim/tests/mseccfg/gengen_src/', 'epmp-tests', 0)
|
||||
|
||||
# Always return 0 (success), even if the test failed. We've successfully
|
||||
# generated a comparison log either way and we don't want to stop Make from
|
||||
# gathering them all up for us.
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(_main())
|
37
dv/uvm/core_ibex/directed_tests/ibex_macros.h
Normal file
37
dv/uvm/core_ibex/directed_tests/ibex_macros.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Ibex specific macros
|
||||
#define SIGNATURE_ADDR 0x8ffffff8
|
||||
|
||||
// signatue type - should be stored at sign_addr[7:0]
|
||||
#define CORE_STATUS 0x0
|
||||
#define TEST_RESULT 0x1
|
||||
#define WRITE_GPR 0x2
|
||||
#define WRITE_CSR 0x3
|
||||
|
||||
// core status - should be stored at sign_addr[12:8]
|
||||
#define INITIALIZED 0x0
|
||||
#define IN_DEBUG_MODE 0x1
|
||||
#define IN_MACHINE_MODE 0x2
|
||||
#define IN_HYPERVISOR_MODE 0x3
|
||||
#define IN_SUPERVISOR_MODE 0x4
|
||||
#define IN_USER_MODE 0x5
|
||||
#define HANDLING_IRQ 0x6
|
||||
#define FINISHED_IRQ 0x7
|
||||
#define HANDLING_EXCEPTION 0x8
|
||||
#define INSTR_FAULT_EXCEPTION 0x9
|
||||
#define ILLEGAL_INSTR_EXCEPTION 0xa
|
||||
#define LOAD_FAULT_EXCEPTION 0xb
|
||||
#define STORE_FAULT_EXCEPTION 0xc
|
||||
#define EBREAK_EXCEPTION 0xd
|
||||
|
||||
// test result - should be stored at sign_addr[8]
|
||||
#define TEST_PASS 0x0
|
||||
#define TEST_FAIL 0x1
|
||||
|
||||
#define CSR_MSECCFG 0x747
|
||||
#define MSECCFG_MML 0x1
|
||||
#define MSECCFG_MMWP 0x2
|
||||
#define MSECCFG_RLB 0x4
|
17
dv/uvm/core_ibex/directed_tests/link.ld
Normal file
17
dv/uvm/core_ibex/directed_tests/link.ld
Normal file
|
@ -0,0 +1,17 @@
|
|||
OUTPUT_ARCH( "riscv" )
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x80000000;
|
||||
.text.init : { *(.text.init) }
|
||||
. = ALIGN(0x1000);
|
||||
.tohost : { *(.tohost) }
|
||||
. = ALIGN(0x1000);
|
||||
.text : { *(.text) }
|
||||
. = ALIGN(0x1000);
|
||||
.data : { *(.data) }
|
||||
.bss : { *(.bss) }
|
||||
_end = .;
|
||||
}
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
###############################################################################
|
||||
# Description:
|
||||
# Setting mseccfg.mml 1 and mseccfg.rlb 0/1 with different PMP.LXWR
|
||||
# permissions and check R/w access to that specific region. Access is
|
||||
# at start, mid and end of the PMP region.
|
||||
# Target of test is to achieve functional coverage for rewriting
|
||||
# permissions in PMP regions with RLB 0/1.
|
||||
# Test accesses in both M-mode and U-mode.
|
||||
|
||||
#include "riscv_test.h"
|
||||
#include "test_macros.h"
|
||||
#include "custom_macros.h"
|
||||
|
||||
#define PMP_GRAN 0x1000
|
||||
|
||||
#if SET_PMP_L == 1
|
||||
#define SET_PMP_L_BIT PMP_L
|
||||
#else
|
||||
#define SET_PMP_L_BIT 0
|
||||
#endif
|
||||
|
||||
#if SET_PMP_L_PREV == 1
|
||||
#define SET_PMP_L_PREV PMP_L
|
||||
#else
|
||||
#define SET_PMP_L_PREV 0
|
||||
#endif
|
||||
|
||||
# Test specific macros
|
||||
#define SET_PMP_AND_ACCESS_TOR(pmp_permissions, region_high, region_low) \
|
||||
SET_PMP_TOR(pmp_region_end, pmp_region_start, pmp_permissions | PMP_TOR, region_high, region_low) \
|
||||
RW_ACCESSES(pmp_region_start, PMP_GRAN)
|
||||
|
||||
#define ACCESS_REGION(region, offset) \
|
||||
la t0, region; \
|
||||
jalr a0, offset(t0);
|
||||
|
||||
#define EXEC_ACCESS_IN_M_MODE(region) \
|
||||
ACCESS_REGION(region, 0); \
|
||||
ACCESS_REGION(region, -2);
|
||||
|
||||
#define EXEC_ACCESS_IN_U_MODE(region) \
|
||||
SWITCH_TO_U_MODE_LABEL(region); \
|
||||
la s0, region; \
|
||||
addi s0, s0, -2; \
|
||||
SWITCH_TO_U_MODE_REG(s0);
|
||||
|
||||
#ifdef U_MODE
|
||||
#define EXEC_ACCESS(region) \
|
||||
EXEC_ACCESS_IN_U_MODE(region)
|
||||
#else
|
||||
#define EXEC_ACCESS(region) \
|
||||
EXEC_ACCESS_IN_M_MODE(region)
|
||||
#endif
|
||||
|
||||
#define SET_PMP_AND_ACCESS_NAPOT(pmp_permissions, region) \
|
||||
SET_PMP_NAPOT(pmp_region_start, PMP_GRAN, pmp_permissions | PMP_NAPOT, region) \
|
||||
RW_ACCESSES(pmp_region_start, PMP_GRAN) \
|
||||
EXEC_ACCESS(pmp_region_start)
|
||||
|
||||
#define SET_DIFF_PMP_CFG(pmp_region, pmp_l, pmp_l_prev) \
|
||||
PREV_PMP_CFG(pmp_l_prev, pmp_region, pmp_l) \
|
||||
PREV_PMP_CFG(pmp_l_prev | PMP_R, pmp_region, pmp_l) \
|
||||
PREV_PMP_CFG(pmp_l_prev | PMP_W, pmp_region, pmp_l) \
|
||||
PREV_PMP_CFG(pmp_l_prev | PMP_X, pmp_region, pmp_l) \
|
||||
PREV_PMP_CFG(pmp_l_prev | PMP_R | PMP_W, pmp_region, pmp_l) \
|
||||
PREV_PMP_CFG(pmp_l_prev | PMP_R | PMP_X, pmp_region, pmp_l) \
|
||||
PREV_PMP_CFG(pmp_l_prev | PMP_W | PMP_X, pmp_region, pmp_l) \
|
||||
PREV_PMP_CFG(pmp_l_prev | PMP_R | PMP_W | PMP_X, pmp_region, pmp_l)
|
||||
|
||||
#define PREV_PMP_CFG(prev_pmp, pmp_region, pmp_l) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(prev_pmp, pmp_region) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(pmp_l, pmp_region) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(prev_pmp, pmp_region) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(pmp_l | PMP_R, pmp_region) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(prev_pmp, pmp_region) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(pmp_l | PMP_W, pmp_region) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(prev_pmp, pmp_region) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(pmp_l | PMP_X, pmp_region) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(prev_pmp, pmp_region) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(pmp_l | PMP_R | PMP_W, pmp_region) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(prev_pmp, pmp_region) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(pmp_l | PMP_R | PMP_X, pmp_region) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(prev_pmp, pmp_region) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(pmp_l | PMP_W | PMP_X, pmp_region) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(prev_pmp, pmp_region) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(pmp_l | PMP_R | PMP_W | PMP_X, pmp_region)
|
||||
|
||||
#ifdef PMP_NEXT_L1
|
||||
#define PREV_PMP_CFG_UNIQUE(prev_pmp, pmp_region, pmp_l) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(prev_pmp, pmp_region) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(pmp_l, pmp_region)
|
||||
#endif
|
||||
|
||||
#ifdef PMP_NEXT_L1_R1
|
||||
#define PREV_PMP_CFG_UNIQUE(prev_pmp, pmp_region, pmp_l) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(prev_pmp, pmp_region) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(pmp_l | PMP_R, pmp_region)
|
||||
#endif
|
||||
|
||||
#ifdef PMP_NEXT_L1_W1
|
||||
#define PREV_PMP_CFG_UNIQUE(prev_pmp, pmp_region, pmp_l) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(prev_pmp, pmp_region) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(pmp_l | PMP_W, pmp_region)
|
||||
#endif
|
||||
|
||||
#ifdef PMP_NEXT_L1_X1
|
||||
#define PREV_PMP_CFG_UNIQUE(prev_pmp, pmp_region, pmp_l) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(prev_pmp, pmp_region) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(pmp_l | PMP_X, pmp_region)
|
||||
#endif
|
||||
|
||||
#ifdef PMP_NEXT_L1_R1_W1
|
||||
#define PREV_PMP_CFG_UNIQUE(prev_pmp, pmp_region, pmp_l) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(prev_pmp, pmp_region) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(pmp_l | PMP_R | PMP_W, pmp_region)
|
||||
#endif
|
||||
|
||||
#ifdef PMP_NEXT_L1_R1_X1
|
||||
#define PREV_PMP_CFG_UNIQUE(prev_pmp, pmp_region, pmp_l) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(prev_pmp, pmp_region) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(pmp_l | PMP_R | PMP_X, pmp_region)
|
||||
#endif
|
||||
|
||||
#ifdef PMP_NEXT_L1_W1_X1
|
||||
#define PREV_PMP_CFG_UNIQUE(prev_pmp, pmp_region, pmp_l) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(prev_pmp, pmp_region) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(pmp_l | PMP_W | PMP_X, pmp_region)
|
||||
#endif
|
||||
|
||||
#ifdef PMP_NEXT_L1_R1_W1_X1
|
||||
#define PREV_PMP_CFG_UNIQUE(prev_pmp, pmp_region, pmp_l) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(prev_pmp, pmp_region) \
|
||||
SET_PMP_AND_ACCESS_NAPOT(pmp_l | PMP_R | PMP_W | PMP_X, pmp_region)
|
||||
#endif
|
||||
|
||||
RVTEST_RV64M
|
||||
RVTEST_CODE_BEGIN
|
||||
|
||||
# setting machine handler
|
||||
la t0, mtvec_handler
|
||||
csrw mtvec, t0
|
||||
|
||||
# resetting all PMP regions and mseccfg
|
||||
RESET_PMP
|
||||
|
||||
# placing 0xffffffff just before pmp_region_start so in
|
||||
# order to deal with boundary instsr as uncompressed
|
||||
li t0, 0xffffffff
|
||||
la t1, pmp_region_start
|
||||
sw t0, -4(t1)
|
||||
|
||||
#ifdef RLB
|
||||
SET_MSECCFG(MSECCFG_RLB)
|
||||
#endif
|
||||
|
||||
# setting M mode permissions as unmatched regions would be ignored when mml 1
|
||||
SET_PMP_TOR(pmp_region_start, _start, PMP_L | PMP_R | PMP_X | PMP_TOR, 0, 0)
|
||||
|
||||
# MML 1
|
||||
SET_MSECCFG(MSECCFG_MML)
|
||||
|
||||
# If RLB 1, PMP locked regions would be modifiable
|
||||
#ifdef RLB
|
||||
SET_DIFF_PMP_CFG(PMP_REGION, SET_PMP_L_BIT, SET_PMP_L_PREV)
|
||||
#else
|
||||
#if SET_PMP_L == 1
|
||||
# If RLB 0, then once locked, not modifiable hence have to use
|
||||
# different PMP regions to cover all transitions
|
||||
PREV_PMP_CFG_UNIQUE(SET_PMP_L_PREV, 4, SET_PMP_L_BIT)
|
||||
PREV_PMP_CFG_UNIQUE(SET_PMP_L_PREV | PMP_R, 5, SET_PMP_L_BIT)
|
||||
PREV_PMP_CFG_UNIQUE(SET_PMP_L_PREV | PMP_W, 6, SET_PMP_L_BIT)
|
||||
PREV_PMP_CFG_UNIQUE(SET_PMP_L_PREV | PMP_X, 7, SET_PMP_L_BIT)
|
||||
PREV_PMP_CFG_UNIQUE(SET_PMP_L_PREV | PMP_R | PMP_W, 8, SET_PMP_L_BIT)
|
||||
PREV_PMP_CFG_UNIQUE(SET_PMP_L_PREV | PMP_R | PMP_X, 9, SET_PMP_L_BIT)
|
||||
PREV_PMP_CFG_UNIQUE(SET_PMP_L_PREV | PMP_W | PMP_X, 10, SET_PMP_L_BIT)
|
||||
PREV_PMP_CFG_UNIQUE(SET_PMP_L_PREV | PMP_R | PMP_W | PMP_X, 11, SET_PMP_L_BIT)
|
||||
#else
|
||||
# If not locked, PMPCFG would be writable
|
||||
SET_DIFF_PMP_CFG(PMP_REGION, SET_PMP_L_BIT, SET_PMP_L_PREV)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
j pass
|
||||
|
||||
TEST_PASSFAIL
|
||||
|
||||
.balign 256
|
||||
mtvec_handler:
|
||||
csrr t0, mcause
|
||||
li t1, CAUSE_FETCH_ACCESS
|
||||
beq t0, t1, restore_to_pc_before_access_fault
|
||||
# jump to a valid PC if illegal instruction
|
||||
# which would be when PMP has execute permissions
|
||||
# in pmp_region_start
|
||||
li t1, CAUSE_ILLEGAL_INSTRUCTION
|
||||
beq t0, t1, restore_to_pc_before_access_fault
|
||||
SKIP_PC
|
||||
j ret_from_mhandler
|
||||
|
||||
restore_to_pc_before_access_fault:
|
||||
csrw mepc, a0
|
||||
|
||||
ret_from_mhandler:
|
||||
# always return to m-mode
|
||||
li t0, MSTATUS_MPP
|
||||
csrs mstatus, t0
|
||||
mret
|
||||
|
||||
RVTEST_CODE_END
|
||||
|
||||
.data
|
||||
RVTEST_DATA_BEGIN
|
||||
|
||||
.balign PMP_GRAN
|
||||
pmp_region_start:
|
||||
jr a0
|
||||
.zero (PMP_GRAN-4)
|
||||
pmp_region_end:
|
||||
TEST_DATA
|
||||
|
||||
RVTEST_DATA_END
|
|
@ -0,0 +1,71 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Tests U-mode execution across all of the locked non-MML PMP permission
|
||||
# configurations
|
||||
|
||||
#include "riscv_test.h"
|
||||
#include "test_macros.h"
|
||||
#include "custom_macros.h"
|
||||
|
||||
RVTEST_RV64M
|
||||
RVTEST_CODE_BEGIN
|
||||
RESET_PMP
|
||||
# Setup region that can be fully accessed by U mode
|
||||
SET_PMP_NAPOT(test_code, 4096, PMP_R | PMP_W | PMP_X, 15)
|
||||
|
||||
# Setup exception handler
|
||||
la t0, mtvec_handler
|
||||
csrw mtvec, t0
|
||||
|
||||
j test_code
|
||||
|
||||
test_end:
|
||||
j pass
|
||||
|
||||
TEST_PASSFAIL
|
||||
|
||||
.balign 4096
|
||||
test_code:
|
||||
# As we cannot modify locked regions setup new regions with increase priority
|
||||
# (lower numbered regions take priority). Run a U-mode execution after every
|
||||
# every new region to setup to test its configuration before moving to the
|
||||
# next.
|
||||
SET_PMP_NAPOT(test_exec_region, 256, PMP_NAPOT, 14)
|
||||
SWITCH_TO_U_MODE_LABEL(test_exec_region)
|
||||
SET_PMP_NAPOT(test_exec_region, 256, PMP_L | PMP_NAPOT, 13)
|
||||
SWITCH_TO_U_MODE_LABEL(test_exec_region)
|
||||
SET_PMP_NAPOT(test_exec_region, 256, PMP_L | PMP_R| PMP_NAPOT, 12)
|
||||
SWITCH_TO_U_MODE_LABEL(test_exec_region)
|
||||
SET_PMP_NAPOT(test_exec_region, 256, PMP_L | PMP_R | PMP_W| PMP_NAPOT, 11)
|
||||
SWITCH_TO_U_MODE_LABEL(test_exec_region)
|
||||
SET_PMP_NAPOT(test_exec_region, 256, PMP_L | PMP_X | PMP_R | PMP_W | PMP_NAPOT, 10)
|
||||
SWITCH_TO_U_MODE_LABEL(test_exec_region)
|
||||
SET_PMP_NAPOT(test_exec_region, 256, PMP_L | PMP_X | PMP_R | PMP_NAPOT, 9)
|
||||
SWITCH_TO_U_MODE_LABEL(test_exec_region)
|
||||
SET_PMP_NAPOT(test_exec_region, 256, PMP_L | PMP_X | PMP_NAPOT, 8)
|
||||
SWITCH_TO_U_MODE_LABEL(test_exec_region)
|
||||
|
||||
j test_end
|
||||
|
||||
.balign 256
|
||||
test_exec_region:
|
||||
add t0, t0, t0
|
||||
# Jump to exception handler to return to M-mode where U mode exec succeeds
|
||||
unimp
|
||||
|
||||
.balign 256
|
||||
mtvec_handler:
|
||||
csrw mepc, a0
|
||||
# always return to m-mode
|
||||
li t0, MSTATUS_MPP
|
||||
csrs mstatus, t0
|
||||
mret
|
||||
|
||||
RVTEST_CODE_END
|
||||
|
||||
.data
|
||||
RVTEST_DATA_BEGIN
|
||||
TEST_DATA
|
||||
RVTEST_DATA_END
|
92
dv/uvm/core_ibex/env/core_ibex_dut_probe_if.sv
vendored
92
dv/uvm/core_ibex/env/core_ibex_dut_probe_if.sv
vendored
|
@ -4,22 +4,54 @@
|
|||
|
||||
// Interface to probe DUT internal signal
|
||||
interface core_ibex_dut_probe_if(input logic clk);
|
||||
logic reset;
|
||||
logic illegal_instr;
|
||||
logic ecall;
|
||||
logic wfi;
|
||||
logic ebreak;
|
||||
logic dret;
|
||||
logic mret;
|
||||
ibex_pkg::fetch_enable_t fetch_enable;
|
||||
logic core_sleep;
|
||||
logic alert_minor;
|
||||
logic alert_major_internal;
|
||||
logic alert_major_bus;
|
||||
logic debug_req;
|
||||
ibex_pkg::priv_lvl_e priv_mode;
|
||||
ibex_pkg::ctrl_fsm_e ctrl_fsm_cs;
|
||||
logic debug_mode;
|
||||
|
||||
import uvm_pkg::*;
|
||||
|
||||
logic reset;
|
||||
logic illegal_instr;
|
||||
logic ecall;
|
||||
logic wfi;
|
||||
logic ebreak;
|
||||
logic dret;
|
||||
logic mret;
|
||||
ibex_pkg::ibex_mubi_t fetch_enable;
|
||||
logic core_sleep;
|
||||
logic alert_minor;
|
||||
logic alert_major_internal;
|
||||
logic alert_major_bus;
|
||||
logic debug_req;
|
||||
logic [ibex_pkg::IC_NUM_WAYS-1:0] ic_tag_req;
|
||||
logic ic_tag_write;
|
||||
logic [ibex_pkg::IC_INDEX_W-1:0] ic_tag_addr;
|
||||
logic [ibex_pkg::IC_NUM_WAYS-1:0] ic_data_req;
|
||||
logic ic_data_write;
|
||||
logic [ibex_pkg::IC_INDEX_W-1:0] ic_data_addr;
|
||||
ibex_pkg::priv_lvl_e priv_mode;
|
||||
ibex_pkg::ctrl_fsm_e ctrl_fsm_cs;
|
||||
logic debug_mode;
|
||||
logic double_fault_seen;
|
||||
logic rf_ren_a;
|
||||
logic rf_ren_b;
|
||||
logic rf_rd_a_wb_match;
|
||||
logic rf_rd_b_wb_match;
|
||||
logic rf_write_wb;
|
||||
logic sync_exc_seen;
|
||||
logic irq_exc_seen;
|
||||
logic csr_save_cause;
|
||||
ibex_pkg::exc_cause_t exc_cause;
|
||||
logic wb_exception;
|
||||
|
||||
always @(posedge clk or posedge reset) begin
|
||||
if (reset) begin
|
||||
irq_exc_seen <= 1'b0;
|
||||
end else begin
|
||||
if (ctrl_fsm_cs == ibex_pkg::IRQ_TAKEN) begin
|
||||
irq_exc_seen <= 1'b1;
|
||||
end else if (mret) begin
|
||||
irq_exc_seen <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
clocking dut_cb @(posedge clk);
|
||||
output fetch_enable;
|
||||
|
@ -35,13 +67,41 @@ interface core_ibex_dut_probe_if(input logic clk);
|
|||
input alert_minor;
|
||||
input alert_major_internal;
|
||||
input alert_major_bus;
|
||||
input ic_tag_req;
|
||||
input ic_tag_write;
|
||||
input ic_tag_addr;
|
||||
input ic_data_req;
|
||||
input ic_data_write;
|
||||
input ic_data_addr;
|
||||
input priv_mode;
|
||||
input ctrl_fsm_cs;
|
||||
input debug_mode;
|
||||
input double_fault_seen;
|
||||
input rf_ren_a;
|
||||
input rf_ren_b;
|
||||
input rf_rd_a_wb_match;
|
||||
input rf_rd_b_wb_match;
|
||||
input rf_write_wb;
|
||||
input sync_exc_seen;
|
||||
input irq_exc_seen;
|
||||
input wb_exception;
|
||||
endclocking
|
||||
|
||||
initial begin
|
||||
debug_req = 1'b0;
|
||||
end
|
||||
|
||||
`DV_CREATE_SIGNAL_PROBE_FUNCTION(signal_probe_rf_ren_a, rf_ren_a)
|
||||
`DV_CREATE_SIGNAL_PROBE_FUNCTION(signal_probe_rf_ren_b, rf_ren_b)
|
||||
`DV_CREATE_SIGNAL_PROBE_FUNCTION(signal_probe_rf_rd_a_wb_match, rf_rd_a_wb_match)
|
||||
`DV_CREATE_SIGNAL_PROBE_FUNCTION(signal_probe_rf_rd_b_wb_match, rf_rd_b_wb_match)
|
||||
`DV_CREATE_SIGNAL_PROBE_FUNCTION(signal_probe_rf_write_wb, rf_write_wb)
|
||||
`DV_CREATE_SIGNAL_PROBE_FUNCTION(signal_probe_alert_minor, alert_minor)
|
||||
`DV_CREATE_SIGNAL_PROBE_FUNCTION(signal_probe_ic_tag_req, ic_tag_req)
|
||||
`DV_CREATE_SIGNAL_PROBE_FUNCTION(signal_probe_ic_tag_write, ic_tag_write)
|
||||
`DV_CREATE_SIGNAL_PROBE_FUNCTION(signal_probe_ic_tag_addr, ic_tag_addr)
|
||||
`DV_CREATE_SIGNAL_PROBE_FUNCTION(signal_probe_ic_data_req, ic_data_req)
|
||||
`DV_CREATE_SIGNAL_PROBE_FUNCTION(signal_probe_ic_data_write, ic_data_write)
|
||||
`DV_CREATE_SIGNAL_PROBE_FUNCTION(signal_probe_ic_data_addr, ic_data_addr)
|
||||
|
||||
endinterface
|
||||
|
|
16
dv/uvm/core_ibex/env/core_ibex_env.sv
vendored
16
dv/uvm/core_ibex/env/core_ibex_env.sv
vendored
|
@ -14,6 +14,7 @@ class core_ibex_env extends uvm_env;
|
|||
core_ibex_vseqr vseqr;
|
||||
core_ibex_env_cfg cfg;
|
||||
scrambling_key_agent scrambling_key_agent_h;
|
||||
core_ibex_scoreboard scoreboard;
|
||||
|
||||
`uvm_component_utils(core_ibex_env)
|
||||
`uvm_component_new
|
||||
|
@ -23,6 +24,14 @@ class core_ibex_env extends uvm_env;
|
|||
if (!uvm_config_db#(core_ibex_env_cfg)::get(this, "", "cfg", cfg)) begin
|
||||
`uvm_fatal(get_full_name(), "Cannot get cfg")
|
||||
end
|
||||
if (!uvm_config_db#(virtual clk_rst_if)::get(this, "", "clk_if",
|
||||
cfg.ibex_clk_vif)) begin
|
||||
`uvm_fatal(`gfn, "failed to get ibex clk_if from uvm_config_db")
|
||||
end
|
||||
if (!uvm_config_db#(virtual core_ibex_dut_probe_if)::get(this, "", "dut_if",
|
||||
cfg.ibex_dut_vif)) begin
|
||||
`uvm_fatal(`gfn, "failed to get ibex dut_if from uvm_config_db")
|
||||
end
|
||||
data_if_response_agent = ibex_mem_intf_response_agent::type_id::
|
||||
create("data_if_response_agent", this);
|
||||
instr_if_response_agent = ibex_mem_intf_response_agent::type_id::
|
||||
|
@ -37,6 +46,8 @@ class core_ibex_env extends uvm_env;
|
|||
cfg.scrambling_key_cfg.if_mode = dv_utils_pkg::Device;
|
||||
// Create virtual sequencer
|
||||
vseqr = core_ibex_vseqr::type_id::create("vseqr", this);
|
||||
// Create scoreboard
|
||||
scoreboard = core_ibex_scoreboard::type_id::create("scoreboard", this);
|
||||
endfunction : build_phase
|
||||
|
||||
function void connect_phase(uvm_phase phase);
|
||||
|
@ -48,11 +59,16 @@ class core_ibex_env extends uvm_env;
|
|||
cosim_agent.dmem_port);
|
||||
instr_if_response_agent.monitor.item_collected_port.connect(
|
||||
cosim_agent.imem_port);
|
||||
if (cfg.enable_double_fault_detector) begin
|
||||
cosim_agent.rvfi_monitor.item_collected_port.connect(
|
||||
scoreboard.rvfi_port.analysis_export);
|
||||
end
|
||||
endfunction : connect_phase
|
||||
|
||||
function void reset();
|
||||
data_if_response_agent.reset();
|
||||
instr_if_response_agent.reset();
|
||||
cosim_agent.reset();
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
|
30
dv/uvm/core_ibex/env/core_ibex_env_cfg.sv
vendored
30
dv/uvm/core_ibex/env/core_ibex_env_cfg.sv
vendored
|
@ -4,6 +4,10 @@
|
|||
|
||||
class core_ibex_env_cfg extends uvm_object;
|
||||
|
||||
virtual clk_rst_if ibex_clk_vif;
|
||||
virtual core_ibex_dut_probe_if ibex_dut_vif;
|
||||
|
||||
bit enable_mem_intg_err;
|
||||
bit enable_irq_single_seq;
|
||||
bit enable_irq_multiple_seq;
|
||||
bit enable_irq_nmi_seq;
|
||||
|
@ -17,7 +21,29 @@ class core_ibex_env_cfg extends uvm_object;
|
|||
bit[31:0] signature_addr;
|
||||
rand scrambling_key_agent_cfg scrambling_key_cfg;
|
||||
|
||||
// Double-Fault detection in scoreboard
|
||||
bit enable_double_fault_detector = 1;
|
||||
int unsigned double_fault_threshold_consecutive = 100;
|
||||
int unsigned double_fault_threshold_total = 1000;
|
||||
// If '1', reaching either threshold fatally ends the test.
|
||||
// If '0', we end the test with a pass.
|
||||
bit is_double_fault_detected_fatal = 1;
|
||||
// If '1', reaching the timeout in seconds fatally ends the test.
|
||||
// If '0', we end the test with a pass.
|
||||
bit is_timeout_s_fatal = 1;
|
||||
// If '1' core_ibex_vseq will randomly choose to enable spurious responses in the data side memory
|
||||
// agent. This will also disable assertions that check the memory interface protocol as spurious
|
||||
// responses violate them.
|
||||
bit enable_spurious_dside_responses = 1;
|
||||
|
||||
// If spurious responses are enabled what percentage of tests will enable them
|
||||
int unsigned spurious_response_pct = 20;
|
||||
|
||||
|
||||
`uvm_object_utils_begin(core_ibex_env_cfg)
|
||||
`uvm_field_int(enable_double_fault_detector, UVM_DEFAULT)
|
||||
`uvm_field_int(is_double_fault_detected_fatal, UVM_DEFAULT)
|
||||
`uvm_field_int(enable_mem_intg_err, UVM_DEFAULT)
|
||||
`uvm_field_int(enable_irq_single_seq, UVM_DEFAULT)
|
||||
`uvm_field_int(enable_irq_multiple_seq, UVM_DEFAULT)
|
||||
`uvm_field_int(enable_irq_nmi_seq, UVM_DEFAULT)
|
||||
|
@ -33,6 +59,10 @@ class core_ibex_env_cfg extends uvm_object;
|
|||
|
||||
function new(string name = "");
|
||||
super.new(name);
|
||||
void'($value$plusargs("enable_double_fault_detector=%0d", enable_double_fault_detector));
|
||||
void'($value$plusargs("is_double_fault_detected_fatal=%0d", is_double_fault_detected_fatal));
|
||||
void'($value$plusargs("is_timeout_s_fatal=%0d", is_timeout_s_fatal));
|
||||
void'($value$plusargs("enable_mem_intg_err=%0d", enable_mem_intg_err));
|
||||
void'($value$plusargs("enable_irq_single_seq=%0d", enable_irq_single_seq));
|
||||
void'($value$plusargs("enable_irq_multiple_seq=%0d", enable_irq_multiple_seq));
|
||||
void'($value$plusargs("enable_irq_nmi_seq=%0d", enable_irq_nmi_seq));
|
||||
|
|
1
dv/uvm/core_ibex/env/core_ibex_env_pkg.sv
vendored
1
dv/uvm/core_ibex/env/core_ibex_env_pkg.sv
vendored
|
@ -23,6 +23,7 @@ package core_ibex_env_pkg;
|
|||
|
||||
`include "core_ibex_vseqr.sv"
|
||||
`include "core_ibex_env_cfg.sv"
|
||||
`include "core_ibex_scoreboard.sv"
|
||||
`include "core_ibex_env.sv"
|
||||
|
||||
endpackage
|
||||
|
|
12
dv/uvm/core_ibex/env/core_ibex_rvfi_if.sv
vendored
12
dv/uvm/core_ibex/env/core_ibex_rvfi_if.sv
vendored
|
@ -26,10 +26,14 @@ interface core_ibex_rvfi_if(input logic clk);
|
|||
logic [3:0] mem_wmask;
|
||||
logic [31:0] mem_rdata;
|
||||
logic [31:0] mem_wdata;
|
||||
logic [31:0] ext_mip;
|
||||
logic [31:0] ext_pre_mip;
|
||||
logic [31:0] ext_post_mip;
|
||||
logic ext_nmi;
|
||||
logic ext_nmi_int;
|
||||
logic [31:0] ext_debug_req;
|
||||
logic [31:0] ext_rf_wr_suppress;
|
||||
logic [63:0] ext_mcycle;
|
||||
logic ext_irq_valid;
|
||||
|
||||
logic [31:0] ext_mhpmcounters [10];
|
||||
logic [31:0] ext_mhpmcountersh [10];
|
||||
|
@ -59,13 +63,17 @@ interface core_ibex_rvfi_if(input logic clk);
|
|||
input mem_wmask;
|
||||
input mem_rdata;
|
||||
input mem_wdata;
|
||||
input ext_mip;
|
||||
input ext_pre_mip;
|
||||
input ext_post_mip;
|
||||
input ext_nmi;
|
||||
input ext_nmi_int;
|
||||
input ext_debug_req;
|
||||
input ext_rf_wr_suppress;
|
||||
input ext_mcycle;
|
||||
input ext_mhpmcounters;
|
||||
input ext_mhpmcountersh;
|
||||
input ext_ic_scr_key_valid;
|
||||
input ext_irq_valid;
|
||||
endclocking
|
||||
|
||||
task automatic wait_clks(input int num);
|
||||
|
|
113
dv/uvm/core_ibex/env/core_ibex_scoreboard.sv
vendored
Normal file
113
dv/uvm/core_ibex/env/core_ibex_scoreboard.sv
vendored
Normal file
|
@ -0,0 +1,113 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class core_ibex_scoreboard extends uvm_scoreboard;
|
||||
|
||||
uvm_tlm_analysis_fifo #(ibex_rvfi_seq_item) rvfi_port;
|
||||
core_ibex_env_cfg cfg;
|
||||
|
||||
// Events for Double-Fault detection
|
||||
uvm_event fault_threshold_consecutive_reached, fault_threshold_total_reached;
|
||||
|
||||
`uvm_component_utils(core_ibex_scoreboard)
|
||||
`uvm_component_new
|
||||
|
||||
function void build_phase(uvm_phase phase);
|
||||
super.build_phase(phase);
|
||||
if (!uvm_config_db#(core_ibex_env_cfg)::get(this, "", "cfg", cfg)) begin
|
||||
`uvm_fatal(get_full_name(), "Cannot get cfg")
|
||||
end
|
||||
rvfi_port = new("rvfi_port_scoreboard", this);
|
||||
endfunction : build_phase
|
||||
|
||||
task run_phase(uvm_phase phase);
|
||||
super.run_phase(phase);
|
||||
fork
|
||||
double_fault_detector();
|
||||
join_none
|
||||
endtask
|
||||
|
||||
task double_fault_detector();
|
||||
int unsigned double_fault_cnt_total = 0;
|
||||
int unsigned double_fault_cnt_consecutive = 0;
|
||||
bit double_fault_pulse_seen = 1'b0;
|
||||
|
||||
ibex_rvfi_seq_item rvfi_instr;
|
||||
|
||||
fault_threshold_consecutive_reached = new();
|
||||
fault_threshold_total_reached = new();
|
||||
|
||||
// There are two observable side-effects of a double fault in the Ibex:
|
||||
// - CPUCTRL.double_fault_seen is set to '1
|
||||
// - The top-level output double_fault_seen_o is asserted for one cycle
|
||||
|
||||
fork
|
||||
// Increment a counter whenever a double_fault was indicated for the last
|
||||
// rvfi_seq_item created. When the counter reaches a threshold, create an event.
|
||||
begin
|
||||
forever begin
|
||||
rvfi_port.get(rvfi_instr);
|
||||
if (double_fault_pulse_seen) begin
|
||||
// There must have been a double_fault during the previous retired insn.
|
||||
double_fault_pulse_seen = 1'b0;
|
||||
double_fault_cnt_total++;
|
||||
double_fault_cnt_consecutive++;
|
||||
end else begin
|
||||
// Reset the consecutive counter.
|
||||
double_fault_cnt_consecutive = 0;
|
||||
end
|
||||
|
||||
// Create an event if either counter reaches its threshold value, then reset the counter.
|
||||
if (double_fault_cnt_consecutive == cfg.double_fault_threshold_consecutive) begin
|
||||
fault_threshold_consecutive_reached.trigger();
|
||||
double_fault_cnt_consecutive = 0;
|
||||
end
|
||||
if (double_fault_cnt_total == cfg.double_fault_threshold_total) begin
|
||||
fault_threshold_total_reached.trigger();
|
||||
double_fault_cnt_total = 0;
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
// Latch the 'double_fault_seen_o' signal to catch the fault.
|
||||
// The single pulse may be receieved sometime before the rvfi_seq_item
|
||||
// corresponding to the faulting instruction is generated. Hence we
|
||||
// latch that pulse when it is seen, and then reset above when the
|
||||
// seq_item arrives.
|
||||
// https://github.com/lowRISC/ibex/pull/1848#discussion_r995903762
|
||||
begin
|
||||
forever begin
|
||||
@(posedge cfg.ibex_dut_vif.dut_cb.double_fault_seen);
|
||||
double_fault_pulse_seen = 1'b1;
|
||||
cfg.ibex_clk_vif.wait_clks(1);
|
||||
end
|
||||
end
|
||||
join_none
|
||||
|
||||
endtask // double_fault_detector
|
||||
|
||||
// Helper method which returns if either of the counter thresholds are reached.
|
||||
virtual task dfd_wait_for_pass_events();
|
||||
fork begin : isolation_fork
|
||||
fork
|
||||
begin
|
||||
fault_threshold_total_reached.wait_trigger();
|
||||
`uvm_info(`gfn,
|
||||
$sformatf({"double_fault detector : reached threshold [%0d] ",
|
||||
"for total double faults seen."}, cfg.double_fault_threshold_total),
|
||||
UVM_LOW)
|
||||
end
|
||||
begin
|
||||
fault_threshold_consecutive_reached.wait_trigger();
|
||||
`uvm_info(`gfn,
|
||||
$sformatf({"double_fault detector : reached threshold [%0d] ",
|
||||
"for consecutive double faults seen."}, cfg.double_fault_threshold_consecutive),
|
||||
UVM_LOW)
|
||||
end
|
||||
join_any
|
||||
disable fork;
|
||||
end join
|
||||
endtask
|
||||
|
||||
endclass
|
|
@ -99,7 +99,13 @@
|
|||
CSR_MHPMCOUNTER28H, \
|
||||
CSR_MHPMCOUNTER29H, \
|
||||
CSR_MHPMCOUNTER30H, \
|
||||
CSR_MHPMCOUNTER31H
|
||||
CSR_MHPMCOUNTER31H, \
|
||||
CSR_MENVCFGH, \
|
||||
CSR_MSECCFGH, \
|
||||
CSR_MCONTEXT, \
|
||||
CSR_MSCONTEXT, \
|
||||
CSR_SCONTEXT, \
|
||||
CSR_TDATA3
|
||||
|
||||
// Debug related CSRs
|
||||
`define DEBUG_CSRS \
|
||||
|
@ -111,10 +117,3 @@
|
|||
CSR_TDATA1, \
|
||||
CSR_TDATA2, \
|
||||
CSR_TDATA3
|
||||
|
||||
// Must exist when implementing hardware triggers (breakpoints), but read as 0 and ignore \
|
||||
// writes. Unused/unneeded by debugger infrastructure. \
|
||||
`define NOT_IMPLEMENTED_CSRS \
|
||||
CSR_MCONTEXT, \
|
||||
CSR_SCONTEXT, \
|
||||
CSR_TDATA3
|
||||
|
|
|
@ -15,7 +15,20 @@ interface core_ibex_fcov_if import ibex_pkg::*; (
|
|||
input debug_mode,
|
||||
|
||||
input fcov_csr_read_only,
|
||||
input fcov_csr_write
|
||||
input fcov_csr_write,
|
||||
|
||||
input fcov_rf_ecc_err_a_id,
|
||||
input fcov_rf_ecc_err_b_id,
|
||||
|
||||
input ibex_mubi_t fetch_enable_i,
|
||||
|
||||
input instr_req_o,
|
||||
input instr_gnt_i,
|
||||
input instr_rvalid_i,
|
||||
|
||||
input data_req_o,
|
||||
input data_gnt_i,
|
||||
input data_rvalid_i
|
||||
);
|
||||
`include "dv_fcov_macros.svh"
|
||||
import uvm_pkg::*;
|
||||
|
@ -381,6 +394,32 @@ interface core_ibex_fcov_if import ibex_pkg::*; (
|
|||
(id_stall_type == IdStallTypeNone) && (id_stall_type_last != IdStallTypeNone) &&
|
||||
id_stall_type_last_valid;
|
||||
|
||||
// V2S Related Probes for Top-Level
|
||||
logic rf_we_glitch_err;
|
||||
logic lockstep_glitch_err;
|
||||
|
||||
logic imem_single_cycle_response, dmem_single_cycle_response;
|
||||
|
||||
mem_monitor_if iside_mem_monitor(
|
||||
.clk_i,
|
||||
.rst_ni,
|
||||
.req_i(instr_req_o),
|
||||
.gnt_i(instr_gnt_i),
|
||||
.rvalid_i(instr_rvalid_i),
|
||||
.outstanding_requests_o(),
|
||||
.single_cycle_response_o(imem_single_cycle_response)
|
||||
);
|
||||
|
||||
mem_monitor_if dside_mem_monitor(
|
||||
.clk_i,
|
||||
.rst_ni,
|
||||
.req_i(data_req_o),
|
||||
.gnt_i(data_gnt_i),
|
||||
.rvalid_i(data_rvalid_i),
|
||||
.outstanding_requests_o(),
|
||||
.single_cycle_response_o(dmem_single_cycle_response)
|
||||
);
|
||||
|
||||
covergroup uarch_cg @(posedge clk_i);
|
||||
option.per_instance = 1;
|
||||
option.name = "uarch_cg";
|
||||
|
@ -423,6 +462,46 @@ interface core_ibex_fcov_if import ibex_pkg::*; (
|
|||
cp_id_stage_state : coverpoint id_stage_state;
|
||||
cp_wb_stage_state : coverpoint wb_stage_state;
|
||||
|
||||
// V2S Coverpoints
|
||||
cp_data_ind_timing: coverpoint cs_registers_i.data_ind_timing_o;
|
||||
cp_data_ind_timing_instr: coverpoint id_instr_category iff (cs_registers_i.data_ind_timing_o) {
|
||||
// Not certain if InstrCategoryOtherIllegal can occur. Put it in illegal_bins for now and
|
||||
// revisit if any issues are seen
|
||||
illegal_bins illegal = {InstrCategoryOther, InstrCategoryOtherIllegal};
|
||||
}
|
||||
|
||||
cp_dummy_instr_en: coverpoint cs_registers_i.dummy_instr_en_o;
|
||||
cp_dummy_instr_mask: coverpoint cs_registers_i.dummy_instr_mask_o;
|
||||
cp_dummy_instr_type: coverpoint if_stage_i.fcov_dummy_instr_type;
|
||||
cp_dummy_instr: coverpoint id_instr_category iff (cs_registers_i.dummy_instr_en_o) {
|
||||
// Not certain if InstrCategoryOtherIllegal can occur. Put it in illegal_bins for now and
|
||||
// revisit if any issues are seen
|
||||
illegal_bins illegal = {InstrCategoryOther, InstrCategoryOtherIllegal};
|
||||
}
|
||||
|
||||
// Each stage sees a dummy instruction.
|
||||
cp_dummy_instr_if_stage: coverpoint if_stage_i.fcov_insert_dummy_instr;
|
||||
cp_dummy_instr_id_stage: coverpoint if_stage_i.dummy_instr_id_o;
|
||||
cp_dummy_instr_wb_stage: coverpoint wb_stage_i.dummy_instr_wb_o;
|
||||
|
||||
`DV_FCOV_EXPR_SEEN(rf_a_ecc_err, fcov_rf_ecc_err_a_id)
|
||||
`DV_FCOV_EXPR_SEEN(rf_b_ecc_err, fcov_rf_ecc_err_b_id)
|
||||
|
||||
`DV_FCOV_EXPR_SEEN(icache_ecc_err, if_stage_i.icache_ecc_error_o)
|
||||
|
||||
`DV_FCOV_EXPR_SEEN(mem_load_ecc_err, load_store_unit_i.load_resp_intg_err_o)
|
||||
`DV_FCOV_EXPR_SEEN(mem_store_ecc_err, load_store_unit_i.store_resp_intg_err_o)
|
||||
|
||||
`DV_FCOV_EXPR_SEEN(lockstep_err, lockstep_glitch_err)
|
||||
`DV_FCOV_EXPR_SEEN(rf_we_glitch_err, rf_we_glitch_err)
|
||||
`DV_FCOV_EXPR_SEEN(pc_mismatch_err, if_stage_i.pc_mismatch_alert_o)
|
||||
|
||||
cp_fetch_enable: coverpoint fetch_enable_i {
|
||||
bins fetch_on = {IbexMuBiOn};
|
||||
bins fetch_off = {IbexMuBiOff};
|
||||
bins fetch_inval = default;
|
||||
}
|
||||
|
||||
// TODO: MRET/WFI in debug mode?
|
||||
// Specific cover points for these as `id_instr_category` will be InstrCategoryPrivIllegal when
|
||||
// executing these instructions in U-mode.
|
||||
|
@ -479,28 +558,20 @@ interface core_ibex_fcov_if import ibex_pkg::*; (
|
|||
ignore_bins ignore = {`IGNORED_CSRS};
|
||||
}
|
||||
|
||||
// All CSR operations perform a read so we don't need to specify the CSR operation.
|
||||
cp_ignored_csrs: coverpoint cs_registers_i.csr_addr_i iff (id_stage_i.csr_access_o) {
|
||||
bins unimplemented_csrs_read = {`NOT_IMPLEMENTED_CSRS};
|
||||
}
|
||||
|
||||
cp_ignored_csrs_w: coverpoint cs_registers_i.csr_addr_i iff (fcov_csr_write) {
|
||||
bins unimplemented_csrs_written = {`NOT_IMPLEMENTED_CSRS};
|
||||
}
|
||||
|
||||
`DV_FCOV_EXPR_SEEN(csr_invalid_read_only, fcov_csr_read_only && cs_registers_i.illegal_csr)
|
||||
`DV_FCOV_EXPR_SEEN(csr_invalid_write, fcov_csr_write && cs_registers_i.illegal_csr)
|
||||
|
||||
cp_debug_mode: coverpoint debug_mode;
|
||||
|
||||
`DV_FCOV_EXPR_SEEN(debug_wakeup, id_stage_i.controller_i.fcov_debug_wakeup)
|
||||
`DV_FCOV_EXPR_SEEN(all_debug_req, id_stage_i.controller_i.fcov_all_debug_req)
|
||||
`DV_FCOV_EXPR_SEEN(debug_entry_if, id_stage_i.controller_i.fcov_debug_entry_if)
|
||||
`DV_FCOV_EXPR_SEEN(debug_entry_id, id_stage_i.controller_i.fcov_debug_entry_id)
|
||||
`DV_FCOV_EXPR_SEEN(pipe_flush, id_stage_i.controller_i.fcov_pipe_flush)
|
||||
`DV_FCOV_EXPR_SEEN(single_step_taken, id_stage_i.controller_i.fcov_debug_single_step_taken)
|
||||
`DV_FCOV_EXPR_SEEN(single_step_exception, id_stage_i.controller_i.do_single_step_d &&
|
||||
id_stage_i.controller_i.fcov_pipe_flush)
|
||||
`DV_FCOV_EXPR_SEEN(insn_trigger_enter_debug, instr_id_matches_trigger_q)
|
||||
`DV_FCOV_EXPR_SEEN(insn_trigger_exception, instr_id_matches_trigger_q &&
|
||||
id_stage_i.controller_i.fcov_pipe_flush)
|
||||
|
||||
cp_nmi_taken: coverpoint ((fcov_irqs[5] || fcov_irqs[4])) iff
|
||||
(id_stage_i.controller_i.fcov_interrupt_taken);
|
||||
|
@ -529,8 +600,7 @@ interface core_ibex_fcov_if import ibex_pkg::*; (
|
|||
bins out_of_flush0 = (FLUSH => DECODE);
|
||||
bins out_of_flush1 = (FLUSH => DBG_TAKEN_ID);
|
||||
bins out_of_flush2 = (FLUSH => WAIT_SLEEP);
|
||||
bins out_of_flush3 = (FLUSH => IRQ_TAKEN);
|
||||
bins out_of_flush4 = (FLUSH => DBG_TAKEN_IF);
|
||||
bins out_of_flush3 = (FLUSH => DBG_TAKEN_IF);
|
||||
bins out_of_wait_sleep = (WAIT_SLEEP => SLEEP);
|
||||
bins out_of_sleep = (SLEEP => FIRST_FETCH);
|
||||
// TODO: VCS does not implement default sequence so illegal_bins will be empty
|
||||
|
@ -573,55 +643,65 @@ interface core_ibex_fcov_if import ibex_pkg::*; (
|
|||
cp_misaligned_second_data_bus_err: coverpoint load_store_unit_i.data_bus_err_i iff
|
||||
(load_store_unit_i.fcov_mis_rvalid_2);
|
||||
|
||||
cp_imem_response_latency: coverpoint imem_single_cycle_response iff (instr_rvalid_i) {
|
||||
bins single_cycle = {1'b1};
|
||||
bins multi_cycle = {1'b0};
|
||||
}
|
||||
|
||||
`DV_FCOV_EXPR_SEEN(imem_req_gnt_rvalid, instr_rvalid_i & instr_req_o & instr_gnt_i)
|
||||
|
||||
cp_dmem_response_latency: coverpoint dmem_single_cycle_response iff (data_rvalid_i) {
|
||||
bins single_cycle = {1'b1};
|
||||
bins multi_cycle = {1'b0};
|
||||
}
|
||||
|
||||
`DV_FCOV_EXPR_SEEN(dmem_req_gnt_rvalid, data_rvalid_i & data_req_o & data_gnt_i)
|
||||
|
||||
misaligned_data_bus_err_cross: cross cp_misaligned_first_data_bus_err,
|
||||
cp_misaligned_second_data_bus_err;
|
||||
cp_misaligned_second_data_bus_err {
|
||||
// Cannot see both bus errors together as they're signalled at different states of the load
|
||||
// store unit FSM
|
||||
illegal_bins illegal = binsof(cp_misaligned_first_data_bus_err) intersect {1'b1} &&
|
||||
binsof(cp_misaligned_second_data_bus_err) intersect {1'b1};
|
||||
}
|
||||
|
||||
misaligned_insn_bus_err_cross: cross id_stage_i.instr_fetch_err_i,
|
||||
id_stage_i.instr_fetch_err_plus2_i;
|
||||
|
||||
controller_instr_cross: cross cp_controller_fsm, cp_id_instr_category {
|
||||
// Only expecting DECODE => FLUSH when we have the instruction categories constrained below.
|
||||
bins decode_to_flush =
|
||||
binsof(cp_controller_fsm.out_of_decode0) &&
|
||||
binsof(cp_id_instr_category) intersect {InstrCategoryMRet, InstrCategoryDRet,
|
||||
InstrCategoryEBreakDbg, InstrCategoryEBreakExc,
|
||||
InstrCategoryECall, InstrCategoryFetchError,
|
||||
InstrCategoryCSRAccess,
|
||||
InstrCategoryCSRIllegal,
|
||||
InstrCategoryUncompressedIllegal,
|
||||
InstrCategoryCompressedIllegal,
|
||||
InstrCategoryPrivIllegal,
|
||||
InstrCategoryOtherIllegal};
|
||||
bins decode_to_dbg = binsof(cp_controller_fsm.out_of_decode1);
|
||||
bins decode_to_irq = binsof(cp_controller_fsm.out_of_decode2);
|
||||
// Only expecting FLUSH => DECODE when we have the instruction categories constrained below.
|
||||
bins flush_to_decode =
|
||||
binsof(cp_controller_fsm.out_of_flush0) &&
|
||||
!binsof(cp_id_instr_category) intersect {InstrCategoryEBreakDbg, InstrCategoryWFI};
|
||||
// Only expecting FLUSH => IRQ_TAKEN when we have InstrCategoryCSRAccess in ID/EX
|
||||
bins flush_to_irq_taken =
|
||||
binsof(cp_controller_fsm.out_of_flush3) &&
|
||||
binsof(cp_id_instr_category) intersect {InstrCategoryCSRAccess};
|
||||
// Only expecting FLUSH => DEBUG_IF when we have InstrCategoryEBreakDbg in ID/EX
|
||||
bins flush_to_dbg_if =
|
||||
binsof(cp_controller_fsm.out_of_flush4) &&
|
||||
!binsof(cp_id_instr_category) intersect {InstrCategoryEBreakDbg};
|
||||
}
|
||||
|
||||
// Include both mstatus.mie enabled/disabled because it should not affect wakeup condition
|
||||
irq_wfi_cross: cross cp_controller_fsm_sleep, cs_registers_i.mstatus_q.mie iff
|
||||
(id_stage_i.irq_pending_i | id_stage_i.irq_nm_i);
|
||||
|
||||
debug_wfi_cross: cross cp_controller_fsm_sleep, cp_debug_wakeup iff
|
||||
(id_stage_i.controller_i.fcov_debug_wakeup);
|
||||
debug_wfi_cross: cross cp_controller_fsm_sleep, cp_all_debug_req iff
|
||||
(id_stage_i.controller_i.fcov_all_debug_req);
|
||||
|
||||
priv_mode_instr_cross: cross cp_priv_mode_id, cp_id_instr_category;
|
||||
priv_mode_instr_cross: cross cp_priv_mode_id, cp_id_instr_category {
|
||||
// No un-privileged CSRs on Ibex so no InstrCategoryCSRAccess in U mode (any CSR instruction
|
||||
// becomes InstrCategoryCSRIllegal).
|
||||
illegal_bins umode_csr_access_illegal =
|
||||
binsof(cp_id_instr_category) intersect {InstrCategoryCSRAccess} &&
|
||||
binsof(cp_priv_mode_id) intersect {PRIV_LVL_U};
|
||||
}
|
||||
|
||||
priv_mode_irq_cross: cross cp_priv_mode_id, cp_interrupt_taken, cs_registers_i.mstatus_q.mie {
|
||||
// No interrupt would be taken in M-mode when its mstatus.MIE = 0 unless it's an NMI
|
||||
illegal_bins mmode_mstatus_mie =
|
||||
binsof(cs_registers_i.mstatus_q.mie) intersect {1'b0} &&
|
||||
binsof(cp_priv_mode_id) intersect {PRIV_LVL_M} with (cp_interrupt_taken >> 4 == 6'd0);
|
||||
}
|
||||
|
||||
priv_mode_exception_cross: cross cp_priv_mode_id, cp_ls_pmp_exception, cp_ls_error_exception {
|
||||
illegal_bins pmp_and_error_exeption_both =
|
||||
(binsof(cp_ls_pmp_exception) intersect {1'b1} &&
|
||||
binsof(cp_ls_error_exception) intersect {1'b1});
|
||||
}
|
||||
|
||||
stall_cross: cross cp_id_instr_category, cp_stall_type_id {
|
||||
illegal_bins illegal =
|
||||
// Only Div, Mul, Branch and Jump instructions can see an instruction stall
|
||||
(!binsof(cp_id_instr_category) intersect {InstrCategoryDiv, InstrCategoryMul,
|
||||
InstrCategoryBranch, InstrCategoryJump} &&
|
||||
InstrCategoryBranch, InstrCategoryJump,
|
||||
InstrCategoryFenceI} &&
|
||||
binsof(cp_stall_type_id) intersect {IdStallTypeInstr})
|
||||
||
|
||||
// Only ALU, Mul, Div, Branch, Jump, Load, Store and CSR Access can see a load hazard stall
|
||||
|
@ -643,9 +723,13 @@ interface core_ibex_fcov_if import ibex_pkg::*; (
|
|||
}
|
||||
|
||||
pipe_cross: cross cp_id_instr_category, cp_if_stage_state, cp_id_stage_state, wb_stage_state {
|
||||
// When ID stage is empty instruction category must be none
|
||||
illegal_bins illegal = !binsof(cp_id_instr_category) intersect {InstrCategoryNone} &&
|
||||
binsof(cp_id_stage_state) intersect {PipeStageEmpty};
|
||||
// When ID stage is empty the only legal instruction category is InstrCategoryNone. Conversly
|
||||
// when the instruction category is InstrCategoryNone the only legal ID stage state is
|
||||
// PipeStageEmpty.
|
||||
illegal_bins illegal = (!binsof(cp_id_instr_category) intersect {InstrCategoryNone} &&
|
||||
binsof(cp_id_stage_state) intersect {PipeStageEmpty}) ||
|
||||
(binsof(cp_id_instr_category) intersect {InstrCategoryNone} &&
|
||||
!binsof(cp_id_stage_state) intersect {PipeStageEmpty});
|
||||
}
|
||||
|
||||
interrupt_taken_instr_cross: cross cp_nmi_taken, instr_unstalled_last,
|
||||
|
@ -662,7 +746,8 @@ interface core_ibex_fcov_if import ibex_pkg::*; (
|
|||
illegal_bins illegal =
|
||||
// Only Div, Mul, Branch and Jump instructions can see an instruction stall
|
||||
(!binsof(cp_id_instr_category) intersect {InstrCategoryDiv, InstrCategoryMul,
|
||||
InstrCategoryBranch, InstrCategoryJump} &&
|
||||
InstrCategoryBranch, InstrCategoryJump,
|
||||
InstrCategoryFenceI} &&
|
||||
binsof(cp_stall_type_id) intersect {IdStallTypeInstr})
|
||||
||
|
||||
// Only ALU, Mul, Div, Branch, Jump, Load, Store and CSR Access can see a load hazard stall
|
||||
|
@ -673,9 +758,12 @@ interface core_ibex_fcov_if import ibex_pkg::*; (
|
|||
binsof(cp_stall_type_id) intersect {IdStallTypeLdHz});
|
||||
|
||||
// Cannot have a memory stall when we see an LS exception unless it is a load or store
|
||||
// instruction
|
||||
// instruction or a fetch error (the raw instruction decode can still indicate a load or store
|
||||
// which produces a stall, though won't cause any load or store to occur due to the fetch
|
||||
// error).
|
||||
illegal_bins mem_stall_illegal =
|
||||
(!binsof(cp_id_instr_category) intersect {InstrCategoryLoad, InstrCategoryStore} &&
|
||||
(!binsof(cp_id_instr_category) intersect {InstrCategoryLoad, InstrCategoryStore,
|
||||
InstrCategoryFetchError} &&
|
||||
binsof(cp_stall_type_id) intersect {IdStallTypeMem}) with
|
||||
(cp_ls_pmp_exception == 1'b1 || cp_ls_error_exception == 1'b1);
|
||||
|
||||
|
@ -696,6 +784,25 @@ interface core_ibex_fcov_if import ibex_pkg::*; (
|
|||
// Only care about specific debug CSRs
|
||||
ignore_bins ignore = !binsof(cp_csr_write) intersect {`DEBUG_CSRS};
|
||||
}
|
||||
|
||||
// V2S Crosses
|
||||
|
||||
dummy_instr_config_cross: cross cp_dummy_instr_type, cp_dummy_instr_mask
|
||||
iff (cs_registers_i.dummy_instr_en_o);
|
||||
|
||||
rf_ecc_err_cross: cross fcov_rf_ecc_err_a_id, fcov_rf_ecc_err_b_id
|
||||
iff (id_stage_i.instr_valid_i);
|
||||
|
||||
// Each stage sees a debug request while executing a dummy instruction.
|
||||
debug_req_dummy_instr_if_stage_cross: cross cp_debug_req, cp_dummy_instr_if_stage;
|
||||
debug_req_dummy_instr_id_stage_cross: cross cp_debug_req, cp_dummy_instr_id_stage;
|
||||
debug_req_dummy_instr_wb_stage_cross: cross cp_debug_req, cp_dummy_instr_wb_stage;
|
||||
|
||||
// Each stage sees an interrupt request while executing a dummy instruction.
|
||||
irq_pending_dummy_instr_if_stage_cross: cross cp_irq_pending, cp_dummy_instr_if_stage;
|
||||
irq_pending_dummy_instr_id_stage_cross: cross cp_irq_pending, cp_dummy_instr_id_stage;
|
||||
irq_pending_dummy_instr_wb_stage_cross: cross cp_irq_pending, cp_dummy_instr_wb_stage;
|
||||
|
||||
endgroup
|
||||
|
||||
bit en_uarch_cov;
|
||||
|
@ -706,3 +813,42 @@ interface core_ibex_fcov_if import ibex_pkg::*; (
|
|||
|
||||
`DV_FCOV_INSTANTIATE_CG(uarch_cg, en_uarch_cov)
|
||||
endinterface
|
||||
|
||||
interface mem_monitor_if (
|
||||
input clk_i,
|
||||
input rst_ni,
|
||||
|
||||
input req_i,
|
||||
input gnt_i,
|
||||
input rvalid_i,
|
||||
|
||||
output int outstanding_requests_o,
|
||||
output logic single_cycle_response_o
|
||||
);
|
||||
|
||||
int outstanding_requests;
|
||||
logic outstanding_requests_inc, outstanding_requests_dec;
|
||||
logic no_outstanding_requests_last_cycle;
|
||||
|
||||
assign outstanding_requests_inc = req_i & gnt_i;
|
||||
assign outstanding_requests_dec = rvalid_i;
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (~rst_ni) begin
|
||||
outstanding_requests <= 0;
|
||||
no_outstanding_requests_last_cycle <= 1'b0;
|
||||
end else begin
|
||||
if (outstanding_requests_inc && !outstanding_requests_dec) begin
|
||||
outstanding_requests <= outstanding_requests + 1;
|
||||
end else if (!outstanding_requests_inc && outstanding_requests_dec) begin
|
||||
outstanding_requests <= outstanding_requests - 1;
|
||||
end
|
||||
|
||||
no_outstanding_requests_last_cycle <= (outstanding_requests == 0) ||
|
||||
((outstanding_requests == 1) && outstanding_requests_dec);
|
||||
end
|
||||
end
|
||||
|
||||
assign outstanding_requests_o = outstanding_requests;
|
||||
assign single_cycle_response_o = no_outstanding_requests_last_cycle & rvalid_i;
|
||||
endinterface
|
||||
|
|
|
@ -121,30 +121,37 @@ interface core_ibex_pmp_fcov_if import ibex_pkg::*; #(
|
|||
assign pmp_iside2_nomatch = ~|pmp_iside2_match;
|
||||
assign pmp_dside_nomatch = ~|pmp_dside_match;
|
||||
|
||||
assign misaligned_pmp_err_last = load_store_unit_i.fcov_ls_first_req ?
|
||||
load_store_unit_i.data_pmp_err_i :
|
||||
misaligned_pmp_err_last;
|
||||
|
||||
// Store the Data Channel PMP match info from the first request to calculate boundary cross
|
||||
always @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) begin
|
||||
pmp_dside_match_last <= '0;
|
||||
pmp_dside_match_last <= '0;
|
||||
misaligned_pmp_err_last <= 1'b0;
|
||||
end else if (load_store_unit_i.fcov_ls_first_req) begin
|
||||
pmp_dside_match_last <= pmp_dside_match;
|
||||
pmp_dside_match_last <= pmp_dside_match;
|
||||
misaligned_pmp_err_last <= load_store_unit_i.data_pmp_err_i;
|
||||
end
|
||||
end
|
||||
|
||||
assign pmp_iside_boundary_cross = |(pmp_iside_match ^ pmp_iside2_match);
|
||||
assign pmp_dside_boundary_cross = |(pmp_dside_match ^ pmp_dside_match_last);
|
||||
assign pmp_dside_boundary_cross = |(pmp_dside_match ^ pmp_dside_match_last) &
|
||||
load_store_unit_i.fcov_ls_second_req;
|
||||
|
||||
for (genvar i_region = 0; i_region < PMPNumRegions; i_region += 1) begin : g_pmp_region_fcov
|
||||
pmp_priv_bits_e pmp_region_priv_bits;
|
||||
pmp_priv_bits_e pmp_region_priv_bits_wr;
|
||||
|
||||
assign pmp_region_priv_bits = pmp_priv_bits_e'({csr_pmp_mseccfg.mml,
|
||||
csr_pmp_cfg[i_region].lock,
|
||||
csr_pmp_cfg[i_region].exec,
|
||||
csr_pmp_cfg[i_region].write,
|
||||
csr_pmp_cfg[i_region].read});
|
||||
assign pmp_region_priv_bits_wr =
|
||||
pmp_priv_bits_e'({csr_pmp_mseccfg.mml,
|
||||
cs_registers_i.g_pmp_registers.pmp_cfg_wdata[i_region].lock,
|
||||
cs_registers_i.g_pmp_registers.pmp_cfg_wdata[i_region].exec,
|
||||
cs_registers_i.g_pmp_registers.pmp_cfg_wdata[i_region].write,
|
||||
cs_registers_i.g_pmp_registers.pmp_cfg_wdata[i_region].read});
|
||||
|
||||
// Do the permission check for Data channel with the privilege level from Instruction channels.
|
||||
// This way we can check the effect of mstatus.mprv changing privilege levels for LSU related
|
||||
// operations.
|
||||
|
@ -171,7 +178,7 @@ interface core_ibex_pmp_fcov_if import ibex_pkg::*; #(
|
|||
// This coverpoint converts pmp_add_napot_valid into 32 bins. The onehot call makes sure
|
||||
// that when the entry is not in NAPOT mode, then no bin is selected. The clog2 call
|
||||
// converts the value 0...010...0 to the index of the one bit that is set.
|
||||
cp_napot_addr_modes: coverpoint $clog2(pmp_addr_napot_valid[i_region])
|
||||
cp_napot_addr_modes : coverpoint $clog2(pmp_addr_napot_valid[i_region])
|
||||
iff ($onehot(pmp_addr_napot_valid[i_region])) {
|
||||
bins napot_addr[] = { [0:31] };
|
||||
}
|
||||
|
@ -261,9 +268,16 @@ interface core_ibex_pmp_fcov_if import ibex_pkg::*; #(
|
|||
(binsof(cp_req_type_iside) intersect {PMP_ACC_EXEC} &&
|
||||
binsof(cp_region_priv_bits) intersect {X, XW, XR, XWR, LX, LXW, LXR, LXWR} &&
|
||||
binsof(pmp_iside_req_err) intersect {1});
|
||||
illegal_bins illegal_deny_exec_machine_unlocked =
|
||||
// Ensuring in machine mode when MML is low, we are in X allowed configuration
|
||||
(binsof(cp_region_priv_bits) intersect {R, W, WR} &&
|
||||
binsof(cp_req_type_iside) intersect {PMP_ACC_EXEC} &&
|
||||
binsof(cp_priv_lvl_iside) intersect {PRIV_LVL_M} &&
|
||||
binsof(pmp_iside_req_err) intersect {1});
|
||||
illegal_bins illegal_machine_deny_exec =
|
||||
// Ensuring MML is high and we are in a X allowed configuration in Machine Mode
|
||||
(binsof(cp_region_priv_bits) intersect {MML_XM_XU, MML_XRM_XU, MML_XRM, MML_XM} &&
|
||||
(binsof(cp_region_priv_bits) intersect {NONE, MML_XM_XU, MML_XRM_XU, MML_XRM,
|
||||
MML_XM} &&
|
||||
binsof(cp_priv_lvl_iside) intersect {PRIV_LVL_M} &&
|
||||
binsof(cp_req_type_iside) intersect {PMP_ACC_EXEC} &&
|
||||
binsof(pmp_iside_req_err) intersect {1});
|
||||
|
@ -312,9 +326,15 @@ interface core_ibex_pmp_fcov_if import ibex_pkg::*; #(
|
|||
(binsof(cp_region_priv_bits) intersect {X, XW, XR, XWR, LX, LXW, LXR, LXWR} &&
|
||||
binsof(cp_req_type_iside2) intersect {PMP_ACC_EXEC} &&
|
||||
binsof(pmp_iside2_req_err) intersect {1});
|
||||
illegal_bins illegal_deny_exec_machine_unlocked =
|
||||
// Ensuring in machine mode when MML is low, we are in X allowed configuration
|
||||
(binsof(cp_region_priv_bits) intersect {R, W, WR} &&
|
||||
binsof(cp_req_type_iside2) intersect {PMP_ACC_EXEC} &&
|
||||
binsof(cp_priv_lvl_iside2) intersect {PRIV_LVL_M} &&
|
||||
binsof(pmp_iside2_req_err) intersect {1});
|
||||
illegal_bins illegal_machine_deny_exec =
|
||||
// Ensuring MML is high and we are in a X allowed configuration in Machine Mode
|
||||
(binsof(cp_region_priv_bits) intersect {MML_XM_XU, MML_XRM_XU, MML_XRM, MML_XM} &&
|
||||
(binsof(cp_region_priv_bits) intersect {NONE, MML_XM_XU, MML_XRM_XU, MML_XRM, MML_XM} &&
|
||||
binsof(cp_priv_lvl_iside2) intersect {PRIV_LVL_M} &&
|
||||
binsof(cp_req_type_iside2) intersect {PMP_ACC_EXEC} &&
|
||||
binsof(pmp_iside2_req_err) intersect {1});
|
||||
|
@ -364,7 +384,8 @@ interface core_ibex_pmp_fcov_if import ibex_pkg::*; #(
|
|||
illegal_bins illegal_machine_deny_read =
|
||||
// Ensuring MML is high and we are in a R allowed configuration in Machine Mode
|
||||
(binsof(cp_region_priv_bits) intersect {MML_WRM_RU, MML_WRM_WRU, MML_RM_RU, MML_RM,
|
||||
MML_WRM, MML_XRM, MML_XRM_XU} &&
|
||||
MML_WRM, MML_XRM, MML_XRM_XU, NONE,
|
||||
W, X, XW} &&
|
||||
binsof(cp_priv_lvl_dside) intersect {PRIV_LVL_M} &&
|
||||
binsof(cp_req_type_dside) intersect {PMP_ACC_READ} &&
|
||||
binsof(pmp_dside_req_err) intersect {1});
|
||||
|
@ -390,7 +411,7 @@ interface core_ibex_pmp_fcov_if import ibex_pkg::*; #(
|
|||
MML_XRM, MML_XRM_XU, MML_RM_RU} &&
|
||||
binsof(cp_priv_lvl_dside) intersect {PRIV_LVL_M} &&
|
||||
binsof(cp_req_type_dside) intersect {PMP_ACC_WRITE} &&
|
||||
binsof(pmp_dside_req_err) intersect {1});
|
||||
binsof(pmp_dside_req_err) intersect {0});
|
||||
illegal_bins illegal_user_allow_write =
|
||||
// Ensuring MML is high and we are not in a W allowed configuration in User Mode
|
||||
(binsof(cp_region_priv_bits) intersect {MML_NONE, MML_RU, MML_WRM_RU, MML_XU, MML_XRU,
|
||||
|
@ -398,7 +419,7 @@ interface core_ibex_pmp_fcov_if import ibex_pkg::*; #(
|
|||
MML_XRM, MML_XRM_XU, MML_RM_RU} &&
|
||||
binsof(cp_priv_lvl_dside) intersect {PRIV_LVL_U} &&
|
||||
binsof(cp_req_type_dside) intersect {PMP_ACC_WRITE} &&
|
||||
binsof(pmp_dside_req_err) intersect {1});
|
||||
binsof(pmp_dside_req_err) intersect {0});
|
||||
|
||||
// Will never see a write access denied when write is allowed
|
||||
illegal_bins illegal_deny_write =
|
||||
|
@ -408,7 +429,8 @@ interface core_ibex_pmp_fcov_if import ibex_pkg::*; #(
|
|||
binsof(pmp_dside_req_err) intersect {1});
|
||||
illegal_bins illegal_machine_deny_write =
|
||||
// Ensuring MML is high and we are in a W allowed configuration in Machine Mode
|
||||
(binsof(cp_region_priv_bits) intersect {MML_WRM_WRU, MML_WRM_RU, MML_WRM} &&
|
||||
(binsof(cp_region_priv_bits) intersect {MML_WRM_WRU, MML_WRM_RU, MML_WRM, NONE,
|
||||
R, X, XR, XW} &&
|
||||
binsof(cp_priv_lvl_dside) intersect {PRIV_LVL_M} &&
|
||||
binsof(cp_req_type_dside) intersect {PMP_ACC_WRITE} &&
|
||||
binsof(pmp_dside_req_err) intersect {1});
|
||||
|
@ -431,6 +453,34 @@ interface core_ibex_pmp_fcov_if import ibex_pkg::*; #(
|
|||
csr_pmp_cfg[i_region].lock &&
|
||||
cs_registers_i.g_pmp_registers.g_pmp_csrs[i_region].u_pmp_addr_csr.wr_en_i &&
|
||||
csr_pmp_mseccfg.rlb)
|
||||
|
||||
// Only sample this cross when we attempt to write to a PMP config that doesn't currently
|
||||
// allow execution with MML enabled
|
||||
pmp_wr_exec_region :
|
||||
cross pmp_region_priv_bits, pmp_region_priv_bits_wr, csr_pmp_mseccfg.rlb iff
|
||||
(fcov_csr_write &&
|
||||
csr_pmp_mseccfg.mml &&
|
||||
cs_registers_i.csr_we_int &&
|
||||
cs_registers_i.csr_addr == (CSR_OFF_PMP_CFG + (i_region[11:0] >> 2)) &&
|
||||
((!pmp_region_priv_bits[2] && pmp_region_priv_bits != MML_XM_XU) ||
|
||||
pmp_region_priv_bits inside {MML_WRM_WRU, MML_RM_RU})) {
|
||||
|
||||
// Only interested in MML configuration, so ignore anything where the top bit is not set
|
||||
ignore_bins non_mml_in =
|
||||
binsof(pmp_region_priv_bits) with (pmp_region_priv_bits >> 4 == 5'b0);
|
||||
|
||||
ignore_bins non_mml_out =
|
||||
binsof(pmp_region_priv_bits_wr) with (pmp_region_priv_bits_wr >> 4 == 5'b0);
|
||||
|
||||
// Only interested in starting configs that weren't executable so ignore executable
|
||||
// regions
|
||||
ignore_bins from_exec_bins = binsof(pmp_region_priv_bits) intersect {MML_XU, MML_XRU,
|
||||
MML_XWRU, MML_XM_XU, MML_XM, MML_XRM, MML_XRM_XU};
|
||||
|
||||
// Only interested in writes that give executable regions so ignore non executable regions
|
||||
ignore_bins to_non_exec_bins = binsof(pmp_region_priv_bits_wr) intersect {MML_NONE,
|
||||
MML_RU, MML_WRM_RU, MML_WRU, MML_WRM_WRU, MML_L, MML_RM, MML_WRM, MML_RM_RU};
|
||||
}
|
||||
endgroup
|
||||
|
||||
`DV_FCOV_INSTANTIATE_CG(pmp_region_cg, en_pmp_fcov)
|
||||
|
@ -512,7 +562,7 @@ interface core_ibex_pmp_fcov_if import ibex_pkg::*; #(
|
|||
iff (fcov_csr_write && cs_registers_i.csr_addr_i == CSR_MSECCFG) {
|
||||
// Trying to enable RLB when RLB is disabled and locked regions present will result
|
||||
// with an ignored write
|
||||
bins sticky = binsof(cp_rlb) intersect {1} && binsof(cp_wdata_rlb) intersect {0}
|
||||
bins sticky = binsof(cp_rlb) intersect {0} && binsof(cp_wdata_rlb) intersect {1}
|
||||
iff (cs_registers_i.g_pmp_registers.any_pmp_entry_locked);
|
||||
}
|
||||
|
||||
|
@ -617,28 +667,48 @@ interface core_ibex_pmp_fcov_if import ibex_pkg::*; #(
|
|||
|
||||
cp_mprv: coverpoint cs_registers_i.mstatus_q.mprv;
|
||||
|
||||
mprv_effect_cross: cross cp_mprv, cs_registers_i.mstatus_q.mpp,
|
||||
mprv_effect_cross: cross cs_registers_i.mstatus_q.mpp,
|
||||
cs_registers_i.priv_mode_id_o, pmp_current_priv_req_err,
|
||||
pmp_dside_req_err iff
|
||||
(id_stage_i.instr_rdata_i[6:0] inside
|
||||
{ibex_pkg::OPCODE_LOAD, ibex_pkg::OPCODE_STORE}){
|
||||
// If MPRV is set to 0, system priv lvl and lsu priv lvl has to be same.
|
||||
illegal_bins illegal_mprv =
|
||||
binsof(cp_mprv) intersect {1'b0} with (pmp_current_priv_req_err != pmp_dside_req_err);
|
||||
{ibex_pkg::OPCODE_LOAD, ibex_pkg::OPCODE_STORE} &&
|
||||
cs_registers_i.mstatus_q.mprv){
|
||||
ignore_bins SamePriv =
|
||||
(binsof(cs_registers_i.mstatus_q.mpp) intersect {PRIV_LVL_M} &&
|
||||
binsof(cs_registers_i.priv_mode_id_o) intersect {PRIV_LVL_M}) ||
|
||||
(binsof(cs_registers_i.mstatus_q.mpp) intersect {PRIV_LVL_U} &&
|
||||
binsof(cs_registers_i.priv_mode_id_o) intersect {PRIV_LVL_U});
|
||||
|
||||
ignore_bins SameErr =
|
||||
(binsof(pmp_current_priv_req_err) intersect {0} &&
|
||||
binsof(pmp_dside_req_err) intersect {0}) ||
|
||||
(binsof(pmp_current_priv_req_err) intersect {1} &&
|
||||
binsof(pmp_dside_req_err) intersect {1});
|
||||
|
||||
// Ibex does not support H or S mode.
|
||||
ignore_bins unsupported_priv_lvl =
|
||||
binsof(cs_registers_i.mstatus_q.mpp) intersect {PRIV_LVL_H, PRIV_LVL_S} ||
|
||||
binsof(cs_registers_i.priv_mode_id_o) intersect {PRIV_LVL_H, PRIV_LVL_S};
|
||||
|
||||
// Cannot have mprv set in U mode (as it is cleared when executing an `mret` which takes the
|
||||
// hart into U mode).
|
||||
illegal_bins no_mrpv_in_umode =
|
||||
binsof(cs_registers_i.priv_mode_id_o) intersect {PRIV_LVL_U};
|
||||
}
|
||||
|
||||
pmp_instr_edge_cross: cross if_stage_i.instr_is_compressed_id_o,
|
||||
pmp_instr_edge_cross: cross if_stage_i.instr_is_compressed_out,
|
||||
pmp_iside_req_err, pmp_iside2_req_err
|
||||
iff (pmp_iside_boundary_cross);
|
||||
iff (pmp_iside_boundary_cross) {
|
||||
// Compressed instruction cannot see an error over the boundary as it only ever does
|
||||
// a single 16-bit fetch.
|
||||
illegal_bins no_iside2_err_on_compressed =
|
||||
binsof(if_stage_i.instr_is_compressed_out) intersect {1'b1} &&
|
||||
binsof(pmp_iside2_req_err) intersect {1'b1};
|
||||
}
|
||||
|
||||
misaligned_lsu_access_cross: cross misaligned_pmp_err_last,
|
||||
load_store_unit_i.fcov_ls_mis_pmp_err_2,
|
||||
pmp_dside_boundary_cross;
|
||||
|
||||
load_store_unit_i.fcov_ls_mis_pmp_err_2
|
||||
iff (pmp_dside_boundary_cross);
|
||||
endgroup
|
||||
|
||||
`DV_FCOV_INSTANTIATE_CG(pmp_top_cg, en_pmp_fcov)
|
||||
|
|
|
@ -17,8 +17,12 @@
|
|||
// Until this list is generated by FuseSoC, we have to use manually generated
|
||||
// wrappers around the prim_* modules to instantiate the prim_generic_* ones,
|
||||
// see https://github.com/lowRISC/ibex/issues/893.
|
||||
+incdir+${LOWRISC_IP_DIR}/ip/prim/rtl
|
||||
${PRJ_DIR}/dv/uvm/core_ibex/common/prim/prim_pkg.sv
|
||||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_assert.sv
|
||||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_util_pkg.sv
|
||||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_count_pkg.sv
|
||||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_count.sv
|
||||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_secded_pkg.sv
|
||||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_secded_22_16_dec.sv
|
||||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_secded_22_16_enc.sv
|
||||
|
@ -30,6 +34,7 @@ ${LOWRISC_IP_DIR}/ip/prim/rtl/prim_secded_hamming_39_32_dec.sv
|
|||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_secded_hamming_39_32_enc.sv
|
||||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_secded_hamming_72_64_dec.sv
|
||||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_secded_hamming_72_64_enc.sv
|
||||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_mubi_pkg.sv
|
||||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_ram_1p_pkg.sv
|
||||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_ram_1p_adv.sv
|
||||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_ram_1p_scr.sv
|
||||
|
@ -43,10 +48,10 @@ ${LOWRISC_IP_DIR}/ip/prim_generic/rtl/prim_generic_clock_mux2.sv
|
|||
${PRJ_DIR}/dv/uvm/core_ibex/common/prim/prim_clock_mux2.sv
|
||||
${LOWRISC_IP_DIR}/ip/prim_generic/rtl/prim_generic_flop.sv
|
||||
${PRJ_DIR}/dv/uvm/core_ibex/common/prim/prim_flop.sv
|
||||
${LOWRISC_IP_DIR}/ip/prim_generic/rtl/prim_generic_and2.sv
|
||||
${PRJ_DIR}/dv/uvm/core_ibex/common/prim/prim_and2.sv
|
||||
|
||||
// Shared lowRISC code
|
||||
+incdir+${LOWRISC_IP_DIR}/ip/prim/rtl
|
||||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_assert.sv
|
||||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_cipher_pkg.sv
|
||||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_lfsr.sv
|
||||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_secded_inv_28_22_enc.sv
|
||||
|
@ -64,7 +69,8 @@ ${LOWRISC_IP_DIR}/ip/prim/rtl/prim_secded_39_32_dec.sv
|
|||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_secded_72_64_enc.sv
|
||||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_secded_72_64_dec.sv
|
||||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_onehot_check.sv
|
||||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_mubi_pkg.sv
|
||||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_onehot_enc.sv
|
||||
${LOWRISC_IP_DIR}/ip/prim/rtl/prim_onehot_mux.sv
|
||||
|
||||
// ibex CORE RTL files
|
||||
+incdir+${PRJ_DIR}/rtl
|
||||
|
@ -100,6 +106,8 @@ ${PRJ_DIR}/rtl/ibex_top.sv
|
|||
${PRJ_DIR}/rtl/ibex_top_tracing.sv
|
||||
|
||||
// Core DV files
|
||||
+incdir+${PRJ_DIR}/dv/uvm/core_ibex/common
|
||||
${PRJ_DIR}/dv/uvm/core_ibex/common/date.c
|
||||
${PRJ_DIR}/vendor/google_riscv-dv/src/riscv_signature_pkg.sv
|
||||
+incdir+${LOWRISC_IP_DIR}/dv/sv/dv_utils
|
||||
+incdir+${LOWRISC_IP_DIR}/dv/sv/dv_base_reg
|
||||
|
@ -117,6 +125,7 @@ ${PRJ_DIR}/vendor/google_riscv-dv/src/riscv_signature_pkg.sv
|
|||
+incdir+${PRJ_DIR}/dv/uvm/core_ibex/common/ibex_cosim_agent
|
||||
+incdir+${PRJ_DIR}/dv/cosim
|
||||
${PRJ_DIR}/dv/uvm/bus_params_pkg/bus_params_pkg.sv
|
||||
${LOWRISC_IP_DIR}/dv/sv/common_ifs/common_ifs_pkg.sv
|
||||
${LOWRISC_IP_DIR}/dv/sv/common_ifs/clk_rst_if.sv
|
||||
${LOWRISC_IP_DIR}/dv/sv/common_ifs/pins_if.sv
|
||||
${LOWRISC_IP_DIR}/dv/sv/str_utils/str_utils_pkg.sv
|
||||
|
@ -129,13 +138,14 @@ ${LOWRISC_IP_DIR}/dv/sv/mem_model/mem_model_pkg.sv
|
|||
${LOWRISC_IP_DIR}/dv/sv/push_pull_agent/push_pull_if.sv
|
||||
${LOWRISC_IP_DIR}/dv/sv/push_pull_agent/push_pull_agent_pkg.sv
|
||||
${PRJ_DIR}/dv/uvm/core_ibex/common/ibex_mem_intf_agent/ibex_mem_intf.sv
|
||||
${PRJ_DIR}/dv/uvm/core_ibex/common/ibex_mem_intf_agent/ibex_mem_intf_agent_pkg.sv
|
||||
${PRJ_DIR}/dv/uvm/core_ibex/common/ibex_mem_intf_agent/ibex_mem_intf_pkg.sv
|
||||
${PRJ_DIR}/dv/uvm/core_ibex/common/irq_agent/irq_if.sv
|
||||
${PRJ_DIR}/dv/uvm/core_ibex/common/irq_agent/irq_agent_pkg.sv
|
||||
${PRJ_DIR}/dv/uvm/core_ibex/env/core_ibex_rvfi_if.sv
|
||||
${PRJ_DIR}/dv/uvm/core_ibex/common/ibex_cosim_agent/core_ibex_ifetch_if.sv
|
||||
${PRJ_DIR}/dv/uvm/core_ibex/common/ibex_cosim_agent/core_ibex_ifetch_pmp_if.sv
|
||||
${PRJ_DIR}/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_agent_pkg.sv
|
||||
${PRJ_DIR}/dv/uvm/core_ibex/common/ibex_mem_intf_agent/ibex_mem_intf_agent_pkg.sv
|
||||
${PRJ_DIR}/dv/uvm/core_ibex/env/core_ibex_instr_monitor_if.sv
|
||||
${PRJ_DIR}/dv/uvm/core_ibex/env/core_ibex_dut_probe_if.sv
|
||||
${PRJ_DIR}/dv/uvm/core_ibex/env/core_ibex_csr_if.sv
|
||||
|
|
|
@ -12,6 +12,8 @@ class ibex_asm_program_gen extends riscv_asm_program_gen;
|
|||
`uvm_object_new
|
||||
|
||||
virtual function void gen_program();
|
||||
bit disable_pmp_exception_handler = 0;
|
||||
|
||||
default_include_csr_write = {
|
||||
MSCRATCH,
|
||||
MVENDORID,
|
||||
|
@ -52,9 +54,31 @@ class ibex_asm_program_gen extends riscv_asm_program_gen;
|
|||
|
||||
riscv_csr_instr::create_csr_filter(cfg);
|
||||
|
||||
if ($value$plusargs("disable_pmp_exception_handler=%d", disable_pmp_exception_handler) &&
|
||||
disable_pmp_exception_handler) begin
|
||||
cfg.pmp_cfg.enable_pmp_exception_handler = 0;
|
||||
end
|
||||
|
||||
super.gen_program();
|
||||
endfunction
|
||||
|
||||
// ECALL trap handler
|
||||
// For riscv-dv in Ibex, ECALL is no-longer used to end the test.
|
||||
// Hence, redefine a simple version here that just increments
|
||||
// MEPC+4 then calls 'mret'. (ECALL is always 4-bytes in RV32)
|
||||
virtual function void gen_ecall_handler(int hart);
|
||||
string instr[$];
|
||||
dump_perf_stats(instr);
|
||||
gen_register_dump(instr);
|
||||
instr = {instr,
|
||||
$sformatf("csrr x%0d, 0x%0x", cfg.gpr[0], MEPC),
|
||||
$sformatf("addi x%0d, x%0d, 4", cfg.gpr[0], cfg.gpr[0]),
|
||||
$sformatf("csrw 0x%0x, x%0d", MEPC, cfg.gpr[0])
|
||||
};
|
||||
instr.push_back("mret");
|
||||
gen_section(get_label("ecall_handler", hart), instr);
|
||||
endfunction
|
||||
|
||||
virtual function void gen_program_header();
|
||||
// Override the mstatus_mprv config because there is no current way to randomize writing to
|
||||
// mstatus.mprv in riscv-dv (it's constrained by set_mstatus_mprv argument to have either
|
||||
|
@ -87,31 +111,30 @@ class ibex_asm_program_gen extends riscv_asm_program_gen;
|
|||
instr.push_back("csrwi 0x7c0, 1");
|
||||
endfunction
|
||||
|
||||
// Generate "test_done" section.
|
||||
// Re-define gen_test_done() to override the base-class with an empty implementation.
|
||||
// Then, our own overrided gen_program() can append new test_done code.
|
||||
virtual function void gen_test_done();
|
||||
// empty
|
||||
endfunction
|
||||
|
||||
// The test is ended from the UVM testbench, which awaits the following write to
|
||||
// the special test-control signature address (signature_addr - 0x4).
|
||||
// The ECALL trap handler will handle the clean up procedure while awaiting the
|
||||
// simulation to be ended.
|
||||
virtual function void gen_test_done();
|
||||
// #FIXME# The existing ECALL trap handler will handle the clean up procedure
|
||||
// while awaiting the simulation to be ended.
|
||||
virtual function void gen_test_end(input test_result_t result,
|
||||
ref string instr[$]);
|
||||
bit [XLEN-1:0] test_control_addr;
|
||||
string str;
|
||||
string i = indent; // lint-hack
|
||||
test_control_addr = cfg.signature_addr - 4'h4;
|
||||
|
||||
str = {str,
|
||||
{format_string("test_done:", LABEL_STR_LEN), "\n"},
|
||||
{i, "li gp, 1", "\n"}
|
||||
};
|
||||
|
||||
if (cfg.bare_program_mode) begin
|
||||
str = {str,
|
||||
{i, "j write_tohost", "\n"}
|
||||
};
|
||||
str = {i, "j write_tohost", "\n"};
|
||||
end else begin
|
||||
// The testbench will await a write of TEST_PASS, and use that to end the test.
|
||||
str = {str,
|
||||
str = {
|
||||
{i, $sformatf( "li x%0d, 0x%0h", cfg.gpr[1], test_control_addr), "\n"},
|
||||
{i, $sformatf( "li x%0d, 0x%0h", cfg.gpr[0], TEST_PASS), "\n"},
|
||||
{i, $sformatf( "li x%0d, 0x%0h", cfg.gpr[0], result), "\n"},
|
||||
{i, $sformatf("slli x%0d, x%0d, 8", cfg.gpr[0], cfg.gpr[0]), "\n"},
|
||||
{i, $sformatf("addi x%0d, x%0d, 0x%0h", cfg.gpr[0], cfg.gpr[0], TEST_RESULT), "\n"},
|
||||
{i, $sformatf( "sw x%0d, 0(x%0d)", cfg.gpr[0], cfg.gpr[1]), "\n"},
|
||||
|
@ -120,7 +143,32 @@ class ibex_asm_program_gen extends riscv_asm_program_gen;
|
|||
};
|
||||
end
|
||||
|
||||
instr_stream.push_back(str);
|
||||
instr.push_back(str);
|
||||
endfunction
|
||||
|
||||
virtual function void gen_init_section(int hart);
|
||||
// This is a good location to put the test done and fail because PMP tests expect these
|
||||
// sequences to be before the main function.
|
||||
string instr[$];
|
||||
|
||||
super.gen_init_section(hart);
|
||||
|
||||
// RISCV-DV assumes main is immediately after init when riscv_instr_pkg::support_pmp isn't set.
|
||||
// This override of gen_init_section breaks that assumption so add a jump to main here so the
|
||||
// test starts correctly for configurations that don't support PMP.
|
||||
if (!riscv_instr_pkg::support_pmp) begin
|
||||
instr_stream.push_back({indent, "j main"});
|
||||
end
|
||||
|
||||
gen_test_end(.result(TEST_PASS), .instr(instr));
|
||||
instr_stream = {instr_stream,
|
||||
{format_string("test_done:", LABEL_STR_LEN)},
|
||||
instr};
|
||||
instr.delete();
|
||||
gen_test_end(.result(TEST_FAIL), .instr(instr));
|
||||
instr_stream = {instr_stream,
|
||||
{format_string("test_fail:", LABEL_STR_LEN)},
|
||||
instr};
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class ibex_hardware_triggers_debug_rom_gen extends riscv_debug_rom_gen;
|
||||
|
||||
`uvm_object_utils(ibex_hardware_triggers_debug_rom_gen)
|
||||
`uvm_object_new
|
||||
|
||||
int unsigned ibex_trigger_idx = 0; // See. [DbgHwBreakNum]
|
||||
|
||||
virtual function void gen_program();
|
||||
string instr[$];
|
||||
|
||||
// Don't save off GPRs (ie. this WILL modify program flow)
|
||||
// (We want to capture a register value (gpr[1]) from the directed_instr_streams in
|
||||
// main() that contains the address for our next trigger.)
|
||||
// This works in tandem with 'ibex_breakpoint_stream' which stores the address of
|
||||
// the instruction to trigger on in a fixed register, then executes an EBREAK to
|
||||
// enter debug mode via the dcsr.ebreakm=1 functionality. The debug rom code
|
||||
// then sets up the breakpoint trigger to this address, and returns, allowing main
|
||||
// to continue executing until we hit the trigger.
|
||||
|
||||
// riscv-debug-1.0.0-STABLE
|
||||
// 5.5 Trigger Registers
|
||||
// <..>
|
||||
// As as result, a debugger can write any supported trigger as follows..
|
||||
//
|
||||
// 1. Write 0 to TDATA1. (This will result in TDATA1 containing a non-zero value,
|
||||
// since the register is WARL).
|
||||
// 2. Write desired values to TDATA2 and TDATA3.
|
||||
// 3. Write desired value to TDATA1.
|
||||
// <..>
|
||||
|
||||
instr = {// Check DCSR.cause (DCSR[8:6]) to branch to the next block of code.
|
||||
$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DCSR),
|
||||
$sformatf("slli x%0d, x%0d, 0x17", cfg.scratch_reg, cfg.scratch_reg),
|
||||
$sformatf("srli x%0d, x%0d, 0x1d", cfg.scratch_reg, cfg.scratch_reg),
|
||||
$sformatf("li x%0d, 0x1", cfg.gpr[0]), // EBREAK = 1
|
||||
$sformatf("beq x%0d, x%0d, 1f", cfg.scratch_reg, cfg.gpr[0]),
|
||||
$sformatf("li x%0d, 0x2", cfg.gpr[0]), // TRIGGER = 2
|
||||
$sformatf("beq x%0d, x%0d, 2f", cfg.scratch_reg, cfg.gpr[0]),
|
||||
$sformatf("li x%0d, 0x3", cfg.gpr[0]), // HALTREQ = 3
|
||||
$sformatf("beq x%0d, x%0d, 3f", cfg.scratch_reg, cfg.gpr[0]),
|
||||
|
||||
// DCSR.cause == EBREAK
|
||||
"1: nop",
|
||||
// 'ibex_breakpoint_stream' inserts EBREAKs such that cfg.gpr[1]
|
||||
// now contain the address of the next trigger.
|
||||
// Enable the trigger and set to this address.
|
||||
$sformatf("csrrwi zero, 0x%0x, %0d", TSELECT, ibex_trigger_idx),
|
||||
$sformatf("csrrw zero, 0x%0x, x0", TDATA1),
|
||||
$sformatf("csrrw zero, 0x%0x, x%0d", TDATA2, cfg.gpr[1]),
|
||||
$sformatf("csrrwi zero, 0x%0x, 5", TDATA1),
|
||||
// Increment the PC + 4 (EBREAK does not do this for you.)
|
||||
$sformatf("csrr x%0d, 0x%0x", cfg.gpr[0], DPC),
|
||||
$sformatf("addi x%0d, x%0d, 4", cfg.gpr[0], cfg.gpr[0]),
|
||||
$sformatf("csrw 0x%0x, x%0d", DPC, cfg.gpr[0]),
|
||||
"j 4f",
|
||||
|
||||
// DCSR.cause == TRIGGER
|
||||
"2: nop",
|
||||
// Disable the trigger until the next breakpoint is known.
|
||||
$sformatf("csrrwi zero, 0x%0x, %0d", TSELECT, ibex_trigger_idx),
|
||||
$sformatf("csrrw zero, 0x%0x, x0", TDATA1),
|
||||
$sformatf("csrrw zero, 0x%0x, x0", TDATA2),
|
||||
"j 4f",
|
||||
|
||||
// DCSR.cause == HALTREQ
|
||||
"3: nop",
|
||||
// Use this once near the start of the test to configure ebreakm/u to enter debug mode
|
||||
// Set DCSR.ebreakm (DCSR[15]) = 1
|
||||
// Set DCSR.ebreaku (DCSR[12]) = 1
|
||||
$sformatf("li x%0d, 0x9000", cfg.scratch_reg),
|
||||
$sformatf("csrs 0x%0x, x%0d", DCSR, cfg.scratch_reg),
|
||||
|
||||
"4: nop"
|
||||
};
|
||||
|
||||
debug_main = {instr,
|
||||
$sformatf("la x%0d, debug_end", cfg.scratch_reg),
|
||||
$sformatf("jalr x0, x%0d, 0", cfg.scratch_reg)
|
||||
};
|
||||
format_section(debug_main);
|
||||
gen_section($sformatf("%0sdebug_rom", hart_prefix(hart)), debug_main);
|
||||
|
||||
debug_end = {dret};
|
||||
format_section(debug_end);
|
||||
gen_section($sformatf("%0sdebug_end", hart_prefix(hart)), debug_end);
|
||||
|
||||
gen_debug_exception_handler();
|
||||
endfunction
|
||||
|
||||
|
||||
// If we get an exception in debug_mode, fail the test immediately.
|
||||
// (something has gone wrong with our stimulus generation)
|
||||
virtual function void gen_debug_exception_handler();
|
||||
string instr[$];
|
||||
instr = {$sformatf("la x%0d, test_fail", cfg.scratch_reg),
|
||||
$sformatf("jalr x1, x%0d, 0", cfg.scratch_reg)};
|
||||
format_section(instr);
|
||||
gen_section($sformatf("%0sdebug_exception", hart_prefix(hart)), instr);
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
||||
class ibex_hardware_triggers_asm_program_gen extends ibex_asm_program_gen;
|
||||
|
||||
`uvm_object_utils(ibex_hardware_triggers_asm_program_gen)
|
||||
`uvm_object_new
|
||||
|
||||
// Same implementation as the parent class, except substitute for our custom debug_rom class.
|
||||
virtual function void gen_debug_rom(int hart);
|
||||
`uvm_info(`gfn, "Creating debug ROM", UVM_LOW)
|
||||
debug_rom = ibex_hardware_triggers_debug_rom_gen::
|
||||
type_id::create("debug_rom", , {"uvm_test_top", ".", `gfn});
|
||||
debug_rom.cfg = cfg;
|
||||
debug_rom.hart = hart;
|
||||
debug_rom.gen_program();
|
||||
instr_stream = {instr_stream, debug_rom.instr_stream};
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
||||
|
||||
class ibex_hardware_triggers_illegal_instr extends riscv_illegal_instr;
|
||||
|
||||
`uvm_object_utils(ibex_hardware_triggers_illegal_instr)
|
||||
`uvm_object_new
|
||||
|
||||
// Make it super-obvious where the illegal instructions are in the assembly.
|
||||
function void post_randomize();
|
||||
super.post_randomize();
|
||||
comment = "INVALID";
|
||||
endfunction
|
||||
|
||||
endclass // ibex_illegal_instr
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue