# dtest **Repository Path**: Lamdonn/dtest ## Basic Information - **Project Name**: dtest - **Description**: dtest (Dynamic Test) - 轻量级动态 C 语言调试框架,一个极度轻便、极具可移植性的纯 C 语言应用层调试与观测框架。它旨在为不方便连接仿真器,或需要进行高频动态观测与控制的嵌入式系统(及 PC 软件)提供远程交互手段,您可以在不修改或重新编译代码的情况下,在系统运行时动态读取/修改内存变量、配置周期信号上报、甚至直接传入地址调用系统内的任意函数(支持传参)。 - **Primary Language**: C/C++ - **License**: GPL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2026-06-16 - **Last Updated**: 2026-09-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # dtest (Dynamic Test) - Lightweight Dynamic C Debugging Framework `dtest` is an extremely lightweight, highly portable, pure C application-layer debugging and observation framework. It aims to provide a remote interaction method similar to UDS (Unified Diagnostic Services) for embedded systems (and PC software) that are inconvenient to connect to hardware emulators, or require high-frequency dynamic observation and control. Through `dtest`, you can dynamically read/modify memory variables, configure periodic signal reporting, or even directly invoke any internal function in the system by passing its memory address (with arguments) at runtime, **without modifying or recompiling the code**. ## 🌟 Core Features - **Extremely Lightweight & Zero Dependencies**: Pure C99 implementation, independent of any specific hardware platform or underlying library, deployable on any 32-bit or 64-bit system. - **UDS-Style Protocol Distribution**: Supports standard Request / Positive Response / Negative Response processing structures, with unified error code (NRC) feedback. - **Multi-core Shared Memory (SHM) & Cache Coherency**: Supports inter-core routing via shared memory channels, and automatically triggers user-registered `cache_inv` (invalidate cache) and `cache_wbinv` (write-back invalidate cache) functions before and after key memory operations (such as checking state flags, reading length, copying data payload, or heartbeat counters) to guarantee cache coherency in heterogeneous/homogeneous multi-core architectures. - **Simplified Multi-core Topology**: Avoids complex manual `is_master` configuration by enforcing that the master router node must have a `core_id` of 0, and non-zero `core_id` nodes automatically act as slaves. - **Dynamic Memory Read/Write (0x22 / 0x2E)**: Fetch and modify arbitrary memory space data based on physical addresses. - **Periodic Signal Monitoring (0x2A)**: Dynamically set monitored memory addresses and packet offsets at runtime. The component automatically sends variable status periodically, preventing frequent polling. - **High-Order Dynamic Function Invocation (0x32)**: Provide the function's memory address and its parameters, and the component will forcefully cast and execute it at runtime, **supporting up to 16 arbitrary type (U8-U64/F32-F64) combined parameter passing and result returning**. - **Background Periodic Tasks (0x33)**: Support registering any function and its runtime parameters into the background to be executed automatically in a loop at a specified cycle (milliseconds). - **Command Queue Mechanism**: Separation of reception and execution, allowing safe command reception in high-frequency / hardware interrupts, deferred to the main loop for unified processing. - **Yielding Memory Control**: No `malloc` is used within the component. All struct memory pools are statically allocated externally and injected by the user, completely eliminating memory fragmentation and illegal overflows. - **Multi-core Platform Data Routing**: Supports one-master multi-slave mode for multi-core system chips. The host PC only needs to interact with the master core to route data automatically to the slave cores. --- ## 🏗️ Framework Architecture & Data Flow ### 1. Architecture ![Framework](image/framework.png) ### 2. Data Flow The core design philosophy of `dtest` is **"Data Decoupling and Asynchronous Execution"**. 1. `dtest_receive()` pushes the serial data received in the low-level hardware interrupt extremely fast into the lock-free ring buffer queue and returns immediately. 2. The main loop `dtest_task()` pops instructions from the queue, parses big-endian data, and dispatches them to corresponding action handlers (such as casting pointers for direct reading). 3. The mounted `tx_cb()` is invoked to synchronously/asynchronously return positive/negative response frames to the host PC. ![DataFlow](image/DataFlow.png) --- ## 🚀 Quick Integration & Porting Guide Porting `dtest` is extremely simple. Just complete the underlying RX/TX connection and memory pool injection. ### 1. Prepare Underlying Transmit Function Implement a data transmission interface that sends a byte stream to external interfaces: ```c dtest_tx_status_t my_uart_tx(const uint8_t *data, uint16_t len) { // If synchronous blocking send // uart_send_blocking(data, len); // return DTEST_TX_OK; // If asynchronous send (e.g., triggering DMA controller) if (uart_dma_send(data, len) == OK) { return DTEST_TX_PENDING; } return DTEST_TX_ERROR; } // For asynchronous sending, a status check interface can be provided dtest_tx_status_t my_uart_check_tx(void) { if (uart_dma_is_busy()) { return DTEST_TX_PENDING; } return DTEST_TX_OK; } ``` ### 2. Initialize dtest Inject the transmit callback into the component during system startup: ```c dtest_config_t config = {0}; config.tx_cb = my_uart_tx; // Mount memory pools here if advanced features like signals and preset functions are needed: // config.signal_pool = user_signal_array; // config.max_signals = 16; dtest_init(&config); ``` ### 3. Receive Data Delivery When the underlying layer (e.g., UART RX interrupt, network RX task) receives data, feed it directly to `dtest`'s receive queue: ```c void on_uart_receive(const uint8_t *rx_data, uint16_t len) { dtest_receive(rx_data, len); } ``` ### 4. Place Periodic Task Periodically call `dtest_task` in your main loop (`while (1)`) or RTOS task: ```c while (1) { // Assume the loop runs every 10ms dtest_task(10); delay_ms(10); } ``` ### 5. Local Terminal Experience The component includes an interactive test terminal `main.c`. You can use it to simulate and test the entire communication and control process in a local PC environment (Windows or Linux). **Compile & Run**: ```bash make ./built/bin/dtest ``` **Running Effect**: The console will prompt that the interactive environment has started. Enter hex commands (space-separated) and press Enter to observe the behavior of the bottom layer. For example: ```text === Interactive dtest Terminal === Task running at 10ms interval. Press Ctrl+C to exit. Enter hex commands separated by space. Example (Read Memory g_temperature): 22 00 00 00 00 00 40 40 20 00 02 ``` Copy, paste, and send the instruction in the Example, and you will see the terminal reply with the corresponding hexadecimal data (simulating the transceiver process). ### 6. Local GUI Experience By enabling the `USE_VIM_COMM` macro in `main.c`, you can directly experience the full interconnection communication between the target device and GUI on your PC: 1. **Compile Target Device Simulator**: ```bash make ./built/bin/dtest ``` The simulator will enter a waiting state. 2. **Start Visual Host GUI**: ```bash python ./gui/gui.py ``` After the host GUI starts, it will automatically connect to the simulator pipe. You can freely try sending packets, observing signal waveforms, and calling internal functions in the GUI! ### 7. Unified HTTP REST & Event-Stream (SSE) APIs for External Integration To enable seamless automation, test-bench integration (e.g. C#, LabVIEW, MATLAB, dSPACE) and lightweight terminal interaction, the `dtest` host GUI automatically spins up an integrated **Unified HTTP REST & Event-Stream (SSE) Server** on port `5000` on startup. This service utilizes standard HTTP protocol and HTML5 SSE (Server-Sent Events) streaming, providing **zero external dependencies, cross-computer LAN debugging, and out-of-the-box CMD/PowerShell operability**. #### Common REST API Endpoints & curl Invocation Examples You can directly interact with the running GUI from any standard Command Prompt (CMD), PowerShell, or any other computer on the local network using the built-in `curl.exe`: 1. **Retrieve the list of defined target ECUs and their status**: ```bash curl.exe http://127.0.0.1:5000/api/ecus ``` 2. **Connect to a specific ECU target (e.g., Gateway_VIM)**: ```bash curl.exe -X POST http://127.0.0.1:5000/api/connect -H "Content-Type: application/json" -d "{\"name\": \"Gateway_VIM\"}" ``` 3. **Dispatch a UDS 0x22 Memory Read request (read 4 bytes from address 0x20001000)**: ```bash curl.exe "http://127.0.0.1:5000/api/read_mem?address=0x20001000&size=4" ``` 4. **Dispatch a UDS 0x2E Memory Write request (write 4 bytes to address 0x20001000)**: ```bash curl.exe -X POST http://127.0.0.1:5000/api/write_mem -H "Content-Type: application/json" -d "{\"address\": \"0x20001000\", \"size\": 4, \"data\": \"00 11 22 33\"}" ``` 5. **Transmit an arbitrary raw hex UDS diagnostic command on the main link**: ```bash curl.exe -X POST http://127.0.0.1:5000/api/send_cmd -H "Content-Type: application/json" -d "{\"hex\": \"22 F1 8C\"}" ``` #### Real-time Logging Event-Stream (Event-Stream / SSE) Using the stream-pushing endpoint, you can instantly subscribe to the host's real-time diagnostic TX/RX logging stream directly from CMD or any test script. The connection is kept alive, and events are pushed actively by the host: * **Initiate real-time logging listener stream**: ```bash curl.exe http://127.0.0.1:5000/api/stream ``` *Upon execution, any UDS command transmitted (TX), hardware reply received (RX), or system warning will be printed to your terminal instantly in real-time JSON format.* ### 8. Advanced Macro Configuration & Customization In `core/dtest.h`, you can fine-tune memory limits based on actual needs: ```c /* --- Core Configuration --- */ // [Core] Max size of internal RX/TX buffers (bytes). Recommended >=128. #define DTEST_FRAME_MAX_LEN 256 // [Signal] Max payload size for periodic snapshot packet (0xAA). Cannot exceed DTEST_FRAME_MAX_LEN. #define DTEST_SIGNAL_FRAME_LEN 64 // [Queue] Async receive command queue depth. Increase to resist burst traffic if task scheduling is slow. #define DTEST_CMD_QUEUE_SIZE 1 ``` *(If you are porting to 64-bit x86 or ARM64 Linux platforms for testing, the `DTEST_PTR_SIZE` macro will automatically infer to 8 bytes, which is perfectly compatible and interoperable with 32-bit MCUs (4 bytes).)* --- ## 🖥️ Host GUI Tool Usage Guide ![ConnectionView](image/ConnectionView.png) After `gui.py` is started, it provides multiple tabs to implement multi-dimensional debugging functions. * **Sidebar**: Selects different pages. * **Top Bar**: Configures target device architecture (32-bit or 64-bit), selects which core (0~15, master is 0) to route commands to, and displays the device connection status. Instant notification popups are shown on the left of the top bar. * **Bottom Bar**: Interactive raw command area. You can input raw hexadecimal requests here, and see the received responses. It also supports configuring execution timeouts and displaying execution states. Clicking buttons in other pages automatically generates, sends, and parses commands in this bar. ### `🔌` Connection (Connection Settings) On the Connection page, you can choose from the commonly supported connection modes and configure their parameters. #### 1. VIM Virtual Interface ![ConnectionVim](image/ConnectionVim.png) Implemented via PC pipes, ideal for local communication testing. * **VIM Role**: As the master host software, select RBS(Tester). * **Mask**: Used to identify the VIM pipe; must match the device configuration. #### 2. Serial Interface ![ConnectionSerial](image/ConnectionSerial.png) Serial interface, suitable for UART, RS232, RS485, etc., relying on the SLUP (Serial Link Universal Protocol) to prevent packet fragmentation. * **Port**: PC port name (e.g., COM1, COM2). * **Baudrate**: Port baud rate. * **Timeout**: Connection timeout; default is 0. * **SLUP Head**: SLUP frame header, HEX format separated by spaces. Up to 4 bytes. Choose characters rarely used in data payloads. * **SLUP Tail**: SLUP frame tail (optional), HEX format separated by spaces. Up to 4 bytes. #### 3. Network Interface ![ConnectionNetwork](image/ConnectionNetwork.png) Network interface, suitable for ETH, WIFI, etc. It also relies on the SLUP protocol to handle packet fragmentation/coalescing and improve stability. * **Protocol**: Protocol type, TCP or UDP. * **Timeout**: Socket timeout, default is 0.5s. * **Local IP**: Host PC IP address. * **Local Port**: Host PC port; 0 means any random available port. * **Target IP**: Target device IP address. * **Target Port**: Target device port. * **SLUP Head/Tail**: Frame delimiters, same as Serial configuration. #### 4. CAN Bus Interface ![ConnectionCAN](image/ConnectionCAN.png) CAN interface, commonly used in automotive ECUs, relying on the CanTp protocol stack. * **Bus Type**: CAN Bus type, supporting socketcan, pcan, virtual, and the mainstream Vector CANoe. * **Channel**: CAN channel. For VN1640 channels 1~4, fill in indices 0~3. * **App Name**: Application identifier for driver linkage (e.g., default APP name **CANoe**). * **Init HW**: Check this option to initialize the analyzer hardware bitrate (e.g., when VN1640 is used exclusively by dtest. Disregard if VN1640 is already initialized by a running CANoe simulation project). * **TX ID**: CanTp protocol stack TX CANID (Host PC -> Device). * **RX ID**: CanTp protocol stack RX CANID (Device -> Host PC). * **Enable CAN-FD**: Enable CAN-FD mode. * **Bitrate**: CAN communication baud rate. * **Data Bitrate**: CAN-FD data segment baud rate. * **Enable BRS**: Enable Bit Rate Switch. --- ### `💾` Memory (Memory Read/Write) ![MemoryView](image/MemoryView.png) Direct physical address-based raw read/write, ideal for debugging registers and hardware variables. #### 1. Read Memory **Address**: Memory physical address in hexadecimal. **Size**: Data length to read in decimal. **Data**: Read data displayed as space-separated hex bytes. #### 2. Write Memory **Address**: Target physical address in hexadecimal. **Size**: Data length to write in decimal. **Data**: Input payload data as space-separated hex bytes. --- ### `📡` Signal (Signal Config & Reading) ![SignalView](image/SignalView.png) Assigns variable names to physical addresses for easy tracking. Signals can be compiled statically into the device (static config) or dynamically set from the GUI (dynamic config). Both configurations require a local signal table to parse and display values. The signal table is defined in the [1. Periodic Signal Observation Config Table (signals.csv)](#1-periodic-signal-observation-config-table-signalscsv) format. #### 1. Signal Configuration **Load Config**: Loads a defined signal table; automatically loads `gui\config\signals.csv` on startup. **Save Config**: Saves the current signal layout to disk. **Send Config to Device**: Dynamically configures the signal table to the target device with a single click. #### 2. Signal Control **Enable Cycle**: Prompts the device to start periodically reporting configured signals. **Disable Cycle**: Prompts the device to stop periodic signal reporting. **Cycle Ms**: Reporting interval in milliseconds. **Enable Dyn Len**: Enables dynamic signal packet length. The reporting frame adapts to the actual size of currently active signals. **Disable Dyn Len**: Disables dynamic length. Signals are sent in a pre-allocated fixed-size packet. **Clear All Signals**: Clears all signal registrations on the device. #### 3. Signal Reading **Signal**: Dropdown to select a loaded signal; automatically populates Signal ID and Type. **ID**: Signal positional index in the table, starting from 0. **Type**: Target data type. **Read**: Instantly requests and returns the selected signal value. **Value**: Displays the parsed variable value according to its type. #### 4. Signal Addition **Signal Address**: Physical variable address (obtainable from compiler MAP/ELF symbol files). **Signal Size**: Data length in bytes. **Frame Offset**: Signal target index position inside the periodic frame payload. **Add Signal**: Manually registers a specific signal on the target device. --- ### ⚙️ Function (Function Call) ![FunctionView](image/FunctionView.png) A magical and advanced feature allowing runtime execution of arbitrary C-type functions directly from the interface. #### 1. Function Prototype The GUI automatically loads `gui\config\function_proto.json` on startup. You can save frequently called parameters as prototypes for easy reuse. **Select Function**: Dropdown to select loaded prototypes. **Load Function**: Populates address, return type, and argument fields. **Function Name**: Identifier name. **Save Function**: Saves/registers current parameter layouts to disk. #### 2. Function Arguments Supports up to 16 parameters. Each can be individually configured as `IN` (value input), `OUT` (pointer reference for output values), and standard types `U8-F64`. **Arg xx**: Checkbox to activate a parameter index. **Type**: Data type: `VOID`, `U8`, `I8`, `U16`, `I16`, `U32`, `I32`, `U64`, `I64`, `F32`, `F64`, `BYTES`. **Dir**: Data direction: `IN`, `OUT`, `INOUT`. **Value**: Value payload for inputs, or output display for return variables. #### 3. Function Execution Allows calling pre-defined static functions (by index) or dynamic functions (by direct absolute address). Returns the function return value (if non-void) and echoes output parameters. **Func ID**: Pre-registered static function index, starting from 0. **Ret Type**: Expected return type. **Ret Val**: Return value feedback. **Static Call**: Invokes a statically registered function. **Func Addr**: Target memory function address in hex. **Dynamic Call**: Invokes a dynamic function by absolute address. --- ### ⏱️ Task (Periodic Task) ![TaskView](image/TaskView.png) Different from normal function execution, Task mounts the execution logic to the background of the target device to be executed periodically (e.g., every 1000ms), without requiring the GUI to remain actively connected. Configuring tasks utilizes the same prototypes and arguments as Function Calling. #### 1. Task Configuration **Func Addr**: Absolute task function address in hex. **Cycle Ms**: Loop interval in milliseconds. **Ret Type**: Task return type (return values are executed in the background and not returned to the GUI). **Add Task**: Registers and starts the periodic task. **Delete Task**: Removes the registered task by absolute function address. **Clear All Tasks**: Removes all periodic background tasks from the device. --- ### 📊 Dashboard (Signal Dashboard) ![DashboardView](image/DashboardView.png) Provides an intuitive, real-time graphical spreadsheet of all variables, featuring safe range monitoring. Safe ranges (Min/Max) are evaluated against the CSV configuration; values exceeding limits are flagged red and increment the error counter. Indicators dynamically switch between green, yellow, and red status symbols. *This feature requires configuring active signals and enabling reporting in the `Signal` tab.* #### 1. Log Saving Supports automated signal logging. Check `Auto-save Log` to record incoming signal streams to a spreadsheet inside the `log/` directory, labeled by recording timestamp. #### 2. Signal Filtering Supports filtering active displays (does not affect back-end log saving). You can filter by `Core` of origin, `Name` matching, `Group` categories, or alarm `Status` (red, yellow, green). #### 3. Signal Summary Provides a concise overview of total signal statuses, alarm flags, and group errors. #### 4. Signal Graphing Click `Graph` to enter the waveform visualization window to see live signals rendered as line charts. ![Graph](image/Graph.png) The `Graph` view contains all basic filtering/display tools of the Dashboard, plus advanced visualization controls: **Source**: Switches between plotting live telemetry data or reviewing historical logs (CSV signal logs). **Y-Axis**: Switches between plotting all variables in a single "Shared" Y-axis subplot, or splitting them into "Separate" subplots. **Marker**: Sets update node marks: Circle, Star, Square, or None. **Y-Scale**: Controls Y-axis bounds. "Auto" fits the viewport to contain all values. "Config Min/Max" locks bounds to the safe values of the CSV configuration to easily spot limit crossings. **Resume Auto-Scale**: Refits scale limits to current data. --- #### 📈 Advanced Waveform Interactions The new plotting system features rich, modern interactive controls: - **X-axis Multi-Channel Synchronization (Separate mode)**: When using separate Y-axes subplots for each signal, all subplots share and synchronize a single X-axis timeline. Panning, zooming, or dragging the X-axis on any subplot instantly synchronizes all other subplots. - **Precision Axis Scroll-Zooming**: Scrolling the mouse wheel over the **X-axis scale** zooms only the time axis; scrolling over the **Y-axis scale** zooms only that subplot's physical Y-axis; scrolling over the plotting area zooms both axes in sync. It features an automated proximity search to detect scrolls near the scale lines. - **Direct Scale Click-and-Drag Panning**: Directly left-clicking and dragging the **X-axis or Y-axis scale lines** pans that specific scale (shifting all subplots in sync for X-axis); dragging inside the plotting area pans both axes freely. --- ### 💻 Console (Logging) ![ConsoleView](image/ConsoleView.png) Displays real-time logs of GUI execution and command handshakes. #### 1. Log Filtering Filters logs by level and category: `Info`, `Warning`, `Error`, `TX`, `RX`. #### 2. Log Saving Check `Auto-save Log` to record console messages to files in the `log/` directory. You can also click `Save Log` to save the active buffer, or `Clear Console` to clean the window. --- ### 📝 GUI Configuration File Format The GUI is driven by configurations placed in the `config/` directory by default. #### 1. Periodic Signal Observation Config Table (`signals.csv`) This is a standard CSV file that can be edited using Excel or a text editor to guide the Dashboard in fetching and evaluating variables. | Column Name | Description | Example | | :--- | :--- | :--- | | **SignalName** | Signal identifier name, used for display and filtering. | `g_system_tick` | | **Group** | Signal group name, useful for batch filtering and error stats. | `System` | | **Address** | Absolute physical address of the variable in target memory (Hex). | `0x20000004` | | **Size** | Actual byte size occupied by the variable in memory (Dec). | `4` | | **Offset** | Byte offset in the `0xAA` report payload from the target device. | `0` | | **Endian** | Data endianness, enter `Little` or `Big`. | `Little` | | **Type** | Base data type (`U8`/`I8`/`U16`/`U32`/`F32`/`F64`/`BYTES`, etc.). | `U32` | | **Factor** | Physical value conversion factor (`Phys = Raw * Factor + OffsetVal`). | `1.0` | | **OffsetVal** | Physical value conversion offset. | `0` | | **Min / Max** | Safe min/max physical thresholds. Dashboard highlights red if exceeded. | `0 / 255` | | **Unit** | Physical unit text for display purposes only. | `ms` | **Reference Example**: ```csv SignalName,Group,Address,Size,Offset,Endian,Type,Factor,OffsetVal,Min,Max,Unit g_system_tick,System,0x00000000,4,0,Little,U32,1.0,0,0,,ms g_temperature,Sensor,0x00000000,2,4,Little,U16,0.1,-40,-20,85,C ``` *(Tip: The `function_proto.json` function prototype configuration file is recommended to be generated directly by saving through the GUI, and generally does not require manual editing.)* --- ## 📖 Protocol Specification (UDS Style) | Frame | Byte 0 | Byte 1 | Byte 2 | Byte 3 | ... | | :--- | :--- | :--- | :--- | :--- | :--- | | **Request** | Core ID | Service ID | Payload | ... | ... | | **Response** | Core ID | Respond Type | ... | ... | ... | Frame structure consists of `[Service ID] + [Payload...]`. Positive response header is `[SID + 0x40]`, and negative response is fixed as `[0x7F] [SID] [NRC]`. > **Note:** The length of the `[Addr]` field adapts to the pointer size of the target system (`sizeof(void*)`). 4 bytes on a 32-bit MCU, 8 bytes on a 64-bit PC. Data is transmitted in Big-Endian. ### 1. Supported Service IDs (SIDs) Overview | SID (Hex) | Service Name | Function Description | | :---: | :--- | :--- | | `0x22` | **Read Memory** | Direct read of MCU memory data of a specified length based on physical address. | | `0x2E` | **Write Memory** | Overwrite/modify MCU memory data of a specified length based on physical address. | | `0x2A` | **Config Signal** | Dynamically set memory observation points. The bottom layer automatically captures snapshots and assembles them into `0xAA` packets for periodic return. | | `0x2B` | **Read Signals** | Instantly request and return the latest memory status snapshot of all (or single) configured observation signals without waiting for the cycle. | | `0x31` | **Call Static** | Trigger calling a specific debug function pre-registered via `dtest_register_function` using a short alias ID. | | `0x32` | **Call Dynamic** | Provide target function physical address and parameter characteristics; forcefully cast and inject arguments at runtime, supporting return value and output pointer extraction. | | `0x33` | **Config PTask** | Mount a calling request for a parameterized function to the background. The bottom layer automatically wakes and executes it periodically. | > **Passive Report Message (`0xAA`)**: When periodic signal observation is configured via `0x2A` and enabled, the device automatically outputs data frames starting with `0xAA`. This is a one-way periodic stream, not a request/response model. ### 2. Basic Data Types (TypeID) & Direction In dynamic invocation (`0x32`) and background tasks (`0x33`), parameter types and flow directions must be specified. The protocol combines the **direction flag** (high 2 bits) and **base data type** (low 6 bits) into a `TypeID` byte via bitwise OR (`|`). | Base Data Type | Value | Description |   | Direction | Value | Description | | :--- | :---: | :--- | :---: | :--- | :---: | :--- | | `VOID` | `0x00` | Void type (no arg/return) | | `IN` (Default)| `0x00` | Input arg (passed directly by value) | | `U8` / `I8` | `0x01`/`0x02` | 8-bit integer | | `OUT` | `0x40` | Output arg (passed by ptr, no initial value) | | `U16` / `I16`| `0x03`/`0x04` | 16-bit integer | | `INOUT` | `0x80` | Bidirectional arg (passed by ptr, with value) | | `U32` / `I32`| `0x05`/`0x06` | 32-bit integer | | - | - | *(e.g., `OUT U32` = 0x40 \| 0x05 = `0x45`)* | | `U64` / `I64`| `0x07`/`0x08` | 64-bit integer | | - | - | - | | `F32` / `F64`| `0x09`/`0x0A` | Float/Double precision | | - | - | - | | `BYTES` | `0x0B` | Var-length byte array (`uint8_t*`) | | - | - | - | > **Packet Assembly Key Points:** > > 1. **Implicit Pointer Conversion**: Parameters marked as `OUT` and `INOUT` are automatically **converted to pointers (addresses)** during the underlying C call. > 2. **Result Recovery**: After target function execution, the latest results of these output parameters are extracted and appended right after the function's `RetVal` in the positive response message. > 3. **Var-length Byte Array**: The payload of a `BYTES` parameter must start with a 2-byte length field `[LenH] [LenL]`. > 4. **Skipping Payload**: Pure `OUT` parameters do not need initial values, so their value payload field can be omitted in the request message (Note: `OUT BYTES` still requires 2 bytes to indicate allocation size). ### 3. Detailed Message Structures #### Read Memory (SID: `0x22`) | Direction | Byte 0 | Byte 1 ~`PTR_SIZE` | Payload (Big-Endian) | | :--- | :--- | :--- | :--- | | **Request** | `0x22` | `Addr` (Start physical address to read) | `Size` (2 bytes, number of continuous bytes to read) | | **Positive Resp** | `0x62` | `Data...` (Actual memory data stream read) | - | #### Write Memory (SID: `0x2E`) | Direction | Byte 0 | Byte 1 ~`PTR_SIZE` | Payload (Big-Endian) | | :--- | :--- | :--- | :--- | | **Request** | `0x2E` | `Addr` (Start physical address to write) | `Size` (2 bytes) + `Data...` (Continuous write stream of Size length) | | **Positive Resp** | `0x6E` | - | *(No data, indicates success)* | #### Config Signal (SID: `0x2A`) | Action | Request Format (Byte 0 =`0x2A`) | Positive Resp (`0x6A`) | | :--- | :--- | :--- | | **Add node** (`0x01`) | `[0x2A] [0x01] [Addr] [Size(2 bytes)] [Offset(2 bytes)]` | `[0x6A]` | | **Clear all** (`0x02`) | `[0x2A] [0x02]` | `[0x6A]` | | **Start/Stop cycle** (`0x03`) | `[0x2A] [0x03] [CycleMs(2 bytes)]` *(Note: No CycleMs means stop)* | `[0x6A]` | | **Enable Dyn Length** (`0x04`) | `[0x2A] [0x04] [Enable(1 byte)]` *(1:Dynamic truncation, 0:Fixed length)* | `[0x6A]` | #### Read Configured Signals (SID: `0x2B`) | Direction | Byte 0 | Byte 1 (Optional) | Payload | | :--- | :--- | :--- | :--- | | **Request** | `0x2B` | `SigID` (Target pool index. If omitted, reads all) | - | | **Positive Resp** | `0x6B` | `Payload...` (Assembled signal data snapshot, format identical to `0xAA` auto message) | - | #### Call Static (SID: `0x31`) | Direction | Byte 0 | Byte 1 | Payload | | :--- | :--- | :--- | :--- | | **Request** | `0x31` | `FuncID` (Pre-registered short alias) | `[Arg1_Val] [Arg2_Val]...` (Argument data stream) | | **Positive Resp** | `0x71` | `RetVal...` (Function's direct return value) | `[OutArg_Val]...` (Values of all OUT parameters extracted) | #### Call Dynamic (SID: `0x32`) | Direction | Byte 0 | Byte 1 ~`PTR_SIZE` | Byte (PTR+1) | Byte (PTR+2) | Payload | | :--- | :--- | :--- | :--- | :--- | :--- | | **Request** | `0x32` | `FuncAddr` (Physical address) | `RetTypeID` | `ArgCnt` | `{ [ArgX_TypeID] [ArgX_Val] }...` | | **Response** | `0x72` | `RetVal...` (Function's direct return value) | `[OutArg_Val]...` (Values of all OUT parameters extracted) | - | - | #### Config Periodic Task (SID: `0x33`) | Action | Request Format (Byte 0 =`0x33`) | Positive Resp (`0x73`) | | :--- | :--- | :--- | | **Add/Update** (`0x01`) | `[0x33] [0x01] [FuncAddr] [CycleMs(2B)] [RetTypeID] [ArgCnt] { [Arg_Type] [Arg_Val] }...` | `[0x73]` | | **Delete single** (`0x02`) | `[0x33] [0x02] [FuncAddr]` | `[0x73]` | | **Clear all** (`0x03`) | `[0x33] [0x03]` | `[0x73]` | ### 4. Negative Response Codes (NRC) Dictionary When a request format is invalid or conditions are unmet, the device rejects execution and returns `[0x7F] [Requested SID] [NRC]`. | NRC | Name | Trigger Scenario | | :--- | :--- | :--- | | `0x10` | General Reject | General rejection (unknown error or transmission locked). | | `0x12` | SubFunction Not Supported | Unsupported action code, or feature disabled due to zero capacity in configuration pool. | | `0x13` | Incorrect Message Length | Message truncated, or payload size does not match declared `TypeID` size. | | `0x31` | Out Of Range | Index out of bounds, read/write size exceeds limit, or signal/task pool is full. | | `0x33` | Memory Overlap | (Specific) Occurs during `0x2A`: Specified `Offset + Size` exceeds single frame max length. | --- ## 📊 Resource Footprint & Performance Evaluation `dtest` is designed for resource-constrained microcontrollers (MCUs), utilizing a **zero dynamic memory allocation** (Zero `malloc`) strategy. ### 1. ROM (Flash) Footprint - **Minimalist Code**: Core source `dtest.c` is only ~1K lines, zero dependencies (only ``, ``, ``). - **Extremely Low Footprint**: Under GCC `-O2`, full feature binary size is typically between **2KB ~ 4KB** (primarily occupied by stack pushing and big-endian parsing logic for dynamic function calling). ### 2. RAM (SRAM) Footprint RAM footprint is completely controlled by the user. All struct memories are statically allocated externally and injected, with no hidden overhead. - **Base Context (`dtest_ctx_t`)**: Consists of `tx_buffer` (default 256 bytes) and `cmd_queue` (default 256 bytes × depth). Base footprint ~**550 bytes**. - **Feature Pool Overhead (Enable as needed)**: - **Signal Pool** (`dtest_signal_t`): ~`16` bytes per slot. - **Static Function Pool** (`dtest_function_t`): ~`32` bytes per slot. - **Periodic Task Pool** (`dtest_periodic_t`): ~`288` bytes per slot (requires backing up all arguments for background execution). --- ## 🔀 RTOS & Bare-metal Deployment Guide The `dtest` architecture naturally supports the "Single-Producer Single-Consumer" model, ideal for complex interrupt and RTOS environments. ### 1. Receive End (ISR Safe) `dtest_receive()` uses a lock-free Ring Buffer design with no blocking or loops (just a fast `memcpy` pushing data). - **Deployment**: Highly recommended to place directly in hardware RX ISRs (UART/CAN) or high-priority network RX threads. - **Concurrency Warning**: If multiple concurrent data sources feed `dtest_receive()`, use a mutex or disable interrupts (`DISABLE_INT()`) to prevent pointer race conditions. ### 2. Execution End (Low Priority Safe) `dtest_task(delta_ms)` is the brain of the framework, executing large memory copies, parsing protocols, and dynamically invoking user functions. - **Deployment**: Due to its unpredictable execution time (depends on the invoked function), it must **NEVER** run in an interrupt context. Please place it inside: - **Bare-metal**: Inside the `while(1)` super-loop in `main()`. - **RTOS**: In a low-priority background thread (like IDLE or Debug task) with `osDelay()` and passing the elapsed `delta_ms`. ### 3. Transmit End (Async DMA Supported) Injected `tx_cb` can be synchronous or asynchronous. - If using DMA, `tx_cb` returns `DTEST_TX_PENDING`. The state machine automatically locks the internal TX buffer until `tx_check_cb` returns `DTEST_TX_OK`, preventing overwrite trampling. --- ## 🛡️ Security & Production Environment Warning (Security Warning) ⚠️ **Extremely Dangerous God Mode** `dtest` grants external interfaces unlimited access to read/write all physical memory and execute internal functions. Leaving it unrestricted in production poses catastrophic security risks. ### Recommended Protections: 1. **Conditional Compilation**: Completely strip it out in Release builds via macros: ```c #ifndef NDEBUG // Only enable in Debug mode dtest_init(&config); dtest_receive(data, len); #endif ``` 2. **Seed & Key Authentication (UDS 0x27 style)**: Implement a handshake layer in the underlying serial callback. Only forward data to `dtest_receive()` after successful authentication. 3. **Memory Barrier Limitation**: For MPU/MMU systems, restrict the execution permissions of the `dtest_task` thread to prevent accidental writes to protected regions (e.g., Bootloader sectors).