1use rhdl::prelude::*;
10
11#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
12pub struct Key {
13 pub client: b32,
14 pub session: b64,
15 pub sequence: b64,
16 pub hash: [b64; 4],
17}
18#[derive(Clone, Copy, Debug, PartialEq, Digital)]
19pub struct Config<const C: usize> {
20 pub valid: bool,
21 pub clients: [b32; C],
22 pub sessions: [b64; C],
23}
24#[derive(Clone, Copy, Debug, PartialEq, Digital)]
25pub struct Decided<const P: usize> {
26 pub valid: bool,
27 pub kind: b8,
29 pub key: Key,
30 pub len: b32,
31 pub data: [b8; P],
32}
33#[derive(Clone, Copy, Debug, PartialEq, Digital)]
34pub struct Dispatch<const P: usize> {
35 pub valid: bool,
36 pub token: b64,
37 pub key: Key,
38 pub len: b32,
39 pub data: [b8; P],
40}
41#[derive(Clone, Copy, Debug, PartialEq, Digital)]
42pub struct Completion<const R: usize> {
43 pub valid: bool,
44 pub token: b64,
45 pub key: Key,
46 pub outcome: b2,
48 pub status: b32,
49 pub len: b32,
50 pub data: [b8; R],
51}
52#[derive(Clone, Copy, Debug, PartialEq, Digital)]
53pub struct Reply<const R: usize> {
54 pub valid: bool,
55 pub key: Key,
56 pub outcome: b2,
57 pub status: b32,
58 pub len: b32,
59 pub data: [b8; R],
60}
61#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
62pub struct Notice {
63 pub valid: bool,
64 pub kind: b8,
65 pub key: Key,
66 pub status: b4,
69 pub state_valid: bool,
70 pub acked_through: b64,
71 pub completed_through: b64,
72}
73#[derive(Clone, Copy, Debug, PartialEq, Digital)]
74pub struct Entry<const P: usize, const R: usize> {
75 pub phase: b2,
77 pub key: Key,
78 pub len: b32,
79 pub data: [b8; P],
80 pub token: b64,
81 pub result: Reply<R>,
82 pub replay: bool,
83}
84#[derive(Clone, Copy, Debug, PartialEq, Digital)]
85pub struct State<const C: usize, const W: usize, const P: usize, const R: usize> {
86 pub initialized: bool,
87 pub retired: bool,
88 pub clients: [b32; C],
89 pub sessions: [b64; C],
90 pub acked: [b64; C],
91 pub completed: [b64; C],
92 pub entries: [[Entry<P, R>; W]; C],
94 pub next_token: b64,
95 pub token_exhausted: bool,
96 pub busy: bool,
97 pub accepted: bool,
98 pub work: Dispatch<P>,
100 pub reply: Reply<R>,
102 pub reply_client: b3,
104 pub reply_slot: b3,
105 pub notice: Notice,
106 pub terminal_completion: Completion<R>,
107 pub stale_completions: b32,
108}
109#[derive(Clone, Copy, Debug, PartialEq, Digital)]
110pub struct Input<const C: usize, const P: usize, const R: usize> {
111 pub cfg: Config<C>,
112 pub decided: Decided<P>,
113 pub app_ready: bool,
114 pub completion: Completion<R>,
115 pub reply_ready: bool,
116 pub notice_ready: bool,
117 pub fence: bool,
118}
119#[derive(Clone, Copy, Debug, PartialEq, Digital)]
120pub struct Output<const P: usize, const R: usize> {
121 pub decided_ready: bool,
122 pub dispatch: Dispatch<P>,
123 pub completion_ready: bool,
124 pub reply: Reply<R>,
125 pub notice: Notice,
126 pub retired: bool,
127}
128
129#[kernel]
130pub fn empty_key() -> Key {
131 Key {
132 client: b32(0),
133 session: b64(0),
134 sequence: b64(0),
135 hash: [b64(0); 4],
136 }
137}
138#[kernel]
139pub fn empty_reply<const R: usize>() -> Reply<R> {
140 Reply::<R> {
141 valid: false,
142 key: empty_key(),
143 outcome: b2(0),
144 status: b32(0),
145 len: b32(0),
146 data: [b8(0); R],
147 }
148}
149#[kernel]
150pub fn empty_completion<const R: usize>() -> Completion<R> {
151 Completion::<R> {
152 valid: false,
153 token: b64(0),
154 key: empty_key(),
155 outcome: b2(0),
156 status: b32(0),
157 len: b32(0),
158 data: [b8(0); R],
159 }
160}
161#[kernel]
162pub fn empty_entry<const P: usize, const R: usize>() -> Entry<P, R> {
163 Entry::<P, R> {
164 phase: b2(0),
165 key: empty_key(),
166 len: b32(0),
167 data: [b8(0); P],
168 token: b64(0),
169 result: empty_reply::<R>(),
170 replay: false,
171 }
172}
173impl<const C: usize> Default for Config<C> {
174 fn default() -> Self {
175 Self {
176 valid: false,
177 clients: [b32(0); C],
178 sessions: [b64(0); C],
179 }
180 }
181}
182impl<const P: usize> Default for Decided<P> {
183 fn default() -> Self {
184 Self {
185 valid: false,
186 kind: b8(0),
187 key: empty_key(),
188 len: b32(0),
189 data: [b8(0); P],
190 }
191 }
192}
193impl<const P: usize> Default for Dispatch<P> {
194 fn default() -> Self {
195 Self {
196 valid: false,
197 token: b64(0),
198 key: empty_key(),
199 len: b32(0),
200 data: [b8(0); P],
201 }
202 }
203}
204impl<const R: usize> Default for Completion<R> {
205 fn default() -> Self {
206 empty_completion::<R>()
207 }
208}
209impl<const R: usize> Default for Reply<R> {
210 fn default() -> Self {
211 empty_reply::<R>()
212 }
213}
214impl<const C: usize, const W: usize, const P: usize, const R: usize> Default for State<C, W, P, R> {
215 fn default() -> Self {
216 Self {
217 initialized: false,
218 retired: false,
219 clients: [b32(0); C],
220 sessions: [b64(0); C],
221 acked: [b64(0); C],
222 completed: [b64(0); C],
223 entries: [[empty_entry::<P, R>(); W]; C],
224 next_token: b64(1),
225 token_exhausted: false,
226 busy: false,
227 accepted: false,
228 work: Dispatch::default(),
229 reply: Reply::default(),
230 reply_client: b3(0),
231 reply_slot: b3(0),
232 notice: Notice::default(),
233 terminal_completion: Completion::default(),
234 stale_completions: b32(0),
235 }
236 }
237}
238impl<const C: usize, const P: usize, const R: usize> Default for Input<C, P, R> {
239 fn default() -> Self {
240 Self {
241 cfg: Config::default(),
242 decided: Decided::default(),
243 app_ready: false,
244 completion: Completion::default(),
245 reply_ready: false,
246 notice_ready: false,
247 fence: false,
248 }
249 }
250}
251
252#[kernel]
255pub fn step<const C: usize, const W: usize, const P: usize, const R: usize>(
256 i: Input<C, P, R>,
257 r: State<C, W, P, R>,
258) -> (Output<P, R>, State<C, W, P, R>) {
259 let mut s = r;
260 let mut o = Output::<P, R> {
261 decided_ready: false,
262 dispatch: r.work,
263 completion_ready: r.initialized,
264 reply: r.reply,
265 notice: r.notice,
266 retired: r.retired || i.fence,
267 };
268 if r.reply.valid && i.reply_ready {
269 s.reply.valid = false;
270 }
271 if r.notice.valid && i.notice_ready {
272 s.notice.valid = false;
273 }
274 if r.work.valid && i.app_ready {
276 s.work.valid = false;
277 s.accepted = true;
278 for c in 0..C {
279 for w in 0..W {
280 if r.entries[c][w].phase == b2(1) && r.entries[c][w].token == r.work.token {
281 s.entries[c][w].phase = b2(2);
282 }
283 }
284 }
285 }
286 if i.fence {
287 s.retired = true;
288 }
289 if !r.initialized && !s.retired && i.cfg.valid {
290 let mut good = C >= 1
293 && C <= 8
294 && W >= 1
295 && W <= 8
296 && P >= 1
297 && P <= 4294967295
298 && R >= 1
299 && R <= 4294967295;
300 for c in 0..C {
301 if i.cfg.sessions[c] == b64(0) {
302 good = false;
303 }
304 for d in 0..C {
305 if c != d && i.cfg.clients[c] == i.cfg.clients[d] {
306 good = false;
307 }
308 }
309 }
310 if good {
311 s.initialized = true;
312 s.clients = i.cfg.clients;
313 s.sessions = i.cfg.sessions;
314 } else {
315 s.retired = true;
316 }
317 }
318 if r.initialized && i.cfg.valid && !s.retired && !s.notice.valid {
319 s.notice = Notice {
320 valid: true,
321 kind: b8(0),
322 key: empty_key(),
323 status: b4(8),
324 state_valid: false,
325 acked_through: b64(0),
326 completed_through: b64(0),
327 };
328 }
329 if r.initialized && i.completion.valid {
330 let active_token = r.busy && i.completion.token == r.work.token;
331 if s.retired {
332 if active_token && !s.terminal_completion.valid {
333 s.terminal_completion = i.completion;
334 }
335 } else if active_token {
336 let good = r.accepted
337 && i.completion.key == r.work.key
338 && i.completion.len <= b32(R as u128)
339 && i.completion.outcome <= b2(1);
340 if !good {
341 s.retired = true;
342 s.terminal_completion = i.completion;
343 } else {
344 let mut data = [b8(0); R];
345 for b in 0..R {
346 if b32(b as u128) < i.completion.len {
347 data[b] = i.completion.data[b];
348 }
349 }
350 for c in 0..C {
351 for w in 0..W {
352 if r.entries[c][w].phase == b2(2) && r.entries[c][w].token == r.work.token {
353 s.entries[c][w].phase = b2(3);
354 s.entries[c][w].result = Reply::<R> {
355 valid: true,
356 key: r.work.key,
357 outcome: i.completion.outcome,
358 status: i.completion.status,
359 len: i.completion.len,
360 data,
361 };
362 s.entries[c][w].replay = true;
363 }
364 }
365 }
366 s.busy = false;
367 s.accepted = false;
368 }
369 } else {
370 s.stale_completions = r.stale_completions + b32(1);
371 }
372 }
373 if r.initialized && !s.retired && i.decided.valid && !s.notice.valid {
374 let d = i.decided;
375 let same_active = r.busy
376 && d.kind == b8(1)
377 && d.key.client == r.work.key.client
378 && d.key.session == r.work.key.session
379 && d.key.sequence == r.work.key.sequence;
380 if !r.busy || same_active {
381 let mut found_client = false;
382 let mut status = b4(3);
383 let mut reject = true;
384 let mut floor = b64(0);
385 let mut completed = b64(0);
386 let mut canonical = [b8(0); P];
387 for b in 0..P {
388 if b32(b as u128) < d.len {
389 canonical[b] = d.data[b];
390 }
391 }
392 for c in 0..C {
393 if d.key.client == r.clients[c] && d.key.session == r.sessions[c] {
394 found_client = true;
395 floor = r.acked[c];
396 completed = r.completed[c];
397 if d.kind == b8(2) {
398 let mut empty = d.len == b32(0);
399 for b in 0..P {
400 if d.data[b] != b8(0) {
401 empty = false;
402 }
403 }
404 if !empty {
405 status = b4(7);
406 } else if d.key.sequence <= floor {
407 status = b4(1);
408 } else if d.key.sequence > completed {
409 status = b4(2);
410 } else {
411 status = b4(0);
412 s.acked[c] = d.key.sequence;
413 floor = d.key.sequence;
414 for w in 0..W {
415 if r.entries[c][w].phase == b2(3)
416 && r.entries[c][w].key.sequence <= d.key.sequence
417 {
418 s.entries[c][w] = empty_entry::<P, R>();
419 }
420 }
421 }
422 } else if d.kind != b8(1) || d.len > b32(P as u128) {
423 status = b4(7);
424 } else if d.key.sequence <= floor {
425 status = b4(5);
426 } else if d.key.sequence - floor > b64(W as u128) {
427 status = b4(4);
428 } else {
429 let mut found = false;
430 for w in 0..W {
431 let e = r.entries[c][w];
432 if e.phase != b2(0) && e.key.sequence == d.key.sequence {
433 found = true;
434 if e.key == d.key && e.len == d.len && e.data == canonical {
435 reject = false;
436 if s.entries[c][w].phase == b2(3) {
437 s.entries[c][w].replay = true;
438 }
439 } else {
440 status = b4(6);
441 }
442 }
443 }
444 if !found && !r.busy {
445 let mut taken = false;
446 for w in 0..W {
447 if r.entries[c][w].phase == b2(0) && !taken {
448 taken = true;
449 reject = false;
450 if r.token_exhausted {
451 s.retired = true;
452 } else {
453 s.entries[c][w] = Entry::<P, R> {
454 phase: b2(1),
455 key: d.key,
456 len: d.len,
457 data: canonical,
458 token: r.next_token,
459 result: empty_reply::<R>(),
460 replay: false,
461 };
462 s.work = Dispatch::<P> {
463 valid: true,
464 token: r.next_token,
465 key: d.key,
466 len: d.len,
467 data: canonical,
468 };
469 s.busy = true;
470 s.accepted = false;
471 if r.next_token == b64(18446744073709551615) {
472 s.token_exhausted = true;
473 } else {
474 s.next_token = r.next_token + b64(1);
475 }
476 }
477 }
478 }
479 if !taken {
482 s.retired = true;
483 reject = false;
484 }
485 }
486 }
487 }
488 }
489 o.decided_ready = !s.retired;
490 if reject && !s.retired {
491 s.notice = Notice {
492 valid: true,
493 kind: d.kind,
494 key: d.key,
495 status,
496 state_valid: found_client,
497 acked_through: floor,
498 completed_through: completed,
499 };
500 }
501 }
502 }
503 if r.initialized && !s.retired {
504 for c in 0..C {
506 for pass in 0..W {
507 let _unused = pass;
508 if s.completed[c] != b64(18446744073709551615) {
509 let next = s.completed[c] + b64(1);
510 let mut exists = false;
511 for w in 0..W {
512 if s.entries[c][w].phase == b2(3) && s.entries[c][w].key.sequence == next {
513 exists = true;
514 }
515 }
516 if exists {
517 s.completed[c] = next;
518 }
519 }
520 }
521 }
522 if !s.reply.valid {
523 let mut copied = false;
524 let mut client = r.reply_client;
525 let mut slot = r.reply_slot;
526 for client_pass in 0..C {
529 let _client_pass = client_pass;
530 for slot_pass in 0..W {
531 let _slot_pass = slot_pass;
532 let mut next_client = client;
533 let mut next_slot = slot + b3(1);
534 if slot.resize::<4>() + b4(1) >= b4(W as u128) {
535 next_slot = b3(0);
536 next_client = client + b3(1);
537 if client.resize::<4>() + b4(1) >= b4(C as u128) {
538 next_client = b3(0);
539 }
540 }
541 if !copied
542 && s.entries[client][slot].phase == b2(3)
543 && s.entries[client][slot].replay
544 {
545 s.reply = s.entries[client][slot].result;
546 s.entries[client][slot].replay = false;
547 s.reply_client = next_client;
548 s.reply_slot = next_slot;
549 copied = true;
550 }
551 client = next_client;
552 slot = next_slot;
553 }
554 }
555 }
556 }
557 o.retired = s.retired;
558 (o, s)
559}