Skip to content

Commit 94b6474

Browse files
authored
Merge pull request #51 from greenlsi/virq
[New feature] vectorized interrupts: virq
2 parents d975ba7 + c6fbdb8 commit 94b6474

File tree

9 files changed

+353
-3
lines changed

9 files changed

+353
-3
lines changed

.github/workflows/ci.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ jobs:
3232
run: |
3333
cargo check --target riscv32imac-unknown-none-elf
3434
cargo check --target riscv32imac-unknown-none-elf --features g002
35+
cargo check --target riscv32imac-unknown-none-elf --features virq
36+
cargo check --target riscv32imac-unknown-none-elf --features g002,virq
3537
3638
# On macOS and Windows, we at least make sure that the crate builds and links.
3739
build-other:
@@ -52,4 +54,6 @@ jobs:
5254
- name: Build crate for host OS
5355
run: |
5456
cargo build
55-
cargo build --features g002
57+
cargo build --features g002
58+
cargo build --features virq
59+
cargo build --features g002,virq

.vscode/settings.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"[rust]": {
3+
"editor.defaultFormatter": "rust-lang.rust-analyzer",
4+
"editor.formatOnSave": true
5+
},
6+
"rust-analyzer.cargo.features": [
7+
"g002",
8+
"virq",
9+
]
10+
}

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
99

1010
- Refactored `e310x-hal::spi` module, splitting the abstraction into `SpiBus` and `SpiExclusiveDevice/SpiSharedDevice` to allow multiple devices on a single SPI bus to co-exist
1111
- Added Pulse Width Modulation interface implementing `embedded_hal::Pwm`
12+
- Added `interrupt` module for vectored interrupt handlers.
13+
This module is only active if feature `virq` is selected.
1214
- Update `e310x` dependency to version 0.10
1315
- Update `riscv` dependency to version 0.8
1416

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "e310x-hal"
3-
version = "0.9.3"
3+
version = "0.10.0"
44
authors = ["David Craven <david@craven.ch>"]
55
repository = "https://github.com/riscv-rust/e310x-hal"
66
categories = ["embedded", "hardware-support", "no-std"]
@@ -18,6 +18,7 @@ e310x = { version = "0.10.0", features = ["rt"] }
1818

1919
[features]
2020
g002 = ["e310x/g002"]
21+
virq = []
2122

2223
[package.metadata.docs.rs]
23-
features = ["g002"]
24+
features = ["g002", "virq"]

build.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
use std::path::PathBuf;
2+
use std::{env, fs};
3+
4+
fn main() {
5+
// Put the linker script somewhere the linker can find it
6+
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
7+
println!("cargo:rustc-link-search={}", out_dir.display());
8+
fs::copy("fe310x-interrupt.x", out_dir.join("fe310x-interrupt.x")).unwrap();
9+
println!("cargo:rerun-if-changed=fe310x-interrupt.x");
10+
}

fe310x-interrupt.x

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/* This file should be included by hifive1 if virq feature is enabled */
2+
3+
/* Weak symbol for other machine external interrupts handler */
4+
PROVIDE(OtherMachineExternal = DefaultMachineExternal);
5+
6+
/* Weak symbols for each external interrupt handler */
7+
PROVIDE(WATCHDOG = OtherMachineExternal);
8+
PROVIDE(RTC = OtherMachineExternal);
9+
PROVIDE(UART0 = OtherMachineExternal);
10+
PROVIDE(UART1 = OtherMachineExternal);
11+
PROVIDE(QSPI0 = OtherMachineExternal);
12+
PROVIDE(QSPI1 = OtherMachineExternal);
13+
PROVIDE(QSPI2 = OtherMachineExternal);
14+
PROVIDE(GPIO0 = OtherMachineExternal);
15+
PROVIDE(GPIO1 = OtherMachineExternal);
16+
PROVIDE(GPIO2 = OtherMachineExternal);
17+
PROVIDE(GPIO3 = OtherMachineExternal);
18+
PROVIDE(GPIO4 = OtherMachineExternal);
19+
PROVIDE(GPIO5 = OtherMachineExternal);
20+
PROVIDE(GPIO6 = OtherMachineExternal);
21+
PROVIDE(GPIO7 = OtherMachineExternal);
22+
PROVIDE(GPIO8 = OtherMachineExternal);
23+
PROVIDE(GPIO9 = OtherMachineExternal);
24+
PROVIDE(GPIO10 = OtherMachineExternal);
25+
PROVIDE(GPIO11 = OtherMachineExternal);
26+
PROVIDE(GPIO12 = OtherMachineExternal);
27+
PROVIDE(GPIO13 = OtherMachineExternal);
28+
PROVIDE(GPIO14 = OtherMachineExternal);
29+
PROVIDE(GPIO15 = OtherMachineExternal);
30+
PROVIDE(GPIO16 = OtherMachineExternal);
31+
PROVIDE(GPIO17 = OtherMachineExternal);
32+
PROVIDE(GPIO18 = OtherMachineExternal);
33+
PROVIDE(GPIO19 = OtherMachineExternal);
34+
PROVIDE(GPIO20 = OtherMachineExternal);
35+
PROVIDE(GPIO21 = OtherMachineExternal);
36+
PROVIDE(GPIO22 = OtherMachineExternal);
37+
PROVIDE(GPIO23 = OtherMachineExternal);
38+
PROVIDE(GPIO24 = OtherMachineExternal);
39+
PROVIDE(GPIO25 = OtherMachineExternal);
40+
PROVIDE(GPIO26 = OtherMachineExternal);
41+
PROVIDE(GPIO27 = OtherMachineExternal);
42+
PROVIDE(GPIO28 = OtherMachineExternal);
43+
PROVIDE(GPIO29 = OtherMachineExternal);
44+
PROVIDE(GPIO30 = OtherMachineExternal);
45+
PROVIDE(GPIO31 = OtherMachineExternal);
46+
PROVIDE(PWM0CMP0 = OtherMachineExternal);
47+
PROVIDE(PWM0CMP1 = OtherMachineExternal);
48+
PROVIDE(PWM0CMP2 = OtherMachineExternal);
49+
PROVIDE(PWM0CMP3 = OtherMachineExternal);
50+
PROVIDE(PWM1CMP0 = OtherMachineExternal);
51+
PROVIDE(PWM1CMP1 = OtherMachineExternal);
52+
PROVIDE(PWM1CMP2 = OtherMachineExternal);
53+
PROVIDE(PWM1CMP3 = OtherMachineExternal);
54+
PROVIDE(PWM2CMP0 = OtherMachineExternal);
55+
PROVIDE(PWM2CMP1 = OtherMachineExternal);
56+
PROVIDE(PWM2CMP2 = OtherMachineExternal);
57+
PROVIDE(PWM2CMP3 = OtherMachineExternal);
58+
/* Weak symbol for I2C0 (g002 only) */
59+
PROVIDE(I2C0 = OtherMachineExternal);

interrupts.x

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/* This file should be included by hifive1 if virq feature is enabled */
2+
3+
/* Weak symbol for other machine external interrupts handler */
4+
PROVIDE(OtherMachineExternal = DefaultMachineExternal);
5+
6+
/* Weak symbols for each external interrupt handler */
7+
PROVIDE(WATCHDOG = OtherMachineExternal);
8+
PROVIDE(RTC = OtherMachineExternal);
9+
PROVIDE(UART0 = OtherMachineExternal);
10+
PROVIDE(UART1 = OtherMachineExternal);
11+
PROVIDE(QSPI0 = OtherMachineExternal);
12+
PROVIDE(QSPI1 = OtherMachineExternal);
13+
PROVIDE(QSPI2 = OtherMachineExternal);
14+
PROVIDE(GPIO0 = OtherMachineExternal);
15+
PROVIDE(GPIO1 = OtherMachineExternal);
16+
PROVIDE(GPIO2 = OtherMachineExternal);
17+
PROVIDE(GPIO3 = OtherMachineExternal);
18+
PROVIDE(GPIO4 = OtherMachineExternal);
19+
PROVIDE(GPIO5 = OtherMachineExternal);
20+
PROVIDE(GPIO6 = OtherMachineExternal);
21+
PROVIDE(GPIO7 = OtherMachineExternal);
22+
PROVIDE(GPIO8 = OtherMachineExternal);
23+
PROVIDE(GPIO9 = OtherMachineExternal);
24+
PROVIDE(GPIO10 = OtherMachineExternal);
25+
PROVIDE(GPIO11 = OtherMachineExternal);
26+
PROVIDE(GPIO12 = OtherMachineExternal);
27+
PROVIDE(GPIO13 = OtherMachineExternal);
28+
PROVIDE(GPIO14 = OtherMachineExternal);
29+
PROVIDE(GPIO15 = OtherMachineExternal);
30+
PROVIDE(GPIO16 = OtherMachineExternal);
31+
PROVIDE(GPIO17 = OtherMachineExternal);
32+
PROVIDE(GPIO18 = OtherMachineExternal);
33+
PROVIDE(GPIO19 = OtherMachineExternal);
34+
PROVIDE(GPIO20 = OtherMachineExternal);
35+
PROVIDE(GPIO21 = OtherMachineExternal);
36+
PROVIDE(GPIO22 = OtherMachineExternal);
37+
PROVIDE(GPIO23 = OtherMachineExternal);
38+
PROVIDE(GPIO24 = OtherMachineExternal);
39+
PROVIDE(GPIO25 = OtherMachineExternal);
40+
PROVIDE(GPIO26 = OtherMachineExternal);
41+
PROVIDE(GPIO27 = OtherMachineExternal);
42+
PROVIDE(GPIO28 = OtherMachineExternal);
43+
PROVIDE(GPIO29 = OtherMachineExternal);
44+
PROVIDE(GPIO30 = OtherMachineExternal);
45+
PROVIDE(GPIO31 = OtherMachineExternal);
46+
PROVIDE(PWM0CMP0 = OtherMachineExternal);
47+
PROVIDE(PWM0CMP1 = OtherMachineExternal);
48+
PROVIDE(PWM0CMP2 = OtherMachineExternal);
49+
PROVIDE(PWM0CMP3 = OtherMachineExternal);
50+
PROVIDE(PWM1CMP0 = OtherMachineExternal);
51+
PROVIDE(PWM1CMP1 = OtherMachineExternal);
52+
PROVIDE(PWM1CMP2 = OtherMachineExternal);
53+
PROVIDE(PWM1CMP3 = OtherMachineExternal);
54+
PROVIDE(PWM2CMP0 = OtherMachineExternal);
55+
PROVIDE(PWM2CMP1 = OtherMachineExternal);
56+
PROVIDE(PWM2CMP2 = OtherMachineExternal);
57+
PROVIDE(PWM2CMP3 = OtherMachineExternal);
58+
/* Weak symbol for I2C0 (g002 only) */
59+
PROVIDE(I2C0 = OtherMachineExternal);

src/interrupt.rs

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
//! Vectored machine external interrupt handler.
2+
//!
3+
//! # Notes
4+
//!
5+
//! - You must activate the `virq` feature to use this module.
6+
//!
7+
//! - The vectored handler automatically claims the PLIC interrupt source as complete.
8+
//! Thus, users do not have to worry about this step.
9+
//!
10+
//! # Features
11+
//!
12+
//! This module provides:
13+
//!
14+
//! - A vectored implementation for handling each machine external interrupt source independently.
15+
//!
16+
//! - A linker script that provides weak symbols for all the interrupt sources of an E310X microcontroller.
17+
//! This file must be supplied using rustflag when compiling.
18+
//!
19+
//! # Implementation details
20+
//!
21+
//! You can define a custom handler for each interrupt source (see [`e310x::interrupt::Interrupt`]).
22+
//! For instance, if you want to define a custom handler for interrupts triggered by
23+
//! the [`e310x::interrupt::Interrupt::GPIO0`] source, you must define the `GPIO0` function:
24+
//!
25+
//! ```ignore
26+
//! #[no_mangle]
27+
//! #[allow(non_snake_case)]
28+
//! fn GPIO0() {
29+
//! // define the behavior of your custom handler
30+
//! }
31+
//! ```
32+
//!
33+
//! Note that the function must be marked as `no_mangle`.
34+
//! You can also use the [`e310x::interrupt!`] macro.
35+
//!
36+
//! If a source without custom handler triggers an interruption, it executes the
37+
//! `OtherMachineExternal` handler. This handler function is shared among all the
38+
//! undefined interrupt sources. You can define this handler as follows:
39+
//!
40+
//! ```ignore,no_run
41+
//! #[no_mangle]
42+
//! #[allow(non_snake_case)]
43+
//! fn OtherMachineExternal() {
44+
//! // define the behavior of this handler
45+
//! }
46+
//! ```
47+
//!
48+
//! By default, `OtherMachineExternal` executes the [`DefaultMachineExternal`] handler.
49+
//! This handler is just an infinite loop.
50+
51+
use crate::core::CorePeripherals;
52+
pub use e310x::interrupt::*;
53+
54+
extern "C" {
55+
fn WATCHDOG();
56+
fn RTC();
57+
fn UART0();
58+
fn UART1();
59+
fn QSPI0();
60+
fn QSPI1();
61+
fn QSPI2();
62+
fn GPIO0();
63+
fn GPIO1();
64+
fn GPIO2();
65+
fn GPIO3();
66+
fn GPIO4();
67+
fn GPIO5();
68+
fn GPIO6();
69+
fn GPIO7();
70+
fn GPIO8();
71+
fn GPIO9();
72+
fn GPIO10();
73+
fn GPIO11();
74+
fn GPIO12();
75+
fn GPIO13();
76+
fn GPIO14();
77+
fn GPIO15();
78+
fn GPIO16();
79+
fn GPIO17();
80+
fn GPIO18();
81+
fn GPIO19();
82+
fn GPIO20();
83+
fn GPIO21();
84+
fn GPIO22();
85+
fn GPIO23();
86+
fn GPIO24();
87+
fn GPIO25();
88+
fn GPIO26();
89+
fn GPIO27();
90+
fn GPIO28();
91+
fn GPIO29();
92+
fn GPIO30();
93+
fn GPIO31();
94+
fn PWM0CMP0();
95+
fn PWM0CMP1();
96+
fn PWM0CMP2();
97+
fn PWM0CMP3();
98+
fn PWM1CMP0();
99+
fn PWM1CMP1();
100+
fn PWM1CMP2();
101+
fn PWM1CMP3();
102+
fn PWM2CMP0();
103+
fn PWM2CMP1();
104+
fn PWM2CMP2();
105+
fn PWM2CMP3();
106+
#[cfg(feature = "g002")]
107+
fn I2C0();
108+
}
109+
110+
#[no_mangle]
111+
#[allow(non_snake_case)]
112+
/// Default machine external interrupt handler. It is an infinite loop.
113+
pub fn DefaultMachineExternal() {
114+
loop {
115+
// Prevent this from turning into a UDF instruction
116+
// see rust-lang/rust#28728 for details
117+
continue;
118+
}
119+
}
120+
121+
#[cfg(not(feature = "g002"))]
122+
const N_INTERRUPTS: usize = 51;
123+
#[cfg(feature = "g002")]
124+
const N_INTERRUPTS: usize = 52;
125+
126+
/// Array of machine external interrupt handlers.
127+
static HANDLERS: [unsafe extern "C" fn(); N_INTERRUPTS] = [
128+
WATCHDOG,
129+
RTC,
130+
UART0,
131+
UART1,
132+
QSPI0,
133+
QSPI1,
134+
QSPI2,
135+
GPIO0,
136+
GPIO1,
137+
GPIO2,
138+
GPIO3,
139+
GPIO4,
140+
GPIO5,
141+
GPIO6,
142+
GPIO7,
143+
GPIO8,
144+
GPIO9,
145+
GPIO10,
146+
GPIO11,
147+
GPIO12,
148+
GPIO13,
149+
GPIO14,
150+
GPIO15,
151+
GPIO16,
152+
GPIO17,
153+
GPIO18,
154+
GPIO19,
155+
GPIO20,
156+
GPIO21,
157+
GPIO22,
158+
GPIO23,
159+
GPIO24,
160+
GPIO25,
161+
GPIO26,
162+
GPIO27,
163+
GPIO28,
164+
GPIO29,
165+
GPIO30,
166+
GPIO31,
167+
PWM0CMP0,
168+
PWM0CMP1,
169+
PWM0CMP2,
170+
PWM0CMP3,
171+
PWM1CMP0,
172+
PWM1CMP1,
173+
PWM1CMP2,
174+
PWM1CMP3,
175+
PWM2CMP0,
176+
PWM2CMP1,
177+
PWM2CMP2,
178+
PWM2CMP3,
179+
#[cfg(feature = "g002")]
180+
I2C0,
181+
];
182+
183+
/// Handler for vectored machine external interrupts (see the [`riscv-rt`] crate).
184+
#[no_mangle]
185+
#[allow(non_snake_case)]
186+
unsafe fn MachineExternal() {
187+
// Steal the PLIC peripheral and claim the interrupt
188+
let mut plic = CorePeripherals::steal().plic;
189+
let interrupt = plic.claim.claim().unwrap();
190+
let interrupt_n = interrupt as usize;
191+
// Match the appropriate machine external interrupt
192+
if interrupt_n == 0 {
193+
// Interrupt number 0 is defined as no interrupt
194+
} else if interrupt_n <= HANDLERS.len() {
195+
// Execute corresponding interrupt handler
196+
HANDLERS[interrupt_n - 1]();
197+
} else {
198+
// Any other interrupt number is not allowed
199+
DefaultMachineExternal();
200+
}
201+
// Claim PLIC interrupt source as complete by this handler
202+
plic.claim.complete(interrupt);
203+
}

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,7 @@ pub mod wdog;
2525

2626
#[cfg(feature = "g002")]
2727
pub mod i2c;
28+
#[cfg(feature = "virq")]
29+
pub mod interrupt;
2830

2931
pub use device::DeviceResources;

0 commit comments

Comments
 (0)