天天看點

VTA硬體

VTA硬體

提供了VTA硬體設計的自上而下的概述。本硬體設計涵蓋兩個級别的VTA硬體:

  • VTA設計及其ISA硬體-軟體接口的體系結構概述。
  • VTA硬體子產品的微體系結構概述以及計算核心的微代碼規範。

VTA概述

VTA是為快速,高效的密集線性代數而建構的通用深度學習加速器。VTA內建了一個簡單的類似RISC的處理器,可以對1或2級張量寄存器執行密集的線性代數運算。另外,該設計采用解耦通路執行以隐藏記憶體通路延遲。

在更廣泛的範圍内,VTA可以用作模闆的深度學習加速器設計,以進行完整的堆棧優化,進而将通用張量計算接口公開給編譯器堆棧。

本文從總體上概述了VTA硬體組織。VTA由四個子產品組成,這些子產品通過FIFO隊列和本地記憶體塊(SRAM)互相通信,以實作任務級管道并行性:

  • 提取子產品負責從DRAM加載指令流。解碼這些指令,路由到三個指令隊列之一。
  • 加載子產品負責将來自DRAM的輸入和權重張量加載到資料專用的片上存儲器中。
  • 計算子產品使用GEMM核執行密集線性代數計算,并使用張量ALU進行正常計算。将資料從DRAM加載到寄存器檔案中,以及将micro-op核心加載到micro-op緩存中。
  • 存儲子產品将計算核心産生的結果存儲回DRAM。

HLS硬體來源組織

VTA設計目前在Vivado HLS C ++中指定,隻有Xilinx工具鍊才支援。VTA硬體來源包含在3rdparty/vta-hw/hardware/xilinx/sources:

  • vta.cc 包含每個VTA子產品的定義以及頂級VTA設計的頂級行為模型。
  • vta.h包含使用Xilinxap_int類型的類型定義以及函數原型聲明。

預處理器宏在下定義3rdparty/vta-hw/include/vta/hw_spec.h。這些宏定義中的大多數是從3rdparty/vta-hw/config/vta_config.json檔案中列出的參數派生的。通過處理json檔案3rdparty/vta-hw/config/vta_config.py來生成一串編譯标志,這些編譯标志定義了預處理器宏。makefile使用該字元串,以便在HLS硬體綜合編譯器和建構VTA運作時的C ++編譯器中設定那些進階參數。

HLS子產品示例

顯示了C ++中定義的VTA子產品之一的定義:

void fetch(

  uint32_t insn_count,

  volatile insn_T *insns,

  hls::stream<insn_T> &load_queue,

  hls::stream<insn_T> &gemm_queue,

  hls::stream<insn_T> &store_queue) {

#pragma HLS INTERFACE s_axilite port = insn_count bundle = CONTROL_BUS

#pragma HLS INTERFACE m_axi port = insns offset = slave bundle = ins_port

#pragma HLS INTERFACE axis port = load_queue

#pragma HLS INTERFACE axis port = gemm_queue

#pragma HLS INTERFACE axis port = store_queue

#pragma HLS INTERFACE s_axilite port = return bundle = CONTROL_BUS

  INSN_DECODE: for (int pc = 0; pc < insn_count; pc++) {

#pragma HLS PIPELINE II = 1

    // Read instruction fields

    insn_T insn = insns[pc];

    // Do some partial decoding

    opcode_T opcode = insn.range(VTA_INSN_MEM_0_1, VTA_INSN_MEM_0_0);

    memop_id_T memory_type = insn.range(VTA_INSN_MEM_5_1, VTA_INSN_MEM_5_0);

    // Push to appropriate instruction queue

    if (opcode == VTA_OPCODE_STORE) {

      store_queue.write(insn);

    } else if (opcode == VTA_OPCODE_LOAD &&

        (memory_type == VTA_MEM_ID_INP || memory_type == VTA_MEM_ID_WGT)) {

      load_queue.write(insn);

    } else {

      gemm_queue.write(insn);

    }

  }

}

關于HLS編碼的一些觀察:

  • · 參數:每個功能的參數清單,與接口實用程式結合,定義了所生成的硬體子產品公開的硬體接口。
    • 通過值傳遞的參數表示主機可以寫入的隻讀硬體記憶體映射寄存器。例如,該提取功能具有一個insn_count參數,該參數将被合成為主機要寫入的存儲器映射寄存器,以便設定給定VTA指令序列的長度。
    • 指針參數可能意味着兩件事之一,具體取決于所使用的接口編譯訓示。
      • 與m_axi接口編譯訓示一起使用時,将生成AXI主接口以提供對DRAM的DMA通路。
      • 與bram接口編譯指令一起使用時,将生成BRAM接口,以将讀取和/或寫入端口公開給FPGA Block-RAM。
      • 通過引用傳遞的HLS流與axis接口編譯訓示相結合,産生了到子產品的FIFO接口。硬體FIFO在子產品之間提供了一種有用的同步機制。
      • · 編譯訓示:編譯器編譯對于定義每個子產品的硬體實作是必不可少的。列出了VTA設計中用于将實作要求傳達給編譯器的幾種編譯訓示。
        • HLS INTERFACE:指定綜合硬體子產品的接口。
        • HLS PIPELINE:通過設定啟動間隔目标來定義硬體管道性能目标。當目标被設定,它告訴編譯器合成的硬體管線應該能夠每個周期執行一個II == 1循環疊代。
        • HLS DEPENDENCE:訓示編譯器在給定循環中忽略某些類型的依賴項檢查。考慮一個寫入和讀取相同BRAM結構的循環體,并且需要實作II為1。HLS編譯器必須假定最壞的情況,即對先前寫入更新了該周期之前位址的位址進行讀取:在給定的BRAM時序特性下,這是無法實作的(至少需要2個周期才能看到更新的值)。為了獲得II等于1,必須放松依賴檢查。啟用此優化功能後,它會落在軟體堆棧上,以防止寫入之後再讀取相同的位址。

筆記

該參考為Xilinx 2018.2工具鍊提供了更深入,更完整的HLS規範。

架構概述

指令集架構

VTA的指令集體系結構(ISA)由4條具有可變執行等待時間的CISC指令組成,其中兩條執行微碼指令序列以執行計算。

VTA說明如下:

  • LOAD指令:将DRAM中的2D張量加載到輸入緩沖區,權重緩沖區或寄存器檔案中。将微核心加載到微操作緩存中。在加載輸入和權重圖塊時支援動态填充。
  • GEMM 指令:在輸入張量和權重張量上執行矩陣矩陣乘法的微操作序列,并将結果添加到寄存器檔案張量中。
  • ALU 指令:對寄存器檔案張量資料執行矩陣矩陣ALU操作的微操作序列。
  • STORE 指令:将2D張量從輸出緩沖區存儲到DRAM。

該LOAD指令由取決于存儲存儲器緩沖器位置目标的負荷和計算子產品執行。在GEMM和ALU指令由計算子產品的核心GEMM和張量ALU執行。最後,STORE指令僅由存儲子產品執行。下圖描述了每個指令的字段。

VTA ISA會随着VTA的體系結構參數(即GEMM核心形狀,資料類型,記憶體大小等)的修改而改變,是以ISA不能保證所有VTA變體之間的相容性。這是可以接受的,因為VTA運作時會适應參數更改,并生成針對要生成的加速器版本量身定制的二進制代碼。這展現了VTA堆棧采用的協同設計理念,該理念包含了軟硬體接口的流動性。

資料流執行

VTA依靠硬體子產品之間的依賴性FIFO隊列來同步并發任務的執行。給定的硬體子產品如何通過使用依賴性FIFO隊列和單讀取器/單寫入器SRAM緩沖區,以資料流方式從其生産者子產品和使用者子產品同時執行。每個子產品通過寫後讀(RAW)和讀後寫(WAR)依賴關系隊列連接配接到其使用者和生産者。

上面的僞代碼描述了子產品如何執行給定指令,該指令基于對其他指令的依賴性而确定。首先,每個指令内的依賴标志都在硬體中解碼。如果指令具有傳入的RAW依賴關系,則根據從生産者子產品接收到RAW依賴關系令牌來确定執行。如果任務具有傳入的WAR依賴關系,則根據從使用者子產品接收到的WAR依賴關系令牌來确定執行。當任務完成時,檢查傳出的RAW和WAR依賴性,分别通知使用者和生産者子產品。

在這種情況下,依賴标記是無資訊的。這是因為每個子產品執行的指令不能按設計重新排序,因為以FIFO順序到達。

管道擴充性

預設的VTA設計由描述一個三階段load-compute-store任務管道的四個子產品組成。遵循資料流硬體組織原則,可以将VTA擴充到更多階段。例如,可以設想将張量ALU與GEMM核心分開,以最大程度地利用GEMM核心。這将導緻load-gemm-activate-store任務流水線緊密地反映TPU設計。增加更多的階段會産生成本:增加存儲空間和額外的邏輯開銷,這就是選擇預設的3階段管道的原因。

微架構概述

描述組成VTA設計的子產品。子產品定義包含在中3rdparty/vta-hw/hardware/xilinx/sources/vta.cc。

提取子產品

VTA由線性指令流程式設計。提取子產品是VTA到CPU的入口,通過三個記憶體映射寄存器進行程式設計:

  • 讀寫control寄存器啟動讀取子產品,并對其進行讀取以檢查其是否完成。
  • 隻寫insn_count寄存器設定要執行的指令數。
  • 隻寫insns寄存器設定DRAM中指令流的起始位址。

CPU在VTA運作時準備的實體連續緩沖區中的DRAM中準備指令流。當指令流準備就緒時,CPU将起始實體位址寫入insns寄存器,将指令流的長度寫入insn_count寄存器,并在寄存器中聲明起始信号control。此過程将啟動VTA,VTA将通過DMA從DRAM讀取指令流。

通路指令流後,擷取子產品會部分解碼指令,将這些指令推入指令隊列,這些指令隊列會饋入裝入,計算和存儲子產品:

  • STORE 指令被推送到存儲指令隊列以由存儲子產品處理。
  • GEMM和ALU指令被推到由計算子產品被處理的計算指令隊列。
  • LOAD 描述微操作核心的加載操作或寄存器檔案資料的指令被推到計算指令隊列中,由計算子產品處理。
  • LOAD 描述輸入或重量資料的加載操作的指令被推到加載指令隊列中,以由加載子產品進行處理。

當指令隊列之一變滿時,訪存子產品将暫停,直到隊列未滿為止。指令隊列的大小應足夠深以允許寬闊的執行視窗,并允許多個任務同時在load-compute-store管道中進行。

計算子產品

VTA的計算子產品充當RISC處理器,該處理器在張量寄存器而非标量寄存器上執行計算。兩個功能單元使寄存器檔案發生變化:張量ALU和GEMM核心。

計算子產品從微操作緩存執行RISC微操作。有兩種類型的計算微操作:ALU和GEMM操作。為了最大程度地減少微操作核心的占用空間,同時避免了對諸如條件跳轉之類的控制流指令的需求,計算子產品在兩級嵌套循環内執行微操作序列,嵌套循環通過以下操作計算每個張量寄存器的位置:仿射功能。這種壓縮方法有助于減少微核心指令的占用空間,适用于矩陣乘法和2D卷積,這在神經網絡運算符中很常見。

所述GEMM芯求值GEMM指令,通過在上述圖中所描述的2級嵌套循環執行的微碼序列。GEMM核心每個周期可以執行一個輸入權重矩陣乘法。單周期矩陣乘法的維數定義了硬體張量内在函數TVM編譯器必須将其降低到較低的計算排程表上。張量固有值由輸入張量,權重和累加器張量的尺寸定義。每種資料類型都可以具有不同的整數精度:權重和輸入類型通常都是低精度的(8位或更少),而累加器張量具有更寬的類型以防止溢出(32位)。為了使GEMM核心保持繁忙,每個輸入緩沖區,權重緩沖區和寄存器檔案都必須公開足夠的讀/寫帶寬。

張量ALU支援一組标準操作來實作共同活化,歸一化,池運算符。VTA是子產品化設計,可以擴充Tensor ALU支援的算子範圍,以提高算子覆寫範圍,但要以提高資源使用率為代價。Tensor ALU可以對立即數執行張量-張量運算以及張量-标量運算。張量ALU的操作碼和立即數由進階CISC指令指定。張量ALU計算上下文中的微代碼僅負責指定資料通路模式。

就計算吞吐量而言,Tensor ALU并非以每個周期一次操作的速度執行。限制來自缺乏讀取端口:由于每個周期可以讀取一個寄存器檔案張量,張量ALU的啟動間隔至少為2(即,每2個周期最多執行1次操作)。一次執行單個張量-張量操作可能會很昂貴,特别是考慮到寄存器檔案類型很寬(通常為32位整數)。為了平衡Tensor ALU與GEMM核心的資源利用,在多個周期内通過矢量向量操作執行張量-張量操作。

人工智能晶片與自動駕駛

繼續閱讀