diff options
| -rwxr-xr-x | tools/dissectors/lua/color_rules.rfnoc | 13 | ||||
| -rwxr-xr-x | tools/dissectors/lua/rfnoc.lua | 556 | 
2 files changed, 569 insertions, 0 deletions
| diff --git a/tools/dissectors/lua/color_rules.rfnoc b/tools/dissectors/lua/color_rules.rfnoc new file mode 100755 index 000000000..6b99db019 --- /dev/null +++ b/tools/dissectors/lua/color_rules.rfnoc @@ -0,0 +1,13 @@ +# Copyright 2022 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# These color rules ease orientation in RFNoC packet display. +# Import this file in Wireshark's Coloring Rules dialog +# (View -> Coloring Rules…) +@RFNoC Management@rfnoc.PktType == 0@[65535, 50372, 2313][0,0,0] +@RFNoC Stream Status@rfnoc.PktType == 1@[0,46774,30583][0,0,0] +@RFNoC Stream Command@rfnoc.PktType == 2@[1028,16705,8995][65535,65535,65535] +@RFNoC Control Transaction@rfnoc.PktType == 4@[25759,46003,59110][0,0,0] +@RFNoC Data Packet without a Timestamp@rfnoc.PktType == 6@[56283, 56283, 51400][32639,32639,32639] +@RFNoC Data Packet with a Timestamp@rfnoc.PktType == 7@[56283, 56283, 51400][0,0,0] diff --git a/tools/dissectors/lua/rfnoc.lua b/tools/dissectors/lua/rfnoc.lua new file mode 100755 index 000000000..866c8ea69 --- /dev/null +++ b/tools/dissectors/lua/rfnoc.lua @@ -0,0 +1,556 @@ +-- Copyright 2022 Ettus Research, a National Instruments Brand +-- +-- SPDX-License-Identifier: GPL-3.0-or-later + +-- This file defines a LUA dissector for RFNoC. All chapter references refer to +-- https://files.ettus.com/app_notes/RFNoC_Specification.pdf. + +-- to install the dissector choose a global or personal LUA plugin folder +-- these folder can be found under Help -> About Wireshark -> Folders + +-- RFNoC UDP port +local RFNOC_PORT = 49153 + +-- general purpose lookup for boolean fields +local yesno = { +  [0] = "no", +  [1] = "yes" +} + +-- lookup table for CHDR with preferences +local chdr_widths = { +  { 0, "64", 64 }, +  { 1, "128", 128 }, +  { 2, "256", 256 }, +  { 3, "512", 512 } +} + +-- Packet type definitions for RFNoC (2.2.1) +local packet_types = { +  [0x0] = "Management", +  [0x1] = "Stream Status", +  [0x2] = "Stream Command", +  [0x3] = "<Reserved>", +  [0x4] = "Control Transaction", +  [0x5] = "<Reserved>", +  [0x6] = "Data Packet without a Timestamp", +  [0x7] = "Data Packet with a Timestamp" +} + +-- same packet type definitions as short names +local packet_types_short = { +  [0x0] = "Mgmt", +  [0x1] = "StrS", +  [0x2] = "StrC", +  [0x3] = "<Re>", +  [0x4] = "Ctrl", +  [0x5] = "<Re>", +  [0x6] = "Data", +  [0x7] = "DaTS" +} + +-- CHDR widths reported by management packet (table 15) +local mgmt_chdr_widths = { +  [0x0] = "64 bits", +  [0x1] = "128 bits", +  [0x2] = "256 bits", +  [0x3] = "512 bits", +  [0x4] = "<Reserved>", +  [0x5] = "<Reserved>", +  [0x6] = "<Reserved>", +  [0x7] = "<Reserved>" +} + +-- op codes for control transaction packet (table 9) +local ctrl_op_codes = { +  [0] = "sleep", +  [1] = "write", +  [2] = "read", +  [3] = "read then write", +  [4] = "block write", +  [5] = "block read", +  [6] = "poll", +  [7] = "<reserved>", +  [8] = "<reserved>", +  [9] = "<reserved>", +  [10] = "user 1", +  [11] = "user 2", +  [12] = "user 3", +  [13] = "user 4", +  [14] = "user 5", +  [15] = "user 6" +} + +-- op codes for stream command packet (table 13) +local strc_op_codes = { +  [0] = "Initialize stream", +  [1] = "Ping", +  [2] = "Resynchronize flow control", +  [3] = "<reserved>", +  [4] = "<reserved>", +  [5] = "<reserved>", +  [6] = "<reserved>", +  [7] = "<reserved>" +} + +-- op codes for management packet (table 15) +local mgmt_op_codes = { +  [0] = "no-op", +  [1] = "advertise", +  [2] = "select destination", +  [3] = "return to sender", +  [4] = "node info request", +  [5] = "node info response", +  [6] = "config write", +  [7] = "config read request", +  [8] = "config read response" +} + +-- node types from management node info response (no ref) +local mgmt_op_nir_types = { +  [0] = "<invalid>", +  [1] = "Crossbar", +  [2] = "Stream Endpoint", +  [3] = "Transport Adapter" +} + +-- fill remaining types +for i = 4, 255 do +  mgmt_op_nir_types[i] = "<invalid>" +end + +-- states of control status field (table 8) +local ctrl_states = { +  [0] = "OK", +  [1] = "CMDERR", +  [2] = "TSERR", +  [3] = "WARNING", +} + +-- states of stream status' status field (table 11) +local strs_states = { +  [0] = "Okay (No Error)", +  [1] = "Command Error (Command execution failed)", +  [2] = "Sequence Error (Sequence number discontinuity)", +  [3] = "Data Error (Data integrity check failed)", +  [4] = "Routing Error (Unexpected destination)", +  [5] = "<reserved>", +  [6] = "<reserved>", +  [7] = "<reserved>" +} + +-- Protocol field definitions +-- Definitions are made for all fields of 32 bit or smaller. Larger fields +-- are handled directly, because the ProtoField does not support bitmasks +-- bigger than 32 bit. Therefore CHDR header fields are split into +-- 32 bit chunks (to make use of the bit-wise display of the ProtoField +-- which eases orientation in the detail pane). Also all CHDR header +-- fields are uint32 even if they would fit into a smaller field. +-- This eases orientation in the bit-wise display of the detail pane. +-- Because most stream packets fields are bigger than 32 bit there are no +-- ProtoField definitions with bit mask for the stream command/status +-- packets. +-- The naming convention for the ProtoFields follows the specification +-- names. All fields starts with rfnoc followed by a dot. CHDR header +-- fields have the format rfnoc.<field_name>. Packet fields follow the +-- convention rfnoc.<packet_short_name>.<field_name>. +-- The display name is a human readable form of the field name without +-- references to the packet name they are associated with. +-- The protocol field LUA names follow the naming scheme +-- pf_<packet_short_name>_<field_name>. CHDR fields use chdr instead of +-- the packet short name. The field name uses underscores as word separator. + +-- CHDR header fields +pf_chdr_dst_epid        = ProtoField.uint32("rfnoc.DstEPID",   "dest EPID",       base.DEC, nil,            0x0000FFFF) +pf_chdr_length          = ProtoField.uint32("rfnoc.Length",    "length",          base.DEC, nil,            0xFFFF0000) +pf_chdr_seq_num         = ProtoField.uint32("rfnoc.SeqNum",    "seq num",         base.DEC, nil,            0x0000FFFF) +pf_chdr_num_mdata       = ProtoField.uint32("rfnoc.NumMData",  "num metadata",    base.DEC, nil,            0x001F0000) +pf_chdr_pkt_type        = ProtoField.uint32("rfnoc.PktType",   "packet type",     base.DEC, packet_types,   0x00E00000) +pf_chdr_end_of_vector   = ProtoField.uint32("rfnoc.EOV",       "end of vector",   base.DEC, yesno,          0x01000000) +pf_chdr_end_of_burst    = ProtoField.uint32("rfnoc.EOB",       "end of burst",    base.DEC, yesno,          0x02000000) +pf_chdr_virtual_channel = ProtoField.uint32("rfnoc.VC",        "virtual channel", base.DEC, nil,            0xFC000000) + +pf_chdr_timestamp       = ProtoField.uint64("rfnoc.Timestamp", "timestamp",       base.HEX) +pf_chdr_metadata        = ProtoField.bytes("rfnoc.Metadata",   "metadata") +pf_chdr_payload         = ProtoField.bytes("rfnoc.Payload",    "payload") + +-- Management packet fields +pf_mgmt_src_epid      = ProtoField.uint32("rfnoc.mgmt.SrcEPID",   "src EPID",         base.DEC, nil,              0x0000FFFF) +pf_mgmt_num_hops      = ProtoField.uint32("rfnoc.mgmt.NumHops",   "num hops",         base.DEC, nil,              0x03FF0000) +-- reserved                                                                                                       0xFC000000 +-- reserved                                                                                                       0x00001FFF +pf_mgmt_chdr_width    = ProtoField.uint32("rfnoc.mgmt.CHDRWidth", "CHDR width",       base.DEC, mgmt_chdr_widths, 0x0000E000) +pf_mgmt_proto_ver     = ProtoField.uint32("rfnoc.mgmt.ProtoVer",  "protocol version", base.DEC, nil,              0xFFFF0000) +pf_mgmt_proto_major   = ProtoField.uint32("rfnoc.mgmt.MajorVer",  "major version",    base.DEC, nil,              0xFF000000) +pf_mgmt_proto_minor   = ProtoField.uint32("rfnoc.mgmt.MinorVer",  "minor version",    base.DEC, nil,              0x00FF0000) + +pf_mgmt_op_codes         = ProtoField.uint8("rfnoc.mgmt.OpCode",            "op code",       base.DEC, mgmt_op_codes) +pf_mgmt_op_sd_dest       = ProtoField.uint16("rfnoc.mgmt.sd.dest",          "select dest",   base.DEC) +pf_mgmt_op_nir_device_id = ProtoField.uint16("rfnoc.mgmt.nir.DeviceID",     "device ID",     base.DEC, nil) +pf_mgmt_op_nir_node_type = ProtoField.uint8("rfnoc.mgmt.nir.NodeType",      "node type",     base.DEC, mgmt_op_nir_types) +pf_mgmt_op_nir_node_inst = ProtoField.uint32("rfnoc.mgmt.nir.NodeInst",     "node inst",     base.DEC, nil) +pf_mgmt_op_nir_node_info = ProtoField.uint32("rfnoc.mgmt.nir.ExtendedInfo", "extended info", base.HEX, nil) +pf_mgmt_op_conf_address  = ProtoField.uint16("rfnoc.mgmt.conf.Address",     "address",       base.HEX) +pf_mgmt_op_conf_data     = ProtoField.uint32("rfnoc.mgmt.conf.Data",        "data",          base.HEX) + +-- Control packet fields +pf_ctrl_dst_port      = ProtoField.uint32("rfnoc.ctrl.DstPort",      "dst port",     base.DEC, nil,           0x000003FF) +pf_ctrl_src_port      = ProtoField.uint32("rfnoc.ctrl.SrcPort",      "src port",     base.DEC, nil,           0x000FFC00) +pf_ctrl_num_data      = ProtoField.uint32("rfnoc.ctrl.NumData",      "num data",     base.DEC, nil,           0x00F00000) +pf_ctrl_ctrl_seq_num  = ProtoField.uint32("rfnoc.ctrl.SeqNum",       "ctrl seq num", base.DEC, nil,           0x3F000000) +pf_ctrl_has_timestamp = ProtoField.uint32("rfnoc.ctrl.HasTimestamp", "has time",     base.DEC, yesno,         0x40000000) +pf_ctrl_is_ack        = ProtoField.uint32("rfnoc.ctrl.IsACK",        "is ACK",       base.DEC, yesno,         0x80000000) +pf_ctrl_src_epid      = ProtoField.uint32("rfnoc.ctrl.SrcEPID",      "src EPID",     base.DEC, nil,           0x0000FFFF) +-- reserved                                                                                                   0xFFFF0000 +pf_ctrl_timestamp     = ProtoField.uint64("rfnoc.ctrl.Timestamp",    "timestamp",    base.HEX) +pf_ctrl_address       = ProtoField.uint32("rfnoc.ctrl.Address",      "address",      base.HEX, nil,           0x000FFFFF) +pf_ctrl_byte_enable   = ProtoField.uint32("rfnoc.ctrl.ByteEnable",   "byte enable",  base.HEX, nil,           0x00F00000) +pf_ctrl_op_code       = ProtoField.uint32("rfnoc.ctrl.OpCode",       "op code",      base.DEC, ctrl_op_codes, 0x0F000000) +-- reserved                                                                                                   0x30000000 +pf_ctrl_status        = ProtoField.uint32("rfnoc.ctrl.Status",       "status",       base.DEC, ctrl_states,   0xC0000000) + +pf_ctrl_data          = ProtoField.bytes("rfnoc.ctrl.Data",          "data") + +-- Stream Status fields +pf_strs_src_epid           = ProtoField.uint16("rfnoc.strs.SrcEPID",           "src EPID") +pf_strs_status             = ProtoField.uint8("rfnoc.strs.Status",             "status", base.DEC, strs_states) +pf_strs_capacity_bytes     = ProtoField.uint64("rfnoc.strs.CapacityBytes",     "capacity bytes") +pf_strs_capacity_packets   = ProtoField.uint32("rfnoc.strs.CapacityPackets",   "capacity packets") +pf_strs_xfer_count_packets = ProtoField.uint64("rfnoc.strs.XfcerCountPackets", "transferred packets") +pf_strs_xfer_count_bytes   = ProtoField.uint64("rfnoc.strs.XferCountBytes",    "transferred bytes") +pf_strs_buff_info          = ProtoField.uint16("rfnoc.strs.BuffInfo",          "buffer info") +pf_strs_status_info        = ProtoField.uint64("rfnoc.strs.StatusInfo",        "status info") + +-- Stream Command fields +pf_strc_src_epid    = ProtoField.uint16("rfnoc.strc.SrcEPID",  "src EPID") +pf_strc_op_code     = ProtoField.uint8("rfnoc.strc.OpCode",    "op code", base.DEC, strc_op_codes) +pf_strc_op_data     = ProtoField.uint8("rfnoc.strc.OpData",    "op data") +pf_strc_num_packets = ProtoField.uint64("rfnoc.strc.NumPkts",  "num packets") +pf_strc_num_bytes   = ProtoField.uint64("rfnoc.strc.NumBytes", "num bytes") + + +-- RFNoC protocol dissector +rfnoc_proto = Proto("RFNoC",  "RFNoC Protocol") +rfnoc_proto.fields = { +  pf_chdr_dst_epid, +  pf_chdr_length, +  pf_chdr_seq_num, +  pf_chdr_num_mdata, +  pf_chdr_pkt_type, +  pf_chdr_end_of_vector, +  pf_chdr_end_of_burst, +  pf_chdr_virtual_channel, + +  pf_chdr_timestamp, +  pf_chdr_metadata, +  pf_chdr_payload, + +  pf_mgmt_src_epid, +  pf_mgmt_num_hops, +  pf_mgmt_chdr_width, +  pf_mgmt_proto_ver, +  pf_mgmt_proto_major, +  pf_mgmt_proto_minor, +  pf_mgmt_op_codes, +  pf_mgmt_op_sd_dest, +  pf_mgmt_op_nir_device_id, +  pf_mgmt_op_nir_node_type, +  pf_mgmt_op_nir_node_inst, +  pf_mgmt_op_nir_node_info, +  pf_mgmt_op_conf_address, +  pf_mgmt_op_conf_data, + +  pf_ctrl_dst_port, +  pf_ctrl_src_port, +  pf_ctrl_num_data, +  pf_ctrl_ctrl_seq_num, +  pf_ctrl_has_timestamp, +  pf_ctrl_is_ack, +  pf_ctrl_src_epid, +  pf_ctrl_timestamp, +  pf_ctrl_address, +  pf_ctrl_byte_enable, +  pf_ctrl_op_code, +  pf_ctrl_status, + +  pf_ctrl_data, + +  pf_strs_src_epid, +  pf_strs_status, +  pf_strs_capacity_bytes, +  pf_strs_capacity_packets, +  pf_strs_xfer_count_packets, +  pf_strs_xfer_count_bytes, +  pf_strs_buff_info, +  pf_strs_status_info, + +  pf_strc_src_epid, +  pf_strc_op_code, +  pf_strc_op_data, +  pf_strc_num_packets, +  pf_strc_num_bytes, +} + +-- define preferences +rfnoc_proto.prefs.chdr_width = Pref.enum( +    "CHDR width",                   -- label +    64,                             -- default +    "CHDR width used for decoding", -- description +    chdr_widths,                    -- lookup +    true                            -- use radio buttons in config +) + +-- main entry point for dissector +-- * decode the CHDR header +-- * dissect metadata +--   - metadata dissector return new dissector offset +-- * lookup decoder for packet type to decode packet-specific fields +--   - each packet decoder returns a tuple of src EPID (might be nil) +--     and additional info that will be placed along with the info +--     column in the main view +--   - the CHDR header is passed to the packet decoder, because +--     some decoders need references to CHDR fields. +function rfnoc_proto.dissector(buffer, pinfo, tree) +  buf_length = buffer:len() +  if buf_length == 0 then return end + +  pinfo.cols.protocol = rfnoc_proto.name +  pinfo.cols.info:clear() + +  local subtree = tree:add(rfnoc_proto, buffer(), "RFNoC") +  local chdr = subtree:add(rfnoc_proto, buffer(0, 8), "CHDR") + +  chdr:add_le(pf_chdr_dst_epid, buffer(0, 4)) +  chdr:add_le(pf_chdr_length, buffer(0, 4)) +  chdr:add_le(pf_chdr_seq_num, buffer(4, 4)) +  chdr:add_le(pf_chdr_num_mdata, buffer(4, 4)) +  chdr:add_le(pf_chdr_pkt_type, buffer(4, 4)) +  chdr:add_le(pf_chdr_end_of_vector, buffer(4, 4)) +  chdr:add_le(pf_chdr_end_of_burst, buffer(4, 4)) +  chdr:add_le(pf_chdr_virtual_channel, buffer(4, 4)) + +  local packet_decoders = { +    [0] = dissect_mgmt_packet, +    [1] = dissect_stream_status_packet, +    [2] = dissect_stream_command_packet, +    [3] = dissect_reserved_packet, +    [4] = dissect_ctrl_packet, +    [5] = dissect_reserved_packet, +    [6] = dissect_data_packet_without_ts, +    [7] = dissect_data_packet_with_ts +  } + +  local packet_len = buffer(2, 2):le_uint() +  local packet_type = buffer(6, 1):bitfield(0, 3) +  local offset = dissect_metadata(buffer, packet_type, pinfo, subtree) +  local src, info = packet_decoders[buffer(6, 1):bitfield(0, 3)]( +      buffer(0, offset), +      buffer(offset, packet_len - offset), +      pinfo, subtree) + +  local src_str = src == nil and "   " or string.format("%3d", src) +  pinfo.cols.info:append(string.format("[%s] %s -> %3d %s", +      packet_types_short[packet_type], -- packet type +      src_str,                         -- src EPID +      buffer(0, 2):le_uint(),          -- dst EPID +      info                             -- packet specific info +  )) +end + +-- see 2.2.1 for details about metadata memory layout depending +-- on CHDR width. +function dissect_metadata(buffer, packet_type, pinfo, subtree) +  local mdata_len = buffer(6,1):bitfield(3, 5) +  local chdr_width_in_bytes = rfnoc_proto.prefs.chdr_width / 8 +  local packet_offset = 0 +  if rfnoc_proto.prefs.chdr_width == 64 and packet_type == 7 then +    packet_offset = chdr_width_in_bytes * (2 + mdata_len); +  else +    packet_offset = chdr_width_in_bytes * (1 + mdata_len); +  end +  if mdata_len > 0 then +    subtree:add(pf_chdr_metadata, buffer(packet_offset, mdata_len * chdr_width_in_bytes)) +  end +  return packet_offset + mdata_len * chdr_width_in_bytes +end + +function dissect_mgmt_op(buffer, hop_node, op_index, op_code, op_payload) +  local op_node = hop_node:add(rfnoc_proto, buffer(), "Operation " .. op_index) +  local op_code = buffer(1, 1):le_uint() +  local ext_info = bit.rshift(buffer(5, 3):le_uint(), 6) +  local op_info =  mgmt_op_codes[buffer(1, 1):le_uint()] + +  op_node:add_le(pf_mgmt_op_codes, buffer(1, 1)) +  if op_code == 2 then     -- select destination +    op_node:add(pf_mgmt_op_sd_dest, bit.band(buffer(2, 2):le_uint(), 0x3FF)) +    op_info = op_info .. " " .. bit.band(buffer(2, 2):le_uint(), 0x3FF) +  elseif op_code == 5 then -- node response info +    local node_type = bit.band(buffer(4, 1):le_uint(), 0xF) +    local node_inst = bit.rshift(bit.band(buffer(4, 2):le_uint(), 0x3FF0), 4) +    op_node:add_le(pf_mgmt_op_nir_device_id, buffer(2, 2)) +    op_node:add_le(pf_mgmt_op_nir_node_type, node_type) +    op_node:add_le(pf_mgmt_op_nir_node_inst, node_inst) +    local node_ext_info = op_node:add_le(pf_mgmt_op_nir_node_info, ext_info) +    if node_type == 1 then -- crossbar +      node_ext_info:set_text(string.format( +          "NPORTS=%d, NPORTS_MGMT=%d, EXT_RTCFG_PORT=%d (0x%05X)", +          bit.band(ext_info, 0xFF), +          bit.band(bit.rshift(ext_info, 8), 0xFF), +          bit.band(bit.rshift(ext_info, 16), 1), +          ext_info +      )) +    elseif node_type == 2 then -- streaming endpoint +      node_ext_info:set_text(string.format( +          "AXIS_CTRL_EN=%d, AXIS_DATA_EN=%d, NUM_DATA_I=%d NUM_DATA_O=%d, REPORT_STREAM_ERRS=%d (0x%05X)", +          bit.band(ext_info, 1), +          bit.band(bit.rshift(ext_info, 1), 1), +          bit.band(bit.rshift(ext_info, 2), 0x3F), +          bit.band(bit.rshift(ext_info, 8), 0x3F), +          bit.band(bit.rshift(ext_info, 14), 1), +          ext_info +      )) +    elseif node_type == 3 then -- transport adapter +      node_ext_info:set_text(string.format( +          "NODE_SUBTYPE=%d (0x%05X)", +          bit.band(ext_info, 0xFF), +          ext_info +      )) +    else +      node_ext_info:append_text(" <invalid>") +    end +  elseif op_code == 6 then -- config write +    op_node:add_le(pf_mgmt_op_conf_address, buffer(2, 2)) +    op_node:add_le(pf_mgmt_op_conf_data, buffer(4, 4)) +    op_info = string.format("%s %x->%4.4x", op_info, buffer(4, 4):le_uint(), buffer(2, 2):le_uint()) +  elseif op_code == 7 then -- config read request +    op_node:add_le(pf_mgmt_op_conf_address, buffer(2, 2)) +  elseif op_code == 8 then -- config read response +    op_node:add_le(pf_mgmt_op_conf_address, buffer(2, 2)) +    op_node:add_le(pf_mgmt_op_conf_data, buffer(4, 4)) +  end +  return op_info +end + +function dissect_mgmt_packet(header_buffer, buffer, pinfo, tree) +  local header = tree:add(rfnoc_proto, buffer(), "Management") +  header:add_le(pf_mgmt_src_epid, buffer(0, 4)) +  header:add_le(pf_mgmt_num_hops, buffer(0, 4)) +  header:add_le(pf_mgmt_chdr_width, buffer(4, 4)) +  header:add_le(pf_mgmt_proto_ver, buffer(4, 4)) +  header:add_le(pf_mgmt_proto_major, buffer(4, 4)) +  header:add_le(pf_mgmt_proto_minor, buffer(4, 4)) + +  local num_hops = bit.band(buffer(2, 4):le_uint(), 0x3FF) +  local offset = 8 +  local hop_info = "" +  for hop = 0, num_hops - 1 do +    local ops_pending = buffer(offset, 1):le_uint() +    local hop_node = header:add(rfnoc_proto, buffer(offset, 8 * (ops_pending + 1)), "Hop " .. hop) +    for op_nr = 0, ops_pending do +      hop_info = hop_info .. dissect_mgmt_op(buffer(offset, 8), hop_node, op_nr, buffer(offset + 1, 1), buffer(offset + 2, 6)) +      offset = offset + 8 +      if ops_pending > 0 then +        ops_pending = buffer(offset, 1):le_uint() +        hop_info = hop_info .. ", " +      end +    end +    if hop < num_hops - 1 then +      hop_info = hop_info .. " | " +    end +  end +  return buffer(0, 2):le_uint(), hop_info +end + +function dissect_stream_status_packet(header_buffer, buffer, pinfo, tree) +  local header = tree:add(rfnoc_proto, buffer(), "Stream Status") +  header:add_le(pf_strs_src_epid, buffer(0, 2)) +  header:add_le(pf_strs_status, buffer(2, 1):bitfield(0, 4)) +  header:add_le(pf_strs_capacity_bytes, buffer(3, 5)) +  header:add_le(pf_strs_capacity_packets, buffer(8, 3)) +  header:add_le(pf_strs_xfer_count_packets, buffer(11, 5)) +  header:add_le(pf_strs_xfer_count_bytes, buffer(16, 8)) +  header:add_le(pf_strs_buff_info, buffer(24, 2)) +  header:add_le(pf_strs_status_info, buffer(26, 6)) +  return buffer(0, 2):le_uint(), string.format("%s (packets: %s, bytes: %s)", +      strs_states[buffer(2, 1):bitfield(0, 4)],                           -- stream status +      tostring(UInt64(buffer(11, 4):le_uint(), buffer(15, 1):le_uint())), -- transferred packets +      tostring(UInt64(buffer(16, 4):le_uint(), buffer(20, 4):le_uint()))  -- transferred bytes +  ) +end + +function dissect_stream_command_packet(header_buffer, buffer, pinfo, tree) +  local header = tree:add(rfnoc_proto, buffer(), "Stream Command") +  header:add_le(pf_strc_src_epid, buffer(0, 2)) +  header:add_le(pf_strc_op_code, buffer(2, 1):bitfield(4, 4)) +  header:add_le(pf_strc_op_data, buffer(2, 1):bitfield(0, 4)) +  header:add_le(pf_strc_num_packets, buffer(3, 5)) +  header:add_le(pf_strc_num_bytes, buffer(8, 8)) +  return buffer(0, 2):le_uint(), string.format("%s (packets: %s, bytes: %s)", +      strc_op_codes[buffer(2, 1):bitfield(4, 4)],                       -- op code +      tostring(UInt64(buffer(3, 4):le_uint(), buffer(7, 1):le_uint())), -- packets +      tostring(UInt64(buffer(8, 4):le_uint(), buffer(12, 4):le_uint())) -- bytes +  ) +end + +function dissect_reserved_packet(header_buffer, buffer, pinfo, tree) +  return nil, "" +end + +function dissect_ctrl_packet(header_buffer, buffer, pinfo, tree) +  local header = tree:add(rfnoc_proto, buffer(), "Control") +  header:add_le(pf_ctrl_dst_port, buffer(0,4)) +  header:add_le(pf_ctrl_src_port, buffer(0,4)) +  header:add_le(pf_ctrl_num_data, buffer(0,4)) +  header:add_le(pf_ctrl_ctrl_seq_num, buffer(0,4)) +  header:add_le(pf_ctrl_has_timestamp, buffer(0,4)) +  header:add_le(pf_ctrl_is_ack, buffer(0,4)) +  header:add_le(pf_ctrl_src_epid, buffer(4,4)) +  local offset = 8 +  local has_ts = buffer(3,1):bitfield(1) +  local ack_info = "" +  local time_info = "" +  if buffer(3,1):bitfield(0) == 1 then +    ack_info = "ACK " +  end +  if has_ts == 1 then +    header:add_le(pf_ctrl_timestamp, buffer(offset, 8)) +    time_info = " @ 0x" .. tostring(buffer(offset, 8):le_uint64():tohex()) +    offset = offset + 8 +  end +  header:add_le(pf_ctrl_address, buffer(offset, 4)) +  header:add_le(pf_ctrl_byte_enable, buffer(offset, 4)) +  header:add_le(pf_ctrl_op_code, buffer(offset, 4)) +  header:add_le(pf_ctrl_status, buffer(offset, 4)) +  header:add(pf_ctrl_data, buffer(offset + 4)) +  return buffer(4, 2):le_uint(), string.format("%s%s %s -> %s%s", +      ack_info,                                                  -- add ACK when is_ack is 1 +      ctrl_op_codes[buffer(offset + 3):bitfield(4,4)],           -- op code +      buffer(offset + 4):bytes():tohex(),                        -- data +      bit.tohex(bit.band(buffer(offset, 4):le_uint(), 0xFFFFF)), -- address +      time_info                                                  -- timestamp if has_ts is 1 +  ) +end + +function dissect_data_packet_without_ts(header_buffer, buffer, pinfo, tree) +  tree:add(pf_chdr_payload, buffer) +  local burst_info = "" + +  if header_buffer(7, 1):bitfield(6) == 1 then +    burst_info = burst_info .. "EOB " +  end +  return nil, burst_info +end + +function dissect_data_packet_with_ts(header_buffer, buffer, pinfo, tree) +  tree:add_le(pf_chdr_timestamp, header_buffer(8, 8)) +  dstEpid, info = dissect_data_packet_without_ts(header_buffer, buffer, pinfo, tree) +  info = info  .. string.format("time: 0x%.8x%.8x", +      header_buffer(12, 4):le_uint(), +      header_buffer(8, 4):le_uint() +  ) +  return nil, info +end + +-- register RFNoC dissector +local udp_port = DissectorTable.get("udp.port") +udp_port:add(RFNOC_PORT, rfnoc_proto) | 
