main.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. /**
  2. * @file main.c
  3. * @brief Test RISC-V backtrace via illegal instruction exception.
  4. *
  5. * This test:
  6. * 1. Registers an illegal instruction exception handler.
  7. * 2. Triggers an illegal instruction (e.g., .word 0).
  8. * 3. In the handler, prints backtrace using riscv_backtrace_print().
  9. *
  10. * <h2>Compilation Requirement</h2>
  11. * <b>MUST</b> be compiled with `-fno-omit-frame-pointer` to ensure
  12. * frame pointers (s0) are pushed to the stack.
  13. *
  14. * <h2>Expected Output</h2>
  15. * @code
  16. * RISC-V Backtrace Test (FP=CFA Convention)
  17. * Stack Top (@_sp): 0x80020000
  18. * Calling chain: level1() -> level2() -> trigger_illegal()...
  19. * Inside level1, calling level2...
  20. * Inside level2, calling trigger_illegal...
  21. * About to execute illegal instruction in trigger_illegal()...
  22. *
  23. * === ILLEGAL INSTRUCTION EXCEPTION ===
  24. * mcause = 0000000000000002 (expected 2)
  25. *
  26. * --- Starting Backtrace ---
  27. * === Exception Frame Information ===
  28. * ra: 0x..., tp: 0x..., ...
  29. * cause: 0x..., epc: 0x..., msubm: 0x...
  30. *
  31. * === Stack Backtrace ===
  32. * Current SP: 0x..., Start FP: 0x...
  33. * Captured X frame(s):
  34. * #0 RA=0x... CFA=0x...
  35. * #1 RA=0x... CFA=0x...
  36. *
  37. * [Addr2Line Hint]
  38. * Run the following command on your host PC:
  39. * riscv64-unknown-elf-addr2line -pfiaC -e <elf> 0x<epc> 0x<ra1> 0x<ra2> ...
  40. * @endcode
  41. */
  42. #include <stdio.h>
  43. #include <stdint.h>
  44. #include "riscv_backtrace.h"
  45. #include <nuclei_sdk_soc.h>
  46. /* ================================================================== */
  47. /* External Dependencies */
  48. /* ================================================================== */
  49. /**
  50. * @brief Stack top address symbol from linker script.
  51. * @note This points to the highest address of the stack region.
  52. */
  53. extern char _sp;
  54. /** Stack size definition (adjust to match your linker script or startup code) */
  55. #define TEST_STACK_SIZE 2048
  56. /* ================================================================== */
  57. /* Test Configuration */
  58. /* ================================================================== */
  59. /**
  60. * @brief Maximum number of times to re-trigger the illegal instruction.
  61. *
  62. * This allows testing nested exception handling. After reaching this limit,
  63. * the handler will print the backtrace and halt instead of re-triggering.
  64. *
  65. * @note Recommended value: 2-5 for testing nested exceptions.
  66. * Higher values may cause stack overflow in exception handler.
  67. */
  68. #ifndef ILLEGAL_RETRY_COUNT
  69. #define ILLEGAL_RETRY_COUNT 2
  70. #endif
  71. /* ================================================================== */
  72. /* Test Logic */
  73. /* ================================================================== */
  74. /** Global flag to track how many times the illegal exception has been triggered */
  75. static volatile int illegal_triggered = 0;
  76. /**
  77. * @brief Illegal instruction exception handler.
  78. *
  79. * @param mcause Exception cause (2 for illegal instruction)
  80. * @param sp Stack pointer at exception entry (pointing to saved context)
  81. */
  82. void illegal_handler(unsigned long mcause, unsigned long sp)
  83. {
  84. unsigned long fp;
  85. asm volatile ("mv %0, s0" : "=r"(fp));
  86. if (illegal_triggered >= ILLEGAL_RETRY_COUNT) {
  87. /* Already handled; just halt to avoid loop */
  88. printf("=== Test completed. Halting. ===\n");
  89. while (1) __asm__ volatile ("wfi");
  90. }
  91. illegal_triggered += 1;
  92. printf("\n=== ILLEGAL INSTRUCTION EXCEPTION ===\n");
  93. printf("mcause = %lu (expected 2), triggered %d times\n", mcause & 0xFFF, illegal_triggered);
  94. if (illegal_triggered < ILLEGAL_RETRY_COUNT) {
  95. ((EXC_Frame_Type *)sp)->epc += 4;
  96. __asm__ volatile (
  97. "nop \n"
  98. ".word 0x00000000 \n"
  99. "nop \n"
  100. );
  101. } else {
  102. printf("\n--- Starting Backtrace ---\n");
  103. riscv_backtrace_print(fp, (unsigned long)&_sp, TEST_STACK_SIZE, sp, PRV_M);
  104. printf("--- End of Backtrace ---\n");
  105. }
  106. }
  107. /**
  108. * @brief Function that deliberately triggers an illegal instruction.
  109. *
  110. * We use 'noinline' to ensure this function has its own stack frame.
  111. */
  112. __attribute__((noinline)) void trigger_illegal(void)
  113. {
  114. printf("About to execute illegal instruction in trigger_illegal()...\n");
  115. /*
  116. * Insert an illegal instruction.
  117. * 0x00000000 is guaranteed to be illegal in RISC-V.
  118. */
  119. __asm__ volatile (
  120. "nop \n"
  121. ".word 0x00000000 \n"
  122. "nop \n"
  123. );
  124. }
  125. /**
  126. * @brief Level 2 caller in the call chain.
  127. */
  128. __attribute__((noinline)) void level2(void)
  129. {
  130. printf("Inside level2, calling trigger_illegal...\n");
  131. trigger_illegal();
  132. }
  133. /**
  134. * @brief Level 1 caller in the call chain.
  135. */
  136. __attribute__((noinline)) void level1(void)
  137. {
  138. printf("Inside level1, calling level2...\n");
  139. level2();
  140. }
  141. int main(void)
  142. {
  143. CSR_MCFGINFO_Type mcfg_info;
  144. printf("RISC-V Backtrace Test (FP=CFA Convention)\n");
  145. #if defined(CPU_SERIES) && CPU_SERIES == 100
  146. mcfg_info.b.clic = 1;
  147. #else
  148. mcfg_info.d = __RV_CSR_READ(CSR_MCFG_INFO);
  149. #endif
  150. #if defined(__ECLIC_PRESENT) && (__ECLIC_PRESENT != 0)
  151. if (0 == mcfg_info.b.clic) {
  152. printf("You expect ECLIC present, but ECLIC is not present, will not run this example!\r\n");
  153. printf("You can rebuild and run this example with extra make option XLCFG_ECLIC=0 if ECLIC not present!\r\n");
  154. return 0;
  155. }
  156. #endif
  157. printf("Stack Top (@_sp): %p\n", (void *)&_sp);
  158. /*
  159. * Register handler for Illegal Instruction exception.
  160. * Exception ID 2 = MCAUSE_ILLEGAL_INSTRUCTION
  161. */
  162. Exception_Register_EXC(IlleIns_EXCn, (unsigned long)illegal_handler);
  163. printf("Calling chain: level1() -> level2() -> trigger_illegal()...\n");
  164. level1();
  165. /* Should not reach here */
  166. printf("ERROR: Should not return from level1!\n");
  167. return -1;
  168. }