rhdl_fpga_planner/
delivered.rs1use crate::*;
4
5#[derive(Clone, Debug, PartialEq)]
7pub struct Service {
8 pub name: &'static str,
10 pub demand: f64,
12 pub supply: f64,
14}
15
16#[derive(Clone, Debug, PartialEq)]
18pub struct SideRate {
19 pub rate: f64,
21 pub bottleneck: String,
23 pub latency_us: f64,
26}
27
28#[derive(Clone, Debug, PartialEq)]
30pub struct Delivered {
31 pub sign: SideRate,
33 pub verify: SideRate,
35 pub per_role: Vec<(Role, f64)>,
37 pub latency_us: Vec<(Role, f64)>,
40 pub services: Vec<Service>,
42 pub sha_bound: bool,
44 pub sha_wanted: f64,
48 pub batch: f64,
50 pub batch_mismatch: bool,
53 pub needs: Vec<(Role, f64)>,
57}
58
59impl Delivered {
61 pub fn side(&self, side: Side) -> &SideRate {
63 match side {
64 Side::Sign => &self.sign,
65 Side::Verify => &self.verify,
66 }
67 }
68 pub fn of(&self, role: Role) -> f64 {
70 self.per_role.iter().find(|(r, _)| *r == role).map_or(0.0, |(_, x)| *x)
71 }
72 pub fn need(&self, role: Role) -> f64 {
75 self.needs.iter().find(|(r, _)| *r == role).map_or(0.0, |(_, x)| *x)
76 }
77 pub fn latency_of(&self, role: Role) -> f64 {
79 self.latency_us.iter().find(|(r, _)| *r == role).map_or(0.0, |(_, x)| *x)
80 }
81}
82
83pub fn deliver(mode: Mode, cx: &Context, slots: &[Slot], parts: &[(Share, Option<Plan>)]) -> Delivered {
85 let mut capacity = [0.0f64; 4];
88 let mut blocks_per_op = [0.0f64; 4];
89 let mut latency = [0.0f64; 4];
90 let mut supply = 0.0;
92 let mut sha_latency_us = 0.0f64;
93 let mut batch = DSIG_BATCH;
95 let mut verify_batch = None;
96 let mut covered: Vec<Role> = Vec::new();
98 for (slot, (_, plan)) in slots.iter().zip(parts) {
99 let Some(p) = plan else { continue };
100 covered.extend(p.covers.iter().copied());
101 if slot.component.role() == Role::Sha512 {
102 sha_latency_us = sha_latency_us.max(p.idle_latency_us(cx.mhz));
103 }
104 if slot.component.role() == Role::DsigSign {
105 if let Some(k) = p.batch {
106 batch = k;
107 }
108 }
109 if slot.component.role() == Role::DsigVerify {
110 verify_batch = p.batch;
111 }
112 supply += p.supplies.iter().filter(|f| f.service == SHA512_BLOCKS).map(|f| f.per_second).sum::<f64>();
113 }
114 for (slot, (_, plan)) in slots.iter().zip(parts) {
115 let Some(p) = plan else { continue };
116 let Some(k) = Role::OPS.iter().position(|r| *r == slot.component.role()) else { continue };
117 capacity[k] += p.rate;
118 let blocks: f64 = p.demands.iter().filter(|f| f.service == SHA512_BLOCKS).map(|f| f.per_second).sum();
119 if p.rate > 0.0 {
120 blocks_per_op[k] = blocks / p.rate;
121 }
122 let sha = if blocks > 0.0 { blocks_per_op[k] * sha_latency_us } else { 0.0 };
125 latency[k] = latency[k].max(p.idle_latency_us(cx.mhz) + sha);
126 }
127 let need = |role: Role| if covered.contains(&role) { 0.0 } else { mode.need(role, cx, batch) };
129 let mut sides = Vec::new();
131 let mut blocks_per_side_op = [0.0f64; 2];
132 for (s, side) in Side::BOTH.iter().enumerate() {
133 let mut rate = f64::INFINITY;
134 let mut bottleneck = String::from("nothing");
135 let mut lat = 0.0f64;
136 for role in side.roles() {
137 let need = need(role);
138 if need <= 0.0 {
139 continue;
140 }
141 let k = Role::OPS.iter().position(|r| *r == role).unwrap();
142 let cap = capacity[k] / need;
143 if cap < rate {
144 rate = cap;
145 bottleneck = role.name().to_string();
146 }
147 blocks_per_side_op[s] += need * blocks_per_op[k];
148 lat = lat.max(latency[k]);
149 }
150 if !rate.is_finite() {
151 rate = 0.0;
152 }
153 sides.push(SideRate { rate, bottleneck, latency_us: lat });
154 }
155 let demand = sides[0].rate * blocks_per_side_op[0] + sides[1].rate * blocks_per_side_op[1];
158 let mut sha_bound = false;
159 if demand > supply && demand > 0.0 {
160 let scale = supply / demand;
161 for s in sides.iter_mut() {
162 if s.rate > 0.0 {
163 s.rate *= scale;
164 s.bottleneck = "SHA-512 line".into();
165 }
166 }
167 sha_bound = true;
168 }
169 let verify = sides.pop().unwrap();
170 let sign = sides.pop().unwrap();
171 let per_role = Role::OPS
172 .iter()
173 .map(|r| {
174 let side_rate = match r.side() {
175 Some(Side::Sign) => sign.rate,
176 _ => verify.rate,
177 };
178 (*r, side_rate * need(*r))
179 })
180 .collect();
181 let latency_us = Role::OPS.iter().enumerate().filter(|(_, r)| need(**r) > 0.0).map(|(k, r)| (*r, latency[k])).collect();
182 let needs = Role::OPS.iter().map(|r| (*r, need(*r))).collect();
183 let used = sign.rate * blocks_per_side_op[0] + verify.rate * blocks_per_side_op[1];
184 let services = if used > 0.0 || supply > 0.0 { vec![Service { name: SHA512_BLOCKS, demand: used, supply }] } else { Vec::new() };
185 let batch_mismatch = verify_batch.is_some_and(|k| k != batch);
186 Delivered { sign, verify, per_role, latency_us, services, sha_bound, sha_wanted: demand, batch, batch_mismatch, needs }
187}