# 一、微架构体系

微架构又称为微体系结构/微处理器体系结构。是在计算机工程中，将一种给定的指令集架构在处理器中执行的方法。一种给定指令集可以在不同的微架构中执行。实施中可能因应不同的设计目的和技术提升而有所不同。计算机架构是微架构和指令集设计的结合。


## （一）概述
GPGPU-Sim 3.x由CPU部分和GPU部分组成。

GPGPU-Sim 3.x中的微体系结构（时序）模型仅对GPU时序周期建模。它不对CPU时序或PCI Express时序（即CPU和GPU之间的内存传输时间）建模。

目前正在努力提供组合的CPU和GPU模拟器，其中GPU部分由GPGPU-Sim建模。

GPGPU-Sim 3.x对GPU微体系结构进行建模，类似于NVIDIA GeForce 8x，9x和Fermi系列中的微体系结构。

GPGPU-Sim的目的是为架构研究提供基础，而不是对任何特定的商用GPU进行精确建模。也就是说，GPGPU-Sim 3.x已针对NVIDIA GT 200和NVIDIA Fermi GPU架构进行了校准。


## （二）Top-Level Organization

### 1、简介
-	SIMT（Single Instruction Multiple Thread）：单指令多线程
-	SIMD （Single Instruction Multiple Data）：单指令多数据流
SIMT内核为高度多线程的流水线SIMD处理器建模，大致相当于NVIDIA所谓的流式多处理器（SM）或AMD所谓的计算单元（CU）。
 

### 2、时钟域
GPGPU-Sim支持四个独立的时钟域：

（1）SIMT核心群集时钟域

（2）互连网络时钟域

（3）L2高速缓存时钟域，该域适用于除DRAM外的存储器分区单元中的所有逻辑

（4）DRAM时钟域

时钟频率可以具有任意值（它们不必是彼此的倍数）。换句话说，我们假设时钟域之间存在同步器。

在GPGPU-Sim 3.x仿真模型中，相邻时钟域中的单元通过时钟交叉缓冲区进行通信，这些缓冲区以源域的时钟速率填充，并以目标域的时钟速率耗尽。（怎么理解这个？）


## （三）SIMT核心集群
The SIMT Cores are grouped into SIMT Core Clusters。
SIMT核心集群中的SIMT核心共享一个公共端口，用于连接互连网络。
 

如图所示：
-	每个SIMT Core Clusters包含多个SIMT Core
-	使用一个 response FIFO 队列来接收来自互联网络的数据包。
-	数据包会被定向到：指令缓存器 或者 存储  pipeline（LDST Uint）。数据包出队
-	若数据包不能被SIMT Core接收，则队列停止
-	为了在LDST单元上生成内存请求，每个SIMT内核都有其自己的互连网络输入端口injection port。
-	但是，输入端口缓冲区(injection port buffer)由群集中的所有SIMT内核共享。






## （四）SIMT核心
SIMT内核对高度多线程的流水线SIMD处理器进行建模，大致相当于NVIDIA所谓的流式多处理器（SM）或AMD所谓的计算单元（CU）。
流处理器（SP）或CUDA核心将对应于SIMT核心中ALU  pipeline内的通道。
 
### 1、Front End
Front End包括：指令高速缓存访问（Fetch）和指令缓冲逻辑（I-Buffer）、Score Board和调度逻辑、SIMT堆栈（SIMT-Stack）。

#### 1.1、Fetch and Decode

##### （1）I-Buffer基本概念
The instruction buffer (I-Buffer) block用来保存从指令缓存中取出来的指令。I-Buffer是静态分区的，因此运行在SIMT core上的所有warps都有专用的存储空间来保存指令。

补充一些有关GPU的概念：CUDA编程——GPU架构，由sp，sm，thread，block，grid，warp说起

硬件上：
-	SP：最基本的处理单元，streaming processor，也称为CUDA core。最后具体的指令和任务都是在SP上处理的。GPU进行并行计算，也就是很多个SP同时做处理。
-	SM：多个SP加上其他的一些资源组成一个streaming multiprocessor。也叫GPU大核，其他资源如：warp scheduler，register，shared memory等。
SM可以看做GPU的心脏（对比CPU核心），register和shared memory是SM的稀缺资源。
CUDA将这些资源分配给所有驻留在SM中的threads。因此，这些有限的资源就使每个SM中active warps有非常严格的限制，也就限制了并行能力。

软件上：
-	thread：一个CUDA的并行程序会被以许多个threads来执行。
-	warp：GPU执行程序时的调度单位，目前cuda的warp的大小为32，同在一个warp的线程，以不同数据资源执行相同的指令,这就是所谓 SIMT。
-	block：数个threads会被群组成一个block，同一个block中的threads可以同步，也可以通过shared memory通信。
-	grid：多个blocks则会再构成grid。

 
##### （2）I-Buffer功能
当前模型中，每个warp拥有三个I-Buffer实体，每个I-Buffer都有：有效位、就绪位 与 已经解码的warp指令：
-	有效位：指示当前I-Buffer中存在未发布（执行？）的warp已解码指令
-	就绪位：指示该warp指令已经准备好发布到执行pipeline中了

就绪位置位：在计划（schedule）与发布（issue）阶段，使用scoreBoard logic和硬件资源使之置位（感觉在说，这个置位是硬件完成的）

I-Buffer初始化为空，有效位与就绪位均被禁用

##### （3）I-Buffer取指
若warp在I-Buffer中没有任何有效指令，则可以执行取指操作

-	Warp按照schedule以循环的方式来访问指令高速Cache
-	选中warp后，读取请求将会被发送到指令高速cache中，请求包括：下一条指令的地址（我的理解：就是让指令高速cache把下一条指令发送到这个地址中）。
-	默认情况下，将会取出两个连续的指令
-	一旦warp收到指令，就会激活I-Buffer中的有效位，直到该warp的所有指令都issue到了执行pipeline


#### 1.2、Instruction Issue
第二个round robin arbiter选择一个warp，从I-Buffer发布到其余  pipeline。This round robin arbiter is decoupled from the round robin arbiter used to schedule instruction cache accesses. 

issue调度器可以配置成：在一个cycle里，issue多个指令，这些指令来自于同一个warp。

当前的每个有效指令（即：已经译码过了，但是没有issue），需要坚持其warp是否有资格issue：
- （1）它的warp没有在障碍处等待
- （2）它的I-Buffer条目中有有效指令（设置了有效位）
- （3）ScoreBoard检查通过（有关更多详细信息，请参见记分板部分）
- （4）指令流水线的操作数访问阶段没有停止。

内存指令（加载，存储或memory barriers）被issue到内存 pipeline。

对于其他指令，它始终首选使用SP pipeline进行操作（issue），这种情况下SP pipeline与SFU pipeline都能使用。

但是，如果检测到control hazard，则将清除与该warp对应的I-Buffer中的指令。warp的下一个pc将更新为指向下一条指令（假定所有分支均未采用）。

有关处理控制流的更多信息，请参阅SIMT Stack。

#### 1.3、SIMT Stack

每个warp的SIMT堆栈被用于处理单指令多线程上的 execution of branch divergence。

这里解释一下divergence：
```
因为所有同一个warp中的thread必须执行相同的指令，那么如果这些线程在遇到控制流语句时，如果进入不同的分支，那么同一时刻除了正在执行的分之外，其余分支都被阻塞了，十分影响性能。

这类问题就是warp divergence。

请注意，warp divergence问题只会发生在同一个warp中。
```
由于divergence降低了这些体系结构的效率，因此可以采用不同的技术来降低这种影响。
最简单的技术之一是： post-dominator stack-based reconvergence mechanism. 。为了提高SIMT架构的效率，This technique synchronizes the divergent branches at the earliest guaranteed reconvergence point。与以前的GPGPU-Sim版本一样，GPGPU-Sim 3.x也采用这种机制。

Entries of the SIMT stack represents a different divergence level。在每个divergence branch中，一个new entry被放入到栈顶，当warp达到它的reconvergence point的时候，栈顶的entry将会被弹出。每个entry都会存储新分支的target PC，the immediate post dominator reconvergence PC and the active mask of threads that are diverging to this branch. 

在这个模型中，每个warp的SIMT堆栈将会在对应的warp指令issue后更新，在no divergence时， The target PC通常会更新到下一个PC；但是在有divergence时，new entries将会与new target PC、the active mask that corresponds to threads that diverge to this PC以及their immediate reconvergence point PC压入堆栈中。因此，如果在栈顶的next PC不等于the PC of the instruction currently under check，就会有control hazard。更多有关信息:  Dynamic Warp Formation: Efficient MIMD Control Flow on SIMD Graphics Hardware

请注意，众所周知，NVIDIA和AMD实际上是使用特殊指令来修改其差异堆栈的内容。 这些散度堆栈指令未在PTX中公开，但在实际的硬件SASS指令集中可见（使用decuda或NVIDIA的cuobjdump可见）。 当现在版本的GPGPU-Sim 3.x配置为通过PTXPlus执行SASS（请参阅PTX与PTXPlus）时，它会忽略这些低级指令，而是会创建一个可比较的控制流程图来标识立即的后支配者。 我们计划在将来的GPGPU-Sim 3.x版本中支持执行低级分支指令。



#### 1.4、Scoreboard

Scoreboard 会检查WAW和RAW有没有dependency hazards。正如之前所说的，经由warp写入的寄存器在issue阶段会被保留，ScoreBoard用过warp的ID来索引（找到寄存器），每个寄存器的编号都有与之对应的warp ID，在ScoreBoard中保留的寄存器 会在写回阶段释放。

已经译码过的指令不会马上issue，而是需要ScoreBoard确认没有WAW或者RAW hazards 时，才能issue。

**如何检测WAW或者RAW hazards？**
ScoreBoard通过跟踪来检查是否有这种情况：寄存器将要被一个已经issue的指令执行写入，但是还没有写入。

The scoreboard detects WAW and RAW hazards by tracking which registers will be written to by an instruction that has issued but not yet written its results back to the register file.（记分板通过跟踪已发出但尚未将其结果写回到寄存器文件中的指令将写入哪些寄存器来检测WAW和RAW危害。）


### 2、Register Access and the Operand Collector

各种NVIDIA专利都描述了一种称为“Operand Collector”的结构。 The operand collector is a set of buffers and arbitration logic used to provide the appearance of a multiported register file using multiple banks of single ported RAMs. （Operand Collector是一组缓冲区和arbitration logic，使用多个Single ported RAM Bank提供多端口寄存器文件的外观）。 

overall arrangement节省面积、降低功耗，这对于提高throughput很重要。 请注意，AMD还使用存储区寄存器文件，但编译器负责确保对这些文件进行访问，以免发生存储区冲突。

下图展示了GPGPU-Sim 3.x models 的 operand collector详细构造：

![](_v_images/_1569811744_3802.png)

指令译码之后，有个硬件单元称为：collector unit，该单元被分配于保存指令的源操作数。

collector unit不是通过寄存器重命名来消除 name dependencies的，而是及时对space register operand 进行访问，从而确保在一个cycle内，不会对bank进行多次访问。

在上图所示的结构中，4个collector unit都包含了3个operand entries，每个operand entry有4个字段：有效位、寄存器标识符、就绪位以及操作数（operand data ）。
每个操作数有128个比特组成（int 类型大小32位？或者是128位） (one four byte value for each scalar thread in a warp)
collector unit包含了标识符，用于表示这条指令属于哪个warp。


The arbitrator为每个bank设置了一个读取请求队列，用于保存访问请求直到请求被批准（执行）。当从译码阶段接收到指令同时collector unit处于可用状态是，将会分配指令与操作数（我理解的是把指令分给某个warp），并设置warp、寄存器标识符、有效位。

此外，源操作数的读取请求将会在arbiter中的读取请求队列中。为了简化设计，数据写回请求的优先级要高于读取请求。arbiter选择一组无冲突的访问（最多4个），发送到register file。为了减少crossbar和collector unit的面积，需要进行选择，因此每个收集器单元每个周期仅接收一个操作数。


当从寄存器文件中读取每个操作数并将其放入相应的collect unit时，将设置“就绪位”。 最后，当所有操作数准备就绪时，将指令发布到SIMD执行单元。

在我们的模型中，每个后端pipeline（SP，SFU，MEM）都有一组专用的collector unit，它们共享一组通用collector unit。 每个pipeline可用的单元数和capacity of the pool of the general units是可配置的。

### 3、ALU Pipelines

GPGPU-Sim v3.x为两种类型的ALU功能单元建模。
- SP单元执行除先验条件外的所有类型的ALU指令。
- SFU单元执行先验指令（正弦，余弦，对数等，换而言之就是结果确定的数学计算？）。

两种类型的单元均已流水线化和SIMD化。
SP单元通常每个周期可以执行一条warp的指令，而SFU单元只能每隔几个周期执行一条新的warp的指令，具体取决于指令类型。例如，SFU单元可以每4个周期执行一个正弦指令，或者每2个周期执行一个倒数指令。不同类型的指令也具有不同的执行等待时间。


每个SIMT core具有一个SP unit和一个SFU unit。 每个单元（SP或者SFU）都有一个来自operand collector的independent issue port。
两个单元共享连接到同一个用于写回阶段的输出流水线寄存器。 在操作数收集器的输出处有一个result bus allocator，以确保这些单元永远不会由于共享的写回而停顿。 每条指令在发布给任一单元之前都需要在结果总线中分配一个循环时隙。 请注意，内存pipeline具有其自己的写回阶段，并且不受此结果总线分配器（result bus allocator）的管理。

### 4、Memory Pipeline (LDST unit)
GPGPU-Sim支持CUDA中在PTX中可见的各种内存空间。 
在我们的模型中，每个SIMT内核具有4个不同的片上1级内存（on-chip level 1 memories）：共享内存，数据缓存，常量缓存（constant cache）和纹理缓存（texture cache）。 

下表展示了4片上存储器为哪种类型的存储器访问服务：

![](_v_images/_1569829196_21208.png)

![](_v_images/_1569829386_1166.png)
尽管将这些模型建模为单独的物理结构，但它们都是内存管线（LDST单元）的所有组件，因此它们都共享相同的写回阶段。下面介绍如何为这些空间提供服务：
- （1）纹理内存：对纹理内存的访问被缓存在L1纹理缓存（仅保留用于纹理访问）中，并且也缓存在L2缓存（如果存在的话）中。 
L1纹理缓存是1998年论文[3]中描述的一种特殊设计。 GPU上的线程无法写入纹理存储空间，因此L1纹理缓存是只读的。
- （2）共享内存：每个SIMT内核包含一个可配置数量的shared scratchpad memory，可由thread block中的线程共享。 该存储器空间不受任何L2的支持（就是不存在L2 cache？），并且由程序员明确管理。
- （3）常量存储器：常量和参数存储器被缓存在只读常量缓存中。
- （4）参数存储器：见上文
- （5）本地内存（Local Memory）：缓存在L1数据缓存中，并由L2支持（两级cache）。 除了无法在本地（私有）数据共享的情况下将值写回逐出外，类似于下面的全局内存的方式处理（Treated in a fashion similar to global memory below except values are written back on eviction since there can be no sharing of local (private) data.）。
- （6）全局存储器（Global Memory）：全局和本地访问都由L1数据缓存提供服务。 如CUDA 3.1编程指南中所述，从同一个warp通过标量线程进行的访问以half-warp合并（Accesses by scalar threads from the same warp are coalesced on a half-warp basis）。1个SIMT core周期最多可以处理2个访问（These accesses are processed at a rate of 2 per SIMT core cycle），因此可以在一个周期内为完全合并为2个访问（每半个循环一次）的一条存储器指令提供服务。 
对于那些产生两个以上访问的指令，这些指令将以每个周期完成2个访问的速率来访问存储系统。 因此，如果一条内存指令产生32条访问（warp中每个通道一个），则将至少需要16个SIMT核心周期才能将指令移至下一个流水线阶段。


#### 4.1、L1 Data Cache

L1数据缓存是每个SIMT核心的专用 non-blocking 一级缓存，用于本地和全局内存访问。 The L1 cache is not banked，并且能够在每个SIMT核心周期为两个合并的内存请求（访问）提供服务。 传入的内存请求不得跨越L1数据高速缓存中的两个或更多高速缓存行。 还要注意，L1数据高速缓存不是一致的（应该是在说和内存中的数据不一定一致，没有使用写穿透策略）。

下表总结了L1数据缓存的写入策略。

![](_v_images/_1569833604_5959.png)




#### 4.2、Texture Cache
纹理缓存模型是预取纹理缓存（我的理解是先从内存中取出一部分数据）。纹理存储器访问主要表现出空间局部性（例如：矩阵，当取出某一行中的某个数（int），可以顺带一并取出该行的所有的数据，并且所花费的时间几乎是一样的），纹理存储器在这里的大小为：16KB。详情见：[CUDA纹理存储器的特性及其使用](https://blog.csdn.net/zhangpinghao/article/details/16833489)

在实际的图形使用场景中，许多纹理缓存访问会丢失（类似于cache未命中）。 DRAM中访问纹理的等待时间约为100个周期。考虑到较大的内存访问延迟和较小的缓存大小，何时在缓存中分配行的问题变得至关重要。

预取纹理缓存如何解决访问丢失（未命中）问题？通过设置标签数组、数据数组。标签数组表示：100个周期之后，仍然未被访问的cache的状态。数据数组表示：未命中时，cache的状态。（The prefetching texture cache solves the problem by temporally decoupling the state of the cache tags from the state of the cache blocks. The tag array represents the state the cache will be in when misses have been serviced 100's of cycles later. The data array represents the state after misses have been serviced. ）

The key to making this decoupling work is to use a reorder buffer to ensure returning texture miss data is placed into the data array in the same order the tag array saw the access.详情见：[Prefetching in a Texture Cache Architecture](http://www-graphics.stanford.edu/papers/texture_prefetch/texture_prefetch_comp.pdf)

#### 4.3、Constant (Read only) Cache
通过L1常量缓存访问常量和参数存储器。此缓存是通过标签数组实现的，并且类似于L1数据缓存，但无法写入。

### 5、Thread Block / CTA / Work Group Scheduling

线程块（CUDA术语中的协作线程数组（CTA）或OpenCL术语中的工作组）一次发给SIMT Core。

在每个SIMT Core时钟周期中，线程块发布机制都会以循环方式选择并循环遍历SIMT Core集群。对于每个选定的SIMT核心群集，将以循环方式选择并循环遍历SIMT核心。对于每个选定的SIMT核心，如果该SIMT核心上有足够的可用资源，则会从选定的内核向该核心issue单个线程块。

如果在应用程序中使用了多个CUDA流或命令队列，则可以在GPGPU-Sim中同时执行多个内核。

可以在不同的SIMT core上执行不同的内核；单个SIMT Core一次只能执行来自单个内核的线程块。

如果同时执行多个内核，则选择发给每个SIMT core的内核也是循环的。

当前，在[CUDA编程手册](https://docs.nvidia.com/pdf/CUDA_C_Programming_Guide.pdf)中详细介绍了：如何在CUDA体系结构上并发执行内核。

## （五）互连网络

通过互连网络通信的包只有4种：

（1）从SIMT core集群发送到内存分区的读请求

（2）从SIMT core集群发送到内存分区的写请求

（3）从内存分区发送到SIMT core集群的读应答

（4）从内存分区发送到SIMT core集群的写确认



这4中包，通过2个网络发送，模拟两个互连网络：一个用于从SIMT核心群集到内存分区的流量，另一个用于从内存分区回到SIMT核心群集的流量。

2个buffer：

（1）injection buffer（icnt  <- SIMT or 内存分区）

（2）ejection buffer（icnt  -> SIMT or 内存分区）





We effectively have a two stage buffer per virtual channel at the output, the first stage contains a buffer per virtual channel that has the same space as the buffers internal to the network, the next stage buffer per virtual channel is where we cross from one clock domain to the other--we push flits into the second stage buffer in the interconnect clock domain, and remove whole packets from the second stage buffer in the shader or L2/DRAM clock domain. We return a credit only when we are able to move a flit from the first stage buffer to the second stage buffer (and this occurs at the interconnect clock frequency).




