Thanks for using Compiler Explorer
Sponsors
Jakt
C++
Ada
Algol68
Analysis
Android Java
Android Kotlin
Assembly
C
C3
Carbon
C with Coccinelle
C++ with Coccinelle
C++ (Circle)
CIRCT
Clean
CMake
CMakeScript
COBOL
C++ for OpenCL
MLIR
Cppx
Cppx-Blue
Cppx-Gold
Cpp2-cppfront
Crystal
C#
CUDA C++
D
Dart
Elixir
Erlang
Fortran
F#
GLSL
Go
Haskell
HLSL
Hook
Hylo
IL
ispc
Java
Julia
Kotlin
LLVM IR
LLVM MIR
Modula-2
Mojo
Nim
Numba
Nix
Objective-C
Objective-C++
OCaml
Odin
OpenCL C
Pascal
Pony
PTX
Python
Racket
Raku
Ruby
Rust
Sail
Snowball
Scala
Slang
Solidity
Spice
SPIR-V
Swift
LLVM TableGen
Toit
Triton
TypeScript Native
V
Vala
Visual Basic
Vyper
WASM
Zig
Javascript
GIMPLE
Ygen
sway
rust source #3
Output
Compile to binary object
Link to binary
Execute the code
Intel asm syntax
Demangle identifiers
Verbose demangling
Filters
Unused labels
Library functions
Directives
Comments
Horizontal whitespace
Debug intrinsics
Compiler
mrustc (master)
rustc 1.0.0
rustc 1.1.0
rustc 1.10.0
rustc 1.11.0
rustc 1.12.0
rustc 1.13.0
rustc 1.14.0
rustc 1.15.1
rustc 1.16.0
rustc 1.17.0
rustc 1.18.0
rustc 1.19.0
rustc 1.2.0
rustc 1.20.0
rustc 1.21.0
rustc 1.22.0
rustc 1.23.0
rustc 1.24.0
rustc 1.25.0
rustc 1.26.0
rustc 1.27.0
rustc 1.27.1
rustc 1.28.0
rustc 1.29.0
rustc 1.3.0
rustc 1.30.0
rustc 1.31.0
rustc 1.32.0
rustc 1.33.0
rustc 1.34.0
rustc 1.35.0
rustc 1.36.0
rustc 1.37.0
rustc 1.38.0
rustc 1.39.0
rustc 1.4.0
rustc 1.40.0
rustc 1.41.0
rustc 1.42.0
rustc 1.43.0
rustc 1.44.0
rustc 1.45.0
rustc 1.45.2
rustc 1.46.0
rustc 1.47.0
rustc 1.48.0
rustc 1.49.0
rustc 1.5.0
rustc 1.50.0
rustc 1.51.0
rustc 1.52.0
rustc 1.53.0
rustc 1.54.0
rustc 1.55.0
rustc 1.56.0
rustc 1.57.0
rustc 1.58.0
rustc 1.59.0
rustc 1.6.0
rustc 1.60.0
rustc 1.61.0
rustc 1.62.0
rustc 1.63.0
rustc 1.64.0
rustc 1.65.0
rustc 1.66.0
rustc 1.67.0
rustc 1.68.0
rustc 1.69.0
rustc 1.7.0
rustc 1.70.0
rustc 1.71.0
rustc 1.72.0
rustc 1.73.0
rustc 1.74.0
rustc 1.75.0
rustc 1.76.0
rustc 1.77.0
rustc 1.78.0
rustc 1.79.0
rustc 1.8.0
rustc 1.80.0
rustc 1.81.0
rustc 1.82.0
rustc 1.83.0
rustc 1.84.0
rustc 1.85.0
rustc 1.86.0
rustc 1.87.0
rustc 1.88.0
rustc 1.89.0
rustc 1.9.0
rustc beta
rustc nightly
rustc-cg-gcc (master)
x86-64 GCCRS (GCC master)
x86-64 GCCRS (GCCRS master)
x86-64 GCCRS 14.1 (GCC assertions)
x86-64 GCCRS 14.1 (GCC)
x86-64 GCCRS 14.2 (GCC assertions)
x86-64 GCCRS 14.2 (GCC)
x86-64 GCCRS 14.3 (GCC assertions)
x86-64 GCCRS 14.3 (GCC)
x86-64 GCCRS 15.1 (GCC assertions)
x86-64 GCCRS 15.1 (GCC)
x86-64 GCCRS 15.2 (GCC assertions)
x86-64 GCCRS 15.2 (GCC)
Options
Source code
#![allow(unused)] reg!{ REG1: u32 { addr: 0xf00, write mask: 0xFFFFFFFF, bits: { R11 = 0, RW; R12 = 2, RW; R13 = 3, R; R14 = 4, RW; } } } const REG2: *mut u32 = 0xf00 as *mut u32; const R21: u32 = 0; const R22: u32 = 2; const R23: u32 = 3; const R24: u32 = 4; use hal::register::Register; pub fn fancy() { unsafe { let val = REG1::get_value(); REG1::set_bits(REG1::R11 | REG1::R14); } } pub fn primitive() { unsafe { let val = REG2.read_volatile(); // This second read is what's done in set_bits above. let val = REG2.read_volatile(); REG2.write_volatile( val | 1 << R21 | 1 << R24 ); } } mod hal { pub mod register { use std::{ marker::PhantomData, ops::{BitAnd, BitOr, BitOrAssign, Not, Shl}, }; /// Used to restrict what data types a register can be. /// /// The registers on the 328P are only 8- or 16-bit, so it will only be implement /// for `u8` and `u16`. pub trait RegisterType: Copy + BitAnd<Output = Self> + BitOr<Output = Self> + Shl<Output = Self> + Not<Output = Self> + Eq + PartialEq { const ZERO: Self; const ONE: Self; } impl RegisterType for u8 { const ZERO: Self = 0; const ONE: Self = 1; } impl RegisterType for u16 { const ZERO: Self = 0; const ONE: Self = 1; } impl RegisterType for u32 { const ZERO: Self = 0; const ONE: Self = 1; } /// This trait and types represent whether a bit is readable and/or writable in the type system. pub trait Access {} pub struct Readable; impl Access for Readable {} pub struct NotReadable; impl Access for NotReadable {} pub struct Writable; impl Access for Writable {} pub struct NotWritable; impl Access for NotWritable {} /// This allows us to say that access is only allowed if both bits have that access. /// It is, essentially, a boolean AND operation, hence the name. pub trait AccessAnd<Rhs> { type Output: Access; } impl AccessAnd<Readable> for Readable { type Output = Readable; } impl AccessAnd<NotReadable> for Readable { type Output = NotReadable; } impl AccessAnd<NotReadable> for NotReadable { type Output = NotReadable; } impl AccessAnd<Readable> for NotReadable { type Output = NotReadable; } impl AccessAnd<Writable> for Writable { type Output = Writable; } impl AccessAnd<NotWritable> for Writable { type Output = NotWritable; } impl AccessAnd<NotWritable> for NotWritable { type Output = NotWritable; } impl AccessAnd<Writable> for NotWritable { type Output = NotWritable; } /// Represents a bit in a register. /// /// A bit needs to be associated with its parent register, and what read/write access the bit has, so incorrect usage /// is prevented at compile-time. pub trait Bit { type ReadAccess: Access; type WriteAccess: Access; type Register: Register; fn bit_id(&self) -> <Self::Register as Register>::DataType; } /// The BitBuilder allows the user to write expressions like `Bit1 | Bit2` to build up a bit pattern, while /// still retaining the connection to the register and the restrictions on read/write access. pub struct BitBuilder<Reg: Register, R: Access, W: Access> { data: Reg::DataType, _p: PhantomData<(Reg, R, W)>, } impl<Reg: Register> BitBuilder<Reg, Readable, Writable> { /// The default should be to have full read/write access. Any OR operations will restrict it as needed. pub fn new() -> Self { Self { data: Reg::DataType::ZERO, _p: PhantomData, } } } impl<Reg: Register, R: Access, W: Access> BitBuilder<Reg, R, W> { pub fn raw_value(&self) -> Reg::DataType { self.data } } /// This one's pretty gnarly. Basically, this lets the user do `bits | bits`, while continuing the connection /// with the register. The resulting BitBuilder will have the most restrictive read/write access. /// E.g. A BitBuilder with read/write access being ORed with one with only read access will result in a BitBuilder /// with only read access. impl<Reg, R1, W1, R2, W2> BitOr<BitBuilder<Reg, R2, W2>> for BitBuilder<Reg, R1, W1> where R1: Access, R2: Access, W1: Access, W2: Access, Reg: Register, R2: AccessAnd<R1>, W2: AccessAnd<W1>, { type Output = BitBuilder<Reg, <R2 as AccessAnd<R1>>::Output, <W2 as AccessAnd<W1>>::Output>; fn bitor(self, rhs: BitBuilder<Reg, R2, W2>) -> Self::Output { BitBuilder { data: self.data | rhs.data, _p: PhantomData, } } } /// This just lets us OR a BitBuilder with a Bit from the same register. As with above, the most restrictive access /// applies. impl<Reg, R1, W1, B> BitOr<B> for BitBuilder<Reg, R1, W1> where R1: Access, W1: Access, Reg: Register, B: Bit<Register = Reg>, B::ReadAccess: AccessAnd<R1>, B::WriteAccess: AccessAnd<W1>, { type Output = BitBuilder< Reg, <B::ReadAccess as AccessAnd<R1>>::Output, <B::WriteAccess as AccessAnd<W1>>::Output, >; // Shut up, clippy, I know it's complex! fn bitor(self, rhs: B) -> Self::Output { BitBuilder { data: self.data | (Reg::DataType::ONE << rhs.bit_id()), _p: PhantomData, } } } /// This lets us do things like: /// /// ``` /// let mut bits = TWEN | TWIE | TWINT; /// if ack { /// bits |= TWEA; /// } /// TWCR::set_value(bits); /// ``` /// /// The restriction is that both read and write access of the new bit must exactly match the access of the BitBuilder /// as it can't alter the return type. impl<Reg, R1, W1, B> BitOrAssign<B> for BitBuilder<Reg, R1, W1> where R1: Access, W1: Access, Reg: Register, B: Bit<Register = Reg, ReadAccess = R1, WriteAccess = W1>, { fn bitor_assign(&mut self, rhs: B) { self.data = self.data | (Reg::DataType::ONE << rhs.bit_id()); } } /// This trait is to fix an irritating papercut. /// /// If we don't have it and have the register functions take a BitBuilder, it would mean that we could do this: /// /// ``` /// TWCR::set_value(TWEN | TWIE); /// ``` /// /// But not this: /// /// ``` /// TWCR::set_value(TWEN); /// ``` /// /// Which feels incredibly inconsistent. pub trait SetValueType { type Register: Register; type WriteAccess: Access; fn value(&self) -> <Self::Register as Register>::DataType; } impl<Reg, R, W> SetValueType for BitBuilder<Reg, R, W> where Reg: Register, R: Access, W: Access, { type Register = Reg; type WriteAccess = W; fn value(&self) -> Reg::DataType { self.data } } /// Abstracts away the messy volatile pointer accesses and bit-twiddling needed for the MMIO /// register. /// /// The type needs to be tracked, as does a write mask, as some bits should never be written to. /// /// One thing that does need to be provided is the ability to set a raw value, as some registers /// are data registers, not control/status registers. Bit-twiddling these makes no sense. pub trait Register: Sized { type DataType: RegisterType; const ADDR: *mut Self::DataType; // Some bits should always be written 0 when writing, this allows that. const WRITE_MASK: Self::DataType; unsafe fn set_raw_value(val: Self::DataType) { Self::ADDR.write_volatile(val & Self::WRITE_MASK); } unsafe fn set_value<V>(val: V) where V: SetValueType<Register = Self, WriteAccess = Writable>, { let value = val.value(); Self::set_raw_value(value); } unsafe fn get_value() -> Self::DataType { Self::ADDR.read_volatile() } unsafe fn get_bit<B>(bit: B) -> bool where B: Bit<Register = Self, ReadAccess = Readable>, { let bit = Self::DataType::ONE << bit.bit_id(); (Self::get_value() & bit) != Self::DataType::ZERO } unsafe fn set_bits<V>(bits: V) where V: SetValueType<Register = Self, WriteAccess = Writable>, { let val = Self::get_value(); Self::set_raw_value(val | bits.value()); } unsafe fn clear_bits<V>(bits: V) where V: SetValueType<Register = Self, WriteAccess = Writable>, { let val = Self::get_value(); Self::set_raw_value(val & !bits.value()); } unsafe fn replace_bits( mask: BitBuilder<Self, Readable, Writable>, new_val: BitBuilder<Self, Readable, Writable>, ) { let reg_val = Self::get_value() & !mask.data; let masked_val = new_val.data & mask.data; Self::set_raw_value(reg_val | masked_val); } } // Unfortunately, all the traits above make actually defining a register and its bits something that no sane // person should do. Fortunately, because the definitions are very regular, macros can be used. /// Used in the `reg_named_bits` macro for expanding the read access declarations. It allows the other macros /// to just match the access flags as a token tree, and have this expand it. #[macro_export] macro_rules! expand_read_access { (R) => { type ReadAccess = crate::hal::register::Readable; }; (W) => { type ReadAccess = crate::hal::register::NotReadable; }; (RW) => { type ReadAccess = crate::hal::register::Readable; }; } /// Used in the `reg_named_bits` macro for expanding the write access declarations. It allows the other macros /// to just match the access flags as a token tree, and have this expand it. #[macro_export] macro_rules! expand_write_access { (R) => { type WriteAccess = crate::hal::register::NotWritable; }; (W) => { type WriteAccess = crate::hal::register::Writable; }; (RW) => { type WriteAccess = crate::hal::register::Writable; }; } /// Because each bit is represented as a unique type, actually declaring them by hand is a tedious nightmare. This /// macro allows the user to declare a whole register's bits using an enum-like representation. #[macro_export] macro_rules! reg_named_bits { ( $reg:ident : $type:ty { $( $(#[$bit_doc:meta])* $bit:ident = $id:expr, $acc:tt;)+ } ) => { $( $(#[$bit_doc])* #[derive(Copy, Clone)] pub struct $bit; impl crate::hal::register::Bit for $bit { type Register = $reg; expand_read_access!{$acc} expand_write_access!{$acc} fn bit_id(&self) -> $type { $id } } impl crate::hal::register::SetValueType for $bit { type Register = $reg; expand_write_access!{$acc} fn value(&self) -> $type { use crate::hal::register::Bit; 1 << self.bit_id() } } // Oh god... what have I written here?! impl<B2> core::ops::BitOr<B2> for $bit where Self: crate::hal::register::Bit, B2: crate::hal::register::Bit<Register = <Self as crate::hal::register::Bit>::Register>, B2::ReadAccess: crate::hal::register::AccessAnd<<Self as crate::hal::register::Bit>::ReadAccess>, B2::WriteAccess: crate::hal::register::AccessAnd<<Self as crate::hal::register::Bit>::WriteAccess>, { type Output = crate::hal::register::BitBuilder< <Self as crate::hal::register::Bit>::Register, <B2::ReadAccess as crate::hal::register::AccessAnd<<Self as crate::hal::register::Bit>::ReadAccess>>::Output, <B2::WriteAccess as crate::hal::register::AccessAnd<<Self as crate::hal::register::Bit>::WriteAccess>>::Output, >; fn bitor(self, rhs: B2) -> Self::Output { crate::hal::register::BitBuilder::new() | self | rhs } } )* }; } /// There are a lot of bits, with rather opaque names. This macro defines constants on the parent register that /// allow the user to find the bit through the register. #[macro_export] macro_rules! reg_bit_consts { ( $struct_name:ident { $( $(#[$bit_doc:meta])* $bit:ident ),+ $(,)* } ) => { impl $struct_name { $( $(#[$bit_doc])* #[allow(dead_code)] pub const $bit: $bit = $bit; )* } } } /// Declaring a register and its bits is a verbose, tedious process. This macro provides a way to declare them /// using a structure similar to declaring a struct in Rust. #[macro_export] macro_rules! reg { ( $(#[$reg_doc:meta])* $name:ident : $type:ty { addr: $addr:expr, write mask: $mask:expr $(,)* } ) => { $(#[$reg_doc])* #[allow(dead_code)] pub struct $name; impl crate::hal::register::Register for $name { type DataType = $type; const WRITE_MASK: $type = $mask; const ADDR: *mut $type = $addr as *mut $type; } }; ( $(#[$reg_doc:meta])* $name:ident: $type:ty { addr: $addr:expr, write mask: $mask:expr, bits: { $( $(#[$bit_doc:meta])* $bit:ident = $id:expr, $acc:tt;)+ } } ) => { reg_named_bits! { $name: $type { $( $(#[$bit_doc])* $bit = $id, $acc; )+ } } reg! { $(#[$reg_doc])* $name: $type { addr: $addr, write mask: $mask, } } reg_bit_consts! { $name { $( $(#[$bit_doc])* $bit ),+ } } }; } } }
Become a Patron
Sponsor on GitHub
Donate via PayPal
Compiler Explorer Shop
Source on GitHub
Mailing list
Installed libraries
Wiki
Report an issue
How it works
Contact the author
CE on Mastodon
CE on Bluesky
Statistics
Changelog
Version tree