mirror of
https://github.com/saymrwulf/uhd.git
synced 2026-05-16 21:10:10 +00:00
This commit adds pybind11 glue code for the userland chdr parsing code introduced in the uhd::utils::chdr namespace. Additionally, it moves some pybind11 adapter code to a common pybind_adaptors.hpp file which originally existed in the cal_python.hpp file. This commit also adds unit tests for the python bindings using a captured wireshark trace which is located in rfnoc_packets_*.py and some handwritten packets in hardcoded_packets.py Signed-off-by: Samuel O'Brien <sam.obrien@ni.com>
369 lines
16 KiB
C++
369 lines
16 KiB
C++
//
|
|
// Copyright 2020 Ettus Research, a National Instruments Brand
|
|
//
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
//
|
|
|
|
#pragma once
|
|
|
|
#include <uhd/utils/chdr/chdr_packet.hpp>
|
|
#include <uhd/utils/pybind_adaptors.hpp>
|
|
#include <pybind11/pybind11.h>
|
|
#include <pybind11/stl.h>
|
|
|
|
namespace py = pybind11;
|
|
|
|
using namespace uhd::utils::chdr;
|
|
namespace chdr_rfnoc = uhd::rfnoc::chdr;
|
|
|
|
template <typename payload_t>
|
|
void export_utils_with_payload(
|
|
pybind11::class_<chdr_packet>& packet_class, std::string packet_name)
|
|
{
|
|
std::string get_payload_name("get_payload_");
|
|
get_payload_name.append(packet_name);
|
|
|
|
std::string to_string_with_payload_name("to_string_with_payload_");
|
|
to_string_with_payload_name.append(packet_name);
|
|
|
|
packet_class
|
|
.def(py::init([](uhd::rfnoc::chdr_w_t chdr_w,
|
|
chdr_rfnoc::chdr_header header,
|
|
payload_t payload,
|
|
boost::optional<uint64_t> timestamp,
|
|
std::vector<uint64_t> metadata) {
|
|
return chdr_packet(chdr_w, header, payload, timestamp, metadata);
|
|
}),
|
|
py::arg("chdr_w"),
|
|
py::arg("header"),
|
|
py::arg("payload"),
|
|
py::arg("timestamp") = boost::optional<uint64_t>(),
|
|
py::arg("metadata") = std::vector<uint64_t>())
|
|
.def(get_payload_name.c_str(),
|
|
&chdr_packet::get_payload<payload_t>,
|
|
py::arg("endianness") = uhd::ENDIANNESS_LITTLE)
|
|
.def("set_payload",
|
|
&chdr_packet::set_payload<payload_t>,
|
|
py::arg("payload"),
|
|
py::arg("endianness") = uhd::ENDIANNESS_LITTLE)
|
|
.def(to_string_with_payload_name.c_str(),
|
|
&chdr_packet::to_string_with_payload<payload_t>,
|
|
py::arg("endianness") = uhd::ENDIANNESS_LITTLE);
|
|
}
|
|
|
|
void export_utils(py::module& m)
|
|
{
|
|
py::enum_<uhd::endianness_t>(m, "Endianness")
|
|
.value("LITTLE", uhd::ENDIANNESS_LITTLE)
|
|
.value("BIG", uhd::ENDIANNESS_BIG);
|
|
|
|
py::enum_<uhd::rfnoc::chdr_w_t>(m, "ChdrWidth")
|
|
.value("W64", uhd::rfnoc::CHDR_W_64)
|
|
.value("W128", uhd::rfnoc::CHDR_W_128)
|
|
.value("W256", uhd::rfnoc::CHDR_W_256)
|
|
.value("W512", uhd::rfnoc::CHDR_W_512);
|
|
|
|
pybind11::class_<chdr_packet> packet_class =
|
|
py::class_<chdr_packet>(m, "ChdrPacket")
|
|
.def(py::init([](uhd::rfnoc::chdr_w_t chdr_w,
|
|
chdr_rfnoc::chdr_header header,
|
|
py::bytes& payload,
|
|
boost::optional<uint64_t> timestamp,
|
|
py::bytes& metadata) {
|
|
return chdr_packet(chdr_w,
|
|
header,
|
|
pybytes_to_vector(payload),
|
|
timestamp,
|
|
pybytes_to_u64_vector(metadata));
|
|
}),
|
|
py::arg("chdr_w"),
|
|
py::arg("header"),
|
|
py::arg("payload"),
|
|
py::arg("timestamp") = boost::optional<uint64_t>(),
|
|
py::arg("metadata") = py::bytes())
|
|
// Methods
|
|
.def("__str__", &chdr_packet::to_string)
|
|
.def("__repr__", &chdr_packet::to_string)
|
|
.def("get_header", &chdr_packet::get_header)
|
|
.def("set_header", &chdr_packet::set_header)
|
|
.def("get_timestamp", &chdr_packet::get_timestamp)
|
|
.def("set_timestamp", &chdr_packet::set_timestamp)
|
|
.def("get_metadata", &chdr_packet::get_metadata)
|
|
.def("serialize",
|
|
&chdr_packet::serialize_to_byte_vector,
|
|
py::arg("endianness") = uhd::ENDIANNESS_LITTLE)
|
|
.def_static(
|
|
"deserialize",
|
|
[](uhd::rfnoc::chdr_w_t chdr_w,
|
|
std::vector<uint8_t> bytes,
|
|
uhd::endianness_t endianness) {
|
|
return chdr_packet::deserialize(
|
|
chdr_w, bytes.begin(), bytes.end(), endianness);
|
|
},
|
|
py::arg("chdr_w"),
|
|
py::arg("bytes"),
|
|
py::arg("endianness") = uhd::ENDIANNESS_LITTLE)
|
|
.def_static(
|
|
"deserialize",
|
|
[](uhd::rfnoc::chdr_w_t chdr_w,
|
|
py::bytes bytes,
|
|
uhd::endianness_t endianness) {
|
|
std::vector<uint8_t> vector = pybytes_to_vector(bytes);
|
|
return chdr_packet::deserialize(
|
|
chdr_w, vector.begin(), vector.end(), endianness);
|
|
},
|
|
py::arg("chdr_w"),
|
|
py::arg("bytes"),
|
|
py::arg("endianness") = uhd::ENDIANNESS_LITTLE)
|
|
.def("get_packet_len", &chdr_packet::get_packet_len)
|
|
.def("get_payload_bytes", &chdr_packet::get_payload_bytes)
|
|
.def("set_payload_bytes", &chdr_packet::set_payload_bytes)
|
|
.def("set_payload_bytes", [](chdr_packet& self, py::bytes bytes) {
|
|
auto bytes_vector = pybytes_to_vector(bytes);
|
|
self.set_payload_bytes(bytes_vector);
|
|
});
|
|
|
|
export_utils_with_payload<chdr_rfnoc::ctrl_payload>(packet_class, "ctrl");
|
|
export_utils_with_payload<chdr_rfnoc::mgmt_payload>(packet_class, "mgmt");
|
|
export_utils_with_payload<chdr_rfnoc::strs_payload>(packet_class, "strs");
|
|
export_utils_with_payload<chdr_rfnoc::strc_payload>(packet_class, "strc");
|
|
|
|
py::class_<chdr_rfnoc::chdr_header>(m, "ChdrHeader")
|
|
// Constructor
|
|
.def(py::init<>())
|
|
|
|
// Methods
|
|
.def("__str__", &chdr_rfnoc::chdr_header::to_string)
|
|
.def("__repr__", &chdr_rfnoc::chdr_header::to_string)
|
|
|
|
// Properties
|
|
.def_property(
|
|
"vc", &chdr_rfnoc::chdr_header::get_vc, &chdr_rfnoc::chdr_header::set_vc)
|
|
.def_property(
|
|
"eob", &chdr_rfnoc::chdr_header::get_eob, &chdr_rfnoc::chdr_header::set_eob)
|
|
.def_property(
|
|
"eov", &chdr_rfnoc::chdr_header::get_eov, &chdr_rfnoc::chdr_header::set_eov)
|
|
.def_property("pkt_type",
|
|
&chdr_rfnoc::chdr_header::get_pkt_type,
|
|
&chdr_rfnoc::chdr_header::set_pkt_type)
|
|
.def_property("seq_num",
|
|
&chdr_rfnoc::chdr_header::get_seq_num,
|
|
&chdr_rfnoc::chdr_header::set_seq_num)
|
|
.def_property("length",
|
|
&chdr_rfnoc::chdr_header::get_length,
|
|
&chdr_rfnoc::chdr_header::set_length)
|
|
.def_property("dst_epid",
|
|
&chdr_rfnoc::chdr_header::get_dst_epid,
|
|
&chdr_rfnoc::chdr_header::set_dst_epid)
|
|
.def_property("num_mdata",
|
|
&chdr_rfnoc::chdr_header::get_num_mdata,
|
|
&chdr_rfnoc::chdr_header::set_num_mdata)
|
|
.def("pack", &chdr_rfnoc::chdr_header::pack);
|
|
|
|
py::enum_<chdr_rfnoc::packet_type_t>(m, "PacketType")
|
|
.value("MGMT", chdr_rfnoc::PKT_TYPE_MGMT)
|
|
.value("STRS", chdr_rfnoc::PKT_TYPE_STRS)
|
|
.value("STRC", chdr_rfnoc::PKT_TYPE_STRC)
|
|
.value("CTRL", chdr_rfnoc::PKT_TYPE_CTRL)
|
|
.value("DATA_NO_TS", chdr_rfnoc::PKT_TYPE_DATA_NO_TS)
|
|
.value("DATA_WITH_TS", chdr_rfnoc::PKT_TYPE_DATA_WITH_TS);
|
|
|
|
py::class_<chdr_rfnoc::ctrl_payload>(m, "CtrlPayload")
|
|
// Constructor
|
|
.def(py::init<>())
|
|
|
|
// Methods
|
|
.def("has_timestamp", &chdr_rfnoc::ctrl_payload::has_timestamp)
|
|
.def_readwrite("dst_port", &chdr_rfnoc::ctrl_payload::dst_port)
|
|
.def_readwrite("src_port", &chdr_rfnoc::ctrl_payload::src_port)
|
|
.def_readwrite("seq_num", &chdr_rfnoc::ctrl_payload::seq_num)
|
|
.def_readwrite("timestamp", &chdr_rfnoc::ctrl_payload::timestamp)
|
|
.def_readwrite("is_ack", &chdr_rfnoc::ctrl_payload::is_ack)
|
|
.def_readwrite("src_epid", &chdr_rfnoc::ctrl_payload::src_epid)
|
|
.def_readwrite("address", &chdr_rfnoc::ctrl_payload::address)
|
|
.def_readwrite("byte_enable", &chdr_rfnoc::ctrl_payload::byte_enable)
|
|
.def_readwrite("op_code", &chdr_rfnoc::ctrl_payload::op_code)
|
|
.def_readwrite("status", &chdr_rfnoc::ctrl_payload::status)
|
|
.def("get_data",
|
|
[](chdr_rfnoc::ctrl_payload& self) -> std::vector<uint32_t> {
|
|
return self.data_vtr;
|
|
})
|
|
.def("set_data",
|
|
[](chdr_rfnoc::ctrl_payload& self, std::vector<uint32_t> data) {
|
|
self.data_vtr = data;
|
|
})
|
|
.def("__str__", &chdr_rfnoc::ctrl_payload::to_string)
|
|
.def("__repr__", &chdr_rfnoc::ctrl_payload::to_string);
|
|
|
|
py::enum_<chdr_rfnoc::ctrl_status_t>(m, "CtrlStatus")
|
|
.value("OKAY", chdr_rfnoc::CMD_OKAY)
|
|
.value("CMDERR", chdr_rfnoc::CMD_CMDERR)
|
|
.value("TSERR", chdr_rfnoc::CMD_TSERR)
|
|
.value("WARNING", chdr_rfnoc::CMD_WARNING);
|
|
|
|
py::enum_<chdr_rfnoc::ctrl_opcode_t>(m, "CtrlOpCode")
|
|
.value("SLEEP", chdr_rfnoc::OP_SLEEP)
|
|
.value("WRITE", chdr_rfnoc::OP_WRITE)
|
|
.value("READ", chdr_rfnoc::OP_READ)
|
|
.value("READ_WRITE", chdr_rfnoc::OP_READ_WRITE)
|
|
.value("BLOCK_WRITE", chdr_rfnoc::OP_BLOCK_WRITE)
|
|
.value("BLOCK_READ", chdr_rfnoc::OP_BLOCK_READ)
|
|
.value("POLL", chdr_rfnoc::OP_POLL)
|
|
.value("USER1", chdr_rfnoc::OP_USER1)
|
|
.value("USER2", chdr_rfnoc::OP_USER2)
|
|
.value("USER3", chdr_rfnoc::OP_USER3)
|
|
.value("USER4", chdr_rfnoc::OP_USER4)
|
|
.value("USER5", chdr_rfnoc::OP_USER5)
|
|
.value("USER6", chdr_rfnoc::OP_USER6);
|
|
|
|
py::class_<chdr_rfnoc::mgmt_payload>(m, "MgmtPayload")
|
|
// Constructor
|
|
.def(py::init<>())
|
|
|
|
// Methods
|
|
.def("set_header",
|
|
&chdr_rfnoc::mgmt_payload::set_header,
|
|
py::arg("src_epid"),
|
|
py::arg("proto_ver"),
|
|
py::arg("chdr_w"))
|
|
.def("add_hop", &chdr_rfnoc::mgmt_payload::add_hop)
|
|
.def("get_num_hops", &chdr_rfnoc::mgmt_payload::get_num_hops)
|
|
.def("get_hop",
|
|
&chdr_rfnoc::mgmt_payload::get_hop,
|
|
py::return_value_policy::reference_internal)
|
|
.def("pop_hop", &chdr_rfnoc::mgmt_payload::pop_hop)
|
|
.def_property("src_epid",
|
|
&chdr_rfnoc::mgmt_payload::get_src_epid,
|
|
&chdr_rfnoc::mgmt_payload::set_src_epid)
|
|
.def_property("chdr_w",
|
|
&chdr_rfnoc::mgmt_payload::get_chdr_w,
|
|
&chdr_rfnoc::mgmt_payload::set_chdr_w)
|
|
.def_property("proto_ver",
|
|
&chdr_rfnoc::mgmt_payload::get_proto_ver,
|
|
&chdr_rfnoc::mgmt_payload::set_proto_ver)
|
|
|
|
.def("__str__", &chdr_rfnoc::mgmt_payload::to_string)
|
|
.def("__repr__", &chdr_rfnoc::mgmt_payload::to_string)
|
|
.def("hops_to_string", &chdr_rfnoc::mgmt_payload::hops_to_string);
|
|
|
|
py::class_<chdr_rfnoc::mgmt_hop_t>(m, "MgmtHop")
|
|
// Constructor
|
|
.def(py::init<>())
|
|
|
|
// Methods
|
|
.def("add_op", &chdr_rfnoc::mgmt_hop_t::add_op)
|
|
.def("get_num_ops", &chdr_rfnoc::mgmt_hop_t::get_num_ops)
|
|
.def("get_op",
|
|
&chdr_rfnoc::mgmt_hop_t::get_op,
|
|
py::return_value_policy::reference_internal)
|
|
.def("__str__", &chdr_rfnoc::mgmt_hop_t::to_string)
|
|
.def("__repr__", &chdr_rfnoc::mgmt_hop_t::to_string);
|
|
|
|
py::class_<chdr_rfnoc::mgmt_op_t>(m, "MgmtOp")
|
|
// Constructor
|
|
.def(py::init<chdr_rfnoc::mgmt_op_t::op_code_t, uint64_t>(),
|
|
py::arg("op_code"),
|
|
py::arg("op_payload") = 0)
|
|
.def(py::init<chdr_rfnoc::mgmt_op_t::op_code_t,
|
|
chdr_rfnoc::mgmt_op_t::sel_dest_payload>(),
|
|
py::arg("op_code"),
|
|
py::arg("op_payload"))
|
|
.def(py::init<chdr_rfnoc::mgmt_op_t::op_code_t,
|
|
chdr_rfnoc::mgmt_op_t::cfg_payload>(),
|
|
py::arg("op_code"),
|
|
py::arg("op_payload"))
|
|
.def(py::init<chdr_rfnoc::mgmt_op_t::op_code_t,
|
|
chdr_rfnoc::mgmt_op_t::node_info_payload>(),
|
|
py::arg("op_code"),
|
|
py::arg("op_payload"))
|
|
|
|
// Methods
|
|
.def_property_readonly("op_code", &chdr_rfnoc::mgmt_op_t::get_op_code)
|
|
.def("get_op_payload", &chdr_rfnoc::mgmt_op_t::get_op_payload)
|
|
.def("__str__", &chdr_rfnoc::mgmt_op_t::to_string)
|
|
.def("__repr__", &chdr_rfnoc::mgmt_op_t::to_string);
|
|
|
|
py::enum_<chdr_rfnoc::mgmt_op_t::op_code_t>(m, "MgmtOpCode")
|
|
.value("NOP", chdr_rfnoc::mgmt_op_t::MGMT_OP_NOP)
|
|
.value("ADVERTISE", chdr_rfnoc::mgmt_op_t::MGMT_OP_ADVERTISE)
|
|
.value("SEL_DEST", chdr_rfnoc::mgmt_op_t::MGMT_OP_SEL_DEST)
|
|
.value("RETURN", chdr_rfnoc::mgmt_op_t::MGMT_OP_RETURN)
|
|
.value("INFO_REQ", chdr_rfnoc::mgmt_op_t::MGMT_OP_INFO_REQ)
|
|
.value("INFO_RESP", chdr_rfnoc::mgmt_op_t::MGMT_OP_INFO_RESP)
|
|
.value("CFG_WR_REQ", chdr_rfnoc::mgmt_op_t::MGMT_OP_CFG_WR_REQ)
|
|
.value("CFG_RD_REQ", chdr_rfnoc::mgmt_op_t::MGMT_OP_CFG_RD_REQ)
|
|
.value("CFG_RD_RESP", chdr_rfnoc::mgmt_op_t::MGMT_OP_CFG_RD_RESP);
|
|
|
|
py::class_<chdr_rfnoc::mgmt_op_t::sel_dest_payload>(m, "MgmtOpSelDest")
|
|
.def(py::init<uint16_t>())
|
|
.def_readonly("dest", &chdr_rfnoc::mgmt_op_t::sel_dest_payload::dest)
|
|
.def_static(
|
|
"parse", [](uint64_t value) -> chdr_rfnoc::mgmt_op_t::sel_dest_payload {
|
|
return value;
|
|
});
|
|
|
|
py::class_<chdr_rfnoc::mgmt_op_t::cfg_payload>(m, "MgmtOpCfg")
|
|
.def(py::init<uint16_t, uint32_t>(), py::arg("addr"), py::arg("data"))
|
|
.def_readonly("addr", &chdr_rfnoc::mgmt_op_t::cfg_payload::addr)
|
|
.def_readonly("data", &chdr_rfnoc::mgmt_op_t::cfg_payload::data)
|
|
.def_static("parse",
|
|
[](uint64_t value) -> chdr_rfnoc::mgmt_op_t::cfg_payload { return value; });
|
|
|
|
py::class_<chdr_rfnoc::mgmt_op_t::node_info_payload>(m, "MgmtOpNodeInfo")
|
|
.def(py::init<uint16_t, uint8_t, uint16_t, uint32_t>(),
|
|
py::arg("device_id"),
|
|
py::arg("node_type"),
|
|
py::arg("node_inst"),
|
|
py::arg("ext_info"))
|
|
.def_readonly("device_id", &chdr_rfnoc::mgmt_op_t::node_info_payload::device_id)
|
|
.def_readonly("node_type", &chdr_rfnoc::mgmt_op_t::node_info_payload::node_type)
|
|
.def_readonly("node_inst", &chdr_rfnoc::mgmt_op_t::node_info_payload::node_inst)
|
|
.def_readonly("ext_info", &chdr_rfnoc::mgmt_op_t::node_info_payload::ext_info)
|
|
.def_static(
|
|
"parse", [](uint64_t value) -> chdr_rfnoc::mgmt_op_t::node_info_payload {
|
|
return value;
|
|
});
|
|
|
|
py::class_<chdr_rfnoc::strs_payload>(m, "StrsPayload")
|
|
// Constructor
|
|
.def(py::init<>())
|
|
|
|
// Methods
|
|
.def_readwrite("src_epid", &chdr_rfnoc::strs_payload::src_epid)
|
|
.def_readwrite("status", &chdr_rfnoc::strs_payload::status)
|
|
.def_readwrite("capacity_bytes", &chdr_rfnoc::strs_payload::capacity_bytes)
|
|
.def_readwrite("capacity_pkts", &chdr_rfnoc::strs_payload::capacity_pkts)
|
|
.def_readwrite("xfer_count_bytes", &chdr_rfnoc::strs_payload::xfer_count_bytes)
|
|
.def_readwrite("xfer_count_pkts", &chdr_rfnoc::strs_payload::xfer_count_pkts)
|
|
.def_readwrite("buff_info", &chdr_rfnoc::strs_payload::buff_info)
|
|
.def_readwrite("status_info", &chdr_rfnoc::strs_payload::status_info)
|
|
|
|
.def("__str__", &chdr_rfnoc::strs_payload::to_string)
|
|
.def("__repr__", &chdr_rfnoc::strs_payload::to_string);
|
|
|
|
py::enum_<chdr_rfnoc::strs_status_t>(m, "StrsStatus")
|
|
.value("OKAY", chdr_rfnoc::STRS_OKAY)
|
|
.value("CMDERR", chdr_rfnoc::STRS_CMDERR)
|
|
.value("SEQERR", chdr_rfnoc::STRS_SEQERR)
|
|
.value("DATAERR", chdr_rfnoc::STRS_DATAERR)
|
|
.value("RTERR", chdr_rfnoc::STRS_RTERR);
|
|
|
|
py::class_<chdr_rfnoc::strc_payload>(m, "StrcPayload")
|
|
// Constructor
|
|
.def(py::init<>())
|
|
|
|
// Methods
|
|
.def_readwrite("src_epid", &chdr_rfnoc::strc_payload::src_epid)
|
|
.def_readwrite("op_code", &chdr_rfnoc::strc_payload::op_code)
|
|
.def_readwrite("op_data", &chdr_rfnoc::strc_payload::op_data)
|
|
.def_readwrite("num_pkts", &chdr_rfnoc::strc_payload::num_pkts)
|
|
.def_readwrite("num_bytes", &chdr_rfnoc::strc_payload::num_bytes)
|
|
|
|
.def("__str__", &chdr_rfnoc::strc_payload::to_string)
|
|
.def("__repr__", &chdr_rfnoc::strc_payload::to_string);
|
|
|
|
py::enum_<chdr_rfnoc::strc_op_code_t>(m, "StrcOpCode")
|
|
.value("INIT", chdr_rfnoc::STRC_INIT)
|
|
.value("PING", chdr_rfnoc::STRC_PING)
|
|
.value("RESYNC", chdr_rfnoc::STRC_RESYNC);
|
|
}
|