Skip to content

Commit

Permalink
Preliminary ACLINT implementation
Browse files Browse the repository at this point in the history
In this commit, based on the implementation of CLINT, an preliminary
ACLINT is implemented, including the basic logic for operating `mtimer`,
`mswi`, and `sswi`.

Additionally, introduces a new script `gen-hart-dts.py` for generating
device-tree configurations and renames the original script to
`gen-clint-dts.py`. The new script is currently unused.

A new macro SEMU_FEATURE_ACLINT is also added to feature.h, allowing
the replacement of CLINT with ACLINT in the implementation.

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.

You can test it directly using the command `make check SMP=n`, where n
is the number of harts you want to simulate.
  • Loading branch information
Mes0903 committed Dec 12, 2024
1 parent 59d39f5 commit 4020bff
Show file tree
Hide file tree
Showing 8 changed files with 467 additions and 12 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);
}
62 changes: 61 additions & 1 deletion device.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,59 @@ void virtio_blk_write(hart_t *vm,
uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file);
#endif /* SEMU_HAS(VIRTIOBLK) */

#if SEMU_HAS(ACLINT)
/* ACLINT MTIMER */
typedef struct {
uint64_t mtimecmp[4095]; // Machine Timer Compare array
semu_timer_t mtime; // Machine Timer Counter
} mtimer_state_t;

void aclint_mtimer_update_interrupts(hart_t *hart, mtimer_state_t *mtimer);
void aclint_mtimer_read(hart_t *hart,
mtimer_state_t *mtimer,
uint32_t addr,
uint8_t width,
uint32_t *value);
void aclint_mtimer_write(hart_t *hart,
mtimer_state_t *mtimer,
uint32_t addr,
uint8_t width,
uint32_t value);

/* ACLINT MSWI */
typedef struct {
uint32_t msip[4096]; // Machine Software Interrupt Pending array
} mswi_state_t;

void aclint_mswi_update_interrupts(hart_t *hart, mswi_state_t *mswi);
void aclint_mswi_read(hart_t *hart,
mswi_state_t *mswi,
uint32_t addr,
uint8_t width,
uint32_t *value);
void aclint_mswi_write(hart_t *hart,
mswi_state_t *mswi,
uint32_t addr,
uint8_t width,
uint32_t value);

/* ACLINT SSWI */
typedef struct {
uint32_t ssip[4096]; // Supervisor Software Interrupt Pending array
} sswi_state_t;

void aclint_sswi_update_interrupts(hart_t *hart, sswi_state_t *sswi);
void aclint_sswi_read(hart_t *hart,
sswi_state_t *sswi,
uint32_t addr,
uint8_t width,
uint32_t *value);
void aclint_sswi_write(hart_t *hart,
sswi_state_t *sswi,
uint32_t addr,
uint8_t width,
uint32_t value);
#else
/* clint */
typedef struct {
uint32_t msip[4096];
Expand All @@ -190,9 +243,9 @@ void clint_write(hart_t *vm,
uint32_t addr,
uint8_t width,
uint32_t value);
#endif

/* memory mapping */

typedef struct {
bool stopped;
uint32_t *ram;
Expand All @@ -205,5 +258,12 @@ typedef struct {
#if SEMU_HAS(VIRTIOBLK)
virtio_blk_state_t vblk;
#endif
#if SEMU_HAS(ACLINT)
/* ACLINT */
mtimer_state_t mtimer;
mswi_state_t mswi;
sswi_state_t sswi;
#else
clint_state_t clint;
#endif
} emu_state_t;
5 changes: 5 additions & 0 deletions feature.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,10 @@
#define SEMU_FEATURE_VIRTIONET 1
#endif

/* ACLINT */
#ifndef SEMU_FEATURE_ACLINT
#define SEMU_FEATURE_ACLINT 1
#endif

/* Feature test macro */
#define SEMU_HAS(x) SEMU_FEATURE_##x
Loading

0 comments on commit 4020bff

Please sign in to comment.