コーディング
- 1つのモジュールで完結するように記述します。
- iを接頭辞にするものが通常アクセスのインターフェイスに関係する信号(マスターインターフェイス)、aを接頭辞にするものがキャッシュタグ操作のインターフェイスに関係する信号です。
- SRAMは外部に置くため、2ポートSRAM用の信号を用意します。
- 外部からの同期リセットでキャッシュの初期化を行うことから、回路増加に繋がる非同期リセットは含めません。
- SRAMモデルの2ポートSRAMも参考にして下さい。ただし、実際はLSIやFPGAのライブラリに置き換えます。シミュレーションはこのまま実行します。
コード(RTL)
/* **************************** MODULE PREAMBLE ********************************
Copyright (c) 2011, ArchiTek
This document constitutes confidential and proprietary information
of ArchiTek. All rights reserved.
*/
// ***************************** MODULE HEADER *********************************
module cache (
iReq, iGnt, iAddr,
iRdStrb, iRdAck, iRdData,
oRdReq, oRdGnt, oRdAddr,
oRdStrb, oRdAck, oRdData,
aOpReq, aOpGnt,
dramWE, dramWA, dramWD, dramRE, dramRA, dramRD,
reset, clk
);
// ************************ PARAMETER DECLARATIONS *****************************
// LLRにはキャッシュライン数を、BLRにはバースト長を設定する
parameter LLR = 2, // Cache Line Length Radix
BLR = 2; // Burst Length Radix
parameter LBR = LLR+BLR;
parameter LL = 1<<LLR;
parameter BL = 1<<BLR;
// *************************** I/O DECLARATIONS ********************************
// Master Address Interface
// マスターインターフェイスのRequest信号グループ(ハンドシェークを実施)
input iReq;
output iGnt;
input [31:0] iAddr;
// Master Read Data Interface
// マスターインターフェイスのRead信号グループ、キャッシュデータとしてSRAMから直接出力
input iRdStrb;
output iRdAck;
output [31:0] iRdData;
// External Memory Read Address Interface
// 外部メモリインターフェイスのRequest信号グループ(ハンドシェークを実施)
output oRdReq;
input oRdGnt;
output [31:0] oRdAddr;
// External Memory Read Data Interface
// 外部メモリインターフェイスのRead信号グループ(バースト長分のハンドシェークを実施)
output oRdStrb;
input oRdAck;
input [31:0] oRdData;
// Asynchronous Interface
// タグ操作信号グループ(ハンドシェークを実施)、クリアだけの機能なので引数なし
input aOpReq;
output aOpGnt;
// Data RAM Port (Dual Port)
// データSRAMのインターフェイス、ReadとWriteの2ポートで深さはパラメータに示した通り
output dramWE;
output [LLR+BLR-1:0] dramWA;
output [31:0] dramWD;
output dramRE;
output [LLR+BLR-1:0] dramRA;
input [31:0] dramRD;
// Utility
// 単一クロック制御
input reset;
input clk;
// ************************** LOCAL DECLARATIONS *******************************
// Tag Control
// タグのValidフラグと上位アドレス情報、WriteがないのでModifyフラグなし
// キャッシュライン数分必要でWayがあるならその積の分量を用意(ここではFFとして用意)
reg [LL-1:0] vldFlag;
reg [31:LBR+2] addrTable[0:LL-1];
wire eAdVld; // 指定するキャッシュラインのValid値
wire [31:LBR+2] eAdTblRef; // 指定するキャッシュラインの上位アドレス
wire eAdBusyHit; // 入力信号と上記の一致を示す(キャッシュヒット)
// Operation Flag Control
// キャッシュラインの位置を示す、入力アドレスの該当部分をそのまま代入
wire [LBR+1:BLR+2] eOpIndex;
// Status Control
// synopsys translate_off
reg [39:0] opStat; // synopsys translate_on デバッグ用(ASCII)
reg [3:0] opMaster; // 状態で変化するタグ・マスター制御(下記4信号)
wire opGnt, opPush, opVldSet, opVldClr;
reg [2:0] opStart; // 状態で変化するFIFO・メモリ制御(下記3信号)
wire opRdRqStart, opRdRtStart, opRdExStart;
wire [2:0] opAck; // 上記opStart信号に対するAck
wire eOpAlloc; // キャッシュ操作の成否
wire eOpVldSet; // キャッシュ操作時のValidフラグセット指示
wire eOpVldClr; // キャッシュ操作時のValidフラグクリア指示
wire eOpPush; // キャッシュ操作時のアドレスタグ書き戻し指示
wire eOpRdRqStart, eOpRdRtStart, eOpRdExStart;
// キャッシュ操作時のFIFO・メモリ制御指示
// Read Request
wire eRdRqGnt; // 外部メモリのGrant
// Read Route FIFO (Depth=2)
// Routing FIFOの深さは2固定、入力1に対して出力はバースト分の割合(下記Cntを使用)でFIFO操作
// FIFOの内容を途中チェックするため、一般的なFIFOモジュールをコールしない
// eRd*は入力側の信号、rRdは出力側の信号を表す
// FIFOは2次元でなく1次元表記(2次元配列のセンシビリティリスト記述は柔軟性が問題なため)
reg [LLR*2-1:0] rdRtIndexFIFO; // FIFO本体、以下フラグとポインター
reg eRdRtFull, rRdRtBusy;
wire eRdRtFullD, rRdRtBusyD;
reg [1:0] eRdRtPtr, rRdRtPtr;
wire [1:0] eRdRtPtrD, rRdRtPtrD;
reg [BLR-1:0] rRdRtCnt; // バーストをカウントするための変数
wire [BLR-1:0] rRdRtCntD;
wire eRdRtReq, rRdRtReq;
wire eRdRtGnt, rRdRtGnt;
reg [LBR+1:BLR+2] rRdRtIndex; // FIFO情報(キャッシュラインのアドレス)
wire [BLR+1:2] rRdRtWord; // FIFO情報(キャッシュライン内のワード位置)
wire eRdRtAlloc, rRdRtAlloc;
wire rdRtBusy; // 判定部で使用するFIFOの状態、Full, Emptyだけでなく
// FIFOの内容と入力信号をチェックしたもの
// Read Strobe
// 外部メモリのデータの状態を示す、外部信号をそのまま代入
wire rRdStReq;
wire [31:0] rRdStData;
// Read Execution FIFO (not implemented)
// Execution FIFOは、本来Non-blockingアクセスのため実装するが、あえて抜いている
// 判定部(eRd*信号)とSRAM調停部(rRd*信号)を直結する
wire eRdExReq, rRdExReq;
wire eRdExGnt, rRdExGnt;
wire [LBR+1:BLR+2] rRdExIndex;
wire [BLR+1:2] rRdExWord;
// RAM Arbiter
// SRAM調停部の信号、SRAM読み出しは1クロックかかるので、Ackはレジスタにして遅延させる
reg mOpRdOtStart;
reg mOpRdOtStartD;
reg rOpRdRqAck, rOpRdDtAck;
// 以下、基本的にこのままSRAM信号になる
reg ramWrReq, ramRdReq;
reg [LBR+1:BLR+2] ramWrIndex, ramRdIndex;
reg [BLR+1:2] ramWrWord, ramRdWord;
reg [31:0] ramWrData;
// RAM Pipe Control
// SRAM出力はマスターに接続するため、マスターのデータ受付けの許可信号(Stallの反転)を作る
wire rOpGnt;
// Utility
// for()文で使用
integer i;
// ****************************** MODULE BODY **********************************
// *****************************************************************************
// Flag Control
// *****************************************************************************
// -----------------------------------------------------------------------------
// Tag Control
// Validフラグは同期リセットとクリア信号(タグ操作インターフェイスが影響)により全てクリア
// セット信号により該当するキャッシュラインのValidフラグのみセット
always @(posedge clk)
if (reset | eOpVldClr) for (i=0; i<LL; i=i+1)
vldFlag[i] <= #1 1'b0;
else if (eOpVldSet)
vldFlag[eOpIndex]
<= #1 1'b1;
// アドレス情報はクリアする必要がないので、Validフラグがセットされるタイミングでセットするのみ
always @(posedge clk)
if (eOpPush)
addrTable[eOpIndex]
<= #1 iAddr[31:LBR+2];
assign eAdVld = vldFlag[eOpIndex];
assign eAdTblRef = addrTable[eOpIndex];
// 最終的なキャッシュヒット信号
assign eAdBusyHit = eAdVld & (iAddr[31:LBR+2] == eAdTblRef);
// -----------------------------------------------------------------------------
// Asynchronous Flush Return
// タグ操作のGrant(許可)は判定部が出すクリア信号を代用する
// 今回インターフェイスと判定部は直結したため、クリア信号自体タグ操作のRequestが関与する
// aOpReq→opVldClr(クリア信号)→aOpGntなので、外部でaOpGnt→aOpReqの組み合わせ回路は厳禁
assign aOpGnt = opVldClr;
// *****************************************************************************
// Cache Control
// *****************************************************************************
// -----------------------------------------------------------------------------
// Operation Address Definition
assign eOpIndex = iAddr[LBR+1:BLR+2];
// -----------------------------------------------------------------------------
// Encode Type of Status
// 判定部: Writeがないため簡単なステートマシンになる
// マスターよりタグ操作のRequestを優先し、キャッシュクリアを先行させる
// 簡単化しているため、ValidフラグではなくeAdBusyHit(キャッシュヒット)が状態判定のキーになる
always @(
aOpReq or
eAdBusyHit or
rdRtBusy
)
casex ({aOpReq, eAdBusyHit, rdRtBusy})
// キャッシュミス時は外部メモリに対しデータフェッチを指示する
// Routing FIFO内に同じアドレスのRequestがあると(rdRtBusy=1)、
// 外部メモリ2度読みなるため、これをブロックする(Stallになるが2度読みよりまし)
3'b0_0_0: begin // synopsys translate_off
opStat = "Fetch"; // synopsys translate_on
opMaster = 4'b0110;
opStart = 3'b110;
end
// キャッシュヒット時はSRAMをReadしマスターに出力させる
// フェッチと同様、SRAMにデータが格納するまではブロックする
3'b0_1_0: begin // synopsys translate_off
opStat = " Read"; // synopsys translate_on
opMaster = 4'b1000;
opStart = 3'b001;
end
// タグ操作のRequestがある場合、全てに優先して処理する(単純にクリア信号を作る)
3'b1_x_0: begin // synopsys translate_off
opStat = " Clr "; // synopsys translate_on
opMaster = 4'b0001;
opStart = 3'b000;
end
// Requestなし、もしくはRead待機(メモリフェッチが完了しヒット状態になるまで)を示す
default: begin // synopsys translate_off
opStat = " NOP "; // synopsys translate_on
opMaster = 4'b0000;
opStart = 3'b000;
end
endcase
// 判定した結果をタグ・マスター制御信号に代入
assign {opGnt, opPush, opVldSet, opVldClr}
= opMaster;
// 判定した結果をFIFO・メモリ制御信号に代入
assign {opRdRqStart, opRdRtStart, opRdExStart}
= opStart;
// FIFO・メモリの状態信号を束ねる
assign opAck = {eRdRqGnt, eRdRtGnt, eRdExGnt};
// -----------------------------------------------------------------------------
// All OK
// FIFO・メモリ制御と状態が合致すれば、キャッシュ操作の実行になる
assign eOpAlloc = &(~opStart | opAck) & iReq;
// -----------------------------------------------------------------------------
// Dispatch Signals of Master
// マスターのGrantには、タグ・マスター制御とFIFO・メモリ制御と状態が合致がダイレクトに伝わる
// Writeが加わる複雑な状態制御になると、深度の大きい組み合わせ回になり動作周波数のボトルネックになる
assign iGnt = &(~opStart | opAck) & opGnt;
// タグ・マスター制御パルスを作る、eOpVldClrは特殊なため不要(複雑な状態制御になるとNG)
assign eOpPush = eOpAlloc & opPush;
assign eOpVldSet = eOpAlloc & opVldSet;
assign eOpVldClr = opVldClr;
// -----------------------------------------------------------------------------
// Dispatch Signals of Memory
// FIFO・メモリ制御パルスを作る
assign eOpRdRqStart = eOpAlloc & opRdRqStart;
assign eOpRdRtStart = eOpAlloc & opRdRtStart;
assign eOpRdExStart = eOpAlloc & opRdExStart;
// *****************************************************************************
// Read Memory FIFO
// *****************************************************************************
// -----------------------------------------------------------------------------
// Read Request
// -----------------------------------------------------------------------------
// 判定部の信号が外部メモリにダイレクトに伝わる、また入力アドレスと出力アドレスはパススルーする
assign oRdReq = eOpRdRqStart;
assign oRdAddr = {iAddr[31:BLR+2], {BLR+2{1'b0}}};
assign eRdRqGnt = oRdGnt;
// -----------------------------------------------------------------------------
// Read Route FIFO (Depth=2)
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// FIFO Control
// Routing FIFO: 一般的なFIFOの記述と同じ、ただし内容を参照するためこのモジュール内に展開
always @(posedge clk)
if (reset) begin
eRdRtFull <= #1 1'b0;
eRdRtPtr <= #1 2'h0;
rRdRtBusy <= #1 1'b0;
rRdRtPtr <= #1 2'h0;
rRdRtCnt <= #1 {BLR{1'b0}};
end
else begin
eRdRtFull <= #1 eRdRtFullD;
eRdRtPtr <= #1 eRdRtPtrD;
rRdRtBusy <= #1 rRdRtBusyD;
rRdRtPtr <= #1 rRdRtPtrD;
rRdRtCnt <= #1 rRdRtCntD;
end
assign eRdRtFullD = (eRdRtPtrD[1] != rRdRtPtrD[1])
& (eRdRtPtrD[0] == rRdRtPtrD[0]);
assign rRdRtBusyD = (eRdRtPtrD != rRdRtPtrD);
assign eRdRtPtrD = eRdRtPtr + {1'h0, eRdRtAlloc};
// rRdRtCntでバースト分を数え、カウントアップしたものをFIFOポインターにする
assign {rRdRtPtrD, rRdRtCntD}
= {rRdRtPtr, rRdRtCnt} + {{BLR+1{1'b0}}, rRdRtAlloc};
assign eRdRtAlloc = eRdRtReq & eRdRtGnt;
assign rRdRtAlloc = rRdRtReq & rRdRtGnt;
// -----------------------------------------------------------------------------
// FIFO Body
// FIFOが1次元配列なので、for()文を使って2次元配列の等価な代入を実施
always @(posedge clk)
if (eRdRtAlloc) for (i=0; i<LLR; i=i+1)
rdRtIndexFIFO[eRdRtPtr[0]*LLR+i]
<= #1 eOpIndex[BLR+2+i];
// -----------------------------------------------------------------------------
// FIFO Signal
// FIFO制御は判定部が与え(eRdRtReq)、FIFO状態が判定部に返る(eRdRtGnt)
assign eRdRtReq = eOpRdRtStart;
assign eRdRtGnt = !eRdRtFull;
// FIFOの出口はSRAM調停部に接続する
assign rRdRtReq = rRdRtBusy;
assign rRdRtGnt = rOpRdRqAck;
assign rRdRtWord = rRdRtCnt;
// FIFOが1次元配列なので、for()文を使ってFIFOの出口のデータを作る
always @(
rdRtIndexFIFO or
rRdRtPtr
)
for (i=0; i<LLR; i=i+1)
rRdRtIndex[BLR+2+i]
= rdRtIndexFIFO[rRdRtPtr[0]*LLR+i];
// -----------------------------------------------------------------------------
// Read Busy Flag on Operation
// Routing FIFO内に入力アドレスと同じアドレスのものがあることを判定部に示す
assign rdRtBusy = |rdRtBusyFunc(eRdRtPtr, rRdRtPtr, eOpIndex, rdRtIndexFIFO);
// -----------------------------------------------------------------------------
// Read Strobe
// -----------------------------------------------------------------------------
// 外部メモリから返ってくるデータを調停部に渡す
assign rRdStReq = oRdAck;
assign rRdStData = oRdData;
assign oRdStrb = rOpRdRqAck;
// *****************************************************************************
// Read Data FIFO
// *****************************************************************************
// -----------------------------------------------------------------------------
// Read Execution FIFO (not implemented)
// -----------------------------------------------------------------------------
// Non-blockingアクセスを可能にするExecution FIFOは実装しないが、実装する場合はFIFOの実体を加える
// ただしRouting FIFOで実施したように、FIFO内の同一アドレスのチェックが必要になる
// -----------------------------------------------------------------------------
// FIFO Signal
// To prevent pipe stall which causes memory write fail to fill cache line,
// the request is masked by next pipe full.
// FIFO制御は判定部が与え(eRdExReq)、FIFO状態が判定部に返る(eRdExGnt)
assign eRdExReq = eOpRdExStart;
assign eRdExGnt = rRdExGnt;
// FIFOの出口はSRAM調停部に接続する
assign rRdExReq = eRdExReq;
assign rRdExGnt = rOpRdDtAck;
assign rRdExIndex = iAddr[LBR+1:BLR+2];
assign rRdExWord = iAddr[BLR+1:2];
// -----------------------------------------------------------------------------
// Read Output FIFO (Depth=0)
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// FIFO Rename
// 調停部の信号がマスターインターフェイスにダイレクトに伝わる、またデータはSRAM出力になる
assign iRdAck = mOpRdOtStart;
assign iRdData = dramRD;
// *****************************************************************************
// SRAM Control (for Dual Type)
// *****************************************************************************
// -----------------------------------------------------------------------------
// RAM Arbiter
// -----------------------------------------------------------------------------
// 調停部は2ポートSRAMなのでReadとWriteに分離できる
// 基本的に競合が生じない、生じる場合はシングルポートもしくはマスターWriteをサポートする場合になる
// -----------------------------------------------------------------------------
// Write Port
// SRAM Writeは外部メモリからデータを取得する場合に発生する
// rOpGnt信号はマスターがデータ受け取れるかどうかを示すが、これが受け取れない場合
// 未読部分への書き込みの恐れがあるため処理をブロックする
// Writeなので変数は組み合わせ回路のみになる
always @(
rOpGnt or
rRdRtIndex or rRdRtWord or
rRdStReq or rRdStData
) begin
rOpRdRqAck = 1'b0;
ramWrReq = 1'b0;
ramWrIndex = {LLR{1'bx}};
ramWrWord = {BLR{1'bx}};
ramWrData = {32{1'bx}};
if (rOpGnt)
if (rRdStReq) begin // Write (Memory -> SRAM)
// Ack
rOpRdRqAck = 1'b1;
// SRAM Operation
ramWrReq = 1'b1;
ramWrIndex = rRdRtIndex;
ramWrWord = rRdRtWord;
ramWrData = rRdStData;
end
end
// -----------------------------------------------------------------------------
// Read Port
// SRAM Readはマスターへのデータの返送時に発生する
// rOpGnt信号がアサートしないとWrite同様ブロック信号になる(単純なパイプラインStall)
// Readデータは1クロック遅延するため、マスターへのAck(mOpRdOtStart)はFFになる記述にする
always @(
rOpGnt or
rRdExReq or rRdExIndex or rRdExWord or
mOpRdOtStart
) begin
mOpRdOtStartD = mOpRdOtStart;
rOpRdDtAck = 1'b0;
ramRdReq = 1'b0;
ramRdIndex = {LLR{1'bx}};
ramRdWord = {BLR{1'bx}};
if (rOpGnt) begin
if (rRdExReq) begin // Read (SRAM -> Internal)
// Read Output Pipe Routing
mOpRdOtStartD = 1'b1;
// Ack
rOpRdDtAck = 1'b1;
// SRAM Operation
ramRdReq = 1'b1;
ramRdIndex = rRdExIndex;
ramRdWord = rRdExWord;
end
else begin // default case
// Read Output Pipe Routing
mOpRdOtStartD = 1'b0;
// Ack
rOpRdDtAck = 1'b1;
end
end
end
// このFFとSRAMのデータ出力が同期する
always @(posedge clk)
if (reset)
mOpRdOtStart <= #1 1'b0;
else
mOpRdOtStart <= #1 mOpRdOtStartD;
// -----------------------------------------------------------------------------
// RAM Pipe Control
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// FIFO Signal
// マスターのデータパイプラインのGrant(Stallの反転)
assign rOpGnt = iRdStrb | !iRdAck;
// -----------------------------------------------------------------------------
// FIFO Rename (SRAM Access Signal)
// 単純に調停部で生成した信号をSRAMに渡す
assign dramWE = ramWrReq;
assign dramWA = {ramWrIndex, ramWrWord[BLR+1:2]};
assign dramWD = ramWrData;
assign dramRE = ramRdReq;
assign dramRA = {ramRdIndex, ramRdWord[BLR+1:2]};
// ************************** FUNCTIONS and TASKS ******************************
// FIFO内のアドレスを入力値で検査して、一致するものはないかチェックする(それぞれ格納ごとに出力)
function [1:0] rdRtBusyFunc;
input [1:0] iptr;
input [1:0] optr;
input [LBR+1:BLR+2] idx;
input [LLR*2-1:0] iplane;
reg [LLR-1:0] iextract;
integer i, j;
// FIFOに格納可能な数を全て走査
for (i=0; i<2; i=i+1) begin
// 準備として1次元配列の入力値から必要な格納値を抜き出す
for (j=0; j<LLR; j=j+1)
iextract[j] = iplane[i*LLR+j];
// 入出力ポインターを見てデータが有効かどうかと、値の一致を調べる
rdRtBusyFunc[i] = ((iptr[1] ^ optr[1])
? (i[0] < iptr[0]) | (i[0] >= optr[0])
: (i[0] < iptr[0]) & (i[0] >= optr[0])
)
& (idx == iextract);
end
endfunction
endmodule
// *****************************************************************************
回路デザイン > 設計例 [キャッシュ] > コーディング 次のページ(テスト) このページのTOP ▲