I wrote a Tofino P4 program, and I can successfully compile it with open-p4studio.
#include <core.p4>
#if __TARGET_TOFINO__ == 3
#include <t3na.p4>
#elif __TARGET_TOFINO__ == 2
#include <t2na.p4>
#else
#include <tna.p4>
#endif
#include "common/headers.p4"
#include "common/util.p4"
struct headers_t {
ethernet_h ethernet;
}
struct metadata_t {
bit<32> qlen;
}
parser SwitchIngressParser(
packet_in pkt,
out headers_t hdr,
out metadata_t ig_md,
out ingress_intrinsic_metadata_t ig_intr_md) {
TofinoIngressParser() tofino_parser;
state start {
tofino_parser.apply(pkt, ig_intr_md);
transition parse_ethernet;
}
state parse_ethernet {
pkt.extract(hdr.ethernet);
transition accept;
}
}
control SwitchIngressDeparser(
packet_out pkt,
inout headers_t hdr,
in metadata_t ig_md,
in ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md) {
apply {
pkt.emit(hdr);
}
}
control SwitchIngress(
inout headers_t hdr,
inout metadata_t ig_md,
in ingress_intrinsic_metadata_t ig_intr_md,
in ingress_intrinsic_metadata_from_parser_t ig_prsr_md,
inout ingress_intrinsic_metadata_for_deparser_t ig_dprsr_md,
inout ingress_intrinsic_metadata_for_tm_t ig_tm_md) {
Register<bit<32>, PortId_t>(size=32w512, initial_value=0) cnt;
RegisterAction<bit<32>, PortId_t, void>(cnt) set = {
void apply(inout bit<32> value) {
value = 32w2222;
}
};
RegisterAction<bit<32>, PortId_t, bit<32>>(cnt) read = {
void apply(inout bit<32> value, out bit<32> read_value) {
read_value = value;
}
};
Counter<bit<32>, bit<2>>(2, CounterType_t.PACKETS_AND_BYTES) counter;
Counter<bit<32>, bit<2>>(2, CounterType_t.PACKETS_AND_BYTES) test;
Counter<bit<32>, bit<2>>(2, CounterType_t.PACKETS_AND_BYTES) test_2;
action do_write(PortId_t port) {
test.count(0);
set.execute(port);
}
action miss() {
test.count(1);
}
table fwd_write {
key = {
hdr.ethernet.dst_addr : ternary;
}
actions = {
do_write;
miss;
}
size = 512;
}
action do_read(PortId_t port) {
test_2.count(0);
ig_md.qlen = read.execute(port);
}
action miss_2() {
test_2.count(1);
}
table fwd_read {
key = {
hdr.ethernet.dst_addr : ternary;
}
actions = {
do_read;
miss_2;
NoAction;
}
size = 512;
}
apply {
fwd_write.apply();
fwd_read.apply();
if (ig_md.qlen > 0) {
counter.count(0);
} else {
counter.count(1);
}
}
}
Pipeline(SwitchIngressParser(),
SwitchIngress(),
SwitchIngressDeparser(),
EmptyEgressParser(),
EmptyEgress(),
EmptyEgressDeparser()) pipe;
Switch(pipe) main;
And I wrote a test script with Python.
import logging
import random
import time
from ptf import config
import ptf.testutils as testutils
import ptf.packet as packet
from p4testutils.misc_utils import *
from bfruntime_client_base_tests import BfRuntimeTest
import bfrt_grpc.client as gc
import bfrt_grpc.bfruntime_pb2 as bfruntime_pb2
##### Required for Thrift #####
import pd_base_tests
##### ******************* #####
pkt_len = int(test_param_get('pkt_size'))
logger = get_logger()
swports = get_sw_ports()
dev_id = 0
g_is_tofino = testutils.test_param_get('arch') == 'tofino'
g_is_tofino2 = testutils.test_param_get('arch') == 'tofino2'
assert g_is_tofino or g_is_tofino2
g_is_hw = testutils.test_param_get('target') == 'hw'
g_is_model = not g_is_hw
def get_pipes(bfrt_info):
trgt = gc.Target(device_id=dev_id)
t = bfrt_info.table_get('device_configuration')
resp = t.default_entry_get(trgt)
data,_ = next(resp)
data_dict = data.to_dict()
num_pipes = data_dict['num_pipes']
return list(range(num_pipes))
def _forward_table_add(table, target, dmac, dmac_mask, priority, port, func):
table.entry_add(
target,
[table.make_key(
[gc.KeyTuple('hdr.ethernet.dst_addr', dmac, dmac_mask),
gc.KeyTuple('$MATCH_PRIORITY', priority)])],
[table.make_data(
[gc.DataTuple('port', port)],
func)])
class DirectCounterTest(BfRuntimeTest):
"""@brief Simple test of the direct counter
"""
def setUp(self):
client_id = 0
p4_name = "my_test"
BfRuntimeTest.setUp(self, client_id, p4_name)
def runTest(self):
ok_port = []
for port in swports:
if port != 0:
ok_port.append(port)
assert len(ok_port) >= 3
print(f"swports: {swports}")
ig_port = ok_port[0]
eg_port = ok_port[1]
logger.info("Ingress port: %d, Egress port: %d", ig_port, eg_port)
smac = '11:33:55:77:99:00'
dmac_mask = 'ff:ff:ff:ff:ff:ff'
dmac = '00:11:22:33:44:55'
# Get bfrt_info and set it as part of the test
bfrt_info = self.interface.bfrt_info_get("my_test")
wt_table = bfrt_info.table_get("SwitchIngress.fwd_write")
wt_table.info.key_field_annotation_add("hdr.ethernet.dst_addr", "mac")
rd_table = bfrt_info.table_get("SwitchIngress.fwd_read")
rd_table.info.key_field_annotation_add("hdr.ethernet.dst_addr", "mac")
target = gc.Target(device_id=dev_id)
_forward_table_add(wt_table, target, dmac, dmac_mask, 0, eg_port, "SwitchIngress.do_write")
_forward_table_add(rd_table, target, dmac, dmac_mask, 0, eg_port, "SwitchIngress.do_read")
pkt = testutils.simple_tcp_packet(pktlen=pkt_len, eth_dst=dmac, eth_src=smac)
testutils.send_packet(self, ig_port, pkt)
I properly added the table items. And I expect that after sending a packet, the counter(0) is 1. However, after testing, I found that counter(1) is 1, and all of the items in cnt are 0. The result really confuses me. Would someone take a look at why this is happening? Thanks!