MDIO: Network support

pull/56/head
Bryant Jones 2023-05-09 18:14:36 +00:00
parent 0de23f2817
commit f5096b879c
21 changed files with 843 additions and 12 deletions

3
.gitignore vendored
View File

@ -11,3 +11,6 @@ third-party/concurrentqueue/tests
.vs .vs
.cache .cache
*.wav *.wav
*.orig
examples/csharp/bin
examples/csharp/obj

View File

@ -245,6 +245,7 @@ set(SRC_FILES
communication/packet/wivicommandpacket.cpp communication/packet/wivicommandpacket.cpp
communication/packet/i2cpacket.cpp communication/packet/i2cpacket.cpp
communication/packet/linpacket.cpp communication/packet/linpacket.cpp
communication/packet/mdiopacket.cpp
communication/packet/scriptstatuspacket.cpp communication/packet/scriptstatuspacket.cpp
communication/packet/componentversionpacket.cpp communication/packet/componentversionpacket.cpp
communication/packet/supportedfeaturespacket.cpp communication/packet/supportedfeaturespacket.cpp
@ -469,6 +470,7 @@ if(LIBICSNEO_BUILD_TESTS)
test/i2cencoderdecodertest.cpp test/i2cencoderdecodertest.cpp
test/linencoderdecodertest.cpp test/linencoderdecodertest.cpp
test/a2bencoderdecodertest.cpp test/a2bencoderdecodertest.cpp
test/mdioencoderdecodertest.cpp
) )
target_link_libraries(libicsneo-tests gtest gtest_main) target_link_libraries(libicsneo-tests gtest gtest_main)

View File

@ -12,6 +12,7 @@
#include "icsneo/communication/message/flexray/control/flexraycontrolmessage.h" #include "icsneo/communication/message/flexray/control/flexraycontrolmessage.h"
#include "icsneo/communication/message/i2cmessage.h" #include "icsneo/communication/message/i2cmessage.h"
#include "icsneo/communication/message/linmessage.h" #include "icsneo/communication/message/linmessage.h"
#include "icsneo/communication/message/mdiomessage.h"
#include "icsneo/communication/command.h" #include "icsneo/communication/command.h"
#include "icsneo/device/device.h" #include "icsneo/device/device.h"
#include "icsneo/communication/packet/canpacket.h" #include "icsneo/communication/packet/canpacket.h"
@ -28,6 +29,7 @@
#include "icsneo/communication/packet/linpacket.h" #include "icsneo/communication/packet/linpacket.h"
#include "icsneo/communication/packet/componentversionpacket.h" #include "icsneo/communication/packet/componentversionpacket.h"
#include "icsneo/communication/packet/supportedfeaturespacket.h" #include "icsneo/communication/packet/supportedfeaturespacket.h"
#include "icsneo/communication/packet/mdiopacket.h"
#include <iostream> #include <iostream>
using namespace icsneo; using namespace icsneo;
@ -166,6 +168,18 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
msg.network = packet->network; msg.network = packet->network;
return true; return true;
} }
case Network::Type::MDIO: {
result = HardwareMDIOPacket::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
}
MDIOMessage& msg = *static_cast<MDIOMessage*>(result.get());
msg.network = packet->network;
return true;
}
case Network::Type::Internal: { case Network::Type::Internal: {
switch(packet->network.getNetID()) { switch(packet->network.getNetID()) {
case Network::NetID::Reset_Status: { case Network::NetID::Reset_Status: {

View File

@ -10,6 +10,7 @@
#include "icsneo/communication/message/i2cmessage.h" #include "icsneo/communication/message/i2cmessage.h"
#include "icsneo/communication/packet/a2bpacket.h" #include "icsneo/communication/packet/a2bpacket.h"
#include "icsneo/communication/packet/linpacket.h" #include "icsneo/communication/packet/linpacket.h"
#include "icsneo/communication/packet/mdiopacket.h"
using namespace icsneo; using namespace icsneo;
@ -108,6 +109,18 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
} }
break; break;
} // End of Network::Type::LIN } // End of Network::Type::LIN
case Network::Type::MDIO: {
auto mdiomsg = std::dynamic_pointer_cast<MDIOMessage>(message);
if(!mdiomsg) {
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
return false;
}
buffer = &result;
if(!HardwareMDIOPacket::EncodeFromMessage(*mdiomsg, result, report)) {
return false;
}
break;
} // End of Network::Type::MDIO
default: default:
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error); report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
return false; return false;

View File

@ -0,0 +1,79 @@
#include "icsneo/communication/packet/mdiopacket.h"
namespace icsneo
{
const size_t HardwareMDIOPacket::mdioDataSize = 2;
std::shared_ptr<Message> HardwareMDIOPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream)
{
auto msg = std::make_shared<MDIOMessage>();
const HardwareMDIOPacket* packet = reinterpret_cast<const HardwareMDIOPacket*>(bytestream.data());
if((sizeof(HardwareMDIOPacket) != (bytestream.size())) || (packet->length != 0))
{
return nullptr;
}
msg->network = Network::GetNetIDFromCoreMiniNetwork(static_cast<Network::CoreMini>(packet->networkID));
msg->clause = static_cast<MDIOMessage::Clause>(packet->header.ST);
msg->direction = static_cast<MDIOMessage::Direction>((packet->header.OP & 0x2) ? 1 : 0);
msg->isTXMsg = static_cast<bool>(packet->header.TRANSMIT & 0x01u);
msg->phyAddress = (packet->header.PHY_ADDR & 0x1Fu);
if (msg->clause == MDIOMessage::Clause::Clause45)
{ // 16-bit register address
msg->devAddress = (packet->header.C45_DEVTYPE & 0x1Fu);
msg->regAddress = (packet->header.REG_ADDR & 0xFFFFu);
}
else
{ // 5-bit register address
msg->devAddress = 0;
msg->regAddress = (packet->header.REG_ADDR & 0x1Fu);
}
msg->isTXMsg = static_cast<bool>(packet->header.TRANSMIT & 0x01u);
msg->txTimeout = static_cast<bool>(packet->header.ERR_TIMEOUT & 0x01u);
msg->txAborted = static_cast<bool>(packet->header.ERR_JOB_CANCELLED & 0x01u);
msg->txInvalidBus = static_cast<bool>(packet->header.ERR_INVALID_BUS & 0x01u);
msg->txInvalidPhyAddr = static_cast<bool>(packet->header.ERR_INVALID_PHYADDR & 0x01u);
msg->txInvalidRegAddr = static_cast<bool>(packet->header.ERR_INVALID_REGADDR & 0x01u);
msg->txInvalidClause = static_cast<bool>(packet->header.ERR_UNSUPPORTED_CLAUSE & 0x01u);
msg->txInvalidOpcode = static_cast<bool>(packet->header.ERR_UNSUPPORTED_OPCODE & 0x01u);
//We don't care about 0xTRB0Dx in this case...
//copy 0xTRB0STAT even though we likely won't use it either
msg->description = packet->stats;
msg->timestamp = (packet->timestamp & (0x7FFFFFFFFFFFFFFFull));
std::copy(packet->data, packet->data + mdioDataSize, std::back_inserter(msg->data));
return msg;
}
bool HardwareMDIOPacket::EncodeFromMessage(const MDIOMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report)
{
const size_t numDataBytes = message.data.size();
if(mdioDataSize < numDataBytes)
{
report(APIEvent::Type::MDIOMessageExceedsMaxLength, APIEvent::Severity::Error);
return false;
}
uint8_t st = (message.clause == MDIOMessage::Clause::Clause45) ? 0x0 : 0x1;
uint8_t op = (message.direction == MDIOMessage::Direction::Read) ? 0x2 : 0x1;
uint8_t phyAddr = message.phyAddress & 0x1F;
uint16_t regAddr = (message.clause == MDIOMessage::Clause::Clause45) ? message.regAddress : message.regAddress & 0x1F;
uint8_t c45DevType = (message.clause == MDIOMessage::Clause::Clause45) ? message.devAddress & 0x1F : 0;
bytestream.push_back(static_cast<uint8_t>((message.description >> 8) & 0xFF)); // MSB first
bytestream.push_back(static_cast<uint8_t>(message.description & 0xFF)); // LSB
bytestream.push_back(op); // opcode
bytestream.push_back(st); // st (clause)
bytestream.push_back(phyAddr); // st (clause)
bytestream.push_back(c45DevType); // clause 45 device type
bytestream.push_back(regAddr & 0xFF); // reg addr LSB
bytestream.push_back((regAddr >> 8) & 0xFF); // reg addr MSB
std::copy(message.data.begin(), message.data.end(), std::back_inserter(bytestream));
return true;
}
}

View File

@ -6,6 +6,7 @@ option(LIBICSNEO_BUILD_CPP_INTERACTIVE_EXAMPLE "Build the command-line interacti
option(LIBICSNEO_BUILD_CPP_A2B_EXAMPLE "Build the A2B example." ON) option(LIBICSNEO_BUILD_CPP_A2B_EXAMPLE "Build the A2B example." ON)
option(LIBICSNEO_BUILD_CPP_LIN_EXAMPLE "Build the LIN example." ON) option(LIBICSNEO_BUILD_CPP_LIN_EXAMPLE "Build the LIN example." ON)
option(LIBICSNEO_BUILD_CPP_COREMINI_EXAMPLE "Build the Coremini example." ON) option(LIBICSNEO_BUILD_CPP_COREMINI_EXAMPLE "Build the Coremini example." ON)
option(LIBICSNEO_BUILD_CPP_MDIO_EXAMPLE "Build the MDIO example." ON)
# Disabled until we properly build these in-tree # Disabled until we properly build these in-tree
# option(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE "Build the command-line interactive C# example." OFF) # option(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE "Build the command-line interactive C# example." OFF)
@ -43,6 +44,10 @@ if(LIBICSNEO_BUILD_CPP_COREMINI_EXAMPLE)
add_subdirectory(cpp/coremini) add_subdirectory(cpp/coremini)
endif() endif()
if(LIBICSNEO_BUILD_CPP_MDIO_EXAMPLE)
add_subdirectory(cpp/mdio)
endif()
# if(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE) # if(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE)
# add_subdirectory(csharp) # add_subdirectory(csharp)
# endif() # endif()

View File

@ -0,0 +1,27 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneocpp-mdio VERSION 0.1.0)
set(CMAKE_CXX_STANDARD_REQUIRED 11)
include(GNUInstallDirs)
# Add an include directory like so if desired
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# Enable Warnings
if(MSVC)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
endif()
# Add libicsneo, usually a git submodule within your project works well
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
add_executable(libicsneocpp-mdio src/MDIOExample.cpp)
target_link_libraries(libicsneocpp-mdio icsneocpp)

View File

@ -0,0 +1,221 @@
#include <iostream>
#include <iomanip>
#include <thread>
#include <chrono>
#include "icsneo/icsneocpp.h"
#include "icsneo/communication/message/mdiomessage.h"
/* Note: This example requires MDIO 1 channels to be connected on the device, and uses RADGalaxy or RADStar2 */
char getCharInput(std::vector<char> allowed)
{
bool found = false;
std::string input;
while (!found)
{
std::cin >> input;
if (input.length() == 1)
{
for (char compare : allowed)
{
if (compare == input.c_str()[0])
{
found = true;
break;
}
}
}
if (!found)
{
std::cout << "Input did not match expected options. Please try again." << std::endl;
std::cout << "<X or x to quit>" << std::endl;
}
}
return input.c_str()[0];
}
int main()
{
// Print version
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
std::cout << "\nFinding devices... " << std::flush;
auto devices = icsneo::FindAllDevices(); // This is type std::vector<std::shared_ptr<icsneo::Device>>
// You now hold the shared_ptrs for these devices, you are considered to "own" these devices from a memory perspective
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
// List off the devices
for (auto &device : devices)
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
std::cout << std::endl;
for (auto &device : devices)
{
std::cout << "Connecting to " << device->describe() << "... ";
bool ret = device->open();
if (!ret)
{ // Failed to open
std::cout << "FAIL" << std::endl;
std::cout << icsneo::GetLastError() << std::endl
<< std::endl;
continue;
}
std::cout << "OK" << std::endl;
// The concept of going "online" tells the connected device to start listening, i.e. ACKing traffic and giving it to us
std::cout << "\tGoing online... ";
ret = device->goOnline();
if (!ret)
{
std::cout << "FAIL" << std::endl;
device->close();
continue;
}
std::cout << "OK" << std::endl;
// A real application would just check the result of icsneo_goOnline() rather than calling this
// This function is intended to be called later on if needed
std::cout << "\tChecking online status... ";
ret = device->isOnline();
if (!ret)
{
std::cout << "FAIL\n"
<< std::endl;
device->close();
continue;
}
std::cout << "OK" << std::endl;
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> message)
{
if(icsneo::Message::Type::Frame == message->type) {
auto frame = std::static_pointer_cast<icsneo::Frame>(message);
if(icsneo::Network::Type::MDIO == frame->network.getType()) {
auto msg = std::static_pointer_cast<icsneo::MDIOMessage>(message);
std::cout << msg->network << " " << ((msg->isTXMsg)? "TX" : "RX") << " frame\n";
std::cout << "Clause: " << ((msg->clause == icsneo::MDIOMessage::Clause::Clause22) ? "22" : "45") << "\n";
std::cout << "Direction: " << ((msg->direction == icsneo::MDIOMessage::Direction::Read) ? "Read" : "Write") << "\n";
std::cout << std::hex << "PHY Address: 0x" << static_cast<int>(msg->phyAddress) << "\n";
if(msg->clause == icsneo::MDIOMessage::Clause::Clause45)
std::cout << std::hex << "Dev Address: 0x" << static_cast<int>(msg->devAddress) << "\n";
std::cout << std::hex << "Reg Address: 0x" << static_cast<int>(msg->regAddress) << "\n";
std::cout << "Data: \n";
for(uint8_t& each : msg->data) {
std::cout << std::hex << "0x" << static_cast<int>(each) << " ";
}
std::cout << "\n";
}
} }));
/*
* BCM89810 on RADGalaxy/RADGigastar
*/
// We can transmit messages to read the PHY ID of BCM89810 PHY
std::cout << "\tTransmitting a MDIO request to read ID on BCM89810...\n";
auto mdio_r = std::make_shared<icsneo::MDIOMessage>();
mdio_r->network = icsneo::Network::NetID::MDIO1;
mdio_r->phyAddress = 0x00u;
mdio_r->regAddress = 0x02u;
mdio_r->direction = icsneo::MDIOMessage::Direction::Read;
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause22;
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
std::cout << (ret ? "OK" : "FAIL") << std::endl;
// We can transmit messages to write to arbitrary register
std::cout << "\tTransmitting a MDIO request to write register on BCM89810...\n";
mdio_r = std::make_shared<icsneo::MDIOMessage>();
mdio_r->network = icsneo::Network::NetID::MDIO1;
mdio_r->phyAddress = 0x00u;
mdio_r->regAddress = 0x1Bu;
mdio_r->data = {0xAA, 0xAF};
mdio_r->direction = icsneo::MDIOMessage::Direction::Write;
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause22;
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
std::cout << (ret ? "OK" : "FAIL") << std::endl;
// We can transmit messages to read back to arbitrary register
std::cout << "\tTransmitting a MDIO request to read register on BCM89810...\n";
mdio_r = std::make_shared<icsneo::MDIOMessage>();
mdio_r->network = icsneo::Network::NetID::MDIO1;
mdio_r->phyAddress = 0x00u;
mdio_r->regAddress = 0x1Bu;
mdio_r->direction = icsneo::MDIOMessage::Direction::Read;
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause22;
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
std::cout << (ret ? "OK" : "FAIL") << std::endl;
/*
* 88Q2112 on RADGigastar, RADSupermoon, RADMoon2
*/
// We can transmit messages to read the PHY ID of BCM89810 PHY
std::cout << "\tTransmitting a MDIO request to read ID on 88Q2112...\n";
mdio_r = std::make_shared<icsneo::MDIOMessage>();
mdio_r->network = icsneo::Network::NetID::MDIO1;
mdio_r->phyAddress = 0x06u;
mdio_r->devAddress = 0x01u;
mdio_r->regAddress = 0x0002u;
mdio_r->direction = icsneo::MDIOMessage::Direction::Read;
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause45;
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
std::cout << (ret ? "OK" : "FAIL") << std::endl;
// We can transmit messages to write to arbitrary register
std::cout << "\tTransmitting a MDIO request to write register on 88Q2112...\n";
mdio_r = std::make_shared<icsneo::MDIOMessage>();
mdio_r->network = icsneo::Network::NetID::MDIO1;
mdio_r->phyAddress = 0x06u;
mdio_r->devAddress = 0x01u;
mdio_r->regAddress = 0x0902u;
mdio_r->data = {0xA3, 0x02};
mdio_r->direction = icsneo::MDIOMessage::Direction::Write;
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause45;
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
std::cout << (ret ? "OK" : "FAIL") << std::endl;
// We can transmit messages to read back to arbitrary register
std::cout << "\tTransmitting a MDIO request to read register on 88Q2112...\n";
mdio_r = std::make_shared<icsneo::MDIOMessage>();
mdio_r->network = icsneo::Network::NetID::MDIO1;
mdio_r->phyAddress = 0x06u;
mdio_r->devAddress = 0x01u;
mdio_r->regAddress = 0x0902u;
mdio_r->direction = icsneo::MDIOMessage::Direction::Read;
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause45;
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "<X or x to quit>\n\n";
// Go offline, stop sending and receiving traffic
auto shutdown = [&]()
{
device->removeMessageCallback(handler);
std::cout << "\tGoing offline... ";
ret = device->goOffline();
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "\tDisconnecting... ";
ret = device->close();
std::cout << (ret ? "OK\n" : "FAIL\n") << std::endl;
};
while (true)
{
char input = getCharInput(std::vector<char>{'X', 'x'});
switch (input)
{
case 'X':
case 'x':
shutdown();
printf("Exiting program\n");
return 0;
default:
break;
}
}
}
return 0;
}

View File

@ -111,6 +111,7 @@ public:
ErrorSettingSocketOption = 0x3107, ErrorSettingSocketOption = 0x3107,
GetIfAddrsError = 0x3108, GetIfAddrsError = 0x3108,
SendToError = 0x3109, SendToError = 0x3109,
MDIOMessageExceedsMaxLength = 0x3110,
// FTD3XX // FTD3XX
FTOK = 0x4000, // placeholder FTOK = 0x4000, // placeholder

View File

@ -0,0 +1,41 @@
#ifndef __MDIOMESSAGE_H_
#define __MDIOMESSAGE_H_
#ifdef __cplusplus
#include "icsneo/communication/message/message.h"
namespace icsneo {
class MDIOMessage : public Frame {
public:
enum class Clause : uint8_t {
Clause45 = 0,
Clause22 = 1
};
enum class Direction : uint8_t {
Write = 0,
Read = 1
};
bool isTXMsg = false;
bool txTimeout = false;
bool txAborted = false;
bool txInvalidBus = false;
bool txInvalidPhyAddr = false;
bool txInvalidRegAddr = false;
bool txInvalidClause = false;
bool txInvalidOpcode = false;
uint8_t phyAddress;
uint8_t devAddress;
uint16_t regAddress;
Direction direction;
Clause clause;
};
}
#endif // __cplusplus
#endif

View File

@ -91,6 +91,7 @@ public:
Ethernet_DAQ = 69, Ethernet_DAQ = 69,
Data_To_Host = 70, Data_To_Host = 70,
TextAPI_To_Host = 71, TextAPI_To_Host = 71,
SPI1 = 72,
OP_Ethernet6 = 73, OP_Ethernet6 = 73,
Red_VBat = 74, Red_VBat = 74,
OP_Ethernet7 = 75, OP_Ethernet7 = 75,
@ -146,7 +147,15 @@ public:
DWCAN16 = 541, DWCAN16 = 541,
LIN7 = 542, LIN7 = 542,
LIN8 = 543, LIN8 = 543,
RED_SET_RTC = 544, SPI2 = 544,
MDIO1 = 545,
MDIO2 = 546,
MDIO3 = 547,
MDIO4 = 548,
MDIO5 = 549,
MDIO6 = 550,
MDIO7 = 551,
MDIO8 = 552,
Any = 0xfffe, // Never actually set as type, but used as flag for filtering Any = 0xfffe, // Never actually set as type, but used as flag for filtering
Invalid = 0xffff Invalid = 0xffff
}; };
@ -163,6 +172,8 @@ public:
ISO9141 = 9, ISO9141 = 9,
I2C = 10, I2C = 10,
A2B = 11, A2B = 11,
SPI = 12,
MDIO = 13,
Any = 0xFE, // Never actually set as type, but used as flag for filtering Any = 0xFE, // Never actually set as type, but used as flag for filtering
Other = 0xFF Other = 0xFF
}; };
@ -239,6 +250,16 @@ public:
DWCAN16 = 69, DWCAN16 = 69,
LIN7 = 70, LIN7 = 70,
LIN8 = 71, LIN8 = 71,
SPI1 = 72,
SPI2 = 73,
MDIO1 = 75,
MDIO2 = 76,
MDIO3 = 77,
MDIO4 = 78,
MDIO5 = 79,
MDIO6 = 80,
MDIO7 = 81,
MDIO8 = 82,
}; };
static const char* GetTypeString(Type type) { static const char* GetTypeString(Type type) {
switch(type) { switch(type) {
@ -266,6 +287,10 @@ public:
return "I²C"; return "I²C";
case Type::A2B: case Type::A2B:
return "A2B"; return "A2B";
case Type::SPI:
return "SPI";
case Type::MDIO:
return "MDIO";
case Type::Invalid: case Type::Invalid:
default: default:
return "Invalid Type"; return "Invalid Type";
@ -327,7 +352,6 @@ public:
case NetID::NeoMemorySDRead: case NetID::NeoMemorySDRead:
case NetID::NeoMemoryWriteDone: case NetID::NeoMemoryWriteDone:
case NetID::RED_GET_RTC: case NetID::RED_GET_RTC:
case NetID::RED_SET_RTC:
return Type::Internal; return Type::Internal;
case NetID::Invalid: case NetID::Invalid:
case NetID::Any: case NetID::Any:
@ -368,6 +392,18 @@ public:
case NetID::A2B1: case NetID::A2B1:
case NetID::A2B2: case NetID::A2B2:
return Type::A2B; return Type::A2B;
case NetID::SPI1:
case NetID::SPI2:
return Type::SPI;
case NetID::MDIO1:
case NetID::MDIO2:
case NetID::MDIO3:
case NetID::MDIO4:
case NetID::MDIO5:
case NetID::MDIO6:
case NetID::MDIO7:
case NetID::MDIO8:
return Type::MDIO;
default: default:
return Type::Other; return Type::Other;
} }
@ -456,8 +492,6 @@ public:
return "RED_HARDWARE_EXCEP"; return "RED_HARDWARE_EXCEP";
case NetID::RED_GET_RTC: case NetID::RED_GET_RTC:
return "RED_GET_RTC"; return "RED_GET_RTC";
case NetID::RED_SET_RTC:
return "RED_SET_RTC";
case NetID::ISO9141_3: case NetID::ISO9141_3:
return "ISO 9141 3"; return "ISO 9141 3";
case NetID::HSCAN2: case NetID::HSCAN2:
@ -626,6 +660,26 @@ public:
return "LIN 08"; return "LIN 08";
case NetID::WBMS: case NetID::WBMS:
return "WBMS"; return "WBMS";
case NetID::SPI1:
return "SPI 1";
case NetID::SPI2:
return "SPI 2";
case NetID::MDIO1:
return "MDIO 1";
case NetID::MDIO2:
return "MDIO 2";
case NetID::MDIO3:
return "MDIO 3";
case NetID::MDIO4:
return "MDIO 4";
case NetID::MDIO5:
return "MDIO 5";
case NetID::MDIO6:
return "MDIO 6";
case NetID::MDIO7:
return "MDIO 7";
case NetID::MDIO8:
return "MDIO 8";
case NetID::Any: case NetID::Any:
case NetID::Invalid: case NetID::Invalid:
break; break;
@ -778,6 +832,26 @@ public:
return CoreMini::LIN7; return CoreMini::LIN7;
case NetID::LIN8: case NetID::LIN8:
return CoreMini::LIN8; return CoreMini::LIN8;
case NetID::SPI1:
return CoreMini::SPI1;
case NetID::SPI2:
return CoreMini::SPI2;
case NetID::MDIO1:
return CoreMini::MDIO1;
case NetID::MDIO2:
return CoreMini::MDIO2;
case NetID::MDIO3:
return CoreMini::MDIO3;
case NetID::MDIO4:
return CoreMini::MDIO4;
case NetID::MDIO5:
return CoreMini::MDIO5;
case NetID::MDIO6:
return CoreMini::MDIO6;
case NetID::MDIO7:
return CoreMini::MDIO7;
case NetID::MDIO8:
return CoreMini::MDIO8;
default: default:
return std::nullopt; return std::nullopt;
} }
@ -928,6 +1002,27 @@ public:
return NetID::LIN7; return NetID::LIN7;
case CoreMini::LIN8: case CoreMini::LIN8:
return NetID::LIN8; return NetID::LIN8;
case CoreMini::SPI1:
return NetID::SPI1;
case CoreMini::SPI2:
return NetID::SPI2;
case CoreMini::MDIO1:
return NetID::MDIO1;
case CoreMini::MDIO2:
return NetID::MDIO2;
case CoreMini::MDIO3:
return NetID::MDIO3;
case CoreMini::MDIO4:
return NetID::MDIO4;
case CoreMini::MDIO5:
return NetID::MDIO5;
case CoreMini::MDIO6:
return NetID::MDIO6;
case CoreMini::MDIO7:
return NetID::MDIO7;
case CoreMini::MDIO8:
return NetID::MDIO8;
} }
return NetID::Invalid; // Should be unreachable, the compiler should warn about new CoreMini IDs return NetID::Invalid; // Should be unreachable, the compiler should warn about new CoreMini IDs
} }

View File

@ -0,0 +1,61 @@
#ifndef __MDIOPACKET_H__
#define __MDIOPACKET_H__
#ifdef __cplusplus
#include "icsneo/communication/message/mdiomessage.h"
#include "icsneo/api/eventmanager.h"
#include "icsneo/communication/packetizer.h"
#include "icsneo/communication/network.h"
#include <cstdint>
#include <memory>
namespace icsneo {
#pragma pack(push, 2)
struct HardwareMDIOPacket {
static std::shared_ptr<Message> DecodeToMessage(const std::vector<uint8_t>& bytestream);
static bool EncodeFromMessage(const MDIOMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report);
struct {
// C1xMDIO
uint16_t PHY_ADDR : 5; // mdio phy address, 5-bit
uint16_t ST : 2; // Start of Frame (Clause 22/45)
uint16_t OP : 2; // OP Code (Read/Write)
uint16_t ADVANCED_MODE : 1; // future?
uint16_t TRANSMIT : 1; // This is a TX message
uint16_t C45_DEVTYPE : 5;
// C2xMDIO
uint16_t ERR_JOB_CANCELLED : 1; // error flag
uint16_t ERR_TIMEOUT : 1; // error flag
uint16_t ERR_INVALID_BUS : 1; // error flag
uint16_t ERR_INVALID_PHYADDR : 1; // error flag
uint16_t ERR_INVALID_REGADDR : 1; // error flag
uint16_t ERR_UNSUPPORTED_CLAUSE : 1; // error flag
uint16_t ERR_UNSUPPORTED_OPCODE : 1; // error flag
uint16_t ERR_OVERFLOW : 1; // error flag
uint16_t : 8;
// C3xMDIO
uint16_t REG_ADDR;
} header;
uint8_t data[8];
uint16_t stats;
uint64_t timestamp;
uint16_t networkID;
uint16_t length;
static const size_t mdioDataSize;
};
#pragma pack(pop)
}
#endif // __cplusplus
#endif

View File

@ -32,7 +32,9 @@ public:
Network::NetID::A2B2, Network::NetID::A2B2,
Network::NetID::I2C, Network::NetID::I2C,
Network::NetID::I2C2 Network::NetID::I2C2,
Network::NetID::MDIO1,
}; };
return supportedNetworks; return supportedNetworks;
} }

View File

@ -47,7 +47,13 @@ public:
Network::NetID::OP_Ethernet9, Network::NetID::OP_Ethernet9,
Network::NetID::OP_Ethernet10, Network::NetID::OP_Ethernet10,
Network::NetID::OP_Ethernet11, Network::NetID::OP_Ethernet11,
Network::NetID::OP_Ethernet12 Network::NetID::OP_Ethernet12,
Network::NetID::MDIO1,
Network::NetID::MDIO2,
Network::NetID::MDIO3,
Network::NetID::MDIO4,
Network::NetID::MDIO5,
}; };
return supportedNetworks; return supportedNetworks;
} }

View File

@ -65,6 +65,9 @@ protected:
Network::NetID::I2C, Network::NetID::I2C,
Network::NetID::I2C2, Network::NetID::I2C2,
Network::NetID::I2C3, Network::NetID::I2C3,
Network::NetID::MDIO1,
Network::NetID::MDIO2,
}; };
rxNetworks.insert(rxNetworks.end(), supportedRxNetworks.begin(), supportedRxNetworks.end()); rxNetworks.insert(rxNetworks.end(), supportedRxNetworks.begin(), supportedRxNetworks.end());
} }
@ -91,6 +94,9 @@ protected:
Network::NetID::I2C, Network::NetID::I2C,
Network::NetID::I2C2, Network::NetID::I2C2,
Network::NetID::I2C3, Network::NetID::I2C3,
Network::NetID::MDIO1,
Network::NetID::MDIO2,
}; };
txNetworks.insert(txNetworks.end(), supportedTxNetworks.begin(), supportedTxNetworks.end()); txNetworks.insert(txNetworks.end(), supportedTxNetworks.begin(), supportedTxNetworks.end());
} }

View File

@ -60,6 +60,9 @@ protected:
Network::NetID::FlexRay1b, Network::NetID::FlexRay1b,
Network::NetID::I2C, Network::NetID::I2C,
Network::NetID::MDIO1,
Network::NetID::MDIO2,
}; };
rxNetworks.insert(rxNetworks.end(), supportedRxNetworks.begin(), supportedRxNetworks.end()); rxNetworks.insert(rxNetworks.end(), supportedRxNetworks.begin(), supportedRxNetworks.end());
} }
@ -84,6 +87,9 @@ protected:
// FlexRay is Receive Only // FlexRay is Receive Only
Network::NetID::I2C, Network::NetID::I2C,
Network::NetID::MDIO1,
Network::NetID::MDIO2,
}; };
txNetworks.insert(txNetworks.end(), supportedTxNetworks.begin(), supportedTxNetworks.end()); txNetworks.insert(txNetworks.end(), supportedTxNetworks.begin(), supportedTxNetworks.end());
} }

View File

@ -16,6 +16,14 @@ public:
APM1000E_CLK, // Clock Option and Keysight Branding APM1000E_CLK, // Clock Option and Keysight Branding
}; };
static const std::vector<Network>& GetSupportedNetworks() {
static std::vector<Network> supportedNetworks = {
Network::NetID::MDIO1,
Network::NetID::MDIO2,
};
return supportedNetworks;
}
SKU getSKU() const { SKU getSKU() const {
switch(getSerial().back()) { switch(getSerial().back()) {
case 'A': case 'A':
@ -66,6 +74,14 @@ protected:
bool requiresVehiclePower() const override { return false; } bool requiresVehiclePower() const override { return false; }
void setupSupportedRXNetworks(std::vector<Network>& rxNetworks) override {
for(auto& netid : GetSupportedNetworks())
rxNetworks.emplace_back(netid);
}
// The supported TX networks are the same as the supported RX networks for this device
void setupSupportedTXNetworks(std::vector<Network>& txNetworks) override { setupSupportedRXNetworks(txNetworks); }
std::optional<MemoryAddress> getCoreminiStartAddressFlash() const override { std::optional<MemoryAddress> getCoreminiStartAddressFlash() const override {
return std::nullopt; return std::nullopt;
} }

View File

@ -24,7 +24,10 @@ public:
Network::NetID::Ethernet, Network::NetID::Ethernet,
Network::NetID::OP_Ethernet1, Network::NetID::OP_Ethernet1,
Network::NetID::OP_Ethernet2 Network::NetID::OP_Ethernet2,
Network::NetID::MDIO1,
Network::NetID::MDIO2,
}; };
return supportedNetworks; return supportedNetworks;
} }

View File

@ -24,6 +24,8 @@ public:
static std::vector<Network> supportedNetworks = { static std::vector<Network> supportedNetworks = {
Network::NetID::Ethernet, Network::NetID::Ethernet,
Network::NetID::OP_Ethernet1, Network::NetID::OP_Ethernet1,
Network::NetID::MDIO1,
Network::NetID::MDIO2,
}; };
return supportedNetworks; return supportedNetworks;
} }

View File

@ -19,6 +19,7 @@
#include "icsneo/communication/message/i2cmessage.h" #include "icsneo/communication/message/i2cmessage.h"
#include "icsneo/communication/message/a2bmessage.h" #include "icsneo/communication/message/a2bmessage.h"
#include "icsneo/communication/message/linmessage.h" #include "icsneo/communication/message/linmessage.h"
#include "icsneo/communication/message/mdiomessage.h"
#include "icsneo/communication/message/callback/streamoutput/a2bwavoutput.h" #include "icsneo/communication/message/callback/streamoutput/a2bwavoutput.h"

View File

@ -0,0 +1,227 @@
#include "icsneo/icsneocpp.h"
#include "icsneo/communication/encoder.h"
#include "icsneo/communication/packet/mdiopacket.h"
#include "icsneo/communication/message/mdiomessage.h"
#include "icsneo/communication/packetizer.h"
#include "icsneo/api/eventmanager.h"
#include "gtest/gtest.h"
#include <vector>
using namespace icsneo;
class MDIOEncoderDecoderTest : public ::testing::Test {
protected:
void SetUp() override {
report = [](APIEvent::Type, APIEvent::Severity) {
// Unless caught by the test, the packetizer should not throw errors
EXPECT_TRUE(false);
};
packetizer.emplace([this](APIEvent::Type t, APIEvent::Severity s) {
report(t, s);
});
packetEncoder.emplace([this](APIEvent::Type t, APIEvent::Severity s) {
report(t, s);
});
packetDecoder.emplace([this](APIEvent::Type t, APIEvent::Severity s) {
report(t, s);
});
}
device_eventhandler_t report;
std::optional<Encoder> packetEncoder;
std::optional<Packetizer> packetizer;
std::optional<Decoder> packetDecoder;
std::vector<uint8_t> testBytesClause22 =
{0xAA, 0x0C, 0x11, 0x00, 0x21, 0x02, 0xAB, 0xCD,
0x01, 0x01, 0x18, 0x00, 0x14, 0x00, 0x56, 0x78};
std::vector<uint8_t> testBytesClause45 =
{0xAA, 0x0C, 0x11, 0x00, 0x21, 0x02, 0xAB, 0xCD,
0x02, 0x00, 0x06, 0x14, 0x34, 0x12, 0x56, 0x78};
std::vector<uint8_t> testBytesClause22Mask =
{0xAA, 0x0C, 0x11, 0x00, 0x21, 0x02, 0xFF, 0xFF,
0x01, 0x01, 0x1F, 0x00, 0x1F, 0x00, 0xFF, 0xFF};
std::vector<uint8_t> testBytesClause45Mask =
{0xAA, 0x0C, 0x11, 0x00, 0x21, 0x02, 0xFF, 0xFF,
0x02, 0x00, 0x1F, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF};
std::vector<uint8_t> recvBytesClause22 =
{
0xaa, 0x0c, // header
0x22, 0x00, // length
0x21, 0x02, // hw netid
0x26, 0x0D, // word1
0x00, 0x00, // word2
0x14, 0x00, // word3
0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // data[8]
0xCD, 0xAB, // stats
0x97, 0x29, 0xe6, 0xfb, 0xc1, 0xfc, 0xb0, 0x80, // timestamp
0x4A, 0x00, // netid
0x00, 0x00, // length
};
std::vector<uint8_t> recvBytesClause45 =
{
0xaa, 0x0c, // header
0x22, 0x00, // length
0x21, 0x02, // hw netid
0x92, 0x1C, // word1
0xFF, 0x00, // word2
0x56, 0x14, // word3
0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // data[8]
0xCD, 0xAB, // stats
0x97, 0x29, 0xe6, 0xfb, 0xc1, 0xfc, 0xb0, 0x80, // timestamp
0x4A, 0x00, // netid
0x00, 0x00, // length
};
};
TEST_F(MDIOEncoderDecoderTest, PacketEncoderClause22Test) {
std::vector<uint8_t> bytestream;
auto message = std::make_shared<icsneo::MDIOMessage>();
message->network = icsneo::Network::NetID::MDIO1;
message->description = 0xABCD;
message->phyAddress = 0x18u;
message->devAddress = 0x13u;
message->regAddress = 0x14u;
message->data = {0x56u, 0x78u};
message->direction = MDIOMessage::Direction::Write;
message->clause = MDIOMessage::Clause::Clause22;
message->isTXMsg = true;
packetEncoder->encode(*packetizer, bytestream, message);
EXPECT_EQ(bytestream, testBytesClause22);
}
TEST_F(MDIOEncoderDecoderTest, PacketEncoderClause45Test) {
std::vector<uint8_t> bytestream;
auto message = std::make_shared<icsneo::MDIOMessage>();
message->network = icsneo::Network::NetID::MDIO1;
message->description = 0xABCD;
message->phyAddress = 0x06u;
message->devAddress = 0x14u;
message->regAddress = 0x1234u;
message->data = {0x56u, 0x78u};
message->direction = MDIOMessage::Direction::Read;
message->clause = MDIOMessage::Clause::Clause45;
message->isTXMsg = true;
packetEncoder->encode(*packetizer, bytestream, message);
EXPECT_EQ(bytestream, testBytesClause45);
}
TEST_F(MDIOEncoderDecoderTest, PacketEncoderClause22MaskTest) {
std::vector<uint8_t> bytestream;
auto message = std::make_shared<icsneo::MDIOMessage>();
message->network = icsneo::Network::NetID::MDIO1;
message->description = 0xFFFFu;
message->phyAddress = 0xFFu;
message->devAddress = 0xFFu;
message->regAddress = 0xFFFFu;
message->data = {0xFFu, 0xFFu};
message->direction = MDIOMessage::Direction::Write;
message->clause = MDIOMessage::Clause::Clause22;
message->isTXMsg = true;
packetEncoder->encode(*packetizer, bytestream, message);
EXPECT_EQ(bytestream, testBytesClause22Mask);
}
TEST_F(MDIOEncoderDecoderTest, PacketEncoderClause45MaskTest) {
std::vector<uint8_t> bytestream;
auto message = std::make_shared<icsneo::MDIOMessage>();
message->network = icsneo::Network::NetID::MDIO1;
message->description = 0xFFFFu;
message->phyAddress = 0xFFu;
message->devAddress = 0xFFu;
message->regAddress = 0xFFFFu;
message->data = {0xFFu, 0xFFu};
message->direction = MDIOMessage::Direction::Read;
message->clause = MDIOMessage::Clause::Clause45;
message->isTXMsg = true;
packetEncoder->encode(*packetizer, bytestream, message);
EXPECT_EQ(bytestream, testBytesClause45Mask);
}
TEST_F(MDIOEncoderDecoderTest, PacketDecoderClause22Test) {
std::shared_ptr<icsneo::Message> decodeMsg;
std::shared_ptr<icsneo::MDIOMessage> message = std::make_shared<icsneo::MDIOMessage>();
message->network = icsneo::Network::NetID::MDIO1;
message->description = 0xABCD;
message->phyAddress = 0x06u;
message->devAddress = 0x00u;
message->regAddress = 0x14u;
message->data = {0x56u, 0x78u};
message->direction = MDIOMessage::Direction::Read;
message->clause = MDIOMessage::Clause::Clause22;
message->isTXMsg = true;
message->timestamp = static_cast<uint64_t>(0xB0FCC1FBE62997);
EXPECT_TRUE(packetizer->input(recvBytesClause22));
auto packets = packetizer->output();
EXPECT_FALSE(packets.empty());
EXPECT_TRUE(packetDecoder->decode(decodeMsg, packets.back()));
auto testMessage = std::dynamic_pointer_cast<icsneo::MDIOMessage>(decodeMsg);
EXPECT_EQ(message->network, testMessage->network);
EXPECT_EQ(message->description, testMessage->description);
EXPECT_EQ(message->phyAddress, testMessage->phyAddress);
EXPECT_EQ(message->devAddress, testMessage->devAddress);
EXPECT_EQ(message->regAddress, testMessage->regAddress);
EXPECT_EQ(message->data, testMessage->data);
EXPECT_EQ(message->direction, testMessage->direction);
EXPECT_EQ(message->clause, testMessage->clause);
EXPECT_EQ(message->isTXMsg, testMessage->isTXMsg);
EXPECT_EQ(message->txTimeout, testMessage->txTimeout);
EXPECT_EQ(message->txAborted, testMessage->txAborted);
EXPECT_EQ(message->txInvalidBus, testMessage->txInvalidBus);
EXPECT_EQ(message->txInvalidPhyAddr, testMessage->txInvalidPhyAddr);
EXPECT_EQ(message->txInvalidRegAddr, testMessage->txInvalidRegAddr);
EXPECT_EQ(message->txInvalidClause, testMessage->txInvalidClause);
EXPECT_EQ(message->txInvalidOpcode, testMessage->txInvalidOpcode);
EXPECT_EQ(message->timestamp, testMessage->timestamp);
}
TEST_F(MDIOEncoderDecoderTest, PacketDecoderClause45Test) {
std::shared_ptr<icsneo::Message> decodeMsg;
std::shared_ptr<icsneo::MDIOMessage> message = std::make_shared<icsneo::MDIOMessage>();
message->network = icsneo::Network::NetID::MDIO1;
message->description = 0xABCD;
message->phyAddress = 0x12u;
message->devAddress = 0x03u;
message->regAddress = 0x1456u;
message->data = {0x56u, 0x78u};
message->direction = MDIOMessage::Direction::Write;
message->clause = MDIOMessage::Clause::Clause45;
message->isTXMsg = true;
message->txTimeout = true;
message->txAborted = true;
message->txInvalidBus = true;
message->txInvalidPhyAddr = true;
message->txInvalidRegAddr = true;
message->txInvalidClause = true;
message->txInvalidOpcode = true;
message->timestamp = static_cast<uint64_t>(0xB0FCC1FBE62997);
EXPECT_TRUE(packetizer->input(recvBytesClause45));
auto packets = packetizer->output();
EXPECT_FALSE(packets.empty());
EXPECT_TRUE(packetDecoder->decode(decodeMsg, packets.back()));
auto testMessage = std::dynamic_pointer_cast<icsneo::MDIOMessage>(decodeMsg);
EXPECT_EQ(message->network, testMessage->network);
EXPECT_EQ(message->description, testMessage->description);
EXPECT_EQ(message->phyAddress, testMessage->phyAddress);
EXPECT_EQ(message->devAddress, testMessage->devAddress);
EXPECT_EQ(message->regAddress, testMessage->regAddress);
EXPECT_EQ(message->data, testMessage->data);
EXPECT_EQ(message->direction, testMessage->direction);
EXPECT_EQ(message->clause, testMessage->clause);
EXPECT_EQ(message->isTXMsg, testMessage->isTXMsg);
EXPECT_EQ(message->txTimeout, testMessage->txTimeout);
EXPECT_EQ(message->txAborted, testMessage->txAborted);
EXPECT_EQ(message->txInvalidBus, testMessage->txInvalidBus);
EXPECT_EQ(message->txInvalidPhyAddr, testMessage->txInvalidPhyAddr);
EXPECT_EQ(message->txInvalidRegAddr, testMessage->txInvalidRegAddr);
EXPECT_EQ(message->txInvalidClause, testMessage->txInvalidClause);
EXPECT_EQ(message->txInvalidOpcode, testMessage->txInvalidOpcode);
EXPECT_EQ(message->timestamp, testMessage->timestamp);
}