Data

Reference 1

The type of Data#, CapTags, CapsPerFlit, and BytesPerFlit.

See CheriBusBytes in MemTypes

Data

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

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

BytesPerFlit

What is the flit here?

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

BytesPerFlit and CheriBusBytes

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

BytesPerFlit in Memory Reqeust

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

Flit in Burst.bsv

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

  1. reference ↩
Created May 8, 2020 // Last Updated May 18, 2021

If you could revise
the fundmental principles of
computer system design
to improve security...

... what would you change?