本文预设读者已了解脉动矩阵基本原理。
Gemmini中脉动阵列框图如下【1】:
PE(process element)是阵列最小单元,实现一维乘加计算。然后PEs规律地按照行列排列成二维的Tile,需要注意Tile里每一个PE是纯组合地连接,PE之间并无寄存器存储中间结果。Tiles则流水地排列起来组成整个脉动阵列,Tile之间插入了寄存器。这一点从后面的代码分析可以了解得更清楚。需要计算的数据则被提前按照一定规律或是存入脉动阵列或是排列在脉动阵列周围的存储器(bank)里。
GEMM的参数:
object Constant {
def inputType = UInt(32.W)
def outputType = UInt(32.W)
def accType = UInt(32.W)
def OS = false.B
def WS = true.B
def df = OS
def latency = 0
def tileRows=16
def tileColumns=16
def meshRows=16
def meshColumns=16
}
基础单元PE
脉动阵列最核心代码为Mesh模块,包括Tile、PE、PEcontrol等子模块,最基础单元为PE模型。由于Gemmini中脉动阵列(systolic matrix)模块支持两种数据流,OS(output stationary)和WS(weight stationary)模式,因此PE结构较为复杂,输入控制端信号较多。甚至自定义了IO端口模块PEcontrol,源码如下:
class PEControl extends Bundle{
//define two control signal
val propagate = UInt(1.W)
val dataflow = UInt(1.W)
}
latency参数指定要在PEs中添加多少个移位寄存器,因为有些数据类型的乘加操作无法在一个周期完成,如浮点数等。
- 端口定义:
val io = IO(new Bundle {
val in_a = Input(inputType)
val in_b = Input(outputType)
val in_d = Input(outputType)
val out_a = Output(inputType)
val out_b = Output(outputType)
val out_c = Output(outputType)
val in_control = Input(new PEControl(accType))
val out_control = Outp