Skip to content

Commit

Permalink
Merge pull request #12 from klonyyy/devel
Browse files Browse the repository at this point in the history
Trace Viewer Module
  • Loading branch information
klonyyy authored Sep 14, 2023
2 parents befc31c + 35641e6 commit 4ee8555
Show file tree
Hide file tree
Showing 94 changed files with 12,757 additions and 1,699 deletions.
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
github: [klonyyy]
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
build/*
build_test/*
.vscode/*

**/[Dd]ebug/
Expand Down
52 changes: 42 additions & 10 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,24 @@ endif()

project(STMViewer)

set(STMVIEWER_VERSION 0.1.2)
set(STMVIEWER_VERSION 0.2.0)

set(CMAKE_BUILD_TYPE Release)
set(CMAKE_CXX_STANDARD 17)

if(CMAKE_BUILD_TYPE STREQUAL "ReleaseWithDbg")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g")
endif()

add_compile_options(-Wall
-Wextra
-Wpedantic
-Wno-unused-parameter
-Wno-missing-field-initializers)

if(UNIX)
add_custom_target(addGitVersion COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/launch/addGitVersion.sh)
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules)
include(${CMAKE_MODULE_PATH}/Findlibusb.cmake)
find_package(libusb REQUIRED)
Expand All @@ -40,6 +47,13 @@ if(UNIX)
endif()

if(WIN32)
# use bat script only if not cross-compiling
if("${PLATFORM}" STREQUAL "native")
add_custom_target(addGitVersion COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/launch/addGitVersion.bat)
else()
add_custom_target(addGitVersion COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/launch/addGitVersion.sh)
endif()

enable_language("RC")
set(ICON_RC "${CMAKE_CURRENT_SOURCE_DIR}/launch/icon.rc")
set(GLFW3_WINDOWS ${CMAKE_CURRENT_SOURCE_DIR}/third_party/GLFW/lib/windows/glfw3.dll)
Expand All @@ -50,19 +64,26 @@ if(WIN32)
set(INSTALL_PATH bin)
endif()



set(PROJECT_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/ElfReader/ElfReader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Gui/Gui.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Gui/GuiPlots.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Gui/GuiSwoPlots.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Gui/GuiSwoControl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/TargetMemoryHandler/TargetMemoryHandler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/TargetMemoryHandler/StlinkHandler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Plot/Plot.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Variable/Variable.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/ConfigHandler/ConfigHandler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Plot/PlotHandler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/FileHandler/NFDFileHandler.cpp)
${CMAKE_CURRENT_SOURCE_DIR}/src/FileHandler/NFDFileHandler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/ImguiPlugins/ImguiPlugins.cpp
${CMAKE_CURRENT_SOURCE_DIR}/third_party/stlink/inc/spdlogWrapper.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/PlotHandler/PlotHandlerBase.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/PlotHandler/PlotHandler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/PlotHandler/TracePlotHandler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/TraceReader/TraceReader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/TraceReader/StlinkTraceDevice.cpp)

set(IMGUI_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/third_party/imgui/imgui.cpp
Expand All @@ -78,12 +99,20 @@ set(IMPLOT_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/third_party/implot/implot_demo.cpp
${CMAKE_CURRENT_SOURCE_DIR}/third_party/implot/implot_items.cpp)

set_source_files_properties(
${IMPLOT_SOURCES}
PROPERTIES
COMPILE_FLAGS "-w"
)

add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/third_party/nfd/)

set(EXECUTABLE ${CMAKE_PROJECT_NAME})

add_executable(${EXECUTABLE} ${PROJECT_SOURCES} ${IMGUI_SOURCES} ${IMPLOT_SOURCES} ${ICON_RC})

add_dependencies(${EXECUTABLE} addGitVersion)

string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" _ "${STMVIEWER_VERSION}")
target_compile_definitions(${EXECUTABLE}
PRIVATE STMVIEWER_VERSION_MAJOR=${CMAKE_MATCH_1}
Expand All @@ -100,10 +129,13 @@ target_include_directories(${EXECUTABLE} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/Variable
${CMAKE_CURRENT_SOURCE_DIR}/src/ConfigHandler
${CMAKE_CURRENT_SOURCE_DIR}/src/ImguiPlugins
${CMAKE_CURRENT_SOURCE_DIR}/src/FileHandler)
${CMAKE_CURRENT_SOURCE_DIR}/src/FileHandler
${CMAKE_CURRENT_SOURCE_DIR}/src/PlotHandler
${CMAKE_CURRENT_SOURCE_DIR}/src/TraceReader
${CMAKE_CURRENT_SOURCE_DIR}/src/RingBuffer)

target_include_directories(${EXECUTABLE} SYSTEM PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/third_party/stlink/inc
${CMAKE_CURRENT_SOURCE_DIR}/third_party/stlink/inc/
${LIBUSB_INCLUDE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/third_party/imgui/backends/
${CMAKE_CURRENT_SOURCE_DIR}/third_party/imgui/
Expand All @@ -117,7 +149,7 @@ target_include_directories(${EXECUTABLE} SYSTEM PRIVATE
if(UNIX)
target_link_libraries(${EXECUTABLE} ${STLINK_LINUX} ${LIBUSB_LIBRARY} ${SPDLOG_LINUX} pthread dl GL glfw nfd)
elseif(WIN32)
target_link_libraries(${EXECUTABLE} ${GLFW3_WINDOWS} ${STLINK_WINDOWS} ${LIBUSB_WINDOWS} ${SPDLOG_WINDOWS} -static opengl32 nfd -static-libstdc++ -static-libgcc)
target_link_libraries(${EXECUTABLE} ${GLFW3_WINDOWS} ${STLINK_WINDOWS} ${LIBUSB_WINDOWS} ${SPDLOG_WINDOWS} -static ssp opengl32 nfd -static-libstdc++ -static-libgcc)
endif()

if(WIN32)
Expand All @@ -139,6 +171,7 @@ add_custom_command(TARGET ${EXECUTABLE} POST_BUILD

install(TARGETS ${EXECUTABLE} RUNTIME DESTINATION ${INSTALL_PATH} COMPONENT applications)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/imgui.ini DESTINATION ${INSTALL_PATH})
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/third_party/stlink/chips DESTINATION ${INSTALL_PATH})

if(WIN32)
if(PRODUCTION)
Expand All @@ -155,13 +188,12 @@ if(WIN32)
endif()

if(UNIX)
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/third_party/stlink/chips DESTINATION ${INSTALL_PATH})
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/launch/icon.png DESTINATION ${INSTALL_PATH})
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/launch/STMViewer.desktop
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
DESTINATION ${DESKTOP_FILE_PATH})
set(CPACK_GENERATOR "DEB")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libusb-1.0-0-dev, libglfw3-dev, libgtk-3-dev, gdb, libspdlog-dev")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libglfw3 | libglfw3-wayland, libgl1, libglib2.0-0, libgtk-3-0, libstdc++6, libusb-1.0-0, gdb")
endif()

set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
Expand Down
93 changes: 78 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,48 +1,111 @@
![example workflow](https://github.com/klonyyy/STMViewer/actions/workflows/build.yaml/badge.svg)

# STMViewer
An open-source GUI tool for viewing and manipulating variables data using debug interface and st-link programmer on STM32 microcontrollers.
STMViewer is an open-source GUI debug tool for STM32 microcontrollers that consists of two modules:
1. Variable Viewer - used for viewing, logging, and manipulating variables data in realtime using debug interface (SWDIO / SWCLK / GND)
2. Trace Viewer - used for graphically representing real-time SWO trace output (SWDIO / SWCLK / SWO / GND)

The only piece of hardware required is an ST-Link programmer.

![_](./docs/STMViewer.gif)
## Introduction

STMViewer can be used to visualize your embedded application data in real-time with no overhead in a non-intrusive way. The software works by reading variables' values directly from RAM using the ST-link programmer debug interface. Addresses are read from the *.elf file which is created when you build your embedded project. This approach's main downside is that the object's address must stay constant throughout the whole program's lifetime, which means the object has to be global. Even though it seems to be a small price to pay in comparison to running some debug protocol over for example UART which is also not free in terms of intrusiveness.
### Variable Viewer
![_](./docs/VarViewer.gif)

Variable Viewer can be used to visualize your embedded application data in real time with no overhead in a non-intrusive way. The software works by reading variables' values directly from RAM using the ST-link programmer debug interface. Addresses are read from the *.elf file which is created when you build your embedded project. This approach's main downside is that the object's address must stay constant throughout the whole program's lifetime, which means the object has to be global. Even though it seems to be a small price to pay in comparison to running some debug protocol over for example UART which is also not free in terms of intrusiveness.

Variable Viewer is a great tool for debugging, but might be of little use with highly optimized release builds (which usually lack debug info), or very fast-changing signals.

### Trace Viewer
![_](./docs/TraceViewer.gif)

STMViewer is a great tool for debugging, but might be of little use with highly optimized release builds (which usually lack debug info).
Trace Viewer is a new module that lets you visualize SWO trace data. It can serve multiple purposes such as profiling a function execution time, confirming the timer's interrupt frequency, or displaying very fast signals. All this is possible thanks to hardware trace peripherals embedded into Cortex M3/M4/M7/M33 cores. For prerequisites and usage please see the Quick Start section.

TraceViewer is not influenced by optimizations, which means it is a great tool to use for profiling on release builds. Moreover it has a very low influence on the program execution as each datapoint is a single register write.

## Installation

Linux:
1. Download the *.deb package and install it using:
`sudo apt install ./STMViewer-x.y.z-Linux.deb`
All dependencies should be installed and you should be ready to go.
Optional: make sure you have the rights to access usb port. When installing ST's software such as Cube Programmer it will most probably also install needed udev rules.

Windows:
1. Make sure you've got GDB installed and added to your PATH (the easiest way is to install using [MinGW](https://www.mingw-w64.org))
1. Make sure you've got GDB installed (v12.1 or later) and added to your PATH (the easiest way is to install using [MinGW](https://www.mingw-w64.org))
2. Download and run the STMViewer installer. Make sure the ST-link is in "STM32 Debug + Mass Storage + VCP" mode as for some reason "STM32 Debug + VCP" throws libusb errors on Windows. This needs further investigation.

You can assing the external GPU to STMViewer for improved performance.
You can assign the external GPU to STMViewer for improved performance.

## Quick Start
## Quick Start

### Variable Viewer
1. Open Options->Acqusition Settings window in the top menu.
2. Select your project's elf file. Make sure the project is compiled in debug mode. Click done.
3. Click 'add variable' button to add new variable. Double-click to change its name to one of your global variables. If you're using structs or classes in C++ make sure to add its name before the variable, exactly like you'd refer to it in the code (example myClass.var, or namespace::myClass.var).
3. Click the 'add variable' button to add a new variable. Double-click to change its name to one of your global variables. If you're using structs or classes in C++ make sure to add its name before the variable, exactly like you'd refer to it in the code (for example myClass.var, or namespace::myClass.var).
4. After adding all variables click 'update variable addresses'. The type and address of the variables you've added should change from "NOT FOUND!" to a valid address based on the *.elf file you've provided.
5. Drag and drop the variable to the plot area.
6. Make sure the ST-Link is connected. Download your executable to the microcontroller and press the "STOPPED" button.

In case of any problems, please try the test/STMViewer_test CubeIDE project and the corresponding STMViewer_test.cfg project file. Please remember to build the project and update the elf file path in the Options -> Acqusition Settings.
In case of any problems, please try the test/STMViewer_test CubeIDE project and the corresponding STMViewer_test.cfg project file. Please remember to build the project and update the elf file path in the Options -> Acquisition Settings.

Example project with STMViewer config file is located in test/STMViewer_test directory.

### Trace Viewer
1. Turn on the SWO pin functionality - in CubeMX System Core -> SYS Mode and Configuration -> choose Trace Asynchronous Sw
2. Place enter and exit markers in the code you'd like to profile. Example for digital data:
```
ITM->PORT[x].u8 = 0xaa; //enter tag 0xaa - plot state high
foo();
ITM->PORT[x].u8 = 0xbb; //exit tag 0xbb - plot state low
```
And for tracing "analog" signals you can use:
```
float a = sin(10.0f * i); // some super fast signal to trace
ITM->PORT[x].u32 = *(uint32_t*)&a; // type-punn to desired size: sizeof(float) = sizeof(uint32_t)
```
or

```
uint16_t a = getAdcSample(); // some super fast signal to trace
ITM->PORT[x].u16 = a; // type-punn to desired size
```

The ITM registers are defined in CMSIS headers so no additional includes should be necessary.

3. Compile and download the program to your STM32 target.
4. In the `Settings` window type in the correct System Core Clock value in kHz (very important as it affects the timebase)
5. Try different trace prescallers that result in a trace speed lower than the max trace speed of your programmer (for example STLINK V2 can read trace up to 2Mhz, whereas ST-Link V3 is theoretically able to do 24Mhz). Example:
- System Core Clock is 160 000 kHz (160 Mhz)
- We're using ST-link V2 so the prescaler should be at least 160 Mhz / 2 Mhz = 80
6. Configure "analog" channels types according to the type used in your code.
7. Press the "STOPPED" button to start recording.

Example project with STMViewer config file is located in test/STMViewer_test directory.

FAQ and common issues:
1. Problem: My trace doesn't look like it's supposed to and I get a lot of error frames
Answer: Try lowering the trace prescaller and check the SWO pin connection - the SWO pin output is a fast signal and it shouldn't be too long.

2. Problem: My trace looks like it's supposed to but I get the "delayed timestamp 3" indicator
Answer: Try logging fewer channels simultaneously. It could be that you've saturated the SWO pin bandwidth.

3. Problem: My trace looks like it's supposed to but I get the "delayed timestamp 1" indicator
Answer: This is not a critical error, however, you should be cautious as some of the trace frames may be delayed. To fix try logging fewer channels simultaneously.


## Why
I'm working in the motor control industry where it is crucial to visualize some of the process data in real-time. Since The beginning, I was working with [STMStudio](https://www.st.com/en/development-tools/stm-studio-stm32.html), which is, or rather was, a great tool. Unfortunately, ST stopped supporting it which means there are some annoying bugs, and it doesn't work well with mangled c++ object names. Also, it works only on Windows which is a big downside. If you've ever used it you probably see how big of an inspiration it was for creating STMViewer :) ST's other project in this area - [Cube Monitor](https://www.st.com/en/development-tools/stm32cubemonitor.html) - is simply underdeveloped, and seems to be abandoned as well so it is simply useless.
I'm working in the motor control industry where it is crucial to visualize some of the process data in real-time. Since the beginning, I have been working with [STMStudio](https://www.st.com/en/development-tools/stm-studio-stm32.html), which is, or rather was a great tool. Unfortunately, ST stopped supporting it which means there are some annoying bugs, and it doesn't work well with mangled c++ object names. Also, it works only on Windows which is a big downside. If you've ever used it you probably see how big of an inspiration it was for creating STMViewer :) ST's other project in this area - [Cube Monitor](https://www.st.com/en/development-tools/stm32cubemonitor.html) - has, in my opinion, too much overhead on adding variables, plots and writing values. I think it's designed for creating dashboards, and thus it serves a very different purpose. On top of that, I think the plot manipulation is much worse compared to STMStudio or STMViewer.

Since the Trace Viewer module was added STMViewer has a unique property of displaying SWO trace data which both CubeMonitor and STMStudio currently lack.

## 3rd party projects used in STMViewer

1. [stlink](https://github.com/stlink-org/stlink)
2. [imgui](https://github.com/ocornut/imgui)
3. [implot](https://github.com/epezent/implot)
4. [mINI](https://github.com/pulzed/mINI)
5. [nfd](https://github.com/btzy/nativefiledialog-extended)
6. [spdlog](https://github.com/gabime/spdlog)
2. [libusb](https://github.com/libusb/libusb)
3. [imgui](https://github.com/ocornut/imgui)
4. [implot](https://github.com/epezent/implot)
5. [mINI](https://github.com/pulzed/mINI)
6. [nfd](https://github.com/btzy/nativefiledialog-extended)
7. [spdlog](https://github.com/gabime/spdlog)

14 changes: 0 additions & 14 deletions TODO.md

This file was deleted.

Binary file removed docs/STMViewer.gif
Binary file not shown.
Binary file removed docs/STMViewer.png
Binary file not shown.
Binary file added docs/TraceViewer.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/VarViewer.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 24 additions & 7 deletions imgui.ini
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[Window][DockSpaceViewport_11111111]
Pos=0,19
Size=1500,981
Size=1920,998
Collapsed=0

[Window][Debug##Default]
Expand All @@ -10,21 +10,21 @@ Collapsed=0

[Window][ImPlot Demo]
Pos=0,19
Size=522,981
Size=522,998
Collapsed=0
DockId=0x00000001,0

[Window][Plots]
Pos=524,19
Size=976,981
Size=1396,998
Collapsed=0
DockId=0x00000002,0

[Window][VarViewer]
[Window][Var Viewer]
Pos=0,19
Size=522,981
Size=522,998
Collapsed=0
DockId=0x00000001,1
DockId=0x00000001,0

[Window][Acqusition Settings]
Pos=612,281
Expand All @@ -51,6 +51,23 @@ Pos=289,209
Size=570,442
Collapsed=0

[Window][Trace Plots]
Pos=0,0
Size=1920,1017
Collapsed=0
DockId=0x00000002,0

[Window][Trace Viewer]
Pos=0,19
Size=522,998
Collapsed=0
DockId=0x00000001,1

[Window][Save?]
Pos=60,60
Size=16,35
Collapsed=0

[Table][0xC16CD03B,3]
Column 0 Weight=1.0000
Column 1 Weight=1.0000
Expand Down Expand Up @@ -81,7 +98,7 @@ Column 2 Weight=1.0000
Column 3 Weight=1.0000

[Docking][Data]
DockSpace ID=0x8B93E3BD Window=0xA787BDB4 Pos=0,19 Size=1500,981 Split=X Selected=0x350511F3
DockSpace ID=0x8B93E3BD Window=0xA787BDB4 Pos=0,19 Size=1920,998 Split=X Selected=0x350511F3
DockNode ID=0x00000001 Parent=0x8B93E3BD SizeRef=522,981 Selected=0x350511F3
DockNode ID=0x00000002 Parent=0x8B93E3BD SizeRef=1396,981 CentralNode=1 Selected=0xE8A2D1AC

3 changes: 2 additions & 1 deletion launch/STMViewer.desktop
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ Type=Application
Name=STMViewer
Path=/usr/local/STMViewer
Icon=/usr/local/STMViewer/icon.png
Exec=/usr/local/STMViewer/STMViewer
Exec=/usr/local/STMViewer/STMViewer
Categories=Development;
1 change: 1 addition & 0 deletions launch/addGitVersion.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
git log --pretty=format:"static const char* GIT_HASH = \"%%H\";" -n 1 > ../src/gitversion.hpp
6 changes: 4 additions & 2 deletions launch/addGitVersion.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#!/bin/bash
cd ..
git log --pretty=format:'#define GIT_INFO_PRESENT%n static const char* GIT_HASH = "%H";' -n 1 > src/gitversion.hpp
cd "$(dirname "$0")"
cd ../../STMViewer/
ls
git log --pretty=format:'static const char* GIT_HASH = "%H";' -n 1 > src/gitversion.hpp
1 change: 1 addition & 0 deletions launch/launch.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
C:\\msys64\\mingw64\\bin\\gdbserver.exe localhost:2000 ./build/STMViewer.exe
Loading

0 comments on commit 4ee8555

Please sign in to comment.