Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: ngscopeclient/scopehal
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 619998e427dd
Choose a base ref
...
head repository: ngscopeclient/scopehal
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 5ef7254073d8
Choose a head ref
  • 2 commits
  • 2 files changed
  • 1 contributor

Commits on Oct 10, 2020

  1. MDIODecoder: Added colorization, some improvements to decoding, and m…

    …erging of MMD register transactions
    azonenberg committed Oct 10, 2020
    Copy the full SHA
    51a688e View commit details
  2. MDIODecoder: added PHY type info. Added decodes for most common KSZ90…

    …31 MMD registers plus a lot more standard registers. Fixes #82.
    azonenberg committed Oct 10, 2020
    Copy the full SHA
    5ef7254 View commit details
Showing with 324 additions and 22 deletions.
  1. +314 −22 scopeprotocols/MDIODecoder.cpp
  2. +10 −0 scopeprotocols/MDIODecoder.h
336 changes: 314 additions & 22 deletions scopeprotocols/MDIODecoder.cpp
Original file line number Diff line number Diff line change
@@ -47,6 +47,12 @@ MDIODecoder::MDIODecoder(string color)
//Set up channels
CreateInput("mdio");
CreateInput("mdc");

m_typename = "PHY Type";
m_parameters[m_typename] = FilterParameter(FilterParameter::TYPE_ENUM, Unit(Unit::UNIT_COUNTS));
m_parameters[m_typename].AddEnumValue("Generic", PHY_TYPE_GENERIC);
m_parameters[m_typename].AddEnumValue("KSZ9031", PHY_TYPE_KSZ9031);
m_parameters[m_typename].SetIntVal(PHY_TYPE_GENERIC);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -108,12 +114,18 @@ void MDIODecoder::Refresh()
auto mdio = GetDigitalInputWaveform(0);
auto mdc = GetDigitalInputWaveform(1);

int phytype = m_parameters[m_typename].GetIntVal();

//Create the capture
auto cap = new MDIOWaveform;
cap->m_timescale = 1; //SampleOnRisingEdges() gives us ps level timestamps
cap->m_startTimestamp = mdc->m_startTimestamp;
cap->m_startPicoseconds = mdc->m_startPicoseconds;

//Maintain MMD state across transactions
int mmd_dev = 0;
bool mmd_is_reg = false;

//Sample the data stream at each clock edge
DigitalWaveform dmdio;
SampleOnRisingEdges(mdio, mdc, dmdio);
@@ -201,9 +213,15 @@ void MDIODecoder::Refresh()
op |= 1;

if(op == 1)
{
pack->m_headers["Op"] = "Write";
pack->m_displayBackgroundColor = m_backgroundColors[PROTO_COLOR_DATA_WRITE];
}
else if(op == 2)
{
pack->m_headers["Op"] = "Read";
pack->m_displayBackgroundColor = m_backgroundColors[PROTO_COLOR_DATA_READ];
}
else
pack->m_headers["Op"] = "ERROR";

@@ -273,7 +291,13 @@ void MDIODecoder::Refresh()
start = dmdio.m_offsets[i];
for(size_t j=0; j<16; j++)
{
len = (dmdio.m_offsets[i+j] - start) + dmdio.m_durations[i+j];
//Use previous clock cycle's duration for last sample
//rather than stretching until next clock edge
if(j == 15)
len = (dmdio.m_offsets[i+j] - start) + dmdio.m_durations[i+j-1];
else
len = (dmdio.m_offsets[i+j] - start) + dmdio.m_durations[i+j];

value <<= 1;
if(dmdio.m_samples[i+j])
value |= 1;
@@ -294,7 +318,16 @@ void MDIODecoder::Refresh()
//802.3 Basic Control
case 0x00:
{
info = "Basic Status: ";
info = "Basic Control: ";

if(value & 0x8000)
info += "Reset ";
if(value & 0x4000)
info += "Loopback ";
if(value & 0x0400)
info += "Isolate ";
if(value & 0x0200)
info += "AnegRestart ";

uint8_t speed = 0;
if(value & 0x0040)
@@ -305,65 +338,161 @@ void MDIODecoder::Refresh()
switch(speed)
{
case 0:
info += "Speed 10M";
info += "10M";
break;

case 1:
info += "Speed 100M";
info += "100M";
break;

case 2:
info += "Speed 1G";
info += "1G";
break;

default:
info += "Speed invalid";
info += "BadSpeed";
break;
}

if( (value & 0x0100) == 0)
info += "/full";
if(value & 0x0100)
info += "/full ";
else
info += "/half";
info += "/half ";

if( (value & 0x1000) == 0)
info += ", Aneg disable";
info += "AnegDisable ";

if( (value & 0x0800) == 0)
info += ", Power down";
if(value & 0x0800)
info += "PowerDown ";
}
break;

//802.3 Basic Status
case 0x1:
info = "Basic Status: ";

if(value & 0x20)
info += "Aneg complete";
else
info += "Aneg not complete";

if(value & 0x4)
info += ", Link up";
info += "Up ";
else
info += ", Link down";
info += "Down ";
if(value & 0x20)
info += "AnegDone ";

if(value & 0x0100)
info += "ExtStatus ";
if(value & 0x01)
info += "ExtCaps ";

if(value & 0x0040)
info += "PreambleSupp ";
if(value & 0x10)
info += "RemoteFault ";
if(value & 0x08)
info += "AnegCapable ";
if(value & 0x02)
info += "JabberDetect ";

info += "PMAs: ";
if(value & 0x8000)
info += "100baseT4 ";
if(value & 0x4000)
info += "100baseTX/full ";
if(value & 0x2000)
info += "100baseTX/half ";
if(value & 0x1000)
info += "10baseT/full ";
if(value & 0x0800)
info += "10baseT/half ";

break;

//PHY ID
case 0x2:
info = "PHY ID 1";
switch(phytype)
{
case PHY_TYPE_KSZ9031:
if(value != 0x0022)
info += ": ERROR, should be 0x0022 for KSZ9031";
else
info += ": Kendin/Micrel/Microchip";
break;

default:
break;
}

break;
case 0x3:
info = "PHY ID 2";
switch(phytype)
{
case PHY_TYPE_KSZ9031:
if( ((value >> 10) & 0x3f) != 0x5)
info += ": ERROR, vendor ID should be 0x5 for KSZ9031";
else
{
if( ((value >> 4) & 0x3f) != 0x22)
info += ": ERROR, model ID should be 0x22 for KSZ9031";
else
info += string(": KSZ9031 stepping ") + to_string(value & 0xf);
}
break;

default:
break;
}
break;

//Autonegotiation
case 0x4:
info = "ANEG Advertisement";
info = "ANEG Advertisement: ";
if( (value & 0x1F) != 1)
info += "NotEthernet ";
if(value & 0x8000)
info += "NextPage ";
if(value & 0x2000)
info += "RemFltSupp ";
if(value & 0x0800)
info += "AsymPause ";
if(value & 0x0400)
info += "SymPause ";
if(value & 0x0200)
info += "100baseT4 ";
if(value & 0x0100)
info += "100baseTX/full ";
if(value & 0x0080)
info += "100baseTX/half ";
if(value & 0x0040)
info += "10baseTX/full ";
if(value & 0x0020)
info += "10baseTX/half ";
break;
case 0x5:
info = "ANEG Partner Ability";

if( (value & 0x1F) != 1)
info += "NotEthernet ";
if(value & 0x8000)
info += "NextPage ";
if(value & 0x4000)
info += "ACK ";
if(value & 0x2000)
info += "RemoteFault ";
if(value & 0x0800)
info += "AsymPause ";
if(value & 0x0400)
info += "SymPause ";
if(value & 0x0200)
info += "100baseT4 ";
if(value & 0x0100)
info += "100baseTX/full ";
if(value & 0x0080)
info += "100baseTX/half ";
if(value & 0x0040)
info += "10baseTX/full ";
if(value & 0x0020)
info += "10baseTX/half ";
break;
case 0x6:
info = "ANEG Expansion";
@@ -420,11 +549,14 @@ void MDIODecoder::Refresh()
//MMD stuff
case 0xd:
info = "MMD Access: ";
pack->m_displayBackgroundColor = m_backgroundColors[PROTO_COLOR_CONTROL];
mmd_is_reg = false;

switch(value >> 14)
{
case 0:
info += "Register";
mmd_is_reg = true;
break;

case 1:
@@ -439,14 +571,32 @@ void MDIODecoder::Refresh()
info += "Data W increment";
break;
}

mmd_dev = (value & 0x1f);
snprintf(tmp, sizeof(tmp), "%02x", mmd_dev);
info += string(", MMD device = ") + tmp;
break;

case 0xe:
info = "MMD Addr/Data";
if(mmd_is_reg)
{
info = "MMD Address";
pack->m_displayBackgroundColor = m_backgroundColors[PROTO_COLOR_CONTROL];
}
else
info = "MMD Data";
break;

case 0xf:
info = "Extended Status";
info = "Extended Status: ";
if(value & 0x8000)
info += "1000base-X/full ";
if(value & 0x4000)
info += "1000base-X/half ";
if(value & 0x2000)
info += "1000base-T/full ";
if(value & 0x1000)
info += "1000base-T/half ";
break;

//TODO: support for PHY vendor specific registers if we know the PHY ID (or are told)
@@ -570,3 +720,145 @@ string MDIODecoder::GetText(int i)

return "";
}

bool MDIODecoder::CanMerge(Packet* first, Packet* /*cur*/, Packet* next)
{
//If different PHYs, obviously can't merge
if(first->m_headers["PHY"] != next->m_headers["PHY"])
return false;

//Start merging when we get an access to the MMD address register
if( (first->m_headers["Reg"] == "0d") && (first->m_headers["Info"].find("Register") != string::npos) )
{
//Only merge accesses to 0e or 0d-with-data
if(next->m_headers["Reg"] == "0e")
return true;

if( (next->m_headers["Reg"] == "0d") && (next->m_headers["Info"].find("Data") != string::npos) )
return true;
}

return false;
}

Packet* MDIODecoder::CreateMergedHeader(Packet* pack, size_t i)
{
Packet* ret = new Packet;
ret->m_offset = pack->m_offset;
ret->m_len = pack->m_len;

//Default to copying everything from the first packet
ret->m_headers["Clause"] = pack->m_headers["Clause"];
ret->m_headers["Op"] = pack->m_headers["Op"];
ret->m_headers["PHY"] = pack->m_headers["PHY"];
ret->m_headers["Reg"] = pack->m_headers["Reg"];
ret->m_headers["Value"] = pack->m_headers["Value"];
ret->m_headers["Info"] = pack->m_headers["Info"];
ret->m_displayBackgroundColor = pack->m_displayBackgroundColor;

int phytype = m_parameters[m_typename].GetIntVal();

//Search forward until we find the actual MMD data access, then update our color/type based on that
unsigned int mmd_reg_addr = 0;
unsigned int mmd_device = 0;
unsigned int mmd_value = 0;
bool mmd_is_addr = false;
for(size_t j=i; j<m_packets.size(); j++)
{
//Check type field
auto p = m_packets[j];
unsigned int pvalue = strtol(p->m_headers["Value"].c_str(), NULL, 16);

//Decode address info
if(p->m_headers["Reg"] == "0d")
{
if(p->m_headers["Info"].find("Register") != string::npos)
mmd_is_addr = true;
else
mmd_is_addr = false;

mmd_device = pvalue & 0x1f;
}

if(p->m_headers["Reg"] == "0e")
{
if(mmd_is_addr)
mmd_reg_addr = pvalue;

//Figure out top level op type on the final data transaction
else
{
ret->m_headers["Op"] = p->m_headers["Op"];
ret->m_headers["Reg"] = p->m_headers["Reg"];
ret->m_headers["Value"] = p->m_headers["Value"];
ret->m_displayBackgroundColor = p->m_displayBackgroundColor;

mmd_value = pvalue;
break;
}
}
}

//Default for unknown PHY type or unknown register
char tmp[128];
snprintf(tmp, sizeof(tmp), "MMD %02x reg %04x = %04x", mmd_device, mmd_reg_addr, mmd_value);
string info = tmp;

switch(phytype)
{
case PHY_TYPE_KSZ9031:
switch(mmd_device)
{
case 0x00:
switch(mmd_reg_addr)
{
case 3:
info = "AN FLP Timer Lo: ";
if(mmd_value == 0x1a80)
info += "16 ms";
else if(mmd_value == 0x4000)
info += "8 ms";
else
info += "Reserved";
break;

case 4:
info = "AN FLP Timer Hi: ";
if(mmd_value == 0x3)
info += "8 ms";
else if(mmd_value == 0x6)
info += "16 ms";
else
info += "Reserved";
break;
}
break;

case 0x1:
break;

case 0x2:
break;

case 0x1c:
if(mmd_reg_addr == 0x23)
{
//EDPD Control
info = "EDPD Control: ";
if(mmd_value & 1)
info += "Enable";
else
info += "Disable";
}
break;
}
break;

default:
break;
}

ret->m_headers["Info"] = info;

return ret;
}
10 changes: 10 additions & 0 deletions scopeprotocols/MDIODecoder.h
Original file line number Diff line number Diff line change
@@ -86,13 +86,23 @@ class MDIODecoder : public PacketDecoder
static std::string GetProtocolName();
virtual void SetDefaultName();

enum PhyTypes
{
PHY_TYPE_GENERIC, //IEEE registers only
PHY_TYPE_KSZ9031
};

virtual std::vector<std::string> GetHeaders();

virtual bool CanMerge(Packet* first, Packet* cur, Packet* next);
virtual Packet* CreateMergedHeader(Packet* pack, size_t i);

virtual bool ValidateChannel(size_t i, StreamDescriptor stream);

PROTOCOL_DECODER_INITPROC(MDIODecoder)

protected:
std::string m_typename;
};

#endif