From 83cd8b07888e01b6eb62a5d4fad7725fcc1a76d3 Mon Sep 17 00:00:00 2001 From: KimiWu Date: Fri, 12 Jul 2024 15:07:56 +0800 Subject: [PATCH] move risc-v stuff to insolated ones --- singer-utils/src/chip_handler/bytecode.rs | 19 ++- singer-utils/src/constants.rs | 25 +--- singer-utils/src/lib.rs | 1 + singer-utils/src/riscv_constant.rs | 171 ++++++++++++++++++++++ singer/benches/riscv/add.rs | 6 +- singer/src/instructions.rs | 2 - singer/src/instructions/riscv/add.rs | 29 ++-- singer/src/instructions_riscv_ext.rs | 59 ++++++++ singer/src/lib.rs | 1 + 9 files changed, 269 insertions(+), 44 deletions(-) create mode 100644 singer-utils/src/riscv_constant.rs create mode 100644 singer/src/instructions_riscv_ext.rs diff --git a/singer-utils/src/chip_handler/bytecode.rs b/singer-utils/src/chip_handler/bytecode.rs index c3afef76e..2d9b3592d 100644 --- a/singer-utils/src/chip_handler/bytecode.rs +++ b/singer-utils/src/chip_handler/bytecode.rs @@ -9,12 +9,12 @@ use crate::{ use super::{BytecodeChipOperations, ROMOperations}; -impl BytecodeChipOperations for ROMHandler { - fn bytecode_with_pc_opcode( +impl ROMHandler { + pub fn bytecode_with_pc( &mut self, circuit_builder: &mut CircuitBuilder, pc: &[CellId], - opcode: OpcodeType, + opcode: u64, ) { let key = [ vec![MixedCell::Constant(Ext::BaseField::from( @@ -26,9 +26,20 @@ impl BytecodeChipOperations for ROMHandler { self.rom_load_mixed( circuit_builder, &key, - &[MixedCell::Constant(Ext::BaseField::from(opcode as u64))], + &[MixedCell::Constant(Ext::BaseField::from(opcode))], ); } +} + +impl BytecodeChipOperations for ROMHandler { + fn bytecode_with_pc_opcode( + &mut self, + circuit_builder: &mut CircuitBuilder, + pc: &[CellId], + opcode: OpcodeType, + ) { + self.bytecode_with_pc(circuit_builder, pc, opcode.into()); + } fn bytecode_with_pc_byte( &mut self, diff --git a/singer-utils/src/constants.rs b/singer-utils/src/constants.rs index 2fabed656..812ea78d4 100644 --- a/singer-utils/src/constants.rs +++ b/singer-utils/src/constants.rs @@ -27,25 +27,12 @@ pub enum OpcodeType { SWAP2 = 0x91, SWAP4 = 0x93, RETURN = 0xf3, - // risc-v - RV_ADD = 0x33, + RISCV = 0xFF, } -// l - -// impl RV64Opcode { -// // Type R -// pub const ADD: RV64Opcode = RV64Opcode::R; -// pub const SUB: RV64Opcode = RV64Opcode::R; -// pub const SLL: RV64Opcode = RV64Opcode::R; -// pub const SLT: RV64Opcode = RV64Opcode::R; -// pub const SLTU: RV64Opcode = RV64Opcode::R; -// pub const XOR: RV64Opcode = RV64Opcode::R; -// pub const SRL: RV64Opcode = RV64Opcode::R; -// pub const SRA: RV64Opcode = RV64Opcode::R; -// pub const OR: RV64Opcode = RV64Opcode::R; -// pub const AND: RV64Opcode = RV64Opcode::R; -// // Type I -// pub const ADDI: RV64Opcode = RV64Opcode::I_ARITH; -// } +impl From for u64 { + fn from(opcode: OpcodeType) -> Self { + opcode as u64 + } +} diff --git a/singer-utils/src/lib.rs b/singer-utils/src/lib.rs index 5e98a75b6..9f1e3c957 100644 --- a/singer-utils/src/lib.rs +++ b/singer-utils/src/lib.rs @@ -4,6 +4,7 @@ pub mod chip_handler; pub mod chips; pub mod constants; pub mod error; +pub mod riscv_constant; pub mod structs; pub mod uint; diff --git a/singer-utils/src/riscv_constant.rs b/singer-utils/src/riscv_constant.rs new file mode 100644 index 000000000..c82318202 --- /dev/null +++ b/singer-utils/src/riscv_constant.rs @@ -0,0 +1,171 @@ +use strum_macros::EnumIter; + +/// This struct is used to define the opcode format for RISC-V instructions, +/// containing three main components: the opcode, funct3, and funct7 fields. +/// These fields are crucial for specifying the +/// exact operation and variants in the RISC-V instruction set architecture. +#[derive(Default, Clone)] +pub struct RvOpcode { + pub opcode: RV64IOpcode, + pub funct3: u8, + pub funct7: u8, +} + +impl From for u64 { + fn from(opcode: RvOpcode) -> Self { + let mut result: u64 = 0; + result |= (opcode.opcode as u64) & 0xFF; + result |= ((opcode.funct3 as u64) & 0xFF) << 8; + result |= ((opcode.funct7 as u64) & 0xFF) << 16; + result + } +} + +/// List all instruction formats in RV64I which contains +/// R-Type, I-Type, S-Type, B-Type, U-Type, J-Type and special type. +#[derive(Debug, Clone)] +pub enum RV64IOpcode { + UNKNOWN = 0x00, + + R = 0x33, + I_LOAD = 0x03, + I_ARITH = 0x13, + S = 0x63, + B = 0x23, + U_LUI = 0x37, + U_AUIPC = 0x7, + J = 0x6F, + JAR = 0x67, + SYS = 0x73, +} + +impl Default for RV64IOpcode { + fn default() -> Self { + RV64IOpcode::UNKNOWN + } +} + +impl From for u8 { + fn from(opcode: RV64IOpcode) -> Self { + opcode as u8 + } +} + +#[derive(Debug, Clone, Copy, EnumIter)] +pub enum RvInstructions { + // Type R + ADD = 0, + SUB, + SLL, + SLTU, + SLT, + XOR, + SRL, + SRA, + OR, + AND, + // Type I-LOAD + LB, + LH, + LW, + LBU, + LHU, + + // a workaround to get number of valid instructions + END, +} + +impl From for RvOpcode { + fn from(ins: RvInstructions) -> Self { + // Find the instruction format here: + // https://fraserinnovations.com/risc-v/risc-v-instruction-set-explanation/ + match ins { + // Type R + RvInstructions::ADD => RvOpcode { + opcode: RV64IOpcode::R, + funct3: 0b000 as u8, + funct7: 0, + }, + RvInstructions::SUB => RvOpcode { + opcode: RV64IOpcode::R, + funct3: 0b000 as u8, + funct7: 0b010_0000, + }, + RvInstructions::SLL => RvOpcode { + opcode: RV64IOpcode::R, + funct3: 0b001 as u8, + funct7: 0, + }, + RvInstructions::SLT => RvOpcode { + opcode: RV64IOpcode::R, + funct3: 0b010 as u8, + funct7: 0, + }, + RvInstructions::SLTU => RvOpcode { + opcode: RV64IOpcode::R, + funct3: 0b011 as u8, + funct7: 0, + }, + RvInstructions::XOR => RvOpcode { + opcode: RV64IOpcode::R, + funct3: 0b100 as u8, + funct7: 0, + }, + RvInstructions::SRL => RvOpcode { + opcode: RV64IOpcode::R, + funct3: 0b101 as u8, + funct7: 0, + }, + RvInstructions::SRA => RvOpcode { + opcode: RV64IOpcode::R, + funct3: 0b101 as u8, + funct7: 0b010_0000, + }, + RvInstructions::OR => RvOpcode { + opcode: RV64IOpcode::R, + funct3: 0b110 as u8, + funct7: 0, + }, + RvInstructions::AND => RvOpcode { + opcode: RV64IOpcode::R, + funct3: 0b111 as u8, + funct7: 0, + }, + // Type I-LOAD + RvInstructions::LB => RvOpcode { + opcode: RV64IOpcode::I_LOAD, + funct3: 0b000 as u8, + funct7: 0, + }, + RvInstructions::LH => RvOpcode { + opcode: RV64IOpcode::I_LOAD, + funct3: 0b001 as u8, + funct7: 0, + }, + RvInstructions::LW => RvOpcode { + opcode: RV64IOpcode::I_LOAD, + funct3: 0b010 as u8, + funct7: 0, + }, + RvInstructions::LBU => RvOpcode { + opcode: RV64IOpcode::I_LOAD, + funct3: 0b100 as u8, + funct7: 0, + }, + RvInstructions::LHU => RvOpcode { + opcode: RV64IOpcode::I_LOAD, + funct3: 0b101 as u8, + funct7: 0, + }, + // TODO add more + _ => RvOpcode::default(), + } + } +} + +impl From for u64 { + fn from(ins: RvInstructions) -> Self { + let opcode: RvOpcode = ins.into(); + opcode.into() + } +} diff --git a/singer/benches/riscv/add.rs b/singer/benches/riscv/add.rs index b8af0731d..bc950bddd 100644 --- a/singer/benches/riscv/add.rs +++ b/singer/benches/riscv/add.rs @@ -36,7 +36,7 @@ const RAYON_NUM_THREADS: usize = 8; use singer::{ instructions::{ - riscv::add::AddInstruction, Instruction, InstructionGraph, SingerCircuitBuilder, + self, riscv::add::AddInstruction, Instruction, InstructionGraph, SingerCircuitBuilder, }, scheme::GKRGraphProverState, CircuitWiresIn, SingerGraphBuilder, SingerParams, @@ -69,7 +69,7 @@ fn bench_add(c: &mut Criterion) { }; let chip_challenges = ChipChallenges::default(); let circuit_builder = - SingerCircuitBuilder::::new(chip_challenges).expect("circuit builder failed"); + SingerCircuitBuilder::::new_riscv(chip_challenges).expect("circuit builder failed"); for instance_num_vars in 11..12 { // expand more input size once runtime is acceptable @@ -115,7 +115,7 @@ fn bench_add(c: &mut Criterion) { &mut singer_builder.graph_builder, &mut singer_builder.chip_builder, &circuit_builder.insts_circuits - [>::OPCODE as usize], + [instructions::riscv::add::RV_INSTRUCTION as usize], vec![phase0], &real_challenges, 1 << instance_num_vars, diff --git a/singer/src/instructions.rs b/singer/src/instructions.rs index c7e9a68ea..d8428242c 100644 --- a/singer/src/instructions.rs +++ b/singer/src/instructions.rs @@ -88,8 +88,6 @@ pub(crate) fn construct_instruction_circuits( 0x93 => SwapInstruction::<4>::construct_circuits(challenges), 0xF3 => ReturnInstruction::construct_circuits(challenges), - // RISC-V iSA - 0x33 => riscv::add::AddInstruction::construct_circuits(challenges), _ => Ok(vec![]), // TODO: Add more instructions. } } diff --git a/singer/src/instructions/riscv/add.rs b/singer/src/instructions/riscv/add.rs index 6f72e8c8e..c670d3948 100644 --- a/singer/src/instructions/riscv/add.rs +++ b/singer/src/instructions/riscv/add.rs @@ -5,11 +5,11 @@ use paste::paste; use simple_frontend::structs::{CircuitBuilder, MixedCell}; use singer_utils::{ chip_handler::{ - BytecodeChipOperations, GlobalStateChipOperations, OAMOperations, ROMOperations, - RegisterChipOperations, + GlobalStateChipOperations, OAMOperations, ROMOperations, RegisterChipOperations, }, constants::OpcodeType, register_witness, + riscv_constant::RvInstructions, structs::{PCUInt, RAMHandler, ROMHandler, RegisterUInt, TSUInt, UInt64}, uint::{UIntAddSub, UIntCmp}, }; @@ -52,9 +52,12 @@ register_witness!( } ); +// TODO a workaround to keep the risc-v instruction +pub const RV_INSTRUCTION: RvInstructions = RvInstructions::ADD; impl Instruction for AddInstruction { - const OPCODE: OpcodeType = OpcodeType::RV_ADD; - const NAME: &'static str = "RV_ADD"; + // OPCODE is not used in RISC-V case, just for compatibility + const OPCODE: OpcodeType = OpcodeType::RISCV; + const NAME: &'static str = "ADD"; fn construct_circuit(challenges: ChipChallenges) -> Result, ZKVMError> { let mut circuit_builder = CircuitBuilder::new(); let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); @@ -68,11 +71,7 @@ impl Instruction for AddInstruction { let zero_cell_ids = [0]; // Bytecode check for (pc, add) - rom_handler.bytecode_with_pc_opcode( - &mut circuit_builder, - pc.values(), - >::OPCODE, - ); + rom_handler.bytecode_with_pc(&mut circuit_builder, pc.values(), RV_INSTRUCTION.into()); // State update ram_handler.state_in( @@ -190,8 +189,8 @@ mod test { use crate::{ instructions::{ - riscv::add::AddInstruction, ChipChallenges, Instruction, InstructionGraph, - SingerCircuitBuilder, + riscv::add::{AddInstruction, RV_INSTRUCTION}, + ChipChallenges, Instruction, InstructionGraph, SingerCircuitBuilder, }, scheme::GKRGraphProverState, test::{get_uint_params, test_opcode_circuit, u2vec}, @@ -252,8 +251,6 @@ mod test { if cfg!(feature = "dbg-opcode") { println!("{:?}", inst_circuit.circuit.assert_consts); - println!("======"); - // println!("{:?}", inst_circuit.circuit.layers); } let mut phase0_values_map = BTreeMap::>::new(); @@ -328,7 +325,7 @@ mod test { let c = GoldilocksExt2::from(66u64); let circuit_witness_challenges = vec![c; 3]; - let circuit_witness = test_opcode_circuit( + test_opcode_circuit( &inst_circuit, &phase0_idx_map, phase0_witness_size, @@ -340,7 +337,7 @@ mod test { fn bench_add_instruction_helper(instance_num_vars: usize) { let chip_challenges = ChipChallenges::default(); let circuit_builder = - SingerCircuitBuilder::::new(chip_challenges).expect("circuit builder failed"); + SingerCircuitBuilder::::new_riscv(chip_challenges).expect("circuit builder failed"); let mut singer_builder = SingerGraphBuilder::::new(); let mut rng = test_rng(); @@ -362,7 +359,7 @@ mod test { let _ = AddInstruction::construct_graph_and_witness( &mut singer_builder.graph_builder, &mut singer_builder.chip_builder, - &circuit_builder.insts_circuits[>::OPCODE as usize], + &circuit_builder.insts_circuits[RV_INSTRUCTION as usize], vec![phase0], &real_challenges, 1 << instance_num_vars, diff --git a/singer/src/instructions_riscv_ext.rs b/singer/src/instructions_riscv_ext.rs new file mode 100644 index 000000000..a71f26421 --- /dev/null +++ b/singer/src/instructions_riscv_ext.rs @@ -0,0 +1,59 @@ +use ff_ext::ExtensionField; +use singer_utils::{ + chips::IntoEnumIterator, + riscv_constant::{RV64IOpcode, RvInstructions, RvOpcode}, + structs::ChipChallenges, +}; + +use crate::{ + error::ZKVMError, + instructions::{riscv, InstCircuit, InstructionGraph, SingerCircuitBuilder}, +}; + +impl SingerCircuitBuilder { + pub fn new_riscv(challenges: ChipChallenges) -> Result { + let ins_len = RvInstructions::END as usize; + let mut insts_circuits = Vec::with_capacity(256); + for opcode in RvInstructions::iter() { + insts_circuits.push(construct_instruction_circuits(opcode.into(), challenges)?); + } + for _ in ins_len..255 { + insts_circuits.push(construct_instruction_circuits( + RvInstructions::END.into(), + challenges, + )?); + } + let insts_circuits: [Vec>; 256] = insts_circuits + .try_into() + .map_err(|_| ZKVMError::CircuitError)?; + Ok(Self { + insts_circuits, + challenges, + }) + } +} + +fn process_opcode_r( + instruction: RvOpcode, + challenges: ChipChallenges, +) -> Result>, ZKVMError> { + // Find the instruction format here: + // https://fraserinnovations.com/risc-v/risc-v-instruction-set-explanation/ + match instruction.funct3 { + 0b000 => match instruction.funct7 { + 0b000_0000 => riscv::add::AddInstruction::construct_circuits(challenges), + _ => Ok(vec![]), // TODO: Add more operations. + }, + _ => Ok(vec![]), // TODO: Add more instructions. + } +} + +pub(crate) fn construct_instruction_circuits( + instruction: RvOpcode, + challenges: ChipChallenges, +) -> Result>, ZKVMError> { + match instruction.opcode { + RV64IOpcode::R => process_opcode_r(instruction, challenges), + _ => Ok(vec![]), // TODO: Add more instructions. + } +} diff --git a/singer/src/lib.rs b/singer/src/lib.rs index 614ae606f..dd31f6db9 100644 --- a/singer/src/lib.rs +++ b/singer/src/lib.rs @@ -15,6 +15,7 @@ use std::mem; pub mod error; pub mod instructions; +pub mod instructions_riscv_ext; pub mod scheme; #[cfg(test)] pub mod test;