NEON 被設計為一種附加的加載/存儲體系結構,以從 C、C++ 等語言提供良好的矢量化編譯器支持。豐富的 NEON 指令集在寬 64 位和 128 位向量寄存器上運行,支持高水平的並行。NEON 指令簡單易懂,也使得手工編碼對於需要最高性能的應用程序來說更加容易。
NEON 技術的一個關鍵優勢是,指令構成了正常的 ARM 或 Thumb 代碼的一部分,使得編程比外部硬件加速器更簡單。可以使用 NEON 指令讀寫外部內存,在 NEON 寄存器和其他 ARM 寄存器之間移動數據,並執行 SIMD 操作。
NEON 架構使用 32×64 位寄存器文件。這些實際上是浮點單元(VFPv3)使用的相同寄存器。浮點寄存器被重新用作 NEON 寄存器並不重要。所有已編譯的代碼和子例程都將符合 EABI,它指定哪些寄存器可以被破壞,哪些寄存器必須被保留。編譯器可以自由地使用代碼中的任何位置的 NEON 或 VFPv3 寄存器來實現浮點值或 NEON 數據。
NEON 體系結構允許 64 位或 128 位並行。這個選擇是為了使 NEON 單元的大小易於管理(矢量ALU很容易變得相當大),同時仍然可以從向量化中獲得良好的性能收益。NEON 體系結構也沒有指定指令計時,可能需要不同數量的循環才能在不同的處理器上執行相同的指令。
與在VFP的通用性
ARM 架構可以支持各種不同的 NEON 和 VFP 選項,但實際上您只能看到組合:
- No NEON or VFP.
- VFP only.
- NEON and VFP.
這些是體系結構的供應商實現選項,因此對於基於 ARM 的設計的特定實現是固定的。
NEON 和 VFP 的關鍵區別在於,NEON 只對向量工作,不支持雙精度浮點數(雙精度由 VFP 支持),不支持平方根、除法等復雜的運算。NEON 擁有 32 個 64 位的注冊庫。如果 NEON 和 VFPv3 都實現了,這個注冊庫就會在硬件中在它們之間共享。這意味着 VFPv3 必須以其具有 32 個雙精度浮點寄存器的 VFPv3-D32 形式存在。這使得對上下文切換的支持更加簡單。保存和恢復 VFP 上下文的代碼還保存和恢復 NEON 上下文。
數據類型
NEON 指令對以下類型的數據類型進行操作:
- 32-bit single precision floating-point
- 8, 16, 32 and 64-bit unsigned and signed integers
- 8 and 16-bit polynomials
NEON 指令中的數據類型說明符包括表示數據類型的字母和表示寬度的數字。它們從指令助記符中分離出來,例如,VMLAL.S8。所以你有以下幾種可能:
- Unsigned integer U8 U16 U32 U64.
- Signed integer S8 S16 S32 S64.
- Integer of unspecified type I8 I16 I32 I64.
- Floating-point number F16 F32.
- Polynomial over {0,1} P8.
---------Note---------- 不支持F16用於數據處理操作。只支持將其轉換為或從其轉換為某種格式。
多項式算法在實現某些加密或數據完整性算法時非常有用。
在{0,1}上加兩個多項式就相當於一個位排除法OR。多項式附加結果與傳統加法不同。
在{0,1}上乘兩個多項式,首先要像常規乘法那樣確定部分乘積,然后將部分乘積作 OR,而不是按常規相加。多項式乘法的結果與傳統乘法不同,因為它需要對部分乘積進行多項式相加。
NEON 技術兼容 IEEE 754-1985,但只支持圓到最近的舍入模式。這是大多數高級語言(如 C 和 Java)使用的舍入模式。此外,NEON 指令總是把 denormal 看成零。
NEON 寄存器
寄存器 bank 可以被看作是 16 個 128 位寄存器(Q0-Q15)或 32 個 64 位寄存器(D0-D31)。每個 Q0-Q15 寄存器映射到一對 D 寄存器,如 Figure 7-3 所示。
Figure 7-4 中寄存器的視圖由使用的指令的形式決定。這樣軟件就不必顯式地改變狀態。
單個元素也可以作為標量訪問。這種雙重觀點的優點是,它適用於擴大或縮小結果的數學運算。例如,將兩個 D 寄存器相乘得到一個 Q 寄存器結果。雙視圖使寄存器 bank 得到更有效的使用。
NEON 數據處理指令通常包含 Normal、Long、Wide、Narrow 和 Saturating variants.
- Normal 指令可以對任何向量類型進行操作,並生成與操作數向量大小相同、類型通常相同的結果向量。
- Long 指令作用於雙字向量操作數並產生一個四字向量結果。結果元素的寬度通常是操作數的兩倍,並且類型相同。長指令是使用附加到指令上的 L 來指定的。Figure 7-5 顯示了這一點,在操作之前提升了輸入操作數。
- Wide 指令操作一個雙字向量操作數和一個四字向量操作數,產生一個四字向量結果。結果元素和第一個操作數的寬度是第二個操作數元素寬度的兩倍。寬指令有一個 W 附加到指令。Figure 7-6 顯示了這一點,輸入雙字操作數在操作之前被提升。
- Narrow 指令作用於四字向量操作數,並產生雙字向量結果。結果元素通常是操作數元素寬度的一半。窄指令是使用附加到指令的 N 來指定的。Figure 7-7 顯示了這一點,輸入操作數在操作之前被降級。
一些 NEON 指令與向量一起作用於標量。標量可以是 8、16、32 或 64 位。使用標量的指令可以訪問寄存器組中的任何元素,盡管對於多重指令存在差異。該指令使用雙字向量的索引來指定標量值。乘法指令只支持 16 位或 32 位標量,並且只能訪問寄存器組中的前 32 位標量(即,16 位標量的 D0-D7 或 32 位標量的 D0-D15)。
NEON 指令集
NEON 指令的所有助記符(與 VFP 一樣)都以字母“V”開頭。指令通常能夠操作不同的數據類型,這是在指令編碼中指定的。大小用指令的后綴表示。元素的數量由指定的寄存器大小表示。
例如:看下面這條指令: VADD.I8 D0, D1, D2 其中
- VADD 表明是一條 NEON ADD 指令
- I8 后綴表明是 8 位整數被加
- D0、D1、D2 表明使用的是 64 位寄存器(D1、D2 是操作數,D0 是目的地址)
所以這個指令並行執行 8 個加法。
有些操作對於輸入和輸出具有不同大小的寄存器。
VMULL.S16 Q2, D8, D9
這條指令執行 D8 和 D9 中打包的 4 個 16 位的數據相乘,並在 Q2 中生成 4 個 32位的結果。
VCVT 指令在單精度浮點數和 32 位整數、定點和(如果實現的話)半精度浮點數之間轉換元素。