100 lines
2.5 KiB
C
100 lines
2.5 KiB
C
#include "interrupt.h"
|
|
#include "rv32cpu.h"
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
|
|
uint32_t interrupt_mi_from_scause(uint32_t scause)
|
|
{
|
|
switch(scause)
|
|
{
|
|
case SCAUSE_SUPERVISOR_TIMER_INTERRUPT:
|
|
return 0x20;
|
|
default:
|
|
fprintf(stderr, "interrupt_mie_bit_from_scause: wrong scause 0x%x\n", scause);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void interrupt_trigger(rv32_cpu_t* cpu, uint32_t scause)
|
|
{
|
|
// Make sure that interrupts are enabled globally
|
|
if(!(csr_read(cpu, CSR_MSTATUS) & STATUS_SIE))
|
|
return;
|
|
|
|
// Make sure that current interrupt is enabled
|
|
if(!(csr_read(cpu, CSR_MIE) & interrupt_mi_from_scause(scause)))
|
|
{
|
|
// Mark interrupt as pending
|
|
csr_write(cpu, CSR_MIP, csr_read(cpu, CSR_MIP) | interrupt_mi_from_scause(scause));
|
|
|
|
return;
|
|
}
|
|
|
|
// TODO : CHECK mideleg to see wether we should handle interrupt is S mode or M mode
|
|
|
|
|
|
// An interrupt can only be triggered from outside
|
|
// of the cpu, so we are on a different thread
|
|
// and we don't already own the CPU mutex
|
|
// We obtain in this thread the control of the CPU,
|
|
// but we know it is not in the middle of an instruction
|
|
// (we got the mutex) ; this way we can just change
|
|
// registers to set interrupt handler execution
|
|
pthread_mutex_lock(&cpu->mutex);
|
|
|
|
// Set xCAUSE with interrupt bit set
|
|
cpu->csr[CSR_SCAUSE] = 0x80000000 | scause;
|
|
|
|
// Set xSTATUS.xPIE (previous interrupt enable) bit
|
|
cpu->csr[CSR_MSTATUS] |= STATUS_SPIE;
|
|
|
|
// Set xSTATUS.xPP (Previous Privilege) bit
|
|
// TODO : Allow user mode interrupts (by not setting this)
|
|
cpu->csr[CSR_MSTATUS] |= 0x100;
|
|
|
|
// Unset xSTATUS.xIE (interrupt enable) bit
|
|
cpu->csr[CSR_MSTATUS] &= (~STATUS_SIE);
|
|
|
|
// Set xEPC : PC at interruption
|
|
cpu->csr[CSR_SEPC] = cpu->pc;
|
|
|
|
// Set PC to xTVEC : exception handler code
|
|
// xTVEC: [Base(30bits) Mode(2 bits)], address 4-byte aligned in base
|
|
// Interrupts can be vectored, if mode == 1, then pc = xTVEC + scause * 4
|
|
int mode = cpu->csr[CSR_STVEC] & 0b11;
|
|
switch(mode)
|
|
{
|
|
case 0:
|
|
cpu->pc = cpu->csr[CSR_STVEC] & 0xFFFFFFFC;
|
|
break;
|
|
case 1:
|
|
cpu->pc = (cpu->csr[CSR_STVEC] & 0xFFFFFFFC) + scause * 4;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "interrupt_trigger: invalid mode encountered in sTVEC register\n");
|
|
exit(EXIT_FAILURE);
|
|
break;
|
|
}
|
|
|
|
pthread_mutex_unlock(&cpu->mutex);
|
|
}
|
|
|
|
void interrupt_timer_thread()
|
|
{
|
|
while(1)
|
|
{
|
|
cpu0->csr[CSR_TIME]++;
|
|
interrupt_trigger(cpu0, SCAUSE_SUPERVISOR_TIMER_INTERRUPT);
|
|
usleep(1);
|
|
}
|
|
}
|
|
|
|
void interrupt_timer_setup()
|
|
{
|
|
pthread_t timer_thread;
|
|
pthread_create(&timer_thread, 0, (void*) interrupt_timer_thread, 0);
|
|
}
|