From 739e082469038768291810d6b37525d89cae14d3 Mon Sep 17 00:00:00 2001 From: Thorsten de Buhr Date: Fri, 4 Sep 2026 11:23:25 +0200 Subject: [PATCH] feat(ctrace): decode Armv8-M DWT match packets --- tools/ctrace/src/decode/DwtPacketDecoder.cpp | 16 ++++++ tools/ctrace/src/model/TraceEvent.h | 12 +++-- tools/ctrace/src/model/TraceSelection.cpp | 6 +++ tools/ctrace/src/output/csv/CsvRowMapper.cpp | 2 + tools/ctrace/src/output/ctf/CtfEncoder.cpp | 17 +++++++ tools/ctrace/src/output/ctf/CtfEncoder.h | 2 + .../src/output/ctf/CtfMetadataWriter.cpp | 21 ++++++++ tools/ctrace/src/output/ctf/CtfSchema.h | 3 ++ .../src/output/ctf/TraceCompassXmlWriter.cpp | 35 +++++++++++++ tools/ctrace/test/data/README.md | 10 ++++ .../trace-match/trace-match.ctrace-run.yml | 47 ++++++++++++++++++ .../test/data/trace-match/trace-match.raw | Bin 0 -> 18 bytes .../test/integration/src/CtraceIntegTests.cpp | 46 +++++++++++++++++ .../unit/src/decode/DwtPacketDecoderTests.cpp | 42 ++++++++++++++++ .../unit/src/model/TraceSelectionTests.cpp | 4 ++ .../unit/src/output/csv/CsvRowMapperTests.cpp | 3 ++ .../unit/src/output/ctf/CtfEncoderTests.cpp | 32 ++++++++++++ .../src/output/ctf/CtfMetadataWriterTests.cpp | 19 ++++++- .../unit/src/output/ctf/CtfSchemaTests.cpp | 3 +- .../ctrace/test/unit/support/CtfTestSupport.h | 3 ++ 20 files changed, 317 insertions(+), 6 deletions(-) create mode 100644 tools/ctrace/test/data/trace-match/trace-match.ctrace-run.yml create mode 100644 tools/ctrace/test/data/trace-match/trace-match.raw diff --git a/tools/ctrace/src/decode/DwtPacketDecoder.cpp b/tools/ctrace/src/decode/DwtPacketDecoder.cpp index 562f77dd7..b64dfa165 100644 --- a/tools/ctrace/src/decode/DwtPacketDecoder.cpp +++ b/tools/ctrace/src/decode/DwtPacketDecoder.cpp @@ -55,6 +55,8 @@ constexpr std::uint32_t kPmuOverflowMask = 0xffU; constexpr std::uint8_t kArmv7MFullPcBytes = 4U; constexpr std::uint8_t kArmv7MAddressOffsetBytes = 2U; +constexpr std::uint8_t kArmv8MMatchBytes = 1U; +constexpr std::uint32_t kArmv8MMatchValue = 1U; /** @brief Describes an invalid DWT event-counter payload. */ static std::string invalidEventCounterMessage(const DwtPayloadPacket& payload) @@ -244,6 +246,20 @@ void DwtPacketDecoder::decodeDataTrace(const DwtPayloadPacket& payload, std::vec event.quality = payload.quality; if (packetType == DwtDataPacketType::Address) { + const auto isMatch = !secondarySubtype && payload.size == kArmv8MMatchBytes && payload.value == kArmv8MMatchValue; + if (isMatch) { + auto& pending = m_pendingDataTrace[comparator]; + if (pending.has_value()) { + flushPending(comparator, qualityForPendingFlush(*pending, payload.quality), payload.tcyc, output); + } + TraceEvent match{DwtMatchTraceEvent{comparator}}; + match.index = payload.index; + match.traceBusId = payload.traceBusId; + match.tcyc = payload.tcyc; + match.quality = payload.quality; + output.push_back(std::move(match)); + return; + } const auto expectedSize = secondarySubtype ? kArmv7MAddressOffsetBytes : kArmv7MFullPcBytes; if (payload.size != expectedSize) { auto flushed = flush(payload.quality, payload.tcyc); diff --git a/tools/ctrace/src/model/TraceEvent.h b/tools/ctrace/src/model/TraceEvent.h index 16fd27e1a..5e6706171 100644 --- a/tools/ctrace/src/model/TraceEvent.h +++ b/tools/ctrace/src/model/TraceEvent.h @@ -95,6 +95,11 @@ struct DwtAddressTraceEvent { DwtAddressTraceLocation location; }; +/** @brief Reports that one DWT comparator generated a match without additional trace data. */ +struct DwtMatchTraceEvent { + std::uint32_t comparator = 0; +}; + /** @brief Returns the program counter carried by a DWT address event, if present. */ inline std::optional dwtAddressPc(const DwtAddressTraceEvent& event) { @@ -230,9 +235,10 @@ struct TraceIssueEvent { }; /** @brief Stores the semantic payload of a decoded trace event. */ -using TraceEventPayload = std::variant; +using TraceEventPayload = + std::variant; /** @brief Describes timestamp and data-loss quality at an event. */ struct TraceQuality { diff --git a/tools/ctrace/src/model/TraceSelection.cpp b/tools/ctrace/src/model/TraceSelection.cpp index 2fafbb4b5..c0a7d223e 100644 --- a/tools/ctrace/src/model/TraceSelection.cpp +++ b/tools/ctrace/src/model/TraceSelection.cpp @@ -36,6 +36,12 @@ static std::optional typeFor(const DwtAddressTraceEvent&) return TraceEventType::Dwt; } +/** @brief Maps a comparator-only DWT match payload to data trace output. */ +static std::optional typeFor(const DwtMatchTraceEvent&) +{ + return TraceEventType::Dwt; +} + /** @brief Maps an exception payload to its selectable event type. */ static std::optional typeFor(const ExceptionTraceEvent&) { diff --git a/tools/ctrace/src/output/csv/CsvRowMapper.cpp b/tools/ctrace/src/output/csv/CsvRowMapper.cpp index f5b5d557d..c2ad981fe 100644 --- a/tools/ctrace/src/output/csv/CsvRowMapper.cpp +++ b/tools/ctrace/src/output/csv/CsvRowMapper.cpp @@ -170,6 +170,8 @@ static CsvRow eventToCsvRow(const TraceEvent& event) if (const auto offset = dwtAddressOffset(*address)) { row[column(CsvColumn::Offset)] = hexValue(*offset, 2); } + } else if (const auto* match = traceEventPayload(event)) { + row[column(CsvColumn::Source)] = std::to_string(match->comparator); } else if (const auto* exception = traceEventPayload(event)) { row[column(CsvColumn::Source)] = std::to_string(exception->number); row[column(CsvColumn::Value)] = exceptionActionCsvValue(exception->action); diff --git a/tools/ctrace/src/output/ctf/CtfEncoder.cpp b/tools/ctrace/src/output/ctf/CtfEncoder.cpp index e83475920..3f0494bac 100644 --- a/tools/ctrace/src/output/ctf/CtfEncoder.cpp +++ b/tools/ctrace/src/output/ctf/CtfEncoder.cpp @@ -200,6 +200,10 @@ void CtfEncoder::writeEvent(const TraceEvent& event) if (selected) { writeDwtAddrEvent(event, *address); } + } else if (const auto* match = traceEventPayload(event)) { + if (selected) { + writeDwtMatchEvent(event, *match); + } } else if (const auto* counters = traceEventPayload(event)) { if (selected) { writeDwtEvent(event, *counters); @@ -363,6 +367,19 @@ void CtfEncoder::writeDwtAddrEvent(const TraceEvent& event, const DwtAddressTrac }); } +void CtfEncoder::writeDwtMatchEvent(const TraceEvent& event, const DwtMatchTraceEvent& match) +{ + constexpr auto payloadSize = 1U + 1U + 4U; + const auto eventTimestamp = allocateEventTimestamp(event.traceBusId); + const auto quality = computeSampleQuality(event); + m_stream.writeRecord(CtfSchema::value(CtfSchema::EventId::DwtMatch), eventTimestamp, event.traceBusId, payloadSize, + [&](CtfStreamWriter::Record& record) { + record.writeU8(static_cast(match.comparator & 0xffU)); + record.writeU8(quality.first); + record.writeU32(quality.second); + }); +} + void CtfEncoder::writeDwtEvent(const TraceEvent& event, const DwtEventTraceEvent& counters) { constexpr auto payloadSize = 1U + 1U + 4U; diff --git a/tools/ctrace/src/output/ctf/CtfEncoder.h b/tools/ctrace/src/output/ctf/CtfEncoder.h index 594fcff89..265549007 100644 --- a/tools/ctrace/src/output/ctf/CtfEncoder.h +++ b/tools/ctrace/src/output/ctf/CtfEncoder.h @@ -75,6 +75,8 @@ class CtfEncoder final { void reportDwtSizeMismatch(const TraceEvent& event, const DwtDataTraceEvent& data, const ResolvedTraceSource* source); /** @brief Encodes one DWT address event. */ void writeDwtAddrEvent(const TraceEvent& event, const DwtAddressTraceEvent& address); + /** @brief Encodes one comparator-only DWT match event. */ + void writeDwtMatchEvent(const TraceEvent& event, const DwtMatchTraceEvent& match); /** @brief Expands one DWT event-counter mask into individual CTF records. */ void writeDwtEvent(const TraceEvent& event, const DwtEventTraceEvent& counters); /** @brief Expands one PMU trace-on-overflow mask into individual CTF records. */ diff --git a/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp b/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp index 68e132ff0..60d115c9c 100644 --- a/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp +++ b/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp @@ -440,6 +440,26 @@ event { )"; } +/** @brief Writes the comparator-only DWT match event declaration. */ +static void writeDwtMatchEvent(std::ostream& out) +{ + out << R"( +event { + id = )" + << CtfSchema::value(CtfSchema::EventId::DwtMatch) << R"(; + name = ")" + << CtfSchema::eventName(CtfSchema::EventId::DwtMatch) << R"("; + stream_id = )" + << CtfSchema::SwoStreamId << R"(; + fields := struct { + cmsis_dwt_comparator_t cmsis_dwt_comparator; + uint8_t cmsis_sample_flags; + uint32_t cmsis_overflow_count; + }; +}; +)"; +} + /** @brief Writes the DWT event-counter declaration. */ static void writeDwtEvent(std::ostream& out) { @@ -565,6 +585,7 @@ void CtfMetadataWriter::write(const std::filesystem::path& outputDir, const std: writeItmEvent(out); writeDwtValueEvent(out); writeDwtAddressEvent(out); + writeDwtMatchEvent(out); writeDwtEvent(out); writePmuEvent(out); writeStatusEvents(out); diff --git a/tools/ctrace/src/output/ctf/CtfSchema.h b/tools/ctrace/src/output/ctf/CtfSchema.h index 343602045..a71427484 100644 --- a/tools/ctrace/src/output/ctf/CtfSchema.h +++ b/tools/ctrace/src/output/ctf/CtfSchema.h @@ -31,6 +31,7 @@ enum class EventId : std::uint32_t { PcSample = 6U, DwtEvent = 7U, PmuEvent = 8U, + DwtMatch = 9U, }; /** @brief Classifies CTF trace-status records. */ @@ -255,6 +256,8 @@ constexpr std::string_view eventName(EventId id) return "DWT_EVENT"; case EventId::PmuEvent: return "PMU_EVENT"; + case EventId::DwtMatch: + return "DWT_MATCH"; } return "UNKNOWN"; } diff --git a/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp b/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp index a8a59b5b3..6315b0c06 100644 --- a/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp +++ b/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp @@ -133,6 +133,30 @@ static std::string eventCounterHandlers(CtfSchema::EventId eventId, std::string_ return handlers.str(); } +/** @brief Generates one visible pulse for every comparator-only DWT match. */ +static std::string dwtMatchHandler() +{ + std::ostringstream handler; + handler << R"( + + + + + + + + + + + + +)"; + return handler.str(); +} + /** @brief Generates the Trace Compass state-provider definition. */ static std::string stateProviderXml() { @@ -159,6 +183,10 @@ static std::string stateProviderXml() )" << valueHandlers(CtfSchema::EventId::Itm, "itm", "cmsis_itm_channel", "value") + << R"( + +)" << dwtMatchHandler() << R"( @@ -380,6 +408,13 @@ static std::string viewsXml() << CtfSchema::eventName(CtfSchema::EventId::DwtAddress) << '/' << R"(*"> + +