MIPSTop.bsv


Q&A

What is the specification of memory interface ?

No mater what the capability width is, the MIPS processor has a memory interface of 256-bit data width (WORD width), 35-bit WORD address width. As there are 2^5 = 32 bytes = 256 bits per word, this is equivalent to a 35 + 5 = 40 byte address.

==> Memory interface is not byte addressable, but only world addressable; But processor could address in bytes inside itself.

==> Not 64-bit addressable processor. 24-bits left unused for memory access.

Can CPU reuse the 24-bit unused memory address?

This is physical address, which highly depends on the actual hardware interface implementation. So if this is possible, it will not have much portability.

If MMU exists: will also have the unused bits of virtual address, and the physical address. OS can manage both virtual and physical address, but application will not be able to inspect the physical address.

If no MMU: probably can embed more infomation in the address.


The mkMIPSTop module instantiates the main processor pipeline and memory hierarchy. 1

Overview

Methods:

  • reset_n()
  • getPause()
  • putState(…)

Rules:

  • instructionFetch
  • debugInstructionFetch
  • fromFetchToScheduler
  • fromSchedulerToDecode
  • fromDecodeToExecute
  • fromExecuteToMemAccess
  • tick
  • putResetOut
  • Other Rules:
    • doInit0(init0)
    • cop1ToMem
    • memToCop1
    • cop3ToMem
    • MemToCop3
    • reportCacheConfig
    • reportCommittingToDCache

interface MIPSTopIfc

MIPSTopIfc is the top level processor interface, exporting the memory interface, interrupts, and debug interface.

No mater what the capability width is, the MIPS processor, has a memory interface of 256-bit data width,

35-bit WORD address width. As there are 2^5 = 32 bytes = 256 bits per word, this is equivalent to a 35 + 5 = 40 byte address.

// file: cheri/trunk/MIPSTopIfc.bsv

// MIPSTopIfc is the interface for the processor top level, exporting the memory
// interface as well as interrupts and a debug interface.
interface MIPSTopIfc;
  `ifdef MULTI
    // Instruction cache invalidate interface
    method Action invalidateICache(PhyAddress addr);
    // Data cache invalidate interface
    method Action invalidateDCache(PhyAddress addr);
    method ActionValue#(Bool) getInvalidateDone;
    interface Master#(CheriMemRequest, CheriMemResponse) imemory;
    interface Master#(CheriMemRequest, CheriMemResponse) dmemory;
  `else
    // Memory client interface (which initializes transactions), 256 bit data
    // width, 35-bit WORD address width. As there are 2^5 = 32 bytes per word,
    // this is equivalent to a 35 + 5 = 40-bit byte address.
    interface Master#(CheriMemRequest, CheriMemResponse) memory;
  `endif
  // interface below is required for the multiport L2Cache
  //interface Client#(MemoryRequest#(35, 32), BigMemoryResponse#(256)) memory;
  // 5 interrupt lines, matching the standard MIPS spec.
  (* always_ready, always_enabled *)
  // Deliver common state to this core.
  method Action putState(Bit#(48) count, Bool pause, Bit#(5) interruptLines);
  // Tell the system to pause.  This should pause all cores.
  method Bool getPause();
  // The debug interface is a byte stream interface, a channel of bytes in and a
  // channel of bytes out.
  interface Server#(Bit#(8), Bit#(8)) debugStream;
    // Also a reset out interface. This allows us to reset the system and also
    // ourselves (if it is fed back in).
  method Bool reset_n();
  // Whether we want the trace unit to be recording at each cycle.

  // For testing the memory sub-system
  interface MIPSMemory mipsMemory;
  
  `ifdef DMA_VIRT
      interface Vector#(2, Server#(TlbRequest, TlbResponse)) tlbs;
  `endif
endinterface

Memory protection modules in the pipeline

  • mkMIPSMemory
  • mkCapCop
  • mkMemAccess
  • mkWriteback
  • mkScheduler
  • mkExecute

Full code of memory access module initiation:

// cheri/trunk/MIPSTop.bsv: 167

 // theMem is the memory hierarchy which needs the system control processor
  // for TLB integration.
  MIPSMemory theMem <- mkMIPSMemory(coreId,theCP0);

//cheri/trunk/MIPSTop.bsv: 178

 // These are the module instantiations for the "Capability" case which
  // includes our memory protection extensions.
  `ifdef USECAP
      // theCapCop is the "Capability coprocessor", logically MIPS coprocessor
      // 2, which inserts itself into the general purpose pipeline for register
      // reads and writes and which also becomes part of the memory path.
      CapCopIfc theCapCop <- mkCapCop(coreId);
      
      // memAccess is the memory access stage of the pipeline which imports data
      // memory and the capability coprocessor interface
      PipeStageIfc memAccess <- mkMemAccess(theMem.dataMemory, theCapCop);
      // The writeback stage of the pipeline imports lots of interfaces because
      // it updates all system state that results from an instruction commit.
      `ifndef STATCOUNTERS
      WritebackIfc writeback <- mkWriteback(theMem, theRF, theCP0, branch, theDebug, cop1, theCapCop, memAccess);
      `else
      WritebackIfc writeback <- mkWriteback(theMem, theRF, theCP0, branch, theDebug, cop1, theCapCop, theMem.statCounters, memAccess);
      `endif
      // The scheduler pulls the instruction out of the instruction memory interface
      // and reports any branches to the branch unit. The scheduler also submits
      // register read addresses to the register file
      `ifndef STATCOUNTERS
      PipeStageIfc scheduler <- mkScheduler(branch, theRF, theCP0, cop1, theCapCop, theMem.instructionMemory);
      `else
      PipeStageIfc scheduler <- mkScheduler(branch, theRF, theCP0, cop1, theCapCop, theMem.instructionMemory, theMem.statCounters);
      `endif
      // The execute stage of the pipeline has access to the coprocessor
      // interfaces since they may hold their own uncommitted temporary values,
      // and also the decode interface which it directly accesses so that it can
      // check conditions of the next instruction before consuming and also the
      // writeback interface so that it can receive values loaded from memory
      // for forwarding.
      PipeStageIfc execute <- mkExecute(theRF, writeback, theCP0, cop1, theCapCop, decode);
  `else
   ...
  `endif

Note:

  • execute:
    • access to the coprocessor interface for uncommitted CP values
    • access to decode interface to check conditions of the next instruction before consuming
    • access to writeback interface for forwarding of memory loaded values

Rules for pipelining

  • instructionFetch
  • fromFetchToScheduler
  • fromSchedulerToDecode
  • fromDecodeToExecute
  • fromExecuteToMemAccess

instructionFetch

Ininitalize control token:

 // Initialize a default control token to insert in the pipeline.
    ControlTokenT ct = defaultControlToken;

Submit read request to instruction memory:

theMem.instructionMemory.reqInstruction(nextPC, ct.id, ct.inst);

fromFetchToScheduler

  // This rule deqs from the toScheduler fifo and enqs to the scheduler, likely
  // because the instruction fetch has finished.
  rule fromFetchToScheduler;
    // Assign the name ct to the top element of the toScheduler fifo.
    ControlTokenT ct = toScheduler.first;
    // Enq ct to the scheduler module fifo interface. The logic for the
    // scheduler/register rename stage happens./simn the enq.
    scheduler.enq(ct);
    // Deq the toScheduler fifo.
    toScheduler.deq();
  endrule

fromSchedulerToDecode

 // This rule takes a token from the scheduler/register rename stage and enqs
  // it to the decode stage. The conditions on this rule only allow it to fire
  // when a CP0 write is pending and we are about to decode (and fetch the
  // registers for) another CP0 instruction.
  rule fromSchedulerToDecode(!(scheduler.first.observesCP0 && theCP0.writePending));
    // Enq the top element in the scheduler output fifo to decode.
    // The enq interface of this module actually performs the decode pipeline stage.
    decode.enq(scheduler.first);
    // Deq the first element from the scheduler output fifo.
    scheduler.deq();
  endrule

fromDecodeToExecute

// The fromDecodeToExecute rule enqs the top element from the output fifo in
  // the decode stage to the execute stage of the pipeline.
  //(* descending_urgency = "fromDecodeToExecute, execute_doReadReport" *)
  rule fromDecodeToExecute;
    // Enq the top element of the output fifo of the decode stage to the execute
    // input fifo.
    execute.enq(decode.first);
    // Deq the decode stage output fifo.
    decode.deq();
  endrule

fromExecuteToMemAccess

 // The fromExecteToMemAccess rule deqs the top element of the execute stage
  // output fifo and enqs it to memAccess.
  rule fromExecuteToMemAccess;
    // Enq the control token in the output fifo of the execute stage to the enq
    // method of memAccess which implements the memory access stage of the
    // pipeline.
    memAccess.enq(execute.first);
    // Remove the control token from the output fifo in the execute stage of the
    // pipeline.
    execute.deq();
  endrule
Created Apr 4, 2020 // Last Updated May 23, 2020

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

... what would you change?