Skip to content
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
e5389e7
fix mockprover error
hero78119 Aug 5, 2025
1359174
ci mock proving to debug build
hero78119 Aug 5, 2025
7e7ee26
better coding style
hero78119 Aug 5, 2025
d2d8031
wip
hero78119 Aug 7, 2025
cae1b09
complete v2 circuit
hero78119 Aug 7, 2025
6dea5b7
branch v2 witness assignment
hero78119 Aug 7, 2025
85699f3
all test pass
hero78119 Aug 7, 2025
9f5408c
slt/sltu with limb style circuit
hero78119 Aug 7, 2025
6a44ba3
add ci steps
hero78119 Aug 7, 2025
f72ffb6
update comments
hero78119 Aug 7, 2025
6ba167b
wip
hero78119 Aug 7, 2025
14c5c39
finish slti/sltiu logic
Aug 8, 2025
3140652
finish addi logic
Aug 8, 2025
01d31d6
skip imm_sign range check
Aug 8, 2025
79a8379
branch imm could be 1 limb
Aug 8, 2025
64c291a
addi test pass
hero78119 Aug 11, 2025
a35031b
slti(u) test pass
hero78119 Aug 12, 2025
9754a6a
refactor slti(u) properly
hero78119 Aug 12, 2025
e708e6e
fix clippy
hero78119 Aug 12, 2025
eb4b9a7
combine imm_internal + i64_base into one
hero78119 Aug 12, 2025
8cb9a85
reformat code
hero78119 Aug 12, 2025
caf4e27
fix clippy
hero78119 Aug 12, 2025
f947856
bge/blt test pass
hero78119 Aug 12, 2025
ea6a827
logic imm test pass
hero78119 Aug 12, 2025
926da60
Merge branch 'master' of github.com:scroll-tech/ceno into feat/imm
hero78119 Aug 13, 2025
58a44df
code cosmetics
hero78119 Aug 13, 2025
00affa7
refactor memory opcode for migration
hero78119 Aug 13, 2025
b1b1cdd
all load/store test pass
hero78119 Aug 13, 2025
19be4cf
make JALR as TODO
hero78119 Aug 13, 2025
f03c6f1
fix clippy
hero78119 Aug 13, 2025
a84dc91
clean up jalr code
hero78119 Aug 13, 2025
7727945
Merge branch 'feat/fix_mock_prover_error' into feat/imm
hero78119 Aug 13, 2025
46c32cb
refactor arith_imm
hero78119 Aug 13, 2025
c5aa5d1
merge with master
hero78119 Aug 15, 2025
c39e3ae
wip
Aug 15, 2025
3853078
migrated lui and test pass
hero78119 Aug 15, 2025
4c38c38
add auipc
hero78119 Aug 15, 2025
635c424
auipc test pass
hero78119 Aug 15, 2025
b3876d2
all test pass
hero78119 Aug 18, 2025
0c24370
merge with master
hero78119 Aug 18, 2025
6ef6c78
format fix
hero78119 Aug 18, 2025
155987f
format fix
hero78119 Aug 18, 2025
d1a6040
babybear test for addi/logici
hero78119 Aug 18, 2025
d03866b
merge with master
hero78119 Aug 20, 2025
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
1 change: 1 addition & 0 deletions ceno_emul/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ tracing.workspace = true
[features]
default = ["forbid_overflow"]
forbid_overflow = []
u16limb_circuit = []

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

emulator also need this feature to interpret auipc/lui as new chip or with addi

114 changes: 70 additions & 44 deletions ceno_emul/src/disassemble/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use rrs_lib::{

/// A transpiler that converts the 32-bit encoded instructions into instructions.
pub(crate) struct InstructionTranspiler {
#[allow(dead_code)]
pc: u32,
word: u32,
}
Expand Down Expand Up @@ -249,59 +250,84 @@ impl InstructionProcessor for InstructionTranspiler {
}
}

/// Convert LUI to ADDI.
///
/// RiscV's load-upper-immediate instruction is necessary to build arbitrary constants,
/// because its ADDI can only have a relatively small immediate value: there's just not
/// enough space in the 32 bits for more.
///
/// Our internal ADDI does not have this limitation, so we can convert LUI to ADDI.
/// See [`InstructionTranspiler::process_auipc`] for more background on the conversion.
fn process_lui(&mut self, dec_insn: UType) -> Self::InstructionResult {
// Verify assumption that the immediate is already shifted left by 12 bits.
assert_eq!(dec_insn.imm & 0xfff, 0);
Instruction {
kind: InsnKind::ADDI,
rd: dec_insn.rd,
rs1: 0,
rs2: 0,
imm: dec_insn.imm,
raw: self.word,
#[cfg(not(feature = "u16limb_circuit"))]
{
// Convert LUI to ADDI.
//
// RiscV's load-upper-immediate instruction is necessary to build arbitrary constants,
// because its ADDI can only have a relatively small immediate value: there's just not
// enough space in the 32 bits for more.
//
// Our internal ADDI does not have this limitation, so we can convert LUI to ADDI.
// See [`InstructionTranspiler::process_auipc`] for more background on the conversion.
Instruction {
kind: InsnKind::ADDI,
rd: dec_insn.rd,
rs1: 0,
rs2: 0,
imm: dec_insn.imm,
raw: self.word,
}
}
#[cfg(feature = "u16limb_circuit")]
{
Instruction {
kind: InsnKind::LUI,
rd: dec_insn.rd,
rs1: 0,
rs2: 0,
imm: dec_insn.imm,
raw: self.word,
}
}
}

/// Convert AUIPC to ADDI.
///
/// RiscV's instructions are designed to be (mosty) position-independent. AUIPC is used
/// to get access to the current program counter, even if the code has been moved around
/// by the linker.
///
/// Our conversion here happens after the linker has done its job, so we can safely hardcode
/// the current program counter into the immediate value of our internal ADDI.
///
/// Note that our internal ADDI can have arbitrary intermediate values, not just 12 bits.
///
/// ADDI is slightly more general than LUI or AUIPC, because you can also specify an
/// input register rs1. That generality might cost us sligthtly in the non-recursive proof,
/// but we suspect decreasing the total number of different instruction kinds will speed up
/// the recursive proof.
///
/// In any case, AUIPC and LUI together make up ~0.1% of instructions executed in typical
/// real world scenarios like a `reth` run.
///
/// TODO(Matthias): run benchmarks to verify the impact on recursion, once we have a working
/// recursion.
fn process_auipc(&mut self, dec_insn: UType) -> Self::InstructionResult {
let pc = self.pc;
// Verify our assumption that the immediate is already shifted left by 12 bits.
assert_eq!(dec_insn.imm & 0xfff, 0);
Instruction {
kind: InsnKind::ADDI,
rd: dec_insn.rd,
rs1: 0,
rs2: 0,
imm: dec_insn.imm.wrapping_add(pc as i32),
raw: self.word,
#[cfg(not(feature = "u16limb_circuit"))]
{
let pc = self.pc;
// Convert AUIPC to ADDI.
//
// RiscV's instructions are designed to be (mosty) position-independent. AUIPC is used
// to get access to the current program counter, even if the code has been moved around
// by the linker.
//
// Our conversion here happens after the linker has done its job, so we can safely hardcode
// the current program counter into the immediate value of our internal ADDI.
//
// Note that our internal ADDI can have arbitrary intermediate values, not just 12 bits.
//
// ADDI is slightly more general than LUI or AUIPC, because you can also specify an
// input register rs1. That generality might cost us sligthtly in the non-recursive proof,
// but we suspect decreasing the total number of different instruction kinds will speed up
// the recursive proof.
//
// In any case, AUIPC and LUI together make up ~0.1% of instructions executed in typical
// real world scenarios like a `reth` run.
Instruction {
kind: InsnKind::ADDI,
rd: dec_insn.rd,
rs1: 0,
rs2: 0,
imm: dec_insn.imm.wrapping_add(pc as i32),
raw: self.word,
}
}
#[cfg(feature = "u16limb_circuit")]
{
Instruction {
kind: InsnKind::AUIPC,
rd: dec_insn.rd,
rs1: 0,
rs2: 0,
imm: dec_insn.imm,
raw: self.word,
}
}
}

Expand Down
12 changes: 12 additions & 0 deletions ceno_emul/src/rv32im.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ pub enum InsnKind {
LW,
LBU,
LHU,
#[cfg(feature = "u16limb_circuit")]
LUI,
#[cfg(feature = "u16limb_circuit")]
AUIPC,
SB,
SH,
SW,
Expand All @@ -216,6 +220,8 @@ impl From<InsnKind> for InsnCategory {
LB | LH | LW | LBU | LHU => Load,
SB | SH | SW => Store,
ECALL => System,
#[cfg(feature = "u16limb_circuit")]
LUI | AUIPC => Compute,
}
}
}
Expand All @@ -234,6 +240,8 @@ impl From<InsnKind> for InsnFormat {
SB | SH | SW => S,
ECALL => I,
INVALID => I,
#[cfg(feature = "u16limb_circuit")]
LUI | AUIPC => U,
}
}
}
Expand Down Expand Up @@ -306,6 +314,10 @@ fn step_compute<M: EmuContext>(ctx: &mut M, kind: InsnKind, insn: &Instruction)

match kind {
ADDI => rs1.wrapping_add(imm_i),
#[cfg(feature = "u16limb_circuit")]
LUI => imm_i,
#[cfg(feature = "u16limb_circuit")]
AUIPC => pc.wrapping_add(imm_i).0,
XORI => rs1 ^ imm_i,
ORI => rs1 | imm_i,
ANDI => rs1 & imm_i,
Expand Down
2 changes: 1 addition & 1 deletion ceno_zkvm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ nightly-features = [
"witness/nightly-features",
]
sanity-check = ["mpcs/sanity-check"]
u16limb_circuit = []
u16limb_circuit = ["ceno_emul/u16limb_circuit"]

[[bench]]
harness = false
Expand Down
15 changes: 8 additions & 7 deletions ceno_zkvm/src/gadgets/signed_limbs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use witness::set_val;
///
/// this configuration structure allows flexible comparison logic depending on whether
/// the operands should be interpreted as signed or unsigned integers.
#[derive(Debug)]
pub struct UIntLimbsLTConfig<E: ExtensionField> {
// Most significant limb of a and b respectively as a field element, will be range
// checked to be within [-32768, 32767) if signed and [0, 65536) if unsigned.
Expand Down Expand Up @@ -46,7 +47,7 @@ impl<E: ExtensionField> UIntLimbsLT<E> {
circuit_builder: &mut CircuitBuilder<E>,
a: &UInt<E>,
b: &UInt<E>,
is_signed: bool,
is_sign_comparison: bool,
) -> Result<UIntLimbsLTConfig<E>, ZKVMError> {
// 1 if a < b, 0 otherwise.
let cmp_lt = circuit_builder.create_bit(|| "cmp_lt")?;
Expand Down Expand Up @@ -120,7 +121,7 @@ impl<E: ExtensionField> UIntLimbsLT<E> {
circuit_builder.assert_ux::<_, _, LIMB_BITS>(
|| "a_msb_f_signed_range_check",
a_msb_f.expr()
+ if is_signed {
+ if is_sign_comparison {
E::BaseField::from_canonical_u32(1 << (LIMB_BITS - 1)).expr()
} else {
Expression::ZERO
Expand All @@ -130,7 +131,7 @@ impl<E: ExtensionField> UIntLimbsLT<E> {
circuit_builder.assert_ux::<_, _, LIMB_BITS>(
|| "b_msb_f_signed_range_check",
b_msb_f.expr()
+ if is_signed {
+ if is_sign_comparison {
E::BaseField::from_canonical_u32(1 << (LIMB_BITS - 1)).expr()
} else {
Expression::ZERO
Expand All @@ -153,9 +154,9 @@ impl<E: ExtensionField> UIntLimbsLT<E> {
lkm: &mut gkr_iop::utils::lk_multiplicity::LkMultiplicity,
a: &[u16],
b: &[u16],
is_signed: bool,
is_sign_comparison: bool,
) -> Result<(), CircuitBuilderError> {
let (cmp_lt, diff_idx, is_a_neg, is_b_neg) = run_cmp(is_signed, a, b);
let (cmp_lt, diff_idx, is_a_neg, is_b_neg) = run_cmp(is_sign_comparison, a, b);
config
.diff_marker
.iter()
Expand All @@ -175,7 +176,7 @@ impl<E: ExtensionField> UIntLimbsLT<E> {
} else {
(
E::BaseField::from_canonical_u16(a[UINT_LIMBS - 1]),
a[UINT_LIMBS - 1] + ((is_signed as u16) << (LIMB_BITS - 1)),
a[UINT_LIMBS - 1] + ((is_sign_comparison as u16) << (LIMB_BITS - 1)),
)
};
let (b_msb_f, b_msb_range) = if is_b_neg {
Expand All @@ -186,7 +187,7 @@ impl<E: ExtensionField> UIntLimbsLT<E> {
} else {
(
E::BaseField::from_canonical_u16(b[UINT_LIMBS - 1]),
b[UINT_LIMBS - 1] + ((is_signed as u16) << (LIMB_BITS - 1)),
b[UINT_LIMBS - 1] + ((is_sign_comparison as u16) << (LIMB_BITS - 1)),
)
};

Expand Down
4 changes: 4 additions & 0 deletions ceno_zkvm/src/instructions/riscv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ mod r_insn;

mod ecall_insn;

#[cfg(feature = "u16limb_circuit")]
mod auipc;
mod im_insn;
#[cfg(feature = "u16limb_circuit")]
mod lui;
mod memory;
mod s_insn;
#[cfg(test)]
Expand Down
Loading