u280_shell_rhdl/
scoreboard.rs1use rhdl::prelude::*;
4
5use crate::abi::LIVE_JOB_SLOTS;
6
7#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
9pub enum JobState {
10 #[default]
12 Empty,
13 Active,
15 Streaming,
17}
18
19#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
21pub struct JobEntry {
22 pub state: JobState,
24 pub job_id: b32,
26 pub cluster: b2,
28}
29
30#[allow(clippy::struct_excessive_bools)] #[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
33pub struct ScoreboardEvents {
34 pub admit: bool,
36 pub admit_job: b32,
38 pub admit_cluster: b2,
40 pub frame_start: bool,
42 pub frame_start_job: b32,
44 pub frame_start_cluster: b2,
46 pub frame_finish: bool,
48 pub frame_finish_job: b32,
50 pub error: bool,
52 pub error_job: b32,
54 pub error_cluster: b2,
56}
57
58#[derive(Clone, Copy, Debug, Digital, Eq, PartialEq)]
60pub struct ScoreboardUpdate {
61 pub entries: [JobEntry; LIVE_JOB_SLOTS],
63 pub occupancy: b4,
65 pub fatal: bool,
67 pub failing_job: b32,
69 pub detail: b8,
71}
72
73impl Default for ScoreboardUpdate {
74 fn default() -> Self {
75 Self {
76 entries: [JobEntry::default(); LIVE_JOB_SLOTS],
77 occupancy: b4(0),
78 fatal: false,
79 failing_job: b32(0xffff_ffff),
80 detail: b8(0),
81 }
82 }
83}
84
85#[kernel]
91fn scoreboard_slot_cluster_kernel(slot: b4) -> b2 {
92 if slot < b4(4) {
93 b2(0)
94 } else if slot < b4(8) {
95 b2(1)
96 } else {
97 b2(2)
98 }
99}
100
101#[kernel]
103#[allow(clippy::assign_op_pattern, clippy::needless_range_loop)] pub fn scoreboard_update_kernel(
105 current: [JobEntry; LIVE_JOB_SLOTS],
106 events: ScoreboardEvents,
107) -> ScoreboardUpdate {
108 let mut next = current;
109 let mut fatal = false;
110 let mut failing_job = b32(0xffff_ffff);
111 let mut detail = b8(0);
112
113 for index in 0..LIVE_JOB_SLOTS {
117 let expected_cluster = scoreboard_slot_cluster_kernel(b4(index as u128));
118 if current[index].state != JobState::Empty && current[index].cluster != expected_cluster {
119 if !fatal {
120 failing_job = current[index].job_id;
121 detail = b8(5);
122 }
123 fatal = true;
124 }
125 }
126
127 if events.frame_start && !fatal {
128 let mut matches = b4(0);
129 let mut exact_matches = b4(0);
130 let mut selected = b4(0);
131 for index in 0..LIVE_JOB_SLOTS {
132 if next[index].state != JobState::Empty && next[index].job_id == events.frame_start_job
133 {
134 matches = matches + b4(1);
135 let expected_cluster = scoreboard_slot_cluster_kernel(b4(index as u128));
136 if next[index].cluster == events.frame_start_cluster
137 && next[index].cluster == expected_cluster
138 {
139 exact_matches = exact_matches + b4(1);
140 selected = b4(index as u128);
141 }
142 }
143 }
144 if matches != b4(1)
145 || exact_matches != b4(1)
146 || events.frame_start_cluster >= b2(3)
147 || next[selected].state != JobState::Active
148 {
149 fatal = true;
150 failing_job = events.frame_start_job;
151 detail = b8(1);
152 } else {
153 next[selected] = JobEntry {
154 state: JobState::Streaming,
155 job_id: events.frame_start_job,
156 cluster: events.frame_start_cluster,
157 };
158 }
159 }
160
161 if events.frame_finish && !fatal {
162 let mut matches = b4(0);
163 let mut exact_matches = b4(0);
164 let mut selected = b4(0);
165 for index in 0..LIVE_JOB_SLOTS {
166 if next[index].state != JobState::Empty && next[index].job_id == events.frame_finish_job
167 {
168 matches = matches + b4(1);
169 let expected_cluster = scoreboard_slot_cluster_kernel(b4(index as u128));
170 if next[index].cluster == expected_cluster {
171 exact_matches = exact_matches + b4(1);
172 selected = b4(index as u128);
173 }
174 }
175 }
176 if matches != b4(1) || exact_matches != b4(1) || next[selected].state != JobState::Streaming
177 {
178 if !fatal {
179 failing_job = events.frame_finish_job;
180 detail = b8(2);
181 }
182 fatal = true;
183 } else {
184 next[selected] = JobEntry::default();
185 }
186 }
187
188 if events.error && !fatal {
189 let mut matches = b4(0);
190 let mut exact_matches = b4(0);
191 let mut selected = b4(0);
192 for index in 0..LIVE_JOB_SLOTS {
193 if next[index].state != JobState::Empty && next[index].job_id == events.error_job {
194 matches = matches + b4(1);
195 let expected_cluster = scoreboard_slot_cluster_kernel(b4(index as u128));
196 if next[index].cluster == events.error_cluster
197 && next[index].cluster == expected_cluster
198 {
199 exact_matches = exact_matches + b4(1);
200 selected = b4(index as u128);
201 }
202 }
203 }
204 if matches != b4(1)
205 || exact_matches != b4(1)
206 || events.error_cluster >= b2(3)
207 || next[selected].state != JobState::Active
208 {
209 if !fatal {
210 failing_job = events.error_job;
211 detail = b8(3);
212 }
213 fatal = true;
214 } else {
215 next[selected] = JobEntry::default();
216 }
217 }
218
219 if events.admit && !fatal {
220 let mut duplicates = b4(0);
221 let mut empty_found = false;
222 let mut empty_index = b4(0);
223 for index in 0..LIVE_JOB_SLOTS {
224 let expected_cluster = scoreboard_slot_cluster_kernel(b4(index as u128));
225 if next[index].state != JobState::Empty && next[index].job_id == events.admit_job {
226 duplicates = duplicates + b4(1);
227 }
228 if !empty_found
229 && expected_cluster == events.admit_cluster
230 && next[index].state == JobState::Empty
231 {
232 empty_found = true;
233 empty_index = b4(index as u128);
234 }
235 }
236 if duplicates != b4(0) || !empty_found || events.admit_cluster >= b2(3) {
237 if !fatal {
238 failing_job = events.admit_job;
239 detail = b8(4);
240 }
241 fatal = true;
242 } else {
243 next[empty_index] = JobEntry {
244 state: JobState::Active,
245 job_id: events.admit_job,
246 cluster: events.admit_cluster,
247 };
248 }
249 }
250
251 if fatal {
255 next = current;
256 }
257
258 let mut occupancy = b4(0);
259 for index in 0..LIVE_JOB_SLOTS {
260 if next[index].state != JobState::Empty {
261 occupancy = occupancy + b4(1);
262 }
263 }
264 ScoreboardUpdate {
265 entries: next,
266 occupancy,
267 fatal,
268 failing_job,
269 detail,
270 }
271}
272
273#[kernel]
275#[allow(clippy::assign_op_pattern, clippy::needless_range_loop)] pub fn scoreboard_occupancy_kernel(entries: [JobEntry; LIVE_JOB_SLOTS]) -> b4 {
277 let mut occupancy = b4(0);
278 for index in 0..LIVE_JOB_SLOTS {
279 if entries[index].state != JobState::Empty {
280 occupancy = occupancy + b4(1);
281 }
282 }
283 occupancy
284}