hoglet67 / PiTubeDirect

Bare-metal Raspberry Pi project that attaches to the Acorn TUBE interface and emulates many BBC Micro Co Processors
GNU General Public License v3.0
188 stars 23 forks source link

RISC-V Co Pro: Pass elapsed_us into emulator so that RISC-V timer interrupts work #198

Closed hoglet67 closed 1 year ago

hoglet67 commented 1 year ago

Currently the emulator is stepped with this call:

      MiniRV32IMAStep(
                      riscv_state,   // struct MiniRV32IMAState * state
                      memory,        // uint8_t * image
                      0,             // uint32_t vProcAddress
                      0,             // uint32_t elapsedUs
                      1              // int count
                      );

elapsedUs is always 0.

This means the timer CSR is never updated:

   uint32_t new_timer = CSR( timerl ) + elapsedUs;
   if( new_timer < CSR( timerl ) ) CSR( timerh )++;
   CSR( timerl ) = new_timer;

So it's not possible to use RISC-V timer interrupts.

What's needed here is to somehow map the arm cycle counter register (CCNT) which increments at the ARM clock rate into elapsed_us increments.

Something like:

static inline uint32_t get_arm_cycle_count (void)
{
  uint32_t value;
  // Read CCNT Register
  asm volatile ("MRC p15, 0, %0, c9, c13, 0\t\n": "=r"(value));  
  return value;
}

Then in the main loop of the RISC-V emulator:

// ARM clock ticks in 1us
uint32_t arm_cycles_per_us = get_clock_rates(ARM_CLK_ID)->rate / 1000000,

// Last arm cycle count
uint32_t last_arm_cycle_count = get_arm_cycle_count(); 

while(1) { // main emulator loop

      uint32_t arm_cycle_count = get_arm_cycle_count();
      uint32_t elapsed_cycles = last_arm_cycle_count - arm_cycle_count;
      uint32_t elapsed_us = 0;
      if (elapsed_cycles >= arm_cycles_per_us) {
          elapsed_us = elapsed_cycles / arm_cycles_per_us;
          last_arm_cycle_count += elapsed_us * arm_cycles_per_us;
      }
      MiniRV32IMAStep(
                      riscv_state,   // struct MiniRV32IMAState * state
                      memory,        // uint8_t * image
                      0,             // uint32_t vProcAddress
                      elapsed_us,     // uint32_t elapsedUs
                      1              // int count
                      );

}

I think the above pattern should prevent rounding errors accumulating, though there may be a simpler way!