diff --git a/docs/P4-16-working-spec.html b/docs/P4-16-working-spec.html index d5ebf636cf..655c12d748 100644 --- a/docs/P4-16-working-spec.html +++ b/docs/P4-16-working-spec.html @@ -1,758 +1,1328 @@ - + - - - - - P4~16~ Language Specification - - - - - -
-
-
-
P416 Language Specification
-
version 1.2.5
-
-
-
The P4 Language Consortium
-
2024-11-06
-
-

Abstract. P4 is a language for programming the data plane of network -devices. This document provides a precise definition of the P416 + + + + +P416 Language Specification : The P4 Language Consortium + + + +

+
+
+
+
+
Abstract
+
+P4 is a language for programming the data plane of network +devices. This document provides a precise definition of the P416 language, which is the 2016 revision of the P4 language -(http://​p4.​org). The target audience for this document includes +(http://p4.org). The target audience for this document includes developers who want to write compilers, simulators, IDEs, and debuggers for P4 programs. This document may also be of interest to P4 programmers who are interested in understanding the syntax and -semantics of the language at a deeper level.

-

1. Scope

-

This specification document defines the structure and interpretation -of programs in the P416 language. It defines the syntax, semantic +semantics of the language at a deeper level. + +

+ +
+
+
+

1. Scope

+
+
+

This specification document defines the structure and interpretation +of programs in the P416 language. It defines the syntax, semantic rules, and requirements for conformant implementations of the -language. -

-

It does not define: -

-
    -
  • Mechanisms by which P4 programs are compiled, loaded, and executed -on packet-processing systems, -
  • -
  • Mechanisms by which data are received by one packet-processing -system and delivered to another system, -
  • -
  • Mechanisms by which the control plane manages the match-action -tables and other stateful objects defined by P4 programs, -
  • -
  • The size or complexity of P4 programs, -
  • -
  • The minimal requirements of packet-processing systems that are -capable of providing a conformant implementation. -
- -

It is understood that some implementations may be unable to implement +language.

+
+
+

It does not define:

+
+
+
    +
  • +

    Mechanisms by which P4 programs are compiled, loaded, and executed +on packet-processing systems,

    +
  • +
  • +

    Mechanisms by which data are received by one packet-processing +system and delivered to another system,

    +
  • +
  • +

    Mechanisms by which the control plane manages the match-action +tables and other stateful objects defined by P4 programs,

    +
  • +
  • +

    The size or complexity of P4 programs,

    +
  • +
  • +

    The minimal requirements of packet-processing systems that are +capable of providing a conformant implementation.

    +
  • +
+
+
+

It is understood that some implementations may be unable to implement the behavior defined here in all cases, or may provide options to eliminate some safety guarantees in exchange for better performance or handling larger programs. They should document where they deviate -from this specification. -

2. Terms, definitions, and symbols

-

Throughout this document, the following terms will be used: -

-
    -
  • Architecture: A set of P4-programmable components and the data -plane interfaces between them. -
  • -
  • Control plane: A class of algorithms and the corresponding input +from this specification.

    +
+
+
+
+

2. Terms, definitions, and symbols

+
+
+

Throughout this document, the following terms will be used:

+
+
+
    +
  • +

    Architecture: A set of P4-programmable components and the data +plane interfaces between them.

    +
  • +
  • +

    Control plane: A class of algorithms and the corresponding input and output data that are concerned with the provisioning and -configuration of the data plane. +configuration of the data plane.

  • -
  • Data plane: A class of algorithms that describe transformations -on packets by packet-processing systems. +
  • +

    Data plane: A class of algorithms that describe transformations +on packets by packet-processing systems.

  • -
  • Metadata: Intermediate data generated during execution of a P4 -program. +
  • +

    Metadata: Intermediate data generated during execution of a P4 +program.

  • -
  • Packet: A network packet is a formatted unit of data carried by -a packet-switched network. +
  • +

    Packet: A network packet is a formatted unit of data carried by +a packet-switched network.

  • -
  • Packet header: Formatted data at the beginning of a packet. A +
  • +

    Packet header: Formatted data at the beginning of a packet. A given packet may contain a sequence of packet headers representing -different network protocols. +different network protocols.

  • -
  • Packet payload: Packet data that follows the packet headers. +
  • +

    Packet payload: Packet data that follows the packet headers.

  • -
  • Packet-processing system: A data-processing system designed +
  • +

    Packet-processing system: A data-processing system designed for processing network packets. In general, packet-processing -systems implement control plane and data plane algorithms. + systems implement control plane and data plane algorithms.

  • -
  • Target: A packet-processing system capable of executing a P4 -program. -
- -

All terms defined explicitly in this document should not be understood +

  • +

    Target: A packet-processing system capable of executing a P4 +program.

    +
  • + +
    +
    +

    All terms defined explicitly in this document should not be understood to refer implicitly to similar terms defined elsewhere. Conversely, any terms not defined explicitly in this document should be interpreted according to -generally recognizable sourcese.g., IETF RFCs. -

    3. Overview

    -
    -

    prgswitch -

    -
    - -
    Figure 1. Traditional switches vs. programmable switches.
    -

    P4 is a language for expressing how packets are processed by the data +generally recognizable sources---e.g., IETF RFCs.

    +
    +
    +
    +
    +

    3. Overview

    +
    +
    +
    +prgswitch +
    +
    Figure 1. Traditional switches vs. programmable switches.
    +
    +
    +

    P4 is a language for expressing how packets are processed by the data plane of a programmable forwarding element such as a hardware or software switch, network interface card, router, or network appliance. The name P4 comes from the original paper that introduced -the language, “Programming Protocol-independent Packet Processors,” -https://​arxiv.​org/​pdf/​1312.​1719.​pdf. While P4 was initially designed +the language, "Programming Protocol-independent Packet Processors," +https://arxiv.org/pdf/1312.1719.pdf. While P4 was initially designed for programming switches, its scope has been broadened to cover a large variety of devices. In the rest of this document we use the -generic term target for all such devices. -

    -

    Many targets implement both a control plane and a data plane. P4 is +generic term target for all such devices.

    +
    +
    +

    Many targets implement both a control plane and a data plane. P4 is designed to specify only the data plane functionality of the target. P4 programs also partially define the interface by which the control plane and the data-plane communicate, but P4 cannot be used to describe the control-plane functionality of the target. In the rest of -this document, when we talk about P4 as “programming a target”, we -mean “programming the data plane of a target”. -

    -

    As a concrete example of a target, Figure 1 illustrates +this document, when we talk about P4 as "programming a target", we +mean "programming the data plane of a target".

    +
    +
    +

    As a concrete example of a target, Figure 1 illustrates the difference between a traditional fixed-function switch and a P4-programmable switch. In a traditional switch the manufacturer defines the data-plane functionality. The control-plane controls the data plane by managing entries in tables (e.g. routing tables), configuring specialized objects (e.g. meters), and by processing control-packets (e.g. routing protocol packets) or asynchronous -events, such as link state changes or learning notifications. -

    -

    A P4-programmable switch differs from a traditional switch in two -essential ways: -

    -
      -
    • The data plane functionality is not fixed in advance but is defined +events, such as link state changes or learning notifications.

      +
    +
    +

    A P4-programmable switch differs from a traditional switch in two +essential ways:

    +
    +
    +
      +
    • +

      The data plane functionality is not fixed in advance but is defined by a P4 program. The data plane is configured at initialization time to implement the functionality described by the P4 program (shown by the long red arrow) and has no built-in knowledge of -existing network protocols. +existing network protocols.

    • -
    • The control plane communicates with the data plane using the same +
    • +

      The control plane communicates with the data plane using the same channels as in a fixed-function device, but the set of tables and other objects in the data plane are no longer fixed, since they are defined by a P4 program. The P4 compiler generates the API that the -control plane uses to communicate with the data plane. -

    - -

    Hence, P4 can be said to be protocol independent, but it enables +control plane uses to communicate with the data plane.

    + + +
    +
    +

    Hence, P4 can be said to be protocol independent, but it enables programmers to express a rich set of protocols and other data plane -behaviors. -

    -
    -

    p4prg -

    -
    - -
    Figure 2. Programming a target with P4.
    -

    -The core abstractions provided by the P4 language are: -

    -
      -
    • Header types describe the format (the set of fields and -their sizes) of each header within a packet. -
    • -
    • Parsers describe the permitted sequences of headers within +behaviors.

      +
    +
    +
    +p4prg +
    +
    Figure 2. Programming a target with P4.
    +
    +
    +

    The core abstractions provided by the P4 language are:

    +
    +
    +
      +
    • +

      Header types describe the format (the set of fields and +their sizes) of each header within a packet.

      +
    • +
    • +

      Parsers describe the permitted sequences of headers within received packets, how to identify those header sequences, and the -headers and fields to extract from packets. +headers and fields to extract from packets.

    • -
    • Tables associate user-defined keys with actions. P4 tables +
    • +

      Tables associate user-defined keys with actions. P4 tables generalize traditional switch tables; they can be used to implement routing tables, flow lookup tables, access-control lists, and other -user-defined table types, including complex multi-variable decisions. +user-defined table types, including complex multi-variable decisions.

    • -
    • Actions are code fragments that describe how packet header +
    • +

      Actions are code fragments that describe how packet header fields and metadata are manipulated. Actions can include data, which -is supplied by the control-plane at runtime. +is supplied by the control-plane at runtime.

    • -
    • Match-action units perform the following sequence of operations: - -
        -
      • Construct lookup keys from packet fields or computed metadata, +
      • +

        Match-action units perform the following sequence of operations:

        +
        +
          +
        • +

          Construct lookup keys from packet fields or computed metadata,

          +
        • +
        • +

          Perform table lookup using the constructed key, choosing an action +(including the associated data) to execute, and

          +
        • +
        • +

          Finally, execute the selected action.

        • -
        • Perform table lookup using the constructed key, choosing an action -(including the associated data) to execute, and +
        +
      • -
      • Finally, execute the selected action. -
    • -
    • Control flow expresses an imperative program that describes +
    • +

      Control flow expresses an imperative program that describes packet-processing on a target, including the data-dependent sequence of match-action unit invocations. Deparsing (packet reassembly) can -also be performed using a control flow. +also be performed using a control flow.

    • -
    • Extern objects are architecture-specific constructs that can be +
    • +

      Extern objects are architecture-specific constructs that can be manipulated by P4 programs through well-defined APIs, but whose internal behavior is hard-wired (e.g., checksum units) and hence not -programmable using P4. -

    • -
    • User-defined metadata: user-defined data structures associated -with each packet. -
    • -
    • Intrinsic metadata: metadata provided by the architecture -associated with each packete.g., the input port where a packet -has been received. -
    - -

    Figure 2 shows a typical tool workflow when programming a -target using P4. -

    -

    Target manufacturers provide the hardware or software implementation +programmable using P4.

    + +
  • +

    User-defined metadata: user-defined data structures associated +with each packet.

    +
  • +
  • +

    Intrinsic metadata: metadata provided by the architecture +associated with each packet---e.g., the input port where a packet +has been received.

    +
  • + +
    +
    +

    Figure 2 shows a typical tool workflow when programming a +target using P4.

    +
    +
    +

    Target manufacturers provide the hardware or software implementation framework, an architecture definition, and a P4 compiler for that target. P4 programmers write programs for a specific architecture, which defines a set of P4-programmable components on the target as -well as their external data plane interfaces. -

    -

    Compiling a set of P4 programs produces two artifacts: -

    -
      -
    • a data plane configuration that implements the forwarding logic -described in the input program and -
    • -
    • an API for managing the state of the data plane objects from the -control plane -
    - -

    P4 is a domain-specific language that is designed to be implementable +well as their external data plane interfaces.

    +
    +
    +

    Compiling a set of P4 programs produces two artifacts:

    +
    +
    +
      +
    • +

      a data plane configuration that implements the forwarding logic +described in the input program and

      +
    • +
    • +

      an API for managing the state of the data plane objects from the +control plane

      +
    • +
    +
    +
    +

    P4 is a domain-specific language that is designed to be implementable on a large variety of targets including programmable network interface cards, FPGAs, software switches, and hardware ASICs. As such, the language is restricted to constructs that can be efficiently -implemented on all of these platforms. -

    -

    Assuming a fixed cost for table lookup operations and interactions +implemented on all of these platforms.

    +
    +
    +

    Assuming a fixed cost for table lookup operations and interactions with extern objects, all P4 programs (i.e., parsers and controls) execute a constant number of operations for each byte of an input packet received and analyzed. Although parsers may contain loops, @@ -763,112 +1333,141 @@ size of the state accumulated while processing data (e.g., the number of flows, or the total number of packets processed). These guarantees are necessary (but not sufficient) for enabling fast packet processing -across a variety of targets. -

    -

    P4 conformance of a target is defined as follows: if a specific +across a variety of targets.

    +
    +
    +

    P4 conformance of a target is defined as follows: if a specific target T supports only a subset of the P4 programming language, say -P4T, programs written in P4T executed on the target should provide +P4T, programs written in P4T executed on the target should provide the exact same behavior as is described in this document. Note that P4 -conformant targets can provide arbitrary P4 language extensions and extern -elements. -

    3.1. Benefits of P4

    -

    Compared to state-of-the-art packet-processing systems (e.g., based on +conformant targets can provide arbitrary P4 language extensions and extern +elements.

    +
    +
    +

    3.1. Benefits of P4

    +
    +

    Compared to state-of-the-art packet-processing systems (e.g., based on writing microcode on top of custom hardware), P4 provides a number of -significant advantages: -

    -
      -
    • Flexibility: P4 makes many packet-forwarding policies +significant advantages:

      +
    +
    +
      +
    • +

      Flexibility: P4 makes many packet-forwarding policies expressible as programs, in contrast to traditional switches, which -expose fixed-function forwarding engines to their users. +expose fixed-function forwarding engines to their users.

    • -
    • Expressiveness: P4 can express sophisticated, +
    • +

      Expressiveness: P4 can express sophisticated, hardware-independent packet processing algorithms using solely general-purpose operations and table look-ups. Such programs are portable across hardware targets that implement the same -architectures (assuming sufficient resources are available). +architectures (assuming sufficient resources are available).

    • -
    • Resource mapping and management: P4 programs describe storage +
    • +

      Resource mapping and management: P4 programs describe storage resources abstractly (e.g., IPv4 source address); compilers map such user-defined fields to available hardware resources and manage -low-level details such as allocation and scheduling. +low-level details such as allocation and scheduling.

    • -
    • Software engineering: P4 programs provide important benefits -such as type checking, information hiding, and software reuse. +
    • +

      Software engineering: P4 programs provide important benefits +such as type checking, information hiding, and software reuse.

    • -
    • Component libraries: Component libraries supplied by manufacturers +
    • +

      Component libraries: Component libraries supplied by manufacturers can be used to wrap hardware-specific functions into portable -high-level P4 constructs. +high-level P4 constructs.

    • -
    • Decoupling hardware and software evolution: Target manufacturers +
    • +

      Decoupling hardware and software evolution: Target manufacturers may use abstract architectures to further decouple the evolution of -low-level architectural details from high-level processing. -

    • -
    • Debugging: Manufacturers can provide software models of an -architecture to aid in the development and debugging of P4 programs. -
    -

    3.2. P4 language evolution: comparison to previous versions (P4 v1.0/v1.1)

    -
    -

    p4transition -

    -
    - -
    Figure 3. Evolution of the language between versions P414 (versions 1.0 and 1.1) and P416.
    -

    Compared to P414, the earlier version of the language, P416 makes +low-level architectural details from high-level processing.

    + +
  • +

    Debugging: Manufacturers can provide software models of an +architecture to aid in the development and debugging of P4 programs.

    +
  • + +
    +
    +
    +

    3.2. P4 language evolution: comparison to previous versions (P4 v1.0/v1.1)

    +
    +
    +p4transition +
    +
    Figure 3. Evolution of the language between versions P414 (versions 1.0 and 1.1) and P416.
    +
    +
    +

    Compared to P414, the earlier version of the language, P416 makes a number of significant, backwards-incompatible changes to the syntax and semantics of the language. The evolution from the previous version -(P414) to the current one (P416) is depicted in Figure -3. In particular, a large number of language +(P414) to the current one (P416) is depicted in +Figure 3. In particular, a large number of language features have been eliminated from the language and moved into -libraries including counters, checksum units, meters, etc. -

    -

    Hence, the language has been transformed from a complex language (more than 70 +libraries including counters, checksum units, meters, etc.

    +
    +
    +

    Hence, the language has been transformed from a complex language (more than 70 keywords) into a relatively small core language (less than 40 keywords, shown in -Section B) accompanied by a library of fundamental -constructs that are needed for writing most P4. -

    -

    The v1.1 version of P4 introduced a language construct called extern that +Appendix B) accompanied by a library of fundamental +constructs that are needed for writing most P4.

    +
    +
    +

    The v1.1 version of P4 introduced a language construct called extern that can be used to describe library elements. Many constructs defined in the v1.1 language specification will thus be transformed into such library elements (including constructs that have been eliminated -from the language, such as counters and meters). Some of these extern objects +from the language, such as counters and meters). Some of these extern objects are expected to be standardized, and they will be in the scope of a future document describing a standard library of P4 elements. In -this document we provide several examples of extern constructs. -P416 also introduces and repurposes some v1.1 language +this document we provide several examples of extern constructs. +P416 also introduces and repurposes some v1.1 language constructs for describing the programmable parts of an -architecture. These language constructs are: parser, state, control, and package. -

    -

    One important goal of the P416 language revision is to provide a -stable language definition. In other words, we strive to ensure that -all programs written in P416 will remain syntactically correct and +architecture. These language constructs are: parser, state, control, and package.

    +
    +
    +

    One important goal of the P416 language revision is to provide a +stable language definition. In other words, we strive to ensure that +all programs written in P416 will remain syntactically correct and behave identically when treated as programs for future versions of the language. Moreover, if some future version of the language requires breaking backwards compatibility, we will seek to provide an easy path -for migrating P416 programs to the new version. -

    4. Architecture Model

    -
    -

    p4interface -

    -
    - -
    Figure 4. P4 program interfaces.
    -

    The P4 architecture identifies the P4-programmable blocks (e.g., +for migrating P416 programs to the new version.

    +
    +
    +
    +
    +
    +

    4. Architecture Model

    +
    +
    +
    +p4interface +
    +
    Figure 4. P4 program interfaces.
    +
    +
    +

    The P4 architecture identifies the P4-programmable blocks (e.g., parser, ingress control flow, egress control flow, deparser, etc.) and their -data plane interfaces. -

    -

    The P4 architecture can be thought of as a contract between the +data plane interfaces.

    +
    +
    +

    The P4 architecture can be thought of as a contract between the program and the target. Each manufacturer must therefore provide both a P4 compiler as well as an accompanying architecture definition for their target. (We expect that P4 compilers can share a common front-end that handles all architectures). The architecture definition does not have to expose the entire programmable surface of -the data planea manufacturer may even choose to provide multiple +the data plane---a manufacturer may even choose to provide multiple definitions for the same hardware device, each with different -capabilities (e.g., with or without multicast support). -

    -

    Figure 4 illustrates the data plane interfaces between P4-programmable -blocks. It shows a target that has two programmable blocks (#1 and #2). +capabilities (e.g., with or without multicast support).

    +
    +
    +

    Figure 4 illustrates the data plane interfaces between P4-programmable +blocks. It shows a target that has two programmable blocks (#1 and #2). Each block is programmed through a separate fragment of P4 code. The target interfaces with the P4 program through a set of control registers or signals. Input controls provide information to P4 @@ -876,11 +1475,12 @@

    intrinsic metadata. +intrinsic metadata. P4 programs can also store and manipulate data pertaining to each -packet as user-defined metadata. -

    -

    The behavior of a P4 program can be fully described in terms of +packet as user-defined metadata.

    +

    +
    +

    The behavior of a P4 program can be fully described in terms of transformations that map vectors of bits to vectors of bits. To actually process a packet, the architecture model interprets the bits that the P4 program writes to intrinsic metadata. @@ -888,820 +1488,1029 @@

    “drop” bit into another dedicated control register. Note that the details of -how intrinsic metadata are interpreted is architecture-specific. -

    -
    -

    p4checksum -

    -
    - -
    Figure 5. P4 program invoking the services of a fixed-function object.
    -

    -P4 programs can invoke services implemented by extern objects and functions provided by the architecture. -Figure 5 depicts a P4 program invoking the services of a +"drop" bit into another dedicated control register. Note that the details of +how intrinsic metadata are interpreted is architecture-specific.

    +

    +
    +
    +p4checksum +
    +
    Figure 5. P4 program invoking the services of a fixed-function object.
    +
    +
    +

    P4 programs can invoke services implemented by extern objects and functions provided by the architecture. +Figure 5 depicts a P4 program invoking the services of a built-in checksum computation unit on a target. The implementation of the checksum unit is not specified in P4, but its interface is. In general, the interface for -an extern object describes each operation it provides, as well as their parameter and return types. -

    -

    In general, P4 programs are not expected to be portable across +an extern object describes each operation it provides, as well as their parameter and return types.

    +
    +
    +

    In general, P4 programs are not expected to be portable across different architectures. For example, executing a P4 program that broadcasts packets by writing into a custom control register will not function correctly on a target that does not have the control register. However, P4 programs written for a given architecture should be portable across all targets that faithfully implement the -corresponding model, provided there are sufficient resources. -

    4.1. Standard architectures

    -

    We expect that the P4 community will evolve a small set of standard architecture +corresponding model, provided there are sufficient resources.

    +
    +
    +

    4.1. Standard architectures

    +
    +

    We expect that the P4 community will evolve a small set of standard architecture models pertaining to specific verticals. Wide adoption of such standard architectures will promote portability of P4 programs across different targets. However, defining these -standard architectures is outside of the scope of this document. -

    4.2. Data plane interfaces

    -

    To describe a functional block that can be programmed in P4, the +standard architectures is outside of the scope of this document.

    +
    +
    +
    +

    4.2. Data plane interfaces

    +
    +

    To describe a functional block that can be programmed in P4, the architecture includes a type declaration that specifies the interfaces between the block and the other components in the architecture. -For example, the architecture might contain a declaration such as the following: -

    -
    -
    -
    control MatchActionPipe<H>(in bit<4> inputPort,
    -                           inout H parsedHeaders,
    -                           out bit<4> outputPort);
    -

    This type declaration describes a block named MatchActionPipe +For example, the architecture might contain a declaration such as the following:

    +
    +
    +
    +
    control MatchActionPipe<H>(in bit<4> inputPort,
    +                           inout H parsedHeaders,
    +                           out bit<4> outputPort);
    +
    +
    +
    +

    This type declaration describes a block named MatchActionPipe that can be programmed using a data-dependent sequence of match-action -unit invocations and other imperative constructs (indicated by the control -keyword). The interface between the MatchActionPipe block and -the other components of the architecture can be read off from this declaration: -

    -
      -
    • The first parameter is a 4-bit value named inputPort. The -direction in indicates that this parameter is an input that -cannot be modified. -
    • -
    • The second parameter is an object of type H named parsedHeaders, -where H is a type variable representing the headers that will +unit invocations and other imperative constructs (indicated by the control +keyword). The interface between the MatchActionPipe block and +the other components of the architecture can be read off from this declaration:

      +
    +
    +
      +
    • +

      The first parameter is a 4-bit value named inputPort. The +direction in indicates that this parameter is an input that +cannot be modified.

      +
    • +
    • +

      The second parameter is an object of type H named parsedHeaders, +where H is a type variable representing the headers that will be defined later by the P4 programmer. -The direction inout indicates that this parameter is -both an input and an output. -

    • -
    • The third parameter is a 4-bit value named outputPort. The -direction out indicates that this parameter is an output -whose value is undefined initially but can be modified. -
    -

    4.3. Extern objects and functions

    -

    P4 programs can also interact with objects and functions provided by the architecture. -Such objects are described using the extern construct, which -describes the interfaces that such objects expose to the data-plane. -

    -

    An extern object describes a set of methods that are implemented +The direction inout indicates that this parameter is +both an input and an output.

    + +
  • +

    The third parameter is a 4-bit value named outputPort. The +direction out indicates that this parameter is an output +whose value is undefined initially but can be modified.

    +
  • + +
    +
    +
    +

    4.3. Extern objects and functions

    +
    +

    P4 programs can also interact with objects and functions provided by the architecture. +Such objects are described using the extern construct, which +describes the interfaces that such objects expose to the data-plane.

    +
    +
    +

    An extern object describes a set of methods that are implemented by an object, but not the implementation of these methods (i.e., it is similar to an abstract class in an object-oriented language). For example, the following construct could be used to describe the operations offered by an -incremental checksum unit: -

    -
    -
    -
    extern Checksum16 {
    -    Checksum16();              // constructor
    -    void clear();              // prepare unit for computation
    -    void update<T>(in T data); // add data to checksum
    -    void remove<T>(in T data); // remove data from existing checksum
    -    bit<16> get(); // get the checksum for the data added since last clear
    -}

    5. Example: A very simple switch

    -

    As an example to illustrate the features of architectures, consider +incremental checksum unit:

    +
    +
    +
    +
    extern Checksum16 {
    +    Checksum16();              // constructor
    +    void clear();              // prepare unit for computation
    +    void update<T>(in T data); // add data to checksum
    +    void remove<T>(in T data); // remove data from existing checksum
    +    bit<16> get(); // get the checksum for the data added since last clear
    +}
    +
    +
    +
    +
    +
    +
    +

    5. Example: A very simple switch

    +
    +
    +

    As an example to illustrate the features of architectures, consider implementing a very simple switch in P4. We will first describe the architecture of the switch and then write a complete P4 program that specifies the data plane behavior of the switch. This example -demonstrates many important features of the P4 programming language. -

    -
    -

    vssarch -

    -
    - -
    Figure 6. The Very Simple Switch (VSS) architecture.
    -

    -We call our architecture the “Very Simple Switch” (VSS). Figure -6 is a diagram of this architecture. There is nothing inherently -special about VSSit is just a pedagogical example that +demonstrates many important features of the P4 programming language.

    +
    +
    +
    +vssarch +
    +
    Figure 6. The Very Simple Switch (VSS) architecture.
    +
    +
    +

    We call our architecture the "Very Simple Switch" (VSS). +Figure 6 is a diagram of this architecture. There is nothing inherently +special about VSS---it is just a pedagogical example that illustrates how programmable switches can be described and programmed in P4. VSS has a number of fixed-function blocks (shown in cyan in our -example), whose behavior is described in Section 5.2. The -white blocks are programmable using P4. -

    -

    VSS receives packets through one of 8 input Ethernet ports, through a +example), whose behavior is described in Section 5.2. The +white blocks are programmable using P4.

    +
    +
    +

    VSS receives packets through one of 8 input Ethernet ports, through a recirculation channel, or from a port connected directly to the CPU. VSS has one single parser, feeding into a single match-action pipeline, which feeds into a single deparser. After exiting the deparser, packets are emitted through one of 8 output Ethernet ports -or one of 3 “special” ports: -

    -
      -
    • Packets sent to the “CPU port” are sent to the control plane -
    • -
    • Packets sent to the “Drop port” are discarded -
    • -
    • Packets sent to the “Recirculate port” are re-injected in the switch -through a special input port -
    - -

    The white blocks in the figure are programmable, and the user must +or one of 3 "special" ports:

    +
    +
    +
      +
    • +

      Packets sent to the "CPU port" are sent to the control plane

      +
    • +
    • +

      Packets sent to the "Drop port" are discarded

      +
    • +
    • +

      Packets sent to the "Recirculate port" are re-injected in the switch +through a special input port

      +
    • +
    +
    +
    +

    The white blocks in the figure are programmable, and the user must provide a corresponding P4 program to specify the behavior of each such block. The red arrows indicate the flow of user-defined data. The cyan blocks are fixed-function components. The green arrows are data plane interfaces used to convey information between the fixed-function -blocks and the programmable blocksexposed in the P4 program as -intrinsic metadata. -

    5.1. Very Simple Switch Architecture

    -

    The following P4 program provides a declaration of VSS in P4, as it +blocks and the programmable blocks---exposed in the P4 program as +intrinsic metadata.

    +
    +
    +

    5.1. Very Simple Switch Architecture

    +
    +

    The following P4 program provides a declaration of VSS in P4, as it would be provided by the VSS manufacturer. The declaration contains several type declarations, constants, and finally declarations for the three programmable blocks; the code uses syntax highlighting. The programmable blocks are described by their types; the implementation -of these blocks has to be provided by the switch programmer. -

    -
    -
    -
    // File "very_simple_switch_model.p4"
    -// Very Simple Switch P4 declaration
    -// core library needed for packet_in and packet_out definitions
    -# include <core.p4>
    -/* Various constants and structure declarations */
    -/* ports are represented using 4-bit values */
    -typedef bit<4> PortId;
    -/* only 8 ports are "real" */
    -const PortId REAL_PORT_COUNT = 4w8;  // 4w8 is the number 8 in 4 bits
    -/* metadata accompanying an input packet */
    -struct InControl {
    -    PortId inputPort;
    -}
    -/* special input port values */
    -const PortId RECIRCULATE_IN_PORT = 0xD;
    -const PortId CPU_IN_PORT = 0xE;
    -/* metadata that must be computed for outgoing packets */
    -struct OutControl {
    -    PortId outputPort;
    -}
    -/* special output port values for outgoing packet */
    -const PortId DROP_PORT = 0xF;
    -const PortId CPU_OUT_PORT = 0xE;
    -const PortId RECIRCULATE_OUT_PORT = 0xD;
    -/* Prototypes for all programmable blocks */
    -/**
    - * Programmable parser.
    - * @param <H> type of headers; defined by user
    - * @param b input packet
    - * @param parsedHeaders headers constructed by parser
    - */
    -parser Parser<H>(packet_in b,
    -                 out H parsedHeaders);
    -/**
    - * Match-action pipeline
    - * @param <H> type of input and output headers
    - * @param headers headers received from the parser and sent to the deparser
    - * @param parseError error that may have surfaced during parsing
    - * @param inCtrl information from architecture, accompanying input packet
    - * @param outCtrl information for architecture, accompanying output packet
    - */
    -control Pipe<H>(inout H headers,
    -                in error parseError,// parser error
    -                in InControl inCtrl,// input port
    -                out OutControl outCtrl); // output port
    -/**
    - * VSS deparser.
    - * @param <H> type of headers; defined by user
    - * @param b output packet
    - * @param outputHeaders headers for output packet
    - */
    -control Deparser<H>(inout H outputHeaders,
    -                    packet_out b);
    -/**
    - * Top-level package declaration - must be instantiated by user.
    - * The arguments to the package indicate blocks that
    - * must be instantiated by the user.
    - * @param <H> user-defined type of the headers processed.
    - */
    -package VSS<H>(Parser<H> p,
    -               Pipe<H> map,
    -               Deparser<H> d);
    -// Architecture-specific objects that can be instantiated
    -// Checksum unit
    -extern Checksum16 {
    -    Checksum16();              // constructor
    -    void clear();              // prepare unit for computation
    -    void update<T>(in T data); // add data to checksum
    -    void remove<T>(in T data); // remove data from existing checksum
    -    bit<16> get(); // get the checksum for the data added since last clear
    -}
    -

    Let us describe some of these elements: -

    -
      -
    • -

      The included file core.p4 is described in more detail in -Appendix D. It defines some standard -data-types and error codes. -

    • -
    • -

      bit<4> is the type of bit-strings with 4 bits. -

    • -
    • -

      The syntax 4w0xF indicates the value 15 represented using 4 -bits. An alternative notation is 4w15. In many circumstances -the width modifier can be omitted, writing just 15. -

    • -
    • -

      error is a built-in P4 type for holding error codes -

    • -
    • -

      Next follows the declaration of a parser: -

      -
      -
      -
      parser Parser<H>(packet_in b, out H parsedHeaders);
      -


      -This declaration describes the interface for a parser, but not yet its +of these blocks has to be provided by the switch programmer.

      +
    +
    +
    +
    // File "very_simple_switch_model.p4"
    +// Very Simple Switch P4 declaration
    +// core library needed for packet_in and packet_out definitions
    +# include <core.p4>
    +/* Various constants and structure declarations */
    +/* ports are represented using 4-bit values */
    +typedef bit<4> PortId;
    +/* only 8 ports are "real" */
    +const PortId REAL_PORT_COUNT = 4w8;  // 4w8 is the number 8 in 4 bits
    +/* metadata accompanying an input packet */
    +struct InControl {
    +    PortId inputPort;
    +}
    +/* special input port values */
    +const PortId RECIRCULATE_IN_PORT = 0xD;
    +const PortId CPU_IN_PORT = 0xE;
    +/* metadata that must be computed for outgoing packets */
    +struct OutControl {
    +    PortId outputPort;
    +}
    +/* special output port values for outgoing packet */
    +const PortId DROP_PORT = 0xF;
    +const PortId CPU_OUT_PORT = 0xE;
    +const PortId RECIRCULATE_OUT_PORT = 0xD;
    +/* Prototypes for all programmable blocks */
    +/**
    + * Programmable parser.
    + * @param <H> type of headers; defined by user
    + * @param b input packet
    + * @param parsedHeaders headers constructed by parser
    + */
    +parser Parser<H>(packet_in b,
    +                 out H parsedHeaders);
    +/**
    + * Match-action pipeline
    + * @param <H> type of input and output headers
    + * @param headers headers received from the parser and sent to the deparser
    + * @param parseError error that may have surfaced during parsing
    + * @param inCtrl information from architecture, accompanying input packet
    + * @param outCtrl information for architecture, accompanying output packet
    + */
    +control Pipe<H>(inout H headers,
    +                in error parseError,// parser error
    +                in InControl inCtrl,// input port
    +                out OutControl outCtrl); // output port
    +/**
    + * VSS deparser.
    + * @param <H> type of headers; defined by user
    + * @param b output packet
    + * @param outputHeaders headers for output packet
    + */
    +control Deparser<H>(inout H outputHeaders,
    +                    packet_out b);
    +/**
    + * Top-level package declaration - must be instantiated by user.
    + * The arguments to the package indicate blocks that
    + * must be instantiated by the user.
    + * @param <H> user-defined type of the headers processed.
    + */
    +package VSS<H>(Parser<H> p,
    +               Pipe<H> map,
    +               Deparser<H> d);
    +// Architecture-specific objects that can be instantiated
    +// Checksum unit
    +extern Checksum16 {
    +    Checksum16();              // constructor
    +    void clear();              // prepare unit for computation
    +    void update<T>(in T data); // add data to checksum
    +    void remove<T>(in T data); // remove data from existing checksum
    +    bit<16> get(); // get the checksum for the data added since last clear
    +}
    +
    +
    +
    +

    Let us describe some of these elements:

    +
    +
    +
      +
    • +

      The included file core.p4 is described in more detail in +Appendix D. It defines some standard +data-types and error codes.

      +
    • +
    • +

      bit<4> is the type of bit-strings with 4 bits.

      +
    • +
    • +

      The syntax 4w0xF indicates the value 15 represented using 4 +bits. An alternative notation is 4w15. In many circumstances +the width modifier can be omitted, writing just 15.

      +
    • +
    • +

      error is a built-in P4 type for holding error codes

      +
    • +
    • +

      Next follows the declaration of a parser:

      +
      +
      +
      +
      +
      parser Parser<H>(packet_in b, out H parsedHeaders);
      +
      +
      +
      +
      +
      +

      This declaration describes the interface for a parser, but not yet its implementation, which will be provided by -the programmer. The parser reads its input from a packet_in, which is +the programmer. The parser reads its input from a packet_in, which is a pre-defined P4 extern object that represents an incoming -packet, declared in the core.p4 library. The parser writes its -output (the out keyword) into the parsedHeaders -argument. The type of this argument is H, yet unknownit will -also be provided by the programmer. -

    • -
    • -

      The declaration -

      -
      -
      -
      control Pipe<H>(inout H headers,
      -              in error parseError,
      -              in InControl inCtrl,
      -              out OutControl outCtrl);
      -


      -describes the interface of a Match-Action pipeline named Pipe. -

    - -

    The pipeline receives three inputs: the headers headers, a parser -error parseError, and the inCtrl control data. Figure -6 indicates the different sources of these pieces of -information. The pipeline writes its outputs into outCtrl, and -it must update in place the headers to be consumed by the deparser. -

    -
      -
    • The top-level package is called VSS; in order to program a +packet, declared in the core.p4 library. The parser writes its +output (the out keyword) into the parsedHeaders +argument. The type of this argument is H, yet unknown---it will +also be provided by the programmer.

      +
    + +
  • +

    The declaration

    +
    +
    +
    +
    +
    control Pipe<H>(inout H headers,
    +                in error parseError,
    +                in InControl inCtrl,
    +                out OutControl outCtrl);
    +
    +
    +
    +
    +
    +

    describes the interface of a Match-Action pipeline named Pipe.

    +
    +
  • + +
    +
    +

    The pipeline receives three inputs: the headers headers, a parser +error parseError, and the inCtrl control data. +Figure 6 indicates the different sources of these pieces of +information. The pipeline writes its outputs into outCtrl, and +it must update in place the headers to be consumed by the deparser.

    +
    +
    +
      +
    • +

      The top-level package is called VSS; in order to program a VSS, the user will have to instantiate a package of this type (shown in the next section). The top-level package declaration also depends -on a type variable H: - -

      -
      -
      package VSS<H>
    - -

    A type variable indicates a type yet unknown that must be provided by -the user at a later time. In this case H is the type of the set +on a type variable H:

    +
    +
    +
    +
    +
    package VSS<H>
    +
    +
    +
    +
    + + +
    +
    +

    A type variable indicates a type yet unknown that must be provided by +the user at a later time. In this case H is the type of the set of headers that the user program will be processing; the parser will produce the parsed representation of these headers, and the match-action pipeline will update the input headers in place to -produce the output headers. -

    -
      -
    • The package VSS declaration has three complex parameters, of -types Parser, Pipe, and Deparser respectively; which are +produce the output headers.

      +
    +
    +
      +
    • +

      The package VSS declaration has three complex parameters, of +types Parser, Pipe, and Deparser respectively; which are exactly the declarations we have just described. In order to program -the target one has to supply values for these parameters. +the target one has to supply values for these parameters.

    • -
    • In this program the inCtrl and outCtrl structures +
    • +

      In this program the inCtrl and outCtrl structures represent control registers. The content of the headers structure is -stored in general-purpose registers. -

    • -
    • The extern Checksum16 declaration describes an extern object -whose services can be invoked to compute checksums. -
    -

    5.2. Very Simple Switch Architecture Description

    -

    In order to fully understand VSS's behavior and write meaningful P4 +stored in general-purpose registers.

    + +
  • +

    The extern Checksum16 declaration describes an extern object +whose services can be invoked to compute checksums.

    +
  • + +
    +
    +
    +

    5.2. Very Simple Switch Architecture Description

    +
    +

    In order to fully understand VSS’s behavior and write meaningful P4 programs for it, and for implementing a control plane, we also need a full behavioral description of the fixed-function blocks. This section can be seen as a simple example illustrating all the details that have to be handled when writing an architecture description. The P4 language is not intended to cover the description of all such -functional blocksthe language can only describe the interfaces +functional blocks---the language can only describe the interfaces between programmable blocks and the architecture. For the current program, -this interface is given by the Parser, Pipe, and Deparser +this interface is given by the Parser, Pipe, and Deparser declarations. In practice we expect that the complete description of the architecture will be provided as an executable program and/or diagrams and text; in -this document we will provide informal descriptions in English. -

    5.2.1. Arbiter block

    -

    The input arbiter block performs the following functions: -

    -
      -
    • It receives packets from one of the physical input Ethernet ports, -from the control plane, or from the input recirculation port. -
    • -
    • For packets received from Ethernet ports, the block computes the +this document we will provide informal descriptions in English.

      +
    +
    +

    5.2.1. Arbiter block

    +
    +

    The input arbiter block performs the following functions:

    +
    +
    +
      +
    • +

      It receives packets from one of the physical input Ethernet ports, +from the control plane, or from the input recirculation port.

      +
    • +
    • +

      For packets received from Ethernet ports, the block computes the Ethernet trailer checksum and verifies it. If the checksum does not match, the packet is discarded. If the checksum does match, it is -removed from the packet payload. +removed from the packet payload.

    • -
    • Receiving a packet involves running an arbitration algorithm if -multiple packets are available. +
    • +

      Receiving a packet involves running an arbitration algorithm if +multiple packets are available.

    • -
    • If the arbiter block is busy processing a previous packet and no +
    • +

      If the arbiter block is busy processing a previous packet and no queue space is available, input ports may drop arriving packets, without indicating the fact that the packets were dropped in any -way. +way.

    • -
    • After receiving a packet, the arbiter block sets the inCtrl.inputPort +
    • +

      After receiving a packet, the arbiter block sets the inCtrl.inputPort value that is an input to the match-action pipeline with the identity of the input port where the packet originated. Physical Ethernet ports are numbered 0 to 7, while the input recirculation port has a number 13 -and the CPU port has the number 14. -

    -

    5.2.2. Parser runtime block

    -

    The parser runtime block works in concert with the parser. It provides +and the CPU port has the number 14.

    + + +
    +
    +
    +

    5.2.2. Parser runtime block

    +
    +

    The parser runtime block works in concert with the parser. It provides an error code to the match-action pipeline, based on the parser actions, and it provides information about the packet payload (e.g., the size of the remaining payload data) to the demux block. As soon as -a packet's processing is completed by the parser, the match-action +a packet’s processing is completed by the parser, the match-action pipeline is invoked with the associated metadata as inputs (packet -headers and user-defined metadata). -

    5.2.3. Demux block

    -

    The core functionality of the demux block is to receive the headers +headers and user-defined metadata).

    +
    +
    +
    +

    5.2.3. Demux block

    +
    +

    The core functionality of the demux block is to receive the headers for the outgoing packet from the deparser and the packet payload from the parser, to assemble them into a new packet and to send the result to the correct output port. The output port is specified by the value -of outCtrl.ouputPort, which is set by the match-action pipeline. -

    -
      -
    • Sending the packet to the drop port causes the packet to disappear. -
    • -
    • Sending the packet to an output Ethernet port numbered between 0 and +of outCtrl.ouputPort, which is set by the match-action pipeline.

      +
    +
    +
      +
    • +

      Sending the packet to the drop port causes the packet to disappear.

      +
    • +
    • +

      Sending the packet to an output Ethernet port numbered between 0 and 7 causes it to be emitted on the corresponding physical interface. The packet may be placed in a queue if the output interface is already busy emitting another packet. When the packet is emitted, the physical interface computes a correct Ethernet checksum -trailer and appends it to the packet. +trailer and appends it to the packet.

    • -
    • Sending a packet to the output CPU port causes the packet to be +
    • +

      Sending a packet to the output CPU port causes the packet to be transferred to the control plane. In this case, the packet that is -sent to the CPU is the original input packet, and not the packet -received from the deparserthe latter packet is discarded. +sent to the CPU is the original input packet, and not the packet +received from the deparser---the latter packet is discarded.

    • -
    • Sending the packet to the output recirculation port causes it to +
    • +

      Sending the packet to the output recirculation port causes it to appear at the input recirculation port. Recirculation is useful when -packet processing cannot be completed in a single pass. -

    • -
    • If the outputPort has an illegal value (e.g., 9), the packet -is dropped. -
    • -
    • Finally, if the demux unit is busy processing a previous packet and there is -no capacity to queue the packet coming from the deparser, the -demux unit may drop the packet, irrespective of the output port indicated. -
    - -

    Please note that some of the behaviors of the demux block may be -unexpectedwe have highlighted them in bold. We are not specifying +packet processing cannot be completed in a single pass.

    + +
  • +

    If the outputPort has an illegal value (e.g., 9), the packet +is dropped.

    +
  • +
  • +

    Finally, if the demux unit is busy processing a previous packet and there is +no capacity to queue the packet coming from the deparser, the +demux unit may drop the packet, irrespective of the output port indicated.

    +
  • + +
    +
    +

    Please note that some of the behaviors of the demux block may be +unexpected---we have highlighted them in bold. We are not specifying here several important behaviors related to queue size, arbitration, -and timing, which also influence the packet processing. -

    -

    The arrow shown from the parser runtime to the demux block represents +and timing, which also influence the packet processing.

    +
    +
    +

    The arrow shown from the parser runtime to the demux block represents an additional information flow from the parser to the demux: the packet being processed as well as the offset within the packet where -parsing ended (i.e., the start of the packet payload). -

    5.2.4. Available extern blocks

    -

    The VSS architecture provides an incremental checksum extern block, -called Checksum16. The checksum unit has a constructor and four -methods: -

    -
      -
    • clear(): prepares the unit for a new computation -
    • -
    • update<T>(in T data): add some data to be checksummed. The -data must be either a bit-string, a header-typed value, or a struct +parsing ended (i.e., the start of the packet payload).

      +
    +
    +
    +

    5.2.4. Available extern blocks

    +
    +

    The VSS architecture provides an incremental checksum extern block, +called Checksum16. The checksum unit has a constructor and four +methods:

    +
    +
    +
      +
    • +

      clear(): prepares the unit for a new computation

      +
    • +
    • +

      update<T>(in T data): add some data to be checksummed. The +data must be either a bit-string, a header-typed value, or a struct containing such values. The fields in the header/struct -are concatenated in the order they appear in the type declaration. +are concatenated in the order they appear in the type declaration.

    • -
    • get(): returns the 16-bit one's complement checksum. When +
    • +

      get(): returns the 16-bit one’s complement checksum. When this function is invoked the checksum must have received an integral -number of bytes of data. -

    • -
    • remove<T>(in T data): assuming that data -was used for computing the current checksum, data is removed -from the checksum. -
    -

    5.3. A complete Very Simple Switch program

    -

    Here we provide a complete P4 program that implements basic forwarding for +number of bytes of data.

    + +
  • +

    remove<T>(in T data): assuming that data +was used for computing the current checksum, data is removed +from the checksum.

    +
  • + +
    +
    +
    +
    +

    5.3. A complete Very Simple Switch program

    +
    +

    Here we provide a complete P4 program that implements basic forwarding for IPv4 packets on the VSS architecture. This program does not utilize all of the -features provided by the architecturee.g., recirculationbut it does use -preprocessor #include directives (see Section 6.2). -

    -
    -

    vssmau -

    -
    - -
    Figure 7. Diagram of the match-action pipeline expressed by the VSS P4 program.
    -

    -The parser attempts to recognize an Ethernet header followed by an IPv4 header. +features provided by the architecture---e.g., recirculation---but it does use +preprocessor #include directives (see Section 6.2).

    +
    +
    +
    +vssmau +
    +
    Figure 7. Diagram of the match-action pipeline expressed by the VSS P4 program.
    +
    +
    +

    The parser attempts to recognize an Ethernet header followed by an IPv4 header. If either of these headers are missing, parsing terminates with an error. Otherwise it extracts the information from these headers into -a Parsed_packet structure. The match-action pipeline is -shown in Figure 7; it comprises four match-action units -(represented by the P4 table keyword): -

    -
      -
    • If any parser error has occurred, the packet is dropped (i.e., by assigning outputPort -to DROP_PORT) -
    • -
    • The first table uses the IPv4 destination address to determine the outputPort +a Parsed_packet structure. The match-action pipeline is +shown in Figure 7; it comprises four match-action units +(represented by the P4 table keyword):

      +
    +
    +
      +
    • +

      If any parser error has occurred, the packet is dropped (i.e., by assigning outputPort +to DROP_PORT)

      +
    • +
    • +

      The first table uses the IPv4 destination address to determine the outputPort and the IPv4 address of the next hop. If this lookup fails, the packet is dropped. -The table also decrements the IPv4 ttl value. +The table also decrements the IPv4 ttl value.

    • -
    • The second table checks the ttl value: if the ttl becomes 0, the -packet is sent to the control plane through the CPU port. +
    • +

      The second table checks the ttl value: if the ttl becomes 0, the +packet is sent to the control plane through the CPU port.

    • -
    • The third table uses the IPv4 address of the next hop (which was computed by -the first table) to determine the Ethernet address of the next hop. +
    • +

      The third table uses the IPv4 address of the next hop (which was computed by +the first table) to determine the Ethernet address of the next hop.

    • -
    • Finally, the last table uses the outputPort to identify the +
    • +

      Finally, the last table uses the outputPort to identify the source Ethernet address of the current switch, which is set in the -outgoing packet. -

    - -

    The deparser constructs the outgoing packet by reassembling the Ethernet and -IPv4 headers as computed by the pipeline. -

    -
    -
    -
    // Include P4 core library
    -# include <core.p4>
    -
    -// Include very simple switch architecture declarations
    -# include "very_simple_switch_model.p4"
    -
    -// This program processes packets comprising an Ethernet and an IPv4
    -// header, and it forwards packets using the destination IP address
    -
    -typedef bit<48>  EthernetAddress;
    -typedef bit<32>  IPv4Address;
    -
    -// Standard Ethernet header
    -header Ethernet_h {
    -    EthernetAddress dstAddr;
    -    EthernetAddress srcAddr;
    -    bit<16>         etherType;
    -}
    -
    -// IPv4 header (without options)
    -header IPv4_h {
    -    bit<4>       version;
    -    bit<4>       ihl;
    -    bit<8>       diffserv;
    -    bit<16>      totalLen;
    -    bit<16>      identification;
    -    bit<3>       flags;
    -    bit<13>      fragOffset;
    -    bit<8>       ttl;
    -    bit<8>       protocol;
    -    bit<16>      hdrChecksum;
    -    IPv4Address  srcAddr;
    -    IPv4Address  dstAddr;
    -}
    -
    -// Structure of parsed headers
    -struct Parsed_packet {
    -    Ethernet_h ethernet;
    -    IPv4_h     ip;
    -}
    -
    -// Parser section
    -
    -// User-defined errors that may be signaled during parsing
    -error {
    -    IPv4OptionsNotSupported,
    -    IPv4IncorrectVersion,
    +outgoing packet.

    + + +
    +
    +

    The deparser constructs the outgoing packet by reassembling the Ethernet and +IPv4 headers as computed by the pipeline.

    +
    +
    +
    +
    // Include P4 core library
    +# include <core.p4>
    +
    +// Include very simple switch architecture declarations
    +# include "very_simple_switch_model.p4"
    +
    +// This program processes packets comprising an Ethernet and an IPv4
    +// header, and it forwards packets using the destination IP address
    +
    +typedef bit<48>  EthernetAddress;
    +typedef bit<32>  IPv4Address;
    +
    +// Standard Ethernet header
    +header Ethernet_h {
    +    EthernetAddress dstAddr;
    +    EthernetAddress srcAddr;
    +    bit<16>         etherType;
    +}
    +
    +// IPv4 header (without options)
    +header IPv4_h {
    +    bit<4>       version;
    +    bit<4>       ihl;
    +    bit<8>       diffserv;
    +    bit<16>      totalLen;
    +    bit<16>      identification;
    +    bit<3>       flags;
    +    bit<13>      fragOffset;
    +    bit<8>       ttl;
    +    bit<8>       protocol;
    +    bit<16>      hdrChecksum;
    +    IPv4Address  srcAddr;
    +    IPv4Address  dstAddr;
    +}
    +
    +// Structure of parsed headers
    +struct Parsed_packet {
    +    Ethernet_h ethernet;
    +    IPv4_h     ip;
    +}
    +
    +// Parser section
    +
    +// User-defined errors that may be signaled during parsing
    +error {
    +    IPv4OptionsNotSupported,
    +    IPv4IncorrectVersion,
         IPv4ChecksumError
    -}
    -
    -parser TopParser(packet_in b, out Parsed_packet p) {
    -    Checksum16() ck;  // instantiate checksum unit
    -
    -    state start {
    -        b.extract(p.ethernet);
    -        transition select(p.ethernet.etherType) {
    -            0x0800: parse_ipv4;
    -            // no default rule: all other packets rejected
    -        }
    -    }
    -
    -    state parse_ipv4 {
    -        b.extract(p.ip);
    -        verify(p.ip.version == 4w4, error.IPv4IncorrectVersion);
    -        verify(p.ip.ihl == 4w5, error.IPv4OptionsNotSupported);
    -        ck.clear();
    -        ck.update(p.ip);
    -        // Verify that packet checksum is zero
    -        verify(ck.get() == 16w0, error.IPv4ChecksumError);
    -        transition accept;
    -    }
    -}
    -
    -// Match-action pipeline section
    -
    -control TopPipe(inout Parsed_packet headers,
    -                in error parseError, // parser error
    -                in InControl inCtrl, // input port
    -                out OutControl outCtrl) {
    -     IPv4Address nextHop;  // local variable
    -
    -     /**
    -      * Indicates that a packet is dropped by setting the
    -      * output port to the DROP_PORT
    -      */
    -      action Drop_action() {
    -          outCtrl.outputPort = DROP_PORT;
    -      }
    -
    -     /**
    -      * Set the next hop and the output port.
    -      * Decrements ipv4 ttl field.
    -      * @param ipv4_dest ipv4 address of next hop
    -      * @param port output port
    -      */
    -      action Set_nhop(IPv4Address ipv4_dest, PortId port) {
    -          nextHop = ipv4_dest;
    -          headers.ip.ttl = headers.ip.ttl - 1;
    -          outCtrl.outputPort = port;
    -      }
    -
    -     /**
    -      * Computes address of next IPv4 hop and output port
    -      * based on the IPv4 destination of the current packet.
    -      * Decrements packet IPv4 TTL.
    -      * @param nextHop IPv4 address of next hop
    -      */
    -     table ipv4_match {
    -         key = { headers.ip.dstAddr: lpm; }  // longest-prefix match
    -         actions = {
    -              Drop_action;
    -              Set_nhop;
    -         }
    -         size = 1024;
    -         default_action = Drop_action;
    -     }
    -
    -     /**
    -      * Send the packet to the CPU port
    -      */
    -      action Send_to_cpu() {
    -          outCtrl.outputPort = CPU_OUT_PORT;
    -      }
    -
    -     /**
    -      * Check packet TTL and send to CPU if expired.
    -      */
    -     table check_ttl {
    -         key = { headers.ip.ttl: exact; }
    -         actions = { Send_to_cpu; NoAction; }
    -         const default_action = NoAction; // defined in core.p4
    -     }
    -
    -     /**
    -      * Set the destination MAC address of the packet
    -      * @param dmac destination MAC address.
    -      */
    -      action Set_dmac(EthernetAddress dmac) {
    -          headers.ethernet.dstAddr = dmac;
    -      }
    -
    -     /**
    -      * Set the destination Ethernet address of the packet
    -      * based on the next hop IP address.
    -      * @param nextHop IPv4 address of next hop.
    -      */
    -      table dmac {
    -          key = { nextHop: exact; }
    -          actions = {
    -               Drop_action;
    -               Set_dmac;
    -          }
    -          size = 1024;
    -          default_action = Drop_action;
    -      }
    -
    -      /**
    -       * Set the source MAC address.
    -       * @param smac: source MAC address to use
    -       */
    -       action Set_smac(EthernetAddress smac) {
    -           headers.ethernet.srcAddr = smac;
    -       }
    -
    -      /**
    -       * Set the source mac address based on the output port.
    -       */
    -      table smac {
    -           key = { outCtrl.outputPort: exact; }
    -           actions = {
    -                Drop_action;
    -                Set_smac;
    -          }
    -          size = 16;
    -          default_action = Drop_action;
    -      }
    -
    -      apply {
    -          if (parseError != error.NoError) {
    -              Drop_action();  // invoke drop directly
    -              return;
    -          }
    -
    -          ipv4_match.apply(); // Match result will go into nextHop
    -          if (outCtrl.outputPort == DROP_PORT) return;
    -
    -          check_ttl.apply();
    -          if (outCtrl.outputPort == CPU_OUT_PORT) return;
    -
    -          dmac.apply();
    -          if (outCtrl.outputPort == DROP_PORT) return;
    -
    -          smac.apply();
    -    }
    -}
    -
    -// deparser section
    -control TopDeparser(inout Parsed_packet p, packet_out b) {
    -    Checksum16() ck;
    -    apply {
    -        b.emit(p.ethernet);
    -        if (p.ip.isValid()) {
    -            ck.clear();              // prepare checksum unit
    -            p.ip.hdrChecksum = 16w0; // clear checksum
    -            ck.update(p.ip);         // compute new checksum.
    -            p.ip.hdrChecksum = ck.get();
    -        }
    -        b.emit(p.ip);
    -    }
    -}
    -
    -// Instantiate the top-level VSS package
    -VSS(TopParser(),
    -    TopPipe(),
    -    TopDeparser()) main;

    6. P4 language definition

    -

    The P4 language can be viewed as having several distinct components, -which we describe separately: -

    -
      -
    • The core language, comprising of types, variables, scoping, +} + +parser TopParser(packet_in b, out Parsed_packet p) { + Checksum16() ck; // instantiate checksum unit + + state start { + b.extract(p.ethernet); + transition select(p.ethernet.etherType) { + 0x0800: parse_ipv4; + // no default rule: all other packets rejected + } + } + + state parse_ipv4 { + b.extract(p.ip); + verify(p.ip.version == 4w4, error.IPv4IncorrectVersion); + verify(p.ip.ihl == 4w5, error.IPv4OptionsNotSupported); + ck.clear(); + ck.update(p.ip); + // Verify that packet checksum is zero + verify(ck.get() == 16w0, error.IPv4ChecksumError); + transition accept; + } +} + +// Match-action pipeline section + +control TopPipe(inout Parsed_packet headers, + in error parseError, // parser error + in InControl inCtrl, // input port + out OutControl outCtrl) { + IPv4Address nextHop; // local variable + + /** + * Indicates that a packet is dropped by setting the + * output port to the DROP_PORT + */ + action Drop_action() { + outCtrl.outputPort = DROP_PORT; + } + + /** + * Set the next hop and the output port. + * Decrements ipv4 ttl field. + * @param ipv4_dest ipv4 address of next hop + * @param port output port + */ + action Set_nhop(IPv4Address ipv4_dest, PortId port) { + nextHop = ipv4_dest; + headers.ip.ttl = headers.ip.ttl - 1; + outCtrl.outputPort = port; + } + + /** + * Computes address of next IPv4 hop and output port + * based on the IPv4 destination of the current packet. + * Decrements packet IPv4 TTL. + * @param nextHop IPv4 address of next hop + */ + table ipv4_match { + key = { headers.ip.dstAddr: lpm; } // longest-prefix match + actions = { + Drop_action; + Set_nhop; + } + size = 1024; + default_action = Drop_action; + } + + /** + * Send the packet to the CPU port + */ + action Send_to_cpu() { + outCtrl.outputPort = CPU_OUT_PORT; + } + + /** + * Check packet TTL and send to CPU if expired. + */ + table check_ttl { + key = { headers.ip.ttl: exact; } + actions = { Send_to_cpu; NoAction; } + const default_action = NoAction; // defined in core.p4 + } + + /** + * Set the destination MAC address of the packet + * @param dmac destination MAC address. + */ + action Set_dmac(EthernetAddress dmac) { + headers.ethernet.dstAddr = dmac; + } + + /** + * Set the destination Ethernet address of the packet + * based on the next hop IP address. + * @param nextHop IPv4 address of next hop. + */ + table dmac { + key = { nextHop: exact; } + actions = { + Drop_action; + Set_dmac; + } + size = 1024; + default_action = Drop_action; + } + + /** + * Set the source MAC address. + * @param smac: source MAC address to use + */ + action Set_smac(EthernetAddress smac) { + headers.ethernet.srcAddr = smac; + } + + /** + * Set the source mac address based on the output port. + */ + table smac { + key = { outCtrl.outputPort: exact; } + actions = { + Drop_action; + Set_smac; + } + size = 16; + default_action = Drop_action; + } + + apply { + if (parseError != error.NoError) { + Drop_action(); // invoke drop directly + return; + } + + ipv4_match.apply(); // Match result will go into nextHop + if (outCtrl.outputPort == DROP_PORT) return; + + check_ttl.apply(); + if (outCtrl.outputPort == CPU_OUT_PORT) return; + + dmac.apply(); + if (outCtrl.outputPort == DROP_PORT) return; + + smac.apply(); + } +} + +// deparser section +control TopDeparser(inout Parsed_packet p, packet_out b) { + Checksum16() ck; + apply { + b.emit(p.ethernet); + if (p.ip.isValid()) { + ck.clear(); // prepare checksum unit + p.ip.hdrChecksum = 16w0; // clear checksum + ck.update(p.ip); // compute new checksum. + p.ip.hdrChecksum = ck.get(); + } + b.emit(p.ip); + } +} + +// Instantiate the top-level VSS package +VSS(TopParser(), + TopPipe(), + TopDeparser()) main; +
    +
    +
    +
    +
    +
    +

    6. P4 language definition

    +
    +
    +

    The P4 language can be viewed as having several distinct components, +which we describe separately:

    +
    +
    +
      +
    • +

      The core language, comprising of types, variables, scoping, declarations, statements, expressions, etc. We start by describing -this part of the language. -

    • -
    • A sub-language for expressing parsers, based on state machines -(Section 13). -
    • -
    • A sub-language for expressing computations using match-action units, based on -traditional imperative control-flow (Section 14). -
    • -
    • A sub-language for describing architectures (Section -17). -
    -

    6.1. Syntax and semantics

    6.1.1. Grammar

    -

    The complete grammar of P416 is given in Appendix G, +this part of the language.

    + +
  • +

    A sub-language for expressing parsers, based on state machines +(Chapter 13).

    +
  • +
  • +

    A sub-language for expressing computations using match-action units, based on +traditional imperative control-flow (Chapter 14).

    +
  • +
  • +

    A sub-language for describing architectures (Chapter 17).

    +
  • + +
    +
    +

    6.1. Syntax and semantics

    +
    +

    6.1.1. Grammar

    +
    +

    The complete grammar of P416 is given in Appendix G, using Yacc/Bison grammar description language. This text is based on the same grammar. We adopt several standard conventions when we provide -excerpts from the grammar: -

    -
      -
    • UPPERCASE symbols denote terminals in the grammar. -
    • -
    • Excerpts from the grammar are given in BNF notation as follows: - -
      -
      -
      p4program
      -  : /* empty */
      -  | p4program declaration
      -  | p4program ";"  /* empty declaration */
      -  ;
    - -

    Pseudo-code (mostly used for describing the semantics of +excerpts from the grammar:

    +
    +
    +
      +
    • +

      UPPERCASE symbols denote terminals in the grammar.

      +
    • +
    • +

      Excerpts from the grammar are given in BNF notation as follows:

      +
    • +
    +
    +
    +
    +
    p4program
    +    : /* empty */
    +    | p4program declaration
    +    | p4program ";"  /* empty declaration */
    +    ;
    +
    +
    +
    +

    Pseudo-code (mostly used for describing the semantics of various P4 constructs) are shown with fixed-size fonts as in the -following example: -

    -
    -
    -
    ParserModel.verify(bool condition, error err) {
    -    if (condition == false) {
    +following example:

    +
    +
    +
    +
    ParserModel.verify(bool condition, error err) {
    +    if (condition == false) {
             ParserModel.parserError = err;
             goto reject;
         }
    -}

    6.1.2. Semantics and the P4 abstract machines

    -

    We describe the semantics of P4 in terms of abstract machines executing +} +

    +
    +
    +
    +

    6.1.2. Semantics and the P4 abstract machines

    +
    +

    We describe the semantics of P4 in terms of abstract machines executing traditional imperative code. There is an abstract machine for each P4 sub-language (parser, control). The abstract machines are described in -this text in pseudo-code and English. -

    -

    P4 compilers are free to reorganize the code they generate in any way as long as +this text in pseudo-code and English.

    +
    +
    +

    P4 compilers are free to reorganize the code they generate in any way as long as the externally visible behaviors of the P4 programs are preserved as -described by this specification where externally visible behavior is defined as: -

    -
      -
    • The input/output behavior of all P4 blocks, and -
    • -
    • The state maintained by extern blocks. -
    -

    6.2. Preprocessing

    -

    To aid composition of programs from multiple source files P4 +described by this specification where externally visible behavior is defined as:

    +
    +
    +
      +
    • +

      The input/output behavior of all P4 blocks, and

      +
    • +
    • +

      The state maintained by extern blocks.

      +
    • +
    +
    +
    +
    +
    +

    6.2. Preprocessing

    +
    +

    To aid composition of programs from multiple source files P4 compilers should support the following subset of the C preprocessor -functionality: -

    -
      -
    • #define for defining macros (without arguments) -
    • -
    • #undef -
    • -
    • #if #else #endif #ifdef #ifndef #elif -
    • -
    • #include -
    - -

    The preprocessor should also remove the sequence backslash newline (ASCII codes 92, 10) -to facilitate splitting content across multiple lines when convenient for formatting. -

    -

    Additional C preprocessor capabilities may be supported, but -are not guaranteede.g., macros with arguments. Similar to C, #include -can specify a file name either within double quotes or within <>. -

    -
    -
    -
    # include <system_file>
    -# include "user_file"
    -

    The difference between the two forms is the order in which the +functionality:

    +
    +
    +
      +
    • +

      #define for defining macros (without arguments)

      +
    • +
    • +

      #undef

      +
    • +
    • +

      #if #else #endif #ifdef #ifndef #elif

      +
    • +
    • +

      #include

      +
    • +
    +
    +
    +

    The preprocessor should also remove the sequence backslash newline (ASCII codes 92, 10) +to facilitate splitting content across multiple lines when convenient for formatting.

    +
    +
    +

    Additional C preprocessor capabilities may be supported, but +are not guaranteed---e.g., macros with arguments. Similar to C, #include +can specify a file name either within double quotes or within <>.

    +
    +
    +
    +
    # include <system_file>
    +# include "user_file"
    +
    +
    +
    +

    The difference between the two forms is the order in which the preprocessor searches for header files when the path is incompletely -specified. -

    -

    P4 compilers should correctly handle #line directives +specified.

    +
    +
    +

    P4 compilers should correctly handle #line directives that may be generated during preprocessing. This functionality allows P4 programs to be built from multiple source files, potentially -produced by different programmers at different times: -

    -
      -
    • the P4 core library, defined in this document, -
    • -
    • the architecture, defining data plane interfaces and extern blocks, -
    • -
    • user-defined libraries of useful components (e.g. standard -protocol header definitions), and -
    • -
    • the P4 programs that specify the behavior of each programmable block. -
    -

    6.3. P4 core library

    -

    The P4 language specification defines a core library that includes +produced by different programmers at different times:

    +
    +
    +
      +
    • +

      the P4 core library, defined in this document,

      +
    • +
    • +

      the architecture, defining data plane interfaces and extern blocks,

      +
    • +
    • +

      user-defined libraries of useful components (e.g. standard +protocol header definitions), and

      +
    • +
    • +

      the P4 programs that specify the behavior of each programmable block.

      +
    • +
    +
    +
    +
    +

    6.3. P4 core library

    +
    +

    The P4 language specification defines a core library that includes several common programming constructs. A -description of the core library is provided in Appendix -D. All P4 programs must include the core -library. Including the core library is done with -

    -
    -
    -
    # include <core.p4>

    6.4. Lexical constructs

    -

    All P4 keywords use only ASCII characters. All P4 identifiers must use +description of the core library is provided in +Appendix D. All P4 programs must include the core +library. Including the core library is done with

    +
    +
    +
    +
    # include <core.p4>
    +
    +
    +
    +
    +

    6.4. Lexical constructs

    +
    +

    All P4 keywords use only ASCII characters. All P4 identifiers must use only ASCII characters. P4 compilers should handle correctly strings containing 8-bit characters in comments and string literals. P4 is case-sensitive. Whitespace characters, including newlines are treated as token separators. Indentation is free-form; however, P4 has C-like block constructs, and all our examples use C-style indentation. Tab -characters are treated as spaces. -

    -

    The lexer recognizes the following kinds of terminals: -

    -
      -
    • IDENTIFIER: start with a letter or underscore, and contain -letters, digits and underscores -
    • -
    • TYPE_IDENTIFIER: identifier that denotes a type name -
    • -
    • INTEGER: integer literals -
    • -
    • DONTCARE: a single underscore -
    • -
    • Keywords such as RETURN. By convention, each keyword terminal corresponds to a +characters are treated as spaces.

      +
    +
    +

    The lexer recognizes the following kinds of terminals:

    +
    +
    +
      +
    • +

      IDENTIFIER: start with a letter or underscore, and contain +letters, digits and underscores

      +
    • +
    • +

      TYPE_IDENTIFIER: identifier that denotes a type name

      +
    • +
    • +

      INTEGER: integer literals

      +
    • +
    • +

      DONTCARE: a single underscore

      +
    • +
    • +

      Keywords such as RETURN. By convention, each keyword terminal corresponds to a language keyword with the same spelling but using lowercase. For -example, the RETURN terminal corresponds to the return -keyword. -

    -

    6.4.1. Identifiers

    -

    P4 identifiers may contain only letters, numbers, and the underscore -character _, and must start with a letter or -underscore. The special identifier consisting of a single underscore _ -is reserved to indicate a “don't care” value; its -type may vary depending on the context. Certain keywords (e.g., apply) -can be used as identifiers if the context makes it unambiguous. -

    -
    -
    -
    nonTypeName
    +example, the RETURN terminal corresponds to the return
    +keyword.

    + + +
    +
    +

    6.4.1. Identifiers

    +
    +

    P4 identifiers may contain only letters, numbers, and the underscore +character _, and must start with a letter or +underscore. The special identifier consisting of a single underscore _ +is reserved to indicate a "don’t care" value; its +type may vary depending on the context. Certain keywords (e.g., apply) +can be used as identifiers if the context makes it unambiguous.

    +
    +
    +
    +
    nonTypeName
         : IDENTIFIER
         | APPLY
         | KEY
    @@ -1715,160 +2524,237 @@ 

    6.4.2. Comments

    -

    P4 supports several kinds of comments: -

    -
      -
    • Single-line comments, introduced by // and spanning to the end of line, -
    • -
    • Multi-line comments, enclosed between /* and */ -
    • -
    • Nested multi-line comments are not supported. -
    • -
    • Javadoc-style comments, starting with /** and ending with */ -
    - -

    Use of Javadoc-style comments is strongly encouraged for the tables and actions -that are used to synthesize the interface with the control-plane. -

    -

    P4 treats comments as token separators and no comments are allowed within a -tokene.g. bi/**/t is parsed as two tokens, bi and t, and -not as a single token bit. -

    6.4.3. Literal constants

    6.4.3.1. Boolean literals
    -

    There are two Boolean literal constants: true and false. -

    6.4.3.2. Integer literals
    -

    Integer literals are non-negative arbitrary-precision integers. + ; +

    +
    +
    +
    +

    6.4.2. Comments

    +
    +

    P4 supports several kinds of comments:

    +
    +
    +
      +
    • +

      Single-line comments, introduced by // and spanning to the end of line,

      +
    • +
    • +

      Multi-line comments, enclosed between /* and */

      +
    • +
    • +

      Nested multi-line comments are not supported.

      +
    • +
    • +

      Javadoc-style comments, starting with /** and ending with */

      +
    • +
    +
    +
    +

    Use of Javadoc-style comments is strongly encouraged for the tables and actions +that are used to synthesize the interface with the control-plane.

    +
    +
    +

    P4 treats comments as token separators and no comments are allowed within a +token---e.g. bi/**/t is parsed as two tokens, bi and t, and +not as a single token bit.

    +
    +
    +
    +

    6.4.3. Literal constants

    +
    +
    6.4.3.1. Boolean literals
    +
    +

    There are two Boolean literal constants: true and false.

    +
    +
    +
    +
    6.4.3.2. Integer literals
    +
    +

    Integer literals are non-negative arbitrary-precision integers. By default, literals are represented in base 10. The following prefixes -must be employed to specify the base explicitly: -

    -
      -
    • 0x or 0X indicates base 16 (hexadecimal) -
    • -
    • 0o or 0O indicates base 8 (octal) -
    • -
    • 0d or 0D indicates base 10 (decimal) -
    • -
    • 0b or 0B indicates base 2 -
    - -

    The width of a numeric literal in bits can be specified by an unsigned +must be employed to specify the base explicitly:

    +
    +
    +
      +
    • +

      0x or 0X indicates base 16 (hexadecimal)

      +
    • +
    • +

      0o or 0O indicates base 8 (octal)

      +
    • +
    • +

      0d or 0D indicates base 10 (decimal)

      +
    • +
    • +

      0b or 0B indicates base 2

      +
    • +
    +
    +
    +

    The width of a numeric literal in bits can be specified by an unsigned number prefix consisting of a number of bits and a signedness -indicator: -

    -
      -
    • w indicates unsigned numbers -
    • -
    • s indicates signed numbers -
    - -

    Note that a leading zero by itself does not indicate an +indicator:

    +
    +
    +
      +
    • +

      w indicates unsigned numbers

      +
    • +
    • +

      s indicates signed numbers

      +
    • +
    +
    +
    +

    Note that a leading zero by itself does not indicate an octal (base 8) constant. The underscore character is considered a digit within number literals but is ignored when computing the value of the parsed number. This allows long constant numbers to be more easily read by grouping digits together. The underscore cannot be used in the width specification or as the first character of an integer literal. No comments or whitespaces are allowed within a -literal. Here are some examples of numeric literals: -

    -
    -
    -
    32w255         // a 32-bit unsigned number with value 255
    -32w0d255       // same value as above
    -32w0xFF        // same value as above
    -32s0xFF        // a 32-bit signed number with value 255
    -8w0b10101010   // an 8-bit unsigned number with value 0xAA
    -8w0b_1010_1010 // same value as above
    -8w170          // same value as above
    -8s0b1010_1010  // an 8-bit signed number with value -86
    -16w0377        // 16-bit unsigned number with value 377 (not 255!)
    -16w0o377       // 16-bit unsigned number with value 255 (base 8)
    6.4.3.3. String literals
    -

    String literals are specified as an arbitrary sequence of 8-bit -characters, enclosed within double quote characters " (ASCII code +literal. Here are some examples of numeric literals:

    +
    +
    +
    +
    32w255         // a 32-bit unsigned number with value 255
    +32w0d255       // same value as above
    +32w0xFF        // same value as above
    +32s0xFF        // a 32-bit signed number with value 255
    +8w0b10101010   // an 8-bit unsigned number with value 0xAA
    +8w0b_1010_1010 // same value as above
    +8w170          // same value as above
    +8s0b1010_1010  // an 8-bit signed number with value -86
    +16w0377        // 16-bit unsigned number with value 377 (not 255!)
    +16w0o377       // 16-bit unsigned number with value 255 (base 8)
    +
    +
    +
    +
    +
    6.4.3.3. String literals
    +
    +

    String literals are specified as an arbitrary sequence of 8-bit +characters, enclosed within double quote characters " (ASCII code 34). Strings start with a double quote character and extend to the first double quote sign which is not immediately preceded by an odd number of backslash characters (ASCII code 92). P4 does not make any validity checks on strings (i.e., it does not check that strings -represent legal UTF-8 encodings). -

    -

    Since P4 does not provide any operations on strings, +represent legal UTF-8 encodings).

    +
    +
    +

    Since P4 does not provide any operations on strings, string literals are generally passed unchanged through the P4 compiler to other third-party tools or compiler-backends, including the terminating quotes. These tools can define their own handling of escape sequences (e.g., how to specify Unicode characters, or handle -unprintable ASCII characters). -

    -

    Here are 3 examples of string literals: -

    -
    -
    -
    "simple string"
    -"string \" with \" embedded \" quotes"
    -"string with embedded
    -line terminator"

    6.4.4. Optional trailing commas

    -

    The P4 grammar allows several kinds of comma-separated lists to end in -an optional comma. -

    -
    -
    -
    optTrailingComma
    -    : /* empty */
    -    | ","
    -    ;
    -

    For example, the following declarations are both legal, and -have the same meaning: -

    -
    -
    -
    enum E {
    -     a, b, c
    -}
    -
    -enum E {
    -     a, b, c,
    -}
    -

    This is particularly useful in combination with preprocessor -directives: -

    -
    -
    -
    enum E {
    -#if SUPPORT_A
    -    a,
    -#endif
    -    b,
    -    c,
    -}

    6.5. Naming conventions

    -

    P4 provides a rich assortment of types. Base types include bit-strings, numbers, and errors. +unprintable ASCII characters).

    +
    +
    +

    Here are 3 examples of string literals:

    +
    +
    +
    +
    "simple string"
    +"string \" with \" embedded \" quotes"
    +"string with embedded
    +line terminator"
    +
    +
    +
    +
    +
    +

    6.4.4. Optional trailing commas

    +
    +

    The P4 grammar allows several kinds of comma-separated lists to end in +an optional comma.

    +
    +
    +
    +
    optTrailingComma
    +    : /* empty */
    +    | ","
    +    ;
    +
    +
    +
    +

    For example, the following declarations are both legal, and +have the same meaning:

    +
    +
    +
    +
    enum E {
    +     a, b, c
    +}
    +
    +enum E {
    +     a, b, c,
    +}
    +
    +
    +
    +

    This is particularly useful in combination with preprocessor +directives:

    +
    +
    +
    +
    enum E {
    +#if SUPPORT_A
    +    a,
    +#endif
    +    b,
    +    c,
    +}
    +
    +
    +
    +
    +
    +

    6.5. Naming conventions

    +
    +

    P4 provides a rich assortment of types. Base types include bit-strings, numbers, and errors. There are also built-in types for representing constructs such as parsers, pipelines, actions, and tables. Users can construct new types based on these: structures, enumerations, headers, -header stacks, header unions, etc. -

    -

    In this document we adopt the following conventions: -

    -
      -
    • Built-in types are written with lowercase characterse.g., int<20>, -
    • -
    • User-defined types are capitalizede.g., IPv4Address, -
    • -
    • Type variables are always uppercasee.g., parser P<H, IH>(), -
    • -
    • Variables are uncapitalized e.g., ipv4header, -
    • -
    • Constants are written with uppercase characterse.g., CPU_PORT, and -
    • -
    • Errors and enumerations are written in camel-case e.g. PacketTooShort. -
    -

    6.6. P4 programs

    -

    A P4 program is a list of declarations: -

    -
    -
    -
    p4program
    -    : /* empty */
    +header stacks, header unions, etc.

    +
    +
    +

    In this document we adopt the following conventions:

    +
    +
    +
      +
    • +

      Built-in types are written with lowercase characters---e.g., int<20>,

      +
    • +
    • +

      User-defined types are capitalized---e.g., IPv4Address,

      +
    • +
    • +

      Type variables are always uppercase---e.g., parser P<H, IH>(),

      +
    • +
    • +

      Variables are uncapitalized--- e.g., ipv4header,

      +
    • +
    • +

      Constants are written with uppercase characters---e.g., CPU_PORT, and

      +
    • +
    • +

      Errors and enumerations are written in camel-case--- e.g. PacketTooShort.

      +
    • +
    +
    +
    +
    +

    6.6. P4 programs

    +
    +

    A P4 program is a list of declarations:

    +
    +
    +
    +
    p4program
    +    : /* empty */
         | p4program declaration
    -    | p4program ";"  /* empty declaration */
    +    | p4program ";"  /* empty declaration */
         ;
     
     declaration
    @@ -1882,84 +2768,113 @@ 

    An empty declarations is indicated with a single semicolon. (Allowing empty + ;

    +
    +
    +
    +

    An empty declarations is indicated with a single semicolon. (Allowing empty declarations accommodates the -habits of C/C++ and Java programmerse.g., certain constructs, like struct, -do not require a terminating semicolon). -

    6.6.1. Scopes

    -

    Some P4 constructs act as namespaces that create local scopes for names including: -

    -
      -
    • Derived type declarations (struct, header, header_union, enum), -which introduce local scopes for field names, -
    • -
    • Block statements, which introduce local lexically-enclosed scopes, -
    • -
    • parser, table, action, and control blocks, which -introduce local scopes -
    • -
    • Declarations with type variables, which introduce a new scope for those -variables. For example, in the following extern declaration, -the scope of the type variable H extends to the end of the -declaration: -
    - -
    -
    -
    extern E<H>(/* parameters omitted */) { /* body omitted */ } // scope of H ends here.
    -

    The order of declarations is important; with the exception of parser -states, all uses of a symbol must follow the symbol's -declaration. (This is a departure from P414, which +habits of C/C++ and Java programmers---e.g., certain constructs, like struct, +do not require a terminating semicolon).

    +
    +
    +

    6.6.1. Scopes

    +
    +

    Some P4 constructs act as namespaces that create local scopes for names including:

    +
    +
    +
      +
    • +

      Derived type declarations (struct, header, header_union, enum), +which introduce local scopes for field names,

      +
    • +
    • +

      Block statements, which introduce local lexically-enclosed scopes,

      +
    • +
    • +

      parser, table, action, and control blocks, which +introduce local scopes

      +
    • +
    • +

      Declarations with type variables, which introduce a new scope for those +variables. For example, in the following extern declaration, +the scope of the type variable H extends to the end of the +declaration:

      +
    • +
    +
    +
    +
    +
    extern E<H>(/* parameters omitted */) { /* body omitted */ } // scope of H ends here.
    +
    +
    +
    +

    The order of declarations is important; with the exception of parser +states, all uses of a symbol must follow the symbol’s +declaration. (This is a departure from P414, which allows declarations in any order. This requirement significantly simplifies the implementation of compilers for P4, allowing compilers to use additional information -about declared identifiers to resolve ambiguities.) -

    6.6.2. Stateful elements

    -

    Most P4 constructs are stateless: given some inputs they produce a +about declared identifiers to resolve ambiguities.)

    +
    +
    +
    +

    6.6.2. Stateful elements

    +
    +

    Most P4 constructs are stateless: given some inputs they produce a result that solely depends on these inputs. There are only two stateful constructs -that may retain information across packets: -

    -
      -
    • -

      tables: Tables are read-only for the data plane, but their -entries can be modified by the control-plane, -

    • -
    • -

      extern objects: many objects have state that can be read and -written by the control plane and data plane. All constructs from the P414 language +that may retain information across packets:

      +
    +
    +
      +
    • +

      tables: Tables are read-only for the data plane, but their +entries can be modified by the control-plane,

      +
    • +
    • +

      extern objects: many objects have state that can be read and +written by the control plane and data plane. All constructs from the P414 language version that encapsulate state (e.g., counters, meters, registers) are -represented using extern objects in P416. -

    - -

    In P4 all stateful elements must be explicitly allocated at -compilation-time through the process called “instantiation”. -

    -

    In addition, parsers, control blocks, and packages +represented using extern objects in P416.

    + + +
    +
    +

    In P4 all stateful elements must be explicitly allocated at +compilation-time through the process called "instantiation".

    +
    +
    +

    In addition, parsers, control blocks, and packages may contain stateful element instantiations. Thus, they are also treated as stateful elements, even if they appear to contain no state, and must be instantiated before they can be used. However, although -they are stateful, tables do not need to be instantiated -explicitlydeclaring a table also creates an instance of +they are stateful, tables do not need to be instantiated +explicitly---declaring a table also creates an instance of it. This convention is designed to support the common case, since most tables are used just once. To have finer-grained control over when -a table is instantiated, a programmer can declare it within -a control. -

    -

    Recall the example in Section 5.3: TopParser, TopPipe, TopDeparser, Checksum16, -and Switch are types. There are two instances of Checksum16, one in TopParser and -one in TopDeparser, both called ck. The TopParser, TopDeparser, TopPipe, -and Switch are instantiated at the end of the program, in the -declaration of the main instance object, which is an instance of -the Switch type (a package). -

    6.7. L-values

    -

    L-values are expressions that may appear on the left side of an -assignment operation or as arguments corresponding to out and inout +a table is instantiated, a programmer can declare it within +a control.

    +
    +
    +

    Recall the example in Section 5.3: TopParser, TopPipe, TopDeparser, Checksum16, +and Switch are types. There are two instances of Checksum16, one in TopParser and +one in TopDeparser, both called ck. The TopParser, TopDeparser, TopPipe, +and Switch are instantiated at the end of the program, in the +declaration of the main instance object, which is an instance of +the Switch type (a package).

    +
    +
    +
    +
    +

    6.7. L-values

    +
    +

    L-values are expressions that may appear on the left side of an +assignment operation or as arguments corresponding to out and inout function parameters. An l-value represents a storage reference. The -following expressions are legal l-values: -

    -
    -
    -
    prefixedNonTypeName
    +following expressions are legal l-values:

    +
    +
    +
    +
    prefixedNonTypeName
         : nonTypeName
         | dotPrefix nonTypeName
         ;
    @@ -1967,308 +2882,404 @@ 

    "." member - | lvalue "[" expression "]" - | lvalue "[" expression ":" expression "]" - | "(" lvalue ")" - ;

    -
      -
    • Identifiers of a base or derived type. -
    • -
    • Structure, header, and header union field member access operations -(using the dot notation). -
    • -
    • References to elements within header stacks (see Section -8.18): indexing, and references to last and next. -
    • -
    • The result of a bit-slice operator [m:l]. -
    - -

    The following is a legal l-value: headers.stack[4].field. Note -that method and function calls cannot return l-values. -

    6.8. Calling convention: call by copy in/copy out

    -

    P4 provides multiple constructs for writing modular programs: extern + | lvalue "." member + | lvalue "[" expression "]" + | lvalue "[" expression ":" expression "]" + | "(" lvalue ")" + ; +

    +
    +
    +
      +
    • +

      Identifiers of a base or derived type.

      +
    • +
    • +

      Structure, header, and header union field member access operations +(using the dot notation).

      +
    • +
    • +

      References to elements within header stacks (see +Section 8.18): indexing, and references to last and next.

      +
    • +
    • +

      The result of a bit-slice operator [m:l].

      +
    • +
    +
    +
    +

    The following is a legal l-value: headers.stack[4].field. Note +that method and function calls cannot return l-values.

    +
    +
    +
    +

    6.8. Calling convention: call by copy in/copy out

    +
    +

    P4 provides multiple constructs for writing modular programs: extern methods, parsers, controls, actions. All these constructs behave -similarly to procedures in standard general-purpose programming languages: -

    -
      -
    • They have named and typed parameters. -
    • -
    • They introduce a new local scope for parameters and local variables. -
    • -
    • They allow arguments to be passed by binding them to their -parameters. -
    - -

    Invocations are executed using copy-in/copy-out semantics. -

    -

    Each parameter may be labeled with a direction: -

    -
      -
    • in parameters are read-only. It is an error to use an in +similarly to procedures in standard general-purpose programming languages:

      +
    +
    +
      +
    • +

      They have named and typed parameters.

      +
    • +
    • +

      They introduce a new local scope for parameters and local variables.

      +
    • +
    • +

      They allow arguments to be passed by binding them to their +parameters.

      +
    • +
    +
    +
    +

    Invocations are executed using copy-in/copy-out semantics.

    +
    +
    +

    Each parameter may be labeled with a direction:

    +
    +
    +
      +
    • +

      in parameters are read-only. It is an error to use an in parameter on the left-hand side of an assignment or to -pass it to a callee as a non-in argument. in parameters +pass it to a callee as a non-in argument. in parameters are initialized by copying the value of the corresponding -argument when the invocation is executed. +argument when the invocation is executed.

    • -
    • out parameters are, with a few exceptions listed below, +
    • +

      out parameters are, with a few exceptions listed below, uninitialized and are treated as l-values -(See Section 6.7) within the body of the method or function. -An argument passed as an out +(See Section 6.7) within the body of the method or function. +An argument passed as an out parameter must be an l-value; after the execution of the call, the value of the parameter is copied to the corresponding storage location -for that l-value. -

    • -
    • inout parameters behave like a combination of in and out parameters simultaneously: - On entry the value of the arguments is copied to the parameters. On return the - value of the parameters is copied back to the arguments. In consequence, an argument - passed as an inout parameter must be an l-value. -
    • -
    • The meaning of parameters with no direction depends upon the kind of -entity the parameter is for: - -
        -
      • For anything other than an action, e.g. a control, parser, or +for that l-value.

        +
      • +
      • +

        inout parameters behave like a combination of in and out parameters simultaneously: +On entry the value of the arguments is copied to the parameters. On return the +value of the parameters is copied back to the arguments. In consequence, an argument +passed as an inout parameter must be an l-value.

        +
      • +
      • +

        The meaning of parameters with no direction depends upon the kind of +entity the parameter is for:

        +
        +
          +
        • +

          For anything other than an action, e.g. a control, parser, or function, a directionless parameter means that the value supplied as an argument in a call must be a compile-time known value -(see Section 18.1). -

        • -
        • For an action, a directionless parameter indicates that it is -“action data”. See Section 14.1 for the meaning of -action data, but its meaning includes the following possibilities: - -
            -
          • The parameter's value is provided in the P4 program. In this -case, the parameter behaves as if the direction were in. Such -an argument expression need not be a compile-time known value. -
          • -
          • The parameter's value is provided by the control plane software +(see Section 18.1).

            +
          • +
          • +

            For an action, a directionless parameter indicates that it is +"action data". See Section 14.1 for the meaning of +action data, but its meaning includes the following possibilities:

            +
            +
              +
            • +

              The parameter’s value is provided in the P4 program. In this +case, the parameter behaves as if the direction were in. Such +an argument expression need not be a compile-time known value.

              +
            • +
            • +

              The parameter’s value is provided by the control plane software when an entry is added to a table that uses that action. See -Section 14.1. -

        - -

        A directionless parameter of extern object type is passed by reference. -

        -

        Direction out parameters are always initialized at the beginning of -execution of the portion of the program that has the out parameters, -e.g. control, parser, action, function, etc. This +Section 14.1.

        +
      • +
      +
    + + +
    + + +
    +
    +

    A directionless parameter of extern object type is passed by reference.

    +
    +
    +

    Direction out parameters are always initialized at the beginning of +execution of the portion of the program that has the out parameters, +e.g. control, parser, action, function, etc. This initialization is not performed for parameters with any direction that -is not out. -

    -
      -
    • If a direction out parameter is of type header or -header_union, it is set to “invalid”. -
    • -
    • If a direction out parameter is of type header stack, all elements -of the header stack are set to “invalid”, and its nextIndex field -is initialized to 0 (see Section 8.18). -
    • -
    • If a direction out parameter is a compound type, e.g. a struct or +is not out.

      +
    +
    +
      +
    • +

      If a direction out parameter is of type header or +header_union, it is set to "invalid".

      +
    • +
    • +

      If a direction out parameter is of type header stack, all elements +of the header stack are set to "invalid", and its nextIndex field +is initialized to 0 (see Section 8.18).

      +
    • +
    • +

      If a direction out parameter is a compound type, e.g. a struct or tuple, other than one of the types listed above, then apply these -rules recursively to its members. -

    • -
    • If a direction out parameter has any other type, e.g. bit<W>, an -implementation need not initialize it to any predictable value. -
    - -

    For example, if a direction out parameter has type s2_t named p: -

    -
    -
    -
    header h1_t {
    -    bit<8> f1;
    -    bit<8> f2;
    -}
    -struct s1_t {
    -    h1_t h1a;
    -    bit<3> a;
    -    bit<7> b;
    -}
    -struct s2_t {
    -    h1_t h1b;
    -    s1_t s1;
    -    bit<5> c;
    -}
    -

    then at the beginning of execution of the part of the program that has -the out parameter p, it must be initialized so that p.h1b and -and p.s1.h1a are invalid. No other parts of p are required to be -initialized. -

    -

    Arguments are evaluated from left to right prior to the invocation of the +rules recursively to its members.

    + +
  • +

    If a direction out parameter has any other type, e.g. bit<W>, an +implementation need not initialize it to any predictable value.

    +
  • + +
    +
    +

    For example, if a direction out parameter has type s2_t named p:

    +
    +
    +
    +
    header h1_t {
    +    bit<8> f1;
    +    bit<8> f2;
    +}
    +struct s1_t {
    +    h1_t h1a;
    +    bit<3> a;
    +    bit<7> b;
    +}
    +struct s2_t {
    +    h1_t h1b;
    +    s1_t s1;
    +    bit<5> c;
    +}
    +
    +
    +
    +

    then at the beginning of execution of the part of the program that has +the out parameter p, it must be initialized so that p.h1b and +and p.s1.h1a are invalid. No other parts of p are required to be +initialized.

    +
    +
    +

    Arguments are evaluated from left to right prior to the invocation of the function itself. The order of evaluation is important when the expression supplied for an argument can have side-effects. Consider -the following example: -

    -
    -
    -
    extern void f(inout bit x, in bit y);
    -extern bit g(inout bit z);
    -bit a;
    -f(a, g(a));
    -

    Note that the evaluation of g may mutate its argument a, so the -compiler has to ensure that the value passed to f for its first +the following example:

    +
    +
    +
    +
    extern void f(inout bit x, in bit y);
    +extern bit g(inout bit z);
    +bit a;
    +f(a, g(a));
    +
    +
    +
    +

    Note that the evaluation of g may mutate its argument a, so the +compiler has to ensure that the value passed to f for its first parameter is not changed by the evaluation of the second argument. The semantics for evaluating a function call is given by the following algorithm (implementations can be different as long as they provide -the same result): -

    -
      -
    1. Arguments are evaluated from left to right as they appear in the -function call expression. -
    2. -
    3. If a parameter has a default value and no corresponding argument is -supplied, the default value is used as an argument. -
    4. -
    5. For each out and inout argument the corresponding +the same result):

      +
    +
    +
      +
    1. +

      Arguments are evaluated from left to right as they appear in the +function call expression.

      +
    2. +
    3. +

      If a parameter has a default value and no corresponding argument is +supplied, the default value is used as an argument.

      +
    4. +
    5. +

      For each out and inout argument the corresponding l-value is saved (so it cannot be changed by the evaluation of the following arguments). This is important if the argument -contains indexing operations into a header stack. +contains indexing operations into a header stack.

    6. -
    7. The value of each argument is saved into a temporary. +
    8. +

      The value of each argument is saved into a temporary.

    9. -
    10. The function is invoked with the temporaries as arguments. We are +
    11. +

      The function is invoked with the temporaries as arguments. We are guaranteed that the temporaries that are passed as arguments are -never aliased to each other, so this “generated” function call +never aliased to each other, so this "generated" function call can be implemented using call-by-reference if supported by the -architecture. -

    12. -
    13. On function return, the temporaries that correspond to out -or inout arguments are copied in order from left to right -into the l-values saved in Step 3. -
    - -

    According to this algorithm, the previous function call is equivalent -to the following sequence of statements: -

    -
    -
    -
    bit tmp1 = a;     // evaluate a; save result
    -bit tmp2 = g(a);  // evaluate g(a); save result; modifies a
    -f(tmp1, tmp2);    // evaluate f; modifies tmp1
    -a = tmp1;         // copy inout result back into a
    -

    To see why Step 3 in the above algorithm is important, consider the following -example: -

    -
    -
    -
    header H { bit z; }
    -H[2] s;
    -f(s[a].z, g(a));
    -

    The evaluation of this call is equivalent to the following sequence of -statements: -

    -
    -
    -
    bit tmp1 = a;          // save the value of a
    -bit tmp2 = s[tmp1].z;  // evaluate first argument
    -bit tmp3 = g(a);       // evaluate second argument; modifies a
    -f(tmp2, tmp3);         // evaluate f; modifies tmp2
    -s[tmp1].z = tmp2;      // copy inout result back; dest is not s[a].z
    -

    When used as arguments, extern objects can only be passed as -directionless parameterse.g., see the packet argument in the -very simple switch example. -

    6.8.1. Justification

    -

    The main reason for using copy-in/copy-out semantics (instead of the more common -call-by-reference semantics) is for controlling the side-effects of extern -functions and methods. extern methods and functions +architecture.

    + +
  • +

    On function return, the temporaries that correspond to out +or inout arguments are copied in order from left to right +into the l-values saved in Step 3.

    +
  • + +
    +
    +

    According to this algorithm, the previous function call is equivalent +to the following sequence of statements:

    +
    +
    +
    +
    bit tmp1 = a;     // evaluate a; save result
    +bit tmp2 = g(a);  // evaluate g(a); save result; modifies a
    +f(tmp1, tmp2);    // evaluate f; modifies tmp1
    +a = tmp1;         // copy inout result back into a
    +
    +
    +
    +

    To see why Step 3 in the above algorithm is important, consider the following +example:

    +
    +
    +
    +
    header H { bit z; }
    +H[2] s;
    +f(s[a].z, g(a));
    +
    +
    +
    +

    The evaluation of this call is equivalent to the following sequence of +statements:

    +
    +
    +
    +
    bit tmp1 = a;          // save the value of a
    +bit tmp2 = s[tmp1].z;  // evaluate first argument
    +bit tmp3 = g(a);       // evaluate second argument; modifies a
    +f(tmp2, tmp3);         // evaluate f; modifies tmp2
    +s[tmp1].z = tmp2;      // copy inout result back; dest is not s[a].z
    +
    +
    +
    +

    When used as arguments, extern objects can only be passed as +directionless parameters---e.g., see the packet argument in the +very simple switch example.

    +
    +
    +

    6.8.1. Justification

    +
    +

    The main reason for using copy-in/copy-out semantics (instead of the more common +call-by-reference semantics) is for controlling the side-effects of extern +functions and methods. extern methods and functions are the main mechanism by which a P4 program communicates with its -environment. With copy-in/copy-out semantics extern functions +environment. With copy-in/copy-out semantics extern functions cannot hold references to P4 program objects; this enables the -compiler to limit the side-effects that extern functions may -have on the P4 program both in space (they can only affect out +compiler to limit the side-effects that extern functions may +have on the P4 program both in space (they can only affect out parameters) and in time (side-effects can only occur at function call -time). -

    -

    In general, extern functions are arbitrarily powerful: they can store -information in global storage, spawn separate threads, “collude” with -each other to share information but they cannot access any +time).

    +
    +
    +

    In general, extern functions are arbitrarily powerful: they can store +information in global storage, spawn separate threads, "collude" with +each other to share information --- but they cannot access any variable in a P4 program. With copy-in/copy-out semantics the compiler -can still reason about P4 programs that invoke extern -functions. -

    -

    There are additional benefits of using copy-in copy-out semantics: -

    -
      -
    • It enables P4 to be compiled for architectures that do not support +can still reason about P4 programs that invoke extern +functions.

      +
    +
    +

    There are additional benefits of using copy-in copy-out semantics:

    +
    +
    +
      +
    • +

      It enables P4 to be compiled for architectures that do not support references (e.g., where all data is allocated to named registers. Such architectures may require indices into header stacks that appear -in a program to be compile-time known values.) -

    • -
    • It simplifies some compiler analyses, since function parameters can -never alias to each other within the function body. -
    - -
    -
    -
    parameterList
    -    : /* empty */
    +in a program to be compile-time known values.)

    + +
  • +

    It simplifies some compiler analyses, since function parameters can +never alias to each other within the function body.

    +
  • + +
    +
    +
    +
    parameterList
    +    : /* empty */
         | nonEmptyParameterList
         ;
     
     nonEmptyParameterList
         : parameter
    -    | nonEmptyParameterList "," parameter
    +    | nonEmptyParameterList "," parameter
         ;
     
     parameter
         : optAnnotations direction typeRef name
    -    | optAnnotations direction typeRef name "=" expression
    +    | optAnnotations direction typeRef name "=" expression
         ;
     
     direction
         : IN
         | OUT
         | INOUT
    -    | /* empty */
    -    ;
    -

    Following is a summary of the constraints imposed by the parameter -directions: -

    -
      -
    • When used as arguments, extern objects can only be passed as -directionless parameters. -
    • -
    • All constructor parameters are evaluated at compilation-time, and in -consequence they must all be directionless (they cannot be in, -out, or inout); this applies to package, control, parser, -and extern objects. Expressions for these parameters must be supplied + | /* empty */ + ; +
    +
    +
    +

    Following is a summary of the constraints imposed by the parameter +directions:

    +
    +
    +
      +
    • +

      When used as arguments, extern objects can only be passed as +directionless parameters.

      +
    • +
    • +

      All constructor parameters are evaluated at compilation-time, and in +consequence they must all be directionless (they cannot be in, +out, or inout); this applies to package, control, parser, +and extern objects. Expressions for these parameters must be supplied at compile-time, and they must evaluate to compile-time known values. -See Section 15 for further details. +See Chapter 15 for further details.

    • -
    • For actions all directionless parameters must be at the end of the -parameter list. When an action appears in a table's actions +
    • +

      For actions all directionless parameters must be at the end of the +parameter list. When an action appears in a table's actions list, only the parameters with a direction must be bound. -See Section 14.1 for further details. +See Section 14.1 for further details.

    • -
    • Actions can also be explicitly invoked using function call syntax, +
    • +

      Actions can also be explicitly invoked using function call syntax, either from a control block or from another action. In this case, values for all action parameters must be supplied explicitly, including values for the directionless parameters. The directionless -parameters in this case behave like in parameters. -See Section 14.1.1 for further details. +parameters in this case behave like in parameters. +See Section 14.1.1 for further details.

    • -
    • Default expressions are only allowed for ‘in’ or direction-less +
    • +

      Default expressions are only allowed for 'in' or direction-less parameters, and the expressions supplied as defaults must be -compile-time known values. +compile-time known values.

    • -
    • If parameters with default values do not appear at the +
    • +

      If parameters with default values do not appear at the end of the list of parameters, invocations that use the default values must use named arguments, as in the following -example: -

    - -
    -
    -
    extern void f(in bit a, in bit<3> b = 2, in bit<5> c);
    -
    -void g()
    -{
    -  f(a = 1, b = 2, c = 3);  // ok
    -  f(a = 1, c = 3);  // ok, equivalent to the previous call, b uses default value
    -  f(1, 2, 3);       // ok, equivalent to the previous call
    -  // f(1, 3); // illegal, since the parameter b is not the last in the list
    -}

    6.8.2. Optional parameters

    -

    A parameter that is annotated with the @optional annotation is +example:

    + + +
    +
    +
    +
    extern void f(in bit a, in bit<3> b = 2, in bit<5> c);
    +
    +void g()
    +{
    +  f(a = 1, b = 2, c = 3);  // ok
    +  f(a = 1, c = 3);  // ok, equivalent to the previous call, b uses default value
    +  f(1, 2, 3);       // ok, equivalent to the previous call
    +  // f(1, 3); // illegal, since the parameter b is not the last in the list
    +}
    +
    +
    +
    +
    +

    6.8.2. Optional parameters

    +
    +

    A parameter that is annotated with the @optional annotation is optional: the user may omit the value for that parameter in an invocation. Optional parameters can only appear for arguments of: packages, parser types, control types, extern functions, extern methods, and extern object @@ -2276,450 +3287,647 @@

    The implementation of such objects is not expressed in P4, so the +list.

    +

    +
    +

    The implementation of such objects is not expressed in P4, so the meaning and implementation of optional parameters should be specified by the target architecture. For example, we can imagine a two-stage switch architecture where the second stage is optional. This could be -declared as a package with an optional parameter: -

    -
    -
    -
    package pipeline(/* parameters omitted */);
    -package switch(pipeline first, @optional pipeline second);
    -
    -pipeline(/* arguments omitted */) ingress;
    -switch(ingress) main;   // a switch with a single-stage pipeline
    -

    Here the target architecture could implement the elided optional argument using an empty pipeline. -

    -

    The following example shows optional parameters and parameters with -default values. -

    -
    -
    -
    extern void h(in bit<32> a, in bool b = true);  // default value
    -
    -// function calls
    -h(10);  // same as h(10, true);
    -h(a = 10);  // same as h(10, true);
    -h(a = 10, b = true);
    -
    -struct Empty {}
    -control nothing(inout Empty h, inout Empty m) {
    -   apply {}
    -}
    -
    -parser parserProto<H, M>(packet_in p, out H h, inout M m);
    -control controlProto<H, M>(inout H h, inout M m);
    -
    -package pack<HP, MP, HC, MC>(
    -    @optional parserProto<HP, MP> _parser,  // optional parameter
    -    controlProto<HC, MC> _control = nothing()); // default parameter value
    -
    -pack() main;   // No value for _parser, _control is an instance of nothing()

    6.9. Name resolution

    -

    P4 objects that introduce namespaces are organized in a hierarchical +declared as a package with an optional parameter:

    +
    +
    +
    +
    package pipeline(/* parameters omitted */);
    +package switch(pipeline first, @optional pipeline second);
    +
    +pipeline(/* arguments omitted */) ingress;
    +switch(ingress) main;   // a switch with a single-stage pipeline
    +
    +
    +
    +

    Here the target architecture could implement the elided optional argument using an empty pipeline.

    +
    +
    +

    The following example shows optional parameters and parameters with +default values.

    +
    +
    +
    +
    extern void h(in bit<32> a, in bool b = true);  // default value
    +
    +// function calls
    +h(10);  // same as h(10, true);
    +h(a = 10);  // same as h(10, true);
    +h(a = 10, b = true);
    +
    +struct Empty {}
    +control nothing(inout Empty h, inout Empty m) {
    +   apply {}
    +}
    +
    +parser parserProto<H, M>(packet_in p, out H h, inout M m);
    +control controlProto<H, M>(inout H h, inout M m);
    +
    +package pack<HP, MP, HC, MC>(
    +    @optional parserProto<HP, MP> _parser,  // optional parameter
    +                             controlProto<HC, MC> _control = nothing()); // default parameter value
    +
    +pack() main;   // No value for _parser, _control is an instance of nothing()
    +
    +
    +
    +
    +
    +

    6.9. Name resolution

    +
    +

    P4 objects that introduce namespaces are organized in a hierarchical fashion. There is a top-level unnamed namespace containing all -top-level declarations. -

    -

    Identifiers prefixed with a dot are always resolved in the top-level -namespace. -

    -
    -
    -
    const bit<32> x = 2;
    -control c() {
    -   int<32> x = 0;
    -   apply {
    -       x = x + (int<32>).x;  // x is the int<32> local variable,
    -                             // .x is the top-level bit<32> variable
    -   }
    -}
    -

    References to resolve an identifier are attempted inside-out, starting +top-level declarations.

    +
    +
    +

    Identifiers prefixed with a dot are always resolved in the top-level +namespace.

    +
    +
    +
    +
    const bit<32> x = 2;
    +control c() {
    +   int<32> x = 0;
    +   apply {
    +       x = x + (int<32>).x;  // x is the int<32> local variable,
    +                             // .x is the top-level bit<32> variable
    +   }
    +}
    +
    +
    +
    +

    References to resolve an identifier are attempted inside-out, starting with the current scope and proceeding to all lexically enclosing scopes. The compiler may provide a warning if multiple resolutions are -possible for the same name (name shadowing). -

    -
    -
    -
    const bit<4> x = 1;
    -control p() {
    -    const bit<8> x = 8;    // x declaration shadows global x
    -    const bit<4> y = .x;   // reference to top-level x
    -    const bit<8> z = x;    // reference to p's local x
    -    apply {}
    -}

    6.10. Visibility

    -

    Identifiers defined in the top-level namespace are globally -visible. Declarations within a parser or control are -private and cannot be referred to from outside of the enclosing parser -or control. -

    7. P4 data types

    -

    P416 is a statically-typed language. Programs that do not pass +possible for the same name (name shadowing).

    +
    +
    +
    +
    const bit<4> x = 1;
    +control p() {
    +    const bit<8> x = 8;    // x declaration shadows global x
    +    const bit<4> y = .x;   // reference to top-level x
    +    const bit<8> z = x;    // reference to p's local x
    +    apply {}
    +}
    +
    +
    +
    +
    +

    6.10. Visibility

    +
    +

    Identifiers defined in the top-level namespace are globally +visible. Declarations within a parser or control are +private and cannot be referred to from outside of the enclosing parser +or control.

    +
    +
    +
    +
    +
    +

    7. P4 data types

    +
    +
    +

    P416 is a statically-typed language. Programs that do not pass the type checker are considered invalid and rejected by the compiler. P4 provides a number of base types as well as type operators that construct derived types. Some values can be converted to a different type using casts. However, to make user intents clear, implicit casts are only allowed in a few circumstances and the range of casts available is intentionally -restricted. -

    7.1. Base types

    -

    P4 supports the following built-in base types: -

    -
      -
    • The void type, which has no values and can be used only in a few -restricted circumstances. -
    • -
    • The error type, which is used to convey errors in a -target-independent, compiler-managed way. -
    • -
    • The string type, which can be used with compile-time known -values of type string. -
    • -
    • The match_kind type, which is used for describing the implementation of -table lookups, -
    • -
    • bool, which represents Boolean values -
    • -
    • int, which represents arbitrary-sized integer values -
    • -
    • Bit-strings of fixed width, denoted by bit<> -
    • -
    • Fixed-width signed integers represented using two's complement int<> -
    • -
    • Bit-strings of dynamically-computed width with a fixed maximum width varbit<> -
    - -
    -
    -
    baseType
    +restricted.

    +
    +
    +

    7.1. Base types

    +
    +

    P4 supports the following built-in base types:

    +
    +
    +
      +
    • +

      The void type, which has no values and can be used only in a few +restricted circumstances.

      +
    • +
    • +

      The error type, which is used to convey errors in a +target-independent, compiler-managed way.

      +
    • +
    • +

      The string type, which can be used with compile-time known +values of type string.

      +
    • +
    • +

      The match_kind type, which is used for describing the implementation of +table lookups,

      +
    • +
    • +

      bool, which represents Boolean values

      +
    • +
    • +

      int, which represents arbitrary-sized integer values

      +
    • +
    • +

      Bit-strings of fixed width, denoted by bit<>

      +
    • +
    • +

      Fixed-width signed integers represented using two’s complement int<>

      +
    • +
    • +

      Bit-strings of dynamically-computed width with a fixed maximum width varbit<>

      +
    • +
    +
    +
    +
    +
    baseType
         : BOOL
         | MATCH_KIND
         | ERROR
         | BIT
         | STRING
         | INT
    -    | BIT "<" INTEGER ">"
    -    | INT "<" INTEGER ">"
    -    | VARBIT "<" INTEGER ">"
    -    | BIT "<" "(" expression ")" ">"
    -    | INT "<" "(" expression ")" ">"
    -    | VARBIT "<" "(" expression ")" ">"
    -    ;

    7.1.1. The void type

    -

    The void type is written void. It contains no values. It is -not included in the production rule baseType as it can only appear in few -restricted places in P4 programs. -

    7.1.2. The error type

    -

    The error type contains opaque distinct values that can be used to signal -errors. It is written as error. New elements of the error type -are defined with the syntax: -

    -
    -
    -
    errorDeclaration
    -    : ERROR "{" identifierList "}"
    -    ;
    -

    All elements of the error type are inserted into the error + | BIT "<" INTEGER ">" + | INT "<" INTEGER ">" + | VARBIT "<" INTEGER ">" + | BIT "<" "(" expression ")" ">" + | INT "<" "(" expression ")" ">" + | VARBIT "<" "(" expression ")" ">" + ; +

    +
    +
    +

    7.1.1. The void type

    +
    +

    The void type is written void. It contains no values. It is +not included in the production rule baseType as it can only appear in few +restricted places in P4 programs.

    +
    +
    +
    +

    7.1.2. The error type

    +
    +

    The error type contains opaque distinct values that can be used to signal +errors. It is written as error. New elements of the error type +are defined with the syntax:

    +
    +
    +
    +
    errorDeclaration
    +    : ERROR "{" identifierList "}"
    +    ;
    +
    +
    +
    +

    All elements of the error type are inserted into the error namespace, irrespective of the place where an error is -defined. error is similar to an enumeration (enum) -type in other languages. A program can contain multiple error declarations, which +defined. error is similar to an enumeration (enum) +type in other languages. A program can contain multiple error declarations, which the compiler will merge together. It is an error to declare the same -identifier multiple times. Expressions of type error are -described in Section 8.2. -

    -

    For example, the following declaration creates two elements of the error -type (these errors are declared in the P4 core library): -

    -
    -
    -
    error { ParseError, PacketTooShort }
    -

    The underlying representation of errors is target-dependent. -

    7.1.3. The match kind type

    -

    The match_kind type is very similar to the error type and +identifier multiple times. Expressions of type error are +described in Section 8.2.

    +
    +
    +

    For example, the following declaration creates two elements of the error +type (these errors are declared in the P4 core library):

    +
    +
    +
    +
    error { ParseError, PacketTooShort }
    +
    +
    +
    +

    The underlying representation of errors is target-dependent.

    +
    +
    +
    +

    7.1.3. The match kind type

    +
    +

    The match_kind type is very similar to the error type and is used to declare a set of distinct names that may be -used in a table's key property (described in Section -14.2.1). +used in a table’s key property (described in Section 14.2.1). All identifiers are inserted into the top-level namespace. -It is an error to declare the same match_kind -identifier multiple times. -

    -
    -
    -
    matchKindDeclaration
    -    : MATCH_KIND "{" identifierList optTrailingComma "}"
    -    ;
    -

    The P4 core library contains the following match_kind declaration: -

    -
    -
    -
    match_kind {
    -   exact,
    -   ternary,
    +It is an error to declare the same match_kind
    +identifier multiple times.

    +
    +
    +
    +
    matchKindDeclaration
    +    : MATCH_KIND "{" identifierList optTrailingComma "}"
    +    ;
    +
    +
    +
    +

    The P4 core library contains the following match_kind declaration:

    +
    +
    +
    +
    match_kind {
    +   exact,
    +   ternary,
        lpm
    -}
    -

    Architectures may support additional match_kinds. The -declaration of new match_kinds can only occur within model -description files; P4 programmers cannot declare new match kinds. -

    -

    Operations on values of type match_kind are described in Section -8.4. -

    7.1.4. The Boolean type

    -

    The Boolean type bool contains just two values, false and true. -Boolean values are not integers or bit-strings. -

    7.1.5. Strings

    -

    The type string represents strings. There are no operations on -string values; one cannot declare variables with a string type. -Parameters with type string can be only directionless (see Section -6.8). P4 does not support string manipulation -in the dataplane; the string type is only allowed for describing +} +

    +
    +
    +

    Architectures may support additional match_kinds. The +declaration of new match_kinds can only occur within model +description files; P4 programmers cannot declare new match kinds.

    +
    +
    +

    Operations on values of type match_kind are described in +Section 8.4.

    +
    +
    +
    +

    7.1.4. The Boolean type

    +
    +

    The Boolean type bool contains just two values, false and true. +Boolean values are not integers or bit-strings.

    +
    +
    +
    +

    7.1.5. Strings

    +
    +

    The type string represents strings. There are no operations on +string values; one cannot declare variables with a string type. +Parameters with type string can be only directionless (see +Section 6.8). +P4 does not support string manipulation +in the dataplane; the string type is only allowed for describing compile-time known values (i.e., string literals, as discussed in -Section 6.4.3.3). Even so, the string type is useful, +Section Section 6.4.3.3). Even so, the string type is useful, for example, in giving the type signature for extern functions such as -the following: -

    -
    -
    -
    extern void log(string message);
    -

    As another example, the following annotation indicates that the specified -name should be used for a given table in the generated control-plane API: -

    -
    -
    -
    @name("acl") table t1 { /* body omitted */ }

    7.1.6. Integers (signed and unsigned)

    -

    P4 supports arbitrary-size integer values. The typing rules for the -integer types are chosen according to the following principles: -

    -
      -
    • Inspired by C: Typing of integers is modeled after the +the following:

      +
    +
    +
    +
    extern void log(string message);
    +
    +
    +
    +

    As another example, the following annotation indicates that the specified +name should be used for a given table in the generated control-plane API:

    +
    +
    +
    +
    @name("acl") table t1 { /* body omitted */ }
    +
    +
    +
    +
    +

    7.1.6. Integers (signed and unsigned)

    +
    +

    P4 supports arbitrary-size integer values. The typing rules for the +integer types are chosen according to the following principles:

    +
    +
    +
      +
    • +

      Inspired by C: Typing of integers is modeled after the well-defined parts of C, expanded to cope with arbitrary fixed-width integers. In particular, the type of the result of an expression only depends on the expression operands, and not on how the result -of the expression is consumed. +of the expression is consumed.

    • -
    • No undefined behaviors: P4 attempts to avoid many of C's +
    • +

      No undefined behaviors: P4 attempts to avoid many of C’s behaviors, which include the size of an integer (int), the results produced on overflow, and the results produced for some input combinations (e.g., shifts with negative amounts, overflows on signed numbers, etc.). P4 computations on integer types have no -undefined behaviors. +undefined behaviors.

    • -
    • Least surprise: The P4 typing rules are chosen to behave as -closely as possible to traditional well-behaved C programs. +
    • +

      Least surprise: The P4 typing rules are chosen to behave as +closely as possible to traditional well-behaved C programs.

    • -
    • Forbid rather than surprise: Rather than provide surprising or +
    • +

      Forbid rather than surprise: Rather than provide surprising or undefined results (e.g., in C comparisons between signed and unsigned integers), we have chosen to forbid expressions with ambiguous interpretations. For example, P4 does not allow binary -operations that combine signed and unsigned integers. -

    - -

    The priority of arithmetic operations is identical to Ce.g., -multiplication binds tighter than addition. -

    7.1.6.1. Portability
    -

    No P4 target can support all possible types and operations. For -example, the type bit<23132312> is legal in P4, but +operations that combine signed and unsigned integers.

    + + +
    +
    +

    The priority of arithmetic operations is identical to C---e.g., +multiplication binds tighter than addition.

    +
    +
    +
    7.1.6.1. Portability
    +
    +

    No P4 target can support all possible types and operations. For +example, the type bit<23132312> is legal in P4, but it is highly unlikely to be supported on any target in practice. Hence, each target can impose restrictions on the types it can support. Such -restrictions may include: -

    -
      -
    • The maximum width supported -
    • -
    • Alignment and padding constraints (e.g., arithmetic may only be -supported on widths which are an integral number of bytes). -
    • -
    • Constraints on some operands (e.g., some architectures may only +restrictions may include:

      +
    +
    +
      +
    • +

      The maximum width supported

      +
    • +
    • +

      Alignment and padding constraints (e.g., arithmetic may only be +supported on widths which are an integral number of bytes).

      +
    • +
    • +

      Constraints on some operands (e.g., some architectures may only support multiplications by small values, or shifts with small -values). -

    - -

    The documentation supplied with a target should clearly specify restrictions, and +values).

    + + +
    +
    +

    The documentation supplied with a target should clearly specify restrictions, and target-specific compilers should provide clear error messages when such restrictions are encountered. An architecture may reject a well-typed P4 program and still be conformant to the P4 spec. However, if an architecture accepts a P4 program as valid, the runtime program -behavior should match this specification. -

    7.1.6.2. Unsigned integers (bit-strings)
    -

    An unsigned integer (which we also call a “bit-string”) has an -arbitrary width, expressed in bits. A bit-string of width W is -declared as: bit<W>. W must be an expression that evaluates to a -local compile-time known value (see Section 18.1) that is a +behavior should match this specification.

    +
    +
    +
    +
    7.1.6.2. Unsigned integers (bit-strings)
    +
    +

    An unsigned integer (which we also call a "bit-string") has an +arbitrary width, expressed in bits. A bit-string of width W is +declared as: bit<W>. W must be an expression that evaluates to a +local compile-time known value (see Section 18.1) that is a non-negative integer. When using an expression for the size, the expression must be parenthesized. Bitstrings with width 0 are allowed; they have no actual bits, and can only have the value 0. See -8.25 for additional details. -Note that bit<W> type refers to both cases of bit<W> -and bit<(expression)> where the width is a compile-time known value. -

    -
    -
    -
    const bit<32> x = 10;   // 32-bit constant with value 10.
    -const bit<(x + 2)> y = 15;  // 12-bit constant with value 15.
    -                            // expression for width must use ()
    -

    Bits within a bit-string are numbered from 0 to W-1. Bit 0 -is the least significant, and bit W-1 is the most significant. -

    -

    For example, the type bit<128> denotes the type of bit-string +Section 8.25 for additional details. +Note that bit<W> type refers to both cases of bit<W> +and bit<(expression)> where the width is a compile-time known value.

    +
    +
    +
    +
    const bit<32> x = 10;   // 32-bit constant with value 10.
    +const bit<(x + 2)> y = 15;  // 12-bit constant with value 15.
    +                            // expression for width must use ()
    +
    +
    +
    +

    Bits within a bit-string are numbered from 0 to W-1. Bit 0 +is the least significant, and bit W-1 is the most significant.

    +
    +
    +

    For example, the type bit<128> denotes the type of bit-string values with 128 bits numbered from 0 to 127, where bit 127 is the most -significant. -

    -

    The type bit is a shorthand for bit<1>. -

    -

    P4 architectures may impose additional constraints on bit types: for +significant.

    +
    +
    +

    The type bit is a shorthand for bit<1>.

    +
    +
    +

    P4 architectures may impose additional constraints on bit types: for example, they may limit the maximum size, or they may only support some arithmetic operations on certain sizes (e.g., 16-, 32-, and 64- -bit values). -

    -

    All operations that can be performed on unsigned integers are -described in Section 8.6. -

    7.1.6.3. Signed Integers
    -

    Signed integers are represented using two's complement. An integer -with W bits is declared as: int<W>. W must be an expression that +bit values).

    +
    +
    +

    All operations that can be performed on unsigned integers are +described in Section 8.6.

    +
    +
    +
    +
    7.1.6.3. Signed Integers
    +
    +

    Signed integers are represented using two’s complement. An integer +with W bits is declared as: int<W>. W must be an expression that evaluates to a local compile-time known (see Section -18.1) value that is a non-negative integer. Note -that int<W> type refers to both cases of int<W> and -int<(expression)> where the width is a local compile-time known -value. -

    -

    Bits within an integer are numbered from 0 to W-1. Bit 0 -is the least significant, and bit W-1 is the sign bit. -

    -

    For example, the type int<64> describes the type of integers +Section 18.1) value that is a non-negative integer. Note +that int<W> type refers to both cases of int<W> and +int<(expression)> where the width is a local compile-time known +value.

    +
    +
    +

    Bits within an integer are numbered from 0 to W-1. Bit 0 +is the least significant, and bit W-1 is the sign bit.

    +
    +
    +

    For example, the type int<64> describes the type of integers represented using exactly 64 bits with bits numbered from 0 to 63, -where bit 63 is the most significant (sign) bit. -

    -

    P4 architectures may impose additional constraints on signed types: +where bit 63 is the most significant (sign) bit.

    +
    +
    +

    P4 architectures may impose additional constraints on signed types: for example, they may limit the maximum size, or they may only support some arithmetic operations on certain sizes (e.g., 16-, 32-, and 64- -bit values). -

    -

    All operations that can be performed on signed integers are described -in Section 8.7. -

    -

    A signed integer with width 1 can only have two legal values: 0 and -1. -

    7.1.6.4. Dynamically-sized bit-strings
    -

    Some network protocols use fields whose size is only known at runtime +bit values).

    +
    +
    +

    All operations that can be performed on signed integers are described +in Section 8.6.

    +
    +
    +

    A signed integer with width 1 can only have two legal values: 0 and -1.

    +
    +
    +
    +
    7.1.6.4. Dynamically-sized bit-strings
    +
    +

    Some network protocols use fields whose size is only known at runtime (e.g., IPv4 options). To support restricted manipulations of such values, P4 provides a special bit-string type whose size is set at -runtime, called a varbit. -

    -

    The type varbit<W> denotes a bit-string with a width of at most W -bits, where W is a local compile-time known value (see Section -18.1) that is a non-negative integer. For -example, the type varbit<120> denotes the type of bit-string values +runtime, called a varbit.

    +
    +
    +

    The type varbit<W> denotes a bit-string with a width of at most W +bits, where W is a local compile-time known value (see Section +Section 18.1) that is a non-negative integer. For +example, the type varbit<120> denotes the type of bit-string values that may have between 0 and 120 bits. Most operations that are -applicable to fixed-size bit-strings (unsigned numbers) cannot be -performed on dynamically sized bit-strings. Note that varbit<W> -type refers to both cases of varbit<W> and varbit<(expression)> -where the width is a compile-time known value. -

    -

    P4 architectures may impose additional constraints on varbit types: -for example, they may limit the maximum size, or they may require varbit -values to always contain an integer number of bytes at runtime. -

    -

    All operations that can be performed on varbits are described in -Section 8.10. -

    7.1.6.5. Arbitrary-precision integers
    -

    The arbitrary-precision data type describes integers with an unlimited -precision. This type is written as int. -

    -

    This type is reserved for integer literals and expressions that -involve only literals. No P4 runtime value can have an int +applicable to fixed-size bit-strings (unsigned numbers) cannot be +performed on dynamically sized bit-strings. Note that varbit<W> +type refers to both cases of varbit<W> and varbit<(expression)> +where the width is a compile-time known value.

    +
    +
    +

    P4 architectures may impose additional constraints on varbit types: +for example, they may limit the maximum size, or they may require varbit +values to always contain an integer number of bytes at runtime.

    +
    +
    +

    All operations that can be performed on varbits are described in +Section 8.10.

    +
    +
    +
    +
    7.1.6.5. Arbitrary-precision integers
    +
    +

    The arbitrary-precision data type describes integers with an unlimited +precision. This type is written as int.

    +
    +
    +

    This type is reserved for integer literals and expressions that +involve only literals. No P4 runtime value can have an int type; at compile time the compiler will convert all int values that have a runtime component to fixed-width types, according to the rules -described below. -

    -

    All operations that can be performed on arbitrary-precision integers -are described in Section 8.8. The following example +described below.

    +
    +
    +

    All operations that can be performed on arbitrary-precision integers +are described in Section 8.8. The following example shows three constant definitions whose values are arbitrary-precision -integers. -

    -
    -
    -
    const int a = 5;
    -const int b = 2 * a;
    -const int c = b - a + 3;
    -

    Parameters with type int are not supported for actions. Parameters -with type int for other callable entities of a program, +integers.

    +
    +
    +
    +
    const int a = 5;
    +const int b = 2 * a;
    +const int c = b - a + 3;
    +
    +
    +
    +

    Parameters with type int are not supported for actions. Parameters +with type int for other callable entities of a program, e.g. controls, parsers, or functions, must be directionless, indicating that all calls must provide a compile-time known value as -an argument for such a parameter. See Section -6.8 for more details on directionless -parameters. -

    7.1.6.6. Integer literal types
    -

    The types of integer literals are as follows: -

    -
      -
    • An integer with no type prefix has type int. -
    • -
    • A non-negative integer prefixed with an integer width W and the -character w has type bit<W>. -
    • -
    • An integer prefixed with an integer width W and the character s -has type int<W>. -
    - -

    The table below shows several examples of integer literals and their -types. For additional examples of literals see Section -6.4.3. -

    - - - - - - - - - -
    Literal Interpretation
    10 Type is int, value is 10
    8w10 Type is bit<8>, value is 10
    8s10 Type is int<8>, value is 10
    2s3 Type is int<2>, value is -1 (last 2 bits), overflow warning
    1w10 Type is bit<1>, value is 0 (last bit), overflow warning
    1s1 Type is int<1>, value is -1, overflow warning

    7.2. Derived types

    -

    P4 provides a number of type constructors that can be used to derive -additional types including: -

    -
      -
    • enum -
    • -
    • header -
    • -
    • header stacks -
    • -
    • struct -
    • -
    • header_union -
    • -
    • tuple -
    • -
    • type specialization -
    • -
    • extern -
    • -
    • parser -
    • -
    • control -
    • -
    • package -
    - -

    The types header, header_union, enum, struct, extern, parser, control, -and package can only be used in type declarations, where they +an argument for such a parameter. See +Section 6.8 for more details on directionless +parameters.

    +
    +
    +
    +
    7.1.6.6. Integer literal types
    +
    +

    The types of integer literals are as follows:

    +
    +
    +
      +
    • +

      An integer with no type prefix has type int.

      +
    • +
    • +

      A non-negative integer prefixed with an integer width W and the +character w has type bit<W>.

      +
    • +
    • +

      An integer prefixed with an integer width W and the character s +has type int<W>.

      +
    • +
    +
    +
    +

    The table below shows several examples of integer literals and their +types. For additional examples of literals see Section 6.4.3.

    +
    + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    LiteralInterpretation

    10

    Type is int, value is 10

    8w10

    Type is bit<8>, value is 10

    8s10

    Type is int<8>, value is 10

    2s3

    Type is int<2>, value is -1 (last 2 bits), overflow warning

    1w10

    Type is bit<1>, value is 0 (last bit), overflow warning

    1s1

    Type is int<1>, value is -1, overflow warning

    +
    +
    +
    +
    +

    7.2. Derived types

    +
    +

    P4 provides a number of type constructors that can be used to derive +additional types including:

    +
    +
    +
      +
    • +

      enum

      +
    • +
    • +

      header

      +
    • +
    • +

      header stacks

      +
    • +
    • +

      struct

      +
    • +
    • +

      header_union

      +
    • +
    • +

      tuple

      +
    • +
    • +

      type specialization

      +
    • +
    • +

      extern

      +
    • +
    • +

      parser

      +
    • +
    • +

      control

      +
    • +
    • +

      package

      +
    • +
    +
    +
    +

    The types header, header_union, enum, struct, extern, parser, control, +and package can only be used in type declarations, where they introduce a new name for the type. The type can subsequently be -referred to using this identifier. -

    -

    Other types cannot be declared, but are synthesized by the compiler +referred to using this identifier.

    +
    +
    +

    Other types cannot be declared, but are synthesized by the compiler internally to represent the type of certain language constructs. These -types are described in Section 7.2.9: set types and +types are described in Section 7.2.9: set types and function types. For example, the programmer cannot declare a variable -with type “set”, but she can write an expression whose value evaluates -to a set type. These types are used during type-checking. -

    -
    -
    -
    typeDeclaration
    +with type "set", but she can write an expression whose value evaluates
    +to a set type. These types are used during type-checking.

    +
    +
    +
    +
    typeDeclaration
         : derivedTypeDeclaration
    -    | typedefDeclaration ";"
    -    | parserTypeDeclaration ";"
    -    | controlTypeDeclaration ";"
    -    | packageTypeDeclaration ";"
    +    | typedefDeclaration ";"
    +    | parserTypeDeclaration ";"
    +    | controlTypeDeclaration ";"
    +    | packageTypeDeclaration ";"
         ;
     
     derivedTypeDeclaration
    @@ -2750,922 +3958,1441 @@ 

    7.2.1. Enumeration types

    -

    An enumeration type is defined using the following syntax: -

    -
    -
    -
    enumDeclaration
    -    : optAnnotations ENUM name "{" identifierList optTrailingComma "}"
    -    | optAnnotations ENUM typeRef name "{"
    -      specifiedIdentifierList optTrailingComma "}"
    +    ;
    +
    +
    +
    +

    7.2.1. Enumeration types

    +
    +

    An enumeration type is defined using the following syntax:

    +
    +
    +
    +
    enumDeclaration
    +    : optAnnotations ENUM name "{" identifierList optTrailingComma "}"
    +    | optAnnotations ENUM typeRef name "{"
    +      specifiedIdentifierList optTrailingComma "}"
         ;
     
     identifierList
         : name
    -    | identifierList "," name
    +    | identifierList "," name
         ;
     
     specifiedIdentifierList
         : specifiedIdentifier
    -    | specifiedIdentifierList "," specifiedIdentifier
    +    | specifiedIdentifierList "," specifiedIdentifier
         ;
     
     specifiedIdentifier
    -    : name "=" initializer
    -    ;
    -

    For example, the declaration -

    -
    -
    -
    enum Suits { Clubs, Diamonds, Hearths, Spades }
    -

    introduces a new enumeration type, which contains four -elementse.g., Suits.Clubs. An enum declaration + : name "=" initializer + ;

    +
    +
    +
    +

    For example, the declaration

    +
    +
    +
    +
    enum Suits { Clubs, Diamonds, Hearths, Spades }
    +
    +
    +
    +

    introduces a new enumeration type, which contains four +elements---e.g., Suits.Clubs. An enum declaration introduces a new identifier in the current scope for naming the created type along with its distinct elements. -The underlying representation of the Suits enum is not -specified, so their “size” in bits is not specified (it is -target-specific). -

    -

    It is also possible to specify an enum with an underlying representation. -These are sometimes called serializable enums, because headers are -allowed to have fields with such enum types. This requires the programmer provide both the fixed-width unsigned (or signed) integer type and +The underlying representation of the Suits enum is not +specified, so their "size" in bits is not specified (it is +target-specific).

    +
    +
    +

    It is also possible to specify an enum with an underlying representation. +These are sometimes called serializable enums, because headers are +allowed to have fields with such enum types. This requires the programmer provide both the fixed-width unsigned (or signed) integer type and an associated integer value for each symbolic entry in the enumeration. -The symbol typeRef in the grammar above must be one of the following types: -

    -
      -
    • an unsigned integer, i.e. bit<W> for some local compile-time known value W. -
    • -
    • a signed integer, i.e. int<W> for some local compile-time known value W. -
    • -
    • a type name declared via typedef, where the base type of that type is either -one of the types listed above, or another typedef name that meets these conditions. -For example, the declaration -
    - -
    -
    -
    enum bit<16> EtherType {
    -  VLAN      = 0x8100,
    -  QINQ      = 0x9100,
    -  MPLS      = 0x8847,
    -  IPV4      = 0x0800,
    -  IPV6      = 0x86dd
    -}
    -

    introduces a new enumeration type, which contains five elementse.g., -EtherType.IPV4. This enum declaration specifies the fixed-width unsigned integer representation -for each entry in the enum and provides an underlying type: bit<16>. -This kind of enum declaration can be thought of as declaring a new bit<16> +The symbol typeRef in the grammar above must be one of the following types:

    +
    +
    +
      +
    • +

      an unsigned integer, i.e. bit<W> for some local compile-time known value W.

      +
    • +
    • +

      a signed integer, i.e. int<W> for some local compile-time known value W.

      +
    • +
    • +

      a type name declared via typedef, where the base type of that type is either +one of the types listed above, or another typedef name that meets these conditions. +For example, the declaration

      +
    • +
    +
    +
    +
    +
    enum bit<16> EtherType {
    +  VLAN      = 0x8100,
    +  QINQ      = 0x9100,
    +  MPLS      = 0x8847,
    +  IPV4      = 0x0800,
    +  IPV6      = 0x86dd
    +}
    +
    +
    +
    +

    introduces a new enumeration type, which contains five elements---e.g., +EtherType.IPV4. This enum declaration specifies the fixed-width unsigned integer representation +for each entry in the enum and provides an underlying type: bit<16>. +This kind of enum declaration can be thought of as declaring a new bit<16> type, where variables or fields of this type are expected to be unsigned 16-bit integer values, and the mapping of symbolic to numeric values defined by the -enum are also defined as a part of this declaration. In this way, -an enum with an underlying type can be thought of as being a type derived +enum are also defined as a part of this declaration. In this way, +an enum with an underlying type can be thought of as being a type derived from the underlying type carrying equality, assignment, and casts to/from the -underlying type. -

    -

    Compiler implementations are expected to raise an error if the fixed-width integer representation -for an enumeration entry falls outside the representation range of the underlying type. -

    -

    For example, the declaration -

    -
    -
    -
    enum bit<8> FailingExample {
    -  first           = 1,
    -  second          = 2,
    -  third           = 3,
    -  unrepresentable = 300
    -}
    -

    would raise an error because 300, the value associated with -FailingExample.unrepresentable cannot be represented as a bit<8> value. -

    -

    The initializer expression must be a compile-time known value. -

    -

    Annotations, represented by the non-terminal optAnnotations, are -described in Section 20. -

    -

    Operations on enum values are described in Section -8.3. -

    7.2.2. Header types

    -

    The declaration of a header type is given by the following -syntax: -

    -
    -
    -
    headerTypeDeclaration
    -    : optAnnotations HEADER name optTypeParameters "{" structFieldList "}"
    +underlying type.

    +
    +
    +

    Compiler implementations are expected to raise an error if the fixed-width integer representation +for an enumeration entry falls outside the representation range of the underlying type.

    +
    +
    +

    For example, the declaration

    +
    +
    +
    +
    enum bit<8> FailingExample {
    +  first           = 1,
    +  second          = 2,
    +  third           = 3,
    +  unrepresentable = 300
    +}
    +
    +
    +
    +

    would raise an error because 300, the value associated with +FailingExample.unrepresentable cannot be represented as a bit<8> value.

    +
    +
    +

    The initializer expression must be a compile-time known value.

    +
    +
    +

    Annotations, represented by the non-terminal optAnnotations, are +described in Chapter 20.

    +
    +
    +

    Operations on enum values are described in +Section 8.3.

    +
    +
    +
    +

    7.2.2. Header types

    +
    +

    The declaration of a header type is given by the following +syntax:

    +
    +
    +
    +
    headerTypeDeclaration
    +    : optAnnotations HEADER name optTypeParameters "{" structFieldList "}"
         ;
     
     structFieldList
    -    : /* empty */
    +    : /* empty */
         | structFieldList structField
         ;
     
     structField
    -    : optAnnotations typeRef name ";"
    -    ;
    -

    where each typeRef is restricted to a bit-string type (fixed or + : optAnnotations typeRef name ";" + ; +

    +
    +
    +

    where each typeRef is restricted to a bit-string type (fixed or variable), a fixed-width signed integer type, a boolean type, or a struct that itself contains other struct fields, nested arbitrarily, as long as all of the -“leaf” types are bit<W>, int<W>, a serializable enum, or a bool. If a -bool is used inside a P4 header, all implementations encode the bool as a -one bit long field, with the value 1 representing true and 0 representing false. -Field names have to be distinct. -

    -

    A header declaration introduces a new identifier in the +"leaf" types are bit<W>, int<W>, a serializable enum, or a bool. If a +bool is used inside a P4 header, all implementations encode the bool as a +one bit long field, with the value 1 representing true and 0 representing false. +Field names have to be distinct.

    +
    +
    +

    A header declaration introduces a new identifier in the current scope; the type can be referred to using this identifier. A header is -similar to a struct in C, containing all the specified fields. However, in -addition, a header also contains a hidden Boolean “validity” field. When the -“validity” bit is true we say that the “header is valid”. When a local +similar to a struct in C, containing all the specified fields. However, in +addition, a header also contains a hidden Boolean "validity" field. When the +"validity" bit is true we say that the "header is valid". When a local variable with a header type is -declared, its “validity” bit is automatically set to false. The “validity” -bit can be manipulated by using the header methods isValid(), setValid(), -and setInvalid(), as described in Section 8.17. -

    -

    Note, nesting of headers is not supported. One reason is that it leads to -complications in defining the behavior of arbitrary sequences of setValid, -setInvalid, and emit operations. Consider an example where header h1 -contains header h2 as a member, both currently valid. A program executes -h2.setInvalid() followed by packet.emit(h1). Should all fields of h1 -be emitted, but skipping h2? Similarly, should h1.setInvalid() -invalidate all headers contained within h1, regardless of how deeply -they are nested? -

    -

    Header types may be empty: -

    -
    -
    -
    header Empty_h { }
    -

    Note that an empty header still contains a validity bit. -

    -

    When a struct is inside of a header, the order of the fields for the purposes -of extract and emit calls is the order of the fields as defined in the source code. -An example of a header including a struct is included below. -

    -
    -
    -
    struct ipv6_addr {
    -    bit<32> Addr0;
    -    bit<32> Addr1;
    -    bit<32> Addr2;
    -    bit<32> Addr3;
    -}
    -
    -header ipv6_t {
    -    bit<4>    version;
    -    bit<8>    trafficClass;
    -    bit<20>   flowLabel;
    -    bit<16>   payloadLen;
    -    bit<8>    nextHdr;
    -    bit<8>    hopLimit;
    -    ipv6_addr src;
    -    ipv6_addr dst;
    -}
    -

    Headers that do not contain any varbit field are “fixed size.” -Headers containing varbit fields have “variable size.” The size (in +declared, its "validity" bit is automatically set to false. The "validity" +bit can be manipulated by using the header methods isValid(), setValid(), +and setInvalid(), as described in Section 8.17.

    +
    +
    +

    Note, nesting of headers is not supported. One reason is that it leads to +complications in defining the behavior of arbitrary sequences of setValid, +setInvalid, and emit operations. Consider an example where header h1 +contains header h2 as a member, both currently valid. A program executes +h2.setInvalid() followed by packet.emit(h1). Should all fields of h1 +be emitted, but skipping h2? Similarly, should h1.setInvalid() +invalidate all headers contained within h1, regardless of how deeply +they are nested?

    +
    +
    +

    Header types may be empty:

    +
    +
    +
    +
    header Empty_h { }
    +
    +
    +
    +

    Note that an empty header still contains a validity bit.

    +
    +
    +

    When a struct is inside of a header, the order of the fields for the purposes +of extract and emit calls is the order of the fields as defined in the source code. +An example of a header including a struct is included below.

    +
    +
    +
    +
    struct ipv6_addr {
    +    bit<32> Addr0;
    +    bit<32> Addr1;
    +    bit<32> Addr2;
    +    bit<32> Addr3;
    +}
    +
    +header ipv6_t {
    +    bit<4>    version;
    +    bit<8>    trafficClass;
    +    bit<20>   flowLabel;
    +    bit<16>   payloadLen;
    +    bit<8>    nextHdr;
    +    bit<8>    hopLimit;
    +    ipv6_addr src;
    +    ipv6_addr dst;
    +}
    +
    +
    +
    +

    Headers that do not contain any varbit field are "fixed size." +Headers containing varbit fields have "variable size." The size (in bits) of a fixed-size header is simply the sum of the sizes of all component fields (without counting the validity bit). There is no padding or alignment of the header fields. Targets may impose -additional constraints on header typese.g., restricting headers to -sizes that are an integer number of bytes. -

    -

    For example, the following declaration describes a typical Ethernet -header: -

    -
    -
    -
    header Ethernet_h {
    -   bit<48> dstAddr;
    -   bit<48> srcAddr;
    -   bit<16> etherType;
    -}
    -

    The following variable declaration uses the newly introduced type Ethernet_h: -

    -
    -
    -
    Ethernet_h ethernetHeader;
    -

    P4's parser language provides an extract method that can be used to -“fill in” the fields of a header from a network packet, as described -in Section 13.8. The successful execution of -an extract operation also sets the validity bit of the extracted -header to true. -

    -

    Here is an example of an IPv4 header with variable-sized options: -

    -
    -
    -
    header IPv4_h {
    -   bit<4>       version;
    -   bit<4>       ihl;
    -   bit<8>       diffserv;
    -   bit<16>      totalLen;
    -   bit<16>      identification;
    -   bit<3>       flags;
    -   bit<13>      fragOffset;
    -   bit<8>       ttl;
    -   bit<8>       protocol;
    -   bit<16>      hdrChecksum;
    -   bit<32>      srcAddr;
    -   bit<32>      dstAddr;
    -   varbit<320>  options;
    -}
    -

    As demonstrated by a code example in Section -13.8.2, another way to support headers that contain -variable-length fields is to define two headers one fixed length, -one containing a varbit field and extract each part in separate -parsing steps. -

    -

    Notice that the names isValid, setValid, minSizeInBits, etc. are all -valid header field names. -

    7.2.3. Header stacks

    -

    A header stack represents an array of headers or header unions. A header stack type is -defined as: -

    -
    -
    -
    headerStackType
    -    : typeName "[" expression "]"
    -    | specializedType "[" expression "]"
    -    ;
    -

    where typeName is the name of a header or header union type. For a -header stack hs[n], the term n is the maximum defined size, and +additional constraints on header types---e.g., restricting headers to +sizes that are an integer number of bytes.

    +
    +
    +

    For example, the following declaration describes a typical Ethernet +header:

    +
    +
    +
    +
    header Ethernet_h {
    +   bit<48> dstAddr;
    +   bit<48> srcAddr;
    +   bit<16> etherType;
    +}
    +
    +
    +
    +

    The following variable declaration uses the newly introduced type Ethernet_h:

    +
    +
    +
    +
    Ethernet_h ethernetHeader;
    +
    +
    +
    +

    P4’s parser language provides an extract method that can be used to +"fill in" the fields of a header from a network packet, as described +in Section 13.8. The successful execution of +an extract operation also sets the validity bit of the extracted +header to true.

    +
    +
    +

    Here is an example of an IPv4 header with variable-sized options:

    +
    +
    +
    +
    header IPv4_h {
    +   bit<4>       version;
    +   bit<4>       ihl;
    +   bit<8>       diffserv;
    +   bit<16>      totalLen;
    +   bit<16>      identification;
    +   bit<3>       flags;
    +   bit<13>      fragOffset;
    +   bit<8>       ttl;
    +   bit<8>       protocol;
    +   bit<16>      hdrChecksum;
    +   bit<32>      srcAddr;
    +   bit<32>      dstAddr;
    +   varbit<320>  options;
    +}
    +
    +
    +
    +

    As demonstrated by a code example in Section 13.8.2, +another way to support headers that contain +variable-length fields is to define two headers — one fixed length, +one containing a varbit field — and extract each part in separate +parsing steps.

    +
    +
    +

    Notice that the names isValid, setValid, minSizeInBits, etc. are all +valid header field names.

    +
    +
    +
    +

    7.2.3. Header stacks

    +
    +

    A header stack represents an array of headers or header unions. A header stack type is +defined as:

    +
    +
    +
    +
    headerStackType
    +    : typeName "[" expression "]"
    +    | specializedType "[" expression "]"
    +    ;
    +
    +
    +
    +

    where typeName is the name of a header or header union type. For a +header stack hs[n], the term n is the maximum defined size, and must be a local compile-time known value that is a positive integer. Nested header stacks are not supported. At runtime a stack -contains n values with type typeName, only some of which may be +contains n values with type typeName, only some of which may be valid. Expressions on header stacks are discussed in Section -8.18. -

    -

    For example, the following declarations, -

    -
    -
    -
    header Mpls_h {
    -    bit<20> label;
    -    bit<3>  tc;
    -    bit     bos;
    -    bit<8>  ttl;
    -}
    -Mpls_h[10] mpls;
    -

    introduce a header stack called mpls containing ten entries, each -of type Mpls_h. -

    -

    Operations on header stacks are described in Section 8.18. -

    7.2.4. Header unions

    -

    A header union represents an alternative containing at most one of several different -headers. Header unions can be used to represent “options” in protocols +Section 8.18.

    +
    +
    +

    For example, the following declarations,

    +
    +
    +
    +
    header Mpls_h {
    +    bit<20> label;
    +    bit<3>  tc;
    +    bit     bos;
    +    bit<8>  ttl;
    +}
    +Mpls_h[10] mpls;
    +
    +
    +
    +

    introduce a header stack called mpls containing ten entries, each +of type Mpls_h.

    +
    +
    +

    Operations on header stacks are described in Section 8.18.

    +
    +
    +
    +

    7.2.4. Header unions

    +
    +

    A header union represents an alternative containing at most one of several different +headers. Header unions can be used to represent "options" in protocols like TCP and IP. They also provide hints to P4 compilers that only one -alternative will be present, allowing them to conserve storage resources. -

    -

    A header union is defined as: -

    -
    -
    -
    headerUnionDeclaration
    -    : optAnnotations HEADER_UNION name optTypeParameters "{" structFieldList "}"
    -    ;
    -

    This declaration introduces a new type with the specified name in the +alternative will be present, allowing them to conserve storage resources.

    +
    +
    +

    A header union is defined as:

    +
    +
    +
    +
    headerUnionDeclaration
    +    : optAnnotations HEADER_UNION name optTypeParameters "{" structFieldList "}"
    +    ;
    +
    +
    +
    +

    This declaration introduces a new type with the specified name in the current scope. Each element of the list of fields used to declare a header union must be of header type. An empty list of fields is legal. -Field names have to be distinct. -

    -

    As an example, the type Ip_h below represents the union of an IPv4 -and IPv6 headers: -

    -
    -
    -
    header_union IP_h {
    -  IPv4_h v4;
    -  IPv6_h v6;
    -}
    -

    A header union is not considered a type with fixed length. -

    -

    Operation on header unions are described in Section 8.19. -

    7.2.5. Struct types

    -

    P4 struct types are defined with the following syntax: -

    -
    -
    -
    structTypeDeclaration
    -    : optAnnotations STRUCT name optTypeParameters "{" structFieldList "}"
    -    ;
    -

    This declaration introduces a new type with the specified name in the -current scope. Field names have to be distinct. An empty struct (with no fields) is legal. For example, the structure Parsed_headers -below contains the headers recognized by a simple parser: -

    -
    -
    -
    header Tcp_h { /* fields omitted */ }
    -header Udp_h { /* fields omitted */ }
    -struct Parsed_headers {
    -    Ethernet_h ethernet;
    -    Ip_h       ip;
    -    Tcp_h      tcp;
    -    Udp_h      udp;
    -}

    7.2.6. Tuple types

    -

    A tuple is similar to a struct, in that it holds multiple values. -The type of tuples with n component types T1,,Tn is written as -

    -
    -
    -
    tuple<T1,/* more fields omitted */,Tn>
    -
    -
    -
    tupleType
    -    : TUPLE "<" typeArgumentList ">"
    -    ;
    -

    Operations that manipulate tuple types are described in Section -8.12. -

    -

    The type tuple<> is a tuple type with no components. -

    7.2.7. List types

    -

    A list holds zero or more values, where every element must have the -same type. The type of a list where all elements have type T is -written as -

    -
    -
    -
    list<T>
    -
    -
    -
    p4listType
    -    : LIST "<" typeArg ">"
    -    ;
    -

    Operations that manipulate list types are described in Section -8.14. -

    7.2.8. Type nesting rules

    -

    The table below lists all types that may appear as members of headers, -header unions, structs, tuples, and lists. Note that int by itself -(i.e. not as part of an int<N> type expression) means an -arbitrary-precision integer, without a width specified. -

    - - - - - - - - - - - - - - - - - - - - - -
    Container kind
    Element type header header_union struct or tuple list header stack
    bit<W> allowed error allowed allowed error
    int<W> allowed error allowed allowed error
    varbit<W> allowed error allowed allowed error
    int error error error allowed error
    void error error error error error
    string error error error allowed error
    error error error allowed allowed error
    match_kind error error error allowed error
    bool allowed error allowed allowed error
    enumeration types allowed1 error allowed allowed error
    header types error allowed allowed allowed allowed
    header stacks error error allowed allowed error
    header unions error error allowed allowed allowed
    struct types allowed2 error allowed allowed error
    tuple types error error allowed allowed error
    list types error error error allowed error
    -

    Rationale: int does not have precise storage requirements, -unlike bit<> or int<> types. match_kind +Field names have to be distinct.

    +
    +
    +

    As an example, the type Ip_h below represents the union of an IPv4 +and IPv6 headers:

    +
    +
    +
    +
    header_union IP_h {
    +  IPv4_h v4;
    +  IPv6_h v6;
    +}
    +
    +
    +
    +

    A header union is not considered a type with fixed length.

    +
    +
    +

    Operation on header unions are described in Section 8.19.

    +
    +
    +
    +

    7.2.5. Struct types

    +
    +

    P4 struct types are defined with the following syntax:

    +
    +
    +
    +
    structTypeDeclaration
    +    : optAnnotations STRUCT name optTypeParameters "{" structFieldList "}"
    +    ;
    +
    +
    +
    +

    This declaration introduces a new type with the specified name in the +current scope. Field names have to be distinct. An empty struct (with no fields) is legal. For example, the structure Parsed_headers +below contains the headers recognized by a simple parser:

    +
    +
    +
    +
    header Tcp_h { /* fields omitted */ }
    +header Udp_h { /* fields omitted */ }
    +struct Parsed_headers {
    +    Ethernet_h ethernet;
    +    Ip_h       ip;
    +    Tcp_h      tcp;
    +    Udp_h      udp;
    +}
    +
    +
    +
    +
    +

    7.2.6. Tuple types

    +
    +

    A tuple is similar to a struct, in that it holds multiple values. +The type of tuples with n component types T1,…​,Tn is written as

    +
    +
    +
    +
    tuple<T1,/* more fields omitted */,Tn>
    +
    +
    +
    +
    +
    tupleType
    +    : TUPLE "<" typeArgumentList ">"
    +    ;
    +
    +
    +
    +

    Operations that manipulate tuple types are described in +Section 8.12.

    +
    +
    +

    The type tuple<> is a tuple type with no components.

    +
    +
    +
    +

    7.2.7. List types

    +
    +

    A list holds zero or more values, where every element must have the +same type. The type of a list where all elements have type T is +written as

    +
    +
    +
    +
    list<T>
    +
    +
    +
    +
    +
    p4listType
    +    : LIST "<" typeArg ">"
    +    ;
    +
    +
    +
    +

    Operations that manipulate list types are described in +Section 8.14.

    +
    +
    +
    +

    7.2.8. Type nesting rules

    +
    +

    The table below lists all types that may appear as members of headers, +header unions, structs, tuples, and lists. Note that int by itself +(i.e. not as part of an int<N> type expression) means an +arbitrary-precision integer, without a width specified.

    +
    + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Element typeContainer kind

    header

    header_union

    struct or tuple

    list

    header stack

    bit<W>

    allowed

    error

    allowed

    allowed

    error

    int<W>

    allowed

    error

    allowed

    allowed

    error

    varbit<W>

    allowed

    error

    allowed

    allowed

    error

    int

    error

    error

    error

    allowed

    error

    void

    error

    error

    error

    error

    error

    string

    error

    error

    error

    allowed

    error

    error

    error

    error

    allowed

    allowed

    error

    match_kind

    error

    error

    error

    allowed

    error

    bool

    allowed

    error

    allowed

    allowed

    error

    enumeration types

    allowed [1]

    error

    allowed

    allowed

    error

    header types

    error

    allowed

    allowed

    allowed

    allowed

    header stacks

    error

    error

    allowed

    allowed

    error

    header_unions

    error

    error

    allowed

    allowed

    allowed

    struct types

    allowed [2]

    error

    allowed

    allowed

    error

    tuple types

    error

    error

    allowed

    allowed

    error

    list types

    error

    error

    error

    allowed

    error

    +
    +

    [1] An enum type used as a field in a header must specify a +underlying type and representation for enum elements.

    +
    +
    +

    [2] A struct or nested struct type that has the same properties, +used as a field in a header must contain only bit<W>, int<W>, a +serializable enum, or a bool.

    +
    +
    +

    Rationale: int does not have precise storage requirements, +unlike bit<> or int<> types. match_kind values are not useful to store in a variable, as they are only used to specify how to match fields in table search keys, -which are all declared at compile time. void is not useful as +which are all declared at compile time. void is not useful as part of another data structure. Headers must have precisely defined formats as sequences of bits in order for them to be parsed or -deparsed. -

    -

    Note the two-argument extract method (see Section -13.8.2) on packets only supports a single varbit -field in a header. -

    -

    The table below lists all types that may appear as base types in a -typedef or type declaration. -

    - - - - - - - - - - - - - - - - - - - - - -
    Base type B typedef B <name> type B <name>
    bit<W> allowed allowed
    int<W> allowed allowed
    varbit<W> allowed error
    int allowed error
    void error error
    string allowed error
    error allowed error
    match_kind error error
    bool allowed allowed
    enumeration types allowed error
    header types allowed error
    header stacks allowed error
    header unions allowed error
    struct types allowed error
    tuple types allowed error
    list types allowed error
    a typedef name allowed allowed3
    a type name allowed allowed
    -

    Rationale: So far, no clear motivation for allowing typedef for void -and match_kind was presented. Therefore, to be on the safe side this -is disallowed. -

    7.2.9. Synthesized data types

    -

    For the purposes of type-checking the P4 compiler can synthesize some +deparsed.

    +
    +
    +

    Note the two-argument extract method (see +Section 13.8.2) on packets only supports a single varbit +field in a header.

    +
    +
    +

    The table below lists all types that may appear as base types in a +typedef or type declaration.

    +
    + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Base type Btypedef B <name>type B <name>

    bit<W>

    allowed

    allowed

    int<W>

    allowed

    allowed

    varbit<W>

    allowed

    error

    int

    allowed

    error

    void

    error

    error

    string

    error

    error

    error

    allowed

    error

    match_kind

    error

    error

    bool

    allowed

    allowed

    enumeration types

    allowed

    error

    header types

    allowed

    error

    header stacks

    allowed

    error

    header unions

    allowed

    error

    struct types

    allowed

    error

    tuple types

    allowed

    error

    list types

    allowed

    error

    a typedef name

    allowed

    allowed [3]

    a type name

    allowed

    allowed

    +
    +

    [3] type B <name> is allowed for a type name B defined via +typedef X B if type X <name> is allowed.

    +
    +
    +

    Rationale: So far, no clear motivation for allowing typedef for void +and match_kind was presented. Therefore, to be on the safe side this +is disallowed.

    +
    +
    +
    +

    7.2.9. Synthesized data types

    +
    +

    For the purposes of type-checking the P4 compiler can synthesize some type representations which cannot be directly expressed by users. These are described in this section: set types and function -types. -

    7.2.9.1. Set types
    -

    The type set<T> describes sets of values of some type T. Set +types.

    +
    +
    +
    7.2.9.1. Set types
    +
    +

    The type set<T> describes sets of values of some type T. Set types can only appear in restricted contexts in P4 programs. For -example, the range expression 8w5 .. 8w8 describes a set -containing the 8-bit numbers 5, 6, 7, and 8, so its type is set<bit<8>>;. -This expression can be used as a label in a select expression -(see Section 13.6), matching any value in this range. Set +example, the range expression 8w5 .. 8w8 describes a set +containing the 8-bit numbers 5, 6, 7, and 8, so its type is set<bit<8>>;. +This expression can be used as a label in a select expression +(see Section 13.6), matching any value in this range. Set types cannot be named or declared by P4 programmers, they are only synthesized by the compiler internally and used for -type-checking. Expressions with set types are described in Section -8.15. -

    7.2.9.2. Function types
    -

    -Function types are created by the P4 compiler internally to represent the types +type-checking. Expressions with set types are described in +Section 8.15.

    +
    +
    +
    +
    7.2.9.2. Function types
    +
    +

    Function types are created by the P4 compiler internally to represent the types of functions (explicit functions or extern functions) and methods during type-checking. We also call the type of a function its -signature. Libraries can contain functions and extern function declarations. -

    -

    For example, consider the following declarations: -

    -
    -
    -
    extern void random(in bit<5> logRange, out bit<32> value);
    -
    -bit<32> add(in bit<32> left, in bit<32> right) {
    -   return left + right;
    -}
    -

    These declarations describe two objects: -

    -
      -
    • random, which has a function type, representing the following information: - -
        -
      • the result type is void -
      • -
      • the function has two inputs -
      • -
      • the first formal parameter has direction in, type bit<5>, and name logRange -
      • -
      • the second formal parameter has direction out, type bit<32>, and name value -
    • -
    • add, also has a function type, representing the following information: - -
        -
      • the result type is bit<32> -
      • -
      • the function has two inputs -
      • -
      • both inputs have direction in and type bit<32> -
    -

    7.2.10. Extern types

    -

    -P4 supports extern object declarations and extern function declarations using the following syntax. -

    -
    -
    -
    externDeclaration
    -    : optAnnotations EXTERN nonTypeName optTypeParameters "{" methodPrototypes "}"
    -    | optAnnotations EXTERN functionPrototype ";"
    -    ;
    7.2.10.1. Extern functions
    -

    -An extern function declaration describes the name and type signature of the function, -but not its implementation. -

    -
    -
    -
    functionPrototype
    -    : typeOrVoid name optTypeParameters "(" parameterList ")"
    -    ;
    -

    For an example of an extern function declaration, see Section -7.2.9.2. -

    7.2.10.2. Extern objects
    -

    -An extern object declaration declares an object and all methods that +signature. Libraries can contain functions and extern function declarations.

    +
    +
    +

    For example, consider the following declarations:

    +
    +
    +
    +
    extern void random(in bit<5> logRange, out bit<32> value);
    +
    +bit<32> add(in bit<32> left, in bit<32> right) {
    +   return left + right;
    +}
    +
    +
    +
    +

    These declarations describe two objects:

    +
    +
    +
      +
    • +

      random, which has a function type, representing the following information:

      +
      +
        +
      • +

        the result type is void

        +
      • +
      • +

        the function has two inputs

        +
      • +
      • +

        the first formal parameter has direction in, type bit<5>, and name logRange

        +
      • +
      • +

        the second formal parameter has direction out, type bit<32>, and name value

        +
      • +
      +
      +
    • +
    • +

      add, also has a function type, representing the following information:

      +
      +
        +
      • +

        the result type is bit<32>

        +
      • +
      • +

        the function has two inputs

        +
      • +
      • +

        both inputs have direction in and type bit<32>

        +
      • +
      +
      +
    • +
    +
    +
    +
    +
    +

    7.2.10. Extern types

    +
    +

    P4 supports extern object declarations and extern function declarations using the following syntax.

    +
    +
    +
    +
    externDeclaration
    +    : optAnnotations EXTERN nonTypeName optTypeParameters "{" methodPrototypes "}"
    +    | optAnnotations EXTERN functionPrototype ";"
    +    ;
    +
    +
    +
    +
    7.2.10.1. Extern functions
    +
    +

    An extern function declaration describes the name and type signature of the function, +but not its implementation.

    +
    +
    +
    +
    functionPrototype
    +    : typeOrVoid name optTypeParameters "(" parameterList ")"
    +    ;
    +
    +
    +
    +

    For an example of an extern function declaration, see +Section 7.2.9.2.

    +
    +
    +
    +
    7.2.10.2. Extern objects
    +
    +

    An extern object declaration declares an object and all methods that can be invoked to perform computations and to alter the state of the object. Extern object declarations can also optionally declare -constructor methods; these must have the same name as the enclosing extern +constructor methods; these must have the same name as the enclosing extern type, no type parameters, and no return type. Extern declarations may only appear as allowed by the architecture model and may be specific -to a target. -

    -
    -
    -
    methodPrototypes
    -    : /* empty */
    +to a target.

    +
    +
    +
    +
    methodPrototypes
    +    : /* empty */
         | methodPrototypes methodPrototype
         ;
     
     methodPrototype
    -    : optAnnotations functionPrototype ';'
    -    | optAnnotations TYPE_IDENTIFIER '(' parameterList ')' ';' //constructor
    -    | optAnnotations ABSTRACT functionPrototype ";"
    +    : optAnnotations functionPrototype ';'
    +    | optAnnotations TYPE_IDENTIFIER '(' parameterList ')' ';' //constructor
    +    | optAnnotations ABSTRACT functionPrototype ";"
         ;
     
     typeOrVoid
         : typeRef
         | VOID
    -    | IDENTIFIER     // may be a type variable
    +    | IDENTIFIER     // may be a type variable
         ;
     
     optTypeParameters
    -    : /* empty */
    +    : /* empty */
         | typeParameters
         ;
     
     typeParameters
    -    : "<" typeParameterList ">"
    +    : "<" typeParameterList ">"
         ;
     
     typeParameterList
         : name
    -    | typeParameterList "," name
    -    ;
    -

    For example, the P4 core library introduces two extern objects packet_in -and packet_out used for manipulating packets -(see Sections 13.8 and 16). Here + | typeParameterList "," name + ; +

    +
    +
    +

    For example, the P4 core library introduces two extern objects packet_in +and packet_out used for manipulating packets +(see Section 13.8 and Chapter 16). Here is an example showing how the methods of these objects can be invoked -on a packet: -

    -
    -
    -
    extern packet_out {
    -    void emit<T>(in T hdr);
    -}
    -control d(packet_out b, in Hdr h) {
    -    apply {
    -        b.emit(h.ipv4);       // write ipv4 header into output packet
    -    }                         // by calling emit method
    -}
    -

    Functions and methods are the only P4 constructs that support +on a packet:

    +
    +
    +
    +
    extern packet_out {
    +    void emit<T>(in T hdr);
    +}
    +control d(packet_out b, in Hdr h) {
    +    apply {
    +        b.emit(h.ipv4);       // write ipv4 header into output packet
    +    }                         // by calling emit method
    +}
    +
    +
    +
    +

    Functions and methods are the only P4 constructs that support overloading: there can exist multiple methods with the same name in the same scope. When overloading is used, the compiler must be able to disambiguate at compile-time which method or function is being called, either by the number of arguments or by the names of the arguments, when calls are specifying argument names. Argument type information -is not used in disambiguating calls. -

    -

    Notice that overloading of parsers, controls, or packages is not allowed: -

    -
    -
    -
    parser p(packet_in p, out bit<32> value) {
    -  ...
    -}
    -
    -// The following will cause an error about a duplicate declaration
    -//parser p(packet_in p, out Headers headers) {
    -//   ...
    -//}
    Abstract methods
    -

    Typical extern object methods are built-in, and are implemented by the -target architecture. P4 programmers can only call such methods. -

    -

    However, some types of extern objects may provide methods that can be +is not used in disambiguating calls.

    +
    +
    +

    Notice that overloading of parsers, controls, or packages is not allowed:

    +
    +
    +
    +
    parser p(packet_in p, out bit<32> value) {
    +  ...
    +}
    +
    +// The following will cause an error about a duplicate declaration
    +//parser p(packet_in p, out Headers headers) {
    +//   ...
    +//}
    +
    +
    +
    +
    7.2.10.2.1. Abstract methods
    +
    +

    Typical extern object methods are built-in, and are implemented by the +target architecture. P4 programmers can only call such methods.

    +
    +
    +

    However, some types of extern objects may provide methods that can be implemented by the P4 programmers. Such methods are described with -the abstract keyword prior to the method definition. Here is an -example: -

    -
    -
    -
    extern Balancer {
    -    Balancer();
    -    // get the number of active flows
    -    bit<32> getFlowCount();
    -    // return port index used for load-balancing
    -    // @param address: IPv4 source address of flow
    -    abstract bit<4> on_new_flow(in bit<32> address);
    -}
    -

    When such an object is instantiated the user has to supply an -implementation of all the abstract methods (see 11.3.1). -

    7.2.11. Type specialization

    -

    A generic type may be specialized by specifying arguments for its type +the abstract keyword prior to the method definition. Here is an +example:

    +
    +
    +
    +
    extern Balancer {
    +    Balancer();
    +    // get the number of active flows
    +    bit<32> getFlowCount();
    +    // return port index used for load-balancing
    +    // @param address: IPv4 source address of flow
    +    abstract bit<4> on_new_flow(in bit<32> address);
    +}
    +
    +
    +
    +

    When such an object is instantiated the user has to supply an +implementation of all the abstract methods (see Section 11.3.1).

    +
    +
    +
    +
    +
    +

    7.2.11. Type specialization

    +
    +

    A generic type may be specialized by specifying arguments for its type variables. In cases where the compiler can infer type arguments type specialization is not necessary. When a type is specialized all its -type variables must be bound. -

    -
    -
    -
    specializedType
    -    : typeName "<" typeArgumentList ">"
    -    ;
    -

    For example, the following extern declaration describes a generic +type variables must be bound.

    +
    +
    +
    +
    specializedType
    +    : typeName "<" typeArgumentList ">"
    +    ;
    +
    +
    +
    +

    For example, the following extern declaration describes a generic block of registers, where the type of the elements stored in each -register is an arbitrary T. -

    -
    -
    -
    extern Register<T> {
    -    Register(bit<32> size);
    -    T read(bit<32> index);
    -    void write(bit<32> index, T value);
    -}
    -

    The type T has to be specified when instantiating a set of -registers, by specializing the Register type: -

    -
    -
    -
    Register<bit<32>>(128) registerBank;
    -

    The instantiation of registerBank is made using the Register -type specialized with the bit<32> bound to the T type argument. -

    -

    struct, header, header_union and header stack types can be +register is an arbitrary T.

    +
    +
    +
    +
    extern Register<T> {
    +    Register(bit<32> size);
    +    T read(bit<32> index);
    +    void write(bit<32> index, T value);
    +}
    +
    +
    +
    +

    The type T has to be specified when instantiating a set of +registers, by specializing the Register type:

    +
    +
    +
    +
    Register<bit<32>>(128) registerBank;
    +
    +
    +
    +

    The instantiation of registerBank is made using the Register +type specialized with the bit<32> bound to the T type argument.

    +
    +
    +

    struct, header, header_union and header stack types can be generic as well. In order to use such a generic type it must be -specialized with appropriate type arguments. For example -

    -
    -
    -
    // generic structure type
    -struct S<T> {
    -    T field;
    -    bool valid;
    -}
    -
    -struct G<T> {
    -    S<T> s;
    -}
    -
    -// specialize S by replacing 'T' with 'bit<32>'
    -const S<bit<32>> s = { field = 32w0, valid = false };
    -// Specialize G by replacing 'T' with 'bit<32>'
    -const G<bit<32>> g = { s = { field = 0, valid = false } };
    -
    -// generic header type
    -header H<T> {
    -    T field;
    -}
    -
    -// Specialize H by replacing 'T' with 'bit<8>'
    -const H<bit<8>> h = { field = 1 };
    -// Header stack produced from a specialization of a generic header type
    -H<bit<8>>[10] stack;
    -
    -// Generic header union
    -header_union HU<T> {
    -    H<bit<32>> h32;
    -    H<bit<8>>  h8;
    -    H<T>       ht;
    -}
    -
    -// Header union with a type obtained by specializing a generic header union type
    -HU<bit> hu;

    7.2.12. Parser and control blocks types

    -

    Parsers and control blocks types are similar to function types: they +specialized with appropriate type arguments. For example

    +
    +
    +
    +
    // generic structure type
    +struct S<T> {
    +    T field;
    +    bool valid;
    +}
    +
    +struct G<T> {
    +    S<T> s;
    +}
    +
    +// specialize S by replacing 'T' with 'bit<32>'
    +const S<bit<32>> s = { field = 32w0, valid = false };
    +// Specialize G by replacing 'T' with 'bit<32>'
    +const G<bit<32>> g = { s = { field = 0, valid = false } };
    +
    +// generic header type
    +header H<T> {
    +    T field;
    +}
    +
    +// Specialize H by replacing 'T' with 'bit<8>'
    +const H<bit<8>> h = { field = 1 };
    +// Header stack produced from a specialization of a generic header type
    +H<bit<8>>[10] stack;
    +
    +// Generic header union
    +header_union HU<T> {
    +    H<bit<32>> h32;
    +    H<bit<8>>  h8;
    +    H<T>       ht;
    +}
    +
    +// Header union with a type obtained by specializing a generic header union type
    +HU<bit> hu;
    +
    +
    +
    +
    +

    7.2.12. Parser and control blocks types

    +
    +

    Parsers and control blocks types are similar to function types: they describe the signature of parsers and control blocks. Such functions have no return values. Declarations of parsers and control block types in architectures may be generic -(i.e., have type parameters). -

    -

    The types parser, control, and package cannot be used as +(i.e., have type parameters).

    +
    +
    +

    The types parser, control, and package cannot be used as types of arguments for methods, parsers, controls, tables, or actions. -They can be used as types for the arguments passed to -constructors (see Section 15). -

    7.2.12.1. Parser type declarations
    -

    A parser type declaration describes the signature of a parser. A -parser should have at least one argument of type packet_in, -representing the received packet that is processed. -

    -
    -
    -
    parserTypeDeclaration
    +They can be used as types for the arguments passed to
    +constructors (see Chapter 15).

    +
    +
    +
    7.2.12.1. Parser type declarations
    +
    +

    A parser type declaration describes the signature of a parser. A +parser should have at least one argument of type packet_in, +representing the received packet that is processed.

    +
    +
    +
    +
    parserTypeDeclaration
         : optAnnotations PARSER name optTypeParameters
    -      "(" parameterList ")"
    -    ;
    -

    For example, the following is a type declaration of a parser type -named P that is parameterized on a type variable H. That parser -receives as input a packet_in value b and produces two values: -

    -
      -
    • A value with a user-defined type H -
    • -
    • A value with a predefined type Counters -
    - -
    -
    -
    struct Counters { /* Fields omitted */ }
    -parser P<H>(packet_in b,
    -             out H packetHeaders,
    -             out Counters counters);
    7.2.12.2. Control type declarations
    -

    A control type declaration describes the signature of a control block. -

    -
    -
    -
    controlTypeDeclaration
    +      "(" parameterList ")"
    +    ;
    +
    +
    +
    +

    For example, the following is a type declaration of a parser type +named P that is parameterized on a type variable H. That parser +receives as input a packet_in value b and produces two values:

    +
    +
    +
      +
    • +

      A value with a user-defined type H

      +
    • +
    • +

      A value with a predefined type Counters

      +
    • +
    +
    +
    +
    +
    struct Counters { /* Fields omitted */ }
    +parser P<H>(packet_in b,
    +             out H packetHeaders,
    +             out Counters counters);
    +
    +
    +
    +
    +
    7.2.12.2. Control type declarations
    +
    +

    A control type declaration describes the signature of a control block.

    +
    +
    +
    +
    controlTypeDeclaration
         : optAnnotations CONTROL name optTypeParameters
    -      "(" parameterList ")"
    -    ;
    -

    Control type declarations are similar to parser type -declarations. -

    7.2.13. Package types

    -

    A package type describes the signature of a package. -

    -
    -
    -
    packageTypeDeclaration
    +      "(" parameterList ")"
    +    ;
    +
    +
    +
    +

    Control type declarations are similar to parser type +declarations.

    +
    +
    +
    +
    +

    7.2.13. Package types

    +
    +

    A package type describes the signature of a package.

    +
    +
    +
    +
    packageTypeDeclaration
         : optAnnotations PACKAGE name optTypeParameters
    -      "(" parameterList ")"
    -    ;
    -

    All parameters of a package are evaluated at compilation time, and in -consequence they must all be directionless (they cannot be in, out, -or inout). Otherwise package types are very similar to + "(" parameterList ")" + ; +

    +
    +
    +

    All parameters of a package are evaluated at compilation time, and in +consequence they must all be directionless (they cannot be in, out, +or inout). Otherwise package types are very similar to parser type declarations. Packages can only be instantiated; there are -no runtime behaviors associated with them. -

    7.2.14. Don't care types

    -

    A don't care (underscore, "_") can be used in some circumstances as +no runtime behaviors associated with them.

    +
    +
    +
    +

    7.2.14. Don’t care types

    +
    +

    A don’t care (underscore, “_”) can be used in some circumstances as a type. It should be only used in a position where one could write a bound type variable. The -underscore can be used to reduce code complexitywhen it is not +underscore can be used to reduce code complexity---when it is not important what the type variable binds to (during type unification the -don't care type can unify with any other type). An example is given -Section 17.1. -

    7.3. Default values

    -

    Some P4 types define a “default value,” which can be used to +don’t care type can unify with any other type). An example is given +Section 17.1.

    +
    +
    +
    +
    +

    7.3. Default values

    +
    +

    Some P4 types define a "default value," which can be used to automatically initialize values of that type. The default values are -as follows: -

    -
      -
    • For int, bit<N> and int<N> types the default value is 0. -
    • -
    • For bool the default value is false. -
    • -
    • For error the default value is error.NoError (defined in core.p4) -
    • -
    • For string the default value is the empty string "" -
    • -
    • For varbit<N> the default value is a string of zero bits (there is currently - no P4 literal to represent such a value). -
    • -
    • For enum values with an underlying type the default value is 0, -even if 0 is actually not one of the named values in the enum. -
    • -
    • For enum values without an underlying type the default value is the -first value that appears in the enum type declaration. -
    • -
    • For header types the default value is invalid. -
    • -
    • For header stacks the default value is that all elements are invalid and the -nextIndex is 0. -
    • -
    • For header_union values the default value is that all union elements are invalid. -
    • -
    • For struct types the default value is a struct where each field has -the default value of the suitable field type if all such default values are -defined. -
    • -
    • For a tuple type the default value is a tuple where each field -has the default value of the suitable type if all such default values -are defined. -
    - -

    Note that some types do not have default values, e.g., match_kind, +as follows:

    +
    +
    +
      +
    • +

      For int, bit<N> and int<N> types the default value is 0.

      +
    • +
    • +

      For bool the default value is false.

      +
    • +
    • +

      For error the default value is error.NoError (defined in core.p4)

      +
    • +
    • +

      For string the default value is the empty string ""

      +
    • +
    • +

      For varbit<N> the default value is a string of zero bits (there is currently +no P4 literal to represent such a value).

      +
    • +
    • +

      For enum values with an underlying type the default value is 0, +even if 0 is actually not one of the named values in the enum.

      +
    • +
    • +

      For enum values without an underlying type the default value is the +first value that appears in the enum type declaration.

      +
    • +
    • +

      For header types the default value is invalid.

      +
    • +
    • +

      For header stacks the default value is that all elements are invalid and the +nextIndex is 0.

      +
    • +
    • +

      For header_union values the default value is that all union elements are invalid.

      +
    • +
    • +

      For struct types the default value is a struct where each field has +the default value of the suitable field type — if all such default values are +defined.

      +
    • +
    • +

      For a tuple type the default value is a tuple where each field +has the default value of the suitable type — if all such default values +are defined.

      +
    • +
    +
    +
    +

    Note that some types do not have default values, e.g., match_kind, set types, function types, extern types, parser types, control types, -package types. -

    7.4. Numeric types

    -

    Many P4 operations are restrained to expressions that evaluate to numeric values. -Such expressions must have one of the following numeric types: -

    -
      -
    • int - an arbitrary-precision integer (section -7.1.6.5) -
    • -
    • bit<W> - a W-bit unsigned integer where W >= 0 (section -7.1.6.2) -
    • -
    • int<W> - a W-bit signed integer where W >= 1 (section -7.1.6.3) -
    • -
    • a serializable enum with an underlying type that is bit<W> or -int<W> (section 7.2.1). -
    -

    7.5. typedef

    -

    A typedef declaration can be used to give an alternative name to -a type. -

    -
    -
    -
    typedefDeclaration
    -    : optAnnotations TYPEDEF typeRef name ';'
    -    | optAnnotations TYPEDEF derivedTypeDeclaration name ';'
    -    ;
    -
    -
    -
    typedef bit<32> u32;
    -typedef struct Point { int<32> x; int<32> y; } Pt;
    -typedef Empty_h[32] HeaderStack;
    -

    The two types are treated as synonyms, and all operations that can be +package types.

    +
    +
    +
    +

    7.4. Numeric types

    +
    +

    Many P4 operations are restrained to expressions that evaluate to numeric values. +Such expressions must have one of the following numeric types:

    +
    +
    +
      +
    • +

      int - an arbitrary-precision integer +(Section 7.1.6.5)

      +
    • +
    • +

      bit<W> - a W-bit unsigned integer where W >= 0 +(Section 7.1.6.2)

      +
    • +
    • +

      int<W> - a W-bit signed integer where W >= 1 +(Section 7.1.6.3)

      +
    • +
    • +

      a serializable enum with an underlying type that is bit<W> or +int<W> (Section 7.2.1).

      +
    • +
    +
    +
    +
    +

    7.5. typedef

    +
    +

    A typedef declaration can be used to give an alternative name to +a type.

    +
    +
    +
    +
    typedefDeclaration
    +    : optAnnotations TYPEDEF typeRef name ';'
    +    | optAnnotations TYPEDEF derivedTypeDeclaration name ';'
    +    ;
    +
    +
    +
    +
    +
    typedef bit<32> u32;
    +typedef struct Point { int<32> x; int<32> y; } Pt;
    +typedef Empty_h[32] HeaderStack;
    +
    +
    +
    +

    The two types are treated as synonyms, and all operations that can be executed using the original type can be also executed using the newly -created type. -

    -

    If typedef is used with a generic type the type must be specialized -with the suitable number of type arguments: -

    -
    -
    -
    struct S<T> {
    -   T field;
    -}
    -
    -// typedef S X;  -- illegal: S does not have type arguments
    -typedef S<bit<32>> X;  // -- legal

    7.6. Introducing new types

    -

    Similarly to typedef, the keyword type can be used to introduce a -new type. -

    -
    -
    -
        | optAnnotations TYPE typeRef name
    -
    -
    -
    type bit<32> U32;
    -U32 x = (U32)0;
    -

    While similar to typedef, the type keyword introduces a new type +created type.

    +
    +
    +

    If typedef is used with a generic type the type must be specialized +with the suitable number of type arguments:

    +
    +
    +
    +
    struct S<T> {
    +   T field;
    +}
    +
    +// typedef S X;  -- illegal: S does not have type arguments
    +typedef S<bit<32>> X;  // -- legal
    +
    +
    +
    +
    +

    7.6. Introducing new types

    +
    +

    Similarly to typedef, the keyword type can be used to introduce a +new type.

    +
    +
    +
    +
        | optAnnotations TYPE typeRef name
    +
    +
    +
    +
    +
    type bit<32> U32;
    +U32 x = (U32)0;
    +
    +
    +
    +

    While similar to typedef, the type keyword introduces a new type which is not a synonym with the original type: values of the original type and the newly introduced type cannot be mixed in -expressions. -

    -

    Currently the types that can be created by the type keyword are -restricted to one of: bit<>, int<>, bool, or types defined using -type from such types. -

    -

    One important use of such types is in describing P4 values that need +expressions.

    +
    +
    +

    Currently the types that can be created by the type keyword are +restricted to one of: bit<>, int<>, bool, or types defined using +type from such types.

    +
    +
    +

    One important use of such types is in describing P4 values that need to be exchanged with the control plane through communication channels (e.g., through the control-plane API or through network packets sent to the control plane). For example, a P4 architecture may define a -type for the switch ports: -

    -
    -
    -
    type bit<9> PortId_t;
    -

    This declaration will prevent PortId_t values from being used in +type for the switch ports:

    +
    +
    +
    +
    type bit<9> PortId_t;
    +
    +
    +
    +

    This declaration will prevent PortId_t values from being used in arithmetic expressions without casts. Moreover, this declaration may enable special manipulation or such values by software that lies outside of the datapath (e.g., a target-specific toolchain could include software -that automatically converts values of type PortId_t to a different -representation when exchanged with the control-plane software). -

    8. Expressions

    -

    This section describes all expressions that can be used in P4, -grouped by the type of value they produce. -

    -

    The grammar production rule for general expressions is as follows: -

    -
    -
    -
    expression
    +that automatically converts values of type PortId_t to a different
    +representation when exchanged with the control-plane software).

    +
    +
    +
    +
    +
    +

    8. Expressions

    +
    +
    +

    This section describes all expressions that can be used in P4, +grouped by the type of value they produce.

    +
    +
    +

    The grammar production rule for general expressions is as follows:

    +
    +
    +
    +
    expression
         : INTEGER
    -    | DOTS                           // DOTS is ...
    +    | DOTS                           // DOTS is ...
         | STRING_LITERAL
         | TRUE
         | FALSE
         | prefixedNonTypeName
    -    | expression '[' expression ']'
    -    | expression '[' expression ':' expression ']'
    -    | '{' expressionList optTrailingComma '}'
    -    | "{#}"
    -    | '{' kvList optTrailingComma '}'
    -    | "{" kvList "," DOTS optTrailingComma "}"
    -    | '(' expression ')'
    -    | '!' expression
    -    | '~' expression
    -    | '-' expression
    -    | '+' expression
    -    | typeName '.' member
    -    | ERROR '.' member
    -    | expression '.' member
    -    | expression '*' expression
    -    | expression '/' expression
    -    | expression '%' expression
    -    | expression '+' expression
    -    | expression '-' expression
    -    | expression '|+|' expression
    -    | expression '|-|' expression
    -    | expression SHL expression      // SHL is <<
    -    | expression '>''>' expression   // check that >> are contiguous
    -    | expression LE expression       // LE is <=
    -    | expression GE expression       // GE is >=
    -    | expression '<' expression
    -    | expression '>' expression
    -    | expression NE expression       // NE is !=
    -    | expression EQ expression       // EQ is ==
    -    | expression '&' expression
    -    | expression '^' expression
    -    | expression '|' expression
    -    | expression PP expression       // PP is ++
    -    | expression AND expression      // AND is &&
    -    | expression OR expression       // OR is ||
    -    | expression '?' expression ':' expression
    -    | expression '<' realTypeArgumentList '>' '(' argumentList ')'
    -    | expression '(' argumentList ')'
    -    | namedType '(' argumentList ')'
    -    | '(' typeRef ')' expression
    +    | expression '[' expression ']'
    +    | expression '[' expression ':' expression ']'
    +    | '{' expressionList optTrailingComma '}'
    +    | "{#}"
    +    | '{' kvList optTrailingComma '}'
    +    | "{" kvList "," DOTS optTrailingComma "}"
    +    | '(' expression ')'
    +    | '!' expression
    +    | '~' expression
    +    | '-' expression
    +    | '+' expression
    +    | typeName '.' member
    +    | ERROR '.' member
    +    | expression '.' member
    +    | expression '*' expression
    +    | expression '/' expression
    +    | expression '%' expression
    +    | expression '+' expression
    +    | expression '-' expression
    +    | expression '|+|' expression
    +    | expression '|-|' expression
    +    | expression SHL expression      // SHL is <<
    +    | expression '>''>' expression   // check that >> are contiguous
    +    | expression LE expression       // LE is <=
    +    | expression GE expression       // GE is >=
    +    | expression '<' expression
    +    | expression '>' expression
    +    | expression NE expression       // NE is !=
    +    | expression EQ expression       // EQ is ==
    +    | expression '&' expression
    +    | expression '^' expression
    +    | expression '|' expression
    +    | expression PP expression       // PP is ++
    +    | expression AND expression      // AND is &&
    +    | expression OR expression       // OR is ||
    +    | expression '?' expression ':' expression
    +    | expression '<' realTypeArgumentList '>' '(' argumentList ')'
    +    | expression '(' argumentList ')'
    +    | namedType '(' argumentList ')'
    +    | '(' typeRef ')' expression
         ;
     
     expressionList
    -    : /* empty */
    +    : /* empty */
         | expression
    -    | expressionList "," expression
    +    | expressionList "," expression
         ;
     
     member
    @@ -3673,13 +5400,13 @@ 

    /* empty */ + : /* empty */ | nonEmptyArgList ; nonEmptyArgList : argument - | nonEmptyArgList "," argument + | nonEmptyArgList "," argument ; argument @@ -3690,318 +5417,423 @@

    "_" + | "_" ; typeArgumentList - : /* empty */ + : /* empty */ | typeArg - | typeArgumentList "," typeArg - ;

    -

    See Appendix G for the complete P4 grammar. -

    -

    This grammar does not indicate the precedence of the various + | typeArgumentList "," typeArg + ; +

    +
    +
    +

    See Appendix G for the complete P4 grammar.

    +
    +
    +

    This grammar does not indicate the precedence of the various operators. The precedence mostly follows the C precedence rules, with one change and some additions. The precedence of -the bitwise operators & | and ^ is higher than the precedence -of relation operators <, <=, >, >=. This is more natural given +the bitwise operators & | and ^ is higher than the precedence +of relation operators <, <=, >, >=. This is more natural given the addition of a true boolean type in the type system, as bitwise operators cannot be applied to boolean types. -Concatenation (++) has the same precedence as infix -addition. Bit-slicing a[m:l] has the same precedence as array -indexing (a[i]). -

    -

    In addition to these expressions, P4 also supports select expressions (described -in Section 13.6), which may be used only in parsers. -

    8.1. Expression evaluation order

    -

    Given a compound expression, the order in which sub-expressions are +Concatenation (++) has the same precedence as infix +addition. Bit-slicing a[m:l] has the same precedence as array +indexing (a[i]).

    +
    +
    +

    In addition to these expressions, P4 also supports select expressions (described +in Section 13.6), which may be used only in parsers.

    +
    +
    +

    8.1. Expression evaluation order

    +
    +

    Given a compound expression, the order in which sub-expressions are evaluated is important when the sub-expressions have -side-effects. P4 expressions are evaluated as follows: -

    -
      -
    • Boolean operators && and || use short-circuit evaluationi.e., -the second operand is only evaluated if necessary. -
    • -
    • The conditional operator e1 ? e2 : e3 evaluates e1, and then -either evaluates e2 or e3. -
    • -
    • All other expressions are evaluated left-to-right as they appear in -the source program. -
    • -
    • Method and function calls are evaluated as described in Section -6.8. -
    -

    8.2. Operations on error types

    -

    Symbolic names declared by an error declaration belong to the error -namespace. The error type only supports equality (==) and inequality (!=) comparisons. -The result of such a comparison is a Boolean value. -

    -

    For example, the following operation tests for the occurrence of an -error: -

    -
    -
    -
    error errorFromParser;
    -
    -if (errorFromParser != error.NoError) { /* code omitted */ }

    8.3. Operations on enum types

    -

    Symbolic names declared by an enum belong to the namespace introduced by the enum declaration rather than the top-level namespace. -

    -
    -
    -
    enum X { v1, v2, v3 }
    -X.v1  // reference to v1
    -v1    // error - v1 is not in the top-level namespace
    -

    Similar to errors, enum expressions without a specified underlying type only support equality (==) -and inequality (!=) comparisons. Expressions whose type is an enum without a specified underlying type -cannot be cast to or from any other type. -

    -

    An enum may also specify an underlying type, such as the following: -

    -
    -
    -
    enum bit<8> E {
    -  e1 = 0,
    -  e2 = 1,
    -  e3 = 2
    -}
    -

    More than one symbolic value in an enum may map to the same fixed-width -integer value. -

    -
    -
    -
    enum bit<8> NonUnique {
    -  b1 = 0,
    -  b2 = 1,  // Note, both b2 and b3 map to the same value.
    -  b3 = 1,
    -  b4 = 2
    -}
    -

    An enum with an underlying type also supports explicit casts to and from the -underlying type. For instance, the following code: -

    -
    -
    -
    bit<8> x;
    -E a = E.e2;
    -E b;
    -
    -x = (bit<8>) a; // sets x to 1
    -b = (E) x;      // sets b to E.e2
    -

    casts a (which was initialized with E.e2) to a bit<8>, using the specified -fixed-width unsigned integer representation for E.e2, i.e. 1. The variable b is then set -to the symbolic value E.e2, which corresponds to the fixed-width unsigned integer value 1. -

    -

    Because it is always safe to cast from an enum to its underlying fixed-width integer type, -implicit casting from an enum to its fixed-width (signed or unsigned) integer type is also supported (see Section 8.11.2): -

    -
    -
    -
    bit<8> x = E.e2; // sets x to 1 (E.e2 is automatically casted to bit<8>)
    -
    -E  a = E.e2
    -bit<8> y = a << 3; // sets y to 8 (a is automatically casted to bit<8> and then shifted)
    -

    Implicit casting from an underlying fixed-width type to an enum is not supported. -

    -
    -
    -
    enum bit<8> E1 {
    -   e1 = 0, e2 = 1, e3 = 2
    -}
    -
    -enum bit<8> E2 {
    -   e1 = 10, e2 = 11, e3 = 12
    -}
    -E1 a = E1.e1;
    -E2 b = E2.e2;
    -
    -a = b;      // Error: b is automatically casted to bit<8>,
    -            // but bit<8> cannot be automatically casted to E1
    -
    -a = (E1) b; // OK
    -
    -a = E1.e1 + 1; // Error: E.e1 is automatically casted to bit<8>,
    -               // and the right-hand expression has
    -               // the type bit<8>, which cannot be casted to E automatically.
    -
    -a = (E1)(E1.e1 + 1); // Final explicit casting makes the assignment legal
    -
    -a = E1.e1 + E1.e2; // Error: both arguments to the addition are automatically
    -                   // casted to bit<8>. Thus the addition itself is legal, but
    -                   // the assignment is not
    -
    -a = (E1)(E2.e1 + E2.e2); //  Final explicit casting makes the assignment legal
    -

    A reasonable compiler might generate a warning in cases that involve multiple automatic casts. -

    -
    -
    -
    E1     a = E1.e1;
    -E2     b = E2.e2;
    -bit<8> c;
    -
    -if (a > b) { // Potential warning: two automatic and different casts to bit<8>.
    -   // code omitted
    -}
    -
    -c = a + b; // Legal, but a warning would be reasonable
    -

    Note that while it is always safe to cast from an enum to its fixed-width unsigned integer type, +side-effects. P4 expressions are evaluated as follows:

    +
    +
    +
      +
    • +

      Boolean operators && and || use short-circuit evaluation---i.e., +the second operand is only evaluated if necessary.

      +
    • +
    • +

      The conditional operator e1 ? e2 : e3 evaluates e1, and then +either evaluates e2 or e3.

      +
    • +
    • +

      All other expressions are evaluated left-to-right as they appear in +the source program.

      +
    • +
    • +

      Method and function calls are evaluated as described in +Section 6.8.

      +
    • +
    +
    +
    +
    +

    8.2. Operations on error types

    +
    +

    Symbolic names declared by an error declaration belong to the error +namespace. The error type only supports equality (==) and inequality (!=) comparisons. +The result of such a comparison is a Boolean value.

    +
    +
    +

    For example, the following operation tests for the occurrence of an +error:

    +
    +
    +
    +
    error errorFromParser;
    +
    +if (errorFromParser != error.NoError) { /* code omitted */ }
    +
    +
    +
    +
    +

    8.3. Operations on enum types

    +
    +

    Symbolic names declared by an enum belong to the namespace introduced by the enum declaration rather than the top-level namespace.

    +
    +
    +
    +
    enum X { v1, v2, v3 }
    +X.v1  // reference to v1
    +v1    // error - v1 is not in the top-level namespace
    +
    +
    +
    +

    Similar to errors, enum expressions without a specified underlying type only support equality (==) +and inequality (!=) comparisons. Expressions whose type is an enum without a specified underlying type +cannot be cast to or from any other type.

    +
    +
    +

    An enum may also specify an underlying type, such as the following:

    +
    +
    +
    +
    enum bit<8> E {
    +  e1 = 0,
    +  e2 = 1,
    +  e3 = 2
    +}
    +
    +
    +
    +

    More than one symbolic value in an enum may map to the same fixed-width +integer value.

    +
    +
    +
    +
    enum bit<8> NonUnique {
    +  b1 = 0,
    +  b2 = 1,  // Note, both b2 and b3 map to the same value.
    +  b3 = 1,
    +  b4 = 2
    +}
    +
    +
    +
    +

    An enum with an underlying type also supports explicit casts to and from the +underlying type. For instance, the following code:

    +
    +
    +
    +
    bit<8> x;
    +E a = E.e2;
    +E b;
    +
    +x = (bit<8>) a; // sets x to 1
    +b = (E) x;      // sets b to E.e2
    +
    +
    +
    +
      +
    1. +

      casts a (which was initialized with E.e2) to a bit<8>, using the specified +fixed-width unsigned integer representation for E.e2, i.e. 1. The variable b is then set +to the symbolic value E.e2, which corresponds to the fixed-width unsigned integer value 1.

      +
    2. +
    +
    +
    +

    Because it is always safe to cast from an enum to its underlying fixed-width integer type, +implicit casting from an enum to its fixed-width (signed or unsigned) integer type is also supported (see Section 8.11.2):

    +
    +
    +
    +
    bit<8> x = E.e2; // sets x to 1 (E.e2 is automatically casted to bit<8>)
    +
    +E  a = E.e2
    +bit<8> y = a << 3; // sets y to 8 (a is automatically casted to bit<8> and then shifted)
    +
    +
    +
    +

    Implicit casting from an underlying fixed-width type to an enum is not supported.

    +
    +
    +
    +
    enum bit<8> E1 {
    +   e1 = 0, e2 = 1, e3 = 2
    +}
    +
    +enum bit<8> E2 {
    +   e1 = 10, e2 = 11, e3 = 12
    +}
    +E1 a = E1.e1;
    +E2 b = E2.e2;
    +
    +a = b;      // Error: b is automatically casted to bit<8>,
    +            // but bit<8> cannot be automatically casted to E1
    +
    +a = (E1) b; // OK
    +
    +a = E1.e1 + 1; // Error: E.e1 is automatically casted to bit<8>,
    +               // and the right-hand expression has
    +               // the type bit<8>, which cannot be casted to E automatically.
    +
    +a = (E1)(E1.e1 + 1); // Final explicit casting makes the assignment legal
    +
    +a = E1.e1 + E1.e2; // Error: both arguments to the addition are automatically
    +                   // casted to bit<8>. Thus the addition itself is legal, but
    +                   // the assignment is not
    +
    +a = (E1)(E2.e1 + E2.e2); //  Final explicit casting makes the assignment legal
    +
    +
    +
    +

    A reasonable compiler might generate a warning in cases that involve multiple automatic casts.

    +
    +
    +
    +
    E1     a = E1.e1;
    +E2     b = E2.e2;
    +bit<8> c;
    +
    +if (a > b) { // Potential warning: two automatic and different casts to bit<8>.
    +   // code omitted
    +}
    +
    +c = a + b; // Legal, but a warning would be reasonable
    +
    +
    +
    +

    Note that while it is always safe to cast from an enum to its fixed-width unsigned integer type, and vice versa, there may be cases where casting a fixed-width unsigned integer value to -its related enum type produces an unnamed value. -

    -
    -
    -
    bit<8> x = 5;
    -E e = (E) x; // sets e to an unnamed value
    -

    sets e to an unnamed value, since there is no symbol corresponding to the -fixed-width unsigned integer value 5. -

    -

    For example, in the following code, the else clause of the if/else if/else -block can be reached even though the matches on x are complete with respect -to the symbols defined in MyPartialEnum_t: -

    -
    -
    -
    enum bit<2> MyPartialEnum_t {
    -    VALUE_A = 2w0,
    -    VALUE_B = 2w1,
    -    VALUE_C = 2w2
    -}
    -
    -bit<2> y = < some value >;
    -MyPartialEnum_t x = (MyPartialEnum_t)y;
    -
    -if (x == MyPartialEnum_t.VALUE_A) {
    -    // some code here
    -} else if (x == MyPartialEnum_t.VALUE_B) {
    -    // some code here
    -} else if (x == MyPartialEnum_t.VALUE_C) {
    -    // some code here
    -} else {
    -    // A P4 compiler MUST ASSUME that this branch can be executed
    -    // some code here
    -}
    -

    Additionally, if an enumeration is used as a field of a header, we would expect -the transition select to match default when the parsed integer value does -not match one of the symbolic values of EtherType in the following example: -

    -
    -
    -
    enum bit<16> EtherType {
    -  VLAN      = 0x8100,
    -  IPV4      = 0x0800,
    -  IPV6      = 0x86dd
    -}
    -
    -header ethernet {
    -  // Some fields omitted
    -  EtherType etherType;
    -}
    -
    -parser my_parser(/* parameters omitted */) {
    -  state parse_ethernet {
    -    packet.extract(hdr.ethernet);
    -    transition select(hdr.ethernet.etherType) {
    -      EtherType.VLAN : parse_vlan;
    -      EtherType.IPV4 : parse_ipv4;
    -      EtherType.IPV6: parse_ipv6;
    -      default: reject;
    -  }
    -}
    -

    Any variable with an enum type that contains an unnamed value (whether as the -result of a cast to an enum with an underlying type, parse into the field of -an enum with an underlying type, or simply the declaration of any enum +its related enum type produces an unnamed value.

    +
    +
    +
    +
    bit<8> x = 5;
    +E e = (E) x; // sets e to an unnamed value
    +
    +
    +
    +

    sets e to an unnamed value, since there is no symbol corresponding to the +fixed-width unsigned integer value 5.

    +
    +
    +

    For example, in the following code, the else clause of the if/else if/else +block can be reached even though the matches on x are complete with respect +to the symbols defined in MyPartialEnum_t:

    +
    +
    +
    +
    enum bit<2> MyPartialEnum_t {
    +    VALUE_A = 2w0,
    +    VALUE_B = 2w1,
    +    VALUE_C = 2w2
    +}
    +
    +bit<2> y = < some value >;
    +MyPartialEnum_t x = (MyPartialEnum_t)y;
    +
    +if (x == MyPartialEnum_t.VALUE_A) {
    +    // some code here
    +} else if (x == MyPartialEnum_t.VALUE_B) {
    +    // some code here
    +} else if (x == MyPartialEnum_t.VALUE_C) {
    +    // some code here
    +} else {
    +    // A P4 compiler MUST ASSUME that this branch can be executed
    +    // some code here
    +}
    +
    +
    +
    +

    Additionally, if an enumeration is used as a field of a header, we would expect +the transition select to match default when the parsed integer value does +not match one of the symbolic values of EtherType in the following example:

    +
    +
    +
    +
    enum bit<16> EtherType {
    +  VLAN      = 0x8100,
    +  IPV4      = 0x0800,
    +  IPV6      = 0x86dd
    +}
    +
    +header ethernet {
    +  // Some fields omitted
    +  EtherType etherType;
    +}
    +
    +parser my_parser(/* parameters omitted */) {
    +  state parse_ethernet {
    +    packet.extract(hdr.ethernet);
    +    transition select(hdr.ethernet.etherType) {
    +      EtherType.VLAN : parse_vlan;
    +      EtherType.IPV4 : parse_ipv4;
    +      EtherType.IPV6: parse_ipv6;
    +      default: reject;
    +  }
    +}
    +
    +
    +
    +

    Any variable with an enum type that contains an unnamed value (whether as the +result of a cast to an enum with an underlying type, parse into the field of +an enum with an underlying type, or simply the declaration of any enum without a specified initial value) will not be equal to any of the values defined for that type. Such an unnamed value should still lead to predictable behavior in cases where any legal value would match, e.g. it should match in -any of these situations: -

    -
      -
    • If used in a select expression, it should match default or _ -in a key set expression. -
    • -
    • If used as a key with match_kind ternary in a table, it should -match a table entry where the field has all bit positions “don't care”. -
    • -
    • If used as a key with match_kind lpm in a table, it should match -a table entry where the field has a prefix length of 0. -
    - -

    Note that if an enum value lacking an underlying type appears in the +any of these situations:

    +
    +
    +
      +
    • +

      If used in a select expression, it should match default or _ +in a key set expression.

      +
    • +
    • +

      If used as a key with match_kind ternary in a table, it should +match a table entry where the field has all bit positions "don’t care".

      +
    • +
    • +

      If used as a key with match_kind lpm in a table, it should match +a table entry where the field has a prefix length of 0.

      +
    • +
    +
    +
    +

    Note that if an enum value lacking an underlying type appears in the control-plane API, the compiler must select a suitable serialization data type -and representation. For enum values with an underlying type and +and representation. For enum values with an underlying type and representations, the compiler should use the specified underlying type as the -serialization data type and representation. -

    -

    Additionally, the size of a serializable enum can be determined at +serialization data type and representation.

    +
    +
    +

    Additionally, the size of a serializable enum can be determined at compile-time. However, the size of an enum without an -underlying type cannot be determined at compile-time (Section 9). -

    8.4. Operations on match_kind types

    -

    Values of type match_kind are similar to enum values. They -support only assignment and comparisons for equality and inequality. -

    -
    -
    -
    match_kind { fuzzy }
    -const bool same = exact == fuzzy;  // always 'false'

    8.5. Expressions on Booleans

    -

    The following operations are provided on Boolean expressions: -

    -
      -
    • “And”, denoted by && -
    • -
    • “Or”, denoted by || -
    • -
    • Negation, denoted by ! -
    • -
    • Equality and inequality tests, denoted by == and != respectively. -
    - -

    The precedence of these operators is similar to C and uses short-circuited -evaluation where relevant. -

    -

    Additionally, the size of a boolean can be determined at -compile-time (Section 9). -

    -

    P4 does not implicitly cast from bit-strings to Booleans or -vice versa. As a consequence, a program that is valid in a language like C such as, -

    -
    -
    -
    if (x) /* body omitted */
    -

    (where x has an integer type) must instead be written in P4 as: -

    -
    -
    -
    if (x != 0) /* body omitted */
    -

    See the discussion on arbitrary-precision types and implicit casts -in Section 8.11.2 for details on how the 0 in this -expression is evaluated. -

    8.5.1. Conditional operator

    -

    A conditional expression of the form e1 ? e2 : e3 behaves the -same as in languages like C. As described above, the expression e1 is evaluated first, -and second either e2 or e3 is evaluated depending on the result. -

    -

    The first sub-expression e1 must have Boolean type +underlying type cannot be determined at compile-time (Chapter 9).

    +
    +
    +
    +

    8.4. Operations on match_kind types

    +
    +

    Values of type match_kind are similar to enum values. They +support only assignment and comparisons for equality and inequality.

    +
    +
    +
    +
    match_kind { fuzzy }
    +const bool same = exact == fuzzy;  // always 'false'
    +
    +
    +
    +
    +

    8.5. Expressions on Booleans

    +
    +

    The following operations are provided on Boolean expressions:

    +
    +
    +
      +
    • +

      "And", denoted by &&

      +
    • +
    • +

      "Or", denoted by ||

      +
    • +
    • +

      Negation, denoted by !

      +
    • +
    • +

      Equality and inequality tests, denoted by == and != respectively.

      +
    • +
    +
    +
    +

    The precedence of these operators is similar to C and uses short-circuited +evaluation where relevant.

    +
    +
    +

    Additionally, the size of a boolean can be determined at +compile-time (Chapter 9).

    +
    +
    +

    P4 does not implicitly cast from bit-strings to Booleans or +vice versa. As a consequence, a program that is valid in a language like C such as,

    +
    +
    +
    +
    if (x) /* body omitted */
    +
    +
    +
    +

    (where x has an integer type) must instead be written in P4 as:

    +
    +
    +
    +
    if (x != 0) /* body omitted */
    +
    +
    +
    +

    See the discussion on arbitrary-precision types and implicit casts +in Section 8.11.2 for details on how the 0 in this +expression is evaluated.

    +
    +
    +

    8.5.1. Conditional operator

    +
    +

    A conditional expression of the form e1 ? e2 : e3 behaves the +same as in languages like C. As described above, the expression e1 is evaluated first, +and second either e2 or e3 is evaluated depending on the result.

    +
    +
    +

    The first sub-expression e1 must have Boolean type and the second and third sub-expressions must have the same type, which cannot both be arbitrary-precision integers unless the condition itself can be evaluated at compilation time. This restriction is designed to ensure that the width of the result of the conditional -expression can be inferred statically at compile time. -

    8.6. Operations on fixed-width bit types (unsigned integers)

    -

    This section discusses all operations that can be performed on -expressions of type bit<W> for some width W, also known as -bit-strings. -

    -

    Arithmetic operations “wrap around”, similar to C operations on +expression can be inferred statically at compile time.

    +
    +
    +
    +
    +

    8.6. Operations on fixed-width bit types (unsigned integers)

    +
    +

    This section discusses all operations that can be performed on +expressions of type bit<W> for some width W, also known as +bit-strings.

    +
    +
    +

    Arithmetic operations "wrap around", similar to C operations on unsigned values (i.e., representing a large value on W bits will only keep the least-significant W bits of the value). In particular, P4 -does not have arithmetic exceptionsthe result of an arithmetic -operation is defined for all possible inputs. -

    -

    P4 target architectures may optionally support saturating arithmetic. All saturating +does not have arithmetic exceptions---the result of an arithmetic +operation is defined for all possible inputs.

    +
    +
    +

    P4 target architectures may optionally support saturating arithmetic. All saturating operations are limited to a fixed range between a minimum and maximum value. Saturating arithmetic has advantages, in particular when used as counters. The result of a saturating counter max-ing out is much closer to the real result than a counter that overflows and wraps around. According to Wikipedia -Saturating Arithmetic -saturating arithmetic is as numerically close to the true answer as possible; +Saturating Arithmetic +is as numerically close to the true answer as possible; for 8-bit binary signed arithmetic, when the correct answer is 130, it is considerably less surprising to get an answer of 127 from saturating arithmetic than to get an answer of −126 from modular arithmetic. @@ -4009,1912 +5841,2610 @@

    W, the minimum value is 0 and the maximum value -is 2^W-1. The precedence of saturating addition and subtraction operations -is the same as for modular arithmetic addition and subtraction. -

    -

    All binary operations except shifts and concatenation require both operands to +integer with bit-width of W, the minimum value is 0 and the maximum value +is 2^W-1. The precedence of saturating addition and subtraction operations +is the same as for modular arithmetic addition and subtraction.

    +

    +
    +

    All binary operations except shifts and concatenation require both operands to have the same exact type and width; supplying operands with different widths produces an error at compile time. No implicit casts are inserted by the compiler to equalize the widths. There are no other binary operations that accept signed and unsigned values simultaneously besides shifts and concatenation. The following operations are provided on bit-string -expressions: -

    -
      -
    • Test for equality between bit-strings of the same width, -designated by ==. The result is a Boolean value. -
    • -
    • Test for inequality between bit-strings of the same width, -designated by !=. The result is a Boolean value. -
    • -
    • Unsigned comparisons <,>,<=,>=. Both operands must have the -same width and the result is a Boolean value. -
    - -

    Each of the following operations produces a bit-string result when applied -to bit-strings of the same width: -

    -
      -
    • Negation, denoted by unary -. The result is computed by -subtracting the value from 2W. The result is unsigned and +expressions:

      +
    +
    +
      +
    • +

      Test for equality between bit-strings of the same width, +designated by ==. The result is a Boolean value.

      +
    • +
    • +

      Test for inequality between bit-strings of the same width, +designated by !=. The result is a Boolean value.

      +
    • +
    • +

      Unsigned comparisons <,>,<=,>=. Both operands must have the +same width and the result is a Boolean value.

      +
    • +
    +
    +
    +

    Each of the following operations produces a bit-string result when applied +to bit-strings of the same width:

    +
    +
    +
      +
    • +

      Negation, denoted by unary -. The result is computed by +subtracting the value from 2W. The result is unsigned and has the same width as the input. The semantics is the same as the -C negation of unsigned numbers. +C negation of unsigned numbers.

    • -
    • Unary plus, denoted by +. This operation behaves like a no-op. +
    • +

      Unary plus, denoted by +. This operation behaves like a no-op.

    • -
    • Addition, denoted by +. This operation is associative. The result is computed by +
    • +

      Addition, denoted by +. This operation is associative. The result is computed by truncating the result of the addition to the width of the output -(similar to C). -

    • -
    • Subtraction, denoted by -. The result is unsigned, and has the -same type as the operands. It is computed by adding the negation -of the second operand (similar to C). -
    • -
    • Multiplication, denoted by *. The result has the same width as the operands -and is computed by truncating the result to the output's width. -P4 architectures may impose additional restrictions e.g., they may only -allow multiplication by a non-negative integer power of two. +(similar to C).

    • -
    • Bitwise “and” between two bit-strings of the same width, denoted by &. -
    • -
    • Bitwise “or” between two bit-strings of the same width, denoted by |. -
    • -
    • Bitwise “complement” of a single bit-string, denoted by ~. -
    • -
    • Bitwise “xor” of two bit-strings of the same width, denoted by ^. -
    • -
    • Saturating addition, denoted by |+|. -
    • -
    • Saturating subtraction, denoted by |-|. -
    - -

    Bit-strings also support the following operations: -

    -
      -
    • Logical shift left and right by a non-negative integer value (which need not be -a compile-time known value), denoted by << and >> respectively. +
    • +

      Subtraction, denoted by -. The result is unsigned, and has the +same type as the operands. It is computed by adding the negation +of the second operand (similar to C).

      +
    • +
    • +

      Multiplication, denoted by *. The result has the same width as the operands +and is computed by truncating the result to the output’s width. +P4 architectures may impose additional restrictions --- e.g., they may only +allow multiplication by a non-negative integer power of two.

      +
    • +
    • +

      Bitwise "and" between two bit-strings of the same width, denoted by &.

      +
    • +
    • +

      Bitwise "or" between two bit-strings of the same width, denoted by |.

      +
    • +
    • +

      Bitwise "complement" of a single bit-string, denoted by ~.

      +
    • +
    • +

      Bitwise "xor" of two bit-strings of the same width, denoted by ^.

      +
    • +
    • +

      Saturating addition, denoted by |+|.

      +
    • +
    • +

      Saturating subtraction, denoted by |-|.

      +
    • +
    +
    +
    +

    Bit-strings also support the following operations:

    +
    +
    +
      +
    • +

      Logical shift left and right by a non-negative integer value (which need not be +a compile-time known value), denoted by << and >> respectively. In a shift, the left operand is unsigned, and right operand must be either an -expression of type bit<S> or a non-negative integer value that is known at +expression of type bit<S> or a non-negative integer value that is known at compile time. The result has the same type as the left operand. Shifting by an amount greater than or equal to the width of the input -produces a result where all bits are zero. -

    • -
    • Extraction of a set of contiguous bits, also known as a slice, -denoted by [H:L], where H and L must be expressions that evaluate to -non-negative, local compile-time known values, and H >= L. The types of H and L -(which do not need to be identical) must be numeric (Section 7.4). -The result is a bit-string of width H - L + 1, including the bits numbered -from L (which becomes the least significant bit of the result) to H (the +produces a result where all bits are zero.

      +
    • +
    • +

      Extraction of a set of contiguous bits, also known as a slice, +denoted by [H:L], where H and L must be expressions that evaluate to +non-negative, local compile-time known values, and H >= L. The types of H and L +(which do not need to be identical) must be numeric (Section 7.4). +The result is a bit-string of width H - L + 1, including the bits numbered +from L (which becomes the least significant bit of the result) to H (the most significant bit of the result) from the source operand. The conditions -0 <= L <= H < W are checked statically (where W is +0 <= L <= H < W are checked statically (where W is the length of the source bit-string). Note that both endpoints of the extraction are inclusive. The bounds are required to be -local compile-time known values so that the width of the result can be computed -at compile time. Slices are also l-values, which means that P4 supports -assigning to a slice: e[H:L] = x . -The effect of this statement is to set bits H through L (inclusive of -both) of e to the bit-pattern represented by x, and leaves all other bits -of e unchanged. A slice of an unsigned integer is an unsigned integer. -

    • -
    • Concatenation of bit-strings and/or fixed-width signed integers, denoted by ++. -The two operands must be either bit<W> or int<W>, and they can be of +local compile-time known values so that the width of the result can be computed at compile time. Slices are also l-values, which means that P4 supports assigning to a slice: e[H:L] = x. +The effect of this statement is to set bits H through L (inclusive of +both) of e to the bit-pattern represented by x, and leaves all other bits +of e unchanged. A slice of an unsigned integer is an unsigned integer.

      +
    • +
    • +

      Concatenation of bit-strings and/or fixed-width signed integers, denoted by ++. +The two operands must be either bit<W> or int<W>, and they can be of different signedness and width. The result has the same signedness as the -left operand and the width equal to the sum of the two operands' width. -In concatenation, the left operand is placed as the most significant bits. -

    - -

    Additionally, the size of a bit-string can be determined at -compile-time (Section 9). -

    8.7. Operations on fixed-width signed integers

    -

    This section discusses all operations that can be performed on expressions of type int<W> -for some W. Recall that the int<W> denotes signed W-bit integers, -represented using two's complement. -

    -

    In general, P4 arithmetic operations do not detect “underflow” or “overflow”: -operations simply “wrap around”, similar to C operations on unsigned values. -Hence, attempting to represent large values using W bits will only keep -the least-significant W bits of the value. -

    -

    P4 supports saturating arithmetic (addition and subtraction) for signed +left operand and the width equal to the sum of the two operands' width. +In concatenation, the left operand is placed as the most significant bits.

    + + +
    +
    +

    Additionally, the size of a bit-string can be determined at +compile-time (Chapter 9).

    +
    +
    +
    +

    8.7. Operations on fixed-width signed integers

    +
    +

    This section discusses all operations that can be performed on expressions of type int<W> +for some W. Recall that the int<W> denotes signed W-bit integers, +represented using two’s complement.

    +
    +
    +

    In general, P4 arithmetic operations do not detect "underflow" or "overflow": +operations simply "wrap around", similar to C operations on unsigned values. +Hence, attempting to represent large values using W bits will only keep +the least-significant W bits of the value.

    +
    +
    +

    P4 supports saturating arithmetic (addition and subtraction) for signed integers. Targets may optionally reject programs using saturating arithmetic. -For a signed integer with bit-width of W, the minimum value is --2^(W-1) and the maximum value is 2^(W-1)-1. -

    -

    P4 also does not support arithmetic exceptions. The runtime result of an +For a signed integer with bit-width of W, the minimum value is +-2^(W-1) and the maximum value is 2^(W-1)-1.

    +
    +
    +

    P4 also does not support arithmetic exceptions. The runtime result of an arithmetic operation is defined for all combinations of input -arguments. -

    -

    All binary operations except shifts and concatenation require both operands +arguments.

    +
    +
    +

    All binary operations except shifts and concatenation require both operands to have the same exact type (signedness) and width and supplying operands with different widths or signedness produces a compile-time error. No implicit casts are inserted by the compiler to equalize the types. Except for shifts and concatenation, P4 does not have any binary operations -that operate simultaneously on signed and unsigned values. -

    -

    Note that bitwise operations on signed integers are well-defined, since the -representation is mandated to be two's complement. -

    -

    The int<W> datatype supports the following operations; all +that operate simultaneously on signed and unsigned values.

    +
    +
    +

    Note that bitwise operations on signed integers are well-defined, since the +representation is mandated to be two’s complement.

    +
    +
    +

    The int<W> datatype supports the following operations; all binary operations require both operands to have the exact same -type. The result always has the same width as the left operand. -

    -
      -
    • Negation, denoted by unary -. +type. The result always has the same width as the left operand.

      +
    +
    +
      +
    • +

      Negation, denoted by unary -.

    • -
    • Unary plus, denoted by +. This operation behaves like a no-op. +
    • +

      Unary plus, denoted by +. This operation behaves like a no-op.

    • -
    • Addition, denoted by +. +
    • +

      Addition, denoted by +.

    • -
    • Subtraction, denoted by -. +
    • +

      Subtraction, denoted by -.

    • -
    • Comparison for equality and inequality, denoted == and != respectively. These operations produce a -Boolean result. +
    • +

      Comparison for equality and inequality, denoted == and != respectively. These operations produce a +Boolean result.

    • -
    • Numeric comparisons, denoted by <,<=,>, and >=. These operations produce a Boolean result. +
    • +

      Numeric comparisons, denoted by <,<=,>, and >=. These operations produce a Boolean result.

    • -
    • Multiplication, denoted by *. Result has the same width as the -operands. P4 architectures may impose additional restrictionse.g., they may -only allow multiplication by a power of two. +
    • +

      Multiplication, denoted by *. Result has the same width as the +operands. P4 architectures may impose additional restrictions---e.g., they may +only allow multiplication by a power of two.

    • -
    • Bitwise “and” between two bit-strings of the same width, denoted by &. +
    • +

      Bitwise "and" between two bit-strings of the same width, denoted by &.

    • -
    • Bitwise “or” between two bit-strings of the same width, denoted by |. +
    • +

      Bitwise "or" between two bit-strings of the same width, denoted by |.

    • -
    • Bitwise “complement” of a single bit-string, denoted by ~. +
    • +

      Bitwise "complement" of a single bit-string, denoted by ~.

    • -
    • Bitwise “xor” of two bit-strings of the same width, denoted by ^. +
    • +

      Bitwise "xor" of two bit-strings of the same width, denoted by ^.

    • -
    • Saturating addition, denoted by |+|. +
    • +

      Saturating addition, denoted by |+|.

    • -
    • Saturating subtraction, denoted by |-|. -
    - -

    The int<W> datatype also support the following operations: -

    -
      -
    • Arithmetic shift left and right denoted by << and >>. +
    • +

      Saturating subtraction, denoted by |-|.

      +
    • +
    +
    +
    +

    The int<W> datatype also support the following operations:

    +
    +
    +
      +
    • +

      Arithmetic shift left and right denoted by << and >>. The left operand is signed and the right operand must be either an -unsigned number of type bit<S> or a compile-time known value that is a non-negative integer. +unsigned number of type bit<S> or a compile-time known value that is a non-negative integer. The result has the same type as the left operand. Shifting left produces the exact same bit pattern as a shift left of an unsigned value. Shift left can thus overflow, when it leads to a change of the sign bit. Shifting by an amount greater than the width of the input produces -a “correct” result: - -

        -
      • all result bits are zero when shifting left -
      • -
      • all result bits are zero when shifting a non-negative value right -
      • -
      • all result bits are one when shifting a negative value right -
    • -
    • Extraction of a set of contiguous bits, also known as a slice, -denoted by [H:L], where H and L must be expressions that evaluate to -non-negative, local compile-time known values, and H >= L must be true. -The types of H and L (which do not need to be identical) -must be numeric (Section 7.4). -The result is an unsigned bit-string of width H - L + 1, including the bits -numbered from L (which becomes the least significant bit of the result) -to H (the most significant bit of the result) from the source operand. -The conditions 0 <= L <= H < W are checked statically (where W is +a "correct" result:

      +
      +
        +
      • +

        all result bits are zero when shifting left

        +
      • +
      • +

        all result bits are zero when shifting a non-negative value right

        +
      • +
      • +

        all result bits are one when shifting a negative value right

        +
      • +
      +
      +
    • +
    • +

      Extraction of a set of contiguous bits, also known as a slice, +denoted by [H:L], where H and L must be expressions that evaluate to +non-negative, local compile-time known values, and H >= L must be true. +The types of H and L (which do not need to be identical) +must be numeric (Section 7.4). +The result is an unsigned bit-string of width H - L + 1, including the bits +numbered from L (which becomes the least significant bit of the result) +to H (the most significant bit of the result) from the source operand. +The conditions 0 <= L <= H < W are checked statically (where W is the length of the source bit-string). Note that both endpoints of the extraction are inclusive. The bounds are required to be values that are known at compile time so that the width of the result can be computed at compile time. Slices are also l-values, which means that P4 -supports assigning to a slice: e[H:L] = x . -The effect of this statement is to set bits H through L of e to the -bit-pattern represented by x, and leaves all other bits of e unchanged. -A slice of a signed integer is treated as an unsigned integer. -

    • -
    • Concatenation of bit-strings and/or fixed-width signed integers, denoted by ++. -The two operands must be either bit<W> or int<W>, and they can be of different signedness and width. -The result has the same signedness as the left operand and the width equal to the sum of the two operands' width. -In concatenation, the left operand is placed as the most significant bits. -
    - -

    Additionally, the size of a fixed-width signed integer can be determined at -compile-time (Section 9). -

    8.8. Operations on arbitrary-precision integers

    -

    The type int denotes arbitrary-precision integers. In P4, all -expressions of type int must be compile-time known values. The type -int supports the following operations: -

    -
      -
    • -

      Negation, denoted by unary - -

    • -
    • -

      Unary plus, denoted by +. This operation behaves like a no-op. -

    • -
    • -

      Addition, denoted by +. -

    • -
    • -

      Subtraction, denoted by -. -

    • -
    • -

      Comparison for equality and inequality, denoted by == and != respectively. These operations produce a -Boolean result. -

    • -
    • -

      Numeric comparisons <,<=,>, and >=. These operations produce a Boolean result. -

    • -
    • -

      Multiplication, denoted by *. -

    • -
    • -

      Truncating integer division between positive values, denoted by /. -

    • -
    • -

      Modulo between positive values, denoted by %. -

    • -
    • -

      Arithmetic shift left and right denoted by << and >>. These operations produce an int result. -The right operand must be either an unsigned value of type bit<S> or a compile-time known value that is a non-negative integer. -The expression a << b is equal to $a \times 2^b$ while a >> b -is equal to $\lfloor{a / 2^b}\rfloor$. -

    • -
    • -

      Bit slices, denoted by [H:L], where H and L must be expressions that evaluate to -non-negative, local compile-time known values, and H >= L must be true. -The types of H and L (which do not need to be identical) -must be one of the following: -

      -
        -
      • int - an arbitrary-precision integer -(section 7.1.6.5) -
      • -
      • bit<W> - a W-bit unsigned integer where W >= 0 -(section 7.1.6.2) -
      • -
      • int<W> - a W-bit signed integer where W >= 1 -(section 7.1.6.3) -
      • -
      • a serializable enum with an underlying type that is bit<W> or int<W> -(section 7.2.1). -
      - -

      The result is an unsigned bit-string of width H - L + 1, including the bits -numbered from L (which becomes the least significant bit of the result) -to H (the most significant bit of the result) from the source operand. -The conditions 0 <= L <= H are checked statically. If necessary, the source -integer value that is sliced is automatically extended to have a width with H +supports assigning to a slice: e[H:L] = x. +The effect of this statement is to set bits H through L of e to the +bit-pattern represented by x, and leaves all other bits of e unchanged. +A slice of a signed integer is treated as an unsigned integer.

      +
    • +
    • +

      Concatenation of bit-strings and/or fixed-width signed integers, denoted by ++. +The two operands must be either bit<W> or int<W>, and they can be of different signedness and width. +The result has the same signedness as the left operand and the width equal to the sum of the two operands' width. +In concatenation, the left operand is placed as the most significant bits.

      +
    • +
    +
    +
    +

    Additionally, the size of a fixed-width signed integer can be determined at +compile-time (Chapter 9).

    +
    +
    +
    +

    8.8. Operations on arbitrary-precision integers

    +
    +

    The type int denotes arbitrary-precision integers. In P4, all +expressions of type int must be compile-time known values. The type +int supports the following operations:

    +
    +
    +
      +
    • +

      Negation, denoted by unary -

      +
    • +
    • +

      Unary plus, denoted by +. This operation behaves like a no-op.

      +
    • +
    • +

      Addition, denoted by +.

      +
    • +
    • +

      Subtraction, denoted by -.

      +
    • +
    • +

      Comparison for equality and inequality, denoted by == and != respectively. These operations produce a +Boolean result.

      +
    • +
    • +

      Numeric comparisons <,<=,>, and >=. These operations produce a Boolean result.

      +
    • +
    • +

      Multiplication, denoted by *.

      +
    • +
    • +

      Truncating integer division between positive values, denoted by /.

      +
    • +
    • +

      Modulo between positive values, denoted by %.

      +
    • +
    • +

      Arithmetic shift left and right denoted by << and >>. These operations produce an int result. +The right operand must be either an unsigned value of type bit<S> or a compile-time known value that is a non-negative integer. +The expression a << b is equal to stem e137740de79d029b9b56331088395037 while a >> b +is equal to stem a8822a017b2f283d08e8ddf4ab6028ea.

      +
    • +
    • +

      Bit slices, denoted by [H:L], where H and L must be expressions that evaluate to +non-negative, local compile-time known values, and H >= L must be true. +The types of H and L (which do not need to be identical) +must be one of the following:

      +
      +
      +
      +
        +
      • +

        int - an arbitrary-precision integer +(Section 7.1.6.5)

        +
      • +
      • +

        bit<W> - a W-bit unsigned integer where W >= 0 +(Section 7.1.6.2)

        +
      • +
      • +

        int<W> - a W-bit signed integer where W >= 1 +(Section 7.1.6.3)

        +
      • +
      • +

        a serializable enum with an underlying type that is bit<W> or int<W> +(Section 7.2.1).

        +
      • +
      +
      +
      +
      +
      +

      The result is an unsigned bit-string of width H - L + 1, including the bits +numbered from L (which becomes the least significant bit of the result) +to H (the most significant bit of the result) from the source operand. +The conditions 0 <= L <= H are checked statically. If necessary, the source +integer value that is sliced is automatically extended to have a width with H bits. Note that both endpoints of the extraction are inclusive. The bounds are required to be values that are known at compile time so that the width of the result can be computed at compile time. A slice of a negative or positive value is always -a positive value. -

    - -

    Each operand that participates in any of these operation must have -type int (except shifts). Binary operations cannot -be used to combine values of type int with values of a fixed-width -type (except shifts). However, the compiler automatically inserts casts from int -to fixed-width types in certain situationssee Section 8.11. -

    -

    All computations on int values are carried out without loss of +a positive value.

    +
    + + +
    +
    +

    Each operand that participates in any of these operation must have +type int (except shifts). Binary operations cannot +be used to combine values of type int with values of a fixed-width +type (except shifts). However, the compiler automatically inserts casts from int +to fixed-width types in certain situations---see Section 8.11.

    +
    +
    +

    All computations on int values are carried out without loss of information. For example, multiplying two 1024-bit values may produce -a 2048-bit value (note that concrete representation of int -values is not specified). int values can be cast to bit<w> -and int<w> values. Casting an int value to a fixed-width +a 2048-bit value (note that concrete representation of int +values is not specified). int values can be cast to bit<w> +and int<w> values. Casting an int value to a fixed-width type will preserve the least-significant bits. If truncation causes -significant bits to be lost, the compiler should emit a warning. -

    -

    Note: bitwise-operations (|,&,^,~) are not -defined on expressions of type int. In addition, it is illegal -to apply division and modulo to negative values. -

    -

    Note: saturating arithmetic is not supported for arbitrary-precision integers. -

    8.9. Concatenation and shifts

    8.9.1. Concatenation

    -

    Concatenation is applied to two bit-strings (signed or unsigned). -It is denoted by the infix operator ++. The result is a bit-string +significant bits to be lost, the compiler should emit a warning.

    +
    +
    +

    Note: bitwise-operations (|, &, ^, ~) are not +defined on expressions of type int. In addition, it is illegal +to apply division and modulo to negative values.

    +
    +
    +

    Note: saturating arithmetic is not supported for arbitrary-precision integers.

    +
    +
    +
    +

    8.9. Concatenation and shifts

    +
    +

    8.9.1. Concatenation

    +
    +

    Concatenation is applied to two bit-strings (signed or unsigned). +It is denoted by the infix operator ++. The result is a bit-string whose length is the sum of the lengths of the inputs where the most significant bits are taken from the left operand; the sign of the -result is taken from the left operand. -

    8.9.2. A note about shifts

    -

    The left operand of shifts can be any one out of unsigned bit-strings, signed bit-strings, +result is taken from the left operand.

    +
    +
    +
    +

    8.9.2. A note about shifts

    +
    +

    The left operand of shifts can be any one out of unsigned bit-strings, signed bit-strings, and arbitrary-precision integers, and the right operand of shifts must be either -an expression of type bit<S> or a compile-time known value that is a non-negative integer. -The result has the same type as the left operand. -

    -

    Shifts on signed and unsigned bit-strings deserve a special discussion -for the following reasons: -

    -
      -
    • Right shift behaves differently for signed and unsigned +an expression of type bit<S> or a compile-time known value that is a non-negative integer. +The result has the same type as the left operand.

      +
    +
    +

    Shifts on signed and unsigned bit-strings deserve a special discussion +for the following reasons:

    +
    +
    +
      +
    • +

      Right shift behaves differently for signed and unsigned bit-strings: right shift for signed bit-strings is an arithmetic shift, -and for unsigned bit-strings is a logical shift. +and for unsigned bit-strings is a logical shift.

    • -
    • Shifting with a negative amount does not have a clear semantics: -the P4 type system makes it illegal to shift with a negative amount. +
    • +

      Shifting with a negative amount does not have a clear semantics: +the P4 type system makes it illegal to shift with a negative amount.

    • -
    • Unlike C, shifting by an amount larger than or equal to the number of bits -has a well-defined result. +
    • +

      Unlike C, shifting by an amount larger than or equal to the number of bits +has a well-defined result.

    • -
    • Finally, depending on the capabilities of the target, shifting may +
    • +

      Finally, depending on the capabilities of the target, shifting may require doing work which is exponential in the number of bits of the -right-hand-side operand. -

    - -

    Consider the following examples: -

    -
    -
    -
    bit<8> x;
    -bit<16> y;
    -bit<16> z = y << x;
    -bit<16> w = y << 1024;
    -

    As mentioned above, P4 gives a precise meaning shifting with an amount -larger than the size of the shifted value, unlike C. -

    -

    P4 targets may impose additional restrictions on shift operations such +right-hand-side operand.

    + + +
    +
    +

    Consider the following examples:

    +
    +
    +
    +
    bit<8> x;
    +bit<16> y;
    +bit<16> z = y << x;
    +bit<16> w = y << 1024;
    +
    +
    +
    +

    As mentioned above, P4 gives a precise meaning shifting with an amount +larger than the size of the shifted value, unlike C.

    +
    +
    +

    P4 targets may impose additional restrictions on shift operations such as forbidding shifts by non-constant expressions, or by expressions whose width exceeds a certain bound. For example, a target may forbid shifting an 8-bit value by a non-constant value whose width is greater -than 3 bits. -

    8.10. Operations on variable-size bit types

    -

    To support parsing headers with variable-length fields, P4 offers a -type varbit. Each occurrence of the type varbit has a +than 3 bits.

    +
    +
    +
    +
    +

    8.10. Operations on variable-size bit types

    +
    +

    To support parsing headers with variable-length fields, P4 offers a +type varbit. Each occurrence of the type varbit has a statically-declared maximum width, as well as a dynamic width, which must not exceed the static bound. Prior to initialization a -variable-size bit-string has an unknown dynamic width. -

    -

    Variable-length bit-strings support a limited set of operations: -

    -
      -
    • Assignment to another variable-sized bit-string. The target of the +variable-size bit-string has an unknown dynamic width.

      +
    +
    +

    Variable-length bit-strings support a limited set of operations:

    +
    +
    +
      +
    • +

      Assignment to another variable-sized bit-string. The target of the assignment must have the same static width as the source. When executed, the assignment sets the dynamic width of the target to the -dynamic width of the source. +dynamic width of the source.

    • -
    • Comparison for equality or inequality with another varbit field. -Two varbit fields can be compared only if they have the same type. +
    • +

      Comparison for equality or inequality with another varbit field. +Two varbit fields can be compared only if they have the same type. Two varbits are equal if they have the same dynamic width and all -the bits up to the dynamic width are the same. -

    - -

    The following operations are not supported directly on a value of -type varbit, but instead on any type for which extract and emit +the bits up to the dynamic width are the same.

    + + +
    +
    +

    The following operations are not supported directly on a value of +type varbit, but instead on any type for which extract and emit operations are supported (e.g. a value with type header) that may -contain a field of type varbit. They are mentioned here only to ease -finding this information in a section dedicated to type varbit. -

    -
      -
    • Parser extraction into a header containing a variable-sized -bit-string using the two-argument extract method of a packet_in -extern object (see Section 13.8.2). This -operation sets the dynamic width of the field. -
    • -
    • The emit method of a packet_out extern object can be performed -on a header and a few other types (see Section 16) that -contain a field with type varbit. Such an emit method call +contain a field of type varbit. They are mentioned here only to ease +finding this information in a section dedicated to type varbit.

      +
    +
    +
      +
    • +

      Parser extraction into a header containing a variable-sized +bit-string using the two-argument extract method of a packet_in +extern object (see Section 13.8.2). This +operation sets the dynamic width of the field.

      +
    • +
    • +

      The emit method of a packet_out extern object can be performed +on a header and a few other types (see Chapter 16) that +contain a field with type varbit. Such an emit method call inserts a variable-sized bit-string with a known dynamic width into -the packet being constructed. -

    - -

    Additionally, the maximum size of a variable-length bit-string can be determined at -compile-time (Section 9). -

    8.11. Casts

    -

    P4 provides a limited set of casts between types. A cast is written -(t) e, where t is a type and e is an expression. Casts are only -permitted on base types and derived types introduced by typedef, type, and enum. -While this design is arguably more onerous for programmers, it has several benefits: -

    -
      -
    • It makes user intent unambiguous. -
    • -
    • It makes the costs associated with converting numeric values +the packet being constructed.

      +
    • +
    +
    +
    +

    Additionally, the maximum size of a variable-length bit-string can be determined at compile-time (Chapter 9).

    +
    +
    +
    +

    8.11. Casts

    +
    +

    P4 provides a limited set of casts between types. A cast is written +(t) e, where t is a type and e is an expression. Casts are only +permitted on base types and derived types introduced by typedef, type, and enum. +While this design is arguably more onerous for programmers, it has several benefits:

    +
    +
    +
      +
    • +

      It makes user intent unambiguous.

      +
    • +
    • +

      It makes the costs associated with converting numeric values explicit. Implementing certain casts involves sign extensions, -and thus can require significant computational resources on some targets. +and thus can require significant computational resources on some targets.

    • -
    • It reduces the number of cases that have to be considered in the P4 -specification. Some targets may not support all casts. -
    -

    8.11.1. Explicit casts

    -

    The following casts are legal in P4: -

    -
      -
    • bit<1> bool: converts the value 0 to false, the value 1 to true, and vice versa. +
    • +

      It reduces the number of cases that have to be considered in the P4 +specification. Some targets may not support all casts.

    • -
    • int bool: only if the int value is 0 (converted to false) or 1 (converted to true) +
    +
    +
    +

    8.11.1. Explicit casts

    +
    +

    The following casts are legal in P4:

    +
    +
    +
      +
    • +

      bit<1>bool: converts the value 0 to false, the value 1 to true, and vice versa.

    • -
    • int<W> bit<W>: preserves all bits unchanged and reinterprets negative -values as positive values +
    • +

      intbool: only if the int value is 0 (converted to false) or 1 (converted to true)

    • -
    • bit<W> int<W>: preserves all bits unchanged and reinterprets values whose most-significant bit is 1 as negative values +
    • +

      int<W>bit<W>: preserves all bits unchanged and reinterprets negative +values as positive values

    • -
    • bit<W> bit<X>: truncates the value if W > X, and otherwise (i.e., if W <= X) pads the value with zero bits. +
    • +

      bit<W>int<W>: preserves all bits unchanged and reinterprets values whose most-significant bit is 1 as negative values

    • -
    • int<W> int<X>: truncates the value if W > X, and otherwise (i.e., if W < X) extends it with the sign bit. +
    • +

      bit<W>bit<X>: truncates the value if W > X, and otherwise (i.e., if W <= X) pads the value with zero bits.

    • -
    • bit<W> int: preserves the value unchanged but converts it to an unlimited-precision integer; the result is always non-negative +
    • +

      int<W>int<X>: truncates the value if W > X, and otherwise (i.e., if W < X) extends it with the sign bit.

    • -
    • int<W> int: preserves the value unchanged but converts it to an unlimited-precision integer; the result may be negative +
    • +

      bit<W>int: preserves the value unchanged but converts it to an unlimited-precision integer; the result is always non-negative

    • -
    • int bit<W>: converts the integer value into a sufficiently -large two's complement bit string to avoid information loss, -and then truncates the result to W bits. The compiler should -emit a warning on overflow or on conversion of negative value. +
    • +

      int<W>int: preserves the value unchanged but converts it to an unlimited-precision integer; the result may be negative

    • -
    • int int<W>: converts the integer value into a -sufficiently-large two's complement bit string to avoid information -loss, and then truncates the result to W bits. The compiler should -emit a warning on overflow. +
    • +

      intbit<W>: converts the integer value into a sufficiently +large two’s complement bit string to avoid information loss, +and then truncates the result to W bits. The compiler should +emit a warning on overflow or on conversion of negative value.

    • -
    • casts between two types that are introduced by typedef and are equivalent to one of the above combinations. +
    • +

      intint<W>: converts the integer value into a +sufficiently-large two’s complement bit string to avoid information +loss, and then truncates the result to W bits. The compiler should +emit a warning on overflow.

    • -
    • casts between a typedef and the original type. +
    • +

      casts between two types that are introduced by typedef and are equivalent to one of the above combinations.

    • -
    • casts between a type introduced by type and the original type. +
    • +

      casts between a typedef and the original type.

    • -
    • casts between an enum with an explicit type and its underlying type +
    • +

      casts between a type introduced by type and the original type.

    • -
    • casts of a key-value list to a struct type or a header type (see Section 8.13) +
    • +

      casts between an enum with an explicit type and its underlying type

    • -
    • casts of a tuple expression to a header stack type +
    • +

      casts of a key-value list to a struct type or a header type (see Section 8.13)

    • -
    • casts of an invalid expression {#} to a header or a header union type +
    • +

      casts of a tuple expression to a header stack type

    • -
    • casts where the destination type is the same as the source type +
    • +

      casts of an invalid expression {#} to a header or a header union type

      +
    • +
    • +

      casts where the destination type is the same as the source type if the destination type appears in this list (this excludes -e.g., parsers or externs). -

    -

    8.11.2. Implicit casts

    -

    To keep the language simple and avoid introducing hidden costs, P4 -only implicitly casts from int to fixed-width types and from enums +e.g., parsers or externs).

    + + +
    +
    +
    +

    8.11.2. Implicit casts

    +
    +

    To keep the language simple and avoid introducing hidden costs, P4 +only implicitly casts from int to fixed-width types and from enums with an underlying type to the underlying type. In particular, -applying a binary operation (except shifts and concatenation) to an expression of type int and an -expression with a fixed-width type will implicitly cast the int +applying a binary operation (except shifts and concatenation) to an expression of type int and an +expression with a fixed-width type will implicitly cast the int expression to the type of the other expression. For enums with an underlying type, it can be implicitly cast to its underlying type whenever appropriate, including but not limited to in shifts, concatenation, bit slicing indexes, -header stack indexes as well as other unary and binary operations. -

    -

    For example, given the following declarations, -

    -
    -
    -
    enum bit<8> E {
    -   a = 5
    -}
    -
    -bit<8>  x;
    -bit<16> y;
    -int<8>  z;
    -

    the compiler will add implicit casts as follows: -

    -
      -
    • x + 1 becomes x + (bit<8>)1 -
    • -
    • z < 0 becomes z < (int<8>)0 -
    • -
    • x | 0xFFF becomes x | (bit<8>)0xFFF; overflow warning -
    • -
    • x + E.a becomes x + (bit<8>)E.a -
    • -
    • x &&& 8 becomes x &&& (bit<8>)8 -
    • -
    • x << 256 remains unchanged; 256 not implicitly cast to 8w0 in a shift; overflow warning -
    • -
    • 16w11 << E.a becomes 16w11 << (bit<8>)E.a -
    • -
    • x[E.a:0] becomes x[(bit<8>)E.a:0] -
    • -
    • E.a ++ 8w0 becomes (bit<8>)E.a ++ 8w0 -
    - -

    The compiler also adds implicit casts when types of different -expressions need to match; for example, as described in Section -13.6, since select labels are compared against the selected +header stack indexes as well as other unary and binary operations.

    +
    +
    +

    For example, given the following declarations,

    +
    +
    +
    +
    enum bit<8> E {
    +   a = 5
    +}
    +
    +bit<8>  x;
    +bit<16> y;
    +int<8>  z;
    +
    +
    +
    +

    the compiler will add implicit casts as follows:

    +
    +
    +
      +
    • +

      x + 1 becomes x + (bit<8>)1

      +
    • +
    • +

      z < 0 becomes z < (int<8>)0

      +
    • +
    • +

      x | 0xFFF becomes x | (bit<8>)0xFFF; overflow warning

      +
    • +
    • +

      x + E.a becomes x + (bit<8>)E.a

      +
    • +
    • +

      x &&& 8 becomes x &&& (bit<8>)8

      +
    • +
    • +

      x << 256 remains unchanged; 256 not implicitly cast to 8w0 in a shift; overflow warning

      +
    • +
    • +

      16w11 << E.a becomes 16w11 << (bit<8>)E.a

      +
    • +
    • +

      x[E.a:0] becomes x[(bit<8>)E.a:0]

      +
    • +
    • +

      E.a ++ 8w0 becomes (bit<8>)E.a ++ 8w0

      +
    • +
    +
    +
    +

    The compiler also adds implicit casts when types of different +expressions need to match; for example, as described in Section +Section 13.6, since select labels are compared against the selected expression, the compiler will insert implicit casts for the select -labels when they have int types. Similarly, when assigning a +labels when they have int types. Similarly, when assigning a structure-valued expression to a structure or header, the compiler will -add implicit casts for int fields. -

    8.11.3. Illegal arithmetic expressions

    -

    Many arithmetic expressions that would be allowed in other languages -are illegal in P4. To illustrate, consider the following declarations: -

    -
    -
    -
    bit<8>  x;
    -bit<16> y;
    -int<8>  z;
    -

    The table below shows several expressions which are illegal because +add implicit casts for int fields.

    +
    +
    +
    +

    8.11.3. Illegal arithmetic expressions

    +
    +

    Many arithmetic expressions that would be allowed in other languages +are illegal in P4. To illustrate, consider the following declarations:

    +
    +
    +
    +
    bit<8>  x;
    +bit<16> y;
    +int<8>  z;
    +
    +
    +
    +

    The table below shows several expressions which are illegal because they do not obey the P4 typing rules. For each expression we provide several ways that the expression could be manually rewritten into a legal expression. Note that for some expression there are several legal alternatives, which may produce different results! The compiler -cannot guess the user intent, so P4 requires the user to disambiguate. -

    - - - - - - - - - - - - - - - - - - - - -
    Expression Why it is illegal Alternatives
    x + y Different widths (bit<16>)x + y
    x + (bit<8>)y
    x + z Different signedness (int<8>)x + z
    x + (bit<8>)z
    (int<8>)y Cannot change both sign and width (int<8>)(bit<8>)y
    (int<8>)(int<16>)y
    y + z Different widths and signs (int<8>)(bit<8>)y + z
    y + (bit<16>)(bit<8>)z
    (bit<8>)y + (bit<8>)z
    (int<16>)y + (int<16>)z
    x << z RHS of shift cannot be signed x << (bit<8>)z
    x < z Different signs x < (bit<8>)z
    (int<8>)x < z
    1 << x Either LHS should have a fixed width (bit shift), 32w1 << x
    Or RHS must be compile-time known (int shift) None
    ~1 Bitwise operation on int ~32w1
    5 & -3 Bitwise operation on int 32w5 & -3

    8.12. Operations on tuple expressions

    -

    Tuples can be assigned to other tuples with the same type, passed as +cannot guess the user intent, so P4 requires the user to disambiguate.

    +
    + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ExpressionWhy it is illegalAlternatives

    x + y

    Different widths

    (bit<16>)x + y

    x + (bit<8>)y

    x + z

    Different signedness

    (int<8>)x + z

    x + (bit<8>)z

    (int<8>)y

    Cannot change both sign and width

    (int<8>)(bit<8>)y

    (int<8>)(int<16>)y

    y + z

    Different widths and signs

    (int<8>)(bit<8>)y + z

    y + (bit<16>)(bit<8>)z

    (bit<8>)y + (bit<8>)z

    (int<16>)y + (int<16>)z

    x << z

    RHS of shift cannot be signed

    x << (bit<8>)z

    x < z

    Different signs

    x < (bit<8>)z

    (int<8>)x < z

    1 << x

    Either LHS should have a fixed width (bit shift),

    32w1 << x

    Or RHS must be compile-time known (int shift)

    None

    ~1

    Bitwise operation on int

    ~32w1

    5 & -3

    Bitwise operation on int

    32w5 & -3

    +
    +
    +
    +

    8.12. Operations on tuple expressions

    +
    +

    Tuples can be assigned to other tuples with the same type, passed as arguments and returned from functions, and can be initialized with -tuple expressions. -

    -
    -
    -
    tuple<bit<32>, bool> x = { 10, false };
    -

    The fields of a tuple can be accessed using array index syntax x[0], -x[1]. The indexes must be local compile-time known values, to enable -the type-checker to identify the field types statically. -

    -

    Tuples can be compared for equality using == and !=; two tuples are -equal if and only if all their fields are respectively equal. -

    -

    Currently tuple fields are not left-values, even if the tuple itself +tuple expressions.

    +
    +
    +
    +
    tuple<bit<32>, bool> x = { 10, false };
    +
    +
    +
    +

    The fields of a tuple can be accessed using array index syntax x[0], +x[1]. The indexes must be local compile-time known values, to enable +the type-checker to identify the field types statically.

    +
    +
    +

    Tuples can be compared for equality using == and !=; two tuples are +equal if and only if all their fields are respectively equal.

    +
    +
    +

    Currently tuple fields are not left-values, even if the tuple itself is. (I.e. a tuple can only be assigned monolithically, and the field values cannot be changed individually.) This restriction may be -lifted in a future version of the language. -

    -

    A tuple expression is written using curly braces, with each element -separated by a comma: -

    -
    -
    -
    expression ...
    -    | '{' expressionList '}'
    +lifted in a future version of the language.

    +
    +
    +

    A tuple expression is written using curly braces, with each element +separated by a comma:

    +
    +
    +
    +
    expression ...
    +    | '{' expressionList '}'
     
     expressionList
    -    : /* empty */
    +    : /* empty */
         | expression
    -    | expressionList "," expression
    -    ;
    -

    The type of a tuple expression is a tuple type (Section -7.2.6). Tuple expressions can be assigned to expressions -of type tuple, struct or header, and can also be + | expressionList "," expression + ; +

    +
    +
    +

    The type of a tuple expression is a tuple type (Section 7.2.6). +Tuple expressions can be assigned to expressions +of type tuple, struct or header, and can also be passed as arguments to methods. Tuples may be nested. However, tuple -expressions are not l-values. -

    -

    For example, the following program fragment uses a tuple expression to -pass several header fields simultaneously to a learning provider: -

    -
    -
    -
    extern LearningProvider<T> {
    -    LearningProvider();
    -    void learn(in T data);
    -}
    -
    -LearningProvider<tuple<bit<48>, bit<32>>>() lp;
    -
    -lp.learn( { hdr.ethernet.srcAddr, hdr.ipv4.src } );
    -

    A tuple may be used to initialize a structure if the tuple has the same +expressions are not l-values.

    +
    +
    +

    For example, the following program fragment uses a tuple expression to +pass several header fields simultaneously to a learning provider:

    +
    +
    +
    +
    extern LearningProvider<T> {
    +    LearningProvider();
    +    void learn(in T data);
    +}
    +
    +LearningProvider<tuple<bit<48>, bit<32>>>() lp;
    +
    +lp.learn( { hdr.ethernet.srcAddr, hdr.ipv4.src } );
    +
    +
    +
    +

    A tuple may be used to initialize a structure if the tuple has the same number of elements as fields in the structure. The effect of such an -initializer is to assign the nth element of the tuple to the nth -field in the structure: -

    -
    -
    -
    struct S {
    -    bit<32> a;
    -    bit<32> b;
    -}
    -const S x = { 10, 20 }; //a = 10, b = 20
    -

    A tuple expression can have an explicit structure or header type +initializer is to assign the nth element of the tuple to the nth +field in the structure:

    +
    +
    +
    +
    struct S {
    +    bit<32> a;
    +    bit<32> b;
    +}
    +const S x = { 10, 20 }; //a = 10, b = 20
    +
    +
    +
    +

    A tuple expression can have an explicit structure or header type specified, and then it is converted automatically to a -structure-valued expression (see 8.13): -

    -
    -
    -
    struct S {
    -    bit<32> a;
    -    bit<32> b;
    -}
    -
    -extern void f<T>(in T data);
    -
    -f((S){ 10, 20 }); // automatically converted to f((S){a = 10, b = 20});
    -

    Tuple expressions can also be used to initialize variables whose type -is a tuple type. -

    -
    -
    -
    tuple<bit<32>, bool> x = { 10, false };
    -

    The empty tuple expression has type tuple<> - a tuple with no components. -

    8.13. Operations on structure-valued expressions

    -

    One can write expressions that evaluate to a structure or header. The -syntax of these expressions is given by: -

    -
    -
    -
    expression ...
    -    | '{' kvList '}'
    -    | '(' typeRef ')' expression
    +structure-valued expression (see Section 8.13):

    +
    +
    +
    +
    struct S {
    +    bit<32> a;
    +    bit<32> b;
    +}
    +
    +extern void f<T>(in T data);
    +
    +f((S){ 10, 20 }); // automatically converted to f((S){a = 10, b = 20});
    +
    +
    +
    +

    Tuple expressions can also be used to initialize variables whose type +is a tuple type.

    +
    +
    +
    +
    tuple<bit<32>, bool> x = { 10, false };
    +
    +
    +
    +

    The empty tuple expression has type tuple<> - a tuple with no components.

    +
    +
    +
    +

    8.13. Operations on structure-valued expressions

    +
    +

    One can write expressions that evaluate to a structure or header. The +syntax of these expressions is given by:

    +
    +
    +
    +
    expression ...
    +    | '{' kvList '}'
    +    | '(' typeRef ')' expression
         ;
     
     kvList
         : kvPair
    -    | kvList "," kvPair
    +    | kvList "," kvPair
         ;
     
     kvPair
    -    : name "=" expression
    -    ;
    -

    For a structure-valued expression typeRef is the name of a struct -or header type. The typeRef can be omitted if it can be inferred -from context, e.g., when initializing a variable with a struct type. -Structure-valued expressions that evaluate to a value of some header -type are always valid. -

    -

    The following example shows a structure-valued expression used in an -equality comparison expression: -

    -
    -
    -
    struct S {
    -    bit<32> a;
    -    bit<32> b;
    -}
    -
    -S s;
    -
    -// Compare s with a structure-valued expression
    -bool b = s == (S) { a = 1, b = 2 };
    -

    Structure-valued expressions can be used in the right-hand side of + : name "=" expression + ; +

    +
    +
    +

    For a structure-valued expression typeRef is the name of a struct +or header type. The typeRef can be omitted if it can be inferred +from context, e.g., when initializing a variable with a struct type. +Structure-valued expressions that evaluate to a value of some header +type are always valid.

    +
    +
    +

    The following example shows a structure-valued expression used in an +equality comparison expression:

    +
    +
    +
    +
    struct S {
    +    bit<32> a;
    +    bit<32> b;
    +}
    +
    +S s;
    +
    +// Compare s with a structure-valued expression
    +bool b = s == (S) { a = 1, b = 2 };
    +
    +
    +
    +

    Structure-valued expressions can be used in the right-hand side of assignments, in comparisons, in field selection expressions, and as arguments to functions, method or actions. Structure-valued -expressions are not left values. -

    -

    Structure-valued expressions that do not have ... as their last +expressions are not left values.

    +
    +
    +

    Structure-valued expressions that do not have ... as their last element must provide a value for every member of the struct or header type to which it evaluates, by mentioning each field name exactly -once. -

    -

    Structure-valued expressions that have ... as their last element are +once.

    +
    +
    +

    Structure-valued expressions that have ... as their last element are allowed to give values to only a subset of the fields of the struct or header type to which it evaluates. Any field names not given a value -explicitly will be given their default value (see Section -8.26). -

    -

    The order of the fields of the struct or header type does not need -to match the order of the values of the structure-valued expression. -

    -

    It is a compile-time error if a field name appears more than once in -the same structure-valued expression. -

    8.14. Operations on lists

    -

    The value of a list is written using curly braces, with each element +explicitly will be given their default value (see Section 8.26).

    +
    +
    +

    The order of the fields of the struct or header type does not need +to match the order of the values of the structure-valued expression.

    +
    +
    +

    It is a compile-time error if a field name appears more than once in +the same structure-valued expression.

    +
    +
    +
    +

    8.14. Operations on lists

    +
    +

    The value of a list is written using curly braces, with each element separated by a comma. The left curly brace is preceded by a -(list<T>) where T is the list element type. Such a value can be -passed as an argument, e.g. to extern constructor functions. -

    -
    -
    -
    struct pair_t {
    -    bit<16> a;
    -    bit<32> b;
    -}
    -
    -extern E {
    -    E(list<pair_t> data);
    -    void run();
    -}
    -
    -control c() {
    -    E((list<pair_t>) {{2, 3}, {4, 5}}) e;
    -    apply {
    -        e.run();
    -    }
    -}
    -

    Additionally, the size of a list can be determined at -compile-time (Section 9). -

    8.15. Operations on sets

    -

    Some P4 expressions denote sets of values (set<T>, for some type T; -see Section 7.2.9.1). These expressions can -appear only in a few contextsparsers and table -entries. For example, the select expression (Section -13.6) has the following structure: -

    -
    -
    -
    select (expression) {
    -   set1: state1;
    -   set2: state2;
    -   // More labels omitted
    -}
    -

    Here the expressions set1, set2, etc. evaluate to sets of values -and the select expression tests whether expression belongs to -the sets used as labels. -

    -
    -
    -
    keysetExpression
    +(list<T>) where T is the list element type.  Such a value can be
    +passed as an argument, e.g. to extern constructor functions.

    +
    +
    +
    +
    struct pair_t {
    +    bit<16> a;
    +    bit<32> b;
    +}
    +
    +extern E {
    +    E(list<pair_t> data);
    +    void run();
    +}
    +
    +control c() {
    +    E((list<pair_t>) {{2, 3}, {4, 5}}) e;
    +    apply {
    +        e.run();
    +    }
    +}
    +
    +
    +
    +

    Additionally, the size of a list can be determined at +compile-time (Chapter 9).

    +
    +
    +
    +

    8.15. Operations on sets

    +
    +

    Some P4 expressions denote sets of values (set<T>, for some type T; +see Section 7.2.9.1). These expressions can +appear only in a few contexts---parsers and table +entries. For example, the select expression +(Section 13.6) has the following structure:

    +
    +
    +
    +
    select (expression) {
    +   set1: state1;
    +   set2: state2;
    +   // More labels omitted
    +}
    +
    +
    +
    +

    Here the expressions set1, set2, etc. evaluate to sets of values +and the select expression tests whether expression belongs to +the sets used as labels.

    +
    +
    +
    +
    keysetExpression
         : tupleKeysetExpression
         | simpleKeysetExpression
         ;
     
     tupleKeysetExpression
    -    : "(" simpleKeysetExpression "," simpleExpressionList ")"
    -    | "(" reducedSimpleKeysetExpression ")"
    +    : "(" simpleKeysetExpression "," simpleExpressionList ")"
    +    | "(" reducedSimpleKeysetExpression ")"
         ;
     
     simpleExpressionList
         : simpleKeysetExpression
    -    | simpleExpressionList "," simpleKeysetExpression
    +    | simpleExpressionList "," simpleKeysetExpression
         ;
     
     reducedSimpleKeysetExpression
    -    : expression "&&&" expression
    -    | expression ".." expression
    +    : expression "&&&" expression
    +    | expression ".." expression
         | DEFAULT
    -    | "_"
    +    | "_"
         ;
     
     simpleKeysetExpression
         : expression
    -    | expression "&&&" expression
    -    | expression ".." expression
    +    | expression "&&&" expression
    +    | expression ".." expression
         | DEFAULT
    -    | "_"
    -    ;
    -

    The mask (&&&) and range (..) operators have the same -precedence; the just above the ?: operator. -

    8.15.1. Singleton sets

    -

    In a set context, expressions denote singleton sets. For example, in -the following program fragment, -

    -
    -
    -
    select (hdr.ipv4.version) {
    -   4: continue;
    -}
    -

    The label 4 denotes the singleton set containing the int value 4. -

    8.15.2. The universal set

    -

    In a set context, the expressions default or _ denote the -universal set, which contains all possible values of a given type: -

    -
    -
    -
    select (hdr.ipv4.version) {
    -   4: continue;
    -   _: reject;
    -}

    8.15.3. Masks

    -

    The infix operator &&& takes two arguments of the same numeric type (Section 7.4), + | "_" + ; +

    +
    +
    +

    The mask (&&&) and range (..) operators have the same +precedence; the just above the ?: operator.

    +
    +
    +

    8.15.1. Singleton sets

    +
    +

    In a set context, expressions denote singleton sets. For example, in +the following program fragment,

    +
    +
    +
    +
    select (hdr.ipv4.version) {
    +   4: continue;
    +}
    +
    +
    +
    +

    The label 4 denotes the singleton set containing the int value 4.

    +
    +
    +
    +

    8.15.2. The universal set

    +
    +

    In a set context, the expressions default or _ denote the +universal set, which contains all possible values of a given type:

    +
    +
    +
    +
    select (hdr.ipv4.version) {
    +   4: continue;
    +   _: reject;
    +}
    +
    +
    +
    +
    +

    8.15.3. Masks

    +
    +

    The infix operator &&& takes two arguments of the same numeric type (Section 7.4), and creates a value of the same type. The right value is used as a -“mask”, where each bit set to 0 in the mask indicates a “don't care” -bit. More formally, the set denoted by a &&& b is defined as -follows: -

    -
    -
    -
    a &&& b = { c where a & b = c & b }
    -

    For example: -

    -
    -
    -
    8w0x0A &&& 8w0x0F
    -

    denotes a set that contains 16 different bit<8> values, whose -bit-pattern is XXXX1010, where the value of an X can be +"mask", where each bit set to 0 in the mask indicates a "don’t care" +bit. More formally, the set denoted by a &&& b is defined as +follows:

    +
    +
    +
    +
    a &&& b = { c where a & b = c & b }
    +
    +
    +
    +

    For example:

    +
    +
    +
    +
    8w0x0A &&& 8w0x0F
    +
    +
    +
    +

    denotes a set that contains 16 different bit<8> values, whose +bit-pattern is XXXX1010, where the value of an X can be any bit. Note that there may be multiple ways to express a keyset -using a mask operatore.g., 8w0xFA &&& 8w0x0F denotes the same -keyset as in the example above. -

    -

    Similar to other binary operations, +using a mask operator---e.g., 8w0xFA &&& 8w0x0F denotes the same +keyset as in the example above.

    +
    +
    +

    Similar to other binary operations, the mask operator allows the compiler to automatically insert casts -to unify the argument types in certain situations (section 8.11.2). -

    -

    P4 architectures may impose additional restrictions on the expressions +to unify the argument types in certain situations (Section 8.11.2).

    +
    +
    +

    P4 architectures may impose additional restrictions on the expressions on the left and right-hand side of a mask operator: for example, they may require that either or both sub-expressions be compile-time known -values. -

    8.15.4. Ranges

    -

    The infix operator .. takes two arguments of the same numeric type T (Section 7.4), +values.

    +
    +
    +
    +

    8.15.4. Ranges

    +
    +

    The infix operator .. takes two arguments of the same numeric type T (Section 7.4), and creates a value of -the type set<T>. The set contains all values numerically between the -first and the second, inclusively. For example: -

    -
    -
    -
    4s5 .. 4s8
    -

    denotes a set of 4 consecutive int<4> values 4s5, 4s6, 4s7, and 4s8. -

    -

    Similar to other binary operations, +the type set<T>. The set contains all values numerically between the +first and the second, inclusively. For example:

    +
    +
    +
    +
    4s5 .. 4s8
    +
    +
    +
    +

    denotes a set of 4 consecutive int<4> values 4s5, 4s6, 4s7, and 4s8.

    +
    +
    +

    Similar to other binary operations, the range operator allows the compiler to automatically insert casts -to unify the argument types in certain situations (section 8.11.2). -

    -

    A range where the second value is smaller than the first one -represents an empty set. -

    8.15.5. Products

    -

    Multiple sets can be combined using Cartesian product: -

    -
    -
    -
    select(hdr.ipv4.ihl, hdr.ipv4.protocol) {
    -     (4w0x5, 8w0x1): parse_icmp;
    -     (4w0x5, 8w0x6): parse_tcp;
    -     (4w0x5, 8w0x11): parse_udp;
    -     (_, _): accept; }
    -

    The type of a product of sets is a set of tuples. -

    8.16. Operations on struct types

    -

    The only operation defined on expressions whose type is a struct is -field access, written using dot (“.”) notatione.g., s.field. If -s is an l-value, then s.field is also an l-value. P4 also allows -copying structs using assignment when the source and target of the -assignment have the same type. Finally, structs can be initialized -with a tuple expression, as discussed in Section 8.12, or +to unify the argument types in certain situations (Section 8.11.2).

    +
    +
    +

    A range where the second value is smaller than the first one +represents an empty set.

    +
    +
    +
    +

    8.15.5. Products

    +
    +

    Multiple sets can be combined using Cartesian product:

    +
    +
    +
    +
    select(hdr.ipv4.ihl, hdr.ipv4.protocol) {
    +     (4w0x5, 8w0x1): parse_icmp;
    +     (4w0x5, 8w0x6): parse_tcp;
    +     (4w0x5, 8w0x11): parse_udp;
    +     (_, _): accept; }
    +
    +
    +
    +

    The type of a product of sets is a set of tuples.

    +
    +
    +
    +
    +

    8.16. Operations on struct types

    +
    +

    The only operation defined on expressions whose type is a struct is +field access, written using dot (".") notation---e.g., s.field. If +s is an l-value, then s.field is also an l-value. P4 also allows +copying structs using assignment when the source and target of the +assignment have the same type. Finally, structs can be initialized +with a tuple expression, as discussed in Section 8.12, or with a structure-valued expression, as described in -8.13. Both of these cases must initialize all +Section 8.13. Both of these cases must initialize all fields of the structure. The size of a struct can be determined at -compile-time (Section 9). -

    -

    Two structs can be compared for equality (==) or inequality (!=) only +compile-time (Chapter 9).

    +
    +
    +

    Two structs can be compared for equality (==) or inequality (!=) only if they have the same type and all of their fields can be recursively compared for equality. Two structures are equal if and only if all -their corresponding fields are equal. -

    -

    The following example shows a structure initialized in several -different ways: -

    -
    -
    -
    struct S {
    -    bit<32> a;
    -    bit<32> b;
    -}
    -const S x = { 10, 20 };             // tuple expression
    -const S x = { a = 10, b = 20 };     // structure-valued expression
    -const S x = (S) { a = 10, b = 20 }; // structure-valued expression
    -

    See Section 8.25 -for a description of the behavior if struct fields are read without -being initialized. -

    8.17. Operations on headers

    -

    Headers provide the same operations as structs. Assignment between -headers also copies the “validity” header bit. -

    -

    In addition, headers support the following methods: -

    -
      -
    • The method isValid() returns the value of the “validity” bit -of the header. -
    • -
    • The method setValid() sets the header's validity bit to -“true”. It can only be applied to an l-value. -
    • -
    • The method setInvalid() sets the header's validity bit to -“false”. It can only be applied to an l-value. -
    - -

    Similar to a struct, a header object can be initialized with a tuple -expression (see Section 8.12) the tuple fields are assigned to the -header fields in the order they appear or with a structure-valued -expression (see Section 8.16). When initialized the header -automatically becomes valid: -

    -
    -
    -
    header H { bit<32> x; bit<32> y; }
    -H h;
    -h = { 10, 12 };  // This also makes the header h valid
    -h = { y = 12, x = 10 };  // Same effect as above
    -

    Two headers can be compared for equality (==) or inequality (!=) only +their corresponding fields are equal.

    +
    +
    +

    The following example shows a structure initialized in several +different ways:

    +
    +
    +
    +
    struct S {
    +    bit<32> a;
    +    bit<32> b;
    +}
    +const S x = { 10, 20 };             // tuple expression
    +const S x = { a = 10, b = 20 };     // structure-valued expression
    +const S x = (S) { a = 10, b = 20 }; // structure-valued expression
    +
    +
    +
    +

    See Section 8.25 +for a description of the behavior if struct fields are read without +being initialized.

    +
    +
    +
    +

    8.17. Operations on headers

    +
    +

    Headers provide the same operations as structs. Assignment between +headers also copies the "validity" header bit.

    +
    +
    +

    In addition, headers support the following methods:

    +
    +
    +
      +
    • +

      The method isValid() returns the value of the "validity" bit +of the header.

      +
    • +
    • +

      The method setValid() sets the header’s validity bit to +"true". It can only be applied to an l-value.

      +
    • +
    • +

      The method setInvalid() sets the header’s validity bit to +"false". It can only be applied to an l-value.

      +
    • +
    +
    +
    +

    Similar to a struct, a header object can be initialized with a tuple +expression (see Section 8.12) --- the tuple fields are assigned to the +header fields in the order they appear --- or with a structure-valued +expression (see Section 8.16). When initialized the header +automatically becomes valid:

    +
    +
    +
    +
    header H { bit<32> x; bit<32> y; }
    +H h;
    +h = { 10, 12 };  // This also makes the header h valid
    +h = { y = 12, x = 10 };  // Same effect as above
    +
    +
    +
    +

    Two headers can be compared for equality (==) or inequality (!=) only if they have the same type. Two headers are equal if and only if they are both invalid, or they are both valid and all their corresponding fields are equal. Furthermore, the size of a header can be determined at -compile-time (Section 9). -

    -

    The expression {#} represents an invalid header of some type, but it +compile-time (Chapter 9).

    +
    +
    +

    The expression {#} represents an invalid header of some type, but it can be any header or header union type. A P4 compiler may require an explicit cast on this expression in cases where it cannot determine -the particular header or header union type from the context. -

    -
    -
    -
    expression
    +the particular header or header union type from the context.

    +
    +
    +
    +
    expression
         ...
    -    | "{#}"
    -

    For example: -

    -
    -
    -
    header H { bit<32> x; bit<32> y; }
    -H h;
    -h = {#};   // This make the header h become invalid
    -if (h == {#}) {     // This is equivalent to the condition !h.isValid()
    -    // ...
    -}
    -

    Note that the # character cannot be misinterpreted as a preprocessor + | "{#}" +

    +
    +
    +

    For example:

    +
    +
    +
    +
    header H { bit<32> x; bit<32> y; }
    +H h;
    +h = {#};   // This make the header h become invalid
    +if (h == {#}) {     // This is equivalent to the condition !h.isValid()
    +    // ...
    +}
    +
    +
    +
    +

    Note that the # character cannot be misinterpreted as a preprocessor directive, since it cannot be the first character on a line when it -occurs in the single lexical token {#}, which may not have -whitespace or any other characters between those shown. -

    -

    See Section 8.25 +occurs in the single lexical token {#}, which may not have +whitespace or any other characters between those shown.

    +
    +
    +

    See Section 8.25 for a description of the behavior if header fields are read without being initialized, or header fields are written to a currently invalid -header. -

    8.18. Operations on header stacks

    -

    A header stack is a fixed-size array of headers with the same +header.

    +
    +
    +
    +

    8.18. Operations on header stacks

    +
    +

    A header stack is a fixed-size array of headers with the same type. The valid elements of a header stack need not be contiguous. P4 provides a set of computations for manipulating header stacks. A -header stack hs of type h[n] can be understood in terms of -the following pseudocode: -

    -
    -
    -
    // type declaration
    -struct hs_t {
    -  bit<32> nextIndex;
    -  bit<32> size;
    -  h[n] data;  // Ordinary array
    +header stack hs of type h[n] can be understood in terms of
    +the following pseudocode:

    +
    +
    +
    +
    // type declaration
    +struct hs_t {
    +  bit<32> nextIndex;
    +  bit<32> size;
    +  h[n] data;  // Ordinary array
     }
     
    -// instance declaration and initialization
    +// instance declaration and initialization
     hs_t hs;
    -hs.nextIndex = 0;
    -hs.size = n;
    -

    Intuitively, a header stack can be thought of as a struct containing -an ordinary array of headers hs and a counter nextIndex +hs.nextIndex = 0; +hs.size = n; +

    +
    +
    +

    Intuitively, a header stack can be thought of as a struct containing +an ordinary array of headers hs and a counter nextIndex that can be used to simplify the construction of parsers for header -stacks, as discussed below. The nextIndex counter is initialized -to 0. -

    -

    Given a header stack value hs of size n, the following -expressions are legal: -

    -
      -
    • -

      hs[index]: produces a reference to the header at the -specified position within the stack; if hs is an l-value, -the result is also an l-value. The header may be invalid. Some implementations -may impose the constraint that the index expression must be a compile-time known value. -A P4 compiler must give an error if an index that is a compile-time known value is out of range. -

      -

      Accessing a header stack hs with an index less than 0 or -greater than or equal to hs.size results in an undefined value. See Section -8.25 for more -details. -

      -

      The index is an expression that must be of numeric types (Section 7.4). -

    • -
    • -

      hs.size: produces a 32-bit unsigned integer that returns the -size of the header stack (a local compile-time known value). -

    • -
    • -

      assignment from a header stack hs into another stack requires -the stacks to have the same types and sizes. All components of hs +stacks, as discussed below. The nextIndex counter is initialized +to 0.

      +
    +
    +

    Given a header stack value hs of size n, the following +expressions are legal:

    +
    +
    +
      +
    • +

      hs[index]: produces a reference to the header at the +specified position within the stack; if hs is an l-value, +the result is also an l-value. The header may be invalid. Some implementations may impose the constraint that the index expression must be a compile-time known value. +A P4 compiler must give an error if an index that is a compile-time known value is out of range.

      +
      +

      Accessing a header stack hs with an index less than 0 or +greater than or equal to hs.size results in an undefined value. See Section 8.25 for more +details.

      +
      +
      +

      The index is an expression that must be of numeric types (Section 7.4).

      +
      +
    • +
    • +

      hs.size: produces a 32-bit unsigned integer that returns the +size of the header stack (a local compile-time known value).

      +
    • +
    • +

      assignment from a header stack hs into another stack requires +the stacks to have the same types and sizes. All components of hs are copied, including its elements and their validity bits, -as well as nextIndex. -

    - -

    To help programmers write parsers for header stacks, P4 also offers +as well as nextIndex.

    + + +
    +
    +

    To help programmers write parsers for header stacks, P4 also offers computations that automatically advance through the stack as elements -are parsed: -

    -
      -
    • -

      hs.next: produces a reference to the element with index hs.nextIndex -in the stack. May only be used in a parser. If the stack's nextIndex -counter is greater than or equal to size, then evaluating this -expression results in a transition to reject and -sets the error to error.StackOutOfBounds. If hs is an -l-value, then hs.next is also an l-value. -

    • -
    • -

      hs.last: produces a reference to the element with index hs.nextIndex - 1 -in the stack, if such an element exists. May only be used in a parser. -If the nextIndex counter is less than 1, -or greater than size, then evaluating this expression results -in a transition to reject and sets the error to error.StackOutOfBounds. -Unlike hs.next, the resulting reference is never an l-value. -

    • -
    • -

      hs.lastIndex: produces a 32-bit unsigned integer that encodes the index hs.nextIndex - 1. -May only be used in a parser. If the nextIndex counter is 0, then -evaluating this expression produces an undefined value. -

    - -

    Finally, P4 offers the following computations that can be used to -manipulate the elements at the front and back of the stack: -

    -
      -
    • -

      hs.push_front(int count): shifts hs “right” by count. The -first count elements become invalid. The last count elements in -the stack are discarded. The hs.nextIndex counter is incremented -by count. The count argument must be a compile-time known value -that is a positive integer. The return type is void. -

    • -
    • -

      hs.pop_front(int count): shifts hs “left” by count -(i.e., element with index count is copied in stack at index 0). -The last count elements become invalid. The hs.nextIndex -counter is decremented by count. The count argument must +are parsed:

      +
    +
    +
      +
    • +

      hs.next: produces a reference to the element with index hs.nextIndex +in the stack. May only be used in a parser. If the stack’s nextIndex +counter is greater than or equal to size, then evaluating this +expression results in a transition to reject and +sets the error to error.StackOutOfBounds. If hs is an +l-value, then hs.next is also an l-value.

      +
    • +
    • +

      hs.last: produces a reference to the element with index hs.nextIndex - 1 +in the stack, if such an element exists. May only be used in a parser. +If the nextIndex counter is less than 1, +or greater than size, then evaluating this expression results +in a transition to reject and sets the error to error.StackOutOfBounds. +Unlike hs.next, the resulting reference is never an l-value.

      +
    • +
    • +

      hs.lastIndex: produces a 32-bit unsigned integer that encodes the index hs.nextIndex - 1. +May only be used in a parser. If the nextIndex counter is 0, then +evaluating this expression produces an undefined value.

      +
    • +
    +
    +
    +

    Finally, P4 offers the following computations that can be used to +manipulate the elements at the front and back of the stack:

    +
    +
    +
      +
    • +

      hs.push_front(int count): shifts hs "right" by count. The +first count elements become invalid. The last count elements in +the stack are discarded. The hs.nextIndex counter is incremented +by count. The count argument must be a compile-time known value +that is a positive integer. The return type is void.

      +
    • +
    • +

      hs.pop_front(int count): shifts hs "left" by count +(i.e., element with index count is copied in stack at index 0). +The last count elements become invalid. The hs.nextIndex +counter is decremented by count. The count argument must be a compile-time known value that is a positive integer. The return -type is void. -

    - -

    The following pseudocode defines the behavior of push_front and pop_front: -

    -
    -
    -
    void push_front(int count) {
    -    for (int i = this.size-1; i >= 0; i -= 1) {
    -        if (i >= count) {
    -            this[i] = this[i-count];
    -        } else {
    -            this[i].setInvalid();
    +type is void.

    + + +
    +
    +

    The following pseudocode defines the behavior of push_front and pop_front:

    +
    +
    +
    +
    void push_front(int count) {
    +    for (int i = this.size-1; i >= 0; i -= 1) {
    +        if (i >= count) {
    +            this[i] = this[i-count];
    +        } else {
    +            this[i].setInvalid();
             }
         }
    -    this.nextIndex = this.nextIndex + count;
    -    if (this.nextIndex > this.size) this.nextIndex = this.size;
    -    // Note: this.last, this.next, and this.lastIndex adjust with this.nextIndex
    +    this.nextIndex = this.nextIndex + count;
    +    if (this.nextIndex > this.size) this.nextIndex = this.size;
    +    // Note: this.last, this.next, and this.lastIndex adjust with this.nextIndex
     }
     
    -void pop_front(int count) {
    -    for (int i = 0; i < this.size; i++) {
    -        if (i+count < this.size) {
    -            this[i] = this[i+count];
    -        } else {
    -            this[i].setInvalid();
    +void pop_front(int count) {
    +    for (int i = 0; i < this.size; i++) {
    +        if (i+count < this.size) {
    +            this[i] = this[i+count];
    +        } else {
    +            this[i].setInvalid();
             }
         }
    -    if (this.nextIndex >= count) {
    -        this.nextIndex = this.nextIndex - count;
    -    } else {
    -        this.nextIndex = 0;
    +    if (this.nextIndex >= count) {
    +        this.nextIndex = this.nextIndex - count;
    +    } else {
    +        this.nextIndex = 0;
         }
    -    // Note: this.last, this.next, and this.lastIndex adjust with this.nextIndex
    -}
    -

    Similar to structs and headers, the size of a header stack is a compile-time known value -(Section 9). -

    -

    Two header stacks can be compared for equality (==) or inequality (!=) + // Note: this.last, this.next, and this.lastIndex adjust with this.nextIndex +} +

    +
    +
    +

    Similar to structs and headers, the size of a header stack is a compile-time known value (Section Chapter 9).

    +
    +
    +

    Two header stacks can be compared for equality (==) or inequality (!=) only if they have the same element type and the same length. Two stacks are equal if and only if all their corresponding elements are -equal. Note that the nextIndex value is not used in the equality comparison. -

    8.18.1. Header stack expressions

    -

    One can write expressions that evaluate to a header stack. The -syntax of these expressions is given by: -

    -
    -
    -
    expression ...
    -    | '{' expressionList '}'
    -    | '(' typeRef ')' expression
    -    ;
    -

    The typeRef is a header stack type. The typeRef can be omitted if +equal. Note that the nextIndex value is not used in the equality comparison.

    +
    +
    +

    8.18.1. Header stack expressions

    +
    +

    One can write expressions that evaluate to a header stack. The +syntax of these expressions is given by:

    +
    +
    +
    +
    expression ...
    +    | '{' expressionList '}'
    +    | '(' typeRef ')' expression
    +    ;
    +
    +
    +
    +

    The typeRef is a header stack type. The typeRef can be omitted if it can be inferred from context, e.g., when initializing a variable with a header stack type. Each expression in the list must evaluate -to a header of the same type as the other stack elements. -

    -

    Here is an example: -

    -
    -
    -
    header H<T> {
    -    bit<32> b;
    -    T t;
    -}
    -H<bit<32>>[3] s = (H<bit<32>>[3]){ {0, 1}, {2, 3}, (H<bit<32>>){#} };
    -// without an explicit cast
    -H<bit<32>>[3] s1 = { {0, 1}, {2, 3}, (H<bit<32>>){#} };
    -// using the default initializer
    -H<bit<32>>[3] s2 = { {0, 1}, {2, 3}, ... };
    -

    The values of s, s1, and s2 in the above example are identical. -

    8.19. Operations on header unions

    -

    A variable declared with a union type is initially invalid. For example: -

    -
    -
    -
    header H1 {
    -  bit<8> f;
    -}
    -header H2 {
    -  bit<16> g;
    -}
    -header_union U {
    -  H1 h1;
    -  H2 h2;
    -}
    -
    -U u; // u invalid
    -

    This also implies that each of the headers h1 through hn contained +to a header of the same type as the other stack elements.

    +
    +
    +

    Here is an example:

    +
    +
    +
    +
    header H<T> {
    +    bit<32> b;
    +    T t;
    +}
    +H<bit<32>>[3] s = (H<bit<32>>[3]){ {0, 1}, {2, 3}, (H<bit<32>>){#} };
    +// without an explicit cast
    +H<bit<32>>[3] s1 = { {0, 1}, {2, 3}, (H<bit<32>>){#} };
    +// using the default initializer
    +H<bit<32>>[3] s2 = { {0, 1}, {2, 3}, ... };
    +
    +
    +
    +

    The values of s, s1, and s2 in the above example are identical.

    +
    +
    +
    +
    +

    8.19. Operations on header unions

    +
    +

    A variable declared with a union type is initially invalid. For example:

    +
    +
    +
    +
    header H1 {
    +  bit<8> f;
    +}
    +header H2 {
    +  bit<16> g;
    +}
    +header_union U {
    +  H1 h1;
    +  H2 h2;
    +}
    +
    +U u; // u invalid
    +
    +
    +
    +

    This also implies that each of the headers h1 through hn contained in a header union are also initially invalid. Unlike headers, a union cannot be initialized. However, the validity of a header union can be -updated by assigning a valid header to one of its elements: -

    -
    -
    -
    U u;
    -H1 my_h1 = { 8w0 }; // my_h1 is valid
    -u.h1 = my_h1;       // u and u.h1 are both valid
    -

    We can also assign a tuple to an element of a header union, -

    -
    -
    -
    U u;
    -u.h2 = { 16w1 };     // u and u.h2 are both valid
    -

    or set their validity bits directly. -

    -
    -
    -
    U u;
    -u.h1.setValid();     // u and u.h1 are both valid
    -H1 my_h1 = u.h1;     // my_h1 is now valid, but contains an undefined value
    -

    Note that reading an uninitialized header produces an undefined value, -even if the header is itself valid. -

    -

    If u is an expression whose type is a header union U with fields -ranged over by hi, then the expression u.hi evaluates to a header, +updated by assigning a valid header to one of its elements:

    +
    +
    +
    +
    U u;
    +H1 my_h1 = { 8w0 }; // my_h1 is valid
    +u.h1 = my_h1;       // u and u.h1 are both valid
    +
    +
    +
    +

    We can also assign a tuple to an element of a header union,

    +
    +
    +
    +
    U u;
    +u.h2 = { 16w1 };     // u and u.h2 are both valid
    +
    +
    +
    +

    or set their validity bits directly.

    +
    +
    +
    +
    U u;
    +u.h1.setValid();     // u and u.h1 are both valid
    +H1 my_h1 = u.h1;     // my_h1 is now valid, but contains an undefined value
    +
    +
    +
    +

    Note that reading an uninitialized header produces an undefined value, +even if the header is itself valid.

    +
    +
    +

    If u is an expression whose type is a header union U with fields +ranged over by hi, then the expression u.hi evaluates to a header, and thus it can be used wherever a header expression is allowed. If -u is a left-value, then u.hi is also a left-value. -

    -

    The following operations: -

    -
      -
    • -

      u.hi.setValid(): sets the valid bit for header hi to true and -sets the valid bit for all other headers to false, which implies that -it is unspecified what value reading any member header of u will return. -

    • -
    • -

      u.hi.setInvalid(): if the valid bit for any member header of u -is true then sets it to false, which implies that it is unspecified -what value reading any member header of u will return. -

    - -

    The assignment to a union field: -

    -
    -
    -
    u.hi = e;
    -

    has the following meaning: -

    -
      -
    • if e is valid, then it is equivalent to: -
    - -
    -
    -
      u.hi.setValid();
    -  u.hi = e;
    -
      -
    • if e is invalid, then it is equivalent to: -
    - -
    -
    -
      u.hi.setInvalid();
    -

    Assignments between variables of the same type of header union are -permitted. The assignment u1 = u2 copies the full state of header -union u2 to u1. If u2 is valid, then there is some header -u2.hi that is valid. The assignment behaves the same as u1.hi = u2.hi. -If u2 is not valid, then u1 becomes invalid (i.e. if any -header of u1 was valid, it becomes invalid). -

    -

    u.isValid() returns true if any member of the header union u is -valid, otherwise it returns false. setValid() and setInvalid() -methods are not defined for header unions. -

    -

    Supplying an expression with a union type to emit simply emits the -single header that is valid, if any. -

    -

    The following example shows how we can use header unions to represent -IPv4 and IPv6 headers uniformly: -

    -
    -
    -
    header_union IP {
    -    IPv4 ipv4;
    -    IPv6 ipv6;
    -}
    -
    -struct Parsed_packet {
    -   Ethernet ethernet;
    -   IP ip;
    -}
    -
    -parser top(packet_in b, out Parsed_packet p) {
    -    state start {
    -       b.extract(p.ethernet);
    -       transition select(p.ethernet.etherType) {
    -           16w0x0800 : parse_ipv4;
    -           16w0x86DD : parse_ipv6;
    -       }
    -   }
    -   state parse_ipv4 {
    -       b.extract(p.ip.ipv4);
    -       transition accept;
    -   }
    -   state parse_ipv6 {
    -       b.extract(p.ip.ipv6);
    -       transition accept;
    -   }
    -}
    -

    As another example, we can also use unions to parse (selected) TCP -options: -

    -
    -
    -
    header Tcp_option_end_h {
    -    bit<8> kind;
    -}
    -header Tcp_option_nop_h {
    -    bit<8> kind;
    -}
    -header Tcp_option_ss_h {
    -    bit<8>  kind;
    -    bit<32> maxSegmentSize;
    -}
    -header Tcp_option_s_h {
    -    bit<8>  kind;
    -    bit<24> scale;
    -}
    -header Tcp_option_sack_h {
    -    bit<8>         kind;
    -    bit<8>         length;
    -    varbit<256>    sack;
    -}
    -header_union Tcp_option_h {
    -    Tcp_option_end_h  end;
    -    Tcp_option_nop_h  nop;
    -    Tcp_option_ss_h   ss;
    -    Tcp_option_s_h    s;
    -    Tcp_option_sack_h sack;
    -}
    -
    -typedef Tcp_option_h[10] Tcp_option_stack;
    -
    -struct Tcp_option_sack_top {
    -    bit<8> kind;
    -    bit<8> length;
    -}
    -
    -parser Tcp_option_parser(packet_in b, out Tcp_option_stack vec) {
    -    state start {
    -        transition select(b.lookahead<bit<8>>()) {
    -            8w0x0 : parse_tcp_option_end;
    -            8w0x1 : parse_tcp_option_nop;
    -            8w0x2 : parse_tcp_option_ss;
    -            8w0x3 : parse_tcp_option_s;
    -            8w0x5 : parse_tcp_option_sack;
    -        }
    -    }
    -    state parse_tcp_option_end {
    -        b.extract(vec.next.end);
    -        transition accept;
    -    }
    -    state parse_tcp_option_nop {
    -         b.extract(vec.next.nop);
    -         transition start;
    -    }
    -    state parse_tcp_option_ss {
    -         b.extract(vec.next.ss);
    -         transition start;
    -    }
    -    state parse_tcp_option_s {
    -         b.extract(vec.next.s);
    -         transition start;
    -    }
    -    state parse_tcp_option_sack {
    -         bit<8> n = b.lookahead<Tcp_option_sack_top>().length;
    -         // n is the total length of the TCP SACK option in bytes.
    -         // The length of the varbit field 'sack' of the
    -         // Tcp_option_sack_h header is thus n-2 bytes.
    -         b.extract(vec.next.sack, (bit<32>) (8 * n - 16));
    -         transition start;
    -    }
    -}
    -

    Similar to headers, the size of a header union is a local compile-time known -value (Section 9). -

    -

    The expression {#} represents an invalid header union of some type, +u is a left-value, then u.hi is also a left-value.

    +
    +
    +

    The following operations:

    +
    +
    +
      +
    • +

      u.hi.setValid(): sets the valid bit for header hi to true and +sets the valid bit for all other headers to false, which implies that +it is unspecified what value reading any member header of u will return.

      +
    • +
    • +

      u.hi.setInvalid(): if the valid bit for any member header of u +is true then sets it to false, which implies that it is unspecified +what value reading any member header of u will return.

      +
    • +
    +
    +
    +

    The assignment to a union field:

    +
    +
    +
    +
    u.hi = e;
    +
    +
    +
    +

    has the following meaning:

    +
    +
    +
      +
    • +

      if e is valid, then it is equivalent to:

      +
    • +
    +
    +
    +
    +
      u.hi.setValid();
    +  u.hi = e;
    +
    +
    +
    +
      +
    • +

      if e is invalid, then it is equivalent to:

      +
    • +
    +
    +
    +
    +
      u.hi.setInvalid();
    +
    +
    +
    +

    Assignments between variables of the same type of header union are +permitted. The assignment u1 = u2 copies the full state of header +union u2 to u1. If u2 is valid, then there is some header +u2.hi that is valid. The assignment behaves the same as u1.hi = u2.hi. +If u2 is not valid, then u1 becomes invalid (i.e. if any +header of u1 was valid, it becomes invalid).

    +
    +
    +

    u.isValid() returns true if any member of the header union u is +valid, otherwise it returns false. setValid() and setInvalid() +methods are not defined for header unions.

    +
    +
    +

    Supplying an expression with a union type to emit simply emits the +single header that is valid, if any.

    +
    +
    +

    The following example shows how we can use header unions to represent +IPv4 and IPv6 headers uniformly:

    +
    +
    +
    +
    header_union IP {
    +    IPv4 ipv4;
    +    IPv6 ipv6;
    +}
    +
    +struct Parsed_packet {
    +   Ethernet ethernet;
    +   IP ip;
    +}
    +
    +parser top(packet_in b, out Parsed_packet p) {
    +    state start {
    +       b.extract(p.ethernet);
    +       transition select(p.ethernet.etherType) {
    +           16w0x0800 : parse_ipv4;
    +           16w0x86DD : parse_ipv6;
    +       }
    +   }
    +   state parse_ipv4 {
    +       b.extract(p.ip.ipv4);
    +       transition accept;
    +   }
    +   state parse_ipv6 {
    +       b.extract(p.ip.ipv6);
    +       transition accept;
    +   }
    +}
    +
    +
    +
    +

    As another example, we can also use unions to parse (selected) TCP +options:

    +
    +
    +
    +
    header Tcp_option_end_h {
    +    bit<8> kind;
    +}
    +header Tcp_option_nop_h {
    +    bit<8> kind;
    +}
    +header Tcp_option_ss_h {
    +    bit<8>  kind;
    +    bit<32> maxSegmentSize;
    +}
    +header Tcp_option_s_h {
    +    bit<8>  kind;
    +    bit<24> scale;
    +}
    +header Tcp_option_sack_h {
    +    bit<8>         kind;
    +    bit<8>         length;
    +    varbit<256>    sack;
    +}
    +header_union Tcp_option_h {
    +    Tcp_option_end_h  end;
    +    Tcp_option_nop_h  nop;
    +    Tcp_option_ss_h   ss;
    +    Tcp_option_s_h    s;
    +    Tcp_option_sack_h sack;
    +}
    +
    +typedef Tcp_option_h[10] Tcp_option_stack;
    +
    +struct Tcp_option_sack_top {
    +    bit<8> kind;
    +    bit<8> length;
    +}
    +
    +parser Tcp_option_parser(packet_in b, out Tcp_option_stack vec) {
    +    state start {
    +        transition select(b.lookahead<bit<8>>()) {
    +            8w0x0 : parse_tcp_option_end;
    +            8w0x1 : parse_tcp_option_nop;
    +            8w0x2 : parse_tcp_option_ss;
    +            8w0x3 : parse_tcp_option_s;
    +            8w0x5 : parse_tcp_option_sack;
    +        }
    +    }
    +    state parse_tcp_option_end {
    +        b.extract(vec.next.end);
    +        transition accept;
    +    }
    +    state parse_tcp_option_nop {
    +         b.extract(vec.next.nop);
    +         transition start;
    +    }
    +    state parse_tcp_option_ss {
    +         b.extract(vec.next.ss);
    +         transition start;
    +    }
    +    state parse_tcp_option_s {
    +         b.extract(vec.next.s);
    +         transition start;
    +    }
    +    state parse_tcp_option_sack {
    +         bit<8> n = b.lookahead<Tcp_option_sack_top>().length;
    +         // n is the total length of the TCP SACK option in bytes.
    +         // The length of the varbit field 'sack' of the
    +         // Tcp_option_sack_h header is thus n-2 bytes.
    +         b.extract(vec.next.sack, (bit<32>) (8 * n - 16));
    +         transition start;
    +    }
    +}
    +
    +
    +
    +

    Similar to headers, the size of a header union is a local compile-time known value (Section Chapter 9).

    +
    +
    +

    The expression {#} represents an invalid header union of some type, but it can be any header or header union type. A P4 compiler may require an explicit cast on this expression in cases where it cannot determine -the particular header or header union type from the context. -

    -
    -
    -
    header_union HU { ... }
    -HU h = (HU){#};  // invalid header union; same as an uninitialized header union.
    -

    Two header unions can be compared for equality (==) or inequality (!=) +the particular header or header union type from the context.

    +
    +
    +
    +
    header_union HU { ... }
    +HU h = (HU){#};  // invalid header union; same as an uninitialized header union.
    +
    +
    +
    +

    Two header unions can be compared for equality (==) or inequality (!=) if they have the same type. The unions are equal if and only if all their corresponding fields are equal (i.e., either all fields are invalid in both unions, or in both unions the same field is valid, and -the values of the valid fields are equal as headers). -

    8.20. Method invocations and function calls

    -

    Method invocations and function calls can be invoked using the -following syntax: -

    -
    -
    -
    expression
    +the values of the valid fields are equal as headers).

    +
    +
    +
    +

    8.20. Method invocations and function calls

    +
    +

    Method invocations and function calls can be invoked using the +following syntax:

    +
    +
    +
    +
    expression
         : ...
    -    | expression '<' realTypeArgumentList '>' '(' argumentList ')'
    -    | expression '(' argumentList ')'
    +    | expression '<' realTypeArgumentList '>' '(' argumentList ')'
    +    | expression '(' argumentList ')'
     
     argumentList
    -    : /* empty */
    +    : /* empty */
         | nonEmptyArgList
         ;
     
     nonEmptyArgList
         : argument
    -    | nonEmptyArgList "," argument
    +    | nonEmptyArgList "," argument
         ;
     
     argument
    -    : expression  /* positional argument */
    -    | name "=" expression  /* named argument */
    -    | "_"
    -    | name "=" "_"
    +    : expression  /* positional argument */
    +    | name "=" expression  /* named argument */
    +    | "_"
    +    | name "=" "_"
         ;
     
     realTypeArgumentList
         : realTypeArg
    -    | realTypeArgumentList "," typeArg
    +    | realTypeArgumentList "," typeArg
         ;
     
     realTypeArg
         : typeRef
         | VOID
    -    | "_"
    -    ;
    -

    A function call or method invocation can optionally specify for each + | "_" + ; +

    +
    +
    +

    A function call or method invocation can optionally specify for each argument the corresponding parameter name. It is illegal to use names only for some arguments: either all or no arguments must specify the parameter name. Function arguments are evaluated in the order -they appear, left to right, before the function invocation takes place. -

    -
    -
    -
    extern void f(in bit<32> x, out bit<16> y);
    -bit<32> xa = 0;
    -bit<16> ya;
    -f(xa, ya);  // match arguments by position
    -f(x = xa, y = ya);  // match arguments by name
    -f(y = ya, x = xa);  // match arguments by name in any order
    -//f(x = xa);  -- error: enough arguments
    -//f(x = xa, x = ya);  -- error: argument specified twice
    -//f(x = xa, ya);  -- error: some arguments specified by name
    -//f(z = xa, w = yz);  -- error: no parameter named z or w
    -//f(x = xa, y = 0);  -- error: y must be a left-value
    -

    The calling convention is copy-in/copy-out (Section -6.8). For generic functions the type arguments +they appear, left to right, before the function invocation takes place.

    +
    +
    +
    +
    extern void f(in bit<32> x, out bit<16> y);
    +bit<32> xa = 0;
    +bit<16> ya;
    +f(xa, ya);  // match arguments by position
    +f(x = xa, y = ya);  // match arguments by name
    +f(y = ya, x = xa);  // match arguments by name in any order
    +//f(x = xa);  -- error: enough arguments
    +//f(x = xa, x = ya);  -- error: argument specified twice
    +//f(x = xa, ya);  -- error: some arguments specified by name
    +//f(z = xa, w = yz);  -- error: no parameter named z or w
    +//f(x = xa, y = 0);  -- error: y must be a left-value
    +
    +
    +
    +

    The calling convention is copy-in/copy-out (Section 6.8). For generic functions the type arguments can be explicitly specified in the function call. The compiler only -inserts implicit casts for direction in arguments to methods or -functions as described in Section 8.11. The types for all -other arguments must match the parameter types exactly. -

    -

    The result returned by a function call is discarded when the function -call is used as a statement. -

    -

    The “don't care” identifier (_) can only be used for an out +inserts implicit casts for direction in arguments to methods or +functions as described in Section 8.11. The types for all +other arguments must match the parameter types exactly.

    +
    +
    +

    The result returned by a function call is discarded when the function +call is used as a statement.

    +
    +
    +

    The "don’t care" identifier (_) can only be used for an out function/method argument, when the value of returned in that argument is ignored by subsequent computations. When used in generic functions or methods, the compiler may reject the program if it is unable to -infer a type for the don't care argument. -

    8.21. Constructor invocations

    -

    Several P4 constructs denote resources that are allocated at -compilation time: -

    -
      -
    • extern objects -
    • -
    • parsers -
    • -
    • control blocks -
    • -
    • packages -
    - -

    Allocation of such objects can be performed in two ways: -

    -
      -
    • Using constructor invocations, which are expressions that return an -object of the corresponding type. -
    • -
    • Using instantiations, described in Section 11.3. -
    - -

    The syntax for a constructor invocation is similar to a function call; +infer a type for the don’t care argument.

    +
    +
    +
    +

    8.21. Constructor invocations

    +
    +

    Several P4 constructs denote resources that are allocated at +compilation time:

    +
    +
    +
      +
    • +

      extern objects

      +
    • +
    • +

      parsers

      +
    • +
    • +

      control blocks

      +
    • +
    • +

      packages

      +
    • +
    +
    +
    +

    Allocation of such objects can be performed in two ways:

    +
    +
    +
      +
    • +

      Using constructor invocations, which are expressions that return an +object of the corresponding type.

      +
    • +
    • +

      Using instantiations, described in Section 11.3.

      +
    • +
    +
    +
    +

    The syntax for a constructor invocation is similar to a function call; constructors can also be called using named arguments. -Constructors are evaluated entirely at compilation time (see Section -18). In consequence, all constructor arguments +Constructors are evaluated entirely at compilation time (see Chapter 18). In consequence, all constructor arguments must also be expressions that can be evaluated at compilation time. When performing type inference and overload resolution, constructor -invocations are treated similar to methods or functions. -

    -

    The following example shows a constructor invocation for setting the -target-dependent implementation property of a table: -

    -
    -
    -
    extern ActionProfile {
    -    ActionProfile(bit<32> size);  // constructor
    -}
    -table tbl {
    -    actions = { /* body omitted */ }
    -    implementation = ActionProfile(1024);  // constructor invocation
    -}

    8.22. Operations on extern objects

    -

    The only operations that can be performed on extern objects are the -following: -

    -
      -
    • Instantiating an extern object using a constructor invocation, as described in -Section 8.21. -
    • -
    • Invoking a method of an extern object instance using a method call expression, as -described in Section 8.20. -
    - -

    Controls, parsers, packages, and extern constructors can have -parameters of type extern only if they are directionless parameters. +invocations are treated similar to methods or functions.

    +
    +
    +

    The following example shows a constructor invocation for setting the +target-dependent implementation property of a table:

    +
    +
    +
    +
    extern ActionProfile {
    +    ActionProfile(bit<32> size);  // constructor
    +}
    +table tbl {
    +    actions = { /* body omitted */ }
    +    implementation = ActionProfile(1024);  // constructor invocation
    +}
    +
    +
    +
    +
    +

    8.22. Operations on extern objects

    +
    +

    The only operations that can be performed on extern objects are the +following:

    +
    +
    +
      +
    • +

      Instantiating an extern object using a constructor invocation, as described in +Section 8.21.

      +
    • +
    • +

      Invoking a method of an extern object instance using a method call expression, as +described in Section 8.20.

      +
    • +
    +
    +
    +

    Controls, parsers, packages, and extern constructors can have +parameters of type extern only if they are directionless parameters. Extern object instances can thus be used as arguments in the -construction of such objects. -

    -

    No other operations are available on extern objects, e.g., equality -comparison is not defined. -

    8.23. Operations on types introduced by type

    -

    Values with a type introduced by the type keyword provide only a few operations: -

    -
      -
    • assignment to left-values of the same type -
    • -
    • comparisons for equality and inequality if the original type supported such comparisons -
    • -
    • casts to and from the original type -
    - -
    -
    -
    type bit<32> U32;
    -U32 x = (U32)0;  // cast needed
    -U32 y = (U32) ((bit<32>)x + 1);  // casts needed for arithmetic
    -bit<32> z = 1;
    -bool b0 = x == (U32)z; // cast needed
    -bool b1 = (bit<32>)x == z;  // cast needed
    -bool b2 = x == y;  // no cast needed

    8.24. Operations on types that are type variables

    -

    Because functions, methods, control, and parsers can be generic, +construction of such objects.

    +
    +
    +

    No other operations are available on extern objects, e.g., equality +comparison is not defined.

    +
    +
    +
    +

    8.23. Operations on types introduced by type

    +
    +

    Values with a type introduced by the type keyword provide only a few operations:

    +
    +
    +
      +
    • +

      assignment to left-values of the same type

      +
    • +
    • +

      comparisons for equality and inequality if the original type supported such comparisons

      +
    • +
    • +

      casts to and from the original type

      +
    • +
    +
    +
    +
    +
    type bit<32> U32;
    +U32 x = (U32)0;  // cast needed
    +U32 y = (U32) ((bit<32>)x + 1);  // casts needed for arithmetic
    +bit<32> z = 1;
    +bool b0 = x == (U32)z; // cast needed
    +bool b1 = (bit<32>)x == z;  // cast needed
    +bool b2 = x == y;  // no cast needed
    +
    +
    +
    +
    +

    8.24. Operations on types that are type variables

    +
    +

    Because functions, methods, control, and parsers can be generic, they offer the possibility of declaring values with types that -are type variables: -

    -
    -
    -
    control C<T>() {
    -    apply {
    -        T x;  // the type of x is T, a type variable
    -    }
    -}
    -

    The type of such objects is not known until the control is -instantiated with specific type arguments. -

    -

    Currently the only operations that are available for such values are -assignment (explicit through =, or implicit, through argument passing). +are type variables:

    +
    +
    +
    +
    control C<T>() {
    +    apply {
    +        T x;  // the type of x is T, a type variable
    +    }
    +}
    +
    +
    +
    +

    The type of such objects is not known until the control is +instantiated with specific type arguments.

    +
    +
    +

    Currently the only operations that are available for such values are +assignment (explicit through =, or implicit, through argument passing). This behavior is similar to languages such as Java, and different from -languages such as C++. -

    -

    A future version of P4 may introduce a notion of type constraints +languages such as C++.

    +
    +
    +

    A future version of P4 may introduce a notion of type constraints which would enable more operations on such values. Because of this -limitation, such values are currently of limited utility. -

    8.25. Reading uninitialized values and writing fields of invalid headers

    -

    As mentioned in Section 8.18, any reference to an element of -a header stack hs[index] where index is a compile-time known value +limitation, such values are currently of limited utility.

    +
    +
    +
    +

    8.25. Reading uninitialized values and writing fields of invalid headers

    +
    +

    As mentioned in Section Section 8.18, any reference to an element of +a header stack hs[index] where index is a compile-time known value must give an error at compile time if the value of the index is out of range. That section also defines the run time behavior of the -expressions hs.next and hs.last, and the behaviors specified there -take precedence over anything in this section for those expressions. -

    -

    All mentions of header stack elements in this section only apply for -expressions hs[index] where index is not a compile-time known +expressions hs.next and hs.last, and the behaviors specified there +take precedence over anything in this section for those expressions.

    +
    +
    +

    All mentions of header stack elements in this section only apply for +expressions hs[index] where index is not a compile-time known value. A P4 implementation may elect not to support expressions of -the form hs[index] where index is not a compile-time known +the form hs[index] where index is not a compile-time known value. However, it does support such expressions, the implementation -should conform to the behaviors specified in this section. -

    -

    The result of reading a value in any of the situations below is that -some unspecified value will be used for that field. -

    -
      -
    • reading a field from a header that is currently invalid. -
    • -
    • reading a field from a header that is currently valid, but the field -has not been initialized since the header was last made valid. -
    • -
    • reading any other value that has not been initialized, e.g. a field -from a struct, any uninitialized variable inside of an action or -control, or an out parameter of a control or action you have +should conform to the behaviors specified in this section.

      +
    +
    +

    The result of reading a value in any of the situations below is that +some unspecified value will be used for that field.

    +
    +
    +
      +
    • +

      reading a field from a header that is currently invalid.

      +
    • +
    • +

      reading a field from a header that is currently valid, but the field +has not been initialized since the header was last made valid.

      +
    • +
    • +

      reading any other value that has not been initialized, e.g. a field +from a struct, any uninitialized variable inside of an action or +control, or an out parameter of a control or action you have called, which was not assigned a value during the execution of that -control or action (this list of examples is not intended to be exhaustive). +control or action (this list of examples is not intended to be exhaustive).

    • -
    • reading a field of a header that is an element of a header stack, -where the index is out of range for the header stack. -
    - -

    Calling the isValid() method on an element of a header stack, where +

  • +

    reading a field of a header that is an element of a header stack, +where the index is out of range for the header stack.

    +
  • + +
    +
    +

    Calling the isValid() method on an element of a header stack, where the index is out of range, returns an undefined boolean value, i.e., -it is either true or false, but the specification does not require +it is either true or false, but the specification does not require one or the other, nor that a consistent value is returned across multiple such calls. Assigning an out-of-range header stack element -to another header variable h leads to a state where h is undefined -in all of its field values, and its validity is also undefined. -

    -

    Where a header is mentioned, it may be a member of a header_union, +to another header variable h leads to a state where h is undefined +in all of its field values, and its validity is also undefined.

    +
    +
    +

    Where a header is mentioned, it may be a member of a header_union, an element in a header stack, or a normal header. This unspecified -value could differ from one such read to another. -

    -

    For an uninitialized field or variable with a type of enum or -error, the unspecified value that is read might not be equal to any +value could differ from one such read to another.

    +
    +
    +

    For an uninitialized field or variable with a type of enum or +error, the unspecified value that is read might not be equal to any of the values defined for that type. Such an unspecified value should still lead to predictable behavior in cases where any legal value -would match, e.g. it should match in any of these situations: -

    -
      -
    • If used in a select expression, it should match default or _ -in a key set expression. -
    • -
    • If used as a key with match_kind ternary in a table, it should -match a table entry where the field has all bit positions “don't care”. -
    • -
    • If used as a key with match_kind lpm in a table, it should match -a table entry where the field has a prefix length of 0. -
    - -

    Consider a situation where a header_union u1 has member headers -u1.h1 and u1.h2, and at a given point in the program's execution -u1.h1 is valid and u1.h2 is invalid. If a write is attempted to a -field of the invalid member header u1.h2, then any or all of the -fields of the valid member header u1.h1 may change as a result. +would match, e.g. it should match in any of these situations:

    +
    +
    +
      +
    • +

      If used in a select expression, it should match default or _ +in a key set expression.

      +
    • +
    • +

      If used as a key with match_kind ternary in a table, it should +match a table entry where the field has all bit positions "don’t care".

      +
    • +
    • +

      If used as a key with match_kind lpm in a table, it should match +a table entry where the field has a prefix length of 0.

      +
    • +
    +
    +
    +

    Consider a situation where a header_union u1 has member headers +u1.h1 and u1.h2, and at a given point in the program’s execution +u1.h1 is valid and u1.h2 is invalid. If a write is attempted to a +field of the invalid member header u1.h2, then any or all of the +fields of the valid member header u1.h1 may change as a result. Such a write must not change the validity of any member headers of -u1, nor any other state that is currently defined in the system, -whether it is defined state in header fields or anywhere else. -

    -

    If any of these kinds of writes are performed: -

    -
      -
    • a write to a field in a currently invalid header, either a regular +u1, nor any other state that is currently defined in the system, +whether it is defined state in header fields or anywhere else.

      +
    +
    +

    If any of these kinds of writes are performed:

    +
    +
    +
      +
    • +

      a write to a field in a currently invalid header, either a regular header or an element of a header stack with an index that is in -range, and that header is not part of a header_union +range, and that header is not part of a header_union

    • -
    • a write to a field in an element of a header stack, where the index -is out of range +
    • +

      a write to a field in an element of a header stack, where the index +is out of range

    • -
    • a method call of setValid() or setInvalid() on an element of a -header stack, where the index is out of range -
    - -

    then that write must not change any state that is currently defined in +

  • +

    a method call of setValid() or setInvalid() on an element of a +header stack, where the index is out of range

    +
  • + +
    +
    +

    then that write must not change any state that is currently defined in the system, neither in header fields nor anywhere else. In particular, if an invalid header is involved in the write, it must -remain invalid. -

    -

    Any writes to fields in a currently invalid header, or to header stack +remain invalid.

    +
    +
    +

    Any writes to fields in a currently invalid header, or to header stack elements where the index is out of range, are allowed to modify state whose values are not defined, e.g. the values of fields in headers -that are currently invalid. -

    -

    For a top level parser or control in an architecture, it is up to +that are currently invalid.

    +
    +
    +

    For a top level parser or control in an architecture, it is up to that architecture to specify whether parameters with -direction in or inout are initialized when the control is called, +direction in or inout are initialized when the control is called, and under what conditions they are initialized, and if so, what their -values will be. -

    -

    Since P4 allows empty tuples and structs, one can construct types -whose values carry no “useful” information, e.g.: -

    -
    -
    -
    struct Empty {
    -   tuple<> t;
    -}
    -

    We call the following “empty” types: -

    -
      -
    • bitstrings with 0 width -
    • -
    • varbits with 0 width -
    • -
    • empty tuples (tuple<>) -
    • -
    • stacks with 0 size -
    • -
    • structs with no fields -
    • -
    • a tuple having all fields of an empty type -
    • -
    • a struct having all fields of an empty type -
    - -

    Values with empty types carry no useful information. In particular, -they do not have to be explicitly initialized to have a legal value. -

    -

    (Header types with no fields always have a validity bit.) -

    8.26. Initializing with default values

    -

    A left-value can be initialized automatically with a default value of the -suitable type using the syntax ... (see Section 7.3). A value -of type struct, header, or tuple can also be initialized using a mix of -explicit values and default values by using the notation ... in a tuple +values will be.

    +
    +
    +

    Since P4 allows empty tuples and structs, one can construct types +whose values carry no "useful" information, e.g.:

    +
    +
    +
    +
    struct Empty {
    +   tuple<> t;
    +}
    +
    +
    +
    +

    We call the following "empty" types:

    +
    +
    +
      +
    • +

      bitstrings with 0 width

      +
    • +
    • +

      varbits with 0 width

      +
    • +
    • +

      empty tuples (tuple<>)

      +
    • +
    • +

      stacks with 0 size

      +
    • +
    • +

      structs with no fields

      +
    • +
    • +

      a tuple having all fields of an empty type

      +
    • +
    • +

      a struct having all fields of an empty type

      +
    • +
    +
    +
    +

    Values with empty types carry no useful information. In particular, +they do not have to be explicitly initialized to have a legal value.

    +
    +
    +

    (Header types with no fields always have a validity bit.)

    +
    +
    +
    +

    8.26. Initializing with default values

    +
    +

    A left-value can be initialized automatically with a default value of the +suitable type using the syntax ... (see Section 7.3). A value +of type struct, header, or tuple can also be initialized using a mix of +explicit values and default values by using the notation ... in a tuple expression initializer; in this case all fields not explicitly -initialized are initialized with default values. When initializing a struct, -header, and tuple with a value containing partially default values -using the ... notation the three dots must appear last in the initializer. -

    -
    -
    -
    struct S {
    -     bit<32> b32;
    -     bool b;
    -}
    -
    -enum int<8> N0 {
    -   one = 1,
    -   zero = 0,
    -   two = 2
    -}
    -
    -enum N1 {
    -     A, B, C, F
    -}
    -
    -struct T {
    -    S s;
    -    N0 n0;
    -    N1 n1;
    -}
    -
    -header H {
    -    bit<16> f1;
    -    bit<8> f2;
    -}
    -
    -N0 n0 = ...;  // initialize n0 with the default value 0
    -N1 n1 = ...;  // initialize n1 with the default value N1.A
    -S s0 = ...;  // initialize s0 with the default value { 0, false }
    -S s1 = { 1, ... };  // initialize s1 with the value { 1, false }
    -S s2 = { b = true, ... }; // initialize s2 with the value { 0, true }
    -T t0 = ...;  // initialize t0 with the value { { 0, false }, 0, N1.A }
    -T t1 = { s = ..., ... }; // initialize t1 with the value { { 0, false }, 0, N1.A }
    -T t2 = { s = ... }; // error: no initializer specified for fields n0 and n1
    -tuple<N0, N1> p = { ... }; // initialize p with default value { 0, N1.A }
    -T t3 = { ..., n0 = 2}; // error: ... must be last
    -H h1 = ...;   // initialize h1 with a header that is invalid
    -H h2 = { f2=5, ... };   // initialize h2 with a header that is valid, field f1 0,
    -                        // field f2 5
    -H h3 = { ... };  // initialize h3 with a header that is valid, field f1 0, field f2 0

    9. Compile-time size determination

    -

    The method calls minSizeInBits, minSizeInBytes, maxSizeInBits, -and maxSizeInBytes can be applied to certain expressions. These +initialized are initialized with default values. When initializing a struct, +header, and tuple with a value containing partially default values +using the ... notation the three dots must appear last in the initializer.

    +
    +
    +
    +
    struct S {
    +     bit<32> b32;
    +     bool b;
    +}
    +
    +enum int<8> N0 {
    +   one = 1,
    +   zero = 0,
    +   two = 2
    +}
    +
    +enum N1 {
    +     A, B, C, F
    +}
    +
    +struct T {
    +    S s;
    +    N0 n0;
    +    N1 n1;
    +}
    +
    +header H {
    +    bit<16> f1;
    +    bit<8> f2;
    +}
    +
    +N0 n0 = ...;  // initialize n0 with the default value 0
    +N1 n1 = ...;  // initialize n1 with the default value N1.A
    +S s0 = ...;  // initialize s0 with the default value { 0, false }
    +S s1 = { 1, ... };  // initialize s1 with the value { 1, false }
    +S s2 = { b = true, ... }; // initialize s2 with the value { 0, true }
    +T t0 = ...;  // initialize t0 with the value { { 0, false }, 0, N1.A }
    +T t1 = { s = ..., ... }; // initialize t1 with the value { { 0, false }, 0, N1.A }
    +T t2 = { s = ... }; // error: no initializer specified for fields n0 and n1
    +tuple<N0, N1> p = { ... }; // initialize p with default value { 0, N1.A }
    +T t3 = { ..., n0 = 2}; // error: ... must be last
    +H h1 = ...;   // initialize h1 with a header that is invalid
    +H h2 = { f2=5, ... };   // initialize h2 with a header that is valid, field f1 0,
    +                        // field f2 5
    +H h3 = { ... };  // initialize h3 with a header that is valid, field f1 0, field f2 0
    +
    +
    +
    +
    +
    +
    +

    9. Compile-time size determination

    +
    +
    +

    The method calls minSizeInBits, minSizeInBytes, maxSizeInBits, +and maxSizeInBytes can be applied to certain expressions. These method calls return the minimum (or maximum) size in bits (or bytes) required to store the expression. Thus, the result type of these methods has type -int. Except in certain situations involving type variables, +int. Except in certain situations involving type variables, discussed below, these method calls produce local compile-time known values; otherwise they produce compile-time known values. None of these methods evaluate the expression that is the receiver of the method call, so it may be invalid (e.g., an out-of-bounds header stack -access). -

    -

    The method minSizeInBytes returns the result of -minSizeInBits rounded up to the next whole number of bytes. -In other words, for any expression e, -e.minSizeInBytes() is equal to (e.minSizeInBits() + 7) >> 3. -

    -

    The method maxSizeInBytes always returns the result of -maxSizeInBits rounded up to the next whole number of bytes. -In other words, for any expression e, -e.maxSizeInBytes() is equal to (e.maxSizeInBits() + 7) >> 3. -

    -

    The definition of e.minSizeInBits() and e.maxSizeInBits() is given -recursively on the type of e as described in the following table: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Type minSizeInBits maxSizeInBits
    bit<N> N N
    int<N> N N
    bool 1 1
    enum bit<N> N N
    enum int<N> N N
    tuple foreach field(tuple) sum of foreach field(tuple) sum of
    field.minSizeInBits() field.maxSizeInBits()
    varbit<N> 0 N
    struct foreach field(struct) sum of foreach field(struct) sum of
    field.minSizeInBits() field.maxSizeInBits()
    header foreach field(header) sum of foreach field(header) sum of
    field.minSizeInBits() field.maxSizeInBits()
    H[N] N * H.minSizeInBits() N * H.maxSizeInBits()
    header_union max(foreach field(header_union) max(foreach field(header_union)
    field.minSizeInBits()) field.maxSizeInBits())
    -

    The methods can also be applied to type name expressions e: -

    -
      -
    • if the type of e is a type introduced by type, the result -is the application of the method to the underlying type -
    • -
    • if e is the name of a type (e.g., introduced by a typedef declaration), +access).

      +
    +
    +

    The method minSizeInBytes returns the result of +minSizeInBits rounded up to the next whole number of bytes. +In other words, for any expression e, +e.minSizeInBytes() is equal to (e.minSizeInBits() + 7) >> 3.

    +
    +
    +

    The method maxSizeInBytes always returns the result of +maxSizeInBits rounded up to the next whole number of bytes. +In other words, for any expression e, +e.maxSizeInBytes() is equal to (e.maxSizeInBits() + 7) >> 3.

    +
    +
    +

    The definition of e.minSizeInBits() and e.maxSizeInBits() is given +recursively on the type of e as described in the following table:

    +
    + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeminSizeInBitsmaxSizeInBits

    bit<N>

    N

    N

    int<N>

    N

    N

    bool

    1

    1

    enum bit<N>

    N

    N

    enum int<N>

    N

    N

    tuple

    foreach field(tuple) sum of field.minSizeInBits()

    foreach field(tuple) sum of field.maxSizeInBits()

    varbit<N>

    0

    N

    struct

    foreach field(struct) sum of field.minSizeInBits()

    foreach field(struct) sum of field.maxSizeInBits()

    header

    foreach field(header) sum of field.minSizeInBits()

    foreach field(header) sum of field.maxSizeInBits()

    H[N]

    N * H.minSizeInBits()

    N * H.maxSizeInBits()

    header_union

    max(foreach field(header_union) field.minSizeInBits())

    max(foreach field(header_union) field.maxSizeInBits())

    +
    +

    The methods can also be applied to type name expressions e:

    +
    +
    +
      +
    • +

      if the type of e is a type introduced by type, the result +is the application of the method to the underlying type

      +
    • +
    • +

      if e is the name of a type (e.g., introduced by a typedef declaration), where the type given a name is one of the above, -then the result is obtained by applying the method to the underlying type. -

    - -

    These methods are defined for: -

    -
      -
    • all serializable types -
    • -
    • for a type that does not contain varbit fields, both methods return the same result -
    • -
    • for a type that does contain varbit fields, maxSizeInBits is the worst-case size -of the serialized representation of the data and minSizeInBits is the “best” case. -
    - -

    Every other case is undefined and will produce a compile-time +then the result is obtained by applying the method to the underlying type.

    + + +
    +
    +

    These methods are defined for:

    +
    +
    +
      +
    • +

      all serializable types

      +
    • +
    • +

      for a type that does not contain varbit fields, both methods return the same result

      +
    • +
    • +

      for a type that does contain varbit fields, maxSizeInBits is the worst-case size +of the serialized representation of the data and minSizeInBits is the "best" case.

      +
    • +
    +
    +
    +

    Every other case is undefined and will produce a compile-time error. In particular, cases involving type variables produce a -compile-time error. -

    10. Function declarations

    -

    Functions can only be declared at the top level and all parameters must have a direction. +compile-time error.

    +
    +
    +
    +
    +

    10. Function declarations

    +
    +
    +

    Functions can only be declared at the top level and all parameters must have a direction. P4 functions are modeled after functions as found in most other programming languages, -but the language does not permit recursive functions. -

    -
    -
    -
    functionDeclaration
    +but the language does not permit recursive functions.

    +
    +
    +
    +
    functionDeclaration
         : annotations functionPrototype blockStatement
         | functionPrototype blockStatement
         ;
     
     functionPrototype
    -    : typeOrVoid name optTypeParameters "(" parameterList ")"
    -    ;
    -

    Here is an example of a function that returns the maximum of two 32-bit values: -

    -
    -
    -
    bit<32> max(in bit<32> left, in bit<32> right) {
    -   return (left > right) ? left : right;
    -}
    -

    A function returns a value using the return statement. A function -with a return type of void can simply use the return statement with no + : typeOrVoid name optTypeParameters "(" parameterList ")" + ; +

    +
    +
    +

    Here is an example of a function that returns the maximum of two 32-bit values:

    +
    +
    +
    +
    bit<32> max(in bit<32> left, in bit<32> right) {
    +   return (left > right) ? left : right;
    +}
    +
    +
    +
    +

    A function returns a value using the return statement. A function +with a return type of void can simply use the return statement with no arguments. A function with a non-void return type must return a value -of the suitable type on all possible execution paths. -

    11. Constants and variable declarations

    11.1. Constants

    -

    Constant values are defined with the syntax: -

    -
    -
    -
    constantDeclaration
    -    : optAnnotations CONST typeRef name "=" initializer ";"
    +of the suitable type on all possible execution paths.

    +
    +
    +
    +
    +

    11. Constants and variable declarations

    +
    +
    +

    11.1. Constants

    +
    +

    Constant values are defined with the syntax:

    +
    +
    +
    +
    constantDeclaration
    +    : optAnnotations CONST typeRef name "=" initializer ";"
         ;
     
     initializer
         : expression
    -    ;
    -

    Such a declaration introduces a constant whose value has the specified type. The -following are all legal constant declarations: -

    -
    -
    -
    const bit<32> COUNTER = 32w0x0;
    -struct Version {
    -    bit<32> major;
    -    bit<32> minor;
    -}
    -const Version version = { 32w0, 32w0 };
    -

    The initializer expression must be a local compile-time known value. -

    11.2. Variables

    -

    Local variables are declared with a type, a name, and an optional -initializer (as well as an optional annotation): -

    -
    -
    -
    variableDeclaration
    -    : annotations typeRef name optInitializer ";"
    -    | typeRef name optInitializer ";"
    +    ;
    +
    +
    +
    +

    Such a declaration introduces a constant whose value has the specified type. The +following are all legal constant declarations:

    +
    +
    +
    +
    const bit<32> COUNTER = 32w0x0;
    +struct Version {
    +    bit<32> major;
    +    bit<32> minor;
    +}
    +const Version version = { 32w0, 32w0 };
    +
    +
    +
    +

    The initializer expression must be a local compile-time known value.

    +
    +
    +
    +

    11.2. Variables

    +
    +

    Local variables are declared with a type, a name, and an optional +initializer (as well as an optional annotation):

    +
    +
    +
    +
    variableDeclaration
    +    : annotations typeRef name optInitializer ";"
    +    | typeRef name optInitializer ";"
         ;
     
     optInitializer
    -    : /* empty */
    -    | "=" initializer
    -    ;
    -

    Variable declarations without an initializer are uninitialized (except for + : /* empty */ + | "=" initializer + ; +

    +
    +
    +

    Variable declarations without an initializer are uninitialized (except for headers and other header-related types, which are initialized to invalid in the -same way as described for direction out parameters in Section -6.8). The language places few restrictions on +same way as described for direction out parameters in Section 6.8). The language places few restrictions on the types of the variables: most P4 types that can be written -explicitly can be used (e.g., base types, struct, header, -header stack, tuple). However, it is impossible to declare variables with type int, -or with types that are only synthesized by the compiler (e.g., set) -In addition, variables of type parser, control, package, -or extern types must be declared using instantiations (see Section 11.3). -

    -

    Reading the value of a variable that has not been initialized yields -an undefined result. The compiler should attempt to detect and emit a warning in such situations. -

    -

    Variables declarations can appear in the following locations within a P4 -program: -

    -
      -
    • In a block statement, -
    • -
    • In a parser state, -
    • -
    • In an action body, -
    • -
    • In a control block's apply sub-block, -
    • -
    • In the list of local declarations in a parser, and -
    • -
    • In the list of local declarations in a control. -
    - -

    Variables have local scope, and behave like stack-allocated variables +explicitly can be used (e.g., base types, struct, header, +header stack, tuple). However, it is impossible to declare variables with type int, +or with types that are only synthesized by the compiler (e.g., set) +In addition, variables of type parser, control, package, +or extern types must be declared using instantiations (see Section 11.3).

    +
    +
    +

    Reading the value of a variable that has not been initialized yields +an undefined result. The compiler should attempt to detect and emit a warning in such situations.

    +
    +
    +

    Variables declarations can appear in the following locations within a P4 +program:

    +
    +
    +
      +
    • +

      In a block statement,

      +
    • +
    • +

      In a parser state,

      +
    • +
    • +

      In an action body,

      +
    • +
    • +

      In a control block’s apply sub-block,

      +
    • +
    • +

      In the list of local declarations in a parser, and

      +
    • +
    • +

      In the list of local declarations in a control.

      +
    • +
    +
    +
    +

    Variables have local scope, and behave like stack-allocated variables in languages such as C. The value of a variable is never preserved from one invocation of its enclosing block to the next. In particular, -variables cannot be used to maintain state between different network packets. -

    11.3. Instantiations

    -

    Instantiations are similar to variable declarations, +variables cannot be used to maintain state between different network packets.

    +
    +
    +
    +

    11.3. Instantiations

    +
    +

    Instantiations are similar to variable declarations, but are reserved for the types with constructors -(extern objects, control blocks, parsers, and packages): -

    -
    -
    -
    instantiation
    -    : typeRef '(' argumentList ')' name ';'
    -    | annotations typeRef '(' argumentList ')' name ';'
    -    ;
    -

    An instantiation is written as a constructor invocation followed by a name. -Instantiations are always executed at compilation time (Section 18.1). +(extern objects, control blocks, parsers, and packages):

    +
    +
    +
    +
    instantiation
    +    : typeRef '(' argumentList ')' name ';'
    +    | annotations typeRef '(' argumentList ')' name ';'
    +    ;
    +
    +
    +
    +

    An instantiation is written as a constructor invocation followed by a name. +Instantiations are always executed at compilation time (Section Section 18.1). The effect is to allocate an object with the specified name, and to bind it to the result of the constructor invocation. -Note that instantiation arguments can be specified by name. -

    -

    For example, a hypothetical bank of counter -objects can be instantiated as follows: -

    -
    -
    -
    // from target library
    -enum CounterType {
    -   Packets,
    -   Bytes,
    +Note that instantiation arguments can be specified by name.

    +
    +
    +

    For example, a hypothetical bank of counter +objects can be instantiated as follows:

    +
    +
    +
    +
    // from target library
    +enum CounterType {
    +   Packets,
    +   Bytes,
        Both
    -}
    -extern Counter {
    -    Counter(bit<32> size, CounterType type);
    -    void increment(in bit<32> index);
    -}
    -// user program
    -control c(/* parameters omitted */) {
    -    Counter(32w1024, CounterType.Both) ctr;  // instantiation
    -    apply { /* body omitted */ }
    -}

    11.3.1. Instantiating objects with abstract methods

    -

    When instantiating an extern type that has abstract methods users -have to supply implementations for all such methods. This is done using object initializers: -

    -
    -
    -
    lvalue:
    +}
    +extern Counter {
    +    Counter(bit<32> size, CounterType type);
    +    void increment(in bit<32> index);
    +}
    +// user program
    +control c(/* parameters omitted */) {
    +    Counter(32w1024, CounterType.Both) ctr;  // instantiation
    +    apply { /* body omitted */ }
    +}
    +
    +
    +
    +

    11.3.1. Instantiating objects with abstract methods

    +
    +

    When instantiating an extern type that has abstract methods users +have to supply implementations for all such methods. This is done using object initializers:

    +
    +
    +
    +
    lvalue:
         ...
         | THIS
     
    @@ -5924,75 +8454,102 @@ 

    "(" argumentList ")" name "=" objInitializer ";" - | typeRef "(" argumentList ")" name "=" objInitializer ";" + | annotations typeRef "(" argumentList ")" name "=" objInitializer ";" + | typeRef "(" argumentList ")" name "=" objInitializer ";" objInitializer - : "{" objDeclarations "}" + : "{" objDeclarations "}" ; objDeclarations - : /* empty */ + : /* empty */ | objDeclarations objDeclaration ; objDeclaration : functionDeclaration | instantiation - ;

    -

    The abstract methods can only use the supplied arguments or refer to + ; +

    +
    +
    +

    The abstract methods can only use the supplied arguments or refer to values that are in the top-level scope. When calling another method -of the same instance the this keyword is used to indicate the -current object instance: -

    -
    -
    -
    // Instantiate a balancer
    -Balancer() b = {  // provide an implementation for the abstract methods
    -    bit<4> on_new_flow(in bit<32> address) {
    -        // uses the address and the number of flows to balance the load
    -        bit<32> count = this.getFlowCount();  // call method of the same instance
    -        return (address + count)[3:0];
    -    }
    -}
    -

    Abstract methods may be invoked by users explicitly, or they may be +of the same instance the this keyword is used to indicate the +current object instance:

    +
    +
    +
    +
    // Instantiate a balancer
    +Balancer() b = {  // provide an implementation for the abstract methods
    +    bit<4> on_new_flow(in bit<32> address) {
    +        // uses the address and the number of flows to balance the load
    +        bit<32> count = this.getFlowCount();  // call method of the same instance
    +        return (address + count)[3:0];
    +    }
    +}
    +
    +
    +
    +

    Abstract methods may be invoked by users explicitly, or they may be invoked by the target architecture. The architectural description has to specify when the abstract methods are invoked and what the meaning of their arguments and return values is; target architectures may -impose additional constraints on abstract methods. -

    11.3.2. Restrictions on top-level instantiations

    -

    A P4 program may not instantiate controls and parsers in the top-level package. +impose additional constraints on abstract methods.

    +
    +
    +
    +

    11.3.2. Restrictions on top-level instantiations

    +
    +

    A P4 program may not instantiate controls and parsers in the top-level package. This restriction is designed to ensure that most state resides in -the architecture itself, or is local to a parser or control. -For example, the following program is not valid: -

    -
    -
    -
    // Program
    -control c(/* parameters omitted */) { /* body omitted */ }
    -c() c1;  // illegal top-level instantiation
    -

    because control c1 is instantiated at the top-level. Note that top-level declarations of -constants and instantiations of extern objects are permitted. -

    12. Statements

    -

    Every statement in P4 except block statements must end with a semicolon. -Statements can appear in several places: -

    -
      -
    • Within parser states -
    • -
    • Within a control block -
    • -
    • Within an action -
    - -

    There are restrictions for the kinds of statements that can appear in -each of these places. For example, returns are not supported in -parsers, and switch statements are only supported in control -blocks. We present here the most general case, for control blocks. -

    -
    -
    -
    statement
    +the architecture itself, or is local to a parser or control.
    +For example, the following program is not valid:

    +
    +
    +
    +
    // Program
    +control c(/* parameters omitted */) { /* body omitted */ }
    +c() c1;  // illegal top-level instantiation
    +
    +
    +
    +

    because control c1 is instantiated at the top-level. Note that top-level declarations of +constants and instantiations of extern objects are permitted.

    +
    +
    +
    +
    +
    +
    +

    12. Statements

    +
    +
    +

    Every statement in P4 except block statements must end with a semicolon. +Statements can appear in several places:

    +
    +
    +
      +
    • +

      Within parser states

      +
    • +
    • +

      Within a control block

      +
    • +
    • +

      Within an action

      +
    • +
    +
    +
    +

    There are restrictions for the kinds of statements that can appear in +each of these places. For example, returns are not supported in +parsers, and switch statements are only supported in control +blocks. We present here the most general case, for control blocks.

    +
    +
    +
    +
    statement
         : assignmentOrMethodCallStatement
         | conditionalStatement
         | emptyStatement
    @@ -6004,40 +8561,55 @@ 

    "(" argumentList ")" ";" - | lvalue "<" typeArgumentList ">" "(" argumentList ")" ";" - | lvalue "=" expression ";" - ;

    -

    In addition, parsers support a transition statement (Section -13.5). -

    12.1. Assignment statement

    -

    An assignment, written with the = sign, first evaluates its left + : lvalue "(" argumentList ")" ";" + | lvalue "<" typeArgumentList ">" "(" argumentList ")" ";" + | lvalue "=" expression ";" + ; +

    +
    +
    +

    In addition, parsers support a transition statement (Section 13.5).

    +
    +
    +

    12.1. Assignment statement

    +
    +

    An assignment, written with the = sign, first evaluates its left sub-expression to an l-value, then evaluates its right sub-expression to a value, and finally copies the value into the l-value. Derived -types (e.g. structs) are copied recursively, and all components -of headers are copied, including “validity” bits. Assignment is -not defined for extern values. -

    12.2. Empty statement

    -

    The empty statement, written ; is a no-op. -

    -
    -
    -
    emptyStatement
    -    : ";"
    -    ;

    12.3. Block statement

    -

    A block statement is denoted by curly braces. It contains a sequence +types (e.g. structs) are copied recursively, and all components +of headers are copied, including "validity" bits. Assignment is +not defined for extern values.

    +
    +
    +
    +

    12.2. Empty statement

    +
    +

    The empty statement, written ; is a no-op.

    +
    +
    +
    +
    emptyStatement
    +    : ";"
    +    ;
    +
    +
    +
    +
    +

    12.3. Block statement

    +
    +

    A block statement is denoted by curly braces. It contains a sequence of statements and declarations, which are executed sequentially. The declarations (e.g., variables and constants) within a block statement -are only visible within the block. -

    -
    -
    -
    blockStatement
    -    : optAnnotations "{" statOrDeclList "}"
    +are only visible within the block.

    +
    +
    +
    +
    blockStatement
    +    : optAnnotations "{" statOrDeclList "}"
         ;
     
     statOrDeclList
    -    : /* empty */
    +    : /* empty */
         | statOrDeclList statementOrDeclaration
         ;
     
    @@ -6045,130 +8617,181 @@ 

    12.4. Return statement

    -

    The return statement immediately terminates the execution of the -action, function or control containing it. return statements -are not allowed within parsers. return statements followed by an + ; +

    +
    +
    +
    +

    12.4. Return statement

    +
    +

    The return statement immediately terminates the execution of the +action, function or control containing it. return statements +are not allowed within parsers. return statements followed by an expression are only allowed within functions that return values; in this case the type of the expression must match the return type of the -function. Any copy-out behavior due to direction out or inout -parameters of the enclosing action, function, or control are -still performed after the execution of the return statement. See -Section 6.8 for details on copy-out behavior. -

    -
    -
    -
    returnStatement
    -    : RETURN ";"
    -    | RETURN expression ";"
    -    ;

    12.5. Exit statement

    -

    The exit statement immediately terminates the execution of all -the blocks currently executing: the current action (if invoked -within an action), the current control, and all its -callers. exit statements are not allowed within parsers or -functions. -

    -

    Any copy-out behavior due to direction out or inout parameters of -the enclosing action or control, and all of its callers, are still -performed after the execution of the exit statement. See Section -6.8 for details on copy-out behavior. -

    -
    -
    -
    exitStatement
    -    : EXIT ";"
    -    ;
    -

    There are some expressions whose evaluation might cause an exit -statement to be executed. Examples include: -

    -
      -
    • table.apply().action_run, which can only appear as the expression -of a switch statement (see Section 12.7), and when -it appears there, it must be the entire expression. -
    • -
    • Any expression containing table.apply().hit or -table.apply().miss (see Section 14.2.2), which can be +function. Any copy-out behavior due to direction out or inout +parameters of the enclosing action, function, or control are +still performed after the execution of the return statement. See +Section 6.8 for details on copy-out behavior.

      +
    +
    +
    +
    returnStatement
    +    : RETURN ";"
    +    | RETURN expression ";"
    +    ;
    +
    +
    +
    +
    +

    12.5. Exit statement

    +
    +

    The exit statement immediately terminates the execution of all +the blocks currently executing: the current action (if invoked +within an action), the current control, and all its +callers. exit statements are not allowed within parsers or +functions.

    +
    +
    +

    Any copy-out behavior due to direction out or inout parameters of +the enclosing action or control, and all of its callers, are still +performed after the execution of the exit statement. See Section 6.8 for details on copy-out behavior.

    +
    +
    +
    +
    exitStatement
    +    : EXIT ";"
    +    ;
    +
    +
    +
    +

    There are some expressions whose evaluation might cause an exit +statement to be executed. Examples include:

    +
    +
    +
      +
    • +

      table.apply().action_run, which can only appear as the expression +of a switch statement (see Section 12.7), and when +it appears there, it must be the entire expression.

      +
    • +
    • +

      Any expression containing table.apply().hit or +table.apply().miss (see Section 14.2.2), which can be part of arbitrarily complex expressions in many places of a P4 -program, such as: - -

        -
      • the expression in an if statement. -
      • -
      • the expression e1 in a conditional expression e1 ? e2 : e3. -
      • -
      • in an assignment statement, in the left and/or right hand sides. -
      • -
      • an argument passed to a function or method call. -
      • -
      • an expression to calculate a table key (see Section -14.2.3). -
    - -

    This list is not intended to be exhaustive. -

    -

    If applying the table causes an action to be executed, which in turn -causes an exit statement to be executed, then evaluation of the +program, such as:

    +
    +
      +
    • +

      the expression in an if statement.

      +
    • +
    • +

      the expression e1 in a conditional expression e1 ? e2 : e3.

      +
    • +
    • +

      in an assignment statement, in the left and/or right hand sides.

      +
    • +
    • +

      an argument passed to a function or method call.

      +
    • +
    • +

      an expression to calculate a table key (see Section 14.2.3).

      +
    • +
    +
    + + +
    +
    +

    This list is not intended to be exhaustive.

    +
    +
    +

    If applying the table causes an action to be executed, which in turn +causes an exit statement to be executed, then evaluation of the expression ends immediately, and the rest of the current expression or -statement does not complete its execution. See Section -8.1 for the order of evaluation of the parts of an +statement does not complete its execution. See Section 8.1 for the order of evaluation of the parts of an expression. For the examples listed above, it also means the -following behavior after the expression evaluation is interrupted. -

    -
      -
    • For a switch statement, if table.apply() exits, then none of the -blocks in the switch statement are executed. -
    • -
    • If table.apply().hit or table.apply().miss cause an exit during -the evaluation of an expression: - -
        -
      • If it is the expression of an if statement, then neither the -‘then’ nor ‘else’ branches of the if statement are executed. -
      • -
      • If it is the expression e1 in a conditional expression e1 ? e2 : e3, -then neither expression e2 nor e3 are evaluated. -
      • -
      • If the expression is the right hand side of an assignment +following behavior after the expression evaluation is interrupted.

        +
    +
    +
      +
    • +

      For a switch statement, if table.apply() exits, then none of the +blocks in the switch statement are executed.

      +
    • +
    • +

      If table.apply().hit or table.apply().miss cause an exit during +the evaluation of an expression:

      +
      +
        +
      • +

        If it is the expression of an if statement, then neither the +'then' nor 'else' branches of the if statement are executed.

        +
      • +
      • +

        If it is the expression e1 in a conditional expression e1 ? e2 : e3, +then neither expression e2 nor e3 are evaluated.

        +
      • +
      • +

        If the expression is the right hand side of an assignment statement, or part of the calculation of the L-value on the left hand side (e.g. the index expression of a header stack reference), -then no assignment occurs. -

      • -
      • If the expression is an argument passed to a function or method -call, then the function/method call does not occur. -
      • -
      • If the expression is a table key, then the table is not applied. -
    -

    12.6. Conditional statement

    -

    The conditional statement uses standard syntax and semantics familiar from many programming languages. -

    -

    However, the condition expression in P4 is required to be a Boolean -(and not an integer). -

    -
    -
    -
    conditionalStatement
    -    : IF "(" expression ")" statement
    -    | IF "(" expression ")" statement ELSE statement
    -    ;
    -

    When several if statements are nested, the else applies to the -innermost if statement that does not have an else statement. -

    12.7. Switch statement

    -

    The switch statement can only be used within control blocks. -

    -
    -
    -
    switchStatement
    -    : SWITCH "(" expression ")" "{" switchCases "}"
    +then no assignment occurs.

    + +
  • +

    If the expression is an argument passed to a function or method +call, then the function/method call does not occur.

    +
  • +
  • +

    If the expression is a table key, then the table is not applied.

    +
  • + +
    + + +
    +
    +
    +

    12.6. Conditional statement

    +
    +

    The conditional statement uses standard syntax and semantics familiar from many programming languages.

    +
    +
    +

    However, the condition expression in P4 is required to be a Boolean +(and not an integer).

    +
    +
    +
    +
    conditionalStatement
    +    : IF "(" expression ")" statement
    +    | IF "(" expression ")" statement ELSE statement
    +    ;
    +
    +
    +
    +

    When several if statements are nested, the else applies to the +innermost if statement that does not have an else statement.

    +
    +
    +
    +

    12.7. Switch statement

    +
    +

    The switch statement can only be used within control blocks.

    +
    +
    +
    +
    switchStatement
    +    : SWITCH "(" expression ")" "{" switchCases "}"
         ;
     
     switchCases
    -    : /* empty */
    +    : /* empty */
         | switchCases switchCase
         ;
     
     switchCase
    -    : switchLabel ":" blockStatement
    -    | switchLabel ":"  // fall-through
    +    : switchLabel ":" blockStatement
    +    | switchLabel ":"  // fall-through
         ;
     
     switchLabel
    @@ -6183,333 +8806,437 @@ 

    "[" expression "]" - | nonBraceExpression "[" expression ":" expression "]" - | "(" expression ")" - | "!" expression %prec PREFIX - | "~" expression %prec PREFIX - | "-" expression %prec PREFIX - | "+" expression %prec PREFIX - | typeName "." member - | ERROR "." member - | nonBraceExpression "." member - | nonBraceExpression "*" expression - | nonBraceExpression "/" expression - | nonBraceExpression "%" expression - | nonBraceExpression "+" expression - | nonBraceExpression "-" expression - | nonBraceExpression "|+|" expression - | nonBraceExpression "|-|" expression - | nonBraceExpression "<<" expression - | nonBraceExpression ">>" expression - | nonBraceExpression "<=" expression - | nonBraceExpression ">=" expression - | nonBraceExpression "<" expression - | nonBraceExpression ">" expression - | nonBraceExpression "!=" expression - | nonBraceExpression "==" expression - | nonBraceExpression "&" expression - | nonBraceExpression "^" expression - | nonBraceExpression "|" expression - | nonBraceExpression "++" expression - | nonBraceExpression "&&" expression - | nonBraceExpression "||" expression - | nonBraceExpression "?" expression ":" expression - | nonBraceExpression "<" realTypeArgumentList ">" "(" argumentList ")" - | nonBraceExpression "(" argumentList ")" - | namedType "(" argumentList ")" - | "(" typeRef ")" expression - ;

    -

    The nonBraceExpression is the same as expression as defined in -Section 8, except it does not include any cases that can -begin with a left brace { character, to avoid syntactic ambiguity -with a block statement. -

    -

    There are two kinds of switch expressions allowed, described -separately in the following two subsections. -

    12.7.1. Switch statement with action_run expression

    -

    For this variant of switch statement, the expression must be of the -form t.apply().action_run, where t is the name of a table (see -Section 14.2.2). All switch labels must be names of -actions of the table t, or default. -

    -
    -
    -
    switch (t.apply().action_run) {
    -   action1:          // fall-through to action2:
    -   action2: { /* body omitted */ }
    -   action3: { /* body omitted */ }  // no fall-through from action2 to action3 labels
    -   default: { /* body omitted */ }
    -}
    -

    Note that the default label of the switch statement is used to + | nonBraceExpression "[" expression "]" + | nonBraceExpression "[" expression ":" expression "]" + | "(" expression ")" + | "!" expression %prec PREFIX + | "~" expression %prec PREFIX + | "-" expression %prec PREFIX + | "+" expression %prec PREFIX + | typeName "." member + | ERROR "." member + | nonBraceExpression "." member + | nonBraceExpression "*" expression + | nonBraceExpression "/" expression + | nonBraceExpression "%" expression + | nonBraceExpression "+" expression + | nonBraceExpression "-" expression + | nonBraceExpression "|+|" expression + | nonBraceExpression "|-|" expression + | nonBraceExpression "<<" expression + | nonBraceExpression ">>" expression + | nonBraceExpression "<=" expression + | nonBraceExpression ">=" expression + | nonBraceExpression "<" expression + | nonBraceExpression ">" expression + | nonBraceExpression "!=" expression + | nonBraceExpression "==" expression + | nonBraceExpression "&" expression + | nonBraceExpression "^" expression + | nonBraceExpression "|" expression + | nonBraceExpression "++" expression + | nonBraceExpression "&&" expression + | nonBraceExpression "||" expression + | nonBraceExpression "?" expression ":" expression + | nonBraceExpression "<" realTypeArgumentList ">" "(" argumentList ")" + | nonBraceExpression "(" argumentList ")" + | namedType "(" argumentList ")" + | "(" typeRef ")" expression + ; +

    +
    +
    +

    The nonBraceExpression is the same as expression as defined in +Chapter 8, except it does not include any cases that can +begin with a left brace { character, to avoid syntactic ambiguity +with a block statement.

    +
    +
    +

    There are two kinds of switch expressions allowed, described +separately in the following two subsections.

    +
    +
    +

    12.7.1. Switch statement with action_run expression

    +
    +

    For this variant of switch statement, the expression must be of the +form t.apply().action_run, where t is the name of a table (see +Section 14.2.2). All switch labels must be names of +actions of the table t, or default.

    +
    +
    +
    +
    switch (t.apply().action_run) {
    +   action1:          // fall-through to action2:
    +   action2: { /* body omitted */ }
    +   action3: { /* body omitted */ }  // no fall-through from action2 to action3 labels
    +   default: { /* body omitted */ }
    +}
    +
    +
    +
    +

    Note that the default label of the switch statement is used to match on the kind of action executed, no matter whether there was a -table hit or miss. The default label does not indicate that the -table missed and the default_action was executed. -

    12.7.2. Switch statement with integer or enumerated type expression

    -

    For this variant of switch statement, the expression must evaluate -to a result with one of these types: -

    -
      -
    • bit<W> -
    • -
    • int<W> -
    • -
    • enum, either with or without an underlying representation specified -
    • -
    • error -
    - -

    All switch labels must be expressions with compile-time known values, +table hit or miss. The default label does not indicate that the +table missed and the default_action was executed.

    +
    +
    +
    +

    12.7.2. Switch statement with integer or enumerated type expression

    +
    +

    For this variant of switch statement, the expression must evaluate +to a result with one of these types:

    +
    +
    +
      +
    • +

      bit<W>

      +
    • +
    • +

      int<W>

      +
    • +
    • +

      enum, either with or without an underlying representation specified

      +
    • +
    • +

      error

      +
    • +
    +
    +
    +

    All switch labels must be expressions with compile-time known values, and must have a type that can be implicitly cast to the type of the -switch expression (see Section 8.11.2). Switch -labels must not begin with a left brace character {, to avoid -ambiguity with a block statement. -

    -
    -
    -
    // Assume the expression hdr.ethernet.etherType has type bit<16>
    -switch (hdr.ethernet.etherType) {
    -   0x86dd: { /* body omitted */ }
    -   0x0800:          // fall-through to the next body
    -   0x0802: { /* body omitted */ }
    -   0xcafe: { /* body omitted */ }
    -   default: { /* body omitted */ }
    -}

    12.7.3. Notes common to all switch statements

    -

    It is a compile-time error if two labels of a switch statement equal +switch expression (see Section 8.11.2). Switch +labels must not begin with a left brace character {, to avoid +ambiguity with a block statement.

    +
    +
    +
    +
    // Assume the expression hdr.ethernet.etherType has type bit<16>.
    +switch (hdr.ethernet.etherType) {
    +   0x86dd: { /* body omitted */ }
    +   0x0800:          // fall-through to the next body
    +   0x0802: { /* body omitted */ }
    +   0xcafe: { /* body omitted */ }
    +   default: { /* body omitted */ }
    +}
    +
    +
    +
    +
    +

    12.7.3. Notes common to all switch statements

    +
    +

    It is a compile-time error if two labels of a switch statement equal each other. The switch label values need not include all possible -values of the switch expression. It is optional to have a switch -case with the default label, but if one is present, it must be the -last one in the switch statement. -

    -

    If a switch label is not followed by a block statement, it falls +values of the switch expression. It is optional to have a switch +case with the default label, but if one is present, it must be the +last one in the switch statement.

    +
    +
    +

    If a switch label is not followed by a block statement, it falls through to the next label. However, if a block statement is present, it does not fall through. Note that this is different from C-style -switch statements, where a break is needed to prevent +switch statements, where a break is needed to prevent fall-through. If the last switch label is not followed by a block statement, the behavior is the same as if the last switch label were -followed by an empty block statement { }. -

    -

    When a switch statement is executed, first the switch expression is +followed by an empty block statement { }.

    +
    +
    +

    When a switch statement is executed, first the switch expression is evaluated, and any side effects from evaluating this expression are -visible to any switch case that is executed. Among switch labels -that are not default, at most one of them can equal the value of the -switch expression. If one is equal, that switch case is executed. -

    -

    If no labels are equal to the switch expression, then: -

    -
      -
    • if there is a default label, the case with the default label is -executed. -
    • -
    • if there is no default label, then no switch case is executed, and -execution continues after the end of the switch statement, with no -side effects (except any that were caused by evaluating the switch -expression). -
    - -

    See “Implementing generalized P4_16 switch statements” -GeneralizedSwitchStatements for possible techniques that one might -use to implement generalized switch statements. -

    12.8. Loop statement

    -

    A loop statement executes a statement zero or more times, based on a condition -or a range or collection. -

    -
    -
    -
    
    -loopStatement
    -    : optAnnotations FOR "(" optForInitStatements ";" expression ";"
    -      optForUpdateStatements ")" statement
    -    | optAnnotations FOR "(" typeRef name IN forCollectionExpr ")" statement
    +visible to any switch case that is executed.  Among switch labels
    +that are not default, at most one of them can equal the value of the
    +switch expression. If one is equal, that switch case is executed.

    +
    +
    +

    If no labels are equal to the switch expression, then:

    +
    +
    +
      +
    • +

      if there is a default label, the case with the default label is +executed.

      +
    • +
    • +

      if there is no default label, then no switch case is executed, and +execution continues after the end of the switch statement, with no +side effects (except any that were caused by evaluating the switch +expression).

      +
    • +
    +
    +
    +

    See "Implementing generalized P4_16 switch statements" +for possible techniques that one might +use to implement generalized switch statements.[1]

    +
    +
    +
    +
    +

    12.8. Loop statement

    +
    +

    A loop statement executes a statement zero or more times, based on a +condition or a range or collection.

    +
    +
    +
    +
    loopStatement
    +    : optAnnotations FOR "(" optForInitStatements ";" expression ";"
    +      optForUpdateStatements ")" statement
    +    | optAnnotations FOR "(" typeRef name IN forCollectionExpr ")" statement
         ;
     
     forInitStatement
         : optAnnotations typeRef name optInitializer
    -    | lvalue "(" argumentList ")"
    -    | lvalue "<" typeArgumentList ">" "(" argumentList ")"
    -    | lvalue "=" expression
    +    | lvalue "(" argumentList ")"
    +    | lvalue "<" typeArgumentList ">" "(" argumentList ")"
    +    | lvalue "=" expression
         ;
     
     forUpdateStatement
    -    : lvalue "(" argumentList ")"
    -    | lvalue "<" typeArgumentList ">" "(" argumentList ")"
    -    | lvalue "=" expression
    -    ;
    -
    -

    The basic 3-clause for statement is similar to a C for statement. The init statements + : lvalue "(" argumentList ")" + | lvalue "<" typeArgumentList ">" "(" argumentList ")" + | lvalue "=" expression + ; +

    +
    +
    +

    The basic 3-clause for statement is similar to a C for statement. The init statements will be executed prior to executing the loop. The condition will be evaluated before each iteration, to determine if the loop should exit. If the condition is false, the loop will exit without executing any statements from the loop body or update. The update statements will be executed after the loop body and before evaluating the -condition again for the next iteration. -

    -

    The for-in statement executes the body once for each value in a range or each +condition again for the next iteration.

    +
    +
    +

    The for-in statement executes the body once for each value in a range or each element in a list expression or header stack. The list or range expression itself will only be evaluated once, before the first iteration of the loop. All side effects -in the list or range expression will occur before the first iteration of the loop body. -

    -

    A break; statement may be executed in a loop body to immediately exit the loop, -without executing the rest of the body or the update statements. -

    -

    A continue; statement may be executed in a loop body to skip the rest of the current +in the list or range expression will occur before the first iteration of the loop body.

    +
    +
    +

    A break; statement may be executed in a loop body to immediately exit the loop, +without executing the rest of the body or the update statements.

    +
    +
    +

    A continue; statement may be executed in a loop body to skip the rest of the current loop body, executing the update statements and then evaluating the condition to -either execute the loop body again, or terminate the loop. -

    -

    The scope of any declaration in a for statement is limited to the for statement and its body. -

    13. Packet parsing

    -

    This section describes the P4 constructs specific to parsing network packets. -

    13.1. Parser states

    -
    -

    parserstatemachine -

    -
    - -
    Figure 8. Parser FSM structure.
    -

    -A P4 parser describes a state machine with one start state and two -final states. The start state is always named start. The two -final states are named accept (indicating successful parsing) -and reject (indicating a parsing failure). The start state -is part of the parser, while the accept and reject states -are distinct from the states provided by the programmer and are logically outside of the parser. Figure 8 -illustrates the general structure of a parser state machine. -

    13.2. Parser declarations

    -

    A parser declaration comprises a name, a list of parameters, an +either execute the loop body again, or terminate the loop.

    +
    +
    +

    The scope of any declaration in a for statement is limited to the for statement and its body.

    +
    +
    +

    >>>>>>> upstream/main:p4-16/spec/P4-16-spec.mdk

    +
    +
    +
    +
    +
    +

    13. Packet parsing

    +
    +
    +

    This section describes the P4 constructs specific to parsing network packets.

    +
    +
    +

    13.1. Parser states

    +
    +
    +parserstatemachine +
    +
    Figure 8. Parser FSM structure.
    +
    +
    +

    A P4 parser describes a state machine with one start state and two +final states. The start state is always named start. The two +final states are named accept (indicating successful parsing) +and reject (indicating a parsing failure). The start state +is part of the parser, while the accept and reject states +are distinct from the states provided by the programmer and are logically outside of the parser. Figure 8 +illustrates the general structure of a parser state machine.

    +
    +
    +
    +

    13.2. Parser declarations

    +
    +

    A parser declaration comprises a name, a list of parameters, an optional list of constructor parameters, local elements, and parser -states (as well as optional annotations). -

    -
    -
    -
    parserTypeDeclaration
    +states (as well as optional annotations).

    +
    +
    +
    +
    parserTypeDeclaration
         : optAnnotations PARSER name optTypeParameters
    -      "(" parameterList ")"
    +      "(" parameterList ")"
         ;
     
     parserDeclaration
         : parserTypeDeclaration optConstructorParameters
    -      "{" parserLocalElements parserStates "}"
    +      "{" parserLocalElements parserStates "}"
         ;
     
     parserLocalElements
    -    : /* empty */
    +    : /* empty */
         | parserLocalElements parserLocalElement
         ;
     
     parserStates
         : parserState
         | parserStates parserState
    -    ;
    -

    For a description of optConstructorParameters, which are useful for -building parameterized parsers, see Section 15. -

    -

    Unlike parser type declarations, parser declarations may not be -generice.g., the following declaration is illegal: -

    -
    -
    -
    parser P<H>(inout H data) { /* body omitted */ }
    -

    Hence, used in the context of a parserDeclaration the production -rule parserTypeDeclaration should not yield type parameters. -

    -

    At least one state, named start, must be present in any parser. + ; +

    +
    +
    +

    For a description of optConstructorParameters, which are useful for +building parameterized parsers, see Chapter 15.

    +
    +
    +

    Unlike parser type declarations, parser declarations may not be +generic---e.g., the following declaration is illegal:

    +
    +
    +
    +
    parser P<H>(inout H data) { /* body omitted */ }
    +
    +
    +
    +

    Hence, used in the context of a parserDeclaration the production +rule parserTypeDeclaration should not yield type parameters.

    +
    +
    +

    At least one state, named start, must be present in any parser. A parser may not define two states with the same name. It is also illegal for a parser to give explicit definitions for -the accept and reject statesthose -states are logically distinct from the states defined by the programmer. -

    -

    State declarations are described below. Preceding the parser states, a parser +the accept and reject states---those +states are logically distinct from the states defined by the programmer.

    +
    +
    +

    State declarations are described below. Preceding the parser states, a parser may also contain a list of local elements. These can be constants, variables, or instantiations of objects that may be used within the -parser. Such objects may be instantiations of extern objects, or -other parsers that may be invoked as subroutines. However, it is illegal to -instantiate a control block within a parser. -

    -
    -
    -
    parserLocalElement
    +parser. Such objects may be instantiations of extern objects, or
    +other parsers that may be invoked as subroutines. However, it is illegal to
    +instantiate a control block within a parser.

    +
    +
    +
    +
    parserLocalElement
         : constantDeclaration
         | instantiation
         | variableDeclaration
         | valueSetDeclaration
    -    ;
    -

    The states and local elements are all in the same namespace, thus the -following example will produce an error: -

    -
    -
    -
    // erroneous example
    -parser p() {
    -    bit<4> t;
    -    state start {
    -       t = 1;
    -       transition t;
    -    }
    -    state t {  // error: name t is duplicated
    -       transition accept;
    -    }
    -}
    -

    For an example containing a complete declaration of a parser see -Section 5.3. -

    13.3. The Parser abstract machine

    -

    The semantics of a P4 parser can be formulated in terms of an abstract -machine that manipulates a ParserModel data structure. This section describes -this abstract machine in pseudo-code. -

    -

    A parser starts execution in the start state and ends execution -when one of the reject or accept states has been reached. -

    -
    -
    -
    ParserModel {
    -    error       parseError;
    +    ;
    +
    +
    +
    +

    The states and local elements are all in the same namespace, thus the +following example will produce an error:

    +
    +
    +
    +
    // erroneous example
    +parser p() {
    +    bit<4> t;
    +    state start {
    +       t = 1;
    +       transition t;
    +    }
    +    state t {  // error: name t is duplicated
    +       transition accept;
    +    }
    +}
    +
    +
    +
    +

    For an example containing a complete declaration of a parser see +Section 5.3.

    +
    +
    +
    +

    13.3. The Parser abstract machine

    +
    +

    The semantics of a P4 parser can be formulated in terms of an abstract +machine that manipulates a ParserModel data structure. This section describes +this abstract machine in pseudo-code.

    +
    +
    +

    A parser starts execution in the start state and ends execution +when one of the reject or accept states has been reached.

    +
    +
    +
    +
    ParserModel {
    +    error       parseError;
         onPacketArrival(packet p) {
    -        ParserModel.parseError = error.NoError;
    +        ParserModel.parseError = error.NoError;
             goto start;
         }
    -}
    -

    An architecture must specify the behavior when the accept -and reject states are reached. For example, an architecture may -specify that all packets reaching the reject state are dropped +} +

    +
    +
    +

    An architecture must specify the behavior when the accept +and reject states are reached. For example, an architecture may +specify that all packets reaching the reject state are dropped without further processing. Alternatively, it may specify that such packets are passed to the next block after the parser, with intrinsic metadata indicating that the parser reached -the reject state, along with the error recorded. -

    13.4. Parser states

    -

    A parser state is declared with the following syntax: -

    -
    -
    -
    parserState
    +the reject state, along with the error recorded.

    +
    +
    +
    +

    13.4. Parser states

    +
    +

    A parser state is declared with the following syntax:

    +
    +
    +
    +
    parserState
         : optAnnotations STATE name
    -      "{" parserStatements transitionStatement "}"
    -    ;
    -

    Each state has a name and a body. The body consists of a sequence of + "{" parserStatements transitionStatement "}" + ; +

    +
    +
    +

    Each state has a name and a body. The body consists of a sequence of statements that describe the processing performed when the parser -transitions to that state, including: -

    -
      -
    • Local variable declarations, -
    • -
    • Assignment statements, -
    • -
    • Method calls, which serve several purposes: - -
        -
      • Invoking functions (e.g., using verify to check the validity of data already parsed), and -
      • -
      • Invoking methods (e.g., extracting data out of packets or computing checksums) and other parsers (see Section 13.10), and -
    • -
    • Conditional statements, -
    • -
    • Transitions to other states (discussed in Section 13.5). -
    - -

    The syntax for parser statements is given by the following grammar rules: -

    -
    -
    -
    parserStatements
    -    : /* empty */
    +transitions to that state, including:

    +
    +
    +
      +
    • +

      Local variable declarations,

      +
    • +
    • +

      Assignment statements,

      +
    • +
    • +

      Method calls, which serve several purposes:

      +
      +
        +
      • +

        Invoking functions (e.g., using verify to check the validity of data already parsed), and

        +
      • +
      • +

        Invoking methods (e.g., extracting data out of packets or computing checksums) and other parsers (see Section 13.10), and

        +
      • +
      +
      +
    • +
    • +

      Conditional statements,

      +
    • +
    • +

      Transitions to other states (discussed in Section 13.5).

      +
    • +
    +
    +
    +

    The syntax for parser statements is given by the following grammar rules:

    +
    +
    +
    +
    parserStatements
    +    : /* empty */
         | parserStatements parserStatement
         ;
     
    @@ -6524,636 +9251,803 @@ 

    "{" parserStatements "}" - ;

    -

    Architectures may place restrictions on the expressions and statements -that can be used in a parsere.g., they may forbid the use of + : optAnnotations "{" parserStatements "}" + ; +

    +
    +
    +

    Architectures may place restrictions on the expressions and statements +that can be used in a parser---e.g., they may forbid the use of operations such as multiplication or place restrictions on the number -of local variables that may be used. -

    -

    In terms of the ParserModel, the sequence of statements in a state -are executed sequentially. -

    13.5. Transition statements

    -

    The last statement in a parser state is an optional transition -statement, which transfers control to another state, possibly accept -or reject. A transition statements is written using the -following syntax: -

    -
    -
    -
    transitionStatement
    -    : /* empty */
    +of local variables that may be used.

    +
    +
    +

    In terms of the ParserModel, the sequence of statements in a state +are executed sequentially.

    +
    +
    +
    +

    13.5. Transition statements

    +
    +

    The last statement in a parser state is an optional transition +statement, which transfers control to another state, possibly accept +or reject. A transition statements is written using the +following syntax:

    +
    +
    +
    +
    transitionStatement
    +    : /* empty */
         | TRANSITION stateExpression
         ;
     
     stateExpression
    -    : name ";"
    +    : name ";"
         | selectExpression
    -    ;
    -

    The execution of the transition statement causes stateExpression -to be evaluated, and transfers control to the resulting state. -

    -

    In terms of the ParserModel, the semantics of a transition -statement can be formalized as follows: -

    -
    -
    -
    goto eval(stateExpression)
    -

    For example, this statement: -

    -
    -
    -
    transition accept;
    -

    terminates execution of the current parser and transitions immediately to -the accept state. -

    -

    If the body of a state block does not end with a transition -statement, the implied statement is -

    -
    -
    -
    transition reject;

    13.6. Select expressions

    -

    A select expression evaluates to a state. The syntax for a select -expression is as follows: -

    -
    -
    -
    selectExpression
    -    : SELECT "(" expressionList ")" "{" selectCaseList "}"
    +    ;
    +
    +
    +
    +

    The execution of the transition statement causes stateExpression +to be evaluated, and transfers control to the resulting state.

    +
    +
    +

    In terms of the ParserModel, the semantics of a transition +statement can be formalized as follows:

    +
    +
    +
    +
    goto eval(stateExpression)
    +
    +
    +
    +

    For example, this statement:

    +
    +
    +
    +
    transition accept;
    +
    +
    +
    +

    terminates execution of the current parser and transitions immediately to +the accept state.

    +
    +
    +

    If the body of a state block does not end with a transition +statement, the implied statement is

    +
    +
    +
    +
    transition reject;
    +
    +
    +
    +
    +

    13.6. Select expressions

    +
    +

    A select expression evaluates to a state. The syntax for a select +expression is as follows:

    +
    +
    +
    +
    selectExpression
    +    : SELECT "(" expressionList ")" "{" selectCaseList "}"
         ;
     
     selectCaseList
    -    : /* empty */
    +    : /* empty */
         | selectCaseList selectCase
         ;
     
     selectCase
    -    : keysetExpression ":" name ";"
    -    ;
    -

    Each expression in the expressionList must have a type of bit<W>, -int<W>, bool, enum, serializable enum, or a tuple type with -fields of one of the above types. -

    -

    In a select expression, if the expressionList has type tuple<T>, -then each keysetExpression must have type set<tuple<T>>. In + : keysetExpression ":" name ";" + ; +

    +
    +
    +

    Each expression in the expressionList must have a type of bit<W>, +int<W>, bool, enum, serializable enum, or a tuple type with +fields of one of the above types.

    +
    +
    +

    In a select expression, if the expressionList has type tuple<T>, +then each keysetExpression must have type set<tuple<T>>. In particular, if a set is specified as a range or mask expression, the endpoints of the range and mask expression are implicitly cast to type -T using the standard rules for casts. -

    -

    In terms of the ParserModel, the meaning of a select expression: -

    -
    -
    -
    select(e) {
    -    ks[0]: s[0];
    -    ks[1]: s[1];
    -    /* more labels omitted */
    -    ks[n-2]: s[n-1];
    -    _ : sd;  // ks[n-1] is default
    -}
    -

    is defined in pseudo-code as: -

    -
    -
    -
    key = eval(e);
    -for (int i=0; i < n; i++) {
    +T using the standard rules for casts.

    +
    +
    +

    In terms of the ParserModel, the meaning of a select expression:

    +
    +
    +
    +
    select(e) {
    +    ks[0]: s[0];
    +    ks[1]: s[1];
    +    /* more labels omitted */
    +    ks[n-2]: s[n-1];
    +    _ : sd;  // ks[n-1] is default
    +}
    +
    +
    +
    +

    is defined in pseudo-code as:

    +
    +
    +
    +
    key = eval(e);
    +for (int i=0; i < n; i++) {
         keyset = eval(ks[i]);
    -    if (keyset.contains(key)) return s[i];
    +    if (keyset.contains(key)) return s[i];
     }
    -verify(false, error.NoMatch);
    -

    Some targets may require that all keyset expressions in a select +verify(false, error.NoMatch); +

    +
    +
    +

    Some targets may require that all keyset expressions in a select expression be compile-time known values. Keysets are evaluated in order, from top to bottom as implied by the pseudo-code above; the -first keyset that includes the value in the select argument +first keyset that includes the value in the select argument provides the result state. If no label matches, the execution triggers -a runtime error with the standard error code error.NoMatch. -

    -

    Note that this implies that all cases after a default or _ +a runtime error with the standard error code error.NoMatch.

    +
    +
    +

    Note that this implies that all cases after a default or _ label are unreachable; the compiler should emit a warning if it detects unreachable cases. -This constitutes an important difference between select -expressions and the switch statements found in many programming languages since the keysets of -a select expression may “overlap”. -

    -

    The typical way to use a select expression is to compare the value +This constitutes an important difference between select +expressions and the switch statements found in many programming languages since the keysets of +a select expression may "overlap".

    +
    +
    +

    The typical way to use a select expression is to compare the value of a recently-extracted header field against a set of values, as in -the following example: -

    -
    -
    -
    header IPv4_h { bit<8> protocol; /* more fields omitted */ }
    -struct P { IPv4_h ipv4; /* more fields omitted */ }
    -P headers;
    -select (headers.ipv4.protocol) {
    -    8w6  : parse_tcp;
    -    8w17 : parse_udp;
    -    _    : accept;
    -}
    -

    For example, to detect TCP reserved ports (< 1024) one could write: -

    -
    -
    -
    select (p.tcp.port) {
    -    16w0 &&& 16w0xFC00: well_known_port;
    -    _: other_port;
    -}
    -

    The expression 16w0 &&& 16w0xFC00 describes the set of 16-bit -values whose most significant six bits are zero. -

    -

    Some targets may support parser value sets; see Section 13.11. Given -a type T for the type parameter of the value set, the type of the value set -is set<T>. The type of the value set must match to the type of all other -keysetExpressions in the same select expression. If there is a mismatch, the +the following example:

    +
    +
    +
    +
    header IPv4_h { bit<8> protocol; /* more fields omitted */ }
    +struct P { IPv4_h ipv4; /* more fields omitted */ }
    +P headers;
    +select (headers.ipv4.protocol) {
    +    8w6  : parse_tcp;
    +    8w17 : parse_udp;
    +    _    : accept;
    +}
    +
    +
    +
    +

    For example, to detect TCP reserved ports (< 1024) one could write:

    +
    +
    +
    +
    select (p.tcp.port) {
    +    16w0 &&& 16w0xFC00: well_known_port;
    +    _: other_port;
    +}
    +
    +
    +
    +

    The expression 16w0 &&& 16w0xFC00 describes the set of 16-bit +values whose most significant six bits are zero.

    +
    +
    +

    Some targets may support parser value sets; see Section 13.11. Given +a type T for the type parameter of the value set, the type of the value set +is set<T>. The type of the value set must match to the type of all other +keysetExpressions in the same select expression. If there is a mismatch, the compiler must raise an error. The type of the values in the set must be either -bit<>, int<>, tuple, struct, or serializable enum. -

    -

    For example, to allow the control plane API to specify TCP reserved ports at -runtime, one could write: -

    -
    -
    -
    struct vsk_t {
    -    @match(ternary)
    -    bit<16> port;
    -}
    -value_set<vsk_t>(4) pvs;
    -select (p.tcp.port) {
    -    pvs: runtime_defined_port;
    -    _: other_port;
    -}
    -

    The above example allows the runtime API to populate up to 4 different -keysetExpressions in the value_set. If the value_set takes a struct +bit<>, int<>, tuple, struct, or serializable enum.

    +
    +
    +

    For example, to allow the control plane API to specify TCP reserved ports at +runtime, one could write:

    +
    +
    +
    +
    struct vsk_t {
    +    @match(ternary)
    +    bit<16> port;
    +}
    +value_set<vsk_t>(4) pvs;
    +select (p.tcp.port) {
    +    pvs: runtime_defined_port;
    +    _: other_port;
    +}
    +
    +
    +
    +

    The above example allows the runtime API to populate up to 4 different +keysetExpressions in the value_set. If the value_set takes a struct as type parameter, the runtime API can use the struct field names to name the objects in the value set. The match type of the struct field is -specified with the @match annotation. If the @match annotation is not -specified on a struct field, by default it is assumed to be @match(exact). +specified with the @match annotation. If the @match annotation is not +specified on a struct field, by default it is assumed to be @match(exact). A single non-exact field must be placed into a struct by itself, with the -desired @match annotation. -

    13.7. verify

    -

    The verify statement provides a simple form of error -handling. verify can only be invoked within a parser; it is used -syntactically as if it were a function with the following signature: -

    -
    -
    -
    extern void verify(in bool condition, in error err);
    -

    If the first argument is true, then executing the statement -has no side-effect. However, if the first argument is false, it causes -an immediate transition to reject, which causes -immediate parsing termination; at the same time, the parserError -associated with the parser is set to the value of the second argument. -

    -

    In terms of the ParserModel the semantics of a verify -statement is given by: -

    -
    -
    -
    ParserModel.verify(bool condition, error err) {
    -    if (condition == false) {
    +desired @match annotation.

    +
    +
    +
    +

    13.7. verify

    +
    +

    The verify statement provides a simple form of error +handling. verify can only be invoked within a parser; it is used +syntactically as if it were a function with the following signature:

    +
    +
    +
    +
    extern void verify(in bool condition, in error err);
    +
    +
    +
    +

    If the first argument is true, then executing the statement +has no side-effect. However, if the first argument is false, it causes +an immediate transition to reject, which causes +immediate parsing termination; at the same time, the parserError +associated with the parser is set to the value of the second argument.

    +
    +
    +

    In terms of the ParserModel the semantics of a verify +statement is given by:

    +
    +
    +
    +
    ParserModel.verify(bool condition, error err) {
    +    if (condition == false) {
             ParserModel.parserError = err;
             goto reject;
         }
    -}

    13.8. Data extraction

    -

    The P4 core library contains the following declaration of a built-in extern -type called packet_in that represents incoming network -packets. The packet_in extern is special: it cannot be +} +

    +
    +
    +
    +

    13.8. Data extraction

    +
    +

    The P4 core library contains the following declaration of a built-in extern +type called packet_in that represents incoming network +packets. The packet_in extern is special: it cannot be instantiated by the user explicitly. Instead, the architecture -supplies a separate instance for each packet_in argument to -a parser instantiation. -

    -
    -
    -
    extern packet_in {
    -    void extract<T>(out T headerLvalue);
    -    void extract<T>(out T variableSizeHeader, in bit<32> varFieldSizeBits);
    -    T lookahead<T>();
    -    bit<32> length();  // This method may be unavailable in some architectures
    -    void advance(bit<32> bits);
    -}
    -

    To extract data from a packet represented by an argument b with -type packet_in, a parser invokes the extract methods of b. -There are two variants of the extract method: a one-argument +supplies a separate instance for each packet_in argument to +a parser instantiation.

    +
    +
    +
    +
    extern packet_in {
    +    void extract<T>(out T headerLvalue);
    +    void extract<T>(out T variableSizeHeader, in bit<32> varFieldSizeBits);
    +    T lookahead<T>();
    +    bit<32> length();  // This method may be unavailable in some architectures
    +    void advance(bit<32> bits);
    +}
    +
    +
    +
    +

    To extract data from a packet represented by an argument b with +type packet_in, a parser invokes the extract methods of b. +There are two variants of the extract method: a one-argument variant for extracting fixed-size headers, and a two-argument variant for extracting variable-sized headers. Because these operations can cause runtime verification failures (see below), these methods can -only be executed within parsers. -

    -

    When extracting data into a bit-string or integer, the first packet -bit is extracted to the most significant bit of the integer. -

    -

    Some targets may perform cut-through packet processing, i.e., they may +only be executed within parsers.

    +
    +
    +

    When extracting data into a bit-string or integer, the first packet +bit is extracted to the most significant bit of the integer.

    +
    +
    +

    Some targets may perform cut-through packet processing, i.e., they may start processing a packet before its length is known (i.e., before all -bytes have been received). On such a target calls to the packet_in.length() +bytes have been received). On such a target calls to the packet_in.length() method cannot be implemented. Attempts to call this method should be flagged as errors (either at compilation time by the compiler back-end, or when attempting to load the compiled P4 program onto a -target that does not support this method). -

    -

    In terms of the ParserModel, the semantics of packet_in -can be captured using the following abstract model of packets: -

    -
    -
    -
    packet_in {
    +target that does not support this method).

    +
    +
    +

    In terms of the ParserModel, the semantics of packet_in +can be captured using the following abstract model of packets:

    +
    +
    +
    +
    packet_in {
         unsigned nextBitIndex;
         byte[] data;
         unsigned lengthInBits;
    -    void initialize(byte[] data) {
    -        this.data = data;
    -        this.nextBitIndex = 0;
    -        this.lengthInBits = data.sizeInBytes * 8;
    -    }
    -    bit<32> length() { return this.lengthInBits / 8; }
    -}

    13.8.1. Fixed-width extraction

    -

    The single-argument extract method handles fixed-width headers, -and is declared in P4 as follows: -

    -
    -
    -
    void extract<T>(out T headerLeftValue);
    -

    The expression headerLeftValue must evaluate to an l-value (see -Section 6.7) of type header with a fixed width. If -this method executes successfully, on completion the headerLvalue -is filled with data from the packet and its validity bit is set to true. This -method may fail in various wayse.g., if there are not -enough bits left in the packet to fill the specified header. -

    -

    For example, the following program fragment extracts an Ethernet header: -

    -
    -
    -
    struct Result { Ethernet_h ethernet;  /* more fields omitted */ }
    -parser P(packet_in b, out Result r) {
    -    state start {
    -        b.extract(r.ethernet);
    +    void initialize(byte[] data) {
    +        this.data = data;
    +        this.nextBitIndex = 0;
    +        this.lengthInBits = data.sizeInBytes * 8;
         }
    -}
    -

    In terms of the ParserModel, the semantics of the -single-argument extract is given in terms of the following -pseudo-code method, using data from the packet class defined -above. We use the special valid$ identifier to indicate the -hidden valid bit of a header, isNext$ to indicate that the -l-value was obtained using next, and nextIndex$ to -indicate the corresponding header or header union stack properties. -

    -
    -
    -
    void packet_in.extract<T>(out T headerLValue) {
    +    bit<32> length() { return this.lengthInBits / 8; }
    +}
    +
    +
    +
    +

    13.8.1. Fixed-width extraction

    +
    +

    The single-argument extract method handles fixed-width headers, +and is declared in P4 as follows:

    +
    +
    +
    +
    void extract<T>(out T headerLeftValue);
    +
    +
    +
    +

    The expression headerLeftValue must evaluate to an l-value (see +Section 6.7) of type header with a fixed width. If +this method executes successfully, on completion the headerLvalue +is filled with data from the packet and its validity bit is set to true. This +method may fail in various ways---e.g., if there are not +enough bits left in the packet to fill the specified header.

    +
    +
    +

    For example, the following program fragment extracts an Ethernet header:

    +
    +
    +
    +
    struct Result { Ethernet_h ethernet;  /* more fields omitted */ }
    +parser P(packet_in b, out Result r) {
    +    state start {
    +        b.extract(r.ethernet);
    +    }
    +}
    +
    +
    +
    +

    In terms of the ParserModel, the semantics of the +single-argument extract is given in terms of the following +pseudo-code method, using data from the packet class defined +above. We use the special valid$ identifier to indicate the +hidden valid bit of a header, isNext$ to indicate that the +l-value was obtained using next, and nextIndex$ to +indicate the corresponding header or header union stack properties.

    +
    +
    +
    +
    void packet_in.extract<T>(out T headerLValue) {
        bitsToExtract = sizeofInBits(headerLValue);
    -   lastBitNeeded = this.nextBitIndex + bitsToExtract;
    -   ParserModel.verify(this.lengthInBits >= lastBitNeeded, error.PacketTooShort);
    -   headerLValue = this.data.extractBits(this.nextBitIndex, bitsToExtract);
    -   headerLValue.valid$ = true;
    -   if headerLValue.isNext$ {
    -     verify(headerLValue.nextIndex$ < headerLValue.size, error.StackOutOfBounds);
    -     headerLValue.nextIndex$ = headerLValue.nextIndex$ + 1;
    +   lastBitNeeded = this.nextBitIndex + bitsToExtract;
    +   ParserModel.verify(this.lengthInBits >= lastBitNeeded, error.PacketTooShort);
    +   headerLValue = this.data.extractBits(this.nextBitIndex, bitsToExtract);
    +   headerLValue.valid$ = true;
    +   if headerLValue.isNext$ {
    +     verify(headerLValue.nextIndex$ < headerLValue.size, error.StackOutOfBounds);
    +     headerLValue.nextIndex$ = headerLValue.nextIndex$ + 1;
        }
    -   this.nextBitIndex += bitsToExtract;
    -}

    13.8.2. Variable-width extraction

    -

    The two-argument extract handles variable-width headers, and is declared in P4 as follows: -

    -
    -
    -
    void extract<T>(out T headerLvalue, in bit<32> variableFieldSize);
    -

    The expression headerLvalue must be an l-value representing a -header that contains exactly one varbit field. The expression variableFieldSize -must evaluate to a bit<32> value that indicates the number of -bits to be extracted into the unique varbit field of the header -(i.e., this size is not the size of the complete header, just the varbit field). -

    -

    In terms of the ParserModel, the semantics of the two-argument extract -is captured by the following pseudo-code: -

    -
    -
    -
    void packet_in.extract<T>(out T headerLvalue,
    -                          in bit<32> variableFieldSize) {
    -   // targets are allowed to include the following line, but need not
    -   // verify(variableFieldSize[2:0] == 0, error.ParserInvalidArgument);
    +   this.nextBitIndex += bitsToExtract;
    +}
    +
    +
    +
    +
    +

    13.8.2. Variable-width extraction

    +
    +

    The two-argument extract handles variable-width headers, and is declared in P4 as follows:

    +
    +
    +
    +
    void extract<T>(out T headerLvalue, in bit<32> variableFieldSize);
    +
    +
    +
    +

    The expression headerLvalue must be an l-value representing a +header that contains exactly one varbit field. The expression variableFieldSize +must evaluate to a bit<32> value that indicates the number of +bits to be extracted into the unique varbit field of the header +(i.e., this size is not the size of the complete header, just the varbit field).

    +
    +
    +

    In terms of the ParserModel, the semantics of the two-argument extract +is captured by the following pseudo-code:

    +
    +
    +
    +
    void packet_in.extract<T>(out T headerLvalue,
    +                          in bit<32> variableFieldSize) {
    +   // targets are allowed to include the following line, but need not
    +   // verify(variableFieldSize[2:0] == 0, error.ParserInvalidArgument);
        bitsToExtract = sizeOfFixedPart(headerLvalue) + variableFieldSize;
    -   lastBitNeeded = this.nextBitIndex + bitsToExtract;
    -   ParserModel.verify(this.lengthInBits >= lastBitNeeded, error.PacketTooShort);
    -   ParserModel.verify(bitsToExtract <= headerLvalue.maxSize, error.HeaderTooShort);
    -   headerLvalue = this.data.extractBits(this.nextBitIndex, bitsToExtract);
    +   lastBitNeeded = this.nextBitIndex + bitsToExtract;
    +   ParserModel.verify(this.lengthInBits >= lastBitNeeded, error.PacketTooShort);
    +   ParserModel.verify(bitsToExtract <= headerLvalue.maxSize, error.HeaderTooShort);
    +   headerLvalue = this.data.extractBits(this.nextBitIndex, bitsToExtract);
        headerLvalue.varbitField.size = variableFieldSize;
    -   headerLvalue.valid$ = true;
    -   if headerLValue.isNext$ {
    -     verify(headerLValue.nextIndex$ < headerLValue.size, error.StackOutOfBounds);
    -     headerLValue.nextIndex$ = headerLValue.nextIndex$ + 1;
    -   }
    -   this.nextBitIndex += bitsToExtract;
    -}
    -

    The following example shows one way to parse IPv4 optionsby -splitting the IPv4 header into two separate headers: -

    -
    -
    -
    // IPv4 header without options
    -header IPv4_no_options_h {
    -   bit<4>   version;
    -   bit<4>   ihl;
    -   bit<8>   diffserv;
    -   bit<16>  totalLen;
    -   bit<16>  identification;
    -   bit<3>   flags;
    -   bit<13>  fragOffset;
    -   bit<8>   ttl;
    -   bit<8>   protocol;
    -   bit<16>  hdrChecksum;
    -   bit<32>  srcAddr;
    -   bit<32>  dstAddr;
    -}
    -header IPv4_options_h {
    -   varbit<320> options;
    -}
    -
    -struct Parsed_headers {
    -    // Some fields omitted
    -    IPv4_no_options_h ipv4;
    -    IPv4_options_h    ipv4options;
    -}
    -
    -error { InvalidIPv4Header }
    -
    -parser Top(packet_in b, out Parsed_headers headers) {
    -   // Some states omitted
    -
    -   state parse_ipv4 {
    -       b.extract(headers.ipv4);
    -       verify(headers.ipv4.ihl >= 5, error.InvalidIPv4Header);
    -       transition select (headers.ipv4.ihl) {
    -           5: dispatch_on_protocol;
    -           _: parse_ipv4_options;
    -       }
    -   }
    -
    -   state parse_ipv4_options {
    -       // use information in the ipv4 header to compute the number of bits to extract
    -       b.extract(headers.ipv4options,
    -                 (bit<32>)(((bit<16>)headers.ipv4.ihl - 5) * 32));
    -       transition dispatch_on_protocol;
    +   headerLvalue.valid$ = true;
    +   if headerLValue.isNext$ {
    +     verify(headerLValue.nextIndex$ < headerLValue.size, error.StackOutOfBounds);
    +     headerLValue.nextIndex$ = headerLValue.nextIndex$ + 1;
        }
    -}

    13.8.3. Lookahead

    -

    The lookahead method provided by the packet_in packet abstraction + this.nextBitIndex += bitsToExtract; +} +

    +
    +
    +

    The following example shows one way to parse IPv4 options---by +splitting the IPv4 header into two separate headers:

    +
    +
    +
    +
    // IPv4 header without options
    +header IPv4_no_options_h {
    +   bit<4>   version;
    +   bit<4>   ihl;
    +   bit<8>   diffserv;
    +   bit<16>  totalLen;
    +   bit<16>  identification;
    +   bit<3>   flags;
    +   bit<13>  fragOffset;
    +   bit<8>   ttl;
    +   bit<8>   protocol;
    +   bit<16>  hdrChecksum;
    +   bit<32>  srcAddr;
    +   bit<32>  dstAddr;
    +}
    +header IPv4_options_h {
    +   varbit<320> options;
    +}
    +
    +struct Parsed_headers {
    +    // Some fields omitted
    +    IPv4_no_options_h ipv4;
    +    IPv4_options_h    ipv4options;
    +}
    +
    +error { InvalidIPv4Header }
    +
    +parser Top(packet_in b, out Parsed_headers headers) {
    +   // Some states omitted
    +
    +   state parse_ipv4 {
    +       b.extract(headers.ipv4);
    +       verify(headers.ipv4.ihl >= 5, error.InvalidIPv4Header);
    +       transition select (headers.ipv4.ihl) {
    +           5: dispatch_on_protocol;
    +           _: parse_ipv4_options;
    +       }
    +   }
    +
    +   state parse_ipv4_options {
    +       // use information in the ipv4 header to compute the number of bits to extract
    +       b.extract(headers.ipv4options,
    +                 (bit<32>)(((bit<16>)headers.ipv4.ihl - 5) * 32));
    +       transition dispatch_on_protocol;
    +   }
    +}
    +
    +
    +
    +
    +

    13.8.3. Lookahead

    +
    +

    The lookahead method provided by the packet_in packet abstraction evaluates to a set of bits from the input packet without advancing the -nextBitIndex pointer. Similar to extract, it will transition to -reject and set the error if there are not enough bits in the packet. -When lookahead returns a value that contains headers (e.g., a header +nextBitIndex pointer. Similar to extract, it will transition to +reject and set the error if there are not enough bits in the packet. +When lookahead returns a value that contains headers (e.g., a header type, or a struct containing headers), the headers values in the -returned result are always valid (otherwise lookahead must have -transitioned to the reject state). -

    -

    The lookahead method can be invoked as follows: -

    -
    -
    -
    b.lookahead<T>()
    -

    where T must be a type with fixed width. In case of success the -result of the evaluation of lookahead returns a value of type T. -

    -

    In terms of the ParserModel, the semantics of lookahead is -given by the following pseudocode: -

    -
    -
    -
    T packet_in.lookahead<T>() {
    +returned result are always valid (otherwise lookahead must have
    +transitioned to the reject state).

    +
    +
    +

    The lookahead method can be invoked as follows:

    +
    +
    +
    +
    b.lookahead<T>()
    +
    +
    +
    +

    where T must be a type with fixed width. In case of success the +result of the evaluation of lookahead returns a value of type T.

    +
    +
    +

    In terms of the ParserModel, the semantics of lookahead is +given by the following pseudocode:

    +
    +
    +
    +
    T packet_in.lookahead<T>() {
        bitsToExtract = sizeof(T);
    -   lastBitNeeded = this.nextBitIndex + bitsToExtract;
    -   ParserModel.verify(this.lengthInBits >= lastBitNeeded, error.PacketTooShort);
    -   T tmp = this.data.extractBits(this.nextBitIndex, bitsToExtract);
    -   return tmp;
    -}
    -

    The TCP options example from Section 8.19 also illustrates how lookahead can be used: -

    -
    -
    -
    state start {
    -    transition select(b.lookahead<bit<8>>()) {
    -        0: parse_tcp_option_end;
    -        1: parse_tcp_option_nop;
    -        2: parse_tcp_option_ss;
    -        3: parse_tcp_option_s;
    -        5: parse_tcp_option_sack;
    -    }
    -}
    -
    -// Some states omitted
    -
    -state parse_tcp_option_sack {
    -    bit<8> n = b.lookahead<Tcp_option_sack_top>().length;
    -    b.extract(vec.next.sack, (bit<32>) (8 * n - 16));
    -    transition start;
    -}

    13.8.4. Skipping bits

    -

    P4 provides two ways to skip over bits in an input packet without -assigning them to a header: -

    -

    One way is to extract to the underscore identifier, explicitly -specifying the type of the data: -

    -
    -
    -
    b.extract<T>(_)
    -

    Another way is to use the advance method of the packet when the -number of bits to skip is known. -

    -

    In terms of the ParserModel, the meaning of advance is -given in pseudocode as follows: -

    -
    -
    -
    void packet_in.advance(bit<32> bits) {
    -   // targets are allowed to include the following line, but need not
    -   // verify(bits[2:0] == 0, error.ParserInvalidArgument);
    -   lastBitNeeded = this.nextBitIndex + bits;
    -   ParserModel.verify(this.lengthInBits >= lastBitNeeded, error.PacketTooShort);
    -   this.nextBitIndex += bits;
    -}

    13.9. Header stacks

    -

    A header stack has two properties, next and last, which + lastBitNeeded = this.nextBitIndex + bitsToExtract; + ParserModel.verify(this.lengthInBits >= lastBitNeeded, error.PacketTooShort); + T tmp = this.data.extractBits(this.nextBitIndex, bitsToExtract); + return tmp; +} +

    +
    +
    +

    The TCP options example from Section 8.19 also illustrates how lookahead can be used:

    +
    +
    +
    +
    state start {
    +    transition select(b.lookahead<bit<8>>()) {
    +        0: parse_tcp_option_end;
    +        1: parse_tcp_option_nop;
    +        2: parse_tcp_option_ss;
    +        3: parse_tcp_option_s;
    +        5: parse_tcp_option_sack;
    +    }
    +}
    +
    +// Some states omitted
    +
    +state parse_tcp_option_sack {
    +    bit<8> n = b.lookahead<Tcp_option_sack_top>().length;
    +    b.extract(vec.next.sack, (bit<32>) (8 * n - 16));
    +    transition start;
    +}
    +
    +
    +
    +
    +

    13.8.4. Skipping bits

    +
    +

    P4 provides two ways to skip over bits in an input packet without +assigning them to a header:

    +
    +
    +

    One way is to extract to the underscore identifier, explicitly +specifying the type of the data:

    +
    +
    +
    +
    b.extract<T>(_)
    +
    +
    +
    +

    Another way is to use the advance method of the packet when the +number of bits to skip is known.

    +
    +
    +

    In terms of the ParserModel, the meaning of advance is +given in pseudocode as follows:

    +
    +
    +
    +
    void packet_in.advance(bit<32> bits) {
    +   // targets are allowed to include the following line, but need not
    +   // verify(bits[2:0] == 0, error.ParserInvalidArgument);
    +   lastBitNeeded = this.nextBitIndex + bits;
    +   ParserModel.verify(this.lengthInBits >= lastBitNeeded, error.PacketTooShort);
    +   this.nextBitIndex += bits;
    +}
    +
    +
    +
    +
    +
    +

    13.9. Header stacks

    +
    +

    A header stack has two properties, next and last, which can be used in parsing. Consider the following declaration, which defines a stack for representing the headers of a packet with at most -ten MPLS headers: -

    -
    -
    -
    header Mpls_h {
    -    bit<20> label;
    -    bit<3>  tc;
    -    bit     bos;
    -    bit<8>  ttl;
    -}
    -Mpls_h[10] mpls;
    -

    The expression mpls.next represents an l-value of type Mpls_h -that references an element in the mpls -stack. Initially, mpls.next refers to the first element of -stack. It is automatically advanced on each successful call to extract. -The mpls.last property refers to the element -immediately preceding next if such an element exists. Attempting -to access mpls.next element when the stack's nextIndex -counter is greater than or equal to size causes a transition to reject -and sets the error to error.StackOutOfBounds. Likewise, -attempting to access mpls.last when the nextIndex counter -is equal to 0 causes a transition to reject and -sets the error to error.StackOutOfBounds. -

    -

    The following example shows a simplified parser for MPLS processing: -

    -
    -
    -
    struct Pkthdr {
    -   Ethernet_h ethernet;
    -   Mpls_h[3] mpls;
    -   // other headers omitted
    -}
    -
    -parser P(packet_in b, out Pkthdr p) {
    -    state start {
    -        b.extract(p.ethernet);
    -        transition select(p.ethernet.etherType) {
    -           0x8847: parse_mpls;
    -           0x0800: parse_ipv4;
    -        }
    -    }
    -    state parse_mpls {
    -         b.extract(p.mpls.next);
    -         transition select(p.mpls.last.bos) {
    -            0: parse_mpls; // This creates a loop
    -            1: parse_ipv4;
    -         }
    -    }
    -    // other states omitted
    -}

    13.10. Sub-parsers

    -

    P4 allows parsers to invoke the services of other parsers, similar to +ten MPLS headers:

    +
    +
    +
    +
    header Mpls_h {
    +    bit<20> label;
    +    bit<3>  tc;
    +    bit     bos;
    +    bit<8>  ttl;
    +}
    +Mpls_h[10] mpls;
    +
    +
    +
    +

    The expression mpls.next represents an l-value of type Mpls_h +that references an element in the mpls +stack. Initially, mpls.next refers to the first element of +stack. It is automatically advanced on each successful call to extract. +The mpls.last property refers to the element +immediately preceding next if such an element exists. Attempting +to access mpls.next element when the stack’s nextIndex +counter is greater than or equal to size causes a transition to reject +and sets the error to error.StackOutOfBounds. Likewise, +attempting to access mpls.last when the nextIndex counter +is equal to 0 causes a transition to reject and +sets the error to error.StackOutOfBounds.

    +
    +
    +

    The following example shows a simplified parser for MPLS processing:

    +
    +
    +
    +
    struct Pkthdr {
    +   Ethernet_h ethernet;
    +   Mpls_h[3] mpls;
    +   // other headers omitted
    +}
    +
    +parser P(packet_in b, out Pkthdr p) {
    +    state start {
    +        b.extract(p.ethernet);
    +        transition select(p.ethernet.etherType) {
    +           0x8847: parse_mpls;
    +           0x0800: parse_ipv4;
    +        }
    +    }
    +    state parse_mpls {
    +         b.extract(p.mpls.next);
    +         transition select(p.mpls.last.bos) {
    +            0: parse_mpls; // This creates a loop
    +            1: parse_ipv4;
    +         }
    +    }
    +    // other states omitted
    +}
    +
    +
    +
    +
    +

    13.10. Sub-parsers

    +
    +

    P4 allows parsers to invoke the services of other parsers, similar to subroutines. To invoke the services of another parser, the sub-parser must be first instantiated; the services of an instance are invoked by -calling it using its apply method. -

    -

    The following example shows a sub-parser invocation: -

    -
    -
    -
    parser callee(packet_in packet, out IPv4 ipv4) { /* body omitted */ }
    -parser caller(packet_in packet, out Headers h) {
    -     callee() subparser;  // instance of callee
    -     state subroutine {
    -          subparser.apply(packet, h.ipv4);  // invoke sub-parser
    -          transition accept;  // accept if sub-parser ends in accept state
    -     }
    -}
    -

    The semantics of a sub-parser invocation can be described as follows: -

    -
      -
    • The state invoking the sub-parser is split into two half-states at -the parser invocation statement. -
    • -
    • The top half includes a transition to the sub-parser start -state. -
    • -
    • The sub-parser's accept state is identified with the bottom -half of the current state -
    • -
    • The sub-parser's reject state is identified with the reject -state of the current parser. -
    - -
    -

    subparser -

    -
    - -
    Figure 9. Semantics of invoking a sub-parser: top: original program, bottom: equivalent program.
    -

    -Figure 9 shows a diagram of this process. -

    -

    Note that since P4 requires definitions to precede uses, it is impossible to -create recursive (or mutually recursive) parsers. -

    -

    When a parser is instantiated, local instantiations of stateful +calling it using its apply method.

    +
    +
    +

    The following example shows a sub-parser invocation:

    +
    +
    +
    +
    parser callee(packet_in packet, out IPv4 ipv4) { /* body omitted */ }
    +parser caller(packet_in packet, out Headers h) {
    +     callee() subparser;  // instance of callee
    +     state subroutine {
    +          subparser.apply(packet, h.ipv4);  // invoke sub-parser
    +          transition accept;  // accept if sub-parser ends in accept state
    +     }
    +}
    +
    +
    +
    +

    The semantics of a sub-parser invocation can be described as follows:

    +
    +
    +
      +
    • +

      The state invoking the sub-parser is split into two half-states at +the parser invocation statement.

      +
    • +
    • +

      The top half includes a transition to the sub-parser start +state.

      +
    • +
    • +

      The sub-parser’s accept state is identified with the bottom +half of the current state

      +
    • +
    • +

      The sub-parser’s reject state is identified with the reject +state of the current parser.

      +
    • +
    +
    +
    +
    +subparser +
    +
    Figure 9. Semantics of invoking a sub-parser: top: original program, bottom: equivalent program.
    +
    +
    +

    Figure 9 shows a diagram of this process.

    +
    +
    +

    Note that since P4 requires definitions to precede uses, it is impossible to +create recursive (or mutually recursive) parsers.

    +
    +
    +

    When a parser is instantiated, local instantiations of stateful objects are evaluated recursively. That is, each instantiation of a parser has a unique set of local parser value sets, extern objects, inner parser instances, etc. Thus, in general, invoking a parser instance twice is not the same as invoking two copies of the same parser instance. Note however that local variables do not persist across invocations of the parser. This semantics also applies to -direct invocation (see Section 15.1). -

    -

    Architectures may impose (static or dynamic) constraints on the +direct invocation (see Section 15.1).

    +
    +
    +

    Architectures may impose (static or dynamic) constraints on the number of parser states that can be traversed for processing each packet. For example, a compiler for a specific target may reject parsers containing loops that cannot be unrolled at compilation time or that may contain cycles that do not advance the cursor. If a parser aborts execution dynamically because it exceeded the time budget allocated for parsing, the parser should transition to -reject and set -the standard error error.ParserTimeout. -

    13.11. Parser Value Sets

    -

    In some cases, the values that determine the transition from one parser state +reject and set +the standard error error.ParserTimeout.

    +
    +
    +
    +

    13.11. Parser Value Sets

    +
    +

    In some cases, the values that determine the transition from one parser state to another need to be determined at run time. MPLS is one example where the value of the MPLS label field is used to determine what headers follow the MPLS tag and this mapping may change dynamically at run time. To support this functionality, P4 supports the notion of a Parser Value Set. This is a named -set of values with a run time API to add and remove values from the set. -

    -

    Value sets are declared locally within a parser. They should be declared before -being referenced in parser keysetExpression and can be used as a label in a -select expression. -

    -

    The syntax for declaring value sets is: -

    -
    -
    -
    valueSetDeclaration
    +set of values with a run time API to add and remove values from the set.

    +
    +
    +

    Value sets are declared locally within a parser. They should be declared before +being referenced in parser keysetExpression and can be used as a label in a +select expression.

    +
    +
    +

    The syntax for declaring value sets is:

    +
    +
    +
    +
    valueSetDeclaration
       : optAnnotations
    -      VALUESET "<" baseType ">" "(" expression ")" name ";"
    +      VALUESET "<" baseType ">" "(" expression ")" name ";"
       | optAnnotations
    -      VALUESET "<" tupleType ">" "(" expression ")" name ";"
    +      VALUESET "<" tupleType ">" "(" expression ")" name ";"
       | optAnnotations
    -      VALUESET "<" typeName ">" "(" expression ")" name ";"
    -  ;
    -

    Parser Value Sets support a size argument to provide hints to the compiler to + VALUESET "<" typeName ">" "(" expression ")" name ";" + ; +

    +
    +
    +

    Parser Value Sets support a size argument to provide hints to the compiler to reserve hardware resources to implement the value set. For example, this parser -value set: -

    -
    -
    -
    value_set<bit<16>>(4) pvs;
    -

    creates a value_set of size 4 with entries of type bit<16>. -

    -

    The semantics of the size argument is similar to the size property of a -table. If a value set has a size argument with value N, it is recommended +value set:

    +
    +
    +
    +
    value_set<bit<16>>(4) pvs;
    +
    +
    +
    +

    creates a value_set of size 4 with entries of type bit<16>.

    +
    +
    +

    The semantics of the size argument is similar to the size property of a +table. If a value set has a size argument with value N, it is recommended that a compiler should choose a data plane implementation that is capable of -storing N value set entries. See “Size property of P4 tables and parser value -sets” P4SizeProperty for further discussion on the implementation of parser -value set size. -

    -

    The value set is populated by the control plane by methods specified in the -P4Runtime specification4. -

    14. Control blocks

    -

    P4 parsers are responsible for extracting bits from a packet into -headers. These headers (and other metadata) can be manipulated and transformed within control +storing N value set entries. See "Size property of P4 tables and parser value +sets" [2] for further discussion on the implementation of parser +value set size.

    +
    +
    +

    The value set is populated by the control plane by methods specified +in the P4Runtime specification.[3]

    +
    +
    +
    +
    +
    +

    14. Control blocks

    +
    +
    +

    P4 parsers are responsible for extracting bits from a packet into +headers. These headers (and other metadata) can be manipulated and transformed within control blocks. The body of a control block resembles a traditional imperative program. Within the body of a control block, match-action units can be invoked to perform data transformations. Match-action units are represented in P4 by -constructs called tables. -

    -

    Syntactically, a control block is declared with a name, +constructs called tables.

    +
    +
    +

    Syntactically, a control block is declared with a name, parameters, optional type parameters, and a sequence of declarations -of constants, variables, actions, tables, and other -instantiations: -

    -
    -
    -
    controlDeclaration
    +of constants, variables, actions, tables, and other
    +instantiations:

    +
    +
    +
    +
    controlDeclaration
         : controlTypeDeclaration optConstructorParameters
    -      /* controlTypeDeclaration cannot contain type parameters */
    -      "{" controlLocalDeclarations APPLY controlBody "}"
    +      /* controlTypeDeclaration cannot contain type parameters */
    +      "{" controlLocalDeclarations APPLY controlBody "}"
         ;
     
     controlLocalDeclarations
    -    : /* empty */
    +    : /* empty */
         | controlLocalDeclarations controlLocalDeclaration
         ;
     
    @@ -7167,132 +10061,171 @@ 

    -

    It is illegal to instantiate a parser within a control + ; +

    +
    +
    +

    It is illegal to instantiate a parser within a control block. -For a description of the optConstructorParameters, which -can be used to build parameterized control blocks, see Section -15. -

    -

    Unlike control type declarations, control declarations may not be -generice.g., the following declaration is illegal: -

    -
    -
    -
    control C<H>(inout H data) { /* Body omitted */ }
    -

    P4 does not support exceptional control-flow within a control +For a description of the optConstructorParameters, which +can be used to build parameterized control blocks, see Chapter 15.

    +
    +
    +

    Unlike control type declarations, control declarations may not be +generic---e.g., the following declaration is illegal:

    +
    +
    +
    +
    control C<H>(inout H data) { /* Body omitted */ }
    +
    +
    +
    +

    P4 does not support exceptional control-flow within a control block. The only statement which -has a non-local effect on control flow is exit, which causes +has a non-local effect on control flow is exit, which causes execution of the enclosing control block to immediately terminate. -That is, there is no equivalent of the verify statement -or the reject state from parsers. Hence, all error handling must -be performed explicitly by the programmer. -

    -

    The rest of this section describes the core components of a control block, -starting with actions. -

    14.1. Actions

    -
    -

    actions -

    -
    - -
    Figure 10. Actions contain code and data. The code is in the P4 program, while the data is provided in the table entries, typically populated by the control plane. Other parameters are bound by the data plane.
    -

    Actions are code fragments that can read and write the data being +That is, there is no equivalent of the verify statement +or the reject state from parsers. Hence, all error handling must +be performed explicitly by the programmer.

    +
    +
    +

    The rest of this section describes the core components of a control block, +starting with actions.

    +
    +
    +

    14.1. Actions

    +
    +
    +actions +
    +
    Figure 10. Actions contain code and data. The code is in the P4 program, while the data is provided in the table entries, typically populated by the control plane. Other parameters are bound by the data plane.
    +
    +
    +

    Actions are code fragments that can read and write the data being processed. Actions may contain data values that can be written by the control plane and read by the data plane. Actions are the main construct by which the control plane can dynamically influence the -behavior of the data plane. Figure 10 shows the abstract -model of an action. -

    -
    -
    -
    actionDeclaration
    -    : optAnnotations ACTION name "(" parameterList ")" blockStatement
    -    ;
    -

    Syntactically actions resemble functions with no return +behavior of the data plane. Figure 10 shows the abstract +model of an action.

    +
    +
    +
    +
    actionDeclaration
    +    : optAnnotations ACTION name "(" parameterList ")" blockStatement
    +    ;
    +
    +
    +
    +

    Syntactically actions resemble functions with no return value. Actions may be declared within a control block; in this case -they can only be used within instances of that control block. -

    -

    The following example shows an action declaration: -

    -
    -
    -
    action Forward_a(out bit<9> outputPort, bit<9> port) {
    -    outputPort = port;
    -}
    -

    Action parameters may not have extern types. Action parameters that -have no direction (e.g., port in the previous example) indicate -“action data.” All such parameters must appear at the end of the -parameter list. When used in a match-action table (see Section -14.2.1.2), these parameters will be provided by the +they can only be used within instances of that control block.

    +
    +
    +

    The following example shows an action declaration:

    +
    +
    +
    +
    action Forward_a(out bit<9> outputPort, bit<9> port) {
    +    outputPort = port;
    +}
    +
    +
    +
    +

    Action parameters may not have extern types. Action parameters that +have no direction (e.g., port in the previous example) indicate +"action data." All such parameters must appear at the end of the +parameter list. When used in a match-action table (see +Section 14.2.1.2), these parameters will be provided by the table entries (e.g., as specified by the control plane, the -default_action table property, or the entries table -property). -

    -

    The body of an action consists of a sequence of statements and -declarations. No table, control, or parser applications can -appear within actions. -

    -

    Some targets may impose additional restrictions on action -bodiese.g., only allowing straight-line code, with no conditional -statements or expressions. -

    14.1.1. Invoking actions

    -

    Actions can be executed in two ways: -

    -
      -
    • Implicitly: by tables during match-action processing. -
    • -
    • Explicitly: either from a control block or from another action. +default_action table property, or the entries table +property).

      +
    +
    +

    The body of an action consists of a sequence of statements and +declarations. No table, control, or parser applications can +appear within actions.

    +
    +
    +

    Some targets may impose additional restrictions on action +bodies---e.g., only allowing straight-line code, with no conditional +statements or expressions.

    +
    +
    +

    14.1.1. Invoking actions

    +
    +

    Actions can be executed in two ways:

    +
    +
    +
      +
    • +

      Implicitly: by tables during match-action processing.

      +
    • +
    • +

      Explicitly: either from a control block or from another action. In either case, the values for all action parameters must be supplied explicitly, including values for the directionless -parameters. In this case, the directionless parameters behave like in -parameters. -

    -

    14.2. Tables

    -
    -

    maudataflow -

    -
    - -
    Figure 11. Match-Action Unit Dataflow.
    -

    A table describes a match-action unit. The -structure of a match-action unit is shown in Figure -11. Processing a packet using a match-action table executes the -following steps: -

    -
      -
    • Key construction. -
    • -
    • Key lookup in a lookup table (the “match” step). The result of key -lookup is an “action”. -
    • -
    • Action execution (the “action step”) over the input data, resulting -in mutations of the data. -
    - -

    A table declaration introduces a table instance. To obtain +parameters. In this case, the directionless parameters behave like in +parameters.

    + + +
    +
    +
    +
    +

    14.2. Tables

    +
    +
    +maudataflow +
    +
    Figure 11. Match-Action Unit Dataflow.
    +
    +
    +

    A table describes a match-action unit. The +structure of a match-action unit is shown in +Figure 11. Processing a packet using a match-action table executes the +following steps:

    +
    +
    +
      +
    • +

      Key construction.

      +
    • +
    • +

      Key lookup in a lookup table (the "match" step). The result of key +lookup is an "action".

      +
    • +
    • +

      Action execution (the "action step") over the input data, resulting +in mutations of the data.

      +
    • +
    +
    +
    +

    A table declaration introduces a table instance. To obtain multiple instances of a table, it must be declared within a control -block that is itself instantiated multiple times. -

    -

    The look-up table is a finite map whose contents are manipulated +block that is itself instantiated multiple times.

    +
    +
    +

    The look-up table is a finite map whose contents are manipulated asynchronously (read/write) by the target control plane, through a -separate control-plane API (see Figure 11). Note that -the term “table” is overloaded: it can refer to the P4 table +separate control-plane API (see Figure 11). Note that +the term "table" is overloaded: it can refer to the P4 table objects that appear in P4 programs, as well as the internal look-up -tables used in targets. We will use the term “match-action unit” when -necessary to disambiguate. -

    -

    Syntactically a table is defined in terms of a set of key-value -properties. Some of these properties are “standard” properties, but +tables used in targets. We will use the term "match-action unit" when +necessary to disambiguate.

    +
    +
    +

    Syntactically a table is defined in terms of a set of key-value +properties. Some of these properties are "standard" properties, but the set of properties can be extended by target-specific compilers as needed. Note duplicated properties are invalid and the compiler should -reject them. -

    -
    -
    -
    tableDeclaration
    -    : optAnnotations TABLE name "{" tablePropertyList "}"
    +reject them.

    +
    +
    +
    +
    tableDeclaration
    +    : optAnnotations TABLE name "{" tablePropertyList "}"
         ;
     
     tablePropertyList
    @@ -7301,10 +10234,10 @@ 

    '=' '{' keyElementList '}' - | ACTIONS '=' '{' actionList '}' - | optAnnotations optCONST ENTRIES '=' '{' entriesList '}' - | optAnnotations optCONST nonTableKwName '=' initializer ';' + : KEY '=' '{' keyElementList '}' + | ACTIONS '=' '{' actionList '}' + | optAnnotations optCONST ENTRIES '=' '{' entriesList '}' + | optAnnotations optCONST nonTableKwName '=' initializer ';' ; nonTableKwName @@ -7314,1822 +10247,2385 @@

    The standard table properties include: -

    -
      -
    • key: An expression that describes how the key used for look-up -is computed. -
    • -
    • actions: A list of all actions that may be found in the table. -
    - -

    In addition, the tables may optionally define the following properties, -

    -
      -
    • default_action: an action to execute when the lookup in the -lookup table fails to find a match for the key used. -
    • -
    • size: an integer specifying the desired size of the table. -
    • -
    • entries: entries that are initially added to a table when the P4 + ;

    +
    +
    +
    +

    The standard table properties include:

    +
    +
    +
      +
    • +

      key: An expression that describes how the key used for look-up +is computed.

      +
    • +
    • +

      actions: A list of all actions that may be found in the table.

      +
    • +
    +
    +
    +

    In addition, the tables may optionally define the following properties,

    +
    +
    +
      +
    • +

      default_action: an action to execute when the lookup in the +lookup table fails to find a match for the key used.

      +
    • +
    • +

      size: an integer specifying the desired size of the table.

      +
    • +
    • +

      entries: entries that are initially added to a table when the P4 program is loaded, some or all of which may be unchangeable by the -control plane software. -

    • -
    • largest_priority_wins - Only useful for some tables with the -entries property. See section 14.2.1.4 for details. -
    • -
    • priority_delta - Only useful for some tables with the entries -property. See section 14.2.1.4 for details. -
    - -

    The compiler must set the default_action to NoAction (and also -insert it into the list of actions) for tables that do not define -the default_action property. Hence, all tables can be thought of -as having a default_action property, either implicitly or -explicitly. -

    -

    In addition, tables may contain architecture-specific properties (see -Section 14.2.1.6). -

    -

    A property marked as const cannot be changed dynamically by the -control plane. The key, actions, and size properties cannot be -modified so the const keyword is not needed for these. -

    14.2.1. Table properties

    14.2.1.1. Keys
    -

    The key is a table property which specifies the data-plane values +control plane software.

    + +
  • +

    largest_priority_wins - Only useful for some tables with the +entries property. See Section 14.2.1.4 for details.

    +
  • +
  • +

    priority_delta - Only useful for some tables with the entries +property. See Section 14.2.1.4 for details.

    +
  • + +
    +
    +

    The compiler must set the default_action to NoAction (and also +insert it into the list of actions) for tables that do not define +the default_action property. Hence, all tables can be thought of +as having a default_action property, either implicitly or +explicitly.

    +
    +
    +

    In addition, tables may contain architecture-specific properties (see +Section 14.2.1.6).

    +
    +
    +

    A property marked as const cannot be changed dynamically by the +control plane. The key, actions, and size properties cannot be +modified so the const keyword is not needed for these.

    +
    +
    +

    14.2.1. Table properties

    +
    +
    14.2.1.1. Keys
    +
    +

    The key is a table property which specifies the data-plane values that should be used to look up an entry. A key is a list of pairs of -the form (e : m), where e is an expression that describes the data -to be matched in the table, and m is a match_kind that describes +the form (e : m), where e is an expression that describes the data +to be matched in the table, and m is a match_kind that describes the algorithm used to perform the lookup (see Section -7.1.3). -

    -
    -
    -
    keyElementList
    -    : /* empty */
    +Section 7.1.3).

    +
    +
    +
    +
    keyElementList
    +    : /* empty */
         | keyElementList keyElement
         ;
     
     keyElement
    -    : expression ":" name optAnnotations ";"
    -    ;
    -

    For example, consider the following program fragment: -

    -
    -
    -
    table Fwd {
    +    : expression ":" name optAnnotations ";"
    +    ;
    +
    +
    +
    +

    For example, consider the following program fragment:

    +
    +
    +
    +
    table Fwd {
         key = {
            ipv4header.dstAddress : ternary;
            ipv4header.version    : exact;
         }
    -    // more fields omitted
    -}
    -

    Here the key comprises two fields from the ipv4header -header: dstAddress and version. The match_kind elements -serve three purposes: -

    -
      -
    • They specify the algorithm used to match data-plane values against -the entries in the table at runtime. -
    • -
    • They are used to synthesize the control-plane API that is used to -populate the table. -
    • -
    • They are used by the compiler back-end to allocate resources for the -implementation of the table. -
    - -

    The P4 core library contains three predefined match_kind identifiers: -

    -
    -
    -
    match_kind {
    -   exact,
    -   ternary,
    +    // more fields omitted
    +}
    +
    +
    +
    +

    Here the key comprises two fields from the ipv4header +header: dstAddress and version. The match_kind elements +serve three purposes:

    +
    +
    +
      +
    • +

      They specify the algorithm used to match data-plane values against +the entries in the table at runtime.

      +
    • +
    • +

      They are used to synthesize the control-plane API that is used to +populate the table.

      +
    • +
    • +

      They are used by the compiler back-end to allocate resources for the +implementation of the table.

      +
    • +
    +
    +
    +

    The P4 core library contains three predefined match_kind identifiers:

    +
    +
    +
    +
    match_kind {
    +   exact,
    +   ternary,
        lpm
    -}
    -

    These identifiers correspond to the P414 match kinds with the same +} +

    +
    +
    +

    These identifiers correspond to the P414 match kinds with the same names. The semantics of these match kinds is actually not needed to describe the behavior of the P4 abstract machine; how they are used influences only the control-plane API and the implementation of the look-up table. From the point of view of the P4 program, a look-up table is an abstract finite map that is given a key and produces as a -result either an action or a “miss” indication, as described in -Section 14.2.3. -

    -

    The expected meaning of these values is as follows: -

    -
      -
    • -

      an exact match kind on a key field means that the value of the +result either an action or a "miss" indication, as described in +Section 14.2.3.

      +
    +
    +

    The expected meaning of these values is as follows:

    +
    +
    +
      +
    • +

      an exact match kind on a key field means that the value of the field in the table specifies exactly the value the lookup key field must have in order to match. This is applicable for all legal key -fields whose types support equality comparisons. -

    • -
    • -

      a ternary match kind on a key field means that the field in the +fields whose types support equality comparisons.

      +
    • +
    • +

      a ternary match kind on a key field means that the field in the table specifies a set of values for the key field using a value and -a mask. The meaning of the (value, mask) pair is similar to the P4 -mask expressions, as described in Section 8.15.3: a key field -k matches the table entry when k & mask == value & mask. -

    • -
    • -

      a lpm (longest prefix match) match kind on a key field is a -specific type of ternary match where the mask is required to have +a mask. The meaning of the (value, mask) pair is similar to the P4 +mask expressions, as described in Section 8.15.3: a key field +k matches the table entry when k & mask == value & mask.

      +
    • +
    • +

      a lpm (longest prefix match) match kind on a key field is a +specific type of ternary match where the mask is required to have a form in binary that is a contiguous set of 1 bits followed by a contiguous set of 0 bits. Masks with more 1 bits have automatically -higher priorities. A mask with all bits 0 is legal. -

    - -

    Some table entries, in particular the ones with at least one ternary +higher priorities. A mask with all bits 0 is legal.

    + + +
    +
    +

    Some table entries, in particular the ones with at least one ternary field, also require a priority value. A priority is a numeric value which is used to break ties when a particular key belongs to multiple sets. When table entries are specified in the P4 program the priorities are generated by the compiler; when entries are specified by the control-plane, the priority may need to be explicitly specified. Entries with higher priority are matched first. This -specification does not mandate whether “higher” priorities are +specification does not mandate whether "higher" priorities are represented by higher or lower numeric values; this choice is left to -the target implementation. -

    -

    An example specifying entries for a table is given in Section 14.2.1.4. -

    -

    If a table has no key property, or if the value of its key -property is the empty tuple, i.e. key = {}, then it contains no -look-up table, just a default actioni.e., the associated lookup -table is always the empty map. -

    -

    Each key element can have an optional @name annotation which is -used to synthesize the control-plane-visible name for the key field. -

    -

    Note some implementations might only support a limited number of keys or -a limited combinations of match_kind for the keys. The implementation -should reject those cases with an error message in this case. -

    14.2.1.2. Actions
    -

    A table must declare all possible actions that may appear within the +the target implementation.

    +
    +
    +

    An example specifying entries for a table is given in Section 14.2.1.4.

    +
    +
    +

    If a table has no key property, or if the value of its key +property is the empty tuple, i.e. key = {}, then it contains no +look-up table, just a default action---i.e., the associated lookup +table is always the empty map.

    +
    +
    +

    Each key element can have an optional @name annotation which is +used to synthesize the control-plane-visible name for the key field.

    +
    +
    +

    Note some implementations might only support a limited number of keys or +a limited combinations of match_kind for the keys. The implementation +should reject those cases with an error message in this case.

    +
    +
    +
    +
    14.2.1.2. Actions
    +
    +

    A table must declare all possible actions that may appear within the associated lookup table or in the default action. This is done with -the actions property; the value of this property is always an actionList: -

    -
    -
    -
    actionList
    -    : /* empty */
    -    | actionList optAnnotations actionRef ";"
    +the actions property; the value of this property is always an actionList:

    +
    +
    +
    +
    actionList
    +    : /* empty */
    +    | actionList optAnnotations actionRef ";"
         ;
     
     actionRef
         : prefixedNonTypeName
    -    | prefixedNonTypeName "(" argumentList ")"
    -    ;
    -

    To illustrate, recall the example Very Simple Switch program in -Section 5.3: -

    -
    -
    -
    action Drop_action() {
    -  outCtrl.outputPort = DROP_PORT;
    -}
    -
    -action Rewrite_smac(EthernetAddress sourceMac) {
    -  headers.ethernet.srcAddr = sourceMac;
    -}
    -
    -table smac {
    -    key = { outCtrl.outputPort : exact; }
    -    actions = {
    -        Drop_action;
    -        Rewrite_smac;
    -    }
    -}
    -
      -
    • The entries in the smac table may contain two different actions: Drop_action and Rewrite_mac. -
    • -
    • The Rewrite_smac action has one parameter, sourceMac, which in this case will be provided by the control plane. -
    - -

    Each action in the list of actions for a table must have a distinct -namee.g., the following program fragment is illegal: -

    -
    -
    -
    action a() {}
    -control c() {
    -    action a() {}
    -    // Illegal table: two actions with the same name
    -    table t { actions = { a; .a; } }
    -}
    -

    Each action parameter that has a direction (in, inout, or out) -must be bound in the actions list specification; conversely, no + | prefixedNonTypeName "(" argumentList ")" + ; +

    +
    +
    +

    To illustrate, recall the example Very Simple Switch program in +Section 5.3:

    +
    +
    +
    +
    action Drop_action() {
    +  outCtrl.outputPort = DROP_PORT;
    +}
    +
    +action Rewrite_smac(EthernetAddress sourceMac) {
    +  headers.ethernet.srcAddr = sourceMac;
    +}
    +
    +table smac {
    +    key = { outCtrl.outputPort : exact; }
    +    actions = {
    +        Drop_action;
    +        Rewrite_smac;
    +    }
    +}
    +
    +
    +
    +
      +
    • +

      The entries in the smac table may contain two different actions: Drop_action and Rewrite_mac.

      +
    • +
    • +

      The Rewrite_smac action has one parameter, sourceMac, which in this case will be provided by the control plane.

      +
    • +
    +
    +
    +

    Each action in the list of actions for a table must have a distinct +name---e.g., the following program fragment is illegal:

    +
    +
    +
    +
    action a() {}
    +control c() {
    +    action a() {}
    +    // Illegal table: two actions with the same name
    +    table t { actions = { a; .a; } }
    +}
    +
    +
    +
    +

    Each action parameter that has a direction (in, inout, or out) +must be bound in the actions list specification; conversely, no directionless parameters may be bound in the list. The expressions -supplied as arguments to an action are not evaluated until the +supplied as arguments to an action are not evaluated until the action is invoked. Applying tables, whether directly via an expression -like table1.apply().hit, or indirectly, are forbidden in the -expressions supplied as action arguments. -

    -
    -
    -
    action a(in bit<32> x) { /* body omitted */ }
    -bit<32> z;
    -action b(inout bit<32> x, bit<8> data) { /* body omitted */ }
    -table t {
    -    actions = {
    -       // a; -- illegal, x parameter must be bound
    -       a(5);  // binding a's parameter x to 5
    -       b(z);  // binding b's parameter x to z
    -       // b(z, 3); -- illegal, cannot bind directionless data parameter
    -       // b(); -- illegal, x parameter must be bound
    -       // a(table2.apply().hit ? 5 : 3); -- illegal, cannot apply a table here
    -    }
    -}
    14.2.1.3. Default action
    -

    The default action for a table is an action that is invoked +like table1.apply().hit, or indirectly, are forbidden in the +expressions supplied as action arguments.

    +
    +
    +
    +
    action a(in bit<32> x) { /* body omitted */ }
    +bit<32> z;
    +action b(inout bit<32> x, bit<8> data) { /* body omitted */ }
    +table t {
    +    actions = {
    +       // a; -- illegal, x parameter must be bound
    +       a(5);  // binding a's parameter x to 5
    +       b(z);  // binding b's parameter x to z
    +       // b(z, 3); -- illegal, cannot bind directionless data parameter
    +       // b(); -- illegal, x parameter must be bound
    +       // a(table2.apply().hit ? 5 : 3); -- illegal, cannot apply a table here
    +    }
    +}
    +
    +
    +
    +
    +
    14.2.1.3. Default action
    +
    +

    The default action for a table is an action that is invoked automatically by the match-action unit whenever the lookup table does -not find a match for the supplied key. -

    -

    If present, the default_action property must appear after the action -property. It may be declared as const, indicating that it cannot -be changed dynamically by the control-plane. The default action -must be one of the actions that appear in the actions list. In -particular, the expressions passed as in, out, or inout +not find a match for the supplied key.

    +
    +
    +

    If present, the default_action property must appear after the action +property. It may be declared as const, indicating that it cannot +be changed dynamically by the control-plane. The default action +must be one of the actions that appear in the actions list. In +particular, the expressions passed as in, out, or inout parameters must be syntactically identical to the expressions used in -one of the elements of the actions list. -

    -

    For example, in the above table we could set the default action -as follows (marking it also as constant): -

    -
    -
    -
    const default_action = Rewrite_smac(48w0xAA_BB_CC_DD_EE_FF);
    -

    Note that the specified default action must supply arguments for the +one of the elements of the actions list.

    +
    +
    +

    For example, in the above table we could set the default action +as follows (marking it also as constant):

    +
    +
    +
    +
    const default_action = Rewrite_smac(48w0xAA_BB_CC_DD_EE_FF);
    +
    +
    +
    +

    Note that the specified default action must supply arguments for the control-plane-bound parameters (i.e., the directionless parameters), since the action is synthesized at compilation time. The expressions -supplied as arguments for parameters with a direction (in, inout, -or out) are evaluated when the action is invoked while the +supplied as arguments for parameters with a direction (in, inout, +or out) are evaluated when the action is invoked while the expressions supplied as arguments for directionless parameters are -evaluated at compile time. -

    -

    Continuing the example from the previous section, the following are several legal -and illegal specifications of default actions for the table t: -

    -
    -
    -
      default_action = a(5); // OK - no control-plane parameters
    -  // default_action = a(z); -- illegal, a's x parameter is already bound to 5
    -  default_action = b(z,8w8); // OK - bind b's data parameter to 8w8
    -  // default_action = b(z); -- illegal, b's data parameter is not bound
    -  // default_action = b(x, 3); -- illegal: x parameter of b bound to x instead of z
    14.2.1.4. Entries
    -

    While table entries are typically installed by the control plane, -tables may also be initialized at compile time with a set of entries. -

    -

    Declaring these entries with const entries +evaluated at compile time.

    +
    +
    +

    Continuing the example from the previous section, the following are several legal +and illegal specifications of default actions for the table t:

    +
    +
    +
    +
      default_action = a(5); // OK - no control-plane parameters
    +  // default_action = a(z); -- illegal, a's x parameter is already bound to 5
    +  default_action = b(z,8w8); // OK - bind b's data parameter to 8w8
    +  // default_action = b(z); -- illegal, b's data parameter is not bound
    +  // default_action = b(x, 3); -- illegal: x parameter of b bound to x instead of z
    +
    +
    +
    +
    +
    14.2.1.4. Entries
    +
    +

    While table entries are typically installed by the control plane, +tables may also be initialized at compile time with a set of entries.

    +
    +
    +

    Declaring these entries with const entries is useful in situations where tables are -used to implement fixed algorithmsdefining table entries statically +used to implement fixed algorithms---defining table entries statically enables expressing these algorithms directly in P4, which allows the compiler to infer how the table is actually used and potentially make better allocation decisions for targets with limited -resources. -

    -

    Declaring entries with entries (without the const qualifier) +resources.

    +
    +
    +

    Declaring entries with entries (without the const qualifier) enables one to specify a mix of some immutable entries that are always in the table, and some mutable entries that the control plane is -allowed to later change or remove. -

    -

    Entries declared in the P4 source are installed in the +allowed to later change or remove.

    +
    +
    +

    Entries declared in the P4 source are installed in the table when the program is loaded onto the target. Entries cannot be -specified for a table with no key (see Sec. 14.2.1.1). -

    -

    Table entries are defined using the following syntax: -

    -
    -
    -
    tableProperty
    - : optAnnotations optCONST ENTRIES '=' '{' entriesList '}'
    +specified for a table with no key (see Section 14.2.1.1).

    +
    +
    +

    Table entries are defined using the following syntax:

    +
    +
    +
    +
    tableProperty
    + : optAnnotations optCONST ENTRIES '=' '{' entriesList '}'
      ;
     
     entriesList
    -    : /* empty */
    +    : /* empty */
         | entriesList entry
         ;
     
     optCONST
    -    : /* empty */
    +    : /* empty */
         | CONST
         ;
     
     entryPriority
    - : PRIORITY '=' INTEGER ":"
    - | PRIORITY '=' '(' expression ')' ":"
    + : PRIORITY '=' INTEGER ":"
    + | PRIORITY '=' '(' expression ')' ":"
      ;
     
     entry
    -    : optCONST entryPriority keysetExpression ':' actionRef optAnnotations ';'
    -    | optCONST keysetExpression ':' actionRef optAnnotations ';'
    -    ;
    -

    Table entries defined using const entries are immutablei.e., they + : optCONST entryPriority keysetExpression ':' actionRef optAnnotations ';' + | optCONST keysetExpression ':' actionRef optAnnotations ';' + ; +

    +
    +
    +

    Table entries defined using const entries are immutable---i.e., they can only be read by the control plane. The control plane is not -allowed to remove or modify any entries defined within const -entries, nor is it allowed to add entries to such a table. -It is allowed for individual entries to have the const keyword +allowed to remove or modify any entries defined within const +entries, nor is it allowed to add entries to such a table. +It is allowed for individual entries to have the const keyword before them, but this is redundant when the entries are declared using -const entries. -

    -

    Table entries defined using entries (without a const qualifier -before it) may have const before them, or not, independently for -each entry. Entries with const before them may not be modified or -removed by the control plane. Entries without const may be modified +const entries.

    +
    +
    +

    Table entries defined using entries (without a const qualifier +before it) may have const before them, or not, independently for +each entry. Entries with const before them may not be modified or +removed by the control plane. Entries without const may be modified or removed by the control plane. It is permitted for the control plane to add entries to such a table (subject to table capacity -limitations), unlike tables declared with const entries. -

    -

    Whether the control plane is allowed to modify a table's default -action at run time is determined by the table's default_action table -property (see Section 14.2.1.3), independently of whether -the control plane is allowed to modify the entries of the table. -

    -

    The keysetExpression component of an entry is a tuple that must -provide a field for each key in the table keys (see Sec. -14.2.1). The table key type must match the type of the -element of the set. The actionRef component must be an action which +limitations), unlike tables declared with const entries.

    +
    +
    +

    Whether the control plane is allowed to modify a table’s default +action at run time is determined by the table’s default_action table +property (see Section 14.2.1.3), independently of whether +the control plane is allowed to modify the entries of the table.

    +
    +
    +

    The keysetExpression component of an entry is a tuple that must +provide a field for each key in the table keys (see Section 14.2.1). The table key type must match the type of the +element of the set. The actionRef component must be an action which appears in the table actions list (and must not have the -@defaultonly annotation), with all its arguments bound. -

    -

    If no entry priorities are specified in the source code, and +@defaultonly annotation), with all its arguments bound.

    +
    +
    +

    If no entry priorities are specified in the source code, and if the runtime API requires a priority for the entries of a -tablee.g. when using the P4 Runtime API, tables with at least one -ternary search key fieldthen the entries are matched in program +table---e.g. when using the P4 Runtime API, tables with at least one +ternary search key field---then the entries are matched in program order, stopping at the first matching entry. Architectures should define the significance of entry order (if any) for other kinds of -tables. -

    -

    Depending on the match_kind of the keys, key set expressions may define +tables.

    +
    +
    +

    Depending on the match_kind of the keys, key set expressions may define one or multiple entries. The compiler will synthesize the correct number of entries to be installed in the table. Target constraints may further restrict the ability of synthesizing entries. For example, if the number of synthesized entries exceeds the table size, the compiler implementation may choose to issue -a warning or an error, depending on target capabilities. -

    -

    To illustrate, consider the following example: -

    -
    -
    -
    header hdr {
    -    bit<8>  e;
    -    bit<16> t;
    -    bit<8>  l;
    -    bit<8>  r;
    -    bit<1>  v;
    -}
    -
    -struct Header_t {
    -    hdr h;
    -}
    -struct Meta_t {}
    -
    -control ingress(inout Header_t h, inout Meta_t m,
    -                inout standard_metadata_t standard_meta) {
    -
    -    action a() { standard_meta.egress_spec = 0; }
    -    action a_params(bit<9> x) { standard_meta.egress_spec = x; }
    -
    -    table t_exact_ternary {
    -
    -      key = {
    -            h.h.e : exact;
    -            h.h.t : ternary;
    -        }
    -
    -    actions = {
    -            a;
    -            a_params;
    -        }
    -
    -    default_action = a;
    -
    -        const entries = {
    -            (0x01, 0x1111 &&& 0xF   ) : a_params(1);
    -            (0x02, 0x1181           ) : a_params(2);
    -            (0x03, 0x1111 &&& 0xF000) : a_params(3);
    -            (0x04, 0x1211 &&& 0x02F0) : a_params(4);
    -            (0x04, 0x1311 &&& 0x02F0) : a_params(5);
    -            (0x06, _                ) : a_params(6);
    -            _                         : a;
    -        }
    -    }
    -
    -}
    -

    In this example we define a set of 7 entries, all of which invoke -action a_params except for the final entry which -invokes action a. Once the program is loaded, these entries are +a warning or an error, depending on target capabilities.

    +
    +
    +

    To illustrate, consider the following example:

    +
    +
    +
    +
    header hdr {
    +    bit<8>  e;
    +    bit<16> t;
    +    bit<8>  l;
    +    bit<8>  r;
    +    bit<1>  v;
    +}
    +
    +struct Header_t {
    +    hdr h;
    +}
    +struct Meta_t {}
    +
    +control ingress(inout Header_t h, inout Meta_t m,
    +                inout standard_metadata_t standard_meta) {
    +
    +    action a() { standard_meta.egress_spec = 0; }
    +    action a_params(bit<9> x) { standard_meta.egress_spec = x; }
    +
    +    table t_exact_ternary {
    +
    +  	key = {
    +            h.h.e : exact;
    +            h.h.t : ternary;
    +        }
    +
    +	actions = {
    +            a;
    +            a_params;
    +        }
    +
    +	default_action = a;
    +
    +        const entries = {
    +            (0x01, 0x1111 &&& 0xF   ) : a_params(1);
    +            (0x02, 0x1181           ) : a_params(2);
    +            (0x03, 0x1111 &&& 0xF000) : a_params(3);
    +            (0x04, 0x1211 &&& 0x02F0) : a_params(4);
    +            (0x04, 0x1311 &&& 0x02F0) : a_params(5);
    +            (0x06, _                ) : a_params(6);
    +            _                         : a;
    +        }
    +    }
    +
    +}
    +
    +
    +
    +

    In this example we define a set of 7 entries, all of which invoke +action a_params except for the final entry which +invokes action a. Once the program is loaded, these entries are installed in the table in the order they are enumerated in the -program. -

    Entry priorities
    -

    If a table has fields where their match_kinds are all exact or -lpm, there is no reason to assign numeric priorities to its entries. -If they are all exact, duplicate keys are not allowed, and thus +program.

    +
    +
    +
    14.2.1.4.1. Entry priorities
    +
    +

    If a table has fields where their match_kinds are all exact or +lpm, there is no reason to assign numeric priorities to its entries. +If they are all exact, duplicate keys are not allowed, and thus every lookup key can match at most one entry, so there is no need for -a tiebreaker. If there is an lpm field, the priority of the entry +a tiebreaker. If there is an lpm field, the priority of the entry corresponds to the length of the prefix, i.e. if a lookup key matches -multiple prefixes, the longest prefix is always the winner. -

    -

    For tables with other match_kind values, e.g. at least one ternary +multiple prefixes, the longest prefix is always the winner.

    +
    +
    +

    For tables with other match_kind values, e.g. at least one ternary field, in general it is possible to install multiple entries such that the same lookup key can match the key of multiple entries installed into the table at the same time. Control plane APIs such as P4Runtime -API4 and TDI5 require control plane software to +API [3] and TDI [4] require control plane software to provide a numeric priority with each entry added to such a table. This enables the data plane to determine which of several matching -entries is the “winner”, i.e. the one entry whose action is invoked. -

    -

    Unfortunately there are two commonly used, but different, ways of -interpreting numeric priority values. -

    -

    The P4Runtime API requires numeric priorities to be positive integers, +entries is the "winner", i.e. the one entry whose action is invoked.

    +
    +
    +

    Unfortunately there are two commonly used, but different, ways of +interpreting numeric priority values.

    +
    +
    +

    The P4Runtime API requires numeric priorities to be positive integers, i.e. 1 or larger, and defines that entries with larger priorities must win over entries with smaller priorities. We will call this -convention largest_priority_wins. -

    -

    TDI requires numeric priorities to be non-negative integers, i.e. 0 or +convention largest_priority_wins.

    +
    +
    +

    TDI requires numeric priorities to be non-negative integers, i.e. 0 or larger, and defines that entries with smaller priorities must win over entries with larger priorities. We will call this convention -smallest_priority_wins. -

    -

    We wish to support either of these conventions when developers specify +smallest_priority_wins.

    +
    +
    +

    We wish to support either of these conventions when developers specify priorities for initial table entries in the program. Thus there is a -table property largest_priority_wins. If explicitly specified for a -table, its value must be boolean. If true, then the priority values -use the largest_priority_wins convention. If false, then the -priority values use the smallest_priority_wins convention. If the +table property largest_priority_wins. If explicitly specified for a +table, its value must be boolean. If true, then the priority values +use the largest_priority_wins convention. If false, then the +priority values use the smallest_priority_wins convention. If the table property is not present at all, then the default convention is -true, corresponding to largest_priority_wins. -

    -

    We also wish to support developers that want the convenience of +true, corresponding to largest_priority_wins.

    +
    +
    +

    We also wish to support developers that want the convenience of predictable entry priority values automatically selected by the compiler, without having to write them in the program, plus the -ability to specify entry priorities explicitly, if they wish. -

    -

    In some cases, developers may wish the initial priority values to have -“gaps” between their values, to leave room for possible later +ability to specify entry priorities explicitly, if they wish.

    +
    +
    +

    In some cases, developers may wish the initial priority values to have +"gaps" between their values, to leave room for possible later insertion of new entries between two initial entries. They can achieve this by explicitly specifying all priority values, of course, -but as a convenience we define the table property priority_delta to +but as a convenience we define the table property priority_delta to be a positive integer value, with a default value of 1 if not specified for a table, to use as a default difference between the -priorities of consecutive entries. -

    -

    There are two steps that occur at compile time for a table with the -entries property involving entry priorities: -

    -
      -
    • Determine the value of the priority of every entry in the entries -list. -
    • -
    • Issue any errors or warnings that are appropriate for these priority -values. Warnings may be suppressed via an appropriate @noWarn -annotation. -
    - -

    These steps are performed independently for each table with the -entries property, and each is described in more detail below. -

    -

    In general, if the developer specifies a priority value for an entry, -that is the value that will be used. -

    -

    If the developer does not specify priority values for any entry, then -the compiler calculates priority values for every entry as follows: -

    -
    -
    -
    // For this pseudocode, table entries in the `entries` list are
    -// numbered 0 through n-1, 0 being the first to appear in order in the
    -// source code.  Their priority values are named prio[0] through
    -// prio[n-1].
    -int p = 1;
    -if (largest_priority_wins == true) {
    -    for (int j = n-1; j >= 0; j -= 1) {
    +priorities of consecutive entries.

    +
    +
    +

    There are two steps that occur at compile time for a table with the +entries property involving entry priorities:

    +
    +
    +
      +
    • +

      Determine the value of the priority of every entry in the entries +list.

      +
    • +
    • +

      Issue any errors or warnings that are appropriate for these priority +values. Warnings may be suppressed via an appropriate @noWarn +annotation.

      +
    • +
    +
    +
    +

    These steps are performed independently for each table with the +entries property, and each is described in more detail below.

    +
    +
    +

    In general, if the developer specifies a priority value for an entry, +that is the value that will be used.

    +
    +
    +

    If the developer does not specify priority values for any entry, then +the compiler calculates priority values for every entry as follows:

    +
    +
    +
    +
    // For this pseudocode, table entries in the `entries` list are
    +// numbered 0 through n-1, 0 being the first to appear in order in the
    +// source code.  Their priority values are named prio[0] through
    +// prio[n-1].
    +int p = 1;
    +if (largest_priority_wins == true) {
    +    for (int j = n-1; j >= 0; j -= 1) {
             prio[j] = p;
             p += priority_delta;
         }
    -} else {
    -    for (int j = 0; j < n; j += 1) {
    +} else {
    +    for (int j = 0; j < n; j += 1) {
             prio[j] = p;
             p += priority_delta;
         }
    -}
    -

    If the developer specifies priority values for at least one entry, +} +

    +
    +
    +

    If the developer specifies priority values for at least one entry, then in order to simplify the rules for determining priorities of -entries without one in the source code, the first entry must have a +entries without one in the source code, the first entry must have a priority value explicitly provided. The priorities of entries that do -not have one in the source code (if any) are determined as follows: -

    -
    -
    -
    // Same conventions here as in the previous block of pseudocode above.
    -// If entry j has a priority value specified in the source code,
    -// prio_specified[j] is true, otherwise it is false.
    -assert(prio_specified[0]);  // compile time error if prio_specified[0] is false
    -p = prio[0];
    -for (int j = 1; j < n; j += 1) {
    -    if (prio_specified[j]) {
    +not have one in the source code (if any) are determined as follows:

    +
    +
    +
    +
    // Same conventions here as in the previous block of pseudocode above.
    +// If entry j has a priority value specified in the source code,
    +// prio_specified[j] is true, otherwise it is false.
    +assert(prio_specified[0]);  // compile time error if prio_specified[0] is false
    +p = prio[0];
    +for (int j = 1; j < n; j += 1) {
    +    if (prio_specified[j]) {
             p = prio[j];
    -    } else {
    -        if (largest_priority_wins == true) {
    +    } else {
    +        if (largest_priority_wins == true) {
                 p -= priority_delta;
    -        } else {
    +        } else {
                 p += priority_delta;
             }
             prio[j] = p;
         }
    -}
    -

    This is the end of the first step: determining entry priorities. -

    -

    The priorities determined in this way are the values used when the P4 +} +

    +
    +
    +

    This is the end of the first step: determining entry priorities.

    +
    +
    +

    The priorities determined in this way are the values used when the P4 program is first loaded into a device. Afterwards, the priorities may -only change by means provided by the control plane API in use. -

    -

    In the second step, the compiler issues errors for out of range +only change by means provided by the control plane API in use.

    +
    +
    +

    In the second step, the compiler issues errors for out of range priority values, and/or warnings for certain combinations of entry priorities that might be unintended by the developer, unless the -developer explicitly disables those warnings. -

    -

    If any priority values are negative, or larger than the maximum -supported value, that is a compile time error. -

    -

    If the annotation @noWarn("duplicate_priorities") is not used on -the entries table property, then the compiler issues a warning if +developer explicitly disables those warnings.

    +
    +
    +

    If any priority values are negative, or larger than the maximum +supported value, that is a compile time error.

    +
    +
    +

    If the annotation @noWarn("duplicate_priorities") is not used on +the entries table property, then the compiler issues a warning if any two entries for the same table have equal priority values. Both P4Runtime and TDI leave it unspecified which entry is the winner if a lookup key matches multiple keys that all have the same priority, hence a warning is useful to less experienced developers that are -unfamiliar with this unspecified behavior. -

    -

    If the annotation @noWarn("duplicate_priorities") is used on the -entries table property, then no warnings of this type are ever +unfamiliar with this unspecified behavior.

    +
    +
    +

    If the annotation @noWarn("duplicate_priorities") is used on the +entries table property, then no warnings of this type are ever issued by the compiler. Using equal priority values for multiple entries in the same table is sometimes useful in reducing the number -of hardware updates required when adding entries to such a table. -

    -

    If the annotation @noWarn("entries_out_of_priority_order") is not -used on the entries table property, then the compiler issues a -warning if: -

    -
      -
    • If largest_priority_wins is true for the table, and there is any -pair of consecutive entries where prio[j] < prio[j+1], then a -warning is issued for that pair of entries. -
    • -
    • If largest_priority_wins is false for the table, and there is any -pair of consecutive entries where prio[j] > prio[j+1], then a -warning is issued for that pair of entries. -
    - -

    This warning is useful to developers that want the order that entries +of hardware updates required when adding entries to such a table.

    +
    +
    +

    If the annotation @noWarn("entries_out_of_priority_order") is not +used on the entries table property, then the compiler issues a +warning if:

    +
    +
    +
      +
    • +

      If largest_priority_wins is true for the table, and there is any +pair of consecutive entries where prio[j] < prio[j+1], then a +warning is issued for that pair of entries.

      +
    • +
    • +

      If largest_priority_wins is false for the table, and there is any +pair of consecutive entries where prio[j] > prio[j+1], then a +warning is issued for that pair of entries.

      +
    • +
    +
    +
    +

    This warning is useful to developers that want the order that entries appear in the source code to match the relative priority of entries in -the target device. -

    -

    If the annotation @noWarn("entries_out_of_priority_order") is used -on the entries table property, then no warnings of this type are +the target device.

    +
    +
    +

    If the annotation @noWarn("entries_out_of_priority_order") is used +on the entries table property, then no warnings of this type are ever issued by the compiler for this table. This option is provided for developers who explicitly choose to specify entries in an order -that does not match their relative priority order. -

    -

    The following example is the same as the first example in section -14.2.1.4, except for the definition of table t_exact_ternary -shown below. -

    -
    -
    -
    table t_exact_ternary {
    -    key = {
    -        h.h.e : exact;
    -        h.h.t : ternary;
    -    }
    -
    -    actions = {
    -        a;
    -        a_params;
    -    }
    -
    -    default_action = a;
    -
    -    largest_priority_wins = false;
    -    priority_delta = 10;
    -    @noWarn("duplicate_priorities")
    -    entries = {
    -        const priority=10: (0x01, 0x1111 &&& 0xF   ) : a_params(1);
    -                           (0x02, 0x1181           ) : a_params(2); // priority=20
    -                           (0x03, 0x1000 &&& 0xF000) : a_params(3); // priority=30
    -        const              (0x04, 0x0210 &&& 0x02F0) : a_params(4); // priority=40
    -              priority=40: (0x04, 0x0010 &&& 0x02F0) : a_params(5);
    -                           (0x06, _                ) : a_params(6); // priority=50
    -    }
    -}
    -

    The entries that do not have an explicit priority specified will be +that does not match their relative priority order.

    +
    +
    +

    The following example is the same as the first example in Section 14.2.1.4, except for the definition of table t_exact_ternary +shown below.

    +
    +
    +
    +
    table t_exact_ternary {
    +    key = {
    +        h.h.e : exact;
    +        h.h.t : ternary;
    +    }
    +
    +    actions = {
    +        a;
    +        a_params;
    +    }
    +
    +    default_action = a;
    +
    +    largest_priority_wins = false;
    +    priority_delta = 10;
    +    @noWarn("duplicate_priorities")
    +    entries = {
    +        const priority=10: (0x01, 0x1111 &&& 0xF   ) : a_params(1);
    +                           (0x02, 0x1181           ) : a_params(2); // priority=20
    +                           (0x03, 0x1000 &&& 0xF000) : a_params(3); // priority=30
    +        const              (0x04, 0x0210 &&& 0x02F0) : a_params(4); // priority=40
    +              priority=40: (0x04, 0x0010 &&& 0x02F0) : a_params(5);
    +                           (0x06, _                ) : a_params(6); // priority=50
    +    }
    +}
    +
    +
    +
    +

    The entries that do not have an explicit priority specified will be assigned the priority values shown in the comments, because -priority_delta is 10, and because of those entries that do have -priority values specified. -

    -

    Normally this program would cause a warning about multiple entries +priority_delta is 10, and because of those entries that do have +priority values specified.

    +
    +
    +

    Normally this program would cause a warning about multiple entries with the same priority of 40, but those warnings will be suppressed -because of the @noWarn("duplicate_priorities") annotation. -

    14.2.1.5. Size
    -

    The size is an optional property of a table. When present, its -value must always be a compile-time known value that is an integer. The size property -is specified in units of number of table entries. -

    -

    If a table is specified with a size property of value N, it is +because of the @noWarn("duplicate_priorities") annotation.

    +
    +
    +
    +
    +
    14.2.1.5. Size
    +
    +

    The size is an optional property of a table. When present, its +value must always be a compile-time known value that is an integer. The size property +is specified in units of number of table entries.

    +
    +
    +

    If a table is specified with a size property of value N, it is recommended that a compiler should choose a data plane implementation -that is capable of storing N table entries. This does not guarantee -that an arbitrary set of N entries can always be inserted in such -a table, only that there is some set of N entries that can be -inserted. For example, attempts to add some combinations of N +that is capable of storing N table entries. This does not guarantee +that an arbitrary set of N entries can always be inserted in such +a table, only that there is some set of N entries that can be +inserted. For example, attempts to add some combinations of N entries may fail because the compiler selected a hash table with -O(1) guaranteed search time. See “Size property of P4 tables and -parser value sets” P4SizeProperty for further discussion on some P4 -table implementations and what they are able to guarantee. -

    -

    If a P4 implementation must dimension table resources at compile time, -they may treat it as an error if they encounter a table with no size -property. -

    -

    Some P4 implementations may be able to dynamically dimension table -resources at run time. If a size value is specified in the P4 -program, it is recommended that such an implementation uses the size -value as the initial capacity of the table. -

    14.2.1.6. Additional properties
    -

    A table declaration defines its essential control and data plane -interfacesi.e., keys and actions. However, the best way to +O(1) guaranteed search time. See "Size property of P4 tables and +parser value sets" [2] for further discussion on some P4 +table implementations and what they are able to guarantee.

    +
    +
    +

    If a P4 implementation must dimension table resources at compile time, +they may treat it as an error if they encounter a table with no size +property.

    +
    +
    +

    Some P4 implementations may be able to dynamically dimension table +resources at run time. If a size value is specified in the P4 +program, it is recommended that such an implementation uses the size +value as the initial capacity of the table.

    +
    +
    +
    +
    14.2.1.6. Additional properties
    +
    +

    A table declaration defines its essential control and data plane +interfaces---i.e., keys and actions. However, the best way to implement a table may actually depend on the nature of the entries that will be installed at runtime (for example, tables could be dense or sparse, could be implemented as hash-tables, associative memories, tries, etc.) In addition, some architectures may support extra table properties whose semantics lies outside the scope of this specification. For example, in architectures where table resources are -statically allocated, programmers may be required to define a size +statically allocated, programmers may be required to define a size table property, which can be used by the compiler back-end to allocate storage resources. However, these architecture-specific properties may not change the semantics of table lookups, which always -produce either a hit and an action or a missthey can +produce either a hit and an action or a miss---they can only change how those results are interpreted on the state of the data plane. This restriction is needed to ensure that it is possible to -reason about the behavior of tables during compilation. -

    -

    As another example, an implementation property could be used to +reason about the behavior of tables during compilation.

    +
    +
    +

    As another example, an implementation property could be used to pass additional information to the compiler back-end. The value of -this property could be an instance of an extern block chosen +this property could be an instance of an extern block chosen from a suitable library of components. For example, the core -functionality of the P414 table action_profile constructs +functionality of the P414 table action_profile constructs could be implemented on architectures that support this feature using -a construct such as the following: -

    -
    -
    -
    extern ActionProfile {
    -   ActionProfile(bit<32> size); // number of distinct actions expected
    -}
    -table t {
    -    key = { /* body omitted */ }
    -    size = 1024;
    -    implementation = ActionProfile(32);  // constructor invocation
    -}
    -

    Here the action profile might be used to optimize for the case where +a construct such as the following:

    +
    +
    +
    +
    extern ActionProfile {
    +   ActionProfile(bit<32> size); // number of distinct actions expected
    +}
    +table t {
    +    key = { /* body omitted */ }
    +    size = 1024;
    +    implementation = ActionProfile(32);  // constructor invocation
    +}
    +
    +
    +
    +

    Here the action profile might be used to optimize for the case where the table has a large number of entries, but the actions associated with those entries are expected to range over a small number of distinct values. Introducing a layer of indirection enables sharing -identical entries, which can significantly reduce the table's storage -requirements. -

    14.2.2. Match-action unit invocation

    -

    A table can be invoked by calling its apply +identical entries, which can significantly reduce the table’s storage +requirements.

    +
    +
    +
    +
    +

    14.2.2. Match-action unit invocation

    +
    +

    A table can be invoked by calling its apply method. Calling an apply method on a table instance returns a value -with a struct type with three fields. This structure is -synthesized by the compiler automatically. For each table T, the -compiler synthesizes an enum and a struct, shown in -pseudo-P4: -

    -
    -
    -
    enum action_list(T) {
    -   // one field for each action in the actions list of table T
    +with a struct type with three fields. This structure is
    +synthesized by the compiler automatically. For each table T, the
    +compiler synthesizes an enum and a struct, shown in
    +pseudo-P4:

    +
    +
    +
    +
    enum action_list(T) {
    +   // one field for each action in the actions list of table T
     }
    -struct apply_result(T) {
    -    bool hit;
    -    bool miss;
    +struct apply_result(T) {
    +    bool hit;
    +    bool miss;
         action_list(T) action_run;
    -}
    -

    The evaluation of the apply method sets the hit field to true -and the field miss to false if a match is found in the lookup-table; -if a match is not found hit is set to false and miss to true. +} +

    +
    +
    +

    The evaluation of the apply method sets the hit field to true +and the field miss to false if a match is found in the lookup-table; +if a match is not found hit is set to false and miss to true. These bits can be used to drive the execution of the control-flow in the -control block that invoked the table: -

    -
    -
    -
    if (ipv4_match.apply().hit) {
    -    // there was a hit
    -} else {
    -    // there was a miss
    -}
    -
    -if (ipv4_host.apply().miss) {
    -    ipv4_lpm.apply(); // Look up the route only if host table missed
    -}
    -

    The action_run field indicates which kind of action was executed +control block that invoked the table:

    +
    +
    +
    +
    if (ipv4_match.apply().hit) {
    +    // there was a hit
    +} else {
    +    // there was a miss
    +}
    +
    +if (ipv4_host.apply().miss) {
    +    ipv4_lpm.apply(); // Look up the route only if host table missed
    +}
    +
    +
    +
    +

    The action_run field indicates which kind of action was executed (irrespective of whether it was a hit or a miss). It can be used in a -switch statement: -

    -
    -
    -
    switch (dmac.apply().action_run) {
    -    Drop_action: { return; }
    -}

    14.2.3. Match-action unit execution semantics

    -

    The semantics of a table invocation statement: -

    -
    -
    -
    m.apply();
    -

    is given by the following pseudocode (see also Figure -11): -

    -
    -
    -
    apply_result(m) m.apply() {
    +switch statement:

    +
    +
    +
    +
    switch (dmac.apply().action_run) {
    +    Drop_action: { return; }
    +}
    +
    +
    +
    +
    +

    14.2.3. Match-action unit execution semantics

    +
    +

    The semantics of a table invocation statement:

    +
    +
    +
    +
    m.apply();
    +
    +
    +
    +

    is given by the following pseudocode (see also Figure 11:

    +
    +
    +
    +
    apply_result(m) m.apply() {
         apply_result(m) result;
     
    -    var lookupKey = m.buildKey(m.key); // using key block
    -    action RA = m.table.lookup(lookupKey);
    -    if (RA == null) {      // miss in lookup table
    -       result.hit = false;
    -       RA = m.default_action;  // use default action
    +    var lookupKey = m.buildKey(m.key); // using key block
    +    action RA = m.table.lookup(lookupKey);
    +    if (RA == null) {      // miss in lookup table
    +       result.hit = false;
    +       RA = m.default_action;  // use default action
         }
    -    else {
    -       result.hit = true;
    +    else {
    +       result.hit = true;
         }
         result.miss = !result.hit;
         result.action_run = action_type(RA);
         evaluate_and_copy_in_RA_args(RA);
         execute(RA);
         copy_out_RA_args(RA);
    -    return result;
    -}
    -

    The behavior of the buildKey call in the pseudocode above is to + return result; +} +

    +
    +
    +

    The behavior of the buildKey call in the pseudocode above is to evaluate each key expression in the order they appear in the table key definition. The behavior must be the same as if the result of evaluating each key expression is assigned to a fresh temporary variable, before starting the evaluation of the following key -expression. For example, this P4 table definition and apply call: -

    -
    -
    -
    bit<8> f1 (in bit<8> a, inout bit<8> b) {
    -    b = a + 5;
    -    return a >> 1;
    -}
    -bit<8> x;
    -bit<8> y;
    -table t1 {
    -    key = {
    -        y & 0x7  : exact @name("masked_y");
    -        f1(x, y) : exact @name("f1");
    -        y        : exact;
    -    }
    -    // ... rest of table properties defined here, not relevant to example
    -}
    -apply {
    -    // assign values to x and y here, not relevant to example
    -    t1.apply();
    -}
    -

    is equivalent in behavior to the following table definition and apply -call: -

    -
    -
    -
    // same definition of f1, x, and y as before, so they are not repeated here
    -bit<8> tmp_1;
    -bit<8> tmp_2;
    -bit<8> tmp_3;
    -table t1 {
    -    key = {
    -        tmp_1 : exact @name("masked_y");
    -        tmp_2 : exact @name("f1");
    -        tmp_3 : exact @name("y");
    -    }
    -    // ... rest of table properties defined here, not relevant to example
    -}
    -apply {
    -    // assign values to x and y here, not relevant to example
    -    tmp_1 = y & 0x7;
    -    tmp_2 = f1(x, y);
    -    tmp_3 = y;
    -    t1.apply();
    -}
    -

    Note that the second code example above is given in order to specify +expression. For example, this P4 table definition and apply call:

    +
    +
    +
    +
    bit<8> f1 (in bit<8> a, inout bit<8> b) {
    +    b = a + 5;
    +    return a >> 1;
    +}
    +bit<8> x;
    +bit<8> y;
    +table t1 {
    +    key = {
    +        y & 0x7  : exact @name("masked_y");
    +        f1(x, y) : exact @name("f1");
    +        y        : exact;
    +    }
    +    // ... rest of table properties defined here, not relevant to example
    +}
    +apply {
    +    // assign values to x and y here, not relevant to example
    +    t1.apply();
    +}
    +
    +
    +
    +

    is equivalent in behavior to the following table definition and apply +call:

    +
    +
    +
    +
    // same definition of f1, x, and y as before, so they are not repeated here
    +bit<8> tmp_1;
    +bit<8> tmp_2;
    +bit<8> tmp_3;
    +table t1 {
    +    key = {
    +        tmp_1 : exact @name("masked_y");
    +        tmp_2 : exact @name("f1");
    +        tmp_3 : exact @name("y");
    +    }
    +    // ... rest of table properties defined here, not relevant to example
    +}
    +apply {
    +    // assign values to x and y here, not relevant to example
    +    tmp_1 = y & 0x7;
    +    tmp_2 = f1(x, y);
    +    tmp_3 = y;
    +    t1.apply();
    +}
    +
    +
    +
    +

    Note that the second code example above is given in order to specify the behavior of the first one. An implementation is free to choose -any technique that achieves this behavior6. -

    14.3. The Match-Action Pipeline Abstract Machine

    -

    We can describe the computational model of a match-action pipeline, +any technique that achieves this behavior.[5]

    +
    +
    +
    +
    +

    14.3. The Match-Action Pipeline Abstract Machine

    +
    +

    We can describe the computational model of a match-action pipeline, embodied by a control block: the body of the control block is executed, similarly to the execution of a traditional imperative -program: -

    -
      -
    • At runtime, statements within a block are executed in the order -they appear in the control block. -
    • -
    • Execution of the return statement causes immediate termination -of the execution of the current control block, and a return to -the caller. -
    • -
    • Execution of the exit statement causes the immediate -termination of the execution of the current control block and -of all the enclosing caller control blocks. -
    • -
    • Applying a table executes the corresponding -match-action unit, as described above. -
    -

    14.4. Invoking controls

    -

    P4 allows controls to invoke the services of other controls, similar +program:

    +
    +
    +
      +
    • +

      At runtime, statements within a block are executed in the order +they appear in the control block.

      +
    • +
    • +

      Execution of the return statement causes immediate termination +of the execution of the current control block, and a return to +the caller.

      +
    • +
    • +

      Execution of the exit statement causes the immediate +termination of the execution of the current control block and +of all the enclosing caller control blocks.

      +
    • +
    • +

      Applying a table executes the corresponding +match-action unit, as described above.

      +
    • +
    +
    +
    +
    +

    14.4. Invoking controls

    +
    +

    P4 allows controls to invoke the services of other controls, similar to subroutines. To invoke the services of another control, it must be first instantiated; the services of an instance are invoked by calling -it using its apply method. -

    -

    The following example shows a control invocation: -

    -
    -
    -
    control Callee(inout IPv4 ipv4) { /* body omitted */ }
    -control Caller(inout Headers h) {
    -     Callee() instance;  // instance of callee
    -     apply {
    -          instance.apply(h.ipv4);  // invoke control
    -     }
    -}
    -

    As with parsers, when a control is instantiated, local instantiations +it using its apply method.

    +
    +
    +

    The following example shows a control invocation:

    +
    +
    +
    +
    control Callee(inout IPv4 ipv4) { /* body omitted */ }
    +control Caller(inout Headers h) {
    +     Callee() instance;  // instance of callee
    +     apply {
    +          instance.apply(h.ipv4);  // invoke control
    +     }
    +}
    +
    +
    +
    +

    As with parsers, when a control is instantiated, local instantiations of stateful objects are evaluated recursively. That is, each instantiation of a control has a unique set of local tables, extern objects, inner control instances, etc. Thus, in general, invoking a control instance twice is not the same as invoking two copies of the same control instance. Note however, that local variables do not persist across invocations of the control. This semantics also applies -to direct invocation (see Section 15.1). -

    -

    When a control is instantiated, all its local declarations of stateful +to direct invocation (see Section 15.1).

    +
    +
    +

    When a control is instantiated, all its local declarations of stateful instantiations are evaluated recursively. Each instantiation of a control will have a unique set of local tables, extern objects, and inner control instances. Thus, invoking a control instance twice is different from invoking two control instances each once, where the former accesses the same local stateful constructs while the latter -access two different copies. -

    -

    The exactly-once evaluation only applies to local stateful instantiations. For local variable declarations, -whether in the apply block or out, and whether with initializers or not, they are always evaluated +access two different copies.

    +
    +
    +

    The exactly-once evaluation only applies to local stateful instantiations. For local variable declarations, +whether in the apply block or out, and whether with initializers or not, they are always evaluated when a control instance is invoked. That is, local variables in a control never persist across invocations. For variables -declared outside the apply block, they are evaluated at the beginning of execution. -

    -

    All the behavior above also applies to direct invocation (see Section 15.1). -

    15. Parameterization

    -

    In order to support libraries of useful P4 components, both parsers -and control blocks can be additionally parameterized through the -use of constructor parameters. -

    -

    Consider again the parser declaration syntax: -

    -
    -
    -
    parserDeclaration
    +declared outside the apply block, they are evaluated at the beginning of execution.

    +
    +
    +

    All the behavior above also applies to direct invocation (see Section 15.1).

    +
    +
    +
    +
    +
    +

    15. Parameterization

    +
    +
    +

    In order to support libraries of useful P4 components, both parsers +and control blocks can be additionally parameterized through the +use of constructor parameters.

    +
    +
    +

    Consider again the parser declaration syntax:

    +
    +
    +
    +
    parserDeclaration
         : parserTypeDeclaration optConstructorParameters
    -      "{" parserLocalElements parserStates "}"
    +      "{" parserLocalElements parserStates "}"
         ;
     
     optConstructorParameters
    -    : /* empty */
    -    | "(" parameterList ")"
    -    ;
    -

    From this grammar fragment we infer that a parser declaration -may have two sets of parameters: -

    -
      -
    • The runtime parser parameters (parameterList) -
    • -
    • Optional parser constructor parameters -(optConstructorParameters) -
    - -

    Constructor parameters must be directionless (i.e., they cannot -be in, out, or inout) and where the parser is + : /* empty */ + | "(" parameterList ")" + ; +

    +
    +
    +

    From this grammar fragment we infer that a parser declaration +may have two sets of parameters:

    +
    +
    +
      +
    • +

      The runtime parser parameters (parameterList)

      +
    • +
    • +

      Optional parser constructor parameters +(optConstructorParameters)

      +
    • +
    +
    +
    +

    Constructor parameters must be directionless (i.e., they cannot +be in, out, or inout) and where the parser is instantiated, it must be possible to fully evaluate the expressions -supplied for these parameters at compilation time. -

    -

    Consider the following example: -

    -
    -
    -
    parser GenericParser(packet_in b, out Packet_header p)
    -                    (bool udpSupport) {   // constructor parameters
    -    state start {
    -        b.extract(p.ethernet);
    -        transition select(p.ethernet.etherType) {
    -            16w0x0800: ipv4;
    -        }
    -    }
    -    state ipv4 {
    -        b.extract(p.ipv4);
    -        transition select(p.ipv4.protocol) {
    -           6: tcp;
    -           17: tryudp;
    -        }
    -    }
    -    state tryudp {
    -        transition select(udpSupport) {
    -            false: accept;
    -            true : udp;
    -        }
    -    }
    -    state udp {
    -         // body omitted
    -    }
    -}
    -

    When instantiating the GenericParser it is necessary to supply a value for -the udpSupport parameter, as in the following example: -

    -
    -
    -
    // topParser is a GenericParser where udpSupport = false
    -GenericParser(false) topParser;

    15.1. Direct type invocation

    -

    Controls and parsers are often instantiated exactly once. As a light +supplied for these parameters at compilation time.

    +
    +
    +

    Consider the following example:

    +
    +
    +
    +
    parser GenericParser(packet_in b, out Packet_header p)
    +                    (bool udpSupport) {   // constructor parameters
    +    state start {
    +        b.extract(p.ethernet);
    +        transition select(p.ethernet.etherType) {
    +            16w0x0800: ipv4;
    +        }
    +    }
    +    state ipv4 {
    +        b.extract(p.ipv4);
    +        transition select(p.ipv4.protocol) {
    +           6: tcp;
    +           17: tryudp;
    +        }
    +    }
    +    state tryudp {
    +        transition select(udpSupport) {
    +            false: accept;
    +            true : udp;
    +        }
    +    }
    +    state udp {
    +         // body omitted
    +    }
    +}
    +
    +
    +
    +

    When instantiating the GenericParser it is necessary to supply a value for +the udpSupport parameter, as in the following example:

    +
    +
    +
    +
    // topParser is a GenericParser where udpSupport = false
    +GenericParser(false) topParser;
    +
    +
    +
    +

    15.1. Direct type invocation

    +
    +

    Controls and parsers are often instantiated exactly once. As a light syntactic sugar, control and parser declarations with no constructor parameters may be applied directly, as if they were an instance. This -has the effect of creating and applying a local instance of that type. -

    -
    -
    -
    control Callee(/* parameters omitted */) { /* body omitted */ }
    -
    -control Caller(/* parameters omitted */)(/* parameters omitted */) {
    -    apply {
    -        Callee.apply(/* arguments omitted */); // Callee is treated as an instance
    -    }
    -}
    -

    The definition of Caller is equivalent to the following. -

    -
    -
    -
    control Caller(/* parameters omitted */)(/* parameters omitted */) {
    -    @name("Callee") Callee() Callee_inst; // local instance of Callee
    -    apply {
    -        Callee_inst.apply(/* arguments omitted */);         // Callee_inst is applied
    -    }
    -}
    -
    -
    -
    directApplication
    -    : typeName "." APPLY "(" argumentList ")" ";"
    -    | specializedType "." APPLY "(" argumentList ")" ";"
    -    ;
    -

    This feature is intended to streamline the common case where a type is -instantiated exactly once. -

    -

    The second production in the grammar allows direct calls for generic -controls or parsers: -

    -
    -
    -
    control Callee<T>(/* parameters omitted */) { /* body omitted */ }
    -
    -control Caller(/* parameters omitted */)(/* parameters omitted */) {
    -    apply {
    -        // Callee<bit<32>> is treated as an instance
    -        Callee<bit<32>>.apply(/* arguments omitted */);
    -    }
    -}
    -

    For completeness, the behavior of directly invoking the same type more -than once is defined as follows. -

    -
      -
    • Direct type invocation in different scopes will result in different -local instances with different fully-qualified control names. -
    • -
    • In the same scope, direct type invocation will result in a different -local instance per invocationhowever, instances of the same type -will share the same global name, via the @name annotation. If +has the effect of creating and applying a local instance of that type.

      +
    +
    +
    +
    control Callee(/* parameters omitted */) { /* body omitted */ }
    +
    +control Caller(/* parameters omitted */)(/* parameters omitted */) {
    +    apply {
    +        Callee.apply(/* arguments omitted */); // Callee is treated as an instance
    +    }
    +}
    +
    +
    +
    +

    The definition of Caller is equivalent to the following.

    +
    +
    +
    +
    control Caller(/* parameters omitted */)(/* parameters omitted */) {
    +    @name("Callee") Callee() Callee_inst; // local instance of Callee
    +    apply {
    +        Callee_inst.apply(/* arguments omitted */);         // Callee_inst is applied
    +    }
    +}
    +
    +
    +
    +
    +
    directApplication
    +    : typeName "." APPLY "(" argumentList ")" ";"
    +    | specializedType "." APPLY "(" argumentList ")" ";"
    +    ;
    +
    +
    +
    +

    This feature is intended to streamline the common case where a type is +instantiated exactly once.

    +
    +
    +

    The second production in the grammar allows direct calls for generic +controls or parsers:

    +
    +
    +
    +
    control Callee<T>(/* parameters omitted */) { /* body omitted */ }
    +
    +control Caller(/* parameters omitted */)(/* parameters omitted */) {
    +    apply {
    +        // Callee<bit<32>> is treated as an instance
    +        Callee<bit<32>>.apply(/* arguments omitted */);
    +    }
    +}
    +
    +
    +
    +

    For completeness, the behavior of directly invoking the same type more +than once is defined as follows.

    +
    +
    +
      +
    • +

      Direct type invocation in different scopes will result in different +local instances with different fully-qualified control names.

      +
    • +
    • +

      In the same scope, direct type invocation will result in a different +local instance per invocation---however, instances of the same type +will share the same global name, via the @name annotation. If the type contains controllable entities, then invoking it directly more than once in the same scope is illegal, because it will produce multiple controllable entities with the same fully-qualified control -name. -

    - -

    See Section 18.3.2 for details of @name -annotations. -

    -

    No direct invocation is possible for controls or parsers that require +name.

    + + +
    +
    +

    See Section 18.3.2 for details of @name +annotations.

    +
    +
    +

    No direct invocation is possible for controls or parsers that require constructor arguments. These need to be instantiated before they are -invoked. -

    16. Deparsing

    -

    The inverse of parsing is deparsing, or packet construction. P4 does +invoked.

    +
    +
    +
    +
    +
    +

    16. Deparsing

    +
    +
    +

    The inverse of parsing is deparsing, or packet construction. P4 does not provide a separate language for packet deparsing; deparsing is -done in a control block that has at least one parameter of type packet_out. -

    -

    For example, the following code sequence writes first an Ethernet -header and then an IPv4 header into a packet_out: -

    -
    -
    -
    control TopDeparser(inout Parsed_packet p, packet_out b) {
    -    apply {
    -        b.emit(p.ethernet);
    -        b.emit(p.ip);
    -    }
    -}
    -

    Emitting a header appends the header to the packet_out only if +done in a control block that has at least one parameter of type packet_out.

    +
    +
    +

    For example, the following code sequence writes first an Ethernet +header and then an IPv4 header into a packet_out:

    +
    +
    +
    +
    control TopDeparser(inout Parsed_packet p, packet_out b) {
    +    apply {
    +        b.emit(p.ethernet);
    +        b.emit(p.ip);
    +    }
    +}
    +
    +
    +
    +

    Emitting a header appends the header to the packet_out only if the header is valid. Emitting a header stack will emit all elements of -the stack in order of increasing indices. -

    16.1. Data insertion into packets

    -

    The packet_out datatype is defined in the P4 core library, and +the stack in order of increasing indices.

    +
    +
    +

    16.1. Data insertion into packets

    +
    +

    The packet_out datatype is defined in the P4 core library, and reproduced below. It provides a method for appending data to an -output packet called emit: -

    -
    -
    -
    extern packet_out {
    -    void emit<T>(in T data);
    -}
    -

    The emit method supports appending the data contained in a -header, header stack, struct, or header union to the output packet. -

    -
      -
    • When applied to a header, emit appends the data in the header to -the packet if it is valid and otherwise behaves like a no-op. -
    • -
    • When applied to a header stack, emit recursively invokes itself to -each element of the stack. -
    • -
    • When applied to a struct or header union, emit recursively -invokes itself to each field. Note, a struct must not contain -a field of type error or enum because these types cannot be serialized. -
    - -

    It is illegal to invoke emit on an expression whose type is a base type, -enum, or error. -

    -

    We can define the meaning of the emit method in pseudocode as -follows: -

    -
    -
    -
    packet_out {
    +output packet called emit:

    +
    +
    +
    +
    extern packet_out {
    +    void emit<T>(in T data);
    +}
    +
    +
    +
    +

    The emit method supports appending the data contained in a +header, header stack, struct, or header union to the output packet.

    +
    +
    +
      +
    • +

      When applied to a header, emit appends the data in the header to +the packet if it is valid and otherwise behaves like a no-op.

      +
    • +
    • +

      When applied to a header stack, emit recursively invokes itself to +each element of the stack.

      +
    • +
    • +

      When applied to a struct or header union, emit recursively +invokes itself to each field. Note, a struct must not contain +a field of type error or enum because these types cannot be serialized.

      +
    • +
    +
    +
    +

    It is illegal to invoke emit on an expression whose type is a base type, +enum, or error.

    +
    +
    +

    We can define the meaning of the emit method in pseudocode as +follows:

    +
    +
    +
    +
    packet_out {
         byte[] data;
         unsigned lengthInBits;
    -    void initializeForWriting() {
    -        this.data.clear();
    -        this.lengthInBits = 0;
    +    void initializeForWriting() {
    +        this.data.clear();
    +        this.lengthInBits = 0;
         }
    -    /// Append data to the packet. Type T must be a header, header
    -    /// stack, header union, or struct formed recursively from those types
    -    void emit<T>(T data) {
    -        if (isHeader(T))
    -            if(data.valid$) {
    -                this.data.append(data);
    -                this.lengthInBits += data.lengthInBits;
    +    /// Append data to the packet. Type T must be a header, header
    +    /// stack, header union, or struct formed recursively from those types
    +    void emit<T>(T data) {
    +        if (isHeader(T))
    +            if(data.valid$) {
    +                this.data.append(data);
    +                this.lengthInBits += data.lengthInBits;
                 }
    -        else if (isHeaderStack(T))
    +        else if (isHeaderStack(T))
                 for (e : data)
                      emit(e);
    -        else if (isHeaderUnion(T) || isStruct(T))
    +        else if (isHeaderUnion(T) || isStruct(T))
                 for (f : data.fields$)
                      emit(e.f)
    -        // Other cases for T are illegal
    -    }
    -

    Here we use the special valid$ identifier to indicate the hidden -valid bit of headers and fields$ to indicate the list of fields -for a struct or header union. We also use standard for-each notation to -iterate through the elements of a stack (e : data) and list of -fields for header unions and structs (f : data.fields$). The + // Other cases for T are illegal + } +

    +
    +
    +

    Here we use the special valid$ identifier to indicate the hidden +valid bit of headers and fields$ to indicate the list of fields +for a struct or header union. We also use standard for-each notation to +iterate through the elements of a stack (e : data) and list of +fields for header unions and structs (f : data.fields$). The iteration order for a struct is the order those fields appear in the -type declaration. -

    17. Architecture description

    -

    The architecture description must be provided by the target +type declaration.

    +
    +
    +
    +
    +
    +

    17. Architecture description

    +
    +
    +

    The architecture description must be provided by the target manufacturer in the form of a library P4 source file that contains at -least one declaration for a package; this package must be +least one declaration for a package; this package must be instantiated by the user to construct a program for a target. For an -example see the Very Simple Switch declaration from Section -5.1. -

    -

    The architecture description file may pre-define data types, +example see the Very Simple Switch declaration from Section 5.1.

    +
    +
    +

    The architecture description file may pre-define data types, constants, helper package implementations, and errors. It must also declare the types of all the programmable blocks that will appear in -the final target: parsers and control blocks. The +the final target: parsers and control blocks. The programmable blocks may optionally be grouped together in packages, -which can be nested. -

    -

    Since some of the target components may manipulate user-defined types, +which can be nested.

    +
    +
    +

    Since some of the target components may manipulate user-defined types, which are unknown at the target declaration time, these are described using type variables, which must be used parametrically in the -programi.e., type variables are checked similar to Java generics, -not C++ templates. -

    17.1. Example architecture description

    -
    -

    switcharch -

    -
    - -
    Figure 12. Fragment of example switch architecture.
    -

    The following example describes a switch by using two packages, each -containing a parser, a match-action pipeline, and a deparser: -

    -
    -
    -
    parser Parser<IH>(packet_in b, out IH parsedHeaders);
    -// ingress match-action pipeline
    -control IPipe<T, IH, OH>(in IH inputHeaders,
    -                         in InControl inCtrl,
    -                         out OH outputHeaders,
    -                         out T toEgress,
    -                         out OutControl outCtrl);
    -// egress match-action pipeline
    -control EPipe<T, IH, OH>(in IH inputHeaders,
    -                         in InControl inCtrl,
    -                         in T fromIngress,
    -                         out OH outputHeaders,
    -                         out OutControl outCtrl);
    -control Deparser<OH>(in OH outputHeaders, packet_out b);
    -package Ingress<T, IH, OH>(Parser<IH> p,
    -                           IPipe<T, IH, OH> map,
    -                           Deparser<OH> d);
    -package Egress<T, IH, OH>(Parser<IH> p,
    -                          EPipe<T, IH, OH> map,
    -                          Deparser<OH> d);
    -package Switch<T>(Ingress<T, _, _> ingress, Egress<T, _, _> egress);
    -

    Just from these declarations, even without reading a precise +program---i.e., type variables are checked similar to Java generics, +not C++ templates.

    +
    +
    +

    17.1. Example architecture description

    +
    +
    +switcharch +
    +
    Figure 12. Fragment of example switch architecture.
    +
    +
    +

    The following example describes a switch by using two packages, each +containing a parser, a match-action pipeline, and a deparser:

    +
    +
    +
    +
    parser Parser<IH>(packet_in b, out IH parsedHeaders);
    +// ingress match-action pipeline
    +control IPipe<T, IH, OH>(in IH inputHeaders,
    +                         in InControl inCtrl,
    +                         out OH outputHeaders,
    +                         out T toEgress,
    +                         out OutControl outCtrl);
    +// egress match-action pipeline
    +control EPipe<T, IH, OH>(in IH inputHeaders,
    +                         in InControl inCtrl,
    +                         in T fromIngress,
    +                         out OH outputHeaders,
    +                         out OutControl outCtrl);
    +control Deparser<OH>(in OH outputHeaders, packet_out b);
    +package Ingress<T, IH, OH>(Parser<IH> p,
    +                           IPipe<T, IH, OH> map,
    +                           Deparser<OH> d);
    +package Egress<T, IH, OH>(Parser<IH> p,
    +                          EPipe<T, IH, OH> map,
    +                          Deparser<OH> d);
    +package Switch<T>(Ingress<T, _, _> ingress, Egress<T, _, _> egress);
    +
    +
    +
    +

    Just from these declarations, even without reading a precise description of the target, the programmer can infer some useful information about the architecture of the described switch, as shown -in Figure 12: -

    -
      -
    • The switch contains two separate packages Ingress and Egress. -
    • -
    • The Parser, IPipe, and Deparser in the Ingress package -are chained together in order. In addition, the Ingress.IPipe block has an -input of type Ingress.IH, which is an output of the Ingress.Parser. -
    • -
    • Similarly, the Parser, EPipe, and Deparser are -chained in the Egress package. -
    • -
    • The Ingress.IPipe is connected to the Egress.EPipe, because the -first outputs a value of type T, which is an input to the -second. Note that the occurrences of the type variable T are -instantiated with the same type in Switch. In contrast, -the Ingress type IH and the Egress type IH may be +in Figure 12:

      +
    +
    +
      +
    • +

      The switch contains two separate packages Ingress and Egress.

      +
    • +
    • +

      The Parser, IPipe, and Deparser in the Ingress package +are chained together in order. In addition, the Ingress.IPipe block has an +input of type Ingress.IH, which is an output of the Ingress.Parser.

      +
    • +
    • +

      Similarly, the Parser, EPipe, and Deparser are +chained in the Egress package.

      +
    • +
    • +

      The Ingress.IPipe is connected to the Egress.EPipe, because the +first outputs a value of type T, which is an input to the +second. Note that the occurrences of the type variable T are +instantiated with the same type in Switch. In contrast, +the Ingress type IH and the Egress type IH may be different. To force them to be the same, we could instead -declare IH and OH at the switch level: -package Switch<T,IH,OH>(Ingress<T, IH, OH> ingress, Egress<T, IH, OH> egress). -

    - -

    Hence, this architecture models a target switch that contains two -separate channels between the ingress and egress pipeline: -

    -
      -
    • A channel that can pass data directly via its argument of type -T. On a software target with shared memory between ingress and +declare IH and OH at the switch level: +package Switch<T,IH,OH>(Ingress<T, IH, OH> ingress, Egress<T, IH, OH> egress).

      +
    • +
    +
    +
    +

    Hence, this architecture models a target switch that contains two +separate channels between the ingress and egress pipeline:

    +
    +
    +
      +
    • +

      A channel that can pass data directly via its argument of type +T. On a software target with shared memory between ingress and egress this could be implemented by passing directly a pointer; on an architecture without shared memory presumably the compiler will -need to automatically synthesize serialization code. -

    • -
    • A channel that can pass data indirectly using a parser and deparser -that serializes data into a packet and back. -
    -

    17.2. Example architecture program

    -

    To construct a program for the architecture, the P4 program must -instantiate a top-level package by passing values for all its -arguments creating a variable called main in the top-level +need to automatically synthesize serialization code.

    + +
  • +

    A channel that can pass data indirectly using a parser and deparser +that serializes data into a packet and back.

    +
  • + +
    +
    +
    +

    17.2. Example architecture program

    +
    +

    To construct a program for the architecture, the P4 program must +instantiate a top-level package by passing values for all its +arguments creating a variable called main in the top-level namespace. The types of the arguments must match the types of the -parametersafter a suitable substitution of the type variables. The +parameters---after a suitable substitution of the type variables. The type substitution can be expressed directly, using type specialization, or can be inferred by a compiler, using a unification -algorithm like Hindley-Milner. -

    -

    For example, given the following type declarations: -

    -
    -
    -
    parser Prs<T>(packet_in b, out T result);
    -control Pipe<T>(in T data);
    -package Switch<T>(Prs<T> p, Pipe<T> map);
    -

    and the following declarations: -

    -
    -
    -
    parser P(packet_in b, out bit<32> index) { /* body omitted */ }
    -control Pipe1(in bit<32> data) { /* body omitted */ }
    -control Pipe2(in bit<8> data) { /* body omitted */ }
    -

    The following is a legal declaration for the top-level target: -

    -
    -
    -
    Switch(P(), Pipe1()) main;
    -

    And the following is illegal: -

    -
    -
    -
    Switch(P(), Pipe2()) main;
    -

    The latter declaration is incorrect because the parser P -requires T to be bit<32>, while Pipe2 requires T -to be bit<8>. -

    -

    The user can also explicitly specify values for the type variables -(otherwise the compiler has to infer values for these type variables): -

    -
    -
    -
    Switch<bit<32>>(P(), Pipe1()) main;

    17.3. A Packet Filter Model

    -
    -

    packetfilter -

    -
    - -
    Figure 13. A packet filter target model. The parser computes a Boolean value, which is used to decide whether the packet is dropped.
    -

    To illustrate the versatility of the P4 architecture description language, +algorithm like Hindley-Milner.

    +
    +
    +

    For example, given the following type declarations:

    +
    +
    +
    +
    parser Prs<T>(packet_in b, out T result);
    +control Pipe<T>(in T data);
    +package Switch<T>(Prs<T> p, Pipe<T> map);
    +
    +
    +
    +

    and the following declarations:

    +
    +
    +
    +
    parser P(packet_in b, out bit<32> index) { /* body omitted */ }
    +control Pipe1(in bit<32> data) { /* body omitted */ }
    +control Pipe2(in bit<8> data) { /* body omitted */ }
    +
    +
    +
    +

    The following is a legal declaration for the top-level target:

    +
    +
    +
    +
    Switch(P(), Pipe1()) main;
    +
    +
    +
    +

    And the following is illegal:

    +
    +
    +
    +
    Switch(P(), Pipe2()) main;
    +
    +
    +
    +

    The latter declaration is incorrect because the parser P +requires T to be bit<32>, while Pipe2 requires T +to be bit<8>.

    +
    +
    +

    The user can also explicitly specify values for the type variables +(otherwise the compiler has to infer values for these type variables):

    +
    +
    +
    +
    Switch<bit<32>>(P(), Pipe1()) main;
    +
    +
    +
    +
    +

    17.3. A Packet Filter Model

    +
    +
    +packetfilter +
    +
    Figure 13. A packet filter target model. The parser computes a Boolean value, which is used to decide whether the packet is dropped.
    +
    +
    +

    To illustrate the versatility of the P4 architecture description language, we give an example of another architecture: one which models a packet filter that makes a drop/no drop decision based only on the -computation in a P4 parser, as shown in Figure 13. -

    -

    This model could be used to program packet filters running in the -Linux kernel. For example, we could replace the tcpdump language with +computation in a P4 parser, as shown in Figure 13.

    +
    +
    +

    This model could be used to program packet filters running in the +Linux kernel. For example, we could replace the tcpdump language with the much more powerful P4 language; P4 can seamlessly support new -protocols, while providing complete “type safety” during packet +protocols, while providing complete "type safety" during packet processing. For such a target, the P4 compiler could generate an eBPF (Extended Berkeley Packet Filter) program, which is injected by the -tcpdump utility into the Linux kernel, and executed by the eBPF -kernel JIT compiler/runtime. -

    -

    In this case the target is the Linux kernel, and the architecture -model is a packet filter. -

    -

    The declaration for this architecture is as follows: -

    -
    -
    -
    parser Parser<H>(packet_in packet, out H headers);
    -control Filter<H>(inout H headers, out bool accept);
    -
    -package Program<H>(Parser<H> p, Filter<H> f);

    18. P4 abstract machine: Evaluation

    -

    The evaluation of a P4 program is done in two stages: -

    -
      -
    • static evaluation: at compile time the P4 program is -analyzed and all stateful blocks are instantiated. -
    • -
    • dynamic evaluation: at runtime each P4 functional block is +tcpdump utility into the Linux kernel, and executed by the eBPF +kernel JIT compiler/runtime.

      +
    +
    +

    In this case the target is the Linux kernel, and the architecture +model is a packet filter.

    +
    +
    +

    The declaration for this architecture is as follows:

    +
    +
    +
    +
    parser Parser<H>(packet_in packet, out H headers);
    +control Filter<H>(inout H headers, out bool accept);
    +
    +package Program<H>(Parser<H> p, Filter<H> f);
    +
    +
    +
    +
    +
    +
    +

    18. P4 abstract machine: Evaluation

    +
    +
    +

    The evaluation of a P4 program is done in two stages:

    +
    +
    +
      +
    • +

      static evaluation: at compile time the P4 program is +analyzed and all stateful blocks are instantiated.

      +
    • +
    • +

      dynamic evaluation: at runtime each P4 functional block is executed to completion, in isolation, when it receives control from the -architecture -

    -

    18.1. Compile-time known and local compile-time known values

    -

    Certain expressions in a P4 program have the property that their value +architecture

    + + +
    +
    +

    18.1. Compile-time known and local compile-time known values

    +
    +

    Certain expressions in a P4 program have the property that their value can be determined at compile time. Moreover, for some of these expressions, their value can be determined only using information in -the current scope. We call these compile-time known values and -local compile-time known values respectively. -

    -

    The following are local compile-time known values: -

    -
      -
    • Integer literals, Boolean literals, and string literals. +the current scope. We call these compile-time known values and +local compile-time known values respectively.

      +
    +
    +

    The following are local compile-time known values:

    +
    +
    +
      +
    • +

      Integer literals, Boolean literals, and string literals.

    • -
    • Identifiers declared as constants using the const keyword. +
    • +

      Identifiers declared as constants using the const keyword.

    • -
    • Identifiers declared in an error, enum, or match_kind declaration. +
    • +

      Identifiers declared in an error, enum, or match_kind declaration.

    • -
    • The default identifier. +
    • +

      The default identifier.

    • -
    • The size field of a value with type header stack. +
    • +

      The size field of a value with type header stack.

    • -
    • The _ identifier when used as a select expression label +
    • +

      The _ identifier when used as a select expression label

    • -
    • The expression {#} representing an invalid header or header union value. +
    • +

      The expression {#} representing an invalid header or header union value.

    • -
    • Instances constructed by instance declarations (Section 11.3) and constructor invocations. +
    • +

      Instances constructed by instance declarations (Section [#sec-instantiations]) and constructor invocations.

    • -
    • Identifiers that represent declared types, actions, functions, tables, parsers, controls, or packages. +
    • +

      Identifiers that represent declared types, actions, functions, tables, parsers, controls, or packages.

    • -
    • Tuple expression where all components are local compile-time known values. +
    • +

      Tuple expression where all components are local compile-time known values.

    • -
    • Structure-valued expressions, where all fields are local compile-time known values. +
    • +

      Structure-valued expressions, where all fields are local compile-time known values.

    • -
    • Expressions evaluating to a list type, where all elements are local compile-time known values. +
    • +

      Expressions evaluating to a list type, where all elements are local compile-time known values.

    • -
    • Legal casts applied to local compile-time known values. +
    • +

      Legal casts applied to local compile-time known values.

    • -
    • The following expressions (+, -, |+|, |-|, *, / , %, !, &, |, ^, &&, ||, << , >>, ~, /, >, <, ==, !=, <=, >=, ++, [:], ?:) when their operands are all local compile-time known values. +
    • +

      The following expressions (`, `-`, `||, |-|, *, / `, `%, !, &, |, ^, &&, ||, << `, `>>, ~, /, >, <, ==, !=, , >=, ++, [:], ?:) when their operands are all local compile-time known values.

    • -
    • Expressions of the form e.minSizeInBits(), e.minSizeInBytes(), e.maxSizeInBits() and e.maxSizeInBytes() where the type of e is not generic. -
    - -

    The following are compile-time known values: -

    -
      -
    • All local compile-time known values. +
    • +

      Expressions of the form e.minSizeInBits(), e.minSizeInBytes(), e.maxSizeInBits() and e.maxSizeInBytes() where the type of e is not generic.

    • -
    • Constructor parameters (i.e., the declared parameters for a parser, control, etc.) +
    +
    +
    +

    The following are compile-time known values:

    +
    +
    +
      +
    • +

      All local compile-time known values.

    • -
    • Tuple expression where all components are compile-time known values. +
    • +

      Constructor parameters (i.e., the declared parameters for a parser, control, etc.)

    • -
    • Expressions evaluating to a list type, where all elements are compile-time known values. +
    • +

      Tuple expression where all components are compile-time known values.

    • -
    • Structure-valued expressions, where all fields are compile-time known values. +
    • +

      Expressions evaluating to a list type, where all elements are compile-time known values.

    • -
    • Expressions evaluating to a list type, where all elements are compile-time known values. +
    • +

      Structure-valued expressions, where all fields are compile-time known values.

    • -
    • Legal casts applied to compile-time known values. +
    • +

      Expressions evaluating to a list type, where all elements are compile-time known values.

    • -
    • The following expressions (+, -, |+|, |-|, *, / , %, cast, !, &, |, ^, &&, ||, << , >> , ~, /, >, <, ==, !=, <=, >=, ++, [:], ?:) when their operands are all compile-time known values. +
    • +

      Legal casts applied to compile-time known values.

    • -
    • Expressions of the form e.minSizeInBits(), e.minSizeInBytes(), e.maxSizeInBits() and e.maxSizeInBytes() where the the type of e is generic. -
    - -

    Intuitively, the main difference between compile-time known values -and local compile-time known values is that the former also contains +

  • +

    The following expressions (`, `-`, `||, |-|, *, / `, `%, cast, !, &, |, ^, &&, ||, << `, `>> `, `~, /, >, <, ==, !=, , >=, ++, [:], ?:) when their operands are all compile-time known values.

    +
  • +
  • +

    Expressions of the form e.minSizeInBits(), e.minSizeInBytes(), e.maxSizeInBits() and e.maxSizeInBytes() where the the type of e is generic.

    +
  • + +
    +
    +

    Intuitively, the main difference between compile-time known values +and local compile-time known values is that the former also contains constructor parameters. The distinction is important when it comes to defining the meaning of features like types. For example, in the type -bit<e>, the expression e must be a local compile-time known -value. Suppose instead that e were a constructor parameteri.e., +bit<e>, the expression e must be a local compile-time known +value. Suppose instead that e were a constructor parameter---i.e., merely a compile-time known value. In this situation, it would be -impossible to resolve bit<e> to a concrete type using purely local -informationwe would have to wait until the constructor was -instantiated and the value of e known. -

    18.2. Compile-time Evaluation

    -

    Evaluation of a program proceeds in order of declarations, starting in -the top-level namespace: -

    -
      -
    • All declarations (e.g., parsers, controls, types, -constants) evaluate to themselves. -
    • -
    • Each table evaluates to a table instance. -
    • -
    • Constructor invocations evaluate to stateful objects of the +impossible to resolve bit<e> to a concrete type using purely local +information---we would have to wait until the constructor was +instantiated and the value of e known.

      +
    +
    +
    +

    18.2. Compile-time Evaluation

    +
    +

    Evaluation of a program proceeds in order of declarations, starting in +the top-level namespace:

    +
    +
    +
      +
    • +

      All declarations (e.g., parsers, controls, types, +constants) evaluate to themselves.

      +
    • +
    • +

      Each table evaluates to a table instance.

      +
    • +
    • +

      Constructor invocations evaluate to stateful objects of the corresponding type. For this purpose, all constructor arguments are evaluated recursively and bound to the constructor parameters. Constructor arguments must be compile-time known values. The order of evaluation of the constructor arguments should -be unimportant all evaluation orders should produce the same -results. +be unimportant --- all evaluation orders should produce the same +results.

    • -
    • Instantiations evaluate to named stateful objects. +
    • +

      Instantiations evaluate to named stateful objects.

    • -
    • The instantiation of a parser or control block +
    • +

      The instantiation of a parser or control block recursively evaluates all stateful instantiations declared in the -block. -

    • -
    • The result of the program's evaluation is the value of the top-level main -variable. -
    - -

    Note that all stateful values are instantiated at compilation time. -

    -

    As an example, consider the following program fragment: -

    -
    -
    -
    // architecture declaration
    -parser P(/* parameters omitted */);
    -control C(/* parameters omitted */);
    -control D(/* parameters omitted */);
    -
    -package Switch(P prs, C ctrl, D dep);
    -
    -extern Checksum16 { /* body omitted */}
    -
    -// user code
    -Checksum16() ck16; // checksum unit instance
    -
    -parser TopParser(/* parameters omitted */)(Checksum16 unit) { /* body omitted */}
    -control Pipe(/* parameters omitted */) { /* body omitted */}
    -control TopDeparser(/* parameters omitted */)(Checksum16 unit) { /* body omitted */}
    -
    -Switch(TopParser(ck16),
    -       Pipe(),
    -       TopDeparser(ck16)) main;
    -

    The evaluation of this program proceeds as follows: -

    -
      -
    1. The declarations of P, C, D, Switch, and Checksum16 all -evaluate to themselves. -
    2. -
    3. The Checksum16() ck16 instantiation is evaluated and it -produces an object named ck16 with type Checksum16. -
    4. -
    5. The declarations for TopParser, Pipe, and TopDeparser -evaluate as themselves. -
    6. -
    7. The main variable instantiation is evaluated: - -
        -
      1. The arguments to the constructor are evaluated recursively -
      2. -
      3. TopParser(ck16) is a constructor invocation -
      4. -
      5. Its argument is evaluated recursively; it evaluates to the ck16 -object -
      6. -
      7. The constructor itself is evaluated, leading to the -instantiation of an object of type TopParser -
      8. -
      9. Similarly, Pipe() and TopDeparser(ck16) are -evaluated as constructor calls. -
      10. -
      11. All the arguments of the Switch package constructor have -been evaluated (they are an instance of TopParser, an -instance of Pipe, and an instance of TopDeparser). -Their signatures are matched with the Switch declaration. -
      12. -
      13. Finally, the Switch constructor can be evaluated. The -result is an instance of the Switch package (that -contains a TopParser named prs the first -parameter of the Switch; a Pipe named ctrl; -and a TopDeparser named dep). -
    8. -
    9. The result of the program evaluation is the value of the main -variable, which is the above instance of the Switch package. -
    - -

    Figure 14 shows the result of the evaluation in a +block.

    + +
  • +

    The result of the program’s evaluation is the value of the top-level main +variable.

    +
  • + +
    +
    +

    Note that all stateful values are instantiated at compilation time.

    +
    +
    +

    As an example, consider the following program fragment:

    +
    +
    +
    +
    // architecture declaration
    +parser P(/* parameters omitted */);
    +control C(/* parameters omitted */);
    +control D(/* parameters omitted */);
    +
    +package Switch(P prs, C ctrl, D dep);
    +
    +extern Checksum16 { /* body omitted */}
    +
    +// user code
    +Checksum16() ck16; // checksum unit instance
    +
    +parser TopParser(/* parameters omitted */)(Checksum16 unit) { /* body omitted */}
    +control Pipe(/* parameters omitted */) { /* body omitted */}
    +control TopDeparser(/* parameters omitted */)(Checksum16 unit) { /* body omitted */}
    +
    +Switch(TopParser(ck16),
    +       Pipe(),
    +       TopDeparser(ck16)) main;
    +
    +
    +
    +

    The evaluation of this program proceeds as follows:

    +
    +
    +
      +
    1. +

      The declarations of P, C, D, Switch, and Checksum16 all +evaluate to themselves.

      +
    2. +
    3. +

      The Checksum16() ck16 instantiation is evaluated and it +produces an object named ck16 with type Checksum16.

      +
    4. +
    5. +

      The declarations for TopParser, Pipe, and TopDeparser +evaluate as themselves.

      +
    6. +
    7. +

      The main variable instantiation is evaluated:

      +
      +
        +
      1. +

        The arguments to the constructor are evaluated recursively

        +
      2. +
      3. +

        TopParser(ck16) is a constructor invocation

        +
        +
          +
        1. +

          Its argument is evaluated recursively; it evaluates to the ck16 +object

          +
        2. +
        +
        +
      4. +
      5. +

        The constructor itself is evaluated, leading to the +instantiation of an object of type TopParser

        +
      6. +
      7. +

        Similarly, Pipe() and TopDeparser(ck16) are +evaluated as constructor calls.

        +
      8. +
      9. +

        All the arguments of the Switch package constructor have +been evaluated (they are an instance of TopParser, an +instance of Pipe, and an instance of TopDeparser). +Their signatures are matched with the Switch declaration.

        +
      10. +
      11. +

        Finally, the Switch constructor can be evaluated. The +result is an instance of the Switch package (that +contains a TopParser named prs the first +parameter of the Switch; a Pipe named ctrl; +and a TopDeparser named dep).

        +
      12. +
      +
      +
    8. +
    9. +

      The result of the program evaluation is the value of the main +variable, which is the above instance of the Switch package.

      +
    10. +
    +
    +
    +

    Figure 14 shows the result of the evaluation in a graphical form. The result is always a graph of instances. There is -only one instance of Checksum16, called ck16, shared -between the TopParser and TopDeparser. Whether this is +only one instance of Checksum16, called ck16, shared +between the TopParser and TopDeparser. Whether this is possible is architecture-dependent. Specific target compilers may -require distinct checksum units to be used in distinct blocks. -

    -
    -

    compileeval -

    -
    - -
    Figure 14. Evaluation result.

    18.3. Control plane names

    -

    Every controllable entity exposed in a P4 program must be assigned a +require distinct checksum units to be used in distinct blocks.

    +
    +
    +
    +compileeval +
    +
    Figure 14. Evaluation result.
    +
    +
    +
    +

    18.3. Control plane names

    +
    +

    Every controllable entity exposed in a P4 program must be assigned a unique, fully-qualified name, which the control plane may use to -interact with that entity. The following entities are controllable. -

    -
      -
    • value sets +interact with that entity. The following entities are controllable.

      +
    +
    +
      +
    • +

      value sets

    • -
    • tables +
    • +

      tables

    • -
    • keys +
    • +

      keys

    • -
    • actions +
    • +

      actions

    • -
    • extern instances -
    - -

    A fully qualified name consists of the local name of a controllable +

  • +

    extern instances

    +
  • + +
    +
    +

    A fully qualified name consists of the local name of a controllable entity prepended with the fully qualified name of its enclosing namespace. Hence, the following program constructs, which enclose controllable entities, must themselves have unique, fully-qualified -names. -

    -
      -
    • control instances -
    • -
    • parser instances -
    - -

    Evaluation may create multiple instances from one type, each of which -must have a unique, fully-qualified name. -

    18.3.1. Computing control-plane names

    -

    The fully-qualified name of a construct is derived by concatenating +names.

    +
    +
    +
      +
    • +

      control instances

      +
    • +
    • +

      parser instances

      +
    • +
    +
    +
    +

    Evaluation may create multiple instances from one type, each of which +must have a unique, fully-qualified name.

    +
    +
    +

    18.3.1. Computing control-plane names

    +
    +

    The fully-qualified name of a construct is derived by concatenating the fully-qualified name of its enclosing construct with its local name. Constructs with no enclosing namespace, i.e. those defined at the global scope, have the same local and fully-qualified names. The local names of controllable entities and enclosing constructs are -derived from the syntax of a P4 program as follows. -

    18.3.1.1. Value sets
    -

    For each value_set construct, its syntactic name becomes the local -name of the value set. For example: -

    -
    -
    -
    struct vsk_t {
    -    @match(ternary)
    -    bit<16> port;
    -}
    -value_set<vsk_t>(4) pvs;
    -

    This value_set's local name is pvs. -

    18.3.1.2. Tables
    -

    For each table construct, its syntactic name becomes the local -name of the table. For example: -

    -
    -
    -
    control c(/* parameters omitted */)() {
    -    table t { /* body omitted */ }
    -}
    -

    This table's local name is t. -

    18.3.1.3. Keys
    -

    Syntactically, table keys are expressions. For simple expressions, +derived from the syntax of a P4 program as follows.

    +
    +
    +
    18.3.1.1. Value sets
    +
    +

    For each value_set construct, its syntactic name becomes the local +name of the value set. For example:

    +
    +
    +
    +
    struct vsk_t {
    +    @match(ternary)
    +    bit<16> port;
    +}
    +value_set<vsk_t>(4) pvs;
    +
    +
    +
    +

    This value_set’s local name is pvs.

    +
    +
    +
    +
    18.3.1.2. Tables
    +
    +

    For each table construct, its syntactic name becomes the local +name of the table. For example:

    +
    +
    +
    +
    control c(/* parameters omitted */)() {
    +    table t { /* body omitted */ }
    +}
    +
    +
    +
    +

    This table’s local name is t.

    +
    +
    +
    +
    18.3.1.3. Keys
    +
    +

    Syntactically, table keys are expressions. For simple expressions, the local key name can be generated from the expression itself; the algorithm by which a compiler derives control-plane names for complex -key expressions is target-dependent. -

    -

    The spec suggests, but does not mandate, the following algorithm for -generating names for some kinds of key expressions: -

    - - - - - - - -
    Kind Example Name
    The isValid() method. h.isValid() "h.isValid()"
    Array accesses. header_stack[1] "header_stack[1]"
    Constants. 1 "1"
    Field projections. data.f1 "data.f1"
    Slices. f1[3:0] "f1[3:0]"
    Masks. h.src & 0xFFFF "h.src & 0xFFFF"
    -

    In the following example, the previous algorithm would derive for -table t two keys with names data.f1 and hdrs[3].f2. -

    -
    -
    -
    table t {
    -    keys = {
    -        data.f1 : exact;
    -        hdrs[3].f2 : exact;
    -    }
    -    actions = { /* body omitted */ }
    -}
    -

    If a compiler cannot generate a name for a key it requires the key -expression to be annotated with a @name annotation (Section -20.3.3), as in the following example: -

    -
    -
    -
    table t {
    -    keys = {
    -        data.f1 + 1 : exact @name("f1_mask");
    -    }
    -    actions = { /* body omitted */ }
    -}
    -

    Here, the @name("f1_mask") annotation assigns the local name "f1_mask" -to this key. -

    18.3.1.4. Actions
    -

    For each action construct, its syntactic name is the local name -of the action. For example: -

    -
    -
    -
    control c(/* parameters omitted */)() {
    -    action a(...) { /* body omitted */ }
    -}
    -

    This action's local name is a. -

    18.3.1.5. Instances
    -

    The local names of extern, parser, and control +key expressions is target-dependent.

    +
    +
    +

    The spec suggests, but does not mandate, the following algorithm for +generating names for some kinds of key expressions:

    +
    + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    KindExampleName

    The isValid() method.

    h.isValid()

    "h.isValid()"

    Array accesses.

    header_stack[1]

    "header_stack[1]"

    Constants.

    1

    "1"

    Field projections.

    data.f1

    "data.f1"

    Slices.

    f1[3:0]

    "f1[3:0]"

    Masks.

    h.src & 0xFFFF

    "h.src & 0xFFFF"

    +
    +

    In the following example, the previous algorithm would derive for +table t two keys with names data.f1 and hdrs[3].f2.

    +
    +
    +
    +
    table t {
    +    keys = {
    +        data.f1 : exact;
    +        hdrs[3].f2 : exact;
    +    }
    +    actions = { /* body omitted */ }
    +}
    +
    +
    +
    +

    If a compiler cannot generate a name for a key it requires the key +expression to be annotated with a @name annotation (Section 20.3.3), as in the following example:

    +
    +
    +
    +
    table t {
    +    keys = {
    +        data.f1 + 1 : exact @name("f1_mask");
    +    }
    +    actions = { /* body omitted */ }
    +}
    +
    +
    +
    +

    Here, the @name("f1_mask") annotation assigns the local name "f1_mask" +to this key.

    +
    +
    +
    +
    18.3.1.4. Actions
    +
    +

    For each action construct, its syntactic name is the local name +of the action. For example:

    +
    +
    +
    +
    control c(/* parameters omitted */)() {
    +    action a(...) { /* body omitted */ }
    +}
    +
    +
    +
    +

    This action’s local name is a.

    +
    +
    +
    +
    18.3.1.5. Instances
    +
    +

    The local names of extern, parser, and control instances are derived based on how the instance is used. If the instance is bound to a name, that name becomes its local control plane -name. For example, if control C is declared as, -

    -
    -
    -
    control C(/* parameters omitted */)() { /* body omitted */ }
    -

    and instantiated as, -

    -
    -
    -
    C() c_inst;
    -

    then the local name of the instance is c_inst. -

    -

    Alternatively, if the instance is created as an actual argument, then its +name. For example, if control C is declared as,

    +
    +
    +
    +
    control C(/* parameters omitted */)() { /* body omitted */ }
    +
    +
    +
    +

    and instantiated as,

    +
    +
    +
    +
    C() c_inst;
    +
    +
    +
    +

    then the local name of the instance is c_inst.

    +
    +
    +

    Alternatively, if the instance is created as an actual argument, then its local name is the name of the formal parameter to which it will be -bound. For example, if extern E and control C are declared as, -

    -
    -
    -
    extern E { /* body omitted */ }
    -control C( /* parameters omitted */ )(E e_in) { /* body omitted */ }
    -

    and instantiated as, -

    -
    -
    -
    C(E()) c_inst;
    -

    then the local name of the extern instance is e_in. -

    -

    If the construct being instantiated is passed as an argument to a +bound. For example, if extern E and control C are declared as,

    +
    +
    +
    +
    extern E { /* body omitted */ }
    +control C( /* parameters omitted */ )(E e_in) { /* body omitted */ }
    +
    +
    +
    +

    and instantiated as,

    +
    +
    +
    +
    C(E()) c_inst;
    +
    +
    +
    +

    then the local name of the extern instance is e_in.

    +
    +
    +

    If the construct being instantiated is passed as an argument to a package, the instance name is derived from the user-supplied type definition when possible. In the following example, the local name of -the instance of MyC is c, and the local name of the extern -is e2, not e1. -

    -
    -
    -
    extern E { /* body omitted */ }
    -control ArchC(E e1);
    -package Arch(ArchC c);
    -
    -control MyC(E e2)() { /* body omitted */ }
    -Arch(MyC()) main;
    -

    Note that in this example, the architecture will supply an instance of -the extern when it applies the instance of MyC passed to the Arch -package. The fully-qualified name of that instance is main.c.e2. -

    -

    Next, consider a larger example that demonstrates name generation when -there are multiple instances. -

    -
    -
    -
    control Callee() {
    -    table t { /* body omitted */ }
    -    apply { t.apply(); }
    -}
    -control Caller() {
    -    Callee() c1;
    -    Callee() c2;
    -    apply {
    -       c1.apply();
    -       c2.apply();
    -    }
    -}
    -control Simple();
    -package Top(Simple s);
    -Top(Caller()) main;
    -

    The compile-time evaluation of this program produces the structure in -Figure 15. Notice that there are two instances of the table t. +the instance of MyC is c, and the local name of the extern +is e2, not e1.

    +
    +
    +
    +
    extern E { /* body omitted */ }
    +control ArchC(E e1);
    +package Arch(ArchC c);
    +
    +control MyC(E e2)() { /* body omitted */ }
    +Arch(MyC()) main;
    +
    +
    +
    +

    Note that in this example, the architecture will supply an instance of +the extern when it applies the instance of MyC passed to the Arch +package. The fully-qualified name of that instance is main.c.e2.

    +
    +
    +

    Next, consider a larger example that demonstrates name generation when +there are multiple instances.

    +
    +
    +
    +
    control Callee() {
    +    table t { /* body omitted */ }
    +    apply { t.apply(); }
    +}
    +control Caller() {
    +    Callee() c1;
    +    Callee() c2;
    +    apply {
    +       c1.apply();
    +       c2.apply();
    +    }
    +}
    +control Simple();
    +package Top(Simple s);
    +Top(Caller()) main;
    +
    +
    +
    +

    The compile-time evaluation of this program produces the structure in +Figure 15. Notice that there are two instances of the table t. These instances must both be exposed to the control plane. To name an object in this hierarchy, one uses a path composed of the names of -containing instances. In this case, the two tables have names s.c1.t -and s.c2.t, where s is the name of the argument to the +containing instances. In this case, the two tables have names s.c1.t +and s.c2.t, where s is the name of the argument to the package instantiation, which is derived from the name of its -corresponding formal parameter. -

    -
    -

    evalmultiple -

    -
    - -
    Figure 15. Evaluating a program that has several instantiations of the same component.

    18.3.2. Annotations controlling naming

    -

    Control plane-related annotations (Section -20.3.3) can alter the names exposed to -the control plane in the following ways. -

    -
      -
    • -

      The @hidden annotation hides a controllable entity from the +corresponding formal parameter.

      +
    +
    +
    +evalmultiple +
    +
    Figure 15. Evaluating a program that has several instantiations of the same component.
    +
    +
    +
    +
    +

    18.3.2. Annotations controlling naming

    +
    +

    Control plane-related annotations (Section 20.3.3) can alter the names exposed to +the control plane in the following ways.

    +
    +
    +
      +
    • +

      The @hidden annotation hides a controllable entity from the control plane. This is the only case in which a controllable entity -is not required to have a unique, fully-qualified name. -

    • -
    • -

      The @name annotation may be used to change the local name of a -controllable entity. -

    - -

    Programs that yield the same fully-qualified name for two different -controllable entities are invalid. -

    18.3.3. Recommendations

    -

    The control plane may refer to a controllable entity by a postfix of +is not required to have a unique, fully-qualified name.

    + +
  • +

    The @name annotation may be used to change the local name of a +controllable entity.

    +
  • + +
    +
    +

    Programs that yield the same fully-qualified name for two different +controllable entities are invalid.

    +
    +
    +
    +

    18.3.3. Recommendations

    +
    +

    The control plane may refer to a controllable entity by a postfix of its fully qualified name when it is unambiguous in the context in -which it is used. Consider the following example. -

    -
    -
    -
    control c( /* parameters omitted */ )() {
    -    action a ( /* parameters omitted */ ) { /* body omitted */ }
    -    table t {
    -        keys = { /* body omitted */ }
    -        actions = { a; } }
    -}
    -c() c_inst;
    -

    Control plane software may refer to action c_inst.a as a -when inserting rules into table c_inst.t, because it is clear -from the definition of the table which action a refers to. -

    -

    Not all unambiguous postfix shortcuts are recommended. For instance, consider the -first example in Section 18.3. One might be tempted to -refer to s.c1 simply as c1, as no other instance named c1 +which it is used. Consider the following example.

    +
    +
    +
    +
    control c( /* parameters omitted */ )() {
    +    action a ( /* parameters omitted */ ) { /* body omitted */ }
    +    table t {
    +        keys = { /* body omitted */ }
    +        actions = { a; } }
    +}
    +c() c_inst;
    +
    +
    +
    +

    Control plane software may refer to action c_inst.a as a +when inserting rules into table c_inst.t, because it is clear +from the definition of the table which action a refers to.

    +
    +
    +

    Not all unambiguous postfix shortcuts are recommended. For instance, consider the +first example in Section 18.3. One might be tempted to +refer to s.c1 simply as c1, as no other instance named c1 appears in the program. However, this leads to a brittle -program since future modifications can never introduce an instance named c1, -or include libraries of P4 code that contain instances with that name. -

    18.4. Dynamic evaluation

    -

    The dynamic evaluation of a P4 program is orchestrated by the architecture +program since future modifications can never introduce an instance named c1, +or include libraries of P4 code that contain instances with that name.

    +
    +
    +
    +
    +

    18.4. Dynamic evaluation

    +
    +

    The dynamic evaluation of a P4 program is orchestrated by the architecture model. Each architecture model needs to specify the order and the conditions under which the various P4 component programs are dynamically -executed. For example, in the Simple Switch example from Section -5.1 the execution flow goes Parser->Pipe->Deparser. -

    -

    Once a P4 execution block is invoked its execution proceeds until -termination according to the semantics defined in this document. -

    18.4.1. Concurrency model

    -

    A typical packet processing system needs to execute multiple -simultaneous logical “threads.” At the very least there is a thread +executed. For example, in the Simple Switch example from Section 5.1 the execution flow goes Parser→Pipe→Deparser.

    +
    +
    +

    Once a P4 execution block is invoked its execution proceeds until +termination according to the semantics defined in this document.

    +
    +
    +

    18.4.1. Concurrency model

    +
    +

    A typical packet processing system needs to execute multiple +simultaneous logical "threads." At the very least there is a thread executing the control plane, which can modify the contents of the tables. Architecture specifications should describe in detail the interactions between the control-plane and the data-plane. The data -plane can exchange information with the control plane through extern +plane can exchange information with the control plane through extern function and method calls. Moreover, high-throughput packet-processing systems may be processing multiple packets simultaneously, e.g., in a pipelined fashion, or concurrently parsing a first packet while performing match-action operations on a second packet. This section specifies the semantics of P4 programs with respect to such concurrent -executions. -

    -

    Each top-level parser or control block is executed as a +executions.

    +
    +
    +

    Each top-level parser or control block is executed as a separate thread when invoked by the architecture. All the parameters -of the block and all local variables are thread-locali.e., each -thread has a private copy of these resources. This applies to the packet_in -and packet_out parameters of parsers and deparsers. -

    -

    As long as a P4 block uses only thread-local storage (e.g., metadata, +of the block and all local variables are thread-local---i.e., each +thread has a private copy of these resources. This applies to the packet_in +and packet_out parameters of parsers and deparsers.

    +
    +
    +

    As long as a P4 block uses only thread-local storage (e.g., metadata, packet headers, local variables), its behavior in the presence of concurrency is identical with the behavior in isolation, since any interleaving of statements from different threads must produce the -same output. -

    -

    In contrast, extern blocks instantiated by a P4 program are -global, shared across all threads. If extern blocks mediate -access to state (e.g., counters, registers)i.e., the methods of -the extern block read and write state, these stateful operations +same output.

    +
    +
    +

    In contrast, extern blocks instantiated by a P4 program are +global, shared across all threads. If extern blocks mediate +access to state (e.g., counters, registers)---i.e., the methods of +the extern block read and write state, these stateful operations are subject to data races. P4 mandates that -execution of a method call on an extern instance is atomic. -

    -

    To allow users to express atomic execution of larger code blocks, P4 -provides an @atomic annotation, which can be applied to block -statements, parser states, control blocks, or whole parsers. -

    -

    Consider the following example: -

    -
    -
    -
    extern Register { /* body omitted */ }
    -control Ingress() {
    -  Register() r;
    -  table flowlet { /* read state of r in an action */ }
    -  table new_flowlet { /* write state of r in an action */ }
    -  apply {
    -    @atomic {
    -       flowlet.apply();
    -       if (ingress_metadata.flow_ipg > FLOWLET_INACTIVE_TIMEOUT)
    -          new_flowlet.apply();
    -}}}
    -

    This program accesses an extern object r of type Register -in actions invoked from tables flowlet (reading) and new_flowlet -(writing). Without the @atomic annotation these two operations -would not execute atomically: a second packet may read the state of r -before the first packet had a chance to update it. -

    -

    Note that even within an action definition, if the action does +execution of a method call on an extern instance is atomic.

    +
    +
    +

    To allow users to express atomic execution of larger code blocks, P4 +provides an @atomic annotation, which can be applied to block +statements, parser states, control blocks, or whole parsers.

    +
    +
    +

    Consider the following example:

    +
    +
    +
    +
    extern Register { /* body omitted */ }
    +control Ingress() {
    +  Register() r;
    +  table flowlet { /* read state of r in an action */ }
    +  table new_flowlet { /* write state of r in an action */ }
    +  apply {
    +    @atomic {
    +       flowlet.apply();
    +       if (ingress_metadata.flow_ipg > FLOWLET_INACTIVE_TIMEOUT)
    +          new_flowlet.apply();
    +}}}
    +
    +
    +
    +

    This program accesses an extern object r of type Register +in actions invoked from tables flowlet (reading) and new_flowlet +(writing). Without the @atomic annotation these two operations +would not execute atomically: a second packet may read the state of r +before the first packet had a chance to update it.

    +
    +
    +

    Note that even within an action definition, if the action does something like reading a register, modifying it, and writing it back, in a way that only the modified value should be visible to the next packet, then, to guarantee correct execution in all cases, that portion of the action definition should be enclosed within a block -annotated with @atomic. -

    -

    A compiler backend must reject a program containing @atomic +annotated with @atomic.

    +
    +
    +

    A compiler backend must reject a program containing @atomic blocks if it cannot implement the atomic execution of the instruction sequence. In such cases, the compiler should provide reasonable -diagnostics. -

    19. Static assertions

    -

    The P4 core library contains two overloaded declarations for a -static_assert function, as follows: -

    -
    -
    -
    /// Static assert evaluates a boolean expression
    -/// at compilation time.  If the expression evaluates to
    -/// false, compilation is stopped and the corresponding message is printed.
    -extern bool static_assert(bool check, string message);
    -
    -/// Like the above but using a default message.
    -extern bool static_assert(bool check);
    -

    These functions both return boolean values. Since the parameters are +diagnostics.

    +
    +
    +
    +
    +
    +
    +

    19. Static assertions

    +
    +
    +

    The P4 core library contains two overloaded declarations for a +static_assert function, as follows:

    +
    +
    +
    +
    /// Static assert evaluates a boolean expression
    +/// at compilation time.  If the expression evaluates to
    +/// false, compilation is stopped and the corresponding message is printed.
    +extern bool static_assert(bool check, string message);
    +
    +/// Like the above but using a default message.
    +extern bool static_assert(bool check);
    +
    +
    +
    +

    These functions both return boolean values. Since the parameters are directionless, these functions require compile-time known values as arguments, thus they can be used to enforce compile-time invariants. Since P4 does not allow statements at the program top-level (outside of apply blocks), these functions can be used at the top-level by -assigning their result to a dummy constant, e.g.: -

    -
    -
    -
    const bool _check = static_assert(V1MODEL_VERSION > 20180000,
    -                                  "Expected a v1 model version >= 20180000");
    -

    As the comment indicates, if static_assert returns false, it +assigning their result to a dummy constant, e.g.:

    +
    +
    +
    +
    const bool _check = static_assert(V1MODEL_VERSION > 20180000,
    +                                  "Expected a v1 model version >= 20180000");
    +
    +
    +
    +

    As the comment indicates, if static_assert returns false, it causes the program compilation to be terminated immediately with an -error. -

    20. Annotations

    -

    Annotations are a simple mechanism for extending the P4 language to +error.

    +
    +
    +
    +
    +

    20. Annotations

    +
    +
    +

    Annotations are a simple mechanism for extending the P4 language to some limited degree without changing the grammar. Annotations are -attached to types, fields, variables, etc. using the @ syntax +attached to types, fields, variables, etc. using the @ syntax (as shown explicitly in the P4 grammar). Unstructured annotations, -or just “annotations,” have an optional body; structured annotations +or just "annotations," have an optional body; structured annotations have a mandatory body, containing at least a pair of square brackets -[]. -

    -
    -
    -
    optAnnotations
    -    : /* empty */
    +[].

    +
    +
    +
    +
    optAnnotations
    +    : /* empty */
         | annotations
         ;
     
    @@ -9139,1007 +12635,1871 @@ 

    "@" name - | "@" name "(" annotationBody ")" - | "@" name "[" structuredAnnotationBody "]" - ;

    -

    Structured annotations and unstructured annotations on any one element must not -use the same name. Thus, a given name can only be applied to one type of + : "@" name + | "@" name "(" annotationBody ")" + | "@" name "[" structuredAnnotationBody "]" + ; +

    +
    +
    +

    Structured annotations and unstructured annotations on any one element must not +use the same name. Thus, a given name can only be applied to one type of annotation or the other for any one element. An annotation used on one element -does not affect the annotation on another because they have different scope. -

    -

    This is legal: -

    -
    -
    -
    @my_anno(1) table T { /* body omitted */ }
    -@my_anno[2] table U { /* body omitted */ } // OK - different scope than previous
    -                                           // use of my_anno
    -

    This is illegal: -

    -
    -
    -
    @my_anno(1)
    -@my_anno[2] table U { /* body omitted */ } // Error - changed type of anno
    -                                           // on an element
    -

    Multiple unstructured annotations using the same name can appear on a given +does not affect the annotation on another because they have different scope.

    +
    +
    +

    This is legal:

    +
    +
    +
    +
    @my_anno(1) table T { /* body omitted */ }
    +@my_anno[2] table U { /* body omitted */ } // OK - different scope than previous
    +                                           // use of my_anno
    +
    +
    +
    +

    This is illegal:

    +
    +
    +
    +
    @my_anno(1)
    +@my_anno[2] table U { /* body omitted */ } // Error - changed type of anno
    +                                           // on an element
    +
    +
    +
    +

    Multiple unstructured annotations using the same name can appear on a given element; they are cumulative. Each one will be bound to that element. In -contrast, only one structured annotation using a given name may appear on an -element; multiple uses of the same name will produce an error. -

    -

    This is legal: -

    -
    -
    -
    @my_anno(1)
    -@my_anno(2) table U { /* body omitted */ }  // OK - unstructured annos accumulate
    -

    This is illegal: -

    -
    -
    -
    @my_anno[1]
    -@my_anno[2] table U { /* body omitted */ } // Error - reused the same structured
    -                                           // anno on an element

    20.1. Bodies of Unstructured Annotations

    -

    The flexibility of P4 unstructured annotations comes from the minimal structure +contrast, only one structured annotation using a given name may appear on an +element; multiple uses of the same name will produce an error.

    +
    +
    +

    This is legal:

    +
    +
    +
    +
    @my_anno(1)
    +@my_anno(2) table U { /* body omitted */ }  // OK - unstructured annos accumulate
    +
    +
    +
    +

    This is illegal:

    +
    +
    +
    +
    @my_anno[1]
    +@my_anno[2] table U { /* body omitted */ } // Error - reused the same structured
    +                                           // anno on an element
    +
    +
    +
    +

    20.1. Bodies of Unstructured Annotations

    +
    +

    The flexibility of P4 unstructured annotations comes from the minimal structure mandated by the P4 grammar: unstructured annotation bodies may contain any sequence of terminals, so long as parentheses are balanced. In the following -grammar fragment, the annotationToken non-terminal represents any terminal +grammar fragment, the annotationToken non-terminal represents any terminal produced by the lexer, including keywords, identifiers, string and integer -literals, and symbols, but excluding parentheses. -

    -
    -
    -
    annotationBody
    -    : /* empty */
    -    | annotationBody "(" annotationBody ")"
    +literals, and symbols, but excluding parentheses.

    +
    +
    +
    +
    annotationBody
    +    : /* empty */
    +    | annotationBody "(" annotationBody ")"
         | annotationBody annotationToken
    -    ;
    -

    Unstructured annotations may impose additional structure on their + ; +

    +
    +
    +

    Unstructured annotations may impose additional structure on their bodies, and are not confined to the P4 language. For example, the -P4Runtime specification4 defines a @pkginfo annotation -that expects key-value pairs. -

    20.2. Bodies of Structured Annotations

    -

    Unlike unstructured annotations, structured annotations use square brackets -[...] and have a restricted format. They are commonly used to declare custom +P4Runtime specification [3] defines a @pkginfo annotation +that expects key-value pairs.

    +
    +
    +
    +

    20.2. Bodies of Structured Annotations

    +
    +

    Unlike unstructured annotations, structured annotations use square brackets +[…​] and have a restricted format. They are commonly used to declare custom metadata, consisting of expression lists or key-value lists but not both. An -expressionList may be empty or contain a comma-separated list of member -expressions. A kvList consists of one or more kvPairs, each consisting of -a key and a value expression. Note the syntax for expression is rich, see -Appendix G for details. -

    -

    All expressions within a structuredAnnotationBody must be compile-time known -values with a result type that is either: string, int, or bool. -In particular, structured expressions (e.g. an expression containing an -expressionList, a kvList, etc.) are not allowed. Note that P4Runtime +expressionList may be empty or contain a comma-separated list of member +expressions. A kvList consists of one or more kvPairs, each consisting of +a key and a value expression. Note the syntax for expression is rich, see +Appendix G for details.

    +
    +
    +

    All expression`s within a `structuredAnnotationBody must be compile-time known +values with a result type that is either: string, int, or bool. +In particular, structured expression`s (e.g. an `expression containing an +expressionList, a kvList, etc.) are not allowed. Note that P4Runtime information (P4Info) may stipulate additional restrictions. For example, an -integer expression might be limited to 64-bit values. -

    -

    It is illegal to duplicate a key within the kvList of a structured -annotation. -

    -
    -
    -
    structuredAnnotationBody
    +integer expression might be limited to 64-bit values.

    +
    +
    +

    It is illegal to duplicate a key within the kvList of a structured +annotation.

    +
    +
    +
    +
    structuredAnnotationBody
         : expressionList optTrailingComma
         | kvList optTrailingComma
         ;
     ...
     expressionList
    -    : /* empty */
    +    : /* empty */
         | expression
    -    | expressionList "," expression
    +    | expressionList "," expression
         ;
     ...
     kvList
         : kvPair
    -    | kvList "," kvPair
    +    | kvList "," kvPair
         ;
     
     kvPair
    -    : name "=" expression
    -    ;

    20.2.1. Structured Annotation Examples

    -

    Empty Expression List -

    -

    The following example produces an empty annotation: -

    -
    -
    -
    @Empty[]
    -table t {
    -    /* body omitted */
    -}
    -

    Mixed Expression List -

    -

    The following example will produce an effective expression list as follows: -

    -
    [1,"hello",true, false, 11]
    -
    -
    -
    #define TEXT_CONST "hello"
    -#define NUM_CONST 6
    -@MixedExprList[1,TEXT_CONST,true,1==2,5+NUM_CONST]
    -table t {
    -    /* body omitted */
    -}
    -

    kvList of Strings -

    -
    -
    -
    @Labels[short="Short Label", hover="My Longer Table Label to appear in hover-help"]
    -table t {
    -    /* body omitted */
    -}
    -

    kvList of Mixed Expressions -

    -

    The following example will produce an effective kvList as follows. -

    -
    [label="text", my_bool=true, int_val=6]
    -
    -
    -
    @MixedKV[label="text", my_bool=true, int_val=2*3]
    -table t {
    -    /* body omitted */
    -}
    -

    Illegal Mixing of kvPair and expressionList -

    -

    The following example is invalid because the body contains both a kvPair and -an expression: -

    -
    -
    -
    @IllegalMixing[key=4, 5] // illegal mixing
    -table t {
    -    /* body omitted */
    -}
    -

    Illegal Duplicate Key -

    -

    The following example is invalid because the same key occurs more than once: -

    -
    -
    -
    @DupKey[k1=4,k1=5] // illegal duplicate key
    -table t {
    -    /* body omitted */
    -}
    -

    Illegal Duplicate Structured Annotation -

    -

    The following example is invalid because the annotation name occurs more -than once on the same element, e.g. table t: -

    -
    -
    -
    @DupAnno[k1=4]
    -@DupAnno[k2=5] // illegal duplicate name
    -table t {
    -    /* body omitted */
    -}
    -

    Illegal Simultaneous Use of Both Structured and Unstructured Annotation -

    -

    The following example is invalid because the annotation name is used by both -an unstructured and structured annotation on the same element table t: -

    -
    -
    -
    @MixAnno("Anything")
    -@MixAnno[k2=5] // illegal use in both annotation types
    -table t {
    -    /* body omitted */
    -}

    20.3. Predefined annotations

    -

    Annotation names that start with lowercase letters are reserved for + : name "=" expression + ; +

    +
    +
    +

    20.2.1. Structured Annotation Examples

    +
    +

    Empty Expression List

    +
    +
    +

    The following example produces an empty annotation:

    +
    +
    +
    +
    @Empty[]
    +table t {
    +    /* body omitted */
    +}
    +
    +
    +
    +

    Mixed Expression List

    +
    +
    +

    The following example will produce an effective expression list as follows:

    +
    +
    +
    +
    [1,"hello",true, false, 11]
    +
    +
    +
    +
    +
    #define TEXT_CONST "hello"
    +#define NUM_CONST 6
    +@MixedExprList[1,TEXT_CONST,true,1==2,5+NUM_CONST]
    +table t {
    +    /* body omitted */
    +}
    +
    +
    +
    +

    kvList of Strings

    +
    +
    +
    +
    @Labels[short="Short Label", hover="My Longer Table Label to appear in hover-help"]
    +table t {
    +    /* body omitted */
    +}
    +
    +
    +
    +

    kvList of Mixed Expressions

    +
    +
    +

    The following example will produce an effective kvList as follows.

    +
    +
    +
    +
    [label="text", my_bool=true, int_val=6]
    +
    +
    +
    +
    +
    @MixedKV[label="text", my_bool=true, int_val=2*3]
    +table t {
    +    /* body omitted */
    +}
    +
    +
    +
    +

    Illegal Mixing of kvPair and expressionList

    +
    +
    +

    The following example is invalid because the body contains both a kvPair and +an expression:

    +
    +
    +
    +
    @IllegalMixing[key=4, 5] // illegal mixing
    +table t {
    +    /* body omitted */
    +}
    +
    +
    +
    +

    Illegal Duplicate Key

    +
    +
    +

    The following example is invalid because the same key occurs more than once:

    +
    +
    +
    +
    @DupKey[k1=4,k1=5] // illegal duplicate key
    +table t {
    +    /* body omitted */
    +}
    +
    +
    +
    +

    Illegal Duplicate Structured Annotation

    +
    +
    +

    The following example is invalid because the annotation name occurs more +than once on the same element, e.g. table t:

    +
    +
    +
    +
    @DupAnno[k1=4]
    +@DupAnno[k2=5] // illegal duplicate name
    +table t {
    +    /* body omitted */
    +}
    +
    +
    +
    +

    Illegal Simultaneous Use of Both Structured and Unstructured Annotation

    +
    +
    +

    The following example is invalid because the annotation name is used by both +an unstructured and structured annotation on the same element table t:

    +
    +
    +
    +
    @MixAnno("Anything")
    +@MixAnno[k2=5] // illegal use in both annotation types
    +table t {
    +    /* body omitted */
    +}
    +
    +
    +
    +
    +
    +

    20.3. Predefined annotations

    +
    +

    Annotation names that start with lowercase letters are reserved for the standard library and architecture. This document pre-defines a -set of “standard” annotations in Appendix C. +set of "standard" annotations in Appendix C. We expect that this list will grow. We encourage custom architectures to define annotations starting with a manufacturer prefix: e.g., an organization named X would use -annotations named like @X_annotation -

    20.3.1. Optional parameter annotations

    -

    A parameter to a package, parser type, control type, extern method, extern function or extern -object constructor can be annotated with @optional to indicate that +annotations named like @X_annotation

    +
    +
    +

    20.3.1. Optional parameter annotations

    +
    +

    A parameter to a package, parser type, control type, extern method, extern function or extern +object constructor can be annotated with @optional to indicate that the user does not need to provide a corresponding argument for that parameter. The meaning of a parameter with no supplied value is -target-dependent. -

    20.3.2. Annotations on the table action list

    -

    The following two annotations can be used to give additional +target-dependent.

    +
    +
    +
    +

    20.3.2. Annotations on the table action list

    +
    +

    The following two annotations can be used to give additional information to the compiler and control-plane about actions in a -table. These annotations have no bodies. -

    -
      -
    • @tableonly: actions with this annotation can only appear -within the table, and never as default action. -
    • -
    • @defaultonly: actions with this annotation can only appear in -the default action, and never in the table. -
    - -
    -
    -
    table t {
    -    actions = {
    -       a,              // can appear anywhere
    -       @tableonly b,   // can only appear in the table
    -       @defaultonly c, // can only appear in the default action
    -    }
    -    /* body omitted */
    -}

    20.3.3. Control-plane API annotations

    -

    The @name annotation directs the compiler to use a different +table. These annotations have no bodies.

    +
    +
    +
      +
    • +

      @tableonly: actions with this annotation can only appear +within the table, and never as default action.

      +
    • +
    • +

      @defaultonly: actions with this annotation can only appear in +the default action, and never in the table.

      +
    • +
    +
    +
    +
    +
    table t {
    +    actions = {
    +       a,              // can appear anywhere
    +       @tableonly b,   // can only appear in the table
    +       @defaultonly c, // can only appear in the default action
    +    }
    +    /* body omitted */
    +}
    +
    +
    +
    +
    +

    20.3.3. Control-plane API annotations

    +
    +

    The @name annotation directs the compiler to use a different local name when generating the external APIs used to manipulate a language element from the control plane. This annotation takes a string literal body. In the -following example, the fully-qualified name of the table is c_inst.t1. -

    -
    -
    -
    control c( /* parameters omitted */ )() {
    -    @name("t1") table t { /* body omitted */ }
    -    apply { /* body omitted */ }
    -}
    -c() c_inst;
    -

    The @hidden annotation hides a controllable entity, e.g. a table, +following example, the fully-qualified name of the table is c_inst.t1.

    +
    +
    +
    +
    control c( /* parameters omitted */ )() {
    +    @name("t1") table t { /* body omitted */ }
    +    apply { /* body omitted */ }
    +}
    +c() c_inst;
    +
    +
    +
    +

    The @hidden annotation hides a controllable entity, e.g. a table, key, action, or extern, from the control plane. This effectively -removes its fully-qualified name (Section 18.3). This annotation -does not have a body. -

    20.3.3.1. Restrictions
    -

    Each element may be annotated with at most one @name -or @hidden annotation, and each control plane name must refer to +removes its fully-qualified name (Section 18.3). This annotation +does not have a body.

    +
    +
    +
    20.3.3.1. Restrictions
    +
    +

    Each element may be annotated with at most one @name +or @hidden annotation, and each control plane name must refer to at most one controllable entity. This is of special concern when -using an absolute @name annotation: if a type containing a @name +using an absolute @name annotation: if a type containing a @name annotation with an absolute pathname (i.e., one starting with a dot) is instantiated more than once, it will result in the same -name referring to two controllable entities. -

    -
    -
    -
    control noargs();
    -package top(noargs c1, noargs c2);
    -
    -control c() {
    -    @name(".foo.bar") table t { /* body omitted */ }
    -    apply { /* body omitted */ }
    -}
    -top(c(), c()) main;
    -

    Without the @name annotation, this program would produce -two controllable entities with fully-qualified names main.c1.t and main.c2.t. -However, the @name(".foo.bar") annotation renames table t -in both instances to foo.bar, resulting in one name that refers -to two controllable entities, which is illegal. -

    20.3.4. Concurrency control annotations

    -

    The @atomic annotation, described in Section 18.4.1 -can be used to enforce the atomic execution of a code block. -

    20.3.5. Value set annotations

    -

    The @match annotation, described in Section 13.6, is used -to specify a match_kind value other than the default match_kind of -exact for a field of a value_set. -

    20.3.6. Extern function/method annotations

    -

    Various annotations may appear on extern function and method +name referring to two controllable entities.

    +
    +
    +
    +
    control noargs();
    +package top(noargs c1, noargs c2);
    +
    +control c() {
    +    @name(".foo.bar") table t { /* body omitted */ }
    +    apply { /* body omitted */ }
    +}
    +top(c(), c()) main;
    +
    +
    +
    +

    Without the @name annotation, this program would produce +two controllable entities with fully-qualified names main.c1.t and main.c2.t. +However, the @name(".foo.bar") annotation renames table t +in both instances to foo.bar, resulting in one name that refers +to two controllable entities, which is illegal.

    +
    +
    +
    +
    +

    20.3.4. Concurrency control annotations

    +
    +

    The @atomic annotation, described in Section 18.4.1 +can be used to enforce the atomic execution of a code block.

    +
    +
    +
    +

    20.3.5. Value set annotations

    +
    +

    The @match annotation, described in Section 13.6, is used +to specify a match_kind value other than the default match_kind of +exact for a field of a value_set.

    +
    +
    +
    +

    20.3.6. Extern function/method annotations

    +
    +

    Various annotations may appear on extern function and method declarations to describe limitations on the behavior and interactions of those functions. By default extern functions might have any effect on the environment of the P4 program and might interact in non-trivial -ways (subject to a few limitations see section -6.8.1). Since externs are +ways (subject to a few limitations — see Section 6.8). Since externs are architecture-specific and their behavior is known to the architecture definition, these annotations are not strictly necessary (an implementation can have knowledge of how externs interact based on their names built into it), but these annotations provide a uniform way of describing certain well-defined interactions (or their -absence), allowing architecture-independent analysis of P4 programs. -

    -
      -
    • -

      @pure - Describes a function that depends solely on its in +absence), allowing architecture-independent analysis of P4 programs.

      +
    +
    +
      +
    • +

      @pure - Describes a function that depends solely on its in parameter values, and has no effect other than returning a value, -and copy-out behavior on its out and inout parameters. No +and copy-out behavior on its out and inout parameters. No hidden state is recorded between calls, and its value does not depend on any hidden state that may be changed by other calls. An -example is a hash function that computes a deterministic hash of +example is a hash function that computes a deterministic hash of its arguments, and its return value does not depend upon any control-plane writable seed or initialization vector value. A -@pure function whose results are unused may be safely eliminated +@pure function whose results are unused may be safely eliminated with no adverse effects, and multiple calls with identical arguments may be combined into a single call (subject to the limits imposed by -copy-out behavior of out and inout parameters). @pure +copy-out behavior of out and inout parameters). @pure functions may also be reordered with respect to other computations -that are not data dependent. -

    • -
    • -

      @noSideEffects - Weaker than @pure and describes a function that +that are not data dependent.

      +
    • +
    • +

      @noSideEffects - Weaker than @pure and describes a function that does not change any hidden state, but may depend on hidden state. -One example is a hash function that computes a deterministic hash +One example is a hash function that computes a deterministic hash of its arguments, plus some internal state that can be modified via control plane API calls such as a seed or initialization vector. Another example is a read of one element of a register array extern object. Such a function may be dead code eliminated, and may be -reordered or combined with other @noSideEffects or @pure calls -(subject to the limits imposed by copy-out behavior of out and -inout parameters), but not with other function calls that may have -side effects that affect the function. -

    -

    20.3.7. Deprecated annotation

    -

    The deprecated annotation has a required string argument that is a +reordered or combined with other @noSideEffects or @pure calls +(subject to the limits imposed by copy-out behavior of out and +inout parameters), but not with other function calls that may have +side effects that affect the function.

    + + +
    +
    +
    +

    20.3.7. Deprecated annotation

    +
    +

    The deprecated annotation has a required string argument that is a message that will be printed by a compiler when a program is using the deprecated construct. This is mostly useful for annotating library -constructs, such as externs. -

    -
    -
    -
    @deprecated("Please use the 'check' function instead")
    -extern Checker {
    -   /* body omitted */
    -}

    20.3.8. No warnings annotation

    -

    The noWarn annotation has a required string argument that indicates +constructs, such as externs.

    +
    +
    +
    +
    @deprecated("Please use the 'check' function instead")
    +extern Checker {
    +   /* body omitted */
    +}
    +
    +
    +
    +
    +

    20.3.8. No warnings annotation

    +
    +

    The noWarn annotation has a required string argument that indicates a compiler warning that will be inhibited. For example -@noWarn("unused") on a declaration will prevent a compiler warning -if that declaration is not used. -

    20.4. Target-specific annotations

    -

    Each P4 compiler implementation can define additional annotations +@noWarn("unused") on a declaration will prevent a compiler warning +if that declaration is not used.

    +
    +
    +
    +
    +

    20.4. Target-specific annotations

    +
    +

    Each P4 compiler implementation can define additional annotations specific to the target of the compiler. The syntax of the annotations should conform to the above description. The semantics of such annotations is target-specific. They could be used in a similar way to -pragmas in other languages. -

    -

    The P4 compiler should provide: -

    -
      -
    • Errors when annotations are used incorrectly (e.g., an annotation +pragmas in other languages.

      +
    +
    +

    The P4 compiler should provide:

    +
    +
    +
      +
    • +

      Errors when annotations are used incorrectly (e.g., an annotation expecting a parameter but used without arguments, or with arguments -of the wrong type) -

    • -
    • Warnings for unknown annotations. -
    -

    A. Appendix: Revision History

    A.1. Summary of changes made in unreleased version

    A.2. Summary of changes made in version 1.2.5, released October 11, 2024

    -
      -
    • Improved type nesting rules (Section7.2.8). -
    • -
    • Clarified that directionless extern parameters are passed by reference. +of the wrong type)

    • -
    • Introduced distinction between local compile-time known and compile-time known values (Section18.1). -
    -

    A.3. Summary of changes made in version 1.2.4,released May 15, 2023

    -
      -
    • Added header stack expressions (Section 8.18.1). +
    • +

      Warnings for unknown annotations.

    • -
    • Allow casts from a type to itself (Section 8.11). -
    • -
    • Added an invalid header or header union expression {#} (Sections 8.17 and 8.19). -
    • -
    • Added a concept of numeric values (Section 7.4). +
    +
    +
    +
    +
    +
    +

    Appendix A: Revision History

    +
    +
    +

    A.1. Summary of changes made in unreleased version

    + +
    +
    +

    A.2. Summary of changes made in version 1.2.5, released October 11, 2024

    +
    +
      +
    • +

      Improved type nesting rules (Section 7.2.8).

    • -
    • Added a section on operations on extern objects (Section 8.22). +
    • +

      Clarified that directionless extern parameters are passed by reference.

    • -
    • Added note in sections operations on types for types that support compile-time size determination. +
    • +

      Introduced distinction between local compile-time known and compile-time known values (Section 18.1).

    • -
    • Clarified that header stacks are arrays of headers or header unions. +
    +
    +
    +
    +

    A.3. Summary of changes made in version 1.2.4,released May 15, 2023

    +
    +
      +
    • +

      Added header stack expressions (Section 8.18.1).

    • -
    • Added distinctness of fields for types that have fields including error, match kind, struct, header, and header union. +
    • +

      Allow casts from a type to itself (Section 8.11).

    • -
    • Clarified types bit<W>, int<W>, and varbit<W> encompass the case where the width is a compile-time known expression evaluating to an appropriate integer (Section 7.1.6.2, Section 7.1.6.3, Section 7.1.6.4). +
    • +

      Added an invalid header or header union expression {#} (Section 8.17 and Section 8.19).

    • -
    • Clarified restrictions for parameters with default values (Section 6.8.1). +
    • +

      Added a concept of numeric values (Section 7.4).

    • -
    • Added optional trailing commas (Section 6.4.4). +
    • +

      Added a section on operations on extern objects (Section 8.22).

    • -
    • Clarified the scope of parser namespaces (Section 13.2). +
    • +

      Added note in sections operations on types for types that support compile-time size determination.

    • -
    • Specified that algorithm for generating control-plane names for keys is optional (Section 18.3.1.3). +
    • +

      Clarified that header stacks are arrays of headers or header unions.

    • -
    • Clarified types of expressions that may appear in select (Section 13.6). +
    • +

      Added distinctness of fields for types that have fields including error, match kind, struct, header, and header union.

    • -
    • Added description of semantics of the core.p4 match kinds (Section 14.2.1.1). +
    • +

      Clarified types bit<W>, int<W>, and varbit<W> encompass the case where the width is a compile-time known expression evaluating to an appropriate integer (Section 7.1.6.2, Section 7.1.6.3, Section 7.1.6.4).

    • -
    • Explicitly disallow overloading of parsers, controls, and packages (Section 7.2.10.2). +
    • +

      Clarified restrictions for parameters with default values (Section 6.8).

    • -
    • Clarified implicit casts present in select expressions (Section 13.6). +
    • +

      Added optional trailing commas (Section 6.4.4).

    • -
    • Clarified that slices can be applied to arbitrary-precision integers (Section 8.8). +
    • +

      Clarified the scope of parser namespaces (Section 13.2).

    • -
    • Clarified that direct invocation is not possible for objects that have constructor arguments (Section 15.1). +
    • +

      Specified that algorithm for generating control-plane names for keys is optional (Section 18.3.1.3).

    • -
    • Added comparison for tuples as a legal operation (Section 8.12). +
    • +

      Clarified types of expressions that may appear in select (Section 13.6).

    • -
    • Clarified the behavior of lookahead on header-typed values (Section 13.8.3). +
    • +

      Added description of semantics of the core.p4 match kinds (Section 14.2.1.1).

    • -
    • Added static_assert function (Section 19). +
    • +

      Explicitly disallow overloading of parsers, controls, and packages (Section 7.2.10.2).

    • -
    • Clarified semantics of ranges where the start is bigger than the end (Section 8.15.4). +
    • +

      Clarified implicit casts present in select expressions (Section 13.6).

    • -
    • Allow ranges to be specified by serializable enums (Section 8.15.4). +
    • +

      Clarified that slices can be applied to arbitrary-precision integers (Section 8.8).

    • -
    • Specified type produced by the *sizeInB* methods (Section 9). +
    • +

      Clarified that direct invocation is not possible for objects that have constructor arguments (Section 15.1).

    • -
    • Added section with operations on match_kind values (Section 8.4). +
    • +

      Added comparison for tuples as a legal operation (Section 8.12).

    • -
    • Renamed infinite-precision integers to arbitrary-precision integers (Section 7.1.6.5). +
    • +

      Clarified the behavior of lookahead on header-typed values (Section 13.8.3).

    • -
    • compiler-inserted default_action is not const (Section 14.2). +
    • +

      Added static_assert function (Chapter 19).

    • -
    • Clarified the restrictions on run time for tables with const entries (Section 14.2.1.4). +
    • +

      Clarified semantics of ranges where the start is bigger than the end (Section 8.15.4).

    • -
    • renamed list expressions to tuple expressions +
    • +

      Allow ranges to be specified by serializable enums (Section 8.15.4).

    • -
    • Added list type (Section 7.2.7). +
    • +

      Specified type produced by the *sizeInB* methods (Chapter 9).

    • -
    • Defined entries table property without const, for entries -installed when the P4 program is loaded, but the control plane can -later change them or add to them (Section 14.2.1.4). +
    • +

      Added section with operations on match_kind values (Section 8.4).

    • -
    • Clarified behavior of table with no key property, or if its list -of keys is empty (Section 14.2.1.1). -
    -

    A.4. Summary of changes made in version 1.2.3, released July 11, 2022.

    -
      -
    • Extended minSizeInBits and minSizeInBytes to apply to more expressions (Section 9). +
    • +

      Renamed infinite-precision integers to arbitrary-precision integers (Section 7.1.6.5).

    • -
    • Added support for maxSizeInBits and maxSizeInBytes (Section 9). +
    • +

      compiler-inserted default_action is not const (Section 14.2).

    • -
    • Added support for empty lists of const entries in tables (Section 14.2.1.4). +
    • +

      Clarified the restrictions on run time for tables with const entries (Section 14.2.1.4).

    • -
    • Added support for switch statements in actions (Section 14.1). +
    • +

      renamed list expressions to tuple expressions

    • -
    • Added support for direct invocation of controls and parsers (Section 15). +
    • +

      Added list type (Section 7.2.7).

    • -
    • Added parser value_set to list of control-plane visible names (Section 18.3). +
    • +

      Defined entries table property without const, for entries +installed when the P4 program is loaded, but the control plane can +later change them or add to them (Section 14.2.1.4).

    • -
    • Added match_kind as a base type (Section 7.1.3). +
    • +

      Clarified behavior of table with no key property, or if its list +of keys is empty (Section 14.2.1.1).

    • -
    • Removed structure initializers as they are subsumed by structure-valued expressions (Section 8.13). +
    +
    +
    +
    +

    A.4. Summary of changes made in version 1.2.3, released July 11, 2022.

    +
    +
      +
    • +

      Extended minSizeInBits and minSizeInBytes to apply to more expressions (Chapter 9).

    • -
    • Specified operations on values typed as type variables (Section 8.24). +
    • +

      Added support for maxSizeInBits and maxSizeInBytes (Chapter 9).

    • -
    • Clarified semantics of compile-time known values (Section 18.1). +
    • +

      Added support for empty lists of const entries in tables (Section 14.2.1.4).

    • -
    • Clarified semantics of directionless parameters (Section 6.8). +
    • +

      Added support for switch statements in actions (Section 14.1).

    • -
    • Clarified semantics of arbitrary precision integers (Section 7.1.6.5). +
    • +

      Added support for direct invocation of controls and parsers (Chapter 15).

    • -
    • Clarified semantics of bit slices, shifts, and concatenation (Section 8.6). +
    • +

      Added parser value_set to list of control-plane visible names (Section 18.3).

    • -
    • Clarified semantics of optional parameters (Section 6.8.2). +
    • +

      Added match_kind as a base type (Section 7.1.3).

    • -
    • Clarified restrictions on extern method and function invocation (Section F). +
    • +

      Removed structure initializers as they are subsumed by structure-valued expressions (Section 8.13).

    • -
    • Clarified semantics of implicit casts (Section 8.11.2). -
    -

    A.5. Summary of changes made in version 1.2.2, released May 17, 2021

    -
      -
    • Added support for accessing tuple fields (Section 8.12). +
    • +

      Specified operations on values typed as type variables (Section 8.24).

    • -
    • Added support for generic structures (Section 7.2.11). +
    • +

      Clarified semantics of compile-time known values (Section 18.1).

    • -
    • Added support for integers, enums, and errors in switch statements (Section 12.7). +
    • +

      Clarified semantics of directionless parameters (Section 6.8).

    • -
    • Added support for additional enumeration types (Section 7.2.1). +
    • +

      Clarified semantics of arbitrary precision integers (Section 7.1.6.5).

    • -
    • Added support for abstract methods (Section 1). +
    • +

      Clarified semantics of bit slices, shifts, and concatenation (Section 8.6).

    • -
    • Added support for conditional statements and empty statements in parsers (Section 13.4). +
    • +

      Clarified semantics of optional parameters (Section 6.8.2).

    • -
    • Added support for casts from int to bool (Section 8.11). +
    • +

      Clarified restrictions on extern method and function invocation (Appendix F).

    • -
    • Added support for 0-width bitstrings and varbits (Section 8.25). +
    • +

      Clarified semantics of implicit casts (Section 8.11.2).

    • -
    • Clarified that default_action is NoAction if otherwise unspecified (Section 14.2). +
    +
    +
    +
    +

    A.5. Summary of changes made in version 1.2.2, released May 17, 2021

    +
    +
      +
    • +

      Added support for accessing tuple fields (Section 8.12).

    • -
    • Clarified the types of expressions that may be used as indexes for header stacks (Section 8.18). +
    • +

      Added support for generic structures (Section 7.2.11).

    • -
    • Clarified representation of Booleans in headers (Section 7.2.2). +
    • +

      Added support for integers, enums, and errors in switch statements (Section 12.7).

    • -
    • Clarified representation of empty types (Section 8.25). +
    • +

      Added support for additional enumeration types (Section 7.2.1).

    • -
    • Clarified that action data can be specified by the control plane, default_action table property, or const entries table property (Section 14.1). +
    • +

      Added support for abstract methods (Section 7.2.10.2.1).

    • -
    • Fixed several typos and inconsistencies in grammar (Section G). +
    • +

      Added support for conditional statements and empty statements in parsers (Section 13.4).

    • -
    • Eliminated annotations on const entries in grammar (Section G). -
    -

    A.6. Summary of changes made in version 1.2.1, released June 11, 2020

    -
      -
    • Added structure-value expressions (Section -8.13). +
    • +

      Added support for casts from int to bool (Section 8.11).

    • -
    • Added support for default values (Section 7.3). +
    • +

      Added support for 0-width bitstrings and varbits (Section 8.25).

    • -
    • Added support for concatenating signed strings (Section -8.9.1). +
    • +

      Clarified that default_action is NoAction if otherwise unspecified (Section 14.2).

    • -
    • Added key-value and list-structured annotations (Section -20). +
    • +

      Clarified the types of expressions that may be used as indexes for header stacks (Section 8.18).

    • -
    • Added @pure and @noSideEffects annotations (Section -20.3.6). +
    • +

      Clarified representation of Booleans in headers (Section 7.2.2).

    • -
    • Added @noWarn annotation (Section 20.3.8). +
    • +

      Clarified representation of empty types (Section 8.25).

    • -
    • Generalized typing for masks to allow serializable enums (Section -8.15.3). +
    • +

      Clarified that action data can be specified by the control plane, default_action table property, or const entries table property (Section 14.1).

    • -
    • Restricted the right operands of bit shifts involving -arbitrary-precision integers to be constant and positive (Section -8.8). +
    • +

      Fixed several typos and inconsistencies in grammar (Appendix G).

    • -
    • Clarified copy-out behavior for return (Section -12.4) and exit (Section 12.5) -statements. +
    • +

      Eliminated annotations on const entries in grammar (Appendix G).

    • -
    • Clarified semantics of invalid header stacks (Section -8.25). +
    +
    +
    +
    +

    A.6. Summary of changes made in version 1.2.1, released June 11, 2020

    +
    +
      +
    • +

      Added structure-value expressions (Section 8.13).

    • -
    • Clarified initialization semantics (Section 6.7 and -6.8), especially for headers and local -variables. +
    • +

      Added support for default values (Section 7.3).

    • -
    • Clarified evaluation order for table keys (Section 14.2.3). +
    • +

      Added support for concatenating signed strings (Section 8.9.1).

    • -
    • Fixed grammar to clarify parsing of right shift operator (>>), -allow empty statements in parser (Section 13.4), -and eliminate annotations on const entries (Section 14.2.1.4). -
    -

    A.7. Summary of changes made in version 1.2.0, released October 14, 2019

    -
      -
    • Added table.apply().miss (Section 14.2.2). +
    • +

      Added key-value and list-structured annotations (Chapter 20).

    • -
    • Added string type (Section 7.1.5). +
    • +

      Added @pure and @noSideEffects annotations (Section 20.3.6).

    • -
    • Added implicit casts from enum values (Section 8.3). +
    • +

      Added @noWarn annotation (Section 20.3.8).

    • -
    • Allow 1-bit signed values +
    • +

      Generalized typing for masks to allow serializable enums (Section 8.15.3).

    • -
    • Define the type of bit slices from signed and unsigned values to be unsigned. +
    • +

      Restricted the right operands of bit shifts involving +arbitrary-precision integers to be constant and positive (Section 8.8).

    • -
    • Constrain default label position for switch statements. +
    • +

      Clarified copy-out behavior for return (Section 12.4) and exit (Section 12.5) +statements.

    • -
    • Allow empty tuples. +
    • +

      Clarified semantics of invalid header stacks (Section 8.25).

    • -
    • Added @deprecated annotation. +
    • +

      Clarified initialization semantics (Section 6.7 and +Section 6.8), especially for headers and local +variables.

    • -
    • Relaxed the structure of annotation bodies. +
    • +

      Clarified evaluation order for table keys (Section 14.2.3).

    • -
    • Removed the @pkginfo annotation, which is now defined by the P4Runtime specification. +
    • +

      Fixed grammar to clarify parsing of right shift operator (>>), +allow empty statements in parser (Section 13.4), +and eliminate annotations on const entries (Section 14.2.1.4).

    • -
    • Added int type (Section 7.1.6.5). +
    +
    +
    +
    +

    A.7. Summary of changes made in version 1.2.0, released October 14, 2019

    +
    +
      +
    • +

      Added table.apply().miss (Section 14.2.2).

    • -
    • Added error ParserInvalidArgument (Sections 13.8.2, 13.8.4). +
    • +

      Added string type (Section 7.1.5).

    • -
    • Clarified the significance of order of entries in const entries (Section 14.2.1.4). +
    • +

      Added implicit casts from enum values (Section 8.3).

    • -
    • Added methods to calculate header size (Section 8.17). -
    -

    A.8. Summary of changes made in version 1.1.0, released November 26, 2017.

    -
      -
    • Top-level functions (Section 10) - -
        -
      • Functions may be declared at the top-level of a P4 program. -
    • -
    • Optional and named parameters (Section 6.8) - -
        -
      • Parameters may be specified by name, with a default value, or designated as optional. -
    • -
    • enum representations (Section 8.3) - -
        -
      • enum values to be specified with a concrete representation. -
    • -
    • Parser values sets (Section 13.11) - -
        -
      • value_set objects for control-plane programmable select labels. -
    • -
    • Type definitions (Section 7.6) - -
        -
      • New types may be introduced in programs. -
    • -
    • Saturating arithmetic (Section 8.6) - -
        -
      • Saturating arithmetic is supported on some targets. -
    • -
    • Structured annotations (Section 20) - -
        -
      • Annotations may be specified as lists of key-value pairs -
    • -
    • Globalname (Section 18.3.2) - -
        -
      • The reserved globalname annotation has been removed. -
    • -
    • Table size property (Section 14.2.1.5) - -
        -
      • Meaning of optional size property for tables has been defined. -
    • -
    • Invalid headers (Section 8.17) - -
        -
      • Clarified semantics of operations on invalid headers. -
    • -
    • Calling restrictions (Section F) - -
        -
      • Added restrictions on kinds of values that may be passed as arguments to calls. -
    • -
    • Bitwise operator precedence (Section G) - -
        -
      • Modified precedence conventions so that bitwise operators & | and ^ have higher precedence than relation operators < > <= >=. -
    • -
    • Computed bitwidths (Section 7.1) - -
        -
      • Added support for specifying widths using expressions in bit and varbit types. -
    -

    A.9. Initial version 1.0.0, released May 17, 2017

    B. Appendix: P4 reserved keywords

    -

    The following table shows all P4 reserved keywords. Some identifiers -are treated as keywords only in specific contexts (e.g., the keyword actions). -

    - - - - - - - - - - - - -
    abstract action apply bit
    bool const control default
    else enum error extern
    exit false header header_union
    if in inout int
    list match_kind package parser
    out return select state
    string struct switch table
    this transition true tuple
    type typedef value_set varbit
    verify void

    C. Appendix: P4 reserved annotations

    -

    The following table shows all P4 reserved annotations. -

    - - - - - - - - - - - - - - -
    Annotation Purpose See Section
    atomic specify atomic execution  18.4.1
    defaultonly action can only appear in the default action  20.3.2
    hidden hides a controllable entity from the control plane  18.3.2
    match specify match_kind of a field in a value_set  20.3.5
    name assign local control-plane name  18.3.2
    optional parameter is optional  20.3.1
    tableonly action cannot be a default_action  20.3.2
    deprecated Construct has been deprecated  20.3.7
    pure pure function  20.3.6
    noSideEffects function with no side effects  20.3.6
    noWarn Has a string argument; inhibits compiler warnings  20.3.8

    D. Appendix: P4 core library

    -

    The P4 core library contains declarations that are useful to most -programs. -

    -

    For example, the core library includes the declarations of the -predefined packet_in and packet_out extern objects, used -in parsers and deparsers to access packet data. -

    -
    -
    -
    /// Standard error codes.  New error codes can be declared by users.
    -error {
    -    NoError,           /// No error.
    -    PacketTooShort,    /// Not enough bits in packet for 'extract'.
    -    NoMatch,           /// 'select' expression has no matches.
    -    StackOutOfBounds,  /// Reference to invalid element of a header stack.
    -    HeaderTooShort,    /// Extracting too many bits into a varbit field.
    -    ParserTimeout,     /// Parser execution time limit exceeded.
    -    ParserInvalidArgument  /// Parser operation was called with a value
    -                           /// not supported by the implementation.
    -}
    -extern packet_in {
    -    /// Read a header from the packet into a fixed-sized header @hdr
    -    /// and advance the cursor.
    -    /// May trigger error PacketTooShort or StackOutOfBounds.
    -    /// @T must be a fixed-size header type
    -    void extract<T>(out T hdr);
    -    /// Read bits from the packet into a variable-sized header @variableSizeHeader
    -    /// and advance the cursor.
    -    /// @T must be a header containing exactly 1 varbit field.
    -    /// May trigger errors PacketTooShort, StackOutOfBounds, or HeaderTooShort.
    -    void extract<T>(out T variableSizeHeader,
    -                    in bit<32> variableFieldSizeInBits);
    -    /// Read bits from the packet without advancing the cursor.
    -    /// @returns: the bits read from the packet.
    -    /// T may be an arbitrary fixed-size type.
    -    T lookahead<T>();
    -    /// Advance the packet cursor by the specified number of bits.
    -    void advance(in bit<32> sizeInBits);
    -    /// @return packet length in bytes.  This method may be unavailable on
    -    /// some target architectures.
    -    bit<32> length();
    -}
    -extern packet_out {
    -    /// Write @data into the output packet, skipping invalid headers
    -    /// and advancing the cursor
    -    /// @T can be a header type, a header stack, a header_union, or a struct
    -    /// containing fields with such types.
    -    void emit<T>(in T data);
    -}
    -action NoAction() {}
    -/// Standard match kinds for table key fields.
    -/// Some architectures may not support all these match kinds.
    -/// Architectures can declare additional match kinds.
    -match_kind {
    -    /// Match bits exactly.
    -    exact,
    -    /// Ternary match, using a mask.
    -    ternary,
    -    /// Longest-prefix match.
    -    lpm
    -}
    -
    -/// Static assert evaluates a boolean expression
    -/// at compilation time.  If the expression evaluates to
    -/// false, compilation is stopped and the corresponding message is printed.
    -/// The function returns a boolean, so that it can be used
    -/// as a global constant value in a program, e.g.:
    -/// const version = static_assert(V1MODEL_VERSION > 20180000, "Expected a v1 model version >= 20180000");
    -extern bool static_assert(bool check, string message);
    -
    -/// Like the above but using a default message.
    -extern bool static_assert(bool check);
    -

    E. Appendix: Checksums

    -

    There are no built-in constructs in P416 for manipulating packet -checksums. We expect that checksum operations can be expressed as extern +

  • +

    Allow 1-bit signed values

    +
  • +
  • +

    Define the type of bit slices from signed and unsigned values to be unsigned.

    +
  • +
  • +

    Constrain default label position for switch statements.

    +
  • +
  • +

    Allow empty tuples.

    +
  • +
  • +

    Added @deprecated annotation.

    +
  • +
  • +

    Relaxed the structure of annotation bodies.

    +
  • +
  • +

    Removed the @pkginfo annotation, which is now defined by the P4Runtime specification.

    +
  • +
  • +

    Added int type (Section 7.1.6.5).

    +
  • +
  • +

    Added error ParserInvalidArgument (Section 13.8.2, Section 13.8.4).

    +
  • +
  • +

    Clarified the significance of order of entries in const entries (Section 14.2.1.4).

    +
  • +
  • +

    Added methods to calculate header size (Section 8.17).

    +
  • + +
    +
    +
    +

    A.8. Summary of changes made in version 1.1.0, released November 26, 2017.

    +
    +
      +
    • +

      Top-level functions (Chapter 10)

      +
      +
        +
      • +

        Functions may be declared at the top-level of a P4 program.

        +
      • +
      +
      +
    • +
    • +

      Optional and named parameters (Section 6.8)

      +
      +
        +
      • +

        Parameters may be specified by name, with a default value, or designated as optional.

        +
      • +
      +
      +
    • +
    • +

      enum representations (Section 8.3)

      +
      +
        +
      • +

        enum values to be specified with a concrete representation.

        +
      • +
      +
      +
    • +
    • +

      Parser values sets (Section 13.11)

      +
      +
        +
      • +

        value_set objects for control-plane programmable select labels.

        +
      • +
      +
      +
    • +
    • +

      Type definitions (Section 7.6)

      +
      +
        +
      • +

        New types may be introduced in programs.

        +
      • +
      +
      +
    • +
    • +

      Saturating arithmetic (Section 8.6)

      +
      +
        +
      • +

        Saturating arithmetic is supported on some targets.

        +
      • +
      +
      +
    • +
    • +

      Structured annotations (Chapter 20)

      +
      +
        +
      • +

        Annotations may be specified as lists of key-value pairs

        +
      • +
      +
      +
    • +
    • +

      Globalname (Section 18.3.2)

      +
      +
        +
      • +

        The reserved globalname annotation has been removed.

        +
      • +
      +
      +
    • +
    • +

      Table size property (Section 14.2.1.5)

      +
      +
        +
      • +

        Meaning of optional size property for tables has been defined.

        +
      • +
      +
      +
    • +
    • +

      Invalid headers (Section 8.17)

      +
      +
        +
      • +

        Clarified semantics of operations on invalid headers.

        +
      • +
      +
      +
    • +
    • +

      Calling restrictions (Appendix F)

      +
      +
        +
      • +

        Added restrictions on kinds of values that may be passed as arguments to calls.

        +
      • +
      +
      +
    • +
    • +

      Bitwise operator precedence (Appendix G)

      +
      +
        +
      • +

        Modified precedence conventions so that bitwise operators & | and ^ have higher precedence than relation operators < > <= >=.

        +
      • +
      +
      +
    • +
    • +

      Computed bitwidths (Section 7.1)

      +
      +
        +
      • +

        Added support for specifying widths using expressions in bit and varbit types.

        +
      • +
      +
      +
    • +
    +
    +
    +
    +

    A.9. Initial version 1.0.0, released May 17, 2017

    + +
    +
    +
    +
    +

    Appendix B: P4 reserved keywords

    +
    +
    +

    The following table shows all P4 reserved keywords. Some identifiers +are treated as keywords only in specific contexts (e.g., the keyword actions).

    +
    + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    abstract

    action

    apply

    bit

    bool

    const

    control

    default

    else

    enum

    error

    extern

    exit

    false

    header

    header_union

    if

    in

    inout

    int

    list

    match_kind

    package

    parser

    out

    return

    select

    state

    string

    struct

    switch

    table

    this

    transition

    true

    tuple

    type

    typedef

    value_set

    varbit

    verify

    void

    +
    +
    +
    +

    Appendix C: P4 reserved annotations

    +
    +
    +

    The following table shows all P4 reserved annotations.

    +
    + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    AnnotationPurposeSee Section

    atomic

    specify atomic execution

    Section 18.4.1

    defaultonly

    action can only appear in the default action

    Chapter 20

    hidden

    hides a controllable entity from the control plane

    Section 18.3.2

    match

    specify match_kind of a field in a value_set

    Section 13.11

    name

    assign local control-plane name

    Section 18.3.2

    optional

    parameter is optional

    Section 6.8.2

    tableonly

    action cannot be a default_action

    Chapter 20

    deprecated

    Construct has been deprecated

    Section 20.3.7

    pure

    pure function

    Section 20.3.6

    noSideEffects

    function with no side effects

    Section 20.3.6

    noWarn

    Has a string argument; inhibits compiler warnings

    Section 20.3.8

    +
    +
    +
    +

    Appendix D: P4 core library

    +
    +
    +

    The P4 core library contains declarations that are useful to most +programs.

    +
    +
    +

    For example, the core library includes the declarations of the +predefined packet_in and packet_out extern objects, used +in parsers and deparsers to access packet data.

    +
    +
    +
    +
    /// Standard error codes.  New error codes can be declared by users.
    +error {
    +    NoError,           /// No error.
    +    PacketTooShort,    /// Not enough bits in packet for 'extract'.
    +    NoMatch,           /// 'select' expression has no matches.
    +    StackOutOfBounds,  /// Reference to invalid element of a header stack.
    +    HeaderTooShort,    /// Extracting too many bits into a varbit field.
    +    ParserTimeout,     /// Parser execution time limit exceeded.
    +    ParserInvalidArgument  /// Parser operation was called with a value
    +                           /// not supported by the implementation.
    +}
    +extern packet_in {
    +    /// Read a header from the packet into a fixed-sized header @hdr
    +    /// and advance the cursor.
    +    /// May trigger error PacketTooShort or StackOutOfBounds.
    +    /// @T must be a fixed-size header type
    +    void extract<T>(out T hdr);
    +    /// Read bits from the packet into a variable-sized header @variableSizeHeader
    +    /// and advance the cursor.
    +    /// @T must be a header containing exactly 1 varbit field.
    +    /// May trigger errors PacketTooShort, StackOutOfBounds, or HeaderTooShort.
    +    void extract<T>(out T variableSizeHeader,
    +                    in bit<32> variableFieldSizeInBits);
    +    /// Read bits from the packet without advancing the cursor.
    +    /// @returns: the bits read from the packet.
    +    /// T may be an arbitrary fixed-size type.
    +    T lookahead<T>();
    +    /// Advance the packet cursor by the specified number of bits.
    +    void advance(in bit<32> sizeInBits);
    +    /// @return packet length in bytes.  This method may be unavailable on
    +    /// some target architectures.
    +    bit<32> length();
    +}
    +extern packet_out {
    +    /// Write @data into the output packet, skipping invalid headers
    +    /// and advancing the cursor
    +    /// @T can be a header type, a header stack, a header_union, or a struct
    +    /// containing fields with such types.
    +    void emit<T>(in T data);
    +}
    +action NoAction() {}
    +/// Standard match kinds for table key fields.
    +/// Some architectures may not support all these match kinds.
    +/// Architectures can declare additional match kinds.
    +match_kind {
    +    /// Match bits exactly.
    +    exact,
    +    /// Ternary match, using a mask.
    +    ternary,
    +    /// Longest-prefix match.
    +    lpm
    +}
    +
    +/// Static assert evaluates a boolean expression
    +/// at compilation time.  If the expression evaluates to
    +/// false, compilation is stopped and the corresponding message is printed.
    +/// The function returns a boolean, so that it can be used
    +/// as a global constant value in a program, e.g.:
    +/// const version = static_assert(V1MODEL_VERSION > 20180000, "Expected a v1 model version >= 20180000");
    +extern bool static_assert(bool check, string message);
    +
    +/// Like the above but using a default message.
    +extern bool static_assert(bool check);
    +
    +
    +
    +
    +
    +

    Appendix E: Checksums

    +
    +
    +

    There are no built-in constructs in P416 for manipulating packet +checksums. We expect that checksum operations can be expressed as extern library objects that are provided in target-specific libraries. The -standard architecture library should provide such checksum units. -

    -

    For example, one could provide an incremental checksum unit Checksum16 -(also described in the VSS example in Section 5.2.4) for -computing 16-bit one's complement using an extern object with a -signature such as: -

    -
    -
    -
    extern Checksum16 {
    -    Checksum16();              // constructor
    -    void clear();              // prepare unit for computation
    -    void update<T>(in T data); // add data to checksum
    -    void remove<T>(in T data); // remove data from existing checksum
    -    bit<16> get(); // get the checksum for the data added since last clear
    -}
    -

    IP checksum verification could be done in a parser as: -

    -
    -
    -
    ck16.clear();           // prepare checksum unit
    -ck16.update(h.ipv4);    // write header
    -verify(ck16.get() == 16w0, error.IPv4ChecksumError); // check for 0 checksum
    -
    -

    IP checksum generation could be done as: -

    -
    -
    -
    h.ipv4.hdrChecksum = 16w0;
    -ck16.clear();
    -ck16.update(h.ipv4);
    -h.ipv4.hdrChecksum = ck16.get();
    -

    Moreover, some switch architectures do not perform checksum +standard architecture library should provide such checksum units.

    +
    +
    +

    For example, one could provide an incremental checksum unit Checksum16 +(also described in the VSS example in Section 5.2.4) for +computing 16-bit one’s complement using an extern object with a +signature such as:

    +
    +
    +
    +
    extern Checksum16 {
    +    Checksum16();              // constructor
    +    void clear();              // prepare unit for computation
    +    void update<T>(in T data); // add data to checksum
    +    void remove<T>(in T data); // remove data from existing checksum
    +    bit<16> get(); // get the checksum for the data added since last clear
    +}
    +
    +
    +
    +

    IP checksum verification could be done in a parser as:

    +
    +
    +
    +
    ck16.clear();           // prepare checksum unit
    +ck16.update(h.ipv4);    // write header
    +verify(ck16.get() == 16w0, error.IPv4ChecksumError); // check for 0 checksum
    +
    +
    +
    +
    +

    IP checksum generation could be done as:

    +
    +
    +
    +
    h.ipv4.hdrChecksum = 16w0;
    +ck16.clear();
    +ck16.update(h.ipv4);
    +h.ipv4.hdrChecksum = ck16.get();
    +
    +
    +
    +

    Moreover, some switch architectures do not perform checksum verification, but only update checksums incrementally to reflect packet modifications. This could be achieved as well, as the following -P4 program fragments illustrates: -

    -
    -
    -
    ck16.clear();
    -ck16.update(h.ipv4.hdrChecksum);  // original checksum
    -ck16.remove( { h.ipv4.ttl, h.ipv4.proto } );
    -h.ipv4.ttl = h.ipv4.ttl - 1;
    -ck16.update( { h.ipv4.ttl, h.ipv4.proto } );
    -h.ipv4.hdrChecksum = ck16.get();

    F. Appendix: Restrictions on compile time and run time calls

    -

    This appendix summarizes restrictions on compile time and run time +P4 program fragments illustrates:

    +
    +
    +
    +
    ck16.clear();
    +ck16.update(h.ipv4.hdrChecksum);  // original checksum
    +ck16.remove( { h.ipv4.ttl, h.ipv4.proto } );
    +h.ipv4.ttl = h.ipv4.ttl - 1;
    +ck16.update( { h.ipv4.ttl, h.ipv4.proto } );
    +h.ipv4.hdrChecksum = ck16.get();
    +
    +
    +
    +
    +
    +

    Appendix F: Restrictions on compile time and run time calls

    +
    +
    +

    This appendix summarizes restrictions on compile time and run time calls that can be made. Many of them are described earlier in this -document, but are collected here for easy reference. -

    -

    The stateful types of objects in P416 are packages, parsers, -controls, externs, tables, and value-sets. P416 functions are also +document, but are collected here for easy reference.

    +
    +
    +

    The stateful types of objects in P416 are packages, parsers, +controls, externs, tables, and value-sets. P416 functions are also considered to be in that group, even if they happen to be pure functions of their arguments. All other types are referred to as -“value types” here. -

    -

    Some guiding principles: -

    -
      -
    • Controls are not allowed to call parsers, and vice versa, so there +"value types" here.

      +
    +
    +

    Some guiding principles:

    +
    +
    +
      +
    • +

      Controls are not allowed to call parsers, and vice versa, so there is no use in passing one type to the other in constructor parameters -or run-time parameters. +or run-time parameters.

    • -
    • At run time, after a control is called, and before that call is +
    • +

      At run time, after a control is called, and before that call is complete, there can be no recursive calls between controls, nor from a control to itself. Similarly for parsers. There can be loops -among states within a single parser. +among states within a single parser.

    • -
    • Externs are not allowed to call parsers or controls, so there is no -use in passing objects of those types to them. +
    • +

      Externs are not allowed to call parsers or controls, so there is no +use in passing objects of those types to them.

    • -
    • Tables are always instantiated directly in their enclosing control, +
    • +

      Tables are always instantiated directly in their enclosing control, and cannot be instantiated at the top level. There is no syntax for specifying parameters that are tables. Tables are only intended to -be used from within the control where they are defined. +be used from within the control where they are defined.

    • -
    • Value-sets can be instantiated in an enclosing parser or at the top level. +
    • +

      Value-sets can be instantiated in an enclosing parser or at the top level. There is no syntax for specifying parameters that are value-sets. Value-sets -can be shared between the parsers as long as they are in the scope. -

    - -

    A note on recursion: It is expected that some architectures will +can be shared between the parsers as long as they are in the scope.

    + + +
    +
    +

    A note on recursion: It is expected that some architectures will define capabilities for recirculating a packet to be processed again -as if it were a newly arriving packet, or to make “clones” of packets +as if it were a newly arriving packet, or to make "clones" of packets that are then processed by parsers and/or control blocks that the original packet has already completed. This does not change the notes -above on recursion that apply while a parser or control is executing. -

    -

    The first table lists restrictions on what types can be passed as -constructor parameters to other types. -

    - - - - - - - - - - - - - -
    can be a constructor parameter for this type
    This type package parser control extern
    package yes no no no
    parser yes yes no no
    control yes no yes no
    extern yes yes yes yes
    function no no no no
    table no no no no
    value-set no no no no
    value types yes yes yes yes
    -

    The next table lists restrictions on where one may perform -instantiations (see Section 11.3) of different types. -The answer for package is always “no” because there is no -“inside a package” where instantiations can be written in P416. One +above on recursion that apply while a parser or control is executing.

    +
    +
    +

    The first table lists restrictions on what types can be passed as +constructor parameters to other types.

    +
    + +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    This typecan be a constructor parameter for this type

    package

    parser

    control

    extern

    package

    yes

    no

    no

    no

    parser

    yes

    yes

    no

    no

    control

    yes

    no

    yes

    no

    extern

    yes

    yes

    yes

    yes

    function

    no

    no

    no

    no

    table

    no

    no

    no

    no

    value-set

    no

    no

    no

    no

    value types

    yes

    yes

    yes

    yes

    +
    +

    The next table lists restrictions on where one may perform +instantiations (see Section 11.3) of different types. +The answer for package is always "no" because there is no +"inside a package" where instantiations can be written in P416. One can definitely make constructor calls and use instances of stateful types as parameters when instantiating a package, and restrictions on -those types are in the table above. -

    -

    For externs, one can only specify their interface in P416, not their +those types are in the table above.

    +
    +
    +

    For externs, one can only specify their interface in P416, not their implementation. Thus there is no place to instantiate objects within -an extern. -

    -

    You may declare variables and constants of any of the value types -within a parser, control, or function (see Section 11.2 for more +an extern.

    +
    +
    +

    You may declare variables and constants of any of the value types +within a parser, control, or function (see Section 11.2 for more details). Declaring a variable or constant is not the same as -instantiation, hence the answer “N/A” (for not applicable) in those +instantiation, hence the answer "N/A" (for not applicable) in those table entries. Variables may not be declared at the top level of your -program, but constants may. -

    - - - - - - - - - - - - - -
    can be instantiated in this place
    This type top level package parser control extern function
    package yes no no no no no
    parser no no yes no no no
    control no no no yes no no
    extern yes no yes yes no no
    function yes no no no no no
    table no no no yes no no
    value-set yes no yes no no no
    value types N/A N/A N/A N/A N/A N/A
    -

    The next table lists restrictions on what types can be passed as +program, but constants may.

    +
    + +++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    This typecan be instantiated in this place

    top level

    package

    parser

    control

    extern

    function

    package

    yes

    no

    no

    no

    no

    no

    parser

    no

    no

    yes

    no

    no

    no

    control

    no

    no

    no

    yes

    no

    no

    extern

    yes

    no

    yes

    yes

    no

    no

    function

    yes

    no

    no

    no

    no

    no

    table

    no

    no

    no

    yes

    no

    no

    value-set

    yes

    no

    yes

    no

    no

    no

    value types

    N/A

    N/A

    N/A

    N/A

    N/A

    N/A

    +
    +

    The next table lists restrictions on what types can be passed as run-time parameters to other callable things that have run-time parameters: parsers, controls, externs (including methods and -extern functions), actions, and functions. -

    - - - - - - - - - - - - - - -
    can be a run-time parameter to this callable thing
    This type parser control extern action function
    package no no no no no
    parser no no no no no
    control no no no no no
    extern yes yes yes no no
    table no no no no no
    value-set no no no no no
    action no no no no no
    function no no no no no
    value types yes yes yes yes yes
    -

    Extern method and extern function calls may only return a value that +extern functions), actions, and functions.

    +
    + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    This typecan be a run-time parameter to this callable thing

    parser

    control

    extern

    action

    function

    package

    no

    no

    no

    no

    no

    parser

    no

    no

    no

    no

    no

    control

    no

    no

    no

    no

    no

    extern

    yes

    yes

    yes

    no

    no

    table

    no

    no

    no

    no

    no

    value-set

    no

    no

    no

    no

    no

    action

    no

    no

    no

    no

    no

    function

    no

    no

    no

    no

    no

    value types

    yes

    yes

    yes

    yes

    yes

    +
    +

    Extern method and extern function calls may only return a value that is a value type, or no value at all (specified by a return type of -void). -

    -

    The next table lists restrictions on what kinds of calls can be made +void).

    +
    +
    +

    The next table lists restrictions on what kinds of calls can be made from which places in a P4 program. Calling a parser, control, or -table means invoking its apply() method. Calling a value-set means using it -in a select expression. The row for extern describes where extern method -calls can be made from. -

    -

    One way that an extern can be called from the top level of a parser or +table means invoking its apply() method. Calling a value-set means using it +in a select expression. The row for extern describes where extern method +calls can be made from.

    +
    +
    +

    One way that an extern can be called from the top level of a parser or control is in an initializer expression for a declared variable, -e.g. bit<32> x = rand.get();. -

    - - - - - - - - - - - - - - - - -
    can be called at run time from this place in a P4 program
    control parser or
    parser apply control
    This type state block top level action extern function
    package N/A N/A N/A N/A N/A N/A
    parser yes no no no no no
    control no yes no no no no
    extern yes yes yes yes no no
    table no yes no no no no
    value-set yes no no no no no
    action no yes no yes no no
    function yes yes no yes no yes
    value types N/A N/A N/A N/A N/A N/A
    -

    There may not be any recursion in calls, neither by a thing calling -itself directly, nor mutual recursion. -

    -

    An extern can never cause any other type of P4 program object to be -called. See Section 6.8.1. -

    -

    Actions may be called directly from a control apply block. -

    -

    Note that while the extern row shows that extern methods can be called +e.g. bit<32> x = rand.get();.

    +
    + +++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    This typecan be called at run time from this place in a P4 program

    parser state

    control apply block

    parser or top level

    action

    extern

    function

    package

    N/A

    N/A

    N/A

    N/A

    N/A

    N/A

    parser

    yes

    no

    no

    no

    no

    no

    control

    no

    yes

    no

    no

    no

    no

    extern

    yes

    yes

    yes

    yes

    no

    no

    table

    no

    yes

    no

    no

    no

    no

    value-set

    yes

    no

    no

    no

    no

    no

    action

    no

    yes

    no

    yes

    no

    no

    function

    yes

    yes

    no

    yes

    no

    yes

    value types

    N/A

    N/A

    N/A

    N/A

    N/A

    N/A

    +
    +

    There may not be any recursion in calls, neither by a thing calling +itself directly, nor mutual recursion.

    +
    +
    +

    An extern can never cause any other type of P4 program object to be +called. See Section 6.8.

    +
    +
    +

    Actions may be called directly from a control apply block.

    +
    +
    +

    Note that while the extern row shows that extern methods can be called from many places, particular externs may have additional restrictions not listed in this table. Any such restrictions should be documented in the description for each extern, as part of the documentation for -the architecture that defines the extern. -

    -

    In many cases, the restriction will be “from a parser state only” or -“from a control apply block or action only”, but it may be even more +the architecture that defines the extern.

    +
    +
    +

    In many cases, the restriction will be "from a parser state only" or +"from a control apply block or action only", but it may be even more restrictive, e.g. only from a particular kind of control block -instantiated in a particular role in an architecture. -

    G. Appendix: P4 grammar

    -

    This is the grammar of P416 written using the YACC/bison +instantiated in a particular role in an architecture.

    +
    +
    +
    +
    +

    Appendix G: P4 grammar

    +
    +
    +

    This is the grammar of P416 written using the YACC/bison language. Absent from this grammar is the precedence of various -operations. -

    -

    The grammar is actually ambiguous, so the lexer and the parser must +operations.

    +
    +
    +

    The grammar is actually ambiguous, so the lexer and the parser must collaborate for parsing the language. In particular, the lexer must be -able to distinguish two kinds of identifiers: -

    -
      -
    • Type names previously introduced (TYPE_IDENTIFIER tokens) -
    • -
    • Regular identifiers (IDENTIFIER token) -
    - -

    The parser has to use a symbol table to indicate to the lexer how to +able to distinguish two kinds of identifiers:

    +
    +
    +
      +
    • +

      Type names previously introduced (TYPE_IDENTIFIER tokens)

      +
    • +
    • +

      Regular identifiers (IDENTIFIER token)

      +
    • +
    +
    +
    +

    The parser has to use a symbol table to indicate to the lexer how to parse subsequent appearances of identifiers. For example, given the -following program fragment: -

    -
    -
    -
    typedef bit<4> t;
    -struct s { /* body omitted */}
    -t x;
    -parser p(bit<8> b) { /* body omitted */ }
    -

    The lexer has to return the following terminal kinds: -

    -
    -
    -
    t - TYPE_IDENTIFIER
    -s - TYPE_IDENTIFIER
    -x - IDENTIFIER
    -p - TYPE_IDENTIFIER
    -b - IDENTIFIER
    -

    This grammar has been heavily influenced by limitations of the Bison -parser generator tool. -

    -

    The STRING_LITERAL token corresponds to a string literal -enclosed within double quotes, as described in Section -6.4.3.3. -

    -

    All other terminals are uppercase spellings of the corresponding -keywords. For example, RETURN is the terminal returned by the -lexer when parsing the keyword return. -

    -
    -
    -
    p4program
    -    : /* empty */
    +following program fragment:

    +
    +
    +
    +
    typedef bit<4> t;
    +struct s { /* body omitted */}
    +t x;
    +parser p(bit<8> b) { /* body omitted */ }
    +
    +
    +
    +

    The lexer has to return the following terminal kinds:

    +
    +
    +
    +
    t - TYPE_IDENTIFIER
    +s - TYPE_IDENTIFIER
    +x - IDENTIFIER
    +p - TYPE_IDENTIFIER
    +b - IDENTIFIER
    +
    +
    +
    +

    This grammar has been heavily influenced by limitations of the Bison +parser generator tool.

    +
    +
    +

    The STRING_LITERAL token corresponds to a string literal +enclosed within double quotes, as described in Section 6.4.3.3.

    +
    +
    +

    All other terminals are uppercase spellings of the corresponding +keywords. For example, RETURN is the terminal returned by the +lexer when parsing the keyword return.

    +
    +
    +
    +
    p4program
    +    : /* empty */
         | p4program declaration
    -    | p4program ";"  /* empty declaration */
    +    | p4program ";"  /* empty declaration */
         ;
     
     declaration
    @@ -10182,12 +14542,12 @@ 

    /* empty */ + : /* empty */ | CONST ; optAnnotations - : /* empty */ + : /* empty */ | annotations ; @@ -10197,14 +14557,14 @@

    "@" name - | "@" name "(" annotationBody ")" - | "@" name "[" structuredAnnotationBody "]" + : "@" name + | "@" name "(" annotationBody ")" + | "@" name "[" structuredAnnotationBody "]" ; annotationBody - : /* empty */ - | annotationBody "(" annotationBody ")" + : /* empty */ + | annotationBody "(" annotationBody ")" | annotationBody annotationToken ; @@ -10258,97 +14618,97 @@

    "_" + | "_" | IDENTIFIER | TYPE_IDENTIFIER | STRING_LITERAL | INTEGER - | "&&&" - | ".." - | "<<" - | "&&" - | "||" - | "==" - | "!=" - | ">=" - | "<=" - | "++" - | "+" - | "|+|" - | "-" - | "|-|" - | "*" - | "/" - | "%" - | "|" - | "&" - | "^" - | "~" - | "[" - | "]" - | "{" - | "}" - | "<" - | ">" - | "!" - | ":" - | "," - | "?" - | "." - | "=" - | ";" - | "@" + | "&&&" + | ".." + | "<<" + | "&&" + | "||" + | "==" + | "!=" + | ">=" + | "<=" + | "++" + | "+" + | "|+|" + | "-" + | "|-|" + | "*" + | "/" + | "%" + | "|" + | "&" + | "^" + | "~" + | "[" + | "]" + | "{" + | "}" + | "<" + | ">" + | "!" + | ":" + | "," + | "?" + | "." + | "=" + | ";" + | "@" ; kvList : kvPair - | kvList "," kvPair + | kvList "," kvPair ; kvPair - : name "=" expression + : name "=" expression ; parameterList - : /* empty */ + : /* empty */ | nonEmptyParameterList ; nonEmptyParameterList : parameter - | nonEmptyParameterList "," parameter + | nonEmptyParameterList "," parameter ; parameter : optAnnotations direction typeRef name - | optAnnotations direction typeRef name "=" expression + | optAnnotations direction typeRef name "=" expression ; direction : IN | OUT | INOUT - | /* empty */ + | /* empty */ ; packageTypeDeclaration : optAnnotations PACKAGE name optTypeParameters - "(" parameterList ")" + "(" parameterList ")" ; instantiation - : annotations typeRef "(" argumentList ")" name ";" - | typeRef "(" argumentList ")" name ";" - | annotations typeRef "(" argumentList ")" name "=" objInitializer ";" - | typeRef "(" argumentList ")" name "=" objInitializer ";" + : annotations typeRef "(" argumentList ")" name ";" + | typeRef "(" argumentList ")" name ";" + | annotations typeRef "(" argumentList ")" name "=" objInitializer ";" + | typeRef "(" argumentList ")" name "=" objInitializer ";" ; objInitializer - : "{" objDeclarations "}" + : "{" objDeclarations "}" ; objDeclarations - : /* empty */ + : /* empty */ | objDeclarations objDeclaration ; @@ -10358,23 +14718,23 @@

    /* empty */ - | "(" parameterList ")" + : /* empty */ + | "(" parameterList ")" ; dotPrefix - : "." + : "." ; -/**************************** PARSER ******************************/ +/**************************** PARSER ******************************/ parserDeclaration : parserTypeDeclaration optConstructorParameters - "{" parserLocalElements parserStates "}" + "{" parserLocalElements parserStates "}" ; parserLocalElements - : /* empty */ + : /* empty */ | parserLocalElements parserLocalElement ; @@ -10387,7 +14747,7 @@

    "(" parameterList ")" + "(" parameterList ")" ; parserStates @@ -10397,11 +14757,11 @@

    "{" parserStatements transitionStatement "}" + "{" parserStatements transitionStatement "}" ; parserStatements - : /* empty */ + : /* empty */ | parserStatements parserStatement ; @@ -10416,30 +14776,30 @@

    "{" parserStatements "}" + : optAnnotations "{" parserStatements "}" ; transitionStatement - : /* empty */ + : /* empty */ | TRANSITION stateExpression ; stateExpression - : name ";" + : name ";" | selectExpression ; selectExpression - : SELECT "(" expressionList ")" "{" selectCaseList "}" + : SELECT "(" expressionList ")" "{" selectCaseList "}" ; selectCaseList - : /* empty */ + : /* empty */ | selectCaseList selectCase ; selectCase - : keysetExpression ":" name ";" + : keysetExpression ":" name ";" ; keysetExpression @@ -10448,59 +14808,59 @@

    "(" simpleKeysetExpression "," simpleExpressionList ")" - | "(" reducedSimpleKeysetExpression ")" + : "(" simpleKeysetExpression "," simpleExpressionList ")" + | "(" reducedSimpleKeysetExpression ")" ; optTrailingComma - : /* empty */ - | "," + : /* empty */ + | "," ; simpleExpressionList : simpleKeysetExpression - | simpleExpressionList "," simpleKeysetExpression + | simpleExpressionList "," simpleKeysetExpression ; reducedSimpleKeysetExpression - : expression "&&&" expression - | expression ".." expression + : expression "&&&" expression + | expression ".." expression | DEFAULT - | "_" + | "_" ; simpleKeysetExpression : expression - | expression "&&&" expression - | expression ".." expression + | expression "&&&" expression + | expression ".." expression | DEFAULT - | "_" + | "_" ; valueSetDeclaration : optAnnotations - VALUESET "<" baseType ">" "(" expression ")" name ";" + VALUESET "<" baseType ">" "(" expression ")" name ";" | optAnnotations - VALUESET "<" tupleType ">" "(" expression ")" name ";" + VALUESET "<" tupleType ">" "(" expression ")" name ";" | optAnnotations - VALUESET "<" typeName ">" "(" expression ")" name ";" + VALUESET "<" typeName ">" "(" expression ")" name ";" ; -/*************************** CONTROL ************************/ +/*************************** CONTROL ************************/ controlDeclaration : controlTypeDeclaration optConstructorParameters - /* controlTypeDeclaration cannot contain type parameters */ - "{" controlLocalDeclarations APPLY controlBody "}" + /* controlTypeDeclaration cannot contain type parameters */ + "{" controlLocalDeclarations APPLY controlBody "}" ; controlTypeDeclaration : optAnnotations CONTROL name optTypeParameters - "(" parameterList ")" + "(" parameterList ")" ; controlLocalDeclarations - : /* empty */ + : /* empty */ | controlLocalDeclarations controlLocalDeclaration ; @@ -10516,28 +14876,28 @@

    /*************************** EXTERN *************************/ +/*************************** EXTERN *************************/ externDeclaration - : optAnnotations EXTERN nonTypeName optTypeParameters "{" methodPrototypes "}" - | optAnnotations EXTERN functionPrototype ";" + : optAnnotations EXTERN nonTypeName optTypeParameters "{" methodPrototypes "}" + | optAnnotations EXTERN functionPrototype ";" ; methodPrototypes - : /* empty */ + : /* empty */ | methodPrototypes methodPrototype ; functionPrototype - : typeOrVoid name optTypeParameters "(" parameterList ")" + : typeOrVoid name optTypeParameters "(" parameterList ")" ; methodPrototype - : optAnnotations functionPrototype ";" - | optAnnotations TYPE_IDENTIFIER "(" parameterList ")" ";" + : optAnnotations functionPrototype ";" + | optAnnotations TYPE_IDENTIFIER "(" parameterList ")" ";" ; -/************************** TYPES ****************************/ +/************************** TYPES ****************************/ typeRef : baseType @@ -10563,20 +14923,20 @@

    "<" typeArg ">" + : LIST "<" typeArg ">" ; tupleType - : TUPLE "<" typeArgumentList ">" + : TUPLE "<" typeArgumentList ">" ; headerStackType - : typeName "[" expression "]" - | specializedType "[" expression "]" + : typeName "[" expression "]" + | specializedType "[" expression "]" ; specializedType - : typeName "<" typeArgumentList ">" + : typeName "<" typeArgumentList ">" ; baseType @@ -10586,64 +14946,64 @@

    "<" INTEGER ">" - | INT "<" INTEGER ">" - | VARBIT "<" INTEGER ">" - | BIT "<" "(" expression ")" ">" - | INT "<" "(" expression ")" ">" - | VARBIT "<" "(" expression ")" ">" + | BIT "<" INTEGER ">" + | INT "<" INTEGER ">" + | VARBIT "<" INTEGER ">" + | BIT "<" "(" expression ")" ">" + | INT "<" "(" expression ")" ">" + | VARBIT "<" "(" expression ")" ">" ; typeOrVoid : typeRef | VOID - | IDENTIFIER // may be a type variable + | IDENTIFIER // may be a type variable ; optTypeParameters - : /* empty */ + : /* empty */ | typeParameters ; typeParameters - : "<" typeParameterList ">" + : "<" typeParameterList ">" ; typeParameterList : name - | typeParameterList "," name + | typeParameterList "," name ; typeArg : typeRef | nonTypeName | VOID - | "_" + | "_" ; typeArgumentList - : /* empty */ + : /* empty */ | typeArg - | typeArgumentList "," typeArg + | typeArgumentList "," typeArg ; realTypeArg : typeRef | VOID - | "_" + | "_" ; realTypeArgumentList : realTypeArg - | realTypeArgumentList "," typeArg + | realTypeArgumentList "," typeArg ; typeDeclaration : derivedTypeDeclaration - | typedefDeclaration ";" - | parserTypeDeclaration ";" - | controlTypeDeclaration ";" - | packageTypeDeclaration ";" + | typedefDeclaration ";" + | parserTypeDeclaration ";" + | controlTypeDeclaration ";" + | packageTypeDeclaration ";" ; derivedTypeDeclaration @@ -10654,52 +15014,52 @@

    "{" structFieldList "}" + : optAnnotations HEADER name optTypeParameters "{" structFieldList "}" ; structTypeDeclaration - : optAnnotations STRUCT name optTypeParameters "{" structFieldList "}" + : optAnnotations STRUCT name optTypeParameters "{" structFieldList "}" ; headerUnionDeclaration - : optAnnotations HEADER_UNION name optTypeParameters "{" structFieldList "}" + : optAnnotations HEADER_UNION name optTypeParameters "{" structFieldList "}" ; structFieldList - : /* empty */ + : /* empty */ | structFieldList structField ; structField - : optAnnotations typeRef name ";" + : optAnnotations typeRef name ";" ; enumDeclaration - : optAnnotations ENUM name "{" identifierList optTrailingComma "}" - | optAnnotations ENUM typeRef name "{" - specifiedIdentifierList optTrailingComma "}" + : optAnnotations ENUM name "{" identifierList optTrailingComma "}" + | optAnnotations ENUM typeRef name "{" + specifiedIdentifierList optTrailingComma "}" ; specifiedIdentifierList : specifiedIdentifier - | specifiedIdentifierList "," specifiedIdentifier + | specifiedIdentifierList "," specifiedIdentifier ; specifiedIdentifier - : name "=" initializer + : name "=" initializer ; errorDeclaration - : ERROR "{" identifierList "}" + : ERROR "{" identifierList "}" ; matchKindDeclaration - : MATCH_KIND "{" identifierList optTrailingComma "}" + : MATCH_KIND "{" identifierList optTrailingComma "}" ; identifierList : name - | identifierList "," name + | identifierList "," name ; typedefDeclaration @@ -10708,44 +15068,44 @@

    /*************************** STATEMENTS *************************/ +/*************************** STATEMENTS *************************/ assignmentOrMethodCallStatement - : lvalue "(" argumentList ")" ";" - | lvalue "<" typeArgumentList ">" "(" argumentList ")" ";" - | lvalue "=" expression ";" + : lvalue "(" argumentList ")" ";" + | lvalue "<" typeArgumentList ">" "(" argumentList ")" ";" + | lvalue "=" expression ";" ; breakStatement - : BREAK ";" + : BREAK ";" ; continueStatement - : CONTINUE ";" + : CONTINUE ";" ; emptyStatement - : ";" + : ";" ; exitStatement - : EXIT ";" + : EXIT ";" ; returnStatement - : RETURN ";" - | RETURN expression ";" + : RETURN ";" + | RETURN expression ";" ; conditionalStatement - : IF "(" expression ")" statement - | IF "(" expression ")" statement ELSE statement + : IF "(" expression ")" statement + | IF "(" expression ")" statement ELSE statement ; -// To support direct invocation of a control or parser without instantiation +// To support direct invocation of a control or parser without instantiation directApplication - : typeName "." APPLY "(" argumentList ")" ";" - | specializedType "." APPLY "(" argumentList ")" ";" + : typeName "." APPLY "(" argumentList ")" ";" + | specializedType "." APPLY "(" argumentList ")" ";" ; statement @@ -10763,26 +15123,26 @@

    "{" statOrDeclList "}" + : optAnnotations "{" statOrDeclList "}" ; statOrDeclList - : /* empty */ + : /* empty */ | statOrDeclList statementOrDeclaration ; switchStatement - : SWITCH "(" expression ")" "{" switchCases "}" + : SWITCH "(" expression ")" "{" switchCases "}" ; switchCases - : /* empty */ + : /* empty */ | switchCases switchCase ; switchCase - : switchLabel ":" blockStatement - | switchLabel ":" // fall-through + : switchLabel ":" blockStatement + | switchLabel ":" // fall-through ; switchLabel @@ -10791,47 +15151,47 @@

    "(" optForInitStatements ";" expression ";" - optForUpdateStatements ")" statement - | optAnnotations FOR "(" typeRef name IN forCollectionExpr ")" statement + : optAnnotations FOR "(" optForInitStatements ";" expression ";" + optForUpdateStatements ")" statement + | optAnnotations FOR "(" typeRef name IN forCollectionExpr ")" statement ; optForInitStatements - : /* empty */ + : /* empty */ | forInitStatements ; forInitStatements : forInitStatement - | forInitStatements "," forInitStatement + | forInitStatements "," forInitStatement ; forInitStatement : optAnnotations typeRef name optInitializer - | lvalue "(" argumentList ")" - | lvalue "<" typeArgumentList ">" "(" argumentList ")" - | lvalue "=" expression + | lvalue "(" argumentList ")" + | lvalue "<" typeArgumentList ">" "(" argumentList ")" + | lvalue "=" expression ; optForUpdateStatements - : /* empty */ + : /* empty */ | forUpdateStatements ; forUpdateStatements : forUpdateStatement - | forUpdateStatements "," forUpdateStatement + | forUpdateStatements "," forUpdateStatement ; forUpdateStatement - : lvalue "(" argumentList ")" - | lvalue "<" typeArgumentList ">" "(" argumentList ")" - | lvalue "=" expression + : lvalue "(" argumentList ")" + | lvalue "<" typeArgumentList ">" "(" argumentList ")" + | lvalue "=" expression ; forCollectionExpr : expression - | expression ".." erxpression + | expression ".." erxpression ; statementOrDeclaration @@ -10840,10 +15200,10 @@

    /************************* TABLE *********************************/ +/************************* TABLE *********************************/ tableDeclaration - : optAnnotations TABLE name "{" tablePropertyList "}" + : optAnnotations TABLE name "{" tablePropertyList "}" ; tablePropertyList @@ -10852,73 +15212,73 @@

    "=" "{" keyElementList "}" - | ACTIONS "=" "{" actionList "}" - | optAnnotations optCONST ENTRIES "=" "{" entriesList "}" - | optAnnotations optCONST nonTableKwName "=" initializer ";" + : KEY "=" "{" keyElementList "}" + | ACTIONS "=" "{" actionList "}" + | optAnnotations optCONST ENTRIES "=" "{" entriesList "}" + | optAnnotations optCONST nonTableKwName "=" initializer ";" ; keyElementList - : /* empty */ + : /* empty */ | keyElementList keyElement ; keyElement - : expression ":" name optAnnotations ";" + : expression ":" name optAnnotations ";" ; actionList - : /* empty */ - | actionList optAnnotations actionRef ";" + : /* empty */ + | actionList optAnnotations actionRef ";" ; actionRef : prefixedNonTypeName - | prefixedNonTypeName "(" argumentList ")" + | prefixedNonTypeName "(" argumentList ")" ; entry - : optCONST entryPriority keysetExpression ':' actionRef optAnnotations ';' - | optCONST keysetExpression ':' actionRef optAnnotations ';' + : optCONST entryPriority keysetExpression ':' actionRef optAnnotations ';' + | optCONST keysetExpression ':' actionRef optAnnotations ';' ; entryPriority - : PRIORITY '=' INTEGER ":" - | PRIORITY '=' '(' expression ')' ":" + : PRIORITY '=' INTEGER ":" + | PRIORITY '=' '(' expression ')' ":" ; entriesList - : /* empty */ + : /* empty */ | entriesList entry ; -/************************* ACTION ********************************/ +/************************* ACTION ********************************/ actionDeclaration - : optAnnotations ACTION name "(" parameterList ")" blockStatement + : optAnnotations ACTION name "(" parameterList ")" blockStatement ; -/************************* VARIABLES *****************************/ +/************************* VARIABLES *****************************/ variableDeclaration - : annotations typeRef name optInitializer ";" - | typeRef name optInitializer ";" + : annotations typeRef name optInitializer ";" + | typeRef name optInitializer ";" ; constantDeclaration - : optAnnotations CONST typeRef name "=" initializer ";" + : optAnnotations CONST typeRef name "=" initializer ";" ; optInitializer - : /* empty */ - | "=" initializer + : /* empty */ + | "=" initializer ; initializer : expression ; -/**************** Expressions ****************/ +/**************** Expressions ****************/ functionDeclaration : annotations functionPrototype blockStatement @@ -10926,26 +15286,26 @@

    /* empty */ + : /* empty */ | nonEmptyArgList ; nonEmptyArgList : argument - | nonEmptyArgList "," argument + | nonEmptyArgList "," argument ; argument - : expression /* positional argument */ - | name "=" expression /* named argument */ - | "_" - | name "=" "_" + : expression /* positional argument */ + | name "=" expression /* named argument */ + | "_" + | name "=" "_" ; expressionList - : /* empty */ + : /* empty */ | expression - | expressionList "," expression + | expressionList "," expression ; structuredAnnotationBody @@ -10965,30 +15325,30 @@

    "." member - | lvalue "[" expression "]" - | lvalue "[" expression ":" expression "]" - | "(" lvalue ")" - ; - -%left "," -%nonassoc "?" -%nonassoc ":" -%left "||" -%left "&&" -%left "==" "!=" -%left "<" ">" "<=" ">=" -%left "|" -%left "^" -%left "&" -%left "<<" ">>" -%left "++" "+" "-" "|+|" "|-|" -%left "*" "/" "%" + | lvalue "." member + | lvalue "[" expression "]" + | lvalue "[" expression ":" expression "]" + | "(" lvalue ")" + ; + +%left "," +%nonassoc "?" +%nonassoc ":" +%left "||" +%left "&&" +%left "==" "!=" +%left "<" ">" "<=" ">=" +%left "|" +%left "^" +%left "&" +%left "<<" ">>" +%left "++" "+" "-" "|+|" "|-|" +%left "*" "/" "%" %right PREFIX -%nonassoc "]" "(" "[" -%left "." +%nonassoc "]" "(" "[" +%left "." -// Additional precedences need to be specified +// Additional precedences need to be specified expression : INTEGER @@ -10998,46 +15358,46 @@

    "[" expression "]" - | expression "[" expression ":" expression "]" - | "{" expressionList optTrailingComma "}" - | "{#}" - | "{" kvList optTrailingComma "}" - | "{" kvList "," DOTS optTrailingComma "}" - | "(" expression ")" - | "!" expression %prec PREFIX - | "~" expression %prec PREFIX - | "-" expression %prec PREFIX - | "+" expression %prec PREFIX - | typeName "." member - | ERROR "." member - | expression "." member - | expression "*" expression - | expression "/" expression - | expression "%" expression - | expression "+" expression - | expression "-" expression - | expression "|+|" expression - | expression "|-|" expression - | expression "<<" expression - | expression ">>" expression - | expression "<=" expression - | expression ">=" expression - | expression "<" expression - | expression ">" expression - | expression "!=" expression - | expression "==" expression - | expression "&" expression - | expression "^" expression - | expression "|" expression - | expression "++" expression - | expression "&&" expression - | expression "||" expression - | expression "?" expression ":" expression - | expression "<" realTypeArgumentList ">" "(" argumentList ")" - | expression "(" argumentList ")" - | namedType "(" argumentList ")" - | "(" typeRef ")" expression + | expression "[" expression "]" + | expression "[" expression ":" expression "]" + | "{" expressionList optTrailingComma "}" + | "{#}" + | "{" kvList optTrailingComma "}" + | "{" kvList "," DOTS optTrailingComma "}" + | "(" expression ")" + | "!" expression %prec PREFIX + | "~" expression %prec PREFIX + | "-" expression %prec PREFIX + | "+" expression %prec PREFIX + | typeName "." member + | ERROR "." member + | expression "." member + | expression "*" expression + | expression "/" expression + | expression "%" expression + | expression "+" expression + | expression "-" expression + | expression "|+|" expression + | expression "|-|" expression + | expression "<<" expression + | expression ">>" expression + | expression "<=" expression + | expression ">=" expression + | expression "<" expression + | expression ">" expression + | expression "!=" expression + | expression "==" expression + | expression "&" expression + | expression "^" expression + | expression "|" expression + | expression "++" expression + | expression "&&" expression + | expression "||" expression + | expression "?" expression ":" expression + | expression "<" realTypeArgumentList ">" "(" argumentList ")" + | expression "(" argumentList ")" + | namedType "(" argumentList ")" + | "(" typeRef ")" expression ; nonBraceExpression @@ -11047,80 +15407,71 @@

    "[" expression "]" - | nonBraceExpression "[" expression ":" expression "]" - | "(" expression ")" - | "!" expression %prec PREFIX - | "~" expression %prec PREFIX - | "-" expression %prec PREFIX - | "+" expression %prec PREFIX - | typeName "." member - | ERROR "." member - | nonBraceExpression "." member - | nonBraceExpression "*" expression - | nonBraceExpression "/" expression - | nonBraceExpression "%" expression - | nonBraceExpression "+" expression - | nonBraceExpression "-" expression - | nonBraceExpression "|+|" expression - | nonBraceExpression "|-|" expression - | nonBraceExpression "<<" expression - | nonBraceExpression ">>" expression - | nonBraceExpression "<=" expression - | nonBraceExpression ">=" expression - | nonBraceExpression "<" expression - | nonBraceExpression ">" expression - | nonBraceExpression "!=" expression - | nonBraceExpression "==" expression - | nonBraceExpression "&" expression - | nonBraceExpression "^" expression - | nonBraceExpression "|" expression - | nonBraceExpression "++" expression - | nonBraceExpression "&&" expression - | nonBraceExpression "||" expression - | nonBraceExpression "?" expression ":" expression - | nonBraceExpression "<" realTypeArgumentList ">" "(" argumentList ")" - | nonBraceExpression "(" argumentList ")" - | namedType "(" argumentList ")" - | "(" typeRef ")" expression - ; -

    -
    -
    - -
    -

    1.an enum type used as a field in a header must specify a -underlying type and representation for enum elements. -

    -
    -

    2.a struct or nested struct type that has the same properties, -used as a field in a header must contain only -bit<W>, int<W>, a serializable enum, or a bool. -

    -
    -

    3.type B <name> is allowed for a type name B -defined via typedef X B if type X <name> is allowed. -

    -
    -

    4.The P4Runtime API is defined as a Google Protocol -Buffer .proto file and an accompanying English specification document -here: -https://​github.​com/​p4lang/​p4runtime -

    -
    -

    5.TDI is the Table Driven Interface. More information can be -found here: https://github.com/p4lang/tdi -

    -
    -

    6.Most existing P416 programs today do -not use function or method calls in table key expressions, and the -order of evaluation of these key expressions makes no difference -in the resulting lookup key value. In this overwhelmingly common -case, if an implementation chooses to insert extra assignment -statements to implement side-effecting key expressions, but does -not insert them when there are no side-effecting key expressions, -then in typical programs they will almost never be inserted. -

    + | nonBraceExpression "[" expression "]" + | nonBraceExpression "[" expression ":" expression "]" + | "(" expression ")" + | "!" expression %prec PREFIX + | "~" expression %prec PREFIX + | "-" expression %prec PREFIX + | "+" expression %prec PREFIX + | typeName "." member + | ERROR "." member + | nonBraceExpression "." member + | nonBraceExpression "*" expression + | nonBraceExpression "/" expression + | nonBraceExpression "%" expression + | nonBraceExpression "+" expression + | nonBraceExpression "-" expression + | nonBraceExpression "|+|" expression + | nonBraceExpression "|-|" expression + | nonBraceExpression "<<" expression + | nonBraceExpression ">>" expression + | nonBraceExpression "<=" expression + | nonBraceExpression ">=" expression + | nonBraceExpression "<" expression + | nonBraceExpression ">" expression + | nonBraceExpression "!=" expression + | nonBraceExpression "==" expression + | nonBraceExpression "&" expression + | nonBraceExpression "^" expression + | nonBraceExpression "|" expression + | nonBraceExpression "++" expression + | nonBraceExpression "&&" expression + | nonBraceExpression "||" expression + | nonBraceExpression "?" expression ":" expression + | nonBraceExpression "<" realTypeArgumentList ">" "(" argumentList ")" + | nonBraceExpression "(" argumentList ")" + | namedType "(" argumentList ")" + | "(" typeRef ")" expression + ; +
    +
    +
    +
    +
    +
    +
    + + +
    +3. The P4Runtime API is defined as a Google Protocol Buffer .proto file and an accompanying English specification document here: https://github.com/p4lang/p4runtime +
    +
    +4. TDI is the Table Driven Interface. More information can be found here: https://github.com/p4lang/tdi +
    +
    +5. Most existing P416 programs today do not use function or method calls in table key expressions, and the order of evaluation of these key expressions makes no difference in the resulting lookup key value. In this overwhelmingly common case, if an implementation chooses to insert extra assignment statements to implement side-effecting key expressions, but does not insert them when there are no side-effecting key expressions, then in typical programs they will almost never be inserted. +
    +
    + - - + \ No newline at end of file diff --git a/docs/P4-16-working-spec.pdf b/docs/P4-16-working-spec.pdf index b12e294912..e6907b66d3 100644 Binary files a/docs/P4-16-working-spec.pdf and b/docs/P4-16-working-spec.pdf differ diff --git a/docs/P4_API_WG_charter.pdf b/docs/P4_API_WG_charter.pdf new file mode 100644 index 0000000000..29825a40c0 Binary files /dev/null and b/docs/P4_API_WG_charter.pdf differ diff --git a/docs/P4_Arch_Charter.html b/docs/P4_Arch_Charter.html index cea634f898..2fe87d8539 100644 --- a/docs/P4_Arch_Charter.html +++ b/docs/P4_Arch_Charter.html @@ -1,368 +1,768 @@ - + - - - - P4 Architecture Working Group Charter - - - - - -
    - -
    -
    -
    P4 Architecture Working Group Charter

    1. Working Group Name

    -

    P4 Architecture Working Group -

    2. Chairs

    -
      -
    • Gordon Brebner gjb@​xilinx.​com + + + + +P4 Architecture Working Group Charter + + + + +
      +
      +

      Working Group Name

      +
      +
      +

      P4 Architecture Working Group

      +
      +
      +
      +
      +

      Chairs

      +
      +
      + -

      3. Description of the Working Group

      -

      One of P416's key innovations is the introductions of a P4 -architecture the equivalent of a programming model. A P4 +

    +
    + + +
    +

    Description of the Working Group

    +
    +
    +

    One of P416's key innovations is the introductions of a P4 +architecture — the equivalent of a programming model. A P4 architecture allows data-plane programmers to write P4 code against a well defined set of functionality. Vendors can define new architectures for their targets or personalize targets with multiple -architectures, including mapping other vendor's architectures to a -target. -

    -

    The main goal of the P4 Architecture working group is to provide a +architectures, including mapping other vendor’s architectures to a +target.

    +
    +
    +

    The main goal of the P4 Architecture working group is to provide a specification for the Portable Switch Architecture (PSA). The PSA is building on the success of the Protocol Independent Switch -Architecture (PISA) that was embedded in P414 and extends it with -new functionality using packages and externs. We aim to provide +Architecture (PISA) that was embedded in P414 and extends it with +new functionality using packages and externs. We aim to provide the foundation to enable writing portable P4 programs that span multiple devices, allow for composing functionality developed by different parties, and in general, provide a common platform that vendors can agree to support as a target to lower the barrier of entry -to data-plane programming. -

    -

    In addition, we will provide a blue-print for defining new -architectures the language specification to define P4-programmable +to data-plane programming.

    +
    +
    +

    In addition, we will provide a blue-print for defining new +architectures — the language specification to define P4-programmable blocks and control flows, and the interfaces between them. To achieve this we are working closely with the P4 language working group and the P4 API working group to ensure the necessary language support and the -appropriate control-plane APIs. -

    -

    All programmable switches rely on a set of core functionality in +appropriate control-plane APIs.

    +
    +
    +

    All programmable switches rely on a set of core functionality in addition to tables and actions, such as counters, meters, registers, -etc. In P416 these are expressed as externs. As part of the PSA +etc. In P416 these are expressed as externs. As part of the PSA definition, we are defining a set of standard functionality and API that every target mapping PSA should support. The goal is to provide a collection of well-defined portable externs, such that programmers -have access to common functionality across multiple platforms. -

    4. Deliverables

    -

    The P4 Architecture working group will initially focus on the +have access to common functionality across multiple platforms.

    +
    +
    +
    +
    +

    Deliverables

    +
    +
    +

    The P4 Architecture working group will initially focus on the definition of the Portable Switch Architecture (PSA), which consists of the following deliverables: -- The PSA specification, which defines a PISA-like pipeline and a set +* The PSA specification, which defines a PISA-like pipeline and a set of externs as a reference for a switch target implementation -- An open-source, reference implementation of the PSA on the P4C - compiler targeting the Behavioral - model software switch. -

    5. Logistics

    -

    Working Group discussion happens in bi-weekly in-person meetings and +* An open-source, reference implementation of the PSA on the P4C + compiler targeting the + Behavioral Model + software switch.

    +
    +
    +
    +
    +

    Logistics

    +
    +
    +

    Working Group discussion happens in bi-weekly in-person meetings and electronic discussion on -Github. The -meeting minutes are made +Github. The +meeting minutes are made available shortly after each meeting. Participation in the Working Group is open to all members of the P4 Language Consortium. To subscribe to the mailing list, please visit the P4-arch mailing list, and to post a message to all members, send an email to -p4-arch@lists.p4.org. -

    +p4-arch@lists.p4.org.

    +
    +
    + + + - - + \ No newline at end of file diff --git a/docs/PSA.html b/docs/PSA.html index eec521875b..023958b5e0 100644 --- a/docs/PSA.html +++ b/docs/PSA.html @@ -1,322 +1,838 @@ - + - - - - - P4~16~ Portable Switch Architecture (PSA) - - - - - -
    - -
    -
    -
    P416 Portable Switch Architecture (PSA)
    -
    version 1.2
    -
    -
    -
    The P4.org Architecture Working Group
    -
    2024-11-06
    -

    - - - - - -

    -
    -

    Abstract. + + + + +P416 Portable Switch Architecture (PSA) + + + +

    +
    +
    +
    +
    +
    Abstract
    +
    P4 is a language for expressing how packets are processed by the data plane of a programmable network forwarding element. P4 programs specify how the various programmable blocks of a target architecture @@ -324,306 +840,314 @@ is a target architecture that describes common capabilities of network switch devices that process and forward packets across multiple interface ports. -

    -

    1. Target Architecture Model

    -

    As an analogy, the PSA is to the P416 language as the C standard + +

    + +
    +
    +
    +

    1. Target Architecture Model

    +
    +
    +

    As an analogy, the PSA is to the P416 language as the C standard library is to the C programming language. PSA defines a library of -types, P416 externs for frequently used constructs such as counters, -meters, and registers, and a set of “packet paths” that enable you to +types, P416 externs for frequently used constructs such as counters, +meters, and registers, and a set of "packet paths" that enable you to write P4 programs that control the flow of packets in a packet switch that has multiple ports, e.g. dozens of Ethernet ports. By following the APIs and guidelines here, developers will be able to write P4 programs that are portable across many devices that are conformant to -the PSA. -

    -

    While parts of PSA are specific to network switches, and the “Portable -NIC Architecture” differs significantly from PSA in those parts, we +the PSA.

    +
    +
    +

    While parts of PSA are specific to network switches, and the "Portable +NIC Architecture" differs significantly from PSA in those parts, we expect the externs defined here will be of general use across multiple -P416 architectures. -

    -

    The Portable Switch Architecture (PSA) Model has six programmable P4 -blocks and two fixed-function blocks, as shown in Figure -1. The behavior of the programmable blocks is specified -using P4. The Packet buffer and Replication Engine (PRE) and the -Buffer Queuing Engine (BQE) are target dependent functional blocks -that may be configured for a fixed set of operations. -

    -
    -

    switch -

    -
    - -
    Figure 1. Portable Switch Pipeline
    -

    Incoming packets are parsed and validated, and then passed to an +P416 architectures.

    +
    +
    +

    The Portable Switch Architecture (PSA) Model has six programmable P4 +blocks and two fixed-function blocks, as shown in Figure 1. The +behavior of the programmable blocks is specified using P4. The Packet +buffer and Replication Engine (PRE) and the Buffer Queuing Engine (BQE) +are target dependent functional blocks that may be configured for a +fixed set of operations.

    +
    +
    +
    +psa pipeline +
    +
    Figure 1. Portable Switch Pipeline
    +
    +
    +

    Incoming packets are parsed and validated, and then passed to an ingress match action pipeline, which makes decisions on where the packets go. The ingress deparser P4 code specifies the packet contents to be sent to the packet buffer, and what metadata related to the packet is carried with it. After the ingress pipeline, the packet may optionally be replicated (i.e. copies made to multiple egress -ports), then stored in the packet buffer. -

    -

    For each such egress port, the packet passes through an egress parser +ports), then stored in the packet buffer.

    +
    +
    +

    For each such egress port, the packet passes through an egress parser and match action pipeline before it is deparsed and queued to leave -the pipeline. -

    -

    A programmer targeting the PSA is required to define objects in P4 for +the pipeline.

    +
    +
    +

    A programmer targeting the PSA is required to define objects in P4 for the programmable blocks that conform to APIs defined later (see -section 5). The programmable block inputs -and outputs are templatized on user defined headers and metadata. -Once these six blocks are defined, a P4 program for PSA instantiates -the main package object, with the programmable blocks passed as -arguments (see Section 7.3 for an example). -

    -

    A P4 programmer wishing to maximize the portability of their program -should follow several general guidelines: -

    -
      -
    • Do not use undefined values in a way that affects the resulting -output packet(s), or for side effects such as updating Counter, -Meter or Register instances. -
    • -
    • Use as few resources as possible, e.g. table search key bits, array -sizes, quantity of metadata associated with packets, etc. -
    - -

    This document contains excerpts of several P416 programs that use -the psa.p4 include file and demonstrate features of PSA. Source +Chapter 6). The programmable block inputs and outputs +are templatized on user defined headers and metadata. Once these six +blocks are defined, a P4 program for PSA instantiates the main +package object, with the programmable blocks passed as arguments (see +Section 8.3 for an example).

    +
    +
    +

    A P4 programmer wishing to maximize the portability of their program +should follow several general guidelines:

    +
    +
    +
      +
    • +

      Do not use undefined values in a way that affects the resulting +output packet(s), or for side effects such as updating Counter, +Meter or Register instances.

      +
    • +
    • +

      Use as few resources as possible, e.g. table search key bits, array +sizes, quantity of metadata associated with packets, etc.

      +
    • +
    +
    +
    +

    This document contains excerpts of several P416 programs that use +the psa.p4 include file and demonstrate features of PSA. Source code for the complete programs can be found in the official repository -containing the PSA specification1. -

    2. Naming conventions

    -

    In this document we use the following naming conventions: -

    -
      -
    • Types are named using CamelCase followed by _t. For example, PortId_t. +containing the PSA specification{PSAExamplePrograms}.

      +
    +
    +
    +
    +

    2. Naming conventions

    +
    +
    +

    In this document we use the following naming conventions:

    +
    +
    +
      +
    • +

      Types are named using CamelCase followed by _t. For example, PortId_t.

    • -
    • Control types and extern object types are named using CamelCase. For -example IngressParser. +
    • +

      Control types and extern object types are named using CamelCase. For +example IngressParser.

    • -
    • Struct types are named using lower case words separated by _ -followed by _t. For example psa_ingress_input_metadata_t. +
    • +

      Struct types are named using lower case words separated by _ +followed by _t. For example psa_ingress_input_metadata_t.

    • -
    • Actions, extern methods, extern functions, headers, structs, and +
    • +

      Actions, extern methods, extern functions, headers, structs, and instances of controls and externs start with lower case and words -are separated using _. For example send_to_port. +are separated using _. For example send_to_port.

    • -
    • Enum members, const definitions, and #define constants are all -caps, with words separated by _. For example PSA_PORT_CPU. -
    - -

    Architecture specific metadata (e.g. structs) are prefixed by psa_. -

    3. Packet paths

    -

    Figure 2 shows all possible paths for packets that +

  • +

    Enum members, const definitions, and #define constants are all +caps, with words separated by _. For example PSA_PORT_CPU.

    +
  • + +
    +
    +

    Architecture specific metadata (e.g. structs) are prefixed by psa_.

    +
    +
    +
    +
    +

    3. Packet paths

    +
    +
    +

    Figure 2 shows all possible paths for packets that must be supported by a PSA implementation. An implementation is -allowed to support paths for packets that are not described here. -

    -
    -

    packet-paths -

    -
    - -
    Figure 2. Packet Paths in PSA
    -

    Table 1 defines the meanings of the -abbreviations in Figure 2. There can be one or more -hardware, software, or PSA architecture components between the “packet -source” and “packet destination” given in that table, e.g. a normal +allowed to support paths for packets that are not described here.

    +
    +
    +
    +psa packet paths figure +
    +
    Figure 2. Packet Paths in PSA
    +
    +
    +

    Table 1 defines the meanings of the +abbreviations in Figure 2. There can be one or more +hardware, software, or PSA architecture components between the "packet +source" and "packet destination" given in that table, e.g. a normal multicast packet passes through the packet replication engine and typically also a packet buffer after leaving the ingress deparser, before arriving at the egress parser. This table focuses on the P4-programmable portions of the architecture as sources and -destinations of these packet paths. -

    -
    - - - - - - - - - - - - - - - - - - - - - - - - -
    Abbreviation Description Packet Source Packet Destination
    NFP normal packet from port port ingress parser
    NFCPU packet from CPU port CPU port ingress parser
    NU normal unicast packet ingress deparser egress parser
    from ingress to egress
    NM normal multicast-replicated ingress deparser, egress parser (more than
    packet from ingress to egress with help from PRE one copy is possible)
    NTP normal packet to port egress deparser port
    NTCPU normal packet to CPU port egress deparser CPU port
    RESUB resubmitted packet ingress deparser ingress parser
    CI2E clone from ingress to egress ingress deparser egress parser
    RECIRC recirculated packet egress deparser ingress parser
    CE2E clone from egress to egress egress deparser egress parser
    -
    - -
    Table 1. Packet path abbreviation meanings.
    -

    Table 2 shows what can +destinations of these packet paths.

    +
    + + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Table 1. Packet path abbreviation meanings
    AbbreviationDescriptionPacket SourcePacket Destination

    NFP

    normal packet from port

    port

    ingress parser

    NFCPU

    packet from CPU port

    CPU port

    ingress parser

    NU

    normal unicast packet from ingress to egress

    ingress deparser

    egress parser

    NM

    normal multicast-replicated packet from ingress to egress

    ingress deparser, with help from PRE

    egress parser (more than one copy is possible)

    NTP

    normal packet to port

    egress deparser

    port

    NTCPU

    normal packet to CPU port

    egress deparser

    CPU port

    RESUB

    resubmitted packet

    ingress deparser

    ingress parser

    CI2E

    clone from ingress to egress

    ingress deparser

    egress parser

    RECIRC

    recirculated packet

    egress deparser

    ingress parser

    CE2E

    clone from egress to egress

    egress deparser

    egress parser

    +
    +

    Table {results-of-one-pkt-thru-ingress-or-egress} shows what can happen to a packet as a result of a single time being processed in ingress, or a single time being processed in egress. The cases are -the same as shown in Table 1, but have been -grouped together by similar values of “Processed next by”. -

    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Processed Resulting
    Abbreviation Description next by packet(s)
    NFP normal packet from port At most one CI2E packet,
    NFCPU packet from CPU port ingress plus at most one of a
    RESUB resubmitted packet RESUB, NU, or NM packet.
    RECIRC recirculated packet See section 6.2 for details.
    NU normal unicast packet At most one CE2E packet,
    from ingress to egress plus at most one of a
    NM normal multicast-replicated egress RECIRC, NTP, or NTCPU
    packet from ingress to egress packet.
    CI2E clone from ingress to egress See section 6.5 for details.
    CE2E clone from egress to egress
    NTP normal packet to port device at other determined by the
    end of port other device
    NTCPU normal packet to CPU port CPU determined by CPU
    -
    - -
    Table 2. Result of packet processed one time by ingress or egress.
    -

    There are metadata fields defined by PSA that enable your P4 program +the same as shown in Table 1, but have been +grouped together by similar values of "Processed next by".

    +
    + + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Table 2. Result of packet processed one time by ingress or egress
    AbbreviationDescriptionaProcessed next byResulting packet(s)

    NFP

    normal packet from port

    ingress

    At most one CI2E packet, plus at most one of a RESUB, NU, or NM packet. See Section 7.3 for details.

    NFCPU

    packet from CPU port

    RESUB

    resubmitted packet

    RECIRC

    recirculated packet

    NU

    normal unicast packet from ingress to egress

    egress

    At most one CE2E packet, plus at most one of a RECIRC, NTP, or NTCPU packet. See Section 7.6 for details.

    NM

    normal multicast-replicated packet from ingress to egress

    CI2E

    clone from ingress to egress

    CE2E

    clone from egress to egress

    NTP

    normal packet to port

    device at other end of port

    determined by the other device

    NTCPU

    normal packet to CPU port

    CPU

    determined by CPU

    +
    +

    There are metadata fields defined by PSA that enable your P4 program to identify which path each packet arrived on, and to control where it -will go next. See section 6. -

    -

    For egress packets, the choice between one of multiple egress ports, -the port to the CPU, or the “recirculation port”, is made by the +will go next. See Chapter 7.

    +
    +
    +

    For egress packets, the choice between one of multiple egress ports, +the port to the CPU, or the "recirculation port", is made by the immediately previous processing step (ingress for NU, NM, or CI2E packets, egress for CE2E packets). Egress processing can choose to drop the packet instead of sending it to the port chosen earlier, but @@ -632,845 +1156,1031 @@ The only exception to ingress choosing the output port is for egress-to-egress clone packets, whose destination port is chosen when the clone is created in the immediately preceding egress processing -step. See section -D.2 for why this -restriction exists. -

    -

    A single packet received by a PSA system from a port can result in 0, +step. See +Section D.2 for why this +restriction exists.

    +
    +
    +

    A single packet received by a PSA system from a port can result in 0, 1, or many packets going out, all under control of the P4 program. For example, a single packet received from a port could cause all of -the following to occur, if the P4 program so directed it: -

    -
      -
    • The original packet received as NFP from port 2. Ingress processing +the following to occur, if the P4 program so directed it:

      +
    +
    +
      +
    • +

      The original packet received as NFP from port 2. Ingress processing creates a CI2E clone destined for the CPU port (copy 1), and a multicast NM packet to multicast group 18, which is configured in the PacketReplicationEngine to have copies made to ports 5 (copy 2) -and the recirculate port PSA_PORT_RECIRCULATE (copy 3). +and the recirculate port PSA_PORT_RECIRCULATE (copy 3).

    • -
    • Copy 1 performs egress processing, which sends the packet on path -NTCPU to the CPU port. +
    • +

      Copy 1 performs egress processing, which sends the packet on path +NTCPU to the CPU port.

    • -
    • Copy 2 performs egress processing, which creates a CE2E clone -destined for port 8 (copy 4), and sends a NTP packet to port 5. +
    • +

      Copy 2 performs egress processing, which creates a CE2E clone +destined for port 8 (copy 4), and sends a NTP packet to port 5.

    • -
    • Copy 3 performs egress processing, which does a RECIRC back to -ingress (copy 5). +
    • +

      Copy 3 performs egress processing, which does a RECIRC back to +ingress (copy 5).

    • -
    • Copy 4 performs egress processing, which sends a NTP packet to port -8. +
    • +

      Copy 4 performs egress processing, which sends a NTP packet to port +8.

    • -
    • Copy 5 performs ingress processing, which sends a NU packet destined -for port 1 (copy 6). +
    • +

      Copy 5 performs ingress processing, which sends a NU packet destined +for port 1 (copy 6).

    • -
    • Copy 6 performs egress processing, which drops the packet instead of -sending it to port 1. -
    - -

    This is simply an example of what is possible given an appropriately +

  • +

    Copy 6 performs egress processing, which drops the packet instead of +sending it to port 1.

    +
  • + +
    +
    +

    This is simply an example of what is possible given an appropriately written P4 program. There is no need to use all of the packet paths available. The numbering of the packet copies above is only for purposes of distinctly identifying each one in the example. The ports described in the example are also arbitrary. A PSA implementation is free to perform the steps above in many possible -orders. -

    -

    There is no mandated mechanism in PSA to prevent a single received +orders.

    +
    +
    +

    There is no mandated mechanism in PSA to prevent a single received packet from creating packets that continue to recirculate, resubmit, or clone from egress to egress indefinitely. This can be prevented by suitable testing of your P4 program, and/or creating in your P4 -program a “time to live” metadata field that is carried along with -copies of a packet, similar to the IPv4 Time To Live header field. -

    -

    A PSA implementation may optionally drop resubmitted, recirculated, or +program a "time to live" metadata field that is carried along with +copies of a packet, similar to the IPv4 Time To Live header field.

    +
    +
    +

    A PSA implementation may optionally drop resubmitted, recirculated, or egress-to-egress clone packets after an implementation-specific maximum number of times from the same original packet. If so, the implementation should maintain counters of packets dropped for these reasons, and preferably record some debug information about the first -few packets dropped for these reasons (perhaps only one). -

    4. PSA Data types

    4.1. PSA type definitions

    -

    Each PSA implementation will have specific bit widths for the +few packets dropped for these reasons (perhaps only one).

    +
    +
    +
    +
    +

    4. PSA Data types

    +
    +
    +

    4.1. PSA type definitions

    +
    +

    Each PSA implementation will have specific bit widths for the following types in the data plane. These widths are defined in the -target specific psa.p4 include file. They are expected to differ -from one PSA implementation to another2. -

    -

    For each of these types, the P4Runtime API6 may use bit +target specific psa.p4 include file. They are expected to differ +from one PSA implementation to another[1].

    +
    +
    +

    For each of these types, the P4Runtime API[2] may use bit widths independent of the targets. These widths are defined by the P4Runtime API specification, and they are expected to be at least as -large as the corresponding InHeader_t type below, such that they +large as the corresponding InHeader_t type below, such that they hold a value for any target. All PSA implementations must use data plane sizes for these types no wider than the corresponding -InHeader_t-defined types. -

    -
    -
    -
    /* These are defined using `typedef`, not `type`, so they are truly
    - * just different names for the type bit<W> for the particular width W
    - * shown.  Unlike the `type` definitions below, values declared with
    - * the `typedef` type names can be freely mingled in expressions, just
    - * as any value declared with type bit<W> can.  Values declared with
    - * one of the `type` names below _cannot_ be so freely mingled, unless
    - * you first cast them to the corresponding `typedef` type.  While
    - * that may be inconvenient when you need to do arithmetic on such
    - * values, it is the price to pay for having all occurrences of values
    - * of the `type` types marked as such in the automatically generated
    - * control plane API.
    - *
    - * Note that the width of typedef <name>Uint_t will always be the same
    - * as the width of type <name>_t. */
    -typedef bit<unspecified> PortIdUint_t;
    -typedef bit<unspecified> MulticastGroupUint_t;
    -typedef bit<unspecified> CloneSessionIdUint_t;
    -typedef bit<unspecified> ClassOfServiceUint_t;
    -typedef bit<unspecified> PacketLengthUint_t;
    -typedef bit<unspecified> EgressInstanceUint_t;
    -typedef bit<unspecified> TimestampUint_t;
    -
    -@p4runtime_translation("p4.org/psa/v1/PortId_t", 32)
    -type PortIdUint_t         PortId_t;
    -@p4runtime_translation("p4.org/psa/v1/MulticastGroup_t", 32)
    -type MulticastGroupUint_t MulticastGroup_t;
    -@p4runtime_translation("p4.org/psa/v1/CloneSessionId_t", 16)
    -type CloneSessionIdUint_t CloneSessionId_t;
    -@p4runtime_translation("p4.org/psa/v1/ClassOfService_t", 8)
    -type ClassOfServiceUint_t ClassOfService_t;
    -@p4runtime_translation("p4.org/psa/v1/PacketLength_t", 16)
    -type PacketLengthUint_t   PacketLength_t;
    -@p4runtime_translation("p4.org/psa/v1/EgressInstance_t", 16)
    -type EgressInstanceUint_t EgressInstance_t;
    -@p4runtime_translation("p4.org/psa/v1/Timestamp_t", 64)
    -type TimestampUint_t      Timestamp_t;
    -typedef error   ParserError_t;
    -
    -const PortId_t PSA_PORT_RECIRCULATE = (PortId_t) unspecified;
    -const PortId_t PSA_PORT_CPU = (PortId_t) unspecified;
    -
    -const CloneSessionId_t PSA_CLONE_SESSION_TO_CPU = (CloneSessiontId_t) unspecified;
    -
    -/* Note: All of the types with `InHeader` in their name are intended
    - * only to carry values of the corresponding types in packet headers
    - * between a PSA device and the P4Runtime Server software that manages
    - * it.
    - *
    - * The bit widths here are _independent_ of any particular PSA target
    - * device, and should _not_ be customized for each target.
    - *
    - * The bit widths are intended to be at least as large as any PSA
    - * device will ever have for that type.  Thus these types may also be
    - * useful to define packet headers that are sent directly between a
    - * PSA device and other devices, without going through P4Runtime
    - * Server software (e.g. this could be useful for sending packets to a
    - * controller or data collection system using higher packet rates than
    - * the P4Runtime Server can handle).  If used for this purpose, there
    - * is no requirement that the PSA data plane _automatically_ perform
    - * the numerical translation of these types that would occur if the
    - * header went through the P4Runtime Server.  Any such desired
    - * translation is up to the author of the P4 program to perform with
    - * explicit code.
    - *
    - * All widths must be a multiple of 8, so that any subset of these
    - * fields may be used in a single P4 header definition, even on P4
    - * implementations that restrict headers to contain fields with a
    - * total length that is a multiple of 8 bits. */
    -
    -/* See the comments near the definition of PortIdUint_t for why these
    - * typedef definitions exist. */
    -typedef bit<32> PortIdInHeaderUint_t;
    -typedef bit<32> MulticastGroupInHeaderUint_t;
    -typedef bit<16> CloneSessionIdInHeaderUint_t;
    -typedef bit<8>  ClassOfServiceInHeaderUint_t;
    -typedef bit<16> PacketLengthInHeaderUint_t;
    -typedef bit<16> EgressInstanceInHeaderUint_t;
    -typedef bit<64> TimestampInHeaderUint_t;
    -
    -@p4runtime_translation("p4.org/psa/v1/PortIdInHeader_t", 32)
    -type  PortIdInHeaderUint_t         PortIdInHeader_t;
    -@p4runtime_translation("p4.org/psa/v1/MulticastGroupInHeader_t", 32)
    -type  MulticastGroupInHeaderUint_t MulticastGroupInHeader_t;
    -@p4runtime_translation("p4.org/psa/v1/CloneSessionIdInHeader_t", 16)
    -type  CloneSessionIdInHeaderUint_t CloneSessionIdInHeader_t;
    -@p4runtime_translation("p4.org/psa/v1/ClassOfServiceInHeader_t", 8)
    -type  ClassOfServiceInHeaderUint_t ClassOfServiceInHeader_t;
    -@p4runtime_translation("p4.org/psa/v1/PacketLengthInHeader_t", 16)
    -type  PacketLengthInHeaderUint_t   PacketLengthInHeader_t;
    -@p4runtime_translation("p4.org/psa/v1/EgressInstanceInHeader_t", 16)
    -type  EgressInstanceInHeaderUint_t EgressInstanceInHeader_t;
    -@p4runtime_translation("p4.org/psa/v1/TimestampInHeader_t", 64)
    -type  TimestampInHeaderUint_t      TimestampInHeader_t;

    4.2. PSA supported metadata types

    -
    -
    -
    enum PSA_PacketPath_t {
    -    NORMAL,     /// Packet received by ingress that is none of the cases below.
    -    NORMAL_UNICAST,   /// Normal packet received by egress which is unicast
    -    NORMAL_MULTICAST, /// Normal packet received by egress which is multicast
    -    CLONE_I2E,  /// Packet created via a clone operation in ingress,
    -                /// destined for egress
    -    CLONE_E2E,  /// Packet created via a clone operation in egress,
    -                /// destined for egress
    -    RESUBMIT,   /// Packet arrival is the result of a resubmit operation
    -    RECIRCULATE /// Packet arrival is the result of a recirculate operation
    -}
    -
    -struct psa_ingress_parser_input_metadata_t {
    -  PortId_t                 ingress_port;
    -  PSA_PacketPath_t         packet_path;
    -}
    -
    -struct psa_egress_parser_input_metadata_t {
    -  PortId_t                 egress_port;
    -  PSA_PacketPath_t         packet_path;
    -}
    -
    -struct psa_ingress_input_metadata_t {
    -  // All of these values are initialized by the architecture before
    -  // the Ingress control block begins executing.
    -  PortId_t                 ingress_port;
    -  PSA_PacketPath_t         packet_path;
    -  Timestamp_t              ingress_timestamp;
    -  ParserError_t            parser_error;
    -}
    -
    -struct psa_ingress_output_metadata_t {
    -  // The comment after each field specifies its initial value when the
    -  // Ingress control block begins executing.
    -  ClassOfService_t         class_of_service; // 0
    -  bool                     clone;            // false
    -  CloneSessionId_t         clone_session_id; // initial value is undefined
    -  bool                     drop;             // true
    -  bool                     resubmit;         // false
    -  MulticastGroup_t         multicast_group;  // 0
    -  PortId_t                 egress_port;      // initial value is undefined
    -}
    -
    -struct psa_egress_input_metadata_t {
    -  ClassOfService_t         class_of_service;
    -  PortId_t                 egress_port;
    -  PSA_PacketPath_t         packet_path;
    -  EgressInstance_t         instance;       /// instance comes from the PacketReplicationEngine
    -  Timestamp_t              egress_timestamp;
    -  ParserError_t            parser_error;
    -}
    -
    -/// This struct is an 'in' parameter to the egress deparser.  It
    -/// includes enough data for the egress deparser to distinguish
    -/// whether the packet should be recirculated or not.
    -struct psa_egress_deparser_input_metadata_t {
    -  PortId_t                 egress_port;
    -}
    -
    -struct psa_egress_output_metadata_t {
    -  // The comment after each field specifies its initial value when the
    -  // Egress control block begins executing.
    -  bool                     clone;         // false
    -  CloneSessionId_t         clone_session_id; // initial value is undefined
    -  bool                     drop;          // false
    -}
    -

    4.3. Match kinds

    -

    PSA supports the following additional match_kind types, over and -above the three defined in the P416 language specification: -

    -
    -
    -
    match_kind {
    -    range,    /// Used to represent min..max intervals
    -    selector, /// Used for dynamic action selection via the ActionSelector extern
    -    optional  /// Either an exact match, or a wildcard matching any value for the entire field
    -}
    -

    selector is only supported for tables with an action selector implementation. -See Section 7.12 for details. -

    4.3.1. Range tables

    -

    If a table has at least one range field, then a single search key could match +InHeader_t-defined types.

    +
    +
    +
    +
    /* These are defined using `typedef`, not `type`, so they are truly
    + * just different names for the type bit<W> for the particular width W
    + * shown.  Unlike the `type` definitions below, values declared with
    + * the `typedef` type names can be freely mingled in expressions, just
    + * as any value declared with type bit<W> can.  Values declared with
    + * one of the `type` names below _cannot_ be so freely mingled, unless
    + * you first cast them to the corresponding `typedef` type.  While
    + * that may be inconvenient when you need to do arithmetic on such
    + * values, it is the price to pay for having all occurrences of values
    + * of the `type` types marked as such in the automatically generated
    + * control plane API.
    + *
    + * Note that the width of typedef <name>Uint_t will always be the same
    + * as the width of type <name>_t. */
    +typedef bit<unspecified> PortIdUint_t;
    +typedef bit<unspecified> MulticastGroupUint_t;
    +typedef bit<unspecified> CloneSessionIdUint_t;
    +typedef bit<unspecified> ClassOfServiceUint_t;
    +typedef bit<unspecified> PacketLengthUint_t;
    +typedef bit<unspecified> EgressInstanceUint_t;
    +typedef bit<unspecified> TimestampUint_t;
    +
    +@p4runtime_translation("p4.org/psa/v1/PortId_t", 32)
    +type PortIdUint_t         PortId_t;
    +@p4runtime_translation("p4.org/psa/v1/MulticastGroup_t", 32)
    +type MulticastGroupUint_t MulticastGroup_t;
    +@p4runtime_translation("p4.org/psa/v1/CloneSessionId_t", 16)
    +type CloneSessionIdUint_t CloneSessionId_t;
    +@p4runtime_translation("p4.org/psa/v1/ClassOfService_t", 8)
    +type ClassOfServiceUint_t ClassOfService_t;
    +@p4runtime_translation("p4.org/psa/v1/PacketLength_t", 16)
    +type PacketLengthUint_t   PacketLength_t;
    +@p4runtime_translation("p4.org/psa/v1/EgressInstance_t", 16)
    +type EgressInstanceUint_t EgressInstance_t;
    +@p4runtime_translation("p4.org/psa/v1/Timestamp_t", 64)
    +type TimestampUint_t      Timestamp_t;
    +typedef error   ParserError_t;
    +
    +const PortId_t PSA_PORT_RECIRCULATE = (PortId_t) unspecified;
    +const PortId_t PSA_PORT_CPU = (PortId_t) unspecified;
    +
    +const CloneSessionId_t PSA_CLONE_SESSION_TO_CPU = (CloneSessiontId_t) unspecified;
    +
    +/* Note: All of the types with `InHeader` in their name are intended
    + * only to carry values of the corresponding types in packet headers
    + * between a PSA device and the P4Runtime Server software that manages
    + * it.
    + *
    + * The bit widths here are _independent_ of any particular PSA target
    + * device, and should _not_ be customized for each target.
    + *
    + * The bit widths are intended to be at least as large as any PSA
    + * device will ever have for that type.  Thus these types may also be
    + * useful to define packet headers that are sent directly between a
    + * PSA device and other devices, without going through P4Runtime
    + * Server software (e.g. this could be useful for sending packets to a
    + * controller or data collection system using higher packet rates than
    + * the P4Runtime Server can handle).  If used for this purpose, there
    + * is no requirement that the PSA data plane _automatically_ perform
    + * the numerical translation of these types that would occur if the
    + * header went through the P4Runtime Server.  Any such desired
    + * translation is up to the author of the P4 program to perform with
    + * explicit code.
    + *
    + * All widths must be a multiple of 8, so that any subset of these
    + * fields may be used in a single P4 header definition, even on P4
    + * implementations that restrict headers to contain fields with a
    + * total length that is a multiple of 8 bits. */
    +
    +/* See the comments near the definition of PortIdUint_t for why these
    + * typedef definitions exist. */
    +typedef bit<32> PortIdInHeaderUint_t;
    +typedef bit<32> MulticastGroupInHeaderUint_t;
    +typedef bit<16> CloneSessionIdInHeaderUint_t;
    +typedef bit<8>  ClassOfServiceInHeaderUint_t;
    +typedef bit<16> PacketLengthInHeaderUint_t;
    +typedef bit<16> EgressInstanceInHeaderUint_t;
    +typedef bit<64> TimestampInHeaderUint_t;
    +
    +@p4runtime_translation("p4.org/psa/v1/PortIdInHeader_t", 32)
    +type  PortIdInHeaderUint_t         PortIdInHeader_t;
    +@p4runtime_translation("p4.org/psa/v1/MulticastGroupInHeader_t", 32)
    +type  MulticastGroupInHeaderUint_t MulticastGroupInHeader_t;
    +@p4runtime_translation("p4.org/psa/v1/CloneSessionIdInHeader_t", 16)
    +type  CloneSessionIdInHeaderUint_t CloneSessionIdInHeader_t;
    +@p4runtime_translation("p4.org/psa/v1/ClassOfServiceInHeader_t", 8)
    +type  ClassOfServiceInHeaderUint_t ClassOfServiceInHeader_t;
    +@p4runtime_translation("p4.org/psa/v1/PacketLengthInHeader_t", 16)
    +type  PacketLengthInHeaderUint_t   PacketLengthInHeader_t;
    +@p4runtime_translation("p4.org/psa/v1/EgressInstanceInHeader_t", 16)
    +type  EgressInstanceInHeaderUint_t EgressInstanceInHeader_t;
    +@p4runtime_translation("p4.org/psa/v1/TimestampInHeader_t", 64)
    +type  TimestampInHeaderUint_t      TimestampInHeader_t;
    +
    +
    +
    +
    +

    4.2. PSA supported metadata types

    +
    +
    +
    enum PSA_PacketPath_t {
    +    NORMAL,     /// Packet received by ingress that is none of the cases below.
    +    NORMAL_UNICAST,   /// Normal packet received by egress which is unicast
    +    NORMAL_MULTICAST, /// Normal packet received by egress which is multicast
    +    CLONE_I2E,  /// Packet created via a clone operation in ingress,
    +                /// destined for egress
    +    CLONE_E2E,  /// Packet created via a clone operation in egress,
    +                /// destined for egress
    +    RESUBMIT,   /// Packet arrival is the result of a resubmit operation
    +    RECIRCULATE /// Packet arrival is the result of a recirculate operation
    +}
    +
    +struct psa_ingress_parser_input_metadata_t {
    +  PortId_t                 ingress_port;
    +  PSA_PacketPath_t         packet_path;
    +}
    +
    +struct psa_egress_parser_input_metadata_t {
    +  PortId_t                 egress_port;
    +  PSA_PacketPath_t         packet_path;
    +}
    +
    +struct psa_ingress_input_metadata_t {
    +  // All of these values are initialized by the architecture before
    +  // the Ingress control block begins executing.
    +  PortId_t                 ingress_port;
    +  PSA_PacketPath_t         packet_path;
    +  Timestamp_t              ingress_timestamp;
    +  ParserError_t            parser_error;
    +}
    +struct psa_ingress_output_metadata_t {
    +  // The comment after each field specifies its initial value when the
    +  // Ingress control block begins executing.
    +  ClassOfService_t         class_of_service; // 0
    +  bool                     clone;            // false
    +  CloneSessionId_t         clone_session_id; // initial value is undefined
    +  bool                     drop;             // true
    +  bool                     resubmit;         // false
    +  MulticastGroup_t         multicast_group;  // 0
    +  PortId_t                 egress_port;      // initial value is undefined
    +}
    +struct psa_egress_input_metadata_t {
    +  ClassOfService_t         class_of_service;
    +  PortId_t                 egress_port;
    +  PSA_PacketPath_t         packet_path;
    +  EgressInstance_t         instance;       /// instance comes from the PacketReplicationEngine
    +  Timestamp_t              egress_timestamp;
    +  ParserError_t            parser_error;
    +}
    +
    +/// This struct is an 'in' parameter to the egress deparser.  It
    +/// includes enough data for the egress deparser to distinguish
    +/// whether the packet should be recirculated or not.
    +struct psa_egress_deparser_input_metadata_t {
    +  PortId_t                 egress_port;
    +}
    +struct psa_egress_output_metadata_t {
    +  // The comment after each field specifies its initial value when the
    +  // Egress control block begins executing.
    +  bool                     clone;         // false
    +  CloneSessionId_t         clone_session_id; // initial value is undefined
    +  bool                     drop;          // false
    +}
    +
    +
    +
    +
    +

    4.3. Match kinds

    +
    +

    PSA supports the following additional match_kind types, over and +above the three defined in the P416 language specification:

    +
    +
    +
    +
    match_kind {
    +    range,    /// Used to represent min..max intervals
    +    selector, /// Used for dynamic action selection via the ActionSelector extern
    +    optional  /// Either an exact match, or a wildcard matching any value for the entire field
    +}
    +
    +
    +
    +

    selector is only supported for tables with an action selector implementation. +See Section 8.12 for details.

    +
    +
    +

    4.3.1. Range tables

    +
    +

    If a table has at least one range field, then a single search key could match multiple table entries. Every entry must be assigned a numeric priority by the control plane software when it is installed. If multiple installed table entries match the same search key, one among them with the maximum numeric priority will -“win”, and its action performed. If there are multiple matching table entries +"win", and its action performed. If there are multiple matching table entries with the same maximum numeric priority, it is implementation-specific which one will have its action performed. Control plane software should assign different priority values to table entries that can match the same packet if they wish to -avoid this implementation-specific behavior. -

    -

    The winner is one with maximum numeric priority value if you use the P4Runtime +avoid this implementation-specific behavior.

    +
    +
    +

    The winner is one with maximum numeric priority value if you use the P4Runtime API to specify the numeric priorities. Check the documentation of your control plane API if you use a different one, as some APIs might choose to use the -convention that smaller numeric priority values win over larger ones. -

    -

    A range table may have one or more lpm fields. If so, the prefix length is +convention that smaller numeric priority values win over larger ones.

    +
    +
    +

    A range table may have one or more lpm fields. If so, the prefix length is used to determine whether a search key matches the entry, but the prefix length -does not determine the relative priority among multiple matching table +does not determine the relative priority among multiple matching table entries. Only the numeric priority supplied by the control plane software -determines that. -

    -

    If a range table has entries defined via a const entries table property, then +determines that.

    +
    +
    +

    If a range table has entries defined via a const entries table property, then the relative priority of the entries are highest priority first, to lowest -priority last, based upon the order they appear in the P4 program. -

    4.3.2. Ternary tables

    -

    If a table has no range field, but at least one ternary or optional field, -then as for tables with range fields, a single search key can be matched by +priority last, based upon the order they appear in the P4 program.

    +
    +
    +
    +

    4.3.2. Ternary tables

    +
    +

    If a table has no range field, but at least one ternary or optional field, +then as for tables with range fields, a single search key can be matched by multiple table entries, and thus every entry must have a numeric priority -assigned by the control plane software. The same note about lpm fields in the +assigned by the control plane software. The same note about lpm fields in the previous section applies here, as well as the note about entries specified via -const entries. -

    4.3.3. Longest prefix match tables

    -

    If a table has no range, ternary, nor optional fields, but at least one -lpm field, there must be exactly one lpm field. There may be 0 or more -exact fields in addition to the lpm field. While there can be multiple +const entries.

    +
    +
    +
    +

    4.3.3. Longest prefix match tables

    +
    +

    If a table has no range, ternary, nor optional fields, but at least one +lpm field, there must be exactly one lpm field. There may be 0 or more +exact fields in addition to the lpm field. While there can be multiple installed table entries that match a single search key, there can be at most one -matching table entry of each possible prefix length of the lpm field (because +matching table entry of each possible prefix length of the lpm field (because no two table entries installed at the same time are allowed to have the same search key). The matching entry with the longest prefix length is always the winner. The control plane cannot specify a priority when installing entries for -such a table it is always determined by the prefix length. -

    -

    If a longest prefix match table has entries defined via a const entries table +such a table — it is always determined by the prefix length.

    +
    +
    +

    If a longest prefix match table has entries defined via a const entries table property, then the relative priority of the entries are determined by the prefix -lengths, not by the order they appear in the P4 program. -

    4.3.4. Exact match tables

    -

    If a table has only exact fields, then for any search key, there can be at +lengths, not by the order they appear in the P4 program.

    +
    +
    +
    +

    4.3.4. Exact match tables

    +
    +

    If a table has only exact fields, then for any search key, there can be at most one matching table entry, because duplicate search keys are not allowed to -be installed. Thus no numeric priority is ever needed to determine the “winning” -matching table entry. -

    -

    If an exact match table has entries defined via a const entries table +be installed. Thus no numeric priority is ever needed to determine the "winning" +matching table entry.

    +
    +
    +

    If an exact match table has entries defined via a const entries table property, there can be at most one matching entry for any search key, so the relative order that entries appear in the P4 program is unimportant in -determining which entry will win. -

    4.4. Data plane vs. control plane data representations

    -

    A PSA data plane may support multiple control plane APIs. Some of the +determining which entry will win.

    +
    +
    +
    +
    +
    +
    +

    5. Data plane vs. control plane data representations

    +
    +
    +

    A PSA data plane may support multiple control plane APIs. Some of the notes in this section apply specifically to the case of the P4Runtime -API6 when used to control a PSA device. If you are using +API[2] when used to control a PSA device. If you are using a different control plane API to control a PSA device, you should consult the documentation for that control plane API to learn exactly -what API it provides to configure your PSA device. -

    -

    A PSA data plane implementation that supports the P4Runtime -API6 includes software called a “P4Runtime Server” that +what API it provides to configure your PSA device.

    +
    +
    +

    A PSA data plane implementation that supports the P4Runtime +API[2] includes software called a "P4Runtime Server" that enables runtime programming of the PSA device from one or more -“P4Runtime Clients”. For brevity, here we will call a P4Runtime Server -an “agent”, and a P4Runtime Client a “controller”. A controller may -control multiple devices with different PSA implementations. -

    -

    As mentioned in section 4.1, different PSA +"P4Runtime Clients". For brevity, here we will call a P4Runtime Server +an "agent", and a P4Runtime Client a "controller". A controller may +control multiple devices with different PSA implementations.

    +
    +
    +

    As mentioned in Section 4.1, different PSA implementations are expected to customize the size of the data types that refer directly to those objects in the data plane, i.e. ports, -multicast group ids, etc. -

    -

    Some PSA implementations are expected to use noticeably fewer +multicast group ids, etc.

    +
    +
    +

    Some PSA implementations are expected to use noticeably fewer resources for things like table keys and action parameters if the data plane stores only the fewest number of bits required for values of these types, for that implementation. For example, an implementation -that defines PortId_t as bit<6> instead of bit<16>, and can take +that defines PortId_t as bit<6> instead of bit<16>, and can take advantage of this difference, could save 10 Mbits of storage in a -table with a million entries3. -

    -

    The P4Runtime API uses quantities with bit widths independent +table with a million entries[3].

    +
    +
    +

    The P4Runtime API uses quantities with bit widths independent of the target device to hold values of the types -listed in section 4.1, to simplify the +listed in Section 4.1, to simplify the manipulation of these values in the controller and agent software. For control plane operations on tables, any trimming or padding of values will be performed in the agent (usually trimming in the direction from controller to device, and padding in the direction from device -to controller). -

    -

    There are multiple channels of communication over which such values +to controller).

    +
    +
    +

    There are multiple channels of communication over which such values might be carried between the controller and the data plane. These -channels of communication include: -

    -
      -
    • Control plane operations on tables, where values of these types may -be included as part of the key, or as an action parameter. +channels of communication include:

      +
    +
    +
      +
    • +

      Control plane operations on tables, where values of these types may +be included as part of the key, or as an action parameter.

    • -
    • Control plane operations on parser value sets, where values of these -types may be included as part of the key. +
    • +

      Control plane operations on parser value sets, where values of these +types may be included as part of the key.

    • -
    • Packets sent to the CPU (“packet in” from the controller's -perspective), or received from the CPU (“packet out” from the -controller's perspective). +
    • +

      Packets sent to the CPU ("packet in" from the controller’s +perspective), or received from the CPU ("packet out" from the +controller’s perspective).

    • -
    • Fields in a Digest extern notification message (section -7.14). +
    • +

      Fields in a Digest extern notification message +(Section 8.14).

    • -
    • Fields in the data contents of a Register array (section -7.9). -
    - -

    Note: There may be other channels not listed above. -

    -

    For packets between the control plane and the PSA device, there is the +

  • +

    Fields in the data contents of a Register array +(Section 8.9).

    +
  • + +
    +
    + + + + + +
    +
    Note
    +
    +There may be other channels not listed above. +
    +
    +
    +

    For packets between the control plane and the PSA device, there is the issue that many PSA implementations are expected to restrict P4 programs to have headers with fields with a total length that is a multiple of 8 bits. To make it possible to define P4 header types that meet this restriction, and contain values of fields with these PSA-specific types, and be source-compatible across multiple PSA -implementations, additional types are defined that contain InHeader in -their name. For example, PortIdInHeader_t is the same as PortId_t, +implementations, additional types are defined that contain InHeader in +their name. For example, PortIdInHeader_t is the same as PortId_t, except it must be a multiple of 8 bits long, and contain at least as -many bits as PortId_t does. -

    -

    Because these InHeader types are guaranteed to be a multiple of 8 bits long, +many bits as PortId_t does.

    +
    +
    +

    Because these InHeader types are guaranteed to be a multiple of 8 bits long, you may include any combination of them in a P4 header type definition, as long as the other fields in the header satisfy the multiple of 8 bits restriction. The controller or P4 program generating packets with such headers should fill in any most -significant “padding” bits with 0. You may do this with a normal +significant "padding" bits with 0. You may do this with a normal assignment statement in your P4 program, where the value on the -right hand side is cast to the wider InHeader type. Similarly, -casting a value of a wider type such as PortIdInHeader_t to the -corresponding narrower type PortId_t truncates the excess most -significant bits as part of the cast. -

    -

    Values of type PortId_t have an unusual property in PSA +right hand side is cast to the wider InHeader type. Similarly, +casting a value of a wider type such as PortIdInHeader_t to the +corresponding narrower type PortId_t truncates the excess most +significant bits as part of the cast.

    +
    +
    +

    Values of type PortId_t have an unusual property in PSA implementations. Because it can make some hardware implementations more straightforward, the numerical values of fields with type -PortId_t in the P4 data plane might not be a simple range of values +PortId_t in the P4 data plane might not be a simple range of values such as 0 through 31, as one might choose when writing control plane software for a 32-port device. The agent is expected to implement numerical translation between controller port id values and data plane port id values, for each of the channels of communication between the -controller and data plane described above. -

    -

    The file psa.p4 contains an annotation p4runtime_translation on -the type definition of types PortId_t and PortIdInHeader_t. +controller and data plane described above.

    +
    +
    +

    The file psa.p4 contains an annotation p4runtime_translation on +the type definition of types PortId_t and PortIdInHeader_t. This enables the compiler to mark all uses of values of these types that are accessible from the P4Runtime API, so the agent software knows that it must translate them, and what kind of translation to perform. The benefit is that you do not need to put any special markings on your uses of values of these types throughout your P4 -program. -

    -

    The cost of this approach is: if you want to do arithmetic on values -of these types, you must explicitly cast them to a bit<W> -type. The psa.p4 include file defines PortIdUint_t as a typedef -with exactly the same width in bits as type PortId_t, so you can -cast values of type PortId_t to type PortIdUint_t, and then you +program.

    +
    +
    +

    The cost of this approach is: if you want to do arithmetic on values +of these types, you must explicitly cast them to a bit<W> +type. The psa.p4 include file defines PortIdUint_t as a typedef +with exactly the same width in bits as type PortId_t, so you can +cast values of type PortId_t to type PortIdUint_t, and then you can perform all P4 arithmetic operations on the value. The result -must be explicitly cast back to type PortId_t if you wish to assign +must be explicitly cast back to type PortId_t if you wish to assign it to a metadata field with that type. Corresponding types with -Uint in their name are defined for all PSA types. -

    -

    Because of this translation, we recommend that values of type -PortId_t be treated similarly to values of an enum type. -Comparing two values of this type for equality or “not equal to” is +Uint in their name are defined for all PSA types.

    +
    +
    +

    Because of this translation, we recommend that values of type +PortId_t be treated similarly to values of an enum type. +Comparing two values of this type for equality or "not equal to" is reasonable, as well as assigning the values to other variables or parameters of the same type, but nearly any other operations are prone -to error. When matching values of type PortId_t as part of a table +to error. When matching values of type PortId_t as part of a table key, always match a complete value exactly, or wildcard every bit of -the value (i.e. a ternary match kind with all bits wildcard, or an -lpm match kind with prefix length 0). If you attempt to do any of -the following things on a value with type PortId_t or -PortIdInHeader_t, the numerical translation performed may lead to -functional errors in your program: -

    -
      -
    • Do a table key match on a subset of the bits, or a range match. -
    • -
    • Compare port values with relational operators like < or >. -
    • -
    • Compare port values to specific numeric literal values like 0 or +the value (i.e. a ternary match kind with all bits wildcard, or an +lpm match kind with prefix length 0). If you attempt to do any of +the following things on a value with type PortId_t or +PortIdInHeader_t, the numerical translation performed may lead to +functional errors in your program:

      +
    +
    +
      +
    • +

      Do a table key match on a subset of the bits, or a range match.

      +
    • +
    • +

      Compare port values with relational operators like < or >.

      +
    • +
    • +

      Compare port values to specific numeric literal values like 0 or 0xff. It is recommended instead to compare their values by using them as table key fields, or parser value set key fields, against values installed by the control plane (which have been translated to the corresponding device-specific value as determined by the -device's agent software). It is also reasonable to compare port +device’s agent software). It is also reasonable to compare port values for equality against the symbolic constant values -PSA_PORT_CPU or PSA_PORT_RECIRCULATE, which have target-specific -numeric values. +PSA_PORT_CPU or PSA_PORT_RECIRCULATE, which have target-specific +numeric values.

    • -
    • Perform arithmetic on the value, and expect to get a value that +
    • +

      Perform arithmetic on the value, and expect to get a value that corresponds to another port of the device. Some numerical values may not correspond to any port of the device, and the values -corresponding to ports need not be consecutive. -

    - -

    The list above is not intended to be exhaustive. -

    -

    All of the comments above apply to all types where numerical +corresponding to ports need not be consecutive.

    + + +
    +
    +

    The list above is not intended to be exhaustive.

    +
    +
    +

    All of the comments above apply to all types where numerical translation occurs between the controller and the data plane. Below is a complete list of numeric types planned for numerical translation -by default in PSA: -

    -
      -
    • PortId_t or PortIdInHeader_t -
    • -
    • ClassOfService_t or ClassOfServiceInHeader_t -
    - -

    For the types listed below, no numerical translation occurs by -default4. A PSA +by default in PSA:

    +
    +
    +
      +
    • +

      PortId_t or PortIdInHeader_t

      +
    • +
    • +

      ClassOfService_t or ClassOfServiceInHeader_t

      +
    • +
    +
    +
    +

    For the types listed below, no numerical translation occurs by +default[4]. A PSA data plane must support all numerical values from 0 up to the maximum -value that it supports. Except for Timestamp_t values, the number +value that it supports. Except for Timestamp_t values, the number of values supported by the data plane need not be a power of 2. -Controllers must have a way to discover each PSA device's maximum -supported value for each of these types. -

    -
      -
    • MulticastGroup_t - 0 is a special value that indicates no +Controllers must have a way to discover each PSA device’s maximum +supported value for each of these types.

      +
    +
    +
      +
    • +

      MulticastGroup_t - 0 is a special value that indicates no multicast replication is to be performed for a packet, so this type is an exception to the rule above that 0 must be supported in the -data plane. +data plane.

    • -
    • CloneSessionId_t +
    • +

      CloneSessionId_t

    • -
    • PacketLength_t +
    • +

      PacketLength_t

    • -
    • EgressInstance_t +
    • +

      EgressInstance_t

    • -
    • Timestamp_t -
    - -
    -
    -

    TBD: Values of type Timestamp_t are being considered for numerical -translation in the agent software, between target-specific values, and -a value with a common unit and 0 value reference across all targets.

    -

    Note that all of these types have a p4runtime_translation annotation -in the psa.p4 include file. This is to ensure that when the +

  • +

    Timestamp_t

    +
  • + +
    +
    + + + + + +
    +
    Note
    +
    +TBD: Values of type Timestamp_t are being considered for numerical translation in the agent software, between target-specific values, and a value with a common unit and 0 value reference across all targets. +
    +
    +
    +

    Note that all of these types have a p4runtime_translation annotation +in the psa.p4 include file. This is to ensure that when the compiler generates a P4Runtime P4Info file from source programs, it will include in the P4Info file the type specified by the -p4runtime_translation rather than the target-specific bit width. +p4runtime_translation rather than the target-specific bit width. For the same P4 source program, the P4Info file contents are intended -to be identical for all targets. -

    -

    If the type bitwidth specified as the second parameter to the -p4runtime_translation is different from the device-specific bitwidth +to be identical for all targets.

    +
    +
    +

    If the type bitwidth specified as the second parameter to the +p4runtime_translation is different from the device-specific bitwidth (of the underlying type), we expect the P4Runtime server to perform the appropriate casting. Additionally, more advanced numerical translation can be enabled at runtime for any type annotated with -p4runtime_translation, although arbitrary numerical translation is -only mandated for PortId_t, ClassOfService_t, and their Inheader +p4runtime_translation, although arbitrary numerical translation is +only mandated for PortId_t, ClassOfService_t, and their Inheader variants. To request arbitrary numerical translation for a give type, the P4Runtime system will expect the URI (first parameter to the -p4runtime_translation) and the desired mapping. -

    5. Programmable blocks

    -

    The following declarations provide a template for the programmable +p4runtime_translation) and the desired mapping.

    +
    +
    +
    +
    +

    6. Programmable blocks

    +
    +
    +

    The following declarations provide a template for the programmable blocks in the PSA. The P4 programmer is responsible for implementing controls that match these interfaces and instantiate -them in a package definition. -

    -

    It uses the same user-defined metadata type IM and header type IH +them in a package definition.

    +
    +
    +

    It uses the same user-defined metadata type IM and header type IH for all ingress parsers and control blocks. The egress parser and control blocks can use the same types for those things, or different -types, as the P4 program author wishes. -

    -
    -
    -
    parser IngressParser<H, M, RESUBM, RECIRCM>(
    -    packet_in buffer,
    -    out H parsed_hdr,
    -    inout M user_meta,
    -    in psa_ingress_parser_input_metadata_t istd,
    -    in RESUBM resubmit_meta,
    -    in RECIRCM recirculate_meta);
    -
    -control Ingress<H, M>(
    -    inout H hdr, inout M user_meta,
    -    in    psa_ingress_input_metadata_t  istd,
    -    inout psa_ingress_output_metadata_t ostd);
    -
    -control IngressDeparser<H, M, CI2EM, RESUBM, NM>(
    -    packet_out buffer,
    -    out CI2EM clone_i2e_meta,
    -    out RESUBM resubmit_meta,
    -    out NM normal_meta,
    -    inout H hdr,
    -    in M meta,
    -    in psa_ingress_output_metadata_t istd);
    -
    -parser EgressParser<H, M, NM, CI2EM, CE2EM>(
    -    packet_in buffer,
    -    out H parsed_hdr,
    -    inout M user_meta,
    -    in psa_egress_parser_input_metadata_t istd,
    -    in NM normal_meta,
    -    in CI2EM clone_i2e_meta,
    -    in CE2EM clone_e2e_meta);
    -
    -control Egress<H, M>(
    -    inout H hdr, inout M user_meta,
    -    in    psa_egress_input_metadata_t  istd,
    -    inout psa_egress_output_metadata_t ostd);
    -
    -control EgressDeparser<H, M, CE2EM, RECIRCM>(
    -    packet_out buffer,
    -    out CE2EM clone_e2e_meta,
    -    out RECIRCM recirculate_meta,
    -    inout H hdr,
    -    in M meta,
    -    in psa_egress_output_metadata_t istd,
    -    in psa_egress_deparser_input_metadata_t edstd);
    -
    -package IngressPipeline<IH, IM, NM, CI2EM, RESUBM, RECIRCM>(
    -    IngressParser<IH, IM, RESUBM, RECIRCM> ip,
    -    Ingress<IH, IM> ig,
    -    IngressDeparser<IH, IM, CI2EM, RESUBM, NM> id);
    -
    -package EgressPipeline<EH, EM, NM, CI2EM, CE2EM, RECIRCM>(
    -    EgressParser<EH, EM, NM, CI2EM, CE2EM> ep,
    -    Egress<EH, EM> eg,
    -    EgressDeparser<EH, EM, CE2EM, RECIRCM> ed);
    -
    -package PSA_Switch<IH, IM, EH, EM, NM, CI2EM, CE2EM, RESUBM, RECIRCM> (
    -    IngressPipeline<IH, IM, NM, CI2EM, RESUBM, RECIRCM> ingress,
    -    PacketReplicationEngine pre,
    -    EgressPipeline<EH, EM, NM, CI2EM, CE2EM, RECIRCM> egress,
    -    BufferingQueueingEngine bqe);
    -

    6. Packet Path Details

    -

    Refer to section 3 for the packet paths provided by PSA, -and their abbreviated names, used often in this section. -

    6.1. Initial values of packets processed by ingress

    -

    Table 3 describes the initial values of the -packet contents and metadata when a packet begins ingress processing. -

    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NFP NFCPU RESUB RECIRC
    packet_in see text
    user_meta see text
    IngressParser istd fields (type psa_ingress_parser_input_metadata_t)
    ingress_port PortId_t value of PSA_PORT_ copied from PSA_PORT_
    packet's input port CPU resub'd packet RECIRCULATE
    packet_path NORMAL NORMAL RESUBMIT RECIRCULATE
    Ingress istd fields (type psa_ingress_input_metadata_t)
    ingress_port Same value as received by IngressParser above.
    packet_path Same value as received by IngressParser above.
    ingress_timestamp Time that packet began processing in IngressParser.
    For RESUB or RECIRC packets, the time the ‘copy’
    began IngressParser, not the original.
    parser_error From IngressParser. Always error.NoError if there
    was no parser error.
    -
    - -
    Table 3. Initial values for packets processed by ingress.
    -

    Note that the ingress_port value for a resubmitted packet could be -PSA_PORT_RECIRCULATE if a packet was recirculated, and then that -recirculated packet was resubmitted. -

    6.1.1. Initial packet contents for packets from ports

    -

    For Ethernet ports, packet_in for FP and NFCPU path packets contains +types, as the P4 program author wishes.

    +
    +
    +
    +
    parser IngressParser<H, M, RESUBM, RECIRCM>(
    +    packet_in buffer,
    +    out H parsed_hdr,
    +    inout M user_meta,
    +    in psa_ingress_parser_input_metadata_t istd,
    +    in RESUBM resubmit_meta,
    +    in RECIRCM recirculate_meta);
    +
    +control Ingress<H, M>(
    +    inout H hdr, inout M user_meta,
    +    in    psa_ingress_input_metadata_t  istd,
    +    inout psa_ingress_output_metadata_t ostd);
    +
    +control IngressDeparser<H, M, CI2EM, RESUBM, NM>(
    +    packet_out buffer,
    +    out CI2EM clone_i2e_meta,
    +    out RESUBM resubmit_meta,
    +    out NM normal_meta,
    +    inout H hdr,
    +    in M meta,
    +    in psa_ingress_output_metadata_t istd);
    +
    +parser EgressParser<H, M, NM, CI2EM, CE2EM>(
    +    packet_in buffer,
    +    out H parsed_hdr,
    +    inout M user_meta,
    +    in psa_egress_parser_input_metadata_t istd,
    +    in NM normal_meta,
    +    in CI2EM clone_i2e_meta,
    +    in CE2EM clone_e2e_meta);
    +
    +control Egress<H, M>(
    +    inout H hdr, inout M user_meta,
    +    in    psa_egress_input_metadata_t  istd,
    +    inout psa_egress_output_metadata_t ostd);
    +
    +control EgressDeparser<H, M, CE2EM, RECIRCM>(
    +    packet_out buffer,
    +    out CE2EM clone_e2e_meta,
    +    out RECIRCM recirculate_meta,
    +    inout H hdr,
    +    in M meta,
    +    in psa_egress_output_metadata_t istd,
    +    in psa_egress_deparser_input_metadata_t edstd);
    +
    +package IngressPipeline<IH, IM, NM, CI2EM, RESUBM, RECIRCM>(
    +    IngressParser<IH, IM, RESUBM, RECIRCM> ip,
    +    Ingress<IH, IM> ig,
    +    IngressDeparser<IH, IM, CI2EM, RESUBM, NM> id);
    +
    +package EgressPipeline<EH, EM, NM, CI2EM, CE2EM, RECIRCM>(
    +    EgressParser<EH, EM, NM, CI2EM, CE2EM> ep,
    +    Egress<EH, EM> eg,
    +    EgressDeparser<EH, EM, CE2EM, RECIRCM> ed);
    +
    +package PSA_Switch<IH, IM, EH, EM, NM, CI2EM, CE2EM, RESUBM, RECIRCM> (
    +    IngressPipeline<IH, IM, NM, CI2EM, RESUBM, RECIRCM> ingress,
    +    PacketReplicationEngine pre,
    +    EgressPipeline<EH, EM, NM, CI2EM, CE2EM, RECIRCM> egress,
    +    BufferingQueueingEngine bqe);
    +
    +
    +
    +
    +
    +

    7. Packet Path Details

    +
    +
    +

    Refer to section {packet-paths} for the packet paths provided by PSA, +and their abbreviated names, used often in this section.

    +
    +
    +

    7.1. Initial values of packets processed by ingress

    +
    +

    Table [#ingress-initial-values] describes the initial values of the +packet contents and metadata when a packet begins ingress processing.

    +
    + + +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Table 3. Initial values for packets processed by ingress
    NFPNFCPURESUBRECIRC

    packet_in

    see text

    user_meta

    see text

    IngressParser istd fields (type psa_ingress_parser_input_metadata_t)

    ingress_port

    PortId_t value of packets' input port

    PSA_PORT_CPU

    copied from resub’d packet

    PSA_PORT_RECIRCULATE

    packet_path

    NORMAL

    NORMAL

    RESUBMIT

    RECIRCULATE

    Ingress istd fields (type psa_ingress_input_metadata_t)

    ingress_port

    Same value as received by IngressParser above.

    packet_path

    Same value as received by IngressParser above.

    ingress_timestamp

    Time that packet began processing in IngressParser. For RESUB or RECIRC packets, the time the 'copy' began IngressParser, not the original.

    parser_error

    From IngressParser. Always error.NoError if there was no parser error.

    +
    +

    Note that the ingress_port value for a resubmitted packet could be +PSA_PORT_RECIRCULATE if a packet was recirculated, and then that +recirculated packet was resubmitted.

    +
    +
    +
    +

    7.2. Initial packet contents for packets from ports

    +
    +

    For Ethernet ports, packet_in for FP and NFCPU path packets contains the Ethernet frame starting with the Ethernet header. It does not -include the Ethernet frame CRC. -

    -
    -
    -

    TBD: Whether the payload is always the -minimum of 46 bytes (64 byte minimum Ethernet frame size, minus 14 -bytes of header, minus 4 bytes of CRC), or whether an implementation -is allowed to leave some of those bytes out.}

    -

    The PSA does not put further restrictions on packet_in.length() -as defined in the P416 spec. Targets that do not support it, should provide mechanisms -to raise an error. -

    -

    The P4Runtime has a “Packet Out” capability to send a packet from the +include the Ethernet frame CRC.

    +
    +
    + + + + + +
    +
    Note
    +
    +TBD Whether the payload is always the minimum of 46 bytes (64 byte minimum Ethernet frame size, minus 14 bytes of header, minus 4 bytes of CRC), or whether an implementation is allowed to leave some of those bytes out. +
    +
    +
    +

    The PSA does not put further restrictions on packet_in.length() +as defined in the P416 spec. Targets that do not support it, should provide mechanisms +to raise an error.

    +
    +
    +

    The P4Runtime has a "Packet Out" capability to send a packet from the controller to a PSA device. Such packets are sent into the PSA device as NFCPU path packets. There is no metadata associated with such packets, only the contents of the packet that are parsed normally by -the P4 program's IngressParser code. There may be some translation of -header field values, as described in Section -4.4. -

    6.1.2. Initial packet contents for resubmitted packets

    -

    For RESUB packets, packet_in is the same as the pre-IngressParser -contents of packet_in, for the packet that caused this resubmitted -packet to occur (i.e. with NO modifications from any ingress processing). -

    6.1.3. Initial packet contents for recirculated packets

    -

    For RECIRC packets, packet_in is created by starting with the +the P4 program’s IngressParser code. There may be some translation of +header field values, as described in +Chapter 5.

    +
    +
    +

    7.2.1. Initial packet contents for resubmitted packets

    +
    +

    For RESUB packets, packet_in is the same as the pre-IngressParser +contents of packet_in, for the packet that caused this resubmitted +packet to occur (i.e. with NO modifications from any ingress processing).

    +
    +
    +
    +

    7.2.2. Initial packet contents for recirculated packets

    +
    +

    For RECIRC packets, packet_in is created by starting with the headers emitted by the egress deparser of the egress packet that was recirculated, followed by the payload of that packet, i.e. the part -that was not parsed by the egress parser. -

    6.1.4. User-defined metadata for all ingress packets

    -

    The PSA architecture does not mandate initialization of user-defined +that was not parsed by the egress parser.

    +
    +
    +
    +

    7.2.3. User-defined metadata for all ingress packets

    +
    +

    The PSA architecture does not mandate initialization of user-defined metadata to known values as given as input to the ingress parser. If -a user's P4 program explicitly initializes all user-defined metadata -early on (e.g. in the parser's start state), then that will flow -through the rest of the parser into the Ingress control block as one +a user’s P4 program explicitly initializes all user-defined metadata +early on (e.g. in the parser’s start state), then that will flow +through the rest of the parser into the Ingress control block as one might normally expect. This will be left as an option to the user in -their P4 programs, not required behavior for all P4 programs. -

    -

    There are two direction in parameters to the ingress parser with -user-defined types, named resubmit_meta and recirculate_meta. +their P4 programs, not required behavior for all P4 programs.

    +
    +
    +

    There are two direction in parameters to the ingress parser with +user-defined types, named resubmit_meta and recirculate_meta. They may be used to carry metadata for resubmitted and recirculated -packets. -

    -

    Consider a packet that arrives at the ingress pipeline, and during +packets.

    +
    +
    +

    Consider a packet that arrives at the ingress pipeline, and during ingress processing the P4 program assigns values to fields of the PSA -standard metadata such that the packet is resubmitted (see Section -6.2 for details on how to do so). In the ingress -deparser, the P4 program assigns a value to the out parameter named -resubmit_meta. This value (which can be a collection of many +standard metadata such that the packet is resubmitted (see +Section 7.3 for details on how to do so). In the ingress +deparser, the P4 program assigns a value to the out parameter named +resubmit_meta. This value (which can be a collection of many individual values in fields, sub-structs, headers, etc.) becomes associated with the resubmitted packet by the PSA implementation, and when the resubmitted packet begins ingress parsing, that becomes the -value of the in parameter named resubmit_meta to the ingress -parser. -

    -

    For resubmitted packets, the value of the in parameter named -recirculate_meta is undefined. -

    -

    Conversely, for recirculated packets, the value of the in parameter -named recirculate_meta contains whatever value was assigned to the -egress deparser out parameter named recirculate_meta when the -packet was recirculated. The value of the in parameter -resubmit_meta is undefined for recirculated packets. -

    -

    For packets from a port, including the CPU port, both of the in -parameters resubmit_meta and recirculate_meta are undefined. -

    6.2. Behavior of packets after ingress processing is complete

    -

    The pseudocode below defines where copies of packets will be made -after the Ingress control block has completed executing, based upon +value of the in parameter named resubmit_meta to the ingress +parser.

    +
    +
    +

    For resubmitted packets, the value of the in parameter named +recirculate_meta is undefined.

    +
    +
    +

    Conversely, for recirculated packets, the value of the in parameter +named recirculate_meta contains whatever value was assigned to the +egress deparser out parameter named recirculate_meta when the +packet was recirculated. The value of the in parameter +resubmit_meta is undefined for recirculated packets.

    +
    +
    +

    For packets from a port, including the CPU port, both of the in +parameters resubmit_meta and recirculate_meta are undefined.

    +
    +
    +
    +
    +

    7.3. Behavior of packets after ingress processing is complete

    +
    +

    The pseudocode below defines where copies of packets will be made +after the Ingress control block has completed executing, based upon the contents of several metadata fields in the struct -psa_ingress_output_metadata_t. -

    -

    The function platform_port_valid() mentioned below takes a value of -type PortId_t, returning true only when the value represents an +psa_ingress_output_metadata_t.

    +
    +
    +

    The function platform_port_valid() mentioned below takes a value of +type PortId_t, returning true only when the value represents an output port for the implementation. It is expected that for some PSA implementations there will be bit patterns for a value of type -PortId_t that do not correspond to any port. This function returns -true for both PSA_PORT_CPU and PSA_PORT_RECIRCULATE. platform_port_valid +PortId_t that do not correspond to any port. This function returns +true for both PSA_PORT_CPU and PSA_PORT_RECIRCULATE. platform_port_valid is not defined in PSA for calling from the P4 data-plane program, since there is no known use case for calling it at packet processing time. It is intended for describing the behavior in pseudocode. The -control plane is expected to configure tables with valid port numbers. -

    -

    A comment saying “recommended to log error” is not a requirement, but +control plane is expected to configure tables with valid port numbers.

    +
    +
    +

    A comment saying "recommended to log error" is not a requirement, but a recommendation, that a PSA implementation should maintain a counter that counts the number of times this error occurs. It would also be useful if the implementation recorded details about the first few times this error occurred, e.g. a FIFO queue of the first several -invalid values of ostd.egress_port that cause an error to occur, +invalid values of ostd.egress_port that cause an error to occur, perhaps with other information about the packet that caused it, with tail dropping if it fills up. Control plane or driver software would be able to read these counters, and read and drain the FIFO queues to -assist P4 developers in debugging their code. -

    -
    -
    -
    struct psa_ingress_output_metadata_t {
    -  // The comment after each field specifies its initial value when the
    -  // Ingress control block begins executing.
    -  ClassOfService_t         class_of_service; // 0
    -  bool                     clone;            // false
    -  CloneSessionId_t         clone_session_id; // initial value is undefined
    -  bool                     drop;             // true
    -  bool                     resubmit;         // false
    -  MulticastGroup_t         multicast_group;  // 0
    -  PortId_t                 egress_port;      // initial value is undefined
    -}
    -

    First we give an outline of behavior, for quick reference of the +assist P4 developers in debugging their code.

    +
    +
    +
    +
    struct psa_ingress_output_metadata_t {
    +  // The comment after each field specifies its initial value when the
    +  // Ingress control block begins executing.
    +  ClassOfService_t         class_of_service; // 0
    +  bool                     clone;            // false
    +  CloneSessionId_t         clone_session_id; // initial value is undefined
    +  bool                     drop;             // true
    +  bool                     resubmit;         // false
    +  MulticastGroup_t         multicast_group;  // 0
    +  PortId_t                 egress_port;      // initial value is undefined
    +}
    +
    +
    +
    +

    First we give an outline of behavior, for quick reference of the relative priority of the possible actions. This outline is only for -reader convenience it is not the specification for the behavior. -

    -
    -
    -
        psa_ingress_output_metadata_t ostd;
    -
    -    if (ostd.clone) {
    -        create ingress to egress clone(s), with options as configured by
    -            the PRE clone session numbered ostd.clone_session_id;
    -    } else { no clone; }
    -
    -    if (ostd.drop) { drop packet; }
    -    else if (ostd.resubmit) { resubmit packet; }
    -    else if (ostd.multicast_group != 0) { PRE multicast replicates packet; }
    -    else { PRE sends one copy of packet to ostd.egress_port; }
    -

    The pseudocode below defines the behavior a PSA implementation must -follow. -

    -
    -
    -
        psa_ingress_output_metadata_t ostd;
    -
    -    if (ostd.clone) {
    -        if (ostd.clone_session_id value is supported) {
    -            from the values configured for ostd.clone_session_id in PRE {
    -                cos = class_of_service
    -                set((egress_port[0], instance[0]), ..., (egress_port[n], instance[n])) =
    +reader convenience — it is not the specification for the behavior.

    +
    +
    +
    +
        psa_ingress_output_metadata_t ostd;
    +
    +    if (ostd.clone) {
    +        create ingress to egress clone(s), with options as configured by
    +            the PRE clone session numbered ostd.clone_session_id;
    +    } else { no clone; }
    +
    +    if (ostd.drop) { drop packet; }
    +    else if (ostd.resubmit) { resubmit packet; }
    +    else if (ostd.multicast_group != 0) { PRE multicast replicates packet; }
    +    else { PRE sends one copy of packet to ostd.egress_port; }
    +
    +
    +
    +

    The pseudocode below defines the behavior a PSA implementation must +follow.

    +
    +
    +
    +
        psa_ingress_output_metadata_t ostd;
    +
    +    if (ostd.clone) {
    +        if (ostd.clone_session_id value is supported) {
    +            from the values configured for ostd.clone_session_id in PRE {
    +                cos = class_of_service
    +                set((egress_port[0], instance[0]), ..., (egress_port[n], instance[n])) =
                         set of egress_port and instance pairs
    -                trunc = truncate
    -                plen = packet_length_bytes
    -            }
    -            if (cos value is not supported) {
    -                cos = 0;
    -                // Recommended to log error about unsupported cos value.
    -            }
    -            for each pair (egress_port, instance) in the set {
    +                trunc = truncate
    +                plen = packet_length_bytes
    +            }
    +            if (cos value is not supported) {
    +                cos = 0;
    +                // Recommended to log error about unsupported cos value.
    +            }
    +            for each pair (egress_port, instance) in the set {
                     Create a clone of the packet and send it to the packet
    -                buffer with the egress_port, instance, and
    -                class_of_service cos, after which it will start egress
    -                processing.  It will contain at most the first plen
    -                bytes of the packet as received at the ingress parser
    -                if trunc is true, otherwise the entire packet.
    -            }
    -        } else {
    -            // Do not create a clone.  Recommended to log error about
    -            // unsupported ostd.clone_session_id value.
    -        }
    -    }
    -    // Continue below, regardless of whether a clone was created.
    -    // Any clone created above is unaffected by the code below.
    -    if (ostd.drop) {
    +                buffer with the egress_port, instance, and
    +                class_of_service cos, after which it will start egress
    +                processing.  It will contain at most the first plen
    +                bytes of the packet as received at the ingress parser
    +                if trunc is true, otherwise the entire packet.
    +            }
    +        } else {
    +            // Do not create a clone.  Recommended to log error about
    +            // unsupported ostd.clone_session_id value.
    +        }
    +    }
    +    // Continue below, regardless of whether a clone was created.
    +    // Any clone created above is unaffected by the code below.
    +    if (ostd.drop) {
             drop the packet
    -        return;   // Do not continue below.
    -    }
    -    if (ostd.class_of_service value is not supported) {
    -        ostd.class_of_service = 0;    // use default class 0 instead
    -        // Recommended to log error about unsupported
    -        // ostd.class_of_service value.
    -    }
    -    if (ostd.resubmit) {
    -        resubmit the packet, i.e. it will go back to starting with the
    -            ingress parser;
    -        return;   // Do not continue below.
    -    }
    -    if (ostd.multicast_group != 0) {
    -        Make 0 or more copies of the packet according to the control
    -            plane configuration of multicast group ostd.multicast_group.
    -            Every copy will have the same value of ostd.class_of_service
    -        return;   // Do not continue below.
    -    }
    -    if (platform_port_valid(ostd.egress_port)) {
    -        enqueue one packet for output port ostd.egress_port with class
    -            of service ostd.class_of_service
    -    } else {
    +        return;   // Do not continue below.
    +    }
    +    if (ostd.class_of_service value is not supported) {
    +        ostd.class_of_service = 0;    // use default class 0 instead
    +        // Recommended to log error about unsupported
    +        // ostd.class_of_service value.
    +    }
    +    if (ostd.resubmit) {
    +        resubmit the packet, i.e. it will go back to starting with the
    +            ingress parser;
    +        return;   // Do not continue below.
    +    }
    +    if (ostd.multicast_group != 0) {
    +        Make 0 or more copies of the packet according to the control
    +            plane configuration of multicast group ostd.multicast_group.
    +            Every copy will have the same value of ostd.class_of_service
    +        return;   // Do not continue below.
    +    }
    +    if (platform_port_valid(ostd.egress_port)) {
    +        enqueue one packet for output port ostd.egress_port with class
    +            of service ostd.class_of_service
    +    } else {
             drop the packet
    -        // Recommended to log error about unsupported ostd.egress_port value.
    -    }
    -

    Whenever the pseudocode above indicates that a packet should be sent + // Recommended to log error about unsupported ostd.egress_port value. + } +

    +
    +
    +

    Whenever the pseudocode above indicates that a packet should be sent on a particular packet path, a PSA implementation may under some circumstances instead drop the packet. For example, the packet buffer may be too low on available space for storing new packets, or some @@ -1479,354 +2189,470 @@ for dropping. It is recommended that an implementation maintain counters of packets dropped, preferably with separate counters for as many different reasons as the implementation has for dropping packets -outside the control of the P4 program. -

    -

    A PSA implementation may implement multiple classes of service for -packets sent to the packet buffer. If so, the Ingress control block -may choose to assign a value to the ostd.class_of_service field to -change the packet's class of service to a value other than the default -of 0. -

    -

    PSA only specifies how the Ingress control block can control the class +outside the control of the P4 program.

    +
    +
    +

    A PSA implementation may implement multiple classes of service for +packets sent to the packet buffer. If so, the Ingress control block +may choose to assign a value to the ostd.class_of_service field to +change the packet’s class of service to a value other than the default +of 0.

    +
    +
    +

    PSA only specifies how the Ingress control block can control the class of service of packets. PSA does not mandate a scheduling policy among queues that may exist in the packet buffer. Something at least as flexible as weighted fair queuing, with an optional strict high priority queue, is recommended for PSA implementations with separate -queues for each class of service. See appendix -F for more on packet ordering -recommendations in PSA devices. -

    -

    The P4Runtime API specification defines how a controller may discover +queues for each class of service. See +Appendix F for more on packet ordering +recommendations in PSA devices.

    +
    +
    +

    The P4Runtime API specification defines how a controller may discover the number of distinct class of service values that a PSA device -supports. -

    6.2.1. Multicast replication

    -

    The control plane may configure each multicast_group in the PRE to +supports.

    +
    +
    +

    7.3.1. Multicast replication

    +
    +

    The control plane may configure each multicast_group in the PRE to create the desired copies of packets sent to that group. Each group begins empty. Sending a packet to an empty group causes the packet to be dropped. The control plane may add one or more pairs of the form -(egress_port, instance) to a multicast group, and may also remove -pairs from a group that were added earlier. -

    -

    Suppose a multicast group contains the following set of pairs: -

    -
    -
    -
        (egress_port[0], instance[0]),
    -    (egress_port[1], instance[1]),
    -    ...,
    -    (egress_port[N-1], instance[N-1])
    -

    When a packet is sent to that group, N copies of the packet are -made. Copy number i that is sent to egress processing will have its -struct of type psa_egress_input_metadata_t filled in with the -field egress_port equal to egress_port[i], and the field -instance filled in with instance[i]. Note: A multicast group is a +(egress_port, instance) to a multicast group, and may also remove +pairs from a group that were added earlier.

    +
    +
    +

    Suppose a multicast group contains the following set of pairs:

    +
    +
    +
    +
        (egress_port[0], instance[0]),
    +    (egress_port[1], instance[1]),
    +    ...,
    +    (egress_port[N-1], instance[N-1])
    +
    +
    +
    +

    When a packet is sent to that group, N copies of the packet are +made. Copy number i that is sent to egress processing will have its +struct of type psa_egress_input_metadata_t filled in with the +field egress_port equal to egress_port[i], and the field +instance filled in with instance[i]. Note: A multicast group is a set of pairs, and it is not required that an implementation create -copies in an order that the control plane can enforce. See appendix -F for more on packet ordering -recommendations in PSA devices. -

    -

    Within a single multicast group, the pairs (egress_port, instance) +copies in an order that the control plane can enforce. See +Appendix F for more on packet ordering +recommendations in PSA devices.

    +
    +
    +

    Within a single multicast group, the pairs (egress_port, instance) must be different from each other, but it is allowed for any number of pairs within a multicast group to have the same value of -egress_port, or to have the same value of instance. The same pair -(egress_port, instance) can occur in any number of different -multicast groups. -

    -

    A PSA implementation need only support egress_port values that +egress_port, or to have the same value of instance. The same pair +(egress_port, instance) can occur in any number of different +multicast groups.

    +
    +
    +

    A PSA implementation need only support egress_port values that represent single ports of the PSA device. That is, it need not -implement support for egress_port values that represent an entire +implement support for egress_port values that represent an entire Link Aggregation Group (LAG) interface, which is a set of physical -ports over which load balancing of traffic is performed. -

    -

    A PSA device must support egress_port values in a multicast group -that are equal to PSA_PORT_CPU or PSA_PORT_RECIRCULATE. The copies of a +ports over which load balancing of traffic is performed.

    +
    +
    +

    A PSA device must support egress_port values in a multicast group +that are equal to PSA_PORT_CPU or PSA_PORT_RECIRCULATE. The copies of a multicast packet made to those ports will behave the same in egress as a unicast packets sent to the corresponding port, i.e. if not dropped, those copies will go the the CPU port, or be recirculated back to -ingress. -

    6.3. Actions for directing packets during ingress

    -

    All of these actions modify one or more metadata fields in the struct -with type psa_ingress_output_metadata_t that is an inout parameter -of the Ingress control block. None of these actions have any other +ingress.

    +
    +
    +
    +
    +

    7.4. Actions for directing packets during ingress

    +
    +

    All of these actions modify one or more metadata fields in the struct +with type psa_ingress_output_metadata_t that is an inout parameter +of the Ingress control block. None of these actions have any other immediate effect. What happens to the packet is determined by the value of all fields in that struct when ingress processing is -complete, not at the time one of these actions is called. See Section -6.2. -

    -

    These actions are provided for convenience in making changes to these +complete, not at the time one of these actions is called. See +Section 7.3.

    +
    +
    +

    These actions are provided for convenience in making changes to these metadata fields. Their effects are expected to be common kinds of changes one will want to make in a P4 program. If they do not suit your use cases, you may modify the metadata fields directly in your P4 -programs however you prefer, e.g. within actions you define. -

    6.3.1. Unicast operation

    -

    Sends packet to a port. See Table 4, column +programs however you prefer, e.g. within actions you define.

    +
    +
    +

    7.4.1. Unicast operation

    +
    +

    Sends packet to a port. See Table [#egress-initial-values], column NU, for how metadata fields are filled in when such a packet begins -egress processing. -

    -
    -
    -
    /// Modify ingress output metadata to cause one packet to be sent to
    -/// egress processing, and then to the output port egress_port.
    -/// (Egress processing may choose to drop the packet instead.)
    -
    -/// This action does not change whether a clone or resubmit operation
    -/// will occur.
    -
    -@noWarn("unused")
    -action send_to_port(inout psa_ingress_output_metadata_t meta,
    -                    in PortId_t egress_port)
    -{
    -    meta.drop = false;
    -    meta.multicast_group = (MulticastGroup_t) 0;
    -    meta.egress_port = egress_port;
    -}

    6.3.2. Multicast operation

    -

    Sends packet to a multicast group or a port. See Table -4, column NM, for how metadata fields are +egress processing.

    +
    +
    +
    +
    /// Modify ingress output metadata to cause one packet to be sent to
    +/// egress processing, and then to the output port egress_port.
    +/// (Egress processing may choose to drop the packet instead.)
    +
    +/// This action does not change whether a clone or resubmit operation
    +/// will occur.
    +
    +@noWarn("unused")
    +action send_to_port(inout psa_ingress_output_metadata_t meta,
    +                    in PortId_t egress_port)
    +{
    +    meta.drop = false;
    +    meta.multicast_group = (MulticastGroup_t) 0;
    +    meta.egress_port = egress_port;
    +}
    +
    +
    +
    +
    +

    7.4.2. Multicast operation

    +
    +

    Sends packet to a multicast group or a port. See Table +[#egress-initial-values], column NM, for how metadata fields are filled in when each multicast-replicated copy of such a packet begins -egress processing. -

    -

    The multicast_group parameter is the multicast group id. The +egress processing.

    +
    +
    +

    The multicast_group parameter is the multicast group id. The control plane must configure the multicast groups through a separate -mechanism such as the P4Runtime API. -

    -
    -
    -
    /// Modify ingress output metadata to cause 0 or more copies of the
    -/// packet to be sent to egress processing.
    -
    -/// This action does not change whether a clone or resubmit operation
    -/// will occur.
    -
    -@noWarn("unused")
    -action multicast(inout psa_ingress_output_metadata_t meta,
    -                 in MulticastGroup_t multicast_group)
    -{
    -    meta.drop = false;
    -    meta.multicast_group = multicast_group;
    -}

    6.3.3. Drop operation

    -

    Do not send a copy of the packet for normal egress processing. -

    -
    -
    -
    /// Modify ingress output metadata to cause no packet to be sent for
    -/// normal egress processing.
    -
    -/// This action does not change whether a clone will occur.  It will
    -/// prevent a packet from being resubmitted.
    -
    -@noWarn("unused")
    -action ingress_drop(inout psa_ingress_output_metadata_t meta)
    -{
    -    meta.drop = true;
    -}

    6.4. Initial values of packets processed by egress

    -

    Table 4 describes the initial values of the -packet contents and metadata when a packet begins egress processing. -

    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NU NM CI2E CE2E
    packet_in see text
    user_meta see text
    EgressParser istd fields (type psa_egress_parser_input_metadata_t)
    egress_port ostd.egress_port from PRE from PRE configuration
    of ingress packet configuration of clone session
    of multicast group
    packet_path NORMAL_ NORMAL_ CLONE_I2E CLONE_E2E
    UNICAST MULTICAST
    Egress istd fields (type psa_egress_input_metadata_t)
    class_of_service ostd.class_of_service from PRE configuration
    of ingress packet of clone session
    egress_port Same value as received by EgressParser above.
    packet_path Same value as received by EgressParser above.
    instance 0 from PRE from PRE configuration
    configuration of clone session
    of multicast group
    egress_timestamp Time that packet began processing in EgressParser. Filled in
    independently for each copy of a multicast-replicated packet.
    parser_error From EgressParser. Always error.NoError if there
    was no parser error. See “Multicast copies” section.
    -
    - -
    Table 4. Initial values for packets processed by egress.
    -

    Note that while some P4 architectures have a standard metadata field +mechanism such as the P4Runtime API.

    +
    +
    +
    +
    /// Modify ingress output metadata to cause 0 or more copies of the
    +/// packet to be sent to egress processing.
    +
    +/// This action does not change whether a clone or resubmit operation
    +/// will occur.
    +
    +@noWarn("unused")
    +action multicast(inout psa_ingress_output_metadata_t meta,
    +                 in MulticastGroup_t multicast_group)
    +{
    +    meta.drop = false;
    +    meta.multicast_group = multicast_group;
    +}
    +
    +
    +
    +
    +

    7.4.3. Drop operation

    +
    +

    Do not send a copy of the packet for normal egress processing.

    +
    +
    +
    +
    /// Modify ingress output metadata to cause no packet to be sent for
    +/// normal egress processing.
    +
    +/// This action does not change whether a clone will occur.  It will
    +/// prevent a packet from being resubmitted.
    +
    +@noWarn("unused")
    +action ingress_drop(inout psa_ingress_output_metadata_t meta)
    +{
    +    meta.drop = true;
    +}
    +
    +
    +
    +
    +
    +

    7.5. Initial values of packets processed by egress

    +
    +

    Table {egress-initial-values} describes the initial values of the +packet contents and metadata when a packet begins egress processing.

    +
    + + +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Table 4. Initial values for packets processed by egress
    NUNMCI2ECE2E

    packet_in

    see text

    user_meta

    see text

    EgressParser istd fields (type psa_egress_parser_input_metadata_t)

    egress_port

    ostd.egress_port of ingress packet

    from PRE configuration of multicast group

    from PRE configuration of clone session

    packet_path

    NORMAL_UNICAST

    NORMAL_MULTICAST

    CLONE_I2E

    CLONE_E2E

    Egress istd fields (type psa_egress_input_metadata_t)

    class_of_service

    ostd.class_of_service of ingress packet

    from PRE configuration of clone session

    egress_port

    Same value as received by EgressParser above.

    packet_path

    Same value as received by EgressParser above.

    instance

    0

    from PRE configuration of multicast group

    from PRE configuration of clone session

    egress_timestamp

    Time that packet began processing in EgressParser. Filled in independently for each copy of a multicast-replicated packet.

    parser_error

    From EgressParser. Always error.NoError if there was no parser error. See "Multicast copies" section.

    +
    +

    Note that while some P4 architectures have a standard metadata field that gives the length of the packet in bytes, there is no such field in PSA. If a target device supports cut-through -switching5 and this feature is enabled, it is +switching[5] and this feature is enabled, it is possible for egress processing to begin before the last byte of the packet has arrived on the input port. Thus it is not possible for the target device to determine the packet length yet. It is recommended that a P4 developer use fields available in some packet headers to determine the length of a packet, such as the Total Length field in -the IPv4 header, or the Payload length field in the IPv6 header. -

    6.4.1. Initial packet contents for normal packets

    -

    For NU and NM packets, packet_in comes from the ingress packet that +the IPv4 header, or the Payload length field in the IPv6 header.

    +
    +
    +

    7.5.1. Initial packet contents for normal packets

    +
    +

    For NU and NM packets, packet_in comes from the ingress packet that caused this packet to be sent to egress. It starts with the packet headers as emitted by the ingress deparser, followed by the payload of -that packet, i.e. the part that was not parsed by the ingress parser. -

    -

    Packets to be recirculated, i.e. those sent to port PSA_PORT_RECIRCULATE +that packet, i.e. the part that was not parsed by the ingress parser.

    +
    +
    +

    Packets to be recirculated, i.e. those sent to port PSA_PORT_RECIRCULATE via the normal unicast or multicast packet paths, fit into this category. They are not treated differently by a PSA implementation from normal unicast or multicast packets until they reach the egress -deparser. -

    6.4.2. Initial packet contents for packets cloned from ingress to egress

    -

    For CI2E packets, packet_in is from the ingress packet that caused +deparser.

    +
    +
    +
    +

    7.5.2. Initial packet contents for packets cloned from ingress to egress

    +
    +

    For CI2E packets, packet_in is from the ingress packet that caused this clone to be created. It is the same as the pre-IngressParser -contents of packet_in of that ingress packet, with no modifications -from any ingress processing. Truncation of the payload is supported. -

    -

    Packets cloned in ingress using a clone session configured with -egress_port equal to PSA_PORT_RECIRCULATE fit into this category. -

    6.4.3. Initial packet contents for packets cloned from egress to egress

    -

    For CE2E packets, packet_in is from the egress packet that caused this +contents of packet_in of that ingress packet, with no modifications +from any ingress processing. Truncation of the payload is supported.

    +
    +
    +

    Packets cloned in ingress using a clone session configured with +egress_port equal to PSA_PORT_RECIRCULATE fit into this category.

    +
    +
    +
    +

    7.5.3. Initial packet contents for packets cloned from egress to egress

    +
    +

    For CE2E packets, packet_in is from the egress packet that caused this clone to be created. It starts with the headers emitted by the egress deparser, followed by the payload of that packet, i.e. the part that was not parsed by the egress parser. Truncation of the payload is -supported. -

    -

    Packets cloned in egress using a clone session configured with -egress_port equal to PSA_PORT_RECIRCULATE fit into this category. -

    6.4.4. User-defined metadata for all egress packets

    -

    This is very similar to how metadata is initialized for ingress -packets. See Section 6.1.4. -

    -

    The primary differences for egress packets are the different packet -paths involved. There are three parameters with direction in for -the egress parser, named normal_meta, clone_i2e_meta, and -clone_e2e_meta. For every packet that begins egress processing, +supported.

    +
    +
    +

    Packets cloned in egress using a clone session configured with +egress_port equal to PSA_PORT_RECIRCULATE fit into this category.

    +
    +
    +
    +

    7.5.4. User-defined metadata for all egress packets

    +
    +

    This is very similar to how metadata is initialized for ingress +packets. See Section 7.2.3.

    +
    +
    +

    The primary differences for egress packets are the different packet +paths involved. There are three parameters with direction in for +the egress parser, named normal_meta, clone_i2e_meta, and +clone_e2e_meta. For every packet that begins egress processing, exactly one of those three has defined contents, and the other two -have undefined contents. -

    -

    For NU and NM packets, the parameter normal_meta is the only one +have undefined contents.

    +
    +
    +

    For NU and NM packets, the parameter normal_meta is the only one with defined contents. Its value is the one that was assigned to the -out parameter with the same name of the ingress deparser, when the -normal packet was completing its ingress processing. -

    -

    For CLONE_I2E packets, the parameter clone_i2e_meta is the only one +out parameter with the same name of the ingress deparser, when the +normal packet was completing its ingress processing.

    +
    +
    +

    For CLONE_I2E packets, the parameter clone_i2e_meta is the only one with defined contents. Its value is the one that was assigned to the -out parameter with the same name of the ingress deparser, when the -clone was created. -

    -

    For CLONE_E2E packets, the parameter clone_e2e_meta is the only one +out parameter with the same name of the ingress deparser, when the +clone was created.

    +
    +
    +

    For CLONE_E2E packets, the parameter clone_e2e_meta is the only one with defined contents. Its value is the one that was assigned to the -out parameter with the same name of the egress deparser, when the -clone was created. -

    6.4.5. Multicast and clone copies

    -

    The following fields may differ among copies of a multicast-replicated +out parameter with the same name of the egress deparser, when the +clone was created.

    +
    +
    +
    +

    7.5.5. Multicast and clone copies

    +
    +

    The following fields may differ among copies of a multicast-replicated packet that are processed in egress. Similarly for copies of a cloned packet when they are processed in egress. Both are referred to as -replicated packets in this section. -

    -
      -
    • egress_port - This field will typically differ among copies of a +replicated packets in this section.

      +
    +
    +
      +
    • +

      egress_port - This field will typically differ among copies of a replicated packet, but it may also be the same for arbitrary copies, as determined by the control plane configuration of the PacketReplicationEngine. It is expected that the control plane will configure the PacketReplicationEngine so that each copy of the same original packet is assigned a unique value of the pair -(egress_port, instance). +(egress_port, instance).

    • -
    • instance - See egress_port +
    • +

      instance - See egress_port

    • -
    • egress_timestamp - This value is filled in independently for each +
    • +

      egress_timestamp - This value is filled in independently for each copy of a replicated packet. Depending upon the quantity of traffic destined to each output port, the timestamp could vary -significantly between copies of the same original packet. +significantly between copies of the same original packet.

    • -
    • parser_error - In the common case, this will typically be the same +
    • +

      parser_error - In the common case, this will typically be the same for every copy of the same original replicated packet. However, it is determined by the EgressParser P4 code for each copy independently, so if that parsing behavior depends upon a field that -can differ among copies, e.g. egress_port, then parser_error -can differ among copies. -

    - -

    All contents of a packet and its associated metadata, other than those +can differ among copies, e.g. egress_port, then parser_error +can differ among copies.

    + + +
    +
    +

    All contents of a packet and its associated metadata, other than those mentioned above, will be the same for every copy of the same original -replicated packet. -

    6.5. Behavior of packets after egress processing is complete

    -

    The pseudocode below defines where copies of packets will be made -after the Egress control block has completed executing, based upon +replicated packet.

    +
    +
    +
    +
    +

    7.6. Behavior of packets after egress processing is complete

    +
    +

    The pseudocode below defines where copies of packets will be made +after the Egress control block has completed executing, based upon the contents of several metadata fields in the struct -psa_egress_output_metadata_t. -

    -
    -
    -
    struct psa_egress_output_metadata_t {
    -  // The comment after each field specifies its initial value when the
    -  // Egress control block begins executing.
    -  bool                     clone;         // false
    -  CloneSessionId_t         clone_session_id; // initial value is undefined
    -  bool                     drop;          // false
    -}
    -
    -    psa_egress_input_metadata_t  istd;
    -    psa_egress_output_metadata_t ostd;
    -
    -    if (ostd.clone) {
    -        if (ostd.clone_session_id value is supported) {
    -            from the values configured for ostd.clone_session_id in PRE {
    -                cos = class_of_service
    -                set((egress_port[0], instance[0]), ..., (egress_port[n], instance[n])) =
    -                    set of egress_port and instance pairs
    -                trunc = truncate
    -                plen = packet_length_bytes
    -            }
    -            if (cos value is not supported) {
    -                cos = 0;
    -                // Recommended to log error about unsupported cos
    -                // value.
    -            }
    -            for each pair (egress_port, instance) in the set {
    -                Create a clone of the packet and send it to the packet
    -                buffer with the egress_port, instance, and
    -                class_of_service cos, after which it will start egress
    -                processing.  It will contain at most the first plen
    -                bytes of the packet as sent out from the egress
    -                deparser if trunc is true, otherwise the entire
    -                packet.
    -            }
    -        } else {
    -            // Do not create a clone.  Recommended to log error about
    -            // unsupported ostd.clone_session_id value.
    -        }
    -    }
    -    // Continue below, regardless of whether a clone was created.
    -    // Any clone created above is unaffected by the code below.
    -    if (ostd.drop) {
    -        drop the packet
    -        return;   // Do not continue below.
    -    }
    -    // The value istd.egress_port below is the same one that the
    -    // packet began its egress processing with, as decided during
    -    // ingress processing for this packet (or as determined by the PRE
    -    // configuration of a clone session, for cloned packets,
    -    // regardless of whether the clone operation was done in ingress
    -    // or egress).  The egress code is not allowed to change it.
    -    if (istd.egress_port == PSA_PORT_RECIRCULATE) {
    -        recirculate the packet, i.e. it will go back to starting with the
    -            ingress parser;
    -        return;   // Do not continue below.
    -    }
    -    enqueue one packet for output port istd.egress_port
    -

    As for the handling of a packet after ingress processing, a PSA +psa_egress_output_metadata_t.

    +
    +
    +
    +
    struct psa_egress_output_metadata_t {
    +  // The comment after each field specifies its initial value when the
    +  // Egress control block begins executing.
    +  bool                     clone;         // false
    +  CloneSessionId_t         clone_session_id; // initial value is undefined
    +  bool                     drop;          // false
    +}
    +
    +psa_egress_input_metadata_t  istd;
    +psa_egress_output_metadata_t ostd;
    +
    +if (ostd.clone) {
    +    if (ostd.clone_session_id value is supported) {
    +        from the values configured for ostd.clone_session_id in PRE {
    +            cos = class_of_service
    +            set((egress_port[0], instance[0]), ..., (egress_port[n], instance[n])) =
    +                set of egress_port and instance pairs
    +            trunc = truncate
    +            plen = packet_length_bytes
    +        }
    +        if (cos value is not supported) {
    +            cos = 0;
    +            // Recommended to log error about unsupported cos
    +            // value.
    +        }
    +        for each pair (egress_port, instance) in the set {
    +            Create a clone of the packet and send it to the packet
    +            buffer with the egress_port, instance, and
    +            class_of_service cos, after which it will start egress
    +            processing.  It will contain at most the first plen
    +            bytes of the packet as sent out from the egress
    +            deparser if trunc is true, otherwise the entire
    +            packet.
    +        }
    +    } else {
    +        // Do not create a clone.  Recommended to log error about
    +        // unsupported ostd.clone_session_id value.
    +    }
    +}
    +// Continue below, regardless of whether a clone was created.
    +// Any clone created above is unaffected by the code below.
    +if (ostd.drop) {
    +    drop the packet
    +    return;   // Do not continue below.
    +}
    +// The value istd.egress_port below is the same one that the
    +// packet began its egress processing with, as decided during
    +// ingress processing for this packet (or as determined by the PRE
    +// configuration of a clone session, for cloned packets,
    +// regardless of whether the clone operation was done in ingress
    +// or egress).  The egress code is not allowed to change it.
    +if (istd.egress_port == PSA_PORT_RECIRCULATE) {
    +    recirculate the packet, i.e. it will go back to starting with the
    +        ingress parser;
    +    return;   // Do not continue below.
    +}
    +enqueue one packet for output port istd.egress_port
    +
    +
    +
    +

    As for the handling of a packet after ingress processing, a PSA implementation may drop a packet after egress processing, even if the pseudocode above says that a packet will be sent. For example, you may attempt to clone a packet after egress when the packet buffer is @@ -1834,57 +2660,80 @@ pipeline is busy handling other packets. It is recommended that an implementation maintain counters of packets dropped, preferably with separate counters for as many different reasons as the implementation -has for dropping packets outside the control of the P4 program. -

    6.6. Actions for directing packets during egress

    6.6.1. Drop operation

    -

    Do not send the packet out of the device after egress processing is -complete. -

    -
    -
    -
    /// Modify egress output metadata to cause no packet to be sent out of
    -/// the device.
    -
    -/// This action does not change whether a clone will occur.
    -
    -@noWarn("unused")
    -action egress_drop(inout psa_egress_output_metadata_t meta)
    -{
    -    meta.drop = true;
    -}

    6.7. Contents of packets sent out to ports

    -

    There is no metadata associated with NTP and NTCPU packets. -

    -

    They begin with the series of bytes emitted by the egress deparser. +has for dropping packets outside the control of the P4 program.

    +
    +
    +
    +

    7.7. Actions for directing packets during egress

    +
    +

    7.7.1. Drop operation

    +
    +

    Do not send the packet out of the device after egress processing is +complete.

    +
    +
    +
    +
    /// Modify egress output metadata to cause no packet to be sent out of
    +/// the device.
    +
    +/// This action does not change whether a clone will occur.
    +
    +@noWarn("unused")
    +action egress_drop(inout psa_egress_output_metadata_t meta)
    +{
    +    meta.drop = true;
    +}
    +
    +
    +
    +
    +
    +

    7.8. Contents of packets sent out to ports

    +
    +

    There is no metadata associated with NTP and NTCPU packets.

    +
    +
    +

    They begin with the series of bytes emitted by the egress deparser. Following that is the payload, which are those packet bytes that were -not parsed in the egress parser. -

    -

    For Ethernet ports, any padding required to get the packet up to the +not parsed in the egress parser.

    +
    +
    +

    For Ethernet ports, any padding required to get the packet up to the minimum frame size required is done by the implementation, as well as -calculation of and appending the Ethernet frame CRC. -

    -

    It is expected that typical P4 programs will have explicit checks to -avoid sending packets larger than a port's maximum frame size. A +calculation of and appending the Ethernet frame CRC.

    +
    +
    +

    It is expected that typical P4 programs will have explicit checks to +avoid sending packets larger than a port’s maximum frame size. A typical implementation will drop frames larger than this maximum supported size. It is recommended that they maintain error counters -for such dropped frames. -

    -

    The P4Runtime has a “Packet In” capability to receive packets sent by -a PSA device to the port PSA_PORT_CPU. There is no metadata +for such dropped frames.

    +
    +
    +

    The P4Runtime has a "Packet In" capability to receive packets sent by +a PSA device to the port PSA_PORT_CPU. There is no metadata associated with such packets, only the contents of the packet that are -emitted normally by the P4 program's EgressDeparser code. There may -be some translation of header field values, as described in Section -4.1. -

    6.8. Packet Cloning

    -

    Packet cloning is a mechanism to send a copy of a packet to a -specified port, in addition to the ‘regular’ packet. Multiple clones +emitted normally by the P4 program’s EgressDeparser code. There may +be some translation of header field values, as described in +Section 4.1.

    +
    +
    +
    +

    7.9. Packet Cloning

    +
    +

    Packet cloning is a mechanism to send a copy of a packet to a +specified port, in addition to the 'regular' packet. Multiple clones can be made via a single clone operation, by appropriate control plane -configuration. -

    -

    One use case for cloning is packet mirroring, i.e. send the packet to +configuration.

    +
    +
    +

    One use case for cloning is packet mirroring, i.e. send the packet to its normal destination according to other features implemented by the P4 program, and in addition, send a copy of the packet as received to -another output port, e.g. to a monitoring device. -

    -

    Packet cloning happens at the end of the ingress and/or egress +another output port, e.g. to a monitoring device.

    +
    +
    +

    Packet cloning happens at the end of the ingress and/or egress pipeline. PSA specifies the following semantics for the clone operation. When the clone operation is invoked at the end of the ingress pipeline, each cloned packet is a copy of the packet as it @@ -1892,145 +2741,170 @@ the end of the egress pipeline, each cloned packet is a copy of the modified packet after egress processing, as output by the egress deparser. In both cases, the cloned packets are submitted to the egress -pipeline for further processing. -

    -

    Logically, PRE implements the mechanics of copying a packet. The +pipeline for further processing.

    +
    +
    +

    Logically, PRE implements the mechanics of copying a packet. The metadata fields that control cloning are those whose names begin with -clone in types psa_ingress_output_metadata_t and -psa_egress_output_metadata_t. -

    -
    -
    -
    bool                     clone;
    -CloneSessionId_t         clone_session_id;
    -

    The clone flag specifies whether a packet should be cloned. If +clone in types psa_ingress_output_metadata_t and +psa_egress_output_metadata_t.

    +
    +
    +
    +
    bool                     clone;
    +CloneSessionId_t         clone_session_id;
    +
    +
    +
    +

    The clone flag specifies whether a packet should be cloned. If true, then a cloned packet, or packets, should be generated at the end of the -pipeline. The clone_session_id specifies one of several possible +pipeline. The clone_session_id specifies one of several possible clone sessions that the control plane may configure in the PRE. For each clone session, the control plane may configure the following values that should be associated with packets cloned using that -session. -

    -
    -
    -
    /// Each clone session may configure zero or more pairs of (egress_port, instance).
    -PortId_t         egress_port;  /// egress_port in a pair of (egress_port, instance)
    -EgressInstance_t instance;  /// instance in a pair of (egress_port, instance)
    -
    -/// Each clone session has configuration for exactly one of each of
    -/// the following values.
    -ClassOfService_t class_of_service;
    -bool             truncate;
    -PacketLength_t   packet_length_bytes;  /// only used if truncate is true
    -

    The configuration of the set of (egress_port, instance) values for a +session.

    +
    +
    +
    +
    /// Each clone session may configure zero or more pairs of (egress_port, instance).
    +PortId_t         egress_port;  /// egress_port in a pair of (egress_port, instance)
    +EgressInstance_t instance;  /// instance in a pair of (egress_port, instance)
    +
    +/// Each clone session has configuration for exactly one of each of
    +/// the following values.
    +ClassOfService_t class_of_service;
    +bool             truncate;
    +PacketLength_t   packet_length_bytes;  /// only used if truncate is true
    +
    +
    +
    +

    The configuration of the set of (egress_port, instance) values for a clone session is similar to, and has the same requirements and restrictions as, the configuration of a set of pairs for a multicast -group, as described in Section 6.2.1. -

    -

    The egress_port values may be any ports that can be used for normal -unicast packets, i.e. any normal port, PSA_PORT_CPU, or -PSA_PORT_RECIRCULATE. For the latter two values, the cloned packet +group, as described in Section 7.3.1.

    +
    +
    +

    The egress_port values may be any ports that can be used for normal +unicast packets, i.e. any normal port, PSA_PORT_CPU, or +PSA_PORT_RECIRCULATE. For the latter two values, the cloned packet will be sent to the CPU, or recirculated at the end of egress processing, as a normal unicast packet would at the end of egress -processing. -

    -

    Truncation of cloned packets is supported as an optimization to +processing.

    +
    +
    +

    Truncation of cloned packets is supported as an optimization to reduce the bandwidth required to send the beginning of packets. This is sometimes useful in sending packet headers to the control plane, or some kinds of data collection system for traffic monitoring. Here by -“headers” we simply mean “some number of bytes from the beginning of -the packet”, not headers as defined and parsed in your P4 program. -

    -

    If truncate is false for a clone session, then no truncation is -performed for packets cloned using that session. -

    -

    Otherwise, packets are truncated to contain at most the first -packet_length_bytes bytes of the packet, with any additional bytes +"headers" we simply mean "some number of bytes from the beginning of +the packet", not headers as defined and parsed in your P4 program.

    +
    +
    +

    If truncate is false for a clone session, then no truncation is +performed for packets cloned using that session.

    +
    +
    +

    Otherwise, packets are truncated to contain at most the first +packet_length_bytes bytes of the packet, with any additional bytes removed. Truncating a packet has no effect on any metadata that is carried along with it, and the size of that metadata is not counted as -part of the packet_length_bytes quantity. Any truncation is based +part of the packet_length_bytes quantity. Any truncation is based completely upon the length of the packet as passed to the type -packet_in parameter to the ingress parser (for ingress to egress -clones), or as sent out as the type packet_out parameter from the -egress deparser (for egress to egress clones). -

    -

    PSA implementations are allowed to support only a restricted set of -possible values for packet_length_bytes, e.g. an implementation -might choose only to support values that are multiples of 32 bytes. -

    -

    Since it is an expected common case to clone packets to the CPU, every +packet_in parameter to the ingress parser (for ingress to egress +clones), or as sent out as the type packet_out parameter from the +egress deparser (for egress to egress clones).

    +
    +
    +

    PSA implementations are allowed to support only a restricted set of +possible values for packet_length_bytes, e.g. an implementation +might choose only to support values that are multiples of 32 bytes.

    +
    +
    +

    Since it is an expected common case to clone packets to the CPU, every PSA implementation begins with a clone session -PSA_CLONE_SESSION_TO_CPU initialized with the set of (egress_port, -instance) values containing exactly one pair with -egress_port = PSA_PORT_CPU and instance = 0. This clone session +PSA_CLONE_SESSION_TO_CPU initialized with the set of (egress_port, +instance) values containing exactly one pair with +egress_port = PSA_PORT_CPU and instance = 0. This clone session is also initialized with the configuration values -class_of_service = 0, and truncate = false. -

    6.8.1. Clone Examples

    -

    The partial program below demonstrates how to clone a packet. -

    -
    -
    -
    header clone_i2e_metadata_t {
    -    bit<8> custom_tag;
    -    EthernetAddress srcAddr;
    -}
    -control ingress(inout headers hdr,
    -                inout metadata user_meta,
    -                in  psa_ingress_input_metadata_t  istd,
    -                inout psa_ingress_output_metadata_t ostd)
    -{
    -    action do_clone (CloneSessionId_t session_id) {
    -        ostd.clone = true;
    -        ostd.clone_session_id = session_id;
    -        user_meta.custom_clone_id = 1;
    -    }
    -    table t {
    -        key = {
    -            user_meta.fwd_metadata.outport : exact;
    -        }
    -        actions = { do_clone; }
    -    }
    -
    -    apply {
    -        t.apply();
    -    }
    -}
    -control IngressDeparserImpl(
    -    packet_out packet,
    -    out clone_i2e_metadata_t clone_i2e_meta,
    -    out empty_metadata_t resubmit_meta,
    -    out metadata normal_meta,
    -    inout headers hdr,
    -    in metadata meta,
    -    in psa_ingress_output_metadata_t istd)
    -{
    -    DeparserImpl() common_deparser;
    -    apply {
    -        // Assignments to the out parameter clone_i2e_meta must be
    -        // guarded by this if condition:
    -        if (psa_clone_i2e(istd)) {
    -            clone_i2e_meta.custom_tag = (bit<8>) meta.custom_clone_id;
    -            if (meta.custom_clone_id == 1) {
    -                clone_i2e_meta.srcAddr = hdr.ethernet.srcAddr;
    -            }
    -        }
    -        common_deparser.apply(packet, hdr);
    -    }
    -}

    6.9. Packet Resubmission

    -

    Packet resubmission is a mechanism to repeat ingress processing on a -packet. -

    -

    Packet resubmission happens at the end of the ingress pipeline. When a +class_of_service = 0, and truncate = false.

    +
    +
    +

    7.9.1. Clone Examples

    +
    +

    The partial program below demonstrates how to clone a packet.

    +
    +
    +
    +
    header clone_i2e_metadata_t {
    +    bit<8> custom_tag;
    +    EthernetAddress srcAddr;
    +}
    +control ingress(inout headers hdr,
    +                inout metadata user_meta,
    +                in  psa_ingress_input_metadata_t  istd,
    +                inout psa_ingress_output_metadata_t ostd)
    +{
    +    action do_clone (CloneSessionId_t session_id) {
    +        ostd.clone = true;
    +        ostd.clone_session_id = session_id;
    +        user_meta.custom_clone_id = 1;
    +    }
    +    table t {
    +        key = {
    +            user_meta.fwd_metadata.outport : exact;
    +        }
    +        actions = { do_clone; }
    +    }
    +
    +    apply {
    +        t.apply();
    +    }
    +}
    +control IngressDeparserImpl(
    +    packet_out packet,
    +    out clone_i2e_metadata_t clone_i2e_meta,
    +    out empty_metadata_t resubmit_meta,
    +    out metadata normal_meta,
    +    inout headers hdr,
    +    in metadata meta,
    +    in psa_ingress_output_metadata_t istd)
    +{
    +    DeparserImpl() common_deparser;
    +    apply {
    +        // Assignments to the out parameter clone_i2e_meta must be
    +        // guarded by this if condition:
    +        if (psa_clone_i2e(istd)) {
    +            clone_i2e_meta.custom_tag = (bit<8>) meta.custom_clone_id;
    +            if (meta.custom_clone_id == 1) {
    +                clone_i2e_meta.srcAddr = hdr.ethernet.srcAddr;
    +            }
    +        }
    +        common_deparser.apply(packet, hdr);
    +    }
    +}
    +
    +
    +
    +
    +
    +

    7.10. Packet Resubmission

    +
    +

    Packet resubmission is a mechanism to repeat ingress processing on a +packet.

    +
    +
    +

    Packet resubmission happens at the end of the ingress pipeline. When a packet is resubmitted, the packet finishes the ingress pipeline processing and re-enters the ingress parser without being deparsed. In other words, the resubmitted packet has the same header and payload as -the original packet. The ingress_port of the resubmitted packet is -the same as the original packet. The packet_path of the resubmitted -packet is changed to RESUBMIT. -

    -

    The ingress parser distinguishes the resubmitted packet from the -original packet with the packet_path field in -ingress_parser_intrinsic_metadata_t. The ingress parser can choose a +the original packet. The ingress_port of the resubmitted packet is +the same as the original packet. The packet_path of the resubmitted +packet is changed to RESUBMIT.

    +
    +
    +

    The ingress parser distinguishes the resubmitted packet from the +original packet with the packet_path field in +ingress_parser_intrinsic_metadata_t. The ingress parser can choose a different algorithm to parse the resubmitted packet. Similarly, the ingress pipeline can choose to process the resubmitted packet with different actions as opposed to the ones used to process the original @@ -2039,252 +2913,361 @@ resubmitted the first time, or second time, by the extra metadata associated with the packet. Note the maximum number of packet resubmission for a single packet is target-dependent. See section -3. -

    -

    PSA specifies that the resubmit operation can only be used in the +{packet-paths}.

    +
    +
    +

    PSA specifies that the resubmit operation can only be used in the ingress pipeline. The egress pipeline cannot resubmit packets. As -described in Section 3, there is no mandated mechanism +described in Section [#packet-paths], there is no mandated mechanism in PSA to prevent a single received packet from creating packets that continue to recirculate, resubmit, or clone from egress to egress indefinitely. However, targets may impose limits on the number of -resubmissions, recirculations, or clones. -

    -

    One use case of packet resubmission is to increase the capacity and +resubmissions, recirculations, or clones.

    +
    +
    +

    One use case of packet resubmission is to increase the capacity and flexibility of the packet processing pipeline. For example, because the same packet is processed by the ingress pipeline multiple times, it effectively increase the amount of operations on the packet by N -folds, where N is the number of times the packet is resubmitted. -

    -

    Another use case is to deploy multiple packet processing algorithms on +folds, where N is the number of times the packet is resubmitted.

    +
    +
    +

    Another use case is to deploy multiple packet processing algorithms on the same packet. For example, the original packet can be parsed and resubmitted in the first pass with additional metadata to select one of the algorithms. Then, the resubmitted packet can be parsed, -modified and deparsed using the selected algorithm. -

    -

    To facilitate communication from the ingress processing pass that +modified and deparsed using the selected algorithm.

    +
    +
    +

    To facilitate communication from the ingress processing pass that caused a resubmit to occur, to the next ingress processing pass after the resubmit has happened, the resubmission mechanism supports attaching optional metadata with the resubmitted packet. The metadata is generated during the pass through the ingress pipeline that chooses -the resubmit operation, and used in the next pass. -

    -

    A PSA implementation provides a configuration bit resubmit to the +the resubmit operation, and used in the next pass.

    +
    +
    +

    A PSA implementation provides a configuration bit resubmit to the PRE to enable the resubmission mechanism. If true, the original packet is resubmitted with the optional resubmit metadata. If false, the resubmission mechanism is disabled and no assignments to -resubmit_meta should be performed. -

    6.10. Packet Recirculation

    -

    Packet recirculation is a mechanism to repeat ingress processing on a +resubmit_meta should be performed.

    +
    +
    +
    +

    7.11. Packet Recirculation

    +
    +

    Packet recirculation is a mechanism to repeat ingress processing on a packet, after it has completed egress processing. Unlike a resubmit, where the resubmitted packet contents are identical to the packet that arrived at the ingress parser, a recirculated packet may have different headers than the packet had before recirculation. This could be useful in implementing features such as multiple levels of -tunnel encapsulation or decapsulation. -

    -

    Whether a packet is recirculated must be chosen during ingress -processing, by sending the packet to port PSA_PORT_RECIRCULATE. Packet +tunnel encapsulation or decapsulation.

    +
    +
    +

    Whether a packet is recirculated must be chosen during ingress +processing, by sending the packet to port PSA_PORT_RECIRCULATE. Packet recirculation happens at the end of the egress pipeline. When a packet is sent to the recirculate port, the packet finishes egress processing, including the egress deparser, and then re-enters the -ingress parser. The ingress_port of the recirculated packet is set -to PSA_PORT_RECIRCULATE. The packet_path of the recirculated packet is -set to RECIRCULATE. -

    -

    Similar to packet resubmission, packet recirculation also supports +ingress parser. The ingress_port of the recirculated packet is set +to PSA_PORT_RECIRCULATE. The packet_path of the recirculated packet is +set to RECIRCULATE.

    +
    +
    +

    Similar to packet resubmission, packet recirculation also supports attaching optional metadata with the recirculated packet. The metadata is generated during egress processing, and filled in by -assigning a value to the out parameter recirculate_meta of the +assigning a value to the out parameter recirculate_meta of the egress deparser. The metadata is available to the ingress parser -after the packet is recirculated. -

    7. PSA Externs

    7.1. Restrictions on where externs may be used

    -

    All instantiations in a P416 program occur at compile time, and can +after the packet is recirculated.

    +
    +
    +
    +
    +
    +

    8. PSA Externs

    +
    +
    +

    8.1. Restrictions on where externs may be used

    +
    +

    All instantiations in a P416 program occur at compile time, and can be arranged in a tree structure we will call the instantiation tree. -The root of the tree T represents the top level of the program. Its -children are the node for the package PSA_Switch described in -Section 5, and any externs instantiated at -the top level of the program. The children of the PSA_Switch node -are the packages and externs passed as parameters to the PSA_Switch -instantiation. See Figure 3 for a drawing of +The root of the tree T represents the top level of the program. Its +children are the node for the package PSA_Switch described in +Chapter 6, and any externs instantiated at +the top level of the program. The children of the PSA_Switch node +are the packages and externs passed as parameters to the PSA_Switch +instantiation. See Figure 3 for a drawing of the smallest instantiation tree possible for a P4 program written for -PSA. -

    -
    -

    instantiation-tree -

    -
    - -
    Figure 3. Minimal PSA instantiation tree
    -

    If any of those parsers or controls instantiate other parsers, +PSA.

    +
    +
    +
    +psa instantiation tree figure +
    +
    Figure 3. Minimal PSA instantiation tree
    +
    +
    +

    If any of those parsers or controls instantiate other parsers, controls, and/or externs, the instantiation tree contains child nodes -for them, continuing until the instantiation tree is complete. -

    -

    For every instance whose node is a descendant of the Ingress node in -this tree, call it an Ingress instance. Similarly for the other +for them, continuing until the instantiation tree is complete.

    +
    +
    +

    For every instance whose node is a descendant of the Ingress node in +this tree, call it an Ingress instance. Similarly for the other ingress and egress parsers and controls. All other instances are top -level instances. -

    -

    A PSA implementation is allowed to reject programs that instantiate +level instances.

    +
    +
    +

    A PSA implementation is allowed to reject programs that instantiate externs, or attempt to call their methods, from anywhere other than -the places mentioned in Table 5. -

    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Extern type Where it may be instantiated and called from
    ActionProfile Ingress, Egress
    ActionSelector Ingress, Egress
    Checksum IngressParser, EgressParser, IngressDeparser, EgressDeparser
    Counter Ingress, Egress
    Digest IngressDeparser
    DirectCounter Ingress, Egress
    DirectMeter Ingress, Egress
    Hash Ingress, Egress
    InternetChecksum IngressParser, EgressParser, IngressDeparser, EgressDeparser
    Meter Ingress, Egress
    Random Ingress, Egress
    Register Ingress, Egress
    -
    - -
    Table 5. Summary of controls that can instantiate and invoke externs.
    -

    For example, Counter being restricted to “Ingress, Egress” means -that every Counter instance must be instantiated within either the -Ingress control block or the Egress control block, or be a descendant -of one of those nodes in the instantiation tree. If a Counter +the places mentioned in Table {table-extern-usage}.

    +
    + + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Table 5. Summary of controls that can instantiate and invoke externs.
    Extern typeWhere it may be instantiated and called from

    ActionProfile

    Ingress, Egress

    ActionSelector

    Ingress, Egress

    Checksum

    IngressParser, EgressParser, IngressDeparser, EgressDeparser

    Counter

    Ingress, Egress

    Digest

    IngressDeparser

    DirectCounter

    Ingress, Egress

    DirectMeter

    Ingress, Egress

    Hash

    Ingress, Egress

    InternetChecksum

    IngressParser, EgressParser, IngressDeparser, EgressDeparser

    Meter

    Ingress, Egress

    Random

    Ingress, Egress

    Register

    Ingress, Egress

    +
    +

    For example, Counter being restricted to "Ingress, Egress" means +that every Counter instance must be instantiated within either the +Ingress control block or the Egress control block, or be a descendant +of one of those nodes in the instantiation tree. If a Counter instance is instantiated in Ingress, for example, then it cannot be referenced, and thus its methods cannot be called, from any control -block except Ingress or one of its descendants in the tree. -

    -

    PSA implementations need not support instantiating these externs at +block except Ingress or one of its descendants in the tree.

    +
    +
    +

    PSA implementations need not support instantiating these externs at the top level. PSA implementations are allowed to accept programs that use these externs in other places, but they need not. Thus P4 programmers wishing to maximize the portability of their programs should restrict their use of these externs to the places indicated in -the table. -

    -

    emit method calls for the type packet_out are restricted to be +the table.

    +
    +
    +

    emit method calls for the type packet_out are restricted to be within deparser control blocks in PSA, because those are the only -places where an instance of type packet_out is visible. Similarly -all methods for type packet_in, e.g. extract and advance, are -restricted to be within parsers in PSA programs. P416 restricts all -verify method calls to be within parsers for all P416 programs, -regardless of whether they are for the PSA. -

    -

    Rationale: -

    -
      -
    • It is expected that the highest performance PSA implementations will -not be able to update the same extern instance from both Ingress and +places where an instance of type packet_out is visible. Similarly +all methods for type packet_in, e.g. extract and advance, are +restricted to be within parsers in PSA programs. P416 restricts all +verify method calls to be within parsers for all P416 programs, +regardless of whether they are for the PSA.

      +
    +
    +

    Rationale:

    +
    +
    +
      +
    • +

      It is expected that the highest performance PSA implementations will +not be able to update the same extern instance from both Ingress and Egress, nor from more than one of the parsers or controls defined in -the PSA architecture. +the PSA architecture.

    • -
    • In a multi-pipeline device, there are effectively multiple +
    • +

      In a multi-pipeline device, there are effectively multiple instantiations of the ingress pipeline and of the egress pipeline. The primary motivation to create a multi-pipeline device is the practical difficulty in allowing the same stateful object (e.g. table, counter, etc.) to be accessed at a packet rate higher than that of a single pipeline. Thus each stateful object should be accessed from only a single pipeline on such a device. -See appendix E. -

    -

    7.2. PSA Table Properties

    -

    Table 6 lists all P4 table properties defined -by PSA that are not included in the base P416 language -specification. -

    -
    - - - - - - - - - - - - - -
    Property name Type See also
    psa_direct_counter one DirectCounter instance name Section 7.7.3
    psa_direct_meter one DirectMeter instance name Section 7.8
    psa_implementation instance name of one ActionProfile Sections 7.11, 7.12
    or ActionSelector
    psa_empty_group_action action Section 7.12
    psa_idle_timeout PSA_IdleTimeout_t Section 7.2.1
    -
    - -
    Table 6. Summary of PSA table properties.
    -

    A PSA implementation need not support both of a psa_implementation -and psa_direct_counter property on the same table. -

    -

    Similarly, a PSA implementation need not support both of a -psa_implementation and psa_direct_meter property on the same -table. -

    -

    A PSA implementation must implement tables that have both a -psa_direct_counter and psa_direct_meter property. -

    -

    A PSA implementation need not support both psa_implementation and -psa_idle_timeout properties on the same table. -

    7.2.1. Table entry timeout notification

    -

    PSA defines the table property psa_idle_timeout to enable specifying +See Appendix E.

    + + +
    +
    +
    +

    8.2. PSA Table Properties

    +
    +

    Table {table-table-properties} lists all P4 table properties defined +by PSA that are not included in the base P416 language +specification.

    +
    + + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Table 6. Summary of PSA table properties
    Property nameTypeSee also

    psa_direct_counter

    one DirectCounter instance name

    Section 8.7.3

    psa_direct_meter

    one DirectMeter instance name

    Section 8.8

    psa_implementation

    instance name of one ActionProfile or ActionSelector

    Section 8.11, Section 8.12

    psa_empty_group_action

    action

    Section 8.12

    psa_idle_timeout

    PSA_IdleTimeout_t

    Section 8.2.1

    +
    +

    A PSA implementation need not support both of a psa_implementation +and psa_direct_counter property on the same table.

    +
    +
    +

    Similarly, a PSA implementation need not support both of a +psa_implementation and psa_direct_meter property on the same +table.

    +
    +
    +

    A PSA implementation must implement tables that have both a +psa_direct_counter and psa_direct_meter property.

    +
    +
    +

    A PSA implementation need not support both psa_implementation and +psa_idle_timeout properties on the same table.

    +
    +
    +

    8.2.1. Table entry timeout notification

    +
    +

    PSA defines the table property psa_idle_timeout to enable specifying whether a table should maintain an idle time for each of its entries, and if so, what the data plane should do when a table entry has not -been matched for a length of time at least its configured idle time. -

    -

    The value assigned to psa_idle_timeout must be a value of type -PSA_IdleTimeout_t: -

    -
    -
    -
    /// Supported values for the psa_idle_timeout table property
    -enum PSA_IdleTimeout_t {
    -    NO_TIMEOUT,
    +been matched for a length of time at least its configured idle time.

    +
    +
    +

    The value assigned to psa_idle_timeout must be a value of type +PSA_IdleTimeout_t:

    +
    +
    +
    +
    /// Supported values for the psa_idle_timeout table property
    +enum PSA_IdleTimeout_t {
    +    NO_TIMEOUT,
         NOTIFY_CONTROL
    -};
    -

    If the property psa_idle_timeout is not specified for a table, its -default value is NO_TIMEOUT. Such tables need not maintain an idle +}; +

    +
    +
    +

    If the property psa_idle_timeout is not specified for a table, its +default value is NO_TIMEOUT. Such tables need not maintain an idle time for any of its table entries, and will not perform any special -action regardless of how long a table entry remains unmatched. -

    -

    If the property psa_idle_timeout is assigned a value of -NOTIFY_CONTROL for a table t, this enables idle timeout +action regardless of how long a table entry remains unmatched.

    +
    +
    +

    If the property psa_idle_timeout is assigned a value of +NOTIFY_CONTROL for a table t, this enables idle timeout notifications for the table. There is an API for the control plane to -set idle timeout values for entries of table t. -

    -

    If a table entry e has not been “hit” (i.e. not matched by an -invocation of t.apply()) for a time interval at least as long as its +set idle timeout values for entries of table t.

    +
    +
    +

    If a table entry e has not been "hit" (ie; not matched by an +invocation of t.apply()) for a time interval at least as long as its idle timeout value, the device should generate a notification to the -control plane for entry e. The rate and mode of how the +control plane for entry e. The rate and mode of how the notifications are generated and delivered to the control plane are subject to configuration parameters specified by the control plane -API. -

    -

    Example: -

    -
    -
    -
    table t {
    -  action a1 () { ... }
    -  action a2 () { ... }
    -  key = { hdr.f1: exact; }
    -  actions = { a1; a2; }
    -  default_action = a2;
    -  psa_idle_timeout = PSA_IdleTimeout_t.NOTIFY_CONTROL;
    -}
    -

    Restrictions on the idle timeout values and notifications: -

    -
      -
    • -

      It is likely that any hardware implementation will have a limited number of +API.

      +
    +
    +

    Example:

    +
    +
    +
    +
    table t {
    +  action a1 () { ... }
    +  action a2 () { ... }
    +  key = { hdr.f1: exact; }
    +  actions = { a1; a2; }
    +  default_action = a2;
    +  psa_idle_timeout = PSA_IdleTimeout_t.NOTIFY_CONTROL;
    +}
    +
    +
    +
    +

    Restrictions on the idle timeout values and notifications:

    +
    +
    +
      +
    • +

      It is likely that any hardware implementation will have a limited number of bits to represent the values, and, since the values are programmed at runtime, it is the responsibility of the runtime (P4Runtime or other controller software) to guarantee that the idle timeout values can be represented in @@ -2292,520 +3275,615 @@

    • -
    • -

      If no value is programmed for a table entry, even though the table has +support the idle timeout at all.

      +
    • +
    • +

      If no value is programmed for a table entry, even though the table has enabled the idle timeout property, the entry will not generate a -notification. -

    • -
    • -

      PSA does not require a timeout value for a default action entry. The reason +notification.

      +
    • +
    • +

      PSA does not require a timeout value for a default action entry. The reason for not making this mandatory in the specification is that the default action may not have an explicit table entry to represent it, and also there are no known compelling use cases for a controller knowing when no misses -have occurred for a particular table for a long time. The default action entry will not be aged out. -

    • -
    • -

      Currently, tables implemented using ActionSelectors and ActionProfiles do not support the psa_idle_timeout property. Future versions of the specification may remove this restriction. -

    - -

    Devices are allowed to send idle timeout notifications on a best +have occurred for a particular table for a long time. The default action entry will not be aged out.

    + +
  • +

    Currently, tables implemented using ActionSelectors and ActionProfiles do not support the psa_idle_timeout property. Future versions of the specification may remove this restriction.

    +
  • + +
    +
    +

    Devices are allowed to send idle timeout notifications on a best effort basis. For example, if a table has one million entries, and every entry reaches its idle timeout duration at the same instant in time, an implementation cannot send all of these notifications instantaneously, but only at some finite rate. Possible behaviors -include: -

    -
      -
    • If the control plane deletes entry e, the deletion might occur -even though the device never notified the control plane that e's -idle timeout was reached before it was deleted. -
    • -
    • If entry e is matched some time after its idle timeout has +include:

      +
    +
    +
      +
    • +

      If the control plane deletes entry e, the deletion might occur +even though the device never notified the control plane that `e’s +idle timeout was reached before it was deleted.

      +
    • +
    • +

      If entry e is matched some time after its idle timeout has elapsed, and the device has not yet generated an idle timeout -notification for e, it is possible that the device will never -generate a notification to the control plane for that entry. -

    -

    7.3. Packet Replication Engine

    -

    The PacketReplicationEngine extern (abbreviated PRE) represents +notification for e, it is possible that the device will never +generate a notification to the control plane for that entry.

    + + +
    +
    +
    +
    +

    8.3. Packet Replication Engine

    +
    +

    The PacketReplicationEngine extern (abbreviated PRE) represents a part of the PSA pipeline that is not programmable via writing P4 -code. -

    -

    Even though the PRE can not be programmed using P4, it can be +code.

    +
    +
    +

    Even though the PRE can not be programmed using P4, it can be configured using control plane APIs, e.g. configuring multicast groups and clone sessions. For every packet, your P4 program will typically assign values to intrinsic metadata in structs such as those of type -psa_ingress_output_metadata_t and psa_egress_output_metadata_t, +psa_ingress_output_metadata_t and psa_egress_output_metadata_t, which direct the operation of the PRE on that packet. The file -psa.p4 defines some actions to help set these metadata fields for -some common use cases, described in sections 6.3 -and 6.6. -

    -

    The PRE extern must be instantiated exactly once, in the PSA_Switch -package instantiation. See near the end of Section -5 for the package definitions from psa.p4. -See below for an example of instantiating these packages, including -the instantiation of one instance of PacketReplicationEngine and one -of BufferingQueueingEngine in the PSA_Switch package -instantiation. -

    -
    -
    -
    IngressPipeline(IngressParserImpl(),
    -                ingress(),
    -                IngressDeparserImpl()) ip;
    -
    -EgressPipeline(EgressParserImpl(),
    -               egress(),
    -               EgressDeparserImpl()) ep;
    -
    -PSA_Switch(ip, PacketReplicationEngine(), ep, BufferingQueueingEngine()) main;

    7.4. Buffering Queuing Engine

    -

    The BufferingQueueingEngine extern (abbreviated BQE) represents +psa.p4 defines some actions to help set these metadata fields for +some common use cases, described in Section 7.4 +and Section 7.7.

    +
    +
    +

    The PRE extern must be instantiated exactly once, in the PSA_Switch +package instantiation. See near the end of Chapter 6 +for the package definitions from psa.p4. See below for an example of +instantiating these packages, including the instantiation of one +instance of PacketReplicationEngine and one of +BufferingQueueingEngine in the PSA_Switch package instantiation.

    +
    +
    +
    +
    IngressPipeline(IngressParserImpl(),
    +                ingress(),
    +                IngressDeparserImpl()) ip;
    +
    +EgressPipeline(EgressParserImpl(),
    +               egress(),
    +               EgressDeparserImpl()) ep;
    +
    +PSA_Switch(ip, PacketReplicationEngine(), ep, BufferingQueueingEngine()) main;
    +
    +
    +
    +
    +

    8.4. Buffering Queuing Engine

    +
    +

    The BufferingQueueingEngine extern (abbreviated BQE) represents another part of the PSA pipeline, after egress, that is not -programmable via writing P4 code. -

    -

    Even though the BQE can not be programmed using P4, it can be +programmable via writing P4 code.

    +
    +
    +

    Even though the BQE can not be programmed using P4, it can be configured both directly using control plane APIs and by setting -intrinsic metadata. -

    -

    The BQE extern must be instantiated exactly once, as the PRE must. -See Section 7.3 for additional discussion and example code. -

    7.5. Hashes

    -

    Supported hash algorithms: -

    -
    -
    -
    enum PSA_HashAlgorithm_t {
    -  IDENTITY,
    -  CRC32,
    -  CRC32_CUSTOM,
    -  CRC16,
    -  CRC16_CUSTOM,
    -  ONES_COMPLEMENT16,  /// One's complement 16-bit sum used for IPv4 headers,
    -                      /// TCP, and UDP.
    -  TARGET_DEFAULT      /// target implementation defined
    -}

    7.5.1. Hash function

    -

    Example usage: -

    -
    -
    -
    parser P() {
    -  Hash<bit<16>>(PSA_HashAlgorithm_t.CRC16) h;
    -  bit<16> hash_value = h.get_hash(buffer);
    -}
    -

    Parameters: -

    -
      -
    • algo The algorithm to use for computation (see 7.5). -
    • -
    • O The type of the return value of the hash. -
    - -
    -
    -
    extern Hash<O> {
    -  /// Constructor
    -  Hash(PSA_HashAlgorithm_t algo);
    -
    -  /// Compute the hash for data.
    -  /// @param data The data over which to calculate the hash.
    -  /// @return The hash value.
    -  @pure
    -  O get_hash<D>(in D data);
    -
    -  /// Compute the hash for data, with modulo by max, then add base.
    -  /// @param base Minimum return value.
    -  /// @param data The data over which to calculate the hash.
    -  /// @param max The hash value is divided by max to get modulo.
    -  ///        An implementation may limit the largest value supported,
    -  ///        e.g. to a value like 32, or 256, and may also only
    -  ///        support powers of 2 for this value.  P4 developers should
    -  ///        limit their choice to such values if they wish to
    -  ///        maximize portability.
    -  /// @return (base + (h % max)) where h is the hash value.
    -  @pure
    -  O get_hash<T, D>(in T base, in D data, in T max);
    -}

    7.6. Checksums

    -

    PSA provides checksum functions compute an integer on the stream of +intrinsic metadata.

    +
    +
    +

    The BQE extern must be instantiated exactly once, as the PRE must. +See Section 8.3 for additional discussion and example code.

    +
    +
    +
    +

    8.5. Hashes

    +
    +

    Supported hash algorithms:

    +
    +
    +
    +
    enum PSA_HashAlgorithm_t {
    +  IDENTITY,
    +  CRC32,
    +  CRC32_CUSTOM,
    +  CRC16,
    +  CRC16_CUSTOM,
    +  ONES_COMPLEMENT16,  /// One's complement 16-bit sum used for IPv4 headers,
    +                      /// TCP, and UDP.
    +  TARGET_DEFAULT      /// target implementation defined
    +}
    +
    +
    +
    +

    8.5.1. Hash function

    +
    +

    Example usage:

    +
    +
    +
    +
    parser P() {
    +  Hash<bit<16>>(PSA_HashAlgorithm_t.CRC16) h;
    +  bit<16> hash_value = h.get_hash(buffer);
    +}
    +
    +
    +
    +

    Parameters:

    +
    +
    +
      +
    • +

      algo — The algorithm to use for computation (see Section 8.5).

      +
    • +
    • +

      O  — The type of the return value of the hash.

      +
    • +
    +
    +
    +
    +
    extern Hash<O> {
    +  /// Constructor
    +  Hash(PSA_HashAlgorithm_t algo);
    +
    +  /// Compute the hash for data.
    +  /// @param data The data over which to calculate the hash.
    +  /// @return The hash value.
    +  @pure
    +  O get_hash<D>(in D data);
    +
    +  /// Compute the hash for data, with modulo by max, then add base.
    +  /// @param base Minimum return value.
    +  /// @param data The data over which to calculate the hash.
    +  /// @param max The hash value is divided by max to get modulo.
    +  ///        An implementation may limit the largest value supported,
    +  ///        e.g. to a value like 32, or 256, and may also only
    +  ///        support powers of 2 for this value.  P4 developers should
    +  ///        limit their choice to such values if they wish to
    +  ///        maximize portability.
    +  /// @return (base + (h % max)) where h is the hash value.
    +  @pure
    +  O get_hash<T, D>(in T base, in D data, in T max);
    +}
    +
    +
    +
    +
    +
    +

    8.6. Checksums

    +
    +

    PSA provides checksum functions compute an integer on the stream of bytes in packet headers. Checksums are often used as an integrity -check to detect corrupted or otherwise malformed packets. -

    7.6.1. Basic checksum

    -

    The basic checksum extern provided in PSA supports arbitrary hash -algorithms. -

    -

    Parameters: -

    -
      -
    • W The width of the checksum -
    - -
    -
    -
    extern Checksum<W> {
    -  /// Constructor
    -  Checksum(PSA_HashAlgorithm_t hash);
    -
    -  /// Reset internal state and prepare unit for computation.
    -  /// Every instance of a Checksum object is automatically initialized as
    -  /// if clear() had been called on it. This initialization happens every
    -  /// time the object is instantiated, that is, whenever the parser or control
    -  /// containing the Checksum object are applied.
    -  /// All state maintained by the Checksum object is independent per packet.
    -  void clear();
    -
    -  /// Add data to checksum
    -  void update<T>(in T data);
    -
    -  /// Get checksum for data added (and not removed) since last clear
    -  @noSideEffects
    -  W    get();
    -}

    7.6.2. Incremental checksum

    -

    PSA also provides an incremental checksum that comes equipped with an -additional subtract method that can be used to remove data previously -added. The checksum is computed using the ONES_COMPLEMENT16 hash -algorithm used with protocols such as IPv4, TCP, and UDP see IETF -RFC 1624 -and section B for details. -

    -
    -
    -
    // Checksum based on `ONES_COMPLEMENT16` algorithm used in IPv4, TCP, and UDP.
    -// Supports incremental updating via `subtract` method.
    -// See IETF RFC 1624.
    -extern InternetChecksum {
    -  /// Constructor
    -  InternetChecksum();
    -
    -  /// Reset internal state and prepare unit for computation.  Every
    -  /// instance of an InternetChecksum object is automatically
    -  /// initialized as if clear() had been called on it, once for each
    -  /// time the parser or control it is instantiated within is
    -  /// executed.  All state maintained by it is independent per packet.
    -  void clear();
    -
    -  /// Add data to checksum.  data must be a multiple of 16 bits long.
    -  void add<T>(in T data);
    -
    -  /// Subtract data from existing checksum.  data must be a multiple of
    -  /// 16 bits long.
    -  void subtract<T>(in T data);
    -
    -  /// Get checksum for data added (and not removed) since last clear
    -  @noSideEffects
    -  bit<16> get();
    -
    -  /// Get current state of checksum computation.  The return value is
    -  /// only intended to be used for a future call to the set_state
    -  /// method.
    -  @noSideEffects
    -  bit<16> get_state();
    -
    -  /// Restore the state of the InternetChecksum instance to one
    -  /// returned from an earlier call to the get_state method.  This
    -  /// state could have been returned from the same instance of the
    -  /// InternetChecksum extern, or a different one.
    -  void set_state(in bit<16> checksum_state);
    -}

    7.6.3. InternetChecksum examples

    -

    The partial program below demonstrates one way to use the InternetChecksum +check to detect corrupted or otherwise malformed packets.

    +
    +
    +

    8.6.1. Basic checksum

    +
    +

    The basic checksum extern provided in PSA supports arbitrary hash +algorithms.

    +
    +
    +

    Parameters:

    +
    +
    +
      +
    • +

      W — The width of the checksum

      +
    • +
    +
    +
    +
    +
    extern Checksum<W> {
    +  /// Constructor
    +  Checksum(PSA_HashAlgorithm_t hash);
    +
    +  /// Reset internal state and prepare unit for computation.
    +  /// Every instance of a Checksum object is automatically initialized as
    +  /// if clear() had been called on it. This initialization happens every
    +  /// time the object is instantiated, that is, whenever the parser or control
    +  /// containing the Checksum object are applied.
    +  /// All state maintained by the Checksum object is independent per packet.
    +  void clear();
    +
    +  /// Add data to checksum
    +  void update<T>(in T data);
    +
    +  /// Get checksum for data added (and not removed) since last clear
    +  @noSideEffects
    +  W    get();
    +}
    +
    +
    +
    +
    +

    8.6.2. Incremental checksum

    +
    +

    PSA also provides an incremental checksum that comes equipped with an +additional subtract method that can be used to remove data previously +added. The checksum is computed using the ONES_COMPLEMENT16 hash +algorithm used with protocols such as IPv4, TCP, and UDP — see [IETF +RFC 1624](https://tools.ietf.org/html/rfc1624) +and Appendix B for details.

    +
    +
    +
    +
    // Checksum based on `ONES_COMPLEMENT16` algorithm used in IPv4, TCP, and UDP.
    +// Supports incremental updating via `subtract` method.
    +// See IETF RFC 1624.
    +extern InternetChecksum {
    +  /// Constructor
    +  InternetChecksum();
    +
    +  /// Reset internal state and prepare unit for computation.  Every
    +  /// instance of an InternetChecksum object is automatically
    +  /// initialized as if clear() had been called on it, once for each
    +  /// time the parser or control it is instantiated within is
    +  /// executed.  All state maintained by it is independent per packet.
    +  void clear();
    +
    +  /// Add data to checksum.  data must be a multiple of 16 bits long.
    +  void add<T>(in T data);
    +
    +  /// Subtract data from existing checksum.  data must be a multiple of
    +  /// 16 bits long.
    +  void subtract<T>(in T data);
    +
    +  /// Get checksum for data added (and not removed) since last clear
    +  @noSideEffects
    +  bit<16> get();
    +
    +  /// Get current state of checksum computation.  The return value is
    +  /// only intended to be used for a future call to the set_state
    +  /// method.
    +  @noSideEffects
    +  bit<16> get_state();
    +
    +  /// Restore the state of the InternetChecksum instance to one
    +  /// returned from an earlier call to the get_state method.  This
    +  /// state could have been returned from the same instance of the
    +  /// InternetChecksum extern, or a different one.
    +  void set_state(in bit<16> checksum_state);
    +}
    +
    +
    +
    +
    +

    8.6.3. InternetChecksum examples

    +
    +

    The partial program below demonstrates one way to use the InternetChecksum extern to verify whether the checksum field in a parsed IPv4 header is correct, and set a parser error if it is wrong. It also demonstrates -checking for parser errors in the Ingress control block, dropping +checking for parser errors in the Ingress control block, dropping the packet if any errors occurred during parsing. PSA programs may choose to handle packets with parser errors in other ways than shown -in this example it is up to the P4 program author to choose and -write the desired behavior. -

    -

    Neither P416 nor the PSA provide any special mechanisms to record +in this example — it is up to the P4 program author to choose and +write the desired behavior.

    +
    +
    +

    Neither P416 nor the PSA provide any special mechanisms to record the location within a packet that a parser error occurred. A P4 program author can choose to record such location information explicitly. For example, one may define metadata fields specifically -for that purpose e.g. to hold an encoded value representing the last -parser state reached, or the number of bytes extracted so far and then -assign values to those fields within the parser state code. -

    -
    -
    -
    // Define additional error values, one of them for packets with
    -// incorrect IPv4 header checksums.
    -error {
    -    UnhandledIPv4Options,
    +for that purpose — e.g. to hold an encoded value representing the last
    +parser state reached, or the number of bytes extracted so far — and then
    +assign values to those fields within the parser state code.

    +
    +
    +
    +
    // Define additional error values, one of them for packets with
    +// incorrect IPv4 header checksums.
    +error {
    +    UnhandledIPv4Options,
         BadIPv4HeaderChecksum
    -}
    -
    -typedef bit<32> PacketCounter_t;
    -typedef bit<8>  ErrorIndex_t;
    -
    -const bit<9> NUM_ERRORS = 256;
    -
    -parser IngressParserImpl(packet_in buffer,
    -                         out headers hdr,
    -                         inout metadata user_meta,
    -                         in psa_ingress_parser_input_metadata_t istd,
    -                         in empty_metadata_t resubmit_meta,
    -                         in empty_metadata_t recirculate_meta)
    -{
    -    InternetChecksum() ck;
    -    state start {
    -        buffer.extract(hdr.ethernet);
    -        transition select(hdr.ethernet.etherType) {
    -            0x0800: parse_ipv4;
    -            default: accept;
    -        }
    -    }
    -    state parse_ipv4 {
    -        buffer.extract(hdr.ipv4);
    -        // TBD: It would be good to enhance this example to
    -        // demonstrate checking of IPv4 header checksums for IPv4
    -        // headers with options, but this example does not handle such
    -        // packets.
    -        verify(hdr.ipv4.ihl == 5, error.UnhandledIPv4Options);
    -        ck.clear();
    -        ck.add({
    -            /* 16-bit word  0   */ hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv,
    -            /* 16-bit word  1   */ hdr.ipv4.totalLen,
    -            /* 16-bit word  2   */ hdr.ipv4.identification,
    -            /* 16-bit word  3   */ hdr.ipv4.flags, hdr.ipv4.fragOffset,
    -            /* 16-bit word  4   */ hdr.ipv4.ttl, hdr.ipv4.protocol,
    -            /* 16-bit word  5 skip hdr.ipv4.hdrChecksum, */
    -            /* 16-bit words 6-7 */ hdr.ipv4.srcAddr,
    -            /* 16-bit words 8-9 */ hdr.ipv4.dstAddr
    -            });
    -        // The verify statement below will cause the parser to enter
    -        // the reject state, and thus terminate parsing immediately,
    -        // if the IPv4 header checksum is wrong.  It will also record
    -        // the error error.BadIPv4HeaderChecksum, which will be
    -        // available in a metadata field in the ingress control block.
    -        verify(ck.get() == hdr.ipv4.hdrChecksum,
    -               error.BadIPv4HeaderChecksum);
    -        transition select(hdr.ipv4.protocol) {
    -            6: parse_tcp;
    -            default: accept;
    -        }
    -    }
    -    state parse_tcp {
    -        buffer.extract(hdr.tcp);
    -        transition accept;
    -    }
    -}
    -
    -control ingress(inout headers hdr,
    -                inout metadata user_meta,
    -                in    psa_ingress_input_metadata_t  istd,
    -                inout psa_ingress_output_metadata_t ostd)
    -{
    -    // Table parser_error_count_and_convert below shows one way to
    -    // count the number of times each parser error was encountered.
    -    // Although it is not used in this example program, it also shows
    -    // how to convert the error value into a unique bit vector value
    -    // 'error_idx', which can be useful if you wish to put a bit
    -    // vector encoding of an error into a packet header, e.g. for a
    -    // packet sent to the control CPU.
    -
    -    DirectCounter<PacketCounter_t>(PSA_CounterType_t.PACKETS) parser_error_counts;
    -    ErrorIndex_t error_idx;
    -
    -    action set_error_idx (ErrorIndex_t idx) {
    -        error_idx = idx;
    -        parser_error_counts.count();
    -    }
    -    table parser_error_count_and_convert {
    -        key = {
    -            istd.parser_error : exact;
    -        }
    -        actions = {
    -            set_error_idx;
    -        }
    -        default_action = set_error_idx(0);
    -        const entries = {
    -            error.NoError               : set_error_idx(1);
    -            error.PacketTooShort        : set_error_idx(2);
    -            error.NoMatch               : set_error_idx(3);
    -            error.StackOutOfBounds      : set_error_idx(4);
    -            error.HeaderTooShort        : set_error_idx(5);
    -            error.ParserTimeout         : set_error_idx(6);
    -            error.BadIPv4HeaderChecksum : set_error_idx(7);
    -            error.UnhandledIPv4Options  : set_error_idx(8);
    -        }
    -        psa_direct_counter = parser_error_counts;
    -    }
    -    apply {
    -        if (istd.parser_error != error.NoError) {
    -            // Example code showing how to count number of times each
    -            // kind of parser error was seen.
    -            parser_error_count_and_convert.apply();
    -            ingress_drop(ostd);
    -            exit;
    -        }
    -        // Do normal packet processing here.
    -    }
    -}
    -

    The partial program below demonstrates one way to use the InternetChecksum +} + +typedef bit<32> PacketCounter_t; +typedef bit<8> ErrorIndex_t; + +const bit<9> NUM_ERRORS = 256; + +parser IngressParserImpl(packet_in buffer, + out headers hdr, + inout metadata user_meta, + in psa_ingress_parser_input_metadata_t istd, + in empty_metadata_t resubmit_meta, + in empty_metadata_t recirculate_meta) +{ + InternetChecksum() ck; + state start { + buffer.extract(hdr.ethernet); + transition select(hdr.ethernet.etherType) { + 0x0800: parse_ipv4; + default: accept; + } + } + state parse_ipv4 { + buffer.extract(hdr.ipv4); + // TBD: It would be good to enhance this example to + // demonstrate checking of IPv4 header checksums for IPv4 + // headers with options, but this example does not handle such + // packets. + verify(hdr.ipv4.ihl == 5, error.UnhandledIPv4Options); + ck.clear(); + ck.add({ + /* 16-bit word 0 */ hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv, + /* 16-bit word 1 */ hdr.ipv4.totalLen, + /* 16-bit word 2 */ hdr.ipv4.identification, + /* 16-bit word 3 */ hdr.ipv4.flags, hdr.ipv4.fragOffset, + /* 16-bit word 4 */ hdr.ipv4.ttl, hdr.ipv4.protocol, + /* 16-bit word 5 skip hdr.ipv4.hdrChecksum, */ + /* 16-bit words 6-7 */ hdr.ipv4.srcAddr, + /* 16-bit words 8-9 */ hdr.ipv4.dstAddr + }); + // The verify statement below will cause the parser to enter + // the reject state, and thus terminate parsing immediately, + // if the IPv4 header checksum is wrong. It will also record + // the error error.BadIPv4HeaderChecksum, which will be + // available in a metadata field in the ingress control block. + verify(ck.get() == hdr.ipv4.hdrChecksum, + error.BadIPv4HeaderChecksum); + transition select(hdr.ipv4.protocol) { + 6: parse_tcp; + default: accept; + } + } + state parse_tcp { + buffer.extract(hdr.tcp); + transition accept; + } +} + +control ingress(inout headers hdr, + inout metadata user_meta, + in psa_ingress_input_metadata_t istd, + inout psa_ingress_output_metadata_t ostd) +{ + // Table parser_error_count_and_convert below shows one way to + // count the number of times each parser error was encountered. + // Although it is not used in this example program, it also shows + // how to convert the error value into a unique bit vector value + // 'error_idx', which can be useful if you wish to put a bit + // vector encoding of an error into a packet header, e.g. for a + // packet sent to the control CPU. + + DirectCounter<PacketCounter_t>(PSA_CounterType_t.PACKETS) parser_error_counts; + ErrorIndex_t error_idx; + + action set_error_idx (ErrorIndex_t idx) { + error_idx = idx; + parser_error_counts.count(); + } + table parser_error_count_and_convert { + key = { + istd.parser_error : exact; + } + actions = { + set_error_idx; + } + default_action = set_error_idx(0); + const entries = { + error.NoError : set_error_idx(1); + error.PacketTooShort : set_error_idx(2); + error.NoMatch : set_error_idx(3); + error.StackOutOfBounds : set_error_idx(4); + error.HeaderTooShort : set_error_idx(5); + error.ParserTimeout : set_error_idx(6); + error.BadIPv4HeaderChecksum : set_error_idx(7); + error.UnhandledIPv4Options : set_error_idx(8); + } + psa_direct_counter = parser_error_counts; + } + apply { + if (istd.parser_error != error.NoError) { + // Example code showing how to count number of times each + // kind of parser error was seen. + parser_error_count_and_convert.apply(); + ingress_drop(ostd); + exit; + } + // Do normal packet processing here. + } +} +

    +
    +
    +

    The partial program below demonstrates one way to use the InternetChecksum extern to calculate and then fill in a correct IPv4 header checksum in the deparser block. In this example, the checksum is calculated fresh, so the outgoing checksum will be correct regardless of what changes might have been made to the IPv4 header fields in the Ingress -(or Egress) control block that precedes it. -

    -
    -
    -
    control EgressDeparserImpl(packet_out packet,
    -                           out empty_metadata_t clone_e2e_meta,
    -                           out empty_metadata_t recirculate_meta,
    -                           inout headers hdr,
    -                           in metadata meta,
    -                           in psa_egress_output_metadata_t istd,
    -                           in psa_egress_deparser_input_metadata_t edstd)
    -{
    -    InternetChecksum() ck;
    -    apply {
    -        ck.clear();
    -        ck.add({
    -            /* 16-bit word  0   */ hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv,
    -            /* 16-bit word  1   */ hdr.ipv4.totalLen,
    -            /* 16-bit word  2   */ hdr.ipv4.identification,
    -            /* 16-bit word  3   */ hdr.ipv4.flags, hdr.ipv4.fragOffset,
    -            /* 16-bit word  4   */ hdr.ipv4.ttl, hdr.ipv4.protocol,
    -            /* 16-bit word  5 skip hdr.ipv4.hdrChecksum, */
    -            /* 16-bit words 6-7 */ hdr.ipv4.srcAddr,
    -            /* 16-bit words 8-9 */ hdr.ipv4.dstAddr
    -            });
    -        hdr.ipv4.hdrChecksum = ck.get();
    -        packet.emit(hdr.ethernet);
    -        packet.emit(hdr.ipv4);
    -        packet.emit(hdr.tcp);
    -    }
    -}
    -

    As a final example, we can use the InternetChecksum to compute an +(or Egress) control block that precedes it.

    +
    +
    +
    +
    control EgressDeparserImpl(packet_out packet,
    +                           out empty_metadata_t clone_e2e_meta,
    +                           out empty_metadata_t recirculate_meta,
    +                           inout headers hdr,
    +                           in metadata meta,
    +                           in psa_egress_output_metadata_t istd,
    +                           in psa_egress_deparser_input_metadata_t edstd)
    +{
    +    InternetChecksum() ck;
    +    apply {
    +        ck.clear();
    +        ck.add({
    +            /* 16-bit word  0   */ hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv,
    +            /* 16-bit word  1   */ hdr.ipv4.totalLen,
    +            /* 16-bit word  2   */ hdr.ipv4.identification,
    +            /* 16-bit word  3   */ hdr.ipv4.flags, hdr.ipv4.fragOffset,
    +            /* 16-bit word  4   */ hdr.ipv4.ttl, hdr.ipv4.protocol,
    +            /* 16-bit word  5 skip hdr.ipv4.hdrChecksum, */
    +            /* 16-bit words 6-7 */ hdr.ipv4.srcAddr,
    +            /* 16-bit words 8-9 */ hdr.ipv4.dstAddr
    +            });
    +        hdr.ipv4.hdrChecksum = ck.get();
    +        packet.emit(hdr.ethernet);
    +        packet.emit(hdr.ipv4);
    +        packet.emit(hdr.tcp);
    +    }
    +}
    +
    +
    +
    +

    As a final example, we can use the InternetChecksum to compute an incremental checksum for the TCP header. Recall the TCP checksum is -computed over the entire packet, including the payload. Because the +computed over the entire packet, including the payload. Because the packet payload need not be available in a PSA implementation, we assume that the TCP checksum on the original packet is correct, and update it incrementally by -invoking subtract and then add on any fields that are modified by -the program. For example, the Ingress control in the program below +invoking subtract and then add on any fields that are modified by +the program. For example, the Ingress control in the program below updates the IPv4 source address, recording the original source address -in a metadata field: -

    -
    -
    -
    control ingress(inout headers hdr,
    -                inout metadata user_meta,
    -                in    psa_ingress_input_metadata_t  istd,
    -                inout psa_ingress_output_metadata_t ostd) {
    -    action drop() {
    -      ingress_drop(ostd);
    -    }
    -    action forward(PortId_t port, bit<32> srcAddr) {
    -      user_meta.fwd_metadata.old_srcAddr = hdr.ipv4.srcAddr;
    -      hdr.ipv4.srcAddr = srcAddr;
    -      send_to_port(ostd, port);      
    -    }
    -    table route {
    -        key = { hdr.ipv4.dstAddr : lpm; }
    -        actions = {
    -          forward;
    -          drop;
    -        }
    -    }
    -    apply {
    -        if(hdr.ipv4.isValid()) {
    -          route.apply();
    -        }
    -    }
    -}
    -

    The deparser first updates the IPv4 checksum as above, and then -incrementally computes the TCP checksum. -

    -
    -
    -
    control EgressDeparserImpl(packet_out packet,
    -                           out empty_metadata_t clone_e2e_meta,
    -                           out empty_metadata_t recirculate_meta,
    -                           inout headers hdr,
    -                           in metadata user_meta,
    -                           in psa_egress_output_metadata_t istd,
    -                           in psa_egress_deparser_input_metadata_t edstd)
    -{
    -    InternetChecksum() ck;
    -    apply {
    -        // Update IPv4 checksum
    -        // This clear() call can be removed without affecting
    -        // behavior, as an InternetCheckum instance is automatically
    -        // cleared for each packet.
    -        ck.clear();
    -        ck.add({
    -            /* 16-bit word  0   */ hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv,
    -            /* 16-bit word  1   */ hdr.ipv4.totalLen,
    -            /* 16-bit word  2   */ hdr.ipv4.identification,
    -            /* 16-bit word  3   */ hdr.ipv4.flags, hdr.ipv4.fragOffset,
    -            /* 16-bit word  4   */ hdr.ipv4.ttl, hdr.ipv4.protocol,
    -            /* 16-bit word  5 skip hdr.ipv4.hdrChecksum, */
    -            /* 16-bit words 6-7 */ hdr.ipv4.srcAddr,
    -            /* 16-bit words 8-9 */ hdr.ipv4.dstAddr
    -            });
    -        hdr.ipv4.hdrChecksum = ck.get();
    -        // Update TCP checksum
    -        // This clear() call is necessary for correct behavior, since
    -        // the same instance 'ck' is reused from above for the same
    -        // packet.  If a second InternetChecksum instance other than
    -        // 'ck' were used below instead, this clear() call would be
    -        // unnecessary.
    -        ck.clear();
    -        // Subtract the original TCP checksum
    -        ck.subtract(hdr.tcp.checksum);
    -        // Subtract the effect of the original IPv4 source address,
    -        // which is part of the TCP 'pseudo-header' for the purposes
    -        // of TCP checksum calculation (see RFC 793), then add the
    -        // effect of the new IPv4 source address.
    -        ck.subtract(user_meta.fwd_metadata.old_srcAddr);
    -        ck.add(hdr.ipv4.srcAddr);
    -        hdr.tcp.checksum = ck.get();
    -        packet.emit(hdr.ethernet);
    -        packet.emit(hdr.ipv4);
    -        packet.emit(hdr.tcp);
    -    }
    -}

    7.7. Counters

    -

    Counters are a mechanism for keeping statistics. The control plane +in a metadata field:

    +
    +
    +
    +
    control ingress(inout headers hdr,
    +                inout metadata user_meta,
    +                in    psa_ingress_input_metadata_t  istd,
    +                inout psa_ingress_output_metadata_t ostd) {
    +    action drop() {
    +      ingress_drop(ostd);
    +    }
    +    action forward(PortId_t port, bit<32> srcAddr) {
    +      user_meta.fwd_metadata.old_srcAddr = hdr.ipv4.srcAddr;
    +      hdr.ipv4.srcAddr = srcAddr;
    +      send_to_port(ostd, port);      
    +    }
    +    table route {
    +        key = { hdr.ipv4.dstAddr : lpm; }
    +        actions = {
    +          forward;
    +          drop;
    +        }
    +    }
    +    apply {
    +        if(hdr.ipv4.isValid()) {
    +          route.apply();
    +        }
    +    }
    +}
    +
    +
    +
    +

    The deparser first updates the IPv4 checksum as above, and then +incrementally computes the TCP checksum.

    +
    +
    +
    +
    control EgressDeparserImpl(packet_out packet,
    +                           out empty_metadata_t clone_e2e_meta,
    +                           out empty_metadata_t recirculate_meta,
    +                           inout headers hdr,
    +                           in metadata user_meta,
    +                           in psa_egress_output_metadata_t istd,
    +                           in psa_egress_deparser_input_metadata_t edstd)
    +{
    +    InternetChecksum() ck;
    +    apply {
    +        // Update IPv4 checksum
    +        // This clear() call can be removed without affecting
    +        // behavior, as an InternetCheckum instance is automatically
    +        // cleared for each packet.
    +        ck.clear();
    +        ck.add({
    +            /* 16-bit word  0   */ hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv,
    +            /* 16-bit word  1   */ hdr.ipv4.totalLen,
    +            /* 16-bit word  2   */ hdr.ipv4.identification,
    +            /* 16-bit word  3   */ hdr.ipv4.flags, hdr.ipv4.fragOffset,
    +            /* 16-bit word  4   */ hdr.ipv4.ttl, hdr.ipv4.protocol,
    +            /* 16-bit word  5 skip hdr.ipv4.hdrChecksum, */
    +            /* 16-bit words 6-7 */ hdr.ipv4.srcAddr,
    +            /* 16-bit words 8-9 */ hdr.ipv4.dstAddr
    +            });
    +        hdr.ipv4.hdrChecksum = ck.get();
    +        // Update TCP checksum
    +        // This clear() call is necessary for correct behavior, since
    +        // the same instance 'ck' is reused from above for the same
    +        // packet.  If a second InternetChecksum instance other than
    +        // 'ck' were used below instead, this clear() call would be
    +        // unnecessary.
    +        ck.clear();
    +        // Subtract the original TCP checksum
    +        ck.subtract(hdr.tcp.checksum);
    +        // Subtract the effect of the original IPv4 source address,
    +        // which is part of the TCP 'pseudo-header' for the purposes
    +        // of TCP checksum calculation (see RFC 793), then add the
    +        // effect of the new IPv4 source address.
    +        ck.subtract(user_meta.fwd_metadata.old_srcAddr);
    +        ck.add(hdr.ipv4.srcAddr);
    +        hdr.tcp.checksum = ck.get();
    +        packet.emit(hdr.ethernet);
    +        packet.emit(hdr.ipv4);
    +        packet.emit(hdr.tcp);
    +    }
    +}
    +
    +
    +
    +
    +
    +

    8.7. Counters

    +
    +

    Counters are a mechanism for keeping statistics. The control plane can read counter values. A P4 program cannot read counter values, only update them. If you wish to implement a feature involving sequence numbers in packets, for example, use Registers instead -(Section 7.9). -

    -

    Direct counters are counters associated with a particular P4 table, -and are implemented by the extern DirectCounter. There are also -indexed counters, which are implemented by the extern Counter. The -primary differences between direct counters and indexed counters are: -

    -
      -
    • Number of independently updatable counter values: - -
        -
      • A single instantiation of a direct counter always contains as many +(Section 8.9).

        +
    +
    +

    Direct counters are counters associated with a particular P4 table, +and are implemented by the extern DirectCounter. There are also +indexed counters, which are implemented by the extern Counter. The +primary differences between direct counters and indexed counters are:

    +
    +
    +
      +
    • +

      Number of independently updatable counter values:

      +
      +
        +
      • +

        A single instantiation of a direct counter always contains as many independent counter values as the number of entries in the table -with which it is associated. +with which it is associated.

      • -
      • You must specify the number of independent counter values for an +
      • +

        You must specify the number of independent counter values for an indexed counter when instantiating it. This number of counters -need not be the same as the size of any table. -

    • -
    • Where counter updates are allowed in the P4 program: - -
        -
      • For a direct counter, you may only invoke its count method from +need not be the same as the size of any table.

        +
      • +
      +
    + +
  • +

    Where counter updates are allowed in the P4 program:

    +
    +
      +
    • +

      For a direct counter, you may only invoke its count method from inside the actions of the table with which it is associated, and this always updates the counter value associated with the matching -table entry. +table entry.

    • -
    • For an indexed counter, you may invoke its count method +
    • +

      For an indexed counter, you may invoke its count method anywhere in the P4 program where extern object method invocations -are permitted (e.g. inside actions, or directly inside a control's -apply block), and every such invocation must specify the index -of the counter value to be updated. -

  • - -

    Counters are only intended to support packet counters and byte -counters, or a combination of both called PACKETS_AND_BYTES. The +are permitted (e.g. inside actions, or directly inside a control’s +apply block), and every such invocation must specify the index +of the counter value to be updated.

    + + +
    + + +
    +
    +

    Counters are only intended to support packet counters and byte +counters, or a combination of both called PACKETS_AND_BYTES. The byte counts are always increased by some measure of the packet length, where the packet length used might vary from one PSA implementation to another. For example, one implementation might use the Ethernet frame @@ -2813,760 +3891,927 @@

    If you wish to keep counts of other quantities, or to have more +how it determines the packet length used for byte counter updates.

    +

    +
    +

    If you wish to keep counts of other quantities, or to have more precise control over the packet length used in a byte counter, you may -use Registers to achieve that (Section 7.9). -

    7.7.1. Counter types

    -
    -
    -
    enum PSA_CounterType_t {
    -    PACKETS,
    -    BYTES,
    +use Registers to achieve that (Section 8.9).

    +
    +
    +

    8.7.1. Counter types

    +
    +
    +
    enum PSA_CounterType_t {
    +    PACKETS,
    +    BYTES,
         PACKETS_AND_BYTES
    -}

    7.7.2. Counter

    -
    -
    -
    /// Indirect counter with n_counters independent counter values, where
    -/// every counter value has a data plane size specified by type W.
    -
    -@noWarn("unused")
    -extern Counter<W, S> {
    -  Counter(bit<32> n_counters, PSA_CounterType_t type);
    -  void count(in S index);
    -}
    -

    See section C for pseudocode of an -example implementation of the Counter extern. -

    -

    PSA implementations must not update any counter values if an indexed +} +

    +
    +
    +
    +

    8.7.2. Counter

    +
    +
    +
    /// Indirect counter with n_counters independent counter values, where
    +/// every counter value has a data plane size specified by type W.
    +
    +@noWarn("unused")
    +extern Counter<W, S> {
    +  Counter(bit<32> n_counters, PSA_CounterType_t type);
    +  void count(in S index);
    +}
    +
    +
    +
    +

    See Appendix C for pseudocode of an +example implementation of the Counter extern.

    +
    +
    +

    PSA implementations must not update any counter values if an indexed counter is updated with an index that is too large. It is recommended that they count such erroneous attempted updates, and record other -information that can help an P4 programmer debug such errors. -

    7.7.3. Direct Counter

    -
    -
    -
    @noWarn("unused")
    -extern DirectCounter<W> {
    -  DirectCounter(PSA_CounterType_t type);
    -  void count();
    -}
    -

    A DirectCounter instance must appear as the value of the -psa_direct_counter table attribute for at most one table. We call -this table the DirectCounter instance's “owner”. It is an error to -call the count method for a DirectCounter instance anywhere except -inside an action of its owner table. -

    -

    The counter value updated by an invocation of count is always the -one associated with the table entry that matched. -

    -

    An action of an owner table need not have count method calls for all -of the DirectCounter instances that the table owns. You must use an -explicit count() method call on a DirectCounter to update it, -otherwise its state will not change. -

    -

    An example implementation for the DirectCounter extern is -essentially the same as the one for Counter. Since there is no -index parameter to the count method, there is no need to check for -whether it is in range. -

    -

    The rules here mean that an action that calls count on a -DirectCounter instance may only be an action of that instance's one -owner table. If you want to have a single action A that can be +information that can help an P4 programmer debug such errors.

    +
    +
    +
    +

    8.7.3. Direct Counter

    +
    +
    +
    @noWarn("unused")
    +extern DirectCounter<W> {
    +  DirectCounter(PSA_CounterType_t type);
    +  void count();
    +}
    +
    +
    +
    +

    A DirectCounter instance must appear as the value of the +psa_direct_counter table attribute for at most one table. We call +this table the DirectCounter instance’s "owner". It is an error to +call the count method for a DirectCounter instance anywhere except +inside an action of its owner table.

    +
    +
    +

    The counter value updated by an invocation of count is always the +one associated with the table entry that matched.

    +
    +
    +

    An action of an owner table need not have count method calls for all +of the DirectCounter instances that the table owns. You must use an +explicit count() method call on a DirectCounter to update it, +otherwise its state will not change.

    +
    +
    +

    An example implementation for the DirectCounter extern is +essentially the same as the one for Counter. Since there is no +index parameter to the count method, there is no need to check for +whether it is in range.

    +
    +
    +

    The rules here mean that an action that calls count on a +DirectCounter instance may only be an action of that instance’s one +owner table. If you want to have a single action A that can be invoked by multiple tables, you can still do so by having a unique -action for each such table with a DirectCounter, where each such -action in turn calls action A, in addition to any count -invocations they have. -

    -

    A DirectCounter instance must have a counter value associated with +action for each such table with a DirectCounter, where each such +action in turn calls action A, in addition to any count +invocations they have.

    +
    +
    +

    A DirectCounter instance must have a counter value associated with its owner table that is updated when there is a default action assigned to the table, and a search of the table results in a miss. If there is no default action assigned to the table, then there need not be any counter updated when a search of the table results in a -miss. -

    -

    By “a default action is assigned to a table”, we mean that either the -table has a default_action table property with an action assigned to +miss.

    +
    +
    +

    By "a default action is assigned to a table", we mean that either the +table has a default_action table property with an action assigned to it in the P4 program, or the control plane has made an explicit call to assign the table a default action. If neither of these is true, -then there is no default action assigned to the table. -

    7.7.4. Example program using counters

    -

    The following partial P4 program demonstrates the instantiation and -updating of Counter and DirectCounter externs. -

    -
    -
    -
    typedef bit<48> ByteCounter_t;
    -typedef bit<32> PacketCounter_t;
    -typedef bit<80> PacketByteCounter_t;
    -
    -const bit<32> NUM_PORTS = 512;
    -
    -struct headers {
    -    ethernet_t       ethernet;
    -    ipv4_t           ipv4;
    -}
    -
    -control ingress(inout headers hdr,
    -                inout metadata user_meta,
    -                in    psa_ingress_input_metadata_t  istd,
    -                inout psa_ingress_output_metadata_t ostd)
    -{
    -    Counter<ByteCounter_t, PortId_t>(NUM_PORTS, PSA_CounterType_t.BYTES)
    -        port_bytes_in;
    -    DirectCounter<PacketByteCounter_t>(PSA_CounterType_t.PACKETS_AND_BYTES)
    -        per_prefix_pkt_byte_count;
    -
    -    action next_hop(PortId_t oport) {
    -        per_prefix_pkt_byte_count.count();
    -        send_to_port(ostd, oport);
    -    }
    -    action default_route_drop() {
    -        per_prefix_pkt_byte_count.count();
    -        ingress_drop(ostd);
    -    }
    -    table ipv4_da_lpm {
    -        key = { hdr.ipv4.dstAddr: lpm; }
    -        actions = {
    -            next_hop;
    -            default_route_drop;
    -        }
    -        default_action = default_route_drop;
    -        // table ipv4_da_lpm owns this DirectCounter instance
    -        psa_direct_counter = per_prefix_pkt_byte_count;
    -    }
    -    apply {
    -        port_bytes_in.count(istd.ingress_port);
    -        if (hdr.ipv4.isValid()) {
    -            ipv4_da_lpm.apply();
    -        }
    -    }
    -}
    -
    -control egress(inout headers hdr,
    -               inout metadata user_meta,
    -               in    psa_egress_input_metadata_t  istd,
    -               inout psa_egress_output_metadata_t ostd)
    -{
    -    Counter<ByteCounter_t, PortId_t>(NUM_PORTS, PSA_CounterType_t.BYTES)
    -        port_bytes_out;
    -    apply {
    -        // By doing these stats updates on egress, then because
    -        // multicast replication happens before egress processing,
    -        // this update will occur once for each copy made, which in
    -        // this example is intentional.
    -        port_bytes_out.count(istd.egress_port);
    -    }
    -}

    7.8. Meters

    -

    Meters (RFC 2698) are a more complex mechanism for keeping statistics +then there is no default action assigned to the table.

    +
    +
    +
    +

    8.7.4. Example program using counters

    +
    +

    The following partial P4 program demonstrates the instantiation and +updating of Counter and DirectCounter externs.

    +
    +
    +
    +
    typedef bit<48> ByteCounter_t;
    +typedef bit<32> PacketCounter_t;
    +typedef bit<80> PacketByteCounter_t;
    +
    +const bit<32> NUM_PORTS = 512;
    +
    +struct headers {
    +    ethernet_t       ethernet;
    +    ipv4_t           ipv4;
    +}
    +
    +control ingress(inout headers hdr,
    +                inout metadata user_meta,
    +                in    psa_ingress_input_metadata_t  istd,
    +                inout psa_ingress_output_metadata_t ostd)
    +{
    +    Counter<ByteCounter_t, PortId_t>(NUM_PORTS, PSA_CounterType_t.BYTES)
    +        port_bytes_in;
    +    DirectCounter<PacketByteCounter_t>(PSA_CounterType_t.PACKETS_AND_BYTES)
    +        per_prefix_pkt_byte_count;
    +
    +    action next_hop(PortId_t oport) {
    +        per_prefix_pkt_byte_count.count();
    +        send_to_port(ostd, oport);
    +    }
    +    action default_route_drop() {
    +        per_prefix_pkt_byte_count.count();
    +        ingress_drop(ostd);
    +    }
    +    table ipv4_da_lpm {
    +        key = { hdr.ipv4.dstAddr: lpm; }
    +        actions = {
    +            next_hop;
    +            default_route_drop;
    +        }
    +        default_action = default_route_drop;
    +        // table ipv4_da_lpm owns this DirectCounter instance
    +        psa_direct_counter = per_prefix_pkt_byte_count;
    +    }
    +    apply {
    +        port_bytes_in.count(istd.ingress_port);
    +        if (hdr.ipv4.isValid()) {
    +            ipv4_da_lpm.apply();
    +        }
    +    }
    +}
    +
    +control egress(inout headers hdr,
    +               inout metadata user_meta,
    +               in    psa_egress_input_metadata_t  istd,
    +               inout psa_egress_output_metadata_t ostd)
    +{
    +    Counter<ByteCounter_t, PortId_t>(NUM_PORTS, PSA_CounterType_t.BYTES)
    +        port_bytes_out;
    +    apply {
    +        // By doing these stats updates on egress, then because
    +        // multicast replication happens before egress processing,
    +        // this update will occur once for each copy made, which in
    +        // this example is intentional.
    +        port_bytes_out.count(istd.egress_port);
    +    }
    +}
    +
    +
    +
    +
    +
    +

    8.8. Meters

    +
    +

    Meters (RFC 2698) are a more complex mechanism for keeping statistics about packets, most often used for dropping -or “marking” packets that exceed an average packet or bit rate. To +or "marking" packets that exceed an average packet or bit rate. To mark a packet means to change one or more of its quality of service values in packet headers such as the 802.1Q PCP (priority code point) or DSCP (differentiated service code point) bits within the IPv4 or IPv6 type of service byte. The meters specified in the PSA are -3-color meters. -

    -

    PSA meters do not require any particular drop or marking actions, nor +3-color meters.

    +
    +
    +

    PSA meters do not require any particular drop or marking actions, nor do they automatically implement those behaviors for you. Meters keep -enough state, and update their state during execute() method calls, -in such a way that they return a GREEN (also known as conform), -YELLOW (exceed), or RED (violate) result. See RFC 2698 for details on the +enough state, and update their state during execute() method calls, +in such a way that they return a GREEN (also known as conform), +YELLOW (exceed), or RED (violate) result. See RFC 2698 for details on the conditions under which one of these three results is returned. The P4 program is responsible for examining that returned result, and making changes to packet forwarding behavior as a result. The value returned by an uninitialized meter shall be GREEN. This is in -accordance with the P4Runtime specification. -

    -

    RFC 2698 describes “color aware” and “color blind” variations of -meters. The Meter and DirectMeter externs implement both. The -only difference is in which execute method you use when updating -them. See the comments on the extern definitions below. -

    -

    Similar to counters, there are two flavors of meters: indexed and +accordance with the P4Runtime specification.

    +
    +
    +

    RFC 2698 describes "color aware" and "color blind" variations of +meters. The Meter and DirectMeter externs implement both. The +only difference is in which execute method you use when updating +them. See the comments on the extern definitions below.

    +
    +
    +

    Similar to counters, there are two flavors of meters: indexed and direct. (Indexed) meters are addressed by index, while direct meters always update a meter state corresponding to the matched table entry or action, and from the control plane API are addressed using -P4Runtime table entry as key. -

    -

    There are many other similarities between counters and meters, -including: -

    -
      -
    • The number of independently updatable meter values. +P4Runtime table entry as key.

      +
    +
    +

    There are many other similarities between counters and meters, +including:

    +
    +
    +
      +
    • +

      The number of independently updatable meter values.

    • -
    • Where meter updates are allowed in a P4 program. +
    • +

      Where meter updates are allowed in a P4 program.

    • -
    • For BYTES type meters, the packet length used in the update is +
    • +

      For BYTES type meters, the packet length used in the update is determined by the PSA implementation, and can vary from one PSA -implementation to another. -

    - -

    Further similarities between direct counters and direct meters -include: -

    -
      -
    • DirectMeter execute method calls must be performed within -actions invoked by the table that owns the DirectMeter instance. -It is optional for such an action to call the execute method. -
    • -
    • There must be a meter state associated with a DirectMeter -instance's owner table, that can be updated when the table result is -a miss. As for a DirectCounter, this state only needs to exist if -a default action is assigned to the table. -
    - -

    The table attribute to specify that a table owns a DirectMeter -instance is psa_direct_meter. The value of this table attribute is -a DirectMeter instance name. -

    -

    As for counters, if you call the execute(idx) method on an indexed -meter and idx is at least the number of meter states, so idx is -out of range, no meter state is updated. The execute call still -returns a value of type PSA_MeterColor_t, but the value is undefined -programs that wish to have predictable behavior across implementations +implementation to another.

    + + +
    +
    +

    Further similarities between direct counters and direct meters +include:

    +
    +
    +
      +
    • +

      DirectMeter execute method calls must be performed within +actions invoked by the table that owns the DirectMeter instance. +It is optional for such an action to call the execute method.

      +
    • +
    • +

      There must be a meter state associated with a DirectMeter +instance’s owner table, that can be updated when the table result is +a miss. As for a DirectCounter, this state only needs to exist if +a default action is assigned to the table.

      +
    • +
    +
    +
    +

    The table attribute to specify that a table owns a DirectMeter +instance is psa_direct_meter. The value of this table attribute is +a DirectMeter instance name.

    +
    +
    +

    As for counters, if you call the execute(idx) method on an indexed +meter and idx is at least the number of meter states, so idx is +out of range, no meter state is updated. The execute call still +returns a value of type PSA_MeterColor_t, but the value is undefined — programs that wish to have predictable behavior across implementations must not use the undefined value in a way that affects the output packet or other side effects. The example code below shows one way to achieve predictable behavior. Note that this undefined behavior -cannot occur if the value of n_meters of an indexed meter is $2^W$, -and the type S used to construct the meter is bit<W>, since the -index value could never be out of range. -

    -
    -
    -
    #define METER1_SIZE 100
    -Meter<bit<7>>(METER1_SIZE, PSA_MeterType_t.BYTES) meter1;
    -bit<7> idx;
    -PSA_MeterColor_t color1;
    -
    -// ... later ...
    -
    -if (idx < METER1_SIZE) {
    -    color1 = meter1.execute(idx, PSA_MeterColor_t.GREEN);
    -} else {
    -    // If idx is out of range, use a default value for color1.  One
    -    // may also choose to store an error flag in some metadata field.
    -    color1 = PSA_MeterColor_t.RED;
    -}
    -

    Any implementation will have a finite range that can be specified for +cannot occur if the value of n_meters of an indexed meter is $2^W$, +and the type S used to construct the meter is bit<W>, since the +index value could never be out of range.

    +
    +
    +
    +
    #define METER1_SIZE 100
    +Meter<bit<7>>(METER1_SIZE, PSA_MeterType_t.BYTES) meter1;
    +bit<7> idx;
    +PSA_MeterColor_t color1;
    +
    +// ... later ...
    +
    +if (idx < METER1_SIZE) {
    +    color1 = meter1.execute(idx, PSA_MeterColor_t.GREEN);
    +} else {
    +    // If idx is out of range, use a default value for color1.  One
    +    // may also choose to store an error flag in some metadata field.
    +    color1 = PSA_MeterColor_t.RED;
    +}
    +
    +
    +
    +

    Any implementation will have a finite range that can be specified for the Peak Burst Size and Committed Burst Size. An implementation should document the maximum burst sizes they support, and if the implementation internally truncates the values that the control plane requests to something more coarse than any number of bytes, that should also be documented. It is recommended that the maximum burst sizes be allowed as large as the number of bytes that can be -transmitted across the implementation's maximum speed port in 100 -milliseconds. -

    -

    Implementations will also have finite ranges and precisions that they +transmitted across the implementation’s maximum speed port in 100 +milliseconds.

    +
    +
    +

    Implementations will also have finite ranges and precisions that they support for the Peak Information Rate and Committed Information Rate. An implementation should document the maximum rate it supports, as well as the precision it supports for implementing requested rates. It is recommended that the maximum rate supported be at least the rate -of the implementation's fastest port, and that the actual implemented -rate should always be within plus or minus 0.1% of the requested rate. -

    7.8.1. Meter types

    -
    -
    -
    enum PSA_MeterType_t {
    -    PACKETS,
    +of the implementation’s fastest port, and that the actual implemented
    +rate should always be within plus or minus 0.1% of the requested rate.

    +
    +
    +

    8.8.1. Meter types

    +
    +
    +
    enum PSA_MeterType_t {
    +    PACKETS,
         BYTES
    -}

    7.8.2. Meter colors

    -
    -
    -
    enum PSA_MeterColor_t { RED, GREEN, YELLOW }

    7.8.3. Meter

    -
    -
    -
    // Indexed meter with n_meters independent meter states.
    -
    -extern Meter<S> {
    -  Meter(bit<32> n_meters, PSA_MeterType_t type);
    -
    -  // Use this method call to perform a color aware meter update (see
    -  // RFC 2698). The color of the packet before the method call was
    -  // made is specified by the color parameter.
    -  PSA_MeterColor_t execute(in S index, in PSA_MeterColor_t color);
    -
    -  // Use this method call to perform a color blind meter update (see
    -  // RFC 2698).  It may be implemented via a call to execute(index,
    -  // MeterColor_t.GREEN), which has the same behavior.
    -  PSA_MeterColor_t execute(in S index);
    -}

    7.8.4. Direct Meter

    -
    -
    -
    extern DirectMeter {
    -  DirectMeter(PSA_MeterType_t type);
    -  // See the corresponding methods for extern Meter.
    -  PSA_MeterColor_t execute(in PSA_MeterColor_t color);
    -  PSA_MeterColor_t execute();
    -}

    7.9. Registers

    -

    Registers are stateful memories whose values can be read and written +} +

    +
    +
    +
    +

    8.8.2. Meter colors

    +
    +
    +
    enum PSA_MeterColor_t { RED, GREEN, YELLOW }
    +
    +
    +
    +
    +

    8.8.3. Meter

    +
    +
    +
    // Indexed meter with n_meters independent meter states.
    +
    +extern Meter<S> {
    +  Meter(bit<32> n_meters, PSA_MeterType_t type);
    +
    +  // Use this method call to perform a color aware meter update (see
    +  // RFC 2698). The color of the packet before the method call was
    +  // made is specified by the color parameter.
    +  PSA_MeterColor_t execute(in S index, in PSA_MeterColor_t color);
    +
    +  // Use this method call to perform a color blind meter update (see
    +  // RFC 2698).  It may be implemented via a call to execute(index,
    +  // MeterColor_t.GREEN), which has the same behavior.
    +  PSA_MeterColor_t execute(in S index);
    +}
    +
    +
    +
    +
    +

    8.8.4. Direct Meter

    +
    +
    +
    extern DirectMeter {
    +  DirectMeter(PSA_MeterType_t type);
    +  // See the corresponding methods for extern Meter.
    +  PSA_MeterColor_t execute(in PSA_MeterColor_t color);
    +  PSA_MeterColor_t execute();
    +}
    +
    +
    +
    +
    +
    +

    8.9. Registers

    +
    +

    Registers are stateful memories whose values can be read and written during packet forwarding under the control of the P4 program. They are similar to counters and meters in that their state can be modified as a result of processing packets, but they are far more general in -the behavior they can implement. -

    -

    Although you may not use register contents directly in table match -keys, you may use the read() method call on the right-hand side of +the behavior they can implement.

    +
    +
    +

    Although you may not use register contents directly in table match +keys, you may use the read() method call on the right-hand side of an assignment statement, which retrieves the current value of the register. You may copy the register value into metadata, and it is -then available for matching in subsequent tables. -

    -

    There are two different constructors for Register instances. The +then available for matching in subsequent tables.

    +
    +
    +

    There are two different constructors for Register instances. The value returned for the uninitialized variant is undefined. The value returned for the initialized variant is the one specified by the -initial_value parameter of the constructor. -

    -

    A simple usage example is to verify that a “first packet” was +initial_value parameter of the constructor.

    +
    +
    +

    A simple usage example is to verify that a "first packet" was seen for a particular type of flow. A register cell would be -allocated to the flow, initialized to “clear”. When the protocol -signaled a “first packet”, the table would match on this value and -update the flow's cell to “marked”. Subsequent packets in the flow +allocated to the flow, initialized to "clear". When the protocol +signaled a "first packet", the table would match on this value and +update the flow’s cell to "marked". Subsequent packets in the flow would be mapped to the same cell; the current cell value would be stored in metadata for the packet and a subsequent table could -check that the flow was marked as active. -

    -
    -
    -
    extern Register<T, S> {
    -  /// Instantiate an array of <size> registers. The initial value is
    -  /// undefined.
    -  Register(bit<32> size);
    -  /// Initialize an array of <size> registers and set their value to
    -  /// initial_value.
    -  Register(bit<32> size, T initial_value);
    -
    -  @noSideEffects
    -  T    read  (in S index);
    -  void write (in S index, in T value);
    -}
    -

    Another example using registers is given below. It implements a +check that the flow was marked as active.

    +
    +
    +
    +
    extern Register<T, S> {
    +  /// Instantiate an array of <size> registers. The initial value is
    +  /// undefined.
    +  Register(bit<32> size);
    +  /// Initialize an array of <size> registers and set their value to
    +  /// initial_value.
    +  Register(bit<32> size, T initial_value);
    +
    +  @noSideEffects
    +  T    read  (in S index);
    +  void write (in S index, in T value);
    +}
    +
    +
    +
    +

    Another example using registers is given below. It implements a packet and byte counter, where the byte counter can be updated by a packet length specified in the P4 program, rather than one chosen by -the PSA implementation. -

    -
    -
    -
    const bit<32> NUM_PORTS = 512;
    -
    -// It would be more convenient to use a struct type to represent the
    -// state of a combined packet and byte count, and many other compound
    -// values one might wish to store in a Register instance.  However,
    -// the latest p4test as of 2018-Feb-10 does not allow a struct type to
    -// be returned from a method call like Register.read().
    -
    -// Refer to this Github issue for status of generalizing this:
    -// https://github.com/p4lang/p4-spec/issues/383
    -
    -#define PACKET_COUNT_WIDTH 32
    -#define BYTE_COUNT_WIDTH 48
    -//#define PACKET_BYTE_COUNT_WIDTH (PACKET_COUNT_WIDTH + BYTE_COUNT_WIDTH)
    -#define PACKET_BYTE_COUNT_WIDTH 80
    -
    -#define PACKET_COUNT_RANGE (PACKET_BYTE_COUNT_WIDTH-1):BYTE_COUNT_WIDTH
    -#define BYTE_COUNT_RANGE (BYTE_COUNT_WIDTH-1):0
    -
    -typedef bit<PACKET_BYTE_COUNT_WIDTH> PacketByteCountState_t;
    -
    -action update_pkt_ip_byte_count (inout PacketByteCountState_t s,
    -                                 in bit<16> ip_length_bytes)
    -{
    -    s[PACKET_COUNT_RANGE] = s[PACKET_COUNT_RANGE] + 1;
    -    s[BYTE_COUNT_RANGE] = (s[BYTE_COUNT_RANGE] +
    -                           (bit<BYTE_COUNT_WIDTH>) ip_length_bytes);
    -}
    -
    -control ingress(inout headers hdr,
    -                inout metadata user_meta,
    -                in    psa_ingress_input_metadata_t  istd,
    -                inout psa_ingress_output_metadata_t ostd)
    -{
    -    Register<PacketByteCountState_t, PortId_t>(NUM_PORTS)
    -        port_pkt_ip_bytes_in;
    -
    -    apply {
    -        ostd.egress_port = (PortId_t) 0;
    -        if (hdr.ipv4.isValid()) {
    -            @atomic {
    -                PacketByteCountState_t tmp;
    -                tmp = port_pkt_ip_bytes_in.read(istd.ingress_port);
    -                update_pkt_ip_byte_count(tmp, hdr.ipv4.totalLen);
    -                port_pkt_ip_bytes_in.write(istd.ingress_port, tmp);
    -            }
    -        }
    -    }
    -}
    -

    Note the use of the @atomic annotation in the block enclosing the -read() and write() method calls on the Register instance. It is -expected to be common that register accesses will need the @atomic +the PSA implementation.

    +
    +
    +
    +
    const bit<32> NUM_PORTS = 512;
    +
    +// It would be more convenient to use a struct type to represent the
    +// state of a combined packet and byte count, and many other compound
    +// values one might wish to store in a Register instance.  However,
    +// the latest p4test as of 2018-Feb-10 does not allow a struct type to
    +// be returned from a method call like Register.read().
    +
    +// Refer to this Github issue for status of generalizing this:
    +// https://github.com/p4lang/p4-spec/issues/383
    +
    +#define PACKET_COUNT_WIDTH 32
    +#define BYTE_COUNT_WIDTH 48
    +//#define PACKET_BYTE_COUNT_WIDTH (PACKET_COUNT_WIDTH + BYTE_COUNT_WIDTH)
    +#define PACKET_BYTE_COUNT_WIDTH 80
    +
    +#define PACKET_COUNT_RANGE (PACKET_BYTE_COUNT_WIDTH-1):BYTE_COUNT_WIDTH
    +#define BYTE_COUNT_RANGE (BYTE_COUNT_WIDTH-1):0
    +
    +typedef bit<PACKET_BYTE_COUNT_WIDTH> PacketByteCountState_t;
    +
    +action update_pkt_ip_byte_count (inout PacketByteCountState_t s,
    +                                 in bit<16> ip_length_bytes)
    +{
    +    s[PACKET_COUNT_RANGE] = s[PACKET_COUNT_RANGE] + 1;
    +    s[BYTE_COUNT_RANGE] = (s[BYTE_COUNT_RANGE] +
    +                           (bit<BYTE_COUNT_WIDTH>) ip_length_bytes);
    +}
    +
    +control ingress(inout headers hdr,
    +                inout metadata user_meta,
    +                in    psa_ingress_input_metadata_t  istd,
    +                inout psa_ingress_output_metadata_t ostd)
    +{
    +    Register<PacketByteCountState_t, PortId_t>(NUM_PORTS)
    +        port_pkt_ip_bytes_in;
    +
    +    apply {
    +        ostd.egress_port = (PortId_t) 0;
    +        if (hdr.ipv4.isValid()) {
    +            @atomic {
    +                PacketByteCountState_t tmp;
    +                tmp = port_pkt_ip_bytes_in.read(istd.ingress_port);
    +                update_pkt_ip_byte_count(tmp, hdr.ipv4.totalLen);
    +                port_pkt_ip_bytes_in.write(istd.ingress_port, tmp);
    +            }
    +        }
    +    }
    +}
    +
    +
    +
    +

    Note the use of the @atomic annotation in the block enclosing the +read() and write() method calls on the Register instance. It is +expected to be common that register accesses will need the @atomic annotation around portions of your program in order to behave as you -desire. As stated in the P416 specification, without the @atomic +desire. As stated in the P416 specification, without the @atomic annotation in this example, an implementation is allowed to process -two packets P1 and P2 in parallel, and perform the register access -operations in this order: -

    -
    -
    -
        // Possible order of operations for the example program if the
    -    // @atomic annotation is _not_ used.
    -
    -    tmp = port_pkt_ip_bytes_in.read(istd.ingress_port);  // for packet P1
    -    tmp = port_pkt_ip_bytes_in.read(istd.ingress_port);  // for packet P2
    -
    -    // At this time, if P1 and P2 came from the same ingress_port,
    -    // each of their values of tmp are identical.
    -
    -    update_pkt_ip_byte_count(tmp, hdr.ipv4.totalLen);    // for packet P1
    -    update_pkt_ip_byte_count(tmp, hdr.ipv4.totalLen);    // for packet P2
    -
    -    port_pkt_ip_bytes_in.write(istd.ingress_port, tmp);  // for packet P1
    -    port_pkt_ip_bytes_in.write(istd.ingress_port, tmp);  // for packet P2
    -    // The write() from packet P1 is lost.
    -

    Since different implementations may have different upper limits on the -complexity of code that they will accept within an @atomic block, we +two packets P1 and P2 in parallel, and perform the register access +operations in this order:

    +
    +
    +
    +
        // Possible order of operations for the example program if the
    +    // @atomic annotation is _not_ used.
    +
    +    tmp = port_pkt_ip_bytes_in.read(istd.ingress_port);  // for packet P1
    +    tmp = port_pkt_ip_bytes_in.read(istd.ingress_port);  // for packet P2
    +
    +    // At this time, if P1 and P2 came from the same ingress_port,
    +    // each of their values of tmp are identical.
    +
    +    update_pkt_ip_byte_count(tmp, hdr.ipv4.totalLen);    // for packet P1
    +    update_pkt_ip_byte_count(tmp, hdr.ipv4.totalLen);    // for packet P2
    +
    +    port_pkt_ip_bytes_in.write(istd.ingress_port, tmp);  // for packet P1
    +    port_pkt_ip_bytes_in.write(istd.ingress_port, tmp);  // for packet P2
    +    // The write() from packet P1 is lost.
    +
    +
    +
    +

    Since different implementations may have different upper limits on the +complexity of code that they will accept within an @atomic block, we recommend you keep them as small as possible, subject to maintaining -your desired correct behavior. -

    -

    Individual counter and meter method calls need not be enclosed in -@atomic blocks to be safe they guarantee atomic behavior of their +your desired correct behavior.

    +
    +
    +

    Individual counter and meter method calls need not be enclosed in +@atomic blocks to be safe — they guarantee atomic behavior of their individual method calls, without losing any updates. Even though the -P416 v1.0.0 language specification currently requires that every -action of a table behave as if its entire body is annotated by an -@atomic annotation, it is recommended to explicitly use @atomic +P416 v1.0.0 language specification currently requires that every +action of a table behave as if its entire body is annotated by an +@atomic annotation, it is recommended to explicitly use @atomic annotations inside of action bodies as if this were not the case, since (a) it is harmless, and more importantly (b) this requirement may be removed in a near future revision of the language -specification. -

    -

    As for indexed counters and meters, access to an index of a register +specification.

    +
    +
    +

    As for indexed counters and meters, access to an index of a register that is at least the size of the register is out of bounds. An out of bounds write has no effect on the state of the system. An out of -bounds read returns an undefined value. See the example in Section -7.8 for one way to write code to guarantee avoiding this +bounds read returns an undefined value. See the example in +Section 8.8 for one way to write code to guarantee avoiding this undefined behavior. Out of bounds register accesses are impossible -for a register instance with type S declared as bit<W> and size -$2^W$ entries. -

    7.10. Random

    -

    The Random extern provides generation of pseudo-random numbers in a +for a register instance with type S declared as bit<W> and size +$2^W$ entries.

    +
    +
    +
    +

    8.10. Random

    +
    +

    The Random extern provides generation of pseudo-random numbers in a specified range with a uniform distribution. If one wishes to generate numbers with a non-uniform distribution, you may do so by first generating a uniformly distributed random value, and then using appropriate table lookups and/or arithmetic on the resulting value to -achieve the desired distribution. -

    -

    An implementation is not required to produce cryptographically strong +achieve the desired distribution.

    +
    +
    +

    An implementation is not required to produce cryptographically strong pseudo-random number generation. For example, a particularly inexpensive implementation might use a linear feedback shift register -to generate values. -

    -
    -
    -
    extern Random<T> {
    -
    -  /// Return a random value in the range [min, max], inclusive.
    -  /// Implementations are allowed to support only ranges where (max -
    -  /// min + 1) is a power of 2.  P4 developers should limit their
    -  /// arguments to such values if they wish to maximize portability.
    -
    -  Random(T min, T max);
    -  T read();
    -}

    7.11. Action Profile

    -

    Action profiles are used as table implementation attributes. -

    -

    Action profiles provide a mechanism to populate table entries +to generate values.

    +
    +
    +
    +
    extern Random<T> {
    +
    +  /// Return a random value in the range [min, max], inclusive.
    +  /// Implementations are allowed to support only ranges where (max -
    +  /// min + 1) is a power of 2.  P4 developers should limit their
    +  /// arguments to such values if they wish to maximize portability.
    +
    +  Random(T min, T max);
    +  T read();
    +}
    +
    +
    +
    +
    +

    8.11. Action Profile

    +
    +

    Action profiles are used as table implementation attributes.

    +
    +
    +

    Action profiles provide a mechanism to populate table entries with action specifications that have been defined outside the table entry specification. An action profile extern can be instantiated as a resource in the P4 program. A table that uses this action profile -must specify its psa_implementation attribute as the action profile -instance. -

    -
    -

    ap -

    -
    - -
    Figure 4. Action profiles in PSA
    -

    Figure 4 contrasts a direct table with a table that has an action -profile implementation. A direct table, as seen in Figure 4 (a) contains -the action specification in each table entry. In this example, the table has a -match key consisting of an LPM on header field h.f. The action is to set the -port. As we can see, entries t1 and t3 have the same action, i.e. to set the -port to 1. Action profiles enable sharing an action across multiple entries by -using a separate table as shown in Figure 4 (b). -

    -

    A table with an action profile implementation has entries that point to a member +must specify its psa_implementation attribute as the action profile +instance.

    +
    +
    +
    +action profile +
    +
    Figure 4. Action profiles in PSA
    +
    +
    +

    Figure 4 contrasts a direct table with a table that has an action +profile implementation. A direct table, as seen in Figure 4 (a) +contains the action specification in each table entry. In this example, +the table has a match key consisting of an LPM on header field h.f. +The action is to set the port. As we can see, entries t1 and t3 have the +same action, i.e. to set the port to 1. Action profiles enable sharing +an action across multiple entries by using a separate table as shown in +Figure 4 (b).

    +
    +
    +

    A table with an action profile implementation has entries that point to a member reference instead of directly defining an action specification. A mapping from member references to action specifications is maintained in a separate table -that is part of the action profile instance defined in the table psa_implementation +that is part of the action profile instance defined in the table psa_implementation attribute. When a table with an action profile implementation is applied, the member reference is resolved and the corresponding action specification is -applied to the packet. -

    -

    Action profile members may only specify action types defined in the actions +applied to the packet.

    +
    +
    +

    Action profile members may only specify action types defined in the actions attribute of the implemented table. An action profile instance may be shared across multiple tables only if all such tables define the same set of actions -in their actions attribute. Tables with an action profile implementation +in their actions attribute. Tables with an action profile implementation cannot define a default action. The default action for such tables is implicitly -set to NoAction. -

    -

    The control plane can add, modify or delete member entries for a +set to NoAction.

    +
    +
    +

    The control plane can add, modify or delete member entries for a given action profile instance. The controller-assigned member reference must be unique in the scope of the action profile instance. An -action profile instance may hold at most size entries as defined in the +action profile instance may hold at most size entries as defined in the constructor parameter. Table entries must specify the action using the controller-assigned reference for the desired member entry. Directly specifying the action as part of the table entry is not allowed for tables with an -action profile implementation. -

    -
    -
    -
    extern ActionProfile {
    -  /// Construct an action profile of 'size' entries
    -  ActionProfile(bit<32> size);
    -}

    7.11.1. Action Profile Example

    -

    The P4 control block Ctrl in the example below instantiates an -action profile ap that can contain at most 128 member entries. Table -indirect uses this instance by specifying the psa_implementation attribute. -The control plane can add member entries to ap, where each member can -specify either a foo or NoAction action. Table entries for indirect -table must specify the action using the controller-assigned member reference. -

    -
    -
    -
    control Ctrl(inout H hdr, inout M meta) {
    -
    -  action foo() { meta.foo = 1; }
    -
    -  ActionProfile(128) ap;
    -
    -  table indirect {
    -    key = {hdr.ipv4.dst_address: exact;}
    -    actions = { foo; NoAction; }
    -    psa_implementation = ap;
    -  }
    -
    -  apply {
    -    indirect.apply();
    -  }
    -}

    7.12. Action Selector

    -

    Action selectors are used as table implementation attributes. -

    -

    Action selectors implement yet another mechanism to populate table +action profile implementation.

    +
    +
    +
    +
    extern ActionProfile {
    +  /// Construct an action profile of 'size' entries
    +  ActionProfile(bit<32> size);
    +}
    +
    +
    +
    +

    8.11.1. Action Profile Example

    +
    +

    The P4 control block Ctrl in the example below instantiates an +action profile ap that can contain at most 128 member entries. Table +indirect uses this instance by specifying the psa_implementation attribute. +The control plane can add member entries to ap, where each member can +specify either a foo or NoAction action. Table entries for indirect +table must specify the action using the controller-assigned member reference.

    +
    +
    +
    +
    control Ctrl(inout H hdr, inout M meta) {
    +
    +  action foo() { meta.foo = 1; }
    +
    +  ActionProfile(128) ap;
    +
    +  table indirect {
    +    key = {hdr.ipv4.dst_address: exact;}
    +    actions = { foo; NoAction; }
    +    psa_implementation = ap;
    +  }
    +
    +  apply {
    +    indirect.apply();
    +  }
    +}
    +
    +
    +
    +
    +
    +

    8.12. Action Selector

    +
    +

    Action selectors are used as table implementation attributes.

    +
    +
    +

    Action selectors implement yet another mechanism to populate table entries with action specifications that have been defined outside the table entry. They are more powerful than action profiles because they also provide the ability to dynamically select the action specification to apply upon matching a table entry. An action selector extern can be instantiated as a resource in the P4 program, similar to action profiles. Furthermore, -a table that uses this action selector must specify its psa_implementation attribute -as the action selector instance. -

    -
    -

    as -

    -
    - -
    Figure 5. Action selectors in PSA
    -

    Figure 5 illustrates a table that has an action selector implementation. +a table that uses this action selector must specify its psa_implementation attribute +as the action selector instance.

    +
    +
    +
    +action selector +
    +
    Figure 5. Action selectors in PSA
    +
    +
    +

    Figure 5 illustrates a table that has an action selector implementation. In this example, the table has a match key consisting of an LPM on header field -h.f. A second match type selector is used to define the fields that are -used to look up the action specification from the selector at runtime. -

    -

    A table with an action action selector implementation consists of entries that -point to either an action profile member reference or an action profile group -reference. An action selector instance can be logically visualized as two tables -as shown in Figure 5. The first table contains a mapping from group -references to a set of member references. The second table contains a mapping -from member references to action specifications. -

    -

    When a packet matches a table entry at runtime, the controller-assigned +h.f. A second match type selector is used to define the fields that are +used to look up the action specification from the selector at runtime.

    +
    +
    +

    A table with an action action selector implementation consists of +entries that point to either an action profile member reference or an +action profile group reference. An action selector instance can be +logically visualized as two tables as shown in Figure 5. The first +table contains a mapping from group references to a set of member +references. The second table contains a mapping from member references +to action specifications.

    +
    +
    +

    When a packet matches a table entry at runtime, the controller-assigned reference of the action profile member or group is read. If the entry points to a member then the corresponding action specification is applied to the packet. However, if the entry points to a group, a dynamic selection algorithm is used to select a member from the group, and the action specification corresponding to that member is applied. The dynamic selection algorithm is -specified as a parameter when instantiating the action selector. -

    -

    Action selector members may only specify action types defined in the -actions attribute of the implemented table. -

    -

    Minimum requirements for a PSA implementation of action selectors: -

    -
      -
    • Support non-empty groups where every action in the same group has -the same action name. -
    • -
    • Within the same group, support arbitrary action parameter values -among different members of the group. -
    • -
    • Support different action names in different groups. -
    • -
    • No predictable data plane behavior is required if a table entry is -matched that points at an empty group. -
    - -

    Optional extensions: -

    -
      -
    • Support non-empty groups where in the same group, different actions +specified as a parameter when instantiating the action selector.

      +
    +
    +

    Action selector members may only specify action types defined in the +actions attribute of the implemented table.

    +
    +
    +

    Minimum requirements for a PSA implementation of action selectors:

    +
    +
    +
      +
    • +

      Support non-empty groups where every action in the same group has +the same action name.

      +
    • +
    • +

      Within the same group, support arbitrary action parameter values +among different members of the group.

      +
    • +
    • +

      Support different action names in different groups.

      +
    • +
    • +

      No predictable data plane behavior is required if a table entry is +matched that points at an empty group.

      +
    • +
    +
    +
    +

    Optional extensions:

    +
    +
    +
      +
    • +

      Support non-empty groups where in the same group, different actions can have different action names, as well as arbitrary action -parameter values. +parameter values.

    • -
    • Support table entries that point at an empty group. When the entry +
    • +

      Support table entries that point at an empty group. When the entry is matched, execute the action assigned to the table property -psa_empty_group_action. -

    - -

    The psa_empty_group_action property of a table is similar to the -default_action property in the following ways: -

    -
      -
    • They both have actions as their values. +psa_empty_group_action.

    • -
    • The P4 source code specifies the initial value. +
    +
    +
    +

    The psa_empty_group_action property of a table is similar to the +default_action property in the following ways:

    +
    +
    +
      +
    • +

      They both have actions as their values.

    • -
    • If the table property psa_empty_group_action is not given in the -P4 source code, its value is NoAction(). +
    • +

      The P4 source code specifies the initial value.

    • -
    • They may have a const modifier, indicating that control software -is not allowed to change this action. +
    • +

      If the table property psa_empty_group_action is not given in the +P4 source code, its value is NoAction().

    • -
    • In the absence of a const modifier, the control software is -allowed to change the action assigned to psa_empty_group_action. -
    - -

    PSA implementers should note that supporting empty groups with +

  • +

    They may have a const modifier, indicating that control software +is not allowed to change this action.

    +
  • +
  • +

    In the absence of a const modifier, the control software is +allowed to change the action assigned to psa_empty_group_action.

    +
  • + +
    +
    +

    PSA implementers should note that supporting empty groups with predictable data plane behavior may be required in a future version of PSA. In some cases, it may be possible for the combination of a PSA data plane plus its P4Runtime server software to achieve this desired behavior, as far as the P4Runtime client controller software can -observe. See Appendix G. -

    -

    An action selector instance may be shared across multiple tables only if all such -tables define the same set of actions in their actions attribute. Furthermore, +observe. See Appendix G.

    +
    +
    +

    An action selector instance may be shared across multiple tables only if all such +tables define the same set of actions in their actions attribute. Furthermore, the selector match fields for such tables must be identical and must be specified in the same order across all tables sharing the selector. Tables with an action selector implementation cannot define a default action. The default action for -such tables is implicitly set to NoAction. -

    -

    The dynamic selection algorithm requires a field list as an input for generating +such tables is implicitly set to NoAction.

    +
    +
    +

    The dynamic selection algorithm requires a field list as an input for generating the index to a member entry in a group. This field list is created by using the -match type selector when defining the table match key. The match fields of -type selector are composed into a field list in the order they are specified. +match type selector when defining the table match key. The match fields of +type selector are composed into a field list in the order they are specified. The composed field list is passed as an input to the action selector -implementation. It is illegal to define a selector type match field if the -table does not have an action selector implementation. -

    -

    The control plane can add, modify or delete member and group entries for a +implementation. It is illegal to define a selector type match field if the +table does not have an action selector implementation.

    +
    +
    +

    The control plane can add, modify or delete member and group entries for a given action selector instance. An action selector instance may hold at most -size member entries as defined in the constructor parameter. The number of +size member entries as defined in the constructor parameter. The number of groups may be at most the size of the table that is implemented by the selector. Table entries must specify the action using a reference to the desired member or group entry. Directly specifying the action as part of the table entry is not -allowed for tables with an action selector implementation. -

    -
    -
    -
    extern ActionSelector {
    -  /// Construct an action selector of 'size' entries
    -  /// @param algo hash algorithm to select a member in a group
    -  /// @param size number of entries in the action selector
    -  /// @param outputWidth size of the key
    -  ActionSelector(PSA_HashAlgorithm_t algo, bit<32> size, bit<32> outputWidth);
    -}

    7.12.1. Action Selector Example

    -

    The P4 control block Ctrl in the example below instantiates an -action selector as that can contain at most 128 member entries. The +allowed for tables with an action selector implementation.

    +
    +
    +
    +
    extern ActionSelector {
    +  /// Construct an action selector of 'size' entries
    +  /// @param algo hash algorithm to select a member in a group
    +  /// @param size number of entries in the action selector
    +  /// @param outputWidth size of the key
    +  ActionSelector(PSA_HashAlgorithm_t algo, bit<32> size, bit<32> outputWidth);
    +}
    +
    +
    +
    +

    8.12.1. Action Selector Example

    +
    +

    The P4 control block Ctrl in the example below instantiates an +action selector as that can contain at most 128 member entries. The action selector uses a crc16 algorithm with output width of 10 bits -to select a member entry within a group. -

    -

    Table indirect_with_selection uses this instance by specifying the -psa_implementation table property as shown. The control plane can add member and group -entries to as. Each member can specify either a foo or NoAction action. -When programming the table entries, the control plane does not include the -fields of match type selector in the match key. The selector match fields are +to select a member entry within a group.

    +
    +
    +

    Table indirect_with_selection uses this instance by specifying the +psa_implementation table property as shown. The control plane can add member and group +entries to as. Each member can specify either a foo or NoAction action. +When programming the table entries, the control plane does not include the +fields of match type selector in the match key. The selector match fields are instead used to compose a list that is passed to the action selector instance. -In the example below, the list {hdr.ipv4.src_address, hdr.ipv4.protocol} is +In the example below, the list {hdr.ipv4.src_address, hdr.ipv4.protocol} is passed as input to the crc16 hash algorithm used for dynamic member selection -by action selector as. -

    -
    -
    -
    control Ctrl(inout H hdr, inout M meta) {
    -
    -  action foo() { meta.foo = 1; }
    -
    -  ActionSelector(PSA_HashAlgorithm_t.CRC16, 128, 10) as;
    -
    -  table indirect_with_selection {
    -    key = {
    -      hdr.ipv4.dst_address: exact;
    -      hdr.ipv4.src_address: selector;
    -      hdr.ipv4.protocol: selector;
    -    }
    -    actions = { foo; NoAction; }
    -    psa_implementation = as;
    -  }
    -
    -  apply {
    -    indirect_with_selection.apply();
    -  }
    -}
    -

    Note that the management of action selector entries in the presence of +by action selector as.

    +
    +
    +
    +
    control Ctrl(inout H hdr, inout M meta) {
    +
    +  action foo() { meta.foo = 1; }
    +
    +  ActionSelector(PSA_HashAlgorithm_t.CRC16, 128, 10) as;
    +
    +  table indirect_with_selection {
    +    key = {
    +      hdr.ipv4.dst_address: exact;
    +      hdr.ipv4.src_address: selector;
    +      hdr.ipv4.protocol: selector;
    +    }
    +    actions = { foo; NoAction; }
    +    psa_implementation = as;
    +  }
    +
    +  apply {
    +    indirect_with_selection.apply();
    +  }
    +}
    +
    +
    +
    +

    Note that the management of action selector entries in the presence of link failures is outside the scope of the PSA. Fast-failover requires information from the control plane and will be addressed as part of the -P4Runtime API6 working group. -

    7.13. Timestamps

    -

    A PSA implementation provides an ingress_timestamp value for every -packet in the Ingress control block, as a field in the struct with -type psa_ingress_input_metadata_t. This timestamp should be close +P4Runtime API[2] working group.

    +
    +
    +
    +
    +

    8.13. Timestamps

    +
    +

    A PSA implementation provides an ingress_timestamp value for every +packet in the Ingress control block, as a field in the struct with +type psa_ingress_input_metadata_t. This timestamp should be close to the time that the first bit of the packet arrived to the device, or alternately, to the time that the device began parsing the packet. -This timestamp is not automatically included with the packet in the -Egress control block. A P4 program wishing to use the value of -ingress_timestamp in egress code must copy it to a user-defined -metadata field that reaches egress. -

    -

    A PSA implementation also provides an egress_timestamp value for -every packet in the Egress control block, as a field of the struct -with type psa_egress_input_metadata_t. -

    -

    One expected use case for timestamps is to store them in tables or -Register instances to implement checking for timeout events for +This timestamp is not automatically included with the packet in the +Egress control block. A P4 program wishing to use the value of +ingress_timestamp in egress code must copy it to a user-defined +metadata field that reaches egress.

    +
    +
    +

    A PSA implementation also provides an egress_timestamp value for +every packet in the Egress control block, as a field of the struct +with type psa_egress_input_metadata_t.

    +
    +
    +

    One expected use case for timestamps is to store them in tables or +Register instances to implement checking for timeout events for protocols, where precision on the order of milliseconds is sufficient -for most protocols. -

    -

    Another expected use case is INT (In-band Network Telemetry7), +for most protocols.

    +
    +
    +

    Another expected use case is INT (In-band Network Telemetry[6]), where precision on the order of microseconds or smaller is necessary to measure queueing latencies that differ by those amounts. It takes only 0.74 microseconds to transmit a 9 Kbyte Ethernet jumbo frame on a -100 gigabit per second link. -

    -

    For these applications, it is recommended that an implementation's +100 gigabit per second link.

    +
    +
    +

    For these applications, it is recommended that an implementation’s timestamp increments at least once every microsecond. Incrementing once per clock cycle in an ASIC or FPGA implementation would be a reasonable choice. The timestamp should increment at a constant rate over time. For example, it should not be a simple count of clock cycles in a device that implements dynamic frequency -scaling8. -

    -

    Timestamps are of type Timestamp_t, which is type bit<W> for a -value of W defined by the implementation. Timestamps are expected +scaling[7].

    +
    +
    +

    Timestamps are of type Timestamp_t, which is type bit<W> for a +value of W defined by the implementation. Timestamps are expected to wrap around during the normal passage of time. It is recommended that an implementation pick a rate of advance and a bit width such that wrapping around occurs at most once every hour. Making the wrap time this long (or longer) makes timestamps more useful for several -use cases. -

    -
      -
    • Checking for timeouts of protocol hello / keep-alive traffic that is -on the order of seconds or minutes. +use cases.

      +
    +
    +
      +
    • +

      Checking for timeouts of protocol hello / keep-alive traffic that is +on the order of seconds or minutes.

    • -
    • If timestamps are placed into packets without converting them to +
    • +

      If timestamps are placed into packets without converting them to other formats, then external data analysis systems using those timestamps will in many cases need to do so, e.g. to compare timestamps stored in packets by different PSA devices. These @@ -3574,547 +4819,655 @@

      If timestamps are converted to other formats within a P4 program, it +
    • +

      If timestamps are converted to other formats within a P4 program, it will need access to parameters that are likely to change every wrap -time, e.g. at least a “base value” to add some calculated value to. +time, e.g. at least a "base value" to add some calculated value to. A straightforward way to do this requires the control plane to -update these values at least once or twice per timestamp wrap time. +update these values at least once or twice per timestamp wrap time.

    • -
    • Programs that wish to use (egress_timestamp - ingress_timestamp) +
    • +

      Programs that wish to use (egress_timestamp - ingress_timestamp) to calculate the queueing latency experienced by a packet need the -wrap time to exceed the maximum queueing latency. -

    - -

    Examples of the number of bits required for wrap times of at least one -hour: -

    -
      -
    • A 32-bit timestamp advancing by 1 per microsecond takes 1.19 hours -to wrap. -
    • -
    • A 42-bit timestamp advancing by 1 per nanosecond takes 1.22 hours to -wrap. -
    - -

    A PSA implementation is not required to implement time -synchronization, e.g. via PTP9 or NTP10. -

    -

    The control plane API excerpt below is intended to be added as part of -the P4Runtime API. -

    -
    -
    -
    // The TimestampInfo and Timestamp messages should be added to the
    -// "oneof" inside of message "Entity".
    -
    -// TimestampInfo is only intended to be read.  Attempts to update this
    -// entity have no effect, and should return an error status that the
    -// entity is read only.
    -
    -message TimestampInfo {
    -  // The number of bits in the device's `Timestamp_t` type.
    -  uint32 size_in_bits = 1;
    -  // The timestamp value of this device increments
    -  // `increments_per_period` times every `period_in_seconds` seconds.
    -  uint64 increments_per_period = 2;
    -  uint64 period_in_seconds = 3;
    -}
    -
    -// The timestamp value can be read or written.  Note that if there are
    -// already timestamp values stored in tables or `Register` instances,
    -// they will not be updated as a result of writing this timestamp
    -// value.  Writing the device timestamp is intended only for
    -// initialization and testing.
    -
    -message Timestamp {
    -  bytes value = 1;
    -}
    -

    For every packet P that is processed by ingress and then egress, +wrap time to exceed the maximum queueing latency.

    + + +
    +
    +

    Examples of the number of bits required for wrap times of at least one +hour:

    +
    +
    +
      +
    • +

      A 32-bit timestamp advancing by 1 per microsecond takes 1.19 hours +to wrap.

      +
    • +
    • +

      A 42-bit timestamp advancing by 1 per nanosecond takes 1.22 hours to +wrap.

      +
    • +
    +
    +
    +

    A PSA implementation is not required to implement time +synchronization, e.g. via PTP[8] or NTP[9].

    +
    +
    +

    The control plane API excerpt below is intended to be added as part of +the P4Runtime API.

    +
    +
    +
    +
    // The TimestampInfo and Timestamp messages should be added to the
    +// "oneof" inside of message "Entity".
    +
    +// TimestampInfo is only intended to be read.  Attempts to update this
    +// entity have no effect, and should return an error status that the
    +// entity is read only.
    +
    +message TimestampInfo {
    +  // The number of bits in the device's `Timestamp_t` type.
    +  uint32 size_in_bits = 1;
    +  // The timestamp value of this device increments
    +  // `increments_per_period` times every `period_in_seconds` seconds.
    +  uint64 increments_per_period = 2;
    +  uint64 period_in_seconds = 3;
    +}
    +
    +// The timestamp value can be read or written.  Note that if there are
    +// already timestamp values stored in tables or `Register` instances,
    +// they will not be updated as a result of writing this timestamp
    +// value.  Writing the device timestamp is intended only for
    +// initialization and testing.
    +
    +message Timestamp {
    +  bytes value = 1;
    +}
    +
    +
    +
    +

    For every packet P that is processed by ingress and then egress, with the minimum possible latency in the packet buffer, it is -guaranteed that the egress_timestamp value for that packet will be -the same as, or slightly larger than, the ingress_timestamp value -that the packet was assigned on ingress. By “slightly larger than”, -we mean that the difference (egress_timestamp - ingress_timestamp) +guaranteed that the egress_timestamp value for that packet will be +the same as, or slightly larger than, the ingress_timestamp value +that the packet was assigned on ingress. By "slightly larger than", +we mean that the difference (egress_timestamp - ingress_timestamp) should be a reasonably accurate estimate of this minimum possible latency through the packet buffer, perhaps truncated down to 0 if -timestamps advance more slowly than this minimum latency. -

    -

    Consider two packets such that at the same time (e.g. the same clock -cycle), one is assigned its value of ingress_timestamp near the time +timestamps advance more slowly than this minimum latency.

    +
    +
    +

    Consider two packets such that at the same time (e.g. the same clock +cycle), one is assigned its value of ingress_timestamp near the time it begins parsing, and the other is assigned its value of -egress_timestamp near the time that it begins its egress processing. +egress_timestamp near the time that it begins its egress processing. It is allowed that these timestamps differ by a few tens of -nanoseconds (or by one “tick” of the timestamp, if one tick is larger +nanoseconds (or by one "tick" of the timestamp, if one tick is larger than that time), due to practical difficulties in making them always -equal. -

    -

    Recall that the binary operators + and - on the bit<W> type in +equal.

    +
    +
    +

    Recall that the binary operators + and - on the bit<W> type in P4 are defined to perform wrap-around unsigned arithmetic. Thus even if a timestamp value wraps around from its maximum value back to 0, you can always calculate the number of ticks that have elapsed from -timestamp $t1$ until timestamp $t2$ using the expression $(t2 - t1)$ -(if more than $2^W$ ticks have elapsed, there will be aliasing of the -result). For example, if timestamps were $W >= 4$ bits in size, -$t1=2^{W}-5$, and $t2=3$, then $(t2-t1)=8$. There is thus no need for -conditional execution to calculate such elapsed times. -

    -

    It is sometimes useful to minimize storage costs by discarding some +timestamp $t1$ until timestamp $t2$ using the expression $(t2 - t1)$ +(if more than $2^W$ ticks have elapsed, there will be aliasing of the +result). For example, if timestamps were $W >= 4$ bits in size, +$t1=2^{W}-5$, and $t2=3$, then $(t2-t1)=8$. There is thus no need for +conditional execution to calculate such elapsed times.

    +
    +
    +

    It is sometimes useful to minimize storage costs by discarding some bits of a timestamp value in a P4 program for use cases that do not need the full wrap time or precision. For example, an application that only needs to detect protocol timeouts with an accuracy of 1 second can discard the least significant bits of a timestamp that -change more often than every 1 second. -

    -

    Another example is an application that needed full precision of the +change more often than every 1 second.

    +
    +
    +

    Another example is an application that needed full precision of the least significant bits of a timestamp, but the combination of the control plane and P4 program are designed to examine all entries of a -Register array where these partial timestamps are stored more often +Register array where these partial timestamps are stored more often than once every 5 seconds, to prevent wrapping. In that case, the P4 program could discard the most significant bits of the timestamp so that the remaining bits wrap every 8 seconds, and store those partial -timestamps in the Register instance. -

    7.14. Packet Digest

    -

    A digest is one mechanism to send a message from the data plane to the +timestamps in the Register instance.

    +
    +
    +
    +

    8.14. Packet Digest

    +
    +

    A digest is one mechanism to send a message from the data plane to the control plane. Another is to send a packet to the control plane via -the port numbered PSA_PORT_CPU. Sending a packet to port -PSA_PORT_CPU typically sends most or all of the original packet +the port numbered PSA_PORT_CPU. Sending a packet to port +PSA_PORT_CPU typically sends most or all of the original packet headers, and perhaps also the payload, each as a separate message to be received and processed by the control plane. The contents of a digest for one packet are typically much smaller than the packet. A PSA implementation can take advantage of this, e.g. it might combine digests for multiple packets into larger messages, to reduce the -rate of messages sent to the control plane. -

    -

    A digest message may contain any values from the data plane. Because +rate of messages sent to the control plane.

    +
    +
    +

    A digest message may contain any values from the data plane. Because a P4 program may have multiple Digest instances, each with different message contents, the PSA implementation as a whole must provide the ability to distinguish the messages created by different Digest -instances from each other. -

    -

    In PSA, a digest is created by calling the pack method on the digest +instances from each other.

    +
    +
    +

    In PSA, a digest is created by calling the pack method on the digest instance. The argument is the value to be included in the digest, -often a collection of values in a P4 struct type. The compiler +often a collection of values in a P4 struct type. The compiler decides the best serialization format to send the digest contents to a local software agent, which is responsible for sending the digest data -in a form defined by the P4Runtime API specification. -

    -

    A PSA program can instantiate multiple Digest instances in the same -IngressDeparser control block, and make at most one pack call on +in a form defined by the P4Runtime API specification.

    +
    +
    +

    A PSA program can instantiate multiple Digest instances in the same +IngressDeparser control block, and make at most one pack call on each instance during a single execution of this control block. A PSA implementation need not support the use of the Digest -extern in the EgressDeparser control block. -

    -

    There is no requirement that if multiple Digest messages are created -while processing the same packet, that these messages must be “bundled -together” in any way. An implementation is free to put them in +extern in the EgressDeparser control block.

    +
    +
    +

    There is no requirement that if multiple Digest messages are created +while processing the same packet, that these messages must be "bundled +together" in any way. An implementation is free to put them in separate queues per Digest instance, for example, and they may arrive to the controller completely separate from each other, and in a different order than they were generated. It is recommended that a -PSA implementation send Digest messages from a single Digest -instance to the control plane in the order they were generated. -

    -

    If you wish to associate multiple Digest messages from different +PSA implementation send Digest messages from a single Digest +instance to the control plane in the order they were generated.

    +
    +
    +

    If you wish to associate multiple Digest messages from different instances with each other in control plane software, it may suit your purposes to include a common sequence number or timestamp in all Digest messages generated by the same packet. Then use those in the -control plane for correlation of different messages. -

    -

    Since high speed PSA implementations are expected to be able to +control plane for correlation of different messages.

    +
    +
    +

    Since high speed PSA implementations are expected to be able to generate digests much faster than control software can consume them, it is expected that loss of such digest messages will occur if the data plane generates them too quickly. It is recommended that PSA implementations maintain a count of digest messages that the data plane creates, but do not reach the control plane, independently for -each digest instance. -

    -
    -
    -
    extern Digest<T> {
    -  Digest();                       /// define a digest stream to the control plane
    -  void pack(in T data);           /// emit data into the stream
    -}
    -

    Below is a part of an example program that demonstrates using a digest +each digest instance.

    +
    +
    +
    +
    extern Digest<T> {
    +  Digest();                       /// define a digest stream to the control plane
    +  void pack(in T data);           /// emit data into the stream
    +}
    +
    +
    +
    +

    Below is a part of an example program that demonstrates using a digest to notify the control plane about source Ethernet MAC addresses and -ingress ports of packets that have not been seen before. -

    -
    -
    -
    struct mac_learn_digest_t {
    -    EthernetAddress srcAddr;
    -    PortId_t        ingress_port;
    -}
    -
    -struct metadata {
    -    bool               send_mac_learn_msg;
    -    mac_learn_digest_t mac_learn_msg;
    -}
    -
    -// This is part of the functionality of a typical Ethernet learning bridge.
    -
    -// The control plane will typically enter the _same_ keys into the
    -// learned_sources and l2_tbl tables.  The entries in l2_tbl are searched for
    -// the packet's dest MAC address, and on a hit the resulting action tells
    -// where to send the packet.
    -
    -// The entries in learned_sources are the same, and the action of every table
    -// entry added is NoAction.  If there is a _miss_ in learned_sources, we want
    -// to send a message to the control plane software containing the packet's
    -// source MAC address, and the port it arrived on.  The control plane will
    -// make a decision about creating an entry with that packet's source MAC
    -// address into both tables, with the l2_tbl sending future packets out this
    -// packet's ingress_port.
    -
    -// This is only a simple example, e.g. there is no implementation of
    -// "flooding" shown here, typical when a learning bridge gets a miss when
    -// looking up the dest MAC address of a packet.
    -
    -control ingress(inout headers hdr,
    -                inout metadata meta,
    -                in    psa_ingress_input_metadata_t  istd,
    -                inout psa_ingress_output_metadata_t ostd)
    -{
    -    action unknown_source () {
    -        meta.send_mac_learn_msg = true;
    -        meta.mac_learn_msg.srcAddr = hdr.ethernet.srcAddr;
    -        meta.mac_learn_msg.ingress_port = istd.ingress_port;
    -        // meta.mac_learn_msg will be sent to control plane in
    -        // IngressDeparser control block
    -    }
    -    table learned_sources {
    -        key = { hdr.ethernet.srcAddr : exact; }
    -        actions = { NoAction; unknown_source; }
    -        default_action = unknown_source();
    -    }
    -
    -    action do_L2_forward (PortId_t egress_port) {
    -        send_to_port(ostd, egress_port);
    -    }
    -    table l2_tbl {
    -        key = { hdr.ethernet.dstAddr : exact; }
    -        actions = { do_L2_forward; NoAction; }
    -        default_action = NoAction();
    -    }
    -    apply {
    -        meta.send_mac_learn_msg = false;
    -        learned_sources.apply();
    -        l2_tbl.apply();
    -    }
    -}
    -
    -control IngressDeparserImpl(packet_out packet,
    -                            out empty_metadata_t clone_i2e_meta,
    -                            out empty_metadata_t resubmit_meta,
    -                            out empty_metadata_t normal_meta,
    -                            inout headers hdr,
    -                            in metadata meta,
    -                            in psa_ingress_output_metadata_t istd)
    -{
    -    CommonDeparserImpl() common_deparser;
    -    Digest<mac_learn_digest_t>() mac_learn_digest;
    -    apply {
    -        if (meta.send_mac_learn_msg) {
    -            mac_learn_digest.pack(meta.mac_learn_msg);
    -        }
    -        common_deparser.apply(packet, hdr);
    -    }
    -}

    8. Atomicity of control plane API operations

    -

    All table add, delete, and modify operations must be atomic relative -to packet forwarding. That is, for every table apply operation, and +ingress ports of packets that have not been seen before.

    +
    +
    +
    +
    struct mac_learn_digest_t {
    +    EthernetAddress srcAddr;
    +    PortId_t        ingress_port;
    +}
    +
    +struct metadata {
    +    bool               send_mac_learn_msg;
    +    mac_learn_digest_t mac_learn_msg;
    +}
    +
    +// This is part of the functionality of a typical Ethernet learning bridge.
    +
    +// The control plane will typically enter the _same_ keys into the
    +// learned_sources and l2_tbl tables.  The entries in l2_tbl are searched for
    +// the packet's dest MAC address, and on a hit the resulting action tells
    +// where to send the packet.
    +
    +// The entries in learned_sources are the same, and the action of every table
    +// entry added is NoAction.  If there is a _miss_ in learned_sources, we want
    +// to send a message to the control plane software containing the packet's
    +// source MAC address, and the port it arrived on.  The control plane will
    +// make a decision about creating an entry with that packet's source MAC
    +// address into both tables, with the l2_tbl sending future packets out this
    +// packet's ingress_port.
    +
    +// This is only a simple example, e.g. there is no implementation of
    +// "flooding" shown here, typical when a learning bridge gets a miss when
    +// looking up the dest MAC address of a packet.
    +
    +control ingress(inout headers hdr,
    +                inout metadata meta,
    +                in    psa_ingress_input_metadata_t  istd,
    +                inout psa_ingress_output_metadata_t ostd)
    +{
    +    action unknown_source () {
    +        meta.send_mac_learn_msg = true;
    +        meta.mac_learn_msg.srcAddr = hdr.ethernet.srcAddr;
    +        meta.mac_learn_msg.ingress_port = istd.ingress_port;
    +        // meta.mac_learn_msg will be sent to control plane in
    +        // IngressDeparser control block
    +    }
    +    table learned_sources {
    +        key = { hdr.ethernet.srcAddr : exact; }
    +        actions = { NoAction; unknown_source; }
    +        default_action = unknown_source();
    +    }
    +
    +    action do_L2_forward (PortId_t egress_port) {
    +        send_to_port(ostd, egress_port);
    +    }
    +    table l2_tbl {
    +        key = { hdr.ethernet.dstAddr : exact; }
    +        actions = { do_L2_forward; NoAction; }
    +        default_action = NoAction();
    +    }
    +    apply {
    +        meta.send_mac_learn_msg = false;
    +        learned_sources.apply();
    +        l2_tbl.apply();
    +    }
    +}
    +
    +control IngressDeparserImpl(packet_out packet,
    +                            out empty_metadata_t clone_i2e_meta,
    +                            out empty_metadata_t resubmit_meta,
    +                            out empty_metadata_t normal_meta,
    +                            inout headers hdr,
    +                            in metadata meta,
    +                            in psa_ingress_output_metadata_t istd)
    +{
    +    CommonDeparserImpl() common_deparser;
    +    Digest<mac_learn_digest_t>() mac_learn_digest;
    +    apply {
    +        if (meta.send_mac_learn_msg) {
    +            mac_learn_digest.pack(meta.mac_learn_msg);
    +        }
    +        common_deparser.apply(packet, hdr);
    +    }
    +}
    +
    +
    +
    +
    +
    +
    +

    9. Atomicity of control plane API operations

    +
    +
    +

    All table add, delete, and modify operations must be atomic relative +to packet forwarding. That is, for every table apply operation, and every control plane operation on a table that adds, deletes, or -modifies one table entry, the apply operation should behave as if +modifies one table entry, the apply operation should behave as if that control plane operation has not yet occurred, or as if the control plane operation is complete. The P4 program should never -behave as if the control plane operation is partially complete. -

    -

    Note that this requirement is for every table apply operation +behave as if the control plane operation is partially complete.

    +
    +
    +

    Note that this requirement is for every table apply operation individually. A PSA implementation is not required to support -performing multiply apply operations on the same table in the same +performing multiply apply operations on the same table in the same invocation of a control block. If it does support that, it is allowed -that a control plane update may occur after one apply call by a -packet to a table, but before the next apply call by the same -packet. -

    -

    A PSA implementation should give an error and fail to compile P4 +that a control plane update may occur after one apply call by a +packet to a table, but before the next apply call by the same +packet.

    +
    +
    +

    A PSA implementation should give an error and fail to compile P4 programs for which it cannot meet this atomicity requirement. For example, perhaps the implementation can only satisfy this requirement for tables with actions having at most 128 bits of action parameters, and thus gives an error if you attempt to compile a P4 program that -contains an action with more bits of parameters. -

    -

    For example, suppose a table T has an action A with 100 total bits +contains an action with more bits of parameters.

    +
    +
    +

    For example, suppose a table T has an action A with 100 total bits of action parameters, and the control plane has added a table entry -with a search key K and action A. Later the control plane -performs an update operation on the entry with key K that leaves the -key K the same, but changes the 100 bits of action parameters. -Every packet doing an apply on table T and matching the entry with -key K should execute action A with either the old 100 bits of -action parameters, or the new 100 bits of action parameters. -

    -

    The P4Runtime API enables controllers to create “batch” messages that perform +with a search key K and action A. Later the control plane +performs an update operation on the entry with key K that leaves the +key K the same, but changes the 100 bits of action parameters. +Every packet doing an apply on table T and matching the entry with +key K should execute action A with either the old 100 bits of +action parameters, or the new 100 bits of action parameters.

    +
    +
    +

    The P4Runtime API enables controllers to create "batch" messages that perform more than one single operation, as defined here. If so, a PSA implementation need only ensure that each single operation is atomic. -There is no requirement that a sequence of multiple table entry add, -delete, or update operations should be atomic. -

    -

    The same applies for all control plane API operations on externs, -unless the control plane operation explicitly documents otherwise. -

    -

    In particular, ActionProfile and ActionSelector single operations, +There is no requirement that a sequence of multiple table entry add, +delete, or update operations should be atomic.

    +
    +
    +

    The same applies for all control plane API operations on externs, +unless the control plane operation explicitly documents otherwise.

    +
    +
    +

    In particular, ActionProfile and ActionSelector single operations, such as adding a member to a group, removing a member from a group, adding an empty group, deleting an empty group, or modifying the action parameters of an action added earlier to a group, should all be -atomic. -

    -

    Also, a control plane read, or write, of a single element of a +atomic.

    +
    +
    +

    Also, a control plane read, or write, of a single element of a Register array should be atomic, and behave as if it occurred before -or after (but not during) any P4 program's section of code labeled -with the @atomic annotation. There is no control plane operation +or after (but not during) any P4 program’s section of code labeled +with the @atomic annotation. There is no control plane operation on a Register that can atomically read an element, then write back a -modified value. -

    -

    Advice for P4 developers: If you desire a capability for the control +modified value.

    +
    +
    +

    Advice for P4 developers: If you desire a capability for the control plane to atomically read, modify, then write back a Register array element, you should write your P4 program such that the desired read, modify, and write operation can be done by a packet that your control plane can inject into the data plane, e.g. via packet in / packet out -P4Runtime API operations. -

    -

    A high speed PSA implementation might process hundreds or thousands of +P4Runtime API operations.

    +
    +
    +

    A high speed PSA implementation might process hundreds or thousands of packets between each single control plane operation. There are common -“write tables from later to earlier in the data flow”, sometimes also -called “back to front” or “pointer flipping”, techniques used by +"write tables from later to earlier in the data flow", sometimes also +called "back to front" or "pointer flipping", techniques used by existing control planes to achieve an effect that is similar to making a sequence of many table entry operations atomic relative to packet forwarding. Recent research analyzes these techniques in a more -general setting11. -

    A. Appendix: Open Issues

    -

    As with any work in progress, we have a number of open issues that are under discussion in the +general setting[10].

    +
    +
    +
    +
    +

    Appendix A: Open Issues

    +
    +
    +

    As with any work in progress, we have a number of open issues that are under discussion in the working group. In addition to the TBDs in the document, there a number of larger issues that are -summarized here: -

    A.1. Action Selectors

    -

    The size parameter in the action_selector instance that defines the maximum number of members in a +summarized here:

    +
    +
    +

    A.1. Action Selectors

    +
    +

    The size parameter in the action_selector instance that defines the maximum number of members in a selector. In some cases it might be useful to allow the controller to dynamically provision resources on the selector or to utilize different selector sizes on different targets, while using -a common P4 program. -

    -

    We also need to formalize the interaction of action profiles and action selectors with counters and -meters. -

    A.2. Observation and control of congestion

    -

    The current PSA does not provide any mechanisms to observe if +a common P4 program.

    +
    +
    +

    We also need to formalize the interaction of action profiles and action selectors with counters and +meters.

    +
    +
    +
    +

    A.2. Observation and control of congestion

    +
    +

    The current PSA does not provide any mechanisms to observe if particular output ports or queues are leading to congestion in the packet buffer. Thus it is not possible without using mechanisms defined outside of PSA to implement a feature like Explicit Congestion -Notification (ECN)12. One possibility here is to define a small +Notification (ECN)[11]. One possibility here is to define a small field, perhaps only 1 bit, that is part of the metadata associated with each packet as it begins egress processing. This field would -indicate “how much congestion” the packet experienced in the packet -buffer. -

    -

    There is also currently no way defined in PSA for ingress P4 code to +indicate "how much congestion" the packet experienced in the packet +buffer.

    +
    +
    +

    There is also currently no way defined in PSA for ingress P4 code to send information about a packet to the packet buffer that might influence the behavior of a congestion control algorithm, such as Approximate Fair Drop (AFD). This is partly because of the variety of -congestion control mechanisms in use by switches today. -

    -

    It would be desirable to define in PSA a small set of fields about a +congestion control mechanisms in use by switches today.

    +
    +
    +

    It would be desirable to define in PSA a small set of fields about a packet that would be useful inputs to multiple congestion control -algorithms. One possibility is a hash of the packet's “flow id”, +algorithms. One possibility is a hash of the packet’s "flow id", often implemented as a hash of packet header fields like IP source and destination address, IP protocol, and optionally TCP/UDP source and destination ports. Given that P4 programmable devices can implement network protocols other than IP, including custom ones, a more general -mechanism is desirable in PSA devices. -

    A.3. Enabling full implementation of In-band Network Telemetry

    -

    One promising use case for P4 programmable network devices is to -implement In-band Network Telemetry7. While PSA mechanisms such +mechanism is desirable in PSA devices.

    +
    +
    +
    +

    A.3. Enabling full implementation of In-band Network Telemetry

    +
    +

    One promising use case for P4 programmable network devices is to +implement In-band Network Telemetry[6]. While PSA mechanisms such as timestamps enable a significant portion of INT features to be implemented, they do not yet define any mechanisms to access information such as egress port link utilization or queue -occupancy13. -

    A.4. PSA profiles

    -

    We are considering whether to specify different limits that a certain PSA implementation has to have in order for the implementation to be considered compliant. The main point of PSA is to enable a variety of devices, and thus limits may be artificial. On the other hand, for most interesting applications, it is necessary to support a minimum of functionality. -

    B. Appendix: Implementation of the InternetChecksum extern

    -

    Besides RFC 1461, RFC 1071 and RFC 1141 also contain useful tips on +occupancyfootnoote:psaint[https://github.com/p4lang/p4-spec/issues/510].

    +
    +
    +
    +

    A.4. PSA profiles

    +
    +

    We are considering whether to specify different limits that a certain PSA implementation has to have in order for the implementation to be considered compliant. The main point of PSA is to enable a variety of devices, and thus limits may be artificial. On the other hand, for most interesting applications, it is necessary to support a minimum of functionality.

    +
    +
    +
    +
    +
    +

    Appendix B: Implementation of the InternetChecksum extern

    +
    +
    +

    Besides RFC 1461, RFC 1071 and RFC 1141 also contain useful tips on efficiently computing the Internet checksum, especially in software -implementations. -

    -

    Here we give reference implementations for the methods of the -InternetChecksum extern, specified with the syntax and semantics of -P416, with extensions of a for loop and a return statement for -returning a value from a function. -

    -

    The minimum internal state necessary for one instance of an -InternetChecksum object is a 16-bit bit vector, here called sum. -

    -
    -
    -
    // This is one way to perform a normal one's complement sum of two
    -// 16-bit values.
    -bit<16> ones_complement_sum(in bit<16> x, in bit<16> y) {
    -    bit<17> ret = (bit<17>) x + (bit<17>) y;
    -    if (ret[16:16] == 1) {
    -        ret = ret + 1;
    -    }
    -    return ret[15:0];
    -}
    -
    -bit<16> sum;
    -
    -void clear() {
    -    sum = 0;
    -}
    -
    -// Restriction: data is a multiple of 16 bits long
    -void add<T>(in T data) {
    -    bit<16> d;
    -    for (each 16-bit aligned piece d of data) {
    -        sum = ones_complement_sum(sum, d);
    -    }
    -}
    -
    -// Restriction: data is a multiple of 16 bits long
    -void subtract<T>(in T data) {
    -    bit<16> d;
    -    for (each 16-bit aligned piece d of data) {
    -        // ~d is the negative of d in one's complement arithmetic.
    -        sum = ones_complement_sum(sum, ~d);
    -    }
    -}
    -
    -// The Internet checksum is the one's complement _of_ the one's
    -// complement sum of the relevant parts of the packet.  The methods
    -// above calculate the one's complement sum of the parts in the
    -// variable 'sum'.  get() returns the bitwise negation of 'sum', which
    -// is the one's complement of 'sum'.
    -
    -bit<16> get() {
    -    return ~sum;
    -}
    -
    -bit<16> get_state() {
    -    return sum;
    -}
    -
    -void set_state(bit<16> checksum_state) {
    -    sum = checksum_state;
    -}

    C. Appendix: Example implementation of Counter extern

    -

    The example implementation below, in particular the function -next_counter_value, is not intended to restrict PSA implementations. -The storage format for PACKETS_AND_BYTES type counters demonstrated +implementations.

    +
    +
    +

    Here we give reference implementations for the methods of the +InternetChecksum extern, specified with the syntax and semantics of +P416, with extensions of a for loop and a return statement for +returning a value from a function.

    +
    +
    +

    The minimum internal state necessary for one instance of an +InternetChecksum object is a 16-bit bit vector, here called sum.

    +
    +
    +
    +
    // This is one way to perform a normal one's complement sum of two
    +// 16-bit values.
    +bit<16> ones_complement_sum(in bit<16> x, in bit<16> y) {
    +    bit<17> ret = (bit<17>) x + (bit<17>) y;
    +    if (ret[16:16] == 1) {
    +        ret = ret + 1;
    +    }
    +    return ret[15:0];
    +}
    +
    +bit<16> sum;
    +
    +void clear() {
    +    sum = 0;
    +}
    +
    +// Restriction: data is a multiple of 16 bits long
    +void add<T>(in T data) {
    +    bit<16> d;
    +    for (each 16-bit aligned piece d of data) {
    +        sum = ones_complement_sum(sum, d);
    +    }
    +}
    +
    +// Restriction: data is a multiple of 16 bits long
    +void subtract<T>(in T data) {
    +    bit<16> d;
    +    for (each 16-bit aligned piece d of data) {
    +        // ~d is the negative of d in one's complement arithmetic.
    +        sum = ones_complement_sum(sum, ~d);
    +    }
    +}
    +
    +// The Internet checksum is the one's complement _of_ the one's
    +// complement sum of the relevant parts of the packet.  The methods
    +// above calculate the one's complement sum of the parts in the
    +// variable 'sum'.  get() returns the bitwise negation of 'sum', which
    +// is the one's complement of 'sum'.
    +
    +bit<16> get() {
    +    return ~sum;
    +}
    +
    +bit<16> get_state() {
    +    return sum;
    +}
    +
    +void set_state(bit<16> checksum_state) {
    +    sum = checksum_state;
    +}
    +
    +
    +
    +
    +
    +

    Appendix C: Example implementation of Counter extern

    +
    +
    +

    The example implementation below, in particular the function +next_counter_value, is not intended to restrict PSA implementations. +The storage format for PACKETS_AND_BYTES type counters demonstrated there is one example of how it could be done. Implementations are free to store state in other ways, as long as the control plane API -returns the correct packet and byte count values. -

    -

    Two common techniques for counter implementations in the data plane are: -

    -
      -
    • wrap around counters -
    • -
    • saturating counters, that ‘stick’ at their maximum possible value, -without wrapping around. -
    - -

    This specification does not mandate any particular approach in the +returns the correct packet and byte count values.

    +
    +
    +

    Two common techniques for counter implementations in the data plane are:

    +
    +
    +
      +
    • +

      wrap around counters

      +
    • +
    • +

      saturating counters, that 'stick' at their maximum possible value, +without wrapping around.

      +
    • +
    +
    +
    +

    This specification does not mandate any particular approach in the data plane. Implementations should strive to avoid losing information in counters. One common implementation technique is to implement an -atomic “read and clear” operation in the data plane that can be +atomic "read and clear" operation in the data plane that can be invoked by the control plane software. The control plane software invokes this operation frequently enough to prevent counters from ever wrapping or saturating, and adds the values read to larger counters in -driver memory. -

    -
    -
    -
    Counter(bit<32> n_counters, PSA_CounterType_t type) {
    -    this.num_counters = n_counters;
    -    this.counter_vals = new array of size n_counters, each element with type W;
    -    this.type = type;
    -    if (this.type == PSA_CounterType_t.PACKETS_AND_BYTES) {
    -        // Packet and byte counts share storage in the same counter
    -        // state.  Should we have a separate constructor with an
    -        // additional argument indicating how many of the bits to use
    -        // for the byte counter?
    -        W shift_amount = TBD;
    -        this.shifted_packet_count = ((W) 1) << shift_amount;
    -        this.packet_count_mask = (~((W) 0)) << shift_amount;
    -        this.byte_count_mask = ~this.packet_count_mask;
    -    }
    -}
    -
    -W next_counter_value(W cur_value, PSA_CounterType_t type) {
    -    if (type == PSA_CounterType_t.PACKETS) {
    -        return (cur_value + 1);
    -    }
    -    // Exactly which packet bytes are included in packet_len is
    -    // implementation-specific.
    -    PacketLength_t packet_len = <packet length in bytes>;
    -    if (type == PSA_CounterType_t.BYTES) {
    -        return (cur_value + packet_len);
    -    }
    -    // type must be PSA_CounterType_t.PACKETS_AND_BYTES
    -    // In type W, the least significant bits contain the byte
    -    // count, and most significant bits contain the packet count.
    -    // This is merely one example storage format.  Implementations
    -    // are free to store packets_and_byte state in other ways, as
    -    // long as the control plane API returns the correct separate
    -    // packet and byte count values.
    -    W next_packet_count = ((cur_value + this.shifted_packet_count) &
    -                           this.packet_count_mask);
    -    W next_byte_count = (cur_value + packet_len) & this.byte_count_mask;
    -    return (next_packet_count | next_byte_count);
    -}
    -
    -void count(in S index) {
    -    if (index < this.num_counters) {
    -        this.counter_vals[index] = next_counter_value(this.counter_vals[index],
    -                                                      this.type);
    -    } else {
    -        // No counter_vals updated if index is out of range.
    -        // See below for optional debug information to record.
    -    }
    -}
    -

    Optional debugging information that may be kept if an index value is -out of range includes: -

    -
      -
    • Number of times this occurs. -
    • -
    • A FIFO of the first N out-of-range index values that occur, where N +driver memory.

      +
    +
    +
    +
    Counter(bit<32> n_counters, PSA_CounterType_t type) {
    +    this.num_counters = n_counters;
    +    this.counter_vals = new array of size n_counters, each element with type W;
    +    this.type = type;
    +    if (this.type == PSA_CounterType_t.PACKETS_AND_BYTES) {
    +        // Packet and byte counts share storage in the same counter
    +        // state.  Should we have a separate constructor with an
    +        // additional argument indicating how many of the bits to use
    +        // for the byte counter?
    +        W shift_amount = TBD;
    +        this.shifted_packet_count = ((W) 1) << shift_amount;
    +        this.packet_count_mask = (~((W) 0)) << shift_amount;
    +        this.byte_count_mask = ~this.packet_count_mask;
    +    }
    +}
    +
    +W next_counter_value(W cur_value, PSA_CounterType_t type) {
    +    if (type == PSA_CounterType_t.PACKETS) {
    +        return (cur_value + 1);
    +    }
    +    // Exactly which packet bytes are included in packet_len is
    +    // implementation-specific.
    +    PacketLength_t packet_len = <packet length in bytes>;
    +    if (type == PSA_CounterType_t.BYTES) {
    +        return (cur_value + packet_len);
    +    }
    +    // type must be PSA_CounterType_t.PACKETS_AND_BYTES
    +    // In type W, the least significant bits contain the byte
    +    // count, and most significant bits contain the packet count.
    +    // This is merely one example storage format.  Implementations
    +    // are free to store packets_and_byte state in other ways, as
    +    // long as the control plane API returns the correct separate
    +    // packet and byte count values.
    +    W next_packet_count = ((cur_value + this.shifted_packet_count) &
    +                           this.packet_count_mask);
    +    W next_byte_count = (cur_value + packet_len) & this.byte_count_mask;
    +    return (next_packet_count | next_byte_count);
    +}
    +
    +void count(in S index) {
    +    if (index < this.num_counters) {
    +        this.counter_vals[index] = next_counter_value(this.counter_vals[index],
    +                                                      this.type);
    +    } else {
    +        // No counter_vals updated if index is out of range.
    +        // See below for optional debug information to record.
    +    }
    +}
    +
    +
    +
    +

    Optional debugging information that may be kept if an index value is +out of range includes:

    +
    +
    +
      +
    • +

      Number of times this occurs.

      +
    • +
    • +

      A FIFO of the first N out-of-range index values that occur, where N is implementation-defined (e.g. it might only be 1). Extra -information to identify which count() method call in the P4 -program had the out-of-range index value is also recommended. -

    -

    D. Appendix: Rationale for design

    D.1. Why egress processing?

    -

    Question: Why is it useful to have separate ingress vs. egress -processing in a switch device? -

    -

    There have been packet processing ASICs built that effectively only do -‘ingress’ processing, then go to a packet buffer with one or more +information to identify which count() method call in the P4 +program had the out-of-range index value is also recommended.

    + + +
    +
    +
    +
    +

    Appendix D: Rationale for design

    +
    +
    +

    D.1. Why egress processing?

    +
    +

    Question: Why is it useful to have separate ingress vs. egress +processing in a switch device?

    +
    +
    +

    There have been packet processing ASICs built that effectively only do +'ingress' processing, then go to a packet buffer with one or more queues, and then go out of the device, effectively being restricted to -no or “empty” egress processing. -

    -

    There are a few things that are trickier to do in such a device. -

    -
      -
    1. Last-nanosecond changes to the packet -
    - -

    If you want to measure the queuing latency through the device, and put +no or "empty" egress processing.

    +
    +
    +

    There are a few things that are trickier to do in such a device.

    +
    +
    +
      +
    1. +

      Last-nanosecond changes to the packet

      +
      +

      If you want to measure the queuing latency through the device, and put a measurement of this quantity inside the packet somewhere, it is in general not possible to know the queueing latency before the packet is -sent to the packet buffer. There are special cases where you can +sent to the packet buffer. There are special cases where you can predict it, e.g. when there is a single FIFO queue feeding a constant -bit rate output port, with nothing like Ethernet pause flow control. -

      -

      But if you have variable bit rate links, e.g. because of things like +bit rate output port, with nothing like Ethernet pause flow control.

      +
      +
      +

      But if you have variable bit rate links, e.g. because of things like Ethernet pause flow control, or Wi-Fi signal quality changes, or if you have multiple class-of-service queues with a scheduling policy between them like weighted fair queueing, then it is not possible to @@ -4122,90 +5475,110 @@

      -

      In such cases, having egress processing for taking the measurement, -after it is known and easy to calculate as “dequeue time - enqueue -time”, allows the egress processing to modify the packet further. -

      -
        -
      1. Multicast efficiency and flexibility -
      - -

      It is possible in a PSA device to handle multicast by doing a +service queues for the same output port.

      +

      +
      +

      In such cases, having egress processing for taking the measurement, +after it is known and easy to calculate as "dequeue time - enqueue +time", allows the egress processing to modify the packet further.

      +
      +
    2. +
    3. +

      Multicast efficiency and flexibility

      +
      +

      It is possible in a PSA device to handle multicast by doing a recirculate plus clone operation for each of N copies to be made, but this reduces the processing capacity of ingress that is available to newly arriving packets, in particular newly arriving packets that you -might consider more important to keep than the multicast packets. -

      -

      By designing a packet buffer that can take a packet with a ‘multicast -group id’, which the control plane configures to make copies to a +might consider more important to keep than the multicast packets.

      +
      +
      +

      By designing a packet buffer that can take a packet with a 'multicast +group id', which the control plane configures to make copies to a selected set of output ports, it frees up the part of the system that performs ingress processing to accept new packets more quickly, and at -a more predictable rate. -

      -

      There could still be a challenge in designing the packet replication +a more predictable rate.

      +
      +
      +

      There could still be a challenge in designing the packet replication portion of the system not to fall behind when many multicast packets to be replicated to many output ports arrive close together in time, but it is fairly easy to separate the concerns of multicast from unicast packets. For example, a device implementer could prioritize unicast packets so that they are not slowed down if multicast -replication is falling behind. -

      -

      Once you have multicast designed in this way, there are still +replication is falling behind.

      +
      +
      +

      Once you have multicast designed in this way, there are still multicast use cases where one needs to process different copies of the packet differently. For example, the copy going out port 5 might need a VLAN tag of 7 placed in its header, whereas the copy going out port 2 might need a VLAN tag of 18 placed in its header. Similarly for multicast packets entering one of many flavors of tunnels, e.g. VXLAN, GRE, etc. By doing this per-copy modifications in egress processing, -the packet replication logic can be kept very simple just make +the packet replication logic can be kept very simple — just make identical copies of the packet as ingress finished with it, except for -some kind of unique ‘id’ on each copy that egress processing can use -to distinguish them. -

      D.2. No output port change during egress

      -

      Question: Why can't my P4 program change the output port during egress -processing? -

      -

      In a network device that has many input and output ports, packets can +some kind of unique 'id' on each copy that egress processing can use +to distinguish them.

      +
      +
    4. +
    +
    +
    +
    +

    D.2. No output port change during egress

    +
    +

    Question: Why can’t my P4 program change the output port during egress +processing?

    +
    +
    +

    In a network device that has many input and output ports, packets can arrive at or near the same time on multiple input ports, all destined -for the same output port. -

    -

    Packet buffers are typically designed into such network devices, to +for the same output port.

    +
    +
    +

    Packet buffers are typically designed into such network devices, to store the packets that cannot be sent out immediately, absorbing this -short term congestion. -

    -

    For a given output port P, we now wish to retrieve packets from the +short term congestion.

    +
    +
    +

    For a given output port P, we now wish to retrieve packets from the packet buffer at a rate that is equal to the rate we will send them to port P, typically equal to the maximum bit rate that it is possible to -send data out of port P. -

    -

    Packet scheduling algorithms such as weighted fair queueing, and many +send data out of port P.

    +
    +
    +

    Packet scheduling algorithms such as weighted fair queueing, and many others, have been developed that can determine which among a set of potentially many FIFO queues that a packet should be read from next, -and sent out on the port. -

    -

    These link scheduling algorithms are real time algorithms with very +and sent out on the port.

    +
    +
    +

    These link scheduling algorithms are real time algorithms with very tight timing constraints. If they go too slow, the output port goes idle and its capacity is wasted. If they go too fast, we read packets from the packet buffer faster than they can be transmitted on the -port, and we are back at the same problem we had originally either +port, and we are back at the same problem we had originally — either drop some of the packets, or store them somewhere again until the port -is ready to transmit them. -

    -

    Such a scheduling algorithm that handles multiple output ports must +is ready to transmit them.

    +
    +
    +

    Such a scheduling algorithm that handles multiple output ports must know which output port all packets are destined to, before they are put into the packet buffer. If that target output port can be changed after the packet is read out, then we can simultaneously overload one -output port while starving another. -

    -

    That is why the egress_port of a packet must be selected during -ingress processing, and egress processing is not allowed to change it. -

    -

    These scheduling algorithms also need to know the size of each packet, -i.e. the size as it will be when transmitted on the port. -

    -

    It is possible in egress P4 code to drop a packet, or to change the +output port while starving another.

    +
    +
    +

    That is why the egress_port of a packet must be selected during +ingress processing, and egress processing is not allowed to change it.

    +
    +
    +

    These scheduling algorithms also need to know the size of each packet, +i.e. the size as it will be when transmitted on the port.

    +
    +
    +

    It is possible in egress P4 code to drop a packet, or to change the size of the packet by adding or removing headers. Very likely P4-programmable network devices will have their scheduling algorithms run just slightly faster than the port to handle cases where many @@ -4213,88 +5586,106 @@

    's capacity unused during times -when all packet sizes are being decreased. -

    -

    Certainly if there are long durations of time when egress decides to +leave some fraction of the output port’s capacity unused during times +when all packet sizes are being decreased.

    +

    +
    +

    Certainly if there are long durations of time when egress decides to drop all packets to an output port, that port will go idle. The scheduling algorithm implementations are all built with a finite -maximum packet scheduling rate. -

    D.3. Ingress deparser and egress parser

    -

    Question: P414 did not have an ingress deparser, or an egress -parser. Why does PSA have these things? -

    -

    P414 did not have these things explicitly, but there was also not -much explicitly stated in the P414 specification about what data +maximum packet scheduling rate.

    +
    +
    +
    +

    D.3. Ingress deparser and egress parser

    +
    +

    Question: P414 did not have an ingress deparser, or an egress +parser. Why does PSA have these things?

    +
    +
    +

    P414 did not have these things explicitly, but there was also not +much explicitly stated in the P414 specification about what data about each packet was carried from ingress to egress. Often such things were left implicit. Some implementations have an ingress deparser whose order of emitting headers is auto-generated from the -P414 program's parser code. This leads to restrictions on your -P414 program, not stated in the P414 specification, that the +P414 program’s parser code. This leads to restrictions on your +P414 program, not stated in the P414 specification, that the contents of your headers and metadata must be in a state where if you -deparse at that point, then your P414 parser code must be able to +deparse at that point, then your P414 parser code must be able to parse that packet, or else the device will fail to parse it in the -(implicit) egress parser. -

    -

    By making the ingress deparser and egress parser explicit, we hope to +(implicit) egress parser.

    +
    +
    +

    By making the ingress deparser and egress parser explicit, we hope to make this behavior more defined, and more portable across different PSA implementations. We expect that the common case will be that the ingress and egress parsers will have much (or all) code in common with -each other, and this is easy to do using P416's capability for one +each other, and this is easy to do using P416's capability for one parser to call another, i.e. you can write a common parser, then call -it from your ingress and egress parsers. -

    -

    You can also choose to make the two parsers different, and have full +it from your ingress and egress parsers.

    +
    +
    +

    You can also choose to make the two parsers different, and have full control over the differences between them. For example, you might wish your egress parser to handle extra headers that you only put onto -cloned packets, that should never appear on packets from input ports. -

    -

    Similarly for the ingress deparser. By making this an explicit and +cloned packets, that should never appear on packets from input ports.

    +
    +
    +

    Similarly for the ingress deparser. By making this an explicit and separate control block, you now have full control over exactly what data about a packet is included when it is sent to the packet buffer, -and what is not. In P414, it was implicit that “all packet metadata -used somewhere in egress code” was carried along with each packet. -This can still be done in P416 programs for the PSA, but now you +and what is not. In P414, it was implicit that "all packet metadata +used somewhere in egress code" was carried along with each packet. +This can still be done in P416 programs for the PSA, but now you must be explicit in doing so. With PSA, you now have a way to restrict how much data is carried with each packet, which can be important if the I/O bandwidth of the packet buffer becomes a -bottleneck. -

    E. Appendix: Multi-pipeline PSA devices

    -

    The highest packet rate network devices today are ASICs running at a +bottleneck.

    +
    +
    +
    +
    +
    +

    Appendix E: Multi-pipeline PSA devices

    +
    +
    +

    The highest packet rate network devices today are ASICs running at a clock rate on the order of 1 to 2 GHz. This discussion will assume 1 GHz for the sake of a concrete numerical example, but everything -discussed here scales linearly with the clock rate. -

    -

    It is common to design a portion of a network ASIC such that it can +discussed here scales linearly with the clock rate.

    +
    +
    +

    It is common to design a portion of a network ASIC such that it can start processing a new packet once every clock cycle, and finish a packet every clock cycle. The latency might be hundreds of clock cycles from starting a packet until it is complete. P4 tables in such ASICs are typically implemented using logic such as TCAMs and SRAMs. TCAM designs can do 1 search per clock cycle. The lowest area and -power SRAMs can do 1 read or 1 write per clock cycle. -

    -

    While there are “multi ported” SRAM designs that can be read and/or +power SRAMs can do 1 read or 1 write per clock cycle.

    +
    +
    +

    While there are "multi ported" SRAM designs that can be read and/or written multiple times per clock cycle, these have a noticeable -increase in area and power over the “single ported” designs that are +increase in area and power over the "single ported" designs that are limited to 1 access per cycle. If multi ported TCAM designs even exist, the cost premium for multi-ported TCAMs is likely to be even higher than the cost premium for multi-ported SRAMs. The typical way to achieve higher TCAM search rates is via parallelism, by creating multiple copies of the desired TCAM, which is a linear increase in -area and power (at least). -

    -

    Due to these issues, if one wishes to create a switch ASIC that +area and power (at least).

    +
    +
    +

    Due to these issues, if one wishes to create a switch ASIC that achieves a packet processing rate of N times the clock rate, e.g. 2 billion packets per second in a 1 GHz ASIC, the most straightforward way to do this is to take advantage of the fact that packet processing is (mostly) an embarrassingly parallel -problem14, and create a device with N -pipelines15, each pipeline processing packets at 1 -billion packets per second. -

    -

    A PSA device designed in this way would typically have N ingress +problem[12], and create a device with N +pipelines[13], each pipeline processing packets at 1 +billion packets per second.

    +
    +
    +

    A PSA device designed in this way would typically have N ingress pipelines, plus N egress pipelines. It is common to assign multiple physical ports of the switch ASIC to each pipeline in a hard-wired fashion, e.g. a device with 32 100 Gigabit Ethernet ports @@ -4303,13 +5694,15 @@

    -

    In such a device, typically you will want the same P4 program to be +pipeline 1 if ingress chose output port 16 through 31.

    +

    +
    +

    In such a device, typically you will want the same P4 program to be run on every one of these N ingress pipelines, and every one of the N -egress pipelines. -

    -

    It is physically possible in such a device for the control plane to +egress pipelines.

    +
    +
    +

    It is physically possible in such a device for the control plane to install different table entries in the different pipelines, and there are use cases where this can help achieve higher scale in number of usable table entries. For example, perhaps you have an ingress table @@ -4320,49 +5713,54 @@

    -

    Regardless of this issue, applying tables in P4 is an embarrassingly +implementation dependent.

    +

    +
    +

    Regardless of this issue, applying tables in P4 is an embarrassingly parallel activity, because as long as table entries are installed where they might be matched, pipelines can operate completely independently of each other with no communication between them (one small exception is described below). The same is true for most PSA -externs, e.g. ActionProfile, ActionSelector, Checksum, Digest, -Hash, and Random. What P4 tables and these externs have in common +externs, e.g. ActionProfile, ActionSelector, Checksum, Digest, +Hash, and Random. What P4 tables and these externs have in common is: either the P4 program behavior cannot modify their state at all -(e.g. tables, ActionProfile, ActionSelector), or can only modify +(e.g. tables, ActionProfile, ActionSelector), or can only modify it in a way that does not affect the processing of other packets -(e.g. Checksum, Digest, Hash). Random is a special case here: +(e.g. Checksum, Digest, Hash). Random is a special case here: updating the state of a pseudo-random number generator can affect the processing of other packets, but this is typically not a concern for the way that pseudo-random numbers are used (e.g. randomly choosing -packets to drop or mark in Random Early Detection). -

    -

    For counters, there is counter state maintained independently in each +packets to drop or mark in Random Early Detection).

    +
    +
    +

    For counters, there is counter state maintained independently in each pipeline, but if corresponding counter entries in each pipeline count -“the same thing” (e.g. packets matching a table entry with key X +"the same thing" (e.g. packets matching a table entry with key X installed in all pipelines), then it is straightforward to add up the -corresponding counter values from all pipelines. -

    -

    Consider a device where meter state is maintained independently in +corresponding counter values from all pipelines.

    +
    +
    +

    Consider a device where meter state is maintained independently in each pipeline. If you have a multi-pipeline PSA device, and wish to -achieve the effect: “meter all packets matching table entry X to at -most Y bytes per second”, this is not an embarrassingly parallel +achieve the effect: "meter all packets matching table entry X to at +most Y bytes per second", this is not an embarrassingly parallel problem. It could be done by coordinating state between the pipelines, e.g. using something like cache coherency protocols -commonly implemented within multi-core CPUs, or by “moving the packets -to where the shared state is”, e.g. recirculating packets to a common +commonly implemented within multi-core CPUs, or by "moving the packets +to where the shared state is", e.g. recirculating packets to a common pipeline where the meter state is kept. Both of these techniques lead to lower packet processing performance, at least in some cases, and both add complexity to the system. It is typical in network switches to simply maintain the meter state independently in each pipeline and -not coordinate it, and accept the resulting behavior. -

    -

    This issue is not specific to packet switches. It falls under the +not coordinate it, and accept the resulting behavior.

    +
    +
    +

    This issue is not specific to packet switches. It falls under the category of accessing mutable state in a distributed system, with not -only correctness concerns, but very strong performance concerns. -

    -

    The Register extern is more general in its capability than meters, +only correctness concerns, but very strong performance concerns.

    +
    +
    +

    The Register extern is more general in its capability than meters, and the same potential issue of state being split across multiple pipelines exists. It is recommended that you talk to your PSA device vendor about this issue if it could affect features you wish to write @@ -4370,11 +5768,12 @@

    -

    Note that the proposed psa_idle_timeout table property introduces a -way by which doing table apply operations does update state within -a P4 table. Each table entry requires at least one, and more likely several, bits of state to represent a “last matched time” value, and this value is updated with every apply operation. If this state in +place where the state is maintained.

    +

    +
    +

    Note that the proposed psa_idle_timeout table property introduces a +way by which doing table apply operations does update state within +a P4 table. Each table entry requires at least one, and more likely several, bits of state to represent a "last matched time" value, and this value is updated with every apply operation. If this state in tables with this option is not automatically coordinated between pipelines, then it can differ for corresponding table entries in different pipelines. An entry with key X in one @@ -4384,91 +5783,110 @@

    -

    For programming multiple pipelines, it is the responsibility of the +tables and externs it contains, a distinct name.

    +

    +
    +

    For programming multiple pipelines, it is the responsibility of the vendor and of target dependent tools to specify how PSA programs are mapped to multiple pipelines. An implementation may use a copy of the -PSA program on each pipeline, thus keeping pipelines fully isolated. -

    F. Appendix: Packet ordering

    -

    This section describes recommendations for PSA implementations on +PSA program on each pipeline, thus keeping pipelines fully isolated.

    +
    +
    +
    +
    +

    Appendix F: Packet ordering

    +
    +
    +

    This section describes recommendations for PSA implementations on the order that packets are processed. These are not requirements, since there are known implementation techniques, especially parallelism that can be taken advantage of in a variety of ways, that can lead to cheaper implementations if these recommendations are not followed. We recommend that developers selecting P4 devices ask their designers about these issues, if those developers consider the issues -important for their purposes. -

    -

    Recommendation 1: Packets that arrive on the same input port should -begin ingress processing in the same relative order as they arrived. -

    -

    Recommendation 2: Packets that go out on the same output port should +important for their purposes.

    +
    +
    +

    Recommendation 1: Packets that arrive on the same input port should +begin ingress processing in the same relative order as they arrived.

    +
    +
    +

    Recommendation 2: Packets that go out on the same output port should be transmitted on the port in the same relative order that they began -egress processing. -

    -

    Recommendation 3: PRE unicast packets (i.e. those that follow the -“enqueue one packet” path in the pseudocode of section -6.2) that arrived from the same ingress port, did -ingress processing once (i.e. were not resubmitted or recirculated), -were sent to the PRE with the same class_of_service value, and -destined for the same egress port, should begin egress processing in -the same relative order as they began ingress processing. -

    -

    It is expected that some PSA implementations will implement the class +egress processing.

    +
    +
    +

    Recommendation 3: PRE unicast packets (i.e. those that follow the +"enqueue one packet" path in the pseudocode of Section 7.3) +that arrived from the same ingress port, did ingress processing once +(i.e. were not resubmitted or recirculated), were sent to the PRE with +the same class_of_service value, and destined for the same egress +port, should begin egress processing in the same relative order as they +began ingress processing.

    +
    +
    +

    It is expected that some PSA implementations will implement the class of service mechanism by having a separate FIFO queue per class of service, and thus while unicast packets with the same ingress port, egress port, and class of service will pass through the system in FIFO order if they follow all recommendations above, unicast packets with the same ingress and egress port, but different classes of service, may be processed by the egress control block in a different order than -they were processed by the ingress control block. -

    -

    If an implementation satisfies recommendations 1 through 3, then +they were processed by the ingress control block.

    +
    +
    +

    If an implementation satisfies recommendations 1 through 3, then unicast traffic assigned to the same class of service will maintain -its relative order through the device. -

    -

    Recommendation 4: Consider PRE multicast packets (i.e. those that -follow the “Make 0 or more copies” path in the pseudocode of section -6.2) that arrived on the same ingress port, did +its relative order through the device.

    +
    +
    +

    Recommendation 4: Consider PRE multicast packets (i.e. those that +follow the "Make 0 or more copies" path in the pseudocode of +Section 7.3) that arrived on the same ingress port, did ingress processing once, and were sent to the PRE with the same pair -of values (class_of_service, multicast_group). For copies of +of values (class_of_service, multicast_group). For copies of those original packets that are destined to the same egress port, and -with the same pair of values for (egress_port, instance), those +with the same pair of values for (egress_port, instance), those copies should begin egress processing in the same relative order as -the original packets began ingress processing. -

    -

    There is no such expectation for multicast packets with different -class_of_service values, again because of separate queues in the PRE -for different class_of_service values. -

    -

    It is also understood that for a short period of time after the +the original packets began ingress processing.

    +
    +
    +

    There is no such expectation for multicast packets with different +class_of_service values, again because of separate queues in the PRE +for different class_of_service values.

    +
    +
    +

    It is also understood that for a short period of time after the control plane modifies the set of copies to be made for a particular -multicast_group value, that it may be especially difficult to +multicast_group value, that it may be especially difficult to satisfy Recommendation 4. That recommendation is intended to apply only when the set of copies to be made for the multicast group has -remained unchanged for a period of time. -

    -

    If an implementation satisfies recommendations 1, 2, and 4, then +remained unchanged for a period of time.

    +
    +
    +

    If an implementation satisfies recommendations 1, 2, and 4, then multicast traffic assigned to the same class of service will maintain its relative order through the device, when multicast group membership -has been stable for long enough. -

    -

    Note that there is no recommendation to enforce any relative ingress +has been stable for long enough.

    +
    +
    +

    Note that there is no recommendation to enforce any relative ingress to egress processing order of unicast packets vs. multicast packets. Commonly used mechanisms for creating multicast copies in the PRE -allow unicast packets to “go around” the packet replication logic, +allow unicast packets to "go around" the packet replication logic, which is unnecessary for unicast packets, and thus change the relative order of such packets there. Also, it is common in packet buffers to -use separate queues for multicast traffic versus unicast. -

    -

    We give some motivations for these recommendations below. -

    -
      -
    1. Expectations of hosts -
    - -

    While the Internet Protocol does not have strong ordering requirements +use separate queues for multicast traffic versus unicast.

    +
    +
    +

    We give some motivations for these recommendations below.

    +
    +
    +
      +
    1. +

      Expectations of hosts

      +
      +

      While the Internet Protocol does not have strong ordering requirements for sequences of packets sent by one host to another, there are still widely deployed implementations of TCP that lead to significantly degraded throughput when the network frequently delivers packets to @@ -4478,41 +5896,43 @@

      's work16 for some of the research done +Kandula et al’s work[14] for some of the research done towards making TCP more robust in the face of network packet -reordering. -

      -

      Such TCP implementations interpret acknowledgements with repeated +reordering.

      +

      +
      +

      Such TCP implementations interpret acknowledgements with repeated cumulative acknowledgement sequence numbers as a likely indication of packet loss in the network, and reduce their sending window in an -effort to avoid network congestion. -

      -

      While applications using UDP should also be aware of possible packet +effort to avoid network congestion.

      +
      +
      +

      While applications using UDP should also be aware of possible packet reordering in a network, some of them behave poorly if this reordering -becomes common17. -

      -

      These expectations of hosts are a significant reason why ECMP (Equal +becomes common[15].

      +
      +
      +

      These expectations of hosts are a significant reason why ECMP (Equal Cost Multi Path) path selection and LAG (Link Aggregation Group) link selection are so often done by using a hash of packet header fields such as IP source and destination address, and TCP or UDP source and destination port. Choosing among parallel paths in this way helps to preserve the order of packets in the same application flow, at the cost of not balancing the load as evenly as possible. If a network -device's internal implementation reordered packets, it would be an -independent source of network reordering. -

      -
        -
      1. Implementation of stateful protocols -
      - -

      This reason is much less significant than the previous one. We +device’s internal implementation reordered packets, it would be an +independent source of network reordering.

      +
      +
    2. +
    3. +

      Implementation of stateful protocols

      +
      +

      This reason is much less significant than the previous one. We mention it here primarily so implementers of networking protocols are aware of the issue. This issue is only relevant for a relatively -small fraction of network protocols. -

      -

      Some networking protocols add sequence numbers to packets, and require +small fraction of network protocols.

      +
      +
      +

      Some networking protocols add sequence numbers to packets, and require either dropping packets that arrive out of sequence number order at a later network point (e.g. GRE with sequence numbers enabled), or with a looser check that allows some amount of network reordering to occur @@ -4520,266 +5940,346 @@

      -

      Similarly, there are some protocols like IP header compression, where +the performance of these protocols.

      +

      +
      +

      Similarly, there are some protocols like IP header compression, where multiple variants have been developed, some with more or less -robustness in the face of network reordering. -

      G. Appendix: Supporting empty action selector groups

      -

      As mentioned in Section 7.12, a PSA data plane -implementation need not implement any specific defined behavior if one -attempts to add a table entry for a key that points at a group that is -currently empty. -

      -

      Some P4 users have expressed an interest in enabling a P4Runtime +robustness in the face of network reordering.

      +
      +
    4. +
    +
    +
    +
    +
    +

    Appendix G: Supporting empty action selector groups

    +
    +
    +

    As mentioned in Section 8.12, a PSA data plane implementation +need not implement any specific defined behavior if one attempts to add +a table entry for a key that points at a group that is currently empty.

    +
    +
    +

    Some P4 users have expressed an interest in enabling a P4Runtime client (hereafter called the controller) to remove the last member of an action selector group, and have the data plane behave in a -predictable way. -

    -

    For example, if one has a table that maps logical interface id numbers +predictable way.

    +
    +
    +

    For example, if one has a table that maps logical interface id numbers to physical port numbers, using an action selector to implement LAG (Link Aggregation Group), what should the controller do when there was only one physical port in a LAG that was enabled, and that port goes -down? A straightforward desired behavior from the controller's +down? A straightforward desired behavior from the controller’s perspective is: issue the P4Runtime command to remove the last member -from the group, and have an empty group action of “drop the packet” +from the group, and have an empty group action of "drop the packet" take effect for any packets applying the table and selecting the empty -group. -

    -

    Fully supporting empty action groups should meet all of these -requirements: -

    -
      -
    • All P4Runtime API operations such as adding a member to a group +group.

      +
    +
    +

    Fully supporting empty action groups should meet all of these +requirements:

    +
    +
    +
      +
    • +

      All P4Runtime API operations such as adding a member to a group (even when changing from empty to 1 member), removing a member from a group (even when changing from 1 member to empty), modifying the action associated with a member, etc. should be atomic relative to data packet processing. That is, every packet should be processed as if the table was in the old state, or the new state, with no -undefined packet processing behavior. +undefined packet processing behavior.

    • -
    • The empty group action that is executed when the matching table +
    • +

      The empty group action that is executed when the matching table entry points at an empty group, may have an action name that is the same as, or different than, the action name used by the group when it was non-empty (if it was non-empty, and then became empty by -removing its last member). -

    - -

    A high performance implementation will also be able to make changes to +removing its last member).

    + + +
    +
    +

    A high performance implementation will also be able to make changes to group membership using a number of data plane update operations that does not grow with the number of table entries that point at the -group. -

    -

    Achieving all of these requirements seems not to be possible with a +group.

    +
    +
    +

    Achieving all of these requirements seems not to be possible with a PSA data plane implementation that meets only the minimum requirements for action selectors, i.e. one that restricts members of a group to all have the same action name, and that does not natively support -predictable behavior if a group is empty in the data plane. -

    -

    Below we describe one way that achieves the goals above, but only for +predictable behavior if a group is empty in the data plane.

    +
    +
    +

    Below we describe one way that achieves the goals above, but only for a data plane implementation that supports multiple different action names in the same group at the same time. For a data plane that does -not support this, the idea only nearly achieves the goal. It +not support this, the idea only nearly achieves the goal. It requires the empty group action to have the same action name as the non-empty groups of the action selector. This may be too onerous of a -restriction on system developers to be worth implementing. -

    -

    This behavior can be implemented via a small extra bit of logic in the +restriction on system developers to be worth implementing.

    +
    +
    +

    This behavior can be implemented via a small extra bit of logic in the P4Runtime server implementation (hereafter called the agent). The basic idea is that the agent obtains the empty group action, with action parameter values, e.g. from the compiled representation of the -P4 program. -

    -

    If no table entry currently “points at” an empty group G, then G's +P4 program.

    +
    +
    +

    If no table entry currently "points at" an empty group G, then G’s empty group action need not be installed anywhere in the data plane. Similarly if G is currently non-empty, and there are table entries -currently pointing at G. -

    -

    Suppose there is currently one member in G, and G is pointed at by at +currently pointing at G.

    +
    +
    +

    Suppose there is currently one member in G, and G is pointed at by at least one table entry. The controller issues a command to remove that -only member from G. -

    -

    The agent can implement this command by the following sequence of -changes in the data plane: -

    -
      -
    1. Add to G a new member, which is the empty group action. Now in the -data plane, for a short time, G contains two members. -
    2. -
    3. Remove from G the member that the controller requested to be +only member from G.

      +
    +
    +

    The agent can implement this command by the following sequence of +changes in the data plane:

    +
    +
    +
      +
    1. +

      Add to G a new member, which is the empty group action. Now in the +data plane, for a short time, G contains two members.

      +
    2. +
    3. +

      Remove from G the member that the controller requested to be removed. Now the data plane version of G has only one member containing the empty group action, so all packets using G will execute -that action. -

    - -

    When G is currently empty as far as the controller is concerned (but +that action.

    + + +
    +
    +

    When G is currently empty as far as the controller is concerned (but contains one member pointing at the empty group action in the data plane), and the controller adds one member to it, the agent can -implement this via these steps: -

    -
      -
    1. Add to G the member requested by the controller. The data plane -temporarily has two members for G, including the empty group action. +implement this via these steps:

      +
    +
    +
      +
    1. +

      Add to G the member requested by the controller. The data plane +temporarily has two members for G, including the empty group action.

    2. -
    3. Remove from G the empty group action. Now G in the data plane is back -to the one member that the controller wants. -
    - -

    A PSA implementation with an agent that implements empty action +

  • +

    Remove from G the empty group action. Now G in the data plane is back +to the one member that the controller wants.

    +
  • + +
    +
    +

    A PSA implementation with an agent that implements empty action selector groups in this way must implement each of the two steps for -such transitions in an atomic way, as described in Section -8, but it is allowed +such transitions in an atomic way, as described in +Chapter 9, but it is allowed for one or more packets to be processed in the intermediate state that -exists between the two steps. -

    -

    If a PSA implementation supports multiple different action names in +exists between the two steps.

    +
    +
    +

    If a PSA implementation supports multiple different action names in the same group at the same time, then there is no need to read further. Below we only describe what might be done for a data plane that restricts each group to contain only actions with the same action -name. -

    -

    Because a PSA implementation need not support multiple different +name.

    +
    +
    +

    Because a PSA implementation need not support multiple different action types in the same action selector group at the same time -(mentioned in Section 7.12), a developer wishing to +(mentioned in Section 8.12), a developer wishing to take advantage of this in a portable way may need to modify one or -more of the actions used in their tables that use action selectors. -

    -

    For example, if in the LAG port selection example mentioned earlier, -there was only one action for a table lag defined like this: -

    -
    -
    -
        action set_output_port (PortId_t p) {
    -        user_meta.out_port = p;
    -    }
    -    ActionProfile(128) ap;
    -    table lag {
    -        key = {
    -            // ... key fields go here ...
    -        }
    -        actions = { set_output_port; }
    -        psa_implementation = ap;
    -    }
    -
    -    control cIngress (
    -                inout headers hdr,
    -                inout metadata user_meta,
    -                in    psa_ingress_input_metadata_t  istd_meta,
    -                inout psa_ingress_output_metadata_t ostd_meta
    -    ) {
    -        apply {
    -            // ... earlier ingress code goes here ...
    -            lag.apply();
    -            send_to_port(ostd_meta, user_meta.out_port);
    -            // ... later ingress code goes here ...
    -        }
    -    }
    -

    with a single action parameter equal to a physical port number of the +more of the actions used in their tables that use action selectors.

    +
    +
    +

    For example, if in the LAG port selection example mentioned earlier, +there was only one action for a table lag defined like this:

    +
    +
    +
    +
        action set_output_port (PortId_t p) {
    +        user_meta.out_port = p;
    +    }
    +    ActionProfile(128) ap;
    +    table lag {
    +        key = {
    +            // ... key fields go here ...
    +        }
    +        actions = { set_output_port; }
    +        psa_implementation = ap;
    +    }
    +
    +    control cIngress (
    +                inout headers hdr,
    +                inout metadata user_meta,
    +                in    psa_ingress_input_metadata_t  istd_meta,
    +                inout psa_ingress_output_metadata_t ostd_meta
    +    ) {
    +        apply {
    +            // ... earlier ingress code goes here ...
    +            lag.apply();
    +            send_to_port(ostd_meta, user_meta.out_port);
    +            // ... later ingress code goes here ...
    +        }
    +    }
    +
    +
    +
    +

    with a single action parameter equal to a physical port number of the device, one of the following approaches could be used, and of course -there are likely to be other approaches not mentioned here. -

    -
      -
    • Approach 1: Use an invalid port number -
    - -

    Pick a value of type PortId_t that corresponds to no valid physical +there are likely to be other approaches not mentioned here.

    +
    +
    +
      +
    • +

      Approach 1: Use an invalid port number

      +
    • +
    +
    +
    +

    Pick a value of type PortId_t that corresponds to no valid physical port of the device (TBD whether PSA should define such a value with a -name it currently doesn't guarantee that such a value even exists -for type PortId_t) and use that value for the empty group action of an -empty group. In the code after lag.apply, add an if statement -checking for that invalid value, like this: -

    -
    -
    -
            apply {
    -            // ... earlier ingress code goes here ...
    -            lag.apply();
    -            if (user_meta.out_port == PORT_INVALID_VALUE) {
    -                ingress_drop(ostd_meta);
    -            } else {
    -                send_to_port(ostd_meta, user_meta.out_port);
    -            }
    -            // ... later ingress code goes here ...
    -        }
    -
      -
    • Approach 2: Add extra parameters to the action -
    - -

    In this case, add a 1-bit parameter indicating whether to drop the -packet later. An if statement is still needed after applying the -table. -

    -
    -
    -
        action set_output_port (PortId_t p, bit<1> drop) {
    -        user_meta.out_port = p;
    -        user_meta.drop = drop;
    -    }
    -
    -    // ...
    -
    -        apply {
    -            // ... earlier ingress code goes here ...
    -            lag.apply();
    -            if (user_meta.drop == 1) {
    -                ingress_drop(ostd_meta);
    -            } else {
    -                send_to_port(ostd_meta, user_meta.out_port);
    -            }
    -            // ... later ingress code goes here ...
    -        }
    -

    In either case, an implementation might also support putting the if -statement inside of the action set_output_port, but this is not -required by PSA. -

    H. Appendix: Revision History

    H.1. Changes made in version 1.2

    -

    Version 1.2 was released December 22, 2022. -

    -

    Summary of changes. For some of these, additional explanation is -provided in sub-sections below. -

    -
      -
    • Add @p4runtime_translation annotations for all seven PSA numeric -types in psa.p4 (all changes in file psa.p4). -
    • -
    • Add clarifications on @p4runtime_translation annotation behavior -(section 4.4). -
    • -
    • Add documentation about the behavior of each match_kind (section -4.3). -
    • -
    • Add explanation why there is no packet length field defined in PSA -(section 6.4). -
    • -
    • Add match_kind optional (section 4.3). -
    • -
    • Changed psa.p4 file explicitly so that it is not usable for -compiling, by adding an #error preprocessor directive to it. -Added a copy of psa-for-bmv2.p4 from open source p4c compiler to -examples directory for quick testing of correct syntax of example -programs (all changes in file psa.p4). -
    • -
    • Remove control plane API function signatures from comments in file -psa.p4 (all changes in file psa.p4). -
    • -
    • Replace @noWarnUnused with standard @noWarn("unused") -annotations in psa.p4 (all changes in file psa.p4). -
    • -
    • Add assert and assume extern functions (all changes in file psa.p4). -
    • -
    • Clarifications of the behavior for different possible values of -table property psa_idle_timeout (section 7.2.1). -
    -

    H.1.1. Add match_kind optional

    -

    This match kind was added to the v1model architecture in early 2020, +name — it currently doesn’t guarantee that such a value even exists +for type PortId_t) and use that value for the empty group action of an +empty group. In the code after lag.apply, add an if statement +checking for that invalid value, like this:

    +
    +
    +
    +
            apply {
    +            // ... earlier ingress code goes here ...
    +            lag.apply();
    +            if (user_meta.out_port == PORT_INVALID_VALUE) {
    +                ingress_drop(ostd_meta);
    +            } else {
    +                send_to_port(ostd_meta, user_meta.out_port);
    +            }
    +            // ... later ingress code goes here ...
    +        }
    +
    +
    +
    +
      +
    • +

      Approach 2: Add extra parameters to the action

      +
    • +
    +
    +
    +

    In this case, add a 1-bit parameter indicating whether to drop the +packet later. An if statement is still needed after applying the +table.

    +
    +
    +
    +
        action set_output_port (PortId_t p, bit<1> drop) {
    +        user_meta.out_port = p;
    +        user_meta.drop = drop;
    +    }
    +
    +    // ...
    +
    +        apply {
    +            // ... earlier ingress code goes here ...
    +            lag.apply();
    +            if (user_meta.drop == 1) {
    +                ingress_drop(ostd_meta);
    +            } else {
    +                send_to_port(ostd_meta, user_meta.out_port);
    +            }
    +            // ... later ingress code goes here ...
    +        }
    +
    +
    +
    +

    In either case, an implementation might also support putting the if +statement inside of the action set_output_port, but this is not +required by PSA.

    +
    +
    +
    +
    +

    Appendix H: Revision History

    +
    +
    +

    H.1. Changes made in version 1.2

    +
    +

    Version 1.2 was released December 22, 2022.

    +
    +
    +

    Summary of changes. For some of these, additional explanation is +provided in sub-sections below.

    +
    +
    +
      +
    • +

      Add @p4runtime_translation annotations for all seven PSA numeric +types in psa.p4 (all changes in file psa.p4).

      +
    • +
    • +

      Add clarifications on @p4runtime_translation annotation behavior +(Chapter 5).

      +
    • +
    • +

      Add documentation about the behavior of each match_kind +(Section 4.3).

      +
    • +
    • +

      Add explanation why there is no packet length field defined in PSA +(Section 7.5).

      +
    • +
    • +

      Add match_kind optional (Section 4.3).

      +
    • +
    • +

      Changed psa.p4 file explicitly so that it is not usable for +compiling, by adding an #error preprocessor directive to it. +Added a copy of psa-for-bmv2.p4 from open source p4c compiler to +examples directory for quick testing of correct syntax of example +programs (all changes in file psa.p4).

      +
    • +
    • +

      Remove control plane API function signatures from comments in file +psa.p4 (all changes in file psa.p4).

      +
    • +
    • +

      Replace @noWarnUnused with standard @noWarn("unused") +annotations in psa.p4 (all changes in file psa.p4).

      +
    • +
    • +

      Add assert and assume extern functions (all changes in file psa.p4).

      +
    • +
    • +

      Clarifications of the behavior for different possible values of +table property psa_idle_timeout (Section 8.2.1).

      +
    • +
    +
    +
    +

    H.1.1. Add match_kind optional

    +
    +

    This match kind was added to the v1model architecture in early 2020, and seems useful in that it (a) provides additional information about how the control plane plans to configure the matching rules of a table, and (b) the additional restrictions it has over match kind -ternary provides opportunities for target devices to implement it -more efficiently than ternary. -

    H.1.2. Remove control plane API function signatures

    -

    As a historical note, the control plane API function signatures that +ternary provides opportunities for target devices to implement it +more efficiently than ternary.

    +
    +
    +
    +

    H.1.2. Remove control plane API function signatures

    +
    +

    As a historical note, the control plane API function signatures that were formerly given in comments preceded by the string -@ControlPlaneAPI were added very early in the process of writing +@ControlPlaneAPI were added very early in the process of writing version 1.0 of the PSA specification, and were never reviewed thoroughly. The P4Runtime API specification version 1.0 was being developed at the same time as version 1.0 of the PSA specification. @@ -4787,177 +6287,262 @@

    H.2. Changes made in version 1.1

    -

    Version 1.1 was released November 22, 2018. -

    -

    Summary of changes. For some of these, additional explanation is -provided in sub-sections below. -

    -
      -
    • Define numeric translation between P4Runtime API control plane and -data plane values. +signatures were required to be implemented.

      +
    +
    +
    +
    +

    H.2. Changes made in version 1.1

    +
    +

    Version 1.1 was released November 22, 2018.

    +
    +
    +

    Summary of changes. For some of these, additional explanation is +provided in sub-sections below.

    +
    +
    +
      +
    • +

      Define numeric translation between P4Runtime API control plane and +data plane values.

      +
    • +
    • +

      Add the ability for packet clone sessions to create multiple copies.

    • -
    • Add the ability for packet clone sessions to create multiple copies. +
    • +

      Add psa_idle_timeout table property.

    • -
    • Add psa_idle_timeout table property. +
    • +

      Add psa_empty_group_action table property.

    • -
    • Add psa_empty_group_action table property. +
    • +

      No longer require PSA implementation to support Digest extern +instances in the egress pipeline.

    • -
    • No longer require PSA implementation to support Digest extern -instances in the egress pipeline. +
    • +

      Several changes to the psa.p4 include file.

    • -
    • Several changes to the psa.p4 include file. +
    • +

      Several changes to example PSA programs in the examples directory.

    • -
    • Several changes to example PSA programs in the examples directory. -
    -

    H.2.1. Numeric translation between P4Runtime API values and data plane values

    -

    There was a series of meetings after PSA v1.0 was released to refine + +

    +
    +

    H.2.1. Numeric translation between P4Runtime API values and data plane values

    +
    +

    There was a series of meetings after PSA v1.0 was released to refine the details of the plan to do numeric translation of values with type -PortId_t (and ClassOfService_t, and optionally other types in the +PortId_t (and ClassOfService_t, and optionally other types in the future). PSA v1.1 reflects the latest design for how to accomplish -this. Changes can be found here: -

    -
      -
    • Section 4.1 “PSA type definitions” -
    • -
    • Section 4.4 “Data plane vs. control plane data representations” -
    -

    H.2.2. Add the ability for packet clone sessions to create multiple copies

    -

    In PSA v1.0, requesting to make a clone of a packet was restricted to +this. Changes can be found here:

    +
    +
    +
      +
    • +

      Section 4.1 "PSA type definitions"

      +
    • +
    • +

      Section 4.4 "Data plane vs. control plane data representations"

      +
    • +
    +
    +
    +
    +

    H.2.2. Add the ability for packet clone sessions to create multiple copies

    +
    +

    In PSA v1.0, requesting to make a clone of a packet was restricted to creating a single clone, sent to a single output port. In PSA v1.1 you may now configure a clone session with a set of -(egress_port, instance) pairs, similar to how a multicast group can -be configured. Changes can be found here: -

    -
      -
    • Section 6.2. “Behavior of packets after ingress processing is complete” +(egress_port, instance) pairs, similar to how a multicast group can +be configured. Changes can be found here:

      +
    +
    +
      +
    • +

      Section 6.2. "Behavior of packets after ingress processing is complete"

      +
    • +
    • +

      Section 6.4.5 "Multicast and clone copies" (formerly called "Multicast copies")

    • -
    • Section 6.4.5 “Multicast and clone copies” (formerly called “Multicast copies”) +
    • +

      Section 6.5 "Behavior of packets after egress processing is complete"

    • -
    • Section 6.5 “Behavior of packets after egress processing is complete” +
    • +

      Section 6.8 "Packet Cloning"

    • -
    • Section 6.8 “Packet Cloning” -
    -

    H.2.3. Add psa_idle_timeout table property

    -

    Adding this brings PSA v1.1 up to date with the support for this + +

    +
    +
    +

    H.2.3. Add psa_idle_timeout table property

    +
    +

    Adding this brings PSA v1.1 up to date with the support for this feature in the P4Runtime API. Using this table property enables the P4 developer to specify that a table must maintain some state of when the last time each table entry was matched, and if an entry remains unmatched for longer than a time configured by the controller, then a -notification message should be sent to the controller. -

    -
      -
    • Section 7.2.1 “Table entry timeout notification” -
    -

    H.2.4. Add psa_empty_group_action table property

    -

    PSA v1.0 did not specify the behavior of a table with an -ActionSelector implementation, if a packet matched a table entry -that was configured with an empty action selector group. -

    -

    PSA v1.1 recommends (but does not require) that implementations -support a new psa_empty_group_action table property, whose value is -an action that should be executed when this situation occurs. -

    -
      -
    • Section 7.12 “Action Selector” -
    -

    H.2.5. Other changes

    -

    In PSA v1.0, the Digest extern was required to be supported in both -the IngressDeparser and EgressDeparser control blocks. It is now -no longer required to be supported in the EgressDeparser control -block. -

    -
      -
    • Table 5 “Summary of controls that can instantiate and invoke externs” -
    -

    H.2.6. Changes to the psa.p4 include file

    -
      -
    • Updates for the latest plan on P4Runtime API numerical translation -of type PortId_t and ClassOfService_t. -
    • -
    • Eliminate obsolete ValueSet extern, because the value_set -construct was added to the P416 language specification in version -1.1.0. -
    • -
    • Fix a few typos in example control plane APIs in comments. -
    • -
    • Eliminate PSA_SWITCH #define macro with arguments, since the -P416 language spec does not require that the P416 pre-processor -support such macros. -
    -

    H.2.7. Changes to example PSA programs in the p4-16/psa/examples directory

    -
      -
    • Small changes required to bring them in harmony with the latest -details on P4Runtime API numerical translation of type PortId_t. -
    - -
    -
    - - -
    -

    2.It is expected that psa.p4 include files for -different targets will typically be nearly identical to each -other. Besides the possibility of differing bit widths for these -PSA types, the only expected differences between psa.p4 files -for different targets would be annotations on externs, etc. that -the P4 compiler for that target needs to do its job. -

    -
    -

    3.While 10 Mbits sounds tiny to one accustomed to computers with hundreds of gigabytes of DRAM, the highest speed PSA implementations are ASICs that must keep tables in on-chip memories, similar to caches in general purpose CPUs. The Intel i9-7980XE released in 2017 has 198 Mbits of on-chip L3 cache shared by its CPU cores. Among Intel processors in Intel's 7th generation Core released in 2017 with at least 100 Mbits of L3 cache, they all cost close to $9 per Mbit of L3 cache. https://​en.​wikipedia.​org/​wiki/​List_​of_​Intel_​microprocessors -

    -
    -

    4.The open source p4c P4 compiler is -planned to support an option to enable numerical translation for -additional types, without modifying your P4 program, nor the -psa.p4 include file. These additional types would be specified -by their name. -

    -
    -

    5.https://en.wikipedia.org/wiki/Cut-through_switching -

    -
    -

    6.The P4Runtime API is defined as a Google Protocol -Buffer .proto file and an accompanying English specification document -here: -https://​github.​com/​p4lang/​p4runtime -

    - - - - -
    -

    11.Pavol Cerny, Nate Foster, Nilesh Jagnik, and Jedidiah McClurg, “Consistent Network Updates in Polynomial Time”. International Symposium on Distributed Computing (DISC), Paris, France, September 2016. -

    - - - -
    -

    15.Here and elsewhere in P4 specification documents, the term “pipeline” refers to a portion of a P4 implementation that implements, for example, the behavior of IngressParser, Ingress, then IngressDeparser in PSA. This is by now traditional in P4 specifications, and although we will not try to change that here, note that there are other reasonable hardware designs that can accomplish this goal that few would call a pipeline, e.g. a collection of CPU cores running in parallel, each processing different packets. -

    -
    -

    16.S. Kandula, D. Katabi, S. Sinha, and A. Berger, “Dynamic load balancing without packet reordering”, ACM SIGCOMM Computer Communication Review, Vol. 37, No. 2, April 2007 -

    -
    -

    17.M. Laor and L. Gendel, “The effect of packet reordering in a backbone link on application throughput”, IEEE Network, 2002. -

    +notification message should be sent to the controller.

    +
    +
    +
      +
    • +

      Section 7.2.1 "Table entry timeout notification"

      +
    • +
    +
    +
    +
    +

    H.2.4. Add psa_empty_group_action table property

    +
    +

    PSA v1.0 did not specify the behavior of a table with an +ActionSelector implementation, if a packet matched a table entry +that was configured with an empty action selector group.

    +
    +
    +

    PSA v1.1 recommends (but does not require) that implementations +support a new psa_empty_group_action table property, whose value is +an action that should be executed when this situation occurs.

    +
    +
    +
      +
    • +

      Section 7.12 "Action Selector"

      +
    • +
    +
    +
    +
    +

    H.2.5. Other changes

    +
    +

    In PSA v1.0, the Digest extern was required to be supported in both +the IngressDeparser and EgressDeparser control blocks. It is now +no longer required to be supported in the EgressDeparser control +block.

    +
    +
    +
      +
    • +

      Table 5 "Summary of controls that can instantiate and invoke externs"

      +
    • +
    +
    +
    +
    +

    H.2.6. Changes to the psa.p4 include file

    +
    +
      +
    • +

      Updates for the latest plan on P4Runtime API numerical translation +of type PortId_t and ClassOfService_t.

      +
    • +
    • +

      Eliminate obsolete ValueSet extern, because the value_set +construct was added to the P416 language specification in version +1.1.0.

      +
    • +
    • +

      Fix a few typos in example control plane APIs in comments.

      +
    • +
    • +

      Eliminate PSA_SWITCH #define macro with arguments, since the +P416 language spec does not require that the P416 pre-processor +support such macros.

      +
    • +
    +
    +
    +
    +

    H.2.7. Changes to example PSA programs in the p4-16/psa/examples directory

    +
    +
      +
    • +

      Small changes required to bring them in harmony with the latest +details on P4Runtime API numerical translation of type PortId_t.

      +
    • +
    +
    +
    +
    +
    + + +
    +
    +
    +1. It is expected that `psa.p4` include files for different targets will typically be nearly identical to each other. Besides the possibility of differing bit widths for these PSA types, the only expected differences between `psa.p4` files for different targets would be annotations on externs, etc. that the P4 compiler for that target needs to do its job. +
    +
    +2. The P4Runtime API is defined as a Google Protocol Buffer `.proto` file and an accompanying English specification document here: https://github.com/p4lang/p4runtime +
    +
    +3. While 10 Mbits sounds tiny to one accustomed to computers with hundreds of gigabytes of DRAM, the highest speed PSA implementations are ASICs that must keep tables in on-chip memories, similar to caches in general purpose CPUs. The Intel i9-7980XE released in 2017 has 198 Mbits of on-chip L3 cache shared by its CPU cores. Among Intel processors in Intel’s 7th generation Core released in 2017 with at least 100 Mbits of L3 cache, they all cost close to $9 per Mbit of L3 cache. https://en.wikipedia.org/wiki/List_of_Intel_microprocessors +
    +
    +4. The open source `p4c` P4 compiler is planned to support an option to enable numerical translation for additional types, without modifying your P4 program, nor the `psa.p4` include file. These additional types would be specified by their name. +
    + + + + + +
    +10. Pavol Cerny, Nate Foster, Nilesh Jagnik, and Jedidiah McClurg, "Consistent Network Updates in Polynomial Time". International Symposium on Distributed Computing (DISC), Paris, France, September 2016. +
    + + +
    +13. Here and elsewhere in P4 specification documents, the term "pipeline" refers to a portion of a P4 implementation that implements, for example, the behavior of IngressParser, Ingress, then IngressDeparser in PSA. This is by now traditional in P4 specifications, and although we will not try to change that here, note that there are other reasonable hardware designs that can accomplish this goal that few would call a pipeline, e.g. a collection of CPU cores running in parallel, each processing different packets. +
    +
    +14. S. Kandula, D. Katabi, S. Sinha, and A. Berger, "Dynamic load balancing without packet reordering", ACM SIGCOMM Computer Communication Review, Vol. 37, No. 2, April 2007 +
    +
    +15. M. Laor and L. Gendel, "The effect of packet reordering in a backbone link on application throughput", IEEE Network, 2002. +
    +
    + + + - - + \ No newline at end of file diff --git a/docs/PSA.pdf b/docs/PSA.pdf index 73558deb7f..dd8b71c0aa 100644 Binary files a/docs/PSA.pdf and b/docs/PSA.pdf differ