├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ └── -riscv---nominate-csr-to-be--write--safe.md └── workflows │ ├── changelog.yaml │ ├── clippy.yaml │ ├── label.yaml │ ├── riscv-peripheral.yaml │ ├── riscv-rt.yaml │ ├── riscv-semihosting.yaml │ ├── riscv-target-parser.yaml │ ├── riscv.yaml │ ├── rustfmt.yaml │ └── tests.yaml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── Cargo.toml ├── README.md ├── riscv-pac ├── CHANGELOG.md ├── Cargo.toml ├── README.md └── src │ ├── lib.rs │ └── result.rs ├── riscv-peripheral ├── CHANGELOG.md ├── Cargo.toml ├── README.md ├── examples │ └── e310x.rs └── src │ ├── aclint.rs │ ├── aclint │ ├── mswi.rs │ ├── mtimer.rs │ └── sswi.rs │ ├── common.rs │ ├── hal.rs │ ├── hal │ └── aclint.rs │ ├── lib.rs │ ├── macros.rs │ ├── plic.rs │ └── plic │ ├── claim.rs │ ├── enables.rs │ ├── pendings.rs │ ├── priorities.rs │ └── threshold.rs ├── riscv-rt ├── CHANGELOG.md ├── Cargo.toml ├── README.md ├── build.rs ├── examples │ ├── device.x │ ├── empty.rs │ └── multi_core.rs ├── exceptions.x ├── interrupts.x ├── link.x.in ├── macros │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── src │ ├── asm.rs │ ├── exceptions.rs │ ├── interrupts.rs │ └── lib.rs ├── riscv-semihosting ├── CHANGELOG.md ├── Cargo.toml ├── README.md ├── build.rs └── src │ ├── debug.rs │ ├── export.rs │ ├── hio.rs │ ├── lib.rs │ ├── macros.rs │ └── nr.rs ├── riscv-target-parser ├── CHANGELOG.md ├── Cargo.toml ├── README.md └── src │ ├── extension.rs │ └── lib.rs ├── riscv ├── CHANGELOG.md ├── Cargo.toml ├── README.md ├── build.rs ├── macros │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── src │ ├── asm.rs │ ├── bits.rs │ ├── critical_section.rs │ ├── delay.rs │ ├── interrupt.rs │ ├── interrupt │ ├── machine.rs │ └── supervisor.rs │ ├── lib.rs │ ├── macros.rs │ ├── register.rs │ └── register │ ├── cycle.rs │ ├── cycleh.rs │ ├── hpmcounterx.rs │ ├── instret.rs │ ├── instreth.rs │ ├── macros.rs │ ├── marchid.rs │ ├── mcause.rs │ ├── mconfigptr.rs │ ├── mcounteren.rs │ ├── mcountinhibit.rs │ ├── mcycle.rs │ ├── mcycleh.rs │ ├── medeleg.rs │ ├── mepc.rs │ ├── mhartid.rs │ ├── mhpmcounterx.rs │ ├── mhpmeventx.rs │ ├── mideleg.rs │ ├── mie.rs │ ├── mimpid.rs │ ├── minstret.rs │ ├── minstreth.rs │ ├── mip.rs │ ├── misa.rs │ ├── mscratch.rs │ ├── mstatus.rs │ ├── mstatush.rs │ ├── mtinst.rs │ ├── mtval.rs │ ├── mtvec.rs │ ├── mvendorid.rs │ ├── pmpaddrx.rs │ ├── pmpcfgx.rs │ ├── satp.rs │ ├── scause.rs │ ├── scontext.rs │ ├── scounteren.rs │ ├── senvcfg.rs │ ├── sepc.rs │ ├── sie.rs │ ├── sip.rs │ ├── sscratch.rs │ ├── sstatus.rs │ ├── stval.rs │ ├── stvec.rs │ ├── tests.rs │ ├── tests │ ├── read_only_csr.rs │ ├── read_write_csr.rs │ └── write_only_csr.rs │ ├── time.rs │ └── timeh.rs ├── tests-build ├── Cargo.toml ├── build.rs ├── device.x ├── examples │ └── empty.rs ├── memory.x └── src │ └── lib.rs └── tests-trybuild ├── Cargo.toml └── tests ├── riscv-rt ├── core_interrupt │ ├── fail_empty_macro.rs │ ├── fail_empty_macro.stderr │ ├── fail_impl_interrupt_number.rs │ ├── fail_impl_interrupt_number.stderr │ ├── fail_signatures.rs │ ├── fail_signatures.stderr │ └── pass_core_interrupt.rs ├── exception │ ├── fail_empty_macro.rs │ ├── fail_empty_macro.stderr │ ├── fail_impl_exception_number.rs │ ├── fail_impl_exception_number.stderr │ ├── fail_signatures.rs │ ├── fail_signatures.stderr │ └── pass_exception.rs └── external_interrupt │ ├── fail_empty_macro.rs │ ├── fail_empty_macro.stderr │ ├── fail_impl_interrupt_number.rs │ ├── fail_impl_interrupt_number.stderr │ ├── fail_signatures.rs │ ├── fail_signatures.stderr │ └── pass_external_interrupt.rs ├── riscv ├── fail_empty_macro.rs ├── fail_empty_macro.stderr ├── fail_no_unsafe.rs ├── fail_no_unsafe.stderr ├── fail_unknown_trait.rs ├── fail_unknown_trait.stderr └── pass_test.rs └── test.rs /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @rust-embedded/riscv -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/-riscv---nominate-csr-to-be--write--safe.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "`riscv`: Nominate CSR to be `write` safe" 3 | about: Suggest to make writes of a given CSR safe 4 | title: "`riscv`: make [CSR] write-safe" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Which CSR do you want to nominate as `write` safe?** 11 | Indicate which CSR you want to be `write` safe. Ex. `mepc` 12 | 13 | **Does a CSR write introduce potential memory safety issues in safe code? Please describe.** 14 | A clear and concise justification on why writing to this CSR will **never** introduce memory safety issues in safe code. 15 | 16 | **Does a CSR write introduce potential undefined behavior in safe code? Please describe.** 17 | A clear and concise justification on why writing to this CSR will **never** lead to undefined behavior in safe code. 18 | 19 | **Does a CSR write invalidate invariants in safe code?** 20 | A clear and concise justification on why writing to this CSR will **never** invalidate invariants in safe code. 21 | 22 | **Additional context** 23 | Please feel free to add any other context or screenshots about your request here. 24 | -------------------------------------------------------------------------------- /.github/workflows/changelog.yaml: -------------------------------------------------------------------------------- 1 | name: Changelog check 2 | 3 | on: 4 | merge_group: 5 | pull_request: 6 | types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled] 7 | 8 | jobs: 9 | changelog-check: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@v4 14 | 15 | - name: Check which component is modified 16 | uses: dorny/paths-filter@v2 17 | id: changes 18 | with: 19 | filters: | 20 | riscv: 21 | - 'riscv/**' 22 | riscv-pac: 23 | - 'riscv-pac/**' 24 | riscv-peripheral: 25 | - 'riscv-peripheral/**' 26 | riscv-rt: 27 | - 'riscv-rt/**' 28 | riscv-semihosting: 29 | - 'riscv-semihosting/**' 30 | riscv-target-parser: 31 | - 'riscv-target-parser/**' 32 | 33 | - name: Check for CHANGELOG.md (riscv) 34 | if: steps.changes.outputs.riscv == 'true' 35 | uses: dangoslen/changelog-enforcer@v3 36 | with: 37 | changeLogPath: ./riscv/CHANGELOG.md 38 | skipLabels: 'skip changelog' 39 | missingUpdateErrorMessage: 'Please add a changelog entry in the riscv/CHANGELOG.md file.' 40 | 41 | - name: Check for CHANGELOG.md (riscv-pac) 42 | if: steps.changes.outputs.riscv-pac == 'true' 43 | uses: dangoslen/changelog-enforcer@v3 44 | with: 45 | changeLogPath: ./riscv-pac/CHANGELOG.md 46 | skipLabels: 'skip changelog' 47 | missingUpdateErrorMessage: 'Please add a changelog entry in the riscv-pac/CHANGELOG.md file.' 48 | 49 | - name: Check for CHANGELOG.md (riscv-peripheral) 50 | if: steps.changes.outputs.riscv-peripheral == 'true' 51 | uses: dangoslen/changelog-enforcer@v3 52 | with: 53 | changeLogPath: ./riscv-peripheral/CHANGELOG.md 54 | skipLabels: 'skip changelog' 55 | missingUpdateErrorMessage: 'Please add a changelog entry in the riscv-peripheral/CHANGELOG.md file.' 56 | 57 | - name: Check for CHANGELOG.md (riscv-rt) 58 | if: steps.changes.outputs.riscv-rt == 'true' 59 | uses: dangoslen/changelog-enforcer@v3 60 | with: 61 | changeLogPath: ./riscv-rt/CHANGELOG.md 62 | skipLabels: 'skip changelog' 63 | missingUpdateErrorMessage: 'Please add a changelog entry in the riscv-rt/CHANGELOG.md file.' 64 | 65 | - name: Check for CHANGELOG.md (riscv-semihosting) 66 | if: steps.changes.outputs.riscv-semihosting == 'true' 67 | uses: dangoslen/changelog-enforcer@v3 68 | with: 69 | changeLogPath: ./riscv-semihosting/CHANGELOG.md 70 | skipLabels: 'skip changelog' 71 | missingUpdateErrorMessage: 'Please add a changelog entry in the riscv-semihosting/CHANGELOG.md file.' 72 | 73 | - name: Check for CHANGELOG.md (riscv-target-parser) 74 | if: steps.changes.outputs.riscv-target-parser == 'true' 75 | uses: dangoslen/changelog-enforcer@v3 76 | with: 77 | changeLogPath: ./riscv-target-parser/CHANGELOG.md 78 | skipLabels: 'skip changelog' 79 | missingUpdateErrorMessage: 'Please add a changelog entry in the riscv-target-parser/CHANGELOG.md file.' 80 | -------------------------------------------------------------------------------- /.github/workflows/clippy.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ master ] 4 | pull_request: 5 | merge_group: 6 | 7 | name: Lints compliance check 8 | 9 | env: 10 | CLIPPY_PARAMS: -W clippy::all -W clippy::pedantic -W clippy::nursery -W clippy::cargo 11 | 12 | jobs: 13 | clippy: 14 | strategy: 15 | matrix: 16 | toolchain: [ stable, nightly ] 17 | include: 18 | # Nightly is only for reference and allowed to fail 19 | - toolchain: nightly 20 | experimental: true 21 | runs-on: ubuntu-latest 22 | continue-on-error: ${{ matrix.experimental || false }} 23 | steps: 24 | - uses: actions/checkout@v4 25 | - uses: dtolnay/rust-toolchain@master 26 | with: 27 | toolchain: ${{ matrix.toolchain }} 28 | components: clippy 29 | - name: Run clippy (no features) 30 | run: cargo clippy --all --no-default-features -- -D warnings 31 | - name: Run clippy (all features) 32 | # We exclude riscv-peripheral because it's not yet stable-compliant (added -A deprecated for pre_init macro) 33 | run: cargo clippy --exclude riscv-peripheral --all --all-features -- -D warnings -A deprecated 34 | 35 | # Additonal clippy checks for riscv-rt 36 | clippy-riscv-rt: 37 | strategy: 38 | matrix: 39 | toolchain: [ stable, nightly ] 40 | runs-on: ubuntu-latest 41 | continue-on-error: ${{ matrix.experimental || false }} 42 | steps: 43 | - uses: actions/checkout@v4 44 | - uses: dtolnay/rust-toolchain@master 45 | with: 46 | toolchain: ${{ matrix.toolchain }} 47 | components: clippy 48 | - name: Run clippy (s-mode) 49 | run: cargo clippy --package riscv-rt --all --features=s-mode -- -D warnings 50 | - name: Run clippy (single-hart) 51 | run: cargo clippy --package riscv-rt --all --features=single-hart -- -D warnings 52 | 53 | # Job to check that all the lint checks succeeded 54 | clippy-check: 55 | needs: 56 | - clippy 57 | - clippy-riscv-rt 58 | runs-on: ubuntu-latest 59 | if: always() 60 | steps: 61 | - run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' 62 | -------------------------------------------------------------------------------- /.github/workflows/label.yaml: -------------------------------------------------------------------------------- 1 | name: Check Labels 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled] 6 | 7 | jobs: 8 | label-check: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: mheap/github-action-required-labels@v5 12 | with: 13 | mode: exactly 14 | count: 0 15 | labels: "work in progress, do not merge" 16 | add_comment: true 17 | message: "This PR is being prevented from merging because it presents one of the blocking labels: {{ provided }}." 18 | -------------------------------------------------------------------------------- /.github/workflows/riscv-peripheral.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ master ] 4 | pull_request: 5 | merge_group: 6 | 7 | name: Build check (riscv-peripheral) 8 | 9 | jobs: 10 | # We check that the crate builds and links for all the toolchains and targets. 11 | build-riscv: 12 | strategy: 13 | matrix: 14 | # All generated code should be running on stable now, MRSV is 1.75.0 15 | toolchain: [ stable, nightly, 1.75.0 ] 16 | target: 17 | - riscv32i-unknown-none-elf 18 | - riscv32imc-unknown-none-elf 19 | - riscv32imac-unknown-none-elf 20 | - riscv64imac-unknown-none-elf 21 | - riscv64gc-unknown-none-elf 22 | include: 23 | # Nightly is only for reference and allowed to fail 24 | - toolchain: nightly 25 | experimental: true 26 | runs-on: ubuntu-latest 27 | continue-on-error: ${{ matrix.experimental || false }} 28 | steps: 29 | - uses: actions/checkout@v4 30 | - uses: dtolnay/rust-toolchain@master 31 | with: 32 | toolchain: ${{ matrix.toolchain }} 33 | targets: ${{ matrix.target }} 34 | - name: Build 35 | run: cargo build --package riscv-peripheral --target ${{ matrix.target }} 36 | 37 | # On MacOS, Ubuntu, and Windows, we run the tests. 38 | build-others: 39 | strategy: 40 | matrix: 41 | os: 42 | - macos-latest 43 | - ubuntu-latest 44 | # - windows-latest issues when testing and external symbols are not found 45 | runs-on: ${{ matrix.os }} 46 | steps: 47 | - uses: actions/checkout@v3 48 | - uses: dtolnay/rust-toolchain@stable 49 | - name: Test 50 | run: cargo test --package riscv-peripheral 51 | 52 | # Job to check that all the builds succeeded 53 | build-check: 54 | needs: 55 | - build-riscv 56 | - build-others 57 | runs-on: ubuntu-latest 58 | if: always() 59 | steps: 60 | - run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' 61 | -------------------------------------------------------------------------------- /.github/workflows/riscv-rt.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ master ] 4 | pull_request: 5 | merge_group: 6 | 7 | name: Build check (riscv-rt) 8 | 9 | jobs: 10 | build-riscv: 11 | strategy: 12 | matrix: 13 | # All generated code should be running on stable now, MRSV is 1.67.0 14 | toolchain: [ stable, nightly, 1.67.0 ] 15 | target: 16 | - riscv32i-unknown-none-elf 17 | - riscv32im-unknown-none-elf 18 | - riscv32imc-unknown-none-elf 19 | - riscv32imac-unknown-none-elf 20 | - riscv32imafc-unknown-none-elf 21 | - riscv64imac-unknown-none-elf 22 | - riscv64gc-unknown-none-elf 23 | example: 24 | - empty 25 | - multi_core 26 | include: 27 | # Nightly is only for reference and allowed to fail 28 | - toolchain: nightly 29 | experimental: true 30 | exclude: 31 | - toolchain: 1.67.0 32 | target: riscv32im-unknown-none-elf 33 | - toolchain: 1.67.0 34 | target: riscv32imafc-unknown-none-elf 35 | runs-on: ubuntu-latest 36 | continue-on-error: ${{ matrix.experimental || false }} 37 | steps: 38 | - uses: actions/checkout@v4 39 | - uses: dtolnay/rust-toolchain@master 40 | with: 41 | toolchain: ${{ matrix.toolchain }} 42 | targets: ${{ matrix.target }} 43 | - name: Build (no features) 44 | run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} 45 | - name : Build (s-mode) 46 | run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --features=s-mode 47 | - name : Build (single-hart) 48 | run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --features=single-hart 49 | - name : Build (v-trap) 50 | run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --features=v-trap 51 | - name : Build (all features except u-boot) 52 | run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example ${{ matrix.example }} --features=s-mode,single-hart,v-trap 53 | - name : Build (u-boot) 54 | run: RUSTFLAGS="-C link-arg=-Triscv-rt/examples/device.x" cargo build --package riscv-rt --target ${{ matrix.target }} --example empty --features=u-boot 55 | 56 | build-others: 57 | strategy: 58 | matrix: 59 | os: [ macos-latest, ubuntu-latest, windows-latest ] 60 | runs-on: ${{ matrix.os }} 61 | steps: 62 | - uses: actions/checkout@v4 63 | - uses: dtolnay/rust-toolchain@stable 64 | - name: Build (no features) 65 | run: cargo build --package riscv-rt 66 | - name: Build (all features but u-boot) 67 | run: cargo build --package riscv-rt --features=s-mode,single-hart,v-trap 68 | - name: Build (u-boot) 69 | run: cargo build --package riscv-rt --features=u-boot 70 | 71 | # Job to check that all the builds succeeded 72 | build-check: 73 | needs: 74 | - build-riscv 75 | - build-others 76 | runs-on: ubuntu-latest 77 | if: always() 78 | steps: 79 | - run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' 80 | -------------------------------------------------------------------------------- /.github/workflows/riscv-semihosting.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ master ] 4 | pull_request: 5 | merge_group: 6 | 7 | name: Build check (riscv-semihosting) 8 | 9 | jobs: 10 | # We check that the crate builds and links for all the toolchains and targets. 11 | build-riscv: 12 | strategy: 13 | matrix: 14 | # All generated code should be running on stable now, MRSV is 1.67.0 15 | toolchain: [ stable, nightly, 1.67.0 ] 16 | target: 17 | - riscv32i-unknown-none-elf 18 | - riscv32imc-unknown-none-elf 19 | - riscv32imac-unknown-none-elf 20 | - riscv64imac-unknown-none-elf 21 | - riscv64gc-unknown-none-elf 22 | include: 23 | # Nightly is only for reference and allowed to fail 24 | - toolchain: nightly 25 | experimental: true 26 | runs-on: ubuntu-latest 27 | continue-on-error: ${{ matrix.experimental || false }} 28 | steps: 29 | - uses: actions/checkout@v4 30 | - uses: dtolnay/rust-toolchain@master 31 | with: 32 | toolchain: ${{ matrix.toolchain }} 33 | targets: ${{ matrix.target }} 34 | - name: Build (M-mode) 35 | run: cargo build --package riscv-semihosting --target ${{ matrix.target }} 36 | - name: Build (U-mode) 37 | run: cargo build --package riscv-semihosting --target ${{ matrix.target }} --features=u-mode 38 | - name: Build (no semihosting) 39 | run: cargo build --package riscv-semihosting --target ${{ matrix.target }} --features=no-semihosting 40 | 41 | # On MacOS, Ubuntu, and Windows, we at least make sure that the crate builds and links. 42 | build-others: 43 | strategy: 44 | matrix: 45 | os: [ macos-latest, ubuntu-latest, windows-latest ] 46 | runs-on: ${{ matrix.os }} 47 | steps: 48 | - uses: actions/checkout@v3 49 | - uses: dtolnay/rust-toolchain@stable 50 | - name: Build (no features) 51 | run: cargo build --package riscv-semihosting 52 | - name: Build (all features) 53 | run: cargo build --package riscv-semihosting --all-features 54 | 55 | # Job to check that all the builds succeeded 56 | build-check: 57 | needs: 58 | - build-riscv 59 | - build-others 60 | runs-on: ubuntu-latest 61 | if: always() 62 | steps: 63 | - run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' 64 | -------------------------------------------------------------------------------- /.github/workflows/riscv-target-parser.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ master ] 4 | pull_request: 5 | merge_group: 6 | 7 | name: Run tests (riscv-target-parser) 8 | 9 | jobs: 10 | run-tests: 11 | strategy: 12 | matrix: 13 | os: [ macos-latest, ubuntu-latest, windows-latest ] 14 | toolchain: [ stable, nightly, 1.67.0 ] 15 | include: 16 | # Nightly is only for reference and allowed to fail 17 | - rust: nightly 18 | experimental: true 19 | runs-on: ${{ matrix.os }} 20 | continue-on-error: ${{ matrix.experimental || false }} 21 | steps: 22 | - uses: actions/checkout@v4 23 | - name: Update Rust toolchain 24 | run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} 25 | - name: Build 26 | run: cargo build --package riscv-target-parser 27 | - name: Run tests 28 | run: cargo test --package riscv-target-parser 29 | 30 | # Job to check that all the builds succeeded 31 | tests-check: 32 | needs: 33 | - run-tests 34 | runs-on: ubuntu-latest 35 | if: always() 36 | steps: 37 | - run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' 38 | -------------------------------------------------------------------------------- /.github/workflows/riscv.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ master ] 4 | pull_request: 5 | merge_group: 6 | 7 | name: Build check (riscv) 8 | 9 | jobs: 10 | # We check that the crate builds and links for all the toolchains and targets. 11 | build-riscv: 12 | strategy: 13 | matrix: 14 | # All generated code should be running on stable now, MRSV is 1.67.0 15 | toolchain: [ stable, nightly, 1.67.0 ] 16 | target: 17 | - riscv32i-unknown-none-elf 18 | - riscv32imc-unknown-none-elf 19 | - riscv32imac-unknown-none-elf 20 | - riscv64imac-unknown-none-elf 21 | - riscv64gc-unknown-none-elf 22 | include: 23 | # Nightly is only for reference and allowed to fail 24 | - toolchain: nightly 25 | experimental: true 26 | runs-on: ubuntu-latest 27 | continue-on-error: ${{ matrix.experimental || false }} 28 | steps: 29 | - uses: actions/checkout@v4 30 | - uses: dtolnay/rust-toolchain@master 31 | with: 32 | toolchain: ${{ matrix.toolchain }} 33 | targets: ${{ matrix.target }} 34 | - name: Build (M-mode) 35 | run: cargo build --package riscv --target ${{ matrix.target }} 36 | - name: Build (M-mode, critical section) 37 | run: cargo build --package riscv --target ${{ matrix.target }} --features=critical-section-single-hart 38 | - name: Build (S-mode) 39 | run: cargo build --package riscv --target ${{ matrix.target }} --features=s-mode 40 | - name: Build (all features) 41 | run: cargo build --package riscv --target ${{ matrix.target }} --all-features 42 | 43 | # On MacOS, Ubuntu, and Windows, we at least make sure that the crate builds and links. 44 | build-others: 45 | strategy: 46 | matrix: 47 | os: [ macos-latest, ubuntu-latest, windows-latest ] 48 | runs-on: ${{ matrix.os }} 49 | steps: 50 | - uses: actions/checkout@v3 51 | - uses: dtolnay/rust-toolchain@stable 52 | - name: Build (no features) 53 | run: cargo build --package riscv 54 | - name: Build (all features) 55 | run: cargo build --package riscv --all-features 56 | 57 | # Job to check that all the builds succeeded 58 | build-check: 59 | needs: 60 | - build-riscv 61 | - build-others 62 | runs-on: ubuntu-latest 63 | if: always() 64 | steps: 65 | - run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' 66 | -------------------------------------------------------------------------------- /.github/workflows/rustfmt.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ master ] 4 | pull_request: 5 | merge_group: 6 | 7 | name: Code formatting check 8 | 9 | jobs: 10 | rustfmt: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: dtolnay/rust-toolchain@stable 15 | with: 16 | components: rustfmt 17 | - name: Run Rustfmt 18 | run: cargo fmt --all -- --check --verbose 19 | -------------------------------------------------------------------------------- /.github/workflows/tests.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ master ] 4 | pull_request: 5 | merge_group: 6 | 7 | name: Run tests (build and trybuild) 8 | 9 | jobs: 10 | run-trybuild: 11 | strategy: 12 | matrix: 13 | os: [ macos-latest, ubuntu-latest ] # windows shows weird linking errors 14 | runs-on: ${{ matrix.os }} 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: dtolnay/rust-toolchain@stable 18 | - name: Run tests 19 | run: cargo test --package tests-trybuild 20 | run-build: 21 | strategy: 22 | matrix: 23 | # All generated code should be running on stable now, MRSV is 1.67.0 24 | toolchain: [ stable, nightly, 1.67.0 ] 25 | target: 26 | - riscv32i-unknown-none-elf 27 | - riscv32im-unknown-none-elf 28 | - riscv32imc-unknown-none-elf 29 | - riscv32imac-unknown-none-elf 30 | - riscv32imafc-unknown-none-elf 31 | - riscv64imac-unknown-none-elf 32 | - riscv64gc-unknown-none-elf 33 | example: 34 | - empty 35 | include: 36 | # Nightly is only for reference and allowed to fail 37 | - toolchain: nightly 38 | experimental: true 39 | exclude: 40 | - toolchain: 1.67.0 41 | target: riscv32im-unknown-none-elf 42 | - toolchain: 1.67.0 43 | target: riscv32imafc-unknown-none-elf 44 | runs-on: ubuntu-latest 45 | continue-on-error: ${{ matrix.experimental || false }} 46 | steps: 47 | - uses: actions/checkout@v4 48 | - uses: dtolnay/rust-toolchain@master 49 | with: 50 | toolchain: ${{ matrix.toolchain }} 51 | targets: ${{ matrix.target }} 52 | - name: Build (no features) 53 | run: RUSTFLAGS="-C link-arg=-Tdevice.x -C link-arg=-Tmemory.x -C link-arg=-Tlink.x" cargo build --package tests-build --target ${{ matrix.target }} --example ${{ matrix.example }} 54 | - name: Build (include device.x) 55 | run: RUSTFLAGS="-C link-arg=-Tmemory.x -C link-arg=-Tlink.x" cargo build --package tests-build --target ${{ matrix.target }} --example ${{ matrix.example }} --features device 56 | - name: Build (include memory.x) 57 | run: RUSTFLAGS="-C link-arg=-Tdevice.x -C link-arg=-Tlink.x" cargo build --package tests-build --target ${{ matrix.target }} --example ${{ matrix.example }} --features memory 58 | - name: Build (include device.x and memory.x) 59 | run: RUSTFLAGS="-C link-arg=-Tlink.x" cargo build --package tests-build --target ${{ matrix.target }} --example ${{ matrix.example }} --features device,memory 60 | 61 | - name: Build (custom interrupts and exceptions) 62 | run: RUSTFLAGS="-C link-arg=-Tdevice.x -C link-arg=-Tmemory.x -C link-arg=-Tlink.x" cargo build --package tests-build --target ${{ matrix.target }} --example ${{ matrix.example }} --features no-interrupts,no-exceptions 63 | - name: Build (custom interrupts and exceptions, include device.x) 64 | run: RUSTFLAGS="-C link-arg=-Tmemory.x -C link-arg=-Tlink.x" cargo build --package tests-build --target ${{ matrix.target }} --example ${{ matrix.example }} --features no-interrupts,no-exceptions,device 65 | - name: Build (custom interrupts and exceptions, include memory.x) 66 | run: RUSTFLAGS="-C link-arg=-Tdevice.x -C link-arg=-Tlink.x" cargo build --package tests-build --target ${{ matrix.target }} --example ${{ matrix.example }} --features no-interrupts,no-exceptions,memory 67 | - name: Build (custom interrupts and exceptions, include device.x and memory.x) 68 | run: RUSTFLAGS="-C link-arg=-Tlink.x" cargo build --package tests-build --target ${{ matrix.target }} --example ${{ matrix.example }} --features no-interrupts,no-exceptions,device,memory 69 | 70 | # Job to check that all the builds succeeded 71 | tests-check: 72 | needs: 73 | - run-trybuild 74 | - run-build 75 | runs-on: ubuntu-latest 76 | if: always() 77 | steps: 78 | - run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' 79 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | target/ 3 | 4 | .vscode/ 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "riscv", 5 | "riscv-pac", 6 | "riscv-peripheral", 7 | "riscv-rt", 8 | "riscv-semihosting", 9 | "riscv-target-parser", 10 | "tests-build", 11 | "tests-trybuild", 12 | ] 13 | 14 | default-members = [ 15 | "riscv", 16 | "riscv-pac", 17 | "riscv-peripheral", 18 | "riscv-rt", 19 | "riscv-semihosting", 20 | ] 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RISC-V crates 2 | 3 | This repository contains various crates useful for writing Rust programs on RISC-V microcontrollers: 4 | 5 | * [`riscv`]: CPU registers access and intrinsics 6 | * [`riscv-pac`]: Common traits to be implemented by RISC-V PACs 7 | * [`riscv-peripheral`]: Interfaces for standard RISC-V peripherals 8 | * [`riscv-rt`]: Startup code and interrupt handling 9 | * [`riscv-semihosting`]: Semihosting for RISC-V processors 10 | * [`riscv-target-parser`]: Utility crate for parsing RISC-V targets in build scripts 11 | 12 | This project is developed and maintained by the [RISC-V team][team]. 13 | 14 | ### Contribution 15 | 16 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the 17 | work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any 18 | additional terms or conditions. 19 | 20 | ## Code of Conduct 21 | 22 | Contribution to this crate is organized under the terms of the [Rust Code of 23 | Conduct][CoC], the maintainer of this crate, the [RISC-V team][team], promises 24 | to intervene to uphold that code of conduct. 25 | 26 | [`riscv`]: https://crates.io/crates/riscv 27 | [`riscv-pac`]: https://crates.io/crates/riscv-pac 28 | [`riscv-peripheral`]: https://crates.io/crates/riscv-peripheral 29 | [`riscv-rt`]: https://crates.io/crates/riscv-rt 30 | [`riscv-semihosting`]: https://crates.io/crates/riscv-semihosting 31 | [`riscv-target-parser`]: https://crates.io/crates/riscv-target-parser 32 | [team]: https://github.com/rust-embedded/wg#the-risc-v-team 33 | [CoC]: CODE_OF_CONDUCT.md 34 | -------------------------------------------------------------------------------- /riscv-pac/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | ## [Unreleased] 9 | 10 | ## [v0.2.0] - 2024-10-19 11 | 12 | ### Added 13 | 14 | - Add `result` module for `Error` and `Result` types 15 | - Add `ExceptionNumber` trait. 16 | - Classify interrupt numbers in `CoreInterruptNumber` and `ExternalInterruptNumber`. 17 | - Added simple tests to illustrate how to implement all the provided traits. 18 | 19 | ### Changed 20 | 21 | - All traits now work with `usize` data type. 22 | 23 | ## [v0.1.1] - 2024-02-15 24 | 25 | - Fix crates.io badge links 26 | 27 | ## [v0.1.0] - 2024-01-14 28 | 29 | ### Added 30 | 31 | - Add `InterruptNumber`, `PriorityNumber`, and `HartIdNumber` traits. 32 | 33 | ### Changed 34 | 35 | - Update `README.md` 36 | -------------------------------------------------------------------------------- /riscv-pac/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "riscv-pac" 3 | version = "0.2.0" 4 | edition = "2021" 5 | rust-version = "1.60" 6 | repository = "https://github.com/rust-embedded/riscv" 7 | authors = ["The RISC-V Team "] 8 | categories = ["embedded", "hardware-support", "no-std"] 9 | description = "Low level access to RISC-V processors" 10 | documentation = "https://docs.rs/riscv-pac" 11 | keywords = ["riscv", "register", "peripheral"] 12 | license = "ISC" 13 | 14 | [package.metadata.docs.rs] 15 | default-target = "riscv64imac-unknown-none-elf" 16 | targets = [ 17 | "riscv32i-unknown-none-elf", "riscv32imc-unknown-none-elf", "riscv32imac-unknown-none-elf", 18 | "riscv64imac-unknown-none-elf", "riscv64gc-unknown-none-elf", 19 | ] 20 | -------------------------------------------------------------------------------- /riscv-pac/README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/d/riscv-pac.svg)](https://crates.io/crates/riscv-pac) 2 | [![crates.io](https://img.shields.io/crates/v/riscv-pac.svg)](https://crates.io/crates/riscv-pac) 3 | 4 | # `riscv-pac` 5 | 6 | > Target-specific traits to be implemented by PACs 7 | 8 | This project is developed and maintained by the [RISC-V team][team]. 9 | 10 | ## [Documentation](https://docs.rs/crate/riscv-pac) 11 | 12 | ## Minimum Supported Rust Version (MSRV) 13 | 14 | This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* 15 | compile with older versions but that may change in any new patch release. 16 | 17 | ## License 18 | 19 | Copyright 2023-2024 [RISC-V team][team] 20 | 21 | Permission to use, copy, modify, and/or distribute this software for any purpose 22 | with or without fee is hereby granted, provided that the above copyright notice 23 | and this permission notice appear in all copies. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 26 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 27 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 28 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 29 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 30 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 31 | THIS SOFTWARE. 32 | 33 | ## Code of Conduct 34 | 35 | Contribution to this crate is organized under the terms of the [Rust Code of 36 | Conduct][CoC], the maintainer of this crate, the [RISC-V team][team], promises 37 | to intervene to uphold that code of conduct. 38 | 39 | [CoC]: CODE_OF_CONDUCT.md 40 | [team]: https://github.com/rust-embedded/wg#the-risc-v-team 41 | -------------------------------------------------------------------------------- /riscv-pac/src/result.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | 3 | /// Convenience alias for the [Result](core::result::Result) type for the library. 4 | pub type Result = core::result::Result; 5 | 6 | /// Represents error variants for the library. 7 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 8 | pub enum Error { 9 | /// Attempted out-of-bounds access. 10 | IndexOutOfBounds { 11 | index: usize, 12 | min: usize, 13 | max: usize, 14 | }, 15 | /// Invalid field value. 16 | InvalidFieldValue { 17 | field: &'static str, 18 | value: usize, 19 | bitmask: usize, 20 | }, 21 | /// Invalid value of a register field that does not match any known variants. 22 | InvalidFieldVariant { field: &'static str, value: usize }, 23 | /// Invalid value. 24 | InvalidValue { value: usize, bitmask: usize }, 25 | /// Invalid value that does not match any known variants. 26 | InvalidVariant(usize), 27 | /// Unimplemented function or type. 28 | Unimplemented, 29 | } 30 | 31 | impl fmt::Display for Error { 32 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 33 | match self { 34 | Self::IndexOutOfBounds { index, min, max } => write!( 35 | f, 36 | "out-of-bounds access, index: {index}, min: {min}, max: {max}" 37 | ), 38 | Self::InvalidFieldValue { 39 | field, 40 | value, 41 | bitmask, 42 | } => write!( 43 | f, 44 | "invalid {field} field value: {value:#x}, valid bitmask: {bitmask:#x}", 45 | ), 46 | Self::InvalidFieldVariant { field, value } => { 47 | write!(f, "invalid {field} field variant: {value:#x}") 48 | } 49 | Self::InvalidValue { value, bitmask } => { 50 | write!(f, "invalid value: {value:#x}, valid bitmask: {bitmask:#x}",) 51 | } 52 | Self::InvalidVariant(value) => { 53 | write!(f, "invalid variant: {value:#x}") 54 | } 55 | Self::Unimplemented => write!(f, "unimplemented"), 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /riscv-peripheral/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | ## [Unreleased] 9 | 10 | ### Changed 11 | 12 | - Rework of CLINT peripheral to use methods instead of associated functions. 13 | This change follows the `svd2rust` pattern, making the ecosystem more consistent. 14 | - Simplify `clint_codegen!` macro using the `Deref` trait. 15 | - Rework of PLIC peripherals to use methods instead of associated functions. 16 | This change follows the `svd2rust` pattern, making the ecosystem more consistent. 17 | - Simplify `plic_codegen!` macro using the `Deref` trait. 18 | - Macros allow now to customize the struct name for CLINT and PLIC. 19 | - Macros allow now to customize the visibility of the `new` function for CLINT and PLIC. 20 | 21 | ### Removed 22 | 23 | - Removed support for `embedded-hal-async`, as it was not flexible enough to be 24 | used in different targets (single HART, multi HART...). Instead, each chip must 25 | have its own `chip-hal-async` crate that properly adapts to its specific needs. 26 | 27 | ### Fixed 28 | 29 | - `clippy` fixes 30 | 31 | ## [v0.2.1] - 2025-02-18 32 | 33 | ### Changed 34 | 35 | - Update `riscv` dependency to 0.13.0 36 | 37 | ## [v0.2.0] - 2024-10-19 38 | 39 | ### Added 40 | 41 | - use `riscv-pac` result types for trait implementations 42 | 43 | ### Changed 44 | 45 | - Adapt to new version of `riscv-pac` traits. 46 | - `PLIC` now expects interrupt enums to implement the `riscv_pac::ExternalInterruptNumber` trait. 47 | 48 | ### Fixed 49 | 50 | - `clippy` fixes 51 | 52 | ## [v0.1.0] - 2024-02-15 53 | 54 | ### Added 55 | 56 | - Add `ACLINT`, `CLINT`, and `PLIC` structs 57 | -------------------------------------------------------------------------------- /riscv-peripheral/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "riscv-peripheral" 3 | version = "0.3.0-rc.1" 4 | edition = "2021" 5 | rust-version = "1.75" 6 | repository = "https://github.com/rust-embedded/riscv" 7 | authors = ["The RISC-V Team "] 8 | categories = ["embedded", "hardware-support", "no-std"] 9 | description = "Interfaces for standard RISC-V peripherals" 10 | documentation = "https://docs.rs/riscv-peripheral" 11 | keywords = ["riscv", "peripheral", "clint", "plic"] 12 | license = "ISC" 13 | 14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 15 | 16 | [dependencies] 17 | embedded-hal = "1.0.0" 18 | paste = "1.0" 19 | riscv = { path = "../riscv", version = "0.13.0" } 20 | riscv-pac = { path = "../riscv-pac", version = "0.2.0" } 21 | 22 | [package.metadata.docs.rs] 23 | all-features = true 24 | default-target = "riscv64imac-unknown-none-elf" 25 | targets = [ 26 | "riscv32i-unknown-none-elf", "riscv32imc-unknown-none-elf", "riscv32imac-unknown-none-elf", 27 | "riscv64imac-unknown-none-elf", "riscv64gc-unknown-none-elf", 28 | ] 29 | -------------------------------------------------------------------------------- /riscv-peripheral/README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/d/riscv-peripheral.svg)](https://crates.io/crates/riscv-peripheral) 2 | [![crates.io](https://img.shields.io/crates/v/riscv-peripheral.svg)](https://crates.io/crates/riscv-peripheral) 3 | 4 | # `riscv-peripheral` 5 | 6 | > Interfaces for standard RISC-V peripherals 7 | 8 | This project is developed and maintained by the [RISC-V team][team]. 9 | 10 | ## [Documentation](https://docs.rs/crate/riscv-peripheral) 11 | 12 | ## Minimum Supported Rust Version (MSRV) 13 | 14 | This crate is guaranteed to compile on stable Rust 1.75 and up. It *might* 15 | compile with older versions but that may change in any new patch release. 16 | 17 | ## License 18 | 19 | Copyright 2023-2024 [RISC-V team][team] 20 | 21 | Permission to use, copy, modify, and/or distribute this software for any purpose 22 | with or without fee is hereby granted, provided that the above copyright notice 23 | and this permission notice appear in all copies. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 26 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 27 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 28 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 29 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 30 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 31 | THIS SOFTWARE. 32 | 33 | ## Code of Conduct 34 | 35 | Contribution to this crate is organized under the terms of the [Rust Code of 36 | Conduct][CoC], the maintainer of this crate, the [RISC-V team][team], promises 37 | to intervene to uphold that code of conduct. 38 | 39 | [CoC]: CODE_OF_CONDUCT.md 40 | [team]: https://github.com/rust-embedded/wg#the-risc-v-team 41 | -------------------------------------------------------------------------------- /riscv-peripheral/examples/e310x.rs: -------------------------------------------------------------------------------- 1 | //! Peripheral definitions for the E310x chip. 2 | //! 3 | //! This is a simple example of how to use the `riscv-peripheral` crate to generate 4 | //! peripheral definitions for a target. 5 | 6 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 7 | #[riscv::pac_enum(unsafe HartIdNumber)] 8 | pub enum HartId { 9 | H0 = 0, 10 | } 11 | 12 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 13 | #[riscv::pac_enum(unsafe ExternalInterruptNumber)] 14 | pub enum Interrupt { 15 | WATCHDOG = 1, 16 | RTC = 2, 17 | UART0 = 3, 18 | UART1 = 4, 19 | QSPI0 = 5, 20 | QSPI1 = 6, 21 | QSPI2 = 7, 22 | GPIO0 = 8, 23 | GPIO1 = 9, 24 | GPIO2 = 10, 25 | GPIO3 = 11, 26 | GPIO4 = 12, 27 | GPIO5 = 13, 28 | GPIO6 = 14, 29 | GPIO7 = 15, 30 | GPIO8 = 16, 31 | GPIO9 = 17, 32 | GPIO10 = 18, 33 | GPIO11 = 19, 34 | GPIO12 = 20, 35 | GPIO13 = 21, 36 | GPIO14 = 22, 37 | GPIO15 = 23, 38 | GPIO16 = 24, 39 | GPIO17 = 25, 40 | GPIO18 = 26, 41 | GPIO19 = 27, 42 | GPIO20 = 28, 43 | GPIO21 = 29, 44 | GPIO22 = 30, 45 | GPIO23 = 31, 46 | GPIO24 = 32, 47 | GPIO25 = 33, 48 | GPIO26 = 34, 49 | GPIO27 = 35, 50 | GPIO28 = 36, 51 | GPIO29 = 37, 52 | GPIO30 = 38, 53 | GPIO31 = 39, 54 | PWM0CMP0 = 40, 55 | PWM0CMP1 = 41, 56 | PWM0CMP2 = 42, 57 | PWM0CMP3 = 43, 58 | PWM1CMP0 = 44, 59 | PWM1CMP1 = 45, 60 | PWM1CMP2 = 46, 61 | PWM1CMP3 = 47, 62 | PWM2CMP0 = 48, 63 | PWM2CMP1 = 49, 64 | PWM2CMP2 = 50, 65 | PWM2CMP3 = 51, 66 | I2C0 = 52, 67 | } 68 | 69 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 70 | #[riscv::pac_enum(unsafe PriorityNumber)] 71 | pub enum Priority { 72 | P0 = 0, 73 | P1 = 1, 74 | P2 = 2, 75 | P3 = 3, 76 | P4 = 4, 77 | P5 = 5, 78 | P6 = 6, 79 | P7 = 7, 80 | } 81 | 82 | // We can define CLINT::new() as a public, safe function 83 | riscv_peripheral::clint_codegen!( 84 | pub CLINT, 85 | base 0x0200_0000, 86 | mtime_freq 32_768, 87 | harts [HartId::H0 => 0] 88 | ); 89 | 90 | // We can define PLIC::new() as a private, safe function... 91 | riscv_peripheral::plic_codegen!( 92 | PLIC, 93 | base 0x0C00_0000, 94 | harts [HartId::H0 => 0] 95 | ); 96 | 97 | // ... and then implement a public, unsafe function to steal the PLIC instance 98 | // Usually, this function is implemented by svd2rust, but we do it manually here 99 | impl PLIC { 100 | pub unsafe fn steal() -> Self { 101 | PLIC::new() 102 | } 103 | } 104 | 105 | fn main() { 106 | let _clint = CLINT::new(); 107 | let _plic = unsafe { PLIC::steal() }; 108 | } 109 | -------------------------------------------------------------------------------- /riscv-peripheral/src/aclint/mtimer.rs: -------------------------------------------------------------------------------- 1 | //! Machine-level Timer Device. 2 | 3 | pub use super::{Clint, HartIdNumber}; 4 | use crate::common::safe_peripheral; 5 | use riscv::register::{mhartid, mie, mip}; 6 | 7 | /// Trait for an MTIMER device. 8 | /// 9 | /// # Note 10 | /// 11 | /// For CLINT peripherals, this trait is automatically implemented. 12 | /// 13 | /// # Safety 14 | /// 15 | /// * This trait must only be implemented on a PAC of a target with an MTIMER device. 16 | /// * The `MTIMECMP` registers base address `MTIMECMP_BASE` must be valid for the target. 17 | /// * The `MTIME` registers base address `MTIME_BASE` must be valid for the target. 18 | /// * The `MTIME` clock frequency `MTIME_FREQ` must be valid for the target. 19 | pub unsafe trait Mtimer: Copy { 20 | /// Base address of the MTIMECMP registers. 21 | const MTIMECMP_BASE: usize; 22 | /// Base address of the MTIME register. 23 | const MTIME_BASE: usize; 24 | /// Clock frequency of the MTIME register. 25 | const MTIME_FREQ: usize; 26 | } 27 | 28 | // SAFETY: the offset of the MSWI peripheral is fixed in the CLINT peripheral 29 | unsafe impl Mtimer for C { 30 | const MTIMECMP_BASE: usize = C::BASE + 0x4000; 31 | const MTIME_BASE: usize = C::BASE + 0xBFF8; 32 | const MTIME_FREQ: usize = C::MTIME_FREQ; 33 | } 34 | 35 | /// MTIMER device. 36 | /// 37 | /// It has a single fixed-frequency monotonic time counter ([`MTIME`]) 38 | /// register and a time compare register ([`MTIMECMP`]) for each HART. 39 | #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] 40 | pub struct MTIMER { 41 | _marker: core::marker::PhantomData, 42 | } 43 | 44 | impl MTIMER { 45 | /// Creates a new `MTIMER` device. 46 | #[inline] 47 | pub const fn new() -> Self { 48 | Self { 49 | _marker: core::marker::PhantomData, 50 | } 51 | } 52 | 53 | /// Returns the base address of the `MTIMECMP` registers. 54 | #[inline] 55 | const fn mtimecmp_as_ptr(self) -> *const u64 { 56 | M::MTIMECMP_BASE as *const u64 57 | } 58 | 59 | /// Returns `true` if a machine timer interrupt is pending. 60 | #[inline] 61 | pub fn is_interrupting(self) -> bool { 62 | mip::read().mtimer() 63 | } 64 | 65 | /// Returns `true` if machine timer interrupts are enabled. 66 | #[inline] 67 | pub fn is_enabled(self) -> bool { 68 | mie::read().mtimer() 69 | } 70 | 71 | /// Enables machine timer interrupts in the current HART. 72 | /// 73 | /// # Safety 74 | /// 75 | /// Enabling interrupts may break mask-based critical sections. 76 | #[inline] 77 | pub unsafe fn enable(self) { 78 | mie::set_mtimer(); 79 | } 80 | 81 | /// Disables machine timer interrupts in the current HART. 82 | #[inline] 83 | pub fn disable(self) { 84 | // SAFETY: it is safe to disable interrupts 85 | unsafe { mie::clear_mtimer() }; 86 | } 87 | 88 | /// Returns the `MTIME` register. 89 | #[inline] 90 | pub const fn mtime(self) -> MTIME { 91 | // SAFETY: valid base address 92 | unsafe { MTIME::new(M::MTIME_BASE) } 93 | } 94 | 95 | /// Returns the `MTIMECMP` register for the HART which ID is `hart_id`. 96 | #[inline] 97 | pub fn mtimecmp(self, hart_id: H) -> MTIMECMP { 98 | // SAFETY: `hart_id` is valid for the target 99 | unsafe { MTIMECMP::new(self.mtimecmp_as_ptr().add(hart_id.number()) as _) } 100 | } 101 | 102 | /// Returns the `MTIMECMP` register for the current HART. 103 | /// 104 | /// # Note 105 | /// 106 | /// This function determines the current HART ID by reading the [`mhartid`] CSR. 107 | /// Thus, it can only be used in M-mode. For S-mode, use [`MTIMER::mtimecmp`] instead. 108 | #[inline] 109 | pub fn mtimecmp_mhartid(self) -> MTIMECMP { 110 | let hart_id = mhartid::read(); 111 | // SAFETY: `hart_id` is valid for the target and is the current hart 112 | unsafe { MTIMECMP::new(self.mtimecmp_as_ptr().add(hart_id) as _) } 113 | } 114 | } 115 | 116 | // MTIMECMP register. 117 | safe_peripheral!(MTIMECMP, u64, RW); 118 | 119 | // MTIME register. 120 | safe_peripheral!(MTIME, u64, RW); 121 | -------------------------------------------------------------------------------- /riscv-peripheral/src/aclint/sswi.rs: -------------------------------------------------------------------------------- 1 | //! Supervisor-level Software Interrupt Device. 2 | 3 | pub use super::HartIdNumber; 4 | use crate::common::unsafe_peripheral; 5 | use riscv::register::{sie, sip}; 6 | 7 | /// Trait for a Supervisor-level Software Interrupt device. 8 | /// 9 | /// # Safety 10 | /// 11 | /// * This trait must only be implemented on a PAC of a target with an SSWI device. 12 | /// * The SSWI device base address `BASE` must be valid for the target. 13 | pub unsafe trait Sswi: Copy { 14 | /// Base address of the SSWI peripheral. 15 | const BASE: usize; 16 | } 17 | 18 | /// SSWI peripheral. 19 | #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] 20 | pub struct SSWI { 21 | _marker: core::marker::PhantomData, 22 | } 23 | 24 | impl SSWI { 25 | /// Creates a new `SSWI` device. 26 | #[inline] 27 | pub const fn new() -> Self { 28 | Self { 29 | _marker: core::marker::PhantomData, 30 | } 31 | } 32 | 33 | /// Returns the base address of the `SSWI` device. 34 | #[inline] 35 | const fn as_ptr(self) -> *const u32 { 36 | S::BASE as *const u32 37 | } 38 | 39 | /// Returns `true` if a supervisor software interrupt is pending. 40 | #[inline] 41 | pub fn is_interrupting(self) -> bool { 42 | sip::read().ssoft() 43 | } 44 | 45 | /// Returns `true` if supervisor software interrupts are enabled. 46 | #[inline] 47 | pub fn is_enabled(self) -> bool { 48 | sie::read().ssoft() 49 | } 50 | 51 | /// Enables supervisor software interrupts in the current HART. 52 | /// 53 | /// # Safety 54 | /// 55 | /// Enabling interrupts may break mask-based critical sections. 56 | #[inline] 57 | pub unsafe fn enable(self) { 58 | sie::set_ssoft(); 59 | } 60 | 61 | /// Disables supervisor software interrupts in the current HART. 62 | #[inline] 63 | pub fn disable(self) { 64 | // SAFETY: it is safe to disable interrupts 65 | unsafe { sie::clear_ssoft() }; 66 | } 67 | 68 | /// Returns the `SETSSIP` register for the HART which ID is `hart_id`. 69 | #[inline] 70 | pub fn setssip(self, hart_id: H) -> SETSSIP { 71 | // SAFETY: `hart_id` is valid for the target 72 | unsafe { SETSSIP::new(self.as_ptr().add(hart_id.number()) as _) } 73 | } 74 | } 75 | 76 | unsafe_peripheral!(SETSSIP, u32, RW); 77 | 78 | impl SETSSIP { 79 | /// Returns `true` if a supervisor software interrupt is pending. 80 | #[inline] 81 | pub fn is_pending(self) -> bool { 82 | self.register.read() != 0 83 | } 84 | 85 | /// Writes to the register to trigger a supervisor software interrupt. 86 | #[inline] 87 | pub fn pend(self) { 88 | self.register.write(1); 89 | } 90 | 91 | /// Clears the register to unpend a supervisor software interrupt. 92 | #[inline] 93 | pub fn unpend(self) { 94 | self.register.write(0); 95 | } 96 | } 97 | 98 | #[cfg(test)] 99 | mod test { 100 | use super::*; 101 | 102 | #[test] 103 | fn test_sswi() { 104 | // Variable to emulate the interrupt pending register 105 | let mut raw_reg = 0u32; 106 | // SAFETY: valid memory address 107 | let setssip = unsafe { SETSSIP::new(&mut raw_reg as *mut u32 as usize) }; 108 | 109 | assert!(!setssip.is_pending()); 110 | assert_eq!(raw_reg, 0); 111 | setssip.pend(); 112 | assert!(setssip.is_pending()); 113 | assert_ne!(raw_reg, 0); 114 | setssip.unpend(); 115 | assert!(!setssip.is_pending()); 116 | assert_eq!(raw_reg, 0); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /riscv-peripheral/src/hal.rs: -------------------------------------------------------------------------------- 1 | //! trait implementations for embedded-hal 2 | 3 | pub use embedded_hal::*; // re-export embedded-hal to allow macros to use it 4 | 5 | pub mod aclint; // ACLINT and CLINT peripherals 6 | -------------------------------------------------------------------------------- /riscv-peripheral/src/hal/aclint.rs: -------------------------------------------------------------------------------- 1 | //! Delay trait implementation for (A)CLINT peripherals 2 | 3 | use crate::aclint::mtimer::{Mtimer, MTIMER}; 4 | use crate::hal::delay::DelayNs; 5 | 6 | impl DelayNs for MTIMER { 7 | #[inline] 8 | fn delay_ns(&mut self, ns: u32) { 9 | let t0 = self.mtime().read(); 10 | let ns_64: u64 = ns.into(); 11 | let n_ticks = ns_64 * M::MTIME_FREQ as u64 / 1_000_000_000; 12 | while self.mtime().read().wrapping_sub(t0) < n_ticks {} 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /riscv-peripheral/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Standard RISC-V peripherals for embedded systems written in Rust. 2 | 3 | #![deny(missing_docs)] 4 | #![no_std] 5 | 6 | pub use riscv; // re-export riscv crate to allow macros to use it 7 | pub use riscv_pac::result; // re-export the result module 8 | 9 | pub mod common; // common definitions for all peripherals 10 | pub mod hal; // trait implementations for embedded-hal 11 | pub mod macros; // macros for easing the definition of peripherals in PACs 12 | 13 | pub mod aclint; // ACLINT and CLINT peripherals 14 | pub mod plic; // PLIC peripheral 15 | 16 | #[cfg(test)] 17 | mod test { 18 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 19 | #[riscv::pac_enum(unsafe ExternalInterruptNumber)] 20 | pub(crate) enum Interrupt { 21 | I1 = 1, 22 | I2 = 2, 23 | I3 = 3, 24 | I4 = 4, 25 | } 26 | 27 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 28 | #[riscv::pac_enum(unsafe PriorityNumber)] 29 | pub(crate) enum Priority { 30 | P0 = 0, 31 | P1 = 1, 32 | P2 = 2, 33 | P3 = 3, 34 | } 35 | 36 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 37 | #[riscv::pac_enum(unsafe HartIdNumber)] 38 | pub(crate) enum HartId { 39 | H0 = 0, 40 | H1 = 1, 41 | H2 = 2, 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /riscv-peripheral/src/plic/claim.rs: -------------------------------------------------------------------------------- 1 | //! Interrupt claim/complete register 2 | 3 | use crate::common::unsafe_peripheral; 4 | use riscv_pac::ExternalInterruptNumber; 5 | 6 | unsafe_peripheral!(CLAIM, u32, RW); 7 | 8 | impl CLAIM { 9 | /// Claims the number of a pending interrupt for for the PLIC context. 10 | /// If no interrupt is pending for this context, it returns [`None`]. 11 | #[inline] 12 | pub fn claim(self) -> Option { 13 | match self.register.read() { 14 | 0 => None, 15 | i => Some(I::from_number(i as _).unwrap()), 16 | } 17 | } 18 | 19 | /// Marks a pending interrupt as complete for the PLIC context. 20 | /// 21 | /// # Note 22 | /// 23 | /// If the source ID does not match an interrupt source that is 24 | /// currently enabled for the target, the completion is silently ignored. 25 | #[inline] 26 | pub fn complete(self, source: I) { 27 | self.register.write(source.number() as _) 28 | } 29 | } 30 | 31 | #[cfg(test)] 32 | mod test { 33 | use super::*; 34 | use crate::test::Interrupt; 35 | use riscv_pac::InterruptNumber; 36 | 37 | #[test] 38 | fn test_claim() { 39 | let mut raw_reg = 0u32; 40 | // SAFETY: valid memory address 41 | let claim = unsafe { CLAIM::new(&mut raw_reg as *mut _ as _) }; 42 | 43 | assert_eq!(claim.claim::(), None); 44 | 45 | for i in 1..=Interrupt::MAX_INTERRUPT_NUMBER { 46 | let interrupt = Interrupt::from_number(i).unwrap(); 47 | claim.complete(interrupt); 48 | assert_eq!(claim.claim(), Some(interrupt)); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /riscv-peripheral/src/plic/pendings.rs: -------------------------------------------------------------------------------- 1 | //! Interrupt pending bits register. 2 | 3 | use crate::common::{Reg, RO}; 4 | use riscv_pac::ExternalInterruptNumber; 5 | 6 | /// Interrupts pending bits register. 7 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 8 | #[repr(transparent)] 9 | pub struct PENDINGS { 10 | ptr: *mut u32, 11 | } 12 | 13 | impl PENDINGS { 14 | /// Creates a new Interrupts pending bits register from a base address. 15 | /// 16 | /// # Safety 17 | /// 18 | /// The base address must point to a valid Interrupts pending bits register. 19 | #[inline] 20 | pub(crate) const unsafe fn new(address: usize) -> Self { 21 | Self { ptr: address as _ } 22 | } 23 | 24 | #[cfg(test)] 25 | #[inline] 26 | pub(crate) fn address(self) -> usize { 27 | self.ptr as _ 28 | } 29 | 30 | /// Checks if an interrupt triggered by a given source is pending. 31 | #[inline] 32 | pub fn is_pending(self, source: I) -> bool { 33 | let source = source.number(); 34 | let offset = (source / u32::BITS as usize) as _; 35 | // SAFETY: valid interrupt number 36 | let reg: Reg = unsafe { Reg::new(self.ptr.offset(offset)) }; 37 | reg.read_bit(source % u32::BITS as usize) 38 | } 39 | } 40 | 41 | #[cfg(test)] 42 | mod test { 43 | use super::*; 44 | use crate::test::Interrupt; 45 | 46 | #[test] 47 | fn test_pendings() { 48 | // slice to emulate the interrupt pendings register 49 | let mut raw_reg = [0u32; 32]; 50 | // SAFETY: valid memory address 51 | let pendings = unsafe { PENDINGS::new(raw_reg.as_mut_ptr() as _) }; 52 | 53 | for i in 0..255 { 54 | // SAFETY: valid memory address 55 | unsafe { raw_reg.as_mut_ptr().write_volatile(i) }; 56 | assert_eq!(pendings.is_pending(Interrupt::I1), i & 0x2 != 0); 57 | assert_eq!(pendings.is_pending(Interrupt::I2), i & 0x4 != 0); 58 | assert_eq!(pendings.is_pending(Interrupt::I3), i & 0x8 != 0); 59 | assert_eq!(pendings.is_pending(Interrupt::I4), i & 0x10 != 0); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /riscv-peripheral/src/plic/priorities.rs: -------------------------------------------------------------------------------- 1 | //! Interrupts Priorities register. 2 | 3 | use crate::common::{Reg, RW}; 4 | use riscv_pac::{ExternalInterruptNumber, PriorityNumber}; 5 | 6 | /// Interrupts priorities register. 7 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 8 | #[repr(transparent)] 9 | pub struct PRIORITIES { 10 | ptr: *mut u32, 11 | } 12 | 13 | impl PRIORITIES { 14 | /// Creates a new Interrupts priorities register from a base address. 15 | /// 16 | /// # Safety 17 | /// 18 | /// The base address must point to a valid Interrupts priorities register. 19 | #[inline] 20 | pub(crate) const unsafe fn new(address: usize) -> Self { 21 | Self { ptr: address as _ } 22 | } 23 | 24 | #[cfg(test)] 25 | #[inline] 26 | pub(crate) fn address(self) -> usize { 27 | self.ptr as _ 28 | } 29 | 30 | /// Returns the priority assigned to a given interrupt source. 31 | #[inline] 32 | pub fn get_priority(self, source: I) -> P { 33 | // SAFETY: valid interrupt number 34 | let reg: Reg = unsafe { Reg::new(self.ptr.add(source.number())) }; 35 | P::from_number(reg.read() as _).unwrap() 36 | } 37 | 38 | /// Sets the priority level of a given interrupt source. 39 | /// 40 | /// # Safety 41 | /// 42 | /// Changing the priority level can break priority-based critical sections. 43 | #[inline] 44 | pub unsafe fn set_priority( 45 | self, 46 | source: I, 47 | priority: P, 48 | ) { 49 | // SAFETY: valid interrupt number 50 | let reg: Reg = unsafe { Reg::new(self.ptr.add(source.number())) }; 51 | reg.write(priority.number() as _); 52 | } 53 | 54 | /// Resets all the priority levels of all the external interrupt sources to 0. 55 | /// 56 | /// # Note 57 | /// 58 | /// Priority level 0 is reserved for "no interrupt". 59 | /// Thus, this method effectively disables the all the external interrupts. 60 | #[inline] 61 | pub fn reset(self) { 62 | for source in 0..=I::MAX_INTERRUPT_NUMBER as _ { 63 | // SAFETY: interrupt number within range 64 | let reg: Reg = unsafe { Reg::new(self.ptr.offset(source)) }; 65 | reg.write(0); 66 | } 67 | } 68 | } 69 | 70 | #[cfg(test)] 71 | mod test { 72 | use super::*; 73 | use crate::test::{Interrupt, Priority}; 74 | use riscv_pac::InterruptNumber; 75 | 76 | #[test] 77 | fn test_priorities() { 78 | // slice to emulate the interrupt priorities register 79 | let mut raw_reg = [0u32; 1024]; 80 | // SAFETY: valid memory address 81 | let priorities = unsafe { PRIORITIES::new(raw_reg.as_mut_ptr() as _) }; 82 | 83 | for i in 1..=Interrupt::MAX_INTERRUPT_NUMBER { 84 | let source = Interrupt::from_number(i).unwrap(); 85 | for j in 0..=Priority::MAX_PRIORITY_NUMBER { 86 | let priority = Priority::from_number(j).unwrap(); 87 | unsafe { priorities.set_priority(source, priority) }; 88 | assert_eq!(priorities.get_priority::<_, Priority>(source), priority); 89 | } 90 | } 91 | priorities.reset::(); 92 | for i in 1..=Interrupt::MAX_INTERRUPT_NUMBER { 93 | let source = Interrupt::from_number(i).unwrap(); 94 | assert_eq!(priorities.get_priority::<_, Priority>(source), Priority::P0); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /riscv-peripheral/src/plic/threshold.rs: -------------------------------------------------------------------------------- 1 | //! Priority threshold register. 2 | 3 | use crate::{common::unsafe_peripheral, plic::PriorityNumber}; 4 | 5 | unsafe_peripheral!(THRESHOLD, u32, RW); 6 | 7 | impl THRESHOLD { 8 | /// Returns the priority threshold level. 9 | #[inline] 10 | pub fn get_threshold(self) -> P { 11 | P::from_number(self.register.read() as _).unwrap() 12 | } 13 | 14 | /// Sets the priority threshold level. 15 | /// 16 | /// # Safety 17 | /// 18 | /// Changing the priority threshold can break priority-based critical sections. 19 | #[inline] 20 | pub unsafe fn set_threshold(self, threshold: P) { 21 | self.register.write(threshold.number() as _) 22 | } 23 | 24 | /// Resets the priority threshold level to 0. 25 | /// 26 | /// # Note 27 | /// 28 | /// Threshold 0 implies that all interrupts are accepted. 29 | /// Thus, resetting the threshold is equivalent to accepting interrupts from any enabled interrupt source. 30 | #[inline] 31 | pub fn reset(self) { 32 | self.register.write(0) 33 | } 34 | } 35 | 36 | #[cfg(test)] 37 | mod test { 38 | use super::*; 39 | use crate::test::Priority; 40 | 41 | #[test] 42 | fn test_threshold() { 43 | let mut raw_reg = 0u32; 44 | // SAFETY: valid memory address 45 | let threshold = unsafe { THRESHOLD::new(&mut raw_reg as *mut _ as _) }; 46 | 47 | for i in 0..=Priority::MAX_PRIORITY_NUMBER { 48 | let priority = Priority::from_number(i).unwrap(); 49 | unsafe { threshold.set_threshold(priority) }; 50 | assert_eq!(threshold.get_threshold::(), priority); 51 | } 52 | threshold.reset(); 53 | assert_eq!(threshold.get_threshold::(), Priority::P0); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /riscv-rt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "riscv-rt" 3 | version = "0.14.0" 4 | rust-version = "1.61" 5 | repository = "https://github.com/rust-embedded/riscv" 6 | authors = ["The RISC-V Team "] 7 | categories = ["embedded", "no-std"] 8 | description = "Minimal runtime / startup for RISC-V CPU's" 9 | documentation = "https://docs.rs/riscv-rt" 10 | keywords = ["riscv", "runtime", "startup"] 11 | license = "ISC" 12 | edition = "2021" 13 | links = "riscv-rt" # Prevent multiple versions of riscv-rt being linked 14 | 15 | [package.metadata.docs.rs] 16 | default-target = "riscv64imac-unknown-none-elf" 17 | features = ["pre-init"] 18 | targets = [ 19 | "riscv32i-unknown-none-elf", "riscv32imc-unknown-none-elf", "riscv32imac-unknown-none-elf", 20 | "riscv64imac-unknown-none-elf", "riscv64gc-unknown-none-elf", 21 | ] 22 | 23 | [build-dependencies] 24 | riscv-target-parser = { path = "../riscv-target-parser", version = "0.1.0" } 25 | 26 | [dependencies] 27 | riscv = { path = "../riscv", version = "0.13.0" } 28 | riscv-pac = { path = "../riscv-pac", version = "0.2.0" } 29 | riscv-rt-macros = { path = "macros", version = "0.4.0" } 30 | 31 | [dev-dependencies] 32 | panic-halt = "1.0.0" 33 | 34 | [features] 35 | pre-init = [] 36 | s-mode = ["riscv-rt-macros/s-mode"] 37 | single-hart = [] 38 | v-trap = ["riscv-rt-macros/v-trap"] 39 | u-boot = ["riscv-rt-macros/u-boot", "single-hart"] 40 | no-interrupts = [] 41 | no-exceptions = [] 42 | device = [] 43 | memory = [] 44 | -------------------------------------------------------------------------------- /riscv-rt/README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/d/riscv-rt.svg)](https://crates.io/crates/riscv-rt) 2 | [![crates.io](https://img.shields.io/crates/v/riscv-rt.svg)](https://crates.io/crates/riscv-rt) 3 | 4 | # `riscv-rt` 5 | 6 | > Minimal runtime / startup for RISC-V CPU's. 7 | 8 | This project is developed and maintained by the [RISC-V team][team]. 9 | 10 | ## [Documentation](https://docs.rs/crate/riscv-rt) 11 | 12 | ## Minimum Supported Rust Version (MSRV) 13 | 14 | This crate is guaranteed to compile on stable Rust 1.61 and up. It *might* 15 | compile with older versions but that may change in any new patch release. 16 | 17 | ## License 18 | 19 | Copyright 2018-2022 [RISC-V team][team] 20 | 21 | Permission to use, copy, modify, and/or distribute this software for any purpose 22 | with or without fee is hereby granted, provided that the above copyright notice 23 | and this permission notice appear in all copies. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 26 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 27 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 28 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 29 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 30 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 31 | THIS SOFTWARE. 32 | 33 | ## Code of Conduct 34 | 35 | Contribution to this crate is organized under the terms of the [Rust Code of 36 | Conduct][CoC], the maintainer of this crate, the [RISC-V team][team], promises 37 | to intervene to uphold that code of conduct. 38 | 39 | [CoC]: CODE_OF_CONDUCT.md 40 | [team]: https://github.com/rust-embedded/wg#the-risc-v-team 41 | -------------------------------------------------------------------------------- /riscv-rt/build.rs: -------------------------------------------------------------------------------- 1 | // NOTE: Adapted from cortex-m/build.rs 2 | 3 | use riscv_target_parser::RiscvTarget; 4 | use std::{env, fs, io, path::PathBuf}; 5 | 6 | // List of all possible RISC-V configurations to check for in risv-rt 7 | const RISCV_CFG: [&str; 4] = ["riscvi", "riscvm", "riscvf", "riscvd"]; 8 | 9 | fn add_linker_script(arch_width: u32) -> io::Result<()> { 10 | // Read the file to a string and replace all occurrences of ${ARCH_WIDTH} with the arch width 11 | let mut content = fs::read_to_string("link.x.in")?; 12 | content = content.replace("${ARCH_WIDTH}", &arch_width.to_string()); 13 | 14 | // Get target-dependent linker configuration and replace ${INCLUDE_LINKER_FILES} with it 15 | let mut include_content = String::new(); 16 | 17 | // If no-exceptions is disabled, include the exceptions.x files 18 | if env::var_os("CARGO_FEATURE_NO_EXCEPTIONS").is_none() { 19 | let exceptions_content = fs::read_to_string("exceptions.x")?; 20 | include_content.push_str(&(exceptions_content + "\n")); 21 | } 22 | // If no-interrupts is disabled, include the interrupts.x files 23 | if env::var_os("CARGO_FEATURE_NO_INTERRUPTS").is_none() { 24 | let interrupts_content = fs::read_to_string("interrupts.x")?; 25 | include_content.push_str(&(interrupts_content + "\n")); 26 | } 27 | // If device is enabled, include the device.x file (usually, provided by PACs) 28 | if env::var_os("CARGO_FEATURE_DEVICE").is_some() { 29 | include_content.push_str("/* Device-specific exception and interrupt handlers */\n"); 30 | include_content.push_str("INCLUDE device.x\n"); 31 | } 32 | // If memory is enabled, include the memory.x file (usually, provided by BSPs) 33 | if env::var_os("CARGO_FEATURE_MEMORY").is_some() { 34 | include_content.push_str("/* Device-specific memory layout */\n"); 35 | include_content.push_str("INCLUDE memory.x\n"); 36 | } 37 | 38 | content = content.replace("${INCLUDE_LINKER_FILES}", &include_content); 39 | 40 | // Put the linker script somewhere the linker can find it 41 | let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); 42 | fs::write(out_dir.join("link.x"), content)?; 43 | println!("cargo:rustc-link-search={}", out_dir.display()); 44 | println!("cargo:rerun-if-changed=link.x"); 45 | 46 | Ok(()) 47 | } 48 | 49 | fn main() { 50 | // Required until target_feature risc-v is stable and in-use (rust 1.75) 51 | for ext in RISCV_CFG.iter() { 52 | println!("cargo:rustc-check-cfg=cfg({ext})"); 53 | } 54 | 55 | let target = env::var("TARGET").unwrap(); 56 | let cargo_flags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap(); 57 | 58 | if let Ok(target) = RiscvTarget::build(&target, &cargo_flags) { 59 | let width = target.width(); 60 | 61 | // set environmet variable RISCV_RT_BASE_ISA to the base ISA of the target. 62 | println!( 63 | "cargo:rustc-env=RISCV_RT_BASE_ISA={}", 64 | target.llvm_base_isa() 65 | ); 66 | // set environment variable RISCV_RT_LLVM_ARCH_PATCH to patch LLVM bug. 67 | // (this env variable is temporary and will be removed after LLVM being fixed) 68 | println!( 69 | "cargo:rustc-env=RISCV_RT_LLVM_ARCH_PATCH={}", 70 | target.llvm_arch_patch() 71 | ); 72 | // make sure that these env variables are not changed without notice. 73 | println!("cargo:rerun-if-env-changed=RISCV_RT_BASE_ISA"); 74 | println!("cargo:rerun-if-env-changed=RISCV_RT_LLVM_ARCH_PATCH"); 75 | if env::var_os("CARGO_FEATURE_V_TRAP").is_some() 76 | && env::var_os("CARGO_FEATURE_NO_INTERRUPTS").is_none() 77 | { 78 | // This environment variable is used by the `#[riscv::pac_enum()]` call in 79 | // `src/interrupts.rs` (when `v-trap` is enabled and `no-interrupts` disabled). 80 | println!("cargo:rerun-if-env-changed=RISCV_MTVEC_ALIGN"); 81 | } 82 | 83 | for flag in target.rustc_flags() { 84 | // Required until target_feature risc-v is stable and in-use 85 | if RISCV_CFG.contains(&flag.as_str()) { 86 | println!("cargo:rustc-cfg={flag}"); 87 | } 88 | } 89 | add_linker_script(width.into()).unwrap(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /riscv-rt/examples/device.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | RAM : ORIGIN = 0x80000000, LENGTH = 16K 4 | FLASH : ORIGIN = 0x20000000, LENGTH = 4M 5 | } 6 | 7 | REGION_ALIAS("REGION_TEXT", FLASH); 8 | REGION_ALIAS("REGION_RODATA", FLASH); 9 | REGION_ALIAS("REGION_DATA", RAM); 10 | REGION_ALIAS("REGION_BSS", RAM); 11 | REGION_ALIAS("REGION_HEAP", RAM); 12 | REGION_ALIAS("REGION_STACK", RAM); 13 | 14 | INCLUDE link.x 15 | -------------------------------------------------------------------------------- /riscv-rt/examples/empty.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate panic_halt; 5 | 6 | use riscv_rt::{core_interrupt, entry, exception, external_interrupt}; 7 | 8 | use riscv::{ 9 | interrupt::{Exception, Interrupt}, 10 | result::*, 11 | }; 12 | 13 | /// Just a dummy type to test the `ExternalInterrupt` trait. 14 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 15 | pub enum ExternalInterrupt { 16 | GPIO, 17 | UART, 18 | } 19 | 20 | unsafe impl riscv::InterruptNumber for ExternalInterrupt { 21 | const MAX_INTERRUPT_NUMBER: usize = 1; 22 | 23 | #[inline] 24 | fn number(self) -> usize { 25 | self as usize 26 | } 27 | 28 | #[inline] 29 | fn from_number(value: usize) -> Result { 30 | match value { 31 | 0 => Ok(Self::GPIO), 32 | 1 => Ok(Self::UART), 33 | _ => Err(Error::InvalidVariant(value)), 34 | } 35 | } 36 | } 37 | unsafe impl riscv::ExternalInterruptNumber for ExternalInterrupt {} 38 | 39 | #[entry] 40 | fn main() -> ! { 41 | // do something here 42 | loop {} 43 | } 44 | 45 | /* EXAMPLES OF USING THE core_interrupt MACRO FOR CORE INTERRUPT HANDLERS. 46 | IF v-trap ENABLED, THE MACRO ALSO DEFINES _start_COREINTERRUPT_trap routines */ 47 | 48 | /// Handler with the simplest signature. 49 | #[core_interrupt(Interrupt::SupervisorSoft)] 50 | fn supervisor_soft() { 51 | // do something here 52 | loop {} 53 | } 54 | 55 | /// Handler with the most complete signature. 56 | #[core_interrupt(Interrupt::SupervisorTimer)] 57 | unsafe fn supervisor_timer() -> ! { 58 | // do something here 59 | loop {} 60 | } 61 | 62 | /* EXAMPLES OF USING THE external_interrupt MACRO FOR EXTERNAL INTERRUPT HANDLERS. */ 63 | 64 | /// Handler with the simplest signature. 65 | #[external_interrupt(ExternalInterrupt::GPIO)] 66 | fn external_gpio() { 67 | // do something here 68 | loop {} 69 | } 70 | 71 | /// Handler with the most complete signature. 72 | #[external_interrupt(ExternalInterrupt::UART)] 73 | unsafe fn external_uart() -> ! { 74 | // do something here 75 | loop {} 76 | } 77 | 78 | /* EXAMPLES OF USING THE exception MACRO FOR EXCEPTION HANDLERS. */ 79 | 80 | /// Handler with the simplest signature. 81 | #[exception(Exception::InstructionMisaligned)] 82 | fn instruction_misaligned() { 83 | // do something here 84 | loop {} 85 | } 86 | 87 | /// Handler with the most complete signature. 88 | #[exception(Exception::IllegalInstruction)] 89 | unsafe fn illegal_instruction(_trap: &riscv_rt::TrapFrame) -> ! { 90 | // do something here 91 | loop {} 92 | } 93 | 94 | // The reference to TrapFrame can be mutable if the handler needs to modify it. 95 | #[exception(Exception::Breakpoint)] 96 | unsafe fn breakpoint(_trap: &mut riscv_rt::TrapFrame) -> ! { 97 | // do something here 98 | loop {} 99 | } 100 | -------------------------------------------------------------------------------- /riscv-rt/examples/multi_core.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate panic_halt; 5 | 6 | use riscv::asm::wfi; 7 | use riscv::register::{mie, mip}; 8 | use riscv_rt::entry; 9 | 10 | #[export_name = "_mp_hook"] 11 | #[rustfmt::skip] 12 | pub extern "Rust" fn user_mp_hook(hartid: usize) -> bool { 13 | if hartid == 0 { 14 | true 15 | } else { 16 | let addr = 0x02000000 + hartid * 4; 17 | unsafe { 18 | // Clear IPI 19 | (addr as *mut u32).write_volatile(0); 20 | 21 | // Start listening for software interrupts 22 | mie::set_msoft(); 23 | 24 | loop { 25 | wfi(); 26 | if mip::read().msoft() { 27 | break; 28 | } 29 | } 30 | 31 | // Stop listening for software interrupts 32 | mie::clear_msoft(); 33 | 34 | // Clear IPI 35 | (addr as *mut u32).write_volatile(0); 36 | } 37 | false 38 | } 39 | } 40 | 41 | #[entry] 42 | fn main(hartid: usize) -> ! { 43 | if hartid == 0 { 44 | // Waking hart 1... 45 | let addr = 0x02000004; 46 | unsafe { 47 | (addr as *mut u32).write_volatile(1); 48 | } 49 | } 50 | 51 | loop {} 52 | } 53 | -------------------------------------------------------------------------------- /riscv-rt/exceptions.x: -------------------------------------------------------------------------------- 1 | /* # EXCEPTION HANDLERS DESCRIBED IN THE STANDARD RISC-V ISA 2 | 3 | If the `no-exceptions` feature is DISABLED, this file will be included in link.x.in. 4 | If the `no-exceptions` feature is ENABLED, this file will be ignored. 5 | */ 6 | 7 | /* It is possible to define a special handler for each exception type. 8 | By default, all exceptions are handled by ExceptionHandler. However, 9 | users can override these alias by defining the symbol themselves */ 10 | PROVIDE(InstructionMisaligned = ExceptionHandler); 11 | PROVIDE(InstructionFault = ExceptionHandler); 12 | PROVIDE(IllegalInstruction = ExceptionHandler); 13 | PROVIDE(Breakpoint = ExceptionHandler); 14 | PROVIDE(LoadMisaligned = ExceptionHandler); 15 | PROVIDE(LoadFault = ExceptionHandler); 16 | PROVIDE(StoreMisaligned = ExceptionHandler); 17 | PROVIDE(StoreFault = ExceptionHandler); 18 | PROVIDE(UserEnvCall = ExceptionHandler); 19 | PROVIDE(SupervisorEnvCall = ExceptionHandler); 20 | PROVIDE(MachineEnvCall = ExceptionHandler); 21 | PROVIDE(InstructionPageFault = ExceptionHandler); 22 | PROVIDE(LoadPageFault = ExceptionHandler); 23 | PROVIDE(StorePageFault = ExceptionHandler); 24 | -------------------------------------------------------------------------------- /riscv-rt/interrupts.x: -------------------------------------------------------------------------------- 1 | /* # CORE INTERRUPT HANDLERS DESCRIBED IN THE STANDARD RISC-V ISA 2 | 3 | If the `no-interrupts` feature is DISABLED, this file will be included in link.x.in. 4 | If the `no-interrupts` feature is ENABLED, this file will be ignored. 5 | */ 6 | 7 | /* It is possible to define a special handler for each interrupt type. 8 | By default, all interrupts are handled by DefaultHandler. However, users can 9 | override these alias by defining the symbol themselves */ 10 | PROVIDE(SupervisorSoft = DefaultHandler); 11 | PROVIDE(MachineSoft = DefaultHandler); 12 | PROVIDE(SupervisorTimer = DefaultHandler); 13 | PROVIDE(MachineTimer = DefaultHandler); 14 | PROVIDE(SupervisorExternal = DefaultHandler); 15 | PROVIDE(MachineExternal = DefaultHandler); 16 | 17 | /* When vectored trap mode is enabled, each interrupt source must implement its own 18 | trap entry point. By default, all interrupts start in _DefaultHandler_trap. 19 | However, users can override these alias by defining the symbol themselves */ 20 | PROVIDE(_start_SupervisorSoft_trap = _start_DefaultHandler_trap); 21 | PROVIDE(_start_MachineSoft_trap = _start_DefaultHandler_trap); 22 | PROVIDE(_start_SupervisorTimer_trap = _start_DefaultHandler_trap); 23 | PROVIDE(_start_MachineTimer_trap = _start_DefaultHandler_trap); 24 | PROVIDE(_start_SupervisorExternal_trap = _start_DefaultHandler_trap); 25 | PROVIDE(_start_MachineExternal_trap = _start_DefaultHandler_trap); 26 | -------------------------------------------------------------------------------- /riscv-rt/macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = [ 3 | "The RISC-V Team ", 4 | "Jorge Aparicio ", 5 | ] 6 | categories = ["embedded", "no-std"] 7 | description = "Attributes re-exported in `riscv-rt`" 8 | documentation = "https://docs.rs/riscv-rt" 9 | keywords = ["riscv", "runtime", "startup"] 10 | license = "MIT OR Apache-2.0" 11 | name = "riscv-rt-macros" 12 | repository = "https://github.com/rust-embedded/riscv" 13 | version = "0.4.0" 14 | edition = "2021" 15 | 16 | [lib] 17 | proc-macro = true 18 | 19 | [dependencies] 20 | quote = "1.0" 21 | proc-macro2 = "1.0" 22 | syn = { version = "2.0", features = ["extra-traits", "full"] } 23 | 24 | [features] 25 | s-mode = [] 26 | v-trap = [] 27 | u-boot = [] 28 | -------------------------------------------------------------------------------- /riscv-rt/src/exceptions.rs: -------------------------------------------------------------------------------- 1 | //! Exception handling for targets that comply with the RISC-V exception handling standard. 2 | //! 3 | //! Exception dispatching is performed by the [`_dispatch_exception`] function. 4 | //! This function is called by the [crate::start_trap_rust] whenever an exception is triggered. 5 | //! This approach relies on the [`__EXCEPTIONS`] array, which sorts all the exception handlers 6 | //! depending on their corresponding exception source code. 7 | //! 8 | //! # Note 9 | //! 10 | //! If your target has custom exception sources, the target PAC might provide equivalent 11 | //! code to adapt for the target needs. In this case, you may need to opt out this module. 12 | //! To do so, activate the `no-exceptions` feature of the `riscv-rt` crate. 13 | 14 | use crate::TrapFrame; 15 | 16 | extern "C" { 17 | fn InstructionMisaligned(trap_frame: &TrapFrame); 18 | fn InstructionFault(trap_frame: &TrapFrame); 19 | fn IllegalInstruction(trap_frame: &TrapFrame); 20 | fn Breakpoint(trap_frame: &TrapFrame); 21 | fn LoadMisaligned(trap_frame: &TrapFrame); 22 | fn LoadFault(trap_frame: &TrapFrame); 23 | fn StoreMisaligned(trap_frame: &TrapFrame); 24 | fn StoreFault(trap_frame: &TrapFrame); 25 | fn UserEnvCall(trap_frame: &TrapFrame); 26 | fn SupervisorEnvCall(trap_frame: &TrapFrame); 27 | fn MachineEnvCall(trap_frame: &TrapFrame); 28 | fn InstructionPageFault(trap_frame: &TrapFrame); 29 | fn LoadPageFault(trap_frame: &TrapFrame); 30 | fn StorePageFault(trap_frame: &TrapFrame); 31 | } 32 | 33 | /// Array with all the exception handlers sorted according to their exception source code. 34 | #[no_mangle] 35 | pub static __EXCEPTIONS: [Option; 16] = [ 36 | Some(InstructionMisaligned), 37 | Some(InstructionFault), 38 | Some(IllegalInstruction), 39 | Some(Breakpoint), 40 | Some(LoadMisaligned), 41 | Some(LoadFault), 42 | Some(StoreMisaligned), 43 | Some(StoreFault), 44 | Some(UserEnvCall), 45 | Some(SupervisorEnvCall), 46 | None, 47 | Some(MachineEnvCall), 48 | Some(InstructionPageFault), 49 | Some(LoadPageFault), 50 | None, 51 | Some(StorePageFault), 52 | ]; 53 | 54 | /// It calls the corresponding exception handler depending on the exception source code. 55 | /// 56 | /// # Safety 57 | /// 58 | /// This function must be called only from the [`crate::start_trap_rust`] function. 59 | /// Do **NOT** call this function directly. 60 | #[no_mangle] 61 | pub unsafe extern "C" fn _dispatch_exception(trap_frame: &TrapFrame, code: usize) { 62 | extern "C" { 63 | fn ExceptionHandler(trap_frame: &TrapFrame); 64 | } 65 | match __EXCEPTIONS.get(code) { 66 | Some(Some(handler)) => handler(trap_frame), 67 | _ => ExceptionHandler(trap_frame), 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /riscv-rt/src/interrupts.rs: -------------------------------------------------------------------------------- 1 | //! Interrupt handling for targets that comply with the RISC-V interrupt handling standard. 2 | //! 3 | //! In direct mode (i.e., `v-trap` feature disabled), interrupt dispatching is performed by the 4 | //! `_dispatch_core_interrupt` function. This function is called by the [crate::start_trap_rust] 5 | //! whenever an interrupt is triggered. This approach relies on the [`__CORE_INTERRUPTS`] array, 6 | //! which sorts all the interrupt handlers depending on their corresponding interrupt source code. 7 | //! 8 | //! In vectored mode (i.e., `v-trap` feature enabled), interrupt dispatching is handled by hardware. 9 | //! To support this mode, we provide inline assembly code that defines the interrupt vector table. 10 | //! Since the alignment constraint of this vector table is implementation-specific, it can be 11 | //! changed by setting the `RISCV_MTVEC_ALIGN` environment variable (the default is 4). 12 | //! 13 | //! # Note 14 | //! 15 | //! If your target has custom core interrupt sources, the target PAC might provide equivalent code 16 | //! to adapt for the target needs (and is responsible for any alignment constraint). In this case, 17 | //! you may need to opt out this module. To do so, activate the `no-interrupts` feature of the 18 | //! `riscv-rt` crate. 19 | 20 | // In vectored mode, we also must provide a vector table 21 | #[riscv::pac_enum(unsafe CoreInterruptNumber)] 22 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 23 | enum Interrupt { 24 | SupervisorSoft = 1, 25 | MachineSoft = 3, 26 | SupervisorTimer = 5, 27 | MachineTimer = 7, 28 | SupervisorExternal = 9, 29 | MachineExternal = 11, 30 | } 31 | -------------------------------------------------------------------------------- /riscv-semihosting/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | This project adheres to [Semantic Versioning](http://semver.org/). 5 | 6 | ## [Unreleased] 7 | 8 | ## [v0.1.3] - 2025-02-18 9 | 10 | ### Changed 11 | 12 | - Update riscv to 0.13.0 13 | 14 | ## [v0.1.2] - 2024-10-20 15 | 16 | ### Changed 17 | 18 | - Update critical-section to 1.2.0 19 | 20 | ## [v0.1.1] - 2024-10-19 21 | 22 | ### Changed 23 | 24 | - Apply clippy changes 25 | - Bump riscv dependency version 26 | - Made `cfg` variable selection more robust for custom targets 27 | - Fixed debug::exit() on riscv64 QEMU simulation 28 | - Fixed an ambiguous link in the generated crate documentation. 29 | 30 | ## [v0.1.0] - 2023-01-18 31 | 32 | - Add recommendation for `semihosting` in README.md. 33 | - Bug fixes 34 | - Moved to the `riscv` Cargo workspace 35 | - Bring in API changes from 36 | [cortex-m-semihosting](https://github.com/rust-embedded/cortex-m/tree/master/cortex-m-semihosting), 37 | including: 38 | - Addition of the `hprint`, `hprintln`, `heprint`, `heprintln`, and `dbg` 39 | macros. 40 | - `hprint` and `heprintln` print to host stdout without and with a 41 | newline, respectively. 42 | - `heprint` and `heprintln` do the same, except to host stderr. 43 | - `dbg` works exactly like 44 | [`std::dbg`](https://doc.rust-lang.org/std/macro.dbg.html). 45 | - `HStdout` and `HStderr` have been combined into `HostStream`. 46 | - `inline-asm` feature removed, switched to stabilized inline asm and MSRV 47 | bumped to 1.59.0 48 | - Clean up documentation, removing unnecessary references to 49 | cortex-m-semihosting and improving clarity. 50 | - Added GitHub Actions CI 51 | - Add features to select the privilege level the semihosting operations will be 52 | started from 53 | 54 | ## [v0.0.1] - 2018-02-27 55 | 56 | - Initial release 57 | 58 | [Unreleased]: https://github.com/riscv-rust/riscv-semihosting/compare/cb1afe4002d576b87bfd4c199f42a43815984ce4..HEAD 59 | [v0.0.1]: https://github.com/riscv-rust/riscv-semihosting/tree/cb1afe4002d576b87bfd4c199f42a43815984ce4 60 | -------------------------------------------------------------------------------- /riscv-semihosting/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = [ 3 | "The Cortex-M Team ", 4 | "Jorge Aparicio ", 5 | "The RISC-V Team ", 6 | ] 7 | description = "Semihosting for RISCV processors" 8 | documentation = "https://docs.rs/riscv-semihosting" 9 | keywords = ["semihosting", "riscv"] 10 | categories = ["no-std", "embedded"] 11 | license = "MIT OR Apache-2.0" 12 | name = "riscv-semihosting" 13 | readme = "README.md" 14 | repository = "https://github.com/riscv-rust/riscv" 15 | version = "0.1.3" 16 | edition = "2021" 17 | rust-version = "1.60.0" 18 | 19 | [features] 20 | u-mode = [] 21 | jlink-quirks = [] 22 | no-semihosting = [] 23 | default = ["jlink-quirks"] 24 | 25 | [dependencies] 26 | critical-section = "1.2.0" 27 | riscv = { path = "../riscv", version = "0.13.0" } 28 | -------------------------------------------------------------------------------- /riscv-semihosting/README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/d/riscv-semihosting.svg)](https://crates.io/crates/riscv-semihosting) 2 | [![crates.io](https://img.shields.io/crates/v/riscv-semihosting.svg)](https://crates.io/crates/riscv-semihosting) 3 | 4 | # `riscv-semihosting` 5 | 6 | > Simple semihosting for RISC-V processors 7 | 8 | This is a fork of the 9 | [`cortex-m-semihosting`] crate with changes 10 | to support the RISC-V Semihosting Specification as documented 11 | [here](https://github.com/riscv/riscv-semihosting-spec/blob/main/riscv-semihosting-spec.adoc) 12 | 13 | This crate can (almost) be used in exactly the same way as cortex-m-semihosting, 14 | simply by changing calls to `cortex_m_semihosting::*` to `riscv_semihosting::*`. 15 | Given this, the 16 | [`cortex-m-semihosting documentation`](https://docs.rs/cortex-m-semihosting) is 17 | generally sufficient for using this library. 18 | 19 | A major difference between this library and `cortex-m-semihosting` is that there 20 | are features to choose the privilege level at which the semihosting 21 | calls are executed. The *machine-mode (M-mode)* feature will cause the macros in `export` 22 | to execute the semihosting operation in an interrupt-free context, while 23 | *user-mode (U-mode)* causes them to just execute the operation. 24 | By default, M-mode is used. You can activate the U-mode via the `u-mode` feature. 25 | 26 | # About the [`semihosting`] crate 27 | 28 | `riscv-semihosting` provides a simple semihosting API that matches [`cortex-m-semihosting`]. 29 | This allows a simple port from Cortex-M applications to RISC-V applications. 30 | However, the [`semihosting`] crate presents a more advanced interface that is compatible 31 | for RISC-V as well as other architectures (e.g., ARM or MIPS). 32 | While `riscv-semihosting` is a good starting point for developing semihosted applications, 33 | **we recommend using the [`semihosting`] crate.** 34 | 35 | 36 | # Minimum Supported Rust Version (MSRV) 37 | 38 | This crate is guaranteed to compile on stable Rust 1.60.0 and up. It **won't** 39 | compile with older versions. 40 | 41 | ## License 42 | 43 | Copyright 2018-2023 [RISC-V team][team] 44 | 45 | Permission to use, copy, modify, and/or distribute this software for any purpose 46 | with or without fee is hereby granted, provided that the above copyright notice 47 | and this permission notice appear in all copies. 48 | 49 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 50 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 51 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 52 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 53 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 54 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 55 | THIS SOFTWARE. 56 | 57 | ### Contribution 58 | 59 | Unless you explicitly state otherwise, any contribution intentionally submitted 60 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 61 | dual licensed as above, without any additional terms or conditions. 62 | 63 | ## Code of Conduct 64 | 65 | Contribution to this crate is organized under the terms of the [Rust Code of 66 | Conduct][CoC], the maintainer of this crate, the [RISC-V team][team], promises 67 | to intervene to uphold that code of conduct. 68 | 69 | [CoC]: ../CODE_OF_CONDUCT.md 70 | [team]: https://github.com/rust-embedded/wg#the-risc-v-team 71 | [`semihosting`]: https://crates.io/crates/semihosting 72 | [`cortex-m-semihosting`]: https://docs.rs/cortex-m-semihosting -------------------------------------------------------------------------------- /riscv-semihosting/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | fn main() { 4 | println!("cargo:rustc-check-cfg=cfg(riscv)"); 5 | 6 | let target = env::var("TARGET").unwrap(); 7 | 8 | if target.starts_with("riscv") { 9 | println!("cargo:rustc-cfg=riscv"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /riscv-semihosting/src/debug.rs: -------------------------------------------------------------------------------- 1 | //! Interacting with debugging agent 2 | //! 3 | //! # Example 4 | //! 5 | //! This example will show how to terminate the QEMU session. The program 6 | //! should be running under QEMU with semihosting enabled 7 | //! (use `-semihosting` flag). 8 | //! 9 | //! Target program: 10 | //! 11 | //! ```no_run 12 | //! use riscv_semihosting::debug::{self, EXIT_SUCCESS, EXIT_FAILURE}; 13 | //! 14 | //! if 2 == 2 { 15 | //! // report success 16 | //! debug::exit(EXIT_SUCCESS); 17 | //! } else { 18 | //! // report failure 19 | //! debug::exit(EXIT_FAILURE); 20 | //! } 21 | //!``` 22 | 23 | /// This values are taken from section 5.5.2 of 24 | /// ADS Debug Target Guide (DUI0058). 25 | // TODO document 26 | #[allow(missing_docs)] 27 | pub enum Exception { 28 | // Hardware reason codes 29 | BranchThroughZero = 0x20000, 30 | UndefinedInstr = 0x20001, 31 | SoftwareInterrupt = 0x20002, 32 | PrefetchAbort = 0x20003, 33 | DataAbort = 0x20004, 34 | AddressException = 0x20005, 35 | IRQ = 0x20006, 36 | FIQ = 0x20007, 37 | // Software reason codes 38 | BreakPoint = 0x20020, 39 | WatchPoint = 0x20021, 40 | StepComplete = 0x20022, 41 | RunTimeErrorUnknown = 0x20023, 42 | InternalError = 0x20024, 43 | UserInterruption = 0x20025, 44 | ApplicationExit = 0x20026, 45 | StackOverflow = 0x20027, 46 | DivisionByZero = 0x20028, 47 | OSSpecific = 0x20029, 48 | } 49 | 50 | /// Status enum for `exit` syscall. 51 | pub type ExitStatus = Result<(), ()>; 52 | 53 | /// Successful execution of a program. 54 | pub const EXIT_SUCCESS: ExitStatus = Ok(()); 55 | 56 | /// Unsuccessful execution of a program. 57 | pub const EXIT_FAILURE: ExitStatus = Err(()); 58 | 59 | /// Reports to the debugger that the execution has completed. 60 | /// 61 | /// This call can be used to terminate QEMU session and report back success 62 | /// or failure. If you need to pass more than one type of error, consider 63 | /// using `report_exception` syscall instead. 64 | /// 65 | /// This call should not return. However, it is possible for the debugger 66 | /// to request that the application continue. In that case this call 67 | /// returns normally. 68 | /// 69 | pub fn exit(status: ExitStatus) { 70 | match status { 71 | EXIT_SUCCESS => report_exception(Exception::ApplicationExit), 72 | EXIT_FAILURE => report_exception(Exception::RunTimeErrorUnknown), 73 | } 74 | } 75 | 76 | /// Report an exception to the debugger directly. 77 | /// 78 | /// Exception handlers can use this SWI at the end of handler chains 79 | /// as the default action, to indicate that the exception has not been handled. 80 | /// 81 | /// This call should not return. However, it is possible for the debugger 82 | /// to request that the application continue. In that case this call 83 | /// returns normally. 84 | /// 85 | /// # Arguments 86 | /// 87 | /// * `reason` - A reason code reported back to the debugger. 88 | /// 89 | pub fn report_exception(reason: Exception) { 90 | let code = reason as usize; 91 | unsafe { 92 | #[cfg(target_arch = "riscv64")] 93 | syscall!(REPORT_EXCEPTION, code, 0); 94 | 95 | #[cfg(not(target_arch = "riscv64"))] 96 | syscall1!(REPORT_EXCEPTION, code); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /riscv-semihosting/src/export.rs: -------------------------------------------------------------------------------- 1 | //! IMPLEMENTATION DETAILS USED BY MACROS 2 | use crate::hio::{self, HostStream}; 3 | use core::{ 4 | fmt::{self, Write}, 5 | ptr::addr_of_mut, 6 | }; 7 | 8 | static mut HSTDOUT: Option = None; 9 | static mut HSTDERR: Option = None; 10 | 11 | #[cfg(not(feature = "u-mode"))] 12 | mod machine { 13 | use super::*; 14 | 15 | pub fn hstdout_str(s: &str) { 16 | let _result = critical_section::with(|_| { 17 | let hstdout = unsafe { &mut *addr_of_mut!(HSTDOUT) }; 18 | if hstdout.is_none() { 19 | *hstdout = Some(hio::hstdout()?); 20 | } 21 | 22 | hstdout.as_mut().unwrap().write_str(s).map_err(drop) 23 | }); 24 | } 25 | 26 | pub fn hstdout_fmt(args: fmt::Arguments) { 27 | let _result = critical_section::with(|_| { 28 | let hstdout = unsafe { &mut *addr_of_mut!(HSTDOUT) }; 29 | if hstdout.is_none() { 30 | *hstdout = Some(hio::hstdout()?); 31 | } 32 | 33 | hstdout.as_mut().unwrap().write_fmt(args).map_err(drop) 34 | }); 35 | } 36 | 37 | pub fn hstderr_str(s: &str) { 38 | let _result = critical_section::with(|_| { 39 | let hstderr = unsafe { &mut *addr_of_mut!(HSTDERR) }; 40 | if hstderr.is_none() { 41 | *hstderr = Some(hio::hstderr()?); 42 | } 43 | 44 | hstderr.as_mut().unwrap().write_str(s).map_err(drop) 45 | }); 46 | } 47 | 48 | pub fn hstderr_fmt(args: fmt::Arguments) { 49 | let _result = critical_section::with(|_| { 50 | let hstderr = unsafe { &mut *addr_of_mut!(HSTDERR) }; 51 | if hstderr.is_none() { 52 | *hstderr = Some(hio::hstderr()?); 53 | } 54 | 55 | hstderr.as_mut().unwrap().write_fmt(args).map_err(drop) 56 | }); 57 | } 58 | } 59 | #[cfg(not(feature = "u-mode"))] 60 | pub use machine::*; 61 | 62 | #[cfg(feature = "u-mode")] 63 | mod user { 64 | use super::*; 65 | 66 | pub fn hstdout_str(s: &str) { 67 | let _result = unsafe { 68 | let hstdout = &mut *addr_of_mut!(HSTDOUT); 69 | if hstdout.is_none() { 70 | *hstdout = Some(hio::hstdout().unwrap()); 71 | } 72 | 73 | hstdout.as_mut().unwrap().write_str(s).map_err(drop) 74 | }; 75 | } 76 | 77 | pub fn hstdout_fmt(args: fmt::Arguments) { 78 | let _result = unsafe { 79 | let hstdout = &mut *addr_of_mut!(HSTDOUT); 80 | if hstdout.is_none() { 81 | *hstdout = Some(hio::hstdout().unwrap()); 82 | } 83 | 84 | hstdout.as_mut().unwrap().write_fmt(args).map_err(drop) 85 | }; 86 | } 87 | 88 | pub fn hstderr_str(s: &str) { 89 | let _result = unsafe { 90 | let hstderr = &mut *addr_of_mut!(HSTDERR); 91 | if hstderr.is_none() { 92 | *hstderr = Some(hio::hstderr().unwrap()); 93 | } 94 | 95 | hstderr.as_mut().unwrap().write_str(s).map_err(drop) 96 | }; 97 | } 98 | 99 | pub fn hstderr_fmt(args: fmt::Arguments) { 100 | let _result = unsafe { 101 | let hstderr = &mut *addr_of_mut!(HSTDERR); 102 | if hstderr.is_none() { 103 | *hstderr = Some(hio::hstderr().unwrap()); 104 | } 105 | 106 | hstderr.as_mut().unwrap().write_fmt(args).map_err(drop) 107 | }; 108 | } 109 | } 110 | #[cfg(feature = "u-mode")] 111 | pub use user::*; 112 | -------------------------------------------------------------------------------- /riscv-semihosting/src/hio.rs: -------------------------------------------------------------------------------- 1 | //! Host I/O 2 | 3 | // Fixing this lint requires a breaking change that does not add much value 4 | #![allow(clippy::result_unit_err)] 5 | 6 | use crate::nr; 7 | use core::{fmt, slice}; 8 | 9 | /// A byte stream to the host (e.g., host's stdout or stderr). 10 | #[derive(Clone, Copy)] 11 | pub struct HostStream { 12 | fd: usize, 13 | } 14 | 15 | impl HostStream { 16 | /// Attempts to write an entire `buffer` into this sink 17 | pub fn write_all(&mut self, buffer: &[u8]) -> Result<(), ()> { 18 | write_all(self.fd, buffer) 19 | } 20 | } 21 | 22 | impl fmt::Write for HostStream { 23 | fn write_str(&mut self, s: &str) -> fmt::Result { 24 | self.write_all(s.as_bytes()).map_err(|_| fmt::Error) 25 | } 26 | } 27 | 28 | /// Construct a new handle to the host's standard error. 29 | pub fn hstderr() -> Result { 30 | // There is actually no stderr access in ARM Semihosting documentation. Use 31 | // convention used in libgloss. 32 | // See: libgloss/arm/syscalls.c, line 139. 33 | // https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=libgloss/arm/syscalls.c#l139 34 | open(":tt\0", nr::open::W_APPEND) 35 | } 36 | 37 | /// Construct a new handle to the host's standard output. 38 | pub fn hstdout() -> Result { 39 | open(":tt\0", nr::open::W_TRUNC) 40 | } 41 | 42 | fn open(name: &str, mode: usize) -> Result { 43 | let name = name.as_bytes(); 44 | match unsafe { syscall!(OPEN, name.as_ptr(), mode, name.len() - 1) } as isize { 45 | -1 => Err(()), 46 | fd => Ok(HostStream { fd: fd as usize }), 47 | } 48 | } 49 | 50 | fn write_all(fd: usize, mut buffer: &[u8]) -> Result<(), ()> { 51 | while !buffer.is_empty() { 52 | match unsafe { syscall!(WRITE, fd, buffer.as_ptr(), buffer.len()) } { 53 | // Done 54 | 0 => return Ok(()), 55 | // `n` bytes were not written 56 | n if n <= buffer.len() => { 57 | let offset = (buffer.len() - n) as isize; 58 | buffer = unsafe { slice::from_raw_parts(buffer.as_ptr().offset(offset), n) } 59 | } 60 | #[cfg(feature = "jlink-quirks")] 61 | // Error (-1) - should be an error but JLink can return -1, -2, -3,... 62 | // For good measure, we allow up to negative 15. 63 | n if n > 0xfffffff0 => return Ok(()), 64 | // Error 65 | _ => return Err(()), 66 | } 67 | } 68 | Ok(()) 69 | } 70 | -------------------------------------------------------------------------------- /riscv-semihosting/src/macros.rs: -------------------------------------------------------------------------------- 1 | /// Variable argument version of `syscall` 2 | #[macro_export] 3 | macro_rules! syscall { 4 | ($nr:ident) => { 5 | $crate::syscall1($crate::nr::$nr, 0) 6 | }; 7 | ($nr:ident, $a1:expr) => { 8 | $crate::syscall($crate::nr::$nr, &[$a1 as usize]) 9 | }; 10 | ($nr:ident, $a1:expr, $a2:expr) => { 11 | $crate::syscall($crate::nr::$nr, &[$a1 as usize, $a2 as usize]) 12 | }; 13 | ($nr:ident, $a1:expr, $a2:expr, $a3:expr) => { 14 | $crate::syscall($crate::nr::$nr, &[$a1 as usize, $a2 as usize, $a3 as usize]) 15 | }; 16 | ($nr:ident, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => { 17 | $crate::syscall( 18 | $crate::nr::$nr, 19 | &[$a1 as usize, $a2 as usize, $a3 as usize, $a4 as usize], 20 | ) 21 | }; 22 | } 23 | 24 | /// Macro version of `syscall1`. 25 | #[macro_export] 26 | macro_rules! syscall1 { 27 | ($nr:ident, $a1:expr) => { 28 | $crate::syscall1($crate::nr::$nr, $a1 as usize) 29 | }; 30 | } 31 | 32 | /// Macro for printing to the HOST standard output. 33 | /// 34 | /// This is similar to the `print!` macro in the standard library. Both will panic on any failure to 35 | /// print. 36 | #[macro_export] 37 | macro_rules! hprint { 38 | ($s:expr) => { 39 | $crate::export::hstdout_str($s) 40 | }; 41 | ($($tt:tt)*) => { 42 | $crate::export::hstdout_fmt(format_args!($($tt)*)) 43 | }; 44 | } 45 | 46 | /// Macro for printing to the HOST standard output, with a newline. 47 | /// 48 | /// This is similar to the `println!` macro in the standard library. Both will panic on any failure to 49 | /// print. 50 | #[macro_export] 51 | macro_rules! hprintln { 52 | () => { 53 | $crate::export::hstdout_str("\n") 54 | }; 55 | ($s:expr) => { 56 | $crate::export::hstdout_str(concat!($s, "\n")) 57 | }; 58 | ($s:expr, $($tt:tt)*) => { 59 | $crate::export::hstdout_fmt(format_args!(concat!($s, "\n"), $($tt)*)) 60 | }; 61 | } 62 | 63 | /// Macro for printing to the HOST standard error. 64 | /// 65 | /// This is similar to the `eprint!` macro in the standard library. Both will panic on any failure 66 | /// to print. 67 | #[macro_export] 68 | macro_rules! heprint { 69 | ($s:expr) => { 70 | $crate::export::hstderr_str($s) 71 | }; 72 | ($($tt:tt)*) => { 73 | $crate::export::hstderr_fmt(format_args!($($tt)*)) 74 | }; 75 | } 76 | 77 | /// Macro for printing to the HOST standard error, with a newline. 78 | /// 79 | /// This is similar to the `eprintln!` macro in the standard library. Both will panic on any failure 80 | /// to print. 81 | #[macro_export] 82 | macro_rules! heprintln { 83 | () => { 84 | $crate::export::hstderr_str("\n") 85 | }; 86 | ($s:expr) => { 87 | $crate::export::hstderr_str(concat!($s, "\n")) 88 | }; 89 | ($s:expr, $($tt:tt)*) => { 90 | $crate::export::hstderr_fmt(format_args!(concat!($s, "\n"), $($tt)*)) 91 | }; 92 | } 93 | 94 | /// Macro that prints and returns the value of a given expression for quick and 95 | /// dirty debugging. 96 | /// 97 | /// Works exactly like `dbg!` in the standard library, replacing `eprintln!` 98 | /// with `heprintln!`. 99 | #[macro_export] 100 | macro_rules! dbg { 101 | () => { 102 | $crate::heprintln!("[{}:{}]", file!(), line!()); 103 | }; 104 | ($val:expr) => { 105 | // Use of `match` here is intentional because it affects the lifetimes 106 | // of temporaries - https://stackoverflow.com/a/48732525/1063961 107 | match $val { 108 | tmp => { 109 | $crate::heprintln!("[{}:{}] {} = {:#?}", 110 | file!(), line!(), stringify!($val), &tmp); 111 | tmp 112 | } 113 | } 114 | }; 115 | // Trailing comma with single argument is ignored 116 | ($val:expr,) => { $crate::dbg!($val) }; 117 | ($($val:expr),+ $(,)?) => { 118 | ($($crate::dbg!($val)),+,) 119 | }; 120 | } 121 | -------------------------------------------------------------------------------- /riscv-semihosting/src/nr.rs: -------------------------------------------------------------------------------- 1 | //! Semihosting operations 2 | //! 3 | //! The details of what each operation does can be found in the 4 | //! [ARM Semihosting Specification](https://github.com/ARM-software/abi-aa/blob/main/semihosting/semihosting.rst#semihosting-operations). 5 | //! The RISC-V Semihosting operations are identiacal to ARM's, so their 6 | //! documentation is sufficient. 7 | 8 | #![allow(missing_docs)] 9 | 10 | pub const CLOCK: usize = 0x10; 11 | pub const CLOSE: usize = 0x02; 12 | pub const ELAPSED: usize = 0x30; 13 | pub const ERRNO: usize = 0x13; 14 | pub const FLEN: usize = 0x0c; 15 | pub const GET_CMDLINE: usize = 0x15; 16 | pub const HEAPINFO: usize = 0x16; 17 | pub const ISERROR: usize = 0x08; 18 | pub const ISTTY: usize = 0x09; 19 | pub const OPEN: usize = 0x01; 20 | pub const READ: usize = 0x06; 21 | pub const READC: usize = 0x07; 22 | pub const REMOVE: usize = 0x0e; 23 | pub const RENAME: usize = 0x0f; 24 | pub const SEEK: usize = 0x0a; 25 | pub const SYSTEM: usize = 0x12; 26 | pub const TICKFREQ: usize = 0x31; 27 | pub const TIME: usize = 0x11; 28 | pub const TMPNAM: usize = 0x0d; 29 | pub const WRITE0: usize = 0x04; 30 | pub const WRITE: usize = 0x05; 31 | pub const WRITEC: usize = 0x03; 32 | pub const ENTER_SVC: usize = 0x17; 33 | pub const REPORT_EXCEPTION: usize = 0x18; 34 | 35 | /// Values for the mode parameter of the OPEN syscall. 36 | pub mod open { 37 | /// Mode corresponding to fopen "r" mode. 38 | pub const R: usize = 0; 39 | /// Mode corresponding to fopen "rb" mode. 40 | pub const R_BINARY: usize = 1; 41 | /// Mode corresponding to fopen "r+" mode. 42 | pub const RW: usize = 2; 43 | /// Mode corresponding to fopen "r+b" mode. 44 | pub const RW_BINARY: usize = 3; 45 | /// Mode corresponding to fopen "w" mode. 46 | pub const W_TRUNC: usize = 4; 47 | /// Mode corresponding to fopen "wb" mode. 48 | pub const W_TRUNC_BINARY: usize = 5; 49 | /// Mode corresponding to fopen "w+" mode. 50 | pub const RW_TRUNC: usize = 6; 51 | /// Mode corresponding to fopen "w+b" mode. 52 | pub const RW_TRUNC_BINARY: usize = 7; 53 | /// Mode corresponding to fopen "a" mode. 54 | pub const W_APPEND: usize = 8; 55 | /// Mode corresponding to fopen "ab" mode. 56 | pub const W_APPEND_BINARY: usize = 9; 57 | /// Mode corresponding to fopen "a+" mode. 58 | pub const RW_APPEND: usize = 10; 59 | /// Mode corresponding to fopen "a+b" mode. 60 | pub const RW_APPEND_BINARY: usize = 11; 61 | } 62 | -------------------------------------------------------------------------------- /riscv-target-parser/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | This project adheres to [Semantic Versioning](http://semver.org/). 5 | 6 | ## [Unreleased] 7 | 8 | ### Fixed 9 | 10 | - Fix links to URLs in docs 11 | - `clippy` fixes 12 | 13 | ## [v0.1.1] - 2025-03-18 14 | 15 | ### Fixed 16 | 17 | - Fix parsing of extensions starting with 's'/'x'/'z' from target features 18 | - Updated the test cases accordingly 19 | 20 | ## [v0.1.0] - 2025-02-18 21 | 22 | ### Added 23 | 24 | - First release. 25 | 26 | -------------------------------------------------------------------------------- /riscv-target-parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "riscv-target-parser" 3 | version = "0.1.1" 4 | rust-version = "1.61" 5 | repository = "https://github.com/rust-embedded/riscv" 6 | authors = ["The RISC-V Team "] 7 | categories = ["embedded", "no-std"] 8 | description = "Parser for RISC-V target specifications" 9 | documentation = "https://docs.rs/riscv-target-parser" 10 | keywords = ["riscv", "build"] 11 | license = "ISC" 12 | edition = "2021" 13 | -------------------------------------------------------------------------------- /riscv-target-parser/README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/d/riscv-target-parser.svg)](https://crates.io/crates/riscv-target-parser) 2 | [![crates.io](https://img.shields.io/crates/v/riscv-target-parser.svg)](https://crates.io/crates/riscv-target-parser) 3 | 4 | # `riscv-target-parser` 5 | 6 | > Utility crate for parsing RISC-V targets in build scripts 7 | 8 | This project is developed and maintained by the [RISC-V team][team]. 9 | 10 | ## [Documentation](https://docs.rs/crate/riscv-target-parser) 11 | 12 | ## Minimum Supported Rust Version (MSRV) 13 | 14 | This crate is guaranteed to compile on stable Rust 1.61 and up. It *might* 15 | compile with older versions but that may change in any new patch release. 16 | 17 | ## License 18 | 19 | Copyright 2024-2025 [RISC-V team][team] 20 | 21 | Permission to use, copy, modify, and/or distribute this software for any purpose 22 | with or without fee is hereby granted, provided that the above copyright notice 23 | and this permission notice appear in all copies. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 26 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 27 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 28 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 29 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 30 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 31 | THIS SOFTWARE. 32 | 33 | ## Code of Conduct 34 | 35 | Contribution to this crate is organized under the terms of the [Rust Code of 36 | Conduct][CoC], the maintainer of this crate, the [RISC-V team][team], promises 37 | to intervene to uphold that code of conduct. 38 | 39 | [CoC]: CODE_OF_CONDUCT.md 40 | [team]: https://github.com/rust-embedded/wg#the-risc-v-team 41 | -------------------------------------------------------------------------------- /riscv/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "riscv" 3 | version = "0.13.0" 4 | edition = "2021" 5 | rust-version = "1.67" 6 | repository = "https://github.com/rust-embedded/riscv" 7 | authors = ["The RISC-V Team "] 8 | categories = ["embedded", "hardware-support", "no-std"] 9 | description = "Low level access to RISC-V processors" 10 | documentation = "https://docs.rs/riscv" 11 | keywords = ["riscv", "register", "peripheral"] 12 | license = "ISC" 13 | 14 | [package.metadata.docs.rs] 15 | all-features = true 16 | default-target = "riscv64imac-unknown-none-elf" 17 | targets = [ 18 | "riscv32i-unknown-none-elf", "riscv32imc-unknown-none-elf", "riscv32imac-unknown-none-elf", 19 | "riscv64imac-unknown-none-elf", "riscv64gc-unknown-none-elf", 20 | ] 21 | 22 | [features] 23 | default = ["riscv-macros"] 24 | s-mode = [] 25 | critical-section-single-hart = ["critical-section/restore-state-bool"] 26 | 27 | [dependencies] 28 | critical-section = "1.2.0" 29 | embedded-hal = "1.0.0" 30 | riscv-pac = { path = "../riscv-pac", version = "0.2.0" } 31 | riscv-macros = { path = "macros", version = "0.2.0", optional = true } 32 | paste = "1.0.15" 33 | -------------------------------------------------------------------------------- /riscv/README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/d/riscv.svg)](https://crates.io/crates/riscv) 2 | [![crates.io](https://img.shields.io/crates/v/riscv.svg)](https://crates.io/crates/riscv) 3 | 4 | # `riscv` 5 | 6 | > Low level access to RISC-V processors 7 | 8 | This project is developed and maintained by the [RISC-V team][team]. 9 | 10 | ## [Documentation](https://docs.rs/crate/riscv) 11 | 12 | ## Minimum Supported Rust Version (MSRV) 13 | 14 | This crate is guaranteed to compile on stable Rust 1.61 and up. It *might* 15 | compile with older versions but that may change in any new patch release. 16 | 17 | ## License 18 | 19 | Copyright 2019-2022 [RISC-V team][team] 20 | 21 | Permission to use, copy, modify, and/or distribute this software for any purpose 22 | with or without fee is hereby granted, provided that the above copyright notice 23 | and this permission notice appear in all copies. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 26 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 27 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 28 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 29 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 30 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 31 | THIS SOFTWARE. 32 | 33 | ## Code of Conduct 34 | 35 | Contribution to this crate is organized under the terms of the [Rust Code of 36 | Conduct][CoC], the maintainer of this crate, the [RISC-V team][team], promises 37 | to intervene to uphold that code of conduct. 38 | 39 | [CoC]: CODE_OF_CONDUCT.md 40 | [team]: https://github.com/rust-embedded/wg#the-risc-v-team 41 | -------------------------------------------------------------------------------- /riscv/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | fn main() { 4 | println!("cargo:rustc-check-cfg=cfg(riscv)"); 5 | println!("cargo:rustc-check-cfg=cfg(riscv32)"); 6 | println!("cargo:rustc-check-cfg=cfg(riscv64)"); 7 | 8 | let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); 9 | 10 | if target_arch == "riscv32" { 11 | println!("cargo:rustc-cfg=riscv"); 12 | println!("cargo:rustc-cfg=riscv32"); 13 | } else if target_arch == "riscv64" { 14 | println!("cargo:rustc-cfg=riscv"); 15 | println!("cargo:rustc-cfg=riscv64"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /riscv/macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = [ 3 | "The RISC-V Team ", 4 | ] 5 | categories = ["embedded", "no-std"] 6 | description = "Procedural macros re-exported in `riscv`" 7 | documentation = "https://docs.rs/riscv" 8 | keywords = ["riscv", "register", "peripheral"] 9 | license = "MIT OR Apache-2.0" 10 | name = "riscv-macros" 11 | repository = "https://github.com/rust-embedded/riscv" 12 | version = "0.2.0" 13 | edition = "2021" 14 | 15 | [lib] 16 | proc-macro = true 17 | 18 | [dependencies] 19 | proc-macro2 = "1.0" 20 | quote = "1.0" 21 | syn = { version = "2.0" } 22 | -------------------------------------------------------------------------------- /riscv/src/bits.rs: -------------------------------------------------------------------------------- 1 | /// Insert a new value into a bitfield 2 | /// 3 | /// `value` is masked to `width` bits and inserted into `orig`.` 4 | #[inline] 5 | pub fn bf_insert(orig: usize, bit: usize, width: usize, value: usize) -> usize { 6 | let mask = (1 << width) - 1; 7 | orig & !(mask << bit) | ((value & mask) << bit) 8 | } 9 | 10 | /// Extract a value from a bitfield 11 | /// 12 | /// Extracts `width` bits from bit offset `bit` and returns it shifted to bit 0.s 13 | #[inline] 14 | pub fn bf_extract(orig: usize, bit: usize, width: usize) -> usize { 15 | let mask = (1 << width) - 1; 16 | (orig >> bit) & mask 17 | } 18 | -------------------------------------------------------------------------------- /riscv/src/critical_section.rs: -------------------------------------------------------------------------------- 1 | use critical_section::{set_impl, Impl, RawRestoreState}; 2 | 3 | use crate::interrupt; 4 | 5 | struct SingleHartCriticalSection; 6 | set_impl!(SingleHartCriticalSection); 7 | 8 | unsafe impl Impl for SingleHartCriticalSection { 9 | #[cfg(not(feature = "s-mode"))] 10 | unsafe fn acquire() -> RawRestoreState { 11 | let mut mstatus: usize; 12 | core::arch::asm!("csrrci {}, mstatus, 0b1000", out(reg) mstatus); 13 | core::mem::transmute::<_, crate::register::mstatus::Mstatus>(mstatus).mie() 14 | } 15 | 16 | #[cfg(feature = "s-mode")] 17 | unsafe fn acquire() -> RawRestoreState { 18 | let mut sstatus: usize; 19 | core::arch::asm!("csrrci {}, sstatus, 0b0010", out(reg) sstatus); 20 | core::mem::transmute::<_, crate::register::sstatus::Sstatus>(sstatus).sie() 21 | } 22 | 23 | unsafe fn release(was_active: RawRestoreState) { 24 | // Only re-enable interrupts if they were enabled before the critical section. 25 | if was_active { 26 | interrupt::enable() 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /riscv/src/delay.rs: -------------------------------------------------------------------------------- 1 | //! Delay devices and providers 2 | use crate::register::mcycle; 3 | use embedded_hal::delay::DelayNs; 4 | 5 | /// Machine mode cycle counter (`mcycle`) as a delay provider 6 | #[derive(Copy, Clone)] 7 | #[repr(transparent)] 8 | pub struct McycleDelay { 9 | /// The clock speed of the core, in Hertz 10 | ticks_second: u32, 11 | } 12 | 13 | impl McycleDelay { 14 | /// Constructs the delay provider. 15 | /// `ticks_second` should be the clock speed of the core, in Hertz 16 | #[inline] 17 | pub const fn new(ticks_second: u32) -> Self { 18 | Self { ticks_second } 19 | } 20 | } 21 | 22 | impl DelayNs for McycleDelay { 23 | #[inline] 24 | fn delay_ns(&mut self, ns: u32) { 25 | let t0 = mcycle::read64(); 26 | let ns_64: u64 = ns.into(); 27 | let clock = (ns_64 * (self.ticks_second as u64)) / 1_000_000_000u64; 28 | while mcycle::read64().wrapping_sub(t0) <= clock {} 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /riscv/src/interrupt.rs: -------------------------------------------------------------------------------- 1 | //! Interrupts 2 | 3 | // NOTE: Adapted from cortex-m/src/interrupt.rs 4 | 5 | use crate::result::Result; 6 | 7 | // re-export useful riscv-pac traits 8 | pub use riscv_pac::{CoreInterruptNumber, ExceptionNumber, InterruptNumber}; 9 | 10 | pub mod machine; 11 | pub mod supervisor; 12 | 13 | #[cfg(not(feature = "s-mode"))] 14 | pub use machine::*; 15 | #[cfg(feature = "s-mode")] 16 | pub use supervisor::*; 17 | 18 | /// Trap Cause. 19 | /// 20 | /// This enum represents the cause of a trap. It can be either an interrupt or an exception. 21 | /// The [`mcause`](crate::register::mcause::Mcause::cause) and 22 | /// [`scause`](crate::register::scause::Scause::cause) registers return a value of this type. 23 | /// However, the trap cause is represented as raw numbers. To get a target-specific trap cause, 24 | /// use [`Trap::try_into`] with your target-specific M-Mode or S-Mode trap cause types. 25 | /// 26 | /// # Example 27 | /// 28 | /// In targets that comply with the RISC-V standard, you can use the standard 29 | /// [`Interrupt`] and [`Exception`] enums to represent the trap cause: 30 | /// 31 | /// ```no_run 32 | /// use riscv::interrupt::{Trap, Interrupt, Exception}; 33 | /// use riscv::register::mcause; 34 | /// 35 | /// let raw_trap: Trap = mcause::read().cause(); 36 | /// let standard_trap: Trap = raw_trap.try_into().unwrap(); 37 | /// ``` 38 | /// 39 | /// Targets that do not comply with the RISC-V standard usually have their own interrupt and exceptions. 40 | /// You can find these types in the target-specific PAC. If it has been generated with `svd2rust`, 41 | /// you can use the `pac::interrupt::CoreInterrupt` and `pac::interrupt::Exception` enums: 42 | /// 43 | /// ```ignore,no_run 44 | /// use riscv::interrupt::Trap; 45 | /// use pac::interrupt::{CoreInterrupt, Exception}; // pac is the target-specific PAC 46 | /// 47 | /// let standard_trap: Trap = pac::interrupt::cause(); 48 | /// ``` 49 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 50 | pub enum Trap { 51 | Interrupt(I), 52 | Exception(E), 53 | } 54 | 55 | /// Trap Error 56 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 57 | pub enum TrapError { 58 | InvalidInterrupt(usize), 59 | InvalidException(usize), 60 | } 61 | 62 | impl Trap { 63 | /// Converts a target-specific trap cause to a generic trap cause 64 | #[inline] 65 | pub fn from(trap: Trap) -> Self { 66 | match trap { 67 | Trap::Interrupt(interrupt) => Trap::Interrupt(interrupt.number()), 68 | Trap::Exception(exception) => Trap::Exception(exception.number()), 69 | } 70 | } 71 | 72 | /// Tries to convert the generic trap cause to a target-specific trap cause 73 | #[inline] 74 | pub fn try_into(self) -> Result> 75 | where 76 | I: CoreInterruptNumber, 77 | E: ExceptionNumber, 78 | { 79 | match self { 80 | Trap::Interrupt(code) => Ok(Trap::Interrupt(I::from_number(code)?)), 81 | Trap::Exception(code) => Ok(Trap::Exception(E::from_number(code)?)), 82 | } 83 | } 84 | } 85 | 86 | impl Trap { 87 | /// Converts a target-specific trap cause to a generic trap cause 88 | #[inline] 89 | pub fn into(self) -> Trap { 90 | Trap::from(self) 91 | } 92 | 93 | /// Tries to convert the generic trap cause to a target-specific trap cause 94 | #[inline] 95 | pub fn try_from(trap: Trap) -> Result { 96 | trap.try_into() 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /riscv/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Low level access to RISC-V processors 2 | //! 3 | //! # Minimum Supported Rust Version (MSRV) 4 | //! 5 | //! This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* 6 | //! compile with older versions but that may change in any new patch release. 7 | //! 8 | //! # Features 9 | //! 10 | //! This crate provides: 11 | //! 12 | //! - Access to core registers like `mstatus` or `mcause`. 13 | //! - Interrupt manipulation mechanisms. 14 | //! - Wrappers around assembly instructions like `WFI`. 15 | //! 16 | //! # Optional features 17 | //! 18 | //! ## `s-mode` 19 | //! 20 | //! This feature re-exports in `interrupt` S-mode interrupt functions defined in `interrupt::supervisor`. 21 | //! By default, the crate assumes that the target is running in M-mode. 22 | //! Thus, `interrupt` re-exports the M-mode functions defined in `interrupt::machine`. 23 | //! 24 | //! ## `critical-section-single-hart` 25 | //! 26 | //! This feature enables a [`critical-section`](https://github.com/rust-embedded/critical-section) 27 | //! implementation suitable for single-hart targets, based on disabling interrupts globally. 28 | //! This feature uses S-mode interrupt handling if the `s-mode` feature is enabled, and M-mode otherwise. 29 | //! 30 | //! It is **unsound** to enable it on multi-hart targets, 31 | //! and may cause functional problems in systems where some interrupts must NOT be disabled 32 | //! or critical sections are managed as part of an RTOS. In these cases, you should use 33 | //! a target-specific implementation instead, typically provided by a HAL or RTOS crate. 34 | 35 | #![no_std] 36 | #![allow(clippy::missing_safety_doc)] 37 | #![allow(clippy::eq_op)] 38 | #![allow(clippy::identity_op)] 39 | 40 | pub use paste::paste; 41 | 42 | pub mod asm; 43 | pub mod bits; 44 | pub mod delay; 45 | pub mod interrupt; 46 | pub mod register; 47 | 48 | // Re-export crates of the RISC-V ecosystem 49 | #[cfg(feature = "riscv-macros")] 50 | pub use riscv_macros::*; 51 | pub use riscv_pac::*; 52 | 53 | #[macro_use] 54 | mod macros; 55 | 56 | #[cfg(all(riscv, feature = "critical-section-single-hart"))] 57 | mod critical_section; 58 | 59 | /// Used to reexport items for use in macros. Do not use directly. 60 | /// Not covered by semver guarantees. 61 | #[doc(hidden)] 62 | pub mod _export { 63 | pub use critical_section; 64 | } 65 | -------------------------------------------------------------------------------- /riscv/src/macros.rs: -------------------------------------------------------------------------------- 1 | /// Macro to create a mutable reference to a statically allocated value 2 | /// 3 | /// This macro returns a value with type `Option<&'static mut $ty>`. `Some($expr)` will be returned 4 | /// the first time the macro is executed; further calls will return `None`. To avoid `unwrap`ping a 5 | /// `None` variant the caller must ensure that the macro is called from a function that's executed 6 | /// at most once in the whole lifetime of the program. 7 | /// 8 | /// # Note 9 | /// 10 | /// This macro requires a `critical-section` implementation to be set. For most single-hart systems, 11 | /// you can enable the `critical-section-single-hart` feature for this crate. For other systems, you 12 | /// have to provide one from elsewhere, typically your chip's HAL crate. 13 | /// 14 | /// # Example 15 | /// 16 | /// ``` no_run 17 | /// use riscv::singleton; 18 | /// 19 | /// fn main() { 20 | /// // OK if `main` is executed only once 21 | /// let x: &'static mut bool = singleton!(: bool = false).unwrap(); 22 | /// 23 | /// let y = alias(); 24 | /// // BAD this second call to `alias` will definitively `panic!` 25 | /// let y_alias = alias(); 26 | /// } 27 | /// 28 | /// fn alias() -> &'static mut bool { 29 | /// singleton!(: bool = false).unwrap() 30 | /// } 31 | /// ``` 32 | #[macro_export] 33 | macro_rules! singleton { 34 | (: $ty:ty = $expr:expr) => { 35 | $crate::_export::critical_section::with(|_| { 36 | static mut VAR: Option<$ty> = None; 37 | 38 | #[allow(unsafe_code)] 39 | let used = unsafe { VAR.is_some() }; 40 | if used { 41 | None 42 | } else { 43 | let expr = $expr; 44 | 45 | #[allow(unsafe_code)] 46 | unsafe { 47 | VAR = Some(expr) 48 | } 49 | 50 | #[allow(unsafe_code)] 51 | unsafe { 52 | VAR.as_mut() 53 | } 54 | } 55 | }) 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /riscv/src/register.rs: -------------------------------------------------------------------------------- 1 | //! RISC-V CSR's 2 | //! 3 | //! The following registers are not available on 64-bit implementations. 4 | //! 5 | //! - cycleh 6 | //! - timeh 7 | //! - instreth 8 | //! - hpmcounter<3-31>h 9 | //! - mcycleh 10 | //! - minstreth 11 | //! - mhpmcounter<3-31>h 12 | //! - mstatush 13 | //! 14 | //! # On Floating-Point CSRs 15 | //! 16 | //! We are deliberately *not* providing instructions that could change the floating-point rounding 17 | //! mode or exception behavior or read the accrued exceptions flags: `frcsr`, `fscsr`, `fsrm`, 18 | //! `frflags`, `fsflags`. 19 | //! 20 | //! Rust makes no guarantees whatsoever about the contents of the accrued exceptions register: Rust 21 | //! floating-point operations may or may not result in this register getting updated with exception 22 | //! state, and the register can change between two invocations of this function even when no 23 | //! floating-point operations appear in the source code (since floating-point operations appearing 24 | //! earlier or later can be reordered). 25 | //! 26 | //! Modifying the rounding mode leads to **immediate Undefined Behavior**: Rust assumes that the 27 | //! default rounding mode is always set and will optimize accordingly. This even applies when the 28 | //! rounding mode is altered and later reset to its original value without any floating-point 29 | //! operations appearing in the source code between those operations (since floating-point 30 | //! operations appearing earlier or later can be reordered). 31 | //! 32 | //! If you need to perform some floating-point operations and check whether they raised an 33 | //! exception, use a single inline assembly block for the entire sequence of operations. 34 | //! 35 | //! If you need to perform some floating-point operations under a different rounding mode, use a 36 | //! single inline assembly block and make sure to restore the original rounding mode before the end 37 | //! of the block. 38 | 39 | #[macro_use] 40 | mod macros; 41 | 42 | // User Counter/Timers 43 | pub mod cycle; 44 | pub mod cycleh; 45 | mod hpmcounterx; 46 | pub use self::hpmcounterx::*; 47 | pub mod instret; 48 | pub mod instreth; 49 | pub mod time; 50 | pub mod timeh; 51 | 52 | // Supervisor Trap Setup 53 | pub mod scounteren; 54 | pub mod sie; 55 | pub mod sstatus; 56 | pub mod stvec; 57 | 58 | // Supervisor Trap Handling 59 | pub mod scause; 60 | pub mod scontext; 61 | pub mod senvcfg; 62 | pub mod sepc; 63 | pub mod sip; 64 | pub mod sscratch; 65 | pub mod stval; 66 | 67 | // Supervisor Protection and Translation 68 | pub mod satp; 69 | 70 | // Machine Information Registers 71 | pub mod marchid; 72 | pub mod mhartid; 73 | pub mod mimpid; 74 | pub mod mvendorid; 75 | 76 | // Machine Trap Setup 77 | pub mod mcounteren; 78 | pub mod medeleg; 79 | pub mod mideleg; 80 | pub mod mie; 81 | pub mod misa; 82 | pub mod mstatus; 83 | pub mod mstatush; 84 | pub mod mtvec; 85 | 86 | // Machine Trap Handling 87 | pub mod mcause; 88 | pub mod mepc; 89 | pub mod mip; 90 | pub mod mscratch; 91 | pub mod mtinst; 92 | pub mod mtval; 93 | 94 | // Machine Protection and Translation 95 | mod pmpcfgx; 96 | pub use self::pmpcfgx::*; 97 | mod pmpaddrx; 98 | pub use self::pmpaddrx::*; 99 | 100 | // Machine Counter/Timers 101 | pub mod mcountinhibit; 102 | pub mod mcycle; 103 | pub mod mcycleh; 104 | mod mhpmcounterx; 105 | pub use self::mhpmcounterx::*; 106 | pub mod minstret; 107 | pub mod minstreth; 108 | 109 | // Machine Counter Setup 110 | mod mhpmeventx; 111 | pub use self::mhpmeventx::*; 112 | 113 | // Machine configuration 114 | pub mod mconfigptr; 115 | 116 | #[cfg(test)] 117 | mod tests; 118 | 119 | // TODO: Debug/Trace Registers (shared with Debug Mode) 120 | 121 | // TODO: Debug Mode Registers 122 | -------------------------------------------------------------------------------- /riscv/src/register/cycle.rs: -------------------------------------------------------------------------------- 1 | //! cycle register 2 | //! 3 | //! Shadow of mcycle register 4 | //! must have `scounteren::cy` or `mcounteren::cy` bit enabled depending on whether 5 | //! S-mode is implemented or not 6 | 7 | read_csr_as_usize!(0xC00); 8 | read_composite_csr!(super::cycleh::read(), read()); 9 | -------------------------------------------------------------------------------- /riscv/src/register/cycleh.rs: -------------------------------------------------------------------------------- 1 | //! cycleh register 2 | //! 3 | //! Shadow of mcycleh register (rv32) 4 | //! must have `scounteren::cy` or `mcounteren::cy` bit enabled depending on whether 5 | //! S-mode is implemented or not 6 | 7 | read_csr_as_usize_rv32!(0xC80); 8 | -------------------------------------------------------------------------------- /riscv/src/register/hpmcounterx.rs: -------------------------------------------------------------------------------- 1 | macro_rules! reg { 2 | ( 3 | $addr:expr, $csrl:ident, $csrh:ident 4 | ) => { 5 | /// Performance-monitoring counter 6 | pub mod $csrl { 7 | read_csr_as_usize!($addr); 8 | read_composite_csr!(super::$csrh::read(), read()); 9 | } 10 | }; 11 | } 12 | 13 | macro_rules! regh { 14 | ( 15 | $addr:expr, $csrh:ident 16 | ) => { 17 | /// Upper 32 bits of performance-monitoring counter (RV32I only) 18 | pub mod $csrh { 19 | read_csr_as_usize_rv32!($addr); 20 | } 21 | }; 22 | } 23 | 24 | reg!(0xC03, hpmcounter3, hpmcounter3h); 25 | reg!(0xC04, hpmcounter4, hpmcounter4h); 26 | reg!(0xC05, hpmcounter5, hpmcounter5h); 27 | reg!(0xC06, hpmcounter6, hpmcounter6h); 28 | reg!(0xC07, hpmcounter7, hpmcounter7h); 29 | reg!(0xC08, hpmcounter8, hpmcounter8h); 30 | reg!(0xC09, hpmcounter9, hpmcounter9h); 31 | reg!(0xC0A, hpmcounter10, hpmcounter10h); 32 | reg!(0xC0B, hpmcounter11, hpmcounter11h); 33 | reg!(0xC0C, hpmcounter12, hpmcounter12h); 34 | reg!(0xC0D, hpmcounter13, hpmcounter13h); 35 | reg!(0xC0E, hpmcounter14, hpmcounter14h); 36 | reg!(0xC0F, hpmcounter15, hpmcounter15h); 37 | reg!(0xC10, hpmcounter16, hpmcounter16h); 38 | reg!(0xC11, hpmcounter17, hpmcounter17h); 39 | reg!(0xC12, hpmcounter18, hpmcounter18h); 40 | reg!(0xC13, hpmcounter19, hpmcounter19h); 41 | reg!(0xC14, hpmcounter20, hpmcounter20h); 42 | reg!(0xC15, hpmcounter21, hpmcounter21h); 43 | reg!(0xC16, hpmcounter22, hpmcounter22h); 44 | reg!(0xC17, hpmcounter23, hpmcounter23h); 45 | reg!(0xC18, hpmcounter24, hpmcounter24h); 46 | reg!(0xC19, hpmcounter25, hpmcounter25h); 47 | reg!(0xC1A, hpmcounter26, hpmcounter26h); 48 | reg!(0xC1B, hpmcounter27, hpmcounter27h); 49 | reg!(0xC1C, hpmcounter28, hpmcounter28h); 50 | reg!(0xC1D, hpmcounter29, hpmcounter29h); 51 | reg!(0xC1E, hpmcounter30, hpmcounter30h); 52 | reg!(0xC1F, hpmcounter31, hpmcounter31h); 53 | 54 | regh!(0xC83, hpmcounter3h); 55 | regh!(0xC84, hpmcounter4h); 56 | regh!(0xC85, hpmcounter5h); 57 | regh!(0xC86, hpmcounter6h); 58 | regh!(0xC87, hpmcounter7h); 59 | regh!(0xC88, hpmcounter8h); 60 | regh!(0xC89, hpmcounter9h); 61 | regh!(0xC8A, hpmcounter10h); 62 | regh!(0xC8B, hpmcounter11h); 63 | regh!(0xC8C, hpmcounter12h); 64 | regh!(0xC8D, hpmcounter13h); 65 | regh!(0xC8E, hpmcounter14h); 66 | regh!(0xC8F, hpmcounter15h); 67 | regh!(0xC90, hpmcounter16h); 68 | regh!(0xC91, hpmcounter17h); 69 | regh!(0xC92, hpmcounter18h); 70 | regh!(0xC93, hpmcounter19h); 71 | regh!(0xC94, hpmcounter20h); 72 | regh!(0xC95, hpmcounter21h); 73 | regh!(0xC96, hpmcounter22h); 74 | regh!(0xC97, hpmcounter23h); 75 | regh!(0xC98, hpmcounter24h); 76 | regh!(0xC99, hpmcounter25h); 77 | regh!(0xC9A, hpmcounter26h); 78 | regh!(0xC9B, hpmcounter27h); 79 | regh!(0xC9C, hpmcounter28h); 80 | regh!(0xC9D, hpmcounter29h); 81 | regh!(0xC9E, hpmcounter30h); 82 | regh!(0xC9F, hpmcounter31h); 83 | -------------------------------------------------------------------------------- /riscv/src/register/instret.rs: -------------------------------------------------------------------------------- 1 | //! instret register 2 | //! 3 | //! Shadow of minstret register 4 | //! must have `scounteren::ir` or `mcounteren::ir` bit enabled depending on whether 5 | //! S-mode is implemented or not 6 | 7 | read_csr_as_usize!(0xC02); 8 | read_composite_csr!(super::instreth::read(), read()); 9 | -------------------------------------------------------------------------------- /riscv/src/register/instreth.rs: -------------------------------------------------------------------------------- 1 | //! instreth register 2 | //! 3 | //! Shadow of minstreth register (rv32) 4 | //! must have `scounteren::ir` or `mcounteren::ir` bit enabled depending on whether 5 | //! S-mode is implemented or not 6 | 7 | read_csr_as_usize!(0xC82); 8 | -------------------------------------------------------------------------------- /riscv/src/register/marchid.rs: -------------------------------------------------------------------------------- 1 | //! marchid register 2 | 3 | read_only_csr! { 4 | /// `marchid` register 5 | Marchid: 0xF12, 6 | mask: 0xffff_ffff, 7 | sentinel: 0, 8 | } 9 | -------------------------------------------------------------------------------- /riscv/src/register/mcause.rs: -------------------------------------------------------------------------------- 1 | //! mcause register 2 | 3 | pub use crate::interrupt::Trap; 4 | 5 | read_only_csr! { 6 | /// `mcause` register 7 | Mcause: 0x342, 8 | mask: 0xffff_ffff, 9 | } 10 | 11 | #[cfg(target_arch = "riscv32")] 12 | read_only_csr_field! { 13 | Mcause, 14 | /// Returns the `code` field. 15 | code: [0:30], 16 | } 17 | 18 | #[cfg(not(target_arch = "riscv32"))] 19 | read_only_csr_field! { 20 | Mcause, 21 | /// Returns the `code` field. 22 | code: [0:62], 23 | } 24 | 25 | #[cfg(target_arch = "riscv32")] 26 | read_only_csr_field! { 27 | Mcause, 28 | /// Is the trap cause an interrupt. 29 | is_interrupt: 31, 30 | } 31 | 32 | #[cfg(not(target_arch = "riscv32"))] 33 | read_only_csr_field! { 34 | Mcause, 35 | /// Is the trap cause an interrupt. 36 | is_interrupt: 63, 37 | } 38 | 39 | impl Mcause { 40 | /// Returns the trap cause represented by this register. 41 | /// 42 | /// # Note 43 | /// 44 | /// This method returns a **raw trap cause**, which means that values are represented as `usize`. 45 | /// To get a target-specific trap cause, use [`Trap::try_into`] with your target-specific M-Mode trap cause types. 46 | #[inline] 47 | pub fn cause(&self) -> Trap { 48 | if self.is_interrupt() { 49 | Trap::Interrupt(self.code()) 50 | } else { 51 | Trap::Exception(self.code()) 52 | } 53 | } 54 | 55 | /// Is trap cause an exception. 56 | #[inline] 57 | pub fn is_exception(&self) -> bool { 58 | !self.is_interrupt() 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /riscv/src/register/mconfigptr.rs: -------------------------------------------------------------------------------- 1 | //! `mconfigptr` register. 2 | 3 | use crate::result::{Error, Result}; 4 | 5 | const MASK: usize = usize::MAX; 6 | 7 | read_only_csr! { 8 | /// `mconfigptr` register. 9 | Mconfigptr: 0xf15, 10 | mask: MASK, 11 | sentinel: 0, 12 | } 13 | 14 | impl Mconfigptr { 15 | /// Represents the bitshift for a properly aligned configuration pointer. 16 | pub const ALIGN_SHIFT: usize = (usize::BITS / 8).ilog2() as usize; 17 | /// Represents the bitmask for a properly aligned configuration pointer. 18 | pub const ALIGN_MASK: usize = (1usize << Self::ALIGN_SHIFT) - 1; 19 | 20 | /// Gets the pointer to the machine configuration structure. 21 | /// 22 | /// # Panics 23 | /// 24 | /// Panics if: 25 | /// 26 | /// - the value is `0`, indicating no configuration structure 27 | /// - the pointer is not aligned to an MXLEN byte value 28 | pub fn as_ptr(&self) -> *const u8 { 29 | self.try_as_ptr().unwrap() 30 | } 31 | 32 | /// Attempts to get the pointer to the machine configuration structure. 33 | /// 34 | /// # Note 35 | /// 36 | /// Returns an error if: 37 | /// 38 | /// - the value is `0`, indicating no configuration structure 39 | /// - the pointer is not aligned to an MXLEN byte value 40 | pub const fn try_as_ptr(&self) -> Result<*const u8> { 41 | match self.bits() { 42 | 0 => Err(Error::InvalidFieldVariant { 43 | field: "mconfigptr", 44 | value: 0, 45 | }), 46 | p if p & Self::ALIGN_MASK != 0 => Err(Error::InvalidFieldValue { 47 | field: "mconfigptr", 48 | value: p, 49 | bitmask: !Self::ALIGN_MASK, 50 | }), 51 | p => Ok(p as *const _), 52 | } 53 | } 54 | } 55 | 56 | #[cfg(test)] 57 | mod tests { 58 | use super::*; 59 | 60 | #[test] 61 | fn test_mconfigptr() { 62 | #[cfg(target_arch = "riscv32")] 63 | const EXP_SHIFT: usize = 2; 64 | #[cfg(not(target_arch = "riscv32"))] 65 | const EXP_SHIFT: usize = 3; 66 | 67 | const EXP_MASK: usize = (1usize << EXP_SHIFT) - 1; 68 | 69 | assert_eq!(Mconfigptr::ALIGN_SHIFT, EXP_SHIFT); 70 | assert_eq!(Mconfigptr::ALIGN_MASK, EXP_MASK); 71 | 72 | (1..usize::BITS) 73 | .map(|b| ((1u128 << b) - 1) as usize) 74 | .for_each(|ptr| { 75 | let mconfigptr = Mconfigptr::from_bits(ptr); 76 | assert_eq!(mconfigptr.bits(), ptr); 77 | 78 | match mconfigptr.try_as_ptr() { 79 | Ok(cfg_ptr) => { 80 | assert_eq!(cfg_ptr, ptr as *const _); 81 | assert_eq!(mconfigptr.as_ptr(), ptr as *const _); 82 | } 83 | Err(err) if ptr == 0 => assert_eq!( 84 | err, 85 | Error::InvalidFieldVariant { 86 | field: "mconfigptr", 87 | value: 0 88 | } 89 | ), 90 | Err(err) => assert_eq!( 91 | err, 92 | Error::InvalidFieldValue { 93 | field: "mconfigptr", 94 | value: ptr, 95 | bitmask: !Mconfigptr::ALIGN_MASK, 96 | } 97 | ), 98 | } 99 | 100 | let aligned_ptr = ptr << Mconfigptr::ALIGN_SHIFT; 101 | let aligned_mconfigptr = Mconfigptr::from_bits(aligned_ptr); 102 | 103 | assert_eq!(aligned_mconfigptr.try_as_ptr(), Ok(aligned_ptr as *const _)); 104 | assert_eq!(aligned_mconfigptr.as_ptr(), aligned_ptr as *const _); 105 | }); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /riscv/src/register/mcounteren.rs: -------------------------------------------------------------------------------- 1 | //! mcounteren register 2 | 3 | use crate::result::{Error, Result}; 4 | 5 | read_write_csr! { 6 | /// `mcounteren` register 7 | Mcounteren: 0x306, 8 | mask: 0xffff_ffff, 9 | } 10 | 11 | read_write_csr_field! { 12 | Mcounteren, 13 | /// Supervisor "cycle\[h\]" Enable 14 | cy: 0, 15 | } 16 | 17 | read_write_csr_field! { 18 | Mcounteren, 19 | /// Supervisor "time\[h\]" Enable 20 | tm: 1, 21 | } 22 | 23 | read_write_csr_field! { 24 | Mcounteren, 25 | /// Supervisor "instret\[h\]" Enable 26 | ir: 2, 27 | } 28 | 29 | read_write_csr_field! { 30 | Mcounteren, 31 | /// Supervisor "hpm\[x\]" Enable (bits 3-31) 32 | hpm: 3..=31, 33 | } 34 | 35 | set!(0x306); 36 | clear!(0x306); 37 | 38 | set_clear_csr!( 39 | /// Supervisor cycle Enable 40 | , set_cy, clear_cy, 1 << 0); 41 | 42 | set_clear_csr!( 43 | /// Supervisor time Enable 44 | , set_tm, clear_tm, 1 << 1); 45 | 46 | set_clear_csr!( 47 | /// Supervisor instret Enable 48 | , set_ir, clear_ir, 1 << 2); 49 | 50 | /// Enables the "hpm\[X\]" counter. 51 | /// 52 | /// Updates the `mcounteren` register. 53 | /// 54 | /// **WARNING**: panics on: 55 | /// 56 | /// - non-`riscv` targets 57 | /// - `index` out-of-bounds 58 | #[inline] 59 | pub unsafe fn set_hpm(index: usize) { 60 | try_set_hpm(index).unwrap(); 61 | } 62 | 63 | /// Attempts to enable the "hpm\[X\]" counter. 64 | /// 65 | /// Updates the `mcounteren` register. 66 | #[inline] 67 | pub unsafe fn try_set_hpm(index: usize) -> Result<()> { 68 | if (3..32).contains(&index) { 69 | _try_set(1 << index) 70 | } else { 71 | Err(Error::IndexOutOfBounds { 72 | index, 73 | min: 3, 74 | max: 31, 75 | }) 76 | } 77 | } 78 | 79 | /// Disables the "hpm\[X\]" counter. 80 | /// 81 | /// Updates the `mcounteren` register. 82 | /// 83 | /// **WARNING**: panics on: 84 | /// 85 | /// - non-`riscv` targets 86 | /// - `index` out-of-bounds 87 | #[inline] 88 | pub unsafe fn clear_hpm(index: usize) { 89 | try_clear_hpm(index).unwrap(); 90 | } 91 | 92 | /// Attempts to disable the "hpm\[X\]" counter. 93 | /// 94 | /// Updates the `mcounteren` register. 95 | #[inline] 96 | pub unsafe fn try_clear_hpm(index: usize) -> Result<()> { 97 | if (3..32).contains(&index) { 98 | _try_clear(1 << index) 99 | } else { 100 | Err(Error::IndexOutOfBounds { 101 | index, 102 | min: 3, 103 | max: 31, 104 | }) 105 | } 106 | } 107 | 108 | #[cfg(test)] 109 | mod tests { 110 | use super::*; 111 | 112 | #[test] 113 | fn test_mcounteren() { 114 | let mut m = Mcounteren { bits: 0 }; 115 | 116 | test_csr_field!(m, cy); 117 | test_csr_field!(m, tm); 118 | test_csr_field!(m, ir); 119 | 120 | (3..32).for_each(|i| test_csr_field!(m, hpm, i)); 121 | 122 | (0..3).chain(32..64).for_each(|index| { 123 | test_csr_field!( 124 | m, 125 | hpm, 126 | index, 127 | Error::IndexOutOfBounds { 128 | index, 129 | min: 3, 130 | max: 31 131 | } 132 | ) 133 | }); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /riscv/src/register/mcountinhibit.rs: -------------------------------------------------------------------------------- 1 | //! `mcountinhibit` register 2 | 3 | use crate::result::{Error, Result}; 4 | 5 | read_write_csr! { 6 | /// `mcountinhibit` register 7 | Mcountinhibit: 0x320, 8 | mask: 0xffff_fffd, 9 | } 10 | 11 | set!(0x320); 12 | clear!(0x320); 13 | 14 | read_write_csr_field! { 15 | Mcountinhibit, 16 | /// Gets the `cycle[h]` inhibit field value. 17 | cy: 0, 18 | } 19 | 20 | read_write_csr_field! { 21 | Mcountinhibit, 22 | /// Gets the `instret[h]` inhibit field value. 23 | ir: 2, 24 | } 25 | 26 | read_write_csr_field! { 27 | Mcountinhibit, 28 | /// Gets the `mhpmcounterX[h]` inhibit field value. 29 | /// 30 | /// **WARN**: `index` must be in the range `[31:3]`. 31 | hpm: 3..=31, 32 | } 33 | 34 | set_clear_csr!( 35 | /// Machine cycle Disable 36 | , set_cy, clear_cy, 1 << 0); 37 | 38 | set_clear_csr!( 39 | /// Machine instret Disable 40 | , set_ir, clear_ir, 1 << 2); 41 | 42 | #[inline] 43 | pub unsafe fn set_hpm(index: usize) { 44 | try_set_hpm(index).unwrap(); 45 | } 46 | 47 | #[inline] 48 | pub unsafe fn try_set_hpm(index: usize) -> Result<()> { 49 | if (3..32).contains(&index) { 50 | _try_set(1 << index) 51 | } else { 52 | Err(Error::IndexOutOfBounds { 53 | index, 54 | min: 3, 55 | max: 31, 56 | }) 57 | } 58 | } 59 | 60 | #[inline] 61 | pub unsafe fn clear_hpm(index: usize) { 62 | try_clear_hpm(index).unwrap(); 63 | } 64 | 65 | #[inline] 66 | pub unsafe fn try_clear_hpm(index: usize) -> Result<()> { 67 | if (3..32).contains(&index) { 68 | _try_clear(1 << index) 69 | } else { 70 | Err(Error::IndexOutOfBounds { 71 | index, 72 | min: 3, 73 | max: 31, 74 | }) 75 | } 76 | } 77 | 78 | #[cfg(test)] 79 | mod tests { 80 | use super::*; 81 | 82 | #[test] 83 | fn test_mcountinhibit() { 84 | let mut m = Mcountinhibit { bits: 0 }; 85 | 86 | assert!(!m.cy()); 87 | 88 | m.set_cy(true); 89 | assert!(m.cy()); 90 | 91 | m.set_cy(false); 92 | assert!(!m.cy()); 93 | 94 | assert!(!m.ir()); 95 | 96 | m.set_ir(true); 97 | assert!(m.ir()); 98 | 99 | m.set_ir(false); 100 | assert!(!m.ir()); 101 | 102 | (3..32).for_each(|i| { 103 | assert!(!m.hpm(i)); 104 | assert_eq!(m.try_hpm(i), Ok(false)); 105 | 106 | m.set_hpm(i, true); 107 | assert!(m.hpm(i)); 108 | assert_eq!(m.try_hpm(i), Ok(true)); 109 | 110 | assert_eq!(m.try_set_hpm(i, false), Ok(())); 111 | assert_eq!(m.try_hpm(i), Ok(false)); 112 | }); 113 | 114 | (0..2).chain(32..64).for_each(|index| { 115 | assert_eq!( 116 | m.try_hpm(index), 117 | Err(Error::IndexOutOfBounds { 118 | index, 119 | min: 3, 120 | max: 31 121 | }) 122 | ); 123 | assert_eq!( 124 | m.try_set_hpm(index, false), 125 | Err(Error::IndexOutOfBounds { 126 | index, 127 | min: 3, 128 | max: 31 129 | }) 130 | ); 131 | }); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /riscv/src/register/mcycle.rs: -------------------------------------------------------------------------------- 1 | //! mcycle register 2 | 3 | read_csr_as_usize!(0xB00); 4 | write_csr_as_usize!(0xB00); 5 | read_composite_csr!(super::mcycleh::read(), read()); 6 | write_composite_csr!(super::mcycleh::write, write); 7 | -------------------------------------------------------------------------------- /riscv/src/register/mcycleh.rs: -------------------------------------------------------------------------------- 1 | //! mcycleh register 2 | 3 | read_csr_as_usize_rv32!(0xB80); 4 | write_csr_as_usize_rv32!(0xB80); 5 | -------------------------------------------------------------------------------- /riscv/src/register/mepc.rs: -------------------------------------------------------------------------------- 1 | //! mepc register 2 | 3 | read_csr_as_usize!(0x341); 4 | write_csr_as_usize!(0x341); 5 | -------------------------------------------------------------------------------- /riscv/src/register/mhartid.rs: -------------------------------------------------------------------------------- 1 | //! mhartid register 2 | 3 | read_csr_as_usize!(0xf14); 4 | -------------------------------------------------------------------------------- /riscv/src/register/mhpmcounterx.rs: -------------------------------------------------------------------------------- 1 | macro_rules! reg { 2 | ( 3 | $addr:expr, $csrl:ident, $csrh:ident 4 | ) => { 5 | /// Machine performance-monitoring counter 6 | pub mod $csrl { 7 | read_csr_as_usize!($addr); 8 | write_csr_as_usize!($addr); 9 | read_composite_csr!(super::$csrh::read(), read()); 10 | } 11 | }; 12 | } 13 | 14 | macro_rules! regh { 15 | ( 16 | $addr:expr, $csrh:ident 17 | ) => { 18 | /// Upper 32 bits of machine performance-monitoring counter (RV32I only) 19 | pub mod $csrh { 20 | read_csr_as_usize_rv32!($addr); 21 | write_csr_as_usize_rv32!($addr); 22 | } 23 | }; 24 | } 25 | 26 | reg!(0xB03, mhpmcounter3, mhpmcounter3h); 27 | reg!(0xB04, mhpmcounter4, mhpmcounter4h); 28 | reg!(0xB05, mhpmcounter5, mhpmcounter5h); 29 | reg!(0xB06, mhpmcounter6, mhpmcounter6h); 30 | reg!(0xB07, mhpmcounter7, mhpmcounter7h); 31 | reg!(0xB08, mhpmcounter8, mhpmcounter8h); 32 | reg!(0xB09, mhpmcounter9, mhpmcounter9h); 33 | reg!(0xB0A, mhpmcounter10, mhpmcounter10h); 34 | reg!(0xB0B, mhpmcounter11, mhpmcounter11h); 35 | reg!(0xB0C, mhpmcounter12, mhpmcounter12h); 36 | reg!(0xB0D, mhpmcounter13, mhpmcounter13h); 37 | reg!(0xB0E, mhpmcounter14, mhpmcounter14h); 38 | reg!(0xB0F, mhpmcounter15, mhpmcounter15h); 39 | reg!(0xB10, mhpmcounter16, mhpmcounter16h); 40 | reg!(0xB11, mhpmcounter17, mhpmcounter17h); 41 | reg!(0xB12, mhpmcounter18, mhpmcounter18h); 42 | reg!(0xB13, mhpmcounter19, mhpmcounter19h); 43 | reg!(0xB14, mhpmcounter20, mhpmcounter20h); 44 | reg!(0xB15, mhpmcounter21, mhpmcounter21h); 45 | reg!(0xB16, mhpmcounter22, mhpmcounter22h); 46 | reg!(0xB17, mhpmcounter23, mhpmcounter23h); 47 | reg!(0xB18, mhpmcounter24, mhpmcounter24h); 48 | reg!(0xB19, mhpmcounter25, mhpmcounter25h); 49 | reg!(0xB1A, mhpmcounter26, mhpmcounter26h); 50 | reg!(0xB1B, mhpmcounter27, mhpmcounter27h); 51 | reg!(0xB1C, mhpmcounter28, mhpmcounter28h); 52 | reg!(0xB1D, mhpmcounter29, mhpmcounter29h); 53 | reg!(0xB1E, mhpmcounter30, mhpmcounter30h); 54 | reg!(0xB1F, mhpmcounter31, mhpmcounter31h); 55 | 56 | regh!(0xB83, mhpmcounter3h); 57 | regh!(0xB84, mhpmcounter4h); 58 | regh!(0xB85, mhpmcounter5h); 59 | regh!(0xB86, mhpmcounter6h); 60 | regh!(0xB87, mhpmcounter7h); 61 | regh!(0xB88, mhpmcounter8h); 62 | regh!(0xB89, mhpmcounter9h); 63 | regh!(0xB8A, mhpmcounter10h); 64 | regh!(0xB8B, mhpmcounter11h); 65 | regh!(0xB8C, mhpmcounter12h); 66 | regh!(0xB8D, mhpmcounter13h); 67 | regh!(0xB8E, mhpmcounter14h); 68 | regh!(0xB8F, mhpmcounter15h); 69 | regh!(0xB90, mhpmcounter16h); 70 | regh!(0xB91, mhpmcounter17h); 71 | regh!(0xB92, mhpmcounter18h); 72 | regh!(0xB93, mhpmcounter19h); 73 | regh!(0xB94, mhpmcounter20h); 74 | regh!(0xB95, mhpmcounter21h); 75 | regh!(0xB96, mhpmcounter22h); 76 | regh!(0xB97, mhpmcounter23h); 77 | regh!(0xB98, mhpmcounter24h); 78 | regh!(0xB99, mhpmcounter25h); 79 | regh!(0xB9A, mhpmcounter26h); 80 | regh!(0xB9B, mhpmcounter27h); 81 | regh!(0xB9C, mhpmcounter28h); 82 | regh!(0xB9D, mhpmcounter29h); 83 | regh!(0xB9E, mhpmcounter30h); 84 | regh!(0xB9F, mhpmcounter31h); 85 | -------------------------------------------------------------------------------- /riscv/src/register/mhpmeventx.rs: -------------------------------------------------------------------------------- 1 | macro_rules! reg { 2 | ( 3 | $addr:expr, $csr:ident 4 | ) => { 5 | /// Machine performance-monitoring event selector 6 | pub mod $csr { 7 | read_csr_as_usize!($addr); 8 | write_csr_as_usize!($addr); 9 | } 10 | }; 11 | } 12 | 13 | reg!(0x323, mhpmevent3); 14 | reg!(0x324, mhpmevent4); 15 | reg!(0x325, mhpmevent5); 16 | reg!(0x326, mhpmevent6); 17 | reg!(0x327, mhpmevent7); 18 | reg!(0x328, mhpmevent8); 19 | reg!(0x329, mhpmevent9); 20 | reg!(0x32A, mhpmevent10); 21 | reg!(0x32B, mhpmevent11); 22 | reg!(0x32C, mhpmevent12); 23 | reg!(0x32D, mhpmevent13); 24 | reg!(0x32E, mhpmevent14); 25 | reg!(0x32F, mhpmevent15); 26 | reg!(0x330, mhpmevent16); 27 | reg!(0x331, mhpmevent17); 28 | reg!(0x332, mhpmevent18); 29 | reg!(0x333, mhpmevent19); 30 | reg!(0x334, mhpmevent20); 31 | reg!(0x335, mhpmevent21); 32 | reg!(0x336, mhpmevent22); 33 | reg!(0x337, mhpmevent23); 34 | reg!(0x338, mhpmevent24); 35 | reg!(0x339, mhpmevent25); 36 | reg!(0x33A, mhpmevent26); 37 | reg!(0x33B, mhpmevent27); 38 | reg!(0x33C, mhpmevent28); 39 | reg!(0x33D, mhpmevent29); 40 | reg!(0x33E, mhpmevent30); 41 | reg!(0x33F, mhpmevent31); 42 | -------------------------------------------------------------------------------- /riscv/src/register/mideleg.rs: -------------------------------------------------------------------------------- 1 | //! mideleg register 2 | 3 | read_write_csr! { 4 | /// `mideleg` register 5 | Mideleg: 0x303, 6 | mask: 0x222, 7 | } 8 | 9 | read_write_csr_field! { 10 | Mideleg, 11 | /// Supervisor Software Interrupt Delegate 12 | ssoft: 1, 13 | } 14 | 15 | read_write_csr_field! { 16 | Mideleg, 17 | /// Supervisor Timer Interrupt Delegate 18 | stimer: 5, 19 | } 20 | 21 | read_write_csr_field! { 22 | Mideleg, 23 | /// Supervisor External Interrupt Delegate 24 | sext: 9, 25 | } 26 | 27 | set!(0x303); 28 | clear!(0x303); 29 | 30 | set_clear_csr!( 31 | /// Supervisor Software Interrupt Delegate 32 | , set_ssoft, clear_ssoft, 1 << 1); 33 | set_clear_csr!( 34 | /// Supervisor Timer Interrupt Delegate 35 | , set_stimer, clear_stimer, 1 << 5); 36 | set_clear_csr!( 37 | /// Supervisor External Interrupt Delegate 38 | , set_sext, clear_sext, 1 << 9); 39 | 40 | #[cfg(test)] 41 | mod tests { 42 | use super::*; 43 | 44 | #[test] 45 | fn test_mideleg() { 46 | let mut m = Mideleg::from_bits(0); 47 | 48 | test_csr_field!(m, ssoft); 49 | test_csr_field!(m, stimer); 50 | test_csr_field!(m, sext); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /riscv/src/register/mie.rs: -------------------------------------------------------------------------------- 1 | //! mie register 2 | 3 | read_write_csr! { 4 | /// `mie` register 5 | Mie: 0x304, 6 | mask: 0xaaa, 7 | } 8 | 9 | read_write_csr_field! { 10 | Mie, 11 | /// Supervisor Software Interrupt Enable 12 | ssoft: 1, 13 | } 14 | 15 | read_write_csr_field! { 16 | Mie, 17 | /// Machine Software Interrupt Enable 18 | msoft: 3, 19 | } 20 | 21 | read_write_csr_field! { 22 | Mie, 23 | /// Supervisor Timer Interrupt Enable 24 | stimer: 5, 25 | } 26 | 27 | read_write_csr_field! { 28 | Mie, 29 | /// Machine Timer Interrupt Enable 30 | mtimer: 7, 31 | } 32 | 33 | read_write_csr_field! { 34 | Mie, 35 | /// Supervisor External Interrupt Enable 36 | sext: 9, 37 | } 38 | 39 | read_write_csr_field! { 40 | Mie, 41 | /// Machine External Interrupt Enable 42 | mext: 11, 43 | } 44 | 45 | set!(0x304); 46 | clear!(0x304); 47 | 48 | set_clear_csr!( 49 | /// Supervisor Software Interrupt Enable 50 | , set_ssoft, clear_ssoft, 1 << 1); 51 | set_clear_csr!( 52 | /// Machine Software Interrupt Enable 53 | , set_msoft, clear_msoft, 1 << 3); 54 | set_clear_csr!( 55 | /// Supervisor Timer Interrupt Enable 56 | , set_stimer, clear_stimer, 1 << 5); 57 | set_clear_csr!( 58 | /// Machine Timer Interrupt Enable 59 | , set_mtimer, clear_mtimer, 1 << 7); 60 | set_clear_csr!( 61 | /// Supervisor External Interrupt Enable 62 | , set_sext, clear_sext, 1 << 9); 63 | set_clear_csr!( 64 | /// Machine External Interrupt Enable 65 | , set_mext, clear_mext, 1 << 11); 66 | 67 | #[cfg(test)] 68 | mod tests { 69 | use super::*; 70 | 71 | #[test] 72 | fn test_mie() { 73 | let mut m = Mie::from_bits(0); 74 | 75 | test_csr_field!(m, ssoft); 76 | test_csr_field!(m, msoft); 77 | test_csr_field!(m, stimer); 78 | test_csr_field!(m, mtimer); 79 | test_csr_field!(m, sext); 80 | test_csr_field!(m, mext); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /riscv/src/register/mimpid.rs: -------------------------------------------------------------------------------- 1 | //! mimpid register 2 | 3 | read_only_csr! { 4 | /// `mimpid` register 5 | Mimpid: 0xF13, 6 | mask: 0xffff_ffff, 7 | sentinel: 0, 8 | } 9 | -------------------------------------------------------------------------------- /riscv/src/register/minstret.rs: -------------------------------------------------------------------------------- 1 | //! minstret register 2 | 3 | read_csr_as_usize!(0xB02); 4 | write_csr_as_usize!(0xB02); 5 | read_composite_csr!(super::minstreth::read(), read()); 6 | write_composite_csr!(super::minstreth::write, write); 7 | -------------------------------------------------------------------------------- /riscv/src/register/minstreth.rs: -------------------------------------------------------------------------------- 1 | //! minstreth register 2 | 3 | read_csr_as_usize_rv32!(0xB82); 4 | write_csr_as_usize_rv32!(0xB82); 5 | -------------------------------------------------------------------------------- /riscv/src/register/mip.rs: -------------------------------------------------------------------------------- 1 | //! mip register 2 | 3 | read_write_csr! { 4 | /// `mip` register 5 | Mip: 0x344, 6 | mask: 0xaaa, 7 | } 8 | 9 | read_write_csr_field! { 10 | Mip, 11 | /// Supervisor Software Interrupt Pending 12 | ssoft: 1, 13 | } 14 | 15 | read_only_csr_field! { 16 | Mip, 17 | /// Machine Software Interrupt Pending 18 | msoft: 3, 19 | } 20 | 21 | read_write_csr_field! { 22 | Mip, 23 | /// Supervisor Timer Interrupt Pending 24 | stimer: 5, 25 | } 26 | 27 | read_only_csr_field! { 28 | Mip, 29 | /// Machine Timer Interrupt Pending 30 | mtimer: 7, 31 | } 32 | 33 | read_write_csr_field! { 34 | Mip, 35 | /// Supervisor External Interrupt Pending 36 | sext: 9, 37 | } 38 | 39 | read_only_csr_field! { 40 | Mip, 41 | /// Machine External Interrupt Pending 42 | mext: 11, 43 | } 44 | 45 | set!(0x344); 46 | clear!(0x344); 47 | 48 | set_clear_csr!( 49 | /// Supervisor Software Interrupt Pending 50 | , set_ssoft, clear_ssoft, 1 << 1); 51 | set_clear_csr!( 52 | /// Supervisor Timer Interrupt Pending 53 | , set_stimer, clear_stimer, 1 << 5); 54 | set_clear_csr!( 55 | /// Supervisor External Interrupt Pending 56 | , set_sext, clear_sext, 1 << 9); 57 | 58 | #[cfg(test)] 59 | mod tests { 60 | use super::*; 61 | 62 | #[test] 63 | fn test_mip() { 64 | let mut m = Mip::from_bits(0); 65 | 66 | test_csr_field!(m, ssoft); 67 | test_csr_field!(m, stimer); 68 | test_csr_field!(m, sext); 69 | 70 | assert!(!m.msoft()); 71 | assert!(!m.mtimer()); 72 | assert!(!m.mext()); 73 | 74 | assert!(Mip::from_bits(1 << 3).msoft()); 75 | assert!(Mip::from_bits(1 << 7).mtimer()); 76 | assert!(Mip::from_bits(1 << 11).mext()); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /riscv/src/register/misa.rs: -------------------------------------------------------------------------------- 1 | //! misa register 2 | 3 | #[cfg(target_arch = "riscv32")] 4 | read_only_csr! { 5 | /// `misa` register 6 | Misa: 0x301, 7 | mask: 0xc3ff_ffff, 8 | sentinel: 0, 9 | } 10 | 11 | #[cfg(not(target_arch = "riscv32"))] 12 | read_only_csr! { 13 | /// `misa` register 14 | Misa: 0x301, 15 | mask: 0xc000_0000_03ff_ffff, 16 | sentinel: 0, 17 | } 18 | 19 | csr_field_enum! { 20 | /// Base integer ISA width 21 | XLEN { 22 | default: XLEN32, 23 | XLEN32 = 1, 24 | XLEN64 = 2, 25 | XLEN128 = 3, 26 | } 27 | } 28 | 29 | #[cfg(target_arch = "riscv32")] 30 | read_only_csr_field! { 31 | Misa, 32 | /// Effective xlen in M-mode (i.e., `MXLEN`). 33 | mxl, 34 | XLEN: [30:31], 35 | } 36 | 37 | #[cfg(not(target_arch = "riscv32"))] 38 | read_only_csr_field! { 39 | Misa, 40 | /// Effective xlen in M-mode (i.e., `MXLEN`). 41 | mxl, 42 | XLEN: [62:63], 43 | } 44 | 45 | impl Misa { 46 | /// Returns true when a given extension is implemented. 47 | /// 48 | /// # Example 49 | /// 50 | /// ```no_run 51 | /// let misa = unsafe { riscv::register::misa::try_read() }.unwrap(); 52 | /// assert!(misa.has_extension('A')); // panics if atomic extension is not implemented 53 | /// ``` 54 | #[inline] 55 | pub fn has_extension(&self, extension: char) -> bool { 56 | let bit = ext_char_to_bit(extension); 57 | if bit > 25 { 58 | return false; 59 | } 60 | self.bits() & (1 << bit) == (1 << bit) 61 | } 62 | } 63 | 64 | #[inline] 65 | const fn ext_char_to_bit(extension: char) -> u8 { 66 | (extension as u8).saturating_sub(b'A') 67 | } 68 | 69 | #[cfg(test)] 70 | mod tests { 71 | use super::*; 72 | use crate::result::Error; 73 | 74 | #[test] 75 | fn test_misa() { 76 | (1..=3) 77 | .zip([XLEN::XLEN32, XLEN::XLEN64, XLEN::XLEN128]) 78 | .for_each(|(raw, exp_xlen)| { 79 | assert_eq!(XLEN::try_from(raw), Ok(exp_xlen)); 80 | assert_eq!(usize::from(exp_xlen), raw); 81 | 82 | let misa = Misa::from_bits(raw << (usize::BITS - 2)); 83 | assert_eq!(misa.try_mxl(), Ok(exp_xlen)); 84 | assert_eq!(misa.mxl(), exp_xlen); 85 | }); 86 | 87 | (0..62).map(|b| 1 << b).for_each(|invalid_mxl| { 88 | assert_eq!( 89 | Misa::from_bits(invalid_mxl).try_mxl(), 90 | Err(Error::InvalidVariant(0)) 91 | ); 92 | }); 93 | 94 | ('A'..='Z').for_each(|ext| { 95 | assert!(!Misa::from_bits(0).has_extension(ext)); 96 | assert!(Misa::from_bits(1 << ext_char_to_bit(ext)).has_extension(ext)); 97 | }); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /riscv/src/register/mscratch.rs: -------------------------------------------------------------------------------- 1 | //! mscratch register 2 | 3 | read_csr_as_usize!(0x340); 4 | write_csr_as_usize!(0x340); 5 | -------------------------------------------------------------------------------- /riscv/src/register/mstatush.rs: -------------------------------------------------------------------------------- 1 | //! mstatush register (RISCV-32 only) 2 | 3 | pub use super::mstatus::Endianness; 4 | 5 | read_write_csr! { 6 | /// mstatus register 7 | Mstatush: 0x310, 8 | mask: 0x30, 9 | } 10 | 11 | read_write_csr_field! { 12 | Mstatush, 13 | /// S-mode non-instruction-fetch memory endianness 14 | sbe, 15 | Endianness: [4:4], 16 | } 17 | 18 | read_write_csr_field! { 19 | Mstatush, 20 | /// M-mode non-instruction-fetch memory endianness 21 | mbe, 22 | Endianness: [5:5], 23 | } 24 | 25 | set_rv32!(0x310); 26 | clear_rv32!(0x310); 27 | 28 | /// Set S-mode non-instruction-fetch memory endianness 29 | #[inline] 30 | pub unsafe fn set_sbe(endianness: Endianness) { 31 | match endianness { 32 | Endianness::BigEndian => _set(1 << 4), 33 | Endianness::LittleEndian => _clear(1 << 4), 34 | } 35 | } 36 | 37 | /// Set M-mode non-instruction-fetch memory endianness 38 | #[inline] 39 | pub unsafe fn set_mbe(endianness: Endianness) { 40 | match endianness { 41 | Endianness::BigEndian => _set(1 << 5), 42 | Endianness::LittleEndian => _clear(1 << 5), 43 | } 44 | } 45 | 46 | #[cfg(test)] 47 | mod tests { 48 | use super::*; 49 | 50 | #[test] 51 | fn test_mstatush() { 52 | let mut m = Mstatush::from_bits(0); 53 | 54 | [Endianness::LittleEndian, Endianness::BigEndian] 55 | .into_iter() 56 | .for_each(|endianness| { 57 | test_csr_field!(m, sbe: endianness); 58 | test_csr_field!(m, mbe: endianness); 59 | }); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /riscv/src/register/mtinst.rs: -------------------------------------------------------------------------------- 1 | //! mtinst register. 2 | 3 | const MASK: usize = usize::MAX; 4 | 5 | read_write_csr! { 6 | /// mtinst register 7 | Mtinst: 0x34a, 8 | mask: MASK, 9 | } 10 | 11 | read_write_csr_field! { 12 | Mtinst, 13 | /// Trapped instruction `opcode` field. 14 | opcode: [0:6], 15 | } 16 | 17 | read_write_csr_field! { 18 | Mtinst, 19 | /// Trapped instruction `rd` field for load instructions. 20 | rd: [7:11], 21 | } 22 | 23 | read_write_csr_field! { 24 | Mtinst, 25 | /// Trapped instruction `funct3` field. 26 | funct3: [12:14], 27 | } 28 | 29 | read_write_csr_field! { 30 | Mtinst, 31 | /// Trapped instruction `address offset` field. 32 | address_offset: [15:19], 33 | } 34 | 35 | read_write_csr_field! { 36 | Mtinst, 37 | /// Trapped instruction `rs2` field for store instructions. 38 | rs2: [20:24], 39 | } 40 | 41 | read_write_csr_field! { 42 | Mtinst, 43 | /// Trapped instruction `rl` field for atomic instructions. 44 | rl: 25, 45 | } 46 | 47 | read_write_csr_field! { 48 | Mtinst, 49 | /// Trapped instruction `aq` field for atomic instructions. 50 | aq: 26, 51 | } 52 | 53 | read_write_csr_field! { 54 | Mtinst, 55 | /// Trapped instruction `funct5` field for atomic instructions. 56 | funct5: [27:31], 57 | } 58 | 59 | read_write_csr_field! { 60 | Mtinst, 61 | /// Trapped instruction `funct7` field for virtual machine instructions. 62 | funct7: [25:31], 63 | } 64 | 65 | set!(0x34a); 66 | clear!(0x34a); 67 | 68 | #[cfg(test)] 69 | mod tests { 70 | use super::*; 71 | 72 | #[test] 73 | fn test_mtinst() { 74 | (1..=usize::BITS) 75 | .map(|r| ((1u128 << r) - 1) as usize) 76 | .for_each(|bits| { 77 | let reset = 0; 78 | let mut mtinst = Mtinst::from_bits(bits); 79 | 80 | test_csr_field!(mtinst, opcode: [0, 6], reset); 81 | test_csr_field!(mtinst, rd: [7, 11], reset); 82 | test_csr_field!(mtinst, funct3: [12, 14], reset); 83 | test_csr_field!(mtinst, address_offset: [15, 19], reset); 84 | test_csr_field!(mtinst, rs2: [20, 24], reset); 85 | test_csr_field!(mtinst, rl); 86 | test_csr_field!(mtinst, aq); 87 | test_csr_field!(mtinst, funct5: [27, 31], reset); 88 | test_csr_field!(mtinst, funct7: [25, 31], reset); 89 | }); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /riscv/src/register/mtval.rs: -------------------------------------------------------------------------------- 1 | //! mtval register 2 | 3 | read_csr_as_usize!(0x343); 4 | -------------------------------------------------------------------------------- /riscv/src/register/mtvec.rs: -------------------------------------------------------------------------------- 1 | //! mtvec register 2 | 3 | use crate::result::{Error, Result}; 4 | 5 | const MASK: usize = usize::MAX; 6 | const TRAP_MASK: usize = 0b11; 7 | 8 | read_write_csr! { 9 | /// mtvec register 10 | Mtvec: 0x305, 11 | mask: MASK, 12 | } 13 | 14 | csr_field_enum! { 15 | /// Trap mode 16 | TrapMode { 17 | default: Direct, 18 | Direct = 0, 19 | Vectored = 1, 20 | } 21 | } 22 | 23 | read_write_csr_field! { 24 | Mtvec, 25 | /// Accesses the trap-vector mode. 26 | trap_mode, 27 | TrapMode: [0:1], 28 | } 29 | 30 | impl Mtvec { 31 | /// Returns the trap-vector base-address 32 | #[inline] 33 | pub const fn address(&self) -> usize { 34 | self.bits & !TRAP_MASK 35 | } 36 | 37 | /// Sets the trap-vector base-address. 38 | /// 39 | /// # Note 40 | /// 41 | /// Panics if the address is not aligned to 4-bytes. 42 | #[inline] 43 | pub fn set_address(&mut self, address: usize) { 44 | self.try_set_address(address).unwrap(); 45 | } 46 | 47 | /// Attempts to set the trap-vector base-address. 48 | /// 49 | /// # Note 50 | /// 51 | /// Returns an error if the address is not aligned to 4-bytes. 52 | #[inline] 53 | pub fn try_set_address(&mut self, address: usize) -> Result<()> { 54 | // check for four-byte alignment 55 | if (address & TRAP_MASK) != 0 { 56 | Err(Error::InvalidFieldVariant { 57 | field: "mtvec::address", 58 | value: address, 59 | }) 60 | } else { 61 | self.bits = address | (self.bits & TRAP_MASK); 62 | Ok(()) 63 | } 64 | } 65 | } 66 | 67 | #[cfg(test)] 68 | mod tests { 69 | use super::*; 70 | 71 | #[test] 72 | fn test_mtvec() { 73 | let mut m = Mtvec::from_bits(0); 74 | 75 | (1..=usize::BITS) 76 | .map(|r| (((1u128 << r) - 1) as usize) & !TRAP_MASK) 77 | .for_each(|address| { 78 | m.set_address(address); 79 | assert_eq!(m.address(), address); 80 | 81 | assert_eq!(m.try_set_address(address), Ok(())); 82 | assert_eq!(m.address(), address); 83 | }); 84 | 85 | (1..=usize::BITS) 86 | .filter_map(|r| match ((1u128 << r) - 1) as usize { 87 | addr if (addr & TRAP_MASK) != 0 => Some(addr), 88 | _ => None, 89 | }) 90 | .for_each(|address| { 91 | assert_eq!( 92 | m.try_set_address(address), 93 | Err(Error::InvalidFieldVariant { 94 | field: "mtvec::address", 95 | value: address, 96 | }) 97 | ); 98 | }); 99 | 100 | test_csr_field!(m, trap_mode: TrapMode::Direct); 101 | test_csr_field!(m, trap_mode: TrapMode::Vectored); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /riscv/src/register/mvendorid.rs: -------------------------------------------------------------------------------- 1 | //! mvendorid register 2 | 3 | read_only_csr! { 4 | /// `mvendorid` register 5 | Mvendorid: 0xF11, 6 | mask: 0xffff_ffff, 7 | sentinel: 0, 8 | } 9 | 10 | read_only_csr_field! { 11 | Mvendorid, 12 | /// Represents the number of continuation bytes (`0x7f`) in the JEDEC manufacturer ID. 13 | bank: [7:31], 14 | } 15 | 16 | read_only_csr_field! { 17 | Mvendorid, 18 | /// Represents the final offset field in the JEDEC manufacturer ID. 19 | /// 20 | /// # Note 21 | /// 22 | /// The encoded value returned by `offset` does not include the odd parity bit (`0x80`). 23 | offset: [0:6], 24 | } 25 | 26 | impl Mvendorid { 27 | /// Represents the JEDEC manufacture continuation byte. 28 | pub const CONTINUATION: u8 = 0x7f; 29 | 30 | /// Gets the decoded JEDEC manufacturer ID from the `mvendorid` value. 31 | /// 32 | /// # Note 33 | /// 34 | /// This function returns an iterator over the decoded bytes. 35 | /// 36 | /// An iterator is needed because the encoding can theoretically return a max count (`0x1ff_ffff`) of continuation bytes (`0x7f`). 37 | /// 38 | /// The final byte in the iterator is the `offset`, including the odd parity bit (set only if even). 39 | pub fn jedec_manufacturer(&self) -> impl Iterator { 40 | const DONE: usize = usize::MAX; 41 | 42 | let mut bank = self.bank(); 43 | let offset = self.offset(); 44 | 45 | core::iter::from_fn(move || match bank { 46 | DONE => None, 47 | 0 => { 48 | bank = DONE; 49 | let parity = ((1 - (offset.count_ones() % 2)) << 7) as usize; 50 | Some((parity | offset) as u8) 51 | } 52 | _ => { 53 | bank -= 1; 54 | Some(Self::CONTINUATION) 55 | } 56 | }) 57 | } 58 | } 59 | 60 | #[cfg(test)] 61 | mod tests { 62 | use super::*; 63 | 64 | #[test] 65 | fn test_mvendorid() { 66 | (0..u32::BITS) 67 | .map(|r| ((1u64 << r) - 1) as usize) 68 | .for_each(|raw| { 69 | let exp_bank = raw >> 7; 70 | let exp_offset = raw & (Mvendorid::CONTINUATION as usize); 71 | let exp_parity = ((1 - (exp_offset.count_ones() % 2)) << 7) as u8; 72 | let exp_mvendorid = Mvendorid::from_bits(raw); 73 | 74 | assert_eq!(exp_mvendorid.bank(), exp_bank); 75 | assert_eq!(exp_mvendorid.offset(), exp_offset); 76 | 77 | let mut jedec_iter = exp_mvendorid.jedec_manufacturer(); 78 | (0..exp_bank) 79 | .for_each(|_| assert_eq!(jedec_iter.next(), Some(Mvendorid::CONTINUATION))); 80 | assert_eq!(jedec_iter.next(), Some(exp_parity | (exp_offset as u8))); 81 | assert_eq!(jedec_iter.next(), None); 82 | }); 83 | 84 | // ISA example used as a concrete test vector. 85 | 86 | let exp_bank = 0xc; 87 | let exp_offset = 0x0a; 88 | let exp_decoded_offset = 0x8a; 89 | let raw_mvendorid = 0x60a; 90 | let exp_mvendorid = Mvendorid::from_bits(raw_mvendorid); 91 | 92 | assert_eq!(exp_mvendorid.bank(), exp_bank); 93 | assert_eq!(exp_mvendorid.offset(), exp_offset); 94 | 95 | let mut jedec_iter = exp_mvendorid.jedec_manufacturer(); 96 | (0..exp_bank).for_each(|_| assert_eq!(jedec_iter.next(), Some(Mvendorid::CONTINUATION))); 97 | assert_eq!(jedec_iter.next(), Some(exp_decoded_offset)); 98 | assert_eq!(jedec_iter.next(), None); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /riscv/src/register/pmpaddrx.rs: -------------------------------------------------------------------------------- 1 | macro_rules! reg { 2 | ( 3 | $addr:expr, $csr:ident 4 | ) => { 5 | /// Physical memory protection address register 6 | pub mod $csr { 7 | read_csr_as_usize!($addr); 8 | write_csr_as_usize!($addr); 9 | } 10 | }; 11 | } 12 | 13 | reg!(0x3B0, pmpaddr0); 14 | reg!(0x3B1, pmpaddr1); 15 | reg!(0x3B2, pmpaddr2); 16 | reg!(0x3B3, pmpaddr3); 17 | reg!(0x3B4, pmpaddr4); 18 | reg!(0x3B5, pmpaddr5); 19 | reg!(0x3B6, pmpaddr6); 20 | reg!(0x3B7, pmpaddr7); 21 | reg!(0x3B8, pmpaddr8); 22 | reg!(0x3B9, pmpaddr9); 23 | reg!(0x3BA, pmpaddr10); 24 | reg!(0x3BB, pmpaddr11); 25 | reg!(0x3BC, pmpaddr12); 26 | reg!(0x3BD, pmpaddr13); 27 | reg!(0x3BE, pmpaddr14); 28 | reg!(0x3BF, pmpaddr15); 29 | -------------------------------------------------------------------------------- /riscv/src/register/scontext.rs: -------------------------------------------------------------------------------- 1 | //! `scontext` register. 2 | 3 | #[cfg(target_arch = "riscv32")] 4 | const MASK: usize = 0xffff; 5 | #[cfg(not(target_arch = "riscv32"))] 6 | const MASK: usize = 0xffff_ffff; 7 | 8 | read_write_csr! { 9 | /// `scontext` register. 10 | Scontext: 0x5a8, 11 | mask: MASK, 12 | } 13 | 14 | set!(0x5a8); 15 | clear!(0x5a8); 16 | 17 | #[cfg(target_arch = "riscv32")] 18 | read_write_csr_field! { 19 | Scontext, 20 | /// Represents the `data` context number of the `scontext` CSR. 21 | data: [0:15], 22 | } 23 | 24 | #[cfg(not(target_arch = "riscv32"))] 25 | read_write_csr_field! { 26 | Scontext, 27 | /// Represents the `data` context number of the `scontext` CSR. 28 | data: [0:31], 29 | } 30 | 31 | #[cfg(test)] 32 | mod tests { 33 | use super::*; 34 | 35 | #[test] 36 | fn test_scontext() { 37 | #[cfg(target_arch = "riscv32")] 38 | const DATA_BITS: usize = 16; 39 | #[cfg(not(target_arch = "riscv32"))] 40 | const DATA_BITS: usize = 32; 41 | 42 | let mut scontext = Scontext::from_bits(0); 43 | 44 | (1..=DATA_BITS) 45 | .map(|b| ((1u64 << b) - 1) as usize) 46 | .for_each(|data| { 47 | scontext.set_data(data); 48 | assert_eq!(scontext.data(), data); 49 | assert_eq!(scontext.bits(), data); 50 | }); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /riscv/src/register/scounteren.rs: -------------------------------------------------------------------------------- 1 | //! scounteren register 2 | 3 | use crate::result::{Error, Result}; 4 | 5 | read_write_csr! { 6 | /// scounteren register 7 | Scounteren: 0x106, 8 | mask: 0xffff_ffff, 9 | } 10 | 11 | read_write_csr_field! { 12 | Scounteren, 13 | /// User "cycle\[h\]" Enable 14 | cy: 0, 15 | } 16 | 17 | read_write_csr_field! { 18 | Scounteren, 19 | /// User "time\[h\]" Enable 20 | tm: 1, 21 | } 22 | 23 | read_write_csr_field! { 24 | Scounteren, 25 | /// User "instret\[h]\" Enable 26 | ir: 2, 27 | } 28 | 29 | read_write_csr_field! { 30 | Scounteren, 31 | /// User "hpm\[x\]" Enable (bits 3-31) 32 | hpm: 3..=31, 33 | } 34 | 35 | set!(0x106); 36 | clear!(0x106); 37 | 38 | set_clear_csr!( 39 | /// User cycle Enable 40 | , set_cy, clear_cy, 1 << 0); 41 | 42 | set_clear_csr!( 43 | /// User time Enable 44 | , set_tm, clear_tm, 1 << 1); 45 | 46 | set_clear_csr!( 47 | /// User instret Enable 48 | , set_ir, clear_ir, 1 << 2); 49 | 50 | /// Sets the "hpm\[x\]" enable (bits 3-31). 51 | /// 52 | /// # Note 53 | /// 54 | /// Panics if `index` is out-of-bounds. 55 | #[inline] 56 | pub unsafe fn set_hpm(index: usize) { 57 | try_set_hpm(index).unwrap(); 58 | } 59 | 60 | /// Attempts to set the "hpm\[x\]" enable (bits 3-31). 61 | #[inline] 62 | pub unsafe fn try_set_hpm(index: usize) -> Result<()> { 63 | if (3..32).contains(&index) { 64 | _try_set(1 << index) 65 | } else { 66 | Err(Error::IndexOutOfBounds { 67 | index, 68 | min: 3, 69 | max: 31, 70 | }) 71 | } 72 | } 73 | 74 | /// Clears the "hpm\[x\]" enable (bits 3-31). 75 | /// 76 | /// # Note 77 | /// 78 | /// Panics if `index` is out-of-bounds. 79 | #[inline] 80 | pub unsafe fn clear_hpm(index: usize) { 81 | try_clear_hpm(index).unwrap() 82 | } 83 | 84 | /// Attempts to clear the "hpm\[x\]" enable (bits 3-31). 85 | #[inline] 86 | pub unsafe fn try_clear_hpm(index: usize) -> Result<()> { 87 | if (3..32).contains(&index) { 88 | _try_clear(1 << index) 89 | } else { 90 | Err(Error::IndexOutOfBounds { 91 | index, 92 | min: 3, 93 | max: 31, 94 | }) 95 | } 96 | } 97 | 98 | #[cfg(test)] 99 | mod tests { 100 | use super::*; 101 | 102 | #[test] 103 | fn test_scounteren() { 104 | const HPM_MIN: usize = 3; 105 | const HPM_MAX: usize = 31; 106 | 107 | let mut scounteren = Scounteren::from_bits(0); 108 | 109 | test_csr_field!(scounteren, cy); 110 | test_csr_field!(scounteren, tm); 111 | test_csr_field!(scounteren, ir); 112 | 113 | (HPM_MIN..=HPM_MAX).for_each(|index| { 114 | test_csr_field!(scounteren, hpm, index); 115 | }); 116 | 117 | (0..usize::BITS as usize) 118 | .filter(|&i| !(HPM_MIN..=HPM_MAX).any(|idx| idx == i)) 119 | .for_each(|index| { 120 | let err = Error::IndexOutOfBounds { 121 | index, 122 | min: 3, 123 | max: 31, 124 | }; 125 | test_csr_field!(scounteren, hpm, index, err) 126 | }); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /riscv/src/register/senvcfg.rs: -------------------------------------------------------------------------------- 1 | //! `senvcfg` register. 2 | 3 | #[cfg(target_arch = "riscv32")] 4 | const MASK: usize = 0xf5; 5 | #[cfg(not(target_arch = "riscv32"))] 6 | const MASK: usize = 0x3_0000_00fd; 7 | 8 | read_write_csr! { 9 | /// `senvcfg` register. 10 | Senvcfg: 0x10a, 11 | mask: MASK, 12 | } 13 | 14 | set!(0x10a); 15 | clear!(0x10a); 16 | 17 | read_write_csr_field! { 18 | Senvcfg, 19 | /// Gets the `fiom` (Fence of I/O Implies Memory) field value. 20 | fiom: 0, 21 | } 22 | 23 | read_write_csr_field! { 24 | Senvcfg, 25 | /// Gets the `lpe` (Landing Pad Enable) field value. 26 | lpe: 2, 27 | } 28 | 29 | #[cfg(not(target_arch = "riscv32"))] 30 | read_write_csr_field! { 31 | Senvcfg, 32 | /// Gets the `sse` (Shadow Stack Enable) field value. 33 | sse: 3, 34 | } 35 | 36 | csr_field_enum! { 37 | /// Represents CBIE (Cache Block Invalidate instruction Enable) field of the `senvcfg` CSR. 38 | Cbie { 39 | default: IllegalInstruction, 40 | /// The instruction takes an illegal instruction exception. 41 | IllegalInstruction = 0b00, 42 | /// The instruction is executed and performs a flush operation. 43 | Flush = 0b01, 44 | /// The instruction is executed and performs an invalidate operation. 45 | Invalidate = 0b11, 46 | } 47 | } 48 | 49 | read_write_csr_field! { 50 | Senvcfg, 51 | /// Gets the `cbie` (Cache Block Invalidate Enable) field value. 52 | cbie, 53 | Cbie: [4:5], 54 | } 55 | 56 | read_write_csr_field! { 57 | Senvcfg, 58 | /// Gets the `cbcfe` (Cache Block Clean and Flush Enable) field value. 59 | cbcfe: 6, 60 | } 61 | 62 | read_write_csr_field! { 63 | Senvcfg, 64 | /// Gets the `cbze` (Cache Block Zero Enable) field value. 65 | cbze: 7, 66 | } 67 | 68 | #[cfg(not(target_arch = "riscv32"))] 69 | csr_field_enum! { 70 | /// Represents PMM (Pointer Masking Mode) field of the `senvcfg` CSR. 71 | Pmm { 72 | default: Disabled, 73 | /// Pointer masking is disabled (PMLEN=0). 74 | Disabled = 0b00, 75 | /// Pointer masking is enabled with PMLEN=XLEN-57 (PMLEN=7 on RV64). 76 | Mask7bit = 0b10, 77 | /// Pointer masking is enabled with PMLEN=XLEN-48 (PMLEN=16 on RV64). 78 | Mask16bit = 0b11, 79 | } 80 | } 81 | 82 | #[cfg(not(target_arch = "riscv32"))] 83 | read_write_csr_field! { 84 | Senvcfg, 85 | /// Gets the `pmm` (Pointer Masking Mode) field value. 86 | pmm, 87 | Pmm: [32:33], 88 | } 89 | 90 | #[cfg(test)] 91 | mod tests { 92 | use super::*; 93 | 94 | #[test] 95 | fn test_senvcfg() { 96 | let mut senvcfg = Senvcfg::from_bits(0); 97 | 98 | test_csr_field!(senvcfg, fiom); 99 | test_csr_field!(senvcfg, lpe); 100 | 101 | #[cfg(not(target_arch = "riscv32"))] 102 | test_csr_field!(senvcfg, sse); 103 | 104 | [Cbie::IllegalInstruction, Cbie::Flush, Cbie::Invalidate] 105 | .into_iter() 106 | .for_each(|cbie| { 107 | test_csr_field!(senvcfg, cbie: cbie); 108 | }); 109 | 110 | test_csr_field!(senvcfg, cbcfe); 111 | test_csr_field!(senvcfg, cbze); 112 | 113 | #[cfg(not(target_arch = "riscv32"))] 114 | [Pmm::Disabled, Pmm::Mask7bit, Pmm::Mask16bit] 115 | .into_iter() 116 | .for_each(|pmm| { 117 | test_csr_field!(senvcfg, pmm: pmm); 118 | }); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /riscv/src/register/sepc.rs: -------------------------------------------------------------------------------- 1 | //! sepc register 2 | 3 | read_csr_as_usize!(0x141); 4 | write_csr_as_usize!(0x141); 5 | -------------------------------------------------------------------------------- /riscv/src/register/sie.rs: -------------------------------------------------------------------------------- 1 | //! sie register 2 | 3 | read_write_csr! { 4 | /// sie register 5 | Sie: 0x104, 6 | mask: 0x222, 7 | } 8 | 9 | read_write_csr_field! { 10 | Sie, 11 | /// Supervisor Software Interrupt Enable 12 | ssoft: 1, 13 | } 14 | 15 | read_write_csr_field! { 16 | Sie, 17 | /// Supervisor Timer Interrupt Enable 18 | stimer: 5, 19 | } 20 | 21 | read_write_csr_field! { 22 | Sie, 23 | /// Supervisor Timer Interrupt Enable 24 | sext: 9, 25 | } 26 | 27 | set!(0x104); 28 | clear!(0x104); 29 | 30 | set_clear_csr!( 31 | /// Supervisor Software Interrupt Enable 32 | , set_ssoft, clear_ssoft, 1 << 1); 33 | set_clear_csr!( 34 | /// Supervisor Timer Interrupt Enable 35 | , set_stimer, clear_stimer, 1 << 5); 36 | set_clear_csr!( 37 | /// Supervisor External Interrupt Enable 38 | , set_sext, clear_sext, 1 << 9); 39 | 40 | #[cfg(test)] 41 | mod tests { 42 | use super::*; 43 | 44 | #[test] 45 | fn test_sie() { 46 | let mut sie = Sie::from_bits(0); 47 | 48 | test_csr_field!(sie, ssoft); 49 | test_csr_field!(sie, stimer); 50 | test_csr_field!(sie, sext); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /riscv/src/register/sip.rs: -------------------------------------------------------------------------------- 1 | //! sip register 2 | 3 | read_write_csr! { 4 | /// sip register 5 | Sip: 0x144, 6 | mask: 0x222, 7 | } 8 | 9 | read_write_csr_field! { 10 | Sip, 11 | /// Supervisor Software Interrupt Pending 12 | ssoft: 1, 13 | } 14 | 15 | read_only_csr_field! { 16 | Sip, 17 | /// Supervisor Timer Interrupt Pending 18 | stimer: 5, 19 | } 20 | 21 | read_only_csr_field! { 22 | Sip, 23 | /// Supervisor External Interrupt Pending 24 | sext: 9, 25 | } 26 | 27 | set!(0x144); 28 | clear!(0x144); 29 | 30 | set_clear_csr!( 31 | /// Supervisor Software Interrupt Pending 32 | , set_ssoft, clear_ssoft, 1 << 1); 33 | 34 | #[cfg(test)] 35 | mod tests { 36 | use super::*; 37 | 38 | #[test] 39 | fn test_sip() { 40 | let mut sip = Sip::from_bits(0); 41 | 42 | test_csr_field!(sip, ssoft); 43 | assert!(!sip.stimer()); 44 | assert!(!sip.sext()); 45 | 46 | assert!(Sip::from_bits(1 << 5).stimer()); 47 | assert!(Sip::from_bits(1 << 9).sext()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /riscv/src/register/sscratch.rs: -------------------------------------------------------------------------------- 1 | //! sscratch register 2 | 3 | read_csr_as_usize!(0x140); 4 | write_csr_as_usize!(0x140); 5 | -------------------------------------------------------------------------------- /riscv/src/register/stval.rs: -------------------------------------------------------------------------------- 1 | //! stval register 2 | 3 | read_csr_as_usize!(0x143); 4 | write_csr!(0x143); 5 | 6 | /// Writes the CSR 7 | #[inline] 8 | pub unsafe fn write(bits: usize) { 9 | _write(bits) 10 | } 11 | -------------------------------------------------------------------------------- /riscv/src/register/stvec.rs: -------------------------------------------------------------------------------- 1 | //! stvec register 2 | 3 | pub use crate::register::mtvec::TrapMode; 4 | use crate::result::{Error, Result}; 5 | 6 | const TRAP_MASK: usize = 0b11; 7 | 8 | read_write_csr! { 9 | /// stvec register 10 | Stvec: 0x105, 11 | mask: usize::MAX, 12 | } 13 | 14 | read_write_csr_field! { 15 | Stvec, 16 | /// Returns the trap-vector mode 17 | trap_mode, 18 | TrapMode: [0:1], 19 | } 20 | 21 | impl Stvec { 22 | /// Returns the trap-vector base-address 23 | #[inline] 24 | pub const fn address(&self) -> usize { 25 | self.bits & !TRAP_MASK 26 | } 27 | 28 | /// Sets the trap-vector base-address. 29 | /// 30 | /// # Note 31 | /// 32 | /// Panics if the address is not aligned to 4-bytes. 33 | #[inline] 34 | pub fn set_address(&mut self, address: usize) { 35 | self.try_set_address(address).unwrap(); 36 | } 37 | 38 | /// Attempts to set the trap-vector base-address. 39 | /// 40 | /// # Note 41 | /// 42 | /// Returns an error if the address is not aligned to 4-bytes. 43 | #[inline] 44 | pub fn try_set_address(&mut self, address: usize) -> Result<()> { 45 | // check for four-byte alignment 46 | if (address & TRAP_MASK) != 0 { 47 | Err(Error::InvalidFieldVariant { 48 | field: "stvec::address", 49 | value: address, 50 | }) 51 | } else { 52 | self.bits = address | (self.bits & TRAP_MASK); 53 | Ok(()) 54 | } 55 | } 56 | } 57 | 58 | #[cfg(test)] 59 | mod tests { 60 | use super::*; 61 | 62 | #[test] 63 | fn test_stvec() { 64 | let mut stvec = Stvec::from_bits(0); 65 | 66 | [TrapMode::Direct, TrapMode::Vectored] 67 | .into_iter() 68 | .for_each(|trap_mode| { 69 | test_csr_field!(stvec, trap_mode: trap_mode); 70 | }); 71 | 72 | (1..=usize::BITS) 73 | .map(|r| (((1u128 << r) - 1) as usize) & !TRAP_MASK) 74 | .for_each(|address| { 75 | stvec.set_address(address); 76 | assert_eq!(stvec.address(), address); 77 | 78 | assert_eq!(stvec.try_set_address(address), Ok(())); 79 | assert_eq!(stvec.address(), address); 80 | }); 81 | 82 | (1..=usize::BITS) 83 | .filter_map(|r| match ((1u128 << r) - 1) as usize { 84 | addr if (addr & TRAP_MASK) != 0 => Some(addr), 85 | _ => None, 86 | }) 87 | .for_each(|address| { 88 | assert_eq!( 89 | stvec.try_set_address(address), 90 | Err(Error::InvalidFieldVariant { 91 | field: "stvec::address", 92 | value: address, 93 | }) 94 | ); 95 | }); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /riscv/src/register/tests.rs: -------------------------------------------------------------------------------- 1 | mod read_only_csr; 2 | mod read_write_csr; 3 | mod write_only_csr; 4 | -------------------------------------------------------------------------------- /riscv/src/register/tests/read_only_csr.rs: -------------------------------------------------------------------------------- 1 | use crate::result::{Error, Result}; 2 | 3 | read_only_csr! { 4 | /// test CSR register type 5 | Mtest: 0x000, 6 | mask: 0b1111_1111_1111, 7 | } 8 | 9 | read_only_csr_field! { 10 | Mtest, 11 | /// test single-bit field 12 | single: 0, 13 | } 14 | 15 | read_only_csr_field! { 16 | Mtest, 17 | /// multiple single-bit field range 18 | multi_range: 1..=3, 19 | } 20 | 21 | read_only_csr_field! { 22 | Mtest, 23 | /// multi-bit field 24 | multi_field: [4:7], 25 | } 26 | 27 | csr_field_enum! { 28 | /// field enum type with valid field variants 29 | MtestFieldEnum { 30 | default: Field1, 31 | Field1 = 1, 32 | Field2 = 2, 33 | Field3 = 3, 34 | Field4 = 15, 35 | } 36 | } 37 | 38 | read_only_csr_field! { 39 | Mtest, 40 | /// multi-bit field 41 | field_enum, 42 | MtestFieldEnum: [8:11], 43 | } 44 | 45 | // we don't test the `read` function, we are only testing in-memory functions. 46 | #[allow(unused)] 47 | pub fn _read_csr() -> Mtest { 48 | read() 49 | } 50 | 51 | #[allow(unused)] 52 | pub fn _try_read_csr() -> Result { 53 | try_read() 54 | } 55 | 56 | #[test] 57 | fn test_mtest_read_only() { 58 | let mut mtest = Mtest::from_bits(0); 59 | 60 | assert_eq!(mtest.bitmask(), Mtest::BITMASK); 61 | assert_eq!(mtest.bits(), 0); 62 | 63 | // check that single bit field getter/setters work. 64 | assert!(!mtest.single()); 65 | 66 | mtest = Mtest::from_bits(1); 67 | assert!(mtest.single()); 68 | 69 | mtest = Mtest::from_bits(0); 70 | 71 | // check that single bit range field getter/setters work. 72 | for i in 1..=3 { 73 | assert!(!mtest.multi_range(i)); 74 | assert_eq!(mtest.try_multi_range(i), Ok(false)); 75 | 76 | mtest = Mtest::from_bits(1 << i); 77 | assert!(mtest.multi_range(i)); 78 | assert_eq!(mtest.try_multi_range(i), Ok(true)); 79 | 80 | mtest = Mtest::from_bits(0 << i); 81 | assert!(!mtest.multi_range(i)); 82 | assert_eq!(mtest.try_multi_range(i), Ok(false)); 83 | } 84 | 85 | // check that multi-bit field getter/setters work. 86 | assert_eq!(mtest.multi_field(), 0); 87 | 88 | mtest = Mtest::from_bits(0xf << 4); 89 | assert_eq!(mtest.multi_field(), 0xf); 90 | 91 | mtest = Mtest::from_bits(0x3 << 4); 92 | assert_eq!(mtest.multi_field(), 0x3); 93 | 94 | // check that only bits in the field are set. 95 | mtest = Mtest::from_bits(0xff << 4); 96 | assert_eq!(mtest.multi_field(), 0xf); 97 | assert_eq!(mtest.bits(), 0xff << 4); 98 | 99 | mtest = Mtest::from_bits(0x0 << 4); 100 | assert_eq!(mtest.multi_field(), 0x0); 101 | 102 | assert_eq!(mtest.try_field_enum(), Err(Error::InvalidVariant(0)),); 103 | 104 | [ 105 | MtestFieldEnum::Field1, 106 | MtestFieldEnum::Field2, 107 | MtestFieldEnum::Field3, 108 | MtestFieldEnum::Field4, 109 | ] 110 | .into_iter() 111 | .for_each(|variant| { 112 | mtest = Mtest::from_bits(variant.into_usize() << 8); 113 | assert_eq!(mtest.field_enum(), variant); 114 | assert_eq!(mtest.try_field_enum(), Ok(variant)); 115 | }); 116 | 117 | // check that setting an invalid variant returns `None` 118 | mtest = Mtest::from_bits(0xbad << 8); 119 | assert_eq!(mtest.try_field_enum(), Err(Error::InvalidVariant(13)),); 120 | } 121 | -------------------------------------------------------------------------------- /riscv/src/register/tests/read_write_csr.rs: -------------------------------------------------------------------------------- 1 | use crate::result::{Error, Result}; 2 | 3 | read_write_csr! { 4 | /// test CSR register type 5 | Mtest: 0x000, 6 | mask: 0b1111_1111_1111, 7 | } 8 | 9 | read_write_csr_field! { 10 | Mtest, 11 | /// test single-bit field 12 | single: 0, 13 | } 14 | 15 | read_write_csr_field! { 16 | Mtest, 17 | /// multiple single-bit field range 18 | multi_range: 1..=3, 19 | } 20 | 21 | read_write_csr_field! { 22 | Mtest, 23 | /// multi-bit field 24 | multi_field: [4:7], 25 | } 26 | 27 | csr_field_enum! { 28 | /// field enum type with valid field variants 29 | MtestFieldEnum { 30 | default: Field1, 31 | Field1 = 1, 32 | Field2 = 2, 33 | Field3 = 3, 34 | Field4 = 15, 35 | } 36 | } 37 | 38 | read_write_csr_field! { 39 | Mtest, 40 | /// multi-bit field 41 | field_enum, 42 | MtestFieldEnum: [8:11], 43 | } 44 | 45 | // we don't test the `read` and `write` functions, we are only testing in-memory functions. 46 | #[allow(unused)] 47 | pub fn _read_csr() -> Mtest { 48 | read() 49 | } 50 | 51 | #[allow(unused)] 52 | pub fn _try_read_csr() -> Result { 53 | try_read() 54 | } 55 | 56 | #[allow(unused)] 57 | pub fn _write_csr(csr: Mtest) { 58 | unsafe { write(csr) }; 59 | } 60 | 61 | #[allow(unused)] 62 | pub fn _try_write_csr(csr: Mtest) { 63 | unsafe { try_write(csr) }; 64 | } 65 | 66 | #[test] 67 | fn test_mtest_read_write() { 68 | let mut mtest = Mtest::from_bits(0); 69 | 70 | assert_eq!(mtest.bitmask(), Mtest::BITMASK); 71 | assert_eq!(mtest.bits(), 0); 72 | 73 | // check that single bit field getter/setters work. 74 | assert!(!mtest.single()); 75 | 76 | mtest.set_single(true); 77 | assert!(mtest.single()); 78 | 79 | mtest.set_single(false); 80 | assert!(!mtest.single()); 81 | 82 | // check that single bit range field getter/setters work. 83 | for i in 1..=3 { 84 | assert!(!mtest.multi_range(i)); 85 | 86 | mtest.set_multi_range(i, true); 87 | assert!(mtest.multi_range(i)); 88 | 89 | mtest.set_multi_range(i, false); 90 | assert!(!mtest.multi_range(i)); 91 | } 92 | 93 | // check that multi-bit field getter/setters work. 94 | assert_eq!(mtest.multi_field(), 0); 95 | 96 | mtest.set_multi_field(0xf); 97 | assert_eq!(mtest.multi_field(), 0xf); 98 | 99 | mtest.set_multi_field(0x3); 100 | assert_eq!(mtest.multi_field(), 0x3); 101 | 102 | // check that only bits in the field are set. 103 | mtest.set_multi_field(0xff); 104 | assert_eq!(mtest.multi_field(), 0xf); 105 | assert_eq!(mtest.bits(), 0xf << 4); 106 | 107 | mtest.set_multi_field(0x0); 108 | assert_eq!(mtest.multi_field(), 0x0); 109 | 110 | assert_eq!(mtest.try_field_enum(), Err(Error::InvalidVariant(0))); 111 | 112 | [ 113 | MtestFieldEnum::Field1, 114 | MtestFieldEnum::Field2, 115 | MtestFieldEnum::Field3, 116 | MtestFieldEnum::Field4, 117 | ] 118 | .into_iter() 119 | .for_each(|variant| { 120 | mtest.set_field_enum(variant); 121 | assert_eq!(mtest.field_enum(), variant); 122 | assert_eq!(mtest.try_field_enum(), Ok(variant)); 123 | }); 124 | 125 | // check that setting an invalid variant returns `None` 126 | mtest = Mtest::from_bits(0xbad << 8); 127 | assert_eq!(mtest.try_field_enum(), Err(Error::InvalidVariant(13))); 128 | } 129 | -------------------------------------------------------------------------------- /riscv/src/register/tests/write_only_csr.rs: -------------------------------------------------------------------------------- 1 | use crate::result::{Error, Result}; 2 | 3 | write_only_csr! { 4 | /// test CSR register type 5 | Mtest: 0x000, 6 | mask: 0b1111_1111_1111, 7 | } 8 | 9 | write_only_csr_field! { 10 | Mtest, 11 | /// setter test single-bit field 12 | set_single: 0, 13 | } 14 | 15 | write_only_csr_field! { 16 | Mtest, 17 | /// setter multiple single-bit field range 18 | set_multi_range: 1..=3, 19 | } 20 | 21 | write_only_csr_field! { 22 | Mtest, 23 | /// setter multi-bit field 24 | set_multi_field: [4:7], 25 | } 26 | 27 | csr_field_enum! { 28 | /// field enum type with valid field variants 29 | MtestFieldEnum { 30 | default: Field1, 31 | Field1 = 1, 32 | Field2 = 2, 33 | Field3 = 3, 34 | Field4 = 15, 35 | } 36 | } 37 | 38 | write_only_csr_field! { 39 | Mtest, 40 | /// setter multi-bit field 41 | set_field_enum, 42 | MtestFieldEnum: [8:11], 43 | } 44 | 45 | // we don't test the `write` function, we are only testing in-memory functions. 46 | #[allow(unused)] 47 | pub fn _write_csr(csr: Mtest) { 48 | unsafe { write(csr) }; 49 | } 50 | 51 | #[allow(unused)] 52 | pub fn _try_write_csr(csr: Mtest) -> Result<()> { 53 | unsafe { try_write(csr) } 54 | } 55 | 56 | #[test] 57 | fn test_mtest_write_only() { 58 | let mut mtest = Mtest::from_bits(0); 59 | 60 | assert_eq!(mtest.bitmask(), Mtest::BITMASK); 61 | assert_eq!(mtest.bits(), 0); 62 | 63 | // check that single bit field getter/setters work. 64 | mtest.set_single(true); 65 | assert_eq!(mtest.bits(), 1); 66 | 67 | mtest.set_single(false); 68 | assert_eq!(mtest.bits(), 0); 69 | 70 | // check that single bit range field getter/setters work. 71 | for i in 1..=3 { 72 | mtest.set_multi_range(i, true); 73 | assert_ne!(mtest.bits() & (1 << i), 0); 74 | 75 | mtest.set_multi_range(i, false); 76 | assert_eq!(mtest.bits() & (1 << i), 0); 77 | } 78 | 79 | // check that multi-bit field getter/setters work. 80 | mtest.set_multi_field(0xf); 81 | assert_eq!(mtest.bits() >> 4, 0xf); 82 | 83 | mtest.set_multi_field(0x3); 84 | assert_eq!(mtest.bits() >> 4, 0x3); 85 | 86 | // check that only bits in the field are set. 87 | mtest.set_multi_field(0xff); 88 | assert_eq!(mtest.bits() >> 4, 0xf); 89 | 90 | mtest.set_multi_field(0x0); 91 | assert_eq!(mtest.bits() >> 4, 0x0); 92 | 93 | mtest = Mtest::from_bits(0); 94 | 95 | assert_eq!( 96 | MtestFieldEnum::from_usize(mtest.bits() >> 8), 97 | Err(Error::InvalidVariant(0)) 98 | ); 99 | 100 | [ 101 | MtestFieldEnum::Field1, 102 | MtestFieldEnum::Field2, 103 | MtestFieldEnum::Field3, 104 | MtestFieldEnum::Field4, 105 | ] 106 | .into_iter() 107 | .for_each(|variant| { 108 | mtest.set_field_enum(variant); 109 | assert_eq!(MtestFieldEnum::from_usize(mtest.bits() >> 8), Ok(variant)); 110 | }); 111 | 112 | // check that setting an invalid variant returns `None` 113 | mtest = Mtest::from_bits(0xbad << 8); 114 | assert_eq!( 115 | MtestFieldEnum::from_usize(mtest.bits() >> 8), 116 | Err(Error::InvalidVariant(13)) 117 | ); 118 | } 119 | -------------------------------------------------------------------------------- /riscv/src/register/time.rs: -------------------------------------------------------------------------------- 1 | //! time register 2 | 3 | read_csr_as_usize!(0xC01); 4 | read_composite_csr!(super::timeh::read(), read()); 5 | -------------------------------------------------------------------------------- /riscv/src/register/timeh.rs: -------------------------------------------------------------------------------- 1 | //! timeh register 2 | 3 | read_csr_as_usize_rv32!(0xC81); 4 | -------------------------------------------------------------------------------- /tests-build/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tests-build" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | panic-halt = "1.0" 8 | riscv = { path = "../riscv", version = "0.13.0" } 9 | riscv-rt = { path = "../riscv-rt", version = "0.14.0" } 10 | 11 | [features] 12 | pre-init = ["riscv-rt/pre-init"] 13 | single-hart = ["riscv-rt/single-hart"] 14 | v-trap = ["riscv-rt/v-trap"] 15 | device = ["riscv-rt/device"] 16 | memory = ["riscv-rt/memory"] 17 | no-exceptions = ["riscv-rt/no-exceptions"] 18 | no-interrupts = ["riscv-rt/no-interrupts"] 19 | -------------------------------------------------------------------------------- /tests-build/build.rs: -------------------------------------------------------------------------------- 1 | use std::{env, fs::File, io::Write, path::PathBuf}; 2 | 3 | fn main() { 4 | // Put device.x somewhere the linker can find it 5 | let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); 6 | File::create(out.join("device.x")) 7 | .unwrap() 8 | .write_all(include_bytes!("device.x")) 9 | .unwrap(); 10 | println!("cargo:rustc-link-search={}", out.display()); 11 | println!("cargo:rerun-if-changed=device.x"); 12 | 13 | // Put memory.x somewhere the linker can find it 14 | File::create(out.join("memory.x")) 15 | .unwrap() 16 | .write_all(include_bytes!("memory.x")) 17 | .unwrap(); 18 | println!("cargo:rustc-link-search={}", out.display()); 19 | println!("cargo:rerun-if-changed=memory.x"); 20 | 21 | println!("cargo:rerun-if-changed=build.rs"); 22 | } 23 | -------------------------------------------------------------------------------- /tests-build/device.x: -------------------------------------------------------------------------------- 1 | /* Core interrupt sources and trap handlers */ 2 | PROVIDE(MachineSoft = DefaultHandler); 3 | PROVIDE(MachineTimer = DefaultHandler); 4 | PROVIDE(MachineExternal = DefaultHandler); 5 | PROVIDE(_start_MachineSoft_trap = _start_DefaultHandler_trap); 6 | PROVIDE(_start_MachineTimer_trap = _start_DefaultHandler_trap); 7 | PROVIDE(_start_MachineExternal_trap = _start_DefaultHandler_trap); 8 | 9 | /* External interrupt sources */ 10 | PROVIDE(Gpio = DefaultHandler); 11 | PROVIDE(Uart = DefaultHandler); 12 | PROVIDE(I2c = DefaultHandler); 13 | 14 | /* Exception sources */ 15 | PROVIDE(InstructionMisaligned = ExceptionHandler); 16 | PROVIDE(InstructionFault = ExceptionHandler); 17 | PROVIDE(IllegalInstruction = ExceptionHandler); 18 | PROVIDE(Breakpoint = ExceptionHandler); 19 | PROVIDE(LoadMisaligned = ExceptionHandler); 20 | PROVIDE(LoadFault = ExceptionHandler); 21 | PROVIDE(StoreMisaligned = ExceptionHandler); 22 | PROVIDE(StoreFault = ExceptionHandler); 23 | PROVIDE(MachineEnvCall = ExceptionHandler); 24 | PROVIDE(InstructionPageFault = ExceptionHandler); 25 | PROVIDE(LoadPageFault = ExceptionHandler); 26 | PROVIDE(StorePageFault = ExceptionHandler); 27 | -------------------------------------------------------------------------------- /tests-build/examples/empty.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate panic_halt; 5 | 6 | use riscv_rt::{core_interrupt, entry, exception, external_interrupt}; 7 | use tests_build::{CoreInterrupt, Exception, ExternalInterrupt}; 8 | 9 | /* EXAMPLES OF USING THE core_interrupt MACRO FOR CORE INTERRUPT HANDLERS. 10 | IF v-trap ENABLED, THE MACRO ALSO DEFINES _start_COREINTERRUPT_trap routines */ 11 | 12 | /// Handler with the simplest signature. 13 | #[core_interrupt(CoreInterrupt::MachineSoft)] 14 | fn supervisor_soft() { 15 | // do something here 16 | loop {} 17 | } 18 | 19 | /// Handler with the most complete signature. 20 | #[core_interrupt(CoreInterrupt::MachineTimer)] 21 | unsafe fn supervisor_timer() -> ! { 22 | // do something here 23 | loop {} 24 | } 25 | 26 | /* EXAMPLES OF USING THE external_interrupt MACRO FOR EXTERNAL INTERRUPT HANDLERS. */ 27 | 28 | /// Handler with the simplest signature. 29 | #[external_interrupt(ExternalInterrupt::Gpio)] 30 | fn external_gpio() { 31 | // do something here 32 | loop {} 33 | } 34 | 35 | /// Handler with the most complete signature. 36 | #[external_interrupt(ExternalInterrupt::Uart)] 37 | unsafe fn external_uart() -> ! { 38 | // do something here 39 | loop {} 40 | } 41 | 42 | /* EXAMPLES OF USING THE exception MACRO FOR EXCEPTION HANDLERS. */ 43 | 44 | /// Handler with the simplest signature. 45 | #[exception(Exception::InstructionMisaligned)] 46 | fn instruction_misaligned() { 47 | // do something here 48 | loop {} 49 | } 50 | 51 | /// Handler with the most complete signature. 52 | #[exception(Exception::IllegalInstruction)] 53 | unsafe fn illegal_instruction(_trap: &riscv_rt::TrapFrame) -> ! { 54 | // do something here 55 | loop {} 56 | } 57 | 58 | // The reference to TrapFrame can be mutable if the handler needs to modify it. 59 | #[exception(Exception::Breakpoint)] 60 | unsafe fn breakpoint(_trap: &mut riscv_rt::TrapFrame) -> ! { 61 | // do something here 62 | loop {} 63 | } 64 | 65 | #[entry] 66 | fn main() -> ! { 67 | // do something here 68 | loop {} 69 | } 70 | -------------------------------------------------------------------------------- /tests-build/memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | RAM : ORIGIN = 0x80000000, LENGTH = 16K 4 | FLASH : ORIGIN = 0x20000000, LENGTH = 4M 5 | } 6 | 7 | REGION_ALIAS("REGION_TEXT", FLASH); 8 | REGION_ALIAS("REGION_RODATA", FLASH); 9 | REGION_ALIAS("REGION_DATA", RAM); 10 | REGION_ALIAS("REGION_BSS", RAM); 11 | REGION_ALIAS("REGION_HEAP", RAM); 12 | REGION_ALIAS("REGION_STACK", RAM); 13 | -------------------------------------------------------------------------------- /tests-build/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 5 | #[riscv::pac_enum(unsafe ExternalInterruptNumber)] 6 | pub enum ExternalInterrupt { 7 | Gpio = 0, 8 | Uart = 1, 9 | I2c = 2, 10 | } 11 | 12 | #[cfg(not(feature = "no-interrupts"))] 13 | pub use riscv::interrupt::Interrupt as CoreInterrupt; 14 | 15 | #[cfg(feature = "no-interrupts")] 16 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 17 | #[riscv::pac_enum(unsafe CoreInterruptNumber)] 18 | pub enum CoreInterrupt { 19 | MachineSoft = 3, 20 | MachineTimer = 7, 21 | MachineExternal = 11, 22 | } 23 | 24 | #[cfg(not(feature = "no-exceptions"))] 25 | pub use riscv::interrupt::Exception; 26 | 27 | #[cfg(feature = "no-exceptions")] 28 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 29 | #[riscv::pac_enum(unsafe ExceptionNumber)] 30 | pub enum Exception { 31 | InstructionMisaligned = 0, 32 | InstructionFault = 1, 33 | IllegalInstruction = 2, 34 | Breakpoint = 3, 35 | LoadMisaligned = 4, 36 | LoadFault = 5, 37 | StoreMisaligned = 6, 38 | StoreFault = 7, 39 | MachineEnvCall = 11, 40 | InstructionPageFault = 12, 41 | LoadPageFault = 13, 42 | StorePageFault = 15, 43 | } 44 | 45 | #[cfg(feature = "pre-init")] 46 | #[cfg_attr(feature = "pre-init", riscv_rt::pre_init)] 47 | unsafe fn pre_init() {} 48 | -------------------------------------------------------------------------------- /tests-trybuild/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tests-trybuild" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | riscv = { path = "../riscv", version = "0.13.0" } 8 | riscv-rt = { path = "../riscv-rt", version = "0.14.0", features = ["no-exceptions", "no-interrupts"]} 9 | trybuild = "1.0" 10 | 11 | [features] 12 | v-trap = ["riscv-rt/v-trap"] 13 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv-rt/core_interrupt/fail_empty_macro.rs: -------------------------------------------------------------------------------- 1 | #[riscv_rt::core_interrupt] 2 | fn my_interrupt() {} 3 | 4 | fn main() {} 5 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv-rt/core_interrupt/fail_empty_macro.stderr: -------------------------------------------------------------------------------- 1 | error: `#[core_interrupt]` attribute expects a path to a variant of an enum that implements the riscv_rt :: CoreInterruptNumber trait. 2 | --> tests/riscv-rt/core_interrupt/fail_empty_macro.rs:1:1 3 | | 4 | 1 | #[riscv_rt::core_interrupt] 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the attribute macro `riscv_rt::core_interrupt` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv-rt/core_interrupt/fail_impl_interrupt_number.rs: -------------------------------------------------------------------------------- 1 | #[riscv_rt::core_interrupt(riscv::interrupt::Exception::LoadMisaligned)] 2 | fn my_interrupt() {} 3 | 4 | fn main() {} 5 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv-rt/core_interrupt/fail_impl_interrupt_number.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `riscv::interrupt::Exception: CoreInterruptNumber` is not satisfied 2 | --> tests/riscv-rt/core_interrupt/fail_impl_interrupt_number.rs:1:28 3 | | 4 | 1 | #[riscv_rt::core_interrupt(riscv::interrupt::Exception::LoadMisaligned)] 5 | | ---------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- 6 | | | | 7 | | | the trait `CoreInterruptNumber` is not implemented for `riscv::interrupt::Exception` 8 | | required by a bound introduced by this call 9 | | 10 | = help: the following other types implement trait `CoreInterruptNumber`: 11 | riscv::interrupt::Interrupt 12 | riscv::interrupt::supervisor::Interrupt 13 | note: required by a bound in `assert_impl` 14 | --> tests/riscv-rt/core_interrupt/fail_impl_interrupt_number.rs:1:1 15 | | 16 | 1 | #[riscv_rt::core_interrupt(riscv::interrupt::Exception::LoadMisaligned)] 17 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_impl` 18 | = note: this error originates in the attribute macro `riscv_rt::core_interrupt` (in Nightly builds, run with -Z macro-backtrace for more info) 19 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv-rt/core_interrupt/fail_signatures.rs: -------------------------------------------------------------------------------- 1 | #[riscv_rt::core_interrupt(riscv::interrupt::Interrupt::SupervisorSoft)] 2 | fn my_interrupt(code: usize) {} 3 | 4 | #[riscv_rt::core_interrupt(riscv::interrupt::Interrupt::SupervisorTimer)] 5 | fn my_other_interrupt() -> usize {} 6 | 7 | #[riscv_rt::core_interrupt(riscv::interrupt::Interrupt::SupervisorExternal)] 8 | async fn my_async_interrupt() {} 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv-rt/core_interrupt/fail_signatures.stderr: -------------------------------------------------------------------------------- 1 | error: `#[core_interrupt]` function must have signature `[unsafe] fn() [-> !]` 2 | --> tests/riscv-rt/core_interrupt/fail_signatures.rs:2:1 3 | | 4 | 2 | fn my_interrupt(code: usize) {} 5 | | ^^ 6 | 7 | error: `#[core_interrupt]` function must have signature `[unsafe] fn() [-> !]` 8 | --> tests/riscv-rt/core_interrupt/fail_signatures.rs:5:1 9 | | 10 | 5 | fn my_other_interrupt() -> usize {} 11 | | ^^ 12 | 13 | error: `#[core_interrupt]` function must have signature `[unsafe] fn() [-> !]` 14 | --> tests/riscv-rt/core_interrupt/fail_signatures.rs:8:1 15 | | 16 | 8 | async fn my_async_interrupt() {} 17 | | ^^^^^ 18 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv-rt/core_interrupt/pass_core_interrupt.rs: -------------------------------------------------------------------------------- 1 | use riscv::interrupt::Interrupt::*; 2 | 3 | #[riscv_rt::core_interrupt(SupervisorSoft)] 4 | fn simple_interrupt() {} 5 | 6 | #[riscv_rt::core_interrupt(SupervisorTimer)] 7 | unsafe fn no_return_interrupt() -> ! { 8 | loop {} 9 | } 10 | 11 | fn main() {} 12 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv-rt/exception/fail_empty_macro.rs: -------------------------------------------------------------------------------- 1 | #[riscv_rt::exception] 2 | fn my_exception() {} 3 | 4 | fn main() {} 5 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv-rt/exception/fail_empty_macro.stderr: -------------------------------------------------------------------------------- 1 | error: `#[exception]` attribute expects a path to a variant of an enum that implements the riscv_rt :: ExceptionNumber trait. 2 | --> tests/riscv-rt/exception/fail_empty_macro.rs:1:1 3 | | 4 | 1 | #[riscv_rt::exception] 5 | | ^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the attribute macro `riscv_rt::exception` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv-rt/exception/fail_impl_exception_number.rs: -------------------------------------------------------------------------------- 1 | #[riscv_rt::exception(riscv::interrupt::Interrupt::SupervisorSoft)] 2 | fn my_exception() {} 3 | 4 | fn main() {} 5 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv-rt/exception/fail_impl_exception_number.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `riscv::interrupt::Interrupt: ExceptionNumber` is not satisfied 2 | --> tests/riscv-rt/exception/fail_impl_exception_number.rs:1:23 3 | | 4 | 1 | #[riscv_rt::exception(riscv::interrupt::Interrupt::SupervisorSoft)] 5 | | ----------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- 6 | | | | 7 | | | the trait `ExceptionNumber` is not implemented for `riscv::interrupt::Interrupt` 8 | | required by a bound introduced by this call 9 | | 10 | = help: the following other types implement trait `ExceptionNumber`: 11 | riscv::interrupt::Exception 12 | riscv::interrupt::supervisor::Exception 13 | note: required by a bound in `assert_impl` 14 | --> tests/riscv-rt/exception/fail_impl_exception_number.rs:1:1 15 | | 16 | 1 | #[riscv_rt::exception(riscv::interrupt::Interrupt::SupervisorSoft)] 17 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_impl` 18 | = note: this error originates in the attribute macro `riscv_rt::exception` (in Nightly builds, run with -Z macro-backtrace for more info) 19 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv-rt/exception/fail_signatures.rs: -------------------------------------------------------------------------------- 1 | #[riscv_rt::exception(riscv::interrupt::Exception::LoadMisaligned)] 2 | fn my_exception(code: usize) {} 3 | 4 | #[riscv_rt::exception(riscv::interrupt::Exception::StoreMisaligned)] 5 | fn my_other_exception(trap_frame: &riscv_rt::TrapFrame, code: usize) {} 6 | 7 | #[riscv_rt::exception(riscv::interrupt::Exception::LoadFault)] 8 | async fn my_async_exception(trap_frame: &riscv_rt::TrapFrame, code: usize) {} 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv-rt/exception/fail_signatures.stderr: -------------------------------------------------------------------------------- 1 | error: `#[exception]` function must have signature `[unsafe] fn([&[mut] riscv_rt::TrapFrame]) [-> !]` 2 | --> tests/riscv-rt/exception/fail_signatures.rs:2:1 3 | | 4 | 2 | fn my_exception(code: usize) {} 5 | | ^^ 6 | 7 | error: `#[exception]` function must have signature `[unsafe] fn([&[mut] riscv_rt::TrapFrame]) [-> !]` 8 | --> tests/riscv-rt/exception/fail_signatures.rs:5:1 9 | | 10 | 5 | fn my_other_exception(trap_frame: &riscv_rt::TrapFrame, code: usize) {} 11 | | ^^ 12 | 13 | error: `#[exception]` function must have signature `[unsafe] fn([&[mut] riscv_rt::TrapFrame]) [-> !]` 14 | --> tests/riscv-rt/exception/fail_signatures.rs:8:1 15 | | 16 | 8 | async fn my_async_exception(trap_frame: &riscv_rt::TrapFrame, code: usize) {} 17 | | ^^^^^ 18 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv-rt/exception/pass_exception.rs: -------------------------------------------------------------------------------- 1 | use riscv::interrupt::Exception::*; 2 | 3 | #[riscv_rt::exception(LoadMisaligned)] 4 | fn simple_exception() {} 5 | 6 | #[riscv_rt::exception(LoadFault)] 7 | fn unmutable_exception(_trap_frame: &riscv_rt::TrapFrame) {} 8 | 9 | #[riscv_rt::exception(StoreMisaligned)] 10 | fn mutable_exception(_trap_frame: &mut riscv_rt::TrapFrame) {} 11 | 12 | #[riscv_rt::exception(StoreFault)] 13 | fn no_return_exception(_trap_frame: &mut riscv_rt::TrapFrame) -> ! { 14 | loop {} 15 | } 16 | 17 | fn main() {} 18 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv-rt/external_interrupt/fail_empty_macro.rs: -------------------------------------------------------------------------------- 1 | #[riscv_rt::external_interrupt] 2 | fn my_interrupt() {} 3 | 4 | fn main() {} 5 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv-rt/external_interrupt/fail_empty_macro.stderr: -------------------------------------------------------------------------------- 1 | error: `#[external_interrupt]` attribute expects a path to a variant of an enum that implements the riscv_rt :: ExternalInterruptNumber trait. 2 | --> tests/riscv-rt/external_interrupt/fail_empty_macro.rs:1:1 3 | | 4 | 1 | #[riscv_rt::external_interrupt] 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the attribute macro `riscv_rt::external_interrupt` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv-rt/external_interrupt/fail_impl_interrupt_number.rs: -------------------------------------------------------------------------------- 1 | #[riscv_rt::external_interrupt(riscv::interrupt::Interrupt::SupervisorSoft)] 2 | fn my_interrupt() {} 3 | 4 | fn main() {} 5 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv-rt/external_interrupt/fail_impl_interrupt_number.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `riscv::interrupt::Interrupt: ExternalInterruptNumber` is not satisfied 2 | --> tests/riscv-rt/external_interrupt/fail_impl_interrupt_number.rs:1:32 3 | | 4 | 1 | #[riscv_rt::external_interrupt(riscv::interrupt::Interrupt::SupervisorSoft)] 5 | | -------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- 6 | | | | 7 | | | the trait `ExternalInterruptNumber` is not implemented for `riscv::interrupt::Interrupt` 8 | | required by a bound introduced by this call 9 | | 10 | note: required by a bound in `assert_impl` 11 | --> tests/riscv-rt/external_interrupt/fail_impl_interrupt_number.rs:1:1 12 | | 13 | 1 | #[riscv_rt::external_interrupt(riscv::interrupt::Interrupt::SupervisorSoft)] 14 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_impl` 15 | = note: this error originates in the attribute macro `riscv_rt::external_interrupt` (in Nightly builds, run with -Z macro-backtrace for more info) 16 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv-rt/external_interrupt/fail_signatures.rs: -------------------------------------------------------------------------------- 1 | use riscv_rt::result::{Error, Result}; 2 | 3 | /// Just a dummy type to test the `ExternalInterrupt` trait. 4 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 5 | pub enum ExternalInterrupt { 6 | GPIO, 7 | UART, 8 | PWM, 9 | } 10 | unsafe impl riscv_rt::InterruptNumber for ExternalInterrupt { 11 | const MAX_INTERRUPT_NUMBER: usize = 2; 12 | 13 | #[inline] 14 | fn number(self) -> usize { 15 | self as usize 16 | } 17 | 18 | #[inline] 19 | fn from_number(value: usize) -> Result { 20 | match value { 21 | 0 => Ok(Self::GPIO), 22 | 1 => Ok(Self::UART), 23 | 2 => Ok(Self::PWM), 24 | _ => Err(Error::InvalidVariant(value)), 25 | } 26 | } 27 | } 28 | unsafe impl riscv::ExternalInterruptNumber for ExternalInterrupt {} 29 | 30 | #[riscv_rt::external_interrupt(ExternalInterrupt::GPIO)] 31 | fn my_interrupt() -> usize {} 32 | 33 | #[riscv_rt::external_interrupt(ExternalInterrupt::UART)] 34 | fn my_other_interrupt(code: usize) -> usize {} 35 | 36 | #[riscv_rt::external_interrupt(ExternalInterrupt::PWM)] 37 | async fn my_async_interrupt(code: usize) -> usize {} 38 | 39 | fn main() {} 40 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv-rt/external_interrupt/fail_signatures.stderr: -------------------------------------------------------------------------------- 1 | error: `#[external_interrupt]` function must have signature `[unsafe] fn() [-> !]` 2 | --> tests/riscv-rt/external_interrupt/fail_signatures.rs:31:1 3 | | 4 | 31 | fn my_interrupt() -> usize {} 5 | | ^^ 6 | 7 | error: `#[external_interrupt]` function must have signature `[unsafe] fn() [-> !]` 8 | --> tests/riscv-rt/external_interrupt/fail_signatures.rs:34:1 9 | | 10 | 34 | fn my_other_interrupt(code: usize) -> usize {} 11 | | ^^ 12 | 13 | error: `#[external_interrupt]` function must have signature `[unsafe] fn() [-> !]` 14 | --> tests/riscv-rt/external_interrupt/fail_signatures.rs:37:1 15 | | 16 | 37 | async fn my_async_interrupt(code: usize) -> usize {} 17 | | ^^^^^ 18 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv-rt/external_interrupt/pass_external_interrupt.rs: -------------------------------------------------------------------------------- 1 | use riscv_rt::result::{Error, Result}; 2 | 3 | /// Just a dummy type to test the `ExternalInterrupt` trait. 4 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 5 | pub enum ExternalInterrupt { 6 | GPIO, 7 | UART, 8 | } 9 | unsafe impl riscv_rt::InterruptNumber for ExternalInterrupt { 10 | const MAX_INTERRUPT_NUMBER: usize = 1; 11 | 12 | #[inline] 13 | fn number(self) -> usize { 14 | self as usize 15 | } 16 | 17 | #[inline] 18 | fn from_number(value: usize) -> Result { 19 | match value { 20 | 0 => Ok(Self::GPIO), 21 | 1 => Ok(Self::UART), 22 | _ => Err(Error::InvalidVariant(value)), 23 | } 24 | } 25 | } 26 | unsafe impl riscv::ExternalInterruptNumber for ExternalInterrupt {} 27 | 28 | #[riscv_rt::external_interrupt(ExternalInterrupt::GPIO)] 29 | fn simple_interrupt() {} 30 | 31 | #[riscv_rt::external_interrupt(ExternalInterrupt::UART)] 32 | unsafe fn no_return_interrupt() -> ! { 33 | loop {} 34 | } 35 | 36 | fn main() {} 37 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv/fail_empty_macro.rs: -------------------------------------------------------------------------------- 1 | #[riscv::pac_enum] 2 | #[derive(Clone, Copy, Debug, PartialEq)] 3 | enum Interrupt { 4 | I1 = 1, 5 | I2 = 2, 6 | I4 = 4, 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv/fail_empty_macro.stderr: -------------------------------------------------------------------------------- 1 | error: unexpected end of input, expected `unsafe` 2 | --> tests/riscv/fail_empty_macro.rs:1:1 3 | | 4 | 1 | #[riscv::pac_enum] 5 | | ^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the attribute macro `riscv::pac_enum` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv/fail_no_unsafe.rs: -------------------------------------------------------------------------------- 1 | #[riscv::pac_enum(InterruptNumber)] 2 | #[derive(Clone, Copy, Debug, PartialEq)] 3 | enum Interrupt { 4 | I1 = 1, 5 | I2 = 2, 6 | I4 = 4, 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv/fail_no_unsafe.stderr: -------------------------------------------------------------------------------- 1 | error: expected `unsafe` 2 | --> tests/riscv/fail_no_unsafe.rs:1:19 3 | | 4 | 1 | #[riscv::pac_enum(InterruptNumber)] 5 | | ^^^^^^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv/fail_unknown_trait.rs: -------------------------------------------------------------------------------- 1 | #[riscv::pac_enum(unsafe InterruptNumber)] 2 | #[derive(Clone, Copy, Debug, PartialEq)] 3 | enum Interrupt { 4 | I1 = 1, 5 | I2 = 2, 6 | I4 = 4, 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv/fail_unknown_trait.stderr: -------------------------------------------------------------------------------- 1 | error: Unknown trait name. Expected: 'ExceptionNumber', 'CoreInterruptNumber', 'ExternalInterruptNumber', 'PriorityNumber', or 'HartIdNumber' 2 | --> tests/riscv/fail_unknown_trait.rs:1:26 3 | | 4 | 1 | #[riscv::pac_enum(unsafe InterruptNumber)] 5 | | ^^^^^^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /tests-trybuild/tests/riscv/pass_test.rs: -------------------------------------------------------------------------------- 1 | use riscv::result::Error; 2 | use riscv::*; 3 | 4 | #[pac_enum(unsafe ExceptionNumber)] 5 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 6 | enum Exception { 7 | E1 = 1, 8 | E3 = 3, 9 | } 10 | 11 | #[pac_enum(unsafe CoreInterruptNumber)] 12 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 13 | enum Interrupt { 14 | I1 = 1, 15 | I2 = 2, 16 | I4 = 4, 17 | I7 = 7, 18 | } 19 | 20 | #[pac_enum(unsafe PriorityNumber)] 21 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 22 | enum Priority { 23 | P0 = 0, 24 | P1 = 1, 25 | P2 = 2, 26 | P3 = 3, 27 | } 28 | 29 | #[pac_enum(unsafe HartIdNumber)] 30 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 31 | enum HartId { 32 | H0 = 0, 33 | H1 = 1, 34 | H2 = 2, 35 | } 36 | 37 | mod isr { 38 | #[export_name = "DefaultHandler"] 39 | fn default_handler() {} 40 | 41 | #[export_name = "I1"] 42 | fn i1() {} 43 | 44 | #[export_name = "I2"] 45 | fn i2() {} 46 | 47 | #[export_name = "I4"] 48 | fn i4() {} 49 | 50 | #[export_name = "I7"] 51 | fn i7() {} 52 | } 53 | 54 | fn main() { 55 | assert_eq!(Exception::E1.number(), 1); 56 | assert_eq!(Exception::E3.number(), 3); 57 | 58 | assert_eq!(Exception::from_number(0), Err(Error::InvalidVariant(0))); 59 | assert_eq!(Exception::from_number(1), Ok(Exception::E1)); 60 | assert_eq!(Exception::from_number(2), Err(Error::InvalidVariant(2))); 61 | assert_eq!(Exception::from_number(3), Ok(Exception::E3)); 62 | assert_eq!(Exception::from_number(4), Err(Error::InvalidVariant(4))); 63 | 64 | assert_eq!(Exception::MAX_EXCEPTION_NUMBER, 3); 65 | 66 | assert_eq!(Interrupt::I1.number(), 1); 67 | assert_eq!(Interrupt::I2.number(), 2); 68 | assert_eq!(Interrupt::I4.number(), 4); 69 | assert_eq!(Interrupt::I7.number(), 7); 70 | 71 | assert_eq!(Interrupt::from_number(0), Err(Error::InvalidVariant(0))); 72 | assert_eq!(Interrupt::from_number(1), Ok(Interrupt::I1)); 73 | assert_eq!(Interrupt::from_number(2), Ok(Interrupt::I2)); 74 | assert_eq!(Interrupt::from_number(3), Err(Error::InvalidVariant(3))); 75 | assert_eq!(Interrupt::from_number(4), Ok(Interrupt::I4)); 76 | assert_eq!(Interrupt::from_number(5), Err(Error::InvalidVariant(5))); 77 | assert_eq!(Interrupt::from_number(6), Err(Error::InvalidVariant(6))); 78 | assert_eq!(Interrupt::from_number(7), Ok(Interrupt::I7)); 79 | 80 | assert_eq!(Interrupt::MAX_INTERRUPT_NUMBER, 7); 81 | 82 | assert_eq!(__CORE_INTERRUPTS.len(), Interrupt::MAX_INTERRUPT_NUMBER + 1); 83 | 84 | assert!(__CORE_INTERRUPTS[0].is_none()); 85 | assert!(__CORE_INTERRUPTS[1].is_some()); 86 | assert!(__CORE_INTERRUPTS[2].is_some()); 87 | assert!(__CORE_INTERRUPTS[3].is_none()); 88 | assert!(__CORE_INTERRUPTS[4].is_some()); 89 | assert!(__CORE_INTERRUPTS[5].is_none()); 90 | assert!(__CORE_INTERRUPTS[6].is_none()); 91 | assert!(__CORE_INTERRUPTS[7].is_some()); 92 | 93 | assert_eq!(Priority::P0.number(), 0); 94 | assert_eq!(Priority::P1.number(), 1); 95 | assert_eq!(Priority::P2.number(), 2); 96 | assert_eq!(Priority::P3.number(), 3); 97 | 98 | assert_eq!(Priority::from_number(0), Ok(Priority::P0)); 99 | assert_eq!(Priority::from_number(1), Ok(Priority::P1)); 100 | assert_eq!(Priority::from_number(2), Ok(Priority::P2)); 101 | assert_eq!(Priority::from_number(3), Ok(Priority::P3)); 102 | assert_eq!(Priority::from_number(4), Err(Error::InvalidVariant(4))); 103 | 104 | assert_eq!(Priority::MAX_PRIORITY_NUMBER, 3); 105 | 106 | assert_eq!(HartId::H0.number(), 0); 107 | assert_eq!(HartId::H1.number(), 1); 108 | assert_eq!(HartId::H2.number(), 2); 109 | 110 | assert_eq!(HartId::from_number(0), Ok(HartId::H0)); 111 | assert_eq!(HartId::from_number(1), Ok(HartId::H1)); 112 | assert_eq!(HartId::from_number(2), Ok(HartId::H2)); 113 | assert_eq!(HartId::from_number(3), Err(Error::InvalidVariant(3))); 114 | 115 | assert_eq!(HartId::MAX_HART_ID_NUMBER, 2); 116 | } 117 | -------------------------------------------------------------------------------- /tests-trybuild/tests/test.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn riscv() { 3 | let t = trybuild::TestCases::new(); 4 | 5 | t.compile_fail("tests/riscv/fail_*.rs"); 6 | t.pass("tests/riscv/pass_*.rs"); 7 | } 8 | 9 | #[test] 10 | fn riscv_rt() { 11 | let t = trybuild::TestCases::new(); 12 | 13 | t.compile_fail("tests/riscv-rt/*/fail_*.rs"); 14 | t.pass("tests/riscv-rt/*/pass_*.rs"); 15 | } 16 | --------------------------------------------------------------------------------