logicaffeine_compile/spatial/lower/fabric/
budget.rs1use std::collections::BTreeMap;
12
13use super::*;
14use crate::spatial::lower::{CapacityRefusal, FabricResource, Over};
15use crate::spatial::part::MemoryKind;
16
17const RAMB18_BITS: u64 = 18_432;
19const LUTRAM_BITS: u64 = 64;
21
22#[derive(Clone, Debug, PartialEq, Eq)]
25pub struct DeclaredMemory {
26 pub name: String,
27 pub words: usize,
28 pub width: u32,
29 pub banks: usize,
30 pub lat: u64,
31 pub read_ports: usize,
32}
33
34impl DeclaredMemory {
35 fn storage(&self) -> Storage {
36 Storage::of(self.words, self.banks, self.lat)
37 }
38
39 pub fn bits(&self) -> u64 {
42 let rows = self.words.max(1).div_ceil(self.banks) as u64;
43 self.banks as u64 * rows * self.width as u64
44 }
45}
46
47pub fn fabric_memories(p: &SpatialProgram, target: &SpatialTarget) -> Result<Vec<DeclaredMemory>, SpatialLowerError> {
50 let ctx = context(p, target)?;
51 let plans = plans(&ctx)?;
52 let banks = ctx.banks;
53 let mut out = Vec::new();
54 for site in p.sites() {
55 for t in &p.tensors {
56 out.push(DeclaredMemory {
57 name: format!("mem_t{}", t.id.idx()),
58 words: ctx.tensor_words(t),
59 width: tensor_width(t, &ctx.ranges),
60 banks,
61 lat: ctx.lat,
62 read_ports: 1,
63 });
64 }
65 for (i, pool) in site_pools(&ctx, &plans, site).into_iter().enumerate() {
66 out.push(DeclaredMemory {
67 name: format!("pool_{}_{}_{i}", site.x, site.y),
68 words: pool.words,
69 width: pool.width,
70 banks,
71 lat: ctx.lat,
72 read_ports: pool.nread,
73 });
74 }
75 }
76 Ok(out)
77}
78
79#[derive(Clone, Debug, PartialEq, Eq)]
82pub struct FabricUsage {
83 pub ramb18: u64,
84 pub lutram_luts: u64,
85 pub flops: u64,
86 pub dsp: u64,
87 pub memories: Vec<DeclaredMemory>,
88}
89
90pub fn fabric_usage(p: &SpatialProgram, target: &SpatialTarget) -> Result<FabricUsage, SpatialLowerError> {
92 let memories = fabric_memories(p, target)?;
93 let (mut block_bits, mut lutram_luts, mut ff_bits) = (0u64, 0u64, 0u64);
94 for m in &memories {
95 match m.storage() {
96 Storage::Block => block_bits += m.bits(),
97 Storage::Distributed => lutram_luts += m.bits().div_ceil(LUTRAM_BITS) * m.read_ports as u64,
99 Storage::Registers | Storage::Flops => ff_bits += m.bits(),
100 }
101 }
102 let ctx = context(p, target)?;
103 let plans = plans(&ctx)?;
104 let dsp = plans
106 .iter()
107 .filter_map(|pl| pe_array(&pl.micro))
108 .map(|(k, n, _)| (k * n) as u64)
109 .sum();
110 Ok(FabricUsage {
111 ramb18: block_bits.div_ceil(RAMB18_BITS),
112 lutram_luts,
113 flops: ff_bits,
114 dsp,
115 memories,
116 })
117}
118
119fn largest_of(mems: &[DeclaredMemory], want: Storage) -> Vec<(String, u64)> {
121 let mut v: Vec<(String, u64)> = mems.iter().filter(|m| m.storage() == want).map(|m| (m.name.clone(), m.bits())).collect();
122 v.sort_by(|a, b| b.1.cmp(&a.1).then_with(|| a.0.cmp(&b.0)));
123 v.truncate(3);
124 v
125}
126
127pub fn check_capacity(p: &SpatialProgram, target: &SpatialTarget) -> Result<(), SpatialLowerError> {
130 let Some(part) = target.part else {
131 return Ok(());
132 };
133 let usage = fabric_usage(p, target)?;
134 let have = |kind: MemoryKind| part.memory(kind).map_or(0, |m| m.count.value);
135 let block_as_ramb18 = have(MemoryKind::Block36) * 2;
136 let budgets: &[(FabricResource, u64, u64, Option<Storage>)] = &[
137 (FabricResource::BlockRam18, usage.ramb18, block_as_ramb18, Some(Storage::Block)),
138 (FabricResource::LutRam, usage.lutram_luts, have(MemoryKind::LutRam), Some(Storage::Distributed)),
139 (FabricResource::Flops, usage.flops, part.flops.value, Some(Storage::Registers)),
140 (FabricResource::Dsp, usage.dsp, part.dsp.value, None),
141 ];
142 let mut over = Vec::new();
143 for &(resource, need, have, storage) in budgets {
144 if need > have {
145 let largest = storage.map(|s| largest_of(&usage.memories, s)).unwrap_or_default();
146 over.push(Over { resource, need, have, largest });
147 }
148 }
149 if over.is_empty() {
150 Ok(())
151 } else {
152 Err(SpatialLowerError::OverCapacity(CapacityRefusal { part: part.part, over }))
153 }
154}
155
156pub fn usage_by_resource(u: &FabricUsage) -> BTreeMap<&'static str, u64> {
158 [
159 (FabricResource::BlockRam18.label(), u.ramb18),
160 (FabricResource::LutRam.label(), u.lutram_luts),
161 (FabricResource::Flops.label(), u.flops),
162 (FabricResource::Dsp.label(), u.dsp),
163 ]
164 .into_iter()
165 .collect()
166}