Commit 16208332 authored by Alessandro Rubini's avatar Alessandro Rubini

Merge branch 'pp_printf'

parents 5c3ae852 31a25094
mainmenu "WR PTP Core software configuration"
config MPRINTF
boolean "Use the old mprintf implementation for printf
default y
help
wrpc-sw has traditionally used mprintf as its printf engine.
Unfortunately, the code base has unclear copyright status,
and we are now able to run pp_printf instead. Such implementation
however has had little testing in wrpc-sw by now. Mprintf
is scheduled to be removed in the future, as soon as pp_printf
is more tested on the field.
If unsure, say y for the time being.
config PP_PRINTF
boolean
default !MPRINTF
choice
prompt "Implementation of pp_printf"
depends on PP_PRINTF
default PRINTF_XINT
config PRINTF_FULL
bool "full"
help
This selects an implementation that supports all standard
formats with modifiers, like "%08X" and "%.5s". It costs
around 2k of compiled code more than XINT.
See pp_printf/README for details.
config PRINTF_XINT
bool "hex-and-int"
help
This selects a printf that can only print decimal and hex
numbers, without obeying the format modifiers. %c and %s are
supported too, and %p is equivalente to %x.
See pp_printf/README for details.
config PRINTF_MINI
bool "minimal"
help
This selects a printf that prints all integers as hex,
without obeying the format modifiers. %c and %s are
supported too. See pp_printf/README for details.
This is not probably what you want for wrpc-sw.
config PRINTF_NONE
bool "empty"
help
This selects a printf that prints the format string
alone and ignores all further arguments. Minimal size,
but not suited for wrpc-sw. See pp_printf/README for details.
endchoice
config PRINT_BUFSIZE
depends on PP_PRINTF
int "Size for the temporary output string of pp_printf"
default 128
config ETHERBONE
boolean "Compile Etherbone support in wrpc-sw"
help
......
......@@ -20,6 +20,8 @@ obj-y += wrc_main.o wrc_ptp.o monitor/monitor.o
cflags-y = -include $(CURDIR)/include/generated/autoconf.h \
-Iinclude -I.
cflags-$(CONFIG_PP_PRINTF) += -I$(CURDIR)/pp_printf
cflags-$(CONFIG_PTP_NOPOSIX) += \
-ffreestanding \
-DPTPD_FREESTANDING \
......@@ -59,6 +61,7 @@ LDFLAGS_PLATFORM = -mmultiply-enabled -mbarrel-shift-enabled \
include shell/shell.mk
include tests/tests.mk
include lib/lib.mk
include pp_printf/printf.mk
include sockitowm/sockitowm.mk
include dev/dev.mk
......
......@@ -57,3 +57,6 @@ SECTIONS
/* First location in stack is highest address in RAM */
PROVIDE(_fstack = ORIGIN(ram) + LENGTH(ram) - 4);
}
/* We need to provide mprintf to ptp-noposix object files, if missing */
PROVIDE(mprintf = pp_printf);
#
# Automatically generated make config: don't edit
#
CONFIG_MPRINTF=y
# CONFIG_PP_PRINTF is not set
CONFIG_ETHERBONE=y
# CONFIG_DETERMINISTIC_BINARY is not set
CONFIG_PTP_NOPOSIX=y
#
# Automatically generated make config: don't edit
#
# CONFIG_MPRINTF is not set
CONFIG_PP_PRINTF=y
# CONFIG_PRINTF_FULL is not set
CONFIG_PRINTF_XINT=y
# CONFIG_PRINTF_MINI is not set
# CONFIG_PRINTF_NONE is not set
CONFIG_PRINT_BUFSIZE=128
CONFIG_ETHERBONE=y
CONFIG_DETERMINISTIC_BINARY=y
CONFIG_PTP_NOPOSIX=y
#
# Automatically generated make config: don't edit
#
CONFIG_MPRINTF=y
# CONFIG_PP_PRINTF is not set
# CONFIG_PRINTF_FULL is not set
# CONFIG_PRINTF_XINT is not set
# CONFIG_PRINTF_MINI is not set
# CONFIG_PRINTF_NONE is not set
# CONFIG_ETHERBONE is not set
# CONFIG_DETERMINISTIC_BINARY is not set
CONFIG_PTP_NOPOSIX=y
......@@ -56,7 +56,8 @@ int8_t get_persistent_mac(uint8_t portnum, uint8_t * mac)
for (i = 0; i < devsnum; ++i) {
//#if DEBUG_PMAC
mprintf("Found device: %x:%x:%x:%x:%x:%x:%x:%x\n",
mprintf("Found device: "
"%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
FamilySN[i][7], FamilySN[i][6], FamilySN[i][5], FamilySN[i][4],
FamilySN[i][3], FamilySN[i][2], FamilySN[i][1], FamilySN[i][0]);
//#endif
......@@ -89,7 +90,7 @@ int8_t get_persistent_mac(uint8_t portnum, uint8_t * mac)
out = 0;
#if DEBUG_PMAC
mprintf("Using EEPROM page: "
"%x:%x:%x:%x:%x:%x\n",
"%02x:%02x:%02x:%02x:%02x:%02x\n",
mac[0], mac[1], mac[2], mac[3],
mac[4], mac[5]);
#endif
......
......@@ -21,16 +21,21 @@ void uart_write_byte(int b)
{
if (b == '\n')
uart_write_byte('\r');
while (uart->SR & UART_SR_TX_BUSY) ;
while (uart->SR & UART_SR_TX_BUSY)
;
uart->TDR = b;
}
void uart_write_string(char *s)
int uart_write_string(const char *s)
{
const char *t = s;
while (*s)
uart_write_byte(*(s++));
return s - t;
}
int puts(const char *s) __attribute__((alias("uart_write_string")));
int uart_poll()
{
return uart->SR & UART_SR_RX_RDY;
......
......@@ -3,7 +3,8 @@
void uart_init(void);
void uart_write_byte(int b);
void uart_write_string(char *s);
int uart_write_string(const char *s);
int puts(const char *s);
int uart_read_byte(void);
#endif
......@@ -7,8 +7,15 @@
* one function and doesn't deserve an header of its own.
* Also, this brings in very common and needed headers
*/
#ifdef CONFIG_MPRINTF
int mprintf(char const *format, ...)
__attribute__((format(printf,1,2)));
#else
#include <pp-printf.h>
#define mprintf pp_printf
#define vprintf pp_vprintf
#define sprintf pp_sprintf
#endif
void wrc_mon_gui(void);
void shell_init(void);
......
obj-y += lib/mprintf.o lib/util.o
obj-$(CONFIG_MPRINTF) += lib/mprintf.o
obj-y += lib/util.o
obj-$(CONFIG_ETHERBONE) += lib/arp.o lib/icmp.o lib/ipv4.o lib/bootp.o
This diff is collapsed.
# Alessandro Rubini for CERN, 2011 -- public domain
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
CFLAGS += -I. -Os -ggdb -Wall
obj-$(CONFIG_PRINTF_FULL) += vsprintf-full.o
obj-$(CONFIG_PRINTF_MINI) += vsprintf-mini.o
obj-$(CONFIG_PRINTF_NONE) += vsprintf-none.o
obj-$(CONFIG_PRINTF_XINT) += vsprintf-xint.o
# set full as a default if nothing is selected
obj-y ?= vsprintf-full.o
obj-y += printf.o
# There is a static variable in pp-printf.c to accumulate stuff
CONFIG_PRINT_BUFSIZE ?= 256
CFLAGS += -DCONFIG_PRINT_BUFSIZE=$(CONFIG_PRINT_BUFSIZE)
# Targets. You may want to make them different in your package
all: pp-printf.o example-printf
pp-printf.o: $(obj-y)
$(LD) -r $(obj-y) -o $@
example-printf: example-printf.c pp-printf.o
$(CC) $(CFLAGS) $^ -o $@
.c.o:
$(CC) -c $(CFLAGS) $< -o $@
clean:
rm -f *.o *~ example-printf
\ No newline at end of file
This is the "poor programmer's" printf implementation. It is meant to
be used in small environments, like microcontrollers or soft
processors. Actually, that's where I needed it years ago and where
I'm still using it.
It is a complete printf (it only misses the %[charset] feature, and
obviously floating point support). It relies on an external "puts"
function for the actual output; the full version also needs strnlen.
Unfortunately, the stdio puts adds a trailing newline (while most
embedded implementations do not). The sprintf function is included.
The printf engine, vsprintf(), comes in four flavours:
In summary:
- the "full" version is a normal printf (GPL2, from older Linux kernel)
- the "xint" version accepts all formats and prints hex and int only
- the "mini" version accepts all formats but only prints hex (GPL2)
- the "none" version accepts all formats and prints nothing (PD)
The version you use can be selected at compile time, so you can
develop with a full-featured printf and install the minimal one in
production, saving a few kilobytes and still not loosing information
in messages. At the end I list compiled sizes for a few use cases.
While I use this very code in several projects, the only example here
is a stupid main that prints something. You are expected to pick these
files and copy them to your projects, rather than use this "package"
as a system library.
The full implementation in detail
=================================
This comes from u-boot, which means that it is an earlier printf as
used in the Linux kernel. It is licensed according to the GNU GPL
version 2. It includes all formats and prefixes and so on. It is
clearly bugless because everybody is using it.
It is selected at compile time by setting the make variable
"CONFIG_PRINTF_FULL" to "y". You can do that in the environment,
or use Kconfig in your application.
(The Makefile selects this by default if you set nothing in the
environment or make variables)
Example calls (example-printf.c):
pp_printf("integer %5i %5i %05i\n", 1024, 666, 53);
pp_printf("octal %5o %5o %05o\n", 1024, 666, 53);
pp_printf("hex %5x %5x %05x\n", 1024, 666, 53);
pp_printf("HEX etc %5X %+5d %-5i\n", 1024, 666, 53);
pp_printf("char: %c string %s %5s %.5s\n", 65, "foo", "foo",
"verylongstring");
Result (as you see, format modifiers are respected):
integer 1024 666 00053
octal 2000 1232 00065
hex 400 29a 00035
HEX etc 400 +666 53
char: A string foo foo veryl
Footprint: 1500-3300 bytes, plus 100-400 bytes for the frontend.
The xint implementaion in detail
================================
This prints correctly "%c", "%s", "%i", "%x". Formats "%u" and "%d"
are synonims of "%i", and "%p"is a synonim for "%x". The only
supported attributes are '0' and a one-digit width (e.g.: "%08x"
works). I personally dislike it, because it
not powerful enough nor low-level as real hacker's too should be.
However, it matches the requirement of some projects with a little
user interface, where the "full" code reveals too large and the "mini"
code is too unfair to the reader. To compile it and link the example,
please set "CONFIG_PRINTF_XINT=y" in your environment or Makefile.
This is the result of the example. As expected, no size nor precision
directives are obeyed:
integer 1024 666 00053
octal 2000 1232 00065
hex 400 29a 00035
HEX etc 400 666 53
char: A string foo foo verylongstring
Footprint: 250-700 bytes, plus 100-250 bytes for the frontend
(FIXME: The figure hasn't been updated after I added the attributes)
The miminal implementaion in detail
===================================
It is derived from the full one. I left all format parsing intact, but
only print "%s" and "%c". Everything else is printed as hex numbers
like "<badc0ffe>". This means your 47 printed as "%03i" will be output
as "<0000002f>" instead of "047". Still, the standard format is
accepted without errors and no information is lost.
I have made no checks nor reasoning about 32-bit vs 64-bit. I only
used it on 32-bit computers and never printed "long long". Now that it
is published, I have an incentive to do it, though.
It is selected at compile time by setting CONFIG_PRINTF_MINI=y as a
make variable, possibly inherited by the environment. It is licensed
as GPL version 2 because it's derived from the full one -- I left the
parsing as I found in there.
Result of example-printf (you can "make CONFIG_PRINTF_MINI=y):
integer <00000400> <0000029a> <00000035>
octal <00000400> <0000029a> <00000035>
hex <00000400> <0000029a> <00000035>
HEX etc <00000400> <0000029a> <00000035>
char: A string foo foo verylongstring
As promised, %c and %s is printed correctly, but without obeying the
format modifiers, but all integer value are printed in hex.
Footprint: 200-600 bytes (usually less than 1k), plus 100-400 for the
frontend.
The empty implementation in detail
==================================
The empty implementation, called "none" to respect the 4-letter
pattern of "full" and "mini" doesn't parse any format. It simply
prints the format string and nothing more. This allows to keep the
most important messages, like the welcome string or a "Panic" string,
while saving code space.
It is selected at compile time by setting CONFIG_PRINTF_NONE.
Result of example-printf (you can "make CONFIG_PRINTF_MINI=y):
integer %5i %5i %05i
octal %5o %5o %05o
hex %5x %5x %05x
HEX etc %5X %+5d %-5i
char: %c string %s %5s %.5s
Footprint: 20-90 bytes, plus 100-400 for the frontend.
If you want to remove all printf overhead in production, you should
use a preprocessor macro to completely kill the printf calls. This
would save you the parameter-passing overhead in the caller and all
the constant strings in .rodata. I don't support this in the package,
though, and I discourage from doing it, for the usual
preprocessor-related reasons.
Footprint of the various implementations
========================================
This table excludes the static buffer (256 in .bss by default) and
only lists the code size (command "size", column "text"), compiled
with -Os as for this Makefile.
(FIXME: The figure fox xint must be redone, as I added the attributes)
printf.o is the frontend and is linked in all four configurations,
the other ones are exclusive one another:
printf.o full xint mini none
x86, gcc-4.4.5 87 1715 300 258 29
x86-64, gcc-4.4.5 418 2325 482 433 70
x86, gcc-4.6.2 255 2210 380 330 89
arm, gcc-4.2.2 156 2408 464 356 48
arm, gcc-4.5.2 128 2235 445 353 32
arm, gcc-4.5.2 thumb2 80 1443 253 209 20
lm32, gcc-4.5.3 196 3228 680 576 36
mips, gcc-4.4.1 184 2616 616 504 72
powerpc, gcc-4.4.1 328 2895 637 521 40
coldfire, gcc-4.4.1 96 2025 331 257 32
sh4, gcc-4.4.1 316 2152 464 408 28
#include <stdio.h>
#include <pp-printf.h>
int main(int argc, char **argv)
{
pp_printf("integer %5i %5i %05i\n", 1024, 666, 53);
pp_printf("octal %5o %5o %05o\n", 1024, 666, 53);
pp_printf("hex %5x %5x %05x\n", 1024, 666, 53);
pp_printf("HEX etc %5X %+5d %-5i\n", 1024, 666, 53);
pp_printf("char: %c string %s %5s %.5s\n", 65, "foo", "foo",
"verylongstring");
return 0;
}
#include <stdarg.h>
extern int pp_printf(const char *fmt, ...)
__attribute__((format(printf,1,2)));
extern int pp_sprintf(char *s, const char *fmt, ...)
__attribute__((format(printf,2,3)));
extern int pp_vprintf(const char *fmt, va_list args);
extern int pp_vsprintf(char *buf, const char *, va_list)
__attribute__ ((format (printf, 2, 0)));
/* This is what we rely on for output */
extern int puts(const char *s);
/*
* Basic printf based on vprintf based on vsprintf
*
* Alessandro Rubini for CERN, 2011 -- public domain
* (please note that the vsprintf is not public domain but GPL)
*/
#include <stdarg.h>
#include <pp-printf.h>
static char print_buf[CONFIG_PRINT_BUFSIZE];
int pp_vprintf(const char *fmt, va_list args)
{
int ret;
ret = pp_vsprintf(print_buf, fmt, args);
puts(print_buf);
return ret;
}
int pp_sprintf(char *s, const char *fmt, ...)
{
va_list args;
int ret;
va_start(args, fmt);
ret = pp_vsprintf(s, fmt, args);
va_end(args);
return ret;
}
int pp_printf(const char *fmt, ...)
{
va_list args;
int ret;
va_start(args, fmt);
ret = pp_vprintf(fmt, args);
va_end(args);
return ret;
}
# This is included from ../Makefile, for the wrc build system.
# The Makefile in this directory is preserved from the upstream version
obj-$(CONFIG_PP_PRINTF) += pp_printf/printf.o
ppprintf-$(CONFIG_PRINTF_FULL) += pp_printf/vsprintf-full.o
ppprintf-$(CONFIG_PRINTF_MINI) += pp_printf/vsprintf-mini.o
ppprintf-$(CONFIG_PRINTF_NONE) += pp_printf/vsprintf-none.o
ppprintf-$(CONFIG_PRINTF_XINT) += pp_printf/vsprintf-xint.o
ppprintf-y ?= pp_printf/vsprintf-xint.o
obj-$(CONFIG_PP_PRINTF) += $(ppprintf-y)
This diff is collapsed.
#include <stdarg.h>
/*
* minimal vsprintf: only %s and hex values
* Alessandro Rubini 2010, based on code in u-boot (from older Linux)
* GNU GPL version 2.
*/
int pp_vsprintf(char *buf, const char *fmt, va_list args)
{
int i, j;
static char hex[] = "0123456789abcdef";
char *s;
char *str = buf;
for (; *fmt ; ++fmt) {
if (*fmt != '%') {
*str++ = *fmt;
continue;
}
repeat:
fmt++; /* Skip '%' initially, other stuff later */
/* Skip the complete format string */
switch(*fmt) {
case '\0':
goto ret;
case '*':
/* should be precision, just eat it */
i = va_arg(args, int);
/* fall through: discard unknown stuff */
default:
goto repeat;
/* Special cases for conversions */
case 'c': /* char: supported */
*str++ = (unsigned char) va_arg(args, int);
break;
case 's': /* string: supported */
s = va_arg(args, char *);
while (*s)
*str++ = *s++;
break;
case 'n': /* number-thus-far: not supported */
break;
case '%': /* supported */
*str++ = '%';
break;
/* all integer (and pointer) are printed as <%08x> */
case 'o':
case 'x':
case 'X':
case 'd':
case 'i':
case 'u':
case 'p':
i = va_arg(args, int);
*str++ = '<';
for (j = 28; j >= 0; j -= 4)
*str++ = hex[(i>>j)&0xf];
*str++ = '>';
break;
}
}
ret:
*str = '\0';
return str - buf;
}
#include <stdarg.h>
#include <pp-printf.h>
/*
* empty vsprintf: only the format string. Public domain
*/
int pp_vsprintf(char *buf, const char *fmt, va_list args)
{
char *str = buf;
for (; *fmt ; ++fmt)
*str++ = *fmt;
return str - buf;
}
/*
* vsprintf-ugly: a possible free-software replacement for mprintf
*
* public domain
*/
#include <stdarg.h>
#include <stdint.h>
static const char hex[] = "0123456789abcdef";
static int number(char *out, int value, int base, int lead, int wid)
{
char tmp[16];
int i = 16, ret;
/* No error checking at all: it is as ugly as possible */
while (value && i) {
tmp[--i] = hex[value % base];
value /= base;
}
if (i == 16)
tmp[--i] = '0';
while (i > 16 - wid)
tmp[--i] = lead;
ret = 16 - i;
while (i < 16)
*(out++) = tmp[i++];
return ret;
}
int pp_vsprintf(char *buf, const char *fmt, va_list args)
{
char *s, *str = buf;
int base, lead, wid;
for (; *fmt ; ++fmt) {
if (*fmt != '%') {
*str++ = *fmt;
continue;
}
base = 10;
lead = ' ';
wid = 1;
repeat:
fmt++; /* Skip '%' initially, other stuff later */
switch(*fmt) {
case '\0':
goto ret;
case '0':
lead = '0';
goto repeat;
case '*':
/* should be precision, just eat it */
base = va_arg(args, int);
/* fall through: discard unknown stuff */
default:
if (*fmt >= '1' && *fmt <= '9')
wid = *fmt - '0';
goto repeat;
/* Special cases for conversions */
case 'c': /* char: supported */
*str++ = (unsigned char) va_arg(args, int);
break;
case 's': /* string: supported */
s = va_arg(args, char *);
while (*s)
*str++ = *s++;
break;
case 'n': /* number-thus-far: not supported */
break;
case '%': /* supported */
*str++ = '%';
break;
/* integers are more or less printed */
case 'p':
case 'x':
case 'X':
base = 16;
case 'o':
if (base == 10) /* yet unchaged */
base = 8;
case 'd':
case 'i':
case 'u':
str += number(str, va_arg(args, int), base, lead, wid);
break;
}
}
ret:
*str = '\0';
return str - buf;
}
......@@ -44,7 +44,7 @@ int cmd_mac(const char *args[])
return -EINVAL;
}
mprintf("MAC-address: %x:%x:%x:%x:%x:%x\n",
mprintf("MAC-address: %02x:%02x:%02x:%02x:%02x:%02x\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
return 0;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment