1 /*
2  * SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #pragma once
8 
9 #include <stdint.h>
10 
11 #include "soc/soc_caps.h"
12 #include "soc/assist_debug_reg.h"
13 #include "soc/interrupt_reg.h"
14 #include "esp_attr.h"
15 #include "riscv/csr.h"
16 #include "riscv/interrupt.h"
17 
18 #ifdef __cplusplus
19 extern "C" {
20 #endif
21 
22 /*performance counter*/
23 #define CSR_PCER_MACHINE    0x7e0
24 #define CSR_PCMR_MACHINE    0x7e1
25 #define CSR_PCCR_MACHINE    0x7e2
26 
27 /* --------------------------------------------------- CPU Control -----------------------------------------------------
28  *
29  * ------------------------------------------------------------------------------------------------------------------ */
30 
rv_utils_wait_for_intr(void)31 FORCE_INLINE_ATTR void __attribute__((always_inline)) rv_utils_wait_for_intr(void)
32 {
33     asm volatile ("wfi\n");
34 }
35 
36 /* -------------------------------------------------- CPU Registers ----------------------------------------------------
37  *
38  * ------------------------------------------------------------------------------------------------------------------ */
39 
rv_utils_get_core_id(void)40 FORCE_INLINE_ATTR __attribute__((pure)) uint32_t rv_utils_get_core_id(void)
41 {
42 #if SOC_CPU_CORES_NUM == 1
43     return 0; // No need to check core ID on single core hardware
44 #else
45     uint32_t cpuid;
46     cpuid = RV_READ_CSR(mhartid);
47     return cpuid;
48 #endif
49 }
50 
rv_utils_get_sp(void)51 FORCE_INLINE_ATTR void *rv_utils_get_sp(void)
52 {
53     void *sp;
54     asm volatile ("mv %0, sp;" : "=r" (sp));
55     return sp;
56 }
57 
rv_utils_get_cycle_count(void)58 FORCE_INLINE_ATTR uint32_t __attribute__((always_inline)) rv_utils_get_cycle_count(void)
59 {
60     return RV_READ_CSR(CSR_PCCR_MACHINE);
61 }
62 
rv_utils_set_cycle_count(uint32_t ccount)63 FORCE_INLINE_ATTR void __attribute__((always_inline)) rv_utils_set_cycle_count(uint32_t ccount)
64 {
65     RV_WRITE_CSR(CSR_PCCR_MACHINE, ccount);
66 }
67 
68 /* ------------------------------------------------- CPU Interrupts ----------------------------------------------------
69  *
70  * ------------------------------------------------------------------------------------------------------------------ */
71 
72 // ---------------- Interrupt Descriptors ------------------
73 
74 // --------------- Interrupt Configuration -----------------
75 
rv_utils_set_mtvec(uint32_t mtvec_val)76 FORCE_INLINE_ATTR void rv_utils_set_mtvec(uint32_t mtvec_val)
77 {
78     mtvec_val |= 1; // Set MODE field to treat MTVEC as a vector base address
79     RV_WRITE_CSR(mtvec, mtvec_val);
80 }
81 
82 // ------------------ Interrupt Control --------------------
83 
rv_utils_intr_enable(uint32_t intr_mask)84 FORCE_INLINE_ATTR void rv_utils_intr_enable(uint32_t intr_mask)
85 {
86     // Disable all interrupts to make updating of the interrupt mask atomic.
87     unsigned old_mstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE);
88     esprv_intc_int_enable(intr_mask);
89     RV_SET_CSR(mstatus, old_mstatus & MSTATUS_MIE);
90 }
91 
rv_utils_intr_disable(uint32_t intr_mask)92 FORCE_INLINE_ATTR void rv_utils_intr_disable(uint32_t intr_mask)
93 {
94     // Disable all interrupts to make updating of the interrupt mask atomic.
95     unsigned old_mstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE);
96     esprv_intc_int_disable(intr_mask);
97     RV_SET_CSR(mstatus, old_mstatus & MSTATUS_MIE);
98 }
99 
rv_utils_intr_get_enabled_mask(void)100 FORCE_INLINE_ATTR uint32_t rv_utils_intr_get_enabled_mask(void)
101 {
102     return REG_READ(INTERRUPT_CORE0_CPU_INT_ENABLE_REG);
103 }
104 
rv_utils_intr_edge_ack(unsigned int intr_num)105 FORCE_INLINE_ATTR void rv_utils_intr_edge_ack(unsigned int intr_num)
106 {
107     REG_SET_BIT(INTERRUPT_CORE0_CPU_INT_CLEAR_REG, intr_num);
108 }
109 
rv_utils_intr_global_enable(void)110 FORCE_INLINE_ATTR void rv_utils_intr_global_enable(void)
111 {
112     RV_SET_CSR(mstatus, MSTATUS_MIE);
113 }
114 
rv_utils_intr_global_disable(void)115 FORCE_INLINE_ATTR void rv_utils_intr_global_disable(void)
116 {
117     RV_CLEAR_CSR(mstatus, MSTATUS_MIE);
118 }
119 
120 /* -------------------------------------------------- Memory Ports -----------------------------------------------------
121  *
122  * ------------------------------------------------------------------------------------------------------------------ */
123 
124 /* ---------------------------------------------------- Debugging ------------------------------------------------------
125  *
126  * ------------------------------------------------------------------------------------------------------------------ */
127 
128 // --------------- Breakpoints/Watchpoints -----------------
129 
rv_utils_set_breakpoint(int bp_num,uint32_t bp_addr)130 FORCE_INLINE_ATTR void rv_utils_set_breakpoint(int bp_num, uint32_t bp_addr)
131 {
132     /* The code bellow sets breakpoint which will trigger `Breakpoint` exception
133      * instead transfering control to debugger. */
134     RV_WRITE_CSR(tselect, bp_num);
135     RV_WRITE_CSR(tcontrol, TCONTROL_MPTE | TCONTROL_MTE);
136     RV_WRITE_CSR(tdata1, TDATA1_USER | TDATA1_MACHINE | TDATA1_EXECUTE);
137     RV_WRITE_CSR(tdata2, bp_addr);
138 }
139 
rv_utils_set_watchpoint(int wp_num,uint32_t wp_addr,size_t size,bool on_read,bool on_write)140 FORCE_INLINE_ATTR void rv_utils_set_watchpoint(int wp_num,
141                                                uint32_t wp_addr,
142                                                size_t size,
143                                                bool on_read,
144                                                bool on_write)
145 {
146     RV_WRITE_CSR(tselect, wp_num);
147     RV_WRITE_CSR(tcontrol, TCONTROL_MPTE | TCONTROL_MTE);
148     RV_WRITE_CSR(tdata1, TDATA1_USER                   |
149                          TDATA1_MACHINE                |
150                          ((size == 1) ? TDATA1_MATCH_EXACT : TDATA1_MATCH_NAPOT) |
151                          (on_read  ? TDATA1_LOAD  : 0) |
152                          (on_write ? TDATA1_STORE : 0));
153     /* From RISC-V Debug Specification:
154      * tdata1(mcontrol) match = 0 : Exact byte match
155      *
156      * tdata1(mcontrol) match = 1 : NAPOT (Naturally Aligned Power-Of-Two):
157      * Matches when the top M bits of any compare value match the top M bits of tdata2.
158      * M is XLEN − 1 minus the index of the least-significant bit containing 0 in tdata2.
159      * Note: Expecting that size is number power of 2 (numbers should be in the range of 1 ~ 31)
160      *
161      * Examples for understanding how to calculate match pattern to tdata2:
162      *
163      * nnnn...nnnnn 1-byte  Exact byte match
164      * nnnn...nnnn0 2-byte  NAPOT range
165      * nnnn...nnn01 4-byte  NAPOT range
166      * nnnn...nn011 8-byte  NAPOT range
167      * nnnn...n0111 16-byte NAPOT range
168      * nnnn...01111 32-byte NAPOT range
169      * ...
170      * n011...11111 2^31 byte NAPOT range
171      *  * where n are bits from original address
172      */
173     uint32_t match_pattern = (wp_addr & ~(size-1)) | ((size-1) >> 1);
174 
175     RV_WRITE_CSR(tdata2, match_pattern);
176 }
177 
rv_utils_clear_breakpoint(int bp_num)178 FORCE_INLINE_ATTR void rv_utils_clear_breakpoint(int bp_num)
179 {
180     RV_WRITE_CSR(tselect, bp_num);
181     /* tdata1 is a WARL(write any read legal) register
182      * We can just write 0 to it
183      */
184     RV_WRITE_CSR(tdata1, 0);
185 }
186 
rv_utils_clear_watchpoint(int wp_num)187 FORCE_INLINE_ATTR void rv_utils_clear_watchpoint(int wp_num)
188 {
189     /* riscv have the same registers for breakpoints and watchpoints */
190     rv_utils_clear_breakpoint(wp_num);
191 }
192 
rv_utils_is_trigger_fired(int id)193 FORCE_INLINE_ATTR bool rv_utils_is_trigger_fired(int id)
194 {
195     RV_WRITE_CSR(tselect, id);
196     return (RV_READ_CSR(tdata1) >> TDATA1_HIT_S) & 1;
197 }
198 
199 // ---------------------- Debugger -------------------------
200 
rv_utils_dbgr_is_attached(void)201 FORCE_INLINE_ATTR bool rv_utils_dbgr_is_attached(void)
202 {
203     return REG_GET_BIT(ASSIST_DEBUG_CORE_0_DEBUG_MODE_REG, ASSIST_DEBUG_CORE_0_DEBUG_MODULE_ACTIVE);
204 }
205 
rv_utils_dbgr_break(void)206 FORCE_INLINE_ATTR void rv_utils_dbgr_break(void)
207 {
208     asm volatile("ebreak\n");
209 }
210 
211 /* ------------------------------------------------------ Misc ---------------------------------------------------------
212  *
213  * ------------------------------------------------------------------------------------------------------------------ */
214 
rv_utils_compare_and_set(volatile uint32_t * addr,uint32_t compare_value,uint32_t new_value)215 FORCE_INLINE_ATTR bool rv_utils_compare_and_set(volatile uint32_t *addr, uint32_t compare_value, uint32_t new_value)
216 {
217     // ESP32C6 starts to support atomic CAS instructions, but it is still a single core target, no need to implement
218     // through lr and sc instructions for now
219     // For an RV target has no atomic CAS instruction, we can achieve atomicity by disabling interrupts
220     unsigned old_mstatus;
221     old_mstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE);
222     // Compare and set
223     uint32_t old_value;
224     old_value = *addr;
225     if (old_value == compare_value) {
226         *addr = new_value;
227     }
228     // Restore interrupts
229     RV_SET_CSR(mstatus, old_mstatus & MSTATUS_MIE);
230 
231     return (old_value == compare_value);
232 }
233 
234 #ifdef __cplusplus
235 }
236 #endif
237