use miden_air::{
logup::{MemoryMsg, MemoryResponseMsg},
trace::{
MainTrace,
chiplets::{MEMORY_IS_READ_COL_IDX, MEMORY_IS_WORD_ACCESS_COL_IDX},
},
};
use miden_core::{
Felt, ONE, ZERO,
operations::{Operation, opcodes},
};
use super::super::{
build_trace_from_ops,
lookup_harness::{Expectations, InteractionLog},
};
use crate::RowIndex;
const FOUR: Felt = Felt::new_unchecked(4);
#[test]
fn memory_chiplet_bus_request_response_pairs() {
let stack = [0, 1, 2, 3, 4];
let operations = vec![
Operation::MStoreW, Operation::Drop,
Operation::Drop,
Operation::Drop,
Operation::Drop,
Operation::MLoad, Operation::MovDn5, Operation::MLoadW, Operation::Push(ONE), Operation::Push(FOUR), Operation::MStore, Operation::Drop,
Operation::MStream, ];
let trace = build_trace_from_ops(operations, &stack);
let log = InteractionLog::new(&trace);
let main = trace.main_trace();
let mut exp = Expectations::new(&log);
let mut request_exps_added = 0usize;
for row in 0..main.num_rows() {
let idx = RowIndex::from(row);
let ctx = main.ctx(idx);
let clk = main.clk(idx);
let next = RowIndex::from(row + 1);
let op = main.get_op_code(idx).as_canonical_u64();
if op == opcodes::MLOAD as u64 {
let addr = main.stack_element(0, idx);
let value = main.stack_element(0, next);
exp.remove(row, &MemoryMsg::read_element(ctx, addr, clk, value));
request_exps_added += 1;
} else if op == opcodes::MSTORE as u64 {
let addr = main.stack_element(0, idx);
let value = main.stack_element(1, idx);
exp.remove(row, &MemoryMsg::write_element(ctx, addr, clk, value));
request_exps_added += 1;
} else if op == opcodes::MLOADW as u64 {
let addr = main.stack_element(0, idx);
let word = next_word(main, next, 0);
exp.remove(row, &MemoryMsg::read_word(ctx, addr, clk, word));
request_exps_added += 1;
} else if op == opcodes::MSTOREW as u64 {
let addr = main.stack_element(0, idx);
let word = [
main.stack_element(1, idx),
main.stack_element(2, idx),
main.stack_element(3, idx),
main.stack_element(4, idx),
];
exp.remove(row, &MemoryMsg::write_word(ctx, addr, clk, word));
request_exps_added += 1;
} else if op == opcodes::MSTREAM as u64 {
let base = main.stack_element(12, idx);
let word0 = next_word(main, next, 0);
let word1 = next_word(main, next, 4);
exp.remove(row, &MemoryMsg::read_word(ctx, base, clk, word0));
exp.remove(row, &MemoryMsg::read_word(ctx, base + FOUR, clk, word1));
request_exps_added += 2;
}
}
assert_eq!(request_exps_added, 6, "expected 6 memory request expectations");
let mut mem_rows_seen = 0usize;
for row in 0..main.num_rows() {
let idx = RowIndex::from(row);
if !main.is_memory_row(idx) {
continue;
}
mem_rows_seen += 1;
let is_read = main.get(idx, MEMORY_IS_READ_COL_IDX);
let is_word = main.get(idx, MEMORY_IS_WORD_ACCESS_COL_IDX);
let mem_ctx = main.chiplet_memory_ctx(idx);
let word_addr = main.chiplet_memory_word(idx);
let idx0 = main.chiplet_memory_idx0(idx);
let idx1 = main.chiplet_memory_idx1(idx);
let addr = word_addr + idx1.double() + idx0;
let mem_clk = main.chiplet_memory_clk(idx);
let word = [
main.chiplet_memory_value_0(idx),
main.chiplet_memory_value_1(idx),
main.chiplet_memory_value_2(idx),
main.chiplet_memory_value_3(idx),
];
let element = if is_word == ZERO {
let element_idx = (idx1.as_canonical_u64() * 2 + idx0.as_canonical_u64()) as usize;
word[element_idx]
} else {
ZERO
};
exp.add(
row,
&MemoryResponseMsg {
is_read,
ctx: mem_ctx,
addr,
clk: mem_clk,
is_word,
element,
word,
},
);
}
assert_eq!(mem_rows_seen, 6, "expected 6 memory chiplet rows");
log.assert_contains(&exp);
}
#[test]
fn cryptostream_emits_four_memory_requests() {
let stack = [
1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 8, 0, 0, ];
let trace = build_trace_from_ops(vec![Operation::CryptoStream], &stack);
let log = InteractionLog::new(&trace);
let mut exp = Expectations::new(&log);
const ROW: usize = 1;
let zero_word = [ZERO, ZERO, ZERO, ZERO];
let cipher1 = [
Felt::new_unchecked(1),
Felt::new_unchecked(2),
Felt::new_unchecked(3),
Felt::new_unchecked(4),
];
let cipher2 = [
Felt::new_unchecked(5),
Felt::new_unchecked(6),
Felt::new_unchecked(7),
Felt::new_unchecked(8),
];
let mut request_exps_added = 0usize;
exp.remove(ROW, &MemoryMsg::read_word(ZERO, ZERO, ONE, zero_word));
request_exps_added += 1;
exp.remove(ROW, &MemoryMsg::read_word(ZERO, FOUR, ONE, zero_word));
request_exps_added += 1;
exp.remove(ROW, &MemoryMsg::write_word(ZERO, Felt::new_unchecked(8), ONE, cipher1));
request_exps_added += 1;
exp.remove(ROW, &MemoryMsg::write_word(ZERO, Felt::new_unchecked(12), ONE, cipher2));
request_exps_added += 1;
assert_eq!(request_exps_added, 4, "expected 4 CryptoStream request expectations");
log.assert_contains(&exp);
}
#[test]
fn hornerbase_emits_two_memory_requests() {
let stack = [
1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, ];
let trace = build_trace_from_ops(vec![Operation::HornerBase], &stack);
let log = InteractionLog::new(&trace);
let mut exp = Expectations::new(&log);
const ROW: usize = 1;
exp.remove(ROW, &MemoryMsg::read_element(ZERO, ZERO, ONE, ZERO));
exp.remove(ROW, &MemoryMsg::read_element(ZERO, ONE, ONE, ZERO));
log.assert_contains(&exp);
}
#[test]
fn hornerext_emits_one_memory_request() {
let stack = [
1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, ];
let trace = build_trace_from_ops(vec![Operation::HornerExt], &stack);
let log = InteractionLog::new(&trace);
let mut exp = Expectations::new(&log);
const ROW: usize = 1;
let zero_word = [ZERO, ZERO, ZERO, ZERO];
exp.remove(ROW, &MemoryMsg::read_word(ZERO, ZERO, ONE, zero_word));
log.assert_contains(&exp);
}
fn next_word(main: &MainTrace, next: RowIndex, start: usize) -> [Felt; 4] {
[
main.stack_element(start, next),
main.stack_element(start + 1, next),
main.stack_element(start + 2, next),
main.stack_element(start + 3, next),
]
}