Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Ecall/Halt #258

Merged
merged 31 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0e69f17
init
kunxian-xia Sep 21, 2024
244a93d
draft
kunxian-xia Sep 24, 2024
1bff712
Merge branch 'master' into feat/ecall_halt
kunxian-xia Sep 24, 2024
0871411
finish ecall/halt
kunxian-xia Sep 25, 2024
69f8816
Merge remote-tracking branch 'origin/master' into feat/ecall_halt
kunxian-xia Sep 25, 2024
2cd2338
fix lints
kunxian-xia Sep 25, 2024
24bbb6b
Merge branch 'master' into feat/ecall_halt
kunxian-xia Sep 25, 2024
8c41c2c
read exit_code from arg0 and write it to global state
kunxian-xia Sep 25, 2024
66d635c
include ecall/halt in integration
kunxian-xia Sep 25, 2024
0713aba
set pc = 0 after ecall/halt in ceno_emul
kunxian-xia Sep 25, 2024
3ef36c0
fmt
kunxian-xia Sep 25, 2024
cd5b477
Merge remote-tracking branch 'origin/master' into feat/ecall_halt
kunxian-xia Sep 26, 2024
ae255a0
add Instance to express enum
kunxian-xia Sep 30, 2024
72c77ff
add query_instance to circuit builder api
kunxian-xia Sep 30, 2024
a8fe2f3
use public_values.exit_code as the value to arg1 in ecall/halt instru…
kunxian-xia Sep 30, 2024
eab9a5f
use register idices in ceno_emul
kunxian-xia Sep 30, 2024
a2822f4
add public input in prover's and verifier's workflow
kunxian-xia Sep 30, 2024
7bcc25d
Merge remote-tracking branch 'origin/master' into feat/ecall_halt
kunxian-xia Sep 30, 2024
b71bc7a
fix clippy errors
kunxian-xia Sep 30, 2024
bfefbac
fix compilation error
kunxian-xia Sep 30, 2024
e30a9fe
fix verify error and add sound check
kunxian-xia Sep 30, 2024
d0399a1
fix test
kunxian-xia Sep 30, 2024
169e206
fix
kunxian-xia Sep 30, 2024
062fafd
require ecall halt proof to exist and has only one instance
kunxian-xia Oct 4, 2024
5a90b4d
Merge remote-tracking branch 'origin/master' into feat/ecall_halt
kunxian-xia Oct 4, 2024
c6ba422
fix clippy
kunxian-xia Oct 4, 2024
406336b
Merge branch 'master' into feat/ecall_halt
kunxian-xia Oct 4, 2024
dadd8ef
fix test error
kunxian-xia Oct 4, 2024
6c492a4
fix mock prover error
kunxian-xia Oct 6, 2024
40784a0
Merge branch 'master' into feat/ecall_halt
kunxian-xia Oct 6, 2024
7121263
Merge branch 'master' into feat/ecall_halt
kunxian-xia Oct 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions ceno_zkvm/src/chip_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use ff_ext::ExtensionField;

use crate::{
error::ZKVMError,
expression::{Expression, WitIn},
expression::{Expression, ToExpr, WitIn},
instructions::riscv::config::ExprLtConfig,
};

Expand All @@ -26,7 +26,7 @@ pub trait RegisterChipOperations<E: ExtensionField, NR: Into<String>, N: FnOnce(
fn register_read(
&mut self,
name_fn: N,
register_id: &WitIn,
register_id: impl ToExpr<E, Output = Expression<E>>,
prev_ts: Expression<E>,
ts: Expression<E>,
value: RegisterExpr<E>,
Expand All @@ -36,7 +36,7 @@ pub trait RegisterChipOperations<E: ExtensionField, NR: Into<String>, N: FnOnce(
fn register_write(
&mut self,
name_fn: N,
register_id: &WitIn,
register_id: impl ToExpr<E, Output = Expression<E>>,
prev_ts: Expression<E>,
ts: Expression<E>,
prev_values: RegisterExpr<E>,
Expand Down
7 changes: 4 additions & 3 deletions ceno_zkvm/src/chip_handler/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use ff_ext::ExtensionField;
use crate::{
circuit_builder::CircuitBuilder,
error::ZKVMError,
expression::{Expression, ToExpr, WitIn},
expression::{Expression, ToExpr},
instructions::riscv::config::ExprLtConfig,
structs::RAMType,
};
Expand All @@ -16,7 +16,7 @@ impl<'a, E: ExtensionField, NR: Into<String>, N: FnOnce() -> NR> RegisterChipOpe
fn register_read(
&mut self,
name_fn: N,
register_id: &WitIn,
register_id: impl ToExpr<E, Output = Expression<E>>,
prev_ts: Expression<E>,
ts: Expression<E>,
value: RegisterExpr<E>,
Expand Down Expand Up @@ -61,12 +61,13 @@ impl<'a, E: ExtensionField, NR: Into<String>, N: FnOnce() -> NR> RegisterChipOpe
fn register_write(
&mut self,
name_fn: N,
register_id: &WitIn,
register_id: impl ToExpr<E, Output = Expression<E>>,
prev_ts: Expression<E>,
ts: Expression<E>,
prev_values: RegisterExpr<E>,
value: RegisterExpr<E>,
) -> Result<(Expression<E>, ExprLtConfig), ZKVMError> {
assert!(register_id.expr().degree() <= 1);
self.namespace(name_fn, |cb| {
// READ (a, v, t)
let read_record = cb.rlc_chip_record(
Expand Down
7 changes: 7 additions & 0 deletions ceno_zkvm/src/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,13 @@ impl<E: ExtensionField> ToExpr<E> for WitIn {
}
}

impl<E: ExtensionField> ToExpr<E> for &WitIn {
type Output = Expression<E>;
fn expr(&self) -> Expression<E> {
Expression::WitIn(self.id)
}
}

impl<F: SmallField, E: ExtensionField<BaseField = F>> ToExpr<E> for F {
type Output = Expression<E>;
fn expr(&self) -> Expression<E> {
Expand Down
3 changes: 3 additions & 0 deletions ceno_zkvm/src/instructions/riscv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ pub mod branch;
pub mod config;
pub mod constants;
pub mod divu;
pub mod ecall;
mod i_insn;
pub mod logic;

mod ecall_insn;
mod r_insn;
pub mod shift_imm;

Expand Down
4 changes: 2 additions & 2 deletions ceno_zkvm/src/instructions/riscv/b_insn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,14 @@ impl BInstructionConfig {
// Register reads.
let (ts, lt_rs1_cfg) = circuit_builder.register_read(
|| "read_rs1",
&rs1_id,
rs1_id,
prev_rs1_ts.expr(),
cur_ts.expr(),
rs1_read,
)?;
let (_ts, lt_rs2_cfg) = circuit_builder.register_read(
|| "read_rs2",
&rs2_id,
rs2_id,
prev_rs2_ts.expr(),
ts,
rs2_read,
Expand Down
1 change: 1 addition & 0 deletions ceno_zkvm/src/instructions/riscv/constants.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::uint::UIntLimbs;
pub use ceno_emul::PC_STEP_SIZE;

pub const ECALL_HALT: [usize; 2] = [0x00_00, 0x00_00];
pub const VALUE_BIT_WIDTH: usize = 16;

#[cfg(feature = "riv32")]
Expand Down
1 change: 1 addition & 0 deletions ceno_zkvm/src/instructions/riscv/ecall.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod halt;
kunxian-xia marked this conversation as resolved.
Show resolved Hide resolved
58 changes: 58 additions & 0 deletions ceno_zkvm/src/instructions/riscv/ecall/halt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use crate::{
circuit_builder::CircuitBuilder,
error::ZKVMError,
instructions::{
riscv::{constants::ECALL_HALT, ecall_insn::EcallInstructionConfig},
Instruction,
},
witness::LkMultiplicity,
};
use ceno_emul::StepRecord;
use ff_ext::ExtensionField;
use std::{marker::PhantomData, mem::MaybeUninit};

pub struct HaltConfig {
ecall_cfg: EcallInstructionConfig,
}

pub struct HaltCircuit<E>(PhantomData<E>);

impl<E: ExtensionField> Instruction<E> for HaltCircuit<E> {
type InstructionConfig = HaltConfig;

fn name() -> String {
"ECALL_HALT".into()
}

fn construct_circuit(cb: &mut CircuitBuilder<E>) -> Result<Self::InstructionConfig, ZKVMError> {
let ecall_cfg = EcallInstructionConfig::construct_circuit(
cb,
[ECALL_HALT[0].into(), ECALL_HALT[1].into()],
None,
Some(0.into()),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We define PC=0 as invalid, and no other instruction can execute after that. I would document this requirement right here as follows:

assert!(!CENO_PLATFORM.can_execute(0), "PC=0 means the program is terminated.");

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: we can define 0 as constant EXIT_PC to improve the readibility

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want PC=0 to be invalid, I would suggest also adding 0x0000_0000 in the first 32 bits of memory. Just for consistency. (32 bits of all zeroes is Risc-V's way of specifying a guaranteed invalid instruction in every version of the instruction set.)

)?;

hero78119 marked this conversation as resolved.
Show resolved Hide resolved
// TODO: read exit_code from arg1 and write it to global state
kunxian-xia marked this conversation as resolved.
Show resolved Hide resolved

Ok(HaltConfig { ecall_cfg })
}

fn assign_instance(
config: &Self::InstructionConfig,
instance: &mut [MaybeUninit<E::BaseField>],
lk_multiplicity: &mut LkMultiplicity,
step: &StepRecord,
) -> Result<(), ZKVMError> {
assert_eq!(
step.rs1().unwrap().value,
(ECALL_HALT[0] + (ECALL_HALT[1] << 16)) as u32
);
assert_eq!(step.pc().after.0, 0);

config
.ecall_cfg
.assign_instance::<E>(instance, lk_multiplicity, step)?;

Ok(())
}
}
103 changes: 103 additions & 0 deletions ceno_zkvm/src/instructions/riscv/ecall_insn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use crate::{
chip_handler::{
GlobalStateRegisterMachineChipOperations, RegisterChipOperations, RegisterExpr,
},
circuit_builder::CircuitBuilder,
error::ZKVMError,
expression::{Expression, ToExpr, WitIn},
instructions::riscv::config::{ExprLtConfig, ExprLtInput},
set_val,
tables::InsnRecord,
witness::LkMultiplicity,
};
use ceno_emul::{InsnKind::EANY, StepRecord, PC_STEP_SIZE};
use ff_ext::ExtensionField;
use std::mem::MaybeUninit;

const X5: u64 = 5;
kunxian-xia marked this conversation as resolved.
Show resolved Hide resolved
#[allow(dead_code)]
const X10: usize = 10;
#[allow(dead_code)]
const X11: usize = 11;

pub struct EcallInstructionConfig {
pub pc: WitIn,
pub ts: WitIn,
prev_x5_ts: WitIn,
lt_x5_cfg: ExprLtConfig,
}

impl EcallInstructionConfig {
pub fn construct_circuit<E: ExtensionField>(
cb: &mut CircuitBuilder<E>,
syscall_id: RegisterExpr<E>,
syscall_ret_value: Option<RegisterExpr<E>>,
next_pc: Option<Expression<E>>,
) -> Result<Self, ZKVMError> {
let pc = cb.create_witin(|| "pc")?;
let ts = cb.create_witin(|| "cur_ts")?;

cb.state_in(pc.expr(), ts.expr())?;

cb.lk_fetch(&InsnRecord::new(
pc.expr(),
(EANY.codes().opcode as usize).into(),
0.into(),
(EANY.codes().func3 as usize).into(),
0.into(),
0.into(),
0.into(), // imm = 0
))?;

let prev_x5_ts = cb.create_witin(|| "prev_x5_ts")?;

// read syscall_id from x5 and write return value to x5
let (_, lt_x5_cfg) = cb.register_write(
|| "write x5",
E::BaseField::from(X5),
prev_x5_ts.expr(),
ts.expr(),
syscall_id.clone(),
syscall_ret_value.map_or(syscall_id, |v| v),
)?;

cb.state_out(
next_pc.map_or(pc.expr() + PC_STEP_SIZE.into(), |next_pc| next_pc),
ts.expr() + 4.into(),
)?;

Ok(Self {
pc,
ts,
prev_x5_ts,
lt_x5_cfg,
})
}

pub fn assign_instance<E: ExtensionField>(
&self,
instance: &mut [MaybeUninit<E::BaseField>],
lk_multiplicity: &mut LkMultiplicity,
step: &StepRecord,
) -> Result<(), ZKVMError> {
set_val!(instance, self.pc, step.pc().before.0 as u64);
set_val!(instance, self.ts, step.cycle());
lk_multiplicity.fetch(step.pc().before.0);

// the access of X5 register is stored in rs1()
// the access of X10 register is stored in rs2()
set_val!(
instance,
self.prev_x5_ts,
step.rs1().unwrap().previous_cycle
);

ExprLtInput {
lhs: step.rs1().unwrap().previous_cycle,
rhs: step.cycle(),
}
.assign(instance, &self.lt_x5_cfg, lk_multiplicity);

Ok(())
}
}
4 changes: 2 additions & 2 deletions ceno_zkvm/src/instructions/riscv/i_insn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,14 @@ impl<E: ExtensionField> IInstructionConfig<E> {
// Register read and write.
let (next_ts, lt_rs1_cfg) = circuit_builder.register_read(
|| "read_rs1",
&rs1_id,
rs1_id,
prev_rs1_ts.expr(),
cur_ts.expr(),
rs1_read,
)?;
let (next_ts, lt_rd_cfg) = circuit_builder.register_write(
|| "write_rd",
&rd_id,
rd_id,
prev_rd_ts.expr(),
next_ts,
prev_rd_value.register_expr(),
Expand Down
6 changes: 3 additions & 3 deletions ceno_zkvm/src/instructions/riscv/r_insn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,21 +79,21 @@ impl<E: ExtensionField> RInstructionConfig<E> {
// Register read and write.
let (next_ts, lt_rs1_cfg) = circuit_builder.register_read(
|| "read_rs1",
&rs1_id,
rs1_id,
prev_rs1_ts.expr(),
cur_ts.expr(),
rs1_read,
)?;
let (next_ts, lt_rs2_cfg) = circuit_builder.register_read(
|| "read_rs2",
&rs2_id,
rs2_id,
prev_rs2_ts.expr(),
next_ts,
rs2_read,
)?;
let (next_ts, lt_prev_ts_cfg) = circuit_builder.register_write(
|| "write_rd",
&rd_id,
rd_id,
prev_rd_ts.expr(),
next_ts,
prev_rd_value.register_expr(),
Expand Down
Loading