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: Connect CPU to ECALL tables #364

Merged
merged 65 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
e929119
interactionkind alu => coprocessor
hidenori-shinohara Feb 15, 2024
a64cf4e
add blake3 test
hidenori-shinohara Feb 16, 2024
d5b0bf2
alu => coprocessor
hidenori-shinohara Feb 16, 2024
216f1ce
mimincing is-alu, adding is_precompile
hidenori-shinohara Feb 16, 2024
e3087fc
elf test sh
hidenori-shinohara Feb 16, 2024
26bc1c0
todo
hidenori-shinohara Feb 16, 2024
7aaa831
breaking tests, but i don't think there's otherw ays
hidenori-shinohara Feb 16, 2024
e176a16
oh wow the test passes..?
hidenori-shinohara Feb 16, 2024
3582d30
io_test.sh
hidenori-shinohara Feb 16, 2024
39499f1
this breaks some tests like IO but i'm trying to make it work only with
hidenori-shinohara Feb 16, 2024
87686b7
small progress
hidenori-shinohara Feb 16, 2024
a7e6d9f
is_bale3_compress
hidenori-shinohara Feb 16, 2024
3789d31
i think i finally figured out where to set is-balke3
hidenori-shinohara Feb 16, 2024
0d7cc97
finally setting is_blake_3 to 1, causing an interaciton error
hidenori-shinohara Feb 16, 2024
377fe4b
confirms that it's sending one too many times!
hidenori-shinohara Feb 16, 2024
9c43c1f
okay blake3 interaction is workign!
hidenori-shinohara Feb 16, 2024
5900756
add more stuff
hidenori-shinohara Feb 16, 2024
97e4953
make progress
hidenori-shinohara Feb 16, 2024
16c8e4f
clena up
hidenori-shinohara Feb 16, 2024
1ffa74c
ec_add_test, failing :(
hidenori-shinohara Feb 16, 2024
904cc5b
fix an interaction bug by ec_add,b but there's still more issues.
hidenori-shinohara Feb 16, 2024
752bf81
sha compress test
hidenori-shinohara Feb 16, 2024
5c88aa1
use an argumetn in sha compress
hidenori-shinohara Feb 16, 2024
f40ce44
sha compress test working
hidenori-shinohara Feb 16, 2024
10e01e6
a better way to send interactions
hidenori-shinohara Feb 16, 2024
3c7b0b4
compiles, simplified
puma314 Mar 12, 2024
bc82307
added syscall cycle encodinG
puma314 Mar 12, 2024
f674627
added ed_add
puma314 Mar 12, 2024
737c960
rename back
puma314 Mar 12, 2024
6409f13
asdf
puma314 Mar 12, 2024
12583aa
small refactor, at least it now executes
puma314 Mar 12, 2024
17052a0
Merge branch 'main' into hide/alu-comprocessor
puma314 Mar 12, 2024
3b724f2
compiles after merge
puma314 Mar 12, 2024
bf75747
CPU fails
puma314 Mar 12, 2024
4511b79
Cleanup
puma314 Mar 12, 2024
bb7c153
More cleanup
puma314 Mar 12, 2024
371b33b
Update precompiles to make sure 2nd arg if unused is 0
puma314 Mar 12, 2024
0825fb0
Cleanup files that shouldn't diff
puma314 Mar 12, 2024
51cc8d4
Progress on cleaning up ecall
puma314 Mar 12, 2024
0ed80b6
asdf
puma314 Mar 13, 2024
7c12748
more commit
puma314 Mar 13, 2024
5456e44
more fixes
puma314 Mar 13, 2024
2825244
ed decompress passees
puma314 Mar 13, 2024
4a039b4
fixed most single-row precompiles except k256 decompress
puma314 Mar 13, 2024
a94b2ae
Fixed lwa ecall test
puma314 Mar 17, 2024
a9f82a8
fixed sha256 extend
puma314 Mar 17, 2024
beaf0d8
keccak now works
puma314 Mar 18, 2024
4839a76
Compress now works
puma314 Mar 18, 2024
c1ffc11
fixed blake
puma314 Mar 18, 2024
e4a756d
ALL TESTS PASS PRAISE
puma314 Mar 18, 2024
fd15251
cleanup
puma314 Mar 18, 2024
09fb744
cleanup
puma314 Mar 18, 2024
e41b6ad
read bits from register instead of memory
ctian1 Mar 18, 2024
5f22560
update syscall
ctian1 Mar 18, 2024
ff188cd
fix
ctian1 Mar 18, 2024
3c14634
changes
puma314 Mar 19, 2024
b95a2c9
exaples
puma314 Mar 19, 2024
51bb9a1
updates
puma314 Mar 19, 2024
34f90de
Merge branch 'main' into hide/alu-comprocessor
puma314 Mar 19, 2024
f29d6f6
cleanup
puma314 Mar 19, 2024
dc9a01d
more cleanup
puma314 Mar 19, 2024
cd75655
tests pass leggo
puma314 Mar 20, 2024
38ec42c
Final cleanup
puma314 Mar 20, 2024
83f4105
make derive easier
puma314 Mar 20, 2024
1a1c865
clippy
puma314 Mar 20, 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
30 changes: 29 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Development Guide

This is a guide with helpful information for developers who want to contribute to the SP1 project.

## Getting started

You can run the test suite in SP1 core by running the following command:

```bash
cd core
cargo test
```



**Debug Constraint Failure**

To debug constraint failures, you can use the `--features debug` feature alongside `--no-default-features` to eliminate the "perf" feature. For example:

```
RUST_LOG=info RUST_BACKTRACE=1 cargo test syscall::precompiles::edwards::ed_add::tests::test_ed_add_simple --release --features debug --no-default-features -- --nocapture
```
3 changes: 3 additions & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ p3-uni-stark = { workspace = true }
p3-util = { workspace = true }
rrs-lib = { git = "https://github.com/GregAC/rrs.git" }
sp1-derive = { path = "../derive" }
sp1-zkvm = { path = "../zkvm/entrypoint" }

anyhow = "1.0.79"
blake3 = "1.5"
Expand All @@ -59,6 +60,8 @@ tracing = "0.1.40"
tracing-forest = { version = "0.1.6", features = ["ansi", "smallvec"] }
tracing-log = "0.2.0"
tracing-subscriber = { version = "0.3.17", features = ["std", "env-filter"] }
strum_macros = "0.26.2"
strum = "0.26.2"

[dev-dependencies]
criterion = "0.5.1"
Expand Down
60 changes: 60 additions & 0 deletions core/src/air/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,66 @@ pub trait AluAirBuilder: BaseAirBuilder {
InteractionKind::Alu,
));
}

/// Sends an syscall operation to be processed (with "ECALL" opcode).
fn send_syscall<EShard, EClk, Ea, Eb, Ec, EMult>(
&mut self,
shard: EShard,
clk: EClk,
syscall_id: Ea,
arg1: Eb,
arg2: Ec,
multiplicity: EMult,
) where
EShard: Into<Self::Expr> + Clone,
EClk: Into<Self::Expr> + Clone,
Ea: Into<Self::Expr> + Clone,
Eb: Into<Self::Expr> + Clone,
Ec: Into<Self::Expr> + Clone,
EMult: Into<Self::Expr>,
{
self.send(AirInteraction::new(
vec![
shard.clone().into(),
clk.clone().into(),
syscall_id.clone().into(),
arg1.clone().into(),
arg2.clone().into(),
],
multiplicity.into(),
InteractionKind::Syscall,
));
}

/// Receives a syscall operation to be processed.
fn receive_syscall<EShard, EClk, Ea, Eb, Ec, EMult>(
&mut self,
shard: EShard,
clk: EClk,
syscall_id: Ea,
arg1: Eb,
arg2: Ec,
multiplicity: EMult,
) where
EShard: Into<Self::Expr> + Clone,
EClk: Into<Self::Expr> + Clone,
Ea: Into<Self::Expr> + Clone,
Eb: Into<Self::Expr> + Clone,
Ec: Into<Self::Expr> + Clone,
EMult: Into<Self::Expr>,
{
self.receive(AirInteraction::new(
vec![
shard.clone().into(),
clk.clone().into(),
syscall_id.clone().into(),
arg1.clone().into(),
arg2.clone().into(),
],
multiplicity.into(),
InteractionKind::Syscall,
));
}
}

/// A trait which contains methods related to memory interactions in an AIR.
Expand Down
90 changes: 78 additions & 12 deletions core/src/cpu/air/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,6 @@ where
let is_branch_instruction: AB::Expr = self.is_branch_instruction::<AB>(&local.selectors);
let is_alu_instruction: AB::Expr = self.is_alu_instruction::<AB>(&local.selectors);

// Clock constraints.
// TODO: Handle dynamic clock jumps based on precompiles.
// builder
// .when_transition()
// .assert_eq(local.clk + AB::F::from_canonical_u32(4), next.clk);

// Program constraints.
builder.send_program(local.pc, local.instruction, local.selectors, local.is_real);

Expand Down Expand Up @@ -129,7 +123,9 @@ where
// AUIPC instruction.
self.auipc_eval(builder, local);

// ALU instructions.
// ECALL instruction.
let (_num_cycles, _is_halt) = self.ecall_eval(builder, local, next);

builder.send_alu(
local.instruction.opcode,
local.op_a_val(),
Expand All @@ -138,13 +134,21 @@ where
is_alu_instruction,
);

// ECALL instructions.
// TODO: Need to handle HALT ecall
// For all non branch or jump instructions, verify that next.pc == pc + 4
// TODO: update the PC.
// Verify that the pc increments by 4 for all instructions except branch, jump and halt instructions.
// The other case is handled by eval_jump, eval_branch and eval_ecall.
// builder
// .when_not(is_branch_instruction + local.selectors.is_jal + local.selectors.is_jalr)
// .when_not(
// is_branch_instruction + local.selectors.is_jal + local.selectors.is_jalr + is_halt,
// )
// .assert_eq(local.pc + AB::Expr::from_canonical_u8(4), next.pc);

// TODO: update the clk.
// let clk_increment = AB::Expr::from_canonical_u32(4) + syscall_cycles;
// builder
// .when_transition()
// .assert_eq(local.clk + clk_increment, next.clk);

// Range checks.
builder.assert_bool(local.is_real);

Expand All @@ -157,14 +161,22 @@ where
}

impl CpuChip {
/// Whether the instruction is a memory instruction.
/// Whether the instruction is an ALU instruction.
pub(crate) fn is_alu_instruction<AB: SP1AirBuilder>(
&self,
opcode_selectors: &OpcodeSelectorCols<AB::Var>,
) -> AB::Expr {
opcode_selectors.is_alu.into()
}

/// Whether the instruction is an ECALL instruction.
pub(crate) fn is_ecall_instruction<AB: SP1AirBuilder>(
&self,
opcode_selectors: &OpcodeSelectorCols<AB::Var>,
) -> AB::Expr {
opcode_selectors.is_ecall.into()
}

/// Constraints related to jump operations.
pub(crate) fn jump_ops_eval<AB: SP1AirBuilder>(
&self,
Expand Down Expand Up @@ -232,6 +244,60 @@ impl CpuChip {
local.selectors.is_auipc,
);
}

/// Constraints related to the ECALL opcode.
pub(crate) fn ecall_eval<AB: SP1AirBuilder>(
&self,
builder: &mut AB,
local: &CpuCols<AB::Var>,
_next: &CpuCols<AB::Var>,
) -> (AB::Var, AB::Var) {
let is_ecall_instruction = self.is_ecall_instruction::<AB>(&local.selectors);
// The syscall code is the read-in value of op_a at the start of the instruction.
let syscall_code = local.op_a_access.prev_value();
// We interpret the syscall_code as little-endian bytes and interpret each byte as a u8
// with different information. Read more about the format in runtime::syscall::SyscallCode.
let syscall_id = syscall_code[0];
let send_to_table = syscall_code[1]; // Does the syscall have a table that should be sent.
let num_cycles = syscall_code[2]; // How many extra cycles to increment the clk for the syscall.
let is_halt = syscall_code[3]; // Whether or not the syscall is a halt.
Comment on lines +261 to +263
Copy link
Member

Choose a reason for hiding this comment

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

might need to check that send_to_table and is_halt are bool


// Check that the ecall_mul_send_to_table column is equal to send_to_table * is_ecall_instruction.
// This is a separate column because it is used as a multiplicity in an interaction which
// requires degree 1 columns.
builder.assert_eq(
send_to_table * is_ecall_instruction,
local.ecall_mul_send_to_table,
);
builder.send_syscall(
local.shard,
local.clk,
syscall_id,
local.op_b_val().reduce::<AB>(),
local.op_c_val().reduce::<AB>(),
local.ecall_mul_send_to_table,
);

// For LWA we assume prover-supplied values. Although to be honest, I'm not 100% sure we need this.
// builder
// .when(local.is_ecall)
// .when_not(is_lwa)
// .assert_word_eq(local.op_a_val(), local.op_a_access.prev_value);

// TODO: fill in constraints if the syscall is HALT.
// For halt instructions, the next pc is 0.
// builder
// .when(is_halt)
// .assert_eq(next.pc, AB::Expr::from_canonical_u16(0));
// // If we're halting and it's a transition, then the next.is_real should be 0.
// builder
// .when_transition()
// .when(is_halt)
// .assert_eq(next.is_real, AB::Expr::zero());
// builder.when_first_row().assert_one(local.is_real);
// We probably need a "halted" flag, this can be "is_noop" that turns on to control "is_real".
(num_cycles, is_halt)
}
}

impl<F> BaseAir<F> for CpuChip {
Expand Down
4 changes: 4 additions & 0 deletions core/src/cpu/columns/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ pub struct CpuCols<T: Copy> {
/// The unsigned memory value is the value after the offset logic is applied. Used for the load
/// memory opcodes (i.e. LB, LH, LW, LBU, and LHU).
pub unsigned_mem_val: Word<T>,

/// The result of selectors.is_ecall * the send_to_table column for the ECALL opcode.
/// TODO: this can be moved into `opcode_specific_columns` for ECALL.
pub ecall_mul_send_to_table: T,
}

impl<T: Copy> CpuCols<T> {
Expand Down
6 changes: 6 additions & 0 deletions core/src/cpu/columns/opcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ pub struct OpcodeSelectorCols<T> {
/// Table selectors for opcodes.
pub is_alu: T,

/// Table selectors for opcodes.
pub is_ecall: T,

/// Memory Instructions.
pub is_lb: T,
pub is_lbu: T,
Expand Down Expand Up @@ -55,6 +58,8 @@ impl<F: PrimeField> OpcodeSelectorCols<F> {

if instruction.is_alu_instruction() {
self.is_alu = F::one();
} else if instruction.is_ecall_instruction() {
self.is_ecall = F::one();
} else if instruction.is_memory_instruction() {
match instruction.opcode {
Opcode::LB => self.is_lb = F::one(),
Expand Down Expand Up @@ -108,6 +113,7 @@ impl<T> IntoIterator for OpcodeSelectorCols<T> {
self.imm_b,
self.imm_c,
self.is_alu,
self.is_ecall,
self.is_lb,
self.is_lbu,
self.is_lh,
Expand Down
10 changes: 10 additions & 0 deletions core/src/cpu/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ impl CpuChip {
self.populate_branch(cols, event, &mut new_alu_events);
self.populate_jump(cols, event, &mut new_alu_events);
self.populate_auipc(cols, event, &mut new_alu_events);
self.populate_ecall(cols, event);

// Assert that the instruction is not a no-op.
cols.is_real = F::one();
Expand Down Expand Up @@ -475,6 +476,15 @@ impl CpuChip {
}
}

/// Populate columns related to ECALL.
fn populate_ecall<F: PrimeField>(&self, cols: &mut CpuCols<F>, _: CpuEvent) {
if cols.selectors.is_ecall == F::one() {
// The send_to_table column is the 1st entry of the op_a_access column prev_value field.
// Look at `ecall_eval` in cpu/air/mod.rs for the corresponding constraint and explanation.
cols.ecall_mul_send_to_table = cols.selectors.is_ecall * cols.op_a_access.prev_value[1];
}
}

fn pad_to_power_of_two<F: PrimeField>(values: &mut Vec<F>) {
let len: usize = values.len();
let n_real_rows = values.len() / NUM_CPU_COLS;
Expand Down
6 changes: 3 additions & 3 deletions core/src/disassembler/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,11 +324,11 @@ impl InstructionProcessor for InstructionTranspiler {
fn process_ecall(&mut self) -> Self::InstructionResult {
Instruction::new(
Opcode::ECALL,
Register::X10 as u32,
Register::X5 as u32,
0,
Register::X10 as u32,
Register::X11 as u32,
false,
false,
true,
)
}

Expand Down
Loading
Loading