diff options
author | Samuel O'Brien <sam.obrien@ni.com> | 2020-06-25 15:43:32 -0500 |
---|---|---|
committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2020-07-16 09:59:25 -0500 |
commit | 919a147afcbecace5107a4d0a4da556cfd56df92 (patch) | |
tree | c7b5faea5b89069232e7fb9f7cfebe1e495f4bca /host/lib/utils | |
parent | be6491428db599867129733f73e7ce0ce23e05a7 (diff) | |
download | uhd-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.hpp | 369 |
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); +} |