mirror of
https://github.com/rfivet/stm32bringup.git
synced 2025-02-21 05:37:09 -05:00
350 lines
10 KiB
HTML
350 lines
10 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>2.6 uptime</title>
|
||
<link type="text/css" rel="stylesheet" href="style.css">
|
||
</head>
|
||
<body>
|
||
<h1>2.6 uptime</h1>
|
||
|
||
It’s time to throw away the prototype and write a production version of
|
||
<code>uptime</code>. There is several things I want to straighten up in
|
||
<b>uptime.1.c</b>:
|
||
<ul>
|
||
<li> <code>main()</code> is using assembly code to wait for interrupt.
|
||
Definitively not high level C.
|
||
|
||
<li> Also not high level C the lines <code>kputu( last) ; puts( ” sec”) ;</code>.
|
||
I should have <code>printf( “%u sec\n”, last) ;</code> instead.
|
||
|
||
<li> <code>kputc()</code> function prototype and the external variable
|
||
declaration for <code>uptime</code> should be included as a C header file.
|
||
</ul>
|
||
Similar to what I did when I split the functionalities between files
|
||
according to the three stages of execution (boot, init, main), I will
|
||
reorganize the code according to three categories (system, library,
|
||
application).
|
||
|
||
<h2>System</h2>
|
||
|
||
First I make clear what the system interface is by writing the header
|
||
<b>system.h</b>. Here belong the global variables external declarations and the
|
||
function prototypes.
|
||
|
||
<pre>
|
||
/* system.h -- system services */
|
||
|
||
extern volatile unsigned uptime ; /* seconds elapsed since boot */
|
||
|
||
int init( void) ; /* System initialization, called once at startup */
|
||
|
||
void kputc( unsigned char c) ; /* character output */
|
||
int kputs( const char s[]) ; /* string output */
|
||
void yield( void) ; /* give way */
|
||
</pre>
|
||
|
||
Next, I make a revision of <b>uplow.1.c</b> by making a copy into
|
||
<b>uplow.2.c</b>.
|
||
<p>
|
||
I include <b>system.h</b> which is the interface that <b>uplow.2.c</b>
|
||
implements. I will have several implementations of the same interface,
|
||
so <b>system.h</b> is not just the interface published by <b>uplow.2.c</b>,
|
||
it’s <b>uplow.2.c</b> which is an implementation of <b>system.h</b>.
|
||
|
||
<pre>
|
||
#include "system.h" /* implements system.h */
|
||
</pre>
|
||
|
||
I extract the code for <b>puts()</b> as it is a library function that doesn’t
|
||
really belong to the system.
|
||
<p>
|
||
I add the implementation of <b>kputs()</b> and <b>yield()</b>.
|
||
|
||
<pre>
|
||
int kputs( const char s[]) { /* string output */
|
||
int cnt = 0 ;
|
||
int c ;
|
||
|
||
while( (c = *s++) != 0) {
|
||
kputc( c) ;
|
||
cnt += 1 ;
|
||
}
|
||
|
||
return cnt ;
|
||
}
|
||
|
||
void yield( void) { /* give way */
|
||
__asm( "WFI") ; /* Wait for System Tick Interrupt */
|
||
}
|
||
</pre>
|
||
|
||
<h2>Library</h2>
|
||
|
||
I create the implementation of <code>printf()</code> in <b>printf.c</b>.
|
||
<ul>
|
||
<li> It uses the system interface <b>system.h</b>.
|
||
|
||
<li> I have eliminated the recursivity from my previous <code>kputu()</code>
|
||
version by adding characters at the beginning of a string.
|
||
|
||
<li> <code>kputu()</code> takes one additional divider parameter, so it can be
|
||
used to print unsigned integer in various format like octal, decimal and
|
||
hexadecimal. Current implementation will work for base 8 to 16, it won’t
|
||
work for binary or base 36.
|
||
|
||
<li> <code>kputi()</code> outputs signed integer.
|
||
|
||
<li> <code>printf()</code> implements a subset of the format interpreter: %%,
|
||
%c, %d, %i, %o, %s, %u, %x, %X.
|
||
</ul>
|
||
<pre>
|
||
/* printf.c -- format and print data */
|
||
#include <stdarg.h>
|
||
#include <stdio.h>
|
||
#include "system.h" /* kputc(), kputs() */
|
||
|
||
static int kputu( unsigned u, unsigned d) {
|
||
char s[ 12] ; /* room for 11 octal digit + EOS */
|
||
char *p = &s[ sizeof s - 1] ; /* point to last byte */
|
||
|
||
*p = 0 ; /* null terminated string */
|
||
do {
|
||
unsigned r = u % d ;
|
||
u /= d ;
|
||
*--p = "0123456789ABCDEF"[ r] ;
|
||
} while( u) ;
|
||
|
||
return kputs( p) ;
|
||
}
|
||
|
||
static int kputi( int i) {
|
||
int flag = i < 0 ;
|
||
if( flag) {
|
||
i = -i ;
|
||
kputc( '-') ;
|
||
}
|
||
|
||
return flag + kputu( i, 10) ;
|
||
}
|
||
|
||
int printf( const char *fmt, ...) {
|
||
va_list ap ;
|
||
int cnt = 0 ;
|
||
int c ; /* current char in format string */
|
||
va_start( ap, fmt) ;
|
||
|
||
while( ( c = *fmt++) != 0)
|
||
if( c != '%') {
|
||
cnt += 1 ; kputc( c) ;
|
||
} else if( ( c = *fmt++) == 0) {
|
||
cnt += 1 ; kputc( '%') ;
|
||
break ;
|
||
} else
|
||
switch( c) {
|
||
case 'c':
|
||
cnt += 1 ; kputc( va_arg( ap, int /* char */)) ;
|
||
break ;
|
||
case 'o':
|
||
cnt += kputu( va_arg( ap, unsigned), 8) ;
|
||
break ;
|
||
case 'u':
|
||
cnt += kputu( va_arg( ap, unsigned), 10) ;
|
||
break ;
|
||
case 'x':
|
||
case 'X':
|
||
cnt += kputu( va_arg( ap, unsigned), 16) ;
|
||
break ;
|
||
case 'i':
|
||
case 'd':
|
||
cnt += kputi( va_arg( ap, int)) ;
|
||
break ;
|
||
case 's':
|
||
cnt += kputs( va_arg( ap, char *)) ;
|
||
break ;
|
||
default:
|
||
cnt += 1 ; kputc( '%') ;
|
||
/* fallthrough */
|
||
case '%':
|
||
cnt += 1 ; kputc( c) ;
|
||
}
|
||
|
||
va_end( ap) ;
|
||
return cnt ;
|
||
}
|
||
</pre>
|
||
|
||
<h2>Application</h2>
|
||
|
||
I write my final version of uptime in <b>uptime.c</b>.
|
||
<ul>
|
||
<li> It uses the system interface and standard library.
|
||
|
||
<li> Instead of a count of seconds elapsed it displays a breakdown in week,
|
||
days, hours, minutes and seconds.
|
||
</ul>
|
||
<pre>
|
||
/* uptime.c -- tells how long the system has been running */
|
||
|
||
#include <stdio.h>
|
||
#include "system.h" /* uptime, yield() */
|
||
|
||
static void display( unsigned u, const char *s) {
|
||
if( u)
|
||
printf( " %d %s%s", u, s, &"s"[ u <= 1]) ;
|
||
}
|
||
|
||
int main( void) {
|
||
unsigned last = 0 ;
|
||
|
||
for( ;;)
|
||
if( last != uptime) {
|
||
unsigned w, d, h, m ,s ;
|
||
|
||
last = uptime ;
|
||
d = h = m = 0 ;
|
||
s = last % 60 ;
|
||
w = last / 60 ;
|
||
if( w) {
|
||
m = w % 60 ;
|
||
w /= 60 ;
|
||
if( w) {
|
||
h = w % 24 ;
|
||
w /= 24 ;
|
||
if( w) {
|
||
d = w % 7 ;
|
||
w /= 7 ;
|
||
}
|
||
}
|
||
}
|
||
|
||
printf( "up") ;
|
||
display( w, "week") ;
|
||
display( d, "day") ;
|
||
display( h, "hour") ;
|
||
display( m, "minute") ;
|
||
display( s, "second") ;
|
||
printf( "\n") ;
|
||
} else
|
||
yield() ; /* Wait for System Tick Interrupt */
|
||
}
|
||
</pre>
|
||
|
||
<h2>Build</h2>
|
||
|
||
To build I add the composition in <b>Makefile</b>.
|
||
|
||
<pre>SRCS = startup.c uplow.2.c uptime.c printf.c</pre>
|
||
|
||
Unfortunately, the build fails at the link phase.
|
||
|
||
<pre>
|
||
$ make
|
||
f030f4.elf
|
||
D:\Program Files (x86)\GNU Arm Embedded Toolchain\arm-gnu-toolchain-14.2.rel1-mi
|
||
ngw-w64-i686-arm-none-eabi\bin\arm-none-eabi-ld.exe: uptime.o: in function `main
|
||
':
|
||
D:\home\Projects\stm32bringup/uptime.c:41:(.text.startup+0xa4): undefined refere
|
||
nce to `putchar'
|
||
make: *** [Makefile:53: f030f4.elf] Error 1
|
||
</pre>
|
||
|
||
The linker found a reference to <code>putchar()</code> at line 41 of
|
||
<b>uptime.c</b>.
|
||
|
||
<pre>
|
||
printf( "\n") ;
|
||
</pre>
|
||
|
||
I haven’t used <code>putchar()</code> in my code and line 41 is a
|
||
<code>printf( "\n")</code> that can be optimized to a
|
||
<code>putchar( '\n')</code>. This must be some high level C optimization of gcc.
|
||
<p>
|
||
I add the code for <code>putchar()</code> in <b>putchar.c</b> as it is a
|
||
standard library function.
|
||
|
||
<pre>
|
||
/* putchar.c -- write a character to stdout */
|
||
#include <stdio.h>
|
||
#include "system.h" /* kputc() */
|
||
|
||
int putchar( int c) {
|
||
kputc( c) ;
|
||
return c ;
|
||
}
|
||
</pre>
|
||
|
||
Updating <b>Makefile</b> by adding <code>putchar.c</code> to the composition.
|
||
|
||
<pre>SRCS = startup.c uplow.2.c uptime.c printf.c putchar.c</pre>
|
||
|
||
The build now complete successfully.
|
||
|
||
<pre>
|
||
$ make
|
||
f030f4.elf
|
||
text data bss dec hex filename
|
||
1317 0 8 1325 52d f030f4.elf
|
||
f030f4.hex
|
||
f030f4.bin
|
||
</pre>
|
||
|
||
By checking the map file provided by the linker, I can see that the
|
||
number of low level modules referred by the code generated by the
|
||
compiler has increased. Both integer and unsigned division but also some
|
||
code to handle <code>switch()</code> statement are now referenced.
|
||
|
||
<pre>
|
||
Archive member included to satisfy reference by file (symbol)
|
||
|
||
D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-mi
|
||
ngw-w64-i686-arm-none-eabi/lib/gcc/arm-none-eabi/14.2.1/thumb/v6-m/nofp\libgcc.a
|
||
(_thumb1_case_sqi.o)
|
||
printf.o (__gnu_thumb1_case_sqi)
|
||
D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-mi
|
||
ngw-w64-i686-arm-none-eabi/lib/gcc/arm-none-eabi/14.2.1/thumb/v6-m/nofp\libgcc.a
|
||
(_udivsi3.o)
|
||
uptime.o (__aeabi_uidiv)
|
||
D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-mi
|
||
ngw-w64-i686-arm-none-eabi/lib/gcc/arm-none-eabi/14.2.1/thumb/v6-m/nofp\libgcc.a
|
||
(_divsi3.o)
|
||
uptime.o (__aeabi_idiv)
|
||
D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-mi
|
||
ngw-w64-i686-arm-none-eabi/lib/gcc/arm-none-eabi/14.2.1/thumb/v6-m/nofp\libgcc.a
|
||
(_dvmd_tls.o)
|
||
D:/Program Files (x86)/GNU Arm Embedded Toolchain/
|
||
arm-gnu-toolchain-14.2.rel1-mingw-w64-i686-arm-none-eabi/lib/gcc/arm-none-eabi/1
|
||
4.2.1/thumb/v6-m/nofp\libgcc.a(_udivsi3.o) (__aeabi_idiv0)
|
||
</pre>
|
||
|
||
<h2>Test</h2>
|
||
|
||
I flash the board and start execution, the output works as expected.
|
||
<p>
|
||
<img alt="uptime" src="img/26_uptime.png">
|
||
<p>
|
||
It will take a while to see the days and weeks counts appear, so I will
|
||
need to power the board independently from it’s serial interface. For
|
||
test purpose I fast forward the execution by using a bigger value for
|
||
the increment of <code>uptime</code> in <code>SysTick_handler()</code>.
|
||
|
||
<h2>Checkpoint</h2>
|
||
|
||
Rereading the code while writing this web page, I found a typo in the
|
||
week calculation. After that I retested with a bigger time increment to
|
||
make sure days and weeks values are correct. It’s also clear that the
|
||
test coverage for the printf format interpreter is not sufficient as I have
|
||
coded more than is necessary to implement <b>uptime</b>.
|
||
<p>
|
||
I didn’t expect gcc to optimize calls to high level C functions,
|
||
replacing some <code>printf()</code> by <code>putchar()</code>, thus forcing me
|
||
to write additional code. So far I am not concerned by execution speed, so this
|
||
type of optimization is a bit counter productive.
|
||
<p>
|
||
<a href="27_library.html">Next</a>, I will make sure that what belongs to the
|
||
library category fits in an actual library file.
|
||
|
||
<hr>© 2020-2025 Renaud Fivet
|
||
</body>
|
||
</html>
|