Introduction to Network Programing Language (NPL)

Venkat Pullela

Venkat Pullela

Network programming Language (NPL) is designed to specify packet processing pipelines that can be efficiently implemented on different architectures. Apart from basic constructs like parse, tables, re-write, NPL supports advanced constructs like multi-lookup logical tables, functions, strength etc. This is an introduction to the NPL language and a few important constructs.

Program specifies the switching pipeline control flow using all the NPL constructs. The program() provides sequential execution semantics to map the constructs on to the underlying pipeline. Target specific backend compilers may re-order and/or parallelize various blocks without changing the semantics of the program to efficiently use the underlying resources.

program l3_app() {

parse_begin (start);

port_table.lookup (0);



if (ing_pkt.l2_grp.l2._PRESENT) {




if (cmd_bus.l3_enable) {






Parse_begin() and parse_continue() allows support for multi-stage re-entrant parsing. A program() combines table lookups with functions to build an efficient switching pipeline.

Standard Data Types in NPL are bit, bit array and varbit. Bit is used to denote a single bit, and bit array is used to declare fields of fixed length. Variable length fields are declared with the maximum possible field width.

bit     cfi;          // single bit

bit[12] vid;          // 12 bit vid

varbit[64] options;    // up to 64b wide

Numbers can be specified in decimal and hex. A non-zero value indicates true and a zero indicates false. All the typical arithmetic and Boolean operators are supported to form expressions.

a = 5;

ipv4.ttl = 0xF;

ipv6.dip = 0x01234567;

if (ipv4.protocol == 0x23)

User defined Type Struct is used to define aggregate data types. It is used to aggregate fields in to a header, form a header group with headers, to define a packet consisting of header groups and a bus with fields.

struct vlan_t {      // Header definition

fields {

bit[16]  tci;

bit[16]  ethertype;


overlays {

pcp : tci[15:13];

dei : tci[12:12];

vid : tci[11:0];

full_tag : tci <> ethertype;



struct l2_group_t {       // Header Group

fields {

l2_t    l2;

vlant_t vlan;



struct l3_packet_t {            // Packet

fields {

l2_group_t l2_grp;

l3_group_t l3_grp;



packet l3_packet_t ingress_pkt;

bus cmd_bus_t cmd_bus;      // Global Bus

Overlays and concatenation (<> operator) helps power users in using bus and other resources efficiently.

Bus is used to connect different components in a pipeline. Explicitly supporting a logical bus construct helps pipeline designers to think in their own language, especially since NPL is a domain specific language.

Logical table allows specifying user view of the underlying physical tables. Actions are separated from logical tables. This makes them lightweight and improves the throughput. This also moves switching logic out of the table scope to global scope to help common design patterns such as concurrent features. Logical table provides a dis-aggregated view of the Match-Action tables. It is dis-aggregated in to lookup, result, processing and actions to the reduce dependencies and improve the scope for parallelization. Smaller primitives also make the programming paradigm more natural and intuitive instead of force fitting to a single universal primitive.

Table attributes like size, table type attributes may be specified so that appropriate memory like TCAM or SRAM is allocated to the table from common memory.

Table attributes like size, table type attributes may be specified so that appropriate memory like TCAM or SRAM is allocated to the table from common memory.

Logical table supports multiple lookups of the same table. Key_construct() method can be used to specify a custom key for each of the lookups. Specifying conditionals allows different handling of different lookups and results.

key_construct() {

if (_LOOKUP0==1) {

mac = ing_pkt.l2_grp.l2.da;


if (_LOOKUP1==1) {

mac =;



fields_assign() {

if (_LOOKUP0==1) { //e.g. Entry 100

obj_bus.dst = port;

obj_bus.dst_discard = d_discard;


if (_LOOKUP1==1) { //e.g. Entry 200

temp_bus.src_port = port;

obj_bus.src_discard = s_discard;



Fields_assign() method is used to specify how the results are processed.

Function primitive isused to specify switching logic. Packet processing involves not only data processing of the packet fields and metadata, it is also a decision making process. Function primitive is used to implement the decision trees. Functions are also used to implement logical and arithmetic operations.

function do_l3_forwarding() {

local_var.no_l3_switch = 0;

cmd_bus.l3_routable = 0;

if (cmd_bus.do_l3_lookup &

cmd_bus.my_stn_routing_enable) {

if ((ingress_pkt.ipv4.ttl == 0) &&

(obj_bus.local_address == 0)) {

local_var.no_l3_switch = 1;


if ((ingress_pkt.ipv4.ttl == 1) &&

(obj_bus.local_address == 0)) {

local_var.no_l3_switch = 1;


if (ingress_pkt.ipv4.option != 0) {

local_var.no_l3_switch = 1;


if (local_var.no_l3_switch == 0) {

// Output to global bus

cmd_bus.l3_routable = 1;




Strength construct is used in prioritization and to resolve object dependencies in the decision making process. This allows multiple tables to be looked up in parallel and resolving the result objects based on priority. Profile tables are used to changing the priority of objects at runtime.

Strength resolution allows building a decision tree in multiple steps across the pipeline by combining bus objects with table results as well as carrying the result objects over the bus.

Strength Construct Diagram


local_bus.cos,          // output field

local_bus.cos_strength, // bus strength

{ pri_tbl._LOOKUP0,     // table 1

NULL, profile_tbl.cos_strength,

pri_tbl.cos},       // table 2

{ dscp_tbl._LOOKUP0, NULL,



Editor constructs are used to make modifications of the packet. To make sure the modifications produce a valid packet, editor constructs allow the use of the headers from the parse tree only.






Special Functions are used for optimal implementation of common pipeline primitives. The functionality of these blocks is programmable. NPL supports flexible selection of inputs and outputs to the special functions. Special functions also support usage mode to select the inputs and outputs at runtime, instead of at compile time.

Network Programming Language (NPL) provides a rich set of constructs to efficiently specify and implement efficient packet processing pipelines. NPL also defined specific constructs to help support parallelization of processing to concurrently support many features at the same time.

Go to for a detailed discussion and specification of NPL.

Why NPL?

Network Programming Language (NPL)

Mohan Kalkunte

With the decoupling of the control plane from the data plane, it is now possible to configure the network elements from a SDN Controller. OpenFlow was the first attempt to describe the data plane of a switch using table semantics. In the last few years, there has been effort to define the data plane functionality in a high-level language. One such language is P4 which is an open source language which continues to evolve.

At Broadcom, network switches have evolved from a fixed function to highly configurable devices. These configurable devices provide significant number of features with a highly efficient pipeline architecture. Broadcom’s Trident family of switches provides a feature rich set of capabilities for Enterprise and Campus data center. The Trident 3 series family is semi-programmable with a tool chain that is factory enabled. This allows Broadcom to introduce new features with a different flex code and not incur the long ASIC development cycle.

The DNX line of devices have long supported programmability starting from the Petra devices since 2010. With every generation of the DNX packet processor, the programmability aspect of the pipeline has improved with Jericho2 device being fully programmable.


Figure: XGS/DNX Device Programmability Progression

Following Trident 3, the next step in the XGS family was to build a highly efficient and programmable pipeline for data plane and instrumentation which led to the recently announced Trident 4. Existing languages did not provide the rich construct needed to express XGS and DNX architectures. Expressing the intent of the data plane for efficient packet processing led us to develop a new programming language called NPL (Network Programming Language) that can efficiently address multiple silicon architectures for switching, routing and appliances.

The key goal of NPL was to have a rich set of constructs to enable highly efficient pipeline architecture. By this we mean the cost of providing programmability over a fixed function should be minimal for the same feature set and table scale. In addition, the need to define the language constructs at a faster pace without being tied to an external standards organization was also a key concern.

NPL at a high level provides the following capabilities: 1) Basic Constructs 2) Advanced Constructs 3) Special functions 4) Instrumentation and 5) Runtime programmability.

Figure: NPL Constructs

Figure: NPL Constructs

Decoupling the action from table lookup and using functions to express complex logic makes for efficient silicon implementation. Key advanced constructs such as multiple table lookups and executing them in parallel is important to deliver a low latency pipeline while delivering high feature capacity. The ability to resolve multiple table lookup decisions based on a programmable strength gives user the freedom to express the capability in a flexible manner. Special functions are like engines which are hardware optimized for specialized functions such as hashing, LAG resolution etc. The inputs to these functions can be defined in NPL by the user and the outputs can be flexibly interpreted by the user and can be expressed in NPL. Runtime programmability is especially important for instrumentation which allows to dynamically attach objects of interest to counters at runtime.

The NPL language is completely open and more information can be found at The NPL front-end tool chain is available on GitHub. Currently, NPL is supported on Trident 4 and Jericho 2 devices.