王昱顺 发表于 2025-3-13 08:01:12

解决编译器【REMOVEUNUSED选项】,可能错误移除问题,现象是感觉复位了

解决编译器【REMOVEUNUSED选项】,
可能错误移除的问题,现象是感觉复位了


深圳国芯人工智能有限公司-库函数

C251编译器【REMOVEUNUSED】
===错误移除部分C251自带内部部分库函数的问题|会 LCALL 0000H
===经查,原因为其他的函数中调用了“stdio.h”中的任意函数,但是主程序中并未使用
===Keil C251 会 无条件移除 【全部的Keil内部的浮点库,16位有符号除法内库,8位有符号除法内库】
===较为完美的解决方法:在单片机头文件中(如AI8051U单片机的头文件就是AI8051U.H),加入如下三行代码
#include "stdio.h"
extern void _m(void);//修正编译器的LCALL 0x0000异常
#define main(x) main(x){_m();while(P0|!P0);sprintf(0, "0");}; void _m(x)

这样,正常使用main函数即可,编译器问题被无感修复

后续这部分代码会合并到头文件中,到时候只需要更新一下头文件就可以了
(目前还是需要自己添加的,如果合并了这里会通知的)


以下为测试截图:
没有调用时生成了异常的LCALL 0x0000(正常应该是一个对应存储函数的实际地址)

添加调用后则正常


以下是可以复现LCALL问题的最小工程,欢迎来一起探讨有无更好解决方案:

备注:编译后直接使用软件模拟Debug即可从反汇编窗口看到LCALL问题,
因为这个是编译器的Bug,与芯片无关


/*--------------------------------------------------------------------------
STDIO.H

Prototypes for standard I/O functions for C251 Version 3.
Copyright (c) 1995-2001 Keil Elektronik GmbH and Keil Software, Inc.
All rights reserved.
--------------------------------------------------------------------------*/

#pragma SAVE
#pragma PARM251

#ifndef EOF
#define EOF -1
#endif

#ifndef NULL
#define NULL ((void *) 0)
#endif

#ifndef _SIZE_T
#define _SIZE_T
typedef unsigned int size_t;
#endif

#if (__C251__ >= 200)
#pragma SAVE
#pragma FUNCTIONS(STATIC)
/* only static functions available */
extern char _getkey(void);
extern char getchar(void);
extern char ungetchar(char);
extern char putchar(char);
extern intprintf   (const char *, ...);
extern char *gets    (char *, int n);
extern intscanf    (const char *, ...);
extern intvprintf(const char *, char *);
extern intputs   (const char *);
#pragma RESTORE
/* available as reentrant & non-reentrant version */
extern intsprintf(char *, const char *, ...);
extern intvsprintf (char *, const char *, char *);
extern intsscanf   (char *, const char *, ...);
#else
extern char _getkey (void);
extern char getchar (void);
extern char ungetchar (char);
extern char putchar (char);
extern int printf   (const char *, ...);
extern int sprintf(char *, const char *, ...);
extern int vprintf(const char *, char *);
extern int vsprintf (char *, const char *, char *);
extern char *gets (char *, int n);
extern int scanf (const char *, ...);
extern int sscanf (char *, const char *, ...);
extern int puts (const char *);
#endif

#pragma RESTORE








zhp 发表于 2025-3-13 12:57:01

关于在C251环境下使用USB的库函数时,如果加上REMOVEUNUSED链接指令后
Keil编译器会错误的移除部分Keil的内部库函数
目前发现错误的移除的库包括:
1、浮点加、减、乘、除、取负数函数
2、所有三角函数和反三角函数
3、楼主发现的整型有符号除法SIDIV函数

如下图,函数移除后,编译器会默认将原函数的地址设置为0000H,导致程序复位



直接去掉REMOVEUNUSED链接指令,可以解决上的问题
但去掉REMOVEUNUSED后,Keil会将USB库中所有模块全部链接到目标文件中
从而会导致相当一部分的程序空间和RAM空间被浪费

建议的解决办法:
如果需要使用USB的库,请同时将STC32G对应的MDU32的库和
FPMU浮点库也一同加入到项目中,如下图:


另外还需要注意一点,由于USB.LIB 库+REMOVEUNUSED,Keil C251 会无条件移除全部的Keil内部的浮点库,
所以如果您的代码中浮点运算并没有在STC32G的FPMU库中支持,依然会出现复位的情况
如上图中的asin函数,STC32G的FPMU库没有支持,依然会LCALL 0000H的地址
所以您需要特别注意STC32G的MDU32和FPMU库所支持的函数

MDU32支持的函数如下:
长整型乘法运算(包含有符号和无符号)
整型乘法运算(包含有符号和无符号)
无符号长整型除法运算
无符号长整型除以无符号整型运算
有符号长整型除法运算
有符号整型除法运算

FPMU指定的函数如下:
浮点数加法运算
浮点数减法运算
浮点数乘法法运算
浮点数除法运算
开方运算
浮点数比较
浮点数取负数运算
浮点数取绝对值运算
浮点数与整数之间的转换函数
正弦三角函数
余弦三角函数
正切三角函数
反正切三角函数

如果您的程序使用了USB库,且需要使用REMOVEUNUSED,
则注意不能使用上面函数之外其他数学函数

神农鼎 发表于 2025-3-14 08:08:31

Keil C251的如下 软件实现的浮点库函数 Ai8051U没有硬件支持:
double exp   (double val);
double log   (double val);
double log10 (double val);
double asin(double val);
double acos(double val);
double sinh(double val);
double cosh(double val);
double tanh(double val);
double atan2 (double y, double x);
double ceil(double val);
double floor (double val);
double modf(double val, double *n);
double pow   (double x, double y);
double fmod(double x, double y);

STC32G 需要实现全部的Keil C251 软件浮点库函数如下:
浮点数基本的加、减、乘、除、比较、取负数
浮点数与整数和长整数之间的转换
以及如下keil的基本浮点函数
double fabs(double val);
double sqrt(double val);
double exp   (double val);
double log   (double val);
double log10 (double val);
double sin   (double val);
double cos   (double val);
double tan   (double val);
double asin(double val);
double acos(double val);
double atan(double val);
double sinh(double val);
double cosh(double val);
double tanh(double val);
double atan2 (double y, double x);
double ceil(double val);
double floor (double val);
double modf(double val, double *n);
double pow   (double x, double y);
double fmod(double x, double y);

内部暂时能想到的有上面这些


王昱顺 发表于 2025-3-17 15:44:03

目前可复现引起LCALL 0x0000问题的最小工程
大家可以帮忙测试看看是否能找到问题所在


已知触发原因为:
其他的.c文件内调用任意包含stdio.h的函数后,在main.c内未使用


已知解决方案为:
在程序最开头手动调用一次stdio.h中的任意函数即可解决问题
这里调用的是sprintf,向内存写了个数据,没有实际意义,但是可以避免掉编译器的Bug


以下是工程文件和出现问题的几种情况截图:







ercircle 发表于 2025-3-17 23:47:58

提供一个简单粗暴的解决办法,在头文件中宏替换main函数,看看是否可行
{:ciya:}:




神农鼎 发表于 2025-3-18 09:19:14

/*--------------------------------------------------------------------------
STDIO.H

Prototypes for standard I/O functions for C251 Version 3.
Copyright (c) 1995-2001 Keil Elektronik GmbH and Keil Software, Inc.
All rights reserved.
--------------------------------------------------------------------------*/

#pragma SAVE
#pragma PARM251

#ifndef EOF
#define EOF -1
#endif

#ifndef NULL
#define NULL ((void *) 0)
#endif

#ifndef _SIZE_T
#define _SIZE_T
typedef unsigned int size_t;
#endif

#if (__C251__ >= 200)
#pragma SAVE
#pragma FUNCTIONS(STATIC)
/* only static functions available */
extern char _getkey(void);
extern char getchar(void);
extern char ungetchar(char);
extern char putchar(char);
extern intprintf   (const char *, ...);
extern char *gets    (char *, int n);
extern intscanf    (const char *, ...);
extern intvprintf(const char *, char *);
extern intputs   (const char *);
#pragma RESTORE
/* available as reentrant & non-reentrant version */
extern intsprintf(char *, const char *, ...);
extern intvsprintf (char *, const char *, char *);
extern intsscanf   (char *, const char *, ...);
#else
extern char _getkey (void);
extern char getchar (void);
extern char ungetchar (char);
extern char putchar (char);
extern int printf   (const char *, ...);
extern int sprintf(char *, const char *, ...);
extern int vprintf(const char *, char *);
extern int vsprintf (char *, const char *, char *);
extern char *gets (char *, int n);
extern int scanf (const char *, ...);
extern int sscanf (char *, const char *, ...);
extern int puts (const char *);
#endif

#pragma RESTORE


国芯人工智能 发表于 2025-3-13 08:03:00

关于REMOVEUNUSED引起的单片机重启问题,经过深入分析,发现问题的根源在于链接器错误地生成了一条LCALL 0x0000复位指令。通过查证,这条指令实际上应是16位有符号除法内库C?SIDIV的调用。在汇编层面,该库的功能是调用DIV指令进行除法运算。<br><br>然而,在问题出现时,该库并未被包含在代码中。为解决此问题,我们利用了MDU32本身的有符号32位除法功能,将原有的16位有符号除法库替换掉。经过多次测试,这一改动有效解决了重启问题。<br><br>在此过程中,我们借鉴了许老师的MDU32D工程框架,并对其进行了适当修改。通过对数据格式的调整和优化,最终实现了稳定的运行效果。这一解决方案不仅修复了REMOVEUNUSED带来的重启问题,还提升了代码的效率和可靠性。<br><br>[本文内容由国芯人工智能辅助生成,仅供参考]

神农鼎 发表于 2025-3-13 08:27:11

暂时不要声明 16位 的 数据类型去 除 ?
===利用MDU32本身的有符号32位除法,将16位的有符号除法库给替换掉
直接声明成 32位的 ?
等我们修改 MDU32的库去取代这个 ?

大明狐 发表于 2025-3-13 08:33:51

{:4_225:}{:4_225:}{:4_225:}

21cnsound 发表于 2025-3-13 09:24:58

这个问题在论坛讨论的次数挺多的,已经查明是C251的BUG。

建议最好能有个统一的、系统的解决方案,比如哪些场景需要怎么规避。

王昱顺 发表于 2025-3-13 09:52:37

21cnsound 发表于 2025-3-13 09:24
这个问题在论坛讨论的次数挺多的,已经查明是C251的BUG。

建议最好能有个统一的、系统的解决方案,比如哪 ...
现已经通过mdu32库解决,c251只需要添加mdu32的lib库即可解决这个问题
官网库函数文件已更新

21cnsound 发表于 2025-3-13 09:55:33

王昱顺 发表于 2025-3-13 09:52
现已经通过mdu32库解决,c251只需要添加mdu32的lib库即可解决这个问题
官网库函数文件已更新
...

关于REMOVEUNUSED引起的单片机重启,C251只是这一个点的bug吗?

王昱顺 发表于 2025-3-13 11:15:46

21cnsound 发表于 2025-3-13 09:55
关于REMOVEUNUSED引起的单片机重启,C251只是这一个点的bug吗?

是的,目前只是这部分会出现问题,正在扩大测试范围
因为编译器本身的问题,目前尚不清楚实际的问题根源,但现在通过添加lib库可以解决这个问题

大明狐 发表于 2025-3-13 12:01:40

因为平时主要玩的是OLED屏幕,当时遇到的情况是USB下载和REMOVEUNUSED同时用会重启。
现象总结起来就是


1、USB-CDC和USB-HID 如果用 LIB方式 添加,那么在设置REMOVEUNUSED之后,如果进行某些计算,编译之后就会反复重启。
而如果用 源码方式 添加USB下载功能,在设置REMOVEUNUSED之后也都正常运行。

2、上面说的LIB方式下出问题的“某些计算”,主要是数据跟2的倍数的数值的除法。
比如 y += fontsize / 8; 运行就会重启。
但是如果写成

(1) y = y + fontsize / 8;
(2) y += (unsigned char)(fontsize / 8);
(3) y += ( fontsize / 8 ) & 0xFF;
(4) y += ( fontsize / 8 ) & 0x00FF;
(5) y += ( fontsize / 8 ) + 0;
(6) y += (8 / 8);
(7) y += fontsize / 7 + 1;
(8) y += fontsize >> 3;

也都能正常运行,其中前五个是只要在前后加点儿零碎儿,而不是单纯的 /8 ,就不会出问题;(6)是常量的计算,(7)是除数不是2的倍数的时候;(8)是用移位代替除号。
另外如果变量y的数据类不是 char 和 unsigned char,比如定义成int类型,也不会出问题。

除此之外还发现float类型变量取负数的时候,
比如 a = -a;// a是float型
LIB方式下编译之后,也会出现 LCALL C?STARTUP(C:Ox0000)。

///////////////////////////////////////////////////////////


这是当时的两个描述和讨论帖

https://www.stcaimcu.com/forum.php?mod=viewthread&tid=9432

https://www.stcaimcu.com/forum.php?mod=viewthread&tid=9602



大明狐 发表于 2025-3-13 12:24:14


试了一下在LIB方式USB下载的有问题的工程里,添加楼主的MDU32_SIDIV.LIB,
重新编译之后果然不会重启了
{:4_189:}

644
页: [1] 2 3 4 5
查看完整版本: 解决编译器【REMOVEUNUSED选项】,可能错误移除问题,现象是感觉复位了