Get a physical addr access request, and return a tag associated with this physical address.
This is a cache impl based on CacheCore, similar to D/ICache, L2Cache.
One Covered Region is a cached tag region. It is of type LineTags
, which is a vector (of size 4) of tags, each vector element contains the tags for one flit (i.e. tags for data in one CheriMemRequest)
CheriTagResponse – LineTags – 4 * Bit#(CapsPerFlit)
CapsPerFlit = number of capabilities in a flit of memory request/response.
// cherilibs/trunk/TagLookup.bsv
// interface types
///////////////////////////////////////////////////////////////////////////////
typedef Vector#(4,Bit#(CapsPerFlit)) LineTags;
typedef union tagged {
void Uncovered;
LineTags Covered;
} CheriTagResponse deriving (Bits);
interface TagLookupIfc;
interface Slave#(CheriMemRequest, CheriTagResponse) cache;
interface Master#(CheriMemRequest, CheriMemResponse) memory;
`ifdef STATCOUNTERS
interface Get#(ModuleEvents) cacheEvents;
`endif
endinterface
Important types:
One line contains four flits (four mem-bus-wide data):
typedef TMul#(CapsPerFlit,4) CapsPerLine;
A LineTags is a 4-ele vector of type Bit#(CapsPerFlit); each tag cache line could store tags for 4 flits of data (4 memory transmission on the memory bus).
256-bit flit line:
64-bit caps: caps per flit = 4; 256 cap tags cover 256 * 64 = 16 KB memory; LineTags contain 4 * 4 tags;
// cherilibs/trunk/TagLookup.bsv
typedef Vector#(4,Bit#(CapsPerFlit)) LineTags;
typedef union tagged {
void Uncovered;
LineTags Covered;
} CheriTagResponse deriving (Bits);
FIFOs:
mRsps
// state register
Reg#(State) state <- mkReg(Init);
// address to zero when in Init state
Reg#(CheriPhyAddr) zeroAddr <- mkReg(tagTabStrtAddr);
// transaction number for memory requests
Reg#(CheriTransactionID) transNum <- mkReg(0);
// pending read requests fifo
// pending read requests fifo
FF#(Tuple2#(Bool,CheriPhyBitOffset),4) readReqs <- mkFF();
// memory requests fifo
FF#(CheriMemRequest, 5) mReqs <- mkUGFFDebug("TagLookup_mReqs");
// memory response fifo
FF#(CheriMemResponse, 2) mRsps <- mkUGFFDebug("TagLookup_mRsps");
// tag cache CacheCore module
CacheCore#(4, TSub#(Indices, 1), 1) tagCache <- mkCacheCore(
12, WriteAllocate, OnlyReadResponses, InOrder, TCache,
zeroExtend(mReqs.remaining()), ff2fifof(mReqs), ff2fifof(mRsps));
// state register Reg#(State) state <- mkReg(Init); // address to zero when in Init state Reg#(CheriPhyAddr) zeroAddr <- mkReg(tagTabStrtAddr); // transaction number for memory requests Reg#(CheriTransactionID) transNum <- mkReg(0); // pending read requests fifo
Two subinterfaces:
interface CheckedPut request; // the interface to receive request from outside;
interface CheckedGet response; // the interface to send response out;
Overview: the interface to receive tag r/w request from tagController; It
It contains the follow two methods:
method Bool canPut() = state == Serving;
// can receive request when Serving
method Action put(CheriMemRequest req) if (state == Serving);
// got a new request and put in queue tagCache.put(mReq)
The put
method:
function Bool isCovered (CheriPhyAddr addr);
Bool r = True;
if (addr < coveredStrtAddr && addr >= coveredEndAddr) r = False;
if (addr >= tagTabStrtAddr && addr < tagTabEndAddr) r = False;
return r;
endfunction
// interface Slave cache:
// interface CheckedPut request:
method Action put(CheriMemRequest req) if (state == Serving);
// check whether we are in the covered region
Bool doTagLookup = isCovered(req.addr);
// various addresses variables
CheriCapAddress capAddr = unpack(pack(req.addr));
CheriPhyAddr tblAddr = tagTabStrtAddr;
// The byte address in the table is the line number >> 3 (the byte we seek) added to the table base.
// Only caclulate the table address for lower general-purpose memory.
tblAddr = unpack(zeroExtend(capAddr.capNumber>>3) + pack(tagTabStrtAddr));
// build the request to the tag cache
// common part of the request
CheriMemRequest mReq = defaultValue;
mReq.addr = tblAddr;
mReq.masterID = mID;
mReq.transactionID = transNum;
case (req.operation) matches
// when it's a read
//////////////////////////////
tagged Read .rop: begin
mReq.operation = tagged Read {
uncached: False,
linked: False,
noOfFlits: 0,
bytesPerFlit: cheriBusBytes
};
readReqs.enq(tuple2(doTagLookup,truncate(capAddr.capNumber)));
end
// when it's a write
//////////////////////////////
tagged Write .wop: begin
Vector#(CapBytes, Bool) byteEnable = replicate(False);
// select the table byte to write
byteEnable[tblAddr.byteOffset] = True;
// select the table bits to write
Bit#(3) bitOffset = capAddr.capNumber[2:0];
Bit#(8) bitEnable = 0;
Bit#(8) tw = 0;
Integer i = 0;
for (i = 0; i < valueOf(CapsPerFlit); i = i + 1) begin
bitEnable[bitOffset+fromInteger(i)] = 1;
tw[bitOffset+fromInteger(i)] = pack(wop.data.cap[i]);
end
// Just replicate the byte and let the byte and bit select choose the bits to write.
CheriData tagsToWrite = Data{
cap: ?,
data: pack(replicate(tw))
};
mReq.operation = tagged Write {
uncached: False,
conditional: False,
byteEnable: byteEnable,
bitEnable: bitEnable,
data: tagsToWrite,
last: True
};
end
// ignore other types of requests
default: doTagLookup = False;
endcase
// when a lookup is required
if (doTagLookup) begin
debug2("taglookup",
$display("<time %0t TagLookup> Request to cache core ",
$time, fshow(mReq)
));
// send the tag cache request
tagCache.put(mReq);
// increment the transaction number
transNum <= transNum + 1;
end
endmethod
call isCovered
: check whether the request addr is in covered memory, if not, the request would be an invalid physical address for the current hardware setting. see physical memory regions
calculate the byte address in the table:
tblAddr = unpack(zeroExtend(capAddr.capNumber>>3) + pack(tagTabStrtAddr));
capAddr
is byte address for an capability, with CapNumber
is the cap aligned address and offset as byte index into the capability, see CheriCapAddress definitionCapNumber >> 3
, gives the 8-capability size aligned physical address, which has 8-bit tags;
CapNumber >> 2
, gives the 4-capability size aligned physical address, which corresponds to 4 bits of cap tags and 4-bits of color tags;craft the memory request to the tag cache:
CheriMemRequest mReq
, with .masterID
to be 12
means a tag request.readReqs.enq(tuple2(doTagLookup,truncate(capAddr.capNumber)));
The interface defines the get
method, which send response back to tagController.
// lookup Slave response interface
//////////////////////////////////////////////////////
interface CheckedGet response;
method Bool canGet() = tagCache.response.canGet();
method CheriTagResponse peek();
CheriMemResponse mRsp = tagCache.response.peek();
return tagsFromCacheRsp(mRsp);
endmethod
method ActionValue#(CheriTagResponse) get() if (tagCache.response.canGet());
// get response from the tag cache and read fifo
CheriMemResponse mRsp <- tagCache.response.get();
readReqs.deq();
// debug
debug2("taglookup",
$display(
"<time %0t TagLookup> got valid cache response ",
$time, fshow(mRsp)
));
return tagsFromCacheRsp(mRsp);
endmethod
endinterface
To prepare a response:
CheriMemResponse mRsp
from tagCache.response
readReqs
// why? dequeue even if a write?CheriMemResponse
and return as CheriTagResponse
, via tagsFromCacheRsp(mRsp)
.The definition of tagsFromCacheRsp
gives the details how a regular memory response is converted into a tag response. See [./#function-tagsfromcachersp]
// cherilibs/trunk/TagLookup.bsv
function CheriTagResponse tagsFromCacheRsp (CheriMemResponse mr);
CheriTagResponse r;
Data#(CheriDataWidth) rData = mr.data;
match {.covered,.offset} = readReqs.first();
// Cast to a vector of tag chunks. The chunks are the set of tags for one line.
Vector#(TDiv#(CheriDataWidth,CapsPerLine), Bit#(CapsPerLine)) sets = unpack(rData.data);
// Pick out the index
CheriPhyBitOffset index = (offset >> valueOf(TLog#(CapsPerLine)));
// Shift by the bottom two bits so that non-burst requests will have
// the tag bit in the bottom.
Bit#(TLog#(CapsPerLine)) shift = truncate(offset);
LineTags thisSet = unpack(sets[index] >> shift);
if (!covered) r = tagged Uncovered;
else r = tagged Covered thisSet;
return r;
endfunction
readReqs
, store as {.covered, .offset}CheriMemResponse
, convert it to a vector of type Bit#(CapsPerLine)
: Vector#(TDiv#(CheriDataWidth,CapsPerLine), Bit#(CapsPerLine)) sets = unpack(rData.data);
: one set is contains CapsPerLine
number of tag bits, which is seen as the 4 times of Tags inside one Data# (Caps in one flit).Why tags for four
Data# in one set? the burst? Note: MultiLevelTagLookup.bsv does not use this CapsPerLine
.
return all four
Data# tags as response.
==> **One CheriMemResponse.data = CheriDataWidth > **
Interface of request and response is connected with mReqs
and mRsps
:
mReqs
and mRsps
are regular memory request that are no difference with non-tag memory access.
// module Master interface
/////////////////////////////////////////////////////////////////////////////
interface Master memory;
interface request = toCheckedGet(ff2fifof(mReqs));
interface response = toCheckedPut(ff2fifof(mRsps));
endinterface
zero out all the tags table in memory.
When table zeroed, go to Serving state.
Constant physical memory region
tagRegion:totalRegion should be
Now the ratio is 2^24:2^30 = 1: 2^6 = 1: 64 // good
region | start addr – end addr | size in hex | size |
---|---|---|---|
total in use | 40’h0000,0000 – 40’h4000,0000 | h4000,0000 | 2^30 1GB |
tag mem | 40’h3F00,0000 – 40’h4000,0000 | h0100,0000 | 2^24 16MB |
regular mem | 40’h0000,0000 – 40’h3EFF,FFFF | h3F00,0000 | 1024-16=1008 MB |
Now support only 1GB physical memory?!
// file: cherilibs/trunk/TagLoopup.bsv
// covered region include DRAM and BROM
// starting address of the covered region
CheriPhyAddr coveredStrtAddr = unpack(40'h00000000);
// ending address of the covered region
CheriPhyAddr coveredEndAddr = unpack(40'h40010000); // Lele: 1GB + 64KB?
// tagd table is at top of DRAM
// starting address of the tags table
CheriPhyAddr tagTabStrtAddr = unpack(40'h3F000000);
// ending address of the tags table
CheriPhyAddr tagTabEndAddr = unpack(40'h40000000);
Reference 1
If you could revise
the fundmental principles of
computer system design
to improve security...
... what would you change?