Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into feat/guest-example
Browse files Browse the repository at this point in the history
  • Loading branch information
kunxian-xia committed Oct 31, 2024
2 parents 8b8f820 + 69d6be7 commit 3e9e8d2
Show file tree
Hide file tree
Showing 25 changed files with 298 additions and 207 deletions.
4 changes: 4 additions & 0 deletions Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ CUR_TARGET = { script = ['''
'''] }
RAYON_NUM_THREADS = "${CORE}"

[tasks.build]
# Override the default `--all-features`, that's broken, because some of our features are mutually exclusive.
args = ["build"]

[tasks.tests]
args = [
"test",
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ Ceno is built in Rust, so [installing the Rust toolchain](https://www.rust-lang.
cargo install cargo-make
```

You will also need to install the Risc-V target for Rust. You can do this with the following command:

```sh
rustup target add riscv32im-unknown-none-elf
```

## Building Ceno and running tests

To run the tests, you can use the following command:
Expand All @@ -29,4 +35,4 @@ cargo check
cargo clippy
```

Neither `cargo build` nor `cargo make build` work. That's a known problem and we're working on it.
Alas, `cargo build` doesn't work. That's a known problem and we're working on it. Please use `cargo make build` instead for now.
2 changes: 1 addition & 1 deletion ceno_emul/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ mod vm_state;
pub use vm_state::VMState;

mod rv32im;
pub use rv32im::{DecodedInstruction, EmuContext, InsnCategory, InsnCodes, InsnKind};
pub use rv32im::{DecodedInstruction, EmuContext, InsnCodes, InsnKind};

mod elf;
pub use elf::Program;
Expand Down
9 changes: 2 additions & 7 deletions ceno_emul/src/platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,6 @@ impl Platform {
(vma >> 8) as RegIdx
}

/// Virtual address of the program counter.
pub const fn pc_vma(&self) -> Addr {
// first 33 indices are reserved for registers X0..X31 and X32 (dark register)
self.register_vma(64)
}

// Startup.

pub const fn pc_base(&self) -> Addr {
Expand Down Expand Up @@ -129,6 +123,7 @@ impl Platform {
#[cfg(test)]
mod tests {
use super::*;
use crate::VMState;

#[test]
fn test_no_overlap() {
Expand All @@ -140,7 +135,7 @@ mod tests {
assert!(!p.is_ram(p.rom_start()));
assert!(!p.is_ram(p.rom_end()));
// Registers do not overlap with ROM or RAM.
for reg in [p.pc_vma(), p.register_vma(0), p.register_vma(31)] {
for reg in [p.register_vma(0), p.register_vma(VMState::REG_COUNT - 1)] {
assert!(!p.is_rom(reg));
assert!(!p.is_ram(reg));
}
Expand Down
136 changes: 75 additions & 61 deletions ceno_emul/src/rv32im.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ pub enum TrapCause {
pub struct DecodedInstruction {
insn: u32,
top_bit: u32,
// The bit fields of the instruction encoding, regardless of the instruction format.
func7: u32,
rs2: u32,
rs1: u32,
Expand All @@ -111,8 +112,9 @@ pub struct DecodedInstruction {
}

#[derive(Clone, Copy, Debug)]
pub enum InsnCategory {
enum InsnCategory {
Compute,
Branch,
Load,
Store,
System,
Expand Down Expand Up @@ -202,6 +204,9 @@ pub struct InsnCodes {
}

impl DecodedInstruction {
/// A virtual register which absorbs the writes to x0.
pub const RD_NULL: u32 = 32;

pub fn new(insn: u32) -> Self {
Self {
insn,
Expand All @@ -223,24 +228,15 @@ impl DecodedInstruction {
self.opcode
}

/// Get the rd field, regardless of the instruction format.
pub fn rd(&self) -> u32 {
self.rd
}

/// Get the register destination, or zero if the instruction does not write to a register.
pub fn rd_or_zero(&self) -> u32 {
/// The internal register destination. It is either the regular rd, or an internal RD_NULL if
/// the instruction does not write to a register or writes to x0.
pub fn rd_internal(&self) -> u32 {
match self.codes().format {
R | I | U | J => self.rd,
_ => 0,
R | I | U | J if self.rd != 0 => self.rd,
_ => Self::RD_NULL,
}
}

/// Get the funct3 field, regardless of the instruction format.
pub fn funct3(&self) -> u32 {
self.func3
}

/// Get the funct3 field, or zero if the instruction does not use funct3.
pub fn funct3_or_zero(&self) -> u32 {
match self.codes().format {
Expand Down Expand Up @@ -275,18 +271,13 @@ impl DecodedInstruction {
}
}

/// Get the funct7 field, regardless of the instruction format.
pub fn funct7(&self) -> u32 {
self.func7
}

/// Get the decoded immediate, or 2^shift, or the funct7 field, depending on the instruction format.
pub fn imm_or_funct7(&self) -> u32 {
match self.codes().format {
R => self.func7,
I => match self.codes().kind {
// decode the shift as a multiplication/division by 1 << immediate
SLLI | SRLI | SRAI => 1 << (self.imm_i() & 0x1f),
SLLI | SRLI | SRAI => 1 << self.imm_shamt(),
_ => self.imm_i(),
},
S => self.imm_s(),
Expand All @@ -296,47 +287,48 @@ impl DecodedInstruction {
}
}

/// Indicate whether the immediate is interpreted as a signed integer, and it is negative.
pub fn imm_is_negative(&self) -> bool {
/// Indicates if the immediate value, when signed, needs to be encoded as field negative.
/// example:
/// imm = ux::MAX - 1 implies
/// imm_field = FIELD_MODULUS - 1 if imm_field_is_negative
/// imm_field = ux::MAX - 1 otherwise
/// see InsnRecord::imm_or_funct7_field
pub fn imm_field_is_negative(&self) -> bool {
match self.codes() {
InsnCodes { format: R | U, .. } => false,
InsnCodes {
kind: SLLI | SRLI | SRAI | ADDI,
kind: SLLI | SRLI | SRAI | ADDI | SLTIU,
..
} => false,
_ => self.top_bit != 0,
}
}

pub fn sign_bit(&self) -> u32 {
self.top_bit
}

pub fn codes(&self) -> InsnCodes {
FastDecodeTable::get().lookup(self)
}

pub fn kind(&self) -> (InsnCategory, InsnKind) {
let i = FastDecodeTable::get().lookup(self);
(i.category, i.kind)
}

pub fn imm_b(&self) -> u32 {
fn imm_b(&self) -> u32 {
(self.top_bit * 0xfffff000)
| ((self.rd & 1) << 11)
| ((self.func7 & 0x3f) << 5)
| (self.rd & 0x1e)
}

pub fn imm_i(&self) -> u32 {
(self.top_bit * 0xfffff000) | (self.func7 << 5) | self.rs2
fn imm_i(&self) -> u32 {
(self.top_bit * 0xffff_f000) | (self.func7 << 5) | self.rs2
}

/// Shift amount field of SLLI, SRLI, SRAI.
fn imm_shamt(&self) -> u32 {
self.rs2
}

pub fn imm_s(&self) -> u32 {
fn imm_s(&self) -> u32 {
(self.top_bit * 0xfffff000) | (self.func7 << 5) | self.rd
}

pub fn imm_j(&self) -> u32 {
fn imm_j(&self) -> u32 {
(self.top_bit * 0xfff00000)
| (self.rs1 << 15)
| (self.func3 << 12)
Expand All @@ -345,7 +337,7 @@ impl DecodedInstruction {
| (self.rs2 & 0x1e)
}

pub fn imm_u(&self) -> u32 {
fn imm_u(&self) -> u32 {
self.insn & 0xfffff000
}
}
Expand Down Expand Up @@ -419,12 +411,12 @@ const RV32IM_ISA: InstructionTable = [
insn(I, SRAI, Compute, 0x13, 0x5, 0x20),
insn(I, SLTI, Compute, 0x13, 0x2, -1),
insn(I, SLTIU, Compute, 0x13, 0x3, -1),
insn(B, BEQ, Compute, 0x63, 0x0, -1),
insn(B, BNE, Compute, 0x63, 0x1, -1),
insn(B, BLT, Compute, 0x63, 0x4, -1),
insn(B, BGE, Compute, 0x63, 0x5, -1),
insn(B, BLTU, Compute, 0x63, 0x6, -1),
insn(B, BGEU, Compute, 0x63, 0x7, -1),
insn(B, BEQ, Branch, 0x63, 0x0, -1),
insn(B, BNE, Branch, 0x63, 0x1, -1),
insn(B, BLT, Branch, 0x63, 0x4, -1),
insn(B, BGE, Branch, 0x63, 0x5, -1),
insn(B, BLTU, Branch, 0x63, 0x6, -1),
insn(B, BGEU, Branch, 0x63, 0x7, -1),
insn(J, JAL, Compute, 0x6f, -1, -1),
insn(I, JALR, Compute, 0x67, 0x0, -1),
insn(U, LUI, Compute, 0x37, -1, -1),
Expand Down Expand Up @@ -557,6 +549,7 @@ impl Emulator {

if match insn.category {
InsnCategory::Compute => self.step_compute(ctx, insn.kind, &decoded)?,
InsnCategory::Branch => self.step_branch(ctx, insn.kind, &decoded)?,
InsnCategory::Load => self.step_load(ctx, insn.kind, &decoded)?,
InsnCategory::Store => self.step_store(ctx, insn.kind, &decoded)?,
InsnCategory::System => self.step_system(ctx, insn.kind, &decoded)?,
Expand All @@ -578,15 +571,7 @@ impl Emulator {

let pc = ctx.get_pc();
let mut new_pc = pc + WORD_SIZE;
let mut rd = decoded.rd;
let imm_i = decoded.imm_i();
let mut br_cond = |cond| -> u32 {
rd = 0;
if cond {
new_pc = pc.wrapping_add(decoded.imm_b());
}
0
};
let out = match kind {
// Instructions that do not read rs1 nor rs2.
JAL => {
Expand Down Expand Up @@ -654,12 +639,6 @@ impl Emulator {
0
}
}
BEQ => br_cond(rs1 == rs2),
BNE => br_cond(rs1 != rs2),
BLT => br_cond((rs1 as i32) < (rs2 as i32)),
BGE => br_cond((rs1 as i32) >= (rs2 as i32)),
BLTU => br_cond(rs1 < rs2),
BGEU => br_cond(rs1 >= rs2),
MUL => rs1.wrapping_mul(rs2),
MULH => {
(sign_extend_u32(rs1).wrapping_mul(sign_extend_u32(rs2)) >> 32)
Expand Down Expand Up @@ -705,7 +684,42 @@ impl Emulator {
if !new_pc.is_aligned() {
return ctx.trap(TrapCause::InstructionAddressMisaligned);
}
ctx.store_register(rd as usize, out)?;
ctx.store_register(decoded.rd_internal() as usize, out)?;
ctx.set_pc(new_pc);
Ok(true)
}

fn step_branch<M: EmuContext>(
&self,
ctx: &mut M,
kind: InsnKind,
decoded: &DecodedInstruction,
) -> Result<bool> {
use InsnKind::*;

let pc = ctx.get_pc();
let rs1 = ctx.load_register(decoded.rs1 as RegIdx)?;
let rs2 = ctx.load_register(decoded.rs2 as RegIdx)?;

let taken = match kind {
BEQ => rs1 == rs2,
BNE => rs1 != rs2,
BLT => (rs1 as i32) < (rs2 as i32),
BGE => (rs1 as i32) >= (rs2 as i32),
BLTU => rs1 < rs2,
BGEU => rs1 >= rs2,
_ => unreachable!("Illegal branch instruction: {:?}", kind),
};

let new_pc = if taken {
pc.wrapping_add(decoded.imm_b())
} else {
pc + WORD_SIZE
};

if !new_pc.is_aligned() {
return ctx.trap(TrapCause::InstructionAddressMisaligned);
}
ctx.set_pc(new_pc);
Ok(true)
}
Expand Down Expand Up @@ -757,7 +771,7 @@ impl Emulator {
}
_ => unreachable!(),
};
ctx.store_register(decoded.rd as usize, out)?;
ctx.store_register(decoded.rd_internal() as usize, out)?;
ctx.set_pc(ctx.get_pc() + WORD_SIZE);
Ok(true)
}
Expand Down
4 changes: 3 additions & 1 deletion ceno_emul/src/tracer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,9 @@ impl StepRecord {
previous_cycle,
}),
rd: rd.map(|rd| WriteOp {
addr: CENO_PLATFORM.register_vma(insn.rd() as RegIdx).into(),
addr: CENO_PLATFORM
.register_vma(insn.rd_internal() as RegIdx)
.into(),
value: rd,
previous_cycle,
}),
Expand Down
23 changes: 8 additions & 15 deletions ceno_emul/src/vm_state.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
use std::collections::HashMap;

use super::rv32im::EmuContext;
use crate::{
PC_STEP_SIZE, Program,
addr::{ByteAddr, RegIdx, Word, WordAddr},
platform::Platform,
rv32im::{DecodedInstruction, Emulator, TrapCause},
tracer::{Change, StepRecord, Tracer},
};
use crate::{Program, addr::{ByteAddr, RegIdx, Word, WordAddr}, platform::Platform, rv32im::{DecodedInstruction, Emulator, TrapCause}, tracer::{Change, StepRecord, Tracer}, PC_STEP_SIZE};
use anyhow::{Result, anyhow};
use std::{iter::from_fn, ops::Deref, sync::Arc};

Expand All @@ -18,13 +12,17 @@ pub struct VMState {
pc: Word,
/// Map a word-address (addr/4) to a word.
memory: HashMap<WordAddr, Word>,
registers: [Word; 32 + 1], // +1 for "dark" register
registers: [Word; VMState::REG_COUNT],
// Termination.
halted: bool,
tracer: Tracer,
}

impl VMState {
/// The number of registers that the VM uses.
/// 32 architectural registers + 1 register RD_NULL for dark writes to x0.
pub const REG_COUNT: usize = 32 + 1;

pub fn new(platform: Platform, program: Program) -> Self {
let pc = program.entry;
let program = Arc::new(program);
Expand All @@ -34,7 +32,7 @@ impl VMState {
platform,
program: program.clone(),
memory: HashMap::new(),
registers: [0; 32 + 1],
registers: [0; VMState::REG_COUNT],
halted: false,
tracer: Tracer::new(),
};
Expand Down Expand Up @@ -151,12 +149,7 @@ impl EmuContext for VMState {
}

/// Store a register and record this operation.
fn store_register(&mut self, mut idx: RegIdx, after: Word) -> Result<()> {
if idx == 0 {
// refer to https://github.com/scroll-tech/ceno/issues/245 for the idea of "dark" register.
// 32 is the index of 'dark' register
idx = 32;
}
fn store_register(&mut self, idx: RegIdx, after: Word) -> Result<()> {
if idx != 0 {
let before = self.peek_register(idx);
self.tracer.store_register(idx, Change { before, after });
Expand Down
Loading

0 comments on commit 3e9e8d2

Please sign in to comment.