Associative 2-way associative BTB.
Also, 16-bit hashed tags. (This is because the only full-speed implementation required duplicating the tags). This implementation uses the MAP library, and a new BRAM instance of it.
This commit is contained in:
@@ -38,7 +38,7 @@
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import ConfigReg::*;
|
||||
import RWBramCore::*;
|
||||
import Map::*;
|
||||
import Vector::*;
|
||||
import CHERICC_Fat::*;
|
||||
import CHERICap::*;
|
||||
@@ -58,10 +58,14 @@ endinterface
|
||||
// Local BTB Typedefs
|
||||
typedef 1 PcLsbsIgnore;
|
||||
typedef 1024 BtbEntries;
|
||||
typedef 2 BtbAssociativity;
|
||||
typedef Bit#(TLog#(SupSizeX2)) BtbBank;
|
||||
typedef TDiv#(BtbEntries,SupSizeX2) BtbIndices;
|
||||
// Total entries/lanes of superscalar lookup/associativity
|
||||
typedef TDiv#(TDiv#(BtbEntries,SupSizeX2),BtbAssociativity) BtbIndices;
|
||||
typedef Bit#(TLog#(BtbIndices)) BtbIndex;
|
||||
typedef Bit#(TSub#(TSub#(AddrSz, TLog#(BtbEntries)), PcLsbsIgnore)) BtbTag;
|
||||
typedef Bit#(16) HashedTag;
|
||||
typedef Bit#(TSub#(TSub#(TSub#(AddrSz,SizeOf#(BtbBank)), SizeOf#(BtbIndex)), PcLsbsIgnore)) BtbTag;
|
||||
|
||||
typedef struct {
|
||||
BtbTag tag;
|
||||
BtbIndex index;
|
||||
@@ -75,82 +79,52 @@ typedef struct {
|
||||
} BtbUpdate deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
BtbTag tag;
|
||||
CapMem nextPc;
|
||||
} BtbRecord deriving(Bits, Eq, FShow);
|
||||
Bool v;
|
||||
data d;
|
||||
} VnD#(type data) deriving(Bits, Eq, FShow);
|
||||
|
||||
(* synthesize *)
|
||||
module mkBtb(NextAddrPred);
|
||||
// Read and Write ordering doesn't matter since this is a predictor
|
||||
// mkRegFileWCF is the RegFile version of mkConfigReg
|
||||
Reg#(BtbTag) tag_reg <- mkRegU;
|
||||
Reg#(BtbBank) firstBank_reg <- mkRegU;
|
||||
Vector#(SupSizeX2, Reg#(BtbIndex)) idxs_reg <- replicateM(mkRegU);
|
||||
Vector#(SupSizeX2, RWBramCore#(BtbIndex, BtbRecord)) records <- replicateM(mkRWBramCoreUG);
|
||||
Vector#(SupSizeX2, Vector#(BtbIndices, Reg#(Bool))) valid <- replicateM(replicateM(mkConfigReg(False)));
|
||||
|
||||
Vector#(SupSizeX2, MapSplit#(HashedTag, BtbIndex, VnD#(CapMem), BtbAssociativity))
|
||||
records <- replicateM(mkMapLossyBRAM);
|
||||
RWire#(BtbUpdate) updateEn <- mkRWire;
|
||||
|
||||
`ifdef SECURITY
|
||||
Reg#(Bool) flushDone <- mkReg(True);
|
||||
`else
|
||||
Bool flushDone = True;
|
||||
`endif
|
||||
|
||||
function BtbAddr getBtbAddr(CapMem pc) = unpack(truncateLSB(getAddr(pc)));
|
||||
function BtbBank getBank(CapMem pc) = getBtbAddr(pc).bank;
|
||||
function BtbIndex getIndex(CapMem pc) = getBtbAddr(pc).index;
|
||||
function BtbTag getTag(CapMem pc) = getBtbAddr(pc).tag;
|
||||
function MapKeyIndex#(HashedTag,BtbIndex) lookupKey(CapMem pc) =
|
||||
MapKeyIndex{key: hash(getTag(pc)), index: getIndex(pc)};
|
||||
|
||||
// no flush, accept update
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule canonUpdate(flushDone &&& updateEn.wget matches tagged Valid .upd);
|
||||
rule canonUpdate(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);
|
||||
let bank = getBank(pc);
|
||||
if(taken) begin
|
||||
valid[bank][index] <= True;
|
||||
records[bank].wrReq(index, BtbRecord{tag: tag, nextPc: nextPc});
|
||||
end else begin
|
||||
// current instruction had been prediceted taken, so clear its target in the TLB
|
||||
valid[bank][index] <= False;
|
||||
records[bank].wrReq(index, BtbRecord{tag: {4'ha,0}, nextPc: nextPc}); // An invalid virtual address.
|
||||
end
|
||||
/*$display("MapUpdate in BTB - pc %x, bank: %x, taken: %x, next: %x, time: %t",
|
||||
pc, getBank(pc), taken, nextPc, $time);*/
|
||||
records[getBank(pc)].update(lookupKey(pc), VnD{v:taken, d:nextPc});
|
||||
endrule
|
||||
|
||||
`ifdef SECURITY
|
||||
// flush, clear everything (and drop update)
|
||||
rule doFlush(!flushDone);
|
||||
for (Integer i = 0; i < valueOf(SupSizeX2); i = i + 1) writeVReg(valid[i], replicate(False));
|
||||
flushDone <= True;
|
||||
endrule
|
||||
`endif
|
||||
|
||||
method Action put_pc(CapMem pc);
|
||||
BtbAddr addr = getBtbAddr(pc);
|
||||
tag_reg <= addr.tag;
|
||||
firstBank_reg <= addr.bank;
|
||||
// Start SupSizeX2 BTB lookups, but ensure to lookup in the appropriate
|
||||
// bank for the alignment of each potential branch.
|
||||
for (Integer i = 0; i < valueOf(SupSizeX2); i = i + 1) begin
|
||||
BtbAddr a = unpack(pack(addr) + fromInteger(i));
|
||||
idxs_reg[a.bank] <= a.index;
|
||||
records[a.bank].rdReq(a.index);
|
||||
records[a.bank].lookupStart(MapKeyIndex{key: hash(a.tag), index: a.index});
|
||||
end
|
||||
endmethod
|
||||
|
||||
method Vector#(SupSizeX2, Maybe#(CapMem)) pred;
|
||||
Vector#(SupSizeX2, Maybe#(BtbRecord)) recs = ?;
|
||||
Vector#(SupSizeX2, Maybe#(CapMem)) ppcs = replicate(Invalid);
|
||||
for (Integer i = 0; i < valueOf(SupSizeX2); i = i + 1)
|
||||
recs[i] = (valid[i][idxs_reg[i]]) ? Valid(records[i].rdResp):Invalid;
|
||||
function Maybe#(CapMem) tagHit(Maybe#(BtbRecord) br) =
|
||||
case (br) matches
|
||||
tagged Valid .b &&& (tag_reg == b.tag): return Valid(b.nextPc);
|
||||
tagged Invalid: return Invalid;
|
||||
endcase;
|
||||
Vector#(SupSizeX2, Maybe#(CapMem)) ppcs = map(tagHit,recs);
|
||||
if (records[i].lookupRead matches tagged Valid .record)
|
||||
ppcs[i] = record.v ? Valid(record.d):Invalid;
|
||||
ppcs = rotateBy(ppcs,unpack(-firstBank_reg)); // Rotate firstBank down to zeroeth element.
|
||||
return ppcs;
|
||||
endmethod
|
||||
@@ -160,10 +134,10 @@ module mkBtb(NextAddrPred);
|
||||
endmethod
|
||||
|
||||
`ifdef SECURITY
|
||||
method Action flush if(flushDone);
|
||||
flushDone <= False;
|
||||
method Action flush method Action flush;
|
||||
for (Integer i = 0; i < valueOf(SupSizeX2); i = i + 1) records[i].clear;
|
||||
endmethod
|
||||
method flush_done = flushDone._read;
|
||||
method flush_done = records[0].clearDone;
|
||||
`else
|
||||
method flush = noAction;
|
||||
method flush_done = True;
|
||||
|
||||
@@ -31,36 +31,137 @@
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import DReg::*;
|
||||
import RegFile::*;
|
||||
import Vector::*;
|
||||
import RWBramCore::*;
|
||||
import Ehr::*;
|
||||
|
||||
typedef struct {
|
||||
ky key;
|
||||
ix index;
|
||||
} MapKeyIndex#(type ky, type ix) deriving(Bits, Eq, FShow);
|
||||
typedef struct {
|
||||
ky key;
|
||||
vl value;
|
||||
} MapKeyValue#(type ky, type vl) deriving(Bits, Eq, FShow);
|
||||
typedef struct {
|
||||
ky key;
|
||||
ix index;
|
||||
vl value;
|
||||
} MapKeyIndexValue#(type ky, type ix, type vl) deriving(Bits, Eq, FShow);
|
||||
|
||||
// Type parameters are for index and key (which together are the "address"),
|
||||
// the value stored in the map, and the associativity of the storage.
|
||||
interface Map#(type ix, type ky, type vl, numeric type as);
|
||||
method Action update(Tuple2#(ky,ix) key, vl value);
|
||||
method Maybe#(vl) lookup(Tuple2#(ky,ix) lookup_key);
|
||||
interface Map#(type ky, type ix, type vl, numeric type as);
|
||||
method Action update(MapKeyIndex#(ky,ix) key, vl value);
|
||||
method Maybe#(vl) lookup(MapKeyIndex#(ky,ix) lookup_key);
|
||||
method Action clear;
|
||||
method Bool clearDone;
|
||||
endinterface
|
||||
|
||||
module mkMapLossy(Map#(ix,ky,vl,as)) provisos (
|
||||
module mkMapLossy(Map#(ky,ix,vl,as)) provisos (
|
||||
Bits#(ky,ky_sz), Bits#(vl,vl_sz), Eq#(ky), Arith#(ky),
|
||||
Bounded#(ix), Literal#(ix), Bits#(ix, ix_sz));
|
||||
Vector#(as, RegFile#(ix, Tuple2#(ky,vl))) mem
|
||||
Bounded#(ix), Literal#(ix), Bits#(ix, ix_sz),
|
||||
Bitwise#(ix), Eq#(ix), Arith#(ix));
|
||||
Vector#(as, RegFile#(ix, MapKeyValue#(ky,vl))) mem
|
||||
<- replicateM(mkRegFileWCF(0, maxBound));
|
||||
Reg#(Bit#(TLog#(as))) wayNext <- mkReg(0);
|
||||
Integer a = valueof(as);
|
||||
|
||||
method Action update(Tuple2#(ky,ix) ki, vl value);
|
||||
match {.key, .index} = ki;
|
||||
mem[wayNext].upd(index, tuple2(key, value));
|
||||
Reg#(Bool) clearReg <- mkReg(False);
|
||||
Reg#(ix) clearCount <- mkReg(0);
|
||||
PulseWire didUpdate <- mkPulseWire;
|
||||
rule doClear(clearReg && !didUpdate);
|
||||
for (Integer i = 0; i < a; i = i + 1) mem[i].upd(clearCount, unpack(0));
|
||||
clearCount <= clearCount + 1;
|
||||
if (clearCount == ~0) clearReg <= False;
|
||||
endrule
|
||||
|
||||
method Action update(MapKeyIndex#(ky,ix) ki, vl value);
|
||||
Bit#(TLog#(as)) way = wayNext;
|
||||
if (a > 1) begin
|
||||
for (Integer i = 0; i < a; i = i + 1)
|
||||
if (mem[i].sub(ki.index).key == ki.key) way = fromInteger(i);
|
||||
end
|
||||
mem[way].upd(ki.index, MapKeyValue{key: ki.key, value: value});
|
||||
wayNext <= (wayNext == fromInteger(a-1)) ? 0: wayNext + 1;
|
||||
didUpdate.send;
|
||||
endmethod
|
||||
method Maybe#(vl) lookup(Tuple2#(ky,ix) ki);
|
||||
match {.lookup_key, .index} = ki;
|
||||
method Maybe#(vl) lookup(MapKeyIndex#(ky,ix) lu);
|
||||
Maybe#(vl) ret = Invalid;
|
||||
for (Integer i = 0; i < a; i = i + 1) begin
|
||||
match {.key, .value} = mem[i].sub(index);
|
||||
if (key == lookup_key) ret = Valid(value);
|
||||
let rd = mem[i].sub(lu.index);
|
||||
if (rd.key == lu.key) ret = Valid(rd.value);
|
||||
end
|
||||
return ret;
|
||||
endmethod
|
||||
method clear if (!clearReg) = clearReg._write(True);
|
||||
method clearDone = clearReg;
|
||||
endmodule
|
||||
|
||||
interface MapSplit#(type ky, type ix, type vl, numeric type as);
|
||||
method Action update(MapKeyIndex#(ky,ix) key, vl value);
|
||||
method Action lookupStart(MapKeyIndex#(ky,ix) lookup_key);
|
||||
method Maybe#(vl) lookupRead;
|
||||
method Action clear;
|
||||
method Bool clearDone;
|
||||
endinterface
|
||||
|
||||
module mkMapLossyBRAM(MapSplit#(ky,ix,vl,as)) provisos (
|
||||
Bits#(ky,ky_sz), Bits#(vl,vl_sz), Eq#(ky), Arith#(ky),
|
||||
Bounded#(ix), Literal#(ix), Bits#(ix, ix_sz),
|
||||
Bitwise#(ix), Eq#(ix), Arith#(ix), PrimIndex#(ix, a__));
|
||||
Vector#(as, RWBramCore#(ix, MapKeyValue#(ky,vl))) mem <- replicateM(mkRWBramCoreUG);
|
||||
Vector#(as, RWBramCore#(ix, ky)) updateKeys <- replicateM(mkRWBramCoreUG);
|
||||
Reg#(MapKeyIndex#(ky,ix)) lookupReg <- mkRegU;
|
||||
Reg#(MapKeyIndexValue#(ky,ix,vl)) updateReg <- mkRegU;
|
||||
Reg#(Bool) updateFresh <- mkDReg(False);
|
||||
Reg#(Bit#(TLog#(as))) wayNext <- mkReg(0);
|
||||
Integer a = valueof(as);
|
||||
|
||||
Reg#(Bool) clearReg <- mkReg(False);
|
||||
Reg#(ix) clearCount <- mkReg(0);
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule updateCanon;
|
||||
if (updateFresh) begin
|
||||
let u = updateReg;
|
||||
Bit#(TLog#(as)) way = wayNext;
|
||||
for (Integer i = 0; i < a; i = i + 1)
|
||||
if (updateKeys[i].rdResp == u.key) way = fromInteger(i);
|
||||
// Always write to both the main memory bank and the copy used for updates.
|
||||
/*$display("MapUpdate - index: %x, key: %x, value: %x, way: %x",
|
||||
u.index, u.key, u.value, way);*/
|
||||
mem[way].wrReq(u.index, MapKeyValue{key: u.key, value: u.value});
|
||||
updateKeys[way].wrReq(u.index, u.key);
|
||||
wayNext <= (wayNext == fromInteger(a-1)) ? 0 : (wayNext + 1);
|
||||
end else if (clearReg) begin
|
||||
for (Integer i = 0; i < a; i = i + 1) mem[i].wrReq(clearCount, unpack(0));
|
||||
clearCount <= clearCount + 1;
|
||||
if (clearCount == ~0) clearReg <= False;
|
||||
end
|
||||
endrule
|
||||
|
||||
method Action update(MapKeyIndex#(ky,ix) ki, vl value);
|
||||
updateReg <= MapKeyIndexValue{key: ki.key, index: ki.index, value: value};
|
||||
updateFresh <= True;
|
||||
for (Integer i = 0; i < a; i = i + 1) updateKeys[i].rdReq(ki.index);
|
||||
endmethod
|
||||
method Action lookupStart(MapKeyIndex#(ky,ix) ki);
|
||||
lookupReg <= ki;
|
||||
for (Integer i = 0; i < a; i = i + 1) mem[i].rdReq(ki.index);
|
||||
endmethod
|
||||
method Maybe#(vl) lookupRead;
|
||||
Maybe#(vl) readVal = Invalid;
|
||||
for (Integer i = 0; i < a; i = i + 1) begin
|
||||
let resp = mem[i].rdResp;
|
||||
if (lookupReg.key == resp.key) readVal = Valid(resp.value);
|
||||
end
|
||||
// If there has been a recent write, take that one.
|
||||
if (updateReg.index == lookupReg.index && updateReg.key == lookupReg.key)
|
||||
readVal = Valid(updateReg.value);
|
||||
return readVal;
|
||||
endmethod
|
||||
method clear if (!clearReg) = clearReg._write(True);
|
||||
method clearDone = clearReg;
|
||||
endmodule
|
||||
|
||||
Reference in New Issue
Block a user