Control and Status Registers

This is a part of Writing a RISC-V Emulator in Rust. Our goal is running xv6, a small Unix-like OS, in your emulator eventually.

The source code used in this page is available at d0iasm/rvemu-for-book/03/.

The Goal of This Page

In this page, we will implement read-and-modify control and status registers (CSRs) instructions, which are defined at the Zicsr extension. CSRs are registers that store additional information of the result of instructions.

We will add Zicsr instructions, csrrw, csrrs, csrrc, csrrwi, csrrsi, and csrrci, to read and write CSRs.

Control and Status Registers (CSRs)

Control and status register (CSR) is a register that stores various information in CPU. RISC-V defines a separate address space of 4096 CSRs so we can have at most 4096 CSRs. RISC-V only allocates a part of address space so we can add custom CSRs in unused addresses. Also, not all CSRs are required on all implementations.

Fig 3.1-3.3 list the machine-level and supervisor CSRs that are currently allocated CSR addresses. The next page will talk about what machine-level (M-mode) and supervisor-level (S-mode) are.

We will support a part of the allocated CSRs used in xv6-riscv. The book only describes them in the following sections.

Fig 3.1 Machine-level CSRs 1 (Source: Table 2.5: Currently allocated RISC-V machine-level CSR addresses. in Volume II: Privileged Architecture)

Fig 3.1 Machine-level CSRs 1 (Source: Table 2.5: Currently allocated RISC-V machine-level CSR addresses. in Volume II: Privileged Architecture)

Fig 3.2 Machine-level CSRs 2 (Source: Table 2.6: Currently allocated RISC-V machine-level CSR addresses. in Volume II: Privileged Architecture)

Fig 3.2 Machine-level CSRs 2 (Source: Table 2.6: Currently allocated RISC-V machine-level CSR addresses. in Volume II: Privileged Architecture)

Fig 3.3 Supervisor-level CSRs (Source: Table 2.3: Currently allocated RISC-V supervisor-level CSR addresses. in Volume II: Privileged Architecture)

Fig 3.3 Supervisor-level CSRs (Source: Table 2.3: Currently allocated RISC-V supervisor-level CSR addresses. in Volume II: Privileged Architecture)

Status Registers (mstatus/sstatus)

The status registers, mstatus for M-mode and sstatus for S-mode, keep track of and control the CPU's current operating status.

mstatus is allocated at 0x300 and sstatus is allocated at 0x100. It means we can access status registers by 0x300 and 0x100.

Fig 3.4 and Fig 3.5 represent the format of mstatus and sstatus. The length of these regsiters is 64. Each bit is allocated to a different meaning and we can tell the status to the CPU by setting/unsetting bits.

A restricted view of mstatus appears as the `sstatusw register.

Fig 3.4 mstatus register (Source: Figure 3.6: Machine-mode status register (mstatus) for RV64. in Volume II: Privileged Architecture)

Fig 3.4 mstatus register (Source: Figure 3.6: Machine-mode status register (mstatus) for RV64. in Volume II: Privileged Architecture)

Fig 3.5 sstatus register (Source: Figure 4.2: Supervisor-mode status register (mstatus) for RV64. in Volume II: Privileged Architecture)

Fig 3.5 sstatus register (Source: Figure 4.2: Supervisor-mode status register (mstatus) for RV64. in Volume II: Privileged Architecture)

MIE and SIE are global insterrupt bits, M for M-mode and S for S-mode. When these bits are set, interrupts are globally enabled.

Trap-vector Base-address Registers (mtvec/stvecc)

The trap-vector base address registers, mtvec for M-mode and stvec for S-mode, trap vector configuration. mtvec is allocated at 0x303 and stvec is allocated at 0x105.

Fig 3.6 mtvec register (Source: Figure 3.9: Machine trap-vector base-address register (mtvec). in Volume II: Privileged Architecture)

Fig 3.6 mtvec register (Source: Figure 3.9: Machine trap-vector base-address register (mtvec). in Volume II: Privileged Architecture)

BASE contains the destination address when trap (an exception or an interrupt) occurs.

MODE can add alignment constraints on the value in BASE. When MODE is 0, the next program counter is set to the value in BASE is used as it is. When MODE is 1, the next program counter is set to the value of BASE + 4 × cause. The value of cause can be gotten in trap cause registers.

Machine Trap Delegation Registers (medeleg/mideleg)

The trap delegation registers, medeleg for machine-level exception delegation and mideleg for machine-level interrupt delegation, indicate the certain exceptions and interrupts should be directly by a lower privileged level. medeleg is allocated at 0x302 and mideleg is allocated at 0x303.

Fig 3.7 medeleg register (Source: Figure 3.10: Machine Exception Delegation Register medeleg. in Volume II: Privileged Architecture)

Fig 3.7 medeleg register (Source: Figure 3.10: Machine Exception Delegation Register medeleg. in Volume II: Privileged Architecture)

Fig 3.8 mideleg register (Source: Figure 3.11: Machine Interrupt Delegation Register mideleg. in Volume II: Privileged Architecture)

Fig 3.8 mideleg register (Source: Figure 3.11: Machine Interrupt Delegation Register mideleg. in Volume II: Privileged Architecture)

By default, all trap should be handled in M-mode (highest privileged mode). These registers can delegate a corresponding trap to lower-level privileged mode.

Interrupt Registers (mip/mie/sip/sie)

The interrupt registers, mip and mie for M-mode and sip and sie for S-mode, contain the information about interrupts. "ip" is the abbreviation of "Interrupt Pending" and "ie" is "Interrupt Enable".

mip is allocated at 0x344, mie is 0x304, sip is 0x144, and sie is 0x104.

Fig 3.9 mip register (Source: Figure 3.12: Machine Interrupt-Pending Register (mip). in Volume II: Privileged Architecture)

Fig 3.9 mip register (Source: Figure 3.12: Machine Interrupt-Pending Register (mip). in Volume II: Privileged Architecture)

Fig 3.10 mie register (Source: Figure 3.13: Machine Interrupt-Enable Register (mie). in Volume II: Privileged Architecture)

Fig 3.10 mie register (Source: Figure 3.13: Machine Interrupt-Enable Register (mie). in Volume II: Privileged Architecture)

Fig 3.11 sip register (Source: Figure 4.4: Supervisor interrupt-pending register (sip). in Volume II: Privileged Architecture)

Fig 3.11 sip register (Source: Figure 4.4: Supervisor interrupt-pending register (sip). in Volume II: Privileged Architecture)

Fig 3.12 sie register (Source: Figure 4.5: Supervisor interrupt-enable register (sie). in Volume II: Privileged Architecture)

Fig 3.12 sie register (Source: Figure 4.5: Supervisor interrupt-enable register (sie). in Volume II: Privileged Architecture)

Exception Program Counters (mepc/sepc)

The exception program counters, mepc for M-mode and sepc for S-mode, contain the information about the program counter when an exception happens.

mepc is allocated at 0x341 and sepc is 0x141.

Fig 3.13 mepc register (Source: Figure 3.24: Machine exception program counter register. in Volume II: Privileged Architecture)

Fig 3.13 mepc register (Source: Figure 3.24: Machine exception program counter register. in Volume II: Privileged Architecture)

Fig 3.14 sepc register (Source: Figure 4.10: Supervisor exception program counter register. in Volume II: Privileged Architecture)

Fig 3.14 sepc register (Source: Figure 4.10: Supervisor exception program counter register. in Volume II: Privileged Architecture)

Trap Cause Registers (mcause/scause)

The trap cause registers, mcause for M-mode and scause for S-Mode, contain a code indicating the event that caused the trap.

mcause is allocated at 0x342 and scause is 0x142.

Fig 3.15 mcause register (Source: Figure 3.25: Machine Cause register mcause. in Volume II: Privileged Architecture)

Fig 3.15 mcause register (Source: Figure 3.25: Machine Cause register mcause. in Volume II: Privileged Architecture)

Fig 3.16 scause register (Source: Figure 4.11: Supervisor Cause register scause. in Volume II: Privileged Architecture)

Fig 3.16 scause register (Source: Figure 4.11: Supervisor Cause register scause. in Volume II: Privileged Architecture)

Trap Value Registers (mtval/stval)

The trap value registers, mtval for M-mode and stval for S-mode, contain the information about a trap.

mtval is allocated at 0x343 and stval is 0x143.

Fig 3.17 mtval register (Source: Figure 3.26: Machine Trap Value register. in Volume II: Privileged Architecture)

Fig 3.17 mtval register (Source: Figure 3.26: Machine Trap Value register. in Volume II: Privileged Architecture)

Fig 3.18 stval register (Source: Figure 4.12: Supervisor Trap Value register. in Volume II: Privileged Architecture)

Fig 3.18 stval register (Source: Figure 4.12: Supervisor Trap Value register. in Volume II: Privileged Architecture)

Supervisor Address Translation and Protection Register (satp)

The supervisor address translation and protection register, satp for S-mode, controls supervisor-mode address translation and protection.

satp is allocated at 0x180.

Fig 3.19 satp register (Source: Figure 4.14: RV64 Supervisor address translation and protection register satp, for MODE values Bare, Sv39, and Sv48. in Volume II: Privileged Architecture)

Fig 3.19 satp register (Source: Figure 4.14: RV64 Supervisor address translation and protection register satp, for MODE values Bare, Sv39, and Sv48. in Volume II: Privileged Architecture)

Add CSRs array to CPU

First, we're going to add csrs field to Cpu structure. We now have 4 fields including regs, pc, and bus in CPU.

cpu.rs


#![allow(unused)]
fn main() {
pub struct Cpu {
    pub regs: [u64; 32],
    pub pc: u64,
    /// Control and status registers. RISC-V ISA sets aside a 12-bit encoding
    /// space (csr[11:0]) for up to 4096 CSRs.
    pub csrs: [u64; 4096],
    pub bus: Bus,
}
}

Load/store CSR methods

Second, we're going to add wrapper methods for load/store CSRs because restricted views of CSRs for M-mode appear as CSRs for S-mode. Wrapper methods can mimic this restricted views.


#![allow(unused)]
fn main() {
fn load_csr(&self, addr: usize) -> u64 {
    match addr {
        SIE => self.csrs[MIE] & self.csrs[MIDELEG],
        _ => self.csrs[addr],
    }
}
}

#![allow(unused)]
fn main() {
fn store_csr(&mut self, addr: usize, value: u64) {
    match addr {
        SIE => {
            self.csrs[MIE] =
                (self.csrs[MIE] & !self.csrs[MIDELEG]) | (value & self.csrs[MIDELEG]);
        }
        _ => self.csrs[addr] = value,
    }
}
}

Zicsr Standard Extension

Fig 3.20 is the list for instructions to read-modify-write CSRs. RISC-V calls the 6 instructions Zicsr standard extension.

A CSR specifier is encoded in the 12-bit csr field of the instruction placed at 31–20 bits. There are 12 bits for specifying which CSR is selected so that we have 4096 CSRs (=2**12). The uimm field is unsigned immediate value, a 5-bit zero-extended.

Fig 3.20 RV64Zicsr Instruction Set (Source: RV32/RV64 Zicsr Standard Extension table. in Volume I: Unprivileged ISA)

Fig 3.20 RV64Zicsr Instruction Set (Source: RV32/RV64 Zicsr Standard Extension table in Volume I: Unprivileged ISA)