hsrzq 发表于 2025-5-28 13:51:34

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

<pre><code class="language-C">__xdata volatile int var0;
__xdata volatile int var1 = 1;

void main(void) {
    // 功能代码
}
</code></pre>
<p>以上这个简单到发指的程序,在STC8A8K64U中遇到个奇怪问题:<code>var0</code>的初始值有时候不是0,而 <code>var1</code>的初始值也不是1,是一些奇怪的随机值。<br />
第一感觉就是XRAM的初始化可能存在BUG,于是稍微了解了一下SDCC中XRAM初始化的原理,没想到还真发现了一些好玩的东西,给大家做个分享。</p>
<p>一些前置知识。</p>
<ol>
<li>无论是Keil还是SDCC,在真正执行main函数之前,会自动插入一些初始化代码,这里面就包括RAM、IRAM和XRAM的初始化等。</li>
<li>在从C编译成汇编的过程中,编译器会按照功能和特性的不同,会生成很多辅助的段或者叫做小节。与本问题相关的小节主要有:</li>
</ol>
<pre><code class="language-asm">;--------------------------------------------------------
; 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;
</code></pre>
<p>未初始化外部变量存储区 <code>XSEG</code>,<code>var0</code>就属于这个区,因为它定义在外部扩展RAM中(__xdata),且代码中没有给它赋初始值;</p>
<p>已初始化外部变量存储区 <code>XISEG</code>,<code>var1</code>就属于这个区,因为它定义在外部扩展RAM中(__xdata),且代码中有给它赋初始值;</p>
<p>已初始化外部变量初始值区 <code>XINIT</code>,注意这个实际上是在代码区里面,是ROM不是RAM,最终会烧录在flash的特定位置。</p>
<p>另外,在编译过程中会生成一些辅助变量,s_是特定区的起始位置,l_是特定区的长度。如 <code>s_XSEG</code>就是 <code>XSEG</code>区的起始位置。</p>
<p>按照C语言规范,未初始化的全局变量(如 <code>var0</code>)应当默认给初始值0,这个SDCC帮我们做了,代码在 <code>crtxclear.asm</code>中:</p>
<pre><code class="language-asm">.globl __XPAGE

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

00006$:
        mov        r0,#l_XSEG
        mov        a,r0
        orl        a,#(l_XSEG &gt;&gt; 8)
        jz        00008$
        mov        r1,#((l_XSEG + 255) &gt;&gt; 8)
        mov        dptr,#s_XSEG
        clr   a
00007$:        movx        @dptr,a
        inc        dptr
        djnz        r0,00007$
        djnz        r1,00007$
00008$:
</code></pre>
<p>已初始化的全局变量(如var1),也需要执行一些操作才能有初始值,这个SDCC帮我们做了,代码在 <code>crtxinit.asm</code>中:</p>
<pre><code class="language-asm">.globl __XPAGE

__mcs51_genXINIT::
        mov        r1,#l_XINIT
        mov        a,r1
        orl        a,#(l_XINIT &gt;&gt; 8)
        jz        00003$
        mov        r2,#((l_XINIT+255) &gt;&gt; 8)
        mov        dptr,#s_XINIT
        mov        r0,#s_XISEG
        mov        __XPAGE,#(s_XISEG &gt;&gt; 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$:
</code></pre>
<p>其实这里就已经能发现问题了,当给XRAM内存赋值时,使用的地址寄存是 <code>R0</code>和 <code>R1</code>。而 <code>R0</code>和 <code>R1</code>只是一个8位的寄存器,还需要其它方式组合出16位地址,才能完全访问STC8A8K64U完整8KB的XRAM。从代码里面看到使用 <code>__XPAGE</code>做了高8位地址,那么就继续追查 <code>__XPAGE</code>是个什么东西。最终是在 <code>crtpagesfr.asm</code>里面:</p>
<pre><code class="language-asm">__XPAGE == 0xa0        ; 0xa0 is P2 on the original 8051
</code></pre>
<p>这个 <code>__XPAGE</code>不就是P2端口么?所有问题豁然开朗。<br />
在传统8052单片机中,如果要扩展RAM,是需要实实在在外接RAM芯片的。<code>P2</code>接RAM芯片的地址高8位,<code>P0</code>口接RAM芯片地址低8位和数据位。SDCC默认的实现正是基于这一点,用 <code>__XPAGE</code>(也就是 <code>P2</code>)锁存了XRAM地址高8位,然后只需要操作地址低8位和数据即可(都是 <code>P0</code>)。<br />
<strong>这在当时的硬件下应该能减少端口操作提升效率,但这并不适合现在STC系列的所有单片机,因为STC的XRAM其实也是内置的,和 <code>P2</code>、<code>P0</code>没有丝毫关系,再按当年的方式来操作必然不能正确生效了。</strong></p>
<p>到这里解决方案其实已经呼之欲出了,重写SDCC内置的方法就能解决问题。已知需要重写的有 <code>crtxclear.asm</code>、<code>crtxinit.asm</code>和 <code>crtxstack.asm</code>,这个等以后有空了再继续分享吧!</p>

国芯人工智能 发表于 2025-5-28 13:53:00

在STC8A8K64U芯片中运行以下简单的C程序时,发现var0和var1的初始值不正确的问题。以下是详细的分析和解决方法:

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

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

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

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

b. SDCC初始化段
SDCC的初始化段如下:
c
area XSEG(XDATA)
var0:: ds 2;
area XISEG(XDATA)
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,以获取更详细的错误信息。

[本文内容由国芯人工智能辅助生成,仅供参考]

C_wolf 发表于 2025-5-28 14:07:54

不但要知其然,还要知其所以然。{:qiang:}

ercircle 发表于 2025-5-28 14:15:53

知识点++,还是函数内使用前初始化吧

hsrzq 发表于 2025-5-28 23:24:11

<p>解决方案来了!重写了 <code>__mcs51_genXINIT</code>和 <code>__mcs51_genXRAMCLEAR</code>方法。</p>
<p>其中 <code>__mcs51_genXINIT</code>用了非常“骚气”的双DPTR指针和DPTR自增特性。好玩!好用!</p>
<p>编译时会报这两个警告,算是正常现象吧:<br />
?ASlink-Warning-Definition of public symbol '__mcs51_genXINIT' found more than once<br />
?ASlink-Warning-Definition of public symbol '__mcs51_genXRAMCLEAR' found more than once</p>
<pre><code class="language-asm">; ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
;   crtxfix.asm: C Runtime XRAM Operations Fix For STC-51 Only
;
;   Copyright (C) 2025 TechQI&lt;techqi@126.com&gt;
;
;   Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
;   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 &quot;AS IS&quot; 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 &gt;&gt; 8)
        jz          1001$
        mov   R1, #((l_XINIT+255) &gt;&gt; 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 &gt;&gt; 8)
    jz          2003$
    mov          R1, #((l_XSEG + 255) &gt;&gt; 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
</code></pre>
<p><a href="forum.php?mod=attachment&amp;aid=101821" title="attachment"><img src="/source/plugin/zhanmishu_markdown/template/editor/images/upload.svg" alt="upload" /> 附件:crtxfix.asm</a></p>
页: [1]
查看完整版本: 关于SDCC下XRAM变量初始值可能不正确的问题