Sunday, March 22, 2020

使用Vivado Xilinx AXI verification IP進行AXI ip開發驗證

Xilinx AXI Verification IP tutorial

前情提要

一般來說開發Xilinx FPGA上的AXI master/slave ip都是透過C/C++ 轉成HLS或是直接撰寫Verilog。透過C/C++ 轉成的HLS可以自己寫簡單的C/C++ 程式作為testbench,驗證結果可以直接看C/C++ testbench的結果而定,Verilog細節、AXI操作等問題Vivado HLS會幫你完全處理(如果這些部份出現問題,你也沒輒了)。但是透過Verilog直接寫成的IP,通常需要CPU還有記憶體界面(eg. mig)之類的進行全系統測試,而獲取波形等資訊需要透過合成ILA在板子上實際測試。這流程十分繁瑣,合成ILA會浪費大量syntesis和implementation時間,而且可以debug的的訊號數量受限於晶片上的block ram以及LUT數量。然而很多人忽略了Xilinx其實提供了AXI verification IP,可以簡單快速撰寫testbench直接進行behavior simulation,這對於縮短演算法邏輯開發的時間有益。

本文主要以 CPU <-> AXI slave <=> AXI master <-> memory此模式為主。

完整文件請看AXI Verification IP v1.0

AXI Verification IP 簡介

Xilinx AXI Verification IP主要提供的功能為:

  •  產生AXI master command與資料(可自訂)
  • 產生AXI slave進行資料讀取與回覆(也可以模擬mig記憶體界面)
  • 檢查AXI protocol

而Xilinx AXI Verification IP (VIP)主要有三種模式:

  • 作為AXI master
    • 用來測試AXI slave ip
  • 作為AXI slave
    • 用來測試AXI master ip
  • 作為AXI pass-through 
    • (檢查兩個AXI IP之間的通訊)


AXI VIP提供SystemVerilog界面,透過SystemVerilog的OOP包裝一些AXI protocol操作與memory model。讓開發者可以專注在測試策略上。

flow

tools -> create and Package New IP -> choose Create AXI Peripheral
其他創造ip都按照基本流程。




這邊可以選取Verify Peripheral IP using AXI4 VIP,會建構完整的測試template。

完成後會產生如下block diagram

並且產生以下範例testbench,註解為程式區段用途。

`timescale 1ns / 1ps
`include "my_dma_v1_0_tb_include.svh"
// 以下為必要package
import axi_vip_pkg::*;
import my_dma_v1_0_bfm_1_slave_0_0_pkg::*;
import my_dma_v1_0_bfm_1_master_0_0_pkg::*;

module my_dma_v1_0_tb();

// AXI slave VIP 參數宣告(以下省略部份程式碼)
xil_axi_uint                            error_cnt = 0;
xil_axi_uint                            comparison_cnt = 0;
axi_transaction                         wr_transaction;   
.......
xil_axi_uint                           mst_agent_verbosity = 0;  
xil_axi_uint                           slv_agent_verbosity = 0;  
// AXI Slave VIP with memory model (如果你的ip為AXI master full會自動產生對應VIP)
my_dma_v1_0_bfm_1_slave_0_0_slv_mem_t          slv_agent_0;

integer result_slave;  
bit [31:0] S00_AXI_test_data[3:0]; 
 localparam LC_AXI_BURST_LENGTH = 8; 
 localparam LC_AXI_DATA_WIDTH = 32; 

// AXI master VIP 參數參數宣告
integer                                 i; 
integer                                 j;  
xil_axi_uint                            trans_cnt_before_switch = 48;  
xil_axi_uint                            passthrough_cmd_switch_cnt = 0;  
......
axi_ready_gen                           arready_gen2;  
xil_axi_payload_byte                    data_mem[xil_axi_ulong];  
//AXI master VIP
my_dma_v1_0_bfm_1_master_0_0_mst_t          mst_agent_0;
// Design under test
  `BD_WRAPPER DUT(
      .ARESETN(reset), 
      .ACLK(clock) 
    ); 
  
initial begin
    //基本AXI VIP agent初始設定
 slv_agent_0 = new("slave vip agent",DUT.`BD_INST_NAME.slave_0.inst.IF);
       slv_agent_0.set_agent_tag("Slave VIP");
    slv_agent_0.set_verbosity(slv_agent_verbosity);
    slv_agent_0.start_slave();
    mst_agent_0 = new("master vip agent",DUT.`BD_INST_NAME.master_0.inst.IF);//ms  
        mst_agent_0.set_agent_tag("Master VIP"); 
    mst_agent_0.set_verbosity(mst_agent_verbosity); 
    mst_agent_0.start_master(); 
     $timeformat (-12, 1, " ps", 1);
  end
  initial begin
    reset <= 1'b0;
    #100ns;
    reset <= 1'b1;
    repeat (5) @(negedge clock); 
  end
  always #5 clock <= ~clock;
  initial begin
      S_AXI_TEST ( );

    init_0 = 0;
    #200ns;
    init_0 =1'b1;
    #20ns;
    init_0 = 1'b0;
    
      #1ns;
      $finish;
  end
  initial begin
  #1;
    forever begin
      slv_agent_0.monitor.item_collected_port.get(slv_monitor_transaction);
      slave_moniter_transaction_queue.push_back(slv_monitor_transaction);
      slave_moniter_transaction_queue_size++;
    end
  end
//測試task範例
task automatic S_AXI_TEST;  
begin   
#1; 
   $display("Sequential write transfers example similar to  AXI BFM WRITE_BURST method starts"); 
   mtestID = 0; 
   mtestADDR = 64'h00000000; 
   mtestBurstLength = 0; 
   mtestDataSize = xil_axi_size_t'(xil_clog2(32/8)); 
   mtestBurstType = XIL_AXI_BURST_TYPE_INCR;  
   mtestLOCK = XIL_AXI_ALOCK_NOLOCK;  
   mtestCacheType = 0;  
   mtestProtectionType = 0;  
   mtestRegion = 0; 
   mtestQOS = 0; 
   result_slave = 1; 
  mtestWDataL[31:0] = 32'h00000001; 
  for(int i = 0; i < 4;i++) begin 
     S00_AXI_test_data[i] <= mtestWDataL[31:0];   
   //AXI master write transaction
   mst_agent_0.AXI4LITE_WRITE_BURST( 
   mtestADDR, 
   mtestProtectionType, 
   mtestWDataL, 
   mtestBresp 
   );   
   mtestWDataL[31:0] = mtestWDataL[31:0] + 1; 
   mtestADDR = mtestADDR + 64'h4; 
  end 
 $display("Sequential write transfers example similar to  AXI BFM WRITE_BURST method completes"); 
 $display("Sequential read transfers example similar to  AXI BFM READ_BURST method starts"); 
 mtestID = 0; 
 mtestADDR = 64'h00000000; 
 mtestBurstLength = 0; 
 mtestDataSize = xil_axi_size_t'(xil_clog2(32/8)); 
 mtestBurstType = XIL_AXI_BURST_TYPE_INCR;  
 mtestLOCK = XIL_AXI_ALOCK_NOLOCK;  
 mtestCacheType = 0;  
 mtestProtectionType = 0;  
 mtestRegion = 0; 
 mtestQOS = 0; 
 for(int i = 0; i < 4;i++) begin 
   //AXI master產生read transaction
   mst_agent_0.AXI4LITE_READ_BURST( 
        mtestADDR, 
        mtestProtectionType, 
        mtestRDataL, 
        mtestRresp 
      ); 

  end 
endtask  

endmodule


  • 對於slave agent有兩種model,分別是:
    • <component_name>_slv_t
      • 必須自行在user environment(testbecnch)中補上write response與read response(response包含回傳訊息的內容等)
    • <component_name>_slv_mem_t
      • write response與read response會由agent自行處理
      • 帶有memory model
      • 對於AXI full master的ip,Vivado會預設使用此agent


Testbench、AXI VIP與DUT的關係



以此架構產生的目標測試環境如上所示。其中由於AXI VIP slave是使用memory mode,所以Slave write/read driver不需要user自己在testbench中補上處理程序。

在使用memory model版本的AXI VIP,可以透過blackdoor_memory_write與blackdoor_memory_read存取模擬出的data memory,而AXI master則可透過一般AXI transaction進行讀寫。memory model大小取決於AXI data width的定址空間大小。

blackdoor memory read範例:
xil_axi_ulong mem_rd_addr = 32'h00000000;
bit [32-1:0] mem_rd_data;
mem_rd_data = slv_agent_0.mem_model.backdoor_memory_read(mem_rd_addr);
blackdoor memory write範例:
bit [32-1:0] wr_data = 0;
xil_axi_ulong wr_addr = 0;
bit [(32/8)-1:0] wr_strb = 4'hf; //byte select
slv_agent_0.mem_model.backdoor_memory_write(wr_addr,wr_data,wr_strb);

memory model使用上可以透過以下code將memory給入預設值方便debug:
slv_agent_0.mem_model.set_memory_fill_policy(XIL_AXI_MEMORY_FILL_FIXED);
slv_agent_0.mem_model.set_default_memory_value(32'hdeadbeef);

AXI master VIP進行AXI4 lite read/write的讀寫方式可透過:
xil_axi_ulong                           mtestADDR;
xil_axi_prot_t                          mtestProtectionType = 3'b000;
bit [63:0]                              mtestWDataL;
bit [63:0]                              mtestRDataL;
xil_axi_resp_t                          mtestBresp;
xil_axi_resp_t[255:0]                   mtestRresp;
mst_agent_0.AXI4LITE_WRITE_BURST(
        mtestADDR,
        mtestProtectionType,
        mtestWDataL,
        mtestBresp);
mst_agent_0.AXI4LITE_READ_BURST(
        mtestADDR,
        mtestProtectionType,
        mtestRDataL,
        mtestRresp);        

示範testbench code

在gist連結中:https://gist.github.com/Daichou/6b2e0a62bfc4eeb338e7f82fe40f0cc8

其他資源

Xilinx AXI VIP很多資訊只能透過從範例code看註解學習,開啟範例的方法為,先建立一個AXI VIP block design,在block diagram中點擊AXI VIP按右鍵然後會跳出選單,選取open IP example design。在該專案根目錄底下的imports就會含有所有範例testbench。

Reference




Friday, March 6, 2020

Verilog Diagram generator

Verilog Diagram generator

最近剛好看到chisel/FIRRTL專案下有一個子專案叫做diagrammer,可以把FIRRTL進行dependency分析後,產生graphiz的dot檔案,包括其中的submodule都可以一併產生,想說看一下verilog開源的專案有沒有類似的工具。後來找到yosys這個synthesis tool可以達到類似的功能。yosys主要針對verilog-2005,systemverilog之類的沒有支援有點可惜。
用法先去clone該專案,進行安裝。
$ git clone https://github.com/YosysHQ/yosys
安裝相依工具:
$ sudo apt-get install build-essential clang bison flex \
 libreadline-dev gawk tcl-dev libffi-dev git \
 graphviz xdot pkg-config python3 libboost-system-dev \
 libboost-python-dev libboost-filesystem-dev zlib1g-dev
進去yosys資料夾後,選擇compiler:
$ make config-clang
$ make config-gcc
然後make && make install
$ make
$ sudo make install
這些步驟都跟readme寫的一樣。
這工具的command和vcs蠻相似的,執行yosys會進到command shell,透過read_verilog載入verilog module,或是read -sv載入verilog module同時elaborate。之後指定top module。她會產生檔案的AST。
yosys> read -sv folder/*.v
yosys> hierarchy -top top_module
接下來可以用show指令顯示diagram。
yosys> show
如果多個module,需要指定顯示程式。(gv是一個程式,可以透過apt-get安裝,ps是postscript的縮寫)
yosys> show -format ps -viewer gv
多個檔案會顯示多組diagram。不過看起來不會寫入合併成一個diagram。他也會在家目錄產生.yosys_show.dotdot檔。可以透過dot程式轉成png或是svg。
其他像xilinx的tool可以產生RTL schematic也可以產生類似功能。而一些商用工具也有這些功能如Synopsys Synplify Pro看起來很完整。
ref

Thursday, July 4, 2019

Linux kernel: Energy Aware scheduling (EAS)

Energy Aware scheduling

之前寫的作業報告,我覺得可以分享出來
hackmd版:https://hackmd.io/@Daichou/ByzK-S60E
主要文章:https://www.linaro.org/blog/energy-aware-scheduling-eas-progress-update/

背景

本文主要以linaro 於2015年提出之EAS(Energy aware scheduling)框架看當今Linux kernel 5.0以後從EAS與Android端整合回kernel的EAS與EM(energy model)。
自從Arm big.LITTLE 從原本的硬體切換叢集大小核,到後來的HMP(heterogeneous multi-processing),傳統Linux的SMP排程方式不敷使用,由於傳統Linux kernel的CFS(Completely Fair Scheduler)是以吞吐量(throughput)為主,對於移動平台的功耗掌控不甚理想。因此Arm與Linaro團隊提出了EAS作為對於HMP下Linux CFS,cpuidle,cpufreq子系統的加強。後來導入EM 框架,作為EAS與driver與其他Linux子系統,如:device tree與Thermal(目前仍未連接)的介面。

Energy Model Framework

Interface

EM(energy model Framework)是Linux kernel特別抽出的介面,用來讓子系統或是driver可以透過em_register_perf_domain()函式註冊一個特別情況下的時脈與功耗的關係(performance domain)。

如上圖可見driver透過em_register_perf_domain註冊效能資料,Kernel透過em_pd_energy()取得功耗估計,而em_cpu_get()用以取得EM中的energy model table的資料。目前僅有Scheduler需要這些資料,不過Linux kernel預期會有更多子系統需要這些資料,因此將特別抽出建立一層抽象化。

OPP(operating performance points)

由於當前SOC盛行,SOC有許多子模塊(例如:CPU cluster),這些子模塊可以依照使用情境給予不同的頻率與電壓組合,並不一定需要整個SOC依照相同頻率運作,這些子模塊通常被分為domain,各domain可以有多組頻率與電壓組合被稱為operating performance points(OPP)。OPP透過device tree source(dts)由開發者建立,通常於開機時初始化。

(資料來源:https://www.linaro.org/assets/blog/EAS-image-11-f4a331f605b5adbd4d8330917f421070c2b0fc0e7d4fd2e22de1cbe2bf8e83c5.jpg)
以上圖為例由於CPU的算力與功率是曲線而非直線,因此透過OPP可以增加EM推算未來功耗的準確度,這項設計可讓EM預測功耗的線性內插運算更為精準。

Performance domain(perf_domain)


整個performance domain結構如上圖。每個cpu run queue會指向一個root_domain(用以表達一個cpu set),每個root_domain會用linked list儲存多個perf_domain,每個performace domain中的CPU必須是相同的microarchitecture(例如均為Cortex-A53)且當調整一個perf_domain的物理參數(如電壓等),整個perf_domain中的硬體均會一起被調整,每個perf_domain對應的em_perf_domain會透過em_register_perf_domain()與對應的callback function建立em_cap_state。以從device tree建立的方式為例,em_register_perf_domain()會透過OPP的_get_cpu_power()依照:
Power = Capacity * Voltage^2 * frequency
公式將OPP轉成mW、frequency以及cost組合存入em_cap_state,其中cost計算方式為:
Cost = max_cpu_frequency * em_cap_state.power/em_cap_state.frequency
perf_domain與root_domain對應到實際上的SOC的關係則是如下圖。通常一個root_domain對應到一個SOC,而perf_domain對應到其中的CPU cluster。如果cluster中的CPU可以特別調頻,則可以在拆分更多perf_domain。

Energy Aware Scheduler

EAS 基本上並非完全提出一個新的scheduler,而是在更改現有scheduler(CFS)在挑選指派cpu的方式。藉此調整HMP的功耗。

CPU capacity

CPU capacity是用以表達當CPU以最高頻率運行時的throughput。透過CPU capacity與scheduler 從Per-Entity Load Tracking(PELT)取得task運作數據了解該CPU的忙碌程度。CPU capacity是開機時在建立CPU topology時解析device tree建立。系統可以透過arch_scale_cpu_capacity()讀取該值。

policy

Energy aware scheduling透過選取CPU的方式,主要透過select_task_rq_fair這個函式將任務發配給cpu runqueue。而整個scheduler有兩個情境會需要執行此函式:
  1. execve -> sched_exec
  2. wake up一個task

task的能耗預測

透過Energy Model提供的em_pd_energy()預測,推算方式如下:
csdomain capacity state

透過頻率與CPU utilization的關係我們可以找到大於當前CPU uilization最小的cs。

由於
cs powerCPU max freqcs frequency=cs cost(em_cap_state.cost)

所以
Predict CPU energy=CPU utilizationcs costCPU max capacity

Linux kernel中使用化約過的(2)式做運算,而我們以下範例使用直觀的(1)式。範例:
當前狀況CPU01為同perf_domain,CPU23為同perf_domain,若P1(平均util=200):

CPU0: 400 / 512 * 300 = 234
CPU1: 100 / 512 * 300 = 58
CPU2: 600 / 768 * 800 = 625
CPU3: 500 / 768 * 800 = 520
共1437
若P1移到CPU1
CPU0: 200 / 341 * 150 = 88
CPU1: 300 / 341 * 150 = 131
CPU2: 600 / 768 * 800 = 625
CPU3: 500 / 768 * 800 = 520
共1364
若P1移到CPU3(無法移到2除非調整OPP)
CPU0: 200 / 341 * 150 = 88
CPU1: 100 / 341 * 150 = 43
CPU2: 600 / 768 * 800 = 625
CPU3: 700 / 768 * 800 = 729
共1485
透過此方式可知將task放到CPU1有最佳功耗。

EAS 時間複雜度

由上的運算可知每次運算(select_task_rq_fair)需要遍歷root_domain下所有的perf_domain,並且需要計算每個CPU與其OPP是落在哪個效能區間,依照kernel文件給出的時間複雜度如下:
此對於task wake up的情況來說,此情況對於時間複雜度很敏感,如果EAS(EM)複雜度過高會導致OS反應不佳,因此EM設立了EM_MAX_COMPLEXITY門檻(預設2048),若Complexity數值高過此數值則不啟用EAS,一般開發人員也可以透過:
  1. 分隔root_domain減少domain中的CPU數量降低複雜度
  2. 減少EAS task wake up的複雜度(需要自己上patch),不過難度很高
來減少task wake-up的延遲。

EAS成立假設

EAS要能夠成功在不影響效能運作,且達成能耗最佳化必須建立在以下三個假設:
  1. 所有CPU都有足夠的idle時間:確保task可以不受效能限制的運行,使PELT計量的task utilization為正確的。
  2. 每個task都被供給足夠的CPU capacity:原因同上。
  3. 每個CPU都有足夠的剩餘capacity,以滿足task wake-up且task的blocking/sleeping愈規律愈好
這些假設必須在CPU低於80% capacity情況下才可以成立,當高於此值稱為over-utilization,此狀況下EAS的會無法得到足夠的選擇,導致效能損失。

Over-utilization

為了確保EAS不要在效能吃緊時影響效能,EAS建立了一個狀態 “over-utilization” 。當CPU使用了超過 80% 的capacity時,SG_OVERUTILIZED這個flag會被設立,這時scheduler會放棄使用EAS的指派CPU機制,而改用原先CFS的指派方式:選擇同一schedule domain下負載最小的CPU(最idle的CPU)或是相鄰schedule domain下覆載最小的CPU。
此機制的檢查是在:
1.透過SCHED_SOFRIRQ發生時,透過update_sg_lb_stats()檢查並更新sched_group的統計數據
2.新的task被放入CFS的紅黑樹時檢查。

load balance

Linux kernel 透過SCHED_SOFTIRQ這個softirq對應的action函式驅動scheduler檢查是否需要進行load balance,然而在EAS啟動時(sched_energy_enabled()回傳值為真)情況下,會直接將當前情況視為沒有負載不均衡(find_busiedt_group()會清除imbalance flag並回傳NULL)。因為EAS很容易造成大小核任務分配不均的情況。然而只要root domain中其中個CPU發生over-utilization的情況,即便EAS啟動,load balance仍會進行。

Dependency

EAS需要以下滿足以下相依性:
  1. 非對稱CPU架構(即SD_ASYM_CPUCAPACITY flag必須開啟),Ex: big.LITTLE架構。
  2. Energy model:用來提供效能參數。
  3. EM複雜度低於門檻。
  4. Schedutil governor:為Linux 4.7以後新增的governor,用以動態根據utilization調節CPU freqency,EAS只能使用這個governor。
  5. Scale-invariant utilization signals:PELT必須能夠給出frequency-invariant and CPU-invariant PELT訊號以給出正確的功耗預測。
綜合5.與6.由於必須要提供frequency-invariant訊號,因此系統的util必須是frequency-invariant。因此在Schedutil governor中使用:

作為調整公式(若為variant,max_freq項需替換成cur_freq)。當CFS的run queue中的utilization改變時會觸發cpufreq_update_util(),由於在EAS情況下會使用SchedUtil governor,會對應到sugov_update_single()或是sugov_update_shared()進行頻率更新。

在linaro文章中提到的是cpufreq governor,其已經被SchedUtil governor正式取代。

效能數據

可以發現負載愈小節能效果愈好,因為task可以指派的選項比較多,可以找到更好的結果。但是EAS仍會造成一些效能損失。
image alt
資料來源:ARM

未來發展

1.EM是否該包含GPU等其他運算單元。
目前移動裝置中仍未出現異質的GPU設計,所以仍未限包含GPU的需求
2.是否有其他子系統需要EM的參數。

其他資料

精選文章

使用Ardunio Atmega2560 連接 nRF24L01+

使用Ardunio Atmega2560 連接 nRF24L01+ 關於library 目前主流有 https://github.com/maniacbug/RF24 與 https://github.com/TMRh20/RF24 這兩個。 其中TMRh20大大做...