-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement a J-type instruction base type and the `JAL` opcode. --------- Co-authored-by: Bryan Gillespie <[email protected]> Co-authored-by: xkx <[email protected]>
- Loading branch information
1 parent
8e2214d
commit cdb771a
Showing
10 changed files
with
258 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
use ceno_emul::{InsnKind, StepRecord}; | ||
use ff_ext::ExtensionField; | ||
|
||
use crate::{ | ||
chip_handler::RegisterExpr, | ||
circuit_builder::CircuitBuilder, | ||
error::ZKVMError, | ||
expression::ToExpr, | ||
instructions::riscv::insn_base::{StateInOut, WriteRD}, | ||
tables::InsnRecord, | ||
witness::LkMultiplicity, | ||
}; | ||
use core::mem::MaybeUninit; | ||
|
||
// Opcode: 1101111 | ||
|
||
/// This config handles the common part of the J-type instruction (JAL): | ||
/// - PC, cycle, fetch | ||
/// - Register access | ||
/// | ||
/// It does not witness the output rd value produced by the JAL opcode, but | ||
/// does constrain next_pc = pc + imm using the instruction table lookup | ||
#[derive(Debug)] | ||
pub struct JInstructionConfig<E: ExtensionField> { | ||
pub vm_state: StateInOut<E>, | ||
pub rd: WriteRD<E>, | ||
} | ||
|
||
impl<E: ExtensionField> JInstructionConfig<E> { | ||
pub fn construct_circuit( | ||
circuit_builder: &mut CircuitBuilder<E>, | ||
insn_kind: InsnKind, | ||
rd_written: RegisterExpr<E>, | ||
) -> Result<Self, ZKVMError> { | ||
// State in and out | ||
let vm_state = StateInOut::construct_circuit(circuit_builder, true)?; | ||
|
||
// Registers | ||
let rd = WriteRD::construct_circuit(circuit_builder, rd_written, vm_state.ts)?; | ||
|
||
// Fetch instruction | ||
circuit_builder.lk_fetch(&InsnRecord::new( | ||
vm_state.pc.expr(), | ||
(insn_kind.codes().opcode as usize).into(), | ||
rd.id.expr(), | ||
0.into(), | ||
0.into(), | ||
0.into(), | ||
vm_state.next_pc.unwrap().expr() - vm_state.pc.expr(), | ||
))?; | ||
|
||
Ok(JInstructionConfig { vm_state, rd }) | ||
} | ||
|
||
pub fn assign_instance( | ||
&self, | ||
instance: &mut [MaybeUninit<<E as ExtensionField>::BaseField>], | ||
lk_multiplicity: &mut LkMultiplicity, | ||
step: &StepRecord, | ||
) -> Result<(), ZKVMError> { | ||
self.vm_state.assign_instance(instance, step)?; | ||
self.rd.assign_instance(instance, lk_multiplicity, step)?; | ||
|
||
// Fetch the instruction. | ||
lk_multiplicity.fetch(step.pc().before.0); | ||
|
||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
mod jal; | ||
|
||
use super::RIVInstruction; | ||
use ceno_emul::InsnKind; | ||
use jal::JalCircuit; | ||
|
||
#[cfg(test)] | ||
mod test; | ||
|
||
pub struct JalOp; | ||
impl RIVInstruction for JalOp { | ||
const INST_KIND: InsnKind = InsnKind::JAL; | ||
} | ||
pub type JalInstruction<E> = JalCircuit<E, JalOp>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
use std::{marker::PhantomData, mem::MaybeUninit}; | ||
|
||
use ff_ext::ExtensionField; | ||
|
||
use crate::{ | ||
circuit_builder::CircuitBuilder, | ||
error::ZKVMError, | ||
expression::ToExpr, | ||
instructions::{ | ||
riscv::{constants::UInt, j_insn::JInstructionConfig, RIVInstruction}, | ||
Instruction, | ||
}, | ||
witness::LkMultiplicity, | ||
Value, | ||
}; | ||
use ceno_emul::PC_STEP_SIZE; | ||
|
||
pub struct JalConfig<E: ExtensionField> { | ||
pub j_insn: JInstructionConfig<E>, | ||
pub rd_written: UInt<E>, | ||
} | ||
|
||
pub struct JalCircuit<E, I>(PhantomData<(E, I)>); | ||
|
||
/// JAL instruction circuit | ||
/// | ||
/// Note: does not validate that next_pc is aligned by 4-byte increments, which | ||
/// should be verified by lookup argument of the next execution step against | ||
/// the program table | ||
/// | ||
/// Assumption: values for valid initial program counter must lie between | ||
/// 2^20 and 2^32 - 2^20 + 2 inclusive, probably enforced by the static | ||
/// program lookup table. If this assumption does not hold, then resulting | ||
/// value for next_pc may not correctly wrap mod 2^32 because of the use | ||
/// of native WitIn values for address space arithmetic. | ||
impl<E: ExtensionField, I: RIVInstruction> Instruction<E> for JalCircuit<E, I> { | ||
type InstructionConfig = JalConfig<E>; | ||
|
||
fn name() -> String { | ||
format!("{:?}", I::INST_KIND) | ||
} | ||
|
||
fn construct_circuit( | ||
circuit_builder: &mut CircuitBuilder<E>, | ||
) -> Result<JalConfig<E>, ZKVMError> { | ||
let rd_written = UInt::new(|| "rd_written", circuit_builder)?; | ||
|
||
let j_insn = JInstructionConfig::construct_circuit( | ||
circuit_builder, | ||
I::INST_KIND, | ||
rd_written.register_expr(), | ||
)?; | ||
|
||
circuit_builder.require_equal( | ||
|| "jal rd_written", | ||
rd_written.value(), | ||
j_insn.vm_state.pc.expr() + PC_STEP_SIZE.into(), | ||
)?; | ||
|
||
Ok(JalConfig { j_insn, rd_written }) | ||
} | ||
|
||
fn assign_instance( | ||
config: &Self::InstructionConfig, | ||
instance: &mut [MaybeUninit<E::BaseField>], | ||
lk_multiplicity: &mut LkMultiplicity, | ||
step: &ceno_emul::StepRecord, | ||
) -> Result<(), ZKVMError> { | ||
config | ||
.j_insn | ||
.assign_instance(instance, lk_multiplicity, step)?; | ||
|
||
let rd_written = Value::new(step.rd().unwrap().value.after, lk_multiplicity); | ||
config.rd_written.assign_value(instance, rd_written); | ||
|
||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
use ceno_emul::{ByteAddr, Change, StepRecord, PC_STEP_SIZE}; | ||
use goldilocks::GoldilocksExt2; | ||
use itertools::Itertools; | ||
use multilinear_extensions::mle::IntoMLEs; | ||
|
||
use crate::{ | ||
circuit_builder::{CircuitBuilder, ConstraintSystem}, | ||
instructions::Instruction, | ||
scheme::mock_prover::{MockProver, MOCK_PC_JAL, MOCK_PROGRAM}, | ||
}; | ||
|
||
use super::JalInstruction; | ||
|
||
#[test] | ||
fn test_opcode_jal() { | ||
let mut cs = ConstraintSystem::<GoldilocksExt2>::new(|| "riscv"); | ||
let mut cb = CircuitBuilder::new(&mut cs); | ||
let config = cb | ||
.namespace( | ||
|| "jal", | ||
|cb| { | ||
let config = JalInstruction::<GoldilocksExt2>::construct_circuit(cb); | ||
Ok(config) | ||
}, | ||
) | ||
.unwrap() | ||
.unwrap(); | ||
|
||
let pc_offset: i32 = -4i32; | ||
let new_pc: ByteAddr = ByteAddr(MOCK_PC_JAL.0.wrapping_add_signed(pc_offset)); | ||
let (raw_witin, _lkm) = JalInstruction::<GoldilocksExt2>::assign_instances( | ||
&config, | ||
cb.cs.num_witin as usize, | ||
vec![StepRecord::new_j_instruction( | ||
4, | ||
Change::new(MOCK_PC_JAL, new_pc), | ||
MOCK_PROGRAM[21], | ||
Change::new(0, (MOCK_PC_JAL + PC_STEP_SIZE).into()), | ||
0, | ||
)], | ||
) | ||
.unwrap(); | ||
|
||
MockProver::assert_satisfied( | ||
&mut cb, | ||
&raw_witin | ||
.de_interleaving() | ||
.into_mles() | ||
.into_iter() | ||
.map(|v| v.into()) | ||
.collect_vec(), | ||
None, | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters