Initial load of files

This commit is contained in:
rsnikhil
2019-03-26 14:49:40 -04:00
parent bc62f17032
commit ee24a93944
1008 changed files with 354221 additions and 224 deletions

View File

@@ -0,0 +1,218 @@
// Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved
package ByteLane;
// ================================================================
// Library for 'adjusting/unadjusting' bytes on a wider bus
// Example: a 64b bit bus can be regarded as 8 "byte lanes"
// with each lane selected by the lower 3 bits of an address.
// Such a bus typically carries data of 1, 2, 4 or 8 bytes.
// For 1,2, and 4, different apps use one of two representations:
// - carry the data on contiguous lower-order bytes, with a separate
// indication of how many bytes are being carried (1,2,4)
// - carry the data bytes on the lanes corresponding to the byte addresses,
// along with a 'mask' indicating which bytes are valid:
// Lane: 7 6 5 4 3 2 1 0
// x x x x D D D D 4-byte data, address lsbs = 3'b000
// D D D D x x x x 4-byte data, address lsbs = 3'b100
//
// x x x x x x D D 2-byte data, address lsbs = 3'b000
// x x x x D D x x 2-byte data, address lsbs = 3'b010
// ...
// D D x x x x x x 2-byte data, address lsbs = 3'b110
//
// x x x x x x x D 1-byte data, address lsbs = 3'b000
// x x x x x x D x 1-byte data, address lsbs = 3'b001
// ...
// D x x x x x x x 1-byte data, address lsbs = 3'b111
// Only 1 bit/lane is needed for the 'mask' (a.k.a. 'byte-enable' or 'strobe')
// ================================================================
// BSV library imports
import Vector :: *;
// ================================================================
// Expand each bit in 'strobe' to a byte, creating a full-width mask.
function Bit #(TMul #(n,8)) fn_strobe_to_mask (Bit #(n) strobe);
function Bit #(8) fn_bit_j_to_byte_j (Integer j);
return signExtend (strobe [j]);
endfunction
Vector #(n, Bit #(8)) v = genWith (fn_bit_j_to_byte_j);
return pack (v);
endfunction
// ================================================================
// Update an n-byte word taking into account an n-bit strobe
function Bit# (TMul #(n,8)) fn_update_strobed_bytes (Bit# (TMul #(n,8)) old_data,
Bit# (TMul #(n,8)) new_data,
Bit #(n) strobe);
Bit# (TMul #(n,8)) mask = fn_strobe_to_mask (strobe);
return ((old_data & (~ mask)) | (new_data & mask));
endfunction
// ================================================================
// Lane-adjust a data word according to the byte-address and data width
// Args: addr: only bottom few bits are relevant
// width: # of relevant bytes: 1, 2, 4, 8
// data_in: lsb-justified data
// Results: Bool err: misaligned, or bad data_width
// strobe: bit vector showing which lanes are active
// data_out: data shifted to be in-lane
// ----------------
// 32b version
function Tuple3 #(Bool, //
Bit #(4), // strobe
Bit #(32)) // lane-adjusted data
fn_lane_adjust_32b (Bit #(32) addr, Bit #(3) dw, Bit #(32) data);
Bit #(4) strobe = 0;
Bool err = False;
case (dw)
1: case (addr [1:0])
2'b00: begin strobe = 'b_0001; end
2'b01: begin strobe = 'b_0010; data = (data << 8); end
2'b10: begin strobe = 'b_0100; data = (data << 16); end
2'b11: begin strobe = 'b_1000; data = (data << 24); end
endcase
2: case (addr [1:0])
2'b00: begin strobe = 'b_0011; end
2'b10: begin strobe = 'b_1100; data = (data << 16); end
default: err = True;
endcase
4: case (addr [1:0])
2'b00: strobe = 'b_1111;
default: err = True;
endcase
default: err = True;
endcase
return tuple3 (err, strobe, data);
endfunction
// ----------------
// 64b version
function Tuple3 #(Bool, // err: misaligned, or bad data_width
Bit #(8), // strobe
Bit #(64)) // lane-adjusted data
fn_lane_adjust_64b (Bit #(64) addr, Bit #(4) dw, Bit #(64) data);
Bit #(8) strobe = 0;
Bool err = False;
case (dw)
1: case (addr [2:0])
3'b000: begin strobe = 'b_0000_0001; end
3'b001: begin strobe = 'b_0000_0010; data = (data << 8); end
3'b010: begin strobe = 'b_0000_0100; data = (data << 16); end
3'b011: begin strobe = 'b_0000_1000; data = (data << 24); end
3'b100: begin strobe = 'b_0001_0000; data = (data << 32); end
3'b101: begin strobe = 'b_0010_0000; data = (data << 40); end
3'b110: begin strobe = 'b_0100_0000; data = (data << 48); end
3'b111: begin strobe = 'b_1000_0000; data = (data << 56); end
endcase
2: case (addr [2:0])
3'b000: begin strobe = 'b_0000_0011; end
3'b010: begin strobe = 'b_0000_1100; data = (data << 16); end
3'b100: begin strobe = 'b_0011_0000; data = (data << 32); end
3'b110: begin strobe = 'b_1100_0000; data = (data << 48); end
default: err = True;
endcase
3: case (addr [2:0])
3'b000: begin strobe = 'b_0000_1111; end
3'b100: begin strobe = 'b_1111_0000; data = (data << 32); end
default: err = True;
endcase
4: case (addr [2:0])
3'b000: begin strobe = 'b_1111_1111; end
default: err = True;
endcase
default: err = True;
endcase
return tuple3 (err, strobe, data);
endfunction
// ================================================================
// Lane-unadjust a data word according to the byte-address and data width
// ----------------
// 32b version
function Tuple2 #(Bool, // err: misaligned, or bad data_width
Bit #(32)) // lane-unadjusted data
fn_lane_unadjust_32b (Bit #(32) addr, Bit #(3) dw, Bit #(32) data);
Bool err = False;
case (dw)
1: case (addr [1:0])
2'b00: data = (data >> 0);
2'b01: data = (data >> 8);
2'b10: data = (data >> 16);
2'b11: data = (data >> 24);
endcase
2: case (addr [1:0])
2'b00: data = (data >> 0);
2'b10: data = (data >> 16);
default: err = True;
endcase
4: case (addr [1:0])
2'b00: data = (data >> 0);
default: err = True;
endcase
default: err = True;
endcase
return tuple2 (err, data);
endfunction
// ----------------
// 64b version
function Tuple2 #(Bool, // err: misaligned, or bad data_width
Bit #(64)) // lane-unadjusted data
fn_lane_unadjust_64b (Bit #(64) addr, Bit #(4) dw, Bit #(64) data);
Bool err = False;
case (dw)
1: case (addr [2:0])
3'b000: data = (data >> 0);
3'b001: data = (data >> 8);
3'b010: data = (data >> 16);
3'b011: data = (data >> 24);
3'b100: data = (data >> 32);
3'b101: data = (data >> 40);
3'b110: data = (data >> 48);
3'b111: data = (data >> 56);
endcase
2: case (addr [2:0])
3'b000: data = (data >> 0);
3'b010: data = (data >> 16);
3'b100: data = (data >> 32);
3'b110: data = (data >> 48);
default: err = True;
endcase
4: case (addr [2:0])
3'b000: data = (data >> 0);
3'b100: data = (data >> 32);
default: err = True;
endcase
8: case (addr [2:0])
3'b000: data = (data >> 0);
default: err = True;
endcase
default: err = True;
endcase
return tuple2 (err, data);
endfunction
// ================================================================
endpackage

View File

@@ -0,0 +1,63 @@
// Copyright (c) 2015-2019 Bluespec, Inc., All Rights Reserved
// Author: Rishiyur S. Nikhil
// A "credit counter" which can be incremented and decremented concurrently.
package CreditCounter;
// ================================================================
// BSV library imports
import Cur_Cycle :: *;
// ================================================================
// Interface
interface CreditCounter_IFC #(numeric type w);
// Current value of internal count
method UInt #(w) value;
// Increment internal count
method Action incr;
// Decrement internal count
method Action decr;
// Clear internal count to 0
method Action clear;
endinterface
// ================================================================
// Module implementation
// Scheduling: value < incr < decr < clear
module mkCreditCounter (CreditCounter_IFC #(w));
Reg #(UInt #(w)) crg [3] <- mkCReg (3, 0);
method UInt #(w) value = crg [1];
method Action incr;
if (crg [0] == maxBound) begin
$display ("%0d: ERROR: CreditCounter: overflow", cur_cycle);
$finish (1); // Assertion failure
end
crg [0] <= crg [0] + 1;
endmethod
method Action decr () if (crg [1] != 0);
if (crg [1] == 0) begin
$display ("%0d: ERROR: CreditCounter: underflow", cur_cycle);
$finish (1); // Assertion failure
end
crg [1] <= crg [1] - 1;
endmethod
method Action clear;
crg [2] <= 0;
endmethod
endmodule
// ================================================================
endpackage

View File

@@ -0,0 +1,15 @@
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved.
package Cur_Cycle;
// ================================================================
// A convenience function to return the current cycle number during BSV simulations
ActionValue #(Bit #(32)) cur_cycle = actionvalue
Bit #(32) t <- $stime;
return t / 10;
endactionvalue;
// ================================================================
endpackage

View File

@@ -0,0 +1,119 @@
// Copyright (c) 2016-2019 Bluespec, Inc. All Rights Reserved.
// EdgeFIFOFs are 1-element FIFOFs used as generic interfaces by IPs
// where they connect to interconnect fabrics. The overall interface
// is a standard FIFOF interface. The IP-side is guarded. The
// Fabric-side is unguarded, so that it's easy to attach transactors
// for some particular bus interface such as AXI4-Lite, AHB-Lite, etc.
package EdgeFIFOFs;
// ================================================================
// BSV library imports
import FIFOF :: *;
// ================================================================
// FIFOFs for Master IPs.
// enq (IP-side) is guarded, deq (Fabric-side) is not.
module mkMaster_EdgeFIFOF (FIFOF #(t))
provisos (Bits #(t, tsz));
Integer port_deq = 0;
Integer port_enq = 1;
Integer port_clear = 2;
Array #(Reg #(Bool)) crg_full <- mkCReg (3, False);
Reg #(t) rg_payload <- mkRegU;
// ----------------
// Clear
method Action clear;
crg_full [port_clear] <= False;
endmethod
// ----------------
// Enq side (IP-side; guarded)
method Bool notFull ();
return (! crg_full [port_enq]);
endmethod
method Action enq (t x) if (! crg_full [port_enq]);
crg_full [port_enq] <= True;
rg_payload <= x;
endmethod
// ----------------
// Deq side (Fabric-side; unguarded)
method Bool notEmpty ();
return crg_full [port_deq];
endmethod
method t first (); // unguarded
return rg_payload;
endmethod
method Action deq (); // unguarded
crg_full [port_deq] <= False;
endmethod
endmodule
// ================================================================
// For Slave IPs.
// enq (Fabric-side) is unguarded, deq (IP-side) is guarded.
module mkSlave_EdgeFIFOF (FIFOF #(t))
provisos (Bits #(t, tsz));
Integer port_deq = 0;
Integer port_enq = 1;
Integer port_clear = 2;
Array #(Reg #(Bool)) crg_full <- mkCReg (3, False);
Reg #(t) rg_payload <- mkRegU;
// ----------------
// Clear
method Action clear;
crg_full [port_clear] <= False;
endmethod
// ----------------
// Enq side (IP-side; unguarded)
method Bool notFull ();
return (! crg_full [port_enq]);
endmethod
method Action enq (t x); // unguarded
crg_full [port_enq] <= True;
rg_payload <= x;
endmethod
// ----------------
// Deq side (Fabric-side; guarded)
method Bool notEmpty ();
return crg_full [port_deq];
endmethod
method t first () if (crg_full [port_deq]);
return rg_payload;
endmethod
method Action deq () if (crg_full [port_deq]);
crg_full [port_deq] <= False;
endmethod
endmodule
// ================================================================
endpackage

View File

@@ -0,0 +1,126 @@
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved.
package GetPut_Aux;
// ================================================================
// Misc. additional useful definitions on FIFOs, Gets and Puts
// ================================================================
// BSV library imports
import FIFOF :: *;
import GetPut :: *;
import ClientServer :: *;
// ================================================================
// A convenience function to 'pop' a value from a FIFO, FIFOF, ...
function ActionValue #(t) pop (ifc f)
provisos (ToGet #(ifc, t));
return toGet (f).get;
endfunction
// ================================================================
// No-op stubs for Get, Put, Client and Server interfaces.
// 'get' is never enabled.
// 'put' is always enabled and just discards its argument.
Get #(t) getstub = interface Get;
method ActionValue #(t) get () if (False);
return ?;
endmethod
endinterface;
Put #(t) putstub = interface Put;
method Action put (t x) if (True);
noAction;
endmethod
endinterface;
Client #(t1,t2) client_stub = interface Client;
interface request = getstub;
interface response = putstub;
endinterface;
Server #(t1,t2) server_stub = interface Server;
interface request = putstub;
interface response = getstub;
endinterface;
// ================================================================
// For debugging, a convenience function to display full/empty status of a FIFO
function Action fa_show_FIFOF_state (String s, FIFOF #(t) f);
action
$write ("%s:", s);
if (! f.notEmpty) $display (" Empty");
else if (! f.notFull) $display (" Full");
else $display (" neither empty nor full");
endaction
endfunction
// ================================================================
// DiscardFIFOF
// enqueue side: always ready, and discards everything
// dequeue side: always empty, never returns anything
module mkDiscardFIFOF (FIFOF #(t));
method Action enq (t x);
noAction;
endmethod
method Bool notFull;
return True;
endmethod
method t first () if (False);
return ?;
endmethod
method Action deq () if (False);
noAction;
endmethod
method Bool notEmpty;
return False;
endmethod
method Action clear;
noAction;
endmethod
endmodule
// ================================================================
// dummy_FIFO interface
// enqueue side: never ready;
// dequeue side: never ready
FIFOF #(t) dummy_FIFOF = interface FIFOF;
method Action enq (x) if (False);
noAction;
endmethod
method notFull;
return False;
endmethod
method first () if (False);
return ?;
endmethod
method Action deq () if (False);
noAction;
endmethod
method notEmpty;
return False;
endmethod
method Action clear;
noAction;
endmethod
endinterface;
// ================================================================
endpackage

View File

@@ -0,0 +1,137 @@
// Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved
package Semi_FIFOF;
// ================================================================
// Separate interfaces for input-side and output-side of FIFOF.
// Conversion functions to these, from FIFOF interfaces.
// ================================================================
// BSV library imports
import FIFOF :: *;
import Connectable :: *;
// ================================================================
// Semi-FIFOF interfaces
interface FIFOF_I #(type t);
method Action enq (t x);
method Bool notFull ();
endinterface
interface FIFOF_O #(type t);
method t first ();
method Action deq ();
method Bool notEmpty ();
endinterface
// ================================================================
// Converters from FIFOF
function FIFOF_I #(t) to_FIFOF_I (FIFOF #(t) f);
return interface FIFOF_I;
method enq (x) = f.enq (x);
method notFull = f.notFull;
endinterface;
endfunction
function FIFOF_O #(t) to_FIFOF_O (FIFOF #(t) f);
return interface FIFOF_O;
method first = f.first;
method deq = f.deq;
method notEmpty = f.notEmpty;
endinterface;
endfunction
// ================================================================
// Connections
// ----------------
// FIFOF_O to FIFOF_I
instance Connectable #(FIFOF_O #(t), FIFOF_I #(t));
module mkConnection #(FIFOF_O #(t) fo, FIFOF_I #(t) fi) (Empty);
rule rl_connect;
fi.enq (fo.first);
fo.deq;
endrule
endmodule
endinstance
// ----------------
// FIFOF_I to FIFOF_O
instance Connectable #(FIFOF_I #(t), FIFOF_O #(t));
module mkConnection #(FIFOF_I #(t) fi, FIFOF_O #(t) fo) (Empty);
mkConnection (fo, fi);
endmodule
endinstance
// ----------------
// FIFOF_O to FIFOF
instance Connectable #(FIFOF_O #(t), FIFOF #(t));
module mkConnection #(FIFOF_O #(t) fo, FIFOF #(t) fi) (Empty);
rule rl_connect;
fi.enq (fo.first);
fo.deq;
endrule
endmodule
endinstance
// ----------------
// FIFOF to FIFOF_I
instance Connectable #(FIFOF #(t), FIFOF_I #(t));
module mkConnection #(FIFOF #(t) fo, FIFOF_I #(t) fi) (Empty);
rule rl_connect;
fi.enq (fo.first);
fo.deq;
endrule
endmodule
endinstance
// ================================================================
// Convenience function combining first/enq
function ActionValue #(t) pop_o (FIFOF_O #(t) f);
actionvalue
f.deq;
return f.first;
endactionvalue
endfunction
// ================================================================
// Dummy tie-off interfaces
// dummy_FIFO_I that never accepts anything (always "full")
FIFOF_I #(t) dummy_FIFOF_I = interface FIFOF_I;
method Action enq (x) if (False);
noAction;
endmethod
method notFull;
return False;
endmethod
endinterface;
// Dummy FIFO_O that never yields anything (always "empty")
FIFOF_O #(t) dummy_FIFOF_O = interface FIFOF_O;
method first () if (False);
return ?;
endmethod
method Action deq () if (False);
noAction;
endmethod
method notEmpty;
return False;
endmethod
endinterface;
// ================================================================
endpackage

977
src_Core/CPU/Core.bsv Normal file
View File

@@ -0,0 +1,977 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// Portions Copyright (c) Bluespec, Inc.
`include "ProcConfig.bsv"
import Vector::*;
import BuildVector::*;
import DefaultValue::*;
import ClientServer::*;
import GetPut::*;
import Assert::*;
import Cntrs::*;
import ConfigReg::*;
import FIFO::*;
import Fifo::*;
import Ehr::*;
import Connectable::*;
import Types::*;
import ProcTypes::*;
import CacheUtils::*;
import TlbTypes::*;
import SynthParam::*;
import VerificationPacket::*;
import Performance::*;
import HasSpecBits::*;
import Exec::*;
import FetchStage::*;
import ITlb::*;
import DTlb::*;
import L2Tlb::*;
import TlbConnect::*;
import EpochManager::*;
import PhysRFile::*;
import RFileSynth::*;
import RenamingTable::*;
import ReorderBuffer::*;
import ReorderBufferSynth::*;
import Scoreboard::*;
import ScoreboardSynth::*;
import SpecTagManager::*;
import Fpu::*;
import MulDiv::*;
import ReservationStationEhr::*;
import ReservationStationAlu::*;
import ReservationStationMem::*;
import ReservationStationFpuMulDiv::*;
import AluExePipeline::*;
import FpuMulDivExePipeline::*;
import MemExePipeline::*;
import SplitLSQ::*;
import StoreBuffer::*;
import GlobalSpecUpdate::*;
import CCTypes::*;
import L1CoCache::*;
import L1Bank::*;
import IBank::*;
import MMIOCore::*;
import RenameStage::*;
import CommitStage::*;
import Bypass::*;
import CsrFile :: *;
interface CoreReq;
method Action start(
Addr startpc,
Addr toHostAddr, Addr fromHostAddr
);
method Action perfReq(PerfLocation loc, PerfType t);
endinterface
interface CoreIndInv;
method ActionValue#(ProcPerfResp) perfResp;
method ActionValue#(void) terminate;
endinterface
interface CoreDeadlock;
interface Get#(L1DCRqStuck) dCacheCRqStuck;
interface Get#(L1DPRqStuck) dCachePRqStuck;
interface Get#(L1ICRqStuck) iCacheCRqStuck;
interface Get#(L1IPRqStuck) iCachePRqStuck;
interface Get#(RenameStuck) renameInstStuck;
interface Get#(RenameStuck) renameCorrectPathStuck;
interface Get#(CommitStuck) commitInstStuck;
interface Get#(CommitStuck) commitUserInstStuck;
interface Get#(void) checkStarted;
endinterface
interface CoreRenameDebug;
interface Get#(RenameErrInfo) renameErr;
endinterface
interface Core;
// core request & indication
interface CoreReq coreReq;
interface CoreIndInv coreIndInv;
// coherent caches to LLC
interface ChildCacheToParent#(L1Way, void) dCacheToParent;
interface ChildCacheToParent#(L1Way, void) iCacheToParent;
// DMA to LLC
interface TlbMemClient tlbToMem;
// MMIO
interface MMIOCoreToPlatform mmioToPlatform;
// stats enable
method ActionValue#(Bool) sendDoStats;
method Action recvDoStats(Bool x);
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface CoreDeadlock deadlock;
// debug rename
interface CoreRenameDebug renameDebug;
// Bluespec: external interrupt requests targeting Machine and Supervisor modes
method Action setMEIP (Bit #(1) v);
method Action setSEIP (Bit #(1) v);
endinterface
// fixpoint to instantiate modules
interface CoreFixPoint;
interface Vector#(AluExeNum, AluExePipeline) aluExeIfc;
interface Vector#(FpuMulDivExeNum, FpuMulDivExePipeline) fpuMulDivExeIfc;
interface MemExePipeline memExeIfc;
method Action killAll; // kill everything: used by commit stage
interface Reg#(Bool) doStatsIfc;
endinterface
(* synthesize *)
module mkCore#(CoreId coreId)(Core);
let verbose = True;
Reg#(Bool) outOfReset <- mkReg(False);
rule rl_outOfReset if (!outOfReset);
$fwrite(stderr, "mkProc came out of reset\n");
outOfReset <= True;
endrule
Reg#(Bool) started <- mkReg(False);
// front end
FetchStage fetchStage <- mkFetchStage;
ITlb iTlb = fetchStage.iTlbIfc;
ICoCache iMem = fetchStage.iMemIfc;
// back end
RFileSynth rf <- mkRFileSynth;
// Bluespec: CsrFile including external interrupt request methods
CsrFile csrf <- mkCsrFile(zeroExtend(coreId)); // hartid in CSRF should be core id
RegRenamingTable regRenamingTable <- mkRegRenamingTable;
EpochManager epochManager <- mkEpochManager;
SpecTagManager specTagManager <- mkSpecTagManager;
ReorderBufferSynth rob <- mkReorderBufferSynth;
// We have two scoreboards: one conservative and other aggressive
// - Aggressive sb is checked at rename stage, so inst after rename may be issued early
// - Conservative sb is checked at reg read stage, to ensure correctness
// Every pipeline should set both sb if it needs to write reg
// - Conservative sb is set when data is written into rf
// - Aggressive sb is set when pipeline sends out wakeup for reservation staion
// Note that wakeup can be sent early if it knows when the data will be produced
ScoreboardCons sbCons <- mkScoreboardCons; // conservative sb
ScoreboardAggr sbAggr <- mkScoreboardAggr; // aggressive sb
// MMIO: need to track in flight CSR inst or interrupt; note we can at most
// 1 CSR inst or 1 interrupt in ROB, so just use 1 bit track it. Commit
// stage use port 0 to reset this, and Rename stage use port 1 to set this.
Ehr#(2, Bool) csrInstOrInterruptInflight <- mkEhr(False);
Reg#(Bool) csrInstOrInterruptInflight_commit = csrInstOrInterruptInflight[0];
Reg#(Bool) csrInstOrInterruptInflight_rename = csrInstOrInterruptInflight[1];
MMIOCoreInput mmioInIfc = (interface MMIOCoreInput;
interface fetch = fetchStage.mmioIfc;
method getMSIP = csrf.getMSIP;
method setMSIP = csrf.setMSIP;
method setMTIP = csrf.setMTIP;
method noInflightCSRInstOrInterrupt = !csrInstOrInterruptInflight[0];
method setTime = csrf.setTime;
endinterface);
MMIOCore mmio <- mkMMIOCore(mmioInIfc);
// fix point module to instantiate other function units
module mkCoreFixPoint#(CoreFixPoint fix)(CoreFixPoint);
// spec update
Vector#(AluExeNum, SpeculationUpdate) aluSpecUpdate;
for(Integer i = 0; i < valueof(AluExeNum); i = i+1) begin
aluSpecUpdate[i] = fix.aluExeIfc[i].specUpdate;
end
Vector#(FpuMulDivExeNum, SpeculationUpdate) fpuMulDivSpecUpdate;
for(Integer i = 0; i < valueof(FpuMulDivExeNum); i = i+1) begin
fpuMulDivSpecUpdate[i] = fix.fpuMulDivExeIfc[i].specUpdate;
end
GlobalSpecUpdate#(CorrectSpecPortNum, ConflictWrongSpecPortNum) globalSpecUpdate <- mkGlobalSpecUpdate(
joinSpeculationUpdate(
append(append(vec(regRenamingTable.specUpdate,
specTagManager.specUpdate,
fix.memExeIfc.specUpdate), aluSpecUpdate), fpuMulDivSpecUpdate)
),
rob.specUpdate
);
// whether perf data is collected
Reg#(Bool) doStatsReg <- mkConfigReg(False);
// redirect func
//function Action redirectFunc(Addr trap_pc, Maybe#(SpecTag) spec_tag, InstTag inst_tag );
//action
// if (verbose) $fdisplay(stdout, "[redirect_action] new pc = 0x%8x, spec_tag = ", trap_pc, fshow(spec_tag));
// epochManager.redirect;
// fetchStage.redirect(trap_pc);
// if (spec_tag matches tagged Valid .valid_spec_tag) begin
// globalSpecUpdate.incorrectSpec(valid_spec_tag, inst_tag);
// end
//endaction
//endfunction
// write aggressive elements + wakupe reservation stations
function Action writeAggr(Integer wrAggrPort, PhyRIndx dst);
action
sbAggr.setReady[wrAggrPort].put(dst);
for(Integer i = 0; i < valueof(AluExeNum); i = i+1) begin
fix.aluExeIfc[i].rsAluIfc.setRegReady[wrAggrPort].put(Valid (dst));
end
for(Integer i = 0; i < valueof(FpuMulDivExeNum); i = i+1) begin
fix.fpuMulDivExeIfc[i].rsFpuMulDivIfc.setRegReady[wrAggrPort].put(Valid (dst));
end
fix.memExeIfc.rsMemIfc.setRegReady[wrAggrPort].put(Valid (dst));
endaction
endfunction
// write conservative elements
function Action writeCons(Integer wrConsPort, PhyRIndx dst, Data data);
action
rf.write[wrConsPort].wr(dst, data);
sbCons.setReady[wrConsPort].put(dst);
endaction
endfunction
Vector#(AluExeNum, FIFO#(FetchTrainBP)) trainBPQ <- replicateM(mkFIFO);
Vector#(AluExeNum, AluExePipeline) aluExe;
for(Integer i = 0; i < valueof(AluExeNum); i = i+1) begin
Vector#(2, SendBypass) sendBypassIfc; // exe and finish
for(Integer sendPort = 0; sendPort < 2; sendPort = sendPort + 1) begin
sendBypassIfc[sendPort] = (interface SendBypass;
method Action send(PhyRIndx dst, Data data);
// broadcast bypass
Integer recvPort = valueof(AluExeNum) * sendPort + i;
for(Integer j = 0; j < valueof(FpuMulDivExeNum); j = j+1) begin
fix.fpuMulDivExeIfc[j].recvBypass[recvPort].recv(dst, data);
end
fix.memExeIfc.recvBypass[recvPort].recv(dst, data);
for(Integer j = 0; j < valueof(AluExeNum); j = j+1) begin
fix.aluExeIfc[j].recvBypass[recvPort].recv(dst, data);
end
endmethod
endinterface);
end
let aluExeInput = (interface AluExeInput;
method sbCons_lazyLookup = sbCons.lazyLookup[aluRdPort(i)].get;
method rf_rd1 = rf.read[aluRdPort(i)].rd1;
method rf_rd2 = rf.read[aluRdPort(i)].rd2;
method csrf_rd = csrf.rd;
method rob_getPC = rob.getOrigPC[i].get;
method rob_getPredPC = rob.getOrigPredPC[i].get;
method rob_setExecuted = rob.setExecuted_doFinishAlu[i].set;
method fetch_train_predictors = toPut(trainBPQ[i]).put;
method setRegReadyAggr = writeAggr(aluWrAggrPort(i));
interface sendBypass = sendBypassIfc;
method writeRegFile = writeCons(aluWrConsPort(i));
method Action redirect(Addr new_pc, SpecTag spec_tag, InstTag inst_tag);
if (verbose) begin
$display("[ALU redirect - %d] ", i, fshow(new_pc),
"; ", fshow(spec_tag), "; ", fshow(inst_tag));
end
epochManager.incrementEpoch;
fetchStage.redirect(new_pc);
globalSpecUpdate.incorrectSpec(False, spec_tag, inst_tag);
endmethod
method correctSpec = globalSpecUpdate.correctSpec[finishAluCorrectSpecPort(i)].put;
method doStats = doStatsReg._read;
endinterface);
aluExe[i] <- mkAluExePipeline(aluExeInput);
// truly call fetch method to train branch predictor
rule doFetchTrainBP;
let train <- toGet(trainBPQ[i]).get;
fetchStage.train_predictors(
train.pc, train.nextPc, train.iType, train.taken,
train.dpTrain, train.mispred
);
endrule
end
Vector#(FpuMulDivExeNum, FpuMulDivExePipeline) fpuMulDivExe;
for(Integer i = 0; i < valueof(FpuMulDivExeNum); i = i+1) begin
let fpuMulDivExeInput = (interface FpuMulDivExeInput;
method sbCons_lazyLookup = sbCons.lazyLookup[fpuMulDivRdPort(i)].get;
method rf_rd1 = rf.read[fpuMulDivRdPort(i)].rd1;
method rf_rd2 = rf.read[fpuMulDivRdPort(i)].rd2;
method rf_rd3 = rf.read[fpuMulDivRdPort(i)].rd3;
method csrf_rd = csrf.rd;
method rob_setExecuted = rob.setExecuted_doFinishFpuMulDiv[i].set;
method Action writeRegFile(PhyRIndx dst, Data data);
writeAggr(fpuMulDivWrAggrPort(i), dst);
writeCons(fpuMulDivWrConsPort(i), dst, data);
endmethod
method conflictWrongSpec = globalSpecUpdate.conflictWrongSpec[finishFpuMulDivConflictWrongSpecPort(i)].put(?);
method doStats = doStatsReg._read;
endinterface);
fpuMulDivExe[i] <- mkFpuMulDivExePipeline(fpuMulDivExeInput);
end
let memExeInput = (interface MemExeInput;
method sbCons_lazyLookup = sbCons.lazyLookup[memRdPort].get;
method rf_rd1 = rf.read[memRdPort].rd1;
method rf_rd2 = rf.read[memRdPort].rd2;
method csrf_rd = csrf.rd;
method rob_getPC = rob.getOrigPC[valueof(AluExeNum)].get; // last getPC port
method rob_setExecuted_doFinishMem = rob.setExecuted_doFinishMem;
method rob_setExecuted_deqLSQ = rob.setExecuted_deqLSQ;
method isMMIOAddr = mmio.isMMIOAddr;
method mmioReq = mmio.dataReq;
method mmioRespVal = mmio.dataRespVal;
method mmioRespDeq = mmio.dataRespDeq;
method setRegReadyAggr_mem = writeAggr(memWrAggrPort);
method setRegReadyAggr_forward = writeAggr(forwardWrAggrPort);
method writeRegFile = writeCons(memWrConsPort);
method doStats = doStatsReg._read;
endinterface);
let memExe <- mkMemExePipeline(memExeInput);
interface aluExeIfc = aluExe;
interface fpuMulDivExeIfc = fpuMulDivExe;
interface memExeIfc = memExe;
method Action killAll;
globalSpecUpdate.incorrectSpec(True, ?, ?);
endmethod
interface doStatsIfc = doStatsReg;
endmodule
CoreFixPoint coreFix <- moduleFix(mkCoreFixPoint);
Vector#(AluExeNum, ReservationStationAlu) reservationStationAlu;
for(Integer i = 0; i < valueof(AluExeNum); i = i+1) begin
reservationStationAlu[i] = coreFix.aluExeIfc[i].rsAluIfc;
end
Vector#(FpuMulDivExeNum, ReservationStationFpuMulDiv) reservationStationFpuMulDiv;
for(Integer i = 0; i < valueof(FpuMulDivExeNum); i = i+1) begin
reservationStationFpuMulDiv[i] = coreFix.fpuMulDivExeIfc[i].rsFpuMulDivIfc;
end
ReservationStationMem reservationStationMem = coreFix.memExeIfc.rsMemIfc;
DTlbSynth dTlb = coreFix.memExeIfc.dTlbIfc;
SplitLSQ lsq = coreFix.memExeIfc.lsqIfc;
StoreBuffer stb = coreFix.memExeIfc.stbIfc;
DCoCache dMem = coreFix.memExeIfc.dMemIfc;
// L2 TLB
L2Tlb l2Tlb <- mkL2Tlb;
mkTlbConnect(iTlb.toParent, dTlb.toParent, l2Tlb.toChildren);
// flags to flush
Reg#(Bool) flush_tlbs <- mkReg(False);
Reg#(Bool) update_vm_info <- mkReg(False);
Reg#(Bool) flush_reservation <- mkReg(False);
`ifdef SECURITY
Reg#(Bool) flush_caches <- mkReg(False);
Reg#(Bool) flush_brpred <- mkReg(False);
`else
Reg#(Bool) flush_caches <- mkReadOnlyReg(False);
Reg#(Bool) flush_brpred <- mkReadOnlyReg(False);
`endif
`ifdef SELF_INV_CACHE
Reg#(Bool) reconcile_i <- mkReg(False);
`else
Reg#(Bool) reconcile_i <- mkReadOnlyReg(False);
`endif
`ifdef SELF_INV_CACHE
`ifdef SYSTEM_SELF_INV_L1D
Reg#(Bool) reconcile_d <- mkReg(False);
`else // !SYSTEM_SELF_INV_L1D
Reg#(Bool) reconcile_d <- mkReadOnlyReg(False);
`endif // SYSTEM_SELF_INV_L1D
`else // !SELF_INV_CACHE
Reg#(Bool) reconcile_d <- mkReadOnlyReg(False);
`endif // SELF_INV_CACHE
// performance counters
Reg#(Bool) doStats = coreFix.doStatsIfc; // whether data is collected
`ifdef PERF_COUNT
// OOO execute stag (in AluExePipeline and MemExePipeline)
// commit stage (many in CommitStage.bsv)
// cycle
Count#(Data) cycleCnt <- mkCount(0);
// buffer/tags size
Count#(Data) ldqFullCycles <- mkCount(0);
Count#(Data) stqFullCycles <- mkCount(0);
Count#(Data) robFullCycles <- mkCount(0);
Count#(Data) aluRS0FullCycles <- mkCount(0);
Count#(Data) aluRS1FullCycles <- mkCount(0);
Count#(Data) fpuMulDivRSFullCycles <- mkCount(0);
Count#(Data) memRSFullCycles <- mkCount(0);
Count#(Data) epochFullCycles <- mkCount(0);
Count#(Data) specTagFullCycles <- mkCount(0);
// FIFOs to connect performance counters
FIFO#(ExeStagePerfType) exePerfReqQ <- mkFIFO1;
FIFO#(ComStagePerfType) comPerfReqQ <- mkFIFO1;
FIFO#(CoreSizePerfType) sizePerfReqQ <- mkFIFO1;
Fifo#(1, PerfResp#(ExeStagePerfType)) exePerfRespQ <- mkCFFifo;
Fifo#(1, PerfResp#(ComStagePerfType)) comPerfRespQ <- mkCFFifo;
Fifo#(1, PerfResp#(CoreSizePerfType)) sizePerfRespQ <- mkCFFifo;
// FIFO of perf resp
FIFO#(ProcPerfResp) perfRespQ <- mkFIFO1;
`endif
// FIFO of perf req
FIFO#(ProcPerfReq) perfReqQ <- mkFIFO1;
// -- End of performance counters
`ifdef CHECK_DEADLOCK
// when to start deadlock checking
Reg#(Bool) startDeadlockCheck <- mkReg(False);
FIFO#(void) deadlockCheckStartedQ <- mkFIFO;
rule doStartDeadlockCheck(!startDeadlockCheck && started);
startDeadlockCheck <= True;
deadlockCheckStartedQ.enq(?);
endrule
`endif
// Rename stage
let renameInput = (interface RenameInput;
interface fetchIfc = fetchStage;
interface robIfc = rob;
interface rtIfc = regRenamingTable;
interface sbConsIfc = sbCons;
interface sbAggrIfc = sbAggr;
interface csrfIfc = csrf;
interface emIfc = epochManager;
interface smIfc = specTagManager;
interface rsAluIfc = reservationStationAlu;
interface rsFpuMulDivIfc = reservationStationFpuMulDiv;
interface rsMemIfc = reservationStationMem;
interface lsqIfc = lsq;
method pendingMMIOPRq = mmio.hasPendingPRq;
method issueCsrInstOrInterrupt = csrInstOrInterruptInflight_rename._write(True);
method Bool checkDeadlock;
`ifdef CHECK_DEADLOCK
return startDeadlockCheck;
`else
return False;
`endif
endmethod
method doStats = coreFix.doStatsIfc._read;
endinterface);
RenameStage renameStage <- mkRenameStage(renameInput);
// commit stage
let commitInput = (interface CommitInput;
interface robIfc = rob;
interface rtIfc = regRenamingTable;
interface csrfIfc = csrf;
method stbEmpty = stb.isEmpty;
method stqEmpty = lsq.stqEmpty;
method lsqSetAtCommit = lsq.setAtCommit;
method tlbNoPendingReq = iTlb.noPendingReq && dTlb.noPendingReq;
method setFlushTlbs = flush_tlbs._write(True);
method setUpdateVMInfo = update_vm_info._write(True);
method setFlushReservation = flush_reservation._write(True);
method setFlushBrPred = flush_brpred._write(True);
method setFlushCaches = flush_caches._write(True);
method setReconcileI = reconcile_i._write(True);
method setReconcileD = reconcile_d._write(True);
method killAll = coreFix.killAll;
method redirectPc = fetchStage.redirect;
method setFetchWaitRedirect = fetchStage.setWaitRedirect;
method incrementEpoch = epochManager.incrementEpoch;
method commitCsrInstOrInterrupt = csrInstOrInterruptInflight_commit._write(False);
method doStats = coreFix.doStatsIfc._read;
method Bool checkDeadlock;
`ifdef CHECK_DEADLOCK
return startDeadlockCheck;
`else
return False;
`endif
endmethod
endinterface);
CommitStage commitStage <- mkCommitStage(commitInput);
// send rob enq time to reservation stations
(* fire_when_enabled, no_implicit_conditions *)
rule sendRobEnqTime;
InstTime t = rob.getEnqTime;
reservationStationMem.setRobEnqTime(t);
for(Integer i = 0; i < valueof(FpuMulDivExeNum); i = i+1) begin
reservationStationFpuMulDiv[i].setRobEnqTime(t);
end
for(Integer i = 0; i < valueof(AluExeNum); i = i+1) begin
reservationStationAlu[i].setRobEnqTime(t);
end
endrule
// preempt has 2 functions here
// 1. break scheduling cycles
// 2. XXX since csrf is configReg now, we should not let this rule fire together with doCommit
// because we read csrf here and write csrf in doCommit
// TODO We can use wires to catch flush / updateVM enable sigals, because
// there cannot be any instruction in pipeline (there can be poisoned inst
// which cannot change CSR or link reg in D$), so doCommit cannot fire.
// MMIO manager may change pending interrupt bits, but will not affect VM
// info.
(* preempts = "prepareCachesAndTlbs, commitStage.doCommitTrap_handle" *)
(* preempts = "prepareCachesAndTlbs, commitStage.doCommitSystemInst" *)
rule prepareCachesAndTlbs(flush_reservation || flush_tlbs || update_vm_info);
if (flush_reservation) begin
flush_reservation <= False;
dMem.resetLinkAddr;
end
if (flush_tlbs) begin
flush_tlbs <= False;
iTlb.flush;
dTlb.flush;
end
if (update_vm_info) begin
update_vm_info <= False;
let vmI = csrf.vmI;
let vmD = csrf.vmD;
iTlb.updateVMInfo(vmI);
dTlb.updateVMInfo(vmD);
l2Tlb.updateVMInfo(vmI, vmD);
end
endrule
`ifdef SECURITY
// Use wires to capture flush regs and empty signals. This is ok because
// there cannot be any activity to make empty -> not-empty or need-flush ->
// no-need-flush when we are trying to flush.
PulseWire doFlushCaches <- mkPulseWire;
PulseWire doFlushBrPred <- mkPulseWire;
rule setDoFlushCaches(flush_caches && fetchStage.emptyForFlush && lsq.noWrongPathLoads);
doFlushCaches.send;
endrule
rule setDoFlushBrPred(flush_brpred && fetchStage.emptyForFlush);
doFlushBrPred.send;
endrule
// security flush cache: need to wait for wrong path loads or inst fetches
// to finish
rule flushCaches(doFlushCaches);
flush_caches <= False;
iMem.flush;
dMem.flush;
endrule
// security flush branch predictors: wait for wrong path inst fetches to
// finish
rule flushBrPred(doFlushBrPred);
flush_brpred <= False;
fetchStage.flush_predictors;
endrule
`endif
`ifdef SELF_INV_CACHE
// Use wires to capture flush regs and empty signals. This is ok because
// there cannot be any activity to make empty -> not-empty or need-flush ->
// no-need-flush when we are trying to flush.
PulseWire doReconcileI <- mkPulseWire;
// We don't really need to wait for fetch to be empty, but just in case we
// back pressure I TLB because I$ is reconciling.
rule setDoReconcileI(reconcile_i && fetchStage.emptyForFlush);
doReconcileI.send;
endrule
rule reconcileI(doReconcileI);
reconcile_i <= False;
iMem.reconcile;
endrule
`ifdef SYSTEM_SELF_INV_L1D
PulseWire doReconcileD <- mkPulseWire;
Reg#(Bool) waitReconcileD <- mkReg(False);
// We don't really need to wait for lsq empty, but just in case
rule setDoReconcileD(reconcile_d && lsq.noWrongPathLoads);
doReconcileD.send;
endrule
rule startReconcileD(doReconcileD && !waitReconcileD);
coreFix.memExeIfc.reconcile.request.put(?);
waitReconcileD <= True;
endrule
rule completeReconcileD(waitReconcileD);
let unused <- coreFix.memExeIfc.reconcile.response.get;
waitReconcileD <= False;
reconcile_d <= False;
endrule
`endif // SYSTEM_SELF_INV_L1D
`endif // SELF_INV_CACHE
rule readyToFetch(
!flush_reservation && !flush_tlbs && !update_vm_info
&& iTlb.flush_done && dTlb.flush_done
`ifdef SECURITY
&& !flush_caches && !flush_brpred
&& iMem.flush_done && dMem.flush_done
&& fetchStage.flush_predictors_done
`endif
`ifdef SELF_INV_CACHE
&& !reconcile_i && iMem.reconcile_done
`ifdef SYSTEM_SELF_INV_L1D
&& !reconcile_d
`endif
`endif
);
fetchStage.done_flushing();
endrule
`ifdef PERF_COUNT
// incr cycle count
(* fire_when_enabled, no_implicit_conditions *)
rule incCycleCnt(doStats);
cycleCnt.incr(1);
endrule
// incr buffer full cycles
(* fire_when_enabled, no_implicit_conditions *)
rule incLdQFull(doStats && lsq.ldqFull_ehrPort0);
ldqFullCycles.incr(1);
endrule
(* fire_when_enabled, no_implicit_conditions *)
rule incStQFull(doStats && lsq.stqFull_ehrPort0);
stqFullCycles.incr(1);
endrule
(* fire_when_enabled, no_implicit_conditions *)
rule incROBFull(doStats && rob.isFull_ehrPort0);
robFullCycles.incr(1);
endrule
(* fire_when_enabled, no_implicit_conditions *)
rule incAluRS0Full(doStats && reservationStationAlu[0].isFull_ehrPort0);
aluRS0FullCycles.incr(1);
endrule
(* fire_when_enabled, no_implicit_conditions *)
rule incAluRS1Full(doStats && reservationStationAlu[1].isFull_ehrPort0);
aluRS1FullCycles.incr(1);
endrule
(* fire_when_enabled, no_implicit_conditions *)
rule incFpuMulDivRSFull(doStats && reservationStationFpuMulDiv[0].isFull_ehrPort0);
fpuMulDivRSFullCycles.incr(1);
endrule
(* fire_when_enabled, no_implicit_conditions *)
rule incMemRSFull(doStats && reservationStationMem.isFull_ehrPort0);
memRSFullCycles.incr(1);
endrule
(* fire_when_enabled, no_implicit_conditions *)
rule incEpochFull(doStats && epochManager.isFull_ehrPort0);
epochFullCycles.incr(1);
endrule
(* fire_when_enabled, no_implicit_conditions *)
rule incSpecTagFull(doStats && specTagManager.isFull_ehrPort0);
specTagFullCycles.incr(1);
endrule
// broadcast whether we should collect data
rule broadcastDoStats;
let stats = csrf.doPerfStats;
doStats <= stats;
iMem.perf.setStatus(stats);
dMem.perf.setStatus(stats);
iTlb.perf.setStatus(stats);
dTlb.perf.setStatus(stats);
l2Tlb.perf.setStatus(stats);
fetchStage.perf.setStatus(stats);
if(stats && !doStats) begin
$display("[stats] enabled");
end
else if(!stats && doStats) begin
$display("[stats] disabled");
end
endrule
// dispatch perf req
rule dispathPerfReq;
perfReqQ.deq;
let r = perfReqQ.first;
case(r.loc)
ICache: begin
iMem.perf.req(unpack(truncate(r.pType)));
end
DCache: begin
dMem.perf.req(unpack(truncate(r.pType)));
end
ITlb: begin
iTlb.perf.req(unpack(truncate(r.pType)));
end
DTlb: begin
dTlb.perf.req(unpack(truncate(r.pType)));
end
L2Tlb: begin
l2Tlb.perf.req(unpack(truncate(r.pType)));
end
DecStage: begin
fetchStage.perf.req(unpack(truncate(r.pType)));
end
ExeStage: begin
exePerfReqQ.enq(unpack(truncate(r.pType)));
end
ComStage: begin
comPerfReqQ.enq(unpack(truncate(r.pType)));
end
CoreSize: begin
sizePerfReqQ.enq(unpack(truncate(r.pType)));
end
default: begin
$fwrite(stderr, "[WARNING] unrecognzied perf req location ", fshow(r.loc), "\n");
doAssert(False, "unknown perf location");
end
endcase
endrule
// handle perf req: exe stage
rule readPerfCnt_Exe;
function Data getAluCnt(ExeStagePerfType pType);
Data cnt = 0;
for(Integer i = 0; i < valueof(AluExeNum); i = i+1) begin
cnt = cnt + coreFix.aluExeIfc[i].getPerf(pType);
end
return cnt;
endfunction
function Data getFpuMulDivCnt(ExeStagePerfType pType);
Data cnt = 0;
for(Integer i = 0; i < valueof(FpuMulDivExeNum); i = i+1) begin
cnt = cnt + coreFix.fpuMulDivExeIfc[i].getPerf(pType);
end
return cnt;
endfunction
let pType <- toGet(exePerfReqQ).get;
Data data = (case(pType)
SupRenameCnt, SpecNoneCycles, SpecNonMemCycles: renameStage.getPerf(pType);
ExeRedirectBr, ExeRedirectJr, ExeRedirectOther: getAluCnt(pType);
ExeTlbExcep, ExeScSuccessCnt,
ExeLrScAmoAcqCnt, ExeLrScAmoRelCnt,
ExeFenceAcqCnt, ExeFenceRelCnt, ExeFenceCnt,
ExeLdStallByLd, ExeLdStallBySt, ExeLdStallBySB,
ExeLdForward, ExeLdMemLat, ExeStMemLat,
ExeLdToUseLat, ExeLdToUseCnt: coreFix.memExeIfc.getPerf(pType);
ExeIntMulCnt, ExeIntDivCnt,
ExeFpFmaCnt, ExeFpDivCnt, ExeFpSqrtCnt: getFpuMulDivCnt(pType);
default: 0;
endcase);
exePerfRespQ.enq(PerfResp {
pType: pType,
data: data
});
endrule
// handle perf req: com stage
rule readPerfCnt_Com;
let pType <- toGet(comPerfReqQ).get;
Data data = (case(pType)
CycleCnt: cycleCnt;
default: commitStage.getPerf(pType);
endcase);
comPerfRespQ.enq(PerfResp {
pType: pType,
data: data
});
endrule
// handle perf req: core size
rule readPerfCnt_Size;
let pType <- toGet(sizePerfReqQ).get;
Data data = (case(pType)
LdQFullCycles: ldqFullCycles;
StQFullCycles: stqFullCycles;
ROBFullCycles: robFullCycles;
AluRS0FullCycles: aluRS0FullCycles;
AluRS1FullCycles: aluRS1FullCycles;
FpuMulDivRSFullCycles: fpuMulDivRSFullCycles;
MemRSFullCycles: memRSFullCycles;
EpochFullCycles: epochFullCycles;
SpecTagFullCycles: specTagFullCycles;
default: 0;
endcase);
sizePerfRespQ.enq(PerfResp {
pType: pType,
data: data
});
endrule
// gather perf resp
rule gatherPerfResp;
Maybe#(ProcPerfResp) resp = Invalid;
if(iMem.perf.respValid) begin
let r <- iMem.perf.resp;
resp = Valid(ProcPerfResp {
loc: ICache,
pType: zeroExtend(pack(r.pType)),
data: r.data
});
end
else if(dMem.perf.respValid) begin
let r <- dMem.perf.resp;
resp = Valid(ProcPerfResp {
loc: DCache,
pType: zeroExtend(pack(r.pType)),
data: r.data
});
end
else if(iTlb.perf.respValid) begin
let r <- iTlb.perf.resp;
resp = Valid(ProcPerfResp {
loc: ITlb,
pType: zeroExtend(pack(r.pType)),
data: r.data
});
end
else if(dTlb.perf.respValid) begin
let r <- dTlb.perf.resp;
resp = Valid(ProcPerfResp {
loc: DTlb,
pType: zeroExtend(pack(r.pType)),
data: r.data
});
end
else if(l2Tlb.perf.respValid) begin
let r <- l2Tlb.perf.resp;
resp = Valid(ProcPerfResp {
loc: L2Tlb,
pType: zeroExtend(pack(r.pType)),
data: r.data
});
end
else if(fetchStage.perf.respValid) begin
let r <- fetchStage.perf.resp;
resp = Valid(ProcPerfResp {
loc: DecStage,
pType: zeroExtend(pack(r.pType)),
data: r.data
});
end
else if(exePerfRespQ.notEmpty) begin
let r <- toGet(exePerfRespQ).get;
resp = Valid(ProcPerfResp {
loc: ExeStage,
pType: zeroExtend(pack(r.pType)),
data: r.data
});
end
else if(comPerfRespQ.notEmpty) begin
let r <- toGet(comPerfRespQ).get;
resp = Valid(ProcPerfResp {
loc: ComStage,
pType: zeroExtend(pack(r.pType)),
data: r.data
});
end
else if(sizePerfRespQ.notEmpty) begin
let r <- toGet(sizePerfRespQ).get;
resp = Valid (ProcPerfResp {
loc: CoreSize,
pType: zeroExtend(pack(r.pType)),
data: r.data
});
end
// enq to resp Q
if(resp matches tagged Valid .r) begin
perfRespQ.enq(r);
end
endrule
`endif
interface CoreReq coreReq;
method Action start(
Bit#(64) startpc,
Addr toHostAddr, Addr fromHostAddr
);
fetchStage.start(startpc);
started <= True;
mmio.setHtifAddrs(toHostAddr, fromHostAddr);
// start rename debug
commitStage.startRenameDebug;
endmethod
method Action perfReq(PerfLocation loc, PerfType t);
perfReqQ.enq(ProcPerfReq {
loc: loc,
pType: t
});
endmethod
endinterface
interface CoreIndInv coreIndInv;
method ActionValue#(ProcPerfResp) perfResp;
`ifdef PERF_COUNT
perfRespQ.deq;
return perfRespQ.first;
`else
perfReqQ.deq;
let r = perfReqQ.first;
return ProcPerfResp {
loc: r.loc,
pType: r.pType,
data: 0
};
`endif
endmethod
method terminate = csrf.terminate;
endinterface
interface dCacheToParent = dMem.to_parent;
interface iCacheToParent = iMem.to_parent;
interface tlbToMem = l2Tlb.toMem;
interface mmioToPlatform = mmio.toP;
method sendDoStats = csrf.sendDoStats;
method recvDoStats = csrf.recvDoStats;
// deadlock check
interface CoreDeadlock deadlock;
interface dCacheCRqStuck = dMem.cRqStuck;
interface dCachePRqStuck = dMem.pRqStuck;
interface iCacheCRqStuck = iMem.cRqStuck;
interface iCachePRqStuck = iMem.pRqStuck;
interface renameInstStuck = renameStage.renameInstStuck;
interface renameCorrectPathStuck = renameStage.renameCorrectPathStuck;
interface commitInstStuck = commitStage.commitInstStuck;
interface commitUserInstStuck = commitStage.commitUserInstStuck;
`ifdef CHECK_DEADLOCK
interface checkStarted = toGet(deadlockCheckStartedQ);
`else
interface checkStarted = nullGet;
`endif
endinterface
// rename debug
interface CoreRenameDebug renameDebug;
interface renameErr = commitStage.renameErr;
endinterface
// Bluespec: external interrupt requests targeting Machine and Supervisor modes
method Action setMEIP (v) = csrf.setMEIP (v);
method Action setSEIP (v) = csrf.setSEIP (v);
endmodule

842
src_Core/CPU/CsrFile.bsv Normal file
View File

@@ -0,0 +1,842 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// Portions Copyright (c) Bluespec, Inc.
`include "ProcConfig.bsv"
import Types::*;
import ProcTypes::*;
import DefaultValue::*;
import ConcatReg::*;
import ConfigReg::*;
import Ehr::*;
import Fifo::*;
import Vector::*;
import FIFO::*;
import GetPut::*;
import BuildVector::*;
//import TRNG::*;
interface CsrFile;
// Read
method Data rd(CSR csr);
// normal write by CSRXXX inst to any CSR
method Action csrInstWr(CSR csr, Data x);
// normal write by FPU inst to FPU CSR
method Bool fpuInstNeedWr(Bit#(5) fflags, Bool fpu_dirty);
method Action fpuInstWr(Bit#(5) fflags); // FPU must become dirty
// Methods for handling traps
method Maybe#(Interrupt) pending_interrupt;
method ActionValue#(Addr) trap(Trap t, Addr pc, Addr faultAddr);
method ActionValue#(Addr) sret;
method ActionValue#(Addr) mret;
// Outputs for CSRs that the rest of the processor needs to know about
method VMInfo vmI;
method VMInfo vmD;
method CsrDecodeInfo decodeInfo;
// Updating minstret CSR outside of normal CSR write instructions. This
// increment will see the effect of normal CSR write.
method Action incInstret(SupCnt x);
// update copy of mtime
method Action setTime(Data t);
// MSIP/MTIP bits for external world (e.g., for MMIO and timer interrupt).
// XXX These methods should only be called when the processor backend
// pipeline does not contain any CSRXXX inst or corresponding interrupt
// inst (the inst which is turned into an interrupt). This ensures that
// CSRXXX and interrupt are handled atomically. MSIP/MTIP should not
// affect other insts (e.g., address translation of loads/stores),
// synchronous exceptions and other types of interrupts.
method Bit#(1) getMSIP;
method Action setMSIP(Bit#(1) v);
method Action setMTIP(Bit#(1) v);
// Bluespec: external interrupts targeting machine and supervisor modes
method Action setMEIP (Bit #(1) v);
method Action setSEIP (Bit #(1) v);
// performance stats is collected or not
method Bool doPerfStats;
// send/recv updates on stats CSR globally
method ActionValue#(Bool) sendDoStats;
method Action recvDoStats(Bool s);
// terminate
method ActionValue#(void) terminate;
endinterface
// Fancy Reg functions
function Reg#(Bit#(n)) truncateReg(Reg#(Bit#(m)) r) provisos (Add#(a__,n,m));
return (interface Reg;
method Bit#(n) _read = truncate(r._read);
method Action _write(Bit#(n) x) = r._write({truncateLSB(r._read), x});
endinterface);
endfunction
function Reg#(Bit#(n)) truncateRegLSB(Reg#(Bit#(m)) r) provisos (Add#(a__,n,m));
return (interface Reg;
method Bit#(n) _read = truncateLSB(r._read);
method Action _write(Bit#(n) x) = r._write({x, truncate(r._read)});
endinterface);
endfunction
function Reg#(Bit#(n)) zeroExtendReg(Reg#(Bit#(m)) r) provisos (Add#(a__,m,n));
return (interface Reg;
method Bit#(n) _read = zeroExtend(r._read);
method Action _write(Bit#(n) x) = r._write(truncate(x));
endinterface);
endfunction
function Reg#(t) readOnlyReg(t r);
return (interface Reg;
method t _read = r;
method Action _write(t x) = noAction;
endinterface);
endfunction
// module version of readOnlyReg for convenience
module mkReadOnlyReg#(t x)(Reg#(t));
return readOnlyReg(x);
endmodule
function Reg#(t) regFromReadOnly(ReadOnly#(t) r);
return (interface Reg;
method t _read = r._read;
method Action _write(t x);
noAction;
endmethod
endinterface);
endfunction
function Reg#(t) addWriteSideEffect(Reg#(t) r, Action a);
return (interface Reg;
method t _read = r._read;
method Action _write(t x);
r._write(x);
a;
endmethod
endinterface);
endfunction
function Bool has_csr_permission(CSR csr, Bit#(2) prv, Bool write);
Bit#(12) csr_index = pack(csr);
return ((prv >= csr_index[9:8]) && (!write || (csr_index[11:10] != 2'b11)));
endfunction
// non-standard terminate CSR
interface Terminate;
interface Reg#(Data) reg_ifc;
method ActionValue#(void) terminate;
endinterface
module mkTerminate(Terminate);
FIFO#(void) terminateQ <- mkFIFO1;
interface Reg reg_ifc;
method Action _write(Data x);
terminateQ.enq(?);
$display(
"[Terminate CSR] being written (val = %x), ",
"send terminate signal to host", x
);
endmethod
method Data _read = 0;
endinterface
method terminate = toGet(terminateQ).get;
endmodule
// stats CSR: there is only one copy in the whole multiprocessor, so any write
// to stats CSR will be broadcasted
interface StatsCsr;
interface Reg#(Data) reg_ifc;
method Bool doPerfStats;
// send/recv updates on stats CSR globally
method ActionValue#(Bool) sendDoStats;
method Action recvDoStats(Bool s);
endinterface
module mkStatsCsr(StatsCsr);
Reg#(Bool) doStats <- mkConfigReg(False);
FIFO#(Bool) writeQ <- mkFIFO1;
interface Reg reg_ifc;
method Data _read = zeroExtend(pack(doStats));
method Action _write(Data x);
writeQ.enq(unpack(truncate(x)));
endmethod
endinterface
method Bool doPerfStats = doStats;
method ActionValue#(Bool) sendDoStats;
writeQ.deq;
return writeQ.first;
endmethod
method Action recvDoStats(Bool s);
doStats <= s;
endmethod
endmodule
// same as EHR except that read port 0 is not ordered with other methods. Read
// port 1 will still get bypassing from write port 0.
module mkConfigEhr#(t init)(Ehr#(n, t)) provisos(Bits#(t, w));
Ehr#(n, t) data <- mkEhr(init);
Wire#(t) read <- mkBypassWire;
(* fire_when_enabled, no_implicit_conditions *)
rule setRead;
read <= data[0];
endrule
Ehr#(n, t) ifc = ?;
ifc[0] = (interface Reg;
method _read = read._read;
method _write = data[0]._write;
endinterface);
for(Integer i = 1; i < valueOf(n); i = i+1) begin
ifc[i] = (interface Reg;
method _read = data[i]._read;
method _write = data[i]._write;
endinterface);
end
return ifc;
endmodule
module mkCsrFile #(Data hartid)(CsrFile);
RiscVISASubset isa = defaultValue;
// To save from bypassing logic, CSR reads will get stale value
let mkCsrReg = mkConfigReg;
let mkCsrEhr = mkConfigEhr;
// current prv level (this is not a csr...)
Reg#(Bit#(2)) prv_reg <- mkCsrReg(prvM);
// Machine level CSRs
// mstatus
Reg#(Bit#(2)) xs_reg <- mkReadOnlyReg(0); // XXX no extension
Reg#(Bit#(2)) fs_reg <- (isa.f || isa.d) ? mkCsrReg(0) : mkReadOnlyReg(0);
Reg#(Bit#(1)) sd_reg = readOnlyReg(
((xs_reg == 2'b11) || (fs_reg == 2'b11)) ? 1 : 0
);
Reg#(Bit#(2)) sxl_reg = readOnlyReg(getXLBits);
Reg#(Bit#(2)) uxl_reg = readOnlyReg(getXLBits);
Reg#(Bit#(1)) tsr_reg <- mkCsrReg(0);
Reg#(Bit#(1)) tw_reg <- mkCsrReg(0);
Reg#(Bit#(1)) tvm_reg <- mkCsrReg(0);
Reg#(Bit#(1)) mxr_reg <- mkCsrReg(0);
Reg#(Bit#(1)) sum_reg <- mkCsrReg(0);
Reg#(Bit#(1)) mprv_reg <- mkCsrReg(0);
Reg#(Bit#(2)) mpp_reg <- mkCsrReg(0);
Reg#(Bit#(1)) spp_reg <- mkCsrReg(0);
Vector#(4, Reg#(Bit#(2))) prev_prv_vec = vec(
// prev_prv_vec[x]: privilege mode before trapping into mode x
readOnlyReg(prvU), // upp
concatReg2(readOnlyReg(1'b0), spp_reg), // spp
readOnlyReg(2'b0), // reserved
mpp_reg
);
Vector#(4, Reg#(Bit#(1))) ie_vec = replicate(
readOnlyReg(0) // ie_vec[x]: interrupt enable for mode x
);
ie_vec[prvU] <- mkCsrReg(0);
ie_vec[prvS] <- mkCsrReg(0);
ie_vec[prvM] <- mkCsrReg(0);
Vector#(4, Reg#(Bit#(1))) prev_ie_vec = replicate(
readOnlyReg(0) // prev_ie_vec[x]: ie_vec[x] before trapping into mode x
);
prev_ie_vec[prvU] <- mkCsrReg(0);
prev_ie_vec[prvS] <- mkCsrReg(0);
prev_ie_vec[prvM] <- mkCsrReg(0);
Reg#(Data) mstatus_csr = concatReg24(
sd_reg, readOnlyReg(27'b0), sxl_reg, uxl_reg, readOnlyReg(9'b0),
tsr_reg, tw_reg, tvm_reg, mxr_reg, sum_reg, mprv_reg, xs_reg, fs_reg,
mpp_reg, readOnlyReg(2'b0), spp_reg,
prev_ie_vec[prvM], readOnlyReg(1'b0),
prev_ie_vec[prvS], prev_ie_vec[prvU],
ie_vec[prvM], readOnlyReg(1'b0),
ie_vec[prvS], ie_vec[prvU]
);
// misa
Reg#(Data) misa_csr = readOnlyReg({getXLBits, 36'b0, getExtensionBits(isa)});
// medeleg: some exceptions don't exist, fix corresponding bits to 0
Reg#(Bit#(1)) medeleg_15_reg <- mkCsrReg(0); // cause 15
Reg#(Bit#(3)) medeleg_13_11_reg <- mkCsrReg(0); // case 13-11
Reg#(Bit#(10)) medeleg_9_0_reg <- mkCsrReg(0); // cause 9-0
Reg#(Data) medeleg_csr = concatReg6(
readOnlyReg(48'b0), medeleg_15_reg,
readOnlyReg(1'b0), medeleg_13_11_reg,
readOnlyReg(1'b0), medeleg_9_0_reg
);
// mideleg: some interrupts don't exist, fix corresponding bits to 0
Reg#(Bit#(1)) mideleg_11_reg <- mkCsrReg(0);
Reg#(Bit#(3)) mideleg_9_7_reg <- mkCsrReg(0);
Reg#(Bit#(3)) mideleg_5_3_reg <- mkCsrReg(0);
Reg#(Bit#(2)) mideleg_1_0_reg <- mkCsrReg(0);
Reg#(Data) mideleg_csr = concatReg8(
readOnlyReg(52'b0), mideleg_11_reg,
readOnlyReg(1'b0), mideleg_9_7_reg,
readOnlyReg(1'b0), mideleg_5_3_reg,
readOnlyReg(1'b0), mideleg_1_0_reg
);
// mie
Vector#(4, Reg#(Bit#(1))) external_int_en_vec = replicate(readOnlyReg(0));
external_int_en_vec[prvU] <- mkCsrReg(0);
external_int_en_vec[prvS] <- mkCsrReg(0);
external_int_en_vec[prvM] <- mkCsrReg(0);
Vector#(4, Reg#(Bit#(1))) timer_int_en_vec = replicate(readOnlyReg(0));
timer_int_en_vec[prvU] <- mkCsrReg(0);
timer_int_en_vec[prvS] <- mkCsrReg(0);
timer_int_en_vec[prvM] <- mkCsrReg(0);
Vector#(4, Reg#(Bit#(1))) software_int_en_vec = replicate(readOnlyReg(0));
software_int_en_vec[prvU] <- mkCsrReg(0);
software_int_en_vec[prvS] <- mkCsrReg(0);
software_int_en_vec[prvM] <- mkCsrReg(0);
Reg#(Data) mie_csr = concatReg13(
readOnlyReg(52'b0),
external_int_en_vec[prvM], readOnlyReg(1'b0),
external_int_en_vec[prvS], external_int_en_vec[prvU],
timer_int_en_vec[prvM], readOnlyReg(1'b0),
timer_int_en_vec[prvS], timer_int_en_vec[prvU],
software_int_en_vec[prvM], readOnlyReg(1'b0),
software_int_en_vec[prvS], software_int_en_vec[prvU]
);
// mtvec
Reg#(Bit#(62)) mtvec_base_hi_reg <- mkCsrReg(0); // this is BASE[63:2]
Reg#(Bit#(1)) mtvec_mode_low_reg <- mkCsrReg(0); // this is MODE[0]
Reg#(Data) mtvec_csr = concatReg3(
mtvec_base_hi_reg, readOnlyReg(1'b0), mtvec_mode_low_reg
);
// mcounteren
Reg#(Bit#(1)) mcounteren_ir_reg <- mkCsrReg(0);
Reg#(Bit#(1)) mcounteren_tm_reg <- mkCsrReg(0);
Reg#(Bit#(1)) mcounteren_cy_reg <- mkCsrReg(0);
Reg#(Data) mcounteren_csr = concatReg5(
readOnlyReg(32'b0),
readOnlyReg(29'b0), // hpmcounter 3-31 not accessible in S mode
mcounteren_ir_reg, mcounteren_tm_reg, mcounteren_cy_reg
);
// mscratch
Reg#(Data) mscratch_csr <- mkCsrReg(0);
// mepc: FIXME Since we don't have C extension, mepc should be 4-byte
// aligned. However, spike is not checking this, so we don't implement it.
Reg#(Data) mepc_csr <- mkCsrReg(0);
// mcause
Reg#(Bit#(1)) mcause_interrupt_reg <- mkCsrReg(0);
Reg#(Bit#(4)) mcause_code_reg <- mkCsrReg(0);
Reg#(Data) mcause_csr = concatReg3(
mcause_interrupt_reg, readOnlyReg(59'b0), mcause_code_reg
);
// mtval (mbadaddr in spike)
Reg#(Data) mtval_csr <- mkCsrReg(0);
// mip
Vector#(4, Reg#(Bit#(1))) external_int_pend_vec = replicate(readOnlyReg(0));
external_int_pend_vec[prvU] <- mkCsrReg(0);
external_int_pend_vec[prvS] <- mkCsrReg(0);
external_int_pend_vec[prvM] <- mkCsrReg(0);
Vector#(4, Reg#(Bit#(1))) timer_int_pend_vec = replicate(readOnlyReg(0));
timer_int_pend_vec[prvU] <- mkCsrReg(0);
timer_int_pend_vec[prvS] <- mkCsrReg(0);
timer_int_pend_vec[prvM] <- mkCsrReg(0);
Vector#(4, Reg#(Bit#(1))) software_int_pend_vec = replicate(readOnlyReg(0));
software_int_pend_vec[prvU] <- mkCsrReg(0);
software_int_pend_vec[prvS] <- mkCsrReg(0);
software_int_pend_vec[prvM] <- mkCsrReg(0);
Reg#(Data) mip_csr = concatReg13(
readOnlyReg(52'b0),
external_int_pend_vec[prvM], readOnlyReg(1'b0),
external_int_pend_vec[prvS], external_int_pend_vec[prvU],
readOnlyReg(timer_int_pend_vec[prvM]), // MTIP is read-only to software
readOnlyReg(1'b0),
timer_int_pend_vec[prvS], timer_int_pend_vec[prvU],
software_int_pend_vec[prvM], readOnlyReg(1'b0),
software_int_pend_vec[prvS], software_int_pend_vec[prvU]
);
// minstret
Ehr#(2, Data) minstret_ehr <- mkCsrEhr(0);
Reg#(Data) minstret_csr = minstret_ehr[0];
// mcycle
Ehr#(2, Data) mcycle_ehr <- mkCsrEhr(0);
Reg#(Data) mcycle_csr = mcycle_ehr[0];
// mvendorid
Reg#(Data) mvendorid_csr = readOnlyReg(0);
// marchid
Reg#(Data) marchid_csr = readOnlyReg(0);
// mimpid
Reg#(Data) mimpid_csr = readOnlyReg(0);
// mhartid
Reg#(Data) mhartid_csr = readOnlyReg(hartid);
// Supervisor level CSRs
// sstatus: restricted view of mstatus
Reg#(Data) sstatus_csr = concatReg17(
sd_reg, readOnlyReg(29'b0), uxl_reg, readOnlyReg(12'b0),
mxr_reg, sum_reg, readOnlyReg(1'b0), xs_reg, fs_reg,
readOnlyReg(4'b0), spp_reg,
readOnlyReg(2'b0), prev_ie_vec[prvS], prev_ie_vec[prvU],
readOnlyReg(2'b0), ie_vec[prvS], ie_vec[prvU]
);
// sie: restricted view of mie
Reg#(Data) sie_csr = concatReg9(
readOnlyReg(54'b0),
external_int_en_vec[prvS], external_int_en_vec[prvU],
readOnlyReg(2'b0),
timer_int_en_vec[prvS], timer_int_en_vec[prvU],
readOnlyReg(2'b0),
software_int_en_vec[prvS], software_int_en_vec[prvU]
);
// stvec
Reg#(Bit#(62)) stvec_base_hi_reg <- mkCsrReg(0); // BASE[63:2]
Reg#(Bit#(1)) stvec_mode_low_reg <- mkCsrReg(0); // MODE[0]
Reg#(Data) stvec_csr = concatReg3(
stvec_base_hi_reg, readOnlyReg(1'b0), stvec_mode_low_reg
);
// scounteren
Reg#(Bit#(1)) scounteren_ir_reg <- mkCsrReg(0);
Reg#(Bit#(1)) scounteren_tm_reg <- mkCsrReg(0);
Reg#(Bit#(1)) scounteren_cy_reg <- mkCsrReg(0);
Reg#(Data) scounteren_csr = concatReg5(
readOnlyReg(32'b0),
readOnlyReg(29'b0), // hpmcounter 3-31 not accessible in U mode
scounteren_ir_reg, scounteren_tm_reg, scounteren_cy_reg
);
// sscratch
Reg#(Data) sscratch_csr <- mkCsrReg(0);
// sepc: FIXME Since we don't have C extension, sepc should be 4-byte
// aligned. However, spike is not checking this, so we don't implement it.
Reg#(Data) sepc_csr <- mkCsrReg(0);
// scause
Reg#(Bit#(1)) scause_interrupt_reg <- mkCsrReg(0);
Reg#(Bit#(4)) scause_code_reg <- mkCsrReg(0);
Reg#(Data) scause_csr = concatReg3(
scause_interrupt_reg, readOnlyReg(59'b0), scause_code_reg
);
// stval (sbadaddr in spike)
Reg#(Data) stval_csr <- mkCsrReg(0);
// sip: restricted view of mip
Reg#(Data) sip_csr = concatReg9(
readOnlyReg(54'b0),
external_int_pend_vec[prvS], external_int_pend_vec[prvU],
readOnlyReg(2'b0),
timer_int_pend_vec[prvS], timer_int_pend_vec[prvU],
readOnlyReg(2'b0),
software_int_pend_vec[prvS], software_int_pend_vec[prvU]
);
// satp (sptbr in spike): FIXME we only support Bare and Sv39, so we hack
// the encoding of mode[3:0] field. Only mode[3] is relevant, other bits
// are always 0
Reg#(Bit#(1)) vm_mode_sv39_reg <- mkCsrReg(0);
Reg#(Bit#(4)) vm_mode_reg = concatReg2(vm_mode_sv39_reg, readOnlyReg(3'b0));
Reg#(Asid) asid_reg <- mkCsrReg(0);
Reg#(Bit#(16)) full_asid_reg = zeroExtendReg(asid_reg);
Reg#(Bit#(44)) ppn_reg <- mkCsrReg(0);
Reg#(Data) satp_csr = concatReg3(vm_mode_reg, full_asid_reg, ppn_reg);
// User level CSRs
// According to spike, any write to fflags/frm/fcsr will set fs_reg as
// dirty, regardless of whether the write truly changes value or not.
// Besides, any non-zero FP exception flags will also make fs_reg dirty.
// fflags: if we directly change fflags_reg (instead of fflags_csr), then
// we must set fs_reg manually
Reg#(Bit#(5)) fflags_reg <- mkCsrReg(0);
Reg#(Data) fflags_csr = addWriteSideEffect(
zeroExtendReg(fflags_reg), fs_reg._write(2'b11)
);
// frm: if we directly change frm_reg (instead of frm_csr), then we must
// set fs_reg manually
Reg#(Bit#(3)) frm_reg <- mkCsrReg(0);
Reg#(Data) frm_csr = addWriteSideEffect(
zeroExtendReg(frm_reg), fs_reg._write(2'b11)
);
// fcsr
Reg#(Data) fcsr_csr = addWriteSideEffect(
zeroExtendReg(concatReg2(frm_reg, fflags_reg)), fs_reg._write(2'b11)
);
// cycle
Reg#(Data) cycle_csr = readOnlyReg(mcycle_csr);
// time
Reg#(Data) time_reg <- mkCsrReg(0);
Reg#(Data) time_csr = readOnlyReg(time_reg);
// instret
Reg#(Data) instret_csr = readOnlyReg(minstret_csr);
// terminate (non-standard)
Terminate terminate_module <- mkTerminate;
Reg#(Data) terminate_csr = terminate_module.reg_ifc;
// whether performance stats is collected
StatsCsr stats_module <- mkStatsCsr;
Reg#(Data) stats_csr = stats_module.reg_ifc;
`ifdef SECURITY
// sanctum machine CSRs
// ### Enclave virtual base and mask
// (per-core) registers
// ( defines a virtual region for which enclave page tables are used in
// place of OS-controlled page tables)
// (machine-mode non-standard read/write)
Reg#(Data) mevbase_csr <- mkCsrReg(maxBound); // impossible base & mask,
Reg#(Data) mevmask_csr <- mkCsrReg(0); // so no enclave accesses are possible
// ### Enclave page table base
// (per core) register
// ( pointer to a separate page table data structure used to translate enclave
// virtual addresses)
// (machine-mode non-standard read/write)
Reg#(Bit#(44)) eppn_reg <- mkCsrReg(0);
Reg#(Data) meatp_csr = zeroExtendReg(eppn_reg);
// ### DRAM bitmap
// (per core) registers (OS and Enclave)
// ( white-lists the DRAM regions the core is allowed to access via OS and
// enclave virtual addresses)
// (machine-mode non-standard read/write)
Reg#(Data) mmrbm_csr <- mkCsrReg(maxBound);
Reg#(Data) memrbm_csr <- mkCsrReg(0);
// ### Protected region base and mask
// (per core) registers (OS and Enclave)
// ( these are used to prevent address translation into a specific range of
// physical addresses, for example to protect the security monitor from all software)
// (machine-mode non-standard read/write)
Reg#(Data) mparbase_csr <- mkCsrReg(maxBound);
Reg#(Data) mparmask_csr <- mkCsrReg(0);
Reg#(Data) meparbase_csr <- mkCsrReg(0);
Reg#(Data) meparmask_csr <- mkCsrReg(0);
// ### Turn on/off speculation
Reg#(Bit#(2)) mspec_reg <- mkCsrReg(mSpecAll);
Reg#(Data) mspec_csr = zeroExtendReg(mspec_reg);
// sanctum user CSR
// ### true random number
// For now, we skip secure boot, keep TRNG = 0
Reg#(Data) trng_csr <- mkReadOnlyReg(0); //mkTRNG;
`endif
rule incCycle;
mcycle_ehr[1] <= mcycle_ehr[1] + 1;
endrule
// Function for getting a csr given an index
function Reg#(Data) get_csr(CSR csr);
return (case (csr)
// User CSRs
CSRfflags: fflags_csr;
CSRfrm: frm_csr;
CSRfcsr: fcsr_csr;
CSRcycle: cycle_csr;
CSRtime: time_csr;
CSRinstret: instret_csr;
CSRterminate: terminate_csr;
CSRstats: stats_csr;
// Supervisor CSRs
CSRsstatus: sstatus_csr;
CSRsie: sie_csr;
CSRstvec: stvec_csr;
CSRscounteren: scounteren_csr;
CSRsscratch: sscratch_csr;
CSRsepc: sepc_csr;
CSRscause: scause_csr;
CSRstval: stval_csr;
CSRsip: sip_csr;
CSRsatp: satp_csr;
// Machine CSRs
CSRmstatus: mstatus_csr;
CSRmisa: misa_csr;
CSRmedeleg: medeleg_csr;
CSRmideleg: mideleg_csr;
CSRmie: mie_csr;
CSRmtvec: mtvec_csr;
CSRmcounteren: mcounteren_csr;
CSRmscratch: mscratch_csr;
CSRmepc: mepc_csr;
CSRmcause: mcause_csr;
CSRmtval: mtval_csr;
CSRmip: mip_csr;
CSRmcycle: mcycle_csr;
CSRminstret: minstret_csr;
CSRmvendorid: mvendorid_csr;
CSRmarchid: marchid_csr;
CSRmimpid: mimpid_csr;
CSRmhartid: mhartid_csr;
`ifdef SECURITY
CSRmevbase: mevbase_csr;
CSRmevmask: mevmask_csr;
CSRmeatp: meatp_csr;
CSRmmrbm: mmrbm_csr;
CSRmemrbm: memrbm_csr;
CSRmparbase: mparbase_csr;
CSRmparmask: mparmask_csr;
CSRmeparbase: meparbase_csr;
CSRmeparmask: meparmask_csr;
CSRmspec: mspec_csr;
CSRtrng: trng_csr;
`endif
default: readOnlyReg(64'b0);
endcase);
endfunction
method Data rd(CSR csr);
return get_csr(csr)._read;
endmethod
method Action csrInstWr(CSR csr, Data x);
get_csr(csr)._write(x);
endmethod
method Bool fpuInstNeedWr(Bit#(5) fflags, Bool fpu_dirty);
Bool fflags_change = (fflags & fflags_reg) != fflags;
// we need to set fs_reg as dirty in two cases
// 1. FP reg is written (i.e., fpu_dirty)
// 2. FP exception (i.e., fflags) is non-zero (try to match spike)
Bool need_set_dirty = fs_reg != 2'b11 && (fpu_dirty || fflags != 0);
return fflags_change || need_set_dirty;
endmethod
method Action fpuInstWr(Bit#(5) fflags);
fs_reg <= 2'b11; // FPU must be dirty
fflags_reg <= fflags_reg | fflags;
endmethod
method Maybe#(Interrupt) pending_interrupt;
// first get all the pending interrupts
Bit#(InterruptNum) pend_ints = truncate(mie_csr & mip_csr);
// now find out all the truly enabled interrupts (that needs handling)
Bit#(InterruptNum) enabled_ints = 0;
// check interrupts that needs to be handled at M mode: all interrupts
// are by default handled at M mode unless it is delegated in
// mideleg_csr, we just need to ignore those interrupts
if(prv_reg < prvM || (prv_reg == prvM && ie_vec[prvM] == 1)) begin
enabled_ints = pend_ints & ~truncate(mideleg_csr);
end
// check interrupts that needs to be handled at S mode only if no
// interrupt needs to be handled at M mode: interrupts handled at S
// mode must be delegated in mideleg_csr
if (enabled_ints == 0 &&
(prv_reg < prvS || (prv_reg == prvS && ie_vec[prvS] == 1))) begin
enabled_ints = pend_ints & truncate(mideleg_csr);
end
// According to spike, return the interrupt bit at LSB
function Bool isEnabled(Integer i) = (enabled_ints[i] == 1);
Vector#(InterruptNum, Integer) idxVec = genVector;
if(find(isEnabled, idxVec) matches tagged Valid .i) begin
return Valid (unpack(fromInteger(i)));
end
else begin
return Invalid;
end
endmethod
method ActionValue#(Addr) trap(Trap t, Addr pc, Addr addr);
// figure out trap cause & trap val
Bit#(1) cause_interrupt = 0;
Bit#(4) cause_code = 0;
Data trap_val = 0;
case(t) matches
tagged Exception .e: begin
cause_code = pack(e);
trap_val = (case(e)
InstAddrMisaligned, InstAccessFault,
Breakpoint, InstPageFault: return pc;
LoadAddrMisaligned, LoadAccessFault,
StoreAddrMisaligned, StoreAccessFault,
LoadPageFault, StorePageFault: return addr;
default: return 0;
endcase);
end
tagged Interrupt .i: begin
cause_code = pack(i);
cause_interrupt = 1;
end
endcase
// function to figure out next PC
function Addr getNextPc(Bit#(1) mode_low, Bit#(62) base_hi);
Addr base = {base_hi, 2'b0};
if(mode_low == 1 && cause_interrupt == 1) begin
// vector jump: only for interrupt
return base + zeroExtend({cause_code, 2'b0});
end
else begin // direct jump
return base;
end
endfunction
// check if trap is delegated
Bool deleg = prv_reg <= prvS && (case(t) matches
tagged Exception .e: return medeleg_csr[pack(e)] == 1;
tagged Interrupt .i: return mideleg_csr[pack(i)] == 1;
default: return False;
endcase);
// handle the trap
if(deleg) begin // handle in S mode
// ie/prv stack
prev_prv_vec[prvS] <= prv_reg;
prv_reg <= prvS;
prev_ie_vec[prvS] <= ie_vec[prvS];
ie_vec[prvS] <= 0;
// record trap info
sepc_csr <= pc;
scause_interrupt_reg <= cause_interrupt;
scause_code_reg <= cause_code;
stval_csr <= trap_val;
// return next pc
return getNextPc(stvec_mode_low_reg, stvec_base_hi_reg);
end
else begin
// ie/prv stack
prev_prv_vec[prvM] <= prv_reg;
prv_reg <= prvM;
prev_ie_vec[prvM] <= ie_vec[prvM];
ie_vec[prvM] <= 0;
// record trap info
mepc_csr <= pc;
mcause_interrupt_reg <= cause_interrupt;
mcause_code_reg <= cause_code;
mtval_csr <= trap_val;
// return next pc
return getNextPc(mtvec_mode_low_reg, mtvec_base_hi_reg);
end
// XXX yield load reservation should be done outside this method
endmethod
method ActionValue#(Addr) mret;
prv_reg <= prev_prv_vec[prvM];
prev_prv_vec[prvM] <= prvU;
ie_vec[prvM] <= prev_ie_vec[prvM];
prev_ie_vec[prvM] <= 1;
return mepc_csr;
endmethod
method ActionValue#(Addr) sret;
prv_reg <= prev_prv_vec[prvS];
prev_prv_vec[prvS] <= prvU;
ie_vec[prvS] <= prev_ie_vec[prvS];
prev_ie_vec[prvS] <= 1;
return sepc_csr;
endmethod
method VMInfo vmI;
// for inst fetch, NO need to consider MPRV
Bit#(2) prv = prv_reg;
return VMInfo {
prv: prv,
asid: asid_reg,
sv39: prv < prvM && vm_mode_sv39_reg == 1,
exeReadable: mxr_reg == 1,
userAccessibleByS: sum_reg == 1,
basePPN: ppn_reg
`ifdef SECURITY
, sanctum_evbase: mevbase_csr,
sanctum_evmask: mevmask_csr,
sanctum_ebasePPN: eppn_reg,
sanctum_mrbm: mmrbm_csr,
sanctum_emrbm: memrbm_csr,
sanctum_parbase: mparbase_csr,
sanctum_parmask: mparmask_csr,
sanctum_eparbase: meparbase_csr,
sanctum_eparmask: meparmask_csr,
// enclave / security monitor should never execute instructions
// from untrusted shared region
sanctum_authShared: False
`endif
};
endmethod
method VMInfo vmD;
// for load/store, need to consider MPRV
Bit#(2) prv = (mprv_reg == 1) ? prev_prv_vec[prvM] : prv_reg;
return VMInfo {
prv: prv,
asid: asid_reg,
sv39: prv < prvM && vm_mode_sv39_reg == 1,
exeReadable: mxr_reg == 1,
userAccessibleByS: sum_reg == 1,
basePPN: ppn_reg
`ifdef SECURITY
, sanctum_evbase: mevbase_csr,
sanctum_evmask: mevmask_csr,
sanctum_ebasePPN: eppn_reg,
sanctum_mrbm: mmrbm_csr,
sanctum_emrbm: memrbm_csr,
sanctum_parbase: mparbase_csr,
sanctum_parmask: mparmask_csr,
sanctum_eparbase: meparbase_csr,
sanctum_eparmask: meparmask_csr,
// enclave / security monitor can read/write untrusted shared
// region when speculation is off (either by mspec CSR or in M
// mode)
// XXX Because of the effects of mprv, we have to use prv_reg here
// instead of prv. Otherwise, we may be in M mode, but prv=S, and
// still forbid shared accesses
sanctum_authShared: mspec_reg != mSpecAll || prv_reg == prvM
`endif
};
endmethod
method CsrDecodeInfo decodeInfo = CsrDecodeInfo {
frm: frm_reg,
fEnabled: fs_reg != 0,
prv: prv_reg,
trapVM: tvm_reg == 1,
timeoutWait: tw_reg == 1,
trapSret: tsr_reg == 1,
cycleReadableByS: mcounteren_cy_reg == 1,
cycleReadableByU: mcounteren_cy_reg == 1 && scounteren_cy_reg == 1,
instretReadableByS: mcounteren_ir_reg == 1,
instretReadableByU: mcounteren_ir_reg == 1 && scounteren_ir_reg == 1,
timeReadableByS: mcounteren_tm_reg == 1,
timeReadableByU: mcounteren_tm_reg == 1 && scounteren_tm_reg == 1
};
method Action incInstret(SupCnt x);
minstret_ehr[1] <= minstret_ehr[1] + zeroExtend(x);
endmethod
method Action setTime(Data t);
time_reg <= t;
endmethod
method getMSIP = software_int_pend_vec[prvM]._read;
method setMSIP = software_int_pend_vec[prvM]._write;
method setMTIP = timer_int_pend_vec[prvM]._write;
// Bluespec: external interrupts targeting machine and supervisor modes
method Action setMEIP (Bit #(1) v);
external_int_pend_vec[prvM] <= v;
endmethod
method Action setSEIP (Bit #(1) v);
external_int_pend_vec[prvS] <= v;
endmethod
method terminate = terminate_module.terminate;
// performance stats
method doPerfStats = stats_module.doPerfStats;
method sendDoStats = stats_module.sendDoStats;
method recvDoStats = stats_module.recvDoStats;
endmodule

View File

@@ -0,0 +1,277 @@
package LLC_AXI4_Adapter;
// ================================================================
// BSV lib imports
import ConfigReg :: *;
import Assert :: *;
import FIFOF :: *;
import Vector :: *;
// ----------------
// BSV additional libs
import GetPut_Aux :: *;
import Cur_Cycle :: *;
import Semi_FIFOF :: *;
import CreditCounter :: *;
// ================================================================
// Project imports
// ----------------
// From MIT RISCY-OOO
import Types :: *;
import CacheUtils :: *;
import CCTypes :: *;
// ----------------
// From Bluespec Pipes
import AXI4_Types :: *;
import Fabric_Defs :: *;
// ================================================================
interface LLC_AXI4_Adapter_IFC;
method Action reset;
// Fabric master interface for memory
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) mem_master;
endinterface
// ================================================================
module mkLLC_AXi4_Adapter #(MemFifoClient #(idT, childT) llc)
(LLC_AXI4_Adapter_IFC)
provisos(Bits#(idT, a__),
Bits#(childT, b__),
FShow#(ToMemMsg#(idT, childT)),
FShow#(MemRsMsg#(idT, childT)),
Add#(SizeOf#(Line), 0, 512)); // assert Line sz = 512
// Verbosity: 0: quiet; 1: LLC transactions; 2: loop detail
Integer verbosity = 2;
Reg #(Bit #(4)) cfg_verbosity <- mkConfigReg (fromInteger (verbosity));
// ================================================================
// Fabric request/response
AXI4_Master_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master_xactor <- mkAXI4_Master_Xactor_2;
// For discarding write-responses
CreditCounter_IFC #(4) ctr_wr_rsps_pending <- mkCreditCounter; // Max 15 writes outstanding
// ================================================================
// Functions to interact with the fabric
// Send a read-request into the fabric
function Action fa_fabric_send_read_req (Fabric_Addr addr);
action
AXI4_Size size = axsize_8;
let mem_req_rd_addr = AXI4_Rd_Addr {arid: fabric_default_id,
araddr: addr,
arlen: 0, // burst len = arlen+1
arsize: size,
arburst: fabric_default_burst,
arlock: fabric_default_lock,
arcache: fabric_default_arcache,
arprot: fabric_default_prot,
arqos: fabric_default_qos,
arregion: fabric_default_region,
aruser: fabric_default_user};
master_xactor.i_rd_addr.enq (mem_req_rd_addr);
// Debugging
if (cfg_verbosity > 1) begin
$display (" ", fshow (mem_req_rd_addr));
end
endaction
endfunction
// Send a write-request into the fabric
function Action fa_fabric_send_write_req (Fabric_Addr addr, Fabric_Strb strb, Bit #(64) st_val);
action
AXI4_Size size = axsize_8;
let mem_req_wr_addr = AXI4_Wr_Addr {awid: fabric_default_id,
awaddr: addr,
awlen: 0, // burst len = awlen+1
awsize: size,
awburst: fabric_default_burst,
awlock: fabric_default_lock,
awcache: fabric_default_awcache,
awprot: fabric_default_prot,
awqos: fabric_default_qos,
awregion: fabric_default_region,
awuser: fabric_default_user};
let mem_req_wr_data = AXI4_Wr_Data {wid: fabric_default_id,
wdata: st_val,
wstrb: strb,
wlast: True,
wuser: fabric_default_user};
master_xactor.i_wr_addr.enq (mem_req_wr_addr);
master_xactor.i_wr_data.enq (mem_req_wr_data);
// Expect a fabric response
ctr_wr_rsps_pending.incr;
// Debugging
if (cfg_verbosity > 1) begin
$display (" To fabric: ", fshow (mem_req_wr_addr));
$display (" ", fshow (mem_req_wr_data));
end
endaction
endfunction
// ================================================================
// Handle read requests and responses
// Don't do reads while writes are outstanding.
// Each 512b cache line takes 8 beats, each handling 64 bits
Reg #(Bit #(3)) rg_rd_req_beat <- mkReg (0);
Reg #(Bit #(3)) rg_rd_rsp_beat <- mkReg (0);
FIFOF #(LdMemRq #(idT, childT)) f_pending_reads <- mkFIFOF;
Reg #(Bit #(512)) rg_cline <- mkRegU;
rule rl_handle_read_req (llc.toM.first matches tagged Ld .ld
&&& (ctr_wr_rsps_pending.value == 0));
if ((cfg_verbosity > 0) && (rg_rd_req_beat == 0)) begin
$display ("%0d: LLC_AXI4_Adapter.rl_handle_read_req: Ld request from LLC to memory: beat %0d",
cur_cycle, rg_rd_req_beat);
$display (" ", fshow (ld));
end
Addr line_addr = { ld.addr [63:6], 6'h0 }; // Addr of containing cache line
Addr offset = zeroExtend ( { rg_rd_req_beat, 3'b_000 } ); // Addr offset of 64b word for this beat
fa_fabric_send_read_req (line_addr | offset);
if (rg_rd_req_beat == 0)
f_pending_reads.enq (ld);
if (rg_rd_req_beat == 7)
llc.toM.deq;
rg_rd_req_beat <= rg_rd_req_beat + 1;
endrule
rule rl_handle_read_rsps;
let mem_rsp <- pop_o (master_xactor.o_rd_data);
if (cfg_verbosity > 1) begin
$display ("%0d: LLC_AXI4_Adapter.rl_handle_read_rsps: beat %0d ", cur_cycle, rg_rd_rsp_beat);
$display (" ", fshow (mem_rsp));
end
if (mem_rsp.rresp != axi4_resp_okay) begin
// TODO: need to raise a non-maskable interrupt (NMI) here
$display ("%0d: LLC_AXI4_Adapter.rl_handle_read_rsp: fabric response error; exit", cur_cycle);
$display (" ", fshow (mem_rsp));
$finish (1);
end
// Shift next 64 bits from fabric into the cache line being assembled
let new_cline = { mem_rsp.rdata, rg_cline [511:64] };
if (rg_rd_rsp_beat == 7) begin
let ldreq <- pop (f_pending_reads);
MemRsMsg #(idT, childT) resp = MemRsMsg {data: unpack (new_cline),
child: ldreq.child,
id: ldreq.id};
llc.rsFromM.enq (resp);
if (cfg_verbosity > 1)
$display (" Response to LLC: ", fshow (resp));
end
rg_cline <= new_cline;
rg_rd_rsp_beat <= rg_rd_rsp_beat + 1;
endrule
// ================================================================
// Handle write requests and responses
// Each 512b cache line takes 8 beats, each handling 64 bits
Reg #(Bit #(3)) rg_wr_req_beat <- mkReg (0);
Reg #(Bit #(3)) rg_wr_rsp_beat <- mkReg (0);
FIFOF #(WbMemRs) f_pending_writes <- mkFIFOF;
rule rl_handle_write_req (llc.toM.first matches tagged Wb .wb);
if ((cfg_verbosity > 0) && (rg_wr_req_beat == 0)) begin
$display ("%d: LLC_AXI4_Adapter.rl_handle_write_req: Wb request from LLC to memory:", cur_cycle);
$display (" ", fshow (wb));
end
Addr line_addr = { wb.addr [63:6], 6'h0 }; // Addr of containing cache line
Line line_data = wb.data;
Vector #(8, Bit #(8)) line_bes = unpack (pack (wb.byteEn));
Addr offset = zeroExtend ( { rg_wr_req_beat, 3'b_000 } ); // Addr offset of 64b word for this beat
Bit #(64) data64 = line_data [rg_wr_req_beat];
Bit #(8) strb8 = line_bes [rg_wr_req_beat];
fa_fabric_send_write_req (line_addr | offset, strb8, data64);
if (rg_wr_req_beat == 0)
f_pending_writes.enq (wb);
if (rg_wr_req_beat == 7)
llc.toM.deq;
rg_wr_req_beat <= rg_wr_req_beat + 1;
endrule
// ----------------
// Discard write-responses from the fabric
rule rl_discard_write_rsp;
let wr_resp <- pop_o (master_xactor.o_wr_resp);
if (cfg_verbosity > 1) begin
$display ("%0d: LLC_AXI4_Adapter.rl_discard_write_rsp: beat %0d ", cur_cycle, rg_wr_rsp_beat);
$display (" ", fshow (wr_resp));
end
if (ctr_wr_rsps_pending.value == 0) begin
$display ("%0d: ERROR: LLC_AXI4_Adapter.rl_discard_write_rsp: unexpected Wr response (ctr_wr_rsps_pending.value == 0)",
cur_cycle);
$display (" ", fshow (wr_resp));
$finish (1); // Assertion failure
end
ctr_wr_rsps_pending.decr;
if (wr_resp.bresp != axi4_resp_okay) begin
// TODO: need to raise a non-maskable interrupt (NMI) here
$display ("%0d: LLC_AXI4_Adapter.rl_discard_write_rsp: fabric response error: exit", cur_cycle);
$display (" ", fshow (wr_resp));
$finish (1);
end
if (rg_wr_rsp_beat == 7) begin
let wrreq <- pop (f_pending_writes);
// LLC does not expect any response for writes
end
rg_wr_rsp_beat <= rg_wr_rsp_beat + 1;
endrule
// ================================================================
// INTERFACE
method Action reset;
ctr_wr_rsps_pending.clear;
endmethod
// Fabric interface for memory
interface mem_master = master_xactor.axi_side;
endmodule
// ================================================================
endpackage

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,253 @@
package MMIO_AXI4_Adapter;
// ================================================================
// BSV lib imports
import Assert :: *;
import ConfigReg :: *;
import FIFOF :: *;
import GetPut :: *;
import ClientServer :: *;
// ----------------
// BSV additional libs
import GetPut_Aux :: *;
import Cur_Cycle :: *;
import Semi_FIFOF :: *;
import CreditCounter :: *;
// ================================================================
// Project imports
// ----------------
// From MIT RISCY-OOO
import ProcTypes :: *;
// ----------------
// From Bluespec Pipes
import AXI4_Types :: *;
import Fabric_Defs :: *;
// ================================================================
interface MMIO_AXI4_Adapter_IFC;
method Action reset;
interface Server #(MMIOCRq, MMIODataPRs) core_side;
// Fabric master interface for IO
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) mmio_master;
endinterface
// ================================================================
module mkMMIO_AXI4_Adapter (MMIO_AXI4_Adapter_IFC);
// Verbosity: 0: quiet; 1: transactions
Integer verbosity = 2;
Reg #(Bit #(4)) cfg_verbosity <- mkConfigReg (fromInteger (verbosity));
// ================================================================
// Requests from and responses to core
FIFOF #(MMIOCRq) f_reqs_from_core <- mkFIFOF;
FIFOF #(MMIODataPRs) f_rsps_to_core <- mkFIFOF;
// ================================================================
// Fabric request/response
AXI4_Master_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master_xactor <- mkAXI4_Master_Xactor_2;
// For discarding write-responses
CreditCounter_IFC #(4) ctr_wr_rsps_pending <- mkCreditCounter; // Max 15 writes outstanding
// ================================================================
// Functions to interact with the fabric
// Send a read-request into the fabric
function Action fa_fabric_send_read_req (Fabric_Addr addr);
action
AXI4_Size size = axsize_8;
let mem_req_rd_addr = AXI4_Rd_Addr {arid: fabric_default_id,
araddr: addr,
arlen: 0, // burst len = arlen+1
arsize: size,
arburst: fabric_default_burst,
arlock: fabric_default_lock,
arcache: fabric_default_arcache,
arprot: fabric_default_prot,
arqos: fabric_default_qos,
arregion: fabric_default_region,
aruser: fabric_default_user};
master_xactor.i_rd_addr.enq (mem_req_rd_addr);
// Debugging
if (cfg_verbosity > 0) begin
$display (" ", fshow (mem_req_rd_addr));
end
endaction
endfunction
// Send a write-request into the fabric
function Action fa_fabric_send_write_req (Fabric_Addr addr, Fabric_Strb strb, Bit #(64) st_val);
action
AXI4_Size size = axsize_8;
let mem_req_wr_addr = AXI4_Wr_Addr {awid: fabric_default_id,
awaddr: addr,
awlen: 0, // burst len = awlen+1
awsize: size,
awburst: fabric_default_burst,
awlock: fabric_default_lock,
awcache: fabric_default_awcache,
awprot: fabric_default_prot,
awqos: fabric_default_qos,
awregion: fabric_default_region,
awuser: fabric_default_user};
let mem_req_wr_data = AXI4_Wr_Data {wid: fabric_default_id,
wdata: st_val,
wstrb: strb,
wlast: True,
wuser: fabric_default_user};
master_xactor.i_wr_addr.enq (mem_req_wr_addr);
master_xactor.i_wr_data.enq (mem_req_wr_data);
// Expect a fabric response
ctr_wr_rsps_pending.incr;
// Debugging
if (cfg_verbosity > 0) begin
$display (" To fabric: ", fshow (mem_req_wr_addr));
$display (" ", fshow (mem_req_wr_data));
end
endaction
endfunction
// ================================================================
// Handle read requests and responses.
// Don't do a read while a write is outstanding.
// This is just an adapter from MMIOCRq/MMIODataPRs to AXI4
rule rl_handle_read_req (f_reqs_from_core.first.func matches Ld
&&& (ctr_wr_rsps_pending.value == 0));
let req <- pop (f_reqs_from_core);
if (cfg_verbosity > 0) begin
$display ("%0d: MMIO_AXI4_Adapter.rl_handle_read_req: Ld request", cur_cycle);
$display (" ", fshow (req));
end
fa_fabric_send_read_req (req.addr);
endrule
// ----------------
rule rl_handle_read_rsps;
let mem_rsp <- pop_o (master_xactor.o_rd_data);
if (cfg_verbosity > 0) begin
$display ("%0d: MMIO_AXI4_Adapter.rl_handle_read_rsps ", cur_cycle);
$display (" ", fshow (mem_rsp));
end
if (mem_rsp.rresp != axi4_resp_okay) begin
// TODO: need to raise a non-maskable interrupt (NMI) here
$display ("%0d: MMIO_AXI4_Adapter.rl_handle_read_rsp: fabric response error; exit", cur_cycle);
$display (" ", fshow (mem_rsp));
$finish (1);
end
let rsp = MMIODataPRs {valid: True, data: mem_rsp.rdata};
f_rsps_to_core.enq (rsp);
if (cfg_verbosity > 0)
$display (" Response MMIO to core: ", fshow (rsp));
endrule
// ================================================================
// Handle write requests and responses
rule rl_handle_write_req (f_reqs_from_core.first.func matches St);
let req <- pop (f_reqs_from_core);
if (cfg_verbosity > 0) begin
$display ("%d: MMIO_AXI4_Adapter.rl_handle_write_req: St request:", cur_cycle);
$display (" ", fshow (req));
end
fa_fabric_send_write_req (req.addr, pack (req.byteEn), req.data);
endrule
// ----------------
// Discard write-responses from the fabric
rule rl_discard_write_rsp;
let wr_resp <- pop_o (master_xactor.o_wr_resp);
if (cfg_verbosity > 0) begin
$display ("%0d: MMIO_AXI4_Adapter.rl_discard_write_rsp", cur_cycle);
$display (" ", fshow (wr_resp));
end
if (ctr_wr_rsps_pending.value == 0) begin
$display ("%0d: ERROR: MMIO_AXI4_Adapter.rl_discard_write_rsp: unexpected Wr response (ctr_wr_rsps_pending.value == 0)",
cur_cycle);
$display (" ", fshow (wr_resp));
$finish (1); // Assertion failure
end
ctr_wr_rsps_pending.decr;
if (wr_resp.bresp != axi4_resp_okay) begin
// TODO: need to raise a non-maskable interrupt (NMI) here
$display ("%0d: MMIO_AXI4_Adapter.rl_discard_write_rsp: fabric response error: exit", cur_cycle);
$display (" ", fshow (wr_resp));
$finish (1);
end
else begin
let rsp = MMIODataPRs {valid: True, data: 0};
f_rsps_to_core.enq (rsp);
end
endrule
// ================================================================
// This adapter should only receive Ld/St requests, no Inst or AMO reqs.
function Bool fn_is_Ld_or_St (MMIOCRq req);
return case (req.func) matches
Ld : True;
St : True;
default: False;
endcase;
endfunction
rule rl_handle_non_Ld_St (! fn_is_Ld_or_St (f_reqs_from_core.first));
let req <- pop (f_reqs_from_core);
$display ("%0d: ERROR: MMIO_AXI4_Adapter.rl_handle_non_Ld_St",
cur_cycle);
$display (" ", fshow (req));
$finish (1); // Assertion failure
endrule
// ================================================================
// INTERFACE
method Action reset;
ctr_wr_rsps_pending.clear;
endmethod
interface Server core_side = toGPServer (f_reqs_from_core, f_rsps_to_core);
// Fabric master interface for IO
interface mmio_master = master_xactor.axi_side;
endmodule
// ================================================================
endpackage

358
src_Core/CPU/Proc.bsv Normal file
View File

@@ -0,0 +1,358 @@
package Proc;
// Copyright (c) 2018 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// Portions Copyright (c) 2019 Bluespec, Inc.
// ================================================================
// BSV lib imports
import Vector::*;
import GetPut::*;
import ClientServer::*;
import Connectable::*;
import FIFOF :: *;
import ConfigReg :: *;
// ----------------
// BSV additional libs
import GetPut_Aux :: *;
// ================================================================
// Project imports
// ----------------
// From MIT RISCY-OOO
import Types::*;
import ProcTypes::*;
import L1CoCache::*;
import L2Tlb::*;
import CCTypes::*;
import CacheUtils::*;
import LLCache::*;
import MemLoader::*;
import L1LLConnect::*;
import LLCDmaConnect::*;
import MMIOAddrs::*;
import MMIOCore::*;
import DramCommon::*;
import Performance::*;
// ----------------
// From McStriiv
import AXI4_Types :: *;
import Fabric_Defs :: *;
import Core :: *;
import Proc_IFC :: *;
import MMIOPlatform :: *;
import LLC_AXI4_Adapter :: *;
import MMIO_AXI4_Adapter :: *;
// ================================================================
(* synthesize *)
module mkProc (Proc_IFC);
// cores
Vector#(CoreNum, Core) core = ?;
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
core[i] <- mkCore(fromInteger(i));
end
// ----------------
// Verbosity control for debugging
// Verbosity: 0=quiet; 1=instruction trace; 2=more detail
Reg #(Bit #(4)) cfg_verbosity <- mkConfigReg (0);
// ----------------
// Reset requests and responses (TODO: to be implemented)
FIFOF #(Bit #(0)) f_reset_reqs <- mkFIFOF;
FIFOF #(Bit #(0)) f_reset_rsps <- mkFIFOF;
// ----------------
// Communication to/from External debug module (TODO: to be implemented)
`ifdef INCLUDE_GDB_CONTROL
// Debugger run-control
FIFOF #(Bool) f_run_halt_reqs <- mkFIFOF;
FIFOF #(Bool) f_run_halt_rsps <- mkFIFOF;
// Stop-request from debugger (e.g., GDB ^C or Dsharp 'stop')
Reg #(Bool) rg_stop_req <- mkReg (False);
// Count instrs after step-request from debugger (via dcsr.step)
Reg #(Bit #(1)) rg_step_count <- mkReg (0);
// Debugger GPR read/write request/response
FIFOF #(MemoryRequest #(5, XLEN)) f_gpr_reqs <- mkFIFOF1;
FIFOF #(MemoryResponse #( XLEN)) f_gpr_rsps <- mkFIFOF1;
`ifdef ISA_F
// Debugger FPR read/write request/response
FIFOF #(MemoryRequest #(5, FLEN)) f_fpr_reqs <- mkFIFOF1;
FIFOF #(MemoryResponse #( FLEN)) f_fpr_rsps <- mkFIFOF1;
`endif
// Debugger CSR read/write request/response
FIFOF #(MemoryRequest #(12, XLEN)) f_csr_reqs <- mkFIFOF1;
FIFOF #(MemoryResponse #( XLEN)) f_csr_rsps <- mkFIFOF1;
`endif
// ----------------
// Tandem Verification (TODO: to be implemented)
`ifdef INCLUDE_TANDEM_VERIF
FIFOF #(Trace_Data) f_trace_data <- mkFIFOF;
`endif
// ----------------
// MMIO
MMIO_AXI4_Adapter_IFC mmio_axi4_adapter <- mkMMIO_AXI4_Adapter;
// MMIO platform
Vector#(CoreNum, MMIOCoreToPlatform) mmioToP;
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
mmioToP[i] = core[i].mmioToPlatform;
end
MMIOPlatform mmioPlatform <- mkMMIOPlatform (mmioToP,
mmio_axi4_adapter.core_side);
// last level cache
LLCache llc <- mkLLCache;
// connect LLC to L1 caches
Vector#(L1Num, ChildCacheToParent#(L1Way, void)) l1 = ?;
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
l1[i] = core[i].dCacheToParent;
l1[i + valueof(CoreNum)] = core[i].iCacheToParent;
end
mkL1LLConnect(llc.to_child, l1);
// ================================================================
// LLC's DMA connections
// Core's tlbToMem
Vector#(CoreNum, TlbMemClient) tlbToMem = ?;
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
tlbToMem[i] = core[i].tlbToMem;
end
// Stub out memLoader (TODO: can be Debug Module's access)
let memLoaderStub = interface MemLoaderMemClient;
interface memReq = nullFifoDeq;
interface respSt = nullFifoEnq;
endinterface;
mkLLCDmaConnect(llc.dma, memLoaderStub, tlbToMem);
// ================================================================
// interface LLC to AXI4
LLC_AXI4_Adapter_IFC llc_axi4_adapter <- mkLLC_AXi4_Adapter (llc.to_mem);
// ================================================================
// Connect stats
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
rule broadcastStats;
Bool doStats <- core[i].sendDoStats;
for(Integer j = 0; j < valueof(CoreNum); j = j+1) begin
core[j].recvDoStats(doStats);
end
llc.perf.setStatus(doStats);
endrule
end
// ================================================================
// Stub out deadlock and renameDebug interfaces
for(Integer j = 0; j < valueof(CoreNum); j = j+1) begin
rule rl_dummy1;
let x <- core[j].deadlock.dCacheCRqStuck.get;
endrule
rule rl_dummy2;
let x <- core[j].deadlock.dCachePRqStuck.get;
endrule
rule rl_dummy3;
let x <- core[j].deadlock.iCacheCRqStuck.get;
endrule
rule rl_dummy4;
let x <- core[j].deadlock.iCachePRqStuck.get;
endrule
rule rl_dummy5;
let x <- core[j].deadlock.renameInstStuck.get;
endrule
rule rl_dummy6;
let x <- core[j].deadlock.renameCorrectPathStuck.get;
endrule
rule rl_dummy7;
let x <- core[j].deadlock.commitInstStuck.get;
endrule
rule rl_dummy8;
let x <- core[j].deadlock.commitUserInstStuck.get;
endrule
rule rl_dummy9;
let x <- core[j].deadlock.checkStarted.get;
endrule
rule rl_dummy20;
let x <- core[j].renameDebug.renameErr.get;
endrule
end
// ================================================================
// Reset
rule rl_reset;
let x <- pop (f_reset_reqs);
llc_axi4_adapter.reset;
mmio_axi4_adapter.reset;
f_reset_rsps.enq (?);
endrule
// ----------------
// Termination detection
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
rule rl_terminate;
let x <- core[i].coreIndInv.terminate;
$display ("Core %d terminated", i);
endrule
end
// Print out values written 'tohost'
rule rl_tohost;
let x <- mmioPlatform.to_host;
$display ("mmioPlatform.rl_tohost: 0x%0x (= %0d)", x, x);
if (x != 0) begin
// Standard RISC-V ISA tests finish by writing a value tohost with x[0]==1.
// Further when x[63:1]==0, all tests within the program pass,
// otherwise x[63:1] = the test within the program that failed.
let failed_testnum = (x >> 1);
if (failed_testnum == 0)
$display ("PASS");
else
$display ("FAIL %0d", failed_testnum);
$finish (0);
end
endrule
// ================================================================
// ================================================================
// ================================================================
// INTERFACE
// Reset
interface Server hart0_server_reset = toGPServer (f_reset_reqs, f_reset_rsps);
// ----------------
// Start the cores running
method Action start (Addr startpc, Addr tohostAddr, Addr fromhostAddr);
action
for(Integer i = 0; i < valueof(CoreNum); i = i+1)
core[i].coreReq.start (startpc, tohostAddr, fromhostAddr);
endaction
mmioPlatform.start (tohostAddr, fromhostAddr);
$display ("Proc.start: startpc = 0x%0h, tohostAddr = 0x%0h, fromhostAddr = %0h",
startpc, tohostAddr, fromhostAddr);
endmethod
// ----------------
// SoC fabric connections
// Fabric master interface for memory (from LLC)
interface master0 = llc_axi4_adapter.mem_master;
// Fabric master interface for IO (from MMIOPlatform)
interface master1 = mmio_axi4_adapter.mmio_master;
// ----------------
// External interrupts
method Action m_external_interrupt_req (x);
core[0].setMEIP (pack (x));
endmethod
method Action s_external_interrupt_req (x);
core[0].setSEIP (pack (x));
endmethod
// ----------------
// Non-maskable interrupt
// TODO: fixup: NMIs should send CPU to an NMI vector (TBD in SoC_Map)
method Action non_maskable_interrupt_req (Bool set_not_clear) = noAction;
// ----------------
// For tracing
method Action set_verbosity (Bit #(4) verbosity);
cfg_verbosity <= verbosity;
endmethod
// ----------------
// Optional interface to Tandem Verifier
`ifdef INCLUDE_TANDEM_VERIF
interface Get trace_data_out = toGet (f_trace_data);
`endif
// ----------------
// Optional interface to Debug Module
`ifdef INCLUDE_GDB_CONTROL
// run-control, other
interface Server hart0_server_run_halt = toGPServer (f_run_halt_reqs, f_run_halt_rsps);
interface Put hart0_put_other_req;
method Action put (Bit #(4) req);
cfg_verbosity <= req;
endmethod
endinterface
// GPR access
interface MemoryServer hart0_gpr_mem_server = toGPServer (f_gpr_reqs, f_gpr_rsps);
`ifdef ISA_F
// FPR access
interface MemoryServer hart0_fpr_mem_server = toGPServer (f_fpr_reqs, f_fpr_rsps);
`endif
// CSR access
interface MemoryServer hart0_csr_mem_server = toGPServer (f_csr_reqs, f_csr_rsps);
`endif
endmodule
endpackage

99
src_Core/CPU/Proc_IFC.bsv Normal file
View File

@@ -0,0 +1,99 @@
// Copyright (c) 2016-2019 Bluespec, Inc. All Rights Reserved
package Proc_IFC;
// ================================================================
// BSV library imports
import Memory :: *;
import GetPut :: *;
import ClientServer :: *;
// ================================================================
// Project imports
import ISA_Decls :: *;
`ifdef INCLUDE_TANDEM_VERIF
import TV_Info :: *;
`endif
import AXI4_Types :: *;
import Fabric_Defs :: *;
// ================================================================
// CPU interface
// Note: this Proc_IFC is similar, but not identical to CPU_IFC for Piccolo and Flute
// Specifically, it removes interfaces for software and timer,
// because the RISCY-OOO mkProc contains those elements.
interface Proc_IFC;
// Reset
interface Server #(Token, Token) hart0_server_reset;
// ----------------
// Start the cores running
method Action start (Addr startpc, Addr tohostAddr, Addr fromhostAddr);
// ----------------
// SoC fabric connections
// Fabric master interface for memory (from LLC)
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master0;
// Fabric master interface for IO (from MMIOPlatform)
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master1;
// ----------------
// External interrupts
(* always_ready, always_enabled *)
method Action m_external_interrupt_req (Bool set_not_clear);
(* always_ready, always_enabled *)
method Action s_external_interrupt_req (Bool set_not_clear);
// ----------------
// Non-maskable interrupt
(* always_ready, always_enabled *)
method Action non_maskable_interrupt_req (Bool set_not_clear);
// ----------------
// Set core's verbosity
method Action set_verbosity (Bit #(4) verbosity);
// ----------------
// Optional interface to Tandem Verifier
`ifdef INCLUDE_TANDEM_VERIF
interface Get #(Trace_Data) trace_data_out;
`endif
// ----------------
// Optional interface to Debug Module
`ifdef INCLUDE_GDB_CONTROL
// run-control, other
interface Server #(Bool, Bool) hart0_server_run_halt;
interface Put #(Bit #(4)) hart0_put_other_req;
// GPR access
interface MemoryServer #(5, XLEN) hart0_gpr_mem_server;
`ifdef ISA_F
// FPR access
interface MemoryServer #(5, FLEN) hart0_fpr_mem_server;
`endif
// CSR access
interface MemoryServer #(12, XLEN) hart0_csr_mem_server;
`endif
endinterface
// ================================================================
endpackage

464
src_Core/Core/CoreW.bsv Normal file
View File

@@ -0,0 +1,464 @@
// Copyright (c) 2018-2019 Bluespec, Inc. All Rights Reserved.
package CoreW;
// ================================================================
// This package defines:
// Core_IFC
// mkCore #(Core_IFC)
// mkFabric_2x3 -- specialized AXI4 fabric used inside this core
//
// mkCoreW instantiates:
// - mkProc (the RISC-V CPU, a version of MIT's RISCY-OOO)
// - mkFabric_2x3
// - mkPLIC_16_2_7
// - mkTV_Encode (Tandem-Verification logic, optional: INCLUDE_TANDEM_VERIF)
// - mkDebug_Module (RISC-V Debug Module, optional: INCLUDE_GDB_CONTROL)
// and connects them all up.
// ================================================================
// BSV library imports
import Vector :: *;
import FIFOF :: *;
import GetPut :: *;
import ClientServer :: *;
import Connectable :: *;
// ----------------
// BSV additional libs
import Cur_Cycle :: *;
import GetPut_Aux :: *;
// ================================================================
// Project imports
// Main fabric
import AXI4_Types :: *;
import AXI4_Fabric :: *;
import Fabric_Defs :: *; // for Wd_Id, Wd_Addr, Wd_Data, Wd_User
import SoC_Map :: *;
`ifdef INCLUDE_GDB_CONTROL
import Debug_Module :: *;
`endif
import Core_IFC :: *;
import PLIC :: *;
import PLIC_16_2_7 :: *;
import Proc_IFC :: *;
import Proc :: *;
`ifdef INCLUDE_TANDEM_VERIF
import TV_Info :: *;
import TV_Encode :: *;
`endif
// TV_Taps needed when both GDB_CONTROL and TANDEM_VERIF are present
`ifdef INCLUDE_GDB_CONTROL
`ifdef INCLUDE_TANDEM_VERIF
import TV_Taps :: *;
`endif
`endif
// ================================================================
// The Core module
(* synthesize *)
module mkCoreW (Core_IFC #(N_External_Interrupt_Sources));
// ================================================================
// STATE
// System address map
SoC_Map_IFC soc_map <- mkSoC_Map;
// McStriiv processor
Proc_IFC proc <- mkProc;
// A 2x3 fabric for connecting {CPU, Debug_Module} to {Fabric, PLIC}
Fabric_2x3_IFC fabric_2x3 <- mkFabric_2x3;
// PLIC (Platform-Level Interrupt Controller)
PLIC_IFC_16_2_7 plic <- mkPLIC_16_2_7;
// Reset requests from SoC and responses to SoC
FIFOF #(Bit #(0)) f_reset_reqs <- mkFIFOF;
FIFOF #(Bit #(0)) f_reset_rsps <- mkFIFOF;
`ifdef INCLUDE_TANDEM_VERIF
// The TV encoder transforms Trace_Data structures produced by the CPU and DM
// into encoded byte vectors for transmission to the Tandem Verifier
TV_Encode_IFC tv_encode <- mkTV_Encode;
`endif
`ifdef INCLUDE_GDB_CONTROL
// Debug Module
Debug_Module_IFC debug_module <- mkDebug_Module;
`endif
// ================================================================
// RESET
// There are two sources of reset requests to the CPU: externally
// from the SoC and, optionally, the DM. The SoC requires a
// response, the DM does not. When both requestors are present
// (i.e., DM is present), we merge the reset requests into the CPU,
// and we remember which one was the requestor in
// f_reset_requestor, so that we know whether or not to respond to
// the SoC.
Bit #(1) reset_requestor_dm = 0;
Bit #(1) reset_requestor_soc = 1;
`ifdef INCLUDE_GDB_CONTROL
FIFOF #(Bit #(1)) f_reset_requestor <- mkFIFOF;
`endif
// Reset-hart0 request from SoC
rule rl_cpu_hart0_reset_from_soc_start;
let req <- pop (f_reset_reqs);
proc.hart0_server_reset.request.put (?); // CPU
plic.server_reset.request.put (?); // PLIC
fabric_2x3.reset; // Local 2x3 Fabric
`ifdef INCLUDE_GDB_CONTROL
// Remember the requestor, so we can respond to it
f_reset_requestor.enq (reset_requestor_soc);
`endif
$display ("%0d: Core.rl_cpu_hart0_reset_from_soc_start", cur_cycle);
endrule
`ifdef INCLUDE_GDB_CONTROL
// Reset-hart0 from Debug Module
rule rl_cpu_hart0_reset_from_dm_start;
let req <- debug_module.hart0_get_reset_req.get;
proc.hart0_server_reset.request.put (?); // CPU
plic.server_reset.request.put (?); // PLIC
fabric_2x3.reset; // Local 2x3 fabric
// Remember the requestor, so we can respond to it
f_reset_requestor.enq (reset_requestor_dm);
$display ("%0d: Core.rl_cpu_hart0_reset_from_dm_start", cur_cycle);
endrule
`endif
rule rl_cpu_hart0_reset_complete;
let rsp1 <- proc.hart0_server_reset.response.get; // CPU
let rsp3 <- plic.server_reset.response.get; // PLIC
plic.set_addr_map (zeroExtend (soc_map.m_plic_addr_base),
zeroExtend (soc_map.m_plic_addr_lim));
Bit #(1) requestor = reset_requestor_soc;
`ifdef INCLUDE_GDB_CONTROL
requestor <- pop (f_reset_requestor);
`endif
if (requestor == reset_requestor_soc)
f_reset_rsps.enq (?);
// Start running the cores
Bit #(64) startpc = 'h_0000_1000; // TODO: fixup
Bit #(64) tohostAddr = 'h_8000_1000; // TODO: fixup
Bit #(64) fromhostAddr = 0;
proc.start (startpc, tohostAddr, fromhostAddr);
$display ("%0d: Core.rl_cpu_hart0_reset_complete; started running proc", cur_cycle);
endrule
// ================================================================
// Direct DM-to-CPU connections
`ifdef INCLUDE_GDB_CONTROL
// DM to CPU connections for run-control and other misc requests
mkConnection (debug_module.hart0_client_run_halt, proc.hart0_server_run_halt);
mkConnection (debug_module.hart0_get_other_req, proc.hart0_put_other_req);
`endif
// ================================================================
// Other CPU/DM/TV connections
// (depends on whether DM, TV or both are present)
`ifdef INCLUDE_GDB_CONTROL
`ifdef INCLUDE_TANDEM_VERIF
// BEGIN SECTION: GDB and TV
// ----------------------------------------------------------------
// DM and TV both present. We instantiate 'taps' into connections
// where the DM writes CPU GPRs, CPU FPRs, CPU CSRs, and main memory,
// in order to produce corresponding writes for the Tandem Verifier.
// Then, we merge the Trace_Data from these three taps with the
// Trace_Data produced by the PROC.
FIFOF #(Trace_Data) f_trace_data_merged <- mkFIFOF;
// Connect merged trace data to trace encoder
mkConnection (toGet (f_trace_data_merged), tv_encode.trace_data_in);
// Merge-in CPU's trace data.
// This is equivalent to: mkConnection (proc.trace_data_out, toPut (f_trace_data_merged))
// but using a rule allows us to name it in scheduling attributes.
rule merge_cpu_trace_data;
let tmp <- proc.trace_data_out.get;
f_trace_data_merged.enq (tmp);
endrule
// Create a tap for DM's memory-writes to the bus, and merge-in the trace data.
DM_Mem_Tap_IFC dm_mem_tap <- mkDM_Mem_Tap;
mkConnection (debug_module.master, dm_mem_tap.slave);
let dm_master_local = dm_mem_tap.master;
rule merge_dm_mem_trace_data;
let tmp <- dm_mem_tap.trace_data_out.get;
f_trace_data_merged.enq (tmp);
endrule
// Create a tap for DM's GPR writes to the CPU, and merge-in the trace data.
DM_GPR_Tap_IFC dm_gpr_tap_ifc <- mkDM_GPR_Tap;
mkConnection (debug_module.hart0_gpr_mem_client, dm_gpr_tap_ifc.server);
mkConnection (dm_gpr_tap_ifc.client, proc.hart0_gpr_mem_server);
rule merge_dm_gpr_trace_data;
let tmp <- dm_gpr_tap_ifc.trace_data_out.get;
f_trace_data_merged.enq (tmp);
endrule
`ifdef ISA_F_OR_D
// Create a tap for DM's FPR writes to the CPU, and merge-in the trace data.
DM_FPR_Tap_IFC dm_fpr_tap_ifc <- mkDM_FPR_Tap;
mkConnection (debug_module.hart0_fpr_mem_client, dm_fpr_tap_ifc.server);
mkConnection (dm_fpr_tap_ifc.client, proc.hart0_fpr_mem_server);
rule merge_dm_fpr_trace_data;
let tmp <- dm_fpr_tap_ifc.trace_data_out.get;
f_trace_data_merged.enq (tmp);
endrule
`endif
// for ifdef ISA_F_OR_D
// Create a tap for DM's CSR writes, and merge-in the trace data.
DM_CSR_Tap_IFC dm_csr_tap <- mkDM_CSR_Tap;
mkConnection(debug_module.hart0_csr_mem_client, dm_csr_tap.server);
mkConnection(dm_csr_tap.client, proc.hart0_csr_mem_server);
`ifdef ISA_F_OR_D
(* descending_urgency = "merge_dm_fpr_trace_data, merge_dm_gpr_trace_data" *)
`endif
(* descending_urgency = "merge_dm_gpr_trace_data, merge_dm_csr_trace_data" *)
(* descending_urgency = "merge_dm_csr_trace_data, merge_dm_mem_trace_data" *)
(* descending_urgency = "merge_dm_mem_trace_data, merge_cpu_trace_data" *)
rule merge_dm_csr_trace_data;
let tmp <- dm_csr_tap.trace_data_out.get;
f_trace_data_merged.enq(tmp);
endrule
// END SECTION: GDB and TV
`else
// for ifdef INCLUDE_TANDEM_VERIF
// ----------------------------------------------------------------
// BEGIN SECTION: GDB and no TV
// Connect DM's GPR interface directly to CPU
mkConnection (debug_module.hart0_gpr_mem_client, proc.hart0_gpr_mem_server);
`ifdef ISA_F_OR_D
// Connect DM's FPR interface directly to CPU
mkConnection (debug_module.hart0_fpr_mem_client, proc.hart0_fpr_mem_server);
`endif
// Connect DM's CSR interface directly to CPU
mkConnection (debug_module.hart0_csr_mem_client, proc.hart0_csr_mem_server);
// DM's bus master is directly the bus master
let dm_master_local = debug_module.master;
// END SECTION: GDB and no TV
`endif
// for ifdef INCLUDE_TANDEM_VERIF
`else
// for ifdef INCLUDE_GDB_CONTROL
// BEGIN SECTION: no GDB
// No DM, so 'DM bus master' is dummy
AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User)
dm_master_local = dummy_AXI4_Master_ifc;
`ifdef INCLUDE_TANDEM_VERIF
// ----------------------------------------------------------------
// BEGIN SECTION: no GDB, TV
// Connect CPU's TV out directly to TV encoder
mkConnection (proc.trace_data_out, tv_encode.trace_data_in);
// END SECTION: no GDB, TV
`endif
`endif
// for ifdef INCLUDE_GDB_CONTROL
// ================================================================
// Connect the local 2x3 fabric
// Masters on the local 2x3 fabric
mkConnection (proc.master1, fabric_2x3.v_from_masters [cpu_dmem_master_num]);
mkConnection (dm_master_local, fabric_2x3.v_from_masters [debug_module_sba_master_num]);
// Slaves on the local 2x3 fabric
// default slave is taken out directly to the Core interface
mkConnection (fabric_2x3.v_to_slaves [plic_slave_num], plic.axi4_slave);
// TODO: This slave can be connected to mkLLCDmaConnect for Debug Module System Bus Access
AXI4_Slave_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) dummy_slave = dummy_AXI4_Slave_ifc;
mkConnection (fabric_2x3.v_to_slaves [near_mem_io_slave_num], dummy_slave);
// ================================================================
// Connect external interrupt lines from PLIC to CPU
rule rl_relay_external_interrupts; // from PLIC
Bool meip = plic.v_targets [0].m_eip;
proc.m_external_interrupt_req (meip);
Bool seip = plic.v_targets [1].m_eip;
proc.s_external_interrupt_req (seip);
// $display ("%0d: Core.rl_relay_external_interrupts: relaying: %d", cur_cycle, pack (x));
endrule
// TODO: fixup. Need to combine NMIs from multiple sources (cache, fabric, devices, ...)
rule rl_relay_non_maskable_interrupt;
proc.non_maskable_interrupt_req (False);
// $display ("%0d: Core.rl_relay_non_maskable_interrupts: relaying: %d", cur_cycle, pack (x));
endrule
// ================================================================
// INTERFACE
// ----------------------------------------------------------------
// Debugging: set core's verbosity
method Action set_verbosity (Bit #(4) verbosity, Bit #(64) logdelay);
// Warning: ignoring logdelay
proc.set_verbosity (verbosity);
endmethod
// ----------------------------------------------------------------
// Soft reset
interface Server cpu_reset_server = toGPServer (f_reset_reqs, f_reset_rsps);
// ----------------------------------------------------------------
// AXI4 Fabric interfaces
// IMem to Fabric master interface
interface AXI4_Master_IFC cpu_imem_master = proc.master0;
// DMem to Fabric master interface
interface AXI4_Master_IFC cpu_dmem_master = fabric_2x3.v_to_slaves [default_slave_num];
// ----------------------------------------------------------------
// External interrupt sources
interface core_external_interrupt_sources = plic.v_sources;
// ----------------------------------------------------------------
// Optional TV interface
`ifdef INCLUDE_TANDEM_VERIF
interface Get tv_verifier_info_get;
method ActionValue #(Info_CPU_to_Verifier) get();
match { .n, .v } <- tv_encode.tv_vb_out.get;
return (Info_CPU_to_Verifier { num_bytes: n, vec_bytes: v });
endmethod
endinterface
`endif
// ----------------------------------------------------------------
// Optional DM interfaces
`ifdef INCLUDE_GDB_CONTROL
// ----------------
// DMI (Debug Module Interface) facing remote debugger
interface DMI dm_dmi = debug_module.dmi;
// ----------------
// Facing Platform
// Non-Debug-Module Reset (reset all except DM)
interface Get dm_ndm_reset_req_get = debug_module.get_ndm_reset_req;
`endif
endmodule: mkCoreW
// ================================================================
// 2x3 Fabric for this Core
// Masters: CPU DMem, Debug Module System Bus Access, External access
// ----------------
// Fabric port numbers for masters
typedef 2 Num_Masters_2x3;
typedef Bit #(TLog #(Num_Masters_2x3)) Master_Num_2x3;
Master_Num_2x3 cpu_dmem_master_num = 0;
Master_Num_2x3 debug_module_sba_master_num = 1;
// ----------------
// Fabric port numbers for slaves
typedef 3 Num_Slaves_2x3;
typedef Bit #(TLog #(Num_Slaves_2x3)) Slave_Num_2x3;
Slave_Num_2x3 default_slave_num = 0;
Slave_Num_2x3 plic_slave_num = 1;
// TODO: repurpose this for Debug Module System Bus Access to connect to mkLLCDramConnect
Slave_Num_2x3 near_mem_io_slave_num = 2;
// ----------------
// Specialization of parameterized AXI4 fabric for 2x3 Core fabric
typedef AXI4_Fabric_IFC #(Num_Masters_2x3,
Num_Slaves_2x3,
Wd_Id,
Wd_Addr,
Wd_Data,
Wd_User) Fabric_2x3_IFC;
// ----------------
(* synthesize *)
module mkFabric_2x3 (Fabric_2x3_IFC);
// System address map
SoC_Map_IFC soc_map <- mkSoC_Map;
// ----------------
// Slave address decoder
// Any addr is legal, and there is only one slave to service it.
function Tuple2 #(Bool, Slave_Num_2x3) fn_addr_to_slave_num_2x3 (Fabric_Addr addr);
if ( (soc_map.m_near_mem_io_addr_base <= addr)
&& (addr < soc_map.m_near_mem_io_addr_lim))
return tuple2 (True, near_mem_io_slave_num);
else if ( (soc_map.m_plic_addr_base <= addr)
&& (addr < soc_map.m_plic_addr_lim))
return tuple2 (True, plic_slave_num);
else
return tuple2 (True, default_slave_num);
endfunction
AXI4_Fabric_IFC #(Num_Masters_2x3, Num_Slaves_2x3, Wd_Id, Wd_Addr, Wd_Data, Wd_User)
fabric <- mkAXI4_Fabric (fn_addr_to_slave_num_2x3);
return fabric;
endmodule: mkFabric_2x3
// ================================================================
endpackage

View File

@@ -0,0 +1,97 @@
// Copyright (c) 2018-2019 Bluespec, Inc. All Rights Reserved.
package Core_IFC;
// ================================================================
// This package defines the interface of a Core module which
// contains:
// - mkCPU (the RISC-V CPU)
// - mkFabric_2x3
// - mkNear_Mem_IO_AXI4
// - mkPLIC_16_2_7
// - mkTV_Encode (Tandem-Verification logic, optional: INCLUDE_TANDEM_VERIF)
// - mkDebug_Module (RISC-V Debug Module, optional: INCLUDE_GDB_CONTROL)
// ================================================================
// BSV library imports
import Vector :: *;
import GetPut :: *;
import ClientServer :: *;
// ================================================================
// Project imports
// Main fabric
import AXI4_Types :: *;
import Fabric_Defs :: *;
// External interrupt request interface
import PLIC :: *;
`ifdef INCLUDE_TANDEM_VERIF
import TV_Info :: *;
`endif
`ifdef INCLUDE_GDB_CONTROL
import Debug_Module :: *;
`endif
// ================================================================
// The Core interface
interface Core_IFC #(numeric type t_n_interrupt_sources);
// ----------------------------------------------------------------
// Debugging: set core's verbosity
method Action set_verbosity (Bit #(4) verbosity, Bit #(64) logdelay);
// ----------------------------------------------------------------
// Soft reset
interface Server #(Bit #(0), Bit #(0)) cpu_reset_server;
// ----------------------------------------------------------------
// AXI4 Fabric interfaces
// CPU IMem to Fabric master interface
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) cpu_imem_master;
// CPU DMem to Fabric master interface
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) cpu_dmem_master;
// ----------------------------------------------------------------
// External interrupt sources
interface Vector #(t_n_interrupt_sources, PLIC_Source_IFC) core_external_interrupt_sources;
// ----------------------------------------------------------------
// Optional Tandem Verifier interface output tuples (n,vb),
// where 'vb' is a vector of bytes
// with relevant bytes in locations [0]..[n-1]
`ifdef INCLUDE_TANDEM_VERIF
interface Get #(Info_CPU_to_Verifier) tv_verifier_info_get;
`endif
// ----------------------------------------------------------------
// Optional Debug Module interfaces
`ifdef INCLUDE_GDB_CONTROL
// ----------------
// DMI (Debug Module Interface) facing remote debugger
interface DMI dm_dmi;
// ----------------
// Facing Platform
// Non-Debug-Module Reset (reset all except DM)
interface Get #(Bit #(0)) dm_ndm_reset_req_get;
`endif
endinterface
// ================================================================
endpackage

View File

@@ -0,0 +1,88 @@
// Copyright (c) 2018-2019 Bluespec, Inc. All Rights Reserved
package Fabric_Defs;
// ================================================================
// Defines key parameters of the AXI4/AXI4-Lite system interconnect
// fabric to which the core connects, such as address bus width, data
// bus width, etc.
// ***** WARNING! WARNING! WARNING! *****
// During system integration, these parameters should be checked to be
// identical to the system interconnect settings. Strong
// type-checking (EXACT match on bus widths) will do this; but some
// languages/tools may silently ignore mismatched widths.
// ================================================================
// BSV lib imports
// None
// ================================================================
// Project imports
import AXI4_Types :: *;
// ================================================================
// Fabric parameters
// ----------------
// Width of fabric 'id' buses
typedef 4 Wd_Id;
typedef Bit #(Wd_Id) Fabric_Id;
// ----------------
// Width of fabric 'addr' buses
`ifdef FABRIC64
typedef 64 Wd_Addr;
`else
typedef 32 Wd_Addr;
`endif
typedef Bit #(Wd_Addr) Fabric_Addr;
typedef TDiv #(Wd_Addr, 8) Bytes_per_Fabric_Addr;
Integer bytes_per_fabric_addr = valueOf (Bytes_per_Fabric_Addr);
// ----------------
// Width of fabric 'data' buses
`ifdef FABRIC64
typedef 64 Wd_Data;
`else
typedef 32 Wd_Data;
`endif
typedef Bit #(Wd_Data) Fabric_Data;
typedef Bit #(TDiv #(Wd_Data, 8)) Fabric_Strb;
typedef TDiv #(Wd_Data, 8) Bytes_per_Fabric_Data;
Integer bytes_per_fabric_data = valueOf (Bytes_per_Fabric_Data);
// ----------------
// Width of fabric 'user' datapaths
typedef 0 Wd_User;
typedef Bit #(Wd_User) Fabric_User;
// ----------------
// Number of zero LSBs in a fabric address aligned to the fabric data width
typedef TLog #(Bytes_per_Fabric_Data) ZLSBs_Aligned_Fabric_Addr;
Integer zlsbs_aligned_fabric_addr = valueOf (ZLSBs_Aligned_Fabric_Addr);
// ================================================================
// AXI4 defaults for this project
Fabric_Id fabric_default_id = 0;
AXI4_Burst fabric_default_burst = axburst_incr;
AXI4_Lock fabric_default_lock = axlock_normal;
AXI4_Cache fabric_default_arcache = arcache_dev_nonbuf;
AXI4_Cache fabric_default_awcache = awcache_dev_nonbuf;
AXI4_Prot fabric_default_prot = { axprot_2_data, axprot_1_secure, axprot_0_unpriv };
AXI4_QoS fabric_default_qos = 0;
AXI4_Region fabric_default_region = 0;
Fabric_User fabric_default_user = ?;
// ================================================================
endpackage

736
src_Core/Core/TV_Encode.bsv Normal file
View File

@@ -0,0 +1,736 @@
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved.
package TV_Encode;
// ================================================================
// module mkTV_Encode is a transforming FIFO
// converting Trace_Data into encoded byte vectors
// ================================================================
// BSV lib imports
import Vector :: *;
import FIFOF :: *;
import GetPut :: *;
import ClientServer :: *;
import Connectable :: *;
// ----------------
// BSV additional libs
import GetPut_Aux :: *;
// ================================================================
// Project imports
import ISA_Decls :: *;
import TV_Info :: *;
// ================================================================
interface TV_Encode_IFC;
method Action reset;
// This module receives Trace_Data structs from the CPU and Debug Module
interface Put #(Trace_Data) trace_data_in;
// This module produces tuples (n,vb),
// where 'vb' is a vector of bytes
// with relevant bytes in locations [0]..[n-1]
interface Get #(Tuple2 #(Bit #(32), TV_Vec_Bytes)) tv_vb_out;
endinterface
// ================================================================
(* synthesize *)
module mkTV_Encode (TV_Encode_IFC);
Reg #(Bool) rg_reset_done <- mkReg (True);
// Keep track of last PC for more efficient encoding of incremented PCs
// TODO: currently always sending full PC
Reg #(WordXL) rg_last_pc <- mkReg (0);
FIFOF #(Trace_Data) f_trace_data <- mkFIFOF;
FIFOF #(Tuple2 #(Bit #(32), TV_Vec_Bytes)) f_vb <- mkFIFOF;
// ----------------------------------------------------------------
// BEHAVIOR
rule rl_log_trace_RESET (rg_reset_done && (f_trace_data.first.op == TRACE_RESET));
let td <- pop (f_trace_data);
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
match { .n1, .vb1 } = encode_byte (te_op_hart_reset);
match { .nN, .vbN } = encode_byte (te_op_end_group);
// Concatenate components into a single byte vec
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
match { .nnN, .xN } = vsubst (nn1, x1, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_GPR_WRITE (rg_reset_done && (f_trace_data.first.op == TRACE_GPR_WRITE));
let td <- pop (f_trace_data);
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
match { .n1, .vb1 } = encode_byte (te_op_state_init);
match { .n2, .vb2 } = encode_reg (fv_gpr_regnum (td.rd), td.word1);
match { .nN, .vbN } = encode_byte (te_op_end_group);
// Concatenate components into a single byte vec
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nnN, .xN } = vsubst (nn2, x2, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_FPR_WRITE (rg_reset_done && (f_trace_data.first.op == TRACE_FPR_WRITE));
let td <- pop (f_trace_data);
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
match { .n1, .vb1 } = encode_byte (te_op_state_init);
match { .n2, .vb2 } = encode_reg (fv_fpr_regnum (td.rd), td.word1);
match { .nN, .vbN } = encode_byte (te_op_end_group);
// Concatenate components into a single byte vec
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nnN, .xN } = vsubst (nn2, x2, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_CSR_WRITE (rg_reset_done && (f_trace_data.first.op == TRACE_CSR_WRITE));
let td <- pop (f_trace_data);
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
match { .n1, .vb1 } = encode_byte (te_op_state_init);
match { .n2, .vb2 } = encode_reg (fv_csr_regnum (truncate (td.word3)), td.word4);
match { .nN, .vbN } = encode_byte (te_op_end_group);
// Concatenate components into a single byte vec
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nnN, .xN } = vsubst (nn2, x2, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_MEM_WRITE (rg_reset_done && (f_trace_data.first.op == TRACE_MEM_WRITE));
let td <- pop (f_trace_data);
Bit #(2) mem_req_size = td.word1 [1:0];
Byte size_and_mem_req_op = { 2'b0, mem_req_size, te_mem_req_op_Store };
Byte result_and_size = { te_mem_result_success, 2'b0, mem_req_size };
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
match { .n1, .vb1 } = encode_byte (te_op_state_init);
match { .n2, .vb2 } = encode_byte (te_op_mem_req);
match { .n3, .vb3 } = encode_mlen (td.word3);
match { .n4, .vb4 } = encode_byte (size_and_mem_req_op);
match { .n5, .vb5 } = encode_mdata (mem_req_size, td.word2);
//match { .n6, .vb6 } = encode_byte (te_op_mem_rsp);
//match { .n7, .vb7 } = encode_byte (result_and_size);
match { .nN, .vbN } = encode_byte (te_op_end_group);
// Concatenate components into a single byte vec
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
match { .nn5, .x5 } = vsubst (nn4, x4, n5, vb5);
//match { .nn6, .x6 } = vsubst (nn5, x5, n6, vb6);
//match { .nn7, .x7 } = vsubst (nn6, x6, n7, vb7);
//match { .nnN, .xN } = vsubst (nn7, x7, nN, vbN);
match { .nnN, .xN } = vsubst (nn5, x5, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_OTHER (rg_reset_done && (f_trace_data.first.op == TRACE_OTHER));
let td <- pop (f_trace_data);
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
match { .n1, .vb1 } = encode_pc (td.pc);
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
match { .nN, .vbN } = encode_byte (te_op_end_group);
// Concatenate components into a single byte vec
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nnN, .xN } = vsubst (nn2, x2, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_I_RD (rg_reset_done && (f_trace_data.first.op == TRACE_I_RD));
let td <- pop (f_trace_data);
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
match { .n1, .vb1 } = encode_pc (td.pc);
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
match { .n3, .vb3 } = encode_reg (fv_gpr_regnum (td.rd), td.word1);
match { .nN, .vbN } = encode_byte (te_op_end_group);
// Concatenate components into a single byte vec
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
match { .nnN, .xN } = vsubst (nn3, x3, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_F_RD (rg_reset_done && (f_trace_data.first.op == TRACE_F_RD));
let td <- pop (f_trace_data);
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
match { .n1, .vb1 } = encode_pc (td.pc);
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
match { .n3, .vb3 } = encode_reg (fv_fpr_regnum (td.rd), td.word1);
match { .nN, .vbN } = encode_byte (te_op_end_group);
// Concatenate components into a single byte vec
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nnN, .xN } = vsubst (nn2, x2, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_I_LOAD (rg_reset_done && (f_trace_data.first.op == TRACE_I_LOAD));
let td <- pop (f_trace_data);
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
match { .n1, .vb1 } = encode_pc (td.pc);
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
match { .n3, .vb3 } = encode_reg (fv_gpr_regnum (td.rd), td.word1);
match { .n4, .vb4 } = encode_eaddr (truncate (td.word3));
match { .nN, .vbN } = encode_byte (te_op_end_group);
// Concatenate components into a single byte vec
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
match { .nnN, .xN } = vsubst (nn4, x4, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_F_LOAD (rg_reset_done && (f_trace_data.first.op == TRACE_F_LOAD));
let td <- pop (f_trace_data);
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
match { .n1, .vb1 } = encode_pc (td.pc);
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
match { .n3, .vb3 } = encode_reg (fv_fpr_regnum (td.rd), td.word1);
match { .n4, .vb4 } = encode_eaddr (truncate (td.word3));
match { .nN, .vbN } = encode_byte (te_op_end_group);
// Concatenate components into a single byte vec
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
match { .nnN, .xN } = vsubst (nn4, x4, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_STORE (rg_reset_done && (f_trace_data.first.op == TRACE_STORE));
let td <- pop (f_trace_data);
let funct3 = instr_funct3 (td.instr); // TODO: what if it's a 16b instr?
let mem_req_size = funct3 [1:0];
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
match { .n1, .vb1 } = encode_pc (td.pc);
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
match { .n3, .vb3 } = encode_stval (mem_req_size, td.word2);
match { .n4, .vb4 } = encode_eaddr (truncate (td.word3));
match { .nN, .vbN } = encode_byte (te_op_end_group);
// Concatenate components into a single byte vec
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
match { .nnN, .xN } = vsubst (nn4, x4, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_AMO (rg_reset_done && (f_trace_data.first.op == TRACE_AMO));
let td <- pop (f_trace_data);
let funct3 = instr_funct3 (td.instr); // TODO: what if it's a 16b instr?
let mem_req_size = funct3 [1:0];
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
match { .n1, .vb1 } = encode_pc (td.pc);
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
match { .n3, .vb3 } = encode_reg (fv_gpr_regnum (td.rd), td.word1);
match { .n4, .vb4 } = encode_stval (mem_req_size, td.word2);
match { .n5, .vb5 } = encode_eaddr (truncate (td.word3));
match { .nN, .vbN } = encode_byte (te_op_end_group);
// Concatenate components into a single byte vec
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
match { .nn5, .x5 } = vsubst (nn4, x4, n5, vb5);
match { .nnN, .xN } = vsubst (nn5, x5, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_CSRRX (rg_reset_done && (f_trace_data.first.op == TRACE_CSRRX));
let td <- pop (f_trace_data);
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
match { .n1, .vb1 } = encode_pc (td.pc);
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
match { .n3, .vb3 } = encode_reg (fv_gpr_regnum (td.rd), td.word1);
match { .n4, .vb4 } = ((td.word2 == 0)
? tuple2 (0, ?) // CSR was not written
: encode_reg (fv_csr_regnum (truncate (td.word3)), td.word4));
match { .nN, .vbN } = encode_byte (te_op_end_group);
// Concatenate components into a single byte vec
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
match { .nnN, .xN } = vsubst (nn4, x4, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_TRAP (rg_reset_done && (f_trace_data.first.op == TRACE_TRAP));
let td <- pop (f_trace_data);
// Use new priv mode to decide which trap regs are updated (M, S or U priv)
Priv_Mode priv = truncate (td.rd);
CSR_Addr csr_addr_status = csr_addr_mstatus;
CSR_Addr csr_addr_cause = csr_addr_mcause;
CSR_Addr csr_addr_epc = csr_addr_mepc;
CSR_Addr csr_addr_tval = csr_addr_mtval;
if (priv == s_Priv_Mode) begin
csr_addr_status = csr_addr_sstatus;
csr_addr_cause = csr_addr_scause;
csr_addr_epc = csr_addr_sepc;
csr_addr_tval = csr_addr_stval;
end
else if (priv == u_Priv_Mode) begin
csr_addr_status = csr_addr_ustatus;
csr_addr_cause = csr_addr_ucause;
csr_addr_epc = csr_addr_uepc;
csr_addr_tval = csr_addr_utval;
end
// Omit the instruction if cause is instruction fault since the instruction is then bogus
Bool is_instr_fault = ( (truncate (td.word2) == exc_code_INSTR_ACCESS_FAULT)
|| (truncate (td.word2) == exc_code_INSTR_PAGE_FAULT));
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
match { .n1, .vb1 } = encode_pc (td.pc);
match { .n2, .vb2 } = (is_instr_fault
? tuple2 (0, ?)
: encode_instr (td.instr_sz, td.instr));
match { .n3, .vb3 } = encode_priv (td.rd);
match { .n4, .vb4 } = encode_reg (fv_csr_regnum (csr_addr_status), td.word1);
match { .n5, .vb5 } = encode_reg (fv_csr_regnum (csr_addr_cause), td.word2);
match { .n6, .vb6 } = encode_reg (fv_csr_regnum (csr_addr_epc), truncate (td.word3));
match { .n7, .vb7 } = encode_reg (fv_csr_regnum (csr_addr_tval), td.word4);
match { .nN, .vbN } = encode_byte (te_op_end_group);
// Concatenate components into a single byte vec
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
match { .nn5, .x5 } = vsubst (nn4, x4, n5, vb5);
match { .nn6, .x6 } = vsubst (nn5, x5, n6, vb6);
match { .nn7, .x7 } = vsubst (nn6, x6, n7, vb7);
match { .nnN, .xN } = vsubst (nn7, x7, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_INTR (rg_reset_done && (f_trace_data.first.op == TRACE_INTR));
let td <- pop (f_trace_data);
// Use new priv mode to decide which trap regs are updated (M, S or U priv)
Priv_Mode priv = truncate (td.rd);
CSR_Addr csr_addr_status = csr_addr_mstatus;
CSR_Addr csr_addr_cause = csr_addr_mcause;
CSR_Addr csr_addr_epc = csr_addr_mepc;
CSR_Addr csr_addr_tval = csr_addr_mtval;
if (priv == s_Priv_Mode) begin
csr_addr_status = csr_addr_sstatus;
csr_addr_cause = csr_addr_scause;
csr_addr_epc = csr_addr_sepc;
csr_addr_tval = csr_addr_stval;
end
else if (priv == u_Priv_Mode) begin
csr_addr_status = csr_addr_ustatus;
csr_addr_cause = csr_addr_ucause;
csr_addr_epc = csr_addr_uepc;
csr_addr_tval = csr_addr_utval;
end
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
match { .n1, .vb1 } = encode_pc (td.pc);
match { .n2, .vb2 } = encode_priv (td.rd);
match { .n3, .vb3 } = encode_reg (fv_csr_regnum (csr_addr_status), td.word1);
match { .n4, .vb4 } = encode_reg (fv_csr_regnum (csr_addr_cause), td.word2);
match { .n5, .vb5 } = encode_reg (fv_csr_regnum (csr_addr_epc), truncate (td.word3));
match { .n6, .vb6 } = encode_reg (fv_csr_regnum (csr_addr_tval), td.word4);
match { .nN, .vbN } = encode_byte (te_op_end_group);
// Concatenate components into a single byte vec
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
match { .nn5, .x5 } = vsubst (nn4, x4, n5, vb5);
match { .nn6, .x6 } = vsubst (nn5, x5, n6, vb6);
match { .nnN, .xN } = vsubst (nn6, x6, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
endrule
rule rl_log_trace_RET (rg_reset_done && (f_trace_data.first.op == TRACE_RET));
let td <- pop (f_trace_data);
// Encode components of td into byte vecs
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
match { .n1, .vb1 } = encode_pc (td.pc);
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
match { .n3, .vb3 } = encode_priv (td.rd);
match { .n4, .vb4 } = encode_reg (fv_csr_regnum (csr_addr_mstatus), td.word1);
match { .nN, .vbN } = encode_byte (te_op_end_group);
// Concatenate components into a single byte vec
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
match { .nnN, .xN } = vsubst (nn4, x4, nN, vbN);
f_vb.enq (tuple2 (nnN, xN));
endrule
// ----------------------------------------------------------------
// INTERFACE
method Action reset ();
endmethod
interface Put trace_data_in = toPut (f_trace_data);
interface Get tv_vb_out = toGet (f_vb);
endmodule
// ****************************************************************
// ****************************************************************
// ****************************************************************
// Encoding Trace_Data into Byte vectors
// ================================================================
// Encodings
// cf. "Trace Protocol Specification Version 2018-09-12, Darius Rad, Bluespec, Inc."
Bit #(8) te_op_begin_group = 1;
Bit #(8) te_op_end_group = 2;
Bit #(8) te_op_incr_pc = 3;
Bit #(8) te_op_full_reg = 4;
Bit #(8) te_op_incr_reg = 5;
Bit #(8) te_op_incr_reg_OR = 6;
Bit #(8) te_op_addl_state = 7;
Bit #(8) te_op_mem_req = 8;
Bit #(8) te_op_mem_rsp = 9;
Bit #(8) te_op_hart_reset = 10;
Bit #(8) te_op_state_init = 11;
Bit #(8) te_op_16b_instr = 16;
Bit #(8) te_op_32b_instr = 17;
Bit #(4) te_mem_req_size_8 = 0;
Bit #(4) te_mem_req_size_16 = 1;
Bit #(4) te_mem_req_size_32 = 2;
Bit #(4) te_mem_req_size_64 = 3;
Bit #(4) te_mem_req_op_Load = 0;
Bit #(4) te_mem_req_op_Store = 1;
Bit #(4) te_mem_req_op_LR = 2;
Bit #(4) te_mem_req_op_SC = 3;
Bit #(4) te_mem_req_op_AMO_swap = 4;
Bit #(4) te_mem_req_op_AMO_add = 5;
Bit #(4) te_mem_req_op_AMO_xor = 6;
Bit #(4) te_mem_req_op_AMO_and = 7;
Bit #(4) te_mem_req_op_AMO_or = 8;
Bit #(4) te_mem_req_op_AMO_min = 9;
Bit #(4) te_mem_req_op_AMO_max = 10;
Bit #(4) te_mem_req_op_AMO_minu = 11;
Bit #(4) te_mem_req_op_AMO_maxu = 12;
Bit #(4) te_mem_req_op_ifetch = 13;
Bit #(4) te_mem_result_success = 0;
Bit #(4) te_mem_result_failure = 1;
Bit #(8) te_op_addl_state_priv = 1;
Bit #(8) te_op_addl_state_paddr = 2;
Bit #(8) te_op_addl_state_eaddr = 3;
Bit #(8) te_op_addl_state_data8 = 4;
Bit #(8) te_op_addl_state_data16 = 5;
Bit #(8) te_op_addl_state_data32 = 6;
Bit #(8) te_op_addl_state_data64 = 7;
Bit #(8) te_op_addl_state_mtime = 8;
Bit #(8) te_op_addl_state_pc_paddr = 9;
Bit #(8) te_op_addl_state_pc = 10;
// ================================================================
// Architectural register address encodings
// cf. "RISC-V External Debug Support"
// 2018-10-02_riscv_debug_spec_v0.13_DRAFT_f2873e71
// "Table 3.3 Abstract Register Numbers"
function Bit #(16) fv_csr_regnum (CSR_Addr csr_addr);
return zeroExtend (csr_addr);
endfunction
function Bit #(16) fv_gpr_regnum (RegName gpr_addr);
return 'h1000 + zeroExtend (gpr_addr);
endfunction
function Bit #(16) fv_fpr_regnum (RegName fpr_addr);
return 'h1020 + zeroExtend (fpr_addr);
endfunction
// ================================================================
// vsubst substitutes vb1[j1:j1+j2-1] with vb2[0:j2-1]
function Tuple2 #(Bit #(32),
Vector #(TV_VB_SIZE, Byte))
vsubst (Bit #(32) j1, Vector #(TV_VB_SIZE, Byte) vb1,
Bit #(32) j2, Vector #(m, Byte) vb2);
function Byte f (Integer j);
Byte x = vb1 [j];
Bit #(32) jj = fromInteger (j);
if ((j1 <= jj) && (jj < j1 + j2))
x = vb2 [jj - j1];
return x;
endfunction
let v = genWith (f);
let n = j1 + j2;
return tuple2 (n, v);
endfunction
// ================================================================
// Encoding of Trace_Data into byte vectors
// Every function below returns:
// (n, vb) :: Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte))
// where vb is a vector of bytes with relevant bytes in vb[0]..vb[n-1]
// ================================================================
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_byte (Byte x);
return tuple2 (1, replicate (x));
endfunction
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_mlen (Bit #(64) word);
Vector #(TV_VB_SIZE, Byte) vb = newVector;
Bit #(32) n;
vb [0] = word[7:0];
vb [1] = word [15:8];
vb [2] = word [23:16];
vb [3] = word [31:24];
vb [4] = word [39:32];
vb [5] = word [47:40];
vb [6] = word [55:48];
vb [7] = word [63:56];
`ifdef RV32
n = 4; // MLEN = 32
`ifdef SV34
n = 5; // MLEN = 34
`endif
`else
n = 8; // MLEN = 64
`endif
return tuple2 (n, vb);
endfunction
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_mdata (MemReqSize mem_req_size, WordXL word);
Vector #(TV_VB_SIZE, Byte) vb = newVector;
Bit #(32) n;
vb [0] = word[7:0];
vb [1] = word [15:8];
vb [2] = word [23:16];
vb [3] = word [31:24];
`ifdef RV64
vb [4] = word [39:32];
vb [5] = word [47:40];
vb [6] = word [55:48];
vb [7] = word [63:56];
`endif
n = case (mem_req_size)
f3_SIZE_B: 1;
f3_SIZE_H: 2;
f3_SIZE_W: 4;
f3_SIZE_D: 8;
endcase;
return tuple2 (n, vb);
endfunction
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_instr (ISize isize, Bit #(32) instr);
Vector #(TV_VB_SIZE, Byte) vb = newVector;
Bit #(32) n = ((isize == ISIZE16BIT) ? 3 : 5);
vb [0] = ((isize == ISIZE16BIT) ? te_op_16b_instr : te_op_32b_instr);
vb [1] = instr [7:0];
vb [2] = instr [15:8];
vb [3] = instr [23:16];
vb [4] = instr [31:24];
return tuple2 (n, vb);
endfunction
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_reg (Bit #(16) regnum, WordXL word);
Vector #(TV_VB_SIZE, Byte) vb = newVector;
Bit #(32) n = 0;
vb [0] = te_op_full_reg;
vb [1] = regnum [7:0];
vb [2] = regnum [15:8];
vb [3] = word[7:0];
vb [4] = word [15:8];
vb [5] = word [23:16];
vb [6] = word [31:24];
n = 7;
`ifdef RV64
vb [7] = word [39:32];
vb [8] = word [47:40];
vb [9] = word [55:48];
vb [10] = word [63:56];
n = 11;
`endif
if (regnum == fv_gpr_regnum (0)) n = 0;
return tuple2 (n, vb);
endfunction
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_priv (Bit #(5) priv);
Vector #(TV_VB_SIZE, Byte) vb = newVector;
vb [0] = te_op_addl_state;
vb [1] = te_op_addl_state_priv;
vb [2] = zeroExtend (priv);
return tuple2 (3, vb);
endfunction
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_pc (WordXL word);
Vector #(TV_VB_SIZE, Byte) vb = newVector;
Bit #(32) n;
vb [0] = te_op_addl_state;
vb [1] = te_op_addl_state_pc;
vb [2] = word [7:0];
vb [3] = word [15:8];
vb [4] = word [23:16];
vb [5] = word [31:24];
n = 6;
`ifdef RV64
vb [6] = word [39:32];
vb [7] = word [47:40];
vb [8] = word [55:48];
vb [9] = word [63:56];
n = 10;
`endif
return tuple2 (n, vb);
endfunction
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_eaddr (WordXL word);
Vector #(TV_VB_SIZE, Byte) vb = newVector;
Bit #(32) n;
vb [0] = te_op_addl_state;
vb [1] = te_op_addl_state_eaddr;
vb [2] = word [7:0];
vb [3] = word [15:8];
vb [4] = word [23:16];
vb [5] = word [31:24];
n = 6;
`ifdef RV64
vb [6] = word [39:32];
vb [7] = word [47:40];
vb [8] = word [55:48];
vb [9] = word [63:56];
n = 10;
`endif
return tuple2 (n, vb);
endfunction
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_stval (MemReqSize mem_req_size, WordXL word);
Vector #(TV_VB_SIZE, Byte) vb = newVector;
Bit #(32) n;
vb [0] = te_op_addl_state;
vb [1] = case (mem_req_size)
f3_SIZE_B: te_op_addl_state_data8;
f3_SIZE_H: te_op_addl_state_data16;
f3_SIZE_W: te_op_addl_state_data32;
f3_SIZE_D: te_op_addl_state_data64;
endcase;
vb [2] = word [7:0];
vb [3] = word [15:8];
vb [4] = word [23:16];
vb [5] = word [31:24];
`ifdef RV64
vb [6] = word [39:32];
vb [7] = word [47:40];
vb [8] = word [55:48];
vb [9] = word [63:56];
`endif
n = case (mem_req_size)
f3_SIZE_B: 2 + 1;
f3_SIZE_H: 2 + 2;
f3_SIZE_W: 2 + 4;
f3_SIZE_D: 2 + 8;
endcase;
return tuple2 (n, vb);
endfunction
// ================================================================
endpackage

239
src_Core/Core/TV_Taps.bsv Normal file
View File

@@ -0,0 +1,239 @@
// Copyright (c) 2018-2019 Bluespec, Inc. All Rights Reserved.
package TV_Taps;
// ================================================================
// This package defines 'taps' on connections between
// - DM and CPU, on which DM accesses CPU GPRs, FPRs and CSRs
// - DM and memory bus, on which DM accesses memory
// Each tap snoops 'writes', and produces a corresponsing Trace_Data
// write-memory command for the Tandem Verifier, so that it keeps its
// GPRs, FPRs, CSRs and memories in sync.
// ================================================================
// BSV library imports
import Assert :: *;
import BUtils :: *;
import FIFOF :: *;
import GetPut :: *;
import ClientServer :: *;
import Connectable :: *;
import Memory :: *;
// ----------------
// BSV additional libs
import Semi_FIFOF :: *;
import GetPut_Aux :: *;
// ================================================================
// Project imports
import ISA_Decls :: *;
import TV_Info :: *;
import AXI4_Types :: *;
import Fabric_Defs :: *;
// ================================================================
// DM-to-memory tap
interface DM_Mem_Tap_IFC;
interface AXI4_Slave_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) slave;
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master;
interface Get #(Trace_Data) trace_data_out;
endinterface
(* synthesize *)
module mkDM_Mem_Tap (DM_Mem_Tap_IFC);
// Transactor facing DM
AXI4_Slave_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) slave_xactor <- mkAXI4_Slave_Xactor;
// Transactor facing memory bus
AXI4_Master_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master_xactor <- mkAXI4_Master_Xactor;
// Tap output
FIFOF #(Trace_Data) f_trace_data <- mkFIFOF;
// ----------------
// AXI requests
// Snoop write requests
rule write_reqs;
let wr_addr = slave_xactor.o_wr_addr.first;
slave_xactor.o_wr_addr.deq;
let wr_data = slave_xactor.o_wr_data.first;
slave_xactor.o_wr_data.deq;
// Pass-through
master_xactor.i_wr_addr.enq (wr_addr);
master_xactor.i_wr_data.enq (wr_data);
// Tap
Bit #(64) paddr = ?;
Bit #(64) stval = ?;
`ifdef FABRIC64
if (wr_data.wstrb == 'h0f) begin
paddr = zeroExtend (wr_addr.awaddr);
stval = (wr_data.wdata & 'h_FFFF_FFFF);
end
else if (wr_data.wstrb == 'hf0) begin
paddr = zeroExtend (wr_addr.awaddr);
stval = ((wr_data.wdata >> 32) & 'h_FFFF_FFFF);
end
else
dynamicAssert(False, "mkDM_Mem_Tap: unsupported byte enables");
`else
paddr = zeroExtend (wr_addr.awaddr);
stval = zeroExtend (wr_data.wdata);
`endif
Trace_Data td = mkTrace_MEM_WRITE (f3_SIZE_W, truncate (stval), paddr);
f_trace_data.enq (td);
endrule
// Read requests, write responses and read responses are not snooped
mkConnection (slave_xactor.o_rd_addr, master_xactor.i_rd_addr);
mkConnection (slave_xactor.i_wr_resp, master_xactor.o_wr_resp);
mkConnection (slave_xactor.i_rd_data, master_xactor.o_rd_data);
// ================================================================
// INTERFACE
// Facing DM
interface slave = slave_xactor.axi_side;
// Facing bus
interface master = master_xactor.axi_side;
// Tap towards verifier
interface Get trace_data_out = toGet (f_trace_data);
endmodule: mkDM_Mem_Tap
// ================================================================
// DM-to-CPU GPR tap (for writes to GPRs)
interface DM_GPR_Tap_IFC;
interface MemoryClient #(5, XLEN) client;
interface MemoryServer #(5, XLEN) server;
interface Get #(Trace_Data) trace_data_out;
endinterface
(* synthesize *)
module mkDM_GPR_Tap (DM_GPR_Tap_IFC);
// req from DM
FIFOF #(MemoryRequest #(5, XLEN)) f_req_in <- mkFIFOF;
// req to CPU
FIFOF #(MemoryRequest #(5, XLEN)) f_req_out <- mkFIFOF;
// resp CPU->DM
FIFOF #(MemoryResponse #(XLEN)) f_rsp <- mkFIFOF;
// Tap to TV
FIFOF #(Trace_Data) f_trace_data <- mkFIFOF;
rule request;
let req <- pop (f_req_in);
// Pass-through to CPU
f_req_out.enq(req);
// Snoop writes and send trace data to TV
if (req.write) begin
Trace_Data td;
td = mkTrace_GPR_WRITE (req.address, req.data);
f_trace_data.enq (td);
end
endrule
interface MemoryClient client = toGPClient (f_req_out, f_rsp);
interface MemoryServer server = toGPServer (f_req_in, f_rsp);
interface Get trace_data_out = toGet (f_trace_data);
endmodule: mkDM_GPR_Tap
// ================================================================
// DM-to-CPU FPR tap (for writes to FPRs)
`ifdef ISA_F_OR_D
interface DM_FPR_Tap_IFC;
interface MemoryClient #(5, FLEN) client;
interface MemoryServer #(5, FLEN) server;
interface Get #(Trace_Data) trace_data_out;
endinterface
(* synthesize *)
module mkDM_FPR_Tap (DM_FPR_Tap_IFC);
// req from DM
FIFOF #(MemoryRequest #(5, FLEN)) f_req_in <- mkFIFOF;
// req to CPU
FIFOF #(MemoryRequest #(5, FLEN)) f_req_out <- mkFIFOF;
// resp CPU->DM
FIFOF #(MemoryResponse #(FLEN)) f_rsp <- mkFIFOF;
// Tap to TV
FIFOF #(Trace_Data) f_trace_data <- mkFIFOF;
rule request;
let req <- pop (f_req_in);
// Pass-through to CPU
f_req_out.enq(req);
// Snoop writes and send trace data to TV
if (req.write) begin
Trace_Data td;
td = mkTrace_FPR_WRITE (req.address, req.data);
f_trace_data.enq (td);
end
endrule
interface MemoryClient client = toGPClient (f_req_out, f_rsp);
interface MemoryServer server = toGPServer (f_req_in, f_rsp);
interface Get trace_data_out = toGet (f_trace_data);
endmodule: mkDM_FPR_Tap
`endif
// ================================================================
// DM-to-CPU CSR tap (for writes to CSRs)
interface DM_CSR_Tap_IFC;
interface MemoryClient #(12, XLEN) client;
interface MemoryServer #(12, XLEN) server;
interface Get #(Trace_Data) trace_data_out;
endinterface
(* synthesize *)
module mkDM_CSR_Tap (DM_CSR_Tap_IFC);
// req from DM
FIFOF #(MemoryRequest #(12, XLEN)) f_req_in <- mkFIFOF;
// req to CPU
FIFOF #(MemoryRequest #(12, XLEN)) f_req_out <- mkFIFOF;
// resp CPU->DM
FIFOF #(MemoryResponse #(XLEN)) f_rsp <- mkFIFOF;
// Tap to TV
FIFOF #(Trace_Data) f_trace_data <- mkFIFOF;
rule request;
let req <- pop (f_req_in);
// Pass-through to CPU
f_req_out.enq(req);
// Snoop writes and send trace data to TV
if (req.write) begin
Trace_Data td = mkTrace_CSR_WRITE (req.address, req.data);
f_trace_data.enq (td);
end
endrule
interface MemoryClient client = toGPClient (f_req_out, f_rsp);
interface MemoryServer server = toGPServer (f_req_in, f_rsp);
interface Get trace_data_out = toGet (f_trace_data);
endmodule: mkDM_CSR_Tap
// ================================================================
endpackage

View File

@@ -0,0 +1,487 @@
// Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved.
package DM_Abstract_Commands;
// ================================================================
// This package implements the 'Abstract Command' part of the RISC-V
// Debug Module, i.e., read/write access to RISC-V GPRs, FPRs and CSRs.
// ================================================================
// BSV library imports
import Memory :: *;
import FIFOF :: *;
import GetPut :: *;
import ClientServer :: *;
// ----------------
// Other library imports
import GetPut_Aux :: *;
import Semi_FIFOF :: *;
import Cur_Cycle :: *;
// ================================================================
import ISA_Decls :: *;
import DM_Common :: *;
// ================================================================
// Interface
interface DM_Abstract_Commands_IFC;
method Action reset;
// ----------------
// DMI facing GDB/host
method ActionValue #(DM_Word) av_read (DM_Addr dm_addr);
method Action write (DM_Addr dm_addr, DM_Word dm_word);
// ----------------
// Facing CPU/hart
interface MemoryClient #(5, XLEN) hart0_gpr_mem_client;
interface MemoryClient #(12, XLEN) hart0_csr_mem_client;
endinterface
// ================================================================
(* synthesize *)
module mkDM_Abstract_Commands (DM_Abstract_Commands_IFC);
Integer verbosity = 0; // Normally 0; non-zero for debugging
// ----------------------------------------------------------------
Reg #(Bool) rg_start_reg_access <- mkReg (False);
// FIFOs for request/response to access GPRs
FIFOF #(MemoryRequest #(5, XLEN)) f_hart0_gpr_reqs <- mkFIFOF1;
FIFOF #(MemoryResponse #( XLEN)) f_hart0_gpr_rsps <- mkFIFOF1;
// FIFOs for request/response to access CSRs
FIFOF #(MemoryRequest #(12, XLEN)) f_hart0_csr_reqs <- mkFIFOF1;
FIFOF #(MemoryResponse #( XLEN)) f_hart0_csr_rsps <- mkFIFOF1;
// ----------------------------------------------------------------
// rg_data0
Reg #(DM_Word ) rg_data0 <- mkRegU;
`ifdef RV64
Reg #(DM_Word ) rg_data1 <- mkRegU;
`endif
// ----------------------------------------------------------------
// rg_data2..11: not implemented
// rg_abstractauto: not implemented
// rg_progbuf0..15: not implemented
// ----------------------------------------------------------------
// rg_abstractcs
Reg #(Bool) rg_abstractcs_busy <- mkRegU;
Reg #(DM_abstractcs_cmderr) rg_abstractcs_cmderr <- mkRegU;
Bit #(5) abstractcs_progsize = 0;
Bit #(5) abstractcs_datacount = 0;
DM_Word virt_rg_abstractcs = {3'b0,
abstractcs_progsize,
11'b0,
pack (rg_abstractcs_busy),
1'b0,
pack (rg_abstractcs_cmderr),
3'b0,
abstractcs_datacount};
function Action fa_rg_abstractcs_write (DM_Word dm_word);
action
if (rg_abstractcs_busy) begin
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_BUSY;
$display ("(%0d): DM_Abstract_Commands.write: [abstractcs] <= 0x%08h: ERROR", cur_cycle, dm_word);
$display (" DM is busy with a previous abstract command");
end
else if (fn_abstractcs_cmderr (dm_word) != DM_ABSTRACTCS_CMDERR_NONE) begin
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_NONE;
if (verbosity != 0)
$display ("(%0d): DM_Abstract_Commands.write [abstractcs]: clearing cmderr", cur_cycle);
end
else begin
if (verbosity != 0)
$display ("(%0d): DM_Abstract_Commands.write [abstractcs]: cmderr unchanged", cur_cycle);
end
endaction
endfunction
// ----------------------------------------------------------------
// rg_command
// cmdtype no register, since we only support 'access reg'
// size no register, since we only support 'lower 32b' for RV32
// and 'lower 64b' for RV64
// postexec no register, since we don't support Program Buffer
// transfer no register, since we always do transfers
Reg #(Bool) rg_command_access_reg_write <- mkRegU;
// regno: we only implement lower 13 bits of this 16-bit field
Reg #(Bit #(13)) rg_command_access_reg_regno <- mkRegU;
DM_Word virt_rg_command = fn_mk_command_access_reg (
DM_COMMAND_ACCESS_REG_SIZE_LOWER32
, False // postexec
, True // transfer
, rg_command_access_reg_write
, zeroExtend (rg_command_access_reg_regno));
function Action fa_rg_command_write (DM_Word dm_word);
action
// TODO: check that CPU is halted, else set cmderr = DM_ABSTRACTCS_CMDERR_HALT_RESUME
DM_abstractcs_cmderr cmderr = rg_abstractcs_cmderr;
let size = fn_command_access_reg_size (dm_word);
// Ignore if 'cmderr' is non-zero
if (cmderr != DM_ABSTRACTCS_CMDERR_NONE) begin
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: ERROR", cur_cycle, dm_word);
$display (" Ignoring since 'cmderr' is 0x%0h", cmderr);
end
else begin
if (rg_abstractcs_busy) begin
cmderr = DM_ABSTRACTCS_CMDERR_BUSY;
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: ERROR", cur_cycle, dm_word);
$display (" DM is busy with a previous abstract command");
end
// Only 'Access Reg' cmdtype is supported
else if (fn_command_cmdtype (dm_word) != DM_COMMAND_CMDTYPE_ACCESS_REG) begin
cmderr = DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED;
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: ERROR", cur_cycle, dm_word);
$display (" ", fshow (fn_command_cmdtype (dm_word)), " not supported");
end
`ifdef RV32
// Only lower 32-bit access is supported
else if (size != DM_COMMAND_ACCESS_REG_SIZE_LOWER32) begin
cmderr = DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED;
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: ERROR", cur_cycle, dm_word);
$display (" For DM_COMMAND_CMDTYPE_ACCESS_REG, ",
fshow (fn_command_access_reg_size (dm_word)), " not supported in RV32 mode");
end
`endif
`ifdef RV64
// Only lower 32-bit and 64-bit access is supported
else if (size != DM_COMMAND_ACCESS_REG_SIZE_LOWER64)
begin
cmderr = DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED;
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: ERROR", cur_cycle, dm_word);
$display (" For DM_COMMAND_CMDTYPE_ACCESS_REG, ",
fshow (fn_command_access_reg_size (dm_word)), " not supported in RV64 mode");
end
`endif
// 'postexec' is not supported
else if (fn_command_access_reg_postexec (dm_word) == True) begin
cmderr = DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED;
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: ERROR", cur_cycle, dm_word);
$display (" For DM_COMMAND_CMDTYPE_ACCESS_REG, postexec not supported");
end
// non-'transfer' is not supported
else if (fn_command_access_reg_transfer (dm_word) == False) begin
cmderr = DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED;
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: ERROR", cur_cycle, dm_word);
$display (" For DM_COMMAND_CMDTYPE_ACCESS_REG, no-transfer not supported");
end
else begin
Bool is_write = fn_command_access_reg_write (dm_word);
Bit #(13) regno = truncate (fn_command_access_reg_regno (dm_word));
rg_command_access_reg_write <= is_write;
rg_command_access_reg_regno <= regno;
rg_abstractcs_busy <= True;
rg_start_reg_access <= True;
cmderr = DM_ABSTRACTCS_CMDERR_NONE;
if (verbosity != 0)
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: OKAY", cur_cycle, dm_word);
end
rg_abstractcs_cmderr <= cmderr;
end
endaction
endfunction
// ----------------------------------------------------------------
// Start reads/writes
Bool is_csr = ( (fromInteger (dm_command_access_reg_regno_csr_0) <= rg_command_access_reg_regno)
&& (rg_command_access_reg_regno <= fromInteger (dm_command_access_reg_regno_csr_FFF)));
Bool is_gpr = ( (fromInteger (dm_command_access_reg_regno_gpr_0) <= rg_command_access_reg_regno)
&& (rg_command_access_reg_regno <= fromInteger (dm_command_access_reg_regno_gpr_1F)));
Bit #(12) csr_addr = truncate (rg_command_access_reg_regno - fromInteger (dm_command_access_reg_regno_csr_0));
Bit #(5) gpr_addr = truncate (rg_command_access_reg_regno - fromInteger (dm_command_access_reg_regno_gpr_0));
// ----------------------------------------------------------------
// Read/Write CSR
rule rl_start_write_csr ( rg_abstractcs_busy
&& rg_start_reg_access
&& rg_command_access_reg_write
&& is_csr);
if (verbosity != 0)
`ifdef RV32
$display ("(%0d): DM_Abstract_Commands.write [command]: write CSR [0x%0h] <= 0x%08h", cur_cycle,
csr_addr, rg_data0);
`endif
`ifdef RV64
$display ("(%0d): DM_Abstract_Commands.write [command]: write CSR [0x%0h] <= 0x%016h", cur_cycle,
csr_addr, {rg_data1, rg_data0});
`endif
let req = MemoryRequest {write: True, byteen: '1, address: csr_addr,
`ifdef RV32
data: rg_data0
`endif
`ifdef RV64
data: {rg_data1, rg_data0}
`endif
};
f_hart0_csr_reqs.enq (req);
rg_start_reg_access <= False;
rg_abstractcs_busy <= False;
endrule
rule rl_start_read_csr ( rg_abstractcs_busy
&& rg_start_reg_access
&& (! rg_command_access_reg_write)
&& is_csr);
if (verbosity != 0)
$display ("(%0d): DM_Abstract_Commands.write [command]: read CSR [0x%0h]", cur_cycle, csr_addr);
let req = MemoryRequest {write: False, byteen: '1, address: csr_addr, data: ?};
f_hart0_csr_reqs.enq (req);
rg_start_reg_access <= False;
endrule
rule rl_finish_csr_read (rg_abstractcs_busy);
let rsp <- pop (f_hart0_csr_rsps);
`ifdef RV32
rg_data0 <= rsp.data;
`endif
`ifdef RV64
rg_data0 <= truncate (rsp.data);
rg_data1 <= rsp.data[63:32];
`endif
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_NONE;
if (verbosity != 0)
`ifdef RV32
$display ("(%0d): DM_Abstract_Commands: csr read data is 0x%08h", cur_cycle, rsp.data);
`endif
`ifdef RV64
$display ("(%0d): DM_Abstract_Commands: csr read data is 0x%016h", cur_cycle, rsp.data);
`endif
rg_abstractcs_busy <= False;
endrule
// ----------------------------------------------------------------
// Read/Write GPR
rule rl_start_write_gpr ( rg_abstractcs_busy
&& rg_start_reg_access
&& rg_command_access_reg_write
&& is_gpr);
if (verbosity != 0)
`ifdef RV32
$display ("(%0d): DM_Abstract_Commands.write [command]: write GPR [0x%0h] <= 0x%08h", cur_cycle,
gpr_addr, rg_data0);
`endif
`ifdef RV64
$display ("(%0d): DM_Abstract_Commands.write [command]: write GPR [0x%0h] <= 0x%016h", cur_cycle,
gpr_addr, {rg_data1, rg_data0});
`endif
let req = MemoryRequest {write: True,
byteen: '1,
address: gpr_addr,
`ifdef RV32
data: rg_data0
`endif
`ifdef RV64
data: {rg_data1, rg_data0}
`endif
};
f_hart0_gpr_reqs.enq (req);
rg_start_reg_access <= False;
rg_abstractcs_busy <= False;
endrule
rule rl_start_read_gpr ( rg_abstractcs_busy
&& rg_start_reg_access
&& (! rg_command_access_reg_write)
&& is_gpr);
if (verbosity != 0)
$display ("(%0d): DM_Abstract_Commands.write [command]: read GPR [0x%0h]", cur_cycle, gpr_addr);
let req = MemoryRequest {
write: False
, byteen: '1
, address: gpr_addr
, data: ?
};
f_hart0_gpr_reqs.enq (req);
rg_start_reg_access <= False;
endrule
rule rl_finish_gpr_read (rg_abstractcs_busy);
let rsp <- pop (f_hart0_gpr_rsps);
`ifdef RV32
rg_data0 <= rsp.data;
`endif
`ifdef RV64
rg_data0 <= truncate (rsp.data);
rg_data1 <= rsp.data[63:32];
`endif
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_NONE;
if (verbosity != 0)
`ifdef RV32
$display ("(%0d): DM_Abstract_Commands: gpr read data is 0x%08h", cur_cycle, rsp.data);
`endif
`ifdef RV64
$display ("(%0d): DM_Abstract_Commands: gpr read data is 0x%016h", cur_cycle, rsp.data);
`endif
rg_abstractcs_busy <= False;
endrule
// ----------------
// Read/Write unknown address
rule rl_start_write_unknown ( rg_abstractcs_busy
&& rg_start_reg_access
&& rg_command_access_reg_write
&& (! is_csr) && (! is_gpr));
if (verbosity != 0)
$display ("(%0d): DM_Abstract_Commands.write [command]: write unknown RISC-V regno [0x%0h] <= 0x%08h", cur_cycle,
rg_command_access_reg_regno, rg_data0);
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_OTHER;
rg_start_reg_access <= False;
rg_abstractcs_busy <= False;
endrule
rule rl_start_read_unknown ( rg_abstractcs_busy
&& rg_start_reg_access
&& (! rg_command_access_reg_write)
&& (! is_csr) && (! is_gpr));
if (verbosity != 0)
$display ("(%0d): DM_Abstract_Commands.write [command]: read unknown RISC-V regno [0x%0h] => ...", cur_cycle,
rg_command_access_reg_regno);
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_OTHER;
rg_start_reg_access <= False;
rg_abstractcs_busy <= False;
endrule
// ----------------------------------------------------------------
// Finish CSR and GPR reads
// ================================================================
// INTERFACE
method Action reset;
rg_start_reg_access <= False;
f_hart0_gpr_reqs.clear;
f_hart0_gpr_rsps.clear;
f_hart0_csr_reqs.clear;
f_hart0_csr_rsps.clear;
rg_abstractcs_busy <= False;
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_NONE;
rg_command_access_reg_write <= False;
rg_command_access_reg_regno <= fromInteger (dm_command_access_reg_regno_gpr_0);
rg_data0 <= 0;
`ifdef RV64
rg_data1 <= 0;
`endif
if (verbosity != 0)
$display ("(%0d): DM_Abstract_Commands: reset", cur_cycle);
endmethod
// ----------------
// Facing DMI/GDB
method ActionValue #(DM_Word) av_read (DM_Addr dm_addr);
actionvalue
let dm_addr_name = fshow_dm_addr (dm_addr);
DM_Word dm_word = case (dm_addr)
dm_addr_abstractcs: virt_rg_abstractcs;
dm_addr_command: virt_rg_command;
dm_addr_data0: rg_data0;
`ifdef RV64
dm_addr_data1: rg_data1;
`endif
// dm_addr_data2..data3
// dm_addr_abstractauto
// dm_addr_progbuf0..15
endcase;
if (verbosity != 0)
$display ("(%0d): DM_Abstract_Commands.av_read: [", cur_cycle, dm_addr_name, "] => 0x%08h", dm_word);
return dm_word;
endactionvalue
endmethod
method Action write (DM_Addr dm_addr, DM_Word dm_word);
action
let dm_addr_name = fshow_dm_addr (dm_addr);
if (dm_addr == dm_addr_abstractcs)
fa_rg_abstractcs_write (dm_word);
else if (rg_abstractcs_cmderr != DM_ABSTRACTCS_CMDERR_NONE) begin
if (verbosity != 0) begin
$display ("(%0d): DM_Abstract_Commands.write: [", cur_cycle, dm_addr_name, "] <= 0x%08h: ERROR", dm_word);
$display (" Ignoring: previous cmderr ", fshow (rg_abstractcs_cmderr));
end
end
else if (dm_addr == dm_addr_command)
fa_rg_command_write (dm_word);
else if (dm_addr == dm_addr_data0) begin
rg_data0 <= dm_word;
if (verbosity != 0)
$display ("(%0d): DM_Abstract_Commands.write: [", cur_cycle, dm_addr_name, "] <= 0x%08h", dm_word);
end
`ifdef RV64
else if (dm_addr == dm_addr_data1) begin
rg_data1 <= dm_word;
if (verbosity != 0)
$display ("(%0d): DM_Abstract_Commands.write: [", cur_cycle, dm_addr_name, "] <= 0x%08h", dm_word);
end
`endif
else begin
// dm_addr_data2..12
// dm_addr_abstractauto
// dm_addr_progbuf0..15
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED;
$display ("(%0d): DM_Abstract_Commands.write: [", cur_cycle, dm_addr_name,
"] <= 0x%08h: ERROR: not supported", dm_word);
end
endaction
endmethod
// ----------------
// Facing CPU/hart
interface MemoryClient hart0_gpr_mem_client = toGPClient (f_hart0_gpr_reqs, f_hart0_gpr_rsps);
interface MemoryClient hart0_csr_mem_client = toGPClient (f_hart0_csr_reqs, f_hart0_csr_rsps);
endmodule
// ================================================================
endpackage

View File

@@ -0,0 +1,512 @@
// Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved.
package DM_Common;
// ================================================================
// This package has non-implementation-specific definitions, i.e.,
// just encodes things in the spec.
// ================================================================
// BSV library imports
// None
// ================================================================
// Project imports
// None
// ================================================================
// Debug Module Interface (DMI) addresses and data.
// Note: data is always 32b, whether the connected CPU is RV32, RV64 or RV128.
typedef Bit #(7) DM_Addr;
DM_Addr max_DM_Addr = 'h5F;
typedef Bit #(32) DM_Word;
// ================================================================
// Debug Module address map
// ----------------
// Run Control
DM_Addr dm_addr_dmcontrol = 'h10;
DM_Addr dm_addr_dmstatus = 'h11;
DM_Addr dm_addr_hartinfo = 'h12;
DM_Addr dm_addr_haltsum = 'h13;
DM_Addr dm_addr_hawindowsel = 'h14;
DM_Addr dm_addr_hawindow = 'h15;
DM_Addr dm_addr_devtreeaddr0 = 'h19;
DM_Addr dm_addr_authdata = 'h30;
DM_Addr dm_addr_haltregion0 = 'h40;
DM_Addr dm_addr_haltregion31 = 'h5F;
DM_Addr dm_addr_verbosity = 'h60; // Non-standard (not in spec)
// ----------------
// Abstract commands (read/write RISC-V registers and RISC-V CSRs)
DM_Addr dm_addr_abstractcs = 'h16;
DM_Addr dm_addr_command = 'h17;
DM_Addr dm_addr_data0 = 'h04;
DM_Addr dm_addr_data1 = 'h05;
DM_Addr dm_addr_data2 = 'h06;
DM_Addr dm_addr_data3 = 'h07;
DM_Addr dm_addr_data4 = 'h08;
DM_Addr dm_addr_data5 = 'h09;
DM_Addr dm_addr_data6 = 'h0a;
DM_Addr dm_addr_data7 = 'h0b;
DM_Addr dm_addr_data8 = 'h0c;
DM_Addr dm_addr_data9 = 'h0d;
DM_Addr dm_addr_data10 = 'h0d;
DM_Addr dm_addr_data11 = 'h0f;
DM_Addr dm_addr_abstractauto = 'h18;
DM_Addr dm_addr_progbuf0 = 'h20;
// ----------------
// System Bus access (read/write RISC-V memory/devices)
DM_Addr dm_addr_sbcs = 'h38;
DM_Addr dm_addr_sbaddress0 = 'h39;
DM_Addr dm_addr_sbaddress1 = 'h3a;
DM_Addr dm_addr_sbaddress2 = 'h3b;
DM_Addr dm_addr_sbdata0 = 'h3c;
DM_Addr dm_addr_sbdata1 = 'h3d;
DM_Addr dm_addr_sbdata2 = 'h3e;
DM_Addr dm_addr_sbdata3 = 'h3f;
// ================================================================
function Fmt fshow_dm_addr (DM_Addr dm_addr);
return case (dm_addr)
// Run Control
dm_addr_dmcontrol: $format ("dm_addr_dmcontrol");
dm_addr_dmstatus: $format ("dm_addr_dmstatus");
dm_addr_hartinfo: $format ("dm_addr_hartinfo");
dm_addr_haltsum: $format ("dm_addr_haltsum");
dm_addr_hawindowsel: $format ("dm_addr_hawindowsel");
dm_addr_hawindow: $format ("dm_addr_hawindow");
dm_addr_devtreeaddr0: $format ("dm_addr_devtreeaddr0");
dm_addr_authdata: $format ("dm_addr_authdata");
dm_addr_haltregion0: $format ("dm_addr_haltregion0");
dm_addr_haltregion31: $format ("dm_addr_haltregion31");
dm_addr_verbosity: $format ("dm_addr_verbosity");
// Abstract Commands
dm_addr_abstractcs: $format ("dm_addr_abstractcs");
dm_addr_command: $format ("dm_addr_command");
dm_addr_data0: $format ("dm_addr_data0");
dm_addr_data1: $format ("dm_addr_data1");
dm_addr_data2: $format ("dm_addr_data2");
dm_addr_data3: $format ("dm_addr_data3");
dm_addr_data4: $format ("dm_addr_data4");
dm_addr_data5: $format ("dm_addr_data5");
dm_addr_data6: $format ("dm_addr_data6");
dm_addr_data7: $format ("dm_addr_data7");
dm_addr_data8: $format ("dm_addr_data8");
dm_addr_data9: $format ("dm_addr_data9");
dm_addr_data10: $format ("dm_addr_data10");
dm_addr_data11: $format ("dm_addr_data11");
dm_addr_abstractauto: $format ("dm_addr_abstractauto");
dm_addr_progbuf0: $format ("dm_addr_progbuf0");
// System Bus
dm_addr_sbcs: $format ("dm_addr_sbcs");
dm_addr_sbaddress0: $format ("dm_addr_sbaddress0");
dm_addr_sbaddress1: $format ("dm_addr_sbaddress1");
dm_addr_sbaddress2: $format ("dm_addr_sbaddress2");
dm_addr_sbdata0: $format ("dm_addr_sbdata0");
dm_addr_sbdata1: $format ("dm_addr_sbdata1");
dm_addr_sbdata2: $format ("dm_addr_sbdata2");
dm_addr_sbdata3: $format ("dm_addr_sbdata3");
default: $format ("<Unknown dm_abstract_command dm_addr 0x%0h>", dm_addr);
endcase;
endfunction
// ================================================================
// Run Control DM register fields
// ----------------------------------------------------------------
// 'dmcontrol' register
function DM_Word fn_mk_dmcontrol (Bool haltreq,
Bool resumereq,
Bool hartreset,
Bool hasel,
Bit #(10) hartsel,
Bool ndmreset,
Bool dmactive);
return {pack (haltreq),
pack (resumereq),
pack (hartreset),
2'b0,
pack (hasel),
hartsel,
14'b0,
pack (ndmreset),
pack (dmactive)};
endfunction
function Bool fn_dmcontrol_haltreq (DM_Word dm_word);
return unpack (dm_word [31]);
endfunction
function Bool fn_dmcontrol_resumereq (DM_Word dm_word);
return unpack (dm_word [30]);
endfunction
function Bool fn_dmcontrol_hartreset (DM_Word dm_word);
return unpack (dm_word [29]);
endfunction
function Bool fn_dmcontrol_hasel (DM_Word dm_word);
return unpack (dm_word [26]);
endfunction
function Bit #(10) fn_dmcontrol_hartsel (DM_Word dm_word);
return dm_word [25:16];
endfunction
function Bool fn_dmcontrol_ndmreset (DM_Word dm_word);
return unpack (dm_word [1]);
endfunction
function Bool fn_dmcontrol_dmactive (DM_Word dm_word);
return unpack (dm_word [0]);
endfunction
// ----------------------------------------------------------------
// 'dmstatus' register
function Bool fn_dmstatus_allresumeack (DM_Word x);
return unpack (x [17]);
endfunction
function Bool fn_dmstatus_anyresumeack (DM_Word x);
return unpack (x [16]);
endfunction
function Bool fn_dmstatus_allnonexistent (DM_Word x);
return unpack (x [15]);
endfunction
function Bool fn_dmstatus_anynonexistent (DM_Word x);
return unpack (x [14]);
endfunction
function Bool fn_dmstatus_allunavail (DM_Word x);
return unpack (x [13]);
endfunction
function Bool fn_dmstatus_anyunavail (DM_Word x);
return unpack (x [12]);
endfunction
function Bool fn_dmstatus_allrunning (DM_Word x);
return unpack (x [11]);
endfunction
function Bool fn_dmstatus_anyrunning (DM_Word x);
return unpack (x [10]);
endfunction
function Bool fn_dmstatus_allhalted (DM_Word x);
return unpack (x [9]);
endfunction
function Bool fn_dmstatus_anyhalted (DM_Word x);
return unpack (x [8]);
endfunction
function Bool fn_dmstatus_authenticated (DM_Word x);
return unpack (x [7]);
endfunction
function Bool fn_dmstatus_authbusy (DM_Word x);
return unpack (x [6]);
endfunction
function Bool fn_dmstatus_devtreevalid (DM_Word x);
return unpack (x [4]);
endfunction
function Bit #(4) fn_dmstatus_version (DM_Word x);
return unpack (x [3:0]);
endfunction
function Fmt fshow_dmstatus (DM_Word x);
Fmt fmt_version = ( (x[3:0] == 0)
? $format ("v.none")
: ( (x[3:0] == 1)
? $format ("v0.11")
: ( (x[3:0] == 2)
? $format ("v0.13")
: $format ("v??"))));
return ( $format ("(all/any) ")
+ $format ("resumeack %0d/%0d ", x[17], x[16])
+ $format ("nonexistent %0d/%0d ", x[15], x[14])
+ $format ("unavail %0d/%0d ", x[13], x[12])
+ $format ("running %0d/%0d ", x[11], x[10])
+ $format ("halted %0d/%0d ", x[9], x[8])
+ $format ("authenticated %0d ", x[7])
+ $format ("authbusy %0d ", x[6])
+ $format ("devtreevalid %0d ", x[4])
+ fmt_version);
endfunction
// ================================================================
// Abstract Command register fields
// ----------------------------------------------------------------
// 'dm_abstractcs' register
typedef enum {DM_ABSTRACTCS_CMDERR_NONE, // 0
DM_ABSTRACTCS_CMDERR_BUSY, // 1
DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED, // 2
DM_ABSTRACTCS_CMDERR_EXCEPTION, // 3
DM_ABSTRACTCS_CMDERR_HALT_RESUME, // 4
DM_ABSTRACTCS_CMDERR_UNDEF5, // 5
DM_ABSTRACTCS_CMDERR_UNDEF6, // 6
DM_ABSTRACTCS_CMDERR_OTHER // 7
} DM_abstractcs_cmderr
deriving (Bits, Eq, FShow);
// The following is used in writes, to clear cmderr
DM_abstractcs_cmderr dm_cmderr_w1c = DM_ABSTRACTCS_CMDERR_OTHER;
function DM_Word fn_mk_abstractcs (DM_abstractcs_cmderr cmderr);
return { 0, pack (cmderr), 8'h0 };
endfunction
function Bit #(5) fn_abstractcs_progsize (DM_Word dm_word);
return unpack (dm_word [28:24]);
endfunction
function Bool fn_abstractcs_busy (DM_Word dm_word);
return unpack (dm_word [12]);
endfunction
function DM_abstractcs_cmderr fn_abstractcs_cmderr (DM_Word dm_word);
return unpack (dm_word [10:8]);
endfunction
function Bit #(5) fn_abstractcs_datacount (DM_Word dm_word);
return unpack (dm_word [4:0]);
endfunction
// ----------------------------------------------------------------
// 'command' register
typedef enum {DM_COMMAND_CMDTYPE_ACCESS_REG,
DM_COMMAND_CMDTYPE_QUICK_ACCESS
} DM_command_cmdtype
deriving (Bits, Eq, FShow);
typedef enum {DM_COMMAND_ACCESS_REG_SIZE_UNDEF0, // 0
DM_COMMAND_ACCESS_REG_SIZE_UNDEF1, // 1
DM_COMMAND_ACCESS_REG_SIZE_LOWER32, // 2
DM_COMMAND_ACCESS_REG_SIZE_LOWER64, // 3
DM_COMMAND_ACCESS_REG_SIZE_LOWER128, // 4
DM_COMMAND_ACCESS_REG_SIZE_UNDEF5, // 5
DM_COMMAND_ACCESS_REG_SIZE_UNDEF6, // 6
DM_COMMAND_ACCESS_REG_SIZE_UNDEF7 // 7
} DM_command_access_reg_size
deriving (Bits, Eq, FShow);
Integer dm_command_access_reg_regno_csr_0 = 'h0000;
Integer dm_command_access_reg_regno_csr_FFF = 'h0FFF;
Integer dm_command_access_reg_regno_gpr_0 = 'h1000;
Integer dm_command_access_reg_regno_gpr_1F = 'h101F;
Integer dm_command_access_reg_regno_fpr_0 = 'h1020;
Integer dm_command_access_reg_regno_fpr_1F = 'h103F;
function DM_Word fn_mk_command_access_reg (DM_command_access_reg_size size,
Bool postexec,
Bool transfer,
Bool write,
Bit #(16) regno);
Bit #(8) b8_cmdtype = zeroExtend (pack (DM_COMMAND_CMDTYPE_ACCESS_REG));
Bit #(3) b3_size = pack (size);
return {b8_cmdtype,
1'b0,
b3_size,
1'b0,
pack (postexec),
pack (transfer),
pack (write),
regno};
endfunction
function DM_command_cmdtype fn_command_cmdtype (DM_Word dm_word);
return unpack (truncate (dm_word [31:24]));
endfunction
function DM_command_access_reg_size fn_command_access_reg_size (DM_Word dm_word);
return unpack (dm_word [22:20]);
endfunction
function Bool fn_command_access_reg_postexec (DM_Word dm_word);
return unpack (dm_word [18]);
endfunction
function Bool fn_command_access_reg_transfer (DM_Word dm_word);
return unpack (dm_word [17]);
endfunction
function Bool fn_command_access_reg_write (DM_Word dm_word);
return unpack (dm_word [16]);
endfunction
function Bit #(16) fn_command_access_reg_regno (DM_Word dm_word);
return dm_word [15:0];
endfunction
// ================================================================
// System Bus Access DM register fields
// ----------------------------------------------------------------
// 'dm_sbcs' register
typedef enum {DM_SBACCESS_8_BIT,
DM_SBACCESS_16_BIT,
DM_SBACCESS_32_BIT,
DM_SBACCESS_64_BIT,
DM_SBACCESS_128_BIT
} DM_sbaccess
deriving (Bits, Eq, FShow);
function Integer fn_sbaccess_to_addr_incr (DM_sbaccess sbaccess);
case (sbaccess)
DM_SBACCESS_8_BIT: return 1;
DM_SBACCESS_16_BIT: return 2;
DM_SBACCESS_32_BIT: return 4;
DM_SBACCESS_64_BIT: return 8;
DM_SBACCESS_128_BIT: return 16;
endcase
endfunction
typedef enum {DM_SBERROR_NONE, // 0
DM_SBERROR_TIMEOUT, // 1
DM_SBERROR_BADADDR, // 2
DM_SBERROR_OTHER, // 3
DM_SBERROR_BUSY_STALE, // 4
DM_SBERROR_UNDEF5, // 5
DM_SBERROR_UNDEF6, // 6
DM_SBERROR_UNDEF7_W1C // 7, used in writes, to clear sberror
} DM_sberror
deriving (Bits, Eq, FShow);
// Constructor
function DM_Word fn_mk_sbcs_val (Bit #(3) sbversion,
Bool sbbusyerror,
Bool sbbusy,
Bool sbreadonaddr,
DM_sbaccess sbaccess,
Bool sbautoincrement,
Bool sbreadondata,
DM_sberror sberror,
Bit #(7) sbasize,
Bit #(1) sbaccess128,
Bit #(1) sbaccess64,
Bit #(1) sbaccess32,
Bit #(1) sbaccess16,
Bit #(1) sbaccess8);
return {sbversion,
6'b0,
pack (sbbusyerror),
pack (sbbusy),
pack (sbreadonaddr),
pack (sbaccess),
pack (sbautoincrement),
pack (sbreadondata),
pack (sberror),
sbasize,
sbaccess128,
sbaccess64,
sbaccess32,
sbaccess16,
sbaccess8};
endfunction
// Selectors
function Bit #(3) fn_sbcs_sbversion (DM_Word dm_word); return unpack (dm_word [31:29]); endfunction
function Bool fn_sbcs_sbbusyerror (DM_Word dm_word); return unpack (dm_word [22]); endfunction
function Bool fn_sbcs_sbbusy (DM_Word dm_word); return unpack (dm_word [21]); endfunction
function Bool fn_sbcs_sbreadonaddr (DM_Word dm_word); return unpack (dm_word [20]); endfunction
function DM_sbaccess fn_sbcs_sbaccess (DM_Word dm_word); return unpack (dm_word [19:17]); endfunction
function Bool fn_sbcs_sbautoincrement (DM_Word dm_word); return unpack (dm_word [16]); endfunction
function Bool fn_sbcs_sbreadondata (DM_Word dm_word); return unpack (dm_word [15]); endfunction
function DM_sberror fn_sbcs_sberror (DM_Word dm_word); return unpack (dm_word [14:12]); endfunction
function Bit #(7) fn_sbcs_sbasize (DM_Word dm_word); return dm_word [11:5]; endfunction
function Bool fn_sbcs_sbaccess128 (DM_Word dm_word); return unpack (dm_word [4]); endfunction
function Bool fn_sbcs_sbaccess64 (DM_Word dm_word); return unpack (dm_word [3]); endfunction
function Bool fn_sbcs_sbaccess32 (DM_Word dm_word); return unpack (dm_word [2]); endfunction
function Bool fn_sbcs_sbaccess16 (DM_Word dm_word); return unpack (dm_word [1]); endfunction
function Bool fn_sbcs_sbaccess8 (DM_Word dm_word); return unpack (dm_word [0]); endfunction
// Debugging
function Fmt fshow_sbcs (DM_Word dm_word);
return ( $format ("SBCS{")
+ $format ("sbversion %0d", fn_sbcs_sbversion (dm_word))
+ $format (" sbbusyerror %0d", fn_sbcs_sbbusyerror (dm_word))
+ $format (" sbbusy %0d", fn_sbcs_sbbusy (dm_word))
+ $format (" sbreadonaddr ") + fshow (fn_sbcs_sbreadonaddr (dm_word))
+ $format (" sbaccess ") + fshow (fn_sbcs_sbaccess (dm_word))
+ $format (" sbautoincrement ") + fshow (fn_sbcs_sbautoincrement (dm_word))
+ $format (" sbreadondata ") + fshow (fn_sbcs_sbreadondata (dm_word))
+ $format (" sberror ") + fshow (fn_sbcs_sberror (dm_word))
+ $format (" sbasize %0d", fn_sbcs_sbasize (dm_word))
+ $format (" sbaccess")
+ ((fn_sbcs_sbaccess128 (dm_word)) ? $format ("_128") : $format ("x"))
+ ((fn_sbcs_sbaccess64 (dm_word)) ? $format ("_64") : $format ("x"))
+ ((fn_sbcs_sbaccess32 (dm_word)) ? $format ("_32") : $format ("x"))
+ ((fn_sbcs_sbaccess16 (dm_word)) ? $format ("_16") : $format ("x"))
+ ((fn_sbcs_sbaccess8 (dm_word)) ? $format ("_8") : $format ("x"))
+ $format ("}"));
endfunction
// ================================================================
// DCSR 'cause' field values
typedef enum {DCSR_CAUSE_RESERVED0,
DCSR_CAUSE_EBREAK,
DCSR_CAUSE_TRIGGER,
DCSR_CAUSE_HALTREQ,
DCSR_CAUSE_STEP,
DCSR_CAUSE_RESERVED5,
DCSR_CAUSE_RESERVED6,
DCSR_CAUSE_RESERVED7
} DCSR_Cause
deriving (Bits, Eq, FShow);
// ================================================================
// Sub-interface of the Debug Module facing the remote debugger (e.g. GDB)
interface DMI;
method Action read_addr (DM_Addr dm_addr);
method ActionValue #(DM_Word) read_data;
method Action write (DM_Addr dm_addr, DM_Word dm_word);
endinterface
// A dummy interface to tie off DMI if it is not used.
DMI dummy_DMI_ifc = interface DMI;
method Action read_addr (DM_Addr dm_addr) = noAction;
method ActionValue #(DM_Word) read_data = actionvalue
return 0;
endactionvalue;
method Action write (DM_Addr dm_addr, DM_Word dm_word) = noAction;
endinterface;
// ================================================================
endpackage

View File

@@ -0,0 +1,343 @@
// Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved.
package DM_Run_Control;
// ================================================================
// This package implements the 'Run Control' part of the RISC-V Debug
// Module, i.e., reset platform, reset hart, run/halt hart
// ================================================================
// BSV library imports
import FIFOF :: *;
import GetPut :: *;
import ClientServer :: *;
// ================================================================
// Project imports
import ISA_Decls :: *;
import DM_Common :: *;
// ================================================================
// Interface
interface DM_Run_Control_IFC;
method Bool dmactive;
method Action reset;
// ----------------
// DMI facing GDB/host
method ActionValue #(DM_Word) av_read (DM_Addr dm_addr);
method Action write (DM_Addr dm_addr, DM_Word dm_word);
// ----------------
// Facing a hart: reset and run-control
interface Get #(Token) hart0_get_reset_req;
interface Client #(Bool, Bool) hart0_client_run_halt;
interface Get #(Bit #(4)) hart0_get_other_req;
// ----------------
// Facing Platform: Non-Debug-Module Reset (reset all except DM)
interface Get #(Token) get_ndm_reset_req;
endinterface
// ================================================================
(* synthesize *)
module mkDM_Run_Control (DM_Run_Control_IFC);
Integer verbosity = 0; // Normally 0; non-zero for debugging
// ----------------------------------------------------------------
// NDM Reset
FIFOF #(Token) f_ndm_reset_reqs <- mkFIFOF;
// ----------------------------------------------------------------
// Hart0 run control
Reg #(Bool) rg_hart0_running <- mkRegU;
// Reset requests to hart
FIFOF #(Token) f_hart0_reset_reqs <- mkFIFOF;
// Run/halt requests to hart and responses
FIFOF #(Bool) f_hart0_run_halt_reqs <- mkFIFOF;
FIFOF #(Bool) f_hart0_run_halt_rsps <- mkFIFOF;
// Non-standard requests to hart and responses
// Currently only verbosity
FIFOF #(Bit #(4)) f_hart0_other_reqs <- mkFIFOF;
// ----------------------------------------------------------------
Bit #(32) haltregion0 = { 31'h0, pack (! rg_hart0_running) };
Bit #(32) haltsum = { 31'h0, pack (! rg_hart0_running) };
// ----------------------------------------------------------------
// rg_dmstatus
// Since we currently support only 1 hart,
// 'anyXX' = 'allXX'
// 'allrunning' = NOT 'allhalted'
Reg#(Bool) rg_dmstatus_allresumeack <- mkRegU;
Bool dmstatus_allresumeack = rg_dmstatus_allresumeack;
Bool dmstatus_anyresumeack = rg_dmstatus_allresumeack;
Bool dmstatus_allnonexistent = False;
Bool dmstatus_anynonexistent = dmstatus_allnonexistent;
Bool dmstatus_allunavail = False;
Bool dmstatus_anyunavail = dmstatus_allunavail;
Bool dmstatus_allrunning = rg_hart0_running;
Bool dmstatus_anyrunning = dmstatus_allrunning;
Bool dmstatus_allhalted = (! rg_hart0_running);
Bool dmstatus_anyhalted = dmstatus_allhalted;
DM_Word virt_rg_dmstatus = {14'b0,
pack (dmstatus_allresumeack),
pack (dmstatus_anyresumeack),
pack (dmstatus_allnonexistent),
pack (dmstatus_anynonexistent),
pack (dmstatus_allunavail),
pack (dmstatus_anyunavail),
pack (dmstatus_allrunning),
pack (dmstatus_anyrunning),
pack (dmstatus_allhalted),
pack (dmstatus_anyhalted),
pack (True), // authenticated
pack (False), // authbusy
1'b0,
pack (False), // devtreevalid
4'h2}; // version
// ----------------------------------------------------------------
// rg_dmcontrol
Reg #(Bool) rg_dmcontrol_haltreq <- mkRegU;
// resumereq is a W1 field, no need for a register
Reg #(Bool) rg_dmcontrol_hartreset <- mkRegU;
Reg #(Bool) rg_dmcontrol_ndmreset <- mkRegU;
Reg #(Bool) rg_dmcontrol_dmactive <- mkReg (False);
DM_Word virt_rg_dmcontrol = {2'b0, // haltreq, resumereq (w-o)
pack (rg_dmcontrol_hartreset),
2'b0,
pack (False), // hasel
10'b0, // hartsel
14'b0,
pack (rg_dmcontrol_ndmreset),
pack (rg_dmcontrol_dmactive)};
function Action fa_rg_dmcontrol_write (DM_Word dm_word);
action
let haltreq = fn_dmcontrol_haltreq (dm_word);
let resumereq = fn_dmcontrol_resumereq (dm_word);
let hartreset = fn_dmcontrol_hartreset (dm_word);
let hasel = fn_dmcontrol_hasel (dm_word);
let hartsel = fn_dmcontrol_hartsel (dm_word);
let ndmreset = fn_dmcontrol_ndmreset (dm_word);
let dmactive = fn_dmcontrol_dmactive (dm_word);
rg_dmcontrol_haltreq <= haltreq;
rg_dmcontrol_hartreset <= hartreset;
rg_dmcontrol_ndmreset <= ndmreset;
rg_dmcontrol_dmactive <= dmactive;
// Debug Module reset
if (! dmactive) begin
// Reset the DM module itself
$display ("DM_Run_Control.write: dmcontrol 0x%08h (dmactive=0): resetting Debug Module",
dm_word);
// Error-checking
if (ndmreset) begin
$display ("DM_Run_Control.write: WARNING: in word written to dmcontrol (0x%08h):",
dm_word);
$display (" [1] (ndmreset) and [0] (dmactive) both asserted");
$display (" dmactive has priority; ignoring ndmreset");
end
if (hartreset) begin
$display ("DM_Run_Control.write: WARNING: in word written to dmcontrol (0x%08h):",
dm_word);
$display (" [29] (hartreset) and [0] (dmactive) both asserted");
$display (" dmactive has priority; ignoring hartreset");
end
// No action here; other rules will fire (see method dmactive, Debug_Module.rl_reset)
noAction;
end
// Platform reset (non-Debug Module)
else if (ndmreset) begin
$display ("DM_Run_Control.write: dmcontrol 0x%08h: ndmreset=1: resetting platform",
dm_word);
f_ndm_reset_reqs.enq (?);
// Error-checking
if (hartreset) begin
$display ("DM_Run_Control.write: WARNING: in word written to dmcontrol (0x%08h):",
dm_word);
$display (" Both ndmreset (bit 1) and hartreset (bit 29) are asserted");
$display (" ndmreset has priority; ignoring hartreset");
end
end
else begin
// Deassert platform reset
if ((verbosity != 0) && rg_dmcontrol_ndmreset)
$display ("DM_Run_Control.write: dmcontrol 0x%08h: clearing ndmreset", dm_word);
// Hart reset
if (hartreset) begin
if (verbosity != 0)
$display ("DM_Run_Control.write: dmcontrol 0x%08h: hartreset=1: resetting hart",
dm_word);
f_hart0_reset_reqs.enq (?);
end
else begin
// Deassert hart reset
if ((verbosity != 0) && rg_dmcontrol_hartreset)
$display ("DM_Run_Control.write: dmcontrol 0x%08h: clearing hartreset", dm_word);
if (hasel)
$display ("DM_Run_Control.write: ERROR: dmcontrol 0x%08h: 'hasel' is not supported",
dm_word);
if (hartsel != 0)
$display ("DM_Run_Control.write: ERROR: dmcontrol 0x%08h: hartsel 0x%0h not supported",
dm_word, hartsel);
if (haltreq && resumereq) begin
$display ("DM_Run_Control.write: ERROR: dmcontrol 0x%08h: haltreq=1 and resumereq=1",
dm_word);
$display (" This behavior is 'undefined' in the spec; ignoring");
end
// Resume hart(s) if not running
else if (resumereq && (! rg_hart0_running)) begin
f_hart0_run_halt_reqs.enq (True);
rg_dmstatus_allresumeack <= False;
$display ("DM_Run_Control.write: hart0 resume request");
end
// Halt hart(s)
else if (haltreq && !rg_dmcontrol_haltreq) begin
f_hart0_run_halt_reqs.enq (False);
$display ("DM_Run_Control.write: hart0 halt request");
end
end
end
endaction
endfunction
// ----------------------------------------------------------------
// Unimplemented
// dm_addr_hartinfo
// dm_addr_hawindowsel
// dm_addr_hawindow
// dm_addr_devtreeaddr0..3
// dm_addr_authdata
// dm_addr_haltregion1..31
// ----------------------------------------------------------------
// rg_verbosity: non-standard
Reg #(Bit #(4)) rg_verbosity <- mkRegU;
// ----------------------------------------------------------------
rule rl_hart0_run_rsp;
let x = f_hart0_run_halt_rsps.first;
f_hart0_run_halt_rsps.deq;
rg_hart0_running <= x;
if (x) begin
rg_dmstatus_allresumeack <= True;
$display ("DM_Run_Control: hart0 running");
end
else begin
$display ("DM_Run_Control: hart0 halted");
end
endrule
// ----------------------------------------------------------------
// INTERFACE
method Bool dmactive;
return rg_dmcontrol_dmactive;
endmethod
method Action reset;
f_ndm_reset_reqs.clear;
rg_hart0_running <= False; // Must be same as initial state of CPU
f_hart0_reset_reqs.clear;
f_hart0_run_halt_reqs.clear;
f_hart0_run_halt_rsps.clear;
rg_dmcontrol_haltreq <= False;
rg_dmcontrol_hartreset <= False;
rg_dmcontrol_ndmreset <= False;
rg_dmcontrol_dmactive <= True; // DM module is now active
rg_dmstatus_allresumeack <= False;
rg_verbosity <= 0;
if (verbosity != 0)
$display ("DM_Run_Control: reset");
endmethod
// ----------------
// DMI facing GDB/host
method ActionValue #(DM_Word) av_read (DM_Addr dm_addr);
actionvalue
DM_Word dm_word = case (dm_addr)
dm_addr_dmcontrol: virt_rg_dmcontrol;
dm_addr_dmstatus: virt_rg_dmstatus;
dm_addr_haltsum: haltsum;
dm_addr_haltregion0: haltregion0;
dm_addr_verbosity: extend (rg_verbosity);
endcase;
if (verbosity != 0)
$display ("DM_Run_Control.av_read: [", fshow_dm_addr (dm_addr), "] => 0x%08h", dm_word);
return dm_word;
endactionvalue
endmethod
method Action write (DM_Addr dm_addr, DM_Word dm_word);
action
if (verbosity != 0)
$display ("DM_Run_Control.write: [", fshow_dm_addr (dm_addr), "] <= 0x%08h", dm_word);
case (dm_addr)
dm_addr_dmcontrol: fa_rg_dmcontrol_write (dm_word);
dm_addr_verbosity: begin
rg_verbosity <= truncate (dm_word);
f_hart0_other_reqs.enq (truncate (dm_word));
end
default: noAction;
endcase
endaction
endmethod
// ----------------
// Facing Hart: Reset, Run-control, etc.
interface Get hart0_get_reset_req = toGet (f_hart0_reset_reqs);
interface Client hart0_client_run_halt = toGPClient (f_hart0_run_halt_reqs, f_hart0_run_halt_rsps);
interface Get hart0_get_other_req = toGet (f_hart0_other_reqs);
// ----------------
// Facing Platform: Non-Debug-Module Reset (reset all except DM)
interface Get get_ndm_reset_req = toGet (f_ndm_reset_reqs);
endmodule
// ================================================================
endpackage

View File

@@ -0,0 +1,663 @@
// Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved.
package DM_System_Bus;
// ================================================================
// This package implements the 'System Bus Access' part of the RISC-V
// Debug Module, i.e., read/write access to RISC-V system memory.
// ================================================================
// BSV library imports
import FIFOF :: *;
// ----------------
// Other library imports
import Semi_FIFOF :: *;
// ================================================================
// Project Imports
import ISA_Decls :: *;
import DM_Common :: *;
import AXI4_Types :: *;
import Fabric_Defs :: *;
// ================================================================
// Interface
interface DM_System_Bus_IFC;
method Action reset;
// ----------------
// DMI facing GDB/host
method ActionValue #(DM_Word) av_read (DM_Addr dm_addr);
method Action write (DM_Addr dm_addr, DM_Word dm_word);
// ----------------
// Facing System
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master;
endinterface
// ================================================================
// Local definitions
// ----------------
// Convert DM code for access size to AXI4 code for access size
function AXI4_Size fn_DM_sbaccess_to_AXI4_Size (DM_sbaccess sbaccess);
AXI4_Size axi4_size = case (sbaccess)
DM_SBACCESS_8_BIT: axsize_1;
DM_SBACCESS_16_BIT: axsize_2;
DM_SBACCESS_32_BIT: axsize_4;
DM_SBACCESS_64_BIT: axsize_8;
endcase;
return axi4_size;
endfunction
// ----------------
// Extract bytes from raw word read from fabric.
// The bytes of interest are offset according to LSBs of addr.
// Arguments:
// - a DM_sbaccess (indicating size of access)
// - a byte-address
// - a load-word from fabric
// result:
// - word with correct byte(s) shifted into LSBs and zero extended
function Bit #(64) fn_extract_and_extend_bytes (DM_sbaccess sbaccess,
Bit #(64) read_addr,
Bit #(64) word64);
Bit #(3) addr_lsbs = read_addr [2:0];
if (valueOf (Wd_Data) == 32)
addr_lsbs = (addr_lsbs & 'h3);
Bit #(64) result = 0;
case (sbaccess)
DM_SBACCESS_8_BIT: case (addr_lsbs)
'h0: result = zeroExtend (word64 [ 7: 0]);
'h1: result = zeroExtend (word64 [15: 8]);
'h2: result = zeroExtend (word64 [23:16]);
'h3: result = zeroExtend (word64 [31:24]);
'h4: result = zeroExtend (word64 [39:32]);
'h5: result = zeroExtend (word64 [47:40]);
'h6: result = zeroExtend (word64 [55:48]);
'h7: result = zeroExtend (word64 [63:56]);
endcase
DM_SBACCESS_16_BIT: case (addr_lsbs)
'h0: result = zeroExtend (word64 [15: 0]);
'h2: result = zeroExtend (word64 [31:16]);
'h4: result = zeroExtend (word64 [47:32]);
'h6: result = zeroExtend (word64 [63:48]);
endcase
DM_SBACCESS_32_BIT: case (addr_lsbs)
'h0: result = zeroExtend (word64 [31: 0]);
'h4: result = zeroExtend (word64 [63:32]);
endcase
DM_SBACCESS_64_BIT: case (addr_lsbs)
'h0: result = word64;
endcase
endcase
return result;
endfunction
// ----------------
// Compute address, data and strobe (byte-enables) for writes to fabric
function Tuple4 #(Fabric_Addr, // addr is 32b- or 64b-aligned
Fabric_Data, // data is lane-aligned
Fabric_Strb, // strobe
AXI4_Size) // 8 for 8-byte writes, else 4
fn_to_fabric_write_fields (DM_sbaccess sbaccess, // size of access
Bit #(64) addr,
Bit #(64) word64); // data is in lsbs
// First compute addr, data and strobe for a 64b-wide fabric
Bit #(8) strobe64 = 0;
Bit #(3) shift_bytes = addr [2:0];
Bit #(6) shift_bits = { shift_bytes, 3'b0 };
AXI4_Size axsize = axsize_128; // Will be updated in 'case' below
case (sbaccess)
DM_SBACCESS_8_BIT: begin
word64 = (word64 << shift_bits);
strobe64 = ('b_1 << shift_bytes);
axsize = axsize_1;
end
DM_SBACCESS_16_BIT: begin
word64 = (word64 << shift_bits);
strobe64 = ('b_11 << shift_bytes);
axsize = axsize_2;
end
DM_SBACCESS_32_BIT: begin
word64 = (word64 << shift_bits);
strobe64 = ('b_1111 << shift_bytes);
axsize = axsize_4;
end
DM_SBACCESS_64_BIT: begin
strobe64 = 'b_1111_1111;
axsize = axsize_8;
end
endcase
// Adjust for 32b fabrics
if ((valueOf (Wd_Data) == 32) && (addr [2] == 1'b1)) begin
word64 = { 32'h0, word64 [63:32] };
strobe64 = { 4'h0, strobe64 [7:4] };
end
// Finally, create fabric addr/data/strobe
Fabric_Addr fabric_addr = truncate (addr);
Fabric_Data fabric_data = truncate (word64);
Fabric_Strb fabric_strobe = truncate (strobe64);
return tuple4 (fabric_addr, fabric_data, fabric_strobe, axsize);
endfunction: fn_to_fabric_write_fields
// ----------------
// System Bus access states
typedef enum {SB_NOTBUSY,
SB_READ_FINISH,
SB_WRITE_FINISH
} SB_State
deriving (Bits, Eq, FShow);
// ================================================================
// Module implementation
(* synthesize *)
module mkDM_System_Bus (DM_System_Bus_IFC);
Integer verbosity = 0; // Normally 0; non-zero for debugging
// ----------------------------------------------------------------
// Interface to memory fabric
AXI4_Master_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master_xactor <- mkAXI4_Master_Xactor_2;
// ----------------------------------------------------------------
// System Bus state
Reg #(SB_State) rg_sb_state <- mkRegU;
Bool sbbusy = (rg_sb_state != SB_NOTBUSY);
// ----------------------------------------------------------------
// rg_sbaddress0,1 (2 not implemented)
// rg_sbdata0 (1, 2, 3 not implemented)
// Support for RV64. Instead of defining in terms of XLEN, defining using
// DM_Word which is always Bit#(32). 64-bit addressing supported for RV64 but
// only 32-bit data accesses are supported from debugger
Reg #(DM_Word) rg_sbaddress0 <- mkReg (0);
Reg #(DM_Word) rg_sbaddress1 <- mkReg (0); // Will always be zero for RV32
// Saved address during a read rg_sbaddress0/1 may be autoincremented,
// but we need original addr byte-lane extraction from response
Reg #(Bit #(64)) rg_sbaddress_reading <- mkRegU;
Bit #(64) sbaddress = { rg_sbaddress1, rg_sbaddress0 };
Reg #(DM_Word) rg_sbdata0 <- mkRegU;
// ----------------------------------------------------------------
// rg_sbcs
Reg #(Bool) rg_sbcs_sbbusyerror <- mkRegU;
Reg #(Bool) rg_sbcs_sbreadonaddr <- mkRegU;
Reg #(DM_sbaccess) rg_sbcs_sbaccess <- mkRegU;
Reg #(Bool) rg_sbcs_sbautoincrement <- mkRegU;
Reg #(Bool) rg_sbcs_sbreadondata <- mkRegU;
Reg #(DM_sberror) rg_sbcs_sberror <- mkRegU;
UInt #(3) sbversion = 1;
DM_Word virt_rg_sbcs = {pack (sbversion),
6'b0,
pack (rg_sbcs_sbbusyerror),
pack (sbbusy),
pack (rg_sbcs_sbreadonaddr),
pack (rg_sbcs_sbaccess),
pack (rg_sbcs_sbautoincrement),
pack (rg_sbcs_sbreadondata),
pack (rg_sbcs_sberror),
`ifdef RV64
7'd64, // sbasize -- address size
`endif
`ifdef RV32
7'd32, // sbasize -- address size
`endif
1'b0, // sbaccess128
1'b0, // sbaccess64
1'b1, // sbaccess32
1'b1, // sbaccess16
1'b1}; // sbaccess8
// ----------------
// Local defs and help functions
Integer addr_incr = fn_sbaccess_to_addr_incr (rg_sbcs_sbaccess);
function Action fa_sbaddress_incr (Bit #(64) addr64);
action
Bit #(64) next_sbaddress = addr64 + fromInteger (addr_incr);
`ifdef RV64
rg_sbaddress1 <= next_sbaddress [63:32];
`else
rg_sbaddress1 <= 0;
`endif
rg_sbaddress0 <= next_sbaddress [31:0];
if (verbosity != 0)
$display (" Increment sbaddr 0x%08h -> 0x%08h", addr64, next_sbaddress);
endaction
endfunction
// ----------------
// Construction and sending of fabric read-requests
function Action fa_fabric_send_read_req (Bit #(64) addr64);
action
Fabric_Addr fabric_addr = truncate (addr64);
let rda = AXI4_Rd_Addr {arid: fabric_default_id,
araddr: fabric_addr,
arlen: 0, // burst len = arlen+1
arsize: fn_DM_sbaccess_to_AXI4_Size (rg_sbcs_sbaccess),
arburst: fabric_default_burst,
arlock: fabric_default_lock,
arcache: fabric_default_arcache,
arprot: fabric_default_prot,
arqos: fabric_default_qos,
arregion: fabric_default_region,
aruser: fabric_default_user};
master_xactor.i_rd_addr.enq (rda);
// Save read-address for byte-lane extraction from later response
// (since rg_sbaddress may be incremented by then).
rg_sbaddress_reading <= addr64;
rg_sb_state <= SB_READ_FINISH;
if (verbosity != 0) begin
$display (" DM_System_Bus.fa_fabric_send_read_req, and => SB_READ_FINISH ");
$display (" ", fshow (rda));
end
endaction
endfunction
// ----------------
// Construction and sending of fabric write-requests
function Action fa_fabric_send_write_req (Bit #(64) data64);
action
match {.fabric_addr,
.fabric_data,
.fabric_strb,
.fabric_size} = fn_to_fabric_write_fields (rg_sbcs_sbaccess, sbaddress, data64);
// fabric_addr is always fabric-data-width aligned
// fabric_data is properly lane-adjusted
// fabric_strb identifies the lanes to be written
// awsize is always the fabric width
let wra = AXI4_Wr_Addr {awid: fabric_default_id,
awaddr: fabric_addr,
awlen: 0, // burst len = awlen+1
awsize: fabric_size,
awburst: fabric_default_burst,
awlock: fabric_default_lock,
awcache: fabric_default_awcache,
awprot: fabric_default_prot,
awqos: fabric_default_qos,
awregion: fabric_default_region,
awuser: fabric_default_user};
master_xactor.i_wr_addr.enq (wra);
let wrd = AXI4_Wr_Data {wid: fabric_default_id,
wdata: fabric_data,
wstrb: fabric_strb,
wlast: True,
wuser: fabric_default_user};
master_xactor.i_wr_data.enq (wrd);
if (verbosity != 0) begin
$display (" DM_System_Bus.fa_fabric_send_write_req:");
$display (" ", fshow (wra));
$display (" ", fshow (wrd));
end
endaction
endfunction
// ================================================================
// Writes to sbcs
function Action fa_rg_sbcs_write (DM_Word dm_word);
action
Bool sbbusyerror = unpack (dm_word [22]);
Bool sbreadonaddr = unpack (dm_word [20]);
DM_sbaccess sbaccess = unpack (dm_word [19:17]);
Bool sbautoincrement = unpack (dm_word [16]);
Bool sbreadondata = unpack (dm_word [15]);
DM_sberror sberror = unpack (dm_word [14:12]);
// No-op if not clearing existing sberror
if ((rg_sbcs_sberror != DM_SBERROR_NONE) && (sberror == DM_SBERROR_NONE)) begin
// Existing error is not being cleared
$display ("DM_System_Bus.sbcs_write <= 0x%08h: ERROR", dm_word);
$display (" ERROR: existing sberror (0x%0h) is not being cleared.", rg_sbcs_sberror);
$display (" Must be cleared to re-enable system bus access.");
end
// No-op if not clearing existing sbbusyerror
else if (rg_sbcs_sbbusyerror && (! sbbusyerror)) begin
$display ("DM_System_Bus.sbcs_write <= 0x%08h: ERROR", dm_word);
$display (" ERROR: existing sbbusyerror (%0d) is not being cleared.", rg_sbcs_sbbusyerror);
$display (" Must be cleared to re-enable system bus access.");
end
// Check that requested access size is supported
else if ( (sbaccess == DM_SBACCESS_128_BIT)
|| (sbaccess == DM_SBACCESS_64_BIT))
begin
rg_sbcs_sberror <= DM_SBERROR_OTHER;
$display ("DM_System_Bus.sbcs_write <= 0x%08h: ERROR", dm_word);
$display (" ERROR: sbaccess ", fshow (sbaccess), " not supported");
end
// Ok
else begin
if (verbosity != 0) begin
$display (" DM_System_Bus.sbcs_write: ", fshow_sbcs (dm_word));
if (rg_sbcs_sberror != DM_SBERROR_NONE)
$display (" Clearing sbcs.sberror");
if (rg_sbcs_sbbusyerror)
$display (" Clearing sbcs.sbbusyerror");
end
rg_sbcs_sbbusyerror <= False;
rg_sbcs_sbreadonaddr <= sbreadonaddr;
rg_sbcs_sbaccess <= sbaccess;
rg_sbcs_sbautoincrement <= sbautoincrement;
rg_sbcs_sbreadondata <= sbreadondata;
rg_sbcs_sberror <= DM_SBERROR_NONE;
end
endaction
endfunction: fa_rg_sbcs_write
// ================================================================
// rg_sbaddress0, rg_sbaddress1 writes
function Action fa_rg_sbaddress_write (DM_Addr dm_addr, DM_Word dm_word);
action
// Debug announce
if (verbosity != 0) begin
$write ("DM_System_Bus.sbaddress.write: [0x%08h] <= 0x%08h", dm_addr, dm_word);
if (rg_sbcs_sbreadonaddr) begin
$write ("; readonaddr");
if (rg_sbcs_sbautoincrement)
$write ("; autoincrement");
end
$display ("");
end
if (sbbusy) begin
$display ("DM_System_Bus.sbaddress.write: busy, setting sbbusyerror");
rg_sbcs_sbbusyerror <= True;
end
else if (rg_sbcs_sbbusyerror)
$display ("DM_System_Bus.sbaddress.write: ignoring due to sbbusyerror");
else if (rg_sbcs_sberror != DM_SBERROR_NONE)
$display ("DM_System_Bus.sbaddress.write: ignoring due to sberror = 0x%0h",
rg_sbcs_sberror);
else if (dm_addr == dm_addr_sbaddress0) begin
Bit #(64) addr64 = { rg_sbaddress1, dm_word };
if (rg_sbcs_sbreadonaddr) begin
fa_fabric_send_read_req (addr64);
if (rg_sbcs_sbautoincrement)
fa_sbaddress_incr (addr64);
else
rg_sbaddress0 <= dm_word;
end
else
rg_sbaddress0 <= dm_word;
end
else begin // (dm_addr == dm_addr_sbaddress1)
`ifdef RV32
rg_sbaddress1 <= 0;
if (verbosity != 0)
$display ("DM_System_Bus.write: [sbaddress1] <= 0 (RV32: ignoring arg value 0x%08h)",
dm_word);
`else
rg_sbaddress1 <= dm_word;
if (verbosity != 0)
$display ("DM_System_Bus.write: [sbaddress1] <= 0x%08h", dm_word);
`endif
end
endaction
endfunction
// ================================================================
// rg_sbdata0, rg_sbdata1 reads
function ActionValue #(DM_Word) fav_rg_sbdata_read (DM_Addr dm_addr);
actionvalue
DM_Word result = 0;
if (sbbusy) begin
$display ("DM_System_Bus.sbdata.read: busy, setting sbbusyerror");
rg_sbcs_sbbusyerror <= True;
end
else if (rg_sbcs_sbbusyerror)
$display ("DM_System_Bus.sbdata.read: ignoring due to sbbusyerror");
else if (rg_sbcs_sberror != DM_SBERROR_NONE)
$display ("DM_System_Bus.sbdata.read: ignoring due to sberror = 0x%0h", rg_sbcs_sberror);
else begin
if (dm_addr == dm_addr_sbdata0)
result = rg_sbdata0;
/* FUTURE: when supporting DM_SBACCESS_64_BIT
else if (dm_addr == dm_addr_sbdata1)
result = rg_sbdata1;
*/
// Increment sbaddress if needed
if (rg_sbcs_sbautoincrement)
fa_sbaddress_incr (sbaddress);
// Auto-read next data if needed
if (rg_sbcs_sbreadondata && (dm_addr == dm_addr_sbdata0))
fa_fabric_send_read_req (sbaddress);
end
return result;
endactionvalue
endfunction
// ----------------
// Finish read request (handle fabric response)
(* descending_urgency = "rl_sb_read_finish, reset" *)
(* descending_urgency = "rl_sb_read_finish, write" *)
rule rl_sb_read_finish ( (rg_sb_state == SB_READ_FINISH)
&& (rg_sbcs_sberror == DM_SBERROR_NONE));
let rdr <- pop_o (master_xactor.o_rd_data);
if (verbosity != 0)
$display ("DM_System_Bus.rule_sb_read_finish: rdr = ", fshow (rdr));
// Extract relevant bytes from fabric data
Bit #(64) rdata64 = zeroExtend (rdr.rdata);
Bit #(64) data = fn_extract_and_extend_bytes (rg_sbcs_sbaccess, rg_sbaddress_reading, rdata64);
if (rdr.rresp != axi4_resp_okay) begin
$display ("DM_System_Bus.rule_sb_read_finish: setting rg_sbcs_sberror to DM_SBERROR_OTHER\n");
$display (" rdr = ", fshow (rdr));
rg_sbcs_sberror <= DM_SBERROR_OTHER;
end
rg_sbdata0 <= data [31:0];
/* FUTURE: when supporting DM_SBACCESS_64_BIT
rg_sbdata1 <= data [63:32];
*/
if (verbosity != 0) begin
$display ("DM_System_Bus.rule_sb_read_finish: addr 0x%0h, sbaccess %0d (%0d bytes)",
rg_sbaddress_reading, rg_sbcs_sbaccess, addr_incr);
$display (" rg_sbdata0 <= 0x%0h", data);
$display (" module state => SB_NOTBUSY");
end
rg_sb_state <= SB_NOTBUSY;
endrule
// ================================================================
// rg_sbdata0, rg_sbdata1 writes
function Action fa_rg_sbdata_write (DM_Addr dm_addr, DM_Word dm_word);
action
if (sbbusy) begin
$display ("DM_System_Bus.sbdata.write: busy, setting sbbusyerror");
rg_sbcs_sbbusyerror <= True;
end
else if (rg_sbcs_sbbusyerror) begin
$display ("DM_System_Bus.sbdata.write: ignoring due to sbbusyerror");
end
else if (rg_sbcs_sberror != DM_SBERROR_NONE) begin
$display ("DM_System_Bus.sbdata.write: ignoring due to sberror = 0x%0h",
rg_sbcs_sberror);
end
else begin
if (verbosity != 0)
$display (" DM_System_Bus.fa_rg_sbdata_write: dm_addr 0x%08h dm_word 0x%08h",
dm_addr, dm_word);
if (dm_addr == dm_addr_sbdata0)
rg_sbdata0 <= dm_word;
/* FUTURE: when supporting DM_SBACCESS_64_BIT
else if (dm_addr == dm_addr_sbdata1)
rg_sbdata1 <= dm_word;
*/
// Initiate system bus write if writing to sbdata0
if (dm_addr == dm_addr_sbdata0) begin
fa_fabric_send_write_req (zeroExtend (dm_word));
// Increment sbaddr ifneeded
if (rg_sbcs_sbautoincrement)
fa_sbaddress_incr (sbaddress);
end
end
endaction
endfunction
// ----------------
// Consume write-responses
rule rl_sb_write_response;
let wrr <- pop_o (master_xactor.o_wr_resp);
if (wrr.bresp != axi4_resp_okay)
rg_sbcs_sberror <= DM_SBERROR_OTHER;
endrule
// ================================================================
// INTERFACE
method Action reset;
// master_xactor.reset; // TODO: introduces a scheduling cycle: fix this
rg_sb_state <= SB_NOTBUSY;
rg_sbcs_sbbusyerror <= False;
rg_sbcs_sbreadonaddr <= False;
rg_sbcs_sbaccess <= DM_SBACCESS_32_BIT;
rg_sbcs_sbautoincrement <= False;
rg_sbcs_sbreadondata <= False;
rg_sbcs_sberror <= DM_SBERROR_NONE;
rg_sbaddress0 <= 0;
rg_sbaddress1 <= 0;
rg_sbdata0 <= 0;
if (verbosity != 0)
$display ("DM_System_Bus: reset");
endmethod
// ----------------
// DMI facing GDB/host
// The predicate on read allows communication flow control to
// throttle requests. This achieves better performance, but is not
// workable for a true JTAG transport.
method ActionValue #(DM_Word) av_read (DM_Addr dm_addr) if (!sbbusy);
actionvalue
DM_Word dm_word = 0;
if (dm_addr == dm_addr_sbcs) begin
dm_word = virt_rg_sbcs;
if (verbosity != 0)
$display ("DM_System_Bus.read: [sbcs] => ", fshow_sbcs (dm_word));
end
else if (dm_addr == dm_addr_sbaddress0) begin
dm_word = rg_sbaddress0;
if (verbosity != 0)
$display ("DM_System_Bus.read: [sbaddress0] => 0x%08h", dm_word);
end
else if (dm_addr == dm_addr_sbaddress1) begin
dm_word = rg_sbaddress1;
if (verbosity != 0)
$display ("DM_System_Bus.read: [sbaddress1] => 0x%08h", dm_word);
end
else if (dm_addr == dm_addr_sbdata0) begin
dm_word <- fav_rg_sbdata_read (dm_addr_sbdata0);
end
else begin
// Unsupported dm address
dm_word = 0;
$display ("DM_System_Bus.read: [", fshow_dm_addr (dm_addr), "] not supported");
end
return dm_word;
endactionvalue
endmethod
method Action write (DM_Addr dm_addr, DM_Word dm_word);
action
if (dm_addr == dm_addr_sbcs)
fa_rg_sbcs_write (dm_word);
else if ((dm_addr == dm_addr_sbaddress0) || (dm_addr == dm_addr_sbaddress1))
fa_rg_sbaddress_write (dm_addr, dm_word);
else if (dm_addr == dm_addr_sbdata0) // FUTURE: || (dm_addr == dm_addr_sbdata1)
fa_rg_sbdata_write (dm_addr, dm_word);
else begin
// Unsupported dm_addr
let addr_name = fshow_dm_addr (dm_addr);
$display ("DM_System_Bus.write: [", addr_name, "] <= 0x%08h; addr not supported", dm_word);
end
endaction
endmethod
// ----------------
// Facing System
interface AXI4_Master_IFC master = master_xactor.axi_side;
endmodule
// ================================================================
endpackage

View File

@@ -0,0 +1,287 @@
// Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved.
package Debug_Module;
// ================================================================
// This is the top-level package of a collection that implements the
// "Debug Module" specified in:
//
// "RISC-V External Debug Support"
// Version 0.13
// 7c760b0151e43523ab3d2180e7852cd6f27d942c
// Mon Jul 3 17:04:59 2017 -0700
// Note: the spec actually represents three (almost) completely
// independent functionalities:
// Run Control: to start/stop harts, query their start/stop status, etc.
// Abstract Commands: to read/write RISC-V registers and RISC-V CSRs
// System Bus Access: to read/write RISC-V memory/devices
// The only exception to complete independence is that the Run Control
// part has a 'reset' for the Debug Module itself, which includes all
// three parts.
// Unfortunately the spec is not written to make this three-part
// organization clear--aspects of the three parts are completely
// intermingled. Even the address map mixes registers from the three
// parts.
// Here, this top-level package is merely a wrapper that dispatches
// DMI requests to the three packages that implement the three parts:
// DM_Run_Control
// DM_Abstract_Commands
// DM_System_Bus
// DM_Common contains common spec-level (implementation-independent) definitions.
// ================================================================
// Our Debug Module (DM) has a two sides:
// - GDB/Host-facing
// - CPU/Platform-facing
// The GDB/Host-facing side is called DMI (Debug Module Interface) in
// the spec, and is a simple memory-like read/write interface, into a
// debug module address space (unrelated to the RISC-V memory address
// space).
// The CPU/Platform-facing side has request/response interfaces for
// CPU run-control, CPU register/CSR access and RISC-V system memory
// access.
// ================================================================
// BSV library imports
import Memory :: *;
import FIFOF :: *;
import GetPut :: *;
import ClientServer :: *;
import SpecialFIFOs :: *;
// ----------------
// Other library imports
import Semi_FIFOF :: *;
import Cur_Cycle :: *;
// ================================================================
// Project imports
import ISA_Decls :: *;
import AXI4_Types :: *;
import Fabric_Defs :: *;
import DM_Common :: *;
import DM_Run_Control :: *;
import DM_Abstract_Commands :: *;
import DM_System_Bus :: *;
// ================================================================
export DM_Common :: *;
export Debug_Module_IFC (..);
export mkDebug_Module;
// ================================================================
// Interface to the Debug Module
interface Debug_Module_IFC;
// ----------------
// DMI (Debug Module Interface) facing remote debugger
interface DMI dmi;
// ----------------
// Facing CPU
// This section replicated for additional harts.
// Reset and run-control
interface Get #(Token) hart0_get_reset_req;
interface Client #(Bool, Bool) hart0_client_run_halt;
interface Get #(Bit #(4)) hart0_get_other_req;
// GPR access
interface MemoryClient #(5, XLEN) hart0_gpr_mem_client;
// CSR access
interface MemoryClient #(12, XLEN) hart0_csr_mem_client;
// ----------------
// Facing Platform
// Non-Debug-Module Reset (reset all except DM)
interface Get #(Token) get_ndm_reset_req;
// Read/Write RISC-V memory
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master;
endinterface
// ================================================================
(* synthesize *)
module mkDebug_Module (Debug_Module_IFC);
// The three parts
DM_Run_Control_IFC dm_run_control <- mkDM_Run_Control;
DM_Abstract_Commands_IFC dm_abstract_commands <- mkDM_Abstract_Commands;
DM_System_Bus_IFC dm_system_bus <- mkDM_System_Bus;
FIFOF#(DM_Addr) f_read_addr <- mkBypassFIFOF;
// ================================================================
// Reset all three parts when dm_run_control.dmactive is low
rule rl_reset (! dm_run_control.dmactive);
$display ("%0d: Debug_Module reset", cur_cycle);
dm_run_control.reset;
dm_abstract_commands.reset;
dm_system_bus.reset;
endrule
// ================================================================
// INTERFACE
// ----------------
// Facing GDB/DMI (Debug Module Interface)
interface DMI dmi;
method Action read_addr (DM_Addr dm_addr);
f_read_addr.enq(dm_addr);
endmethod
method ActionValue #(DM_Word) read_data;
let dm_addr = f_read_addr.first;
f_read_addr.deq;
DM_Word dm_word = ?;
if ( (dm_addr == dm_addr_dmcontrol)
|| (dm_addr == dm_addr_dmstatus)
|| (dm_addr == dm_addr_hartinfo)
|| (dm_addr == dm_addr_haltsum)
|| (dm_addr == dm_addr_hawindowsel)
|| (dm_addr == dm_addr_hawindow)
|| (dm_addr == dm_addr_devtreeaddr0)
|| (dm_addr == dm_addr_authdata)
|| (dm_addr == dm_addr_haltregion0)
|| (dm_addr == dm_addr_haltregion31)
|| (dm_addr == dm_addr_verbosity))
dm_word <- dm_run_control.av_read (dm_addr);
else if ( (dm_addr == dm_addr_abstractcs)
|| (dm_addr == dm_addr_command)
|| (dm_addr == dm_addr_data0)
|| (dm_addr == dm_addr_data1)
|| (dm_addr == dm_addr_data2)
|| (dm_addr == dm_addr_data3)
|| (dm_addr == dm_addr_data4)
|| (dm_addr == dm_addr_data5)
|| (dm_addr == dm_addr_data6)
|| (dm_addr == dm_addr_data7)
|| (dm_addr == dm_addr_data8)
|| (dm_addr == dm_addr_data9)
|| (dm_addr == dm_addr_data10)
|| (dm_addr == dm_addr_data11)
|| (dm_addr == dm_addr_abstractauto)
|| (dm_addr == dm_addr_progbuf0))
dm_word <- dm_abstract_commands.av_read (dm_addr);
else if ( (dm_addr == dm_addr_sbcs)
|| (dm_addr == dm_addr_sbaddress0)
|| (dm_addr == dm_addr_sbaddress1)
|| (dm_addr == dm_addr_sbaddress2)
|| (dm_addr == dm_addr_sbdata0)
|| (dm_addr == dm_addr_sbdata1)
|| (dm_addr == dm_addr_sbdata2)
|| (dm_addr == dm_addr_sbdata3))
dm_word <- dm_system_bus.av_read (dm_addr);
else begin
// TODO: set error status?
dm_word = 0;
end
return dm_word;
endmethod
method Action write (DM_Addr dm_addr, DM_Word dm_word);
if ( (dm_addr == dm_addr_dmcontrol)
|| (dm_addr == dm_addr_dmstatus)
|| (dm_addr == dm_addr_hartinfo)
|| (dm_addr == dm_addr_haltsum)
|| (dm_addr == dm_addr_hawindowsel)
|| (dm_addr == dm_addr_hawindow)
|| (dm_addr == dm_addr_devtreeaddr0)
|| (dm_addr == dm_addr_authdata)
|| (dm_addr == dm_addr_haltregion0)
|| (dm_addr == dm_addr_haltregion31)
|| (dm_addr == dm_addr_verbosity))
dm_run_control.write (dm_addr, dm_word);
else if ( (dm_addr == dm_addr_abstractcs)
|| (dm_addr == dm_addr_command)
|| (dm_addr == dm_addr_data0)
|| (dm_addr == dm_addr_data1)
|| (dm_addr == dm_addr_data2)
|| (dm_addr == dm_addr_data3)
|| (dm_addr == dm_addr_data4)
|| (dm_addr == dm_addr_data5)
|| (dm_addr == dm_addr_data6)
|| (dm_addr == dm_addr_data7)
|| (dm_addr == dm_addr_data8)
|| (dm_addr == dm_addr_data9)
|| (dm_addr == dm_addr_data10)
|| (dm_addr == dm_addr_data11)
|| (dm_addr == dm_addr_abstractauto)
|| (dm_addr == dm_addr_progbuf0))
dm_abstract_commands.write (dm_addr, dm_word);
else if ( (dm_addr == dm_addr_sbcs)
|| (dm_addr == dm_addr_sbaddress0)
|| (dm_addr == dm_addr_sbaddress1)
|| (dm_addr == dm_addr_sbaddress2)
|| (dm_addr == dm_addr_sbdata0)
|| (dm_addr == dm_addr_sbdata1)
|| (dm_addr == dm_addr_sbdata2)
|| (dm_addr == dm_addr_sbdata3))
dm_system_bus.write (dm_addr, dm_word);
else begin
// TODO: set error status?
noAction;
end
endmethod
endinterface
// ----------------
// Facing CPU/hart0
// Reset and run-control
interface Get hart0_get_reset_req = dm_run_control.hart0_get_reset_req;
interface Client hart0_client_run_halt = dm_run_control.hart0_client_run_halt;
interface Get hart0_get_other_req = dm_run_control.hart0_get_other_req;
// GPR access
interface MemoryClient hart0_gpr_mem_client = dm_abstract_commands.hart0_gpr_mem_client;
// CSR access
interface MemoryClient hart0_csr_mem_client = dm_abstract_commands.hart0_csr_mem_client;
// ----------------
// Facing Platform
// Non-Debug-Module Reset (reset all except DM)
interface Get get_ndm_reset_req = dm_run_control.get_ndm_reset_req;
// Read/Write RISC-V memory
interface AXI4_Master_IFC master = dm_system_bus.master;
endmodule
// ================================================================
endpackage

View File

@@ -0,0 +1,162 @@
'Debug_Module' implements a Debug Module for RISC-V processors in
accordance with the RISC-V standard "External Debug Support" spec:
RISC-V External Debug Support
Version 0.13-DRAFT
dd8d8714184970031fa447a452068223f257b51c
Mon Dec 18 13:54:14 2017 -0800
Note: the spec is independent of any particular RISC-V CPU
implementation. It just specifies the standard registers in the Debug
Module that can be read and written by an external debugger (such as
GDB). It specifies the address map of these registers, and the
semantics, i.e., what happens when one reads or writes these
registers. The spec does not say anything about how this spec is
implemented.
Please see comments in Debug_Module.bsv for more details on our
implementation of the Debug Module spec. This implementation is also
not specific to any particular CPU implementation. We use it in
Bluespec RISC-V CPUs, but it could be used with other CPUs as well.
We use this Debug Module in the Bluespec RISC-V Verification Factory
(BRVF).
// ================================================================
What follows is a concise cheat-sheet on the registers in the Debug
Module based on the spec.
// ----------------
// Run Control
DM_Addr dm_addr_dmcontrol = 'h10;
31.30.29.28| 27.26.25.24| 23.22.21.20| 19.18.17.16| 15.14.13.12| 11.10.9.8| 7.6.5.4| 3.2.1.0|
| | | | |------------10--------------| | |dmactive
| | | | |hartsel |ndmreset
| | | |hasel
| | | 0: Single hart selected (hartsel)
| | | 1: Multiple harts selected (hartsel + hart array mask)
| | |hartreset
| |resumereq
|haltreq
DM_Addr dm_addr_dmstatus = 'h11;
31.30.29.28| 27.26.25.24| 23.22.21.20| 19.18.17.16| 15.14.13.12| 11.10.9.8| 7.6.5.4| 3.2.1.0|
| | | | | | | | | | | | | | | | | | | | | | | | |--4--|version
| | | | | | | | | | | | | | | | | | | | | | | | | 0: no DM present
| | | | | | | | | | | | | | | | | | | | | | | | | 1: DM v011
| | | | | | | | | | | | | | | | | | | | | | | | | 2: DM v013
| | | | | | | | | | | | | | | | | | | | | | | | | 15: DM vUnknown
| | | | | | | | | | | | | | | | | | | | | | | |devtreevalid
| | | | | | | | | | | | | | | | | | | | | | |0
| | | | | | | | | | | | | | | | | | | | | |authbusy
| | | | | | | | | | | | | | | | | | | | |authenticated
| | | | | | | | | | | | | | | | | | | |anyhalted
| | | | | | | | | | | | | | | | | | |allhalted
| | | | | | | | | | | | | | | | | |anyrunning
| | | | | | | | | | | | | | | | |allrunning
| | | | | | | | | | | | | | | |anyunavail
| | | | | | | | | | | | | | |allunavail
| | | | | | | | | | | | | |anynonexistent
| | | | | | | | | | | | |allnonexistent
| | | | | | | | | | | |anyresumeack
| | | | | | | | | | |allresumeack
| | | | | | | | | |anyhavereset
| | | | | | | | |allhavereset
| | | | | | |--|0
| | | | | |impebreak
| | | | | 0 No implicit EBREAK at end of PB
| | | | | 1 Implicit EBREAK at end of PB
| | | | |0
| | |--3--|dmerr
| | 0 No error
| | 1 bad addr
| | 7 other error
|----- 5-----|0
DM_Addr dm_addr_hartinfo = 'h12;
DM_Addr dm_addr_haltsum = 'h13;
DM_Addr dm_addr_hawindowsel = 'h14;
DM_Addr dm_addr_hawindow = 'h15;
DM_Addr dm_addr_devtreeaddr0 = 'h19;
DM_Addr dm_addr_authdata = 'h30;
DM_Addr dm_addr_haltregion0 = 'h40;
DM_Addr dm_addr_haltregion31 = 'h5F;
// ----------------
// Abstract commands (read/write RISC-V registers and RISC-V CSRs)
DM_Addr dm_addr_abstractcs = 'h16;
31.30.29.28| 27.26.25.24| 23.22.21.20| 19.18.17.16| 15.14.13.12| 11.10.9.8| 7.6.5.4| 3.2.1.0|
|-----5------|progsize |busy |-3-|cmderr |----5---|datacount
DM_Addr dm_addr_command = 'h17;
31.30.29.28| 27.26.25.24| 23.22.21.20| 19.18.17.16| 15.14.13.12| 11.10.9.8| 7.6.5.4| 3.2.1.0|
| | | | | |-----------------16------------------|regno
| | | | | | 0x0000-0x0FFF CSRs (dpc => PC)
| | | | | | 0x1000-0x101F GPRs
| | | | | | 0x1020-0x103F Floating Point Regs
| | | | | | 0xC000-0xFFFF Reserved
| | | | |write
| | | | 0: specified reg -> arg0 of data
| | | | 1: specified reg <- arg0 of data
| | | |transfer
| | | 0 Don't do the 'write' op
| | | 1 Do the 'write' op
| | | Allows exec of PB without valid vals in 'size' and 'regno'
| | |postexec
| | 1 exec Program Buffer exactly once after the xfer
| |--3--|size
| 2 Lowest 32b of reg
| 3 Lowest 64b of reg
| 4 Lowest 128b of reg
|---------8-----------|cmdtype
0 ACCESS_REG
1 QUICK_ACCESS
DM_Addr dm_addr_data0 = 'h04;
DM_Addr dm_addr_data1 = 'h05;
DM_Addr dm_addr_data2 = 'h06;
DM_Addr dm_addr_data3 = 'h07;
DM_Addr dm_addr_data4 = 'h08;
DM_Addr dm_addr_data5 = 'h09;
DM_Addr dm_addr_data6 = 'h0a;
DM_Addr dm_addr_data7 = 'h0b;
DM_Addr dm_addr_data8 = 'h0c;
DM_Addr dm_addr_data9 = 'h0d;
DM_Addr dm_addr_data10 = 'h0d;
DM_Addr dm_addr_data11 = 'h0f;
DM_Addr dm_addr_abstractauto = 'h18;
DM_Addr dm_addr_progbuf0 = 'h20;
// ----------------
// System Bus access (read/write RISC-V memory/devices)
DM_Addr dm_addr_sbcs = 'h38;
31.30.29.28| 27.26.25.24| 23.22.21.20| 19.18.17.16| 15.14.13.12| 11.10.9.8| 7.6.5.4| 3.2.1.0|
| | | | | | | | | | | |sbaccess8
| | | | | | | | | | |sbaccess16
| | | | | | | | | |sbaccess32
| | | | | | | | |sbaccess64
| | | | | | | |sbaccess128
| | | | | | |-----7-------|sbasize
| | | | |--3--|sberror
| | | | | 0: no bus err
| | | | | 1: timeout
| | | | | 2: bad addr
| | | | | 3: other err (e.g., alignment)
| | | | | 4: busy
| | | |sbautoread
| | |sbautoincrement
| |--3--|sbaccess
| | 0:8b, 1:16b, 2:32b, 3:64b, 4:128b
|singleread
1 triggers single read at sbaddress of size sbaccess
DM_Addr dm_addr_sbaddress0 = 'h39;
DM_Addr dm_addr_sbaddress1 = 'h3a;
DM_Addr dm_addr_sbaddress2 = 'h3b;
DM_Addr dm_addr_sbdata0 = 'h3c;
DM_Addr dm_addr_sbdata1 = 'h3d;
DM_Addr dm_addr_sbdata2 = 'h3e;
DM_Addr dm_addr_sbdata3 = 'h3f;

View File

@@ -0,0 +1,94 @@
default: compile link
all: compile link simulate
TOP = Testbench
TOPFILE = $(TOP).bsv
TOPMODULE = mk$(TOP)
# BSCFLAGS = -keep-fires -aggressive-conditions -no-warn-action-shadowing -no-inline-rwire
# BSCFLAGS = -keep-fires -aggressive-conditions -no-inline-rwire -show-range-conflict -show-schedule
BSCFLAGS = -D RV32 \
-keep-fires \
-aggressive-conditions \
-suppress-warnings G0020 \
-show-schedule
# ----------------------------------------------------------------
# FOR BLUESIM
ISA_DECLS_DIR = $(HOME)/Projects/RISCV/Bluespec_RISCV/ISA
TRX_DIR = $(HOME)/Projects/RISCV/Bluespec_RISCV/Fabrics/TRX
ADDL_LIBS_DIR = $(HOME)/Projects/RISCV/Bluespec_RISCV/BSV_Additional_Libs/BSV
BSCDIRS_BSIM = -simdir build_bsim -bdir build -info-dir build
BSCPATH_BSIM = -p .:..:$(ISA_DECLS_DIR):$(TRX_DIR):$(ADDL_LIBS_DIR):%/Prelude:%/Libraries
build_bsim:
mkdir -p $@
build:
mkdir -p $@
.PHONY: compile
compile: build_bsim build
@echo Compiling...
bsc -u -sim $(BSCDIRS_BSIM) $(BSCFLAGS) $(BSCPATH_BSIM) $(TOPFILE)
@echo Compilation finished
.PHONY: link
link:
@echo Linking...
bsc -e $(TOPMODULE) $(BSCFLAGS) -parallel-sim-link 8 -sim -o ./$(TOP)_bsim_exe $(BSCDIRS_BSIM) $(BSCPATH_BSIM)
@echo Linking finished
.PHONY: simulate
simulate:
@echo Simulation...
logsave bsim.log ./$(TOP)_bsim_exe -V
@echo Simulation finished
# ----------------------------------------------------------------
# FOR VERILOG
BSCDIRS_V = -vdir verilog -bdir build_v -info-dir build_v
BSCPATH_V = -p .:./$(SRC_BSV):%/Prelude:%/Libraries:%/Libraries/TLM3
# Set VSIM to desired Verilog simulator
# VSIM = modelsim
VSIM ?= cvc
# VSIM ?= iverilog
build_v:
mkdir -p $@
verilog:
mkdir -p $@
.PHONY: rtl
rtl: build_v verilog
@echo Verilog generation ...
bsc -u -elab -verilog $(BSCDIRS_V) $(BSCFLAGS) $(BSCPATH_V) $(TOPFILE)
@echo Verilog generation finished
.PHONY: vlink
vlink:
bsc -v -e $(TOPMODULE) -verilog -o ./out_v -vdir verilog -vsim $(VSIM) -keep-fires \
verilog/$(TOPMODULE).v
.PHONY: vsim
vsim:
@echo Simulation...
./out_v
@echo Simulation finished
# ----------------------------------------------------------------
.PHONY: clean
clean:
rm -f *~ src_*/*~ src_*/*.o build/* build_bsim/* build_v/* *.cxx *.h *.o
.PHONY: full_clean
full_clean: clean
rm -r -f *_bsim_exe *.so out_v verilog build build_bsim dump.vcd bsim.log

View File

@@ -0,0 +1,597 @@
package Testbench;
// ================================================================
// Testbench for basic sanity-check testing of the Debug Module.
// ================================================================
// BSV library imports
import FIFOF :: *;
import GetPut :: *;
import ClientServer :: *;
import Connectable :: *;
import StmtFSM :: *;
// ----------------
// Other library imports
import Semi_FIFOF :: *;
// ================================================================
import ISA_Decls :: *;
import TRX :: *;
import Debug_Module :: *;
// ================================================================
Integer csr_addr_dcsr = 'h7b0;
Integer csr_addr_dpc = 'h7b1;
Integer csr_addr_dscratch0 = 'h7b2;
Integer csr_addr_dscratch1 = 'h7b3;
// ================================================================
(* synthesize *)
module mkTestbench (Empty);
// ================================================================
// Cycle-counter and cycle-limit termination
Reg #(Bit #(32)) rg_cycle <- mkReg (0);
Integer cycle_limit = 100;
rule rl_count_cycles;
if (rg_cycle == fromInteger (100)) begin
$display ("Testench: stopping at cycle %0d", cycle_limit);
$finish (0);
end
rg_cycle <= rg_cycle +1;
endrule
// ================================================================
// The Debug Module
Debug_Module_IFC dm <- mkDebug_Module;
// ================================================================
// Model of a hart, and connections to dm
Hart_DM_IFC hart0 <- mkHart_Model (0);
// Reset
mkConnection (dm.hart0_reset_req, hart0.hart_reset_req);
// Run-control
mkConnection (dm.hart0_run_req_rsp, hart0.hart_run_req_rsp);
// GPR access
mkConnection (dm.master_for_gprs, hart0.slave_for_gprs);
// CSR access
mkConnection (dm.master_for_csrs, hart0.slave_for_csrs);
// ================================================================
// Over-simplified model of platform reset (all except DM)
rule rl_ndm_reset;
let x <- dm.ndm_reset_req.get;
$display ("Testbench.rl_ndm_reset: Resetting all platform except Debug Module");
endrule
// ================================================================
// Over-simplified model of system (RISC-V) memory
// On reads, return addr + 2.
rule rl_mem_read;
let rda <- pop_o (dm.master.fo_rda);
let data = rda.addr + 2; // Bogus data, for now
let rdr = TRX_RdR {trans_id: rda.trans_id,
status : TRX_OKAY,
data : data};
dm.master.fi_rdr.enq (rdr);
$display ("Testbench: memory read [0x%08h] => 0x%08h", rda.addr, data);
endrule
rule rl_mem_write;
let wra <- pop_o (dm.master.fo_wra);
let wrd <- pop_o (dm.master.fo_wrd);
let wrr = TRX_WrR {trans_id: wra.trans_id,
status : TRX_OKAY};
dm.master.fi_wrr.enq (wrr);
$display ("Testbench: memory write [0x%08h] <= 0x%08h", wra.addr, wrd.data);
endrule
// ================================================================
// Abstract command sequences (read/write GPR/CSR)
Reg #(Bit #(32)) rg_abstractcs <- mkRegU;
// Read a register
function Stmt fn_stmt_read_reg (Bit #(16) regno);
return
seq
$display ("----------------\nRead RISC-V reg");
// Clear any prior error status
dm.write (dm_addr_abstractcs, fn_mk_abstractcs (dm_cmderr_w1c));
// Perform the read
dm.write (dm_addr_command,
fn_mk_command_access_reg (DM_COMMAND_ACCESS_REG_SIZE_LOWER32,
False, // postexec
True, // transfer
False, // write
regno));
// Read status to check no error
action
let x <- dm.av_read (dm_addr_abstractcs);
rg_abstractcs <= x;
endaction
while (fn_abstractcs_busy (rg_abstractcs)) seq
$display ("Testbench: read reg: busy");
action
let x <- dm.av_read (dm_addr_abstractcs);
rg_abstractcs <= x;
endaction
endseq
if (fn_abstractcs_cmderr (rg_abstractcs) != DM_ABSTRACTCS_CMDERR_NONE)
$display ("Testbench: read reg => ", fshow (fn_abstractcs_cmderr (rg_abstractcs)));
else action
let x <- dm.av_read (dm_addr_data0);
$display ("Testbench: read reg => 0x%08h", x);
endaction
endseq;
endfunction
// Write a register
function Stmt fn_stmt_write_reg (Bit #(16) regno, Bit #(32) data);
return
seq
$display ("----------------\nWrite RISC-V reg");
// Clear any prior error status
dm.write (dm_addr_abstractcs, fn_mk_abstractcs (dm_cmderr_w1c));
// Write data0
dm.write (dm_addr_data0, data);
// Perform the write
dm.write (dm_addr_command,
fn_mk_command_access_reg (DM_COMMAND_ACCESS_REG_SIZE_LOWER32,
False, // postexec
True, // transfer
True, // write
regno));
// Read status to check no error
action
let x <- dm.av_read (dm_addr_abstractcs);
rg_abstractcs <= x;
endaction
while (fn_abstractcs_busy (rg_abstractcs)) seq
$display ("Testbench: write reg: busy");
action
let x <- dm.av_read (dm_addr_abstractcs);
rg_abstractcs <= x;
endaction
endseq
$display ("Testbench: write reg => ", fshow (fn_abstractcs_cmderr (rg_abstractcs)));
endseq;
endfunction
// ================================================================
// System Bus access sequences (read/write RISC-V memory)
Reg #(Bool) rg_busy <- mkRegU;
Reg #(Bit #(32)) rg_j <- mkRegU;
Reg #(Bit #(32)) rg_addr <- mkRegU;
Reg #(Bit #(32)) rg_data <- mkRegU;
Stmt stmt_wait_for_sb_nonbusy = (
seq
rg_busy <= True;
while (rg_busy) seq
delay (1);
action
let x <- dm.av_read (dm_addr_sbcs);
let sberror = fn_sbcs_sberror (x);
rg_busy <= (sberror == DM_SBERROR_BUSY_STALE);
if ( (sberror != DM_SBERROR_NONE)
&& (sberror != DM_SBERROR_BUSY_STALE))
begin
$display ("Testbench: stmt_wait_for_sb_nonbusy: ", fshow (sberror));
$finish (1);
end
endaction
endseq
endseq);
// Do a single-read from memory
Stmt stmt_mem_read_1 = (
seq
dm.write (dm_addr_sbaddress0, 'h1_0000);
dm.write (dm_addr_sbcs, fn_mk_sbcs (True, // sbsingleread
DM_SBACCESS_32_BIT,
False, // sbautoincrement
False, // sbautoread
DM_SBERROR_UNDEF7_W1C)); // clear sberror
stmt_wait_for_sb_nonbusy;
action
let x <- dm.av_read (dm_addr_sbdata0);
$display ("stmt_mem_read_1: read-data = 0x%08h", x);
endaction
endseq);
// Do a multiple-read from memory
Stmt stmt_mem_read_4 = (
seq
dm.write (dm_addr_sbaddress0, 'h1_0000);
dm.write (dm_addr_sbcs, fn_mk_sbcs (True, // sbsingleread
DM_SBACCESS_32_BIT,
True, // sbautoincrement
True, // sbautoread
DM_SBERROR_UNDEF7_W1C)); // clear sberror
for (rg_j <= 0; rg_j < 3; rg_j <= rg_j + 1) seq
stmt_wait_for_sb_nonbusy;
action
let x <- dm.av_read (dm_addr_sbdata0);
$display ("stmt_mem_read_4: read-data [%0d] = 0x%08h", rg_j, x);
endaction
endseq
dm.write (dm_addr_sbcs, fn_mk_sbcs (False, // sbsingleread
DM_SBACCESS_32_BIT,
False, // sbautoincrement
False, // sbautoread
DM_SBERROR_UNDEF7_W1C)); // clear sberror
stmt_wait_for_sb_nonbusy;
action
let x <- dm.av_read (dm_addr_sbdata0);
$display ("stmt_mem_read_4: read-data [%0d] = 0x%08h", rg_j, x);
endaction
endseq);
// Do a single-write to memory
Stmt stmt_mem_write_1 = (
seq
dm.write (dm_addr_sbcs, fn_mk_sbcs (False, // sbsingleread
DM_SBACCESS_32_BIT,
False, // sbautoincrement
False, // sbautoread
DM_SBERROR_UNDEF7_W1C)); // clear sberror
stmt_wait_for_sb_nonbusy;
dm.write (dm_addr_sbaddress0, 'h1_0000);
dm.write (dm_addr_sbdata0, 'h_BEEF);
endseq);
// Do a multiple-write to memory
Stmt stmt_mem_write_4 = (
seq
dm.write (dm_addr_sbcs, fn_mk_sbcs (False, // sbsingleread
DM_SBACCESS_32_BIT,
True, // sbautoincrement
False, // sbautoread
DM_SBERROR_UNDEF7_W1C)); // clear sberror
stmt_wait_for_sb_nonbusy;
action
rg_addr <= 'h_2000;
rg_data <= 'h_DAFA_0000;
endaction
dm.write (dm_addr_sbaddress0, rg_addr);
for (rg_j <= 0; rg_j < 4; rg_j <= rg_j + 1) seq
stmt_wait_for_sb_nonbusy;
action
$display ("stmt_mem_write_4: [0x%08h] x = 0x%08h", rg_addr + rg_j, rg_data);
dm.write (dm_addr_sbdata0, rg_data);
rg_data <= rg_data + 1;
endaction
endseq
endseq);
// ================================================================
// Run-control test sequences (reset, run, halt, single-step)
let dmcontrol_dm_reset
= fn_mk_dmcontrol (False, // haltreq
False, // resumereq
False, // hartreset
False, // hasel
0, // hartsel,
False, // ndmreset
False); // dmactive; assert reset
let dmcontrol_ndmreset
= fn_mk_dmcontrol (False, // haltreq
False, // resumereq
False, // hartreset
False, // hasel,
0, // hartsel
True, // ndmreset
True); // dmactive
let dmcontrol_err_hasel
= fn_mk_dmcontrol (False, // haltreq
False, // resumereq
False, // hartreset
True, // hasel,
0, // hartsel
False, // ndmreset
True); // dmactive
let dmcontrol_err_hartsel
= fn_mk_dmcontrol (False, // haltreq
False, // resumereq
False, // hartreset
False, // hasel,
3, // hartsel
False, // ndmreset
True); // dmactive
let dmcontrol_hartreset
= fn_mk_dmcontrol (False, // haltreq
False, // resumereq
True, // hartreset
False, // hasel,
0, // hartsel
False, // ndmreset
True); // dmactive
let dmcontrol_err_haltreq_resumereq
= fn_mk_dmcontrol (True, // haltreq
True, // resumereq
False, // hartreset
False, // hasel,
0, // hartsel
False, // ndmreset
True); // dmactive
let dmcontrol_haltreq
= fn_mk_dmcontrol (True, // haltreq
False, // resumereq
False, // hartreset
False, // hasel,
0, // hartsel
False, // ndmreset
True); // dmactive
let dmcontrol_resumereq
= fn_mk_dmcontrol (False, // haltreq
True, // resumereq
False, // hartreset
False, // hasel,
0, // hartsel
False, // ndmreset
True); // dmactive
function Stmt fn_stmt_run_control (DM_Word dm_word);
return seq
dm.write (dm_addr_dmcontrol, dm_word);
delay (5);
// Check and show status
action
let x <- dm.av_read (dm_addr_dmstatus);
$display (" ", fshow_dmstatus (x));
endaction
endseq;
endfunction
// ----------------
// For single-step, set 'step' bit in DCSR, then run
let dcsr_step = {4'h4, // xdebugver
12'b0,
1'b0, // ebreakm
1'b0,
1'b0, // ebreaks
1'b0, // ebreaku
1'b0, // stepie
1'b0, // stepcount
1'b0, // steptime
3'b0, // cause
3'b0,
1'b1, // step
2'h3};
Stmt stmt_single_step = (
seq
// set 'step' in dcsr
fn_stmt_write_reg (fromInteger (dm_command_access_reg_regno_csr_0 + csr_addr_dcsr),
dcsr_step); // priv
fn_stmt_run_control (dmcontrol_resumereq);
endseq);
// ================================================================
// Top-level test. Comment/Uncomment desired parts.
Stmt test = seq
// Reset DM
$display ("----------------\n'Testbench: Reset DM'");
fn_stmt_run_control (dmcontrol_dm_reset);
/*
$display ("----------------\n'Testbench: Reset Platform'");
fn_stmt_run_control (dmcontrol_ndmreset);
$display ("----------------\n'Testbench: Err hasel'");
fn_stmt_run_control (dmcontrol_err_hasel);
$display ("----------------\n'Testbench: Err hartsel'");
fn_stmt_run_control (dmcontrol_err_hartsel);
$display ("----------------\n'Testbench: Reset hart'");
fn_stmt_run_control (dmcontrol_hartreset);
$display ("----------------\n'Testbench: Err haltreq and resumereq'");
fn_stmt_run_control (dmcontrol_err_haltreq_resumereq);
$display ("----------------\n'Testbench: Continue'");
fn_stmt_run_control (dmcontrol_resumereq);
$display ("----------------\n'Testbench: Halt'");
fn_stmt_run_control (dmcontrol_haltreq);
$display ("----------------\n'Testbench: Single step'");
stmt_single_step;
*/
$display ("----------------\n'Testbench: Read GPR'");
fn_stmt_read_reg (fromInteger (dm_command_access_reg_regno_gpr_0 + 5));
$display ("----------------\n'Testbench: Read CSR'");
fn_stmt_read_reg (fromInteger (dm_command_access_reg_regno_csr_0 + 3));
$display ("----------------\n'Testbench: Write GPR'");
fn_stmt_write_reg (fromInteger (dm_command_access_reg_regno_gpr_0 + 5), 'h_AAAA_0005);
$display ("----------------\n'Testbench: Write CSR'");
fn_stmt_write_reg (fromInteger (dm_command_access_reg_regno_csr_0 + 3), 'h_CCCC_0003);
/*
$display ("----------------\n'Testbench: Read 1'");
stmt_mem_read_1;
$display ("----------------\n'Testbench: Write 1'");
stmt_mem_write_1;
$display ("----------------\n'Testbench: Read 4'");
stmt_mem_read_4;
$display ("----------------\n'Testbench: Write 4'");
stmt_mem_write_4;
*/
await (False);
endseq;
mkAutoFSM (test);
endmodule
// ================================================================
// Over-simplified model of a hart (reset, run/halt, read/write GPR/CSR)
interface Hart_DM_IFC;
// Reset
interface Put #(Token) hart_reset_req;
// Run-control
interface Server #(Bool, Bool) hart_run_req_rsp;
// GPR access
interface TRX_Slave_IFC #(5,32,0) slave_for_gprs;
// CSR access
interface TRX_Slave_IFC #(12,32,0) slave_for_csrs;
endinterface
(* synthesize *)
module mkHart_Model #(parameter Bit #(10) hart_id) (Hart_DM_IFC);
Reg #(Bool) rg_hart_running <- mkReg (False);
FIFOF #(Token) f_hart_reset_reqs <- mkFIFOF;
FIFOF #(Bool) f_hart_run_reqs <- mkFIFOF;
FIFOF #(Bool) f_hart_run_rsps <- mkFIFOF;
// TRX interface to gprs
TRX_Buffer_IFC #(5,32,0) trx_buf_gprs <- mkTRX_Buffer;
// TRX interface to crs
TRX_Buffer_IFC #(12,32,0) trx_buf_csrs <- mkTRX_Buffer;
// ----------------------------------------------------------------
// BEHAVIOR
// ----------------
// Reset
rule rl_hart_reset;
let x = f_hart_reset_reqs.first;
f_hart_reset_reqs.deq;
$display ("Testbench.hart [%0d]: reset", hart_id);
endrule
// ----------------
// Run-control
rule rl_resume_hart (f_hart_run_reqs.first);
f_hart_run_reqs.deq;
rg_hart_running <= True;
if (rg_hart_running)
$display ("Testbench.hart [%0d].rl_resume_hart: already running", hart_id);
else
$display ("Testbench.hart [%0d].rl_resume_hart: resuming", hart_id);
f_hart_run_rsps.enq (True);
endrule
rule rl_halt_hart (! f_hart_run_reqs.first);
f_hart_run_reqs.deq;
rg_hart_running <= False;
if (rg_hart_running)
$display ("Testbench.hart [%0d].rl_halt_hart: halting", hart_id);
else
$display ("Testbench.hart [%0d].rl_halt_hart: already halted", hart_id);
f_hart_run_rsps.enq (False);
endrule
// ----------------
// GPR access
rule rl_read_gpr;
let rda <- pop_o (trx_buf_gprs.master.fo_rda);
Bit #(32) data = extend (rda.addr) + 'h1000;
let rdr = TRX_RdR {trans_id: rda.trans_id,
status: TRX_OKAY,
data: data};
trx_buf_gprs.master.fi_rdr.enq (rdr);
$display ("Testbench.hart [%0d]: Read GPR [%0h] => 0x%08h", hart_id, rda.addr, data);
endrule
rule rl_read_csr;
let rda <- pop_o (trx_buf_csrs.master.fo_rda);
Bit #(32) data = extend (rda.addr) + 'h2000;
let rdr = TRX_RdR {trans_id: rda.trans_id,
status: TRX_OKAY,
data: data};
trx_buf_csrs.master.fi_rdr.enq (rdr);
$display ("Testbench.hart [%0d]: Read CSR [%0h] => 0x%08h", hart_id, rda.addr, data);
endrule
rule rl_write_gpr;
let wra <- pop_o (trx_buf_gprs.master.fo_wra);
let wrd <- pop_o (trx_buf_gprs.master.fo_wrd);
let wrr = TRX_WrR {trans_id: wra.trans_id, status: TRX_OKAY};
trx_buf_gprs.master.fi_wrr.enq (wrr);
$display ("Testbench.hart [%0d]: Write GPR [%0h] <= 0x%08h", hart_id, wra.addr, wrd.data);
endrule
rule rl_write_csr;
let wra <- pop_o (trx_buf_csrs.master.fo_wra);
let wrd <- pop_o (trx_buf_csrs.master.fo_wrd);
let wrr = TRX_WrR {trans_id: wra.trans_id, status: TRX_OKAY};
trx_buf_csrs.master.fi_wrr.enq (wrr);
$display ("Testbench.hart [%0d]: Write CSR [%0h] <= 0x%08h", hart_id, wra.addr, wrd.data);
endrule
// ----------------------------------------------------------------
// INTERFACE
// Reset
interface Put hart_reset_req = toPut (f_hart_reset_reqs);
// Run-control
interface Server hart_run_req_rsp = toGPServer (f_hart_run_reqs, f_hart_run_rsps);
// GPR access
interface TRX_Slave_IFC slave_for_gprs = trx_buf_gprs.slave;
// CSR access
interface TRX_Slave_IFC slave_for_csrs = trx_buf_csrs.slave;
endmodule
// ================================================================
endpackage

1125
src_Core/ISA/ISA_Decls.bsv Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,179 @@
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved
// ================================================================
// This is an 'include' file, not a separate BSV package
//
// Contains RISC-V ISA defs for the 'C' ("compressed") extension
// i.e., 16-bit instructions
//
// ================================================================
// Instruction field encodings
typedef Bit #(16) Instr_C;
Bit #(2) opcode_C0 = 2'b00;
Bit #(2) opcode_C1 = 2'b01;
Bit #(2) opcode_C2 = 2'b10;
Bit #(3) funct3_C_LWSP = 3'b_010;
Bit #(3) funct3_C_LDSP = 3'b_011; // RV64 and RV128
Bit #(3) funct3_C_LQSP = 3'b_001; // RV128
Bit #(3) funct3_C_FLWSP = 3'b_011; // RV32FC
Bit #(3) funct3_C_FLDSP = 3'b_001; // RV32DC, RV64DC
Bit #(3) funct3_C_SWSP = 3'b_110;
Bit #(3) funct3_C_SQSP = 3'b_101; // RV128
Bit #(3) funct3_C_FSDSP = 3'b_101; // RV32DC, RV64DC
Bit #(3) funct3_C_SDSP = 3'b_111; // RV64 and RV128
Bit #(3) funct3_C_FSWSP = 3'b_111; // RV32FC
Bit #(3) funct3_C_LQ = 3'b_001; // RV128
Bit #(3) funct3_C_FLD = 3'b_001; // RV32DC, RV64DC
Bit #(3) funct3_C_LW = 3'b_010;
Bit #(3) funct3_C_LD = 3'b_011; // RV64 and RV128
Bit #(3) funct3_C_FLW = 3'b_011; // RV32FC
Bit #(3) funct3_C_FSD = 3'b_101; // RV32DC, RV64DC
Bit #(3) funct3_C_SQ = 3'b_101; // RV128
Bit #(3) funct3_C_SW = 3'b_110;
Bit #(3) funct3_C_SD = 3'b_111; // RV64 and RV128
Bit #(3) funct3_C_FSW = 3'b_111; // RV32FC
Bit #(3) funct3_C_JAL = 3'b_001; // RV32
Bit #(3) funct3_C_J = 3'b_101;
Bit #(3) funct3_C_BEQZ = 3'b_110;
Bit #(3) funct3_C_BNEZ = 3'b_111;
Bit #(4) funct4_C_JR = 4'b_1000;
Bit #(4) funct4_C_JALR = 4'b_1001;
Bit #(3) funct3_C_LI = 3'b_010;
Bit #(3) funct3_C_LUI = 3'b_011; // RV64 and RV128
Bit #(3) funct3_C_NOP = 3'b_000;
Bit #(3) funct3_C_ADDI = 3'b_000;
Bit #(3) funct3_C_ADDIW = 3'b_001;
Bit #(3) funct3_C_ADDI16SP = 3'b_011;
Bit #(3) funct3_C_ADDI4SPN = 3'b_000;
Bit #(3) funct3_C_SLLI = 3'b_000;
Bit #(3) funct3_C_SRLI = 3'b_100;
Bit #(2) funct2_C_SRLI = 2'b_00;
Bit #(3) funct3_C_SRAI = 3'b_100;
Bit #(2) funct2_C_SRAI = 2'b_01;
Bit #(3) funct3_C_ANDI = 3'b_100;
Bit #(2) funct2_C_ANDI = 2'b_10;
Bit #(4) funct4_C_MV = 4'b_1000;
Bit #(4) funct4_C_ADD = 4'b_1001;
Bit #(6) funct6_C_AND = 6'b_100_0_11;
Bit #(2) funct2_C_AND = 2'b_11;
Bit #(6) funct6_C_OR = 6'b_100_0_11;
Bit #(2) funct2_C_OR = 2'b_10;
Bit #(6) funct6_C_XOR = 6'b_100_0_11;
Bit #(2) funct2_C_XOR = 2'b_01;
Bit #(6) funct6_C_SUB = 6'b_100_0_11;
Bit #(2) funct2_C_SUB = 2'b_00;
Bit #(6) funct6_C_ADDW = 6'b_100_1_11;
Bit #(2) funct2_C_ADDW = 2'b_01;
Bit #(6) funct6_C_SUBW = 6'b_100_1_11;
Bit #(2) funct2_C_SUBW = 2'b_00;
Bit #(4) funct4_C_EBREAK = 4'b_1001;
// ================================================================
// Functions to extract instruction fields from 'C' (compressed) instructions
function Tuple4 #(Bit #(4), RegName, RegName, Bit #(2)) fv_ifields_CR_type (Instr_C instr);
let funct4 = instr [15:12];
let rd_rs1 = instr [11: 7];
let rs2 = instr [ 6: 2];
let op = instr [ 1: 0];
return tuple4 (funct4, rd_rs1, rs2, op);
endfunction
function Tuple5 #(Bit #(3), Bit #(1), Bit #(5), Bit #(5), Bit #(2)) fv_ifields_CI_type (Instr_C instr);
let funct3 = instr [15:13];
let imm_at_12 = instr [12:12];
let rd_rs1 = instr [11: 7];
let imm_at_6_2 = instr [ 6: 2];
let op = instr [ 1: 0];
return tuple5 (funct3, imm_at_12, rd_rs1, imm_at_6_2, op);
endfunction
function Tuple4 #(Bit #(3), Bit #(6), RegName, Bit #(2)) fv_ifields_CSS_type (Instr_C instr);
let funct3 = instr [15:13];
let imm_at_12_7 = instr [12: 7];
let rs2 = instr [ 6: 2];
let op = instr [ 1: 0];
return tuple4 (funct3, imm_at_12_7, rs2, op);
endfunction
function Tuple4 #(Bit #(3), Bit #(8), RegName, Bit #(2)) fv_ifields_CIW_type (Instr_C instr);
let funct3 = instr [15:13];
let imm_at_12_5 = instr [12: 5];
let rd = {2'b01, instr [4:2]};
let op = instr [ 1: 0];
return tuple4 (funct3, imm_at_12_5, rd, op);
endfunction
function Tuple6 #(Bit #(3), Bit #(3), RegName, Bit #(2), RegName, Bit #(2)) fv_ifields_CL_type (Instr_C instr);
let funct3 = instr [15:13];
let imm_at_12_10 = instr [12:10];
let rs1 = {2'b01, instr [9:7]};
let imm_at_6_5 = instr [ 6: 5];
let rd = {2'b01, instr [4:2]};
let op = instr [ 1: 0];
return tuple6 (funct3, imm_at_12_10, rs1, imm_at_6_5, rd, op);
endfunction
function Tuple6 #(Bit #(3), Bit #(3), RegName, Bit #(2), RegName, Bit #(2)) fv_ifields_CS_type (Instr_C instr);
let funct3 = instr [15:13];
let imm_at_12_10 = instr [12:10];
let rs1 = {2'b01, instr [9:7]};
let imm_at_6_5 = instr [ 6: 5];
let rs2 = {2'b01, instr [4:2]};
let op = instr [ 1: 0];
return tuple6 (funct3, imm_at_12_10, rs1, imm_at_6_5, rs2, op);
endfunction
function Tuple5 #(Bit #(6), RegName, Bit #(2), RegName, Bit #(2)) fv_ifields_CA_type (Instr_C instr);
let funct6 = instr [15:10];
let rd_rs1 = {2'b01, instr [9:7]};
let funct2 = instr [ 6: 5];
let rs2 = {2'b01, instr [4:2]};
let op = instr [ 1: 0];
return tuple5 (funct6, rd_rs1, funct2, rs2, op);
endfunction
function Tuple5 #(Bit #(3), Bit #(3), RegName, Bit #(5), Bit #(2)) fv_ifields_CB_type (Instr_C instr);
let funct3 = instr [15:13];
let imm_at_12_10 = instr [12:10];
let rs1 = {2'b01, instr [9:7]};
let imm_at_6_2 = instr [ 6: 2];
let op = instr [ 1: 0];
return tuple5 (funct3, imm_at_12_10, rs1, imm_at_6_2, op);
endfunction
function Tuple3 #(Bit #(3), Bit #(11), Bit #(2)) fv_ifields_CJ_type (Instr_C instr);
let funct3 = instr [15:13];
let imm_at_12_2 = instr [12: 2];
let op = instr [ 1: 0];
return tuple3 (funct3, imm_at_12_2, op);
endfunction
// ================================================================

View File

@@ -0,0 +1,716 @@
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved
// ================================================================
// This is an 'include' file, not a separate BSV package
//
// Contains RISC-V Machine-Level ISA defs
//
// ================================================================
// ================================================================
// Utility functions
// In these functions, 'bitpos' is Bit #(6) which is enough to index
// 64-bit words in RV64.
function Bit #(n) fv_assign_bit (Bit #(n) x, Bit #(6) bitpos, Bit #(1) b)
provisos (Add #(a__, 1, n));
Bit #(n) mask = (1 << bitpos);
Bit #(n) val = (extend (b) << bitpos);
return ((x & (~ mask)) | val);
endfunction
function Bit #(n) fv_assign_bits (Bit #(n) x, Bit #(6) bitpos, Bit #(w) bs)
provisos (Add #(a__, w, n));
Bit #(n) mask = (((1 << valueOf (w)) - 1) << bitpos);
Bit #(n) val = (extend (bs) << bitpos);
return ((x & (~ mask)) | val);
endfunction
function Bit #(w) fv_get_bits (Bit #(n) x, Bit #(6) bitpos)
provisos (Add #(a__, w, n));
Bit #(n) mask = ((1 << valueOf (w)) - 1);
return truncate ((x >> bitpos) & mask);
endfunction
// ================================================================
// Machine-level CSRs
CSR_Addr csr_addr_mvendorid = 12'hF11; // Vendor ID
CSR_Addr csr_addr_marchid = 12'hF12; // Architecture ID
CSR_Addr csr_addr_mimpid = 12'hF13; // Implementation ID
CSR_Addr csr_addr_mhartid = 12'hF14; // Hardware thread ID
CSR_Addr csr_addr_mstatus = 12'h300; // Machine status
CSR_Addr csr_addr_misa = 12'h301; // ISA and extensions
CSR_Addr csr_addr_medeleg = 12'h302; // Machine exception delegation
CSR_Addr csr_addr_mideleg = 12'h303; // Machine interrupt delegation
CSR_Addr csr_addr_mie = 12'h304; // Machine interrupt-enable
CSR_Addr csr_addr_mtvec = 12'h305; // Machine trap handler base address
CSR_Addr csr_addr_mcounteren = 12'h306; // Machine counter enable
CSR_Addr csr_addr_mscratch = 12'h340; // Scratch reg for machine trap handlers
CSR_Addr csr_addr_mepc = 12'h341; // Machine exception program counter
CSR_Addr csr_addr_mcause = 12'h342; // Machine trap cause
CSR_Addr csr_addr_mtval = 12'h343; // Machine bad address
CSR_Addr csr_addr_mip = 12'h344; // Machine interrupt pending
CSR_Addr csr_addr_pmpcfg0 = 12'h3A0; // PMP Config
CSR_Addr csr_addr_pmpcfg1 = 12'h3A1; // PMP Config
CSR_Addr csr_addr_pmpcfg2 = 12'h3A2; // PMP Config
CSR_Addr csr_addr_pmpcfg3 = 12'h3A3; // PMP Config
CSR_Addr csr_addr_pmpaddr0 = 12'h3B0; // PMP address register
CSR_Addr csr_addr_pmpaddr1 = 12'h3B1; // PMP address register
CSR_Addr csr_addr_pmpaddr2 = 12'h3B2; // PMP address register
CSR_Addr csr_addr_pmpaddr3 = 12'h3B3; // PMP address register
CSR_Addr csr_addr_pmpaddr4 = 12'h3B4; // PMP address register
CSR_Addr csr_addr_pmpaddr5 = 12'h3B5; // PMP address register
CSR_Addr csr_addr_pmpaddr6 = 12'h3B6; // PMP address register
CSR_Addr csr_addr_pmpaddr7 = 12'h3B7; // PMP address register
CSR_Addr csr_addr_pmpaddr8 = 12'h3B8; // PMP address register
CSR_Addr csr_addr_pmpaddr9 = 12'h3B9; // PMP address register
CSR_Addr csr_addr_pmpaddr10 = 12'h3BA; // PMP address register
CSR_Addr csr_addr_pmpaddr11 = 12'h3BB; // PMP address register
CSR_Addr csr_addr_pmpaddr12 = 12'h3BC; // PMP address register
CSR_Addr csr_addr_pmpaddr13 = 12'h3BD; // PMP address register
CSR_Addr csr_addr_pmpaddr14 = 12'h3BE; // PMP address register
CSR_Addr csr_addr_pmpaddr15 = 12'h3BF; // PMP address register
CSR_Addr csr_addr_mcycle = 12'hB00; // Machine cycle counter
CSR_Addr csr_addr_minstret = 12'hB02; // Machine Instructions retired counter
CSR_Addr csr_addr_mhpmcounter3 = 12'hB03; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter4 = 12'hB04; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter5 = 12'hB05; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter6 = 12'hB06; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter7 = 12'hB07; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter8 = 12'hB08; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter9 = 12'hB09; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter10 = 12'hB0A; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter11 = 12'hB0B; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter12 = 12'hB0C; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter13 = 12'hB0D; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter14 = 12'hB0E; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter15 = 12'hB0F; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter16 = 12'hB10; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter17 = 12'hB11; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter18 = 12'hB12; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter19 = 12'hB13; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter20 = 12'hB14; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter21 = 12'hB15; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter22 = 12'hB16; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter23 = 12'hB17; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter24 = 12'hB18; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter25 = 12'hB19; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter26 = 12'hB1A; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter27 = 12'hB1B; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter28 = 12'hB1C; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter29 = 12'hB1D; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter30 = 12'hB1E; // Machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter31 = 12'hB1F; // Machine performance-monitoring counter
CSR_Addr csr_addr_mcycleh = 12'hB80; // Upper 32 bits of csr_mcycle (RV32I only)
CSR_Addr csr_addr_minstreth = 12'hB82; // Upper 32 bits of csr_minstret (RV32I only)
CSR_Addr csr_addr_mhpmcounter3h = 12'hB83; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter4h = 12'hB84; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter5h = 12'hB85; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter6h = 12'hB86; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter7h = 12'hB87; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter8h = 12'hB88; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter9h = 12'hB89; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter10h = 12'hB8A; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter11h = 12'hB8B; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter12h = 12'hB8C; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter13h = 12'hB8D; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter14h = 12'hB8E; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter15h = 12'hB8F; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter16h = 12'hB90; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter17h = 12'hB91; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter18h = 12'hB92; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter19h = 12'hB93; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter20h = 12'hB94; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter21h = 12'hB95; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter22h = 12'hB96; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter23h = 12'hB97; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter24h = 12'hB98; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter25h = 12'hB99; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter26h = 12'hB9A; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter27h = 12'hB9B; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter28h = 12'hB9C; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter29h = 12'hB9D; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter30h = 12'hB9E; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmcounter31h = 12'hB9F; // Upper 32 bits of machine performance-monitoring counter
CSR_Addr csr_addr_mhpmevent3 = 12'h323; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent4 = 12'h324; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent5 = 12'h325; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent6 = 12'h326; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent7 = 12'h327; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent8 = 12'h328; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent9 = 12'h329; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent10 = 12'h32A; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent11 = 12'h32B; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent12 = 12'h32C; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent13 = 12'h32D; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent14 = 12'h32E; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent15 = 12'h32F; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent16 = 12'h330; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent17 = 12'h331; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent18 = 12'h332; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent19 = 12'h333; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent20 = 12'h334; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent21 = 12'h335; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent22 = 12'h336; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent23 = 12'h337; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent24 = 12'h338; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent25 = 12'h339; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent26 = 12'h33A; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent27 = 12'h33B; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent28 = 12'h33C; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent29 = 12'h33D; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent30 = 12'h33E; // Machine performance-monitoring event selector
CSR_Addr csr_addr_mhpmevent31 = 12'h33F; // Machine performance-monitoring event selector
CSR_Addr csr_addr_tselect = 12'h7A0; // Debug/Trace trigger register select
CSR_Addr csr_addr_tdata1 = 12'h7A1; // First Debug/Trace trigger data
CSR_Addr csr_addr_tdata2 = 12'h7A2; // Secont Debug/Trace trigger data
CSR_Addr csr_addr_tdata3 = 12'h7A3; // Third Debug/Trace trigger data
CSR_Addr csr_addr_dcsr = 12'h7B0; // Debug control and status
CSR_Addr csr_addr_dpc = 12'h7B1; // Debug PC
CSR_Addr csr_addr_dscratch0 = 12'h7B2; // Debug scratch0
CSR_Addr csr_addr_dscratch1 = 12'h7B3; // Debug scratch1
// ================================================================
// MISA
typedef struct {
Bit #(2) mxl;
Bit #(1) z; Bit #(1) y;
Bit #(1) x; Bit #(1) w; Bit #(1) v; Bit #(1) u; Bit #(1) t; Bit #(1) s; Bit #(1) r; Bit #(1) q;
Bit #(1) p; Bit #(1) o; Bit #(1) n; Bit #(1) m; Bit #(1) l; Bit #(1) k; Bit #(1) j; Bit #(1) i;
Bit #(1) h; Bit #(1) g; Bit #(1) f; Bit #(1) e; Bit #(1) d; Bit #(1) c; Bit #(1) b; Bit #(1) a;
} MISA
deriving (Bits);
Bit #(2) misa_mxl_zero = 0;
Bit #(2) misa_mxl_32 = 1;
Bit #(2) misa_mxl_64 = 2;
Bit #(2) misa_mxl_128 = 3;
function WordXL misa_to_word (MISA ms);
return {ms.mxl,
0, // expands appropriately for RV32 and RV64
ms.z, ms.y,
ms.x, ms.w, ms.v, ms.u, ms.t, ms.s, ms.r, ms.q,
ms.p, ms.o, ms.n, ms.m, ms.l, ms.k, ms.j, ms.i,
ms.h, ms.g, ms.f, ms.e, ms.d, ms.c, ms.b, ms.a};
endfunction
function MISA word_to_misa (WordXL x);
return MISA {mxl: x [xlen-1:xlen-2],
z: x [25], y: x [24],
x: x [23], w: x [22], v: x [21], u: x [20], t: x [19], s: x [18], r: x [17], q: x [16],
p: x [15], o: x [14], n: x [13], m: x [12], l: x [11], k: x [10], j: x [9], i: x [8],
h: x [7], g: x [6], f: x [5], e: x [4], d: x [3], c: x [2], b: x [1], a: x [0]};
endfunction
instance FShow #(MISA);
function Fmt fshow (MISA misa);
let fmt_mxl = case (misa.mxl)
1: $format ("mxl 32");
2: $format ("mxl 64");
3: $format ("mxl 128");
default: $format ("mxl unknown %0d", misa.mxl);
endcase;
return ( fmt_mxl
+ $format ((misa.z == 1'b1) ? "Z" : "")
+ $format ((misa.y == 1'b1) ? "Y" : "")
+ $format ((misa.x == 1'b1) ? "X" : "")
+ $format ((misa.w == 1'b1) ? "W" : "")
+ $format ((misa.v == 1'b1) ? "V" : "")
+ $format ((misa.u == 1'b1) ? "U" : "")
+ $format ((misa.t == 1'b1) ? "T" : "")
+ $format ((misa.s == 1'b1) ? "S" : "")
+ $format ((misa.r == 1'b1) ? "R" : "")
+ $format ((misa.q == 1'b1) ? "Q" : "")
+ $format ((misa.p == 1'b1) ? "P" : "")
+ $format ((misa.o == 1'b1) ? "O" : "")
+ $format ((misa.n == 1'b1) ? "N" : "")
+ $format ((misa.m == 1'b1) ? "M" : "")
+ $format ((misa.l == 1'b1) ? "L" : "")
+ $format ((misa.k == 1'b1) ? "K" : "")
+ $format ((misa.j == 1'b1) ? "J" : "")
+ $format ((misa.i == 1'b1) ? "I" : "")
+ $format ((misa.h == 1'b1) ? "H" : "")
+ $format ((misa.g == 1'b1) ? "G" : "")
+ $format ((misa.f == 1'b1) ? "F" : "")
+ $format ((misa.d == 1'b1) ? "E" : "")
+ $format ((misa.d == 1'b1) ? "D" : "")
+ $format ((misa.c == 1'b1) ? "C" : "")
+ $format ((misa.b == 1'b1) ? "B" : "")
+ $format ((misa.a == 1'b1) ? "A" : ""));
endfunction
endinstance
// ================================================================
// MSTATUS
Integer mstatus_sd_bitpos = xlen - 1;
Integer mstatus_sxl_bitpos = 34;
Integer mstatus_uxl_bitpos = 32;
Integer mstatus_tsr_bitpos = 22;
Integer mstatus_tw_bitpos = 21;
Integer mstatus_tvm_bitpos = 20;
Integer mstatus_mxr_bitpos = 19;
Integer mstatus_sum_bitpos = 18;
Integer mstatus_mprv_bitpos = 17;
Integer mstatus_xs_bitpos = 15;
Integer mstatus_fs_bitpos = 13;
Integer mstatus_mpp_bitpos = 11;
Integer mstatus_WPRI_9_bitpos = 9;
Integer mstatus_spp_bitpos = 8;
Integer mstatus_mpie_bitpos = 7;
Integer mstatus_WPRI_6_bitpos = 6;
Integer mstatus_spie_bitpos = 5;
Integer mstatus_upie_bitpos = 4;
Integer mstatus_mie_bitpos = 3;
Integer mstatus_WPRI_2_bitpos = 2;
Integer mstatus_sie_bitpos = 1;
Integer mstatus_uie_bitpos = 0;
// Values for FS and XS
Bit #(2) fs_xs_off = 2'h0;
Bit #(2) fs_xs_initial = 2'h1;
Bit #(2) fs_xs_clean = 2'h2;
Bit #(2) fs_xs_dirty = 2'h3;
// Extract MSTATUS.FS field
function Bit #(2) fv_mstatus_fs (WordXL mstatus);
return (fv_get_bits (mstatus, fromInteger (mstatus_fs_bitpos)));
endfunction
// Virtual field SD is computed from FS and XS
function Bit #(1) fv_mstatus_sd (WordXL mstatus);
Bit #(2) xs = fv_get_bits (mstatus, fromInteger (mstatus_xs_bitpos));
Bit #(2) fs = fv_get_bits (mstatus, fromInteger (mstatus_fs_bitpos));
return (((fs == fs_xs_dirty) || (xs == fs_xs_dirty)) ? 1 : 0);
endfunction
function Fmt fshow_mstatus (MISA misa, WordXL mstatus);
Bit #(2) sxl = ((misa.mxl == misa_mxl_64) ? fv_get_bits (mstatus, fromInteger (mstatus_sxl_bitpos)) : 0);
Bit #(2) uxl = ((misa.mxl == misa_mxl_64) ? fv_get_bits (mstatus, fromInteger (mstatus_uxl_bitpos)) : 0);
Bit #(2) xs = fv_get_bits (mstatus, fromInteger (mstatus_xs_bitpos));
Bit #(2) fs = fv_get_bits (mstatus, fromInteger (mstatus_fs_bitpos));
Bit #(2) mpp = fv_get_bits (mstatus, fromInteger (mstatus_mpp_bitpos));
return ( $format ("MStatus{")
+ $format ("sd:%0d", fv_mstatus_sd (mstatus))
+ ((misa.mxl == misa_mxl_64) ? $format (" sxl:%0d uxl:%0d", sxl, uxl) : $format (""))
+ $format (" tsr:%0d", mstatus [mstatus_tsr_bitpos])
+ $format (" tw:%0d", mstatus [mstatus_tw_bitpos])
+ $format (" tvm:%0d", mstatus [mstatus_tvm_bitpos])
+ $format (" mxr:%0d", mstatus [mstatus_mxr_bitpos])
+ $format (" sum:%0d", mstatus [mstatus_sum_bitpos])
+ $format (" mprv:%0d", mstatus [mstatus_mprv_bitpos])
+ $format (" xs:%0d", xs)
+ $format (" fs:%0d", fs)
+ $format (" mpp:%0d", mpp)
+ $format (" spp:%0d", mstatus [mstatus_spp_bitpos])
+ $format (" pies:%0d_%0d%0d",
mstatus [mstatus_mpie_bitpos], mstatus [mstatus_spie_bitpos], mstatus [mstatus_upie_bitpos])
+ $format (" ies:%0d_%0d%0d",
mstatus [mstatus_mie_bitpos], mstatus [mstatus_sie_bitpos], mstatus [mstatus_uie_bitpos])
+ $format ("}")
);
endfunction
// ----------------
// Help functions to manipulate mstatus on traps and trap-returns
function Priv_Mode fv_new_priv_on_exception (MISA misa,
Priv_Mode from_priv,
Bool interrupt,
Exc_Code exc_code,
Bit #(16) medeleg,
Bit #(12) mideleg,
Bit #(16) sedeleg,
Bit #(12) sideleg);
Priv_Mode to_priv = m_Priv_Mode;
Bit #(1) deleg_bit = 1'b0;
// If the current priv mode is M, it cannot be delegated.
if (from_priv < m_Priv_Mode) begin
// If S is supported
if (misa.s == 1'b1) begin
// Look in medeleg/mideleg for the cause bit; if set, delegate.
if (interrupt)
deleg_bit = mideleg [exc_code];
else
deleg_bit = medeleg [exc_code];
if (deleg_bit == 1'b1) begin
// If the current priv mode is S, then delegate to S.
to_priv = s_Priv_Mode;
// If the current priv mode is U, and user mode traps are supported,
// then consult sedeleg/sideleg to determine if delegated to U mode.
if ((from_priv == u_Priv_Mode) && (misa.n == 1'b1)) begin
if (interrupt)
deleg_bit = sideleg [exc_code];
else
deleg_bit = sedeleg [exc_code];
if (deleg_bit == 1'b1)
to_priv = u_Priv_Mode;
end
end
end
else begin
// S is not supported
// If user mode traps are supported,
// then consult medele/mideleg to determine if delegated to U mode.
if (misa.n == 1'b1) begin
// Look in medeleg/mideleg for the cause bit; if set, delegate.
if (interrupt)
deleg_bit = mideleg [exc_code];
else
deleg_bit = medeleg [exc_code];
if (deleg_bit == 1'b1)
to_priv = u_Priv_Mode;
end
end
end
return to_priv;
endfunction
function WordXL fv_new_mstatus_on_exception (WordXL mstatus, Priv_Mode from_y, Priv_Mode to_x);
Bit #(6) ie_to_x = extend (to_x);
Bit #(6) pie_to_x = fromInteger (mstatus_upie_bitpos) + extend (to_x);
// xPIE = xIE
mstatus = fv_assign_bit (mstatus, pie_to_x, mstatus [ie_to_x]);
// xIE = 0
mstatus = fv_assign_bit (mstatus, ie_to_x, 1'b0);
// xPP = y Assert: (to_x == m_Priv_Mode) || (to_x == s_Priv_Mode)
mstatus = ( (to_x == m_Priv_Mode)
? fv_assign_bits (mstatus, fromInteger (mstatus_mpp_bitpos), from_y)
: fv_assign_bit (mstatus, fromInteger (mstatus_spp_bitpos), from_y [0]));
return mstatus;
endfunction
function Tuple2 #(WordXL, Priv_Mode) fv_new_mstatus_on_ret (MISA misa,
WordXL mstatus,
Priv_Mode from_x);
Bit #(6) ie_from_x = extend (from_x);
Bit #(6) pie_from_x = fromInteger (mstatus_upie_bitpos) + extend (from_x);
// Pop the interrupt-enable stack
// (set xIE = xPIE)
mstatus = fv_assign_bit (mstatus, ie_from_x, mstatus [pie_from_x]);
// Enable interrupt at from_x
// (set xPIE = 1)
mstatus = fv_assign_bit (mstatus, pie_from_x, 1'b1);
// Pop the previous privilege mode
// which empties the one-element stack, revealing the default value
// (set xPP to U -- or M if U is not supported)
Priv_Mode to_y;
Priv_Mode default_pp = ((misa.u == 1'b1) ? u_Priv_Mode : m_Priv_Mode);
if (from_x == m_Priv_Mode) begin
to_y = fv_get_bits (mstatus, fromInteger (mstatus_mpp_bitpos));
mstatus = fv_assign_bits (mstatus, fromInteger (mstatus_mpp_bitpos), default_pp);
end
else begin //if (from_x == s_Priv_Mode)
to_y = {1'b0, mstatus [mstatus_spp_bitpos]};
mstatus = fv_assign_bit (mstatus, fromInteger (mstatus_spp_bitpos), default_pp [0]);
end
return tuple2 (mstatus, to_y);
endfunction
// ================================================================
// Logical view of csr_mtvec register
typedef enum {DIRECT, VECTORED} MTVEC_Mode
deriving (Bits, Eq, FShow);
typedef struct {
Bit #(XLEN_MINUS_2) base;
MTVEC_Mode mode;
} MTVec
deriving (Bits, FShow);
function WordXL mtvec_to_word (MTVec mv);
return {mv.base,
1'b0,
pack (mv.mode)};
endfunction
function MTVec word_to_mtvec (WordXL x);
return MTVec {base: truncate (x >> 2),
mode: unpack (x[0])};
endfunction
// ================================================================
// Logical view of csr_mcounteren register
typedef struct {
Bit#(1) ir;
Bit#(1) tm;
Bit#(1) cy;
} MCounteren
deriving (Bits, FShow);
function WordXL mcounteren_to_word (MCounteren mc);
return {0,
mc.ir,
mc.tm,
mc.cy};
endfunction
function MCounteren word_to_mcounteren (WordXL x);
return MCounteren {ir: x[2],
tm: x[1],
cy: x[0]};
endfunction
function MCounteren mcounteren_reset_value;
return MCounteren {ir: 1'b0,
tm: 1'b0,
cy: 1'b0};
endfunction
// ================================================================
// MIP and MIE fields (interrupt pending, interrupt enable)
Integer mip_usip_bitpos = 0;
Integer mip_ssip_bitpos = 1;
Integer mip_msip_bitpos = 3;
Integer mip_utip_bitpos = 4;
Integer mip_stip_bitpos = 5;
Integer mip_mtip_bitpos = 7;
Integer mip_ueip_bitpos = 8;
Integer mip_seip_bitpos = 9;
Integer mip_meip_bitpos = 11;
// ================================================================
// MCAUSE (reason for exception)
typedef struct {
Bit #(1) interrupt;
Exc_Code exc_code;
} MCause
deriving (Bits);
instance FShow #(MCause);
function Fmt fshow (MCause mc);
if (mc.interrupt == 1)
return fshow_interrupt_Exc_Code (mc.exc_code);
else
return fshow_trap_Exc_Code (mc.exc_code);
endfunction
endinstance
function WordXL mcause_to_word (MCause mc);
return {mc.interrupt, 0, mc.exc_code};
endfunction
function MCause word_to_mcause (WordXL x);
return MCause {interrupt: msb (x),
exc_code: truncate (x)};
endfunction
// Exception Codes in mcause
typedef Bit #(4) Exc_Code;
// When Interrupt = 1 (interrupt)
Exc_Code exc_code_USER_SW_INTERRUPT = 0;
Exc_Code exc_code_SUPERVISOR_SW_INTERRUPT = 1;
Exc_Code exc_code_HYPERVISOR_SW_INTERRUPT = 2;
Exc_Code exc_code_MACHINE_SW_INTERRUPT = 3;
Exc_Code exc_code_USER_TIMER_INTERRUPT = 4;
Exc_Code exc_code_SUPERVISOR_TIMER_INTERRUPT = 5;
Exc_Code exc_code_HYPERVISOR_TIMER_INTERRUPT = 6;
Exc_Code exc_code_MACHINE_TIMER_INTERRUPT = 7;
Exc_Code exc_code_USER_EXTERNAL_INTERRUPT = 8;
Exc_Code exc_code_SUPERVISOR_EXTERNAL_INTERRUPT = 9;
Exc_Code exc_code_HYPERVISOR_EXTERNAL_INTERRUPT = 10;
Exc_Code exc_code_MACHINE_EXTERNAL_INTERRUPT = 11;
// When Interrupt = 0 (trap)
Exc_Code exc_code_INSTR_ADDR_MISALIGNED = 0;
Exc_Code exc_code_INSTR_ACCESS_FAULT = 1;
Exc_Code exc_code_ILLEGAL_INSTRUCTION = 2;
Exc_Code exc_code_BREAKPOINT = 3;
Exc_Code exc_code_LOAD_ADDR_MISALIGNED = 4;
Exc_Code exc_code_LOAD_ACCESS_FAULT = 5;
Exc_Code exc_code_STORE_AMO_ADDR_MISALIGNED = 6;
Exc_Code exc_code_STORE_AMO_ACCESS_FAULT = 7;
Exc_Code exc_code_ECALL_FROM_U = 8;
Exc_Code exc_code_ECALL_FROM_S = 9;
Exc_Code exc_code_RESERVED_10 = 10;
Exc_Code exc_code_ECALL_FROM_M = 11;
Exc_Code exc_code_INSTR_PAGE_FAULT = 12;
Exc_Code exc_code_LOAD_PAGE_FAULT = 13;
Exc_Code exc_code_RESERVED_14 = 14;
Exc_Code exc_code_STORE_AMO_PAGE_FAULT = 15;
function Fmt fshow_interrupt_Exc_Code (Exc_Code exc_code);
return case (exc_code)
exc_code_USER_SW_INTERRUPT: $format ("USER_SW_INTERRUPT");
exc_code_SUPERVISOR_SW_INTERRUPT: $format ("SUPERVISOR_SW_INTERRUPT");
exc_code_HYPERVISOR_SW_INTERRUPT: $format ("HYPERVISOR_SW_INTERRUPT");
exc_code_MACHINE_SW_INTERRUPT: $format ("MACHINE_SW_INTERRUPT");
exc_code_USER_TIMER_INTERRUPT: $format ("USER_TIMER_INTERRUPT");
exc_code_SUPERVISOR_TIMER_INTERRUPT: $format ("SUPERVISOR_TIMER_INTERRUPT");
exc_code_HYPERVISOR_TIMER_INTERRUPT: $format ("HYPERVISOR_TIMER_INTERRUPT");
exc_code_MACHINE_TIMER_INTERRUPT: $format ("MACHINE_TIMER_INTERRUPT");
exc_code_USER_EXTERNAL_INTERRUPT: $format ("USER_EXTERNAL_INTERRUPT");
exc_code_SUPERVISOR_EXTERNAL_INTERRUPT: $format ("SUPERVISOR_EXTERNAL_INTERRUPT");
exc_code_HYPERVISOR_EXTERNAL_INTERRUPT: $format ("HYPERVISOR_EXTERNAL_INTERRUPT");
exc_code_MACHINE_EXTERNAL_INTERRUPT: $format ("MACHINE_EXTERNAL_INTERRUPT");
default: $format ("unknown interrupt Exc_Code %d", exc_code);
endcase;
endfunction
function Fmt fshow_trap_Exc_Code (Exc_Code exc_code);
return case (exc_code)
exc_code_INSTR_ADDR_MISALIGNED: $format ("INSTRUCTION_ADDR_MISALIGNED");
exc_code_INSTR_ACCESS_FAULT: $format ("INSTRUCTION_ACCESS_FAULT");
exc_code_ILLEGAL_INSTRUCTION: $format ("ILLEGAL_INSTRUCTION");
exc_code_BREAKPOINT: $format ("BREAKPOINT");
exc_code_LOAD_ADDR_MISALIGNED: $format ("LOAD_ADDR_MISALIGNED");
exc_code_LOAD_ACCESS_FAULT: $format ("LOAD_ACCESS_FAULT");
exc_code_STORE_AMO_ADDR_MISALIGNED: $format ("STORE_AMO_ADDR_MISALIGNED");
exc_code_STORE_AMO_ACCESS_FAULT: $format ("STORE_AMO_ACCESS_FAULT");
exc_code_ECALL_FROM_U: $format ("ECALL_FROM_U");
exc_code_ECALL_FROM_S: $format ("ECALL_FROM_S");
exc_code_ECALL_FROM_M: $format ("ECALL_FROM_M");
exc_code_INSTR_PAGE_FAULT: $format ("INSTRUCTION_PAGE_FAULT");
exc_code_LOAD_PAGE_FAULT: $format ("LOAD_PAGE_FAULT");
exc_code_STORE_AMO_PAGE_FAULT: $format ("STORE_AMO_PAGE_FAULT");
default: $format ("unknown trap Exc_Code %d", exc_code);
endcase;
endfunction
// ================================================================
// Function from various CSRs and current privilege to:
// whether or not an interrupt is pending,
// and if so, corresponding exception code
function Maybe #(Exc_Code) fv_interrupt_pending (MISA misa,
WordXL mstatus,
WordXL mip,
WordXL mie,
Bit #(12) mideleg,
Bit #(12) sideleg,
Priv_Mode cur_priv);
function Maybe #(Exc_Code) fv_interrupt_i_pending (Exc_Code i);
Bool intr_pending = ((mip [i] == 1) && (mie [i] == 1));
Priv_Mode handler_priv;
if (mideleg [i] == 1)
if (misa.u == 1)
if (misa.s == 1)
// System with M, S, U
if (sideleg [i] == 1)
if (misa.n == 1)
// M->S->U delegation
handler_priv = u_Priv_Mode;
else
// Error: SIDELEG [i] should not be 1 if MISA.N is 0
handler_priv = m_Priv_Mode;
else
// M->S delegation
handler_priv = s_Priv_Mode;
else
// System with M, U
if (misa.n == 1)
// M->U delegation
handler_priv = u_Priv_Mode;
else
// Error: MIDELEG [i] should not be 1 if MISA.N is 0
handler_priv = m_Priv_Mode;
else
// Error: System with M only; MIDELEG [i] should not be 1
handler_priv = m_Priv_Mode;
else
// no delegation
handler_priv = m_Priv_Mode;
Bool xie;
if (cur_priv == u_Priv_Mode)
xie = (mstatus [mstatus_uie_bitpos] == 1);
else if (cur_priv == s_Priv_Mode)
xie = (mstatus [mstatus_sie_bitpos] == 1);
else if (cur_priv == m_Priv_Mode)
xie = (mstatus [mstatus_mie_bitpos] == 1);
else
// Error: unexpected mode
xie = False;
Bool glob_enabled = ( (cur_priv < handler_priv)
|| ((cur_priv == handler_priv) && xie));
return ((intr_pending && glob_enabled) ? (tagged Valid i) : (tagged Invalid));
endfunction
// Check all interrupts in the following decreasing priority order
Maybe #(Exc_Code) m_ec;
m_ec = fv_interrupt_i_pending (exc_code_MACHINE_EXTERNAL_INTERRUPT);
if (m_ec matches tagged Invalid)
m_ec = fv_interrupt_i_pending (exc_code_MACHINE_SW_INTERRUPT);
if (m_ec matches tagged Invalid)
m_ec = fv_interrupt_i_pending (exc_code_MACHINE_TIMER_INTERRUPT);
if (m_ec matches tagged Invalid)
m_ec = fv_interrupt_i_pending (exc_code_SUPERVISOR_EXTERNAL_INTERRUPT);
if (m_ec matches tagged Invalid)
m_ec = fv_interrupt_i_pending (exc_code_SUPERVISOR_SW_INTERRUPT);
if (m_ec matches tagged Invalid)
m_ec = fv_interrupt_i_pending (exc_code_SUPERVISOR_TIMER_INTERRUPT);
if (m_ec matches tagged Invalid)
m_ec = fv_interrupt_i_pending (exc_code_USER_EXTERNAL_INTERRUPT);
if (m_ec matches tagged Invalid)
m_ec = fv_interrupt_i_pending (exc_code_USER_SW_INTERRUPT);
if (m_ec matches tagged Invalid)
m_ec = fv_interrupt_i_pending (exc_code_USER_TIMER_INTERRUPT);
return m_ec;
endfunction
// ================================================================

View File

@@ -0,0 +1,421 @@
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved
// ================================================================
// WARNING: this is an 'include' file, not a separate BSV package!
//
// Contains RISC-V Supervisor-Level ISA defs, based on:
// The RISC-V Instruction Set Manual"
// Volume II: Privileged Architecture
// Privileged Architecture Version 1.10
// Document Version 1.10
// May 7, 2017
//
// ================================================================
// Invariants on ifdefs:
// - If RV32 is defined, we assume Sv32 for the VM system
// - If RV64 is defined, one of SV39 or SV48 must also be defined for the VM system
// ================================================================
// Supervisor-level CSRs
CSR_Addr csr_addr_sstatus = 12'h100; // Supervisor status
CSR_Addr csr_addr_sedeleg = 12'h102; // Supervisor exception delegation
CSR_Addr csr_addr_sideleg = 12'h103; // Supervisor interrupt delegation
CSR_Addr csr_addr_sie = 12'h104; // Supervisor interrupt enable
CSR_Addr csr_addr_stvec = 12'h105; // Supervisor trap handler base address
CSR_Addr csr_addr_scounteren = 12'h106; // Supervisor counter enable
CSR_Addr csr_addr_sscratch = 12'h140; // Scratch reg for supervisor trap handlers
CSR_Addr csr_addr_sepc = 12'h141; // Supervisor exception program counter
CSR_Addr csr_addr_scause = 12'h142; // Supervisor trap cause
CSR_Addr csr_addr_stval = 12'h143; // Supervisor bad address or instruction
CSR_Addr csr_addr_sip = 12'h144; // Supervisor interrupt pending
CSR_Addr csr_addr_satp = 12'h180; // Supervisor address translation and protection
// ================================================================
// SSTATUS
function Bit #(1) fn_sstatus_sd (WordXL sstatus_val); return sstatus_val [xlen-1]; endfunction
`ifdef RV64
function Bit #(2) fn_sstatus_UXL (WordXL sstatus_val); return sstatus_val [33:32]; endfunction
`endif
function Bit #(1) fn_sstatus_SUM (WordXL sstatus_val); return sstatus_val [19]; endfunction
function Bit #(1) fn_sstatus_MXR (WordXL sstatus_val); return sstatus_val [18]; endfunction
function Bit #(2) fn_sstatus_xs (WordXL sstatus_val); return sstatus_val [16:15]; endfunction
function Bit #(2) fn_sstatus_fs (WordXL sstatus_val); return sstatus_val [14:13]; endfunction
function Bit #(1) fn_sstatus_spp (WordXL sstatus_val); return sstatus_val [8]; endfunction
function Bit #(1) fn_sstatus_spie (WordXL sstatus_val); return sstatus_val [5]; endfunction
function Bit #(1) fn_sstatus_upie (WordXL sstatus_val); return sstatus_val [4]; endfunction
function Bit #(1) fn_sstatus_sie (WordXL sstatus_val); return sstatus_val [1]; endfunction
function Bit #(1) fn_sstatus_uie (WordXL sstatus_val); return sstatus_val [0]; endfunction
// ----------------
// SCAUSE (reason for exception)
function Bit #(1) scause_interrupt (WordXL scause_val); return scause_val [xlen-1]; endfunction
function Bit #(TSub #(XLEN,1)) scause_exception_code (WordXL scause_val); return scause_val [xlen-2:0]; endfunction
// ================================================================
`ifdef ISA_PRIV_S
// ================================================================
// SATP (supervisor address translation and protection)
// ----------------
`ifdef RV32
typedef Bit #(1) VM_Mode;
typedef Bit #(9) ASID;
function WordXL fn_mk_satp_val (VM_Mode mode, ASID asid, PA pa) = { mode, asid, pa [33:12] };
function VM_Mode fn_satp_to_VM_Mode (Bit #(32) satp_val); return satp_val [31]; endfunction
function ASID fn_satp_to_ASID (Bit #(32) satp_val); return satp_val [30:22]; endfunction
function PPN fn_satp_to_PPN (Bit #(32) satp_val); return satp_val [21: 0]; endfunction
Bit #(1) satp_mode_RV32_bare = 1'h_0;
Bit #(1) satp_mode_RV32_sv32 = 1'h_1;
`elsif RV64
typedef Bit #(4) VM_Mode;
typedef Bit #(16) ASID;
function WordXL fn_mk_satp_val (VM_Mode mode, ASID asid, PA pa) = { mode, asid, pa [55:12] };
function VM_Mode fn_satp_to_VM_Mode (Bit #(64) satp_val); return satp_val [63:60]; endfunction
function ASID fn_satp_to_ASID (Bit #(64) satp_val); return satp_val [59:44]; endfunction
function PPN fn_satp_to_PPN (Bit #(64) satp_val); return satp_val [43: 0]; endfunction
Bit #(4) satp_mode_RV64_bare = 4'd__0;
Bit #(4) satp_mode_RV64_sv39 = 4'd__8;
Bit #(4) satp_mode_RV64_sv48 = 4'd__9;
Bit #(4) satp_mode_RV64_sv57 = 4'd_10;
Bit #(4) satp_mode_RV64_sv64 = 4'd_11;
`endif
// ----------------------------------------------------------------
// Virtual and Physical addresses, page numbers, offsets
// Page table (PT) fields and entries (PTEs)
// For Sv32 and Sv39
// ----------------
// RV32.Sv32
`ifdef RV32
// Virtual addrs
typedef 32 VA_sz;
typedef 20 VPN_sz;
typedef 10 VPN_J_sz;
// Physical addrs
typedef 34 PA_sz;
typedef 22 PPN_sz;
typedef 12 PPN_1_sz;
typedef 10 PPN_0_sz;
// Offsets within a page
typedef 12 Offset_sz;
// PTNodes (nodes in the page-table tree)
typedef 1024 PTNode_sz; // # of PTEs in a PTNode
// VAs, VPN selectors
function VA fn_mkVA (VPN_J vpn1, VPN_J vpn0, Bit #(Offset_sz) offset) = { vpn1, vpn0, offset };
function VPN fn_Addr_to_VPN (Bit #(n) addr) = addr [31:12];
function VPN_J fn_Addr_to_VPN_1 (Bit #(n) addr) = addr [31:22];
function VPN_J fn_Addr_to_VPN_0 (Bit #(n) addr) = addr [21:12];
// ----------------
// RV64.Sv39
// ifdef RV32
`elsif RV64
// ----------------
// RV64.Sv39
// ifdef RV32 .. elsif RV64
`ifdef SV39
// Virtual addrs
typedef 39 VA_sz;
typedef 27 VPN_sz;
typedef 9 VPN_J_sz;
// Physical addrs
typedef 64 PA_sz; // need 56b in Sv39 mode and 64b in Bare mode
typedef 44 PPN_sz;
typedef 26 PPN_2_sz;
typedef 9 PPN_1_sz;
typedef 9 PPN_0_sz;
// Offsets within a page
typedef 12 Offset_sz;
// PTNodes (nodes in the page-table tree)
typedef 512 PTNode_sz; // # of PTEs in a PTNode
// VAs, VPN selectors
function VA fn_mkVA (VPN_J vpn2, VPN_J vpn1, VPN_J vpn0, Bit #(Offset_sz) offset) = { vpn2, vpn1, vpn0, offset };
function VPN fn_Addr_to_VPN (Bit #(n) addr) = addr [38:12];
function VPN_J fn_Addr_to_VPN_2 (Bit #(n) addr) = addr [38:30];
function VPN_J fn_Addr_to_VPN_1 (Bit #(n) addr) = addr [29:21];
function VPN_J fn_Addr_to_VPN_0 (Bit #(n) addr) = addr [20:12];
// ifdef RV32 .. elsif RV64 / ifdef SV39
`else
// TODO: RV64.SV48 definitions
// ifdef RV32 .. elsif RV64 / ifdef SV39 .. else
`endif
// ifdef RV32 .. elsif RV64
`endif
// ----------------
// Derived types and values
// Physical addrs
Integer pa_sz = valueOf (PA_sz); typedef Bit #(PA_sz) PA;
function PA fn_WordXL_to_PA (WordXL eaddr);
`ifdef RV32
return extend (eaddr);
`elsif RV64
return truncate (eaddr);
`endif
endfunction
// Virtual addrs -- derived types and values
Integer va_sz = valueOf (VA_sz); typedef Bit #(VA_sz) VA;
function VA fn_WordXL_to_VA (WordXL eaddr);
`ifdef RV32
return eaddr;
`elsif RV64
return truncate (eaddr);
`endif
endfunction
// Page offsets
function Offset fn_Addr_to_Offset (Bit #(n) addr);
return addr [offset_sz - 1: 0];
endfunction
// VPNs
Integer vpn_sz = valueOf (VPN_sz); typedef Bit #(VPN_sz) VPN;
Integer vpn_j_sz = valueOf (VPN_J_sz); typedef Bit #(VPN_J_sz) VPN_J;
Integer offset_sz = valueOf (Offset_sz); typedef Bit #(Offset_sz) Offset;
// PPNs
Integer ppn_sz = valueOf (PPN_sz); typedef Bit #(PPN_sz) PPN;
`ifdef RV64
Integer ppn_2_sz = valueOf (PPN_2_sz); typedef Bit #(PPN_2_sz) PPN_2;
`endif
Integer ppn_1_sz = valueOf (PPN_1_sz); typedef Bit #(PPN_1_sz) PPN_1;
Integer ppn_0_sz = valueOf (PPN_0_sz); typedef Bit #(PPN_0_sz) PPN_0;
`ifdef RV32
typedef Bit #(PPN_1_sz) PPN_MEGA;
`elsif RV64
typedef Bit #(TAdd #(PPN_2_sz, PPN_1_sz)) PPN_MEGA;
typedef Bit #(PPN_2_sz) PPN_GIGA;
`endif
function PPN fn_PA_to_PPN (PA pa);
return pa [ppn_sz + offset_sz - 1: offset_sz];
endfunction
function PA fn_PPN_and_Offset_to_PA (PPN ppn, Offset offset);
`ifdef RV32
return {ppn, offset};
`elsif RV64
return zeroExtend ({ppn, offset});
`endif
endfunction
// ----------------
// PTNodes (nodes in the page-table tree)
Integer ptnode_sz = valueOf (PTNode_sz); // # of PTEs in a PTNode
typedef TLog #(PTNode_sz) PTNode_Index_sz;
typedef Bit #(PTNode_Index_sz) PTNode_Index;
Integer ptnode_index_sz = valueOf (PTNode_Index_sz);
// ----------------
// PTEs (Page Table Entries in PTNodes)
typedef WordXL PTE;
Integer pte_V_offset = 0; // Valid
Integer pte_R_offset = 1; // Read permission
Integer pte_W_offset = 2; // Write permission
Integer pte_X_offset = 3; // Execute permission
Integer pte_U_offset = 4; // Accessible-to-user-mode
Integer pte_G_offset = 5; // Global mapping
Integer pte_A_offset = 6; // Accessed
Integer pte_D_offset = 7; // Dirty
Integer pte_RSW_offset = 8; // Reserved for supervisor SW
`ifdef RV32
Integer pte_PPN_0_offset = 10;
Integer pte_PPN_1_offset = 20;
`elsif RV64
Integer pte_PPN_0_offset = 10;
Integer pte_PPN_1_offset = 19;
Integer pte_PPN_2_offset = 28;
`endif
function Bit #(1) fn_PTE_to_V (PTE pte);
return pte [pte_V_offset];
endfunction
function Bit #(1) fn_PTE_to_R (PTE pte);
return pte [pte_R_offset];
endfunction
function Bit #(1) fn_PTE_to_W (PTE pte);
return pte [pte_W_offset];
endfunction
function Bit #(1) fn_PTE_to_X (PTE pte);
return pte [pte_X_offset];
endfunction
function Bit #(1) fn_PTE_to_U (PTE pte);
return pte [pte_U_offset];
endfunction
function Bit #(1) fn_PTE_to_G (PTE pte);
return pte [pte_G_offset];
endfunction
function Bit #(1) fn_PTE_to_A (PTE pte);
return pte [pte_A_offset];
endfunction
function Bit #(1) fn_PTE_to_D (PTE pte);
return pte [pte_D_offset];
endfunction
function PPN fn_PTE_to_PPN (PTE pte);
return pte [ppn_sz + pte_PPN_0_offset - 1 : pte_PPN_0_offset];
endfunction
function PPN_MEGA fn_PTE_to_PPN_mega (PTE pte);
return pte [ppn_sz + pte_PPN_0_offset - 1 : pte_PPN_1_offset];
endfunction
`ifdef RV64
function PPN_GIGA fn_PTE_to_PPN_giga (PTE pte);
return pte [ppn_sz + pte_PPN_0_offset - 1 : pte_PPN_2_offset];
endfunction
`endif
function PPN_0 fn_PTE_to_PPN_0 (PTE pte);
return pte [pte_PPN_1_offset - 1 : pte_PPN_0_offset];
endfunction
function PPN_1 fn_PTE_to_PPN_1 (PTE pte);
return pte [ppn_1_sz + pte_PPN_1_offset - 1 : pte_PPN_1_offset];
endfunction
`ifdef RV64
function PPN_2 fn_PTE_to_PPN_2 (PTE pte);
return pte [ppn_2_sz + pte_PPN_2_offset - 1 : pte_PPN_2_offset];
endfunction
`endif
// ----------------
// Check if a PTE is invalid (V bit clear, or improper R/W bits)
function Bool is_invalid_pte (PTE pte);
return ( (fn_PTE_to_V (pte) == 0)
|| ( (fn_PTE_to_R (pte) == 0)
&& (fn_PTE_to_W (pte) == 1)));
endfunction
// ----------------
// Check if PTE bits deny a virtual-mem access
function Bool is_pte_denial (Bool dmem_not_imem, // load-store or fetch?
Bool read_not_write,
Priv_Mode priv,
Bit #(1) sstatus_SUM,
Bit #(1) mstatus_MXR,
PTE pte);
let pte_u = fn_PTE_to_U (pte);
let pte_x = fn_PTE_to_X (pte);
let pte_w = fn_PTE_to_W (pte);
let pte_r = fn_PTE_to_R (pte);
Bool priv_deny = ( ((priv == u_Priv_Mode) && (pte_u == 1'b0))
|| ((priv == s_Priv_Mode) && (pte_u == 1'b1) && (sstatus_SUM == 1'b0)));
Bool access_fetch = ((! dmem_not_imem) && read_not_write);
Bool access_load = (dmem_not_imem && read_not_write);
Bool access_store = (dmem_not_imem && (! read_not_write));
let pte_r_mxr = (pte_r | (mstatus_MXR & pte_x));
Bool access_ok = ( (access_fetch && (pte_x == 1'b1))
|| (access_load && (pte_r_mxr == 1'b1))
|| (access_store && (pte_w == 1'b1)));
return (priv_deny || (! access_ok));
endfunction
// ----------------
// Check PTE A and D bits
function Bool is_pte_A_D_fault (Bool read_not_write, PTE pte);
return ( (fn_PTE_to_A (pte) == 0)
|| ((! read_not_write) && (fn_PTE_to_D (pte) == 0)));
endfunction
// ----------------
// Choose particular kind of page fault
function Exc_Code fn_page_fault_exc_code (Bool dmem_not_imem, Bool read_not_write);
return ((! dmem_not_imem) ? exc_code_INSTR_PAGE_FAULT
:(read_not_write ? exc_code_LOAD_PAGE_FAULT
: exc_code_STORE_AMO_PAGE_FAULT));
endfunction
`else // ifdef ISA_PRIV_S
// The below definitions are valid for cases where there is no VM
// Physical addrs -- without VM, PA is same as WordXL
typedef XLEN PA_sz;
// Physical addrs
Integer pa_sz = valueOf (PA_sz); typedef Bit #(PA_sz) PA;
function PA fn_WordXL_to_PA (WordXL eaddr);
return eaddr;
endfunction
`endif // else-ifdef ISA_PRIV_S
// ----------------
// Choose particular kind of access fault
function Exc_Code fn_access_exc_code (Bool dmem_not_imem, Bool read_not_write);
return ((! dmem_not_imem) ? exc_code_INSTR_ACCESS_FAULT
:(read_not_write ? exc_code_LOAD_ACCESS_FAULT
: exc_code_STORE_AMO_ACCESS_FAULT));
endfunction
// ================================================================

362
src_Core/ISA/TV_Info.bsv Normal file
View File

@@ -0,0 +1,362 @@
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved
// ================================================================
// Definition of Tandem Verifier Packets.
// The CPU sends out such a packet for each instruction retired.
// A Tandem Verifier contains a "golden model" simulator of the RISC-V
// ISA, and verifies that the information in the packet is correct,
// instruction by instruction.
// ================================================================
package TV_Info;
// ================================================================
// Bluespec library imports
import DefaultValue :: *;
import Vector :: *;
// ================================================================
// Project imports
import ISA_Decls :: *;
// ================================================================
typedef enum {// These are not from instruction flow and do not have a PC or instruction
TRACE_RESET,
TRACE_GPR_WRITE,
TRACE_FPR_WRITE,
TRACE_CSR_WRITE,
TRACE_MEM_WRITE,
// These are from instruction flow and have a PC and instruction
TRACE_OTHER,
TRACE_I_RD, TRACE_F_RD,
TRACE_I_LOAD, TRACE_F_LOAD,
TRACE_STORE,
TRACE_AMO,
TRACE_TRAP,
TRACE_RET,
TRACE_CSRRX,
// These are from an interrupt and has a PC but no instruction
TRACE_INTR
} Trace_Op
deriving (Bits, Eq, FShow);
typedef struct {
Trace_Op op;
WordXL pc;
ISize instr_sz;
Bit #(32) instr;
RegName rd;
WordXL word1;
WordXL word2;
Bit #(64) word3; // Wider than WordXL because can contain paddr (in RV32, paddr can be 34 bits)
WordXL word4;
} Trace_Data
deriving (Bits);
// RESET
// op pc instr_sz instr rd word1 word2 word3 word4
// x
function Trace_Data mkTrace_RESET ();
Trace_Data td = ?;
td.op = TRACE_RESET;
return td;
endfunction
// GPR_WRITE
// op pc instr_sz instr rd word1 word2 word3 word4
// x x rdval
function Trace_Data mkTrace_GPR_WRITE (RegName rd, WordXL rdval);
Trace_Data td = ?;
td.op = TRACE_GPR_WRITE;
td.rd = rd;
td.word1 = rdval;
return td;
endfunction
// FPR_WRITE
// op pc instr_sz instr rd word1 word2 word3 word4
// x x rdval
function Trace_Data mkTrace_FPR_WRITE (RegName rd, WordXL rdval);
Trace_Data td = ?;
td.op = TRACE_FPR_WRITE;
td.rd = rd;
td.word1 = rdval;
return td;
endfunction
// CSR_WRITE
// op pc instr_sz instr rd word1 word2 word3 word4
// x csraddr csrval
function Trace_Data mkTrace_CSR_WRITE (CSR_Addr csraddr, WordXL csrval);
Trace_Data td = ?;
td.op = TRACE_CSR_WRITE;
td.word3 = zeroExtend (csraddr);
td.word4 = csrval;
return td;
endfunction
// MEM_WRITE
// op pc instr_sz instr rd word1 word2 word3 word4
// x sz stval paddr
function Trace_Data mkTrace_MEM_WRITE (MemReqSize sz, WordXL stval, Bit #(64) paddr);
Trace_Data td = ?;
td.op = TRACE_MEM_WRITE;
td.word1 = zeroExtend (sz);
td.word2 = stval;
td.word3 = paddr;
return td;
endfunction
// OTHER
// op pc instr_sz instr rd word1 word2 word3 word4
// x x x x
function Trace_Data mkTrace_OTHER (WordXL pc, ISize isize, Bit #(32) instr);
Trace_Data td = ?;
td.op = TRACE_OTHER;
td.pc = pc;
td.instr_sz = isize;
td.instr = instr;
return td;
endfunction
// I_RD
// op pc instr_sz instr rd word1 word2 word3 word4
// x x x x x rdval
function Trace_Data mkTrace_I_RD (WordXL pc, ISize isize, Bit #(32) instr, RegName rd, WordXL rdval);
Trace_Data td = ?;
td.op = TRACE_I_RD;
td.pc = pc;
td.instr_sz = isize;
td.instr = instr;
td.rd = rd;
td.word1 = rdval;
return td;
endfunction
// F_RD
// op pc instr_sz instr rd word1 word2 word3 word4
// x x x x x rdval
function Trace_Data mkTrace_F_RD (WordXL pc, ISize isize, Bit #(32) instr, RegName rd, WordXL rdval);
Trace_Data td = ?;
td.op = TRACE_F_RD;
td.pc = pc;
td.instr_sz = isize;
td.instr = instr;
td.rd = rd;
td.word1 = rdval;
return td;
endfunction
// I_LOAD
// op pc instr_sz instr rd word1 word2 word3 word4
// x x x x x rdval eaddr
function Trace_Data mkTrace_I_LOAD (WordXL pc, ISize isize, Bit #(32) instr, RegName rd, WordXL rdval, WordXL eaddr);
Trace_Data td = ?;
td.op = TRACE_I_LOAD;
td.pc = pc;
td.instr_sz = isize;
td.instr = instr;
td.rd = rd;
td.word1 = rdval;
td.word3 = zeroExtend (eaddr);
return td;
endfunction
// F_LOAD
// op pc instr_sz instr rd word1 word2 word3 word4
// x x x x x rdval eaddr
function Trace_Data mkTrace_F_LOAD (WordXL pc, ISize isize, Bit #(32) instr, RegName rd, WordXL rdval, WordXL eaddr);
Trace_Data td = ?;
td.op = TRACE_F_LOAD;
td.pc = pc;
td.instr_sz = isize;
td.instr = instr;
td.rd = rd;
td.word1 = rdval;
td.word3 = zeroExtend (eaddr);
return td;
endfunction
// STORE
// op pc instr_sz instr rd word1 word2 word3 word4
// x x x x stval eaddr
function Trace_Data mkTrace_STORE (WordXL pc, ISize isize, Bit #(32) instr, WordXL stval, WordXL eaddr);
Trace_Data td = ?;
td.op = TRACE_STORE;
td.pc = pc;
td.instr_sz = isize;
td.instr = instr;
td.word2 = stval;
td.word3 = zeroExtend (eaddr);
return td;
endfunction
// AMO
// op pc instr_sz instr rd word1 word2 word3 word4
// x x x x x rdval stval eaddr
function Trace_Data mkTrace_AMO (WordXL pc, ISize isize, Bit #(32) instr,
RegName rd, WordXL rdval, WordXL stval, WordXL eaddr);
Trace_Data td = ?;
td.op = TRACE_AMO;
td.pc = pc;
td.instr_sz = isize;
td.instr = instr;
td.rd = rd;
td.word1 = rdval;
td.word2 = stval;
td.word3 = zeroExtend (eaddr);
return td;
endfunction
// TRAP
// op pc instr_sz instr rd word1 word2 word3 word4
// x x x x priv mstatus mcause mepc mtval
function Trace_Data mkTrace_TRAP (WordXL pc, ISize isize, Bit #(32) instr,
Priv_Mode priv, WordXL mstatus, WordXL mcause, WordXL mepc, WordXL mtval);
Trace_Data td = ?;
td.op = TRACE_TRAP;
td.pc = pc;
td.instr_sz = isize;
td.instr = instr;
td.rd = zeroExtend (priv);
td.word1 = mstatus;
td.word2 = mcause;
td.word3 = zeroExtend (mepc);
td.word4 = mtval;
return td;
endfunction
// RET
// op pc instr_sz instr rd word1 word2 word3 word4
// x x x x priv mstatus
function Trace_Data mkTrace_RET (WordXL pc, ISize isize, Bit #(32) instr, Priv_Mode priv, WordXL mstatus);
Trace_Data td = ?;
td.op = TRACE_RET;
td.pc = pc;
td.instr_sz = isize;
td.instr = instr;
td.rd = zeroExtend (priv);
td.word1 = mstatus;
return td;
endfunction
// CSRRX
// op pc instr_sz instr rd word1 word2 word3 word4
// x x x x x rdval csrvalid csraddr csrval
function Trace_Data mkTrace_CSRRX (WordXL pc, ISize isize, Bit #(32) instr,
RegName rd, WordXL rdval, Bool csrvalid, CSR_Addr csraddr, WordXL csrval);
Trace_Data td = ?;
td.op = TRACE_CSRRX;
td.pc = pc;
td.instr_sz = isize;
td.instr = instr;
td.rd = rd;
td.word1 = rdval;
td.word2 = (csrvalid ? 1 : 0);
td.word3 = zeroExtend (csraddr);
td.word4 = csrval;
return td;
endfunction
// INTR
// op pc instr_sz instr rd word1 word2 word3 word4
// x x priv mstatus mcause mepc mtval
function Trace_Data mkTrace_INTR (WordXL pc,
Priv_Mode priv, WordXL mstatus, WordXL mcause, WordXL mepc, WordXL mtval);
Trace_Data td = ?;
td.op = TRACE_INTR;
td.pc = pc;
td.rd = zeroExtend (priv);
td.word1 = mstatus;
td.word2 = mcause;
td.word3 = zeroExtend (mepc);
td.word4 = mtval;
return td;
endfunction
// ================================================================
// Display of Trace_Data for debugging
instance FShow #(Trace_Data);
function Fmt fshow (Trace_Data td);
Fmt fmt = $format ("Trace_Data{", fshow (td.op));
if (td.op == TRACE_RESET) begin
end
else if ((td.op == TRACE_GPR_WRITE) || (td.op == TRACE_FPR_WRITE))
fmt = fmt + $format (" rd %0d rdval %0h", td.rd, td.word1);
else if (td.op == TRACE_CSR_WRITE)
fmt = fmt + $format (" csraddr %0h csrval %0h", td.word3, td.word4);
else if (td.op == TRACE_MEM_WRITE)
fmt = fmt + $format (" sz %0d stval %0h paddr %0h", td.word1, td.word2, td.word3);
else begin
fmt = fmt + $format (" pc %0h", td.pc);
if (td.op != TRACE_INTR)
fmt = fmt + $format (" instr.%0d %0h:", pack (td.instr_sz), td.instr);
if ((td.op == TRACE_I_RD) || (td.op == TRACE_F_RD))
fmt = fmt + $format (" rd %0d rdval %0h", td.rd, td.word1);
else if ((td.op == TRACE_I_LOAD) || (td.op == TRACE_F_LOAD))
fmt = fmt + $format (" rd %0d rdval %0h eaddr %0h",
td.rd, td.word1, td.word3);
else if (td.op == TRACE_STORE)
fmt = fmt + $format (" stval %0h eaddr %0h", td.word2, td.word3);
else if (td.op == TRACE_AMO)
fmt = fmt + $format (" rd %0d rdval %0h stval %0h eaddr %0h",
td.rd, td.word1, td.word2, td.word3);
else if (td.op == TRACE_CSRRX)
fmt = fmt + $format (" rd %0d rdval %0h csraddr %0h csrval %0h",
td.rd, td.word1, td.word3, td.word4);
else if ((td.op == TRACE_TRAP) || (td.op == TRACE_INTR))
fmt = fmt + $format (" priv %0d mstatus %0h mcause %0h mepc %0h mtval %0h",
td.rd, td.word1, td.word2, td.word3, td.word4);
else if (td.op == TRACE_RET)
fmt = fmt + $format (" priv %0d mstatus %0h", td.rd, td.word1);
end
fmt = fmt + $format ("}");
return fmt;
endfunction
endinstance
// ================================================================
// Trace_Data is encoded in module mkTV_Encode into vectors of bytes,
// which are eventually streamed out to an on-line tandem verifier/
// analyzer (or to a file for off-line tandem-verification/analysis).
// Various 'transactions' produce a Trace_Data struct (e.g., reset,
// each instruction retirement, each GDB write to registers or memory,
// etc.). Each struct is encoded into a vector of bytes; the number
// of bytes depends on the kind of transaction and various encoding
// choices.
typedef 72 TV_VB_SIZE; // max bytes needed for each transaction
typedef Vector #(TV_VB_SIZE, Byte) TV_Vec_Bytes;
// ================================================================
typedef struct {
Bit #(32) num_bytes;
TV_Vec_Bytes vec_bytes;
} Info_CPU_to_Verifier deriving (Bits, FShow);
// ================================================================
endpackage

41
src_Core/PLIC/Makefile Normal file
View File

@@ -0,0 +1,41 @@
# Copyright (c) 2019 Bluespec, Inc. All Rights Reserved
# Makefile to create Test_PLIC and PLIC into a standalone unit-test executable.
# Edit the StmtFSM in Test_PLIC to create different tests.
# ----------------
REPO = $(HOME)/GitHub/Flute
# ----------------
# Top-level file and module
TOPFILE ?= $(REPO)/src_Core/PLIC/Test_PLIC.bsv
TOPMODULE ?= mkTest_PLIC
# ================================================================
# RISC-V config macros passed into Bluespec 'bsc' compiler
BSC_COMPILATION_FLAGS += \
-D RV64 \
-D ISA_PRIV_M -D ISA_PRIV_U -D ISA_PRIV_S \
-D SV39 \
-D ISA_I -D ISA_M -D ISA_A \
-D SHIFT_BARREL \
-D MULT_SYNTH \
-D Near_Mem_Caches \
-D FABRIC64 \
# ================================================================
# Common boilerplate rules
include $(REPO)/builds/Resources/Include_Common.mk
# ================================================================
# Makefile rules for building for specific simulator: bluesim
include $(REPO)/builds/Resources/Include_bluesim.mk
# ================================================================

623
src_Core/PLIC/PLIC.bsv Normal file
View File

@@ -0,0 +1,623 @@
// Copyright (c) 2019 Bluespec, Inc. All Rights Reserved
package PLIC;
// ================================================================
// This package implements a PLIC (Platform-Level Interrupt Controller)
// conforming to the RISC-V PLIC standard.
// It is parameterized for:
// - # of sources
// - # of targets
// - # of priorities
//
// ================================================================
// Bluespec lib imports
import ConfigReg :: *;
import Vector :: *;
import FIFOF :: *;
import ClientServer :: *;
import Assert :: *;
// ----------------
// BSV additional libs
import Cur_Cycle :: *;
import GetPut_Aux :: *;
import Semi_FIFOF :: *;
// ================================================================
// Project imports
import AXI4_Types :: *;
import AXI4_Fabric :: *;
import Fabric_Defs :: *; // for Wd_Id, Wd_Addr, Wd_Data, Wd_User
// ================================================================
// Change bitwidth without requiring < or > constraints.
function Bit #(m) changeWidth (Bit #(n) x);
Bit #(TAdd #(m, n)) y = zeroExtend (x);
Bit #(m) z = y [valueOf (m)-1 : 0];
return z;
endfunction
// ================================================================
// Maximum supported sources, targets, ...
typedef 10 T_wd_source_id; // Max 1024 sources (source 0 is reserved for 'no interrupt')
typedef 5 T_wd_target_id; // Max 32 targets
// ================================================================
// Interfaces
// ----------------
// Individual source interface
interface PLIC_Source_IFC;
(* always_ready, always_enabled *)
method Action m_interrupt_req (Bool set_not_clear);
endinterface
// ----------------
// Individual target interface
interface PLIC_Target_IFC;
(* always_ready *)
method Bool m_eip; // external interrupt pending
endinterface
// ----------------
// PLIC interface
interface PLIC_IFC #(numeric type t_n_external_sources,
numeric type t_n_targets,
numeric type t_max_priority);
// Debugging
method Action set_verbosity (Bit #(4) verbosity);
method Action show_PLIC_state;
// Reset
interface Server #(Bit #(0), Bit #(0)) server_reset;
// set_addr_map should be called after this module's reset
method Action set_addr_map (Bit #(64) addr_base, Bit #(64) addr_lim);
// Memory-mapped access
interface AXI4_Slave_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) axi4_slave;
// sources
interface Vector #(t_n_external_sources, PLIC_Source_IFC) v_sources;
// targets EIPs (External Interrupt Pending)
interface Vector #(t_n_targets, PLIC_Target_IFC) v_targets;
endinterface
// ================================================================
// PLIC module implementation
module mkPLIC (PLIC_IFC #(t_n_external_sources, t_n_targets, t_max_priority))
provisos (Add #(1, t_n_external_sources, t_n_sources), // source 0 is reserved for 'no source'
Add #(_any_0, TLog #(t_n_sources), T_wd_source_id),
Add #(_any_1, TLog #(t_n_targets), T_wd_target_id),
Log #(TAdd #(t_max_priority, 1), t_wd_priority));
Reg #(Bit #(4)) cfg_verbosity <- mkConfigReg (0);
// Source_Ids and Priorities are read and written over the memory interface
// and should fit within the data bus width, currently 64 bits.
staticAssert ((valueOf (TLog #(t_n_sources)) <= 64), "PLIC: t_n_sources parameter too large");
staticAssert ((valueOf (TLog #(TAdd #(t_max_priority, 1))) <= 64), "PLIC: t_max_priority parameter too large");
Integer n_sources = valueOf (t_n_sources);
Integer n_targets = valueOf (t_n_targets);
// ----------------
// Soft reset requests and responses
FIFOF #(Bit #(0)) f_reset_reqs <- mkFIFOF;
FIFOF #(Bit #(0)) f_reset_rsps <- mkFIFOF;
// ----------------
// Memory-mapped access
// Base and limit addrs for this memory-mapped block.
Reg #(Bit #(64)) rg_addr_base <- mkRegU;
Reg #(Bit #(64)) rg_addr_lim <- mkRegU;
// Connector to AXI4 fabric
AXI4_Slave_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) slave_xactor <- mkAXI4_Slave_Xactor;
// ----------------
// Per-interrupt source state
// Interrupt pending from source
Vector #(t_n_sources, Reg #(Bool)) vrg_source_ip <- replicateM (mkConfigReg (False));
// Interrupt claimed and being serviced by a hart
Vector #(t_n_sources, Reg #(Bool)) vrg_source_busy <- replicateM (mkReg (False));
// Priority for this source
Vector #(t_n_sources, Reg #(Bit #(t_wd_priority))) vrg_source_prio <- replicateM (mkReg (0));
// ----------------
// Per-target hart context state
// Threshold: interrupts at or below threshold should be masked out for target
Vector #(t_n_targets, Reg #(Bit #(t_wd_priority))) vrg_target_threshold <- replicateM (mkReg ('1));
// Target has claimed interrupt for source and is servicing it
Vector #(t_n_targets, Reg #(Bit #(TLog #(t_n_sources)))) vrg_servicing_source <- replicateM (mkReg (0));
// ----------------
// Per-target, per-source state
// Interrupt enables from source to target
Vector #(t_n_targets,
Vector #(t_n_sources, Reg #(Bool))) vvrg_ie <- replicateM (replicateM (mkReg (False)));
// ================================================================
// Compute outputs for each target (combinational)
function Tuple2 #(Bit #(t_wd_priority), Bit #(TLog #(t_n_sources)))
fn_target_max_prio_and_max_id (Bit #(T_wd_target_id) target_id);
Bit #(t_wd_priority) max_prio = 0;
Bit #(TLog #(t_n_sources)) max_id = 0;
// Note: source_ids begin at 1, not 0.
for (Integer source_id = 1; source_id < n_sources; source_id = source_id + 1)
if ( vrg_source_ip [source_id]
&& (vrg_source_prio [source_id] > max_prio)
&& (vvrg_ie [target_id][source_id])) begin
max_id = fromInteger (source_id);
max_prio = vrg_source_prio [source_id];
end
// Assert: if any interrupt is pending (max_id > 0), then prio > 0
return tuple2 (max_prio, max_id);
endfunction
function Action fa_show_PLIC_state;
action
$display ("----------------");
$write ("Src IPs :");
for (Integer source_id = 0; source_id < n_sources; source_id = source_id + 1)
$write (" %0d", pack (vrg_source_ip [source_id]));
$display ("");
$write ("Src Prios:");
for (Integer source_id = 0; source_id < n_sources; source_id = source_id + 1)
$write (" %0d", vrg_source_prio [source_id]);
$display ("");
$write ("Src busy :");
for (Integer source_id = 0; source_id < n_sources; source_id = source_id + 1)
$write (" %0d", pack (vrg_source_busy [source_id]));
$display ("");
for (Integer target_id = 0; target_id < n_targets; target_id = target_id + 1) begin
$write ("T %0d IEs :", target_id);
for (Integer source_id = 0; source_id < n_sources; source_id = source_id + 1)
$write (" %0d", vvrg_ie [target_id][source_id]);
match { .max_prio, .max_id } = fn_target_max_prio_and_max_id (fromInteger (target_id));
$display (" MaxPri %0d, Thresh %0d, MaxId %0d, Svcing %0d",
max_prio, vrg_target_threshold [target_id], max_id, vrg_servicing_source [target_id]);
end
endaction
endfunction
// ================================================================
// Soft reset
rule rl_reset;
if (cfg_verbosity > 0)
$display ("%0d: PLIC.rl_reset", cur_cycle);
let x <- pop (f_reset_reqs);
for (Integer source_id = 0; source_id < n_sources; source_id = source_id + 1) begin
vrg_source_ip [source_id] <= False;
vrg_source_busy [source_id] <= False;
vrg_source_prio [source_id] <= 0;
end
for (Integer target_id = 0; target_id < n_targets; target_id = target_id + 1) begin
// Mask all interrupts with highest threshold
vrg_target_threshold [target_id] <= '1;
vrg_servicing_source [target_id] <= 0;
end
for (Integer target_id = 0; target_id < n_targets; target_id = target_id + 1)
for (Integer source_id = 0; source_id < n_sources; source_id = source_id + 1)
vvrg_ie [target_id][source_id] <= False;
slave_xactor.reset;
f_reset_rsps.enq (?);
endrule
// ================================================================
// Bus interface for reading/writing control/status regs
// Relative-address map is same as 'SiFive U54-MC Core Complex Manual v1p0'.
// Accesses are 4-bytes wide, even though bus may be 64b wide.
// ----------------------------------------------------------------
// Handle memory-mapped read requests
rule rl_process_rd_req (! f_reset_reqs.notEmpty);
let rda <- pop_o (slave_xactor.o_rd_addr);
if (cfg_verbosity > 1) begin
$display ("%0d: PLIC.rl_process_rd_req:", cur_cycle);
$display (" ", fshow (rda));
end
let addr_offset = rda.araddr - rg_addr_base;
Bit #(64) rdata = 0;
AXI4_Resp rresp = axi4_resp_okay;
if (rda.araddr < rg_addr_base) begin
// Technically this should not happen: the fabric should
// never have delivered such an addr to this IP.
$display ("%0d: ERROR: PLIC.rl_process_rd_req: unrecognized addr", cur_cycle);
$display (" ", fshow (rda));
rresp = axi4_resp_decerr;
end
// Source Priority
else if (addr_offset < 'h1000) begin
Bit #(T_wd_source_id) source_id = truncate (addr_offset [11:2]);
if ((0 < source_id) && (source_id <= fromInteger (n_sources - 1))) begin
rdata = changeWidth (vrg_source_prio [source_id]);
if (cfg_verbosity > 0)
$display ("%0d: PLIC.rl_process_rd_req: reading Source Priority: source %0d = 0x%0h",
cur_cycle, source_id, rdata);
end
else
rresp = axi4_resp_slverr;
end
// Source IPs (interrupt pending).
// Return 32 consecutive IP bits starting with addr.
else if (('h1000 <= addr_offset) && (addr_offset < 'h2000)) begin
Bit #(T_wd_source_id) source_id_base = truncate ({ addr_offset [11:0], 5'h0 });
function Bool fn_ip_source_id (Integer source_id_offset);
let source_id = source_id_base + fromInteger (source_id_offset);
Bool ip_source_id = ( (source_id <= fromInteger (n_sources - 1))
? vrg_source_ip [source_id]
: False);
return ip_source_id;
endfunction
if (source_id_base <= fromInteger (n_sources - 1)) begin
Bit #(32) v_ip = pack (genWith (fn_ip_source_id));
rdata = changeWidth (v_ip);
if (cfg_verbosity > 0)
$display ("%0d: PLIC.rl_process_rd_req: reading Intr Pending 32 bits from source %0d = 0x%0h",
cur_cycle, source_id_base, rdata);
end
else
rresp = axi4_resp_slverr;
end
// Source IEs (interrupt enables) for a target
// Return 32 consecutive IE bits starting with addr.
// Target 0 addrs: 2000-207F, Target 1 addrs: 2080-20FF, ...
else if (('h2000 <= addr_offset) && (addr_offset < 'h3000)) begin
Bit #(T_wd_target_id) target_id = truncate (addr_offset [11:7]);
Bit #(T_wd_source_id) source_id_base = truncate ({ addr_offset [6:0], 5'h0 });
function Bool fn_ie_source_id (Integer source_id_offset);
let source_id = fromInteger (source_id_offset) + source_id_base;
return ( (source_id <= fromInteger (n_sources - 1))
? vvrg_ie [target_id][source_id]
: False);
endfunction
if ( (source_id_base <= fromInteger (n_sources - 1))
&& (target_id <= fromInteger (n_targets - 1))) begin
Bit #(32) v_ie = pack (genWith (fn_ie_source_id));
rdata = changeWidth (v_ie);
if (cfg_verbosity > 0)
$display ("%0d: PLIC.rl_process_rd_req: reading Intr Enable 32 bits from source %0d = 0x%0h",
cur_cycle, source_id_base, rdata);
end
else
rresp = axi4_resp_slverr;
end
// Target threshold
else if ((addr_offset [31:0] & 32'hFFFF_0FFF) == 32'h0020_0000) begin
Bit #(T_wd_target_id) target_id = truncate (addr_offset [20:12]);
if (target_id <= fromInteger (n_targets - 1)) begin
rdata = changeWidth (vrg_target_threshold [target_id]);
if (cfg_verbosity > 0)
$display ("%0d: PLIC.rl_process_rd_req: reading Threshold for target %0d = 0x%0h",
cur_cycle, target_id, rdata);
end
else
rresp = axi4_resp_slverr;
end
// Interrupt service claim by target
else if ((addr_offset [31:0] & 32'hFFFF_0FFF) == 32'h0020_0004) begin
Bit #(T_wd_target_id) target_id = truncate (addr_offset [20:12]);
match { .max_prio, .max_id } = fn_target_max_prio_and_max_id (target_id);
Bool eip = (max_prio > vrg_target_threshold [target_id]);
if (target_id <= fromInteger (n_targets - 1)) begin
if (vrg_servicing_source [target_id] != 0) begin
$display ("%0d: ERROR: PLIC: target %0d claiming without prior completion",
cur_cycle, target_id);
$display (" Still servicing interrupt from source %0d", vrg_servicing_source [target_id]);
$display (" Trying to claim service for source %0d", max_id);
$display (" Ignoring.");
rresp = axi4_resp_slverr;
end
else begin
if (max_id != 0) begin
vrg_source_ip [max_id] <= False;
vrg_source_busy [max_id] <= True;
vrg_servicing_source [target_id] <= truncate (max_id);
rdata = changeWidth (max_id);
if (cfg_verbosity > 0)
$display ("%0d: PLIC.rl_process_rd_req: reading Claim for target %0d = 0x%0h",
cur_cycle, target_id, rdata);
end
end
end
else
rresp = axi4_resp_slverr;
end
else
rresp = axi4_resp_slverr;
if (rresp != axi4_resp_okay) begin
$display ("%0d: ERROR: PLIC.rl_process_rd_req: unrecognized addr", cur_cycle);
$display (" ", fshow (rda));
end
if ((valueOf (Wd_Data) == 64) && ((addr_offset & 'h7) == 'h4))
rdata = { rdata [31:0], 32'h0 };
// Send read-response to bus
Fabric_Data x = truncate (rdata);
let rdr = AXI4_Rd_Data {rid: rda.arid,
rdata: x,
rresp: rresp,
rlast: True,
ruser: rda.aruser};
slave_xactor.i_rd_data.enq (rdr);
if (cfg_verbosity > 1) begin
$display ("%0d: PLIC.rl_process_rd_req", cur_cycle);
$display (" ", fshow (rda));
$display (" ", fshow (rdr));
end
endrule: rl_process_rd_req
// ----------------------------------------------------------------
// Handle memory-mapped write requests
(* descending_urgency = "rl_process_rd_req, rl_process_wr_req" *) // Ad hoc order
rule rl_process_wr_req (! f_reset_reqs.notEmpty);
let wra <- pop_o (slave_xactor.o_wr_addr);
let wrd <- pop_o (slave_xactor.o_wr_data);
if (cfg_verbosity > 1) begin
$display ("%0d: PLIC.rl_process_wr_req", cur_cycle);
$display (" ", fshow (wra));
$display (" ", fshow (wrd));
end
let addr_offset = wra.awaddr - rg_addr_base;
let wdata32 = (((valueOf (Wd_Data) == 64) && ((addr_offset & 'h7) == 'h4))
? wrd.wdata [63:32]
: wrd.wdata [31:0]);
let bresp = axi4_resp_okay;
if (wra.awaddr < rg_addr_base) begin
// Technically this should not happen: the fabric should
// never have delivered such an addr to this IP.
$display ("%0d: ERROR: PLIC.rl_process_wr_req: unrecognized addr", cur_cycle);
$display (" ", fshow (wra));
$display (" ", fshow (wrd));
bresp = axi4_resp_decerr;
end
// Source priority
else if (addr_offset < 'h1000) begin
Bit #(T_wd_source_id) source_id = truncate (addr_offset [11:2]);
if ((0 < source_id) && (source_id <= fromInteger (n_sources - 1))) begin
vrg_source_prio [source_id] <= changeWidth (wdata32);
if (cfg_verbosity > 0)
$display ("%0d: PLIC.rl_process_wr_req: writing Source Priority: source %0d = 0x%0h",
cur_cycle, source_id, wdata32);
end
else begin
// Note: write to source_id 0 is error; should it just be ignored?
bresp = axi4_resp_slverr;
end
end
// Source IPs (interrupt pending).
// Read-only, so ignore write; just check that addr ok.
else if (('h1000 <= addr_offset) && (addr_offset < 'h2000)) begin
Bit #(T_wd_source_id) source_id_base = truncate ({ addr_offset [11:0], 5'h0 });
if (source_id_base <= fromInteger (n_sources - 1)) begin
if (cfg_verbosity > 0)
$display ("%0d: PLIC.rl_process_wr_req: Ignoring write to Read-only Intr Pending 32 bits from source %0d",
cur_cycle, source_id_base);
end
else
bresp = axi4_resp_slverr;
end
// Source IEs (interrupt enables) for a target
// Write 32 consecutive IE bits starting with addr.
// Target 0 addrs: 2000-207F, Target 1 addrs: 2080-20FF, ...
else if (('h2000 <= addr_offset) && (addr_offset < 'h3000)) begin
Bit #(T_wd_target_id) target_id = truncate (addr_offset [11:7]);
Bit #(T_wd_source_id) source_id_base = truncate ({ addr_offset [6:0], 5'h0 });
if ( (source_id_base <= fromInteger (n_sources - 1))
&& (target_id <= fromInteger (n_targets - 1))) begin
for (Bit #(T_wd_source_id) k = 0; k < 32; k = k + 1) begin
Bit #(T_wd_source_id) source_id = source_id_base + k;
if (source_id <= fromInteger (n_sources - 1))
vvrg_ie [target_id][source_id] <= unpack (wdata32 [k]);
end
if (cfg_verbosity > 0)
$display ("%0d: PLIC.rl_process_wr_req: writing Intr Enable 32 bits for target %0d from source %0d = 0x%0h",
cur_cycle, target_id, source_id_base, wdata32);
end
else
bresp = axi4_resp_slverr;
end
// Target threshold
else if ((addr_offset [31:0] & 32'hFFFF_0FFF) == 32'h0020_0000) begin
Bit #(T_wd_target_id) target_id = truncate (addr_offset [20:12]);
if (target_id <= fromInteger (n_targets - 1)) begin
vrg_target_threshold [target_id] <= changeWidth (wdata32);
if (cfg_verbosity > 0)
$display ("%0d: PLIC.rl_process_wr_req: writing threshold for target %0d = 0x%0h",
cur_cycle, target_id, wdata32);
end
else
bresp = axi4_resp_slverr;
end
// Interrupt service completion by target
// Actual memory-write-data is irrelevant.
else if ((addr_offset [31:0] & 32'hFFFF_0FFF) == 32'h0020_0004) begin
Bit #(T_wd_target_id) target_id = truncate (addr_offset [20:12]);
Bit #(T_wd_source_id) source_id = zeroExtend (vrg_servicing_source [target_id]);
if (target_id <= fromInteger (n_targets - 1)) begin
if (vrg_source_busy [source_id]) begin
vrg_source_busy [source_id] <= False;
vrg_servicing_source [target_id] <= 0;
if (cfg_verbosity > 0)
$display ("%0d: PLIC.rl_process_wr_req: writing completion for target %0d for source 0x%0h",
cur_cycle, target_id, source_id);
end
else begin
$display ("%0d: ERROR: PLIC: interrupt completion to source that is not being serviced",
cur_cycle);
$display (" Completion message from target %0d to source %0d", target_id, source_id);
$display (" Ignoring");
bresp = axi4_resp_slverr;
end
end
else
bresp = axi4_resp_slverr;
end
else
bresp = axi4_resp_slverr;
if (bresp != axi4_resp_okay) begin
$display ("%0d: ERROR: PLIC.rl_process_wr_req: unrecognized addr", cur_cycle);
$display (" ", fshow (wra));
$display (" ", fshow (wrd));
end
// Send write-response to bus
let wrr = AXI4_Wr_Resp {bid: wra.awid,
bresp: bresp,
buser: wra.awuser};
slave_xactor.i_wr_resp.enq (wrr);
if (cfg_verbosity > 1) begin
$display ("%0d: PLIC.AXI4.rl_process_wr_req", cur_cycle);
$display (" ", fshow (wra));
$display (" ", fshow (wrd));
$display (" ", fshow (wrr));
end
endrule: rl_process_wr_req
// ================================================================
// Creator of each source interface
function PLIC_Source_IFC fn_mk_PLIC_Source_IFC (Integer source_id);
return interface PLIC_Source_IFC;
method Action m_interrupt_req (Bool set_not_clear);
action
if (! vrg_source_busy [source_id]) begin
vrg_source_ip [source_id] <= set_not_clear;
if ((cfg_verbosity > 0) && (vrg_source_ip [source_id] != set_not_clear))
$display ("%0d: Changing vrg_source_ip [%0d] to %0d",
cur_cycle, source_id, pack (set_not_clear));
end
endaction
endmethod
endinterface;
endfunction
// ================================================================
// Creator of each target interface
function PLIC_Target_IFC fn_mk_PLIC_Target_IFC (Integer target_id);
return interface PLIC_Target_IFC;
method Bool m_eip; // external interrupt pending
match { .max_prio, .max_id } = fn_target_max_prio_and_max_id (fromInteger (target_id));
Bool eip = (max_prio > vrg_target_threshold [target_id]);
return eip;
endmethod
endinterface;
endfunction
// ================================================================
// INTERFACE
// Debugging
method Action set_verbosity (Bit #(4) verbosity);
cfg_verbosity <= verbosity;
endmethod
method Action show_PLIC_state;
fa_show_PLIC_state;
endmethod
// Reset
interface server_reset = toGPServer (f_reset_reqs, f_reset_rsps);
// set_addr_map should be called after this module's reset
method Action set_addr_map (Bit #(64) addr_base, Bit #(64) addr_lim);
if (addr_base [1:0] != 0)
$display ("%0d: WARNING: PLIC.set_addr_map: addr_base 0x%0h is not 4-Byte-aligned",
cur_cycle, addr_base);
if (addr_lim [1:0] != 0)
$display ("%0d: WARNING: PLIC.set_addr_map: addr_lim 0x%0h is not 4-Byte-aligned",
cur_cycle, addr_lim);
rg_addr_base <= addr_base;
rg_addr_lim <= addr_lim;
if (cfg_verbosity > 0)
$display ("%0d: PLIC.set_addr_map: base 0x%0h limit 0x%0h", cur_cycle, addr_base, addr_lim);
endmethod
// Memory-mapped access
interface axi4_slave = slave_xactor.axi_side;
// sources
interface v_sources = genWith (fn_mk_PLIC_Source_IFC);
// targets
interface v_targets = genWith (fn_mk_PLIC_Target_IFC);
endmodule
// ================================================================
endpackage

View File

@@ -0,0 +1,42 @@
// Copyright (c) 2019 Bluespec, Inc. All Rights Reserved
package PLIC_16_2_7;
// ================================================================
// Instantiation of parameterized PLIC to specific parameter values.
//
// ================================================================
// Bluespec lib imports
// None
// ----------------
// BSV additional libs
// None
// ================================================================
// Project imports
import SoC_Map :: *; // For N_External_Interrupt_Sources
import PLIC :: *; // For PLIC_IFC, mkPLIC
// ================================================================
// PLIC for this core
typedef 2 PLIC_N_Targets;
typedef 7 PLIC_Max_Priority;
typedef PLIC_IFC #(N_External_Interrupt_Sources,
PLIC_N_Targets,
PLIC_Max_Priority) PLIC_IFC_16_2_7;
(* synthesize *)
module mkPLIC_16_2_7 (PLIC_IFC_16_2_7);
let m <- mkPLIC;
return m;
endmodule
// ================================================================
endpackage

View File

@@ -0,0 +1,134 @@
The (relative) Memory map for this PLIC follows the one given in:
SiFive U54-MC Core Complex Manual, v1p0, Oct 4 2017
Chapter 8 Platform Level Interrupt Controller, pp.32-38.
>----------------
Priority registers
0x0000 Reserved
0x0004 Source 1 priority
0x0008 Source 2 priority
...
0x0800 Source 511 priority
>----------------
Reserved
0x0804 ... 0x0FFF
>----------------
IP (Interrupt Pending) array: 32 sources per 32b word
0x1000
...
0x103C
>----------------
Reserved
0x1018 ... 0x1FFF
>----------------
IE (interrupt enables) array for Hart0 M mode (1 bit per source)
0x2000
...
0x2014
>----------------
Reserved
0x2018 ... 0x207F
>----------------
IE (interrupt enables) array for Hart1 M mode (1 bit per source)
0x2080
...
0x2094
>----------------
IE (interrupt enables) array for Hart1 S mode (1 bit per source)
0x2100
...
0x2114
>----------------
IE (interrupt enables) array for Hart2 M mode (1 bit per source)
0x2180
...
0x2194
>----------------
IE (interrupt enables) array for Hart2 S mode (1 bit per source)
0x2200
...
0x2214
>----------------
IE (interrupt enables) array for Hart3 M mode (1 bit per source)
0x2280
...
0x2294
>----------------
IE (interrupt enables) array for Hart3 S mode (1 bit per source)
0x2300
...
0x2314
>----------------
IE (interrupt enables) array for Hart4 M mode (1 bit per source)
0x2380
...
0x2394
>----------------
IE (interrupt enables) array for Hart4 S mode (1 bit per source)
0x2400
...
0x2414
>----------------
Reserved
0x0C00 2480 ... 0x0C1F FFFF
>----------------
Threshold (of priority) and Claim/Complete registers
0x0C20 0000 Hart 0 M-mode
0x0C20 0004 Hart 0 M-mode claim/complete
>----------------
0x0C20 1000 Hart 1 M-mode
0x0C20 1004 Hart 1 M-mode claim/complete
>----------------
0x0C20 2000 Hart 1 S-mode
0x0C20 2004 Hart 1 S-mode claim/complete
>----------------
0x0C20 3000 Hart 2 M-mode
0x0C20 3004 Hart 2 M-mode claim/complete
>----------------
0x0C20 4000 Hart 2 S-mode
0x0C20 4004 Hart 2 S-mode claim/complete
>----------------
0x0C20 5000 Hart 3 M-mode
0x0C20 5004 Hart 3 M-mode claim/complete
>----------------
0x0C20 6000 Hart 3 S-mode
0x0C20 6004 Hart 3 S-mode claim/complete
>----------------
0x0C20 7000 Hart 4 M-mode
0x0C20 7004 Hart 4 M-mode claim/complete
>----------------
0x0C20 8000 Hart 4 S-mode
0x0C20 8004 Hart 4 S-mode claim/complete
>----------------

296
src_Core/PLIC/Test_PLIC.bsv Normal file
View File

@@ -0,0 +1,296 @@
// Copyright (c) 2019 Bluespec, Inc. All Rights Reserved
package Test_PLIC;
// ================================================================
// Standalone unit-test testbench for PLIC.
//
// ================================================================
// Bluespec lib imports
import ConfigReg :: *;
import Vector :: *;
import FIFOF :: *;
import GetPut :: *;
import ClientServer :: *;
import Connectable :: *;
import Assert :: *;
import StmtFSM :: *;
// ----------------
// BSV additional libs
import Cur_Cycle :: *;
import GetPut_Aux :: *;
import Semi_FIFOF :: *;
// ================================================================
// Project imports
import AXI4_Types :: *;
import AXI4_Fabric :: *;
import Fabric_Defs :: *; // for Wd_Id, Wd_Addr, Wd_Data, Wd_User
import SoC_Map :: *;
import PLIC :: *;
import PLIC_16_2_7 :: *;
// ================================================================
Integer n_external_interrupt_sources = valueOf (N_External_Interrupt_Sources);
typedef TAdd #(1, N_External_Interrupt_Sources) N_Interrupt_Sources;
typedef Bit #(TLog #(N_Interrupt_Sources)) Source_Id;
Integer plic_n_targets = valueOf (PLIC_N_Targets);
Integer target_0 = 0;
Integer target_1 = 1;
Integer plic_max_priority = valueOf (PLIC_Max_Priority);
typedef Bit #(TLog #(TAdd #(1, PLIC_Max_Priority))) Priority;
// ================================================================
// The Core module
(* synthesize *)
module mkTest_PLIC (Empty);
// System address map
SoC_Map_IFC soc_map <- mkSoC_Map;
// PLIC (Platform-Level Interrupt Controller)
PLIC_IFC_16_2_7 plic <- mkPLIC_16_2_7;
// Master transactor through which to read/write PLIC regs
AXI4_Master_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master_xactor <- mkAXI4_Master_Xactor;
Vector #(N_Interrupt_Sources, Reg #(Bool)) vrg_irqs <- replicateM (mkReg (False));
// ================================================================
// AXI4 interactions
FIFOF #(Fabric_Addr) f_read_addr <- mkFIFOF;
FIFOF #(Fabric_Data) f_read_data <- mkFIFOF;
function Action fa_read_req (Integer addr);
action
let fabric_addr = soc_map.m_plic_addr_base + fromInteger (addr);
let mem_req_rd_addr = AXI4_Rd_Addr {arid: fabric_default_id,
araddr: fabric_addr,
arlen: 0, // burst len = arlen+1
arsize: zeroExtend (axsize_4),
arburst: fabric_default_burst,
arlock: fabric_default_lock,
arcache: fabric_default_arcache,
arprot: fabric_default_prot,
arqos: fabric_default_qos,
arregion: fabric_default_region,
aruser: fabric_default_user};
master_xactor.i_rd_addr.enq (mem_req_rd_addr);
f_read_addr.enq (fabric_addr);
endaction
endfunction
function Action fa_read_rsp;
action
let fabric_addr <- pop (f_read_addr);
let rd_data <- pop_o (master_xactor.o_rd_data);
if (rd_data.rresp != axi4_resp_okay) begin
$display ("ERROR: fa_read_rsp: fabric response error");
$display (" ", fshow (rd_data));
end
let x = (((valueOf (Wd_Data) == 64) && ((fabric_addr & 'h7) == 'h4))
? (rd_data.rdata >> 32)
: rd_data.rdata);
f_read_data.enq (x);
endaction
endfunction
function Action fa_write_req (Integer addr, Fabric_Data data);
action
let fabric_addr = soc_map.m_plic_addr_base + fromInteger (addr);
let mem_req_wr_addr = AXI4_Wr_Addr {awid: fabric_default_id,
awaddr: fabric_addr,
awlen: 0, // burst len = awlen+1
awsize: zeroExtend (axsize_4),
awburst: fabric_default_burst,
awlock: fabric_default_lock,
awcache: fabric_default_awcache,
awprot: fabric_default_prot,
awqos: fabric_default_qos,
awregion: fabric_default_region,
awuser: fabric_default_user};
let x = (((valueOf (Wd_Data) == 64) && ((fabric_addr & 'h7) == 'h4))
? (data << 32)
: data);
let mem_req_wr_data = AXI4_Wr_Data {wid: fabric_default_id,
wdata: x,
wstrb: '1,
wlast: True,
wuser: fabric_default_user};
master_xactor.i_wr_addr.enq (mem_req_wr_addr);
master_xactor.i_wr_data.enq (mem_req_wr_data);
endaction
endfunction
function Action fa_write_rsp;
action
let wr_resp <- pop_o (master_xactor.o_wr_resp);
if (wr_resp.bresp != axi4_resp_okay) begin
$display ("ERROR: rl_discard_write_rsp: fabric response error");
$display (" ", fshow (wr_resp));
end
endaction
endfunction
// ================================================================
// Help functions to interact with PLIC at an "API" level
function Action fa_print_plic_eips ();
action
$write ("PLIC.v_target eip =");
$write (" ", fshow (plic.v_targets [0].m_eip));
$write (" ", fshow (plic.v_targets [1].m_eip));
$display ("");
endaction
endfunction
function Stmt fstmt_set_source_priority (Integer src, Priority prio);
return seq
fa_write_req (src * 4, zeroExtend (prio));
fa_write_rsp;
endseq;
endfunction
function Stmt fstmt_set_target_ies (Integer target, Bit #(32) ies);
return seq
fa_write_req ('h2000 + (target * 'h80), zeroExtend (ies));
fa_write_rsp;
endseq;
endfunction
function Stmt fstmt_set_target_threshold (Integer target, Priority threshold);
return seq
fa_write_req ('h20_0000 + (target * 'h1000), zeroExtend (threshold));
fa_write_rsp;
endseq;
endfunction
function Stmt fstmt_claim (Integer target);
return seq
fa_read_req ('h20_0004 + (target * 'h1000));
fa_read_rsp;
action
let x <- pop (f_read_data);
$display ("fstmt_claim: PLIC returned %0d", x);
endaction
endseq;
endfunction
function Stmt fstmt_complete (Integer target, Source_Id source_id);
return seq
fa_write_req ('h20_0004 + (target * 'h1000), zeroExtend (source_id));
fa_write_rsp;
endseq;
endfunction
// ================================================================
// BEHAVIOR
mkConnection (master_xactor.axi_side, plic.axi4_slave);
// Drive all interrupt requests from local regs
for (Integer j = 0; j < n_external_interrupt_sources; j = j + 1)
rule rl_drive_irq;
plic.v_sources [j].m_interrupt_req (vrg_irqs [j]);
endrule
// ================================================================
Stmt init = seq
action
$display ("Initializing PLIC");
plic.server_reset.request.put (?);
endaction
action
let rsp <- plic.server_reset.response.get;
plic.set_addr_map (zeroExtend (soc_map.m_plic_addr_base),
zeroExtend (soc_map.m_plic_addr_lim));
endaction
fstmt_set_source_priority (1, 0);
fstmt_set_source_priority (2, 0);
fstmt_set_source_priority (3, 0);
fstmt_set_source_priority (4, 0);
fstmt_set_source_priority (5, 0);
fstmt_set_source_priority (6, 0);
fstmt_set_source_priority (7, 0);
fstmt_set_source_priority (8, 0);
fstmt_set_source_priority (9, 0);
fstmt_set_source_priority (10, 0);
fstmt_set_source_priority (11, 0);
fstmt_set_source_priority (12, 0);
fstmt_set_source_priority (13, 0);
fstmt_set_source_priority (14, 0);
fstmt_set_source_priority (15, 0);
fstmt_set_source_priority (16, 0);
fstmt_set_target_ies (target_0, 0);
fstmt_set_target_ies (target_1, 0);
fstmt_set_target_threshold (target_0, 7);
fstmt_set_target_threshold (target_1, 7);
delay (5);
$display ("Finished Initializing PLIC");
endseq;
Stmt test1 = seq
$display (">---------------- TEST 1");
plic.set_verbosity (1);
fstmt_set_source_priority (5, 4);
fstmt_set_target_ies (target_0, 'b10_0000); // bit 5
fstmt_set_target_threshold (target_0, 4);
fstmt_set_target_ies (target_1, 'b10_0000); // bit 5
fstmt_set_target_threshold (target_1, 2);
plic.show_PLIC_state;
fa_print_plic_eips;
vrg_irqs [5] <= True;
delay (2);
plic.show_PLIC_state;
fa_print_plic_eips;
fstmt_set_target_threshold (target_0, 3);
plic.show_PLIC_state;
fa_print_plic_eips;
fstmt_claim (target_1);
plic.show_PLIC_state;
fa_print_plic_eips;
fstmt_complete (target_1, 5);
plic.show_PLIC_state;
fa_print_plic_eips;
endseq;
// ================================================================
mkAutoFSM (seq
init;
test1;
$finish (0);
endseq);
endmodule
// ================================================================
endpackage

View File

@@ -0,0 +1,21 @@
Copyright (c) 2017, 2018 Massachusetts Institute of Technology
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

153
src_Core/RISCY_OOO/Makefile Normal file
View File

@@ -0,0 +1,153 @@
# This Makefile copies source BSV files from the MIT riscy-OOO repo
# for use in the Bluespec environment
RISCY_HOME = ~/Projects/RISCV/MIT-riscy/riscy-OOO
copyfiles: copy_RV64_OOO_files copy_procs_lib_files copy_coherence_src_files copy_fpgautils_files copy_connectal_files
PROCS_RV64G_OOO = procs/RV64G_OOO
.PHONY: copy_RV64_OOO_files
copy_RV64_OOO_files:
mkdir -p $(PROCS_RV64G_OOO)
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/AluExePipeline.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/CommitStage.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/FetchStage.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/FpuMulDivExePipeline.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/MemExePipeline.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/ProcConfig.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/RenameStage.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/ReorderBufferSynth.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/ReservationStationAlu.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/ReservationStationFpuMulDiv.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/ReservationStationMem.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/RFileSynth.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/ScoreboardSynth.bsv ./$(PROCS_RV64G_OOO)/
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/SynthParam.bsv ./$(PROCS_RV64G_OOO)/
PROCS_LIB = procs/lib
.PHONY: copy_procs_lib_files
copy_procs_lib_files:
mkdir -p $(PROCS_LIB)
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Amo.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Bht.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/BrPred.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Btb.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Bypass.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/CacheUtils.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/ConcatReg.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Decode.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/DirPredictor.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/DTlb.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Ehr.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/EpochManager.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Exec.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Fifo.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Fpu.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/FullAssocTlb.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/GlobalBrHistReg.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/GlobalSpecUpdate.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/GSelectPred.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/GSharePred.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/HasSpecBits.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/ITlb.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/L1CoCache.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/L1LLConnect.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/L2SetAssocTlb.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/L2Tlb.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/LatencyTimer.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/LLCache.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/LLCDmaConnect.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/LLCRqMshrSecureModel.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MemLoader.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MemLoaderIF.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MemoryTypes.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MMIOAddrs.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MMIOCore.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MMIOInst.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MsgFifo.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MulDiv.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Performance.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/PhysRFile.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/ProcTypes.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Ras.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/RenameDebugIF.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/RenamingTable.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/ReorderBuffer.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/ReservationStationEhr.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/SafeCounter.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Scoreboard.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/SetAssocTlb.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/SpecFifo.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/SpecPoisonFifo.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/SpecTagManager.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/SplitLSQ.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/StoreBuffer.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/TlbConnect.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/TlbTypes.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/TourPred.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/TourPredSecure.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/TranslationCache.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Types.bsv ./$(PROCS_LIB)/
cp -p $(RISCY_HOME)/$(PROCS_LIB)/VerificationPacket.bsv ./$(PROCS_LIB)/
COHERENCE_SRC = coherence/src
.PHONY: copy_coherence_src_files
copy_coherence_src_files:
mkdir -p $(COHERENCE_SRC)
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/CCPipe.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/CCTypes.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/CrossBar.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/IBank.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/ICRqMshr.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/IPRqMshr.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/L1Bank.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/L1CRqMshr.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/L1Pipe.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/L1PRqMshr.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/LLBank.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/LLCRqMshr.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/LLPipe.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/MshrDeadlockChecker.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/RandomReplace.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/RWBramCore.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/SelfInvIBank.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/SelfInvIPipe.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/SelfInvL1Bank.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/SelfInvL1Pipe.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/SelfInvLLBank.bsv ./$(COHERENCE_SRC)/
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/SelfInvLLPipe.bsv ./$(COHERENCE_SRC)/
FPGAUTILS_LIB = fpgautils/lib
FPGAUTILS_XILINX_FPU = fpgautils/xilinx/fpu
FPGAUTILS_XILINX_RESET_REGS = fpgautils/xilinx/reset_regs
.PHONY: copy_fpgautils_files
copy_fpgautils_files:
mkdir -p $(FPGAUTILS_LIB)
mkdir -p $(FPGAUTILS_XILINX_FPU)
mkdir -p $(FPGAUTILS_XILINX_RESET_REGS)
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/DramCommon.bsv ./$(FPGAUTILS_LIB)/
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/ResetGuard.bsv ./$(FPGAUTILS_LIB)/
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/SyncFifo.bsv ./$(FPGAUTILS_LIB)/
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/XilinxFpu.bsv ./$(FPGAUTILS_LIB)/
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/XilinxIntDiv.bsv ./$(FPGAUTILS_LIB)/
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/XilinxIntMul.bsv ./$(FPGAUTILS_LIB)/
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/XilinxSyncFifo.bsv ./$(FPGAUTILS_LIB)/
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/WaitAutoReset.bsv ./$(FPGAUTILS_LIB)/
cp -p $(RISCY_HOME)/$(FPGAUTILS_XILINX_FPU)/fp_sqrt_sim.v ./$(FPGAUTILS_XILINX_FPU)/
cp -p $(RISCY_HOME)/$(FPGAUTILS_XILINX_FPU)/fp_fma_sim.v ./$(FPGAUTILS_XILINX_FPU)/
cp -p $(RISCY_HOME)/$(FPGAUTILS_XILINX_FPU)/fp_div_sim.v ./$(FPGAUTILS_XILINX_FPU)/
cp -p $(RISCY_HOME)/$(FPGAUTILS_XILINX_RESET_REGS)/reset_guard.v ./$(FPGAUTILS_XILINX_RESET_REGS)/
CONNECTAL_BSV = connectal/bsv
CONNECTAL_LIB_BSV = connectal/lib/bsv
CONNECTAL_TESTS_SPI = connectal/tests/spi
.PHONY: copy_connectal_files
copy_connectal_files:
mkdir -p $(CONNECTAL_BSV)
mkdir -p $(CONNECTAL_LIB_BSV)
mkdir -p $(CONNECTAL_TESTS_SPI)
cp -p $(RISCY_HOME)/$(CONNECTAL_BSV)/ConnectalBramFifo.bsv ./$(CONNECTAL_BSV)/
cp -p $(RISCY_HOME)/$(CONNECTAL_BSV)/ConnectalClocks.bsv ./$(CONNECTAL_BSV)/
cp -p $(RISCY_HOME)/$(CONNECTAL_LIB_BSV)/Arith.bsv ./$(CONNECTAL_LIB_BSV)/
cp -p $(RISCY_HOME)/$(CONNECTAL_TESTS_SPI)/ConnectalProjectConfig.bsv ./$(CONNECTAL_TESTS_SPI)/
tree .

View File

@@ -0,0 +1,408 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Ehr::*;
import Fifo::*;
import Vector::*;
import RWBramCore::*;
import FShow::*;
import Types::*;
import CCTypes::*;
// general type param ordering: way < index < tag < msi < dir < owner < other < rep < line < pipeCmd
typedef union tagged {
void Invalid;
msiT DownDir; // cRs downgraded toState
msiT UpCs; // pRs upgraded toState
} RespState#(type msiT) deriving(Bits, Eq, FShow);
typedef struct {
pipeCmdT cmd;
// tag match & ram output
wayT way;
Bool pRqMiss; // pRq miss, valid only if go through tag match
RamData#(tagT, msiT, dirT, ownerT, otherT, lineT) ram;
// replace info, actually not needed, just output for debug purposes
repT repInfo;
} PipeOut#(
type wayT,
type tagT,
type msiT,
type dirT,
type ownerT,
type otherT,
type repT,
type lineT,
type pipeCmdT
) deriving(Bits, Eq, FShow);
interface CCPipe#(
numeric type wayNum,
type indexT,
type tagT,
type msiT,
type dirT,
type ownerT,
type otherT,
type repT,
type lineT,
type pipeCmdT
);
method Action enq(pipeCmdT cmd, Maybe#(lineT) respLine, RespState#(msiT) toState);
method Bool notFull;
method PipeOut#(Bit#(TLog#(wayNum)), tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT) first;
method PipeOut#(Bit#(TLog#(wayNum)), tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT) unguard_first;
method Bool notEmpty;
method Action deqWrite(
Maybe#(pipeCmdT) newCmd,
RamData#(tagT, msiT, dirT, ownerT, otherT, lineT) wrRam,
Bool updateRep // update replacement info
);
// empty signal when we need to flush self-invalidate cache
method Bool emptyForFlush;
endinterface
// internal pipeline reg types
// three stages
// 1: enq
// 2: tag match, read data and dir
// 3: output
typedef struct {
pipeCmdT cmd;
// bypasses
Vector#(wayNum, Maybe#(CacheInfo#(tagT, msiT, dirT, ownerT, otherT))) infoVec;
Maybe#(repT) repInfo; // replacement info for the whole set
// CRs/PRs info
Maybe#(lineT) respLine;
RespState#(msiT) toState;
} Enq2Match#(
numeric type wayNum,
type tagT,
type msiT,
type dirT,
type ownerT,
type otherT,
type repT,
type lineT,
type pipeCmdT
) deriving(Bits, Eq, FShow);
typedef struct {
pipeCmdT cmd;
// tag match results
wayT way;
Bool pRqMiss;
// RAM outputs
// cs is merged with PRs toState
// dir is merged with CRs toState
CacheInfo#(tagT, msiT, dirT, ownerT, otherT) info;
repT repInfo;
// bypassed or resp line
Maybe#(lineT) line;
} Match2Out#(
type wayT,
type tagT,
type msiT,
type dirT,
type ownerT,
type otherT,
type repT,
type lineT,
type pipeCmdT
) deriving(Bits, Eq, FShow);
typedef struct {
indexT index;
wayT way;
RamData#(tagT, msiT, dirT, ownerT, otherT, lineT) ram; // data to write into RAM
repT repInfo; // replacement info write into RAM
} BypassInfo#(
type wayT,
type indexT,
type tagT,
type msiT,
type dirT,
type ownerT,
type otherT,
type repT,
type lineT
) deriving(Bits, Eq, FShow);
typedef struct {
wayT way;
Bool pRqMiss;
} TagMatchResult#(type wayT) deriving(Bits, Eq, FShow);
typedef struct {
msiT cs;
} UpdateByUpCs#(type msiT) deriving(Bits, Eq, FShow);
typedef struct {
msiT cs;
dirT dir;
} UpdateByDownDir#(type msiT, type dirT) deriving(Bits, Eq, FShow);
// index to data ram: {way, normal index}
function dataIndexT getDataRamIndex(wayT w, indexT i) provisos(
Alias#(wayT, Bit#(_waySz)),
Alias#(indexT, Bit#(_indexSz)),
Alias#(dataIndexT, Bit#(TAdd#(_waySz, _indexSz)))
);
return {w, i};
endfunction
module mkCCPipe#(
ReadOnly#(Bool) initDone,
function indexT getIndex(pipeCmdT cmd),
function ActionValue#(TagMatchResult#(wayT)) tagMatch(
// actionvalue enable us to do checking inside the function
pipeCmdT cmd,
// below are current RAM outputs, is merged with ram write from final stage
// but is NOT merged with state changes carried in PRs/CRs
Vector#(wayNum, tagT) tagVec,
Vector#(wayNum, msiT) csVec,
Vector#(wayNum, ownerT) ownerVec,
repT repInfo
),
function ActionValue#(UpdateByUpCs#(msiT)) updateByUpCs(
pipeCmdT cmd, msiT toState, Bool dataValid, msiT oldCs
),
function ActionValue#(UpdateByDownDir#(msiT, dirT)) updateByDownDir(
pipeCmdT cmd, msiT toState, Bool dataValid, msiT oldCs, dirT oldDir
),
function ActionValue#(repT) updateRepInfo(repT oldRep, wayT hitWay),
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam,
RWBramCore#(indexT, repT) repRam,
RWBramCore#(dataIndexT, lineT) dataRam
)(
CCPipe#(wayNum, indexT, tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT)
) provisos (
Alias#(wayT, Bit#(TLog#(wayNum))),
Alias#(indexT, Bit#(_indexSz)),
Alias#(infoT, CacheInfo#(tagT, msiT, dirT, ownerT, otherT)),
Alias#(ramDataT, RamData#(tagT, msiT, dirT, ownerT, otherT, lineT)),
Alias#(respStateT, RespState#(msiT)),
Alias#(pipeOutT, PipeOut#(wayT, tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT)),
Alias#(enq2MatchT, Enq2Match#(wayNum, tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT)),
Alias#(match2OutT, Match2Out#(wayT, tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT)),
Alias#(bypassInfoT, BypassInfo#(wayT, indexT, tagT, msiT, dirT, ownerT, otherT, repT, lineT)),
Bits#(tagT, _tagSz),
Bits#(msiT, _msiSz),
Bits#(dirT, _dirSz),
Bits#(ownerT, _ownerSz),
Bits#(otherT, _otherSz),
Bits#(repT, _repSz),
Bits#(lineT, _lineSz),
Bits#(pipeCmdT, _pipeCmdSz),
// index to data ram: {way, normal index}
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), _indexSz)))
);
// pipeline regs
Ehr#(3, Maybe#(enq2MatchT)) enq2Mat <- mkEhr(Invalid);
// port 0: bypass
Reg#(Maybe#(enq2MatchT)) enq2Mat_bypass = enq2Mat[0];
// port 1: tag match
Reg#(Maybe#(enq2MatchT)) enq2Mat_match = enq2Mat[1];
// port 2: enq
Reg#(Maybe#(enq2MatchT)) enq2Mat_enq = enq2Mat[2];
Ehr#(2, Maybe#(match2OutT)) mat2Out <- mkEhr(Invalid);
// port 0: out
Reg#(Maybe#(match2OutT)) mat2Out_out = mat2Out[0];
// port 1: tag match
Reg#(Maybe#(match2OutT)) mat2Out_match = mat2Out[1];
// bypass write to ram
RWire#(bypassInfoT) bypass <- mkRWire;
// stage 2: first get bypass
(* fire_when_enabled, no_implicit_conditions *)
rule doMatch_bypass(isValid(bypass.wget) && isValid(enq2Mat_bypass) && initDone);
bypassInfoT b = fromMaybe(?, bypass.wget);
enq2MatchT e2m = fromMaybe(?, enq2Mat_bypass);
if(b.index == getIndex(e2m.cmd)) begin
e2m.infoVec[b.way] = Valid (b.ram.info);
e2m.repInfo = Valid (b.repInfo);
end
enq2Mat_bypass <= Valid (e2m);
endrule
rule doTagMatch(isValid(enq2Mat_match) && !isValid(mat2Out_match) && initDone);
enq2MatchT e2m = fromMaybe(?, enq2Mat_match);
// get cache output & merge with bypass
Vector#(wayNum, infoT) infoVec;
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
infoRam[i].deqRdResp;
infoVec[i] = fromMaybe(infoRam[i].rdResp, e2m.infoVec[i]);
end
repRam.deqRdResp;
repT repInfo = fromMaybe(repRam.rdResp, e2m.repInfo);
// do tag match to get way to occupy
Vector#(wayNum, tagT) tagVec;
Vector#(wayNum, msiT) csVec;
Vector#(wayNum, ownerT) ownerVec;
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
tagVec[i] = infoVec[i].tag;
csVec[i] = infoVec[i].cs;
ownerVec[i] = infoVec[i].owner;
end
let tmRes <- tagMatch(e2m.cmd, tagVec, csVec, ownerVec, repInfo);
wayT way = tmRes.way;
Bool pRqMiss = tmRes.pRqMiss;
// read data
indexT index = getIndex(e2m.cmd);
dataRam.rdReq(getDataRamIndex(way, index));
// set mat2out & merge with CRs/PRs & merge with data bypass
// resp data has higher priority than data bypass
match2OutT m2o = Match2Out {
cmd: e2m.cmd,
way: way,
pRqMiss: pRqMiss,
info: infoVec[way],
repInfo: repInfo,
line: e2m.respLine
};
if(e2m.toState matches tagged UpCs .s) begin
UpdateByUpCs#(msiT) upd <- updateByUpCs(
e2m.cmd, s, isValid(e2m.respLine), m2o.info.cs
);
m2o.info.cs = upd.cs;
end
else if(e2m.toState matches tagged DownDir .s) begin
UpdateByDownDir#(msiT, dirT) upd <- updateByDownDir(
e2m.cmd, s, isValid(e2m.respLine), m2o.info.cs, m2o.info.dir
);
m2o.info.cs = upd.cs;
m2o.info.dir = upd.dir;
end
if(bypass.wget matches tagged Valid .b &&& b.index == index &&& b.way == way &&& !isValid(m2o.line)) begin
// bypass has lower priority than resp data
m2o.line = Valid (b.ram.line);
end
mat2Out_match <= Valid (m2o);
// reset enq2mat
enq2Mat_match <= Invalid;
endrule
// construct output with bypass/resp data
function pipeOutT firstOut;
match2OutT m2o = fromMaybe(?, mat2Out_out);
return PipeOut {
cmd: m2o.cmd,
way: m2o.way,
pRqMiss: m2o.pRqMiss,
ram: RamData {
info: m2o.info,
line: fromMaybe(dataRam.rdResp, m2o.line)
},
repInfo: m2o.repInfo
};
endfunction
Bool enq_guard = !isValid(enq2Mat_enq) && initDone;
Bool deq_guard = isValid(mat2Out_out) && initDone;
// stage 1: enq req to pipeline: access info+rep RAM & bypass
method Action enq(pipeCmdT cmd, Maybe#(lineT) respLine, respStateT toState) if(enq_guard);
// read ram
indexT index = getIndex(cmd);
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
infoRam[i].rdReq(index);
end
repRam.rdReq(index);
// write reg & get bypass
enq2MatchT e2m = Enq2Match {
cmd: cmd,
infoVec: replicate(Invalid),
repInfo: Invalid,
respLine: respLine,
toState: toState
};
if(bypass.wget matches tagged Valid .b &&& b.index == index) begin
e2m.infoVec[b.way] = Valid (b.ram.info);
e2m.repInfo = Valid (b.repInfo);
end
enq2Mat_enq <= Valid (e2m);
endmethod
method Bool notFull = enq_guard;
method pipeOutT first if(deq_guard);
return firstOut;
endmethod
method pipeOutT unguard_first;
return firstOut;
endmethod
method Bool notEmpty = deq_guard;
method Action deqWrite(Maybe#(pipeCmdT) newCmd, ramDataT wrRam, Bool updateRep) if(deq_guard);
match2OutT m2o = fromMaybe(?, mat2Out_out);
wayT way = m2o.way;
indexT index = getIndex(m2o.cmd);
// update replacement info
repT repInfo = m2o.repInfo;
if(updateRep) begin
repInfo <- updateRepInfo(m2o.repInfo, way);
end
// write ram
infoRam[way].wrReq(index, wrRam.info);
repRam.wrReq(index, repInfo);
dataRam.wrReq(getDataRamIndex(way, index), wrRam.line);
// set bypass to Enq and Match stages
bypass.wset(BypassInfo {
index: index,
way: way,
ram: wrRam,
repInfo: repInfo
});
// change pipeline reg
if(newCmd matches tagged Valid .cmd) begin
// update pipeline reg
mat2Out_out <= Valid (Match2Out {
cmd: cmd, // swapped in new cmd
way: way, // keep way same
pRqMiss: False, // reset (not valid for swapped in pRq)
info: wrRam.info, // get bypass
repInfo: repInfo, // get bypass
line: Valid (wrRam.line) // get bypass
});
end
else begin
// XXX deq ram resp, I think this should not block
dataRam.deqRdResp;
// reset pipeline reg
mat2Out_out <= Invalid;
end
endmethod
method Bool emptyForFlush;
return !isValid(mat2Out[0]) && !isValid(enq2Mat[0]);
endmethod
endmodule

View File

@@ -0,0 +1,464 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Types::*; // import from RISCY repo
import MemoryTypes::*; // import from RISCY repo
import Vector::*;
import FShow::*;
import CacheUtils::*;
import Assert::*;
import Connectable::*;
import GetPut::*;
import ClientServer::*;
typedef enum {
I = 2'd0,
S = 2'd1,
E = 2'd2,
M = 2'd3
} MESI deriving(Bits, Eq, FShow);
typedef MESI Msi;
instance Ord#(MESI);
function Bool \< ( MESI x, MESI y );
return pack(x) < pack(y);
endfunction
function Bool \<= ( MESI x, MESI y );
return pack(x) <= pack(y);
endfunction
function Bool \> ( MESI x, MESI y );
return pack(x) > pack(y);
endfunction
function Bool \>= ( MESI x, MESI y );
return pack(x) >= pack(y);
endfunction
function Ordering compare( MESI x, MESI y );
return compare(pack(x), pack(y));
endfunction
function MESI min( MESI x, MESI y );
return x < y ? x : y;
endfunction
function MESI max( MESI x, MESI y );
return x > y ? x : y;
endfunction
endinstance
// whether cache state is enough to serice upgrade req, i.e., no
// need to req parent
function Bool enoughCacheState(Msi cs, Msi to);
return cs >= to || cs >= E;
endfunction
// the maximum dir state that a peer can have so that it will not
// be downgraded to service upgrade req to state x
function Msi toCompat(Msi x);
return x == S ? S : I;
endfunction
typedef TDiv#(DataSz, 8) DataSzBytes;
typedef TLog#(DataSzBytes) LgDataSzBytes;
typedef Bit#(LgDataSzBytes) DataBytesOffset;
typedef TDiv#(InstSz, 8) InstSzBytes;
typedef TLog#(InstSzBytes) LgInstSzBytes;
// 64B cache line -- XXX same with parameters in CacheUtils.bsv
typedef CacheUtils::LogCLineNumData LgLineSzData;
typedef CacheUtils::LogCLineNumBytes LgLineSzBytes;
typedef CacheUtils::CLineAddrSz LineAddrSz;
typedef CacheUtils::CLineAddr LineAddr;
typedef CacheUtils::CLineNumData LineSzData;
typedef CacheUtils::CLineDataSel LineDataOffset;
typedef CacheUtils::CLineNumBytes LineSzBytes;
typedef CacheUtils::CLineByteEn LineByteEn;
typedef TDiv#(LineSzBytes, InstSzBytes) LineSzInst;
typedef Bit#(TLog#(LineSzInst)) LineInstOffset;
typedef Vector#(LineSzData, Data) Line;
function LineAddr getLineAddr(Addr addr) = truncateLSB(addr);
function LineDataOffset getLineDataOffset(Addr a);
return truncate(a >> valueOf(LgDataSzBytes));
endfunction
function LineInstOffset getLineInstOffset(Addr a);
return truncate(a >> valueof(LgInstSzBytes));
endfunction
function Line getUpdatedLine(Line curLine, LineByteEn wrBE, Line wrLine);
Vector#(LineSzBytes, Bit#(8)) curVec = unpack(pack(curLine));
Vector#(LineSzBytes, Bit#(8)) wrVec = unpack(pack(wrLine));
function Bit#(8) getNewByte(Integer i);
return wrBE[i] ? wrVec[i] : curVec[i];
endfunction
Vector#(LineSzBytes, Bit#(8)) newVec = map(getNewByte, genVector);
return unpack(pack(newVec));
endfunction
function Data getUpdatedData(Data curData, ByteEn wrBE, Data wrData);
Vector#(DataSzBytes, Bit#(8)) curVec = unpack(pack(curData));
Vector#(DataSzBytes, Bit#(8)) wrVec = unpack(pack(wrData));
function Bit#(8) getNewByte(Integer i);
return wrBE[i] ? wrVec[i] : curVec[i];
endfunction
Vector#(DataSzBytes, Bit#(8)) newVec = map(getNewByte, genVector);
return pack(newVec);
endfunction
// calculate tag
typedef TSub#(AddrSz, TAdd#(LgLineSzBytes, TAdd#(lgBankNum, lgSetNum)))
GetTagSz#(numeric type lgBankNum, numeric type lgSetNum);
// dependency tracking in MSHR
typedef union tagged {
cRqIdxT CRq;
pRqIdxT PRq;
} MshrIndex#(type cRqIdxT, type pRqIdxT) deriving(Bits, Eq, FShow);
// cache owner
typedef struct {
cRqIdxT mshrIdx;
Bool replacing;
} CRqOwner#(type cRqIdxT) deriving(Bits, Eq, FShow);
typedef struct {
pRqIdxT mshrIdx;
Bool hasSucc; // has successor in dependency chain
} PRqOwner#(type pRqIdxT) deriving(Bits, Eq, FShow);
typedef union tagged {
void Invalid;
CRqOwner#(cRqIdxT) CRq;
PRqOwner#(pRqIdxT) PRq;
} CacheOwner#(type cRqIdxT, type pRqIdxT) deriving(Bits, Eq, FShow);
// cache info: tag, cs, dir, owner, and other
typedef struct {
tagT tag;
msiT cs;
dirT dir;
ownerT owner;
otherT other;
} CacheInfo#(
type tagT,
type msiT,
type dirT,
type ownerT,
type otherT
) deriving(Bits, Eq, FShow);
// ram output
typedef struct {
CacheInfo#(tagT, msiT, dirT, ownerT, otherT) info;
lineT line;
} RamData#(
type tagT,
type msiT,
type dirT,
type ownerT,
type otherT,
type lineT
) deriving(Bits, Eq, FShow);
// processor req/resp
typedef struct {
idT id;
Addr addr;
Msi toState;
// below are detailed mem op
MemOp op; // Ld, St, Lr, Sc, Amo
ByteEn byteEn; // valid when op == Sc
Data data; // valid when op == Sc/Amo
AmoInst amoInst; // valid when op == Amo
} ProcRq#(type idT) deriving(Bits, Eq, FShow);
interface L1ProcReq#(type idT);
method Action req(ProcRq#(idT) r);
endinterface
interface L1ProcResp#(type idT);
method Action respLd(idT id, Data resp);
method Action respLrScAmo(idT id, Data resp);
method ActionValue#(Tuple2#(LineByteEn, Line)) respSt(idT id);
method Action evict(LineAddr a); // called when cache line is evicted
endinterface
// RISCV-specific store-cond return values
typedef 0 ScSuccVal;
typedef 1 ScFailVal;
`ifdef DEBUG_ICACHE
typedef struct {
Bit#(64) id;
Line line;
} DebugICacheResp deriving(Bits, Eq, FShow);
`endif
// I$ req/resp
interface InstServer#(numeric type supSz);
interface Put#(Addr) req;
interface Get#(Vector#(supSz, Maybe#(Instruction))) resp;
`ifdef DEBUG_ICACHE
interface Get#(DebugICacheResp) done; // the id and cache line of the I$ req that truly performs
`endif
endinterface
typedef struct {
Addr addr;
`ifdef DEBUG_ICACHE
Bit#(64) id; // incremening id for each incoming I$ req (0,1,...)
`endif
} ProcRqToI deriving(Bits, Eq, FShow);
// child/parent req/resp
typedef struct {
Addr addr;
Msi fromState;
Msi toState;
Bool canUpToE; // meaningful to upgrade to E if toState is S
idT id; // slot id in child cache
childT child; // from which child
} CRqMsg#(type idT, type childT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState;
Maybe#(Line) data;
childT child; // from which child
} CRsMsg#(type childT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState;
childT child; // to which child
} PRqMsg#(type childT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState;
childT child; // to which child
Maybe#(Line) data;
idT id; // slot id in cache
} PRsMsg#(type idT, type childT) deriving(Bits, Eq, FShow);
typedef union tagged {
PRqMsg#(childT) PRq;
PRsMsg#(idT, childT) PRs;
} PRqRsMsg#(type idT, type childT) deriving(Bits, Eq, FShow);
interface ChildCacheToParent#(type cRqIdT, type childT);
interface FifoDeq#(CRsMsg#(childT)) rsToP;
interface FifoDeq#(CRqMsg#(cRqIdT, childT)) rqToP;
interface FifoEnq#(PRqRsMsg#(cRqIdT, childT)) fromP;
endinterface
interface ParentCacheToChild#(type cRqIdT, type childT);
interface FifoEnq#(CRsMsg#(childT)) rsFromC;
interface FifoEnq#(CRqMsg#(cRqIdT, childT)) rqFromC;
interface FifoDeq#(PRqRsMsg#(cRqIdT, childT)) toC;
endinterface
// unified child req & dma req
typedef union tagged {
cRqIdT Child;
dmaRqIdT Dma;
} LLRqId#(type cRqIdT, type dmaRqIdT) deriving(Bits, Eq, FShow);
typedef struct {
// common addr
Addr addr;
// child req stuff
Msi fromState;
Msi toState;
Bool canUpToE;
childT child;
// dma req stuff
LineByteEn byteEn;
// req id: distinguish between child and dma
LLRqId#(cRqIdT, dmaRqIdT) id;
} LLRq#(type cRqIdT, type dmaRqIdT, type childT) deriving(Bits, Eq, FShow);
// memory msg
typedef struct {
Addr addr;
childT child; // from which LLC/Dir
idT id; // ld req id and other info need encoding
} LdMemRq#(type idT, type childT) deriving(Bits, Eq, FShow);
typedef struct { // LdMemRq id with more info encoded to handle DMA req in LLC
Bool refill; // the future mem resp will refill LLC cache line
// this is False for DMA read req that miss in LLC (i.e. resp won't refill LLC)
mshrIdxT mshrIdx; // mshr id
} LdMemRqId#(type mshrIdxT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
LineByteEn byteEn;
Line data;
} WbMemRs deriving(Bits, Eq, FShow);
typedef union tagged {
LdMemRq#(idT, childT) Ld;
WbMemRs Wb;
} ToMemMsg#(type idT, type childT) deriving(Bits, Eq, FShow);
typedef struct {
Line data;
childT child; // send to which LLC/Dir
idT id; // original Ld req id
} MemRsMsg#(type idT, type childT) deriving(Bits, Eq, FShow);
// Dma req/resp
typedef struct {
Addr addr;
LineByteEn byteEn; // all False means read
Line data;
idT id; // req id (resp may come out of order, may contain routing info)
} DmaRq#(type idT) deriving(Bits, Eq, FShow);
typedef struct {
Line data; // meaningless for write
idT id;
} DmaRs#(type idT) deriving(Bits, Eq, FShow);
interface DmaServer#(type dmaRqIdT);
interface FifoEnq#(DmaRq#(dmaRqIdT)) memReq;
interface FifoDeq#(DmaRs#(dmaRqIdT)) respLd;
interface FifoDeq#(dmaRqIdT) respSt;
`ifdef DEBUG_DMA
// signal when DMA req really takes effect
interface Get#(dmaRqIdT) wrMissResp;
interface Get#(dmaRqIdT) wrHitResp;
interface Get#(dmaRqIdT) rdMissResp;
interface Get#(dmaRqIdT) rdHitResp;
`endif
endinterface
// memory interface
interface MemFifoServer#(type idT, type childT);
interface FifoEnq#(ToMemMsg#(idT, childT)) fromC;
interface FifoDeq#(MemRsMsg#(idT, childT)) rsToC;
endinterface
interface MemFifoClient#(type idT, type childT);
interface FifoDeq#(ToMemMsg#(idT, childT)) toM;
interface FifoEnq#(MemRsMsg#(idT, childT)) rsFromM;
endinterface
instance Connectable#(MemFifoServer#(idT, childT), MemFifoClient#(idT, childT));
module mkConnection#(
MemFifoServer#(idT, childT) server,
MemFifoClient#(idT, childT) client
)(Empty);
rule doCToM;
client.toM.deq;
server.fromC.enq(client.toM.first);
endrule
rule doMToC;
server.rsToC.deq;
client.rsFromM.enq(server.rsToC.first);
endrule
endmodule
endinstance
instance Connectable#(MemFifoClient#(idT, childT), MemFifoServer#(idT, childT));
module mkConnection#(
MemFifoClient#(idT, childT) client,
MemFifoServer#(idT, childT) server
)(Empty);
mkConnection(server, client);
endmodule
endinstance
// MSHR dir pending bits
typedef union tagged {
void Invalid;
Msi ToSend; // need to send down req to downgrade to some state
Msi Waiting; // waiting for down resp for some state
} DirPend deriving(Bits, Eq, FShow);
function Vector#(childNum, DirPend) getDirPendInitVal;
return replicate(Invalid);
endfunction
function Bool getNeedReqChild(Vector#(childNum, DirPend) dirPend);
// function to determine whether we need to send req to some children
function Bool isToSend(DirPend dp);
if(dp matches tagged ToSend .s) begin
return True;
end
else begin
return False;
end
endfunction
return any(isToSend, dirPend);
endfunction
// Self-inv cache dir pending bits
typedef union tagged {
void Invalid;
childT ToSend;
childT Waiting;
} SelfInvDirPend#(type childT) deriving(Bits, Eq, FShow);
function SelfInvDirPend#(childT) getSelfInvDirPendInitVal;
return Invalid;
endfunction
function Bool getSelfInvNeedReqChild(SelfInvDirPend#(childT) dirPend);
return dirPend matches tagged ToSend .c ? True : False;
endfunction
// useful functions
function Action check(Bool v);
action
when(v, noAction);
endaction
endfunction
function Maybe#(Bit#(logv)) searchIndex
(function Bool pred(element_type x1), Vector#(vsize, element_type) vect)
provisos (Log#(vsize, logv));
return case (findIndex(pred, vect)) matches
tagged Valid .idx: return tagged Valid pack(idx);
Invalid: return tagged Invalid;
endcase;
endfunction
function a readReg(Reg#(a) r) provisos(Bits#(a, aSz)) = r;
function Vector#(n, a) readVector(Vector#(n, Reg#(a)) vr) provisos(Bits#(a, aSz)) = map(readReg, vr);
// doAssert now defined in Types.bsv
//function Action doAssert(Bool b, String str) = dynamicAssert(b, "%m: " + str);
function Get#(t) nullGet;
return (interface Get;
method ActionValue#(t) get if(False);
return ?;
endmethod
endinterface);
endfunction

View File

@@ -0,0 +1,215 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import GetPut::*;
import Connectable::*;
import CacheUtils::*;
import CCTypes::*;
import Types::*;
import FShow::*;
import Fifo::*;
import Ehr::*;
typedef struct {
dstIdxT idx;
dstDataT data;
} XBarDstInfo#(type dstIdxT, type dstDataT) deriving(Bits, Eq);
module mkXBar#(
function XBarDstInfo#(dstIdxT, dstDataT) getDstInfo(srcIdxT idx, srcDataT data),
Vector#(srcNum, Get#(srcDataT)) srcIfc,
Vector#(dstNum, Put#(dstDataT)) dstIfc
)(Empty) provisos(
Alias#(srcIdxT, Bit#(TLog#(srcNum))),
Alias#(dstIdxT, Bit#(TLog#(dstNum))),
Bits#(srcDataT, _srcDataSz),
Bits#(dstDataT, _dstDataSz),
FShow#(dstDataT)
);
// proposed data transfer by each src
Vector#(srcNum, Ehr#(2, Maybe#(dstIdxT))) propDstIdx <- replicateM(mkEhr(Invalid));
Vector#(srcNum, Ehr#(2, dstDataT)) propDstData <- replicateM(mkEhr(?));
// enq command that should be carried out
Vector#(dstNum, Ehr#(2, Maybe#(dstDataT))) enqDst <- replicateM(mkEhr(Invalid));
// src propose data transfer when src is not empty
// and there is no unfinished src proposal
for(Integer i = 0; i < valueOf(srcNum); i = i+1) begin
rule srcPropose(!isValid(propDstIdx[i][0]));
let d <- srcIfc[i].get;
let info = getDstInfo(fromInteger(i), d);
propDstIdx[i][0] <= Valid (info.idx);
propDstData[i][0] <= info.data;
endrule
end
// round-robin select src for each dst
Vector#(dstNum, Reg#(srcIdxT)) srcRR <- replicateM(mkReg(0));
// do selection for each dst & generate enq commands & deq src proposals
// dst which has unfinished enq command cannot select src
(* fire_when_enabled, no_implicit_conditions *)
rule dstSelectSrc;
// which src to deq (i.e. select) by each dst
Vector#(dstNum, Maybe#(srcIdxT)) deqSrcByDst = replicate(Invalid);
// each dst selects
for(Integer dst = 0; dst < valueOf(dstNum); dst = dst + 1) begin
// only select src to deq when dst has no unfinished enq command
if(!isValid(enqDst[dst][0])) begin
function Bool isFromSrc(srcIdxT src);
// src has proposed data to this dst or not
if(propDstIdx[src][1] matches tagged Valid .dstIdx &&& dstIdx == fromInteger(dst)) begin
return True;
end
else begin
return False;
end
endfunction
Maybe#(srcIdxT) whichSrc = Invalid; // the src to select
if(isFromSrc(srcRR[dst])) begin
// first check the src with priority
whichSrc = Valid (srcRR[dst]);
end
else begin
// otherwise just get one valid src
Vector#(srcNum, srcIdxT) srcIdxVec = genWith(fromInteger);
whichSrc = searchIndex(isFromSrc, srcIdxVec);
end
if(whichSrc matches tagged Valid .src) begin
// can do enq & deq
deqSrcByDst[dst] = whichSrc;
enqDst[dst][0] <= Valid (propDstData[src][1]); // set enq command
// change round robin
srcRR[dst] <= srcRR[dst] == fromInteger(valueOf(srcNum) - 1) ? 0 : srcRR[dst] + 1;
end
end
end
// deq selected src
function Bool isDeqSrc(srcIdxT src);
function Bool isMatch(Maybe#(srcIdxT) deqIdx);
return deqIdx matches tagged Valid .idx &&& idx == src ? True : False;
endfunction
return any(isMatch, deqSrcByDst);
endfunction
for(Integer i = 0; i < valueOf(srcNum); i = i+1) begin
if(isDeqSrc(fromInteger(i))) begin
propDstIdx[i][1] <= Invalid;
$display("%t XBar %m: deq src %d", $time, i);
doAssert(isValid(propDstIdx[i][1]), "src must be proposing");
end
end
endrule
// enq dst & change round robin
for(Integer i = 0; i < valueOf(dstNum); i = i+1) begin
// XXX since dstIfc.put may conflict with other rules
// the following rule should only fire when it can make progress
// otherwise it may prevent other rules from firing forever
rule doEnq(enqDst[i][1] matches tagged Valid .d);
dstIfc[i].put(d);
enqDst[i][1] <= Invalid; // reset enq command
$display("%t XBAR %m: enq dst %d ; ", $time, i, fshow(d));
endrule
end
endmodule
// XBar with latency added at src or dst (mainly for model timing)
interface XBarDelay#(numeric type srcLat, numeric type dstLat);
endinterface
module mkXBarDelay#(
function XBarDstInfo#(dstIdxT, dstDataT) getDstInfo(srcIdxT idx, srcDataT data),
Vector#(srcNum, Get#(srcDataT)) srcIfc,
Vector#(dstNum, Put#(dstDataT)) dstIfc
)(XBarDelay#(srcLat, dstLat)) provisos(
Alias#(srcIdxT, Bit#(TLog#(srcNum))),
Alias#(dstIdxT, Bit#(TLog#(dstNum))),
Bits#(srcDataT, _srcDataSz),
Bits#(dstDataT, _dstDataSz),
FShow#(dstDataT)
);
// Add latency at src side
Vector#(srcNum, Get#(srcDataT)) src = srcIfc;
if(valueof(srcLat) > 0) begin
for(Integer i = 0; i < valueof(srcNum); i = i+1) begin
Vector#(srcLat, Fifo#(2, srcDataT)) delayQ <- replicateM(mkCFFifo);
mkConnection(srcIfc[i], toPut(delayQ[0]));
for(Integer j = 0; j < valueof(srcLat) - 1; j = j+1) begin
mkConnection(toGet(delayQ[j]), toPut(delayQ[j + 1]));
end
src[i] = toGet(delayQ[valueof(srcLat) - 1]);
end
end
// Add latency at dst side
Vector#(dstNum, Put#(dstDataT)) dst = dstIfc;
if(valueof(dstLat) > 0) begin
for(Integer i = 0; i < valueof(dstNum); i = i+1) begin
Vector#(dstLat, Fifo#(2, dstDataT)) delayQ <- replicateM(mkCFFifo);
mkConnection(toGet(delayQ[0]), dstIfc[i]);
for(Integer j = 0; j < valueof(dstLat) - 1; j = j+1) begin
mkConnection(toGet(delayQ[j + 1]), toPut(delayQ[j]));
end
dst[i] = toPut(delayQ[valueof(dstLat) - 1]);
end
end
mkXBar(getDstInfo, src, dst);
endmodule
interface CrossBar#(
numeric type srcNum,
numeric type srcLat,
type srcDataT,
numeric type dstNum,
numeric type dstLat,
type dstDataT
);
interface Vector#(srcNum, FifoEnq#(srcDataT)) srcIfc;
interface Vector#(dstNum, FifoDeq#(dstDataT)) dstIfc;
endinterface
module mkCrossBar#(
function XBarDstInfo#(dstIdxT, dstDataT) getDstInfo(srcIdxT idx, srcDataT data)
)(
CrossBar#(srcNum, srcLat, srcDataT, dstNum, dstLat, dstDataT)
) provisos(
Alias#(srcIdxT, Bit#(TLog#(srcNum))),
Alias#(dstIdxT, Bit#(TLog#(dstNum))),
Bits#(srcDataT, _srcDataSz),
Bits#(dstDataT, _dstDataSz),
FShow#(dstDataT)
);
// in/out FIFOs
Vector#(srcNum, Fifo#(2, srcDataT)) srcQ <- replicateM(mkCFFifo);
Vector#(dstNum, Fifo#(2, dstDataT)) dstQ <- replicateM(mkCFFifo);
XBarDelay#(srcLat, dstLat) xbar <- mkXBarDelay(getDstInfo, map(toGet, srcQ), map(toPut, dstQ));
interface srcIfc = map(toFifoEnq, srcQ);
interface dstIfc = map(toFifoDeq, dstQ);
endmodule

View File

@@ -0,0 +1,849 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
`include "ProcConfig.bsv"
import Types::*;
import MemoryTypes::*;
import Amo::*;
import Cntrs::*;
import Vector::*;
import ConfigReg::*;
import FIFO::*;
import GetPut::*;
import ClientServer::*;
import CCTypes::*;
import ICRqMshr::*;
import IPRqMshr::*;
import CCPipe::*;
import L1Pipe ::*;
import FShow::*;
import DefaultValue::*;
import Fifo::*;
import CacheUtils::*;
import Performance::*;
import LatencyTimer::*;
import RandomReplace::*;
export ICRqStuck(..);
export IPRqStuck(..);
export IBank(..);
export mkIBank;
// L1 I$
// although pRq never appears in dependency chain
// we still need pRq MSHR to limit the number of pRq
// and thus limit the size of rsToPIndexQ
typedef struct {
Addr addr;
ICRqState state;
Bool waitP;
} ICRqStuck deriving(Bits, Eq, FShow);
typedef IPRqMshrStuck IPRqStuck;
interface IBank#(
numeric type supSz, // superscalar size
numeric type lgBankNum,
numeric type wayNum,
numeric type indexSz,
numeric type tagSz,
numeric type cRqNum,
numeric type pRqNum
);
interface ChildCacheToParent#(Bit#(TLog#(wayNum)), void) to_parent;
interface InstServer#(supSz) to_proc; // to child, i.e. processor
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface Get#(ICRqStuck) cRqStuck;
interface Get#(IPRqStuck) pRqStuck;
// security: flush
method Action flush;
method Bool flush_done;
// performance
method Action setPerfStatus(Bool stats);
method Data getPerfData(L1IPerfType t);
endinterface
module mkIBank#(
Bit#(lgBankNum) bankId,
module#(ICRqMshr#(cRqNum, wayT, tagT, procRqT, resultT)) mkICRqMshrLocal,
module#(IPRqMshr#(pRqNum)) mkIPRqMshrLocal,
module#(L1Pipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT, pRqIdxT)) mkL1Pipeline
)(
IBank#(supSz, lgBankNum, wayNum, indexSz, tagSz, cRqNum, pRqNum)
) provisos(
Alias#(wayT, Bit#(TLog#(wayNum))),
Alias#(indexT, Bit#(indexSz)),
Alias#(tagT, Bit#(tagSz)),
Alias#(cRqIdxT, Bit#(TLog#(cRqNum))),
Alias#(pRqIdxT, Bit#(TLog#(pRqNum))),
Alias#(cacheOwnerT, Maybe#(cRqIdxT)), // owner cannot be pRq
Alias#(cacheInfoT, CacheInfo#(tagT, Msi, void, cacheOwnerT, void)),
Alias#(ramDataT, RamData#(tagT, Msi, void, cacheOwnerT, void, Line)),
Alias#(procRqT, ProcRqToI),
Alias#(cRqToPT, CRqMsg#(wayT, void)),
Alias#(cRsToPT, CRsMsg#(void)),
Alias#(pRqFromPT, PRqMsg#(void)),
Alias#(pRsFromPT, PRsMsg#(wayT, void)),
Alias#(pRqRsFromPT, PRqRsMsg#(wayT, void)),
Alias#(cRqSlotT, ICRqSlot#(wayT, tagT)), // cRq MSHR slot
Alias#(l1CmdT, L1Cmd#(indexT, cRqIdxT, pRqIdxT)),
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, void, cacheOwnerT, void, RandRepInfo, Line, l1CmdT)),
Alias#(resultT, Vector#(supSz, Maybe#(Instruction))),
// requirements
FShow#(pipeOutT),
Add#(tagSz, a__, AddrSz),
// make sure: cRqNum <= wayNum
Add#(cRqNum, b__, wayNum),
Add#(TAdd#(tagSz, indexSz), TAdd#(lgBankNum, LgLineSzBytes), AddrSz)
);
ICRqMshr#(cRqNum, wayT, tagT, procRqT, resultT) cRqMshr <- mkICRqMshrLocal;
IPRqMshr#(pRqNum) pRqMshr <- mkIPRqMshrLocal;
L1Pipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT, pRqIdxT) pipeline <- mkL1Pipeline;
Fifo#(1, Addr) rqFromCQ <- mkBypassFifo;
Fifo#(2, cRsToPT) rsToPQ <- mkCFFifo;
Fifo#(2, cRqToPT) rqToPQ <- mkCFFifo;
Fifo#(2, pRqRsFromPT) fromPQ <- mkCFFifo;
FIFO#(MshrIndex#(cRqIdxT, pRqIdxT)) rsToPIndexQ <- mkSizedFIFO(valueOf(TAdd#(cRqNum, pRqNum)));
FIFO#(cRqIdxT) rqToPIndexQ <- mkSizedFIFO(valueOf(cRqNum));
// temp fifo for pipelineResp & sendRsToP (reduce conflict)
FIFO#(cRqIdxT) rqToPIndexQ_pipelineResp <- mkFIFO;
FIFO#(cRqIdxT) rqToPIndexQ_sendRsToP <- mkFIFO;
// index Q to order all in flight cRq for in-order resp
FIFO#(cRqIdxT) cRqIndexQ <- mkSizedFIFO(valueof(cRqNum));
`ifdef DEBUG_ICACHE
// id for each cRq, incremented when each new req comes
Reg#(Bit#(64)) cRqId <- mkReg(0);
// FIFO to signal the id of cRq that is performed
// FIFO has 0 cycle latency to match L1 D$ resp latency
Fifo#(1, DebugICacheResp) cRqDoneQ <- mkBypassFifo;
`endif
// security flush
`ifdef SECURITY
Reg#(Bool) flushDone <- mkReg(True);
Reg#(Bool) flushReqStart <- mkReg(False);
Reg#(Bool) flushReqDone <- mkReg(False);
Reg#(Bool) flushRespDone <- mkReg(False);
Reg#(indexT) flushIndex <- mkReg(0);
Reg#(wayT) flushWay <- mkReg(0);
`else
Bool flushDone = True;
`endif
`ifdef PERF_COUNT
Reg#(Bool) doStats <- mkConfigReg(False);
Count#(Data) ldCnt <- mkCount(0);
Count#(Data) ldMissCnt <- mkCount(0);
Count#(Data) ldMissLat <- mkCount(0);
LatencyTimer#(cRqNum, 10) latTimer <- mkLatencyTimer;
function Action incrReqCnt;
action
if(doStats) begin
ldCnt.incr(1);
end
endaction
endfunction
function Action incrMissCnt(cRqIdxT idx);
action
let lat <- latTimer.done(idx);
if(doStats) begin
ldMissLat.incr(zeroExtend(lat));
ldMissCnt.incr(1);
end
endaction
endfunction
`endif
function tagT getTag(Addr a) = truncateLSB(a);
// XXX since I$ may be requested by processor constantly
// cRq may come at every cycle, so we must make cRq has lower priority than pRq/pRs
// otherwise the whole system may deadlock/livelock
// we stop accepting cRq when we need to flush for security
rule cRqTransfer(flushDone);
Addr addr <- toGet(rqFromCQ).get;
`ifdef DEBUG_ICACHE
procRqT r = ProcRqToI {addr: addr, id: cRqId};
cRqId <= cRqId + 1;
`else
procRqT r = ProcRqToI {addr: addr};
`endif
cRqIdxT n <- cRqMshr.getEmptyEntryInit(r);
// send to pipeline
pipeline.send(CRq (L1PipeRqIn {
addr: r.addr,
mshrIdx: n
}));
// enq to indexQ for in order resp
cRqIndexQ.enq(n);
`ifdef PERF_COUNT
// performance counter: cRq type
incrReqCnt;
`endif
$display("%t I %m cRqTransfer: ", $time,
fshow(n), " ; ",
fshow(r)
);
endrule
// this descending urgency is necessary to avoid deadlock/livelock
(* descending_urgency = "pRqTransfer, cRqTransfer" *)
rule pRqTransfer(fromPQ.first matches tagged PRq .req);
fromPQ.deq;
pRqIdxT n <- pRqMshr.getEmptyEntryInit(req);
// send to pipeline
pipeline.send(PRq (L1PipeRqIn {
addr: req.addr,
mshrIdx: n
}));
$display("%t I %m pRqTransfer: ", $time,
fshow(n), " ; ",
fshow(req)
);
endrule
// this descending urgency is necessary to avoid deadlock/livelock
(* descending_urgency = "pRsTransfer, cRqTransfer" *)
rule pRsTransfer(fromPQ.first matches tagged PRs .resp);
fromPQ.deq;
pipeline.send(PRs (L1PipePRsIn {
addr: resp.addr,
toState: S,
data: resp.data,
way: resp.id
}));
$display("%t I %m pRsTransfer: ", $time, fshow(resp));
doAssert(resp.toState == S && isValid(resp.data), "I$ must upgrade to S with data");
endrule
`ifdef SECURITY
// start flush when cRq MSHR is empty
rule startFlushReq(!flushDone && !flushReqStart && cRqMshr.emptyForFlush);
flushReqStart <= True;
endrule
(* descending_urgency = "pRsTransfer, flushTransfer" *)
(* descending_urgency = "pRqTransfer, flushTransfer" *)
rule flushTransfer(!flushDone && flushReqStart && !flushReqDone);
// We allocate a pRq MSHR entry for 2 reasons:
// (1) reuse the pRq logic to send resp to parent
// (2) control the number of downgrade resp to avoid stalling the cache
// pipeline
pRqIdxT n <- pRqMshr.getEmptyEntryInit(PRqMsg {
addr: ?,
toState: I,
child: ?
});
pipeline.send(Flush (L1PipeFlushIn {
index: flushIndex,
way: flushWay,
mshrIdx: n
}));
// increment flush index/way
if (flushWay < fromInteger(valueof(wayNum) - 1)) begin
flushWay <= flushWay + 1;
end
else begin
flushWay <= 0;
flushIndex <= flushIndex + 1; // index num should be power of 2
if (flushIndex == maxBound) begin
flushReqDone <= True;
end
end
$display("%t I %m flushTransfer: ", $time, fshow(n), " ; ",
fshow(flushIndex), " ; ", fshow(flushWay));
endrule
`endif
rule sendRsToP_cRq(rsToPIndexQ.first matches tagged CRq .n);
rsToPIndexQ.deq;
// get cRq replacement info
procRqT req = cRqMshr.sendRsToP_cRq.getRq(n);
cRqSlotT slot = cRqMshr.sendRsToP_cRq.getSlot(n);
// send resp to parent
cRsToPT resp = CRsMsg {
addr: {slot.repTag, truncate(req.addr)}, // get bank id & index from req
toState: I,
data: Invalid, // I$ never downgrade with data to writeback
child: ?
};
rsToPQ.enq(resp);
// req parent for upgrade now
// (prevent parent resp from coming to release MSHR entry before replace resp is sent)
rqToPIndexQ_sendRsToP.enq(n);
$display("%t I %m sendRsToP: ", $time,
fshow(rsToPIndexQ.first)," ; ",
fshow(req), " ; ",
fshow(slot), " ; ",
fshow(resp)
);
endrule
rule sendRsToP_pRq(rsToPIndexQ.first matches tagged PRq .n);
rsToPIndexQ.deq;
// get pRq info & send resp & release MSHR entry
pRqFromPT req = pRqMshr.sendRsToP_pRq.getRq(n);
cRsToPT resp = CRsMsg {
addr: req.addr,
toState: I, // I$ must downgrade to I
data: Invalid, // I$ never downgrade with data to writeback
child: ?
};
rsToPQ.enq(resp);
pRqMshr.sendRsToP_pRq.releaseEntry(n); // mshr entry released
$display("%t I %m sendRsToP: ", $time,
fshow(rsToPIndexQ.first), " ; ",
fshow(req), " ; ",
fshow(resp)
);
doAssert(req.toState == I, "I$ only has downgrade req to I");
endrule
rule sendRqToP;
rqToPIndexQ.deq;
cRqIdxT n = rqToPIndexQ.first;
procRqT req = cRqMshr.sendRqToP.getRq(n);
cRqSlotT slot = cRqMshr.sendRqToP.getSlot(n);
cRqToPT cRqToP = CRqMsg {
addr: req.addr,
fromState: I, // I$ upgrade from I
toState: S, // I$ upgrade to S
canUpToE: False,
id: slot.way,
child: ?
};
rqToPQ.enq(cRqToP);
$display("%t I %m sendRqToP: ", $time,
fshow(n), " ; ",
fshow(req), " ; ",
fshow(slot), " ; ",
fshow(cRqToP)
);
`ifdef PERF_COUNT
// performance counter: start miss timer
latTimer.start(n);
`endif
endrule
// last stage of pipeline: process req
// XXX: in L1, pRq cannot exist in dependency chain
// because there are only two ways to include pRq into chain
// (1) append to a cRq that could finish, but such cRq must have been directly reponded
// (2) overtake cRq (S->M), but such downgrade can be done instaneously without the need of chaining
// (this cannot happen in I$)
// Thus, dependency chain in L1 only contains cRq
// pipeline outputs
pipeOutT pipeOut = pipeline.first;
ramDataT ram = pipeOut.ram;
// get proc req to select from cRqMshr
procRqT pipeOutCRq = cRqMshr.pipelineResp.getRq(
case(pipeOut.cmd) matches
tagged L1CRq .n: (n);
default: (fromMaybe(0, ram.info.owner)); // L1PRs
endcase
);
// function to get superscaler inst read result
function resultT readInst(Line line, Addr addr);
Vector#(LineSzInst, Instruction) instVec = unpack(pack(line));
// the start offset for reading inst
LineInstOffset startSel = getLineInstOffset(addr);
// calculate the maximum inst count that could be read from line
LineInstOffset maxCntMinusOne = maxBound - startSel;
// read inst superscalaer
resultT val = ?;
for(Integer i = 0; i < valueof(supSz); i = i+1) begin
if(fromInteger(i) <= maxCntMinusOne) begin
LineInstOffset sel = startSel + fromInteger(i);
val[i] = Valid (instVec[sel]);
end
else begin
val[i] = Invalid;
end
end
return val;
endfunction
// function to process cRq hit (MSHR slot may have garbage)
function Action cRqHit(cRqIdxT n, procRqT req);
action
$display("%t I %m pipelineResp: Hit func: ", $time,
fshow(n), " ; ",
fshow(req)
);
// check tag & cs: even this function is called by pRs, tag should match,
// because tag is written into cache before sending req to parent
doAssert(ram.info.tag == getTag(req.addr) && ram.info.cs == S,
"cRqHit but tag or cs incorrect"
);
// deq pipeline or swap in successor
Maybe#(cRqIdxT) succ = cRqMshr.pipelineResp.getSucc(n);
pipeline.deqWrite(succ, RamData {
info: CacheInfo {
tag: getTag(req.addr), // should be the same as original tag
cs: ram.info.cs, // use cs in ram
dir: ?,
owner: succ,
other: ?
},
line: ram.line
}, True); // hit, so update rep info
// process req to get superscalar inst read results
// set MSHR entry as Done & save inst results
let instResult = readInst(ram.line, req.addr);
cRqMshr.pipelineResp.setResult(n, instResult);
cRqMshr.pipelineResp.setStateSlot(n, Done, ?);
$display("%t I %m pipelineResp: Hit func: update ram: ", $time,
fshow(succ), " ; ", fshow(instResult)
);
`ifdef DEBUG_ICACHE
// signal that this req is performed
cRqDoneQ.enq(DebugICacheResp {
id: req.id,
line: ram.line
});
`endif
endaction
endfunction
rule pipelineResp_cRq(pipeOut.cmd matches tagged L1CRq .n);
$display("%t I %m pipelineResp: ", $time, fshow(pipeOut));
procRqT procRq = pipeOutCRq;
$display("%t I %m pipelineResp: cRq: ", $time, fshow(n), " ; ", fshow(procRq));
// find end of dependency chain
Maybe#(cRqIdxT) cRqEOC = cRqMshr.pipelineResp.searchEndOfChain(procRq.addr);
// function to process cRq miss without replacement (MSHR slot may have garbage)
function Action cRqMissNoReplacement;
action
cRqSlotT cSlot = cRqMshr.pipelineResp.getSlot(n);
// it is impossible in L1 to have slot.waitP == True in this function
// because cRq is not set to Depend when pRq invalidates it (pRq just directly resp)
// and this func is only called when cs < S (otherwise will hit)
// because L1 has no children to wait for
doAssert(!cSlot.waitP && ram.info.cs == I, "waitP must be false and cs must be I");
// Thus we must send req to parent
// XXX first send to a temp indexQ to avoid conflict, then merge to rqToPIndexQ later
rqToPIndexQ_pipelineResp.enq(n);
// update mshr
cRqMshr.pipelineResp.setStateSlot(n, WaitSt, ICRqSlot {
way: pipeOut.way, // use way from pipeline
repTag: ?, // no replacement
waitP: True // must fetch from parent
});
// deq pipeline & set owner, tag
pipeline.deqWrite(Invalid, RamData {
info: CacheInfo {
tag: getTag(procRq.addr), // tag may be garbage if cs == I
cs: ram.info.cs,
dir: ?,
owner: Valid (n), // owner is req itself
other: ?
},
line: ram.line
}, False);
endaction
endfunction
// function to do replacement for cRq
function Action cRqReplacement;
action
// deq pipeline
pipeline.deqWrite(Invalid, RamData {
info: CacheInfo {
tag: getTag(procRq.addr), // set to req tag (old tag is replaced right now)
cs: I,
dir: ?,
owner: Valid (n), // owner is req itself
other: ?
},
line: ? // data is no longer used
}, False);
doAssert(ram.info.cs == S, "I$ replacement only replace S line");
// update MSHR to save replaced tag
// although we send req to parent later (when resp to parent is sent)
// we set state to WaitSt now, since the req to parent is already on schedule
cRqMshr.pipelineResp.setStateSlot(n, WaitSt, ICRqSlot {
way: pipeOut.way, // use way from pipeline
repTag: ram.info.tag, // tag being replaced for sending rs to parent
waitP: True
});
// send replacement resp to parent
rsToPIndexQ.enq(CRq (n));
endaction
endfunction
// function to set cRq to Depend, and make no further change to cache
function Action cRqSetDepNoCacheChange;
action
cRqMshr.pipelineResp.setStateSlot(n, Depend, defaultValue);
pipeline.deqWrite(Invalid, pipeOut.ram, False);
endaction
endfunction
if(ram.info.owner matches tagged Valid .cOwner) begin
if(cOwner != n) begin
// owner is another cRq, so must just go through tag match
// tag match must be hit (because replacement algo won't give a way with owner)
doAssert(ram.info.cs == S && ram.info.tag == getTag(procRq.addr),
"cRq should hit in tag match"
);
// should be added to a cRq in dependency chain & deq from pipeline
doAssert(isValid(cRqEOC), "cRq hit on another cRq, cRqEOC must be true");
cRqMshr.pipelineResp.setSucc(fromMaybe(?, cRqEOC), Valid (n));
cRqSetDepNoCacheChange;
$display("%t I %m pipelineResp: cRq: own by other cRq ", $time,
fshow(cOwner), ", depend on cRq ", fshow(cRqEOC)
);
end
else begin
// owner is myself, so must be swapped in
// tag should match, since always swapped in by cRq, cs = S
doAssert(ram.info.tag == getTag(procRq.addr) && ram.info.cs == S,
"cRq swapped in by previous cRq, tag must match & cs = S"
);
// Hit
$display("%t I %m pipelineResp: cRq: own by itself, hit", $time);
cRqHit(n, procRq);
end
end
else begin
// cache has no owner, cRq must just go through tag match
// check for cRqEOC to append to dependency chain
if(cRqEOC matches tagged Valid .k) begin
$display("%t I %m pipelineResp: cRq: no owner, depend on cRq ", $time, fshow(k));
cRqMshr.pipelineResp.setSucc(k, Valid (n));
cRqSetDepNoCacheChange;
end
else if(ram.info.cs == I || ram.info.tag == getTag(procRq.addr)) begin
// No Replacement necessary
if(ram.info.cs > I) begin
$display("%t I %m pipelineResp: cRq: no owner, hit", $time);
cRqHit(n, procRq);
end
else begin
$display("%t I %m pipelineResp: cRq: no owner, miss no replace", $time);
cRqMissNoReplacement;
end
end
else begin
$display("%t I %m pipelineResp: cRq: no owner, replace", $time);
cRqReplacement;
end
end
endrule
rule pipelineResp_pRs(pipeOut.cmd == L1PRs);
$display("%t I %m pipelineResp: ", $time, fshow(pipeOut));
$display("%t I %m pipelineResp: pRs: ", $time);
if(ram.info.owner matches tagged Valid .cOwner) begin
procRqT procRq = pipeOutCRq;
doAssert(ram.info.cs == S && ram.info.tag == getTag(procRq.addr),
"pRs must be a hit"
);
cRqHit(cOwner, procRq);
`ifdef PERF_COUNT
// performance counter: miss cRq
incrMissCnt(cOwner);
`endif
end
else begin
doAssert(False, ("pRs owner must match some cRq"));
end
endrule
rule pipelineResp_pRq(pipeOut.cmd matches tagged L1PRq .n);
pRqFromPT pRq = pRqMshr.pipelineResp.getRq(n);
$display("%t I %m pipelineResp: pRq: ", $time, fshow(n), " ; ", fshow(pRq));
doAssert(pRq.toState == I, "I$ pRq only downgrade to I");
// pRq is never in dependency chain, so it is never swapped in
// pRq must go through tag match, which either returns a tag matched way or asserts pRqMiss
// In I$ a tag matched way should always be processed since pRq always downgrades to I
// pRq is always directly handled: either dropped or Done
if(pipeOut.pRqMiss) begin
$display("%t I %m pipelineResp: pRq: drop", $time);
// pRq can be directly dropped, no successor (since just go through pipeline)
pRqMshr.pipelineResp.releaseEntry(n);
pipeline.deqWrite(Invalid, pipeOut.ram, False);
end
else begin
$display("%t I %m pipelineResp: pRq: valid process", $time);
// should process pRq
doAssert(ram.info.cs == S && pRq.toState == I && ram.info.tag == getTag(pRq.addr),
"pRq should be processed"
);
// line cannot be owned, because
// (1) pRq never own cache line
// (2) if owned by cRq, cRq would have hit and released ownership
doAssert(ram.info.owner == Invalid, "pRq cannot hit on line owned by anyone");
// write ram: set cs to I
pipeline.deqWrite(Invalid, RamData {
info: CacheInfo {
tag: ram.info.tag,
cs: I, // I$ is always downgraded by pRq to I
dir: ?,
owner: Invalid, // no successor
other: ?
},
line: ? // line is not useful
}, False);
// pRq is done
pRqMshr.pipelineResp.setDone(n);
// send resp to parent
rsToPIndexQ.enq(PRq (n));
end
endrule
`ifdef SECURITY
rule pipelineResp_flush(
!flushDone &&& !flushRespDone &&&
pipeOut.cmd matches tagged L1Flush .flush
);
pRqIdxT n = flush.mshrIdx;
$display("%t I %m pipelineResp: flush: ", $time, fshow(flush));
// During flush, cRq MSHR is empty, so cache line cannot have owner
doAssert(ram.info.owner == Invalid, "flushing line cannot have owner");
// flush always goes through cache pipeline, and is directly handled
// here: either dropped or Done
if(ram.info.cs == I) begin
$display("%t I %m pipelineResp: flush: drop", $time);
// flush can be directly dropped
pRqMshr.pipelineResp.releaseEntry(n);
end
else begin
$display("%t I %m pipelineResp: flush: valid process", $time);
pRqMshr.pipelineResp.setDone(n);
rsToPIndexQ.enq(PRq (n));
// record the flushed addr in MSHR so that sendRsToP rule knows
// which addr is invalidated
Bit#(LgLineSzBytes) offset = 0;
Addr addr = {ram.info.tag, flush.index, bankId, offset};
pRqMshr.pipelineResp.setFlushAddr(n, addr);
end
// always clear the cache line
pipeline.deqWrite(Invalid, RamData {
info: CacheInfo {
tag: ?,
cs: I, // downgraded to I
dir: ?,
owner: Invalid, // no successor
other: ?
},
line: ?
}, False);
// check if we have finished all flush
if (flush.index == maxBound &&
pipeOut.way == fromInteger(valueof(wayNum) - 1)) begin
flushRespDone <= True;
end
endrule
rule completeFlush(!flushDone && flushReqStart && flushReqDone && flushRespDone);
flushDone <= True;
flushReqStart <= False;
flushReqDone <= False;
flushRespDone <= False;
endrule
`endif
// merge rq to parent index into indexQ
rule rqIndexFromPipelineResp;
let n <- toGet(rqToPIndexQ_pipelineResp).get;
rqToPIndexQ.enq(n);
endrule
(* descending_urgency = "rqIndexFromPipelineResp, rqIndexFromSendRsToP" *)
rule rqIndexFromSendRsToP;
let n <- toGet(rqToPIndexQ_sendRsToP).get;
rqToPIndexQ.enq(n);
endrule
interface ChildCacheToParent to_parent;
interface rsToP = toFifoDeq(rsToPQ);
interface rqToP = toFifoDeq(rqToPQ);
interface fromP = toFifoEnq(fromPQ);
endinterface
interface InstServer to_proc;
interface Put req;
method Action put(Addr addr);
rqFromCQ.enq(addr);
endmethod
endinterface
interface Get resp;
method ActionValue#(resultT) get if(
cRqMshr.sendRsToC.getResult(cRqIndexQ.first) matches tagged Valid .inst
);
cRqIndexQ.deq;
cRqMshr.sendRsToC.releaseEntry(cRqIndexQ.first); // release MSHR entry
$display("%t I %m sendRsToC: ", $time,
fshow(cRqIndexQ.first), " ; ",
fshow(inst)
);
return inst;
endmethod
endinterface
`ifdef DEBUG_ICACHE
interface done = toGet(cRqDoneQ);
`endif
endinterface
interface Get cRqStuck;
method ActionValue#(ICRqStuck) get;
let s <- cRqMshr.stuck.get;
return ICRqStuck {
addr: s.req.addr,
state: s.state,
waitP: s.waitP
};
endmethod
endinterface
interface pRqStuck = pRqMshr.stuck;
`ifdef SECURITY
method Action flush if(flushDone);
flushDone <= False;
endmethod
method flush_done = flushDone._read;
`else
method flush = noAction;
method flush_done = True;
`endif
method Action setPerfStatus(Bool stats);
`ifdef PERF_COUNT
doStats <= stats;
`else
noAction;
`endif
endmethod
method Data getPerfData(L1IPerfType t);
return (case(t)
`ifdef PERF_COUNT
L1ILdCnt: ldCnt;
L1ILdMissCnt: ldMissCnt;
L1ILdMissLat: ldMissLat;
`endif
default: 0;
endcase);
endmethod
endmodule
// Scheduling note
// cRqTransfer (toC.req.put): write new cRq MSHR entry, cRqMshr.getEmptyEntry
// pRqTransfer: write new pRq MSHR entry, pRqMshr.getEmptyEntry
// pRsTransfer: -
// sendRsToC (toC.resp.get): read cRq MSHR result, releaseEntry
// sendRsToP_cRq: read cRq MSHR req/slot that is replacing
// sendRsToP_pRq: read pRq MSHR entry that is responding, pRqMshr.releaseEntry
// sendRqToP: read cRq MSHR req/slot that is requesting parent
// pipelineResp_cRq:
// -- read cRq MSHR req/state/slot currently processed
// -- write cRq MSHR state/slot/result currently processed
// -- write succ of some existing cRq MSHR entry (in WaitNewTag or WaitSt)
// -- read all state/req/succ in cRq MSHR entry (searchEOC)
// -- not affected by write in cRqTransfer (state change is Empty->Init)
// -- not affected by write in sendRsC (state change is Done->Empty)
// pipelineResp_pRs:
// -- read cRq MSHR req/succ, write cRq MSHR state/slot/result
// pipelineResp_pRq:
// -- r/w pRq MSHR entry, pRqMshr.releaseEntry
// ---- conflict analysis ----
// XXXTransfer is conflict with each other
// Impl of getEmptyEntry and releaseEntry ensures that they are not on the same entry (e.g. cRqTransfer v.s. sendRsToC)
// XXXTransfer should operate on different cRq/pRq from other rules
// sendRsToC is ordered after pipelineResp to save 1 cycle in I$ latency
// sendRqToP and sendRsToP_cRq are read only
// sendRsToP_pRq is operating on different pRq from pipelineResp_pRq (since we use CF index FIFO)
// ---- conclusion ----
// rules/methods are operating on different MSHR entries, except pipelineResp v.s. sendRsToC
// we have 5 ports from cRq MSHR
// 1. cRqTransfer
// 2. sendRsToC
// 3. sendRsToP_cRq
// 4. sendRqToP
// 5. pipelineResp
// we have 3 ports from pRq MSHR
// 1. pRqTransfer
// 2. sendRsToP_pRq
// 3. pipelineResp
// safe version: use EHR ports
// sendRsToP_cRq/sendRqToP/pipelineResp < sendRsToC < cRqTransfer
// pipelineResp < sendRsToP_pRq < pRqTransfer
// (note there is no bypass path from pipelineResp to sendRsToP_pRq since sendRsToP_pRq only reads pRq)
// unsafe version: all reads read the original reg value, except sendRsToC, which should bypass from pipelineResp
// all writes are cononicalized. NOTE: writes of sendRsToC should be after pipelineResp
// we maintain the logical ordering in safe version

View File

@@ -0,0 +1,353 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import GetPut::*;
import RegFile::*;
import FIFO::*;
import FShow::*;
import Types::*;
import CCTypes::*;
import DefaultValue::*;
import Ehr::*;
import MshrDeadlockChecker::*;
// MSHR dependency chain invariant:
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
// in L1, pRq is always handled immediately, so cRq never depends on pRq and vice versa
// CRq MSHR entry state
typedef enum {
Empty,
Init,
WaitSt, // wait pRs/cRs to come
Done, // resp is in index FIFO
Depend
} ICRqState deriving(Bits, Eq, FShow);
// CRq info returned to outside
typedef struct {
wayT way; // the way to occupy
tagT repTag; // tag being replaced
Bool waitP; // waiting for parent resp
} ICRqSlot#(type wayT, type tagT) deriving(Bits, Eq, FShow);
instance DefaultValue#(ICRqSlot#(wayT, tagT));
defaultValue = ICRqSlot {
way: ?,
repTag: ?,
waitP: False
};
endinstance
typedef struct {
reqT req;
ICRqState state;
Bool waitP;
} ICRqMshrStuck#(type reqT) deriving(Bits, Eq, FShow);
// MSHR data is purely for replacement resp to parent
// (resp to processor is done immediately, no data buffering needed)
// port for sendRsToP_cRq
interface ICRqMshr_sendRsToP_cRq#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method ICRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
endinterface
// port for sendRqToP
interface ICRqMshr_sendRqToP#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method ICRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
endinterface
// port for pipelineResp
interface ICRqMshr_pipelineResp#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT,
type resultT
);
method ICRqState getState(Bit#(TLog#(cRqNum)) n);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method ICRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
method Action setResult(Bit#(TLog#(cRqNum)) n, resultT r); // set a valid result
method Action setStateSlot(
Bit#(TLog#(cRqNum)) n,
ICRqState state,
ICRqSlot#(wayT, tagT) slot
);
// can only change state to NON-Empty state
// cannot be used to release MSHR entry (use releaseSlot instead)
method Maybe#(Bit#(TLog#(cRqNum))) getSucc(Bit#(TLog#(cRqNum)) n);
method Action setSucc(Bit#(TLog#(cRqNum)) n, Maybe#(Bit#(TLog#(cRqNum))) succ);
// index in setSucc is usually different from other getXXX methods
// find existing cRq which has gone through pipeline, but not in Done state, and has not successor
// i.e. search the end of dependency chain
method Maybe#(Bit#(TLog#(cRqNum))) searchEndOfChain(Addr addr);
endinterface
interface ICRqMshr_sendRsToC#(
numeric type cRqNum,
type resultT
);
method Action releaseEntry(Bit#(TLog#(cRqNum)) n);
method Maybe#(resultT) getResult(Bit#(TLog#(cRqNum)) n);
endinterface
interface ICRqMshr#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT, // child req type
type resultT // inst result type
);
// port for cRqTransfer, initialization is done inside method
method ActionValue#(Bit#(TLog#(cRqNum))) getEmptyEntryInit(reqT r);
// port for sendRsToC
interface ICRqMshr_sendRsToC#(cRqNum, resultT) sendRsToC;
// port for sendRsToP_cRq
interface ICRqMshr_sendRsToP_cRq#(cRqNum, wayT, tagT, reqT) sendRsToP_cRq;
// port for sendRqToP
interface ICRqMshr_sendRqToP#(cRqNum, wayT, tagT, reqT) sendRqToP;
// port for pipelineResp
interface ICRqMshr_pipelineResp#(cRqNum, wayT, tagT, reqT, resultT) pipelineResp;
// port for security flush
method Bool emptyForFlush;
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface Get#(ICRqMshrStuck#(reqT)) stuck;
endinterface
//////////////////
// safe version //
//////////////////
module mkICRqMshrSafe#(
function Addr getAddrFromReq(reqT r)
)(
ICRqMshr#(cRqNum, wayT, tagT, reqT, resultT)
) provisos (
Alias#(cRqIndexT, Bit#(TLog#(cRqNum))),
Alias#(slotT, ICRqSlot#(wayT, tagT)),
Alias#(wayT, Bit#(_waySz)),
Alias#(tagT, Bit#(_tagSz)),
Bits#(reqT, _reqSz),
Bits#(resultT, _resultTSz)
);
// EHR ports
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
Integer cRqTransfer_port = 2;
Integer sendRsToC_port = 1; // create a bypass behavior from pipelineResp to sendRsToC (to save a cycle)
Integer pipelineResp_port = 0;
Integer sendRqToP_port = 0; // sendRqToP is read only
Integer sendRsToP_cRq_port = 0; // sendRsToP_cRq is read only
Integer flush_port = 0; // flush port is read only
// MSHR entry state
Vector#(cRqNum, Ehr#(3, ICRqState)) stateVec <- replicateM(mkEhr(Empty));
// cRq req contents
Vector#(cRqNum, Ehr#(3, reqT)) reqVec <- replicateM(mkEhr(?));
// cRq mshr slots
Vector#(cRqNum, Ehr#(3, slotT)) slotVec <- replicateM(mkEhr(defaultValue));
// result
Vector#(cRqNum, Ehr#(3, Maybe#(resultT))) resultVec <- replicateM(mkEhr(Invalid));
// successor valid bit
Vector#(cRqNum, Ehr#(3, Bool)) succValidVec <- replicateM(mkEhr(False));
// successor MSHR index
RegFile#(cRqIndexT, cRqIndexT) succFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
// empty entry FIFO
FIFO#(cRqIndexT) emptyEntryQ <- mkSizedFIFO(valueOf(cRqNum));
// empty entry FIFO needs initialization
Reg#(Bool) inited <- mkReg(False);
Reg#(cRqIndexT) initIdx <- mkReg(0);
rule initEmptyEntry(!inited);
emptyEntryQ.enq(initIdx);
initIdx <= initIdx + 1;
if(initIdx == fromInteger(valueOf(cRqNum) - 1)) begin
inited <= True;
$display("%t ICRqMshrSafe %m: init empty entry done", $time);
end
endrule
`ifdef CHECK_DEADLOCK
MshrDeadlockChecker#(cRqNum) checker <- mkMshrDeadlockChecker;
FIFO#(ICRqMshrStuck#(reqT)) stuckQ <- mkFIFO1;
(* fire_when_enabled *)
rule checkDeadlock;
let stuckIdx <- checker.getStuckIdx;
if(stuckIdx matches tagged Valid .n) begin
stuckQ.enq(ICRqMshrStuck {
req: reqVec[n][0],
state: stateVec[n][0],
waitP: slotVec[n][0].waitP
});
end
endrule
`endif
method ActionValue#(cRqIndexT) getEmptyEntryInit(reqT r) if(inited);
emptyEntryQ.deq;
cRqIndexT n = emptyEntryQ.first;
stateVec[n][cRqTransfer_port] <= Init;
slotVec[n][cRqTransfer_port] <= defaultValue;
resultVec[n][cRqTransfer_port] <= Invalid;
succValidVec[n][cRqTransfer_port] <= False;
reqVec[n][cRqTransfer_port] <= r;
`ifdef CHECK_DEADLOCK
checker.initEntry(n);
`endif
return n;
endmethod
interface ICRqMshr_sendRsToC sendRsToC;
method Action releaseEntry(cRqIndexT n) if(inited);
emptyEntryQ.enq(n);
stateVec[n][sendRsToC_port] <= Empty;
`ifdef CHECK_DEADLOCK
checker.releaseEntry(n);
`endif
endmethod
method Maybe#(resultT) getResult(Bit#(TLog#(cRqNum)) n);
return resultVec[n][sendRsToC_port];
endmethod
endinterface
interface ICRqMshr_sendRsToP_cRq sendRsToP_cRq;
method reqT getRq(cRqIndexT n);
return reqVec[n][sendRsToP_cRq_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][sendRsToP_cRq_port];
endmethod
endinterface
interface ICRqMshr_sendRqToP sendRqToP;
method reqT getRq(cRqIndexT n);
return reqVec[n][sendRqToP_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][sendRqToP_port];
endmethod
endinterface
interface ICRqMshr_pipelineResp pipelineResp;
method ICRqState getState(cRqIndexT n);
return stateVec[n][pipelineResp_port];
endmethod
method reqT getRq(cRqIndexT n);
return reqVec[n][pipelineResp_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][pipelineResp_port];
endmethod
method Action setStateSlot(cRqIndexT n, ICRqState state, slotT slot);
doAssert(state != Empty, "use releaseEntry to set state to Empty");
stateVec[n][pipelineResp_port] <= state;
slotVec[n][pipelineResp_port] <= slot;
endmethod
method Action setResult(cRqIndexT n, resultT r);
resultVec[n][pipelineResp_port] <= Valid (r);
endmethod
method Maybe#(cRqIndexT) getSucc(cRqIndexT n);
return succValidVec[n][pipelineResp_port] ? (Valid (succFile.sub(n))) : Invalid;
endmethod
method Action setSucc(cRqIndexT n, Maybe#(cRqIndexT) succ);
succValidVec[n][pipelineResp_port] <= isValid(succ);
succFile.upd(n, fromMaybe(?, succ));
endmethod
method Maybe#(cRqIndexT) searchEndOfChain(Addr addr);
function Bool isEndOfChain(Integer i);
// check entry i is end of chain or not
ICRqState state = stateVec[i][pipelineResp_port];
Bool notDone = state != Done;
Bool processedOnce = state != Empty && state != Init;
Bool addrMatch = getLineAddr(getAddrFromReq(reqVec[i][pipelineResp_port])) == getLineAddr(addr);
Bool noSucc = !succValidVec[i][pipelineResp_port];
return notDone && processedOnce && addrMatch && noSucc;
endfunction
Vector#(cRqNum, Integer) idxVec = genVector;
return searchIndex(isEndOfChain, idxVec);
endmethod
endinterface
method Bool emptyForFlush;
function Bool isEmpty(Integer i) = stateVec[i][flush_port] == Empty;
Vector#(cRqNum, Integer) idxVec = genVector;
return all(isEmpty, idxVec);
endmethod
`ifdef CHECK_DEADLOCK
interface stuck = toGet(stuckQ);
`else
interface stuck = nullGet;
`endif
endmodule
// exported version
module mkICRqMshr#(
function Addr getAddrFromReq(reqT r)
)(
ICRqMshr#(cRqNum, wayT, tagT, reqT, resultT)
) provisos (
Alias#(wayT, Bit#(_waySz)),
Alias#(tagT, Bit#(_tagSz)),
Bits#(reqT, _reqSz),
Bits#(resultT, _resultTSz)
);
let m <- mkICRqMshrSafe(getAddrFromReq);
return m;
endmodule

View File

@@ -0,0 +1,214 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import GetPut::*;
import RegFile::*;
import FIFO::*;
import FShow::*;
import GetPut::*;
import Types::*;
import CCTypes::*;
import DefaultValue::*;
import Ehr::*;
import Fifo::*;
import MshrDeadlockChecker::*;
// MSHR dependency chain invariant:
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
// in L1, pRq is always directly handled at the end of pipeline
// PRq MSHR entry state
typedef enum {
Empty,
Init,
Done
} IPRqState deriving (Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState;
IPRqState state;
} IPRqMshrStuck deriving(Bits, Eq, FShow);
interface IPRqMshr_sendRsToP_pRq#(numeric type pRqNum);
method PRqMsg#(void) getRq(Bit#(TLog#(pRqNum)) n);
method Action releaseEntry(Bit#(TLog#(pRqNum)) n);
endinterface
interface IPRqMshr_pipelineResp#(numeric type pRqNum);
method PRqMsg#(void) getRq(Bit#(TLog#(pRqNum)) n);
method Action releaseEntry(Bit#(TLog#(pRqNum)) n);
method Action setDone(Bit#(TLog#(pRqNum)) n);
`ifdef SECURITY
method Action setFlushAddr(Bit#(TLog#(pRqNum)) n, Addr a);
`endif
endinterface
interface IPRqMshr#(numeric type pRqNum);
// port for pRqTransfer
method ActionValue#(Bit#(TLog#(pRqNum))) getEmptyEntryInit(PRqMsg#(void) r);
// port for sendRsToP_pRq
interface IPRqMshr_sendRsToP_pRq#(pRqNum) sendRsToP_pRq;
// port for pipelineResp
interface IPRqMshr_pipelineResp#(pRqNum) pipelineResp;
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface Get#(IPRqMshrStuck) stuck;
endinterface
//////////////////
// safe version //
//////////////////
module mkIPRqMshrSafe(
IPRqMshr#(pRqNum)
) provisos(
Alias#(pRqIndexT, Bit#(TLog#(pRqNum)))
);
// EHR port
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
Integer pRqTransfer_port = 2;
Integer sendRsToP_pRq_port = 1;
Integer pipelineResp_port = 0;
// MSHR entry state
Vector#(pRqNum, Ehr#(3, IPRqState)) stateVec <- replicateM(mkEhr(Empty));
Vector#(pRqNum, Ehr#(3, PRqMsg#(void))) reqVec <- replicateM(mkEhr(?));
// empty entry FIFO
FIFO#(pRqIndexT) emptyEntryQ <- mkSizedFIFO(valueOf(pRqNum));
// empty entry FIFO needs initialization
Reg#(Bool) inited <- mkReg(False);
Reg#(pRqIndexT) initIdx <- mkReg(0);
// released entry index fifos
Fifo#(1, pRqIndexT) releaseEntryQ_sendRsToP_pRq <- mkBypassFifo;
Fifo#(1, pRqIndexT) releaseEntryQ_pipelineResp <- mkBypassFifo;
rule initEmptyEntry(!inited);
emptyEntryQ.enq(initIdx);
initIdx <= initIdx + 1;
if(initIdx == fromInteger(valueOf(pRqNum) - 1)) begin
inited <= True;
$display("%t IPRqMshrSafe %m: init empty entry done", $time);
end
endrule
`ifdef CHECK_DEADLOCK
MshrDeadlockChecker#(pRqNum) checker <- mkMshrDeadlockChecker;
FIFO#(IPRqMshrStuck) stuckQ <- mkFIFO1;
(* fire_when_enabled *)
rule checkDeadlock;
let stuckIdx <- checker.getStuckIdx;
if(stuckIdx matches tagged Valid .n) begin
stuckQ.enq(IPRqMshrStuck {
addr: reqVec[n][0].addr,
toState: reqVec[n][0].toState,
state: stateVec[n][0]
});
end
endrule
`endif
rule doReleaseEntry_sendRsToP_pRq(inited);
let n <- toGet(releaseEntryQ_sendRsToP_pRq).get;
emptyEntryQ.enq(n);
`ifdef CHECK_DEADLOCK
checker.releaseEntry(n);
`endif
endrule
(* descending_urgency = "doReleaseEntry_sendRsToP_pRq, doReleaseEntry_pipelineResp" *)
rule doReleaseEntry_pipelineResp(inited);
let n <- toGet(releaseEntryQ_pipelineResp).get;
emptyEntryQ.enq(n);
`ifdef CHECK_DEADLOCK
checker.releaseEntry(n);
`endif
endrule
method ActionValue#(pRqIndexT) getEmptyEntryInit(PRqMsg#(void) r) if(inited);
emptyEntryQ.deq;
pRqIndexT n = emptyEntryQ.first;
stateVec[n][pRqTransfer_port] <= Init;
reqVec[n][pRqTransfer_port] <= r;
`ifdef CHECK_DEADLOCK
checker.initEntry(n);
`endif
return n;
endmethod
interface IPRqMshr_sendRsToP_pRq sendRsToP_pRq;
method PRqMsg#(void) getRq(pRqIndexT n);
return reqVec[n][sendRsToP_pRq_port];
endmethod
method Action releaseEntry(pRqIndexT n) if(inited);
releaseEntryQ_sendRsToP_pRq.enq(n);
stateVec[n][sendRsToP_pRq_port] <= Empty;
endmethod
endinterface
interface IPRqMshr_pipelineResp pipelineResp;
method PRqMsg#(void) getRq(pRqIndexT n);
return reqVec[n][pipelineResp_port];
endmethod
method Action setDone(pRqIndexT n);
stateVec[n][pipelineResp_port] <= Done;
endmethod
method Action releaseEntry(pRqIndexT n) if(inited);
releaseEntryQ_pipelineResp.enq(n);
stateVec[n][pipelineResp_port] <= Empty;
endmethod
`ifdef SECURITY
method Action setFlushAddr(Bit#(TLog#(pRqNum)) n, Addr a);
reqVec[n][pipelineResp_port] <= PRqMsg {
addr: a,
toState: I,
child: ?
};
endmethod
`endif
endinterface
`ifdef CHECK_DEADLOCK
interface stuck = toGet(stuckQ);
`else
interface stuck = nullGet;
`endif
endmodule
// exportd version
module mkIPRqMshr(IPRqMshr#(pRqNum));
let m <- mkIPRqMshrSafe;
return m;
endmodule

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,378 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import GetPut::*;
import RegFile::*;
import FIFO::*;
import FShow::*;
import Types::*;
import CCTypes::*;
import DefaultValue::*;
import Ehr::*;
import MshrDeadlockChecker::*;
// MSHR dependency chain invariant:
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
// in L1, pRq is always handled immediately, so cRq never depends on pRq and vice versa
// CRq MSHR entry state
typedef enum {
Empty,
Init,
WaitNewTag, // waiting replacement resp to send (but tag in RAM is already updated)
WaitSt, // wait pRs/cRs to come
Done, // resp is in index FIFO
Depend
} L1CRqState deriving(Bits, Eq, FShow);
// CRq info returned to outside
typedef struct {
wayT way; // the way to occupy
Msi cs; // current cache MSI, used in sending upgrade req to parent
tagT repTag; // tag being replaced: only valid in WaitOld/NewTag states
Bool waitP; // waiting for parent resp
} L1CRqSlot#(type wayT, type tagT) deriving(Bits, Eq, FShow);
instance DefaultValue#(L1CRqSlot#(wayT, tagT));
defaultValue = L1CRqSlot {
way: ?,
cs: ?,
repTag: ?,
waitP: False
};
endinstance
typedef struct {
reqT req;
L1CRqState state;
Msi slotCs;
Bool waitP;
} L1CRqMshrStuck#(type reqT) deriving(Bits, Eq, FShow);
// MSHR data is purely for replacement resp to parent
// (resp to processor is done immediately, no data buffering needed)
// port for cRqTransfer and retry
interface L1CRqMshr_transfer#(
numeric type cRqNum,
type reqT
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method ActionValue#(Bit#(TLog#(cRqNum))) getEmptyEntryInit(reqT r);
endinterface
// port for sendRsToP_cRq
interface L1CRqMshr_sendRsToP_cRq#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT
);
method L1CRqState getState(Bit#(TLog#(cRqNum)) n);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method L1CRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
method Maybe#(Line) getData(Bit#(TLog#(cRqNum)) n);
method Action setWaitSt_setSlot_clearData(
Bit#(TLog#(cRqNum)) n,
L1CRqSlot#(wayT, tagT) slot
);
// data is set to invalid, state is set to WaitSt here
endinterface
// port for sendRqToP
interface L1CRqMshr_sendRqToP#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method L1CRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
endinterface
// port for pipelineResp
interface L1CRqMshr_pipelineResp#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT
);
method Action releaseEntry(Bit#(TLog#(cRqNum)) n);
method L1CRqState getState(Bit#(TLog#(cRqNum)) n);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method L1CRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
method Action setData(Bit#(TLog#(cRqNum)) n, Maybe#(Line) d);
method Action setStateSlot(
Bit#(TLog#(cRqNum)) n,
L1CRqState state,
L1CRqSlot#(wayT, tagT) slot
);
// can only change state to NON-Empty state
// cannot be used to release MSHR entry (use releaseSlot instead)
method Maybe#(Bit#(TLog#(cRqNum))) getSucc(Bit#(TLog#(cRqNum)) n);
method Action setSucc(Bit#(TLog#(cRqNum)) n, Maybe#(Bit#(TLog#(cRqNum))) succ);
// index in setSucc is usually different from other getXXX methods
// find existing cRq which has gone through pipeline, but not in Done state, and has not successor
// i.e. search the end of dependency chain
method Maybe#(Bit#(TLog#(cRqNum))) searchEndOfChain(Addr addr);
endinterface
interface L1CRqMshr#(
numeric type cRqNum,
type wayT,
type tagT,
type reqT // child req type
);
// port for cRqTransfer and retry
interface L1CRqMshr_transfer#(cRqNum, reqT) cRqTransfer;
// port for sendRsToP_cRq
interface L1CRqMshr_sendRsToP_cRq#(cRqNum, wayT, tagT, reqT) sendRsToP_cRq;
// port for sendRqToP
interface L1CRqMshr_sendRqToP#(cRqNum, wayT, tagT, reqT) sendRqToP;
// port for pipelineResp
interface L1CRqMshr_pipelineResp#(cRqNum, wayT, tagT, reqT) pipelineResp;
// port for security flush
method Bool emptyForFlush;
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface Get#(L1CRqMshrStuck#(reqT)) stuck;
endinterface
//////////////////
// safe version //
//////////////////
module mkL1CRqMshrSafe#(
function Addr getAddrFromReq(reqT r)
)(
L1CRqMshr#(cRqNum, wayT, tagT, reqT)
) provisos (
Alias#(cRqIndexT, Bit#(TLog#(cRqNum))),
Alias#(slotT, L1CRqSlot#(wayT, tagT)),
Alias#(wayT, Bit#(_waySz)),
Alias#(tagT, Bit#(_tagSz)),
Bits#(reqT, _reqSz)
);
// EHR ports
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
Integer flush_port = 0; // flush port is read only
Integer sendRqToP_port = 0; // sendRqToP is read only
Integer sendRsToP_cRq_port = 0;
Integer pipelineResp_port = 1;
Integer cRqTransfer_port = 2;
// MSHR entry state
Vector#(cRqNum, Ehr#(3, L1CRqState)) stateVec <- replicateM(mkEhr(Empty));
// cRq req contents
Vector#(cRqNum, Ehr#(3, reqT)) reqVec <- replicateM(mkEhr(?));
// cRq mshr slots
Vector#(cRqNum, Ehr#(3, slotT)) slotVec <- replicateM(mkEhr(defaultValue));
// data valid bit
Vector#(cRqNum, Ehr#(3, Bool)) dataValidVec <- replicateM(mkEhr(False));
// data values
RegFile#(cRqIndexT, Line) dataFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
// successor valid bit
Vector#(cRqNum, Ehr#(3, Bool)) succValidVec <- replicateM(mkEhr(False));
// successor MSHR index
RegFile#(cRqIndexT, cRqIndexT) succFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
// empty entry FIFO
FIFO#(cRqIndexT) emptyEntryQ <- mkSizedFIFO(valueOf(cRqNum));
// empty entry FIFO needs initialization
Reg#(Bool) inited <- mkReg(False);
Reg#(cRqIndexT) initIdx <- mkReg(0);
rule initEmptyEntry(!inited);
emptyEntryQ.enq(initIdx);
initIdx <= initIdx + 1;
if(initIdx == fromInteger(valueOf(cRqNum) - 1)) begin
inited <= True;
$display("%t L1CRqMshrSafe %m: init empty entry done", $time);
end
endrule
`ifdef CHECK_DEADLOCK
MshrDeadlockChecker#(cRqNum) checker <- mkMshrDeadlockChecker;
FIFO#(L1CRqMshrStuck#(reqT)) stuckQ <- mkFIFO1;
(* fire_when_enabled *)
rule checkDeadlock;
let stuckIdx <- checker.getStuckIdx;
if(stuckIdx matches tagged Valid .n) begin
stuckQ.enq(L1CRqMshrStuck {
req: reqVec[n][0],
state: stateVec[n][0],
slotCs: slotVec[n][0].cs,
waitP: slotVec[n][0].waitP
});
end
endrule
`endif
interface L1CRqMshr_transfer cRqTransfer;
method reqT getRq(cRqIndexT n);
return reqVec[n][cRqTransfer_port];
endmethod
method ActionValue#(cRqIndexT) getEmptyEntryInit(reqT r) if(inited);
emptyEntryQ.deq;
cRqIndexT n = emptyEntryQ.first;
stateVec[n][cRqTransfer_port] <= Init;
slotVec[n][cRqTransfer_port] <= defaultValue;
dataValidVec[n][cRqTransfer_port] <= False;
succValidVec[n][cRqTransfer_port] <= False;
reqVec[n][cRqTransfer_port] <= r;
`ifdef CHECK_DEADLOCK
checker.initEntry(n);
`endif
return n;
endmethod
endinterface
interface L1CRqMshr_sendRsToP_cRq sendRsToP_cRq;
method L1CRqState getState(cRqIndexT n);
return stateVec[n][sendRsToP_cRq_port];
endmethod
method reqT getRq(cRqIndexT n);
return reqVec[n][sendRsToP_cRq_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][sendRsToP_cRq_port];
endmethod
method Maybe#(Line) getData(cRqIndexT n);
return dataValidVec[n][sendRsToP_cRq_port] ? (Valid (dataFile.sub(n))) : Invalid;
endmethod
method Action setWaitSt_setSlot_clearData(cRqIndexT n, slotT s);
stateVec[n][sendRsToP_cRq_port] <= WaitSt;
slotVec[n][sendRsToP_cRq_port] <= s;
dataValidVec[n][sendRsToP_cRq_port] <= False;
endmethod
endinterface
interface L1CRqMshr_sendRqToP sendRqToP;
method reqT getRq(cRqIndexT n);
return reqVec[n][sendRqToP_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][sendRqToP_port];
endmethod
endinterface
interface L1CRqMshr_pipelineResp pipelineResp;
method L1CRqState getState(cRqIndexT n);
return stateVec[n][pipelineResp_port];
endmethod
method reqT getRq(cRqIndexT n);
return reqVec[n][pipelineResp_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][pipelineResp_port];
endmethod
method Action releaseEntry(cRqIndexT n) if(inited);
emptyEntryQ.enq(n);
stateVec[n][pipelineResp_port] <= Empty;
`ifdef CHECK_DEADLOCK
checker.releaseEntry(n);
`endif
endmethod
method Action setStateSlot(cRqIndexT n, L1CRqState state, slotT slot);
doAssert(state != Empty, "use releaseEntry to set state to Empty");
stateVec[n][pipelineResp_port] <= state;
slotVec[n][pipelineResp_port] <= slot;
endmethod
method Action setData(cRqIndexT n, Maybe#(Line) line);
dataValidVec[n][pipelineResp_port] <= isValid(line);
dataFile.upd(n, fromMaybe(?, line));
endmethod
method Maybe#(cRqIndexT) getSucc(cRqIndexT n);
return succValidVec[n][pipelineResp_port] ? (Valid (succFile.sub(n))) : Invalid;
endmethod
method Action setSucc(cRqIndexT n, Maybe#(cRqIndexT) succ);
succValidVec[n][pipelineResp_port] <= isValid(succ);
succFile.upd(n, fromMaybe(?, succ));
endmethod
method Maybe#(cRqIndexT) searchEndOfChain(Addr addr);
function Bool isEndOfChain(Integer i);
// check entry i is end of chain or not
L1CRqState state = stateVec[i][pipelineResp_port];
Bool notDone = state != Done;
Bool processedOnce = state != Empty && state != Init;
Bool addrMatch = getLineAddr(getAddrFromReq(reqVec[i][pipelineResp_port])) == getLineAddr(addr);
Bool noSucc = !succValidVec[i][pipelineResp_port];
return notDone && processedOnce && addrMatch && noSucc;
endfunction
Vector#(cRqNum, Integer) idxVec = genVector;
return searchIndex(isEndOfChain, idxVec);
endmethod
endinterface
method Bool emptyForFlush;
function Bool isEmpty(Integer i) = stateVec[i][flush_port] == Empty;
Vector#(cRqNum, Integer) idxVec = genVector;
return all(isEmpty, idxVec);
endmethod
`ifdef CHECK_DEADLOCK
interface stuck = toGet(stuckQ);
`else
interface stuck = nullGet;
`endif
endmodule
// exported version
module mkL1CRqMshr#(
function Addr getAddrFromReq(reqT r)
)(
L1CRqMshr#(cRqNum, wayT, tagT, reqT)
) provisos (
Alias#(wayT, Bit#(_waySz)),
Alias#(tagT, Bit#(_tagSz)),
Bits#(reqT, _reqSz)
);
let m <- mkL1CRqMshrSafe(getAddrFromReq);
return m;
endmodule

View File

@@ -0,0 +1,231 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import RegFile::*;
import FIFO::*;
import FShow::*;
import GetPut::*;
import Types::*;
import CCTypes::*;
import DefaultValue::*;
import Ehr::*;
import Fifo::*;
import MshrDeadlockChecker::*;
// MSHR dependency chain invariant:
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
// in L1, pRq is always directly handled at the end of pipeline
// PRq MSHR entry state
typedef enum {
Empty,
Init,
Done
} L1PRqState deriving (Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState;
L1PRqState state;
} L1PRqMshrStuck deriving(Bits, Eq, FShow);
interface L1PRqMshr_sendRsToP_pRq#(numeric type pRqNum);
method PRqMsg#(void) getRq(Bit#(TLog#(pRqNum)) n);
method Maybe#(Line) getData(Bit#(TLog#(pRqNum)) n);
method Action releaseEntry(Bit#(TLog#(pRqNum)) n);
endinterface
interface L1PRqMshr_pipelineResp#(numeric type pRqNum);
method PRqMsg#(void) getRq(Bit#(TLog#(pRqNum)) n);
method L1PRqState getState(Bit#(TLog#(pRqNum)) n);
method Action releaseEntry(Bit#(TLog#(pRqNum)) n);
method Action setDone_setData(Bit#(TLog#(pRqNum)) n, Maybe#(Line) d);
`ifdef SECURITY
method Action setFlushAddr(Bit#(TLog#(pRqNum)) n, Addr a);
`endif
endinterface
interface L1PRqMshr#(numeric type pRqNum);
// port for pRqTransfer
method ActionValue#(Bit#(TLog#(pRqNum))) getEmptyEntryInit(PRqMsg#(void) r);
// port for sendRsToP_pRq
interface L1PRqMshr_sendRsToP_pRq#(pRqNum) sendRsToP_pRq;
// port for pipelineResp
interface L1PRqMshr_pipelineResp#(pRqNum) pipelineResp;
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface Get#(L1PRqMshrStuck) stuck;
endinterface
//////////////////
// safe version //
//////////////////
module mkL1PRqMshrSafe(
L1PRqMshr#(pRqNum)
) provisos(
Alias#(pRqIndexT, Bit#(TLog#(pRqNum)))
);
// EHR port
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
Integer sendRsToP_pRq_port = 0;
Integer pipelineResp_port = 1;
Integer pRqTransfer_port = 2;
// MSHR entry state
Vector#(pRqNum, Ehr#(3, L1PRqState)) stateVec <- replicateM(mkEhr(Empty));
Vector#(pRqNum, Ehr#(3, PRqMsg#(void))) reqVec <- replicateM(mkEhr(?));
// data valid bits
Vector#(pRqNum, Ehr#(3, Bool)) dataValidVec <- replicateM(mkEhr(False));
// data values
RegFile#(pRqIndexT, Line) dataFile <- mkRegFile(0, fromInteger(valueOf(pRqNum) - 1));
// empty entry FIFO
FIFO#(pRqIndexT) emptyEntryQ <- mkSizedFIFO(valueOf(pRqNum));
// empty entry FIFO needs initialization
Reg#(Bool) inited <- mkReg(False);
Reg#(pRqIndexT) initIdx <- mkReg(0);
// released entry index fifos
Fifo#(1, pRqIndexT) releaseEntryQ_sendRsToP_pRq <- mkBypassFifo;
Fifo#(1, pRqIndexT) releaseEntryQ_pipelineResp <- mkBypassFifo;
rule initEmptyEntry(!inited);
emptyEntryQ.enq(initIdx);
initIdx <= initIdx + 1;
if(initIdx == fromInteger(valueOf(pRqNum) - 1)) begin
inited <= True;
$display("%t L1PRqMshrSafe %m: init empty entry done", $time);
end
endrule
`ifdef CHECK_DEADLOCK
MshrDeadlockChecker#(pRqNum) checker <- mkMshrDeadlockChecker;
FIFO#(L1PRqMshrStuck) stuckQ <- mkFIFO1;
(* fire_when_enabled *)
rule checkDeadlock;
let stuckIdx <- checker.getStuckIdx;
if(stuckIdx matches tagged Valid .n) begin
stuckQ.enq(L1PRqMshrStuck {
addr: reqVec[n][0].addr,
toState: reqVec[n][0].toState,
state: stateVec[n][0]
});
end
endrule
`endif
rule doReleaseEntry_sendRsToP_pRq(inited);
let n <- toGet(releaseEntryQ_sendRsToP_pRq).get;
emptyEntryQ.enq(n);
`ifdef CHECK_DEADLOCK
checker.releaseEntry(n);
`endif
endrule
(* descending_urgency = "doReleaseEntry_sendRsToP_pRq, doReleaseEntry_pipelineResp" *)
rule doReleaseEntry_pipelineResp(inited);
let n <- toGet(releaseEntryQ_pipelineResp).get;
emptyEntryQ.enq(n);
`ifdef CHECK_DEADLOCK
checker.releaseEntry(n);
`endif
endrule
method ActionValue#(pRqIndexT) getEmptyEntryInit(PRqMsg#(void) r) if(inited);
emptyEntryQ.deq;
pRqIndexT n = emptyEntryQ.first;
stateVec[n][pRqTransfer_port] <= Init;
reqVec[n][pRqTransfer_port] <= r;
dataValidVec[n][pRqTransfer_port] <= False;
`ifdef CHECK_DEADLOCK
checker.initEntry(n);
`endif
return n;
endmethod
interface L1PRqMshr_sendRsToP_pRq sendRsToP_pRq;
method PRqMsg#(void) getRq(pRqIndexT n);
return reqVec[n][sendRsToP_pRq_port];
endmethod
method Maybe#(Line) getData(pRqIndexT n);
return dataValidVec[n][sendRsToP_pRq_port] ? (Valid (dataFile.sub(n))) : Invalid;
endmethod
method Action releaseEntry(pRqIndexT n) if(inited);
releaseEntryQ_sendRsToP_pRq.enq(n);
stateVec[n][sendRsToP_pRq_port] <= Empty;
endmethod
endinterface
interface L1PRqMshr_pipelineResp pipelineResp;
method PRqMsg#(void) getRq(pRqIndexT n);
return reqVec[n][pipelineResp_port];
endmethod
method L1PRqState getState(pRqIndexT n);
return stateVec[n][pipelineResp_port];
endmethod
method Action setDone_setData(pRqIndexT n, Maybe#(Line) line);
stateVec[n][pipelineResp_port] <= Done;
dataValidVec[n][pipelineResp_port] <= isValid(line);
dataFile.upd(n, fromMaybe(?, line));
endmethod
method Action releaseEntry(pRqIndexT n) if(inited);
releaseEntryQ_pipelineResp.enq(n);
stateVec[n][pipelineResp_port] <= Empty;
endmethod
`ifdef SECURITY
method Action setFlushAddr(Bit#(TLog#(pRqNum)) n, Addr a);
reqVec[n][pipelineResp_port] <= PRqMsg {
addr: a,
toState: I,
child: ?
};
endmethod
`endif
endinterface
`ifdef CHECK_DEADLOCK
interface stuck = toGet(stuckQ);
`else
interface stuck = nullGet;
`endif
endmodule
// exportd version
module mkL1PRqMshr(L1PRqMshr#(pRqNum));
let m <- mkL1PRqMshrSafe;
return m;
endmodule

View File

@@ -0,0 +1,391 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import FShow::*;
import Types::*;
import CCTypes::*;
import CCPipe::*;
import RWBramCore::*;
import RandomReplace::*;
export L1PipeRqIn(..);
export L1PipePRsIn(..);
export L1PipeFlushIn(..);
export L1PipeIn(..);
export L1FlushCmd(..);
export L1Cmd(..);
export L1Pipe(..);
export mkL1Pipe;
// type param ordering: bank < way < index < tag < cRq < pRq
// in L1 cache, only cRq can occupy cache line (pRq handled immediately)
// replacement is always done immediately (never have replacing line)
// so cache owner type is simply Maybe#(cRqIdxT)
// input types
typedef struct {
Addr addr;
rqIdxT mshrIdx;
} L1PipeRqIn#(type rqIdxT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState;
Maybe#(Line) data;
wayT way;
} L1PipePRsIn#(type wayT) deriving(Bits, Eq, FShow);
typedef struct {
indexT index;
wayT way;
pRqIdxT mshrIdx;
} L1PipeFlushIn#(
type wayT,
type indexT,
type pRqIdxT
) deriving(Bits, Eq, FShow);
typedef union tagged {
L1PipeRqIn#(cRqIdxT) CRq;
L1PipeRqIn#(pRqIdxT) PRq;
L1PipePRsIn#(wayT) PRs;
`ifdef SECURITY
L1PipeFlushIn#(wayT, indexT, pRqIdxT) Flush;
`endif
} L1PipeIn#(
type wayT,
type indexT,
type cRqIdxT,
type pRqIdxT
) deriving (Bits, Eq, FShow);
// output cmd to the processing rule in L1$
typedef struct {
indexT index;
pRqIdxT mshrIdx;
} L1FlushCmd#(type indexT, type pRqIdxT) deriving(Bits, Eq, FShow);
typedef union tagged {
cRqIdxT L1CRq;
pRqIdxT L1PRq;
void L1PRs;
`ifdef SECURITY
L1FlushCmd#(indexT, pRqIdxT) L1Flush;
`endif
} L1Cmd#(
type indexT,
type cRqIdxT,
type pRqIdxT
) deriving (Bits, Eq, FShow);
interface L1Pipe#(
numeric type lgBankNum,
numeric type wayNum,
type indexT,
type tagT,
type cRqIdxT,
type pRqIdxT
);
method Action send(L1PipeIn#(Bit#(TLog#(wayNum)), indexT, cRqIdxT, pRqIdxT) r);
method PipeOut#(
Bit#(TLog#(wayNum)),
tagT, Msi, void, // no dir
Maybe#(cRqIdxT), void, RandRepInfo, // no other
Line, L1Cmd#(indexT, cRqIdxT, pRqIdxT)
) first;
method Action deqWrite(
Maybe#(cRqIdxT) swapRq,
RamData#(tagT, Msi, void, Maybe#(cRqIdxT), void, Line) wrRam, // always write BRAM
Bool updateRep
);
endinterface
// real cmd used in pipeline
typedef struct {
Addr addr;
wayT way;
} L1PipePRsCmd#(type wayT) deriving(Bits, Eq, FShow);
typedef union tagged {
L1PipeRqIn#(cRqIdxT) CRq;
L1PipeRqIn#(pRqIdxT) PRq;
L1PipePRsCmd#(wayT) PRs;
`ifdef SECURITY
L1PipeFlushIn#(wayT, indexT, pRqIdxT) Flush;
`endif
} L1PipeCmd#(
type wayT,
type indexT,
type cRqIdxT,
type pRqIdxT
) deriving (Bits, Eq, FShow);
module mkL1Pipe(
L1Pipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT, pRqIdxT)
) provisos(
Alias#(wayT, Bit#(TLog#(wayNum))),
Alias#(dirT, void), // no directory
Alias#(ownerT, Maybe#(cRqIdxT)),
Alias#(otherT, void), // no other cache info
Alias#(repT, RandRepInfo), // use random replace
Alias#(pipeInT, L1PipeIn#(wayT, indexT, cRqIdxT, pRqIdxT)),
Alias#(pipeCmdT, L1PipeCmd#(wayT, indexT, cRqIdxT, pRqIdxT)),
Alias#(l1CmdT, L1Cmd#(indexT, cRqIdxT, pRqIdxT)),
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, l1CmdT)), // output type
Alias#(infoT, CacheInfo#(tagT, Msi, dirT, ownerT, otherT)),
Alias#(ramDataT, RamData#(tagT, Msi, dirT, ownerT, otherT, Line)),
Alias#(respStateT, RespState#(Msi)),
Alias#(tagMatchResT, TagMatchResult#(wayT)),
Alias#(updateByUpCsT, UpdateByUpCs#(Msi)),
Alias#(updateByDownDirT, UpdateByDownDir#(Msi, dirT)),
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), indexSz))),
// requirement
Alias#(indexT, Bit#(indexSz)),
Alias#(tagT, Bit#(tagSz)),
Alias#(cRqIdxT, Bit#(cRqIdxSz)),
Alias#(pRqIdxT, Bit#(pRqIdxSz)),
Add#(indexSz, a__, AddrSz),
Add#(tagSz, b__, AddrSz)
);
// RAMs
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam <- replicateM(mkRWBramCore);
RWBramCore#(indexT, repT) repRam <- mkRandRepRam;
RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore;
// initialize RAM
Reg#(Bool) initDone <- mkReg(False);
Reg#(indexT) initIndex <- mkReg(0);
rule doInit(!initDone);
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
infoRam[i].wrReq(initIndex, CacheInfo {
tag: 0,
cs: I,
dir: ?,
owner: Invalid,
other: ?
});
end
repRam.wrReq(initIndex, randRepInitInfo); // useless for random replace
initIndex <= initIndex + 1;
if(initIndex == maxBound) begin
initDone <= True;
end
endrule
// random replacement
RandomReplace#(wayNum) randRep <- mkRandomReplace;
// functions
function Addr getAddrFromCmd(pipeCmdT cmd);
return (case(cmd) matches
tagged CRq .r: r.addr;
tagged PRq .r: r.addr;
tagged PRs .r: r.addr;
`ifdef SECURITY
// fake an address for flush req that has the same index
tagged Flush .r: (zeroExtend(r.index) << (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
`endif
default: ?;
endcase);
endfunction
function indexT getIndex(pipeCmdT cmd);
Addr a = getAddrFromCmd(cmd);
return truncate(a >> (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
endfunction
function ActionValue#(tagMatchResT) tagMatch(
pipeCmdT cmd,
Vector#(wayNum, tagT) tagVec,
Vector#(wayNum, Msi) csVec,
Vector#(wayNum, ownerT) ownerVec,
repT repInfo
);
return actionvalue
function tagT getTag(Addr a) = truncateLSB(a);
$display("%t L1 %m tagMatch: ", $time,
fshow(cmd), " ; ",
fshow(getTag(getAddrFromCmd(cmd))),
fshow(tagVec), " ; ",
fshow(csVec), " ; ",
fshow(ownerVec), " ; "
);
if(cmd matches tagged PRs .rs) begin
// PRs directly read from cmd
return TagMatchResult {
way: rs.way,
pRqMiss: False
};
end
`ifdef SECURITY
else if(cmd matches tagged Flush .flush) begin
// flush directly read from cmd
return TagMatchResult {
way: flush.way,
pRqMiss: False
};
end
`endif
else begin
// CRq/PRq: need tag matching
Addr addr = getAddrFromCmd(cmd);
tagT tag = getTag(addr);
// find hit way (nothing is being replaced)
function Bool isMatch(Tuple2#(Msi, tagT) csTag);
match {.cs, .t} = csTag;
return cs > I && t == tag;
endfunction
Maybe#(wayT) hitWay = searchIndex(isMatch, zip(csVec, tagVec));
if(hitWay matches tagged Valid .w) begin
return TagMatchResult {
way: w,
pRqMiss: False
};
end
else if(cmd matches tagged PRq .rq) begin
// pRq miss
return TagMatchResult {
way: 0, // default to 0
pRqMiss: True
};
end
else begin
// find a unlocked way to replace for cRq
Vector#(wayNum, Bool) unlocked = ?;
Vector#(wayNum, Bool) invalid = ?;
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
invalid[i] = csVec[i] == I;
unlocked[i] = !isValid(ownerVec[i]);
end
Maybe#(wayT) repWay = randRep.getReplaceWay(unlocked, invalid);
// sanity check: repWay must be valid
if(!isValid(repWay)) begin
$fwrite(stderr, "[L1Pipe] ERROR: ", fshow(cmd), " cannot find way to replace\n");
$finish;
end
return TagMatchResult {
way: fromMaybe(?, repWay),
pRqMiss: False
};
end
end
endactionvalue;
endfunction
function ActionValue#(updateByUpCsT) updateByUpCs(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs
);
actionvalue
doAssert(toState > oldCs, "should truly upgrade cs");
doAssert((oldCs == I) == dataV, "valid resp data for upgrade from I");
return UpdateByUpCs {cs: toState};
endactionvalue
endfunction
function ActionValue#(updateByDownDirT) updateByDownDir(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs, dirT oldDir
);
actionvalue
doAssert(False, "L1 does not have dir");
return UpdateByDownDir {cs: oldCs, dir: oldDir};
endactionvalue
endfunction
function ActionValue#(repT) updateRepInfo(repT r, wayT w);
actionvalue
return ?; // random replace does not have bookkeeping
endactionvalue
endfunction
CCPipe#(
wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT
) pipe <- mkCCPipe(
regToReadOnly(initDone), getIndex, tagMatch,
updateByUpCs, updateByDownDir, updateRepInfo,
infoRam, repRam, dataRam
);
method Action send(pipeInT req);
case(req) matches
tagged CRq .rq: begin
pipe.enq(CRq (rq), Invalid, Invalid);
end
tagged PRq .rq: begin
pipe.enq(PRq (rq), Invalid, Invalid);
end
tagged PRs .rs: begin
pipe.enq(PRs (L1PipePRsCmd {
addr: rs.addr,
way: rs.way
}), rs.data, UpCs (rs.toState));
end
`ifdef SECURITY
tagged Flush .flush: begin
pipe.enq(Flush (flush), Invalid, Invalid);
end
`endif
endcase
endmethod
// need to adapt pipeline output to real output format
method pipeOutT first;
let pout = pipe.first;
return PipeOut {
cmd: (case(pout.cmd) matches
tagged CRq .rq: L1CRq (rq.mshrIdx);
tagged PRq .rq: L1PRq (rq.mshrIdx);
tagged PRs .rs: L1PRs;
`ifdef SECURITY
tagged Flush .flush: L1Flush (L1FlushCmd {
index: flush.index,
mshrIdx: flush.mshrIdx
});
`endif
default: ?;
endcase),
way: pout.way,
pRqMiss: pout.pRqMiss,
ram: pout.ram,
repInfo: pout.repInfo
};
endmethod
method Action deqWrite(Maybe#(cRqIdxT) swapRq, ramDataT wrRam, Bool updateRep);
// get new cmd
Maybe#(pipeCmdT) newCmd = Invalid;
if(swapRq matches tagged Valid .idx) begin // swap in cRq
Addr addr = getAddrFromCmd(pipe.first.cmd); // inherit addr
newCmd = Valid (CRq (L1PipeRqIn {addr: addr, mshrIdx: idx}));
`ifdef SECURITY
doAssert(pipe.first.cmd matches tagged Flush .f ? False : True,
"Cannot swap after a flush req");
`endif
end
// call pipe
pipe.deqWrite(newCmd, wrRam, updateRep);
endmethod
endmodule

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,447 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import GetPut::*;
import RegFile::*;
import FIFO::*;
import FShow::*;
import Types::*;
import CCTypes::*;
import DefaultValue::*;
import Ehr::*;
import Fifo::*;
import MshrDeadlockChecker::*;
// MSHR dependency chain invariant:
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
// in LLC, the head (h1) of a chain may be linked as the successor of the head (h2) of another chain
// when h2 is replacing the addr of h1
// h1 should be waken up and sent to pipeline when replacement is done (i.e. h2 gets to WaitSt)
// CRq MSHR entry state
typedef enum {
Empty,
Init,
WaitOldTag,
WaitSt,
Done,
Depend
} LLCRqState deriving(Bits, Eq, FShow);
// we split data from slot info
// because data may be used to buffer mem resp data
typedef struct {
wayT way; // the way to occupy
tagT repTag; // tag being replaced, used in sending down req to children
Bool waitP; // wait parent resp
dirPendT dirPend; // pending child downgrade
} LLCRqSlot#(type wayT, type tagT, type dirPendT) deriving(Bits, Eq, FShow);
typedef struct {
reqT req;
LLCRqState state;
Bool waitP;
dirPendT dirPend;
} LLCRqMshrStuck#(type dirPendT, type reqT) deriving(Bits, Eq, FShow);
// interface for cRq/mRs/cRsTransfer
interface LLCRqMshr_transfer#(
numeric type cRqNum,
type wayT,
type tagT,
type dirPendT,
type reqT // child req type
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method LLCRqSlot#(wayT, tagT, dirPendT) getSlot(Bit#(TLog#(cRqNum)) n);
method ActionValue#(Bit#(TLog#(cRqNum))) getEmptyEntryInit(reqT r, Maybe#(Line) d);
// check if any empty MSHR entry is available in order to get MSHR blocking
// stats. The argument is not really used here, just in case some other
// MSHR implementations may bank entries based on addr.
method Bool hasEmptyEntry(reqT r);
endinterface
// interface for mRsDeq
interface LLCRqMshr_mRsDeq#(numeric type cRqNum);
method Action setData(Bit#(TLog#(cRqNum)) n, Maybe#(Line) d);
endinterface
// interface for sendToM
interface LLCRqMshr_sendToM#(
numeric type cRqNum,
type wayT,
type tagT,
type dirPendT,
type reqT // child req type
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method LLCRqSlot#(wayT, tagT, dirPendT) getSlot(Bit#(TLog#(cRqNum)) n);
method Maybe#(Line) getData(Bit#(TLog#(cRqNum)) n);
endinterface
// interface for sendRsToDma and sendRsToC
interface LLCRqMshr_sendRsToDmaC#(
numeric type cRqNum,
type reqT // child req type
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method Maybe#(Line) getData(Bit#(TLog#(cRqNum)) n);
method Action releaseEntry(Bit#(TLog#(cRqNum)) n);
endinterface
// interface for sendRqToC
interface LLCRqMshr_sendRqToC#(
numeric type cRqNum,
type wayT,
type tagT,
type dirPendT,
type reqT // child req type
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method LLCRqState getState(Bit#(TLog#(cRqNum)) n);
method LLCRqSlot#(wayT, tagT, dirPendT) getSlot(Bit#(TLog#(cRqNum)) n);
method Action setSlot(Bit#(TLog#(cRqNum)) n, LLCRqSlot#(wayT, tagT, dirPendT) s);
// find cRq that needs to send req to child to downgrade
// (either replacement, or incompatible children states)
// we can pass in a suggested req idx (which will have priority)
method Maybe#(Bit#(TLog#(cRqNum))) searchNeedRqChild(Maybe#(Bit#(TLog#(cRqNum))) suggestIdx);
endinterface
// interface for pipelineResp_xxx
interface LLCRqMshr_pipelineResp#(
numeric type cRqNum,
type wayT,
type tagT,
type dirPendT,
type reqT // child req type
);
method reqT getRq(Bit#(TLog#(cRqNum)) n);
method LLCRqState getState(Bit#(TLog#(cRqNum)) n);
method LLCRqSlot#(wayT, tagT, dirPendT) getSlot(Bit#(TLog#(cRqNum)) n);
method Maybe#(Line) getData(Bit#(TLog#(cRqNum)) n);
method Maybe#(Bit#(TLog#(cRqNum))) getAddrSucc(Bit#(TLog#(cRqNum)) n);
method Maybe#(Bit#(TLog#(cRqNum))) getRepSucc(Bit#(TLog#(cRqNum)) n);
method Action setData(Bit#(TLog#(cRqNum)) n, Maybe#(Line) d);
method Action setStateSlot(
Bit#(TLog#(cRqNum)) n, LLCRqState state,
LLCRqSlot#(wayT, tagT, dirPendT) slot
);
method Action setAddrSucc( // same address successor
Bit#(TLog#(cRqNum)) n,
Maybe#(Bit#(TLog#(cRqNum))) succ
);
method Action setRepSucc( // successor due to replacement
Bit#(TLog#(cRqNum)) n,
Maybe#(Bit#(TLog#(cRqNum))) succ
);
// find existing cRq which has gone through pipeline, but not in Done state, and has no addr successor
// (it could have rep successor)
// i.e. search the end of dependency chain for req to the same addr
method Maybe#(Bit#(TLog#(cRqNum))) searchEndOfChain(Addr addr);
endinterface
interface LLCRqMshr#(
numeric type cRqNum,
type wayT,
type tagT,
type dirPendT,
type reqT // child req type
);
interface LLCRqMshr_transfer#(cRqNum, wayT, tagT, dirPendT, reqT) transfer;
interface LLCRqMshr_mRsDeq#(cRqNum) mRsDeq;
interface LLCRqMshr_sendToM#(cRqNum, wayT, tagT, dirPendT, reqT) sendToM;
interface LLCRqMshr_sendRsToDmaC#(cRqNum, reqT) sendRsToDmaC;
interface LLCRqMshr_sendRqToC#(cRqNum, wayT, tagT, dirPendT, reqT) sendRqToC;
interface LLCRqMshr_pipelineResp#(cRqNum, wayT, tagT, dirPendT, reqT) pipelineResp;
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface Get#(LLCRqMshrStuck#(dirPendT, reqT)) stuck;
endinterface
function LLCRqSlot#(wayT, tagT, dirPendT) getLLCRqSlotInitVal(dirPendT dirPendInitVal);
return LLCRqSlot {
way: ?,
repTag: ?,
waitP: False,
dirPend: dirPendInitVal
};
endfunction
//////////////////
// safe version //
//////////////////
module mkLLCRqMshr#(
function Addr getAddrFromReq(reqT r),
function Bool needDownReq(dirPendT dirPend),
dirPendT dirPendInitVal
)(
LLCRqMshr#(cRqNum, wayT, tagT, dirPendT, reqT)
) provisos (
Alias#(cRqIndexT, Bit#(TLog#(cRqNum))),
Alias#(slotT, LLCRqSlot#(wayT, tagT, dirPendT)),
Alias#(wayT, Bit#(_waySz)),
Alias#(tagT, Bit#(_tagSz)),
Bits#(dirPendT, _dirPendSz),
Bits#(reqT, _reqSz)
);
slotT slotInitVal = getLLCRqSlotInitVal(dirPendInitVal);
// logical ordering: sendToM < sendRqToC < sendRsToDma/C < mRsDeq < pipelineResp < transfer
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
// EHR ports
Integer sendToM_port = 0; // sendToM is read-only, so use port 0
Integer sendRqToC_port = 0; // read req/state/slot, write slot
Integer sendRsToDmaC_port = 0; // sendRsToDma/C read req/data, write state
Integer mRsDeq_port = 0; // mRsDeq only writes data
Integer pipelineResp_port = 1; // read/write lots of things
Integer transfer_port = 2; // cRqTransfer_xx, mRsTransfer_send, read/write lots of things
// cRq req contents
Vector#(cRqNum, Ehr#(3, reqT)) reqVec <- replicateM(mkEhr(?));
// MSHR entry state
Vector#(cRqNum, Ehr#(3, LLCRqState)) stateVec <- replicateM(mkEhr(Empty));
// summary bit of dirPend in each entry: asserted when some dirPend[i] = ToSend
Vector#(cRqNum, Ehr#(3, Bool)) needReqChildVec <- replicateM(mkEhr(False));
// cRq mshr slots
Vector#(cRqNum, Ehr#(3, slotT)) slotVec <- replicateM(mkEhr(slotInitVal));
// data valid bit
Vector#(cRqNum, Ehr#(3, Bool)) dataValidVec <- replicateM(mkEhr(False));
// data values
Vector#(cRqNum, Ehr#(3, Line)) dataVec <- replicateM(mkEhr(?));
// successor valid bit
Vector#(cRqNum, Ehr#(3, Bool)) addrSuccValidVec <- replicateM(mkEhr(False));
Vector#(cRqNum, Ehr#(3, Bool)) repSuccValidVec <- replicateM(mkEhr(False));
// successor MSHR index
RegFile#(cRqIndexT, cRqIndexT) addrSuccFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
RegFile#(cRqIndexT, cRqIndexT) repSuccFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
// empty entry FIFO
Fifo#(cRqNum, cRqIndexT) emptyEntryQ <- mkCFFifo;
// empty entry FIFO needs initialization
Reg#(Bool) inited <- mkReg(False);
Reg#(cRqIndexT) initIdx <- mkReg(0);
rule initEmptyEntry(!inited);
emptyEntryQ.enq(initIdx);
initIdx <= initIdx + 1;
if(initIdx == fromInteger(valueOf(cRqNum) - 1)) begin
inited <= True;
$display("%t LLCRqMshrSafe %m: init empty entry done", $time);
end
endrule
`ifdef CHECK_DEADLOCK
MshrDeadlockChecker#(cRqNum) checker <- mkMshrDeadlockChecker;
FIFO#(LLCRqMshrStuck#(dirPendT, reqT)) stuckQ <- mkFIFO1;
(* fire_when_enabled *)
rule checkDeadlock;
let stuckIdx <- checker.getStuckIdx;
if(stuckIdx matches tagged Valid .n) begin
stuckQ.enq(LLCRqMshrStuck {
req: reqVec[n][0],
state: stateVec[n][0],
waitP: slotVec[n][0].waitP,
dirPend: slotVec[n][0].dirPend
});
end
endrule
`endif
function Action writeSlot(Integer ehrPort, cRqIndexT n, slotT s);
action
slotVec[n][ehrPort] <= s;
// set dirPend summary bit
needReqChildVec[n][ehrPort] <= needDownReq(s.dirPend);
endaction
endfunction
interface LLCRqMshr_transfer transfer;
method reqT getRq(cRqIndexT n);
return reqVec[n][transfer_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][transfer_port];
endmethod
method ActionValue#(cRqIndexT) getEmptyEntryInit(reqT r, Maybe#(Line) d) if(inited);
emptyEntryQ.deq;
cRqIndexT n = emptyEntryQ.first;
reqVec[n][transfer_port] <= r;
stateVec[n][transfer_port] <= Init;
writeSlot(transfer_port, n, slotInitVal);
dataValidVec[n][transfer_port] <= isValid(d);
dataVec[n][transfer_port] <= validValue(d);
addrSuccValidVec[n][transfer_port] <= False;
repSuccValidVec[n][transfer_port] <= False;
`ifdef CHECK_DEADLOCK
checker.initEntry(n);
`endif
return n;
endmethod
method Bool hasEmptyEntry(reqT r);
return emptyEntryQ.notEmpty;
endmethod
endinterface
interface LLCRqMshr_mRsDeq mRsDeq;
method Action setData(cRqIndexT n, Maybe#(Line) d);
dataValidVec[n][mRsDeq_port] <= isValid(d);
dataVec[n][mRsDeq_port] <= fromMaybe(?, d);
endmethod
endinterface
interface LLCRqMshr_sendToM sendToM;
method reqT getRq(cRqIndexT n);
return reqVec[n][sendToM_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][sendToM_port];
endmethod
method Maybe#(Line) getData(cRqIndexT n);
return dataValidVec[n][sendToM_port] ? Valid (dataVec[n][sendToM_port]) : Invalid;
endmethod
endinterface
interface LLCRqMshr_sendRsToDmaC sendRsToDmaC;
method reqT getRq(cRqIndexT n);
return reqVec[n][sendRsToDmaC_port];
endmethod
method Maybe#(Line) getData(cRqIndexT n);
return dataValidVec[n][sendRsToDmaC_port] ? Valid (dataVec[n][sendRsToDmaC_port]) : Invalid;
endmethod
method Action releaseEntry(cRqIndexT n) if(inited);
emptyEntryQ.enq(n);
stateVec[n][sendRsToDmaC_port] <= Empty;
`ifdef CHECK_DEADLOCK
checker.releaseEntry(n);
`endif
endmethod
endinterface
interface LLCRqMshr_sendRqToC sendRqToC;
method reqT getRq(cRqIndexT n);
return reqVec[n][sendRqToC_port];
endmethod
method LLCRqState getState(cRqIndexT n);
return stateVec[n][sendRqToC_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][sendRqToC_port];
endmethod
method Action setSlot(cRqIndexT n, slotT s);
writeSlot(sendRqToC_port, n, s);
endmethod
method Maybe#(cRqIndexT) searchNeedRqChild(Maybe#(cRqIndexT) suggestIdx);
function Bool isNeedRqChild(cRqIndexT i);
return (stateVec[i][sendRqToC_port] == WaitOldTag || stateVec[i][sendRqToC_port] == WaitSt)
&& needReqChildVec[i][sendRqToC_port];
endfunction
if(suggestIdx matches tagged Valid .idx &&& isNeedRqChild(idx)) begin
return suggestIdx;
end
else begin
Vector#(cRqNum, cRqIndexT) idxVec = genWith(fromInteger);
return searchIndex(isNeedRqChild, idxVec);
end
endmethod
endinterface
interface LLCRqMshr_pipelineResp pipelineResp;
method reqT getRq(cRqIndexT n);
return reqVec[n][pipelineResp_port];
endmethod
method LLCRqState getState(cRqIndexT n);
return stateVec[n][pipelineResp_port];
endmethod
method slotT getSlot(cRqIndexT n);
return slotVec[n][pipelineResp_port];
endmethod
method Maybe#(Line) getData(cRqIndexT n);
return dataValidVec[n][pipelineResp_port] ? Valid (dataVec[n][pipelineResp_port]) : Invalid;
endmethod
method Maybe#(cRqIndexT) getAddrSucc(cRqIndexT n);
return addrSuccValidVec[n][pipelineResp_port] ? Valid (addrSuccFile.sub(n)) : Invalid;
endmethod
method Maybe#(cRqIndexT) getRepSucc(cRqIndexT n);
return repSuccValidVec[n][pipelineResp_port] ? Valid (repSuccFile.sub(n)) : Invalid;
endmethod
method Action setData(cRqIndexT n, Maybe#(Line) d);
dataValidVec[n][pipelineResp_port] <= isValid(d);
dataVec[n][pipelineResp_port] <= fromMaybe(?, d);
endmethod
method Action setStateSlot(cRqIndexT n, LLCRqState state, slotT slot);
stateVec[n][pipelineResp_port] <= state;
writeSlot(pipelineResp_port, n, slot);
endmethod
method Action setAddrSucc(cRqIndexT n, Maybe#(cRqIndexT) succ);
addrSuccValidVec[n][pipelineResp_port] <= isValid(succ);
addrSuccFile.upd(n, fromMaybe(?, succ));
endmethod
method Action setRepSucc(cRqIndexT n, Maybe#(cRqIndexT) succ);
repSuccValidVec[n][pipelineResp_port] <= isValid(succ);
repSuccFile.upd(n, fromMaybe(?, succ));
endmethod
method Maybe#(cRqIndexT) searchEndOfChain(Addr addr);
function Bool isEndOfChain(Integer i);
// check entry i is end of chain or not
let state = stateVec[i][pipelineResp_port];
Bool notDone = state != Done;
Bool processedOnce = state != Empty && state != Init;
Bool addrMatch = getLineAddr(getAddrFromReq(reqVec[i][pipelineResp_port])) == getLineAddr(addr);
Bool noAddrSucc = !addrSuccValidVec[i][pipelineResp_port];
return notDone && processedOnce && addrMatch && noAddrSucc;
endfunction
Vector#(cRqNum, Integer) idxVec = genVector;
return searchIndex(isEndOfChain, idxVec);
endmethod
endinterface
`ifdef CHECK_DEADLOCK
interface stuck = toGet(stuckQ);
`else
interface stuck = nullGet;
`endif
endmodule

View File

@@ -0,0 +1,371 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import FShow::*;
import Types::*;
import CCTypes::*;
import CCPipe::*;
import RWBramCore::*;
import RandomReplace::*;
export LLPipeCRqIn(..);
export LLPipeMRsIn(..);
export LLPipeIn(..);
export LLCmd(..);
export LLPipe(..);
export mkLLPipe;
// type param ordering: bank < child < way < index < tag < cRq
// input types
typedef struct {
Addr addr;
cRqIdxT mshrIdx;
} LLPipeCRqIn#(type cRqIdxT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState; // come from req in MSHR (E or M)
Line data; // come from memory must be valid
wayT way; // come from MSHR
} LLPipeMRsIn#(type wayT) deriving(Bits, Eq, FShow);
typedef union tagged {
LLPipeCRqIn#(cRqIdxT) CRq;
CRsMsg#(childT) CRs;
LLPipeMRsIn#(wayT) MRs;
} LLPipeIn#(
type childT,
type wayT,
type cRqIdxT
) deriving (Bits, Eq, FShow);
// output cmd to the processing rule in LLC
typedef union tagged {
cRqIdxT LLCRq; // mshr idx of the cRq
childT LLCRs; // which child is downgrading
void LLMRs;
} LLCmd#(type childT, type cRqIdxT) deriving (Bits, Eq, FShow);
interface LLPipe#(
numeric type lgBankNum,
numeric type childNum,
numeric type wayNum,
type indexT,
type tagT,
type cRqIdxT
);
method Action send(LLPipeIn#(Bit#(TLog#(childNum)), Bit#(TLog#(wayNum)), cRqIdxT) r);
method Bool notEmpty;
method PipeOut#(
Bit#(TLog#(wayNum)),
tagT, Msi, Vector#(childNum, Msi),
Maybe#(CRqOwner#(cRqIdxT)), void, RandRepInfo, // no other
Line, LLCmd#(Bit#(TLog#(childNum)), cRqIdxT)
) first;
method PipeOut#(
Bit#(TLog#(wayNum)),
tagT, Msi, Vector#(childNum, Msi),
Maybe#(CRqOwner#(cRqIdxT)), void, RandRepInfo, // no other
Line, LLCmd#(Bit#(TLog#(childNum)), cRqIdxT)
) unguard_first;
method Action deqWrite(
Maybe#(cRqIdxT) swapRq,
RamData#(tagT, Msi, Vector#(childNum, Msi), Maybe#(CRqOwner#(cRqIdxT)), void, Line) wrRam, // always write BRAM
Bool updateRep
);
endinterface
// real cmd used in pipeline
typedef struct {
Addr addr;
childT child;
} LLPipeCRsCmd#(type childT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
wayT way;
} LLPipeMRsCmd#(type wayT) deriving(Bits, Eq, FShow);
typedef union tagged {
LLPipeCRqIn#(cRqIdxT) CRq;
LLPipeCRsCmd#(childT) CRs;
LLPipeMRsCmd#(wayT) MRs;
} LLPipeCmd#(
type childT,
type wayT,
type cRqIdxT
) deriving (Bits, Eq, FShow);
module mkLLPipe(
LLPipe#(lgBankNum, childNum, wayNum, indexT, tagT, cRqIdxT)
) provisos(
Alias#(childT, Bit#(TLog#(childNum))),
Alias#(wayT, Bit#(TLog#(wayNum))),
Alias#(dirT, Vector#(childNum, Msi)),
Alias#(ownerT, Maybe#(CRqOwner#(cRqIdxT))),
Alias#(otherT, void), // no other cache info
Alias#(repT, RandRepInfo), // use random replace
Alias#(pipeInT, LLPipeIn#(childT, wayT, cRqIdxT)),
Alias#(pipeCmdT, LLPipeCmd#(childT, wayT, cRqIdxT)),
Alias#(llCmdT, LLCmd#(childT, cRqIdxT)),
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, llCmdT)), // output type
Alias#(infoT, CacheInfo#(tagT, Msi, dirT, ownerT, otherT)),
Alias#(ramDataT, RamData#(tagT, Msi, dirT, ownerT, otherT, Line)),
Alias#(respStateT, RespState#(Msi)),
Alias#(tagMatchResT, TagMatchResult#(wayT)),
Alias#(updateByUpCsT, UpdateByUpCs#(Msi)),
Alias#(updateByDownDirT, UpdateByDownDir#(Msi, dirT)),
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), indexSz))),
// requirement
Alias#(indexT, Bit#(indexSz)),
Alias#(tagT, Bit#(tagSz)),
Alias#(cRqIdxT, Bit#(_cRqIdxSz)),
Add#(indexSz, a__, AddrSz),
Add#(tagSz, b__, AddrSz)
);
// RAMs
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam <- replicateM(mkRWBramCore);
RWBramCore#(indexT, repT) repRam <- mkRandRepRam;
RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore;
// initialize RAM
Reg#(Bool) initDone <- mkReg(False);
Reg#(indexT) initIndex <- mkReg(0);
rule doInit(!initDone);
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
infoRam[i].wrReq(initIndex, CacheInfo {
tag: 0,
cs: I,
dir: replicate(I),
owner: Invalid,
other: ?
});
end
repRam.wrReq(initIndex, randRepInitInfo); // useless for random replace
initIndex <= initIndex + 1;
if(initIndex == maxBound) begin
initDone <= True;
end
endrule
// random replacement
RandomReplace#(wayNum) randRep <- mkRandomReplace;
// functions
function Addr getAddrFromCmd(pipeCmdT cmd);
return (case(cmd) matches
tagged CRq .r: r.addr;
tagged CRs .r: r.addr;
tagged MRs .r: r.addr;
default: ?;
endcase);
endfunction
function indexT getIndex(pipeCmdT cmd);
Addr a = getAddrFromCmd(cmd);
return truncate(a >> (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
endfunction
function ActionValue#(tagMatchResT) tagMatch(
pipeCmdT cmd,
Vector#(wayNum, tagT) tagVec,
Vector#(wayNum, Msi) csVec,
Vector#(wayNum, ownerT) ownerVec,
repT repInfo
);
return actionvalue
function tagT getTag(Addr a) = truncateLSB(a);
$display("%t LL %m tagMatch: ", $time,
fshow(cmd), " ; ",
fshow(getTag(getAddrFromCmd(cmd))), " ; ",
fshow(tagVec), " ; ",
fshow(csVec), " ; ",
fshow(ownerVec)
);
if(cmd matches tagged MRs .rs) begin
// MRs directly read from cmd
return TagMatchResult {
way: rs.way,
pRqMiss: False
};
end
else begin
// CRq/CRs: need tag matching
Addr addr = getAddrFromCmd(cmd);
tagT tag = getTag(addr);
// find hit way (we do not check replacing bit in LLC)
// this makes <cRq a> blocked by other <cRq b> which is replacing addr a
function Bool isMatch(Tuple2#(Msi, tagT) csTag);
match {.cs, .t} = csTag;
return cs > I && t == tag;
endfunction
Maybe#(wayT) hitWay = searchIndex(isMatch, zip(csVec, tagVec));
if(hitWay matches tagged Valid .w) begin
return TagMatchResult {
way: w,
pRqMiss: False
};
end
else begin
// cRs must hit, so only cRq cannot enter here
doAssert(cmd matches tagged CRq ._rq ? True : False,
"only cRq can tag match miss"
);
// find a unlocked way to replace for cRq
Vector#(wayNum, Bool) unlocked = ?;
Vector#(wayNum, Bool) invalid = ?;
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
invalid[i] = csVec[i] == I;
unlocked[i] = !isValid(ownerVec[i]);
end
Maybe#(wayT) repWay = randRep.getReplaceWay(unlocked, invalid);
// sanity check: repWay must be valid
doAssert(isValid(repWay), "should always find a way to replace");
return TagMatchResult {
way: fromMaybe(?, repWay),
pRqMiss: False
};
end
end
endactionvalue;
endfunction
function ActionValue#(updateByUpCsT) updateByUpCs(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs
);
actionvalue
doAssert(toState > oldCs, "should truly upgrade cs");
doAssert((oldCs == I) && dataV, "LLC mRs always has data");
return UpdateByUpCs {cs: toState};
endactionvalue
endfunction
function ActionValue#(updateByDownDirT) updateByDownDir(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs, dirT oldDir
);
actionvalue
// update dir
dirT newDir = oldDir;
if(cmd matches tagged CRs .cRs) begin
if(dataV) begin
doAssert(oldDir[cRs.child] >= E, "cRs with data, dir must >= E");
end
else begin
doAssert(oldDir[cRs.child] < M, "cRs without data, dir must < M");
end
if(oldDir[cRs.child] == M) begin
doAssert(dataV, "must have data");
end
newDir[cRs.child] = toState;
end
else begin
// should not happen
doAssert(False, "only cRs updates dir");
end
// update cs
// XXX since child can upgrade from E to M silently, use data valid
// to determine if we need to upgrade to M. Note that the data
// valid field has not been overwritten by bypass in CCPipe.
Msi newCs = oldCs;
if(dataV) begin
doAssert(oldCs >= E, "cRs has data, cs must >= E");
newCs = M;
end
return UpdateByDownDir {cs: newCs, dir: newDir};
endactionvalue
endfunction
function ActionValue#(repT) updateRepInfo(repT r, wayT w);
actionvalue
return ?; // random replace does not have bookkeeping
endactionvalue
endfunction
CCPipe#(wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT) pipe <- mkCCPipe(
regToReadOnly(initDone), getIndex, tagMatch,
updateByUpCs, updateByDownDir, updateRepInfo,
infoRam, repRam, dataRam
);
// get first output from CCPipe output
function pipeOutT getFirst(PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT) pout);
return PipeOut {
cmd: (case(pout.cmd) matches
tagged CRq .rq: LLCRq (rq.mshrIdx);
tagged CRs .rs: LLCRs (rs.child);
tagged MRs .rs: LLMRs;
default: ?;
endcase),
way: pout.way,
pRqMiss: pout.pRqMiss,
ram: pout.ram,
repInfo: pout.repInfo
};
endfunction
method Action send(pipeInT req);
case(req) matches
tagged CRq .rq: begin
pipe.enq(CRq (rq), Invalid, Invalid);
end
tagged CRs .rs: begin
pipe.enq(CRs (LLPipeCRsCmd {
addr: rs.addr,
child: rs.child
}), rs.data, DownDir (rs.toState));
end
tagged MRs .rs: begin
pipe.enq(MRs (LLPipeMRsCmd {
addr: rs.addr,
way: rs.way
}), Valid (rs.data), UpCs (rs.toState));
end
endcase
endmethod
// need to adapt pipeline output to real output format
method pipeOutT first;
return getFirst(pipe.first); // guarded version
endmethod
method pipeOutT unguard_first;
return getFirst(pipe.unguard_first); // unguarded version
endmethod
method notEmpty = pipe.notEmpty;
method Action deqWrite(Maybe#(cRqIdxT) swapRq, ramDataT wrRam, Bool updateRep);
// get new cmd
Addr addr = getAddrFromCmd(pipe.first.cmd); // inherit addr
Maybe#(pipeCmdT) newCmd = Invalid;
if(swapRq matches tagged Valid .idx) begin
newCmd = Valid (CRq (LLPipeCRqIn {addr: addr, mshrIdx: idx}));
end
// call pipe
pipe.deqWrite(newCmd, wrRam, updateRep);
endmethod
endmodule

View File

@@ -0,0 +1,83 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Ehr::*;
import CCTypes::*;
import Vector::*;
import ProcTypes::*;
interface MshrDeadlockChecker#(numeric type num);
method ActionValue#(Maybe#(Bit#(TLog#(num)))) getStuckIdx; // get deadlock MSHR idx
method Action initEntry(Bit#(TLog#(num)) n); // new MSHR entry allocated
method Action releaseEntry(Bit#(TLog#(num)) n); // existing MSHR entry released
endinterface
module mkMshrDeadlockChecker(MshrDeadlockChecker#(num)) provisos(
Alias#(idxT, Bit#(TLog#(num)))
);
Integer check_port = 0;
Integer incr_port = 1;
// timer for each entry to detect deadlock: being processed for 64M cycles
Vector#(num, Ehr#(2, Maybe#(DeadlockTimer))) timer <- replicateM(mkEhr(Invalid));
// when new entry is allocated, init timer
Vector#(num, PulseWire) init <- replicateM(mkPulseWire);
// when existing entry is released, end the timer
Vector#(num, PulseWire) done <- replicateM(mkPulseWire);
(* fire_when_enabled, no_implicit_conditions *)
rule incrTimer;
for(Integer i = 0; i < valueof(num); i = i+1) begin
if(init[i]) begin
timer[i][incr_port] <= Valid (0);
end
else if(done[i]) begin
timer[i][incr_port] <= Invalid;
end
else if(timer[i][incr_port] matches tagged Valid .t &&& t != maxBound) begin
timer[i][incr_port] <= Valid (t + 1);
end
end
endrule
method ActionValue#(Maybe#(idxT)) getStuckIdx;
function Bool isDeadlock(Integer i);
return timer[i][check_port] == Valid (maxBound);
endfunction
Vector#(num, Integer) idxVec = genVector;
if(searchIndex(isDeadlock, idxVec) matches tagged Valid .n) begin
timer[n][check_port] <= Valid (0);
return Valid (n);
end
else begin
return Invalid;
end
endmethod
method Action initEntry(idxT n);
init[n].send;
endmethod
method Action releaseEntry(idxT n);
done[n].send;
endmethod
endmodule

View File

@@ -0,0 +1,64 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import BRAMCore::*;
import Fifo::*;
interface RWBramCore#(type addrT, type dataT);
method Action wrReq(addrT a, dataT d);
method Action rdReq(addrT a);
method dataT rdResp;
method Bool rdRespValid;
method Action deqRdResp;
endinterface
module mkRWBramCore(RWBramCore#(addrT, dataT)) provisos(
Bits#(addrT, addrSz), Bits#(dataT, dataSz)
);
BRAM_DUAL_PORT#(addrT, dataT) bram <- mkBRAMCore2(valueOf(TExp#(addrSz)), False);
BRAM_PORT#(addrT, dataT) wrPort = bram.a;
BRAM_PORT#(addrT, dataT) rdPort = bram.b;
// 1 elem pipeline fifo to add guard for read req/resp
// must be 1 elem to make sure rdResp is not corrupted
// BRAMCore should not change output if no req is made
Fifo#(1, void) rdReqQ <- mkPipelineFifo;
method Action wrReq(addrT a, dataT d);
wrPort.put(True, a, d);
endmethod
method Action rdReq(addrT a);
rdReqQ.enq(?);
rdPort.put(False, a, ?);
endmethod
method dataT rdResp if(rdReqQ.notEmpty);
return rdPort.read;
endmethod
method rdRespValid = rdReqQ.notEmpty;
method Action deqRdResp;
rdReqQ.deq;
endmethod
endmodule

View File

@@ -0,0 +1,90 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import Fifo::*;
import CCTypes::*;
import RWBramCore::*;
// random replace does not need any bookkeeping
typedef void RandRepInfo;
function RandRepInfo randRepInitInfo;
return ?;
endfunction
module mkRandRepRam(RWBramCore#(indexT, RandRepInfo));
Fifo#(1, void) rdReqQ <- mkPipelineFifo;
method Action wrReq(indexT a, RandRepInfo d);
noAction;
endmethod
method Action rdReq(indexT a);
rdReqQ.enq(?);
endmethod
method RandRepInfo rdResp if(rdReqQ.notEmpty);
return ?;
endmethod
method rdRespValid = rdReqQ.notEmpty;
method deqRdResp = rdReqQ.deq;
endmodule
interface RandomReplace#(numeric type wayNum);
// find a way to replace, which is not locked
// and Invalid way has priority
method Maybe#(Bit#(TLog#(wayNum))) getReplaceWay(
Vector#(wayNum, Bool) unlocked,
Vector#(wayNum, Bool) invalid
);
endinterface
module mkRandomReplace(RandomReplace#(wayNum)) provisos(
Alias#(wayT, Bit#(TLog#(wayNum)))
);
Reg#(wayT) randWay <- mkReg(0);
rule tick;
randWay <= randWay == fromInteger(valueOf(wayNum) - 1) ? 0 : randWay + 1;
endrule
method Maybe#(wayT) getReplaceWay(Vector#(wayNum, Bool) unlocked, Vector#(wayNum, Bool) invalid);
// first search for invalid & unlocked way
function Bool isInvUnlock(Integer i);
return unlocked[i] && invalid[i];
endfunction
Vector#(wayNum, Integer) idxVec = genVector;
Maybe#(wayT) repWay = searchIndex(isInvUnlock, idxVec);
if(!isValid(repWay)) begin
// check whether random way is unlocked
if(unlocked[randWay]) begin
repWay = Valid (randWay);
end
else begin
// just find a unlocked way
repWay = searchIndex(id, unlocked);
end
end
return repWay;
endmethod
endmodule

View File

@@ -0,0 +1,629 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
`include "ProcConfig.bsv"
import Types::*;
import MemoryTypes::*;
import Amo::*;
import Cntrs::*;
import Vector::*;
import ConfigReg::*;
import FIFO::*;
import GetPut::*;
import ClientServer::*;
import CCTypes::*;
import ICRqMshr::*;
import CCPipe::*;
import SelfInvIPipe ::*;
import FShow::*;
import DefaultValue::*;
import Fifo::*;
import CacheUtils::*;
import Performance::*;
import LatencyTimer::*;
import RandomReplace::*;
export SelfInvICRqStuck(..);
export SelfInvIPRqStuck(..);
export SelfInvIBank(..);
export mkSelfInvIBank;
// L1 I$, no pRq
typedef struct {
Addr addr;
ICRqState state;
Bool waitP;
} SelfInvICRqStuck deriving(Bits, Eq, FShow);
typedef void SelfInvIPRqStuck; // not used
interface SelfInvIBank#(
numeric type supSz, // superscalar size
numeric type lgBankNum,
numeric type wayNum,
numeric type indexSz,
numeric type tagSz,
numeric type cRqNum
);
interface ChildCacheToParent#(Bit#(TLog#(wayNum)), void) to_parent;
interface InstServer#(supSz) to_proc; // to child, i.e. processor
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
interface Get#(SelfInvICRqStuck) cRqStuck;
interface Get#(SelfInvIPRqStuck) pRqStuck;
// security: flush (not implemented)
method Action flush;
method Bool flush_done;
// reconcile
method Action reconcile;
method Bool reconcile_done;
// performance
method Action setPerfStatus(Bool stats);
method Data getPerfData(L1IPerfType t);
endinterface
module mkSelfInvIBank#(
Bit#(lgBankNum) bankId,
module#(ICRqMshr#(cRqNum, wayT, tagT, procRqT, resultT)) mkICRqMshrLocal,
module#(SelfInvIPipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT)) mkIPipeline
)(
SelfInvIBank#(supSz, lgBankNum, wayNum, indexSz, tagSz, cRqNum)
) provisos(
Alias#(wayT, Bit#(TLog#(wayNum))),
Alias#(indexT, Bit#(indexSz)),
Alias#(tagT, Bit#(tagSz)),
Alias#(cRqIdxT, Bit#(TLog#(cRqNum))),
Alias#(pRqIdxT, Bit#(TLog#(pRqNum))),
Alias#(cacheOwnerT, Maybe#(cRqIdxT)), // owner cannot be pRq
Alias#(otherT, void),
Alias#(cacheInfoT, CacheInfo#(tagT, Msi, void, cacheOwnerT, otherT)),
Alias#(ramDataT, RamData#(tagT, Msi, void, cacheOwnerT, otherT, Line)),
Alias#(procRqT, ProcRqToI),
Alias#(cRqToPT, CRqMsg#(wayT, void)),
Alias#(cRsToPT, CRsMsg#(void)),
Alias#(pRqFromPT, PRqMsg#(void)),
Alias#(pRsFromPT, PRsMsg#(wayT, void)),
Alias#(pRqRsFromPT, PRqRsMsg#(wayT, void)),
Alias#(cRqSlotT, ICRqSlot#(wayT, tagT)), // cRq MSHR slot
Alias#(iCmdT, SelfInvICmd#(cRqIdxT)),
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, void, cacheOwnerT, otherT, RandRepInfo, Line, iCmdT)),
Alias#(resultT, Vector#(supSz, Maybe#(Instruction))),
// requirements
FShow#(pipeOutT),
Add#(tagSz, a__, AddrSz),
// make sure: cRqNum <= wayNum
Add#(cRqNum, b__, wayNum),
Add#(TAdd#(tagSz, indexSz), TAdd#(lgBankNum, LgLineSzBytes), AddrSz)
);
ICRqMshr#(cRqNum, wayT, tagT, procRqT, resultT) cRqMshr <- mkICRqMshrLocal;
SelfInvIPipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT) pipeline <- mkIPipeline;
Fifo#(1, Addr) rqFromCQ <- mkBypassFifo;
Fifo#(2, cRqToPT) rqToPQ <- mkCFFifo;
Fifo#(2, pRqRsFromPT) fromPQ <- mkCFFifo;
FIFO#(cRqIdxT) rqToPIndexQ <- mkSizedFIFO(valueOf(cRqNum));
// index Q to order all in flight cRq for in-order resp
FIFO#(cRqIdxT) cRqIndexQ <- mkSizedFIFO(valueof(cRqNum));
// Reconcile states
Reg#(Bool) needReconcile <- mkReg(False);
Reg#(Bool) waitReconcileDone <- mkReg(False);
`ifdef DEBUG_ICACHE
// id for each cRq, incremented when each new req comes
Reg#(Bit#(64)) cRqId <- mkReg(0);
// FIFO to signal the id of cRq that is performed
// FIFO has 0 cycle latency to match L1 D$ resp latency
Fifo#(1, DebugICacheResp) cRqDoneQ <- mkBypassFifo;
`endif
`ifdef PERF_COUNT
Reg#(Bool) doStats <- mkConfigReg(False);
Count#(Data) ldCnt <- mkCount(0);
Count#(Data) ldMissCnt <- mkCount(0);
Count#(Data) ldMissLat <- mkCount(0);
Count#(Data) reconcileCnt <- mkCount(0);
LatencyTimer#(cRqNum, 10) latTimer <- mkLatencyTimer;
function Action incrReqCnt;
action
if(doStats) begin
ldCnt.incr(1);
end
endaction
endfunction
function Action incrMissCnt(cRqIdxT idx);
action
let lat <- latTimer.done(idx);
if(doStats) begin
ldMissLat.incr(zeroExtend(lat));
ldMissCnt.incr(1);
end
endaction
endfunction
`endif
function tagT getTag(Addr a) = truncateLSB(a);
// XXX since I$ may be requested by processor constantly
// cRq may come at every cycle, so we must make cRq has lower priority than pRq/pRs
// otherwise the whole system may deadlock/livelock
// we stop accepting cRq when we need to reconcile
rule cRqTransfer(!needReconcile);
Addr addr <- toGet(rqFromCQ).get;
`ifdef DEBUG_ICACHE
procRqT r = ProcRqToI {addr: addr, id: cRqId};
cRqId <= cRqId + 1;
`else
procRqT r = ProcRqToI {addr: addr};
`endif
cRqIdxT n <- cRqMshr.getEmptyEntryInit(r);
// send to pipeline
pipeline.send(CRq (SelfInvIPipeRqIn {
addr: r.addr,
mshrIdx: n
}));
// enq to indexQ for in order resp
cRqIndexQ.enq(n);
`ifdef PERF_COUNT
// performance counter: cRq type
incrReqCnt;
`endif
$display("%t I %m cRqTransfer: ", $time,
fshow(n), " ; ",
fshow(r)
);
endrule
// this descending urgency is necessary to avoid deadlock/livelock
// pRq cannot happen, because I$ is never exclusive
(* descending_urgency = "pRqTransfer, cRqTransfer" *)
rule pRqTransfer(fromPQ.first matches tagged PRq .req);
fromPQ.deq;
$display("%t I %m pRqTransfer: ", $time, fshow(req));
doAssert(False, "should not have pRq");
endrule
// this descending urgency is necessary to avoid deadlock/livelock
(* descending_urgency = "pRsTransfer, cRqTransfer" *)
rule pRsTransfer(fromPQ.first matches tagged PRs .resp);
fromPQ.deq;
pipeline.send(PRs (SelfInvIPipePRsIn {
addr: resp.addr,
toState: S,
data: resp.data,
way: resp.id
}));
$display("%t I %m pRsTransfer: ", $time, fshow(resp));
doAssert(resp.toState == S && isValid(resp.data), "I$ must upgrade to S with data");
endrule
rule sendRqToP;
rqToPIndexQ.deq;
cRqIdxT n = rqToPIndexQ.first;
procRqT req = cRqMshr.sendRqToP.getRq(n);
cRqSlotT slot = cRqMshr.sendRqToP.getSlot(n);
cRqToPT cRqToP = CRqMsg {
addr: req.addr,
fromState: I, // I$ upgrade from I
toState: S, // I$ upgrade to S
canUpToE: False,
id: slot.way,
child: ?
};
rqToPQ.enq(cRqToP);
$display("%t I %m sendRqToP: ", $time,
fshow(n), " ; ",
fshow(req), " ; ",
fshow(slot), " ; ",
fshow(cRqToP)
);
`ifdef PERF_COUNT
// performance counter: start miss timer
latTimer.start(n);
`endif
endrule
// last stage of pipeline: process req
// XXX: in L1, pRq cannot exist in dependency chain
// because there are only two ways to include pRq into chain
// (1) append to a cRq that could finish, but such cRq must have been directly reponded
// (2) overtake cRq (S->M), but such downgrade can be done instaneously without the need of chaining
// (this cannot happen in I$)
// Thus, dependency chain in L1 only contains cRq
// pipeline outputs
pipeOutT pipeOut = pipeline.first;
ramDataT ram = pipeOut.ram;
// get proc req to select from cRqMshr
procRqT pipeOutCRq = cRqMshr.pipelineResp.getRq(
case(pipeOut.cmd) matches
tagged ICRq .n: (n);
default: (fromMaybe(0, ram.info.owner)); // L1PRs
endcase
);
// function to get superscaler inst read result
function resultT readInst(Line line, Addr addr);
Vector#(LineSzInst, Instruction) instVec = unpack(pack(line));
// the start offset for reading inst
LineInstOffset startSel = getLineInstOffset(addr);
// calculate the maximum inst count that could be read from line
LineInstOffset maxCntMinusOne = maxBound - startSel;
// read inst superscalaer
resultT val = ?;
for(Integer i = 0; i < valueof(supSz); i = i+1) begin
if(fromInteger(i) <= maxCntMinusOne) begin
LineInstOffset sel = startSel + fromInteger(i);
val[i] = Valid (instVec[sel]);
end
else begin
val[i] = Invalid;
end
end
return val;
endfunction
// function to process cRq hit (MSHR slot may have garbage)
function Action cRqHit(cRqIdxT n, procRqT req);
action
$display("%t I %m pipelineResp: Hit func: ", $time,
fshow(n), " ; ",
fshow(req)
);
// check tag & cs: even this function is called by pRs, tag should match,
// because tag is written into cache before sending req to parent
doAssert(ram.info.tag == getTag(req.addr) && ram.info.cs == S,
"cRqHit but tag or cs incorrect"
);
// deq pipeline or swap in successor
Maybe#(cRqIdxT) succ = cRqMshr.pipelineResp.getSucc(n);
pipeline.deqWrite(succ, RamData {
info: CacheInfo {
tag: getTag(req.addr), // should be the same as original tag
cs: ram.info.cs, // use cs in ram
dir: ?,
owner: succ,
other: ?
},
line: ram.line
}, True); // hit, so update rep info
// process req to get superscalar inst read results
// set MSHR entry as Done & save inst results
let instResult = readInst(ram.line, req.addr);
cRqMshr.pipelineResp.setResult(n, instResult);
cRqMshr.pipelineResp.setStateSlot(n, Done, ?);
$display("%t I %m pipelineResp: Hit func: update ram: ", $time,
fshow(succ), " ; ", fshow(instResult)
);
`ifdef DEBUG_ICACHE
// signal that this req is performed
cRqDoneQ.enq(DebugICacheResp {
id: req.id,
line: ram.line
});
`endif
endaction
endfunction
rule pipelineResp_cRq(pipeOut.cmd matches tagged ICRq .n);
$display("%t I %m pipelineResp: ", $time, fshow(pipeOut));
procRqT procRq = pipeOutCRq;
$display("%t I %m pipelineResp: cRq: ", $time, fshow(n), " ; ", fshow(procRq));
// find end of dependency chain
Maybe#(cRqIdxT) cRqEOC = cRqMshr.pipelineResp.searchEndOfChain(procRq.addr);
// function to process cRq miss without replacement (MSHR slot may have garbage)
// We never replace in self-inv I$, because all S lines can be replaced silently
function Action cRqMissNoReplacement;
action
cRqSlotT cSlot = cRqMshr.pipelineResp.getSlot(n);
// it is impossible in L1 to have slot.waitP == True in this function
// because cRq is not set to Depend when pRq invalidates it (pRq just directly resp)
doAssert(!cSlot.waitP, "waitP must be false");
// No exclusive in I$
doAssert(ram.info.cs <= S, "no exclusive in I$");
// This cannot be a hit
doAssert(ram.info.cs == I || ram.info.tag != getTag(procRq.addr), "cannot hit");
// Thus we must send req to parent
rqToPIndexQ.enq(n);
// update mshr
cRqMshr.pipelineResp.setStateSlot(n, WaitSt, ICRqSlot {
way: pipeOut.way, // use way from pipeline
repTag: ?, // no replacement
waitP: True // must fetch from parent
});
// deq pipeline & set owner, tag
pipeline.deqWrite(Invalid, RamData {
info: CacheInfo {
tag: getTag(procRq.addr), // tag may be garbage if cs == I or silent replace
cs: I, // line must be invalid
dir: ?,
owner: Valid (n), // owner is req itself
other: ?
},
line: ram.line
}, False);
endaction
endfunction
// function to set cRq to Depend, and make no further change to cache
function Action cRqSetDepNoCacheChange;
action
cRqMshr.pipelineResp.setStateSlot(n, Depend, defaultValue);
pipeline.deqWrite(Invalid, pipeOut.ram, False);
endaction
endfunction
if(ram.info.owner matches tagged Valid .cOwner) begin
if(cOwner != n) begin
// owner is another cRq, so must just go through tag match
// tag match must be hit (because replacement algo won't give a way with owner)
doAssert(ram.info.cs == S && ram.info.tag == getTag(procRq.addr),
"cRq should hit in tag match"
);
// should be added to a cRq in dependency chain & deq from pipeline
doAssert(isValid(cRqEOC), "cRq hit on another cRq, cRqEOC must be true");
cRqMshr.pipelineResp.setSucc(fromMaybe(?, cRqEOC), Valid (n));
cRqSetDepNoCacheChange;
$display("%t I %m pipelineResp: cRq: own by other cRq ", $time,
fshow(cOwner), ", depend on cRq ", fshow(cRqEOC)
);
end
else begin
// owner is myself, so must be swapped in
// tag should match, since always swapped in by cRq, cs = S
// Reconcile happens only when no cRq in MSHR, so it won't affect swap
doAssert(ram.info.tag == getTag(procRq.addr) && ram.info.cs == S,
"cRq swapped in by previous cRq, tag must match & cs = S"
);
// Hit
$display("%t I %m pipelineResp: cRq: own by itself, hit", $time);
cRqHit(n, procRq);
end
end
else begin
// cache has no owner, cRq must just go through tag match
// check for cRqEOC to append to dependency chain
if(cRqEOC matches tagged Valid .k) begin
$display("%t I %m pipelineResp: cRq: no owner, depend on cRq ", $time, fshow(k));
cRqMshr.pipelineResp.setSucc(k, Valid (n));
cRqSetDepNoCacheChange;
end
else if(ram.info.cs > I && ram.info.tag == getTag(procRq.addr)) begin
$display("%t I %m pipelineResp: cRq: no owner, hit", $time);
cRqHit(n, procRq);
end
else begin
// can always sliently replace
$display("%t I %m pipelineResp: cRq: no owner, miss no replace", $time);
cRqMissNoReplacement;
end
end
endrule
rule pipelineResp_pRs(pipeOut.cmd == IPRs);
$display("%t I %m pipelineResp: ", $time, fshow(pipeOut));
$display("%t I %m pipelineResp: pRs: ", $time);
if(ram.info.owner matches tagged Valid .cOwner) begin
procRqT procRq = pipeOutCRq;
doAssert(ram.info.cs == S && ram.info.tag == getTag(procRq.addr),
"pRs must be a hit"
);
cRqHit(cOwner, procRq);
`ifdef PERF_COUNT
// performance counter: miss cRq
incrMissCnt(cOwner);
`endif
end
else begin
doAssert(False, ("pRs owner must match some cRq"));
end
endrule
// Reconcile lines in S state: start after cRq MSHR is empty
// Since cRqTransfer rule cannot fire when needReconcile is true, we use a
// wire to catch cRqMshr.empty to avoid scheduling cycles
PulseWire cRqMshrEmpty <- mkPulseWire;
(* fire_when_enabled, no_implicit_conditions *)
rule setCRqMshrEmpty(cRqMshr.emptyForFlush);
cRqMshrEmpty.send;
endrule
rule startReconcile(needReconcile && !waitReconcileDone && cRqMshrEmpty);
pipeline.reconcile;
waitReconcileDone <= True;
$display("%t I %m startReconcile", $time);
`ifdef PERF_COUNT
if(doStats) begin
reconcileCnt.incr(1);
end
`endif
endrule
rule completeReconcile(needReconcile && waitReconcileDone && pipeline.reconcile_done);
needReconcile <= False;
waitReconcileDone <= False;
$display("%t I %m completeReconcile", $time);
endrule
interface ChildCacheToParent to_parent;
interface rsToP = nullFifoDeq;
interface rqToP = toFifoDeq(rqToPQ);
interface fromP = toFifoEnq(fromPQ);
endinterface
interface InstServer to_proc;
interface Put req;
method Action put(Addr addr);
rqFromCQ.enq(addr);
endmethod
endinterface
interface Get resp;
method ActionValue#(resultT) get if(
cRqMshr.sendRsToC.getResult(cRqIndexQ.first) matches tagged Valid .inst
);
cRqIndexQ.deq;
cRqMshr.sendRsToC.releaseEntry(cRqIndexQ.first); // release MSHR entry
$display("%t I %m sendRsToC: ", $time,
fshow(cRqIndexQ.first), " ; ",
fshow(inst)
);
return inst;
endmethod
endinterface
`ifdef DEBUG_ICACHE
interface done = toGet(cRqDoneQ);
`endif
endinterface
interface Get cRqStuck;
method ActionValue#(SelfInvICRqStuck) get;
let s <- cRqMshr.stuck.get;
return SelfInvICRqStuck {
addr: s.req.addr,
state: s.state,
waitP: s.waitP
};
endmethod
endinterface
interface pRqStuck = nullGet;
`ifdef SECURITY
method Action flush if(flushDone);
flushDone <= False;
endmethod
method flush_done = flushDone._read;
`else
method flush = noAction;
method flush_done = True;
`endif
method Action reconcile if(!needReconcile);
needReconcile <= True;
endmethod
method Bool reconcile_done;
return !needReconcile;
endmethod
method Action setPerfStatus(Bool stats);
`ifdef PERF_COUNT
doStats <= stats;
`else
noAction;
`endif
endmethod
method Data getPerfData(L1IPerfType t);
return (case(t)
`ifdef PERF_COUNT
L1ILdCnt: ldCnt;
L1ILdMissCnt: ldMissCnt;
L1ILdMissLat: ldMissLat;
L1IReconcileCnt: reconcileCnt;
`endif
default: 0;
endcase);
endmethod
endmodule
// Scheduling note
// cRqTransfer (toC.req.put): write new cRq MSHR entry, cRqMshr.getEmptyEntry
// pRqTransfer: write new pRq MSHR entry, pRqMshr.getEmptyEntry
// pRsTransfer: -
// sendRsToC (toC.resp.get): read cRq MSHR result, releaseEntry
// sendRsToP_cRq: read cRq MSHR req/slot that is replacing
// sendRsToP_pRq: read pRq MSHR entry that is responding, pRqMshr.releaseEntry
// sendRqToP: read cRq MSHR req/slot that is requesting parent
// pipelineResp_cRq:
// -- read cRq MSHR req/state/slot currently processed
// -- write cRq MSHR state/slot/result currently processed
// -- write succ of some existing cRq MSHR entry (in WaitNewTag or WaitSt)
// -- read all state/req/succ in cRq MSHR entry (searchEOC)
// -- not affected by write in cRqTransfer (state change is Empty->Init)
// -- not affected by write in sendRsC (state change is Done->Empty)
// pipelineResp_pRs:
// -- read cRq MSHR req/succ, write cRq MSHR state/slot/result
// pipelineResp_pRq:
// -- r/w pRq MSHR entry, pRqMshr.releaseEntry
// ---- conflict analysis ----
// XXXTransfer is conflict with each other
// Impl of getEmptyEntry and releaseEntry ensures that they are not on the same entry (e.g. cRqTransfer v.s. sendRsToC)
// XXXTransfer should operate on different cRq/pRq from other rules
// sendRsToC is ordered after pipelineResp to save 1 cycle in I$ latency
// sendRqToP and sendRsToP_cRq are read only
// sendRsToP_pRq is operating on different pRq from pipelineResp_pRq (since we use CF index FIFO)
// ---- conclusion ----
// rules/methods are operating on different MSHR entries, except pipelineResp v.s. sendRsToC
// we have 5 ports from cRq MSHR
// 1. cRqTransfer
// 2. sendRsToC
// 3. sendRsToP_cRq
// 4. sendRqToP
// 5. pipelineResp
// we have 3 ports from pRq MSHR
// 1. pRqTransfer
// 2. sendRsToP_pRq
// 3. pipelineResp
// safe version: use EHR ports
// sendRsToP_cRq/sendRqToP/pipelineResp < sendRsToC < cRqTransfer
// pipelineResp < sendRsToP_pRq < pRqTransfer
// (note there is no bypass path from pipelineResp to sendRsToP_pRq since sendRsToP_pRq only reads pRq)
// unsafe version: all reads read the original reg value, except sendRsToC, which should bypass from pipelineResp
// all writes are cononicalized. NOTE: writes of sendRsToC should be after pipelineResp
// we maintain the logical ordering in safe version

View File

@@ -0,0 +1,466 @@
// Copyright (c) 2019 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Assert::*;
import ConfigReg::*;
import Vector::*;
import FShow::*;
import Types::*;
import Fifo::*;
import CCTypes::*;
import CCPipe::*;
import RWBramCore::*;
import RandomReplace::*;
export SelfInvIPipeRqIn(..);
export SelfInvIPipePRsIn(..);
export SelfInvIPipeIn(..);
export SelfInvICmd(..);
export SelfInvIPipe(..);
export mkSelfInvIPipe;
// type param ordering: bank < way < index < tag < cRq
// In I cache, only cRq can occupy cache line, there is no pRq or explicity
// replacement, so cache owner type is simply Maybe#(cRqIdxT)
// input types
typedef struct {
Addr addr;
rqIdxT mshrIdx;
} SelfInvIPipeRqIn#(type rqIdxT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState;
Maybe#(Line) data;
wayT way;
} SelfInvIPipePRsIn#(type wayT) deriving(Bits, Eq, FShow);
typedef union tagged {
SelfInvIPipeRqIn#(cRqIdxT) CRq;
SelfInvIPipePRsIn#(wayT) PRs;
} SelfInvIPipeIn#(
type wayT,
type cRqIdxT
) deriving (Bits, Eq, FShow);
// output cmd to the processing rule in I$
typedef union tagged {
cRqIdxT ICRq;
void IPRs;
} SelfInvICmd#(
type cRqIdxT
) deriving (Bits, Eq, FShow);
interface SelfInvIPipe#(
numeric type lgBankNum,
numeric type wayNum,
type indexT,
type tagT,
type cRqIdxT
);
method Action send(SelfInvIPipeIn#(Bit#(TLog#(wayNum)), cRqIdxT) r);
method PipeOut#(
Bit#(TLog#(wayNum)),
tagT, Msi, void, // no dir
Maybe#(cRqIdxT), void, RandRepInfo, // no other
Line, SelfInvICmd#(cRqIdxT)
) first;
method Action deqWrite(
Maybe#(cRqIdxT) swapRq,
RamData#(tagT, Msi, void, Maybe#(cRqIdxT), void, Line) wrRam, // always write BRAM
Bool updateRep
);
// drop stale clean cache lines
method Action reconcile;
method Bool reconcile_done;
endinterface
// real cmd used in pipeline
typedef struct {
Addr addr;
wayT way;
} SelfInvIPipePRsCmd#(type wayT) deriving(Bits, Eq, FShow);
typedef union tagged {
SelfInvIPipeRqIn#(cRqIdxT) CRq;
SelfInvIPipePRsCmd#(wayT) PRs;
} SelfInvIPipeCmd#(
type wayT,
type cRqIdxT
) deriving (Bits, Eq, FShow);
// cache state array with reconcile port
// ram sub interface has same scheduling as RWBramCore:
// - wrReq CF rdReq (read cannot see write)
// - deqRdResp < rdReq
interface CacheStateArray#(type indexT);
interface RWBramCore#(indexT, Msi) ram;
method Action reconcile;
endinterface
module mkCacheStateArray(CacheStateArray#(indexT)) provisos(
Alias#(indexT, Bit#(indexSz)),
NumAlias#(size, TExp#(indexSz))
);
staticAssert(pack(Msi'(I)) == 2'd0, "I = 0");
staticAssert(pack(Msi'(S)) == 2'd1, "S = 0");
Vector#(size, Reg#(Bit#(1))) state <- replicateM(mkConfigReg(0));
Fifo#(1, Msi) rdRespQ <- mkPipelineFifo;
interface RWBramCore ram;
method Action wrReq(indexT idx, Msi s);
doAssert(s == I || s == S, "only I or S");
state[idx] <= truncate(pack(s));
endmethod
method Action rdReq(indexT idx);
rdRespQ.enq(unpack(zeroExtend(state[idx])));
endmethod
method rdResp = rdRespQ.first;
method rdRespValid = rdRespQ.notEmpty;
method deqRdResp = rdRespQ.deq;
endinterface
method Action reconcile;
function Action resetS(Reg#(Bit#(1)) s);
action
s <= 0;
endaction
endfunction
joinActions(map(resetS, state));
endmethod
endmodule
// Put cache state array and tag+owner ram together to form an array
typedef struct {
tagT tag;
ownerT owner;
otherT other;
} TagOwnerOther#(type tagT, type ownerT, type otherT) deriving(Bits, Eq, FShow);
interface CacheInfoArray#(type indexT, type tagT, type ownerT, type otherT);
interface RWBramCore#(indexT, CacheInfo#(tagT, Msi, void, ownerT, otherT)) ram;
method Action reconcile;
endinterface
module mkCacheInfoArray(CacheInfoArray#(indexT, tagT, ownerT, otherT)) provisos(
Alias#(indexT, Bit#(indexSz)),
NumAlias#(size, TExp#(indexSz)),
Alias#(tagOwnerOtherT, TagOwnerOther#(tagT, ownerT, otherT)),
Alias#(infoT, CacheInfo#(tagT, Msi, void, ownerT, otherT)),
Bits#(tagT, _tagSz),
Bits#(ownerT, _ownerSz),
Bits#(otherT, _otherSz)
);
RWBramCore#(indexT, tagOwnerOtherT) tagOwnerOtherRam <- mkRWBramCore;
CacheStateArray#(indexT) csArray <- mkCacheStateArray;
interface RWBramCore ram;
method Action wrReq(indexT idx, infoT x);
tagOwnerOtherRam.wrReq(idx, TagOwnerOther {
tag: x.tag,
owner: x.owner,
other: x.other
});
csArray.ram.wrReq(idx, x.cs);
endmethod
method Action rdReq(indexT idx);
tagOwnerOtherRam.rdReq(idx);
csArray.ram.rdReq(idx);
endmethod
method infoT rdResp;
tagOwnerOtherT tagOwnerOther = tagOwnerOtherRam.rdResp;
Msi cs = csArray.ram.rdResp;
return CacheInfo {
tag: tagOwnerOther.tag,
cs: cs,
dir: ?,
owner: tagOwnerOther.owner,
other: tagOwnerOther.other
};
endmethod
method Bool rdRespValid;
return tagOwnerOtherRam.rdRespValid && csArray.ram.rdRespValid;
endmethod
method Action deqRdResp;
tagOwnerOtherRam.deqRdResp;
csArray.ram.deqRdResp;
endmethod
endinterface
method reconcile = csArray.reconcile;
endmodule
module mkSelfInvIPipe(
SelfInvIPipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT)
) provisos(
Alias#(wayT, Bit#(TLog#(wayNum))),
Alias#(dirT, void), // no directory
Alias#(ownerT, Maybe#(cRqIdxT)),
Alias#(otherT, void),
Alias#(repT, RandRepInfo),
Alias#(pipeInT, SelfInvIPipeIn#(wayT, cRqIdxT)),
Alias#(pipeCmdT, SelfInvIPipeCmd#(wayT, cRqIdxT)),
Alias#(iCmdT, SelfInvICmd#(cRqIdxT)),
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, iCmdT)), // output type
Alias#(infoT, CacheInfo#(tagT, Msi, dirT, ownerT, otherT)),
Alias#(ramDataT, RamData#(tagT, Msi, dirT, ownerT, otherT, Line)),
Alias#(respStateT, RespState#(Msi)),
Alias#(tagMatchResT, TagMatchResult#(wayT)),
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), indexSz))),
// requirement
Alias#(indexT, Bit#(indexSz)),
Alias#(tagT, Bit#(tagSz)),
Alias#(cRqIdxT, Bit#(cRqIdxSz)),
Add#(indexSz, a__, AddrSz),
Add#(tagSz, b__, AddrSz)
);
// info RAM
Vector#(wayNum, CacheInfoArray#(indexT, tagT, ownerT, otherT)) infoArray <- replicateM(mkCacheInfoArray);
function RWBramCore#(indexT, infoT) getInfoRam(Integer i) = infoArray[i].ram;
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam = map(getInfoRam, genVector);
// rep RAM (dummy)
RWBramCore#(indexT, repT) repRam <- mkRandRepRam;
// data RAM
RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore;
// initialize RAM
Reg#(Bool) initDone <- mkReg(False);
Reg#(indexT) initIndex <- mkReg(0);
rule doInit(!initDone);
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
infoRam[i].wrReq(initIndex, CacheInfo {
tag: 0,
cs: I,
dir: ?,
owner: Invalid,
other: ?
});
end
repRam.wrReq(initIndex, randRepInitInfo); // useless for random replace
initIndex <= initIndex + 1;
if(initIndex == maxBound) begin
initDone <= True;
end
endrule
// random replacement
RandomReplace#(wayNum) randRep <- mkRandomReplace;
// functions
function Addr getAddrFromCmd(pipeCmdT cmd);
return (case(cmd) matches
tagged CRq .r: r.addr;
tagged PRs .r: r.addr;
default: ?;
endcase);
endfunction
function indexT getIndex(pipeCmdT cmd);
Addr a = getAddrFromCmd(cmd);
return truncate(a >> (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
endfunction
function ActionValue#(tagMatchResT) tagMatch(
pipeCmdT cmd,
Vector#(wayNum, tagT) tagVec,
Vector#(wayNum, Msi) csVec,
Vector#(wayNum, ownerT) ownerVec,
repT repInfo
);
return actionvalue
function tagT getTag(Addr a) = truncateLSB(a);
$display("%t L1 %m tagMatch: ", $time,
fshow(cmd), " ; ",
fshow(getTag(getAddrFromCmd(cmd))),
fshow(tagVec), " ; ",
fshow(csVec), " ; ",
fshow(ownerVec), " ; "
);
if(cmd matches tagged PRs .rs) begin
// PRs directly read from cmd
return TagMatchResult {
way: rs.way,
pRqMiss: False
};
end
else begin
doAssert(cmd matches tagged CRq .rq ? True : False, "must be cRq");
// CRq: need tag matching
Addr addr = getAddrFromCmd(cmd);
tagT tag = getTag(addr);
// find hit way (nothing is being replaced)
function Bool isMatch(Tuple2#(Msi, tagT) csTag);
match {.cs, .t} = csTag;
return cs > I && t == tag;
endfunction
Maybe#(wayT) hitWay = searchIndex(isMatch, zip(csVec, tagVec));
if(hitWay matches tagged Valid .w) begin
return TagMatchResult {
way: w,
pRqMiss: False
};
end
else begin
// find a unlocked way to replace for cRq
Vector#(wayNum, Bool) unlocked = ?;
Vector#(wayNum, Bool) invalid = ?;
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
invalid[i] = csVec[i] == I;
unlocked[i] = !isValid(ownerVec[i]);
end
Maybe#(wayT) repWay = randRep.getReplaceWay(unlocked, invalid);
// sanity check: repWay must be valid
if(!isValid(repWay)) begin
$fwrite(stderr, "[L1Pipe] ERROR: ", fshow(cmd), " cannot find way to replace\n");
$finish;
end
return TagMatchResult {
way: fromMaybe(?, repWay),
pRqMiss: False
};
end
end
endactionvalue;
endfunction
function ActionValue#(UpdateByUpCs#(Msi)) updateByUpCs(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs
);
actionvalue
doAssert(toState == S && oldCs == I, "upgrade cs I->S");
doAssert(dataV, "self inv L1 always needs data resp");
return UpdateByUpCs {cs: toState};
endactionvalue
endfunction
function ActionValue#(UpdateByDownDir#(Msi, dirT)) updateByDownDir(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs, dirT oldDir
);
actionvalue
doAssert(False, "L1 should not have cRs");
return UpdateByDownDir {cs: oldCs, dir: oldDir};
endactionvalue
endfunction
function ActionValue#(repT) updateRepInfo(repT r, wayT w);
actionvalue
return ?; // random replace does not have bookkeeping
endactionvalue
endfunction
CCPipe#(
wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT
) pipe <- mkCCPipe(
regToReadOnly(initDone), getIndex, tagMatch,
updateByUpCs, updateByDownDir, updateRepInfo,
infoRam, repRam, dataRam
);
// reconcile: wait until pipeline empty and drop all S states. Stall
// pipeline enq while we are waiting. Make the reconcile rule conflict with
// pipeline deq to remove any possible race (the guard of reconcile rule
// actually should have done the job).
// Since send method will not fire when needReconcile, we can use a wire to
// catch pipeline empty signal to avoid scheduling issue
Reg#(Bool) needReconcile <- mkReg(False);
RWire#(void) conflict_reconcile_deq <- mkRWire;
PulseWire pipeEmpty <- mkPulseWire;
(* fire_when_enabled, no_implicit_conditions *)
rule setPipeEmpty(pipe.emptyForFlush);
pipeEmpty.send;
endrule
rule doReconcile(initDone && needReconcile && pipeEmpty);
function Action flush(CacheInfoArray#(indexT, tagT, ownerT, otherT) ifc);
action
ifc.reconcile;
endaction
endfunction
joinActions(map(flush, infoArray));
// reconcile is done
needReconcile <= False;
// conflict with deq
conflict_reconcile_deq.wset(?);
$display("%t I %m doReconcile", $time);
endrule
// stall enq for reconcile
method Action send(pipeInT req) if(!needReconcile);
case(req) matches
tagged CRq .rq: begin
pipe.enq(CRq (rq), Invalid, Invalid);
end
tagged PRs .rs: begin
pipe.enq(PRs (SelfInvIPipePRsCmd {
addr: rs.addr,
way: rs.way
}), rs.data, UpCs (rs.toState));
end
endcase
endmethod
// need to adapt pipeline output to real output format
method pipeOutT first;
let pout = pipe.first;
return PipeOut {
cmd: (case(pout.cmd) matches
tagged CRq .rq: ICRq (rq.mshrIdx);
tagged PRs .rs: IPRs;
default: ?;
endcase),
way: pout.way,
pRqMiss: pout.pRqMiss,
ram: pout.ram,
repInfo: pout.repInfo
};
endmethod
method Action deqWrite(Maybe#(cRqIdxT) swapRq, ramDataT wrRam, Bool updateRep);
// get new cmd
Maybe#(pipeCmdT) newCmd = Invalid;
if(swapRq matches tagged Valid .idx) begin // swap in cRq
Addr addr = getAddrFromCmd(pipe.first.cmd); // inherit addr
newCmd = Valid (CRq (SelfInvIPipeRqIn {addr: addr, mshrIdx: idx}));
end
// call pipe
pipe.deqWrite(newCmd, wrRam, updateRep);
// conflict with reconcile
conflict_reconcile_deq.wset(?);
endmethod
method Action reconcile if(!needReconcile);
needReconcile <= True;
endmethod
method Bool reconcile_done;
return !needReconcile;
endmethod
endmodule

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,492 @@
// Copyright (c) 2019 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Assert::*;
import ConfigReg::*;
import Vector::*;
import FShow::*;
import Fifo::*;
import Types::*;
import CCTypes::*;
import CCPipe::*;
import RWBramCore::*;
import RandomReplace::*;
export SelfInvL1PipeRqIn(..);
export SelfInvL1PipePRsIn(..);
export SelfInvL1PipeIn(..);
export SelfInvL1Cmd(..);
export SelfInvL1Hits(..);
export SelfInvL1Pipe(..);
export mkSelfInvL1Pipe;
// type param ordering: bank < way < index < tag < cRq < pRq
// in L1 cache, only cRq can occupy cache line (pRq handled immediately)
// replacement is always done immediately (never have replacing line)
// so cache owner type is simply Maybe#(cRqIdxT)
// input types
typedef struct {
Addr addr;
rqIdxT mshrIdx;
} SelfInvL1PipeRqIn#(type rqIdxT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState;
Maybe#(Line) data;
wayT way;
} SelfInvL1PipePRsIn#(type wayT) deriving(Bits, Eq, FShow);
typedef union tagged {
SelfInvL1PipeRqIn#(cRqIdxT) CRq;
SelfInvL1PipeRqIn#(pRqIdxT) PRq;
SelfInvL1PipePRsIn#(wayT) PRs;
} SelfInvL1PipeIn#(
type wayT,
type cRqIdxT,
type pRqIdxT
) deriving (Bits, Eq, FShow);
// output cmd to the processing rule in L1$
typedef union tagged {
cRqIdxT L1CRq;
pRqIdxT L1PRq;
void L1PRs;
} SelfInvL1Cmd#(
type cRqIdxT,
type pRqIdxT
) deriving (Bits, Eq, FShow);
// The "other" field in CacheInfo tracks the number of hits on the cache line
// before self inv
typedef struct {
Bit#(TLog#(maxHitNum)) hits;
} SelfInvL1Hits#(numeric type maxHitNum) deriving(Bits, Eq, FShow);
interface SelfInvL1Pipe#(
numeric type lgBankNum,
numeric type wayNum,
numeric type maxHitNum,
type indexT,
type tagT,
type cRqIdxT,
type pRqIdxT
);
method Action send(SelfInvL1PipeIn#(Bit#(TLog#(wayNum)), cRqIdxT, pRqIdxT) r);
method PipeOut#(
Bit#(TLog#(wayNum)),
tagT, Msi, void, // no dir
Maybe#(cRqIdxT), SelfInvL1Hits#(maxHitNum), RandRepInfo,
Line, SelfInvL1Cmd#(cRqIdxT, pRqIdxT)
) first;
method Action deqWrite(
Maybe#(cRqIdxT) swapRq,
RamData#(tagT, Msi, void, Maybe#(cRqIdxT), SelfInvL1Hits#(maxHitNum), Line) wrRam, // always write BRAM
Bool updateRep
);
// drop stale clean cache lines
method Action reconcile;
method Bool reconcile_done;
endinterface
// real cmd used in pipeline
typedef struct {
Addr addr;
wayT way;
} SelfInvL1PipePRsCmd#(type wayT) deriving(Bits, Eq, FShow);
typedef union tagged {
SelfInvL1PipeRqIn#(cRqIdxT) CRq;
SelfInvL1PipeRqIn#(pRqIdxT) PRq;
SelfInvL1PipePRsCmd#(wayT) PRs;
} SelfInvL1PipeCmd#(
type wayT,
type cRqIdxT,
type pRqIdxT
) deriving (Bits, Eq, FShow);
// cache state array with reconcile port
// ram sub interface has same scheduling as RWBramCore:
// - wrReq CF rdReq (read cannot see write)
// - deqRdResp < rdReq
interface CacheStateArray#(type indexT);
interface RWBramCore#(indexT, Msi) ram;
method Action reconcile;
endinterface
module mkCacheStateArray(CacheStateArray#(indexT)) provisos(
Alias#(indexT, Bit#(indexSz)),
NumAlias#(size, TExp#(indexSz))
);
Vector#(size, Reg#(Msi)) state <- replicateM(mkConfigReg(I));
Fifo#(1, Msi) rdRespQ <- mkPipelineFifo;
interface RWBramCore ram;
method Action wrReq(indexT idx, Msi s);
state[idx] <= s;
endmethod
method Action rdReq(indexT idx);
rdRespQ.enq(state[idx]);
endmethod
method rdResp = rdRespQ.first;
method rdRespValid = rdRespQ.notEmpty;
method deqRdResp = rdRespQ.deq;
endinterface
method Action reconcile;
function Action resetS(Reg#(Msi) s);
action
if(s == S) begin
s <= I;
end
endaction
endfunction
joinActions(map(resetS, state));
endmethod
endmodule
// Put cache state array and tag+owner ram together to form an array
typedef struct {
tagT tag;
ownerT owner;
otherT other;
} TagOwnerOther#(type tagT, type ownerT, type otherT) deriving(Bits, Eq, FShow);
interface CacheInfoArray#(type indexT, type tagT, type ownerT, type otherT);
interface RWBramCore#(indexT, CacheInfo#(tagT, Msi, void, ownerT, otherT)) ram;
method Action reconcile;
endinterface
module mkCacheInfoArray(CacheInfoArray#(indexT, tagT, ownerT, otherT)) provisos(
Alias#(indexT, Bit#(indexSz)),
NumAlias#(size, TExp#(indexSz)),
Alias#(tagOwnerOtherT, TagOwnerOther#(tagT, ownerT, otherT)),
Alias#(infoT, CacheInfo#(tagT, Msi, void, ownerT, otherT)),
Bits#(tagT, _tagSz),
Bits#(ownerT, _ownerSz),
Bits#(otherT, _otherSz)
);
RWBramCore#(indexT, tagOwnerOtherT) tagOwnerOtherRam <- mkRWBramCore;
CacheStateArray#(indexT) csArray <- mkCacheStateArray;
interface RWBramCore ram;
method Action wrReq(indexT idx, infoT x);
tagOwnerOtherRam.wrReq(idx, TagOwnerOther {
tag: x.tag,
owner: x.owner,
other: x.other
});
csArray.ram.wrReq(idx, x.cs);
endmethod
method Action rdReq(indexT idx);
tagOwnerOtherRam.rdReq(idx);
csArray.ram.rdReq(idx);
endmethod
method infoT rdResp;
tagOwnerOtherT tagOwnerOther = tagOwnerOtherRam.rdResp;
Msi cs = csArray.ram.rdResp;
return CacheInfo {
tag: tagOwnerOther.tag,
cs: cs,
dir: ?,
owner: tagOwnerOther.owner,
other: tagOwnerOther.other
};
endmethod
method Bool rdRespValid;
return tagOwnerOtherRam.rdRespValid && csArray.ram.rdRespValid;
endmethod
method Action deqRdResp;
tagOwnerOtherRam.deqRdResp;
csArray.ram.deqRdResp;
endmethod
endinterface
method reconcile = csArray.reconcile;
endmodule
module mkSelfInvL1Pipe(
SelfInvL1Pipe#(lgBankNum, wayNum, maxHitNum, indexT, tagT, cRqIdxT, pRqIdxT)
) provisos(
Alias#(wayT, Bit#(TLog#(wayNum))),
Alias#(dirT, void), // no directory
Alias#(ownerT, Maybe#(cRqIdxT)),
Alias#(otherT, SelfInvL1Hits#(maxHitNum)),
Alias#(repT, RandRepInfo),
Alias#(pipeInT, SelfInvL1PipeIn#(wayT, cRqIdxT, pRqIdxT)),
Alias#(pipeCmdT, SelfInvL1PipeCmd#(wayT, cRqIdxT, pRqIdxT)),
Alias#(l1CmdT, SelfInvL1Cmd#(cRqIdxT, pRqIdxT)),
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, l1CmdT)), // output type
Alias#(infoT, CacheInfo#(tagT, Msi, dirT, ownerT, otherT)),
Alias#(ramDataT, RamData#(tagT, Msi, dirT, ownerT, otherT, Line)),
Alias#(respStateT, RespState#(Msi)),
Alias#(tagMatchResT, TagMatchResult#(wayT)),
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), indexSz))),
// requirement
Alias#(indexT, Bit#(indexSz)),
Alias#(tagT, Bit#(tagSz)),
Alias#(cRqIdxT, Bit#(cRqIdxSz)),
Alias#(pRqIdxT, Bit#(pRqIdxSz)),
Add#(indexSz, a__, AddrSz),
Add#(tagSz, b__, AddrSz)
);
// info RAM
Vector#(wayNum, CacheInfoArray#(indexT, tagT, ownerT, otherT)) infoArray <- replicateM(mkCacheInfoArray);
function RWBramCore#(indexT, infoT) getInfoRam(Integer i) = infoArray[i].ram;
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam = map(getInfoRam, genVector);
// rep RAM (dummy)
RWBramCore#(indexT, repT) repRam <- mkRandRepRam;
// data RAM
RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore;
// initialize RAM
Reg#(Bool) initDone <- mkReg(False);
Reg#(indexT) initIndex <- mkReg(0);
rule doInit(!initDone);
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
infoRam[i].wrReq(initIndex, CacheInfo {
tag: 0,
cs: I,
dir: ?,
owner: Invalid,
other: SelfInvL1Hits {hits: 0}
});
end
repRam.wrReq(initIndex, randRepInitInfo); // useless for random replace
initIndex <= initIndex + 1;
if(initIndex == maxBound) begin
initDone <= True;
end
endrule
// random replacement
RandomReplace#(wayNum) randRep <- mkRandomReplace;
// functions
function Addr getAddrFromCmd(pipeCmdT cmd);
return (case(cmd) matches
tagged CRq .r: r.addr;
tagged PRq .r: r.addr;
tagged PRs .r: r.addr;
default: ?;
endcase);
endfunction
function indexT getIndex(pipeCmdT cmd);
Addr a = getAddrFromCmd(cmd);
return truncate(a >> (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
endfunction
function ActionValue#(tagMatchResT) tagMatch(
pipeCmdT cmd,
Vector#(wayNum, tagT) tagVec,
Vector#(wayNum, Msi) csVec,
Vector#(wayNum, ownerT) ownerVec,
repT repInfo
);
return actionvalue
function tagT getTag(Addr a) = truncateLSB(a);
$display("%t L1 %m tagMatch: ", $time,
fshow(cmd), " ; ",
fshow(getTag(getAddrFromCmd(cmd))),
fshow(tagVec), " ; ",
fshow(csVec), " ; ",
fshow(ownerVec), " ; "
);
if(cmd matches tagged PRs .rs) begin
// PRs directly read from cmd
return TagMatchResult {
way: rs.way,
pRqMiss: False
};
end
else begin
// CRq/PRq: need tag matching
Addr addr = getAddrFromCmd(cmd);
tagT tag = getTag(addr);
// find hit way (nothing is being replaced)
function Bool isMatch(Tuple2#(Msi, tagT) csTag);
match {.cs, .t} = csTag;
return cs > I && t == tag;
endfunction
Maybe#(wayT) hitWay = searchIndex(isMatch, zip(csVec, tagVec));
if(hitWay matches tagged Valid .w) begin
return TagMatchResult {
way: w,
pRqMiss: False
};
end
else if(cmd matches tagged PRq .rq) begin
// pRq miss
return TagMatchResult {
way: 0, // default to 0
pRqMiss: True
};
end
else begin
// find a unlocked way to replace for cRq
Vector#(wayNum, Bool) unlocked = ?;
Vector#(wayNum, Bool) invalid = ?;
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
invalid[i] = csVec[i] == I;
unlocked[i] = !isValid(ownerVec[i]);
end
Maybe#(wayT) repWay = randRep.getReplaceWay(unlocked, invalid);
// sanity check: repWay must be valid
if(!isValid(repWay)) begin
$fwrite(stderr, "[L1Pipe] ERROR: ", fshow(cmd), " cannot find way to replace\n");
$finish;
end
return TagMatchResult {
way: fromMaybe(?, repWay),
pRqMiss: False
};
end
end
endactionvalue;
endfunction
function ActionValue#(UpdateByUpCs#(Msi)) updateByUpCs(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs
);
actionvalue
doAssert(toState > oldCs, "should truly upgrade cs");
doAssert(dataV, "self inv L1 always needs data resp");
return UpdateByUpCs {cs: toState};
endactionvalue
endfunction
function ActionValue#(UpdateByDownDir#(Msi, dirT)) updateByDownDir(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs, dirT oldDir
);
actionvalue
doAssert(False, "L1 should not have cRs");
return UpdateByDownDir {cs: oldCs, dir: oldDir};
endactionvalue
endfunction
function ActionValue#(repT) updateRepInfo(repT r, wayT w);
actionvalue
return ?; // random replace does not have bookkeeping
endactionvalue
endfunction
CCPipe#(
wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT
) pipe <- mkCCPipe(
regToReadOnly(initDone), getIndex, tagMatch,
updateByUpCs, updateByDownDir, updateRepInfo,
infoRam, repRam, dataRam
);
// reconcile: wait until pipeline empty and drop all S states. Stall
// pipeline enq while we are waiting. Make the reconcile rule conflict with
// pipeline deq to remove any possible race (the guard of reconcile rule
// actually should have done the job).
// Since send method will not fire when needReconcile, we can use a wire to
// catch pipeline empty signal to avoid scheduling issue
Reg#(Bool) needReconcile <- mkReg(False);
RWire#(void) conflict_reconcile_deq <- mkRWire;
PulseWire pipeEmpty <- mkPulseWire;
(* fire_when_enabled, no_implicit_conditions *)
rule setPipeEmpty(pipe.emptyForFlush);
pipeEmpty.send;
endrule
rule doReconcile(initDone && needReconcile && pipeEmpty);
function Action flush(CacheInfoArray#(indexT, tagT, ownerT, otherT) ifc);
action
ifc.reconcile;
endaction
endfunction
joinActions(map(flush, infoArray));
// reconcile is done
needReconcile <= False;
// conflict with deq
conflict_reconcile_deq.wset(?);
$display("%t L1 %m doReconcile", $time);
endrule
// stall enq for reconcile
method Action send(pipeInT req) if(!needReconcile);
case(req) matches
tagged CRq .rq: begin
pipe.enq(CRq (rq), Invalid, Invalid);
end
tagged PRq .rq: begin
pipe.enq(PRq (rq), Invalid, Invalid);
end
tagged PRs .rs: begin
pipe.enq(PRs (SelfInvL1PipePRsCmd {
addr: rs.addr,
way: rs.way
}), rs.data, UpCs (rs.toState));
end
endcase
endmethod
// need to adapt pipeline output to real output format
method pipeOutT first;
let pout = pipe.first;
return PipeOut {
cmd: (case(pout.cmd) matches
tagged CRq .rq: L1CRq (rq.mshrIdx);
tagged PRq .rq: L1PRq (rq.mshrIdx);
tagged PRs .rs: L1PRs;
default: ?;
endcase),
way: pout.way,
pRqMiss: pout.pRqMiss,
ram: pout.ram,
repInfo: pout.repInfo
};
endmethod
method Action deqWrite(Maybe#(cRqIdxT) swapRq, ramDataT wrRam, Bool updateRep);
// get new cmd
Maybe#(pipeCmdT) newCmd = Invalid;
if(swapRq matches tagged Valid .idx) begin // swap in cRq
Addr addr = getAddrFromCmd(pipe.first.cmd); // inherit addr
newCmd = Valid (CRq (SelfInvL1PipeRqIn {addr: addr, mshrIdx: idx}));
end
// call pipe
pipe.deqWrite(newCmd, wrRam, updateRep);
// conflict with reconcile
conflict_reconcile_deq.wset(?);
endmethod
method Action reconcile if(!needReconcile);
needReconcile <= True;
endmethod
method Bool reconcile_done;
return !needReconcile;
endmethod
endmodule

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,366 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import FShow::*;
import Types::*;
import CCTypes::*;
import CCPipe::*;
import RWBramCore::*;
import RandomReplace::*;
// type param ordering: bank < child < way < index < tag < cRq
// input types
typedef struct {
Addr addr;
cRqIdxT mshrIdx;
} SelfInvLLPipeCRqIn#(type cRqIdxT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
Msi toState; // come from req in MSHR (E or M)
Line data; // come from memory must be valid
wayT way; // come from MSHR
} SelfInvLLPipeMRsIn#(type wayT) deriving(Bits, Eq, FShow);
typedef union tagged {
SelfInvLLPipeCRqIn#(cRqIdxT) CRq;
CRsMsg#(childT) CRs;
SelfInvLLPipeMRsIn#(wayT) MRs;
} SelfInvLLPipeIn#(
type childT,
type wayT,
type cRqIdxT
) deriving (Bits, Eq, FShow);
// output cmd to the processing rule in LLC
typedef union tagged {
cRqIdxT LLCRq; // mshr idx of the cRq
childT LLCRs; // which child is downgrading
void LLMRs;
} SelfInvLLCmd#(type childT, type cRqIdxT) deriving (Bits, Eq, FShow);
// Track only exclusive child in directory
typedef struct {
childT exChild;
Msi state; // I/E/M, we don't track S
} SelfInvDir#(type childT) deriving(Bits, Eq, FShow);
interface SelfInvLLPipe#(
numeric type lgBankNum,
numeric type childNum,
numeric type wayNum,
type indexT,
type tagT,
type cRqIdxT
);
method Action send(SelfInvLLPipeIn#(Bit#(TLog#(childNum)), Bit#(TLog#(wayNum)), cRqIdxT) r);
method Bool notEmpty;
method PipeOut#(
Bit#(TLog#(wayNum)),
tagT, Msi, SelfInvDir#(Bit#(TLog#(childNum))),
Maybe#(CRqOwner#(cRqIdxT)), void, RandRepInfo, // no other
Line, SelfInvLLCmd#(Bit#(TLog#(childNum)), cRqIdxT)
) first;
method PipeOut#(
Bit#(TLog#(wayNum)),
tagT, Msi, SelfInvDir#(Bit#(TLog#(childNum))),
Maybe#(CRqOwner#(cRqIdxT)), void, RandRepInfo, // no other
Line, SelfInvLLCmd#(Bit#(TLog#(childNum)), cRqIdxT)
) unguard_first;
method Action deqWrite(
Maybe#(cRqIdxT) swapRq,
RamData#(tagT, Msi, SelfInvDir#(Bit#(TLog#(childNum))), Maybe#(CRqOwner#(cRqIdxT)), void, Line) wrRam, // always write BRAM
Bool updateRep
);
endinterface
// real cmd used in pipeline
typedef struct {
Addr addr;
childT child;
} SelfInvLLPipeCRsCmd#(type childT) deriving(Bits, Eq, FShow);
typedef struct {
Addr addr;
wayT way;
} SelfInvLLPipeMRsCmd#(type wayT) deriving(Bits, Eq, FShow);
typedef union tagged {
SelfInvLLPipeCRqIn#(cRqIdxT) CRq;
SelfInvLLPipeCRsCmd#(childT) CRs;
SelfInvLLPipeMRsCmd#(wayT) MRs;
} SelfInvLLPipeCmd#(
type childT,
type wayT,
type cRqIdxT
) deriving (Bits, Eq, FShow);
module mkSelfInvLLPipe(
SelfInvLLPipe#(lgBankNum, childNum, wayNum, indexT, tagT, cRqIdxT)
) provisos(
Alias#(childT, Bit#(TLog#(childNum))),
Alias#(wayT, Bit#(TLog#(wayNum))),
Alias#(dirT, SelfInvDir#(childT)),
Alias#(ownerT, Maybe#(CRqOwner#(cRqIdxT))),
Alias#(otherT, void), // no other cache info
Alias#(repT, RandRepInfo), // use random replace
Alias#(pipeInT, SelfInvLLPipeIn#(childT, wayT, cRqIdxT)),
Alias#(pipeCmdT, SelfInvLLPipeCmd#(childT, wayT, cRqIdxT)),
Alias#(llCmdT, SelfInvLLCmd#(childT, cRqIdxT)),
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, llCmdT)), // output type
Alias#(infoT, CacheInfo#(tagT, Msi, dirT, ownerT, otherT)),
Alias#(ramDataT, RamData#(tagT, Msi, dirT, ownerT, otherT, Line)),
Alias#(respStateT, RespState#(Msi)),
Alias#(tagMatchResT, TagMatchResult#(wayT)),
Alias#(updateByUpCsT, UpdateByUpCs#(Msi)),
Alias#(updateByDownDirT, UpdateByDownDir#(Msi, dirT)),
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), indexSz))),
// requirement
Alias#(indexT, Bit#(indexSz)),
Alias#(tagT, Bit#(tagSz)),
Alias#(cRqIdxT, Bit#(_cRqIdxSz)),
Add#(indexSz, a__, AddrSz),
Add#(tagSz, b__, AddrSz)
);
// RAMs
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam <- replicateM(mkRWBramCore);
RWBramCore#(indexT, repT) repRam <- mkRandRepRam;
RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore;
// initialize RAM
Reg#(Bool) initDone <- mkReg(False);
Reg#(indexT) initIndex <- mkReg(0);
rule doInit(!initDone);
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
infoRam[i].wrReq(initIndex, CacheInfo {
tag: 0,
cs: I,
dir: SelfInvDir {exChild: ?, state: I},
owner: Invalid,
other: ?
});
end
repRam.wrReq(initIndex, randRepInitInfo); // useless for random replace
initIndex <= initIndex + 1;
if(initIndex == maxBound) begin
initDone <= True;
end
endrule
// random replacement
RandomReplace#(wayNum) randRep <- mkRandomReplace;
// functions
function Addr getAddrFromCmd(pipeCmdT cmd);
return (case(cmd) matches
tagged CRq .r: r.addr;
tagged CRs .r: r.addr;
tagged MRs .r: r.addr;
default: ?;
endcase);
endfunction
function indexT getIndex(pipeCmdT cmd);
Addr a = getAddrFromCmd(cmd);
return truncate(a >> (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
endfunction
function ActionValue#(tagMatchResT) tagMatch(
pipeCmdT cmd,
Vector#(wayNum, tagT) tagVec,
Vector#(wayNum, Msi) csVec,
Vector#(wayNum, ownerT) ownerVec,
repT repInfo
);
return actionvalue
function tagT getTag(Addr a) = truncateLSB(a);
$display("%t LL %m tagMatch: ", $time,
fshow(cmd), " ; ",
fshow(getTag(getAddrFromCmd(cmd))), " ; ",
fshow(tagVec), " ; ",
fshow(csVec), " ; ",
fshow(ownerVec)
);
if(cmd matches tagged MRs .rs) begin
// MRs directly read from cmd
return TagMatchResult {
way: rs.way,
pRqMiss: False
};
end
else begin
// CRq/CRs: need tag matching
Addr addr = getAddrFromCmd(cmd);
tagT tag = getTag(addr);
// find hit way (we do not check replacing bit in LLC)
// this makes <cRq a> blocked by other <cRq b> which is replacing addr a
function Bool isMatch(Tuple2#(Msi, tagT) csTag);
match {.cs, .t} = csTag;
return cs > I && t == tag;
endfunction
Maybe#(wayT) hitWay = searchIndex(isMatch, zip(csVec, tagVec));
if(hitWay matches tagged Valid .w) begin
return TagMatchResult {
way: w,
pRqMiss: False
};
end
else begin
// cRs must hit, so only cRq cannot enter here
doAssert(cmd matches tagged CRq ._rq ? True : False,
"only cRq can tag match miss"
);
// find a unlocked way to replace for cRq
Vector#(wayNum, Bool) unlocked = ?;
Vector#(wayNum, Bool) invalid = ?;
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
invalid[i] = csVec[i] == I;
unlocked[i] = !isValid(ownerVec[i]);
end
Maybe#(wayT) repWay = randRep.getReplaceWay(unlocked, invalid);
// sanity check: repWay must be valid
doAssert(isValid(repWay), "should always find a way to replace");
return TagMatchResult {
way: fromMaybe(?, repWay),
pRqMiss: False
};
end
end
endactionvalue;
endfunction
function ActionValue#(updateByUpCsT) updateByUpCs(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs
);
actionvalue
doAssert(toState > oldCs, "should truly upgrade cs");
doAssert((oldCs == I) && dataV, "LLC mRs always has data");
return UpdateByUpCs {cs: toState};
endactionvalue
endfunction
function ActionValue#(updateByDownDirT) updateByDownDir(
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs, dirT oldDir
);
actionvalue
// update dir
// The exclusive child is downgraded. Since we don't track S, just make
// dir invalid, child field is useless for I
dirT newDir = SelfInvDir {exChild: ?, state: I};
doAssert(oldDir.state >= E && toState <= S, "no more exclusive child");
doAssert(cmd matches tagged CRs .cRs &&& oldDir.exChild == cRs.child ? True : False,
"cRs child should match");
if(!dataV) begin
doAssert(oldDir.state < M, "cRs without data, dir must < M");
end
if(oldDir.state == M) begin
doAssert(dataV, "downgrade from M must have data");
doAssert(oldCs == M, "cs must be M");
end
// update cs
// XXX since child can upgrade from E to M silently, use data valid
// to determine if we need to upgrade to M. Note that the data
// valid field has not been overwritten by bypass in CCPipe.
Msi newCs = oldCs;
if(dataV) begin
doAssert(oldCs >= E, "cRs has data, cs must >= E");
newCs = M;
end
return UpdateByDownDir {cs: newCs, dir: newDir};
endactionvalue
endfunction
function ActionValue#(repT) updateRepInfo(repT r, wayT w);
actionvalue
return ?; // random replace does not have bookkeeping
endactionvalue
endfunction
CCPipe#(wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT) pipe <- mkCCPipe(
regToReadOnly(initDone), getIndex, tagMatch,
updateByUpCs, updateByDownDir, updateRepInfo,
infoRam, repRam, dataRam
);
// get first output from CCPipe output
function pipeOutT getFirst(PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT) pout);
return PipeOut {
cmd: (case(pout.cmd) matches
tagged CRq .rq: LLCRq (rq.mshrIdx);
tagged CRs .rs: LLCRs (rs.child);
tagged MRs .rs: LLMRs;
default: ?;
endcase),
way: pout.way,
pRqMiss: pout.pRqMiss,
ram: pout.ram,
repInfo: pout.repInfo
};
endfunction
method Action send(pipeInT req);
case(req) matches
tagged CRq .rq: begin
pipe.enq(CRq (rq), Invalid, Invalid);
end
tagged CRs .rs: begin
pipe.enq(CRs (SelfInvLLPipeCRsCmd {
addr: rs.addr,
child: rs.child
}), rs.data, DownDir (rs.toState));
end
tagged MRs .rs: begin
pipe.enq(MRs (SelfInvLLPipeMRsCmd {
addr: rs.addr,
way: rs.way
}), Valid (rs.data), UpCs (rs.toState));
end
endcase
endmethod
// need to adapt pipeline output to real output format
method pipeOutT first;
return getFirst(pipe.first); // guarded version
endmethod
method pipeOutT unguard_first;
return getFirst(pipe.unguard_first); // unguarded version
endmethod
method notEmpty = pipe.notEmpty;
method Action deqWrite(Maybe#(cRqIdxT) swapRq, ramDataT wrRam, Bool updateRep);
// get new cmd
Addr addr = getAddrFromCmd(pipe.first.cmd); // inherit addr
Maybe#(pipeCmdT) newCmd = Invalid;
if(swapRq matches tagged Valid .idx) begin
newCmd = Valid (CRq (SelfInvLLPipeCRqIn {addr: addr, mshrIdx: idx}));
end
// call pipe
pipe.deqWrite(newCmd, wrRam, updateRep);
endmethod
endmodule

View File

@@ -0,0 +1,178 @@
// Copyright (c) 2015 Quanta Research Cambridge, Inc.
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Clocks::*;
import Vector::*;
import FIFO::*;
import FIFOF::*;
import BRAMFIFO::*;
import CBus::*; // extendNP and truncateNP
`include "ConnectalProjectConfig.bsv"
import Arith::*;
import ConnectalClocks::*;
`ifdef SIMULATION
`ifdef BOARD_xsim
`define USE_XILINX_MACRO
`endif // xsim
`else // not SIMULATION
`ifdef XILINX
`define USE_XILINX_MACRO
`endif // XILINX
`endif // not SIMULATION
`ifdef USE_XILINX_MACRO
(* always_ready, always_enabled *)
interface X7FifoSyncMacro#(numeric type data_width);
method Bit#(1) empty();
method Bit#(1) full();
method Action din(Bit#(data_width) v);
method Action wren(Bit#(1) wren);
method Bit#(data_width) dout();
method Action rden(Bit#(1) rden);
method Bit#(9) wrcount();
method Bit#(9) rdcount();
endinterface
import "BVI" FIFO_DUALCLOCK_MACRO =
module vmkBramFifo#(String fifo_size, Clock wrClock, Reset wrReset, Clock rdClock, Reset rdReset)(X7FifoSyncMacro#(data_width));
`ifndef BSV_POSITIVE_RESET
let rdReset1 <- mkSyncReset(10, rdReset, wrClock);
let eitherReset <- mkResetEither(wrReset, rdReset1, clocked_by wrClock);
let positiveReset <- mkPositiveReset(10, eitherReset, wrClock);
// rst must be asserted for 5 read and write clock cycles
let fifoReset = positiveReset.positiveReset;
`else
let fifoReset = wrReset;
`endif
`ifdef XilinxUltrascale
parameter DEVICE = "ULTRASCALE";
`else
parameter DEVICE = "7SERIES";
`endif
parameter DATA_WIDTH = valueOf(data_width);
parameter FIFO_SIZE = fifo_size;
`ifndef SIMULATION
parameter FIRST_WORD_FALL_THROUGH = "TRUE"; // not supported by xsim
`else
parameter FIRST_WORD_FALL_THROUGH = True;
`endif
default_clock wrClock(WRCLK) = wrClock;
no_reset;
// RST is asynchronous
input_reset wrReset(RST) clocked_by(no_clock) = fifoReset;
input_clock rdClock(RDCLK) = rdClock;
method EMPTY empty() clocked_by (rdClock) reset_by (wrReset);
method FULL full() clocked_by (wrClock) reset_by (wrReset);
method din(DI) enable ((*inhigh*)EN_di) clocked_by (wrClock) reset_by (wrReset);
method wren(WREN) enable ((*inhigh*)EN_wren) clocked_by (wrClock) reset_by (wrReset);
method DO dout() clocked_by (rdClock) reset_by (wrReset);
method rden(RDEN) enable ((*inhigh*)EN_rden) clocked_by (rdClock) reset_by (wrReset);
// wrcount and rdcount ports are needed for xsim
method WRCOUNT wrcount() clocked_by (wrClock) reset_by (wrReset);
method RDCOUNT rdcount() clocked_by (rdClock) reset_by (wrReset);
schedule (empty, full, dout, din, wren, rden, wrcount, rdcount) CF (empty, full, dout, din, wren, rden, wrcount, rdcount);
endmodule
module mkDualClockBramFIFOF#(Clock srcClock, Reset srcReset, Clock dstClock, Reset dstReset)(FIFOF#(t))
provisos (Bits#(t,sizet),
Add#(1,a__,sizet));
String fifo_size = "18Kb";
Vector#(TDiv#(sizet,36),X7FifoSyncMacro#(36)) fifos <- replicateM(vmkBramFifo(fifo_size, srcClock, srcReset, dstClock, dstReset));
Wire#(Bit#(1)) rdenWire <- mkDWire(0, clocked_by dstClock, reset_by dstReset);
Wire#(Bit#(1)) wrenWire <- mkDWire(0, clocked_by srcClock, reset_by srcReset);
Vector#(TDiv#(sizet,36),Wire#(Bit#(36))) dinWires <- replicateM(mkDWire(0, clocked_by srcClock, reset_by srcReset));
for (Integer i = 0; i < valueOf(TDiv#(sizet,36)); i = i+1) begin
Reg#(Bit#(9)) rdcount <- mkReg(0, clocked_by dstClock, reset_by dstReset);
Reg#(Bit#(9)) wrcount <- mkReg(0, clocked_by srcClock, reset_by srcReset);
rule rdenRule;
fifos[i].rden(rdenWire);
endrule
rule wrenRule;
fifos[i].wren(wrenWire);
endrule
rule inputs;
fifos[i].din(dinWires[i]);
endrule
rule countrds;
rdcount <= fifos[i].rdcount();
endrule
rule countwrs;
wrcount <= fifos[i].wrcount();
endrule
end
function Bool fifoNotEmpty(X7FifoSyncMacro#(36) f); return f.empty == 0; endfunction
function Bool fifoNotFull(X7FifoSyncMacro#(36) f); return f.full == 0; endfunction
method t first() if (all(fifoNotEmpty, fifos));
function Bit#(36) fifoFirst(Integer i); return fifos[i].dout(); endfunction
Vector#(TDiv#(sizet,36), Bit#(36)) v = genWith(fifoFirst);
return unpack(truncateNP(pack(v)));
endmethod
method Action deq() if (all(fifoNotEmpty, fifos));
rdenWire <= 1;
endmethod
method notEmpty = all(fifoNotEmpty, fifos);
method Action enq(t v) if (all(fifoNotFull, fifos));
Vector#(TDiv#(sizet,36), Bit#(36)) vs = unpack(extendNP(pack(v)));
Vector#(TDiv#(sizet,36), Integer) indices = genVector();
function Action fifoEnq(Integer i); action dinWires[i] <= vs[i]; endaction endfunction
mapM_(fifoEnq, indices);
wrenWire <= 1;
endmethod
method notFull = all(fifoNotEmpty, fifos);
endmodule
module mkDualClockBramFIFO#(Clock srcClock, Reset srcReset, Clock dstClock, Reset dstReset)(FIFO#(t))
provisos (Bits#(t,sizet),
Add#(1,a__,sizet));
let syncFifo <- mkDualClockBramFIFOF(srcClock, srcReset, dstClock, dstReset);
method enq = syncFifo.enq;
method deq = syncFifo.deq;
method first = syncFifo.first;
endmodule
`else // compatibility mode
module mkDualClockBramFIFOF#(Clock srcClock, Reset srcReset, Clock dstClock, Reset dstReset)(FIFOF#(t))
provisos (Bits#(t,sizet),
Add#(1,a__,sizet));
let syncFifo <- mkSyncFIFO(512, srcClock, srcReset, dstClock);
method enq = syncFifo.enq;
method deq = syncFifo.deq;
method first = syncFifo.first;
method notFull = syncFifo.notFull;
method notEmpty = syncFifo.notEmpty;
endmodule
module mkDualClockBramFIFO#(Clock srcClock, Reset srcReset, Clock dstClock, Reset dstReset)(FIFO#(t))
provisos (Bits#(t,sizet),
Add#(1,a__,sizet));
let syncFifo <- mkSyncFIFO(512, srcClock, srcReset, dstClock);
method enq = syncFifo.enq;
method deq = syncFifo.deq;
method first = syncFifo.first;
endmodule
`endif

View File

@@ -0,0 +1,130 @@
// Copyright (c) 2014 Quanta Research Cambridge, Inc.
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Clocks::*;
`include "ConnectalProjectConfig.bsv"
`ifdef PcieClockPeriod
Real pcieClockPeriod = `PcieClockPeriod;
`endif
Real mainClockPeriod = `MainClockPeriod;
Real derivedClockPeriod =`DerivedClockPeriod;
(* always_ready, always_enabled *)
interface B2C;
interface Clock c;
interface Reset r;
method Action inputclock(Bit#(1) v);
method Action inputreset(Bit#(1) v);
endinterface
import "BVI" CONNECTNET2 =
module mkB2C(B2C);
default_clock no_clock;
default_reset no_reset;
output_clock c(OUT1);
output_reset r(OUT2);
method inputclock(IN1) enable((*inhigh*) en_inputclock) clocked_by(c);
method inputreset(IN2) enable((*inhigh*) en_inputreset) clocked_by(c);
schedule ( inputclock, inputreset) CF ( inputclock, inputreset);
endmodule
(* always_ready, always_enabled *)
interface B2C1;
interface Clock c;
method Action inputclock(Bit#(1) v);
endinterface
import "BVI" CONNECTNET =
module mkB2C1(B2C1);
default_clock clk();
default_reset rst();
output_clock c(OUT);
method inputclock(IN) enable((*inhigh*) en_inputclock);
schedule ( inputclock) CF ( inputclock);
endmodule
(* always_ready, always_enabled *)
interface C2B;
method Bit#(1) o();
endinterface
import "BVI" CONNECTNET =
module mkC2B#(Clock c)(C2B);
default_clock clk();
default_reset no_reset;
//default_reset rst();
input_clock ck(IN) = c;
method OUT o();
schedule ( o) CF ( o);
endmodule
(* always_ready, always_enabled *)
interface B2R;
interface Reset r;
method Action inputreset(Bit#(1) v);
endinterface
import "BVI" CONNECTNET =
module mkB2R(B2R);
default_clock clk();
default_reset rst();
output_reset r(OUT);
method inputreset(IN) enable((*inhigh*) en_inputclock);
schedule ( inputreset) CF ( inputreset);
endmodule
(* always_ready, always_enabled *)
interface R2B;
method Bit#(1) o();
endinterface
import "BVI" CONNECTNET =
module mkR2B#(Reset r)(C2B);
default_clock no_clock;
default_reset no_reset;
//default_reset rst();
input_reset rst(IN) = r;
method OUT o();
schedule ( o) CF ( o);
endmodule
interface PositiveReset;
interface Reset positiveReset;
endinterface
import "BVI" PositiveReset =
module mkPositiveReset#(Integer resetDelay, Reset reset, Clock clock)(PositiveReset);
parameter RSTDELAY = resetDelay;
default_clock clock(CLK) = clock;
default_reset reset(IN_RST) clocked_by (no_clock) = reset;
output_reset positiveReset(OUT_RST);
endmodule
interface FpgaReset;
interface Reset fpgaReset;
endinterface
import "BVI" FpgaReset =
module exposeFpgaReset#(Integer resetDelay, Clock clock)(FpgaReset);
parameter RSTDELAY = resetDelay;
default_clock clock(CLK) = clock;
no_reset;
output_reset fpgaReset(OUT_RST);
endmodule

View File

@@ -0,0 +1,42 @@
// Copyright (c) 2014 Quanta Research Cambridge, Inc.
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
function Bool booland(Bool x1, Bool x2); return x1 && x2; endfunction
function Bool boolor(Bool x1, Bool x2); return x1 || x2; endfunction
function Bool eq(a x1, a x2) provisos (Eq#(a)); return x1 == x2; endfunction
function a add(a x1, a x2) provisos (Arith#(a)); return x1 + x2; endfunction
function a mul(a x1, a x2) provisos (Arith#(a)); return x1 * x2; endfunction
function Bit#(b) rshift(Bit#(b) x1, Integer i); return x1 >> i; endfunction
function Vector#(n, a) vadd(Vector#(n, a) x1, Vector#(n, a) x2) provisos (Arith#(a));
return map(uncurry(add), zip(x1, x2));
endfunction
function Vector#(n, a) vmul(Vector#(n, a) x1, Vector#(n, a) x2) provisos (Arith#(a));
return map(uncurry(mul), zip(x1, x2));
endfunction
function Vector#(n, Bit#(b)) vrshift(Vector#(n, Bit#(b)) x1, Integer i);
return map(flip(rshift)(i), x1);
endfunction

View File

@@ -0,0 +1,15 @@
`define ConnectalVersion 15.11.1
`define NumberOfMasters 1
`define PinType Empty
`define PinTypeInclude Misc
`define NumberOfUserTiles 1
`define SlaveDataBusWidth 32
`define SlaveControlAddrWidth 5
`define BurstLenSize 10
`define project_dir $(DTOP)
`define MainClockPeriod 20
`define DerivedClockPeriod 10.000000
`define BsimHostInterface
`define PhysAddrWidth 40
`define SIMULATION
`define BOARD_bluesim

View File

@@ -0,0 +1,76 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Assert::*;
// User interface:
// all DRAM use 64B data block
// present user with 64-bit address space, but addr should be in terms of 64B
typedef 512 DramUserDataSz;
typedef TDiv#(DramUserDataSz, 8) DramUserBESz;
typedef 58 DramUserAddrSz;
typedef Bit#(DramUserDataSz) DramUserData;
typedef Bit#(DramUserBESz) DramUserBE;
typedef Bit#(DramUserAddrSz) DramUserAddr;
typedef struct { // read/write req
DramUserAddr addr;
DramUserData data;
DramUserBE wrBE; // all 0 means read,
// otherwise wrBE[i]=1 means to write byte i
} DramUserReq deriving(Bits, Eq, FShow);
interface DramUser#(
// maximum number of in-flight requests (not always used)
numeric type maxReadNum,
numeric type maxWriteNum,
// simulation delay (fully pipelined)
numeric type simDelay,
// error of dram controller
// XXX we should not check address overflow because of wrong-path loads
type errT
);
method Action req(DramUserReq r);
method ActionValue#(DramUserData) rdResp; // only read has resp
method ActionValue#(errT) err;
endinterface
// Full interface
interface DramFull#(
numeric type maxReadNum,
numeric type maxWriteNum,
numeric type simDelay,
type errT,
// pin type
type pinT
);
interface DramUser#(maxReadNum, maxWriteNum, simDelay, errT) user;
interface pinT pins;
endinterface
`ifdef BSIM
function Action doAssert(Bool b, String s) = action if(!b) $fdisplay(stderr, "\n%m: ASSERT FAIL!!"); dynamicAssert(b, s); endaction;
`else
function Action doAssert(Bool b, String s) = noAction;
`endif

View File

@@ -0,0 +1,38 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Clocks::*;
interface ResetGuard;
method Bool isReady;
endinterface
import "BVI" reset_guard =
module mkResetGuard(ResetGuard);
default_clock clk(CLK);
default_reset rst(RST);
method IS_READY isReady();
schedule (isReady) CF (isReady);
endmodule

View File

@@ -0,0 +1,114 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Clocks::*;
import FIFOF::*;
import Assert::*;
import ConnectalBramFifo::*;
import BRAMFIFO::*;
import XilinxSyncFifo::*;
import ResetGuard::*;
export mkSyncFifo;
export mkSyncBramFifo;
// sync fifo type selec macros: USE_CONNECTAL_BRAM_SYNC_FIFO, USE_BSV_BRAM_SYNC_FIFO, USE_XILINX_SYNC_FIFO
// we also guard the sync fifo from enq/deq before the reset
module mkSyncFifo#(
Integer depth, Clock srcClk, Reset srcRst, Clock dstClk, Reset dstRst
)(SyncFIFOIfc#(t)) provisos(
Bits#(t, tW),
NumAlias#(w, TMax#(1, tW)) // some sync fifo doesn't support 0-bit data
);
`ifdef USE_CONNECTAL_BRAM_SYNC_FIFO
staticAssert(depth <= 512, "depth of FIFO18 is 512");
FIFOF#(Bit#(w)) q <- mkDualClockBramFIFOF(srcClk, srcRst, dstClk, dstRst);
`elsif USE_BSV_BRAM_SYNC_FIFO
SyncFIFOIfc#(Bit#(w)) q <- mkSyncBRAMFIFO(depth, srcClk, srcRst, dstClk, dstRst);
`elsif USE_XILINX_SYNC_FIFO
staticAssert(depth <= 16, "depth of xilinx lut ram FIFO is 16");
SyncFIFOIfc#(Bit#(w)) q <- mkXilinxSyncFifo(srcClk, srcRst, dstClk);
`else
SyncFIFOIfc#(Bit#(w)) q <- mkSyncFIFO(depth, srcClk, srcRst, dstClk);
`endif
// guard sync fifo operations
ResetGuard srcGuard <- mkResetGuard(clocked_by srcClk, reset_by srcRst);
ResetGuard dstGuard <- mkResetGuard(clocked_by dstClk, reset_by dstRst);
method notFull = q.notFull && srcGuard.isReady;
method Action enq(t x) if(srcGuard.isReady);
q.enq(zeroExtend(pack(x)));
endmethod
method notEmpty = q.notEmpty && dstGuard.isReady;
method t first if(dstGuard.isReady);
return unpack(truncate(q.first));
endmethod
method Action deq if(dstGuard.isReady);
q.deq;
endmethod
endmodule
module mkSyncBramFifo#(
Integer depth, Clock srcClk, Reset srcRst, Clock dstClk, Reset dstRst
)(SyncFIFOIfc#(t)) provisos(
Bits#(t, tW),
NumAlias#(w, TMax#(1, tW)) // some sync fifo doesn't support 0-bit data
);
`ifdef USE_CONNECTAL_BRAM_SYNC_FIFO
staticAssert(depth <= 512, "depth of FIFO18 is 512");
FIFOF#(Bit#(w)) q <- mkDualClockBramFIFOF(srcClk, srcRst, dstClk, dstRst);
`elsif USE_XILINX_SYNC_FIFO
staticAssert(depth <= 512, "depth of xilinx block ram FIFO is 512");
SyncFIFOIfc#(Bit#(w)) q <- mkXilinxSyncBramFifo(srcClk, srcRst, dstClk);
`else
SyncFIFOIfc#(Bit#(w)) q <- mkSyncBRAMFIFO(depth, srcClk, srcRst, dstClk, dstRst);
`endif
// guard sync fifo operations
ResetGuard srcGuard <- mkResetGuard(clocked_by srcClk, reset_by srcRst);
ResetGuard dstGuard <- mkResetGuard(clocked_by dstClk, reset_by dstRst);
method notFull = q.notFull && srcGuard.isReady;
method Action enq(t x) if(srcGuard.isReady);
q.enq(zeroExtend(pack(x)));
endmethod
method notEmpty = q.notEmpty && dstGuard.isReady;
method t first if(dstGuard.isReady);
return unpack(truncate(q.first));
endmethod
method Action deq if(dstGuard.isReady);
q.deq;
endmethod
endmodule

View File

@@ -0,0 +1,51 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import ResetGuard::*;
// some xilinx IP automatically reset
// we wait several cycles to make sure the reset completes
// we also block signals on the IP before reset of current clock domain completes
// because such signals may be garbage
interface WaitAutoReset#(numeric type logCycles);
method Bool isReady;
endinterface
module mkWaitAutoReset(WaitAutoReset#(logCycles));
Reg#(Bit#(logCycles)) cnt <- mkReg(0);
Reg#(Bool) init <- mkReg(False);
ResetGuard rg <- mkResetGuard;
rule doInit(!init);
cnt <= cnt + 1;
if(cnt == maxBound) begin
init <= True;
end
endrule
method Bool isReady;
return init && rg.isReady;
endmethod
endmodule

View File

@@ -0,0 +1,387 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import DefaultValue::*;
import GetPut::*;
import ClientServer::*;
import WaitAutoReset::*;
import FloatingPoint::*;
import FIFO::*;
export mkXilinxFpFmaIP;
export mkXilinxFpDivIP;
export mkXilinxFpSqrtIP;
export mkXilinxFpFmaSim;
export mkXilinxFpDivSim;
export mkXilinxFpSqrtSim;
export mkXilinxFpFma;
export mkXilinxFpDiv;
export mkXilinxFpSqrt;
typedef FloatingPoint::RoundMode FpuRoundMode;
typedef FloatingPoint::Exception FpuException;
export FpuRoundMode(..);
export FpuException(..);
// import Xilinx IP core
// xilinx raw FMA IP is a * b + c, later we need to adapt to a + b * c
interface FpFmaImport;
method Action enqA(Bit#(64) a);
method Action enqB(Bit#(64) b);
method Action enqC(Bit#(64) c);
method Action deq;
method Bit#(64) resp;
method Bit#(3) excep;
endinterface
import "BVI" fp_fma =
module mkFpFmaImport(FpFmaImport);
default_clock clk(aclk, (*unused*) unused_gate);
default_reset no_reset;
method enqA(s_axis_a_tdata) enable(s_axis_a_tvalid) ready(s_axis_a_tready);
method enqB(s_axis_b_tdata) enable(s_axis_b_tvalid) ready(s_axis_b_tready);
method enqC(s_axis_c_tdata) enable(s_axis_c_tvalid) ready(s_axis_c_tready);
method deq() enable(m_axis_result_tready) ready(m_axis_result_tvalid);
method m_axis_result_tdata resp() ready(m_axis_result_tvalid);
method m_axis_result_tuser excep() ready(m_axis_result_tvalid);
schedule (enqA, enqB, enqC) CF (deq, resp, excep);
schedule (enqA) CF (enqB, enqC);
schedule (enqB) CF (enqC);
schedule (enqA) C (enqA);
schedule (enqB) C (enqB);
schedule (enqC) C (enqC);
schedule (deq) C (deq);
schedule (resp, excep, deq) CF (resp, excep);
endmodule
interface FpDivImport;
method Action enqA(Bit#(64) a);
method Action enqB(Bit#(64) b);
method Action deq;
method Bit#(64) resp;
method Bit#(4) excep;
endinterface
import "BVI" fp_div =
module mkFpDivImport(FpDivImport);
default_clock clk(aclk, (*unused*) unused_gate);
default_reset no_reset;
method enqA(s_axis_a_tdata) enable(s_axis_a_tvalid) ready(s_axis_a_tready);
method enqB(s_axis_b_tdata) enable(s_axis_b_tvalid) ready(s_axis_b_tready);
method deq() enable(m_axis_result_tready) ready(m_axis_result_tvalid);
method m_axis_result_tdata resp() ready(m_axis_result_tvalid);
method m_axis_result_tuser excep() ready(m_axis_result_tvalid);
schedule (enqA, enqB) CF (deq, resp, excep);
schedule (enqA) CF (enqB);
schedule (enqA) C (enqA);
schedule (enqB) C (enqB);
schedule (deq) C (deq);
schedule (resp, excep, deq) CF (resp, excep);
endmodule
interface FpSqrtImport;
method Action enqA(Bit#(64) a);
method Action deq;
method Bit#(64) resp;
method Bit#(1) excep;
endinterface
import "BVI" fp_sqrt =
module mkFpSqrtImport(FpSqrtImport);
default_clock clk(aclk, (*unused*) unused_gate);
default_reset no_reset;
method enqA(s_axis_a_tdata) enable(s_axis_a_tvalid) ready(s_axis_a_tready);
method deq() enable(m_axis_result_tready) ready(m_axis_result_tvalid);
method m_axis_result_tdata resp() ready(m_axis_result_tvalid);
method m_axis_result_tuser excep() ready(m_axis_result_tvalid);
schedule (enqA) CF (deq, resp, excep);
schedule (enqA) C (enqA);
schedule (deq) C (deq);
schedule (resp, excep, deq) CF (resp, excep);
endmodule
// adapt the IP core to Bluespec FPU interface
(* synthesize *)
module mkXilinxFpFmaIP(Server#(
Tuple4#(Maybe#(Double), Double, Double, FpuRoundMode),
Tuple2#(Double, FpuException)
));
WaitAutoReset#(8) init <- mkWaitAutoReset;
FpFmaImport fpFma <- mkFpFmaImport;
// xilinx raw ip is a * b + c
// what we need to provide is in1 + in2 * in3
// so in1 -> c, in2 -> a, in3 -> b
interface Put request;
method Action put(Tuple4#(Maybe#(Double), Double, Double, FpuRoundMode) req) if(init.isReady);
let {maybe_in1, in2, in3, rm} = req;
Double in1 = fromMaybe(zero(False), maybe_in1);
fpFma.enqA(pack(in2));
fpFma.enqB(pack(in3));
fpFma.enqC(pack(in1));
if(rm != Rnd_Nearest_Even) begin
$fdisplay(stderr, "[Xlinx FMA] WARNING: unsupported rounding mode ", fshow(rm));
end
endmethod
endinterface
interface Get response;
method ActionValue#(Tuple2#(Double, FpuException)) get if(init.isReady);
fpFma.deq;
Double val = unpack(fpFma.resp);
Bit#(3) exBits = fpFma.excep;
FpuException excep = defaultValue;
excep.underflow = exBits[0] == 1;
excep.overflow = exBits[1] == 1;
excep.invalid_op = exBits[2] == 1;
return tuple2(val, excep);
endmethod
endinterface
endmodule
(* synthesize *)
module mkXilinxFpDivIP(Server#(
Tuple3#(Double, Double, FpuRoundMode),
Tuple2#(Double, FpuException)
));
WaitAutoReset#(8) init <- mkWaitAutoReset;
FpDivImport fpDiv <- mkFpDivImport;
interface Put request;
method Action put(Tuple3#(Double, Double, FpuRoundMode) req) if(init.isReady);
let {a, b, rm} = req;
fpDiv.enqA(pack(a));
fpDiv.enqB(pack(b));
if(rm != Rnd_Nearest_Even) begin
$fdisplay(stderr, "[Xlinx DIV] WARNING: unsupported rounding mode ", fshow(rm));
end
endmethod
endinterface
interface Get response;
method ActionValue#(Tuple2#(Double, FpuException)) get if(init.isReady);
fpDiv.deq;
Double val = unpack(fpDiv.resp);
Bit#(4) exBits = fpDiv.excep;
FpuException excep = defaultValue;
excep.underflow = exBits[0] == 1;
excep.overflow = exBits[1] == 1;
excep.invalid_op = exBits[2] == 1;
excep.divide_0 = exBits[3] == 1;
return tuple2(val, excep);
endmethod
endinterface
endmodule
(* synthesize *)
module mkXilinxFpSqrtIP(Server#(
Tuple2#(Double, FpuRoundMode),
Tuple2#(Double, FpuException)
));
WaitAutoReset#(8) init <- mkWaitAutoReset;
FpSqrtImport fpSqrt <- mkFpSqrtImport;
interface Put request;
method Action put(Tuple2#(Double, FpuRoundMode) req) if(init.isReady);
let {a, rm} = req;
fpSqrt.enqA(pack(a));
if(rm != Rnd_Nearest_Even) begin
$fdisplay(stderr, "[Xlinx SQRT] WARNING: unsupported rounding mode ", fshow(rm));
end
endmethod
endinterface
interface Get response;
method ActionValue#(Tuple2#(Double, FpuException)) get if(init.isReady);
fpSqrt.deq;
Double val = unpack(fpSqrt.resp);
Bit#(1) exBits = fpSqrt.excep;
FpuException excep = defaultValue;
excep.invalid_op = exBits[0] == 1;
return tuple2(val, excep);
endmethod
endinterface
endmodule
// import simulation
interface FpFmaSim;
method Bit#(64) fma(Bit#(64) a, Bit#(64) b, Bit#(64) c);
endinterface
import "BVI" fp_fma_sim =
module mkFpFmaSim(FpFmaSim);
default_clock no_clock;
default_reset no_reset;
method RES fma(A, B, C);
endmodule
interface FpDivSim;
method Bit#(64) div(Bit#(64) a, Bit#(64) b);
endinterface
import "BVI" fp_div_sim =
module mkFpDivSim(FpDivSim);
default_clock no_clock;
default_reset no_reset;
method RES div(A, B);
endmodule
interface FpSqrtSim;
method Bit#(64) sqrt(Bit#(64) a);
endinterface
import "BVI" fp_sqrt_sim =
module mkFpSqrtSim(FpSqrtSim);
default_clock no_clock;
default_reset no_reset;
method RES sqrt(A);
endmodule
// adapt the simulation fp ops to Bluespec FPU interface
// Xilinx FPU has 2-element buffers inside
(* synthesize *)
module mkXilinxFpFmaSim(Server#(
Tuple4#(Maybe#(Double), Double, Double, FpuRoundMode),
Tuple2#(Double, FpuException)
));
FpFmaSim fpFma <- mkFpFmaSim;
FIFO#(Tuple2#(Double, FpuException)) respQ <- mkFIFO;
// xilinx raw ip is a * b + c
// what we need to provide is in1 + in2 * in3
// so in1 -> c, in2 -> a, in3 -> b
interface Put request;
method Action put(Tuple4#(Maybe#(Double), Double, Double, FpuRoundMode) req);
let {maybe_in1, in2, in3, rm} = req;
Double in1 = fromMaybe(zero(False), maybe_in1);
Double val = unpack(fpFma.fma(pack(in2), pack(in3), pack(in1)));
respQ.enq(tuple2(val, defaultValue));
endmethod
endinterface
interface response = toGet(respQ);
endmodule
(* synthesize *)
module mkXilinxFpDivSim(Server#(
Tuple3#(Double, Double, FpuRoundMode),
Tuple2#(Double, FpuException)
));
FpDivSim fpDiv <- mkFpDivSim;
FIFO#(Tuple2#(Double, FpuException)) respQ <- mkFIFO;
interface Put request;
method Action put(Tuple3#(Double, Double, FpuRoundMode) req);
let {a, b, rm} = req;
Double val = unpack(fpDiv.div(pack(a), pack(b)));
respQ.enq(tuple2(val, defaultValue));
endmethod
endinterface
interface response = toGet(respQ);
endmodule
(* synthesize *)
module mkXilinxFpSqrtSim(Server#(
Tuple2#(Double, FpuRoundMode),
Tuple2#(Double, FpuException)
));
FpSqrtSim fpSqrt <- mkFpSqrtSim;
FIFO#(Tuple2#(Double, FpuException)) respQ <- mkFIFO;
interface Put request;
method Action put(Tuple2#(Double, FpuRoundMode) req);
let {a, rm} = req;
Double val = unpack(fpSqrt.sqrt(pack(a)));
respQ.enq(tuple2(val, defaultValue));
endmethod
endinterface
interface response = toGet(respQ);
endmodule
// wrap simulation and xilinx ip together
(* synthesize *)
module mkXilinxFpFma(Server#(
Tuple4#(Maybe#(Double), Double, Double, FpuRoundMode),
Tuple2#(Double, FpuException)
));
`ifdef BSIM
let m <- mkXilinxFpFmaSim;
`else
let m <- mkXilinxFpFmaIP;
`endif
return m;
endmodule
(* synthesize *)
module mkXilinxFpDiv(Server#(
Tuple3#(Double, Double, FpuRoundMode),
Tuple2#(Double, FpuException)
));
`ifdef BSIM
let m <- mkXilinxFpDivSim;
`else
let m <- mkXilinxFpDivIP;
`endif
return m;
endmodule
(* synthesize *)
module mkXilinxFpSqrt(Server#(
Tuple2#(Double, FpuRoundMode),
Tuple2#(Double, FpuException)
));
`ifdef BSIM
let m <- mkXilinxFpSqrtSim;
`else
let m <- mkXilinxFpSqrtIP;
`endif
return m;
endmodule

View File

@@ -0,0 +1,203 @@
import FIFOF::*;
import FIFO::*;
import WaitAutoReset::*;
export XilinxIntDiv(..);
export mkXilinxIntDiv;
// import Xilinx IP core for unsigned division
// axi tuser: user tag + info needed to handle divide by 0 + sign info
typedef struct {
Bool divByZero;
Bit#(64) divByZeroRem; // remainder in case of divide by zero
Bool signedDiv; // signed division (so dividend/divisor has been abs)
Bit#(1) quotientSign; // sign bit of quotient (in case of signedDiv)
Bit#(1) remainderSign; // sign bit of remainder (in case of signedDiv)
Bit#(8) tag;
} IntDivUser deriving(Bits, Eq, FShow);
interface IntDivUnsignedImport;
method Action enqDividend(Bit#(64) dividend, IntDivUser user);
method Action enqDivisor(Bit#(64) divisor);
method Action deqResp;
method Bool respValid;
method Bit#(128) quotient_remainder;
method IntDivUser respUser;
endinterface
import "BVI" int_div_unsigned =
module mkIntDivUnsignedImport(IntDivUnsignedImport);
default_clock clk(aclk, (*unused*) unused_gate);
default_reset no_reset;
method enqDividend(
s_axis_dividend_tdata, s_axis_dividend_tuser
) enable(s_axis_dividend_tvalid) ready(s_axis_dividend_tready);
method enqDivisor(
s_axis_divisor_tdata
) enable(s_axis_divisor_tvalid) ready(s_axis_divisor_tready);
method deqResp() enable(m_axis_dout_tready) ready(m_axis_dout_tvalid);
method m_axis_dout_tvalid respValid;
method m_axis_dout_tdata quotient_remainder ready(m_axis_dout_tvalid);
method m_axis_dout_tuser respUser ready(m_axis_dout_tvalid);
schedule (enqDividend) C (enqDividend);
schedule (enqDivisor) C (enqDivisor);
schedule (deqResp) C (deqResp);
schedule (enqDividend) CF (enqDivisor, deqResp);
schedule (enqDivisor) CF (deqResp);
schedule (
respValid,
quotient_remainder,
respUser
) CF (
respValid,
quotient_remainder,
respUser,
enqDividend,
enqDivisor,
deqResp
);
endmodule
// simulation
module mkIntDivUnsignedSim(IntDivUnsignedImport);
FIFO#(Tuple2#(Bit#(64), IntDivUser)) dividendQ <- mkFIFO;
FIFO#(Bit#(64)) divisorQ <- mkFIFO;
FIFOF#(Tuple2#(Bit#(128), IntDivUser)) respQ <- mkSizedFIFOF(2);
rule compute;
dividendQ.deq;
divisorQ.deq;
let {dividend, user} = dividendQ.first;
let divisor = divisorQ.first;
UInt#(64) a = unpack(dividend);
UInt#(64) b = unpack(divisor);
Bit#(64) q = pack(a / b);
Bit#(64) r = pack(a % b);
respQ.enq(tuple2({q, r}, user));
endrule
method Action enqDividend(Bit#(64) dividend, IntDivUser user);
dividendQ.enq(tuple2(dividend, user));
endmethod
method Action enqDivisor(Bit#(64) divisor);
divisorQ.enq(divisor);
endmethod
method Action deqResp;
respQ.deq;
endmethod
method respValid = respQ.notEmpty;
method quotient_remainder = tpl_1(respQ.first);
method respUser = tpl_2(respQ.first);
endmodule
// Wrapper for user (add reset guard, check overflow/divided by 0). We cannot
// unify two dividers to one, because divider latency may not be a constant.
interface XilinxIntDiv#(type tagT);
method Action req(Bit#(64) dividend, Bit#(64) divisor, Bool signedDiv, tagT tag);
// response
method Action deqResp;
method Bool respValid;
method Bit#(64) quotient;
method Bit#(64) remainder;
method tagT respTag;
endinterface
module mkXilinxIntDiv(XilinxIntDiv#(tagT)) provisos (
Bits#(tagT, tagSz), Add#(tagSz, a__, 8)
);
`ifdef BSIM
IntDivUnsignedImport divIfc <- mkIntDivUnsignedSim;
`else
IntDivUnsignedImport divIfc <- mkIntDivUnsignedImport;
`endif
WaitAutoReset#(4) init <- mkWaitAutoReset;
method Action req(
Bit#(64) dividend, Bit#(64) divisor, Bool signedDiv, tagT tag
) if(init.isReady);
// compute the input ops to div unsigned IP
Bit#(1) dividend_sign = truncateLSB(dividend);
Bit#(1) divisor_sign = truncateLSB(divisor);
Bit#(64) a = dividend;
Bit#(64) b = divisor;
if(signedDiv) begin
if(dividend_sign == 1) begin
a = 0 - dividend;
end
if(divisor_sign == 1) begin
b = 0 - divisor;
end
end
// get the user struct (sign/divide by 0)
let user = IntDivUser {
divByZero: divisor == 0,
divByZeroRem: dividend,
signedDiv: signedDiv,
// quotient negative when dividend and divisor have different signs
quotientSign: dividend_sign ^ divisor_sign,
// remainder sign follows that of dividend
remainderSign: dividend_sign,
tag: zeroExtend(pack(tag))
};
divIfc.enqDividend(a, user);
divIfc.enqDivisor(b);
endmethod
// we also put reset guard on deq port to prevent random signals before
// reset from dequing or corrupting axi states
method Action deqResp if(init.isReady);
divIfc.deqResp;
endmethod
method respValid = divIfc.respValid && init.isReady;
method Bit#(64) quotient if(init.isReady);
let user = divIfc.respUser;
Bit#(64) q;
if(user.divByZero) begin
q = maxBound;
end
else begin
q = truncateLSB(divIfc.quotient_remainder);
if(user.signedDiv && user.quotientSign == 1) begin
q = 0 - q;
end
// signed overflow is automatically handled
end
return q;
endmethod
method Bit#(64) remainder if(init.isReady);
let user = divIfc.respUser;
Bit#(64) r;
if(user.divByZero) begin
r = user.divByZeroRem;
end
else begin
r = truncate(divIfc.quotient_remainder);
if(user.signedDiv && user.remainderSign == 1) begin
r = 0 - r;
end
// signed overflow is automatically handled
end
return r;
endmethod
method tagT respTag if(init.isReady);
return unpack(truncate(divIfc.respUser.tag));
endmethod
endmodule

View File

@@ -0,0 +1,205 @@
import Vector::*;
import FIFOF::*;
import Assert::*;
export XilinxIntMulSign(..);
export XilinxIntMul(..);
export mkXilinxIntMul;
// Xilinx int multiplier IP is a rigorous pipeline with a fixed latency. There
// is no back pressure in the raw IP. We will wrap it with flow control. To do
// this, we need to know the pipeline latency from macro
// XILINX_INT_MUL_LATENCY.
typedef `XILINX_INT_MUL_LATENCY IntMulLatency;
interface IntMulImport;
method Action req(Bit#(64) a, Bit#(64) b);
method Bit#(128) product;
endinterface
import "BVI" int_mul_signed =
module mkIntMulSignedImport(IntMulImport);
default_clock clk(CLK, (*unused*) unused_gate);
default_reset no_reset;
method req(A, B) enable((*inhigh*) EN);
method P product;
schedule (req) C (req);
schedule (product) CF (req, product);
endmodule
import "BVI" int_mul_unsigned =
module mkIntMulUnsignedImport(IntMulImport);
default_clock clk(CLK, (*unused*) unused_gate);
default_reset no_reset;
method req(A, B) enable((*inhigh*) EN);
method P product;
schedule (req) C (req);
schedule (product) CF (req, product);
endmodule
import "BVI" int_mul_signed_unsigned =
module mkIntMulSignedUnsignedImport(IntMulImport);
default_clock clk(CLK, (*unused*) unused_gate);
default_reset no_reset;
method req(A, B) enable((*inhigh*) EN);
method P product;
schedule (req) C (req);
schedule (product) CF (req, product);
endmodule
// mul req type
typedef enum {
Signed,
Unsigned,
SignedUnsigned
} XilinxIntMulSign deriving(Bits, Eq, FShow);
// simulation
module mkIntMulSim#(XilinxIntMulSign sign)(IntMulImport);
RWire#(Bit#(128)) newReq <- mkRWire;
Vector#(
IntMulLatency, Reg#(Maybe#(Bit#(128)))
) pipe <- replicateM(mkReg(Invalid));
(* fire_when_enabled, no_implicit_conditions *)
rule canon;
for(Integer i = 1; i < valueof(IntMulLatency); i = i+1) begin
pipe[i] <= pipe[i - 1];
end
pipe[0] <= newReq.wget;
endrule
method Action req(Bit#(64) a, Bit#(64) b);
Int#(128) op1 = (case(sign)
Signed, SignedUnsigned: (unpack(signExtend(a)));
default: (unpack(zeroExtend(a)));
endcase);
Int#(128) op2 = (case(sign)
Signed: (unpack(signExtend(b)));
default: (unpack(zeroExtend(b)));
endcase);
Int#(128) prod = op1 * op2;
newReq.wset(pack(prod));
endmethod
method Bit#(128) product = fromMaybe(?, pipe[valueof(IntMulLatency) - 1]);
endmodule
// wrap up all mul IPs to have back pressure
interface XilinxIntMul#(type tagT);
method Action req(Bit#(64) a, Bit#(64) b, XilinxIntMulSign sign, tagT tag);
method Action deqResp;
method Bool respValid;
method Bit#(128) product;
method tagT respTag;
endinterface
module mkXilinxIntMul(XilinxIntMul#(tagT)) provisos(
Bits#(tagT, tagSz),
// credit based flow control types
NumAlias#(TAdd#(IntMulLatency, 1), maxCredit),
Alias#(Bit#(TLog#(TAdd#(maxCredit, 1))), creditT)
);
// different multilpliers: WaitAutoReset is not needed, since mul is a
// pipeline with fixed latency
`ifdef BSIM
IntMulImport mulSigned <- mkIntMulSim(Signed);
IntMulImport mulUnsigned <- mkIntMulSim(Unsigned);
IntMulImport mulSignedUnsigned <- mkIntMulSim(SignedUnsigned);
`else
IntMulImport mulSigned <- mkIntMulSignedImport;
IntMulImport mulUnsigned <- mkIntMulUnsignedImport;
IntMulImport mulSignedUnsigned <- mkIntMulSignedUnsignedImport;
`endif
// resp FIFO (unguarded) & flow ctrl credit
FIFOF#(
Tuple2#(Bit#(128), tagT)
) respQ <- mkUGSizedFIFOF(valueof(maxCredit));
Reg#(creditT) credit <- mkReg(fromInteger(valueof(maxCredit)));
// shift regs for sign + tag
Vector#(
IntMulLatency,
Reg#(Maybe#(Tuple2#(XilinxIntMulSign, tagT)))
) pipe <- replicateM(mkReg(Invalid));
// wire to catch input req
RWire#(Tuple2#(XilinxIntMulSign, tagT)) newReq <- mkRWire;
// wire to catch deq
PulseWire deqEn <- mkPulseWire;
(* fire_when_enabled, no_implicit_conditions *)
rule canon;
creditT nextCredit = credit;
// incr credit if resp FIFO is deq
if(deqEn) begin
if(nextCredit >= fromInteger(valueof(maxCredit))) begin
$fdisplay(stderr, "\n%m: ASSERT FAIL!!");
dynamicAssert(False, "credit overflow");
end
nextCredit = nextCredit + 1;
end
// enq resp FIFO if something is outputed from mul
if (
pipe[valueof(IntMulLatency) - 1] matches tagged Valid {.sign, .tag}
) begin
Bit#(128) prod = (case(sign)
Signed: (mulSigned.product);
Unsigned: (mulUnsigned.product);
SignedUnsigned: (mulSignedUnsigned.product);
default: (?);
endcase);
respQ.enq(tuple2(prod, tag));
end
// shift pipe regs
for(Integer i = 1; i < valueof(IntMulLatency); i = i+1) begin
pipe[i] <= pipe[i - 1];
end
pipe[0] <= newReq.wget;
// decr credit if new req is taken
if(isValid(newReq.wget)) begin
if(nextCredit == 0) begin
$fdisplay(stderr, "\n%m: ASSERT FAIL!!");
dynamicAssert(False, "credit underflow");
end
nextCredit = nextCredit - 1;
end
// update credit
credit <= nextCredit;
endrule
method Action req(Bit#(64) a, Bit#(64) b,
XilinxIntMulSign sign, tagT tag) if(credit > 0);
case(sign)
Signed: mulSigned.req(a, b);
Unsigned: mulUnsigned.req(a, b);
SignedUnsigned: mulSignedUnsigned.req(a, b);
endcase
newReq.wset(tuple2(sign, tag)); // notify new req
endmethod
method Action deqResp if(respQ.notEmpty);
respQ.deq;
deqEn.send; // notify deq resp
endmethod
method respValid = respQ.notEmpty;
method Bit#(128) product if(respQ.notEmpty);
return tpl_1(respQ.first);
endmethod
method tagT respTag if(respQ.notEmpty);
return tpl_2(respQ.first);
endmethod
endmodule

View File

@@ -0,0 +1,181 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import Clocks::*;
import CBus::*;
export mkXilinxSyncFifo;
export mkXilinxSyncBramFifo;
// import sync fifos from Xilinx FIFO generator
interface SyncFifoImport#(numeric type w);
method Bool full;
method Action enq(Bit#(w) x);
method Bool empty;
method Bit#(w) first;
method Action deq;
endinterface
import "BVI" sync_fifo_w32_d16 =
module mkSyncFifoImport_w32_d16#(Clock srcClk, Clock dstClk)(SyncFifoImport#(32));
default_clock no_clock;
default_reset no_reset;
input_clock (wr_clk) = srcClk;
input_clock (rd_clk) = dstClk;
method full full() clocked_by(srcClk) reset_by(no_reset);
method enq(din) enable(wr_en) clocked_by(srcClk) reset_by(no_reset);
method empty empty() clocked_by(dstClk) reset_by(no_reset);
method dout first() clocked_by(dstClk) reset_by(no_reset);
method deq() enable(rd_en) clocked_by(dstClk) reset_by(no_reset);
schedule (full, enq) CF (empty, first, deq);
schedule (full) CF (full, enq);
schedule (enq) C (enq);
schedule (empty, first) CF (empty, first, deq);
schedule (deq) C (deq);
endmodule
import "BVI" sync_bram_fifo_w36_d512 =
module mkSyncBramFifoImport_w36_d512#(Clock srcClk, Clock dstClk)(SyncFifoImport#(36));
default_clock no_clock;
default_reset no_reset;
input_clock (wr_clk) = srcClk;
input_clock (rd_clk) = dstClk;
method full full() clocked_by(srcClk) reset_by(no_reset);
method enq(din) enable(wr_en) clocked_by(srcClk) reset_by(no_reset);
method empty empty() clocked_by(dstClk) reset_by(no_reset);
method dout first() clocked_by(dstClk) reset_by(no_reset);
method deq() enable(rd_en) clocked_by(dstClk) reset_by(no_reset);
schedule (full, enq) CF (empty, first, deq);
schedule (full) CF (full, enq);
schedule (enq) C (enq);
schedule (empty, first) CF (empty, first, deq);
schedule (deq) C (deq);
endmodule
// wrap up imported BSV or simulation module
(* synthesize, no_default_clock, no_default_reset *)
module mkSyncFifo_w32_d16#(Clock srcClk, Reset srcRst, Clock dstClk)(SyncFIFOIfc#(Bit#(w))) provisos (NumAlias#(w, 32));
`ifdef BSIM
SyncFIFOIfc#(Bit#(w)) q <- mkSyncFIFO(16, srcClk, srcRst, dstClk);
return q;
`else
SyncFifoImport#(w) q <- mkSyncFifoImport_w32_d16(srcClk, dstClk);
method notFull = !q.full;
method Action enq(Bit#(w) x) if(!q.full);
q.enq(x);
endmethod
method notEmpty = !q.empty;
method Bit#(w) first if(!q.empty);
return q.first;
endmethod
method Action deq if(!q.empty);
q.deq;
endmethod
`endif
endmodule
(* synthesize, no_default_clock, no_default_reset *)
module mkSyncBramFifo_w36_d512#(Clock srcClk, Reset srcRst, Clock dstClk)(SyncFIFOIfc#(Bit#(w))) provisos (NumAlias#(w, 36));
`ifdef BSIM
SyncFIFOIfc#(Bit#(w)) q <- mkSyncFIFO(512, srcClk, srcRst, dstClk);
return q;
`else
SyncFifoImport#(w) q <- mkSyncBramFifoImport_w36_d512(srcClk, dstClk);
method notFull = !q.full;
method Action enq(Bit#(w) x) if(!q.full);
q.enq(x);
endmethod
method notEmpty = !q.empty;
method Bit#(w) first if(!q.empty);
return q.first;
endmethod
method Action deq if(!q.empty);
q.deq;
endmethod
`endif
endmodule
// generate parameterized-width fifo from fixed-with fifo
module mkGenXilinxSyncFifo#(
function module#(SyncFIFOIfc#(Bit#(fifoW))) mkfifo(Clock srcClk, Reset srcRst, Clock dstClk),
Clock srcClk, Reset srcRst, Clock dstClk
)(SyncFIFOIfc#(t)) provisos(
Bits#(t, dataW),
Add#(1, a__, dataW),
NumAlias#(fifoNum, TDiv#(dataW, fifoW))
);
Vector#(fifoNum, SyncFIFOIfc#(Bit#(fifoW))) fifos <- replicateM(mkfifo(srcClk, srcRst, dstClk));
function Bool getNotFull(SyncFIFOIfc#(Bit#(fifoW)) ifc) = ifc.notFull;
Bool isNotFull = all(getNotFull, fifos);
function Bool getNotEmpty(SyncFIFOIfc#(Bit#(fifoW)) ifc) = ifc.notEmpty;
Bool isNotEmpty = all(getNotEmpty, fifos);
method notFull = isNotFull;
method Action enq(t x);
Vector#(fifoNum, Bit#(fifoW)) data = unpack(zeroExtendNP(pack(x)));
for(Integer i = 0; i < valueof(fifoNum); i = i+1) begin
fifos[i].enq(data[i]);
end
endmethod
method notEmpty = isNotEmpty;
method t first;
Vector#(fifoNum, Bit#(fifoW)) data = ?;
for(Integer i = 0; i < valueof(fifoNum); i = i+1) begin
data[i] = fifos[i].first;
end
return unpack(truncateNP(pack(data)));
endmethod
method Action deq;
for(Integer i = 0; i < valueof(fifoNum); i = i+1) begin
fifos[i].deq;
end
endmethod
endmodule
module mkXilinxSyncFifo#(Clock srcClk, Reset srcRst, Clock dstClk)(SyncFIFOIfc#(t)) provisos(
Bits#(t, dataW), Add#(1, a__, dataW)
);
SyncFIFOIfc#(t) m <- mkGenXilinxSyncFifo(mkSyncFifo_w32_d16, srcClk, srcRst, dstClk);
return m;
endmodule
module mkXilinxSyncBramFifo#(Clock srcClk, Reset srcRst, Clock dstClk)(SyncFIFOIfc#(t)) provisos(
Bits#(t, dataW), Add#(1, a__, dataW)
);
SyncFIFOIfc#(t) m <- mkGenXilinxSyncFifo(mkSyncBramFifo_w36_d512, srcClk, srcRst, dstClk);
return m;
endmodule

View File

@@ -0,0 +1,8 @@
module fp_div_sim(
input [63:0] A,
input [63:0] B,
output [63:0] RES
);
assign RES = $realtobits($bitstoreal(A) / $bitstoreal(B));
endmodule

View File

@@ -0,0 +1,9 @@
module fp_fma_sim(
input [63:0] A,
input [63:0] B,
input [63:0] C,
output [63:0] RES
);
assign RES = $realtobits($bitstoreal(A) * $bitstoreal(B) + $bitstoreal(C));
endmodule

View File

@@ -0,0 +1,7 @@
module fp_sqrt_sim(
input [63:0] A,
output [63:0] RES
);
assign RES = $realtobits($sqrt($bitstoreal(A)));
endmodule

View File

@@ -0,0 +1,69 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// This module outputs a 1-bit signal
// The signal is initially 0 after programming the FPGA
// XXX: everything should be inited to 0 after programming
// When a reset arrives, the module starts counting
// In N cycles after the reset, the signal becomes 1
// This signal can be used as a guard for sync fifo operations
`ifdef BSV_POSITIVE_RESET
`define BSV_RESET_VALUE 1'b1
`define BSV_RESET_EDGE posedge
`else
`define BSV_RESET_VALUE 1'b0
`define BSV_RESET_EDGE negedge
`endif
module reset_guard(
input CLK,
input RST,
output IS_READY
);
reg ready = 0;
reg rst_done = 0;
always@(posedge CLK) begin
if(RST == `BSV_RESET_VALUE) begin
ready <= 0;
rst_done <= 1;
// synopsys translate_off
if(!rst_done) begin
$display("[reset_guard] %t %m reset happen", $time);
end
// synopsys translate_on
end
else if(rst_done) begin
ready <= 1;
// synopsys translate_off
if(!ready) begin
$display("[reset_guard] %t %m guard ready", $time);
end
// synopsys translate_on
end
end
assign IS_READY = ready;
endmodule

View File

@@ -0,0 +1,382 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
`include "ProcConfig.bsv"
import Vector::*;
import FIFO::*;
import GetPut::*;
import BuildVector::*;
import Cntrs::*;
import Types::*;
import ProcTypes::*;
import SynthParam::*;
import Exec::*;
import Performance::*;
import BrPred::*;
import DirPredictor::*;
import ReservationStationEhr::*;
import ReservationStationAlu::*;
import ReorderBuffer::*;
import SpecFifo::*;
import HasSpecBits::*;
import Bypass::*;
// ALU pipeline has 4 stages
// dispatch -> reg read -> exe -> finish (write reg)
// bypass is sent out from the end of exe stage
// and reg read stage will recv bypass
typedef struct {
// inst info
DecodedInst dInst;
PhyRegs regs;
InstTag tag;
DirPredTrainInfo dpTrain;
// specualtion
Maybe#(SpecTag) spec_tag;
} AluDispatchToRegRead deriving(Bits, Eq, FShow);
typedef struct {
// inst info
DecodedInst dInst;
Maybe#(PhyDst) dst;
InstTag tag;
DirPredTrainInfo dpTrain;
// src reg vals & pc & ppc
Data rVal1;
Data rVal2;
Addr pc;
Addr ppc;
// specualtion
Maybe#(SpecTag) spec_tag;
} AluRegReadToExe deriving(Bits, Eq, FShow);
typedef struct {
// inst info
IType iType;
Maybe#(PhyDst) dst;
InstTag tag;
DirPredTrainInfo dpTrain;
// result
Data data; // alu compute result
Maybe#(Data) csrData; // data to write CSR file
ControlFlow controlFlow;
// speculation
Maybe#(SpecTag) spec_tag;
} AluExeToFinish deriving(Bits, Eq, FShow);
// XXX currently ALU/Br should not have any exception, so we don't have cause feild above
// TODO FIXME In future, if branch target is unaligned to 4 bytes, we may have exception
// and probably JR/JAL should NOT write dst reg when exception happens
// However, currently JR/JAL renaming is included in the previous checkpoint
// so we need to explicitly check exception and don't write reg
// synthesized pipeline fifos
typedef SpecFifo_SB_deq_enq_C_deq_enq#(1, AluDispatchToRegRead) AluDispToRegFifo;
(* synthesize *)
module mkAluDispToRegFifo(AluDispToRegFifo);
let m <- mkSpecFifo_SB_deq_enq_C_deq_enq(False);
return m;
endmodule
// this one must be 1 elem FIFO, otherwise we cannot get correct bypass
typedef SpecFifo_SB_deq_enq_C_deq_enq#(1, AluRegReadToExe) AluRegToExeFifo;
(* synthesize *)
module mkAluRegToExeFifo(AluRegToExeFifo);
let m <- mkSpecFifo_SB_deq_enq_C_deq_enq(False);
return m;
endmodule
// this one must be 1 elem FIFO, otherwise we cannot get correct bypass
typedef SpecFifo_SB_deq_enq_SB_deq_wrong_C_enq#(1, AluExeToFinish) AluExeToFinFifo;
(* synthesize *)
module mkAluExeToFinFifo(AluExeToFinFifo);
let m <- mkSpecFifo_SB_deq_enq_SB_deq_wrong_C_enq(False);
return m;
endmodule
typedef struct {
Addr pc;
Addr nextPc;
IType iType;
Bool taken;
DirPredTrainInfo dpTrain;
Bool mispred;
} FetchTrainBP deriving(Bits, Eq, FShow);
interface AluExeInput;
// conservative scoreboard check in reg read stage
method RegsReady sbCons_lazyLookup(PhyRegs r);
// Phys reg file
method Data rf_rd1(PhyRIndx rindx);
method Data rf_rd2(PhyRIndx rindx);
// CSR file
method Data csrf_rd(CSR csr);
// ROB
method Addr rob_getPC(InstTag t);
method Addr rob_getPredPC(InstTag t);
method Action rob_setExecuted(InstTag t, Maybe#(Data) csrData, ControlFlow cf);
// Fetch stage
method Action fetch_train_predictors(FetchTrainBP train);
// global broadcast methods
// set aggressive sb & wake up inst in RS
method Action setRegReadyAggr(PhyRIndx dst);
// send bypass from exe and finish stage
interface Vector#(2, SendBypass) sendBypass;
// write reg file & set conservative sb
method Action writeRegFile(PhyRIndx dst, Data data);
// redirect
method Action redirect(Addr new_pc, SpecTag spec_tag, InstTag inst_tag);
// spec update
method Action correctSpec(SpecTag t);
// performance
method Bool doStats;
endinterface
interface AluExePipeline;
// recv bypass from exe and finish stages of each ALU pipeline
interface Vector#(TMul#(2, AluExeNum), RecvBypass) recvBypass;
interface ReservationStationAlu rsAluIfc;
interface SpeculationUpdate specUpdate;
method Data getPerf(ExeStagePerfType t);
endinterface
module mkAluExePipeline#(AluExeInput inIfc)(AluExePipeline);
Bool verbose = True;
// alu reservation station
ReservationStationAlu rsAlu <- mkReservationStationAlu;
// pipeline fifos
let dispToRegQ <- mkAluDispToRegFifo;
let regToExeQ <- mkAluRegToExeFifo;
let exeToFinQ <- mkAluExeToFinFifo;
// wire to recv bypass
Vector#(TMul#(2, AluExeNum), RWire#(Tuple2#(PhyRIndx, Data))) bypassWire <- replicateM(mkRWire);
// index to send bypass, ordering doesn't matter
Integer exeSendBypassPort = 0;
Integer finishSendBypassPort = 1;
`ifdef PERF_COUNT
// performance counters
Count#(Data) exeRedirectBrCnt <- mkCount(0);
Count#(Data) exeRedirectJrCnt <- mkCount(0);
Count#(Data) exeRedirectOtherCnt <- mkCount(0);
`endif
rule doDispatchAlu;
rsAlu.doDispatch;
let x = rsAlu.dispatchData;
if(verbose) $display("[doDispatchAlu] ", fshow(x));
// set reg ready aggressively
if(x.regs.dst matches tagged Valid .dst) begin
inIfc.setRegReadyAggr(dst.indx);
end
// go to next stage
dispToRegQ.enq(ToSpecFifo {
data: AluDispatchToRegRead {
dInst: x.data.dInst,
regs: x.regs,
tag: x.tag,
dpTrain: x.data.dpTrain,
spec_tag: x.spec_tag
},
spec_bits: x.spec_bits
});
endrule
rule doRegReadAlu;
dispToRegQ.deq;
let dispToReg = dispToRegQ.first;
let x = dispToReg.data;
if(verbose) $display("[doRegReadAlu] ", fshow(dispToReg));
// check conservative scoreboard
let regsReady = inIfc.sbCons_lazyLookup(x.regs);
// get rVal1 (check bypass)
Data rVal1 = ?;
if(x.dInst.csr matches tagged Valid .csr) begin
rVal1 = inIfc.csrf_rd(csr);
end
else if(x.regs.src1 matches tagged Valid .src1) begin
rVal1 <- readRFBypass(src1, regsReady.src1, inIfc.rf_rd1(src1), bypassWire);
end
// get rVal2 (check bypass)
Data rVal2 = ?;
if(x.regs.src2 matches tagged Valid .src2) begin
rVal2 <- readRFBypass(src2, regsReady.src2, inIfc.rf_rd2(src2), bypassWire);
end
// get PC and PPC
let pc = inIfc.rob_getPC(x.tag);
let ppc = inIfc.rob_getPredPC(x.tag);
// go to next stage
regToExeQ.enq(ToSpecFifo {
data: AluRegReadToExe {
dInst: x.dInst,
dst: x.regs.dst,
tag: x.tag,
dpTrain: x.dpTrain,
rVal1: rVal1,
rVal2: rVal2,
pc: pc,
ppc: ppc,
spec_tag: x.spec_tag
},
spec_bits: dispToReg.spec_bits
});
endrule
rule doExeAlu;
regToExeQ.deq;
let regToExe = regToExeQ.first;
let x = regToExe.data;
if(verbose) $display("[doExeAlu] ", fshow(regToExe));
// execution
ExecResult exec_result = basicExec(x.dInst, x.rVal1, x.rVal2, x.pc, x.ppc);
// when inst needs to store csrData in ROB, it must have iType = Csr, cannot mispredict
if(isValid(x.dInst.csr)) begin
doAssert(x.dInst.iType == Csr, "Only Csr inst needs to update csrData in ROB");
doAssert(!exec_result.controlFlow.mispredict, "Csr inst cannot mispredict");
doAssert(exec_result.controlFlow.nextPc == x.ppc && x.ppc == x.pc + 4, "Csr inst ppc = pc+4");
end
// send bypass
if(x.dst matches tagged Valid .dst) begin
inIfc.sendBypass[exeSendBypassPort].send(dst.indx, exec_result.data);
end
// go to next stage
exeToFinQ.enq(ToSpecFifo {
data: AluExeToFinish {
iType: x.dInst.iType,
dst: x.dst,
tag: x.tag,
dpTrain: x.dpTrain,
data: exec_result.data,
csrData: isValid(x.dInst.csr) ? Valid (exec_result.csrData) : Invalid,
controlFlow: exec_result.controlFlow,
spec_tag: x.spec_tag
},
spec_bits: regToExe.spec_bits
});
endrule
rule doFinishAlu;
exeToFinQ.deq;
let exeToFin = exeToFinQ.first;
let x = exeToFin.data;
if(verbose) $display("[doFinishAlu] ", fshow(exeToFin));
// send bypass & write reg file
if(x.dst matches tagged Valid .dst) begin
inIfc.sendBypass[finishSendBypassPort].send(dst.indx, x.data);
inIfc.writeRegFile(dst.indx, x.data);
end
// update the instruction in the reorder buffer.
inIfc.rob_setExecuted(
x.tag,
x.csrData,
x.controlFlow
);
// handle spec tags for branch predictions
(* split *)
if (x.controlFlow.mispredict) (* nosplit *) begin
// wrong branch predictin, we must have spec tag
doAssert(isValid(x.spec_tag), "mispredicted branch must have spec tag");
inIfc.redirect(x.controlFlow.nextPc, validValue(x.spec_tag), x.tag);
// must be a branch, train branch predictor
doAssert(x.iType == Jr || x.iType == Br, "only jr and br can mispredict");
inIfc.fetch_train_predictors(FetchTrainBP {
pc: x.controlFlow.pc,
nextPc: x.controlFlow.nextPc,
iType: x.iType,
taken: x.controlFlow.taken,
dpTrain: x.dpTrain,
mispred: True
});
`ifdef PERF_COUNT
// performance counter
if(inIfc.doStats) begin
case(x.iType)
Br: exeRedirectBrCnt.incr(1);
Jr: exeRedirectJrCnt.incr(1);
default: exeRedirectOtherCnt.incr(1);
endcase
end
`endif
end
else (* nosplit *) begin
// release spec tag
if (x.spec_tag matches tagged Valid .valid_spec_tag) begin
inIfc.correctSpec(valid_spec_tag);
end
// train branch predictor if needed
// since we can only do 1 training in a cycle, split the rule
// XXX not training JAL, reduce chance of conflicts
if(x.iType == Jr || x.iType == Br) begin
inIfc.fetch_train_predictors(FetchTrainBP {
pc: x.controlFlow.pc,
nextPc: x.controlFlow.nextPc,
iType: x.iType,
taken: x.controlFlow.taken,
dpTrain: x.dpTrain,
mispred: False
});
end
end
endrule
interface recvBypass = map(getRecvBypassIfc, bypassWire);
interface rsAluIfc = rsAlu;
interface specUpdate = joinSpeculationUpdate(vec(
rsAlu.specUpdate,
dispToRegQ.specUpdate,
regToExeQ.specUpdate,
exeToFinQ.specUpdate
));
method Data getPerf(ExeStagePerfType t);
return (case(t)
`ifdef PERF_COUNT
ExeRedirectBr: exeRedirectBrCnt;
ExeRedirectJr: exeRedirectJrCnt;
ExeRedirectOther: exeRedirectOtherCnt;
`endif
default: 0;
endcase);
endmethod
endmodule

View File

@@ -0,0 +1,756 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
`include "ProcConfig.bsv"
import Vector::*;
import GetPut::*;
import Cntrs::*;
import ConfigReg::*;
import FIFO::*;
import Types::*;
import ProcTypes::*;
import CCTypes::*;
import Performance::*;
import ReorderBuffer::*;
import ReorderBufferSynth::*;
import RenamingTable::*;
import CsrFile::*;
import StoreBuffer::*;
import VerificationPacket::*;
import RenameDebugIF::*;
typedef struct {
// info about the inst blocking at ROB head
Addr pc;
IType iType;
Maybe#(Trap) trap;
RobInstState state;
Bool claimedPhyReg;
Bool ldKilled;
Bool memAccessAtCommit;
Bool lsqAtCommitNotified;
Bool nonMMIOStDone;
Bool epochIncremented;
SpecBits specBits;
// info about LSQ/TLB
Bool stbEmpty;
Bool stqEmpty;
Bool tlbNoPendingReq;
// CSR info: previlige mode
Bit#(2) prv;
// inst count
Data instCount;
} CommitStuck deriving(Bits, Eq, FShow);
interface CommitInput;
// func units
interface ReorderBufferSynth robIfc;
interface RegRenamingTable rtIfc;
interface CsrFile csrfIfc;
// no stores
method Bool stbEmpty;
method Bool stqEmpty;
// notify LSQ that inst has reached commit
interface Vector#(SupSize, Put#(LdStQTag)) lsqSetAtCommit;
// TLB has stopped processing now
method Bool tlbNoPendingReq;
// set flags
method Action setFlushTlbs;
method Action setUpdateVMInfo;
method Action setFlushReservation;
method Action setFlushBrPred; // security
method Action setFlushCaches; // security
method Action setReconcileI; // recocile I$
method Action setReconcileD; // recocile D$
// redirect
method Action killAll;
method Action redirectPc(Addr trap_pc);
method Action setFetchWaitRedirect;
method Action incrementEpoch;
// record if we commit a CSR inst or interrupt
method Action commitCsrInstOrInterrupt;
// performance
method Bool doStats;
// deadlock check
method Bool checkDeadlock;
endinterface
typedef struct {
RenameError err;
Addr pc;
IType iType;
Maybe#(Trap) trap;
SpecBits specBits;
} RenameErrInfo deriving(Bits, Eq, FShow);
interface CommitStage;
// performance
method Data getPerf(ComStagePerfType t);
// deadlock check
interface Get#(CommitStuck) commitInstStuck;
interface Get#(CommitStuck) commitUserInstStuck;
// rename debug
method Action startRenameDebug;
interface Get#(RenameErrInfo) renameErr;
endinterface
// we apply actions the end of commit rule
// use struct to record actions to be done
typedef struct {
Addr pc;
Addr addr;
Trap trap;
} CommitTrap deriving(Bits, Eq, FShow);
module mkCommitStage#(CommitInput inIfc)(CommitStage);
Bool verbose = True;
// func units
ReorderBufferSynth rob = inIfc.robIfc;
RegRenamingTable regRenamingTable = inIfc.rtIfc;
CsrFile csrf = inIfc.csrfIfc;
// FIXME FIXME FIXME wires to set atCommit in LSQ: avoid scheduling cycle.
// Using wire should be fine, because LSQ does not need to see atCommit
// signal immediately. XXX The concern is about killAll which checks
// atCommit in LSQ, but we never call killAll and setAtCommit in the same
// cycle.
Vector#(SupSize, RWire#(LdStQTag)) setLSQAtCommit <- replicateM(mkRWire);
for(Integer i = 0; i< valueof(SupSize); i = i+1) begin
(* fire_when_enabled, no_implicit_conditions *)
rule doSetLSQAtCommit(setLSQAtCommit[i].wget matches tagged Valid .tag);
inIfc.lsqSetAtCommit[i].put(tag);
endrule
end
// commit stage performance counters
`ifdef PERF_COUNT
// inst
Count#(Data) instCnt <- mkCount(0);
Count#(Data) userInstCnt <- mkCount(0);
Count#(Data) supComUserCnt <- mkCount(0);
// branch/jump inst
Count#(Data) comBrCnt <- mkCount(0);
Count#(Data) comJmpCnt <- mkCount(0);
Count#(Data) comJrCnt <- mkCount(0);
// mem inst
Count#(Data) comLdCnt <- mkCount(0);
Count#(Data) comStCnt <- mkCount(0);
Count#(Data) comLrCnt <- mkCount(0);
Count#(Data) comScCnt <- mkCount(0);
Count#(Data) comAmoCnt <- mkCount(0);
// load mispeculation
Count#(Data) comLdKillByLdCnt <- mkCount(0);
Count#(Data) comLdKillByStCnt <- mkCount(0);
Count#(Data) comLdKillByCacheCnt <- mkCount(0);
// exception/sys inst related
Count#(Data) comSysCnt <- mkCount(0);
Count#(Data) excepCnt <- mkCount(0);
Count#(Data) interruptCnt <- mkCount(0);
// flush tlb
Count#(Data) flushTlbCnt <- mkCount(0);
// flush security
Count#(Data) flushSecurityCnt <- mkCount(0);
Count#(Data) flushBPCnt <- mkCount(0);
Count#(Data) flushCacheCnt <- mkCount(0);
`endif
// deadlock check
`ifdef CHECK_DEADLOCK
// timer to check deadlock
Reg#(DeadlockTimer) commitInstTimer <- mkReg(0);
Reg#(DeadlockTimer) commitUserInstTimer <- mkReg(0);
// FIFOs to output deadlock info
FIFO#(CommitStuck) commitInstStuckQ <- mkFIFO1;
FIFO#(CommitStuck) commitUserInstStuckQ <- mkFIFO1;
// wires to indicate that deadlock is reported, so reset timers
PulseWire commitInstStuckSent <- mkPulseWire;
PulseWire commitUserInstStuckSent <- mkPulseWire;
// wires to reset timers since processor is making progress
PulseWire commitInst <- mkPulseWire;
PulseWire commitUserInst <- mkPulseWire;
function CommitStuck commitStuck;
let x = rob.deqPort[0].deq_data;
return CommitStuck {
pc: x.pc,
iType: x.iType,
trap: x.trap,
state: x.rob_inst_state,
claimedPhyReg: x.claimed_phy_reg,
ldKilled: isValid(x.ldKilled),
memAccessAtCommit: x.memAccessAtCommit,
lsqAtCommitNotified: x.lsqAtCommitNotified,
nonMMIOStDone: x.nonMMIOStDone,
epochIncremented: x.epochIncremented,
specBits: x.spec_bits,
stbEmpty: inIfc.stbEmpty,
stqEmpty: inIfc.stqEmpty,
tlbNoPendingReq: inIfc.tlbNoPendingReq,
prv: csrf.decodeInfo.prv,
instCount: csrf.rd(CSRinstret)
};
endfunction
(* fire_when_enabled *)
rule checkDeadlock_commitInst(inIfc.checkDeadlock && commitInstTimer == maxBound);
commitInstStuckQ.enq(commitStuck);
commitInstStuckSent.send;
endrule
(* fire_when_enabled *)
rule checkDeadlock_commitUserInst(inIfc.checkDeadlock && commitUserInstTimer == maxBound);
commitUserInstStuckQ.enq(commitStuck);
commitUserInstStuckSent.send;
endrule
(* fire_when_enabled, no_implicit_conditions *)
rule incrDeadlockTimer(inIfc.checkDeadlock);
function DeadlockTimer getNextTimer(DeadlockTimer t);
return t == maxBound ? maxBound : t + 1;
endfunction
commitInstTimer <= (commitInst || commitInstStuckSent) ? 0 : getNextTimer(commitInstTimer);
commitUserInstTimer <= (commitUserInst || commitUserInstStuckSent) ? 0 : getNextTimer(commitUserInstTimer);
endrule
`endif
`ifdef RENAME_DEBUG
// rename debug
Reg#(Bool) renameDebugStarted <- mkConfigReg(False);
Reg#(Maybe#(RenameErrInfo)) renameErrInfo <- mkConfigReg(Invalid);
Bool canSetRenameErr = renameDebugStarted && renameErrInfo == Invalid; // only set err info once
// only send 1 error msg
FIFO#(RenameErrInfo) renameErrQ <- mkFIFO1;
rule sendRenameErr(renameDebugStarted &&& renameErrInfo matches tagged Valid .info);
renameErrQ.enq(info);
renameDebugStarted <= False;
endrule
`endif
// we commit trap in two cycles: first cycle deq ROB and flush; second
// cycle handles trap, redirect and handles system consistency
Reg#(Maybe#(CommitTrap)) commitTrap <- mkReg(Invalid); // saves new pc here
// maintain system consistency when system state (CSR) changes or for security
function Action makeSystemConsistent(Bool flushTlb,
Bool flushSecurity,
Bool reconcileI);
action
`ifndef SECURITY
flushSecurity = False;
`endif
`ifndef DISABLE_SECURE_FLUSH_TLB
if(flushTlb || flushSecurity) begin
`else
if(flushTlb) begin
`endif
inIfc.setFlushTlbs;
`ifdef PERF_COUNT
if(inIfc.doStats) begin
flushTlbCnt.incr(1);
end
`endif
end
// notify TLB to keep update of CSR changes
inIfc.setUpdateVMInfo;
// always wait store buffer and SQ to be empty
when(inIfc.stbEmpty && inIfc.stqEmpty, noAction);
// We wait TLB to finish all requests and become sync with memory.
// Notice that currently TLB is read only, so TLB is always in sync
// with memory (i.e., there is no write to commit to memory). Since all
// insts have been killed, nothing can be issued to D TLB at this time.
// Since fetch stage is set to wait for redirect, fetch1 stage is
// stalled, and nothing can be issued to I TLB at this time.
// Therefore, we just need to make sure that I and D TLBs are not
// handling any miss req. Besides, when I and D TLBs do not have any
// miss req, L2 TLB must be idling.
when(inIfc.tlbNoPendingReq, noAction);
// yield load reservation in cache
inIfc.setFlushReservation;
// flush for security, we can delay the stall for fetch-empty and
// wrong-path-load-empty until we really do the flush. This delay is
// valid because these wrong path inst/req will not interfere with
// whatever CSR changes we are making now.
if(flushSecurity) begin
`ifdef PERF_COUNT
if(inIfc.doStats) begin
flushSecurityCnt.incr(1);
end
`endif
`ifndef DISABLE_SECURE_FLUSH_BP
inIfc.setFlushBrPred;
`ifdef PERF_COUNT
if(inIfc.doStats) begin
flushBPCnt.incr(1);
end
`endif
`endif
`ifndef DISABLE_SECURE_FLUSH_CACHE
inIfc.setFlushCaches;
`ifdef PERF_COUNT
if(inIfc.doStats) begin
flushCacheCnt.incr(1);
end
`endif
`endif
end
`ifdef SELF_INV_CACHE
// reconcile I$
if(reconcileI) begin
inIfc.setReconcileI;
end
`ifdef SYSTEM_SELF_INV_L1D
// FIXME is this reconcile of D$ necessary?
inIfc.setReconcileD;
`endif
`endif
endaction
endfunction
// TODO Currently we don't check spec bits == 0 when we commit an
// instruction. This is because killings of wrong path instructions are
// done in a single cycle. However, when we make killings distributed or
// pipelined, then we need to check spec bits at commit port.
rule doCommitTrap_flush(
!isValid(commitTrap) &&&
rob.deqPort[0].deq_data.trap matches tagged Valid .trap
);
rob.deqPort[0].deq;
let x = rob.deqPort[0].deq_data;
if(verbose) $display("[doCommitTrap] ", fshow(x));
// record trap info
Addr vaddr = ?;
if(x.ppc_vaddr_csrData matches tagged VAddr .va) begin
vaddr = va;
end
commitTrap <= Valid (CommitTrap {
trap: trap,
pc: x.pc,
addr: vaddr
});
// flush everything. Only increment epoch and stall fetch when we haven
// not done it yet (we may have already done them at rename stage)
inIfc.killAll;
if(!x.epochIncremented) begin
inIfc.incrementEpoch;
inIfc.setFetchWaitRedirect;
end
// faulting mem inst may have claimed phy reg, we should not commit it;
// instead, we kill the renaming by calling killAll
`ifdef PERF_COUNT
// performance counter
if(inIfc.doStats) begin
if(trap matches tagged Exception .e) begin
excepCnt.incr(1);
end
else begin
interruptCnt.incr(1);
end
end
`endif
// checks
doAssert(x.rob_inst_state == Executed, "must be executed");
doAssert(x.spec_bits == 0, "cannot have spec bits");
endrule
rule doCommitTrap_handle(commitTrap matches tagged Valid .trap);
// reset commitTrap
commitTrap <= Invalid;
// notify commit of interrupt (so MMIO pRq may be handled)
if(trap.trap matches tagged Interrupt .inter) begin
inIfc.commitCsrInstOrInterrupt;
end
// trap handling & redirect
let new_pc <- csrf.trap(trap.trap, trap.pc, trap.addr);
inIfc.redirectPc(new_pc);
// system consistency
// TODO spike flushes TLB here, but perhaps it is because spike's TLB
// does not include prv info, and it has to flush when prv changes.
// XXX As approximation, Trap may cause context switch, so flush for
// security
makeSystemConsistent(False, True, False);
endrule
// commit misspeculated load
rule doCommitKilledLd(
!isValid(commitTrap) &&&
!isValid(rob.deqPort[0].deq_data.trap) &&&
rob.deqPort[0].deq_data.ldKilled matches tagged Valid .killBy
);
rob.deqPort[0].deq;
let x = rob.deqPort[0].deq_data;
if(verbose) $display("[doCommitKilledLd] ", fshow(x));
// kill everything, redirect, and increment epoch
inIfc.killAll;
inIfc.redirectPc(x.pc);
inIfc.incrementEpoch;
// the killed Ld should have claimed phy reg, we should not commit it;
// instead, we have kill the renaming by calling killAll
`ifdef PERF_COUNT
if(inIfc.doStats) begin
case(killBy)
Ld: comLdKillByLdCnt.incr(1);
St: comLdKillByStCnt.incr(1);
Cache: comLdKillByCacheCnt.incr(1);
endcase
end
`endif
// checks
doAssert(!x.epochIncremented, "cannot increment epoch before");
doAssert(x.rob_inst_state == Executed, "must be executed");
doAssert(x.spec_bits == 0, "cannot have spec bits");
endrule
// commit system inst
rule doCommitSystemInst(
!isValid(commitTrap) &&
!isValid(rob.deqPort[0].deq_data.trap) &&
!isValid(rob.deqPort[0].deq_data.ldKilled) &&
rob.deqPort[0].deq_data.rob_inst_state == Executed &&
isSystem(rob.deqPort[0].deq_data.iType)
);
rob.deqPort[0].deq;
let x = rob.deqPort[0].deq_data;
if(verbose) $display("[doCommitSystemInst] ", fshow(x));
// we claim a phy reg for every inst, so commit its renaming
regRenamingTable.commit[0].commit;
Bool write_satp = False; // flush tlb when satp csr is modified
Bool flush_security = False; // flush for security when the flush csr is written
if(x.iType == Csr) begin
// notify commit of CSR (so MMIO pRq may be handled)
inIfc.commitCsrInstOrInterrupt;
// write CSR
let csr_idx = validValue(x.csr);
Data csr_data = ?;
if(x.ppc_vaddr_csrData matches tagged CSRData .d) begin
csr_data = d;
end
else begin
doAssert(False, "must have csr data");
end
csrf.csrInstWr(csr_idx, csr_data);
// check if satp is modified or not
write_satp = csr_idx == CSRsatp;
`ifdef SECURITY
flush_security = csr_idx == CSRmflush;
`endif
end
// redirect (Sret and Mret redirect pc is got from CSRF)
Addr next_pc = x.ppc_vaddr_csrData matches tagged PPC .ppc ? ppc : (x.pc + 4);
doAssert(next_pc == x.pc + 4, "ppc must be pc + 4");
if(x.iType == Sret) begin
next_pc <- csrf.sret;
end
else if(x.iType == Mret) begin
next_pc <- csrf.mret;
end
inIfc.redirectPc(next_pc);
// rename stage only sends out system inst when ROB is empty, so no
// need to flush ROB again
// system consistency
// flush TLB for SFence.VMA and when SATP CSR is modified
// XXX as approximation, sret/mret may mean context switch, so flush
// for security
makeSystemConsistent(
x.iType == SFence || write_satp, // TODO flush TLB when change sanctum regs?
flush_security || x.iType == Sret || x.iType == Mret,
x.iType == FenceI // reconcile I$ for fence.i
);
// incr inst cnt
csrf.incInstret(1);
`ifdef PERF_COUNT
if(inIfc.doStats) begin
comSysCnt.incr(1);
// inst count stats
instCnt.incr(1);
if(csrf.decodeInfo.prv == 0) begin
userInstCnt.incr(1);
end
end
`endif
`ifdef CHECK_DEADLOCK
commitInst.send;
if(csrf.decodeInfo.prv == 0) begin
commitUserInst.send;
end
`endif
// checks
doAssert(x.epochIncremented, "must have already incremented epoch");
doAssert((x.iType == Csr) == isValid(x.csr), "only CSR has valid csr idx");
doAssert(x.fflags == 0 && !x.will_dirty_fpu_state, "cannot dirty FPU");
doAssert(x.spec_bits == 0, "cannot have spec bits");
doAssert(x.claimed_phy_reg, "must have claimed phy reg");
`ifdef RENAME_DEBUG
if(!x.claimed_phy_reg && canSetRenameErr) begin
renameErrInfo <= Valid (RenameErrInfo {
err: NonTrapCommitLackClaim,
pc: x.pc,
iType: x.iType,
trap: x.trap,
specBits: x.spec_bits
});
end
`endif
endrule
// Lr/Sc/Amo/MMIO cannot proceed to executed until we notify LSQ that it
// has reached the commit stage
rule notifyLSQCommit(
!isValid(commitTrap) &&
!isValid(rob.deqPort[0].deq_data.trap) &&
!isValid(rob.deqPort[0].deq_data.ldKilled) &&
rob.deqPort[0].deq_data.rob_inst_state != Executed &&
rob.deqPort[0].deq_data.memAccessAtCommit &&
!rob.deqPort[0].deq_data.lsqAtCommitNotified
);
let x = rob.deqPort[0].deq_data;
let inst_tag = rob.deqPort[0].getDeqInstTag;
if(verbose) $display("[notifyLSQCommit] ", fshow(x), "; ", fshow(inst_tag));
// notify LSQ, and record in ROB that notification is done
setLSQAtCommit[0].wset(x.lsqTag);
rob.setLSQAtCommitNotified(inst_tag);
endrule
// commit normal: fire when at least one commit can be done
rule doCommitNormalInst(
!isValid(commitTrap) &&
!isValid(rob.deqPort[0].deq_data.trap) &&
!isValid(rob.deqPort[0].deq_data.ldKilled) &&
rob.deqPort[0].deq_data.rob_inst_state == Executed &&
!isSystem(rob.deqPort[0].deq_data.iType)
);
// stop superscalar commit after we
// 1. see a trap or system inst or killed Ld
// 2. inst is not ready to commit
Bool stop = False;
// We merge writes on FPU csr and apply writes at the end of the rule
Bit#(5) fflags = 0;
Bool will_dirty_fpu_state = False;
// rename error
Maybe#(RenameErrInfo) renameError = Invalid;
// incr committed inst cnt at the end of rule
SupCnt comInstCnt = 0;
SupCnt comUserInstCnt = 0;
`ifdef PERF_COUNT
// incr some performance counter at the end of rule
SupCnt brCnt = 0;
SupCnt jmpCnt = 0;
SupCnt jrCnt = 0;
SupCnt ldCnt = 0;
SupCnt stCnt = 0;
SupCnt lrCnt = 0;
SupCnt scCnt = 0;
SupCnt amoCnt = 0;
`endif
// compute what actions to take
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
if(!stop && rob.deqPort[i].canDeq) begin
let x = rob.deqPort[i].deq_data;
let inst_tag = rob.deqPort[i].getDeqInstTag;
// check can be committed or not
if(x.rob_inst_state != Executed || isValid(x.ldKilled) || isValid(x.trap) || isSystem(x.iType)) begin
// inst not ready for commit, or system inst, or trap, or killed, stop here
stop = True;
end
else begin
if (verbose) $display("[doCommitNormalInst - %d] ", i, fshow(inst_tag), " ; ", fshow(x));
// inst can be committed, deq it
rob.deqPort[i].deq;
// every inst here should have been renamed, commit renaming
regRenamingTable.commit[i].commit;
doAssert(x.claimed_phy_reg, "should have renamed");
`ifdef RENAME_DEBUG
// send debug msg for rename error
if(!x.claimed_phy_reg && !isValid(renameError)) begin
renameError = Valid (RenameErrInfo {
err: NonTrapCommitLackClaim,
pc: x.pc,
iType: x.iType,
trap: x.trap,
specBits: x.spec_bits
});
end
`endif
// cumulate writes to FPU csr
fflags = fflags | x.fflags;
will_dirty_fpu_state = will_dirty_fpu_state || x.will_dirty_fpu_state;
// for non-mmio st, notify SQ that store is committed
if(x.nonMMIOStDone) begin
setLSQAtCommit[i].wset(x.lsqTag);
end
// inst commit counter
comInstCnt = comInstCnt + 1;
if(csrf.decodeInfo.prv == 0) begin
comUserInstCnt = comUserInstCnt + 1; // user space inst
end
`ifdef PERF_COUNT
// performance counter
case(x.iType)
Br: brCnt = brCnt + 1;
J : jmpCnt = jmpCnt + 1;
Jr: jrCnt = jrCnt + 1;
Ld: ldCnt = ldCnt + 1;
St: stCnt = stCnt + 1;
Lr: lrCnt = lrCnt + 1;
Sc: scCnt = scCnt + 1;
Amo: amoCnt = amoCnt + 1;
endcase
`endif
end
end
end
// write FPU csr
if(csrf.fpuInstNeedWr(fflags, will_dirty_fpu_state)) begin
csrf.fpuInstWr(fflags);
end
// incr inst cnt
csrf.incInstret(comInstCnt);
`ifdef RENAME_DEBUG
// set rename error
if(canSetRenameErr && isValid(renameError)) begin
renameErrInfo <= renameError;
end
`endif
`ifdef CHECK_DEADLOCK
commitInst.send; // ROB head is removed
if(comUserInstCnt > 0) begin
commitUserInst.send;
end
`endif
`ifdef PERF_COUNT
// performance counter
if(inIfc.doStats) begin
// branch stats
comBrCnt.incr(zeroExtend(brCnt));
comJmpCnt.incr(zeroExtend(jmpCnt));
comJrCnt.incr(zeroExtend(jrCnt));
// mem stats
comLdCnt.incr(zeroExtend(ldCnt));
comStCnt.incr(zeroExtend(stCnt));
comLrCnt.incr(zeroExtend(lrCnt));
comScCnt.incr(zeroExtend(scCnt));
comAmoCnt.incr(zeroExtend(amoCnt));
// inst count stats
instCnt.incr(zeroExtend(comInstCnt));
userInstCnt.incr(zeroExtend(comUserInstCnt));
if(comUserInstCnt > 1) begin
supComUserCnt.incr(1);
end
end
`endif
endrule
method Data getPerf(ComStagePerfType t);
return (case(t)
`ifdef PERF_COUNT
InstCnt: instCnt;
UserInstCnt: userInstCnt;
SupComUserCnt: supComUserCnt;
ComBrCnt: comBrCnt;
ComJmpCnt: comJmpCnt;
ComJrCnt: comJrCnt;
ComLdCnt: comLdCnt;
ComStCnt: comStCnt;
ComLrCnt: comLrCnt;
ComScCnt: comScCnt;
ComAmoCnt: comAmoCnt;
ComLdKillByLd: comLdKillByLdCnt;
ComLdKillBySt: comLdKillByStCnt;
ComLdKillByCache: comLdKillByCacheCnt;
ComSysCnt: comSysCnt;
ExcepCnt: excepCnt;
InterruptCnt: interruptCnt;
FlushTlbCnt: flushTlbCnt;
FlushSecurityCnt: flushSecurityCnt;
FlushBPCnt: flushBPCnt;
FlushCacheCnt: flushCacheCnt;
`endif
default: 0;
endcase);
endmethod
`ifdef CHECK_DEADLOCK
interface commitInstStuck = toGet(commitInstStuckQ);
interface commitUserInstStuck = toGet(commitUserInstStuckQ);
`else
interface commitInstStuck = nullGet;
interface commitUserInstStuck = nullGet;
`endif
`ifdef RENAME_DEBUG
method Action startRenameDebug if(!renameDebugStarted);
renameDebugStarted <= True;
endmethod
interface renameErr = toGet(renameErrQ);
`else
method Action startRenameDebug;
noAction;
endmethod
interface renameErr = nullGet;
`endif
endmodule

View File

@@ -0,0 +1,641 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
`include "ProcConfig.bsv"
import BrPred::*;
import DirPredictor::*;
import Btb::*;
import ClientServer::*;
import Connectable::*;
import Decode::*;
import Ehr::*;
import Fifo::*;
import GetPut::*;
import MemoryTypes::*;
import Types::*;
import ProcTypes::*;
import CCTypes::*;
import Ras::*;
import EpochManager::*;
import Performance::*;
import Vector::*;
import Assert::*;
import Cntrs::*;
import ConfigReg::*;
import TlbTypes::*;
import ITlb::*;
import CCTypes::*;
import L1CoCache::*;
import MMIOInst::*;
interface FetchStage;
// pipeline
interface Vector#(SupSize, SupFifoDeq#(FromFetchStage)) pipelines;
// tlb and mem connections
interface ITlb iTlbIfc;
interface ICoCache iMemIfc;
interface MMIOInstToCore mmioIfc;
// starting and stopping
method Action start(Addr pc);
method Action stop();
// redirection methods
method Action setWaitRedirect;
method Action redirect(Addr pc);
method Action done_flushing();
method Action train_predictors(
Addr pc, Addr next_pc, IType iType, Bool taken,
DirPredTrainInfo dpTrain, Bool mispred
);
// security
method Bool emptyForFlush;
method Action flush_predictors;
method Bool flush_predictors_done;
// debug
method FetchDebugState getFetchState;
// performance
interface Perf#(DecStagePerfType) perf;
endinterface
typedef struct {
Addr pc;
Epoch mainEp;
Bool waitForRedirect;
Bool waitForFlush;
} FetchDebugState deriving(Bits, Eq, FShow);
typedef struct {
Addr pc;
Addr pred_next_pc;
Bool decode_epoch;
Epoch main_epoch;
} Fetch1ToFetch2 deriving(Bits, Eq, FShow);
typedef struct {
Addr pc;
Addr phys_pc;
Addr pred_next_pc;
Maybe#(Exception) cause;
Bool access_mmio; // inst fetch from MMIO
Bool decode_epoch;
Epoch main_epoch;
} Fetch2ToFetch3 deriving(Bits, Eq, FShow);
typedef struct {
Addr pc;
Addr ppc;
Bool decode_epoch;
Epoch main_epoch;
Instruction inst;
Maybe#(Exception) cause;
} Fetch3ToDecode deriving(Bits, Eq, FShow);
typedef struct {
Addr pc;
Addr ppc;
Epoch main_epoch;
DirPredTrainInfo dpTrain;
Instruction inst;
DecodedInst dInst;
ArchRegs regs;
Maybe#(Exception) cause;
} FromFetchStage deriving (Bits, Eq, FShow);
// train next addr pred (BTB)
typedef struct {
Addr pc;
Addr nextPc;
} TrainNAP deriving(Bits, Eq, FShow);
(* synthesize *)
module mkFetchStage(FetchStage);
// rule ordering: Fetch1 (BTB+TLB) < Fetch3 (decode & dir pred) < redirect method
// Fetch1 < Fetch3 to avoid bypassing path on PC and epochs
let verbose = True;
// Basic State Elements
Reg#(Bool) started <- mkReg(False);
// Stall fetch when trap happens or system inst is renamed
// All inst younger than the trap/system inst will be killed
// Since CSR may be modified, sending wrong path request to TLB may cause problem
// So we stall until the next redirection happens
// The next redirect is either by the trap/system inst or an older one
Reg#(Bool) waitForRedirect <- mkReg(False);
// We don't want setWaitForRedirect method and redirect method to happen together
// make them conflict
RWire#(void) setWaitRedirect_redirect_conflict <- mkRWire;
// Stall fetch during the flush triggered by the procesing trap/system inst in commit stage
// We stall until the flush is done
Reg#(Bool) waitForFlush <- mkReg(False);
Ehr#(3, Addr) pc_reg <- mkEhr(0);
Integer pc_fetch1_port = 0;
Integer pc_decode_port = 1;
Integer pc_redirect_port = 2;
// Epochs
Reg#(Bool) decode_epoch <- mkReg(False);
Reg#(Epoch) f_main_epoch <- mkReg(0); // fetch estimate of main epoch
// Pipeline Stage FIFOs
Fifo#(2, Tuple2#(Bit#(TLog#(SupSize)),Fetch1ToFetch2)) f12f2 <- mkCFFifo;
Fifo#(4, Tuple2#(Bit#(TLog#(SupSize)),Fetch2ToFetch3)) f22f3 <- mkCFFifo; // FIFO should match I$ latency
Fifo#(2, Tuple2#(Bit#(TLog#(SupSize)),Fetch2ToFetch3)) f32d <- mkCFFifo;
Fifo#(2, Vector#(SupSize,Maybe#(Instruction))) instdata <- mkPipelineFifo();
SupFifo#(SupSize, 2, FromFetchStage) out_fifo <- mkSupFifo;
// Can the fifo size be smaller?
// Branch Predictors
NextAddrPred nextAddrPred <- mkBtb;
let dirPred <- mkDirPredictor;
ReturnAddrStack ras <- mkRas;
// Wire to train next addr pred (NAP)
RWire#(TrainNAP) napTrainByExe <- mkRWire;
RWire#(TrainNAP) napTrainByDec <- mkRWire;
Fifo#(1, TrainNAP) napTrainByDecQ <- mkPipelineFifo; // cut off critical path
// TLB and Cache connections
ITlb iTlb <- mkITlb;
ICoCache iMem <- mkICoCache;
MMIOInst mmio <- mkMMIOInst;
Server#(Addr, TlbResp) tlb_server = iTlb.to_proc;
Server#(Addr, Vector#(SupSize, Maybe#(Instruction))) mem_server = iMem.to_proc;
// performance counters
Fifo#(1, DecStagePerfType) perfReqQ <- mkCFFifo; // perf req FIFO
`ifdef PERF_COUNT
Reg#(Bool) doStats <- mkConfigReg(False);
// decode stage redirect
Count#(Data) decRedirectBrCnt <- mkCount(0);
Count#(Data) decRedirectJmpCnt <- mkCount(0);
Count#(Data) decRedirectJrCnt <- mkCount(0);
Count#(Data) decRedirectOtherCnt <- mkCount(0);
// perf resp FIFO
Fifo#(1, PerfResp#(DecStagePerfType)) perfRespQ <- mkCFFifo;
rule doPerfReq;
let t <- toGet(perfReqQ).get;
Data d = (case(t)
DecRedirectBr: decRedirectBrCnt;
DecRedirectJmp: decRedirectJmpCnt;
DecRedirectJr: decRedirectJrCnt;
DecRedirectOther: decRedirectOtherCnt;
default: 0;
endcase);
perfRespQ.enq(PerfResp {
pType: t,
data: d
});
endrule
`endif
// We don't send req to TLB when waiting for redirect or TLB flush. Since
// there is no FIFO between doFetch1 and TLB, when OOO commit stage wait
// TLB idle to change VM CSR / signal flush TLB, there is no wrong path
// request afterwards to race with the system code that manage paget table.
rule doFetch1(started && !waitForRedirect && !waitForFlush);
let pc = pc_reg[pc_fetch1_port];
// Chain of prediction for the next instructions
// We need a BTB with a register file with enough ports!
// Instead of cascading predictions, we can always feed pc+4*i into
// predictor, because we will break superscaler fetch if nextpc != pc+4
Vector#(SupSize, Addr) pred_future_pc;
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
pred_future_pc[i] = nextAddrPred.predPc(pc + fromInteger(4 * i));
end
// Next pc is the first nextPc that breaks the chain of pc+4 or
// that is at the end of a cacheline.
Vector#(SupSize,Integer) indexes = genVector;
function Bool findNextPc(Addr pc, Integer i);
Bool notLastInst = getLineInstOffset(pc + fromInteger(4*i)) != maxBound;
Bool noJump = pred_future_pc[i] == pc + fromInteger(4*(i+1));
return (!(notLastInst && noJump));
endfunction
Integer posLastSup = fromMaybe(valueof(SupSize) - 1, find(findNextPc(pc), indexes));
Addr pred_next_pc = pred_future_pc[posLastSup];
pc_reg[pc_fetch1_port] <= pred_next_pc;
// Send TLB request
tlb_server.request.put(pc);
let out = Fetch1ToFetch2 {
pc: pc,
pred_next_pc: pred_next_pc,
decode_epoch: decode_epoch,
main_epoch: f_main_epoch};
f12f2.enq(tuple2(fromInteger(posLastSup),out));
if (verbose) $display("Fetch1: ", fshow(out));
endrule
rule doFetch2;
let {nbSup,in} = f12f2.first;
f12f2.deq;
// Get TLB response
match {.phys_pc, .cause} <- tlb_server.response.get;
// Access main mem or boot rom
Bool access_mmio = False;
if (!isValid(cause)) begin
case(mmio.getFetchTarget(phys_pc))
MainMem: begin
// Send ICache request
mem_server.request.put(phys_pc);
end
BootRom: begin
// Send MMIO req. Luckily boot rom is also aligned with
// cache line size, so all nbSup+1 insts can be fetched
// from boot rom. It won't happen that insts fetched from
// boot rom is less than requested.
mmio.bootRomReq(phys_pc, nbSup);
access_mmio = True;
end
default: begin
// Access fault
cause = Valid (InstAccessFault);
end
endcase
end
let out = Fetch2ToFetch3 {
pc: in.pc,
phys_pc: phys_pc,
pred_next_pc: in.pred_next_pc,
cause: cause,
access_mmio: access_mmio,
decode_epoch: in.decode_epoch,
main_epoch: in.main_epoch };
f22f3.enq(tuple2(nbSup,out));
if (verbose) $display("Fetch2: ", fshow(out));
endrule
// Break out of i$
rule doFetch3;
let {nbSup, fetch3In} = f22f3.first;
f22f3.deq();
if (verbose) $display("Fetch3 %d",fetch3In.pc);
// Get ICache/MMIO response if no exception
// In case of exception, we still need to process at least inst_data[0]
// (it will be turned to an exception later), so inst_data[0] must be
// valid.
Vector#(SupSize,Maybe#(Instruction)) inst_d = replicate(tagged Valid (0));
if(!isValid(fetch3In.cause)) begin
if(fetch3In.access_mmio) begin
if(verbose) $display("get answer from MMIO %d", fetch3In.pc);
inst_d <- mmio.bootRomResp;
end
else begin
if(verbose) $display("get answer from memory %d", fetch3In.pc);
inst_d <- mem_server.response.get;
end
end
if(verbose) $display("epoch instr: %d, epoch main : %d", fetch3In.main_epoch, f_main_epoch);
instdata.enq(inst_d);
f32d.enq(f22f3.first);
endrule
rule doDecode;
let {nbSup, fetch3In} = f32d.first;
f32d.deq();
let inst_data = instdata.first();
instdata.deq();
// The main_epoch check is required to make sure this stage doesn't
// redirect the PC if a later stage already redirected the PC.
if (fetch3In.main_epoch == f_main_epoch) begin
Bool decode_epoch_local = decode_epoch; // next value for decode epoch
Maybe#(Addr) redirectPc = Invalid; // next pc redirect by branch predictor
Maybe#(TrainNAP) trainNAP = Invalid; // training data sent to next addr pred
`ifdef PERF_COUNT
// performance counter: inst being redirect by decode stage
// Note that only 1 redirection may happen in a cycle
Maybe#(IType) redirectInst = Invalid;
`endif
for (Integer i = 0; i < valueof(SupSize); i=i+1) begin
if (inst_data[i] != tagged Invalid && fromInteger(i) <= nbSup) begin
// get the input to decode
let in = Fetch3ToDecode {
pc: fetch3In.pc+fromInteger(4*i),
// last inst, next pc may not be pc+4
ppc: fromInteger(i) == nbSup ? fetch3In.pred_next_pc :
fetch3In.pc+fromInteger(4*(i+1)),
decode_epoch: fetch3In.decode_epoch,
main_epoch: fetch3In.main_epoch,
inst: fromMaybe(?,inst_data[i]),
cause: fetch3In.cause
};
let cause = in.cause;
if (verbose) $display("Decode %d\n",i);
// do decode and branch prediction
// Drop here if does not match the decode_epoch.
if (in.decode_epoch == decode_epoch_local) begin
doAssert(in.main_epoch == f_main_epoch, "main epoch must match");
let decode_result = decode(in.inst);
// update cause if there was not an early detected exception
if (!isValid(cause)) begin
cause = decode_result.illegalInst ? tagged Valid IllegalInst : tagged Invalid;
end
let dInst = decode_result.dInst;
let regs = decode_result.regs;
DirPredTrainInfo dp_train = ?; // dir pred training bookkeeping
// update predicted next pc
if (!isValid(cause)) begin
// direction predict
Bool pred_taken = False;
if(dInst.iType == Br) begin
let pred_res <- dirPred.pred[i].pred(in.pc);
pred_taken = pred_res.taken;
dp_train = pred_res.train;
end
Maybe#(Addr) nextPc = decodeBrPred(in.pc, dInst, pred_taken);
// return address stack link reg is x1 or x5
function Bool linkedR(Maybe#(ArchRIndx) register);
Bool res = False;
if (register matches tagged Valid .r &&& (r == tagged Gpr 1 || r == tagged Gpr 5)) begin
res = True;
end
return res;
endfunction
Bool dst_link = linkedR(regs.dst);
Bool src1_link = linkedR(regs.src1);
Addr push_addr = in.pc + 4;
Addr pop_addr = ras.ras[i].first;
if (dInst.iType == J && dst_link) begin
// rs1 is invalid, i.e., not link: push
ras.ras[i].popPush(False, Valid (push_addr));
end
else if (dInst.iType == Jr) begin // jalr
if (!dst_link && src1_link) begin
// rd is link while rs1 is not: pop
nextPc = Valid (pop_addr);
ras.ras[i].popPush(True, Invalid);
end
else if (!src1_link && dst_link) begin
// rs1 is not link while rd is link: push
ras.ras[i].popPush(False, Valid (push_addr));
end
else if (dst_link && src1_link) begin
// both rd and rs1 are links
if (regs.src1 != regs.dst) begin
// not same reg: first pop, then push
nextPc = Valid (pop_addr);
ras.ras[i].popPush(True, Valid (push_addr));
end
else begin
// same reg: push
ras.ras[i].popPush(False, Valid (push_addr));
end
end
end
if(verbose) begin
$display("Branch prediction: ", fshow(dInst.iType), " ; ", fshow(in.pc), " ; ",
fshow(in.ppc), " ; ", fshow(pred_taken), " ; ", fshow(nextPc));
end
// check previous mispred
if (nextPc matches tagged Valid .decode_pred_next_pc &&& decode_pred_next_pc != in.ppc) begin
if (verbose) $display("ppc and decodeppc : %h %h", in.ppc, decode_pred_next_pc);
decode_epoch_local = !decode_epoch_local;
redirectPc = Valid (decode_pred_next_pc); // record redirect next pc
in.ppc = decode_pred_next_pc;
// train next addr pred when mispredict
trainNAP = Valid (TrainNAP {pc: in.pc, nextPc: decode_pred_next_pc});
`ifdef PERF_COUNT
// performance stats: record decode redirect
doAssert(redirectInst == Invalid, "at most 1 decode redirect per cycle");
redirectInst = Valid (dInst.iType);
`endif
end
end
let out = FromFetchStage{pc: in.pc,
ppc: in.ppc,
main_epoch: in.main_epoch,
dpTrain: dp_train,
inst: in.inst,
dInst: dInst,
regs: decode_result.regs,
cause: cause };
out_fifo.enqS[i].enq(out);
if (verbose) $display("Decode: ", fshow(out));
end
else begin
if (verbose) $display("Drop decoded within a superscalar");
// just drop wrong path instructions
end
end
else if (inst_data[i] == tagged Invalid && fromInteger(i) <= nbSup) begin
// inst num is less than expected; this should not happen
// because both I$ and boot rom are aligned to cache line
// size.
doAssert(False, "Fetched insts not enough");
end
end
// update PC and epoch
if(redirectPc matches tagged Valid .nextPc) begin
pc_reg[pc_decode_port] <= nextPc;
end
decode_epoch <= decode_epoch_local;
// send training data for next addr pred
if (trainNAP matches tagged Valid .x) begin
napTrainByDecQ.enq(x);
end
`ifdef PERF_COUNT
// performance counter: check whether redirect happens
if(redirectInst matches tagged Valid .iType &&& doStats) begin
case(iType)
Br: decRedirectBrCnt.incr(1);
J : decRedirectJmpCnt.incr(1);
Jr: decRedirectJrCnt.incr(1);
default: decRedirectOtherCnt.incr(1);
endcase
end
`endif
end
else begin
if (verbose) $display("drop in fetch3decode");
end
endrule
// train next addr pred: we use a wire to catch outputs of napTrainByDecQ.
// This prevents napTrainByDecQ from clogging doDecode rule when
// superscalar size is large
(* fire_when_enabled *)
rule setTrainNAPByDec;
napTrainByDecQ.deq;
napTrainByDec.wset(napTrainByDecQ.first);
endrule
(* fire_when_enabled, no_implicit_conditions *)
rule doTrainNAP(isValid(napTrainByDec.wget) || isValid(napTrainByExe.wget));
// Give priority to train from exe. This is because exe has train data
// only when misprediction happens, i.e., train by dec is already at
// wrong path.
TrainNAP train = fromMaybe(validValue(napTrainByDec.wget), napTrainByExe.wget);
nextAddrPred.update(train.pc, train.nextPc, train.nextPc != train.pc + 4);
endrule
// Security: we can flush when front end is empty, i.e.
// (1) Fetch1 is stalled for waiting flush
// (2) all internal FIFOs are empty (the output sup fifo needs not to be
// empty, but why leave this security hole)
Bool empty_for_flush = waitForFlush &&
!f12f2.notEmpty && !f22f3.notEmpty &&
!f32d.notEmpty && out_fifo.internalEmpty;
interface Vector pipelines = out_fifo.deqS;
interface iTlbIfc = iTlb;
interface iMemIfc = iMem;
interface mmioIfc = mmio.toCore;
method Action start(Addr start_pc);
pc_reg[0] <= start_pc;
started <= True;
waitForRedirect <= False;
waitForFlush <= False;
endmethod
method Action stop();
started <= False;
endmethod
method Action setWaitRedirect;
waitForRedirect <= True;
setWaitRedirect_redirect_conflict.wset(?); // conflict with redirect
endmethod
method Action redirect(Addr new_pc);
if (verbose) $display("Redirect: newpc %h, old f_main_epoch %d, new f_main_epoch %d",new_pc,f_main_epoch,f_main_epoch+1);
pc_reg[pc_redirect_port] <= new_pc;
f_main_epoch <= (f_main_epoch == fromInteger(valueOf(NumEpochs)-1)) ? 0 : f_main_epoch + 1;
// redirect comes, stop stalling for redirect
waitForRedirect <= False;
setWaitRedirect_redirect_conflict.wset(?); // conflict with setWaitForRedirect
// this redirect may be caused by a trap/system inst in commit stage
// we conservatively set wait for flush TODO make this an input parameter
waitForFlush <= True;
endmethod
method Action done_flushing() if (waitForFlush);
// signal that the pipeline can resume fetching
waitForFlush <= False;
// XXX The guard prevents the readyToFetch rule in Core.bsv from firing every cycle
// The guard also makes this method sequence before (restricted) redirect method
// So the effect of setting waitForFlush in redirect method will not be overwritten
// Then we don't need to make two methods conflict
// It's fine for the effect of this method to be overwritten, because it fires very often
endmethod
method Action train_predictors(
Addr pc, Addr next_pc, IType iType, Bool taken,
DirPredTrainInfo dpTrain, Bool mispred
);
//if (iType == J || (iType == Br && next_pc < pc)) begin
// // Only train the next address predictor for jumps and backward branches
// // next_pc != pc + 4 is a substitute for taken
// nextAddrPred.update(pc, next_pc, taken);
//end
if (iType == Br) begin
// Train the direction predictor for all branches
dirPred.update(pc, taken, dpTrain, mispred);
end
// train next addr pred when mispred
if(mispred) begin
napTrainByExe.wset(TrainNAP {pc: pc, nextPc: next_pc});
end
endmethod
// security
method Bool emptyForFlush;
return empty_for_flush;
endmethod
method Action flush_predictors;
nextAddrPred.flush;
dirPred.flush;
ras.flush;
endmethod
method Bool flush_predictors_done;
return nextAddrPred.flush_done && dirPred.flush_done && ras.flush_done;
endmethod
method FetchDebugState getFetchState;
return FetchDebugState {
pc: pc_reg[0],
waitForRedirect: waitForRedirect,
waitForFlush: waitForFlush,
mainEp: f_main_epoch
};
endmethod
interface Perf perf;
method Action setStatus(Bool stats);
`ifdef PERF_COUNT
doStats <= stats;
`else
noAction;
`endif
endmethod
method Action req(DecStagePerfType r);
perfReqQ.enq(r);
endmethod
method ActionValue#(PerfResp#(DecStagePerfType)) resp;
`ifdef PERF_COUNT
perfRespQ.deq;
return perfRespQ.first;
`else
perfReqQ.deq;
return PerfResp {
pType: perfReqQ.first,
data: 0
};
`endif
endmethod
`ifdef PERF_COUNT
method Bool respValid = perfRespQ.notEmpty;
`else
method Bool respValid = perfReqQ.notEmpty;
`endif
endinterface
endmodule

View File

@@ -0,0 +1,325 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
`include "ProcConfig.bsv"
import DefaultValue::*;
import Cntrs::*;
import Vector::*;
import BuildVector::*;
import Types::*;
import ProcTypes::*;
import SynthParam::*;
import Exec::*;
import Performance::*;
import ReservationStationEhr::*;
import ReservationStationFpuMulDiv::*;
import ReorderBuffer::*;
import HasSpecBits::*;
import SpecFifo::*;
import MulDiv::*;
import Fpu::*;
import Bypass::*;
typedef struct {
// inst info
ExecFunc execFunc;
PhyRegs regs;
InstTag tag;
// FpuMulDiv must not have valid spec tag
} FpuMulDivDispatchToRegRead deriving(Bits, Eq, FShow);
typedef struct {
// inst info
ExecFunc execFunc;
Maybe#(PhyDst) dst;
InstTag tag;
// src reg vals
Data rVal1;
Data rVal2;
Data rVal3;
} FpuMulDivRegReadToExe deriving(Bits, Eq, FShow);
typedef struct {
// inst info
ExecFunc execFunc;
Maybe#(PhyDst) dst;
InstTag tag;
} FpuMulDivExeToFinish deriving(Bits, Eq, FShow);
// synthesized pipeline fifos
typedef SpecFifo_SB_deq_enq_C_deq_enq#(1, FpuMulDivDispatchToRegRead) FpuMulDivDispToRegFifo;
(* synthesize *)
module mkFpuMulDivDispToRegFifo(FpuMulDivDispToRegFifo);
let m <- mkSpecFifo_SB_deq_enq_C_deq_enq(False);
return m;
endmodule
typedef SpecFifo_SB_deq_enq_C_deq_enq#(1, FpuMulDivRegReadToExe) FpuMulDivRegToExeFifo;
(* synthesize *)
module mkFpuMulDivRegToExeFifo(FpuMulDivRegToExeFifo);
let m <- mkSpecFifo_SB_deq_enq_C_deq_enq(False);
return m;
endmodule
interface FpuMulDivExeInput;
// conservative scoreboard check in reg read stage
method RegsReady sbCons_lazyLookup(PhyRegs r);
// Phys reg file
method Data rf_rd1(PhyRIndx rindx);
method Data rf_rd2(PhyRIndx rindx);
method Data rf_rd3(PhyRIndx rindx);
// CSR file
method Data csrf_rd(CSR csr);
// ROB
method Action rob_setExecuted(InstTag t, Bit#(5) fflags);
// global broadcast methods
// write reg file & set both conservative and aggressive sb & wake up inst
method Action writeRegFile(PhyRIndx dst, Data data);
// spec update
method Action conflictWrongSpec;
// performance
method Bool doStats;
endinterface
interface FpuMulDivExePipeline;
// recv bypass from the ALU exe and finish stages
interface Vector#(TMul#(2, AluExeNum), RecvBypass) recvBypass;
interface ReservationStationFpuMulDiv rsFpuMulDivIfc;
interface SpeculationUpdate specUpdate;
// performance
method Data getPerf(ExeStagePerfType t);
endinterface
module mkFpuMulDivExePipeline#(FpuMulDivExeInput inIfc)(FpuMulDivExePipeline);
Bool verbose = True;
// fpu mul div reservation station
ReservationStationFpuMulDiv rsFpuMulDiv <- mkReservationStationFpuMulDiv;
// pipeline fifos
let dispToRegQ <- mkFpuMulDivDispToRegFifo;
let regToExeQ <- mkFpuMulDivRegToExeFifo;
// wire to recv bypass
Vector#(TMul#(2, AluExeNum), RWire#(Tuple2#(PhyRIndx, Data))) bypassWire <- replicateM(mkRWire);
// mul div fpu func units
MulDivExec mulDivExec <- mkMulDivExec;
FpuExec fpuExec <- mkFpuExecPipeline;
// fpu/mul/div performance counters
`ifdef PERF_COUNT
Count#(Data) exeIntMulCnt <- mkCount(0);
Count#(Data) exeIntDivCnt <- mkCount(0);
Count#(Data) exeFpFmaCnt <- mkCount(0);
Count#(Data) exeFpDivCnt <- mkCount(0);
Count#(Data) exeFpSqrtCnt <- mkCount(0);
`endif
rule doDispatchFpuMulDiv;
rsFpuMulDiv.doDispatch;
let x = rsFpuMulDiv.dispatchData;
if(verbose) $display("[doDispatchFpuMulDiv] ", fshow(x));
// FPU MUL DIV never have exception or misprecition, so no spec tag
doAssert(!isValid(x.spec_tag), "FpuMulDiv should not carry any spec tag");
// go to next stage
dispToRegQ.enq(ToSpecFifo {
data: FpuMulDivDispatchToRegRead {
execFunc: x.data.execFunc,
regs: x.regs,
tag: x.tag
},
spec_bits: x.spec_bits
});
endrule
rule doRegReadFpuMulDiv;
dispToRegQ.deq;
let dispToReg = dispToRegQ.first;
let x = dispToReg.data;
if(verbose) $display("[doRegReadFpuMulDiv] ", fshow(dispToReg));
// check conservative scoreboard
let regsReady = inIfc.sbCons_lazyLookup(x.regs);
// get rVal1 (check bypass)
Data rVal1 = ?;
if(x.regs.src1 matches tagged Valid .src1) begin
rVal1 <- readRFBypass(src1, regsReady.src1, inIfc.rf_rd1(src1), bypassWire);
end
// get rVal2 (check bypass)
Data rVal2 = ?;
if(x.regs.src2 matches tagged Valid .src2) begin
rVal2 <- readRFBypass(src2, regsReady.src2, inIfc.rf_rd2(src2), bypassWire);
end
// get rVal3 (check bypass)
Data rVal3 = ?;
if(x.regs.src3 matches tagged Valid .src3) begin
rVal3 <- readRFBypass(src3, regsReady.src3, inIfc.rf_rd3(src3), bypassWire);
end
// go to next stage
regToExeQ.enq(ToSpecFifo {
data: FpuMulDivRegReadToExe {
execFunc: x.execFunc,
dst: x.regs.dst,
tag: x.tag,
rVal1: rVal1,
rVal2: rVal2,
rVal3: rVal3
},
spec_bits: dispToReg.spec_bits
});
endrule
rule doExeFpuMulDiv;
regToExeQ.deq;
let regToExe = regToExeQ.first;
let x = regToExe.data;
let spec_bits = regToExe.spec_bits;
if(verbose) $display("[doExeFpuMulDiv] ", fshow(regToExe));
// send to exe unit
Data rVal1 = x.rVal1;
Data rVal2 = x.rVal2;
Data rVal3 = x.rVal3;
case (x.execFunc) matches
tagged Fpu .fpu_inst: begin
fpuExec.exec(fpu_inst, rVal1, rVal2, rVal3, x.dst, x.tag, spec_bits);
end
tagged MulDiv .muldiv_inst: begin
mulDivExec.exec(muldiv_inst, rVal1, rVal2, x.dst, x.tag, spec_bits);
end
default: begin
doAssert(False, "unknown execFunc for doExeFpuMulDiv");
end
endcase
endrule
function Action doFinish(Maybe#(PhyDst) dst, InstTag tag, Data data, Bit#(5) fflags);
action
// write to register file
if(dst matches tagged Valid .valid_dst) begin
inIfc.writeRegFile(valid_dst.indx, data);
end
// update the instruction in the reorder buffer.
inIfc.rob_setExecuted(tag, fflags);
// since FPU op has no spec tag, this doFinish rule is ordered before
// other rules that calls incorrectSpec, and BSV compiler creates
// cycles in scheduling. We manually creates a conflict between this
// rule and incorrectSpec to break the cycle
//inIfc.conflictWrongSpec;
endaction
endfunction
rule doFinishFpSimple;
FpuResp resp <- fpuExec.simpleResp;
if(verbose) $display("[doFinishFpSimple] ", fshow(resp));
doFinish(resp.dst, resp.tag, resp.res.data, resp.res.fflags);
endrule
rule doFinishFpFma;
FpuResp resp <- fpuExec.fmaResp;
if(verbose) $display("[doFinishFpFma] ", fshow(resp));
doFinish(resp.dst, resp.tag, resp.res.data, resp.res.fflags);
`ifdef PERF_COUNT
if(inIfc.doStats) begin
exeFpFmaCnt.incr(1);
end
`endif
endrule
rule doFinishFpDiv;
FpuResp resp <- fpuExec.divResp;
if(verbose) $display("[doFinishFpDiv] ", fshow(resp));
doFinish(resp.dst, resp.tag, resp.res.data, resp.res.fflags);
`ifdef PERF_COUNT
if(inIfc.doStats) begin
exeFpDivCnt.incr(1);
end
`endif
endrule
rule doFinishFpSqrt;
FpuResp resp <- fpuExec.sqrtResp;
if(verbose) $display("[doFinishFpSqrt] ", fshow(resp));
doFinish(resp.dst, resp.tag, resp.res.data, resp.res.fflags);
`ifdef PERF_COUNT
if(inIfc.doStats) begin
exeFpSqrtCnt.incr(1);
end
`endif
endrule
rule doFinishIntMul;
MulDivResp resp <- mulDivExec.mulResp;
if(verbose) $display("[doFinishIntMul] ", fshow(resp));
doFinish(resp.dst, resp.tag, resp.data, 0);
`ifdef PERF_COUNT
if(inIfc.doStats) begin
exeIntMulCnt.incr(1);
end
`endif
endrule
rule doFinishIntDiv;
MulDivResp resp <- mulDivExec.divResp;
if(verbose) $display("[doFinishIntDiv] ", fshow(resp));
doFinish(resp.dst, resp.tag, resp.data, 0);
`ifdef PERF_COUNT
if(inIfc.doStats) begin
exeIntDivCnt.incr(1);
end
`endif
endrule
interface recvBypass = map(getRecvBypassIfc, bypassWire);
interface rsFpuMulDivIfc = rsFpuMulDiv;
interface specUpdate = joinSpeculationUpdate(vec(
rsFpuMulDiv.specUpdate,
dispToRegQ.specUpdate,
regToExeQ.specUpdate,
fpuExec.specUpdate,
mulDivExec.specUpdate
));
method Data getPerf(ExeStagePerfType t);
return (case(t)
`ifdef PERF_COUNT
ExeIntMulCnt: exeIntMulCnt;
ExeIntDivCnt: exeIntDivCnt;
ExeFpFmaCnt: exeFpFmaCnt;
ExeFpDivCnt: exeFpDivCnt;
ExeFpSqrtCnt: exeFpSqrtCnt;
`endif
default: 0;
endcase);
endmethod
endmodule

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,326 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// Core size and cache size are controlled by macros CORE_XXX and CACHE_XXX. In
// MICRO 2018 paper, configuration BASE-T is defining CORE_SMALL and
// CACHE_LARGE, and configuration BASE-R is defining CORE_MEDIUM and
// CACHE_LARGE. Due to resource restrictions, 4-core multiprocessor on AWS is
// defining CORE_TINY and CACHE_MC.
//
// ==== common parameters ====
//
`define rv64 True
`define m True
`define a True
`define f True
`define d True
//`define NUM_CORES 1 // defined in make file
//`define PERF_COUNT // defined in makefile
`define REUSE_FMA // use FMA for add and mul
`define LOG_BOOT_ROM_BYTES 12 // 4KB boot rom
// tournament predictor, other options are: BHT, TOUR, GSELECT, GSHARE. NOTE
// that the predictors are of different size.
`define DIR_PRED_TOUR
`define LOG_DEADLOCK_CYCLES 26 // 64M cycles for deadlock detection
// Be lazy in reservation station wake and phy reg file, and enqs. LSQ is by
// default lazy. 1-elem spec FIFOs (pipeline stage regs) are by default not
// lazy.
`define LAZY_RS_RF True
`define RS_LAZY_ENQ True
`define ROB_LAZY_ENQ True
`define L1_TLB_SIZE 32 // L1 fully assoc TLB size
`define L2_TLB_HUGE_SIZE 8 // L2 2MB/1GB TLB size
`define LOG_L2_TLB_4KB_SIZE 10 // L2 4KB TLB log size (1024 entries)
`define LOG_L2_TLB_4KB_WAYS 2 // L2 4KB TLB log ways (4 ways)
// FMA bookkeeping FIFO: add 1 to allow simultaneous enq/deq
`define BOOKKEEPING_FP_FMA_SIZE TAdd#(`XILINX_FP_FMA_LATENCY, 1)
// INT MUL bookkeeping FIFO: add 1 to allow simultaneous enq/deq, another 1
// because of internal flow control in MUL unit
`define BOOKKEEPING_INT_MUL_SIZE TAdd#(`XILINX_INT_MUL_LATENCY, 2)
// non-blocking DTLB
`define DTLB_REQ_NUM 4
// non-blocking L2 TLB
`define L2TLB_REQ_NUM 2
`define DRAM_MAX_READS TExp#(`LOG_LLC_WAYS) // max reads in DRAM, match LLC ways
`define DRAM_MAX_WRITES 16 // write buffer size in AWS DRAM controller
`define DRAM_MAX_REQS 24
`define DRAM_LATENCY 120 // model a constant dram latency
`ifdef SECURITY
`define LOG_DRAM_REGION_NUM 6 // 64 DRAM regions
`define LOG_DRAM_REGION_SIZE 25 // 32MB for each DRAM region
`endif
//
// ==== CACHE SIZE ====
//
`ifdef CACHE_SMALL
// L1
`define LOG_L1_LINES 8 // 16KB
`define LOG_L1_WAYS 3 // 8 ways
// LLC
`define LOG_LLC_LINES 12 // 256KB
`define LOG_LLC_WAYS 4 // 16 ways
`endif
`ifdef CACHE_LARGE
// L1
`define LOG_L1_LINES 9 // 32KB
`define LOG_L1_WAYS 3 // 8 ways
// LLC
`define LOG_LLC_LINES 14 // 1MB
`define LOG_LLC_WAYS 4 // 16 ways
`endif
`ifdef CACHE_MC_1MB
// L1
`define LOG_L1_LINES 9 // 32KB
`define LOG_L1_WAYS 2 // 4 ways
// LLC
`define LOG_LLC_LINES 14 // 1MB
`define LOG_LLC_WAYS 4 // 16 ways
`endif
`ifdef CACHE_MC_2MB
// L1
`define LOG_L1_LINES 9 // 32KB
`define LOG_L1_WAYS 2 // 4 ways
// LLC
`define LOG_LLC_LINES 15 // 2MB
`define LOG_LLC_WAYS 4 // 16 ways
`endif
//
// ==== CORE SIZE ====
//
`ifdef CORE_TINY
// superscalar
`define sizeSup 2
// ROB
`define ROB_SIZE 48
// speculation
`define NUM_EPOCHS 8
`define NUM_SPEC_TAGS 8
// Smaller L1 TLB
`undef TLB_SIZE
`define TLB_SIZE 16
// LSQ
`define LDQ_SIZE 18
`define STQ_SIZE 10
`define SB_SIZE 2
// reservation station sizes
`define RS_ALU_SIZE 10
`define RS_MEM_SIZE 10
`define RS_FPUMULDIV_SIZE 10
`endif
`ifdef CORE_SMALL
// superscalar
`define sizeSup 2
// ROB
`define ROB_SIZE 64
// speculation
`define NUM_EPOCHS 12
`define NUM_SPEC_TAGS 12
// LSQ
`define LDQ_SIZE 24
`define STQ_SIZE 14
`define SB_SIZE 4
// reservation station sizes
`define RS_ALU_SIZE 16
`define RS_MEM_SIZE 16
`define RS_FPUMULDIV_SIZE 16
`endif
`ifdef CORE_MEDIUM
// superscalar
`define sizeSup 2
// ROB
`define ROB_SIZE 80
// speculation
`define NUM_EPOCHS 12
`define NUM_SPEC_TAGS 12
// LSQ
`define LDQ_SIZE 24
`define STQ_SIZE 14
`define SB_SIZE 4
// reservation station sizes
`define RS_ALU_SIZE 16
`define RS_MEM_SIZE 16
`define RS_FPUMULDIV_SIZE 16
`endif
`ifdef CORE_SMALL_WIDE
// superscalar
`define sizeSup 4
// ROB
`define ROB_SIZE 64
// speculation
`define NUM_EPOCHS 16
`define NUM_SPEC_TAGS 16
// LSQ
`define LDQ_SIZE 24
`define STQ_SIZE 14
`define SB_SIZE 4
// reservation station sizes
`define RS_ALU_SIZE 8
`define RS_MEM_SIZE 8
`define RS_FPUMULDIV_SIZE 16
`endif
`ifdef CORE_BOOM
// we extend SMALL to match BOOM's ROB and memory latency, we also increase
// spec tags because of increased ROB size
// superscalar
`define sizeSup 2
// ROB
`define ROB_SIZE 80
// speculation
`define NUM_EPOCHS 12
`define NUM_SPEC_TAGS 12
// LSQ
`define LDQ_SIZE 24
`define STQ_SIZE 14
`define SB_SIZE 4
// reservation station sizes
`define RS_ALU_SIZE 16
`define RS_MEM_SIZE 16
`define RS_FPUMULDIV_SIZE 16
// change memory latency to 80
`undef DRAM_LATENCY
`define DRAM_LATENCY 80
`endif
`ifdef CORE_LARGE
// superscalar
`define sizeSup 2
// ROB
`define ROB_SIZE 128
// speculation
`define NUM_EPOCHS 16
`define NUM_SPEC_TAGS 32
// LSQ
`define LDQ_SIZE 48
`define STQ_SIZE 28
`define SB_SIZE 4
// reservation station sizes
`define RS_ALU_SIZE 32
`define RS_MEM_SIZE 32
`define RS_FPUMULDIV_SIZE 32
`endif
`ifdef CORE_LARGE_WIDE
// superscalar
`define sizeSup 4
// ROB
`define ROB_SIZE 128
// speculation
`define NUM_EPOCHS 16
`define NUM_SPEC_TAGS 32
// LSQ
`define LDQ_SIZE 48
`define STQ_SIZE 28
`define SB_SIZE 4
// reservation station sizes
`define RS_ALU_SIZE 16
`define RS_MEM_SIZE 16
`define RS_FPUMULDIV_SIZE 32
`endif
//
// ==== derived parameters ====
//

View File

@@ -0,0 +1,34 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
`include "ProcConfig.bsv"
import PhysRFile::*;
import SynthParam::*;
typedef RFile#(RFileWrPortNum, RFileRdPortNum) RFileSynth;
(* synthesize *)
module mkRFileSynth(RFileSynth);
let m <- mkRFile(`LAZY_RS_RF);
return m;
endmodule

View File

@@ -0,0 +1,963 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
`include "ProcConfig.bsv"
import Vector::*;
import GetPut::*;
import Cntrs::*;
import Fifo::*;
import FIFO::*;
import Types::*;
import ProcTypes::*;
import CCTypes::*;
import SynthParam::*;
import Performance::*;
import Exec::*;
import FetchStage::*;
import RenamingTable::*;
import ReorderBuffer::*;
import ReorderBufferSynth::*;
import Scoreboard::*;
import ScoreboardSynth::*;
import CsrFile::*;
import SpecTagManager::*;
import EpochManager::*;
import ReservationStationEhr::*;
import ReservationStationAlu::*;
import ReservationStationMem::*;
import ReservationStationFpuMulDiv::*;
import SplitLSQ::*;
typedef struct {
FetchDebugState fetch;
EpochDebugState epoch;
} RenameStuck deriving(Bits, Eq, FShow);
interface RenameInput;
// func units
interface FetchStage fetchIfc; // just for debug
interface ReorderBufferSynth robIfc;
interface RegRenamingTable rtIfc;
interface ScoreboardCons sbConsIfc;
interface ScoreboardAggr sbAggrIfc;
interface CsrFile csrfIfc;
interface EpochManager emIfc;
interface SpecTagManager smIfc;
interface Vector#(AluExeNum, ReservationStationAlu) rsAluIfc;
interface Vector#(FpuMulDivExeNum, ReservationStationFpuMulDiv) rsFpuMulDivIfc;
interface ReservationStationMem rsMemIfc;
interface SplitLSQ lsqIfc;
// pending MMIO req from platform
method Bool pendingMMIOPRq;
// record that a CSR inst or interrupt is sent to ROB
method Action issueCsrInstOrInterrupt;
// deadlock check
method Bool checkDeadlock;
// performance
method Bool doStats;
endinterface
interface RenameStage;
// performance count
method Data getPerf(ExeStagePerfType t);
// deadlock check
interface Get#(RenameStuck) renameInstStuck;
interface Get#(RenameStuck) renameCorrectPathStuck;
endinterface
module mkRenameStage#(RenameInput inIfc)(RenameStage);
Bool verbose = True;
// func units
FetchStage fetchStage = inIfc.fetchIfc;
ReorderBufferSynth rob = inIfc.robIfc;
RegRenamingTable regRenamingTable = inIfc.rtIfc;
ScoreboardCons sbCons = inIfc.sbConsIfc;
ScoreboardAggr sbAggr = inIfc.sbAggrIfc;
CsrFile csrf = inIfc.csrfIfc;
EpochManager epochManager = inIfc.emIfc;
SpecTagManager specTagManager = inIfc.smIfc;
Vector#(AluExeNum, ReservationStationAlu) reservationStationAlu = inIfc.rsAluIfc;
Vector#(FpuMulDivExeNum, ReservationStationFpuMulDiv) reservationStationFpuMulDiv = inIfc.rsFpuMulDivIfc;
ReservationStationMem reservationStationMem = inIfc.rsMemIfc;
SplitLSQ lsq = inIfc.lsqIfc;
// performance counter
`ifdef PERF_COUNT
Count#(Data) supRenameCnt <- mkCount(0);
`ifdef SECURITY
Count#(Data) specNoneCycles <- mkCount(0);
Count#(Data) specNonMemCycles <- mkCount(0);
`endif
`endif
// deadlock check
`ifdef CHECK_DEADLOCK
// timer to check deadlock
Reg#(DeadlockTimer) renameInstTimer <- mkReg(0);
Reg#(DeadlockTimer) renameCorrectPathTimer <- mkReg(0);
// FIFOs to output deadlock info
FIFO#(RenameStuck) renameInstStuckQ <- mkFIFO1;
FIFO#(RenameStuck) renameCorrectPathStuckQ <- mkFIFO1;
// wires to indicate that deadlock is reported, so reset timers
PulseWire renameInstStuckSent <- mkPulseWire;
PulseWire renameCorrectPathStuckSent <- mkPulseWire;
// wires to reset timers since processor is making progress
PulseWire renameWrongPath <- mkPulseWire;
PulseWire renameCorrectPath <- mkPulseWire;
let renameStuck = RenameStuck {
fetch: fetchStage.getFetchState,
epoch: epochManager.getEpochState
};
(* fire_when_enabled *)
rule checkDeadlock_renameInst(inIfc.checkDeadlock && renameInstTimer == maxBound);
renameInstStuckQ.enq(renameStuck);
renameInstStuckSent.send;
endrule
(* fire_when_enabled *)
rule checkDeadlock_renameCorrecPath(inIfc.checkDeadlock && renameCorrectPathTimer == maxBound);
renameCorrectPathStuckQ.enq(renameStuck);
renameCorrectPathStuckSent.send;
endrule
(* fire_when_enabled, no_implicit_conditions *)
rule incrDeadlockTimer(inIfc.checkDeadlock);
function DeadlockTimer getNextTimer(DeadlockTimer t);
return t == maxBound ? maxBound : t + 1;
endfunction
renameInstTimer <= (renameCorrectPath || renameWrongPath || renameInstStuckSent) ? 0 : getNextTimer(renameInstTimer);
renameCorrectPathTimer <= (renameCorrectPath || renameCorrectPathStuckSent) ? 0 : getNextTimer(renameCorrectPathTimer);
endrule
`endif
// kill wrong path inst
// XXX we have to make this a separate rule instead of merging it with rename correct path
// This is because the rename correct path rule is conflict with other rules that redirect
// If wrong path inst keeps coming in, the rename rule may only kill wrong path, but blocks the redirect rule
rule doRenaming_wrongPath(
!epochManager.checkEpoch[0].check(fetchStage.pipelines[0].first.main_epoch) // first wrong path, so at least kill one
);
// we stop when we see a correct path inst
Bool stop = False;
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
if(!stop && fetchStage.pipelines[i].canDeq) begin
let x = fetchStage.pipelines[i].first;
if(epochManager.checkEpoch[i].check(x.main_epoch)) begin
// correct path; stop killing
stop = True;
end
else begin
// wrong path, kill it & update prev epoch
fetchStage.pipelines[i].deq;
epochManager.updatePrevEpoch[i].update(x.main_epoch);
if(verbose) $display("[doRenaming - %d] wrong path: pc = %16x", i, x.pc);
end
end
end
`ifdef CHECK_DEADLOCK
renameWrongPath.send;
`endif
endrule
// check for exceptions and interrupts
function Maybe#(Trap) getTrap(FromFetchStage x);
Maybe#(Trap) trap = tagged Invalid;
let csr_state = csrf.decodeInfo;
let pending_interrupt = csrf.pending_interrupt;
let new_exception = checkForException(x.dInst, x.regs, csr_state);
if (isValid(x.cause)) begin
// previously found exception
trap = tagged Valid (tagged Exception fromMaybe(?, x.cause));
end else if (isValid(pending_interrupt)) begin
// pending interrupt
trap = tagged Valid (tagged Interrupt fromMaybe(?, pending_interrupt));
end else if (isValid(new_exception)) begin
// newly found exception
trap = tagged Valid (tagged Exception fromMaybe(?, new_exception));
end
return trap;
endfunction
// trap for first inst to rename
Maybe#(Trap) firstTrap = getTrap(fetchStage.pipelines[0].first);
// XXX Stall renaming till ROB is empty if we need to replay this inst (i.e. system inst)
// This is a rough fix for a bug with the FPU CSR registers.
// i.e. stall when doReplay(inst) is true
function Action incrEpochStallFetch;
action
epochManager.incrementEpoch;
// stall fetch until redirect
fetchStage.setWaitRedirect;
endaction
endfunction
// rename single trap
rule doRenaming_Trap(
!inIfc.pendingMMIOPRq // stall when MMIO pRq is pending
&& epochManager.checkEpoch[0].check(fetchStage.pipelines[0].first.main_epoch) // correct path
&& isValid(firstTrap) // take trap
&& rob.isEmpty // stall for ROB empty
);
fetchStage.pipelines[0].deq;
let x = fetchStage.pipelines[0].first;
let pc = x.pc;
let ppc = x.ppc;
let main_epoch = x.main_epoch;
let dpTrain = x.dpTrain;
let inst = x.inst;
let dInst = x.dInst;
let arch_regs = x.regs;
let cause = x.cause;
if(verbose) $display("[doRenaming] trap: ", fshow(x));
// update prev epoch
epochManager.updatePrevEpoch[0].update(main_epoch);
// Flip epoch without redirecting
// This avoids doing incorrect work
incrEpochStallFetch;
// just place it in the reorder buffer
let y = ToReorderBuffer{pc: pc,
iType: dInst.iType,
csr: dInst.csr,
claimed_phy_reg: False, // no renaming is done
trap: firstTrap,
// default values of FullResult
ppc_vaddr_csrData: PPC (ppc), // default use PPC
fflags: 0,
////////
will_dirty_fpu_state: False,
rob_inst_state: Executed,
lsqTag: ?,
ldKilled: Invalid,
memAccessAtCommit: False,
lsqAtCommitNotified: False,
nonMMIOStDone: False,
epochIncremented: True, // we have incremented epoch
spec_bits: specTagManager.currentSpecBits
};
rob.enqPort[0].enq(y);
// record if we issue an interrupt
if(firstTrap matches tagged Valid (tagged Interrupt .i)) begin
inIfc.issueCsrInstOrInterrupt;
end
`ifdef CHECK_DEADLOCK
renameCorrectPath.send;
`endif
endrule
// print rename info
function Action printRename(Integer i,
RegsReady regs_ready_cons,
RegsReady regs_ready_aggr,
ArchRegs arch_regs,
PhyRegs phy_regs);
action
$display(" [doRenaming - %d] regs_ready: cons ", i, fshow(regs_ready_cons), " ; aggr ", fshow(regs_ready_aggr));
if (arch_regs.src1 matches tagged Valid .valid_src) begin
if (phy_regs.src1 matches tagged Valid .valid_src_renamed) begin
$display(" [SRC RENAMING] ", fshow(valid_src), " -> ", fshow(valid_src_renamed));
end else begin
$display(" [SRC RENAMING] ERROR: ", fshow(valid_src), " -> INVALID");
doAssert(False, "rename src1 invalid");
end
end
if (arch_regs.src2 matches tagged Valid .valid_src) begin
if (phy_regs.src2 matches tagged Valid .valid_src_renamed) begin
$fdisplay(stdout, " [SRC RENAMING] ", fshow(valid_src), " -> ", fshow(valid_src_renamed));
end else begin
$fdisplay(stdout, " [SRC RENAMING] ERROR: ", fshow(valid_src), " -> INVALID");
doAssert(False, "rename src2 invalid");
end
end
if (arch_regs.src3 matches tagged Valid .valid_src) begin
if (phy_regs.src3 matches tagged Valid .valid_src_renamed) begin
$fdisplay(stdout, " [SRC RENAMING] ", fshow(valid_src), " -> ", fshow(valid_src_renamed));
end else begin
$fdisplay(stdout, " [SRC RENAMING] ERROR: ", fshow(valid_src), " -> INVALID");
doAssert(False, "rename src3 invalid");
end
end
if (arch_regs.dst matches tagged Valid .valid_dst) begin
if (phy_regs.dst matches tagged Valid .valid_dst_renamed) begin
$fdisplay(stdout, " [DST RENAMING] ", fshow(valid_dst), " => ", fshow(valid_dst_renamed));
end else begin
$fdisplay(stdout, " [DST RENAMING] ERROR: ", fshow(valid_dst), " -> INVALID");
doAssert(False, "rename dst invalid");
end
end
endaction
endfunction
// check for system inst that needs to replay
Bool firstReplay = doReplay(fetchStage.pipelines[0].first.dInst.iType);
// System inst is renamed only when ROB is empty
rule doRenaming_SystemInst(
!inIfc.pendingMMIOPRq // stall when MMIO pRq is pending
&& epochManager.checkEpoch[0].check(fetchStage.pipelines[0].first.main_epoch) // correct path
&& !isValid(firstTrap) // not trap
&& firstReplay // system inst needs replay
&& rob.isEmpty // stall for ROB empty
);
fetchStage.pipelines[0].deq;
let x = fetchStage.pipelines[0].first;
let pc = x.pc;
let ppc = x.ppc;
let main_epoch = x.main_epoch;
let dpTrain = x.dpTrain;
let inst = x.inst;
let dInst = x.dInst;
let arch_regs = x.regs;
let cause = x.cause;
if(verbose) $display("[doRenaming] system inst: ", fshow(x));
// update prev epoch
epochManager.updatePrevEpoch[0].update(main_epoch);
// Flip epoch without redirecting. This avoids doing incorrect work
incrEpochStallFetch;
// get spec bits (should be 0), and no need to checkout spec tag
let spec_bits = specTagManager.currentSpecBits;
doAssert(spec_bits == 0, "cannot have spec bits");
// do renaming (renaming is non-speculative)
let rename_result = regRenamingTable.rename[0].getRename(arch_regs);
let phy_regs = rename_result.phy_regs;
regRenamingTable.rename[0].claimRename(arch_regs, spec_bits);
// scoreboard lookup
let regs_ready_cons = sbCons.eagerLookup[0].get(phy_regs);
let regs_ready_aggr = sbAggr.eagerLookup[0].get(phy_regs);
sbCons.setBusy[0].set(phy_regs.dst);
sbAggr.setBusy[0].set(phy_regs.dst);
// print rename info
if (verbose) begin
printRename(0, regs_ready_cons, regs_ready_aggr, arch_regs, phy_regs);
end
// get ROB tag
let inst_tag = rob.enqPort[0].getEnqInstTag;
// CSR inst will be sent to ALU exe pipeline
Bool to_exec = False;
if (dInst.execFunc matches tagged Alu .alu) begin
to_exec = True;
doAssert(dInst.iType == Csr, "only CSR inst send to exe");
end
else begin
doAssert(dInst.iType == FenceI ||
dInst.iType == SFence ||
dInst.iType == Sret ||
dInst.iType == Mret,
"non-CSR inst not send to exe");
doAssert(dInst.execFunc == tagged Other,
"non-exe inst exec func is other");
end
// send to ALU reservation station
if (to_exec) begin
reservationStationAlu[0].enq(ToReservationStation {
data: AluRSData {dInst: dInst, dpTrain: dpTrain},
regs: phy_regs,
tag: inst_tag,
spec_bits: spec_bits,
spec_tag: Invalid,
regs_ready: regs_ready_aggr // alu will recv bypass
});
end
// send to ROB
Bool will_dirty_fpu_state = False;
if (arch_regs.dst matches tagged Valid( tagged Fpu .r )) begin
will_dirty_fpu_state = True;
doAssert(False, "system inst never touches FP regs");
end
RobInstState rob_inst_state = to_exec ? NotDone : Executed;
let y = ToReorderBuffer{pc: pc,
iType: dInst.iType,
csr: dInst.csr,
claimed_phy_reg: True, // XXX we always claim a free reg in rename
trap: Invalid, // no trap
// default values of FullResult
ppc_vaddr_csrData: PPC (ppc), // default use PPC
fflags: 0,
////////
will_dirty_fpu_state: will_dirty_fpu_state,
rob_inst_state: rob_inst_state,
lsqTag: ?,
ldKilled: Invalid,
memAccessAtCommit: False,
lsqAtCommitNotified: False,
nonMMIOStDone: False,
epochIncremented: True, // system inst has incremented epoch
spec_bits: spec_bits
};
rob.enqPort[0].enq(y);
// record if we issue an CSR inst
if(dInst.iType == Csr) begin
inIfc.issueCsrInstOrInterrupt;
end
`ifdef CHECK_DEADLOCK
renameCorrectPath.send;
`endif
endrule
`ifdef SECURITY
// speculation control:
// M mode: turn off speculation for mem inst only
// non-M mode: controlled by mspec CSR
Bool machineMode = csrf.decodeInfo.prv == prvM;
Bool specNone = !machineMode && csrf.rd(CSRmspec) == zeroExtend(mSpecNone);
Bool specNonMem = machineMode || csrf.rd(CSRmspec) == zeroExtend(mSpecNonMem);
`ifdef PERF_COUNT
rule incSpecNoneCycles(inIfc.doStats && specNone);
specNoneCycles.incr(1);
endrule
rule incSpecNonMemCycles(inIfc.doStats && specNonMem);
specNonMemCycles.incr(1);
endrule
`endif
// first inst is mem inst
function Bool isMemInst(ExecFunc f);
return f matches tagged Mem .m ? True : False;
endfunction
Bool firstMem = isMemInst(fetchStage.pipelines[0].first.dInst.execFunc);
// In case speculation is turned off for mem inst only, we rename mem inst
// only when ROB is empty
rule doRenaming_MemInst(
!inIfc.pendingMMIOPRq // stall when MMIO pRq is pending
&& epochManager.checkEpoch[0].check(fetchStage.pipelines[0].first.main_epoch) // correct path
&& !isValid(firstTrap) // not trap
&& !firstReplay // not system inst
// turn off speculation for mem inst only, and first inst is mem
&& (specNonMem && firstMem)
&& rob.isEmpty // stall for ROB empty to process mem inst
);
fetchStage.pipelines[0].deq;
let x = fetchStage.pipelines[0].first;
let pc = x.pc;
let ppc = x.ppc;
let main_epoch = x.main_epoch;
let dpTrain = x.dpTrain;
let inst = x.inst;
let dInst = x.dInst;
let arch_regs = x.regs;
let cause = x.cause;
if(verbose) $display("[doRenaming] mem inst: ", fshow(x));
// update prev epoch
epochManager.updatePrevEpoch[0].update(main_epoch);
// get spec bits (should be 0), and no need to checkout spec tag
let spec_bits = specTagManager.currentSpecBits;
doAssert(spec_bits == 0, "cannot have spec bits");
// do renaming (renaming is non-speculative)
let rename_result = regRenamingTable.rename[0].getRename(arch_regs);
let phy_regs = rename_result.phy_regs;
regRenamingTable.rename[0].claimRename(arch_regs, spec_bits);
// scoreboard lookup
let regs_ready_cons = sbCons.eagerLookup[0].get(phy_regs);
let regs_ready_aggr = sbAggr.eagerLookup[0].get(phy_regs);
sbCons.setBusy[0].set(phy_regs.dst);
sbAggr.setBusy[0].set(phy_regs.dst);
// print rename info
if (verbose) begin
printRename(0, regs_ready_cons, regs_ready_aggr, arch_regs, phy_regs);
end
// get ROB tag
let inst_tag = rob.enqPort[0].getEnqInstTag;
// LSQ tag
LdStQTag lsq_tag = ?;
// send to MEM reservation station
if (dInst.execFunc matches tagged Mem .mem_inst) begin
Bool isLdQ = isLdQMemFunc(mem_inst.mem_func);
Maybe#(LdStQTag) lsqEnqTag = isLdQ ? lsq.enqLdTag : lsq.enqStTag;
if (lsqEnqTag matches tagged Valid .lsqTag) begin
// can process, send to Mem rs and LSQ
lsq_tag = lsqTag; // record LSQ tag
if (dInst.iType != Fence) begin // Fence does not go to RS
reservationStationMem.enq(ToReservationStation {
data: MemRSData {
mem_func: mem_inst.mem_func,
imm: validValue(dInst.imm),
ldstq_tag: lsqTag
},
regs: phy_regs,
tag: inst_tag,
spec_bits: spec_bits,
spec_tag: Invalid,
regs_ready: regs_ready_aggr // mem currently recv bypass
});
end
doAssert(ppc == pc + 4, "Mem next PC is not PC+4");
doAssert(!isValid(dInst.csr), "Mem never explicitly read/write CSR");
doAssert((dInst.iType != Fence) == isValid(dInst.imm),
"Mem (non-Fence) needs imm for virtual addr");
// put in ldstq
if(isLdQ) begin
lsq.enqLd(inst_tag, mem_inst, phy_regs.dst, spec_bits);
end
else begin
lsq.enqSt(inst_tag, mem_inst, phy_regs.dst, spec_bits);
end
end
else begin
// cannot process this inst, stall
when(False, noAction);
end
end
else begin
doAssert(False, "Must be mem inst");
end
// send to ROB
Bool will_dirty_fpu_state = False;
if (arch_regs.dst matches tagged Valid( tagged Fpu .r )) begin
will_dirty_fpu_state = True;
end
RobInstState rob_inst_state = NotDone; // mem inst always needs execution
let y = ToReorderBuffer{pc: pc,
iType: dInst.iType,
csr: dInst.csr,
claimed_phy_reg: True, // XXX we always claim a free reg in rename
trap: Invalid, // no trap
// default values of FullResult
ppc_vaddr_csrData: PPC (ppc), // default use PPC
fflags: 0,
////////
will_dirty_fpu_state: will_dirty_fpu_state,
rob_inst_state: rob_inst_state,
lsqTag: lsq_tag,
ldKilled: Invalid,
memAccessAtCommit: False, // set by ROB in case of fence
lsqAtCommitNotified: False,
nonMMIOStDone: False,
epochIncremented: False,
spec_bits: spec_bits
};
rob.enqPort[0].enq(y);
`ifdef CHECK_DEADLOCK
renameCorrectPath.send;
`endif
endrule
`endif
// Count based scheduling in case of $n$ RS for the same inst type. We
// assume all such RS are of the same size, and prioritize RS with smaller
// valid (occupied) entries.
function Maybe#(idxT) scheduleRS(
Vector#(n, countT) valid_cnt, Vector#(n, Bool) rdy
) provisos(
Ord#(countT), Alias#(idxT, Bit#(TLog#(n))), Add#(1, a__, n)
);
function Bit#(TLog#(n)) getRS(idxT a, idxT b);
if(!rdy[a]) begin
return b;
end
else if(!rdy[b]) begin
return a;
end
else begin
// prioritize RS with smaller valid-entry count
return valid_cnt[a] < valid_cnt[b] ? a : b;
end
endfunction
Vector#(n, idxT) idxVec = genWith(fromInteger);
idxT idx = fold(getRS, idxVec);
return rdy[idx] ? Valid (idx) : Invalid;
endfunction
// rename correct path inst
rule doRenaming(
!inIfc.pendingMMIOPRq // stall when MMIO pRq is pending
&& epochManager.checkEpoch[0].check(fetchStage.pipelines[0].first.main_epoch) // correct path
&& !isValid(firstTrap) // not trap
&& !firstReplay // not system inst
`ifdef SECURITY
// stall for ROB empty if we don't allow speculation at all
&& (!specNone || rob.isEmpty)
// don't process mem inst if we don't allow speculation for mem inst only
&& !(specNonMem && firstMem)
`endif
);
// we stop superscalar rename when an instruction cannot be processed:
// (a) It has trap
// (b) It is wrong path
// (c) It is system inst (we handle system inst in a separate rule)
// (d) It does not have enough resource
Bool stop = False;
// We automatically stop after an inst cannot be deq from fetch stage
// because canDeq signal for sup-fifo is consecutive
// Note that epoch will not change in this rule
// track limited resource usage
Vector#(AluExeNum, Bool) aluExeUsed = replicate(False);
Vector#(FpuMulDivExeNum, Bool) fpuMulDivExeUsed = replicate(False);
Bool memExeUsed = False;
Bool specTagClaimed = False; // specTagManager
// track rename activity
Bool doCorrectPath = False;
SupCnt renameCnt = 0;
// initial spec bits at the beginning of this cycle
// we may update it during the processing
SpecBits spec_bits = specTagManager.currentSpecBits;
// ALU RS valid counts
Vector#(AluExeNum, Bit#(TLog#(TAdd#(`RS_ALU_SIZE, 1)))) aluRSCount;
for(Integer i = 0; i < valueof(AluExeNum); i = i+1) begin
aluRSCount[i] = reservationStationAlu[i].approximateCount;
end
// FPU/MUL/DIV RS valid counts
Vector#(FpuMulDivExeNum, Bit#(TLog#(TAdd#(`RS_FPUMULDIV_SIZE, 1)))) fpuMulDivRSCount;
for(Integer i = 0; i < valueof(FpuMulDivExeNum); i = i+1) begin
fpuMulDivRSCount[i] = reservationStationFpuMulDiv[i].approximateCount;
end
// We apply actions at the end of each iteration
// We **cannot** apply actions at the end of rule,
// because intermediate iterations may change state
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
if(!stop && fetchStage.pipelines[i].canDeq) begin
let x = fetchStage.pipelines[i].first; // don't deq now, inst may not have resource
let pc = x.pc;
let ppc = x.ppc;
let main_epoch = x.main_epoch;
let dpTrain = x.dpTrain;
let inst = x.inst;
let dInst = x.dInst;
let arch_regs = x.regs;
let cause = x.cause;
// check for wrong path, if wrong path, don't process it, leave to the other rule in next cycle
if(!epochManager.checkEpoch[i].check(main_epoch)) begin
stop = True;
end
// for correct path
// check ROB can be enq, otherwise cannot process
if(!rob.enqPort[i].canEnq) begin
stop = True;
end
// check trap, if trap, cannot process, leave to the other rule in next cycle
if(isValid(getTrap(x))) begin
stop = True;
end
// for system inst, process in next cycle (in a different rule)
if(doReplay(dInst.iType)) begin
stop = True;
end
`ifdef SECURITY
// When speculation is not allowed at all, the second inst
// cannot be processed
if(specNone && i != 0) begin
stop = True;
end
// When speculation is not allowed for mem inst only, stop when
// we seen any mem inst
if(specNonMem && isMemInst(dInst.execFunc)) begin
stop = True;
end
`endif
// check renaming table can be enq, otherwise cannot process now
if(!regRenamingTable.rename[i].canRename) begin
stop = True;
end
// Figure out if there is new speculation and if there is
// speculative renaming happening.
Bool new_speculation = False;
Bool speculative_renaming = False; // deprecated: this is originally for Ld/St checkpoint
if (dInst.execFunc matches tagged Br .br) begin
// This instruction can cause a redirection due to branch
// misprediction. Lets claim a checkpoint for this instruction.
// If this instruction is a JAL or JRAL instruction, then the
// checkpoint should include the renaming for the destination
// register.
new_speculation = True;
speculative_renaming = False;
end
// if need spec tag, check spec tag is available, otherwise cannot process
if(new_speculation && (specTagClaimed || !specTagManager.canClaim)) begin
stop = True;
end
if(!stop) begin
// we can continue to analyze this inst
// Claim a speculation tag from the specTagManager if necessary
Maybe#(SpecTag) spec_tag = tagged Invalid;
if (new_speculation) begin
spec_tag = tagged Valid specTagManager.nextSpecTag;
end
// get renaming
// If the renaming is speculative, then the renaming will
// depend on the current spec_tag too.
let renaming_spec_bits = spec_bits | (speculative_renaming ? (1 << fromMaybe(?,spec_tag)) : 0);
let rename_result = regRenamingTable.rename[i].getRename(arch_regs);
let phy_regs = rename_result.phy_regs;
// scoreboard lookup
let regs_ready_cons = sbCons.eagerLookup[i].get(phy_regs);
let regs_ready_aggr = sbAggr.eagerLookup[i].get(phy_regs);
// get ROB tag
let inst_tag = rob.enqPort[i].getEnqInstTag;
// LSQ tag
LdStQTag lsq_tag = ?;
// check execution pipelines availability
// this determines whether this inst can finally be processed
// so we will directly take actions on exe pipelines
Bool to_exec = False;
Bool to_mem = False;
Bool to_FpuMulDiv = False;
case (dInst.execFunc) matches
tagged Alu .alu: to_exec = True;
tagged Br .br: to_exec = True;
tagged MulDiv .muldiv: to_FpuMulDiv = True;
tagged Fpu .fpu: to_FpuMulDiv = True;
tagged Mem .mem: to_mem = True;
default:
// no need for execution, directly become Executed
noAction;
endcase
if (to_exec) begin
// find an ALU pipeline
function Bool aluValid(Integer k) = !aluExeUsed[k] && reservationStationAlu[k].canEnq;
Vector#(AluExeNum, Bool) aluReady = map(aluValid, genVector);
if(scheduleRS(aluRSCount, aluReady) matches tagged Valid .k) begin
// can process, send to ALU rs
aluExeUsed[k] = True; // mark resource used
reservationStationAlu[k].enq(ToReservationStation {
data: AluRSData {dInst: dInst, dpTrain: dpTrain},
regs: phy_regs,
tag: inst_tag,
spec_bits: spec_bits,
spec_tag: spec_tag,
regs_ready: regs_ready_aggr // alu will recv bypass
});
end
else begin
// cannot process this inst, stop
stop = True;
end
end
else if (to_FpuMulDiv) begin
function Bool fpuMulDivValid(Integer k) = !fpuMulDivExeUsed[k] && reservationStationFpuMulDiv[k].canEnq;
Vector#(FpuMulDivExeNum, Bool) fpuMulDivReady = map(fpuMulDivValid, genVector);
if(scheduleRS(fpuMulDivRSCount, fpuMulDivReady) matches tagged Valid .k) begin
// can process, send to FPU MUL DIV rs
fpuMulDivExeUsed[k] = True; // mark resource used
reservationStationFpuMulDiv[k].enq(ToReservationStation {
data: FpuMulDivRSData {execFunc: dInst.execFunc},
regs: phy_regs,
tag: inst_tag,
spec_bits: spec_bits,
spec_tag: spec_tag,
regs_ready: regs_ready_aggr // fpu mul div recv bypass
});
doAssert(ppc == pc + 4, "FpuMulDiv next PC is not PC+4");
doAssert(!isValid(dInst.csr), "FpuMulDiv never explicitly read/write CSR");
doAssert(!isValid(spec_tag), "should not have spec tag");
end
else begin
// cannot process this inst, stop
stop = True;
end
end
else if (to_mem) begin
if (dInst.execFunc matches tagged Mem .mem_inst) begin
Bool isLdQ = isLdQMemFunc(mem_inst.mem_func);
Maybe#(LdStQTag) lsqEnqTag = isLdQ ? lsq.enqLdTag : lsq.enqStTag;
if (!memExeUsed &&& reservationStationMem.canEnq &&&
lsqEnqTag matches tagged Valid .lsqTag) begin
// can process, send to Mem rs and LSQ
memExeUsed = True; // mark resource used
lsq_tag = lsqTag; // record LSQ tag
if (dInst.iType != Fence) begin // fence does not go to RS
reservationStationMem.enq(ToReservationStation {
data: MemRSData {
mem_func: mem_inst.mem_func,
imm: validValue(dInst.imm),
ldstq_tag: lsqTag
},
regs: phy_regs,
tag: inst_tag,
spec_bits: spec_bits,
spec_tag: spec_tag,
regs_ready: regs_ready_aggr // mem currently recv bypass
});
end
doAssert(ppc == pc + 4, "Mem next PC is not PC+4");
doAssert(!isValid(dInst.csr), "Mem never explicitly read/write CSR");
doAssert((dInst.iType != Fence) == isValid(dInst.imm),
"Mem (non-Fence) needs imm for virtual addr");
doAssert(!isValid(spec_tag), "should not have spec tag");
// put in ldstq
if(isLdQ) begin
lsq.enqLd(inst_tag, mem_inst, phy_regs.dst, spec_bits);
end
else begin
lsq.enqSt(inst_tag, mem_inst, phy_regs.dst, spec_bits);
end
end
else begin
// cannot process this inst, stop
stop = True;
end
end
else begin
stop = True;
doAssert(False, "non memory instruction has to_mem == True");
end
end
// apply remaining actions if inst can be processed
if(!stop) begin
if(verbose) $display("[doRenaming - %d] ", i, fshow(x));
// deq fetch & update epochs match
fetchStage.pipelines[i].deq;
epochManager.updatePrevEpoch[i].update(main_epoch);
// Claim a speculation tag
if (new_speculation) begin
specTagClaimed = True; // mark resource used
specTagManager.claimSpecTag;
end
// Do renaming
regRenamingTable.rename[i].claimRename(arch_regs, renaming_spec_bits);
// Scoreboard Operations
sbCons.setBusy[i].set(phy_regs.dst);
sbAggr.setBusy[i].set(phy_regs.dst);
// display information
if (verbose) begin
printRename(i, regs_ready_cons, regs_ready_aggr, arch_regs, phy_regs);
end
// Enqueue into reorder buffer
Bool will_dirty_fpu_state = False;
if (arch_regs.dst matches tagged Valid( tagged Fpu .r )) begin
will_dirty_fpu_state = True;
end
RobInstState rob_inst_state = (to_exec || to_mem || to_FpuMulDiv) ? NotDone : Executed;
let y = ToReorderBuffer{pc: pc,
iType: dInst.iType,
csr: dInst.csr,
claimed_phy_reg: True, // XXX we always claim a free reg in rename
trap: Invalid, // no trap
// default values of FullResult
ppc_vaddr_csrData: PPC (ppc), // default use PPC
fflags: 0,
////////
will_dirty_fpu_state: will_dirty_fpu_state,
rob_inst_state: rob_inst_state,
lsqTag: lsq_tag,
ldKilled: Invalid,
memAccessAtCommit: False, // set by ROB in case of fence
lsqAtCommitNotified: False,
nonMMIOStDone: False,
epochIncremented: False,
spec_bits: spec_bits
};
rob.enqPort[i].enq(y);
// record activity
doCorrectPath = True;
renameCnt = renameCnt + 1;
// update spec bits if spec tag is claimed
if(spec_tag matches tagged Valid .t) begin
spec_bits = spec_bits | (1 << t);
end
end
end
end
end
// only fire this rule if we make some progress
// otherwise this rule may block other rules forever
when(doCorrectPath, noAction);
`ifdef PERF_COUNT
if(inIfc.doStats) begin
if(renameCnt > 1) begin
supRenameCnt.incr(1);
end
end
`endif
`ifdef CHECK_DEADLOCK
if(doCorrectPath) begin
renameCorrectPath.send;
end
`endif
endrule
`ifdef CHECK_DEADLOCK
interface renameInstStuck = toGet(renameInstStuckQ);
interface renameCorrectPathStuck = toGet(renameCorrectPathStuckQ);
`else
interface renameInstStuck = nullGet;
interface renameCorrectPathStuck = nullGet;
`endif
method Data getPerf(ExeStagePerfType t);
return (case(t)
`ifdef PERF_COUNT
SupRenameCnt: supRenameCnt;
`ifdef SECURITY
SpecNoneCycles: specNoneCycles;
SpecNonMemCycles: specNoneCycles;
`endif
`endif
default: 0;
endcase);
endmethod
endmodule

View File

@@ -0,0 +1,44 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
`include "ProcConfig.bsv"
import ReorderBuffer::*;
import SynthParam::*;
typedef ReorderBufferRowEhr#(AluExeNum, FpuMulDivExeNum) RobRowSynth;
(* synthesize *)
module mkRobRowSynth(RobRowSynth);
let m <- mkReorderBufferRowEhr;
return m;
endmodule
// use superscalar ROB
typedef SupReorderBuffer#(AluExeNum, FpuMulDivExeNum) ReorderBufferSynth;
(* synthesize *)
module mkReorderBufferSynth(ReorderBufferSynth);
let m <- mkSupReorderBuffer(`ROB_LAZY_ENQ, mkRobRowSynth);
return m;
endmodule

View File

@@ -0,0 +1,43 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
`include "ProcConfig.bsv"
import Types::*;
import ProcTypes::*;
import ReservationStationEhr::*;
import SynthParam::*;
import BrPred::*;
import DirPredictor::*;
typedef struct {
DecodedInst dInst;
DirPredTrainInfo dpTrain;
} AluRSData deriving(Bits, Eq, FShow);
// ALU pipeline is aggressive, i.e. it recv bypass and early RS wakeup
typedef ReservationStation#(`RS_ALU_SIZE, WrAggrPortNum, AluRSData) ReservationStationAlu;
(* synthesize *)
module mkReservationStationAlu(ReservationStationAlu);
ReservationStationAlu m <- mkReservationStation(`LAZY_RS_RF, `RS_LAZY_ENQ, valueof(AluExeNum) > 1);
return m;
endmodule

View File

@@ -0,0 +1,40 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
`include "ProcConfig.bsv"
import Types::*;
import ProcTypes::*;
import ReservationStationEhr::*;
import SynthParam::*;
typedef struct {
ExecFunc execFunc;
} FpuMulDivRSData deriving(Bits, Eq, FShow);
// FPU MUL DIV pipeline is aggressive, i.e. it recv bypass and early RS wakeup
typedef ReservationStation#(`RS_FPUMULDIV_SIZE, WrAggrPortNum, FpuMulDivRSData) ReservationStationFpuMulDiv;
(* synthesize *)
module mkReservationStationFpuMulDiv(ReservationStationFpuMulDiv);
let m <- mkReservationStation(`LAZY_RS_RF, `RS_LAZY_ENQ, valueof(FpuMulDivExeNum) > 1);
return m;
endmodule

View File

@@ -0,0 +1,42 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
`include "ProcConfig.bsv"
import Types::*;
import ProcTypes::*;
import ReservationStationEhr::*;
import SynthParam::*;
typedef struct {
MemFunc mem_func;
ImmData imm;
LdStQTag ldstq_tag;
} MemRSData deriving(Bits, Eq, FShow);
// MEM pipeline is aggressive, i.e. it recv bypass and early RS wakeup
typedef ReservationStation#(`RS_MEM_SIZE, WrAggrPortNum, MemRSData) ReservationStationMem;
(* synthesize *)
module mkReservationStationMem(ReservationStationMem);
let m <- mkReservationStation(`LAZY_RS_RF, `RS_LAZY_ENQ, False);
return m;
endmodule

View File

@@ -0,0 +1,42 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Scoreboard::*;
import SynthParam::*;
// conservative sb
typedef RenamingScoreboard#(WrConsPortNum, SbLazyLookupPortNum) ScoreboardCons;
(* synthesize *)
module mkScoreboardCons(ScoreboardCons);
let m <- mkRenamingScoreboard;
return m;
endmodule
// aggressive sb
typedef RenamingScoreboard#(WrAggrPortNum, 0) ScoreboardAggr;
(* synthesize *)
module mkScoreboardAggr(ScoreboardAggr);
let m <- mkRenamingScoreboard;
return m;
endmodule

View File

@@ -0,0 +1,77 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import DefaultValue::*;
import Types::*;
import ProcTypes::*;
import HasSpecBits::*;
import ReservationStationEhr::*;
// ALU exe num = superscalar size
// FPU exe num = superscalar size / 2
typedef SupSize AluExeNum;
typedef TDiv#(SupSize, 2) FpuMulDivExeNum;
// Phy RFile
// write: Alu < FpuMulDiv < Mem
// read: Alu, FpuMulDiv, Mem
typedef TAdd#(1, TAdd#(FpuMulDivExeNum, AluExeNum)) RFileWrPortNum;
typedef TAdd#(1, TAdd#(FpuMulDivExeNum, AluExeNum)) RFileRdPortNum;
// sb lazy lookup num: same as RFile read, becaues all pipelines recv bypass
typedef RFileRdPortNum SbLazyLookupPortNum;
// ports for writing conservative elements
// i.e. write rf & set conservative sb & wake up rs in conservative pipeline (i.e. not recv bypass)
// ordering: Alu < FpuMulDiv < Mem
typedef RFileWrPortNum WrConsPortNum;
function Integer aluWrConsPort(Integer i) = i;
function Integer fpuMulDivWrConsPort(Integer i) = valueof(AluExeNum) + i;
Integer memWrConsPort = valueof(FpuMulDivExeNum) + valueof(AluExeNum);
// ports for writing aggressive elements
// i.e. set aggressive sb & wake up rs in aggressive pipeline (i.e. recv bypass)
// Mem pipeline has two places: mem resp and forwarding. Since the rule
// containing forwarding calls lsq.issue, and the rule containing mem resp
// calls lsq.wakeupLd, we order forward before mem.
// Ordering: Alu < FpuMulDiv < Forward < Mem
typedef TAdd#(2, TAdd#(FpuMulDivExeNum, AluExeNum)) WrAggrPortNum;
function Integer aluWrAggrPort(Integer i) = i;
function Integer fpuMulDivWrAggrPort(Integer i) = valueof(AluExeNum) + i;
Integer forwardWrAggrPort = valueof(FpuMulDivExeNum) + valueof(AluExeNum);
Integer memWrAggrPort = 1 + valueof(FpuMulDivExeNum) + valueof(AluExeNum);
// ports for read rf & lazy lookup in sb, ordering doesn't matter
function Integer aluRdPort(Integer i) = i;
function Integer fpuMulDivRdPort(Integer i) = valueof(AluExeNum) + i;
Integer memRdPort = valueof(FpuMulDivExeNum) + valueof(AluExeNum);
// ports for correct spec, ordering doesn't matter
typedef TAdd#(2, AluExeNum) CorrectSpecPortNum;
function Integer finishAluCorrectSpecPort(Integer i) = i;
Integer deqLSQCorrectSpecPort = valueof(AluExeNum);
Integer finishMemCorrectSpecPort = 1 + valueof(AluExeNum);
// ports for manual conflict with wrong spec, ordering doesn't matter
typedef FpuMulDivExeNum ConflictWrongSpecPortNum;
function Integer finishFpuMulDivConflictWrongSpecPort(Integer i) = i;

View File

@@ -0,0 +1,84 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Types::*;
import ProcTypes::*;
(* noinline *)
function Data amoExec(AmoInst amo_inst, Data current_data, Data in_data, Bool upper_32_bits);
// upper_32_bits is true if the operation is a 32-bit operation and the
// address is the upper 32-bits of a 64-bit region.
Data new_data = 0;
Data old_data = current_data;
if (!amo_inst.doubleWord) begin
if (!upper_32_bits) begin
current_data = (amo_inst.func == Min || amo_inst.func == Max) ? signExtend(current_data[31:0]) : zeroExtend(current_data[31:0]);
in_data = (amo_inst.func == Min || amo_inst.func == Max) ? signExtend(in_data[31:0]) : zeroExtend(in_data[31:0]);
end else begin
// use upper 32-bits instead
current_data = (amo_inst.func == Min || amo_inst.func == Max) ? signExtend(current_data[63:32]) : zeroExtend(current_data[63:32]);
in_data = (amo_inst.func == Min || amo_inst.func == Max) ? signExtend(in_data[31:0]) : zeroExtend(in_data[31:0]);
end
end
case (amo_inst.func)
Swap: new_data = in_data;
Add: new_data = current_data + in_data;
Xor: new_data = current_data ^ in_data;
And: new_data = current_data & in_data;
Or: new_data = current_data | in_data;
Min: new_data = sMin(current_data, in_data);
Max: new_data = sMax(current_data, in_data);
Minu: new_data = uMin(current_data, in_data);
Maxu: new_data = uMax(current_data, in_data);
endcase
if (!amo_inst.doubleWord) begin
if (!upper_32_bits) begin
return {old_data[63:32], new_data[31:0]};
end else begin
// change upper 32-bits instead
return {new_data[31:0], old_data[31:0]};
end
end else begin
return new_data;
end
endfunction
function Bit#(t) sMax( Bit#(t) a, Bit#(t) b );
Int#(t) x = max(unpack(a), unpack(b));
return pack(x);
endfunction
function Bit#(t) sMin( Bit#(t) a, Bit#(t) b );
Int#(t) x = min(unpack(a), unpack(b));
return pack(x);
endfunction
function Bit#(t) uMax( Bit#(t) a, Bit#(t) b );
UInt#(t) x = max(unpack(a), unpack(b));
return pack(x);
endfunction
function Bit#(t) uMin( Bit#(t) a, Bit#(t) b );
UInt#(t) x = min(unpack(a), unpack(b));
return pack(x);
endfunction

View File

@@ -0,0 +1,81 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Types::*;
import ProcTypes::*;
import RegFile::*;
import Vector::*;
import BrPred::*;
export BhtTrainInfo;
export mkBht;
typedef Bit#(0) BhtTrainInfo; // no training info needs to be remembered
// Local BHT Typedefs
typedef 128 BhtEntries;
typedef Bit#(TLog#(BhtEntries)) BhtIndex;
(* synthesize *)
module mkBht(DirPredictor#(BhtTrainInfo));
// Read and Write ordering doesn't matter since this is a predictor
// mkRegFileWCF is the RegFile version of mkConfigReg
RegFile#(BhtIndex, Bit#(2)) hist <- mkRegFileWCF(0,fromInteger(valueOf(BhtEntries)-1));
function BhtIndex getIndex(Addr pc);
return truncate(pc >> 2);
endfunction
Vector#(SupSize, DirPred#(BhtTrainInfo)) predIfc;
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
predIfc[i] = (interface DirPred;
method ActionValue#(DirPredResult#(BhtTrainInfo)) pred(Addr pc);
let index = getIndex(pc);
Bit#(2) cnt = hist.sub(index);
Bool taken = cnt[1] == 1;
return DirPredResult {
taken: taken,
train: 0
};
endmethod
endinterface);
end
interface pred = predIfc;
method Action update(Addr pc, Bool taken, BhtTrainInfo train, Bool mispred);
let index = getIndex(pc);
let current_hist = hist.sub(index);
Bit#(2) next_hist;
if(taken) begin
next_hist = (current_hist == 2'b11) ? 2'b11 : current_hist + 1;
end else begin
next_hist = (current_hist == 2'b00) ? 2'b00 : current_hist - 1;
end
hist.upd(index, next_hist);
endmethod
method flush = noAction;
method flush_done = True;
endmodule

View File

@@ -0,0 +1,68 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Types::*;
import ProcTypes::*;
import Vector::*;
(* noinline *)
function Maybe#(Addr) decodeBrPred( Addr pc, DecodedInst dInst, Bool histTaken );
Addr pcPlus4 = pc + 4;
Data imm_val = fromMaybe(?, getDInstImm(dInst));
Maybe#(Addr) nextPc = tagged Invalid;
if( dInst.iType == J ) begin
Addr jTarget = pc + imm_val;
nextPc = tagged Valid jTarget;
end else if( dInst.iType == Br ) begin
if( histTaken ) begin
nextPc = tagged Valid (pc + imm_val);
end else begin
nextPc = tagged Valid pcPlus4;
end
end else if( dInst.iType == Jr ) begin
// target is unknown until RegFetch
nextPc = tagged Invalid;
end else begin
nextPc = tagged Valid pcPlus4;
end
return nextPc;
endfunction
// general types for direction predictor
typedef struct {
Bool taken;
trainInfoT train; // info that a branch must keep for future training
} DirPredResult#(type trainInfoT) deriving(Bits, Eq, FShow);
interface DirPred#(type trainInfoT);
method ActionValue#(DirPredResult#(trainInfoT)) pred(Addr pc);
endinterface
interface DirPredictor#(type trainInfoT);
interface Vector#(SupSize, DirPred#(trainInfoT)) pred;
method Action update(Addr pc, Bool taken, trainInfoT train, Bool mispred);
method Action flush;
method Bool flush_done;
endinterface

View File

@@ -0,0 +1,121 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Types::*;
import ProcTypes::*;
import ConfigReg::*;
import RegFile::*;
import Vector::*;
export NextAddrPred(..);
export mkBtb;
interface NextAddrPred;
method Addr predPc(Addr pc);
method Action update(Addr pc, Addr nextPc, Bool taken);
// security
method Action flush;
method Bool flush_done;
endinterface
// Local BTB Typedefs
typedef 256 BtbEntries; // 4KB BTB
typedef Bit#(TLog#(BtbEntries)) BtbIndex;
typedef Bit#(TSub#(TSub#(AddrSz, TLog#(BtbEntries)), 2)) BtbTag;
typedef struct {
Addr pc;
Addr nextPc;
Bool taken;
} BtbUpdate deriving(Bits, Eq, FShow);
// No synthesize boundary because we need to call predPC several times
module mkBtb(NextAddrPred);
// Read and Write ordering doesn't matter since this is a predictor
// mkRegFileWCF is the RegFile version of mkConfigReg
RegFile#(BtbIndex, Addr) next_addrs <- mkRegFileWCF(0,fromInteger(valueOf(BtbEntries)-1));
RegFile#(BtbIndex, BtbTag) tags <- mkRegFileWCF(0,fromInteger(valueOf(BtbEntries)-1));
Vector#(BtbEntries, Reg#(Bool)) valid <- replicateM(mkConfigReg(False));
RWire#(BtbUpdate) updateEn <- mkRWire;
`ifdef SECURITY
Reg#(Bool) flushDone <- mkReg(True);
`else
Bool flushDone = True;
`endif
function BtbIndex getIndex(Addr pc) = truncate(pc >> 2);
function BtbTag getTag(Addr pc) = truncateLSB(pc);
// no flush, accept update
(* fire_when_enabled, no_implicit_conditions *)
rule canonUpdate(flushDone &&& updateEn.wget matches tagged Valid .upd);
let pc = upd.pc;
let nextPc = upd.nextPc;
let taken = upd.taken;
let index = getIndex(pc);
let tag = getTag(pc);
if(taken) begin
valid[index] <= True;
tags.upd(index, tag);
next_addrs.upd(index, nextPc);
end else if( tags.sub(index) == tag ) begin
// current instruction has target in btb, so clear it
valid[index] <= False;
end
endrule
`ifdef SECURITY
// flush, clear everything (and drop update)
rule doFlush(!flushDone);
writeVReg(valid, replicate(False));
flushDone <= True;
endrule
`endif
method Addr predPc(Addr pc);
BtbIndex index = getIndex(pc);
BtbTag tag = getTag(pc);
if(valid[index] && tag == tags.sub(index))
return next_addrs.sub(index);
else
return (pc + 4);
endmethod
method Action update(Addr pc, Addr nextPc, Bool taken);
updateEn.wset(BtbUpdate {pc: pc, nextPc: nextPc, taken: taken});
endmethod
`ifdef SECURITY
method Action flush if(flushDone);
flushDone <= False;
endmethod
method flush_done = flushDone._read;
`else
method flush = noAction;
method flush_done = True;
`endif
endmodule

View File

@@ -0,0 +1,75 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Types::*;
import ProcTypes::*;
import Vector::*;
interface SendBypass;
method Action send(PhyRIndx dst, Data data);
endinterface
interface RecvBypass;
method Action recv(PhyRIndx dst, Data data);
endinterface
function ActionValue#(Data) readRFBypass(
PhyRIndx src,
Bool rfReady, Data rfData,
Vector#(n, RWire#(Tuple2#(PhyRIndx, Data))) bypassWire
);
actionvalue
if(rfReady) begin
return rfData;
end
else begin
// search through all bypass
function Maybe#(Data) getBypassData(RWire#(Tuple2#(PhyRIndx, Data)) w);
if(w.wget matches tagged Valid {.dst, .d} &&& dst == src) begin
return Valid (d);
end
else begin
return Invalid;
end
endfunction
Vector#(n, Maybe#(Data)) bypassData = map(getBypassData, bypassWire);
if(find(isValid, bypassData) matches tagged Valid .b) begin
doAssert(isValid(b), "bypass found must be valid");
return validValue(b);
end
else begin
when(False, noAction);
return ?;
end
end
endactionvalue
endfunction
function RecvBypass getRecvBypassIfc(RWire#(Tuple2#(PhyRIndx, Data)) w);
return (interface RecvBypass;
method Action recv(PhyRIndx dst, Data data);
w.wset(tuple2(dst, data));
endmethod
endinterface);
endfunction

View File

@@ -0,0 +1,153 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
`include "ProcConfig.bsv"
import BRAM::*;
import Types::*;
import MemoryTypes::*;
import GetPut::*;
import ClientServer::*;
import Connectable::*;
import Vector::*;
import Fifo::*;
import Ehr::*;
import FIFO::*;
import FIFOF::*;
import Performance::*;
import FShow::*;
import MsgFifo::*;
// 64B cache line
typedef 8 CLineNumData;
typedef TLog#(CLineNumData) LogCLineNumData;
typedef Bit#(LogCLineNumData) CLineDataSel;
function CLineDataSel getCLineDataSel(Addr a);
return truncate(a >> valueOf(TLog#(NumBytes)));
endfunction
typedef TMul#(CLineNumData, DataSz) CacheLineSz;
typedef Bit#(CacheLineSz) CacheLine;
typedef TMul#(CLineNumData, NumBytes) CLineNumBytes;
typedef TLog#(CLineNumBytes) LogCLineNumBytes;
typedef Vector#(CLineNumBytes, Bool) CLineByteEn;
function Bool isCLineAlignAddr(Addr a);
Bit#(LogCLineNumBytes) offset = truncate(a);
return offset == 0;
endfunction
// cache line addr (drop the offset within cache line)
typedef TSub#(AddrSz, LogCLineNumBytes) CLineAddrSz;
typedef Bit#(CLineAddrSz) CLineAddr;
// cache line v.s. instruction
typedef TDiv#(CacheLineSz, InstSz) CLineNumInst;
typedef Bit#(TLog#(CLineNumInst)) CLineInstSel;
function CLineInstSel getCLineInstSel(Addr a);
return truncate(a >> valueof(TLog#(TDiv#(InstSz, 8))));
endfunction
// FIFO enq/deq ifc
interface FifoEnq#(type t);
method Bool notFull;
method Action enq(t x);
endinterface
function FifoEnq#(t) toFifoEnq(Fifo#(n, t) f);
return (interface FifoEnq;
method notFull = f.notFull;
method enq = f.enq;
endinterface);
endfunction
function FifoEnq#(t) nullFifoEnq;
return (interface FifoEnq;
method Bool notFull = True;
method Action enq(t x);
noAction;
endmethod
endinterface);
endfunction
instance ToPut#(FifoEnq#(t), t);
function Put#(t) toPut(FifoEnq#(t) ifc);
return (interface Put;
method Action put(t x);
ifc.enq(x);
endmethod
endinterface);
endfunction
endinstance
interface FifoDeq#(type t);
method Bool notEmpty;
method Action deq;
method t first;
endinterface
function FifoDeq#(t) toFifoDeq(Fifo#(n, t) f);
return (interface FifoDeq;
method notEmpty = f.notEmpty;
method deq = f.deq;
method first = f.first;
endinterface);
endfunction
function FifoDeq#(t) nullFifoDeq;
return (interface FifoDeq;
method Bool notEmpty = False;
method Action deq if(False);
noAction;
endmethod
method t first if(False);
return ?;
endmethod
endinterface);
endfunction
instance ToGet#(FifoDeq#(t), t);
function Get#(t) toGet(FifoDeq#(t) ifc);
return (interface Get;
method ActionValue#(t) get;
ifc.deq;
return ifc.first;
endmethod
endinterface);
endfunction
endinstance
instance Connectable#(FifoEnq#(t), FifoDeq#(t));
module mkConnection#(FifoEnq#(t) enq, FifoDeq#(t) deq)(Empty);
mkConnection(toGet(deq), toPut(enq));
endmodule
endinstance
instance Connectable#(FifoDeq#(t), FifoEnq#(t));
module mkConnection#(FifoDeq#(t) deq, FifoEnq#(t) enq)(Empty);
mkConnection(toGet(deq), toPut(enq));
endmodule
endinstance

View File

@@ -0,0 +1,506 @@
// This file was created by gen_ConcatReg.py. If you want to modify this file,
// please modify gen_ConcatReg.py instead. If you need a wider concatReg
// function, change the value of n in gen_ConcatReg.py and run it again.
// The Bluespec provided BuildVector.bsv provides another example of
// constructing a function that takes a variable number of arguments
// Typeclass for creating _concatReg with a variable number of arguments.
typeclass ConcatReg#(type r, numeric type n1, numeric type n2)
dependencies ((r,n1) determines n2, (r,n2) determines n1);
// dependencies (r determines (n1,n2));
function r _concatReg(Reg#(Bit#(n1)) r1, Reg#(Bit#(n2)) r2);
endtypeclass
// Base case
instance ConcatReg#(Reg#(Bit#(n3)), n1, n2) provisos (Add#(n1, n2, n3));
function Reg#(Bit#(TAdd#(n1,n2))) _concatReg(Reg#(Bit#(n1)) r1, Reg#(Bit#(n2)) r2);
return (interface Reg;
method Bit#(TAdd#(n1,n2)) _read = {r1._read, r2._read};
method Action _write(Bit#(TAdd#(n1,n2)) x);
r1._write(truncateLSB(x));
r2._write(truncate(x));
endmethod
endinterface);
endfunction
endinstance
// Recursion
instance ConcatReg#(function r f(Reg#(Bit#(n3)) r3), n1, n2) provisos (ConcatReg#(r, TAdd#(n1, n2), n3));
function function r f(Reg#(Bit#(n3)) r3) _concatReg(Reg#(Bit#(n1)) r1, Reg#(Bit#(n2)) r2);
return _concatReg(interface Reg;
method Bit#(TAdd#(n1,n2)) _read = {r1._read, r2._read};
method Action _write(Bit#(TAdd#(n1,n2)) x);
r1._write(truncateLSB(x));
r2._write(truncate(x));
endmethod
endinterface);
endfunction
endinstance
// Wrapper function for users. This can take a variable number of arguments.
// You will need to use asReg() for the third argument and beyond.
function r concatReg(Reg#(Bit#(n1)) r1, Reg#(Bit#(n2)) r2) provisos(ConcatReg#(r, n1, n2));
return _concatReg(asReg(r1),asReg(r2));
endfunction
// Automatically generated macros with a set number of registers.
// These don't require asReg when used.
function Reg#(Bit#(n)) concatReg2(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2
) provisos (
Add#(n1,n2,n)
);
return concatReg(asReg(r1),asReg(r2));
endfunction
function Reg#(Bit#(n)) concatReg3(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3
) provisos (
Add#(TAdd#(n1,n2),n3,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3));
endfunction
function Reg#(Bit#(n)) concatReg4(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4
) provisos (
Add#(TAdd#(TAdd#(n1,n2),n3),n4,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4));
endfunction
function Reg#(Bit#(n)) concatReg5(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5
) provisos (
Add#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5));
endfunction
function Reg#(Bit#(n)) concatReg6(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6));
endfunction
function Reg#(Bit#(n)) concatReg7(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7));
endfunction
function Reg#(Bit#(n)) concatReg8(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8));
endfunction
function Reg#(Bit#(n)) concatReg9(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9));
endfunction
function Reg#(Bit#(n)) concatReg10(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10));
endfunction
function Reg#(Bit#(n)) concatReg11(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11));
endfunction
function Reg#(Bit#(n)) concatReg12(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12));
endfunction
function Reg#(Bit#(n)) concatReg13(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13));
endfunction
function Reg#(Bit#(n)) concatReg14(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13,
Reg#(Bit#(n14)) r14
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14));
endfunction
function Reg#(Bit#(n)) concatReg15(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13,
Reg#(Bit#(n14)) r14,
Reg#(Bit#(n15)) r15
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15));
endfunction
function Reg#(Bit#(n)) concatReg16(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13,
Reg#(Bit#(n14)) r14,
Reg#(Bit#(n15)) r15,
Reg#(Bit#(n16)) r16
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16));
endfunction
function Reg#(Bit#(n)) concatReg17(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13,
Reg#(Bit#(n14)) r14,
Reg#(Bit#(n15)) r15,
Reg#(Bit#(n16)) r16,
Reg#(Bit#(n17)) r17
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17));
endfunction
function Reg#(Bit#(n)) concatReg18(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13,
Reg#(Bit#(n14)) r14,
Reg#(Bit#(n15)) r15,
Reg#(Bit#(n16)) r16,
Reg#(Bit#(n17)) r17,
Reg#(Bit#(n18)) r18
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18));
endfunction
function Reg#(Bit#(n)) concatReg19(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13,
Reg#(Bit#(n14)) r14,
Reg#(Bit#(n15)) r15,
Reg#(Bit#(n16)) r16,
Reg#(Bit#(n17)) r17,
Reg#(Bit#(n18)) r18,
Reg#(Bit#(n19)) r19
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18),n19,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18),asReg(r19));
endfunction
function Reg#(Bit#(n)) concatReg20(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13,
Reg#(Bit#(n14)) r14,
Reg#(Bit#(n15)) r15,
Reg#(Bit#(n16)) r16,
Reg#(Bit#(n17)) r17,
Reg#(Bit#(n18)) r18,
Reg#(Bit#(n19)) r19,
Reg#(Bit#(n20)) r20
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18),n19),n20,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18),asReg(r19),asReg(r20));
endfunction
function Reg#(Bit#(n)) concatReg21(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13,
Reg#(Bit#(n14)) r14,
Reg#(Bit#(n15)) r15,
Reg#(Bit#(n16)) r16,
Reg#(Bit#(n17)) r17,
Reg#(Bit#(n18)) r18,
Reg#(Bit#(n19)) r19,
Reg#(Bit#(n20)) r20,
Reg#(Bit#(n21)) r21
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18),n19),n20),n21,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18),asReg(r19),asReg(r20),asReg(r21));
endfunction
function Reg#(Bit#(n)) concatReg22(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13,
Reg#(Bit#(n14)) r14,
Reg#(Bit#(n15)) r15,
Reg#(Bit#(n16)) r16,
Reg#(Bit#(n17)) r17,
Reg#(Bit#(n18)) r18,
Reg#(Bit#(n19)) r19,
Reg#(Bit#(n20)) r20,
Reg#(Bit#(n21)) r21,
Reg#(Bit#(n22)) r22
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18),n19),n20),n21),n22,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18),asReg(r19),asReg(r20),asReg(r21),asReg(r22));
endfunction
function Reg#(Bit#(n)) concatReg23(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13,
Reg#(Bit#(n14)) r14,
Reg#(Bit#(n15)) r15,
Reg#(Bit#(n16)) r16,
Reg#(Bit#(n17)) r17,
Reg#(Bit#(n18)) r18,
Reg#(Bit#(n19)) r19,
Reg#(Bit#(n20)) r20,
Reg#(Bit#(n21)) r21,
Reg#(Bit#(n22)) r22,
Reg#(Bit#(n23)) r23
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18),n19),n20),n21),n22),n23,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18),asReg(r19),asReg(r20),asReg(r21),asReg(r22),asReg(r23));
endfunction
function Reg#(Bit#(n)) concatReg24(
Reg#(Bit#(n1)) r1,
Reg#(Bit#(n2)) r2,
Reg#(Bit#(n3)) r3,
Reg#(Bit#(n4)) r4,
Reg#(Bit#(n5)) r5,
Reg#(Bit#(n6)) r6,
Reg#(Bit#(n7)) r7,
Reg#(Bit#(n8)) r8,
Reg#(Bit#(n9)) r9,
Reg#(Bit#(n10)) r10,
Reg#(Bit#(n11)) r11,
Reg#(Bit#(n12)) r12,
Reg#(Bit#(n13)) r13,
Reg#(Bit#(n14)) r14,
Reg#(Bit#(n15)) r15,
Reg#(Bit#(n16)) r16,
Reg#(Bit#(n17)) r17,
Reg#(Bit#(n18)) r18,
Reg#(Bit#(n19)) r19,
Reg#(Bit#(n20)) r20,
Reg#(Bit#(n21)) r21,
Reg#(Bit#(n22)) r22,
Reg#(Bit#(n23)) r23,
Reg#(Bit#(n24)) r24
) provisos (
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18),n19),n20),n21),n22),n23),n24,n)
);
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18),asReg(r19),asReg(r20),asReg(r21),asReg(r22),asReg(r23),asReg(r24));
endfunction

View File

@@ -0,0 +1,606 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
`include "ProcConfig.bsv"
import ClientServer::*;
import DefaultValue::*;
import GetPut::*;
import Types::*;
import ProcTypes::*;
import TlbTypes::*;
import Performance::*;
import FullAssocTlb::*;
import ConfigReg::*;
import Fifo::*;
import Cntrs::*;
import SafeCounter::*;
import CacheUtils::*;
import LatencyTimer::*;
import HasSpecBits::*;
import Vector::*;
import Ehr::*;
export DTlbReq(..);
export DTlbResp(..);
export DTlbRqToP(..);
export DTlbTransRsFromP(..);
export DTlbToParent(..);
export DTlb(..);
export mkDTlb;
typedef `L1_TLB_SIZE DTlbSize;
// req & resp with core
// D TLB also keeps the information of the requesting inst, so we don't need
// extra bookkeeping outside D TLB.
typedef struct {
instT inst;
SpecBits specBits;
} DTlbReq#(type instT) deriving(Bits, Eq, FShow);
typedef struct {
TlbResp resp;
instT inst;
SpecBits specBits;
} DTlbResp#(type instT) deriving(Bits, Eq, FShow);
// req & resp with L2 TLB
typedef struct {
Vpn vpn;
DTlbReqIdx id;
} DTlbRqToP deriving(Bits, Eq, FShow);
typedef struct {
// may get page fault: i.e. hit invalid page or
// get non-leaf page at last-level page table
Maybe#(TlbEntry) entry;
DTlbReqIdx id;
} DTlbTransRsFromP deriving(Bits, Eq, FShow);
interface DTlbToParent;
interface FifoDeq#(DTlbRqToP) rqToP;
interface FifoEnq#(DTlbTransRsFromP) ldTransRsFromP;
// after DTLB flush itself, it notifies L2, and wait L2 to flush
interface Client#(void, void) flush;
endinterface
interface DTlb#(type instT);
// system consistency related
method Bool flush_done;
method Action flush;
method Action updateVMInfo(VMInfo vm);
method Bool noPendingReq;
// req/resp with core
method Action procReq(DTlbReq#(instT) req);
method DTlbResp#(instT) procResp;
method Action deqProcResp;
// req/resp with L2 TLB
interface DTlbToParent toParent;
// speculation
interface SpeculationUpdate specUpdate;
// performance
interface Perf#(L1TlbPerfType) perf;
endinterface
typedef FullAssocTlb#(DTlbSize) DTlbArray;
module mkDTlbArray(DTlbArray);
let m <- mkFullAssocTlb(True); // randomness in replacement
return m;
endmodule
// a pending tlb req may be in following states
typedef union tagged {
void None;
void WaitParent;
DTlbReqIdx WaitPeer;
} DTlbWait deriving(Bits, Eq, FShow);
module mkDTlb#(
function TlbReq getTlbReq(instT inst)
)(DTlb::DTlb#(instT)) provisos(Bits#(instT, a__));
Bool verbose = True;
// TLB array
DTlbArray tlb <- mkDTlbArray;
// processor init flushing by setting this flag
Reg#(Bool) needFlush <- mkReg(False);
// after flushing ITLB itself, we want parent TLB to flush
Reg#(Bool) waitFlushP <- mkReg(False);
// current processor VM information
Reg#(VMInfo) vm_info <- mkReg(defaultValue);
// pending reqs
// pendWait should be meaningful even when entry is invalid. pendWait =
// WaitParent True means this entry is waiting for parent TLB resp;
// pendWait = WaitPeer means this entry is waiting for a resp initiated by
// another req. Thus, pendWait must be None when entry is invalid.
Vector#(DTlbReqNum, Ehr#(2, Bool)) pendValid <- replicateM(mkEhr(False));
Vector#(DTlbReqNum, Reg#(DTlbWait)) pendWait <- replicateM(mkReg(None));
Vector#(DTlbReqNum, Reg#(Bool)) pendPoisoned <- replicateM(mkRegU);
Vector#(DTlbReqNum, Reg#(instT)) pendInst <- replicateM(mkRegU);
Vector#(DTlbReqNum, Reg#(TlbResp)) pendResp <- replicateM(mkRegU);
Vector#(DTlbReqNum, Ehr#(2, SpecBits)) pendSpecBits <- replicateM(mkEhr(?));
// ordering of methods/rules that access pend reqs
// procReq mutually exclusive with doPRs (no procReq when pRs ready)
// procResp < {doPRs, procReq}
// wrongSpec C {procReq, doPRs, procResp}
// correctSpec C wrongSpec
// correctSpec CF doPRs
// {procReq, procResp} < correctSpec (correctSpec is always at end)
RWire#(void) wrongSpec_procResp_conflict <- mkRWire;
RWire#(void) wrongSpec_doPRs_conflict <- mkRWire;
RWire#(void) wrongSpec_procReq_conflict <- mkRWire;
let pendValid_noMiss = getVEhrPort(pendValid, 0);
let pendValid_wrongSpec = getVEhrPort(pendValid, 0);
let pendValid_procResp = getVEhrPort(pendValid, 0); // write
let pendValid_doPRs = getVEhrPort(pendValid, 1); // assert
let pendValid_procReq = getVEhrPort(pendValid, 1); // write
let pendSpecBits_wrongSpec = getVEhrPort(pendSpecBits, 0);
let pendSpecBits_procResp = getVEhrPort(pendSpecBits, 0);
let pendSpecBits_procReq = getVEhrPort(pendSpecBits, 0); // write
let pendSpecBits_correctSpec = getVEhrPort(pendSpecBits, 1);
// free list of pend entries, to cut off path from procResp to procReq
Fifo#(DTlbReqNum, DTlbReqIdx) freeQ <- mkCFFifo;
Reg#(Bool) freeQInited <- mkReg(False);
Reg#(DTlbReqIdx) freeQInitIdx <- mkReg(0);
// req & resp with parent TLB
Fifo#(DTlbReqNum, DTlbRqToP) rqToPQ <- mkCFFifo; // large enough so won't block on enq
Fifo#(2, DTlbTransRsFromP) ldTransRsFromPQ <- mkCFFifo;
// When a resp comes, we first process for the initiating req, then process
// other reqs that in WaitPeer.
Reg#(Maybe#(DTlbReqIdx)) respForOtherReq <- mkReg(Invalid);
// flush req/resp with parent TLB
Fifo#(1, void) flushRqToPQ <- mkCFFifo;
Fifo#(1, void) flushRsFromPQ <- mkCFFifo;
// perf counters
Fifo#(1, L1TlbPerfType) perfReqQ <- mkCFFifo;
`ifdef PERF_COUNT
Fifo#(1, PerfResp#(L1TlbPerfType)) perfRespQ <- mkCFFifo;
Reg#(Bool) doStats <- mkConfigReg(False);
Count#(Data) accessCnt <- mkCount(0);
Count#(Data) missParentCnt <- mkCount(0);
Count#(Data) missParentLat <- mkCount(0);
Count#(Data) missPeerCnt <- mkCount(0);
Count#(Data) missPeerLat <- mkCount(0);
Count#(Data) hitUnderMissCnt <- mkCount(0);
Count#(Data) allMissCycles <- mkCount(0);
LatencyTimer#(DTlbReqNum, 12) latTimer <- mkLatencyTimer; // max latency: 4K cycles
rule doPerf;
let t <- toGet(perfReqQ).get;
Data d = (case(t)
L1TlbAccessCnt: (accessCnt);
L1TlbMissParentCnt: (missParentCnt);
L1TlbMissParentLat: (missParentLat);
L1TlbMissPeerCnt: (missPeerCnt);
L1TlbMissPeerLat: (missPeerLat);
L1TlbHitUnderMissCnt: (hitUnderMissCnt);
L1TlbAllMissCycles: (allMissCycles);
default: (0);
endcase);
perfRespQ.enq(PerfResp {
pType: t,
data: d
});
endrule
rule incrAllMissCycles(doStats);
function Bool isMiss(DTlbWait x) = x != None;
when(all(isMiss, readVReg(pendWait)), allMissCycles.incr(1));
endrule
`endif
// do flush: start when all misses resolve
Bool noMiss = all(\== (False) , readVReg(pendValid_noMiss));
rule doStartFlush(needFlush && !waitFlushP && noMiss);
tlb.flush;
// request parent TLB to flush
flushRqToPQ.enq(?);
waitFlushP <= True;
if(verbose) $display("[DTLB] flush begin");
endrule
rule doFinishFlush(needFlush && waitFlushP);
flushRsFromPQ.deq;
needFlush <= False;
waitFlushP <= False;
if(verbose) $display("[DTLB] flush done");
endrule
// get resp from parent TLB
// At high level, this rule is always exclusive with doStartFlush, though
// we don't bother to make compiler understand this...
rule doPRs(ldTransRsFromPQ.notEmpty);
let pRs = ldTransRsFromPQ.first;
// the current req being served is either the initiating req or other
// req pending on the same resp
let idx = fromMaybe(pRs.id, respForOtherReq);
TlbReq r = getTlbReq(pendInst[idx]);
if(pendPoisoned[idx]) begin
// poisoned inst, do nothing
if(verbose) $display("[DTLB] refill poisoned: idx %d; ", idx, fshow(r));
end
else if(pRs.entry matches tagged Valid .en) begin
// check permission
if(hasVMPermission(vm_info,
en.pteType,
en.ppn,
en.level,
r.write ? DataStore : DataLoad)) begin
// fill TLB, and record resp
tlb.addEntry(en);
let trans_addr = translate(r.addr, en.ppn, en.level);
pendResp[idx] <= tuple2(trans_addr, Invalid);
if(verbose) begin
$display("[DTLB] refill: idx %d; ", idx, fshow(r),
"; ", fshow(trans_addr));
end
end
else begin
// page fault
Exception fault = r.write ? StorePageFault : LoadPageFault;
pendResp[idx] <= tuple2(?, Valid (fault));
if(verbose) begin
$display("[DTLB] refill no permission: idx %d; ", idx, fshow(r));
end
end
end
else begin
// page fault
Exception fault = r.write ? StorePageFault : LoadPageFault;
pendResp[idx] <= tuple2(?, Valid (fault));
if(verbose) $display("[DTLB] refill page fault: idx %d; ", idx, fshow(r));
end
// get parent resp, miss resolved, reset wait bit
pendWait[idx] <= None;
doAssert(pendValid_doPRs[idx], "entry must be valid");
if(isValid(respForOtherReq)) begin
doAssert(pendWait[idx] == WaitPeer (pRs.id), "entry must be waiting for resp");
end
else begin
doAssert(pendWait[idx] == WaitParent, "entry must be waiting for resp");
end
// find another req waiting for this resp
function Bool waitForResp(DTlbReqIdx i);
// we can ignore pendValid here, because not-None pendWait implies
// pendValid is true
return pendWait[i] == WaitPeer (pRs.id) && i != idx;
endfunction
Vector#(DTlbReqNum, DTlbReqIdx) idxVec = genWith(fromInteger);
if(find(waitForResp, idxVec) matches tagged Valid .i) begin
// still have req waiting for this resp, keep processing
respForOtherReq <= Valid (i);
doAssert(pendValid_doPRs[i], "waiting entry must be valid");
end
else begin
// all req done, deq the pRs
respForOtherReq <= Invalid;
ldTransRsFromPQ.deq;
end
`ifdef PERF_COUNT
// perf: miss
let lat <- latTimer.done(idx);
if(doStats) begin
if(isValid(respForOtherReq)) begin
missPeerLat.incr(zeroExtend(lat));
missPeerCnt.incr(1);
end
else begin
missParentLat.incr(zeroExtend(lat));
missParentCnt.incr(1);
end
end
`endif
// conflict with wrong spec
wrongSpec_doPRs_conflict.wset(?);
endrule
// init freeQ
rule doInitFreeQ(!freeQInited);
freeQ.enq(freeQInitIdx);
freeQInitIdx <= freeQInitIdx + 1;
if(freeQInitIdx == fromInteger(valueof(DTlbReqNum) - 1)) begin
freeQInited <= True;
end
endrule
// idx of entries that are ready to resp to proc
function Maybe#(DTlbReqIdx) validProcRespIdx;
function Bool validResp(DTlbReqIdx i);
return pendValid_procResp[i] && pendWait[i] == None && !pendPoisoned[i];
endfunction
Vector#(DTlbReqNum, DTlbReqIdx) idxVec = genWith(fromInteger);
return find(validResp, idxVec);
endfunction
function Maybe#(DTlbReqIdx) poisonedProcRespIdx;
function Bool poisonedResp(DTlbReqIdx i);
return pendValid_procResp[i] && pendWait[i] == None && pendPoisoned[i];
endfunction
Vector#(DTlbReqNum, DTlbReqIdx) idxVec = genWith(fromInteger);
return find(poisonedResp, idxVec);
endfunction
// drop poisoned resp
rule doPoisonedProcResp(poisonedProcRespIdx matches tagged Valid .idx &&& freeQInited);
pendValid_procResp[idx] <= False;
freeQ.enq(idx);
// conflict with wrong spec
wrongSpec_procResp_conflict.wset(?);
endrule
method Action flush if(!needFlush);
needFlush <= True;
waitFlushP <= False;
// this won't interrupt current processing, since
// (1) miss process will continue even if needFlush=True
// (2) flush truly starts when there is no pending req
endmethod
method Bool flush_done = !needFlush;
method Action updateVMInfo(VMInfo vm);
vm_info <= vm;
endmethod
// Since this method is called at commit stage to determine no in-flight
// TLB req, even poisoned req should be considered as pending, because it
// may be in L2 TLB.
method Bool noPendingReq = noMiss;
// We do not accept new req when flushing flag is set. We also do not
// accept new req when parent resp is ready. This avoids bypass in TLB. We
// also check rqToPQ not full. This simplifies the guard, i.e., it does not
// depend on whether we hit in TLB or not.
method Action procReq(DTlbReq#(instT) req) if(
!needFlush && !ldTransRsFromPQ.notEmpty && rqToPQ.notFull && freeQInited
);
// allocate MSHR entry
freeQ.deq;
DTlbReqIdx idx = freeQ.first;
doAssert(!pendValid_procReq[idx], "free entry cannot be valid");
doAssert(pendWait[idx] == None, "entry cannot wait for parent resp");
pendValid_procReq[idx] <= True;
pendPoisoned[idx] <= False;
pendInst[idx] <= req.inst;
pendSpecBits_procReq[idx] <= req.specBits;
// pendWait and pendResp are set later in this method
// try to translate
TlbReq r = getTlbReq(req.inst);
`ifdef SECURITY
// Security check
// Forbid any data load shared outside of the protection domain
// if shared load are not allowed
// No need to special case M mode with special vm_info value because we
// assume that we allow shared load all the time when in M mode.
// (Because we are always non speculative in M mode)
if (!vm_info.sanctum_authShared && outOfProtectionDomain(vm_info, r.addr))begin
pendWait[idx] <= None;
pendResp[idx] <= tuple2(?, Valid (LoadAccessFault));
end
`else
// No security check
if (False) begin
noAction;
end
`endif
else if (vm_info.sv39) begin
let vpn = getVpn(r.addr);
let trans_result = tlb.translate(vpn, vm_info.asid);
if (trans_result.hit) begin
// TLB hit
let entry = trans_result.entry;
// check permission
if (hasVMPermission(vm_info,
entry.pteType,
entry.ppn,
entry.level,
r.write ? DataStore : DataLoad)) begin
// update TLB replacement info
tlb.updateRepByHit(trans_result.index);
// translate addr
Addr trans_addr = translate(r.addr, entry.ppn, entry.level);
pendWait[idx] <= None;
pendResp[idx] <= tuple2(trans_addr, Invalid);
if(verbose) begin
$display("[DTLB] req (hit): idx %d; ", idx, fshow(r),
"; ", fshow(trans_result));
end
`ifdef PERF_COUNT
// perf: hit under miss
if(doStats && readVReg(pendWait) != replicate(None)) begin
hitUnderMissCnt.incr(1);
end
`endif
end
else begin
// page fault
Exception fault = r.write ? StorePageFault : LoadPageFault;
pendWait[idx] <= None;
pendResp[idx] <= tuple2(?, Valid (fault));
if(verbose) $display("[DTLB] req no permission: idx %d; ", idx, fshow(r));
end
end
else begin
// TLB miss, req to parent TLB only if there is no existing req
// for the same VPN already waiting for parent TLB resp
function Bool reqSamePage(DTlbReqIdx i);
// we can ignore pendValid here, because not-None pendWait implies
// pendValid is true
let r_i = getTlbReq(pendInst[i]);
return pendWait[i] == WaitParent && getVpn(r.addr) == getVpn(r_i.addr);
endfunction
Vector#(DTlbReqNum, DTlbReqIdx) idxVec = genWith(fromInteger);
if(find(reqSamePage, idxVec) matches tagged Valid .i) begin
// peer entry has already requested, so don't send duplicate req
pendWait[idx] <= WaitPeer (i);
doAssert(pendValid_procReq[i], "peer entry must be valid");
if(verbose) begin
$display("[DTLB] req miss, pend on peer: idx %d, ",
idx, "; ", fshow(r), "; ", fshow(i));
end
end
else begin
// this is the first req for this VPN
pendWait[idx] <= WaitParent;
rqToPQ.enq(DTlbRqToP {
vpn: vpn,
id: idx
});
if(verbose) begin
$display("[DTLB] req miss, send to parent: idx %d, ",
idx, fshow(r));
end
end
`ifdef PERF_COUNT
// perf: miss
latTimer.start(idx);
`endif
end
end
else begin
// bare mode
pendWait[idx] <= None;
pendResp[idx] <= tuple2(r.addr, Invalid);
if(verbose) $display("DTLB %m req (bare): ", fshow(r));
end
`ifdef PERF_COUNT
// perf: access
if(doStats) begin
accessCnt.incr(1);
end
`endif
// conflict with wrong spec
wrongSpec_procReq_conflict.wset(?);
endmethod
method Action deqProcResp if(
validProcRespIdx matches tagged Valid .idx &&& freeQInited
);
pendValid_procResp[idx] <= False;
freeQ.enq(idx);
// conflict with wrong spec
wrongSpec_procResp_conflict.wset(?);
endmethod
method DTlbResp#(instT) procResp if(
validProcRespIdx matches tagged Valid .idx &&& freeQInited
);
return DTlbResp {
inst: pendInst[idx],
resp: pendResp[idx],
specBits: pendSpecBits_procResp[idx]
};
endmethod
interface DTlbToParent toParent;
interface rqToP = toFifoDeq(rqToPQ);
interface ldTransRsFromP = toFifoEnq(ldTransRsFromPQ);
interface Client flush;
interface request = toGet(flushRqToPQ);
interface response = toPut(flushRsFromPQ);
endinterface
endinterface
interface SpeculationUpdate specUpdate;
method Action incorrectSpeculation(Bool kill_all, SpecTag x);
// poison entries
for(Integer i = 0 ; i < valueOf(DTlbReqNum) ; i = i+1) begin
if(kill_all || pendSpecBits_wrongSpec[i][x] == 1'b1) begin
pendPoisoned[i] <= True;
end
end
// make conflicts with procReq, doPRs, procResp
wrongSpec_procReq_conflict.wset(?);
wrongSpec_doPRs_conflict.wset(?);
wrongSpec_procResp_conflict.wset(?);
endmethod
method Action correctSpeculation(SpecBits mask);
// clear spec bits for all entries
for(Integer i = 0 ; i < valueOf(DTlbReqNum) ; i = i+1) begin
let new_spec_bits = pendSpecBits_correctSpec[i] & mask;
pendSpecBits_correctSpec[i] <= new_spec_bits;
end
endmethod
endinterface
interface Perf perf;
method Action setStatus(Bool stats);
`ifdef PERF_COUNT
doStats <= stats;
`else
noAction;
`endif
endmethod
method Action req(L1TlbPerfType r);
perfReqQ.enq(r);
endmethod
method ActionValue#(PerfResp#(L1TlbPerfType)) resp;
`ifdef PERF_COUNT
perfRespQ.deq;
return perfRespQ.first;
`else
perfReqQ.deq;
return PerfResp {
pType: perfReqQ.first,
data: 0
};
`endif
endmethod
method Bool respValid;
`ifdef PERF_COUNT
return perfRespQ.notEmpty;
`else
return perfReqQ.notEmpty;
`endif
endmethod
endinterface
endmodule

View File

@@ -0,0 +1,743 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
`include "ProcConfig.bsv"
import Types::*;
import ProcTypes::*;
import MemoryTypes::*;
import Vector::*;
import DefaultValue::*;
Bit#(3) memWU = 3'b110;
// Smaller decode functions
function Maybe#(MemInst) decodeMemInst(Instruction inst);
MemInst mem_inst = MemInst{ mem_func: ?,
amo_func: None,
unsignedLd: False,
byteEn: replicate(False),
aq: False,
rl: False };
Bool illegalInst = False;
Opcode opcode = unpackOpcode(inst[6:0]);
let funct5 = inst[31:27];
let funct3 = inst[14:12];
// mem_func + amo_func
MemFunc mem_func = Ld;
AmoFunc amo_func = None;
if (opcode == Load || opcode == LoadFp) begin
mem_func = Ld;
end else if (opcode == Store || opcode == StoreFp) begin
mem_func = St;
end else if (opcode == Amo) begin
case (funct5)
fnLR : mem_func = Lr;
fnSC : mem_func = Sc;
fnAMOSWAP,
fnAMOADD,
fnAMOXOR,
fnAMOAND,
fnAMOOR,
fnAMOMIN,
fnAMOMAX,
fnAMOMINU,
fnAMOMAXU : mem_func = Amo;
default : illegalInst = True;
endcase
// now for amo_func
case (funct5)
fnAMOSWAP : amo_func = Swap;
fnAMOADD : amo_func = Add;
fnAMOXOR : amo_func = Xor;
fnAMOAND : amo_func = And;
fnAMOOR : amo_func = Or;
fnAMOMIN : amo_func = Min;
fnAMOMAX : amo_func = Max;
fnAMOMINU : amo_func = Minu;
fnAMOMAXU : amo_func = Maxu;
endcase
end else begin
illegalInst = True;
end
// unsignedLd
// it doesn't matter if this is set to True for stores
Bool unsignedLd = False;
case (funct3)
memB, memH, memW, memD:
unsignedLd = False;
memBU, memHU, memWU:
unsignedLd = True;
default:
illegalInst = True;
endcase
// This is a minor fix to make our processor's results match spike since
// they don't sign extend when loading single precision values from memory
if (opcode == LoadFp) begin
unsignedLd = True;
end
// byteEn
// TODO: Some combinations of operations and byteEn's are illegal.
// They should be detected here.
ByteEn byteEn = replicate(False);
case (funct3)
memB, memBU : byteEn[0] = True;
memH, memHU : begin
byteEn[0] = True;
byteEn[1] = True;
end
memW, memWU : begin
byteEn[0] = True;
byteEn[1] = True;
byteEn[2] = True;
byteEn[3] = True;
end
memD : byteEn = replicate(True);
default : illegalInst = True;
endcase
// aq + rl
Bool aq = False;
Bool rl = False;
if (opcode == Amo) begin
// aq and rl are only defined for Amo operations
aq = unpack(inst[ 26 ]);
rl = unpack(inst[ 25 ]);
end
if (illegalInst) begin
return tagged Invalid;
end else begin
return tagged Valid ( MemInst{
mem_func: mem_func,
amo_func: amo_func,
unsignedLd: unsignedLd,
byteEn: byteEn,
aq: aq,
rl: rl } );
end
endfunction
(* noinline *)
function DecodeResult decode(Instruction inst);
RiscVISASubset isa = defaultValue;
// initialize dInst with default values
DecodedInst dInst = DecodedInst {
iType: Unsupported,
execFunc: tagged Other,
csr: tagged Invalid,
imm: tagged Invalid
};
ArchRegs regs = ArchRegs {
src1: tagged Invalid,
src2: tagged Invalid,
src3: tagged Invalid,
dst: tagged Invalid
};
Bool illegalInst = False;
Opcode opcode = unpackOpcode(inst[ 6 : 0 ]);
let rd = inst[ 11 : 7 ];
let funct3 = inst[ 14 : 12 ];
let rs1 = inst[ 19 : 15 ];
let rs2 = inst[ 24 : 20 ];
let funct7 = inst[ 31 : 25 ];
// For "F" and "D" ISA extensions
let funct5 = inst[ 31 : 27 ];
let fmt = inst[ 26 : 25 ];
let rs3 = inst[ 31 : 27 ];
let funct2 = inst[ 26 : 25 ];
// For "A" ISA extension
Bool aq = unpack(inst[ 26 ]);
Bool rl = unpack(inst[ 25 ]);
ImmData immI = signExtend(inst[31:20]);
ImmData immS = signExtend({ inst[31:25], inst[11:7] });
ImmData immB = signExtend({ inst[31], inst[7], inst[30:25], inst[11:8], 1'b0});
ImmData immU = signExtend({ inst[31:12], 12'b0 });
ImmData immJ = signExtend({ inst[31], inst[19:12], inst[20], inst[30:21], 1'b0});
// Results of mini-decoders
Maybe#(MemInst) mem_inst = decodeMemInst(inst);
// TODO better detection of illegal insts
case (opcode)
OpImm: begin
dInst.iType = Alu;
dInst.execFunc = tagged Alu (case (funct3)
fnADD: Add;
fnSLT: Slt;
fnSLTU: Sltu;
fnAND: And;
fnOR: Or;
fnXOR: Xor;
fnSLL: Sll;
fnSR: (immI[10] == 0 ? Srl : Sra);
endcase);
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Invalid;
dInst.imm = Valid(immI);
dInst.csr = tagged Invalid;
end
OpImm32: begin
dInst.iType = Alu;
dInst.execFunc = tagged Alu (case (funct3)
fnADD: Addw;
fnSLL: Sllw;
fnSR: (immI[10] == 0 ? Srlw : Sraw);
endcase);
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Invalid;
dInst.imm = Valid(immI);
dInst.csr = tagged Invalid;
end
Op: begin
dInst.iType = Alu;
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Valid(tagged Gpr rs2);
dInst.imm = Invalid;
dInst.csr = tagged Invalid;
case (funct7)
opALU1: begin
dInst.execFunc = tagged Alu (case(funct3)
fnADD: Add;
fnSLT: Slt;
fnSLTU: Sltu;
fnAND: And;
fnOR: Or;
fnXOR: Xor;
fnSLL: Sll;
fnSR: Srl;
endcase);
end
opALU2: begin
dInst.execFunc = tagged Alu (case (funct3)
fnADD: Sub;
fnSR: Sra;
endcase);
end
opMULDIV: begin
if (isa.m) begin
// Processor includes "M" extension
MulDivFunc func = (case(funct3)
fnMUL : Mul;
fnMULH : Mulh;
fnMULHSU : Mulh;
fnMULHU : Mulh;
fnDIV : Div;
fnDIVU : Div;
fnREM : Rem;
fnREMU : Rem;
endcase);
Bool w = False;
MulDivSign sign = (case(funct3)
fnMUL : Signed;
fnMULH : Signed;
fnMULHSU : SignedUnsigned;
fnMULHU : Unsigned;
fnDIV : Signed;
fnDIVU : Unsigned;
fnREM : Signed;
fnREMU : Unsigned;
endcase);
dInst.execFunc = tagged MulDiv (MulDivInst {
func: func, w: w, sign: sign
});
end else begin
// Processor doesn't include "M" extension
illegalInst = True;
end
end
endcase
end
Op32: begin
dInst.iType = Alu;
case (funct7)
opALU1: begin
dInst.execFunc = tagged Alu (case(funct3)
fnADD: Addw;
fnSLL: Sllw;
fnSR: Srlw;
endcase);
end
opALU2: begin
dInst.execFunc = tagged Alu (case (funct3)
fnADD: Subw;
fnSR: Sraw;
endcase);
end
opMULDIV: begin
if (isa.m) begin
// Processor includes "M" extension
// TODO mark MULH as illegal inst
MulDivFunc func = (case(funct3)
fnMUL : Mul;
fnMULH : Mulh; // illegal
fnMULHSU : Mulh; // illegal
fnMULHU : Mulh; // illegal
fnDIV : Div;
fnDIVU : Div;
fnREM : Rem;
fnREMU : Rem;
endcase);
Bool w = True;
MulDivSign sign = (case(funct3)
fnMUL : Signed;
fnMULH : Signed; // illegal
fnMULHSU : SignedUnsigned; // illegal
fnMULHU : Unsigned; // illegal
fnDIV : Signed;
fnDIVU : Unsigned;
fnREM : Signed;
fnREMU : Unsigned;
endcase);
dInst.execFunc = tagged MulDiv (MulDivInst{
func: func, w: w, sign: sign
});
end else begin
// Processor doesn't include "M" extension
illegalInst = True;
end
end
endcase
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Valid(tagged Gpr rs2);
dInst.imm = Invalid;
dInst.csr = tagged Invalid;
end
Lui: begin // treated as an x0 + immU
dInst.iType = Alu;
dInst.execFunc = tagged Alu Add;
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Gpr 0);
regs.src2 = Invalid;
dInst.imm = Valid(immU);
dInst.csr = tagged Invalid;
end
Auipc: begin
dInst.iType = Auipc;
dInst.execFunc = tagged Alu Add;
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Invalid;
regs.src2 = Invalid;
dInst.imm = Valid(immU);
dInst.csr = tagged Invalid;
end
Jal: begin
dInst.iType = J;
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Invalid;
regs.src2 = Invalid;
dInst.imm = Valid(immJ);
dInst.csr = tagged Invalid;
dInst.execFunc = tagged Br AT;
end
Jalr: begin
dInst.iType = Jr;
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Invalid;
dInst.imm = Valid(immI);
dInst.csr = tagged Invalid;
dInst.execFunc = tagged Br AT;
end
Branch: begin
dInst.iType = Br;
dInst.execFunc = tagged Br (case(funct3)
fnBEQ: Eq;
fnBNE: Neq;
fnBLT: Lt;
fnBLTU: Ltu;
fnBGE: Ge;
fnBGEU: Geu;
endcase);
regs.dst = Invalid;
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Valid(tagged Gpr rs2);
dInst.imm = Valid(immB);
dInst.csr = tagged Invalid;
end
Load: begin
dInst.iType = Ld;
if (isValid(mem_inst)) begin
dInst.execFunc = tagged Mem fromMaybe(?, mem_inst);
end else begin
illegalInst = True;
end
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Invalid;
dInst.imm = Valid(immI);
dInst.csr = tagged Invalid;
end
Store: begin
dInst.iType = St;
if (isValid(mem_inst)) begin
dInst.execFunc = tagged Mem fromMaybe(?, mem_inst);
end else begin
illegalInst = True;
end
regs.dst = Invalid;
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Valid(tagged Gpr rs2);
dInst.imm = Valid(immS);
dInst.csr = tagged Invalid;
end
Amo: begin
if (!isa.a) begin
// unsupported
illegalInst = True;
end else begin
// AMO defaults
dInst.iType = Amo;
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Valid(tagged Gpr rs2);
dInst.imm = Valid(0);
dInst.csr = Invalid;
case (funct5)
fnLR: begin
dInst.iType = Lr;
if (isValid(mem_inst)) begin
dInst.execFunc = tagged Mem fromMaybe(?, mem_inst);
end else begin
illegalInst = True;
end
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Invalid;
dInst.imm = Valid(0);
dInst.csr = Invalid;
end
fnSC: begin
dInst.iType = Sc;
if (isValid(mem_inst)) begin
dInst.execFunc = tagged Mem fromMaybe(?, mem_inst);
end else begin
illegalInst = True;
end
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Valid(tagged Gpr rs2);
dInst.imm = Valid(0);
dInst.csr = Invalid;
end
fnAMOSWAP,
fnAMOADD,
fnAMOXOR,
fnAMOAND,
fnAMOOR,
fnAMOMIN,
fnAMOMAX,
fnAMOMINU,
fnAMOMAXU: begin
if (isValid(mem_inst)) begin
dInst.execFunc = tagged Mem fromMaybe(?, mem_inst);
end else begin
illegalInst = True;
end
end
default: begin
illegalInst = True;
end
endcase
end
end
// Instructions for "F" and "D" ISA extensions - FPU
OpFp: begin
// check if instruction is supported
if ((fmt == fmtS && !isa.f) || (fmt == fmtD && !isa.d) || (fmt != fmtS && fmt != fmtD)) begin
illegalInst = True;
end else begin
// Instruction is supported
dInst.iType = Fpu;
regs.dst = Valid(tagged Fpu rd);
regs.src1 = Valid(tagged Fpu rs1);
regs.src2 = Valid(tagged Fpu rs2);
dInst.imm = Invalid;
dInst.csr = tagged Invalid;
FpuFunc func = (case (funct5)
opFADD: FAdd;
opFSUB: FSub;
opFMUL: FMul;
opFDIV: FDiv;
opFSQRT: FSqrt;
opFSGNJ: ((funct3 == 0) ? FSgnj : ((funct3 == 1) ? FSgnjn : FSgnjx));
opFMINMAX: ((funct3 == 0) ? FMin : FMax);
opFCMP: ((funct3 == 0) ? FLe : ((funct3 == 1) ? FLt : FEq));
opFMV_XF: ((funct3 == 0) ? FMv_XF : FClass); // also CLASS
opFMV_FX: FMv_FX;
opFCVT_FF: FCvt_FF;
opFCVT_WF: ((rs2 == 0) ? FCvt_WF : ((rs2 == 1) ? FCvt_WUF : ((rs2 == 2) ? FCvt_LF : FCvt_LUF)));
opFCVT_FW: ((rs2 == 0) ? FCvt_FW : ((rs2 == 1) ? FCvt_FWU : ((rs2 == 2) ? FCvt_FL : FCvt_FLU)));
endcase);
FpuPrecision precision = (fmt == fmtS) ? Single : Double;
dInst.execFunc = tagged Fpu(FpuInst{func: func, rm: unpack(funct3), precision: precision});
// Special cases
case (funct5)
opFSQRT: begin
regs.dst = Valid(tagged Fpu rd);
regs.src1 = Valid(tagged Fpu rs1);
regs.src2 = Invalid;
end
opFCMP: begin
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Fpu rs1);
regs.src2 = Valid(tagged Fpu rs2);
end
opFMV_XF: begin
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Fpu rs1);
regs.src2 = Invalid;
end
opFMV_FX: begin
regs.dst = Valid(tagged Fpu rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Invalid;
end
opFCVT_FF: begin
regs.dst = Valid(tagged Fpu rd);
regs.src1 = Valid(tagged Fpu rs1);
regs.src2 = Invalid;
end
opFCVT_WF: begin
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Valid(tagged Fpu rs1);
regs.src2 = Invalid;
end
opFCVT_FW: begin
regs.dst = Valid(tagged Fpu rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Invalid;
end
endcase
end
end
LoadFp: begin
// check if instruction is supported
if (!isa.f && !isa.d) begin
// FIXME: Check more cases
illegalInst = True;
end else begin
// Same decode logic as Int Ld
dInst.iType = Ld;
if (isValid(mem_inst)) begin
dInst.execFunc = tagged Mem fromMaybe(?, mem_inst);
end else begin
illegalInst = True;
end
regs.dst = Valid(tagged Fpu rd);
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Invalid;
dInst.imm = Valid(immI);
dInst.csr = tagged Invalid;
end
end
StoreFp: begin
// check if instruction is supported
if (!isa.f && !isa.d) begin
// FIXME: Check more cases
illegalInst = True;
end else begin
// Same decode logic as Int St
dInst.iType = St;
if (isValid(mem_inst)) begin
dInst.execFunc = tagged Mem fromMaybe(?, mem_inst);
end else begin
illegalInst = True;
end
regs.dst = Invalid;
regs.src1 = Valid(tagged Gpr rs1);
regs.src2 = Valid(tagged Fpu rs2);
dInst.imm = Valid(immS);
dInst.csr = tagged Invalid;
end
end
Fmadd, Fmsub, Fnmsub, Fnmadd: begin
// check if instruction is supported
if ((fmt == fmtS && !isa.f) ||
(fmt == fmtD && !isa.d) ||
(fmt != fmtS && fmt != fmtD)) begin
dInst.iType = Unsupported;
illegalInst = True;
end else begin
// Instruction is supported
dInst.iType = Fpu;
FpuFunc func = ?;
case (opcode)
Fmadd: func = FMAdd;
Fmsub: func = FMSub;
Fnmsub: func = FNMSub;
Fnmadd: func = FNMAdd;
default: illegalInst = True;
endcase
dInst.execFunc = tagged Fpu (FpuInst {
func: func,
rm: unpack(funct3),
precision: fmt == fmtS ? Single : Double
});
regs.src1 = Valid(tagged Fpu rs1);
regs.src2 = Valid(tagged Fpu rs2);
regs.src3 = Valid(rs3);
regs.dst = Valid(tagged Fpu rd);
dInst.csr = Invalid;
dInst.imm = Invalid;
end
end
MiscMem: begin
case (funct3)
fnFENCEI: begin
dInst.iType = FenceI;
dInst.execFunc = tagged Other;
end
fnFENCE: begin
// extract bits for P/S IORW
Bool old_st = unpack(inst[26] | inst[24]); // PO, PW
Bool young_ld = unpack(inst[23] | inst[21]); // SI, SR
// get acq/reconcile and rel/commit needed to enforce
// different orderings
`ifdef TSO_MM
// Orderings enfored by fence in TSO:
// St -> Ld: commit
// Others: N/A
Bool reconcile = False;
Bool commit = old_st && young_ld;
`else
// Orderings enforced by fence in WMM
// St -> St: commit
// St -> Ld: commit + reconcile
// Ld -> Ld: reconcile
// Ld -> St: N/A
Bool reconcile = young_ld; // reconcile when younger load is in ordering
Bool commit = old_st; // commit when older store is in ordering
`endif
// set up fence inst
if (reconcile || commit) begin
dInst.iType = Fence;
dInst.execFunc = tagged Mem (MemInst {
mem_func: Fence,
amo_func: None,
unsignedLd: False,
byteEn: replicate(False),
aq: reconcile,
rl: commit
});
end
else begin
dInst.iType = Nop;
dInst.execFunc = tagged Other;
end
end
default: illegalInst = True;
endcase
regs.dst = Invalid;
regs.src1 = Invalid;
regs.src2 = Invalid;
dInst.imm = Invalid;
dInst.csr = Invalid;
end
System: begin
if (funct3 == fnPRIV) begin
if (funct7 == privSFENCEVMA) begin
dInst.iType = SFence;
// FIXME SFENCE.VMA is implemented in coarse grain,
// ignoring rs1 and rs2
end
else begin
case (truncate(immI))
privSRET: dInst.iType = Sret;
privMRET: dInst.iType = Mret;
privECALL: dInst.iType = Ecall;
privEBREAK: dInst.iType = Ebreak;
privWFI: dInst.iType = Nop; // treat as NOP
default: illegalInst = True;
endcase
end
regs.dst = Invalid;
regs.src1 = Invalid;
regs.src2 = Invalid;
dInst.csr = Invalid;
dInst.imm = Invalid;
end
else begin // fnCSRRWI, fnCSRRW, fnCSRRSI, fnCSRRS, fnCSRRCI, fnCSRRC
dInst.iType = Csr;
dInst.execFunc = (case (funct3)
fnCSRRWI, fnCSRRW: tagged Alu Csrw;
fnCSRRSI, fnCSRRS: tagged Alu Csrs;
fnCSRRCI, fnCSRRC: tagged Alu Csrc;
endcase);
regs.dst = Valid(tagged Gpr rd);
regs.src1 = Invalid; // going to be CSR Reg
regs.src2 = (funct3[2] == 0 ? Valid(tagged Gpr rs1) : Invalid);
dInst.imm = (funct3[2] == 0 ? Invalid : Valid(zeroExtend(rs1)));
dInst.csr = Valid(unpackCSR(truncate(immI)));
end
end
default: begin
illegalInst = True;
end
endcase
// XXX to ensure renaming + phy regs works correctly, must remove any write
// to x0
if(regs.dst matches tagged Valid .dst &&& dst == tagged Gpr 0) begin
regs.dst = tagged Invalid;
end
return DecodeResult{dInst: dInst, regs: regs, illegalInst: illegalInst};
endfunction
// All this does is add the CSR state to the decoding
function FpuInst updateRoundingMode(FpuInst fpu_f, CsrDecodeInfo csrState);
let new_fpu_f = fpu_f;
new_fpu_f.rm = (fpu_f.rm == RDyn) ? unpack(csrState.frm) : fpu_f.rm;
return new_fpu_f;
endfunction

View File

@@ -0,0 +1,85 @@
// Copyright (c) 2017 Massachusetts Institute of Technology
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
`include "ProcConfig.bsv"
import Assert::*;
import Types::*;
import ProcTypes::*;
import Vector::*;
import BrPred::*;
import Bht::*;
import GSelectPred::*;
import GSharePred::*;
import TourPred::*;
import TourPredSecure::*;
export DirPredTrainInfo(..);
export mkDirPredictor;
`ifdef DIR_PRED_BHT
typedef BhtTrainInfo DirPredTrainInfo;
`endif
`ifdef DIR_PRED_GSELECT
typedef GSelectTrainInfo DirPredTrainInfo;
`endif
`ifdef DIR_PRED_GSHARE
typedef GShareTrainInfo DirPredTrainInfo;
`endif
`ifdef DIR_PRED_TOUR
typedef TourTrainInfo DirPredTrainInfo;
`endif
(* synthesize *)
module mkDirPredictor(DirPredictor#(DirPredTrainInfo));
`ifdef DIR_PRED_BHT
`ifdef SECURITY
staticAssert(False, "BHT with flush methods is not implemented");
`endif
let m <- mkBht;
`endif
`ifdef DIR_PRED_GSELECT
`ifdef SECURITY
staticAssert(False, "GSelect with flush methods is not implemented");
`endif
let m <- mkGSelectPred;
`endif
`ifdef DIR_PRED_GSHARE
`ifdef SECURITY
staticAssert(False, "GShare with flush methods is not implemented");
`endif
let m <- mkGSharePred;
`endif
`ifdef DIR_PRED_TOUR
`ifdef SECURITY
let m <- mkTourPredSecure;
`else
let m <- mkTourPred;
`endif
`endif
return m;
endmodule

Some files were not shown because too many files have changed in this diff Show More