Compare commits
16 Commits
71f3fbc8b5
...
master
Author | SHA1 | Date | |
---|---|---|---|
efbf73f6b5 | |||
9dd57071ce | |||
c8fbd9f4da | |||
3e110a82f4 | |||
de202e25b9 | |||
e9cc295470 | |||
b5cf188c0a | |||
923e9d39a0 | |||
0983be511c | |||
c423e6a2aa | |||
d6af840ed1 | |||
6330104873 | |||
2d33e50074 | |||
cf8a1de199 | |||
b57739fe38 | |||
07f683dc41 |
9
LICENSE
Normal file
9
LICENSE
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright 2023 Valentin HAUDIQUET
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
46
Makefile
46
Makefile
@@ -4,11 +4,14 @@ CFLAGS=-O3 -Wall -I src
|
|||||||
LDFLAGS=-lpthread
|
LDFLAGS=-lpthread
|
||||||
BUILD_DIR=build
|
BUILD_DIR=build
|
||||||
|
|
||||||
|
# Risc-V toolchain
|
||||||
|
RV_LINUX_CCPREFIX=riscv32-unknown-linux-gnu-
|
||||||
|
|
||||||
C_FILES := $(shell find src/ -name '*.c')
|
C_FILES := $(shell find src/ -name '*.c')
|
||||||
|
|
||||||
all: $(BUILD_DIR)/$(NAME)
|
all: $(BUILD_DIR)/$(NAME)
|
||||||
|
|
||||||
# Top-level targets
|
# Top-level target : vriscv
|
||||||
$(BUILD_DIR)/$(NAME): $(C_FILES) | $(BUILD_DIR)
|
$(BUILD_DIR)/$(NAME): $(C_FILES) | $(BUILD_DIR)
|
||||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
@@ -16,16 +19,51 @@ $(BUILD_DIR)/$(NAME): $(C_FILES) | $(BUILD_DIR)
|
|||||||
$(BUILD_DIR):
|
$(BUILD_DIR):
|
||||||
mkdir -p $(BUILD_DIR)
|
mkdir -p $(BUILD_DIR)
|
||||||
|
|
||||||
# Phony targets
|
# Clean : clean built executable
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
|
rm -rf $(BUILD_DIR)/$(NAME)
|
||||||
|
|
||||||
|
# Distclean : clean build directory
|
||||||
|
.PHONY: distclean
|
||||||
|
distclean:
|
||||||
rm -rf $(BUILD_DIR)
|
rm -rf $(BUILD_DIR)
|
||||||
|
|
||||||
|
rungdb: all
|
||||||
|
echo $(shell objdump -h ../riscv-pk/build/bbl | grep .payload | awk '{print $4}')
|
||||||
|
|
||||||
|
# Linux and bootloader, for running linux
|
||||||
|
$(BUILD_DIR)/linux:
|
||||||
|
cd $(BUILD_DIR) && git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git --depth 1 -b v6.6
|
||||||
|
|
||||||
|
$(BUILD_DIR)/linux/.config: hardware/linux.config | $(BUILD_DIR)/linux
|
||||||
|
cp hardware/linux.config $(BUILD_DIR)/linux/.config
|
||||||
|
|
||||||
|
$(BUILD_DIR)/linux/vmlinux: $(BUILD_DIR)/linux/.config | $(BUILD_DIR)/linux
|
||||||
|
cd $(BUILD_DIR)/linux/ && make ARCH=riscv CROSS_COMPILE=$(RV_LINUX_CCPREFIX) -j7 vmlinux
|
||||||
|
|
||||||
|
$(BUILD_DIR)/riscv-pk:
|
||||||
|
cd $(BUILD_DIR) && git clone https://github.com/riscv-software-src/riscv-pk --depth 1
|
||||||
|
|
||||||
|
$(BUILD_DIR)/riscv-pk/build: | $(BUILD_DIR)/riscv-pk
|
||||||
|
mkdir $(BUILD_DIR)/riscv-pk/build
|
||||||
|
|
||||||
|
$(BUILD_DIR)/riscv-pk/build/bbl: $(BUILD_DIR)/linux/vmlinux hardware/vriscv.dts | $(BUILD_DIR)/riscv-pk/build
|
||||||
|
cd $(BUILD_DIR)/riscv-pk/build && ../configure \
|
||||||
|
--prefix=$(CURDIR)/$(BUILD_DIR)/riscv-pk/build/prefix \
|
||||||
|
--host=riscv32-unknown-elf \
|
||||||
|
--with-arch=rv32ima_zicsr_zifencei --with-abi=ilp32 \
|
||||||
|
--with-dts=../../../hardware/vriscv.dts \
|
||||||
|
--with-payload=../../linux/vmlinux
|
||||||
|
cd $(BUILD_DIR)/riscv-pk/build && make && make install
|
||||||
|
|
||||||
|
# Run : run linux on the emulator
|
||||||
.PHONY: run
|
.PHONY: run
|
||||||
.SILENT: run
|
.SILENT: run
|
||||||
run: all
|
run: all $(BUILD_DIR)/riscv-pk/build/bbl
|
||||||
./$(BUILD_DIR)/$(NAME)
|
./$(BUILD_DIR)/$(NAME) -m4096 $(BUILD_DIR)/riscv-pk/build/bbl
|
||||||
|
|
||||||
|
# Test : all the tests
|
||||||
.PHONY: tests
|
.PHONY: tests
|
||||||
.SILENT: tests
|
.SILENT: tests
|
||||||
tests: all
|
tests: all
|
||||||
|
@@ -1,5 +1,10 @@
|
|||||||
# vriscv - a risc-v simulator
|
# vriscv - a risc-v simulator
|
||||||
|
|
||||||
|
Linux and the BBL bootloader can be downloaded, built, and ran on the simulator using:
|
||||||
|
```
|
||||||
|
make run
|
||||||
|
```
|
||||||
|
|
||||||
## Unit tests
|
## Unit tests
|
||||||
|
|
||||||
Unit tests can be compiled and run using :
|
Unit tests can be compiled and run using :
|
||||||
@@ -22,6 +27,3 @@ Juraj's Blog, mostly:
|
|||||||
|
|
||||||
RISC-V SBI Specifications:
|
RISC-V SBI Specifications:
|
||||||
- https://github.com/riscv-non-isa/riscv-sbi-doc/releases
|
- https://github.com/riscv-non-isa/riscv-sbi-doc/releases
|
||||||
|
|
||||||
Buildroot fork for nommu linux:
|
|
||||||
- https://github.com/regymm/buildroot
|
|
||||||
|
2038
hardware/linux.config
Normal file
2038
hardware/linux.config
Normal file
File diff suppressed because it is too large
Load Diff
74
hardware/vriscv.dts
Normal file
74
hardware/vriscv.dts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/dts-v1/;
|
||||||
|
/ {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
compatible = "riscv-virtio";
|
||||||
|
model = "riscv-virtio,qemu";
|
||||||
|
|
||||||
|
chosen {
|
||||||
|
bootargs = "debug keep_bootcon earlycon=sbi console=sbi";
|
||||||
|
stdout-path = "/uart0@3000000";
|
||||||
|
};
|
||||||
|
|
||||||
|
cpus {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
timebase-frequency = <10000000>;
|
||||||
|
|
||||||
|
cpu0: cpu@0 {
|
||||||
|
device_type = "cpu";
|
||||||
|
reg = <0>;
|
||||||
|
compatible = "riscv";
|
||||||
|
riscv,isa = "riscv,sv32";
|
||||||
|
clock-frequency = <10000000>;
|
||||||
|
cpu0_intc: interrupt-controller {
|
||||||
|
#interrupt-cells = <1>;
|
||||||
|
compatible = "riscv,cpu-intc";
|
||||||
|
interrupt-controller;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
ram: memory@0 {
|
||||||
|
device_type = "memory";
|
||||||
|
reg = <0x0 0xFFFFFFFF>;
|
||||||
|
};
|
||||||
|
|
||||||
|
soc {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
compatible = "simple-bus";
|
||||||
|
ranges;
|
||||||
|
|
||||||
|
clint0: clint@2000000 {
|
||||||
|
#interrupt-cells = <1>;
|
||||||
|
compatible = "riscv,clint0";
|
||||||
|
reg = <0x2000000 0xC000>;
|
||||||
|
interrupts-extended = <&cpu0_intc 3 &cpu0_intc 7>;
|
||||||
|
};
|
||||||
|
|
||||||
|
// /* FIXME: This is probably not correct for now */
|
||||||
|
plic0: interrupt-controller@c000000 {
|
||||||
|
#interrupt-cells = <1>;
|
||||||
|
interrupt-controller;
|
||||||
|
compatible = "riscv,plic0";
|
||||||
|
reg = <0xC000000 0x4000000>;
|
||||||
|
interrupts-extended = <&cpu0_intc 9>, <&cpu0_intc 11>;
|
||||||
|
riscv,ndev = <1>;
|
||||||
|
riscv,max-priority = <7>;
|
||||||
|
};
|
||||||
|
|
||||||
|
// uart0: serial@3000000 {
|
||||||
|
// interrupts = <0xa>;
|
||||||
|
// interrupt-parent = <&plic0>;
|
||||||
|
// clock-frequency = <0x384000>;
|
||||||
|
// reg = <0x3000000 0x1>;
|
||||||
|
// compatible = "simple-uart";
|
||||||
|
// };
|
||||||
|
};
|
||||||
|
uart0: serial@3000000 {
|
||||||
|
clock-frequency = <0x384000>;
|
||||||
|
reg = <0x3000000 0x1>;
|
||||||
|
compatible = "sifive,uart0";
|
||||||
|
};
|
||||||
|
};
|
@@ -7,7 +7,12 @@ uint32_t csr_read(struct RV32_CPU* cpu, uint32_t csr)
|
|||||||
{
|
{
|
||||||
case CSR_CYCLE:
|
case CSR_CYCLE:
|
||||||
return cpu->sim_ticks_done;
|
return cpu->sim_ticks_done;
|
||||||
break;
|
case CSR_SSTATUS:
|
||||||
|
return csr_read(cpu, CSR_MSTATUS);
|
||||||
|
case CSR_SIE:
|
||||||
|
return csr_read(cpu, CSR_MIE);
|
||||||
|
case CSR_SIP:
|
||||||
|
return csr_read(cpu, CSR_MIP);
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -17,5 +22,19 @@ uint32_t csr_read(struct RV32_CPU* cpu, uint32_t csr)
|
|||||||
|
|
||||||
void csr_write(struct RV32_CPU* cpu, uint32_t csr, uint32_t value)
|
void csr_write(struct RV32_CPU* cpu, uint32_t csr, uint32_t value)
|
||||||
{
|
{
|
||||||
|
switch(csr)
|
||||||
|
{
|
||||||
|
case CSR_SSTATUS:
|
||||||
|
csr_write(cpu, CSR_MSTATUS, value);
|
||||||
|
return;
|
||||||
|
case CSR_SIE:
|
||||||
|
csr_write(cpu, CSR_MIE, value);
|
||||||
|
return;
|
||||||
|
case CSR_SIP:
|
||||||
|
csr_write(cpu, CSR_MIP, value);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
cpu->csr[csr] = value;
|
cpu->csr[csr] = value;
|
||||||
}
|
}
|
||||||
|
@@ -61,4 +61,40 @@
|
|||||||
/* Machine Memory Protection */
|
/* Machine Memory Protection */
|
||||||
#define CSR_PMPCFG0 0x3A0
|
#define CSR_PMPCFG0 0x3A0
|
||||||
|
|
||||||
|
// CSR STATUS
|
||||||
|
// SIE: Supervisor Interrupt Enable
|
||||||
|
#define STATUS_SIE 0x2
|
||||||
|
// MIE: Machine Interrupt Enable
|
||||||
|
#define STATUS_MIE 0x8
|
||||||
|
// SPIE: Supervisor Previous Interrupt Enable
|
||||||
|
#define STATUS_SPIE 0x20
|
||||||
|
// MPIE: Machine Previous Interrupt Enable
|
||||||
|
#define STATUS_MPIE 0x80
|
||||||
|
// UBE : User Big Endian (always 0 for us, we are little endian)
|
||||||
|
#define STATUS_UBE 0x40
|
||||||
|
// SPP : Supervisor Previous Privilege
|
||||||
|
#define STATUS_SPP 0x100
|
||||||
|
// VS (2bits) : for extensions
|
||||||
|
#define STATUS_VS 0x600
|
||||||
|
// MPP (2bits) : Machine Previous Privilege
|
||||||
|
#define STATUS_MPP 0x1800
|
||||||
|
// FS (2bits) : for extensions
|
||||||
|
#define STATUS_FS 0x6000
|
||||||
|
// XS (2bits) : for extensions
|
||||||
|
#define STATUS_XS 0x18000
|
||||||
|
// MPRV : Modify PRiVilege
|
||||||
|
#define STATUS_MPRV 0x20000
|
||||||
|
// SUM : permit Supervisor User Memory access
|
||||||
|
#define STATUS_SUM 0x40000
|
||||||
|
// MXR : Make eXecutable Readable
|
||||||
|
#define STATUS_MXR 0x80000
|
||||||
|
// TVM : Trap Virtual Memory (virtualization support)
|
||||||
|
#define STATUS_TVM 0x100000
|
||||||
|
// TW : Timeout Wait (virtualization support)
|
||||||
|
#define STATUS_TW 0x200000
|
||||||
|
// TSR : Trap SRET (virtualization support)
|
||||||
|
#define STATUS_TSR 0x400000
|
||||||
|
// SD : for extensions
|
||||||
|
#define STATUS_SD 0x80000000
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
#include "exception.h"
|
#include "exception.h"
|
||||||
|
#include "vriscv.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
void exception_trigger(rv32_cpu_t* cpu, uint32_t scause, uint32_t tval)
|
__attribute__((noreturn)) void exception_trigger(rv32_cpu_t* cpu, uint32_t scause, uint32_t tval)
|
||||||
{
|
{
|
||||||
// An exception can only be triggered by the CPU itself,
|
// An exception can only be triggered by the CPU itself,
|
||||||
// so we know we already own the mutex
|
// so we know we already own the mutex
|
||||||
@@ -14,36 +15,55 @@ void exception_trigger(rv32_cpu_t* cpu, uint32_t scause, uint32_t tval)
|
|||||||
|
|
||||||
// Exceptions cannot be disabled
|
// Exceptions cannot be disabled
|
||||||
|
|
||||||
// Set xCAUSE : exception cause, with interrupt bit set to null
|
// TODO : Check medeleg to see if we should handle exception in S mode or M mode
|
||||||
cpu->csr[CSR_SCAUSE] = scause;
|
|
||||||
|
|
||||||
// Save previous interrupt enable in xSTATUS.xPIE
|
|
||||||
if(cpu->csr[CSR_SSTATUS] & 0b10U)
|
|
||||||
cpu->csr[CSR_SSTATUS] |= 0x80;
|
|
||||||
|
|
||||||
// Set previous privilege mode in xSTATUS.xPP
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
// Unset SIE (interrupt enable) bit
|
// Unset SIE (interrupt enable) bit
|
||||||
cpu->csr[CSR_SSTATUS] &= ~0b10U;
|
csr_write(cpu, CSR_SSTATUS, csr_read(cpu, CSR_SSTATUS) & (~STATUS_SIE));
|
||||||
|
|
||||||
|
// Set xCAUSE : exception cause, with interrupt bit set to null
|
||||||
|
csr_write(cpu, CSR_SCAUSE, scause);
|
||||||
|
if(gdbstub && scause == SCAUSE_BREAKPOINT)
|
||||||
|
{
|
||||||
|
cpu->sim_ticks_left = 0;
|
||||||
|
// No simulation ticks left : wakeup people waiting on sim end
|
||||||
|
pthread_cond_signal(&cpu->sim_condition);
|
||||||
|
// Then, wait for simulation state to change until we get more ticks to simulate
|
||||||
|
while(!cpu->sim_ticks_left)
|
||||||
|
pthread_cond_wait(&cpu->sim_condition, &cpu->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save previous interrupt enable in xSTATUS.xPIE
|
||||||
|
if(csr_read(cpu, CSR_SSTATUS) & STATUS_SIE)
|
||||||
|
csr_write(cpu, CSR_SSTATUS, csr_read(cpu, CSR_SSTATUS) | STATUS_SPIE);
|
||||||
|
else
|
||||||
|
csr_write(cpu, CSR_SSTATUS, csr_read(cpu, CSR_SSTATUS) & (~STATUS_SPIE));
|
||||||
|
|
||||||
|
// Set previous privilege mode in xSTATUS.xPP
|
||||||
|
if(cpu->privilege_mode == SUPERVISOR)
|
||||||
|
csr_write(cpu, CSR_SSTATUS, csr_read(cpu, CSR_SSTATUS) | STATUS_SPP);
|
||||||
|
else if(cpu->privilege_mode == USER)
|
||||||
|
csr_write(cpu, CSR_SSTATUS, csr_read(cpu, CSR_SSTATUS) & (~STATUS_SPP));
|
||||||
|
|
||||||
// Set privilege mode for exception handling, checking for delegation
|
// Set privilege mode for exception handling, checking for delegation
|
||||||
// TODO
|
// TODO
|
||||||
|
|
||||||
// Set xTVAL, exception-specific information related to xCAUSE
|
// Set xTVAL, exception-specific information related to xCAUSE
|
||||||
cpu->csr[CSR_STVAL] = tval;
|
csr_write(cpu, CSR_STVAL, tval);
|
||||||
|
|
||||||
// Set SEPC to instruction that caused exception
|
// Set SEPC to instruction that caused exception
|
||||||
cpu->csr[CSR_SEPC] = cpu->pc;
|
csr_write(cpu, CSR_SEPC, cpu->pc);
|
||||||
|
|
||||||
// Set PC to xTVEC : exception handling code
|
// Set PC to xTVEC : exception handling code
|
||||||
// xTVEC: [Base(30bits) Mode(2 bits)], address 4-byte aligned in base
|
// xTVEC: [Base(30bits) Mode(2 bits)], address 4-byte aligned in base
|
||||||
// Exceptions are not vectored (we can safely ignore mode)
|
// Exceptions are not vectored (we can safely ignore mode)
|
||||||
cpu->pc = cpu->csr[CSR_STVEC] & 0xFFFFFFFC;
|
cpu->pc = csr_read(cpu, CSR_STVEC) & 0xFFFFFFFC;
|
||||||
|
|
||||||
// Unlock cpu mutex, cpu_loop will lock it just after
|
// Unlock cpu mutex, cpu_loop will lock it just after
|
||||||
pthread_mutex_unlock(&cpu->mutex);
|
pthread_mutex_unlock(&cpu->mutex);
|
||||||
|
|
||||||
// cpu loop (attribute noreturn should erase previous stack)
|
// TODO : Hard reset the stack pointer
|
||||||
|
// cpu loop
|
||||||
cpu_loop(cpu);
|
cpu_loop(cpu);
|
||||||
|
|
||||||
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include "rv32cpu.h"
|
#include "rv32cpu.h"
|
||||||
|
|
||||||
void exception_trigger(rv32_cpu_t* cpu, uint32_t scause, uint32_t tval);
|
__attribute__((noreturn)) void exception_trigger(rv32_cpu_t* cpu, uint32_t scause, uint32_t tval);
|
||||||
|
|
||||||
#define SCAUSE_INSTRUCTION_MISSALIGNED 0x0
|
#define SCAUSE_INSTRUCTION_MISSALIGNED 0x0
|
||||||
#define SCAUSE_INSTRUCTION_ACCESS_FAULT 0x1
|
#define SCAUSE_INSTRUCTION_ACCESS_FAULT 0x1
|
||||||
|
@@ -4,28 +4,38 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
void interrupt_trigger(rv32_cpu_t* cpu, uint32_t scause)
|
uint32_t interrupt_mi_from_scause(uint32_t scause)
|
||||||
{
|
{
|
||||||
// Make sure that interrupts are enabled
|
switch(scause)
|
||||||
if(cpu->privilege_mode == MACHINE)
|
|
||||||
{
|
{
|
||||||
// In machine mode, we check mstatus.sie (bit 1)
|
case SCAUSE_SUPERVISOR_TIMER_INTERRUPT:
|
||||||
if(!(cpu->csr[CSR_MSTATUS] & 0b10))
|
return 0x20;
|
||||||
return;
|
default:
|
||||||
}
|
fprintf(stderr, "interrupt_mie_bit_from_scause: wrong scause 0x%x\n", scause);
|
||||||
else if(cpu->privilege_mode == SUPERVISOR)
|
|
||||||
{
|
|
||||||
// In supervisor mode, we check sstatus.sie (bit 1)
|
|
||||||
if(!(cpu->csr[CSR_SSTATUS] & 0b10))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
fprintf(stderr, "interrupt_trigger in non M/S-mode not implemented yet\n");
|
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void interrupt_trigger(rv32_cpu_t* cpu, uint32_t scause)
|
||||||
|
{
|
||||||
|
// Make sure that interrupts are enabled globally
|
||||||
|
if(!(csr_read(cpu, CSR_MSTATUS) & STATUS_SIE))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Make sure that current interrupt is enabled
|
||||||
|
if(!(csr_read(cpu, CSR_MIE) & interrupt_mi_from_scause(scause)))
|
||||||
|
{
|
||||||
|
// Mark interrupt as pending
|
||||||
|
csr_write(cpu, CSR_MIP, csr_read(cpu, CSR_MIP) | interrupt_mi_from_scause(scause));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO : CHECK mideleg to see wether we should handle interrupt is S mode or M mode
|
||||||
|
|
||||||
|
|
||||||
// An interrupt can only be triggered from outside
|
// An interrupt can only be triggered from outside
|
||||||
// of the cpu, so we are on a different thread
|
// of the cpu, so we are on a different thread
|
||||||
// and we don't already own the CPU mutex
|
// and we don't already own the CPU mutex
|
||||||
@@ -39,12 +49,14 @@ void interrupt_trigger(rv32_cpu_t* cpu, uint32_t scause)
|
|||||||
cpu->csr[CSR_SCAUSE] = 0x80000000 | scause;
|
cpu->csr[CSR_SCAUSE] = 0x80000000 | scause;
|
||||||
|
|
||||||
// Set xSTATUS.xPIE (previous interrupt enable) bit
|
// Set xSTATUS.xPIE (previous interrupt enable) bit
|
||||||
cpu->csr[CSR_SSTATUS] |= 0x80;
|
cpu->csr[CSR_MSTATUS] |= STATUS_SPIE;
|
||||||
|
|
||||||
// Set xSTATUS.xPP (Previous Privilege) bit
|
// Set xSTATUS.xPP (Previous Privilege) bit
|
||||||
|
// TODO : Allow user mode interrupts (by not setting this)
|
||||||
|
cpu->csr[CSR_MSTATUS] |= 0x100;
|
||||||
|
|
||||||
// Unset xSTATUS.xIE (interrupt enable) bit
|
// Unset xSTATUS.xIE (interrupt enable) bit
|
||||||
cpu->csr[CSR_SSTATUS] &= ~0b10U;
|
cpu->csr[CSR_MSTATUS] &= (~STATUS_SIE);
|
||||||
|
|
||||||
// Set xEPC : PC at interruption
|
// Set xEPC : PC at interruption
|
||||||
cpu->csr[CSR_SEPC] = cpu->pc;
|
cpu->csr[CSR_SEPC] = cpu->pc;
|
||||||
|
@@ -529,18 +529,22 @@ static void cpu_execute(rv32_cpu_t* cpu, instruction_t* instruction)
|
|||||||
case IMM_SRET:
|
case IMM_SRET:
|
||||||
// SRET: Return from supervisor interrupt
|
// SRET: Return from supervisor interrupt
|
||||||
// Restore Interrupt Enable from Previous Interrupt Enable
|
// Restore Interrupt Enable from Previous Interrupt Enable
|
||||||
uint32_t sstatus = csr_read(cpu, CSR_SSTATUS);
|
if(csr_read(cpu, CSR_SSTATUS) & STATUS_SPIE)
|
||||||
if(sstatus & 0x80)
|
csr_write(cpu, CSR_SSTATUS, csr_read(cpu, CSR_SSTATUS) | STATUS_SIE);
|
||||||
csr_write(cpu, CSR_SSTATUS, sstatus | 0b10);
|
|
||||||
|
|
||||||
// Restore privilege mode from Previous Privilege
|
// Restore privilege mode from Previous Privilege
|
||||||
// TODO
|
if(!(csr_read(cpu, CSR_SSTATUS) & STATUS_SPP))
|
||||||
|
{
|
||||||
|
// Previous Privilege was 0, return to user mode
|
||||||
|
fprintf(stderr, "SRET to user mode : not implemented yet\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
// Set Previous Interrupt Enable to 1
|
// Set Previous Interrupt Enable to 1
|
||||||
csr_write(cpu, CSR_SSTATUS, csr_read(cpu, CSR_SSTATUS) | 0x80);
|
csr_write(cpu, CSR_SSTATUS, csr_read(cpu, CSR_SSTATUS) | STATUS_SPIE);
|
||||||
|
|
||||||
// Set Previous Privilege to 0
|
// Set Previous Privilege to 0
|
||||||
// TODO
|
csr_write(cpu, CSR_SSTATUS, csr_read(cpu, CSR_SSTATUS) & (~STATUS_SPP));
|
||||||
|
|
||||||
// Saved PC before interrupt is in CSR SEPC, jump back
|
// Saved PC before interrupt is in CSR SEPC, jump back
|
||||||
cpu->pc = csr_read(cpu, CSR_SEPC) - 4;
|
cpu->pc = csr_read(cpu, CSR_SEPC) - 4;
|
||||||
@@ -632,13 +636,19 @@ static void cpu_execute(rv32_cpu_t* cpu, instruction_t* instruction)
|
|||||||
case FUNC75_LRW:
|
case FUNC75_LRW:
|
||||||
// Load-Reserved Word
|
// Load-Reserved Word
|
||||||
cpu->regs.x[instruction->rd] = mem_read32(address);
|
cpu->regs.x[instruction->rd] = mem_read32(address);
|
||||||
// TODO register reservation set that subsumes the bytes in word
|
cpu->load_reservation = address;
|
||||||
break;
|
break;
|
||||||
case FUNC75_SCW:
|
case FUNC75_SCW:
|
||||||
// Store-Conditional Word
|
// Store-Conditional Word
|
||||||
// TODO succeed only if the reservation is still valid and the reservation set contains the bytes written
|
if(cpu->load_reservation == address)
|
||||||
|
{
|
||||||
mem_write32(address, cpu->regs.x[instruction->rs2]);
|
mem_write32(address, cpu->regs.x[instruction->rs2]);
|
||||||
cpu->regs.x[instruction->rd] = 0; // TODO write 1 in rd on failure
|
cpu->regs.x[instruction->rd] = 0; // Write 0 in rd on success
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cpu->regs.x[instruction->rd] = 1; // Write 1 in rd on failure
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case FUNC75_AMOSWAPW:
|
case FUNC75_AMOSWAPW:
|
||||||
// Atomic Memory Operation SWAP Word
|
// Atomic Memory Operation SWAP Word
|
||||||
@@ -759,6 +769,8 @@ __attribute__((noreturn)) void cpu_loop(rv32_cpu_t* cpu)
|
|||||||
// Let go of cpu mutex
|
// Let go of cpu mutex
|
||||||
pthread_mutex_unlock(&cpu->mutex);
|
pthread_mutex_unlock(&cpu->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cpu_print_instruction(instruction_t* instruction)
|
static void cpu_print_instruction(instruction_t* instruction)
|
||||||
|
@@ -200,6 +200,7 @@ typedef struct RV32_CPU
|
|||||||
uint32_t csr[CSR_COUNT];
|
uint32_t csr[CSR_COUNT];
|
||||||
|
|
||||||
rvcpu_privilege_mode_t privilege_mode;
|
rvcpu_privilege_mode_t privilege_mode;
|
||||||
|
uint32_t load_reservation;
|
||||||
|
|
||||||
// Simulation data
|
// Simulation data
|
||||||
ssize_t sim_ticks_left; // -1 : simulate forever
|
ssize_t sim_ticks_left; // -1 : simulate forever
|
||||||
|
@@ -75,7 +75,7 @@ void sbi_call(rv32_cpu_t* cpu, uint32_t extension_id, uint32_t func_id)
|
|||||||
case SBI_EXTENSION_LEGACY_SHUTDOWN:
|
case SBI_EXTENSION_LEGACY_SHUTDOWN:
|
||||||
{
|
{
|
||||||
printf("sbi_call: shutdown, goodbye !\n");
|
printf("sbi_call: shutdown, goodbye !\n");
|
||||||
cpu->sim_ticks_left = 1;
|
exit(cpu->regs.a0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@@ -63,6 +63,13 @@ void mem_write8(uint32_t address, uint8_t value)
|
|||||||
io = io->next;
|
io = io->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if we are inside of physical memory
|
||||||
|
if(address + 1 > memory_size)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "MEMORY: Invalid write of size 1 outside of physical memory at address 0x%x\n", address);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
// Proceed with memory write
|
// Proceed with memory write
|
||||||
pthread_mutex_lock(&memory_mutex);
|
pthread_mutex_lock(&memory_mutex);
|
||||||
memory[address] = value;
|
memory[address] = value;
|
||||||
@@ -94,6 +101,13 @@ void mem_write16(uint32_t address, uint16_t value)
|
|||||||
io = io->next;
|
io = io->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if we are inside of physical memory
|
||||||
|
if(address + 2 > memory_size)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "MEMORY: Invalid write of size 2 outside of physical memory at address 0x%x\n", address);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
// Proceed with memory write
|
// Proceed with memory write
|
||||||
pthread_mutex_lock(&memory_mutex);
|
pthread_mutex_lock(&memory_mutex);
|
||||||
*((uint16_t*) &memory[address]) = value;
|
*((uint16_t*) &memory[address]) = value;
|
||||||
@@ -125,6 +139,13 @@ void mem_write32(uint32_t address, uint32_t value)
|
|||||||
io = io->next;
|
io = io->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if we are inside of physical memory
|
||||||
|
if(address + 4 > memory_size)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "MEMORY: Invalid write of size 1 outside of physical memory at address 0x%x\n", address);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
// Proceed with memory write
|
// Proceed with memory write
|
||||||
pthread_mutex_lock(&memory_mutex);
|
pthread_mutex_lock(&memory_mutex);
|
||||||
*((uint32_t*) &memory[address]) = value;
|
*((uint32_t*) &memory[address]) = value;
|
||||||
@@ -155,6 +176,13 @@ uint8_t mem_read8(uint32_t address)
|
|||||||
io = io->next;
|
io = io->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if we are inside of physical memory
|
||||||
|
if(address + 1 > memory_size)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "MEMORY: Invalid read of size 1 outside of physical memory at address 0x%x\n", address);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
// Proceed with memory read
|
// Proceed with memory read
|
||||||
pthread_mutex_lock(&memory_mutex);
|
pthread_mutex_lock(&memory_mutex);
|
||||||
uint8_t tr = memory[address];
|
uint8_t tr = memory[address];
|
||||||
@@ -186,6 +214,13 @@ uint16_t mem_read16(uint32_t address)
|
|||||||
io = io->next;
|
io = io->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if we are inside of physical memory
|
||||||
|
if(address + 2 > memory_size)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "MEMORY: Invalid read of size 2 outside of physical memory at address 0x%x\n", address);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
// Proceed with memory read
|
// Proceed with memory read
|
||||||
pthread_mutex_lock(&memory_mutex);
|
pthread_mutex_lock(&memory_mutex);
|
||||||
uint16_t tr = *((uint16_t*) &memory[address]);
|
uint16_t tr = *((uint16_t*) &memory[address]);
|
||||||
@@ -217,6 +252,13 @@ uint32_t mem_read32(uint32_t address)
|
|||||||
io = io->next;
|
io = io->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if we are inside of physical memory
|
||||||
|
if(address + 4 > memory_size)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "MEMORY: Invalid read of size 4 outside of physical memory at address 0x%x\n", address);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
// Proceed with memory read
|
// Proceed with memory read
|
||||||
pthread_mutex_lock(&memory_mutex);
|
pthread_mutex_lock(&memory_mutex);
|
||||||
uint32_t tr = *((uint32_t*) &memory[address]);
|
uint32_t tr = *((uint32_t*) &memory[address]);
|
||||||
@@ -240,6 +282,13 @@ uint32_t mem_fetch(uint32_t address)
|
|||||||
io = io->next;
|
io = io->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if we are inside of physical memory
|
||||||
|
if(address + 4 > memory_size)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "MEMORY: Invalid fetch outside of physical memory at address 0x%x\n", address);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
// Proceed with memory read
|
// Proceed with memory read
|
||||||
pthread_mutex_lock(&memory_mutex);
|
pthread_mutex_lock(&memory_mutex);
|
||||||
uint32_t tr = *((uint32_t*) &memory[address]);
|
uint32_t tr = *((uint32_t*) &memory[address]);
|
||||||
|
@@ -89,6 +89,18 @@ uint32_t mmu_resolve(rv32_cpu_t* cpu, memory_access_type_t access_type, uint32_t
|
|||||||
// Leaf PTE, we are ready to resolve the mapping
|
// Leaf PTE, we are ready to resolve the mapping
|
||||||
// This is a 4 MiB megapage
|
// This is a 4 MiB megapage
|
||||||
|
|
||||||
|
// For an execute, check if we are allowed to execute
|
||||||
|
if(access_type == INSTRUCTION_FETCH && !(pte & PTE_X))
|
||||||
|
exception_trigger(cpu, mmu_scause_from_access(access_type), vaddr);
|
||||||
|
|
||||||
|
// For a write, check if we are allowed to write
|
||||||
|
if(access_type == WRITE && !(pte & PTE_W))
|
||||||
|
exception_trigger(cpu, mmu_scause_from_access(access_type), vaddr);
|
||||||
|
|
||||||
|
// For a read, check if we are allowed to read
|
||||||
|
if(access_type == READ && !(pte & PTE_R))
|
||||||
|
exception_trigger(cpu, mmu_scause_from_access(access_type), vaddr);
|
||||||
|
|
||||||
// Physical Address: [PPN[1] = pte.PPN[1], PPN[0] = vaddr.VPN[0], offset]
|
// Physical Address: [PPN[1] = pte.PPN[1], PPN[0] = vaddr.VPN[0], offset]
|
||||||
uint32_t paddr = 0;
|
uint32_t paddr = 0;
|
||||||
paddr |= (PTE_PPN_1(pte) << 22);
|
paddr |= (PTE_PPN_1(pte) << 22);
|
||||||
@@ -120,6 +132,18 @@ uint32_t mmu_resolve(rv32_cpu_t* cpu, memory_access_type_t access_type, uint32_t
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For an execute, check if we are allowed to execute
|
||||||
|
if(access_type == INSTRUCTION_FETCH && !(pte & PTE_X))
|
||||||
|
exception_trigger(cpu, mmu_scause_from_access(access_type), vaddr);
|
||||||
|
|
||||||
|
// For a write, check if we are allowed to write
|
||||||
|
if(access_type == WRITE && !(pte & PTE_W))
|
||||||
|
exception_trigger(cpu, mmu_scause_from_access(access_type), vaddr);
|
||||||
|
|
||||||
|
// For a read, check if we are allowed to read
|
||||||
|
if(access_type == READ && !(pte & PTE_R))
|
||||||
|
exception_trigger(cpu, mmu_scause_from_access(access_type), vaddr);
|
||||||
|
|
||||||
// Physical Address: [PPN[1] = pte.PPN[1], PPN[0] = pte.PPN[0], offset]
|
// Physical Address: [PPN[1] = pte.PPN[1], PPN[0] = pte.PPN[0], offset]
|
||||||
uint32_t paddr = 0;
|
uint32_t paddr = 0;
|
||||||
paddr |= (PTE_PPN_1(pte) << 22);
|
paddr |= (PTE_PPN_1(pte) << 22);
|
||||||
|
@@ -2,7 +2,7 @@ AS=riscv32-elf-as
|
|||||||
LD=riscv32-elf-ld
|
LD=riscv32-elf-ld
|
||||||
BUILD_DIR=../build/tests/
|
BUILD_DIR=../build/tests/
|
||||||
|
|
||||||
S_FILES := $(shell find ./ -name '*.s')
|
S_FILES := $(shell find ./ -name '*.s' -not -name 'exit_return.s')
|
||||||
NAMES = $(basename $(S_FILES))
|
NAMES = $(basename $(S_FILES))
|
||||||
OBJECTS=$(patsubst %, $(BUILD_DIR)/%, $(NAMES))
|
OBJECTS=$(patsubst %, $(BUILD_DIR)/%, $(NAMES))
|
||||||
|
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
|
.include "exit_return.s"
|
||||||
|
|
||||||
.global _start
|
.global _start
|
||||||
_start:
|
_start:
|
||||||
addi a0, zero, 0xBA
|
addi a0, zero, 0xBA
|
||||||
ebreak
|
exret
|
||||||
|
10
tests/beq.s
10
tests/beq.s
@@ -1,3 +1,5 @@
|
|||||||
|
.include "exit_return.s"
|
||||||
|
|
||||||
.global _start
|
.global _start
|
||||||
_start:
|
_start:
|
||||||
# Set base value of a0 to 'test failed'
|
# Set base value of a0 to 'test failed'
|
||||||
@@ -14,18 +16,18 @@ _start:
|
|||||||
beq t0, t1, eq1
|
beq t0, t1, eq1
|
||||||
|
|
||||||
# On failure, return
|
# On failure, return
|
||||||
ebreak
|
exret
|
||||||
|
|
||||||
eqNeg:
|
eqNeg:
|
||||||
# All passed
|
# All passed
|
||||||
addi a0, zero, 0
|
addi a0, zero, 0
|
||||||
ebreak
|
exret
|
||||||
|
|
||||||
eq0:
|
eq0:
|
||||||
# Inequality failed
|
# Inequality failed
|
||||||
ebreak
|
exret
|
||||||
|
|
||||||
eq1:
|
eq1:
|
||||||
# Equality passed ; now try to test a negative offset case
|
# Equality passed ; now try to test a negative offset case
|
||||||
beq t0, t1, eqNeg
|
beq t0, t1, eqNeg
|
||||||
ebreak
|
exret
|
||||||
|
10
tests/blt.s
10
tests/blt.s
@@ -1,3 +1,5 @@
|
|||||||
|
.include "exit_return.s"
|
||||||
|
|
||||||
.global _start
|
.global _start
|
||||||
_start:
|
_start:
|
||||||
# Set base value of a0 to 'test failed'
|
# Set base value of a0 to 'test failed'
|
||||||
@@ -12,20 +14,20 @@ _start:
|
|||||||
blt t0, t1, lt1
|
blt t0, t1, lt1
|
||||||
|
|
||||||
# On failure, return
|
# On failure, return
|
||||||
ebreak
|
exret
|
||||||
|
|
||||||
ltNeg:
|
ltNeg:
|
||||||
# All passed
|
# All passed
|
||||||
addi a0, zero, 0
|
addi a0, zero, 0
|
||||||
ebreak
|
exret
|
||||||
|
|
||||||
lt0:
|
lt0:
|
||||||
# Inequality failed
|
# Inequality failed
|
||||||
ebreak
|
exret
|
||||||
|
|
||||||
lt1:
|
lt1:
|
||||||
# Inequality passed ; now try with negative numbers
|
# Inequality passed ; now try with negative numbers
|
||||||
addi t0, zero, -1
|
addi t0, zero, -1
|
||||||
addi t1, zero, -2
|
addi t1, zero, -2
|
||||||
blt t1, t0, ltNeg
|
blt t1, t0, ltNeg
|
||||||
ebreak
|
exret
|
||||||
|
4
tests/exit_return.s
Normal file
4
tests/exit_return.s
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
.macro exret
|
||||||
|
addi a7, zero, 0x8
|
||||||
|
ecall
|
||||||
|
.endm
|
@@ -1,3 +1,5 @@
|
|||||||
|
.include "exit_return.s"
|
||||||
|
|
||||||
.global _start
|
.global _start
|
||||||
_start:
|
_start:
|
||||||
# Set base value of a0 to 'test failed'
|
# Set base value of a0 to 'test failed'
|
||||||
@@ -8,17 +10,17 @@ _start:
|
|||||||
addi t0, t0, 12
|
addi t0, t0, 12
|
||||||
# Jump and link
|
# Jump and link
|
||||||
jal ra, fn0
|
jal ra, fn0
|
||||||
ebreak
|
exret
|
||||||
|
|
||||||
fnNeg:
|
fnNeg:
|
||||||
# All good
|
# All good
|
||||||
addi a0, zero, 0
|
addi a0, zero, 0
|
||||||
ebreak
|
exret
|
||||||
|
|
||||||
fn0:
|
fn0:
|
||||||
# Check ra value with our t0 construct
|
# Check ra value with our t0 construct
|
||||||
beq t0, ra, eq0
|
beq t0, ra, eq0
|
||||||
ebreak
|
exret
|
||||||
|
|
||||||
eq0:
|
eq0:
|
||||||
# Try to jump back to a negative offset
|
# Try to jump back to a negative offset
|
||||||
|
14
tests/jalr.s
14
tests/jalr.s
@@ -1,3 +1,5 @@
|
|||||||
|
.include "exit_return.s"
|
||||||
|
|
||||||
.global _start
|
.global _start
|
||||||
_start:
|
_start:
|
||||||
# Set base value of a0 to 'test failed'
|
# Set base value of a0 to 'test failed'
|
||||||
@@ -19,13 +21,13 @@ _start:
|
|||||||
# Jump far to test jalr with negative offset
|
# Jump far to test jalr with negative offset
|
||||||
jal fnfar
|
jal fnfar
|
||||||
|
|
||||||
ebreak
|
exret
|
||||||
|
|
||||||
# just_after : address is 16 bytes after auipc
|
# just_after : address is 16 bytes after auipc
|
||||||
just_after:
|
just_after:
|
||||||
# ra must still be the old address
|
# ra must still be the old address
|
||||||
ret
|
ret
|
||||||
ebreak
|
exret
|
||||||
|
|
||||||
# fn0 : function that calls fn1 and returns
|
# fn0 : function that calls fn1 and returns
|
||||||
fn0:
|
fn0:
|
||||||
@@ -42,18 +44,18 @@ fn0:
|
|||||||
addi sp, sp, 4
|
addi sp, sp, 4
|
||||||
|
|
||||||
ret
|
ret
|
||||||
ebreak
|
exret
|
||||||
|
|
||||||
# fn1 : just return
|
# fn1 : just return
|
||||||
fn1:
|
fn1:
|
||||||
ret
|
ret
|
||||||
ebreak
|
exret
|
||||||
|
|
||||||
fnneg:
|
fnneg:
|
||||||
addi a0, zero, 0
|
addi a0, zero, 0
|
||||||
ebreak
|
exret
|
||||||
|
|
||||||
fnfar:
|
fnfar:
|
||||||
auipc ra, 0
|
auipc ra, 0
|
||||||
jalr -8(ra)
|
jalr -8(ra)
|
||||||
ebreak
|
exret
|
||||||
|
40
tests/mulh.s
Normal file
40
tests/mulh.s
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
.include "exit_return.s"
|
||||||
|
|
||||||
|
.global _start
|
||||||
|
_start:
|
||||||
|
# Set base value of a0 to 'test failed'
|
||||||
|
addi a0, zero, 1
|
||||||
|
|
||||||
|
# Multiply 2 * 3 in t0, high bits
|
||||||
|
addi t0, zero, 3
|
||||||
|
addi t1, zero, 2
|
||||||
|
mulh t0, t0, t1
|
||||||
|
addi t1, zero, 0
|
||||||
|
beq t0, t1, mulh_low_ok
|
||||||
|
exret
|
||||||
|
|
||||||
|
mulh_low_ok:
|
||||||
|
# Multiply 2<<29 * 8 in t0, high bits
|
||||||
|
# Result high should be 2
|
||||||
|
addi t1, zero, 2
|
||||||
|
slli t0, t1, 29
|
||||||
|
addi t1, zero, 8
|
||||||
|
mulh t0, t0, t1
|
||||||
|
addi t1, zero, 2
|
||||||
|
beq t0, t1, mulh_high_ok
|
||||||
|
exret
|
||||||
|
|
||||||
|
mulh_high_ok:
|
||||||
|
# Multiply 2 << 29 * -8 in t0, high bits
|
||||||
|
# Result high should be 0xFFFF_FFFE ie -2 signed ?
|
||||||
|
addi t1, zero, 2
|
||||||
|
slli t0, t1, 29
|
||||||
|
addi t1, zero, -8
|
||||||
|
mulh t0, t0, t1
|
||||||
|
addi t1, zero, -2
|
||||||
|
beq t0, t1, mulh_neg_ok
|
||||||
|
exret
|
||||||
|
|
||||||
|
mulh_neg_ok:
|
||||||
|
addi a0, zero, 0
|
||||||
|
exret
|
@@ -1,5 +1,7 @@
|
|||||||
|
.include "exit_return.s"
|
||||||
|
|
||||||
.global _start
|
.global _start
|
||||||
_start:
|
_start:
|
||||||
addi a1, zero, 0xBA
|
addi a1, zero, 0xBA
|
||||||
mv a0, a1
|
mv a0, a1
|
||||||
ebreak
|
exret
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
.include "exit_return.s"
|
||||||
|
|
||||||
.global _start
|
.global _start
|
||||||
_start:
|
_start:
|
||||||
# Set base value of a0 to 'test failed'
|
# Set base value of a0 to 'test failed'
|
||||||
@@ -16,12 +18,12 @@ _start:
|
|||||||
# Compare
|
# Compare
|
||||||
beq t0, t1, good
|
beq t0, t1, good
|
||||||
|
|
||||||
ebreak
|
exret
|
||||||
|
|
||||||
good:
|
good:
|
||||||
beq t0, t2, xtragood
|
beq t0, t2, xtragood
|
||||||
ebreak
|
exret
|
||||||
|
|
||||||
xtragood:
|
xtragood:
|
||||||
addi a0, zero, 0
|
addi a0, zero, 0
|
||||||
ebreak
|
exret
|
||||||
|
@@ -29,3 +29,4 @@ test "BLT : Branch Less Than " "../build/tests/blt " 0
|
|||||||
test "JAL : Jump And Link " "../build/tests/jal " 0
|
test "JAL : Jump And Link " "../build/tests/jal " 0
|
||||||
test "SWLW : Store Word Load Word " "../build/tests/swlw " 0
|
test "SWLW : Store Word Load Word " "../build/tests/swlw " 0
|
||||||
test "JALR : Jump And Link Register " "../build/tests/jalr " 0
|
test "JALR : Jump And Link Register " "../build/tests/jalr " 0
|
||||||
|
test "MULH : MULtply High " "../build/tests/mulh " 0
|
||||||
|
Reference in New Issue
Block a user