-
In the vein of "Surely this has been done before, probably many times!"
Does anyone happen to have a utility that will generate the printf()s needed to pretty-print a C structure, given the definitions from the .h file?
In other words, given:
typedef struct
{
__IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */
__IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */
__IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */
__IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */
} SysTick_Type;
I would like to get something like:
#define uint32_t_format "0x%08x"
printf("SysTick\n");
printf(" CTRL = " uint32_t_format "!< Offset: 0x000 (R/W) SysTick Control and Status Register\n", p->CTRL);
printf(" LOAD = " uint32_t_format "!< Offset: 0x004 (R/W) SysTick Reload Value Register\n", p->LOAD);
printf(" VAL = " uint32_t_format "!< Offset: 0x008 (R/W) SysTick Current Value Register\n", p->VAL);
printf(" CALIB = " uint32_t_format "!< Offset: 0x00C (R/ ) SysTick Calibration Register \n", p->CALIB);
I'm don't much care about the language the tool is written in, and it need not be all-powerful and inclusive.
(Yeah, it should work from .h files rather than from .svd or similar, since the latter can be hard to find.)
-
(For a nice collection of SVDs: https://github.com/posborne/cmsis-svd/tree/master/data )
-
For a nice collection of SVDs
I was aware of that.
I cannot find an SVD for the Renesas ra4m1 used on the new Arduino, though. (Part of the whole "Renesas docs for this chip really suck" status.)
(Web search shows some complaints about the SVD for RA chips failing verification, but apparently the "solution" to this was to remove the files entirely. :-( )
-
Parsing C header files can be hairier than it seems. To do it right without compromises really means implementing most of the front end of a compiler, including the preprocessor.
As a result, you're going to find mostly janky homemade tools that work correctly only if the header you want parsed is written a certain way, and doesn't use certain c features (bit fields, anyone?)
This is what I do instead: I write python that defines the structs I want and the same code emits the c header with the struct definitions, routines for pretty printing, and something I want but which you don't mention: routines for accessing the same structures through Python. I use the latter to make it much easier to implement tests that access binary data from my code under test.
This is actually not all that difficult to write yourself, especially if all you want is a stringifier.
-
To the extent GDB and friends are able to interpret data structures -- would it be possible/feasible to use debug info to create this?
Would be a two-pass process of course, either running code generation after compiling the project and just compiling twice when changes occur, or, as a pre-build step, building a toy module that uses the headers of interest, and obtaining debug info from that.
(I know nothing about the structure / ease of use of the data so encoded, I'm no help implementing this; just putting the thought out there.)
Tm
-
I cannot find an SVD for the Renesas ra4m1 used on the new Arduino, though. (Part of the whole "Renesas docs for this chip really suck" status.)
All their devices are grouped into a single pack - https://www.keil.arm.com/vendors/renesas/packs/ (https://www.keil.arm.com/vendors/renesas/packs/) It is stupid, just like literally everything about those devices.
(Web search shows some complaints about the SVD for RA chips failing verification, but apparently the "solution" to this was to remove the files entirely. :-( )
Almost all SVD files out there are failing verification. I doubt vendors even verify them at all, and there is no real spec for SVD files. You kind of just have to do your best guess based on the schema file.
Parsers in the tools have to be very accepting of random stuff and gracefully ignore stuff they don't understand. Most of them are fine with practical files..
-
To the extent GDB and friends are able to interpret data structures -- would it be possible/feasible to use debug info to create this?
It is indeed possible. All the info you need is in the debug info that the compiler generates. It's not easy to learn how that all works, but at least if you're using GCC, it's open source.
Another option is that compilers like clang / LLVM make some of the intermediate format steps available to other tools during compilation, and I bet that could be used to make pretty-printers. You just need to learn how that all works. :-)
-
The debug info stored in the DWARF format, but it is not trivial by any means. There are libraries for parsing it too, but it is more trouble than it is worth for this use, IMO. You also won't get the comments.
For somewhat robust generic version, parsing SVD is the easiest.
For a quick hack for well-formatted header files (as headers often are), a simple Python script would work too.
A bigger issue would be what to do with non-trivial types. What if that struct includes another struct? Print it recursively? Ignore it?
-
Quote from: djacobow on Today at 07:27:39 pm (https://www.eevblog.com/forum/index.php?topic=389668.msg5021701#msg5021701)Parsing C header files can be hairier than it seems. To do it righ
you're going to find mostly janky homemade tools that work correctly only if the header you want parsed is written a certain way, and doesn't use certain c features (bit fields, anyone?)
I'm fine with something that does 85% of the work for "written in a certain way" files. I'll get rid of bitfields manually, unless I think they're really important. And I care most about the more-or-less CMSIS-compliant header files, so that simplifies things.
It would be great if someone already had a hack that used DWARF info and spit out code that would print every structure referenced in a .elf file, but a lot less would still be useful. My normal strategy would be to create a save some EMACS keyboard macros.
I write python that defines the structs I want and the same code emits the c header
Not applicable, since what I want is to print out contents of structs that someone else has defined.
Are there SVDs for the ARM cores themselves? (ie covering my sysTick example; usually not in a vendor SVD file.)
-
Are there SVDs for the ARM cores themselves? (ie covering my sysTick example; usually not in a vendor SVD file.)
Yes, they are a part of the ARM CMSIS SVD pack. Or just get them from the source - https://github.com/ARM-software/CMSIS_5/tree/develop/Device/ARM/SVD
Wait, those files don't seem to contain anything useful. Yes, I remember now running into that some time ago. And I don't think I could find real SVD files for that, which was puzzling.
Many vendor files contain those peripherals, probably for that reason. I had to specifically filter them out in my SVD to H converter because they conflicted with the ARM header files.
-
In the vein of "Surely this has been done before, probably many times!"
Yup, google clang's "__builtin_dump_struct", it's done by the compiler so it sorts out any parsing issues for you. From the manpage (https://clang.llvm.org/docs/LanguageExtensions.html):
Examples:
struct S {
int x, y;
float f;
struct T {
int i;
} t;
};
void func(struct S *s) {
__builtin_dump_struct(s, printf);
}
Example output:
struct S {
int x = 100
int y = 42
float f = 3.141593
struct T t = {
int i = 1997
}
}
-
Oh, that's great about the clang builtin!
-
Have you looked at doxygen?
-
So I ended up using this EMACS keyboard macro, which helps a lot...
It's not fully optimized; I don't think I fully understand the ways that keyboard macros can be edited...
;; Keyboard Macro Editor. Press C-c C-c to finish; press C-x k RET to cancel.
;; Original keys: ESC d ESC d ESC d sprintf ( spbuffer , "%6s=%0x08lx SPC ( %s ) " , " SPC C-f ESC \ ESC C-s ; C-b C-w C-d C-y " , SPC p-> C-y , SPC " M-\ C-e " ) ; C-n C-a mySerial.print ( spbuffer ) ; RET
Command: last-kbd-macro
Key: none
Macro:
ESC d ;; kill-word -get rid of "__IOM uint32_t"
ESC d ;; kill-word
ESC d ;; kill-word
sprintf ;; self-insert-command * 7
( ;; c-electric-paren
spbuffer ;; self-insert-command * 8
, ;; c-electric-semi&comma
"%6s=%0x08lx ;; self-insert-command "sprintf(spbuf, "%6s=0x%08lx%s","
SPC ;; self-insert-command
( ;; c-electric-paren
%s ;; self-insert-command * 2
) ;; c-electric-paren
" ;; self-insert-command
, ;; c-electric-semi&comma
" ;; self-insert-command
SPC ;; self-insert-command
C-f ;; forward-char skip the spaces
ESC \ ;; delete-horizontal-space
ESC C-s ;; isearch-forward-regexp copy structure member name
; ;; c-electric-semi&comma into the kill buffer
C-b ;; backward-char ...
C-w ;; kill-region
C-d ;; c-electric-delete-forward
C-y ;; yank into the quoted string
" ;; self-insert-command
, ;; c-electric-semi&comma
SPC ;; self-insert-command
p-> ;; self-insert-command * 3
C-y ;; yank and again as a variable p->member
, ;; c-electric-semi&comma
SPC ;; self-insert-command
" ;; self-insert-command
M-\ ;; delete-horizontal-space and clean up the description from the comment
C-e ;; move-end-of-line
" ;; self-insert-command
) ;; c-electric-paren
; ;; c-electric-semi&comma
C-n ;; next-line
C-a ;; move-beginning-of-line
mySerial.print ;; self-insert-command * 14 and print the constructed string
( ;; c-electric-paren
spbuffer ;; self-insert-command * 8
) ;; c-electric-paren
; ;; c-electric-semi&comma
RET ;; newline
-
get rid of "__IOM uint32_t"
You do not print structs but volatile structs.
Those structs might be write only and/or undefined behavior on read. Some values might change content or status of some fifo on read, also some are integers but some are bitfields so interpretation is the key to prettyfying it.
I would go svd way. It contains more data.