R0 нарушается после возврата функции

#function #arm #return-value

#функция #arm #возвращаемое значение

Вопрос:

Я реализую очередь uart в s3c44b0x (ARM7TDMI), uart0 ISR поставит символ в очередь, в то время как основной цикл удалит символ из очереди. однако при удалении из очереди возвращаемое значение (в R0) может быть не тем, которое было удалено из очереди, и я обнаружил, что R0 нарушается после возврата из функции удаления из очереди (ввод ‘v’ продолжается, и test () находится в основном цикле): желаю вашей помощи.

 CHAR cliDequeue(void)
{
    CHAR bTmpCh;

    if (gwCliQSize == 0)
    {
        return 0;
    }

    bTmpCh = gabCliQ[gwCliQTail];  /* char is enqueued in the Q in ISR */
    gwCliQTail  ;
    gwCliQTail %= MAX_CLI_QUEUE_LEN;
    ASSERT(gwCliQSize > 0);
    gwCliQSize--;

    ASSERT(bTmpCh == 'v');   /* will not assert */
    //uartPutChar(bTmpCh);
    return bTmpCh;
}

void test(void)
{
    CHAR bTestCh;

    bTestCh = cliDequeue();


    if (bTestCh != 0)
    { 
        ASSERT(bTestCh == 'v');  /* assert here ! */
            uartPutChar(bTestCh);

    }
}
  

Комментарии:

1. это происходит не каждый раз, просто иногда, примерно в 1 случае из 10.

Ответ №1:

У нас недостаточно информации / контекста для окончательного ответа. Также было бы полезно, если бы вы опубликовали соответствующий код сборки, чтобы мы могли видеть, как / когда вещи перемещаются в R0 и из него. Несмотря на это, несколько вещей сразу приходят на ум из вашего опубликованного C-кода.

(0) Являются ли переменные общими для прерываний и основного цикла, объявленные как volatile ?

(1) В CliDequeue вы получаете доступ к массиву, который совместно используется с ISR. Похоже, что это конструкция с одним устройством чтения / записи, так что автоматически это не плохо, но ваше ведение домашнего хозяйства не является герметичным.

Например, один инвариант, который вы должны обязательно соблюдать, заключается в том, что размер очереди и указатель хвоста синхронизированы. Тем не менее, если эта процедура не вызывается с отключенными прерываниями, ваш конечный указатель и размер очереди не корректируются как единая транзакция.

(2) Кроме того, я бы предположил, что gwCliQSize это также корректируется в прерывании (увеличивается в ISR, уменьшается в приложении). Другое условие гонки. Для выполнения gwCliQSize-- , за кулисами вы, вероятно, считываете данные из памяти в регистр, уменьшаете регистр, а затем записываете его обратно. Что произойдет, если вы считываете 5 из памяти в R1, затем запускается прерывание и увеличивает его до 6, затем вы выходите из ISR, а регистр уменьшается и выполняется обратная запись (со значением 4).

(3) Наконец, возможно (хотя и не слишком вероятно), что bTmpCh or bTestCh хранятся в стеке, и что ваш стек поврежден / захлопнут другой задачей / прерыванием / и т.д. Итак, когда ваш assert завершается с ошибкой, вы думаете, что поврежден R0, но на самом деле может быть, что значение, перемещенное в R0 перед возвратом, или значение, перемещенное из R0 в переменную стека, становится искаженным.

Я уже достаточно наговорил. Есть и другие возможности, но из того, что вы опубликовали (и не опубликовали), невозможно сказать наверняка.

P.S. Если вы использовали отладчик, и повреждается действительно и буквально значение R0, а не только значение символа в очереди, это указывает на проблему в вашем планировщике / переключателе контекста / ISR до или после перехода и т.д…

Ответ №2:

вот ассемблерный код: для теста():

 0x00001308  E92D4010  STMDB     R13!,{R4,R14}
    37:     bTestCh = cliDequeue(); 
    38:          
0x0000130C  EB000207  BL        cliDequeue(0x00001B30)
0x00001310  E1A04000  MOV       R4,R0
    39:     if (bTestCh != 0) 
    40:     {  
0x00001314  E3540000  CMP       R4,#pTest(0x00000000)
0x00001318  0A000007  BEQ       0x0000133C
    41:         ASSERT(bTestCh == 'v'); 
0x0000131C  E1A00000  NOP       
0x00001320  E3540076  CMP       R4,#0x00000076
0x00001324  0A000001  BEQ       0x00001330
0x00001328  E1A00000  NOP       
0x0000132C  EAFFFFFE  B         0x0000132C
0x00001330  E1A00000  NOP       
    42:         uartPutChar(bTestCh); 
    43:     } 
0x00001334  E1A00004  MOV       R0,R4
0x00001338  EB00014A  BL        uartPutChar(0x00001868)
    44: } 
    45:  
    46: int main(void) 
0x0000133C  E8BD4010  LDMIA     R13!,{R4,R14}
0x00001340  E12FFF1E  BX        R14
  

для cliDequeu(), кстати, gwCliQSize определяется как
UINT32 volatile gwCliQSize;

 0x00001B30  E59F00D4  LDR       R0,[PC,#0x00D4]
0x00001B34  E5900000  LDR       R0,[R0]
0x00001B38  E3500000  CMP       R0,#pTest(0x00000000)
0x00001B3C  1A000001  BNE       0x00001B48
    78:         return 0; 
    79:     } 
    80:      
    81:     bTmpCh = gabCliQ[gwCliQTail]; 
    82:     gwCliQTail  ; 
    83:     gwCliQTail %= MAX_CLI_QUEUE_LEN; 
    84:     ASSERT(gwCliQSize > 0); 
    85:     gwCliQSize--; 
    86:  
    87:     //chCheck(bTmpCh); 
    88:          ASSERT(bTmpCh == 'v');   /* will not assert */ 
    89:     //uartPutChar(bTmpCh); 
    90:      
    91:     return bTmpCh; 
0x00001B40  E3A00000  MOV       R0,#pTest(0x00000000)
    92: } 
    93:  
    94:  
    95: void cliQInit(void) 
0x00001B44  E12FFF1E  BX        R14
    81:     bTmpCh = gabCliQ[gwCliQTail]; 
0x00001B48  E59F00C0  LDR       R0,[PC,#0x00C0]
0x00001B4C  E59F20C4  LDR       R2,[PC,#0x00C4]
0x00001B50  E5922000  LDR       R2,[R2]
0x00001B54  E7D01002  LDRB      R1,[R0,R2]
    82:     gwCliQTail  ; 
0x00001B58  E59F00B8  LDR       R0,[PC,#0x00B8]
0x00001B5C  E5900000  LDR       R0,[R0]
0x00001B60  E2800001  ADD       R0,R0,#0x00000001
0x00001B64  E59F20AC  LDR       R2,[PC,#0x00AC]
0x00001B68  E5820000  STR       R0,[R2]
    83:     gwCliQTail %= MAX_CLI_QUEUE_LEN; 
0x00001B6C  E2820000  ADD       R0,R2,#pTest(0x00000000)
0x00001B70  E5900000  LDR       R0,[R0]
0x00001B74  E20000FF  AND       R0,R0,#0x000000FF
0x00001B78  E5820000  STR       R0,[R2]
    84:     ASSERT(gwCliQSize > 0); 
0x00001B7C  E1A00000  NOP       
0x00001B80  E59F0084  LDR       R0,[PC,#0x0084]
0x00001B84  E5900000  LDR       R0,[R0]
0x00001B88  E3500000  CMP       R0,#pTest(0x00000000)
0x00001B8C  1A000001  BNE       0x00001B98
0x00001B90  E1A00000  NOP       
0x00001B94  EAFFFFFE  B         0x00001B94
0x00001B98  E1A00000  NOP       
    85:     gwCliQSize--; 
    86:  
    87:     //chCheck(bTmpCh); 
0x00001B9C  E59F0068  LDR       R0,[PC,#0x0068]
0x00001BA0  E5900000  LDR       R0,[R0]
0x00001BA4  E2400001  SUB       R0,R0,#0x00000001
0x00001BA8  E59F205C  LDR       R2,[PC,#0x005C]
0x00001BAC  E5820000  STR       R0,[R2]
    88:          ASSERT(bTmpCh == 'v');   /* will not assert */ 
    89:     //uartPutChar(bTmpCh); 
    90:      
0x00001BB0  E1A00000  NOP       
0x00001BB4  E3510076  CMP       R1,#0x00000076
0x00001BB8  0A000001  BEQ       0x00001BC4
0x00001BBC  E1A00000  NOP       
0x00001BC0  EAFFFFFE  B         0x00001BC0
0x00001BC4  E1A00000  NOP       
    91:     return bTmpCh; 
    92: } 
    93:  
    94:  
    95: void cliQInit(void) 
0x00001BC8  E1A00001  MOV       R0,R1
0x00001BCC  EAFFFFDC  B         0x00001B44
  

для cliEnqueue:

 void cliEnqueue(CHAR bC)
{
    if (gwCliQSize == MAX_CLI_QUEUE_LEN)
    {
        ASSERT(0);
    }

    gabCliQ[gwCliQHeader] = bC;
    gwCliQHeader  ;
    gwCliQHeader %= MAX_CLI_QUEUE_LEN;
    gwCliQSize  ;
}
  

сборка:

 0x00001A5C  E59F11AC  LDR       R1,[PC,#0x01AC]
0x00001A60  E59F21AC  LDR       R2,[PC,#0x01AC]
0x00001A64  E5922000  LDR       R2,[R2]
0x00001A68  E7C10002  STRB      R0,[R1,R2]
    25:     gwCliQHeader  ; 
0x00001A6C  E59F11A0  LDR       R1,[PC,#0x01A0]
0x00001A70  E5911000  LDR       R1,[R1]
0x00001A74  E2811001  ADD       R1,R1,#0x00000001
0x00001A78  E59F2194  LDR       R2,[PC,#0x0194]
0x00001A7C  E5821000  STR       R1,[R2]
    26:     gwCliQHeader %= MAX_CLI_QUEUE_LEN; 
0x00001A80  E2821000  ADD       R1,R2,#pTest(0x00000000)
0x00001A84  E5911000  LDR       R1,[R1]
0x00001A88  E20110FF  AND       R1,R1,#0x000000FF
0x00001A8C  E5821000  STR       R1,[R2]
    27:     gwCliQSize  ; 
0x00001A90  E59F1174  LDR       R1,[PC,#0x0174]
0x00001A94  E5911000  LDR       R1,[R1]
0x00001A98  E2811001  ADD       R1,R1,#0x00000001
0x00001A9C  E59F2168  LDR       R2,[PC,#0x0168]
0x00001AA0  E5821000  STR       R1,[R2]
    28: } 
    29:  
    30:  
    31: static void chCheck(CHAR cTmpChar) 
    32: { 
0x00001AA4  E12FFF1E  BX        R14
  

0), 1): что gwCliQSize и массив являются общими для ISR и основного цикла.

2) gwCliQSize определяется как volatile 3) в сборке bTestCh равен R4 (перемещен из R0), а bTmpCh равен R1 (перемещен в R0 перед B) 4) Я использую J-LINK, но без J-LINK (запуск из flash) он все еще существует.

Комментарии:

1. R4 и R0 совпадают при сбое, поэтому я думаю, что R0 поврежден, тогда неверное значение перемещается в R4. при другом размере, я думаю, R1 является правильным, поскольку передается ASSERT() в cliDequeue().