From 4dd9b39b051521f901dfe5d8eb5572c41c6a2e15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Mon, 9 Dec 2024 18:46:56 +0100 Subject: [PATCH 1/4] dummy-keccak: draft ecall circuit --- .../instructions/riscv/dummy/dummy_circuit.rs | 8 +- .../instructions/riscv/dummy/dummy_ecall.rs | 89 +++++++++++++++++++ ceno_zkvm/src/instructions/riscv/dummy/mod.rs | 3 + ceno_zkvm/src/instructions/riscv/insn_base.rs | 24 +++-- 4 files changed, 114 insertions(+), 10 deletions(-) create mode 100644 ceno_zkvm/src/instructions/riscv/dummy/dummy_ecall.rs diff --git a/ceno_zkvm/src/instructions/riscv/dummy/dummy_circuit.rs b/ceno_zkvm/src/instructions/riscv/dummy/dummy_circuit.rs index bb85ad7c1..06562133c 100644 --- a/ceno_zkvm/src/instructions/riscv/dummy/dummy_circuit.rs +++ b/ceno_zkvm/src/instructions/riscv/dummy/dummy_circuit.rs @@ -94,7 +94,7 @@ pub struct DummyConfig { impl DummyConfig { #[allow(clippy::too_many_arguments)] - fn construct_circuit( + pub(super) fn construct_circuit( circuit_builder: &mut CircuitBuilder, kind: InsnKind, with_rs1: bool, @@ -212,7 +212,7 @@ impl DummyConfig { }) } - fn assign_instance( + pub(super) fn assign_instance( &self, instance: &mut [MaybeUninit], lk_multiplicity: &mut LkMultiplicity, @@ -263,4 +263,8 @@ impl DummyConfig { Ok(()) } + + pub(super) fn ts(&self) -> WitIn { + self.vm_state.ts + } } diff --git a/ceno_zkvm/src/instructions/riscv/dummy/dummy_ecall.rs b/ceno_zkvm/src/instructions/riscv/dummy/dummy_ecall.rs new file mode 100644 index 000000000..21b51ab7d --- /dev/null +++ b/ceno_zkvm/src/instructions/riscv/dummy/dummy_ecall.rs @@ -0,0 +1,89 @@ +use std::marker::PhantomData; + +use ceno_emul::{InsnKind, StepRecord}; +use ff_ext::ExtensionField; +use itertools::izip; + +use super::{super::insn_base::WriteMEM, dummy_circuit::DummyConfig}; +use crate::{ + circuit_builder::CircuitBuilder, error::ZKVMError, expression::Expression, + instructions::Instruction, witness::LkMultiplicity, +}; +use core::mem::MaybeUninit; + +trait EcallSpec { + const NAME: &'static str; + + const MEM_WRITE_COUNT: usize; +} + +/// DummyEcall can handle any instruction and produce its side-effects. +pub struct DummyEcall(PhantomData<(E, I)>); + +impl Instruction for DummyEcall { + type InstructionConfig = DummyEcallConfig; + + fn name() -> String { + format!("{}_DUMMY", I::NAME) + } + + fn construct_circuit( + circuit_builder: &mut CircuitBuilder, + ) -> Result { + let dummy_insn = DummyConfig::construct_circuit( + circuit_builder, + InsnKind::EANY, + true, + true, + false, + false, + false, + false, + )?; + + // TODO. + let mem_addr = Expression::ZERO; + let val_before = Expression::ZERO; + let val_after = Expression::ZERO; + + let mem_writes = (0..I::MEM_WRITE_COUNT) + .map(|_| { + WriteMEM::construct_circuit( + circuit_builder, + mem_addr.clone(), // TODO: + offset. + val_before.clone(), + val_after.clone(), + dummy_insn.ts(), + ) + }) + .collect::, _>>()?; + + Ok(DummyEcallConfig { + dummy_insn, + mem_writes, + }) + } + + fn assign_instance( + config: &Self::InstructionConfig, + instance: &mut [MaybeUninit], + lk_multiplicity: &mut LkMultiplicity, + step: &StepRecord, + ) -> Result<(), ZKVMError> { + let ops = &step.syscall().unwrap().mem_writes; + for (mem_write, op) in izip!(&config.mem_writes, ops) { + mem_write.assign_op(instance, lk_multiplicity, step.cycle(), op)?; + } + + config + .dummy_insn + .assign_instance(instance, lk_multiplicity, step) + } +} + +#[derive(Debug)] +pub struct DummyEcallConfig { + dummy_insn: DummyConfig, + + mem_writes: Vec, +} diff --git a/ceno_zkvm/src/instructions/riscv/dummy/mod.rs b/ceno_zkvm/src/instructions/riscv/dummy/mod.rs index 9c912f422..e37276de7 100644 --- a/ceno_zkvm/src/instructions/riscv/dummy/mod.rs +++ b/ceno_zkvm/src/instructions/riscv/dummy/mod.rs @@ -12,5 +12,8 @@ mod dummy_circuit; pub use dummy_circuit::DummyInstruction; +mod dummy_ecall; +pub use dummy_ecall::DummyEcall; + #[cfg(test)] mod test; diff --git a/ceno_zkvm/src/instructions/riscv/insn_base.rs b/ceno_zkvm/src/instructions/riscv/insn_base.rs index bcdae575f..f3ab2ca06 100644 --- a/ceno_zkvm/src/instructions/riscv/insn_base.rs +++ b/ceno_zkvm/src/instructions/riscv/insn_base.rs @@ -1,6 +1,7 @@ -use ceno_emul::{StepRecord, Word}; +use ceno_emul::{Cycle, StepRecord, Word, WriteOp}; use ff::Field; use ff_ext::ExtensionField; +use goldilocks::SmallField; use itertools::Itertools; use super::constants::{PC_STEP_SIZE, UINT_LIMBS, UInt}; @@ -332,17 +333,24 @@ impl WriteMEM { lk_multiplicity: &mut LkMultiplicity, step: &StepRecord, ) -> Result<(), ZKVMError> { - set_val!( - instance, - self.prev_ts, - step.memory_op().unwrap().previous_cycle - ); + let op = step.memory_op().unwrap(); + self.assign_op(instance, lk_multiplicity, step.cycle(), &op) + } + + pub fn assign_op( + &self, + instance: &mut [MaybeUninit], + lk_multiplicity: &mut LkMultiplicity, + cycle: Cycle, + op: &WriteOp, + ) -> Result<(), ZKVMError> { + set_val!(instance, self.prev_ts, op.previous_cycle); self.lt_cfg.assign_instance( instance, lk_multiplicity, - step.memory_op().unwrap().previous_cycle, - step.cycle() + Tracer::SUBCYCLE_MEM, + op.previous_cycle, + cycle + Tracer::SUBCYCLE_MEM, )?; Ok(()) From 9b941114c9e0a0ed5fb0c3465475fa5f9641d04f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Wed, 11 Dec 2024 18:40:03 +0100 Subject: [PATCH 2/4] dummy-keccak: Register and Memory ops. Test. --- ceno_emul/src/lib.rs | 3 + ceno_emul/src/rv32im_encode.rs | 7 ++ ceno_emul/src/syscalls.rs | 18 +-- ceno_emul/src/syscalls/keccak_permute.rs | 13 +- ceno_emul/src/test_utils.rs | 25 ++++ ceno_emul/tests/test_elf.rs | 8 +- .../instructions/riscv/dummy/dummy_ecall.rs | 117 +++++++++++++----- ceno_zkvm/src/instructions/riscv/dummy/mod.rs | 2 +- .../src/instructions/riscv/dummy/test.rs | 25 ++++ ceno_zkvm/src/instructions/riscv/insn_base.rs | 12 +- 10 files changed, 174 insertions(+), 56 deletions(-) create mode 100644 ceno_emul/src/test_utils.rs diff --git a/ceno_emul/src/lib.rs b/ceno_emul/src/lib.rs index 536887b7d..ee1b51a7a 100644 --- a/ceno_emul/src/lib.rs +++ b/ceno_emul/src/lib.rs @@ -21,3 +21,6 @@ mod rv32im_encode; pub use rv32im_encode::encode_rv32; mod syscalls; +pub use syscalls::{KECCAK_PERMUTE, keccak_permute::KECCAK_WORDS}; + +pub mod test_utils; diff --git a/ceno_emul/src/rv32im_encode.rs b/ceno_emul/src/rv32im_encode.rs index b6a80f32b..fc9d9e172 100644 --- a/ceno_emul/src/rv32im_encode.rs +++ b/ceno_emul/src/rv32im_encode.rs @@ -26,6 +26,13 @@ pub const fn encode_rv32(kind: InsnKind, rs1: u32, rs2: u32, rd: u32, imm: u32) } } +pub const fn load_immediate(rd: u32, imm: u32) -> [u32; 2] { + [ + encode_rv32(InsnKind::LUI, 0, 0, rd, imm), + encode_rv32(InsnKind::ORI, rd, 0, rd, imm), + ] +} + // R-Type // 25 20 15 12 7 0 // +------+-----+-----+--------+----+-------+ diff --git a/ceno_emul/src/syscalls.rs b/ceno_emul/src/syscalls.rs index e5b3413cd..7b80c1d53 100644 --- a/ceno_emul/src/syscalls.rs +++ b/ceno_emul/src/syscalls.rs @@ -1,8 +1,7 @@ use crate::{RegIdx, Tracer, VMState, Word, WordAddr, WriteOp}; use anyhow::Result; -use itertools::chain; -mod keccak_permute; +pub mod keccak_permute; pub const KECCAK_PERMUTE: u32 = 0x00_01_01_09; @@ -17,8 +16,8 @@ pub fn handle_syscall(vm: &VMState, function_code: u32) -> Result, - pub reg_accesses: Vec, + pub mem_ops: Vec, + pub reg_ops: Vec, } /// The effects of a syscall to apply on the VM. @@ -35,7 +34,7 @@ impl SyscallEffects { /// Iterate over the register values after the syscall. pub fn iter_reg_values(&self) -> impl Iterator + '_ { self.witness - .reg_accesses + .reg_ops .iter() .map(|op| (op.register_index(), op.value.after)) } @@ -43,15 +42,18 @@ impl SyscallEffects { /// Iterate over the memory values after the syscall. pub fn iter_mem_values(&self) -> impl Iterator + '_ { self.witness - .mem_writes + .mem_ops .iter() .map(|op| (op.addr, op.value.after)) } /// Keep track of the cycles of registers and memory accesses. pub fn finalize(mut self, tracer: &mut Tracer) -> SyscallWitness { - for op in chain(&mut self.witness.reg_accesses, &mut self.witness.mem_writes) { - op.previous_cycle = tracer.track_access(op.addr, 0); + for op in &mut self.witness.reg_ops { + op.previous_cycle = tracer.track_access(op.addr, Tracer::SUBCYCLE_RD); + } + for op in &mut self.witness.mem_ops { + op.previous_cycle = tracer.track_access(op.addr, Tracer::SUBCYCLE_MEM); } self.witness } diff --git a/ceno_emul/src/syscalls/keccak_permute.rs b/ceno_emul/src/syscalls/keccak_permute.rs index 05c04c872..f34acff7a 100644 --- a/ceno_emul/src/syscalls/keccak_permute.rs +++ b/ceno_emul/src/syscalls/keccak_permute.rs @@ -6,7 +6,7 @@ use crate::{Change, EmuContext, Platform, VMState, WORD_SIZE, WordAddr, WriteOp} use super::{SyscallEffects, SyscallWitness}; const KECCAK_CELLS: usize = 25; // u64 cells -const KECCAK_WORDS: usize = KECCAK_CELLS * 2; // u32 words +pub const KECCAK_WORDS: usize = KECCAK_CELLS * 2; // u32 words /// Trace the execution of a Keccak permutation. /// @@ -18,7 +18,7 @@ pub fn keccak_permute(vm: &VMState) -> SyscallEffects { let state_ptr = vm.peek_register(Platform::reg_arg0()); // Read the argument `state_ptr`. - let reg_accesses = vec![WriteOp::new_register_op( + let reg_ops = vec![WriteOp::new_register_op( Platform::reg_arg0(), Change::new(state_ptr, state_ptr), 0, // Cycle set later in finalize(). @@ -49,7 +49,7 @@ pub fn keccak_permute(vm: &VMState) -> SyscallEffects { }; // Write permuted state. - let mem_writes = izip!(addrs, input, output) + let mem_ops = izip!(addrs, input, output) .map(|(addr, before, after)| WriteOp { addr, value: Change { before, after }, @@ -57,12 +57,9 @@ pub fn keccak_permute(vm: &VMState) -> SyscallEffects { }) .collect_vec(); - assert_eq!(mem_writes.len(), KECCAK_WORDS); + assert_eq!(mem_ops.len(), KECCAK_WORDS); SyscallEffects { - witness: SyscallWitness { - mem_writes, - reg_accesses, - }, + witness: SyscallWitness { mem_ops, reg_ops }, next_pc: None, } } diff --git a/ceno_emul/src/test_utils.rs b/ceno_emul/src/test_utils.rs new file mode 100644 index 000000000..812e2fc88 --- /dev/null +++ b/ceno_emul/src/test_utils.rs @@ -0,0 +1,25 @@ +use crate::{ + CENO_PLATFORM, InsnKind, Platform, Program, StepRecord, VMState, encode_rv32, + rv32im_encode::load_immediate, syscalls::KECCAK_PERMUTE, +}; +use anyhow::Result; + +pub fn keccak_step() -> (StepRecord, Vec) { + let instructions = [ + // Call Keccak-f. + &load_immediate(Platform::reg_arg0() as u32, CENO_PLATFORM.ram.start)[..], + &load_immediate(Platform::reg_ecall() as u32, KECCAK_PERMUTE)[..], + &[encode_rv32(InsnKind::EANY, 0, 0, 0, 0)], + // Halt. + &load_immediate(Platform::reg_ecall() as u32, Platform::ecall_halt())[..], + &[encode_rv32(InsnKind::EANY, 0, 0, 0, 0)], + ] + .concat(); + + let pc = CENO_PLATFORM.pc_base(); + let program = Program::new(pc, pc, instructions.clone(), Default::default()); + let mut vm = VMState::new(CENO_PLATFORM, program.into()); + let steps = vm.iter_until_halt().collect::>>().unwrap(); + + (steps[4].clone(), instructions) +} diff --git a/ceno_emul/tests/test_elf.rs b/ceno_emul/tests/test_elf.rs index 0aa3da539..6e97ffcbc 100644 --- a/ceno_emul/tests/test_elf.rs +++ b/ceno_emul/tests/test_elf.rs @@ -100,15 +100,15 @@ fn test_ceno_rt_keccak() -> Result<()> { // Check the syscall effects. for (witness, expect) in izip!(syscalls, keccak_outs) { - assert_eq!(witness.reg_accesses.len(), 1); + assert_eq!(witness.reg_ops.len(), 1); assert_eq!( - witness.reg_accesses[0].register_index(), + witness.reg_ops[0].register_index(), Platform::reg_arg0() ); - assert_eq!(witness.mem_writes.len(), expect.len() * 2); + assert_eq!(witness.mem_ops.len(), expect.len() * 2); let got = witness - .mem_writes + .mem_ops .chunks_exact(2) .map(|write_ops| { assert_eq!( diff --git a/ceno_zkvm/src/instructions/riscv/dummy/dummy_ecall.rs b/ceno_zkvm/src/instructions/riscv/dummy/dummy_ecall.rs index 21b51ab7d..4b1b4f15e 100644 --- a/ceno_zkvm/src/instructions/riscv/dummy/dummy_ecall.rs +++ b/ceno_zkvm/src/instructions/riscv/dummy/dummy_ecall.rs @@ -1,65 +1,96 @@ use std::marker::PhantomData; -use ceno_emul::{InsnKind, StepRecord}; +use ceno_emul::{Change, InsnKind, KECCAK_WORDS, StepRecord, WORD_SIZE}; use ff_ext::ExtensionField; -use itertools::izip; +use itertools::Itertools; use super::{super::insn_base::WriteMEM, dummy_circuit::DummyConfig}; use crate::{ - circuit_builder::CircuitBuilder, error::ZKVMError, expression::Expression, - instructions::Instruction, witness::LkMultiplicity, + Value, + circuit_builder::CircuitBuilder, + error::ZKVMError, + expression::{ToExpr, WitIn}, + instructions::{ + Instruction, + riscv::{constants::UInt, insn_base::WriteRD}, + }, + set_val, + witness::LkMultiplicity, }; use core::mem::MaybeUninit; trait EcallSpec { const NAME: &'static str; - const MEM_WRITE_COUNT: usize; + const REG_OPS_COUNT: usize; + const MEM_OPS_COUNT: usize; } -/// DummyEcall can handle any instruction and produce its side-effects. -pub struct DummyEcall(PhantomData<(E, I)>); +pub struct KeccakSpec; -impl Instruction for DummyEcall { - type InstructionConfig = DummyEcallConfig; +impl EcallSpec for KeccakSpec { + const NAME: &'static str = "KECCAK"; + + const REG_OPS_COUNT: usize = 1; + const MEM_OPS_COUNT: usize = KECCAK_WORDS; +} + +/// LargeEcallDummy can handle any instruction and produce its effects, +/// including multiple memory operations. +/// +/// Unsafe: The content is not constrained. +pub struct LargeEcallDummy(PhantomData<(E, S)>); + +impl Instruction for LargeEcallDummy { + type InstructionConfig = LargeEcallConfig; fn name() -> String { - format!("{}_DUMMY", I::NAME) + format!("{}_DUMMY", S::NAME) } - fn construct_circuit( - circuit_builder: &mut CircuitBuilder, - ) -> Result { + fn construct_circuit(cb: &mut CircuitBuilder) -> Result { let dummy_insn = DummyConfig::construct_circuit( - circuit_builder, + cb, InsnKind::EANY, - true, - true, + true, // Read the ecall function code. + false, false, false, false, false, )?; - // TODO. - let mem_addr = Expression::ZERO; - let val_before = Expression::ZERO; - let val_after = Expression::ZERO; + let start_addr = cb.create_witin(|| "mem_addr"); + + let reg_writes = (0..S::REG_OPS_COUNT) + .map(|i| { + let val_after = UInt::new_unchecked(|| format!("reg_after_{}", i), cb)?; + + WriteRD::construct_circuit(cb, val_after.register_expr(), dummy_insn.ts()) + .map(|writer| (val_after, writer)) + }) + .collect::, _>>()?; + + let mem_writes = (0..S::MEM_OPS_COUNT) + .map(|i| { + let val_before = cb.create_witin(|| format!("mem_before_{}", i)); + let val_after = cb.create_witin(|| format!("mem_after_{}", i)); - let mem_writes = (0..I::MEM_WRITE_COUNT) - .map(|_| { WriteMEM::construct_circuit( - circuit_builder, - mem_addr.clone(), // TODO: + offset. - val_before.clone(), - val_after.clone(), + cb, + start_addr.expr() + (i * WORD_SIZE) as u64, + val_before.expr(), + val_after.expr(), dummy_insn.ts(), ) + .map(|writer| (Change::new(val_before, val_after), writer)) }) .collect::, _>>()?; - Ok(DummyEcallConfig { + Ok(LargeEcallConfig { dummy_insn, + start_addr, + reg_writes, mem_writes, }) } @@ -70,20 +101,38 @@ impl Instruction for DummyEcall { lk_multiplicity: &mut LkMultiplicity, step: &StepRecord, ) -> Result<(), ZKVMError> { - let ops = &step.syscall().unwrap().mem_writes; - for (mem_write, op) in izip!(&config.mem_writes, ops) { - mem_write.assign_op(instance, lk_multiplicity, step.cycle(), op)?; - } + let ops = &step.syscall().expect("syscall step"); + // Assign instruction. config .dummy_insn - .assign_instance(instance, lk_multiplicity, step) + .assign_instance(instance, lk_multiplicity, step)?; + + set_val!(instance, config.start_addr, u64::from(ops.mem_ops[0].addr)); + + // Assign registers. + for ((value, writer), op) in config.reg_writes.iter().zip_eq(&ops.reg_ops) { + value.assign_value(instance, Value::new_unchecked(op.value.after)); + writer.assign_op(instance, lk_multiplicity, step.cycle(), op)?; + } + + // Assign memory. + for ((value, writer), op) in config.mem_writes.iter().zip_eq(&ops.mem_ops) { + set_val!(instance, value.before, op.value.before as u64); + set_val!(instance, value.after, op.value.after as u64); + writer.assign_op(instance, lk_multiplicity, step.cycle(), op)?; + } + + Ok(()) } } #[derive(Debug)] -pub struct DummyEcallConfig { +pub struct LargeEcallConfig { dummy_insn: DummyConfig, - mem_writes: Vec, + reg_writes: Vec<(UInt, WriteRD)>, + + start_addr: WitIn, + mem_writes: Vec<(Change, WriteMEM)>, } diff --git a/ceno_zkvm/src/instructions/riscv/dummy/mod.rs b/ceno_zkvm/src/instructions/riscv/dummy/mod.rs index e37276de7..02114e737 100644 --- a/ceno_zkvm/src/instructions/riscv/dummy/mod.rs +++ b/ceno_zkvm/src/instructions/riscv/dummy/mod.rs @@ -13,7 +13,7 @@ mod dummy_circuit; pub use dummy_circuit::DummyInstruction; mod dummy_ecall; -pub use dummy_ecall::DummyEcall; +pub use dummy_ecall::LargeEcallDummy; #[cfg(test)] mod test; diff --git a/ceno_zkvm/src/instructions/riscv/dummy/test.rs b/ceno_zkvm/src/instructions/riscv/dummy/test.rs index df1eb0572..e46f8d68f 100644 --- a/ceno_zkvm/src/instructions/riscv/dummy/test.rs +++ b/ceno_zkvm/src/instructions/riscv/dummy/test.rs @@ -1,4 +1,5 @@ use ceno_emul::{Change, InsnKind, StepRecord, encode_rv32}; +use dummy_ecall::KeccakSpec; use goldilocks::GoldilocksExt2; use super::*; @@ -37,6 +38,30 @@ fn test_dummy_ecall() { MockProver::assert_satisfied_raw(&cb, raw_witin, &[insn_code], None, Some(lkm)); } +#[test] +fn test_dummy_keccak() { + type KeccakDummy = LargeEcallDummy; + + let mut cs = ConstraintSystem::::new(|| "riscv"); + let mut cb = CircuitBuilder::new(&mut cs); + let config = cb + .namespace( + || "keccak_dummy", + |cb| { + let config = KeccakDummy::construct_circuit(cb); + Ok(config) + }, + ) + .unwrap() + .unwrap(); + + let (step, program) = ceno_emul::test_utils::keccak_step(); + let (raw_witin, lkm) = + KeccakDummy::assign_instances(&config, cb.cs.num_witin as usize, vec![step]).unwrap(); + + MockProver::assert_satisfied_raw(&cb, raw_witin, &program, None, Some(lkm)); +} + #[test] fn test_dummy_r() { let mut cs = ConstraintSystem::::new(|| "riscv"); diff --git a/ceno_zkvm/src/instructions/riscv/insn_base.rs b/ceno_zkvm/src/instructions/riscv/insn_base.rs index f3ab2ca06..72c5edd79 100644 --- a/ceno_zkvm/src/instructions/riscv/insn_base.rs +++ b/ceno_zkvm/src/instructions/riscv/insn_base.rs @@ -223,6 +223,16 @@ impl WriteRD { step: &StepRecord, ) -> Result<(), ZKVMError> { let op = step.rd().expect("rd op"); + self.assign_op(instance, lk_multiplicity, step.cycle(), &op) + } + + pub fn assign_op( + &self, + instance: &mut [MaybeUninit], + lk_multiplicity: &mut LkMultiplicity, + cycle: Cycle, + op: &WriteOp, + ) -> Result<(), ZKVMError> { set_val!(instance, self.id, op.register_index() as u64); set_val!(instance, self.prev_ts, op.previous_cycle); @@ -237,7 +247,7 @@ impl WriteRD { instance, lk_multiplicity, op.previous_cycle, - step.cycle() + Tracer::SUBCYCLE_RD, + cycle + Tracer::SUBCYCLE_RD, )?; Ok(()) From 5d429a62bb6d60408319bd82dbf665caec8f60e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Fri, 13 Dec 2024 09:09:51 +0100 Subject: [PATCH 3/4] dummy-keccak: fix after merge --- ceno_emul/src/test_utils.rs | 30 +++++++++++++----------------- ceno_emul/tests/test_elf.rs | 5 +---- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/ceno_emul/src/test_utils.rs b/ceno_emul/src/test_utils.rs index c3aef2d01..0343d9534 100644 --- a/ceno_emul/src/test_utils.rs +++ b/ceno_emul/src/test_utils.rs @@ -1,32 +1,28 @@ use crate::{ CENO_PLATFORM, InsnKind, Instruction, Platform, Program, StepRecord, VMState, encode_rv32, - rv32im_encode::load_immediate, syscalls::KECCAK_PERMUTE, + encode_rv32u, syscalls::KECCAK_PERMUTE, }; use anyhow::Result; -pub const fn load_immediate(rd: u32, imm: u32) -> [u32; 2] { - [ - encode_rv32(InsnKind::LUI, 0, 0, rd, imm), - encode_rv32(InsnKind::ORI, rd, 0, rd, imm), - ] -} - pub fn keccak_step() -> (StepRecord, Vec) { - let instructions = [ + let instructions = vec![ // Call Keccak-f. - &load_immediate(Platform::reg_arg0() as u32, CENO_PLATFORM.ram.start)[..], - &load_immediate(Platform::reg_ecall() as u32, KECCAK_PERMUTE)[..], - &[encode_rv32(InsnKind::EANY, 0, 0, 0, 0)], + load_immediate(Platform::reg_arg0() as u32, CENO_PLATFORM.ram.start), + load_immediate(Platform::reg_ecall() as u32, KECCAK_PERMUTE), + encode_rv32(InsnKind::ECALL, 0, 0, 0, 0), // Halt. - &load_immediate(Platform::reg_ecall() as u32, Platform::ecall_halt())[..], - &[encode_rv32(InsnKind::EANY, 0, 0, 0, 0)], - ] - .concat(); + load_immediate(Platform::reg_ecall() as u32, Platform::ecall_halt()), + encode_rv32(InsnKind::ECALL, 0, 0, 0, 0), + ]; let pc = CENO_PLATFORM.pc_base(); let program = Program::new(pc, pc, instructions.clone(), Default::default()); let mut vm = VMState::new(CENO_PLATFORM, program.into()); let steps = vm.iter_until_halt().collect::>>().unwrap(); - (steps[4].clone(), instructions) + (steps[2].clone(), instructions) +} + +const fn load_immediate(rd: u32, imm: u32) -> Instruction { + encode_rv32u(InsnKind::ADDI, 0, 0, rd, imm) } diff --git a/ceno_emul/tests/test_elf.rs b/ceno_emul/tests/test_elf.rs index 80310f43c..ce979f089 100644 --- a/ceno_emul/tests/test_elf.rs +++ b/ceno_emul/tests/test_elf.rs @@ -101,10 +101,7 @@ fn test_ceno_rt_keccak() -> Result<()> { // Check the syscall effects. for (witness, expect) in izip!(syscalls, keccak_outs) { assert_eq!(witness.reg_ops.len(), 1); - assert_eq!( - witness.reg_ops[0].register_index(), - Platform::reg_arg0() - ); + assert_eq!(witness.reg_ops[0].register_index(), Platform::reg_arg0()); assert_eq!(witness.mem_ops.len(), expect.len() * 2); let got = witness From 48081aee9a24b1cde1c657c07e0c9b5f5b55cfff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Nicolas?= Date: Mon, 16 Dec 2024 12:55:42 +0100 Subject: [PATCH 4/4] dummy-keccak: fix after merge --- ceno_emul/src/rv32im_encode.rs | 123 ------------------ ceno_emul/src/test_utils.rs | 2 +- .../instructions/riscv/dummy/dummy_ecall.rs | 5 +- ceno_zkvm/src/instructions/riscv/insn_base.rs | 4 +- 4 files changed, 5 insertions(+), 129 deletions(-) delete mode 100644 ceno_emul/src/rv32im_encode.rs diff --git a/ceno_emul/src/rv32im_encode.rs b/ceno_emul/src/rv32im_encode.rs deleted file mode 100644 index fc9d9e172..000000000 --- a/ceno_emul/src/rv32im_encode.rs +++ /dev/null @@ -1,123 +0,0 @@ -use crate::{InsnKind, rv32im::InsnFormat}; - -const MASK_4_BITS: u32 = 0xF; -const MASK_5_BITS: u32 = 0x1F; -const MASK_6_BITS: u32 = 0x3F; -const MASK_7_BITS: u32 = 0x7F; -const MASK_8_BITS: u32 = 0xFF; -const MASK_10_BITS: u32 = 0x3FF; -const MASK_12_BITS: u32 = 0xFFF; - -/// Generate bit encoding of a RISC-V instruction. -/// -/// Values `rs1`, `rs2` and `rd1` are 5-bit register indices, and `imm` is of -/// bit length depending on the requirements of the instruction format type. -/// -/// Fields not required by the instruction's format type are ignored, so one can -/// safely pass an arbitrary value for these, say 0. -pub const fn encode_rv32(kind: InsnKind, rs1: u32, rs2: u32, rd: u32, imm: u32) -> u32 { - match kind.codes().format { - InsnFormat::R => encode_r(kind, rs1, rs2, rd), - InsnFormat::I => encode_i(kind, rs1, rd, imm), - InsnFormat::S => encode_s(kind, rs1, rs2, imm), - InsnFormat::B => encode_b(kind, rs1, rs2, imm), - InsnFormat::U => encode_u(kind, rd, imm), - InsnFormat::J => encode_j(kind, rd, imm), - } -} - -pub const fn load_immediate(rd: u32, imm: u32) -> [u32; 2] { - [ - encode_rv32(InsnKind::LUI, 0, 0, rd, imm), - encode_rv32(InsnKind::ORI, rd, 0, rd, imm), - ] -} - -// R-Type -// 25 20 15 12 7 0 -// +------+-----+-----+--------+----+-------+ -// funct7 | rs2 | rs1 | funct3 | rd | opcode -const fn encode_r(kind: InsnKind, rs1: u32, rs2: u32, rd: u32) -> u32 { - let rs2 = rs2 & MASK_5_BITS; // 5-bits mask - let rs1 = rs1 & MASK_5_BITS; - let rd = rd & MASK_5_BITS; - let func7 = kind.codes().func7; - let func3 = kind.codes().func3; - let opcode = kind.codes().opcode; - func7 << 25 | rs2 << 20 | rs1 << 15 | func3 << 12 | rd << 7 | opcode -} - -// I-Type -// 20 15 12 7 0 -// +---------+-----+--------+----+-------+ -// imm[0:11] | rs1 | funct3 | rd | opcode -const fn encode_i(kind: InsnKind, rs1: u32, rd: u32, imm: u32) -> u32 { - let rs1 = rs1 & MASK_5_BITS; - let rd = rd & MASK_5_BITS; - let func3 = kind.codes().func3; - let opcode = kind.codes().opcode; - // SRLI/SRAI use a specialization of the I-type format with the shift type in imm[10]. - let is_arithmetic_right_shift = (matches!(kind, InsnKind::SRAI) as u32) << 10; - let imm = imm & MASK_12_BITS | is_arithmetic_right_shift; - imm << 20 | rs1 << 15 | func3 << 12 | rd << 7 | opcode -} - -// S-Type -// 25 20 15 12 7 0 -// +---------+-----+-----+--------+----------+-------+ -// imm[5:11] | rs2 | rs1 | funct3 | imm[0:4] | opcode -const fn encode_s(kind: InsnKind, rs1: u32, rs2: u32, imm: u32) -> u32 { - let rs2 = rs2 & MASK_5_BITS; - let rs1 = rs1 & MASK_5_BITS; - let func3 = kind.codes().func3; - let opcode = kind.codes().opcode; - let imm_lo = imm & MASK_5_BITS; - let imm_hi = (imm >> 5) & MASK_7_BITS; // 7-bits mask - imm_hi << 25 | rs2 << 20 | rs1 << 15 | func3 << 12 | imm_lo << 7 | opcode -} - -// B-Type -// 31 25 20 15 12 8 7 0 -// +-------+-----------+-----+-----+--------+----------+---------+-------+ -// imm[12] | imm[5:10] | rs2 | rs1 | funct3 | imm[1:4] | imm[11] | opcode -const fn encode_b(kind: InsnKind, rs1: u32, rs2: u32, imm: u32) -> u32 { - let rs2 = rs2 & MASK_5_BITS; - let rs1 = rs1 & MASK_5_BITS; - let func3 = kind.codes().func3; - let opcode = kind.codes().opcode; - let imm_1_4 = (imm >> 1) & MASK_4_BITS; // skip imm[0] - let imm_5_10 = (imm >> 5) & MASK_6_BITS; - ((imm >> 12) & 1) << 31 - | imm_5_10 << 25 - | rs2 << 20 - | rs1 << 15 - | func3 << 12 - | imm_1_4 << 8 - | ((imm >> 11) & 1) << 7 - | opcode -} - -// J-Type -// 31 21 20 12 7 0 -// +-------+-----------+---------+------------+----+-------+ -// imm[20] | imm[1:10] | imm[11] | imm[12:19] | rd | opcode -const fn encode_j(kind: InsnKind, rd: u32, imm: u32) -> u32 { - let rd = rd & MASK_5_BITS; - let opcode = kind.codes().opcode; - let imm_1_10 = (imm >> 1) & MASK_10_BITS; // skip imm[0] - let imm_12_19 = (imm >> 12) & MASK_8_BITS; - ((imm >> 20) & 1) << 31 - | imm_1_10 << 21 - | ((imm >> 11) & 1) << 20 - | imm_12_19 << 12 - | rd << 7 - | opcode -} - -// U-Type -// 12 7 0 -// +----------+----+--------+ -// imm[12:31] | rd | opcode -const fn encode_u(kind: InsnKind, rd: u32, imm: u32) -> u32 { - (imm >> 12) << 12 | (rd & MASK_5_BITS) << 7 | kind.codes().opcode -} diff --git a/ceno_emul/src/test_utils.rs b/ceno_emul/src/test_utils.rs index 0343d9534..07a5a817c 100644 --- a/ceno_emul/src/test_utils.rs +++ b/ceno_emul/src/test_utils.rs @@ -7,7 +7,7 @@ use anyhow::Result; pub fn keccak_step() -> (StepRecord, Vec) { let instructions = vec![ // Call Keccak-f. - load_immediate(Platform::reg_arg0() as u32, CENO_PLATFORM.ram.start), + load_immediate(Platform::reg_arg0() as u32, CENO_PLATFORM.heap.start), load_immediate(Platform::reg_ecall() as u32, KECCAK_PERMUTE), encode_rv32(InsnKind::ECALL, 0, 0, 0, 0), // Halt. diff --git a/ceno_zkvm/src/instructions/riscv/dummy/dummy_ecall.rs b/ceno_zkvm/src/instructions/riscv/dummy/dummy_ecall.rs index 4b1b4f15e..cd3156387 100644 --- a/ceno_zkvm/src/instructions/riscv/dummy/dummy_ecall.rs +++ b/ceno_zkvm/src/instructions/riscv/dummy/dummy_ecall.rs @@ -17,7 +17,6 @@ use crate::{ set_val, witness::LkMultiplicity, }; -use core::mem::MaybeUninit; trait EcallSpec { const NAME: &'static str; @@ -51,7 +50,7 @@ impl Instruction for LargeEcallDummy { fn construct_circuit(cb: &mut CircuitBuilder) -> Result { let dummy_insn = DummyConfig::construct_circuit( cb, - InsnKind::EANY, + InsnKind::ECALL, true, // Read the ecall function code. false, false, @@ -97,7 +96,7 @@ impl Instruction for LargeEcallDummy { fn assign_instance( config: &Self::InstructionConfig, - instance: &mut [MaybeUninit], + instance: &mut [E::BaseField], lk_multiplicity: &mut LkMultiplicity, step: &StepRecord, ) -> Result<(), ZKVMError> { diff --git a/ceno_zkvm/src/instructions/riscv/insn_base.rs b/ceno_zkvm/src/instructions/riscv/insn_base.rs index cc10f0ea7..057a27bb5 100644 --- a/ceno_zkvm/src/instructions/riscv/insn_base.rs +++ b/ceno_zkvm/src/instructions/riscv/insn_base.rs @@ -227,7 +227,7 @@ impl WriteRD { pub fn assign_op( &self, - instance: &mut [MaybeUninit], + instance: &mut [E::BaseField], lk_multiplicity: &mut LkMultiplicity, cycle: Cycle, op: &WriteOp, @@ -348,7 +348,7 @@ impl WriteMEM { pub fn assign_op( &self, - instance: &mut [MaybeUninit], + instance: &mut [F], lk_multiplicity: &mut LkMultiplicity, cycle: Cycle, op: &WriteOp,