Skip to main content

NeedsBus

Struct NeedsBus 

Source
pub struct NeedsBus(/* private fields */);
Expand description

Proof that a step needs the bus, produced only by Rdp::tick_without_bus and consumed only by Rdp::tick_with_bus.

The two halves exist for a caller that cannot hand out a borrow of this struct without first moving the struct itself — it needs to know whether the step will use the bus before paying the 344-byte core::mem::take that arranging one costs. (In this workspace that caller is the Bus, which owns every chip.) The bus half then has real preconditions: the pipeline is unfrozen, stall is zero, and the command FIFO is non-empty.

Those preconditions are carried by this token rather than by a comment, an assert!, or a debug_assert! alone. A comment is not checked; a debug_assert compiles out, so a release build would decode a command from an empty FIFO in silence; and a release guard that returns early would make a caller who never calls tick_without_bus hang, because stall would never count down. Requiring the token removes all three: calling the bus half out of order does not compile.

The field is private and the type has no constructor, so it cannot be forged.

Dropping a token loses a cycle, not the work. The Some path mutates nothing — it is reached only once stall is zero, so no decrement has happened, and no FIFO pointer moves until the bus half runs. The command is therefore still pending and the next rdp_tick retries the identical step. What is lost is the step: the RDP made no progress during that GCLK and is one cycle late from then on. That is the failure mode worth naming here, because it is a timing divergence with no wrong state anywhere — correct-but-late, which no state comparison can see. Hence #[must_use], below, rather than a debug_assert on the token count.

What makes ignoring one loud is the #[must_use] on Rdp::tick_without_bus itself, not the one on this type: an attribute on T does not propagate through Option<T>, and Option — unlike Result — is not #[must_use] either. Verified by discarding the call and watching the lint appear only once the attribute moved to the function.

Copy and Clone are deliberately not derived. Either would let a caller keep a token past the step it authorized and present it again after the state it attested to had changed — the same hole as taking it by reference. Debug is derived because it cannot duplicate the value.

Trait Implementations§

Source§

impl Debug for NeedsBus

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.