本文预设读者已了解脉动矩阵基本原理。

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.Bdef WS = true.Bdef df = OSdef latency = 0def tileRows=16def tileColumns=16def meshRows=16def 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 signalval 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 = Output(new PEControl(accType))val in_valid = Input(Bool())val out_valid = Output(Bool())})

矩阵乘法:C = A ∗ B + D,D是偏置矩阵。输入有in_a\in_b\in_d,以及控制信号in_control,和valid信号;输出信号有,直接传递的out_a,以及out_b\out_c,传递给下一个PE的控制信号out_control,和valid信号.
PE模块框图如下:

  • 源码分析

1.移位输出,直接传到下一个PE。

  val cType = if (df == Dataflow.WS) inputType else accTypeval a  = ShiftRegister(io.in_a, latency)val b  = ShiftRegister(io.in_b, latency)val d  = ShiftRegister(io.in_d, latency)
//val c1 = Reg(cType)val c2 = Reg(cType)val dataflow = ShiftRegister(io.in_control.dataflow, latency)val prop  = ShiftRegister(io.in_control.propagate, latency)val shift = ShiftRegister(io.in_control.shift, latency)val valid = ShiftRegister(io.in_valid, latency) // TODO should we clockgate the rest of the ShiftRegisters based on the values in this ShiftRegisters
//*after latency cycle ,input signal passthrough to output.
//*why output a ?
//In both dataflow,a will always propagate. io.out_a := aio.out_control.dataflow := dataflowio.out_control.propagate := propio.out_control.shift := shiftio.out_valid := valid

2.计算模块部分
TODO

Tile模块

  • 参数
/*** A Tile is a purely combinational 2D array of passThrough PEs.* a, b, s, and in_propag are broadcast across the entire arrayand are passed through to the Tile's outputs* @param width The data width of each PE in bits* @param rows Number of PEs on each row* @param columns Number of PEs on each column*///*Arithmetic ??what type?//*pe_latency: Int what mean?class Tile[T <: Data : Arithmetic](inputType: T, outputType: T, accType: T, df: Dataflow.Value, pe_latency: Int, val rows: Int, val columns: Int) extends Module {//... code skipped
}

T <: Data : Arithmetic,T为自定义数据类型,属于Data类的子类和自定义Arithmetic类。Arithmetic类详见Arithmetic.scala。

  • 模块端口
  val io = IO(new Bundle {val in_a        = Input(Vec(rows, inputType))val in_b        = Input(Vec(columns, outputType)) // This is the output of the tile next to itval in_d        = Input(Vec(columns, outputType))val in_control  = Input(Vec(columns, new PEControl(accType)))val out_a       = Output(Vec(rows, inputType))val out_b       = Output(Vec(columns, outputType))val out_c       = Output(Vec(columns, outputType))val out_control = Output(Vec(columns, new PEControl(accType)))val in_valid = Input(Vec(columns, Bool()))val out_valid = Output(Vec(columns, Bool()))})

端口与PE相同,但量级从一维变成二维,采用Vec统一例化。

  • 源码分析

1.Seq例化二维PE阵列。

  val tile = Seq.fill(rows, columns)(Module(new PE(inputType, outputType, accType, df, pe_latency)))val tileT = tile.transpose

2.阵列内部PE连接关系
PEs被纯组合地行和列顺序连接。每行连接以in_a的传播为例:

  // Broadcast 'a' horizontally across the Tilefor (r <- 0 until rows) {tile(r).foldLeft(io.in_a(r)) {case (in_a, pe) =>pe.io.in_a := in_ape.io.out_a}}

对于每一行PE(tile( r )),首先in_a( r )连接到最右边的PE,然后再前一个输出连接到后一个输入,类似于一个链表。准确理解foldLeft函数含义即可。计算过程:首先tile(r ).foldLeft(io.in_a( r)){…}以io.in_a(r )为初始值,匹配到case里的in_a,tile(r )第一个函数元素匹配到pe,代码{pe.io.in_a := in_a,pe.io.out_a}实现in_a与pe的连接,并且运算结果为pe.io.out_a,作为下一次case里匹配到in_a的值,因此即可实现一行里每一个PE的级联。
每列连接同理:

  // Broadcast 'b' vertically across the Tilefor (c <- 0 until columns) {tileT(c).foldLeft(io.in_b(c)) {case (in_b, pe) =>pe.io.in_b := in_bpe.io.out_b}

3.驱动底部和右部输出

  // Drive the Tile's bottom IOfor (c <- 0 until columns) {io.out_c(c) := tile(rows-1)(c).io.out_cio.out_b(c) := tile(rows-1)(c).io.out_bio.out_control(c) := tile(rows-1)(c).io.out_controlio.out_valid(c) := tile(rows-1)(c).io.out_valid}// Drive the Tile's right IOfor (r <- 0 until rows) {io.out_a(r) := tile(r)(columns-1).io.out_a}

取最后一行和最后一列相应信号驱动模块的输出。

Mesh模块

结构与Tile相似,不同之处在于子单元之间插入了寄存器,使得各Tile之间成流水形式组织起来。
以行流水的a为例:

  // Chain tile_a_out -> tile_a_in (pipeline a across each row)// TODO clock-gate A signals with in_garbagefor (r <- 0 until meshRows) {mesh(r).foldLeft(io.in_a(r)) {case (in_a, tile) =>tile.io.in_a := RegNext(in_a)tile.io.out_a}}

RegNext(in_a)即往Tile之间插入一个寄存器,存储中间状态。

结语

接下来将源码中既有的脉动阵列测试模块跑通。目前Gemmini源码run还有一些问题,环境未配好,亟待解决。

参考文献:
1.Genc H, Haj-Ali A, Iyer V, et al. Gemmini: An agile systolic array generator enabling systematic evaluations of deep-learning architectures[J]. arXiv preprint arXiv:1911.09925, 2019.
2.https://github.com/ucb-bar/gemmini

开源加速器Gemmini代码解析(一):脉动阵列相关推荐

  1. 学习开源播放器代码解析之参数设置

    1.前言   出于学习音视频的目的,在Github找了个基于FFMPEG的播放器代码,代码量比较小.地址:fflayer.于是乎下载编译了下,运行结果良好.So,出于学习的目的,写写学习笔记,归纳归纳 ...

  2. 视觉SLAM开源算法ORB-SLAM3 原理与代码解析

    来源:深蓝学院,文稿整理者:何常鑫,审核&修改:刘国庆 本文总结于上交感知与导航研究所科研助理--刘国庆关于[视觉SLAM开源算法ORB-SLAM3 原理与代码解析]的公开课. ORB-SLA ...

  3. 密码算法中iv值是什么_?标检测中的?极?值抑制算法(nms):python代码解析

    ⾮极⼤值抑制(Non-Maximum Suppression)原理 ⾮极⼤值抑制,顾名思义,找出极⼤值,抑制⾮极⼤值.这种思路和算法在各个领域中应⽤⼴泛,⽐如边缘检测算法canny算⼦中就使⽤了该⽅法 ...

  4. 【Alljoyn】Alljoyn学习笔记五 AllJoyn开源技术基础概念解析

    AllJoyn开源技术基础概念解析 摘要: 总线(Bus) 实现P2P通信的基础 AllJoyn 的底层协议类似于D-Bus,相当于是跨设备分布式的 D-Bus 总线附件(Bus Attachment ...

  5. nms python代码_?标检测中的?极?值抑制算法(nms):python代码解析

    ⾮极⼤值抑制(Non-Maximum Suppression)原理 ⾮极⼤值抑制,顾名思义,找出极⼤值,抑制⾮极⼤值.这种思路和算法在各个领域中应⽤⼴泛,⽐如边缘检测算法canny算⼦中就使⽤了该⽅法 ...

  6. java eventbus 原理_本文为 Android 开源项目实现原理解析 EventBus 部分,从源码分析 EventBus 的实现原理...

    之前太忙导致 Android 开源项目实现原理解析 一度搁浅,目前一期进行中,我也完成了 EventBus 分析的初稿,大家可以稍微看看后面会继续润色下. PS:本文直接复制 Markdown,格式有 ...

  7. 开源项目征集 | CSDN “开源加速器计划”之【开源技术栈选型 Show】

    国内专业开发者社区CSDN旗下的代码托管协作平台GitCode历经7年摸索,如今全面升级!它承载着新时期的使命和责任,致力于服务全球中文开发者,现推出"开源服务加速计划",助力国内 ...

  8. 火出B站的冠状病毒传播模拟仿真程序代码解析来啦!

    整理 | 夕颜 代码解析部分来源于 | xuyuanwai(ID:xxuyuanwai) 近日,有一位B站up主上传了一段视频,用计算机仿真程序模拟了冠状病毒传播的场景,并提醒大众不要出门活动.这个视 ...

  9. Wormhole资产跨链项目代码解析

    1. 引言 Wormhole支持基于Solana与多个链进行资产转移,开源代码为: https://github.com/certusone/wormhole/tree/main 实际部署配置信息参见 ...

  10. Polygon zkEVM的pil-stark Fibonacci状态机代码解析

    1. 引言 前序博客有: Polygon zkEVM的pil-stark Fibonacci状态机初体验 STARKs and STARK VM: Proofs of Computational In ...

最新文章

  1. 解决Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz国内下载速度缓慢的问题
  2. OpenResty快速入门
  3. 实例18:python
  4. MySQL 深潜 - MDL 锁的实现与获取机制
  5. docker 安装azkaban_azkaban安装
  6. AndroidStudio中添加第三库文件的方法
  7. mybatis-java-依赖注入
  8. php大作业含代码_目标检测 | 目标检测技巧大汇总(含代码与解读)
  9. 远程体验Linux Lite
  10. 阶段5 3.微服务项目【学成在线】_day01 搭建环境 CMS服务端开发_25-页面查询接口测试-Swagger...
  11. 如何拒绝国外IP/屏蔽国外IP访问服务器?
  12. jvm执行java大致采用过程_java练习题
  13. python正则匹配日期2019-03-11_正则表达式验证日期(多种日期格式)——转载
  14. 计算机网络教室的配置,计算机网络教室建设和配备要求.doc
  15. Volley读取文档和图片
  16. 囚徒困境(进化优化算法)
  17. Terracotta 3.2.1简介 (二)
  18. Allegro学习进行时
  19. 键盘钢琴c语言,键盘钢琴c
  20. 【C#】身份证识别(三):身份证信息识别

热门文章

  1. m7405d粉盒清零方法_联想m7605d清零方法
  2. keil4调试时出现Error Flash Download failed - “Cortex-M4”问题
  3. 图像分割并存储 matlab,matlab图像分割算法源码.pdf
  4. OpenCV怎么下载?OpenCV下载后怎么配置?
  5. 孙玄:微服务架构中分布式事务实现方案如何取舍
  6. mtk刷机工具sp flash tool_qpst刷机(翻到高中刷机时的记录,想来qpst对现在玩机的人也有用吧...
  7. 三层交换机静态路由VLAN配置实例(华为)
  8. sql vb xml 换行_VB中文本框换行--------急!!!
  9. MTK TouchPanel 驱动框架
  10. 乾颐堂现任明教教主(2014年课程)TCPIP协议详解卷一 第二节课笔记