@@ -25,36 +25,45 @@ use hil_test as _;
2525
2626static SWINT0 : Mutex < RefCell < Option < SoftwareInterrupt < 0 > > > > = Mutex :: new ( RefCell :: new ( None ) ) ;
2727
28- #[ allow( unused) ] // TODO: Remove attribute when interrupt latency test re-enabled
28+ #[ unsafe( no_mangle) ]
29+ static mut LAST_PERF : u32 = 0 ;
30+
31+ #[ unsafe( no_mangle) ]
32+ static mut SW_TRIGGER_ADDR : * mut u32 = core:: ptr:: null_mut ( ) ;
33+
2934struct Context {
3035 sw0_trigger_addr : u32 ,
3136}
3237
38+ // We need to place this close to the trap handler for the jump to be resolved properly
39+ #[ unsafe( link_section = ".trap.rust" ) ]
3340#[ unsafe( no_mangle) ]
34- fn interrupt20 ( ) {
35- unsafe { asm ! ( "csrrwi x0, 0x7e1, 0 #disable timer" ) }
36- critical_section:: with ( |cs| {
37- SWINT0 . borrow_ref ( cs) . as_ref ( ) . unwrap ( ) . reset ( ) ;
38- } ) ;
39-
40- let mut perf_counter: u32 = 0 ;
41- unsafe {
42- asm ! (
43- "
44- csrr {x}, 0x7e2
45- " ,
46- options( nostack) ,
47- x = inout( reg) perf_counter,
48- )
49- } ;
50- defmt:: info!( "Performance counter:{}" , perf_counter) ;
51- // TODO these values should be adjusted to catch smaller regressions
52- cfg_if:: cfg_if! {
53- if #[ cfg( any( feature = "esp32c3" , feature = "esp32c2" ) ) ] {
54- assert!( perf_counter < 1100 ) ;
55- } else {
56- assert!( perf_counter < 750 ) ;
57- }
41+ #[ unsafe( naked) ]
42+ #[ rustfmt:: skip]
43+ unsafe extern "C" fn interrupt_handler ( ) {
44+ core:: arch:: naked_asm! { "
45+ # save affected registers
46+ addi sp, sp, -16*4 # allocate 16 words for saving regs (will work with just 2, but RISC-V wants it to be aligned by 16)
47+ sw t0, 1*4(sp)
48+ sw t1, 2*4(sp)
49+
50+ csrrwi x0, 0x7e1, 0 #disable timer
51+
52+ lw t0, {sw} # t0 <- &SW_TRIGGER_ADDR
53+ sw x0, 0(t0) # clear (deassert) *SW_TRIGGER_ADDR
54+
55+ csrr t1, 0x7e2 # read performance counter
56+ la t0, {x}
57+ sw t1, 0(t0) # store word to LAST_PERF
58+
59+ # restore affected registers
60+ lw t0, 1*4(sp)
61+ lw t1, 2*4(sp)
62+ addi sp, sp, 16*4
63+ mret
64+ " ,
65+ x = sym LAST_PERF ,
66+ sw = sym SW_TRIGGER_ADDR ,
5867 }
5968}
6069
@@ -77,47 +86,66 @@ mod tests {
7786 }
7887
7988 let sw0_trigger_addr = cpu_intr. register_block ( ) . cpu_intr_from_cpu ( 0 ) as * const _ as u32 ;
89+ unsafe {
90+ SW_TRIGGER_ADDR = sw0_trigger_addr as * mut u32 ;
91+ }
8092
8193 critical_section:: with ( |cs| {
8294 SWINT0
8395 . borrow_ref_mut ( cs)
8496 . replace ( sw_ints. software_interrupt0 )
8597 } ) ;
98+
8699 interrupt:: enable_direct (
87100 Interrupt :: FROM_CPU_INTR0 ,
88101 Priority :: Priority3 ,
89102 CpuInterrupt :: Interrupt20 ,
103+ interrupt_handler,
90104 )
91105 . unwrap ( ) ;
92106
93107 Context { sw0_trigger_addr }
94108 }
95109
110+ // Handler function (assigned above) reads cycles count from CSR and stores it into `LAST_PERF`
111+ // static, which this test then reads and asserts its value to detect regressions
96112 #[ test]
97113 #[ rustfmt:: skip]
98- fn interrupt_latency ( _ctx : Context ) {
99- // unsafe {
100- // asm!(
101- // "
102- // csrrwi x0, 0x7e0, 1 #what to count, for cycles write 1 for instructions write 2
103- // csrrwi x0, 0x7e1, 0 #disable counter
104- // csrrwi x0, 0x7e2, 0 #reset counter
105- // "
106- // );
107- // }
108-
109- // // interrupt is raised from assembly for max timer granularity.
110- // unsafe {
111- // asm!(
112- // "
113- // li {bit}, 1 # Flip flag (bit 0)
114- // csrrwi x0, 0x7e1, 1 # enable timer
115- // sw {bit}, 0({addr}) # trigger FROM_CPU_INTR0
116- // ",
117- // options(nostack),
118- // addr = in(reg) ctx.sw0_trigger_addr,
119- // bit = out(reg) _,
120- // )
121- // }
114+ fn interrupt_latency ( ctx : Context ) {
115+ unsafe {
116+ asm ! (
117+ "
118+ csrrwi x0, 0x7e0, 1 #what to count, for cycles write 1 for instructions write 2
119+ csrrwi x0, 0x7e1, 0 #disable counter
120+ csrrwi x0, 0x7e2, 0 #reset counter
121+ "
122+ ) ;
123+ }
124+
125+ // interrupt is raised from assembly for max timer granularity.
126+ unsafe {
127+ asm ! (
128+ "
129+ li {bit}, 1 # Flip flag (bit 0)
130+ csrrwi x0, 0x7e1, 1 # enable timer
131+ sw {bit}, 0({addr}) # trigger FROM_CPU_INTR0
132+ " ,
133+ options( nostack) ,
134+ addr = in( reg) ctx. sw0_trigger_addr,
135+ bit = out( reg) _,
136+ )
137+ }
138+
139+ let perf_counter = unsafe { LAST_PERF } ;
140+ defmt:: info!( "Performance counter: {}" , perf_counter) ;
141+
142+ // TODO c3/c2 values should be adjusted to catch smaller regressions
143+ cfg_if:: cfg_if! {
144+ if #[ cfg( any( feature = "esp32c3" , feature = "esp32c2" ) ) ] {
145+ assert!( perf_counter < 400 ) ;
146+ } else {
147+ assert!( perf_counter < 155 ) ;
148+ }
149+ }
122150 }
123151}
0 commit comments