Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
92595157eThis file is pinned to the selected release. Download the original to open it in your design tool.
// Copyright 2019- RSD contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
//
// Active list
//
`include "BasicMacros.sv"
import BasicTypes::*;
import MemoryMapTypes::*;
import MicroOpTypes::*;
import RenameLogicTypes::*;
import SchedulerTypes::*;
import ActiveListIndexTypes::*;
import LoadStoreUnitTypes::*;
import PipelineTypes::*;
import DebugTypes::*;
import OpFormatTypes::*;
//
// Current implementation is a FIFO with:
// - 1 input to tail for dispatch.
// - 1 output from head for commit/recovery.
// - 1 input to random entries for execution.
//
module ActiveList(
ActiveListIF.ActiveList port,
RecoveryManagerIF.ActiveList recovery,
ControllerIF.ActiveList ctrl,
DebugIF.ActiveList debug
);
//
// --- Pointers
//
ActiveListIndexPath headPtr/*verilator public*/; // Set it public from a test bench for verilator.
ActiveListIndexPath headPtrList[COMMIT_WIDTH];
ActiveListIndexPath tailPtr; // 次に割り当てを行うエントリを指す
ActiveListIndexPath tailPtrList[COMMIT_WIDTH];
ActiveListIndexPath readPtrList[COMMIT_WIDTH];
ActiveListIndexPath pushedTailPtr [RENAME_WIDTH];
ActiveListCountPath count;
RenameLaneCountPath pushNum;
// Parameter: Size, Initial head pos., Initial tail pos., Initial count
BiTailMultiWidthQueuePointer #(ACTIVE_LIST_ENTRY_NUM, 0, 0, 0, RENAME_WIDTH, COMMIT_WIDTH)
activeListPointer(
.clk(port.clk),
.rst(port.rst), // On flush, pointers are recovered by the store committer.
.popHead(port.popHeadNum > 0),
.popHeadCount(port.popHeadNum),
.pushTail(pushNum > 0),
.pushTailCount(pushNum),
.popTail( port.popTailNum > 0 ),
.popTailCount( port.popTailNum ),
.count(count),
.headPtr(headPtr),
.tailPtr(tailPtr)
);
always_comb begin
// In the active list, recovery and allocation can be simultaneously carried out,
// thus allocation is decided by its current count without its phase.
port.allocatable = (count <= ACTIVE_LIST_ENTRY_NUM - RENAME_WIDTH) ? TRUE : FALSE;
port.validEntryNum = count;
pushNum = 0;
for (int i = 0; i < RENAME_WIDTH; i++) begin
if ((tailPtr + pushNum) >= ACTIVE_LIST_ENTRY_NUM) begin
pushedTailPtr[i] = tailPtr + pushNum - ACTIVE_LIST_ENTRY_NUM;
end
else begin
pushedTailPtr[i] = tailPtr + pushNum;
end
pushNum += (port.pushTail[i] ? 1 : 0);
end
for (int i = 0; i < COMMIT_WIDTH; i++) begin
if ((headPtr + i) < ACTIVE_LIST_ENTRY_NUM) begin
headPtrList[i] = headPtr + i;
end
else begin
headPtrList[i] = headPtr + i - ACTIVE_LIST_ENTRY_NUM;
end
end
for (int i = 0; i < COMMIT_WIDTH; i++) begin //Actually limited to RENAME_WIDTH
if (( tailPtr - 1 - i) >= ACTIVE_LIST_ENTRY_NUM) begin
tailPtrList[i] = tailPtr - 1 - i + ACTIVE_LIST_ENTRY_NUM;
end
else begin
tailPtrList[i] = tailPtr - 1 - i;
end
end
if( RECOVERY_FROM_ACTIVE_LIST && ( port.popTailNum > 0 ) ) begin
readPtrList = tailPtrList;
end else begin
readPtrList = headPtrList;
end
port.detectedFlushRangeTailPtr = tailPtr + pushNum;
port.pushedTailPtr = pushedTailPtr;
//port.detectedFlushRangeHeadPtr = headPtr + port.popHeadNum;
ctrl.activeListEmpty = count == 0;
`ifndef RSD_DISABLE_DEBUG_REGISTER
// Debug Register
debug.activeListHeadPtr = headPtr;
debug.activeListCount = count;
`endif
end
//
// --- Active List
//
logic pushTail [RENAME_WIDTH];
ActiveListEntry pushedTailData [RENAME_WIDTH];
ActiveListEntry readData[COMMIT_WIDTH];
//DistributedMultiPortRAM #(
DistributedMultiBankRAM #(
.ENTRY_NUM( ACTIVE_LIST_ENTRY_NUM ),
.ENTRY_BIT_SIZE( $bits( ActiveListEntry ) ),
.READ_NUM( COMMIT_WIDTH ),
.WRITE_NUM( RENAME_WIDTH )
) activeList (
.clk( port.clk ),
.we( pushTail ),
.wa( pushedTailPtr ),
.wv( pushedTailData ),
.ra( readPtrList ),
.rv( readData )
);
always_comb begin
pushTail = port.pushTail;
pushedTailData = port.pushedTailData;
port.readData = readData;
end
//
// --- Execution result write
//
logic we[ISSUE_WIDTH];
ActiveListWriteData writeData[ISSUE_WIDTH];
always_comb begin
for (int i = 0; i < INT_ISSUE_WIDTH; i++) begin
we[i] = port.intWrite[i];
writeData[i] = port.intWriteData[i];
end
`ifndef RSD_MARCH_UNIFIED_MULDIV_MEM_PIPE
for (int i = 0; i < COMPLEX_ISSUE_WIDTH; i++) begin
we[(i+INT_ISSUE_WIDTH)] = port.complexWrite[i];
writeData[(i+INT_ISSUE_WIDTH)] = port.complexWriteData[i];
end
`endif
for (int i = 0; i < MEM_ISSUE_WIDTH; i++ ) begin
we[(i+INT_ISSUE_WIDTH+COMPLEX_ISSUE_WIDTH)] = port.memWrite[i];
writeData[(i+INT_ISSUE_WIDTH+COMPLEX_ISSUE_WIDTH)] = port.memWriteData[i];
end
`ifdef RSD_MARCH_FP_PIPE
for (int i = 0; i < FP_ISSUE_WIDTH; i++) begin
we[(i+INT_ISSUE_WIDTH+COMPLEX_ISSUE_WIDTH+MEM_ISSUE_WIDTH)] = port.fpWrite[i];
writeData[(i+INT_ISSUE_WIDTH+COMPLEX_ISSUE_WIDTH+MEM_ISSUE_WIDTH)] = port.fpWriteData[i];
end
`endif
end
// Recovery register.
// Record the oldest recovery point of an op that occurs misprediction.
typedef struct packed
{
PC_Path pc;
AddrPath faultingDataAddr;
ActiveListIndexPath ptr;
LoadQueueIndexPath loadQueuePtr;
StoreQueueIndexPath storeQueuePtr;
logic valid;
ExecutionState state;
} RecoveryRegPath;
ActiveListCountPath oldestAge;
ActiveListCountPath writeAge[ISSUE_WIDTH];
logic exceptionDetected;
RefetchType refetchType;
IssueLaneIndexPath exceptionIndex;
// Some exceptions (e.g. EXEC_STATE_FAULT_LOAD_VIOLATION) must be handled at commit time
// because of handling CSRs correctly.
logic startRecoveryAtCommit;
RecoveryRegPath recoveryReg;
RecoveryRegPath nextRecoveryReg;
ActiveListCountPath recoveryEntryNum, nextRecoveryEntryNum;
ActiveListIndexPath flushRangeHeadPtr, flushRangeTailPtr;
always_ff@(posedge port.clk) begin
if (port.rst ||
((recovery.toRecoveryPhase && !exceptionDetected) && !startRecoveryAtCommit)) begin
// Reset recoveryReg
// But, to handle exceptions correctly, do not reset the recoveryReg in the following cases:
// 1) another exception occurs when starting recovery
// 2) need to start recovery at commit time
recoveryReg <= '0;
end
else begin
recoveryReg <= nextRecoveryReg;
end
end
// One cycle delay for RenameLogicCommitter.
always_ff@(posedge port.clk) begin
if(port.rst)
recoveryEntryNum <= '0;
else if(recovery.toRecoveryPhase) begin
recoveryEntryNum <= nextRecoveryEntryNum;
end
end
always_comb begin
nextRecoveryReg = recoveryReg;
oldestAge = ActiveListPtrToAge(recoveryReg.ptr, headPtr);
exceptionIndex = '0;
exceptionDetected = FALSE;
refetchType = REFETCH_TYPE_THIS_PC;
startRecoveryAtCommit = FALSE;
for (int i = 0; i < ISSUE_WIDTH; i++) begin
writeAge[i] = ActiveListPtrToAge(writeData[i].ptr, headPtr);
if (we[i] &&
!(writeData[i].state inside {EXEC_STATE_NOT_FINISHED, EXEC_STATE_SUCCESS})
) begin
// Record the oldest recovery point.
if (!nextRecoveryReg.valid || writeAge[i] < oldestAge) begin
oldestAge = writeAge[i];
nextRecoveryReg.valid = TRUE;
nextRecoveryReg.ptr = writeData[i].ptr;
nextRecoveryReg.loadQueuePtr = writeData[i].loadQueuePtr;
nextRecoveryReg.storeQueuePtr = writeData[i].storeQueuePtr;
nextRecoveryReg.pc = writeData[i].pc;
nextRecoveryReg.faultingDataAddr = writeData[i].dataAddr;
nextRecoveryReg.state = writeData[i].state;
// Rwステージで例外が検出された時点でリカバリを開始する(詳細はRecoveryManager)
// EXEC_STATE_TRAP や例外は必ずコミット時に処理する(CSR の操作が伴うため)
exceptionDetected = writeData[i].state inside {
EXEC_STATE_REFETCH_NEXT, EXEC_STATE_REFETCH_THIS, EXEC_STATE_REFETCH_THIS_SERIALIZED
};
exceptionIndex = i;
if (!(writeData[i].state inside {EXEC_STATE_REFETCH_NEXT, EXEC_STATE_REFETCH_THIS, EXEC_STATE_REFETCH_THIS_SERIALIZED})) begin
// Need to update the recovery register when handling exceptions at commit time
startRecoveryAtCommit = TRUE;
end
// refetchTypeはflushOpのheadPtrを決めるためとrecoveredPCを確定するために使う
// Int命令の例外は分岐命令なので分岐ターゲットに飛ぶ
// Mem命令のREFETCH_NEXTは順序違反検出なので次のPCに飛ぶ
if(writeData[i].state == EXEC_STATE_REFETCH_NEXT) begin
refetchType = (
writeData[i].isBranch ? REFETCH_TYPE_BRANCH_TARGET :
writeData[i].isStore ? REFETCH_TYPE_STORE_NEXT_PC :
REFETCH_TYPE_NEXT_PC
);
end
else if (writeData[i].state == EXEC_STATE_REFETCH_THIS_SERIALIZED) begin
refetchType = REFETCH_TYPE_THIS_PC_SERIALIZED;
end
else begin
refetchType = REFETCH_TYPE_THIS_PC;
end
end
end
if (exceptionDetected && !recovery.unableToStartRecovery) begin
nextRecoveryReg.state = EXEC_STATE_SUCCESS;
end
end
//PCのリカバリに用いる
//これらのPCはRecoveryManagerのRecoveryRegisterを経由してFetchStageに送られる
recovery.recoveredPC_FromCommitStage = ToAddrFromPC(recoveryReg.pc);
recovery.recoveredPC_FromRwStage = ToAddrFromPC(nextRecoveryReg.pc);
// Fault handling
recovery.faultingDataAddr = recoveryReg.faultingDataAddr;
//LSQのリカバリに用いる
port.loadQueueRecoveryTailPtr = recoveryReg.loadQueuePtr;
port.storeQueueRecoveryTailPtr = recoveryReg.storeQueuePtr;
//リカバリをしなければならない命令はアクティブリストのtailからリカバリを起こした命令(またはその命令の後ろ)までのエントリにあたる
flushRangeHeadPtr = recovery.flushRangeHeadPtr;
flushRangeTailPtr = recovery.flushRangeTailPtr;
// Calculate # of flush insts
if (flushRangeHeadPtr == flushRangeTailPtr
&& count == ACTIVE_LIST_ENTRY_NUM) begin
// Flush all insts in ActiveList
// Because head and tail pointers match when ActiveList is full or empty,
// flushAllInsns is needed to distinguish between them
nextRecoveryEntryNum = ACTIVE_LIST_ENTRY_NUM;
recovery.flushAllInsns = TRUE;
end
else begin
nextRecoveryEntryNum = (flushRangeTailPtr >= flushRangeHeadPtr) ?
flushRangeTailPtr - flushRangeHeadPtr : ACTIVE_LIST_ENTRY_NUM + flushRangeTailPtr - flushRangeHeadPtr;
recovery.flushAllInsns = FALSE;
end
// RenameLogicCommitterにtoRecoveryPhase信号が
// 届いた次のサイクルに信号を送る必要があるので,1サイクルの遅延を入れている
port.recoveryEntryNum = recoveryEntryNum;
// RecoveryManagerに例外が検出されたことを知らせる信号
// 先行してRwステージリカバリを行っていた場合は, コミット時まで待つ
recovery.exceptionDetectedInRwStage = exceptionDetected && !recovery.unableToStartRecovery;
recovery.refetchTypeFromRwStage = refetchType;
// flushRangeHeadPtrを保存するために用いる
port.exceptionOpPtr = nextRecoveryReg.ptr;
end
//
// --- ExecutionState
//
parameter EXEC_STATE_WRITE_NUM = RENAME_WIDTH + ISSUE_WIDTH;
logic esWE[EXEC_STATE_WRITE_NUM];
ActiveListIndexPath esWA[EXEC_STATE_WRITE_NUM];
ActiveListIndexPath esRA[COMMIT_WIDTH];
logic esWV[EXEC_STATE_WRITE_NUM];
logic esRV[COMMIT_WIDTH];
ExecutionState headExecState[COMMIT_WIDTH];
DistributedMultiPortRAM #(
.ENTRY_NUM(ACTIVE_LIST_ENTRY_NUM),
.ENTRY_BIT_SIZE($bits(logic)),
.READ_NUM(COMMIT_WIDTH),
.WRITE_NUM(EXEC_STATE_WRITE_NUM)
) execState (
.clk(port.clk),
.we(esWE),
.wa(esWA),
.wv(esWV),
.ra(esRA),
.rv(esRV)
);
always_comb begin
if (port.rst) begin
// Head entries are initialized.
for (int i = 0; i < EXEC_STATE_WRITE_NUM; i++) begin
esWE[i] = FALSE;
esWA[i] = i;
esWV[i] = FALSE;
end
end
else begin
for (int i = 0; i < RENAME_WIDTH; i++) begin
esWE[i] = port.pushTail[i];
esWA[i] = pushedTailPtr[i];
esWV[i] = FALSE;
end
for (int i = 0; i < ISSUE_WIDTH; i++ ) begin
esWE[(i+RENAME_WIDTH)] = we[i];
esWA[(i+RENAME_WIDTH)] = writeData[i].ptr;
//Rwステージで検出された例外はコミットステージで再度検出されないようにExecStateをEXEC_STATE_SUCCESSにしておく必要がある
//exceptionIndexは例外を起こした命令の場所を示す
//esWV[(i+RENAME_WIDTH)] = ( exceptionDetected && !recovery.unableToStartRecovery && (exceptionIndex == i) ) ? TRUE : writeData[i].state;
esWV[(i+RENAME_WIDTH)] = (writeData[i].state != EXEC_STATE_NOT_FINISHED);
end
end
esRA = headPtrList;
for (int i = 0; i < COMMIT_WIDTH; i++) begin
if (recoveryReg.valid && (esRA[i] == recoveryReg.ptr)) begin
headExecState[i] = recoveryReg.state;
end
else begin
if (esRV[i]) begin
headExecState[i] = EXEC_STATE_SUCCESS;
end
else begin
headExecState[i] = EXEC_STATE_NOT_FINISHED;
end
end
end
port.headExecState = headExecState;
end
`ifdef RSD_MARCH_FP_PIPE
//
// --- FFLAGS state
//
parameter FFLAGS_STATE_WRITE_NUM = RENAME_WIDTH + FP_ISSUE_WIDTH;
logic ffsWE[FFLAGS_STATE_WRITE_NUM];
ActiveListIndexPath ffsWA[FFLAGS_STATE_WRITE_NUM];
ActiveListIndexPath ffsRA[COMMIT_WIDTH];
FFlags_Path ffsWV[FFLAGS_STATE_WRITE_NUM];
FFlags_Path ffsRV[COMMIT_WIDTH];
DistributedMultiPortRAM #(
.ENTRY_NUM(ACTIVE_LIST_ENTRY_NUM),
.ENTRY_BIT_SIZE($bits(FFlags_Path)),
.READ_NUM(COMMIT_WIDTH),
.WRITE_NUM(FFLAGS_STATE_WRITE_NUM)
) fflagsState (
.clk(port.clk),
.we(ffsWE),
.wa(ffsWA),
.wv(ffsWV),
.ra(ffsRA),
.rv(ffsRV)
);
always_comb begin
if (port.rst) begin
// Head entries are initialized.
for (int i = 0; i < FFLAGS_STATE_WRITE_NUM; i++) begin
ffsWE[i] = FALSE;
ffsWA[i] = i;
ffsWV[i] = '0;
end
end
else begin
for (int i = 0; i < RENAME_WIDTH; i++) begin
ffsWE[i] = port.pushTail[i];
ffsWA[i] = pushedTailPtr[i];
ffsWV[i] = '0;
end
for (int i = 0; i < FP_ISSUE_WIDTH; i++ ) begin
ffsWE[(i+RENAME_WIDTH)] = we[(i+INT_ISSUE_WIDTH+COMPLEX_ISSUE_WIDTH+MEM_ISSUE_WIDTH)];
ffsWA[(i+RENAME_WIDTH)] = writeData[(i+INT_ISSUE_WIDTH+COMPLEX_ISSUE_WIDTH+MEM_ISSUE_WIDTH)].ptr;
ffsWV[(i+RENAME_WIDTH)] = port.fpFFlagsData[i];
end
end
ffsRA = headPtrList;
port.fflagsData = ffsRV;
end
`endif
// Verify whether the ExecState is correct
`ifndef RSD_SYNTHESIS
logic esRefWE[EXEC_STATE_WRITE_NUM];
ActiveListIndexPath esRefWA[EXEC_STATE_WRITE_NUM];
ActiveListIndexPath esRefRA[COMMIT_WIDTH];
logic [EXEC_STATE_BIT_WIDTH-1:0] esRefWV[EXEC_STATE_WRITE_NUM];
logic [EXEC_STATE_BIT_WIDTH-1:0] esRefRV[COMMIT_WIDTH];
logic execStateIsDifferentFromRef;
// Whether in recovery
logic regInRecovery, nextInRecovery;
always_ff@(posedge port.clk) begin
if (port.rst) begin
regInRecovery <= FALSE;
end
else begin
regInRecovery <= nextInRecovery;
end
end
// Record ExecState of all instructions in ActiveList
DistributedMultiPortRAM #(
.ENTRY_NUM(ACTIVE_LIST_ENTRY_NUM),
.ENTRY_BIT_SIZE($bits(ExecutionState)),
.READ_NUM(COMMIT_WIDTH),
.WRITE_NUM(EXEC_STATE_WRITE_NUM)
) execStateRef (
.clk(port.clk),
.we(esRefWE),
.wa(esRefWA),
.wv(esRefWV),
.ra(esRefRA),
.rv(esRefRV)
);
ExecutionState headExecStateRef[COMMIT_WIDTH];
always_comb begin
esRefRA = headPtrList;
if (port.rst) begin
// Head entries are initialized.
for (int i = 0; i < EXEC_STATE_WRITE_NUM; i++) begin
esRefWE[i] = FALSE;
esRefWA[i] = i;
esRefWV[i] = EXEC_STATE_NOT_FINISHED;
end
end
else begin
for (int i = 0; i < RENAME_WIDTH; i++) begin
esRefWE[i] = port.pushTail[i];
esRefWA[i] = pushedTailPtr[i];
esRefWV[i] = EXEC_STATE_NOT_FINISHED;
end
for (int i = 0; i < ISSUE_WIDTH; i++ ) begin
esRefWE[(i+RENAME_WIDTH)] = we[i];
esRefWA[(i+RENAME_WIDTH)] = writeData[i].ptr;
//Rwステージで検出された例外はコミットステージで再度検出されないようにExecStateをEXEC_STATE_SUCCESSにしておく必要がある
//exceptionIndexは例外を起こした命令の場所を示す
esRefWV[(i+RENAME_WIDTH)] = ( exceptionDetected && !recovery.unableToStartRecovery && (exceptionIndex == i) ) ? EXEC_STATE_SUCCESS : writeData[i].state;
end
end
nextInRecovery = regInRecovery;
if (recovery.toRecoveryPhase) begin
nextInRecovery = TRUE;
end
if (recovery.toCommitPhase) begin
nextInRecovery = FALSE;
end
execStateIsDifferentFromRef = FALSE;
for (int i = 0; i < COMMIT_WIDTH; i++) begin
headExecStateRef[i] = ExecutionState'(esRefRV[i]);
// Compare ExecState to reference value
if (esRV[i] && (i < count) && !nextInRecovery) begin
execStateIsDifferentFromRef |=
(headExecState[i] != headExecStateRef[i]);
end
// Do not check instructions after the exception
if (esRA[i] == recoveryReg.ptr) begin
break;
end
end
end
`RSD_ASSERT_CLK(
port.clk,
!execStateIsDifferentFromRef,
"Execution State value is different from the reference's value."
);
`endif
endmodule : ActiveList