Tag Lookup


Q&A

How many tags it caches for physical memory access request?


Overview

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.

Interfaces - TagLookupIfc

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

Type CapsPerLine

Important types:

One line contains four flits (four mem-bus-wide data):

typedef TMul#(CapsPerFlit,4) CapsPerLine;

Return struct CheriTagResponse

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:

  • 256-bit caps: caps per flit = 1; 256 cap tags cover 256 * 256 bit = 64KB memory; LineTags contain 4 * 1 tags; ==> wasted 252 bits???
  • 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);

States

FIFOs:

  • readReqs
  • mReqs
  • 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

Interface Slave

Two subinterfaces:

  • interface CheckedPut request; // the interface to receive request from outside;

    • method Bool canPut();
    • method Action put(CheriMemRequest req)
  • interface CheckedGet response; // the interface to send response out;

    • method Bool canGet();
    • method CheriTagResponse peek();
    • method ActionValue#(CheriTagResponse) get() if (tagCache.response.canGet());

Slave request

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

    • covered region: [coveredStrtAddr, coveredEndAddr] - [tagTabStrtAddr, tagTabEndAddr)
    • tagRegion:dataRegion:totalRegion should be 1:256:257 or 1:128:129, 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 definition
    • CapNumber >> 3, gives the 8-capability size aligned physical address, which has 8-bit tags;
      • To extend it from 1-bit=tag per cap to a 2-bit=tag per cap: use 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.
    • if read, enqueue fifo readReqs.enq(tuple2(doTagLookup,truncate(capAddr.capNumber)));
    • if write, set the index of bits need to be written

Slave response

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:

  • grab a response CheriMemResponse mRsp from tagCache.response
  • dequeue an element from readReqs // why? dequeue even if a write?
  • pass the response 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]

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
  • get first in readReqs, store as {.covered, .offset}
  • unpack the data in memory response struct 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 Master

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

rule initialize

zero out all the tags table in memory.

When table zeroed, go to Serving state.

Physical Memory Region

Constant physical memory region

tagRegion:totalRegion should be

  • >= 1:257, 1:128, or
  • >=2:258, 1:64; or
  • >=3:259, 1:64; or
  • >=4:260, 1:64; or

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


  1. github/beri ↩
Created May 8, 2020 // Last Updated May 23, 2020

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

... what would you change?