openvm_circuit/system/memory/
mod.rs

1use std::sync::Arc;
2
3use openvm_circuit_primitives::{StructReflection, StructReflectionHelper};
4use openvm_circuit_primitives_derive::AlignedBorrow;
5use openvm_stark_backend::{
6    interaction::PermutationCheckBus, p3_util::log2_strict_usize, StarkProtocolConfig,
7};
8
9mod controller;
10pub mod merkle;
11pub mod offline_checker;
12pub mod online;
13pub mod persistent;
14#[cfg(test)]
15mod tests;
16
17pub use controller::*;
18pub use online::{Address, AddressMap, INITIAL_TIMESTAMP};
19
20use crate::{
21    arch::{AirRefWithColumns, MemoryConfig},
22    system::memory::{
23        dimensions::MemoryDimensions, interface::MemoryInterfaceAirs, merkle::MemoryMerkleAir,
24        offline_checker::MemoryBridge, persistent::PersistentBoundaryAir,
25    },
26};
27
28// @dev Currently this is only used for debug assertions, but we may switch to making it constant
29// and removing from MemoryConfig
30pub const POINTER_MAX_BITS: usize = 29;
31
32#[derive(PartialEq, Copy, Clone, Debug, Eq)]
33pub enum OpType {
34    Read = 0,
35    Write = 1,
36}
37
38/// The full pointer to a location in memory consists of an address space and a pointer within
39/// the address space.
40#[derive(Clone, Copy, Debug, PartialEq, Eq, AlignedBorrow, StructReflection)]
41#[repr(C)]
42pub struct MemoryAddress<S, T> {
43    pub address_space: S,
44    pub pointer: T,
45}
46
47impl<S, T> MemoryAddress<S, T> {
48    pub fn new(address_space: S, pointer: T) -> Self {
49        Self {
50            address_space,
51            pointer,
52        }
53    }
54
55    pub fn from<T1, T2>(a: MemoryAddress<T1, T2>) -> Self
56    where
57        T1: Into<S>,
58        T2: Into<T>,
59    {
60        Self {
61            address_space: a.address_space.into(),
62            pointer: a.pointer.into(),
63        }
64    }
65}
66
67#[derive(Clone)]
68pub struct MemoryAirInventory {
69    pub bridge: MemoryBridge,
70    pub interface: MemoryInterfaceAirs,
71}
72
73impl MemoryAirInventory {
74    pub fn new(
75        bridge: MemoryBridge,
76        mem_config: &MemoryConfig,
77        merkle_bus: PermutationCheckBus,
78        compression_bus: PermutationCheckBus,
79    ) -> Self {
80        let memory_bus = bridge.memory_bus();
81        let memory_dims = MemoryDimensions {
82            addr_space_height: mem_config.addr_space_height,
83            address_height: mem_config.pointer_max_bits - log2_strict_usize(CHUNK),
84        };
85        let boundary = PersistentBoundaryAir::<CHUNK> {
86            memory_bus,
87            merkle_bus,
88            compression_bus,
89        };
90        let merkle = MemoryMerkleAir::<CHUNK> {
91            memory_dimensions: memory_dims,
92            merkle_bus,
93            compression_bus,
94        };
95        let interface = MemoryInterfaceAirs { boundary, merkle };
96        Self { bridge, interface }
97    }
98
99    /// The order of memory AIRs is boundary, merkle (if exists)
100    pub fn into_airs<SC: StarkProtocolConfig>(self) -> Vec<AirRefWithColumns<SC>> {
101        vec![
102            Arc::new(self.interface.boundary),
103            Arc::new(self.interface.merkle),
104        ]
105    }
106}
107
108/// This is O(1) and returns the length of
109/// [`MemoryAirInventory::into_airs`].
110pub const fn num_memory_airs() -> usize {
111    // boundary + merkle
112    2
113}