Last active
March 3, 2025 14:54
-
-
Save max-dark/d4acb76e3dc702325336e3084f11fed1 to your computer and use it in GitHub Desktop.
draft for SUMP protocol parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // See: Openbench Logic Sniffer (OLS) / SUMP protocol | |
| // https://sigrok.org/wiki/SUMP_compatibles | |
| // https://sigrok.org/wiki/Openbench_Logic_Sniffer | |
| // https://sigrok.org/gitweb/?p=libsigrok.git;a=blob_plain;f=src/hardware/openbench-logic-sniffer/protocol.h;hb=HEAD | |
| // https://firmware.buspirate.com/binmode-reference/protocol-sump | |
| // https://web.archive.org/web/20190317154112if_/http://mygizmos.org/ols/Logic-Sniffer-FPGA-Spec.pdf | |
| #include <QCoreApplication> | |
| #include <QDebug> | |
| #include <QSerialPort> | |
| #include <QSerialPortInfo> | |
| #include <QTimer> | |
| int main(int argc, char *argv[]) | |
| { | |
| QCoreApplication a(argc, argv); | |
| qInfo() << "port dump"; | |
| QSerialPort port; | |
| port.setPortName("/dev/ttyUSB1"); | |
| //port.setBaudRate(QSerialPort::Baud115200); | |
| port.setBaudRate(921600); | |
| port.setDataBits(QSerialPort::Data8); | |
| port.setStopBits(QSerialPort::OneStop); | |
| port.setParity(QSerialPort::NoParity); | |
| bool run = false; | |
| uint8_t state = 0; | |
| QTimer send_data; | |
| QObject::connect(&send_data, &QTimer::timeout, [&]() { | |
| if (!run) return; | |
| port.write((const char*)&state, sizeof(state)); | |
| ++state; | |
| }); | |
| QByteArray data; | |
| QObject::connect(&port, &QSerialPort::readyRead, [&]() { | |
| auto tmp = port.readAll(); | |
| qInfo() << "inp:" << tmp.toHex(); | |
| data.append(tmp); | |
| constexpr int long_cmd = 5; | |
| bool have_data = !data.isEmpty(); | |
| while (have_data) | |
| { | |
| int to_remove = 1; | |
| bool can_process_long = data.size() >= long_cmd; | |
| auto bytes = reinterpret_cast<const uint8_t*>(data.data()); | |
| switch (bytes[0]) | |
| { | |
| case 0x00: | |
| { | |
| /* | |
| * Reset (00h) | |
| * Resets the device. | |
| * Should be sent 5 times when the receiver status is unknown. | |
| * (It could be waiting for up to four bytes of pending long command data.) | |
| */ | |
| qInfo() << "reset"; | |
| run = false; | |
| state = 0; | |
| send_data.stop(); | |
| break; | |
| } | |
| case 0x01: | |
| { | |
| /* | |
| * Run (01h) | |
| * Arms the trigger. | |
| */ | |
| qInfo() << "run"; | |
| run = true; | |
| state = 0; | |
| send_data.setInterval(std::chrono::milliseconds{10}); | |
| send_data.start(); | |
| break; | |
| } | |
| case 0x02: | |
| { | |
| /* | |
| * D (02h) | |
| * Asks for device identification. | |
| * The device will respond with four bytes. | |
| * The first three ("SLA") identify the device. | |
| * The last one identifies the protocol version which is currently either "0" or "1" | |
| * | |
| * SigRok accepts "1SLO" and "1ALS" | |
| */ | |
| qInfo() << "get id"; | |
| port.write("1ALS", 4); // note: FOURID("SLA1") / LSB first | |
| break; | |
| } | |
| case 0x04: // EXT_GET_METADATA | |
| { | |
| // https://sigrok.org/gitweb/?p=libsigrok.git;a=blob;f=src/hardware/openbench-logic-sniffer/protocol.h;h=652ee8d1848c1ec9e8a94b15dd224616f6b2b4eb;hb=HEAD#l59 | |
| /* Metadata tokens */ | |
| #define METADATA_TOKEN_END 0x0 | |
| #define METADATA_TOKEN_DEVICE_NAME 0x1 | |
| #define METADATA_TOKEN_FPGA_VERSION 0x2 | |
| #define METADATA_TOKEN_ANCILLARY_VERSION 0x3 | |
| #define METADATA_TOKEN_NUM_PROBES_LONG 0x20 | |
| #define METADATA_TOKEN_SAMPLE_MEMORY_BYTES 0x21 | |
| #define METADATA_TOKEN_DYNAMIC_MEMORY_BYTES 0x22 | |
| #define METADATA_TOKEN_MAX_SAMPLE_RATE_HZ 0x23 | |
| #define METADATA_TOKEN_PROTOCOL_VERSION_LONG 0x24 | |
| #define METADATA_TOKEN_CAPABILITIES 0x25 /* not implemented in Demon Core v3.07 */ | |
| #define METADATA_TOKEN_NUM_PROBES_SHORT 0x40 | |
| #define METADATA_TOKEN_PROTOCOL_VERSION_SHORT 0x41 | |
| qInfo() << "EXT: get metadata"; | |
| const char info[] = | |
| // METADATA_TOKEN_DEVICE_NAME | |
| "\x01PortDump Emulator\x00" // device name | |
| // METADATA_TOKEN_FPGA_VERSION | |
| "\x02No FPGA\x00" // FPGA firmware version | |
| // METADATA_TOKEN_ANCILLARY_VERSION | |
| "\x03No MCU \x00" // MCU firmware version | |
| // METADATA_TOKEN_NUM_PROBES_LONG | |
| "\x20\x00\x00\x00\x08" // (u32, MSB first) num probes(channels) == 8x | |
| // METADATA_TOKEN_SAMPLE_MEMORY_BYTES | |
| "\x21\x00\xA0\x00\x00" // (u32, MSB first) (wrong meaning?)amount of sample memory in bytes == 10MiB | |
| // METADATA_TOKEN_DYNAMIC_MEMORY_BYTES | |
| "\x22\x00\x02\x00\x00" // (u32, MSB first) (unused?) amount of dynamic memory in bytes == 64KiB | |
| // METADATA_TOKEN_MAX_SAMPLE_RATE_HZ | |
| "\x23\x00\x0F\x42\x40" // (u32, MSB first) (ignored?)Maximum sample rate (Hz) = 1MHz | |
| // METADATA_TOKEN_PROTOCOL_VERSION_LONG | |
| "\x24\x00\x00\x00\x02" // (u32, MSB first) (ignored?)protocol version == 2 | |
| // METADATA_TOKEN_CAPABILITIES | |
| //"\x25\x00\x00\x00\x00" // (u32, MSB first) (unused?) Capabilities | |
| // METADATA_TOKEN_NUM_PROBES_SHORT | |
| //"\x40\x08" // short: num channels == 8x channels | |
| // METADATA_TOKEN_PROTOCOL_VERSION_SHORT | |
| //"\x41\x02" // short: protocol version == 2 | |
| // METADATA_TOKEN_END | |
| "" // End of data == 0x00 | |
| ; | |
| qInfo() << "EXT: send" << port.write(info, sizeof(info)); | |
| break; | |
| } | |
| case 0x11: | |
| { | |
| /* | |
| * XON (11h) | |
| * Put transmitter out of pause mode. | |
| * It will continue to transmit captured data if any is pending. | |
| * This command is being used for xon/xoff flow control. | |
| */ | |
| qInfo() << "XON - continue"; | |
| break; | |
| } | |
| case 0x13: | |
| { | |
| /* | |
| * XOFF (13h) | |
| * Put transmitter in pause mode. | |
| * It will stop transmitting captured data. | |
| * This command is being used for xon/xoff flow control. | |
| */ | |
| qInfo() << "XOFF - pause"; | |
| break; | |
| } | |
| case 0xC0: case 0xC4: case 0xC8: case 0xCC: | |
| { | |
| to_remove = long_cmd; | |
| if (!can_process_long) | |
| { | |
| break; | |
| } | |
| /* | |
| * Set Trigger Mask (C0h, C4h, C8h, CCh) | |
| * Defines which trigger values must match. | |
| * In parallel mode each bit represents one channel, | |
| * in serial mode each bit represents one of the last 32 samples of the selected channel. | |
| * The opcodes refer to stage 0-3 in the order given above. | |
| * (Protocol version 0 only supports stage 0.) | |
| */ | |
| qInfo() << "trigger mask" << data.left(long_cmd).toHex(); | |
| break; | |
| } | |
| case 0xC1: case 0xC5: case 0xC9: case 0xCD: | |
| { | |
| to_remove = long_cmd; | |
| if (!can_process_long) | |
| { | |
| break; | |
| } | |
| /* | |
| * Set Trigger Values (C1h, C5h, C9h, CDh) | |
| * Defines which values individual bits must have. | |
| * In parallel mode each bit represents one channel, | |
| * in serial mode each bit represents one of the last 32 samples of the selected channel. | |
| * The opcodes refer to stage 0-3 in the order given above. | |
| * (Protocol version 0 only supports stage 0.) | |
| */ | |
| qInfo() << "trigger value" << data.left(long_cmd).toHex(); | |
| break; | |
| } | |
| case 0xC2: case 0xC6: case 0xCA: case 0xCE: | |
| { | |
| to_remove = long_cmd; | |
| if (!can_process_long) | |
| { | |
| break; | |
| } | |
| /* | |
| * Set Trigger Configuration (C2h, C6h, CAh, CEh) | |
| * Configures the selected trigger stage. | |
| * The opcodes refer to stage 0-3 in the order given above. | |
| * The following parameters will be set: | |
| * * delay - If a match occures, the action of the stage is delayed by the given | |
| * number of samples. | |
| * * level - Trigger level at which the stage becomes active. | |
| * * channel - Channel to be used in serial mode. (0-31 in normal operation; | |
| * 0-15 when demux flag is set) | |
| * * serial - When set to 1 the stage operates as serial trigger, | |
| * otherwise it used as parallel trigger. | |
| * * start - When set to 1 a match will start the capturing process. | |
| * The trigger level will rise on match regardless of this flag. | |
| */ | |
| uint16_t delay = bytes[1] | (bytes[2] << 8); | |
| uint16_t level = bytes[3] & 0b0000'0011; | |
| uint16_t channel = ((bytes[3] & 0b1111'0000) >> 4) | ((bytes[4] & 0b0000'0001) << 4); | |
| bool b_start = (bytes[4] & 0b0000'1000) != 0; | |
| bool b_serial = (bytes[4] & 0b0000'0100) != 0; | |
| qInfo() << "trigger config" << data.left(long_cmd).toHex(); | |
| qInfo() << " delay =" << delay; | |
| qInfo() << " level =" << level; | |
| qInfo() << " channel =" << channel; | |
| qInfo() << " b_start =" << b_start; | |
| qInfo() << " b_serial=" << b_serial; | |
| break; | |
| } | |
| case 0x80: | |
| { | |
| to_remove = long_cmd; | |
| if (!can_process_long) | |
| { | |
| break; | |
| } | |
| /* | |
| * Set Divider (80h) | |
| * When x is written, the sampling frequency is set to f = clock / (x + 1) | |
| */ | |
| uint32_t divider = 0 | |
| | (bytes[1] << 0) | |
| | (bytes[2] << 8) | |
| | (bytes[3] << 16) | |
| ; | |
| qInfo() << "set divider = " << divider << "/" << data.left(long_cmd).toHex(); | |
| break; | |
| } | |
| case 0x81: | |
| { | |
| to_remove = long_cmd; | |
| if (!can_process_long) | |
| { | |
| break; | |
| } | |
| /* | |
| * Set Read & Delay Count (81h) | |
| * Read Count is the number of samples (divided by four) to read back from memory | |
| * and sent to the host computer. | |
| * Delay Count is the number of samples (divided by four) to capture after the trigger fired. | |
| * A Read Count bigger than the Delay Count means that data from before | |
| * the trigger match will be read back. | |
| * This data will only be valid if the device was running long enough before | |
| * the trigger matched. | |
| * | |
| * SigRok uses 0x83 / 0x84 if devc->max_samples > (256 * 1024) | |
| * See also: 0x83 / 0x84 | |
| */ | |
| uint16_t read_cnt = bytes[1] | (bytes[2] << 8); | |
| uint16_t delay_cnt = bytes[3] | (bytes[4] << 8); | |
| qInfo() << "set read & delay count" << data.left(long_cmd).toHex(); | |
| qInfo() << " read :" << read_cnt; | |
| qInfo() << " delay:" << delay_cnt; | |
| break; | |
| } | |
| case 0x83: // see 0x81 | |
| { | |
| to_remove = long_cmd; | |
| if (!can_process_long) | |
| { | |
| break; | |
| } | |
| uint32_t count = 0 | |
| | (bytes[1] << 0) | |
| | (bytes[2] << 8) | |
| | (bytes[3] << 16) | |
| | (bytes[4] << 24) | |
| ; | |
| qInfo() << "EXT: set delay count = " << count << data.left(long_cmd).toHex(); | |
| break; | |
| } | |
| case 0x84: // see 0x81 | |
| { | |
| to_remove = long_cmd; | |
| if (!can_process_long) | |
| { | |
| break; | |
| } | |
| uint32_t count = 0 | |
| | (bytes[1] << 0) | |
| | (bytes[2] << 8) | |
| | (bytes[3] << 16) | |
| | (bytes[4] << 24) | |
| ; | |
| qInfo() << "EXT: set read count = " << count << data.left(long_cmd).toHex(); | |
| break; | |
| } | |
| case 0x82: | |
| { | |
| to_remove = long_cmd; | |
| if (!can_process_long) | |
| { | |
| break; | |
| } | |
| /* | |
| * Set Flags (82h) | |
| * Sets the following flags: | |
| * | |
| * * demux Enables the demux input module. (Filter must be off.) | |
| * Meaning: Demux Mode. Double-Data-Rate Capturing. | |
| * Captures groups 0 & 1 inputs on both edges of internal | |
| * reference clock (ie: two samples per clock per enabled group). | |
| * * filter Enables the filter input module. (Demux must be off.) | |
| * Meaning: Noise Filter Mode. Reduces occurrence of glitches on inputs | |
| * * channel groups Disable channel group. | |
| * Disabled groups are excluded from data transmissions. | |
| * This can be used to speed up transfers. | |
| * There are four groups, each represented by one bit. | |
| * Starting with the least significant bit of the channel group field | |
| * channels are assigned as follows: 0-7, 8-15, 16-23, 24-31 | |
| * * external Selects the clock to be used for sampling. | |
| * External Clock Source. Use external clock for sampling. | |
| * If set to 0, the internal clock divided by the configured divider is used, | |
| * and if set to 1, the external clock will be used. | |
| * (filter and demux are only available with internal clock) | |
| * * inverted When set to 1, the external clock will be inverted before being used. | |
| * Inverted External Capture Clock. | |
| * Capture data on falling edge of sample clock when using external clock. | |
| * The inversion causes a delay that may cause problems at very high clock rates. | |
| * This option only has an effect with external set to 1. | |
| */ | |
| uint8_t flags = bytes[1]; | |
| bool b_demux = 0 != (flags & 0b0000'0001); | |
| bool b_filter = 0 != (flags & 0b0000'0010); | |
| uint32_t groups = (flags & 0b00111100) >> 2; // disabled groups mask | |
| bool b_external = 0 != (flags & 0b0100'0000); | |
| bool b_inverted = 0 != (flags & 0b1000'0000); | |
| qInfo() << "set flags" << data.left(long_cmd).toHex() << Qt::bin << flags; | |
| qInfo() << " b_demux" << b_demux; | |
| qInfo() << " b_filter" << b_filter; | |
| qInfo() << " groups" << Qt::bin << groups; | |
| qInfo() << " b_external" << b_external; | |
| qInfo() << " b_inverted" << b_inverted; | |
| break; | |
| } | |
| default: | |
| { | |
| qInfo() << "Unknown (skip all):" << data.toHex(); | |
| to_remove = data.size(); | |
| break; | |
| } | |
| } | |
| bool removed = false; // BUG here(?): no enough data | |
| if (to_remove <= data.size()) | |
| { | |
| data.remove(0, to_remove); | |
| removed = true; | |
| } | |
| have_data = removed && !data.isEmpty(); | |
| } | |
| }); | |
| qInfo() << "Open:" << port.open(QIODevice::ReadWrite); | |
| return QCoreApplication::exec(); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment