Vengineerの戯言

人生は短いけど、長いです。人生を楽しみましょう!

RISC-VなGPGPUであるVortexを深堀する (その2)

はじめに

RISC-VなGPGPUであるVortexを深堀する (その2)

今回は、Vortex の内部構成 (マイクロアーキテクチャ)を見ていきます。

マイクロアーキテクチャ

マイクロアーキテクチャについては、Vortex の doc の下にある microarchitecture.md に書かれています。

下図はその中にあるものです。説明のために引用します。

図の上が、Vorex Core のマイクロアーキテクチャです。図の下は 右から複数のVortex CoreをまとめたCluster、ClusterをまとめたProcessor、Processortを入れたFPGAとなり、このFPGAがホストとPCIeで接続されています。

Clusterの中には、L2 Cache を、Processorの中には、L3 Cache を搭載しています。Clusterの中に4個の Vortex を入れて、4つの Cluster を Processor を入れれば、16コアな Vortex を搭載できます。

FPGAには、Vortex Processorの他に

  • DMA Controller
  • Device Controller
  • CSRs Controller
  • FPGA Interface Unit : Local DRAM
  • PCIe IP

があります。FPGAは、Intel の Stratix 10 に実装しているようです。

RTL

RTL は、

github.com

にあります。

top : Vortex (Vortex_axi.sv)

Vortex の top RTL は、AXI I/F を持っているものがあります。AXI I/F なので、Intelだけでなく、XilinxFPGAにも実装できそうです。また、ASICへの実装も容易だと思います。

Vortex Processor の RTL では、NUM_CLUSTERS マクロで定義されている下図の cluster (VX_cluser) がインスタンスされています。

    for (genvar i = 0; i < `NUM_CLUSTERS; i++) begin

        `RESET_RELAY (cluster_reset);

        VX_cluster #(
            .CLUSTER_ID(i)
        ) cluster (
            `SCOPE_BIND_Vortex_cluster(i)

            .clk            (clk),
            .reset          (cluster_reset),

            .mem_req_valid  (per_cluster_mem_req_valid [i]),
            .mem_req_rw     (per_cluster_mem_req_rw    [i]),
            .mem_req_byteen (per_cluster_mem_req_byteen[i]),
            .mem_req_addr   (per_cluster_mem_req_addr  [i]),
            .mem_req_data   (per_cluster_mem_req_data  [i]),
            .mem_req_tag    (per_cluster_mem_req_tag   [i]),
            .mem_req_ready  (per_cluster_mem_req_ready [i]),

            .mem_rsp_valid  (per_cluster_mem_rsp_valid [i]),
            .mem_rsp_data   (per_cluster_mem_rsp_data  [i]),
            .mem_rsp_tag    (per_cluster_mem_rsp_tag   [i]),
            .mem_rsp_ready  (per_cluster_mem_rsp_ready [i]),

            .busy           (per_cluster_busy           [i])
        );
    end

L3 Cache は、L3_ENABLE マクロが 0 以外の時に インスタンスされます。L3 Cache は、VX_cache で、L2 Cache と同じものを使っています。

    if (`L3_ENABLE) begin
    `ifdef PERF_ENABLE
        VX_perf_cache_if perf_l3cache_if();
    `endif

        `RESET_RELAY (l3_reset);

        VX_cache #(
            .CACHE_ID           (`L3_CACHE_ID),
            .CACHE_SIZE         (`L3_CACHE_SIZE),
            .CACHE_LINE_SIZE    (`L3_CACHE_LINE_SIZE),
            .NUM_BANKS          (`L3_NUM_BANKS),
            .NUM_PORTS          (`L3_NUM_PORTS),
            .WORD_SIZE          (`L3_WORD_SIZE),
            .NUM_REQS           (`L3_NUM_REQS),
            .CREQ_SIZE          (`L3_CREQ_SIZE),
            .CRSQ_SIZE          (`L3_CRSQ_SIZE),
            .MSHR_SIZE          (`L3_MSHR_SIZE),
            .MRSQ_SIZE          (`L3_MRSQ_SIZE),
            .MREQ_SIZE          (`L3_MREQ_SIZE),
            .WRITE_ENABLE       (1),
            .CORE_TAG_WIDTH     (`L2_MEM_TAG_WIDTH),
            .CORE_TAG_ID_BITS   (0),
            .MEM_TAG_WIDTH      (`L3_MEM_TAG_WIDTH),
            .NC_ENABLE          (1)
        ) l3cache (
            `SCOPE_BIND_Vortex_l3cache
 
            .clk                (clk),
            .reset              (l3_reset),

        `ifdef PERF_ENABLE
            .perf_cache_if      (perf_l3cache_if),
        `endif

            // Core request    
            .core_req_valid     (per_cluster_mem_req_valid),
            .core_req_rw        (per_cluster_mem_req_rw),
            .core_req_byteen    (per_cluster_mem_req_byteen),
            .core_req_addr      (per_cluster_mem_req_addr),
            .core_req_data      (per_cluster_mem_req_data),
            .core_req_tag       (per_cluster_mem_req_tag),
            .core_req_ready     (per_cluster_mem_req_ready),

            // Core response
            .core_rsp_valid     (per_cluster_mem_rsp_valid),
            .core_rsp_data      (per_cluster_mem_rsp_data),
            .core_rsp_tag       (per_cluster_mem_rsp_tag),              
            .core_rsp_ready     (per_cluster_mem_rsp_ready),
            `UNUSED_PIN (core_rsp_tmask),

            // Memory request
            .mem_req_valid      (mem_req_valid),
            .mem_req_rw         (mem_req_rw),
            .mem_req_byteen     (mem_req_byteen),
            .mem_req_addr       (mem_req_addr),
            .mem_req_data       (mem_req_data),
            .mem_req_tag        (mem_req_tag),
            .mem_req_ready      (mem_req_ready),

            // Memory response
            .mem_rsp_valid      (mem_rsp_valid),            
            .mem_rsp_data       (mem_rsp_data),
            .mem_rsp_tag        (mem_rsp_tag),
            .mem_rsp_ready      (mem_rsp_ready)
        );

L3 Cache が無いときは、各 Cluster からのアクセスの arbiter (VX_mem_arb) が入っています。

    end else begin

        `RESET_RELAY (mem_arb_reset);

        VX_mem_arb #(
            .NUM_REQS     (`NUM_CLUSTERS),
            .DATA_WIDTH   (`L3_MEM_DATA_WIDTH),            
            .ADDR_WIDTH   (`L3_MEM_ADDR_WIDTH),
            .TAG_IN_WIDTH (`L2_MEM_TAG_WIDTH),
            .TYPE         ("R"),
            .BUFFERED_REQ (1),
            .BUFFERED_RSP (1)
        ) mem_arb (
            .clk            (clk),
            .reset          (mem_arb_reset),

            // Core request
            .req_valid_in   (per_cluster_mem_req_valid),
            .req_rw_in      (per_cluster_mem_req_rw),
            .req_byteen_in  (per_cluster_mem_req_byteen),
            .req_addr_in    (per_cluster_mem_req_addr),
            .req_data_in    (per_cluster_mem_req_data),  
            .req_tag_in     (per_cluster_mem_req_tag),  
            .req_ready_in   (per_cluster_mem_req_ready),

            // Memory request
            .req_valid_out  (mem_req_valid),
            .req_rw_out     (mem_req_rw),        
            .req_byteen_out (mem_req_byteen),        
            .req_addr_out   (mem_req_addr),
            .req_data_out   (mem_req_data),
            .req_tag_out    (mem_req_tag),
            .req_ready_out  (mem_req_ready),

            // Core response
            .rsp_valid_out  (per_cluster_mem_rsp_valid),
            .rsp_data_out   (per_cluster_mem_rsp_data),
            .rsp_tag_out    (per_cluster_mem_rsp_tag),
            .rsp_ready_out  (per_cluster_mem_rsp_ready),
            
            // Memory response
            .rsp_valid_in   (mem_rsp_valid),
            .rsp_tag_in     (mem_rsp_tag),
            .rsp_data_in    (mem_rsp_data),
            .rsp_ready_in   (mem_rsp_ready)
        );

    end

Cluster数がある程度ある場合は、L3 Cache が無いとメモリアクセスが増えそうですね。

Cluster : VX_cluster.sv

NUM_CORES マクロで、VX_core を インスタンスしています。

    for (genvar i = 0; i < `NUM_CORES; i++) begin

        `RESET_RELAY (core_reset);

        VX_core #(
            .CORE_ID(i + (CLUSTER_ID * `NUM_CORES))
        ) core (
            `SCOPE_BIND_VX_cluster_core(i)

            .clk            (clk),
            .reset          (core_reset),

            .mem_req_valid  (per_core_mem_req_valid[i]),
            .mem_req_rw     (per_core_mem_req_rw   [i]),                
            .mem_req_byteen (per_core_mem_req_byteen[i]),                
            .mem_req_addr   (per_core_mem_req_addr [i]),
            .mem_req_data   (per_core_mem_req_data [i]),
            .mem_req_tag    (per_core_mem_req_tag  [i]),
            .mem_req_ready  (per_core_mem_req_ready[i]),
                     
            .mem_rsp_valid  (per_core_mem_rsp_valid[i]),                
            .mem_rsp_data   (per_core_mem_rsp_data [i]),
            .mem_rsp_tag    (per_core_mem_rsp_tag  [i]),
            .mem_rsp_ready  (per_core_mem_rsp_ready[i]),

            .busy           (per_core_busy         [i])
        );
    end

L2_CACHE マクロが 0 以外の時は、L2 Cache をインスタンスされています。

    if (`L2_ENABLE) begin
    `ifdef PERF_ENABLE
        VX_perf_cache_if perf_l2cache_if();
    `endif

        `RESET_RELAY (l2_reset);

        VX_cache #(
            .CACHE_ID           (`L2_CACHE_ID),
            .CACHE_SIZE         (`L2_CACHE_SIZE),
            .CACHE_LINE_SIZE    (`L2_CACHE_LINE_SIZE),
            .NUM_BANKS          (`L2_NUM_BANKS),
            .NUM_PORTS          (`L2_NUM_PORTS),
            .WORD_SIZE          (`L2_WORD_SIZE),
            .NUM_REQS           (`L2_NUM_REQS),
            .CREQ_SIZE          (`L2_CREQ_SIZE),
            .CRSQ_SIZE          (`L2_CRSQ_SIZE),
            .MSHR_SIZE          (`L2_MSHR_SIZE),
            .MRSQ_SIZE          (`L2_MRSQ_SIZE),
            .MREQ_SIZE          (`L2_MREQ_SIZE),
            .WRITE_ENABLE       (1),          
            .CORE_TAG_WIDTH     (`L1_MEM_TAG_WIDTH),
            .CORE_TAG_ID_BITS   (0),
            .MEM_TAG_WIDTH      (`L2_MEM_TAG_WIDTH),
            .NC_ENABLE          (1)
        ) l2cache (
            `SCOPE_BIND_VX_cluster_l2cache
              
            .clk                (clk),
            .reset              (l2_reset),

        `ifdef PERF_ENABLE
            .perf_cache_if      (perf_l2cache_if),
        `endif

            // Core request
            .core_req_valid     (per_core_mem_req_valid),
            .core_req_rw        (per_core_mem_req_rw),
            .core_req_byteen    (per_core_mem_req_byteen),
            .core_req_addr      (per_core_mem_req_addr),
            .core_req_data      (per_core_mem_req_data),  
            .core_req_tag       (per_core_mem_req_tag),  
            .core_req_ready     (per_core_mem_req_ready),

            // Core response
            .core_rsp_valid     (per_core_mem_rsp_valid),
            .core_rsp_data      (per_core_mem_rsp_data),
            .core_rsp_tag       (per_core_mem_rsp_tag),
            .core_rsp_ready     (per_core_mem_rsp_ready),
            `UNUSED_PIN (core_rsp_tmask),

            // Memory request
            .mem_req_valid      (mem_req_valid),
            .mem_req_rw         (mem_req_rw),        
            .mem_req_byteen     (mem_req_byteen),
            .mem_req_addr       (mem_req_addr),
            .mem_req_data       (mem_req_data),
            .mem_req_tag        (mem_req_tag),
            .mem_req_ready      (mem_req_ready),
            
            // Memory response
            .mem_rsp_valid      (mem_rsp_valid),
            .mem_rsp_tag        (mem_rsp_tag),
            .mem_rsp_data       (mem_rsp_data),
            .mem_rsp_ready      (mem_rsp_ready)
        );

L2_CACHE が 0 の時は、L3 Cache の時とように、VX_mem_arbインスタンスされています。

    end else begin

        `RESET_RELAY (mem_arb_reset);

        VX_mem_arb #(
            .NUM_REQS     (`NUM_CORES),
            .DATA_WIDTH   (`DCACHE_MEM_DATA_WIDTH),
            .ADDR_WIDTH   (`DCACHE_MEM_ADDR_WIDTH),           
            .TAG_IN_WIDTH (`L1_MEM_TAG_WIDTH),            
            .TYPE         ("R"),
            .TAG_SEL_IDX  (1), // Skip 0 for NC flag
            .BUFFERED_REQ (1),
            .BUFFERED_RSP (1)
        ) mem_arb (
            .clk            (clk),
            .reset          (mem_arb_reset),

            // Core request
            .req_valid_in   (per_core_mem_req_valid),
            .req_rw_in      (per_core_mem_req_rw),
            .req_byteen_in  (per_core_mem_req_byteen),
            .req_addr_in    (per_core_mem_req_addr),
            .req_data_in    (per_core_mem_req_data),  
            .req_tag_in     (per_core_mem_req_tag),  
            .req_ready_in   (per_core_mem_req_ready),

            // Memory request
            .req_valid_out  (mem_req_valid),
            .req_rw_out     (mem_req_rw),        
            .req_byteen_out (mem_req_byteen),        
            .req_addr_out   (mem_req_addr),
            .req_data_out   (mem_req_data),
            .req_tag_out    (mem_req_tag),
            .req_ready_out  (mem_req_ready),

            // Core response
            .rsp_valid_out  (per_core_mem_rsp_valid),
            .rsp_data_out   (per_core_mem_rsp_data),
            .rsp_tag_out    (per_core_mem_rsp_tag),
            .rsp_ready_out  (per_core_mem_rsp_ready),
            
            // Memory response
            .rsp_valid_in   (mem_rsp_valid),
            .rsp_tag_in     (mem_rsp_tag),
            .rsp_data_in    (mem_rsp_data),
            .rsp_ready_in   (mem_rsp_ready)
        );

おわりに

次回は、RISC-VなGPGPUである Vortex を深堀する (その3)として、VX_core をみていきます。