Reference 1
The type of Data#, CapTags, CapsPerFlit, and BytesPerFlit.
It contains both data and capability tag.
TODO: What is the data_width
? and the relationship between number of tag bits and this data_width
, and the CHERI bits?
Data# definition:
// Data type
typedef struct {
`ifdef USECAP
// is this frame has capabilities
CapTags cap;
`endif
// actual data
Bit#(width) data;
} Data#(numeric type width) deriving (Bits, Eq, FShow);
CapTags definition
CapsPerFlit
is originally defined as the number of capabilities on a bus width data transmission, i.e. one flit transmission.
used to define CapNumber/CheriCapAddress
`ifdef USECAP
typedef TDiv#(CheriBusBytes,CapBytes) CapsPerFlit;
typedef Vector#(CapsPerFlit,Bool) CapTags;
typedef Bit#(TSub#(40,TLog#(CapBytes))) CapNumber;
typedef struct {
CapNumber capNumber;
Bit#(TLog#(CapBytes)) offset;
} CheriCapAddress deriving (Bits, Eq, Bounded, FShow);
`endif
see CapBytes
There can be many caps per flit; According to …, one CheriMemRequest/Response will have exactly one flit of data; then a CheriMemRequest/Response will have one flit with >=1 tags in it.
For example, in CacheCore.bsv: rule finishLookup. ???
// cherilibs/trunk/CacheCore.bsv: rule finishLookup: 1368-1419
// Only finish the write if this is the next operation in order.
if (req.operation matches tagged Write .wop &&& !dead) begin
cacheResp.operation = tagged Write;
if (cachedWrite) begin
//Construct new line.
function Byte choose(Byte o, Byte n, Bool sel) = (sel) ? ((n&wop.bitEnable)|(o&~wop.bitEnable)):o;
// zipWith3 combines the three vectors with the function "choose", defined above, producing another vector.
// In this case it is just selecting the old byte or new byte based on byteEnable.
Vector#(CheriBusBytes,Byte) maskedWriteVec = zipWith3(choose, unpack(dataRead.data), unpack(wop.data.data), wop.byteEnable);
Data#(CheriDataWidth) maskedWrite = wop.data;
maskedWrite.data = pack(maskedWriteVec);
`ifdef USECAP
// Fold in capability tags.
CapTags capTags = dataRead.cap;
$display("wop.byteEnable: %x, capTags: %x, wop.data.cap: %x", wop.byteEnable, capTags, wop.data.cap);
Integer i;
for (i=0; i<valueOf(CapsPerFlit); i=i+1) begin
Integer bot = i*valueOf(CapBytes);
Integer top = bot + valueOf(CapBytes) - 1;
Bit#(CapBytes) capBytes = pack(wop.byteEnable)[top:bot];
if (capBytes != 0) capTags[i] = wop.data.cap[i];
end
//$display("capTags: %x", capTags);
maskedWrite.cap = capTags;
`ifdef STATCOUNTERS
cacheCoreEvents.incSetTagWrite = pack(capTags) != 0;
`endif
`endif
//Write updated line to cache.
debug2("CacheCore", $display("<time %0t, cache %0d, CacheCore> wrote cache bank %x, way %x with %x",$time, cacheId,
{addr.key, addr.bank}, way, maskedWrite));
dataRead = maskedWrite;
data[way].write(DataKey{key:addr.key, bank:addr.bank}, dataRead);
`ifdef WRITEBACK_DCACHE
if (supportDirtyBytes) begin
// Update the dirty bytes if we didn't write through. Could check performWritethrough, but possibly checking doMemRequest is more reliable.
if (!writeThrough && !doMemRequest) dirties = unpack(pack(dirties)|pack(wop.byteEnable)); // Mark newly written bytes as dirty.
else dirties = unpack(pack(dirties)&~pack(wop.byteEnable)); // If we have written through, these bytes are clean.
dirtyBytes[way].write(DataKey{key:addr.key, bank:addr.bank}, dirties);
debug2("CacheCore", $display("<time %0t, cache %0d, CacheCore> updated dirties bank %x, way %x with %x, doMemRequest: %d",$time, cacheId,
{addr.key, addr.bank}, way, dirties, doMemRequest));
end
`endif
respValid = True;
end
// If this is a store conditional and we're not handling it,
// the response is coming later.
if (conditional && (performWritethrough)) dead = True;
if (supportInvalidates && writethroughNext.notEmpty && doMemRequest && !dead) writethroughNext.deq;
if (miss && (performWritethrough)) respValid = True;
if (wop.uncached) respValid = True;
end
end
Flit is a chunk of data with the actual size being requested/responsed, it is also equal to the data width of memory data bus. But the memory could be read in ‘burst’, where multiple ‘flit’, or multiple bus width data, will be returned (in multipel bus cycles) even if only one ‘flit’ is useful in this request.
What does this mean?
Max number of flits is 8; The max number of flits to be returned in a read request.
// cherilibs/trunk/MemTypes.bsv
typedef 8 MaxTransactions;
typedef 8 MaxNoOfFlits;
The memory request has a field noting the bytes per flit:
mReq.operation.bytesPerFlit
, which is usually set to be CheriBusBytes.
// file: cherilibs/trunk/MultiLevelTagLookup.bsv
// A CheriMemRequest mReq
mReq.operation
= tagged Read {
uncached: False,
linked: False,
noOfFlits: 0, // Lele: why 0???
bytesPerFlit: cheriBusBytes // one flit == busbytes
};
// cherilibs/trunk/MemTypes.bsv
// bytes per flit
typedef enum {
BYTE_1 = 0, // 8 bits
BYTE_2 = 1, // 16 bits
BYTE_4 = 2, // 32 bits // used in ICache.
BYTE_8 = 3, // 64 bits
BYTE_16 = 4, // 128 bits
BYTE_32 = 5, // 256 bits
BYTE_64 = 6, // 512 bits
BYTE_128 = 7 // 1024 bits
} BytesPerFlit deriving (Bits, Eq, Bounded, FShow);
// cherilibs/trunk/MemTypes.bsv
// physical address for cheri
`ifdef MEM128
typedef 16 CheriBusBytes;
BytesPerFlit cheriBusBytes = BYTE_16; //4
`elsif MEM64
typedef 8 CheriBusBytes;
BytesPerFlit cheriBusBytes = BYTE_8; //3
`else
typedef 32 CheriBusBytes;
BytesPerFlit cheriBusBytes = BYTE_32; //5
`endif
`ifdef CAP
BytesPerFlit capBytesPerFlit = BYTE_32;
`elsif CAP128
BytesPerFlit capBytesPerFlit = BYTE_16;
`elsif CAP64
BytesPerFlit capBytesPerFlit = BYTE_8;
`endif
typedef struct { // byte address addr_t addr; … union tagged { // read operation struct { // uncached / cached access Bool uncached; // LL / standard load Bool linked; // number of flits to be returned UInt#(TLog#(MaxNoOfFlits)) noOfFlits; // number of bytes per flit BytesPerFlit bytesPerFlit; } Read; … } … } MemoryRequest#(type addr_t, type masterid_t, type transactionid_t, numeric type data_width) deriving (Bits);
// cherilibs/trunk/Burst.bsv
interface Master master;
interface CheckedGet request;
method Bool canGet();
return req_fifo.notEmpty();
endmethod
method CheriMemRequest peek();
return req;
endmethod
method ActionValue#(CheriMemRequest) get();
CheriMemRequest reqIn = req_fifo.first();
if (reqIn.operation matches tagged Read .rop) begin
if (rop.noOfFlits == flit) begin
flit <= 0;
req_fifo.deq();
last_fifo.enq(True);
end else begin
flit <= flit + 1;
last_fifo.enq(False);
end
end else begin
req_fifo.deq();
last_fifo.enq(True);
end
return req;
endmethod
endinterface
interface CheckedPut response;
method Bool canPut();
return resp_fifo.notFull();
endmethod
method Action put(CheriMemResponse resp);
if (resp.operation matches tagged Read .rop)
resp.operation = tagged Read{last: last_fifo.first};
last_fifo.deq();
resp_fifo.enq(resp);
endmethod
endinterface
endinterface
endmodule
If you could revise
the fundmental principles of
computer system design
to improve security...
... what would you change?