@@ -53,26 +53,187 @@ pub unsafe fn set_stack_watchpoint(addr: usize) {
5353 ) ;
5454 }
5555 } else {
56- let addr = ( addr & !0b11 ) | 0b01 ;
57-
58- let id = 0 ; // breakpoint 0
59- let tdata = ( 1 << 3 ) | ( 1 << 6 ) | ( 1 << 1 ) ; // bits: 0 = load, 1 = store, 6 = m-mode, 3 = u-mode
60- let tcontrol = 1 << 3 ; // M-mode trigger
61-
62- unsafe {
63- core:: arch:: asm!(
64- "
65- csrw 0x7a0, {id} // tselect
66- csrrs {tcontrol}, 0x7a5, {tcontrol} // tcontrol
67- csrrs {tdata}, 0x7a1, {tdata} // tdata1
68- csrw 0x7a2, {addr} // tdata2
69- " , id = in( reg) id,
70- addr = in( reg) addr,
71- tdata = in( reg) tdata,
72- tcontrol = in( reg) tcontrol,
73- ) ;
74- }
56+ unsafe { set_watchpoint( 0 , addr, 4 ) ; }
7557 }
7658 }
7759 }
7860}
61+
62+ #[ cfg( riscv) ]
63+ pub ( crate ) static DEBUGGER_LOCK : esp_sync:: RawMutex = esp_sync:: RawMutex :: new ( ) ;
64+
65+ #[ cfg( riscv) ]
66+ const NAPOT_MATCH : u8 = 1 ;
67+
68+ #[ cfg( riscv) ]
69+ bitfield:: bitfield! {
70+ /// Only match type (0x2) triggers are supported.
71+ #[ derive( Clone , Copy , Default ) ]
72+ pub ( crate ) struct Tdata1 ( u32 ) ;
73+
74+ /// Set this for configuring the selected trigger to fire right before a load operation with matching
75+ /// data address is executed by the CPU.
76+ pub bool , load, set_load: 0 ;
77+
78+ /// Set this for configuring the selected trigger to fire right before a store operation with matching
79+ /// data address is executed by the CPU.
80+ pub bool , store, set_store: 1 ;
81+
82+ /// Set this for configuring the selected trigger to fire right before an instruction with matching
83+ /// virtual address is executed by the CPU.
84+ pub bool , execute, set_execute: 2 ;
85+
86+ /// Set this for enabling selected trigger to operate in user mode.
87+ pub bool , u, set_u: 3 ;
88+
89+ /// Set this for enabling selected trigger to operate in machine mode.
90+ pub bool , m, set_m: 6 ;
91+
92+ /// Configures the selected trigger to perform one of the available matching operations on a
93+ /// data/instruction address. Valid options are:
94+ /// 0x0: exact byte match, i.e. address corresponding to one of the bytes in an access must match
95+ /// the value of maddress exactly.
96+ /// 0x1: NAPOT match, i.e. at least one of the bytes of an access must lie in the NAPOT region
97+ /// specified in maddress.
98+ /// Note: Writing a larger value will clip it to the largest possible value 0x1.
99+ pub u8 , _match, set_match: 10 , 7 ;
100+
101+ /// Configures the selected trigger to perform one of the available actions when firing. Valid
102+ /// options are:
103+ /// 0x0: cause breakpoint exception.
104+ /// 0x1: enter debug mode (only valid when dmode = 1)
105+ /// Note: Writing an invalid value will set this to the default value 0x0.
106+ pub u8 , action, set_action: 15 , 12 ;
107+
108+ /// This is found to be 1 if the selected trigger had fired previously. This bit is to be cleared manually.
109+ pub bool , hit, set_hit: 20 ;
110+
111+ /// 0: Both Debug and M mode can write the tdata1 and tdata2 registers at the selected tselect.
112+ /// 1: Only Debug Mode can write the tdata1 and tdata2 registers at the selected tselect. Writes from
113+ /// other modes are ignored.
114+ /// Note: Only writable from debug mode.
115+ pub bool , dmode, set_dmode: 27 ;
116+ }
117+
118+ #[ cfg( riscv) ]
119+ bitfield:: bitfield! {
120+ /// Only match type (0x2) triggers are supported.
121+ #[ derive( Clone , Copy , Default ) ]
122+ pub ( crate ) struct Tcontrol ( u32 ) ;
123+
124+ /// Current M mode trigger enable bit
125+ pub bool , mte, set_mte: 3 ;
126+
127+ /// Previous M mode trigger enable bit
128+ pub bool , mpte, set_mpte: 7 ;
129+
130+ }
131+
132+ #[ cfg( riscv) ]
133+ pub ( crate ) struct WatchPoint {
134+ tdata1 : u32 ,
135+ tdata2 : u32 ,
136+ }
137+
138+ /// Clear the watchpoint
139+ #[ cfg( riscv) ]
140+ pub ( crate ) unsafe fn clear_watchpoint ( id : u8 ) -> WatchPoint {
141+ assert ! ( id < 4 ) ;
142+
143+ // tdata1 is a WARL(write any read legal) register. We can just write 0 to it.
144+ let mut tdata1 = 0 ;
145+ let mut tdata2 = 0 ;
146+
147+ DEBUGGER_LOCK . lock ( || unsafe {
148+ core:: arch:: asm!(
149+ "
150+ csrw 0x7a0, {id} // tselect
151+ csrrw {tdata1}, 0x7a1, {tdata2} // tdata1
152+ csrr {tdata2}, 0x7a2 // tdata2
153+ " , id = in( reg) id,
154+ tdata1 = inout( reg) tdata1,
155+ tdata2 = out( reg) tdata2,
156+ ) ;
157+ } ) ;
158+
159+ WatchPoint { tdata1, tdata2 }
160+ }
161+
162+ /// Clear the watchpoint
163+ #[ cfg( riscv) ]
164+ pub ( crate ) unsafe fn restore_watchpoint ( id : u8 , watchpoint : WatchPoint ) {
165+ DEBUGGER_LOCK . lock ( || unsafe {
166+ core:: arch:: asm!(
167+ "
168+ csrw 0x7a0, {id} // tselect
169+ csrw 0x7a1, {tdata1} // tdata1
170+ csrw 0x7a2, {tdata2} // tdata2
171+ " , id = in( reg) id,
172+ tdata1 = in( reg) watchpoint. tdata1,
173+ tdata2 = in( reg) watchpoint. tdata2,
174+ ) ;
175+ } ) ;
176+ }
177+
178+ /// Clear the watchpoint
179+ #[ cfg( all( riscv, feature = "exception-handler" ) ) ]
180+ pub ( crate ) unsafe fn watchpoint_hit ( id : u8 ) -> bool {
181+ assert ! ( id < 4 ) ;
182+ let mut tdata = Tdata1 :: default ( ) ;
183+
184+ DEBUGGER_LOCK . lock ( || unsafe {
185+ core:: arch:: asm!(
186+ "
187+ csrw 0x7a0, {id} // tselect
188+ csrr {tdata}, 0x7a1 // tdata1
189+ " , id = in( reg) id,
190+ tdata = out( reg) tdata. 0 ,
191+ ) ;
192+ } ) ;
193+
194+ tdata. hit ( )
195+ }
196+
197+ /// Set watchpoint and enable triggers.
198+ #[ cfg( riscv) ]
199+ pub ( crate ) unsafe fn set_watchpoint ( id : u8 , addr : usize , len : usize ) {
200+ assert ! ( id < 4 ) ;
201+ assert ! ( len. is_power_of_two( ) ) ;
202+ assert ! ( addr. is_multiple_of( len) ) ;
203+
204+ let z = len. trailing_zeros ( ) ;
205+ let mask = {
206+ let mut mask: usize = 0 ;
207+ for i in 0 ..z {
208+ mask |= 1 << i;
209+ }
210+ mask
211+ } ;
212+
213+ let napot_encoding = { mask & !( 1 << ( z - 1 ) ) } ;
214+ let addr = ( addr & !mask) | napot_encoding;
215+
216+ let mut tdata = Tdata1 :: default ( ) ;
217+ tdata. set_m ( true ) ;
218+ tdata. set_store ( true ) ;
219+ tdata. set_match ( NAPOT_MATCH ) ;
220+ let tdata: u32 = tdata. 0 ;
221+
222+ let mut tcontrol = Tcontrol :: default ( ) ;
223+ tcontrol. set_mte ( true ) ;
224+ let tcontrol: u32 = tcontrol. 0 ;
225+
226+ DEBUGGER_LOCK . lock ( || unsafe {
227+ core:: arch:: asm!(
228+ "
229+ csrw 0x7a0, {id} // tselect
230+ csrw 0x7a5, {tcontrol} // tcontrol
231+ csrw 0x7a1, {tdata} // tdata1
232+ csrw 0x7a2, {addr} // tdata2
233+ " , id = in( reg) id,
234+ addr = in( reg) addr,
235+ tdata = in( reg) tdata,
236+ tcontrol = in( reg) tcontrol,
237+ ) ;
238+ } ) ;
239+ }
0 commit comments