Based on this benchmark of elapsed_ms():
```rust
pub fn elapsed_ms(&self) -> u64 {
let response = send_message(
self.conn,
xous::Message::new_blocking_scalar(api::Opcode::ElapsedMs.to_usize().unwrap(), 0, 0, 0, 0),
)
.expect("Ticktimer: failure to send message to Ticktimer");
if let xous::Result::Scalar2(upper, lower) = response {
upper as u64 | ((lower as u64) << 32)
} else {
panic!("Ticktimer elapsed_ms(): unexpected return value.");
}
}
```
We find the following context switches:
1. send_message -> kernel/src/syscall.rs @ 166 - ss.remember_server_message()
- *context switch round trip* to enqueue the message into the target thread's space, then switch back.
- takes 546ns just in the target context, out of 4430ns round-trip
(measured as leave userspace of caller to enter userspace of callee) (12% of total time)
- emplaces the message in the target queue
- generates "sender_idx" which is the index in the message queue to find the message, which is put into Envelope
2. send_message -> kernel/src/syscall.rs @ 222 - ss.activate_process_thread()
- activates the thread to run
- Total penalty is closer to 1us - 22% of total time - once pre/amble/post-amble overhead considered
This set of patches removes the context-switch round trip to emplace messages in the target's thread
space, as well as to set thread results, by passing them as arguments to `activate_process_thread`.
What then happens is that these items are emplaced after the activation privot to the target
process space occurs, saving a set of TLB-flushes and page table walks.
More testing is required, but at least on a reasonable set of cases the system seems to be stable
with these patcehs, and we get a 24% speedup on blocking scalar messages and 8% speedup on
mutable lends in the case that the target is waiting to receive messages.
The double-flush in activate() has a big impact on performance,
and I'm pretty sure it only needs to happen after the SATP update
not before, at least architecturally. Eliminating the extra
flush improves the fast message passing benchmark time by 9%.
The "main" product line is affected here in that the UART
init is hosted above the trng init. This should have no impact
on security or boot time, but allows us to more easily debug
TRNG init issues. This is necessary because in the vexii test
version the XADC interface is broken by the slower clocking
and I'm not taking the time to trace down that issue as it
does nothing to further the goal of testing the vex-ii core
in a non-production config.
legacy_int handler allows vex-ii to emulate/behave like the vex-i
in terms of interrupt handling.
eventually this will be replaced with/augmeted by a PLIC.
1. Instead of panicing when a page is re-requested, return an
error message to the caller. This allows us to have the UART
pre-allocated by the loader and the kernel to request it prophylactically
2. add A/D flags for R/W pages in vexii-testing. This is a work-around
until we have proper A/D tracking in the page lifecycle mechanism.
this is a much simpler, race condition-free version of flash
writing which has the penalty of requiring a copy of the page
to be made by the caller before the write. However the
copy is mostly already paid for because on the initial access
to the page, we had to copy the flash into RAM.
this is a fairly major re-org of where key constants go that
define platform features. things that were in xous-rs that were
platform specific (namely precursor) were moved into a boards file,
and things that are generic to the system (like PAGE_SIZE) are moved
to a public location in xous-rs::arch.
this is probably the most significant source of consternation but
also this code path has been fairly well-trodden.
That being said it might be worth considering if there is any
way to use some of the new Rust const based reasoning logic
to see if we can't tighten this up someday, but today is not
that day.
So in Rust 1.83, we now get a strongly worded warning telling
us to really, really don't use static muts. They aren't wrong,
except that in some places like the kernel we have few options:
we are conjuring pointers out of thin air, creating memory mappings,
and then doing things with them that are hard for the compiler
to reason about.
I think the newly minted warning was made because the intention
is that we could use the new fancy `const` semantics to take
the place of static mut usage by wrapping things in UnsafeCell,
and I think maaaaybe in some
cases this could apply here, but as a simple example a dive
into the kernel debug UART indicates that to initialize the
shared variable we have to allocate pages in the page table,
which involves calling the memory manager and doing some
decidedly non-const type of things. So one option is we could
just push the debug UART initialization into the loader and
paper over the whole thing by pretending it's static.
Instead of that, we adopt a pattern of casting all our static
muts to a raw pointer, and then back into what we want. The
cast to raw pointer is like an "uber unsafe" marker that says
"no we really mean that this pointer is where it is and we
conjured it out of thin air and we promise that the behavior
is defined and values are representable, etc.".
In general we're fairly judicious about the use of static
mut in the kernel, and when in the kernel there's no thread
pre-emption (although we could take an interrupt or page
fault) so generally we are single-threaded. The trickiest
spots are when we have to take a page fault while handling
a page fault (for example, allocating a new L1 page table
entry would require something like this) but this code path
is fairly carefully vetted.
So instead of just doing a global lint silence, we use an
`&raw` idiom to call out the fact that we're doing something
naughty wherever we do it, which makes the compiler "happy"
(for now) while still making the code ugly enough that it
looks sufficiently scary and you have to apply some
cognitive burden to even reason about what's going on,
which I *think* is the intention of Rust in this case.
Basically you get a get out of jail free card but you can't
simply wear it on a lanyard and pass through the gate -- every
time we need to use it, we have to do a little dance and
wash ourselves in the waters of unsafety and recite the
mantras about defined behavior, representable numbers, thread
safety, etc.
not doing a reformat of the entire project to match
nightly formatting changes, but capturing the published
crates so we can maybe save a version bump later on to get
the project clean.