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 #2
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
use register::*; pub unsafe fn configure_timers() { TCCR0A::set_value(COM0A0 | WGM01 | WGM00); TCCR0B::set_value(CS01 | CS00); TCCR1A::set_value(COM1A0 | WGM10); TCCR1B::set_value(WGM11 | CS12 | CS10); // Now that they're configured, I'm going to force a compare match on channel A. TCCR0B::set_bits(FOC0A); TCCR1C::set_bits(FOC1A); } reg! { TCCR0A: u8 { addr: 0x44, write mask: 0b11110011, bits: { WGM00 = 0, RW; WGM01 = 1, RW; COM0B0 = 4, RW; COM0B1 = 5, RW; COM0A0 = 6, RW; COM0A1 = 7, RW; } } } reg! { TCCR0B: u8 { addr: 0x45, write mask: 0b11001111, bits: { CS00 = 0, RW; CS01 = 1, RW; CS02 = 2, RW; WGM02 = 3, RW; FOC0B = 6, W; FOC0A = 7, W; } } } reg! { TCCR1A: u8 { addr: 0x80, write mask: 0b11110011, bits: { WGM10 = 0, RW; WGM11 = 1, RW; COM1B0 = 4, RW; COM1B1 = 5, RW; COM1A0 = 6, RW; COM1A1 = 7, RW; } } } reg! { TCCR1B: u8 { addr: 0x81, write mask: 0b11011111, bits: { CS10 = 0, RW; CS11 = 1, RW; CS12 = 2, RW; WGM12 = 3, RW; WGM13 = 4, RW; ICES1 = 6, RW; ICNC1 = 7, RW; } } } reg! { TCCR1C: u8 { addr: 0x82, write mask: 0b11000000, bits: { FOC1B = 6, W; FOC1A = 7, W; } } } mod register { // Here be the dragons that power all of this. use std::{ // Would be core in reality, but example code... ops::{BitAnd, BitOr, BitOrAssign, Shl, Not}, marker::PhantomData, }; // Used to restrict what data types a register can be. 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; } // 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; } // 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<DataType: RegisterType, Reg: Register<DataType = DataType>, R: Access, W: Access>{ data: DataType, _p: PhantomData<(Reg, R, W)> } impl<DataType: RegisterType, Reg: Register<DataType = DataType>> BitBuilder<DataType, Reg, Readable, Writable> { pub fn new() -> Self { Self { data: DataType::ZERO, _p: PhantomData, } } } impl <DataType: RegisterType, Reg: Register<DataType = DataType>, R: Access, W: Access> BitBuilder<DataType, Reg, R, W> { pub fn raw_value(&self) -> 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<DataType, Reg, R1, W1, R2, W2> BitOr<BitBuilder<DataType, Reg, R2, W2>> for BitBuilder<DataType, Reg, R1, W1> where R1: Access, R2: Access, W1: Access, W2: Access, DataType: RegisterType, Reg: Register<DataType = DataType>, R2: AccessAnd<R1>, W2: AccessAnd<W1>, { type Output = BitBuilder<DataType, Reg, <R2 as AccessAnd<R1>>::Output, <W2 as AccessAnd<W1>>::Output>; fn bitor(self, rhs: BitBuilder<DataType, 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<DataType, Reg, R1, W1, B> BitOr<B> for BitBuilder<DataType, Reg, R1, W1> where R1: Access, W1: Access, DataType: RegisterType, Reg: Register<DataType = DataType>, B: Bit<Register=Reg>, B::ReadAccess: AccessAnd<R1>, B::WriteAccess: AccessAnd<W1>, { type Output = BitBuilder<DataType, Reg, <B::ReadAccess as AccessAnd<R1>>::Output, <B::WriteAccess as AccessAnd<W1>>::Output>; fn bitor(self, rhs: B) -> Self::Output { BitBuilder { data: self.data | (DataType::ONE << rhs.bit_id()), _p: PhantomData, } } } // This one's a little restrictive, but lets us do things like: // // let mut bits = TWEN | TWIE | TWINT; // if ack { // bits |= TWEA; // } // TWCR::set_value(bits); // // One restriction is that both read and write access of the new bit must exactly match the access of the BitBuilder. impl<DataType, Reg, R1, W1, B> BitOrAssign<B> for BitBuilder<DataType, Reg, R1, W1> where R1: Access, W1: Access, DataType: RegisterType, Reg: Register<DataType = DataType>, B: Bit<Register=Reg, ReadAccess=R1, WriteAccess=W1>, { fn bitor_assign(&mut self, rhs: B) { self.data = self.data | (DataType::ONE << rhs.bit_id()); } } // This trait is to fix an irritating papercut. If we don't have it and have set_value 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 DataType: RegisterType; type Register: Register<DataType = Self::DataType>; type WriteAccess: Access; fn value(&self) -> Self::DataType; } impl<DataType, Reg, R, W> SetValueType for BitBuilder<DataType, Reg, R, W> where DataType: RegisterType, Reg: Register<DataType = DataType>, R: Access, W: Access, { type DataType = DataType; type Register = Reg; type WriteAccess = W; fn value(&self) -> DataType { self.data } } // Abstracts away the messy volatile pointer accessses and bit-twiddling needed for an MMIO. // 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<DataType=Self::DataType, 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<DataType=Self::DataType, 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<DataType=Self::DataType, Register=Self, WriteAccess=Writable>, { let val = Self::get_value(); Self::set_raw_value(val & !bits.value()); } unsafe fn replace_bits( mask: BitBuilder<Self::DataType, Self, Readable, Writable>, new_val: BitBuilder<Self::DataType, 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 and above make actually defining a register and its bits something that no sane person // should do. Fortunately, because it's the same for all registers/bits, macros can be used. // These two are just for expanding the read/write access type defs. It allows the other macros to just match // the access flags as a token tree, and have these two expand it. #[macro_export] macro_rules! expand_read_access { (R) => { type ReadAccess = crate::register::Readable; }; (W) => { type ReadAccess = crate::register::NotReadable; }; (RW) => { type ReadAccess = crate::register::Readable; }; } #[macro_export] macro_rules! expand_write_access { (R) => { type WriteAccess = crate::register::NotWritable; }; (W) => { type WriteAccess = crate::register::Writable; }; (RW) => { type WriteAccess = crate::register::Writable; }; } // This one is what creates all the types representing the bits. #[macro_export] macro_rules! reg_named_bits { ( $name:ident : $type:ty { $( $(#[$bit_doc:meta])* $bit:ident = $id:expr, $acc:tt;)+ } ) => { $( $(#[$bit_doc])* #[derive(Copy, Clone)] pub struct $bit; impl crate::register::Bit for $bit { type Register = $name; expand_read_access!{$acc} expand_write_access!{$acc} fn bit_id(&self) -> $type { $id } } impl crate::register::SetValueType for $bit { type DataType = $type; type Register = $name; expand_write_access!{$acc} fn value(&self) -> $type { use crate::register::Bit; 1 << self.bit_id() } } impl<B2> core::ops::BitOr<B2> for $bit where Self: crate::register::Bit, B2: crate::register::Bit<Register = <Self as crate::register::Bit>::Register>, B2::ReadAccess: crate::register::AccessAnd<<Self as crate::register::Bit>::ReadAccess>, B2::WriteAccess: crate::register::AccessAnd<<Self as crate::register::Bit>::WriteAccess>, { type Output = crate::register::BitBuilder< <<Self as crate::register::Bit>::Register as crate::register::Register>::DataType, <Self as crate::register::Bit>::Register, <B2::ReadAccess as crate::register::AccessAnd<<Self as crate::register::Bit>::ReadAccess>>::Output, <B2::WriteAccess as crate::register::AccessAnd<<Self as crate::register::Bit>::WriteAccess>>::Output, >; fn bitor(self, rhs: B2) -> Self::Output { crate::register::BitBuilder::new() | self | rhs } } )* }; } // There are a lot of bits, and it would be useful to namespace them. This macro allows the user to access the bits // like this: TWCR::TWEN; #[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; )* } } } #[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::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 ),+ } } }; } }
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