From 07dd70d77b8d7dc2dd51f73a9f3ff2c5e1226061 Mon Sep 17 00:00:00 2001 From: jon <> Date: Wed, 24 Mar 2021 09:56:25 +0000 Subject: [PATCH] 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. --- src_Core/RISCY_OOO/procs/lib/Btb.bsv | 80 ++++++----------- src_Core/RISCY_OOO/procs/lib/Map.bsv | 127 ++++++++++++++++++++++++--- 2 files changed, 141 insertions(+), 66 deletions(-) diff --git a/src_Core/RISCY_OOO/procs/lib/Btb.bsv b/src_Core/RISCY_OOO/procs/lib/Btb.bsv index 17ab7ca..be3d636 100644 --- a/src_Core/RISCY_OOO/procs/lib/Btb.bsv +++ b/src_Core/RISCY_OOO/procs/lib/Btb.bsv @@ -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; diff --git a/src_Core/RISCY_OOO/procs/lib/Map.bsv b/src_Core/RISCY_OOO/procs/lib/Map.bsv index d43a2fc..c6b47c9 100644 --- a/src_Core/RISCY_OOO/procs/lib/Map.bsv +++ b/src_Core/RISCY_OOO/procs/lib/Map.bsv @@ -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