RISC-Vのオープンソースベクトル実装のリファレンスとしてAraというものが公開されている。
AraのSIMD ALUモジュールについて。
simd_alu
モジュール:
名前 | 型 | 方向 | 説明 |
---|---|---|---|
operand_a_i | elen_t | I | 入力オペランド1 |
operand_b_i | elen_t | I | 入力オペランド2 |
valid_i | logic | I | |
vm_i | logic | I | マスクが有効かを示す |
mask_i | strb_t | I | マスク |
narrowing_select_i | logic | I | narrowingが有効かを示す |
op_i | ara_op_e | I | 演算種類 |
vew_i | vew_e | I | データサイズ |
vxsat_o | vxsat_t | O | 固定小数点の演算情報 |
rm | strb_t | I | |
vxrm_i | vxrm_t | I | 固定小数点のまるめモード |
result_o | elen_t | O | 計算結果 |
elen_t
の定義は以下。
ara_pkg.sv
// Ara only supports vector elements up to 64 bits. localparam int unsigned ELEN = 64; // Maximum size of a single vector element, in bytes. localparam int unsigned ELENB = ELEN / 8; typedef logic [ELEN-1:0] elen_t;
まずは入力データを、それぞれのデータタイプに対応できるように変換していく。
simd_alu.sv
typedef union packed { logic [0:0][63:0] w64; logic [1:0][31:0] w32; logic [3:0][15:0] w16; logic [7:0][ 7:0] w8; } alu_operand_t; alu_operand_t opa, opb, res; assign opa = operand_a_i; assign opb = operand_b_i; assign result_o = res;
それでもって、各データ型に応じて演算を適用していく。この辺のデータパスの作り込みについてはどうすればいいのか迷っていたが、とりあえずこのようにひたすら並べていく安直な方法でもいいっぽい。
VADD, VADC, VMADC, VREDSUM, VWREDSUMU, VWREDSUM: unique case (vew_i) EW8: for (int b = 0; b < 8; b++) begin automatic logic [ 8:0] sum = opa.w8 [b] + opb.w8 [b] + logic'(op_i inside {VADC, VMADC} && mask_i[1*b] & ~vm_i); res.w8[b] = (op_i == VMADC) ? {6'b0, 1'b1, sum[8]} : sum[7:0]; end EW16: for (int b = 0; b < 4; b++) begin automatic logic [16:0] sum = opa.w16[b] + opb.w16[b] + logic'(op_i inside {VADC, VMADC} && mask_i[2*b] & ~vm_i); res.w16[b] = (op_i == VMADC) ? {14'b0, 1'b1, sum[16]} : sum[15:0]; end EW32: for (int b = 0; b < 2; b++) begin automatic logic [32:0] sum = opa.w32[b] + opb.w32[b] + logic'(op_i inside {VADC, VMADC} && mask_i[4*b] & ~vm_i); res.w32[b] = (op_i == VMADC) ? {30'b0, 1'b1, sum[32]} : sum[31:0]; end EW64: for (int b = 0; b < 1; b++) begin automatic logic [64:0] sum = opa.w64[b] + opb.w64[b] + logic'(op_i inside {VADC, VMADC} && mask_i[8*b] & ~vm_i); res.w64[b] = (op_i == VMADC) ? {62'b0, 1'b1, sum[64]} : sum[63:0]; end endcase
strb_t
というのは、バイト単位のストローブ信号を示しており、マスクに使用している。
localparam int unsigned DataWidth = $bits(elen_t), localparam int unsigned StrbWidth = DataWidth/8, localparam type strb_t = logic [StrbWidth-1:0]
マスクの使用については、とりあえず演算時にマスクに応じて何かをする必要はない。一部命令については必要なようだけども、それ以外は特に必要ないようだ。
EW8: for (int b = 0; b < 8; b++) begin automatic logic [ 8:0] sum = opa.w8 [b] + opb.w8 [b] + logic'(op_i inside {VADC, VMADC} && mask_i[1*b] & ~vm_i); res.w8[b] = (op_i == VMADC) ? {6'b0, 1'b1, sum[8]} : sum[7:0]; end
マスク自体は外から与えられる。
valu.sv
simd_alu #( .FixPtSupport (FixPtSupport ) ) i_simd_alu ( .operand_a_i (alu_operand_a ), .operand_b_i (alu_operand_b ), .valid_i (valu_valid ), .vm_i (vinsn_issue_q.vm ), .mask_i ((mask_valid_i && !vinsn_issue_q.vm) ? mask_i : {StrbWidth{1'b1}}), .narrowing_select_i(narrowing_select_q ), .op_i (vinsn_issue_q.op ), .vew_i (vinsn_issue_q.vtype.vsew ), .vxsat_o (alu_vxsat ), .vxrm_i (alu_vxrm_i ), .rm (r ), .result_o (valu_result ) );
マスクの情報は、Mask Unitから来ているようだった。これはレーンに関係なく1つモジュールがインスタンスされている。
///////////////// // Mask unit // ///////////////// masku #( .NrLanes(NrLanes), .vaddr_t(vaddr_t) ) i_masku ( .clk_i (clk_i ), .rst_ni (rst_ni ), // Interface with the main sequencer .pe_req_i (pe_req ), .pe_req_valid_i (pe_req_valid ), .pe_vinsn_running_i (pe_vinsn_running ), .pe_req_ready_o (pe_req_ready[NrLanes+OffsetMask]), .pe_resp_o (pe_resp[NrLanes+OffsetMask] ), .result_scalar_o (result_scalar ), .result_scalar_valid_o (result_scalar_valid ), // Interface with the lanes .masku_operand_i (masku_operand ), .masku_operand_valid_i (masku_operand_valid ), .masku_operand_ready_o (masku_operand_ready_masku ), .masku_result_req_o (masku_result_req ), .masku_result_addr_o (masku_result_addr ), .masku_result_id_o (masku_result_id ), .masku_result_wdata_o (masku_result_wdata ), .masku_result_be_o (masku_result_be ), .masku_result_gnt_i (masku_result_gnt ), .masku_result_final_gnt_i(masku_result_final_gnt ), // Interface with the VFUs .mask_o (mask ), .mask_valid_o (mask_valid ), .mask_valid_lane_o (mask_valid_lane ), .lane_mask_ready_i (lane_mask_ready ), .vldu_mask_ready_i (vldu_mask_ready ), .vstu_mask_ready_i (vstu_mask_ready ), .sldu_mask_ready_i (sldu_mask_ready ) );
ここまで来て、とりあえず全体構成を把握する必要があるのではないかという気がしてきたので一端そっちを見てみる。
ara ara_dispatcher i_dispatcher(); ara_sequencer i_sequencer(); for (genvar lane = 0; lane < NrLanes; lane++) begin: gen_lanes lane i_lane(); spill_register i_mask_ready_spill_register(); lane_sequencer i_lane_sequencer(); operand_requestor i_operand_requestor(); vector_regfile i_vrf(); operand_queues_stage i_operand_queues(); vector_fus_stage i_vfus(); valu i_valu(); stream_register i_mask_operand_register(); spill_register i_alu_reduction_spill_register(); simd_alu i_simd_alu(); vmfpu i_vmfpu(); end endgenerate vlsu i_vlsu(); sldu i_sldu(); masku i_masku();