1use serde::Serialize;
11
12use super::rdu_chip::{Fact, Provenance};
13
14pub const DS180: &str = "DS180 (v1.17) 7 Series FPGAs Data Sheet: Overview";
15pub const UG473: &str = "UG473 (v1.14) 7 Series FPGAs Memory Resources";
16pub const UG474: &str = "UG474 (v1.8) 7 Series FPGAs Configurable Logic Block";
17pub const UG479: &str = "UG479 (v1.10) 7 Series DSP48E1 Slice";
18pub const DS891: &str = "DS891 (v1.11.1) Zynq UltraScale+ MPSoC Data Sheet: Overview";
19pub const UG573: &str = "UG573 (v1.9) UltraScale Architecture Memory Resources";
20pub const UG574: &str = "UG574 (v1.5) UltraScale Architecture Configurable Logic Block";
21pub const UG579: &str = "UG579 (v1.7) UltraScale Architecture DSP Slice";
22pub const S10_OVERVIEW: &str = "S10-OVERVIEW (2020.09.28) Intel Stratix 10 GX/SX Device Overview";
23pub const S10_PRODUCT_TABLE: &str = "GEN-1023-1.0 (May 2015) Stratix 10 Product Table";
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
27pub enum LogicUnit {
28 Lut6,
30 Alm,
32}
33
34#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]
36pub enum MemoryKind {
37 LutRam,
39 Mlab,
41 Block36,
43 M20k,
45 Ultra,
47}
48
49impl MemoryKind {
50 pub fn is_block(self) -> bool {
52 matches!(self, MemoryKind::Block36 | MemoryKind::M20k | MemoryKind::Ultra)
53 }
54}
55
56#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
58pub struct Memory {
59 pub kind: MemoryKind,
60 pub count: Fact<u64>,
62 pub bits: Fact<u64>,
64 pub read_bits_per_cycle: Fact<u64>,
66 pub preloaded: Fact<bool>,
68}
69
70#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
72pub struct PartResources {
73 pub part: &'static str,
75 pub logic_unit: LogicUnit,
76 pub logic: Fact<u64>,
77 pub flops: Fact<u64>,
78 pub dsp: Fact<u64>,
79 pub multiplier: Fact<(u32, u32)>,
81 pub memories: &'static [Memory],
82 pub sources: &'static [&'static str],
83}
84
85impl PartResources {
86 pub fn catalog() -> Vec<PartResources> {
88 vec![PartResources::xc7a100t(), PartResources::xc7a200t(), PartResources::xczu7ev(), PartResources::sx2800()]
89 }
90
91 pub fn by_name(part: &str) -> Option<PartResources> {
92 PartResources::catalog().into_iter().find(|p| p.part == part)
93 }
94
95 pub fn xc7a100t() -> PartResources {
97 PartResources {
98 part: "xc7a100t",
99 logic_unit: LogicUnit::Lut6,
100 logic: Fact::derived(
101 63_400,
102 "DS180 (v1.17) 7 Series FPGAs Data Sheet: Overview, Table 2: XC7A100T \"Slices 15,850\"; note 1 \"Each 7 series \
103 FPGA slice contains four LUTs and eight flip-flops\": 15,850 × 4",
104 ),
105 flops: Fact::derived(
106 126_800,
107 "DS180 (v1.17) 7 Series FPGAs Data Sheet: Overview, Table 2: XC7A100T \"Slices 15,850\"; note 1 \"four LUTs and \
108 eight flip-flops\": 15,850 × 8",
109 ),
110 dsp: Fact::published(240, "DS180 (v1.17) 7 Series FPGAs Data Sheet: Overview, Table 2: XC7A100T \"DSP48E1 Slices 240\""),
111 multiplier: Fact::published((25, 18), "UG479 (v1.10) 7 Series DSP48E1 Slice: \"25 × 18 two's-complement multiplier\""),
112 memories: XC7A100T_MEMORIES,
113 sources: XC7_SOURCES,
114 }
115 }
116
117 pub fn xc7a200t() -> PartResources {
119 PartResources {
120 part: "xc7a200t",
121 logic_unit: LogicUnit::Lut6,
122 logic: Fact::derived(
123 134_600,
124 "DS180 (v1.17) 7 Series FPGAs Data Sheet: Overview, Table 2: XC7A200T \"Slices 33,650\"; note 1 \"Each 7 series \
125 FPGA slice contains four LUTs and eight flip-flops\": 33,650 × 4",
126 ),
127 flops: Fact::derived(
128 269_200,
129 "DS180 (v1.17) 7 Series FPGAs Data Sheet: Overview, Table 2: XC7A200T \"Slices 33,650\"; note 1 \"four LUTs and \
130 eight flip-flops\": 33,650 × 8",
131 ),
132 dsp: Fact::published(740, "DS180 (v1.17) 7 Series FPGAs Data Sheet: Overview, Table 2: XC7A200T \"DSP48E1 Slices 740\""),
133 multiplier: Fact::published((25, 18), "UG479 (v1.10) 7 Series DSP48E1 Slice: \"25 × 18 two's-complement multiplier\""),
134 memories: XC7A200T_MEMORIES,
135 sources: XC7_SOURCES,
136 }
137 }
138
139 pub fn xczu7ev() -> PartResources {
141 PartResources {
142 part: "xczu7ev",
143 logic_unit: LogicUnit::Lut6,
144 logic: Fact::published(
145 230_400,
146 "DS891 (v1.11.1) Zynq UltraScale+ MPSoC Data Sheet: Overview, Table 5: EV Device Feature Summary, ZU7EV \"CLB LUTs \
147 230,400\"",
148 ),
149 flops: Fact::published(
150 460_800,
151 "DS891 (v1.11.1) Zynq UltraScale+ MPSoC Data Sheet: Overview, Table 5: EV Device Feature Summary, ZU7EV \"CLB \
152 Flip-Flops 460,800\"",
153 ),
154 dsp: Fact::published(
155 1_728,
156 "DS891 (v1.11.1) Zynq UltraScale+ MPSoC Data Sheet: Overview, Table 5: EV Device Feature Summary, ZU7EV \"DSP Slices \
157 1,728\"",
158 ),
159 multiplier: Fact::published(
160 (27, 18),
161 "UG579 (v1.7) UltraScale Architecture DSP Slice: \"27 × 18 two's complement multiplier with dynamic bypass\"",
162 ),
163 memories: XCZU7EV_MEMORIES,
164 sources: ZYNQ_US_PLUS_SOURCES,
165 }
166 }
167
168 pub fn sx2800() -> PartResources {
171 PartResources {
172 part: "1sx280",
173 logic_unit: LogicUnit::Alm,
174 logic: Fact::published(
175 933_120,
176 "GEN-1023-1.0 (May 2015) Stratix 10 Product Table: GX 2800 / SX 2800 \"Adaptive logic modules (ALMs) 933,120\"",
177 ),
178 flops: Fact::published(
179 3_732_480,
180 "GEN-1023-1.0 (May 2015) Stratix 10 Product Table: GX 2800 / SX 2800 \"ALM registers 3,732,480\"; S10-OVERVIEW \
181 (2020.09.28) Intel Stratix 10 GX/SX Device Overview: \"4 Registers per ALM\"",
182 ),
183 dsp: Fact::published(
184 5_760,
185 "GEN-1023-1.0 (May 2015) Stratix 10 Product Table: GX 2800 / SX 2800 \"Variable-precision digital signal processing \
186 (DSP) blocks 5,760\", \"18 x 19 multipliers 11,520\"",
187 ),
188 multiplier: Fact::published(
189 (18, 19),
190 "S10-OVERVIEW (2020.09.28) Intel Stratix 10 GX/SX Device Overview: \"Each DSP block can be independently configured \
191 at compile time as either dual 18x19 or a single 27x27 multiply accumulate\"; the array multiplies on the two \
192 18x19 halves (Table 4: \"18x19 Multipliers 11,520\", note 2: \"The number of 27x27 multipliers is one-half the \
193 number of 18x19 multipliers\")",
194 ),
195 memories: SX2800_MEMORIES,
196 sources: S10_SOURCES,
197 }
198 }
199
200 pub fn memory(&self, kind: MemoryKind) -> Option<&'static Memory> {
202 self.memories.iter().find(|m| m.kind == kind)
203 }
204
205 pub fn block_bytes(&self) -> u64 {
207 self.memories.iter().filter(|m| m.kind.is_block()).map(|m| m.count.value * m.bits.value / 8).sum()
208 }
209
210 pub fn block_read_bytes_per_cycle(&self) -> u64 {
212 self.memories
213 .iter()
214 .filter(|m| m.kind.is_block())
215 .map(|m| m.count.value * m.read_bits_per_cycle.value / 8)
216 .sum()
217 }
218
219 pub fn facts(&self) -> Vec<(String, Provenance, &'static str)> {
221 let mut out = vec![
222 ("logic".to_string(), self.logic.provenance, self.logic.cite),
223 ("flops".to_string(), self.flops.provenance, self.flops.cite),
224 ("dsp".to_string(), self.dsp.provenance, self.dsp.cite),
225 ("multiplier".to_string(), self.multiplier.provenance, self.multiplier.cite),
226 ];
227 for m in self.memories {
228 let at = |field: &str| format!("memory.{:?}.{field}", m.kind);
229 out.push((at("count"), m.count.provenance, m.count.cite));
230 out.push((at("bits"), m.bits.provenance, m.bits.cite));
231 out.push((at("read_bits_per_cycle"), m.read_bits_per_cycle.provenance, m.read_bits_per_cycle.cite));
232 out.push((at("preloaded"), m.preloaded.provenance, m.preloaded.cite));
233 }
234 out
235 }
236}
237
238const XC7_SOURCES: &[&str] = &[DS180, UG473, UG474, UG479];
239const S10_SOURCES: &[&str] = &[S10_OVERVIEW, S10_PRODUCT_TABLE];
240const ZYNQ_US_PLUS_SOURCES: &[&str] = &[DS891, UG573, UG574, UG579];
241
242const RAMB36E1_BITS: Fact<u64> = Fact::derived(
243 36_864,
244 "UG473 (v1.14) 7 Series FPGAs Memory Resources: a 36 Kb block RAM is \"32K x 1, 16K x 2, 8K x 4, 4K x 9, 2K x 18, 1K x 36, \
245 or 512 x 72 in simple dual-port mode\": 512 × 72",
246);
247const RAMB36E1_READ: Fact<u64> = Fact::derived(
248 72,
249 "UG473 (v1.14) 7 Series FPGAs Memory Resources: \"512 x 72 in simple dual-port mode\", or two 1K x 36 ports in true \
250 dual-port mode: 72 bits a cycle either way",
251);
252const RAMB36E1_PRELOADED: Fact<bool> = Fact::published(
253 true,
254 "UG473 (v1.14) 7 Series FPGAs Memory Resources: \"memory content can be initialized or cleared by the configuration \
255 bitstream\"",
256);
257const XC7_LUT_BITS: Fact<u64> = Fact::published(
258 64,
259 "UG474 (v1.8) 7 Series FPGAs Configurable Logic Block: SLICEMs \"can also use their LUTs as distributed 64-bit RAM\"",
260);
261const XC7_LUT_READ: Fact<u64> = Fact::derived(
262 1,
263 "UG474 (v1.8) 7 Series FPGAs Configurable Logic Block: a LUT of \"distributed 64-bit RAM\" read as 64 x 1 is one bit a \
264 cycle per read port",
265);
266const XC7_LUT_PRELOADED: Fact<bool> = Fact::derived(
267 true,
268 "UG474 (v1.8) 7 Series FPGAs Configurable Logic Block: distributed RAM is the LUT's own 64 configuration bits, which the \
269 bitstream writes",
270);
271
272const XC7A100T_MEMORIES: &[Memory] = &[
273 Memory {
274 kind: MemoryKind::LutRam,
275 count: Fact::derived(
276 19_008,
277 "DS180 (v1.17) 7 Series FPGAs Data Sheet: Overview, Table 2: XC7A100T \"Max Distributed RAM (Kb) 1,188\": 1,188 × 1024 \
278 / 64 bits a LUT",
279 ),
280 bits: XC7_LUT_BITS,
281 read_bits_per_cycle: XC7_LUT_READ,
282 preloaded: XC7_LUT_PRELOADED,
283 },
284 Memory {
285 kind: MemoryKind::Block36,
286 count: Fact::published(
287 135,
288 "DS180 (v1.17) 7 Series FPGAs Data Sheet: Overview, Table 2: XC7A100T \"Block RAM Blocks 18 Kb 270, 36 Kb 135\"",
289 ),
290 bits: RAMB36E1_BITS,
291 read_bits_per_cycle: RAMB36E1_READ,
292 preloaded: RAMB36E1_PRELOADED,
293 },
294];
295
296const XC7A200T_MEMORIES: &[Memory] = &[
297 Memory {
298 kind: MemoryKind::LutRam,
299 count: Fact::derived(
300 46_208,
301 "DS180 (v1.17) 7 Series FPGAs Data Sheet: Overview, Table 2: XC7A200T \"Max Distributed RAM (Kb) 2,888\": 2,888 × 1024 \
302 / 64 bits a LUT",
303 ),
304 bits: XC7_LUT_BITS,
305 read_bits_per_cycle: XC7_LUT_READ,
306 preloaded: XC7_LUT_PRELOADED,
307 },
308 Memory {
309 kind: MemoryKind::Block36,
310 count: Fact::published(
311 365,
312 "DS180 (v1.17) 7 Series FPGAs Data Sheet: Overview, Table 2: XC7A200T \"Block RAM Blocks 18 Kb 730, 36 Kb 365\"",
313 ),
314 bits: RAMB36E1_BITS,
315 read_bits_per_cycle: RAMB36E1_READ,
316 preloaded: RAMB36E1_PRELOADED,
317 },
318];
319
320const XCZU7EV_MEMORIES: &[Memory] = &[
321 Memory {
322 kind: MemoryKind::LutRam,
323 count: Fact::derived(
324 102_400,
325 "DS891 (v1.11.1) Zynq UltraScale+ MPSoC Data Sheet: Overview, Table 5: ZU7EV \"Distributed RAM (Mb) 6.2\", an upper \
326 bound from the top of its rounding: 6.25 × 2^20 / 64 bits a LUT (UG574 (v1.5) UltraScale Architecture Configurable \
327 Logic Block: \"distributed 64-bit RAM\")",
328 ),
329 bits: Fact::published(
330 64,
331 "UG574 (v1.5) UltraScale Architecture Configurable Logic Block: \"A SLICEM (memory) can also use the LUTs as \
332 distributed 64-bit RAM\"",
333 ),
334 read_bits_per_cycle: Fact::derived(
335 1,
336 "UG574 (v1.5) UltraScale Architecture Configurable Logic Block: \"The LUT can be configured as either a 64 x 1 or 32 \
337 x 2 memory\": one bit a cycle per read port at full depth",
338 ),
339 preloaded: Fact::derived(
340 true,
341 "UG574 (v1.5) UltraScale Architecture Configurable Logic Block: distributed RAM is the LUT's own 64 configuration bits, \
342 which the bitstream writes",
343 ),
344 },
345 Memory {
346 kind: MemoryKind::Block36,
347 count: Fact::published(
348 312,
349 "DS891 (v1.11.1) Zynq UltraScale+ MPSoC Data Sheet: Overview, Table 5: EV Device Feature Summary, ZU7EV \"Block RAM \
350 Blocks 312\"",
351 ),
352 bits: Fact::derived(
353 36_864,
354 "UG573 (v1.9) UltraScale Architecture Memory Resources: \"a 36 Kb block RAM can additionally be configured with a port \
355 width of 512 x 72 bits\": 512 × 72",
356 ),
357 read_bits_per_cycle: Fact::derived(
358 72,
359 "UG573 (v1.9) UltraScale Architecture Memory Resources: \"512 x 72 bits (when used as simple dual-port (SDP) memory)\", \
360 or two 1K x 36 read ports in true dual-port mode: 72 bits a cycle either way",
361 ),
362 preloaded: Fact::published(
363 true,
364 "UG573 (v1.9) UltraScale Architecture Memory Resources: \"The memory content can be initialized or cleared by the \
365 configuration bitstream\"",
366 ),
367 },
368 Memory {
369 kind: MemoryKind::Ultra,
370 count: Fact::published(
371 96,
372 "DS891 (v1.11.1) Zynq UltraScale+ MPSoC Data Sheet: Overview, Table 5: EV Device Feature Summary, ZU7EV \"UltraRAM \
373 Blocks 96\"",
374 ),
375 bits: Fact::derived(
376 294_912,
377 "UG573 (v1.9) UltraScale Architecture Memory Resources: \"Each UltraRAM block can store up to 288K bits of data and is \
378 configured as a 4K x 72 memory block\": 4096 × 72",
379 ),
380 read_bits_per_cycle: Fact::derived(
381 144,
382 "UG573 (v1.9) UltraScale Architecture Memory Resources: \"UltraRAM has two ports, both of which address all 4K x 72 \
383 bits. Each port can independently perform either one read or one write operation per clock cycle\": 2 × 72",
384 ),
385 preloaded: Fact::published(
386 false,
387 "UG573 (v1.9) UltraScale Architecture Memory Resources: \"There is no user defined INIT attribute and therefore the \
388 content of the SRAM array cannot be initialized to user defined values\"",
389 ),
390 },
391];
392
393const SX2800_MEMORIES: &[Memory] = &[
394 Memory {
395 kind: MemoryKind::Mlab,
396 count: Fact::published(
397 23_796,
398 "S10-OVERVIEW (2020.09.28) Intel Stratix 10 GX/SX Device Overview, Table 4: GX 2800/ SX 2800 \"MLAB Counts 23,796\"",
399 ),
400 bits: Fact::published(
401 640,
402 "S10-OVERVIEW (2020.09.28) Intel Stratix 10 GX/SX Device Overview: \"two types of embedded memory blocks: M20K (20 \
403 Kb) and MLAB (640 bit)\"",
404 ),
405 read_bits_per_cycle: Fact::derived(
406 20,
407 "S10-OVERVIEW (2020.09.28) Intel Stratix 10 GX/SX Device Overview, Table 10: MLAB \"32 x 20\", its widest port",
408 ),
409 preloaded: Fact::derived(
410 true,
411 "S10-OVERVIEW (2020.09.28) Intel Stratix 10 GX/SX Device Overview: \"Both M20K and MLAB embedded memory blocks can be \
412 configured as a single-port or dual-port RAM, FIFO, ROM, or shift register\"; a ROM's contents come from the \
413 bitstream",
414 ),
415 },
416 Memory {
417 kind: MemoryKind::M20k,
418 count: Fact::published(
419 11_721,
420 "S10-OVERVIEW (2020.09.28) Intel Stratix 10 GX/SX Device Overview, Table 4: GX 2800/ SX 2800 \"M20K Blocks 11,721\", \
421 \"M20K Mbits 229\"",
422 ),
423 bits: Fact::derived(
424 20_480,
425 "S10-OVERVIEW (2020.09.28) Intel Stratix 10 GX/SX Device Overview, Table 10: M20K (20 Kb) \"512 x 40 (or x32)\": 512 \
426 × 40",
427 ),
428 read_bits_per_cycle: Fact::derived(
429 40,
430 "S10-OVERVIEW (2020.09.28) Intel Stratix 10 GX/SX Device Overview, Table 10: M20K \"2K x 10 (or x8)\", \"1K x 20 (or \
431 x16)\", \"512 x 40 (or x32)\": 40 bits a cycle at its widest",
432 ),
433 preloaded: Fact::derived(
434 true,
435 "S10-OVERVIEW (2020.09.28) Intel Stratix 10 GX/SX Device Overview: \"Both M20K and MLAB embedded memory blocks can be \
436 configured as a single-port or dual-port RAM, FIFO, ROM, or shift register\"; a ROM's contents come from the \
437 bitstream",
438 ),
439 },
440];