Skip to main content

rhdl_fpga_planner/
shares.rs

1//! Step 1 of a plan: each module's guaranteed share of the budget.
2
3use crate::*;
4
5/// Sum of the fixed allowances.
6pub fn fixed_total(slots: &[Slot]) -> Cost {
7    slots.iter().filter_map(|s| s.component.fixed()).fold(Cost::default(), |a, f| a.add(f, 1))
8}
9
10/// Each slot's fixed allowance (`None` for a module with a power).
11pub fn allowances(slots: &[Slot]) -> Vec<Option<Share>> {
12    slots.iter().map(|s| s.component.fixed()).collect()
13}
14
15/// [`allowances`] of the slots `mode` [admits](Mode::admits) (`None` for the
16/// rest): a uBFT chip has no SHA-512 line, and a paper no replica.
17pub fn allowances_in(mode: Mode, slots: &[Slot]) -> Vec<Option<Share>> {
18    slots.iter().map(|s| if mode.admits(&*s.component) { s.component.fixed() } else { None }).collect()
19}
20
21/// Each slot's share: fixed allowances as given (the SHA-512 line's part of
22/// the LUT split); everyone else `power` % of what the budget has left after
23/// them ([`Budget::rest`]). A module's manual [limit](Slot::limits) on a
24/// resource gives it exactly that % of the resource; the rest of that
25/// resource is divided among the other modules in proportion to their
26/// powers.
27pub fn shares(budget: &Budget, slots: &[Slot]) -> Vec<Share> {
28    shares_with(budget, slots, &allowances(slots))
29}
30
31/// [`shares`] with the fixed allowances given per slot (a line the planner
32/// sizes to demand, [`Component::size_for`]).
33pub fn shares_with(budget: &Budget, slots: &[Slot], allow: &[Option<Share>]) -> Vec<Share> {
34    let mut out = power_shares(budget, slots, allow);
35    let rest = budget.rest_with(allow);
36    for (i, r) in Resource::ALL.into_iter().enumerate() {
37        let limited: Vec<usize> =
38            (0..slots.len()).filter(|&k| slots[k].component.fixed().is_none() && slots[k].limits[i].is_some()).collect();
39        if limited.is_empty() {
40            continue;
41        }
42        let total = r.of(&rest) as f64;
43        let mut taken = 0.0;
44        for &k in &limited {
45            let n = total * slots[k].limits[i].unwrap().clamp(0.0, 100.0) / 100.0;
46            taken += n;
47            out[k] = r.with(out[k], n as u32);
48        }
49        let left = (total - taken).max(0.0);
50        let others: Vec<usize> =
51            (0..slots.len()).filter(|&k| slots[k].component.fixed().is_none() && slots[k].limits[i].is_none()).collect();
52        let powers: f64 = others.iter().map(|&k| slots[k].power.max(0.0)).sum();
53        for &k in &others {
54            let n = if powers > 0.0 { left * slots[k].power.max(0.0) / powers } else { 0.0 };
55            out[k] = r.with(out[k], n as u32);
56        }
57    }
58    out
59}
60
61/// The shares from the powers alone.
62pub(crate) fn power_shares(budget: &Budget, slots: &[Slot], allow: &[Option<Share>]) -> Vec<Share> {
63    // Fixed allowances come out of the budget first; the powers divide the
64    // rest.
65    let rest = budget.rest_with(allow);
66    slots
67        .iter()
68        .zip(allow)
69        .map(|(s, a)| match a {
70            Some(f) => *f,
71            None => {
72                let f = s.power.clamp(0.0, 100.0) / 100.0;
73                Share {
74                    lut: (rest.lut as f64 * f) as u32,
75                    dsp: (rest.dsp as f64 * f) as u32,
76                    bram36: (rest.bram36 as f64 * f) as u32,
77                    uram: (rest.uram as f64 * f) as u32,
78                }
79            }
80        })
81        .collect()
82}