1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/// A memory mapped register, for instance ARM debug registers (DHCSR, etc).
pub trait MemoryMappedRegister<T>: Clone + From<T> + Into<T> + Sized + std::fmt::Debug {
    /// The register's address in the target memory.
    /// For some architectures (e.g. ARM Cortex-A) this address is offset from a base address.
    /// For others (e.g. ARM Cortex-M, RISC-V) this address is absolute.
    const ADDRESS_OFFSET: u64;
    /// The register's name.
    const NAME: &'static str;
    /// Get the register's address in the memory map.
    /// For architectures like ARM Cortex-A, this address is offset from a base address, which must be supplied when calling this method.
    /// For others (e.g. ARM Cortex-M, RISC-V) where this address is a constant, please use [`MemoryMappedRegister::get_mmio_address`].
    fn get_mmio_address_from_base(base_address: u64) -> Result<u64, anyhow::Error> {
        if let Some(mmio_address) = base_address.checked_add(Self::ADDRESS_OFFSET) {
            Ok(mmio_address)
        } else {
            Err(anyhow::anyhow!("Overflow while attempting to determine the MMIO address for register {} at offset {:#x} from base address {:#x}", Self::NAME, Self::ADDRESS_OFFSET, base_address))
        }
    }
    /// Get the register's address in the memory map.
    /// For architectures ARM Cortex-M and RISC-V, this address is constant value stored as part of [`MemoryMappedRegister::ADDRESS_OFFSET`].
    /// For other architectures (e.g. ARM Cortex-A) where this address is offset from a base address, please use [`MemoryMappedRegister::get_mmio_address_from_base`].
    fn get_mmio_address() -> u64 {
        Self::ADDRESS_OFFSET
    }
}

#[macro_export]
/// Create a [`MemoryMappedRegister`] type, with the required method implementations for:
/// - Trait implementations required by [`MemoryMappedRegister`]
/// - Includes a `bitfield!` mapping for bitfield access to optionally defined fields.
/// When no bitfields are defined, the default `.0` field must be used.
///
/// # Example
/// ```
/// use bitfield::bitfield;
/// use probe_rs::memory_mapped_bitfield_register;
/// memory_mapped_bitfield_register! {
///    /// Abstract Control and Status (see section xyz of some reference manual)
///    pub struct AbstractCS(u32);
///    0x16,"abstractcs",
///    impl From;
///    progbufsize, _: 28, 24;
///    busy, _: 12;
///    cmderr, set_cmderr: 10, 8;
///    datacount, _: 3, 0;
/// }
/// ```
/// This will generate a struct with the name `AbstractCS`, which has:
/// - A `pub` visibility.
/// - A `u32` register type.
/// - A `Debug` implementation.
/// - A `Copy` implementation.
/// - A `Clone` implementation.
/// - Default `From<u32>` and `From<MemoryMappedRegister>` impls for [`MemoryMappedRegister`] are generated.
/// - A `bitfield!` mapping for the fields `progbufsize`, `busy`, `cmderr`, `datacount`.
/// - `bitfield!` getters and setters for the fields as defined - See [`bitfield::bitfield!`] for more information.
/// - A `const ADDRESS_OFFSET: u64 = 0x16;`.
/// - A `const NAME: &'static str = "abstractcs";`.
macro_rules! memory_mapped_bitfield_register {
    ($(#[$outer:meta])* $visibility:vis struct $struct_name:ident($reg_type:ty); $addr:expr, $reg_name:expr, impl From; $($rest:tt)*) => {
        $crate::memory_mapped_bitfield_register!{
            $(#[$outer])* $visibility struct $struct_name($reg_type); $addr, $reg_name, $($rest)*
        }

        impl From<$struct_name> for $reg_type {
            fn from(register: $struct_name) -> Self {
                register.0
            }
        }

        impl From<$reg_type> for $struct_name {
            fn from(value: $reg_type) -> Self {
                Self(value)
            }
        }
    };
    ($(#[$outer:meta])* $vis_modifier:vis struct $struct_name:ident($reg_type:ty); $addr:expr, $reg_name:expr, $($rest:tt)*) => {
        // Using paste here, because as of bitfield = "0.14.0" they do not use the 'vis' specifier, and balks at being passed a visibility token.
        paste::paste!{
            bitfield::bitfield!{
                $(#[$outer])*
                #[doc= concat!("A [`bitfield::bitfield!`] register mapping for the register `",  $reg_name, "` located at address `", stringify!($addr), "`.")]
                #[derive(Copy, Clone)]
                $vis_modifier struct $struct_name($reg_type);
                impl Debug;
                $($rest)*
            }
        }

        impl $crate::MemoryMappedRegister<$reg_type> for $struct_name {
            const ADDRESS_OFFSET: u64 = $addr;
            const NAME: &'static str = $reg_name;
        }
    };
}