Keil C51 - ERROR L107: ADDRESS SPACE OVERFLOW
在给一个做好的板子写出厂测试程序. 一共要写20个测试功能.MCU为STC15F2K60S2. 编译器为 MDK5(keil C51)SEGMENT:?DT?DT?ADC可以看到 data 超过了127, xdata不到2048, code不到64K.在网上查了资料, L07的错误原因: 不是RAM超了, 就是ROM超了.我这的情况是data超过了127bytes.
文章目录
- Keil C51 - ERROR L107: ADDRESS SPACE OVERFLOW
- 概述
- * 将内存模式从Small换成Compact或Large, 再编译通过.
- * 将不用的变量去掉.减少内存用量.
- 从map文件, 可以看到data用量是被谁占用的.
- 不能优化data内存的项
- REG 0000H 0008H ABSOLUTE "REG BANK 0"
- DATA 0008H 0014H UNIT _DATA_GROUP_
- BIT 0020H.0 0001H.1 UNIT _BIT_GROUP_
- 0021H.1 0000H.7 *** GAP ***
- IDATA 0080H 0001H UNIT ?STACK
- 可以改的data选项
- 优化data的方法
- 将函数内部的变量改为xdata
- 将函数入参去掉, 用全局变量代替
- 将全局变量可以复用的地方, 用union的结构变量
- 备注
- END
Keil C51 - ERROR L107: ADDRESS SPACE OVERFLOW
概述
在给一个做好的板子写出厂测试程序. 一共要写20个测试功能.
MCU为STC15F2K60S2. 编译器为 MDK5(keil C51)
开始很正常, 大概完成一半功能实现后, 编译报错如下:
*** ERROR L107: ADDRESS SPACE OVERFLOW
SPACE: DATA
SEGMENT: ?DT?_TX2_WRITE2BUFF?USART
LENGTH: 0001H
*** ERROR L107: ADDRESS SPACE OVERFLOW
SPACE: DATA
SEGMENT: ?DT?_ADC_POWERCONTROL?ADC
LENGTH: 0001H
Program Size: data=131.1 xdata=510 code=14336
Target not created.
Build Time Elapsed: 00:00:01
可以看到 data 超过了127, xdata不到2048, code不到64K.
在网上查了资料, L07的错误原因: 不是RAM超了, 就是ROM超了.
我这的情况是data超过了127bytes.
网上解决方法:
* 将内存模式从Small换成Compact或Large, 再编译通过.
这种方法不适合我现在的场景.
现在我挂了一片32KB的外部RAM, 只能用small模式正常访问. 如果换成Compact或Large, 无法正常访问外部RAM, 读写校验时报错, 看反汇编, 发现操作外部RAM的DPTR, 操作了2次, 每操作1次, DPTR++. small模式只操作了1次.
因为是用C写的, 也没有特别的方法能保证操作外部RAM时和small模式一样.
所以, 必须用small模式来编译.
官方demo也是在small模式下编译的, 换成其他2个内存模式后, 访问外部RAM也是不正常的.
另外以后如果用了RTOS, RTOS也是在small模式下编译的.
所以, 不能改内存模式. 内存模式只能用small
* 将不用的变量去掉.减少内存用量.
想让程序编译过, 现在只能将data小于127才能编译过.
当前情况, 外存用的都很少, ROM空间也剩下的很多. 只要将data用量优化后小于127, 就能编译通过.
如果ROM用量超过了, 就说明MCU选型不对, 就不能选51的MCU了. 或者说, 程序逻辑要改(e.g. 不能包含巨大的测试素材的数组)
像我这个出厂测试程序就是测试这块STC15F2K60S2的板子, MCU不能改, 只要不放很大的素材在ROM中, 64KB写个出厂测试程序还是够的.
现在编译选项采用无优化
从map文件, 可以看到data用量是被谁占用的.
在完全编译, 出现报错后, 内存用量可以在map文件中看到.
需要先打开产生产生map文件的编译选项.
现在去看map文件
用vscode打开map文件
MEMORY MODEL: SMALL
INPUT MODULES INCLUDED:
.\Objects\STARTUP.obj (?C_STARTUP)
.\Objects\main.obj (MAIN)
可以看到, 当前内存模型是small
LINK MAP OF MODULE: .\Objects\krgy_factory_mode (?C_STARTUP)
TYPE BASE LENGTH RELOCATION SEGMENT NAME
-----------------------------------------------------
* * * * * * * D A T A M E M O R Y * * * * * * *
REG 0000H 0008H ABSOLUTE "REG BANK 0"
DATA 0008H 0014H UNIT _DATA_GROUP_
DATA 001CH 0004H UNIT ?DT?_TEST_XRAM_READ_WRITE?UART_CMD_TEST_EX_RAM
BIT 0020H.0 0001H.1 UNIT _BIT_GROUP_
0021H.1 0000H.7 *** GAP ***
DATA 0022H 0009H UNIT ?DT?_MY_ASSERT?APP_CFG
DATA 002BH 0005H UNIT ?DT?_FN_PARSE_USER_INPUT_FORM_UART1?UART_CMD
DATA 0030H 0004H UNIT ?DT?_EXT_INILIZE?EXTI
DATA 0034H 0004H UNIT ?DT?_GPIO_INILIZE?GPIO
DATA 0038H 0004H UNIT ?DT?_USART_CONFIGURATION?USART
DATA 003CH 0004H UNIT ?DT?_GET_ADC10BITRESULT?ADC
DATA 0040H 0003H UNIT ?DT?_IN1_PROCESS_UART_CMD?UART_CMD
DATA 0043H 0003H UNIT ?DT?_FN_PROC_CMD_DEFAULT?UART_CMD
DATA 0046H 0003H UNIT ?DT?_FN_PROC_CMD_HELP?UART_CMD_HELP
DATA 0049H 0003H UNIT ?DT?_FN_PROC_CMD_TEST_IND_LEDS?UART_CMD_TEST_IND_LEDS
DATA 004CH 0003H UNIT ?DT?CLEAR_7SEG_LED?UART_CMD_TEST_7SEG
DATA 004FH 0003H UNIT ?DT?_FN_PROC_CMD_TEST_7SEG?UART_CMD_TEST_7SEG
DATA 0052H 0003H UNIT ?DT?_FN_PROC_CMD_TEST_EXINT_2KEY?UART_CMD_TEST_EXINT_2KEY
DATA 0055H 0003H UNIT ?DT?_FN_PROC_CMD_TEST_KEY16_NORMAL?UART_CMD_TEST_KEY16_NORMAL
DATA 0058H 0003H UNIT ?DT?IO_KEYSCAN_KEY16_NORMAL_READ_ONCE?UART_CMD_TEST_KEY16_NORMAL
DATA 005BH 0003H UNIT ?DT?IO_KEYSCAN_KEY16_NORMAL?UART_CMD_TEST_KEY16_NORMAL
DATA 005EH 0003H UNIT ?DT?_FN_PROC_CMD_TEST_KEY16_ADC?UART_CMD_TEST_KEY16_ADC
DATA 0061H 0003H UNIT ?DT?_FN_PROC_CMD_TEST_EX_RAM?UART_CMD_TEST_EX_RAM
DATA 0064H 0003H UNIT ?DT?_DELAY_MS?DELAY
DATA 0067H 0003H UNIT ?DT?_PRINTSTRING1?USART
DATA 006AH 0003H UNIT ?DT?_PRINTSTRING2?USART
DATA 006DH 0003H UNIT ?DT?_ADC_INILIZE?ADC
DATA 0070H 0002H UNIT ?DT?_SEND_595?UART_CMD_TEST_7SEG
DATA 0072H 0002H UNIT ?DT?_SET_595_VALUE?UART_CMD_TEST_7SEG
DATA 0074H 0002H UNIT ?DT?INIT_7SEG?UART_CMD_TEST_7SEG
DATA 0076H 0002H UNIT ?DT?INIT_KEY16_NORMAL?UART_CMD_TEST_KEY16_NORMAL
DATA 0078H 0002H UNIT ?DT?_SHOW_595_U16?UART_CMD_TEST_KEY16_ADC
DATA 007AH 0002H UNIT ?DT?_ADC_STAND_RANGE?UART_CMD_TEST_KEY16_ADC
DATA 007CH 0002H UNIT ?DT?TESTXRAM?UART_CMD_TEST_EX_RAM
DATA 007EH 0002H UNIT ?DT?UART_CMD_TEST_EX_RAM
IDATA 0080H 0001H UNIT ?STACK
* * * * * * * X D A T A M E M O R Y * * * * * * *
"D A T A M E M O R Y"区域内存, 就是data被谁占用了.
不能优化data内存的项
REG 0000H 0008H ABSOLUTE “REG BANK 0”
- “REG BANK 0” 这个是寄存器组, 必须有的, 我们也操作不到, 只能是这样.
DATA 0008H 0014H UNIT DATA_GROUP
DATA_GROUP 是函数调用链深度用到data, 这个也改不了.
BIT 0020H.0 0001H.1 UNIT BIT_GROUP
位变量用到的data, 这个不用去改.
0021H.1 0000H.7 *** GAP ***
位变量用到的data, 这个不用去改.
IDATA 0080H 0001H UNIT ?STACK
栈用掉的data, 这个改不了.
可以改的data选项
DATA 001CH 0004H UNIT ?DT?_TEST_XRAM_READ_WRITE?UART_CMD_TEST_EX_RAM
类型是DATA, 名称为?DT?X?Y
这种都可以改.
Y代表哪个C文件名称
X代表在Y.C中哪个函数名称
这个工程, 我用了STC15官方的库函数实现.
自己的实现和STC15库函数的实现都是可以改的.
先优先改自己的函数, 实在不行再优化第三方的函数
优化data的方法
有些关键函数不能改. e.g. 内存访问操作, my_assert格式化封装的函数
改了之后, 使用起来极其不方便(e.g. my_assert要打印__FILE_, __LINE, 必须正常将函数入参传进来, 而不能使用全局变量). 或者根本就起作用(e.g. 访问外部RAM, 只能是从xdata地址到data变量的直接访问)
这样的关键函数很少. 如果有这样的函数, 只能保留.
将函数内部的变量改为xdata
在C51中, 函数内部的局部变量, 用的并不是栈空间, 而是全局内存空间.
将函数内部的局部变量改为xdata, 这是最方便的.先做这一步, 再看map文件, 将这种函数局部变量都优化掉.
u8 wait_until_user_input_cmd_from_uart1_special(void)
{
// const char* pDst
BOOL xdata is_cmd_process_ok = FALSE;
将函数入参去掉, 用全局变量代替
如果优化完函数局部变量, 由于函数数量多, 导致还是L107错误.
这时, 只能是将函数入参优化成全局变量传递, 只剩这一招了.
函数参数也占用全局内存空间, 即使函数只有一个u8类型的参数. 也会占用1字节的data空间.
编译器默认会将函数参数分配为data类型
如果有100个以上的函数都要调用, 那么data区的127个字节空间就危险了.
如果xdata空间足够, 可以初步将函数参数由全局变量代替.
在函数调用前, 对全局变量参数赋值.
在函数中, 读写这些全局变量.
将全局变量可以复用的地方, 用union的结构变量
如果xdata空间紧张, 或者考虑到后续xdata空间不够用的原因, 需要再对全局变量空间进行优化.
用union类型的全局变量结构体来代替单独的全局变量, 只要保证这些全局变量不会同时用到, 就可以放到union中用来节省xdata空间.
可以将每个函数的入参, 封装成一个结构体_tag_fn_A, _tag_fn_B.
我代码中的一个例子
typedef struct _tag_function_param_uart_cmd__wait_until_user_input_cmd_from_uart1_special
{
// u8 wait_until_user_input_cmd_from_uart1_special(const char* pDst)
const char* pDst;
}TAG_FUNCTION_PARAM_uart_cmd__wait_until_user_input_cmd_from_uart1_special;
typedef union _un_function_param_uart_cmd {
TAG_FUNCTION_PARAM_uart_cmd__wait_until_user_input_cmd_from_uart1_special wait_until_user_input_cmd_from_uart1_special;
}UN_FUNCTION_PARAM_uart_cmd;
extern UN_FUNCTION_PARAM_uart_cmd xdata g_UN_FUNCTION_PARAM_uart_cmd;
我封装了一个union类型的全局变量, 如果其他函数的入参要放进来, 就在 UN_FUNCTION_PARAM_uart_cmd 中加入其他函数的参数结构
.函数调用前, 对全局变量赋值
g_UN_FUNCTION_PARAM_uart_cmd.wait_until_user_input_cmd_from_uart1_special.pDst = "next";
if (wait_until_user_input_cmd_from_uart1_special())
{
PrintString1("开始检查下一项\r\n");
break;
}
进了函数之后, 对全局参数进行读写
u8 wait_until_user_input_cmd_from_uart1_special(void)
{
// const char* pDst
BOOL xdata is_cmd_process_ok = FALSE;
wait_until_user_input_cmd_from_uart1();
do {
if (NULL == g_UN_FUNCTION_PARAM_uart_cmd.wait_until_user_input_cmd_from_uart1_special.pDst)
{
break;
}
将每个文件所有的函数入参, 封装为一个union. 自己从逻辑上保证不会同时用到这个union(而是调用每个函数前, 才会去填充这个union, 然后再调用函数, 函数内部再取全局union中的函数对应参数), 这样的话, 内存就省很多.
备注
这种优化data的方法已经试过了, 好使.
如果预估到自己的工程如果按照正常的写法会不断消耗data, 但是工程的功能还远没有写完(e.g. 功能现在只完成了一半), 就已经出现了L107错误.
这时, 最好参考map文件, 一次性的将消耗data的函数都优化完.免的临时报佛脚, 只优化眼前的报错, 稍后新写了一段代码, 又出现了L107错误.
新加的函数实现的内部变量和参数, 函数调用, 都按照这种优化data的思路来写.
END
更多推荐
所有评论(0)