34 unsigned line_rate = 100;
36 double clock_rate = 1.22;
39 std::map<unsigned, unsigned> ipg = {{1, 8}, {25, 5}, {40, 1}, {50, 1}, {100, 1}};
42 unsigned target_min_packet_size = 8 + 60 + 4 + ipg[line_rate];
45 double target_min_avg_rate = line_rate * 1e9 / (8.0 * clock_rate * 1e9);
59 struct ExtractorUsage :
public ordered_map<PHV::Type, unsigned> {
63 if (count(type)) rv = (*this).at(type);
71 for (
auto use : *
this) {
72 if (use.first.size() == size) rv += use.second;
82 match_to_extractor_usage;
90 auto rv = Inspector::init_apply(root);
93 BUG_CHECK(!cgl.graphs().empty(),
"Parser IR hasn't been lowered yet?");
97 bool preorder(
const IR::BFN::LoweredParserState *state)
override {
98 auto parser = findContext<IR::BFN::LoweredParser>();
99 parser_to_states[parser].insert(state);
103 void get_extractor_usage(
const IR::BFN::LoweredParserMatch *match) {
104 auto rv =
new ExtractorUsage;
106 for (
auto stmt : match->extracts) {
107 if (
auto ep = stmt->to<IR::BFN::LoweredExtractPhv>()) rv->add(ep->dest->container);
110 match_to_extractor_usage[match] = rv;
113 void get_clot_usage(
const IR::BFN::LoweredParserMatch *match) {
114 for (
auto stmt : match->extracts) {
115 if (
auto ec = stmt->to<IR::BFN::LoweredExtractClot>())
116 match_to_clot_usage[match].insert(ec->dest->tag);
120 void get_checksum_usage(
const IR::BFN::LoweredParserMatch *match) {
121 for (
auto csum : match->checksums) match_to_checksum_usage[match].insert(csum->unit_id);
124 bool preorder(
const IR::BFN::LoweredParserMatch *match)
override {
125 auto state = findContext<IR::BFN::LoweredParserState>();
126 state_to_matches[state].insert(match);
127 match_to_state[match] = state;
129 get_extractor_usage(match);
130 get_clot_usage(match);
131 get_checksum_usage(match);
136 void print_state_usage(
const IR::BFN::LoweredParser *parser) {
137 if (!parser_to_states.count(parser))
return;
139 std::string total_extract_label =
"Total Extractors";
140 if (Device::currentDevice() != Device::TOFINO) total_extract_label +=
" (16-bit)";
144 {
"State",
"Match",
"8-bit",
"16-bit",
"32-bit", total_extract_label,
"Other"},
145 TablePrinter::Align::LEFT);
147 auto sorted = cgl.graphs().at(parser)->topological_sort();
149 for (
auto s : sorted) {
150 if (state_to_matches.count(s)) {
151 std::string state_label;
153 for (
auto m : state_to_matches.at(s)) {
154 auto &usage = match_to_extractor_usage.at(m);
155 auto state = match_to_state.at(m);
157 auto usage_8b = usage->get(PHV::Size::b8);
158 auto usage_16b = usage->get(PHV::Size::b16);
159 auto usage_32b = usage->get(PHV::Size::b32);
161 unsigned sausage = 0;
163 if (Device::currentDevice() == Device::TOFINO)
164 sausage = usage_8b + usage_16b + usage_32b;
165 else if (Device::currentDevice() != Device::TOFINO)
166 sausage = usage_8b + usage_16b + 2 * usage_32b;
168 if (state_label.empty())
169 state_label = std::string(state->name);
173 std::stringstream other_usage;
175 if (match_to_clot_usage.count(m)) {
176 other_usage <<
"clot ";
177 for (
auto c : match_to_clot_usage.at(m)) other_usage << c <<
" ";
180 if (match_to_checksum_usage.count(m)) {
181 other_usage <<
"csum ";
182 for (
auto c : match_to_checksum_usage.at(m)) other_usage << c <<
" ";
185 tp.addRow({state_label, std::string(m->value->toString()),
186 std::to_string(usage_8b), std::to_string(usage_16b),
187 std::to_string(usage_32b), std::to_string(sausage),
196 static double round(
double val,
int decimal_point) {
197 unsigned round_by = pow(10, decimal_point);
198 return (val * round_by) / round_by;
201 void print_timing_report(
const IR::BFN::LoweredParser *parser,
202 const std::vector<const IR::BFN::LoweredParserState *> &path) {
203 unsigned total_user_header_bits = 0;
205 for (
auto it = path.begin(); it != path.end(); it++) {
207 if ((*it)->name.startsWith(
"$"))
continue;
209 auto next = std::next(it);
211 if (next != path.end()) {
212 auto matches = cgl.graphs().at(parser)->transitions(*it, *next);
213 if (!matches.empty()) total_user_header_bits += (*matches.begin())->shift;
215 auto matches = cgl.graphs().at(parser)->to_pipe(*it);
216 if (!matches.empty()) total_user_header_bits += (*matches.begin())->shift;
220 if (total_user_header_bits < target_min_packet_size * 8)
221 total_user_header_bits = target_min_packet_size * 8;
223 double avg_rate = total_user_header_bits / (8.0 * path.size());
225 if (avg_rate < target_min_avg_rate) {
226 unsigned min_packet_size = std::max(
227 target_min_packet_size,
unsigned(ceil(target_min_avg_rate * (path.size()))));
229 unsigned min_payload_size = min_packet_size - total_user_header_bits / 8;
230 double max_data_rate = clock_rate * (total_user_header_bits / path.size());
231 double max_packet_rate = (1e3 * max_data_rate) / total_user_header_bits;
233 max_data_rate = round(max_data_rate, 2);
234 max_packet_rate = round(max_packet_rate, 2);
236 std::clog <<
"Average rate: " << avg_rate <<
" Bps" << std::endl;
238 std::clog <<
"Min packet size at " << line_rate <<
" Gbps: " << min_packet_size <<
" B"
239 <<
" (" << min_payload_size <<
" B payload)" << std::endl;
241 std::clog <<
"Max data rate for min-sized packets: " << max_data_rate <<
" Gbps / "
242 << max_packet_rate <<
" MPps" << std::endl;
244 std::clog <<
"Timing is met for min-sized packet (" << target_min_packet_size <<
" B)"
245 <<
" running at " << line_rate <<
" Gbps" << std::endl;
249 void print_parser_summary(
const IR::BFN::LoweredParser *parser) {
250 unsigned num_states = 0;
251 unsigned num_matches = 0;
253 if (parser_to_states.count(parser)) {
254 num_states = parser_to_states.at(parser).size();
256 for (
auto s : parser_to_states.at(parser)) {
257 if (state_to_matches.count(s)) num_matches += state_to_matches.at(s).size();
261 auto gress = parser->gress;
263 std::clog <<
"Number of states on " << gress <<
": " << num_states << std::endl;
265 std::clog << std::endl;
267 std::clog <<
"Number of matches on " << gress <<
": " << num_matches << std::endl;
269 std::clog << std::endl;
271 auto longest_path = cgl.graphs().at(parser)->longest_path_states(parser->start);
273 std::clog <<
"Longest path (" << longest_path.size() <<
" states) on " << gress <<
":"
276 for (
auto s : longest_path) std::clog <<
" " << s->name << std::endl;
278 std::clog << std::endl;
280 auto shortest_path = cgl.graphs().at(parser)->shortest_path_states(parser->start);
282 std::clog <<
"Shortest path (" << shortest_path.size() <<
" states) on " << gress <<
":"
285 for (
auto s : shortest_path) std::clog <<
" " << s->name << std::endl;
287 std::clog << std::endl;
289 print_timing_report(parser, longest_path);
291 std::clog << std::endl;
293 std::clog <<
"Extractor usage:" << std::endl;
295 print_state_usage(parser);
298 void end_apply(
const IR::Node *root)
override {
299 const IR::BFN::Pipe *pipe = root->to<IR::BFN::Pipe>();
300 if (BackendOptions().verbose > 0) {
303 std::clog <<
"Parser Characterization Report:" << std::endl;
305 for (
auto ps : parser_to_states) print_parser_summary(ps.first);