ECE391 note

Context Switch

ra(x1) saved by _swtch,
And _swtch should act like an 'nop'
And tp is never used by compiler

Build ECE391 Env on Win64

On Msys2 MINGW64

cf. https://wiki.qemu.org/Hosts/W32

pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-glib2 mingw-w64-x86_64-gettext-runtime mingw-w64-x86_64-gcc mingw-w64-x86_64-binutils mingw-w64-x86_64-pixman mingw-w64-x86_64-SDL2 mingw-w64-x86_64-gtk3 mingw-w64-x86_64-riscv64-unknown-elf-gcc mingw-w64-x86_64-gdb-multiarch git diffutils zsh vim)

Please enable Developer Mode to support soft link without Administrator permission, otherwise it prompts

Found ninja-1.12.1 at D:/msys64/usr/bin/ninja.exe
Running postconf script 'D:/ZJUI/ECE391/qemu/build/pyvenv/bin/python3.exe D:/ZJUI/ECE391/qemu/scripts/symlink-install-tree.py'
Please enable Developer Mode to support soft link without Administrator permission
error making symbolic link D:/msys64/opt/toolchains/riscv/lib/libfdt.a
Traceback (most recent call last):
  File "D:/ZJUI/ECE391/qemu/scripts/symlink-install-tree.py", line 35, in <module>
    raise e
  File "D:/ZJUI/ECE391/qemu/scripts/symlink-install-tree.py", line 28, in <module>
    os.symlink(source, bundle_dest)
OSError: [WinError 1314] 客户端没有所需的特权。: 'D:/ZJUI/ECE391/qemu/build/subprojects/dtc/libfdt/libfdt.a' -> 'qemu-bundle/msys64/opt/toolchains/riscv/lib/libfdt.a'

./configure --prefix=/opt/toolchains/riscv --target-list=riscv32-softmmu,riscv64-softmmu --enable-gtk --enable-system --disable-werror
make -j
make install

Change zsh for MINGW session https://superuser.com/questions/961699/change-default-shell-on-msys2

sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

Set in mingw64.ini:

SHELL=/usr/bin/zsh

There is no PTS on windows so use telnet instead
source:https://www.uni-koeln.de/~pbogusze/posts/QEMU_serial_console.html

qemu-system-riscv64 -machine virt -bios none -kernel cp1.elf -m 8M -nographic -serial mon:stdio -serial telnet:localhost:4321,server,nowait

Add D:\msys64\mingw64\bin to PATH of Windows
Then telnet to attach to con1

telnet localhost 4321

[RISC-V ECE391]MP2

Some Programming/Reference Manuals

  • Volume 1, Unprivileged Specification version 20240411 PDF GitHub
  • Volume 2, Privileged Specification version 20240411 PDF GitHub
  • PLIC Specs v1.0.0
    For newest PLIC manual please refer to https://riscv.org/technical/specifications/ and https://github.com/riscv/riscv-plic-spec/releases
  • NS16550 UART(Texus Instrument)
    cf. Section 8.6 for register specification.

    Note on CSR instructions

    CSR can't be accessed directly, thus those instructions are needed.
    Only 6 instructions, CSR[RS,RC,RW,RSI,RCI,RWI],I is immediate.
    S for set, C for clear, W for write, other mnemonic(e.g. CSRR/CSRW) are pseudo-instructions with either rs1=x0 or ds=x0.
    For both CSRRS and CSRRC, if rs1=x0, then the instruction will not write to the CSR at all
    For both CSRRSI and CSRRCI, if uimm[4:0] field is zero, then the instruction will not write to the CSR at all
    But CAVEAT LECTOR!, if rs1=x0 or uimm[4:0]=0 for CSRRW or CSRRWI, then csr will be CLEARED

    IRQ number(PLIC source no./id) and MMAP(Memory Mapping) in ECE391 FA24

    It is modified based on https://github.com/qemu/qemu/blob/master/hw/riscv/virt.c
    7 uarts are added and IRQ number of RTC is moved to 9

    Ext ASM in C code

    Reference How to Use Inline Assembly Language in C Code

  • Do not expect a sequence of asm statements to remain perfectly consecutive after compilation, even when you are using the volatile qualifier. If certain instructions need to remain consecutive in the output, put them in a single multi-instruction asm statement.
  • \n\t to seperate assembly instructions in a single Assembly Template
    Input: using , delimiter

    [ [asmSymbolicName] ] constraint (cexpression)

    Output:

    [ [asmSymbolicName] ] constraint (cvariablename)
  • asmSymbolicName
    Specifies a symbolic name for the operand. Reference the name in the assembler template by enclosing it in square brackets (i.e. %[Value]). The scope of the name is the asm statement that contains the definition. Any valid C variable name is acceptable, including names already defined in the surrounding code. No two operands within the same asm statement can use the same symbolic name.
    When not using an asmSymbolicName, use the (zero-based) position of the operand in the list of operands in the assembler template. For example if there are two output operands and three inputs, use %2 in the template to refer to the first input operand, %3 for the second, and %4 for the third.

For example

uint32_t Mask = 1234;
uint32_t Index;
asm ("bsfl %[aMask], %[aIndex]"
     : [aIndex] "=r" (Index)
     : [aMask] "r" (Mask)
     : "cc");

if no asmSymbolicName

__asm__ ("btsl %2,%1\n\t" // Turn on zero-based bit #Offset in Base.
         "sbb %0,%0"      // Use the CF to calculate old.
   : "=r" (old), "+rm" (*Base)
   : "Ir" (Offset)
   : "cc");
return old;

output registers begin with 0, the number of input regs are after output regs.

If using I(immediate) as constraint, the input value must be known at assembly time or later.

Control Status Registers(CSR)

mstatus:
mip:
mie:
mtvec:
mcause:
mepc:

PLIC

Each source has a priority(0 is disabled, higher for high prio).
destination is a contex, which is a HART combined with mode(M/S)
every context (e.g. context 0) has a source enable(not prio).
PLIC Indicates which interrupt source raised the interrupt by

Trap entry

  • Places current PC into mepc CSR
  • Sets mstatus.MPP to previous privilege mode (current privilege mode is decided by path)
  • Sets mstatus.MPIE to mstatus.MIE
  • Sets mstatus.MIE to 0 (interrupts disabled on entry)
  • Sets PC to mtvec.BASE (if mode 0) or mtvec.BASE + 4×cause

    Trap exit

  • Sets mstatus.mie to mstatus.MPP ; sets MPIE to 1
  • Changes privilege mode to mstatus.MPP; sets MPP to 0
  • Set PC to mepc
    You will get to the vector addr of Externel M-Mode Interrupt(mtvec[1:0]==2b'01) or just BASE(mtvec[1:0]==2b'00), then check which source issued the interrupt, then go to the corresponding ISR(interrupt service routine) or what so-called interrupt handler.
    A CPU claims the interrupt to identify source
  • CPU reads special per-context claim register on PLIC
  • PLIC returns highest priority source number on read
  • PLIC will not offer the interrupt source to other CPUs
  • CPU has committed to servicing it
    CPU can always read claim register to poll for highest priority interrupt

    End procedure for PLIC(Interrupt Completion)

    When CPU is finished servicing the itnerrupt, it signals completion to the PLIC

  • CPU writes source number to its claim register on PLIC
  • This indicates CPU is done servicing the interrupt source

vscode添加RISC-V调试支持

vscode 前置

  1. 安装C/C++插件
  2. 开启Allow Breakpoints Everywhere Vscode 在汇编文件中添加调试断点
  3. Makefile中添加ASFLAGS = -g

    vscode文件设置

    在workspace下.vscode下新建启动文件与任务文件launch.json, tasks.json

    launch.json

    miDebuggerPath 填riscv-gdb绝对路径,program填要调的程序

    {
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Debug RISC-V",
            "type": "cppdbg",
            "request": "launch",
            "program":"${workspaceFolder}/demo.elf",
            "cwd": "${workspaceFolder}",
            "miDebuggerPath": "/opt/toolchains/riscv/bin/riscv64-unknown-elf-gdb",
            "miDebuggerServerAddress": "localhost:1234",
            "stopOnEntry": true,
            "preLaunchTask": "Run QEMU"
        }
    ]
    }

    tasks.json

command中修改调试器启动参数,修改对应启动elf文件。不要删除前面的一条命令echo 'QEMU started',这个是为了让vscode结束启动等待用

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Build",
            "type": "shell",
            "command": "make",
            "args": [
                ""
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "problemMatcher": [
                "$gcc"
            ]
        },
        {
            "label": "Run QEMU",
            "type": "shell",
            "command": "echo 'QEMU started';qemu-system-riscv64 -s -S -machine virt -bios none -kernel demo.elf -m 128M -serial mon:stdio -device bochs-display",
            "dependsOn": ["Build"],
            "args": [],
            "group": {
                "kind": "test",
                "isDefault": true
            },
            "isBackground": true,
            "problemMatcher": [
                {
                    "pattern": [
                        {
                            "regexp": ".",
                            "file": 1,
                            "location": 2,
                            "message": 3
                        }
                    ],
                    "background": {
                        "activeOnStart": true,
                        "beginsPattern": ".",
                        "endsPattern": ".",
                    }
                }
            ]
        }
    ]
}