Skip to content

Commit

Permalink
Replace CLINT with preliminary ACLINT support
Browse files Browse the repository at this point in the history
Based on the implementation of CLINT, a preliminary ACLINT is
implemented, including the basic logic for operating `mtimer`, `mswi`,
and `sswi`. CLINT was replaced with ACLINT, and the old CLINT
implementation was removed entirely.

Currently, due to the lack of implementation, the introduced ACLINT uses
only supervisor-level IPI. Therefore, although the logic for mswi is
implemented, it is not being used at the moment.

The implementation can be tested by `make check SMP=n`, where n is the
number of harts you want to simulate.
  • Loading branch information
Mes0903 committed Dec 14, 2024
1 parent 59d39f5 commit e6b0add
Show file tree
Hide file tree
Showing 7 changed files with 355 additions and 143 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ OBJS := \
plic.o \
uart.o \
main.o \
clint.o \
aclint.o \
$(OBJS_EXTRA)

deps := $(OBJS:%.o=.%.o.d)
Expand Down
220 changes: 220 additions & 0 deletions aclint.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
#include <stdint.h>
#include "device.h"
#include "riscv.h"
#include "riscv_private.h"

/* ACLINT MTIMER */
void aclint_mtimer_update_interrupts(hart_t *hart, mtimer_state_t *mtimer)
{
if (semu_timer_get(&mtimer->mtime) >= mtimer->mtimecmp[hart->mhartid])
hart->sip |= RV_INT_STI_BIT; // Set Supervisor Timer Interrupt
else
hart->sip &= ~RV_INT_STI_BIT; // Clear Supervisor Timer Interrupt
}

static bool aclint_mtimer_reg_read(mtimer_state_t *mtimer,
uint32_t addr,
uint32_t *value)
{
/**
* @brief `addr & 0x4` is used to determine the upper or lower 32 bits
* of the mtimecmp register. If `addr & 0x4` is 0, then the lower 32
* bits are accessed.
*
* `addr >> 3` is used to get the index of the mtimecmp array. In
* "ACLINT MTIMER Compare Register Map", each mtimecmp register is 8
* bytes long. So, we need to divide the address by 8 to get the index.
*
*/

/* mtimecmp (0x4300000 ~ 0x4307FF8) */
if (addr < 0x7FF8) {
*value =
(uint32_t) (mtimer->mtimecmp[addr >> 3] >> (addr & 0x4 ? 32 : 0));
return true;
}

/* mtime (0x4307FF8 ~ 0x4308000) */
if (addr < 0x8000) {
*value = (uint32_t) (semu_timer_get(&mtimer->mtime) >>
(addr & 0x4 ? 32 : 0));
return true;
}
return false;
}

static bool aclint_mtimer_reg_write(mtimer_state_t *mtimer,
uint32_t addr,
uint32_t value)
{
/**
* @brief The `cmp_val & 0xFFFFFFFF` is used to select the upper 32 bits
* of mtimer->mtimecmp[addr >> 3], then shift the value to the left by
* 32 bits to set the upper 32 bits.
*
*/

/* mtimecmp (0x4300000 ~ 0x4307FF8) */
if (addr < 0x7FF8) {
uint64_t cmp_val = mtimer->mtimecmp[addr >> 3];

if (addr & 0x4)
cmp_val = (cmp_val & 0xFFFFFFFF) | ((uint64_t) value << 32);
else
cmp_val = (cmp_val & 0xFFFFFFFF00000000ULL) | value;

mtimer->mtimecmp[addr >> 3] = cmp_val;
return true;
}

/* mtime (0x4307FF8 ~ 0x4308000) */
if (addr < 0x8000) {
uint64_t mtime_val = mtimer->mtime.begin;
if (addr & 0x4)
mtime_val = (mtime_val & 0xFFFFFFFF) | ((uint64_t) value << 32);
else
mtime_val = (mtime_val & 0xFFFFFFFF00000000ULL) | value;

semu_timer_rebase(&mtimer->mtime, mtime_val);
return true;
}

return false;
}

void aclint_mtimer_read(hart_t *hart,
mtimer_state_t *mtimer,
uint32_t addr,
uint8_t width,
uint32_t *value)
{
if (!aclint_mtimer_reg_read(mtimer, addr, value))
vm_set_exception(hart, RV_EXC_LOAD_FAULT, hart->exc_val);

*value >>= (RV_MEM_SW - width);
}

void aclint_mtimer_write(hart_t *hart,
mtimer_state_t *mtimer,
uint32_t addr,
uint8_t width,
uint32_t value)
{
if (!aclint_mtimer_reg_write(mtimer, addr, value << (RV_MEM_SW - width)))
vm_set_exception(hart, RV_EXC_STORE_FAULT, hart->exc_val);
}

/* ACLINT MSWI */
void aclint_mswi_update_interrupts(hart_t *hart, mswi_state_t *mswi)
{
if (mswi->msip[hart->mhartid])
hart->sip |= RV_INT_SSI_BIT; // Set Machine Software Interrupt
else
hart->sip &= ~RV_INT_SSI_BIT; // Clear Machine Software Interrupt
}

static bool aclint_mswi_reg_read(mswi_state_t *mswi,
uint32_t addr,
uint32_t *value)
{
/**
* @brief `msip` is an array where each entry corresponds to a Hart,
* each entry is 4 bytes (32 bits). So, we need to divide the address
* by 4 to get the index.
*/

/* Address range for msip: 0x4400000 ~ 0x4404000 */
if (addr < 0x4000) {
*value = mswi->msip[addr >> 2];
return true;
}
return false;
}

static bool aclint_mswi_reg_write(mswi_state_t *mswi,
uint32_t addr,
uint32_t value)
{
if (addr < 0x4000) {
mswi->msip[addr >> 2] = value & 0x1; // Only the LSB is valid
return true;
}
return false;
}

void aclint_mswi_read(hart_t *hart,
mswi_state_t *mswi,
uint32_t addr,
uint8_t width,
uint32_t *value)
{
if (!aclint_mswi_reg_read(mswi, addr, value))
vm_set_exception(hart, RV_EXC_LOAD_FAULT, hart->exc_val);

*value >>= (RV_MEM_SW - width);
}

void aclint_mswi_write(hart_t *hart,
mswi_state_t *mswi,
uint32_t addr,
uint8_t width,
uint32_t value)
{
if (!aclint_mswi_reg_write(mswi, addr, value << (RV_MEM_SW - width)))
vm_set_exception(hart, RV_EXC_STORE_FAULT, hart->exc_val);
}

/* ACLINT SSWI */
void aclint_sswi_update_interrupts(hart_t *hart, sswi_state_t *sswi)
{
if (sswi->ssip[hart->mhartid])
hart->sip |= RV_INT_SSI_BIT; // Set Supervisor Software Interrupt
else
hart->sip &= ~RV_INT_SSI_BIT; // Clear Supervisor Software Interrupt
}

static bool aclint_sswi_reg_read(__attribute__((unused)) sswi_state_t *sswi,
uint32_t addr,
uint32_t *value)
{
/* Address range for ssip: 0x4500000 ~ 0x4504000 */
if (addr < 0x4000) {
*value = 0; // Upper 31 bits are zero, and LSB reads as 0
return true;
}
return false;
}

static bool aclint_sswi_reg_write(sswi_state_t *sswi,
uint32_t addr,
uint32_t value)
{
if (addr < 0x4000) {
sswi->ssip[addr >> 2] = value & 0x1; // Only the LSB is valid

return true;
}
return false;
}

void aclint_sswi_read(hart_t *hart,
sswi_state_t *sswi,
uint32_t addr,
uint8_t width,
uint32_t *value)
{
if (!aclint_sswi_reg_read(sswi, addr, value))
vm_set_exception(hart, RV_EXC_LOAD_FAULT, hart->exc_val);

*value >>= (RV_MEM_SW - width);
}

void aclint_sswi_write(hart_t *hart,
sswi_state_t *sswi,
uint32_t addr,
uint8_t width,
uint32_t value)
{
if (!aclint_sswi_reg_write(sswi, addr, value << (RV_MEM_SW - width)))
vm_set_exception(hart, RV_EXC_STORE_FAULT, hart->exc_val);
}
96 changes: 0 additions & 96 deletions clint.c

This file was deleted.

Loading

0 comments on commit e6b0add

Please sign in to comment.