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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//! Cache and branch predictor maintenance operations
//!
//! *NOTE* Not available on Armv6-M.

use volatile_register::WO;

use crate::peripheral::CBP;

/// Register block
#[repr(C)]
pub struct RegisterBlock {
    /// I-cache invalidate all to PoU
    pub iciallu: WO<u32>,
    reserved0: u32,
    /// I-cache invalidate by MVA to PoU
    pub icimvau: WO<u32>,
    /// D-cache invalidate by MVA to PoC
    pub dcimvac: WO<u32>,
    /// D-cache invalidate by set-way
    pub dcisw: WO<u32>,
    /// D-cache clean by MVA to PoU
    pub dccmvau: WO<u32>,
    /// D-cache clean by MVA to PoC
    pub dccmvac: WO<u32>,
    /// D-cache clean by set-way
    pub dccsw: WO<u32>,
    /// D-cache clean and invalidate by MVA to PoC
    pub dccimvac: WO<u32>,
    /// D-cache clean and invalidate by set-way
    pub dccisw: WO<u32>,
    /// Branch predictor invalidate all
    pub bpiall: WO<u32>,
}

const CBP_SW_WAY_POS: u32 = 30;
const CBP_SW_WAY_MASK: u32 = 0x3 << CBP_SW_WAY_POS;
const CBP_SW_SET_POS: u32 = 5;
const CBP_SW_SET_MASK: u32 = 0x1FF << CBP_SW_SET_POS;

impl CBP {
    /// I-cache invalidate all to PoU
    #[inline(always)]
    pub fn iciallu(&mut self) {
        unsafe { self.iciallu.write(0) };
    }

    /// I-cache invalidate by MVA to PoU
    #[inline(always)]
    pub fn icimvau(&mut self, mva: u32) {
        unsafe { self.icimvau.write(mva) };
    }

    /// D-cache invalidate by MVA to PoC
    #[inline(always)]
    pub unsafe fn dcimvac(&mut self, mva: u32) {
        self.dcimvac.write(mva);
    }

    /// D-cache invalidate by set-way
    ///
    /// `set` is masked to be between 0 and 3, and `way` between 0 and 511.
    #[inline(always)]
    pub unsafe fn dcisw(&mut self, set: u16, way: u16) {
        // The ARMv7-M Architecture Reference Manual, as of Revision E.b, says these set/way
        // operations have a register data format which depends on the implementation's
        // associativity and number of sets. Specifically the 'way' and 'set' fields have
        // offsets 32-log2(ASSOCIATIVITY) and log2(LINELEN) respectively.
        //
        // However, in Cortex-M7 devices, these offsets are fixed at 30 and 5, as per the Cortex-M7
        // Generic User Guide section 4.8.3. Since no other ARMv7-M implementations except the
        // Cortex-M7 have a DCACHE or ICACHE at all, it seems safe to do the same thing as the
        // CMSIS-Core implementation and use fixed values.
        self.dcisw.write(
            ((u32::from(way) & (CBP_SW_WAY_MASK >> CBP_SW_WAY_POS)) << CBP_SW_WAY_POS)
                | ((u32::from(set) & (CBP_SW_SET_MASK >> CBP_SW_SET_POS)) << CBP_SW_SET_POS),
        );
    }

    /// D-cache clean by MVA to PoU
    #[inline(always)]
    pub fn dccmvau(&mut self, mva: u32) {
        unsafe {
            self.dccmvau.write(mva);
        }
    }

    /// D-cache clean by MVA to PoC
    #[inline(always)]
    pub fn dccmvac(&mut self, mva: u32) {
        unsafe {
            self.dccmvac.write(mva);
        }
    }

    /// D-cache clean by set-way
    ///
    /// `set` is masked to be between 0 and 3, and `way` between 0 and 511.
    #[inline(always)]
    pub fn dccsw(&mut self, set: u16, way: u16) {
        // See comment for dcisw() about the format here
        unsafe {
            self.dccsw.write(
                ((u32::from(way) & (CBP_SW_WAY_MASK >> CBP_SW_WAY_POS)) << CBP_SW_WAY_POS)
                    | ((u32::from(set) & (CBP_SW_SET_MASK >> CBP_SW_SET_POS)) << CBP_SW_SET_POS),
            );
        }
    }

    /// D-cache clean and invalidate by MVA to PoC
    #[inline(always)]
    pub fn dccimvac(&mut self, mva: u32) {
        unsafe {
            self.dccimvac.write(mva);
        }
    }

    /// D-cache clean and invalidate by set-way
    ///
    /// `set` is masked to be between 0 and 3, and `way` between 0 and 511.
    #[inline(always)]
    pub fn dccisw(&mut self, set: u16, way: u16) {
        // See comment for dcisw() about the format here
        unsafe {
            self.dccisw.write(
                ((u32::from(way) & (CBP_SW_WAY_MASK >> CBP_SW_WAY_POS)) << CBP_SW_WAY_POS)
                    | ((u32::from(set) & (CBP_SW_SET_MASK >> CBP_SW_SET_POS)) << CBP_SW_SET_POS),
            );
        }
    }

    /// Branch predictor invalidate all
    #[inline(always)]
    pub fn bpiall(&mut self) {
        unsafe {
            self.bpiall.write(0);
        }
    }
}