|
12 |
RISC-Vプロセッサの設計 (37) |
Pipeline部分のソースの部分を示します。
(* synthesize *)
module mkProcessor(Proc);
// 段間FIFO(図の段間レジスタ)
PipeQ#(PC2IF) pc2if <- mkPipeQ(PC2IF { pc: resetVector - 4, epoch: False, sn: '1 }); // PCの箱
FIFO#(IF2ID) if2id <- mkPipelineFIFO;
FIFO#(ID2EX) id2ex <- mkPipelineFIFO;
FIFO#(EX2MA) ex2ma <- mkPipelineFIFO;
FIFO#(MA2WB) ma2wb <- mkPipelineFIFO;
FIFO#(Commit) commitQ <- mkFIFO;
// 取り消し:<EX>が書き、同じサイクルの<ID>, <IF>, <PC>が後ろのポートで読む
Reg#(Bool) exEpoch[3] <- mkCReg(3, False); // [0]:EX, [1]:ID, [2]:IF
Reg#(Maybe#(Addr)) redirect[2] <- mkCReg(2, tagged Invalid); // [0]:EX, [1]:PC
IMem imem <- mkIMem("prog.mem"); // <PC>でreq、<IF>でdata
Wire#(Stall) stallW <- mkDWire(unpack(0));
Kanata log <- mkKanata("kanata.log");
// <WB>
rule wb_stage (!stallW.wb);
let x = ma2wb.first; ma2wb.deq;
commitQ.enq(Commit { pc: x.pc, inst: x.inst, wen: False, rd: 0, wdata: 0,
sten: False, saddr: 0, ssize: 0, sdata: 0 });
log.wbStage.pass(x.sn);
endrule
// <MA>
rule ma_stage (!stallW.ma);
let x = ex2ma.first; ex2ma.deq;
ma2wb.enq(MA2WB { pc: x.pc, sn: x.sn, inst: x.inst });
log.maStage.pass(x.sn);
endrule
// <EX> 分岐が成立したら exEpoch を反転し、分岐先を redirect に書く
Bool exKill = id2ex.first.epoch != exEpoch[0];
rule ex_kill (exKill);
id2ex.deq;
log.exStage.kill(id2ex.first.sn);
endrule
rule ex_stage (!exKill && !stallW.ex);
let x = id2ex.first; id2ex.deq;
let br = stubBranch(x.pc, x.inst);
if (br.taken) begin
exEpoch[0] <= !exEpoch[0];
redirect[0] <= tagged Valid br.target;
end
ex2ma.enq(EX2MA { pc: x.pc, sn: x.sn, inst: x.inst });
log.exStage.pass(x.sn);
endrule
// <ID>
Bool idKill = if2id.first.epoch != exEpoch[1];
rule id_kill (idKill);
if2id.deq;
log.idStage.kill(if2id.first.sn);
endrule
rule id_stage (!idKill && !stallW.id);
let x = if2id.first; if2id.deq;
id2ex.enq(ID2EX { pc: x.pc, epoch: x.epoch, sn: x.sn, inst: x.inst });
log.idStage.pass(x.sn);
log.decoded(x.sn, x.pc, x.inst); // 逆アセンブルをKanataの左の欄に出す
endrule
// <IF> 前のサイクルに<PC>がreqした命令をIMemから受け取る
Bool ifKill = pc2if.first.epoch != exEpoch[2];
rule if_kill (ifKill);
pc2if.deq;
log.ifStage.kill(pc2if.first.sn);
endrule
rule if_stage (!ifKill && !stallW.if_);
let x = pc2if.first; pc2if.deq;
let inst = imem.data;
if2id.enq(IF2ID { pc: x.pc, epoch: x.epoch, sn: x.sn, inst: inst });
log.ifStage.pass(x.sn);
log.fetched(x.sn, inst);
endrule
// <PC> PCの箱(last) → +4 → Mux(redirectが優先) → PCの箱、同じPCでIMemにreq
rule pc_stage (!stallW.pc);
let cur = pc2if.last;
Addr pc = cur.pc + 4;
Bool epoch = cur.epoch;
if (redirect[1] matches tagged Valid .target) begin
pc = target;
epoch = !cur.epoch;
redirect[1] <= tagged Invalid;
end
pc2if.enq(PC2IF { pc: pc, epoch: epoch, sn: cur.sn + 1 });
imem.req(pc);
log.pcStage(cur.sn + 1, pc);
endrule