目录
前言
一、Schedule端口
二、Schedule代码分析
2.1 schedule_if
2.2 decode_sched_if
2.3 issue_sched_if
2.4 commit_sched_if
2.5 warp_ctl_if
2.6 sched_csr_if
2.7 branch_ctl_if
2.8 base_dcr
2.9 gbar_bus_if
2.10 其他功能
总结
前言
本篇重点分析Vortex RISC-V内核的6级流水线之六,Schedule部分,是SIMT的核心和总控部件。
严格的说,Schedule是Fetch的上游,应该是第0级。放在最后,当作最后一级来分析,是因为在分析其他流水线的时候,就把它们跟Schedule接口信号及其功能都了解了大部分,剩下的一些边角放在最后,同时把之前分析的功能再串讲一下,整体上更容易加深理解。
本系列"探索Vortex开源GPGPU:RISC-V SIMT架构"
一、Schedule端口
基于Schedule与其他5级流水线的接口信号,从前端到后端,如下8个端口,以下表格列出了基本信息,代码分析依此顺序展开。
| Fetch schedule_if | Schedule产生uuid/wid/PC送到Fetch |
| Decode decode_sched_if | Decode通知Schedule,当前warp已经执行完毕,可以继续产生新PC |
| Issue issue_sched_if | 通知Schedule当前warp已经开始送到下一级,执行开始 |
| Commit commit_sched_if | 通知Schedule当前warp已经处理完毕,执行结束 |
| Execute warp_ctl_if | 自定义6条指令的启动/执行/结束:wspawn/tmc/split/join/barrier/pred |
| Execute sched_csr_if |
提供CSR要读的信息:激活的warp掩码,当前warp的线程掩码,warp执行完毕标志 |
| Execute branch_ctl_if | Execute ALU提供Branch相关的信号 |
| DCR base_dcr | 复位期间提供内核启动地址,内核缓冲区结构体指针 |
| gbar_bus_if | 往socket/cluster传播全局barrier信息,并反向广播全局barrier清除信号 |

// configuration input base_dcrs_t base_dcrs, //SFU CSR章节,复位时设置启动地址
// inputsdecode_if VX_warp_ctl_if.slave warp_ctl_if, //SFU wctl章节,控制自定义6条
//指令的执行
VX_branch_ctl_if.slave branch_ctl_if [`NUM_ALU_BLOCKS], //控制跳转指令执行
VX_decode_sched_if.slave decode_sched_if,
//Decode章节,通知Schedule某一个warp的PC
//已经译码,它可以开始这个warp的下一条PC
VX_issue_sched_if.slave issue_sched_if[`ISSUE_WIDTH],
//SFU章节,和commit_sched_if配合,用来统计
//同一warp的pending_size(未执行完毕标志) VX_commit_sched_if.slave commit_sched_if,
// outputs VX_schedule_if.master schedule_if, //Fetch章节,Fetch流水线的输入信号
`ifdef GBAR_ENABLE VX_gbar_bus_if.master gbar_bus_if, //SFU wctl章节,处理全局barrier, //要往socket和cluster传播,并且反
//向广播全局barrier解锁信号 `endif VX_sched_csr_if.master sched_csr_if, //SFU章节,提供CSR读数据,以及
//各个warp的完成标志(FPU CSR读写
//需要这个标志)
// status output wire busy
二、Schedule代码分析
2.1 schedule_if
schedule_if的三个功能:
- 最基本的功能是产生PC地址;
- 其次后面的icache(Fetch流水线)/dcache(Execut流水线)总线的要求是要有一个类似AXI ID的边带信号,在Vortex这边就是uuid;
- 对于SIMT处理器来说,功能上必然要支持warp ID(wid),线程掩码(tmask)
为了timing能做得更高,schedule和fetch之间加了一个full timing slice,全缓冲valid/ready/data。
同样的原因,从fetch的bus response直到decode译码结果送到Issue,全部路径都是组合逻辑,所以Issue的ibuffer也是起了一个timing slice的作用。
相关PC/uuid/wid/tmask代码如下:
VX_elastic_buffer #( .DATAW (`NUM_THREADS + PC_BITS + NW_WIDTH + UUID_WIDTH), .SIZE (2), .OUT_REG (1) ) out_buf ( //配置成full timing slice, 隔离schedule和fetch之间的valid/read/data .clk (clk), .reset (reset), .valid_in (schedule_valid), .ready_in (schedule_ready), .data_in ({schedule_tmask, schedule_pc, schedule_wid, instr_uuid}), .data_out ({schedule_if.data.tmask, schedule_if.data.PC, schedule_if.data.wid, schedule_if.data.uuid}), .valid_out (schedule_if.valid), .ready_out (schedule_if.ready) ); wire schedule_fire = schedule_valid && schedule_ready; wire schedule_if_fire = schedule_if.valid && schedule_if.ready;
if (schedule_if_fire) begin //在schedule_if_fire,warp的PC值自增
//实际上也可以用schedule_fire
//用schedule_if_fire更好一点,路径短 warp_pcs_n[schedule_if.data.wid] = schedule_if.data.PC + from_fullPC(`XLEN'(4)); end //还有3种情况改变PC:wspawn/join/branch
VX_uuid_gen #( .CORE_ID (CORE_ID) ) uuid_gen ( //产生uuid .clk (clk), .reset (reset), .incr (schedule_fire), .wid (schedule_wid), .uuid (instr_uuid) //uuid[43:0]={core_id, wid, incremental_counter[31:0]} );
if (schedule_fire) begin //送出uuid/wid/PC时,锁住当前warp
//stalled_warps是stalled_warps_n的寄存器信号 stalled_warps_n[schedule_wid] = 1; end
wire [`NUM_WARPS-1:0] ready_warps = active_warps & ~stalled_warps; VX_priority_encoder #( .N (`NUM_WARPS) ) wid_select ( //优先级编码产生warp ID(wid) .data_in (ready_warps), //active_warps是当前激活的warp掩码
//受wspaw/tmc/pred影响
//stalled_warps是当前停止的warp掩码
//指令发到Fetch,就lock住,两个条件解锁
//a) 一般指令在Decode阶段解锁
//b) 特别指令由相应的后级流水线解锁
//详细3种特例见Decode章节描述 .index_out (schedule_wid), .valid_out (schedule_valid), `UNUSED_PIN (onehot_out) );
//线程掩码tmask的逻辑比较分散,它的置位受4个指令控制
//tmc/pred/split/join, 它的清0受tmc/pred/barrier控制
//split/join还引入了ipdom_stack来处理条件分支和汇聚,参见SFU第2个章节
//总的来说,wid和tmask的执行顺序:
//Execute流水线的wctl_unit发出6种自定义指令的有效请求,送到Shedule
//Execute流水线的csr_unit发出FPU CSR解锁warp请求,送到Schedule
//详细过程,代码分析见Execute SFU的4个章节,这里略过
//Execute流水线的alu_int发出branch请求,送到Schedule
2.2 decode_sched_if
//对于一般指令,当Decode流水线数据流被Issue流水线接收时,解锁schedule的warp if (decode_sched_if.valid && decode_sched_if.unlock) begin stalled_warps_n[decode_sched_if.wid] = 0; end
2.3 issue_sched_if
2.4 commit_sched_if
issue_sched_if/commit_sched_if关联在一起,用来产生每个warp的待处理计数器
//每个warp待处理计数器
for (genvar i = 0; i < `NUM_WARPS; ++i) begin : g_pending_sizes
localparam isw = wid_to_isw(i); //issue width localparam wis = wid_to_wis(i); //warp index
VX_pending_size #( .SIZE (4096), .ALM_EMPTY (1) ) counter ( .clk (clk), .reset (reset), //Issue operands_if 输出端有效握手,计数器自增 .incr (issue_sched_if[isw].valid && (issue_sched_if[isw].wis == wis)), .decr (commit_sched_if.committed_warps[i]),
//Commit WB有效握手,延迟一拍,计数器再自增 .empty (pending_warp_empty[i]), //zero pending,warp全部处理完 .alm_empty (pending_warp_alm_empty[i]), // 1 pending, 只剩一个warp,还未传到
// Commit流水线 `UNUSED_PIN (full), `UNUSED_PIN (alm_full), `UNUSED_PIN (size) ); //counter for each WARP end
2.5 warp_ctl_if
6个自定义指令的处理通过warp_ctl_if来传递,方向是从Execute SFU的wctl_unit到Schedule。这个接口涉及了SIMT最基本也是最重要的功能:
- warp的启动和停止
- 线程的启动和停止
- 条件判断的分支聚合,原因是SIMT架构下,同一warp的线程要共享PC
- 同步功能,core内,和core之间,既本地barrier和全局barrier的设置
具体细节见SFU 4个章节, 第3点用了ipdom_stack,是一个比较巧妙的动态分组解决办法。
2.6 sched_csr_if
assign sched_csr_if.alm_empty = pending_warp_alm_empty[sched_csr_if.alm_empty_wid];
//通知csr_unit,当前的warp ID是
//流水线中同ID的最后一个待处理 //在csr_unit中处理FPU CSR读写
//时,要等待这个信号,具体原因
//不清楚,暂且记下来
assign sched_csr_if.active_warps = active_warps; //激活warp和允许线程送到csr_unit assign sched_csr_if.thread_masks = thread_masks; //CSR指令可以读取这些信息
2.7 branch_ctl_if
Execute ALU中分支相关指令的处理信息也要送到Schedule,解锁warp和跳转。
注意的是它不是放到Commit流水线来做这一步,这和Execute SFU wctl_unit类似,越早解锁warp意味着core可以处理更多事情。
- JAL(UJ类型,20位正负偏移跳转,16b对齐), JALR(I类型,11位正负偏移跳转)
- SB类型所有指令(12位正负偏移跳转,16b对齐)
- I类型中的INST_SYS,处理ECALL/EBREAK/URET/SRET/MRET
指令信息详细见Decode章节
//NUM_ALU_BLOCKS跟ISSUE_WIDTH相等,等于NUM_WARPS/16
for (integer i = 0; i < `NUM_ALU_BLOCKS; ++i) begin if (branch_valid[i]) begin if (branch_taken[i]) begin warp_pcs_n[branch_wid[i]] = branch_dest[i]; //设定warp新PC end stalled_warps_n[branch_wid[i]] = 0; //解锁warp end end
2.8 base_dcr
base_dcr功能很简单,提供第一个warp的启动地址
// activate first warp warp_pcs[0] <= from_fullPC(base_dcrs.startup_addr);
2.9 gbar_bus_if
处理全局barrier的接口,从下往上逐级传播全局同步请求,在socket/cluster内仲裁,最后由cluster的gbar_unit返回全局barrier清除信号,同时广播到各个core清除全局barrier请求,详见SFU第3章节
2.10 其他功能
- busy信号,busy信号送到Vortex的IO PIN作为观察信号。这个busy有点不合理,因为某个指令流可能在Decode之后,Issue Operands之前,而pending_size计数器在Issue Operands才开始自增计数,这样漏掉了中间的流水线逻辑,它们实际上是busy的状态。
`BUFFER_EX(busy, (active_warps != 0 || ~no_pending_instr), 1'b1, 1, 1); //有激活的warp,或者warp全部都禁掉,但是有待处理的warp还在流水线上
if (busy) begin cycles <= cycles + 1; end //忙周期统计信息
always @(posedge clk) begin //debug手段,用来动态监测是否有长时间死锁的情况 if (reset) begin timeout_ctr <= '0; timeout_enable <= 0; end else begin if (decode_sched_if.valid && decode_sched_if.unlock) begin timeout_enable <= 1; end //有激活的warp并且全部都锁住,timeout计数器自增 if (timeout_enable && active_warps !=0 && active_warps == stalled_warps) begin timeout_ctr <= timeout_ctr + 1; end else if (active_warps == 0 || active_warps != stalled_warps) begin timeout_ctr <= '0; //warp都禁掉,或者warp有解锁的情况,计数器清0 end end end
`RUNTIME_ASSERT(timeout_ctr < STALL_TIMEOUT, ("%t: *** %s timeout: stalled_warps=%b", $time, INSTANCE_ID, stalled_warps))
Schedule章节到此结束,剩下Execute ALU/LSU/FPU/TCU没有时间分析,留待以后。接下来要开始OpenCL这一部分的内容,可能要多花一点时间,希望理清的时间越快越好:)
总结
本文详细分析了Vortex RISC-V内核6级流水线中的Schedule模块。作为Fetch的上游模块,Schedule负责产生PC地址、uuid和warp ID,并通过8个端口与其他流水线交互。重点包括:1) schedule_if接口生成PC/uuid/wid/tmask;2) decode_sched_if/issue_sched_if/commit_sched_if实现warp状态管理;3) warp_ctl_if处理6个自定义指令;4) branch_ctl_if处理分支跳转。此外还介绍了全局barrier同步(base_dcr/gbar_bus_if)和CSR接口(sched_csr_if)等功能。Schedule模块通过优先级编码选择就绪warp,使用pending计数器跟踪warp执行状态,总控SIMT的执行和多核间同步,是SIMT架构实现线程调度的核心部件。



