andes_riscv/
l1c.rs

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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
//! L1C, level 1 cache

use cctl_cmds::L1D_VA_LOCK;

use crate::register;
use crate::riscv;

// cctl_cmd
pub mod cctl_cmds {
    pub const L1D_VA_INVAL: u8 = 0;
    pub const L1D_VA_WB: u8 = 1;
    pub const L1D_VA_WBINVAL: u8 = 2;
    pub const L1D_VA_LOCK: u8 = 3;
    pub const L1D_VA_UNLOCK: u8 = 4;
    pub const L1D_WBINVAL_ALL: u8 = 6;
    pub const L1D_WB_ALL: u8 = 7;

    pub const L1I_VA_INVAL: u8 = 8;
    pub const L1I_VA_LOCK: u8 = 11;
    pub const L1I_VA_UNLOCK: u8 = 12;

    pub const L1D_IX_INVAL: u8 = 16;
    pub const L1D_IX_WB: u8 = 17;
    pub const L1D_IX_WBINVAL: u8 = 18;

    pub const L1D_IX_RTAG: u8 = 19;
    pub const L1D_IX_RDATA: u8 = 20;
    pub const L1D_IX_WTAG: u8 = 21;
    pub const L1D_IX_WDATA: u8 = 22;

    pub const L1D_INVAL_ALL: u8 = 23;

    pub const L1I_IX_INVAL: u8 = 24;
    pub const L1I_IX_RTAG: u8 = 27;
    pub const L1I_IX_RDATA: u8 = 28;
    pub const L1I_IX_WTAG: u8 = 29;
    pub const L1I_IX_WDATA: u8 = 30;
}

#[inline(always)]
pub fn dc_is_enabled() -> bool {
    register::mcache_ctl().read().dc_en()
}

#[inline(always)]
pub fn ic_is_enabled() -> bool {
    register::mcache_ctl().read().ic_en()
}

pub unsafe fn dc_enable() {
    if !dc_is_enabled() {
        register::mcache_ctl().modify(|w| w.set_dc_warnd(0b11));
        register::mcache_ctl().modify(|w| {
            // TODO: set dc_warnd
            w.set_dpref_en(true);
            w.set_dc_en(true);
        });
    }
}

pub unsafe fn dc_disable() {
    if dc_is_enabled() {
        register::mcache_ctl().modify(|w| w.set_dc_en(false));
    }
}

pub unsafe fn ic_enable() {
    if !ic_is_enabled() {
        register::mcache_ctl().modify(|w| {
            w.set_ic_en(true);
            w.set_ipref_en(true);
            w.set_cctl_suen(true);
        });
    }
}

pub unsafe fn ic_disable() {
    if ic_is_enabled() {
        register::mcache_ctl().modify(|w| w.set_ic_en(false));
    }
}

pub unsafe fn dc_invalidate_all() {
    register::mcctlcommand().write_value(cctl_cmds::L1D_INVAL_ALL as u32);
}

pub unsafe fn dc_writeback_all() {
    register::mcctlcommand().write_value(cctl_cmds::L1D_WB_ALL as u32);
}

pub unsafe fn dc_flush_all() {
    register::mcctlcommand().write_value(cctl_cmds::L1D_WBINVAL_ALL as u32);
}

#[inline(always)]
fn cctl_get_address() -> u32 {
    register::mcctlbeginaddr().read()
}

// HPM_L1C_CACHELINE_SIZE
fn cacheline_size() -> u32 {
    let dsz = register::mdcm_cfg().read().dsz();
    match dsz {
        0 => 0,
        1 => 8,
        2 => 16,
        3 => 32,
        4 => 64,
        5 => 128,
        _ => 0,
    }
}

pub unsafe fn l1c_op(opcode: u8, address: u32, size: u32) {
    let mstatus = riscv::register::mstatus::read();
    if mstatus.mie() {
        riscv::register::mstatus::clear_mie();
    }

    let ver = register::mmsc_cfg().read().vcctl();

    if ver != 0 {
        register::mcctlbeginaddr().write_value(address);
        let mut next_address = address;

        while next_address < address + size && next_address >= address {
            register::mcctlcommand().write_value(opcode as u32);
            next_address = cctl_get_address();
        }
    } else {
        let cl_size = cacheline_size();
        if cl_size == 0 {
            return;
        }
        let mut i = 0;
        while i < size {
            register::mcctlbeginaddr().write_value(address + i);
            register::mcctlcommand().write_value(opcode as u32);
            i += cl_size;
        }
    }

    if mstatus.mie() {
        riscv::register::mstatus::set_mie();
    }
}

fn check_addr_size(address: u32, size: u32) {
    let cl_sz = cacheline_size();

    assert!(
        address % cl_sz == 0,
        "address must be aligned to cacheline size"
    );
    assert!(size % cl_sz == 0, "size must be multiple of cacheline size");
}

pub unsafe fn dc_fill_lock(address: u32, size: u32) {
    check_addr_size(address, size);
    l1c_op(L1D_VA_LOCK, address, size);
}

pub unsafe fn dc_invalidate(address: u32, size: u32) {
    check_addr_size(address, size);
    l1c_op(cctl_cmds::L1D_VA_INVAL, address, size);
}

pub unsafe fn dc_writeback(address: u32, size: u32) {
    check_addr_size(address, size);
    l1c_op(cctl_cmds::L1D_VA_WB, address, size);
}

pub unsafe fn dc_flush(address: u32, size: u32) {
    check_addr_size(address, size);
    l1c_op(cctl_cmds::L1D_VA_WBINVAL, address, size);
}

pub unsafe fn ic_invalidate(address: u32, size: u32) {
    check_addr_size(address, size);
    l1c_op(cctl_cmds::L1I_VA_INVAL, address, size);
}

pub unsafe fn ic_fill_lock(address: u32, size: u32) {
    check_addr_size(address, size);
    l1c_op(cctl_cmds::L1I_VA_LOCK, address, size);
}

#[inline]
pub fn cacheline_align_down(addr: u32) -> u32 {
    addr & !(cacheline_size() - 1)
}

#[inline]
pub fn cacheline_align_up(addr: u32) -> u32 {
    cacheline_align_down(addr + cacheline_size() - 1)
}