本文轉(zhuǎn)自公眾號(hào),歡迎關(guān)注
- 一.過程
- 二.思考
一.過程
關(guān)鍵代碼如下
通過串口驅(qū)動(dòng)接口,注冊串口接收回調(diào)函數(shù),uart_rx_callback
該回調(diào)函數(shù)中如果收到串口數(shù)據(jù),長度非0,則更新全局變量uart_rx_len
主循環(huán)中再檢查全局變量uart_rx_len
,如果大于0說明收到了串口數(shù)據(jù),將收到的數(shù)據(jù)再發(fā)送出去,實(shí)現(xiàn)簡單的串口回環(huán)測試。
static int uart_rx_len = 0;
void uart_rx_callback(const void *buffer, uint32_t length)
{
if(length >0)
{
uart_rx_len = length;
}
}
int main(void)
{
......
debug_uart_init(IOT_UART_PORT_1);
uint8_t buffer[64];
iot_uart_register_rx_callback(IOT_UART_PORT_1,buffer,sizeof(buffer),uart_rx_callback);
while(1)
{
if(uart_rx_len > 0)
{
iot_uart_write_buffer(IOT_UART_PORT_1,buffer,uart_rx_len);
uart_rx_len=0;
}
}
}
現(xiàn)象是并沒有實(shí)現(xiàn)上述回環(huán)測試的功能。
于是進(jìn)行調(diào)試,先確認(rèn)是否進(jìn)入了接收處理,
b uart_rx_callback
發(fā)現(xiàn)可以進(jìn)入該回調(diào)函數(shù),說明收到了數(shù)據(jù)。
step
單步運(yùn)行到執(zhí)行完uart_rx_len = length;
,再查看該變量的值
(gdb) p uart_rx_len
$2 = 1
也確實(shí)收到了一個(gè)字節(jié)。
然后繼續(xù)往下看,看如下條件是否進(jìn)入
if(uart_rx_len > 0)
{
iot_uart_write_buffer(IOT_UART_PORT_1,buffer,uart_rx_len);
uart_rx_len=0;
}
b main.c:146
在iot_uart_write_buffer(IOT_UART_PORT_1,buffer,uart_rx_len
所在行146行,打斷點(diǎn)。
發(fā)現(xiàn)進(jìn)不了該斷點(diǎn)。
這里就比較奇怪了,前面uart_rx_len
確實(shí)是1,了但是這里條件卻進(jìn)不去,其他地方也沒有寫uart_rx_len
的地方。
那么只有繼續(xù)看該處代碼對應(yīng)的匯編代碼
先在uart_rx_callback
前打斷點(diǎn),串口接收一個(gè)字節(jié)觸發(fā)該回調(diào)執(zhí)行。再在 if(uart_rx_len > 0)
所在的行144行前打斷點(diǎn),b main.c:144
,c
運(yùn)行到該處。
此時(shí)看到uart_ex_len
的值是1正確的。
(gdb) p uart_rx_len
$2 = 1
layout split
打開匯編和C對照窗口。
查看變量uart_rx_len
的地址,為0x20300c8
(gdb) p &uart_rx_len
$4 = (int *) 0x20300c8 < uart_rx_len >
(gdb) info reg s1
s1 0x2030000 33751040
S1
寄存器的值設(shè)置為0x2030000, lui s1,0x2030
lui的u表示up(高20位),加載0x2030到S1的高20位。
lw a2,200(S1)
即將S1對應(yīng)的地址偏移200(0xC8)地址處的值加載到a2寄存器。
正好是獲取0x2030C8(uart_rx_len)的值到A2,
然后再執(zhí)行
blez a2,0x20001ba進(jìn)行判斷uart_rx_len和0的值比較,判斷是否往下執(zhí)行還是在此死循環(huán)。
初看沒問題,一細(xì)看就有端倪了。
假設(shè)一開始uart_rx_len=0,
那么后面始終執(zhí)行的是一條語句,blez a2,0x20001ba,a2寄存器的值不再更新了,這就有問題了,內(nèi)存中0x2030C8(uart_rx_len)的值變了,但是寄存器a2的值不再變化。
這就是編譯器自作主張優(yōu)化,生成的代碼沒有繼續(xù)去從內(nèi)存0x2030C8(uart_rx_len)處更新值到a2寄存器了。理論上是需要再次執(zhí)行上述lw a2,200(S1)
指令的。這就編譯器優(yōu)化導(dǎo)致的問題。
我們修改
static int uart_rx_len = 0;
改為
`static volatile int uart_rx_len = 0;``
再來看匯編代碼。
可以看到如果a5小于0,會(huì)跳轉(zhuǎn)到addi s1,s2,200處執(zhí)行再繼續(xù)lw a5,0(S1)處加載uart_rx_len的值到a5,會(huì)不斷從內(nèi)存處更新值到寄存器。
這就是volatile的作用,加了volatile后編譯器始終,會(huì)從內(nèi)存中更新值到寄存器,而不會(huì)自作主張使用寄存器中緩存的值。
默認(rèn)SCons/riscv_tools.py中是Os優(yōu)化,
CCFLAGS = common_flags + [
"-Os",
]
不加volatile且優(yōu)化改為
-O3,-O2,-O1,-O0分別看一下。
可以看到-O1優(yōu)化編譯就進(jìn)行了優(yōu)化,后面-O2,-O3就不用看了。
-O0
-O1
二.思考
這里函數(shù)uart_rx_callback寫了變量uart_rx_len
void uart_rx_callback(const void *buffer, uint32_t length)
{
if(length >0)
{
uart_rx_len = length;
}
}
且uart_rx_callback函數(shù)也作為回調(diào)函數(shù)使用了,理論上編譯器應(yīng)該指導(dǎo)uart_rx_len會(huì)被改寫,不應(yīng)該作此優(yōu)化。
手動(dòng)調(diào)用以下uart_rx_callback
uart_rx_callback(0, 0);
debug_uart_init(IOT_UART_PORT_1);
還是一樣的優(yōu)化了
看來編譯器還是聰明過頭了。
這里主要是
while(1)后面第一條語句就是判斷uart_rx_len,如果之前還有其他語句,則編譯器可能不會(huì)優(yōu)化了。
while(1)
{
if(uart_rx_len > 0)
{
iot_uart_write_buffer(IOT_UART_PORT_1,buffer,uart_rx_len);
uart_rx_len=0;
}
}
審核編輯:湯梓紅
-
嵌入式
+關(guān)注
關(guān)注
5082文章
19104瀏覽量
304791 -
串口
+關(guān)注
關(guān)注
14文章
1551瀏覽量
76421 -
優(yōu)化
+關(guān)注
關(guān)注
0文章
220瀏覽量
23890 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4327瀏覽量
62569 -
RISC-V
+關(guān)注
關(guān)注
45文章
2270瀏覽量
46125
發(fā)布評論請先 登錄
相關(guān)推薦
評論