mirror of
https://github.com/EttusResearch/uhd.git
synced 2026-10-25 17:36:10 +00:00
595 lines
24 KiB
Lua
Executable File
595 lines
24 KiB
Lua
Executable File
-- 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
|
|
-- Word 0
|
|
pf_ctrl_dst_port = ProtoField.uint32("rfnoc.ctrl.DstPort", "dst port", base.DEC, nil, 0x000003FF)
|
|
pf_ctrl_num_data = ProtoField.uint32("rfnoc.ctrl.NumData", "num data", base.DEC, nil, 0x00003C00)
|
|
pf_ctrl_has_timestamp = ProtoField.uint32("rfnoc.ctrl.HasTimestamp", "has time", base.DEC, yesno, 0x00004000)
|
|
pf_ctrl_is_ack = ProtoField.uint32("rfnoc.ctrl.IsACK", "is ACK", base.DEC, yesno, 0x00008000)
|
|
pf_ctrl_src_epid = ProtoField.uint32("rfnoc.ctrl.SrcEPID", "src EPID", base.DEC, nil, 0xFFFF0000)
|
|
-- Word 1
|
|
pf_ctrl_src_port = ProtoField.uint32("rfnoc.ctrl.SrcPort", "src port", base.DEC, nil, 0x000003FF)
|
|
-- rem dst port (reserved in a CHDR control packet) 0x000FFC00
|
|
pf_ctrl_ctrl_seq_num = ProtoField.uint32("rfnoc.ctrl.SeqNum", "ctrl seq num", base.DEC, nil, 0x0FF00000)
|
|
pf_ctrl_req_size = ProtoField.uint32("rfnoc.ctrl.ReqSize", "req size", base.DEC, nil, 0xF0000000)
|
|
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_req_size,
|
|
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(4, 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(4, 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_num_data, 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(0,4))
|
|
header:add_le(pf_ctrl_src_port, buffer(4,4))
|
|
header:add_le(pf_ctrl_ctrl_seq_num, buffer(4,4))
|
|
header:add_le(pf_ctrl_req_size, buffer(4,4))
|
|
local offset = 8
|
|
local has_ts = buffer(1,1):bitfield(1)
|
|
local is_ack = buffer(1,1):bitfield(0) == 1
|
|
local ack_info = ""
|
|
local time_info = ""
|
|
if is_ack 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))
|
|
local num_data_hdr = buffer(2, 1):bitfield(0, 4)
|
|
-- NumData is the number of data words actually present in the packet. Use
|
|
-- the buffer length as an upper bound to guard against malformed packets
|
|
-- where NumData is larger than the data actually present. For CHDR widths >
|
|
-- 64 bits this may include trailing padding words, but since num_data_hdr is
|
|
-- at most 15, the math.min below dominates for any valid or near-valid
|
|
-- packet.
|
|
local buffer_data_words = math.max(0, math.floor((buffer:len() - offset - 4) / 4))
|
|
local actual_data_words = math.min(num_data_hdr, buffer_data_words)
|
|
local data_hex = ""
|
|
for i = 0, actual_data_words - 1 do
|
|
local item = header:add_le(pf_ctrl_data, buffer(offset + 4 + i * 4, 4))
|
|
item:set_text(string.format("Data[%d]: 0x%s", i,
|
|
bit.tohex(buffer(offset + 4 + i * 4, 4):le_uint())))
|
|
if i == 0 then
|
|
data_hex = bit.tohex(buffer(offset + 4 + i * 4, 4):le_uint())
|
|
if actual_data_words > 1 then
|
|
data_hex = data_hex .. " ..."
|
|
end
|
|
end
|
|
end
|
|
-- Flag any bytes beyond the expected payload as unexpected. This can
|
|
-- indicate a malformed packet or an older firmware implementation that
|
|
-- sends extra data in read requests or write/sleep responses.
|
|
local expected_end = offset + 4 + actual_data_words * 4
|
|
if buffer:len() > expected_end then
|
|
local extra_item = header:add(pf_ctrl_data,
|
|
buffer(expected_end, buffer:len() - expected_end))
|
|
extra_item:set_text(string.format("Unexpected data (%d bytes)",
|
|
buffer:len() - expected_end))
|
|
extra_item:add_expert_info(PI_PROTOCOL, PI_NOTE,
|
|
"Unexpected data beyond expected control payload")
|
|
end
|
|
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
|
|
data_hex, -- 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)
|