Data Types
// Basic data Types:
bool flag; // two values: true & false
int sigData; // signed integer
bit bitData; // unsigned int (bit-string)
varbit varData; // variable-size bit-str.
// typedef: introduces alternate type name
typedef bit<48> macAddr_t;
typedef bit<32> ip4Addr_t;
// const: constant value definition
const bit<16> IPV4_TYPE = 0x0800;
const bit<8> UDP_PROT = 0x11;
// headers: ordered collection of members
// operations test and set validity bits:
// isValid(), setValid(), setInvalid()
header ethernet_t {
macAddr_t dstAddr;
macAddr_t srcAddr;
bit<16> type;
}
// struct: unordered collection of members
// use to define header structures
struct headers_t {
ethernet_t ethernet;
vlan_t[2] vlan; // stack
}
// structs can also be used to define
// metadata structures
struct meta_out_t {
macAddr_t dstAddr;
bit<16> offset;
}
// error: contains opaque values that
// can be used to signal errors
error {
InvalidEthernetType,
IPv4VersionNotSupported,
}
Compiler Directives
// define macros (without arguments)
#define NULL 0
// undefine existing macros
#undef NULL
// conditional directives
#if #else #endif #ifdef #ifndef #elif
// include contents from other files
#include <file.p4> // can use quotes ‘”’
Statements and Expressions
// local metadata declaration
bit<16> tmp1, tmp2, tmp3, tmp4;
bit<16> tmp5 = 0; // default value
// variable assignment and member access
tmp1 = hdr.ethernet.type;
// bit slicing, concatenation (++)
tmp2 = tmp1[7:0] ++ tmp1[15:8];
// data type casting
tmp3 = (bit<16>)tmp2;
// arithmetic operators:
// +, -, *, <<, >>
tmp4 = 2*tmp1 + tmp3 - (bit<16>)tmp1[7:0];
// bitwise operators:
// and, or, not, &, |, ^, ~
tmp5 = (~tmp1 & tmp2) | (tmp3 ^ tmp4);
// conditional statement
// expression output requires a bool
// logical operators: ==, !=, >, >=, <, <=
if (next_hop == 0) {
metadata.next_hop = hdr.ipv4.dst;
} else {
metadata.next_hop = next_hop;
}
Tables
table ipv4_lpm {
// key match kinds: exact, ternary,
// lpm, range, field_mask & unusued
key = {
hdr.ipv4.dstAddr : lpm;
hdr.ipv4.srcAddr : exact;
}
// actions that can be invoked
actions = {
ipv4_forward;
drop_packet;
}
// table properties:
// maximum number of entries
size = 1024;
// enable direct match
// (for exact keys only)
direct_match = true;
// number of supported unique masks:
// ternary and lpm only
num_masks = 256;
// action invoked when table fails
// to find a match for the key used
// default_action = drop_packet();
}
Actions
// action declaration:
action ipv4_forward(bit<9> port) {
// local variable and assignment
bit<48> tmpAddr = hdr.ipv4.srcAddr;
// in/out values from/to data plane
hdr.ipv4.srcAddr = hdr.ipv4.dstAddr;
hdr.ipv4.dstAddr = tmpAddr;
// inputs provided by control plane
meta.port = port; // stored in table
}
// explicit action invocation:
ipv4_forward(0x123); // constant arg. value
Parsing
// parser: must always begin with the
// special state "start"
state start {
transition parse_ethernet;
}
// User-defined parser state
state parse_ethernet {
// fixed length extraction
packet.extract(hdr.ethernet);
// assignment to metadata
meta.eth_dmac = hdr.ethernet.dstAddr;
// select transition to next state
transition select(hdr.ethernet.type) {
0x0800 : parse_ipv4;
0x8100 : parse_vlan;
default : accept;
}
}
// header stack extraction
state parse_vlan {
packet.extract(hdr.vlan.next);
transition select(hdr.vlan.last.tpid) {
VLAN_TYPE : parse_vlan; // loop
IPV4_TYPE : parse_ipv4;
default : accept;
}
}
// advanced parsing
state parse_ipv4 {
// fixed length extraction
packet.extract(hdr.ipv4);
// custom form of error handling
// false causes transition to reject
verify(hdr.ipv4.version == 4,
error.IpVersionNotSupported);
// variable length extraction
packet.extract(hdr.ipv4opt,
((bit<32>)hdr.ipv4.hdr_len - 5) * 32);
// constant transition (end of packet)
transition accept; // fixed transition
}
Deparsing
// the inverse of parsing is deparsing,
// or packet reconstruction
apply {
// insert headers into packet(if valid)
packet.emit(hdr.ethernet);
packet.emit(hdr.vlan); // stack
packet.emit(hdr.ipv4);
packet.emit(hdr.ipv4opt); //varbit
}
Processing
// all variables, tables and actions
// definitions outside apply methodapply {
// unconditional table search
ipv6_lpm.appply();
// branch on header validity
// conditional table search
if (hdr.ipv4.isValid()) {
ipv4_lpm.apply();
}
// branch on table hit result
if (local_ip_table.apply().hit) {
send_to_cpu();
} else {
drop_packet();
}
// branch on error
if (smeta.parser_error ==
error.InvalidEthernetType)
drop_packet();
// header manipulation
hdr.new_vlan = hdr_vlan;
hdr.vlan.setInvalid();
if (hdr.ipv4.isValid())
hdr.new_vlan.tpid = 0x0800
}
Vitis Networking P4 Architecture
// standard metadata format
struct standard_metadata_t {
bit<1> drop;
bit<64> ingress_timestamp;
bit<16> parsed_bytes;
error parser_error;
}
// Pipeline elements
parser Parser<H, M>(
packet_in b,
out H hdr,
inout M meta,
inout standard_metadata_t smeta
);
control MatchAction<H, M>(
inout H hdr,
inout M meta,
inout standard_metadata_t smeta
);
control Deparser<H, M>(
packet_out b,
in H hdr,
inout M meta,
inout standard_metadata_t smeta
);
// architecture’s main package
package XilinxPipeline<H, M>(
Parser<H, M> p,
MatchAction<H, M> ma,
Deparser<H, M> dep
);
Built-In Externs
User Externs
const bit<32> NUM_COUNTERS = 8192; // up to 65536 supported
typedef bit<13> CounterIndex_t; // CounterIndex type should correspond with the number of counters
// Instantiation of Counter Externs
// 3 modes supported, as shown here
Counter<bit<64>, CounterIndex_t>(NUM_COUNTERS, CounterType_t.PACKETS) PacketCounter;
Counter<bit<64>, CounterIndex_t>(NUM_COUNTERS, CounterType_t.BYTES) ByteCounter;
Counter<bit<64>, CounterIndex_t>(NUM_COUNTERS, CounterType_t.PACKETS_AND_BYTES) ComboCounter;
// The "count" method can only be called once per counter instance per packet
PacketCounter.count(counter_index);
ByteCounter.count(counter_index);
ComboCounter.count(counter_index);
// Structure Definitions
struct divider_input {
bit<32> divisor;
bit<32> dividend;
}
struct divider_output {
bit<32> remainder;
bit<32> quotient;
}
divider_input div_in;
divider_output div_out;
// Instantiation of UserExtern Object
UserExtern<divider_input, divider_output>(34) calc_divide;
// Interface to User Extern
calc_divide.apply(div_in, div_out);