commit
974f60328a
|
@ -45,3 +45,7 @@ doc/Drawings.odg
|
||||||
doc/html
|
doc/html
|
||||||
env.sh
|
env.sh
|
||||||
gh-md-toc
|
gh-md-toc
|
||||||
|
*.dump
|
||||||
|
*.log
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "spdlog"]
|
||||||
|
path = spdlog
|
||||||
|
url = https://github.com/gabime/spdlog
|
|
@ -15,6 +15,7 @@ set(CMAKE_C_STANDARD 17)
|
||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
set(CMAKE_PREFIX_PATH ../systemc-2.3.3/build/ ../spdlog/install/)
|
set(CMAKE_PREFIX_PATH ../systemc-2.3.3/build/ ../spdlog/install/)
|
||||||
|
add_definitions(-DCMAKE_EXPORT_COMPILE_COMMANDS=ON)
|
||||||
|
|
||||||
add_compile_options(-O3 -g -Wall -Wextra -Wunused-function -Wpedantic)
|
add_compile_options(-O3 -g -Wall -Wextra -Wunused-function -Wpedantic)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,244 @@
|
||||||
|
# Tests
|
||||||
|
|
||||||
|
Using riscv-arch-test (commit #acf857c68f3e5a476da24cff653b51176ac14dbf)
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
+ Copy doc/riscv-arch-test/RISCV-TLM to riscv-arch-test/riscv-target/
|
||||||
|
+ Run tests
|
||||||
|
|
||||||
|
```make verify```
|
||||||
|
|
||||||
|
## Results
|
||||||
|
### RV 64
|
||||||
|
|
||||||
|
#### I Extension
|
||||||
|
+ Check add-01 ... OK
|
||||||
|
+ Check addi-01 ... OK
|
||||||
|
+ Check addiw-01 ... OK
|
||||||
|
+ Check addw-01 ... OK
|
||||||
|
+ Check and-01 ... OK
|
||||||
|
+ Check andi-01 ... OK
|
||||||
|
+ Check auipc-01 ... OK
|
||||||
|
+ Check beq-01 ... OK
|
||||||
|
+ Check bge-01 ... OK
|
||||||
|
+ Check bgeu-01 ... OK
|
||||||
|
+ Check blt-01 ... OK
|
||||||
|
+ Check bltu-01 ... OK
|
||||||
|
+ Check bne-01 ... OK
|
||||||
|
+ Check fence-01 ... OK
|
||||||
|
+ Check jal-01 ... OK
|
||||||
|
+ Check jalr-01 ... OK
|
||||||
|
+ Check lb-align-01 ... OK
|
||||||
|
+ Check lbu-align-01 ... OK
|
||||||
|
+ Check ld-align-01 ... OK
|
||||||
|
+ Check lh-align-01 ... OK
|
||||||
|
+ Check lhu-align-01 ... OK
|
||||||
|
+ Check lui-01 ... OK
|
||||||
|
+ Check lw-align-01 ... OK
|
||||||
|
+ Check lwu-align-01 ... OK
|
||||||
|
+ Check or-01 ... OK
|
||||||
|
+ Check ori-01 ... OK
|
||||||
|
+ Check sb-align-01 ... OK
|
||||||
|
+ Check sd-align-01 ... OK
|
||||||
|
+ Check sh-align-01 ... OK
|
||||||
|
+ Check sll-01 ... OK
|
||||||
|
+ Check slli-01 ... OK
|
||||||
|
+ Check slliw-01 ... OK
|
||||||
|
+ Check sllw-01 ... OK
|
||||||
|
+ Check slt-01 ... OK
|
||||||
|
+ Check slti-01 ... OK
|
||||||
|
+ Check sltiu-01 ... OK
|
||||||
|
+ Check sltu-01 ... OK
|
||||||
|
+ Check sra-01 ... OK
|
||||||
|
+ Check srai-01 ... OK
|
||||||
|
+ Check sraiw-01 ... OK
|
||||||
|
+ Check sraw-01 ... OK
|
||||||
|
+ Check srl-01 ... OK
|
||||||
|
+ Check srli-01 ... OK
|
||||||
|
+ Check srliw-01 ... OK
|
||||||
|
+ Check srlw-01 ... OK
|
||||||
|
+ Check sub-01 ... OK
|
||||||
|
+ Check subw-01 ... OK
|
||||||
|
+ Check sw-align-01 ... OK
|
||||||
|
+ Check xor-01 ... OK
|
||||||
|
+ Check xori-01 ... OK
|
||||||
|
|
||||||
|
#### C Extension
|
||||||
|
+ Check cadd-01 ... OK
|
||||||
|
+ Check caddi-01 ... OK
|
||||||
|
+ Check caddi16sp-01 ... OK
|
||||||
|
+ Check caddi4spn-01 ... OK
|
||||||
|
+ Check caddiw-01 ... OK
|
||||||
|
+ Check caddw-01 ... OK
|
||||||
|
+ Check cand-01 ... OK
|
||||||
|
+ Check candi-01 ... OK
|
||||||
|
+ Check cbeqz-01 ... OK
|
||||||
|
+ Check cbnez-01 ... OK
|
||||||
|
+ Check cebreak-01 ... FAIL
|
||||||
|
+ Check cj-01 ... OK
|
||||||
|
+ Check cjalr-01 ... OK
|
||||||
|
+ Check cjr-01 ... OK
|
||||||
|
+ Check cld-01 ... OK
|
||||||
|
+ Check cldsp-01 ... OK
|
||||||
|
+ Check cli-01 ... OK
|
||||||
|
+ Check clui-01 ... OK
|
||||||
|
+ Check clw-01 ... OK
|
||||||
|
+ Check clwsp-01 ... OK
|
||||||
|
+ Check cmv-01 ... OK
|
||||||
|
+ Check cnop-01 ... OK
|
||||||
|
+ Check cor-01 ... OK
|
||||||
|
+ Check csd-01 ... OK
|
||||||
|
+ Check csdsp-01 ... OK
|
||||||
|
+ Check cslli-01 ... OK
|
||||||
|
+ Check csrai-01 ... OK
|
||||||
|
+ Check csrli-01 ... OK
|
||||||
|
+ Check csub-01 ... OK
|
||||||
|
+ Check csubw-01 ... OK
|
||||||
|
+ Check csw-01 ... OK
|
||||||
|
+ Check cswsp-01 ... OK
|
||||||
|
+ Check cxor-01 ... OK
|
||||||
|
|
||||||
|
#### M Extension
|
||||||
|
+ Check div-01 ... OK
|
||||||
|
+ Check divu-01 ... OK
|
||||||
|
+ Check divuw-01 ... OK
|
||||||
|
+ Check divw-01 ... OK
|
||||||
|
+ Check mul-01 ... OK
|
||||||
|
+ Check mulh-01 ... OK
|
||||||
|
+ Check mulhsu-01 ... OK
|
||||||
|
+ Check mulhu-01 ... OK
|
||||||
|
+ Check mulw-01 ... OK
|
||||||
|
+ Check rem-01 ... OK
|
||||||
|
+ Check remu-01 ... OK
|
||||||
|
+ Check remuw-01 ... OK
|
||||||
|
+ Check remw-01 ... OK
|
||||||
|
|
||||||
|
#### Privilege
|
||||||
|
+ Check ebreak ... FAIL
|
||||||
|
+ Check ecall ... FAIL
|
||||||
|
+ Check misalign1-jalr-01 ... OK
|
||||||
|
+ Check misalign2-jalr-01 ... FAIL
|
||||||
|
+ Check misalign-beq-01 ... OK
|
||||||
|
+ Check misalign-bge-01 ... OK
|
||||||
|
+ Check misalign-bgeu-01 ... OK
|
||||||
|
+ Check misalign-blt-01 ... OK
|
||||||
|
+ Check misalign-bltu-01 ... OK
|
||||||
|
+ Check misalign-bne-01 ... OK
|
||||||
|
+ Check misalign-jal-01 ... OK
|
||||||
|
+ Check misalign-ld-01 ... FAIL
|
||||||
|
+ Check misalign-lh-01 ... FAIL
|
||||||
|
+ Check misalign-lhu-01 ... FAIL
|
||||||
|
+ Check misalign-lw-01 ... FAIL
|
||||||
|
+ Check misalign-lwu-01 ... FAIL
|
||||||
|
+ Check misalign-sd-01 ... FAIL
|
||||||
|
+ Check misalign-sh-01 ... FAIL
|
||||||
|
+ Check misalign-sw-01 ... FAIL
|
||||||
|
|
||||||
|
#### Zfencei
|
||||||
|
+ Check Fencei ... OK
|
||||||
|
|
||||||
|
|
||||||
|
### RV32
|
||||||
|
|
||||||
|
#### I Extension
|
||||||
|
+ Check add-01 ... OK
|
||||||
|
+ Check addi-01 ... OK
|
||||||
|
+ Check and-01 ... OK
|
||||||
|
+ Check andi-01 ... OK
|
||||||
|
+ Check auipc-01 ... OK
|
||||||
|
+ Check beq-01 ... OK
|
||||||
|
+ Check bge-01 ... OK
|
||||||
|
+ Check bgeu-01 ... OK
|
||||||
|
+ Check blt-01 ... OK
|
||||||
|
+ Check bltu-01 ... OK
|
||||||
|
+ Check bne-01 ... OK
|
||||||
|
+ Check fence-01 ... OK
|
||||||
|
+ Check jal-01 ... OK
|
||||||
|
+ Check jalr-01 ... OK
|
||||||
|
+ Check lb-align-01 ... OK
|
||||||
|
+ Check lbu-align-01 ... OK
|
||||||
|
+ Check lh-align-01 ... OK
|
||||||
|
+ Check lhu-align-01 ... OK
|
||||||
|
+ Check lui-01 ... OK
|
||||||
|
+ Check lw-align-01 ... OK
|
||||||
|
+ Check or-01 ... OK
|
||||||
|
+ Check ori-01 ... OK
|
||||||
|
+ Check sb-align-01 ... OK
|
||||||
|
+ Check sh-align-01 ... OK
|
||||||
|
+ Check sll-01 ... OK
|
||||||
|
+ Check slli-01 ... OK
|
||||||
|
+ Check slt-01 ... OK
|
||||||
|
+ Check slti-01 ... OK
|
||||||
|
+ Check sltiu-01 ... OK
|
||||||
|
+ Check sltu-01 ... OK
|
||||||
|
+ Check sra-01 ... OK
|
||||||
|
+ Check srai-01 ... OK
|
||||||
|
+ Check srl-01 ... OK
|
||||||
|
+ Check srli-01 ... OK
|
||||||
|
+ Check sub-01 ... OK
|
||||||
|
+ Check sw-align-01 ... OK
|
||||||
|
+ Check xor-01 ... OK
|
||||||
|
+ Check xori-01 ... OK
|
||||||
|
|
||||||
|
|
||||||
|
#### C Extension
|
||||||
|
+ Check cadd-01 ... OK
|
||||||
|
+ Check caddi-01 ... OK
|
||||||
|
+ Check caddi16sp-01 ... OK
|
||||||
|
+ Check caddi4spn-01 ... OK
|
||||||
|
+ Check cand-01 ... OK
|
||||||
|
+ Check candi-01 ... OK
|
||||||
|
+ Check cbeqz-01 ... OK
|
||||||
|
+ Check cbnez-01 ... OK
|
||||||
|
+ Check cebreak-01 ... FAIL
|
||||||
|
+ Check cj-01 ... OK
|
||||||
|
+ Check cjal-01 ... OK
|
||||||
|
+ Check cjalr-01 ... OK
|
||||||
|
+ Check cjr-01 ... OK
|
||||||
|
+ Check cli-01 ... OK
|
||||||
|
+ Check clui-01 ... OK
|
||||||
|
+ Check clw-01 ... OK
|
||||||
|
+ Check clwsp-01 ... OK
|
||||||
|
+ Check cmv-01 ... OK
|
||||||
|
+ Check cnop-01 ... OK
|
||||||
|
+ Check cor-01 ... OK
|
||||||
|
+ Check cslli-01 ... OK
|
||||||
|
+ Check csrai-01 ... OK
|
||||||
|
+ Check csrli-01 ... OK
|
||||||
|
+ Check csub-01 ... OK
|
||||||
|
+ Check csw-01 ... OK
|
||||||
|
+ Check cswsp-01 ... OK
|
||||||
|
+ Check cxor-01 ... OK
|
||||||
|
|
||||||
|
#### M Extension
|
||||||
|
+ Check div-01 ... OK
|
||||||
|
+ Check divu-01 ... OK
|
||||||
|
+ Check mul-01 ... OK
|
||||||
|
+ Check mulh-01 ... OK
|
||||||
|
+ Check mulhsu-01 ... OK
|
||||||
|
+ Check mulhu-01 ... OK
|
||||||
|
+ Check rem-01 ... OK
|
||||||
|
+ Check remu-01 ... OK
|
||||||
|
|
||||||
|
#### Privilage
|
||||||
|
+ Check ebreak ... FAIL
|
||||||
|
+ Check ecall ... FAIL
|
||||||
|
+ Check misalign1-jalr-01 ... OK
|
||||||
|
+ Check misalign2-jalr-01 ... FAIL
|
||||||
|
+ Check misalign-beq-01 ... OK
|
||||||
|
+ Check misalign-bge-01 ... OK
|
||||||
|
+ Check misalign-bgeu-01 ... OK
|
||||||
|
+ Check misalign-blt-01 ... OK
|
||||||
|
+ Check misalign-bltu-01 ... OK
|
||||||
|
+ Check misalign-bne-01 ... OK
|
||||||
|
+ Check misalign-jal-01 ... OK
|
||||||
|
+ Check misalign-lh-01 ... FAIL
|
||||||
|
+ Check misalign-lhu-01 ... FAIL
|
||||||
|
+ Check misalign-lw-01 ... FAIL
|
||||||
|
+ Check misalign-sh-01 ... FAIL
|
||||||
|
+ Check misalign-sw-01 ... FAIL
|
||||||
|
|
||||||
|
### Zifencei Extension
|
||||||
|
+ Check Fencei ... OK
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
TARGET_SIM ?= /home/marius/Work/RISC-V/RISC-V-TLM/RISCV_TLM
|
||||||
|
TARGET_FLAGS ?= $(RISCV_TARGET_FLAGS)
|
||||||
|
ifeq ($(shell command -v $(TARGET_SIM) 2> /dev/null),)
|
||||||
|
$(error Target simulator executable '$(TARGET_SIM)` not found)
|
||||||
|
endif
|
||||||
|
|
||||||
|
START_ADDRES := $(shell readelf -s $(<) | grep "begin_signature" | awk '{print "0x"$2 }')
|
||||||
|
END_ADDRES := $(shell readelf -s $(<) | grep "end_signature" | awk '{print "0x"$2 }')
|
||||||
|
|
||||||
|
RUN_TARGET=\
|
||||||
|
$(TARGET_SIM) $(TARGET_FLAGS) \
|
||||||
|
-B `readelf -s $(<) | grep "begin_signature" | awk '{print "0x"$$2 }'`\
|
||||||
|
-E `readelf -s $(<) | grep "end_signature" | awk '{print "0x"$$2 }'` -T \
|
||||||
|
$(<).hex ;\
|
||||||
|
mv *output $(work_dir_isa)/
|
||||||
|
|
||||||
|
RISCV_PREFIX ?= riscv32-unknown-elf-
|
||||||
|
RISCV_GCC ?= $(RISCV_PREFIX)gcc
|
||||||
|
RISCV_OBJDUMP ?= $(RISCV_PREFIX)objdump
|
||||||
|
RISCV_GCC_OPTS ?= -g -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles $(RVTEST_DEFINES)
|
||||||
|
|
||||||
|
COMPILE_CMD = $$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
|
||||||
|
-I$(ROOTDIR)/riscv-test-suite/env/ \
|
||||||
|
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
|
||||||
|
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
|
||||||
|
$$(<) -o $$@
|
||||||
|
|
||||||
|
OBJ_CMD = $$(RISCV_OBJDUMP) $$@ -D > $$@.objdump; \
|
||||||
|
$$(RISCV_OBJDUMP) $$@ --source > $$@.debug
|
||||||
|
|
||||||
|
OBJ_HEX = riscv32-unknown-elf-objcopy -Oihex $$@ $$@.hex
|
||||||
|
|
||||||
|
COMPILE_TARGET=\
|
||||||
|
$(COMPILE_CMD); \
|
||||||
|
$(OBJ_CMD);\
|
||||||
|
$(OBJ_HEX)
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
TARGET_SIM ?= /home/marius/Work/RISC-V/RISC-V-TLM/RISCV_TLM
|
||||||
|
TARGET_FLAGS ?= $(RISCV_TARGET_FLAGS)
|
||||||
|
ifeq ($(shell command -v $(TARGET_SIM) 2> /dev/null),)
|
||||||
|
$(error Target simulator executable '$(TARGET_SIM)` not found)
|
||||||
|
endif
|
||||||
|
|
||||||
|
RUN_TARGET=\
|
||||||
|
$(TARGET_SIM) $(TARGET_FLAGS) \
|
||||||
|
-B `readelf -s $(<) | grep "begin_signature" | awk '{print "0x"$$2 }'`\
|
||||||
|
-E `readelf -s $(<) | grep "end_signature" | awk '{print "0x"$$2 }'` -T \
|
||||||
|
$(<).hex ;\
|
||||||
|
mv *output $(work_dir_isa)/
|
||||||
|
|
||||||
|
RISCV_PREFIX ?= riscv32-unknown-elf-
|
||||||
|
RISCV_GCC ?= $(RISCV_PREFIX)gcc
|
||||||
|
RISCV_OBJDUMP ?= $(RISCV_PREFIX)objdump
|
||||||
|
RISCV_GCC_OPTS ?= -g -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles $(RVTEST_DEFINES)
|
||||||
|
|
||||||
|
COMPILE_CMD = $$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
|
||||||
|
-I$(ROOTDIR)/riscv-test-suite/env/ \
|
||||||
|
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
|
||||||
|
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
|
||||||
|
$$(<) -o $$@
|
||||||
|
|
||||||
|
OBJ_CMD = $$(RISCV_OBJDUMP) $$@ -D > $$@.objdump; \
|
||||||
|
$$(RISCV_OBJDUMP) $$@ --source > $$@.debug
|
||||||
|
|
||||||
|
OBJ_HEX = riscv32-unknown-elf-objcopy -Oihex $$@ $$@.hex
|
||||||
|
|
||||||
|
COMPILE_TARGET=\
|
||||||
|
$(COMPILE_CMD); \
|
||||||
|
$(OBJ_CMD);\
|
||||||
|
$(OBJ_HEX)
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
TARGET_SIM ?= /home/marius/Work/RISC-V/RISC-V-TLM/RISCV_TLM
|
||||||
|
TARGET_FLAGS ?= $(RISCV_TARGET_FLAGS)
|
||||||
|
ifeq ($(shell command -v $(TARGET_SIM) 2> /dev/null),)
|
||||||
|
$(error Target simulator executable '$(TARGET_SIM)` not found)
|
||||||
|
endif
|
||||||
|
|
||||||
|
START_ADDRES := $(shell readelf -s $(<) | grep "begin_signature" | awk '{print "0x"$2 }')
|
||||||
|
END_ADDRES := $(shell readelf -s $(<) | grep "end_signature" | awk '{print "0x"$2 }')
|
||||||
|
|
||||||
|
RUN_TARGET=\
|
||||||
|
$(TARGET_SIM) $(TARGET_FLAGS) \
|
||||||
|
-B `readelf -s $(<) | grep "begin_signature" | awk '{print "0x"$$2 }'`\
|
||||||
|
-E `readelf -s $(<) | grep "end_signature" | awk '{print "0x"$$2 }'` -T \
|
||||||
|
$(<).hex ;\
|
||||||
|
mv *output $(work_dir_isa)/
|
||||||
|
|
||||||
|
RISCV_PREFIX ?= riscv32-unknown-elf-
|
||||||
|
RISCV_GCC ?= $(RISCV_PREFIX)gcc
|
||||||
|
RISCV_OBJDUMP ?= $(RISCV_PREFIX)objdump
|
||||||
|
RISCV_GCC_OPTS ?= -g -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles $(RVTEST_DEFINES)
|
||||||
|
|
||||||
|
COMPILE_CMD = $$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
|
||||||
|
-I$(ROOTDIR)/riscv-test-suite/env/ \
|
||||||
|
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
|
||||||
|
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
|
||||||
|
$$(<) -o $$@
|
||||||
|
|
||||||
|
OBJ_CMD = $$(RISCV_OBJDUMP) $$@ -D > $$@.objdump; \
|
||||||
|
$$(RISCV_OBJDUMP) $$@ --source > $$@.debug
|
||||||
|
|
||||||
|
OBJ_HEX = riscv32-unknown-elf-objcopy -Oihex $$@ $$@.hex
|
||||||
|
|
||||||
|
COMPILE_TARGET=\
|
||||||
|
$(COMPILE_CMD); \
|
||||||
|
$(OBJ_CMD);\
|
||||||
|
$(OBJ_HEX)
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
TARGET_SIM ?= /home/marius/Work/RISC-V/RISC-V-TLM/RISCV_TLM
|
||||||
|
TARGET_FLAGS ?= $(RISCV_TARGET_FLAGS)
|
||||||
|
ifeq ($(shell command -v $(TARGET_SIM) 2> /dev/null),)
|
||||||
|
$(error Target simulator executable '$(TARGET_SIM)` not found)
|
||||||
|
endif
|
||||||
|
|
||||||
|
START_ADDRES := $(shell readelf -s $(<) | grep "begin_signature" | awk '{print "0x"$2 }')
|
||||||
|
END_ADDRES := $(shell readelf -s $(<) | grep "end_signature" | awk '{print "0x"$2 }')
|
||||||
|
|
||||||
|
RUN_TARGET=\
|
||||||
|
$(TARGET_SIM) $(TARGET_FLAGS) \
|
||||||
|
-B `readelf -s $(<) | grep "begin_signature" | awk '{print "0x"$$2 }'`\
|
||||||
|
-E `readelf -s $(<) | grep "end_signature" | awk '{print "0x"$$2 }'` -T \
|
||||||
|
$(<).hex ;\
|
||||||
|
mv *output $(work_dir_isa)/
|
||||||
|
|
||||||
|
RISCV_PREFIX ?= riscv32-unknown-elf-
|
||||||
|
RISCV_GCC ?= $(RISCV_PREFIX)gcc
|
||||||
|
RISCV_OBJDUMP ?= $(RISCV_PREFIX)objdump
|
||||||
|
RISCV_GCC_OPTS ?= -g -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles $(RVTEST_DEFINES)
|
||||||
|
|
||||||
|
COMPILE_CMD = $$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
|
||||||
|
-I$(ROOTDIR)/riscv-test-suite/env/ \
|
||||||
|
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
|
||||||
|
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
|
||||||
|
$$(<) -o $$@
|
||||||
|
|
||||||
|
OBJ_CMD = $$(RISCV_OBJDUMP) $$@ -D > $$@.objdump; \
|
||||||
|
$$(RISCV_OBJDUMP) $$@ --source > $$@.debug
|
||||||
|
|
||||||
|
OBJ_HEX = riscv32-unknown-elf-objcopy -Oihex $$@ $$@.hex
|
||||||
|
|
||||||
|
COMPILE_TARGET=\
|
||||||
|
$(COMPILE_CMD); \
|
||||||
|
$(OBJ_CMD);\
|
||||||
|
$(OBJ_HEX)
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
TARGET_SIM ?= /home/marius/Work/RISC-V/RISC-V-TLM/RISCV_TLM
|
||||||
|
TARGET_FLAGS ?= $(RISCV_TARGET_FLAGS)
|
||||||
|
ifeq ($(shell command -v $(TARGET_SIM) 2> /dev/null),)
|
||||||
|
$(error Target simulator executable '$(TARGET_SIM)` not found)
|
||||||
|
endif
|
||||||
|
|
||||||
|
START_ADDRES := $(shell readelf -s $(<) | grep "begin_signature" | awk '{print "0x"$2 }')
|
||||||
|
END_ADDRES := $(shell readelf -s $(<) | grep "end_signature" | awk '{print "0x"$2 }')
|
||||||
|
|
||||||
|
RUN_TARGET=\
|
||||||
|
$(TARGET_SIM) $(TARGET_FLAGS) \
|
||||||
|
-B `readelf -s $(<) | grep "begin_signature" | awk '{print "0x"$$2 }'`\
|
||||||
|
-E `readelf -s $(<) | grep "end_signature" | awk '{print "0x"$$2 }'` -T \
|
||||||
|
$(<).hex ;\
|
||||||
|
mv *output $(work_dir_isa)/
|
||||||
|
|
||||||
|
RISCV_PREFIX ?= riscv32-unknown-elf-
|
||||||
|
RISCV_GCC ?= $(RISCV_PREFIX)gcc
|
||||||
|
RISCV_OBJDUMP ?= $(RISCV_PREFIX)objdump
|
||||||
|
RISCV_GCC_OPTS ?= -g -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles $(RVTEST_DEFINES)
|
||||||
|
|
||||||
|
COMPILE_CMD = $$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
|
||||||
|
-I$(ROOTDIR)/riscv-test-suite/env/ \
|
||||||
|
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
|
||||||
|
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
|
||||||
|
$$(<) -o $$@
|
||||||
|
|
||||||
|
OBJ_CMD = $$(RISCV_OBJDUMP) $$@ -D > $$@.objdump; \
|
||||||
|
$$(RISCV_OBJDUMP) $$@ --source > $$@.debug
|
||||||
|
|
||||||
|
OBJ_HEX = riscv32-unknown-elf-objcopy -Oihex $$@ $$@.hex
|
||||||
|
|
||||||
|
COMPILE_TARGET=\
|
||||||
|
$(COMPILE_CMD); \
|
||||||
|
$(OBJ_CMD);\
|
||||||
|
$(OBJ_HEX)
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
TARGET_SIM ?= /home/marius/Work/RISC-V/RISC-V-TLM/RISCV_TLM
|
||||||
|
TARGET_FLAGS ?= $(RISCV_TARGET_FLAGS)
|
||||||
|
ifeq ($(shell command -v $(TARGET_SIM) 2> /dev/null),)
|
||||||
|
$(error Target simulator executable '$(TARGET_SIM)` not found)
|
||||||
|
endif
|
||||||
|
|
||||||
|
START_ADDRES := $(shell readelf -s $(<) | grep "begin_signature" | awk '{print "0x"$2 }')
|
||||||
|
END_ADDRES := $(shell readelf -s $(<) | grep "end_signature" | awk '{print "0x"$2 }')
|
||||||
|
|
||||||
|
RUN_TARGET=\
|
||||||
|
$(TARGET_SIM) $(TARGET_FLAGS) -R64 -T \
|
||||||
|
$(<).hex ;\
|
||||||
|
mv *output $(work_dir_isa)/
|
||||||
|
|
||||||
|
RISCV_PREFIX ?= riscv64-unknown-elf-
|
||||||
|
RISCV_GCC ?= $(RISCV_PREFIX)gcc
|
||||||
|
RISCV_OBJDUMP ?= $(RISCV_PREFIX)objdump
|
||||||
|
RISCV_GCC_OPTS ?= -g -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles $(RVTEST_DEFINES)
|
||||||
|
|
||||||
|
COMPILE_CMD = $$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
|
||||||
|
-I$(ROOTDIR)/riscv-test-suite/env/ \
|
||||||
|
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
|
||||||
|
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
|
||||||
|
$$(<) -o $$@
|
||||||
|
|
||||||
|
OBJ_CMD = $$(RISCV_OBJDUMP) $$@ -D > $$@.objdump; \
|
||||||
|
$$(RISCV_OBJDUMP) $$@ --source > $$@.debug
|
||||||
|
|
||||||
|
OBJ_HEX = riscv64-unknown-elf-objcopy -Oihex $$@ $$@.hex
|
||||||
|
|
||||||
|
COMPILE_TARGET=\
|
||||||
|
$(COMPILE_CMD); \
|
||||||
|
$(OBJ_CMD);\
|
||||||
|
$(OBJ_HEX)
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
TARGET_SIM ?= /home/marius/Work/RISC-V/RISC-V-TLM/RISCV_TLM
|
||||||
|
TARGET_FLAGS ?= $(RISCV_TARGET_FLAGS)
|
||||||
|
ifeq ($(shell command -v $(TARGET_SIM) 2> /dev/null),)
|
||||||
|
$(error Target simulator executable '$(TARGET_SIM)` not found)
|
||||||
|
endif
|
||||||
|
|
||||||
|
RUN_TARGET=\
|
||||||
|
$(TARGET_SIM) $(TARGET_FLAGS) -R64 -T \
|
||||||
|
$(<).hex ;\
|
||||||
|
mv *output $(work_dir_isa)/
|
||||||
|
|
||||||
|
RISCV_PREFIX ?= riscv64-unknown-elf-
|
||||||
|
RISCV_GCC ?= $(RISCV_PREFIX)gcc
|
||||||
|
RISCV_OBJDUMP ?= $(RISCV_PREFIX)objdump
|
||||||
|
RISCV_GCC_OPTS ?= -g -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles $(RVTEST_DEFINES)
|
||||||
|
|
||||||
|
COMPILE_CMD = $$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
|
||||||
|
-I$(ROOTDIR)/riscv-test-suite/env/ \
|
||||||
|
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
|
||||||
|
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
|
||||||
|
$$(<) -o $$@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
OBJ_CMD = $$(RISCV_OBJDUMP) $$@ -D > $$@.objdump; \
|
||||||
|
$$(RISCV_OBJDUMP) $$@ --source > $$@.debug
|
||||||
|
|
||||||
|
OBJ_HEX = riscv64-unknown-elf-objcopy -Oihex $$@ $$@.hex
|
||||||
|
|
||||||
|
COMPILE_TARGET=\
|
||||||
|
$(COMPILE_CMD); \
|
||||||
|
$(OBJ_CMD);\
|
||||||
|
$(OBJ_HEX)
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
TARGET_SIM ?= /home/marius/Work/RISC-V/RISC-V-TLM/RISCV_TLM
|
||||||
|
TARGET_FLAGS ?= $(RISCV_TARGET_FLAGS)
|
||||||
|
ifeq ($(shell command -v $(TARGET_SIM) 2> /dev/null),)
|
||||||
|
$(error Target simulator executable '$(TARGET_SIM)` not found)
|
||||||
|
endif
|
||||||
|
|
||||||
|
START_ADDRES := $(shell readelf -s $(<) | grep "begin_signature" | awk '{print "0x"$2 }')
|
||||||
|
END_ADDRES := $(shell readelf -s $(<) | grep "end_signature" | awk '{print "0x"$2 }')
|
||||||
|
|
||||||
|
RUN_TARGET=\
|
||||||
|
$(TARGET_SIM) $(TARGET_FLAGS) -R64 -T \
|
||||||
|
$(<).hex ;\
|
||||||
|
mv *output $(work_dir_isa)/
|
||||||
|
|
||||||
|
RISCV_PREFIX ?= riscv64-unknown-elf-
|
||||||
|
RISCV_GCC ?= $(RISCV_PREFIX)gcc
|
||||||
|
RISCV_OBJDUMP ?= $(RISCV_PREFIX)objdump
|
||||||
|
RISCV_GCC_OPTS ?= -g -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles $(RVTEST_DEFINES)
|
||||||
|
|
||||||
|
COMPILE_CMD = $$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
|
||||||
|
-I$(ROOTDIR)/riscv-test-suite/env/ \
|
||||||
|
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
|
||||||
|
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
|
||||||
|
$$(<) -o $$@
|
||||||
|
|
||||||
|
OBJ_CMD = $$(RISCV_OBJDUMP) $$@ -D > $$@.objdump; \
|
||||||
|
$$(RISCV_OBJDUMP) $$@ --source > $$@.debug
|
||||||
|
|
||||||
|
OBJ_HEX = riscv64-unknown-elf-objcopy -Oihex $$@ $$@.hex
|
||||||
|
|
||||||
|
COMPILE_TARGET=\
|
||||||
|
$(COMPILE_CMD); \
|
||||||
|
$(OBJ_CMD);\
|
||||||
|
$(OBJ_HEX)
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
TARGET_SIM ?= /home/marius/Work/RISC-V/RISC-V-TLM/RISCV_TLM
|
||||||
|
TARGET_FLAGS ?= $(RISCV_TARGET_FLAGS)
|
||||||
|
ifeq ($(shell command -v $(TARGET_SIM) 2> /dev/null),)
|
||||||
|
$(error Target simulator executable '$(TARGET_SIM)` not found)
|
||||||
|
endif
|
||||||
|
|
||||||
|
RUN_TARGET=\
|
||||||
|
$(TARGET_SIM) $(TARGET_FLAGS) -R64 -T \
|
||||||
|
$(<).hex ;\
|
||||||
|
mv *output $(work_dir_isa)/
|
||||||
|
|
||||||
|
RISCV_PREFIX ?= riscv64-unknown-elf-
|
||||||
|
RISCV_GCC ?= $(RISCV_PREFIX)gcc
|
||||||
|
RISCV_OBJDUMP ?= $(RISCV_PREFIX)objdump
|
||||||
|
RISCV_GCC_OPTS ?= -g -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles $(RVTEST_DEFINES)
|
||||||
|
|
||||||
|
COMPILE_CMD = $$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
|
||||||
|
-I$(ROOTDIR)/riscv-test-suite/env/ \
|
||||||
|
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
|
||||||
|
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
|
||||||
|
$$(<) -o $$@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
OBJ_CMD = $$(RISCV_OBJDUMP) $$@ -D > $$@.objdump; \
|
||||||
|
$$(RISCV_OBJDUMP) $$@ --source > $$@.debug
|
||||||
|
|
||||||
|
OBJ_HEX = riscv64-unknown-elf-objcopy -Oihex $$@ $$@.hex
|
||||||
|
|
||||||
|
COMPILE_TARGET=\
|
||||||
|
$(COMPILE_CMD); \
|
||||||
|
$(OBJ_CMD);\
|
||||||
|
$(OBJ_HEX)
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
TARGET_SIM ?= /home/marius/Work/RISC-V/RISC-V-TLM/RISCV_TLM
|
||||||
|
TARGET_FLAGS ?= $(RISCV_TARGET_FLAGS)
|
||||||
|
ifeq ($(shell command -v $(TARGET_SIM) 2> /dev/null),)
|
||||||
|
$(error Target simulator executable '$(TARGET_SIM)` not found)
|
||||||
|
endif
|
||||||
|
|
||||||
|
RUN_TARGET=\
|
||||||
|
$(TARGET_SIM) $(TARGET_FLAGS) -R64 -T \
|
||||||
|
$(<).hex ;\
|
||||||
|
mv *output $(work_dir_isa)/
|
||||||
|
|
||||||
|
RISCV_PREFIX ?= riscv64-unknown-elf-
|
||||||
|
RISCV_GCC ?= $(RISCV_PREFIX)gcc
|
||||||
|
RISCV_OBJDUMP ?= $(RISCV_PREFIX)objdump
|
||||||
|
RISCV_GCC_OPTS ?= -g -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles $(RVTEST_DEFINES)
|
||||||
|
|
||||||
|
COMPILE_CMD = $$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
|
||||||
|
-I$(ROOTDIR)/riscv-test-suite/env/ \
|
||||||
|
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
|
||||||
|
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
|
||||||
|
$$(<) -o $$@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
OBJ_CMD = $$(RISCV_OBJDUMP) $$@ -D > $$@.objdump; \
|
||||||
|
$$(RISCV_OBJDUMP) $$@ --source > $$@.debug
|
||||||
|
|
||||||
|
OBJ_HEX = riscv64-unknown-elf-objcopy -Oihex $$@ $$@.hex
|
||||||
|
|
||||||
|
COMPILE_TARGET=\
|
||||||
|
$(COMPILE_CMD); \
|
||||||
|
$(OBJ_CMD);\
|
||||||
|
$(OBJ_HEX)
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
OUTPUT_ARCH( "riscv" )
|
||||||
|
ENTRY(rvtest_entry_point)
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = 0x00000000;
|
||||||
|
.text.init : { *(.text.init) }
|
||||||
|
. = ALIGN(0x1000);
|
||||||
|
/* .tohost : { *(.tohost) } */
|
||||||
|
. = ALIGN(0x1000);
|
||||||
|
.text : { *(.text) }
|
||||||
|
. = ALIGN(0x1000);
|
||||||
|
.data : { *(.data) }
|
||||||
|
.data.string : { *(.data.string)}
|
||||||
|
.bss : { *(.bss) }
|
||||||
|
_end = .;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,349 @@
|
||||||
|
#ifndef _COMPLIANCE_MODEL_H
|
||||||
|
#define _COMPLIANCE_MODEL_H
|
||||||
|
|
||||||
|
#define TESTNUM gp
|
||||||
|
|
||||||
|
#if XLEN == 64
|
||||||
|
#define ALIGNMENT 3
|
||||||
|
#else
|
||||||
|
#define ALIGNMENT 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//RV_COMPLIANCE_HALT
|
||||||
|
|
||||||
|
#define RVMODEL_HALT \
|
||||||
|
la t0, begin_signature; \
|
||||||
|
la t1, end_signature; \
|
||||||
|
fence; \
|
||||||
|
ecall
|
||||||
|
|
||||||
|
#define RVMODEL_BOOT \
|
||||||
|
.align 6; \
|
||||||
|
.weak stvec_handler; \
|
||||||
|
.weak mtvec_handler; \
|
||||||
|
/* reset vector */ \
|
||||||
|
j reset_vector; \
|
||||||
|
.align 2; \
|
||||||
|
trap_vector: \
|
||||||
|
/* test whether the test came from pass/fail */ \
|
||||||
|
csrr t5, mcause; \
|
||||||
|
li t6, CAUSE_USER_ECALL; \
|
||||||
|
beq t5, t6, write_tohost; \
|
||||||
|
li t6, CAUSE_SUPERVISOR_ECALL; \
|
||||||
|
beq t5, t6, write_tohost; \
|
||||||
|
li t6, CAUSE_MACHINE_ECALL; \
|
||||||
|
beq t5, t6, write_tohost; \
|
||||||
|
/* if an mtvec_handler is defined, jump to it */ \
|
||||||
|
la t5, mtvec_handler; \
|
||||||
|
beqz t5, 1f; \
|
||||||
|
jr t5; \
|
||||||
|
/* was it an interrupt or an exception? */ \
|
||||||
|
1: csrr t5, mcause; \
|
||||||
|
bgez t5, handle_exception; \
|
||||||
|
handle_exception: \
|
||||||
|
/* we don't know how to handle whatever the exception was */ \
|
||||||
|
other_exception: \
|
||||||
|
/* some unhandlable exception occurred */ \
|
||||||
|
1: ori gp, gp, 1337; \
|
||||||
|
write_tohost: \
|
||||||
|
sw gp, tohost, t5; \
|
||||||
|
j write_tohost; \
|
||||||
|
reset_vector: \
|
||||||
|
li a0, MSTATUS_MPP; \
|
||||||
|
csrs mstatus, a0; \
|
||||||
|
la t0, trap_vector; \
|
||||||
|
csrw mtvec, t0; \
|
||||||
|
la t0, rvtest_init; \
|
||||||
|
csrw mepc, t0; \
|
||||||
|
csrr a0, mhartid; \
|
||||||
|
mret; \
|
||||||
|
1: \
|
||||||
|
|
||||||
|
//RV_COMPLIANCE_DATA_BEGIN
|
||||||
|
#define RVMODEL_DATA_BEGIN \
|
||||||
|
.align 4; .global begin_signature; begin_signature:
|
||||||
|
|
||||||
|
//RV_COMPLIANCE_DATA_END
|
||||||
|
#define RVMODEL_DATA_END \
|
||||||
|
.align 4; .global end_signature; end_signature: \
|
||||||
|
.pushsection .tohost,"aw",@progbits; \
|
||||||
|
.align 8; .global tohost; tohost: .dword 0; \
|
||||||
|
.align 8; .global fromhost; fromhost: .dword 0; \
|
||||||
|
.popsection; \
|
||||||
|
.align 8; .global begin_regstate; begin_regstate: \
|
||||||
|
.word 128; \
|
||||||
|
.align 8; .global end_regstate; end_regstate: \
|
||||||
|
.word 4; \
|
||||||
|
.align ALIGNMENT;\
|
||||||
|
|
||||||
|
#define RVMODEL_IO_INIT
|
||||||
|
|
||||||
|
#define RVMODEL_SET_MSW_INT
|
||||||
|
|
||||||
|
|
||||||
|
#define RVMODEL_CLEAR_MSW_INT
|
||||||
|
|
||||||
|
#define RVMODEL_CLEAR_MTIMER_INT
|
||||||
|
|
||||||
|
#define RVMODEL_CLEAR_MEXT_INT
|
||||||
|
|
||||||
|
#endif // _COMPLIANCE_MODEL_H
|
||||||
|
|
||||||
|
// RISC-V Compliance IO Test Header File
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2005-2018 Imperas Software Ltd., www.imperas.com
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
* either express or implied.
|
||||||
|
*
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// In general the following registers are reserved
|
||||||
|
// ra, a0, t0, t1
|
||||||
|
// x1, x10 x5, x6
|
||||||
|
// new reserve x31
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef _COMPLIANCE_IO_H
|
||||||
|
#define _COMPLIANCE_IO_H
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef RVMODEL_ASSERT
|
||||||
|
# define RVMODEL_IO_QUIET
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
// RV IO Macros (Character transfer by custom instruction)
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
#define STRINGIFY(x) #x
|
||||||
|
#define TOSTRING(x) STRINGIFY(x)
|
||||||
|
|
||||||
|
#define RVTEST_CUSTOM1 0x40000000
|
||||||
|
|
||||||
|
#ifdef RVMODEL_IO_QUIET
|
||||||
|
|
||||||
|
#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)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define RVMODEL_IO_INIT
|
||||||
|
|
||||||
|
#if (__riscv_xlen==32)
|
||||||
|
# define RSIZE 4
|
||||||
|
# define SX sw
|
||||||
|
# define LX lw
|
||||||
|
#endif
|
||||||
|
#if (__riscv_xlen==64)
|
||||||
|
# define RSIZE 8
|
||||||
|
# define SX sd
|
||||||
|
# define LX ld
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// _SP = (volatile register)
|
||||||
|
#define LOCAL_IO_PUSH(_SP) \
|
||||||
|
la _SP, begin_regstate; \
|
||||||
|
SX x1, (1*RSIZE)(_SP); \
|
||||||
|
SX x5, (5*RSIZE)(_SP); \
|
||||||
|
SX x6, (6*RSIZE)(_SP); \
|
||||||
|
SX x8, (8*RSIZE)(_SP); \
|
||||||
|
SX x10, (10*RSIZE)(_SP);
|
||||||
|
|
||||||
|
// _SP = (volatile register)
|
||||||
|
#define LOCAL_IO_POP(_SP) \
|
||||||
|
la _SP, begin_regstate; \
|
||||||
|
LX x1, (1*RSIZE)(_SP); \
|
||||||
|
LX x5, (5*RSIZE)(_SP); \
|
||||||
|
LX x6, (6*RSIZE)(_SP); \
|
||||||
|
LX x8, (8*RSIZE)(_SP); \
|
||||||
|
LX x10, (10*RSIZE)(_SP);
|
||||||
|
|
||||||
|
#define LOCAL_IO_WRITE_GPR(_R) \
|
||||||
|
mv a0, _R; \
|
||||||
|
la t0, FN_WriteA0; \
|
||||||
|
jalr t0;
|
||||||
|
|
||||||
|
#define LOCAL_IO_WRITE_FPR(_F) \
|
||||||
|
fmv.x.s a0, _F; \
|
||||||
|
jal FN_WriteA0;
|
||||||
|
|
||||||
|
#define LOCAL_IO_WRITE_DFPR(_V1, _V2) \
|
||||||
|
mv a0, _V1; \
|
||||||
|
jal FN_WriteA0; \
|
||||||
|
mv a0, _V2; \
|
||||||
|
jal FN_WriteA0; \
|
||||||
|
|
||||||
|
#define LOCAL_IO_PUTC(_R) \
|
||||||
|
la t3, RVTEST_CUSTOM1; \
|
||||||
|
sw _R, (0)(t3);
|
||||||
|
|
||||||
|
|
||||||
|
// Assertion violation: file file.c, line 1234: (expr)
|
||||||
|
// _SP = (volatile register)
|
||||||
|
// _R = GPR
|
||||||
|
// _I = Immediate
|
||||||
|
#define RVMODEL_IO_ASSERT_GPR_EQ(_SP, _R, _I) \
|
||||||
|
LOCAL_IO_PUSH(_SP) \
|
||||||
|
mv s0, _R; \
|
||||||
|
li t0, _I; \
|
||||||
|
beq s0, t0, 20002f; \
|
||||||
|
LOCAL_IO_WRITE_STR("Assertion violation: file "); \
|
||||||
|
LOCAL_IO_WRITE_STR(__FILE__); \
|
||||||
|
LOCAL_IO_WRITE_STR(", line "); \
|
||||||
|
LOCAL_IO_WRITE_STR(TOSTRING(__LINE__)); \
|
||||||
|
LOCAL_IO_WRITE_STR(": "); \
|
||||||
|
LOCAL_IO_WRITE_STR(# _R); \
|
||||||
|
LOCAL_IO_WRITE_STR("("); \
|
||||||
|
LOCAL_IO_WRITE_GPR(s0); \
|
||||||
|
LOCAL_IO_WRITE_STR(") != "); \
|
||||||
|
LOCAL_IO_WRITE_STR(# _I); \
|
||||||
|
LOCAL_IO_WRITE_STR("\n"); \
|
||||||
|
li TESTNUM, 100; \
|
||||||
|
j rvtest_code_end; \
|
||||||
|
20002: \
|
||||||
|
LOCAL_IO_POP(_SP)
|
||||||
|
|
||||||
|
|
||||||
|
// _F = FPR
|
||||||
|
// _C = GPR
|
||||||
|
// _I = Immediate
|
||||||
|
#define RVMODEL_IO_ASSERT_SFPR_EQ(_F, _C, _I) \
|
||||||
|
fmv.x.s t0, _F; \
|
||||||
|
beq _C, t0, 20003f; \
|
||||||
|
LOCAL_IO_WRITE_STR("Assertion violation: file "); \
|
||||||
|
LOCAL_IO_WRITE_STR(__FILE__); \
|
||||||
|
LOCAL_IO_WRITE_STR(", line "); \
|
||||||
|
LOCAL_IO_WRITE_STR(TOSTRING(__LINE__)); \
|
||||||
|
LOCAL_IO_WRITE_STR(": "); \
|
||||||
|
LOCAL_IO_WRITE_STR(# _F); \
|
||||||
|
LOCAL_IO_WRITE_STR("("); \
|
||||||
|
LOCAL_IO_WRITE_FPR(_F); \
|
||||||
|
LOCAL_IO_WRITE_STR(") != "); \
|
||||||
|
LOCAL_IO_WRITE_STR(# _I); \
|
||||||
|
LOCAL_IO_WRITE_STR("\n"); \
|
||||||
|
li TESTNUM, 100; \
|
||||||
|
j rvtest_code_end; \
|
||||||
|
20003:
|
||||||
|
|
||||||
|
// _D = DFPR
|
||||||
|
// _R = GPR
|
||||||
|
// _I = Immediate
|
||||||
|
#define RVMODEL_IO_ASSERT_DFPR_EQ(_D, _R, _I) \
|
||||||
|
fmv.x.d t0, _D; \
|
||||||
|
beq _R, t0, 20005f; \
|
||||||
|
LOCAL_IO_WRITE_STR("Assertion violation: file "); \
|
||||||
|
LOCAL_IO_WRITE_STR(__FILE__); \
|
||||||
|
LOCAL_IO_WRITE_STR(", line "); \
|
||||||
|
LOCAL_IO_WRITE_STR(TOSTRING(__LINE__)); \
|
||||||
|
LOCAL_IO_WRITE_STR(": "); \
|
||||||
|
LOCAL_IO_WRITE_STR(# _D); \
|
||||||
|
LOCAL_IO_WRITE_STR("("); \
|
||||||
|
LOCAL_IO_WRITE_DFPR(_D); \
|
||||||
|
LOCAL_IO_WRITE_STR(") != "); \
|
||||||
|
LOCAL_IO_WRITE_STR(# _I); \
|
||||||
|
LOCAL_IO_WRITE_STR("\n"); \
|
||||||
|
li TESTNUM, 100; \
|
||||||
|
j rvtest_code_end; \
|
||||||
|
20005:
|
||||||
|
|
||||||
|
// _SP = (volatile register)
|
||||||
|
#define LOCAL_IO_WRITE_STR(_STR) RVMODEL_IO_WRITE_STR(x31, _STR)
|
||||||
|
#define RVMODEL_IO_WRITE_STR(_SP, _STR) \
|
||||||
|
LOCAL_IO_PUSH(_SP) \
|
||||||
|
.section .data.string; \
|
||||||
|
20001: \
|
||||||
|
.string _STR; \
|
||||||
|
.section .text.init; \
|
||||||
|
la a0, 20001b; \
|
||||||
|
la t0, FN_WriteStr; \
|
||||||
|
jalr t0; \
|
||||||
|
LOCAL_IO_POP(_SP)
|
||||||
|
|
||||||
|
// generate assertion listing
|
||||||
|
#define LOCAL_CHECK() RVMODEL_IO_CHECK()
|
||||||
|
#define RVMODEL_IO_CHECK() \
|
||||||
|
li zero, -1; \
|
||||||
|
|
||||||
|
//
|
||||||
|
// FN_WriteStr: Uses a0, t0
|
||||||
|
//
|
||||||
|
FN_WriteStr:
|
||||||
|
mv t0, a0;
|
||||||
|
10000:
|
||||||
|
lbu a0, (t0);
|
||||||
|
addi t0, t0, 1;
|
||||||
|
beq a0, zero, 10000f;
|
||||||
|
LOCAL_IO_PUTC(a0);
|
||||||
|
j 10000b;
|
||||||
|
10000:
|
||||||
|
ret;
|
||||||
|
|
||||||
|
//
|
||||||
|
// FN_WriteA0: write register a0(x10) (destroys a0(x10), t0-t2(x5-x7))
|
||||||
|
//
|
||||||
|
FN_WriteA0:
|
||||||
|
mv t0, a0
|
||||||
|
// determine architectural register width
|
||||||
|
li a0, -1
|
||||||
|
srli a0, a0, 31
|
||||||
|
srli a0, a0, 1
|
||||||
|
bnez a0, FN_WriteA0_64
|
||||||
|
|
||||||
|
FN_WriteA0_32:
|
||||||
|
// reverse register when xlen is 32
|
||||||
|
li t1, 8
|
||||||
|
10000: slli t2, t2, 4
|
||||||
|
andi a0, t0, 0xf
|
||||||
|
srli t0, t0, 4
|
||||||
|
or t2, t2, a0
|
||||||
|
addi t1, t1, -1
|
||||||
|
bnez t1, 10000b
|
||||||
|
li t1, 8
|
||||||
|
j FN_WriteA0_common
|
||||||
|
|
||||||
|
FN_WriteA0_64:
|
||||||
|
// reverse register when xlen is 64
|
||||||
|
li t1, 16
|
||||||
|
10000: slli t2, t2, 4
|
||||||
|
andi a0, t0, 0xf
|
||||||
|
srli t0, t0, 4
|
||||||
|
or t2, t2, a0
|
||||||
|
addi t1, t1, -1
|
||||||
|
bnez t1, 10000b
|
||||||
|
li t1, 16
|
||||||
|
|
||||||
|
FN_WriteA0_common:
|
||||||
|
// write reversed characters
|
||||||
|
li t0, 10
|
||||||
|
10000: andi a0, t2, 0xf
|
||||||
|
blt a0, t0, 10001f
|
||||||
|
addi a0, a0, 'a'-10
|
||||||
|
j 10002f
|
||||||
|
10001: addi a0, a0, '0'
|
||||||
|
10002: LOCAL_IO_PUTC(a0)
|
||||||
|
srli t2, t2, 4
|
||||||
|
addi t1, t1, -1
|
||||||
|
bnez t1, 10000b
|
||||||
|
ret
|
||||||
|
|
||||||
|
#endif // RVMODEL_IO_QUIET
|
||||||
|
|
||||||
|
#endif // _COMPLIANCE_IO_H
|
|
@ -52,59 +52,468 @@ namespace riscv_tlm {
|
||||||
/**
|
/**
|
||||||
* @brief Instruction decoding and fields access
|
* @brief Instruction decoding and fields access
|
||||||
*/
|
*/
|
||||||
class A_extension : public extension_base {
|
template<typename T>
|
||||||
|
class A_extension : public extension_base<T> {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Constructor, same as base class
|
* @brief Constructor, same as base class
|
||||||
*/
|
*/
|
||||||
using extension_base::extension_base;
|
using extension_base<T>::extension_base;
|
||||||
|
|
||||||
|
using signed_T = typename std::make_signed<T>::type;
|
||||||
|
using unsigned_T = typename std::make_unsigned<T>::type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Access to opcode field
|
* @brief Access to opcode field
|
||||||
* @return return opcode field
|
* @return return opcode field
|
||||||
*/
|
*/
|
||||||
inline int32_t opcode() const override {
|
inline unsigned_T opcode() const override {
|
||||||
return static_cast<int32_t>(m_instr.range(31, 27));
|
return static_cast<unsigned_T>(this->m_instr.range(31, 27));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Decodes opcode of instruction
|
* @brief Decodes opcode of instruction
|
||||||
* @return opcode of instruction
|
* @return opcode of instruction
|
||||||
*/
|
*/
|
||||||
op_A_Codes decode() const;
|
op_A_Codes decode() const {
|
||||||
|
|
||||||
|
switch (opcode()) {
|
||||||
|
case A_LR:
|
||||||
|
return OP_A_LR;
|
||||||
|
break;
|
||||||
|
case A_SC:
|
||||||
|
return OP_A_SC;
|
||||||
|
break;
|
||||||
|
case A_AMOSWAP:
|
||||||
|
return OP_A_AMOSWAP;
|
||||||
|
break;
|
||||||
|
case A_AMOADD:
|
||||||
|
return OP_A_AMOADD;
|
||||||
|
break;
|
||||||
|
case A_AMOXOR:
|
||||||
|
return OP_A_AMOXOR;
|
||||||
|
break;
|
||||||
|
case A_AMOAND:
|
||||||
|
return OP_A_AMOAND;
|
||||||
|
break;
|
||||||
|
case A_AMOOR:
|
||||||
|
return OP_A_AMOOR;
|
||||||
|
break;
|
||||||
|
case A_AMOMIN:
|
||||||
|
return OP_A_AMOMIN;
|
||||||
|
break;
|
||||||
|
case A_AMOMAX:
|
||||||
|
return OP_A_AMOMAX;
|
||||||
|
break;
|
||||||
|
case A_AMOMINU:
|
||||||
|
return OP_A_AMOMINU;
|
||||||
|
break;
|
||||||
|
case A_AMOMAXU:
|
||||||
|
return OP_A_AMOMAXU;
|
||||||
|
break;
|
||||||
|
[[unlikely]] default:
|
||||||
|
return OP_A_ERROR;
|
||||||
|
break;
|
||||||
|
|
||||||
inline void dump() const override {
|
|
||||||
std::cout << std::hex << "0x" << m_instr << std::dec << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Exec_A_LR();
|
return OP_A_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
bool Exec_A_SC();
|
inline void dump() const override {
|
||||||
|
std::cout << std::hex << "0x" << this->m_instr << std::dec << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
bool Exec_A_AMOSWAP() const;
|
bool Exec_A_LR() {
|
||||||
|
std::uint32_t mem_addr = 0;
|
||||||
|
int rd, rs1, rs2;
|
||||||
|
std::uint32_t data;
|
||||||
|
|
||||||
bool Exec_A_AMOADD() const;
|
rd = this->get_rd();
|
||||||
|
rs1 = this->get_rs1();
|
||||||
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
bool Exec_A_AMOXOR() const;
|
if (rs2 != 0) {
|
||||||
|
std::cout << "ILEGAL INSTRUCTION, LR.W: rs2 != 0" << std::endl;
|
||||||
|
this->RaiseException(EXCEPTION_CAUSE_ILLEGAL_INSTRUCTION, this->m_instr);
|
||||||
|
|
||||||
bool Exec_A_AMOAND() const;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool Exec_A_AMOOR() const;
|
mem_addr = this->regs->getValue(rs1);
|
||||||
|
data = this->mem_intf->readDataMem(mem_addr, 4);
|
||||||
|
this->perf->dataMemoryRead();
|
||||||
|
this->regs->setValue(rd, static_cast<int32_t>(data));
|
||||||
|
|
||||||
bool Exec_A_AMOMIN() const;
|
TLB_reserve(mem_addr);
|
||||||
|
|
||||||
bool Exec_A_AMOMAX() const;
|
this->logger->debug("{} ns. PC: 0x{:x}. A.LR.W: x{:d}(0x{:x}) -> x{:d}(0x{:x}) ",
|
||||||
|
sc_core::sc_time_stamp().value(),
|
||||||
|
this->regs->getPC(),
|
||||||
|
rs1, mem_addr, rd, data);
|
||||||
|
|
||||||
bool Exec_A_AMOMINU() const;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Exec_A_AMOMAXU() const;
|
bool Exec_A_SC() {
|
||||||
|
std::uint32_t mem_addr;
|
||||||
|
int rd, rs1, rs2;
|
||||||
|
std::uint32_t data;
|
||||||
|
|
||||||
bool process_instruction(Instruction &inst);
|
rd = this->get_rd();
|
||||||
|
rs1 = this->get_rs1();
|
||||||
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
void TLB_reserve(std::uint32_t address);
|
mem_addr = this->regs->getValue(rs1);
|
||||||
|
data = this->regs->getValue(rs2);
|
||||||
|
|
||||||
bool TLB_reserved(std::uint32_t address);
|
if (TLB_reserved(mem_addr)) {
|
||||||
|
this->mem_intf->writeDataMem(mem_addr, data, 4);
|
||||||
|
this->perf->dataMemoryWrite();
|
||||||
|
this->regs->setValue(rd, 0); // SC writes 0 to rd on success
|
||||||
|
} else {
|
||||||
|
this->regs->setValue(rd, 1); // SC writes nonzero on failure
|
||||||
|
}
|
||||||
|
|
||||||
|
this->logger->debug("{} ns. PC: 0x{:x}. A.SC.W: (0x{:x}) <- x{:d}(0x{:x}) ",
|
||||||
|
sc_core::sc_time_stamp().value(),
|
||||||
|
this->regs->getPC(),
|
||||||
|
mem_addr, rs2, data);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Exec_A_AMOSWAP() const {
|
||||||
|
std::uint32_t mem_addr;
|
||||||
|
int rd, rs1, rs2;
|
||||||
|
std::uint32_t data;
|
||||||
|
std::uint32_t aux;
|
||||||
|
|
||||||
|
/* These instructions must be atomic */
|
||||||
|
|
||||||
|
rd = this->get_rd();
|
||||||
|
rs1 = this->get_rs1();
|
||||||
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
|
mem_addr = this->regs->getValue(rs1);
|
||||||
|
data = this->mem_intf->readDataMem(mem_addr, 4);
|
||||||
|
this->perf->dataMemoryRead();
|
||||||
|
this->regs->setValue(rd, static_cast<int32_t>(data));
|
||||||
|
|
||||||
|
// swap
|
||||||
|
aux = this->regs->getValue(rs2);
|
||||||
|
this->regs->setValue(rs2, static_cast<int32_t>(data));
|
||||||
|
|
||||||
|
this->mem_intf->writeDataMem(mem_addr, aux, 4);
|
||||||
|
this->perf->dataMemoryWrite();
|
||||||
|
|
||||||
|
this->logger->debug("{} ns. PC: 0x{:x}. A.AMOSWAP");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Exec_A_AMOADD() const {
|
||||||
|
std::uint32_t mem_addr;
|
||||||
|
int rd, rs1, rs2;
|
||||||
|
std::uint32_t data;
|
||||||
|
|
||||||
|
/* These instructions must be atomic */
|
||||||
|
|
||||||
|
rd = this->get_rd();
|
||||||
|
rs1 = this->get_rs1();
|
||||||
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
|
mem_addr = this->regs->getValue(rs1);
|
||||||
|
data = this->mem_intf->readDataMem(mem_addr, 4);
|
||||||
|
this->perf->dataMemoryRead();
|
||||||
|
|
||||||
|
this->regs->setValue(rd, static_cast<int32_t>(data));
|
||||||
|
|
||||||
|
// add
|
||||||
|
data = data + this->regs->getValue(rs2);
|
||||||
|
|
||||||
|
this->mem_intf->writeDataMem(mem_addr, data, 4);
|
||||||
|
this->perf->dataMemoryWrite();
|
||||||
|
|
||||||
|
this->logger->debug("{} ns. PC: 0x{:x}. A.AMOADD");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Exec_A_AMOXOR() const {
|
||||||
|
std::uint32_t mem_addr;
|
||||||
|
int rd, rs1, rs2;
|
||||||
|
std::uint32_t data;
|
||||||
|
|
||||||
|
/* These instructions must be atomic */
|
||||||
|
|
||||||
|
rd = this->get_rd();
|
||||||
|
rs1 = this->get_rs1();
|
||||||
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
|
mem_addr = this->regs->getValue(rs1);
|
||||||
|
data = this->mem_intf->readDataMem(mem_addr, 4);
|
||||||
|
this->perf->dataMemoryRead();
|
||||||
|
|
||||||
|
this->regs->setValue(rd, static_cast<int32_t>(data));
|
||||||
|
|
||||||
|
// add
|
||||||
|
data = data ^ this->regs->getValue(rs2);
|
||||||
|
|
||||||
|
this->mem_intf->writeDataMem(mem_addr, data, 4);
|
||||||
|
this->perf->dataMemoryWrite();
|
||||||
|
|
||||||
|
this->logger->debug("{} ns. PC: 0x{:x}. A.AMOXOR");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Exec_A_AMOAND() const {
|
||||||
|
std::uint32_t mem_addr;
|
||||||
|
int rd, rs1, rs2;
|
||||||
|
std::uint32_t data;
|
||||||
|
|
||||||
|
/* These instructions must be atomic */
|
||||||
|
|
||||||
|
rd = this->get_rd();
|
||||||
|
rs1 = this->get_rs1();
|
||||||
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
|
mem_addr = this->regs->getValue(rs1);
|
||||||
|
data = this->mem_intf->readDataMem(mem_addr, 4);
|
||||||
|
this->perf->dataMemoryRead();
|
||||||
|
|
||||||
|
this->regs->setValue(rd, static_cast<int32_t>(data));
|
||||||
|
|
||||||
|
// add
|
||||||
|
data = data & this->regs->getValue(rs2);
|
||||||
|
|
||||||
|
this->mem_intf->writeDataMem(mem_addr, data, 4);
|
||||||
|
this->perf->dataMemoryWrite();
|
||||||
|
|
||||||
|
this->logger->debug("{} ns. PC: 0x{:x}. A.AMOAND");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Exec_A_AMOOR() const {
|
||||||
|
std::uint32_t mem_addr;
|
||||||
|
int rd, rs1, rs2;
|
||||||
|
std::uint32_t data;
|
||||||
|
|
||||||
|
/* These instructions must be atomic */
|
||||||
|
|
||||||
|
rd = this->get_rd();
|
||||||
|
rs1 = this->get_rs1();
|
||||||
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
|
mem_addr = this->regs->getValue(rs1);
|
||||||
|
data = this->mem_intf->readDataMem(mem_addr, 4);
|
||||||
|
this->perf->dataMemoryRead();
|
||||||
|
|
||||||
|
this->regs->setValue(rd, static_cast<int32_t>(data));
|
||||||
|
|
||||||
|
// add
|
||||||
|
data = data | this->regs->getValue(rs2);
|
||||||
|
|
||||||
|
this->mem_intf->writeDataMem(mem_addr, data, 4);
|
||||||
|
this->perf->dataMemoryWrite();
|
||||||
|
|
||||||
|
this->logger->debug("{} ns. PC: 0x{:x}. A.AMOOR");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Exec_A_AMOMIN() const {
|
||||||
|
std::uint32_t mem_addr;
|
||||||
|
int rd, rs1, rs2;
|
||||||
|
std::uint32_t data;
|
||||||
|
std::uint32_t aux;
|
||||||
|
|
||||||
|
/* These instructions must be atomic */
|
||||||
|
|
||||||
|
rd = this->get_rd();
|
||||||
|
rs1 = this->get_rs1();
|
||||||
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
|
mem_addr = this->regs->getValue(rs1);
|
||||||
|
data = this->mem_intf->readDataMem(mem_addr, 4);
|
||||||
|
this->perf->dataMemoryRead();
|
||||||
|
|
||||||
|
this->regs->setValue(rd, static_cast<int32_t>(data));
|
||||||
|
|
||||||
|
// min
|
||||||
|
aux = this->regs->getValue(rs2);
|
||||||
|
if ((int32_t) data < (int32_t) aux) {
|
||||||
|
aux = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->mem_intf->writeDataMem(mem_addr, aux, 4);
|
||||||
|
this->perf->dataMemoryWrite();
|
||||||
|
|
||||||
|
this->logger->debug("{} ns. PC: 0x{:x}. A.AMOMIN");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Exec_A_AMOMAX() const {
|
||||||
|
std::uint32_t mem_addr;
|
||||||
|
int rd, rs1, rs2;
|
||||||
|
std::uint32_t data;
|
||||||
|
std::uint32_t aux;
|
||||||
|
|
||||||
|
/* These instructions must be atomic */
|
||||||
|
|
||||||
|
rd = this->get_rd();
|
||||||
|
rs1 = this->get_rs1();
|
||||||
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
|
mem_addr = this->regs->getValue(rs1);
|
||||||
|
data = this->mem_intf->readDataMem(mem_addr, 4);
|
||||||
|
this->perf->dataMemoryRead();
|
||||||
|
|
||||||
|
this->regs->setValue(rd, static_cast<int32_t>(data));
|
||||||
|
|
||||||
|
// >
|
||||||
|
aux = this->regs->getValue(rs2);
|
||||||
|
if ((int32_t) data > (int32_t) aux) {
|
||||||
|
aux = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->mem_intf->writeDataMem(mem_addr, aux, 4);
|
||||||
|
this->perf->dataMemoryWrite();
|
||||||
|
|
||||||
|
this->logger->debug("{} ns. PC: 0x{:x}. A.AMOMAX");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Exec_A_AMOMINU() const {
|
||||||
|
std::uint32_t mem_addr;
|
||||||
|
int rd, rs1, rs2;
|
||||||
|
std::uint32_t data;
|
||||||
|
std::uint32_t aux;
|
||||||
|
|
||||||
|
/* These instructions must be atomic */
|
||||||
|
|
||||||
|
rd = this->get_rd();
|
||||||
|
rs1 = this->get_rs1();
|
||||||
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
|
mem_addr = this->regs->getValue(rs1);
|
||||||
|
data = this->mem_intf->readDataMem(mem_addr, 4);
|
||||||
|
this->perf->dataMemoryRead();
|
||||||
|
|
||||||
|
this->regs->setValue(rd, static_cast<int32_t>(data));
|
||||||
|
|
||||||
|
// min
|
||||||
|
aux = this->regs->getValue(rs2);
|
||||||
|
if (data < aux) {
|
||||||
|
aux = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->mem_intf->writeDataMem(mem_addr, aux, 4);
|
||||||
|
this->perf->dataMemoryWrite();
|
||||||
|
|
||||||
|
this->logger->debug("{} ns. PC: 0x{:x}. A.AMOMINU");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Exec_A_AMOMAXU() const {
|
||||||
|
std::uint32_t mem_addr;
|
||||||
|
int rd, rs1, rs2;
|
||||||
|
std::uint32_t data;
|
||||||
|
std::uint32_t aux;
|
||||||
|
|
||||||
|
/* These instructions must be atomic */
|
||||||
|
|
||||||
|
rd = this->get_rd();
|
||||||
|
rs1 = this->get_rs1();
|
||||||
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
|
mem_addr = this->regs->getValue(rs1);
|
||||||
|
data = this->mem_intf->readDataMem(mem_addr, 4);
|
||||||
|
this->perf->dataMemoryRead();
|
||||||
|
|
||||||
|
this->regs->setValue(rd, static_cast<int32_t>(data));
|
||||||
|
|
||||||
|
// max
|
||||||
|
aux = this->regs->getValue(rs2);
|
||||||
|
if (data > aux) {
|
||||||
|
aux = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->mem_intf->writeDataMem(mem_addr, aux, 4);
|
||||||
|
this->perf->dataMemoryWrite();
|
||||||
|
|
||||||
|
this->logger->debug("{} ns. PC: 0x{:x}. A.AMOMAXU");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TLB_reserve(std::uint32_t address) {
|
||||||
|
TLB_A_Entries.insert(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TLB_reserved(std::uint32_t address) {
|
||||||
|
if (TLB_A_Entries.count(address) == 1) {
|
||||||
|
TLB_A_Entries.erase(address);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool process_instruction(Instruction &inst) {
|
||||||
|
bool PC_not_affected = true;
|
||||||
|
|
||||||
|
this->setInstr(inst.getInstr());
|
||||||
|
|
||||||
|
switch (decode()) {
|
||||||
|
case OP_A_LR:
|
||||||
|
Exec_A_LR();
|
||||||
|
break;
|
||||||
|
case OP_A_SC:
|
||||||
|
Exec_A_SC();
|
||||||
|
break;
|
||||||
|
case OP_A_AMOSWAP:
|
||||||
|
Exec_A_AMOSWAP();
|
||||||
|
break;
|
||||||
|
case OP_A_AMOADD:
|
||||||
|
Exec_A_AMOADD();
|
||||||
|
break;
|
||||||
|
case OP_A_AMOXOR:
|
||||||
|
Exec_A_AMOXOR();
|
||||||
|
break;
|
||||||
|
case OP_A_AMOAND:
|
||||||
|
Exec_A_AMOAND();
|
||||||
|
break;
|
||||||
|
case OP_A_AMOOR:
|
||||||
|
Exec_A_AMOOR();
|
||||||
|
break;
|
||||||
|
case OP_A_AMOMIN:
|
||||||
|
Exec_A_AMOMIN();
|
||||||
|
break;
|
||||||
|
case OP_A_AMOMAX:
|
||||||
|
Exec_A_AMOMAX();
|
||||||
|
break;
|
||||||
|
case OP_A_AMOMINU:
|
||||||
|
Exec_A_AMOMINU();
|
||||||
|
break;
|
||||||
|
case OP_A_AMOMAXU:
|
||||||
|
Exec_A_AMOMAXU();
|
||||||
|
break;
|
||||||
|
[[unlikely]] default:
|
||||||
|
std::cout << "A instruction not implemented yet" << std::endl;
|
||||||
|
inst.dump();
|
||||||
|
this->NOP();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PC_not_affected;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_set<std::uint32_t> TLB_A_Entries;
|
std::unordered_set<std::uint32_t> TLB_A_Entries;
|
||||||
|
|
1982
inc/BASE_ISA.h
1982
inc/BASE_ISA.h
File diff suppressed because it is too large
Load Diff
186
inc/CPU.h
186
inc/CPU.h
|
@ -28,13 +28,28 @@
|
||||||
|
|
||||||
|
|
||||||
namespace riscv_tlm {
|
namespace riscv_tlm {
|
||||||
/**
|
|
||||||
* @brief ISC_V CPU model
|
|
||||||
* @param name name of the module
|
|
||||||
*/
|
|
||||||
class CPU : sc_core::sc_module {
|
class CPU : sc_core::sc_module {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
/* Constructors */
|
||||||
|
explicit CPU(sc_core::sc_module_name const &name, bool debug);
|
||||||
|
|
||||||
|
CPU() noexcept = delete;
|
||||||
|
CPU(const CPU& other) noexcept = delete;
|
||||||
|
CPU(CPU && other) noexcept = delete;
|
||||||
|
CPU& operator=(const CPU& other) noexcept = delete;
|
||||||
|
CPU& operator=(CPU&& other) noexcept = delete;
|
||||||
|
|
||||||
|
/* Destructors */
|
||||||
|
~CPU() override = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Perform one instruction step
|
||||||
|
* @return Breackpoint found (TBD, always true)
|
||||||
|
*/
|
||||||
|
virtual bool CPU_step() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Instruction Memory bus socket
|
* @brief Instruction Memory bus socket
|
||||||
* @param trans transction to perfoem
|
* @param trans transction to perfoem
|
||||||
|
@ -49,60 +64,91 @@ namespace riscv_tlm {
|
||||||
*/
|
*/
|
||||||
tlm_utils::simple_target_socket<CPU> irq_line_socket;
|
tlm_utils::simple_target_socket<CPU> irq_line_socket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief DMI pointer is not longer valid
|
||||||
|
* @param start memory address region start
|
||||||
|
* @param end memory address region end
|
||||||
|
*/
|
||||||
|
void invalidate_direct_mem_ptr(sc_dt::uint64 start, sc_dt::uint64 end);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief CPU main thread
|
||||||
|
*/
|
||||||
|
[[noreturn]] void CPU_thread();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Process and triggers IRQ if all conditions met
|
||||||
|
* @return true if IRQ is triggered, false otherwise
|
||||||
|
*/
|
||||||
|
virtual bool cpu_process_IRQ() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief callback for IRQ simple socket
|
||||||
|
* @param trans transaction to perform (empty)
|
||||||
|
* @param delay time to annotate
|
||||||
|
*
|
||||||
|
* it triggers an IRQ when called
|
||||||
|
*/
|
||||||
|
virtual void call_interrupt(tlm::tlm_generic_payload &trans,
|
||||||
|
sc_core::sc_time &delay) = 0;
|
||||||
|
|
||||||
|
virtual std::uint64_t getStartDumpAddress() = 0;
|
||||||
|
virtual std::uint64_t getEndDumpAddress() = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
MemoryInterface *mem_intf;
|
||||||
|
protected:
|
||||||
|
Performance *perf;
|
||||||
|
std::shared_ptr<spdlog::logger> logger;
|
||||||
|
tlm_utils::tlm_quantumkeeper *m_qk;
|
||||||
|
Instruction inst;
|
||||||
|
bool interrupt;
|
||||||
|
bool irq_already_down;
|
||||||
|
sc_core::sc_time default_time;
|
||||||
|
bool dmi_ptr_valid;
|
||||||
|
tlm::tlm_generic_payload trans;
|
||||||
|
unsigned char *dmi_ptr = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief RISC_V CPU 32 bits model
|
||||||
|
* @param name name of the module
|
||||||
|
*/
|
||||||
|
class RV32 : public CPU {
|
||||||
|
public:
|
||||||
|
using BaseType = std::uint32_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Constructor
|
* @brief Constructor
|
||||||
* @param name Module name
|
* @param name Module name
|
||||||
* @param PC Program Counter initialize value
|
* @param PC Program Counter initialize value
|
||||||
* @param debug To start debugging
|
* @param debug To start debugging
|
||||||
*/
|
*/
|
||||||
CPU(sc_core::sc_module_name const &name, std::uint32_t PC, bool debug);
|
RV32(sc_core::sc_module_name const &name, BaseType PC, bool debug);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Destructor
|
* @brief Destructor
|
||||||
*/
|
*/
|
||||||
~CPU() override;
|
~RV32() override;
|
||||||
|
|
||||||
MemoryInterface *mem_intf;
|
bool CPU_step() override;
|
||||||
|
Registers<BaseType> *getRegisterBank() { return register_bank; }
|
||||||
bool CPU_step();
|
|
||||||
|
|
||||||
|
|
||||||
Registers *getRegisterBank() { return register_bank; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Registers *register_bank;
|
Registers<BaseType> *register_bank;
|
||||||
Performance *perf;
|
C_extension<BaseType> *c_inst;
|
||||||
std::shared_ptr<spdlog::logger> logger;
|
M_extension<BaseType> *m_inst;
|
||||||
C_extension *c_inst;
|
A_extension<BaseType> *a_inst;
|
||||||
M_extension *m_inst;
|
BASE_ISA<BaseType> *exec;
|
||||||
A_extension *a_inst;
|
BaseType int_cause;
|
||||||
BASE_ISA *exec;
|
BaseType INSTR;
|
||||||
tlm_utils::tlm_quantumkeeper *m_qk;
|
|
||||||
|
|
||||||
Instruction inst;
|
|
||||||
bool interrupt;
|
|
||||||
std::uint32_t int_cause;
|
|
||||||
bool irq_already_down;
|
|
||||||
sc_core::sc_time default_time;
|
|
||||||
bool dmi_ptr_valid;
|
|
||||||
|
|
||||||
tlm::tlm_generic_payload trans;
|
|
||||||
std::uint32_t INSTR;
|
|
||||||
unsigned char *dmi_ptr = nullptr;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @brief Process and triggers IRQ if all conditions met
|
* @brief Process and triggers IRQ if all conditions met
|
||||||
* @return true if IRQ is triggered, false otherwise
|
* @return true if IRQ is triggered, false otherwise
|
||||||
*/
|
*/
|
||||||
bool cpu_process_IRQ();
|
bool cpu_process_IRQ() override;
|
||||||
|
|
||||||
/**
|
|
||||||
* main thread for CPU simulation
|
|
||||||
* @brief CPU mai thread
|
|
||||||
*/
|
|
||||||
[[noreturn]] void CPU_thread();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief callback for IRQ simple socket
|
* @brief callback for IRQ simple socket
|
||||||
|
@ -112,15 +158,65 @@ namespace riscv_tlm {
|
||||||
* it triggers an IRQ when called
|
* it triggers an IRQ when called
|
||||||
*/
|
*/
|
||||||
void call_interrupt(tlm::tlm_generic_payload &trans,
|
void call_interrupt(tlm::tlm_generic_payload &trans,
|
||||||
sc_core::sc_time &delay);
|
sc_core::sc_time &delay) override;
|
||||||
|
|
||||||
|
std::uint64_t getStartDumpAddress() override;
|
||||||
|
std::uint64_t getEndDumpAddress() override;
|
||||||
|
}; // RV32 class
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DMI pointer is not longer valid
|
* @brief RISC_V CPU 64 bits model
|
||||||
* @param start memory address region start
|
* @param name name of the module
|
||||||
* @param end memory address region end
|
|
||||||
*/
|
*/
|
||||||
void invalidate_direct_mem_ptr(sc_dt::uint64 start, sc_dt::uint64 end);
|
class RV64 : public CPU {
|
||||||
};
|
public:
|
||||||
|
using BaseType = std::uint64_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructor
|
||||||
|
* @param name Module name
|
||||||
|
* @param PC Program Counter initialize value
|
||||||
|
* @param debug To start debugging
|
||||||
|
*/
|
||||||
|
RV64(sc_core::sc_module_name const &name, BaseType PC, bool debug);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Destructor
|
||||||
|
*/
|
||||||
|
~RV64() override;
|
||||||
|
|
||||||
|
bool CPU_step() override;
|
||||||
|
Registers<BaseType> *getRegisterBank() { return register_bank; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Registers<BaseType> *register_bank;
|
||||||
|
C_extension<BaseType> *c_inst;
|
||||||
|
M_extension<BaseType> *m_inst;
|
||||||
|
A_extension<BaseType> *a_inst;
|
||||||
|
BASE_ISA<BaseType> *exec;
|
||||||
|
BaseType int_cause;
|
||||||
|
BaseType INSTR;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @brief Process and triggers IRQ if all conditions met
|
||||||
|
* @return true if IRQ is triggered, false otherwise
|
||||||
|
*/
|
||||||
|
bool cpu_process_IRQ() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief callback for IRQ simple socket
|
||||||
|
* @param trans transaction to perform (empty)
|
||||||
|
* @param delay time to annotate
|
||||||
|
*
|
||||||
|
* it triggers an IRQ when called
|
||||||
|
*/
|
||||||
|
void call_interrupt(tlm::tlm_generic_payload &trans,
|
||||||
|
sc_core::sc_time &delay) override;
|
||||||
|
|
||||||
|
std::uint64_t getStartDumpAddress() override;
|
||||||
|
std::uint64_t getEndDumpAddress() override;
|
||||||
|
}; // RV64 class
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
1277
inc/C_extension.h
1277
inc/C_extension.h
File diff suppressed because it is too large
Load Diff
|
@ -25,7 +25,7 @@ namespace riscv_tlm {
|
||||||
class Debug : sc_core::sc_module {
|
class Debug : sc_core::sc_module {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Debug(riscv_tlm::CPU *cpu, Memory *mem);
|
Debug(riscv_tlm::RV32 *cpu, Memory *mem);
|
||||||
|
|
||||||
~Debug() override;
|
~Debug() override;
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ namespace riscv_tlm {
|
||||||
static constexpr size_t bufsize = 1024 * 8;
|
static constexpr size_t bufsize = 1024 * 8;
|
||||||
char iobuf[bufsize]{};
|
char iobuf[bufsize]{};
|
||||||
int conn;
|
int conn;
|
||||||
riscv_tlm::CPU *dbg_cpu;
|
riscv_tlm::RV32 *dbg_cpu;
|
||||||
Memory *dbg_mem;
|
Memory *dbg_mem;
|
||||||
tlm::tlm_generic_payload dbg_trans;
|
tlm::tlm_generic_payload dbg_trans;
|
||||||
unsigned char pyld_array[128]{};
|
unsigned char pyld_array[128]{};
|
||||||
|
|
|
@ -25,7 +25,11 @@ namespace riscv_tlm {
|
||||||
OP_M_DIVU,
|
OP_M_DIVU,
|
||||||
OP_M_REM,
|
OP_M_REM,
|
||||||
OP_M_REMU,
|
OP_M_REMU,
|
||||||
|
OP_M_MULW,
|
||||||
|
OP_M_DIVW,
|
||||||
|
OP_M_DIVUW,
|
||||||
|
OP_M_REMW,
|
||||||
|
OP_M_REMUW,
|
||||||
OP_M_ERROR
|
OP_M_ERROR
|
||||||
} op_M_Codes;
|
} op_M_Codes;
|
||||||
|
|
||||||
|
@ -38,46 +42,414 @@ namespace riscv_tlm {
|
||||||
M_DIVU = 0b101,
|
M_DIVU = 0b101,
|
||||||
M_REM = 0b110,
|
M_REM = 0b110,
|
||||||
M_REMU = 0b111,
|
M_REMU = 0b111,
|
||||||
|
M_MULW = 0b000,
|
||||||
|
M_DIVW = 0b100,
|
||||||
|
M_DIVUW = 0b101,
|
||||||
|
M_REMW = 0b110,
|
||||||
|
M_REMUW = 0b111,
|
||||||
} M_Codes;
|
} M_Codes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Instruction decoding and fields access
|
* @brief Instruction decoding and fields access
|
||||||
*/
|
*/
|
||||||
class M_extension : public extension_base {
|
template<typename T>
|
||||||
|
class M_extension : public extension_base<T> {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Constructor, same as base clase
|
* @brief Constructor, same as base class
|
||||||
*/
|
*/
|
||||||
using extension_base::extension_base;
|
using extension_base<T>::extension_base;
|
||||||
|
|
||||||
|
using signed_T = typename std::make_signed<T>::type;
|
||||||
|
using unsigned_T = typename std::make_unsigned<T>::type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Decodes opcode of instruction
|
* @brief Decodes opcode of instruction
|
||||||
* @return opcode of instruction
|
* @return opcode of instruction
|
||||||
*/
|
*/
|
||||||
op_M_Codes decode() const;
|
[[nodiscard]] op_M_Codes decode() const {
|
||||||
|
|
||||||
inline void dump() const override {
|
if (this->m_instr.range(6,0) == 0b110011) {
|
||||||
std::cout << std::hex << "0x" << m_instr << std::dec << std::endl;
|
switch (opcode()) {
|
||||||
|
case M_MUL:
|
||||||
|
return OP_M_MUL;
|
||||||
|
break;
|
||||||
|
case M_MULH:
|
||||||
|
return OP_M_MULH;
|
||||||
|
break;
|
||||||
|
case M_MULHSU:
|
||||||
|
return OP_M_MULHSU;
|
||||||
|
break;
|
||||||
|
case M_MULHU:
|
||||||
|
return OP_M_MULHU;
|
||||||
|
break;
|
||||||
|
case M_DIV:
|
||||||
|
return OP_M_DIV;
|
||||||
|
break;
|
||||||
|
case M_DIVU:
|
||||||
|
return OP_M_DIVU;
|
||||||
|
break;
|
||||||
|
case M_REM:
|
||||||
|
return OP_M_REM;
|
||||||
|
break;
|
||||||
|
case M_REMU:
|
||||||
|
return OP_M_REMU;
|
||||||
|
break;
|
||||||
|
[[unlikely]] default:
|
||||||
|
return OP_M_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (opcode()) {
|
||||||
|
case M_MULW:
|
||||||
|
return OP_M_MULW;
|
||||||
|
break;
|
||||||
|
case M_DIVW:
|
||||||
|
return OP_M_DIVW;
|
||||||
|
break;
|
||||||
|
case M_DIVUW:
|
||||||
|
return OP_M_DIVUW;
|
||||||
|
break;
|
||||||
|
case M_REMW:
|
||||||
|
return OP_M_REMW;
|
||||||
|
break;
|
||||||
|
case M_REMUW:
|
||||||
|
return OP_M_REMUW;
|
||||||
|
break;
|
||||||
|
[[unlikely]] default:
|
||||||
|
return OP_M_ERROR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Exec_M_MUL() const;
|
return OP_M_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
bool Exec_M_MULH() const;
|
inline void dump() const override {
|
||||||
|
std::cout << std::hex << "0x" << this->m_instr << std::dec << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
bool Exec_M_MULHSU() const;
|
void Exec_M_MUL() const {
|
||||||
|
unsigned int rd, rs1, rs2;
|
||||||
|
signed_T multiplier, multiplicand;
|
||||||
|
std::int64_t result;
|
||||||
|
|
||||||
bool Exec_M_MULHU() const;
|
rd = this->get_rd();
|
||||||
|
rs1 = this->get_rs1();
|
||||||
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
bool Exec_M_DIV() const;
|
multiplier = static_cast<signed_T>(this->regs->getValue(rs1));
|
||||||
|
multiplicand = static_cast<signed_T>(this->regs->getValue(rs2));
|
||||||
|
result = static_cast<std::int64_t>(multiplier * multiplicand);
|
||||||
|
|
||||||
bool Exec_M_DIVU() const;
|
this->regs->setValue(rd, static_cast<signed_T>(result));
|
||||||
|
|
||||||
bool Exec_M_REM() const;
|
this->logger->debug("{} ns. PC: 0x{:x}. M.MUL: x{:d}({:d}) * x{:d}({:d}) -> x{:d}({:d})",
|
||||||
|
sc_core::sc_time_stamp().value(),
|
||||||
|
this->regs->getPC(),
|
||||||
|
rs1, rs2, multiplier, multiplicand, rd, result);
|
||||||
|
}
|
||||||
|
|
||||||
bool Exec_M_REMU() const;
|
void Exec_M_MULH() const;
|
||||||
|
|
||||||
bool process_instruction(Instruction &inst);
|
void Exec_M_MULHSU() const;
|
||||||
|
|
||||||
|
void Exec_M_MULHU() const;
|
||||||
|
|
||||||
|
void Exec_M_DIV() const {
|
||||||
|
unsigned int rd, rs1, rs2;
|
||||||
|
signed_T divisor, dividend;
|
||||||
|
std::int64_t result;
|
||||||
|
|
||||||
|
rd = this->get_rd();
|
||||||
|
rs1 = this->get_rs1();
|
||||||
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
|
dividend = this->regs->getValue(rs1);
|
||||||
|
divisor = this->regs->getValue(rs2);
|
||||||
|
|
||||||
|
if (divisor == 0) {
|
||||||
|
result = -1;
|
||||||
|
} else if ((divisor == -1) && (dividend == static_cast<std::int32_t>(0x80000000))) {
|
||||||
|
result = 0x0000000080000000;
|
||||||
|
} else {
|
||||||
|
result = dividend / divisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->regs->setValue(rd, result);
|
||||||
|
|
||||||
|
this->logger->debug("{} ns. PC: 0x{:x}. M.DIV: x{:d} / x{:d} -> x{:d}({:d})",
|
||||||
|
sc_core::sc_time_stamp().value(),
|
||||||
|
this->regs->getPC(),
|
||||||
|
rs1, rs2, rd, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Exec_M_DIVU() const {
|
||||||
|
unsigned int rd, rs1, rs2;
|
||||||
|
unsigned_T divisor, dividend;
|
||||||
|
std::uint64_t result;
|
||||||
|
|
||||||
|
rd = this->get_rd();
|
||||||
|
rs1 = this->get_rs1();
|
||||||
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
|
dividend = this->regs->getValue(rs1);
|
||||||
|
divisor = this->regs->getValue(rs2);
|
||||||
|
|
||||||
|
if (divisor == 0) {
|
||||||
|
result = -1;
|
||||||
|
} else {
|
||||||
|
result = dividend / divisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->regs->setValue(rd, result);
|
||||||
|
|
||||||
|
this->logger->debug("{} ns. PC: 0x{:x}. M.DIVU: x{:d} / x{:d} -> x{:d}({:d})",
|
||||||
|
sc_core::sc_time_stamp().value(),
|
||||||
|
this->regs->getPC(),
|
||||||
|
rs1, rs2, rd, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Exec_M_REM() const {
|
||||||
|
unsigned int rd, rs1, rs2;
|
||||||
|
signed_T divisor, dividend;
|
||||||
|
signed_T result;
|
||||||
|
|
||||||
|
rd = this->get_rd();
|
||||||
|
rs1 = this->get_rs1();
|
||||||
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
|
dividend = this->regs->getValue(rs1);
|
||||||
|
divisor = this->regs->getValue(rs2);
|
||||||
|
|
||||||
|
if (divisor == 0) {
|
||||||
|
result = dividend;
|
||||||
|
} else if ((divisor == -1) && (dividend == static_cast<std::int32_t>(0x80000000))) {
|
||||||
|
result = 0;
|
||||||
|
} else {
|
||||||
|
result = dividend % divisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->regs->setValue(rd, result);
|
||||||
|
|
||||||
|
this->logger->debug("{} ns. PC: 0x{:x}. M.REM: x{:d} % x{:d} -> x{:d}({:d})",
|
||||||
|
sc_core::sc_time_stamp().value(),
|
||||||
|
this->regs->getPC(),
|
||||||
|
rs1, rs2, rd, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Exec_M_REMU() const {
|
||||||
|
unsigned int rd, rs1, rs2;
|
||||||
|
unsigned_T divisor, dividend;
|
||||||
|
unsigned_T result;
|
||||||
|
|
||||||
|
rd = this->get_rd();
|
||||||
|
rs1 = this->get_rs1();
|
||||||
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
|
dividend = this->regs->getValue(rs1);
|
||||||
|
divisor = this->regs->getValue(rs2);
|
||||||
|
|
||||||
|
if (divisor == 0) {
|
||||||
|
result = dividend;
|
||||||
|
} else {
|
||||||
|
result = dividend % divisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->regs->setValue(rd, result);
|
||||||
|
|
||||||
|
this->logger->debug("{} ns. PC: 0x{:x}. M.REMU: x{:d} % x{:d} -> x{:d}({:d})",
|
||||||
|
sc_core::sc_time_stamp().value(),
|
||||||
|
this->regs->getPC(),
|
||||||
|
rs1, rs2, rd, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Exec_M_MULW() const {
|
||||||
|
unsigned int rd, rs1, rs2;
|
||||||
|
std::uint32_t multiplier, multiplicand;
|
||||||
|
std::int64_t result;
|
||||||
|
|
||||||
|
rd = this->get_rd();
|
||||||
|
rs1 = this->get_rs1();
|
||||||
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
|
multiplier = static_cast<std::uint32_t>(this->regs->getValue(rs1) & 0x00000000FFFFFFFF);
|
||||||
|
multiplicand = static_cast<std::uint32_t>(this->regs->getValue(rs2) & 0x00000000FFFFFFFF);
|
||||||
|
result = static_cast<std::int32_t>((multiplier * multiplicand) & 0x00000000FFFFFFFF);
|
||||||
|
|
||||||
|
this->regs->setValue(rd, result);
|
||||||
|
|
||||||
|
this->logger->debug("{} ns. PC: 0x{:x}. M.MULW: x{:d}({:d}) * x{:d}({:d}) -> x{:d}({:d})",
|
||||||
|
sc_core::sc_time_stamp().value(),
|
||||||
|
this->regs->getPC(),
|
||||||
|
rs1, rs2, multiplier, multiplicand, rd, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Exec_M_DIVW() const {
|
||||||
|
unsigned int rd, rs1, rs2;
|
||||||
|
std::int32_t divisor, dividend;
|
||||||
|
std::int64_t result;
|
||||||
|
|
||||||
|
rd = this->get_rd();
|
||||||
|
rs1 = this->get_rs1();
|
||||||
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
|
dividend = static_cast<std::int32_t>(this->regs->getValue(rs1) & 0x00000000FFFFFFFF);
|
||||||
|
divisor = static_cast<std::int32_t>(this->regs->getValue(rs2) & 0x00000000FFFFFFFF);
|
||||||
|
|
||||||
|
if (divisor == 0) {
|
||||||
|
result = -1;
|
||||||
|
} else if ((divisor == -1) && (dividend == static_cast<std::int32_t>(0x80000000))) {
|
||||||
|
result = 0x0000000080000000;
|
||||||
|
} else {
|
||||||
|
result = dividend / divisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->regs->setValue(rd, result);
|
||||||
|
|
||||||
|
this->logger->debug("{} ns. PC: 0x{:x}. M.DIVW: x{:d} / x{:d} -> x{:d}({:d})",
|
||||||
|
sc_core::sc_time_stamp().value(),
|
||||||
|
this->regs->getPC(),
|
||||||
|
rs1, rs2, rd, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Exec_M_DIVUW() const {
|
||||||
|
unsigned int rd, rs1, rs2;
|
||||||
|
std::uint32_t divisor, dividend;
|
||||||
|
std::int64_t result;
|
||||||
|
|
||||||
|
rd = this->get_rd();
|
||||||
|
rs1 = this->get_rs1();
|
||||||
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
|
dividend = static_cast<std::uint32_t>(this->regs->getValue(rs1) & 0x00000000FFFFFFFF);
|
||||||
|
divisor = static_cast<std::uint32_t>(this->regs->getValue(rs2) & 0x00000000FFFFFFFF);
|
||||||
|
|
||||||
|
if (divisor == 0) {
|
||||||
|
result = -1;
|
||||||
|
} else if ((divisor == 0xFFFFFFFF) && (dividend == static_cast<std::uint32_t>(0x80000000))) {
|
||||||
|
result = 0x0000000080000000;
|
||||||
|
} else {
|
||||||
|
result = static_cast<std::int32_t>(dividend / divisor);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->regs->setValue(rd, result);
|
||||||
|
|
||||||
|
this->logger->debug("{} ns. PC: 0x{:x}. M.DIVUW: x{:d} / x{:d} -> x{:d}({:d})",
|
||||||
|
sc_core::sc_time_stamp().value(),
|
||||||
|
this->regs->getPC(),
|
||||||
|
rs1, rs2, rd, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Exec_M_REMW() const {
|
||||||
|
unsigned int rd, rs1, rs2;
|
||||||
|
std::int32_t divisor, dividend;
|
||||||
|
signed_T result;
|
||||||
|
|
||||||
|
rd = this->get_rd();
|
||||||
|
rs1 = this->get_rs1();
|
||||||
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
|
dividend = static_cast<std::int32_t>(this->regs->getValue(rs1) & 0x00000000FFFFFFFF);
|
||||||
|
divisor = static_cast<std::int32_t>(this->regs->getValue(rs2) & 0x00000000FFFFFFFF);
|
||||||
|
|
||||||
|
if (divisor == 0) {
|
||||||
|
result = dividend;
|
||||||
|
} else if (divisor == -1) {
|
||||||
|
result = 0;
|
||||||
|
} else {
|
||||||
|
result = dividend % divisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->regs->setValue(rd, result);
|
||||||
|
|
||||||
|
this->logger->debug("{} ns. PC: 0x{:x}. M.REMW: x{:d} % x{:d} -> x{:d}({:d})",
|
||||||
|
sc_core::sc_time_stamp().value(),
|
||||||
|
this->regs->getPC(),
|
||||||
|
rs1, rs2, rd, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Exec_M_REMUW() const {
|
||||||
|
unsigned int rd, rs1, rs2;
|
||||||
|
std::uint32_t divisor, dividend;
|
||||||
|
std::int64_t result;
|
||||||
|
|
||||||
|
rd = this->get_rd();
|
||||||
|
rs1 = this->get_rs1();
|
||||||
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
|
dividend = static_cast<std::uint32_t>(this->regs->getValue(rs1) & 0x00000000FFFFFFFF);
|
||||||
|
divisor = static_cast<std::uint32_t>(this->regs->getValue(rs2) & 0x00000000FFFFFFFF);
|
||||||
|
|
||||||
|
if (divisor == 0) {
|
||||||
|
result = static_cast<std::int32_t>(dividend);
|
||||||
|
} else {
|
||||||
|
result = static_cast<std::int32_t>(dividend % divisor);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->regs->setValue(rd, result);
|
||||||
|
|
||||||
|
this->logger->debug("{} ns. PC: 0x{:x}. M.REMUW: x{:d}({:d}) % x{:d}({:d}) -> x{:d}({:d})",
|
||||||
|
sc_core::sc_time_stamp().value(),
|
||||||
|
this->regs->getPC(),
|
||||||
|
rs1, dividend, rs2, divisor, rd, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool process_instruction(Instruction &inst) {
|
||||||
|
this->setInstr(inst.getInstr());
|
||||||
|
|
||||||
|
switch (decode()) {
|
||||||
|
case OP_M_MUL:
|
||||||
|
Exec_M_MUL();
|
||||||
|
break;
|
||||||
|
case OP_M_MULH:
|
||||||
|
Exec_M_MULH();
|
||||||
|
break;
|
||||||
|
case OP_M_MULHSU:
|
||||||
|
Exec_M_MULHSU();
|
||||||
|
break;
|
||||||
|
case OP_M_MULHU:
|
||||||
|
Exec_M_MULHU();
|
||||||
|
break;
|
||||||
|
case OP_M_DIV:
|
||||||
|
Exec_M_DIV();
|
||||||
|
break;
|
||||||
|
case OP_M_DIVU:
|
||||||
|
Exec_M_DIVU();
|
||||||
|
break;
|
||||||
|
case OP_M_REM:
|
||||||
|
Exec_M_REM();
|
||||||
|
break;
|
||||||
|
case OP_M_REMU:
|
||||||
|
Exec_M_REMU();
|
||||||
|
break;
|
||||||
|
case OP_M_MULW:
|
||||||
|
Exec_M_MULW();
|
||||||
|
break;
|
||||||
|
case OP_M_DIVW:
|
||||||
|
Exec_M_DIVW();
|
||||||
|
break;
|
||||||
|
case OP_M_DIVUW:
|
||||||
|
Exec_M_DIVUW();
|
||||||
|
break;
|
||||||
|
case OP_M_REMW:
|
||||||
|
Exec_M_REMW();
|
||||||
|
break;
|
||||||
|
case OP_M_REMUW:
|
||||||
|
Exec_M_REMUW();
|
||||||
|
break;
|
||||||
|
[[unlikely]] default:
|
||||||
|
std::cout << "M instruction not implemented yet" << "\n";
|
||||||
|
inst.dump();
|
||||||
|
//NOP(inst);
|
||||||
|
sc_core::sc_stop();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -85,8 +457,8 @@ namespace riscv_tlm {
|
||||||
* @brief Access to opcode field
|
* @brief Access to opcode field
|
||||||
* @return return opcode field
|
* @return return opcode field
|
||||||
*/
|
*/
|
||||||
inline std::int32_t opcode() const override {
|
[[nodiscard]] inline unsigned_T opcode() const override {
|
||||||
return static_cast<std::int32_t>(m_instr.range(14, 12));
|
return static_cast<unsigned_T>(this->m_instr.range(14, 12));
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#define REGISTERS_H
|
#define REGISTERS_H
|
||||||
|
|
||||||
#define SC_INCLUDE_DYNAMIC_PROCESSES
|
#define SC_INCLUDE_DYNAMIC_PROCESSES
|
||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
@ -112,6 +113,7 @@ namespace riscv_tlm {
|
||||||
/**
|
/**
|
||||||
* @brief Register file implementation
|
* @brief Register file implementation
|
||||||
*/
|
*/
|
||||||
|
template<typename T>
|
||||||
class Registers {
|
class Registers {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -186,33 +188,56 @@ namespace riscv_tlm {
|
||||||
/**
|
/**
|
||||||
* Default constructor
|
* Default constructor
|
||||||
*/
|
*/
|
||||||
Registers();
|
Registers() {
|
||||||
|
perf = Performance::getInstance();
|
||||||
|
|
||||||
|
initCSR();
|
||||||
|
register_bank[sp] = Memory::SIZE - 4; // default stack at the end of the memory
|
||||||
|
register_PC = 0x80000000; // default _start address
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set value for a register
|
* Set value for a register
|
||||||
* @param reg_num register number
|
* @param reg_num register number
|
||||||
* @param value register value
|
* @param value register value
|
||||||
*/
|
*/
|
||||||
void setValue(int reg_num, std::int32_t value);
|
void setValue(unsigned int reg_num, T value) {
|
||||||
|
if ((reg_num != 0) && (reg_num < 32)) {
|
||||||
|
register_bank[reg_num] = value;
|
||||||
|
perf->registerWrite();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns register value
|
* Returns register value
|
||||||
* @param reg_num register number
|
* @param reg_num register number
|
||||||
* @return register value
|
* @return register value
|
||||||
*/
|
*/
|
||||||
std::uint32_t getValue(int reg_num) const;
|
T getValue(unsigned int reg_num) const {
|
||||||
|
if (reg_num < 32) {
|
||||||
|
perf->registerRead();
|
||||||
|
return register_bank[reg_num];
|
||||||
|
} else {
|
||||||
|
/* Extend sign for any possible T type */
|
||||||
|
return static_cast<T>(std::numeric_limits<T>::max());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns PC value
|
* Returns PC value
|
||||||
* @return PC value
|
* @return PC value
|
||||||
*/
|
*/
|
||||||
std::uint32_t getPC() const;
|
T getPC() const {
|
||||||
|
return register_PC;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets arbitraty value to PC
|
* Sets arbitraty value to PC
|
||||||
* @param new_pc new address to PC
|
* @param new_pc new address to PC
|
||||||
*/
|
*/
|
||||||
void setPC(std::uint32_t new_pc);
|
void setPC(T new_pc) {
|
||||||
|
register_PC = new_pc;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Increments PC couunter to next address
|
* Increments PC couunter to next address
|
||||||
|
@ -230,40 +255,88 @@ namespace riscv_tlm {
|
||||||
* @param csr CSR number to access
|
* @param csr CSR number to access
|
||||||
* @return CSR value
|
* @return CSR value
|
||||||
*/
|
*/
|
||||||
std::uint32_t getCSR(int csr);
|
T getCSR(int csr) {
|
||||||
|
T ret_value;
|
||||||
|
|
||||||
|
switch (csr) {
|
||||||
|
case CSR_CYCLE:
|
||||||
|
case CSR_MCYCLE:
|
||||||
|
ret_value = static_cast<std::uint64_t>(sc_core::sc_time(
|
||||||
|
sc_core::sc_time_stamp()
|
||||||
|
- sc_core::sc_time(sc_core::SC_ZERO_TIME)).to_double())
|
||||||
|
& 0x00000000FFFFFFFF;
|
||||||
|
break;
|
||||||
|
case CSR_CYCLEH:
|
||||||
|
case CSR_MCYCLEH:
|
||||||
|
ret_value = static_cast<std::uint32_t>((std::uint64_t) (sc_core::sc_time(
|
||||||
|
sc_core::sc_time_stamp()
|
||||||
|
- sc_core::sc_time(sc_core::SC_ZERO_TIME)).to_double())
|
||||||
|
>> 32 & 0x00000000FFFFFFFF);
|
||||||
|
break;
|
||||||
|
case CSR_TIME:
|
||||||
|
ret_value = static_cast<std::uint64_t>(sc_core::sc_time(
|
||||||
|
sc_core::sc_time_stamp()
|
||||||
|
- sc_core::sc_time(sc_core::SC_ZERO_TIME)).to_double())
|
||||||
|
& 0x00000000FFFFFFFF;
|
||||||
|
break;
|
||||||
|
case CSR_TIMEH:
|
||||||
|
ret_value = static_cast<std::uint32_t>((std::uint64_t) (sc_core::sc_time(
|
||||||
|
sc_core::sc_time_stamp()
|
||||||
|
- sc_core::sc_time(sc_core::SC_ZERO_TIME)).to_double())
|
||||||
|
>> 32 & 0x00000000FFFFFFFF);
|
||||||
|
break;
|
||||||
|
[[likely]] default:
|
||||||
|
ret_value = CSR[csr];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret_value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set CSR value
|
* @brief Set CSR value
|
||||||
* @param csr CSR number to access
|
* @param csr CSR number to access
|
||||||
* @param value new value to register
|
* @param value new value to register
|
||||||
*/
|
*/
|
||||||
void setCSR(int csr, std::uint32_t value);
|
void setCSR(int csr, T value) {
|
||||||
|
/* @FIXME: rv32mi-p-ma_fetch tests doesn't allow MISA to be writable,
|
||||||
|
* but Volume II: Privileged Architecture v1.10 says MISA is writable (?)
|
||||||
|
*/
|
||||||
|
if (csr != CSR_MISA) {
|
||||||
|
CSR[csr] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dump register data to console
|
* Dump register data to console
|
||||||
*/
|
*/
|
||||||
void dump();
|
void dump() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* bank of registers (32 regs of 32bits each)
|
* bank of registers (32 regs of 32bits each)
|
||||||
*/
|
*/
|
||||||
std::array<std::uint32_t, 32> register_bank = {{0}};
|
std::array<T, 32> register_bank = {{0}};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Program counter (32 bits width)
|
* Program counter (32 bits width)
|
||||||
*/
|
*/
|
||||||
std::uint32_t register_PC;
|
T register_PC;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSR registers (4096 maximum)
|
* CSR registers (4096 maximum)
|
||||||
*/
|
*/
|
||||||
std::unordered_map<std::uint32_t, unsigned int> CSR;
|
std::unordered_map<T, unsigned int> CSR;
|
||||||
|
|
||||||
|
|
||||||
Performance *perf;
|
Performance *perf;
|
||||||
|
|
||||||
void initCSR();
|
void initCSR();
|
||||||
|
/*
|
||||||
|
void initCSR() {
|
||||||
|
CSR[CSR_MISA] = MISA_MXL | MISA_M_EXTENSION | MISA_C_EXTENSION
|
||||||
|
| MISA_A_EXTENSION | MISA_I_BASE;
|
||||||
|
CSR[CSR_MSTATUS] = MISA_MXL;
|
||||||
|
}
|
||||||
|
*/
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -28,60 +28,107 @@
|
||||||
|
|
||||||
namespace riscv_tlm {
|
namespace riscv_tlm {
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
class extension_base {
|
class extension_base {
|
||||||
|
|
||||||
|
using signed_T = typename std::make_signed<T>::type;
|
||||||
|
using unsigned_T = typename std::make_unsigned<T>::type;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
extension_base(const sc_dt::sc_uint<32> &instr, Registers *register_bank,
|
extension_base(const T &instr, Registers<T> *register_bank,
|
||||||
MemoryInterface *mem_interface);
|
MemoryInterface *mem_interface) :
|
||||||
|
m_instr(instr), regs(register_bank), mem_intf(mem_interface) {
|
||||||
|
|
||||||
virtual ~extension_base() = 0;
|
perf = Performance::getInstance();
|
||||||
|
logger = spdlog::get("my_logger");
|
||||||
|
}
|
||||||
|
|
||||||
void setInstr(std::uint32_t p_instr);
|
virtual ~extension_base() = default;
|
||||||
|
|
||||||
void RaiseException(std::uint32_t cause, std::uint32_t inst);
|
void setInstr(std::uint32_t p_instr) {
|
||||||
|
m_instr = sc_dt::sc_uint<32>(p_instr);
|
||||||
|
}
|
||||||
|
|
||||||
bool NOP();
|
void RaiseException(std::uint32_t cause, std::uint32_t inst) {
|
||||||
|
std::uint32_t new_pc, current_pc, m_cause;
|
||||||
|
|
||||||
|
current_pc = regs->getPC();
|
||||||
|
m_cause = regs->getCSR(CSR_MSTATUS);
|
||||||
|
m_cause |= cause;
|
||||||
|
|
||||||
|
new_pc = regs->getCSR(CSR_MTVEC);
|
||||||
|
|
||||||
|
regs->setCSR(CSR_MEPC, current_pc);
|
||||||
|
|
||||||
|
if (cause == EXCEPTION_CAUSE_ILLEGAL_INSTRUCTION) {
|
||||||
|
regs->setCSR(CSR_MTVAL, inst);
|
||||||
|
} else {
|
||||||
|
regs->setCSR(CSR_MTVAL, current_pc);
|
||||||
|
}
|
||||||
|
|
||||||
|
regs->setCSR(CSR_MCAUSE, cause);
|
||||||
|
regs->setCSR(CSR_MSTATUS, m_cause);
|
||||||
|
|
||||||
|
regs->setPC(new_pc);
|
||||||
|
|
||||||
|
logger->debug("{} ns. PC: 0x{:x}. Exception! new PC 0x{:x} ", sc_core::sc_time_stamp().value(),
|
||||||
|
regs->getPC(),
|
||||||
|
new_pc);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NOP() {
|
||||||
|
logger->debug("{} ns. PC: 0x{:x}. NOP! new PC 0x{:x} ", sc_core::sc_time_stamp().value(), regs->getPC());
|
||||||
|
logger->flush();
|
||||||
|
sc_core::sc_stop();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* pure virtual functions */
|
/* pure virtual functions */
|
||||||
virtual std::int32_t opcode() const = 0;
|
virtual unsigned_T opcode() const = 0;
|
||||||
|
|
||||||
virtual std::int32_t get_rd() const {
|
virtual unsigned int get_rd() const {
|
||||||
return m_instr.range(11, 7);
|
return m_instr.range(11, 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void set_rd(std::int32_t value) {
|
virtual void set_rd(unsigned int value) {
|
||||||
m_instr.range(11, 7) = value;
|
m_instr.range(11, 7) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual std::int32_t get_rs1() const {
|
virtual unsigned int get_rs1() const {
|
||||||
return m_instr.range(19, 15);
|
return m_instr.range(19, 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void set_rs1(std::int32_t value) {
|
virtual void set_rs1(unsigned int value) {
|
||||||
m_instr.range(19, 15) = value;
|
m_instr.range(19, 15) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual std::int32_t get_rs2() const {
|
virtual unsigned int get_rs2() const {
|
||||||
return m_instr.range(24, 20);
|
return m_instr.range(24, 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void set_rs2(std::int32_t value) {
|
virtual void set_rs2(unsigned int value) {
|
||||||
m_instr.range(24, 20) = value;
|
m_instr.range(24, 20) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual std::int32_t get_funct3() const {
|
virtual unsigned int get_funct3() const {
|
||||||
return m_instr.range(14, 12);
|
return m_instr.range(14, 12);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void set_funct3(std::int32_t value) {
|
virtual void set_funct3(unsigned int value) {
|
||||||
m_instr.range(14, 12) = value;
|
m_instr.range(14, 12) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void dump() const;
|
// For RV64 SLLI, SRLI & SRAI
|
||||||
|
virtual unsigned int get_shamt_slli() const {
|
||||||
|
return m_instr.range(25, 20);
|
||||||
|
}
|
||||||
|
virtual void dump() const {
|
||||||
|
std::cout << std::hex << "0x" << m_instr << std::dec << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
sc_dt::sc_uint<32> m_instr;
|
sc_dt::sc_uint<32> m_instr;
|
||||||
Registers *regs;
|
Registers<T> *regs;
|
||||||
Performance *perf;
|
Performance *perf;
|
||||||
MemoryInterface *mem_intf;
|
MemoryInterface *mem_intf;
|
||||||
std::shared_ptr<spdlog::logger> logger;
|
std::shared_ptr<spdlog::logger> logger;
|
||||||
|
|
|
@ -7,439 +7,3 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#include "A_extension.h"
|
#include "A_extension.h"
|
||||||
|
|
||||||
namespace riscv_tlm {
|
|
||||||
|
|
||||||
op_A_Codes A_extension::decode() const {
|
|
||||||
|
|
||||||
switch (opcode()) {
|
|
||||||
case A_LR:
|
|
||||||
return OP_A_LR;
|
|
||||||
break;
|
|
||||||
case A_SC:
|
|
||||||
return OP_A_SC;
|
|
||||||
break;
|
|
||||||
case A_AMOSWAP:
|
|
||||||
return OP_A_AMOSWAP;
|
|
||||||
break;
|
|
||||||
case A_AMOADD:
|
|
||||||
return OP_A_AMOADD;
|
|
||||||
break;
|
|
||||||
case A_AMOXOR:
|
|
||||||
return OP_A_AMOXOR;
|
|
||||||
break;
|
|
||||||
case A_AMOAND:
|
|
||||||
return OP_A_AMOAND;
|
|
||||||
break;
|
|
||||||
case A_AMOOR:
|
|
||||||
return OP_A_AMOOR;
|
|
||||||
break;
|
|
||||||
case A_AMOMIN:
|
|
||||||
return OP_A_AMOMIN;
|
|
||||||
break;
|
|
||||||
case A_AMOMAX:
|
|
||||||
return OP_A_AMOMAX;
|
|
||||||
break;
|
|
||||||
case A_AMOMINU:
|
|
||||||
return OP_A_AMOMINU;
|
|
||||||
break;
|
|
||||||
case A_AMOMAXU:
|
|
||||||
return OP_A_AMOMAXU;
|
|
||||||
break;
|
|
||||||
[[unlikely]] default:
|
|
||||||
return OP_A_ERROR;
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return OP_A_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool A_extension::Exec_A_LR() {
|
|
||||||
std::uint32_t mem_addr = 0;
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::uint32_t data;
|
|
||||||
|
|
||||||
rd = get_rd();
|
|
||||||
rs1 = get_rs1();
|
|
||||||
rs2 = get_rs2();
|
|
||||||
|
|
||||||
if (rs2 != 0) {
|
|
||||||
std::cout << "ILEGAL INSTRUCTION, LR.W: rs2 != 0" << std::endl;
|
|
||||||
RaiseException(EXCEPTION_CAUSE_ILLEGAL_INSTRUCTION, m_instr);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mem_addr = regs->getValue(rs1);
|
|
||||||
data = mem_intf->readDataMem(mem_addr, 4);
|
|
||||||
perf->dataMemoryRead();
|
|
||||||
regs->setValue(rd, static_cast<int32_t>(data));
|
|
||||||
|
|
||||||
TLB_reserve(mem_addr);
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. A.LR.W: x{:d}(0x{:x}) -> x{:d}(0x{:x}) ", sc_core::sc_time_stamp().value(),
|
|
||||||
regs->getPC(),
|
|
||||||
rs1, mem_addr, rd, data);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool A_extension::Exec_A_SC() {
|
|
||||||
std::uint32_t mem_addr;
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::uint32_t data;
|
|
||||||
|
|
||||||
rd = get_rd();
|
|
||||||
rs1 = get_rs1();
|
|
||||||
rs2 = get_rs2();
|
|
||||||
|
|
||||||
mem_addr = regs->getValue(rs1);
|
|
||||||
data = regs->getValue(rs2);
|
|
||||||
|
|
||||||
if (TLB_reserved(mem_addr)) {
|
|
||||||
mem_intf->writeDataMem(mem_addr, data, 4);
|
|
||||||
perf->dataMemoryWrite();
|
|
||||||
regs->setValue(rd, 0); // SC writes 0 to rd on success
|
|
||||||
} else {
|
|
||||||
regs->setValue(rd, 1); // SC writes nonzero on failure
|
|
||||||
}
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. A.SC.W: (0x{:x}) <- x{:d}(0x{:x}) ", sc_core::sc_time_stamp().value(),
|
|
||||||
regs->getPC(),
|
|
||||||
mem_addr, rs2, data);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool A_extension::Exec_A_AMOSWAP() const {
|
|
||||||
std::uint32_t mem_addr;
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::uint32_t data;
|
|
||||||
std::uint32_t aux;
|
|
||||||
|
|
||||||
/* These instructions must be atomic */
|
|
||||||
|
|
||||||
rd = get_rd();
|
|
||||||
rs1 = get_rs1();
|
|
||||||
rs2 = get_rs2();
|
|
||||||
|
|
||||||
mem_addr = regs->getValue(rs1);
|
|
||||||
data = mem_intf->readDataMem(mem_addr, 4);
|
|
||||||
perf->dataMemoryRead();
|
|
||||||
regs->setValue(rd, static_cast<int32_t>(data));
|
|
||||||
|
|
||||||
// swap
|
|
||||||
aux = regs->getValue(rs2);
|
|
||||||
regs->setValue(rs2, static_cast<int32_t>(data));
|
|
||||||
|
|
||||||
mem_intf->writeDataMem(mem_addr, aux, 4);
|
|
||||||
perf->dataMemoryWrite();
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. A.AMOSWAP");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool A_extension::Exec_A_AMOADD() const {
|
|
||||||
std::uint32_t mem_addr;
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::uint32_t data;
|
|
||||||
|
|
||||||
/* These instructions must be atomic */
|
|
||||||
|
|
||||||
rd = get_rd();
|
|
||||||
rs1 = get_rs1();
|
|
||||||
rs2 = get_rs2();
|
|
||||||
|
|
||||||
mem_addr = regs->getValue(rs1);
|
|
||||||
data = mem_intf->readDataMem(mem_addr, 4);
|
|
||||||
perf->dataMemoryRead();
|
|
||||||
|
|
||||||
regs->setValue(rd, static_cast<int32_t>(data));
|
|
||||||
|
|
||||||
// add
|
|
||||||
data = data + regs->getValue(rs2);
|
|
||||||
|
|
||||||
mem_intf->writeDataMem(mem_addr, data, 4);
|
|
||||||
perf->dataMemoryWrite();
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. A.AMOADD");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool A_extension::Exec_A_AMOXOR() const {
|
|
||||||
std::uint32_t mem_addr;
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::uint32_t data;
|
|
||||||
|
|
||||||
/* These instructions must be atomic */
|
|
||||||
|
|
||||||
rd = get_rd();
|
|
||||||
rs1 = get_rs1();
|
|
||||||
rs2 = get_rs2();
|
|
||||||
|
|
||||||
mem_addr = regs->getValue(rs1);
|
|
||||||
data = mem_intf->readDataMem(mem_addr, 4);
|
|
||||||
perf->dataMemoryRead();
|
|
||||||
|
|
||||||
regs->setValue(rd, static_cast<int32_t>(data));
|
|
||||||
|
|
||||||
// add
|
|
||||||
data = data ^ regs->getValue(rs2);
|
|
||||||
|
|
||||||
mem_intf->writeDataMem(mem_addr, data, 4);
|
|
||||||
perf->dataMemoryWrite();
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. A.AMOXOR");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool A_extension::Exec_A_AMOAND() const {
|
|
||||||
std::uint32_t mem_addr;
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::uint32_t data;
|
|
||||||
|
|
||||||
/* These instructions must be atomic */
|
|
||||||
|
|
||||||
rd = get_rd();
|
|
||||||
rs1 = get_rs1();
|
|
||||||
rs2 = get_rs2();
|
|
||||||
|
|
||||||
mem_addr = regs->getValue(rs1);
|
|
||||||
data = mem_intf->readDataMem(mem_addr, 4);
|
|
||||||
perf->dataMemoryRead();
|
|
||||||
|
|
||||||
regs->setValue(rd, static_cast<int32_t>(data));
|
|
||||||
|
|
||||||
// add
|
|
||||||
data = data & regs->getValue(rs2);
|
|
||||||
|
|
||||||
mem_intf->writeDataMem(mem_addr, data, 4);
|
|
||||||
perf->dataMemoryWrite();
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. A.AMOAND");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool A_extension::Exec_A_AMOOR() const {
|
|
||||||
std::uint32_t mem_addr;
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::uint32_t data;
|
|
||||||
|
|
||||||
/* These instructions must be atomic */
|
|
||||||
|
|
||||||
rd = get_rd();
|
|
||||||
rs1 = get_rs1();
|
|
||||||
rs2 = get_rs2();
|
|
||||||
|
|
||||||
mem_addr = regs->getValue(rs1);
|
|
||||||
data = mem_intf->readDataMem(mem_addr, 4);
|
|
||||||
perf->dataMemoryRead();
|
|
||||||
|
|
||||||
regs->setValue(rd, static_cast<int32_t>(data));
|
|
||||||
|
|
||||||
// add
|
|
||||||
data = data | regs->getValue(rs2);
|
|
||||||
|
|
||||||
mem_intf->writeDataMem(mem_addr, data, 4);
|
|
||||||
perf->dataMemoryWrite();
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. A.AMOOR");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool A_extension::Exec_A_AMOMIN() const {
|
|
||||||
std::uint32_t mem_addr;
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::uint32_t data;
|
|
||||||
std::uint32_t aux;
|
|
||||||
|
|
||||||
/* These instructions must be atomic */
|
|
||||||
|
|
||||||
rd = get_rd();
|
|
||||||
rs1 = get_rs1();
|
|
||||||
rs2 = get_rs2();
|
|
||||||
|
|
||||||
mem_addr = regs->getValue(rs1);
|
|
||||||
data = mem_intf->readDataMem(mem_addr, 4);
|
|
||||||
perf->dataMemoryRead();
|
|
||||||
|
|
||||||
regs->setValue(rd, static_cast<int32_t>(data));
|
|
||||||
|
|
||||||
// min
|
|
||||||
aux = regs->getValue(rs2);
|
|
||||||
if ((int32_t) data < (int32_t) aux) {
|
|
||||||
aux = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
mem_intf->writeDataMem(mem_addr, aux, 4);
|
|
||||||
perf->dataMemoryWrite();
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. A.AMOMIN");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool A_extension::Exec_A_AMOMAX() const {
|
|
||||||
std::uint32_t mem_addr;
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::uint32_t data;
|
|
||||||
std::uint32_t aux;
|
|
||||||
|
|
||||||
/* These instructions must be atomic */
|
|
||||||
|
|
||||||
rd = get_rd();
|
|
||||||
rs1 = get_rs1();
|
|
||||||
rs2 = get_rs2();
|
|
||||||
|
|
||||||
mem_addr = regs->getValue(rs1);
|
|
||||||
data = mem_intf->readDataMem(mem_addr, 4);
|
|
||||||
perf->dataMemoryRead();
|
|
||||||
|
|
||||||
regs->setValue(rd, static_cast<int32_t>(data));
|
|
||||||
|
|
||||||
// >
|
|
||||||
aux = regs->getValue(rs2);
|
|
||||||
if ((int32_t) data > (int32_t) aux) {
|
|
||||||
aux = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
mem_intf->writeDataMem(mem_addr, aux, 4);
|
|
||||||
perf->dataMemoryWrite();
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. A.AMOMAX");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool A_extension::Exec_A_AMOMINU() const {
|
|
||||||
std::uint32_t mem_addr;
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::uint32_t data;
|
|
||||||
std::uint32_t aux;
|
|
||||||
|
|
||||||
/* These instructions must be atomic */
|
|
||||||
|
|
||||||
rd = get_rd();
|
|
||||||
rs1 = get_rs1();
|
|
||||||
rs2 = get_rs2();
|
|
||||||
|
|
||||||
mem_addr = regs->getValue(rs1);
|
|
||||||
data = mem_intf->readDataMem(mem_addr, 4);
|
|
||||||
perf->dataMemoryRead();
|
|
||||||
|
|
||||||
regs->setValue(rd, static_cast<int32_t>(data));
|
|
||||||
|
|
||||||
// min
|
|
||||||
aux = regs->getValue(rs2);
|
|
||||||
if (data < aux) {
|
|
||||||
aux = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
mem_intf->writeDataMem(mem_addr, aux, 4);
|
|
||||||
perf->dataMemoryWrite();
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. A.AMOMINU");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool A_extension::Exec_A_AMOMAXU() const {
|
|
||||||
std::uint32_t mem_addr;
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::uint32_t data;
|
|
||||||
std::uint32_t aux;
|
|
||||||
|
|
||||||
/* These instructions must be atomic */
|
|
||||||
|
|
||||||
rd = get_rd();
|
|
||||||
rs1 = get_rs1();
|
|
||||||
rs2 = get_rs2();
|
|
||||||
|
|
||||||
mem_addr = regs->getValue(rs1);
|
|
||||||
data = mem_intf->readDataMem(mem_addr, 4);
|
|
||||||
perf->dataMemoryRead();
|
|
||||||
|
|
||||||
regs->setValue(rd, static_cast<int32_t>(data));
|
|
||||||
|
|
||||||
// max
|
|
||||||
aux = regs->getValue(rs2);
|
|
||||||
if (data > aux) {
|
|
||||||
aux = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
mem_intf->writeDataMem(mem_addr, aux, 4);
|
|
||||||
perf->dataMemoryWrite();
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. A.AMOMAXU");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void A_extension::TLB_reserve(std::uint32_t address) {
|
|
||||||
TLB_A_Entries.insert(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool A_extension::TLB_reserved(std::uint32_t address) {
|
|
||||||
if (TLB_A_Entries.count(address) == 1) {
|
|
||||||
TLB_A_Entries.erase(address);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool A_extension::process_instruction(Instruction &inst) {
|
|
||||||
bool PC_not_affected = true;
|
|
||||||
|
|
||||||
setInstr(inst.getInstr());
|
|
||||||
|
|
||||||
switch (decode()) {
|
|
||||||
case OP_A_LR:
|
|
||||||
Exec_A_LR();
|
|
||||||
break;
|
|
||||||
case OP_A_SC:
|
|
||||||
Exec_A_SC();
|
|
||||||
break;
|
|
||||||
case OP_A_AMOSWAP:
|
|
||||||
Exec_A_AMOSWAP();
|
|
||||||
break;
|
|
||||||
case OP_A_AMOADD:
|
|
||||||
Exec_A_AMOADD();
|
|
||||||
break;
|
|
||||||
case OP_A_AMOXOR:
|
|
||||||
Exec_A_AMOXOR();
|
|
||||||
break;
|
|
||||||
case OP_A_AMOAND:
|
|
||||||
Exec_A_AMOAND();
|
|
||||||
break;
|
|
||||||
case OP_A_AMOOR:
|
|
||||||
Exec_A_AMOOR();
|
|
||||||
break;
|
|
||||||
case OP_A_AMOMIN:
|
|
||||||
Exec_A_AMOMIN();
|
|
||||||
break;
|
|
||||||
case OP_A_AMOMAX:
|
|
||||||
Exec_A_AMOMAX();
|
|
||||||
break;
|
|
||||||
case OP_A_AMOMINU:
|
|
||||||
Exec_A_AMOMINU();
|
|
||||||
break;
|
|
||||||
case OP_A_AMOMAXU:
|
|
||||||
Exec_A_AMOMAXU();
|
|
||||||
break;
|
|
||||||
[[unlikely]] default:
|
|
||||||
std::cout << "A instruction not implemented yet" << std::endl;
|
|
||||||
inst.dump();
|
|
||||||
NOP();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return PC_not_affected;
|
|
||||||
}
|
|
||||||
}
|
|
1731
src/BASE_ISA.cpp
1731
src/BASE_ISA.cpp
File diff suppressed because it is too large
Load Diff
194
src/CPU.cpp
194
src/CPU.cpp
|
@ -11,37 +11,22 @@ namespace riscv_tlm {
|
||||||
|
|
||||||
SC_HAS_PROCESS(CPU);
|
SC_HAS_PROCESS(CPU);
|
||||||
|
|
||||||
CPU::CPU(sc_core::sc_module_name const &name, std::uint32_t PC, bool debug) :
|
CPU::CPU(sc_core::sc_module_name const &name, bool debug) : sc_module(name), instr_bus("instr_bus"), inst(0), default_time(10, sc_core::SC_NS) {
|
||||||
sc_module(name), instr_bus("instr_bus"), inst(0), default_time(10,
|
|
||||||
sc_core::SC_NS), INSTR(0) {
|
|
||||||
register_bank = new Registers();
|
|
||||||
mem_intf = new MemoryInterface();
|
|
||||||
|
|
||||||
perf = Performance::getInstance();
|
perf = Performance::getInstance();
|
||||||
|
logger = spdlog::get("my_logger");
|
||||||
register_bank->setPC(PC);
|
|
||||||
register_bank->setValue(Registers::sp, (Memory::SIZE / 4) - 1);
|
|
||||||
|
|
||||||
irq_line_socket.register_b_transport(this, &CPU::call_interrupt);
|
|
||||||
interrupt = false;
|
|
||||||
|
|
||||||
int_cause = 0;
|
|
||||||
irq_already_down = false;
|
|
||||||
|
|
||||||
dmi_ptr_valid = false;
|
|
||||||
instr_bus.register_invalidate_direct_mem_ptr(this,
|
|
||||||
&CPU::invalidate_direct_mem_ptr);
|
|
||||||
|
|
||||||
exec = new BASE_ISA(0, register_bank, mem_intf);
|
|
||||||
c_inst = new C_extension(0, register_bank, mem_intf);
|
|
||||||
m_inst = new M_extension(0, register_bank, mem_intf);
|
|
||||||
a_inst = new A_extension(0, register_bank, mem_intf);
|
|
||||||
|
|
||||||
m_qk = new tlm_utils::tlm_quantumkeeper();
|
m_qk = new tlm_utils::tlm_quantumkeeper();
|
||||||
m_qk->reset();
|
m_qk->reset();
|
||||||
|
mem_intf = nullptr;
|
||||||
|
dmi_ptr_valid = false;
|
||||||
|
|
||||||
|
irq_already_down = false;
|
||||||
|
interrupt = false;
|
||||||
|
|
||||||
|
irq_line_socket.register_b_transport(this, &CPU::call_interrupt);
|
||||||
|
|
||||||
trans.set_command(tlm::TLM_READ_COMMAND);
|
trans.set_command(tlm::TLM_READ_COMMAND);
|
||||||
trans.set_data_ptr(reinterpret_cast<unsigned char *>(&INSTR));
|
|
||||||
trans.set_data_length(4);
|
trans.set_data_length(4);
|
||||||
trans.set_streaming_width(4); // = data_length to indicate no streaming
|
trans.set_streaming_width(4); // = data_length to indicate no streaming
|
||||||
trans.set_byte_enable_ptr(nullptr); // 0 indicates unused
|
trans.set_byte_enable_ptr(nullptr); // 0 indicates unused
|
||||||
|
@ -51,147 +36,12 @@ namespace riscv_tlm {
|
||||||
if (!debug) {
|
if (!debug) {
|
||||||
SC_THREAD(CPU_thread);
|
SC_THREAD(CPU_thread);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
logger = spdlog::get("my_logger");
|
void CPU::invalidate_direct_mem_ptr(sc_dt::uint64 start, sc_dt::uint64 end) {
|
||||||
}
|
(void) start;
|
||||||
|
(void) end;
|
||||||
CPU::~CPU() {
|
dmi_ptr_valid = false;
|
||||||
delete register_bank;
|
|
||||||
delete mem_intf;
|
|
||||||
delete exec;
|
|
||||||
delete c_inst;
|
|
||||||
delete m_inst;
|
|
||||||
delete a_inst;
|
|
||||||
delete m_qk;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CPU::cpu_process_IRQ() {
|
|
||||||
std::uint32_t csr_temp;
|
|
||||||
bool ret_value = false;
|
|
||||||
|
|
||||||
if (interrupt) {
|
|
||||||
csr_temp = register_bank->getCSR(CSR_MSTATUS);
|
|
||||||
if ((csr_temp & MSTATUS_MIE) == 0) {
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. Interrupt delayed", sc_core::sc_time_stamp().value(),
|
|
||||||
register_bank->getPC());
|
|
||||||
|
|
||||||
return ret_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
csr_temp = register_bank->getCSR(CSR_MIP);
|
|
||||||
|
|
||||||
if ((csr_temp & MIP_MEIP) == 0) {
|
|
||||||
csr_temp |= MIP_MEIP; // MEIP bit in MIP register (11th bit)
|
|
||||||
register_bank->setCSR(CSR_MIP, csr_temp);
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. Interrupt!", sc_core::sc_time_stamp().value(),
|
|
||||||
register_bank->getPC());
|
|
||||||
|
|
||||||
|
|
||||||
/* updated MEPC register */
|
|
||||||
std::uint32_t old_pc = register_bank->getPC();
|
|
||||||
register_bank->setCSR(CSR_MEPC, old_pc);
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. Old PC Value 0x{:x}", sc_core::sc_time_stamp().value(),
|
|
||||||
register_bank->getPC(),
|
|
||||||
old_pc);
|
|
||||||
|
|
||||||
/* update MCAUSE register */
|
|
||||||
register_bank->setCSR(CSR_MCAUSE, 0x80000000);
|
|
||||||
|
|
||||||
/* set new PC address */
|
|
||||||
std::uint32_t new_pc = register_bank->getCSR(CSR_MTVEC);
|
|
||||||
//new_pc = new_pc & 0xFFFFFFFC; // last two bits always to 0
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. NEW PC Value 0x{:x}", sc_core::sc_time_stamp().value(),
|
|
||||||
register_bank->getPC(),
|
|
||||||
new_pc);
|
|
||||||
register_bank->setPC(new_pc);
|
|
||||||
|
|
||||||
ret_value = true;
|
|
||||||
interrupt = false;
|
|
||||||
irq_already_down = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!irq_already_down) {
|
|
||||||
csr_temp = register_bank->getCSR(CSR_MIP);
|
|
||||||
csr_temp &= ~MIP_MEIP;
|
|
||||||
register_bank->setCSR(CSR_MIP, csr_temp);
|
|
||||||
irq_already_down = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CPU::CPU_step() {
|
|
||||||
bool PC_not_affected = false;
|
|
||||||
|
|
||||||
/* Get new PC value */
|
|
||||||
if (dmi_ptr_valid) {
|
|
||||||
/* if memory_offset at Memory module is set, this won't work */
|
|
||||||
std::memcpy(&INSTR, dmi_ptr + register_bank->getPC(), 4);
|
|
||||||
} else {
|
|
||||||
sc_core::sc_time delay = sc_core::SC_ZERO_TIME;
|
|
||||||
tlm::tlm_dmi dmi_data;
|
|
||||||
trans.set_address(register_bank->getPC());
|
|
||||||
instr_bus->b_transport(trans, delay);
|
|
||||||
|
|
||||||
if (trans.is_response_error()) {
|
|
||||||
SC_REPORT_ERROR("CPU base", "Read memory");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trans.is_dmi_allowed()) {
|
|
||||||
dmi_ptr_valid = instr_bus->get_direct_mem_ptr(trans, dmi_data);
|
|
||||||
if (dmi_ptr_valid) {
|
|
||||||
std::cout << "Get DMI_PTR " << std::endl;
|
|
||||||
dmi_ptr = dmi_data.get_dmi_ptr();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
perf->codeMemoryRead();
|
|
||||||
inst.setInstr(INSTR);
|
|
||||||
bool breakpoint = false;
|
|
||||||
|
|
||||||
/* check what type of instruction is and execute it */
|
|
||||||
switch (inst.check_extension()) {
|
|
||||||
[[likely]] case BASE_EXTENSION:
|
|
||||||
PC_not_affected = exec->process_instruction(inst, &breakpoint);
|
|
||||||
if (PC_not_affected) {
|
|
||||||
register_bank->incPC();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case C_EXTENSION:
|
|
||||||
PC_not_affected = c_inst->process_instruction(inst, &breakpoint);
|
|
||||||
if (PC_not_affected) {
|
|
||||||
register_bank->incPCby2();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case M_EXTENSION:
|
|
||||||
PC_not_affected = m_inst->process_instruction(inst);
|
|
||||||
if (PC_not_affected) {
|
|
||||||
register_bank->incPC();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case A_EXTENSION:
|
|
||||||
PC_not_affected = a_inst->process_instruction(inst);
|
|
||||||
if (PC_not_affected) {
|
|
||||||
register_bank->incPC();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
[[unlikely]] default:
|
|
||||||
std::cout << "Extension not implemented yet" << std::endl;
|
|
||||||
inst.dump();
|
|
||||||
exec->NOP();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (breakpoint) {
|
|
||||||
std::cout << "Breakpoint set to true\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
perf->instructionsInc();
|
|
||||||
|
|
||||||
return breakpoint;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]] void CPU::CPU_thread() {
|
[[noreturn]] void CPU::CPU_thread() {
|
||||||
|
@ -219,18 +69,4 @@ namespace riscv_tlm {
|
||||||
#endif
|
#endif
|
||||||
} // while(1)
|
} // while(1)
|
||||||
} // CPU_thread
|
} // CPU_thread
|
||||||
|
|
||||||
void CPU::call_interrupt(tlm::tlm_generic_payload &m_trans,
|
|
||||||
sc_core::sc_time &delay) {
|
|
||||||
interrupt = true;
|
|
||||||
/* Socket caller send a cause (its id) */
|
|
||||||
memcpy(&int_cause, m_trans.get_data_ptr(), sizeof(std::uint32_t));
|
|
||||||
delay = sc_core::SC_ZERO_TIME;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CPU::invalidate_direct_mem_ptr(sc_dt::uint64 start, sc_dt::uint64 end) {
|
|
||||||
(void) start;
|
|
||||||
(void) end;
|
|
||||||
dmi_ptr_valid = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -5,749 +5,3 @@
|
||||||
\date August 2018
|
\date August 2018
|
||||||
*/
|
*/
|
||||||
#include "C_extension.h"
|
#include "C_extension.h"
|
||||||
|
|
||||||
namespace riscv_tlm {
|
|
||||||
|
|
||||||
op_C_Codes C_extension::decode() const {
|
|
||||||
|
|
||||||
switch (opcode()) {
|
|
||||||
|
|
||||||
case 0b00:
|
|
||||||
switch (get_funct3()) {
|
|
||||||
case C_ADDI4SPN:
|
|
||||||
return OP_C_ADDI4SPN;
|
|
||||||
break;
|
|
||||||
case C_FLD:
|
|
||||||
return OP_C_FLD;
|
|
||||||
break;
|
|
||||||
case C_LW:
|
|
||||||
return OP_C_LW;
|
|
||||||
break;
|
|
||||||
case C_FLW:
|
|
||||||
return OP_C_FLW;
|
|
||||||
break;
|
|
||||||
case C_FSD:
|
|
||||||
return OP_C_FSD;
|
|
||||||
break;
|
|
||||||
case C_SW:
|
|
||||||
return OP_C_SW;
|
|
||||||
break;
|
|
||||||
case C_FSW:
|
|
||||||
return OP_C_FSW;
|
|
||||||
break;
|
|
||||||
[[unlikely]] default:
|
|
||||||
return OP_C_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0b01:
|
|
||||||
switch (get_funct3()) {
|
|
||||||
case C_ADDI:
|
|
||||||
return OP_C_ADDI;
|
|
||||||
break;
|
|
||||||
case C_JAL:
|
|
||||||
return OP_C_JAL;
|
|
||||||
break;
|
|
||||||
case C_LI:
|
|
||||||
return OP_C_LI;
|
|
||||||
break;
|
|
||||||
case C_ADDI16SP:
|
|
||||||
return OP_C_ADDI16SP;
|
|
||||||
break;
|
|
||||||
case C_SRLI:
|
|
||||||
switch (m_instr.range(11, 10)) {
|
|
||||||
case C_2_SRLI:
|
|
||||||
return OP_C_SRLI;
|
|
||||||
break;
|
|
||||||
case C_2_SRAI:
|
|
||||||
return OP_C_SRAI;
|
|
||||||
break;
|
|
||||||
case C_2_ANDI:
|
|
||||||
return OP_C_ANDI;
|
|
||||||
break;
|
|
||||||
case C_2_SUB:
|
|
||||||
switch (m_instr.range(6, 5)) {
|
|
||||||
case C_3_SUB:
|
|
||||||
return OP_C_SUB;
|
|
||||||
break;
|
|
||||||
case C_3_XOR:
|
|
||||||
return OP_C_XOR;
|
|
||||||
break;
|
|
||||||
case C_3_OR:
|
|
||||||
return OP_C_OR;
|
|
||||||
break;
|
|
||||||
case C_3_AND:
|
|
||||||
return OP_C_AND;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case C_J:
|
|
||||||
return OP_C_J;
|
|
||||||
break;
|
|
||||||
case C_BEQZ:
|
|
||||||
return OP_C_BEQZ;
|
|
||||||
break;
|
|
||||||
case C_BNEZ:
|
|
||||||
return OP_C_BNEZ;
|
|
||||||
break;
|
|
||||||
[[unlikely]] default:
|
|
||||||
return OP_C_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0b10:
|
|
||||||
switch (get_funct3()) {
|
|
||||||
case C_SLLI:
|
|
||||||
return OP_C_SLLI;
|
|
||||||
break;
|
|
||||||
case C_FLDSP:
|
|
||||||
case C_LWSP:
|
|
||||||
return OP_C_LWSP;
|
|
||||||
break;
|
|
||||||
case C_FLWSP:
|
|
||||||
return OP_C_FLWSP;
|
|
||||||
break;
|
|
||||||
case C_JR:
|
|
||||||
if (m_instr[12] == 0) {
|
|
||||||
if (m_instr.range(6, 2) == 0) {
|
|
||||||
return OP_C_JR;
|
|
||||||
} else {
|
|
||||||
return OP_C_MV;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (m_instr.range(11, 2) == 0) {
|
|
||||||
return OP_C_EBREAK;
|
|
||||||
} else if (m_instr.range(6, 2) == 0) {
|
|
||||||
return OP_C_JALR;
|
|
||||||
} else {
|
|
||||||
return OP_C_ADD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case C_FDSP:
|
|
||||||
break;
|
|
||||||
case C_SWSP:
|
|
||||||
return OP_C_SWSP;
|
|
||||||
break;
|
|
||||||
case C_FWWSP:
|
|
||||||
[[unlikely]]
|
|
||||||
default:
|
|
||||||
return OP_C_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
[[unlikely]] default:
|
|
||||||
|
|
||||||
return OP_C_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return OP_C_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_JR() {
|
|
||||||
std::uint32_t mem_addr;
|
|
||||||
int rs1;
|
|
||||||
int new_pc;
|
|
||||||
|
|
||||||
rs1 = get_rs1();
|
|
||||||
mem_addr = 0;
|
|
||||||
|
|
||||||
new_pc = static_cast<std::int32_t>(
|
|
||||||
static_cast<std::int32_t>((regs->getValue(rs1)) + static_cast<std::int32_t>(mem_addr)) & 0xFFFFFFFE);
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.JR: PC <- 0x{:x}", sc_core::sc_time_stamp().value(), regs->getPC(), new_pc);
|
|
||||||
|
|
||||||
regs->setPC(new_pc);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_MV() {
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::uint32_t calc;
|
|
||||||
|
|
||||||
rd = get_rd();
|
|
||||||
rs1 = 0;
|
|
||||||
rs2 = get_rs2();
|
|
||||||
|
|
||||||
calc = regs->getValue(rs1) + regs->getValue(rs2);
|
|
||||||
regs->setValue(rd, static_cast<std::int32_t>(calc));
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.MV: x{:d}(0x{:x}) + x{:d}(0x{:x}) -> x{:d}(0x{:x})",
|
|
||||||
sc_core::sc_time_stamp().value(), regs->getPC(),
|
|
||||||
rs1, regs->getValue(rs1), rs2, regs->getValue(rs2), rd, calc);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_ADD() {
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::uint32_t calc;
|
|
||||||
|
|
||||||
rd = get_rs1();
|
|
||||||
rs1 = get_rs1();
|
|
||||||
rs2 = get_rs2();
|
|
||||||
|
|
||||||
calc = regs->getValue(rs1) + regs->getValue(rs2);
|
|
||||||
regs->setValue(rd, static_cast<std::int32_t>(calc));
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.ADD: x{:d} + x{} -> x{:d}(0x{:x})", sc_core::sc_time_stamp().value(),
|
|
||||||
regs->getPC(),
|
|
||||||
rs1, rs2, rd, calc);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_LWSP() {
|
|
||||||
std::uint32_t mem_addr;
|
|
||||||
int rd, rs1;
|
|
||||||
std::int32_t imm;
|
|
||||||
std::uint32_t data;
|
|
||||||
|
|
||||||
// lw rd, offset[7:2](x2)
|
|
||||||
|
|
||||||
rd = get_rd();
|
|
||||||
rs1 = 2;
|
|
||||||
imm = get_imm_LWSP();
|
|
||||||
|
|
||||||
mem_addr = imm + regs->getValue(rs1);
|
|
||||||
data = mem_intf->readDataMem(mem_addr, 4);
|
|
||||||
perf->dataMemoryRead();
|
|
||||||
regs->setValue(rd, static_cast<std::int32_t>(data));
|
|
||||||
|
|
||||||
regs->setValue(rd, data);
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.LWSP: x{:d} + {:d}(@0x{:x}) -> x{:d}({:x})",
|
|
||||||
sc_core::sc_time_stamp().value(), regs->getPC(),
|
|
||||||
rs1, imm, mem_addr, rd, data);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_ADDI4SPN() {
|
|
||||||
int rd, rs1;
|
|
||||||
std::int32_t imm;
|
|
||||||
std::int32_t calc;
|
|
||||||
|
|
||||||
rd = get_rdp();
|
|
||||||
rs1 = 2;
|
|
||||||
imm = get_imm_ADDI4SPN();
|
|
||||||
|
|
||||||
if (imm == 0) {
|
|
||||||
RaiseException(EXCEPTION_CAUSE_ILLEGAL_INSTRUCTION, m_instr);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
calc = static_cast<std::int32_t>(regs->getValue(rs1)) + imm;
|
|
||||||
regs->setValue(rd, calc);
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.ADDI4SN: x{:d} + (0x{:x}) + {:d} -> x{:d}(0x{:x})",
|
|
||||||
sc_core::sc_time_stamp().value(), regs->getPC(),
|
|
||||||
rs1, regs->getValue(rs1), imm, rd, calc);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_ADDI16SP() {
|
|
||||||
// addi x2, x2, nzimm[9:4]
|
|
||||||
int rd;
|
|
||||||
std::int32_t imm;
|
|
||||||
|
|
||||||
if (get_rd() == 2) {
|
|
||||||
int rs1;
|
|
||||||
std::int32_t calc;
|
|
||||||
|
|
||||||
rd = 2;
|
|
||||||
rs1 = 2;
|
|
||||||
imm = get_imm_ADDI16SP();
|
|
||||||
|
|
||||||
calc = static_cast<std::int32_t>(regs->getValue(rs1)) + imm;
|
|
||||||
regs->setValue(rd, calc);
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.ADDI16SP: x{:d} + {:d} -> x{:d} (0x{:x})",
|
|
||||||
sc_core::sc_time_stamp().value(), regs->getPC(),
|
|
||||||
rs1, imm, rd, calc);
|
|
||||||
} else {
|
|
||||||
/* C.LUI OPCODE */
|
|
||||||
rd = get_rd();
|
|
||||||
imm = get_imm_LUI();
|
|
||||||
regs->setValue(rd, imm);
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.LUI: x{:d} <- 0x{:x}", sc_core::sc_time_stamp().value(), regs->getPC(),
|
|
||||||
rd, imm);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_SWSP() {
|
|
||||||
// sw rs2, offset(x2)
|
|
||||||
std::uint32_t mem_addr;
|
|
||||||
int rs1, rs2;
|
|
||||||
std::int32_t imm;
|
|
||||||
std::uint32_t data;
|
|
||||||
|
|
||||||
rs1 = 2;
|
|
||||||
rs2 = get_rs2();
|
|
||||||
imm = get_imm_CSS();
|
|
||||||
|
|
||||||
mem_addr = imm + regs->getValue(rs1);
|
|
||||||
data = regs->getValue(rs2);
|
|
||||||
|
|
||||||
mem_intf->writeDataMem(mem_addr, data, 4);
|
|
||||||
perf->dataMemoryWrite();
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.SWSP: x{:d}(0x{:x}) -> x{:d} + {} (@0x{:x})",
|
|
||||||
sc_core::sc_time_stamp().value(), regs->getPC(),
|
|
||||||
rs2, data, rs1, imm, mem_addr);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_BEQZ() {
|
|
||||||
int rs1;
|
|
||||||
int new_pc;
|
|
||||||
std::uint32_t val1;
|
|
||||||
|
|
||||||
rs1 = get_rs1p();
|
|
||||||
val1 = regs->getValue(rs1);
|
|
||||||
|
|
||||||
if (val1 == 0) {
|
|
||||||
new_pc = static_cast<std::int32_t>(regs->getPC()) + get_imm_CB();
|
|
||||||
regs->setPC(new_pc);
|
|
||||||
} else {
|
|
||||||
regs->incPCby2();
|
|
||||||
new_pc = static_cast<std::int32_t>(regs->getPC());
|
|
||||||
}
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.BEQZ: x{:d}(0x{:x}) == 0? -> PC (0xx{:d})",
|
|
||||||
sc_core::sc_time_stamp().value(), regs->getPC(),
|
|
||||||
rs1, val1, new_pc);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_BNEZ() {
|
|
||||||
int rs1;
|
|
||||||
int new_pc;
|
|
||||||
std::uint32_t val1;
|
|
||||||
|
|
||||||
rs1 = get_rs1p();
|
|
||||||
val1 = regs->getValue(rs1);
|
|
||||||
|
|
||||||
if (val1 != 0) {
|
|
||||||
new_pc = static_cast<std::int32_t>(regs->getPC()) + get_imm_CB();
|
|
||||||
regs->setPC(new_pc);
|
|
||||||
} else {
|
|
||||||
regs->incPCby2(); //PC <- PC +2
|
|
||||||
new_pc = static_cast<std::int32_t>(regs->getPC());
|
|
||||||
}
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.BNEZ: x{:d}(0x{:x}) != 0? -> PC (0xx{:d})",
|
|
||||||
sc_core::sc_time_stamp().value(), regs->getPC(),
|
|
||||||
rs1, val1, new_pc);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_LI() {
|
|
||||||
int rd, rs1;
|
|
||||||
std::int32_t imm;
|
|
||||||
std::int32_t calc;
|
|
||||||
|
|
||||||
rd = get_rd();
|
|
||||||
rs1 = 0;
|
|
||||||
imm = get_imm_ADDI();
|
|
||||||
|
|
||||||
calc = static_cast<std::int32_t>(regs->getValue(rs1)) + imm;
|
|
||||||
regs->setValue(rd, calc);
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.LI: x{:d} ({:d}) + {:d} -> x{:d}(0x{:x}) ",
|
|
||||||
sc_core::sc_time_stamp().value(), regs->getPC(),
|
|
||||||
rs1, regs->getValue(rs1), imm, rd, calc);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_SRLI() {
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::uint32_t shift;
|
|
||||||
std::uint32_t calc;
|
|
||||||
|
|
||||||
rd = get_rs1p();
|
|
||||||
rs1 = get_rs1p();
|
|
||||||
rs2 = get_rs2();
|
|
||||||
|
|
||||||
shift = rs2 & 0x1F;
|
|
||||||
|
|
||||||
calc = static_cast<std::uint32_t>(regs->getValue(rs1)) >> shift;
|
|
||||||
regs->setValue(rd, static_cast<std::int32_t>(calc));
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.SRLI: x{:d} >> {} -> x{:d}", sc_core::sc_time_stamp().value(),
|
|
||||||
regs->getPC(),
|
|
||||||
rs1, shift, rd);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_SRAI() {
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::uint32_t shift;
|
|
||||||
std::int32_t calc;
|
|
||||||
|
|
||||||
rd = get_rs1p();
|
|
||||||
rs1 = get_rs1p();
|
|
||||||
rs2 = get_rs2();
|
|
||||||
|
|
||||||
shift = rs2 & 0x1F;
|
|
||||||
|
|
||||||
calc = static_cast<std::int32_t>(regs->getValue(rs1)) >> shift;
|
|
||||||
regs->setValue(rd, calc);
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.SRAI: x{:d} >> {} -> x{:d}(0x{:x})", sc_core::sc_time_stamp().value(),
|
|
||||||
regs->getPC(),
|
|
||||||
rs1, shift, rd, calc);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_SLLI() {
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::uint32_t shift;
|
|
||||||
std::uint32_t calc;
|
|
||||||
|
|
||||||
rd = get_rs1p();
|
|
||||||
rs1 = get_rs1p();
|
|
||||||
rs2 = get_imm_ADDI();
|
|
||||||
|
|
||||||
shift = rs2 & 0x1F;
|
|
||||||
|
|
||||||
calc = static_cast<std::uint32_t>(regs->getValue(rs1)) << shift;
|
|
||||||
regs->setValue(rd, static_cast<std::int32_t>(calc));
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.SLLI: x{:d} << {} -> x{:d}(0x{:x})", sc_core::sc_time_stamp().value(),
|
|
||||||
regs->getPC(),
|
|
||||||
rs1, shift, rd, calc);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_ANDI() {
|
|
||||||
int rd, rs1;
|
|
||||||
std::uint32_t imm;
|
|
||||||
std::uint32_t aux;
|
|
||||||
std::uint32_t calc;
|
|
||||||
|
|
||||||
rd = get_rs1p();
|
|
||||||
rs1 = get_rs1p();
|
|
||||||
imm = get_imm_ADDI();
|
|
||||||
|
|
||||||
aux = regs->getValue(rs1);
|
|
||||||
calc = aux & imm;
|
|
||||||
regs->setValue(rd, static_cast<std::int32_t>(calc));
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.ANDI: x{:d}(0x{:x}) AND 0x{:x} -> x{:d}", sc_core::sc_time_stamp().value(),
|
|
||||||
regs->getPC(),
|
|
||||||
rs1, aux, imm, rd);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_SUB() {
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::uint32_t calc;
|
|
||||||
|
|
||||||
rd = get_rs1p();
|
|
||||||
rs1 = get_rs1p();
|
|
||||||
rs2 = get_rs2p();
|
|
||||||
|
|
||||||
calc = regs->getValue(rs1) - regs->getValue(rs2);
|
|
||||||
regs->setValue(rd, static_cast<std::int32_t>(calc));
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.SUB: x{:d} - x{:d} -> x{:d}(0x{:x})", sc_core::sc_time_stamp().value(),
|
|
||||||
regs->getPC(),
|
|
||||||
rs1, rs2, rd, calc);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_XOR() {
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::uint32_t calc;
|
|
||||||
|
|
||||||
rd = get_rs1p();
|
|
||||||
rs1 = get_rs1p();
|
|
||||||
rs2 = get_rs2p();
|
|
||||||
|
|
||||||
calc = regs->getValue(rs1) ^ regs->getValue(rs2);
|
|
||||||
regs->setValue(rd, static_cast<std::int32_t>(calc));
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.XOR: x{:d} XOR x{:d} -> x{:d}", sc_core::sc_time_stamp().value(),
|
|
||||||
regs->getPC(),
|
|
||||||
rs1, rs2, rd);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_OR() {
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::uint32_t calc;
|
|
||||||
|
|
||||||
rd = get_rs1p();
|
|
||||||
rs1 = get_rs1p();
|
|
||||||
rs2 = get_rs2p();
|
|
||||||
|
|
||||||
calc = regs->getValue(rs1) | regs->getValue(rs2);
|
|
||||||
regs->setValue(rd, static_cast<std::int32_t>(calc));
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.OR: x{:d} OR x{:d} -> x{:d}", sc_core::sc_time_stamp().value(),
|
|
||||||
regs->getPC(),
|
|
||||||
rs1, rs2, rd);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_AND() {
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::uint32_t calc;
|
|
||||||
|
|
||||||
rd = get_rs1p();
|
|
||||||
rs1 = get_rs1p();
|
|
||||||
rs2 = get_rs2p();
|
|
||||||
|
|
||||||
calc = regs->getValue(rs1) & regs->getValue(rs2);
|
|
||||||
regs->setValue(rd, static_cast<std::int32_t>(calc));
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.AND: x{:d} AND x{:d} -> x{:d}", sc_core::sc_time_stamp().value(),
|
|
||||||
regs->getPC(),
|
|
||||||
rs1, rs2, rd);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_ADDI() const {
|
|
||||||
int rd, rs1;
|
|
||||||
std::int32_t imm;
|
|
||||||
std::int32_t calc;
|
|
||||||
|
|
||||||
rd = get_rd();
|
|
||||||
rs1 = rd;
|
|
||||||
imm = get_imm_ADDI();
|
|
||||||
|
|
||||||
calc = static_cast<std::int32_t>(regs->getValue(rs1)) + imm;
|
|
||||||
regs->setValue(rd, calc);
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.ADDI: x{:d} + {} -> x{:d}(0x{:x})", sc_core::sc_time_stamp().value(),
|
|
||||||
regs->getPC(), rs1, imm, rd, calc);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_JALR() {
|
|
||||||
std::uint32_t mem_addr = 0;
|
|
||||||
int rd, rs1;
|
|
||||||
int new_pc, old_pc;
|
|
||||||
|
|
||||||
rd = 1;
|
|
||||||
rs1 = get_rs1();
|
|
||||||
|
|
||||||
old_pc = static_cast<std::int32_t>(regs->getPC());
|
|
||||||
regs->setValue(rd, old_pc + 2);
|
|
||||||
|
|
||||||
new_pc = static_cast<std::int32_t>((regs->getValue(rs1) + mem_addr) & 0xFFFFFFFE);
|
|
||||||
regs->setPC(new_pc);
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.JALR: x{:d} <- 0x{:x} PC <- 0xx{:x}", sc_core::sc_time_stamp().value(),
|
|
||||||
regs->getPC(),
|
|
||||||
rd, old_pc + 4, new_pc);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_LW() {
|
|
||||||
std::uint32_t mem_addr;
|
|
||||||
int rd, rs1;
|
|
||||||
std::int32_t imm;
|
|
||||||
std::uint32_t data;
|
|
||||||
|
|
||||||
rd = get_rdp();
|
|
||||||
rs1 = get_rs1p();
|
|
||||||
imm = get_imm_L();
|
|
||||||
|
|
||||||
mem_addr = imm + regs->getValue(rs1);
|
|
||||||
data = mem_intf->readDataMem(mem_addr, 4);
|
|
||||||
perf->dataMemoryRead();
|
|
||||||
regs->setValue(rd, static_cast<std::int32_t>(data));
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.LW: x{:d}(0x{:x}) + {:d} (@0x{:x}) -> {:d} (0x{:x})",
|
|
||||||
sc_core::sc_time_stamp().value(), regs->getPC(),
|
|
||||||
rs1, regs->getValue(rs1), imm, mem_addr, rd, data);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_SW() {
|
|
||||||
std::uint32_t mem_addr;
|
|
||||||
int rs1, rs2;
|
|
||||||
std::int32_t imm;
|
|
||||||
std::uint32_t data;
|
|
||||||
|
|
||||||
rs1 = get_rs1p();
|
|
||||||
rs2 = get_rs2p();
|
|
||||||
imm = get_imm_L();
|
|
||||||
|
|
||||||
mem_addr = imm + regs->getValue(rs1);
|
|
||||||
data = regs->getValue(rs2);
|
|
||||||
|
|
||||||
mem_intf->writeDataMem(mem_addr, data, 4);
|
|
||||||
perf->dataMemoryWrite();
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.SW: x{:d}(0x{:x}) -> x{:d} + 0x{:x}(@0x{:x})",
|
|
||||||
sc_core::sc_time_stamp().value(), regs->getPC(),
|
|
||||||
rs2, data, rs1, imm, mem_addr);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_JAL(int m_rd) {
|
|
||||||
std::int32_t mem_addr;
|
|
||||||
int rd;
|
|
||||||
int new_pc, old_pc;
|
|
||||||
|
|
||||||
rd = m_rd;
|
|
||||||
mem_addr = get_imm_J();
|
|
||||||
old_pc = static_cast<std::int32_t>(regs->getPC());
|
|
||||||
|
|
||||||
new_pc = old_pc + mem_addr;
|
|
||||||
regs->setPC(new_pc);
|
|
||||||
|
|
||||||
old_pc = old_pc + 2;
|
|
||||||
regs->setValue(rd, old_pc);
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. C.JAL: x{:d} <- 0x{:x}. PC + 0x{:x} -> PC (0x{:x})",
|
|
||||||
sc_core::sc_time_stamp().value(), regs->getPC(),
|
|
||||||
rd, old_pc, mem_addr, new_pc);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::Exec_C_EBREAK() {
|
|
||||||
|
|
||||||
logger->debug("C.EBREAK");
|
|
||||||
std::cout << "\n" << "C.EBRAK Instruction called, dumping information"
|
|
||||||
<< "\n";
|
|
||||||
regs->dump();
|
|
||||||
std::cout << "Simulation time " << sc_core::sc_time_stamp() << "\n";
|
|
||||||
perf->dump();
|
|
||||||
|
|
||||||
sc_core::sc_stop();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool C_extension::process_instruction(Instruction &inst, bool *breakpoint) {
|
|
||||||
bool PC_not_affected = true;
|
|
||||||
|
|
||||||
*breakpoint = false;
|
|
||||||
|
|
||||||
setInstr(inst.getInstr());
|
|
||||||
|
|
||||||
switch (decode()) {
|
|
||||||
case OP_C_ADDI4SPN:
|
|
||||||
PC_not_affected = Exec_C_ADDI4SPN();
|
|
||||||
break;
|
|
||||||
case OP_C_LW:
|
|
||||||
Exec_C_LW();
|
|
||||||
break;
|
|
||||||
case OP_C_SW:
|
|
||||||
Exec_C_SW();
|
|
||||||
break;
|
|
||||||
case OP_C_ADDI:
|
|
||||||
Exec_C_ADDI();
|
|
||||||
break;
|
|
||||||
case OP_C_JAL:
|
|
||||||
Exec_C_JAL(1);
|
|
||||||
PC_not_affected = false;
|
|
||||||
break;
|
|
||||||
case OP_C_J:
|
|
||||||
Exec_C_JAL(0);
|
|
||||||
PC_not_affected = false;
|
|
||||||
break;
|
|
||||||
case OP_C_LI:
|
|
||||||
Exec_C_LI();
|
|
||||||
break;
|
|
||||||
case OP_C_SLLI:
|
|
||||||
Exec_C_SLLI();
|
|
||||||
break;
|
|
||||||
case OP_C_LWSP:
|
|
||||||
Exec_C_LWSP();
|
|
||||||
break;
|
|
||||||
case OP_C_JR:
|
|
||||||
Exec_C_JR();
|
|
||||||
PC_not_affected = false;
|
|
||||||
break;
|
|
||||||
case OP_C_MV:
|
|
||||||
Exec_C_MV();
|
|
||||||
break;
|
|
||||||
case OP_C_JALR:
|
|
||||||
Exec_C_JALR();
|
|
||||||
PC_not_affected = false;
|
|
||||||
break;
|
|
||||||
case OP_C_ADD:
|
|
||||||
Exec_C_ADD();
|
|
||||||
break;
|
|
||||||
case OP_C_SWSP:
|
|
||||||
Exec_C_SWSP();
|
|
||||||
break;
|
|
||||||
case OP_C_ADDI16SP:
|
|
||||||
Exec_C_ADDI16SP();
|
|
||||||
break;
|
|
||||||
case OP_C_BEQZ:
|
|
||||||
Exec_C_BEQZ();
|
|
||||||
PC_not_affected = false;
|
|
||||||
break;
|
|
||||||
case OP_C_BNEZ:
|
|
||||||
Exec_C_BNEZ();
|
|
||||||
PC_not_affected = false;
|
|
||||||
break;
|
|
||||||
case OP_C_SRLI:
|
|
||||||
Exec_C_SRLI();
|
|
||||||
break;
|
|
||||||
case OP_C_SRAI:
|
|
||||||
Exec_C_SRAI();
|
|
||||||
break;
|
|
||||||
case OP_C_ANDI:
|
|
||||||
Exec_C_ANDI();
|
|
||||||
break;
|
|
||||||
case OP_C_SUB:
|
|
||||||
Exec_C_SUB();
|
|
||||||
break;
|
|
||||||
case OP_C_XOR:
|
|
||||||
Exec_C_XOR();
|
|
||||||
break;
|
|
||||||
case OP_C_OR:
|
|
||||||
Exec_C_OR();
|
|
||||||
break;
|
|
||||||
case OP_C_AND:
|
|
||||||
Exec_C_AND();
|
|
||||||
break;
|
|
||||||
case OP_C_EBREAK:
|
|
||||||
Exec_C_EBREAK();
|
|
||||||
std::cout << "C_EBREAK" << std::endl;
|
|
||||||
*breakpoint = true;
|
|
||||||
break;
|
|
||||||
[[unlikely]] default:
|
|
||||||
std::cout << "C instruction not implemented yet" << "\n";
|
|
||||||
inst.dump();
|
|
||||||
NOP();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return PC_not_affected;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -23,7 +23,7 @@ namespace riscv_tlm {
|
||||||
constexpr char nibble_to_hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
|
constexpr char nibble_to_hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
|
||||||
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||||
|
|
||||||
Debug::Debug(riscv_tlm::CPU *cpu, Memory *mem) : sc_module(sc_core::sc_module_name("Debug")) {
|
Debug::Debug(riscv_tlm::RV32 *cpu, Memory *mem) : sc_module(sc_core::sc_module_name("Debug")) {
|
||||||
dbg_cpu = cpu;
|
dbg_cpu = cpu;
|
||||||
dbg_mem = mem;
|
dbg_mem = mem;
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ namespace riscv_tlm {
|
||||||
void Debug::handle_gdb_loop() {
|
void Debug::handle_gdb_loop() {
|
||||||
std::cout << "Handle_GDB_Loop\n";
|
std::cout << "Handle_GDB_Loop\n";
|
||||||
|
|
||||||
Registers *register_bank = dbg_cpu->getRegisterBank();
|
Registers<std::uint32_t> *register_bank = dbg_cpu->getRegisterBank();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
std::string msg = receive_packet();
|
std::string msg = receive_packet();
|
||||||
|
@ -120,7 +120,7 @@ namespace riscv_tlm {
|
||||||
send_packet(conn, stream.str());
|
send_packet(conn, stream.str());
|
||||||
} else if (boost::starts_with(msg, "p")) {
|
} else if (boost::starts_with(msg, "p")) {
|
||||||
long n = strtol(msg.c_str() + 1, 0, 16);
|
long n = strtol(msg.c_str() + 1, 0, 16);
|
||||||
unsigned int reg_value;
|
std::uint64_t reg_value;
|
||||||
if (n < 32) {
|
if (n < 32) {
|
||||||
reg_value = register_bank->getValue(n);
|
reg_value = register_bank->getValue(n);
|
||||||
} else if (n == 32) {
|
} else if (n == 32) {
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace riscv_tlm {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension_t Instruction::check_extension() const {
|
extension_t Instruction::check_extension() const {
|
||||||
if (((m_instr & 0x0000007F) == 0b0110011)
|
if ((((m_instr & 0x0000007F) == 0b0110011) || ((m_instr & 0x0000007F) == 0b0111011))
|
||||||
&& (((m_instr & 0x7F000000) >> 25) == 0b0000001)) {
|
&& (((m_instr & 0x7F000000) >> 25) == 0b0000001)) {
|
||||||
return M_EXTENSION;
|
return M_EXTENSION;
|
||||||
} else if ((m_instr & 0x0000007F) == 0b0101111) {
|
} else if ((m_instr & 0x0000007F) == 0b0101111) {
|
||||||
|
|
|
@ -7,293 +7,162 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#include "M_extension.h"
|
#include "M_extension.h"
|
||||||
|
|
||||||
namespace riscv_tlm {
|
namespace riscv_tlm {
|
||||||
|
// RV32
|
||||||
op_M_Codes M_extension::decode() const {
|
template<>
|
||||||
|
void M_extension<std::uint32_t>::Exec_M_MULH() const {
|
||||||
switch (opcode()) {
|
unsigned int rd, rs1, rs2;
|
||||||
case M_MUL:
|
signed_T multiplier, multiplicand;
|
||||||
return OP_M_MUL;
|
|
||||||
break;
|
|
||||||
case M_MULH:
|
|
||||||
return OP_M_MULH;
|
|
||||||
break;
|
|
||||||
case M_MULHSU:
|
|
||||||
return OP_M_MULHSU;
|
|
||||||
break;
|
|
||||||
case M_MULHU:
|
|
||||||
return OP_M_MULHU;
|
|
||||||
break;
|
|
||||||
case M_DIV:
|
|
||||||
return OP_M_DIV;
|
|
||||||
break;
|
|
||||||
case M_DIVU:
|
|
||||||
return OP_M_DIVU;
|
|
||||||
break;
|
|
||||||
case M_REM:
|
|
||||||
return OP_M_REM;
|
|
||||||
break;
|
|
||||||
case M_REMU:
|
|
||||||
return OP_M_REMU;
|
|
||||||
break;
|
|
||||||
[[unlikely]] default:
|
|
||||||
return OP_M_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return OP_M_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool M_extension::Exec_M_MUL() const {
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::int32_t multiplier, multiplicand;
|
|
||||||
std::int64_t result;
|
std::int64_t result;
|
||||||
|
signed_T ret_value;
|
||||||
|
|
||||||
rd = get_rd();
|
rd = this->get_rd();
|
||||||
rs1 = get_rs1();
|
rs1 = this->get_rs1();
|
||||||
rs2 = get_rs2();
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
multiplier = static_cast<std::int32_t>(regs->getValue(rs1));
|
multiplier = this->regs->getValue(rs1);
|
||||||
multiplicand = static_cast<std::int32_t>(regs->getValue(rs2));
|
multiplicand = this->regs->getValue(rs2);
|
||||||
|
|
||||||
result = static_cast<std::int64_t>(multiplier * multiplicand);
|
|
||||||
result = result & 0x00000000FFFFFFFF;
|
|
||||||
regs->setValue(rd, static_cast<std::int32_t>(result));
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. M.MUL: x{:d} * x{:d} -> x{:d}({:d})", sc_core::sc_time_stamp().value(),
|
|
||||||
regs->getPC(),
|
|
||||||
rs1, rs2, rd, result);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool M_extension::Exec_M_MULH() const {
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::int32_t multiplier, multiplicand;
|
|
||||||
std::int64_t result;
|
|
||||||
std::int32_t ret_value;
|
|
||||||
|
|
||||||
rd = get_rd();
|
|
||||||
rs1 = get_rs1();
|
|
||||||
rs2 = get_rs2();
|
|
||||||
|
|
||||||
multiplier = static_cast<std::int32_t>(regs->getValue(rs1));
|
|
||||||
multiplicand = static_cast<std::int32_t>(regs->getValue(rs2));
|
|
||||||
|
|
||||||
result = static_cast<std::int64_t>(multiplier) * static_cast<std::int64_t>(multiplicand);
|
result = static_cast<std::int64_t>(multiplier) * static_cast<std::int64_t>(multiplicand);
|
||||||
|
|
||||||
ret_value = static_cast<std::int32_t>((result >> 32) & 0x00000000FFFFFFFF);
|
ret_value = static_cast<std::int32_t>((result >> 32) & 0x00000000FFFFFFFF);
|
||||||
regs->setValue(rd, ret_value);
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. M.MULH: x{:d} * x{:d} -> x{:d}({:d})", sc_core::sc_time_stamp().value(),
|
this->regs->setValue(rd, ret_value);
|
||||||
regs->getPC(),
|
|
||||||
rs1, rs2, rd, result);
|
|
||||||
|
|
||||||
return true;
|
this->logger->debug("{} ns. PC: 0x{:x}. M.MULH: x{:d}({:d}) * x{:d}({:d}) -> x{:d}({:d})",
|
||||||
|
sc_core::sc_time_stamp().value(),
|
||||||
|
this->regs->getPC(),
|
||||||
|
rs1, multiplier, rs2, multiplicand, rd, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool M_extension::Exec_M_MULHSU() const {
|
template<>
|
||||||
int rd, rs1, rs2;
|
void M_extension<std::uint32_t>::Exec_M_MULHSU() const {
|
||||||
|
unsigned int rd, rs1, rs2;
|
||||||
std::int32_t multiplier;
|
std::int32_t multiplier;
|
||||||
std::uint32_t multiplicand;
|
std::uint32_t multiplicand;
|
||||||
std::int64_t result;
|
std::int64_t result;
|
||||||
|
|
||||||
rd = get_rd();
|
rd = this->get_rd();
|
||||||
rs1 = get_rs1();
|
rs1 = this->get_rs1();
|
||||||
rs2 = get_rs2();
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
multiplier = static_cast<std::int32_t>(regs->getValue(rs1));
|
multiplier = static_cast<std::int32_t>(this->regs->getValue(rs1));
|
||||||
multiplicand = regs->getValue(rs2);
|
multiplicand = this->regs->getValue(rs2);
|
||||||
|
|
||||||
result = static_cast<std::int64_t>(multiplier * static_cast<std::uint64_t>(multiplicand));
|
result = static_cast<std::int64_t>(multiplier * static_cast<std::uint64_t>(multiplicand));
|
||||||
result = (result >> 32) & 0x00000000FFFFFFFF;
|
result = (result >> 32) & 0x00000000FFFFFFFF;
|
||||||
regs->setValue(rd, static_cast<std::int32_t>(result));
|
this->regs->setValue(rd, static_cast<std::int32_t>(result));
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. M.MULHSU: x{:d} * x{:d} -> x{:d}({:d})", sc_core::sc_time_stamp().value(),
|
this->logger->debug("{} ns. PC: 0x{:x}. M.MULHSU: x{:d} * x{:d} -> x{:d}({:d})",
|
||||||
regs->getPC(),
|
sc_core::sc_time_stamp().value(),
|
||||||
|
this->regs->getPC(),
|
||||||
rs1, rs2, rd, result);
|
rs1, rs2, rd, result);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool M_extension::Exec_M_MULHU() const {
|
template<>
|
||||||
int rd, rs1, rs2;
|
void M_extension<std::uint32_t>::Exec_M_MULHU() const {
|
||||||
|
unsigned int rd, rs1, rs2;
|
||||||
std::uint32_t multiplier, multiplicand;
|
std::uint32_t multiplier, multiplicand;
|
||||||
std::uint64_t result;
|
std::uint64_t result;
|
||||||
std::int32_t ret_value;
|
std::int32_t ret_value;
|
||||||
|
|
||||||
rd = get_rd();
|
rd = this->get_rd();
|
||||||
rs1 = get_rs1();
|
rs1 = this->get_rs1();
|
||||||
rs2 = get_rs2();
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
multiplier = static_cast<std::int32_t>(regs->getValue(rs1));
|
multiplier = static_cast<std::int32_t>(this->regs->getValue(rs1));
|
||||||
multiplicand = static_cast<std::int32_t>(regs->getValue(rs2));
|
multiplicand = static_cast<std::int32_t>(this->regs->getValue(rs2));
|
||||||
|
|
||||||
result = static_cast<std::uint64_t>(multiplier) * static_cast<std::uint64_t>(multiplicand);
|
result = static_cast<std::uint64_t>(multiplier) * static_cast<std::uint64_t>(multiplicand);
|
||||||
ret_value = static_cast<std::int32_t>((result >> 32) & 0x00000000FFFFFFFF);
|
ret_value = static_cast<std::int32_t>((result >> 32) & 0x00000000FFFFFFFF);
|
||||||
regs->setValue(rd, ret_value);
|
this->regs->setValue(rd, ret_value);
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. M.MULHU: x{:d} * x{:d} -> x{:d}({:d})", sc_core::sc_time_stamp().value(),
|
this->logger->debug("{} ns. PC: 0x{:x}. M.MULHU: x{:d} * x{:d} -> x{:d}({:d})",
|
||||||
regs->getPC(),
|
sc_core::sc_time_stamp().value(),
|
||||||
|
this->regs->getPC(),
|
||||||
rs1, rs2, rd, result);
|
rs1, rs2, rd, result);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool M_extension::Exec_M_DIV() const {
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::int32_t divisor, dividend;
|
|
||||||
std::int64_t result;
|
|
||||||
|
|
||||||
rd = get_rd();
|
// RV64
|
||||||
rs1 = get_rs1();
|
// I need to use SystemC bigint with 128 bits to perform 64 x 64 bits multiplication and keep the high half
|
||||||
rs2 = get_rs2();
|
template<>
|
||||||
|
void M_extension<std::uint64_t>::Exec_M_MULH() const {
|
||||||
|
unsigned int rd, rs1, rs2;
|
||||||
|
signed_T multiplier, multiplicand;
|
||||||
|
signed_T result;
|
||||||
|
|
||||||
dividend = static_cast<std::int32_t>(regs->getValue(rs1));
|
rd = this->get_rd();
|
||||||
divisor = static_cast<std::int32_t>(regs->getValue(rs2));
|
rs1 = this->get_rs1();
|
||||||
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
if (divisor == 0) {
|
multiplier = this->regs->getValue(rs1);
|
||||||
result = -1;
|
multiplicand = this->regs->getValue(rs2);
|
||||||
} else if ((divisor == -1) && (dividend == static_cast<std::int32_t>(0x80000000))) {
|
|
||||||
result = 0x0000000080000000;
|
sc_dt::sc_bigint<128> mul = multiplier;
|
||||||
} else {
|
sc_dt::sc_bigint<128> muld = multiplicand;
|
||||||
result = dividend / divisor;
|
sc_dt::sc_bigint<128> res = mul * muld;
|
||||||
result = result & 0x00000000FFFFFFFF;
|
result = res.range(127, 64).to_int64();
|
||||||
|
|
||||||
|
this->regs->setValue(rd, result);
|
||||||
|
|
||||||
|
this->logger->debug("{} ns. PC: 0x{:x}. M.MULH: x{:d}({:d}) * x{:d}({:d}) -> x{:d}({:d})",
|
||||||
|
sc_core::sc_time_stamp().value(),
|
||||||
|
this->regs->getPC(),
|
||||||
|
rs1, multiplier, rs2, multiplicand, rd, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
regs->setValue(rd, static_cast<std::int32_t>(result));
|
template<>
|
||||||
|
void M_extension<std::uint64_t>::Exec_M_MULHSU() const {
|
||||||
|
unsigned int rd, rs1, rs2;
|
||||||
|
signed_T multiplier;
|
||||||
|
unsigned_T multiplicand;
|
||||||
|
signed_T result;
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. M.DIV: x{:d} / x{:d} -> x{:d}({:d})", sc_core::sc_time_stamp().value(),
|
rd = this->get_rd();
|
||||||
regs->getPC(),
|
rs1 = this->get_rs1();
|
||||||
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
|
multiplier = this->regs->getValue(rs1);
|
||||||
|
multiplicand = this->regs->getValue(rs2);
|
||||||
|
|
||||||
|
sc_dt::sc_bigint<128> mul = multiplier;
|
||||||
|
sc_dt::sc_bigint<128> muld = multiplicand;
|
||||||
|
sc_dt::sc_bigint<128> res = mul * muld;
|
||||||
|
result = res.range(127, 64).to_int64();
|
||||||
|
|
||||||
|
this->regs->setValue(rd, result);
|
||||||
|
|
||||||
|
this->logger->debug("{} ns. PC: 0x{:x}. M.MULHSU: x{:d} * x{:d} -> x{:d}({:d})",
|
||||||
|
sc_core::sc_time_stamp().value(),
|
||||||
|
this->regs->getPC(),
|
||||||
rs1, rs2, rd, result);
|
rs1, rs2, rd, result);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool M_extension::Exec_M_DIVU() const {
|
template<>
|
||||||
int rd, rs1, rs2;
|
void M_extension<std::uint64_t>::Exec_M_MULHU() const {
|
||||||
std::uint32_t divisor, dividend;
|
unsigned int rd, rs1, rs2;
|
||||||
std::uint64_t result;
|
unsigned_T multiplier, multiplicand;
|
||||||
|
unsigned_T result;
|
||||||
|
|
||||||
rd = get_rd();
|
rd = this->get_rd();
|
||||||
rs1 = get_rs1();
|
rs1 = this->get_rs1();
|
||||||
rs2 = get_rs2();
|
rs2 = this->get_rs2();
|
||||||
|
|
||||||
dividend = regs->getValue(rs1);
|
multiplier = this->regs->getValue(rs1);
|
||||||
divisor = regs->getValue(rs2);
|
multiplicand = this->regs->getValue(rs2);
|
||||||
|
|
||||||
if (divisor == 0) {
|
sc_dt::sc_bigint<128> mul = multiplier;
|
||||||
result = -1;
|
sc_dt::sc_bigint<128> muld = multiplicand;
|
||||||
} else {
|
sc_dt::sc_bigint<128> res = mul * muld;
|
||||||
result = dividend / divisor;
|
result = res.range(127, 64).to_uint64();
|
||||||
result = result & 0x00000000FFFFFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
regs->setValue(rd, static_cast<std::int32_t>(result));
|
this->regs->setValue(rd, result);
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. M.DIVU: x{:d} / x{:d} -> x{:d}({:d})", sc_core::sc_time_stamp().value(),
|
this->logger->debug("{} ns. PC: 0x{:x}. M.MULHU: x{:d} * x{:d} -> x{:d}({:d})",
|
||||||
regs->getPC(),
|
sc_core::sc_time_stamp().value(),
|
||||||
|
this->regs->getPC(),
|
||||||
rs1, rs2, rd, result);
|
rs1, rs2, rd, result);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool M_extension::Exec_M_REM() const {
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::int32_t divisor, dividend;
|
|
||||||
std::int32_t result;
|
|
||||||
|
|
||||||
rd = get_rd();
|
|
||||||
rs1 = get_rs1();
|
|
||||||
rs2 = get_rs2();
|
|
||||||
|
|
||||||
dividend = static_cast<std::int32_t>(regs->getValue(rs1));
|
|
||||||
divisor = static_cast<std::int32_t>(regs->getValue(rs2));
|
|
||||||
|
|
||||||
if (divisor == 0) {
|
|
||||||
result = dividend;
|
|
||||||
} else if ((divisor == -1) && (dividend == static_cast<std::int32_t>(0x80000000))) {
|
|
||||||
result = 0;
|
|
||||||
} else {
|
|
||||||
result = dividend % divisor;
|
|
||||||
}
|
|
||||||
|
|
||||||
regs->setValue(rd, result);
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. M.REM: x{:d} / x{:d} -> x{:d}({:d})", sc_core::sc_time_stamp().value(),
|
|
||||||
regs->getPC(),
|
|
||||||
rs1, rs2, rd, result);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool M_extension::Exec_M_REMU() const {
|
|
||||||
int rd, rs1, rs2;
|
|
||||||
std::uint32_t divisor, dividend;
|
|
||||||
std::uint32_t result;
|
|
||||||
|
|
||||||
rd = get_rd();
|
|
||||||
rs1 = get_rs1();
|
|
||||||
rs2 = get_rs2();
|
|
||||||
|
|
||||||
dividend = static_cast<std::int32_t>(regs->getValue(rs1));
|
|
||||||
divisor = static_cast<std::int32_t>(regs->getValue(rs2));
|
|
||||||
|
|
||||||
if (divisor == 0) {
|
|
||||||
result = dividend;
|
|
||||||
} else {
|
|
||||||
result = dividend % divisor;
|
|
||||||
}
|
|
||||||
|
|
||||||
regs->setValue(rd, static_cast<std::int32_t>(result));
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. M.REMU: x{:d} / x{:d} -> x{:d}({:d})", sc_core::sc_time_stamp().value(),
|
|
||||||
regs->getPC(),
|
|
||||||
rs1, rs2, rd, result);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool M_extension::process_instruction(Instruction &inst) {
|
|
||||||
bool PC_not_affected = true;
|
|
||||||
|
|
||||||
setInstr(inst.getInstr());
|
|
||||||
|
|
||||||
switch (decode()) {
|
|
||||||
case OP_M_MUL:
|
|
||||||
Exec_M_MUL();
|
|
||||||
break;
|
|
||||||
case OP_M_MULH:
|
|
||||||
Exec_M_MULH();
|
|
||||||
break;
|
|
||||||
case OP_M_MULHSU:
|
|
||||||
Exec_M_MULHSU();
|
|
||||||
break;
|
|
||||||
case OP_M_MULHU:
|
|
||||||
Exec_M_MULHU();
|
|
||||||
break;
|
|
||||||
case OP_M_DIV:
|
|
||||||
Exec_M_DIV();
|
|
||||||
break;
|
|
||||||
case OP_M_DIVU:
|
|
||||||
Exec_M_DIVU();
|
|
||||||
break;
|
|
||||||
case OP_M_REM:
|
|
||||||
Exec_M_REM();
|
|
||||||
break;
|
|
||||||
case OP_M_REMU:
|
|
||||||
Exec_M_REMU();
|
|
||||||
break;
|
|
||||||
[[unlikely]] default:
|
|
||||||
std::cout << "M instruction not implemented yet" << "\n";
|
|
||||||
inst.dump();
|
|
||||||
//NOP(inst);
|
|
||||||
sc_core::sc_stop();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return PC_not_affected;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -7,6 +7,8 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#include "MemoryInterface.h"
|
#include "MemoryInterface.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
namespace riscv_tlm {
|
namespace riscv_tlm {
|
||||||
|
|
||||||
|
@ -36,8 +38,11 @@ namespace riscv_tlm {
|
||||||
data_bus->b_transport(trans, delay);
|
data_bus->b_transport(trans, delay);
|
||||||
|
|
||||||
if (trans.is_response_error()) {
|
if (trans.is_response_error()) {
|
||||||
SC_REPORT_ERROR("Memory", "Read memory");
|
std::stringstream error_msg;
|
||||||
|
error_msg << "Read memory: 0x" << std::hex << addr;
|
||||||
|
SC_REPORT_ERROR("Memory", error_msg.str().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,5 +67,11 @@ namespace riscv_tlm {
|
||||||
trans.set_address(addr);
|
trans.set_address(addr);
|
||||||
|
|
||||||
data_bus->b_transport(trans, delay);
|
data_bus->b_transport(trans, delay);
|
||||||
|
|
||||||
|
if (trans.is_response_error()) {
|
||||||
|
std::stringstream error_msg;
|
||||||
|
error_msg << "Write memory: 0x" << std::hex << addr;
|
||||||
|
SC_REPORT_ERROR("Memory", error_msg.str().c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,193 @@
|
||||||
|
/*!
|
||||||
|
\file CPU.cpp
|
||||||
|
\brief Main CPU class
|
||||||
|
\author Màrius Montón
|
||||||
|
\date August 2018
|
||||||
|
*/
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
#include "CPU.h"
|
||||||
|
|
||||||
|
namespace riscv_tlm {
|
||||||
|
|
||||||
|
SC_HAS_PROCESS(RV32);
|
||||||
|
|
||||||
|
RV32::RV32(sc_core::sc_module_name const &name, BaseType PC, bool debug) :
|
||||||
|
CPU(name, debug), INSTR(0) {
|
||||||
|
|
||||||
|
register_bank = new Registers<BaseType>();
|
||||||
|
mem_intf = new MemoryInterface();
|
||||||
|
register_bank->setPC(PC);
|
||||||
|
register_bank->setValue(Registers<BaseType>::sp, (Memory::SIZE / 4) - 1);
|
||||||
|
|
||||||
|
int_cause = 0;
|
||||||
|
|
||||||
|
instr_bus.register_invalidate_direct_mem_ptr(this,
|
||||||
|
&RV32::invalidate_direct_mem_ptr);
|
||||||
|
|
||||||
|
exec = new BASE_ISA<BaseType>(0, register_bank, mem_intf);
|
||||||
|
c_inst = new C_extension<BaseType>(0, register_bank, mem_intf);
|
||||||
|
m_inst = new M_extension<BaseType>(0, register_bank, mem_intf);
|
||||||
|
a_inst = new A_extension<BaseType>(0, register_bank, mem_intf);
|
||||||
|
|
||||||
|
trans.set_data_ptr(reinterpret_cast<unsigned char *>(&INSTR));
|
||||||
|
|
||||||
|
logger->info("Created RV32 CPU");
|
||||||
|
std::cout << "Created RV32 CPU" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
RV32::~RV32() {
|
||||||
|
delete register_bank;
|
||||||
|
delete mem_intf;
|
||||||
|
delete exec;
|
||||||
|
delete c_inst;
|
||||||
|
delete m_inst;
|
||||||
|
delete a_inst;
|
||||||
|
delete m_qk;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RV32::cpu_process_IRQ() {
|
||||||
|
BaseType csr_temp;
|
||||||
|
bool ret_value = false;
|
||||||
|
|
||||||
|
if (interrupt) {
|
||||||
|
csr_temp = register_bank->getCSR(CSR_MSTATUS);
|
||||||
|
if ((csr_temp & MSTATUS_MIE) == 0) {
|
||||||
|
logger->debug("{} ns. PC: 0x{:x}. Interrupt delayed", sc_core::sc_time_stamp().value(),
|
||||||
|
register_bank->getPC());
|
||||||
|
|
||||||
|
return ret_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
csr_temp = register_bank->getCSR(CSR_MIP);
|
||||||
|
|
||||||
|
if ((csr_temp & MIP_MEIP) == 0) {
|
||||||
|
csr_temp |= MIP_MEIP; // MEIP bit in MIP register (11th bit)
|
||||||
|
register_bank->setCSR(CSR_MIP, csr_temp);
|
||||||
|
|
||||||
|
logger->debug("{} ns. PC: 0x{:x}. Interrupt!", sc_core::sc_time_stamp().value(),
|
||||||
|
register_bank->getPC());
|
||||||
|
|
||||||
|
/* updated MEPC register */
|
||||||
|
BaseType old_pc = register_bank->getPC();
|
||||||
|
register_bank->setCSR(CSR_MEPC, old_pc);
|
||||||
|
|
||||||
|
logger->debug("{} ns. PC: 0x{:x}. Old PC Value 0x{:x}", sc_core::sc_time_stamp().value(),
|
||||||
|
register_bank->getPC(),
|
||||||
|
old_pc);
|
||||||
|
|
||||||
|
/* update MCAUSE register */
|
||||||
|
register_bank->setCSR(CSR_MCAUSE, 0x80000000);
|
||||||
|
|
||||||
|
/* set new PC address */
|
||||||
|
BaseType new_pc = register_bank->getCSR(CSR_MTVEC);
|
||||||
|
//new_pc = new_pc & 0xFFFFFFFC; // last two bits always to 0
|
||||||
|
logger->debug("{} ns. PC: 0x{:x}. NEW PC Value 0x{:x}", sc_core::sc_time_stamp().value(),
|
||||||
|
register_bank->getPC(),
|
||||||
|
new_pc);
|
||||||
|
register_bank->setPC(new_pc);
|
||||||
|
|
||||||
|
ret_value = true;
|
||||||
|
interrupt = false;
|
||||||
|
irq_already_down = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!irq_already_down) {
|
||||||
|
csr_temp = register_bank->getCSR(CSR_MIP);
|
||||||
|
csr_temp &= ~MIP_MEIP;
|
||||||
|
register_bank->setCSR(CSR_MIP, csr_temp);
|
||||||
|
irq_already_down = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RV32::CPU_step() {
|
||||||
|
bool PC_not_affected = false;
|
||||||
|
|
||||||
|
/* Get new PC value */
|
||||||
|
if (dmi_ptr_valid) {
|
||||||
|
/* if memory_offset at Memory module is set, this won't work */
|
||||||
|
std::memcpy(&INSTR, dmi_ptr + register_bank->getPC(), 4);
|
||||||
|
} else {
|
||||||
|
sc_core::sc_time delay = sc_core::SC_ZERO_TIME;
|
||||||
|
tlm::tlm_dmi dmi_data;
|
||||||
|
trans.set_address(register_bank->getPC());
|
||||||
|
instr_bus->b_transport(trans, delay);
|
||||||
|
|
||||||
|
if (trans.is_response_error()) {
|
||||||
|
SC_REPORT_ERROR("CPU base", "Read memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trans.is_dmi_allowed()) {
|
||||||
|
dmi_ptr_valid = instr_bus->get_direct_mem_ptr(trans, dmi_data);
|
||||||
|
if (dmi_ptr_valid) {
|
||||||
|
std::cout << "Get DMI_PTR " << std::endl;
|
||||||
|
dmi_ptr = dmi_data.get_dmi_ptr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
perf->codeMemoryRead();
|
||||||
|
inst.setInstr(INSTR);
|
||||||
|
bool breakpoint = false;
|
||||||
|
|
||||||
|
/* check what type of instruction is and execute it */
|
||||||
|
switch (inst.check_extension()) {
|
||||||
|
[[likely]] case BASE_EXTENSION:
|
||||||
|
PC_not_affected = exec->process_instruction(inst, &breakpoint);
|
||||||
|
if (PC_not_affected) {
|
||||||
|
register_bank->incPC();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case C_EXTENSION:
|
||||||
|
PC_not_affected = c_inst->process_instruction(inst, &breakpoint);
|
||||||
|
if (PC_not_affected) {
|
||||||
|
register_bank->incPCby2();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case M_EXTENSION:
|
||||||
|
PC_not_affected = m_inst->process_instruction(inst);
|
||||||
|
if (PC_not_affected) {
|
||||||
|
register_bank->incPC();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case A_EXTENSION:
|
||||||
|
PC_not_affected = a_inst->process_instruction(inst);
|
||||||
|
if (PC_not_affected) {
|
||||||
|
register_bank->incPC();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
[[unlikely]] default:
|
||||||
|
std::cout << "Extension not implemented yet" << std::endl;
|
||||||
|
inst.dump();
|
||||||
|
exec->NOP();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (breakpoint) {
|
||||||
|
std::cout << "Breakpoint set to true\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
perf->instructionsInc();
|
||||||
|
|
||||||
|
return breakpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void RV32::call_interrupt(tlm::tlm_generic_payload &m_trans,
|
||||||
|
sc_core::sc_time &delay) {
|
||||||
|
interrupt = true;
|
||||||
|
/* Socket caller send a cause (its id) */
|
||||||
|
memcpy(&int_cause, m_trans.get_data_ptr(), sizeof(BaseType));
|
||||||
|
delay = sc_core::SC_ZERO_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint64_t RV32::getStartDumpAddress() {
|
||||||
|
return register_bank->getValue(Registers<std::uint32_t>::t0);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint64_t RV32::getEndDumpAddress() {
|
||||||
|
return register_bank->getValue(Registers<std::uint32_t>::t1);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,189 @@
|
||||||
|
/*!
|
||||||
|
\file CPU.cpp
|
||||||
|
\brief Main CPU class
|
||||||
|
\author Màrius Montón
|
||||||
|
\date August 2018
|
||||||
|
*/
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
#include "CPU.h"
|
||||||
|
|
||||||
|
namespace riscv_tlm {
|
||||||
|
|
||||||
|
RV64::RV64(sc_core::sc_module_name const &name, BaseType PC, bool debug) :
|
||||||
|
CPU(name, debug), INSTR(0) {
|
||||||
|
|
||||||
|
register_bank = new Registers<BaseType>();
|
||||||
|
mem_intf = new MemoryInterface();
|
||||||
|
register_bank->setPC(PC);
|
||||||
|
register_bank->setValue(Registers<BaseType>::sp, (Memory::SIZE / 4) - 1);
|
||||||
|
|
||||||
|
int_cause = 0;
|
||||||
|
|
||||||
|
instr_bus.register_invalidate_direct_mem_ptr(this,
|
||||||
|
&RV64::invalidate_direct_mem_ptr);
|
||||||
|
|
||||||
|
exec = new BASE_ISA<BaseType>(0, register_bank, mem_intf);
|
||||||
|
c_inst = new C_extension<BaseType>(0, register_bank, mem_intf);
|
||||||
|
m_inst = new M_extension<BaseType>(0, register_bank, mem_intf);
|
||||||
|
a_inst = new A_extension<BaseType>(0, register_bank, mem_intf);
|
||||||
|
|
||||||
|
trans.set_data_ptr(reinterpret_cast<unsigned char *>(&INSTR));
|
||||||
|
|
||||||
|
logger->info("Created RV64 CPU");
|
||||||
|
std::cout << "Created RV64 CPU" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
RV64::~RV64() {
|
||||||
|
delete register_bank;
|
||||||
|
delete mem_intf;
|
||||||
|
delete exec;
|
||||||
|
delete c_inst;
|
||||||
|
delete m_inst;
|
||||||
|
delete a_inst;
|
||||||
|
delete m_qk;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RV64::cpu_process_IRQ() {
|
||||||
|
BaseType csr_temp;
|
||||||
|
bool ret_value = false;
|
||||||
|
|
||||||
|
if (interrupt) {
|
||||||
|
csr_temp = register_bank->getCSR(CSR_MSTATUS);
|
||||||
|
if ((csr_temp & MSTATUS_MIE) == 0) {
|
||||||
|
logger->debug("{} ns. PC: 0x{:x}. Interrupt delayed", sc_core::sc_time_stamp().value(),
|
||||||
|
register_bank->getPC());
|
||||||
|
|
||||||
|
return ret_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
csr_temp = register_bank->getCSR(CSR_MIP);
|
||||||
|
|
||||||
|
if ((csr_temp & MIP_MEIP) == 0) {
|
||||||
|
csr_temp |= MIP_MEIP; // MEIP bit in MIP register (11th bit)
|
||||||
|
register_bank->setCSR(CSR_MIP, csr_temp);
|
||||||
|
|
||||||
|
logger->debug("{} ns. PC: 0x{:x}. Interrupt!", sc_core::sc_time_stamp().value(),
|
||||||
|
register_bank->getPC());
|
||||||
|
|
||||||
|
/* updated MEPC register */
|
||||||
|
BaseType old_pc = register_bank->getPC();
|
||||||
|
register_bank->setCSR(CSR_MEPC, old_pc);
|
||||||
|
|
||||||
|
logger->debug("{} ns. PC: 0x{:x}. Old PC Value 0x{:x}", sc_core::sc_time_stamp().value(),
|
||||||
|
register_bank->getPC(),
|
||||||
|
old_pc);
|
||||||
|
|
||||||
|
/* update MCAUSE register */
|
||||||
|
register_bank->setCSR(CSR_MCAUSE, 0x80000000);
|
||||||
|
|
||||||
|
/* set new PC address */
|
||||||
|
BaseType new_pc = register_bank->getCSR(CSR_MTVEC);
|
||||||
|
//new_pc = new_pc & 0xFFFFFFFC; // last two bits always to 0
|
||||||
|
logger->debug("{} ns. PC: 0x{:x}. NEW PC Value 0x{:x}", sc_core::sc_time_stamp().value(),
|
||||||
|
register_bank->getPC(),
|
||||||
|
new_pc);
|
||||||
|
register_bank->setPC(new_pc);
|
||||||
|
|
||||||
|
ret_value = true;
|
||||||
|
interrupt = false;
|
||||||
|
irq_already_down = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!irq_already_down) {
|
||||||
|
csr_temp = register_bank->getCSR(CSR_MIP);
|
||||||
|
csr_temp &= ~MIP_MEIP;
|
||||||
|
register_bank->setCSR(CSR_MIP, csr_temp);
|
||||||
|
irq_already_down = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RV64::CPU_step() {
|
||||||
|
bool PC_not_affected = false;
|
||||||
|
|
||||||
|
/* Get new PC value */
|
||||||
|
if (dmi_ptr_valid) {
|
||||||
|
/* if memory_offset at Memory module is set, this won't work */
|
||||||
|
std::memcpy(&INSTR, dmi_ptr + register_bank->getPC(), 4);
|
||||||
|
} else {
|
||||||
|
sc_core::sc_time delay = sc_core::SC_ZERO_TIME;
|
||||||
|
tlm::tlm_dmi dmi_data;
|
||||||
|
trans.set_address(register_bank->getPC());
|
||||||
|
instr_bus->b_transport(trans, delay);
|
||||||
|
|
||||||
|
if (trans.is_response_error()) {
|
||||||
|
SC_REPORT_ERROR("CPU base", "Read memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trans.is_dmi_allowed()) {
|
||||||
|
dmi_ptr_valid = instr_bus->get_direct_mem_ptr(trans, dmi_data);
|
||||||
|
if (dmi_ptr_valid) {
|
||||||
|
std::cout << "Get DMI_PTR " << std::endl;
|
||||||
|
dmi_ptr = dmi_data.get_dmi_ptr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
perf->codeMemoryRead();
|
||||||
|
inst.setInstr(INSTR);
|
||||||
|
bool breakpoint = false;
|
||||||
|
|
||||||
|
/* check what type of instruction is and execute it */
|
||||||
|
switch (inst.check_extension()) {
|
||||||
|
[[likely]] case BASE_EXTENSION:
|
||||||
|
PC_not_affected = exec->process_instruction(inst, &breakpoint);
|
||||||
|
if (PC_not_affected) {
|
||||||
|
register_bank->incPC();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case C_EXTENSION:
|
||||||
|
PC_not_affected = c_inst->process_instruction(inst, &breakpoint);
|
||||||
|
if (PC_not_affected) {
|
||||||
|
register_bank->incPCby2();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case M_EXTENSION:
|
||||||
|
PC_not_affected = m_inst->process_instruction(inst);
|
||||||
|
if (PC_not_affected) {
|
||||||
|
register_bank->incPC();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case A_EXTENSION:
|
||||||
|
PC_not_affected = a_inst->process_instruction(inst);
|
||||||
|
if (PC_not_affected) {
|
||||||
|
register_bank->incPC();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
[[unlikely]] default:
|
||||||
|
std::cout << "Extension not implemented yet" << std::endl;
|
||||||
|
inst.dump();
|
||||||
|
exec->NOP();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (breakpoint) {
|
||||||
|
std::cout << "Breakpoint set to true\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
perf->instructionsInc();
|
||||||
|
|
||||||
|
return breakpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RV64::call_interrupt(tlm::tlm_generic_payload &m_trans,
|
||||||
|
sc_core::sc_time &delay) {
|
||||||
|
interrupt = true;
|
||||||
|
/* Socket caller send a cause (its id) */
|
||||||
|
memcpy(&int_cause, m_trans.get_data_ptr(), sizeof(BaseType));
|
||||||
|
delay = sc_core::SC_ZERO_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint64_t RV64::getStartDumpAddress() {
|
||||||
|
return register_bank->getValue(Registers<std::uint32_t>::t0);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint64_t RV64::getEndDumpAddress() {
|
||||||
|
return register_bank->getValue(Registers<std::uint32_t>::t1);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,16 +9,16 @@
|
||||||
#include "Registers.h"
|
#include "Registers.h"
|
||||||
|
|
||||||
namespace riscv_tlm {
|
namespace riscv_tlm {
|
||||||
|
/* Specialization for each XLEN (RV32, RV64)*/
|
||||||
Registers::Registers() {
|
template<>
|
||||||
perf = Performance::getInstance();
|
void Registers<std::uint32_t>::initCSR() {
|
||||||
|
CSR[CSR_MISA] = MISA_MXL | MISA_M_EXTENSION | MISA_C_EXTENSION
|
||||||
initCSR();
|
| MISA_A_EXTENSION | MISA_I_BASE;
|
||||||
register_bank[sp] = Memory::SIZE - 4; // default stack at the end of the memory
|
CSR[CSR_MSTATUS] = MISA_MXL;
|
||||||
register_PC = 0x80000000; // default _start address
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Registers::dump() {
|
template<>
|
||||||
|
void Registers<std::uint32_t>::dump() const {
|
||||||
std::cout << "************************************" << std::endl;
|
std::cout << "************************************" << std::endl;
|
||||||
std::cout << "Registers dump" << std::dec << std::endl;
|
std::cout << "Registers dump" << std::dec << std::endl;
|
||||||
std::cout << std::setfill('0') << std::uppercase;
|
std::cout << std::setfill('0') << std::uppercase;
|
||||||
|
@ -98,79 +98,91 @@ namespace riscv_tlm {
|
||||||
std::cout << "************************************" << std::endl;
|
std::cout << "************************************" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Registers::setValue(int reg_num, std::int32_t value) {
|
template<>
|
||||||
if ((reg_num != 0) && (reg_num < 32)) {
|
void Registers<std::uint64_t>::initCSR() {
|
||||||
register_bank[reg_num] = value;
|
CSR[CSR_MISA] = (((std::uint64_t) 0x02) << 30) | MISA_M_EXTENSION | MISA_C_EXTENSION
|
||||||
perf->registerWrite();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t Registers::getValue(int reg_num) const {
|
|
||||||
if ((reg_num >= 0) && (reg_num < 32)) {
|
|
||||||
perf->registerRead();
|
|
||||||
return register_bank[reg_num];
|
|
||||||
} else {
|
|
||||||
return static_cast<std::int32_t>(0xFFFFFFFF);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t Registers::getPC() const {
|
|
||||||
return register_PC;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Registers::setPC(std::uint32_t new_pc) {
|
|
||||||
register_PC = new_pc;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t Registers::getCSR(const int csr) {
|
|
||||||
std::uint32_t ret_value;
|
|
||||||
|
|
||||||
switch (csr) {
|
|
||||||
case CSR_CYCLE:
|
|
||||||
case CSR_MCYCLE:
|
|
||||||
ret_value = static_cast<std::uint64_t>(sc_core::sc_time(
|
|
||||||
sc_core::sc_time_stamp()
|
|
||||||
- sc_core::sc_time(sc_core::SC_ZERO_TIME)).to_double())
|
|
||||||
& 0x00000000FFFFFFFF;
|
|
||||||
break;
|
|
||||||
case CSR_CYCLEH:
|
|
||||||
case CSR_MCYCLEH:
|
|
||||||
ret_value = static_cast<std::uint32_t>((std::uint64_t) (sc_core::sc_time(
|
|
||||||
sc_core::sc_time_stamp()
|
|
||||||
- sc_core::sc_time(sc_core::SC_ZERO_TIME)).to_double())
|
|
||||||
>> 32 & 0x00000000FFFFFFFF);
|
|
||||||
break;
|
|
||||||
case CSR_TIME:
|
|
||||||
ret_value = static_cast<std::uint64_t>(sc_core::sc_time(
|
|
||||||
sc_core::sc_time_stamp()
|
|
||||||
- sc_core::sc_time(sc_core::SC_ZERO_TIME)).to_double())
|
|
||||||
& 0x00000000FFFFFFFF;
|
|
||||||
break;
|
|
||||||
case CSR_TIMEH:
|
|
||||||
ret_value = static_cast<std::uint32_t>((std::uint64_t) (sc_core::sc_time(
|
|
||||||
sc_core::sc_time_stamp()
|
|
||||||
- sc_core::sc_time(sc_core::SC_ZERO_TIME)).to_double())
|
|
||||||
>> 32 & 0x00000000FFFFFFFF);
|
|
||||||
break;
|
|
||||||
[[likely]] default:
|
|
||||||
ret_value = CSR[csr];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return ret_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Registers::setCSR(int csr, std::uint32_t value) {
|
|
||||||
/* @FIXME: rv32mi-p-ma_fetch tests doesn't allow MISA to be writable,
|
|
||||||
* but Volume II: Privileged Architecture v1.10 says MISA is writable (?)
|
|
||||||
*/
|
|
||||||
if (csr != CSR_MISA) {
|
|
||||||
CSR[csr] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Registers::initCSR() {
|
|
||||||
CSR[CSR_MISA] = MISA_MXL | MISA_M_EXTENSION | MISA_C_EXTENSION
|
|
||||||
| MISA_A_EXTENSION | MISA_I_BASE;
|
| MISA_A_EXTENSION | MISA_I_BASE;
|
||||||
CSR[CSR_MSTATUS] = MISA_MXL;
|
CSR[CSR_MSTATUS] = MISA_MXL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void Registers<std::uint64_t>::dump() const {
|
||||||
|
std::cout << "************************************" << std::endl;
|
||||||
|
std::cout << "Registers dump" << std::dec << std::endl;
|
||||||
|
std::cout << std::setfill('0') << std::uppercase;
|
||||||
|
std::cout << "x0 (zero): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[0];
|
||||||
|
std::cout << " x1 (ra): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[1];
|
||||||
|
std::cout << " x2 (sp): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[2];
|
||||||
|
std::cout << " x3 (gp): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[3] << std::endl;
|
||||||
|
|
||||||
|
std::cout << "x4 (tp): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[4];
|
||||||
|
std::cout << " x5 (t0): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[5];
|
||||||
|
std::cout << " x6 (t1): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[6];
|
||||||
|
std::cout << " x7 (t2): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[7] << std::endl;
|
||||||
|
|
||||||
|
std::cout << "x8 (s0/fp): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[8];
|
||||||
|
std::cout << " x9 (s1): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[9];
|
||||||
|
std::cout << " x10 (a0): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[10];
|
||||||
|
std::cout << " x11 (a1): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[11] << std::endl;
|
||||||
|
|
||||||
|
std::cout << "x12 (a2): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[12];
|
||||||
|
std::cout << " x13 (a3): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[13];
|
||||||
|
std::cout << " x14 (a4): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[14];
|
||||||
|
std::cout << " x15 (a5): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[15] << std::endl;
|
||||||
|
|
||||||
|
std::cout << "x16 (a6): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[16];
|
||||||
|
std::cout << " x17 (a7): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[17];
|
||||||
|
std::cout << " x18 (s2): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[18];
|
||||||
|
std::cout << " x19 (s3): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[19] << std::endl;
|
||||||
|
|
||||||
|
std::cout << "x20 (s4): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[20];
|
||||||
|
std::cout << " x21 (s5): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[21];
|
||||||
|
std::cout << " x22 (s6): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[22];
|
||||||
|
std::cout << " x23 (s7): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[23] << std::endl;
|
||||||
|
|
||||||
|
std::cout << "x24 (s8): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[24];
|
||||||
|
std::cout << " x25 (s9): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[25];
|
||||||
|
std::cout << " x26 (s10): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[26];
|
||||||
|
std::cout << " x27 (s11): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[27] << std::endl;
|
||||||
|
|
||||||
|
std::cout << "x28 (t3): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[28];
|
||||||
|
std::cout << " x29 (t4): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[29];
|
||||||
|
std::cout << " x30 (t5): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[30];
|
||||||
|
std::cout << " x31 (t6): 0x" << std::right << std::setw(16)
|
||||||
|
<< std::hex << register_bank[31] << std::endl;
|
||||||
|
|
||||||
|
std::cout << "PC: 0x" << std::setw(16) << std::hex << register_PC << std::dec << std::endl;
|
||||||
|
std::cout << "************************************" << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -15,7 +15,6 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include "CPU.h"
|
#include "CPU.h"
|
||||||
#include "Memory.h"
|
|
||||||
#include "BusCtrl.h"
|
#include "BusCtrl.h"
|
||||||
#include "Trace.h"
|
#include "Trace.h"
|
||||||
#include "Timer.h"
|
#include "Timer.h"
|
||||||
|
@ -24,12 +23,16 @@
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
#include "spdlog/sinks/basic_file_sink.h"
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
|
|
||||||
|
typedef enum {RV32, RV64} cpu_types_t;
|
||||||
|
|
||||||
std::string filename;
|
std::string filename;
|
||||||
bool debug_session = false;
|
bool debug_session = false;
|
||||||
bool mem_dump = false;
|
bool mem_dump = false;
|
||||||
uint32_t dump_addr_st = 0;
|
uint32_t dump_addr_st = 0;
|
||||||
uint32_t dump_addr_end = 0;
|
uint32_t dump_addr_end = 0;
|
||||||
|
|
||||||
|
cpu_types_t cpu_type_opt = RV32;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class Simulator
|
* @class Simulator
|
||||||
* This class instantiates all necessary modules, connects its ports and starts
|
* This class instantiates all necessary modules, connects its ports and starts
|
||||||
|
@ -45,13 +48,19 @@ public:
|
||||||
riscv_tlm::peripherals::Trace *trace;
|
riscv_tlm::peripherals::Trace *trace;
|
||||||
riscv_tlm::peripherals::Timer *timer;
|
riscv_tlm::peripherals::Timer *timer;
|
||||||
|
|
||||||
explicit Simulator(sc_core::sc_module_name const &name): sc_module(name) {
|
explicit Simulator(sc_core::sc_module_name const &name, cpu_types_t cpu_type_m): sc_module(name) {
|
||||||
std::uint32_t start_PC;
|
std::uint32_t start_PC;
|
||||||
|
|
||||||
MainMemory = new riscv_tlm::Memory("Main_Memory", filename);
|
MainMemory = new riscv_tlm::Memory("Main_Memory", filename);
|
||||||
start_PC = MainMemory->getPCfromHEX();
|
start_PC = MainMemory->getPCfromHEX();
|
||||||
|
|
||||||
cpu = new riscv_tlm::CPU("cpu", start_PC, debug_session);
|
cpu_type = cpu_type_m;
|
||||||
|
|
||||||
|
if (cpu_type == RV32) {
|
||||||
|
cpu = new riscv_tlm::RV32("cpu", start_PC, debug_session);
|
||||||
|
} else {
|
||||||
|
cpu = new riscv_tlm::RV64("cpu", start_PC, debug_session);
|
||||||
|
}
|
||||||
|
|
||||||
Bus = new riscv_tlm::BusCtrl("BusCtrl");
|
Bus = new riscv_tlm::BusCtrl("BusCtrl");
|
||||||
trace = new riscv_tlm::peripherals::Trace("Trace");
|
trace = new riscv_tlm::peripherals::Trace("Trace");
|
||||||
|
@ -67,7 +76,7 @@ public:
|
||||||
timer->irq_line.bind(cpu->irq_line_socket);
|
timer->irq_line.bind(cpu->irq_line_socket);
|
||||||
|
|
||||||
if (debug_session) {
|
if (debug_session) {
|
||||||
riscv_tlm::Debug debug(cpu, MainMemory);
|
//riscv_tlm::Debug debug(cpu, MainMemory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,10 +92,19 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void MemoryDump() {
|
void MemoryDump() const {
|
||||||
std::cout << "********** MEMORY DUMP ***********\n";
|
std::cout << "********** MEMORY DUMP ***********\n";
|
||||||
|
|
||||||
|
if (dump_addr_st == 0) {
|
||||||
|
dump_addr_st = cpu->getStartDumpAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dump_addr_end == 0) {
|
||||||
|
dump_addr_end = cpu->getEndDumpAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "from 0x" << std::hex << dump_addr_st << " to 0x" << dump_addr_end << "\n";
|
||||||
tlm::tlm_generic_payload trans;
|
tlm::tlm_generic_payload trans;
|
||||||
tlm::tlm_dmi dmi_data;
|
|
||||||
sc_core::sc_time delay;
|
sc_core::sc_time delay;
|
||||||
std::uint32_t data[4];
|
std::uint32_t data[4];
|
||||||
|
|
||||||
|
@ -116,6 +134,9 @@ private:
|
||||||
|
|
||||||
signature_file.close();
|
signature_file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
cpu_types_t cpu_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
Simulator *top;
|
Simulator *top;
|
||||||
|
@ -131,11 +152,12 @@ void intHandler(int dummy) {
|
||||||
void process_arguments(int argc, char *argv[]) {
|
void process_arguments(int argc, char *argv[]) {
|
||||||
|
|
||||||
int c;
|
int c;
|
||||||
int debug_level;
|
long int debug_level;
|
||||||
|
|
||||||
debug_session = false;
|
debug_session = false;
|
||||||
|
cpu_type_opt = RV32;
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "DTE:B:L:f:?")) != -1) {
|
while ((c = getopt(argc, argv, "DTE:B:L:f:R:?")) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'D':
|
case 'D':
|
||||||
debug_session = true;
|
debug_session = true;
|
||||||
|
@ -144,13 +166,13 @@ void process_arguments(int argc, char *argv[]) {
|
||||||
mem_dump = true;
|
mem_dump = true;
|
||||||
break;
|
break;
|
||||||
case 'B':
|
case 'B':
|
||||||
dump_addr_st = std::strtoul (optarg, 0, 16);
|
dump_addr_st = std::strtoul (optarg, nullptr, 16);
|
||||||
break;
|
break;
|
||||||
case 'E':
|
case 'E':
|
||||||
dump_addr_end = std::strtoul(optarg, 0, 16);
|
dump_addr_end = std::strtoul(optarg, nullptr, 16);
|
||||||
break;
|
break;
|
||||||
case 'L':
|
case 'L':
|
||||||
debug_level = std::atoi(optarg);
|
debug_level = std::strtol(optarg, nullptr, 10);
|
||||||
|
|
||||||
switch (debug_level) {
|
switch (debug_level) {
|
||||||
case 3:
|
case 3:
|
||||||
|
@ -173,6 +195,13 @@ void process_arguments(int argc, char *argv[]) {
|
||||||
case 'f':
|
case 'f':
|
||||||
filename = std::string(optarg);
|
filename = std::string(optarg);
|
||||||
break;
|
break;
|
||||||
|
case 'R':
|
||||||
|
if (strcmp(optarg, "32") == 0) {
|
||||||
|
cpu_type_opt = RV32;
|
||||||
|
} else {
|
||||||
|
cpu_type_opt = RV64;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case '?':
|
case '?':
|
||||||
std::cout << "Call ./RISCV_TLM -D -L <debuglevel> (0..3) filename.hex"
|
std::cout << "Call ./RISCV_TLM -D -L <debuglevel> (0..3) filename.hex"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
@ -199,15 +228,15 @@ int sc_main(int argc, char *argv[]) {
|
||||||
/* SystemC time resolution set to 1 ns*/
|
/* SystemC time resolution set to 1 ns*/
|
||||||
sc_core::sc_set_time_resolution(1, sc_core::SC_NS);
|
sc_core::sc_set_time_resolution(1, sc_core::SC_NS);
|
||||||
|
|
||||||
spdlog::filename_t filename = SPDLOG_FILENAME_T("newlog.txt");
|
spdlog::filename_t log_filename = SPDLOG_FILENAME_T("newlog.txt");
|
||||||
logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("my_logger", filename);
|
logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("my_logger", log_filename, true);
|
||||||
logger->set_pattern("%v");
|
logger->set_pattern("%v");
|
||||||
logger->set_level(spdlog::level::info);
|
logger->set_level(spdlog::level::info);
|
||||||
|
|
||||||
/* Parse and process program arguments. -f is mandatory */
|
/* Parse and process program arguments. -f is mandatory */
|
||||||
process_arguments(argc, argv);
|
process_arguments(argc, argv);
|
||||||
|
|
||||||
top = new Simulator("top");
|
top = new Simulator("top", cpu_type_opt);
|
||||||
|
|
||||||
auto start = std::chrono::steady_clock::now();
|
auto start = std::chrono::steady_clock::now();
|
||||||
sc_core::sc_start();
|
sc_core::sc_start();
|
||||||
|
@ -219,7 +248,8 @@ int sc_main(int argc, char *argv[]) {
|
||||||
std::cout << "Total elapsed time: " << elapsed_seconds.count() << "s" << std::endl;
|
std::cout << "Total elapsed time: " << elapsed_seconds.count() << "s" << std::endl;
|
||||||
std::cout << "Simulated " << int(std::round(instructions)) << " instr/sec" << std::endl;
|
std::cout << "Simulated " << int(std::round(instructions)) << " instr/sec" << std::endl;
|
||||||
|
|
||||||
if (!mem_dump) {
|
if (!mem_dump)
|
||||||
|
{
|
||||||
std::cout << "Press Enter to finish" << std::endl;
|
std::cout << "Press Enter to finish" << std::endl;
|
||||||
std::cin.ignore();
|
std::cin.ignore();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,57 +7,3 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#include "extension_base.h"
|
#include "extension_base.h"
|
||||||
|
|
||||||
namespace riscv_tlm {
|
|
||||||
|
|
||||||
extension_base::extension_base(const sc_dt::sc_uint<32> &instr,
|
|
||||||
Registers *register_bank, MemoryInterface *mem_interface) :
|
|
||||||
m_instr(instr), regs(register_bank), mem_intf(mem_interface) {
|
|
||||||
|
|
||||||
perf = Performance::getInstance();
|
|
||||||
logger = spdlog::get("my_logger");
|
|
||||||
}
|
|
||||||
|
|
||||||
extension_base::~extension_base() = default;
|
|
||||||
|
|
||||||
void extension_base::setInstr(std::uint32_t p_instr) {
|
|
||||||
m_instr = sc_dt::sc_uint<32>(p_instr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void extension_base::dump() const {
|
|
||||||
std::cout << std::hex << "0x" << m_instr << std::dec << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void extension_base::RaiseException(std::uint32_t cause, std::uint32_t inst) {
|
|
||||||
std::uint32_t new_pc, current_pc, m_cause;
|
|
||||||
|
|
||||||
current_pc = regs->getPC();
|
|
||||||
m_cause = regs->getCSR(CSR_MSTATUS);
|
|
||||||
m_cause |= cause;
|
|
||||||
|
|
||||||
new_pc = regs->getCSR(CSR_MTVEC);
|
|
||||||
|
|
||||||
regs->setCSR(CSR_MEPC, current_pc);
|
|
||||||
|
|
||||||
if (cause == EXCEPTION_CAUSE_ILLEGAL_INSTRUCTION) {
|
|
||||||
regs->setCSR(CSR_MTVAL, inst);
|
|
||||||
} else {
|
|
||||||
regs->setCSR(CSR_MTVAL, current_pc);
|
|
||||||
}
|
|
||||||
|
|
||||||
regs->setCSR(CSR_MCAUSE, cause);
|
|
||||||
regs->setCSR(CSR_MSTATUS, m_cause);
|
|
||||||
|
|
||||||
regs->setPC(new_pc);
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. Exception! new PC 0x{:x} ", sc_core::sc_time_stamp().value(), regs->getPC(),
|
|
||||||
new_pc);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool extension_base::NOP() {
|
|
||||||
|
|
||||||
logger->debug("{} ns. PC: 0x{:x}. NOP! new PC 0x{:x} ", sc_core::sc_time_stamp().value(), regs->getPC());
|
|
||||||
sc_core::sc_stop();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue