Skip to content

Conversation

@alevy
Copy link
Member

@alevy alevy commented Nov 15, 2017

Pull Request Overview

Instead of using a global queue to notify the event loop about pending
interrups, this commit uses the built in NVIC pending bits themselves.
This has a few benefits:

  1. It removes controller specific ISRs that do nothing but enqueue an interrupt event.
  2. It makes this code generic for all Cortex-M4 chips (and should be ported to Cortex-M0 et
    al). As a result of both of these, it removes about 10kB of code on hail
    and imix.
  3. it removes the need for controller drivers to handle
    enabling/disabling nvic lines themselves.
  4. it's much faster since enqueing and dequeuing in the ring buffer is actually pretty expensive.

Downsides?

  • The nvic indexes can't be an enum anymore since those "names" are chip specific. Oh well...

Testing Strategy

In principle, this should be a pretty transparent change except some drivers relied on explicit nvic handling to disable interrupts. I believe I've fixed that in all cases, and have tested a number of the example apps on hail and imix. However, more comprehensive testing would be useful.

Tested fairly extensively on Hail, NRF51-DK and NRF52-DK. On Hail, we've run a bunch of apps, including tests/hail, rot13, test/nonvolatile_storage, blink and on the NRFs we ran ble_advertising, blink, tests/aes.

TODO or Help Wanted

The flash driver has a particular place where it actually called the ISR explicitly from the code. That ISR no longer exists (and either way would be useless) and I don't understand what role exactly that was playing, or how to reproduce that functionality (perhaps by just calling handle_interrupt, but why did the original code not just do that?).

I left a TODO where that code used to be. Does anyone know how to trigger the issue that led to that hack?

Documentation Updated

  • Kernel: The relevant files in /docs have been updated or no updates are required.
  • Userland: The application README has been added, updated, or no updates are required.

Formatting

  • make formatall has been run.

Copy link
Contributor

@bradjc bradjc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And of course the issues with flash discussed on irc.

@@ -0,0 +1,110 @@
//! Cortex-M3 NVIC
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cortext-M4?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's just... like... your opinion man

@bradjc
Copy link
Contributor

bradjc commented Nov 17, 2017

Oh I also tested with the blink, hail, and rot13 apps all running concurrently and they seemed to work.

/* Set thread mode to privileged */
mov r0, #0
msr CONTROL, r0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

generic_isr is only called to service interrupts, correct? My reading of B1.3.1 of the ARM v7m ARM is that the processor should be in Handler mode, which is by definition already privileged. I don't think this is needed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed to make sure we're in privileged mode when we return the kernel, not for this handler's code.

ldr r1, =INTERRUPT_TABLE
ldr r0, [r1, r0]
/* r1 = NVIC.ICER[r0 / 32] */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From this comment, I expect one of the instructions below to be a memory read?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, it's taking an address: r1 = &NVIC.ICER[r0 / 32]

movt r1, #0xe000
lsr r2, r0, #5
mov r3, #4
mul r2, r3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why load 4 into a register and multiply instead of just shifting again here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cause like... I don't know how to do that... would it just be lsr r2, r0, #7?

/* r2 = 1 << (r0 & 31) */
mov r2, #1
mov r3, #31
and r3, r0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can consolidate these two instructions to just
and r3, r0, #31

use kernel::common::volatile_cell::VolatileCell;

#[repr(C, packed)]
// Registers for the NVIC. Each
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each what? Don't leave me hanging! :D

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You get the answer when you buy the DLC

@alevy alevy force-pushed the feature/interruptopt branch from 5ab0566 to 5482c8a Compare November 21, 2017 01:05
@alevy
Copy link
Member Author

alevy commented Nov 21, 2017

Rebased, fixedup, ported to NRF52, cortex-m0/nrf51, but need to test after the rebase and some cleanup.

Last I tested thoroughly before the rebase, sam4l was working as was nrf52. More tomorrow

@alevy alevy changed the title Replace SAM4L queue-based interrupts with NVIC regs Replace queue-based interrupts with NVIC regs Nov 21, 2017
@alevy alevy force-pushed the feature/interruptopt branch from 4ecfbfc to 6d58a5f Compare November 21, 2017 14:40
@niklasad1
Copy link
Member

Looks good, have you tested it on both nRF51-DK and nRF52-DK?

@alevy
Copy link
Member Author

alevy commented Nov 21, 2017

@niklasad1 literally just tested right now, and after some fixes, it's all working.

The one thing i noticed is that ble_passive_scan seems to block after a few scans, but that is happening on master as well.

@alevy
Copy link
Member Author

alevy commented Nov 21, 2017

I think this is ready, maybe it could use some more assembly auditing from the venerable @ppannuto. I trust the assembly insofar as I've tested it, but @ppanuto's caught obvious things in the past that I've missed, and is also good at asking for the right documentation for assembly.

Copy link
Contributor

@bradjc bradjc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't look super thoroughly, just at the parts I've been looking at.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

M0 now?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gah! Too many files!!!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't want this in Hail proper.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't need this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, removed it and I trust you, but I needed it before your changes on master, so should test this just in case before merging (which I can do first thing tomorrow)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested. Works.

alevy added 15 commits November 21, 2017 21:49
Instead of using a global queue to notify the event loop about pending
interrups, this commit uses the built in NVIC pending bits themselves.
This has a few benefits. First, it removes controller specific ISRs that
do nothing but enqueue an interrupt event. Second, it makes this code
generic for all Cortex-M4 chips (and should be ported to Cortex-M0 et
al). As a result of both of these, it removes about 10kB of code on hail
and imix. Third, it removes the need for controller drivers to handle
enabling/disabling nvic lines themselves. Fourth, it's much faster since
enqueing and dequeuing in the ring buffer is actually pretty expensive.
@alevy alevy force-pushed the feature/interruptopt branch from 77ac17f to b87b353 Compare November 22, 2017 02:53
Copy link
Member

@ppannuto ppannuto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just looked over the assembly

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comment still appropriate? (also weird spacing for this block?)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shift right 31? Is this supposed to be the r0 / 32 step? I'd expect that to be a shift right of 5 bits?

I think the readability here in general could be improve with some pedantry for commenting assembly, i.e.
asrs r3, r0, #5 /* r3 = r0 / 32 */
where every step lists its purpose. Assembly arithmetic is hard enough to justify this

/* NVIC.ICER[r0 / 32] = 1 << (r0 & 31) */
movs r2, #1
movs r3, #32
sdiv r3, r0, r3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace these two with asr r3, r0, #5? Shorter and matches m0 implementation

/* NVIC.ICER[r0 / 32] = 1 << (r0 & 31) */
movs r2, #31
asrs r3, r0, #31
ands r3, r2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now I'm a bit more confused, as it looks like this is and'ing with the shifted r0? I think commenting each step will alleviate confusion here.

@alevy
Copy link
Member Author

alevy commented Nov 22, 2017

@ppannuto I cleaned up and commented the assembly for disabling nvics in the ISR.

I think the problem was that I had compiled the assembly from C code that used signed (rather than unsigned) integers so GCC was be overly clever. I re-did it with unsigned ints and the result was good enough for me to understand and comment.

@alevy alevy force-pushed the feature/interruptopt branch from e24c4c5 to ce71eae Compare November 22, 2017 20:56
Copy link
Member

@ppannuto ppannuto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me now. Only blocking thing I have left is the spelling of deferred :)

/* r0 = 1 << (r0 & 31) */
movs r3, #1 /* r3 = 1 */
and r0, r0, #31 /* r0 = r0 & r2 (31) */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: The assembly is right, the comment is wrong (r2 not involved here)

TIMER1 => nrf5x::timer::ALARM1.handle_interrupt(),
TIMER2 => nrf5x::timer::TIMER2.handle_interrupt(),
UART0 => uart::UART0.handle_interrupt(),
_ => debug!("NvicIdx not supported by Tock\r\n"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: don't need \r\n here, debug! adds the line endings for you

GPFRLO,
}

static DEFERED_CALL: DeferedCall = unsafe { DeferedCall::new(Task::Flashcalw) };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pervasive -- and don't hate me -- s/defered/deferred

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ppannuto I couldn't hate you if I wanted to.


pub fn handle_interrupt(&mut self) {
let registers: &mut DMARegisters = unsafe { mem::transmute(self.registers) };
registers.interrupt_disable.set(0xffffffff);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: you use !0 pretty much everywhere else

@alevy
Copy link
Member Author

alevy commented Nov 23, 2017

@ppannuto addressed your comments. I think also all of @bradjc's comments are addressed.

(Fine if approving/merging this waits until after Thanksgiving break)

@niklasad1
Copy link
Member

niklasad1 commented Nov 25, 2017

@alevy yeah, I have noticed that the ble_passive_scan app hangs too.

But I haven't understood how to debug user-space applications so I have ignored it but perhaps best to create an issue! I thought it ran out of memory but I don't know!

@alevy alevy mentioned this pull request Nov 27, 2017
3 tasks
@bradjc bradjc added the kernel label Nov 27, 2017
@ppannuto ppannuto merged commit 1e96512 into tock:master Nov 27, 2017
@alevy alevy deleted the feature/interruptopt branch November 27, 2017 22:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants