use getset::CopyGetters;
use openvm_circuit_primitives::{
assert_less_than::{AssertLessThanIo, AssertLtSubAir},
is_zero::{IsZeroIo, IsZeroSubAir},
utils::not,
var_range::VariableRangeCheckerBus,
SubAir,
};
use openvm_stark_backend::{
interaction::InteractionBuilder, p3_air::AirBuilder, p3_field::PrimeCharacteristicRing,
};
use super::bus::MemoryBus;
use crate::system::memory::{
offline_checker::columns::{
MemoryBaseAuxCols, MemoryReadAuxCols, MemoryReadOrImmediateAuxCols, MemoryWriteAuxCols,
},
MemoryAddress,
};
pub const AUX_LEN: usize = 2;
#[derive(Clone, Copy, Debug)]
pub struct MemoryBridge {
offline_checker: MemoryOfflineChecker,
}
impl MemoryBridge {
pub fn new(
memory_bus: MemoryBus,
timestamp_max_bits: usize,
range_bus: VariableRangeCheckerBus,
) -> Self {
Self {
offline_checker: MemoryOfflineChecker::new(memory_bus, timestamp_max_bits, range_bus),
}
}
pub fn memory_bus(&self) -> MemoryBus {
self.offline_checker.memory_bus
}
pub fn range_bus(&self) -> VariableRangeCheckerBus {
self.offline_checker.timestamp_lt_air.bus
}
#[must_use]
pub fn read<'a, T, V, const N: usize>(
&self,
address: MemoryAddress<impl Into<T>, impl Into<T>>,
data: [impl Into<T>; N],
timestamp: impl Into<T>,
aux: &'a MemoryReadAuxCols<V>,
) -> MemoryReadOperation<'a, T, V, N> {
MemoryReadOperation {
offline_checker: self.offline_checker,
address: MemoryAddress::from(address),
data: data.map(Into::into),
timestamp: timestamp.into(),
aux,
}
}
#[must_use]
pub fn read_or_immediate<'a, T, V>(
&self,
address: MemoryAddress<impl Into<T>, impl Into<T>>,
data: impl Into<T>,
timestamp: impl Into<T>,
aux: &'a MemoryReadOrImmediateAuxCols<V>,
) -> MemoryReadOrImmediateOperation<'a, T, V> {
MemoryReadOrImmediateOperation {
offline_checker: self.offline_checker,
address: MemoryAddress::from(address),
data: data.into(),
timestamp: timestamp.into(),
aux,
}
}
#[must_use]
pub fn write<'a, T, V, const N: usize>(
&self,
address: MemoryAddress<impl Into<T>, impl Into<T>>,
data: [impl Into<T>; N],
timestamp: impl Into<T>,
aux: &'a MemoryWriteAuxCols<V, N>,
) -> MemoryWriteOperation<'a, T, V, N> {
MemoryWriteOperation {
offline_checker: self.offline_checker,
address: MemoryAddress::from(address),
data: data.map(Into::into),
timestamp: timestamp.into(),
aux,
}
}
}
pub struct MemoryReadOperation<'a, T, V, const N: usize> {
offline_checker: MemoryOfflineChecker,
address: MemoryAddress<T, T>,
data: [T; N],
timestamp: T,
aux: &'a MemoryReadAuxCols<V>,
}
impl<F: PrimeCharacteristicRing, V: Copy + Into<F>, const N: usize>
MemoryReadOperation<'_, F, V, N>
{
pub fn eval<AB>(self, builder: &mut AB, enabled: impl Into<AB::Expr>)
where
AB: InteractionBuilder<Var = V, Expr = F>,
{
let enabled = enabled.into();
self.offline_checker.eval_timestamps(
builder,
self.timestamp.clone(),
&self.aux.base,
enabled.clone(),
);
self.offline_checker.eval_bulk_access(
builder,
self.address,
&self.data,
&self.data,
self.timestamp.clone(),
self.aux.base.prev_timestamp,
enabled,
);
}
}
pub struct MemoryReadOrImmediateOperation<'a, T, V> {
offline_checker: MemoryOfflineChecker,
address: MemoryAddress<T, T>,
data: T,
timestamp: T,
aux: &'a MemoryReadOrImmediateAuxCols<V>,
}
impl<F: PrimeCharacteristicRing, V: Copy + Into<F>> MemoryReadOrImmediateOperation<'_, F, V> {
pub fn eval<AB>(self, builder: &mut AB, enabled: impl Into<AB::Expr>)
where
AB: InteractionBuilder<Var = V, Expr = F>,
{
let enabled = enabled.into();
{
let is_zero_io = IsZeroIo::new(
self.address.address_space.clone(),
self.aux.is_immediate.into(),
enabled.clone(),
);
IsZeroSubAir.eval(builder, (is_zero_io, self.aux.is_zero_aux));
}
builder
.when(self.aux.is_immediate)
.assert_eq(self.data.clone(), self.address.pointer.clone());
self.offline_checker.eval_timestamps(
builder,
self.timestamp.clone(),
&self.aux.base,
enabled.clone(),
);
#[allow(clippy::cloned_ref_to_slice_refs)]
self.offline_checker.eval_bulk_access(
builder,
self.address,
#[allow(clippy::cloned_ref_to_slice_refs)]
&[self.data.clone()],
&[self.data],
self.timestamp,
self.aux.base.prev_timestamp,
enabled * not(self.aux.is_immediate),
);
}
}
pub struct MemoryWriteOperation<'a, T, V, const N: usize> {
offline_checker: MemoryOfflineChecker,
address: MemoryAddress<T, T>,
data: [T; N],
timestamp: T,
aux: &'a MemoryWriteAuxCols<V, N>,
}
impl<T: PrimeCharacteristicRing, V: Copy + Into<T>, const N: usize>
MemoryWriteOperation<'_, T, V, N>
{
pub fn eval<AB>(self, builder: &mut AB, enabled: impl Into<AB::Expr>)
where
AB: InteractionBuilder<Var = V, Expr = T>,
{
let enabled = enabled.into();
self.offline_checker.eval_timestamps(
builder,
self.timestamp.clone(),
&self.aux.base,
enabled.clone(),
);
self.offline_checker.eval_bulk_access(
builder,
self.address,
&self.data,
&self.aux.prev_data.map(Into::into),
self.timestamp,
self.aux.base.prev_timestamp,
enabled,
);
}
}
#[derive(Clone, Copy, Debug, CopyGetters)]
struct MemoryOfflineChecker {
#[get_copy = "pub"]
memory_bus: MemoryBus,
#[get_copy = "pub"]
timestamp_lt_air: AssertLtSubAir,
}
impl MemoryOfflineChecker {
fn new(
memory_bus: MemoryBus,
timestamp_max_bits: usize,
range_bus: VariableRangeCheckerBus,
) -> Self {
Self {
memory_bus,
timestamp_lt_air: AssertLtSubAir::new(range_bus, timestamp_max_bits),
}
}
fn eval_timestamps<AB: InteractionBuilder>(
&self,
builder: &mut AB,
timestamp: AB::Expr,
base: &MemoryBaseAuxCols<AB::Var>,
enabled: AB::Expr,
) {
let lt_io = AssertLessThanIo::new(base.prev_timestamp, timestamp.clone(), enabled);
self.timestamp_lt_air
.eval(builder, (lt_io, &base.timestamp_lt_aux.lower_decomp));
}
#[allow(clippy::too_many_arguments)]
fn eval_bulk_access<AB, const N: usize>(
&self,
builder: &mut AB,
address: MemoryAddress<AB::Expr, AB::Expr>,
data: &[AB::Expr; N],
prev_data: &[AB::Expr; N],
timestamp: AB::Expr,
prev_timestamp: AB::Var,
enabled: AB::Expr,
) where
AB: InteractionBuilder,
{
self.memory_bus
.receive(address.clone(), prev_data.to_vec(), prev_timestamp)
.eval(builder, enabled.clone());
self.memory_bus
.send(address, data.to_vec(), timestamp)
.eval(builder, enabled);
}
}