Network Programming Language (NPL)
Network Programming Language (NPL)
Network Programming Language (NPL)
v1.3
Table of Contents
1 Scope 8
2 Terminology 9
3 Overview 10
3.1 Benefits 11
4.2.1.1 bit-array 13
4.2.3 const 14
4.2.4 list 14
4.2.5 struct 15
4.2.7 enum 17
4.2.8 auto_enum 17
4.3 Expressions 17
4.3.3 Operators 18
5.2.2.2 execute() 46
5.3.2.1 <dynamic_table_name>.<method_name>(<argument_list>) 48
5.3.2.2 lookup() 48
7 Generic Constructs 59
7.1.2.1 Initialization 62
7.1.2.2 Relational 63
7.3 Comments 64
7.4 Print 64
9.2 Function 66
12.1 Directives 76
Table of Tables
Table 1: struct Construct 16
Table 2: Conditionals in NPL 19
Table 3: Operators in NPL 19
Table 4: Program (Order of Execution) Construct 21
Table 5: Header Type (struct) Construct 23
Table 6: parser_node Construct 28
Table 7: logical_table Construct 33
Table 8: logical_register Construct 39
Table 9: function Construct 40
Table 10: add_header Construct 42
Table 11: delete_header Construct 42
Table 12: replace_header_field Construct 43
Table 13: create_checksum Construct 43
Table 14: update_packet_length Construct 44
Table 15: extern Construct 46
Table 16: special_function Construct 47
Table 17: execute() 48
Table 18: dynamic_table Construct 49
Table 19: lookup() 50
Table 20: strength Construct 52
Table 21: use_strength Construct 52
Table 22: strength_resolve Construct 53
Table 23: Positional Attributes 61
Table 24: Non-Positional Attributes 64
Table 25: struct usage guide 68
Table 26: Referencing struct in Packet 68
Table 27: packet_drop 79
Table 28: packet_trace 79
Table 29: packet_count 80
Table of Figures
Figure 1: Architecture Mode 13
Figure 2: Packet, Header Group, Header Representation 23
Figure 3: Strength Using Tables with Static Indexing 54
Figure 4: Strength Using Tables with Dynamic Indexing 56
Figure 5: Strength Using Table and Bus 57
1 Scope
This document describes the Network Programming Language (NPL) constructs and usage.
The main objective of NPL is to describe Data Plane Packet Processing behavior using an appropriate set of
constructs. A packet processing application in NPL includes high level constructs for functions such as parsing,
match action tables, and packet editing. It can also include specific constructs for other features, such as
functions.
Since a primary requirement of the language is to map onto flexible hardware, the overall set of available
constructs tends to be focused on features found in flexible hardware. Application development must be done
using these constructs.
This document is intended for System Architects, Design Engineers, and Software Engineers so that they can
understand NPL logic details, make modifications to an NPL program, or develop their own custom packet
processing applications. Test Engineers should understand NPL to enable them to develop comprehensive
test plans.
This document does not cover any particular programmable device architecture or the operation of the NPL
Front End or Back End compilers.
2 Terminology
The following lists the terms, concepts, symbols, and acronyms used in this document.
Term Description
Front End (FE) Compiler A component of the compiler that parses and syntax checks NPL
source code, and generates an Intermediate Representation (IR)
Back End (BE) Compiler A component of the compiler that takes an IR and generates the
personality for various hardware components
Metadata Bus, header, and table fields that are not created in the NPL but are
still present and accessible.
3 Overview
The growth of Software Defined Networks (SDN) raised the expectations for network programmability and
automation. Initially SDN focused on control plane issues, with users seeking to overcome limitations of
traditional management models. Subsequently users wanted more flexible solutions capable of adapting to
changing network needs, such as new overlay protocols and advanced telemetry features. This enlarged the
scope of SDN to include programming the data plane. However, new flexible switching solutions must be
capable of delivering full line-rate performance with optimized switch resources and power.
While different programing languages could be designed for programming the data plane, a language with
appropriate constructs that take advantage of advanced programmable hardware architecture capabilities is
key. For that reason a new language was developed: NPL (Network Programming Language) is intended to be
an open, high level language that addresses the unique requirements of efficiently programming packet
forwarding planes. NPL includes constructs to express networking behavior that take advantage of advanced
features of the underlying programmable hardware.
In its first incarnation, NPL provides all the necessary features to implement robust network switching
solutions. NPL building blocks range from data types that allow the specification of individual control signals to
high-level constructs which allow interfacing with complex hardware blocks.
In a typical fixed function switch the set of tables and objects for packet processing are designed in, with only
limited changes possible after manufacturing. In a programmable switch, packet processing elements can be
determined by the user. NPL allows the user to specify details of tables and other objects to achieve the
desired behavior. NPL is specialized for programming the data plane. It relies on conventional programming
languages to specify how the control plane utilizes features in the switch data path.
The NPL language is not intrinsically bound to any specific hardware architecture. It is intended to be
implemented on multiple hardware platforms such as programmable ASICs, programmable network interface
cards (NICs), FPGAs and pure software switches. While certain language constructs are intended to optimize
use of specific hardware features on certain targets, these would not prevent mapping to targets that do not
support these features.
Like any high level programming language, NPL requires a set of compilers and associated tools to map the
programs written in NPL to target hardware objects. The front-end compiler is responsible for checking the
syntax and semantics of the user-written program in the NPL language generating an Intermediate
Representation (IR). The back-end compiler is responsible for mapping these intermediate representations into
specific hardware objects. It also generates an API that the control plane uses to manage the behavior of the
switch.
The compilers provide a level of parallelization which is determined by the NPL and underlying hardware.
3.1 Benefits
The development of NPL began with a review of existing industry strength networking platforms and how to
enable customers with their programmability needs, recognizing that the solution should encompass both SW
and HW aspects. So, the result required an ability to expose and take advantage of architectural efficiencies of
the underlying hardware. Similarly, the solution also required end users to specify the data plane behavior
efficiently and elevate it to the OS and application layer, with clearly specifying user intents. Thus, a new
language evolved that would enable users to specify Data Plane networking functionality with capabilities to
expose target efficiencies with clear user intent. The result is NPL.
Compared to configurable and other programmable solutions available today, NPL provides many advantages.
The language has sophisticated features that promote:
NPL also provides constructs that provide for the inclusion of component libraries that implement fixed function
hardware blocks. These features enable describing a range of dataplane application in NPL, from simple table-
based architectures to more advanced architectures that incorporate a range of highly efficient building blocks.
The language constructs allow for the expression of these capabilities, which help significantly improve power
efficiency and reduce cost in the resulting hardware implementation. Similarly, NPL language constructs
promote software reuse that help in building a family of switching solutions ranging from simple to increasingly
complex.
Figure 1: Architecture Model shows a block diagram of the basic NPL architectural components and how they
relate to each other.
An instance of this architecture model can be created with zero or more of these sub-components in arbitrary
order.
The following sections describe the language constructs used to program these core abstractions, with
examples.
Definitions for NPL identifiers and constants, as well as the complete NPL grammar, are provided in the
Appendix. Briefly:
● Identifiers must start with [a-z A-Z _] and can contain characters from [a-z A-Z _ 0-9]
● Decimal and hexadecimal literals
● String literals (e.g., “foobar”)
The remainder of this section provides details for each of the supported constructs.
4.2.1.1 bit-array
A multi-bit field description would use the bit-array type to represent the field’s width. NPL imposes no
restriction on the size of a bit array. Note that NPL does not support arrays of bit arrays.
Example
NPL allows static and variable indexing of arrays. Initially, support is limited to bit arrays.
An index is specified as unsigned integer values. It can be one bit or a range of bits.
Example
local.rst1 = local.rpa_id_profile1[3:2];
local.rst1 = local.rpa_id_profile1[0:0];
Example
local.rst1 = local.rpa_id_profile1[ip_tmp_bus.idx:ip_tmp_bus.idx];
Example
4.2.3 const
const data type is used to denote a constant. integer or enum value.
Example
4.2.4 list
Some NPL constructs may require combining a variable number of arguments into a list; for example, dynamic_table,
strength_resolve and create_checksum. The list data type is used to represent such cases.
Example
The dynamic_table arguments use lists to provide the fields which can be used in the preselect template.
flex_digest_lkup.presel_template(
{
ing_cmd_bus.l2_iif_opaque_ctrl_id,
ing_cmd_bus.vfi_opaque_ctrl_id,
ing_cmd_bus.l2_iif_flex_digest_ctrl_id_a,
ing_cmd_bus.l2_iif_flex_digest_ctrl_id_b,
ing_cmd_bus.fixed_hve_iparser1_0,
ing_cmd_bus.flex_hve_iparser1_1,
ing_cmd_bus.fixed_hve_iparser2_0,
ing_cmd_bus.flex_hve_iparser2_1,
ing_cmd_bus.my_station_hit
});
The create_checksum construct contains a list argument which provides a list of fields to be used in the
checksum generation.
create_checksum(egress_pkt.fwd_l3_l4_hdr.udp.checksum,
{
egress_pkt.fwd_l3_l4_hdr.ipv4.sa,
egress_pkt.fwd_l3_l4_hdr.ipv4.da,
editor_dummy_bus.zero_byte,
egress_pkt.fwd_l3_l4_hdr.ipv4.protocol,
egress_pkt.fwd_l3_l4_hdr.udp.udp_length,
egress_pkt.fwd_l3_l4_hdr.udp.src_port,
egress_pkt.fwd_l3_l4_hdr.udp.dst_port,
egress_pkt.fwd_l3_l4_hdr.udp.udp_length,
egress_pkt.fwd_l3_l4_hdr.udp._PAYLOAD
});
4.2.5 struct
Struct is used to specify an ordered aggregate of fields. Struct is the basic building block used in a variety of
constructs. Only bit and struct are allowed inside a struct. (Refer to 6.1 struct for more information).
Subsequent sections describe the usage of structs.
Struct allows overlays to reference fields in a struct in more than one way..
Example
struct switch_bus_s {
fields {
bit[4] otpid_enable;
bit olp_enable;
bit ts_enable;
bit[10] ing_port_num; // Base field for the overlay fields, defined later.
bit svp_enable;
}
overlays {
ing_svp : ing_port_num[7:0];
ing_pri : ing_port_num[9:8]; // multiple overlays on same base field
exp : ing_port_num[9:8];
}
}
struct mpls_header_stack_t {
fields {
mpls_t mpls[3]; // means 3 mpls_t headers can be there.
}
}
4.2.7 enum
NPL supports the enum construct for defining enumeration types. Values of enum members must be provided
by the user. NPL enums are simple identifier lists providing a subset of what is provided in C/C++. enum is not
a datatype in NPL. It is used to represent constants. enum defined constants are used as arguments in
function calls and rvalue in an assignment.
Example
enum drop_reason{
NO_DROP = 0,
MEMBERSHIP_DROP = 1,
TTL_DROP = 2
}
packet_drop(drop_bus.disable_drop, drop_reason.TTL_DROP, 5);
4.2.8 auto_enum
NPL supports auto_enum data type for defining enumeration types. Values of auto_enum members is
assigned by compilers. Target vendor may decide how to map and assign values of auto_enum.
Typical usage for auto_enums are Logical Table Lookup, Multi-Data View and Strength Based Resolution Index.
Target compiler may assign auto_enum values based on the context in which they are used. Thus
auto_enums must be global and used in single instance. For example, auto_enums used in logical table
lookup cannot be used in special functions.
Example
auto_enum qos_entry {
QOS_DISABLE,
QOS_L3_TUNNEL,
QOS_L2_TUNNEL
}
qos_sfc.sf_profile_entry("sfc_qos_profile", qos_entry.QOS_L3_TUNNEL,
{
obj_bus.mapping_ptr,
cmd_bus.effective_exp
},
{
cmd_bus.int_pri,
cmd_bus.pri
} );
4.3 Expressions
Example
a = 5; //decimal
ipv4.ttl = 0xF; //hex
ipv6.dip = 0x01234567;
ipv6.dip[63:0] = 0x0123456789abcdef;.
if (ipv4.protocol == 0x23)
ipv4.protocol = 0x231;
Conditional Description
if, else if, else If statements
switch Switch statement
4.3.3 Operators
NPL allows multiple operations. Usage restrictions are described in the various construct sections.
|| Logical OR
Shift operators << Shift left
>> Shift right
Bitwise Logical & AND
operators
| OR
!A Logical NOT
~A One’s Complement
^ XOR
Unary operators &A Reduction AND (all bits 1)
|A Reduction OR (all bits 0)
Assignment operators = Assignment - Note that the left and right hand
sides of an assignment are both sized.
If the lvalue is larger than the rvalue then the
rvalue is zero extended to the target.
If the lvalue is smaller than the rvalue then the
compiler shall issue an error.
Mask operator mask Used in a switch statement case, treated as an
AND.
In NPL, the following variable names are in global namespaces. Unique names must be used while specifying
these:
● enum
● auto_enum
● struct
● bus
● packet
● logical_table
● logical_register
● parser_node
● function
● special_function
● dynamic_table
● strength
● auto_enum - elements
● special_function - methods
● dynamic_table - methods
A program may call the following constructs using the keywords in Table 4: Program (Order of Execution)
Construct.
● Parser Tree
● Table Lookups (logical_table, dynamic_table)
● Packet Processing Functions
● Special Function
● Strength Resolution
Example
program mim_main () {
parse_begin (ethernet); /* start parsing from "parser_node" Ethernet. */
port.lookup (0); /* lookup the port table. */
iif.lookup (0); /* lookup the iif table. */
my_station.lookup (0); /* lookup the my_station table. */
isid.lookup (0); /* lookup the isid table */
mim_isid_switch_logic1(); /* execute logic function. */
...
if (cmd_bus.do_l3) { /* conditional lookup. */
l3_host.lookup (0);
}
...
l3_switch_logic1(); /* packet processing function call */
...
next_hop.lookup (0); /* lookup next_hop table. */
do_packet_edits(); /* editor function. */
}
NPL allows specifying basic protocol header types using struct. A parser specification would use header types
to declare header group level structs. A parser specification would construct a packet level struct using header
groups.
In all other usage of struct these two additional options are invalid.
NPL allows header array.
Arguments/Options Description
header_length_exp For variable lengths, specifies the equation with which the length
can be determined.
Operations supported in the equation are +,*.
This expression should take the form: var * c0 + c1
Where var is a header field and c0 and c1 are constants.
Example
struct vlan_t {
fields {
bit[3] pcp;
bit cfi;
bit[12] vid;
bit[16] ethertype;
}
}
Specifies width and the order in which the fields pack in the header.
struct ipv4_t {
fields {
bit[4] version;
bit[4] hdr_len;
bit[8] tos;
bit[32] sa;
bit[32] da;
varbit[320] option; // specify max allowed size of the variable length field
}
header_length_exp: hdr_len*4; // specifies how to compute the number of bytes in the header.
}
In this example:
● ”option" is the variable length field.
● "header_length_exp" specifies how to compute the length of the header.
● header_length_exp : (payload_len*4)+2; //another example
● Arithmetic operators allowed : (+,*)
● varbit[num] - num specifies the maximum allowed width for the field.
● Each Variable length header must have a header_length_exp attribute.
● A struct with multiple variable length fields is not supported. Specify individual structs in that case.
● A header group struct must only instantiate header structs. bit/bit-array are not allowed.
● Arrays of header groups are not allowed.
● Headers and header groups must be listed in the sequence in which they would appear in a packet.
This sequence is important.
Example
struct l2_header_t {
fields {
bit[48] macda;
bit[48] macsa;
bit[16] ethertype;
}
}
struct vlan_tag_t {
fields {
bit[3] pcp;
bit cfi;
bit[12] vid;
}
}
struct group0_t {
fields {
l2_header_t l2_header;
vlan_tag_t ovlan;
}
}
The header group struct specifies the order in which headers are packed in a header group.
struct group1_t {
fields {
mpls_t mpls[3]; // means 3 mpls_t headers are there.
}
}
This statement declares a packet with the name instance-name which is described by the struct declared as
struct-name. The associated struct (struct-name) is referred to as a packet level structure. The members of a
packet level structure must be header group structures.
Packet cannot contain bit, bit-array members. Packet instances cannot be arrays.
A packet level struct is used to aggregate header groups into a packet. The header groups must follow the
order in which the groups appear across all packets.
Example
struct macs_t { // Header structure because member of header group structure and
// all members are bit-arrays
fields {
bit[48] dmac;
bit[48] smac;
}
}
struct vlan_t { // Header structure
fields {
bit[16] tpid;
bit[3] pcp;
bit dei;
bit[12] vid;
}
}
struct ethertype_t { // Header structure
fields {
bit[16] type;
}
}
struct mpls_t { // Header structure because member of header group structure and
// all members are bit arrays
fields {
bit[20] label;
bit[3] tc;
bit s;
bit[8] ttl;
}
}
struct mpls_grp_t { // Header group structure
fields{
mpls_t mpls[3];
}
}
struct ipv4_t { // Header structure
fields {
// IPv4 field definitions (bit-arrays only)...
}
}
struct ipv6_t { // Header structure
fields {
// IPv6 field definitions (bit-arrays only)...
}
}
struct l2_t { // Header group structure because member of packet level structure and
// all members are structures
fields {
macs_t macs;
vlan_t ctag;
ethertype_t etype;
}
}
struct l3_t { // Header group structure
fields {
ipv4_t ipv4;
ipv6_t ipv6;
}
}
struct ingress_packet_t { // Packet level structure because instantiated as packet (below)
fields {
l2_t l2;
mpls_grp_t mpls_grp;
l3_t l3;
}
}
packet ingress_packet_t ing_pkt; // This declaration identifies ingress_packet_t as a
// packet level structure
Header and fields in a packet should be referenced according to the following example:
ing_pkt.l2.macs.da
Target may have restrictions on Packet access and modification. For example, for a split Ingress and Egress
target architecture, there may be 2 packets, ingress packet and egress packet. Ingress packets may be read-
only and can not be modified. All Packet modifications are performed on egress packets.
The _PRESENT bit indicates whether a particular header instance is valid in the current packet. This makes
_PRESENT a dynamic metadata. If in an incoming packet, a header is present, then _PRESENT is evaluated
as 1.
_PRESENT is a read-only metadata field and will maintain its state during the life of the packet.
Example
struct tcp_t {
fields {
...
}
}
struct group0_t {
fields {
tcp_t tcp;
...
}
}
struct packet_t {
fields {
group0_t group0;
...
}
}
program l3 () {
...
if (ing_pkt.group0.tcp._PRESENT) {
l2_table.lookup(0);
}
...
Example
struct l2_t {
fields {
bit[48] macda;
bit[48] macsa;
bit[16] ethertype;
}
}
struct group1_t {
fields {
l2_t l2;
vlan_t vlan;
}
}
struct ing_pkt_t {
fields {
group1_t group1;
}
}
parser_node start {
root_node : 1;
next_node ethernet;
}
parser_node ethernet {
extract_fields(ing_pkt.group1.l2);
switch (latest.ethertype) {
parser_node ctag {
extract_fields(ing_pkt.group1.vlan);
if (current(0,16) == r_ing_outer_tpid_0.tpid) {
next_node otag;
}
next_node ingress;
}
parser_node ingress {
end_node : 1;
}
NPL provides mechanism to perform table lookups or other packet processing during parsing. This may be required
when some decision in parser node requires output from table lookup. Thus flow needs to break from parser tree
traversal and return control to program and then continue from the same node after the lookup is done. This is done
using parse_break and parse_continue constructs.
Example
Perform a table lookup before parsing ethernet packet and mpls packet.
program mpls_switch() {
parse_begin(start);
port_table.lookup(0);
// ethernet node consumes output from port_table i.e logical_bus.otpid_enable
parse_continue(ethernet);
// mpls_label node consumes output from mpls_table i.e logical_bus.mpls_table_result_type
mpls_table.lookup(0);
parse_continue(mpls_label);
}
parser_node start {
root_node : 1;
switch(logical_bus.rx_port_parse_ctrl) {
0x0: next_node ppd;
0x2: next_node sobmh;
0x3: parse_break(ethernet);
default: next_node ingress;
}
}
parser_node ethernet {
extract_fields(ingress_pkt.outer_l2_hdr.l2);
if (logical_bus.otpid_enable[3:3] && latest.ethertype == 0x8100) {next_node otag;} //0x8100
if (logical_bus.otpid_enable[2:2] && latest.ethertype == 0x8100) {next_node otag;} //0x8100
if (logical_bus.otpid_enable[1:1] && latest.ethertype == 0x8100) {next_node otag;} //0x8100
if (logical_bus.otpid_enable[0:0] && latest.ethertype == 0x8100) {next_node otag;} //0x8100
parser_node mpls_0 {
extract_fields(ingress_pkt.outer_l3_l4_hdr.mpls[0]);
switch (latest.stack) {
0x0: next_node mpls_1;
0x1: parse_break(mpls_label);
default: next_node ingress;
}
}
parser_node mpls_label {
extract_fields(ingress_pkt.outer_l3_l4_hdr.mpls[4]);
switch (logical_bus.mpls_table_result_type) {
0x0: next_node mpls_cw;
0x1: next_node inner_ethernet;
0x2: next_node inner_l3_speculative;
default: next_node ingress;
}
}
parser_node ingress {
end_node:1;
}
Note:
end_node:1 marks the end of parsing, once control reaches here parse tree traversal will end.
Example
INSTANTIATE A BUS
struct control_bus_t {
fields {
bit ts_enable;
bit olp_enable;
bit[4] otpid_enable;
}
}
parser_node pkt_start{
root_node : 1;
next_node ethernet;
}
parser_node ethernet {
extract_fields(ing_pkt.group0.l2);
if (control_id.ts_enable == 0) { // control_id is a logical bus.
// ts_enable is a field in the bus.
if (control_id.otpid_enable != 0 ) {
switch (latest.ethertype) {
0xABCD : {next_node vntag};
0x8888 : {next_node etag};
0x8100 : {next_node otag};
0x9100 : {next_node itag};
0x0000 mask 0xFC00 : {next_node llc};
default : {next_node payload};
}
}
}
}
parser_node otag{
extract_fields(ing_pkt.group0.ovlan);
end_node:1;
}
Logical table Keys and Fields are locally scoped. NPL logical tables can be looked-up multiple number of
times, depending on the target architecture capabilities.
All declared logical tables must be called with the <logical table name>.lookup(lookup_num) construct.
lookup_num = 0 means first lookup and so on.
Example
logical_table port {
table_type : index;
minsize : 128;
maxsize : 128;
keys {
bit[7] port_num;
}
fields {
bit[1] l3_enable;
bit[1] otag_enable;
bit[8] src_modid;
bit[12] default_vid;
}
key_construct() {
port_num = obj_bus.port_num;
}
fields_assign() {
if (_LOOKUP0 == 1) {
cmd_bus.port_l3_enable = l3_enable;
...
}
}
}
logical_table my_station_hit {
table_type : tcam;
maxsize : 512;
minsize : 512;
keys {
bit[48] macda;
bit[12] vid;
bit[8] src_modid;
}
fields {
bit[2] mpls_tunnel_type;
bit local_l3_host;
}
key_construct() {
macda = ing_pkt.l2_grp.l2.macda;
vid = obj_bus.vlan_id;
src_modid = obj_bus.source_logical_port;
}
fields_assign() {
if (_LOOKUP0 == 1) {
l3_cmd_bus.local_l3_host = local_l3_host;
...
}
}
}
Calling a TABLE
program ingress {
port.lookup(0); //calls port logical table
if (cmd_bus.vlan_valid == 1) {
my_station_hit.lookup(0); //calls my_station_hit logical table first time
my_station_hit.lookup(1); //calls my_station_hit logical table second time
}
}
Example
logical_table table_a {
...
fields_assign() {
if (_LOOKUP0) {
obj_bus.src_hit_index = _HIT_INDEX0;
}
if (_LOOKUP1) {
obj_bus.dst_hit_index = _HIT_INDEX1;
}
}
}
As with the header metadata, it increases application readability and provides a basis for instrumentation.
Example
program {
if ((ing_pkt.l2_grp.l2._PRESENT) & (ing_pkt.l2_grp.vlan.vid != 0)) { //Condition is supported.
mac_table.lookup(0);
mac_table.lookup(1);
}
}
NPL writer must specify all the fields, across different data types, in the fields{} construct.
Example
auto_enum multi_view {
UC_VIEW,
MC_VIEW,
BC_VIEW
}
logical_table NHI {
...
fields {
bit[3] A;
bit[15] B;
bit[7] C;
bit[10] D;
bit[4] E,
bit[4] F;
bit[16] strength_object_G;
multi_view X; //the data_type field for denoting different views. Use auto_enum.
}
fields_assign() {
if (_LOOKUP0 == 0) {
if (_VALID == 1) { //_VALID is same as hit.
if (X == UC_VIEW) {
bus.A = A;
bus.B = B;
bus.C = C;
}
if (X == MC_VIEW) {
bus.A = A;
bus.D = D;
bus.E = E;
bus.F = F;
}
} //end _VALID == 1
else { //_VALID == 0 //specify only data_type = 0 fields.
bus.A = 0;
bus.B = 0;
bus.C = 5; //example of a non-zero constant
bus.G = 0;
}//end _VALID == 0
}
if (_LOOKUP1 == 1) {
...
}
}
}
A logical_register is often useful in the parse tree, functions, etc. They can be used in lieu of constants, which
may be configurable by control-plane.
Example
DEFINE A LOGICAL_REGISTER
// For example, this construct can be used to specify values like TPID.
logical_register tpid_values {
fields {
bit[16] tpid0 = 0x8100;
bit[16] tpid1 = 0x9100;
bit[16] tpid2 = 0x7100;
bit[16] tpid3 = 0x8868;
}
}
Function allow conditional statements, assignment statements and complex operations for data
transformation of the logical buses. Functions can access and update logical bus data. Functions can be
nested within other functions. Declarations are not allowed inside functions.
Here are a few scenarios where function could be used. Functions can be used to implement flexible decision
logic. For example, decode lookup results to determine whether a packet is unicast or multicast. Function can
be used to extract packet data. Function can be used to invoke logical table lookups, special functions.
Function can also be used to specify application modularity. For such usage, function may have logical table
lookups, strength resolve, special function calls, dynamic table calls etc. All items which can be specified in
“program” construct can be specified in such function. It is suggested to maintain separate NPL functions for
modularity and packet processing.
Target may choose to limit scope and usage of function depending on hardware architecture.
Example
function l3_switch_logic1 () {
temp.no_l3_switch = 0;
if (port.l3_enable &&
(ingress_pkt.outer_l3_l4_hdr.ipv4._PRESENT || ingress_pkt.outer_l3_l4_hdr.ipv6._PRESENT)
) {
if (obj_bus.tunnel_pkt || obj_bus.tunnel_error) {
if (obj_bus.tunnel_error) {
obj_bus.tunnel_decap = 0;
temp.no_l3_switch = 1;
if (cpu_control.tunnel_to_cpu) {
obj_bus.copy_to_cpu = 1;
}
} else {
obj_bus.tunnel_decap = 1;
}
} else { //Not a tunneled pkt
obj_bus.tunnel_decap = 0;
}
}
// Trace packets
packet_trace(temp.no_l3_switch, cpu_reason.NO_SWITCH);
The modified packets (after editing) must conform to one of the packets described in the parsing graph. Thus,
editor commands must use the same names for headers as in the parsing tree.
Construction of new headers is not in scope of the editor constructs. Editor constructs must be invoked from
within a function.
Ingress packets are read only and cannot be modified. Packet modifications are performed on egress packets.
Example
If an application needs to add an otag and construct it before calling add_header(otag), do the following;
egr_pkt.group1.otag.vid = ing_pkt.itag.vid+100; /*both otag and itag are defined as headers in the
packet construct.*/
egr_pkt.group1.otag.pcp = obj_bus.egr_port_table_pcp; /*from the object bus.*/
egr_pkt.group1.otag.tpid = 0x9100;
add_header(egr_pkt.group1.otag);
egr_pkt.group2.tunnel_l2.dmac = 0xff;
egr_pkt.group2.tunnel_l2.smac = obj_bus.l3_interface_smac;
egr_pkt.group2.tunnel_l2.vid = obj_bus.l3_next_hop_vid;
add_header(egr_pkt.group2.tunnel_l2);
Example
delete_header(egr_pkt.group1.otag);
This works on this instance of the packet and removes otag from this packet. It has no impact on the parsing
tree.
If an application is trying to remove a header group from the packet:
delete_header(egr_pkt.group1);
This works on this instance of the packet and removes header group1 from this packet. It has no impact on the
parsing tree.
Example
create_checksum(egress_pkt.group2.ipv4.hdr_checksum,
{egress_pkt.group2.ipv4.version, egress_pkt.group2.ipv4.hdr_len,
egress_pkt.group2.ipv4.tos, egress_pkt.group2.ipv4.v4_length,
egress_pkt.group2.ipv4.id, egress_pkt.group2.ipv4.flags,
egress_pkt.group2.ipv4.frag_offset, egress_pkt.group2.ipv4.ttl,
egress_pkt.group2.ipv4.protocol, egress_pkt.group2.ipv4.sa,
egress_pkt.group2.ipv4.da});
create_checksum(egress_pkt.fwd_l3_l4_hdr.udp.checksum,
{egress_pkt.fwd_l3_l4_hdr.ipv4.sa,
egress_pkt.fwd_l3_l4_hdr.ipv4.da,
editor_dummy_bus.zero_byte,
egress_pkt.fwd_l3_l4_hdr.ipv4.protocol,
egress_pkt.fwd_l3_l4_hdr.udp.udp_length,
egress_pkt.fwd_l3_l4_hdr.udp.src_port,
egress_pkt.fwd_l3_l4_hdr.udp.dst_port,
egress_pkt.fwd_l3_l4_hdr.udp.udp_length,
egress_pkt.fwd_l3_l4_hdr.udp._PAYLOAD});
update_packet_length(egress_pkt.group2.ipv4.v4_length, 1);
Example
The following is an example using create_checksum and update_packet_length constructs. Here the packet
length must be updated and checksum on egress.
function do_checksum_update() {
create_checksum(egress_pkt.group2.ipv4.hdr_checksum,
{egress_pkt.group2.ipv4.version, egress_pkt.group2.ipv4.hdr_len,
egress_pkt.group2.ipv4.tos, egress_pkt.group2.ipv4.v4_length,
egress_pkt.group2.ipv4.id, egress_pkt.group2.ipv4.flags,
egress_pkt.group2.ipv4.frag_offset, egress_pkt.group2.ipv4.ttl,
egress_pkt.group2.ipv4.protocol, egress_pkt.group2.ipv4.sa,
egress_pkt.group2.ipv4.da});
create_checksum(egress_pkt.group4.ipv4.hdr_checksum,
{egress_pkt.group4.ipv4.version, egress_pkt.group4.ipv4.hdr_len,
egress_pkt.group4.ipv4.tos, egress_pkt.group4.ipv4.v4_length,
egress_pkt.group4.ipv4.id, egress_pkt.group4.ipv4.flags,
egress_pkt.group4.ipv4.frag_offset, egress_pkt.group4.ipv4.ttl,
egress_pkt.group4.ipv4.protocol, egress_pkt.group4.ipv4.sa,
egress_pkt.group4.ipv4.da});
}
function do_packet_length_update() {
update_packet_length(egress_pkt.group2.ipv4.v4_length, 1);
update_packet_length(egress_pkt.group4.ipv4.v4_length, 1);
}
program app {
...
do_packet_length_update();
do_checksum_update();
...
}
Target extern functions are the basic functions in a target architecture. Extern functions may be called
repeatedly in the application along with other NPL constructs. e.g. packet drop extern function called as part of
logical_table lookup.
Special function constructs are used to specify target accelerators or IP, and their usage modes. Special
functions require specific-connectivity in terms of inputs and outputs. The internals of the special functions is
not specified in NPL. Target vendor defines special function. NPL writer calls them.
The Dynamic Table constructs are used to specify target specific “runtime” logical tables. Target vendor
specifies basic structure of dynamic table. NPL writer specifies a menu of logical signals such that target-SDK
can create runtime logical tables.
After NPL is converted to a simulation language such as C++, sample behavior of the Special Functions and
Target Extern functions may be provided by target.
NPL allows target vendor to specify such functions as Extern Functions. Target vendor defines extern function
template with information required to better utilize hardware.Following sections cover definition and call
process.
Arguments/O
Construct ptions Description
direction in or out
Example
Where trigger means any bus field which can trigger packet drop. And other properties associated
with packet drop.
Example
logical_table packet_integrity {
….
fields {
bit copy_to_cpu;
bit pkt_integrity_drop;
}
……
fields_assign() {
….
packet_drop(pkt_integrity_drop, drop_reason.PKT_INTEGRITY_CHECK_FAILED, 2);
}
}
A target vendor may specify the following methods to define IP block interface.
Example
special_function flex_qos_phb {
usage_mode_create(in const index,
in bit[10] qos_base,
in varbit[6] qos_attr,
out bit[4] int_pri
);
usage_mode_select(in bit[6] eindex);
}
The NPL writer uses the special_function methods, provided by the target architecture, to specify connections to the
IP blocks. The position of the these methods call in the NPL does not reflect the call sequence.
Syntax to call such method is as below. Argument type and their order MUST match from template provided.
<special_function_name>.<method_name>(<arguments>)
5.2.2.2 execute()
The execute() is a built-in method that is not specified in the special_function construct. This method activate
IP block and provide IP block relative position in logical functionality. This method doesn’t take any argument.
Example
program app {
...
flex_qos_phb.usage_mode_create(flex_qos_entry.QOS_MPLS_EXP_L3_TUNNEL_ECN,
ing_obj_bus.mpls_exp_mapping_ptr[9:0],
ing_cmd_bus.mpls_effective_exp_for_phb,
ing_cmd_bus.int_pri
);
flex_qos_phb.usage_mode_create(flex_qos_entry.QOS_MPLS_EXP_L2_TUNNEL_ECN,
ing_obj_bus.mpls_exp_mapping_ptr[9:0],
ing_cmd_bus.mpls_effective_exp_for_phb1,
ing_cmd_bus.int_pri1
);
...
flex_qos_phb.usage_mode_select(phb_select_lts_tcam_key.entry_index);
flex_qos_phb.execute();
...
}
Target vendor provides a dynamic_table definition. This definition does not have any explicit input or output
connections. It indicates to the subsequent SDK how to use the dynamic tables.
A target vendor may specify the following methods to define a dynamic table interface.
Example
dynamic_table ing_fp {
presel_template(in list presel_menu);
rule_template(in list rule_menu);
action_template(out list action_menu);
}
5.3.2.1 <dynamic_table_name>.<method_name>(<argument_list>)
The NPL writer uses the dynamic_table methods, provided by the target architecture, to connect logical signals to the
dynamic table blocks. The position of the method calls in the NPL does not reflect the call sequence.
5.3.2.2 lookup()
The lookup() is a built-in method that is not specified in the dynamic_table construct. This method is used to
specify the location of the dynamic_table call.
Example
program {
...
ing_fp.presel_template(
{
ing_cmd_bus.l2_iif_opaque_ctrl_id,
ing_cmd_bus.l3_iif_opaque_ctrl_id,
...
});
ing_fp.rule_template(
{
pkt_fwd_field_bus.macda,
pkt_fwd_field_bus.macsa,
...
});
ing_fp.action_template(
{
ifp_scratch_bus.ifp_drop_action,
ifp_scratch_bus.ifp_drop_code,
...
});
ing_fp.lookup(); //lookup is the constructor for the dynamic_table
}
NPL provides constructs to associate strength values with table lookup results. Each lookup generates one
strength profile. Strengths can be static (per table based) or dynamic (per entry based).
Use the struct construct to create the strength table fields. These are the fields that require strength resolution.
● The struct can only have bit fields and not nested structs
The entries in the strength table for resolving lookups will be specified in the arguments to the strength_resolve
construct.
strength struct name Name of the Strength table structure with field
names representing the fields of strength table
entry.
This should be a struct.
strength table name Name of the Strength Table.
This can be a string
...
strength_entry
Example
struct obj_strength_t {
fields {
bit[4] obj_k_str;
}
}
logical_table Table_A {
...
fields {
bit[8] obj_k;
}
fields_assign() {
use_strength(obj_strength_profile, 1); // reference to strength profile table entry
}
}
logical_table Table_B {
...
fields {
bit[8] obj_k;
}
fields_assign() {
use_strength(obj_strength_profile, 2); // reference to strength profile table entry
}
}
program app {
...
strength_resolve(cmd_bus.obj_k, NULL,
{ Table_A._LOOKUP0, NULL, obj_strength_profile.obj_k_str, Table_A.obj_k},
{ Table_B._LOOKUP0, NULL, obj_strength_profile.obj_k_str, Table_B.obj_k});
...
}
struct obj_strength_t {
fields {
bit[4] obj_k_str;
}
}
logical_table Table_A {
...
fields {
bit[8] obj_k;
bit[5] strength_index;
}
fields_assign() {
use_strength(obj_strength_profile, strength_index);
}
}
logical_table Table_B {
...
fields {
bit[8] obj_k;
bit[5] strength_index;
}
fields_assign() {
use_strength(obj_strength_profile, strength_index);
}
}
program app {
...
strength_resolve(cmd_bus.obj_k, NULL,
{ Table_A._LOOKUP0, NULL, obj_strength_profile.obj_k_str, Table_A.obj_k},
{ Table_B._LOOKUP0, NULL, obj_strength_profile.obj_k_str, Table_B.obj_k});
...
}
SPECIFYING STRENGTH FOR AN OBJECT BEING DRIVEN FROM A TABLE AND BUS
struct obj_strength_t {
fields {
bit[4] obj_k_str;
}
}
logical_table Table_A {
...
fields {
bit[8] obj_k;
bit[3] a_strength_index;
}
fields_assign() {
use_strength(obj_strength_profile, a_strength_index);
}
}
program () {
strength_resolve(cmd_bus.obj_k, cmd_bus.obj_k_str,
{Table_A._LOOKUP0, NULL, obj_strength_profile.obj_k_str, Table_A.obj_k});
}
struct obj_bus_t {
fields {
bit[12] dst_vlan;
bit[12] src_vlan;
bit[11] dst_vfi;
bit[11] src_vfi;
}
}
struct cmd_bus_t {
fields {
bit dst_discard;
bit src_discard;
}
}
struct UAT_strength_profile_t {
fields {
bit[4] obj_src_discard_str;
bit[4] obj_K_str;
}
}
program () {
...
//multi (2) lookups
Table_A.lookup(0);
Table_A.lookup(1);
//Single Lookup
Table_C.lookup(0);
strength_resolve(cmd_bus.src_discard, NULL,
{Table_A._LOOKUP1, NULL, UAT_strength_profile.obj_src_discard_str, Table_A.discard},
{Table_B._LOOKUP1, NULL, UAT_strength_profile.obj_src_discard_str, Table_B.discard});
strength_resolve(cmd_bus.dst_discard, NULL,
{Table_A._LOOKUP0, NULL, UAT_strength_profile.obj_K_str, Table_A.discard},
{Table_B._LOOKUP0, NULL, UAT_strength_profile.obj_K_str, Table_B.discard});
...
}
7 Generic Constructs
Compilers can propagate NPL Attributes to the target vendor output files. e.g. target compiler can dump
documentation about logical_table in output file called, Regsfile or Map File. The following Table summarises
the use of Positional Attributes.
NPL Code:
<!(REGSFILE, DESC: "This table is looked up using Layer 2 incoming interface packet was received on.
This table provides incoming Layer 2 interface attributes for the packet.") !>
logical_table ing_l2_iif_table {
...
fields {
<!(REGSFILE, DESC: "If set, IPV6 Tunnel is enabled on this interface.") !>
bit ipv6_tunnel_enable;
}
}
NPL Code:
enum pvlan_port {
PVLAN_PROMISCUOUS_PORT = 0,
PVLAN_COMMUNITY_PORT = 1,
PVLAN_ISOLATED_PORT = 2
}
logical_table ing_vfi_table {
...
fields {
<!REGSFILE, ENCODING: pvlan_port !> !
<!REGSFILE, ENCODING: DESC: pvlan_port.PVLAN_PROMISCUOUS_PORT = Pvlan Promiscuous port !>
bit[FIELD_2_WD] src_pvlan_port_type;
}
}
NPl Code:
dynamic_table egr_flex_ctr {
ORDER: 2
TAG: bus_select
WIDTH: 64
All Non-Positional Attributes are collected in a IFILE (Intent File) yaml file. The IFILE will only contain non-
positional attributes. These attributes will be grouped based on the specified functionality. The non-positional
attributes can support different functionality.
Consumer of the IFILE must write utility to consume compiler outputs and convert them in desired format along
with checks, if any.
NPL Non-Positional Attributes are propagated to the IFILE File based on the Description. Following Table
summarises the use of Non-Positional Attributes.
(IFILE, <blk>: <code>) ifile.yml The <code> is added to the <blk> block in the IFILE.
7.1.2.1 Initialization
The NPL writer may specify initialization for different logical tables, strength tables or any other element inside.
Example
NPL:
Output IFILE:
INIT:
0: |-
lt ing_l3_next_hop_1_table nhop_index_1=0 dvp=0x0 l3_oif_1=0x0
1: |-
pt IFTA150_SBR_PROFILE_TABLE_0_INDEX=10 strength=0x5
2: |-
lt _SYMBOL=TIMESTAMP _INDEX=10 data=0x5
3: |-
pt EPOST_FMT_AUX_BOTP_IN_DATA mtu_drop=NPL_EGR_MTU_DROP_ENUM
7.1.2.2 Relational
The NPL writer may want to specify additional functionality related to an NPL symbol or call.
Example
NPL:
<! IFILE, REL: ”ecmp_level0:npl_ecmp_level0_member_table” !>
Output IFILE:
REL:
0: |-
ecmp_level0:npl_ecmp_level0_member_table
#ifdef XYZ
#define CPU_PORT 5
#define MAC_ADDR_BCAST 0xffffffffffff
7.3 Comments
NPL allows 2 kinds of comments:
● Multi line - Use /* and */ at beginning and end of the comment. They can be multi-line or in-line.
● Single line - Use // at the beginning of the comment.
7.4 Print
NPL uses print construct to print value of any variable in the program. print command is translated to the
Behavioral C Model. It does not have significance from compilation point of view. print invocation is called in
the C model in the same sequence as in an NPL program. Only fields can be printed, no structs. Internally,
print command works in a similar way as the C printf command.
Ingress Egress
Parser - NPL Parser Constructs define and modify the behavior of the HW Parser Block.
Match Action - NPL Logical Tables and Logical Register define and modify the behavior of the Match Action
Blocks.
Processing Unit - This is a generic target specific processing element. NPL functions, strength resolve define
and modify the behavior of this block.
Editor - NPL editor constructs define the behavior of the editor block.
A target may choose to have multiple of each of these units and in any order.
NPL allows struct nesting. However, depending on the different use cases for the structs there may be valid or
invalid use cases. They are marked below.
packet.struct.struct.field Y packet.group.header.field
packet.struct.struct Y packet.group.header
packet.struct.field N no group/header
packet.field N packet must have group/header.
no packet N
9.2 Function
Functions with arguments are not supported. Functions must operate on logical bus and packet data.
2. Overlay fields can be defined on Base Fields of type struct. (i.e., fields specified as struct in the bus).
However partial overlay of the struct is not allowed.
3. Overlays can overlap.
4. Overlay fields can NOT be defined on other overlay fields.
5. Overlay fields can NOT be of type struct.
Example
a = b[7:4];
if (b[5:3])...
obj_bus.a[5:4] = ing_pkt.ipv4.ecn; // mostly in function, action.
a = b[0:0]; // single bit access uses range of one bit
Use Cases
1. if (a[5:3]) //supported
2. a[5:3] = b[5:3]; //supported
● If the comparison is with "register value" only the (==) operator is supported.
The lexer tokenizes identifiers (ID) for user defined names by the regular expression “[A-Za-z_][\w_]*”.
Decimal constants must be natural numbers. Hex constants are as usual (e.g., 0x0f, 0X0f, 0x0F, etc.). Width
constants are like hex constants with printf-style sizes (e.g., 8x00). String constants must be enclosed in
double quotes and not contain a new line (e.g., “foo.bar”).
Identifiers:
primary_types : constant
| identifier
| STR_CONST /* r'\"([^\\\n]|(\\.))*?\"' */
constant : | DEC_CONST /* ([0-9][0-9]*) */
| HEX_CONST /* (0[xX][0-9A-Fa-f]+) */
identifier : ID
dir : IN
| OUT
Declarations
npl_node : npl_declaration_specifier
| empty
npl_declaration_specifier : npl_declaration
| npl_declaration_specifier npl_declaration
npl_declaration : struct_definition_specifier
| packet_definition_specifier
| strength_definition_specifier
| bus_definition_specifier
| sp_definition_specifier
| function_definition_specifier
| special_func_defintion_specifier
| enum_defintiion_specifier
| program_definition_specifier
| table_definition_specifier
| register_definition_specifier
| parsernode_definition_specifier
| print_command
| generic_block
field_declaration_list : declaration_expn
| field_declaration_list declaration_expn
Overlay
overlay_declarator : OVERLAYS ‘{’ overlay_declaration_list ‘}’
overlay_declaration_list : overlay_expression
| overlay_declaration_list overlay_expression
Signal Concatenation
concat_format_list : concat_format
| concat_format_list CONCAT concat_format
concat_format : identifier
| identifier range_access_format
Structure
struct_definition_specifier : STRUCT identifier ‘{’ struct_body ‘}’
| STRUCT identifier ‘{’field_declaration_list ‘}’
| STRUCT ‘{’struct_body‘}’
Enumerator
enum_defintiion_specifier : ENUM postfix_expression args_list_format
Bus declaration
bus_definition_specifier : BUS identifier identifier ‘;’
Table declaration
table_definition_specifier : LOGICAL_TABLE identifier ‘{’ table_body_block ‘}’
table_type
table_body_block : table_body
| table_body_block table_body
| key_declarator
| field_declarator
| KEY_CONSTRUCT key_construct_definition_block
| FIELDS_ASSIGN fields_assign_definition_block
| MAXSIZE ‘:’ constant ‘;’
| MINSIZE ‘:’ constant ‘;’
table_keys_list : table_keys_expression
| table_keys_list table_keys_expression
Register Declaration
register_definition_specifier : LOGICAL_REGISTER identifier ‘{’ field_declarator ‘}’
Packet Declaration
packet_definition_specifier : packet_instance
Strength
strength_definition_specifier : strength_instance
Statements Block
generic_statement_list : generic_block
| generic_statement_list generic_block
generic_block : statement
statement : expression_statement
| select_statement
| compound_statement
| label_statement
| header_command
| parser_statement
| pragma_call
Conditional Statements
select_statement : IF ‘(’ expression ‘)’ statement ELSE statement
Expressions
expression_statement : expression ‘;’
expression : assignment_expression
| lookup_statement
| parse_init
| function_call
assignment_expression : generic_expression
| generic_expression assignment_operator assignment_expression
generic_expression : binary_expression
binary_expression : unary_expression
| function_call
| binary_expression ‘!=’ binary_expression
| binary_expression ‘==’ binary_expression
| binary_expression ‘&’ binary_expression
| binary_expression ‘<’ binary_expression
| binary_expression ‘<=’ binary_expression
| binary_expression ‘>=’ binary_expression
| binary_expression ‘>’ binary_expression
| binary_expression ‘|’ binary_expression
| binary_expression ‘^’ binary_expression
| binary_expression ‘&&’ binary_expression
| binary_expression ‘||’ binary_expression
| binary_expression ‘<<’ binary_expression
| binary_expression ‘>>’ binary_expression
| binary_expression ‘*’ binary_expression
| binary_expression ‘+’ binary_expression
| binary_expression ‘-’ binary_expression
| binary_expression ‘/’ binary_expression
| binary_expression ‘%’ binary_expression
| binary_expression ‘<>’ binary_expression
unary_operator : ‘~’
| ‘!’
| ‘|’
| ‘&’
assignment_operator : ‘==’
primary_expression : primary_types
| metainfo
| header_position
| profile_type
postfix_expression : primary_expression
| postfix_expression ‘.’ identifier
| postfix_expression ‘.’ metainfo
| postfix_expression array_access_format
| postfix_expression range_access_format
Program
program_definition_specifier : PROGRAM postfix_expression ‘{’ generic_statement_list ‘}’
| PROGRAM postfix_expression ‘{’ ‘}’
Parser
parsernode_definition_specifier : PARSER_NODE identifier ‘{’ generic_statement_list ‘}’
parser_statement : next_node
| root_node
| parsing_done
| parser_field_extract
Table lookup
lookup_statement : LOOKUP args_access_format
Function
function_call : postfix_expression ‘(’ ‘)’
| postfix_expression args_access_format
func_def_args : args_format_specifier
| empty
function_code_block : statement
| function_code_block statement
Special Function
sp_definition_specifier : SFC postfix_expression postfix_expression ‘;’
special_func_def_list : special_func_def
| special_func_def_list special_func_def
Function Arguments
args_access_format : ‘(’ args_format_specifier ‘)’
args_format_specifier : args_type_specifier
| args_format_specifier ‘,’ args_type_specifier
| args_list_format
| args_format_specifier ‘,’ args_list_format
| postfix_expression
| args_format_specifier ‘,’ postfix_expression
| args_size_dir
| args_format_specifier ‘,’ args_size_dir
args_size_multi : args_size
| args_size_multi ‘,’ args_size
Print Command
print_command : PRINTLN ‘(’ STR_CONST ‘)’
Pragmas
pragma_call : directive NPL_PRAGMA pragma_access_format
directive : PRAGMA
12.1 Directives
Directives assist in specifying desired behavior which may have hardware dependencies. Directives assist in
BE Mapping.
Rules:
● Directives may be specified in the NPL logic file or in a separate file.
● There is no positioning associated with directives.
● Directives must start with newline.
● Use keyword, "null", in case there is no object associated with a directive or multiple objects require a
directive.
Example
Say in an application user wants to define fixed bus here is how to specify it -
function vlan_assign_functions()
@NPL_PRAGMA(vlan_assign_functions, mapping:"hw_proc_block_20")
Packet Drop
bus.field_name Specify the Field Name which if set means the packet
must be dropped.
This can be: <bus.field>
Within logical_table fields_assign(), this must be a
<table_field>.
drop_code Specify a constant associated with this drop reason.
This can be: constant/enum
● Valid values: 0-255
● Value of 0 means, No Drop Code.
packet_drop(
<bus.field_name>, // signal name on which user wants to connect drop
<drop_code>, // constant drop opcode
<strength> // Priority for drop opcode
);
Packet Trace
packet_trace(
<bus.field_name>, // signal name on which user wants to connect trace
<trace_code> // constant trace opcode
);
Packet Count
packet_count(
<bus.field_name>, // signal name on which user wants to connect count
<counter_id> // constant counter id
);
Example
The following is an example of count, trace and drop. The following is needed:
● Drop all IPV4 packets with ttl as 0; used drop_code 11 and priority 5.
● Trace all IPV4 packets with ttl as 1
● Count all IPV4 packets with ttl as 2
struct cond_bus_s {
bit ttl_0;
bit ttl_1;
bit ttl_2;
function func_ttl_proc () {
if (header_ipv4.ttl == 0) {
cond_bus.ttl_0 = 1;
}
if (header_ipv4.ttl == 1) {
cond_bus.ttl_1 = 1;
}
if (header_ipv4.ttl == 2) {
cond_bus.ttl_2 = 1;
}
packet_drop(cond_bus.ttl_0, TTL0, 5);
packet_trace(cond_bus.ttl_1, 3);
packet_count(cond_bus.ttl_2, 1);
}
program ipv4() {
...
func_ttl_proc();
...
}