找回密码
 立即注册
查看: 1450|回复: 55

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

[复制链接]
  • 打卡等级:以坛为家II
  • 打卡总天数:458
  • 最近打卡:2025-05-01 07:48:22
已绑定手机
已实名认证

110

主题

2218

回帖

5450

积分

版主

积分
5450
发表于 2025-3-13 08:01:12 | 显示全部楼层 |阅读模式
解决编译器【REMOVEUNUSED选项】,
可能错误移除的问题,现象是感觉复位了
截图202503191010511582.jpg

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

C251编译器【REMOVEUNUSED】

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

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

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


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

以下是可以复现LCALL问题的最小工程,欢迎来一起探讨有无更好解决方案:
重现LCALL异常问题工程.zip (26.53 KB, 下载次数: 13)
备注:编译后直接使用软件模拟Debug即可从反汇编窗口看到LCALL问题,
因为这个是编译器的Bug,与芯片无关
截图202503171603058894.jpg

/*--------------------------------------------------------------------------
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 int  printf   (const char *, ...);
extern char *gets    (char *, int n);
extern int  scanf    (const char *, ...);
extern int  vprintf  (const char *, char *);
extern int  puts     (const char *);
#pragma RESTORE
/* available as reentrant & non-reentrant version */
extern int  sprintf  (char *, const char *, ...);
extern int  vsprintf (char *, const char *, char *);
extern int  sscanf   (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








5 喜欢他/她就送朵鲜花吧,赠人玫瑰,手有余香!

本帖被以下淘专辑推荐:

回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:38
  • 最近打卡:2025-04-30 14:38:57

25

主题

977

回帖

3599

积分

超级版主

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

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

截图202503131213476729.jpg


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

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

截图202503131217149761.jpg

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

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

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

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

点评

目前通过调试定位到,原因是有其他程序文件使用了stdio.h的内的任意函数后(例如printf、scanf),如果main函数没有调用这些函数,那么使用REMOVEUNUSED就会出现LCALL 0x0000异常。 最快的解决方案就是在程序开头加上  详情 回复 发表于 2025-3-17 13:32
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:54
  • 最近打卡:2025-05-01 09:07:55

717

主题

1万

回帖

1万

积分

管理员

积分
15609
发表于 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);

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


回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:458
  • 最近打卡:2025-05-01 07:48:22
已绑定手机
已实名认证

110

主题

2218

回帖

5450

积分

版主

积分
5450
发表于 2025-3-17 15:44:03 | 显示全部楼层
目前可复现引起LCALL 0x0000问题的最小工程
大家可以帮忙测试看看是否能找到问题所在


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


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


以下是工程文件和出现问题的几种情况截图:
重现LCALL异常问题工程.zip (26.53 KB, 下载次数: 17)


截图202503171540287049.jpg
截图202503171540385847.jpg
截图202503171540426978.jpg

回复 支持 1 反对 0

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:162
  • 最近打卡:2025-05-01 06:56:44
已绑定手机

56

主题

1319

回帖

2938

积分

荣誉版主

无情的代码机器

积分
2938
发表于 2025-3-17 23:47:58 | 显示全部楼层
提供一个简单粗暴的解决办法,在头文件中宏替换main函数,看看是否可行

截图202503172339214559.jpg
截图202503172339321762.jpg

重现LCALL异常问题工程-宏替换跑马灯.zip (59.95 KB, 下载次数: 10)

点评

有一点小问题,就是main函数最开头定义变量话会因为C89格式问题导致报错 这里修改了一下 这样编译就正常了 [attachimg]88362[/attachimg] 开头定义变量也没问题,因为while(1)的存在,sprintf实际没有执行,但是可以  详情 回复 发表于 2025-3-18 10:24
好方法,这样用户部分就成无感调用了  详情 回复 发表于 2025-3-18 09:49
三天不学习,赶不上刘少奇~
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:54
  • 最近打卡:2025-05-01 09:07:55

717

主题

1万

回帖

1万

积分

管理员

积分
15609
发表于 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 int  printf   (const char *, ...);
extern char *gets    (char *, int n);
extern int  scanf    (const char *, ...);
extern int  vprintf  (const char *, char *);
extern int  puts     (const char *);
#pragma RESTORE
/* available as reentrant & non-reentrant version */
extern int  sprintf  (char *, const char *, ...);
extern int  vsprintf (char *, const char *, char *);
extern int  sscanf   (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


回复 支持 反对

使用道具 举报 送花

3

主题

1160

回帖

1031

积分

等待验证会员

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

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:54
  • 最近打卡:2025-05-01 09:07:55

717

主题

1万

回帖

1万

积分

管理员

积分
15609
发表于 2025-3-13 08:27:11 | 显示全部楼层
暂时不要声明 16位 的 数据类型去 除 ?
===利用MDU32本身的有符号32位除法,将16位的有符号除法库给替换掉
直接声明成 32位的 ?
等我们修改 MDU32的库去取代这个 ?
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:392
  • 最近打卡:2025-05-01 15:00:39

24

主题

305

回帖

2556

积分

荣誉版主

积分
2556
发表于 2025-3-13 08:33:51 | 显示全部楼层
能体会到发现一个不理解的现象然后找原因然后要么解决掉问题要么被问题解决掉的那种快乐是我的幸运
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:452
  • 最近打卡:2025-05-01 06:20:50
已绑定手机

13

主题

1257

回帖

2971

积分

金牌会员

积分
2971
发表于 2025-3-13 09:24:58 | 显示全部楼层
这个问题在论坛讨论的次数挺多的,已经查明是C251的BUG。

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

点评

现已经通过mdu32库解决,c251只需要添加mdu32的lib库即可解决这个问题  详情 回复 发表于 2025-3-13 09:52
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:458
  • 最近打卡:2025-05-01 07:48:22
已绑定手机
已实名认证

110

主题

2218

回帖

5450

积分

版主

积分
5450
发表于 2025-3-13 09:52:37 | 显示全部楼层
21cns*** 发表于 2025-3-13 09:24
这个问题在论坛讨论的次数挺多的,已经查明是C251的BUG。

建议最好能有个统一的、系统的解决方案,比如哪 ...

现已经通过mdu32库解决,c251只需要添加mdu32的lib库即可解决这个问题
官网库函数文件已更新
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:452
  • 最近打卡:2025-05-01 06:20:50
已绑定手机

13

主题

1257

回帖

2971

积分

金牌会员

积分
2971
发表于 2025-3-13 09:55:33 | 显示全部楼层
王*** 发表于 2025-3-13 09:52
现已经通过mdu32库解决,c251只需要添加mdu32的lib库即可解决这个问题
官网库函数文件已更新
...

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

点评

是的,目前只是这部分会出现问题,正在扩大测试范围 因为编译器本身的问题,目前尚不清楚实际的问题根源,但现在通过添加lib库可以解决这个问题  详情 回复 发表于 2025-3-13 11:15
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:458
  • 最近打卡:2025-05-01 07:48:22
已绑定手机
已实名认证

110

主题

2218

回帖

5450

积分

版主

积分
5450
发表于 2025-3-13 11:15:46 | 显示全部楼层
21cns*** 发表于 2025-3-13 09:55
关于REMOVEUNUSED引起的单片机重启,C251只是这一个点的bug吗?

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

点评

好的,多谢!  发表于 2025-3-13 11:53
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:392
  • 最近打卡:2025-05-01 15:00:39

24

主题

305

回帖

2556

积分

荣誉版主

积分
2556
发表于 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



点评

我现在尝试同时添加USB-CDC库和使用乘除法(添加MDU32LIB情况下),得到程序是正常使用的 可以帮忙测试一下原来有问题工程中,添加官网刚更新的MDU32LIB库,看看是否能正常使用?  详情 回复 发表于 2025-3-13 12:24
能体会到发现一个不理解的现象然后找原因然后要么解决掉问题要么被问题解决掉的那种快乐是我的幸运
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:392
  • 最近打卡:2025-05-01 15:00:39

24

主题

305

回帖

2556

积分

荣誉版主

积分
2556
发表于 2025-3-13 12:24:14 | 显示全部楼层

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



点评

好的,感谢测试  发表于 2025-3-13 12:26
目前此程序分支已经合并进入官网的MDU32库中 [attachimg]87743[/attachimg] 可以直接使用官网的LIB库来解决了  详情 回复 发表于 2025-3-13 12:26
能体会到发现一个不理解的现象然后找原因然后要么解决掉问题要么被问题解决掉的那种快乐是我的幸运
回复 支持 反对

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-5-1 20:08 , Processed in 0.577005 second(s), 157 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表