Merge pull request #13 from Ziyue-Zhang/master
Merge branch 'southlake' into master
This commit is contained in:
commit
7fd1143d54
|
@ -12,10 +12,15 @@ AM_SRCS := noop/isa/riscv/trm_flash.c \
|
|||
nemu/common/video.c \
|
||||
dummy/audio.c \
|
||||
noop/isa/riscv/instr.c \
|
||||
dummy/mpe.c \
|
||||
xs/isa/riscv/mpe.c \
|
||||
xs/isa/riscv/clint.c \
|
||||
xs/isa/riscv/pmp.c \
|
||||
xs/isa/riscv/plic.c \
|
||||
xs/isa/riscv/pma.c \
|
||||
xs/isa/riscv/cache.c \
|
||||
nemu/isa/riscv/boot/start_flash.S
|
||||
|
||||
CFLAGS += -I$(AM_HOME)/am/src/nemu/include -DISA_H=\"riscv.h\"
|
||||
CFLAGS += -I$(AM_HOME)/am/src/nemu/include -I$(AM_HOME)/am/src/xs/include -DISA_H=\"riscv.h\"
|
||||
|
||||
ASFLAGS += -DMAINARGS=\"$(mainargs)\"
|
||||
.PHONY: $(AM_HOME)/am/src/nemu/common/mainargs.S
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
include $(AM_HOME)/am/arch/isa/riscv64.mk
|
||||
|
||||
AM_SRCS := noop/isa/riscv/trm_flash.c \
|
||||
nemu/common/mainargs.S \
|
||||
noop/isa/riscv/perf.c \
|
||||
southlake/common/uartlite.c \
|
||||
nemu/isa/riscv/cte.c \
|
||||
nemu/isa/riscv/trap.S \
|
||||
nemu/isa/riscv/cte64.c \
|
||||
nemu/isa/riscv/mtime.S \
|
||||
nemu/isa/riscv/vme.c \
|
||||
nemu/common/ioe.c \
|
||||
dummy/input.c \
|
||||
nemu/common/timer.c \
|
||||
dummy/video.c \
|
||||
dummy/audio.c \
|
||||
noop/isa/riscv/instr.c \
|
||||
xs/isa/riscv/mpe.c \
|
||||
xs/isa/riscv/clint.c \
|
||||
xs/isa/riscv/pmp.c \
|
||||
xs/isa/riscv/plic.c \
|
||||
xs/isa/riscv/pma.c \
|
||||
xs/isa/riscv/cache.c \
|
||||
nemu/isa/riscv/boot/start_flash.S
|
||||
|
||||
CFLAGS += -I$(AM_HOME)/am/src/nemu/include -I$(AM_HOME)/am/src/xs/include -DISA_H=\"riscv.h\" -DNOPRINT
|
||||
|
||||
ASFLAGS += -DMAINARGS=\"$(mainargs)\"
|
||||
.PHONY: $(AM_HOME)/am/src/nemu/common/mainargs.S
|
||||
|
||||
LDFLAGS += -T $(AM_HOME)/am/src/southlake/ldscript/loaderflash.ld
|
||||
|
||||
image:
|
||||
@echo + LD "->" $(BINARY_REL).elf
|
||||
@$(LD) $(LDFLAGS) --gc-sections -o $(BINARY).elf --start-group $(LINK_FILES) --end-group
|
||||
@$(OBJDUMP) -d $(BINARY).elf > $(BINARY).txt
|
||||
@echo + OBJCOPY "->" $(BINARY_REL).bin
|
||||
@$(OBJCOPY) -S --set-section-flags .bss=alloc,contents -O binary $(BINARY).elf $(BINARY).bin
|
||||
@$(OBJCOPY) -S --set-section-flags .bss=alloc,contents --adjust-vma=-0x40000000 -O verilog $(BINARY).elf $(BINARY).bin.txt
|
||||
|
||||
NEMU_ARGS = --batch --log=$(shell dirname $(BINARY))/nemu-log.txt $(BINARY).bin
|
||||
|
||||
run:
|
||||
$(MAKE) -C $(NEMU_HOME) ISA=$(ISA) run ARGS="$(NEMU_ARGS)"
|
|
@ -0,0 +1,41 @@
|
|||
include $(AM_HOME)/am/arch/isa/riscv64.mk
|
||||
|
||||
AM_SRCS := noop/isa/riscv/trm.c \
|
||||
nemu/common/mainargs.S \
|
||||
noop/isa/riscv/perf.c \
|
||||
southlake/common/uartlite.c \
|
||||
nemu/isa/riscv/cte.c \
|
||||
nemu/isa/riscv/trap.S \
|
||||
nemu/isa/riscv/cte64.c \
|
||||
nemu/isa/riscv/mtime.S \
|
||||
nemu/isa/riscv/vme.c \
|
||||
nemu/common/ioe.c \
|
||||
dummy/input.c \
|
||||
nemu/common/timer.c \
|
||||
dummy/video.c \
|
||||
dummy/audio.c \
|
||||
noop/isa/riscv/instr.c \
|
||||
xs/isa/riscv/mpe.c \
|
||||
xs/isa/riscv/clint.c \
|
||||
xs/isa/riscv/pmp.c \
|
||||
xs/isa/riscv/plic.c \
|
||||
xs/isa/riscv/pma.c \
|
||||
xs/isa/riscv/cache.c \
|
||||
nemu/isa/riscv/boot/start.S
|
||||
|
||||
CFLAGS += -I$(AM_HOME)/am/src/nemu/include -I$(AM_HOME)/am/src/xs/include -DISA_H=\"riscv.h\" -DNOPRINT
|
||||
|
||||
ASFLAGS += -DMAINARGS=\"$(mainargs)\"
|
||||
.PHONY: $(AM_HOME)/am/src/nemu/common/mainargs.S
|
||||
|
||||
LDFLAGS += -T $(AM_HOME)/am/src/southlake/ldscript/loadermem.ld
|
||||
|
||||
image:
|
||||
@echo + LD "->" $(BINARY_REL).elf
|
||||
@$(LD) $(LDFLAGS) --gc-sections -o $(BINARY).elf --start-group $(LINK_FILES) --end-group
|
||||
@$(OBJDUMP) -d $(BINARY).elf > $(BINARY).txt
|
||||
@echo + OBJCOPY "->" $(BINARY_REL).bin
|
||||
@$(OBJCOPY) -S --set-section-flags .bss=alloc,contents -O binary $(BINARY).elf $(BINARY).bin
|
||||
|
||||
run:
|
||||
$(MAKE) -C $(NOOP_HOME) emu-run IMAGE="$(BINARY).bin" DATAWIDTH=64
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef __ARCH_H__
|
||||
#include "riscv64-nemu.h"
|
||||
|
||||
#define MAP(c, f) c(f)
|
||||
|
||||
#define COUNTERS(f) \
|
||||
f(cycle) f(time) f(instr)
|
||||
|
||||
#define CNT_IDX(cnt) PERFCNT_##cnt
|
||||
#define CNT_ENUM_ITEM(cnt) CNT_IDX(cnt),
|
||||
enum {
|
||||
MAP(COUNTERS, CNT_ENUM_ITEM)
|
||||
NR_PERFCNT,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
struct { uint32_t lo, hi; };
|
||||
int64_t val;
|
||||
} cnts[NR_PERFCNT];
|
||||
} PerfCntSet;
|
||||
|
||||
void __am_perfcnt_read(PerfCntSet *t);
|
||||
void __am_perfcnt_sub(PerfCntSet *res, PerfCntSet *t1, PerfCntSet *t0);
|
||||
void __am_perfcnt_add(PerfCntSet *res, PerfCntSet *t1, PerfCntSet *t0);
|
||||
void __am_perfcnt_show(PerfCntSet *t);
|
||||
void __am_perfcnt_excel(PerfCntSet *t);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef __ARCH_H__
|
||||
#include "riscv64-nemu.h"
|
||||
|
||||
#define MAP(c, f) c(f)
|
||||
|
||||
#define COUNTERS(f) \
|
||||
f(cycle) f(time) f(instr)
|
||||
|
||||
#define CNT_IDX(cnt) PERFCNT_##cnt
|
||||
#define CNT_ENUM_ITEM(cnt) CNT_IDX(cnt),
|
||||
enum {
|
||||
MAP(COUNTERS, CNT_ENUM_ITEM)
|
||||
NR_PERFCNT,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
struct { uint32_t lo, hi; };
|
||||
int64_t val;
|
||||
} cnts[NR_PERFCNT];
|
||||
} PerfCntSet;
|
||||
|
||||
void __am_perfcnt_read(PerfCntSet *t);
|
||||
void __am_perfcnt_sub(PerfCntSet *res, PerfCntSet *t1, PerfCntSet *t0);
|
||||
void __am_perfcnt_add(PerfCntSet *res, PerfCntSet *t1, PerfCntSet *t0);
|
||||
void __am_perfcnt_show(PerfCntSet *t);
|
||||
void __am_perfcnt_excel(PerfCntSet *t);
|
||||
|
||||
#endif
|
|
@ -25,6 +25,9 @@
|
|||
# define SCREEN_ADDR 0x40001000
|
||||
# define SYNC_ADDR 0x40001004
|
||||
# define FB_ADDR 0x50000000
|
||||
#elif defined(__ARCH_RISCV64_XS_SOUTHLAKE) || defined(__ARCH_RISCV64_XS_SOUTHLAKE_FLASH)
|
||||
# define RTC_ADDR 0x1f1000bff8
|
||||
// CLINT 0x1f00000000
|
||||
#else
|
||||
# define SERIAL_PORT 0xa10003f8
|
||||
# define KBD_ADDR 0xa1000060
|
||||
|
@ -41,9 +44,6 @@
|
|||
# define AUDIO_SBUF_ADDR 0xa0800000
|
||||
#endif
|
||||
|
||||
#define MMIO_BASE 0xa0000000
|
||||
#define MMIO_SIZE 0x10000000
|
||||
|
||||
extern char _pmem_start, _pmem_end;
|
||||
|
||||
#define NEMU_PADDR_SPACE \
|
||||
|
|
|
@ -14,7 +14,10 @@ void __am_switch(_Context *c);
|
|||
|
||||
_Context* (*interrupt_handler[INTERRUPT_CAUSE_SIZE])(_Event *ev, _Context *c);
|
||||
_Context* (*exception_handler[EXCEPTION_CAUSE_SIZE])(_Event *ev, _Context *c);
|
||||
|
||||
/*
|
||||
* default handler for all possible irqs
|
||||
* just panic
|
||||
*/
|
||||
_Context* __am_irq_default_handler(_Event *ev, _Context *c) {
|
||||
printf("unregisted irq detected, scause=%d, sepc=%llx\n", c->scause, c->sepc);
|
||||
ev->event = _EVENT_ERROR;
|
||||
|
@ -22,14 +25,20 @@ _Context* __am_irq_default_handler(_Event *ev, _Context *c) {
|
|||
// should never reach here
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* default handler for Supervisor Timer Interrupt
|
||||
* set event to IRQ_TIMER
|
||||
* may call custom timer handler if registered
|
||||
*/
|
||||
_Context* __am_irq_STIP_handler(_Event *ev, _Context *c) {
|
||||
#if __riscv_xlen == 64
|
||||
asm volatile ("csrwi sip, 0");
|
||||
#endif
|
||||
printf("inside irq STIP handler\n");
|
||||
// printf("inside irq STIP handler\n");
|
||||
ev->event = _EVENT_IRQ_TIMER;
|
||||
if (custom_timer_handler != NULL) {
|
||||
printf("dive into custom timer handler");
|
||||
// printf("dive into custom timer handler");
|
||||
custom_timer_handler(*ev, c);
|
||||
}
|
||||
// machine mode will clear stip
|
||||
|
@ -37,6 +46,12 @@ _Context* __am_irq_STIP_handler(_Event *ev, _Context *c) {
|
|||
// printf("STIP handler finished\n");
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* default handler for Supervisor External Interrupt
|
||||
* set event to IEQ_IODEV
|
||||
* may call custom external handler if registered
|
||||
*/
|
||||
_Context* __am_irq_SEIP_handler(_Event *ev, _Context *c) {
|
||||
// WARNING: this has no effect since in S mode only SSIP can be cleared.
|
||||
// It's not deleted because we want to test sip write mask.
|
||||
|
@ -48,12 +63,17 @@ _Context* __am_irq_SEIP_handler(_Event *ev, _Context *c) {
|
|||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* default handler for Supervisor Ecall Exception
|
||||
* set event to YIELD or SYSCALL according to a7
|
||||
* may call custom secall handler if registered
|
||||
*/
|
||||
_Context* __am_irq_SECALL_handler(_Event *ev, _Context *c) {
|
||||
ev->event = (c->GPR1 == -1) ? _EVENT_YIELD : _EVENT_SYSCALL;
|
||||
c->sepc += 4;
|
||||
//if (ev->event == _EVENT_YIELD)
|
||||
// printf("SECALL: is YIELD\n");
|
||||
printf("Inside secall handler\n");
|
||||
// printf("Inside secall handler\n");
|
||||
if (custom_secall_handler != NULL) {
|
||||
custom_secall_handler(*ev, c);
|
||||
}
|
||||
|
@ -88,16 +108,35 @@ _Context* __am_irq_handle(_Context *c) {
|
|||
|
||||
extern void __am_asm_trap(void);
|
||||
|
||||
/*
|
||||
* Supervisor timer interrupt custom handler register function
|
||||
* handler: the function to be registered
|
||||
*/
|
||||
void stip_handler_reg(_Context*(*handler)(_Event, _Context*)) {
|
||||
custom_timer_handler = handler;
|
||||
}
|
||||
|
||||
/*
|
||||
* Supervisor external interrupt custom handler register function
|
||||
* handler: the function to be registered
|
||||
*/
|
||||
void seip_handler_reg(_Context*(*handler)(_Event, _Context*)) {
|
||||
custom_external_handler = handler;
|
||||
}
|
||||
|
||||
/*
|
||||
* Supervisor ecall exception custom handler register function
|
||||
* handler: the function to be registered
|
||||
*/
|
||||
void secall_handler_reg(_Context*(*handler)(_Event, _Context*)) {
|
||||
custom_secall_handler = handler;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic interrupt/exception CUSTOM handler register function
|
||||
* code: scause code
|
||||
* handler: the function to be registered
|
||||
*/
|
||||
void custom_handler_reg(uintptr_t code, _Context*(*handler)(_Event, _Context*)) {
|
||||
switch (code) {
|
||||
#if __riscv_xlen == 64
|
||||
|
@ -118,6 +157,11 @@ void custom_handler_reg(uintptr_t code, _Context*(*handler)(_Event, _Context*))
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic interrupt/exception handler register function
|
||||
* code: scause code
|
||||
* handler: the function to be registered
|
||||
*/
|
||||
void irq_handler_reg(uintptr_t code, _Context*(*handler)(_Event*, _Context*)) {
|
||||
uintptr_t offset = (code << 1) >> 1;
|
||||
if (INTR_BIT & code) {
|
||||
|
@ -143,7 +187,8 @@ int _cte_init(_Context *(*handler)(_Event ev, _Context *ctx)) {
|
|||
__am_init_cte64();
|
||||
#endif
|
||||
for (int i = 0; i < INTERRUPT_CAUSE_SIZE; i++) {
|
||||
interrupt_handler[i] = __am_irq_default_handler;
|
||||
irq_handler_reg(INTR_BIT | i, __am_irq_default_handler);
|
||||
// interrupt_handler[i] = __am_irq_default_handler;
|
||||
}
|
||||
for (int i = 0; i < EXCEPTION_CAUSE_SIZE; i++) {
|
||||
exception_handler[i] = __am_irq_default_handler;
|
||||
|
|
|
@ -8,7 +8,7 @@ static void init_machine_exception() {
|
|||
asm volatile("csrw mtvec, %0" : : "r"(__am_timervec));
|
||||
}
|
||||
|
||||
|
||||
int g_config_disable_timer = 0; // dirty hack of __am_init_cte64(), to be refactored
|
||||
extern void init_timer();
|
||||
extern void enable_timer();
|
||||
extern void init_pmp();
|
||||
|
@ -32,16 +32,29 @@ void __am_init_cte64() {
|
|||
// asm volatile("csrw pmpcfg2, %0" : : "r"(31));
|
||||
|
||||
init_pmp();
|
||||
#if defined(__ARCH_RISCV64_NOOP) || defined(__ARCH_RISCV32_NOOP) || defined(__ARCH_RISCV64_XS)
|
||||
// protect 0x90000000 + 0x10000 for test purpose
|
||||
enable_pmp(1, 0x90000000, 0x10000, 0, 0);
|
||||
// printf("pmp NA inited\n");
|
||||
// protect 0xb00000000 + 0x100
|
||||
enable_pmp_TOR(4, 0xb0000000, 0x100, 0, 0);
|
||||
//printf("pmp TOR inited\n");
|
||||
#elif defined(__ARCH_RISCV64_XS_SOUTHLAKE) || defined(__ARCH_RISCV64_XS_SOUTHLAKE_FLASH)
|
||||
// protect 0x210000000 + 0x10000 for test purpose
|
||||
enable_pmp(1, 0x2010000000, 0x10000, 0, 0);
|
||||
// printf("pmp NA inited\n");
|
||||
// protect 0x240000000 + 0x100
|
||||
enable_pmp_TOR(4, 0x2040000000, 0x100, 0, 0);
|
||||
//printf("pmp TOR inited\n");
|
||||
#else
|
||||
// invalid arch
|
||||
#endif
|
||||
|
||||
init_machine_exception();
|
||||
init_timer();
|
||||
enable_timer();
|
||||
if(!g_config_disable_timer){
|
||||
enable_timer();
|
||||
}
|
||||
init_eip();
|
||||
|
||||
// enter S-mode
|
||||
|
|
|
@ -17,8 +17,14 @@ static const _Area segments[] = { // Kernel memory mappings
|
|||
RANGE_LEN(CLINT_MMIO, 0x10000), // clint/timer
|
||||
RANGE_LEN(FB_ADDR, 0x400000), // vmem
|
||||
RANGE_LEN(SCREEN_ADDR,0x1000), // vmem
|
||||
RANGE_LEN(0x3c000000, 0x4000000), // PLIC
|
||||
RANGE_LEN(0xc0000000, 0x100000), // page table test allocates from this position
|
||||
RANGE_LEN(0x3c000000, 0x4000000), // PLIC
|
||||
RANGE_LEN(0xc0000000, 0x100000), // page table test allocates from this position
|
||||
#elif defined(__ARCH_RISCV64_XS_SOUTHLAKE) || defined(__ARCH_RISCV64_XS_SOUTHLAKE_FLASH)
|
||||
RANGE_LEN(0x2000000000, 0x8000000), // PMEM
|
||||
RANGE_LEN(0x1f00050000, 0x1000), // uart
|
||||
// RANGE_LEN(CLINT_MMIO, 0x10000), // clint/timer
|
||||
// RANGE_LEN(0x1f0c000000, 0x4000000), // PLIC
|
||||
RANGE_LEN(0x2040000000, 0x100000), // page table test allocates from this position
|
||||
#else
|
||||
NEMU_PADDR_SPACE,
|
||||
#if __riscv_xlen == 64
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
// uart IO is disabled to speed up SoC test
|
||||
|
||||
#include <riscv.h>
|
||||
#include <klib.h>
|
||||
|
||||
#define UARTLITE_MMIO 0x1f00050000
|
||||
#define UARTLITE_RX_FIFO 0x0
|
||||
#define UARTLITE_TX_FIFO 0x4
|
||||
#define UARTLITE_STAT_REG 0x8
|
||||
#define UARTLITE_CTRL_REG 0xc
|
||||
|
||||
#define UARTLITE_RST_FIFO 0x03
|
||||
#define UARTLITE_TX_FULL 0x08
|
||||
#define UARTLITE_RX_VALID 0x01
|
||||
|
||||
void __am_init_uartlite(void) {
|
||||
#ifndef NOPRINT
|
||||
outb(UARTLITE_MMIO + UARTLITE_CTRL_REG, UARTLITE_RST_FIFO);
|
||||
#endif
|
||||
}
|
||||
|
||||
void __am_uartlite_putchar(char ch) {
|
||||
#ifndef NOPRINT
|
||||
if (ch == '\n') __am_uartlite_putchar('\r');
|
||||
|
||||
while (inb(UARTLITE_MMIO + UARTLITE_STAT_REG) & UARTLITE_TX_FULL);
|
||||
outb(UARTLITE_MMIO + UARTLITE_TX_FIFO, ch);
|
||||
#else
|
||||
assert(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
int __am_uartlite_getchar() {
|
||||
#ifndef NOPRINT
|
||||
if (inb(UARTLITE_MMIO + UARTLITE_STAT_REG) & UARTLITE_RX_VALID)
|
||||
return inb(UARTLITE_MMIO + UARTLITE_RX_FIFO);
|
||||
return 0;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
MEMORY {
|
||||
FLASH (rxa) : ORIGIN = 0x1ffff80000, LENGTH = 16M
|
||||
RAM (rwxa) : ORIGIN = 0x2000000000, LENGTH = 64M
|
||||
}
|
||||
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS {
|
||||
.text :
|
||||
{
|
||||
*(entry)
|
||||
_mainargs = .;
|
||||
*(.text)
|
||||
} >FLASH
|
||||
etext = .;
|
||||
_etext = .;
|
||||
|
||||
.rodata :
|
||||
{
|
||||
*(.rodata*)
|
||||
} >FLASH
|
||||
|
||||
.data :
|
||||
{
|
||||
sdata = .;
|
||||
*(.data)
|
||||
*(.sdata)
|
||||
edata = .;
|
||||
} >RAM AT > FLASH
|
||||
_data = .;
|
||||
_sidata = LOADADDR(.data);
|
||||
|
||||
.bss :
|
||||
{
|
||||
_bss_start = .;
|
||||
*(.bss*)
|
||||
*(.sbss*)
|
||||
*(.scommon)
|
||||
_bss_end = .;
|
||||
} >RAM AT > FLASH
|
||||
_sibss = LOADADDR(.bss);
|
||||
end = .;
|
||||
_end = .;
|
||||
|
||||
ram_start = .;
|
||||
|
||||
. = ORIGIN(RAM);
|
||||
_stack_top = ALIGN(0x1000);
|
||||
. = _stack_top + 0x8000;
|
||||
_stack_pointer = .;
|
||||
end = .;
|
||||
_end = .;
|
||||
_heap_start = ALIGN(0x1000);
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
pmem_base = 0x2000000000;
|
||||
|
||||
MEMORY {
|
||||
ram (rwxa) : ORIGIN = 0x2000000000, LENGTH = 1024M
|
||||
}
|
||||
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS {
|
||||
. = ORIGIN(ram);
|
||||
.text : {
|
||||
*(entry)
|
||||
*(.text)
|
||||
}
|
||||
etext = .;
|
||||
_etext = .;
|
||||
.rodata : {
|
||||
*(.rodata*)
|
||||
}
|
||||
.data : {
|
||||
*(.data)
|
||||
}
|
||||
edata = .;
|
||||
_data = .;
|
||||
.bss : {
|
||||
_bss_start = .;
|
||||
*(.bss*)
|
||||
*(.sbss*)
|
||||
*(.scommon)
|
||||
}
|
||||
_stack_top = ALIGN(0x1000);
|
||||
. = _stack_top + 0x8000;
|
||||
_stack_pointer = .;
|
||||
end = .;
|
||||
_end = .;
|
||||
_heap_start = ALIGN(0x1000);
|
||||
_pmem_start = pmem_base;
|
||||
_pmem_end = _pmem_start + LENGTH(ram);
|
||||
}
|
|
@ -7,6 +7,18 @@
|
|||
|
||||
#include ISA_H // "x86.h", "mips32.h", ...
|
||||
|
||||
#if defined(__ARCH_RISCV64_NOOP) || defined(__ARCH_RISCV32_NOOP) || defined(__ARCH_RISCV64_XS) || defined(__ARCH_RISCV64_XS_FLASH)
|
||||
#define INTR_GEN_ADDR (0x40070000UL)
|
||||
#define INTR_RANDOM (0x40070008UL)
|
||||
#define INTR_RANDOM_MASK (0x40070010UL)
|
||||
#define PLIC_BASE_ADDR (0x3c000000UL)
|
||||
#elif defined(__ARCH_RISCV64_XS_SOUTHLAKE) || defined(__ARCH_RISCV64_XS_SOUTHLAKE_FLASH)
|
||||
#define INTR_GEN_ADDR (0x1f00060000UL)
|
||||
#define INTR_RANDOM (0x1f00060008UL)
|
||||
#define INTR_RANDOM_MASK (0x1f00060010UL)
|
||||
#define PLIC_BASE_ADDR (0x1f1c000000UL)
|
||||
#endif
|
||||
|
||||
extern int __am_ncpu;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -9,9 +9,9 @@ typedef struct {
|
|||
|
||||
ClintInfo timer_handle;
|
||||
|
||||
#if defined(__ARCH_RISCV64_NOOP) || defined(__ARCH_RISCV64_XS)
|
||||
#if defined(__ARCH_RISCV64_NOOP) || defined(__ARCH_RISCV64_XS) || defined(__ARCH_RISCV64_XS_SOUTHLAKE) || defined(__ARCH_RISCV64_XS_SOUTHLAKE_FLASH)
|
||||
#define CLINT_MMIO (RTC_ADDR - 0xbff8)
|
||||
#define TIME_INC 0x80000
|
||||
#define TIME_INC 0x800
|
||||
#else
|
||||
#define CLINT_MMIO 0xa2000000
|
||||
#define TIME_INC 0x800
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
#include <am.h>
|
||||
#include <xs.h>
|
||||
|
||||
#define READ_WORD(addr) (*((volatile uint32_t *)(addr)))
|
||||
#define WRITE_WORD(addr, data) (*((volatile uint32_t *)(addr)) = (data))
|
||||
#define EXTRACT_BIT(data, i) ((data) & (0x1UL << (i)))
|
||||
#define SET_BIT(data, i) ((data) | (0x1UL << (i)))
|
||||
#define CLEAR_BIT(data, i) ((data) ^ EXTRACT_BIT(data, i))
|
||||
|
||||
#define INTR_GEN_ADDR (0x40070000UL)
|
||||
#define INTR_REG_WIDTH 32
|
||||
#define INTR_REG_ADDR(i) ((INTR_GEN_ADDR) + ((i) << 2))
|
||||
#define INTR_REG_INDEX(i) INTR_REG_ADDR(((i) / INTR_REG_WIDTH))
|
||||
#define INTR_REG_OFFSET(i) ((i) % INTR_REG_WIDTH)
|
||||
|
||||
#define INTR_RANDOM (0x40070008UL)
|
||||
#define INTR_RANDOM_ADDR(i) ((INTR_RANDOM) + ((i) << 2))
|
||||
#define INTR_RANDOM_MASK (0x40070010UL)
|
||||
|
||||
#define READ_INTR_REG(i) READ_WORD(INTR_REG_ADDR(i))
|
||||
#define READ_INTR(i) EXTRACT_BIT(READ_INTR_REG(INTR_REG_INDEX(i)), INTR_REG_OFFSET(i))
|
||||
|
@ -22,7 +21,6 @@
|
|||
|
||||
#define CONTEXT_M 0
|
||||
#define CONTEXT_S 1
|
||||
#define PLIC_BASE_ADDR (0x3c000000UL)
|
||||
#define PLIC_PRIORITY (PLIC_BASE_ADDR + 0x4UL)
|
||||
#define PLIC_PENDING (PLIC_BASE_ADDR + 0x1000UL)
|
||||
#define PLIC_ENABLE(c) (PLIC_BASE_ADDR + 0x2000UL + c*0x80UL)
|
||||
|
|
|
@ -66,6 +66,7 @@ void stip_handler_reg(_Context*(*handler)(_Event, _Context*));
|
|||
void seip_handler_reg(_Context*(*handler)(_Event, _Context*));
|
||||
void secall_handler_reg(_Context*(*handler)(_Event, _Context*));
|
||||
void custom_handler_reg(uintptr_t code, _Context*(*handler)(_Event, _Context*));
|
||||
void irq_handler_reg(uintptr_t code, _Context*(*handler)(_Event*, _Context*));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
NAME = dummy-bootrom
|
||||
SRCS = main.c
|
||||
|
||||
include $(AM_HOME)/Makefile.app
|
|
@ -0,0 +1,9 @@
|
|||
#include "klib.h"
|
||||
|
||||
extern uint8_t ram_start;
|
||||
|
||||
int main() {
|
||||
void (*f)(void) = (void *)&ram_start;
|
||||
f();
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
NAME = hello
|
||||
SRCS = hello.c
|
||||
include $(AM_HOME)/Makefile.app
|
|
@ -0,0 +1,7 @@
|
|||
#include <klib.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("Hello, XiangShan!\n");
|
||||
return 0;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
NAME = flash-loader
|
||||
SRCS = main.c image.S
|
||||
ELF_PATH = ./image/coremark-riscv64-xs.bin
|
||||
ELF_PATH ?= ./image/coremark-riscv64-xs.bin
|
||||
ASFLAGS += -DELF_PATH=\"$(ELF_PATH)\"
|
||||
|
||||
image.S: $(ELF_PATH)
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,33 @@
|
|||
# 异常处理与Handler注册
|
||||
|
||||
## 异常处理流程
|
||||
|
||||
### RISC-V异常处理模型概述
|
||||
|
||||
RISC-V处理器的异常在默认情况下一律陷入到machine态,machine态软件视情况决定直接处理或转交supervisor态处理。为减少整体trap次数以提升常见中断异常的处理速度,RISC-V还提供了中断和异常代理功能。符合代理条件的异常可不经machine态直接转交supervisor态处理。
|
||||
|
||||
### AM在该异常处理模型下的实现细节
|
||||
|
||||
AM在RISC-V处理器上运行时,复位在machine态,在完成machine态的异常handler设置及异常代理、CLINT、PMP等必须在machine态完成的初始化设置后,进入supervisor态并设置supervisor态的异常handler。
|
||||
|
||||
machine态的异常handler实际仅处理时钟中断和非法指令两种异常。时钟中断异常在machine态利用更新mtimecmp csr的方式清除后,machine态handler会主动设置supervisor态的外部中断异常以便supervisor态软件得知中断触发并处理。考虑到supervisor态无法主动清除machine态所设置的supervisor态外部中断异常信号,为其在machine态提供了一个利用非法指令清除外部中断信号的方法。machine态handler在检测到非法指令异常时,会将supervisor态外部异常信号清空并返回触发异常指令的下一条指令执行。
|
||||
|
||||
supervisor态是AM用户接触最多的状态。为兼顾通用性及各功能处理由自身产生的各项异常需求,AM在supervisor态提供了分两层的运行时动态注册异常handler功能。
|
||||
|
||||
具体地,supervisor态异常handler在保存现场后会进入C语言`__am_irq_handle`函数,该函数根据scause寄存器最高位判断异常是否为中断并分别进入异常和中断handler表中对应cause所注册的异常handler。用户可使用`irq_handler_reg`函数注册相应handler。该级handler是可由用户主动注册的第一级handler。在默认情况下,时钟中断handler被注册为`__am_irq_STIP_handler`,外部中断handler被注册为`__am_irq_SEIP_handler`,ecall异常handler被注册为`__am_irq_SECALL_handler`,其他类型异常handler被注册为`__am_irq_default_handler`。
|
||||
|
||||
考虑到amtest中大量测试需要主动触发外部中断、ecall异常并自行对异常做进一步的处理,SEIP、SECALL、STIP三个handler还进一步提供了自定义handler注册机制。用户可使用`custom_handler_reg`函数按照异常编号进行自定义handler注册或直接调用`seip_handler_reg`、`secall_handler_reg`及`stip_handler_reg`函数注册相应handler。这类handler一旦注册,将会在默认handler执行结束后被调用。这一级handler是可由用户主动注册的第二级handler。
|
||||
|
||||
## handler注册示例与说明
|
||||
|
||||
`test/amtest`目录下的测试充分利用了上述机制,可用于参考。
|
||||
|
||||
main.c中在调用每个具体测试前,首先使用CTE宏完成中断机制初始化,而后利用REEH、RCEH、RTEH等宏分别调用外部中断、ecall异常和时钟中断的自定义handler注册,最终再调用测试本身。
|
||||
|
||||
此外,`src/nemu/isa/riscv/cte.c`中`_cte_init`函数对`irq_handler_reg`函数的调用可用作该函数用法参考。
|
||||
|
||||
### handler实现需求
|
||||
|
||||
第一级handler执行前,supervisor态软件并未对该异常做任何具体处理,一切与此异常处理具体相关的操作都要在该handler内实现完毕,通常包括清除中断信号(以避免重复进入该异常的处理)、设置相关event、设置特权态csr等。第二级handler执行前,supervisor态软件往往已对该异常进行了必要处理并生成了该异常具体原因(event),二级handler一般仅需要根据该event信息修改部分全局变量值、进行某些输出等操作。
|
||||
|
||||
第一级handler可参考`src/nemu/isa/riscv/cte.c`中`__am_irq_SEIP_handler`实现,第二级handler可参考`tests/amtest/tests/intr.c`中`simple_trap`实现。
|
|
@ -858,7 +858,7 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const
|
|||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef NOPRINT
|
||||
int printf_(const char* format, ...)
|
||||
{
|
||||
va_list va;
|
||||
|
@ -868,6 +868,12 @@ int printf_(const char* format, ...)
|
|||
va_end(va);
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
int printf_(const char* format, ...)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int sprintf_(char* buffer, const char* format, ...)
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#define REEH(h) ({ _Context *h(_Event, _Context *); seip_handler_reg(h);})
|
||||
#define RTEH(h) ({ _Context *h(_Event, _Context *); stip_handler_reg(h);})
|
||||
#define RCEH(h) ({ _Context *h(_Event, _Context *); secall_handler_reg(h);})
|
||||
#define NOTIMEINT() ({ extern int g_config_disable_timer; g_config_disable_timer = 1;})
|
||||
|
||||
#define VME(f1, f2) ({ void *f1(size_t); void f2(void *); _vme_init(f1, f2); })
|
||||
#define PRE_MPE(arg) ({ _mpe_setncpu(arg); })
|
||||
|
|
|
@ -20,9 +20,10 @@ static const char *tests[256] = {
|
|||
|
||||
int main(const char *args) {
|
||||
switch (args[0]) {
|
||||
CASE('x', dma_test);
|
||||
CASE('h', hello);
|
||||
CASE('i', hello_intr, IOE, CTE(simple_trap), REEH(simple_trap), RCEH(simple_trap), RTEH(simple_trap));
|
||||
CASE('e', external_intr, IOE, CTE(external_trap), REEH(external_trap), RTEH(external_trap));
|
||||
CASE('e', external_intr, IOE, NOTIMEINT(), CTE(external_trap), REEH(external_trap), RTEH(external_trap));
|
||||
CASE('d', devscan, IOE);
|
||||
CASE('m', finalize, PRE_MPE(args[1]), MPE(mp_print));
|
||||
CASE('t', rtc_test, IOE);
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
#include <amtest.h>
|
||||
#include <xs.h>
|
||||
|
||||
enum {
|
||||
s_idle,
|
||||
s_read,
|
||||
s_write,
|
||||
s_wait_resp_b,
|
||||
s_wait_resp_r0,
|
||||
s_wait_resp_r1
|
||||
};
|
||||
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
uint64_t value : 8;
|
||||
uint64_t reserved : 56;
|
||||
} dma_state;
|
||||
|
||||
typedef struct {
|
||||
uint64_t data[8];
|
||||
dma_state state;
|
||||
uint64_t address;
|
||||
uint64_t mask;
|
||||
uint64_t reserved[5];
|
||||
} dma_mshr;
|
||||
|
||||
// CHANGE this according to the memory map
|
||||
volatile dma_mshr *mshr = (volatile dma_mshr *)0x1f00070000UL;
|
||||
volatile uint64_t *mshr_valid = (volatile uint64_t *)0x1f00072000UL;
|
||||
|
||||
// 4GB - 6GB
|
||||
volatile uint8_t *memory = (volatile uint8_t *)0x2100000000UL;
|
||||
// 6GB - 8GB
|
||||
volatile uint8_t *ref_memory = (volatile uint8_t *)0x2180000000UL;
|
||||
|
||||
inline void riscv_fence() {
|
||||
asm volatile("fence");
|
||||
}
|
||||
|
||||
uint64_t random_number() {
|
||||
static uint64_t count = 1;
|
||||
count++;
|
||||
uint64_t x = ((count >> 16) ^ count) * 0x45d9f3b;
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3b;
|
||||
x = (x >> 16) ^ x;
|
||||
return x;
|
||||
}
|
||||
|
||||
inline uint64_t random_memory_offset() {
|
||||
// max 16MB
|
||||
return random_number() % 0x1000000UL;
|
||||
}
|
||||
|
||||
void dma_test() {
|
||||
assert(sizeof(dma_state) == 8);
|
||||
assert(sizeof(dma_mshr) == 16 * 8);
|
||||
printf("Starting DMA Test\n");
|
||||
printf("Setting memory\n");
|
||||
// randomly touch 256K * 64B = 8MB memory
|
||||
for (int i = 0; i < 64; i++) {
|
||||
uint64_t offset = random_memory_offset();
|
||||
uint64_t rand_num = random_number();
|
||||
memory[offset] = rand_num;
|
||||
ref_memory[offset] = rand_num;
|
||||
mshr[i].data[3] = rand_num;
|
||||
mshr[i].data[5] = rand_num;
|
||||
mshr[i].state.value = s_write;
|
||||
uint64_t address = (uint64_t)(memory + offset) ^ ((uint64_t)(memory + offset) & 0x3f);
|
||||
mshr[i].address = (uint64_t)(memory + offset) ^ ((uint64_t)(memory + offset) & 0x3f);
|
||||
assert(address == mshr[i].address);
|
||||
mshr[i].mask = 0xff00ff000000UL;
|
||||
volatile uint64_t *t = (volatile uint64_t *)(mshr[i].address + (uint64_t)(ref_memory - memory));
|
||||
t[3] = rand_num;
|
||||
t[5] = rand_num;
|
||||
}
|
||||
riscv_fence();
|
||||
printf("Finished setting memory. Starting DMA write.\n");
|
||||
*mshr_valid = 0xffffffffffffffffUL;
|
||||
bool mshr_cleared = false;
|
||||
while (!mshr_cleared) {
|
||||
mshr_cleared = true;
|
||||
for (int i = 0; i < 64; i++) {
|
||||
if (mshr[i].state.value) {
|
||||
mshr_cleared = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("Finished DMA write. Starting CPU read.\n");
|
||||
for (int i = 0; i < 64; i++) {
|
||||
uint64_t base_offset = (uint64_t *)mshr[i].address - (uint64_t *)memory;
|
||||
volatile uint64_t *golden = (uint64_t *)ref_memory + base_offset;
|
||||
volatile uint64_t *dut = (uint64_t *)memory + base_offset;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
uint64_t dut_data = dut[j];
|
||||
uint64_t ref_data = golden[j];
|
||||
if (dut_data != ref_data) {
|
||||
printf("[ERROR ] Test %d at offset %d: DUT(0x%016lx) != REF(0x%016lx) at address 0x%lx and 0x%lx\n",
|
||||
i, j, dut_data, ref_data, dut + j, golden + j);
|
||||
_halt(1);
|
||||
}
|
||||
else {
|
||||
// printf("[SUCCESS] Test %d at offset %d: DUT(0x%016lx) == REF(0x%016lx) at address 0x%lx and 0x%lx\n",
|
||||
// i, j, dut_data, ref_data, dut + j, golden + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
*mshr_valid = 0x0UL;
|
||||
printf("All tests passed.\n");
|
||||
}
|
|
@ -7,15 +7,12 @@
|
|||
#define SET_BIT(data, i) ((data) | (0x1UL << (i)))
|
||||
#define CLEAR_BIT(data, i) ((data) ^ EXTRACT_BIT(data, i))
|
||||
|
||||
#define INTR_GEN_ADDR (0x40070000UL)
|
||||
#define INTR_REG_WIDTH 32
|
||||
#define INTR_REG_ADDR(i) ((INTR_GEN_ADDR) + ((i) << 2))
|
||||
#define INTR_REG_INDEX(i) INTR_REG_ADDR(((i) / INTR_REG_WIDTH))
|
||||
#define INTR_REG_OFFSET(i) ((i) % INTR_REG_WIDTH)
|
||||
|
||||
#define INTR_RANDOM (0x40070008UL)
|
||||
#define INTR_RANDOM_ADDR(i) ((INTR_RANDOM) + ((i) << 2))
|
||||
#define INTR_RANDOM_MASK (0x40070010UL)
|
||||
|
||||
#define READ_INTR_REG(i) READ_WORD(INTR_REG_ADDR(i))
|
||||
#define READ_INTR(i) EXTRACT_BIT(READ_INTR_REG(INTR_REG_INDEX(i)), INTR_REG_OFFSET(i))
|
||||
|
@ -24,7 +21,6 @@
|
|||
|
||||
#define CONTEXT_M 0
|
||||
#define CONTEXT_S 1
|
||||
#define PLIC_BASE_ADDR (0x3c000000UL)
|
||||
#define PLIC_PRIORITY (PLIC_BASE_ADDR + 0x4UL)
|
||||
#define PLIC_PENDING (PLIC_BASE_ADDR + 0x1000UL)
|
||||
#define PLIC_ENABLE(c) (PLIC_BASE_ADDR + 0x2000UL + c*0x80UL)
|
||||
|
@ -126,8 +122,11 @@ void external_trigger(bool shall_trigger, bool wfi, int context) {
|
|||
printf("should trigger: %s\n", shall_trigger ? "Yes" : "No");
|
||||
current_context = context;
|
||||
should_trigger = shall_trigger;
|
||||
|
||||
#if defined(__ARCH_RISCV64_XS_SOUTHLAKE) || defined(__ARCH_RISCV64_XS_SOUTHLAKE_FLASH)
|
||||
const uint32_t MAX_RAND_ITER = 2;
|
||||
#else
|
||||
const uint32_t MAX_RAND_ITER = 1000;
|
||||
#endif
|
||||
int origin_claim;
|
||||
for (int i = 0; i < MAX_RAND_ITER; i++) {
|
||||
should_claim = (rand() % MAX_EXTERNAL_INTR) + PLIC_EXT_INTR_OFFSET;
|
||||
|
@ -140,7 +139,7 @@ void external_trigger(bool shall_trigger, bool wfi, int context) {
|
|||
}
|
||||
else {
|
||||
int counter = 0;
|
||||
while (should_claim != -1 && counter < 100) {
|
||||
while (should_claim != -1 && counter < 2000) {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
@ -212,8 +211,10 @@ void external_intr() {
|
|||
// external_trigger(true, CONTEXT_M);
|
||||
// external_trigger(false, CONTEXT_S);
|
||||
|
||||
#if !defined(__ARCH_RISCV64_XS_SOUTHLAKE) && !defined(__ARCH_RISCV64_XS_SOUTHLAKE_FLASH)
|
||||
plic_intr_init();
|
||||
random_trigger();
|
||||
#endif
|
||||
|
||||
// for (int i = 0; i < MAX_EXTERNAL_INTR + MAX_INTERNAL_INTR; i++) {
|
||||
// printf("claim_count[%d] = %lu\n", i, claim_count[i]);
|
||||
|
|
|
@ -17,10 +17,15 @@ void hello_intr() {
|
|||
printf(" t = timer, d = device, y = yield\n");
|
||||
_intr_write(1);
|
||||
// printf("hello intr written\n");
|
||||
#if defined(__ARCH_RISCV64_XS_SOUTHLAKE) || defined(__ARCH_RISCV64_XS_SOUTHLAKE_FLASH)
|
||||
for (volatile int i = 0; i < 512; i++) ;
|
||||
_yield();
|
||||
#else
|
||||
while (1) {
|
||||
for (volatile int i = 0; i < 10000000; i++) ;
|
||||
_yield();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void hello_n(int n);
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#include <amtest.h>
|
||||
#include <pmp.h>
|
||||
#include <xsextra.h>
|
||||
|
||||
/*
|
||||
* Note that to be able to run this test, PMP should be set up before AM jump to supervisor
|
||||
|
@ -7,19 +9,51 @@
|
|||
|
||||
#define PMP_1
|
||||
|
||||
#define EXCEPTION_STORE_ACCESS_FAULT 7
|
||||
|
||||
uint64_t access_fault_to_be_reported = 0;
|
||||
|
||||
_Context* store_access_fault_handler(_Event* ev, _Context *c) {
|
||||
printf("store access fault triggered\n");
|
||||
if (access_fault_to_be_reported) {
|
||||
_halt(0);
|
||||
} else {
|
||||
_halt(1); // something went wrong
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
void pmp_test() {
|
||||
irq_handler_reg(EXCEPTION_STORE_ACCESS_FAULT, &store_access_fault_handler);
|
||||
printf("start pmp test\n");
|
||||
#if defined(__ARCH_RISCV64_NOOP) || defined(__ARCH_RISCV32_NOOP) || defined(__ARCH_RISCV64_XS)
|
||||
#ifdef PMP_1
|
||||
access_fault_to_be_reported = 1;
|
||||
volatile int *a = (int *)(0x90000040UL);
|
||||
*a = 1; // should trigger a fault
|
||||
#endif
|
||||
#ifdef PMP_2
|
||||
access_fault_to_be_reported = 0;
|
||||
int *b = (int *)(0xa0000000UL);
|
||||
*b = 1; // should not trigger a fault
|
||||
#endif
|
||||
#ifdef PMP_3
|
||||
access_fault_to_be_reported = 1;
|
||||
int *c = (int *)(0xb00000040UL);
|
||||
*c = 1; // should trigger a fault
|
||||
#endif
|
||||
asm volatile("ebreak");
|
||||
#elif defined(__ARCH_RISCV64_XS_SOUTHLAKE) || defined(__ARCH_RISCV64_XS_SOUTHLAKE_FLASH)
|
||||
access_fault_to_be_reported = 0;
|
||||
int *b = (int *)(0x2030000000UL);
|
||||
*b = 1; // should not trigger a fault
|
||||
access_fault_to_be_reported = 1;
|
||||
volatile int *a = (int *)(0x2010000040UL);
|
||||
*a = 1; // should trigger a fault
|
||||
printf("Store access fault not triggered\n");
|
||||
_halt(1);
|
||||
#else
|
||||
// invalid arch
|
||||
printf("invalid arch\n");
|
||||
_halt(1);
|
||||
#endif
|
||||
}
|
|
@ -1,10 +1,49 @@
|
|||
#include <amtest.h>
|
||||
#include <pmp.h>
|
||||
#include <xsextra.h>
|
||||
|
||||
/*
|
||||
* RISC-V 64 SV39 Virutal Memory test
|
||||
*/
|
||||
|
||||
#define EXCEPTION_LOAD_PAGE_FAULT 13
|
||||
#define EXCEPTION_STORE_PAGE_FAULT 15
|
||||
|
||||
#if defined(__ARCH_RISCV64_NOOP) || defined(__ARCH_RISCV32_NOOP) || defined(__ARCH_RISCV64_XS)
|
||||
static char *sv39_alloc_base = (char *)(0xc0000000UL);
|
||||
#elif defined(__ARCH_RISCV64_XS_SOUTHLAKE) || defined(__ARCH_RISCV64_XS_SOUTHLAKE_FLASH)
|
||||
static char *sv39_alloc_base = (char *)(0x2040000000UL);
|
||||
#else
|
||||
// invalid arch
|
||||
#endif
|
||||
|
||||
uint64_t page_fault_to_be_reported = 0;
|
||||
|
||||
inline int inst_is_compressed(uint64_t addr){
|
||||
uint8_t byte = *(uint8_t*)addr;
|
||||
return (byte | 0x3) != 0x3;
|
||||
}
|
||||
|
||||
_Context* store_page_fault_handler(_Event* ev, _Context *c) {
|
||||
printf("store page fault triggered\n");
|
||||
if(!page_fault_to_be_reported){
|
||||
_halt(1); // something went wrong
|
||||
}
|
||||
page_fault_to_be_reported = 0;
|
||||
c->sepc = inst_is_compressed(c->sepc) ? c->sepc + 2: c->sepc + 4;
|
||||
return c;
|
||||
}
|
||||
|
||||
_Context* load_page_fault_handler(_Event* ev, _Context *c) {
|
||||
printf("load page fault triggered\n");
|
||||
if(!page_fault_to_be_reported){
|
||||
_halt(1); // something went wrong
|
||||
}
|
||||
page_fault_to_be_reported = 0;
|
||||
c->sepc = inst_is_compressed(c->sepc) ? c->sepc + 2: c->sepc + 4;
|
||||
return c;
|
||||
}
|
||||
|
||||
static uintptr_t sv39_alloced_size = 0;
|
||||
void* sv39_pgalloc(size_t pg_size) {
|
||||
assert(pg_size == 0x1000);
|
||||
|
@ -13,15 +52,19 @@ void* sv39_pgalloc(size_t pg_size) {
|
|||
sv39_alloced_size += pg_size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void sv39_pgfree(void *ptr) {
|
||||
return ;
|
||||
}
|
||||
|
||||
extern _AddressSpace kas;
|
||||
#include <riscv.h>
|
||||
|
||||
void sv39_test() {
|
||||
printf("start sv39 test\n");
|
||||
_vme_init(sv39_pgalloc, sv39_pgfree);
|
||||
printf("sv39 setup done\n");
|
||||
#if defined(__ARCH_RISCV64_NOOP) || defined(__ARCH_RISCV32_NOOP) || defined(__ARCH_RISCV64_XS)
|
||||
_map(&kas, (void *)0x900000000UL, (void *)0x80020000, PTE_R | PTE_A | PTE_D);
|
||||
_map(&kas, (void *)0xa00000000UL, (void *)0x80020000, PTE_W | PTE_R | PTE_A | PTE_D);
|
||||
_map(&kas, (void *)0xb00000000UL, (void *)0x80020000, PTE_A | PTE_D);
|
||||
|
@ -29,10 +72,39 @@ void sv39_test() {
|
|||
char *w_ptr = (char *)(0xa00000000UL);
|
||||
char *r_ptr = (char *)(0x900000000UL);
|
||||
char *fault_ptr = (char *)(0xb00000000UL);
|
||||
#elif defined(__ARCH_RISCV64_XS_SOUTHLAKE) || defined(__ARCH_RISCV64_XS_SOUTHLAKE_FLASH)
|
||||
_map(&kas, (void *)0x2100000000UL, (void *)0x2000020000, PTE_W | PTE_R | PTE_A | PTE_D);
|
||||
_map(&kas, (void *)0x2200000000UL, (void *)0x2000020000, PTE_R | PTE_A | PTE_D);
|
||||
_map(&kas, (void *)0x2300000000UL, (void *)0x2000020000, PTE_A | PTE_D);
|
||||
printf("memory map done\n");
|
||||
char *w_ptr = (char *)(0x2100000000UL);
|
||||
char *r_ptr = (char *)(0x2200000000UL);
|
||||
char *fault_ptr = (char *)(0x2300000000UL);
|
||||
#else
|
||||
// invalid arch
|
||||
_halt(1);
|
||||
#endif
|
||||
irq_handler_reg(EXCEPTION_STORE_PAGE_FAULT, &store_page_fault_handler);
|
||||
irq_handler_reg(EXCEPTION_LOAD_PAGE_FAULT, &load_page_fault_handler);
|
||||
|
||||
printf("test sv39 data write\n");
|
||||
*w_ptr = 'a';
|
||||
printf("sv39 data written\n");
|
||||
|
||||
printf("test sv39 data read\n");
|
||||
assert(*r_ptr == 'a');
|
||||
printf("triggering fault\n");
|
||||
*fault_ptr = 'b';
|
||||
printf("should not reach here!\n");
|
||||
|
||||
printf("test sv39 store page fault\n");
|
||||
page_fault_to_be_reported = 1;
|
||||
*fault_ptr = 'b'; // store: not compressed
|
||||
if(page_fault_to_be_reported){
|
||||
_halt(1);
|
||||
}
|
||||
|
||||
printf("test sv39 load page fault\n");
|
||||
page_fault_to_be_reported = 1;
|
||||
*w_ptr = *fault_ptr;
|
||||
if(page_fault_to_be_reported){
|
||||
_halt(1);
|
||||
}
|
||||
_halt(0);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
NAME := llcop-test
|
||||
SRCS := $(shell find -L ./src/ -name "*.[cS]")
|
||||
|
||||
include $(AM_HOME)/Makefile.app
|
|
@ -0,0 +1,130 @@
|
|||
#include <am.h>
|
||||
#include <klib.h>
|
||||
|
||||
// naive LLC cache op test
|
||||
#if defined(__ARCH_RISCV64_XS_SOUTHLAKE) || defined(__ARCH_RISCV64_XS_SOUTHLAKE_FLASH)
|
||||
#define CACHE_CTRL_BASE 0x1f10040100
|
||||
#define CACHE_CMD_BASE 0x1f10040200
|
||||
#define HART_CTRL_RESET_REG_BASE 0x1f10001000
|
||||
#else
|
||||
#define CACHE_CTRL_BASE 0x39000100
|
||||
#define CACHE_CMD_BASE 0x39000200
|
||||
#define HART_CTRL_RESET_REG_BASE 0x39001000
|
||||
#endif
|
||||
#define CMD_CMO_INV (0 + 16)
|
||||
#define CMD_CMO_CLEAN (1 + 16)
|
||||
#define CMD_CMO_FLUSH (2 + 16)
|
||||
#define CTRL_TAG_OFFSET 0
|
||||
#define CTRL_SET_OFFSET 8
|
||||
#define CTRL_WAY_OFFSET 16
|
||||
#define CTRL_DATA_OFFSET 24
|
||||
#define CTRL_DIR_OFFSET 32
|
||||
#define TEST_BUFFER_SIZE 128
|
||||
#define L3_SIZE_KB (3 * 1024)
|
||||
#define L3_NR_WAY 6
|
||||
#define L3_NR_BANK 4
|
||||
#define OFFSET_LEN 6
|
||||
|
||||
unsigned int log2(unsigned int n) {
|
||||
unsigned int result = 0;
|
||||
while (n > 1) {
|
||||
assert(n % 2 == 0);
|
||||
n = n / 2;
|
||||
result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void wait(int cycle) {
|
||||
volatile int i = cycle;
|
||||
while(i > 0){
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
volatile uint64_t test_buffer[TEST_BUFFER_SIZE] __attribute__((aligned(64))) = {0};
|
||||
|
||||
void success() {
|
||||
printf("test passed.\n");
|
||||
// asm("li a0, 0\n");
|
||||
// asm(".word 0x0000006b\n");
|
||||
_halt(0);
|
||||
}
|
||||
|
||||
void failure() {
|
||||
printf("test failed.\n");
|
||||
// asm("li a0, 1\n");
|
||||
// asm(".word 0x0000006b\n");
|
||||
_halt(1);
|
||||
}
|
||||
|
||||
void test1() {
|
||||
test_buffer[0] = 1;
|
||||
asm("fence\n");
|
||||
(*(uint64_t*)CACHE_CMD_BASE) = CMD_CMO_INV;
|
||||
wait(100);
|
||||
printf("huancun op invalid done\n");
|
||||
printf("data %lx\n", test_buffer[0]);
|
||||
if (test_buffer[0] == 1) {
|
||||
printf("CMO_INV failed: data right %lx wrong %lx", 1, test_buffer[0]);
|
||||
failure();
|
||||
}
|
||||
}
|
||||
|
||||
void test2() {
|
||||
test_buffer[0] = 2;
|
||||
asm("fence\n");
|
||||
(*(uint64_t*)CACHE_CMD_BASE) = CMD_CMO_CLEAN;
|
||||
wait(100);
|
||||
printf("huancun op clean done\n");
|
||||
printf("data %lx\n", test_buffer[0]);
|
||||
if (test_buffer[0] != 2) {
|
||||
printf("CMD_CMO_CLEAN failed: data right %lx wrong %lx", 2, test_buffer[0]);
|
||||
failure();
|
||||
}
|
||||
}
|
||||
|
||||
void test3() {
|
||||
test_buffer[0] = 3;
|
||||
asm("fence\n");
|
||||
(*(uint64_t*)CACHE_CMD_BASE) = CMD_CMO_FLUSH;
|
||||
wait(100);
|
||||
printf("huancun op flush done\n");
|
||||
printf("data %lx\n", test_buffer[0]);
|
||||
if (test_buffer[0] != 3) {
|
||||
printf("CMD_CMO_FLUSH failed: data right %lx wrong %lx", 3, test_buffer[0]);
|
||||
failure();
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("HuanCun op (mmio based) test. Note that --no-diff is required!\n");
|
||||
printf("HuanCun l3 size is set to %d KB, nr_way is set to %d, nr_bank is set to %d, ", L3_SIZE_KB, L3_NR_WAY, L3_NR_BANK);
|
||||
unsigned int set_size = L3_SIZE_KB * 1024 / L3_NR_BANK / L3_NR_WAY / 64;
|
||||
unsigned int set_len = log2(set_size);
|
||||
printf("nr_set is %u, set_len is %u\n", set_size, set_len);
|
||||
|
||||
/* Fill data for test_buffer */
|
||||
for (int i = 0; i < TEST_BUFFER_SIZE; i++) {
|
||||
test_buffer[i] = (uint64_t)&test_buffer[i];
|
||||
}
|
||||
|
||||
/* In our LLC design, full address is passed by CtrlUnit to one of the SliceCtrls according to BankBits
|
||||
* Afterwards, BankBits are truncated in SliceCtrl to generate real MSHR request
|
||||
* So we should provide full address here
|
||||
*/
|
||||
uint64_t tag = ((uint64_t)&test_buffer >> OFFSET_LEN) >> set_len; // paddr to l3 tag
|
||||
uint64_t set = ((uint64_t)&test_buffer >> OFFSET_LEN) & (set_size-1); // paddr to l3 set
|
||||
*(uint64_t*)(CACHE_CTRL_BASE + CTRL_TAG_OFFSET) = tag;
|
||||
*(uint64_t*)(CACHE_CTRL_BASE + CTRL_SET_OFFSET) = set;
|
||||
printf("addr 0x%llx tag 0x%llx set 0x%llx\n", &test_buffer, tag, set);
|
||||
asm("fence\n");
|
||||
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
|
||||
// test to be added for a wider addr range
|
||||
success();
|
||||
return 0;
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
#include <am.h>
|
||||
#include <klib.h>
|
||||
|
||||
// naive l2 cache op test
|
||||
|
||||
#define CACHE_CTRL_BASE 0x39000100
|
||||
#define CACHE_CMD_BASE 0x39000200
|
||||
#define HART_CTRL_RESET_REG_BASE 0x39001000
|
||||
#define CMD_CMO_INV (0 + 16)
|
||||
#define CMD_CMO_CLEAN (1 + 16)
|
||||
#define CMD_CMO_FLUSH (2 + 16)
|
||||
#define CTRL_TAG_OFFSET 0
|
||||
#define CTRL_SET_OFFSET 8
|
||||
#define CTRL_WAY_OFFSET 16
|
||||
#define CTRL_DATA_OFFSET 24
|
||||
#define CTRL_DIR_OFFSET 32
|
||||
#define TEST_BUFFER_SIZE 32
|
||||
#define L3_SIZE_KB (6 * 1024)
|
||||
|
||||
volatile uint64_t test_buffer[TEST_BUFFER_SIZE] = {0};
|
||||
|
||||
void success() {
|
||||
printf("test passed.\n");
|
||||
asm("li a0, 0\n");
|
||||
asm(".word 0x0000006b\n");
|
||||
}
|
||||
|
||||
void failure() {
|
||||
printf("test failed.\n");
|
||||
asm("li a0, 1\n");
|
||||
asm(".word 0x0000006b\n");
|
||||
}
|
||||
|
||||
int main(){
|
||||
printf("huancun op (mmio based) test\n");
|
||||
printf("huancun l3 size is set to %d KB\n", L3_SIZE_KB);
|
||||
for(int i = 0; i < TEST_BUFFER_SIZE; i++){
|
||||
test_buffer[i] = (uint64_t)&test_buffer[i];
|
||||
}
|
||||
uint64_t tag = (uint64_t)&test_buffer >> 6 >> 12; // paddr to l3 tag
|
||||
uint64_t set = (uint64_t)&test_buffer >> 6; // paddr to l3 set
|
||||
*(uint64_t*)(CACHE_CTRL_BASE + CTRL_TAG_OFFSET) = tag;
|
||||
*(uint64_t*)(CACHE_CTRL_BASE + CTRL_SET_OFFSET) = set;
|
||||
printf("addr %x tag %x set %x\n", &test_buffer, tag, set);
|
||||
asm("fence\n");
|
||||
|
||||
// (*(uint64_t*)CACHE_CMD_BASE) = CMD_CMO_FLUSH;
|
||||
// printf("huancun op flush done\n");
|
||||
// printf("data %lx\n", test_buffer[0]);
|
||||
// (*(uint64_t*)CACHE_CMD_BASE) = CMD_CMO_CLEAN;
|
||||
// printf("huancun op clean done\n");
|
||||
// printf("data %lx\n", test_buffer[0]);
|
||||
// (*(uint64_t*)CACHE_CMD_BASE) = CMD_CMO_INV;
|
||||
// printf("huancun op invalid done\n");
|
||||
// printf("data %lx\n", test_buffer[0]);
|
||||
|
||||
test_buffer[0] = 1;
|
||||
asm("fence\n");
|
||||
(*(uint64_t*)CACHE_CMD_BASE) = CMD_CMO_INV;
|
||||
printf("huancun op invalid done\n");
|
||||
printf("data %lx\n", test_buffer[0]);
|
||||
|
||||
test_buffer[0] = 2;
|
||||
asm("fence\n");
|
||||
(*(uint64_t*)CACHE_CMD_BASE) = CMD_CMO_CLEAN;
|
||||
printf("huancun op clean done\n");
|
||||
printf("data %lx\n", test_buffer[0]);
|
||||
|
||||
test_buffer[0] = 3;
|
||||
asm("fence\n");
|
||||
(*(uint64_t*)CACHE_CMD_BASE) = CMD_CMO_FLUSH;
|
||||
printf("huancun op flush done\n");
|
||||
printf("data %lx\n", test_buffer[0]);
|
||||
|
||||
// test to be added for a wider addr range
|
||||
|
||||
success();
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue