找回密码
 立即注册
查看: 170|回复: 6

关于SDCC下XRAM变量初始值可能不正确的问题

[复制链接]
  • 打卡等级:以坛为家II
  • 打卡总天数:500
  • 最近打卡:2025-06-16 10:39:44
已绑定手机

29

主题

349

回帖

1991

积分

金牌会员

机长

积分
1991
发表于 2025-5-28 13:51:34 | 显示全部楼层 |阅读模式
__xdata volatile int var0;
__xdata volatile int var1 = 1;

void main(void) {
    // 功能代码
}

以上这个简单到发指的程序,在STC8A8K64U中遇到个奇怪问题:var0的初始值有时候不是0,而 var1的初始值也不是1,是一些奇怪的随机值。
第一感觉就是XRAM的初始化可能存在BUG,于是稍微了解了一下SDCC中XRAM初始化的原理,没想到还真发现了一些好玩的东西,给大家做个分享。

一些前置知识。

  1. 无论是Keil还是SDCC,在真正执行main函数之前,会自动插入一些初始化代码,这里面就包括RAM、IRAM和XRAM的初始化等。
  2. 在从C编译成汇编的过程中,编译器会按照功能和特性的不同,会生成很多辅助的段或者叫做小节。与本问题相关的小节主要有:
;--------------------------------------------------------
; uninitialized external ram data
;--------------------------------------------------------
	.area XSEG    (XDATA)
_var0::
	.ds 2
;--------------------------------------------------------
; initialized external ram data
;--------------------------------------------------------
	.area XISEG   (XDATA)
_var1::
	.ds 2
;--------------------------------------------------------
; external ram initial data
;--------------------------------------------------------
    .area XINIT   (CODE)
__xinit__var1:
	.byte #0x01, #0x00;

未初始化外部变量存储区 XSEGvar0就属于这个区,因为它定义在外部扩展RAM中(__xdata),且代码中没有给它赋初始值;

已初始化外部变量存储区 XISEGvar1就属于这个区,因为它定义在外部扩展RAM中(__xdata),且代码中有给它赋初始值;

已初始化外部变量初始值区 XINIT,注意这个实际上是在代码区里面,是ROM不是RAM,最终会烧录在flash的特定位置。

另外,在编译过程中会生成一些辅助变量,s_是特定区的起始位置,l_是特定区的长度。如 s_XSEG就是 XSEG区的起始位置。

按照C语言规范,未初始化的全局变量(如 var0)应当默认给初始值0,这个SDCC帮我们做了,代码在 crtxclear.asm中:

.globl __XPAGE

__mcs51_genXRAMCLEAR::
	mov	r0,#l_PSEG
	mov	a,r0
	orl	a,#(l_PSEG >> 8)
	jz	00006$
	mov	r1,#s_PSEG
	mov	__XPAGE,#(s_PSEG >> 8)
	clr     a
00005$:	movx	@r1,a
	inc	r1
	djnz	r0,00005$

00006$:
	mov	r0,#l_XSEG
	mov	a,r0
	orl	a,#(l_XSEG >> 8)
	jz	00008$
	mov	r1,#((l_XSEG + 255) >> 8)
	mov	dptr,#s_XSEG
	clr     a
00007$:	movx	@dptr,a
	inc	dptr
	djnz	r0,00007$
	djnz	r1,00007$
00008$:

已初始化的全局变量(如var1),也需要执行一些操作才能有初始值,这个SDCC帮我们做了,代码在 crtxinit.asm中:

.globl __XPAGE

__mcs51_genXINIT::
	mov	r1,#l_XINIT
	mov	a,r1
	orl	a,#(l_XINIT >> 8)
	jz	00003$
	mov	r2,#((l_XINIT+255) >> 8)
	mov	dptr,#s_XINIT
	mov	r0,#s_XISEG
	mov	__XPAGE,#(s_XISEG >> 8)
00001$:	clr	a
	movc	a,@a+dptr
	movx	@r0,a
	inc	dptr
	inc	r0
	cjne	r0,#0,00002$
	inc	__XPAGE
00002$:	djnz	r1,00001$
	djnz	r2,00001$
	mov	__XPAGE,#0xFF
00003$:

其实这里就已经能发现问题了,当给XRAM内存赋值时,使用的地址寄存是 R0R1。而 R0R1只是一个8位的寄存器,还需要其它方式组合出16位地址,才能完全访问STC8A8K64U完整8KB的XRAM。从代码里面看到使用 __XPAGE做了高8位地址,那么就继续追查 __XPAGE是个什么东西。最终是在 crtpagesfr.asm里面:

__XPAGE == 0xa0	; 0xa0 is P2 on the original 8051

这个 __XPAGE不就是P2端口么?所有问题豁然开朗。
在传统8052单片机中,如果要扩展RAM,是需要实实在在外接RAM芯片的。P2接RAM芯片的地址高8位,P0口接RAM芯片地址低8位和数据位。SDCC默认的实现正是基于这一点,用 __XPAGE(也就是 P2)锁存了XRAM地址高8位,然后只需要操作地址低8位和数据即可(都是 P0)。
这在当时的硬件下应该能减少端口操作提升效率,但这并不适合现在STC系列的所有单片机,因为STC的XRAM其实也是内置的,和 P2P0没有丝毫关系,再按当年的方式来操作必然不能正确生效了。

到这里解决方案其实已经呼之欲出了,重写SDCC内置的方法就能解决问题。已知需要重写的有 crtxclear.asmcrtxinit.asmcrtxstack.asm,这个等以后有空了再继续分享吧!

1 喜欢他/她就送朵鲜花吧,赠人玫瑰,手有余香!
业余撸代码,专业开飞机 https://gitee.com/STC-MCU
回复

使用道具 举报 送花

3

主题

714

回帖

139

积分

等待验证会员

积分
139
发表于 2025-5-28 13:53:00 | 显示全部楼层
在STC8A8K64U芯片中运行以下简单的C程序时,发现var0和var1的初始值不正确的问题。以下是详细的分析和解决方法:

1. 程序分析
代码如下:
  1. c
  2. void main(void) {
  3.     // 功能代码
  4. }
复制代码

2. 问题背景
在运行该程序时,发现var0的初始值不为0,var1的初始值不为1。这表明XRAM的初始化可能存在错误。

3. XRAM初始化原理
SDCC在编译时会根据功能段处理RAM和IRAM的数据。以下是XRAM初始化的逻辑:

a. Keil初始化段
Keil部分设置了一些变量:
  1. c
  2. var0:: ds 2;
复制代码
这表明var0被初始化为2。

b. SDCC初始化段
SDCC的初始化段如下:
  1. c
  2. area XSEG  (XDATA)
  3. var0:: ds 2;
  4. area XISEG  (XDATA)
  5. var1:: ds 2;
复制代码
这表明XRAM部分初始化了var0和var1为2。

4. 问题分析
根据代码,var0被初始化为2,而var1被初始化为1。然而,用户观察到var0的初始值不为0,var1的初始值不为1。这表明XRAM的初始化可能存在问题。

5. 可能原因
1. SDCC初始化段设置错误:
可能未正确设置XRAM的初始化值。
未正确配置XRAM的访问权限。

2. Keil初始化段配置错误:
可能未正确配置Keil的初始化段。
未正确处理Keil数据在编译时的处理。

3. 编译选项设置错误:
未正确设置XRAM的初始化选项。
未正确配置SDCC的XRAM初始化方式。

6. 解决方案
检查SDCC初始化段:
确保XRAM部分的初始化值正确设置为0或1。
确保XRAM的访问权限正确。
检查Keil初始化段:
确保KeIL数据在编译时处理正确。
确保KeIL的初始化段配置正确。
检查编译选项:
确保SDCC的XRAM初始化选项正确设置。
确保KeIL的XRAM初始化选项正确设置。

7. 步骤总结
1. 检查SDCC初始化段:
确认XRAM部分的初始化值是否为0或1。
确认XRAM的访问权限是否正确。

2. 检查KeIL初始化段:
确认KeIL数据是否正确设置。
确保KeIL的初始化段配置正确。

3. 检查编译选项:
确保SDCC的XRAM初始化选项正确设置。
确保KeIL的XRAM初始化选项正确设置。

8. 验证
运行程序后,检查var0和var1的初始值是否正确。

9. 结论
通过上述步骤,可以发现XRAM的初始化存在错误,具体原因可能涉及SDCC初始化段或KeIL初始化段的配置问题。通过检查SDCC和KeIL的初始化段,可以发现并修复这个问题。

如果问题仍然存在,建议进一步使用调试工具,例如GDB或LLVM,以获取更详细的错误信息。

[本文内容由国芯人工智能辅助生成,仅供参考]
回复 支持 0 反对 1

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:236
  • 最近打卡:2025-06-16 00:04:38
已绑定手机

7

主题

97

回帖

1197

积分

金牌会员

积分
1197
发表于 2025-5-28 14:07:54 | 显示全部楼层
不但要知其然,还要知其所以然。
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:208
  • 最近打卡:2025-06-16 08:41:37
已绑定手机

67

主题

1920

回帖

4311

积分

荣誉版主

无情的代码机器

积分
4311
发表于 2025-5-28 14:15:53 | 显示全部楼层
知识点++,还是函数内使用前初始化吧

点评

是的,函数内初始化最保险。但我现在在搞FreeRTOS,它有全局初始化的变量,不想动OS本身代码……  发表于 2025-5-28 23:42
三天不学习,赶不上刘少奇~
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:500
  • 最近打卡:2025-06-16 10:39:44
已绑定手机

29

主题

349

回帖

1991

积分

金牌会员

机长

积分
1991
发表于 2025-5-28 23:24:11 | 显示全部楼层

解决方案来了!重写了 __mcs51_genXINIT__mcs51_genXRAMCLEAR方法。

其中 __mcs51_genXINIT用了非常“骚气”的双DPTR指针和DPTR自增特性。好玩!好用!

编译时会报这两个警告,算是正常现象吧:
?ASlink-Warning-Definition of public symbol '__mcs51_genXINIT' found more than once
?ASlink-Warning-Definition of public symbol '__mcs51_genXRAMCLEAR' found more than once

; ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
;   crtxfix.asm: C Runtime XRAM Operations Fix For STC-51 Only
;
;   Copyright (C) 2025 TechQI<techqi@126.com>
;
;   Licensed under the Apache License, Version 2.0 (the "License");
;   you may not use this file except in compliance with the License.
;   You may obtain a copy of the License at
;
;       http://www.apache.org/licenses/LICENSE-2.0
;
;   Unless required by applicable law or agreed to in writing, software
;   distributed under the License is distributed on an "AS IS" BASIS,
;   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;   See the License for the specific language governing permissions and
;   limitations under the License.
; ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓


.area GSINIT3 (CODE)
.globl _DPS
; -------------------------------------------------------------------------
; copy XINIT to XISEG, with auto-incremented DUAL-DPTR
; -------------------------------------------------------------------------
__mcs51_genXINIT::
	mov     R0,#l_XINIT
	mov     A, R0
	orl     A, #(l_XINIT >> 8)
	jz	    1001$
	mov     R1, #((l_XINIT+255) >> 8)
	mov     _DPS, #0x38     ; ID1=0, ID0=0, TSL=1, AU1=1, AU0=1, SEL=0
	mov	    DPTR, #s_XINIT	; DPTR0 for code
	mov     DPTR, #s_XISEG	; DPTR1 for xdata
1000$:
    clr     A
	movc	A, @A+DPTR      ; read code byte (increment DPTR0 automatically) 
	movx	@DPTR, A        ; write to xdata (increment DPTR1 automatically)
	djnz	R0, 1000$
	djnz	R1, 1000$
    mov     _DPS, #0x00
1001$:


.area GSINIT4 (CODE)
; -------------------------------------------------------------------------
; Set uninitialized XRAM to zero
; -------------------------------------------------------------------------
__mcs51_genXRAMCLEAR::
; PDATA
    mov     R0, #l_PSEG
    mov     A, R0
    jz      2001$
    mov     DPH, #0
    mov     DPL, #s_PSEG
    clr     A
2000$:
    mov     _DPS, #0x18     ; ID1=0, ID0=0, TSL=0, AU1=1, AU0=1, SEL=0
	movx	@DPTR, A        ; write to xdata (increment DPTR0 automatically)
    djnz	R0, 2000$

; XDATA
2001$:
    mov     R0, #l_XSEG
    mov	    A, R0
    orl	    A, #(l_XSEG >> 8)
    jz	    2003$
    mov	    R1, #((l_XSEG + 255) >> 8)
    mov	    DPTR, #s_XSEG
    clr     A
2002$:
    mov     _DPS, #0x18     ; ID1=0, ID0=0, TSL=0, AU1=1, AU0=1, SEL=0
    movx	@DPTR, A        ; write to xdata (increment DPTR0 automatically)
    djnz	R0, 2002$
    djnz	R1, 2002$
2003$:
    mov     _DPS, #0x00

upload 附件:crtxfix.asm

业余撸代码,专业开飞机 https://gitee.com/STC-MCU
回复 支持 反对

使用道具 举报 送花

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

本版积分规则

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

GMT+8, 2025-6-16 22:13 , Processed in 0.391183 second(s), 82 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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