2011年4月15日 星期五

android froyo (rowboat) DSP 簡易故障排除

本文在 2011/04/15 21:25 發表於 Yahoo!奇摩部落格
因Yahoo!奇摩部落格將於2013年12月26日終止服務故遷移至此


歡樂的 demo 都是無盡的痛苦堆起來的XD
玩 linux 等 open source 的真的要非常有耐性


接續前篇 在 devkit8000 上安裝 android froyo (rowboat)
這篇將描述一下如何做故障排除
透過除錯訊息來分析問題的所在
主要是 dsp 部分的除錯
其實這套系統啥都有, 都包好了
也沒有其他週邊出錯的機會
只有 dsp 是最麻煩的

如果你遇上這張板子上的問題, 而我的文章內沒寫的
就...不用問我了XD
我只能記下我遇到的, 且有解的
無解的問題我也沒輒 (攤手)
這種含 dsp 的系統我也是第一次使用



如果要找人問, 可以去 ti 的論壇問
不過記得, 提問要有技巧
一定要貼進階除錯訊息, 否則不會有人甩你
另外, 提問前最好先用這些錯誤訊息去 google 看看
如果有人問過就直接用, 比較快

所以, 首先要知道如何印出進階除錯訊息
這套系統大致分兩種除錯資訊
一是來自 android 系統的
另一是來自 dvsdk 的, 也就是 ti 的 dsp 軟體
android 預設將除錯訊息塞進他們寫的 kernel 裝置中
用 logcat 就可以印出來
它會長得像這樣

...
I/Zygote  (  809): Accepting command socket connections
E/BatteryService(  887): Could not open /sys/class/power_supply
I/sysproc (  887): Entered system_init()
I/sysproc (  887): ServiceManager: 0x11e610
I/SurfaceFlinger(  887): SurfaceFlinger is starting
I/SurfaceFlinger(  887): SurfaceFlinger's main thread ready to run. Initializing graphics H/W...
D/libEGL  (  887): loaded /system/lib/egl/libGLES_android.so
D/libEGL  (  887): loaded /system/lib/egl/libEGL_POWERVR_SGX530_121.so
D/libEGL  (  887): loaded /system/lib/egl/libGLESv1_CM_POWERVR_SGX530_121.so
D/libEGL  (  887): loaded /system/lib/egl/libGLESv2_POWERVR_SGX530_121.so
...

第一個字母是錯誤等級 I = info, E = error, D = debug, V = verbose ...等
第二個單字是錯誤來源
在 android 原始碼中可以找到, 通常在檔案最上方
會有個 #define LOG_TAG "xxx", 這 xxx 就會顯示在這裡
用來識別這錯誤是從哪個檔案的程式碼出來的
後面的則是錯誤訊息
出錯時, 若是 android 內部錯誤這裡幾乎都可以看到
不過有些錯誤僅是 "結果"
例如: android 底層呼叫外部函式庫, 或是驅動硬體時
傳回值並無如同預期, 那就印出錯誤
可是, 這錯誤代碼不是 android 定的, 是外部函式庫定的
那麼除錯就必須往外部函式庫去找
以 dsp 段錯誤來說, 就是要找 dvsdk
dvsdk 非常龐大, 裡面也有許多種類的錯誤
但是預設並不會印出
因為從 uart 去印錯誤會讓系統非常的慢
所以, 若要看 dvsdk 的錯誤, 就需要做些設定, 把訊息引出來

根據 ti 的資料
在執行他們的環境時
如果設定環境變數 CE_DEBUG=1 則會印出 codec engine 段的除錯訊息
1 為精簡的訊息, 2 為進階, 3 為所有訊息
除了 CE_DEBUG 外, 其他套件的也能印出, 但變數就各不相同了
像 DMAI_DEBUG 就是 Davinci Multimedia Application Interface 的訊息
而 android 有自己的 init script
因此設定方法就是寫在 init.rc
把它丟到檔案最上方那堆 export 處
寫這樣:

export CE_DEBUG 1
不要用 linux 上的習慣 CE_DEBUG=1, 那是 shell script 在用的
完成後訊息還不會出來
原因是 init 這隻程式預設把所有的 stdout 都丟到 /dev/null
所以要再加個東西把它引出來
我們知道 dvsdk 會被併在 mediaservice 裡面
因此就在 init.rc 中 mediaservice 啟動腳本下加個東西

service media /system/bin/mediaserver
    user root
    group system audio camera graphics inet net_bt net_bt_admin net_raw sdcard_rw
    ioprio rt 4
    console

要求 init 把它當 console 使用, 丟出所有輸出
接著用 rs232 通訊軟體的截取功能 (以 minicom 來說就是 Ctrl+A -> L)
就可以取得完整的除錯訊息

取得訊息了, 也不要急著丟上網
因為就像 android 訊息
它有可能來自別的地方, 印出的只是結果
同樣的, dvsdk 有好幾層轉接層
如果只看最後一行錯, 那也只是最後的結果
得先確定在中間的哪一層出錯
要知道這, 就要先了解 dvsdk 是在幹麻的
再 codec engine 目錄下有兩份文件一定要看

Codec Engine Algorithm Creator User's Guide
Codec Engine Server Integrator User's Guide

它會告訴你 codec engine 是啥, 在做啥, 以及怎麼做的
大概就像這樣


概略來說就是同時在通用處理器 GPP (以 OMAP3 來說就是那顆 Cortex-A8)
以及信號處理器 DSP (以 OMAP3 來說就是那顆 TMS320C64x+)
兩顆處理器上都創造一個 process
接著透過 shared memory 以及 fifo 讓這兩個 process 同步
並且交互處理資料
適合 GPP 做的, 像是串流切割, 檔頭分析就由 GPP 做
做完後把適合 DSP 做的工作以及需要的資料, 像是 iDCT 等丟給 DSP 做
DSP 做完後會把資料放進 shared memory
GPP 再把它撈出來看是要丟到螢幕還是保存這樣

ti 表示這樣的設計是為了讓系統廠整合時更有效率
做 GPP 段的人不需要知道 DSP 設計細節
DSP 段的開發人員也只要專心搞好 DSP
不用知道 GPP 上資料怎麼走, 要放到哪裡
讓這兩組人員能合作無間
各發展各的專業, 然後快速整合成產品
除此之外, 這 GPP/DSP 工作的分配也不限制
沒有說 iDCT 一定要丟 DSP
codec engine 也允許全部都 DSP 做, 或是全部都 GPP 做
只要做系統的把這段分配調整好
codec engine 的上層應用人員可以不用去管這套系統的硬體細節
就依照 spec 寫的去呼叫指定的介面就可以工作
ti 的技術力相當可怕

接著, 就來看看我遇到的錯誤以及解決方案
首先, 第一個是這個

I/omx-dsp (  804): 0010575592 [INTERFACE.ERROR] Failed to open engine 'codecServer': 3

這是 android 段的錯誤
如同前面所說, 它是最終的結果, 而非過程
主要導致這錯誤的原因有 2 個

1. cs.x64P 位置不正確或是權限不正確
2. cmem 未配置正確

cs.x64P 必須出現在 /system/ti-dsp
且 mediaserver 權限需為 root (在 init.rc 中修改)
不過這是前幾版的錯誤, 現在已經修掉了
所以通常會是第二個
用軟體攔截開機訊息, 確實的檢查 cmem 載入時是否出錯
這裡有一些 CMEM 的資料可以參考

CMEM Overview
http://processors.wiki.ti.com/index.php/CMEM_Overview

若順利載入
在開機過程中印出一堆除錯訊息
應該要有一行是像這樣:

[DSP] @0x000001e5:[T:0x00000000] server - main> Welcome to DSP server's main().

這表示在 dsp 上的 codec server 已經載入, 開始初始化
沒看到這行就繼續往前步驟去找錯, 不用往後看了
這行之後 codec server 會開始抓取它所需要的資源
也就是一堆的記憶體空間
它們會長得像這樣:

...
@48,379,089us: [+0 T:0x0006a0d0 S:0x4112ec3c] OM - Memory_alloc> Enter(0x16e360)
@48,379,119us: [+0 T:0x0006a0d0 S:0x4112ebfc] OM - Memory_contigAlloc> Enter(size=1500000, align=-1, cached=FALSE, heap=FALSE)
@48,379,455us: [+4 T:0x0006a0d0 S:0x4112ebfc] OM - Memory_contigAlloc> CMEM_alloc(1500000) = 0x4112f000.
...
@48,714,813us: [+0 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> Enter(size=393216, align=-1, cached=FALSE, heap=FALSE)
@48,714,965us: [+4 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> CMEM_alloc(393216) = 0x41335000.
@48,715,057us: [+4 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> CMEM_getPhys(0x41335000) = 0x86cfe000.
...
@48,715,789us: [+0 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> Enter(size=393216, align=-1, cached=FALSE, heap=FALSE)
@48,715,942us: [+4 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> CMEM_alloc(393216) = 0x413cc000.
@48,716,033us: [+4 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> CMEM_getPhys(0x413cc000) = 0x86c67000.
...
@49,050,811us: [+0 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> Enter(size=393216, align=-1, cached=FALSE, heap=FALSE)
@49,050,964us: [+4 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> CMEM_alloc(393216) = 0x41463000.
@49,051,055us: [+4 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> CMEM_getPhys(0x41463000) = 0x86bd0000.
...
@49,051,788us: [+0 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> Enter(size=393216, align=-1, cached=FALSE, heap=FALSE)
@49,051,940us: [+4 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> CMEM_alloc(393216) = 0x414fa000.
@49,052,001us: [+4 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> CMEM_getPhys(0x414fa000) = 0x86b39000.
...
@49,386,566us: [+0 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> Enter(size=393216, align=-1, cached=FALSE, heap=FALSE)
@49,386,749us: [+4 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> CMEM_alloc(393216) = 0x41591000.
@49,386,810us: [+4 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> CMEM_getPhys(0x41591000) = 0x86aa2000.
...
@49,387,542us: [+0 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> Enter(size=393216, align=-1, cached=FALSE, heap=FALSE)
@49,387,695us: [+4 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> CMEM_alloc(393216) = 0x41628000.
@49,387,756us: [+4 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> CMEM_getPhys(0x41628000) = 0x86a0b000.
...
@49,722,381us: [+0 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> Enter(size=393216, align=-1, cached=FALSE, heap=FALSE)
@49,722,534us: [+4 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> CMEM_alloc(393216) = 0x416bf000.
@49,722,595us: [+4 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> CMEM_getPhys(0x416bf000) = 0x86974000.
...
@49,723,358us: [+0 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> Enter(size=393216, align=-1, cached=FALSE, heap=FALSE)
@49,723,480us: [+4 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> CMEM_alloc(393216) = 0x41756000.
@49,723,541us: [+4 T:0x0006a0d0 S:0x4112ebd4] OM - Memory_contigAlloc> CMEM_getPhys(0x41756000) = 0x868dc000.
...

僅留下重點, 中間一堆(...)訊息省略
這就是編解碼所需要的緩衝區
都是連續記憶體區段
pool 就是配置給這邊用的
如果有其中一個失敗就全毀了
它不會不開機, 只是無法播放影片而已
看哪個記憶體配失敗了
就到 init.rc 裡補給它就是了
所以前篇才會有 pools=4x500000,8x614500,2x618800,1x1500000
就是 500000 byte pool x4, 614500 byte pool x8 ...

這裡過了以後接著就是影片播放
播放時一樣需要溝通 codec engine
在 GPP, DSP 上各創造一個 process
若無法創造, 有可能是參數不正確或是記憶體不足
通常會是前者
錯誤訊息長得像這樣

[DSP] @2506,350,541tk: [+7 T:0x87efd334 S:0x87f00fec] ti.sdo.ce.alg.Algorithm - Algorithm_create> Algorithm creation FAILED; make sure that 1) alg params are correct/appropriate, 2) there is enough internal and external algorithm memory available -- check DSKT2 sett@1787,161,560us: [+0 T:0x0007ba98 S:0x41dde964] OC - Comm_put> Enter(queue=0x0, msg=0x40119880)

最後會顯示這樣

@1787,499,755us: [+7 T:0x0007ba98 S:0x41ddeb9c] ti.sdo.dmai - [Vdec2] Failed to open video decode algorithm

所謂參數不正確通常是影像格式不支援
因此前篇才會找那軟體
要一模一樣才能播放
我找到那播放器是從官方提供的 sample movie 來看
不過官方僅提供 3gp 和 mpeg-4
並無 h264
所以目前我還不知道 h264 應該要用哪種軟體去壓

這就是全部了, 有想到再補上
有錯請踢, 有其他問題就別問了XD
我只會這些而已, 都梭了, 沒有其他料了XD

沒有留言:

張貼留言

注意:只有此網誌的成員可以留言。