寫在前面
最近由於公司需要,所以就做了個基於SWD協議的離線燒寫器。由於過程中參考了很多大神的文章,因此就想寫個隨筆記錄下。整個燒寫器由三個部分組成分別為:
- SWD協議讀寫晶片內部暫存器、RAM、Flash.
- STM32 + SPI + Flash + USB + FAFTS,通過USB虛擬出一個U盤,將要燒寫的BIN檔案放到這個U盤中,就可以燒寫了.
- OLED + 按鍵,實現簡單的操作頁面.
本章先對SWD協議進行解析,整個文章主要分了三個部分,第一部分是對SWD協議進行簡介;第二部分是對SWD協議進行物理層上的解析;
1 SWD協議簡介
SWD的全稱應該是The Serial Wire Debug Port(SW-DP),也就是序列除錯埠,是ARM目前支援的兩種除錯埠之一,另一個除錯埠叫做JTAG Debug Port,也就是我們常用的J-link上面的除錯埠(JTAG模式下)。
基於ARM CoreSight除錯構架,SWD可以通過傳輸資料包來讀寫晶片的暫存器。
2 SWD物理層協議解析
SWD需要三根線與目標的MCU連線,分別為SWDIO、SWDCLK和GND.後面的內容中,HOST為主機,就是我們提供的SCK的一方;TARGET為目標MCU。
- SWDIO為雙向Data線,主機讀寫目標晶片資料。
- SWDCLK為時鐘線,類似於SPI需要由主機提供時鐘。同時,資料都是在時鐘下降沿讀取,上升沿進行資料翻轉。
- GND為雙向Data口,主機讀寫目標晶片資料。
2.1 SWD通訊時序分析
首先放個寫操作成功的時序圖。
從圖中可知,整個流程大致分為三部分:
- 第一部分:HOST -> TARGET 主機傳送讀寫命令,這部分指定了讀寫操作,指定了要訪問AP還是DP,還指定了ADDR。
- 第二部分:TARGET -> HOST 目標MCU回覆Ack,通知主機是否可以繼續操作。
- 第三部分:HOST -> TARGET 主機寫資料
下面,我們針對圖中的一些名詞做出解釋:
- Start: 一位起始位,值為1。
- APnDP: 一位,表示訪問的是DP暫存器還是AP暫存器,0:DP,1: AP。
- RnW: 一位,表明是讀操作還是寫操作,0:寫,1:讀。
- ADDR[2:3]:兩位,給出DP或者AP暫存器地址ADDR[3:2]地址區域,傳送時低位在前(後面有具體說明)。
- Parity: 一位 為前面的資料包提供奇偶校驗。在包頭中的校驗位校驗了APnDP, RnW and A[2:3],在資料中的校驗位校驗了32位的資料。
- Stop: 一位,停止位,值為0。
- Park: 一位,在傳輸該位時,主機不對SWDIO進行驅動,該線由硬體拉高(上拉),所以這位讀作一。
- Trn: 調轉週期,在該週期中,不對線進行前驅動,實際應用時,這個時候要將SWDIO引腳的輸入輸出狀態翻轉。這個週期的時間由TURNROUND控制(位於WCR暫存器中)。
- Ack[0:2]: 三位,目標MCU到主機的響應,低位在前。100:成功,010:等待,001:失敗。
- WDATA[0:31]: 32位的寫資料包,由主機傳送給目標MCU。
- RDATA[0:31]: 32位的讀資料包,由MCU傳送給主機。
值的注意的是,Trn,調轉週期,因為我們是單匯流排通訊,一根線上既有寫又有讀,而這個Trn就是發生在寫讀切換的時候的一個延時。
2.2 SWD 暫存器簡介
SWD通訊的時候主要涉及的暫存器就兩個,一個DP,一個AP。
如圖所示,DP就是Debug port,AP 就是 Access port。如果要訪問核心暫存器,就得先配置DP->AP->MEM-AP。
2.2.1 DP暫存器
DP暫存器如下圖,地址就是我們在包頭中的ADDR[2:3],這兩位指示的地址。具體的暫存器實在是太多了,具體的請參考《ARM Debug Interface v5 Architecture Specification》。
值的說明的是,匯流排復位之後,然後進行JTAG和SWD的切換操作(發0X79,0XE7或者0X6D,0XB7),然後必須先讀下IDDCODE,判斷下MCU的型別,然後才能繼續別的操作。
2.2.2 AP暫存器
AP暫存器如下路,AP暫存器相比較於DP則是複雜了很多。具體的暫存器實在是太多了,具體的請參考《ARM Debug Interface v5 Architecture Specification》。
2.3 SWD通訊流程
2.3.1 SWD復位
如下圖,ARM對SWD的復位有明確的說明,要求連續50個時鐘週期的高電平,認為是SWD復位,同時復位之後必須讀一次IDCODE(位於DP暫存器中)。
下圖是我的復位時序,多寫了5個週期的電平
2.3.2 SWD讀IDCODE
IDCODE位於SWD DP暫存器中,將地址設定為00b,讀DP操作,就可以讀到IDCODE了,根據核心型號不同,一般第一數字不一樣,我的是0x0BB11477.
下圖是我的讀IDCODE時序
2.3.3 SWD清除錯誤標誌位,並且使能AP除錯
通過寫DP中的APBORT[4:1],來清除錯誤標誌位。
通過寫DP中的CTRL/STAT[30]和CTRL/STAT[28]使能AP除錯
2.3.4 SWD讀取AP IDR(也就是AP暫存器的ID CODE)
讀取AP暫存器的步驟:
- 第一步通過寫DP 中的SELECT暫存器,確定APBANK.
- 第二步通過AP命令包頭的地址,確定APBANK內部的位置,傳送讀AP命令。
如圖DP SELECT暫存器。
如圖,AP暫存器,APBANK決定的是BANK 0x0 -> BANK 0xF.ADDR[3:2]決定的是BANK內部的地址(ADDR[1:0]都是0),0x00,0x04,0x08,0x0C,
所以,如果我要讀取AP IDR的話,就先設定DP SELECT暫存器中的APBANKSEL為0xF,然後直接發讀AP命令,ADDR[3:2]為11b.
2.3.5 SWD讀寫MCU任意暫存器
讀取MCU任意暫存器的步驟:
- 第一步通過寫DP 中的SELECT暫存器,確定APBANK為BANK0.
- 第二步通過AP命令包頭的ADDR[3:2]地址,確定APBANK內部的地址為0x00(CSW),傳送寫AP命令。資料為0x23000002(該值可能有問題,此處請自行檢視CSW暫存器描述,如果這個值寫完之後依然讀不出DRW,請聯絡我).
- 第三步通過AP命令包頭的ADDR[3:2]地址,確定APBANK內部的地址為0x04(TAR),傳送寫AP命令。資料為要讀取的暫存器地址。
- 第四步通過AP命令包頭的ADDR[3:2]地址,確定APBANK內部的地址為0x0C(DRW),傳送讀AP命令。一次讀回來的資料無效。
- 第五步通過AP命令包頭的ADDR[3:2]地址,確定APBANK內部的地址為0x0C(DRW),傳送讀AP命令。此時讀回來的資料為正確資料。(這裡讀DP的RDBUFF也可以,詳細請自行查閱暫存器文件)
步驟一波形:
步驟二波形:
步驟三波形:
步驟四波形:
步驟五波形:
寫入MCU任意暫存器的步驟:
- 第一步通過寫DP 中的SELECT暫存器,確定APBANK為BANK0.
- 第二步通過AP命令包頭的ADDR[3:2]地址,確定APBANK內部的地址為0x00(CSW),傳送寫AP命令。資料為0x23000012(該值可能有問題,此處請自行檢視CSW暫存器描述,如果這個值寫完之後依然不能寫入DRW,請聯絡我).
- 第三步通過AP命令包頭的ADDR[3:2]地址,確定APBANK內部的地址為0x04(TAR),傳送寫AP命令。資料為要讀取的暫存器地址。
- 第四步通過AP命令包頭的ADDR[3:2]地址,確定APBANK內部的地址為0x0C(DRW),傳送寫AP命令。寫入成功。
步驟一波形:
步驟二波形:
步驟三波形:
步驟四波形:
特殊說下,設定好CSW[5:4]之後,每次讀寫DRW之後,DRW的地址會自動+1,省去了需要每次重新設定地址的麻煩。
以上,本章的內容就結束了,通過本章的內容,可以實現SWD任意讀取MCU暫存器,如果問題,請聯絡我~~~
參考文獻:ARM Debug Interface