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:
jon
2021-03-24 09:56:25 +00:00
parent 20e2249761
commit 07dd70d77b
2 changed files with 141 additions and 66 deletions

View File

@@ -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;

View File

@@ -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