aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorLars Amsel <lars.amsel@ni.com>2022-05-15 20:09:47 +0200
committerAaron Rossetto <aaron.rossetto@ni.com>2022-06-20 05:23:21 -0700
commitb584a7a923596b02d1413cc82cec5090080a45ef (patch)
tree998bf90ee08ec8ada254a6d81c6381b39b33b030 /tools
parentci: streaming: flash x310 fpga (diff)
downloaduhd-b584a7a923596b02d1413cc82cec5090080a45ef.tar.xz
uhd-b584a7a923596b02d1413cc82cec5090080a45ef.zip
rfnoc: Add LUA based dissector
Add a dissector based on LUA. The C/C++ based dissector is hard to use on Windows system, because one needs to compile Wireshark as well. The LUA based dissector can be used without any compilation and it runs out of the box on all systems that run wireshark. It also provides improved preview display in the info column which makes it less often necessary to lookup data in the detail pane. Along with the LUA dissector comes a color rules file that colors the RFNoC packets based on their type which makes navigation in the log even more easy. There LUA dissector is able to configure the CHDR width in it's preferences. Co-authored-by: Martin Braun <martin.braun@ettus.com> Co-authored-by: Wade Fife <wade.fife@ettus.com> Co-authored-by: Aaron Rossetto <aaron.rossetto@ni.com>
Diffstat (limited to 'tools')
-rwxr-xr-xtools/dissectors/lua/color_rules.rfnoc13
-rwxr-xr-xtools/dissectors/lua/rfnoc.lua556
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)