Life is all like this, meaningless, hopeless and
Inst Buffer Structure
Inst buffer包含32个entries,每个entry的structure如下所示:
1 | //========================================================== |
Inst Buffer 关键逻辑
空满逻辑
ibuf的空满逻辑如下,当ibuf满时,需要将ibctrl进行stall
- Full:当ibuf中不能再保存9条指令时,为满(指针左移8位,并与vld信号比较,如果发现移动后的指针所在位置已经vld,说明ibuf full)
- Empty:当创建的指令数和退休的指令数相同,且entry invalid,则为空
1 | assign ibuf_full = |({ibuf_create_pointer[ENTRY_NUM-9:0], |
Empty的判断条件相对比较绕
Time | ||
---|---|---|
$T$ | ||
$2T$ | ||
$3T$ | ||
$4T$ |
Create Logic
在ibuf
的create逻辑中,我们重点需要解决两个问题:
- 在
ibuf
什么位置保存指令(pointer logic) - 指令是否需要保存在
ibuf
中(vld logic)
Create pointer logic
对于一个FIFO,我们会有入队列和出队列两种逻辑,而在ibuf中,入队列的逻辑是由create pointer logic进行控制的,使用了一个变量ibuf_create_pointer
作为FIFO的指针。当CPU进行rst之后,ibuf_create_pointer
的值为0x00000001。更新create pointer时,会根据半字的数量,预先生成create pointer
1 | always @( ibuf_create_pointer[31:0] |
由于最多有9条指令需要被保存在ibuf中,因此我们会创建9个pointer(通过左移指向待保存的位置)。这个半字的数量是由ibdp_ibuf_half_vld_num
决定的,根据block中是否存在条件分支或转移语句,决定提供给ib
阶段的半字有多少个:
1 | assign ibdp_half_vld_num[3:0] = (ipdp_ibdp_con_br_num_vld) |
所以,此处createpointer_pre表示ibuf需要预先创建多少个指针用来保存需要存在ibuf
中的指令数目$N{max}$,但是实际保存的指令数目$N<N_{max}$。那么,实际需要保存至ibuf
中的指令数目该如何计算呢,设ibuf
中当前保存的指令数目为$N_0$,那么理论上需要保存的有效指令数最大为$n_v$,则ibuf
中预计需要保存到指令数为:
$N_{max}$表示无bypass的情况。
1 | always @( ibuf_create_num[4:0] |
而实际保存的指令需要减掉bypass的指令数$n_b$,故最终保存在ibuf中有效的指令数目为:
而$n_b$在处理bypass的情况时进行计算
1 | always @( ibuf_create_num[4:0] |
实际的指针如下:
1 | assign ibuf_create_pointer0[ENTRY_NUM-1:0] = ibuf_create_pointer[ENTRY_NUM-1:0]; |
Create Num Logic
在对FIFO进行入队列操作时,我们还需要对进入FIFO的指令数量进行统计,这个参数会被用于判断bypass是否有效,以及ibuf
是否为空,计算方法如下:
其中,$V$是用于保存至ibuf中的有效半字个数
Merge 情况处理
当ibuf中存在指令,但是指令不足以凑成完整的三条送入idu时,需要和ip送来的指令进行Merge
Merge 指令数量计算
我们需要根据ibuf中能pop出的指令数目,来确认需要bypass提供的merge指令数目
其中,$N_m$为bypass提供的与pop的指令进行merge的指令(32位)
1 | casez({ibuf_pop_inst1_valid,bypass_way_inst0_32_start,bypass_way_inst1_valid,bypass_way_inst1_32_start}) |
当发生merge时
1 | // &CombBeg; @315 |
Merge retire指针计算
1 | casez({ibuf_pop_inst1_valid,bypass_way_inst0_32_start,bypass_way_inst1_valid,bypass_way_inst1_32_start}) |
Create Valid Logic
在前面的小节中我们讨论了当向ibuf
中保存指令时Create指针移动的逻辑,本节讨论哪些指令需要保存在ibuf
中或从ibuf
中退休,即entry create & retire vld的逻辑。在Xuantie的代码中,使用了entry_create_*
和entry_retire_*
两个32位的变量,与ibuf
的32个entry一一对应,用于表示哪一个entry需要创建或退休。本文首先讨论entry create的逻辑。
在entry create的逻辑中,Xuantie的代码分为了nopass和bypass两种类型进行讨论,实际nopass还可以分为merge和pop两种情况。对于nopass的情况,我们依据三个条件确定需要create的entry:
ibuf
create指针所在位置,即create_pointer0-9- 传递给
ibuf
的指令的有效性,即ib_hn_create_vld[8:0] - merge指令的掩码(当merge无效时为全1,此时无merge指令,否则根据merge指令数量及指令是否为32bit,确定掩码)
对于bypass的情况,则根据ibuf
create指针所在位置以及bypass的指令数量及类型,确定create的entry。
1 | default : begin //1?1?1 |
得到32位create vld bits后,会将每一位赋给entry,作为vld信号。注意,vld信号仅控制control signal的更新,保存在entry中的数据不受vld信号控制。control signal包括:
- entry是否vld
- entry中的指令是否为32位指令前半部分
Create Input Data Generate
在得到ibuf
的entry create vld信号后,还需要组织向ibuf
中保存的指令的信息,这一部分逻辑较为简单,从3667行到5520行为该段逻辑。
retire logic
1 | always @( ibuf_merge_retire_pointer[31:0] |
Retire Valid Logic
Retire Valid逻辑相比Create来说较为简单,首先由retire_pointer*
确定可能retire的entry,然后和retire_vld_*
进行mask,而retire_vld_*
根据实际从ibuf
中pop的指令数量确定
1 | casez({pop_h0_32_start,pop_h1_32_start,pop_h2_32_start, |
Bypass 逻辑
何时进行bypass
在ibuf为空时,将会对指令进行bypass,最多bypass3条指令,其余的将会被保存在ibuf中。
1 | assign bypass_vld = (ibuf_create_num[4:0] == |
Inst attr
在IBUF的bypass逻辑中,最多可以输出三条指令(16bits或32bits)。首先,ibuf以16bits为单位,对h0到h5的属性进行了判断
1 | assign bypass_way_h0_vld = (ibdp_ibuf_h0_vld) |
Inst Gen
指令生成逻辑。在bypass valid的情况下,ibuf最多会提供三条指令(16或32位),此时的逻辑如下:
ibuf中会根据bypass_way_hn_32_start
的标志位来进行指令的生成(8570-9383行),同时记录bypass了几条half的指令
1 | casez({bypass_way_h0_32_start,bypass_way_h1_32_start,bypass_way_h2_32_start, |
指令Merge逻辑
在ibuf
中,需要根据ibuf中所保存的指令,将Bypass和ibuf中获得的指令进行Merge,并得到最终送给decoder的指令。其代码片段如下,此处容易迷惑的点是这里虽然叫merge_way_inst0
,但是实际上指的是用于指令merge的bypass的指令。当需要从ibuf中取出指令时,bypass最多提供两条指令进行merge,所以此处我们只需要考虑inst0
和inst1
。
1 | assign merge_way_inst0_sel = !ibuf_pop_inst1_valid; |
我们考虑如下几种情况:
- 情况一:ibuf中的3条指令都有效
此时直接使用ibuf中pop出来的指令,无需进行merge
- 情况二:ibuf中
inst1
或inst2
无效,此时分为如下几种情况讨论:inst1
无效inst2
无效inst1
和inst2
均无效
对于第一种子情况,得到的结果如下:
1 | merge_way_inst0 = bypass_way_inst0_data; |
对于第二种子情况,得到的结果如下:
1 | merge_way_inst0 // 不会被选择 |
对于第三种子情况,得到的结果如下:
1 | merge_way_inst0 = bypass_way_inst0_data; |
最终,从ibuf
提供给ibdp
的三条指令如下:
1 | assign ibuf_ibdp_inst0[31:0] = ibuf_pop_inst0_data[31:0]; |
这三条指令已经包含了merge bypass的过程,因此可以在ib阶段直接进行mux,选择需要送给decoder的三条指令即可:
1 | case({bypass_inst_vld,ibuf_inst_vld,lbuf_inst_vld}) |
Design: 4 normal inst bypass
为了增加ibuf的吞吐量,我们为ibuf
设计了一个新的bypass路径,当fetch的指令为4条normal类型的指令,且ibuf
为空的情况下,我们会将这四条normal指令直接送至IDU进行decoder处理,此时control signal新增一条
1 | assign bypass_all_inst_vld = ibctrl_ibdp_bypass_all_inst_vld; |
问题
- 在Bypass逻辑的判断过程中,为何是h0-h4而不是h0-h5?
因为根据h0-h4即可知道全部bypass inst的信息,首先如果h4是一条16bits的语句,那么bypass的指令必然没有h5;如果h4是32位指令,那么{h4, h5}可以拼成一条32位指令,根据h4即可获得h5的信息
- 在Merge的逻辑判断过程中,为何没有考虑ibuf的inst0无效的逻辑?
1 | assign ibuf_ibdp_inst0[31:0] = ibuf_pop_inst0_data[31:0]; |