Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
build/DOOM1.WAD*
build/rv32emu
build/compliance
*.o
*.o.d
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "riscv-compliance"]
path = riscv-compliance
url = https://github.com/riscv/riscv-compliance.git
16 changes: 16 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,22 @@ check: $(BIN)
(cd $(OUT); ../$(BIN) hello.elf)
(cd $(OUT); ../$(BIN) puzzle.elf)

# variables for compliance
COMPLIANCE_DIR ?= ./riscv-compliance
export RISCV_PREFIX ?= riscv32-unknown-elf-
export RISCV_TARGET = compliance-target
export TARGETDIR = $(shell pwd)
export XLEN = 32
export JOBS ?= -j
export WORK = $(TARGETDIR)/build/compliance
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure why pwd is necessary. Can you explain?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the build process of compliance involves multiple makefiles in different places, I found out that the makefile from the compliance will fail to find our target file if I just set TARGETDIR to .. Therefore, I set it to the absolute path of our project root.


$(COMPLIANCE_DIR):
git submodule update --init

compliance: $(BIN) $(COMPLIANCE_DIR)
$(Q)$(MAKE) --quiet -C $(COMPLIANCE_DIR) clean;
$(Q)$(MAKE) --quiet -C $(COMPLIANCE_DIR);

demo: $(BIN) $(OUT)/DOOM1.WAD
(cd $(OUT); ../$(BIN) doom.elf)

Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ make demo
The build script will then download data file for Doom automatically. SDL2 based window
should appear when Doom is loaded and executed.

### Run riscv-compliance test
Once the submodule `riscv-compliance` is pulled, run all the available compliance test via command:
```shell
make compliance
```
+ To run the tests for specific extension, set the environmental variable `RISCV_DEVICE` to one of `I`, `M`, `Zifencei`, `privilege`.
+ To run a specific test case, set both `RISCV_DEVICE` and `RISCV_TEST`. For example:
```shell
make compliance RISCV_DEVICE=M RISCV_TEST=div-01
```

## Customization

`rv32emu` is configurable, and you can modify `Makefile` to fit your expectations:
Expand Down
22 changes: 22 additions & 0 deletions compliance-target/device/rv32i_m/I/Makefile.include
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
RUN_TARGET= $(TARGETDIR)/build/rv32emu $(<) \
$(RISCV_TARGET_FLAGS) \
--compliance $(*).signature.output \
1>$(@) 2>&1

RISCV_GCC ?= $(RISCV_PREFIX)gcc
RISCV_GCC_OPTS ?= \
-march=rv32g \
-mabi=ilp32 \
-static \
-mcmodel=medany \
-fvisibility=hidden \
$(RVTEST_DEFINES) \
-nostdlib \
-nostartfiles

COMPILE_TARGET=\
$$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
-I$(ROOTDIR)/riscv-test-suite/env/ \
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
$$(<) -o $$(@);
22 changes: 22 additions & 0 deletions compliance-target/device/rv32i_m/M/Makefile.include
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
RUN_TARGET= $(TARGETDIR)/build/rv32emu $(<) \
$(RISCV_TARGET_FLAGS) \
--compliance $(*).signature.output \
1>$(@) 2>&1

RISCV_GCC ?= $(RISCV_PREFIX)gcc
RISCV_GCC_OPTS ?= \
-march=rv32g \
-mabi=ilp32 \
-static \
-mcmodel=medany \
-fvisibility=hidden \
$(RVTEST_DEFINES) \
-nostdlib \
-nostartfiles

COMPILE_TARGET=\
$$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
-I$(ROOTDIR)/riscv-test-suite/env/ \
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
$$(<) -o $$(@);
22 changes: 22 additions & 0 deletions compliance-target/device/rv32i_m/Zifencei/Makefile.include
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
RUN_TARGET= $(TARGETDIR)/build/rv32emu $(<) \
$(RISCV_TARGET_FLAGS) \
--compliance $(*).signature.output \
1>$(@) 2>&1

RISCV_GCC ?= $(RISCV_PREFIX)gcc
RISCV_GCC_OPTS ?= \
-march=rv32g \
-mabi=ilp32 \
-static \
-mcmodel=medany \
-fvisibility=hidden \
$(RVTEST_DEFINES) \
-nostdlib \
-nostartfiles

COMPILE_TARGET=\
$$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
-I$(ROOTDIR)/riscv-test-suite/env/ \
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
$$(<) -o $$(@);
22 changes: 22 additions & 0 deletions compliance-target/device/rv32i_m/privilege/Makefile.include
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
RUN_TARGET= $(TARGETDIR)/build/rv32emu $(<) \
$(RISCV_TARGET_FLAGS) \
--compliance $(*).signature.output \
1>$(@) 2>&1

RISCV_GCC ?= $(RISCV_PREFIX)gcc
RISCV_GCC_OPTS ?= \
-march=rv32g \
-mabi=ilp32 \
-static \
-mcmodel=medany \
-fvisibility=hidden \
$(RVTEST_DEFINES) \
-nostdlib \
-nostartfiles

COMPILE_TARGET=\
$$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
-I$(ROOTDIR)/riscv-test-suite/env/ \
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
$$(<) -o $$(@);
16 changes: 16 additions & 0 deletions compliance-target/link.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
OUTPUT_ARCH( "riscv" )
ENTRY(rvtest_entry_point)

SECTIONS
{
. = 0x80000000;
.text.init : { *(.text.init) }
. = ALIGN(0x1000);
.text : { *(.text) }
. = ALIGN(0x1000);
.data : { *(.data) }
.data.string : { *(.data.string)}
.bss : { *(.bss) }
_end = .;
}

27 changes: 27 additions & 0 deletions compliance-target/model_test.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

#define RVMODEL_HALT \
add a7, x0, 93; \
add a0, x0, 0; \
ecall

#define RVMODEL_BOOT

#define RVTEST_RV32M

#define RVMODEL_DATA_BEGIN \
.align 4; .global begin_signature; begin_signature:

#define RVMODEL_DATA_END \
.align 4; .global end_signature; end_signature:


#define RVMODEL_IO_INIT
#define RVMODEL_IO_WRITE_STR(_SP, _STR)
#define RVMODEL_IO_CHECK()
#define RVMODEL_IO_ASSERT_GPR_EQ(_SP, _R, _I)
#define RVMODEL_IO_ASSERT_SFPR_EQ(_F, _R, _I)
#define RVMODEL_IO_ASSERT_DFPR_EQ(_D, _R, _I)
#define RVMODEL_SET_MSW_INT
#define RVMODEL_CLEAR_MSW_INT
#define RVMODEL_CLEAR_MTIMER_INT
#define RVMODEL_CLEAR_MEXT_INT
29 changes: 29 additions & 0 deletions elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,26 @@ enum {
STT_TLS = 6,
};

enum {
SHT_NULL = 0,
SHT_PROGBITS = 1,
SHT_SYMTAB = 2,
SHT_STRTAB = 3,
SHT_RELA = 4,
SHT_HASH = 5,
SHT_DYNAMIC = 6,
SHT_NOTE = 7,
SHT_NOBITS = 8,
SHT_REL = 9,
SHT_SHLIB = 10,
SHT_DYNSYM = 11,
SHT_NUM = 12,
SHT_LOPROC = 0x70000000,
SHT_HIPROC = 0x7fffffff,
SHT_LOUSER = 0x80000000,
SHT_HIUSER = 0xffffffff
};

#define ELF_ST_TYPE(x) (((unsigned int) x) & 0xf)

struct Elf32_Ehdr {
Expand Down Expand Up @@ -249,6 +269,15 @@ const char *elf_find_symbol(elf_t *e, uint32_t addr)
return c_map_at_end(e->symbols, &it) ? NULL : c_map_iter_value(&it, char *);
}

bool elf_get_data_section_range(elf_t *e, uint32_t *start, uint32_t *end) {
const struct Elf32_Shdr *shdr = get_section_header(e, ".data");
if (!shdr) return false;
if (shdr->sh_type == SHT_NOBITS) return false;
*start = shdr->sh_addr;
*end = *start + shdr->sh_size;
return true;
}

bool elf_load(elf_t *e, struct riscv_t *rv, memory_t *mem)
{
rv_set_pc(rv, e->hdr->e_entry); /* set the entry point */
Expand Down
5 changes: 4 additions & 1 deletion elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,11 @@ bool elf_open(elf_t *e, const char *path);
/* Find a symbol entry */
const struct Elf32_Sym *elf_get_symbol(elf_t *e, const char *name);

/* Find symbole from a specified ELF file */
/* Find symbol from a specified ELF file */
const char *elf_find_symbol(elf_t *e, uint32_t addr);

/* get the range of .data section from the ELF file */
bool elf_get_data_section_range(elf_t *e, uint32_t *start, uint32_t *end);

/* Load the ELF file into a memory abstraction */
bool elf_load(elf_t *e, struct riscv_t *rv, memory_t *mem);
51 changes: 50 additions & 1 deletion main.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
/* enable program trace mode */
static bool opt_trace = false;

/* RISCV compliance test mode */
static bool opt_compliance = false;
static char *signature_out_file;

/* target executable */
static const char *opt_prog_name = "a.out";

Expand Down Expand Up @@ -101,7 +105,8 @@ static void print_usage(const char *filename)
"RV32I[MA] Emulator which loads an ELF file to execute.\n"
"Usage: %s [options] [filename]\n"
"Options:\n"
" --trace : print executable trace\n",
" --trace : print executable trace\n"
" --compliance [filename] : dump signature to the given file, required by compliance test\n",
filename);
}

Expand All @@ -118,6 +123,16 @@ static bool parse_args(int argc, char **args)
opt_trace = true;
continue;
}
if (!strcmp(arg, "--compliance")) {
opt_compliance = true;
if (i + 1 >= argc) {
fprintf(stderr,
"Filename for signature output required in compliance mode.\n");
return false;
}
signature_out_file = args[++i];
continue;
}
/* otherwise, error */
fprintf(stderr, "Unknown argument '%s'\n", arg);
return false;
Expand All @@ -129,6 +144,35 @@ static bool parse_args(int argc, char **args)
return true;
}

void dump_test_signature(struct riscv_t *rv, elf_t *elf)
{
uint32_t start = 0, end = 0;
const struct Elf32_Sym *sym;
FILE *f = fopen(signature_out_file, "w");
if (!f) {
fprintf(stderr, "Cannot open signature output file.\n");
return;
}

/* use the entire .data section as a fallback */
elf_get_data_section_range(elf, &start, &end);
/* try and access the exact signature range */
if ((sym = elf_get_symbol(elf, "begin_signature")))
start = sym->st_value;
if ((sym = elf_get_symbol(elf, "end_signature")))
end = sym->st_value;

state_t *s = rv_userdata(rv);

/* dump it word by word */
for (uint32_t addr = start; addr < end; addr += 4) {
fprintf(f, "%08x\n", memory_read_w(s->mem, addr));
}

fclose(f);
}


int main(int argc, char **args)
{
if (!parse_args(argc, args)) {
Expand Down Expand Up @@ -177,6 +221,11 @@ int main(int argc, char **args)
run(rv);
}

/* dump test result in test mode */
if (opt_compliance) {
dump_test_signature(rv, elf);
}

/* finalize the RISC-V runtime */
elf_delete(elf);
rv_delete(rv);
Expand Down
1 change: 1 addition & 0 deletions riscv-compliance
Submodule riscv-compliance added at 2e95f1