一颂郎,前言
之前做了基于Segger RTT的上下位機(jī)谒获,來測試os的task調(diào)度扳埂。我向來對整體系統(tǒng)(上下位機(jī)的通信很感興趣)知道了RTT的原理业簿,所以就進(jìn)行了資源利用,后來底層我改成了自己寫的精簡FIFO聂喇,也很好用辖源。那么threadx的traceX模塊設(shè)計(jì)是否也能為我所用呢蔚携!
二,帶著問題看源碼
1. threadx中的traceX模塊的上下位機(jī)設(shè)計(jì)原理和RTT一樣嗎克饶?
答:不一樣酝蜒。我看了Azure RTOS TraceX的使用,它描述是轉(zhuǎn)儲(將內(nèi)存數(shù)據(jù)轉(zhuǎn)為s19等上位機(jī)工具支持的格式)矾湃,而且是事后查看亡脑。于是我去看了單片機(jī)端的code,就找到了TX_TRACE_IN_LINE_INSERT邀跃。也就是FIFO進(jìn)入的函數(shù)霉咨,沒有找到退出的函數(shù),且用循環(huán)覆蓋來實(shí)現(xiàn)環(huán)形FIFO拍屑。所以我理解上位機(jī)是不支持實(shí)時(shí)查看的途戒,等于單片機(jī)一直把log放入FIFO內(nèi)存,等用戶暫停調(diào)試單片機(jī)的時(shí)候僵驰,可以將內(nèi)存取出轉(zhuǎn)儲后喷斋,通過上位機(jī)來查看切換情況。
2.TX_TRACE_IN_LINE_INSERT入隊(duì)列中怎么沒有開關(guān)中斷蒜茴?
答:RTT主要用來實(shí)時(shí)打印及和上位機(jī)交互數(shù)據(jù)的(其實(shí)是用來代替串口的)星爪,所用不同優(yōu)先級的task會去調(diào)用它的寫數(shù)據(jù)函數(shù),就存在一個(gè)是否函數(shù)可重入的問題粉私,由于多個(gè)task用一個(gè)資源顽腾,那么就需要用到互斥,也就是要加開關(guān)全局中斷的诺核。當(dāng)然抄肖,全局開關(guān)中斷也不能加太多,否則會影響整體系統(tǒng)運(yùn)行猪瞬,但是不添加的話憎瘸,可能打印的log準(zhǔn)確性可能會有問題,這在使用的時(shí)候可以綜合考慮陈瘦。
threadx的trace log功能僅僅是向內(nèi)存寫入事件切換的時(shí)間戳及相關(guān)對象的4個(gè)附帶信息。那么若僅在切換的時(shí)候?qū)懭氤笔郏蛘哒f是os 切換各種事件的時(shí)候?qū)懭肴睿敲次依斫獠辉趖ask或中斷運(yùn)行時(shí)候?qū)懭刖筒淮嬖诖驍鄦栴},因?yàn)樗写驍喽际莖s切換導(dǎo)致的酥诽,若僅在os切換事件的時(shí)候?qū)懭氚叭涂梢圆挥锰砑娱_關(guān)中斷。
3.循環(huán)隊(duì)列成員為什么都用指針
答:循環(huán)隊(duì)列之前了解的比較多肮帐。設(shè)計(jì)時(shí)候結(jié)構(gòu)體用數(shù)組的比較多咖驮,單鏈表方式也有边器,里面特別的元素成員就是size和溢出flag。這里的設(shè)計(jì)直接用end和start地址托修,所以省略了size忘巧。關(guān)于溢出的判斷也變簡單了,就是start==end
地址說明溢出了睦刃。我理解這是不同的用途導(dǎo)致FIFO設(shè)計(jì)時(shí)候使用的結(jié)構(gòu)體也設(shè)計(jì)的不同砚嘴,因?yàn)榇藅race功能只有FIFO輸入沒有輸出,所有size成員用途不大涩拙。稍微功能有點(diǎn)點(diǎn)區(qū)別际长,就可以考慮更為優(yōu)化的設(shè)計(jì)了。設(shè)計(jì)真是千變?nèi)f化呢兴泥,這就是編碼吸引我的地方工育,可以自由創(chuàng)作,然后尋找最優(yōu)方案搓彻。
三如绸,traceX底層源碼分析
官網(wǎng)先看了help,也把traceX上位機(jī)軟件打開對照字段看了下好唯。還是很容易理解它的設(shè)計(jì)的竭沫。主的一個(gè)結(jié)構(gòu)體對象中,包括了obj和buffer成員骑篙。
為什么要obj和buffer2個(gè)成員呢蜕提?原因就是buffer的后面4個(gè)u32字節(jié)為每個(gè)obj成員特有的,所以trace對象就包括了obj和buffer2個(gè)成員靶端,buffer可以引用obj的數(shù)據(jù)谎势。上位機(jī)用的就是buffer成員。
最簡單的看代碼的方法就是先看數(shù)據(jù)結(jié)構(gòu)杨名,因?yàn)閿?shù)據(jù)解構(gòu)決定算法設(shè)計(jì)脏榆。
/* Define the an Trace Buffer Entry. */
typedef struct TX_TRACE_BUFFER_ENTRY_STRUCT
{
ULONG tx_trace_buffer_entry_thread_pointer;
ULONG tx_trace_buffer_entry_thread_priority;
ULONG tx_trace_buffer_entry_event_id;
ULONG tx_trace_buffer_entry_time_stamp;
#ifdef TX_MISRA_ENABLE
ULONG tx_trace_buffer_entry_info_1;
ULONG tx_trace_buffer_entry_info_2;
ULONG tx_trace_buffer_entry_info_3;
ULONG tx_trace_buffer_entry_info_4;
#else
ULONG tx_trace_buffer_entry_information_field_1;
ULONG tx_trace_buffer_entry_information_field_2;
ULONG tx_trace_buffer_entry_information_field_3;
ULONG tx_trace_buffer_entry_information_field_4;
#endif
} TX_TRACE_BUFFER_ENTRY;
代碼的結(jié)構(gòu)體和上位機(jī)的截圖前8個(gè)完全對應(yīng),所以就是從單片機(jī)獲取的數(shù)據(jù)台谍。對應(yīng)的4個(gè)不同內(nèi)容也有注釋须喂,比如queue對象,就有queue prt,destination ptr,wait option和enqueued和上位機(jī)一致趁蕊。
/* I1 = queue ptr, I2 = destination ptr, I3 = wait option, I4 = enqueued */
#define TX_TRACE_QUEUE_RECEIVE 68
/* I1 = queue ptr, I2 = source ptr, I3 = wait option, I4 = enqueued */
#define TX_TRACE_QUEUE_SEND 69
再看看源碼中TX_TRACE_IN_LINE_INSERT宏函數(shù)坞生,賦值8個(gè)字段,全部可以對應(yīng)上掷伙。
trace_event_ptr -> tx_trace_buffer_entry_thread_pointer = (ULONG) trace_thread_ptr; \
trace_event_ptr -> tx_trace_buffer_entry_thread_priority = (ULONG) trace_priority; \
trace_event_ptr -> tx_trace_buffer_entry_event_id = (ULONG) (i); \
trace_event_ptr -> tx_trace_buffer_entry_time_stamp = (ULONG) TX_TRACE_TIME_SOURCE; \
TX_TRACE_INFO_FIELD_ASSIGNMENT((a),(b),(c),(d)) \
我之前說它是FIFO且覆蓋方式代碼如下是己,完成current的指針賦值8個(gè)字段后,就指向下一個(gè)空閑內(nèi)容任柜,且判斷下一個(gè)空閑內(nèi)容是否為end卒废,若為end就指向start沛厨。
trace_event_ptr++; \
if (trace_event_ptr >= _tx_trace_buffer_end_ptr) \
{ \
trace_event_ptr = _tx_trace_buffer_start_ptr; \
_tx_trace_buffer_current_ptr = trace_event_ptr; \
_tx_trace_header_ptr -> tx_trace_header_buffer_current_pointer = (ULONG) trace_event_ptr; \
if (_tx_trace_full_notify_function) \
(_tx_trace_full_notify_function)((VOID *) _tx_trace_header_ptr); \
} \
else \
{ \
_tx_trace_buffer_current_ptr = trace_event_ptr; \
_tx_trace_header_ptr -> tx_trace_header_buffer_current_pointer = (ULONG) trace_event_ptr; \
} \
這里可圈可點(diǎn)的內(nèi)容如下,就是剛進(jìn)入函數(shù)時(shí)候的if判斷是否使能摔认。而且用的是mask對象的方式逆皮,我之前自己設(shè)計(jì)沒有考慮到此項(xiàng),這樣就可以實(shí)現(xiàn)通過宏定義來配置使能某些想觀察的對象级野。而非控制所有页屠,可以用來調(diào)整的顆粒度為obj對象。這是本次閱讀源碼設(shè)計(jì)最大的收獲蓖柔。
#define TX_TRACE_IN_LINE_INSERT(i,a,b,c,d,e) \
{ \
TX_TRACE_BUFFER_ENTRY *trace_event_ptr; \
ULONG trace_system_state; \
ULONG trace_priority; \
TX_THREAD *trace_thread_ptr; \
trace_event_ptr = _tx_trace_buffer_current_ptr; \
if ((trace_event_ptr) && (_tx_trace_event_enable_bits & ((ULONG) (e)))) \
{ \
...
} \
四辰企,小結(jié)
同樣的模板字段可以輸出不同的含義內(nèi)容,這就是buffer復(fù)用的思想况鸣。這樣就可以使用同一的框架牢贸,這樣的設(shè)計(jì)思想我也可以借鑒。另外镐捧,對不同對象進(jìn)行mask使能的方式也很有用潜索。哈哈,收獲頗豐~