Bumped supported commit of P4Runtime and BMv2

Includes fixes for:
- ONOS-7593: Support for indirect resource Index type
- ONOS-7595: Removed ID from direct resources
- P4Runtime requires unset bits to be 0 in ternary field matches
- Incorrect parsing of flow rule byte counters
- Full entity names in P4Info with top-level control block (fixed only
	for basic.p4, other programs need to be re-compiled and PI IDs in
	respective pipeconf changed)

Change-Id: Ia19aa949c02e363a550e692915c6d6516a2d13d7
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiCounterCellId.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiCounterCellId.java
index da8a882..4b9a70e 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiCounterCellId.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiCounterCellId.java
@@ -35,8 +35,8 @@
     private final long index;
     private final PiTableEntry tableEntry;
 
-    private PiCounterCellId(PiCounterId counterId, PiCounterType counterType, long index,
-                            PiTableEntry tableEntry) {
+    private PiCounterCellId(PiCounterId counterId, PiCounterType counterType,
+                            long index, PiTableEntry tableEntry) {
         this.counterId = counterId;
         this.counterType = counterType;
         this.index = index;
@@ -44,7 +44,9 @@
     }
 
     /**
-     * Returns the identifier of the counter instance where this cell is contained.
+     * Returns the identifier of the counter instance where this cell is
+     * contained. Meaningful only if the counter is of type {@link
+     * PiCounterType#INDIRECT}.
      *
      * @return counter identifier
      */
@@ -62,8 +64,8 @@
     }
 
     /**
-     * Returns the counter index to which this cell ID is associated. Meaningful only if the counter is of type {@link
-     * PiCounterType#INDIRECT}.
+     * Returns the counter index to which this cell ID is associated. Meaningful
+     * only if the counter is of type {@link PiCounterType#INDIRECT}.
      *
      * @return counter index
      */
@@ -72,8 +74,9 @@
     }
 
     /**
-     * Returns the table entry to which this cell ID is associated. Meaningful only if the counter is of type {@link
-     * PiCounterType#DIRECT}, otherwise returns null.
+     * Returns the table entry to which this cell ID is associated. Meaningful
+     * only if the counter is of type {@link PiCounterType#DIRECT}, otherwise
+     * returns null.
      *
      * @return PI table entry or null
      */
@@ -82,16 +85,15 @@
     }
 
     /**
-     * Return a direct counter cell ID for the given counter ID and table entry.
+     * Return a direct counter cell ID for the given counter ID and table
+     * entry.
      *
-     * @param counterId  counter ID
      * @param tableEntry table entry
      * @return counter cell ID
      */
-    public static PiCounterCellId ofDirect(PiCounterId counterId, PiTableEntry tableEntry) {
-        checkNotNull(counterId);
+    public static PiCounterCellId ofDirect(PiTableEntry tableEntry) {
         checkNotNull(tableEntry);
-        return new PiCounterCellId(counterId, PiCounterType.DIRECT, -1, tableEntry);
+        return new PiCounterCellId(null, PiCounterType.DIRECT, -1, tableEntry);
     }
 
     /**
@@ -129,7 +131,8 @@
 
     @Override
     public String toString() {
-        return counterId.toString() + ':'
-                + (counterType == PiCounterType.DIRECT ? tableEntry.toString() : String.valueOf(index));
+        return counterType == PiCounterType.DIRECT
+                ? tableEntry.toString()
+                : counterId.toString() + ':' + String.valueOf(index);
     }
 }
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMeterCellId.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMeterCellId.java
index 09cf7bf..34af8bf 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMeterCellId.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMeterCellId.java
@@ -46,6 +46,7 @@
 
     /**
      * Returns the identifier of the meter instance where this cell is contained.
+     * Meaningful only if the meter is of type {@link PiMeterType#DIRECT}, otherwise returns null.
      *
      * @return meter identifier
      */
@@ -90,14 +91,12 @@
     /**
      * Return a direct meter cell ID for the given meter ID and table entry.
      *
-     * @param meterId  meter ID
      * @param tableEntry table entry
      * @return meter cell ID
      */
-    public static PiMeterCellId ofDirect(PiMeterId meterId, PiTableEntry tableEntry) {
-        checkNotNull(meterId);
+    public static PiMeterCellId ofDirect(PiTableEntry tableEntry) {
         checkNotNull(tableEntry);
-        return new PiMeterCellId(meterId, PiMeterType.DIRECT, -1, tableEntry);
+        return new PiMeterCellId(null, PiMeterType.DIRECT, -1, tableEntry);
     }
 
     /**
@@ -135,7 +134,8 @@
 
     @Override
     public String toString() {
-        return meterId.toString() + ':'
-                + (meterType == PiMeterType.DIRECT ? tableEntry.toString() : String.valueOf(index));
+        return meterType == PiMeterType.DIRECT
+                ? tableEntry.toString()
+                : meterId.toString() + ':' + String.valueOf(index);
     }
 }
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java
index 18b31f2..7b8797a 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java
@@ -32,8 +32,6 @@
 @Beta
 public final class PiTableEntry implements PiEntity {
 
-    public static final PiTableEntry EMTPY = new PiTableEntry();
-
     private static final int NO_PRIORITY = -1;
     private static final double NO_TIMEOUT = -1;
 
@@ -44,15 +42,6 @@
     private final int priority;
     private final double timeout;
 
-    private PiTableEntry() {
-        this.tableId = null;
-        this.matchKey = null;
-        this.tableAction = null;
-        this.cookie = 0;
-        this.priority = NO_PRIORITY;
-        this.timeout = NO_TIMEOUT;
-    }
-
     private PiTableEntry(PiTableId tableId, PiMatchKey matchKey,
                          PiTableAction tableAction, long cookie, int priority, double timeout) {
         this.tableId = tableId;
diff --git a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiCounterCellDataTest.java b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiCounterCellDataTest.java
index 4104050..6676470 100644
--- a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiCounterCellDataTest.java
+++ b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiCounterCellDataTest.java
@@ -19,7 +19,6 @@
 import com.google.common.testing.EqualsTester;
 import org.junit.Test;
 import org.onosproject.net.pi.model.PiActionId;
-import org.onosproject.net.pi.model.PiCounterId;
 import org.onosproject.net.pi.model.PiTableId;
 
 import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
@@ -30,9 +29,6 @@
  */
 public class PiCounterCellDataTest {
 
-    private static final PiCounterId PI_COUNTER_ID_1 = PiCounterId.of("Name1");
-    private static final PiCounterId PI_COUNTER_ID_2 = PiCounterId.of("Name2");
-
     private static final PiTableEntry PI_TABLE_ENTRY_1 = PiTableEntry.builder()
             .forTable(PiTableId.of("T10"))
             .withCookie(0xac)
@@ -49,9 +45,9 @@
             .build();
 
     private static final PiCounterCellId PI_COUNTER_CELL_ID_1 =
-            PiCounterCellId.ofDirect(PI_COUNTER_ID_1, PI_TABLE_ENTRY_1);
+            PiCounterCellId.ofDirect(PI_TABLE_ENTRY_1);
     private static final PiCounterCellId PI_COUNTER_CELL_ID_2 =
-            PiCounterCellId.ofDirect(PI_COUNTER_ID_2, PI_TABLE_ENTRY_2);
+            PiCounterCellId.ofDirect(PI_TABLE_ENTRY_2);
 
     private static final long PACKETS_1 = 10;
     private static final long PACKETS_2 = 20;
@@ -83,4 +79,4 @@
                 .addEqualityGroup(PI_COUNTER_CELL_DATA_2)
                 .testEquals();
     }
-}
\ No newline at end of file
+}
diff --git a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiTableEntryTest.java b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiTableEntryTest.java
index 0b451e8..087eb77 100644
--- a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiTableEntryTest.java
+++ b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiTableEntryTest.java
@@ -81,16 +81,6 @@
     }
 
     /**
-     * Tests equality of the empty table entry.
-     */
-    @Test
-    public void testEmptyEquals() {
-        new EqualsTester()
-                .addEqualityGroup(PiTableEntry.EMTPY, PiTableEntry.EMTPY)
-                .testEquals();
-    }
-
-    /**
      * Tests creation of a DefaultFlowRule using a FlowRule constructor.
      */
     @Test
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java
index 85c763c..f22fdde 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java
@@ -59,6 +59,7 @@
 
 import static java.lang.String.format;
 import static org.onlab.util.ImmutableByteSequence.ByteSequenceTrimException;
+import static org.onlab.util.ImmutableByteSequence.prefixOnes;
 import static org.onosproject.net.flow.criteria.Criterion.Type.PROTOCOL_INDEPENDENT;
 import static org.onosproject.net.pi.impl.CriterionTranslatorHelper.translateCriterion;
 import static org.onosproject.net.pi.impl.PiUtils.getInterpreterOrNull;
@@ -128,7 +129,17 @@
         }
 
         if (needPriority) {
-            tableEntryBuilder.withPriority(rule.priority());
+            // In the P4 world 0 is the highest priority, in ONOS the lowest one.
+            // FIXME: move priority conversion to the P4Runtime driver
+            final int newPriority;
+            if (rule.priority() > MAX_PI_PRIORITY) {
+                log.warn("Flow rule priority too big, setting translated priority to max value {}: {}",
+                         MAX_PI_PRIORITY, rule);
+                newPriority = 0;
+            } else {
+                newPriority = MAX_PI_PRIORITY - rule.priority();
+            }
+            tableEntryBuilder.withPriority(newPriority);
         }
 
         if (!rule.isPermanent()) {
@@ -290,7 +301,6 @@
             PiMatchFieldId fieldId = fieldModel.id();
 
             int bitWidth = fieldModel.bitWidth();
-            int fieldByteWidth = (int) Math.ceil((double) bitWidth / 8);
 
             Optional<Criterion.Type> criterionType =
                     interpreter == null
@@ -304,17 +314,8 @@
                 // Can ignore if the match is ternary or LPM.
                 switch (fieldModel.matchType()) {
                     case TERNARY:
-                        // Wildcard the whole field.
-                        fieldMatches.put(fieldId, new PiTernaryFieldMatch(
-                                fieldId,
-                                ImmutableByteSequence.ofZeros(fieldByteWidth),
-                                ImmutableByteSequence.ofZeros(fieldByteWidth)));
-                        break;
                     case LPM:
-                        // LPM with prefix 0
-                        fieldMatches.put(fieldId, new PiLpmFieldMatch(fieldId,
-                                                                      ImmutableByteSequence.ofZeros(fieldByteWidth),
-                                                                      0));
+                        // Skip field.
                         break;
                     // FIXME: Can we handle the case of RANGE or VALID match?
                     default:
@@ -404,7 +405,9 @@
         /*
         Here we try to be robust against wrong size fields with the goal of having PiCriterion independent of the
         pipeline model. We duplicate the field match, fitting the byte sequences to the bit-width specified in the
-        model. This operation is expensive when performed for each field match of each flow rule, but should be
+        model. We also normalize ternary (and LPM) field matches by setting to 0 unused bits, as required by P4Runtime.
+
+        These operations are expensive when performed for each field match of each flow rule, but should be
         mitigated by the translation cache provided by PiFlowRuleTranslationServiceImpl.
         */
 
@@ -414,9 +417,12 @@
                     return new PiExactFieldMatch(fieldMatch.fieldId(),
                                                  ((PiExactFieldMatch) fieldMatch).value().fit(modelBitWidth));
                 case TERNARY:
-                    return new PiTernaryFieldMatch(fieldMatch.fieldId(),
-                                                   ((PiTernaryFieldMatch) fieldMatch).value().fit(modelBitWidth),
-                                                   ((PiTernaryFieldMatch) fieldMatch).mask().fit(modelBitWidth));
+                    PiTernaryFieldMatch ternField = (PiTernaryFieldMatch) fieldMatch;
+                    ImmutableByteSequence ternMask = ternField.mask().fit(modelBitWidth);
+                    ImmutableByteSequence ternValue = ternField.value()
+                            .fit(modelBitWidth)
+                            .bitwiseAnd(ternMask);
+                    return new PiTernaryFieldMatch(fieldMatch.fieldId(), ternValue, ternMask);
                 case LPM:
                     PiLpmFieldMatch lpmfield = (PiLpmFieldMatch) fieldMatch;
                     if (lpmfield.prefixLength() > modelBitWidth) {
@@ -424,9 +430,13 @@
                                 "Invalid prefix length for LPM field '%s', found %d but field has bit-width %d",
                                 fieldMatch.fieldId(), lpmfield.prefixLength(), modelBitWidth));
                     }
+                    ImmutableByteSequence lpmMask = prefixOnes(modelBitWidth * Byte.SIZE,
+                                                               lpmfield.prefixLength());
+                    ImmutableByteSequence lpmValue = lpmfield.value()
+                            .fit(modelBitWidth)
+                            .bitwiseAnd(lpmMask);
                     return new PiLpmFieldMatch(fieldMatch.fieldId(),
-                                               lpmfield.value().fit(modelBitWidth),
-                                               lpmfield.prefixLength());
+                                               lpmValue, lpmfield.prefixLength());
                 case RANGE:
                     return new PiRangeFieldMatch(fieldMatch.fieldId(),
                                                  ((PiRangeFieldMatch) fieldMatch).lowValue().fit(modelBitWidth),
diff --git a/core/net/src/test/java/org/onosproject/net/pi/impl/PiTranslatorServiceTest.java b/core/net/src/test/java/org/onosproject/net/pi/impl/PiTranslatorServiceTest.java
index 9af6745..e55ec25 100644
--- a/core/net/src/test/java/org/onosproject/net/pi/impl/PiTranslatorServiceTest.java
+++ b/core/net/src/test/java/org/onosproject/net/pi/impl/PiTranslatorServiceTest.java
@@ -182,7 +182,6 @@
                 .addEqualityGroup(entry1, entry2)
                 .testEquals();
 
-        int numMatchParams = pipeconf.pipelineModel().table(TBL_TABLE0_ID).get().matchFields().size();
         // parse values stored in entry1
         PiTernaryFieldMatch inPortParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(HDR_IN_PORT_ID).get();
         PiTernaryFieldMatch ethDstParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(HDR_ETH_DST_ID).get();
@@ -191,10 +190,6 @@
         Optional<Double> expectedTimeout = pipeconf.pipelineModel().table(TBL_TABLE0_ID).get().supportsAging()
                 ? Optional.of((double) rule1.timeout()) : Optional.empty();
 
-        // check that the number of parameters in the entry is the same as the number of table keys
-        assertThat("Incorrect number of match parameters",
-                   entry1.matchKey().fieldMatches().size(), is(equalTo(numMatchParams)));
-
         // check that values stored in entry are the same used for the flow rule
         assertThat("Incorrect inPort match param value",
                    inPortParam.value().asReadOnlyBuffer().getShort(), is(equalTo(inPort)));
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
index bcc298d..352eca8 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
@@ -56,7 +56,6 @@
 import java.util.stream.Collectors;
 
 import static com.google.common.collect.Lists.newArrayList;
-import static java.util.Collections.singleton;
 import static org.onosproject.drivers.p4runtime.P4RuntimeFlowRuleProgrammable.Operation.APPLY;
 import static org.onosproject.drivers.p4runtime.P4RuntimeFlowRuleProgrammable.Operation.REMOVE;
 import static org.onosproject.net.flow.FlowEntry.FlowEntryState.ADDED;
@@ -78,8 +77,8 @@
     private static final String DELETE_BEFORE_UPDATE = "tableDeleteBeforeUpdate";
     private static final boolean DEFAULT_DELETE_BEFORE_UPDATE = false;
 
-    // If true, we ignore re-installing rules that already exist the
-    // device, i.e. same match key and action.
+    // If true, we ignore re-installing rules that already exist in the
+    // device mirror, i.e. same match key and action.
     private static final String IGNORE_SAME_ENTRY_UPDATE = "tableIgnoreSameEntryUpdate";
     private static final boolean DEFAULT_IGNORE_SAME_ENTRY_UPDATE = false;
 
@@ -233,7 +232,7 @@
 
         if (cellData != null) {
             return new DefaultFlowEntry(translatedEntity.get().original(),
-                                        ADDED, timedEntry.lifeSec(), cellData.bytes(),
+                                        ADDED, timedEntry.lifeSec(), cellData.packets(),
                                         cellData.bytes());
         } else {
             return new DefaultFlowEntry(translatedEntity.get().original(),
@@ -397,11 +396,14 @@
         try {
             if (driverBoolProperty(READ_ALL_DIRECT_COUNTERS,
                                    DEFAULT_READ_ALL_DIRECT_COUNTERS)) {
-                cellDatas = client.readAllCounterCells(
-                        singleton(counterId), pipeconf).get();
+                // FIXME: re-implement reading all counters ONOS-7595, or
+                // (even better) read counters when dumping table entries ONOS-7596
+                // cellDatas = client.readAllCounterCells(
+                //        singleton(counterId), pipeconf).get();
+                cellDatas = Collections.emptyList();
             } else {
                 Set<PiCounterCellId> cellIds = tableEntries.stream()
-                        .map(entry -> PiCounterCellId.ofDirect(counterId, entry))
+                        .map(PiCounterCellId::ofDirect)
                         .collect(Collectors.toSet());
                 cellDatas = client.readCounterCells(cellIds, pipeconf).get();
             }
diff --git a/pipelines/basic/src/main/java/org/onosproject/pipelines/basic/BasicConstants.java b/pipelines/basic/src/main/java/org/onosproject/pipelines/basic/BasicConstants.java
index b6ee3c2..0ef19bf 100644
--- a/pipelines/basic/src/main/java/org/onosproject/pipelines/basic/BasicConstants.java
+++ b/pipelines/basic/src/main/java/org/onosproject/pipelines/basic/BasicConstants.java
@@ -31,48 +31,75 @@
 
     // TODO: constants could be auto-generated starting from the P4info.
 
-    private static final String TABLE0_CONTROL = "table0_control";
-    private static final String WCMP_CONTROL = "wcmp_control";
-
-    // Header field IDs
-    public static final String DOT =  ".";
+    public static final String DOT = ".";
+    private static final String INGRESS = "ingress";
+    private static final String EGRESS = "egress";
+    private static final String TABLE0_CTRL = INGRESS + DOT + "table0_control";
+    private static final String WCMP_CTRL = INGRESS + DOT + "wcmp_control";
+    private static final String PORT_COUNT_INGRESS_CTRL = INGRESS + DOT + "port_counters_ingress";
+    private static final String PORT_COUNT_EGRESS_CTRL = EGRESS + DOT + "port_counters_egress";
     public static final String HDR = "hdr";
     public static final String ETHERNET = "ethernet";
     public static final String IPV4 = "ipv4";
     public static final String LOCAL_METADATA = "local_metadata";
     public static final String STANDARD_METADATA = "standard_metadata";
-    public static final PiMatchFieldId HDR_IN_PORT_ID = PiMatchFieldId.of(STANDARD_METADATA + DOT + "ingress_port");
-    public static final PiMatchFieldId HDR_ETH_DST_ID = PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "dst_addr");
-    public static final PiMatchFieldId HDR_ETH_SRC_ID = PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "src_addr");
-    public static final PiMatchFieldId HDR_ETH_TYPE_ID = PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "ether_type");
-    public static final PiMatchFieldId HDR_IPV4_DST_ID = PiMatchFieldId.of(HDR + DOT + IPV4 + DOT + "dst_addr");
-    public static final PiMatchFieldId HDR_IPV4_SRC_ID = PiMatchFieldId.of(HDR + DOT + IPV4 + DOT + "src_addr");
-    public static final PiMatchFieldId HDR_NEXT_HOP_ID = PiMatchFieldId.of(LOCAL_METADATA + DOT + "next_hop_id");
-    public static final PiMatchFieldId HDR_SELECTOR_ID = PiMatchFieldId.of(LOCAL_METADATA + DOT + "selector");
+
+    // Header field IDs
+
+    public static final PiMatchFieldId HDR_IN_PORT_ID =
+            PiMatchFieldId.of(STANDARD_METADATA + DOT + "ingress_port");
+    public static final PiMatchFieldId HDR_ETH_DST_ID =
+            PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "dst_addr");
+    public static final PiMatchFieldId HDR_ETH_SRC_ID =
+            PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "src_addr");
+    public static final PiMatchFieldId HDR_ETH_TYPE_ID =
+            PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "ether_type");
+    public static final PiMatchFieldId HDR_IPV4_DST_ID =
+            PiMatchFieldId.of(HDR + DOT + IPV4 + DOT + "dst_addr");
+    public static final PiMatchFieldId HDR_IPV4_SRC_ID =
+            PiMatchFieldId.of(HDR + DOT + IPV4 + DOT + "src_addr");
+    public static final PiMatchFieldId HDR_NEXT_HOP_ID =
+            PiMatchFieldId.of(LOCAL_METADATA + DOT + "next_hop_id");
+    public static final PiMatchFieldId HDR_SELECTOR_ID =
+            PiMatchFieldId.of(LOCAL_METADATA + DOT + "selector");
     // Table IDs
-    public static final PiTableId TBL_TABLE0_ID = PiTableId.of(TABLE0_CONTROL + DOT  + "table0");
-    public static final PiTableId TBL_WCMP_TABLE_ID = PiTableId.of(WCMP_CONTROL + DOT  + "wcmp_table");
+    public static final PiTableId TBL_TABLE0_ID =
+            PiTableId.of(TABLE0_CTRL + DOT + "table0");
+    public static final PiTableId TBL_WCMP_TABLE_ID =
+            PiTableId.of(WCMP_CTRL + DOT + "wcmp_table");
     // Counter IDs
     public static final PiCounterId CNT_EGRESS_PORT_COUNTER_ID =
-            PiCounterId.of("port_counters_egress.egress_port_counter");
+            PiCounterId.of(PORT_COUNT_EGRESS_CTRL + DOT + "egress_port_counter");
     public static final PiCounterId CNT_INGRESS_PORT_COUNTER_ID =
-            PiCounterId.of("port_counters_ingress.ingress_port_counter");
-    public static final PiCounterId CNT_TABLE0_ID = PiCounterId.of(TABLE0_CONTROL + DOT  + "table0_counter");
-    public static final PiCounterId CNT_WCMP_TABLE_ID = PiCounterId.of(WCMP_CONTROL + DOT  + "wcmp_table_counter");
+            PiCounterId.of(PORT_COUNT_INGRESS_CTRL + DOT + "ingress_port_counter");
+    public static final PiCounterId CNT_TABLE0_ID =
+            PiCounterId.of(TABLE0_CTRL + DOT + "table0_counter");
+    public static final PiCounterId CNT_WCMP_TABLE_ID =
+            PiCounterId.of(WCMP_CTRL + DOT + "wcmp_table_counter");
     // Action IDs
-    public static final PiActionId ACT_NOACTION_ID = PiActionId.of("NoAction");
-    public static final PiActionId ACT_DROP_ID = PiActionId.of("_drop");
-    public static final PiActionId ACT_SET_EGRESS_PORT_ID = PiActionId.of("set_egress_port");
-    public static final PiActionId ACT_SET_NEXT_HOP_ID = PiActionId.of(TABLE0_CONTROL + DOT  + "set_next_hop_id");
-    public static final PiActionId ACT_SEND_TO_CPU_ID = PiActionId.of("send_to_cpu");
+    public static final PiActionId ACT_NOACTION_ID =
+            PiActionId.of("NoAction");
+    public static final PiActionId ACT_DROP_ID =
+            PiActionId.of("_drop");
+    public static final PiActionId ACT_SET_EGRESS_PORT_ID =
+            PiActionId.of("set_egress_port");
+    public static final PiActionId ACT_SET_NEXT_HOP_ID =
+            PiActionId.of(TABLE0_CTRL + DOT + "set_next_hop_id");
+    public static final PiActionId ACT_SEND_TO_CPU_ID =
+            PiActionId.of("send_to_cpu");
     // Action Param IDs
-    public static final PiActionParamId ACT_PRM_PORT_ID = PiActionParamId.of("port");
-    public static final PiActionParamId ACT_PRM_NEXT_HOP_ID = PiActionParamId.of("next_hop_id");
+    public static final PiActionParamId ACT_PRM_PORT_ID =
+            PiActionParamId.of("port");
+    public static final PiActionParamId ACT_PRM_NEXT_HOP_ID =
+            PiActionParamId.of("next_hop_id");
     // Action Profile IDs
-    public static final PiActionProfileId ACT_PRF_WCMP_SELECTOR_ID = PiActionProfileId.of("wcmp_control.wcmp_selector");
+    public static final PiActionProfileId ACT_PRF_WCMP_SELECTOR_ID =
+            PiActionProfileId.of(WCMP_CTRL + DOT + "wcmp_selector");
     // Packet Metadata IDs
-    public static final PiControlMetadataId PKT_META_EGRESS_PORT_ID = PiControlMetadataId.of("egress_port");
-    public static final PiControlMetadataId PKT_META_INGRESS_PORT_ID = PiControlMetadataId.of("ingress_port");
+    public static final PiControlMetadataId PKT_META_EGRESS_PORT_ID =
+            PiControlMetadataId.of("egress_port");
+    public static final PiControlMetadataId PKT_META_INGRESS_PORT_ID =
+            PiControlMetadataId.of("ingress_port");
     // Bitwidths
     public static final int PORT_BITWIDTH = 9;
 
diff --git a/pipelines/basic/src/main/resources/p4c-out/bmv2/basic.json b/pipelines/basic/src/main/resources/p4c-out/bmv2/basic.json
index b90e0d6..e9378ef 100644
--- a/pipelines/basic/src/main/resources/p4c-out/bmv2/basic.json
+++ b/pipelines/basic/src/main/resources/p4c-out/bmv2/basic.json
@@ -112,12 +112,14 @@
         ["deq_timedelta", 32, false],
         ["deq_qdepth", 19, false],
         ["ingress_global_timestamp", 48, false],
+        ["egress_global_timestamp", 48, false],
         ["lf_field_list", 32, false],
         ["mcast_grp", 16, false],
-        ["resubmit_flag", 1, false],
+        ["resubmit_flag", 32, false],
         ["egress_rid", 16, false],
         ["checksum_error", 1, false],
-        ["_padding_1", 4, false]
+        ["recirculate_flag", 32, false],
+        ["_padding_1", 5, false]
       ]
     }
   ],
@@ -214,6 +216,7 @@
           "parser_ops" : [],
           "transitions" : [
             {
+              "type" : "hexstr",
               "value" : "0x00ff",
               "mask" : null,
               "next_state" : "parse_packet_out"
@@ -270,6 +273,7 @@
           ],
           "transitions" : [
             {
+              "type" : "hexstr",
               "value" : "0x0800",
               "mask" : null,
               "next_state" : "parse_ipv4"
@@ -303,11 +307,13 @@
           ],
           "transitions" : [
             {
+              "type" : "hexstr",
               "value" : "0x06",
               "mask" : null,
               "next_state" : "parse_tcp"
             },
             {
+              "type" : "hexstr",
               "value" : "0x11",
               "mask" : null,
               "next_state" : "parse_udp"
@@ -426,6 +432,7 @@
       ]
     }
   ],
+  "parse_vsets" : [],
   "deparsers" : [
     {
       "name" : "deparser",
@@ -441,7 +448,7 @@
   ],
   "meter_arrays" : [
     {
-      "name" : "port_meters_ingress.ingress_port_meter",
+      "name" : "ingress.port_meters_ingress.ingress_port_meter",
       "id" : 0,
       "source_info" : {
         "filename" : "include/port_meters.p4",
@@ -455,7 +462,7 @@
       "type" : "bytes"
     },
     {
-      "name" : "host_meter_control.host_meter",
+      "name" : "ingress.host_meter_control.host_meter",
       "id" : 1,
       "source_info" : {
         "filename" : "include/host_meter_table.p4",
@@ -467,11 +474,11 @@
       "rate_count" : 2,
       "type" : "bytes",
       "size" : 1024,
-      "binding" : "host_meter_control.host_meter_table",
+      "binding" : "ingress.host_meter_control.host_meter_table",
       "result_target" : ["scalars", "local_metadata_t.meter_tag"]
     },
     {
-      "name" : "port_meters_egress.egress_port_meter",
+      "name" : "egress.port_meters_egress.egress_port_meter",
       "id" : 2,
       "source_info" : {
         "filename" : "include/port_meters.p4",
@@ -487,7 +494,7 @@
   ],
   "counter_arrays" : [
     {
-      "name" : "port_counters_ingress.ingress_port_counter",
+      "name" : "ingress.port_counters_ingress.ingress_port_counter",
       "id" : 0,
       "source_info" : {
         "filename" : "include/port_counters.p4",
@@ -499,19 +506,19 @@
       "is_direct" : false
     },
     {
-      "name" : "table0_control.table0_counter",
+      "name" : "ingress.table0_control.table0_counter",
       "id" : 1,
       "is_direct" : true,
-      "binding" : "table0_control.table0"
+      "binding" : "ingress.table0_control.table0"
     },
     {
-      "name" : "wcmp_control.wcmp_table_counter",
+      "name" : "ingress.wcmp_control.wcmp_table_counter",
       "id" : 2,
       "is_direct" : true,
-      "binding" : "wcmp_control.wcmp_table"
+      "binding" : "ingress.wcmp_control.wcmp_table"
     },
     {
-      "name" : "port_counters_egress.egress_port_counter",
+      "name" : "egress.port_counters_egress.egress_port_counter",
       "id" : 3,
       "source_info" : {
         "filename" : "include/port_counters.p4",
@@ -645,7 +652,7 @@
       "primitives" : []
     },
     {
-      "name" : "table0_control.set_next_hop_id",
+      "name" : "ingress.table0_control.set_next_hop_id",
       "id" : 6,
       "runtime_data" : [
         {
@@ -676,7 +683,7 @@
       ]
     },
     {
-      "name" : "host_meter_control.read_meter",
+      "name" : "ingress.host_meter_control.read_meter",
       "id" : 7,
       "runtime_data" : [],
       "primitives" : []
@@ -734,7 +741,7 @@
           "parameters" : [
             {
               "type" : "counter_array",
-              "value" : "port_counters_ingress.ingress_port_counter"
+              "value" : "ingress.port_counters_ingress.ingress_port_counter"
             },
             {
               "type" : "field",
@@ -779,7 +786,7 @@
           "parameters" : [
             {
               "type" : "meter_array",
-              "value" : "port_meters_ingress.ingress_port_meter"
+              "value" : "ingress.port_meters_ingress.ingress_port_meter"
             },
             {
               "type" : "field",
@@ -1052,7 +1059,7 @@
           "parameters" : [
             {
               "type" : "counter_array",
-              "value" : "port_counters_egress.egress_port_counter"
+              "value" : "egress.port_counters_egress.egress_port_counter"
             },
             {
               "type" : "field",
@@ -1097,7 +1104,7 @@
           "parameters" : [
             {
               "type" : "meter_array",
-              "value" : "port_meters_egress.egress_port_meter"
+              "value" : "egress.port_meters_egress.egress_port_meter"
             },
             {
               "type" : "field",
@@ -1241,7 +1248,7 @@
           }
         },
         {
-          "name" : "table0_control.table0",
+          "name" : "ingress.table0_control.table0",
           "id" : 3,
           "source_info" : {
             "filename" : "include/table0.p4",
@@ -1252,46 +1259,55 @@
           "key" : [
             {
               "match_type" : "ternary",
+              "name" : "standard_metadata.ingress_port",
               "target" : ["standard_metadata", "ingress_port"],
               "mask" : null
             },
             {
               "match_type" : "ternary",
+              "name" : "hdr.ethernet.src_addr",
               "target" : ["ethernet", "src_addr"],
               "mask" : null
             },
             {
               "match_type" : "ternary",
+              "name" : "hdr.ethernet.dst_addr",
               "target" : ["ethernet", "dst_addr"],
               "mask" : null
             },
             {
               "match_type" : "ternary",
+              "name" : "hdr.ethernet.ether_type",
               "target" : ["ethernet", "ether_type"],
               "mask" : null
             },
             {
               "match_type" : "ternary",
+              "name" : "hdr.ipv4.src_addr",
               "target" : ["ipv4", "src_addr"],
               "mask" : null
             },
             {
               "match_type" : "ternary",
+              "name" : "hdr.ipv4.dst_addr",
               "target" : ["ipv4", "dst_addr"],
               "mask" : null
             },
             {
               "match_type" : "ternary",
+              "name" : "hdr.ipv4.protocol",
               "target" : ["ipv4", "protocol"],
               "mask" : null
             },
             {
               "match_type" : "ternary",
+              "name" : "local_metadata.l4_src_port",
               "target" : ["scalars", "local_metadata_t.l4_src_port"],
               "mask" : null
             },
             {
               "match_type" : "ternary",
+              "name" : "local_metadata.l4_dst_port",
               "target" : ["scalars", "local_metadata_t.l4_dst_port"],
               "mask" : null
             }
@@ -1303,13 +1319,13 @@
           "support_timeout" : false,
           "direct_meters" : null,
           "action_ids" : [0, 2, 6, 3],
-          "actions" : ["set_egress_port", "send_to_cpu", "table0_control.set_next_hop_id", "_drop"],
-          "base_default_next" : "host_meter_control.host_meter_table",
+          "actions" : ["set_egress_port", "send_to_cpu", "ingress.table0_control.set_next_hop_id", "_drop"],
+          "base_default_next" : "ingress.host_meter_control.host_meter_table",
           "next_tables" : {
-            "set_egress_port" : "host_meter_control.host_meter_table",
-            "send_to_cpu" : "host_meter_control.host_meter_table",
-            "table0_control.set_next_hop_id" : "host_meter_control.host_meter_table",
-            "_drop" : "host_meter_control.host_meter_table"
+            "set_egress_port" : "ingress.host_meter_control.host_meter_table",
+            "send_to_cpu" : "ingress.host_meter_control.host_meter_table",
+            "ingress.table0_control.set_next_hop_id" : "ingress.host_meter_control.host_meter_table",
+            "_drop" : "ingress.host_meter_control.host_meter_table"
           },
           "default_entry" : {
             "action_id" : 3,
@@ -1319,7 +1335,7 @@
           }
         },
         {
-          "name" : "host_meter_control.host_meter_table",
+          "name" : "ingress.host_meter_control.host_meter_table",
           "id" : 4,
           "source_info" : {
             "filename" : "include/host_meter_table.p4",
@@ -1330,6 +1346,7 @@
           "key" : [
             {
               "match_type" : "lpm",
+              "name" : "hdr.ethernet.src_addr",
               "target" : ["ethernet", "src_addr"],
               "mask" : null
             }
@@ -1339,9 +1356,9 @@
           "max_size" : 1024,
           "with_counters" : false,
           "support_timeout" : false,
-          "direct_meters" : "host_meter_control.host_meter",
+          "direct_meters" : "ingress.host_meter_control.host_meter",
           "action_ids" : [7, 4],
-          "actions" : ["host_meter_control.read_meter", "NoAction"],
+          "actions" : ["ingress.host_meter_control.read_meter", "NoAction"],
           "base_default_next" : null,
           "next_tables" : {
             "__HIT__" : "tbl_act_2",
@@ -1470,7 +1487,7 @@
           }
         },
         {
-          "name" : "wcmp_control.wcmp_table",
+          "name" : "ingress.wcmp_control.wcmp_table",
           "id" : 10,
           "source_info" : {
             "filename" : "include/wcmp.p4",
@@ -1481,13 +1498,14 @@
           "key" : [
             {
               "match_type" : "exact",
+              "name" : "local_metadata.next_hop_id",
               "target" : ["scalars", "local_metadata_t.next_hop_id"],
               "mask" : null
             }
           ],
           "match_type" : "exact",
           "type" : "indirect_ws",
-          "action_profile" : "wcmp_control.wcmp_selector",
+          "action_profile" : "ingress.wcmp_control.wcmp_selector",
           "max_size" : 1024,
           "with_counters" : true,
           "support_timeout" : false,
@@ -1503,7 +1521,7 @@
       ],
       "action_profiles" : [
         {
-          "name" : "wcmp_control.wcmp_selector",
+          "name" : "ingress.wcmp_control.wcmp_selector",
           "id" : 0,
           "max_size" : 64,
           "selector" : {
@@ -1584,7 +1602,7 @@
             }
           },
           "true_next" : "tbl_act_1",
-          "false_next" : "table0_control.table0"
+          "false_next" : "ingress.table0_control.table0"
         },
         {
           "name" : "node_11",
@@ -1651,7 +1669,7 @@
             }
           },
           "false_next" : null,
-          "true_next" : "wcmp_control.wcmp_table"
+          "true_next" : "ingress.wcmp_control.wcmp_table"
         }
       ]
     },
@@ -1818,6 +1836,10 @@
       ["standard_metadata", "ingress_global_timestamp"]
     ],
     [
+      "intrinsic_metadata.egress_global_timestamp",
+      ["standard_metadata", "egress_global_timestamp"]
+    ],
+    [
       "intrinsic_metadata.lf_field_list",
       ["standard_metadata", "lf_field_list"]
     ],
@@ -1832,6 +1854,10 @@
     [
       "intrinsic_metadata.egress_rid",
       ["standard_metadata", "egress_rid"]
+    ],
+    [
+      "intrinsic_metadata.recirculate_flag",
+      ["standard_metadata", "recirculate_flag"]
     ]
   ]
 }
\ No newline at end of file
diff --git a/pipelines/basic/src/main/resources/p4c-out/bmv2/basic.p4info b/pipelines/basic/src/main/resources/p4c-out/bmv2/basic.p4info
index 9ec66ba..8d39c13 100644
--- a/pipelines/basic/src/main/resources/p4c-out/bmv2/basic.p4info
+++ b/pipelines/basic/src/main/resources/p4c-out/bmv2/basic.p4info
@@ -1,7 +1,7 @@
 tables {
   preamble {
-    id: 33571508
-    name: "table0_control.table0"
+    id: 33561568
+    name: "ingress.table0_control.table0"
     alias: "table0"
   }
   match_fields {
@@ -65,19 +65,19 @@
     id: 16829080
   }
   action_refs {
-    id: 16802895
+    id: 16777316
   }
   action_refs {
     id: 16784184
   }
   const_default_action_id: 16784184
-  direct_resource_ids: 302046050
+  direct_resource_ids: 302038973
   size: 1024
 }
 tables {
   preamble {
-    id: 33597882
-    name: "host_meter_control.host_meter_table"
+    id: 33571781
+    name: "ingress.host_meter_control.host_meter_table"
     alias: "host_meter_table"
   }
   match_fields {
@@ -87,18 +87,18 @@
     match_type: LPM
   }
   action_refs {
-    id: 16832719
+    id: 16823832
   }
   action_refs {
     id: 16800567
   }
-  direct_resource_ids: 318776014
+  direct_resource_ids: 318783457
   size: 1024
 }
 tables {
   preamble {
-    id: 33592597
-    name: "wcmp_control.wcmp_table"
+    id: 33594717
+    name: "ingress.wcmp_control.wcmp_table"
     alias: "wcmp_table"
   }
   match_fields {
@@ -114,8 +114,8 @@
     id: 16800567
     annotations: "@defaultonly()"
   }
-  implementation_id: 285259294
-  direct_resource_ids: 302001091
+  implementation_id: 285253634
+  direct_resource_ids: 302034578
   size: 1024
 }
 actions {
@@ -153,8 +153,8 @@
 }
 actions {
   preamble {
-    id: 16802895
-    name: "table0_control.set_next_hop_id"
+    id: 16777316
+    name: "ingress.table0_control.set_next_hop_id"
     alias: "set_next_hop_id"
   }
   params {
@@ -165,25 +165,25 @@
 }
 actions {
   preamble {
-    id: 16832719
-    name: "host_meter_control.read_meter"
+    id: 16823832
+    name: "ingress.host_meter_control.read_meter"
     alias: "read_meter"
   }
 }
 action_profiles {
   preamble {
-    id: 285259294
-    name: "wcmp_control.wcmp_selector"
+    id: 285253634
+    name: "ingress.wcmp_control.wcmp_selector"
     alias: "wcmp_selector"
   }
-  table_ids: 33592597
+  table_ids: 33594717
   with_selector: true
   size: 64
 }
 counters {
   preamble {
-    id: 302012579
-    name: "port_counters_ingress.ingress_port_counter"
+    id: 302004684
+    name: "ingress.port_counters_ingress.ingress_port_counter"
     alias: "ingress_port_counter"
   }
   spec {
@@ -193,8 +193,8 @@
 }
 counters {
   preamble {
-    id: 302012501
-    name: "port_counters_egress.egress_port_counter"
+    id: 302040487
+    name: "egress.port_counters_egress.egress_port_counter"
     alias: "egress_port_counter"
   }
   spec {
@@ -204,30 +204,30 @@
 }
 direct_counters {
   preamble {
-    id: 302046050
-    name: "table0_control.table0_counter"
+    id: 302038973
+    name: "ingress.table0_control.table0_counter"
     alias: "table0_counter"
   }
   spec {
     unit: BOTH
   }
-  direct_table_id: 33571508
+  direct_table_id: 33561568
 }
 direct_counters {
   preamble {
-    id: 302001091
-    name: "wcmp_control.wcmp_table_counter"
+    id: 302034578
+    name: "ingress.wcmp_control.wcmp_table_counter"
     alias: "wcmp_table_counter"
   }
   spec {
     unit: BOTH
   }
-  direct_table_id: 33592597
+  direct_table_id: 33594717
 }
 meters {
   preamble {
-    id: 318770010
-    name: "port_meters_ingress.ingress_port_meter"
+    id: 318803935
+    name: "ingress.port_meters_ingress.ingress_port_meter"
     alias: "ingress_port_meter"
   }
   spec {
@@ -237,8 +237,8 @@
 }
 meters {
   preamble {
-    id: 318779497
-    name: "port_meters_egress.egress_port_meter"
+    id: 318792425
+    name: "egress.port_meters_egress.egress_port_meter"
     alias: "egress_port_meter"
   }
   spec {
@@ -248,14 +248,14 @@
 }
 direct_meters {
   preamble {
-    id: 318776014
-    name: "host_meter_control.host_meter"
+    id: 318783457
+    name: "ingress.host_meter_control.host_meter"
     alias: "host_meter"
   }
   spec {
     unit: BYTES
   }
-  direct_table_id: 33597882
+  direct_table_id: 33571781
 }
 controller_packet_metadata {
   preamble {
diff --git a/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeClient.java b/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeClient.java
index 91254cf..67f9fcf 100644
--- a/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeClient.java
+++ b/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeClient.java
@@ -17,18 +17,18 @@
 package org.onosproject.p4runtime.api;
 
 import com.google.common.annotations.Beta;
-import org.onosproject.net.pi.model.PiPipeconf;
-import org.onosproject.net.pi.runtime.PiActionGroup;
 import org.onosproject.net.pi.model.PiActionProfileId;
+import org.onosproject.net.pi.model.PiCounterId;
+import org.onosproject.net.pi.model.PiMeterId;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.model.PiTableId;
+import org.onosproject.net.pi.runtime.PiActionGroup;
 import org.onosproject.net.pi.runtime.PiCounterCellData;
 import org.onosproject.net.pi.runtime.PiCounterCellId;
-import org.onosproject.net.pi.model.PiCounterId;
 import org.onosproject.net.pi.runtime.PiMeterCellConfig;
 import org.onosproject.net.pi.runtime.PiMeterCellId;
-import org.onosproject.net.pi.model.PiMeterId;
 import org.onosproject.net.pi.runtime.PiPacketOperation;
 import org.onosproject.net.pi.runtime.PiTableEntry;
-import org.onosproject.net.pi.model.PiTableId;
 
 import java.nio.ByteBuffer;
 import java.util.Collection;
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/CounterEntryCodec.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/CounterEntryCodec.java
index df4e72f..17bff76 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/CounterEntryCodec.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/CounterEntryCodec.java
@@ -19,27 +19,31 @@
 import org.onosproject.net.pi.model.PiCounterId;
 import org.onosproject.net.pi.model.PiCounterType;
 import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.model.PiTableId;
 import org.onosproject.net.pi.runtime.PiCounterCellData;
 import org.onosproject.net.pi.runtime.PiCounterCellId;
 import org.onosproject.net.pi.runtime.PiTableEntry;
 import org.slf4j.Logger;
+import p4.P4RuntimeOuterClass;
 import p4.P4RuntimeOuterClass.CounterData;
 import p4.P4RuntimeOuterClass.CounterEntry;
 import p4.P4RuntimeOuterClass.DirectCounterEntry;
 import p4.P4RuntimeOuterClass.Entity;
 
 import java.util.Collection;
-import java.util.Map;
+import java.util.Collections;
 import java.util.Objects;
 import java.util.stream.Collectors;
 
 import static java.lang.String.format;
+import static org.onosproject.p4runtime.ctl.P4RuntimeUtils.indexMsg;
 import static org.slf4j.LoggerFactory.getLogger;
 import static p4.P4RuntimeOuterClass.Entity.EntityCase.COUNTER_ENTRY;
 import static p4.P4RuntimeOuterClass.Entity.EntityCase.DIRECT_COUNTER_ENTRY;
 
 /**
- * Encoder/decoder of PI counter IDs to counter entry protobuf messages, and vice versa.
+ * Encoder/decoder of PI counter IDs to counter entry protobuf messages, and
+ * vice versa.
  */
 final class CounterEntryCodec {
 
@@ -50,27 +54,32 @@
     }
 
     /**
-     * Returns a collection of P4Runtime entity protobuf messages describing both counter or direct counter entries,
-     * encoded from the given collection of PI counter cell identifiers, for the given pipeconf. If a PI counter cell
-     * identifier cannot be encoded, it is skipped, hence the returned collection might have different size than the
-     * input one.
-     * <p>
-     * This method takes as parameter also a map between numeric P4Info IDs and PI counter IDs, that will be populated
-     * during the process and that is then needed to aid in the decode process.
+     * Returns a collection of P4Runtime entity protobuf messages describing
+     * both counter or direct counter entries, encoded from the given collection
+     * of PI counter cell identifiers, for the given pipeconf. If a PI counter
+     * cell identifier cannot be encoded, it is skipped, hence the returned
+     * collection might have different size than the input one.
      *
-     * @param cellIds      counter cell identifiers
-     * @param counterIdMap counter ID map (empty, it will be populated during this method execution)
-     * @param pipeconf     pipeconf
-     * @return collection of entity messages describing both counter or direct counter entries
+     * @param cellIds  counter cell identifiers
+     * @param pipeconf pipeconf
+     * @return collection of entity messages describing both counter or direct
+     * counter entries
      */
     static Collection<Entity> encodePiCounterCellIds(Collection<PiCounterCellId> cellIds,
-                                                     Map<Integer, PiCounterId> counterIdMap,
                                                      PiPipeconf pipeconf) {
+
+        final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
+
+        if (browser == null) {
+            log.error("Unable to get a P4Info browser for pipeconf {}", pipeconf.id());
+            return Collections.emptyList();
+        }
+
         return cellIds
                 .stream()
                 .map(cellId -> {
                     try {
-                        return encodePiCounterCellId(cellId, counterIdMap, pipeconf);
+                        return encodePiCounterCellId(cellId, pipeconf, browser);
                     } catch (P4InfoBrowser.NotFoundException | EncodeException e) {
                         log.warn("Unable to encode PI counter cell id: {}", e.getMessage());
                         return null;
@@ -81,28 +90,33 @@
     }
 
     /**
-     * Returns a collection of PI counter cell data, decoded from the given P4Runtime entity protobuf messages
-     * describing both counter or direct counter entries, for the given counter ID map (populated by {@link
-     * #encodePiCounterCellIds(Collection, Map, PiPipeconf)}), and pipeconf. If an entity message cannot be encoded, it
-     * is skipped, hence the returned collection might have different size than the input one.
+     * Returns a collection of P4Runtime entity protobuf messages to be used in
+     * requests to read all cells from the given counter identifiers. Works for
+     * both indirect or direct counters. If a PI counter identifier cannot be
+     * encoded, it is skipped, hence the returned collection might have
+     * different size than the input one.
      *
-     * @param entities     P4Runtime entity messages
-     * @param counterIdMap counter ID map (previously populated)
-     * @param pipeconf     pipeconf
-     * @return collection of PI counter cell data
+     * @param counterIds counter identifiers
+     * @param pipeconf   pipeconf
+     * @return collection of entity messages
      */
-    static Collection<PiCounterCellData> decodeCounterEntities(Collection<Entity> entities,
-                                                               Map<Integer, PiCounterId> counterIdMap,
-                                                               PiPipeconf pipeconf) {
-        return entities
+    static Collection<Entity> readAllCellsEntities(Collection<PiCounterId> counterIds,
+                                                   PiPipeconf pipeconf) {
+        final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
+
+        if (browser == null) {
+            log.error("Unable to get a P4Info browser for pipeconf {}", pipeconf.id());
+            return Collections.emptyList();
+        }
+
+        return counterIds
                 .stream()
-                .filter(entity -> entity.getEntityCase() == COUNTER_ENTRY ||
-                        entity.getEntityCase() == DIRECT_COUNTER_ENTRY)
-                .map(entity -> {
+                .map(counterId -> {
                     try {
-                        return decodeCounterEntity(entity, counterIdMap, pipeconf);
-                    } catch (EncodeException | P4InfoBrowser.NotFoundException e) {
-                        log.warn("Unable to decode counter entity message: {}", e.getMessage());
+                        return readAllCellsEntity(counterId, pipeconf, browser);
+                    } catch (P4InfoBrowser.NotFoundException | EncodeException e) {
+                        log.warn("Unable to encode counter ID to read-all-cells entity: {}",
+                                 e.getMessage());
                         return null;
                     }
                 })
@@ -110,95 +124,160 @@
                 .collect(Collectors.toList());
     }
 
-    private static Entity encodePiCounterCellId(PiCounterCellId cellId, Map<Integer, PiCounterId> counterIdMap,
-                                                PiPipeconf pipeconf)
-            throws P4InfoBrowser.NotFoundException, EncodeException {
+    /**
+     * Returns a collection of PI counter cell data, decoded from the given
+     * P4Runtime entity protobuf messages describing both counter or direct
+     * counter entries, and pipeconf. If an entity message cannot be encoded, it
+     * is skipped, hence the returned collection might have different size than
+     * the input one.
+     *
+     * @param entities P4Runtime entity messages
+     * @param pipeconf pipeconf
+     * @return collection of PI counter cell data
+     */
+    static Collection<PiCounterCellData> decodeCounterEntities(Collection<Entity> entities,
+                                                               PiPipeconf pipeconf) {
 
         final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
 
+        if (browser == null) {
+            log.error("Unable to get a P4Info browser for pipeconf {}", pipeconf.id());
+            return Collections.emptyList();
+        }
+
+        return entities
+                .stream()
+                .filter(entity -> entity.getEntityCase() == COUNTER_ENTRY ||
+                        entity.getEntityCase() == DIRECT_COUNTER_ENTRY)
+                .map(entity -> {
+                    try {
+                        return decodeCounterEntity(entity, pipeconf, browser);
+                    } catch (EncodeException | P4InfoBrowser.NotFoundException e) {
+                        log.warn("Unable to decode counter entity message: {}",
+                                 e.getMessage());
+                        return null;
+                    }
+                })
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+    }
+
+    private static Entity encodePiCounterCellId(PiCounterCellId cellId,
+                                                PiPipeconf pipeconf,
+                                                P4InfoBrowser browser)
+            throws P4InfoBrowser.NotFoundException, EncodeException {
+
         int counterId;
         Entity entity;
         // Encode PI cell ID into entity message and add to read request.
         switch (cellId.counterType()) {
             case INDIRECT:
-                counterId = browser.counters().getByName(cellId.counterId().id()).getPreamble().getId();
-                entity = Entity.newBuilder().setCounterEntry(CounterEntry.newBuilder()
-                                                                     .setCounterId(counterId)
-                                                                     .setIndex(cellId.index())
-                                                                     .build())
+                counterId = browser.counters()
+                        .getByName(cellId.counterId().id())
+                        .getPreamble()
+                        .getId();
+                entity = Entity.newBuilder()
+                        .setCounterEntry(
+                                CounterEntry.newBuilder()
+                                        .setCounterId(counterId)
+                                        .setIndex(indexMsg(cellId.index()))
+                                        .build())
                         .build();
                 break;
             case DIRECT:
-                counterId = browser.directCounters().getByName(cellId.counterId().id()).getPreamble().getId();
-                DirectCounterEntry.Builder entryBuilder = DirectCounterEntry.newBuilder().setCounterId(counterId);
-                if (!cellId.tableEntry().equals(PiTableEntry.EMTPY)) {
-                    entryBuilder.setTableEntry(TableEntryEncoder.encode(cellId.tableEntry(), pipeconf));
-                }
-                entity = Entity.newBuilder().setDirectCounterEntry(entryBuilder.build()).build();
+                DirectCounterEntry.Builder entryBuilder = DirectCounterEntry.newBuilder();
+                entryBuilder.setTableEntry(
+                        TableEntryEncoder.encode(cellId.tableEntry(), pipeconf));
+                entity = Entity.newBuilder()
+                        .setDirectCounterEntry(entryBuilder.build())
+                        .build();
                 break;
             default:
-                throw new EncodeException(format("Unrecognized PI counter cell ID type '%s'", cellId.counterType()));
+                throw new EncodeException(format(
+                        "Unrecognized PI counter cell ID type '%s'",
+                        cellId.counterType()));
         }
-        counterIdMap.put(counterId, cellId.counterId());
 
         return entity;
     }
 
-    private static PiCounterCellData decodeCounterEntity(Entity entity, Map<Integer, PiCounterId> counterIdMap,
-                                                         PiPipeconf pipeconf)
+    private static Entity readAllCellsEntity(PiCounterId counterId,
+                                             PiPipeconf pipeconf,
+                                             P4InfoBrowser browser)
+            throws P4InfoBrowser.NotFoundException, EncodeException {
+
+        if (!pipeconf.pipelineModel().counter(counterId).isPresent()) {
+            throw new EncodeException(format(
+                    "not such counter '%s' in pipeline model", counterId));
+        }
+        final PiCounterType counterType = pipeconf.pipelineModel()
+                .counter(counterId).get().counterType();
+
+        switch (counterType) {
+            case INDIRECT:
+                final int p4InfoCounterId = browser.counters()
+                        .getByName(counterId.id())
+                        .getPreamble().getId();
+                return Entity.newBuilder().setCounterEntry(
+                        P4RuntimeOuterClass.CounterEntry.newBuilder()
+                                // Index unset to read all cells
+                                .setCounterId(p4InfoCounterId)
+                                .build())
+                        .build();
+            case DIRECT:
+                final PiTableId tableId = pipeconf.pipelineModel()
+                        .counter(counterId).get().table();
+                if (tableId == null) {
+                    throw new EncodeException(format(
+                            "null table for direct counter '%s'", counterId));
+                }
+                final int p4TableId = browser.tables().getByName(tableId.id())
+                        .getPreamble().getId();
+                return Entity.newBuilder().setDirectCounterEntry(
+                        P4RuntimeOuterClass.DirectCounterEntry.newBuilder()
+                                .setTableEntry(
+                                        // Match unset to read all cells
+                                        P4RuntimeOuterClass.TableEntry.newBuilder()
+                                                .setTableId(p4TableId)
+                                                .build())
+                                .build())
+                        .build();
+            default:
+                throw new EncodeException(format(
+                        "unrecognized PI counter type '%s'", counterType));
+        }
+    }
+
+    private static PiCounterCellData decodeCounterEntity(Entity entity,
+                                                         PiPipeconf pipeconf,
+                                                         P4InfoBrowser browser)
             throws EncodeException, P4InfoBrowser.NotFoundException {
 
-        int counterId;
         CounterData counterData;
-
-        if (entity.getEntityCase() == COUNTER_ENTRY) {
-            counterId = entity.getCounterEntry().getCounterId();
-            counterData = entity.getCounterEntry().getData();
-        } else {
-            counterId = entity.getDirectCounterEntry().getCounterId();
-            counterData = entity.getDirectCounterEntry().getData();
-        }
-
-        // Process only counter IDs that were requested in the first place.
-        if (!counterIdMap.containsKey(counterId)) {
-            throw new EncodeException(format("Unrecognized counter ID '%s'", counterId));
-        }
-
-        PiCounterId piCounterId = counterIdMap.get(counterId);
-
-        if (!pipeconf.pipelineModel().counter(piCounterId).isPresent()) {
-            throw new EncodeException(format(
-                    "Unable to find counter '%s' in pipeline model", counterId));
-        }
-        PiCounterType piCounterType = pipeconf.pipelineModel().counter(piCounterId).get().counterType();
-
-        // Compute PI cell ID.
         PiCounterCellId piCellId;
 
-        switch (piCounterType) {
-            case INDIRECT:
-                if (entity.getEntityCase() != COUNTER_ENTRY) {
-                    throw new EncodeException(format(
-                            "Counter ID '%s' is indirect, but processed entity is %s",
-                            piCounterId, entity.getEntityCase()));
-                }
-                piCellId = PiCounterCellId.ofIndirect(piCounterId,
-                                                      entity.getCounterEntry().getIndex());
-                break;
-            case DIRECT:
-                if (entity.getEntityCase() != DIRECT_COUNTER_ENTRY) {
-                    throw new EncodeException(format(
-                            "Counter ID '%s' is direct, but processed entity is %s",
-                            piCounterId, entity.getEntityCase()));
-                }
-                PiTableEntry piTableEntry = TableEntryEncoder.decode(entity.getDirectCounterEntry().getTableEntry(),
-                                                                     pipeconf);
-                piCellId = PiCounterCellId.ofDirect(piCounterId, piTableEntry);
-                break;
-            default:
-                throw new EncodeException(format("Unrecognized PI counter ID type '%s'", piCounterType));
+        if (entity.getEntityCase() == COUNTER_ENTRY) {
+            String counterName = browser.counters()
+                    .getById(entity.getCounterEntry().getCounterId())
+                    .getPreamble()
+                    .getName();
+            piCellId = PiCounterCellId.ofIndirect(
+                    PiCounterId.of(counterName),
+                    entity.getCounterEntry().getIndex().getIndex());
+            counterData = entity.getCounterEntry().getData();
+        } else if (entity.getEntityCase() == DIRECT_COUNTER_ENTRY) {
+            PiTableEntry piTableEntry = TableEntryEncoder.decode(
+                    entity.getDirectCounterEntry().getTableEntry(), pipeconf);
+            piCellId = PiCounterCellId.ofDirect(piTableEntry);
+            counterData = entity.getDirectCounterEntry().getData();
+        } else {
+            throw new EncodeException(format(
+                    "Unrecognized entity type '%s' in P4Runtime message",
+                    entity.getEntityCase().name()));
         }
 
-        return new PiCounterCellData(piCellId, counterData.getPacketCount(), counterData.getByteCount());
+        return new PiCounterCellData(piCellId,
+                                     counterData.getPacketCount(),
+                                     counterData.getByteCount());
     }
 }
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/MeterEntryCodec.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/MeterEntryCodec.java
index 9019613..fc60dcd 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/MeterEntryCodec.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/MeterEntryCodec.java
@@ -16,30 +16,35 @@
 
 package org.onosproject.p4runtime.ctl;
 
+import org.onosproject.net.pi.model.PiMeterId;
 import org.onosproject.net.pi.model.PiMeterType;
 import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.model.PiTableId;
 import org.onosproject.net.pi.runtime.PiMeterBand;
 import org.onosproject.net.pi.runtime.PiMeterCellConfig;
 import org.onosproject.net.pi.runtime.PiMeterCellId;
-import org.onosproject.net.pi.model.PiMeterId;
 import org.onosproject.net.pi.runtime.PiTableEntry;
 import org.slf4j.Logger;
-import p4.P4RuntimeOuterClass.MeterConfig;
-import p4.P4RuntimeOuterClass.MeterEntry;
+import p4.P4RuntimeOuterClass;
 import p4.P4RuntimeOuterClass.DirectMeterEntry;
 import p4.P4RuntimeOuterClass.Entity;
+import p4.P4RuntimeOuterClass.MeterConfig;
+import p4.P4RuntimeOuterClass.MeterEntry;
 
 import java.util.Collection;
-import java.util.Map;
+import java.util.Collections;
 import java.util.Objects;
 import java.util.stream.Collectors;
 
 import static java.lang.String.format;
+import static org.onosproject.p4runtime.ctl.P4RuntimeUtils.indexMsg;
 import static org.slf4j.LoggerFactory.getLogger;
-import static p4.P4RuntimeOuterClass.Entity.EntityCase.*;
+import static p4.P4RuntimeOuterClass.Entity.EntityCase.DIRECT_METER_ENTRY;
+import static p4.P4RuntimeOuterClass.Entity.EntityCase.METER_ENTRY;
 
 /**
- * Encoder/decoder of PI meter cell configurations to meter entry protobuf messages, and vice versa.
+ * Encoder/decoder of PI meter cell configurations to meter entry protobuf
+ * messages, and vice versa.
  */
 final class MeterEntryCodec {
 
@@ -50,27 +55,31 @@
     }
 
     /**
-     * Returns a collection of P4Runtime entity protobuf messages describing both meter or direct meter entries,
-     * encoded from the given collection of PI meter cell configurations, for the given pipeconf. If a PI meter cell
-     * configurations cannot be encoded, it is skipped, hence the returned collection might have different size than the
-     * input one.
-     * <p>
-     * This method takes as parameter also a map between numeric P4Info IDs and PI meter IDs, that will be populated
-     * during the process and that is then needed to aid in the decode process.
+     * Returns a collection of P4Runtime entity protobuf messages describing
+     * both meter or direct meter entries, encoded from the given collection of
+     * PI meter cell configurations, for the given pipeconf. If a PI meter cell
+     * configurations cannot be encoded, it is skipped, hence the returned
+     * collection might have different size than the input one.
      *
-     * @param cellConfigs  meter cell configurations
-     * @param meterIdMap   meter ID map (empty, it will be populated during this method execution)
-     * @param pipeconf     pipeconf
-     * @return collection of entity messages describing both meter or direct meter entries
+     * @param cellConfigs meter cell configurations
+     * @param pipeconf    pipeconf
+     * @return collection of entity messages describing both meter or direct
+     * meter entries
      */
     static Collection<Entity> encodePiMeterCellConfigs(Collection<PiMeterCellConfig> cellConfigs,
-                                                       Map<Integer, PiMeterId> meterIdMap,
                                                        PiPipeconf pipeconf) {
+        final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
+
+        if (browser == null) {
+            log.error("Unable to get a P4Info browser for pipeconf {}", pipeconf.id());
+            return Collections.emptyList();
+        }
+
         return cellConfigs
                 .stream()
                 .map(cellConfig -> {
                     try {
-                        return encodePiMeterCellConfig(cellConfig, meterIdMap, pipeconf);
+                        return encodePiMeterCellConfig(cellConfig, pipeconf, browser);
                     } catch (P4InfoBrowser.NotFoundException | EncodeException e) {
                         log.warn("Unable to encode PI meter cell id: {}", e.getMessage());
                         log.debug("exception", e);
@@ -82,26 +91,67 @@
     }
 
     /**
-     * Returns a collection of PI meter cell configurations, decoded from the given P4Runtime entity protobuf messages
-     * describing both meter or direct meter entries, for the given meter ID map (populated by {@link
-     * #encodePiMeterCellConfigs(Collection, Map, PiPipeconf)}), and pipeconf. If an entity message cannot be encoded,
-     * it is skipped, hence the returned collection might have different size than the input one.
+     * Returns a collection of P4Runtime entity protobuf messages to be used in
+     * requests to read all cells from the given meter identifiers. Works for
+     * both indirect or direct meters. If a PI meter identifier cannot be
+     * encoded, it is skipped, hence the returned collection might have
+     * different size than the input one.
      *
-     * @param entities     P4Runtime entity messages
-     * @param meterIdMap   meter ID map (previously populated)
-     * @param pipeconf     pipeconf
+     * @param meterIds meter identifiers
+     * @param pipeconf pipeconf
+     * @return collection of entity messages
+     */
+    static Collection<Entity> readAllCellsEntities(Collection<PiMeterId> meterIds,
+                                                   PiPipeconf pipeconf) {
+        final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
+
+        if (browser == null) {
+            log.error("Unable to get a P4Info browser for pipeconf {}", pipeconf.id());
+            return Collections.emptyList();
+        }
+
+        return meterIds
+                .stream()
+                .map(meterId -> {
+                    try {
+                        return readAllCellsEntity(meterId, pipeconf, browser);
+                    } catch (P4InfoBrowser.NotFoundException | EncodeException e) {
+                        log.warn("Unable to encode meter ID to read-all-cells entity: {}",
+                                 e.getMessage());
+                        return null;
+                    }
+                })
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Returns a collection of PI meter cell configurations, decoded from the
+     * given P4Runtime entity protobuf messages describing both meter or direct
+     * meter entries, and pipeconf. If an entity message cannot be encoded, it
+     * is skipped, hence the returned collection might have different size than
+     * the input one.
+     *
+     * @param entities P4Runtime entity messages
+     * @param pipeconf pipeconf
      * @return collection of PI meter cell data
      */
     static Collection<PiMeterCellConfig> decodeMeterEntities(Collection<Entity> entities,
-                                                               Map<Integer, PiMeterId> meterIdMap,
-                                                               PiPipeconf pipeconf) {
+                                                             PiPipeconf pipeconf) {
+        final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
+
+        if (browser == null) {
+            log.error("Unable to get a P4Info browser for pipeconf {}", pipeconf.id());
+            return Collections.emptyList();
+        }
+
         return entities
                 .stream()
                 .filter(entity -> entity.getEntityCase() == METER_ENTRY ||
                         entity.getEntityCase() == DIRECT_METER_ENTRY)
                 .map(entity -> {
                     try {
-                        return decodeMeterEntity(entity, meterIdMap, pipeconf);
+                        return decodeMeterEntity(entity, pipeconf, browser);
                     } catch (EncodeException | P4InfoBrowser.NotFoundException e) {
                         log.warn("Unable to decode meter entity message: {}", e.getMessage());
                         return null;
@@ -111,23 +161,23 @@
                 .collect(Collectors.toList());
     }
 
-    private static Entity encodePiMeterCellConfig(PiMeterCellConfig config, Map<Integer, PiMeterId> meterIdMap,
-                                                  PiPipeconf pipeconf)
+    private static Entity encodePiMeterCellConfig(PiMeterCellConfig config,
+                                                  PiPipeconf pipeconf,
+                                                  P4InfoBrowser browser)
             throws P4InfoBrowser.NotFoundException, EncodeException {
 
-        final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
-
         int meterId;
         Entity entity;
-        //The band with bigger burst is peak if rate of them is equal,
-        //if bands are not specificed, using default value(0).
-        long cir = 0;
-        long cburst = 0;
-        long pir = 0;
-        long pburst = 0;
-        PiMeterBand[] bands = config.meterBands().toArray(new PiMeterBand[config.meterBands().size()]);
+        MeterConfig meterConfig;
+
+        PiMeterBand[] bands = config.meterBands()
+                .toArray(new PiMeterBand[config.meterBands().size()]);
         if (bands.length == 2) {
-            if (bands[0].rate() > bands[1].rate()) {
+            long cir, cburst, pir, pburst;
+            // The band with bigger burst is peak if rate of them is equal.
+            if (bands[0].rate() > bands[1].rate() ||
+                    (bands[0].rate() == bands[1].rate() &&
+                            bands[0].burst() >= bands[1].burst())) {
                 cir = bands[1].rate();
                 cburst = bands[1].burst();
                 pir = bands[0].rate();
@@ -138,105 +188,133 @@
                 pir = bands[1].rate();
                 pburst = bands[1].burst();
             }
+            meterConfig = MeterConfig.newBuilder()
+                    .setCir(cir)
+                    .setCburst(cburst)
+                    .setPir(pir)
+                    .setPburst(pburst)
+                    .build();
+        } else if (bands.length == 0) {
+            // When reading meter cells.
+            meterConfig = null;
+        } else {
+            throw new EncodeException("number of meter bands should be either 2 or 0");
         }
 
-        // Encode PI cell ID into entity message and add to read request.
         switch (config.cellId().meterType()) {
             case INDIRECT:
-                meterId = browser.meters().getByName(config.cellId().meterId().id()).getPreamble().getId();
-                entity = Entity.newBuilder().setMeterEntry(MeterEntry
-                                                                   .newBuilder().setMeterId(meterId)
-                                                                   .setIndex(config.cellId().index())
-                                                                   .setConfig(MeterConfig.newBuilder()
-                                                                                      .setCir(cir)
-                                                                                      .setCburst(cburst)
-                                                                                      .setPir(pir)
-                                                                                      .setPburst(pburst)
-                                                                                      .build())
-                                                                   .build())
-                        .build();
+                meterId = browser.meters()
+                        .getByName(config.cellId().meterId().id())
+                        .getPreamble().getId();
+                MeterEntry.Builder indEntryBuilder = MeterEntry.newBuilder()
+                        .setMeterId(meterId)
+                        .setIndex(indexMsg(config.cellId().index()));
+                if (meterConfig != null) {
+                    indEntryBuilder.setConfig(meterConfig);
+                }
+                entity = Entity.newBuilder()
+                        .setMeterEntry(indEntryBuilder.build()).build();
                 break;
             case DIRECT:
-                meterId = browser.directMeters().getByName(config.cellId().meterId().id()).getPreamble().getId();
-                DirectMeterEntry.Builder entryBuilder = DirectMeterEntry.newBuilder()
-                        .setMeterId(meterId)
-                        .setConfig(MeterConfig.newBuilder()
-                                           .setCir(cir)
-                                           .setCburst(cburst)
-                                           .setPir(pir)
-                                           .setPburst(pburst)
-                                           .build());
-
-                if (!config.cellId().tableEntry().equals(PiTableEntry.EMTPY)) {
-                    entryBuilder.setTableEntry(TableEntryEncoder.encode(config.cellId().tableEntry(), pipeconf));
+                DirectMeterEntry.Builder dirEntryBuilder = DirectMeterEntry.newBuilder()
+                        .setTableEntry(TableEntryEncoder.encode(
+                                config.cellId().tableEntry(), pipeconf));
+                if (meterConfig != null) {
+                    dirEntryBuilder.setConfig(meterConfig);
                 }
-                entity = Entity.newBuilder().setDirectMeterEntry(entryBuilder.build()).build();
+                entity = Entity.newBuilder()
+                        .setDirectMeterEntry(dirEntryBuilder.build()).build();
                 break;
             default:
-                throw new EncodeException(format("Unrecognized PI meter cell ID type '%s'",
+                throw new EncodeException(format("unrecognized PI meter type '%s'",
                                                  config.cellId().meterType()));
         }
-        meterIdMap.put(meterId, config.cellId().meterId());
 
         return entity;
     }
 
-    private static PiMeterCellConfig decodeMeterEntity(Entity entity, Map<Integer, PiMeterId> meterIdMap,
-                                                         PiPipeconf pipeconf)
+    private static Entity readAllCellsEntity(PiMeterId meterId,
+                                             PiPipeconf pipeconf,
+                                             P4InfoBrowser browser)
+            throws P4InfoBrowser.NotFoundException, EncodeException {
+
+        if (!pipeconf.pipelineModel().meter(meterId).isPresent()) {
+            throw new EncodeException(format(
+                    "not such meter '%s' in pipeline model", meterId));
+        }
+        final PiMeterType meterType = pipeconf.pipelineModel()
+                .meter(meterId).get().meterType();
+
+        switch (meterType) {
+            case INDIRECT:
+                final int p4InfoMeterId = browser.meters()
+                        .getByName(meterId.id())
+                        .getPreamble().getId();
+                return Entity.newBuilder().setMeterEntry(
+                        P4RuntimeOuterClass.MeterEntry.newBuilder()
+                                // Index unset to read all cells
+                                .setMeterId(p4InfoMeterId)
+                                .build())
+                        .build();
+            case DIRECT:
+                final PiTableId tableId = pipeconf.pipelineModel()
+                        .meter(meterId).get().table();
+                if (tableId == null) {
+                    throw new EncodeException(format(
+                            "null table for direct meter '%s'", meterId));
+                }
+                final int p4TableId = browser.tables().getByName(tableId.id())
+                        .getPreamble().getId();
+                return Entity.newBuilder().setDirectMeterEntry(
+                        P4RuntimeOuterClass.DirectMeterEntry.newBuilder()
+                                .setTableEntry(
+                                        // Match unset to read all cells
+                                        P4RuntimeOuterClass.TableEntry.newBuilder()
+                                                .setTableId(p4TableId)
+                                                .build())
+                                .build())
+                        .build();
+            default:
+                throw new EncodeException(format(
+                        "unrecognized PI meter type '%s'", meterType));
+        }
+    }
+
+    private static PiMeterCellConfig decodeMeterEntity(Entity entity,
+                                                       PiPipeconf pipeconf,
+                                                       P4InfoBrowser browser)
             throws EncodeException, P4InfoBrowser.NotFoundException {
 
-        int meterId;
         MeterConfig meterConfig;
-
-        if (entity.getEntityCase() == METER_ENTRY) {
-            meterId = entity.getMeterEntry().getMeterId();
-            meterConfig = entity.getMeterEntry().getConfig();
-        } else {
-            meterId = entity.getDirectMeterEntry().getMeterId();
-            meterConfig = entity.getDirectMeterEntry().getConfig();
-        }
-
-        // Process only meter IDs that were requested in the first place.
-        if (!meterIdMap.containsKey(meterId)) {
-            throw new EncodeException(format("Unrecognized meter ID '%s'", meterId));
-        }
-
-        PiMeterId piMeterId = meterIdMap.get(meterId);
-        if (!pipeconf.pipelineModel().meter(piMeterId).isPresent()) {
-            throw new EncodeException(format("Unable to find meter '%s' in pipeline model",  meterId));
-        }
-
-        PiMeterType piMeterType = pipeconf.pipelineModel().meter(piMeterId).get().meterType();
-        // Compute PI cell ID.
         PiMeterCellId piCellId;
 
-        switch (piMeterType) {
-            case INDIRECT:
-                if (entity.getEntityCase() != METER_ENTRY) {
-                    throw new EncodeException(format(
-                            "Meter ID '%s' is indirect, but processed entity is %s",
-                            piMeterId, entity.getEntityCase()));
-                }
-                piCellId = PiMeterCellId.ofIndirect(piMeterId, entity.getMeterEntry().getIndex());
-                break;
-            case DIRECT:
-                if (entity.getEntityCase() != DIRECT_METER_ENTRY) {
-                    throw new EncodeException(format(
-                            "Meter ID '%s' is direct, but processed entity is %s",
-                            piMeterId, entity.getEntityCase()));
-                }
-                PiTableEntry piTableEntry = TableEntryEncoder.decode(entity.getDirectMeterEntry().getTableEntry(),
-                                                                     pipeconf);
-                piCellId = PiMeterCellId.ofDirect(piMeterId, piTableEntry);
-                break;
-            default:
-                throw new EncodeException(format("Unrecognized PI meter ID type '%s'", piMeterType));
+        if (entity.getEntityCase() == METER_ENTRY) {
+            String meterName = browser.meters()
+                    .getById(entity.getCounterEntry().getCounterId())
+                    .getPreamble()
+                    .getName();
+            piCellId = PiMeterCellId.ofIndirect(
+                    PiMeterId.of(meterName),
+                    entity.getMeterEntry().getIndex().getIndex());
+            meterConfig = entity.getMeterEntry().getConfig();
+        } else if (entity.getEntityCase() == DIRECT_METER_ENTRY) {
+            PiTableEntry piTableEntry = TableEntryEncoder.decode(
+                    entity.getDirectMeterEntry().getTableEntry(),
+                    pipeconf);
+            piCellId = PiMeterCellId.ofDirect(piTableEntry);
+            meterConfig = entity.getDirectMeterEntry().getConfig();
+        } else {
+            throw new EncodeException(format(
+                    "unrecognized entity type '%s' in P4Runtime message",
+                    entity.getEntityCase().name()));
         }
 
-        PiMeterCellConfig.Builder builder = PiMeterCellConfig.builder();
-        builder.withMeterBand(new PiMeterBand(meterConfig.getCir(), meterConfig.getCburst()));
-        builder.withMeterBand(new PiMeterBand(meterConfig.getPir(), meterConfig.getPburst()));
-
-        return builder.withMeterCellId(piCellId).build();
+        return PiMeterCellConfig.builder()
+                .withMeterCellId(piCellId)
+                .withMeterBand(new PiMeterBand(meterConfig.getCir(),
+                                               meterConfig.getCburst()))
+                .withMeterBand(new PiMeterBand(meterConfig.getPir(),
+                                               meterConfig.getPburst()))
+                .build();
     }
-}
\ No newline at end of file
+}
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java
index 609c5c5..e8b4e21 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java
@@ -21,7 +21,6 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
 import com.google.protobuf.ByteString;
 import com.google.protobuf.InvalidProtocolBufferException;
 import io.grpc.Context;
@@ -36,7 +35,7 @@
 import org.onosproject.net.MastershipRole;
 import org.onosproject.net.pi.model.PiActionProfileId;
 import org.onosproject.net.pi.model.PiCounterId;
-import org.onosproject.net.pi.model.PiCounterType;
+import org.onosproject.net.pi.model.PiMeterId;
 import org.onosproject.net.pi.model.PiPipeconf;
 import org.onosproject.net.pi.model.PiTableId;
 import org.onosproject.net.pi.runtime.PiActionGroup;
@@ -46,8 +45,6 @@
 import org.onosproject.net.pi.runtime.PiEntity;
 import org.onosproject.net.pi.runtime.PiMeterCellConfig;
 import org.onosproject.net.pi.runtime.PiMeterCellId;
-import org.onosproject.net.pi.model.PiMeterType;
-import org.onosproject.net.pi.model.PiMeterId;
 import org.onosproject.net.pi.runtime.PiPacketOperation;
 import org.onosproject.net.pi.runtime.PiTableEntry;
 import org.onosproject.net.pi.service.PiPipeconfService;
@@ -131,8 +128,6 @@
     private Map<Uint128, CompletableFuture<Boolean>> arbitrationUpdateMap = Maps.newConcurrentMap();
     protected Uint128 p4RuntimeElectionId;
 
-    private static final long DEFAULT_INDEX = 0;
-
     /**
      * Default constructor.
      *
@@ -219,39 +214,8 @@
     @Override
     public CompletableFuture<Collection<PiCounterCellData>> readAllCounterCells(Set<PiCounterId> counterIds,
                                                                                 PiPipeconf pipeconf) {
-
-        /*
-        From p4runtime.proto, the scope of a ReadRequest is defined as follows:
-        CounterEntry:
-            - All counter cells for all meters if counter_id = 0 (default).
-            - All counter cells for given counter_id if index = 0 (default).
-        DirectCounterEntry:
-            - All counter cells for all meters if counter_id = 0 (default).
-            - All counter cells for given counter_id if table_entry.match is empty.
-         */
-
-        Set<PiCounterCellId> cellIds = Sets.newHashSet();
-
-        for (PiCounterId counterId : counterIds) {
-            if (!pipeconf.pipelineModel().counter(counterId).isPresent()) {
-                log.warn("Unable to find counter '{}' in pipeline model",  counterId);
-                continue;
-            }
-            PiCounterType counterType = pipeconf.pipelineModel().counter(counterId).get().counterType();
-            switch (counterType) {
-                case INDIRECT:
-                    cellIds.add(PiCounterCellId.ofIndirect(counterId, 0));
-                    break;
-                case DIRECT:
-                    cellIds.add(PiCounterCellId.ofDirect(counterId, PiTableEntry.EMTPY));
-                    break;
-                default:
-                    log.warn("Unrecognized PI counter type '{}'", counterType);
-            }
-        }
-
-        return supplyInContext(() -> doReadCounterCells(cellIds, pipeconf),
-                               "readAllCounterCells-" + cellIds.hashCode());
+        return supplyInContext(() -> doReadAllCounterCells(counterIds, pipeconf),
+                               "readAllCounterCells-" + counterIds.hashCode());
     }
 
     @Override
@@ -297,34 +261,8 @@
     @Override
     public CompletableFuture<Collection<PiMeterCellConfig>> readAllMeterCells(Set<PiMeterId> meterIds,
                                                                                 PiPipeconf pipeconf) {
-
-        /*
-        From p4runtime.proto, the scope of a ReadRequest is defined as follows:
-        MeterEntry:
-            - All meter cells for all meters if meter_id = 0 (default).
-            - All meter cells for given meter_id if index = 0 (default).
-        DirectCounterEntry:
-            - All meter cells for all meters if meter_id = 0 (default).
-            - All meter cells for given meter_id if table_entry.match is empty.
-         */
-
-        Set<PiMeterCellId> cellIds = Sets.newHashSet();
-        for (PiMeterId meterId : meterIds) {
-            PiMeterType meterType = pipeconf.pipelineModel().meter(meterId).get().meterType();
-            switch (meterType) {
-                case INDIRECT:
-                    cellIds.add(PiMeterCellId.ofIndirect(meterId, DEFAULT_INDEX));
-                    break;
-                case DIRECT:
-                    cellIds.add(PiMeterCellId.ofDirect(meterId, PiTableEntry.EMTPY));
-                    break;
-                default:
-                    log.warn("Unrecognized PI meter type '{}'", meterType);
-            }
-        }
-
-        return supplyInContext(() -> doReadMeterCells(cellIds, pipeconf),
-                               "readAllMeterCells-" + cellIds.hashCode());
+        return supplyInContext(() -> doReadAllMeterCells(meterIds, pipeconf),
+                               "readAllMeterCells-" + meterIds.hashCode());
     }
 
     /* Blocking method implementations below */
@@ -416,7 +354,7 @@
             return true;
         }
 
-        Collection<Update> updateMsgs = null;
+        Collection<Update> updateMsgs;
         try {
             updateMsgs = TableEntryEncoder.encode(piTableEntries, pipeconf)
                     .stream()
@@ -566,20 +504,32 @@
         controller.postEvent(event);
     }
 
-    private Collection<PiCounterCellData> doReadCounterCells(Collection<PiCounterCellId> cellIds, PiPipeconf pipeconf) {
+    private Collection<PiCounterCellData> doReadAllCounterCells(
+            Collection<PiCounterId> counterIds, PiPipeconf pipeconf) {
+        return doReadCounterEntities(
+                CounterEntryCodec.readAllCellsEntities(counterIds, pipeconf),
+                pipeconf);
+    }
 
-        // We use this map to remember the original PI counter IDs of the returned response.
-        final Map<Integer, PiCounterId> counterIdMap = Maps.newHashMap();
+    private Collection<PiCounterCellData> doReadCounterCells(
+            Collection<PiCounterCellId> cellIds, PiPipeconf pipeconf) {
+        return doReadCounterEntities(
+                CounterEntryCodec.encodePiCounterCellIds(cellIds, pipeconf),
+                pipeconf);
+    }
+
+    private Collection<PiCounterCellData> doReadCounterEntities(
+            Collection<Entity> counterEntities, PiPipeconf pipeconf) {
+
+        if (counterEntities.size() == 0) {
+            return Collections.emptyList();
+        }
 
         final ReadRequest request = ReadRequest.newBuilder()
                 .setDeviceId(p4DeviceId)
-                .addAllEntities(CounterEntryCodec.encodePiCounterCellIds(cellIds, counterIdMap, pipeconf))
+                .addAllEntities(counterEntities)
                 .build();
 
-        if (request.getEntitiesList().size() == 0) {
-            return Collections.emptyList();
-        }
-
         final Iterable<ReadResponse> responses;
         try {
             responses = () -> blockingStub.read(request);
@@ -593,7 +543,7 @@
                 .flatMap(List::stream)
                 .collect(Collectors.toList());
 
-        return CounterEntryCodec.decodeCounterEntities(entities, counterIdMap, pipeconf);
+        return CounterEntryCodec.decodeCounterEntities(entities, pipeconf);
     }
 
     private boolean doWriteActionGroupMembers(PiActionGroup group, WriteOperationType opType, PiPipeconf pipeconf) {
@@ -789,47 +739,60 @@
         }
     }
 
-    private Collection<PiMeterCellConfig> doReadMeterCells(Collection<PiMeterCellId> cellIds, PiPipeconf pipeconf) {
+    private Collection<PiMeterCellConfig> doReadAllMeterCells(
+            Collection<PiMeterId> meterIds, PiPipeconf pipeconf) {
+        return doReadMeterEntities(MeterEntryCodec.readAllCellsEntities(
+                meterIds, pipeconf), pipeconf);
+    }
 
-        // We use this map to remember the original PI meter IDs of the returned response.
-        Map<Integer, PiMeterId> meterIdMap = Maps.newHashMap();
-        Collection<PiMeterCellConfig> piMeterCellConfigs = cellIds.stream()
+    private Collection<PiMeterCellConfig> doReadMeterCells(
+            Collection<PiMeterCellId> cellIds, PiPipeconf pipeconf) {
+
+        final Collection<PiMeterCellConfig> piMeterCellConfigs = cellIds.stream()
                 .map(cellId -> PiMeterCellConfig.builder()
-                        .withMeterCellId(cellId).build())
+                        .withMeterCellId(cellId)
+                        .build())
                 .collect(Collectors.toList());
 
+        return doReadMeterEntities(MeterEntryCodec.encodePiMeterCellConfigs(
+                piMeterCellConfigs, pipeconf), pipeconf);
+    }
+
+    private Collection<PiMeterCellConfig> doReadMeterEntities(
+            Collection<Entity> entitiesToRead, PiPipeconf pipeconf) {
+
+        if (entitiesToRead.size() == 0) {
+            return Collections.emptyList();
+        }
+
         final ReadRequest request = ReadRequest.newBuilder()
                 .setDeviceId(p4DeviceId)
-                .addAllEntities(MeterEntryCodec.encodePiMeterCellConfigs(piMeterCellConfigs, meterIdMap, pipeconf))
+                .addAllEntities(entitiesToRead)
                 .build();
 
-        if (request.getEntitiesList().size() == 0) {
-            return Collections.emptyList();
-        }
-
         final Iterable<ReadResponse> responses;
         try {
             responses = () -> blockingStub.read(request);
         } catch (StatusRuntimeException e) {
-            log.warn("Unable to read meters config: {}", e.getMessage());
+            log.warn("Unable to read meter cells: {}", e.getMessage());
             log.debug("exception", e);
             return Collections.emptyList();
         }
 
-        List<Entity> entities = StreamSupport.stream(responses.spliterator(), false)
+        List<Entity> responseEntities = StreamSupport
+                .stream(responses.spliterator(), false)
                 .map(ReadResponse::getEntitiesList)
                 .flatMap(List::stream)
                 .collect(Collectors.toList());
 
-        return MeterEntryCodec.decodeMeterEntities(entities, meterIdMap, pipeconf);
+        return MeterEntryCodec.decodeMeterEntities(responseEntities, pipeconf);
     }
 
     private boolean doWriteMeterCells(Collection<PiMeterCellConfig> cellIds, PiPipeconf pipeconf) {
 
-        final Map<Integer, PiMeterId> meterIdMap = Maps.newHashMap();
         WriteRequest.Builder writeRequestBuilder = WriteRequest.newBuilder();
 
-        Collection<Update> updateMsgs = MeterEntryCodec.encodePiMeterCellConfigs(cellIds, meterIdMap, pipeconf)
+        Collection<Update> updateMsgs = MeterEntryCodec.encodePiMeterCellConfigs(cellIds, pipeconf)
                 .stream()
                 .map(meterEntryMsg ->
                              Update.newBuilder()
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeUtils.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeUtils.java
index 411e1fb..25ef153 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeUtils.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeUtils.java
@@ -17,6 +17,7 @@
 package org.onosproject.p4runtime.ctl;
 
 import com.google.protobuf.ByteString;
+import p4.P4RuntimeOuterClass;
 
 import static java.lang.String.format;
 
@@ -48,4 +49,8 @@
                     entityDescr, bitWidth, prefixLength));
         }
     }
+
+    static P4RuntimeOuterClass.Index indexMsg(long index) {
+        return P4RuntimeOuterClass.Index.newBuilder().setIndex(index).build();
+    }
 }
diff --git a/protocols/p4runtime/proto/BUCK b/protocols/p4runtime/proto/BUCK
index 53db8a5..aa3d8d3 100644
--- a/protocols/p4runtime/proto/BUCK
+++ b/protocols/p4runtime/proto/BUCK
@@ -5,12 +5,13 @@
 PROTOBUF_VER = '3.2.0'
 GRPC_VER = '1.3.1'
 
-PI_COMMIT = '13d611a9c655938676ebcde2bd5653b461f46ca7'
+PI_COMMIT = '219b3d67299ec09b49f433d7341049256ab5f512'
 PI_BASEURL = 'https://github.com/p4lang/PI.git'
 
 # Wondering which .proto files to build? Check p4runtime's Makefile:
 # https://github.com/p4lang/PI/blob/master/proto/Makefile.am
 PROTO_SRCS = [
+    '/proto/p4/p4types.proto',
     '/proto/p4/p4runtime.proto',
     '/proto/p4/config/p4info.proto',
     '/proto/google/rpc/status.proto',
diff --git a/tools/dev/p4vm/install-p4-tools.sh b/tools/dev/p4vm/install-p4-tools.sh
index 7c57e8d..8625daa 100755
--- a/tools/dev/p4vm/install-p4-tools.sh
+++ b/tools/dev/p4vm/install-p4-tools.sh
@@ -15,9 +15,9 @@
 set -e
 
 BUILD_DIR=~/p4tools
-BMV2_COMMIT="3f1d8d7893d7cf1657285c8aacbb4af5c6d22620"
-PI_COMMIT="0325da7746efe192935e8969fd08eed68d654c98"
-P4C_COMMIT="4c0d629ce2492294ff4108c910f8e6be44112c68"
+BMV2_COMMIT="7e25eeb19d01eee1a8e982dc7ee90ee438c10a05"
+PI_COMMIT="219b3d67299ec09b49f433d7341049256ab5f512"
+P4C_COMMIT="48a57a6ae4f96961b74bd13f6bdeac5add7bb815"
 PROTOBUF_COMMIT="tags/v3.2.0"
 GRPC_COMMIT="tags/v1.3.2"
 LIBYANG_COMMIT="v0.14-r1"