2023-07-07 07:19 AM
Hi all,
I wonder if that's something that has been overlooked during a HAL code review (and could be improved in next releases)? Or can you suggest another way of tracing ALL HW registers writes in STM32 for educational/debugging purposes?
For embedded SW professionals, when writing own code, it's sometimes useful to do some kind of reverse-engineering for available standard SW modules or libraries. That can be a valuable help when their own code is not working as expected.
I wondered if there was an easy way to trace all HW registers accesses e.g. during the system startup and initialization. What I did (in a nutshell) was I instrumented SET_BIT(), CLEAR_BIT(), WRITE_REG(), CLEAR_REG(), MODIFY_REG() macros that are found in stm32l4xx.h (in case of my board) so that the macros were writing registers addresses and values in a global array. Just to illustrate the idea:
#define WRITE_REG(REG, VAL) ((REG) = (VAL)); \
if (g_logOn) { \
g_logTable[g_index].op = (uint32_t) 'W'; \
g_logTable[g_index].reg = &(REG); \
g_logTable[g_index++].val = (VAL); \
}
Then I added a simple loop in main.c to dump the contents of the global array to a terminal console. It worked and showed me a sequence of MMIO writes during clock, GPIO, UART etc. initialization in my sample project.
The problem is that the HAL source code is not consequent with using the mentioned macros for registers accesses. For example when you look into the UART driver source, you will see an extensive use of *_REG() macros, either directly or indirectly, BUT in some cases registers are written with a regular C language expression, e.g.
huart->Instance->BRR = value;
Of course it means that my simple instrumentation is not catching all register write operations...
(RR)
2023-12-14 08:33 AM
Problem is that's not needed for 99.999% of use cases, where the most efficient means of writing, folding, and in-lining of code is desired.
There's a market for hardware trace, but honestly it's pretty small.
2023-12-14 10:47 AM
Depending on which STM32 (you haven't specified this), you can place a data breakpoint (a.k.a. watchpoint) on areas of interest (limited size, so can't capture *every* register at once, but may be able to do for one peripheral). If not running with debugger, you can then use the Debug Monitor interrupt to capture/log these acesses. The breaks are not immediate, so it's not very straightforward to decipher, but with some additional work may generate a comprehensive list of accesses.
OTOH, staring at the code may be more instructive.
JW
2023-12-14 11:00 AM - edited 2023-12-14 02:17 PM
@RudolphR This makes sense (I had to do something similar few times in other projects) but not feasible here because of, as you've noticed, in a lot of source files the registers are accessed via normal C expressions. What is more important, normal C expressions are easier for a human to read.
IMHO these days a better way would be using instrumentation of an open-source C compiler: patch the compiler to add the trace code (should be doable with clang, not sure about gcc). The registers are defined as compile-time constants defined in certain .h files (typecast'ed to C struct pointers) - this is how the instrumentation can detect them. And this will work with any Cortex-Mx derivative from any vendor, and possibly with other architectures.
-- pa
2023-12-14 01:35 PM
@Pavel A. I still think it's just a lack of a diligent code review for HAL code authors, because there is no reason why they define and use their register-access macros in the most of cases, but they randomly fail to use them in some places.
2023-12-14 01:51 PM - edited 2023-12-14 02:05 PM
You're likely right, but even so it won't help. Also, many customers will object to massive changes in existing ST libraries, especially when readability of code suffers.
In other words: if your work depends on this, expectation that they will do this in time frame of ~ few months is not realistic. Don't build on it.
2023-12-14 01:59 PM
@Tesla DeLorean The macros I mentioned have nothing to do with the code efficiency or readibility. They easily and obviously unfold to the same single line of code that writes/reads a register. It rather looks like a poor job that code reviewers did on this code.