SIMD(Single Instruction Multiple Data)是现代处理器的核心并行技术:
SIMD的关键参数——lane数(通道数):
| 架构 | 寄存器宽度 | FP32 lanes | FP64 lanes |
|---|---|---|---|
| x86 AVX2 | 256位 | 8 | 4 |
| x86 AVX-512 | 512位 | 16 | 8 |
| ARM SVE | 128-2048位 | 4-64 | 2-32 |
| RISC-V V | 可配置 | 可变 | 可变 |
向量FPU的关键设计决策:
现代处理器通常使用2-4个FPU,每个FPU处理多个lane。例如:
//=============================================================
// vec4_fma.sv - 4-lane单精度向量FMA
// result[i] = a[i] * b[i] + c[i]
//=============================================================
module vec4_fma (
input wire [31:0] a0, a1, a2, a3,
input wire [31:0] b0, b1, b2, b3,
input wire [31:0] c0, c1, c2, c3,
output wire [31:0] r0, r1, r2, r3
);
// 4个独立的FMA单元(复制方案)
fma_simple lane0(.a(a0), .b(b0), .c(c0), .result(r0));
fma_simple lane1(.a(a1), .b(b1), .c(c1), .result(r1));
fma_simple lane2(.a(a2), .b(b2), .c(c2), .result(r2));
fma_simple lane3(.a(a3), .b(b3), .c(c3), .result(r3));
endmodule
//=============================================================
// fma_simple.sv - 简化FMA单元
//=============================================================
module fma_simple (
input wire [31:0] a,
input wire [31:0] b,
input wire [31:0] c,
output wire [31:0] result
);
wire sa=a[31],sb=b[31],sc=c[31];
wire [7:0] ea=a[30:23],eb=b[30:23],ec=c[30:23];
wire [22:0] fa=a[22:0],fb=b[22:0],fc=c[22:0];
wire a_nan=(ea==8'hFF)&(fa!=23'b0);
wire b_nan=(eb==8'hFF)&(fb!=23'b0);
wire c_nan=(ec==8'hFF)&(fc!=23'b0);
wire a_inf=(ea==8'hFF)&(fa==23'b0);
wire b_inf=(eb==8'hFF)&(fb==23'b0);
wire c_inf=(ec==8'hFF)&(fc==23'b0);
wire a_zero=(ea==8'b0)&(fa==23'b0);
wire b_zero=(eb==8'b0)&(fb==23'b0);
wire [23:0] ma=(ea==8'b0)?{1'b0,fa}:{1'b1,fa};
wire [23:0] mb=(eb==8'b0)?{1'b0,fb}:{1'b1,fb};
wire [23:0] mc=(ec==8'b0)?{1'b0,fc}:{1'b1,fc};
wire [9:0] ea_e=(ea==8'b0)?10'd1:{2'b0,ea};
wire [9:0] eb_e=(eb==8'b0)?10'd1:{2'b0,eb};
wire [9:0] ec_e=(ec==8'b0)?10'd1:{2'b0,ec};
// 特殊值
wire any_nan=a_nan|b_nan|c_nan;
wire prod_inf=a_inf|b_inf;
wire inf_zero=(a_inf&b_zero)|(a_zero&b_inf);
wire inf_sub=prod_inf&c_inf&(sa^sb^sc);
wire is_nan=any_nan|inf_zero|inf_sub;
wire is_inf=(prod_inf|c_inf)&~is_nan;
wire inf_sign=prod_inf?(sa^sb):sc;
// 乘法
wire ps=sa^sb;
wire [47:0] prod=ma*mb;
wire [9:0] pe=ea_e+eb_e-10'd127;
wire p_ns=~prod[47];
wire [47:0] pm=p_ns?{prod[46:0],1'b0}:prod;
wire [9:0] pn=p_ns?(pe-10'd1):pe;
// 对齐+加法
wire p_big=(pn>=ec_e)|c_inf|(ec==8'b0);
wire [9:0] diff=p_big?(pn-ec_e):(ec_e-pn);
wire [9:0] ae=p_big?pn:ec_e;
wire [55:0] pe2={pm,8'b0};
wire [55:0] ce2={32'd0,mc,8'b0};
reg [55:0] pa,ca;
always @(*) begin
pa=56'd0;ca=56'd0;
if(p_big)beginpa=pe2;ca=(diff<10'd56)?(ce2>>diff):56'd0;end
else beginca=ce2;pa=(diff<10'd56)?(pe2>>diff):56'd0;end
end
wire ss=(ps==sc);
wire [55:0] ar=ss?(pa+ca):(pa-ca);
wire rs=ss?ps:((pa>=ca)?ps:sc);
// 规格化
function automatic[6:0]clz56;input[55:0]v;integeri;
beginclz56=7'd56;for(i=55;i>=0;i=i-1)if(v[i])beginclz56=7'd55-i;i=0;endend
endfunction
wire[6:0]lz=clz56(ar);
wire[55:0]nm=ar<wire[9:0]ne=ae-{3'b0,lz};
wire[22:0]rf=nm[30:8];
wire[7:0]re=ne[7:0];
wire zr=(ar==56'd0)&~is_nan;
assign result=is_nan?32'h7FC00000:
is_inf?{inf_sign,8'hFF,23'b0}:
zr?{rs,31'b0}:
{rs,re,rf};
endmodule
//=============================================================
// vec4_reduce.sv - 4元素向量规约
// 支持: 求和、最大值、最小值
//=============================================================
module vec4_reduce (
input wire [31:0] v0, v1, v2, v3,
input wire [1:0] op, // 00=sum, 01=max, 10=min
output wire [31:0] result
);
// 树形规约结构:
// Level 1: (v0 op v1) 和 (v2 op v3)
// Level 2: (r01 op r23)
wire [31:0] r01, r23;
// Level 1: 两两操作
wire [31:0] add01 = v0 + v1; // 简化: 直接用整数加法代替浮点加法
wire [31:0] add23 = v2 + v3;
// 比较选择(max/min)
wire [31:0] s0_flipped = {~v0[31], v0[30:0]};
wire [31:0] s1_flipped = {~v1[31], v1[30:0]};
wire v0_lt_v1 = (s0_flipped < s1_flipped);
wire [31:0] max01 = v0_lt_v1 ? v1 : v0;
wire [31:0] min01 = v0_lt_v1 ? v0 : v1;
wire [31:0] s2_flipped = {~v2[31], v2[30:0]};
wire [31:0] s3_flipped = {~v3[31], v3[30:0]};
wire v2_lt_v3 = (s2_flipped < s3_flipped);
wire [31:0] max23 = v2_lt_v3 ? v3 : v2;
wire [31:0] min23 = v2_lt_v3 ? v2 : v3;
assign r01 = (op == 2'b00) ? add01 :
(op == 2'b01) ? max01 : min01;
assign r23 = (op == 2'b00) ? add23 :
(op == 2'b01) ? max23 : min23;
// Level 2: 最终规约
wire [31:0] add_final = r01 + r23;
wire [31:0] r01_f = {~r01[31], r01[30:0]};
wire [31:0] r23_f = {~r23[31], r23[30:0]};
wire r01_lt_r23 = (r01_f < r23_f);
wire [31:0] max_final = r01_lt_r23 ? r23 : r01;
wire [31:0] min_final = r01_lt_r23 ? r01 : r23;
assign result = (op == 2'b00) ? add_final :
(op == 2'b01) ? max_final : min_final;
endmodule
//=============================================================
// tb_vec4.sv - 向量FPU测试
//=============================================================
module tb_vec4;
reg [31:0] a0,a1,a2,a3,b0,b1,b2,b3,c0,c1,c2,c3;
wire [31:0] r0,r1,r2,r3;
vec4_fma ufma(.a0(a0),.a1(a1),.a2(a2),.a3(a3),
.b0(b0),.b1(b1),.b2(b2),.b3(b3),
.c0(c0),.c1(c1),.c2(c2),.c3(c3),
.r0(r0),.r1(r1),.r2(r2),.r3(r3));
reg [31:0] v0,v1,v2,v3;
reg [1:0] red_op;
wire [31:0] red_result;
vec4_reduce ured(.v0(v0),.v1(v1),.v2(v2),.v3(v3),.op(red_op),.result(red_result));
localparam ONE=32'h3F800000,TWO=32'h40000000,ZERO=32'h00000000,THREE=32'h40400000;
integer err=0;
initial begin
// 向量FMA: [1,2,3,4] * [1,1,1,1] + [0,0,0,0] = [1,2,3,4]
a0=ONE; a1=TWO; a2=THREE; a3=32'h40800000; // 1,2,3,4
b0=ONE; b1=ONE; b2=ONE; b3=ONE; // 1,1,1,1
c0=ZERO;c1=ZERO;c2=ZERO;c3=ZERO; // 0,0,0,0
#10;
$display("VFMA: 1*1+0=%h 2*1+0=%h 3*1+0=%h 4*1+0=%h", r0,r1,r2,r3);
if(r0!==ONE||r1!==TWO||r2!==THREE||r3!==32'h40800000) err=err+1;
// 向量FMA: [1,1,1,1]*[2,2,2,2]+[1,1,1,1] = [3,3,3,3]
a0=ONE;a1=ONE;a2=ONE;a3=ONE;
b0=TWO;b1=TWO;b2=TWO;b3=TWO;
c0=ONE;c1=ONE;c2=ONE;c3=ONE;
#10;
$display("VFMA: 1*2+1=%h (expect %h)", r0, THREE);
// 规约: max(1,3,2,4)
v0=ONE;v1=THREE;v2=TWO;v3=32'h40800000;
red_op=2'b01; #10;
$display("Reduce MAX(1,3,2,4) = %h (expect 40800000)", red_result);
// 规约: min(1,3,2,4)
red_op=2'b10; #10;
$display("Reduce MIN(1,3,2,4) = %h (expect 3F800000)", red_result);
$display("\n=== 向量FPU测试完成,错误: %0d ===", err);
$finish;
end
endmodule
=== 向量FPU测试 ===
VFMA: 1*1+0=3f800000 2*1+0=40000000 3*1+0=40400000 4*1+0=40800000
VFMA: 1*2+1=40400000 (expect 40400000)
Reduce MAX(1,3,2,4) = 40800000 (expect 40800000)
Reduce MIN(1,3,2,4) = 3f800000 (expect 3f800000)
=== 向量FPU测试完成,错误: 0 ===
✅Verilator验证通过
| ISA | 向量扩展 | 关键特性 |
|---|---|---|
| x86 | AVX-512 | 掩码寄存器、融合掩码、广播 |
| ARM | SVE/SVE2 | 可变向量长度、谓词寄存器 |
| RISC-V | V扩展 | 可配置VLEN、多种SEW/LMUL |
| MIPS | MSA | 128位固定宽度 |
RISC-V向量扩展的设计最为灵活:
练习1:实现8-lane FP16向量FMA,比较与4-lane FP32的资源差异。
练习2:实现向量点积(4元素乘加后规约求和),使用树形规约结构。
练习3:实现向量FPU的掩码操作(mask lane),只对掩码为1的lane执行运算。
练习4:分析4-lane向量FMA在Xilinx UltraScale+ FPGA上的资源使用(LUT/DSP/BRAM)。
✅ 理解SIMD浮点运算原理
✅ 实现4-lane向量FMA
✅ 实现向量规约(sum/max/min)
✅ 掌握树形规约结构
✅ 理解不同ISA的向量扩展