1use getset::CopyGetters;
2use openvm_circuit_primitives::{
3 assert_less_than::{AssertLessThanIo, AssertLtSubAir},
4 is_zero::{IsZeroIo, IsZeroSubAir},
5 utils::not,
6 var_range::VariableRangeCheckerBus,
7 SubAir,
8};
9use openvm_stark_backend::{
10 interaction::InteractionBuilder, p3_air::AirBuilder, p3_field::PrimeCharacteristicRing,
11};
12
13use super::bus::MemoryBus;
14use crate::system::memory::{
15 offline_checker::columns::{
16 MemoryBaseAuxCols, MemoryReadAuxCols, MemoryReadOrImmediateAuxCols, MemoryWriteAuxCols,
17 },
18 MemoryAddress,
19};
20
21pub const AUX_LEN: usize = 2;
26
27#[derive(Clone, Copy, Debug)]
30pub struct MemoryBridge {
31 offline_checker: MemoryOfflineChecker,
32}
33
34impl MemoryBridge {
35 pub fn new(
37 memory_bus: MemoryBus,
38 timestamp_max_bits: usize,
39 range_bus: VariableRangeCheckerBus,
40 ) -> Self {
41 Self {
42 offline_checker: MemoryOfflineChecker::new(memory_bus, timestamp_max_bits, range_bus),
43 }
44 }
45
46 pub fn memory_bus(&self) -> MemoryBus {
47 self.offline_checker.memory_bus
48 }
49
50 pub fn range_bus(&self) -> VariableRangeCheckerBus {
51 self.offline_checker.timestamp_lt_air.bus
52 }
53
54 #[must_use]
56 pub fn read<'a, T, V, const N: usize>(
57 &self,
58 address: MemoryAddress<impl Into<T>, impl Into<T>>,
59 data: [impl Into<T>; N],
60 timestamp: impl Into<T>,
61 aux: &'a MemoryReadAuxCols<V>,
62 ) -> MemoryReadOperation<'a, T, V, N> {
63 MemoryReadOperation {
64 offline_checker: self.offline_checker,
65 address: MemoryAddress::from(address),
66 data: data.map(Into::into),
67 timestamp: timestamp.into(),
68 aux,
69 }
70 }
71
72 #[must_use]
74 pub fn read_or_immediate<'a, T, V>(
75 &self,
76 address: MemoryAddress<impl Into<T>, impl Into<T>>,
77 data: impl Into<T>,
78 timestamp: impl Into<T>,
79 aux: &'a MemoryReadOrImmediateAuxCols<V>,
80 ) -> MemoryReadOrImmediateOperation<'a, T, V> {
81 MemoryReadOrImmediateOperation {
82 offline_checker: self.offline_checker,
83 address: MemoryAddress::from(address),
84 data: data.into(),
85 timestamp: timestamp.into(),
86 aux,
87 }
88 }
89
90 #[must_use]
92 pub fn write<'a, T, V, const N: usize>(
93 &self,
94 address: MemoryAddress<impl Into<T>, impl Into<T>>,
95 data: [impl Into<T>; N],
96 timestamp: impl Into<T>,
97 aux: &'a MemoryWriteAuxCols<V, N>,
98 ) -> MemoryWriteOperation<'a, T, V, N> {
99 MemoryWriteOperation {
100 offline_checker: self.offline_checker,
101 address: MemoryAddress::from(address),
102 data: data.map(Into::into),
103 timestamp: timestamp.into(),
104 aux,
105 }
106 }
107}
108
109pub struct MemoryReadOperation<'a, T, V, const N: usize> {
118 offline_checker: MemoryOfflineChecker,
119 address: MemoryAddress<T, T>,
120 data: [T; N],
121 timestamp: T,
122 aux: &'a MemoryReadAuxCols<V>,
123}
124
125impl<F: PrimeCharacteristicRing, V: Copy + Into<F>, const N: usize>
129 MemoryReadOperation<'_, F, V, N>
130{
131 pub fn eval<AB>(self, builder: &mut AB, enabled: impl Into<AB::Expr>)
133 where
134 AB: InteractionBuilder<Var = V, Expr = F>,
135 {
136 let enabled = enabled.into();
137
138 self.offline_checker.eval_timestamps(
142 builder,
143 self.timestamp.clone(),
144 &self.aux.base,
145 enabled.clone(),
146 );
147
148 self.offline_checker.eval_bulk_access(
149 builder,
150 self.address,
151 &self.data,
152 &self.data,
153 self.timestamp.clone(),
154 self.aux.base.prev_timestamp,
155 enabled,
156 );
157 }
158}
159
160pub struct MemoryReadOrImmediateOperation<'a, T, V> {
170 offline_checker: MemoryOfflineChecker,
171 address: MemoryAddress<T, T>,
172 data: T,
173 timestamp: T,
174 aux: &'a MemoryReadOrImmediateAuxCols<V>,
175}
176
177impl<F: PrimeCharacteristicRing, V: Copy + Into<F>> MemoryReadOrImmediateOperation<'_, F, V> {
185 pub fn eval<AB>(self, builder: &mut AB, enabled: impl Into<AB::Expr>)
187 where
188 AB: InteractionBuilder<Var = V, Expr = F>,
189 {
190 let enabled = enabled.into();
191
192 {
194 let is_zero_io = IsZeroIo::new(
195 self.address.address_space.clone(),
196 self.aux.is_immediate.into(),
197 enabled.clone(),
198 );
199 IsZeroSubAir.eval(builder, (is_zero_io, self.aux.is_zero_aux));
200 }
201 builder
203 .when(self.aux.is_immediate)
204 .assert_eq(self.data.clone(), self.address.pointer.clone());
205
206 self.offline_checker.eval_timestamps(
208 builder,
209 self.timestamp.clone(),
210 &self.aux.base,
211 enabled.clone(),
212 );
213
214 #[allow(clippy::cloned_ref_to_slice_refs)]
215 self.offline_checker.eval_bulk_access(
216 builder,
217 self.address,
218 #[allow(clippy::cloned_ref_to_slice_refs)]
219 &[self.data.clone()],
220 &[self.data],
221 self.timestamp,
222 self.aux.base.prev_timestamp,
223 enabled * not(self.aux.is_immediate),
224 );
225 }
226}
227
228pub struct MemoryWriteOperation<'a, T, V, const N: usize> {
235 offline_checker: MemoryOfflineChecker,
236 address: MemoryAddress<T, T>,
237 data: [T; N],
238 timestamp: T,
240 aux: &'a MemoryWriteAuxCols<V, N>,
241}
242
243impl<T: PrimeCharacteristicRing, V: Copy + Into<T>, const N: usize>
247 MemoryWriteOperation<'_, T, V, N>
248{
249 pub fn eval<AB>(self, builder: &mut AB, enabled: impl Into<AB::Expr>)
251 where
252 AB: InteractionBuilder<Var = V, Expr = T>,
253 {
254 let enabled = enabled.into();
255 self.offline_checker.eval_timestamps(
256 builder,
257 self.timestamp.clone(),
258 &self.aux.base,
259 enabled.clone(),
260 );
261
262 self.offline_checker.eval_bulk_access(
263 builder,
264 self.address,
265 &self.data,
266 &self.aux.prev_data.map(Into::into),
267 self.timestamp,
268 self.aux.base.prev_timestamp,
269 enabled,
270 );
271 }
272}
273
274#[derive(Clone, Copy, Debug, CopyGetters)]
275struct MemoryOfflineChecker {
276 #[get_copy = "pub"]
277 memory_bus: MemoryBus,
278 #[get_copy = "pub"]
279 timestamp_lt_air: AssertLtSubAir,
280}
281
282impl MemoryOfflineChecker {
283 fn new(
284 memory_bus: MemoryBus,
285 timestamp_max_bits: usize,
286 range_bus: VariableRangeCheckerBus,
287 ) -> Self {
288 Self {
289 memory_bus,
290 timestamp_lt_air: AssertLtSubAir::new(range_bus, timestamp_max_bits),
291 }
292 }
293
294 fn eval_timestamps<AB: InteractionBuilder>(
298 &self,
299 builder: &mut AB,
300 timestamp: AB::Expr,
301 base: &MemoryBaseAuxCols<AB::Var>,
302 enabled: AB::Expr,
303 ) {
304 let lt_io = AssertLessThanIo::new(base.prev_timestamp, timestamp.clone(), enabled);
305 self.timestamp_lt_air
306 .eval(builder, (lt_io, &base.timestamp_lt_aux.lower_decomp));
307 }
308
309 #[allow(clippy::too_many_arguments)]
314 fn eval_bulk_access<AB, const N: usize>(
315 &self,
316 builder: &mut AB,
317 address: MemoryAddress<AB::Expr, AB::Expr>,
318 data: &[AB::Expr; N],
319 prev_data: &[AB::Expr; N],
320 timestamp: AB::Expr,
321 prev_timestamp: AB::Var,
322 enabled: AB::Expr,
323 ) where
324 AB: InteractionBuilder,
325 {
326 self.memory_bus
327 .receive(address.clone(), prev_data.to_vec(), prev_timestamp)
328 .eval(builder, enabled.clone());
329
330 self.memory_bus
331 .send(address, data.to_vec(), timestamp)
332 .eval(builder, enabled);
333 }
334}