RISC-V debugging: data and instruction cache handling

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • RISC-V debugging: data and instruction cache handling

      Hello,

      I'm working on a debug unit for a custom RISC-V CPU and would like to get it working with the SEGGER J-Link debug probe and tools. Currently, the debug unit works fine with OpenOCD + GDB. Also with JLinkExe, I can successfully connect, downloading files, halt the CPU, and step through code. Setting breakpoints and more complicated operations don't work or are buggy and it appears that the reason might be the fact that the data cache is not flushed and that the instruction cache is not invalidated after memory writes. I know that there is no standardized way of doing this, e.g., OpenOCD uses a combination of fence and fence.i instructions.

      I'm just curious how the SEGGER debugger is handling caches in general and how I could add the flush/invalidation myself. This must be quite a common problem for other RISC-V CPUs as well, so maybe there is an elegant way to handle this.

      Thank you!
    • Hi Evan,

      For now, I did the following (which feels a bit hacked but seems to work):
      • After memory write access: Executing a fence.i and fence instruction. In my implementation, fence.i invalidates the instruction cache and fence flushes the data cache. This is mainly required to ensure that software breakpoints are visible to the CPU.
      • Before memory read access: Executing a fence instruction to ensure that the debugger reads fresh data from memory when looking at memory content. I added a check to see if the CPU is halted or not because it seems the hook is also called when doing background memory reads using the SBA.


      C Source Code

      1. int HandleAfterMemAccessWrite(U32 Addr, U32 NumBytes, U32 Flags)
      2. {
      3. int ret;
      4. JLINK_SYS_Report("Executing fence.i & fence instructions");
      5. JLINK_RISCV_DMI_AutodetectDMISettings();
      6. // Write fence.i instruction to progbuf0
      7. ret = JLINK_RISCV_DMI_WriteReg(0x20, 0x0000100f);
      8. // Write fence instruction to progbuf1
      9. ret = JLINK_RISCV_DMI_WriteReg(0x21, 0x0000000f);
      10. // Write to command register
      11. // regno: 0x1000
      12. // write: 0
      13. // transfer: 0
      14. // postexec: 1
      15. // aarpostincrement: 0
      16. // -> 0x00241000
      17. ret = JLINK_RISCV_DMI_WriteReg(0x17, 0x00241000);
      18. return 0;
      19. }
      20. int HandleBeforeMemAccessRead(U32 Addr, U32 NumBytes, U32 Flags)
      21. {
      22. int is_halted;
      23. int ret;
      24. is_halted = JLINK_TARGET_IsHalted();
      25. if (is_halted == 1)
      26. {
      27. JLINK_SYS_Report("Executing fence instruction");
      28. JLINK_RISCV_DMI_AutodetectDMISettings();
      29. // Write fence instruction to progbuf0
      30. ret = JLINK_RISCV_DMI_WriteReg(0x20, 0x0000000f);
      31. // Write ebreak instruction to progbuf1
      32. ret = JLINK_RISCV_DMI_WriteReg(0x21, 0x00100073);
      33. // Write to command register
      34. // regno: 0x1000
      35. // write: 0
      36. // transfer: 0
      37. // postexec: 1
      38. // aarpostincrement: 0
      39. // -> 0x00241000
      40. ret = JLINK_RISCV_DMI_WriteReg(0x17, 0x00241000);
      41. }
      42. return 0;
      43. }
      Display All