Repository: AntonLydike/riscemu (language: python)

Issue:
Support the snitch extension
This will be an effort of multiple PRs, we would need to do:

The main goals of this effort are:

 - [ ] Add the `Xfrep` ISA extension
 - [x] Add the `Xssr` ISA extension

In order to achieve this, we need to do the following steps (at least):

 - [ ] Add `Zicsr` extension to the userspace (currently only implemented for the whole machine mode system CPU thingy)
    - Add a CSR bank to the CPU, I'm not sure if the design on the PrivCPU is a good one tbh.
 - [x] Add a "streaming register" bank, a rough version was drafted during the hackathon [in the snitch branch](https://github.com/AntonLydike/riscemu/blob/snitch/snitch/regs.py)
 - [ ] Figure out a way to make the CPU extensible in a way so that the `Xfrep` extension doesn't require a custom CPU class


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f1febae..76aeabd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
 
 # Upcoming
 
+ - Feature: Starting to add support for Snitch architecture (Xssr)
  - Rework: Improve handling of immediates, so that `beq a0, a1, 1b` and `beq a0, a1, -16` can both can be handled correctly.
  - BugFix: Fix some more errors in the RV32F implementation
  - Dev: Move to poetry for project development environment
diff --git a/examples/snitch_simple.asm b/examples/snitch_simple.asm
new file mode 100644
index 0000000..3b201f9
--- /dev/null
+++ b/examples/snitch_simple.asm
@@ -0,0 +1,41 @@
+.data
+
+vec0:
+.word 0x0, 0x3f800000, 0x40000000, 0x40400000, 0x40800000, 0x40a00000, 0x40c00000, 0x40e00000, 0x41000000, 0x41100000
+vec1:
+.word 0x0, 0x3f800000, 0x40000000, 0x40400000, 0x40800000, 0x40a00000, 0x40c00000, 0x40e00000, 0x41000000, 0x41100000
+dest:
+.space 40
+
+.text
+.globl main
+
+main:
+    // ssr config
+    ssr.configure   0, 10, 4
+    ssr.configure   1, 10, 4
+    ssr.configure   2, 10, 4
+
+    la              a0, vec0
+    ssr.read        a0, 0, 0
+
+    la              a0, vec1
+    ssr.read        a0, 1, 0
+
+    la              a0, dest
+    ssr.write       a0, 2, 0
+
+    ssr.enable
+
+    // set up loop
+    li a0, 10
+loop:
+    fadd.s      ft2, ft0, ft1
+
+    addi a0, a0, -1
+    bne  a0, zero, loop
+
+    // end of loop:
+    ssr.disable
+
+    ret
diff --git a/snitch/__init__.py b/snitch/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/snitch/__main__.py b/snitch/__main__.py
new file mode 100644
index 0000000..b0f28d5
--- /dev/null
+++ b/snitch/__main__.py
@@ -0,0 +1,26 @@
+"""
+RiscEmu (c) 2021 Anton Lydike
+
+SPDX-License-Identifier: MIT
+
+This file holds the logic for starting the emulator from the CLI
+"""
+import sys
+
+from .regs import StreamingRegs
+from .xssr import RV32_Xssr_pseudo
+from riscemu.riscemu_main import RiscemuMain
+
+
+class SnitchMain(RiscemuMain):
+    def configure_cpu(self):
+        super().configure_cpu()
+        self.cpu.regs = StreamingRegs(self.cpu.mmu)
+
+    def register_all_isas(self):
+        super().register_all_isas()
+        self.available_ins_sets.update({"Xssr": RV32_Xssr_pseudo})
+
+
+if __name__ == "__main__":
+    SnitchMain().run_from_cli(sys.argv)
diff --git a/snitch/regs.py b/snitch/regs.py
new file mode 100644
index 0000000..333d9d4
--- /dev/null
+++ b/snitch/regs.py
@@ -0,0 +1,103 @@
+from typing import Dict, List, Tuple
+from riscemu.registers import Registers
+from riscemu.MMU import MMU
+from riscemu.types import Int32, UInt32
+
+from dataclasses import dataclass
+
+from enum import Enum
+
+from riscemu.types.float32 import Float32
+
+
+class StreamMode(Enum):
+    READ = 1
+    WRITE = 2
+    # READWRITE = 3
+
+
+@dataclass
+class StreamDef:
+    base: int = 0
+    """
+    Base address to read from
+    """
+    bound: int = 0
+    """
+    How many elements are in here
+    """
+    stride: int = 0
+    """
+    stride = register width
+    """
+    mode: StreamMode = StreamMode.READ
+    """
+    Differentiate between read/write
+    """
+    dim: int = 0
+    """
+    Supports nested loops
+    """
+    # internal:
+    pos: int = 0
+    """
+    Next element in stream
+    """
+
+
+class StreamingRegs(Registers):
+    mem: MMU
+    dm_by_id: List[StreamDef]
+    streams: Dict[str, StreamDef]
+    enabled: bool
+
+    def __init__(
+        self,
+        mem: MMU,
+        xssr_regs: Tuple[str] = ("ft0", "ft1", "ft2"),
+        infinite_regs: bool = False,
+    ):
+        self.mem = mem
+        self.enabled = False
+        self.streams = dict()
+        self.dm_by_id = []
+        for reg in xssr_regs:
+            stream_def = StreamDef()
+            self.dm_by_id.append(stream_def)
+            self.streams[reg] = stream_def
+        super().__init__(infinite_regs)
+
+    def get_f(self, reg, mark_read=True) -> "Float32":
+        if not self.enabled or reg not in self.streams:
+            return super().get_f(reg, mark_read)
+
+        # do the streaming stuff:
+        stream = self.streams[reg]
+        # TODO: Implement other modes
+        assert stream.mode is StreamMode.READ
+        # TODO: Check overflow
+        # TODO: repetition
+        addr = stream.base + (stream.pos * stream.stride)
+        val = self.mem.read_float(addr)
+        # increment pos
+        print(
+            "stream: got val {} from addr 0x{:x}, stream {}".format(val, addr, stream)
+        )
+        stream.pos += 1
+        return val
+
+    def set_f(self, reg, val: "Float32", mark_set=True) -> bool:
+        if not self.enabled or reg not in self.streams:
+            return super().set_f(reg, mark_set)
+
+        stream = self.streams[reg]
+        assert stream.mode is StreamMode.WRITE
+
+        addr = stream.base + (stream.pos * stream.stride)
+        self.mem.write(addr, 4, bytearray(val.bytes))
+
+        print(
+            "stream: wrote val {} into addr 0x{:x}, stream {}".format(val, addr, stream)
+        )
+        stream.pos += 1
+        return True
diff --git a/snitch/xssr.py b/snitch/xssr.py
new file mode 100644
index 0000000..1c90d1a
--- /dev/null
+++ b/snitch/xssr.py
@@ -0,0 +1,41 @@
+from riscemu.instructions.instruction_set import InstructionSet, Instruction
+
+from .regs import StreamingRegs, StreamDef, StreamMode
+
+
+class RV32_Xssr_pseudo(InstructionSet):
+    def instruction_ssr_enable(self, ins: Instruction):
+        self._stream.enabled = True
+
+    def instruction_ssr_disable(self, ins: Instruction):
+        self._stream.enabled = False
+
+    def instruction_ssr_configure(self, ins: Instruction):
+        dm = ins.get_imm(0).abs_value.value
+        bound = ins.get_imm(1).abs_value.value
+        stride = ins.get_imm(2).abs_value.value
+
+        self._stream.dm_by_id[dm].bound = bound
+        self._stream.dm_by_id[dm].stride = stride
+
+    def instruction_ssr_read(self, ins: Instruction):
+        base_pointer = ins.get_reg(0)
+        dm = ins.get_imm(1).abs_value.value
+        dim = ins.get_imm(2).abs_value.value
+
+        self._stream.dm_by_id[dm].base = self.regs.get(base_pointer).value
+        self._stream.dm_by_id[dm].dim = dim
+        self._stream.dm_by_id[dm].mode = StreamMode.READ
+
+    def instruction_ssr_write(self, ins: Instruction):
+        base_pointer = ins.get_reg(0)
+        dm = ins.get_imm(1).abs_value.value
+        dim = ins.get_imm(2).abs_value.value
+
+        self._stream.dm_by_id[dm].base = self.regs.get(base_pointer).value
+        self._stream.dm_by_id[dm].dim = dim
+        self._stream.dm_by_id[dm].mode = StreamMode.WRITE
+
+    @property
+    def _stream(self) -> StreamingRegs:
+        return self.cpu.regs  # type: ignore


Test patch:
diff --git a/test/filecheck/snitch_simple.asm b/test/filecheck/snitch_simple.asm
new file mode 100644
index 0000000..3cdab19
--- /dev/null
+++ b/test/filecheck/snitch_simple.asm
@@ -0,0 +1,74 @@
+// RUN: python3 -m snitch %s -o libc | filecheck %s
+
+.data
+
+vec0:
+.word 0x0, 0x3f800000, 0x40000000, 0x40400000, 0x40800000, 0x40a00000, 0x40c00000, 0x40e00000, 0x41000000, 0x41100000
+vec1:
+.word 0x0, 0x3f800000, 0x40000000, 0x40400000, 0x40800000, 0x40a00000, 0x40c00000, 0x40e00000, 0x41000000, 0x41100000
+dest:
+.space 40
+
+.text
+.globl main
+
+main:
+    // ssr config
+    ssr.configure   0, 10, 4
+    ssr.configure   1, 10, 4
+    ssr.configure   2, 10, 4
+
+    la              a0, vec0
+    ssr.read        a0, 0, 0
+
+    la              a0, vec1
+    ssr.read        a0, 1, 0
+
+    la              a0, dest
+    ssr.write       a0, 2, 0
+
+    ssr.enable
+
+    // set up loop
+    li a0, 100
+loop:
+    fadd.s      ft2, ft0, ft1
+
+    addi a0, a0, -1
+    bne  a0, zero, loop
+
+    // end of loop:
+    ssr.disable
+
+    ret
+
+//CHECK: stream: got val 0.0 from addr 0x80148, stream StreamDef(base=524616, bound=10, stride=4, mode=<StreamMode.READ: 1>, dim=0, pos=0)
+//CHECK_NEXT: stream: got val 0.0 from addr 0x80170, stream StreamDef(base=524656, bound=10, stride=4, mode=<StreamMode.READ: 1>, dim=0, pos=0)
+//CHECK_NEXT: stream: wrote val 0.0 into addr 0x80198, stream StreamDef(base=524696, bound=10, stride=4, mode=<StreamMode.WRITE: 2>, dim=0, pos=0)
+//CHECK_NEXT: stream: got val 1.0 from addr 0x8014c, stream StreamDef(base=524616, bound=10, stride=4, mode=<StreamMode.READ: 1>, dim=0, pos=1)
+//CHECK_NEXT: stream: got val 1.0 from addr 0x80174, stream StreamDef(base=524656, bound=10, stride=4, mode=<StreamMode.READ: 1>, dim=0, pos=1)
+//CHECK_NEXT: stream: wrote val 2.0 into addr 0x8019c, stream StreamDef(base=524696, bound=10, stride=4, mode=<StreamMode.WRITE: 2>, dim=0, pos=1)
+//CHECK_NEXT: stream: got val 2.0 from addr 0x80150, stream StreamDef(base=524616, bound=10, stride=4, mode=<StreamMode.READ: 1>, dim=0, pos=2)
+//CHECK_NEXT: stream: got val 2.0 from addr 0x80178, stream StreamDef(base=524656, bound=10, stride=4, mode=<StreamMode.READ: 1>, dim=0, pos=2)
+//CHECK_NEXT: stream: wrote val 4.0 into addr 0x801a0, stream StreamDef(base=524696, bound=10, stride=4, mode=<StreamMode.WRITE: 2>, dim=0, pos=2)
+//CHECK_NEXT: stream: got val 3.0 from addr 0x80154, stream StreamDef(base=524616, bound=10, stride=4, mode=<StreamMode.READ: 1>, dim=0, pos=3)
+//CHECK_NEXT: stream: got val 3.0 from addr 0x8017c, stream StreamDef(base=524656, bound=10, stride=4, mode=<StreamMode.READ: 1>, dim=0, pos=3)
+//CHECK_NEXT: stream: wrote val 6.0 into addr 0x801a4, stream StreamDef(base=524696, bound=10, stride=4, mode=<StreamMode.WRITE: 2>, dim=0, pos=3)
+//CHECK_NEXT: stream: got val 4.0 from addr 0x80158, stream StreamDef(base=524616, bound=10, stride=4, mode=<StreamMode.READ: 1>, dim=0, pos=4)
+//CHECK_NEXT: stream: got val 4.0 from addr 0x80180, stream StreamDef(base=524656, bound=10, stride=4, mode=<StreamMode.READ: 1>, dim=0, pos=4)
+//CHECK_NEXT: stream: wrote val 8.0 into addr 0x801a8, stream StreamDef(base=524696, bound=10, stride=4, mode=<StreamMode.WRITE: 2>, dim=0, pos=4)
+//CHECK_NEXT: stream: got val 5.0 from addr 0x8015c, stream StreamDef(base=524616, bound=10, stride=4, mode=<StreamMode.READ: 1>, dim=0, pos=5)
+//CHECK_NEXT: stream: got val 5.0 from addr 0x80184, stream StreamDef(base=524656, bound=10, stride=4, mode=<StreamMode.READ: 1>, dim=0, pos=5)
+//CHECK_NEXT: stream: wrote val 10.0 into addr 0x801ac, stream StreamDef(base=524696, bound=10, stride=4, mode=<StreamMode.WRITE: 2>, dim=0, pos=5)
+//CHECK_NEXT: stream: got val 6.0 from addr 0x80160, stream StreamDef(base=524616, bound=10, stride=4, mode=<StreamMode.READ: 1>, dim=0, pos=6)
+//CHECK_NEXT: stream: got val 6.0 from addr 0x80188, stream StreamDef(base=524656, bound=10, stride=4, mode=<StreamMode.READ: 1>, dim=0, pos=6)
+//CHECK_NEXT: stream: wrote val 12.0 into addr 0x801b0, stream StreamDef(base=524696, bound=10, stride=4, mode=<StreamMode.WRITE: 2>, dim=0, pos=6)
+//CHECK_NEXT: stream: got val 7.0 from addr 0x80164, stream StreamDef(base=524616, bound=10, stride=4, mode=<StreamMode.READ: 1>, dim=0, pos=7)
+//CHECK_NEXT: stream: got val 7.0 from addr 0x8018c, stream StreamDef(base=524656, bound=10, stride=4, mode=<StreamMode.READ: 1>, dim=0, pos=7)
+//CHECK_NEXT: stream: wrote val 14.0 into addr 0x801b4, stream StreamDef(base=524696, bound=10, stride=4, mode=<StreamMode.WRITE: 2>, dim=0, pos=7)
+//CHECK_NEXT: stream: got val 8.0 from addr 0x80168, stream StreamDef(base=524616, bound=10, stride=4, mode=<StreamMode.READ: 1>, dim=0, pos=8)
+//CHECK_NEXT: stream: got val 8.0 from addr 0x80190, stream StreamDef(base=524656, bound=10, stride=4, mode=<StreamMode.READ: 1>, dim=0, pos=8)
+//CHECK_NEXT: stream: wrote val 16.0 into addr 0x801b8, stream StreamDef(base=524696, bound=10, stride=4, mode=<StreamMode.WRITE: 2>, dim=0, pos=8)
+//CHECK_NEXT: stream: got val 9.0 from addr 0x8016c, stream StreamDef(base=524616, bound=10, stride=4, mode=<StreamMode.READ: 1>, dim=0, pos=9)
+//CHECK_NEXT: stream: got val 9.0 from addr 0x80194, stream StreamDef(base=524656, bound=10, stride=4, mode=<StreamMode.READ: 1>, dim=0, pos=9)
+//CHECK_NEXT: stream: wrote val 18.0 into addr 0x801bc, stream StreamDef(base=524696, bound=10, stride=4, mode=<StreamMode.WRITE: 2>, dim=0, pos=9)