Skip to content

Instantly share code, notes, and snippets.

@raczben
Last active December 23, 2024 12:34
Show Gist options
  • Save raczben/a8b5410440b601ce6e7d64fd96b2d79d to your computer and use it in GitHub Desktop.
Save raczben/a8b5410440b601ce6e7d64fd96b2d79d to your computer and use it in GitHub Desktop.
This is a simple sprintf implementation to minimaze ROM and RAM usage
#include "xil_sprintf.h" // Define here: void xil_sprintf(char* s, const char *ctrl1, ...);
#include "xil_types.h"
#include "ctype.h"
#include "stdarg.h"
#if defined (__aarch64__)
Not tested on __aarch64__!!!
Remove these lines to use on __aarch64__, but no guarantee:
__aarch64__ related lines has not been reviewed.
#endif
static int spadding(char* s, const s32 l_flag,const struct params_s *par);
static int souts(char* s, const char * lp, struct params_s *par);
static s32 getnum( char ** linep);
typedef struct params_s {
s32 len;
s32 num1;
s32 num2;
char8 pad_character;
s32 do_padding;
s32 left_flag;
s32 unsigned_flag;
} params_t;
/*---------------------------------------------------*/
/* The purpose of this routine is to output data the */
/* same as the standard printf function without the */
/* overhead most run-time libraries involve. Usually */
/* the printf brings in many kilobytes of code and */
/* that is unacceptable in most embedded systems. */
/*---------------------------------------------------*/
/*---------------------------------------------------*/
/* */
/* This routine puts pad characters into the output */
/* buffer. */
/* */
static int spadding(char* s, const s32 l_flag, const struct params_s *par)
{
s32 i;
s32 padLen = 0;
if ((par->do_padding != 0) && (l_flag != 0) && (par->len < par->num1)) {
i=(par->len);
for (; i<(par->num1); i++) {
s[padLen++] = par->pad_character;
}
}
return padLen;
}
/*---------------------------------------------------*/
/* */
/* This routine moves a string to the output buffer */
/* as directed by the padding and positioning flags. */
/* */
static int souts(char* s, const char * lp, struct params_s *par)
{
int strLen = 0;
const char * LocalPtr;
LocalPtr = lp;
/* pad on left if needed */
if(LocalPtr != NULL) {
par->len = (s32)strlen( LocalPtr);
}
strLen += spadding(s+strLen, !(par->left_flag), par);
/* Move string to the buffer */
while (((*LocalPtr) != (char8)0) && ((par->num2) != 0)) {
(par->num2)--;
s[strLen++] = *LocalPtr;
LocalPtr += 1;
}
/* Pad on right if needed */
/* CR 439175 - elided next stmt. Seemed bogus. */
/* par->len = strlen( lp) */
strLen += spadding(s+strLen, par->left_flag, par);
return strLen;
}
/*---------------------------------------------------*/
/* */
/* This routine moves a number to the output buffer */
/* as directed by the padding and positioning flags. */
/* */
static int soutnum(char* s, const s32 n, const s32 base, struct params_s *par)
{
int numLen = 0;
s32 negative;
s32 i;
char8 outbuf[32];
const char8 digits[] = "0123456789ABCDEF";
u32 num;
for(i = 0; i<32; i++) {
outbuf[i] = '0';
}
/* Check if number is negative */
if ((par->unsigned_flag == 0) && (base == 10) && (n < 0L)) {
negative = 1;
num =(-(n));
}
else{
num = n;
negative = 0;
}
/* Build number (backwards) in outbuf */
i = 0;
do {
outbuf[i] = digits[(num % base)];
i++;
num /= base;
} while (num > 0);
if (negative != 0) {
outbuf[i] = '-';
i++;
}
outbuf[i] = 0;
i--;
/* Move the converted number to the buffer and */
/* add in the padding where needed. */
par->len = (s32)strlen(outbuf);
numLen += spadding(s+numLen, !(par->left_flag), par);
while (&outbuf[i] >= outbuf) {
s[numLen++] = outbuf[i];
i--;
}
numLen += spadding(s+numLen, par->left_flag, par);
return numLen;
}
/*---------------------------------------------------*/
/* */
/* This routine gets a number from the format */
/* string. */
/* */
static s32 getnum( char ** linep)
{
s32 n;
s32 ResultIsDigit = 0;
char * cptr;
n = 0;
cptr = *linep;
if(cptr != NULL){
ResultIsDigit = isdigit(((s32)*cptr));
}
while (ResultIsDigit != 0) {
if(cptr != NULL){
n = ((n*10) + (((s32)*cptr) - (s32)'0'));
cptr += 1;
if(cptr != NULL){
ResultIsDigit = isdigit(((s32)*cptr));
}
}
ResultIsDigit = isdigit(((s32)*cptr));
}
*linep = ((char * )(cptr));
return(n);
}
/*---------------------------------------------------*/
/* */
/* This routine operates just like a printf/sprintf */
/* routine. It outputs a set of data under the */
/* control of a formatting string. Not all of the */
/* standard C format control are supported. The ones */
/* provided are primarily those needed for embedded */
/* systems work. Primarily the floating point */
/* routines are omitted. Other formats could be */
/* added easily by following the examples shown for */
/* the supported formats. */
/* */
/* void esp_printf( const func_ptr f_ptr,
const char * ctrl1, ...) */
void xil_sprintf(char* s, const char *ctrl1, ...)
{
s32 Check;
int sCounter = 0;
#if defined (__aarch64__)
s32 long_flag;
#endif
s32 dot_flag;
params_t par;
char8 ch;
va_list argp;
char8 *ctrl = (char8 *)ctrl1;
va_start( argp, ctrl1);
while ((ctrl != NULL) && (*ctrl != (char8)0)) {
/* move format string chars to buffer until a */
/* format control is found. */
if (*ctrl != '%') {
s[sCounter++] = *ctrl;
ctrl += 1;
continue;
}
/* initialize all the flags for this format. */
dot_flag = 0;
#if defined (__aarch64__)
long_flag = 0;
#endif
par.unsigned_flag = 0;
par.left_flag = 0;
par.do_padding = 0;
par.pad_character = ' ';
par.num2=32767;
par.num1=0;
par.len=0;
try_next:
if(ctrl != NULL) {
ctrl += 1;
}
if(ctrl != NULL) {
ch = *ctrl;
}
else {
ch = *ctrl;
}
if (isdigit((s32)ch) != 0) {
if (dot_flag != 0) {
par.num2 = getnum(&ctrl);
}
else {
if (ch == '0') {
par.pad_character = '0';
}
if(ctrl != NULL) {
par.num1 = getnum(&ctrl);
}
par.do_padding = 1;
}
if(ctrl != NULL) {
ctrl -= 1;
}
goto try_next;
}
switch (tolower((s32)ch)) {
case '%':
s[sCounter++] ='%';
Check = 1;
break;
case '-':
par.left_flag = 1;
Check = 0;
break;
case '.':
dot_flag = 1;
Check = 0;
break;
case 'l':
#if defined (__aarch64__)
long_flag = 1;
#endif
Check = 0;
break;
case 'u':
par.unsigned_flag = 1;
/* fall through */
case 'i':
case 'd':
#if defined (__aarch64__)
if (long_flag != 0){
outnum1((s64)va_arg(argp, s64), 10L, &par);
}
else {
sCounter += soutnum(s+sCounter, va_arg(argp, s32), 10L, &par);
}
#else
sCounter += soutnum(s+sCounter, va_arg(argp, s32), 10L, &par);
#endif
Check = 1;
break;
case 'p':
#if defined (__aarch64__)
par.unsigned_flag = 1;
outnum1((s64)va_arg(argp, s64), 16L, &par);
Check = 1;
break;
#endif
case 'X':
case 'x':
par.unsigned_flag = 1;
#if defined (__aarch64__)
if (long_flag != 0) {
outnum1((s64)va_arg(argp, s64), 16L, &par);
}
else {
sCounter += soutnum(s+sCounter, (s32)va_arg(argp, s32), 16L, &par);
}
#else
sCounter += soutnum(s+sCounter, (s32)va_arg(argp, s32), 16L, &par);
#endif
Check = 1;
break;
case 's':
sCounter += souts(s+sCounter, va_arg( argp, char *), &par);
Check = 1;
break;
case 'c':
s[sCounter++] = va_arg( argp, s32);
Check = 1;
break;
case '\\':
switch (*ctrl) {
case 'a':
s[sCounter++] = ((char8)0x07);
break;
case 'h':
s[sCounter++] = ((char8)0x08);
break;
case 'r':
s[sCounter++] = ((char8)0x0D);
break;
case 'n':
s[sCounter++] = ((char8)0x0D);
s[sCounter++] = ((char8)0x0A);
break;
default:
s[sCounter++] = *ctrl;
break;
}
ctrl += 1;
Check = 0;
break;
default:
Check = 1;
break;
}
if(Check == 1) {
if(ctrl != NULL) {
ctrl += 1;
}
continue;
}
goto try_next;
}
va_end( argp);
}
@raczben
Copy link
Author

raczben commented Jul 3, 2020

This is a solution for several of Xilinx's sprintf issues. Use this to reduce code size and stack usage.

https://forums.xilinx.com/t5/Embedded-Development-Tools/Problem-using-sprintf/m-p/208249#M21807
https://forums.xilinx.com/t5/Embedded-Development-Tools/Converting-xil-printf-into-xil-sprintf/td-p/599410

Tested on microblaze on Kintex US with Vivado 2017.4

@CaglayanDokme
Copy link

Thanks for providing your implementation. Returning the length of the formatted string would be nice as normally sprintf(..) does so.

@vladimirnesterov
Copy link

Returning the length of the formatted string would be nice

Change function return type "void" to "int" and add "return sCounter;" at the end of the function.

@CaglayanDokme
Copy link

Returning the length of the formatted string would be nice

Change function return type "void" to "int" and add "return sCounter;" at the end of the function.

Oh, thanks. I've been waiting for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment