#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().