openvm_native_circuit/adapters/
convert_adapter.rs1use std::{
2 borrow::{Borrow, BorrowMut},
3 mem::size_of,
4};
5
6use openvm_circuit::{
7 arch::{
8 get_record_from_slice, AdapterAirContext, AdapterTraceExecutor, AdapterTraceFiller,
9 BasicAdapterInterface, ExecutionBridge, ExecutionState, MinimalInstruction, VmAdapterAir,
10 },
11 system::{
12 memory::{
13 offline_checker::{
14 MemoryBridge, MemoryReadAuxCols, MemoryReadAuxRecord, MemoryWriteAuxCols,
15 MemoryWriteBytesAuxRecord,
16 },
17 online::TracingMemory,
18 MemoryAddress, MemoryAuxColsFactory,
19 },
20 native_adapter::util::tracing_read_native,
21 },
22};
23use openvm_circuit_primitives::AlignedBytesBorrow;
24use openvm_circuit_primitives_derive::AlignedBorrow;
25use openvm_instructions::{
26 instruction::Instruction, program::DEFAULT_PC_STEP, riscv::RV32_MEMORY_AS,
27};
28use openvm_native_compiler::conversion::AS;
29use openvm_rv32im_circuit::adapters::tracing_write;
30use openvm_stark_backend::{
31 interaction::InteractionBuilder,
32 p3_air::BaseAir,
33 p3_field::{Field, FieldAlgebra, PrimeField32},
34};
35
36#[repr(C)]
37#[derive(AlignedBorrow)]
38pub struct ConvertAdapterCols<T, const READ_SIZE: usize, const WRITE_SIZE: usize> {
39 pub from_state: ExecutionState<T>,
40 pub a_pointer: T,
41 pub b_pointer: T,
42 pub writes_aux: [MemoryWriteAuxCols<T, WRITE_SIZE>; 1],
43 pub reads_aux: [MemoryReadAuxCols<T>; 1],
44}
45
46#[derive(Clone, Copy, Debug, derive_new::new)]
47pub struct ConvertAdapterAir<const READ_SIZE: usize, const WRITE_SIZE: usize> {
48 pub(super) execution_bridge: ExecutionBridge,
49 pub(super) memory_bridge: MemoryBridge,
50}
51
52impl<F: Field, const READ_SIZE: usize, const WRITE_SIZE: usize> BaseAir<F>
53 for ConvertAdapterAir<READ_SIZE, WRITE_SIZE>
54{
55 fn width(&self) -> usize {
56 ConvertAdapterCols::<F, READ_SIZE, WRITE_SIZE>::width()
57 }
58}
59
60impl<AB: InteractionBuilder, const READ_SIZE: usize, const WRITE_SIZE: usize> VmAdapterAir<AB>
61 for ConvertAdapterAir<READ_SIZE, WRITE_SIZE>
62{
63 type Interface =
64 BasicAdapterInterface<AB::Expr, MinimalInstruction<AB::Expr>, 1, 1, READ_SIZE, WRITE_SIZE>;
65
66 fn eval(
67 &self,
68 builder: &mut AB,
69 local: &[AB::Var],
70 ctx: AdapterAirContext<AB::Expr, Self::Interface>,
71 ) {
72 let cols: &ConvertAdapterCols<_, READ_SIZE, WRITE_SIZE> = local.borrow();
73 let timestamp = cols.from_state.timestamp;
74 let mut timestamp_delta = 0usize;
75 let mut timestamp_pp = || {
76 timestamp_delta += 1;
77 timestamp + AB::F::from_canonical_usize(timestamp_delta - 1)
78 };
79
80 let d = AB::Expr::TWO;
81 let e = AB::Expr::from_canonical_u32(AS::Native as u32);
82
83 self.memory_bridge
84 .read(
85 MemoryAddress::new(e.clone(), cols.b_pointer),
86 ctx.reads[0].clone(),
87 timestamp_pp(),
88 &cols.reads_aux[0],
89 )
90 .eval(builder, ctx.instruction.is_valid.clone());
91
92 self.memory_bridge
93 .write(
94 MemoryAddress::new(d.clone(), cols.a_pointer),
95 ctx.writes[0].clone(),
96 timestamp_pp(),
97 &cols.writes_aux[0],
98 )
99 .eval(builder, ctx.instruction.is_valid.clone());
100
101 self.execution_bridge
102 .execute_and_increment_or_set_pc(
103 ctx.instruction.opcode,
104 [
105 cols.a_pointer.into(),
106 cols.b_pointer.into(),
107 AB::Expr::ZERO,
108 d,
109 e,
110 ],
111 cols.from_state,
112 AB::F::from_canonical_usize(timestamp_delta),
113 (DEFAULT_PC_STEP, ctx.to_pc),
114 )
115 .eval(builder, ctx.instruction.is_valid);
116 }
117
118 fn get_from_pc(&self, local: &[AB::Var]) -> AB::Var {
119 let cols: &ConvertAdapterCols<_, READ_SIZE, WRITE_SIZE> = local.borrow();
120 cols.from_state.pc
121 }
122}
123
124#[repr(C)]
125#[derive(AlignedBytesBorrow, Debug)]
126pub struct ConvertAdapterRecord<F, const READ_SIZE: usize, const WRITE_SIZE: usize> {
127 pub from_pc: u32,
128 pub from_timestamp: u32,
129
130 pub a_ptr: F,
131 pub b_ptr: F,
132
133 pub read_aux: MemoryReadAuxRecord,
134 pub write_aux: MemoryWriteBytesAuxRecord<WRITE_SIZE>,
135}
136
137#[derive(derive_new::new, Clone, Copy)]
138pub struct ConvertAdapterExecutor<const READ_SIZE: usize, const WRITE_SIZE: usize>;
139
140#[derive(derive_new::new)]
141pub struct ConvertAdapterFiller<const READ_SIZE: usize, const WRITE_SIZE: usize>;
142
143impl<F: PrimeField32, const READ_SIZE: usize, const WRITE_SIZE: usize> AdapterTraceExecutor<F>
144 for ConvertAdapterExecutor<READ_SIZE, WRITE_SIZE>
145{
146 const WIDTH: usize = size_of::<ConvertAdapterCols<u8, READ_SIZE, WRITE_SIZE>>();
147 type ReadData = [F; READ_SIZE];
148 type WriteData = [u8; WRITE_SIZE];
149 type RecordMut<'a> = &'a mut ConvertAdapterRecord<F, READ_SIZE, WRITE_SIZE>;
150
151 #[inline(always)]
152 fn start(pc: u32, memory: &TracingMemory, record: &mut Self::RecordMut<'_>) {
153 record.from_pc = pc;
154 record.from_timestamp = memory.timestamp;
155 }
156
157 #[inline(always)]
158 fn read(
159 &self,
160 memory: &mut TracingMemory,
161 instruction: &Instruction<F>,
162 record: &mut Self::RecordMut<'_>,
163 ) -> Self::ReadData {
164 let &Instruction { b, e, .. } = instruction;
165 debug_assert_eq!(e.as_canonical_u32(), AS::Native as u32);
166
167 record.b_ptr = b;
168
169 tracing_read_native(
170 memory,
171 b.as_canonical_u32(),
172 &mut record.read_aux.prev_timestamp,
173 )
174 }
175
176 #[inline(always)]
177 fn write(
178 &self,
179 memory: &mut TracingMemory,
180 instruction: &Instruction<F>,
181 data: Self::WriteData,
182 record: &mut Self::RecordMut<'_>,
183 ) {
184 let &Instruction { a, d, .. } = instruction;
185
186 debug_assert_eq!(d.as_canonical_u32(), RV32_MEMORY_AS);
187
188 record.a_ptr = a;
189 tracing_write(
190 memory,
191 RV32_MEMORY_AS,
192 a.as_canonical_u32(),
193 data,
194 &mut record.write_aux.prev_timestamp,
195 &mut record.write_aux.prev_data,
196 );
197 }
198}
199
200impl<F: PrimeField32, const READ_SIZE: usize, const WRITE_SIZE: usize> AdapterTraceFiller<F>
201 for ConvertAdapterFiller<READ_SIZE, WRITE_SIZE>
202{
203 const WIDTH: usize = size_of::<ConvertAdapterCols<u8, READ_SIZE, WRITE_SIZE>>();
204
205 #[inline(always)]
206 fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory<F>, mut row_slice: &mut [F]) {
207 let record: &ConvertAdapterRecord<F, READ_SIZE, WRITE_SIZE> =
211 unsafe { get_record_from_slice(&mut row_slice, ()) };
212 let adapter_row: &mut ConvertAdapterCols<F, READ_SIZE, WRITE_SIZE> = row_slice.borrow_mut();
213
214 mem_helper.fill(
216 record.read_aux.prev_timestamp,
217 record.from_timestamp,
218 adapter_row.reads_aux[0].as_mut(),
219 );
220
221 adapter_row.writes_aux[0]
222 .set_prev_data(record.write_aux.prev_data.map(F::from_canonical_u8));
223 mem_helper.fill(
224 record.write_aux.prev_timestamp,
225 record.from_timestamp + 1,
226 adapter_row.writes_aux[0].as_mut(),
227 );
228
229 adapter_row.b_pointer = record.b_ptr;
230 adapter_row.a_pointer = record.a_ptr;
231
232 adapter_row.from_state.timestamp = F::from_canonical_u32(record.from_timestamp);
233 adapter_row.from_state.pc = F::from_canonical_u32(record.from_pc);
234 }
235}