macro impl_csr_read($struct:ident, $repr:ty, $reg:ident, $register:ty) {
    impl tock_registers::interfaces::Readable for $struct {
        type T = $repr;
        type R = $register;

        #[inline]
        fn get(&self) -> $repr {
            let mut value: $repr;
            unsafe {
                core::arch::asm!(concat!("csrr {0}, ", stringify!($reg)), out(reg) value);
            }
            value
        }
    }
}
macro impl_csr_write($struct:ident, $repr:ty, $reg:ident, $register:ty) {
    impl tock_registers::interfaces::Writeable for $struct {
        type T = $repr;
        type R = $register;

        #[inline]
        fn set(&self, value: $repr) {
            unsafe {
                core::arch::asm!(concat!("csrw ", stringify!($reg), ", {0}"), in(reg) value);
            }
        }
    }
}

pub mod satp {
    use tock_registers::register_bitfields;

    use super::{impl_csr_read, impl_csr_write};

    register_bitfields!(
        u64,
        pub SATP [
            PPN OFFSET(0) NUMBITS(44) [],
            ASID OFFSET(44) NUMBITS(16) [],
            MODE OFFSET(60) NUMBITS(4) [
                Bare = 0,
                Sv39 = 8,
                Sv48 = 9,
                Sv57 = 10,
                Sv64 = 11,
            ],
        ]
    );

    pub struct Reg;

    impl_csr_read!(Reg, u64, satp, SATP::Register);
    impl_csr_write!(Reg, u64, satp, SATP::Register);

    pub const SATP: Reg = Reg;
}

pub mod stvec {
    use tock_registers::{interfaces::ReadWriteable, register_bitfields};

    use super::{impl_csr_read, impl_csr_write};

    register_bitfields!(
        u64,
        pub STVEC [
            MODE OFFSET(0) NUMBITS(2) [
                Direct = 0,
                Vectored = 1
            ],
            BASE OFFSET(2) NUMBITS(62) [],
        ]
    );

    pub struct Reg;

    impl_csr_read!(Reg, u64, stvec, STVEC::Register);
    impl_csr_write!(Reg, u64, stvec, STVEC::Register);

    impl Reg {
        pub fn set_base(&self, base: usize) {
            debug_assert_eq!(base & 0xF, 0);
            let mask = match base & 63 != 0 {
                false => 0,
                true => 0x3 << 62,
            };
            self.modify(STVEC::BASE.val(((base as u64) >> 2) | mask));
        }
    }

    pub const STVEC: Reg = Reg;
}

pub mod scause {
    use tock_registers::register_bitfields;

    use super::{impl_csr_read, impl_csr_write};

    register_bitfields!(
        u64,
        pub SCAUSE [
            CODE OFFSET(0) NUMBITS(63) [],
            INTERRUPT OFFSET(63) NUMBITS(1) [],
        ]
    );

    pub struct Reg;

    impl_csr_read!(Reg, u64, scause, SCAUSE::Register);
    impl_csr_write!(Reg, u64, scause, SCAUSE::Register);

    pub const SCAUSE: Reg = Reg;
}

pub mod stval {
    use super::{impl_csr_read, impl_csr_write};

    pub struct Reg;

    impl_csr_read!(Reg, u64, stval, ());
    impl_csr_write!(Reg, u64, stval, ());

    pub const STVAL: Reg = Reg;
}

pub mod sepc {
    use super::{impl_csr_read, impl_csr_write};

    pub struct Reg;

    impl_csr_read!(Reg, u64, sepc, ());
    impl_csr_write!(Reg, u64, sepc, ());

    pub const SEPC: Reg = Reg;
}

pub mod sstatus {
    use tock_registers::register_bitfields;

    use super::{impl_csr_read, impl_csr_write};

    register_bitfields!(
        u64,
        pub SSTATUS [
            SUM OFFSET(18) NUMBITS(1) [],
            SPP OFFSET(8) NUMBITS(1) [],
            SIE OFFSET(1) NUMBITS(1) [],
        ]
    );

    pub struct Reg;

    impl_csr_read!(Reg, u64, sstatus, SSTATUS::Register);
    impl_csr_write!(Reg, u64, sstatus, SSTATUS::Register);

    pub const SSTATUS: Reg = Reg;
}

pub mod sscratch {
    use super::{impl_csr_read, impl_csr_write};

    pub struct Reg;

    impl_csr_read!(Reg, u64, sscratch, ());
    impl_csr_write!(Reg, u64, sscratch, ());

    pub const SSCRATCH: Reg = Reg;
}

pub mod sip {
    use tock_registers::register_bitfields;

    use super::{impl_csr_read, impl_csr_write};

    register_bitfields!(
        u64,
        pub SIP [
            SSIP OFFSET(1) NUMBITS(1) [],
            STIP OFFSET(5) NUMBITS(1) [],
            SEIP OFFSET(9) NUMBITS(1) [],
        ]
    );

    pub struct Reg;

    impl_csr_read!(Reg, u64, sip, SIP::Register);
    impl_csr_write!(Reg, u64, sip, SIP::Register);

    pub const SIP: Reg = Reg;
}

pub mod sie {
    use tock_registers::register_bitfields;

    use super::{impl_csr_read, impl_csr_write};

    register_bitfields!(
        u64,
        pub SIE [
            SSIE OFFSET(1) NUMBITS(1) [],
            STIE OFFSET(5) NUMBITS(1) [],
            SEIE OFFSET(9) NUMBITS(1) [],
        ]
    );

    pub struct Reg;

    impl_csr_read!(Reg, u64, sie, SIE::Register);
    impl_csr_write!(Reg, u64, sie, SIE::Register);

    pub const SIE: Reg = Reg;
}

pub use satp::SATP;
pub use scause::SCAUSE;
pub use sepc::SEPC;
pub use sie::SIE;
pub use sip::SIP;
pub use sscratch::SSCRATCH;
pub use sstatus::SSTATUS;
pub use stval::STVAL;
pub use stvec::STVEC;
