• Keil C51 - ERROR L107: ADDRESS SPACE OVERFLOW


    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    可以看到 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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    可以看到, 当前内存模型是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  * * * * * * *
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    "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
    
    • 1

    类型是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;
    
    • 1
    • 2
    • 3
    • 4

    将函数入参去掉, 用全局变量代替

    如果优化完函数局部变量, 由于函数数量多, 导致还是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;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    我封装了一个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;
    		}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    进了函数之后, 对全局参数进行读写

    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;
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    将每个文件所有的函数入参, 封装为一个union. 自己从逻辑上保证不会同时用到这个union(而是调用每个函数前, 才会去填充这个union, 然后再调用函数, 函数内部再取全局union中的函数对应参数), 这样的话, 内存就省很多.

    备注

    这种优化data的方法已经试过了, 好使.
    如果预估到自己的工程如果按照正常的写法会不断消耗data, 但是工程的功能还远没有写完(e.g. 功能现在只完成了一半), 就已经出现了L107错误.
    这时, 最好参考map文件, 一次性的将消耗data的函数都优化完.免的临时报佛脚, 只优化眼前的报错, 稍后新写了一段代码, 又出现了L107错误.
    新加的函数实现的内部变量和参数, 函数调用, 都按照这种优化data的思路来写.

    END

  • 相关阅读:
    SpringCloud Alibaba(七) - JWT(JSON Web Token)
    Spring 声明式事务机制
    信息学奥赛一本通-编程启蒙3349:练60.3 余数个数
    【GIS前言】OGIS如何跨分布式计算平台
    .net Framwork请求https携带客户端证书
    常见python脚本集合
    揭秘Karmada百倍集群规模多云基础设施体系
    vue学习之 v-for key
    Python采集 11月最新 世界疫情数据 + 可视化动态地图,实时查询超稳定
    TI xWR系列毫米波雷达如何使用MATLAB自动连接串口?
  • 原文地址:https://blog.csdn.net/LostSpeed/article/details/127685479