Merge pull request #13 from Ziyue-Zhang/master

Merge branch 'southlake' into master
This commit is contained in:
William Wang 2022-06-08 16:33:49 +08:00 committed by GitHub
commit 7fd1143d54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 809 additions and 113 deletions

View File

@ -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

View File

@ -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)"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 \

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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
}

View File

@ -0,0 +1,4 @@
NAME = dummy-bootrom
SRCS = main.c
include $(AM_HOME)/Makefile.app

View File

@ -0,0 +1,9 @@
#include "klib.h"
extern uint8_t ram_start;
int main() {
void (*f)(void) = (void *)&ram_start;
f();
return 0;
}

3
apps/hello/Makefile Normal file
View File

@ -0,0 +1,3 @@
NAME = hello
SRCS = hello.c
include $(AM_HOME)/Makefile.app

7
apps/hello/hello.c Normal file
View File

@ -0,0 +1,7 @@
#include <klib.h>
int main()
{
printf("Hello, XiangShan!\n");
return 0;
}

View File

@ -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.

View File

@ -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`实现。

View File

@ -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, ...)

View File

@ -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); })

View File

@ -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);

View File

@ -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");
}

View File

@ -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]);

View File

@ -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);

View File

@ -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
}

View File

@ -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);
}

View File

@ -0,0 +1,4 @@
NAME := llcop-test
SRCS := $(shell find -L ./src/ -name "*.[cS]")
include $(AM_HOME)/Makefile.app

View File

@ -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;
}

View File

@ -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;
}