aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/utils
diff options
context:
space:
mode:
authorSamuel O'Brien <sam.obrien@ni.com>2020-06-25 15:43:32 -0500
committerAaron Rossetto <aaron.rossetto@ni.com>2020-07-16 09:59:25 -0500
commit919a147afcbecace5107a4d0a4da556cfd56df92 (patch)
treec7b5faea5b89069232e7fb9f7cfebe1e495f4bca /host/lib/utils
parentbe6491428db599867129733f73e7ce0ce23e05a7 (diff)
downloaduhd-919a147afcbecace5107a4d0a4da556cfd56df92.tar.gz
uhd-919a147afcbecace5107a4d0a4da556cfd56df92.tar.bz2
uhd-919a147afcbecace5107a4d0a4da556cfd56df92.zip
python: Add bindings for C++ CHDR Parser
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>
Diffstat (limited to 'host/lib/utils')
-rw-r--r--host/lib/utils/utils_python.hpp369
1 files changed, 369 insertions, 0 deletions
diff --git a/host/lib/utils/utils_python.hpp b/host/lib/utils/utils_python.hpp
new file mode 100644
index 000000000..0e66e80a8
--- /dev/null
+++ b/host/lib/utils/utils_python.hpp
@@ -0,0 +1,369 @@
+//
+// 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);
+}