Communication: Add SPI support
parent
6d82289864
commit
88d32128c9
|
|
@ -204,6 +204,7 @@ set(SRC_FILES
|
|||
communication/packet/flexraypacket.cpp
|
||||
communication/packet/canpacket.cpp
|
||||
communication/packet/a2bpacket.cpp
|
||||
communication/packet/spipacket.cpp
|
||||
communication/packet/ethernetpacket.cpp
|
||||
communication/packet/versionpacket.cpp
|
||||
communication/packet/iso9141packet.cpp
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ pybind11_add_module(icsneopy
|
|||
icsneopy/communication/message/mdiomessage.cpp
|
||||
icsneopy/communication/message/gptpstatusmessage.cpp
|
||||
icsneopy/communication/message/ethernetstatusmessage.cpp
|
||||
icsneopy/communication/message/spimessage.cpp
|
||||
icsneopy/communication/message/macsecmessage.cpp
|
||||
icsneopy/communication/message/scriptstatusmessage.cpp
|
||||
icsneopy/communication/message/callback/messagecallback.cpp
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/functional.h>
|
||||
|
||||
#include "icsneo/communication/message/spimessage.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
void init_spimessage(pybind11::module_& m) {
|
||||
pybind11::class_<SPIMessage, std::shared_ptr<SPIMessage>, Frame> spiMessage(m, "SPIMessage");
|
||||
pybind11::enum_<SPIMessage::Direction>(spiMessage, "Direction")
|
||||
.value("Write", SPIMessage::Direction::Write)
|
||||
.value("Read", SPIMessage::Direction::Read);
|
||||
spiMessage
|
||||
.def(pybind11::init())
|
||||
.def_readwrite("direction", &SPIMessage::direction)
|
||||
.def_readwrite("address", &SPIMessage::address)
|
||||
.def_readwrite("mms", &SPIMessage::mms)
|
||||
.def_readwrite("stats", &SPIMessage::stats)
|
||||
.def_readwrite("payload", &SPIMessage::payload);
|
||||
}
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
|
|
@ -20,6 +20,7 @@ void init_linmessage(pybind11::module_&);
|
|||
void init_tc10statusmessage(pybind11::module_&);
|
||||
void init_gptpstatusmessage(pybind11::module_&);
|
||||
void init_mdiomessage(pybind11::module_&);
|
||||
void init_spimessage(pybind11::module_&);
|
||||
void init_ethernetstatusmessage(pybind11::module_&);
|
||||
void init_macsecmessage(pybind11::module_&);
|
||||
void init_scriptstatusmessage(pybind11::module_&);
|
||||
|
|
@ -55,6 +56,7 @@ PYBIND11_MODULE(icsneopy, m) {
|
|||
init_ethernetstatusmessage(m);
|
||||
init_macsecmessage(m);
|
||||
init_scriptstatusmessage(m);
|
||||
init_spimessage(m);
|
||||
init_messagefilter(m);
|
||||
init_messagecallback(m);
|
||||
init_diskdriver(m);
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
#include "icsneo/communication/packet/genericbinarystatuspacket.h"
|
||||
#include "icsneo/communication/packet/livedatapacket.h"
|
||||
#include "icsneo/communication/packet/hardwareinfopacket.h"
|
||||
#include "icsneo/communication/packet/spipacket.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
|
@ -184,6 +185,19 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
|||
msg.timestamp *= timestampResolution;
|
||||
return true;
|
||||
}
|
||||
case Network::Type::SPI: {
|
||||
result = HardwareSPIPacket::DecodeToMessage(packet->data);
|
||||
|
||||
if(!result) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false; // A nullptr was returned, the packet was not long enough to decode
|
||||
}
|
||||
|
||||
SPIMessage& msg = *static_cast<SPIMessage*>(result.get());
|
||||
msg.network = packet->network;
|
||||
msg.timestamp *= timestampResolution;
|
||||
return true;
|
||||
}
|
||||
case Network::Type::MDIO: {
|
||||
result = HardwareMDIOPacket::DecodeToMessage(packet->data);
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include "icsneo/communication/packet/a2bpacket.h"
|
||||
#include "icsneo/communication/packet/linpacket.h"
|
||||
#include "icsneo/communication/packet/mdiopacket.h"
|
||||
#include "icsneo/communication/packet/spipacket.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
|
|
@ -133,6 +134,17 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
|
|||
}
|
||||
break;
|
||||
} // End of Network::Type::MDIO
|
||||
case Network::Type::SPI: {
|
||||
auto msg = std::dynamic_pointer_cast<SPIMessage>(message);
|
||||
if(!msg) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false; // The message was not a properly formed LiveDataMessage
|
||||
}
|
||||
if(!HardwareSPIPacket::EncodeFromMessage(*msg, result, report))
|
||||
return false;
|
||||
result = packetizer.packetWrap(result, false);
|
||||
return true;
|
||||
} // End of Network::Type::SPI
|
||||
default:
|
||||
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
#include "icsneo/communication/packet/spipacket.h"
|
||||
#include "icsneo/communication/command.h"
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
static size_t SPISubHeaderLength = 5u;
|
||||
|
||||
std::shared_ptr<Message> HardwareSPIPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
|
||||
if(bytestream.size() < sizeof(HardwareSPIPacket)) {
|
||||
return nullptr;
|
||||
}
|
||||
const HardwareSPIPacket* packet = (const HardwareSPIPacket*)bytestream.data();
|
||||
size_t totalPackedLength = static_cast<size_t>(bytestream.size()) - sizeof(HardwareSPIPacket); // First 28 bytes are message header.
|
||||
if(totalPackedLength < SPISubHeaderLength) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint8_t* bytes = bytestream.data() + sizeof(HardwareSPIPacket);
|
||||
std::shared_ptr<SPIMessage> msg = std::make_shared<SPIMessage>();
|
||||
msg->direction = static_cast<SPIMessage::Direction>(bytes[0]);
|
||||
msg->address = *reinterpret_cast<const uint16_t*>(&bytes[1]);
|
||||
msg->mms = bytes[3];
|
||||
msg->stats = packet->stats;
|
||||
msg->timestamp = packet->timestamp.TS;
|
||||
|
||||
size_t numWords = (totalPackedLength - SPISubHeaderLength) / 4;
|
||||
msg->payload.reserve(numWords);
|
||||
for(size_t offset = SPISubHeaderLength; offset < totalPackedLength; offset += 4) {
|
||||
msg->payload.push_back(*reinterpret_cast<const uint32_t*>(bytes + offset));
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
bool HardwareSPIPacket::EncodeFromMessage(const SPIMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& /*report*/) {
|
||||
// Payload length is everything excluding cmdHeader (note at the beginning there is an offset of 2)
|
||||
uint16_t payloadLength = static_cast<uint16_t>(
|
||||
2 +
|
||||
sizeof(HardwareSPIPacket) +
|
||||
SPISubHeaderLength +
|
||||
message.payload.size() * sizeof(uint32_t)
|
||||
);
|
||||
if(payloadLength % 2) {
|
||||
// Pad payload to even number
|
||||
payloadLength++;
|
||||
}
|
||||
// +1 for AA, another +1 for firmware nuance
|
||||
uint16_t fullSize = 1 + sizeof(ExtendedCommandHeader) + payloadLength + 1;
|
||||
uint16_t unwrappedSize = sizeof(ExtendedCommandHeader) + payloadLength; // fullSize without AA and firmware nuance
|
||||
|
||||
bytestream.resize(unwrappedSize, 0);
|
||||
uint32_t offset = 0;
|
||||
auto* cmdHeader = reinterpret_cast<ExtendedCommandHeader*>(bytestream.data() + offset);
|
||||
cmdHeader->netid = static_cast<uint8_t>(Network::NetID::Main51);
|
||||
cmdHeader->fullLength = fullSize;
|
||||
cmdHeader->command = static_cast<uint8_t>(Command::Extended);
|
||||
cmdHeader->extendedCommand = static_cast<uint16_t>(ExtendedCommand::TransmitCoreminiMessage);
|
||||
cmdHeader->payloadLength = payloadLength;
|
||||
|
||||
offset += sizeof(ExtendedCommandHeader) + 2; // Offset of 2 between header and packet
|
||||
auto* packet = reinterpret_cast<HardwareSPIPacket*>(bytestream.data() + offset);
|
||||
packet->header.frameLength = static_cast<uint16_t>(SPISubHeaderLength + message.payload.size() * sizeof(uint32_t));
|
||||
packet->networkID = static_cast<uint16_t>(message.network.getNetID());
|
||||
packet->length = packet->header.frameLength;
|
||||
packet->timestamp.IsExtended = 1;
|
||||
|
||||
offset += sizeof(HardwareSPIPacket);
|
||||
// Write the sub header details
|
||||
bytestream[offset++] = static_cast<uint8_t>(message.direction);
|
||||
bytestream[offset++] = static_cast<uint8_t>(message.address & 0xFF);
|
||||
bytestream[offset++] = static_cast<uint8_t>((message.address >> 8) & 0xFF);
|
||||
bytestream[offset++] = static_cast<uint8_t>(message.mms);
|
||||
bytestream[offset++] = static_cast<uint8_t>(message.payload.size());
|
||||
|
||||
// Write the words
|
||||
for(uint32_t word : message.payload) {
|
||||
*reinterpret_cast<uint32_t*>(bytestream.data() + offset) = word;
|
||||
offset += sizeof(uint32_t);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ option(LIBICSNEO_BUILD_CPP_MDIO_EXAMPLE "Build the MDIO example." ON)
|
|||
option(LIBICSNEO_BUILD_CPP_VSA_EXAMPLE "Build the VSA example." ON)
|
||||
option(LIBICSNEO_BUILD_CPP_APP_ERROR_EXAMPLE "Build the app error example." ON)
|
||||
option(LIBICSNEO_BUILD_CPP_FLEXRAY_EXAMPLE "Build the FlexRay example." ON)
|
||||
option(LIBICSNEO_BUILD_CPP_SPI_EXAMPLE "Build the SPI example." ON)
|
||||
|
||||
if(LIBICSNEO_BUILD_C_INTERACTIVE_EXAMPLE)
|
||||
add_subdirectory(c/interactive)
|
||||
|
|
@ -63,3 +64,7 @@ endif()
|
|||
if(LIBICSNEO_BUILD_CPP_FLEXRAY_EXAMPLE)
|
||||
add_subdirectory(cpp/flexray)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_BUILD_CPP_SPI_EXAMPLE)
|
||||
add_subdirectory(cpp/spi)
|
||||
endif()
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
add_executable(libicsneocpp-spi src/spi.cpp)
|
||||
target_link_libraries(libicsneocpp-spi icsneocpp)
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
/**
|
||||
* libicsneo SPI message example
|
||||
*
|
||||
* Read a register on NetID::SPI_01 and display the result to stdout
|
||||
*
|
||||
* Usage: libicsneo-spi [deviceSerial] [address] [numWords]
|
||||
*
|
||||
* Arguments:
|
||||
* deviceSerial: 6 character string for device serial
|
||||
* address: 16 bit register address
|
||||
* numWords: number of words to read
|
||||
*/
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <tuple>
|
||||
|
||||
#include "icsneo/icsneocpp.h"
|
||||
#include "icsneo/communication/message/spimessage.h"
|
||||
|
||||
static const std::string usage = "Usage: libicsneo-spi [deviceSerial] [address] [numWords]\n\n"
|
||||
"Arguments:\n"
|
||||
"deviceSerial: 6 character string for device serial\n"
|
||||
"address: hex number representing 16 bit register number\n"
|
||||
"numWords: number of words to read\n";
|
||||
|
||||
static std::tuple<std::string_view, uint16_t, uint16_t> parseArgs(std::vector<std::string_view>& args, bool& fail) {
|
||||
|
||||
if(args.size() != 4) {
|
||||
std::cerr << "Invalid argument count" << std::endl;
|
||||
std::cerr << usage;
|
||||
fail = true;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string_view serial = args[1];
|
||||
if(serial.size() != 6) {
|
||||
std::cerr << "Invalid serial length" << std::endl;
|
||||
std::cerr << usage;
|
||||
fail = true;
|
||||
return {};
|
||||
}
|
||||
|
||||
char* endPtr;
|
||||
uint16_t address = static_cast<uint16_t>(std::strtoul(args[2].data(), &endPtr, 16));
|
||||
if(endPtr != (args[2].data() + args[2].size())) {
|
||||
std::cerr << "Failed to convert address to hex" << std::endl;
|
||||
std::cerr << usage;
|
||||
fail = true;
|
||||
return {};
|
||||
}
|
||||
|
||||
uint16_t numWords = static_cast<uint16_t>(std::strtoul(args[3].data(), &endPtr, 16));
|
||||
if(endPtr != (args[3].data() + args[3].size())) {
|
||||
std::cerr << "Failed to convert numWords to decimal integer" << std::endl;
|
||||
std::cerr << usage;
|
||||
fail = true;
|
||||
return {};
|
||||
}
|
||||
fail = false;
|
||||
return {serial, address, numWords};
|
||||
}
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
std::vector<std::string_view> args(argv, argv + argc);
|
||||
bool fail;
|
||||
auto tup = parseArgs(args, fail);
|
||||
if(fail) {
|
||||
return -1;
|
||||
}
|
||||
auto serial = std::get<0>(tup);
|
||||
auto address = std::get<1>(tup);
|
||||
auto numWords = std::get<2>(tup);
|
||||
|
||||
std::shared_ptr<icsneo::Device> device = nullptr;
|
||||
for(const auto& dev : icsneo::FindAllDevices()) {
|
||||
if(dev->getSerial() == serial) {
|
||||
device = dev;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!device) {
|
||||
std::cerr << "Failed to find device" << std::endl;
|
||||
std::cerr << usage;
|
||||
return -1;
|
||||
}
|
||||
if(!device->open()) {
|
||||
std::cerr << "Failed to open device" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
if(!device->goOnline()) {
|
||||
std::cerr << "Failed to go online" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::cout << "Reading register " << std::setfill('0') << std::setw(4) << std::right << std::hex << address << std::endl;
|
||||
|
||||
// Make message
|
||||
std::shared_ptr<icsneo::SPIMessage> msg = std::make_shared<icsneo::SPIMessage>();
|
||||
msg->network = icsneo::Network::NetID::SPI_01;
|
||||
msg->direction = icsneo::SPIMessage::Direction::Read;
|
||||
msg->address = address;
|
||||
msg->payload.resize(numWords, 0); // Resize payload to desired length
|
||||
|
||||
// Add callback
|
||||
bool receivedMessage = false;
|
||||
auto handle = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> msg) {
|
||||
if(receivedMessage) {
|
||||
return;
|
||||
}
|
||||
if(msg->type == icsneo::Message::Type::Frame) {
|
||||
auto frame = std::dynamic_pointer_cast<icsneo::Frame>(msg);
|
||||
if(!frame) {
|
||||
return;
|
||||
}
|
||||
if(frame->network == icsneo::Network::NetID::SPI_01) {
|
||||
auto spiMsg = std::dynamic_pointer_cast<icsneo::SPIMessage>(frame);
|
||||
if(spiMsg->address == address) {
|
||||
std::cout << "Received " << spiMsg->payload.size() << " words" << std::endl;
|
||||
for(uint32_t word : spiMsg->payload) {
|
||||
std::cout << std::setfill('0') << std::setw(8) << std::right << std::hex << word << ' ';
|
||||
}
|
||||
std::cout << std::endl;
|
||||
receivedMessage = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
device->transmit(msg);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
||||
|
||||
if(!receivedMessage) {
|
||||
std::cout << "Did not receive response from device." << std::endl;
|
||||
}
|
||||
device->removeMessageCallback(handle);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -56,6 +56,7 @@ enum class ExtendedCommand : uint16_t {
|
|||
GetComponentVersions = 0x001A,
|
||||
Reboot = 0x001C,
|
||||
SetRootFSEntryFlags = 0x0027,
|
||||
TransmitCoreminiMessage = 0x0028,
|
||||
GenericBinaryInfo = 0x0030,
|
||||
LiveData = 0x0035,
|
||||
RequestTC10Wake = 0x003D,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef __SPIMESSAGE_H_
|
||||
#define __SPIMESSAGE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/communication/message/message.h"
|
||||
#include <vector>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class SPIMessage : public Frame {
|
||||
public:
|
||||
enum class Direction : uint8_t {
|
||||
Read = 0,
|
||||
Write = 1
|
||||
};
|
||||
|
||||
Direction direction = Direction::Read;
|
||||
uint16_t address = static_cast<uint16_t>(0x0000u);
|
||||
uint8_t mms = static_cast<uint16_t>(0x0000u); // Memory Map Selector for ADI MAC Phy
|
||||
uint16_t stats = static_cast<uint16_t>(0x0000u);
|
||||
|
||||
std::vector<uint32_t> payload;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef __SPIPACKET_H__
|
||||
#define __SPIPACKET_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include "icsneo/communication/message/spimessage.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
typedef uint16_t icscm_bitfield;
|
||||
|
||||
#pragma pack(push, 2)
|
||||
struct HardwareSPIPacket {
|
||||
|
||||
static std::shared_ptr<Message> DecodeToMessage(const std::vector<uint8_t>& bytestream);
|
||||
static bool EncodeFromMessage(const SPIMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report);
|
||||
|
||||
struct {
|
||||
uint16_t frameLength;
|
||||
} header;
|
||||
uint8_t offset[12];
|
||||
uint16_t stats;
|
||||
struct {
|
||||
uint64_t TS : 60;
|
||||
uint64_t : 3; // Reserved for future status bits
|
||||
uint64_t IsExtended : 1;
|
||||
} timestamp;
|
||||
uint16_t networkID;
|
||||
uint16_t length;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
Loading…
Reference in New Issue