1use rhdl::prelude::*;
20use rhdl_fpga::core::dff::DFF;
21
22use crate::tag::{TagFields, decode_tag_kernel, encode_tag_kernel, tag_is_well_formed_kernel};
23
24pub const FUTURE_CONTEXTS: usize = 9;
30
31#[allow(clippy::struct_excessive_bools)] #[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
34pub struct SequencerInput {
35 pub start_valid: bool,
37 pub context: b4,
39 pub generation: b8,
41 pub output_credit_available: bool,
43 pub request_ready: bool,
45 pub response_valid: bool,
47 pub response_tag: b32,
49}
50
51#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
53pub struct HardwareTaskCounts {
54 pub seed: b11,
56 pub public_seed: b11,
58 pub mask: b11,
60 pub segment_prf: b11,
62 pub secret_data: b11,
64 pub secret_padding: b11,
66 pub chain: b11,
68 pub public_key: b11,
70}
71
72#[kernel]
77pub fn encode_hardware_counts_kernel(counts: HardwareTaskCounts) -> b88 {
78 let seed: b88 = counts.seed.resize();
79 let public_seed: b88 = counts.public_seed.resize();
80 let mask: b88 = counts.mask.resize();
81 let segment_prf: b88 = counts.segment_prf.resize();
82 let secret_data: b88 = counts.secret_data.resize();
83 let secret_padding: b88 = counts.secret_padding.resize();
84 let chain: b88 = counts.chain.resize();
85 let public_key: b88 = counts.public_key.resize();
86 seed | (public_seed << 11)
87 | (mask << 22)
88 | (segment_prf << 33)
89 | (secret_data << 44)
90 | (secret_padding << 55)
91 | (chain << 66)
92 | (public_key << 77)
93}
94
95#[kernel]
97pub fn decode_hardware_counts_kernel(counts: b88) -> HardwareTaskCounts {
98 HardwareTaskCounts {
99 seed: counts.resize(),
100 public_seed: (counts >> 11).resize(),
101 mask: (counts >> 22).resize(),
102 segment_prf: (counts >> 33).resize(),
103 secret_data: (counts >> 44).resize(),
104 secret_padding: (counts >> 55).resize(),
105 chain: (counts >> 66).resize(),
106 public_key: (counts >> 77).resize(),
107 }
108}
109
110#[allow(clippy::struct_excessive_bools)] #[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
113pub struct SequencerOutput {
114 pub start_ready: bool,
116 pub active: bool,
118 pub output_credit_reserved: bool,
120 pub request_valid: bool,
122 pub request_tag: b32,
124 pub request_fields: TagFields,
126 pub response_ready: bool,
128 pub response_rejected: bool,
130 pub done: bool,
132 pub output_credit_release: bool,
134 pub counts: HardwareTaskCounts,
136}
137
138#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
140#[rhdl(dq_no_prefix)]
141pub struct SingleContextSequencer {
142 active: DFF<bool>,
143 waiting: DFF<bool>,
144 credit_reserved: DFF<bool>,
145 cursor: DFF<b32>,
146 counts: DFF<b88>,
147 response_rejected: DFF<bool>,
148 done: DFF<bool>,
149 credit_release: DFF<bool>,
150}
151
152impl Default for SingleContextSequencer {
153 fn default() -> Self {
154 Self {
155 active: DFF::new(false),
156 waiting: DFF::new(false),
157 credit_reserved: DFF::new(false),
158 cursor: DFF::new(b32(0)),
159 counts: DFF::new(b88(0)),
160 response_rejected: DFF::new(false),
161 done: DFF::new(false),
162 credit_release: DFF::new(false),
163 }
164 }
165}
166
167impl SynchronousIO for SingleContextSequencer {
168 type I = SequencerInput;
169 type O = SequencerOutput;
170 type Kernel = single_context_sequencer_kernel;
171}
172
173#[kernel]
175#[allow(clippy::used_underscore_binding)] pub fn single_context_sequencer_kernel(
177 _clock_reset: ClockReset,
178 input: SequencerInput,
179 q: Q,
180) -> (SequencerOutput, D) {
181 let mut d = D::dont_care();
182 d.active = q.active;
183 d.waiting = q.waiting;
184 d.credit_reserved = q.credit_reserved;
185 d.cursor = q.cursor;
186 d.counts = q.counts;
187 d.response_rejected = false;
188 d.done = false;
189 d.credit_release = false;
190
191 let request_fields = decode_tag_kernel(q.cursor);
192 let request_tag = q.cursor;
193 let current_counts = decode_hardware_counts_kernel(q.counts);
194 let mut next_cursor = request_fields;
195 let mut next_counts = current_counts;
196 let response_matches =
197 tag_is_well_formed_kernel(input.response_tag) && input.response_tag == request_tag;
198
199 if !q.active {
200 if input.start_valid && input.output_credit_available {
201 d.active = true;
202 d.waiting = false;
203 d.credit_reserved = true;
204 next_cursor = TagFields {
205 context: input.context,
206 kind: b3(0),
207 segment: b7(0),
208 chain_step: b4(0),
209 block_index: b6(0),
210 generation: input.generation,
211 };
212 next_counts = HardwareTaskCounts {
213 seed: b11(0),
214 public_seed: b11(0),
215 mask: b11(0),
216 segment_prf: b11(0),
217 secret_data: b11(0),
218 secret_padding: b11(0),
219 chain: b11(0),
220 public_key: b11(0),
221 };
222 }
223 } else if !q.waiting {
224 if input.request_ready {
225 d.waiting = true;
226 }
227 } else if input.response_valid {
228 if response_matches {
229 d.waiting = false;
230 match request_fields.kind {
231 Bits::<3>(0) => {
232 next_counts.seed = current_counts.seed + b11(1);
233 next_cursor.kind = b3(1);
234 }
235 Bits::<3>(1) => {
236 next_counts.public_seed = current_counts.public_seed + b11(1);
237 next_cursor.kind = b3(2);
238 next_cursor.segment = b7(0);
239 }
240 Bits::<3>(2) => {
241 next_counts.mask = current_counts.mask + b11(1);
242 if request_fields.segment == b7(15) {
243 next_cursor.kind = b3(3);
244 next_cursor.segment = b7(0);
245 } else {
246 next_cursor.segment = request_fields.segment + b7(1);
247 }
248 }
249 Bits::<3>(3) => {
250 next_counts.segment_prf = current_counts.segment_prf + b11(1);
251 next_cursor.kind = b3(4);
252 }
253 Bits::<3>(4) => {
254 next_counts.secret_data = current_counts.secret_data + b11(1);
255 next_cursor.kind = b3(5);
256 next_cursor.block_index = b6(1);
257 }
258 Bits::<3>(5) => {
259 next_counts.secret_padding = current_counts.secret_padding + b11(1);
260 next_cursor.kind = b3(6);
261 next_cursor.chain_step = b4(1);
262 next_cursor.block_index = b6(0);
263 }
264 Bits::<3>(6) => {
265 next_counts.chain = current_counts.chain + b11(1);
266 if request_fields.chain_step == b4(15) {
267 next_cursor.chain_step = b4(0);
268 if request_fields.segment == b7(66) {
269 next_cursor.kind = b3(7);
270 next_cursor.segment = b7(0);
271 next_cursor.block_index = b6(0);
272 } else {
273 next_cursor.kind = b3(3);
274 next_cursor.segment = request_fields.segment + b7(1);
275 }
276 } else {
277 next_cursor.chain_step = request_fields.chain_step + b4(1);
278 }
279 }
280 _ => {
281 next_counts.public_key = current_counts.public_key + b11(1);
282 if request_fields.block_index == b6(33) {
283 let exact_prefix = current_counts.seed == b11(1)
284 && current_counts.public_seed == b11(1)
285 && current_counts.mask == b11(16)
286 && current_counts.segment_prf == b11(67)
287 && current_counts.secret_data == b11(67)
288 && current_counts.secret_padding == b11(67)
289 && current_counts.chain == b11(1005)
290 && current_counts.public_key == b11(33);
291 if exact_prefix {
292 d.active = false;
293 d.credit_reserved = false;
294 d.done = true;
295 d.credit_release = true;
296 } else {
297 d.response_rejected = true;
298 d.waiting = true;
299 next_counts.public_key = current_counts.public_key;
300 }
301 } else {
302 next_cursor.block_index = request_fields.block_index + b6(1);
303 }
304 }
305 }
306 } else {
307 d.response_rejected = true;
308 }
309 }
310
311 let output = SequencerOutput {
312 start_ready: !q.active && input.output_credit_available,
313 active: q.active,
314 output_credit_reserved: q.credit_reserved,
315 request_valid: q.active && !q.waiting,
316 request_tag,
317 request_fields,
318 response_ready: q.active && q.waiting && response_matches,
319 response_rejected: q.response_rejected,
320 done: q.done,
321 output_credit_release: q.credit_release,
322 counts: current_counts,
323 };
324 d.cursor = encode_tag_kernel(next_cursor);
325 d.counts = encode_hardware_counts_kernel(next_counts);
326 (output, d)
327}
328
329const _: () = {
330 assert!(FUTURE_CONTEXTS <= 16);
331};