自作CPUにおいて、ある程度ベクトル移動命令が動作するようになってきた。
ベクトル命令のオペランドは、ベクトル・レジスタ、整数レジスタ、浮動小数点レジスタ、即値が選択できる。 これをどのように制御するかについて考えた。
scariv_vec_alu_datapath u_vec_alu_datapath ( .i_op (r_ex1_pipe_ctrl.op ), .i_sew (r_ex1_issue.vlvtype.vtype.vsew ), .i_vs1 (r_ex1_vpr_rs_data[0][d_idx*64 +: 64] ), .i_rs1_valid (r_ex1_issue.rd_regs[0].typ != scariv_pkg::VPR ), .i_rs1 (r_ex1_rs1_data ), .i_vs2 (r_ex1_vpr_rs_data[1][d_idx*64 +: 64] ), .i_wr_old (r_ex1_vpr_wr_old_data[d_idx*64 +: 64] ), .i_en_mask (w_ex1_en_mask ), .i_v0 ('h0 ), .o_res (w_ex1_vec_result [d_idx*64 +: 64] ) );
整数レジスタと浮動小数点の選択は、読み込みレジスタの違いで制御している。
r_ex1_rs1_data <= i_ex0_issue.rd_regs[0].valid & (i_ex0_issue.rd_regs[0].typ == scariv_pkg::FPR) ? ex0_fpr_regread_rs1.data : i_ex0_issue.rd_regs[0].valid & (i_ex0_issue.rd_regs[0].typ == scariv_pkg::GPR) ? ex0_xpr_regread_rs1.data : i_ex0_issue.inst[19:15];
汎用レジスタを使用するときは、オペランドはベクトルの各レーンに展開されるので、事前に展開する。
assign w_vs1 = i_rs1_valid ? w_rs1 : i_vs1; assign w_vs2 = i_vs2; assign w_wr_old = i_wr_old; always_comb begin unique case (i_sew) scariv_vec_pkg::EW8 : for (int b = 0; b < 8; b++) w_rs1.w8 [b] = i_rs1[ 7: 0]; scariv_vec_pkg::EW16: for (int b = 0; b < 4; b++) w_rs1.w16[b] = i_rs1[15: 0]; scariv_vec_pkg::EW32: for (int b = 0; b < 2; b++) w_rs1.w32[b] = i_rs1[31: 0]; scariv_vec_pkg::EW64: for (int b = 0; b < 1; b++) w_rs1.w64[b] = i_rs1[63: 0]; default : w_rs1 = 'h0; endcase // unique case (i_sew) end
演算自体は以下のように定義する。
OP_ADD : begin unique case (i_sew) scariv_vec_pkg::EW8 : for (int b = 0; b < 8; b++) w_res.w8 [b] = w_vs1.w8 [b] + w_vs2.w8 [b]; scariv_vec_pkg::EW16: for (int b = 0; b < 4; b++) w_res.w16[b] = w_vs1.w16[b] + w_vs2.w16[b]; scariv_vec_pkg::EW32: for (int b = 0; b < 2; b++) w_res.w32[b] = w_vs1.w32[b] + w_vs2.w32[b]; scariv_vec_pkg::EW64: for (int b = 0; b < 1; b++) w_res.w64[b] = w_vs1.w64[b] + w_vs2.w64[b]; default : w_res = 'h0; endcase // unique case (i_sew) end
最後に、マスクを適用する。
always_comb begin unique case (i_sew) scariv_vec_pkg::EW8 : for (int b = 0; b < 8; b++) w_masked_res.w8 [b] = i_en_mask[b] ? w_res.w8 [b] : w_wr_old.w8 [b]; scariv_vec_pkg::EW16: for (int b = 0; b < 4; b++) w_masked_res.w16[b] = i_en_mask[b] ? w_res.w16[b] : w_wr_old.w16[b]; scariv_vec_pkg::EW32: for (int b = 0; b < 2; b++) w_masked_res.w32[b] = i_en_mask[b] ? w_res.w32[b] : w_wr_old.w32[b]; scariv_vec_pkg::EW64: for (int b = 0; b < 1; b++) w_masked_res.w64[b] = i_en_mask[b] ? w_res.w64[b] : w_wr_old.w64[b]; default : w_masked_res = 'h0; endcase // unique case (i_sew) end
これで、一応基本的な動作をするベクトル算術演算命令が実行できるようになった。
1 / 29 : vfmv_v_f : PASS 2 / 29 : vmv_v_i : PASS 3 / 29 : vand_v_v : PASS 4 / 29 : vmin_v_v : PASS 5 / 29 : vmv_v_x : PASS 6 / 29 : vsub_v_x : PASS 7 / 29 : vadd_v_v : PASS 8 / 29 : vmv_v_v : PASS 9 / 29 : vmaxu_v_v : PASS 10 / 29 : vmax_v_v : PASS 11 / 29 : vadd_v_x : PASS 12 / 29 : vor_v_v : PASS 13 / 29 : vminu_v_v : PASS 14 / 29 : vxor_v_v : PASS 15 / 29 : vsetvli01 : PASS 16 / 29 : vsub_v_v : PASS 17 / 29 : vrsub_v_x : PASS 18 / 29 : vor_v_x : PASS 19 / 29 : vxor_v_i : PASS 20 / 29 : vmaxu_v_x : PASS 21 / 29 : vand_v_x : PASS 22 / 29 : vadd_v_i : PASS 23 / 29 : vor_v_i : PASS 24 / 29 : vmax_v_x : PASS 25 / 29 : vmin_v_x : PASS 26 / 29 : vminu_v_x : PASS 27 / 29 : vxor_v_x : PASS 28 / 29 : vrsub_v_i : PASS 29 / 29 : vand_v_i : PASS