logicaffeine_compile/spatial/decode_engine/
ddr.rs1use std::path::Path;
13
14use serde::Serialize;
15
16use crate::codegen_sva::ddr4_jedec::{measure, Ddr4Facts, Ddr4Grade, Ddr4Timing, D5005_EMIF_IP, LOOKAHEAD};
17use crate::codegen_sva::dram::StreamDesc;
18use crate::spatial::d5005::{Beat, DDR_CHANNELS};
19use crate::spatial::decode::{DecodeStep, Deployment, GgufHeader, StageKind};
20
21pub const HEADER: &str = "qwen3.8-27b-q4_k_m";
23pub const WEIGHT_LINES: u64 = 1 << 16;
25pub const STATE_CHUNKS: [u64; 4] = [8, 32, 128, 512];
27pub const CACHE_POSITIONS: [u64; 3] = [256, 2048, 8192];
29pub const SEQUENCES: u64 = 4;
31
32#[derive(Clone, Debug, PartialEq, Serialize)]
34pub struct DdrRow {
35 pub pattern: String,
36 pub beat: Beat,
37 pub line_bytes: u64,
38 pub facts: Ddr4Facts,
39 pub efficiency: f64,
40 pub controller_ck: u64,
42 pub channel_bytes_per_s: f64,
43 pub card_bytes_per_s: f64,
45}
46
47#[derive(Clone, Debug, PartialEq, Serialize)]
50pub struct DdrLedger {
51 pub source: &'static str,
52 pub timing: Ddr4Timing,
53 pub lookahead: usize,
54 pub header: &'static str,
55 pub state_bytes: u64,
57 pub position_bytes: u64,
59 pub rows: Vec<DdrRow>,
60}
61
62fn line_bytes(beat: Beat, t: &Ddr4Timing) -> u64 {
63 match beat {
64 Beat::Data64 => 64,
65 Beat::Full72 => t.burst_bits() / 8,
66 }
67}
68
69fn run(base: u64, bytes: u64, write: bool) -> StreamDesc {
70 StreamDesc { base, elem_bytes: bytes, stride_bytes: bytes, count: 1, write }
71}
72
73pub fn stream_bytes(h: &GgufHeader) -> Result<(u64, u64), String> {
76 let d = Deployment { kv_bytes: 1.0, state_bytes: 2.0, ..Deployment::as_stored(1) };
77 let step = DecodeStep::of(h, &d).map_err(|e| format!("{e:?}"))?;
78 let state = step.stages.iter().find(|s| s.kind == StageKind::Recurrence).ok_or("no recurrent block")?.state_read;
79 let position = step.stages.iter().find(|s| s.kind == StageKind::Attention).ok_or("no attention block")?.state_write;
80 Ok((state as u64, position as u64))
81}
82
83pub fn ddr_ledger(decode_dir: &Path) -> Result<DdrLedger, String> {
85 let h = GgufHeader::read_ledger(&decode_dir.join(format!("headers/{HEADER}.json")))?;
86 let (state_bytes, position_bytes) = stream_bytes(&h)?;
87 let t = Ddr4Grade::D5005_EMIF.at();
88 let mut rows = Vec::new();
89 for beat in [Beat::Data64, Beat::Full72] {
90 let lb = line_bytes(beat, &t);
91 let mut price = |pattern: String, streams: Vec<StreamDesc>| -> Result<(), String> {
92 let facts = measure(&t, lb, &streams).map_err(|v| format!("{pattern}: {v}"))?;
93 let channel = facts.bytes_per_s(&t, lb);
94 rows.push(DdrRow {
95 pattern,
96 beat,
97 line_bytes: lb,
98 facts,
99 efficiency: facts.efficiency(),
100 controller_ck: t.controller_ck(facts.end),
101 channel_bytes_per_s: channel,
102 card_bytes_per_s: channel * DDR_CHANNELS as f64,
103 });
104 Ok(())
105 };
106 price(format!("weights: one read of {WEIGHT_LINES} lines"), vec![run(0, WEIGHT_LINES * lb, false)])?;
107 let state_lines = state_bytes.div_ceil(lb);
108 for chunk in STATE_CHUNKS {
109 let mut streams = Vec::new();
110 for s in 0..SEQUENCES {
111 let base = s * state_lines;
112 let mut at = 0;
113 while at < state_lines {
114 let n = chunk.min(state_lines - at);
115 streams.push(run((base + at) * lb, n * lb, false));
116 streams.push(run((base + at) * lb, n * lb, true));
117 at += n;
118 }
119 }
120 price(format!("Gated-DeltaNet state: {SEQUENCES} memories of {state_lines} lines, read and written back {chunk} lines at a time"), streams)?;
121 }
122 for positions in CACHE_POSITIONS {
123 let region = ((positions + 1) * position_bytes).div_ceil(lb);
124 let mut streams = Vec::new();
125 for s in 0..SEQUENCES {
126 let base = s * region * lb;
127 streams.push(run(base, positions * position_bytes, false));
128 streams.push(run(base + positions * position_bytes, position_bytes, true));
129 }
130 price(format!("attention cache: {SEQUENCES} sequences of {positions} positions read, one appended"), streams)?;
131 }
132 }
133 Ok(DdrLedger { source: D5005_EMIF_IP, timing: t, lookahead: LOOKAHEAD, header: HEADER, state_bytes, position_bytes, rows })
134}