参考文献

FireFly: A High-Throughput Hardware Accelerator for Spiking Neural Networks With Efficient DSP and Memory Optimization, IEEE Transactions on Very Large Scale Integration (VLSI) Systems, 10.1109/TVLSI.2023.3279349

URL: https://ieeexplore.ieee.org/document/10143752/

其他参考资料

  1. UltraScale Architecture DSP Slice, UG579 (v1.7) June 4, 2018

1. 通过单个 DSP48E2 进行突触交叉计算 (Synaptic Crossbar Computation by a Single DSP48E2)

DSP48E2是Xilinx Ultrascale SoC中会提供的一个数字信号运算IP,基本架构在下两图所示,由三部分组成:

  1. 27位预加器 (27’b pre-adder)

  2. 27 x 18 乘法器(multiplier)

  3. 一个灵活的 48 位 ALU(flexible 48-bit ALU),负责进行加法减法(post-adder/subtractor)、累加(accumulator)、逻辑运算(logic unit)

图1: DSP48E2总体架构

图2: DSP48E2细化的功能架构

让我们先看看论文中的表述:
图3: 单一DSP实现突触交叉运算的核心思路

图3: 论文fig-2

思想:将一组量化成INT8的权重数送到DSPMux里,再利用输入脉冲决定哪些权重通过做后续运算,哪些不通过(不通过等价于卷积乘了0,不参与累加运算)。

四组INT8权重经过有符号拓展成INT12,再拼接成48位的大向量。上30位送进A端口,下18位送进B端口。

DSP48E2有几个控制端口,论文中分为静态设置动态控制。静态设置主要设置DSP工作方式(计算方式),动态控制主要是利用脉冲控制多路选择器。在本文中,静态设置4-bit ALUMODE = 4'b0000, 5-bit INMODE = 5’b10001。标志位置设置完成后,工作状态如下图所示
DSP48E2静态工作状态设置:INMODE
DSP48E2静态工作状态设置:ALUMODE

INMODE说明: 最低位和最高位负责控制A,B输入打几拍之后再输出(即经过几个时钟周期/过了几个filp-flop再输出)。INMODE[0]和INMODE[4]置为1,表示A,B均打一拍之后输出。

INMODE[0]控制Dual A模块的输出
INMODE[4]控制Dual B模块的输出

ALUMODE说明: ALU则被设置为SIMD(单指令多数据, Single Instruction, Multiple Data)模式。输入的48位数据可以被分成12位的4个数据切片/段(data segments),每个小数据段(W, X, Y, Z)独立进行相同的计算,互不干扰。

ALU的SIMD模式

将ALUMODE设成全0,ALU工作在SIMD ADD模式:Z + (W + X + Y + CIN)

下面是DSP IP块的动态控制。论文中主要通过输入脉冲来动态控制一个9位的OPMODE端口来控制WXYZ四个Mux的输出。OPMODE端口的定义和功能如下图所示。

OPMODE控制W的输出
OPMODE控制X的输出
OPMODE控制Y的输出
OPMODE控制Z的输出

以论文fig-2为例,OPMODE={2{s2},3'b001,2'b00,2{s3}}; Z的输出为PCIN(来自上一级经由专用 DSP 级联路径(Dedicated DSP Cascade Path)传到此一级),Y的输出恒为0,s2脉冲控制W输出,s3脉冲控制X输出。

DSP模块E1和E2在端口上的区别

DSP48E2中独有的端口: AMULTSE

附:Verilog代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
module single_dsp48(
input clk,
input wire [11:0] w_20,
input wire [11:0] w_21,
input wire [11:0] w_22,
input wire [11:0] w_23,
input wire [11:0] w_30,
input wire [11:0] w_31,
input wire [11:0] w_32,
input wire [11:0] w_33,
input wire s2,
input wire s3,

output wire [47:0] p

);

wire [29:0] A;
wire [17:0] B;

wire [47:0] C;
wire [8:0] OPMODE;

assign {B, A} = {w_30, w_31, w_32, w_33};
assign C = {w_20, w_21, w_22, w_23};
assign OPMODE = {{2{s2}}, 3'b001, 2'b00, {2{s3}}}; //复制两次s2/s3, 即只能是00, 11两种可能输入

DSP48E2 #(
// Feature Control Attributes: Data Path Selection
.A_INPUT("DIRECT"), // Selects A input source, "DIRECT" (A port) or "CASCADE" (ACIN port)
.B_INPUT("DIRECT"), // Selects B input source, "DIRECT" (B port) or "CASCADE" (BCIN port)
// .USE_DPORT("FALSE"), // Select D port usage (TRUE or FALSE)
.AMULTSEL("A"), //DSP48E2特性
.USE_MULT("NONE"), // Select multiplier usage ("MULTIPLY", "DYNAMIC", or "NONE")
.USE_SIMD("FOUR12"), // SIMD selection ("ONE48", "TWO24", "FOUR12")
// Pattern Detector Attributes: Pattern Detection Configuration
.AUTORESET_PATDET("NO_RESET"), // "NO_RESET", "RESET_MATCH", "RESET_NOT_MATCH"
.MASK(48'h3fffffffffff), // 48-bit mask value for pattern detect (1=ignore)
.PATTERN(48'h000000000000), // 48-bit pattern match for pattern detect
.SEL_MASK("MASK"), // "C", "MASK", "ROUNDING_MODE1", "ROUNDING_MODE2"
.SEL_PATTERN("PATTERN"), // Select pattern value ("PATTERN" or "C")
.USE_PATTERN_DETECT("NO_PATDET"), // Enable pattern detect ("PATDET" or "NO_PATDET")
// Register Control Attributes: Pipeline Register Configuration
.ACASCREG(0), // Number of pipeline stages between A/ACIN and ACOUT (0, 1 or 2)
.ADREG(1), // Number of pipeline stages for pre-adder (0 or 1)
.ALUMODEREG(0), // Number of pipeline stages for ALUMODE (0 or 1)
.AREG(0), // Number of pipeline stages for A (0, 1 or 2)
.BCASCREG(0), // Number of pipeline stages between B/BCIN and BCOUT (0, 1 or 2)
.BREG(0), // Number of pipeline stages for B (0, 1 or 2)
.CARRYINREG(1), // Number of pipeline stages for CARRYIN (0 or 1)
.CARRYINSELREG(1), // Number of pipeline stages for CARRYINSEL (0 or 1)
.CREG(0), // Number of pipeline stages for C (0 or 1)
.DREG(0), // Number of pipeline stages for D (0 or 1)
.INMODEREG(1), // Number of pipeline stages for INMODE (0 or 1)
.MREG(0), // Number of multiplier pipeline stages (0 or 1)
.OPMODEREG(1), // Number of pipeline stages for OPMODE (0 or 1)
.PREG(1) // Number of pipeline stages for P (0 or 1)
)
DSP48E2_inst (
// Cascade: 30-bit (each) output: Cascade Ports
.ACOUT(), // 30-bit output: A port cascade output
.BCOUT(), // 18-bit output: B port cascade output
.CARRYCASCOUT(), // 1-bit output: Cascade carry output
.MULTSIGNOUT(), // 1-bit output: Multiplier sign cascade output
.PCOUT(), // 48-bit output: Cascade output
// Control: 1-bit (each) output: Control Inputs/Status Bits
.OVERFLOW(), // 1-bit output: Overflow in add/acc output
.PATTERNBDETECT(), // 1-bit output: Pattern bar detect output
.PATTERNDETECT(), // 1-bit output: Pattern detect output
.UNDERFLOW(), // 1-bit output: Underflow in add/acc output
// Data: 4-bit (each) output: Data Ports
.CARRYOUT(), // 4-bit output: Carry output
.P(p), // 48-bit output: Primary data output
// Cascade: 30-bit (each) input: Cascade Ports
.ACIN(30'd0), // 30-bit input: A cascade data input
.BCIN(18'd0), // 18-bit input: B cascade input
.CARRYCASCIN(1'b0), // 1-bit input: Cascade carry input
.MULTSIGNIN(1'b0), // 1-bit input: Multiplier sign input
.PCIN(), // 48-bit input: P cascade input
// Control: 4-bit (each) input: Control Inputs/Status Bits
.ALUMODE(4'b0000), // 4-bit input: ALU control input
.CARRYINSEL(3'b000), // 3-bit input: Carry select input
.CLK(clk), // 1-bit input: Clock input
.INMODE(5'b10001), // 5-bit input: INMODE control input
.OPMODE(OPMODE), // 7-bit input: Operation mode input
// Data: 30-bit (each) input: Data Ports
.A(A), // 30-bit input: A data input
.B(B), // 18-bit input: B data input
.C(C), // 48-bit input: C data input
.CARRYIN(1'b0), // 1-bit input: Carry input signal
.D(0), // 25-bit input: D data input
// Reset/Clock Enable: 1-bit (each) input: Reset/Clock Enable Inputs
.CEA1(1'b1), // 1-bit input: Clock enable input for 1st stage AREG
.CEA2(1'b1), // 1-bit input: Clock enable input for 2nd stage AREG
.CEAD(1'b0), // 1-bit input: Clock enable input for ADREG
.CEALUMODE(1'b1), // 1-bit input: Clock enable input for ALUMODE
.CEB1(1'b1), // 1-bit input: Clock enable input for 1st stage BREG
.CEB2(1'b1), // 1-bit input: Clock enable input for 2nd stage BREG
.CEC(1'b1), // 1-bit input: Clock enable input for CREG
.CECARRYIN(1'b0), // 1-bit input: Clock enable input for CARRYINREG
.CECTRL(1'b1), // 1-bit input: Clock enable input for OPMODEREG and CARRYINSELREG
.CED(1'b0), // 1-bit input: Clock enable input for DREG
.CEINMODE(1'b1), // 1-bit input: Clock enable input for INMODEREG
.CEM(1'b1), // 1-bit input: Clock enable input for MREG
.CEP(1'b1), // 1-bit input: Clock enable input for PREG
.RSTA(1'b0), // 1-bit input: Reset input for AREG
.RSTALLCARRYIN(1'b0), // 1-bit input: Reset input for CARRYINREG
.RSTALUMODE(1'b0), // 1-bit input: Reset input for ALUMODEREG
.RSTB(1'b0), // 1-bit input: Reset input for BREG
.RSTC(1'b0), // 1-bit input: Reset input for CREG
.RSTCTRL(1'b0), // 1-bit input: Reset input for OPMODEREG and CARRYINSELREG
.RSTD(1'b0), // 1-bit input: Reset input for DREG and ADREG
.RSTINMODE(1'b0), // 1-bit input: Reset input for INMODEREG
.RSTM(1'b0), // 1-bit input: Reset input for MREG
.RSTP(i_corr_en == 0 || i_dump) // 1-bit input: Reset input for PREG
);
endmodule

PS:不要直接用IP Catelog里的DSP Macro IP核,高度定制化的IP核还是应参照Xilinx数据手册,编写原语实现。

当然,上述代码只能过RTL和Synthesis仿真,不能进行布局布线,因为我们把 OPMODE[5:4] 设置为 ‘01’,这表示正在使用 PCIN 总线,但在上述代码中所有的 PCIN 总线引脚被连接到 GND。

勘误:

原论文中的fig-2©中的第三个赋值语句,赋值顺序应颠倒,即

1
assign {A, B} = {w_30, w_31, w_32, w_33};