顯示具有 embedded 標籤的文章。 顯示所有文章
顯示具有 embedded 標籤的文章。 顯示所有文章

2021年2月28日 星期日

Raspberry Pi Zero W Project Part 3 - Software Interrupt & Handler example

For ARM architecture, the instruction for software interrupt is SWI or SVC (ARMv7).

Using the instruction is very simple:

SVC #imm

When an ARM processor executes the instruction, it will switch to SVC/SWI mode and jump to the SWI or SVC hander in exception vector table specified by Vector Base Address Register (VBAR).

Similar to previous parts, please get from repo https://github.com/champyen/rpiz_bare_metal.git 

please checkout the commit: 637086c2

$ git checkout 637086c2

It is not difficult to use SWI/SVC instruction. But it requires experience to apply it well. Using SWI/SVC instrution for system calls, we need to:

  • design system call table ( SWI number , system call pair)
  • implement SWI/SVC handler, get the system call number
  • implement system calls with corresponding number

For get the SWI (or system call) number, it is describe in ARM's "SWI Handler's" document. The SWI number is encoded as lowest 24 bit in SWI/SVC instruction. Therefore we could get SWI/SVC number by fetching the instruction itself and clearing the MSb 8bits with BIC instruction right after entering SWI hander:

ldr     r0, [lr,#-4]
bic     r0, r0, #0xff000000

After getting SWI number, we could pass it to system_handler as an argument saved in R0. The system_hander is very similar to ISR, but we don't need to check the hardware status to know which hardware interrupts CPU. We just need to handler the SWI or system call by the SWI number. Of course, as IRQ has Interrupt latency SWI/SVC instruction consumes more cycles than normal instructions for most CPUs.

For Application processor it is easy to understand the meaning of mode switching and SWI handling. In fact, Cortex-R, and Cortex-M processor also has SWI/SVC instruction. For most Embedded / RTOS developers, they think it is useless or trivial in such processor.

For some embedded or RTOS, mutual-exclusive applications can be loaded on demand. This can be done by well-designed linker-script (e.g.: different LAs with same). For such Embedded / RTOS, SWI / SVC instruction is useful to maintain an API layer between Applications and Kernel. An intuitive / naive way is to setup and maintain table of function pointers on the kernel side (Of course we still need to setup API index for using the function as SWI number). For development it has an drawback: it is hard to maintain or adjust or expand the table. With SWI/SVC instruction, no table forwarding is needed. Besides it provides flexibility to group system calls and reserve numbers for future needs. (Since for table-based method, it required to reserve a huge table).


2021年2月6日 星期六

Raspberry Pi Zero W Project Part 2 - Interrupt Service Routine (ISR) example

After implementing naive function and printf, let's add ISR to it.

please checkout the commit: 91abc0d3

$ git checkout 91abc0d3

There are many changes from previous commit:

head.S

it becomes more complicated. As we know, its context starts at 0x8000 address.

start:
    ldr     pc, reset_target        /* 0x00 mode: svc */
    ldr     pc, undefined_target    /* 0x04 mode: ? */
    ldr     pc, swi_target          /* 0x08 mode: svc */
    ldr     pc, prefetch_target     /* 0x0c mode: abort */
    ldr     pc, abort_target        /* 0x10 mode: abort */
    ldr     pc, unused_target       /* 0x14 unused */
    ldr     pc, irq_target          /* 0x18 mode: irq */
    ldr     pc, fiq_target          /* 0x1c mode: fiq */

reset_target:           .word   reset_entry

undefined_target:       .word   undefined_entry
swi_target:             .word   syscall_entry
prefetch_target:        .word   prefetch_entry
abort_target:           .word   abort_entry
unused_target:          .word   unused_entry
irq_target:             .word   irq_entry
fiq_target:             .word   fiq_entry

After loading the binary, it will jump to a routine named "reset_entry". Before we trace the reset_entry. The 8 "ldr pc, XXXXXX" instructions are so called Exception Vector Table. (FIQ is a special irq mode, it has a advantage - the implementation can be start at the location, the jump is not necessary. Therefore one jump delay is saved. ) It is used to handle system exceptions. Each has corresponding privileged mode to it. Besides, each mode has dedicated LR and SP registers - this means OS / firmware implementation should take care of stack space arrangement for the mode:


In fact, for reset_entry here, its major work is setting stack for each mode:

reset_entry:
    /* set VBAR to 0x8000 */
    mov r0, #0x8000
    mcr p15, 0, r0, c12, c0, 0


    /* (PSR_FIQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS) */
    mov r0,#0xD1
    msr cpsr_c,r0
    ldr sp, stack_fiq_top

... other 4 modes ...

    /* (PSR_SVC_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS) */
    mov r0,#0xD3
    msr cpsr_c,r0
    ldr sp, stack_svc_top

    cpsie i
    bl  bare_metal_start

In addition to stack assignment and jump to bare_metal_start , there are two key points here:

  1. setup Vector Base Address Register (VBAR) - From ARMv6, the exception vector can be placed other than 0x00000000 and 0xFF000000. This is achieved by setting VBAR, please refer to "3.2.43 c12, Secure or Non-secure Vector Base Address Register" in ARM1176JZF-S TRM.
  2. enable interrupt - cpsie instruction

And we have to trace isr_entry:

irq_entry:
    stmfd  sp!, {r0-r12, lr}
    add     lr, pc, #4
    bl      isr_entry
    ldmfd   sp!, {r0-r12, lr}

    subs    pc, lr, #4
For ISR, it is not surprised to backup and restore all (non-dedicated) registers. The most interesting things are - LR register setting and the instruction to leave IRQ mode. For LR setting it is easy to figure out, the target return address is the 'bl isr_entry' not 'add lr, pc, #4". That's the main reason to save "pc+4" to LR. And for leaving each mode, please refer to "2.12.2 Exception entry and exit summary" of ARM1176JZF-S TRM.

isr.c

There 3 functions in the source file: timer_enable, timer_check and isr_enty. Here we use "System Timer" in BCM2835, please refer to Chap 7 and Chap 12 of "BCM2835 Peripheral specification". Besides the IRQ number of System Timer is not listed in the document, please refer to the link of  "errata and some additional information" on the page.

The timer_enable will enable System Timer 1 or 3 by index and timer_check is used to clear IRQ state and update next timeout interrupt. Therefore the isr_enty just check status and call timer_check for clear the IRQ.

bare_metal.c

For demonstrate IRQ and main thread's progress, a busy loop with counter is added. The loop will print out a number when specified condition is met. And Timer is enabled before the loop, You can see the timer tick with ISR and the main thread keeps counting.

void bare_metal_start(void)
{
    int base = 0;
    asm volatile (
        "mov %0, sp\n\t" : "=r" (base)
    );

    printf("\n\n%s:%x: Hello World! %s %s %d\n\n", __func__, base, __DATE__, __TIME__, __LINE__);
    printf("enter busy loop\n");

    timer_enable(1);

    volatile int i = 0;
    while(1){
        if((i++ & 0x00FFFFFF) == 0)
            printf("%d\n", i);
    }

}

 



 

 

 

 

2017年9月4日 星期一

Kirin 970 NPU - 看圖說故事

從 Image Sensors World 看到了華為旗艦手機晶片 Kirin 970 中 NPU 的投影片
其中有趣的是第五張圖


"25X performance, 50X efficiency"
相較於 CPU 有著 25倍性能 與 50倍的效率
由於 Performance 有相當多比較的面向, 因此不清楚是用絕對的指標(total GFLOPs, or per core) 或是相對指標 (OPs/cycle/core), 甚至是實際應用的時間來做比對(eg. FPS)
但是若是 Efficiency 談的比較可能的是 Performance Per Watt (另外還有 area efficiency, 甚至一起綜合看)
Efficiency 比較上為了有較好的數字, 比較可能的是與大核等級的 CPU 相比, 因此這裡以 Kirin 970 中的 Cortex-A73 為對比
以 NEON 的 GFLOPS 來看2.4Ghz Cortex-A73 單核的 GFLOPs 為:
2.4Ghz * 4 lane * 2 mad * 2 unit = 38.4 GFLOPS 
(寫到這就知道 Performance 不是以絕對 GFLOPS 計算)
而若 TSMC 10nm 製程能將 2.4Ghz Cortex-A7x 最大功耗(Peak Power)壓低至 1 Watt/Core(這是個人經驗粗略推估數字), 那麼 50X 效率可以推得:
38.4 x 50 = 1920 GFLOPs/Watt =  1.92 TFlops/Watt
(寫到這覺得蠻剛好的)
又可以從投影片上得知華為對外宣稱其 Kirin 970 內的 NPU 有著高達 1.92 TFLOPS/s (FP16) computation throughput




那麼可以由上面的比較, 對於 NPU 的最大功耗能推估為 1 Watt
以此看推估觀察運作的功耗圖介於 0.3~0.7 Watt 看來也是合理的(畢竟應該有著 Thermal/Power 管理面向的 DVFS)


2017年1月25日 星期三

解析 Qualcomm Hexagon 680 架構 III - Thread Model and Execution Model

Thread Model

最後讓我們講述 Hexagon 680 的 Thread Model
在 Qualcomm 開發 DSP 的過程中一直都有 Hardware Threads 的支援
因此在談 Hexagon 680 的 Thread Model 前, 讓我們先來看看 V5x 的 HW Threads

上圖為 Qualcomm 在 2013 年 Hotchips 會議上投影片的 Page 11
從這圖可以看到 Hexagon 680 的 V6 基本上與 V5 架構差異不大
也是 4-slot VLIW, 兩個是 Data Unit, 兩個是 eXecution Unit
同個 DSP 核心具有3個 Thread Context, 在硬體設計上已考慮簡化多工上的需求

在 2015 年度 Hotchips 中 Qualcomm 簡報的 Page 8:
由此可見, V6 Scalar DSP 以 2Ghz 頻率運作
俱備 4 HW Threads 支援, 每個 thread 以 500Mhz 運作
而 HVX coprocessor 以 1Ghz 頻率運作, 具有兩個 context, 一樣每個 thread 以 500 Mhz 運作
因此同一時間可以有兩個 Thread 控制著 HVX, 另外兩個處理 Scalar 工作
但光以上看投影片, 不知道是否有人懷疑過, 或許 V6 DSP 為 quad core, 而 HVX 為 dual core?

這裡我們回到 HVX 官方文件當中:
從這張圖可以看出, V6 DSP 具有的是 1C4T, 而 HVX 為 1C2T
但是, 官方在投影片與到此中所說明的, 並非 Hexagon 680 所有的模式
如果還有印象, 在系列文II當中留了一些不確定:
1. HVX 對於 L2 的總寬度為 512b x2, 而非 1024b x 1
2. 對於 VPF/VRF 的描述, 我加了 "基本上" 這個有所保留的詞

再談這之前, 先來介紹 HVX 的 Vector Modes:

也就是 HVX 中支援了兩種不同長度的 Vector
  • 64B 模式, Vector 寬度為 512 bits
  • 128B 模式, Vector 寬度為 1024 BITS
而這樣的模式並不是像 NEON 中分為長短 vector 並且可以混用, 而是如上圖, 直接將所有的單元(VX, VPF, VRF), 以 512b 寬度的方式等分為二

所以除了 1C2T(128B) 的方式外, 事實上 HVX 還存在另一種 HW thread 的型態 - 1C4T (64B)
在這個模式下, 4 個 V6 HW Threads 各自都可以控制一個 64B Vector Mode 的 VX Thread Context. 所以 Hexagon 680 提供多元且彈性的運作模式. 而這也說明了 系列文II 當中沒交代清楚的部分.

Execution Mode


Qualcomm 曾經官方撰文介紹過 Hexagon DSP 的軟體開發
在 Qualcomm 裝置上, 在 DSP 上執行工作需要幾個步驟:
  1. 下載與安裝 Qualcomm Hexagon SDK
  2. 將你於 DSP 上實作的功能 API 以其 IDL (Interface Description Language) 方式描述.
  3. SDK 會自動產生 Header files 與 stub (下圖中應用處理器端, 也就是 ARM 處理器端提供給 client 的呼叫介面, client 通常是其他的 library 或是 executable)及 skel (DSP 端去呼叫你實作功能的進入點) 等相關函式庫.
  4. 在 DSP 端實作你的 API, 並與第3點中自動產生的 skel 函式庫 link 成為一個 shared object 並放置於該平台裝置中.
  5. 將你的 native APP 與 link 到第3點自動產生的 stub 來呼叫你的 API.
  6. (選擇性) 建立你的 API 專屬的 Java bindings 好讓 APK 能夠使用.
而這些背後所仰賴的機制的核心即是上圖所描述的 FastRPC

一旦 Native APP 呼叫所需的 function, 便會執行自動產生的 Stub lib, 它會 透過 ARM 與 DSP 間橋接的 Driver-Framework, 載入先前所產生的 DSP 端的 shared object, 而 DSP 端上的 OS 會動態地載入該 object, 並且以 Skel 介面處理來自 ARM 處理器端的請求.

2017年1月22日 星期日

解析 Qualcomm Hexagon 680 架構 II - Hexagon Vector eXtension (HVX)

在講解 680 中的 SIMD 單元 - HVX 之前, 還是先以系列文 I 的 blocks diagram開頭, 並且今日重點會是文中提到第3點的官方文件

從 blocks diagram 中可以看到 HVX 由三個主要部分所組成
  • VX : Vector Extension Unit, 也就是 HVX 的 SIMD 指令執行單元
  • VPF : Vector Predicate register File, 這部分是用來處理 SIMD 的中較不易處理的 branch, if-else 等 divergence. 以 8bit 運算來說, 若需要提供 SIMD 中每個 lane 所需的 true/false boolean 資訊, 需要 1024/8 = 128 bit, 而 Predication Register 寬度, 基本上就是 128b.
  • VRF : Vector Register File, 這即是 VX unit 中用來存放的暫存器, HVX 為 1024b vector, 因此基本上這部分為 1024b
而對外的介面有兩個:
  • Co-Processor Instruction Port: 用來與 V6x 處理器溝通的, V6x DSP 是透過這個介面將指令送給 HVX 單元執行
  • 兩組 512b-wide L2-cache/L2-TCM port: 相較 V6x 處理器對於 L2 的頻寬為 256b port, HVX 對於總寬度為 512b x2 , 這樣的設計很重要, 這讓 HVX 能夠更快的取得所需要的 vector 資料, 由於 HVX 為寬度 1024b vector 的 SIMD Unit, 因此這樣的寬度是有必要的. 至於為何是兩組 512b 而非一組 1024b, 這部分與其 thread model 有關, 在下一篇會介紹這個部分.

首先來談 VPF/VRF 的 Register File 部分, 在 HVX 中分別有著 32組 vector register 以 V0 ~ V31 表示, 而 predicate register 共有 4組, 以 Q0 ~ Q3 表示, 這些表示法用在 SDK toolchain 所產生的 .s assembly 中, 對於 V0 ~ V31, 並沒有區分資料型別, 因此為了表示使用的精確度, 在指令中對於 register 使用的方式必須有標記所
從語法中可以看出兩點, 首先是並不支援 float 運算(當然以這樣的notation 要新增也不是難事就是了), 再者是 SIMD 的 operation 與 data type 是脫勾的, 重點是搭配的 register data type, 以上面的例子來說
V2.h = VADD(V3.h, V4.h)
這個 asm 指令是指套用的是 add 指令, 並且資料型別是 h 所代表的 signed halfword (也就是通常 C 中的 short), 因此這行所代表的意義為: 將 V3 與 V4 的資料以 signed 16b 的方式相加後存入 V2. 這樣的 asm 設計也直接影響到了 HVX 在 C intrinsics 上的 coding style, 而這樣的設計其實有好有壞. 好處顯而易見的是 asm 很簡單, 而且能有效的降低寶貴的 register 浪費, 但這樣的設計當 export 到 C 的層次的時候, 就缺乏了對於各資料型別的 vector type, 因此沒有對應型別的 operator overloading, 但這點若採用 C++ 去自己實作也並不難就是了.

接著進入 HVX 的指令類別, HVX 有著六類的指令類型, 在系列文 I 當中也討論過 V6x 各個 slot 的支援, 而 HVX 也是使用相同 VLIW4 的設計, 針對這六類支援的指令, 於官方文件中有著下列表格:
從表格中可以出六類分別為:
  • Aligned Memory Load
  • Aligned Memory Store
  • Unaligned Memory Load/Store
  • Multiplies and special ALU
  • Vextract
  • Simple ALU, Permute, Shift
對於表格的呈現方式, 各位或許會覺得很奇怪, 為何不是像 V6x 處理器中那樣, 是以 slot 為主要分類, 而是以 instruction type 為主要分類. 這主因在於對於同一 cycle 中4個指令的使用上搭配要考慮的不是 slot 本身而已, 因為對於 HVX 來說支援每個 instruction type 所動用到內部的 "resource" 不同. 那什麼是 resource  呢?

在官方文件中有說明, HVX 本身由六種 resource 所組成, 這些 resource 讓 SIMD 指令得以運作
因此弄懂每個指令背後所動用到的 resource 也是重要的:
等等!!! 不是6類嗎, 怎麼變成11類了? 其實細看一下, 你會發現它只是將一些指令細分, 特別是第4類與第6類再去細分, 但是基本上還是那六類. 像是 Multiply 區分了 8b 與 16b 的部分, 這裡請注意, 8b 與 16b 這兩者分別使用的 Multiply Resource 數目不同, 所以這裡讓我們以官方在 Hotchips 中的 slides 對於效能作進一步解釋的說明:
上面這張 Qualcomm 官方投影片很常出現在一些淺談 Hexagon 680 與 HVX 的文章中, 但請注意其中的第2大點的 8b 與 16b 的乘法數目, 相信很多人很難理解(或者根本沒弄懂)為何 8b 的乘法數目為 16b 的 4倍, 因為 1024/8 = 128, 而 1024/16 = 64, 單以 SIMD 的寬度是無法解釋這樣的落差, 但這時若將指令背後 resource 的消耗數目納入考量就會很清楚了, 因為 HVX 處理器中提供了兩組 Multiply Resource, 而 16b 的乘法指令按照上面提供的表格一次即會消耗完兩組,  而 8b 的乘法指令只會消耗一組, 因此同一個 cycle 中, 至多只能塞入一個 16b SIMD 乘法 也就是 1024/16 = 64 個, 而 8b 可以一次執行兩個 8b SIMD 乘法, 也就是 (1024/8)*2 = 256 個.

所以若不考慮 data dependency 因素, 一個指令能否再塞進一個 VLIW 指令中以同一cycle執行, 這背後必須要同時考量 slot 是否 conflict, 以及 resource 是否有餘裕, 這兩者同時都滿足, 才能夠塞進同一個. 對於這樣的考量, 官方文件提供了一份整理好的總表
在這份總表中, 查找所需滿足的條件, 即反映了是否需要額外的 cycle 去執行所想加入的指令. 另外需要注意一件事, 請注意 resource 使用的平衡性, 像是 shift unit 只有一個, 千萬不要不明就裡, 將乘法全以 shift 替換就以為是優化, 在這樣的情況下, 有可能增加的 cycle 數目是以倍數來計, 對於這樣的處理器, 優化的策略在於儘量在乘法與Shift所需要的運算數目中取得平衡. 其他比較常考慮的就是 load/store unit.

對於 HVX 指令中是否有讓我覺得印象深刻的? 我個人認為是 vdelta/vrdelta 以及 permute 所屬指令, 這個類別與一般 DSP 常提供的 PACK/UNPACK 太不同了, 它提供了一個 general permute network 來做兩個 vector register data 的次序調移.
其實當下我覺得這東西一時間我只想到 sorting network, 而且還搞不清楚狀況, 直到看到了一個圖解, 發現這 1 cycle 運算真是潛力無窮, 所有 1-1 mapping 的方式的次序改變, 只要能算得出 Vv, 就可以快速地作 Vu 至 Vd 的轉換. 而對於像我腦子這麼簡單的人, 它也提供了入門版的 shuffle 與 pack 指令.
最後來談 HVX 的資料取得方式來源 - L2-cache/L2-TCM, 對於一般 DSP 在資料傳輸與核心運算的平行部分, 儘管 HVX 文件沒有特別強調 DMA, 但實際上 HVX 提供了對應功能的 L2FETCH, 以一個多媒體為訴求的 DSP  來說, 其也支援了 1D/2D 資料的傳輸.

而到此並沒有特別的地方. 值得一提的是, 這裡的 Level-2 是只對於 V6x 而言, 但對於 HVX 的角度來說, 它就是如同 L1 的存在, 最特別地方在於 V6x 架構中 L2 可以扮演兩個角色, 在 block diagram 中已經標明了
  • L2 data cache
  • L2-TCM
這設計是很有趣的, 因為這兩種記憶體各自有著不同且互補的特性:
  • cache本身有著 transparent 特性, 對於 programmer 本身並不需要特別改變, 只需要注意沒有不利於 cache 管理機制的存取方式,  cache 能夠帶來效能的 scalability (隨著 cache 的加大而效能改善), 功能上的 portability (無論 cache 大小, 功能本身的正確性不會影響), 這樣的特性對於一些高度 locality 的程式很有幫助, 也能降低程式的實作複雜度, 但是 cache 本身有著難以精確控制的特性, 對於一些資料的存取並不能有時間上的保證.
  • Tightly-Coupled Memory (TCM), TCM 有著指定的位置, 距離 processor 很近, 有著存取時間上的保證, 但是由於使用上 Programmer 必須意識到它的存在, 並且自己管理, 所以 TCM 若容量增大也在不修改程式的情況下無法直接增加效能; TCM 若容量變小在不修改程式的情況下程式可能無法正常運作. 但是它卻能有時間上存取的保證, 因此對於一些像是 Lookup-Table (LUT) 的需求, TCM 顯得相當重要
在 V6x 中能將 L2-cache 做分割, 雖然相關機制未知, 但是運作上能將部分 L2 作為 Cache 而部分作為 TCM 是標注在 V6x 的 features list 中:
下篇文, 沒意外的話也是系列文最後一篇, 要談論的是 Hexagon 680 的 Thread Model 以及 Execution Model

2017年1月21日 星期六

解析 Qualcomm Hexagon 680 架構 I - V6x 架構

Qualcomm S835 於不久前發佈
其中內建了強大的 Hexagon 682 DSP
雖然在 S835 的發佈宣傳文中僅說明了新增了對於 TensorFlow 與 Halide Language 的支援
但是還是有發佈專文說明 Hexagon 682 DSP 即便與 Adreno 540 GPU 相較下
在 TensorFlow 與 Machine Learing 方面的效能與功耗效率的其表現出色
儘管 DSP 處理器這字眼對於程式工作人員並不陌生
但除了特定領域面向的工作者外, 有機會接觸者並不多
這與應用平台的可見度與普及性
以及各家 DSP 相關的軟硬體在架構與軟體資訊較為封閉有關

Qualcomm 自 S820 加入的 Hexagon 680 這顆運算性能強大的 DSP
(事實上先前即有 V5 的 DSP,  但是 HVX 讓 V6 增色不少)
相關文件與 SDK 皆有公開釋出, 所以是個很好的說明範例
而 Hexagon 682 應該與 680 相去不遠
因此這裡以系列短文介紹探討這顆 DSP 的架構

探討的內容為 Qualcomm 官網內的一份簡報與兩分文件 (需註冊帳號, 免費)
1. Architecture of the Hexagon 680 DSP for Mobile Image and Computer Vision
2. Hexagon V60/V61 Programmer's Reference Manual
3. Hexagon V60 HVX Programmer's Reference Manual

Hexagon 68x 為 Hexagon V6x 架構的處理器
而 HVX 為 Hexagon Vector Extension 的縮寫, 以 Coprocessor 的方式與 V6x DSP 整合運作
下圖取自官方文件 3. 的系統 blocks diagram
首先讓我們先以上圖來討論 Hexagon V6x DSP
Hexagon DSP 主要由 5 個部分所組成, 其中各個 block 意義如下:
  • D$ - Data Cache
  • RF - Register File
  • I$ - Instruction Cache
  • XU - eXecution Unit
  • MMU - Memory Management Unit
另外有3個對外的控制介面, 分別是:
  • AXI, AHB 對外的 Bus 與 Host 端系統整合
  • 256b-wide L2-cache port
  • Co-Processor Instruction Port 控制 Hexagon VX Co-processor
其中值得注意的地方是 AXI/AHB 以及 L2-cache port 中, V6x 內部中以 L2 Control 與 cache state/tag table 的 function block 連接著, 整個 Hexagon DSP 的資料, 都仰賴著 V6x processor 統一對外透過 AXI/AHB 而取得, 若了存入 L2-cache, 相關資訊則會以 L2 state/tag 紀錄, V6x processor 本身即能夠完全掌握系統中 L1 與 L2 一致性控制的管理. 而 HVX co-processor 即是在 V6x processor 控制流程與 data control 的方式下, 以兩個 512b-wide L2-cache port 存取資料來運作. 即便 HVX 所需資料發生 cache miss, 負責處理這問題的是 V6x proccessor 本身.

首先是 Cache Size, MMU 的相關資訊:
這樣的設計方式與其他 DSP 不同的地方在於它的 instruction 處理部分, 預設是採用 I-Cache 的方式, 以架構來說,  缺乏 I-TCM 的方式對於一些 hard realtime 的應用稍有不利的地方, 但是其支援了 hardware instruction prefetch 的能力彌補來這部分. 而 MMU 部分提供了 4GB 的定址空間, 作為 DSP 的 Virtual Address 與 Physical Address 間著轉換. 由於對於 ARMv8 系統都提供了 > 4GB 定址空間, 因此對於 V6x DSP 使用的記憶體區間應有軟體上或硬體上的限制.

接著來探討 V6x 的剩下的 XU 與 RF, 讓我們用官方的 architecture 圖來說明
透過官方架構圖, 可以看到需要探究的東西增加了, Control Registers 基本上是控制流程(Loop Regs, Predicate Regs)與提供流程相關(Status Regs, Program Counter)資訊的用途, 這裡就捨去不談, 這裡主要談 XU 衍生的 Sequencer + S0 ~ S3 以及 RF 所代表的 General Registers R0 ~ R31

對於 S0 ~ S3 來說, S 代表的是 Slot 的意思, 看到這個字眼稍有敏銳的人就知道接著要說的是 Very Large Instruction Word (VLIW), V6x processor 本身即為 VLIW 架構, 許多的 DSP 都採用這樣的方式, 其優點是:
  • 簡化硬體設計: 對於 DSP 來說若採用 SuperScalar 架構會增加面積在 hardware instruction scheduler 的實作上, 除了增加成本外也增加 power/thermal 的風險
  • 可精確分析與預期效能: 由於 VLIW 的指令排程是透過 compiler 來完成, 而硬體上來說的處理相對簡單, 因此執行上的時間, 若以 single loop iteration 而言 compiler 即能提供資訊, runtime 所需時間, 透過 simulator/emulator 就能得到很精確的時間估測
對於 VLIW 而言, 弄清楚每個 slot 能夠處理的指令是一個重點, 以下為 V6x 的 Slot-Instruction 配置說明:
可以看出, 除了共通的 ALU32 指令外, 每個 Slot  都有著其所被賦予的任務:
  • Slot 0, 1: 主要負責記憶體的 load & store, 其中 Slot 0 更負責了 cache 維護, maintenance 與匯流排的操作.
  • Slot 2, 3: 負責主要的負責數理運算, 以及 Vector 相關指令, 程式內部的流程控制 (J/JR/CR)
到這邊可能會有人想喊 "等等!!! Vector 運算 !?"
是的, V6x 本身即支援了初步的 64b-wide Vector 運算, 下圖為官方的指令範例:
但 V6x 有一點是特別的, 就是它的 Register File
主要是它將 General Register 與 Vector Register 整合在一起:
在操作上亦提供了便利的 32/64b Register Notation
對於 V6x 就以此簡短的介紹, 有興趣者可以再透過官方文件深入
下一篇會介紹讓 Hexagon V6x 大幅提升計算能力的 HVX

在 ARM 平台上使用 Function Multi-Versioning (FMV) - 以使用 Android NDK 為例

Function Multi-Versioning (FMV) 過往的 CPU 發展歷程中, x86 平台由於因應各種應用需求的提出, 而陸陸續續加入了不同的指令集, 此外也可能因為針對市場做等級區隔, 支援的數量與種類也不等. 在 Linux 平台上這些 CPU 資訊可以透過...