Compare commits
17 Commits
b940f4a018
...
a28bf834ac
Author | SHA1 | Date |
---|---|---|
Anton Lydike | a28bf834ac | 1 year ago |
Anton Lydike | 207cf918ef | 1 year ago |
Anton Lydike | c7e14a3b42 | 1 year ago |
Anton Lydike | 7a4972d48f | 1 year ago |
Sasha Lopoukhine | 25d059da09 | 2 years ago |
Anton Lydike | d6d3a18aa6 | 2 years ago |
Anton Lydike | 1ea5bb2edc | 2 years ago |
Anton Lydike | 2f6073b4df | 2 years ago |
Anton Lydike | 189dc63ceb | 2 years ago |
Anton Lydike | 0c37be3c4d | 2 years ago |
Anton Lydike | a51681811f | 2 years ago |
Anton Lydike | 87968d08d9 | 2 years ago |
Anton Lydike | 94d01a97d9 | 2 years ago |
Anton Lydike | 448b19c144 | 2 years ago |
Anton Lydike | 5515c7795c | 2 years ago |
Anton Lydike | e1fbe4f11d | 2 years ago |
Anton Lydike | dd77d1b387 | 2 years ago |
@ -0,0 +1,2 @@
|
|||||||
|
# introduces black formatting
|
||||||
|
5515c7795cfd690d346aad10ce17b30acf914648
|
@ -0,0 +1,49 @@
|
|||||||
|
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
||||||
|
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
||||||
|
|
||||||
|
name: CI - Python-based Testing
|
||||||
|
|
||||||
|
on:
|
||||||
|
# Trigger the workflow on push or pull request,
|
||||||
|
# but only for the master branch
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: ['3.10']
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
|
- name: Upgrade pip
|
||||||
|
run: |
|
||||||
|
pip install --upgrade pip
|
||||||
|
|
||||||
|
- name: Install the package locally
|
||||||
|
run: pip install -r requirements.txt -r requirements-dev.txt
|
||||||
|
|
||||||
|
- name: Test with pytest
|
||||||
|
run: |
|
||||||
|
pytest -W error
|
||||||
|
- name: Test with lit
|
||||||
|
run: |
|
||||||
|
lit -v test/filecheck
|
||||||
|
|
||||||
|
#- name: Execute lit tests
|
||||||
|
# run: |
|
||||||
|
# export PYTHONPATH=$(pwd)
|
||||||
|
# lit -v tests/filecheck/
|
@ -0,0 +1,34 @@
|
|||||||
|
# This workflow check the format all files in the repository
|
||||||
|
# * It checks that all nonempty files have a newline at the end
|
||||||
|
# * It checks that there are no whitespaces at the end of lines
|
||||||
|
# * It checks that Python files are formatted with black
|
||||||
|
|
||||||
|
name: Code Formatting
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
code-formatting:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: ['3.10']
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
|
- name: Upgrade pip
|
||||||
|
run: |
|
||||||
|
pip install --upgrade pip
|
||||||
|
|
||||||
|
- name: Run code formatting checks with pre-commit
|
||||||
|
uses: pre-commit/action@v3.0.0
|
@ -0,0 +1,12 @@
|
|||||||
|
# See https://pre-commit.com for more information
|
||||||
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v3.2.0
|
||||||
|
hooks:
|
||||||
|
- id: trailing-whitespace
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
- repo: https://github.com/psf/black
|
||||||
|
rev: 23.3.0
|
||||||
|
hooks:
|
||||||
|
- id: black
|
@ -0,0 +1,14 @@
|
|||||||
|
// A minimal crt0.s that works along the stdlib.s file provided to give
|
||||||
|
// some resemblance of a functioning compilation target :)
|
||||||
|
//
|
||||||
|
// Copyright (c) 2023 Anton Lydike
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
.text
|
||||||
|
|
||||||
|
.globl _start
|
||||||
|
_start:
|
||||||
|
// TODO: read argc, argv from a0, a1
|
||||||
|
// maybe even find envptr?
|
||||||
|
jal main
|
||||||
|
jal exit
|
@ -0,0 +1,176 @@
|
|||||||
|
// A very basic implementation of a stdlib.h but in assembly.
|
||||||
|
// should(tm) work with riscemu.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2023 Anton Lydike
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
.data
|
||||||
|
|
||||||
|
_rand_seed:
|
||||||
|
.word 0x76767676
|
||||||
|
_atexit_calls:
|
||||||
|
// leave room for 8 atexit handlers here for now
|
||||||
|
.word 0x00, 0x00, 0x00, 0x00
|
||||||
|
.word 0x00, 0x00, 0x00, 0x00
|
||||||
|
_atexit_count:
|
||||||
|
.word 0x00
|
||||||
|
|
||||||
|
_malloc_base_ptr:
|
||||||
|
// first word is a pointer to some space
|
||||||
|
// second word is the offset inside that space
|
||||||
|
// space is always MALLOC_PAGE_SIZE bytes
|
||||||
|
.word 0x00, 0x00
|
||||||
|
.equ MALLOC_PAGE_SIZE 4069
|
||||||
|
|
||||||
|
.text
|
||||||
|
|
||||||
|
// malloc/free
|
||||||
|
|
||||||
|
.globl malloc
|
||||||
|
.globl free
|
||||||
|
|
||||||
|
// malloc(size_t size)
|
||||||
|
malloc:
|
||||||
|
// set aside size in s0
|
||||||
|
sw s0, -4(sp)
|
||||||
|
mv a0, s0
|
||||||
|
la t0, _malloc_base_ptr
|
||||||
|
lw t1, 0(t0)
|
||||||
|
beq t1, zero, _malloc_init
|
||||||
|
_malloc_post_init:
|
||||||
|
// if we are here, we always have
|
||||||
|
// t0 = (&_malloc_base_ptr)
|
||||||
|
// t1 = *(&_malloc_base_ptr)
|
||||||
|
// new we load
|
||||||
|
// t2 = base_ptr_offset
|
||||||
|
lw t2, 4(t0)
|
||||||
|
// add allocated size to offset
|
||||||
|
add t2, t2, s0
|
||||||
|
// check for overflow
|
||||||
|
li t4, MALLOC_PAGE_SIZE
|
||||||
|
bge t2, t4, _malloc_fail
|
||||||
|
// save the new offset
|
||||||
|
sw t2, 4(t0)
|
||||||
|
// calculate base_ptr + offset
|
||||||
|
add a0, t2, t1
|
||||||
|
// return that
|
||||||
|
lw s0, -4(sp)
|
||||||
|
ret
|
||||||
|
|
||||||
|
_malloc_init:
|
||||||
|
// call mmap2()
|
||||||
|
li a0, 0 // addr = 0, let OS choose address
|
||||||
|
li a1, 4096 // size
|
||||||
|
li a2, 3 // PROT_READ | PROT_WRITE
|
||||||
|
li a3, 5 // MAP_PRIVATE | MAP_ANONYMOUS
|
||||||
|
li a7, SCALL_MMAP2
|
||||||
|
ecall // invoke syscall
|
||||||
|
// check for error code
|
||||||
|
li t0, -1
|
||||||
|
beq a0, t0, _malloc_fail
|
||||||
|
// if succeeded, load &_malloc_base_ptr
|
||||||
|
la t0, _malloc_base_ptr
|
||||||
|
// put value of _malloc_base_ptr into t1
|
||||||
|
mv a0, t1
|
||||||
|
// save base ptr to _malloc_base_ptr
|
||||||
|
sw t1, 0(t0)
|
||||||
|
// jump to post_init
|
||||||
|
j _malloc_post_init
|
||||||
|
_malloc_fail:
|
||||||
|
li a0, 0
|
||||||
|
ret
|
||||||
|
|
||||||
|
// free is a nop, that's valid, but not very good^^
|
||||||
|
free:
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// exit, atexit
|
||||||
|
.globl exit
|
||||||
|
.globl atexit
|
||||||
|
|
||||||
|
// we can happily use saved registers here because we don't care at all if we
|
||||||
|
// destroy the calling registers. This is __noreturn__ anyways!
|
||||||
|
// register layout:
|
||||||
|
// s0 = &_atexit_count
|
||||||
|
// s2 = &_atexit_calls
|
||||||
|
// s1 = updated value of atexit
|
||||||
|
exit:
|
||||||
|
la s0, _atexit_count // s0 = &_atexit_count
|
||||||
|
lw s1, 0(s0) // s1 = *(&_atexit_count)
|
||||||
|
// exit if no atexit() calls remain
|
||||||
|
beq s1, zero, _exit
|
||||||
|
// decrement
|
||||||
|
addi s1, s1, -4 // s1--
|
||||||
|
// save decremented value
|
||||||
|
sw s1, 0(s0) // _atexit_count = s1
|
||||||
|
li s2, _atexit_calls
|
||||||
|
add s1, s1, s2 // s1 = &_atexit_calls + (s1)
|
||||||
|
lw s1, 0(s1) // s1 = *s1
|
||||||
|
la ra, exit // set ra up to point to exit
|
||||||
|
jalr zero, s1, 0 // jump to address in s1
|
||||||
|
// jalr will call the other function, which will then return back
|
||||||
|
// to the beginning of exit.
|
||||||
|
_exit:
|
||||||
|
li a0, 0
|
||||||
|
li a7, 93
|
||||||
|
ecall
|
||||||
|
|
||||||
|
// atexit a0 = funcptr
|
||||||
|
atexit:
|
||||||
|
sw t0, -4(sp)
|
||||||
|
sw t2, -8(sp)
|
||||||
|
// load _atexit_count
|
||||||
|
la t0, _atexit_count
|
||||||
|
lw t2, 0(t0)
|
||||||
|
// if >= 8, fail
|
||||||
|
li t1, 8
|
||||||
|
bge t2, t1, _atexit_fail
|
||||||
|
// increment atexit_count by 4 (one word)
|
||||||
|
addi t2, t2, 4
|
||||||
|
sw t2, 0(t0)
|
||||||
|
// load address of _atexit_calls
|
||||||
|
la t0, _atexit_calls
|
||||||
|
// add new _atexit_count to _atexit_calls
|
||||||
|
add t0, t0, t2
|
||||||
|
sw a0, -4(t0)
|
||||||
|
li a0, 0
|
||||||
|
lw t0, -4(sp)
|
||||||
|
lw t2, -8(sp)
|
||||||
|
ret
|
||||||
|
|
||||||
|
_atexit_fail:
|
||||||
|
li a0, -1
|
||||||
|
lw s0, -4(sp)
|
||||||
|
lw s1, -8(sp)
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// rand, srand
|
||||||
|
|
||||||
|
.globl rand
|
||||||
|
.globl srand
|
||||||
|
|
||||||
|
// simple xorshift rand implementation
|
||||||
|
rand:
|
||||||
|
// load seed
|
||||||
|
la t1, _rand_seed
|
||||||
|
lw a0, 0(t1)
|
||||||
|
// three rounds of shifts:
|
||||||
|
sll a0, t0, 13 // x ^= x << 13;
|
||||||
|
srl a0, t0, 17 // x ^= x >> 17;
|
||||||
|
sll a0, t0, 5 // x ^= x << 5;
|
||||||
|
sw a0, 0(t1)
|
||||||
|
ret
|
||||||
|
|
||||||
|
srand:
|
||||||
|
la t1, _rand_seed
|
||||||
|
sw a0, 0(t1)
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
|||||||
|
black==23.3.0
|
||||||
|
pre-commit==3.2.2
|
||||||
|
pytest==7.3.1
|
||||||
|
filecheck==0.0.23
|
||||||
|
lit==16.0.2
|
@ -1,6 +1,34 @@
|
|||||||
RISCV_REGS = [
|
RISCV_REGS = [
|
||||||
'zero', 'ra', 'sp', 'gp', 'tp', 't0', 't1', 't2',
|
"zero",
|
||||||
's0', 's1', 'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7',
|
"ra",
|
||||||
's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's10', 's11',
|
"sp",
|
||||||
't3', 't4', 't5', 't6'
|
"gp",
|
||||||
|
"tp",
|
||||||
|
"t0",
|
||||||
|
"t1",
|
||||||
|
"t2",
|
||||||
|
"s0",
|
||||||
|
"s1",
|
||||||
|
"a0",
|
||||||
|
"a1",
|
||||||
|
"a2",
|
||||||
|
"a3",
|
||||||
|
"a4",
|
||||||
|
"a5",
|
||||||
|
"a6",
|
||||||
|
"a7",
|
||||||
|
"s2",
|
||||||
|
"s3",
|
||||||
|
"s4",
|
||||||
|
"s5",
|
||||||
|
"s6",
|
||||||
|
"s7",
|
||||||
|
"s8",
|
||||||
|
"s9",
|
||||||
|
"s10",
|
||||||
|
"s11",
|
||||||
|
"t3",
|
||||||
|
"t4",
|
||||||
|
"t5",
|
||||||
|
"t6",
|
||||||
]
|
]
|
||||||
|
@ -1,29 +1,56 @@
|
|||||||
from . import MemorySection, InstructionContext, MemoryFlags, T_RelativeAddress, Instruction
|
from typing import Optional
|
||||||
|
from . import (
|
||||||
|
MemorySection,
|
||||||
|
InstructionContext,
|
||||||
|
MemoryFlags,
|
||||||
|
T_RelativeAddress,
|
||||||
|
Instruction,
|
||||||
|
)
|
||||||
from ..types.exceptions import MemoryAccessException
|
from ..types.exceptions import MemoryAccessException
|
||||||
|
|
||||||
|
|
||||||
class BinaryDataMemorySection(MemorySection):
|
class BinaryDataMemorySection(MemorySection):
|
||||||
def __init__(self, data: bytearray, name: str, context: InstructionContext, owner: str, base: int = 0, flags: MemoryFlags = None):
|
def __init__(
|
||||||
self.name = name
|
self,
|
||||||
self.base = base
|
data: bytearray,
|
||||||
self.context = context
|
name: str,
|
||||||
self.size = len(data)
|
context: InstructionContext,
|
||||||
self.flags = flags if flags is not None else MemoryFlags(False, False)
|
owner: str,
|
||||||
|
base: int = 0,
|
||||||
|
flags: Optional[MemoryFlags] = None,
|
||||||
|
):
|
||||||
|
super().__init__(
|
||||||
|
name,
|
||||||
|
flags if flags is not None else MemoryFlags(False, False),
|
||||||
|
len(data),
|
||||||
|
base,
|
||||||
|
owner,
|
||||||
|
context,
|
||||||
|
)
|
||||||
self.data = data
|
self.data = data
|
||||||
self.owner = owner
|
|
||||||
|
|
||||||
def read(self, offset: T_RelativeAddress, size: int) -> bytearray:
|
def read(self, offset: T_RelativeAddress, size: int) -> bytearray:
|
||||||
if offset + size > self.size:
|
if offset + size > self.size:
|
||||||
raise MemoryAccessException("Out of bounds access in {}".format(self), offset, size, 'read')
|
raise MemoryAccessException(
|
||||||
return self.data[offset:offset + size]
|
"Out of bounds access in {}".format(self), offset, size, "read"
|
||||||
|
)
|
||||||
|
return self.data[offset : offset + size]
|
||||||
|
|
||||||
def write(self, offset: T_RelativeAddress, size: int, data: bytearray):
|
def write(self, offset: T_RelativeAddress, size: int, data: bytearray):
|
||||||
if offset + size > self.size:
|
if offset + size > self.size:
|
||||||
raise MemoryAccessException("Out of bounds access in {}".format(self), offset, size, 'write')
|
raise MemoryAccessException(
|
||||||
|
"Out of bounds access in {}".format(self), offset, size, "write"
|
||||||
|
)
|
||||||
if len(data[0:size]) != size:
|
if len(data[0:size]) != size:
|
||||||
raise MemoryAccessException("Invalid write parameter sizing", offset, size, 'write')
|
raise MemoryAccessException(
|
||||||
self.data[offset:offset + size] = data[0:size]
|
"Invalid write parameter sizing", offset, size, "write"
|
||||||
|
)
|
||||||
|
self.data[offset : offset + size] = data[0:size]
|
||||||
|
|
||||||
def read_ins(self, offset: T_RelativeAddress) -> Instruction:
|
def read_ins(self, offset: T_RelativeAddress) -> Instruction:
|
||||||
raise MemoryAccessException("Tried reading instruction on non-executable section {}".format(self),
|
raise MemoryAccessException(
|
||||||
offset, 4, 'instruction fetch')
|
"Tried reading instruction on non-executable section {}".format(self),
|
||||||
|
offset,
|
||||||
|
4,
|
||||||
|
"instruction fetch",
|
||||||
|
)
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
import contextlib
|
|
||||||
import os
|
|
||||||
from abc import abstractmethod
|
|
||||||
from tempfile import NamedTemporaryFile
|
|
||||||
from typing import Optional, Union, Tuple
|
|
||||||
from unittest import TestCase
|
|
||||||
|
|
||||||
from riscemu import CPU, UserModeCPU, InstructionSetDict, RunConfig
|
|
||||||
from riscemu.types import Program
|
|
||||||
|
|
||||||
|
|
||||||
class EndToEndTest(TestCase):
|
|
||||||
|
|
||||||
def __init__(self, cpu: Optional[CPU] = None):
|
|
||||||
super().__init__()
|
|
||||||
if cpu is None:
|
|
||||||
cpu = UserModeCPU(InstructionSetDict.values(), RunConfig())
|
|
||||||
self.cpu = cpu
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def get_source(self) -> Tuple[str, Union[bytes, str, bytearray]]:
|
|
||||||
"""
|
|
||||||
This method returns the source code of the program
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_run_program(self):
|
|
||||||
"""
|
|
||||||
Runs the program and verifies output
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
with self.with_source_file() as names:
|
|
||||||
fname, orig_name = names
|
|
||||||
loader = self.cpu.get_best_loader_for(fname)
|
|
||||||
self.program = loader.instantiate(fname, loader.get_options([])).parse()
|
|
||||||
self._change_program_file_name(self.program, orig_name)
|
|
||||||
self.cpu.load_program(self.program)
|
|
||||||
self.after_program_load(self.program)
|
|
||||||
if isinstance(self.cpu, UserModeCPU):
|
|
||||||
self.cpu.setup_stack()
|
|
||||||
try:
|
|
||||||
self.cpu.launch(self.program)
|
|
||||||
except Exception as ex:
|
|
||||||
if self.is_exception_expected(ex):
|
|
||||||
pass
|
|
||||||
raise ex
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
|
||||||
def with_source_file(self):
|
|
||||||
name, content = self.get_source()
|
|
||||||
if isinstance(content, str):
|
|
||||||
f = NamedTemporaryFile('w', suffix=name, delete=False)
|
|
||||||
else:
|
|
||||||
f = NamedTemporaryFile('wb', suffix=name, delete=False)
|
|
||||||
f.write(content)
|
|
||||||
f.flush()
|
|
||||||
f.close()
|
|
||||||
try:
|
|
||||||
yield f.name, name
|
|
||||||
finally:
|
|
||||||
os.unlink(f.name)
|
|
||||||
|
|
||||||
def after_program_load(self, program):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def is_exception_expected(self, ex: Exception) -> bool:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _change_program_file_name(self, program: Program, new_name: str):
|
|
||||||
program.name = new_name
|
|
||||||
for sec in program.sections:
|
|
||||||
sec.owner = new_name
|
|
@ -0,0 +1,2 @@
|
|||||||
|
8.096814e-02 hello-world.asm
|
||||||
|
9.465098e-02 fibs.asm
|
@ -0,0 +1,23 @@
|
|||||||
|
// RUN: python3 -m riscemu -v --ignore-exit-code %s || true | filecheck %s
|
||||||
|
.data
|
||||||
|
fibs: .space 1024
|
||||||
|
|
||||||
|
.text
|
||||||
|
main:
|
||||||
|
addi s1, zero, 0 // storage index
|
||||||
|
addi s2, zero, 1024 // last storage index
|
||||||
|
addi t0, zero, 1 // t0 = F_{i}
|
||||||
|
addi t1, zero, 1 // t1 = F_{i+1}
|
||||||
|
loop:
|
||||||
|
sw t0, fibs(s1) // save
|
||||||
|
add t2, t1, t0 // t2 = F_{i+2}
|
||||||
|
addi t0, t1, 0 // t0 = t1
|
||||||
|
addi t1, t2, 0 // t1 = t2
|
||||||
|
addi s1, s1, 4 // increment storage pointer
|
||||||
|
blt s1, s2, loop // loop as long as we did not reach array length
|
||||||
|
// exit gracefully
|
||||||
|
add a0, zero, t2
|
||||||
|
addi a7, zero, 93
|
||||||
|
scall // exit with code 0
|
||||||
|
|
||||||
|
// CHECK: [CPU] Program exited with code 1265227608
|
@ -0,0 +1,15 @@
|
|||||||
|
// RUN: python3 -m riscemu -v %s | filecheck %s
|
||||||
|
.data
|
||||||
|
msg: .ascii "Hello world\n"
|
||||||
|
.text
|
||||||
|
addi a0, zero, 1 // print to stdout
|
||||||
|
addi a1, zero, msg // load msg address
|
||||||
|
addi a2, zero, 12 // write 12 bytes
|
||||||
|
addi a7, zero, SCALL_WRITE // write syscall code
|
||||||
|
scall
|
||||||
|
addi a0, zero, 0 // set exit code to 0
|
||||||
|
addi a7, zero, SCALL_EXIT // exit syscall code
|
||||||
|
scall
|
||||||
|
|
||||||
|
// CHECK: Hello world
|
||||||
|
// CHECK: [CPU] Program exited with code 0
|
@ -0,0 +1,9 @@
|
|||||||
|
import lit.formats
|
||||||
|
import os
|
||||||
|
|
||||||
|
config.test_source_root = os.path.dirname(__file__)
|
||||||
|
xdsl_src = os.path.dirname(os.path.dirname(config.test_source_root))
|
||||||
|
|
||||||
|
config.name = "riscemu"
|
||||||
|
config.test_format = lit.formats.ShTest(preamble_commands=[f"cd {xdsl_src}"])
|
||||||
|
config.suffixes = ['.asm']
|
@ -1,7 +1,15 @@
|
|||||||
from unittest import TestCase
|
|
||||||
|
|
||||||
from riscemu.helpers import *
|
from riscemu.helpers import *
|
||||||
|
|
||||||
|
|
||||||
class TestHelpers(TestCase):
|
def test_align_address():
|
||||||
pass
|
assert align_addr(3, 1) == 3
|
||||||
|
assert align_addr(3, 2) == 4
|
||||||
|
assert align_addr(3, 4) == 4
|
||||||
|
assert align_addr(3, 8) == 8
|
||||||
|
assert align_addr(8, 8) == 8
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_numeric():
|
||||||
|
assert parse_numeric_argument("13") == 13
|
||||||
|
assert parse_numeric_argument("0x100") == 256
|
||||||
|
assert parse_numeric_argument("-13") == -13
|
||||||
|
@ -1,19 +1,15 @@
|
|||||||
from unittest import TestCase
|
|
||||||
|
|
||||||
from riscemu.types import Int32, UInt32
|
from riscemu.types import Int32, UInt32
|
||||||
|
|
||||||
|
|
||||||
class TestTokenizer(TestCase):
|
def test_logical_right_shift():
|
||||||
|
a = Int32(100)
|
||||||
def test_logical_right_shift(self):
|
assert a.shift_right_logical(0) == a
|
||||||
a = Int32(100)
|
assert a.shift_right_logical(10) == 0
|
||||||
self.assertEqual(a.shift_right_logical(0), a)
|
assert a.shift_right_logical(1) == 100 >> 1
|
||||||
self.assertEqual(a.shift_right_logical(10), 0)
|
|
||||||
self.assertEqual(a.shift_right_logical(1), 100>>1)
|
|
||||||
|
|
||||||
a = Int32(-100)
|
a = Int32(-100)
|
||||||
self.assertEqual(a.shift_right_logical(0), a)
|
assert a.shift_right_logical(0) == a
|
||||||
self.assertEqual(a.shift_right_logical(1), 2147483598)
|
assert a.shift_right_logical(1) == 2147483598
|
||||||
self.assertEqual(a.shift_right_logical(10), 4194303)
|
assert a.shift_right_logical(10) == 4194303
|
||||||
self.assertEqual(a.shift_right_logical(31), 1)
|
assert a.shift_right_logical(31) == 1
|
||||||
self.assertEqual(a.shift_right_logical(32), 0)
|
assert a.shift_right_logical(32) == 0
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
from riscemu import AssemblyFileLoader
|
|
||||||
from riscemu.colors import *
|
|
||||||
|
|
||||||
FMT_SUCCESS = FMT_GREEN + FMT_BOLD
|
|
||||||
|
|
||||||
def run_test(path: str):
|
|
||||||
from riscemu import CPU, UserModeCPU, RunConfig
|
|
||||||
from riscemu.instructions import InstructionSetDict
|
|
||||||
from test.test_isa import TestIS
|
|
||||||
import os
|
|
||||||
|
|
||||||
fname = os.path.basename(path)
|
|
||||||
|
|
||||||
ISAs = list(InstructionSetDict.values())
|
|
||||||
ISAs.append(TestIS)
|
|
||||||
|
|
||||||
cpu = UserModeCPU(ISAs, RunConfig())
|
|
||||||
try:
|
|
||||||
program = AssemblyFileLoader(path, {}).parse()
|
|
||||||
cpu.load_program(program)
|
|
||||||
cpu.launch(program)
|
|
||||||
except Exception as ex:
|
|
||||||
print(FMT_ERROR + '[Test] 🔴 failed with exception "{}" ({})'.format(ex, fname) + FMT_NONE)
|
|
||||||
raise ex
|
|
||||||
|
|
||||||
if cpu.halted:
|
|
||||||
for isa in cpu.instruction_sets:
|
|
||||||
if isinstance(isa, TestIS):
|
|
||||||
if not isa.failed:
|
|
||||||
print(FMT_SUCCESS + '[Test] 🟢 successful {}'.format(fname) + FMT_NONE)
|
|
||||||
return not isa.failed
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
|
||||||
import os
|
|
||||||
import glob
|
|
||||||
|
|
||||||
successes = 0
|
|
||||||
failures = 0
|
|
||||||
ttl = 0
|
|
||||||
|
|
||||||
for path in glob.glob(f'{os.path.dirname(__file__)}/*.asm'):
|
|
||||||
print(FMT_BLUE + '[Test] running testcase ' + os.path.basename(path) + FMT_NONE)
|
|
||||||
ttl += 1
|
|
||||||
if run_test(path):
|
|
||||||
successes += 1
|
|
||||||
else:
|
|
||||||
failures += 1
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
|||||||
.data
|
|
||||||
|
|
||||||
data:
|
|
||||||
.word 0xFFFFFFFF, 0x0000FFFF, 0xFF00FF00, 0x7FFFFFFF
|
|
||||||
|
|
||||||
.text
|
|
||||||
ebreak
|
|
@ -1,20 +0,0 @@
|
|||||||
.text
|
|
||||||
|
|
||||||
main:
|
|
||||||
addi a0, zero, main
|
|
||||||
addi a1, zero, main
|
|
||||||
addi t0, zero, 1000
|
|
||||||
assert a0, ==, 0x100
|
|
||||||
1:
|
|
||||||
addi a1, a1, 1
|
|
||||||
blt a1, t0, 1b
|
|
||||||
sub a1, a1, a0
|
|
||||||
j 1f
|
|
||||||
addi a1, zero, 0
|
|
||||||
fail
|
|
||||||
1:
|
|
||||||
assert a1, ==, 744
|
|
||||||
add a0, zero, a1 ; set exit code to a1
|
|
||||||
addi a7, zero, SCALL_EXIT ; exit syscall code
|
|
||||||
scall
|
|
||||||
fail
|
|
Loading…
Reference in New Issue