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
Board-based instantiation of chip drivers and interrupt --> driver mapping for Apollo3 + SAM4L #2069
Conversation
I was skeptical at first, but now I think this is a very nice change that I would like to see merged. My main comment is just around the naming. I think using "peripheral" instead of "driver" would be less confusing, since
|
I think it would also make sense to start a "layer responsibilities" doc that describes what various layers are tasked with doing, and why. I think this PR adds a rather nonintuitive responsibility for board crates. |
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.
@hudson-ayers thank you for this PR!
I have actually played around with the idea myself when working on the OpenTitan chip_config.rs
, as it is cleaner and more flexible. I have eventually dropped the idea, because it seemed to costly for the benefit of chip_config
alone. You have found stronger reasons, more useful use-cases, making this change more justifiable. I think this is a great idea, thanks again!
Tiny thing, which I think is not an issue is that if board
decides to override a driver - the default chip driver will still be built. I guess that is not much of an issue, as this does not affect the final image size, just increases the intermediate library size and the compilation time. However, I imagine overriding default drivers will probably be a rare occasion, which makes this a non-issue?
pub static mut UART1: Uart = Uart::new(UART1_BASE); | ||
|
||
const UART1_BASE: StaticRef<UartRegisters> = | ||
pub const UART1_BASE: StaticRef<UartRegisters> = | ||
unsafe { StaticRef::new(0x4001_D000 as *const UartRegisters) }; | ||
|
||
register_structs! { |
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.
I think that register definitions should probably be split out in a separate .rs
, as they remain the same regardless what driver we use. Allows to avoid reinventing the wheel for the "board" drivers.
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.
I think that can be done on a per-chip basis as needed. Having everything in one place is convenient in the common case.
chips/apollo3/src/chip.rs
Outdated
impl Apollo3 { | ||
pub unsafe fn new() -> Apollo3 { | ||
Apollo3 { | ||
impl<P: Platform + 'static> Apollo3<P> { |
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.
For earlgrey
, I think it is worth now initialise the chip_config::Config, from chip_config.rs
in the corresponding board
and passing the structure as a parameter into new
.
For OpenTitan it will also allow to remove the following features from the .../earlgrey/Cargo.toml
:
[features]
config_fpga_nexysvideo = ["config_disable_default"]
config_sim_verilator = ["config_disable_default"]
config_disable_default = []
And instead have the config related features entirely in the board Cargo.toml.
P.S.
I know this is apollo
related change, but seems like the most appropriate place to leave this comment.
chips/apollo3/src/gpio.rs
Outdated
@@ -8,13 +8,72 @@ use kernel::common::registers::{register_bitfields, register_structs, ReadWrite} | |||
use kernel::common::StaticRef; | |||
use kernel::hil::gpio; | |||
|
|||
const GPIO_BASE: StaticRef<GpioRegisters> = | |||
pub const GPIO_BASE: StaticRef<GpioRegisters> = |
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.
I think that base address, together with the register definitions should probably be split out in a separate .rs
, as they remain the same regardless what driver we use. Allows to avoid reinventing the wheel for the "board" drivers.
Basically decouple implementation from the register and base definitions.
pub unsafe fn new() -> Apollo3 { | ||
Apollo3 { | ||
impl<I: InterruptService + 'static> Apollo3<I> { | ||
pub unsafe fn new(interrupt_service: &'static I) -> Self { |
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.
I think apollo3_driver_definitions
would need to be passed into the chip, as the chip might want internally use a driver which has been overridden by the board. Otherwise we would have a mismatch of board using one driver implementation, and the board another.
In this case the macro below, probably could just be a function, something like apollo3_default_drivers_get
or something similar.
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.
I think that if a chip needs to use a driver internally, that driver should be a field on the Chip
struct itself, revealing that the Chip depends on that specific driver (and thereby showing that driver is not optional). If we want that driver to further be replaceable by multiple implementations, then introducing a trait for that particular driver would be appropriate. But I think we want to make it explicitly not possible for the chip to depend on the particular contents of whatever struct implements InterruptService
, and instead depend only on the interface exposed by InterruptService
.
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 seems like having that driver be an argument to chip::new()
is reasonable to handle this case.
I have added support for deferred calls, and implemented this for the sam4l + Imix. Doing so ended up requiring..a lot of code changes. I will do this for hail next, but after that I think it might be best to get this in as separate PRs for each subsequent chip. Imix code size report from before this PR:
Imix code size report from after this PR:
Noticeably, almost nothing left in For a minimal "Imix_mini" board that only supports gpio + led + timer, with no modifications to the sam4l crate: with this PR:
before this PR:
This was a rough/quick comparison, and does not exclude debug machinery or the power manager in either example. But it is at least clear that excluding drivers can provide meaningful benefit for code size, both by removing code size from drivers, and by reducing the portion of the core kernel code that is actually included in the final binary. |
Is the data just going into |
I am not sure..is this maybe an MPU alignment thing, where the RAM section is always going to be a power of 2, and the leftover RAM is given to uninitialized variables? Have some numbers: imix_mini2 (current master):
imix_mini (this PR):
Clearly some of .data has moved to .bss, such as the gpio::PA struct. But I don't see why, say, there would be space for an unitinitialized USBC struct, unless I have done something wrong. |
Hmm...given that the sam4l has 8x more flash than RAM, this seems like a change we don't want. RAM usage goes up by (32704-21304) - (3272-64) = 8192 bytes. |
I think this is just an artifact of MPU alignment. If i decrease the stack size on Imix by 100 bytes, then the bss size drops to 24512 (a decrease of 8192 bytes, leaving the total RAM usage the same as without this PR). So the impact of this change on RAM usage is much smaller than that in reality. Note: The MPU minimum alignment for the sam4l is 8K. |
5386a89
to
0f13daf
Compare
I think this is ready for review, for apollo3 + sam4l. I have reverted the By playing with the stack size on current master and this PR to see what stack size marks the boundary where RAM use increases by 8k, I have determined that this PR actually increases memory usage by about 175 bytes. One thing to pay attention to: constructors of chip peripherals are now public, so they can be called from boards. This has the potential to be a bit of a footgun, as only one instance of the peripheral can receive interrupts. We could potentially prevent this with a capability, but for now I think we should just see how it works out. |
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.
I'm not a fan of the giant macro but otherwise I think this is a good improvement. The current way of handling interrupts has always annoyed me and I think this is a bit better.
I reviewed the Apollo3 parts, no the SAM4L though.
I have removed the large macros, and just use structs defined in chips.rs instead. I also added a little documentation to |
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.
Can you fix the merge conflicts?
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.
Looks good for the Apollo3
2f4d934
to
fc0ff7f
Compare
done |
Some new conflicts, resolve and I'm ready to approve. |
@hudson-ayers Have you verified that the non-macro approach does make a smaller binary if you do not use all peripherals? |
Just did, the results seem pretty much the same as using the macro approach -- still significant savings. |
fc0ff7f
to
fda677e
Compare
Merge conflicts have been addressed.
bors r+ |
Build failed: |
looks like a network connection error. bors retry |
Build failed: |
https://git.qemu.org/ is down, so the QEMU CI cannot pass. But neither of these chips is one of the QEMU tested chips anyway, so I think this is safe to manually merge. |
That's a pain. We could use the GitHub mirror instead? |
Given that we already have to rely on Github being up to run CI, that would probably be an improvement. This may be a sufficiently rare occurrence it isn't worth investing the time to switch it though. |
2189: Board based instantiation of chip drivers and interrupt mappings: Msp432 r=hudson-ayers a=hudson-ayers ### Pull Request Overview This pull request ports the MSP432 chip/board to the new peripheral instantiation approach that does not rely on global variables (first proposed in #2069). This chip was relatively easy to change compared to some of the others, but @lebakassemmerl I would still appreciate a quick look over the changes or a test of a couple apps to ensure I haven't done anything stupid. This is the last remaining chip before all upstream chips/boards have been ported! ### Testing Strategy This pull request was tested by compiling, hardware testing would be nice. ### TODO or Help Wanted N/A ### Documentation Updated - [x] Updated the relevant files in `/docs`, or no updates are required. ### Formatting - [x] Ran `make prepush`. Co-authored-by: Hudson Ayers <hayers@stanford.edu>
2200: Add Teensy 4 board r=bradjc a=mciantyre ### Pull Request Overview The PR proposes Tock support for the Teensy 4. As of this PR, the Tock kernel, and a variety of small examples, can run on [Teensy 4.0][teensy40] and [Teensy 4.1][teensy41] development boards. The PR builds on the i.MX RT chip foundation added in #1918. [teensy40]: https://www.pjrc.com/store/teensy40.html [teensy41]: https://www.pjrc.com/store/teensy41.html The board makes use of - the LED on pin 13 - UART2 on pins 14 and 15 - GPT1 as an alarm The Teensy 4 boards use i.MX RT **1062** chips. Given our current chip features, the 1060 chip family is identical to the 1050 chip family. Before integrating the Teensy 4 board, I *renamed `chips/imxrt1050` to `chips/imxrt10xx`*. I updated the `imxrt1050-evkb` board to use the renamed crate. Additional changes to the i.MX RT chip crate include - adding a LPUART2 peripheral - renaming the `gpt1` module to `gpt`, and adding `GPT2` - supporting periodic clock selection, allowing a user to select the static crystal oscillator as the GPT clock source Changes to the chip crate should be backwards compatible for `imxrt1050-evkb` users. Let me know if we see an issue. ### Testing Strategy I tested the PR by running `blink` and `console` libtock-c examples on both a Teensy 4.0 and 4.1 board. I repackaged the examples [here](https://github.com/mciantyre/tock-teensy4-apps). The PR was **not** tested on an NXP i.MX RT 1050 evaluation board; I don't have that hardware. ### TODO or Help Wanted This pull request may be tested on a 1050 evaluation board. @valexandru, if you have an opportunity to test this work and review these changes, I'd appreciate it! This PR does not address any of the TODOs noted in #1918. In particular, the `imxrt10xx` chip still does not use the new peripheral instantiation approach (#2069). If this is still TODO and not urgent, I'm happy to support that cleanup in a separate PR. This Teensy 4 support was based on a different chip implementation. That chip implementation supported DMA, and a UART driver that used DMA. If we see value in a DMA driver for i.MX RT chips, I'm happy to contribute the driver. ### Documentation Updated - [x] ~~Updated the relevant files in `/docs`~~, or **no updates are required** I've added documentation in `boards/teensy4`. The documentation - lists the tools that you need to program a Teensy 4 - how to build the kernel and apps - how to convert the program to a HEX file, which is necessary to program a board ### Formatting - [x] Ran `make prepush`. ### New Platform Checklist - [x] Hardware is widely available. - [x] I can support the platform, which includes release testing for the platform, at least initially. - Basic features are implemented: - [x] `Console`, including `debug!()` and userspace `printf()`. - [x] Timers. - [x] GPIO with interrupts. I assume the basic chip features were provided by #1918. Let me know if we need to make all three features available through the board. As of this writing, the Teensy 4 board does not expose an input GPIO that responds to an interrupt. 2216: add 15.4 and ble to nano33ble r=bradjc a=hudson-ayers ### Pull Request Overview This pull request adds the 15.4 and BLE drivers to the nano33ble, rather than leaving support as commented out, as the comments had already fallen out-of-date. ### Testing Strategy BLE was tested using the `ble_advertising` and `ble_passive_scanning` apps in `libtock-c`. 15.4 was tested using the `radio_tx` and `radio_rx` apps in `libtock-c` and sending messages back and forth with an nrf52840-dk. ### TODO or Help Wanted N/A ### Documentation Updated - [x] No updates are required. ### Formatting - [x] Ran `make prepush`. Co-authored-by: Ian McIntyre <ianpmcintyre@gmail.com> Co-authored-by: Hudson Ayers <hayers@stanford.edu> Co-authored-by: Hudson Ayers <32688905+hudson-ayers@users.noreply.github.com>
2311: Board based instantiation of chip drivers and interrupt mappings: imxrt10xx r=bradjc a=mciantyre ### Pull Request Overview The PR refactors the `imxrt10xx` chip and boards to support board-based initialization (first proposed in #2069). It builds on the work of #1918 and #2200, where we noted the effort as a TODO. After this PR, you may instantiate and configure i.MX RT peripherals in a board, or use the default peripherals provided by the chip. Most peripherals were easily transitioned to the new API. The exception was the GPIO driver, which referenced `static` GPIO ports and pins throughout the code. I may have found some issues in the driver, so I took the refactoring opportunity to update the implementation, trying to follow [this suggestion](https://github.com/tock/tock/blob/master/chips/stm32f4xx/src/gpio.rs#L595). I'll leave more details in review comments. Other changes include - fix a GPT clock gating bug introduced in #2200 - move UART root clock initialization out of the `configure()` method, and into CCM peripheral setup ### Testing Strategy Tested `boards/teensy40` on a Teensy 4.0 using the tests from #2200. `boards/imxrt1050-evkb` continues to compile, but I don't have the hardware to test examples. ### TODO or Help Wanted Looking for feedback on the refactor. Let me know if it deviates too much from the other chips. Holding as draft to - [x] ~~wait for #2310, since the new GPIO driver needs `min_const_generics`~~ accepting as-is, and we'll remove the feature later - [x] figure out a way to remove the remaining `static` peripheral in `iomuxc_snvs` - [x] remove `std` dependency in new unit tests, avoiding the conditional `no_std` in the crate - [x] leave thoughts on GPIO changes - [x] clean up commit messages ### Documentation Updated - [x] ~~Updated the relevant files in `/docs`, or~~ no updates are required. ### Formatting - [x] Ran `make prepush`. Co-authored-by: Ian McIntyre <ianpmcintyre@gmail.com>
This commit removes the const_mut_refs unstable feature and all uses of it. For the most part, this just required making every const constructor that created a `TakeCell` no longer const, as a result of `TakeCell::empty()` no longer being const. Thanks to the updated peripheral instantation approach in tock#2069 and related PRs, this was very straightforward to perform, as peripherals no longer need to be created in const context.
3082: Remove `#![feature(const_mut_refs)]` r=bradjc a=hudson-ayers ### Pull Request Overview This PR removes the `const_mut_refs` unstable feature and all uses of it. For the most part, this just required making every const constructor that created a `TakeCell` no longer const, as a result of `TakeCell::empty()` no longer being const. Thanks to the updated peripheral instantation approach in #2069 and related PRs, this was very straightforward to perform, as peripherals no longer need to be created in const context. ### Testing Strategy This pull request was tested by compiling, no functional changes are included. ### TODO or Help Wanted N/A ### Documentation Updated - [x] I will update #1654 if/when this is merged. ### Formatting - [x] Ran `make prepush`. Co-authored-by: Hudson Ayers <hayers@stanford.edu>
Pull Request Overview
This pull request proposes a new design for managing chip drivers and interrupts.
The new design makes boards responsible for instantiating chip drivers, and for defining the mapping from interrupt numbers --> drivers.
It also adds the
InterruptService
trait tokernel/src/platform/mod.rs
, as it will no longer only be used by nordic boards.I would appreciate feedback before I implement this for all chips/boards, and answers to my outstanding questions below.
Advantages of the design:
static mut
variables in these scenarios (a step in the right direction forstatic mut
data considered harmful (as opposed tostatic_init
) #1545 / Missed optimization opportunities withstatic mut
data? #1662 ).chips/
to define the default interrupt mapping, so that upstream boards will not have to reimplement interrupt -> driver mappings in the common case.const_fn
throughout Tock, except for intock-register-interface
. Most uses ofconst_fn
in Tock today are only required because of the widespread practice of initializing drivers as globals. By transitioning away from this design, most constructors can be made non-const, a big step towards Building Tock with stable rustc #1654 .earlgray
crate, and maintain only a board crate out-of-tree, which is probably a win for both projects.So far, this PR is only implemented for the
apollo3
/redboard_artemis_nano
combination (I picked it because there aren't very many apollo3 drivers at this point, so the total work was smallest).I have verified that this PR excludes chip driver code in a way that is not currently possible on master, by removing the ble and i2c drivers from the board. Unfortunately removing const_fn everywhere seems to have introduced some code size cost.
Testing Strategy
This pull request was tested by compiling.
TODO or Help Wanted
Outstanding Questions
How to handle deferred calls? I propose that they should be handled identically to interrupts in this design, that is
handle_deferred_call()
should be added to theInterruptService
trait, and populated by a macro for the common case. -- Done.Is modifying theI decided that usingPlatform
trait ideal, or would it be better to use basically theInterruptService
trait suggested in [RFC] trait for servicing interrupts #1501, which could be implemented for the Platform, or for a struct containing just the drivers?InterruptService
is clearer and makes more sense than conflatingPlatform
with interrupt mapping. This technique allows for chips to still define some set of required interrupt mappings if it wishes, and should make adapting this to the nordic boards easier.Is it in fact desirable to remove all uses ofDecided to revert the const removals, for now.#[const_fn]
, or should I remove that portion of this PR and save it as a followup PR? It seems to me that const constructors would be preferable if the feature was stable, so maybe I am jumping the gun on this.Some drivers do not handle interrupts (
mcuctrl
,pwrctrl
,clkctrl
in theApollo3
case), but still do not need to be initialized as globals. I propose that when these drivers are only used inmain.rs
, they should simply be stack allocated inreset_handler()
. If used inchip.rs
, they should be made fields on the respective chip struct, and initialized with the Chip. This makes it clear which drivers are necessary for chip operation vs. optionally included by boards. In fact, this approach could be taken one step further, by making "required" drivers fields of the chip struct. Things like uart and timer which are prerequisites for including a board in Tock could be placed on the chip struct.Documentation Updated
/docs
, or no updates are required.Formatting
make prepush
.