libmicroemu 0.2.0
ARM Microcontroller Emulator Library
Loading...
Searching...
No Matches
exceptions_ops.h
1#pragma once
2
3#include "libmicroemu/exception_states.h"
4#include "libmicroemu/exception_type.h"
7#include "libmicroemu/internal/utils/bit_manip.h"
8#include "libmicroemu/logger.h"
9#include "libmicroemu/register_details.h"
10#include "libmicroemu/register_id.h"
12#include <assert.h>
13#include <cstddef>
14#include <cstdint>
15
16namespace libmicroemu::internal {
17
19 u32 return_adr; // return address of the exception
20};
21
22enum class ProcessorMode {
23 kThread = 0U, // Thread mode
24 kHandler = 1U, // Handler mode
25};
26
27enum class ExecutionInstant {
28 kPreFetch = 0U, // Before fetching the instruction
29 kPostFetch = 1U, // After fetching the instruction
30 kPostExecution = 2U // After executing the instruction
31};
32
34public:
35 static constexpr ExecutionInstant kInstant = ExecutionInstant::kPreFetch;
36
37 // there are no real asynchronous exceptions in the emulator. But for the sake of
38 // completeness, we can consider them as asynchronous
39 static constexpr bool isSynchronous = false;
40};
41
43public:
44 static constexpr ExecutionInstant kInstant = ExecutionInstant::kPostFetch;
45
46 static constexpr bool isSynchronous = true;
47};
48
50public:
51 static constexpr ExecutionInstant kInstant = ExecutionInstant::kPostExecution;
52 static constexpr bool isSynchronous = true;
53};
54
55template <typename TCpuAccessor, typename TPcOps, typename TLogger = NullLogger>
56class ExceptionsOps {
57public:
58 using SId = SpecialRegisterId;
59 using Pc = TPcOps;
60
61 ExceptionsOps() = delete;
62 ~ExceptionsOps() = delete;
63 ExceptionsOps &operator=(const ExceptionsOps &r_src) = delete;
64 ExceptionsOps(ExceptionsOps &&r_src) = delete;
65 ExceptionsOps &operator=(ExceptionsOps &&r_src) = delete;
66 ExceptionsOps(const ExceptionsOps &r_src) = delete;
67
68 static void SetProcessorMode(TCpuAccessor &cpua, const ProcessorMode &mode) {
69 auto sys_ctrl = cpua.template ReadSpecialRegister<SId::kSysCtrl>();
70
71 sys_ctrl &= ~SysCtrlRegister::kExecModeMsk;
72 sys_ctrl |= mode == ProcessorMode::kHandler ? SysCtrlRegister::kExecModeHandler
73 : SysCtrlRegister::kExecModeThread;
74 cpua.template WriteSpecialRegister<SId::kSysCtrl>(sys_ctrl);
75 }
76
77 static void LogImportantRegisters(TCpuAccessor &cpua, const char *preamble,
78 const ExceptionType &exception_type) {
79#if IS_LOGLEVEL_TRACE_ENABLED == true
80 const auto is_handler_mode = Predicates::IsHandlerMode(cpua);
81 const auto mode_str = is_handler_mode ? "Handler" : "Thread";
82
83 auto &exception_states = cpua.GetExceptionStates();
84 auto &selected_exception = exception_states.exception[static_cast<u32>(exception_type) - 1U];
85
86 auto apsr = cpua.template ReadSpecialRegister<SId::kApsr>();
87 auto ipsr = cpua.template ReadSpecialRegister<SId::kIpsr>();
88 auto epsr = cpua.template ReadSpecialRegister<SId::kEpsr>();
89 auto xpsr = cpua.template ReadSpecialRegister<SId::kXpsr>();
90 auto sp = cpua.template ReadRegister<RegisterId::kSp>();
91 auto stack_type = Predicates::IsMainStack(cpua) ? "Main" : "Process";
92 LOG_TRACE(TLogger,
93 "%s: "
94 "type_id = %d, "
95 "priority = %d, "
96 "CurrentMode = \"%s\", "
97 "APSR = 0x%08X, "
98 "IPSR = 0x%08X, "
99 "EPSR = 0x%08X, "
100 "XPSR = 0x%08X, "
101 "SP = 0x%08X (%s)",
102 preamble, static_cast<uint32_t>(exception_type), selected_exception.GetPriority(),
103 mode_str, apsr, ipsr, epsr, xpsr, sp, stack_type);
104#else
105 static_cast<void>(cpua);
106 static_cast<void>(preamble);
107 static_cast<void>(exception_type);
108#endif
109 }
110 static void InitDefaultExceptionStates(TCpuAccessor &cpua) {
111 auto &exception_states = cpua.GetExceptionStates();
112 exception_states.pending_exceptions = 0U;
113 auto &exceptions = exception_states.exception;
114 for (u32 i = 0U; i < CountExceptions(); ++i) {
115 auto e_type = static_cast<ExceptionType>(i + 1U);
116 switch (e_type) {
118 exceptions[i].SetPriority(-3);
119 break;
120 }
121 case ExceptionType::kNMI: {
122 exceptions[i].SetPriority(-2);
123 break;
124 }
126 exceptions[i].SetPriority(-1);
127 break;
128 }
129 // For testing purposes
130 // case ExceptionType::kSysTick: {
131 // exceptions[i].priority = 1;
132 // break;
133 // }
134 default: {
135 exceptions[i].SetPriority(0);
136 break;
137 }
138 }
139 exceptions[i].ClearFlags();
140 }
141 }
142
143 template <typename ExcInstant, typename TBus>
144 static Result<void> ExceptionEntry(TCpuAccessor &cpua, TBus &bus,
145 const ExceptionType &exception_type,
146 const ExceptionContext &context) {
147#if IS_LOGLEVEL_TRACE_ENABLED == true
148 if (constexpr auto instant = ExcInstant::kInstant; instant == ExecutionInstant::kPreFetch) {
149 LogImportantRegisters(cpua, "[BEGIN] ExceptionEntry (PreFetch)", exception_type);
150 } else if (instant == ExecutionInstant::kPostFetch) {
151 LogImportantRegisters(cpua, "[BEGIN] ExceptionEntry (PostFetch)", exception_type);
152 } else {
153 LogImportantRegisters(cpua, "[BEGIN] ExceptionEntry (PostExec)", exception_type);
154 }
155#endif
156
157 TRY(void, PushStack<ExcInstant>(cpua, bus, exception_type, context));
158
159 TRY(void, ExceptionTaken(cpua, bus, exception_type));
160
161 LOG_TRACE(TLogger, "[END] ExceptionEntry");
162 return Ok();
163 }
164
165 template <typename ExcInstant, typename TBus>
166 static Result<void> PushStack(TCpuAccessor &cpua, TBus &bus, const ExceptionType &exception_type,
167 const ExceptionContext &context) {
168 // Taken from: Armv7-M Architecture Reference Manual Issue E.e p532
169
170 static_cast<void>(exception_type);
171
172 u32 framesize{0U};
173 u32 forcealign{0U};
174
175 // if HaveFPExt() && CONTROL.FPCA == '1' then
176 if (false) {
177 // framesize = 0x68;
178 // forcealign = '1';
179 } else {
180 framesize = 0x20U;
181 auto ccr = cpua.template ReadSpecialRegister<SId::kCcr>();
182 forcealign = (ccr & CcrRegister::kStkAlignMsk) >> CcrRegister::kStkAlignPos;
183 }
184
185 auto spmask = ~static_cast<u32>(forcealign << 2U);
186
187 u32 frameptralign{0U};
188 u32 frameptr{0U};
189
190 const bool is_thread_mode = Predicates::IsThreadMode(cpua);
191 const bool is_process_stack = Predicates::IsProcessStack(cpua);
192
193 // if CONTROL.SPSEL == '1' && CurrentMode == Mode_Thread then
194 if (is_process_stack && is_thread_mode) {
195 auto sp_process = cpua.template ReadSpecialRegister<SId::kSpProcess>();
196
197 frameptralign = ((sp_process & 0x4U) >> 2U) & forcealign;
198
199 sp_process = (sp_process - framesize) & spmask;
200 LOG_TRACE(TLogger, "Setting processes stack pointer to = 0x%08X", sp_process);
201 cpua.template WriteSpecialRegister<SId::kSpProcess>(sp_process);
202
203 // frameptralign = SP_process<2> AND forcealign;
204 // SP_process = (SP_process - framesize) AND spmask;
205 // frameptr = SP_process;
206
207 frameptr = sp_process;
208 } else {
209 auto sp_main = cpua.template ReadSpecialRegister<SId::kSpMain>();
210
211 frameptralign = ((sp_main & 0x4U) >> 2U) & forcealign;
212
213 sp_main = (sp_main - framesize) & spmask;
214 LOG_TRACE(TLogger, "Setting main stack pointer to = 0x%08X", sp_main);
215 cpua.template WriteSpecialRegister<SId::kSpMain>(sp_main);
216
217 frameptr = sp_main;
218 }
219
220 /* only the stack locations, not the store order, are architected */
221
222 // MemA[frameptr,4 ] = R[0];
223 const auto r0 = cpua.template ReadRegister<RegisterId::kR0>();
224 TRY(void, bus.template WriteOrRaise<u32>(cpua, frameptr, r0, BusExceptionType::kRaiseUnstkerr));
225
226 // MemA[frameptr+0x4,4 ] = R[1];
227 const auto r1 = cpua.template ReadRegister<RegisterId::kR1>();
228 TRY(void, bus.template WriteOrRaise<u32>(cpua, frameptr + 0x4U, r1,
229 BusExceptionType::kRaiseUnstkerr));
230
231 // MemA[frameptr+0x8,4 ] = R[2];
232 const auto r2 = cpua.template ReadRegister<RegisterId::kR2>();
233 TRY(void, bus.template WriteOrRaise<u32>(cpua, frameptr + 0x8U, r2,
234 BusExceptionType::kRaiseUnstkerr));
235
236 // MemA[frameptr+0xC,4 ] = R[3];
237 const auto r3 = cpua.template ReadRegister<RegisterId::kR3>();
238 TRY(void, bus.template WriteOrRaise<u32>(cpua, frameptr + 0xCU, r3,
239 BusExceptionType::kRaiseUnstkerr));
240
241 // MemA[frameptr+0x10,4] = R[12];
242 const auto r12 = cpua.template ReadRegister<RegisterId::kR12>();
243 TRY(void, bus.template WriteOrRaise<u32>(cpua, frameptr + 0x10U, r12,
244 BusExceptionType::kRaiseUnstkerr));
245
246 // MemA[frameptr+0x14,4] = LR;
247 const auto lr = cpua.template ReadRegister<RegisterId::kLr>();
248 TRY(void, bus.template WriteOrRaise<u32>(cpua, frameptr + 0x14U, lr,
249 BusExceptionType::kRaiseUnstkerr));
250
251 // MemA[frameptr+0x18,4] = ReturnAddress(ExceptionType);
252 const auto return_address = ReturnAddress<ExcInstant>(cpua, exception_type, context);
253
254 TRY(void, bus.template WriteOrRaise<u32>(cpua, frameptr + 0x18U, return_address,
255 BusExceptionType::kRaiseUnstkerr));
256
257 // MemA[frameptr+0x1C,4] = (XPSR<31:10>:frameptralign:XPSR<8:0>);
258 // //see ReturnAddress() in-line note for information on XPSR.IT bits
259 const auto xpsr = cpua.template ReadSpecialRegister<SId::kXpsr>();
260 const auto xpsr_adapt = (xpsr & Bm32::GenerateBitMask<8U, 0U>()) | (frameptralign << 9U) |
262
263 TRY(void, bus.template WriteOrRaise<u32>(cpua, frameptr + 0x1CU, xpsr_adapt,
264 BusExceptionType::kRaiseUnstkerr));
265
266 LOG_TRACE(TLogger,
267 "Pushed R0 = 0x%08X, "
268 "R1 = 0x%08X, "
269 "R2 = 0x%08X, "
270 "R3 = 0x%08X, "
271 "R12 = 0x%08X, "
272 "LR = 0x%08X, "
273 "ReturnAddress = 0x%08X, "
274 "XPSR = 0x%08X",
275 r0, r1, r2, r3, r12, lr, return_address, xpsr_adapt);
276
277 // if HaveFPExt() && CONTROL.FPCA == '1' then
278 if (false) {
279 // if FPCCR.LSPEN == '0' then
280 // CheckVFPEnabled();
281 // for i = 0 to 15
282 // MemA[frameptr+0x20+(4*i),4] = S[i];
283 // MemA[frameptr+0x60,4] = FPSCR;
284 // for i = 0 to 15
285 // S[i] = bits(32) UNKNOWN;
286 // FPSCR = bits(32) UNKNOWN;
287 // else
288 // UpdateFPCCR(frameptr);
289 }
290
291 // if HaveFPExt() then
292 if (false) {
293 // if CurrentMode==Mode_Handler then
294 // LR = Ones(27):NOT(CONTROL.FPCA):'0001'; else
295 // LR = Ones(27):NOT(CONTROL.FPCA):'1':CONTROL.SPSEL:'01';
296 } else {
297 const auto is_handler_mode = Predicates::IsHandlerMode(cpua);
298 if (is_handler_mode) {
299 // LR = Ones(28):'0001';
300 const auto lr = Bm32::GenerateBitMask<31U, 4U>() | 0b0001U;
301 LOG_TRACE(TLogger, "Setting LR = 0x%08X (currently in Handler mode)", lr);
302 cpua.template WriteRegister<RegisterId::kLr>(lr);
303 } else { // Thread mode
304 // LR = Ones(29):CONTROL.SPSEL:'01';
305 const auto sctrl = cpua.template ReadSpecialRegister<SId::kSysCtrl>();
306 const auto spsel =
307 (sctrl & SysCtrlRegister::kControlSpSelMsk) >> SysCtrlRegister::kControlSpSelPos;
308 const auto lr = Bm32::GenerateBitMask<31U, 3U>() | (spsel << 2U) | 0b01U;
309
310 LOG_TRACE(TLogger, "Setting LR = 0x%08X (currently in Thread mode)", lr);
311 cpua.template WriteRegister<RegisterId::kLr>(lr);
312 }
313 }
314 return Ok();
315 }
316
317 // Call before the emulator fetches an instruction
318 // This is typically the point where external exceptions are taken.
319 // The return address is the address of the instruction which will be fetched next.
320 template <typename ExcInstant,
321 typename std::enable_if_t<ExcInstant::kInstant == ExecutionInstant::kPreFetch, int> = 0>
322 static u32 ReturnAddress(TCpuAccessor &cpua, const ExceptionType &exception_type,
323 const ExceptionContext &context) {
324 static_cast<void>(cpua);
325 // Taken from: Armv7-M Architecture Reference Manual Issue E.e p534
326 // bits(32) ReturnAddress(integer ExceptionType)
327 // // Returns the following values based on the exception cause
328 // // NOTE: ReturnAddress() is always halfword aligned, meaning bit<0> is always zero
329 // xPSR.IT bits saved to the stack are consistent with ReturnAddress()
330
331 switch (exception_type) {
336 return context.return_adr;
337 }
338
339 default: {
340 if (static_cast<u32>(exception_type) >= 16U) {
341 return context.return_adr;
342 }
343 assert(false &&
344 "Return address calculation of these exceptions should not be called at this point");
345 return context.return_adr;
346 }
347 }
348 return 0x0U; // should not happen
349 }
350
351 template <typename ExcInstant, typename std::enable_if_t<
352 ExcInstant::kInstant == ExecutionInstant::kPostFetch, int> = 0>
353 static u32 ReturnAddress(TCpuAccessor &cpua, const ExceptionType &exception_type,
354 const ExceptionContext &context) {
355 static_cast<void>(cpua);
356 static_cast<void>(context);
357 // Taken from: Armv7-M Architecture Reference Manual Issue E.e p534
358 // bits(32) ReturnAddress(integer ExceptionType)
359 // // Returns the following values based on the exception cause
360 // // NOTE: ReturnAddress() is always halfword aligned, meaning bit<0> is always zero
361 // xPSR.IT bits saved to the stack are consistent with ReturnAddress()
362
363 switch (exception_type) {
365 case ExceptionType::kUsageFault: // Invalid state
366 case ExceptionType::kBusFault: // instruction fetch from a location that does not allow that
367 {
368 return context.return_adr;
369 }
370
371 default: {
372 assert(false &&
373 "Return address calculation of these exceptions should not be called at this point");
374 return context.return_adr;
375 }
376 }
377 return 0x0U; // should not happen
378 }
379
380 // Call after the emulator executes an instruction
381 // This is typically the point where internal exceptions are taken.
382 // The return address is the address of the instruction which was executed.
383 template <
384 typename ExcInstant,
385 typename std::enable_if_t<ExcInstant::kInstant == ExecutionInstant::kPostExecution, int> = 0>
386 static u32 ReturnAddress(TCpuAccessor &cpua, const ExceptionType &exception_type,
387 const ExceptionContext &context) {
388 static_cast<void>(cpua);
389 // Taken from: Armv7-M Architecture Reference Manual Issue E.e p534
390 // bits(32) ReturnAddress(integer ExceptionType)
391 // // Returns the following values based on the exception cause
392 // // NOTE: ReturnAddress() is always halfword aligned, meaning bit<0> is always zero
393 // xPSR.IT bits saved to the stack are consistent with ReturnAddress()
394
395 switch (exception_type) {
401 return context.return_adr;
402 }
403 default: {
404 assert(false &&
405 "Return address calculation of these exceptions should not be called at this point");
406 }
407 }
408 return 0x0U; // should not happen
409 }
410
411 template <typename TBus>
412 static Result<void> ExceptionTaken(TCpuAccessor &cpua, TBus &bus,
413 const ExceptionType &exception_type) {
414 // Taken from: Armv7-M Architecture Reference Manual Issue E.e p533
415 // ExceptionTaken(integer ExceptionNumber)
416
417 // bit tbit;
418 // bits(32) tmp;
419 // for i = 0 to 3
420 // R[i] = bits(32) UNKNOWN;
421 // R[12] = bits(32) UNKNOWN;
422
423 // bits(32) VectorTable = VTOR<31:7>:'0000000';
424 const auto vector_table = cpua.template ReadSpecialRegister<SId::kVtor>() << 7U;
425
426 // tmp = MemA[VectorTable+4*ExceptionNumber,4];
427 TRY_ASSIGN(tmp, void,
428 bus.template Read<u32>(cpua, vector_table + 4U * static_cast<u32>(exception_type)));
429
430 // BranchTo(tmp AND 0xFFFFFFFE<31:0>);
431 const auto exception_address = tmp & 0xFFFFFFFEU;
432 LOG_TRACE(TLogger, "Branching to exception address = 0x%08X", exception_address);
433 Pc::BranchTo(cpua, exception_address);
434
435 const auto tbit = tmp & 0x1U; // tbit = tmp<0>;
436
437 SetProcessorMode(cpua, ProcessorMode::kHandler);
438
439 // APSR = bits(32) UNKNOWN; // Flags UNPREDICTABLE due to other activations
440
441 auto ipsr = cpua.template ReadSpecialRegister<SId::kIpsr>();
442
443 // IPSR SECTION
444 // ------------
445 const auto exception_number = static_cast<u32>(exception_type);
446 ipsr &= ~static_cast<u32>(IpsrRegister::kExceptionNumberMsk);
447 ipsr |= exception_number; // ExceptionNumber set in IPSR
448 cpua.template WriteSpecialRegister<SId::kIpsr>(ipsr);
449
450 // EPSR SECTION
451 // ------------
452 auto epsr = cpua.template ReadSpecialRegister<SId::kEpsr>();
453 epsr &= ~static_cast<u32>(EpsrRegister::kTMsk); // clear t bit
454 epsr |= tbit << EpsrRegister::kTPos; // T-bit set from vector
455 epsr &= ~static_cast<u32>(EpsrRegister::kItMsk); // IT/ICI bits cleared
456 cpua.template WriteSpecialRegister<SId::kEpsr>(epsr);
457
458 auto sys_ctrl = cpua.template ReadSpecialRegister<SpecialRegisterId::kSysCtrl>();
459 /* PRIMASK, FAULTMASK, BASEPRI unchanged on exception entry */
460 sys_ctrl = cpua.template ReadSpecialRegister<SId::kSysCtrl>();
461
462 // CONTROL.FPCA = '0'; // Mark Floating-point inactive
463
464 // current Stack is Main, CONTROL.nPRIV unchanged
465 sys_ctrl &= ~static_cast<u32>(SysCtrlRegister::kControlSpSelMsk);
466 cpua.template WriteSpecialRegister<SId::kSysCtrl>(sys_ctrl);
467
468 /* CONTROL.nPRIV unchanged */
469
470 SetExceptionActive(cpua, exception_type);
471
472 // SCS_UpdateStatusRegs(); // update SCS registers as appropriate
473 // ClearExclusiveLocal(ProcessorID());
474 // SetEventRegister(); // see WFE instruction for more details
475 // InstructionSynchronizationBarrier('1111');
476 return Ok();
477 }
478
479 static constexpr bool IsExceptionSynchronous() {
480 // every exception is synchronous due to the nature of the emulator
481 return true;
482 }
483
484 template <typename TBus>
485 static Result<void> ExceptionReturn(TCpuAccessor &cpua, TBus &bus, u32 exc_return) {
486 // Taken from: Armv7-M Architecture Reference Manual Issue E.e p541
487 // ExceptionReturn(bits(28) EXC_RETURN)
488
489 LOG_TRACE(TLogger, "[BEGIN] ExceptionReturn: exc_return = 0x%08X", exc_return);
490
491 assert((Predicates::IsHandlerMode(cpua) == true) &&
492 "ExceptionReturn should only be called in Handler mode");
493
494 // if HaveFPExt() then
495 if (false) {
496 // if !IsOnes(EXC_RETURN<27:5>) then UNPREDICTABLE;
497 } else {
498 // if !IsOnes(EXC_RETURN<27:4>) then UNPREDICTABLE;
499 if ((exc_return & 0x0FFFFFF0U) != 0x0FFFFFF0U) {
501 }
502 }
503
504 // integer ReturningExceptionNumber = UInt(IPSR<8:0>);
505 auto ret_exception_n =
506 cpua.template ReadSpecialRegister<SId::kIpsr>() & IpsrRegister::kExceptionNumberMsk;
507
508 // integer NestedActivation;
509 // used for Handler => Thread check when value == 1
510
511 // NestedActivation = ExceptionActiveBitCount(); // Number of active exceptions
512
513 u32 frameptr{0U};
514
515 // if ExceptionActive[ReturningExceptionNumber] == '0' then
516 if (false) {
517 // DeActivate(ReturningExceptionNumber);
518 // UFSR.INVPC = '1';
519 // LR = '1111':EXC_RETURN;
520 // ExceptionTaken(UsageFault); // returning from an inactive handler
521 // return;
522 } else {
523
524 switch (exc_return & 0xFU) {
525 case 0b0001U: { // return to Handler
526 frameptr = cpua.template ReadSpecialRegister<SId::kSpMain>();
527 SetProcessorMode(cpua, ProcessorMode::kHandler);
528
529 auto sys_ctrl = cpua.template ReadSpecialRegister<SpecialRegisterId::kSysCtrl>();
530
531 // CONTROL.SPSEL = '0';
532 sys_ctrl &= ~static_cast<u32>(SysCtrlRegister::kControlSpSelMsk);
533
534 cpua.template WriteSpecialRegister<SpecialRegisterId::kSysCtrl>(sys_ctrl);
535 break;
536 }
537
538 case 0b1001U: // returning to Thread using Main stack
539 // if NestedActivation != 1 && CCR.NONBASETHRDENA == '0' then
540 if (false) {
541 // DeActivate(ReturningExceptionNumber);
542 // UFSR.INVPC = '1';
543 // LR = '1111':EXC_RETURN;
544 // ExceptionTaken(UsageFault); // return to Thread exception mismatch
545 // return;
546 } else {
547 frameptr = cpua.template ReadSpecialRegister<SId::kSpMain>();
548 SetProcessorMode(cpua, ProcessorMode::kThread);
549
550 auto sys_ctrl = cpua.template ReadSpecialRegister<SpecialRegisterId::kSysCtrl>();
551
552 // CONTROL.SPSEL = '0';
553 sys_ctrl &= ~static_cast<u32>(SysCtrlRegister::kControlSpSelMsk);
554
555 cpua.template WriteSpecialRegister<SpecialRegisterId::kSysCtrl>(sys_ctrl);
556 }
557 break;
558 case 0b1101U: // returning to Thread using Process stack
559 // if NestedActivation != 1 && CCR.NONBASETHRDENA == '0' then
560 if (false) {
561 // DeActivate(ReturningExceptionNumber);
562 // UFSR.INVPC = '1';
563 // LR = '1111':EXC_RETURN;
564 // ExceptionTaken(UsageFault); // return to Thread exception mismatch
565 // return;
566 } else {
567 frameptr = cpua.template ReadSpecialRegister<SId::kSpProcess>();
568 SetProcessorMode(cpua, ProcessorMode::kThread);
569
570 auto sys_ctrl = cpua.template ReadSpecialRegister<SpecialRegisterId::kSysCtrl>();
571
572 // CONTROL.SPSEL = '1';
573 sys_ctrl |= static_cast<u32>(SysCtrlRegister::kControlSpSelMsk);
574
575 cpua.template WriteSpecialRegister<SpecialRegisterId::kSysCtrl>(sys_ctrl);
576 }
577 break;
578 default:
579 assert(false && "Not implemented");
580 // DeActivate(ReturningExceptionNumber);
581 // UFSR.INVPC = '1';
582 // LR = '1111':EXC_RETURN;
583 // ExceptionTaken(UsageFault); // illegal EXC_RETURN
584 // return;
585 return Err(StatusCode::kNotImplemented);
586 break;
587 }
588
589 ClearExceptionActive(cpua, static_cast<ExceptionType>(ret_exception_n));
590
591 // PopStack(frameptr, EXC_RETURN);
592 TRY(void, PopStack(cpua, bus, frameptr, exc_return));
593
594 const auto ipsr_8_0 =
595 cpua.template ReadSpecialRegister<SId::kIpsr>() & IpsrRegister::kExceptionNumberMsk;
596
597 const auto is_handler_mode = Predicates::IsHandlerMode(cpua);
598
599 if (is_handler_mode && ipsr_8_0 == 0U) {
600 // UFSR.INVPC = '1';
601 // PushStack(UsageFault); // to negate PopStack()
602 // LR = '1111':EXC_RETURN;
603 // ExceptionTaken(UsageFault); // return IPSR is inconsistent
604 // return;
605 LOG_ERROR(TLogger, "Returning to Handler mode with IPSR inconsistent");
606 return Err(StatusCode::kUsageFault);
607 }
608
609 const auto is_thread_mode = Predicates::IsThreadMode(cpua);
610 if (is_thread_mode && ipsr_8_0 != 0U) {
611 // UFSR.INVPC = '1';
612 // PushStack(UsageFault); // to negate PopStack()
613 // LR = '1111':EXC_RETURN;
614 // ExceptionTaken(UsageFault); // return IPSR is inconsistent
615 // return;
616 LOG_ERROR(TLogger, "Returning to Thread mode with IPSR inconsistent");
617 return Err(StatusCode::kUsageFault);
618 }
619
620 // ClearExclusiveLocal(ProcessorID());
621 // SetEventRegister(); // see WFE instruction for more details
622 // InstructionSynchronizationBarrier('1111');
623
624 // if CurrentMode==Mode_Thread && NestedActivation == 0 && SCR.SLEEPONEXIT == '1' then
625 // SleepOnExit(); // IMPLEMENTATION DEFINED
626
627#if IS_LOGLEVEL_TRACE_ENABLED == true
628 LogImportantRegisters(cpua, "[END] ExceptionReturn",
629 static_cast<ExceptionType>(ret_exception_n));
630#else
631 static_cast<void>(ret_exception_n);
632#endif
633
634 return Ok();
635 }
636 }
637 template <typename TBus>
638 static Result<void> PopStack(TCpuAccessor &cpua, TBus &bus, u32 frameptr, u32 exc_return) {
639 static_cast<void>(bus);
640
641 // Taken from: Armv7-M Architecture Reference Manual Issue E.e p542
642 // PopStack(bits(32) frameptr, bits(28) EXC_RETURN)
643
644 /* only stack locations, not the load order, are architected */
645
646 LOG_TRACE(TLogger, "Popping stack from 0x%08X", frameptr);
647 u32 forcealign{0U};
648 u32 framesize{0U};
649
650 // if HaveFPExt() && EXC_RETURN<4> == '0' then
651 if (false) {
652 // framesize = 0x68;
653 // forcealign = '1';
654 } else {
655 framesize = 0x20U;
656
657 auto ccr = cpua.template ReadSpecialRegister<SId::kCcr>();
658 forcealign = (ccr & CcrRegister::kStkAlignMsk) >> CcrRegister::kStkAlignPos;
659 }
660
661 // R[0] = MemA[frameptr,4];
662 TRY_ASSIGN(r0, void,
663 (bus.template ReadOrRaise<u32>(cpua, frameptr, BusExceptionType::kRaiseStkerr)));
664
665 LOG_TRACE(TLogger, " R0 ADR = 0x%08X", frameptr);
666 cpua.template WriteRegister<RegisterId::kR0>(r0);
667
668 // R[1] = MemA[frameptr+0x4,4];
670 r1, void,
671 (bus.template ReadOrRaise<u32>(cpua, frameptr + 0x4U, BusExceptionType::kRaiseStkerr)));
672 cpua.template WriteRegister<RegisterId::kR1>(r1);
673
674 // R[2] = MemA[frameptr+0x8,4];
676 r2, void,
677 (bus.template ReadOrRaise<u32>(cpua, frameptr + 0x8U, BusExceptionType::kRaiseStkerr)));
678 cpua.template WriteRegister<RegisterId::kR2>(r2);
679
680 // R[3] = MemA[frameptr+0xC,4];
682 r3, void,
683 (bus.template ReadOrRaise<u32>(cpua, frameptr + 0xCU, BusExceptionType::kRaiseStkerr)));
684 cpua.template WriteRegister<RegisterId::kR3>(r3);
685
686 // R[12] = MemA[frameptr+0x10,4];
688 r12, void,
689 (bus.template ReadOrRaise<u32>(cpua, frameptr + 0x10U, BusExceptionType::kRaiseStkerr)));
690 cpua.template WriteRegister<RegisterId::kR12>(r12);
691
692 // LR = MemA[frameptr+0x14,4];
694 lr, void,
695 (bus.template ReadOrRaise<u32>(cpua, frameptr + 0x14U, BusExceptionType::kRaiseStkerr)));
696 cpua.template WriteRegister<RegisterId::kLr>(lr);
697
698 // BranchTo(MemA[frameptr+0x18,4]); UNPREDICTABLE if the new PC not halfword aligned
700 return_adr, void,
701 (bus.template ReadOrRaise<u32>(cpua, frameptr + 0x18U, BusExceptionType::kRaiseStkerr)));
702 Pc::BranchTo(cpua, return_adr);
703
704 // psr = MemA[frameptr+0x1C,4];
706 psr, void,
707 (bus.template ReadOrRaise<u32>(cpua, frameptr + 0x1CU, BusExceptionType::kRaiseStkerr)));
708
709 // Combine every LOG_TRACE into on single LOG_TRACE
710 LOG_TRACE(TLogger,
711 "Popped R0 = 0x%08X, "
712 "R1 = 0x%08X, "
713 "R2 = 0x%08X, "
714 "R3 = 0x%08X, "
715 "R12 = 0x%08X, "
716 "LR = 0x%08X, "
717 "ReturnAddress = 0x%08X, "
718 "PSR = 0x%08X",
719 r0, r1, r2, r3, r12, lr, return_adr, psr);
720
721 // if HaveFPExt()
722 if (false) {
723 // if EXC_RETURN<4> == '0' then if FPCCR.LSPACT == '1' then
724 if (false) {
725 // FPCCR.LSPACT = '0'; // state in FP is still valid
726 // else
727 // CheckVFPEnabled();
728 // for i = 0 to 15
729 // S[i] = MemA[frameptr+0x20+(4*i),4];
730 // FPSCR = MemA[frameptr+0x60,4];
731 // CONTROL.FPCA = NOT(EXC_RETURN<4>);
732 }
733 }
734
735 // spmask = Zeros(29):(psr<9> AND forcealign):'00';
736 const auto spmask = (Bm32::ExtractBits1R<9U, 9U>(psr) & forcealign) << 2U;
737
738 switch (exc_return & 0xFU) {
739 case 0b0001: { // returning to Handler using Main stack
740 // SP_main = (SP_main + framesize) OR spmask;
741 auto sp_main = (cpua.template ReadSpecialRegister<SId::kSpMain>() + framesize) | spmask;
742 LOG_TRACE(TLogger, "Returning to handler mode using main stack: SP_main = 0x%08X", sp_main);
743 cpua.template WriteSpecialRegister<SId::kSpMain>(sp_main);
744 break;
745 }
746 case 0b1001: { // returning to Thread using Main stack
747 // SP_main = (SP_main + framesize) OR spmask;
748 auto sp_main = (cpua.template ReadSpecialRegister<SId::kSpMain>() + framesize) | spmask;
749 LOG_TRACE(TLogger, "Returning to thread mode using main stack: SP_main = 0x%08X", sp_main);
750 cpua.template WriteSpecialRegister<SId::kSpMain>(sp_main);
751 break;
752 }
753
754 case 0b1101: { // returning to Thread using Process stack
755 auto sp_process = (cpua.template ReadSpecialRegister<SId::kSpProcess>() + framesize) | spmask;
756 LOG_TRACE(TLogger, "Returning to thread mode using process stack: SP_process = 0x%08X",
757 sp_process);
758 cpua.template WriteSpecialRegister<SId::kSpProcess>(sp_process);
759 break;
760 }
761 default: {
762 return Err(StatusCode::kUnexpected);
763 }
764 }
765
766 // APSR<31:27> = psr<31:27>; // valid APSR bits loaded from memory
768 cpua.template WriteSpecialRegister<SId::kApsr>(psr_31_27 << ApsrRegister::kQPos);
769
770 // if HaveDSPExt() then
771 if (false) {
772 // APSR<19:16> = psr<19:16>;
773 }
774
775 // IPSR<8:0> = psr<8:0>; // valid IPSR bits loaded from memory
776 auto ipsr_8_0 = psr & IpsrRegister::kExceptionNumberMsk;
777
778 cpua.template WriteSpecialRegister<SId::kIpsr>(ipsr_8_0);
779
780 // EPSR<26:24,15:10> = psr<26:24,15:10>; // valid EPSR bits loaded from memory
782 << EpsrRegister::kTPos) |
784 << EpsrRegister::kItBit2Pos);
785
786 cpua.template WriteSpecialRegister<SId::kEpsr>(epsr_new);
787
788 return Ok();
789 }
790
791 static void SetExceptionPending(TCpuAccessor &cpua, ExceptionType exception_type) {
792#ifdef LOGLEVEL_ERROR
793 switch (exception_type) {
794
796 LOG_ERROR(TLogger, "Set HardFault exception pending");
797 break;
799 LOG_ERROR(TLogger, "Set MemoryManagementFault exception pending");
800 break;
802 LOG_ERROR(TLogger, "Set BusFault exception pending");
803 break;
805 LOG_ERROR(TLogger, "Set UsageFault exception pending");
806 break;
807 default:
808 // do nothing
809 break;
810 }
811#endif
812
813 assert(static_cast<u32>(exception_type) >= 1U);
814 assert(static_cast<u32>(exception_type) <= CountExceptions());
815
816 auto &exception_states = cpua.GetExceptionStates();
817 auto &selected_exception = exception_states.exception[static_cast<u32>(exception_type) - 1U];
818
819 // cannot have multiple pending exceptions of the same type
820 if (selected_exception.IsPending() == false) {
821 exception_states.pending_exceptions += 1U;
822 }
823
824 selected_exception.SetPending();
825
826 LOG_TRACE(TLogger, "SetExceptionPending: exception_type = %d, priority = %d",
827 static_cast<uint32_t>(exception_type), selected_exception.GetPriority());
828 }
829
830 static void ClearExceptionPending(TCpuAccessor &cpua, ExceptionType exception_type) {
831 assert(static_cast<u32>(exception_type) >= 1U);
832 assert(static_cast<u32>(exception_type) <= CountExceptions());
833
834 auto &exception_states = cpua.GetExceptionStates();
835 auto &selected_exception = exception_states.exception[static_cast<u32>(exception_type) - 1U];
836
837 // cannot clear a non-pending exception
838 assert(selected_exception.IsPending() == true);
839
840 selected_exception.ClearPending();
841 exception_states.pending_exceptions -= 1U;
842
843 LOG_TRACE(TLogger, "ClearExceptionPending: exception_type = %d, priority = %d",
844 static_cast<uint32_t>(exception_type), selected_exception.GetPriority());
845 }
846
847 static void SetExceptionActive(TCpuAccessor &cpua, ExceptionType exception_type) {
848 assert(static_cast<u32>(exception_type) >= 1U);
849 assert(static_cast<u32>(exception_type) <= CountExceptions());
850
851 auto &exception_states = cpua.GetExceptionStates();
852 auto &selected_exception = exception_states.exception[static_cast<u32>(exception_type) - 1U];
853 assert(selected_exception.IsActive() == false);
854 selected_exception.SetActive();
855
856 LOG_TRACE(TLogger, "SetExceptionActive: exception_type = %d, priority = %d",
857 static_cast<uint32_t>(exception_type), selected_exception.GetPriority());
858 }
859
860 static void ClearExceptionActive(TCpuAccessor &cpua, ExceptionType exception_type) {
861 assert(static_cast<u32>(exception_type) >= 1U);
862 assert(static_cast<u32>(exception_type) <= CountExceptions());
863
864 auto &exception_states = cpua.GetExceptionStates();
865 auto &selected_exception = exception_states.exception[static_cast<u32>(exception_type) - 1U];
866 assert(selected_exception.IsActive() == true);
867 selected_exception.ClearActive();
868
869 LOG_TRACE(TLogger, "ClearExceptionActive: exception_type = %d, priority = %d",
870 static_cast<uint32_t>(exception_type), selected_exception.GetPriority());
871 }
872
873 template <typename ExcInstant,
874 typename std::enable_if_t<ExcInstant::kInstant == ExecutionInstant::kPreFetch, int> = 0>
875 static bool CanExceptionExecute(const ExceptionType &exception_type) {
876 switch (exception_type) {
881 return true;
882 default: {
883 if (static_cast<u32>(exception_type) >= 16U) {
884 return true;
885 } else {
886 return false;
887 }
888 }
889 }
890 }
891
892 template <typename ExcInstant, typename std::enable_if_t<
893 ExcInstant::kInstant == ExecutionInstant::kPostFetch, int> = 0>
894 static bool CanExceptionExecute(const ExceptionType &exception_type) {
895 switch (exception_type) {
897 case ExceptionType::kBusFault: // After fetch bus fault and memory management fault can occur
898 case ExceptionType::kUsageFault: // Invalid state
899 return true;
900 default: {
901 return false;
902 }
903 }
904 }
905
906 template <
907 typename ExcInstant,
908 typename std::enable_if_t<ExcInstant::kInstant == ExecutionInstant::kPostExecution, int> = 0>
909 static bool CanExceptionExecute(const ExceptionType &exception_type) {
910 switch (exception_type) {
916 return true;
917 default:
918 return false;
919 }
920 }
921
922 template <typename ExcInstant, typename TBus>
923 static Result<bool> CheckExceptions(TCpuAccessor &cpua, TBus &bus,
924 const ExceptionContext &context) {
925 auto &exception_states = cpua.GetExceptionStates();
926 auto &pending_exceptions = exception_states.pending_exceptions;
927
928 // if no exceptions are pending, return
929 if (pending_exceptions == 0U) {
930 return Ok(false);
931 }
932
933 auto executing_exc_type =
934 cpua.template ReadSpecialRegister<SId::kIpsr>() & IpsrRegister::kExceptionNumberMsk;
935
936 i16 executing_exc_priority =
937 kLowestExceptionPriority + 1U; // one lower than the lowest priority
938
939 if (executing_exc_type != 0U) {
940 assert(static_cast<u32>(executing_exc_type) >= 1U);
941 assert(static_cast<u32>(executing_exc_type) <= CountExceptions());
942 executing_exc_priority = exception_states.exception[executing_exc_type - 1U].GetPriority();
943 }
944
945 u32 preempt_exc_type = 0U; // 0 means no exception to preempt
946 i16 preempt_exc_priority = kLowestExceptionPriority + 1U; // one lower than the lowest priority
947
948 for (auto i = 0U; i < CountExceptions(); ++i) {
949
950 auto &exception = exception_states.exception[i];
951
952 if (exception.IsPending()) {
953 if (exception.IsActive()) {
954 // Exception is currently active ... skip
955 continue;
956 }
957
958 if (exception.GetPriority() >= executing_exc_priority) {
959 // Exception has lower or equal priority than the currently executing exception
960 // ... skip
961 continue;
962 }
963
964 if (exception.GetPriority() > preempt_exc_priority) {
965 // Exception has lower priority than the last found exception to preempt ... skip
966
967 // when multiple exceptions with the same priority are pending, the one with the lowest
968 // exception number is taken
969 continue;
970 }
971 preempt_exc_type = i + 1U;
972 preempt_exc_priority = exception.GetPriority();
973 }
974 }
975
976 // if no exceptions should preempt, return
977 if (preempt_exc_type == 0U) {
978 return Ok(false);
979 }
980
981 if (CanExceptionExecute<ExcInstant>(static_cast<ExceptionType>(preempt_exc_type)) == false) {
982 // Will be processed later
983 return Ok(false);
984 }
985
986 auto e_preemp_exc_type = static_cast<ExceptionType>(preempt_exc_type);
987
988 ClearExceptionPending(cpua, e_preemp_exc_type);
989 TRY(bool, ExceptionEntry<ExcInstant>(cpua, bus, e_preemp_exc_type, context));
990
991 return Ok(true);
992 };
993};
994
995} // namespace libmicroemu::internal
static constexpr u32 GenerateBitMask()
Definition bit_manip.h:67
static constexpr u32 ExtractBits1R(const u32 &value)
Definition bit_manip.h:136
Definition exceptions_ops.h:49
Definition exceptions_ops.h:42
Definition exceptions_ops.h:33
static bool IsProcessStack(const TCpuAccessor &cpua)
Check if the cpu is using the process stack.
Definition predicates.h:61
static bool IsHandlerMode(const TCpuAccessor &cpua)
Check if the cpu is in handler mode.
Definition predicates.h:38
static bool IsMainStack(const TCpuAccessor &cpua)
Check if the cpu is using the main stack.
Definition predicates.h:49
static bool IsThreadMode(const TCpuAccessor &cpua)
Check if the cpu is in thread mode.
Definition predicates.h:27
The libmicroemu::internal namespace contains all classes and functions of libmicroemu which are priva...
Definition bkpt_flags.h:6
ExceptionType
Exception enumeration.
Definition exception_type.h:12
@ kDebugMonitor
Debugging related ExceptionsOps.
Definition exception_type.h:51
@ kMemoryManagementFault
Memory related fault (bus access error either for instructions or data)
Definition exception_type.h:24
@ kSysTick
SysTick exception, used for system timer.
Definition exception_type.h:60
@ kSVCall
Exception for system calls.
Definition exception_type.h:48
@ kUsageFault
Usage fault exception, triggered by errors in the usage of the processor.
Definition exception_type.h:30
@ kNMI
Definition exception_type.h:18
@ kPendSV
PendSV exception, used for context switching.
Definition exception_type.h:57
@ kHardFault
Hard fault exception, triggered by faults that cannot be handled by any other exception.
Definition exception_type.h:21
@ kReset
Special exception to reset the processor.
Definition exception_type.h:14
@ kBusFault
Bus fault exception, triggered by bus errors on instruction fetches and data accesses.
Definition exception_type.h:27
SpecialRegisterId
Enumeration of special register IDs.
Definition special_register_id.h:18
@ kUnexpected
An unexpected error occurred.
Definition status_code.h:28
@ kUsageFault
Usage fault occurred.
Definition status_code.h:95
@ kExecutorUnpredictable
Executor encountered an unpredictable operation.
Definition status_code.h:76
@ kNotImplemented
Operation or feature is not implemented.
Definition status_code.h:34
This file contains the declaration of the Predicates class.
Contains the Result class which is used to return the result of a function.
#define TRY_ASSIGN(NAME, OUT_TYPE, CALL)
Try to assign a value to a variable and return an error if the assignment fails.
Definition result.h:123
#define TRY(OUT_TYPE, CALL)
Try to call a function and return an error if the call fails.
Definition result.h:138
This file contains the declaration of the SpecialRegisterId enumeration and related functions.
Definition register_details.h:254
Definition exceptions_ops.h:18
Definition result.h:15