# 一、 GPGPU-SIM模拟器的配置文件

## （一） 初始化shader的配置文件

### 1、 每个shader的线程数


### 2、 warp的大小


### 3、 确定流水线的级数，每个stage的名称

### 4、 SP、SFU的延迟

### 5、 L1 cache配置

##### （1） cache类型
根据cache的种类，可以分为三种：
```
    enum FuncCache {
        FuncCachePreferNone = 0,
        FuncCachePreferShared = 1,
        FuncCachePreferL1 = 2
    };
```

##### （2） cache的策略
实际上下面四个配置文件都是同一个类，不过根据输入的string，其相关参数会有所不同：
```
        int ntok = sscanf(config,"%u:%u:%u,%c:%c:%c:%c:%c,%c:%u:%u,%u:%u,%u",
                          &m_nset, &m_line_sz, &m_assoc, &rp, &wp, &ap, &wap,
                          &sif, &mshr_type, &m_mshr_entries, &m_mshr_max_merge,
                          &m_miss_queue_size, &m_result_fifo_entries,
                          &m_data_port_width);
```
###### ①、 替换策略
cache更新的时候：
```
        switch (rp) {
            case 'L': m_replacement_policy = LRU; break;
            case 'F': m_replacement_policy = FIFO; break;
            default: exit_parse_error();
        }
```


###### ②、 写策略 （Write Hit的时候，所采用的策略）
```
        switch (wp) {
            case 'R': m_write_policy = READ_ONLY; break;        // 只读不写
            case 'B': m_write_policy = WRITE_BACK; break;       // 写回（只修改cache的内容）：在数据更新时只写入缓存Cache，只在数据被替换出缓存时，被修改（用dirty标记）的缓存数据才会被写到后端存储（主存）
            case 'T': m_write_policy = WRITE_THROUGH; break;    // 在数据更新时，同时写入缓存Cache和后端存储（主存）；
            case 'E': m_write_policy = WRITE_EVICT; break;      // 全局内存的策略，网上查了下没找到相关资料
            case 'L': m_write_policy = LOCAL_WB_GLOBAL_WT; break;    // 也没找到相关资料
            default: exit_parse_error();
        }
```
![](_v_images/_1571627235_3742.png)

这里先来解释下，一般情况下，cache发生Miss 或 Hit的时候的策略：
![](_v_images/_1571627636_6995.png)

###### ③、 分配策略
```
        switch (ap) {
            case 'm': m_alloc_policy = ON_MISS; break;
            case 'f': m_alloc_policy = ON_FILL; break;
            default: exit_parse_error();
        }
```

###### ④、 mshr类型
```
        switch (mshr_type) {
            case 'F': m_mshr_type = TEX_FIFO; assert(ntok==13); break;
            case 'A': m_mshr_type = ASSOC; break;
            default: exit_parse_error();
        }
```
MSHR：Miss Status Holding Registers ，未命中状态保留寄存器，用于保存未命中的状态（是指某个未命中的cache的行列）

MSHR表，用来存放miss轨迹的，我的理解是，这个MSHR是用来调试或者计算性能的时候才会用到

###### ⑤、 写分配策略 （Write Miss的时候，采用的策略）
```
        switch(wap){
            case 'W': m_write_alloc_policy = WRITE_ALLOCATE; break;    // 将要写的地址所在的块先从main memory(主存)调入cache中，然后写cache；
            case 'N': m_write_alloc_policy = NO_WRITE_ALLOCATE; break; // 将要写的内容直接写回main memory;
            default: exit_parse_error();
        }
```
![](_v_images/_1571627248_19089.png)

这里先来解释下，一般情况下，cache发生Miss 或 Hit的时候的策略：
![](_v_images/_1571627636_6995.png)



###### ⑥、 索引策略？？
很迷，不知道怎么用
```
        switch(sif){
            case 'H': m_set_index_function = FERMI_HASH_SET_FUNCTION; break;
            case 'C': m_set_index_function = CUSTOM_SET_FUNCTION; break;
            case 'L': m_set_index_function = LINEAR_SET_FUNCTION; break;
            default: exit_parse_error();
        }
```


#### 5.1、 L1I的配置


#### 5.2、 L1T的配置


#### 5.3、 L1C的配置


#### 5.4、 L1D的配置

#### 5.5、 配置纹理cache的line大小

#### 5.6、 配置常量cache的line大小

## （二） 初始化主存（显存）的配置

内存延迟表示系统进入数据存取操作就绪状态前等待内存响应的时间，它通常用4个连着的阿拉伯数字来表示，例如 “3-4-4-8” ，一般而言四个数中越往后值越大，这4个数字越小，表示内存性能越好。

- 第一个数字最为重要，表示注册读取命令到第一个输出数据之间的延迟（CAS Latency），即： CL值，单位是时钟周期。这是纵向地址脉冲的反应时间。 

- 第二个数字表示内存行地址控制器预充电时间（RAS Precharge），即： tRP。指内存从结束一个行访问到重新开始的间隔时间。

- 第三个数字表示从内存行地址到列地址的延迟时间（RAS to CAS Delay），即： tRCD。

- 第四个数字表示内存行地址控制器激活时间Act-to-Precharge Precharge Delay  ， 即：tRAS



### 1、 nbk 
number of banks，bank的数量

### 2、 tCCD
Column to Column Delay (RD/WR to RD/WR different banks)
列到列的访问时延

### 3、 tRRD
Row to Row Delay (Active to Active different banks)
行到行的访问时延

### 4、 tRCD
Row to Column Delay (Active to RD/WR/RTR/WTR/LTR)
行到列的访问时延


### 5、 tRAS
Active to PRECHARGE command period

### 6、 tRP
PRECHARGE command period

### 7、 tRC
Active to Active command period (same bank)

### 8、 CL
CAS Latency，是指内存纵向地址脉冲的反应时间，是在一定频率下衡量不同规范内存的重要标志之一。对于PC1600和PC2100的内存来说，其规定的CL应该为2，即他读取数据的延迟时间是两个时钟周期。

### 9、 WL
WRITE latency

### 10、 tCDLR
Last data-in to Read Delay (switching from write to read)

### 11、 tWR
WRITE recovery time

### 12、nbkgrp
number of bank groups
bank组的数量

### 13、tCCDL
column to column delay between accesses to different bank groups
在不同的bank组之间的列到列的时延

### 14、tRTPL
read to precharge delay between accesses to different bank groups


### 15、 tRCDWR


### 16、 tRTW


### 17、 tWTR


### 18、 tWTP


### 19、 dram_atom_size


## （三） 初始化时钟的配置


### 1、 设置core的频率



### 2、 设置icnt的频率


### 3、 设置L2的频率


### 4、 设置DRAM的频率


### 5、 根据频率计算：时钟周期

时钟周期  ==  频率的倒数



## （四） 初始化电源配置





## （五） 初始化跟踪器

在trace文件里面，将对应的使能位置位true


# 二、 GPGPU-SIM模拟器


## （一） 初始化





## （二） 运行函数（cycle）

首先介绍一下，4个时钟域：

（1）core的时钟，即：SM    

（2）DRAM的时钟，即：主存

（3）L2 cache的时钟，L2 cache在DRAM里面，类似于缓存区一样的功能，解决icnt与DRAM之间速度不匹配的问题

（4）icnt的时钟，即：互联网络

整体图：

![](_v_images/_1572395378_1046.png)

### 1、 gpgpu_sim::cycle()






#### 1.1、 寻找下一个时钟

通过计算上述4个时钟域的最小值，确定本轮循环中，工作的是哪些部件（core、ICNT、DRAM、L2）


#### 1.2、运行core部分

##### 1.2.1、微体系结构

![](_v_images/_1573450806_16776.png)



##### 1.2.2、代码流程

（2）从icnt的ejection buffer中取出指令，放入到cluster的FIFO队列

（1）根据mf类型，将ICNT中FIFO的指令放入  LDST  或者  指令cache

#### 1.3、运行ICNT部分

##### 1.3.1、微体系结构

ICNT 与 L2 cache之间的交互

![](_v_images/_1573087770_2078.png)


##### 1.3.2、代码流程

将主存中的指令（包含所需数据） ->  ICNT的缓冲区



#### 1.4、运行DRAM部分

##### 1.4.1、微体系结构

DRAM与L2 cache之间的数据交互

![](_v_images/_1573042254_4079.png)

##### 1.4.2、代码流程

（1） DRAM的操作：主要是DRAM与L2之间的数据交互

（2） L2 cache将来自ICNT的请求传到DRAM，DRAM完成请求后将数据传给L2 cache



#### 1.5、运行L2 cache（内存子分区）部分

##### 1.5.1、微体系结构
L2 cache 与 DRAM之间的交互


###### （1）内存子分区的运行函数（cycle）
![](_v_images/_1573087801_10874.png)

###### （2）L2 cache的运行函数（cycle）

从**未命中队列（L2 cache bank）**中取出请求放入**内存端口（L2->DRAM队列）**



##### 1.5.2、代码流程



#### 1.6、运行ICNT部分

暂时没有看懂


#### 1.7、运行CORE部分

##### 1.7.0、基础知识

因为这部分涉及一些指令和流水线的相关知识，在此补充一下

###### 1.7.0.1、指令（CPU）


**（1）指令分类**
大致分为4类：

①、运算类指令：加减乘除，逻辑运算等，比如：ADD

②、传送类指令：从存储器到通用存储器等，比如：MOV 

③、控制类指令：暂停处理器，标志位的操作等，比如：NOP
![](_v_images/_1573438280_17186.png)

④、转移类指令：无条件转移、过程调用等，比如：JMP


**（2）指令译码（X86）**

这里给出的是CPU的译码，GPU应该也是这么做的
![](_v_images/_1573438033_19314.png)


###### 1.7.0.2、流水线（CPU）

**（1）流水线冒险**
![](_v_images/_1573438726_30560.png)


**（2）结构冒险**
![](_v_images/_1573438867_29885.png)

①、指令与数据放在同一内存

②、同时读、写寄存器

**（3）数据冒险**
![](_v_images/_1573439010_13632.png)


**（4）控制冒险**

![](_v_images/_1573439068_30697.png)





##### 1.7.1、微体系结构

###### （1）GPU的SM架构
![](_v_images/_1573174946_21395.png)



###### （3）CUDA core

![](_v_images/_1573175069_22056.png)

###### （4）概览图

![](_v_images/_1573175210_7314.png)


###### （5）代码中的core（相当于SM）


##### 1.7.2、运行cluster（cluster->cycle）
依次运行cluster中的core（SM）

注意这里面的

###### （1）fetch
取指操作确定了：指令的资源、warp，但是真正有效的指令还没有放进来（万事俱备只等译码，等decode译码之后直接放入指定的buffer）

m_inst_fetch_buffer ：指令存放的buffer

###### （2）decode
一次译码1条或者2条指令

通过函数： ptx_fetch_inst。 取出译码之后的指令放入 m_inst_fetch_buffer

判断指令的操作数类型，更新统计信息

###### （3）issue
issue操作实际上是调度器操作，运行调度器： schedulers[i]->cycle()

根据指令的不同，可能会产生流水线停滞



###### （4）read_operands
无代码

###### （5）execute


###### （6）writeback



##### 1.7.3、计算流水线的占空比




##### 1.7.4、将CTA（block）发布到core



##### 1.7.5、刷新L1缓存    or    刷新L2缓存





##### 1.7.6、快照




