Author Topic: CH32V003 Can't printf floating point values, is there a .h include to support?  (Read 1867 times)

0 Members and 1 Guest are viewing this topic.

Offline rkaTopic starter

  • Newbie
  • Posts: 6
  • Country: us
Hello all,

I've just recently gotten a CH32V003 EVT board running on WCH's Moun River IDE.  One of the things I wanted to do initially was to try some timings, and so to ensure I could do floating point for my math I added a simple float printf to the GPIO routine in the EXAM section.

                  float foo = 22.15;
                  printf("FOO is:  %2.2 \n\r", foo);

This results in:

                   FOO is:                                       << No value returned

On one hand I think that the CH32V003 is a small controller and wouldn't support floating point.

On the other hand, I wondered whether I'd missed a step and left out a .h include file for example.

After a lot of queries on Google and looking through the RM, I'm not finding any hints one way or another.

Mavens -- Any ideas?

rka
 

Offline rkaTopic starter

  • Newbie
  • Posts: 6
  • Country: us
Correction,

                  float foo = 22.15;
                  printf("FOO is:  %2.2f \n\r", foo);
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11652
  • Country: us
    • Personal site
This depends on the C library used in the compiler, there is no way to fix this with a single .h include. No idea what stock WCH toolchain uses, but generally it is not viable to have floating point printf() on a 16 KB MCU. On ARM full printf() with floating point support is about 20 KB.
Alex
 

Online amwales

  • Regular Contributor
  • *
  • Posts: 99
  • Country: gb
I have mounriver intalled but there doesnt appear to be any source for the libraries so its impossible to tell why they work the way they do unless you can find some documentation. You should be able to accomplish what you are after with something like this, assuming %d works

printf("%d.%d\n", (int)foo, (int)((foo - (int)foo)*100));
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 15156
  • Country: fr
floating point support is usually disabled by default when using GCC with newlib (nano), which is likely what you have here.

One way of enabling it is to pass the "-u _printf_float" option to the linker. If you're using some IDE, look at compiler/linker options and see if there is any such option available.

As ataradov, it's not recommended when you have very little memory available though, it takes a lot of it.
 

Online brucehoult

  • Super Contributor
  • ***
  • Posts: 4392
  • Country: nz
I tried the following stupid little program (bonus points if you know the result without actually running it):

Code: [Select]
#include <stdio.h>

int main(){
    float total=0,last=0,sign=4,cnt=1;
    do {
        last = total;
        total = total + sign/cnt;
        sign = -sign;
        cnt += 2;
        //printf("%d: %f\n", (int)cnt, total);
    } while (total != last);
    return 100*total;
}

Compiled with generic RISC-V gcc 14 newlib toolchain, not whatever is in MounRiver...

ISAtextdata
rv32gc54761416
rv32ic117401416
rv32ic800161872Uncomment printf
rv32ic13936120Uncomment printf, nano.spec
rv32ic7988124nano.spec

So soft float costs just over 6k (library, plus longer code to marshal arguments, call the functions etc). That's a chunk of a 16k flash but not a killer.

printf with fp support?  Forget it. Blows the flash out by 5x.

printf fits ok with Newlib Nano ... and it runs with trying to print the float, but simply ignores it. Not useful if you actually wanted to printf a float. But if your own code is small then printf for integers and strings can be ok on the 003.

I don't have my toolchain configured with rv32ec libs
 

Offline AVI-crak

  • Regular Contributor
  • *
  • Posts: 133
  • Country: ru
    • Rtos
 intended purpose - macro printo();
 printo("text", double, float, uint(8-16-32-64)_t, int(8-16-32-64)_t )
 printing without specifying the type of the variable,
 up to 9 variables and constants at a time,
 has no external dependencies,
 all functions are thread independent,
 minimum weight +140 bytes, complete set 1684 bytes,
 maximum speed 20~470 ticks,
 does not use division and floating point,
 optimized for ARM
https://github.com/AVI-crak/Rtos_cortex/tree/master/printo
 

Offline langwadt

  • Super Contributor
  • ***
  • Posts: 4650
  • Country: dk
I tried the following stupid little program (bonus points if you know the result without actually running it):

Code: [Select]
#include <stdio.h>

int main(){
    float total=0,last=0,sign=4,cnt=1;
    do {
        last = total;
        total = total + sign/cnt;
        sign = -sign;
        cnt += 2;
        //printf("%d: %f\n", (int)cnt, total);
    } while (total != last);
    return 100*total;
}

Compiled with generic RISC-V gcc 14 newlib toolchain, not whatever is in MounRiver...

ISAtextdata
rv32gc54761416
rv32ic117401416
rv32ic800161872Uncomment printf
rv32ic13936120Uncomment printf, nano.spec
rv32ic7988124nano.spec

So soft float costs just over 6k (library, plus longer code to marshal arguments, call the functions etc). That's a chunk of a 16k flash but not a killer.

printf with fp support?  Forget it. Blows the flash out by 5x.

printf fits ok with Newlib Nano ... and it runs with trying to print the float, but simply ignores it. Not useful if you actually wanted to printf a float. But if your own code is small then printf for integers and strings can be ok on the 003.

I don't have my toolchain configured with rv32ec libs

tried this? https://github.com/mpaland/printf
 

Online brucehoult

  • Super Contributor
  • ***
  • Posts: 4392
  • Country: nz
intended purpose - macro printo();
 printo("text", double, float, uint(8-16-32-64)_t, int(8-16-32-64)_t )
 printing without specifying the type of the variable,
 up to 9 variables and constants at a time,
 has no external dependencies,
 all functions are thread independent,
 minimum weight +140 bytes, complete set 1684 bytes,
 maximum speed 20~470 ticks,
 does not use division and floating point,
 optimized for ARM
https://github.com/AVI-crak/Rtos_cortex/tree/master/printo

That might be "optimised for Arm", but other than one optional use of inline asm it's all plain C code that compiles out of the box for RV32 (but not RV64 due to assumptions of the size of long/long long).

Seems to pass almost all tests ootb.

Code: [Select]
bruce@i9:~/programs/Rtos_cortex/printo$ riscv64-unknown-elf-gcc -O -march=rv32imc -mabi=ilp32 test.c printo.c -o test
bruce@i9:~/programs/Rtos_cortex/printo$ ./test
23:24:35
inf   float         =                                                                                                  inf
-inf  float         =                                                                                                  -inf
nan   float         =                                                                                                  NaN
-nan  float         =                                                                                                  -NaN
0     float         =                                                                                                 
-0    float         =                                                                                                  -
1.11111111111d      =                                                                                                  1.111111111110000049
-1.0f               =                                                                                                  -1.000000000000000000
-1uL =                                                                                                 -2.328306298
1.5f                =                                                                                                  1.500000000000000000
-1uL =                                                                                                 1.499999880790710449                                                                                                     

I suspect maybe a non-portable assumption about struct bitfield packing rules. Dunno. But it's very close.

Code: [Select]
#include <stdio.h>
#include "printo.h"

void M_print(char *text){
  puts(text);
}

int main(){
  print_float(355./113);
  return 0;
}

Output:

Code: [Select]
3.141592979431152344

That's 7 accurate digits, then junk.

The float_char() ("convert float to string") function is 116 bytes of code, and it tail-calls floating_char() which is 244 bytes. Floating_char() calls 64 bit multiply (174 bytes) and shift (40 bytes) utility functions which I'm not convinced are better than gcc would simply do for you if you just wrote the C code. And it calls a couple of character out functions.

« Last Edit: June 10, 2024, 02:03:29 pm by brucehoult »
 

Offline rkaTopic starter

  • Newbie
  • Posts: 6
  • Country: us
All,

Thank you all for your in-depth responses to my little question. 

I must admit that they have been very instructive for this CH32V Newbie.

Most of my time spent fooling with these SOC things has been mostly using larger devices (ESP32, STM32, NXP, etc.) in which RAM isn't such a big limiter.  Also, that work has been done with Arduino and PlatformIO, which has a larger legacy community base and is far more mature. 

As I've played with this device I've gravitated towards approaches that I'm familiar with, hence my question.  However, after reading the responses I a little in awe at the "virtuousity" of the replies (I'm not trying to flatter anyone, it's just a great example of real serious technical analysis to my eye).

I'll try a couple of the suggestions to go a couple steps further in appreciating the problem at hand.  Thanks again.

 :D

rks

 

Offline AVI-crak

  • Regular Contributor
  • *
  • Posts: 133
  • Country: ru
    • Rtos
The calculation method differs from the standard printf().
All sacrifices for the sake of speed - my project worked in real time.
 

Offline HwAoRrDk

  • Super Contributor
  • ***
  • Posts: 1561
  • Country: gb
tried this? https://github.com/mpaland/printf

This is what I use with CH32V with good success, much smaller than newlib implementation. When compiled with PRINTF_DISABLE_SUPPORT_FLOAT, PRINTF_DISABLE_SUPPORT_EXPONENTIAL, and PRINTF_DISABLE_SUPPORT_LONG_LONG it occupies only approx. 2kB. Can't comment on its floating point support, or how that changes the size, as I've never used it.
 

Online brucehoult

  • Super Contributor
  • ***
  • Posts: 4392
  • Country: nz
The calculation method differs from the standard printf().
All sacrifices for the sake of speed - my project worked in real time.

I'd suggest a utility function to allow printing only the first N digits of the generated string. And use N = 7 for float.

What do you think is happening with -1.0f - 1UL? It's just completely wrong. That's bf7fffff (I assume by "-1UL" you mean "towards zero") being printed as c01502f8 should be. Those aren't even similar in any way.
 

Offline ejeffrey

  • Super Contributor
  • ***
  • Posts: 3867
  • Country: us
You need to include the printf float routines.  As an alternative to linker command line options it should work to add the following to one of your source files:

asm(".global _printf_float");

 
The following users thanked this post: amwales, peter-h

Offline AVI-crak

  • Regular Contributor
  • *
  • Posts: 133
  • Country: ru
    • Rtos
/// determine the number of characters for numbers (6-20)
#define OUT_TXT_SIZE_FLOATING  17 <-----------------
#define DEL_ZERO    0

What do you think is happening with -1.0f - 1UL?
Fixed the test a little bit.



 

Online brucehoult

  • Super Contributor
  • ***
  • Posts: 4392
  • Country: nz
What do you think is happening with -1.0f - 1UL?
Fixed the test a little bit.

omg that's got to be the least convenient way ever to get an update. wget doesn't really interact with eevblog attachment URLs very well. Just "git pull" would have been far simpler.

Anyway, I'm seeing a ton of weird whitespace/indentation changes, which don't matter. And replacing 'printf("\n ...' with 'printf("\n\r ...' everywhere ... whyyyy? What is that supposed to do? Any system that needs CRLF line endings is supposed to expand \n to CRLF so on Windows or something that should produce CRLFCR which is redundant. On Linux and Mac it just screws everything up.

More importantly, I'm not seeing anything fixed.

All of the following outputs are similarly weird:

-2.328306298  -1f -1uL 
2.328306437  1e+8f
2.328306250  1e+8f -1uL
2.328306437  1e+10f
2.328306198  1e+8f -1uL
2.328306298  1.0000000149e-1f -1uL
2.328306303  1.00000001335e-10f -1uL
2.328306437  9.999999999d
2.328306363  test.fl[1] 9.99999968266e-23f

All other results look ok.
 

Online westfw

  • Super Contributor
  • ***
  • Posts: 4278
  • Country: us
Quote
soft float costs just over 6k
There's a software floating point library for Cortex-M0 that fits in about 1k.
I'm not familiar enough with either architecture to tell whether it's conceptually portable to RISC-V.
 

Online brucehoult

  • Super Contributor
  • ***
  • Posts: 4392
  • Country: nz
Quote
soft float costs just over 6k
There's a software floating point library for Cortex-M0 that fits in about 1k.
I'm not familiar enough with either architecture to tell whether it's conceptually portable to RISC-V.

You can do it very small if the results are not required to be accurate to the last bit, support Nans, infinities, denorms etc. The FP library used by AVR Arduino is a great example.

On the other extreme is Apple's SANE implementation of full IEEE 754 math (or 8087/68881 compatible anyway) for the 6502

https://apple.fandom.com/wiki/Standard_Apple_Numerics_Environment
 

Offline AVI-crak

  • Regular Contributor
  • *
  • Posts: 133
  • Country: ru
    • Rtos
I don't have a CH32V003. The test runs on CH32V307VCT6, stm32f030f4, STM32F746BG - no errors.

/// determine the number of characters for numbers (6-20)
#define OUT_TXT_SIZE_FLOATING  17 <----------------- исправить на 17
#define DEL_ZERO    0

Code: [Select]
void _write_o(char *buf)
{
    while(*buf != 0)
    {
#if(DEBUG == DEBUG_UART1)
        while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
        USART_SendData(USART1, *buf++);
#elif(DEBUG == DEBUG_UART2)
        while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
        USART_SendData(USART2, *buf++);
#elif(DEBUG == DEBUG_UART3)
        while(USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET);
        USART_SendData(USART3, *buf++);
#endif
    }
}
 

Online brucehoult

  • Super Contributor
  • ***
  • Posts: 4392
  • Country: nz
I don't have a CH32V003. The test runs on CH32V307VCT6, stm32f030f4, STM32F746BG - no errors.

I don't either -- or at least I'm not using it for this.

I'm running on QEMU (via bimfmt_misc) after building with standard GCC. Both are a simple package install away on all major OSes: HomeBREW on Mac [1], apt get on Debian-based including Windows WSL. If you're running Fedora or Arch or Alpine or something then I'm sure you know your own package management system.

[1] actually I'd recommend docker on Mac
 

Offline AVI-crak

  • Regular Contributor
  • *
  • Posts: 133
  • Country: ru
    • Rtos
brucehoult - It works for me...
 

Offline cnich

  • Newbie
  • Posts: 1
  • Country: us
https://github.com/charlesnicholson/nanoprintf is a single .h file implementation of printf that supports float/double and is designed with tiny systems in mind; perhaps it might help you?
 
The following users thanked this post: amwales

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 15156
  • Country: fr
https://github.com/charlesnicholson/nanoprintf is a single .h file implementation of printf that supports float/double and is designed with tiny systems in mind; perhaps it might help you?

I have used nanoprintf. Fun fact, compared to using printf from newlib nano, it actually took more code size (tested on a RISCV, GCC with -02).
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf