Skip to content

Commit 59250e7

Browse files
authored
Merge pull request #2753 from o1-labs/sai/instruction-decoding-riscv32
create interpreter implementation and adding instruction decoder for riscvi32
2 parents fe68e1c + 0681bf0 commit 59250e7

File tree

2 files changed

+211
-12
lines changed

2 files changed

+211
-12
lines changed

o1vm/src/interpreters/riscv32i/interpreter.rs

+11-10
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,11 @@ pub enum IInstruction {
5555
SetLessThanImmediateUnsigned, // sltiu
5656

5757
AddImmediate, // addi
58-
AndImmediate, // andi
5958
XorImmediate, // xori
6059
OrImmediate, // ori
60+
AndImmediate, // andi
61+
62+
JumpAndLinkRegister, // jalr
6163
}
6264

6365
#[derive(
@@ -76,11 +78,11 @@ pub enum SInstruction {
7678
pub enum SBInstruction {
7779
#[default]
7880
BranchEq, // beq
79-
BranchNeq, // bne
80-
BranchLessThan, // blt
81-
BranchGe, // bge
82-
BranchLessThanUnsigned, // bltu
83-
BranchGreaterThanEqual, // bgeu
81+
BranchNeq, // bne
82+
BranchLessThan, // blt
83+
BranchGreaterThanEqual, // bge
84+
BranchLessThanUnsigned, // bltu
85+
BranchGreaterThanEqualUnsigned, // bgeu
8486
}
8587

8688
#[derive(
@@ -99,7 +101,6 @@ pub enum UInstruction {
99101
pub enum UJInstruction {
100102
#[default]
101103
JumpAndLink, // jal
102-
JumpAndLinkRegister, // jalr
103104
}
104105

105106
#[derive(
@@ -220,6 +221,7 @@ impl std::fmt::Display for IInstruction {
220221
IInstruction::XorImmediate => write!(f, "xori"),
221222
IInstruction::OrImmediate => write!(f, "ori"),
222223
IInstruction::AndImmediate => write!(f, "andi"),
224+
IInstruction::JumpAndLinkRegister => write!(f, "jalr"),
223225
}
224226
}
225227
}
@@ -240,9 +242,9 @@ impl std::fmt::Display for SBInstruction {
240242
SBInstruction::BranchEq => write!(f, "beq"),
241243
SBInstruction::BranchNeq => write!(f, "bne"),
242244
SBInstruction::BranchLessThan => write!(f, "blt"),
243-
SBInstruction::BranchGe => write!(f, "bge"),
245+
SBInstruction::BranchGreaterThanEqual => write!(f, "bge"),
244246
SBInstruction::BranchLessThanUnsigned => write!(f, "bltu"),
245-
SBInstruction::BranchGreaterThanEqual => write!(f, "bgeu"),
247+
SBInstruction::BranchGreaterThanEqualUnsigned => write!(f, "bgeu"),
246248
}
247249
}
248250
}
@@ -260,7 +262,6 @@ impl std::fmt::Display for UJInstruction {
260262
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
261263
match self {
262264
UJInstruction::JumpAndLink => write!(f, "jal"),
263-
UJInstruction::JumpAndLinkRegister => write!(f, "jalr"),
264265
}
265266
}
266267
}

o1vm/src/interpreters/riscv32i/witness.rs

+200-2
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22
// to the SAME register/memory addrss?
33
use super::{
44
column::Column,
5-
interpreter::{Instruction, InterpreterEnv},
5+
interpreter::{
6+
self, IInstruction, Instruction, InterpreterEnv, RInstruction, SBInstruction, SInstruction,
7+
SyscallInstruction, UInstruction, UJInstruction,
8+
},
69
registers::Registers,
710
INSTRUCTION_SET_SIZE, SCRATCH_SIZE,
811
};
912
use crate::{
10-
cannon::{PAGE_ADDRESS_MASK, PAGE_ADDRESS_SIZE, PAGE_SIZE},
13+
cannon::{State, PAGE_ADDRESS_MASK, PAGE_ADDRESS_SIZE, PAGE_SIZE},
1114
lookups::Lookup,
1215
};
1316
use ark_ff::Field;
@@ -567,6 +570,201 @@ impl<Fp: Field> InterpreterEnv for Env<Fp> {
567570
}
568571

569572
impl<Fp: Field> Env<Fp> {
573+
pub fn create(page_size: usize, state: State) -> Self {
574+
let initial_instruction_pointer = state.pc;
575+
let next_instruction_pointer = state.next_pc;
576+
577+
let selector = INSTRUCTION_SET_SIZE;
578+
579+
let mut initial_memory: Vec<(u32, Vec<u8>)> = state
580+
.memory
581+
.into_iter()
582+
// Check that the conversion from page data is correct
583+
.map(|page| (page.index, page.data))
584+
.collect();
585+
586+
for (_address, initial_memory) in initial_memory.iter_mut() {
587+
initial_memory.extend((0..(page_size - initial_memory.len())).map(|_| 0u8));
588+
assert_eq!(initial_memory.len(), page_size);
589+
}
590+
591+
let memory_offsets = initial_memory
592+
.iter()
593+
.map(|(offset, _)| *offset)
594+
.collect::<Vec<_>>();
595+
596+
let initial_registers = {
597+
Registers {
598+
general_purpose: state.registers,
599+
current_instruction_pointer: initial_instruction_pointer,
600+
next_instruction_pointer,
601+
heap_pointer: state.heap,
602+
}
603+
};
604+
605+
let mut registers = initial_registers.clone();
606+
registers[2] = 0x408004f0;
607+
// set the stack pointer to the top of the stack
608+
609+
Env {
610+
instruction_counter: state.step,
611+
memory: initial_memory.clone(),
612+
last_memory_accesses: [0usize; 3],
613+
memory_write_index: memory_offsets
614+
.iter()
615+
.map(|offset| (*offset, vec![0u64; page_size]))
616+
.collect(),
617+
last_memory_write_index_accesses: [0usize; 3],
618+
registers,
619+
registers_write_index: Registers::default(),
620+
scratch_state_idx: 0,
621+
scratch_state: fresh_scratch_state(),
622+
halt: state.exited,
623+
selector,
624+
}
625+
}
626+
627+
pub fn next_instruction_counter(&self) -> u64 {
628+
(self.normalized_instruction_counter() + 1) * MAX_ACC
629+
}
630+
631+
pub fn decode_instruction(&mut self) -> (Instruction, u32) {
632+
/* https://www.cs.cornell.edu/courses/cs3410/2024fa/assignments/cpusim/riscv-instructions.pdf */
633+
let instruction =
634+
((self.get_memory_direct(self.registers.current_instruction_pointer) as u32) << 24)
635+
| ((self.get_memory_direct(self.registers.current_instruction_pointer + 1) as u32)
636+
<< 16)
637+
| ((self.get_memory_direct(self.registers.current_instruction_pointer + 2) as u32)
638+
<< 8)
639+
| (self.get_memory_direct(self.registers.current_instruction_pointer + 3) as u32);
640+
let instruction = instruction.to_be(); // convert to big endian for more straightforward decoding
641+
println!(
642+
"Decoding instruction at address {:x} with value {:b}, with opcode",
643+
self.registers.current_instruction_pointer, instruction
644+
);
645+
646+
let opcode = {
647+
match instruction & 0b1111111 // bits 0-6
648+
{
649+
0b0110111 => Instruction::UType(UInstruction::LoadUpperImmediate),
650+
0b0010111 => Instruction::UType(UInstruction::AddUpperImmediate),
651+
0b1101111 => Instruction::UJType(UJInstruction::JumpAndLink),
652+
0b1100011 =>
653+
match (instruction >> 12) & 0x7 // bits 12-14 for func3
654+
{
655+
0b000 => Instruction::SBType(SBInstruction::BranchEq),
656+
0b001 => Instruction::SBType(SBInstruction::BranchNeq),
657+
0b100 => Instruction::SBType(SBInstruction::BranchLessThan),
658+
0b101 => Instruction::SBType(SBInstruction::BranchGreaterThanEqual),
659+
0b110 => Instruction::SBType(SBInstruction::BranchLessThanUnsigned),
660+
0b111 => Instruction::SBType(SBInstruction::BranchGreaterThanEqualUnsigned),
661+
_ => panic!("Unknown SBType instruction with full inst {}", instruction),
662+
},
663+
0b1100111 => Instruction::IType(IInstruction::JumpAndLinkRegister),
664+
0b0000011 =>
665+
match (instruction >> 12) & 0x7 // bits 12-14 for func3
666+
{
667+
0b000 => Instruction::IType(IInstruction::LoadByte),
668+
0b001 => Instruction::IType(IInstruction::LoadHalf),
669+
0b010 => Instruction::IType(IInstruction::LoadWord),
670+
0b100 => Instruction::IType(IInstruction::LoadByteUnsigned),
671+
0b101 => Instruction::IType(IInstruction::LoadHalfUnsigned),
672+
_ => panic!("Unknown IType instruction with full inst {}", instruction),
673+
},
674+
0b0100011 =>
675+
match (instruction >> 12) & 0x7 // bits 12-14 for func3
676+
{
677+
0b000 => Instruction::SType(SInstruction::StoreByte),
678+
0b001 => Instruction::SType(SInstruction::StoreHalf),
679+
0b010 => Instruction::SType(SInstruction::StoreWord),
680+
_ => panic!("Unknown SType instruction with full inst {}", instruction),
681+
},
682+
0b0010011 =>
683+
match (instruction >> 12) & 0x7 // bits 12-14 for func3
684+
{
685+
0b000 => Instruction::IType(IInstruction::AddImmediate),
686+
0b010 => Instruction::IType(IInstruction::SetLessThanImmediate),
687+
0b011 => Instruction::IType(IInstruction::SetLessThanImmediateUnsigned),
688+
0b100 => Instruction::IType(IInstruction::XorImmediate),
689+
0b110 => Instruction::IType(IInstruction::OrImmediate),
690+
0b111 => Instruction::IType(IInstruction::AndImmediate),
691+
0b001 => Instruction::IType(IInstruction::ShiftLeftLogicalImmediate),
692+
0b101 =>
693+
match (instruction >> 30) & 0x1 // bit 30 in simm component of IType
694+
{
695+
0b0 => Instruction::IType(IInstruction::ShiftRightLogicalImmediate),
696+
0b1 => Instruction::IType(IInstruction::ShiftRightArithmeticImmediate),
697+
_ => panic!("Unknown IType in shift right instructions with full inst {}", instruction),
698+
},
699+
_ => panic!("Unknown IType instruction with full inst {}", instruction),
700+
},
701+
0b0110011 =>
702+
match (instruction >> 12) & 0x7 // bits 12-14 for func3
703+
{
704+
0b000 =>
705+
match (instruction >> 30) & 0x1 // bit 30 of funct5 component in RType
706+
{
707+
0b0 => Instruction::RType(RInstruction::Add),
708+
0b1 => Instruction::RType(RInstruction::Sub),
709+
_ => panic!("Unknown RType in add/sub instructions with full inst {}", instruction),
710+
},
711+
0b001 => Instruction::RType(RInstruction::ShiftLeftLogical),
712+
0b010 => Instruction::RType(RInstruction::SetLessThan),
713+
0b011 => Instruction::RType(RInstruction::SetLessThanUnsigned),
714+
0b100 => Instruction::RType(RInstruction::Xor),
715+
0b101 =>
716+
match (instruction >> 30) & 0x1 // bit 30 of funct5 component in RType
717+
{
718+
0b0 => Instruction::RType(RInstruction::ShiftRightLogical),
719+
0b1 => Instruction::RType(RInstruction::ShiftRightArithmetic),
720+
_ => panic!("Unknown RType in shift right instructions with full inst {}", instruction),
721+
},
722+
0b110 => Instruction::RType(RInstruction::Or),
723+
0b111 => Instruction::RType(RInstruction::And),
724+
_ => panic!("Unknown RType 0110011 instruction with full inst {}", instruction),
725+
},
726+
0b0001111 =>
727+
match (instruction >> 12) & 0x7 // bits 12-14 for func3
728+
{
729+
0b000 => Instruction::RType(RInstruction::Fence),
730+
0b001 => Instruction::RType(RInstruction::FenceI),
731+
_ => panic!("Unknown RType 0001111 (Fence) instruction with full inst {}", instruction),
732+
},
733+
// FIXME: we should implement more syscalls here, and check the register state.
734+
// Even better, only one constructor call ecall, and in the
735+
// interpreter, we do the action depending on it
736+
0b1110011 => Instruction::SyscallType(SyscallInstruction::SyscallSuccess),
737+
_ => panic!("Unknown instruction with full inst {:b}, and opcode {:b}", instruction, instruction & 0b1111111),
738+
}
739+
};
740+
// display the opcode
741+
println!(
742+
"Decoded instruction {:?} with opcode {:?}",
743+
instruction, opcode
744+
);
745+
(opcode, instruction)
746+
}
747+
748+
/// Execute a single step in the RISCV32i program
749+
pub fn step(&mut self) -> Instruction {
750+
self.reset_scratch_state();
751+
let (opcode, _instruction) = self.decode_instruction();
752+
753+
interpreter::interpret_instruction(self, opcode);
754+
755+
self.instruction_counter = self.next_instruction_counter();
756+
757+
// Integer division by MAX_ACC to obtain the actual instruction count
758+
if self.halt {
759+
println!(
760+
"Halted at step={} instruction={:?}",
761+
self.normalized_instruction_counter(),
762+
opcode
763+
);
764+
}
765+
opcode
766+
}
767+
570768
pub fn reset_scratch_state(&mut self) {
571769
self.scratch_state_idx = 0;
572770
self.scratch_state = fresh_scratch_state();

0 commit comments

Comments
 (0)