P4C
The P4 Compiler
Loading...
Searching...
No Matches
tofino/bf-asm/tofino/deparser.cpp
1
17
18/* deparser template specializations for tofino -- #included directly in top-level deparser.cpp */
19
20#define YES(X) X
21#define NO(X)
22
23#define SIMPLE_INTRINSIC(GR, PFX, NAME, IF_SHIFT) \
24 DEPARSER_INTRINSIC(Tofino, GR, NAME, 1) { \
25 PFX.NAME.phv = intrin.vals[0].val->reg.deparser_id(); \
26 IF_SHIFT(PFX.NAME.shft = intrin.vals[0].val->lo;) \
27 if (!intrin.vals[0].pov.empty()) \
28 error(intrin.vals[0].pov.front().lineno, "No POV support in tofino " #NAME); \
29 PFX.NAME.valid = 1; \
30 }
31#define SIMPLE_INTRINSIC_RENAME(GR, PFX, NAME, REGNAME, IF_SHIFT) \
32 DEPARSER_INTRINSIC(Tofino, GR, NAME, 1) { \
33 PFX.REGNAME.phv = intrin.vals[0].val->reg.deparser_id(); \
34 IF_SHIFT(PFX.REGNAME.shft = intrin.vals[0].val->lo;) \
35 PFX.REGNAME.valid = 1; \
36 }
37#define IIR_MAIN_INTRINSIC(NAME, SHFT) SIMPLE_INTRINSIC(INGRESS, regs.input.iir.main_i, NAME, SHFT)
38#define IIR_INTRINSIC(NAME, SHFT) SIMPLE_INTRINSIC(INGRESS, regs.input.iir.ingr, NAME, SHFT)
39#define HIR_INTRINSIC(NAME, SHFT) SIMPLE_INTRINSIC(INGRESS, regs.header.hir.ingr, NAME, SHFT)
40#define HIR_INTRINSIC_RENAME(NAME, REGNAME, SHFT) \
41 SIMPLE_INTRINSIC_RENAME(INGRESS, regs.header.hir.ingr, NAME, REGNAME, SHFT)
42#define IER_MAIN_INTRINSIC(NAME, SHFT) SIMPLE_INTRINSIC(EGRESS, regs.input.ier.main_e, NAME, SHFT)
43#define HER_INTRINSIC(NAME, SHFT) SIMPLE_INTRINSIC(EGRESS, regs.header.her.egr, NAME, SHFT)
44
45IIR_MAIN_INTRINSIC(egress_unicast_port, NO)
46IIR_MAIN_INTRINSIC(drop_ctl, YES)
47IIR_INTRINSIC(copy_to_cpu, YES)
48HIR_INTRINSIC_RENAME(egress_multicast_group_0, egress_multicast_group[0], NO)
49HIR_INTRINSIC_RENAME(egress_multicast_group_1, egress_multicast_group[1], NO)
50HIR_INTRINSIC_RENAME(hash_lag_ecmp_mcast_0, hash_lag_ecmp_mcast[0], NO)
51HIR_INTRINSIC_RENAME(hash_lag_ecmp_mcast_1, hash_lag_ecmp_mcast[1], NO)
52HIR_INTRINSIC(copy_to_cpu_cos, YES)
53DEPARSER_INTRINSIC(Tofino, INGRESS, ingress_port_source, 1) {
54 regs.header.hir.ingr.ingress_port.phv = intrin.vals[0].val->reg.deparser_id();
55 regs.header.hir.ingr.ingress_port.sel = 0;
56}
57HIR_INTRINSIC(deflect_on_drop, YES)
58HIR_INTRINSIC(meter_color, YES)
59HIR_INTRINSIC(icos, YES)
60HIR_INTRINSIC(qid, YES)
61HIR_INTRINSIC(xid, NO)
62HIR_INTRINSIC(yid, NO)
63HIR_INTRINSIC(rid, NO)
64HIR_INTRINSIC(bypss_egr, YES)
65HIR_INTRINSIC(ct_disable, YES)
66HIR_INTRINSIC(ct_mcast, YES)
67
68IER_MAIN_INTRINSIC(egress_unicast_port, NO)
69IER_MAIN_INTRINSIC(drop_ctl, YES)
70HER_INTRINSIC(force_tx_err, YES)
71HER_INTRINSIC(tx_pkt_has_offsets, YES)
72HER_INTRINSIC(capture_tx_ts, YES)
73HER_INTRINSIC(coal, NO)
74HER_INTRINSIC(ecos, YES)
75
76#undef SIMPLE_INTRINSIC
77#undef IIR_MAIN_INTRINSIC
78#undef IIR_INTRINSIC
79#undef HIR_INTRINSIC
80#undef IER_INTRINSIC
81#undef HER_INTRINSIC
82
83#define TOFINO_DIGEST(GRESS, NAME, CFG, TBL, IFSHIFT, IFID, CNT) \
84 DEPARSER_DIGEST(Tofino, GRESS, NAME, CNT, IFSHIFT(can_shift = true;)) { \
85 CFG.phv = data.select->reg.deparser_id(); \
86 IFSHIFT(CFG.shft = data.shift + data.select->lo;) \
87 CFG.valid = 1; \
88 if (!data.select.pov.empty()) \
89 error(data.select.pov.front().lineno, "No POV bit support in tofino %s digest", \
90 #NAME); \
91 for (auto &set : data.layout) { \
92 int id = set.first >> data.shift; \
93 unsigned idx = 0; \
94 bool first = true, ok = true; \
95 int last = -1; \
96 int maxidx = TBL[id].phvs.size() - 1; \
97 for (auto &reg : set.second) { \
98 if (first) { \
99 first = false; \
100 IFID(TBL[id].id_phv = reg->reg.deparser_id(); continue;) \
101 } \
102 /* The same 16b/32b container cannot appear consecutively, but 8b can. */ \
103 if (last == reg->reg.deparser_id() && reg->reg.size != 8) { \
104 error(data.lineno, "%s: %db container %s seen in consecutive locations", \
105 #NAME, reg->reg.size, reg->reg.name); \
106 continue; \
107 } \
108 for (int i = reg->reg.size / 8; i > 0; i--) { \
109 if (idx > maxidx) { \
110 error(data.lineno, "%s digest limited to %d bytes", #NAME, maxidx + 1); \
111 ok = false; \
112 break; \
113 } \
114 TBL[id].phvs[idx++] = reg->reg.deparser_id(); \
115 } \
116 last = reg->reg.deparser_id(); \
117 if (!ok) break; \
118 } \
119 TBL[id].valid = 1; \
120 TBL[id].len = idx; \
121 } \
122 }
123
124TOFINO_DIGEST(INGRESS, learning, regs.input.iir.ingr.learn_cfg, regs.input.iir.ingr.learn_tbl, NO,
125 NO, 8)
126TOFINO_DIGEST(INGRESS, mirror, regs.header.hir.main_i.mirror_cfg, regs.header.hir.main_i.mirror_tbl,
127 YES, YES, 8)
128TOFINO_DIGEST(EGRESS, mirror, regs.header.her.main_e.mirror_cfg, regs.header.her.main_e.mirror_tbl,
129 YES, YES, 8)
130TOFINO_DIGEST(INGRESS, resubmit, regs.input.iir.ingr.resub_cfg, regs.input.iir.ingr.resub_tbl, YES,
131 NO, 8)
132
133void tofino_field_dictionary(checked_array_base<fde_pov> &fde_control,
134 checked_array_base<fde_phv> &fde_data,
135 checked_array_base<ubits<8>> &pov_layout,
136 std::vector<Phv::Ref> &pov_order,
137 ordered_map<const Phv::Register *, unsigned> &reg_pov,
138 std::vector<Deparser::FDEntry> &dict, json::vector &fd_gress,
139 json::vector &fd_entries, gress_t gress) {
140 std::map<unsigned, unsigned> pov;
141 json::vector chunk_bytes;
142 json::vector fd_entry_chunk_bytes;
143 unsigned pov_byte = 0, pov_size = 0, total_headers = 0;
144 for (auto &ent : pov_order)
145 if (pov.count(ent->reg.deparser_id()) == 0) {
146 total_headers++;
147 pov[ent->reg.deparser_id()] = pov_size;
148 pov_size += ent->reg.size;
149 for (unsigned i = 0; i < ent->reg.size; i += 8) {
150 if (pov_byte >= Target::Tofino::DEPARSER_MAX_POV_BYTES) {
151 error(ent.lineno,
152 "Exceeded hardware limit for POV bits (%d) in deparser. "
153 "Using %d or more headers. Please reduce the number of headers",
154 Target::Tofino::DEPARSER_MAX_POV_BYTES * 8, total_headers);
155 return;
156 }
157 pov_layout[pov_byte++] = ent->reg.deparser_id();
158 }
159 }
160 while (pov_byte < Target::Tofino::DEPARSER_MAX_POV_BYTES) pov_layout[pov_byte++] = 0xff;
161
162 int row = -1, prev = -1, prev_pov = -1;
163 bool prev_is_checksum = false;
164 unsigned pos = 0;
165 unsigned total_bytes = 0;
166 int prev_row = 0;
167 for (auto &ent : dict) {
168 unsigned size = ent.what->size();
169 total_bytes += size;
170 int pov_bit = pov[ent.pov.front()->reg.deparser_id()] + ent.pov.front()->lo;
171
172 if (options.match_compiler) {
173 if (ent.what->is<Deparser::FDEntry::Checksum>()) {
174 /* checksum unit -- make sure it gets its own dictionary line */
175 prev_pov = -1;
176 prev_is_checksum = true;
177 } else {
178 if (prev_is_checksum) prev_pov = -1;
179 prev_is_checksum = false;
180 }
181 }
182
183 if (ent.what->is<Deparser::FDEntry::Phv>() && prev_pov == pov_bit &&
184 int(ent.what->encode()) == prev && ent.what->size() & 6)
185 error(ent.lineno, "16 and 32-bit container cannot be repeatedly deparsed");
186 while (size--) {
187 if (pov_bit != prev_pov || pos >= 4 /*|| (pos & (size-1)) != 0*/) {
188 if (row >= 0) {
189 fde_control[row].num_bytes = pos & 3;
190 fde_data[row].num_bytes = pos & 3;
191 }
192 // Entries used - (192 each in INGRESS & EGRESS for Tofino)
193 if (++row >= Target::Tofino::DEPARSER_MAX_FD_ENTRIES) {
194 error(ent.lineno,
195 "Exceeded hardware limit for "
196 "deparser field dictionary entries (%d). Using %d headers and %" PRIu64
197 " containers. Please reduce the number of headers and/or their length.",
198 Target::Tofino::DEPARSER_MAX_FD_ENTRIES, total_headers,
199 uint64_t(dict.size()));
200 return;
201 }
202 fde_control[row].pov_sel = pov_bit;
203 fde_control[row].version = 0xf;
204 fde_control[row].valid = 1;
205 pos = 0;
206 }
207 if (prev_row != row) {
208 json::map fd;
209 json::map fd_entry;
210 fd["Field Dictionary Number"] = prev_row;
211 fd_entry["entry"] = prev_row;
212 auto prevPovReg = Phv::reg(pov_layout[fde_control[prev_row].pov_sel.value / 8]);
213 auto prevPovBit = fde_control[prev_row].pov_sel.value;
214 auto prevPovOffset = prevPovBit - reg_pov[prevPovReg];
215 Deparser::write_pov_in_json(fd, fd_entry, prevPovReg, prevPovBit, prevPovOffset);
216 fd["Content"] = std::move(chunk_bytes);
217 fd_entry["chunks"] = std::move(fd_entry_chunk_bytes);
218 fd_gress.push_back(std::move(fd));
219 fd_entries.push_back(std::move(fd_entry));
220 prev_row = row;
221 }
222 auto povReg = Phv::reg(pov_layout[fde_control[row].pov_sel.value / 8]);
223 auto povBit = fde_control[row].pov_sel.value % povReg->size;
224 json::map chunk_byte;
225 json::map fd_entry_chunk_byte;
226 json::map fd_entry_chunk;
227 chunk_byte["Byte"] = pos;
228 fd_entry_chunk_byte["chunk_number"] = pos;
229 auto phvReg = Phv::reg(ent.what->encode());
230 if (ent.what->encode() < CHECKSUM_ENGINE_PHVID_TOFINO_LOW ||
231 ent.what->encode() > CHECKSUM_ENGINE_PHVID_TOFINO_HIGH) {
232 write_field_name_in_json(phvReg, povReg, povBit, chunk_byte, fd_entry_chunk, 11,
233 gress);
234 } else {
235 write_csum_const_in_json(ent.what->encode(), chunk_byte, fd_entry_chunk, gress);
236 }
237 fd_entry_chunk_byte["chunk"] = std::move(fd_entry_chunk);
238 chunk_bytes.push_back(std::move(chunk_byte.clone()));
239 fd_entry_chunk_bytes.push_back(std::move(fd_entry_chunk_byte.clone()));
240 fde_data[row].phv[pos++] = ent.what->encode();
241 prev_pov = pov_bit;
242 }
243
244 prev = ent.what->encode();
245 }
246 if (pos) {
247 fde_control[row].num_bytes = pos & 3;
248 fde_data[row].num_bytes = pos & 3;
249 }
250
251 // Compute average occupancy. For deparser FDE compression to work,
252 // need to make sure have certain average occupancy.
253 // This error check may still be too high level. I think it needs a finer granularity,
254 // but I'm not sure how to model the allowed variability of packet headers.
255
256 // Tofino deparser has a maximum output header size of 480 bytes. This is done in 2 phases.
257 // Each phase can do 240 bytes, corresponding to 18 QFDEs (4 * 18 * 4 bytes = 288 bytes)
258 // This means that average occupancy must be better than 240 / 288 bytes, or roughly 83%.
259 // This is the value we will check.
260 // We gate the check on total bytes occupied being greater than 64 bytes in an attempt
261 // to consider the QFDE constraint that it can only drive four stage 2 buses for compression.
262
263 unsigned max_bytes_for_rows_occupied = 4 * (row + 1);
264 double occupancy = 0.0;
265
266 if (max_bytes_for_rows_occupied > 0)
267 occupancy =
268 static_cast<double>(total_bytes) / static_cast<double>(max_bytes_for_rows_occupied);
269
270 if (total_bytes > 64 && occupancy < (240.0 / 288.0)) {
271 std::stringstream warn_msg;
272 warn_msg.precision(4);
273 warn_msg << "Deparser field dictionary occupancy is too sparse.";
274 warn_msg << "\nHardware requires an occupancy of " << 100.0 * 240.0 / 288.0
275 << " to deparse the output header,";
276 warn_msg << "\nbut the PHV layout for the header structures was such that"
277 " the occupancy was only "
278 << 100.0 * occupancy << ".";
279 warn_msg << "\nThis situation is usually caused by a program that has one or"
280 " more of the following requirements:";
281 warn_msg << "\n 1. many 'short' headers that are not guaranteed to coexist"
282 " (e.g. less than 4 bytes)";
283 warn_msg << "\n 2. many packet headers that are not multiples of 4 bytes";
284 warn_msg << "\n 3. many conditionally updated checksums";
285 warning(0, "%s", warn_msg.str().c_str());
286 }
287}
288
289template <typename IN_GRP, typename IN_SPLIT, typename EG_GRP, typename EG_SPLIT>
290void tofino_phv_ownership(bitvec phv_use[2], IN_GRP &in_grp, IN_SPLIT &in_split, EG_GRP &eg_grp,
291 EG_SPLIT &eg_split, unsigned first, unsigned count) {
292 BUG_CHECK(in_grp.val.size() == eg_grp.val.size(), "in_grp and eg_grp must have same size");
293 BUG_CHECK(in_split.val.size() == eg_split.val.size(),
294 "in_split and eg_split must have same size");
295 BUG_CHECK((in_grp.val.size() + 1) * in_split.val.size() == count,
296 "in_grp and in_split must have same size");
297 unsigned group_size = in_split.val.size();
298 // DANGER -- this only works because tofino Phv::Register uids happend to match
299 // DANGER -- the deparser encoding of phv containers.
300 unsigned reg = first;
301 for (unsigned i = 0; i < in_grp.val.size(); i++, reg += group_size) {
302 unsigned last = reg + group_size - 1;
303 int count = 0;
304 if (phv_use[INGRESS].getrange(reg, group_size)) {
305 in_grp.val |= 1U << i;
306 if (i * group_size >= 16 && i * group_size < 32)
307 error(0, "%s..%s(R%d..R%d) used by ingress deparser but only available to egress",
308 Phv::reg(reg)->name, Phv::reg(last)->name, reg, last);
309 else
310 count++;
311 }
312 if (phv_use[EGRESS].getrange(reg, group_size)) {
313 eg_grp.val |= 1U << i;
314 if (i * group_size < 16)
315 error(0, "%s..%s(R%d..R%d) used by egress deparser but only available to ingress",
316 Phv::reg(reg)->name, Phv::reg(last)->name, reg, last);
317 else
318 count++;
319 }
320 if (count > 1)
321 error(0, "%s..%s(R%d..R%d) used by both ingress and egress deparser",
322 Phv::reg(reg)->name, Phv::reg(last)->name, reg, last);
323 }
324 in_split.val = phv_use[INGRESS].getrange(reg, group_size);
325 eg_split.val = phv_use[EGRESS].getrange(reg, group_size);
326}
327
328static short tofino_phv2cksum[Target::Tofino::Phv::NUM_PHV_REGS][2] = {
329 // normal {LSWord, MSWord}
330 {287, 286},
331 {283, 282},
332 {279, 278},
333 {275, 274},
334 {271, 270},
335 {267, 266},
336 {263, 262},
337 {259, 258},
338 {255, 254},
339 {251, 250},
340 {247, 246},
341 {243, 242},
342 {239, 238},
343 {235, 234},
344 {231, 230},
345 {227, 226},
346 {223, 222},
347 {219, 218},
348 {215, 214},
349 {211, 210},
350 {207, 206},
351 {203, 202},
352 {199, 198},
353 {195, 194},
354 {191, 190},
355 {187, 186},
356 {183, 182},
357 {179, 178},
358 {175, 174},
359 {171, 170},
360 {167, 166},
361 {163, 162},
362 {285, 284},
363 {281, 280},
364 {277, 276},
365 {273, 272},
366 {269, 268},
367 {265, 264},
368 {261, 260},
369 {257, 256},
370 {253, 252},
371 {249, 248},
372 {245, 244},
373 {241, 240},
374 {237, 236},
375 {233, 232},
376 {229, 228},
377 {225, 224},
378 {221, 220},
379 {217, 216},
380 {213, 212},
381 {209, 208},
382 {205, 204},
383 {201, 200},
384 {197, 196},
385 {193, 192},
386 {189, 188},
387 {185, 184},
388 {181, 180},
389 {177, 176},
390 {173, 172},
391 {169, 168},
392 {165, 164},
393 {161, 160},
394 {147, -1},
395 {145, -1},
396 {143, -1},
397 {141, -1},
398 {127, -1},
399 {125, -1},
400 {123, -1},
401 {121, -1},
402 {107, -1},
403 {105, -1},
404 {103, -1},
405 {101, -1},
406 {87, -1},
407 {85, -1},
408 {83, -1},
409 {81, -1},
410 {67, -1},
411 {65, -1},
412 {63, -1},
413 {61, -1},
414 {47, -1},
415 {45, -1},
416 {43, -1},
417 {41, -1},
418 {27, -1},
419 {25, -1},
420 {23, -1},
421 {21, -1},
422 {7, -1},
423 {5, -1},
424 {3, -1},
425 {1, -1},
426 {146, -1},
427 {144, -1},
428 {142, -1},
429 {140, -1},
430 {126, -1},
431 {124, -1},
432 {122, -1},
433 {120, -1},
434 {106, -1},
435 {104, -1},
436 {102, -1},
437 {100, -1},
438 {86, -1},
439 {84, -1},
440 {82, -1},
441 {80, -1},
442 {66, -1},
443 {64, -1},
444 {62, -1},
445 {60, -1},
446 {46, -1},
447 {44, -1},
448 {42, -1},
449 {40, -1},
450 {26, -1},
451 {24, -1},
452 {22, -1},
453 {20, -1},
454 {6, -1},
455 {4, -1},
456 {2, -1},
457 {0, -1},
458 {159, -1},
459 {157, -1},
460 {155, -1},
461 {153, -1},
462 {151, -1},
463 {149, -1},
464 {139, -1},
465 {137, -1},
466 {135, -1},
467 {133, -1},
468 {131, -1},
469 {129, -1},
470 {119, -1},
471 {117, -1},
472 {115, -1},
473 {113, -1},
474 {111, -1},
475 {109, -1},
476 {99, -1},
477 {97, -1},
478 {95, -1},
479 {93, -1},
480 {91, -1},
481 {89, -1},
482 {79, -1},
483 {77, -1},
484 {75, -1},
485 {73, -1},
486 {71, -1},
487 {69, -1},
488 {59, -1},
489 {57, -1},
490 {55, -1},
491 {53, -1},
492 {51, -1},
493 {49, -1},
494 {39, -1},
495 {37, -1},
496 {35, -1},
497 {33, -1},
498 {31, -1},
499 {29, -1},
500 {19, -1},
501 {17, -1},
502 {15, -1},
503 {13, -1},
504 {11, -1},
505 {9, -1},
506 {158, -1},
507 {156, -1},
508 {154, -1},
509 {152, -1},
510 {150, -1},
511 {148, -1},
512 {138, -1},
513 {136, -1},
514 {134, -1},
515 {132, -1},
516 {130, -1},
517 {128, -1},
518 {118, -1},
519 {116, -1},
520 {114, -1},
521 {112, -1},
522 {110, -1},
523 {108, -1},
524 {98, -1},
525 {96, -1},
526 {94, -1},
527 {92, -1},
528 {90, -1},
529 {88, -1},
530 {78, -1},
531 {76, -1},
532 {74, -1},
533 {72, -1},
534 {70, -1},
535 {68, -1},
536 {58, -1},
537 {56, -1},
538 {54, -1},
539 {52, -1},
540 {50, -1},
541 {48, -1},
542 {38, -1},
543 {36, -1},
544 {34, -1},
545 {32, -1},
546 {30, -1},
547 {28, -1},
548 {18, -1},
549 {16, -1},
550 {14, -1},
551 {12, -1},
552 {10, -1},
553 {8, -1},
554
555 {-1, -1},
556 {-1, -1},
557 {-1, -1},
558 {-1, -1},
559 {-1, -1},
560 {-1, -1},
561 {-1, -1},
562 {-1, -1},
563 {-1, -1},
564 {-1, -1},
565 {-1, -1},
566 {-1, -1},
567 {-1, -1},
568 {-1, -1},
569 {-1, -1},
570 {-1, -1},
571 {-1, -1},
572 {-1, -1},
573 {-1, -1},
574 {-1, -1},
575 {-1, -1},
576 {-1, -1},
577 {-1, -1},
578 {-1, -1},
579 {-1, -1},
580 {-1, -1},
581 {-1, -1},
582 {-1, -1},
583 {-1, -1},
584 {-1, -1},
585 {-1, -1},
586 {-1, -1},
587
588 // tagalong {LSWord, MSWord}
589 {1, 0},
590 {3, 2},
591 {5, 4},
592 {7, 6},
593 {9, 8},
594 {11, 10},
595 {13, 12},
596 {15, 14},
597 {17, 16},
598 {19, 18},
599 {21, 20},
600 {23, 22},
601 {25, 24},
602 {27, 26},
603 {29, 28},
604 {31, 30},
605 {33, 32},
606 {35, 34},
607 {37, 36},
608 {39, 38},
609 {41, 40},
610 {43, 42},
611 {45, 44},
612 {47, 46},
613 {49, 48},
614 {51, 50},
615 {53, 52},
616 {55, 54},
617 {57, 56},
618 {59, 58},
619 {61, 60},
620 {63, 62},
621 {64, -1},
622 {65, -1},
623 {66, -1},
624 {67, -1},
625 {68, -1},
626 {69, -1},
627 {70, -1},
628 {71, -1},
629 {72, -1},
630 {73, -1},
631 {74, -1},
632 {75, -1},
633 {76, -1},
634 {77, -1},
635 {78, -1},
636 {79, -1},
637 {80, -1},
638 {81, -1},
639 {82, -1},
640 {83, -1},
641 {84, -1},
642 {85, -1},
643 {86, -1},
644 {87, -1},
645 {88, -1},
646 {89, -1},
647 {90, -1},
648 {91, -1},
649 {92, -1},
650 {93, -1},
651 {94, -1},
652 {95, -1},
653 {96, -1},
654 {97, -1},
655 {98, -1},
656 {99, -1},
657 {100, -1},
658 {101, -1},
659 {102, -1},
660 {103, -1},
661 {104, -1},
662 {105, -1},
663 {106, -1},
664 {107, -1},
665 {108, -1},
666 {109, -1},
667 {110, -1},
668 {111, -1},
669 {112, -1},
670 {113, -1},
671 {114, -1},
672 {115, -1},
673 {116, -1},
674 {117, -1},
675 {118, -1},
676 {119, -1},
677 {120, -1},
678 {121, -1},
679 {122, -1},
680 {123, -1},
681 {124, -1},
682 {125, -1},
683 {126, -1},
684 {127, -1},
685 {128, -1},
686 {129, -1},
687 {130, -1},
688 {131, -1},
689 {132, -1},
690 {133, -1},
691 {134, -1},
692 {135, -1},
693 {136, -1},
694 {137, -1},
695 {138, -1},
696 {139, -1},
697 {140, -1},
698 {141, -1},
699 {142, -1},
700 {143, -1}};
701
702#define TAGALONG_THREAD_BASE \
703 (Target::Tofino::Phv::COUNT_8BIT_TPHV + Target::Tofino::Phv::COUNT_16BIT_TPHV + \
704 2 * Target::Tofino::Phv::COUNT_32BIT_TPHV)
705
706template <typename DTYPE, typename STYPE>
707static void copy_csum_cfg_entry(DTYPE &dst_unit, STYPE &src_unit) {
708 BUG_CHECK(dst_unit.size() == src_unit.size(), "dst_unit and src_unit have different sizes");
709
710 for (unsigned i = 0; i < dst_unit.size(); i++) {
711 auto &src = src_unit[i];
712 auto &dst = dst_unit[i];
713
714 dst.zero_l_s_b = src.zero_l_s_b;
715 dst.zero_m_s_b = src.zero_m_s_b;
716 dst.swap = src.swap;
717 }
718}
719
720template <class ENTRIES>
721static void init_tofino_checksum_entry(ENTRIES &entry) {
722 entry.zero_l_s_b = 1;
723 entry.zero_l_s_b.rewrite();
724 entry.zero_m_s_b = 1;
725 entry.zero_m_s_b.rewrite();
726 entry.swap = 0;
727 entry.swap.rewrite();
728}
729
730template <typename IPO, typename HPO>
731static void tofino_checksum_units(checked_array_base<IPO> &main_csum_units,
732 checked_array_base<HPO> &tagalong_csum_units, gress_t gress,
733 Deparser::FullChecksumUnit checksum_unit[]) {
734 BUG_CHECK(tofino_phv2cksum[Target::Tofino::Phv::NUM_PHV_REGS - 1][0] == 143,
735 "invalid phv2cksum");
736 for (int i = 0; i < Target::Tofino::DEPARSER_CHECKSUM_UNITS; i++) {
737 auto &main_unit = main_csum_units[i].csum_cfg_entry;
738 auto &tagalong_unit = tagalong_csum_units[i].csum_cfg_entry;
739 auto &tagalong_unit_zeros_as_ones = tagalong_csum_units[i].zeros_as_ones;
740 for (auto &ent : main_unit) init_tofino_checksum_entry(ent);
741 for (auto &ent : tagalong_unit) init_tofino_checksum_entry(ent);
742 if (checksum_unit[i].entries.empty()) continue;
743 // Tofino does not support checksum calculation using multiple
744 // partial checksum unit.
745 // Full checksum unit and partial checksum unit will always be same
746 BUG_CHECK(checksum_unit[i].entries.size() == 1,
747 "multiple partial checksum unit not supported");
748 auto &checksum_unit_entries = checksum_unit[i].entries[i];
749 for (auto &reg : checksum_unit_entries) {
750 int mask = reg.mask;
751 int swap = reg.swap;
752 int idx = reg->reg.deparser_id();
753 if (!reg.pov.empty())
754 error(reg.pov.front().lineno, "No POV support in tofino checksum");
755 auto cksum_idx0 = tofino_phv2cksum[idx][0];
756 auto cksum_idx1 = tofino_phv2cksum[idx][1];
757 BUG_CHECK(cksum_idx0 >= 0, "invalid phv2cksum");
758 if (idx >= 256) {
759 write_checksum_entry(tagalong_unit[cksum_idx0], mask & 3, swap & 1, i,
760 reg->reg.name);
761 if (cksum_idx1 >= 0)
762 write_checksum_entry(tagalong_unit[cksum_idx1], mask >> 2, swap >> 1, i,
763 reg->reg.name);
764 else
765 BUG_CHECK((mask >> 2 == 0) && (swap >> 1 == 0), "invalid phv2cksum");
766 } else {
767 write_checksum_entry(main_unit[cksum_idx0], mask & 3, swap & 1, i, reg->reg.name);
768 if (cksum_idx1 >= 0)
769 write_checksum_entry(main_unit[cksum_idx1], mask >> 2, swap >> 1, i,
770 reg->reg.name);
771 else
772 BUG_CHECK((mask >> 2 == 0) && (swap >> 1 == 0), "invalid phv2cksum");
773 }
774 }
775 // Thread non-tagalong checksum results through the tagalong unit
776 int idx = i + TAGALONG_THREAD_BASE + gress * Target::Tofino::DEPARSER_CHECKSUM_UNITS;
777 write_checksum_entry(tagalong_unit[idx], 0x3, 0x0, i);
778 // Setting Zeros_As_Ones enable
779 tagalong_unit_zeros_as_ones.en = checksum_unit[i].zeros_as_ones_en;
780 main_unit.set_modified();
781 tagalong_unit.set_modified();
782 }
783}
784
785static void tofino_checksum_units(
786 Target::Tofino::deparser_regs &regs,
787 Deparser::FullChecksumUnit full_checksum_unit[2][MAX_DEPARSER_CHECKSUM_UNITS]) {
788 for (unsigned id = 2; id < MAX_DEPARSER_CHECKSUM_UNITS; id++) {
789 if (!full_checksum_unit[0][id].entries.empty() &&
790 !full_checksum_unit[1][id].entries.empty())
791 error(-1, "deparser checksum unit %d used in both ingress and egress", id);
792 }
793
794 tofino_checksum_units(regs.input.iim.ii_phv_csum.csum_cfg,
795 regs.header.him.hi_tphv_csum.csum_cfg, INGRESS,
796 full_checksum_unit[INGRESS]);
797 tofino_checksum_units(regs.input.iem.ie_phv_csum.csum_cfg,
798 regs.header.hem.he_tphv_csum.csum_cfg, EGRESS,
799 full_checksum_unit[EGRESS]);
800
801 // make sure shared units are configured identically
802 for (unsigned id = 2; id < Target::Tofino::DEPARSER_CHECKSUM_UNITS; id++) {
803 auto &eg_main_unit = regs.input.iem.ie_phv_csum.csum_cfg[id].csum_cfg_entry;
804 auto &ig_main_unit = regs.input.iim.ii_phv_csum.csum_cfg[id].csum_cfg_entry;
805
806 auto &eg_tphv_unit = regs.header.hem.he_tphv_csum.csum_cfg[id].csum_cfg_entry;
807 auto &ig_tphv_unit = regs.header.him.hi_tphv_csum.csum_cfg[id].csum_cfg_entry;
808
809 if (!full_checksum_unit[0][id].entries.empty()) {
810 copy_csum_cfg_entry(eg_main_unit, ig_main_unit);
811 copy_csum_cfg_entry(eg_tphv_unit, ig_tphv_unit);
812 } else if (!full_checksum_unit[1][id].entries.empty()) {
813 copy_csum_cfg_entry(ig_main_unit, eg_main_unit);
814 copy_csum_cfg_entry(ig_tphv_unit, eg_tphv_unit);
815 }
816 }
817}
818
819template <>
820void Deparser::write_config(Target::Tofino::deparser_regs &regs) {
821 regs.input.icr.inp_cfg.disable();
822 regs.input.icr.intr.disable();
823 regs.header.hem.he_edf_cfg.disable();
824 regs.header.him.hi_edf_cfg.disable();
825
826 tofino_checksum_units(regs, full_checksum_unit);
827 json::map field_dictionary_alloc;
828 json::vector fd_gress;
829 json::vector fde_entries_i;
830 json::vector fde_entries_e;
831
832 // Deparser resources
833 json::vector resources_deparser;
834
835 // Create field dictionaries for ingress
836 tofino_field_dictionary(regs.input.iim.ii_fde_pov.fde_pov, regs.header.him.hi_fde_phv.fde_phv,
837 regs.input.iir.main_i.pov.phvs, pov_order[INGRESS], pov[INGRESS],
838 dictionary[INGRESS], fd_gress, fde_entries_i, INGRESS);
839 field_dictionary_alloc["ingress"] = std::move(fd_gress);
840 // Create field dictionaries for egress
841 tofino_field_dictionary(regs.input.iem.ie_fde_pov.fde_pov, regs.header.hem.he_fde_phv.fde_phv,
842 regs.input.ier.main_e.pov.phvs, pov_order[EGRESS], pov[EGRESS],
843 dictionary[EGRESS], fd_gress, fde_entries_e, EGRESS);
844 field_dictionary_alloc["egress"] = std::move(fd_gress);
845
846 if (Log::verbosity() > 0) {
847 auto json_dump = open_output("logs/field_dictionary.log");
848 *json_dump << &field_dictionary_alloc;
849 }
850 // Output deparser resources
851 report_resources_deparser_json(fde_entries_i, fde_entries_e);
852
853 if (Phv::use(INGRESS).intersects(Phv::use(EGRESS))) {
854 warning(lineno[INGRESS], "Registers used in both ingress and egress in pipeline: %s",
855 Phv::db_regset(Phv::use(INGRESS) & Phv::use(EGRESS)).c_str());
856 /* FIXME -- this only (sort-of) works because 'deparser' comes first in the alphabet,
857 * FIXME -- so is the first section to have its 'output' method run. Its a hack
858 * FIXME -- anyways to attempt to correct broken asm that should be an error */
859 Phv::unsetuse(INGRESS, phv_use[EGRESS]);
860 Phv::unsetuse(EGRESS, phv_use[INGRESS]);
861 }
862
863 tofino_phv_ownership(phv_use, regs.input.iir.ingr.phv8_grp, regs.input.iir.ingr.phv8_split,
864 regs.input.ier.egr.phv8_grp, regs.input.ier.egr.phv8_split,
865 Target::Tofino::Phv::FIRST_8BIT_PHV, Target::Tofino::Phv::COUNT_8BIT_PHV);
866 tofino_phv_ownership(phv_use, regs.input.iir.ingr.phv16_grp, regs.input.iir.ingr.phv16_split,
867 regs.input.ier.egr.phv16_grp, regs.input.ier.egr.phv16_split,
868 Target::Tofino::Phv::FIRST_16BIT_PHV,
869 Target::Tofino::Phv::COUNT_16BIT_PHV);
870 tofino_phv_ownership(phv_use, regs.input.iir.ingr.phv32_grp, regs.input.iir.ingr.phv32_split,
871 regs.input.ier.egr.phv32_grp, regs.input.ier.egr.phv32_split,
872 Target::Tofino::Phv::FIRST_32BIT_PHV,
873 Target::Tofino::Phv::COUNT_32BIT_PHV);
874
875 for (unsigned i = 0; i < 8; i++) {
876 if (phv_use[EGRESS].intersects(Target::Tofino::Phv::tagalong_groups[i])) {
877 regs.input.icr.tphv_cfg.i_e_assign |= 1 << i;
878 if (phv_use[INGRESS].intersects(Target::Tofino::Phv::tagalong_groups[i])) {
879 error(lineno[INGRESS],
880 "tagalong group %d used in both ingress and "
881 "egress deparser",
882 i);
883 }
884 }
885 }
886
887 for (auto &intrin : intrinsics) intrin.type->setregs(regs, *this, intrin);
888
889 if (!regs.header.hir.ingr.ingress_port.sel.modified())
890 regs.header.hir.ingr.ingress_port.sel = 1;
891
892 for (auto &digest : digests) digest.type->setregs(regs, *this, digest);
893
894 // The csum_cfg_entry registers are NOT reset by hardware and must be
895 // explicitly configured. We remove the disable_if_reset_value() calls on
896 // these register tree for now, but ideally they should have a flag to indicate no
897 // reset value is present and the register tree should prune only those regs
898 // if (options.condense_json) {
899 // regs.input.disable_if_reset_value();
900 // regs.header.disable_if_reset_value(); }
901 if (error_count == 0 && options.gen_json) {
902 regs.input.emit_json(*open_output("regs.all.deparser.input_phase.cfg.json"));
903 regs.header.emit_json(*open_output("regs.all.deparser.header_phase.cfg.json"));
904 }
905 TopLevel::regs<Target::Tofino>()->reg_pipe.deparser.hdr.set("regs.all.deparser.header_phase",
906 &regs.header);
907 TopLevel::regs<Target::Tofino>()->reg_pipe.deparser.inp.set("regs.all.deparser.input_phase",
908 &regs.input);
909}
910
911template <>
912unsigned Deparser::FDEntry::Checksum::encode<Target::Tofino>() {
913 return CHECKSUM_ENGINE_PHVID_TOFINO_LOW + (gress * CHECKSUM_ENGINE_PHVID_TOFINO_PER_GRESS) +
914 unit;
915}
916
917template <>
918unsigned Deparser::FDEntry::Constant::encode<Target::Tofino>() {
919 error(lineno, "Tofino deparser does not support constant entries");
920 return -1;
921}
922
923template <>
924void Deparser::gen_learn_quanta(Target::Tofino::parser_regs &regs, json::vector &learn_quanta) {}
925
926template <>
927void Deparser::process(Target::Tofino *) {
928 // Chip-specific code for process method
929 // None for Tofino
930}
Definition bf-asm/phv.h:32
Definition checked_array.h:34
Definition backends/tofino/bf-asm/json.h:300
Definition backends/tofino/bf-asm/json.h:222
void process()
optionally process the data if not done during parsing
Definition tofino/bf-asm/deparser.cpp:654
Definition tofino/bf-asm/deparser.h:40
Definition tofino/bf-asm/deparser.h:137
void warning(const char *format, Args &&...args)
Report a warning with the given message.
Definition lib/error.h:128
void error(const char *format, Args &&...args)
Report an error with the given message.
Definition lib/error.h:58
Definition tofino/action_data_bus.cpp:28
Definition bson.cpp:69
STL namespace.
Definition tofino/bf-asm/deparser.cpp:70
Definition tofino/bf-asm/deparser.cpp:53
Definition ubits.h:82