/* ** ** Copyright 2020 OpenHW Group ** ** Licensed under the Solderpad Hardware Licence, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** https://solderpad.org/licenses/ ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. ** ******************************************************************************* ** Basic debugger test. Needs more work and bugs fixed ** It will launch a debug request and have debugger code execute (debugger.S) ******************************************************************************* */ #include #include #include #include "corev_uvmt.h" volatile int glb_hart_status = 0; // Written by main code only, read by debug code volatile int glb_debug_status = 0; // Written by debug code only, read by main code volatile int glb_ebreak_status = 0; // Written by ebreak code only, read by main code volatile int glb_illegal_insn_status = 0; // Written by illegal instruction code only, read by main code volatile int glb_debug_exception_status = 0; // Written by debug code during exception only volatile int glb_exception_ebreak_status = 0; // Written by main code, read by exception handler volatile int glb_previous_dpc = 0; // holds last dpc, used for checking correctness of stepping volatile int glb_step_info = 0; // info to dbg code about actions to take on stepping volatile int glb_step_count = 0; // Written by debug code for each time single step is entered // Expectation flags. Raise an error if handler or routine is enterred when not expected, volatile int glb_expect_illegal_insn = 0; volatile int glb_expect_ebreak_handler = 0; volatile int glb_expect_debug_entry = 0; volatile int glb_expect_debug_exception = 0; volatile int glb_expect_irq_entry = 0; volatile int glb_irq_timeout = 0; // Counter values // Checked at start and end of debug code // Only lower 32 bits checked, as simulation cannot overflow on 32 bits volatile int glb_mcycle_start = 0; volatile int glb_mcycle_end = 0; volatile int glb_minstret_start = 0; volatile int glb_minstret_end = 0; #define TEST_FAILED *(volatile int *)CV_VP_STATUS_FLAGS_BASE = 1 #define TEST_PASSED *(volatile int *)CV_VP_STATUS_FLAGS_BASE = 123456789 extern int __stack_start; typedef union { struct { unsigned int start_delay : 15; // 14: 0 unsigned int rand_start_delay : 1; // 15 unsigned int pulse_width : 13; // 28:16 unsigned int rand_pulse_width : 1; // 29 unsigned int pulse_mode : 1; // 30 0 = level, 1 = pulse unsigned int value : 1; // 31 } fields; unsigned int bits; } debug_req_control_t; #define DEBUG_REQ_CONTROL_REG *((volatile uint32_t *) (CV_VP_DEBUG_CONTROL_BASE)) #define TIMER_REG_ADDR ((volatile uint32_t *) (CV_VP_INTR_TIMER_BASE+0)) #define TIMER_VAL_ADDR ((volatile uint32_t *) (CV_VP_INTR_TIMER_BASE+4)) typedef union { struct { unsigned int uie : 1; // 0 // Implemented if USER mode enabled unsigned int sie : 1; // 1 unsigned int wpri : 1; // 2 unsigned int mie : 1; // 3 // Implemented unsigned int upie : 1; // 4 // Implemented if USER mode enabled unsigned int spie : 1; // 5 unsigned int wpri0 : 1; // 6 unsigned int mpie : 1; // 7 // Implemented unsigned int spp : 1; // 8 unsigned int wpri1 : 2; // 10: 9 unsigned int mpp : 2; // 12:11 // Implemented unsigned int fs : 2; // 14:13 unsigned int xs : 2; // 16:15 unsigned int mprv : 1; // 17 unsigned int sum : 1; // 18 unsigned int mxr : 1; // 19 unsigned int tvm : 1; // 20 unsigned int tw : 1; // 21 unsigned int tsr : 1; // 22 unsigned int wpri3 : 8; // 30:23 unsigned int sd : 1; // 31 } fields; unsigned int bits; } mstatus_t; extern void _single_step(int d); // Tag is simply to help debug and determine where the failure came from void check_debug_status(int tag, int value) { if(glb_debug_status != value){ printf("ERROR: check_debug_status(%d, %d): Tag=%d status=%d, exp=%d \n\n", tag, value, tag, glb_debug_status, value); TEST_FAILED; } } void check_debug_exception_status(int tag, int value) { if(glb_debug_exception_status != value){ printf("ERROR: check_debug_exception_status(%d, %d): Tag=%d status=%d, exp=%d \n\n", tag, value, tag, glb_debug_exception_status, value); TEST_FAILED; } } void check_hart_status(int tag, int value) { if(glb_hart_status != value){ printf("ERROR: check_hart_status(%d, %d): Tag=%d status=%d, exp=%d \n\n", tag, value, tag, glb_hart_status, value); TEST_FAILED; } } void check_ebreak_status(int tag, int value) { if(glb_ebreak_status != value){ printf("ERROR: check_ebreak_status(%d, %d): Tag=%d status=%d, exp=%d \n\n", tag, value, tag, glb_ebreak_status, value); TEST_FAILED; } } void check_illegal_insn_status(int tag, int value) { if(glb_illegal_insn_status != value){ printf("ERROR: check_illegal_insn_status(%d, %d): Tag=%d status=%d, exp=%d \n\n", tag, value, tag, glb_illegal_insn_status, value); TEST_FAILED; } } void delay(int count) { for (volatile int d = 0; d < count; d++); } void mstatus_mie_enable() { int mie_bit = 0x1 << 3; asm volatile("csrrs x0, mstatus, %0" : : "r" (mie_bit)); } void mstatus_mie_disable() { int mie_bit = 0x1 << 3; asm volatile("csrrc x0, mstatus, %0" : : "r" (mie_bit)); } void mie_enable_all() { uint32_t mie_mask = (uint32_t) -1; asm volatile("csrrs x0, mie, %0" : : "r" (mie_mask)); } void mie_disable_all() { uint32_t mie_mask = (uint32_t) -1; asm volatile("csrrc x0, mie, %0" : : "r" (mie_mask)); } void mie_enable(uint32_t irq) { // Enable the interrupt irq in MIE uint32_t mie_bit = 0x1 << irq; asm volatile("csrrs x0, mie, %0" : : "r" (mie_bit)); } void mie_disable(uint32_t irq) { // Disable the interrupt irq in MIE uint32_t mie_bit = 0x1 << irq; asm volatile("csrrc x0, mie, %0" : : "r" (mie_bit)); } void mm_ram_assert_irq(uint32_t mask, uint32_t cycle_delay) { *TIMER_REG_ADDR = mask; *TIMER_VAL_ADDR = 1 + cycle_delay; } void counters_enable() { // Enable counters mcycle (bit0) and minstret (bit2) uint32_t mask = 1<<2 | 1<<0; asm volatile("csrrc x0, 0x320, %0" : : "r" (mask)); } #define MACHINE 3 int main(int argc, char *argv[]) { unsigned int temp,temp1,temp2; debug_req_control_t debug_req_control; mstatus_t mstatus, mstatus_cmp; counters_enable(); printf("\nBasic test checking debug functionality.\n"); printf("------------------------\n"); printf(" Test1: check initialization values\n"); //check_debug_status(0,0); temp1 = 0xFFFFFFFF; /* get relevant CSRs and compare init values*/ __asm__ volatile("csrr %0, mstatus" : "=r"(temp1)); __asm__ volatile("csrw mstatus, %0 " : "=r"(temp1)); __asm__ volatile("csrr %0, mstatus" : "=r"(mstatus.bits)); __asm__ volatile("csrr %0, mie" : "=r"(temp)); __asm__ volatile("csrw mie, %0 " : "=r"(temp1)); __asm__ volatile("csrr %0, mie" : "=r"(temp2)); printf("\tmstats_rval = 0x%0x 0x%0x 0x%0x 0x%0x\n",temp2,mstatus.bits,temp,temp1); check_debug_status(0,0); printf("------------------------\n"); printf(" Test2.1: check access to Debug and Trigger registers\n"); // debug specifcation 13.2: 4.8 Core Debug Registers are not accessable unless in debug mode // ---------------------- // Check Debug Write Access temp = 0xFFFFFFFF; temp1 = glb_illegal_insn_status+1; glb_expect_illegal_insn = 1; __asm__ volatile("csrw dcsr, %0" : "=r"(temp)); // Debug DCSR check_illegal_insn_status(11,temp1++); glb_expect_illegal_insn = 1; __asm__ volatile("csrw dpc, %0" : "=r"(temp)); // Debug DPC check_illegal_insn_status(12,temp1++); glb_expect_illegal_insn = 1; __asm__ volatile("csrw dscratch, %0" : "=r"(temp)); // Debug DSCRATCH0 check_illegal_insn_status(13,temp1++); glb_expect_illegal_insn = 1; __asm__ volatile("csrw 0x7b3, %0" : "=r"(temp)); // Debug DSCRATCH1 check_illegal_insn_status(14,temp1++); // Check Read Access temp1 = glb_illegal_insn_status+1; // Allow illegal instruction handler to run glb_expect_illegal_insn = 1; __asm__ volatile("csrr %0, dcsr" : "=r"(temp)); // Debug DCSR check_illegal_insn_status(1,temp1++); glb_expect_illegal_insn = 1; __asm__ volatile("csrr %0, dpc" : "=r"(temp)); // Debug DPC check_illegal_insn_status(2,temp1++); glb_expect_illegal_insn = 1; __asm__ volatile("csrr %0, dscratch": "=r"(temp)); // Debug DSCRATCH0 check_illegal_insn_status(3,temp1++); glb_expect_illegal_insn = 1; __asm__ volatile("csrr %0, 0x7b3" : "=r"(temp)); // Debug DSCRATCH1 check_illegal_insn_status(4,temp1++); printf("------------------------\n"); printf(" Test2.2: check access to Trigger registers\n"); // NOTE: As of July 2024, CVA6 does not implement the trigger module. // TODO: Consider adding trigger module support in future revisions. // Writes are ignored temp = 0xFFFFFFFF; //TODO:MT should these be writes? __asm__ volatile("csrw 0x7a0, %0" : "=r"(temp)); // Trigger TSELECT __asm__ volatile("csrw 0x7a1, %0" : "=r"(temp)); // Trigger TDATA1 __asm__ volatile("csrw 0x7a2, %0" : "=r"(temp)); // Trigger TDATA2 __asm__ volatile("csrw 0x7a3, %0" : "=r"(temp)); // Trigger TDATA3 __asm__ volatile("csrw 0x7a4, %0" : "=r"(temp)); // Trigger TINFO // Read default value __asm__ volatile("csrr %0, 0x7a0" : "=r"(temp)); // Trigger TSELECT // CVA6 if(temp != 0x0){printf("ERROR: TSELET Read\n");TEST_FAILED;} __asm__ volatile("csrr %0, 0x7a1" : "=r"(temp)); // Trigger TDATA1 // CVA6 // 31:28 type = 2 // CVA6 // 27 dmode = 1 // CVA6 // 15:12 action = 1 // CVA6 // 6 m(achine) = 1 // CVA6 if(temp != (2<<28 | 1<<27 | 1<<12 | 1<<6)){printf("ERROR: TDATA1 Read\n");TEST_FAILED;} __asm__ volatile("csrr %0, 0x7a2" : "=r"(temp)); // Trigger TDATA2 // CVA6 if(temp != 0x0){printf("ERROR: TDATA2 Read\n");TEST_FAILED;} __asm__ volatile("csrr %0, 0x7a3" : "=r"(temp)); // Trigger TDATA3 // CVA6 if(temp != 0x0){printf("ERROR: TDATA3 Read\n");TEST_FAILED;} __asm__ volatile("csrr %0, 0x7a4" : "=r"(temp)); // Trigger TINFO // CVA6 // tmatch = 1<<2 // CVA6 if(temp != 1<<2){printf("ERROR: TINFO Read %d \n",temp);TEST_FAILED;} // Do not expect or allow any more illegal instructions mstatus_cmp = (mstatus_t) { .fields.mpp = MACHINE // }; if(mstatus_cmp.bits != mstatus.bits) {printf("ERROR: init mstatus mismatch exp=%x val=%x\n", mstatus_cmp.bits, mstatus.bits); TEST_FAILED;} //TODO:MT are these switched up? printf("------------------------\n"); printf(" Test3.1: check hart ebreak executes ebreak handler but does not execute debugger code\n"); glb_expect_ebreak_handler = 1; asm volatile("c.ebreak"); check_ebreak_status(32,1); printf("------------------------\n"); printf(" Test3.2: check hart c.ebreak executes ebreak handler but does not execute debugger code\n"); glb_expect_ebreak_handler = 1; asm volatile(".4byte 0x00100073"); check_ebreak_status(32,2); printf("------------------------\n"); printf(" Test4: request hardware debugger\n"); debug_req_control = (debug_req_control_t) { .fields.value = 1, .fields.pulse_mode = 1, //PULSE Mode .fields.rand_pulse_width = 0, .fields.pulse_width = 5,// TODO:MT determine pulse width with non-sticky debug_req .fields.rand_start_delay = 0, .fields.start_delay = 200 }; glb_expect_debug_entry = 1; DEBUG_REQ_CONTROL_REG = debug_req_control.bits; glb_hart_status = 4; // Basic test while(glb_debug_status != glb_hart_status){ printf("Wait for Debugger\n"); } check_debug_status(41,glb_hart_status); printf("------------------------\n"); printf(" Test5: have debugger execute ebreak 3 more times\n"); glb_hart_status = 5; glb_expect_debug_entry = 1; DEBUG_REQ_CONTROL_REG = debug_req_control.bits; while(glb_debug_status != (5+3)){ printf("Wait for Debugger\n"); } check_debug_status(51,(5+3)); printf("------------------------\n"); printf(" Test6: Test CSR access and default values in debug mode\n"); glb_hart_status = 6; glb_expect_debug_entry = 1; DEBUG_REQ_CONTROL_REG = debug_req_control.bits; while(glb_debug_status != glb_hart_status){ printf("Wait for Debugger\n"); } check_debug_status(61,glb_hart_status); printf("------------------------\n"); printf(" Test10: check hart ebreak executes debugger code\n"); glb_hart_status = 10; glb_expect_debug_entry = 1; asm volatile(".4byte 0x00100073"); check_debug_status(33,glb_hart_status); printf("------------------------\n"); printf(" Test11: check illegal csr exception during debug launches debugger exception and no csr modified\n"); glb_hart_status = 11; glb_expect_debug_entry = 1; glb_expect_debug_exception = 1; DEBUG_REQ_CONTROL_REG = debug_req_control.bits; while(glb_debug_status != glb_hart_status){ printf("Wait for Debugger\n"); } check_debug_status(111,glb_hart_status); check_debug_exception_status(111,glb_hart_status); //FIXME TBD BUG : need to update test to check actual csrs not modified. printf("------------------------\n"); printf(" Test12: check ecall exception during debug launches debugger exception and no csr modified\n"); glb_hart_status = 12; glb_expect_debug_entry = 1; glb_expect_debug_exception = 1; DEBUG_REQ_CONTROL_REG = debug_req_control.bits; while(glb_debug_status != glb_hart_status){ printf("Wait for Debugger\n"); } check_debug_status(112,glb_hart_status); check_debug_exception_status(112,glb_hart_status); //FIXME TBD BUG : need to update test to check actual csrs not modified. printf("------------------------\n"); printf(" Test13: check mret during debug launches debugger exception and no csr modified\n"); glb_hart_status = 13; glb_expect_debug_entry = 1; glb_expect_debug_exception = 1; DEBUG_REQ_CONTROL_REG = debug_req_control.bits; while(glb_debug_status != glb_hart_status){ printf("Wait for Debugger\n"); } check_debug_status(113,glb_hart_status); check_debug_exception_status(113,glb_hart_status); printf("------------------------\n"); printf(" Test14: Check exception ebreak enters debug mode\n"); glb_hart_status = 14; glb_expect_illegal_insn = 1; glb_exception_ebreak_status = 1; glb_expect_debug_entry = 1; // DCSR read will cause illegal instruction. // Exception routine reads glb_exception_ebreak_status=1 and executes c.ebreak __asm__ volatile("csrr %0, dcsr" : "=r"(temp)); // Debug DCSR while(glb_debug_status != glb_hart_status){ printf("Wait for Debugger\n"); } check_illegal_insn_status(114,temp1++); check_debug_status(114, glb_hart_status); printf("----------------------\n"); printf("Test 16: dret in m-mode causes exception\n"); glb_expect_illegal_insn = 1; __asm__ volatile("dret"); check_illegal_insn_status(16, temp1++); printf("------------------------\n"); printf("Test 17: WFI before debug_req_i and WFI in debug mode\n"); printf("If test hangs, WFI is NOT converted to NOP\n"); glb_expect_debug_entry = 1; glb_hart_status = 17; // start_delay is set to 200, should get the wfi executing before dbg request is asserted DEBUG_REQ_CONTROL_REG = debug_req_control.bits; // Execute WFI, when debug is asserted, it will act as NOP and enter debug mode // If not, test will hang __asm__ volatile("wfi"); check_debug_status(117, glb_hart_status); printf("----------------------\n"); printf("Checking interrupt, as this is needed by later tests\n"); // Assert and check irq, as this is needed by some tests. mstatus_mie_enable(); mie_enable(30); glb_expect_irq_entry = 1; mm_ram_assert_irq(0x40000000, 1); while(glb_expect_irq_entry == 1); mm_ram_assert_irq(0,0); printf("Irq check done\n"); // Check that stoupcount bit (10) in dcsr has no affect printf("-------------------------\n"); printf("Test 21: Setting stopcount bit=1\n"); glb_expect_debug_entry = 1; glb_hart_status = 21; DEBUG_REQ_CONTROL_REG = debug_req_control.bits; while(glb_debug_status != glb_hart_status){ printf("Wait for Debugger\n"); } check_debug_status(121, glb_hart_status); printf("------------------------\n"); printf("Test 18: Single stepping\n"); glb_hart_status = 18; // Run single step code (in single_step.S) _single_step(0); // Single step code should generate 2 illegal insn temp1++; check_illegal_insn_status(118, temp1++); check_debug_status(118, glb_hart_status); printf("Stepped %d times\n", glb_step_count); printf("------------------------\n"); printf("Test 19: irq in debug\n"); glb_hart_status = 19; glb_expect_debug_entry = 1; // Does not expect irq to be taken while in debug mode // but it will be taken when we exit from debug. // Timeout added in debug code to check for taken irq or not glb_expect_irq_entry = 1; DEBUG_REQ_CONTROL_REG=debug_req_control.bits; while(glb_debug_status != glb_hart_status){ printf("Wait for Debugger\n"); } check_debug_status(119, glb_hart_status); if(glb_irq_timeout != 0) { printf("glb_irq_timeout != 0, interrupt taken in debug.\n"); TEST_FAILED; } // Test debug req vs irq timing printf("-----------------------\n"); printf("Test 20: Asserting debug_req and irq at the same cycle\n"); glb_expect_debug_entry = 1; glb_expect_irq_entry = 1; glb_hart_status = 20; DEBUG_REQ_CONTROL_REG = debug_req_control.bits; // 170 halts on first instuction in interrupt handler // 175 gives same timing for interrupt and debug_req_i mm_ram_assert_irq(0x40000000, 175+20); while(glb_debug_status != glb_hart_status){ printf("Wait for Debugger\n"); } check_debug_status(120, glb_hart_status); // Execute fence instruction in debug printf("-----------------------------\n"); printf("Test 22: Execute fence in debug mode\n"); glb_expect_debug_entry = 1; glb_hart_status = 22; DEBUG_REQ_CONTROL_REG = debug_req_control.bits; while(glb_debug_status != glb_hart_status) { printf("Wait for debugger\n"); } check_debug_status(121, glb_hart_status); printf("------------------------\n"); printf("Test 23: trigger match in debug mode with match disabled\n"); glb_hart_status = 23; glb_expect_debug_entry = 1; // Request debug DEBUG_REQ_CONTROL_REG = debug_req_control.bits; while(glb_debug_status != glb_hart_status){ printf("Wait for Debugger\n"); } check_debug_status(123, glb_hart_status); printf("------------------------\n"); printf("Test 24: trigger register access in D-mode\n"); glb_hart_status = 24; glb_expect_debug_entry = 1; // Request debug DEBUG_REQ_CONTROL_REG = debug_req_control.bits; while(glb_debug_status != glb_hart_status){ printf("Wait for Debugger\n"); } check_debug_status(124, glb_hart_status); //-------------------------------- //return EXIT_FAILURE; printf("------------------------\n"); printf("Finished \n"); return EXIT_SUCCESS; }