diff --git a/machine/src/lib.rs b/machine/src/lib.rs
index 4d30c40..5743117 100644
--- a/machine/src/lib.rs
+++ b/machine/src/lib.rs
@@ -3,6 +3,7 @@
 #![feature(decl_macro)]
 #![feature(allocator_api)]
 #![feature(format_args_nl)]
+#![feature(const_fn_fn_ptr_basics)]
 #![feature(nonnull_slice_from_raw_parts)]
 #![feature(custom_test_frameworks)]
 #![test_runner(crate::tests::test_runner)]
diff --git a/machine/src/platform/mod.rs b/machine/src/platform/mod.rs
index 5926078..dfd3731 100644
--- a/machine/src/platform/mod.rs
+++ b/machine/src/platform/mod.rs
@@ -2,4 +2,51 @@
  * SPDX-License-Identifier: BlueOak-1.0.0
  * Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
  */
+use core::{marker::PhantomData, ops};
+
+//--------------------------------------------------------------------------------------------------
+// Public Definitions
+//--------------------------------------------------------------------------------------------------
+
 pub mod rpi3;
+
+pub struct MMIODerefWrapper<T> {
+    base_addr: usize,
+    phantom: PhantomData<fn() -> T>,
+}
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+impl<T> MMIODerefWrapper<T> {
+    /// Create an instance.
+    ///
+    /// # Safety
+    ///
+    /// Unsafe, duh!
+    pub const unsafe fn new(start_addr: usize) -> Self {
+        Self {
+            base_addr: start_addr,
+            phantom: PhantomData,
+        }
+    }
+}
+
+/// Deref to RegisterBlock
+///
+/// Allows writing
+/// ```
+/// self.GPPUD.read()
+/// ```
+/// instead of something along the lines of
+/// ```
+/// unsafe { (*GPIO::ptr()).GPPUD.read() }
+/// ```
+impl<T> ops::Deref for MMIODerefWrapper<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        unsafe { &*(self.base_addr as *const _) }
+    }
+}
diff --git a/machine/src/platform/rpi3/gpio.rs b/machine/src/platform/rpi3/gpio.rs
index 3fc0625..656a14e 100644
--- a/machine/src/platform/rpi3/gpio.rs
+++ b/machine/src/platform/rpi3/gpio.rs
@@ -7,12 +7,12 @@
 
 use {
     super::BcmHost,
-    crate::arch::loop_delay,
-    core::{marker::PhantomData, ops},
+    crate::{arch::loop_delay, platform::MMIODerefWrapper},
+    core::marker::PhantomData,
     tock_registers::{
         fields::FieldValue,
         interfaces::{ReadWriteable, Readable, Writeable},
-        register_bitfields,
+        register_bitfields, register_structs,
         registers::{ReadOnly, ReadWrite, WriteOnly},
     },
 };
@@ -48,102 +48,107 @@ states! {
     Uninitialized, Input, Output, Alt
 }
 
-/// A wrapper type that prevents reads or writes to its value.
-///
-/// This type implements no methods. It is meant to make the inner type
-/// inaccessible to prevent accidental reads or writes.
-#[repr(C)]
-pub struct Reserved<T>(T);
-
-/// The offsets for reach register.
-/// From <https://wiki.osdev.org/Raspberry_Pi_Bare_Bones> and
-/// <https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf>
-#[allow(non_snake_case)]
-#[repr(C)]
-pub struct RegisterBlock {
-    pub FSEL: [ReadWrite<u32>; 6], // 0x00-0x14 function select
-    __reserved_0: Reserved<u32>,   // 0x18
-    pub SET: [WriteOnly<u32>; 2],  // 0x1c-0x20 set output pin
-    __reserved_1: Reserved<u32>,   // 0x24
-    pub CLR: [WriteOnly<u32>; 2],  // 0x28-0x2c clear output pin
-    __reserved_2: Reserved<u32>,   // 0x30
-    pub LEV: [ReadOnly<u32>; 2],   // 0x34-0x38 get input pin level
-    __reserved_3: Reserved<u32>,   // 0x3C
-    pub EDS: [ReadWrite<u32>; 2],  // 0x40-0x44
-    __reserved_4: Reserved<u32>,   // 0x48
-    pub REN: [ReadWrite<u32>; 2],  // 0x4c-0x50
-    __reserved_5: Reserved<u32>,   // 0x54
-    pub FEN: [ReadWrite<u32>; 2],  // 0x58-0x5c
-    __reserved_6: Reserved<u32>,   // 0x60
-    pub HEN: [ReadWrite<u32>; 2],  // 0x64-0x68
-    __reserved_7: Reserved<u32>,   // 0x6c
-    pub LEN: [ReadWrite<u32>; 2],  // 0x70-0x74
-    __reserved_8: Reserved<u32>,   // 0x78
-    pub AREN: [ReadWrite<u32>; 2], // 0x7c-0x80
-    __reserved_9: Reserved<u32>,   // 0x84
-    pub AFEN: [ReadWrite<u32>; 2], // 0x88-0x8c
-    __reserved_10: Reserved<u32>,  // 0x90
-    pub PUD: ReadWrite<u32>,       // 0x94      pull up down
-    pub PUDCLK: [ReadWrite<u32, PUDCLK0::Register>; 2], // 0x98-0x9C -- @todo remove this register
+register_structs! {
+    /// The offsets for each register.
+    /// From <https://wiki.osdev.org/Raspberry_Pi_Bare_Bones> and
+    /// <https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf>
+    #[allow(non_snake_case)]
+    RegisterBlock {
+        (0x00 => pub FSEL: [ReadWrite<u32>; 6]), // function select
+        (0x18 => __reserved_1),
+        (0x1c => pub SET: [WriteOnly<u32>; 2]), // set output pin
+        (0x24 => __reserved_2),
+        (0x28 => pub CLR: [WriteOnly<u32>; 2]), // clear output pin
+        (0x30 => __reserved_3),
+        (0x34 => pub LEV: [ReadOnly<u32>; 2]), // get input pin level
+        (0x3c => __reserved_4),
+        (0x40 => pub EDS: [ReadWrite<u32>; 2]),
+        (0x48 => __reserved_5),
+        (0x4c => pub REN: [ReadWrite<u32>; 2]),
+        (0x54 => __reserved_6),
+        (0x58 => pub FEN: [ReadWrite<u32>; 2]),
+        (0x60 => __reserved_7),
+        (0x64 => pub HEN: [ReadWrite<u32>; 2]),
+        (0x6c => __reserved_8),
+        (0x70 => pub LEN: [ReadWrite<u32>; 2]),
+        (0x78 => __reserved_9),
+        (0x7c => pub AREN: [ReadWrite<u32>; 2]),
+        (0x84 => __reserved_10),
+        (0x88 => pub AFEN: [ReadWrite<u32>; 2]),
+        (0x90 => __reserved_11),
+        (0x94 => pub PUD: ReadWrite<u32>), // pull up down
+        (0x98 => pub PUDCLK: [ReadWrite<u32, PUDCLK0::Register>; 2]), // 0x98-0x9C -- TODO: remove this register?
+        // (0xE4 => GPIO_PUP_PDN_CNTRL_REG0: ReadWrite<u32, GPIO_PUP_PDN_CNTRL_REG0::Register>), -- ??
+        (0xa0 => __reserved_12),
+        (0xE8 => @END),
+    }
 }
 
+// Hide RegisterBlock from public api.
+type Registers = MMIODerefWrapper<RegisterBlock>;
+
 /// Public interface to the GPIO MMIO area
 pub struct GPIO {
-    base_addr: usize,
+    registers: Registers,
 }
 
-/// Deref to RegisterBlock
-///
-/// Allows writing
-/// ```
-/// self.GPPUD.read()
-/// ```
-/// instead of something along the lines of
-/// ```
-/// unsafe { (*GPIO::ptr()).GPPUD.read() }
-/// ```
-impl ops::Deref for GPIO {
-    type Target = RegisterBlock;
-
-    fn deref(&self) -> &Self::Target {
-        unsafe { &*self.ptr() }
-    }
-}
+pub const GPIO_START: usize = 0x20_0000;
 
 impl Default for GPIO {
     fn default() -> GPIO {
         // Default RPi3 GPIO base address
-        const GPIO_BASE: usize = BcmHost::get_peripheral_address() + 0x20_0000;
-        GPIO::new(GPIO_BASE)
+        const GPIO_BASE: usize = BcmHost::get_peripheral_address() + GPIO_START;
+        unsafe { GPIO::new(GPIO_BASE) }
     }
 }
 
 impl GPIO {
-    pub fn new(base_addr: usize) -> GPIO {
-        GPIO { base_addr }
-    }
-
-    /// Returns a pointer to the register block
-    fn ptr(&self) -> *const RegisterBlock {
-        self.base_addr as *const _
+    /// # Safety
+    ///
+    /// Unsafe, duh!
+    pub const unsafe fn new(base_addr: usize) -> GPIO {
+        GPIO {
+            registers: Registers::new(base_addr),
+        }
     }
 
     pub fn get_pin(&self, pin: usize) -> Pin<Uninitialized> {
-        Pin::new(pin, self.base_addr)
+        unsafe { Pin::new(pin, self.registers.base_addr) }
     }
-}
 
-pub fn enable_uart_pins(gpio: &GPIO) {
-    gpio.PUD.set(0);
+    pub fn enable_uart_pins(&self) {
+        self.registers.PUD.set(0);
 
-    loop_delay(150);
+        loop_delay(2000);
 
-    // enable pins 14 and 15
-    gpio.PUDCLK[0].write(PUDCLK0::PUDCLK14::AssertClock + PUDCLK0::PUDCLK15::AssertClock);
+        // enable pins 14 and 15
+        self.registers.PUDCLK[0]
+            .write(PUDCLK0::PUDCLK14::AssertClock + PUDCLK0::PUDCLK15::AssertClock);
 
-    loop_delay(150);
+        loop_delay(2000);
 
-    gpio.PUDCLK[0].set(0);
+        self.registers.PUDCLK[0].set(0);
+    }
+
+    pub fn power_off(&self) {
+        // power off gpio pins (but not VCC pins)
+        for bank in 0..5 {
+            self.registers.FSEL[bank].set(0);
+        }
+
+        self.registers.PUD.set(0);
+
+        loop_delay(2000);
+
+        self.registers.PUDCLK[0].set(0xffff_ffff);
+        self.registers.PUDCLK[1].set(0xffff_ffff);
+
+        loop_delay(2000);
+
+        // flush GPIO setup
+        self.registers.PUDCLK[0].set(0);
+        self.registers.PUDCLK[1].set(0);
+    }
 }
 
 /// An alternative GPIO function.
@@ -174,7 +179,7 @@ impl ::core::convert::From<Function> for u32 {
 /// `into_alt` methods before it can be used.
 pub struct Pin<State> {
     pin: usize,
-    base_addr: usize,
+    registers: Registers,
     _state: PhantomData<State>,
 }
 
@@ -186,34 +191,10 @@ impl<State> Pin<State> {
     fn transition<NewState>(self) -> Pin<NewState> {
         Pin {
             pin: self.pin,
-            base_addr: self.base_addr,
+            registers: self.registers,
             _state: PhantomData,
         }
     }
-
-    /// Returns a pointer to the register block
-    #[inline(always)]
-    fn ptr(&self) -> *const RegisterBlock {
-        self.base_addr as *const _
-    }
-}
-
-/// Deref to Pin's Registers
-///
-/// Allows writing
-/// ```
-/// self.PUD.read()
-/// ```
-/// instead of something along the lines of
-/// ```
-/// unsafe { (*Pin::ptr()).PUD.read() }
-/// ```
-impl<State> ops::Deref for Pin<State> {
-    type Target = RegisterBlock;
-
-    fn deref(&self) -> &Self::Target {
-        unsafe { &*self.ptr() }
-    }
 }
 
 impl Pin<Uninitialized> {
@@ -222,13 +203,13 @@ impl Pin<Uninitialized> {
     /// # Panics
     ///
     /// Panics if `pin` > `53`.
-    fn new(pin: usize, base_addr: usize) -> Pin<Uninitialized> {
+    unsafe fn new(pin: usize, base_addr: usize) -> Pin<Uninitialized> {
         if pin > 53 {
             panic!("gpio::Pin::new(): pin {} exceeds maximum of 53", pin);
         }
 
         Pin {
-            base_addr,
+            registers: Registers::new(base_addr),
             pin,
             _state: PhantomData,
         }
@@ -239,7 +220,11 @@ impl Pin<Uninitialized> {
     pub fn into_alt(self, function: Function) -> Pin<Alt> {
         let bank = self.pin / 10;
         let off = self.pin % 10;
-        self.FSEL[bank].modify(FieldValue::<u32, ()>::new(0b111, off * 3, function.into()));
+        self.registers.FSEL[bank].modify(FieldValue::<u32, ()>::new(
+            0b111,
+            off * 3,
+            function.into(),
+        ));
         self.transition()
     }
 
@@ -262,7 +247,7 @@ impl Pin<Output> {
         // Guarantees: pin number is between [0; 53] by construction.
         let bank = self.pin / 32;
         let shift = self.pin % 32;
-        self.SET[bank].set(1 << shift);
+        self.registers.SET[bank].set(1 << shift);
     }
 
     /// Clears (turns off) this pin.
@@ -270,7 +255,7 @@ impl Pin<Output> {
         // Guarantees: pin number is between [0; 53] by construction.
         let bank = self.pin / 32;
         let shift = self.pin % 32;
-        self.CLR[bank].set(1 << shift);
+        self.registers.CLR[bank].set(1 << shift);
     }
 }
 
@@ -283,7 +268,7 @@ impl Pin<Input> {
         // Guarantees: pin number is between [0; 53] by construction.
         let bank = self.pin / 32;
         let off = self.pin % 32;
-        self.LEV[bank].matches_all(FieldValue::<u32, ()>::new(1, off, 1))
+        self.registers.LEV[bank].matches_all(FieldValue::<u32, ()>::new(1, off, 1))
     }
 }
 
@@ -294,7 +279,7 @@ mod tests {
     #[test_case]
     fn test_pin_transitions() {
         let mut reg = [0u32; 40];
-        let gpio = GPIO::new(&mut reg as *mut _ as usize);
+        let gpio = unsafe { GPIO::new(&mut reg as *mut _ as usize) };
 
         let _out = gpio.get_pin(1).into_output();
         assert_eq!(reg[0], 0b001_000);
@@ -307,7 +292,7 @@ mod tests {
     #[test_case]
     fn test_pin_outputs() {
         let mut reg = [0u32; 40];
-        let gpio = GPIO::new(&mut reg as *mut _ as usize);
+        let gpio = unsafe { GPIO::new(&mut reg as *mut _ as usize) };
 
         let pin = gpio.get_pin(1);
         let mut out = pin.into_output();
@@ -327,7 +312,7 @@ mod tests {
     #[test_case]
     fn test_pin_inputs() {
         let mut reg = [0u32; 40];
-        let gpio = GPIO::new(&mut reg as *mut _ as usize);
+        let gpio = unsafe { GPIO::new(&mut reg as *mut _ as usize) };
 
         let pin = gpio.get_pin(1);
         let inp = pin.into_input();
diff --git a/machine/src/platform/rpi3/mini_uart.rs b/machine/src/platform/rpi3/mini_uart.rs
index b47097b..c59a42e 100644
--- a/machine/src/platform/rpi3/mini_uart.rs
+++ b/machine/src/platform/rpi3/mini_uart.rs
@@ -9,12 +9,12 @@
 use tock_registers::interfaces::{Readable, Writeable};
 use {
     super::{gpio, BcmHost},
-    crate::devices::ConsoleOps,
+    crate::{devices::ConsoleOps, platform::MMIODerefWrapper},
     cfg_if::cfg_if,
-    core::{convert::From, fmt, ops},
+    core::{convert::From, fmt},
     tock_registers::{
         interfaces::ReadWriteable,
-        register_bitfields,
+        register_bitfields, register_structs,
         registers::{ReadOnly, ReadWrite, WriteOnly},
     },
 };
@@ -88,33 +88,41 @@ register_bitfields! {
         ]
     ],
 
-    /// Mini Uart Baudrate
+    /// Mini Uart Baud rate
     AUX_MU_BAUD [
-        /// Mini UART baudrate counter
+        /// Mini UART baud rate counter
         RATE OFFSET(0) NUMBITS(16) []
     ]
 }
 
-#[allow(non_snake_case)]
-#[repr(C)]
-pub struct RegisterBlock {
-    __reserved_0: u32,                                  // 0x00 - AUX_IRQ?
-    AUX_ENABLES: ReadWrite<u32, AUX_ENABLES::Register>, // 0x04
-    __reserved_1: [u32; 14],                            // 0x08
-    AUX_MU_IO: ReadWrite<u32>,                          // 0x40 - Mini Uart I/O Data
-    AUX_MU_IER: WriteOnly<u32>,                         // 0x44 - Mini Uart Interrupt Enable
-    AUX_MU_IIR: WriteOnly<u32, AUX_MU_IIR::Register>,   // 0x48
-    AUX_MU_LCR: WriteOnly<u32, AUX_MU_LCR::Register>,   // 0x4C
-    AUX_MU_MCR: WriteOnly<u32>,                         // 0x50
-    AUX_MU_LSR: ReadOnly<u32, AUX_MU_LSR::Register>,    // 0x54
-    __reserved_2: [u32; 2],                             // 0x58 - AUX_MU_MSR, AUX_MU_SCRATCH
-    AUX_MU_CNTL: WriteOnly<u32, AUX_MU_CNTL::Register>, // 0x60
-    __reserved_3: u32,                                  // 0x64 - AUX_MU_STAT
-    AUX_MU_BAUD: WriteOnly<u32, AUX_MU_BAUD::Register>, // 0x68
+register_structs! {
+    #[allow(non_snake_case)]
+    RegisterBlock {
+        // 0x00 - AUX_IRQ?
+        (0x00 => __reserved_1),
+        (0x04 => AUX_ENABLES: ReadWrite<u32, AUX_ENABLES::Register>),
+        (0x08 => __reserved_2),
+        (0x40 => AUX_MU_IO: ReadWrite<u32>),//Mini Uart I/O Data
+        (0x44 => AUX_MU_IER: WriteOnly<u32>),//Mini Uart Interrupt Enable
+        (0x48 => AUX_MU_IIR: WriteOnly<u32, AUX_MU_IIR::Register>),
+        (0x4c => AUX_MU_LCR: WriteOnly<u32, AUX_MU_LCR::Register>),
+        (0x50 => AUX_MU_MCR: WriteOnly<u32>),
+        (0x54 => AUX_MU_LSR: ReadOnly<u32, AUX_MU_LSR::Register>),
+        // 0x58 - AUX_MU_MSR
+        // 0x5c - AUX_MU_SCRATCH
+        (0x58 => __reserved_3),
+        (0x60 => AUX_MU_CNTL: WriteOnly<u32, AUX_MU_CNTL::Register>),
+        // 0x64 - AUX_MU_STAT
+        (0x64 => __reserved_4),
+        (0x68 => AUX_MU_BAUD: WriteOnly<u32, AUX_MU_BAUD::Register>),
+        (0x6c => @END),
+    }
 }
 
+type Registers = MMIODerefWrapper<RegisterBlock>;
+
 pub struct MiniUart {
-    base_addr: usize,
+    registers: Registers,
 }
 
 pub struct PreparedMiniUart(MiniUart);
@@ -130,41 +138,24 @@ impl From<Rate> for u32 {
     }
 }
 
-/// Deref to RegisterBlock
-///
-/// Allows writing
-/// ```
-/// self.MU_IER.read()
-/// ```
-/// instead of something along the lines of
-/// ```
-/// unsafe { (*MiniUart::ptr()).MU_IER.read() }
-/// ```
-impl ops::Deref for MiniUart {
-    type Target = RegisterBlock;
-
-    fn deref(&self) -> &Self::Target {
-        unsafe { &*self.ptr() }
-    }
-}
-
 // [temporary] Used in mmu.rs to set up local paging
-pub const UART1_BASE: usize = BcmHost::get_peripheral_address() + 0x21_5000;
+pub const UART1_START: usize = 0x21_5000;
 
 impl Default for MiniUart {
-    fn default() -> MiniUart {
-        MiniUart::new(UART1_BASE)
+    fn default() -> Self {
+        const UART1_BASE: usize = BcmHost::get_peripheral_address() + UART1_START;
+        unsafe { MiniUart::new(UART1_BASE) }
     }
 }
 
 impl MiniUart {
-    pub fn new(base_addr: usize) -> MiniUart {
-        MiniUart { base_addr }
-    }
-
-    /// Returns a pointer to the register block
-    fn ptr(&self) -> *const RegisterBlock {
-        self.base_addr as *const _
+    /// # Safety
+    ///
+    /// Unsafe, duh!
+    pub const unsafe fn new(base_addr: usize) -> MiniUart {
+        MiniUart {
+            registers: Registers::new(base_addr),
+        }
     }
 }
 
@@ -174,14 +165,14 @@ impl MiniUart {
             /// Set baud rate and characteristics (115200 8N1) and map to GPIO
             pub fn prepare(self, gpio: &gpio::GPIO) -> PreparedMiniUart {
                 // initialize UART
-                self.AUX_ENABLES.modify(AUX_ENABLES::MINI_UART_ENABLE::SET);
-                self.AUX_MU_IER.set(0);
-                self.AUX_MU_CNTL.set(0);
-                self.AUX_MU_LCR.write(AUX_MU_LCR::DATA_SIZE::EightBit);
-                self.AUX_MU_MCR.set(0);
-                self.AUX_MU_IER.set(0);
-                self.AUX_MU_IIR.write(AUX_MU_IIR::FIFO_CLEAR::All);
-                self.AUX_MU_BAUD
+                self.registers.AUX_ENABLES.modify(AUX_ENABLES::MINI_UART_ENABLE::SET);
+                self.registers.AUX_MU_IER.set(0);
+                self.registers.AUX_MU_CNTL.set(0);
+                self.registers.AUX_MU_LCR.write(AUX_MU_LCR::DATA_SIZE::EightBit);
+                self.registers.AUX_MU_MCR.set(0);
+                self.registers.AUX_MU_IER.set(0);
+                self.registers.AUX_MU_IIR.write(AUX_MU_IIR::FIFO_CLEAR::All);
+                self.registers.AUX_MU_BAUD
                     .write(AUX_MU_BAUD::RATE.val(Rate::Baud115200.into()));
 
                 // Pin 14
@@ -193,13 +184,13 @@ impl MiniUart {
                 gpio.get_pin(14).into_alt(MINI_UART_TXD);
                 gpio.get_pin(15).into_alt(MINI_UART_RXD);
 
-                gpio::enable_uart_pins(gpio);
+                gpio.enable_uart_pins();
 
-                self.AUX_MU_CNTL
+                self.registers.AUX_MU_CNTL
                     .write(AUX_MU_CNTL::RX_EN::Enabled + AUX_MU_CNTL::TX_EN::Enabled);
 
                 // Clear FIFOs before using the device
-                self.AUX_MU_IIR.write(AUX_MU_IIR::FIFO_CLEAR::All);
+                self.registers.AUX_MU_IIR.write(AUX_MU_IIR::FIFO_CLEAR::All);
 
                 PreparedMiniUart(self)
             }
@@ -214,6 +205,7 @@ impl MiniUart {
 impl Drop for PreparedMiniUart {
     fn drop(&mut self) {
         self.0
+            .registers
             .AUX_ENABLES
             .modify(AUX_ENABLES::MINI_UART_ENABLE::CLEAR);
         // @todo disable gpio.PUD ?
@@ -226,10 +218,10 @@ impl ConsoleOps for PreparedMiniUart {
             /// Send a character
             fn putc(&self, c: char) {
                 // wait until we can send
-                crate::arch::loop_until(|| self.0.AUX_MU_LSR.is_set(AUX_MU_LSR::TX_EMPTY));
+                crate::arch::loop_until(|| self.0.registers.AUX_MU_LSR.is_set(AUX_MU_LSR::TX_EMPTY));
 
                 // write the character to the buffer
-                self.0.AUX_MU_IO.set(c as u32);
+                self.0.registers.AUX_MU_IO.set(c as u32);
             }
 
             /// Display a string
@@ -247,10 +239,10 @@ impl ConsoleOps for PreparedMiniUart {
             /// Receive a character
             fn getc(&self) -> char {
                 // wait until something is in the buffer
-                crate::arch::loop_until(|| self.0.AUX_MU_LSR.is_set(AUX_MU_LSR::DATA_READY));
+                crate::arch::loop_until(|| self.0.registers.AUX_MU_LSR.is_set(AUX_MU_LSR::DATA_READY));
 
                 // read it and return
-                let mut ret = self.0.AUX_MU_IO.get() as u8 as char;
+                let mut ret = self.0.registers.AUX_MU_IO.get() as u8 as char;
 
                 // convert carriage return to newline
                 if ret == '\r' {
@@ -263,7 +255,7 @@ impl ConsoleOps for PreparedMiniUart {
             /// Wait until the TX FIFO is empty, aka all characters have been put on the
             /// line.
             fn flush(&self) {
-                crate::arch::loop_until(|| self.0.AUX_MU_LSR.is_set(AUX_MU_LSR::TX_IDLE));
+                crate::arch::loop_until(|| self.0.registers.AUX_MU_LSR.is_set(AUX_MU_LSR::TX_IDLE));
             }
         } else {
             fn putc(&self, _c: char) {}
diff --git a/machine/src/platform/rpi3/pl011_uart.rs b/machine/src/platform/rpi3/pl011_uart.rs
index 557c3cb..cd1e04c 100644
--- a/machine/src/platform/rpi3/pl011_uart.rs
+++ b/machine/src/platform/rpi3/pl011_uart.rs
@@ -14,12 +14,11 @@ use {
         mailbox::{self, MailboxOps},
         BcmHost,
     },
-    crate::{arch::loop_until, devices::ConsoleOps},
-    core::ops,
+    crate::{arch::loop_until, devices::ConsoleOps, platform::MMIODerefWrapper},
     snafu::Snafu,
     tock_registers::{
         interfaces::{Readable, Writeable},
-        register_bitfields,
+        register_bitfields, register_structs,
         registers::{ReadOnly, ReadWrite, WriteOnly},
     },
 };
@@ -111,29 +110,31 @@ register_bitfields! {
     ]
 }
 
-#[allow(non_snake_case)]
-#[repr(C)]
-pub struct RegisterBlock {
-    DR: ReadWrite<u32>,                   // 0x00
-    __reserved_0: [u32; 5],               // 0x04 (UART0_RSRECR=0x04)
-    FR: ReadOnly<u32, FR::Register>,      // 0x18
-    __reserved_1: [u32; 1],               // 0x1c
-    ILPR: u32,                            // 0x20
-    IBRD: WriteOnly<u32, IBRD::Register>, // 0x24
-    FBRD: WriteOnly<u32, FBRD::Register>, // 0x28
-    LCRH: WriteOnly<u32, LCRH::Register>, // 0x2C
-    CR: WriteOnly<u32, CR::Register>,     // 0x30
-    IFLS: u32,                            // 0x34
-    IMSC: u32,                            // 0x38
-    RIS: u32,                             // 0x3C
-    MIS: u32,                             // 0x40
-    ICR: WriteOnly<u32, ICR::Register>,   // 0x44
-    DMACR: u32,                           // 0x48
-    __reserved_2: [u32; 14],              // 0x4c-0x7c
-    ITCR: u32,                            // 0x80
-    ITIP: u32,                            // 0x84
-    ITOP: u32,                            // 0x88
-    TDR: u32,                             // 0x8C
+register_structs! {
+    #[allow(non_snake_case)]
+    RegisterBlock {
+        (0x00 => DR: ReadWrite<u32>),
+        (0x04 => __reserved_1), // (UART0_RSRECR=0x04)
+        (0x18 => FR: ReadOnly<u32, FR::Register>),
+        (0x1c => __reserved_2),
+        (0x20 => ILPR: u32),
+        (0x24 => IBRD: WriteOnly<u32, IBRD::Register>),
+        (0x28 => FBRD: WriteOnly<u32, FBRD::Register>),
+        (0x2c => LCRH: WriteOnly<u32, LCRH::Register>),
+        (0x30 => CR: WriteOnly<u32, CR::Register>),
+        (0x34 => IFLS: u32),
+        (0x38 => IMSC: u32),
+        (0x3c => RIS: u32),
+        (0x40 => MIS: u32),
+        (0x44 => ICR: WriteOnly<u32, ICR::Register>),
+        (0x48 => DMACR: u32),
+        (0x4c => __reserved_3),
+        (0x80 => ITCR: u32),
+        (0x84 => ITIP: u32),
+        (0x88 => ITOP: u32),
+        (0x8c => TDR: u32),
+        (0x90 => @END),
+    }
 }
 
 #[derive(Debug, Snafu)]
@@ -141,10 +142,13 @@ pub enum PL011UartError {
     #[snafu(display("PL011 UART setup failed in mailbox operation"))]
     MailboxError,
 }
+
 pub type Result<T> = ::core::result::Result<T, PL011UartError>;
 
+type Registers = MMIODerefWrapper<RegisterBlock>;
+
 pub struct PL011Uart {
-    base_addr: usize,
+    registers: Registers,
 }
 
 pub struct PreparedPL011Uart(PL011Uart);
@@ -160,37 +164,23 @@ impl From<Rate> for u32 {
     }
 }
 
-impl ops::Deref for PL011Uart {
-    type Target = RegisterBlock;
-
-    fn deref(&self) -> &Self::Target {
-        unsafe { &*self.ptr() }
-    }
-}
-
-impl ops::Deref for PreparedPL011Uart {
-    type Target = RegisterBlock;
-
-    fn deref(&self) -> &Self::Target {
-        unsafe { &*self.0.ptr() }
-    }
-}
+pub const UART_START: usize = 0x20_1000;
 
 impl Default for PL011Uart {
     fn default() -> Self {
-        const UART0_BASE: usize = BcmHost::get_peripheral_address() + 0x20_1000;
-        PL011Uart::new(UART0_BASE)
+        const UART0_BASE: usize = BcmHost::get_peripheral_address() + UART_START;
+        unsafe { PL011Uart::new(UART0_BASE) }
     }
 }
 
 impl PL011Uart {
-    pub fn new(base_addr: usize) -> PL011Uart {
-        PL011Uart { base_addr }
-    }
-
-    /// Returns a pointer to the register block
-    fn ptr(&self) -> *const RegisterBlock {
-        self.base_addr as *const _
+    /// # Safety
+    ///
+    /// Unsafe, duh!
+    pub const unsafe fn new(base_addr: usize) -> PL011Uart {
+        PL011Uart {
+            registers: Registers::new(base_addr),
+        }
     }
 
     /// Set baud rate and characteristics (115200 8N1) and map to GPIO
@@ -200,7 +190,7 @@ impl PL011Uart {
         gpio: &gpio::GPIO,
     ) -> Result<PreparedPL011Uart> {
         // turn off UART0
-        self.CR.set(0);
+        self.registers.CR.set(0);
 
         // set up clock for consistent divisor values
         let index = mbox.request();
@@ -220,15 +210,18 @@ impl PL011Uart {
         gpio.get_pin(14).into_alt(UART_TXD);
         gpio.get_pin(15).into_alt(UART_RXD);
 
-        gpio::enable_uart_pins(gpio);
+        gpio.enable_uart_pins();
 
-        self.ICR.write(ICR::ALL::CLEAR);
+        self.registers.ICR.write(ICR::ALL::CLEAR);
         // @todo Configure divisors more sanely
-        self.IBRD.write(IBRD::IBRD.val(Rate::Baud115200.into()));
-        self.FBRD.write(FBRD::FBRD.val(0xB)); // Results in 115200 baud
-        self.LCRH.write(LCRH::WLEN::EightBit); // 8N1
+        self.registers
+            .IBRD
+            .write(IBRD::IBRD.val(Rate::Baud115200.into()));
+        self.registers.FBRD.write(FBRD::FBRD.val(0xB)); // Results in 115200 baud
+        self.registers.LCRH.write(LCRH::WLEN::EightBit); // 8N1
 
-        self.CR
+        self.registers
+            .CR
             .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
 
         Ok(PreparedPL011Uart(self))
@@ -237,7 +230,9 @@ impl PL011Uart {
 
 impl Drop for PreparedPL011Uart {
     fn drop(&mut self) {
-        self.CR
+        self.0
+            .registers
+            .CR
             .write(CR::UARTEN::Disabled + CR::TXE::Disabled + CR::RXE::Disabled);
     }
 }
@@ -246,10 +241,10 @@ impl ConsoleOps for PreparedPL011Uart {
     /// Send a character
     fn putc(&self, c: char) {
         // wait until we can send
-        loop_until(|| !self.FR.is_set(FR::TXFF));
+        loop_until(|| !self.0.registers.FR.is_set(FR::TXFF));
 
         // write the character to the buffer
-        self.DR.set(c as u32);
+        self.0.registers.DR.set(c as u32);
     }
 
     /// Display a string
@@ -267,10 +262,10 @@ impl ConsoleOps for PreparedPL011Uart {
     /// Receive a character
     fn getc(&self) -> char {
         // wait until something is in the buffer
-        loop_until(|| !self.FR.is_set(FR::RXFE));
+        loop_until(|| !self.0.registers.FR.is_set(FR::RXFE));
 
         // read it and return
-        let mut ret = self.DR.get() as u8 as char;
+        let mut ret = self.0.registers.DR.get() as u8 as char;
 
         // convert carriage return to newline
         if ret == '\r' {
diff --git a/machine/src/platform/rpi3/power.rs b/machine/src/platform/rpi3/power.rs
index 279b901..4d0d3b9 100644
--- a/machine/src/platform/rpi3/power.rs
+++ b/machine/src/platform/rpi3/power.rs
@@ -11,23 +11,24 @@ use {
         mailbox::{channel, Mailbox, MailboxOps},
         BcmHost,
     },
-    crate::arch::loop_delay,
-    core::ops,
+    crate::platform::MMIODerefWrapper,
     snafu::Snafu,
     tock_registers::{
         interfaces::{Readable, Writeable},
+        register_structs,
         registers::ReadWrite,
     },
 };
 
-const POWER_BASE: usize = BcmHost::get_peripheral_address() + 0x0010_001C;
-
-#[allow(non_snake_case)]
-#[repr(C)]
-pub struct RegisterBlock {
-    PM_RSTC: ReadWrite<u32>, // 0x1C
-    PM_RSTS: ReadWrite<u32>, // 0x20
-    PM_WDOG: ReadWrite<u32>, // 0x24
+register_structs! {
+    #[allow(non_snake_case)]
+    RegisterBlock {
+        (0x00 => __reserved_1),
+        (0x1c => PM_RSTC: ReadWrite<u32>),
+        (0x20 => PM_RSTS: ReadWrite<u32>),
+        (0x24 => PM_WDOG: ReadWrite<u32>),
+        (0x28 => @END),
+    }
 }
 
 const PM_PASSWORD: u32 = 0x5a00_0000;
@@ -50,28 +51,33 @@ pub enum PowerError {
     #[snafu(display("Power setup failed in mailbox operation"))]
     MailboxError,
 }
+
 pub type Result<T> = ::core::result::Result<T, PowerError>;
 
+type Registers = MMIODerefWrapper<RegisterBlock>;
+
+const POWER_START: usize = 0x0010_0000;
+
 /// Public interface to the Power subsystem
-pub struct Power;
+pub struct Power {
+    registers: Registers,
+}
 
-impl ops::Deref for Power {
-    type Target = RegisterBlock;
-
-    fn deref(&self) -> &Self::Target {
-        unsafe { &*Self::ptr() }
+impl Default for Power {
+    fn default() -> Power {
+        const POWER_BASE: usize = BcmHost::get_peripheral_address() + POWER_START;
+        unsafe { Power::new(POWER_BASE) }
     }
 }
 
 impl Power {
-    #[allow(clippy::new_without_default)]
-    pub fn new() -> Power {
-        Power
-    }
-
-    /// Returns a pointer to the register block
-    fn ptr() -> *const RegisterBlock {
-        POWER_BASE as *const _
+    /// # Safety
+    ///
+    /// Unsafe, duh!
+    pub const unsafe fn new(base_addr: usize) -> Power {
+        Power {
+            registers: Registers::new(base_addr),
+        }
     }
 
     /// Shutdown the board
@@ -88,30 +94,14 @@ impl Power {
                 .map_err(|_| PowerError::MailboxError)?;
         }
 
-        // power off gpio pins (but not VCC pins)
-        for bank in 0..5 {
-            gpio.FSEL[bank].set(0);
-        }
-
-        gpio.PUD.set(0);
-
-        loop_delay(150);
-
-        gpio.PUDCLK[0].set(0xffff_ffff);
-        gpio.PUDCLK[1].set(0xffff_ffff);
-
-        loop_delay(150);
-
-        // flush GPIO setup
-        gpio.PUDCLK[0].set(0);
-        gpio.PUDCLK[1].set(0);
+        gpio.power_off();
 
         // We set the watchdog hard reset bit here to distinguish this
         // reset from the normal (full) reset. bootcode.bin will not
         // reboot after a hard reset.
-        let mut val = self.PM_RSTS.get();
+        let mut val = self.registers.PM_RSTS.get();
         val |= PM_PASSWORD | PM_RSTS_RASPBERRYPI_HALT;
-        self.PM_RSTS.set(val);
+        self.registers.PM_RSTS.set(val);
 
         // Continue with normal reset mechanism
         self.reset();
@@ -120,11 +110,11 @@ impl Power {
     /// Reboot
     pub fn reset(&self) -> ! {
         // use a timeout of 10 ticks (~150us)
-        self.PM_WDOG.set(PM_PASSWORD | 10);
-        let mut val = self.PM_RSTC.get();
+        self.registers.PM_WDOG.set(PM_PASSWORD | 10);
+        let mut val = self.registers.PM_RSTC.get();
         val &= PM_RSTC_WRCFG_CLR;
         val |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET;
-        self.PM_RSTC.set(val);
+        self.registers.PM_RSTC.set(val);
 
         crate::endless_sleep()
     }
diff --git a/nucleus/src/main.rs b/nucleus/src/main.rs
index 8a60006..7f651ef 100644
--- a/nucleus/src/main.rs
+++ b/nucleus/src/main.rs
@@ -192,7 +192,7 @@ fn reboot() -> ! {
             use machine::platform::rpi3::power::Power;
 
             println!("Bye, going to reset now");
-            Power::new().reset()
+            Power::default().reset()
         }
     }
 }