diff --git a/ceno_zkvm/src/chip_handler.rs b/ceno_zkvm/src/chip_handler.rs index 8ed029e5d..8a6ab8c5e 100644 --- a/ceno_zkvm/src/chip_handler.rs +++ b/ceno_zkvm/src/chip_handler.rs @@ -16,6 +16,13 @@ pub trait GlobalStateRegisterMachineChipOperations { fn state_in(&mut self, pc: Expression, ts: Expression) -> Result<(), ZKVMError>; fn state_out(&mut self, pc: Expression, ts: Expression) -> Result<(), ZKVMError>; + + fn state_out_with_exit_code( + &mut self, + pc: Expression, + ts: Expression, + exit_code: Expression, + ) -> Result<(), ZKVMError>; } /// The common representation of a register value. diff --git a/ceno_zkvm/src/chip_handler/global_state.rs b/ceno_zkvm/src/chip_handler/global_state.rs index 50b235fb3..8a127cd9d 100644 --- a/ceno_zkvm/src/chip_handler/global_state.rs +++ b/ceno_zkvm/src/chip_handler/global_state.rs @@ -12,6 +12,7 @@ impl<'a, E: ExtensionField> GlobalStateRegisterMachineChipOperations for Circ Expression::Constant(E::BaseField::from(RAMType::GlobalState as u64)), pc, ts, + 0.into(), ]; let rlc_record = self.rlc_chip_record(items); @@ -23,8 +24,25 @@ impl<'a, E: ExtensionField> GlobalStateRegisterMachineChipOperations for Circ Expression::Constant(E::BaseField::from(RAMType::GlobalState as u64)), pc, ts, + 0.into(), ]; let rlc_record = self.rlc_chip_record(items); self.write_record(|| "state_out", rlc_record) } + + fn state_out_with_exit_code( + &mut self, + pc: Expression, + ts: Expression, + exit_code: Expression, + ) -> Result<(), ZKVMError> { + let items: Vec> = vec![ + Expression::Constant(E::BaseField::from(RAMType::GlobalState as u64)), + pc, + ts, + exit_code, + ]; + let rlc_record = self.rlc_chip_record(items); + self.write_record(|| "state_out_with_exit_code", rlc_record) + } } diff --git a/ceno_zkvm/src/instructions/riscv/ecall/halt.rs b/ceno_zkvm/src/instructions/riscv/ecall/halt.rs index 3d6be6c72..0d39e2378 100644 --- a/ceno_zkvm/src/instructions/riscv/ecall/halt.rs +++ b/ceno_zkvm/src/instructions/riscv/ecall/halt.rs @@ -1,40 +1,67 @@ use crate::{ + chip_handler::RegisterChipOperations, circuit_builder::CircuitBuilder, error::ZKVMError, + expression::{ToExpr, WitIn}, instructions::{ - riscv::{constants::ECALL_HALT, ecall_insn::EcallInstructionConfig}, + riscv::{ + config::{ExprLtConfig, ExprLtInput}, + constants::{UInt, ECALL_HALT}, + ecall_insn::EcallInstructionConfig, + }, Instruction, }, + set_val, witness::LkMultiplicity, + Value, }; use ceno_emul::StepRecord; use ff_ext::ExtensionField; use std::{marker::PhantomData, mem::MaybeUninit}; -pub struct HaltConfig { +pub struct HaltConfig { ecall_cfg: EcallInstructionConfig, + prev_x10_value: UInt, + prev_x10_ts: WitIn, + lt_x10_cfg: ExprLtConfig, } pub struct HaltCircuit(PhantomData); impl Instruction for HaltCircuit { - type InstructionConfig = HaltConfig; + type InstructionConfig = HaltConfig; fn name() -> String { "ECALL_HALT".into() } fn construct_circuit(cb: &mut CircuitBuilder) -> Result { + let prev_x10_ts = cb.create_witin(|| "prev_x10_ts")?; + let prev_x10_value = UInt::new_unchecked(|| "prev_x10_value", cb)?; + let ecall_cfg = EcallInstructionConfig::construct_circuit( cb, [ECALL_HALT[0].into(), ECALL_HALT[1].into()], None, Some(0.into()), + Some(prev_x10_value.register_expr()), )?; - // TODO: read exit_code from arg1 and write it to global state + // read exit_code from arg0 (X10 register) and write it to global state + let (_, lt_x10_cfg) = cb.register_read( + || "read x10", + E::BaseField::from(ceno_emul::CENO_PLATFORM.reg_arg0() as u64), + prev_x10_ts.expr(), + ecall_cfg.ts.expr(), + prev_x10_value.register_expr(), + )?; - Ok(HaltConfig { ecall_cfg }) + Ok(HaltConfig { + ecall_cfg, + prev_x10_value, + prev_x10_ts, + lt_x10_cfg, + }) } fn assign_instance( @@ -49,6 +76,22 @@ impl Instruction for HaltCircuit { ); assert_eq!(step.pc().after.0, 0); + set_val!( + instance, + config.prev_x10_ts, + step.rs2().unwrap().previous_cycle + ); + config.prev_x10_value.assign_limbs( + instance, + Value::new_unchecked(step.rs2().unwrap().value).u16_fields(), + ); + + ExprLtInput { + lhs: step.rs2().unwrap().previous_cycle, + rhs: step.cycle(), + } + .assign(instance, &config.lt_x10_cfg, lk_multiplicity); + config .ecall_cfg .assign_instance::(instance, lk_multiplicity, step)?; diff --git a/ceno_zkvm/src/instructions/riscv/ecall_insn.rs b/ceno_zkvm/src/instructions/riscv/ecall_insn.rs index 782c0cfd7..63da60af4 100644 --- a/ceno_zkvm/src/instructions/riscv/ecall_insn.rs +++ b/ceno_zkvm/src/instructions/riscv/ecall_insn.rs @@ -33,6 +33,7 @@ impl EcallInstructionConfig { syscall_id: RegisterExpr, syscall_ret_value: Option>, next_pc: Option>, + exit_code: Option>, ) -> Result { let pc = cb.create_witin(|| "pc")?; let ts = cb.create_witin(|| "cur_ts")?; @@ -61,9 +62,12 @@ impl EcallInstructionConfig { syscall_ret_value.map_or(syscall_id, |v| v), )?; - cb.state_out( + cb.state_out_with_exit_code( next_pc.map_or(pc.expr() + PC_STEP_SIZE.into(), |next_pc| next_pc), ts.expr() + 4.into(), + exit_code.map_or(0.into(), |ec| { + ec[0].clone() + (Expression::::from(1usize << 16) * ec[1].clone()) + }), )?; Ok(Self {