-
-
Notifications
You must be signed in to change notification settings - Fork 779
RISC-V: Update support for HiFive1 board #1324
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
The initial version of the `UserlandKernelBoundary` trait abstracted details about context switching on cortex-m platforms out of process.rs, however, the trait was very much designed around the structure of how cortex-m handles switching between privilege modes. After working on RISC-V context switching, it is clear some of the interface does not map to RISC-V very well. To make `UserlandKernelBoundary` more generic, this commit makes two general changes to the trait. 1. Rather than having a separate "getter" for retreiving which syscall was actually called after learning that the process stopped running because it called a syscall, we now return the syscall information directly with the context switch reason. Arguably, this is how it should have been done before, and this change actually has nothing to do with making UKB more generic. 2. The Cortex-M context switch uses a special stack frame created by the SVC call. This approach is not used in RISC-V. Therefore, the terminology around "popping" and "pushing" stack frames doesn't make sense in all cases. Also, having `pop_stack_frame()` at all was a bit of blur between the kernel loop and the context switch interface details as pop was only called with a yield(). What is generic is the idea that we want to be able to call a function in the process that the process should execute when it starts (or resumes) running. That function takes over the old "push" function. "pop" has been removed. I believe that this new interface will be implementable in RISC-V, as well as Cortex-M, and actually improves the boundary between the kernel loop and the context switching code. This commit also then updates process.rs and sched.rs to use this new interface. This required some minor changes to process.rs as these functions are essentially passed through process.rs from the kernel sched loop to the UKB code.
For portability reasons the syscall interface has been updated, and this commit implements it for cortex-m. The major change is that we no longer remove the SVC stack frame after a yield call. In fact, the API for popping stack frames has been removed. Instead, when we want the process to run a new function, we re-use the old SVC stack frame. In the case that the app has never run before we do have to create the stack frame just like we used to. In theory, this is actually more efficient since we save the effort of removing the stack frame, but that wasn't very substancial so who knows. The other change is we now set state.psr and state.yield_pc after every syscall, and not just effectively after yield(). I don't think this has any effect.
Obviously setting the baud rate for UART depends on the clock speed. Make this a parameter because different boards use different settings. We probably want a more formal way to do this, but this is simple and works for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall this looks quite reasonable, but I commented on some code that stuck out.
| [dependencies] | ||
| kernel = { path = "../../kernel" } | ||
| tock_rt0 = { path = "../../libraries/tock-rt0" } | ||
| riscvregs = { git = "https://github.com/DieracDelta/riscvregs.git", rev = "c03eb6c" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately currently Tock doesn't support (i.e. Tock doesn't allow) external dependencies because we cannot track unsafe use in dependencies currently. So, to use a library like this it would either need to be included in the /library folder in Tock, or included as a module in the rv32i crate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be reasonable to add this in as a git submodule under libraries? Or should I manually move it into the library folder and abandon its git history?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally we'd avoid git submodules where possible (it's just a bigger hassle to develop with). Moving this into the repo would be great. You can technically do so without losing the git history with some git magic (git filter-branch i believe).
| None => (), | ||
| Some(ext_interrupt_id) => { | ||
| debug!("interrupt triggered {}\n", ext_interrupt_id); | ||
| plic::complete(ext_interrupt_id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't look quite right. The trap handler should only disable the interrupt, not mark it as finished. Or does this not work how I think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple of things to note here. Spec is best described in section 10.7 here. It's under-specified by the spec what happens if you claim the interrupt (read the claim/complete MMIO) multiple times. So at the very least I don't think we can rely on being able to handle the interrupt anytime after this. On the e310 board, from the testing I've done (for example, add in another claim before completing in this same spot), you get interrupt id 0 (no more pending interrupts). So we either need to claim, handle the interrupt, and complete the interrupt here, or have some global "in progress" state that we write the ID to and then read from in service_pending_interrupts to complete. This seemed easier at the time. With this added knowledge, do you think it's worth changing? If so, I can probably get a change out quickly.
You're right, I should disable the interrupt here.
Ideally, all interrupts should be suppressed after the reset_handler, so my intention was just to notify that the interrupt occurred (as it's not expected behavior), and then ignore it.
| } | ||
| } | ||
|
|
||
| pub unsafe fn surpress_all() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add comment.
| : | ||
| : "volatile"); | ||
| /// The trap handler is called on exceptions and for interrupts. | ||
| pub unsafe fn configure_trap_handler(mode: PermissionMode) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to keep the comment about how this is a generic implementation that specific boards may not want to use.
|
I added a couple of things to get c_hello to run (see most recent push):
for the end of the inline assembly snippet in switch_to_process using the code as it currently stands in the riscv-pr branch. This is bad because
|
|
Hmm bors closed this...that was unexpected. @DieracDelta can you rebase this and re-open? |
Pull Request Overview
This is an attempt to merge MIT's branch of Tock into upstream for RISC-V, and then presumably master.
The major additions were:
- MMIO
- read/write for time/timecp
- triggering software interrupts
- Note that it seems like this memory region got replaced in the Arty-e21 board by just a timer, and you've abstracted over it with a machine timer. If we reduce the memory map for that machine timer (or distinguish it/include it as a parameter with different types), we could reuse the abstraction for both the clint and the machine-timer.
- I've implemented the bitfields in the library, so this should make things easier, as we can rely on this abstraction.
- I don't have any way of testing this outside of qemu (only have e310 board), so would appreciate some help from people with a board.
- Do we want to have the PMP implement the same things as the MPU? Presumably on most implementations, we can have it provide similar functionality. As it stands right now, the PMP is implementing the kernel::mpu::MPU trait. Does it make sense to refactor with the MPU renamed to something more general--perhaps a MemoryProtector trait--and have the PMP and the cortex-ms have the MPU and PMP implement that trait?
- Claims and completes external interrupts.
- Distinguishes each type of trap
- Exceptions include debugging information.
Testing Strategy
I was able to test on the Hifive1 board using the included Makefile. Oddly, was not able to get gdb to work with OpenOCD. Has anyone had success with this? I'd been running something along the lines of:
With a gdbinit file that looked like:
It fails due to not having enough space to load into. Has anyone encountered this?
TODO or Help Wanted
See prior section.
Documentation Updated
/docs, or no updates are required.Formatting
make formatall.