-- File: [Indigo]<Sakura>Dragon>DragonCacheControlImpl.sak
-- Dragon Cache
-- 18-Feb-82 15:50:59

DIRECTORY
  DragonCacheControl: TYPE, 
  Inline: TYPE, 
  SakuraRT: TYPE;

DragonCacheImpl: MONITOR
  IMPORTS Inline, SakuraRT
  EXPORTS DragonCacheControl
  
  -- The functionality of Dragon cache chip is described under the following 
  --conventions: there are two main processes, a process to watch the processor bus
  --commands (PbusFetch), and a process to watch the memory bus commands
  --(BackDoor, backdoor operations).  Furthermore, PbusFetch cooperates with 
  --several other processes to allow overlapping of pipline operations: MProc is a 
  --process that is invoked from PbusFetch and invokes Mbus operations directly.  
  --Operations associated with Pbus commands are all split into two parts so that 
  --they can be piplined.  All the front parts of these operations are inside 
  --PbusFetch, so they do not constitute independent processes.  The latter parts 
  --are all independent processes.  They are FetchTransport, StoreTransport, and 
  --DoMapOpTransport.  
  
  -- Communications among processes:
  --Communication between PbusFetch and MProc is done through two global
  --variables: DoMProc and MProcDone.  The communication between PbusFetch
  --and transport processes, FetchTransport, StoreTransport, and
  --DoMapOpTransport, is done by global variables, FetchTransportGo, 
  --StoreTransportGo, and DoMapOpTransportGo, and ~Reject for the other
  --direction.
  
  -- i) Fetch
  --Match on Pbus
  --IF success THEN Fetch
  --ELSE {
  --  IF Victim dirty THEN WriteQuad Victim;
  --  Partial Match on Pbus
  --  IF ~ success THEN ReadMap;
  --  ReadQuad};
  
  --ii) Store
  --Match on Pbus
  --IF success THEN
  --  IF ~Shared THEN
  --    IF RpDirty THEN Store to Ram
  --    ELSE {SetRpDirty; Store to Ram}
  --  ELSE 
  --    IF RpDirty THEN WriteSingle
  --    ELSE {SetRpDirty; WriteSingle}
  --ELSE {
  --  IF Victim dirty THEN WriteQuad Victim;
  --  PartialMatch;
  --  IF ~success THEN ReadMap;
  --  ReadQuad;
  --  GO TO ii) Store}
  
  = BEGIN
  Cell: TYPE = RECORD [vp: LONG CARDINAL, 
                       rp: LONG CARDINAL, 
                       bl: INTEGER [0..37B], 
                       D: ARRAY INTEGER [0..3] OF LONG CARDINAL, 
                       VpValid, RpValid, RpDirty, CEDirty, Shared, TIP: BOOLEAN]; 
  PbusType: TYPE = 
    RECORD [body: SELECT kind: {Instruction, Data} FROM 
                    Instruction => [vp: LONG CARDINAL, -- actually 25 bits -- 
                                    bl: INTEGER [0..37B], 
                                    word: INTEGER [0..3]],
                    Data => [data: LONG CARDINAL]
                    ENDCASE]; 
  MbusType: TYPE = 
    RECORD [body: SELECT kind: {Instruction, MapCommand, Data} FROM 
                    Instruction => [command: MbusCommand, 
                                    rp: LONG CARDINAL, -- actually 25 bits -- 
                                    bl: INTEGER [0..37B], 
                                    word: INTEGER [0..3]],
                    MapCommand => [command: MbusCommand, 
                                   mapop: MapOpKind, 
                                   fault: BOOLEAN, 
                                   vp: LONG CARDINAL],
                    Data => [data: LONG CARDINAL]
                    ENDCASE]; 
  MbusCommand: TYPE = {ReadQuad, WriteQuad, WriteSingle, NotReady, ReadMap, 
                       DoMapOp, SetRpDirty, SetRpDirtyDone}; 
  MapOpKind: TYPE = {SetMap, SetMapFlags, GetMapFlags}; 
  MProcType: TYPE = PROC; 
  
  -- Constants
  DataArraySize: CARDINAL = 64; 
  
  -- Global variables
  FetchTransportGo, StoreTransportGo, DoMapOpTransportGo, Grant, Holding, MShared, 
  DoMProc, MProcDone, PRq, NewRq, MRq, Orph, MCMD: BOOLEAN; 
  Instruction, Reqbus: {Fetch, FetchHold, Store, MapOp, Noop}; 
  DataIndex, VictimIndex: CARDINAL; 
  RqVP, MVP: LONG CARDINAL; 
  RqBL, PBL, MBL: INTEGER [0..37B]; 
  RqData: LONG CARDINAL; 
  PWord, MWord: INTEGER [0..3]; 
  PResult: LONG CARDINAL; 
  PRP, MRP: LONG CARDINAL; 
  Pbus: PbusType; 
  Mbus: MbusType; 
  Data: ARRAY INTEGER [0..DataArraySize) OF Cell; 
  MOp: MProcType; 
  MOResult: LONG CARDINAL; 
  MRdData: LONG CARDINAL; 
  MWData: LONG CARDINAL; 
  MInst: MbusCommand; 
  MapOp: MapOpKind; 
  

  PageFault: SIGNAL = CODE; 
  WriteViolation: SIGNAL = CODE; 
  


  Cache: PUBLIC PROC
       [PDataIn, MDataIn: SakuraRT.Handle, CMDIn, RQ, SharedIn: SakuraRT.Handle, 
        Exception, Reject, SharedOut, CMDOut: SakuraRT.Handle, 
        PDataOut, MDataOut: SakuraRT.Handle] = {
    

    -- PbusFetch process procedures
    
    PbusFetch: PROC = {
      success: BOOLEAN;
      DO --A (Phase A)
         IF NARROW[SakuraRT.Get[RQ], REF BOOLEAN]↑ AND 
              NOT NARROW[SakuraRT.Get[Reject], REF BOOLEAN]↑ THEN 
            {SakuraRT.Put[Exception, NEW[BOOLEAN ← FALSE]]; 
             WITH b: Pbus SELECT FROM 
               Instruction => {RqVP ← b.vp; RqBL ← b.bl; PWord ← b.word}
               ENDCASE => ERROR; 
             Instruction ← Reqbus; 
             --M (Phase B)
             [success, DataIndex] ← FullMatch[RqVP, RqBL]; 
             {ENABLE {
                PageFault => 
                  {Pbus ← [Data[-1]]; SakuraRT.Put[Exception, NEW[BOOLEAN ← TRUE]]};
                WriteViolation => 
                  {Pbus ← [Data[0]]; SakuraRT.Put[Exception, NEW[BOOLEAN ← TRUE]]}};
              SELECT Instruction FROM 
                Fetch => CallFetch[];
                FetchHold => 
                  CallFetchHold
                    [! PageFault => 
                         {Pbus ← [Data[-1]]; Holding ← FALSE; 
                          SakuraRT.Put[Exception, NEW[BOOLEAN ← TRUE]]};
                       WriteViolation => 
                         {Pbus ← [Data[0]]; SakuraRT.Put[Exception, NEW[BOOLEAN ← TRUE]]}];
                Store => CallStore[];
                MapOp => CallMapOp[];
                Noop => NULL
                ENDCASE}}
         ENDLOOP}; 
    
    CallFetch: PROC = {
      success: BOOLEAN;
      -- Started at Phase B
      IF success --hit
         THEN {SakuraRT.Put[Reject, NEW[BOOLEAN ← FALSE]]; 
               FetchTransportGo ← TRUE; 
               Holding ← FALSE} 
         ELSE {SakuraRT.Put[Reject, NEW[BOOLEAN ← TRUE]]; 
               Holding ← FALSE; 
               --R (Phase A)
               Holding ← TRUE; 
               --T (Phase B)
               PRq ← TRUE; 
               WaitForGrant[]; 
               WriteBackVictimIfDirty[]; 
               [success, PRP] ← PartialMatch[RqVP]; 
               --Clock (Phase B)
               IF NOT success THEN CallMProc[ReadMap]; 
               CallMProc[ReadQuad]; 
               PRq ← FALSE; 
               SakuraRT.Put[Reject, NEW[BOOLEAN ← FALSE]]; 
               [success, DataIndex] ← FullMatch[RqVP, RqBL]; 
               IF NOT success THEN ERROR; -- This is a description error
               FetchTransportGo ← TRUE}}; 
    
    CallFetchHold: PROC = {
      -- Started at Phase B
      IF NOT Holding THEN 
         {SakuraRT.Put[Reject, NEW[BOOLEAN ← TRUE]]; 
          PRq ← TRUE; 
          WaitForGrant[]; 
          --Clock (Phase B)
          Holding ← TRUE}; 
      CallFetch[]}; 
    
    CallStore: PROC = {
      success: BOOLEAN;
      SakuraRT.Put[Reject, NEW[BOOLEAN ← TRUE]]; 
      -- Clock (A)
      WITH b: Pbus SELECT FROM 
        Data => RqData ← b.data
        ENDCASE => ERROR; 
      IF NOT success THEN 
         {PRq ← TRUE; 
          WaitForGrant[]; 
          WriteBackVictimIfDirty[]; 
          --Clock (Phase B)
          [success, PRP] ← PartialMatch[RqVP]; 
          IF NOT success THEN CallMProc[ReadMap]; 
          --Clock (Phase B)
          CallMProc[ReadQuad]}; 
      --Clock (B)
      [success, DataIndex] ← FullMatch[RqVP, RqBL]; 
      IF NOT Data[DataIndex].Shared 
         THEN {IF NOT Data[DataIndex].RpDirty THEN CallMProc[SetRpDirty]; 
               SakuraRT.Put[Reject, NEW[BOOLEAN ← FALSE]]; 
               StoreTransportGo ← TRUE} 
         ELSE {IF NOT Data[DataIndex].RpDirty THEN CallMProc[SetRpDirty]; 
               Data[DataIndex].D[PWord] ← RqData; 
               CallMProc[WriteSingle]}; 
      PRq ← FALSE}; -- CallStore
    
    CallMapOp: PROC = {
      -- Started at Phase B
      SakuraRT.Put[Reject, NEW[BOOLEAN ← TRUE]]; 
      PRq ← TRUE; 
      WaitForGrant[]; 
      -- Clock (A)
      WITH b: Pbus SELECT FROM 
        Data => RqData ← b.data
        ENDCASE => ERROR; 
      CallMProc[DoMapOp]; 
      PRq ← FALSE}; -- CallMapOp
    
    CallMProc: PROC [procedure: MProcType] = {
      -- Starts in Phase B and finishes in Phase B
      MProcDone ← FALSE; 
      DoMProc ← TRUE; 
      MOp ← procedure; 
      DO --Clock (Phase A)
         IF MProcDone THEN EXIT
         ENDLOOP}; -- CallMProc
    
    WriteBackVictimIfDirty: PROC = {
      -- Starts at Phase A
      MRP ← Data[VictimIndex].rp; 
      MBL ← Data[VictimIndex].bl; 
      IF Data[VictimIndex].CEDirty THEN 
         {CallMProc[WriteQuad]; 
          Data[VictimIndex].CEDirty ← FALSE}}; -- WriteBackVictimIfNotClean
    
    WaitForGrant: PROC = {
      DO --Clock (Phase A)
         IF Grant THEN EXIT
         ENDLOOP}; -- WaitForGrant
    


    -- FetchTransport process procedures
    
    FetchTransport: PROC = {
      -- Starts at Phase A
      DO --R (Phase A)
         IF FetchTransportGo THEN 
            {PResult ← Data[DataIndex].D[PWord]; 
             FetchTransportGo ← FALSE; 
             --T (Phase B)
             Pbus ← [Data[PResult]]}
         ENDLOOP}; 
    
    StoreTransport: PROC = {
      -- Starts at Phase A
      DO --clock (A)
         IF StoreTransportGo THEN 
            {Data[DataIndex].D[PWord] ← RqData; 
             StoreTransportGo ← FALSE}
         ENDLOOP}; -- StoreTransport
    
    DoMapOpTransport: PROC = {
      DO -- clock (B)
         IF DoMapOpTransportGo THEN 
            {NewRq ← TRUE; 
             MOResult ← MRdData; 
             SakuraRT.Put[Reject, NEW[BOOLEAN ← FALSE]]; 
             --clock (A)
             PResult ← MOResult; 
             NewRq ← FALSE; 
             -- clock (B)
             IF Inline.BITAND[Inline.HighHalf[PResult], 40000B] # 0 THEN 
                SakuraRT.Put[Exception, NEW[BOOLEAN ← TRUE]]; 
             Pbus ← [Data[PResult]]}
         ENDLOOP}; -- DoMapOpTransport
    


    -- MProc process procedures
    
    MProc: PROC = {DO --Clock (Phase A)
                      MProcDone ← FALSE; 
                      IF DoMProc THEN 
                         {DoMProc ← FALSE; 
                          MOp[]}
                      ENDLOOP}; 
    
    WriteQuad: PROC = {
      i: CARDINAL; 
      WriteOneWord: PROC [pword: INTEGER [0..3]] = {
        --Clock (Phase B)
        MWData ← Data[DataIndex].D[pword]; 
        --Clock (Phase A)
        Mbus ← [Data[MWData]]}; -- WriteOneWord
      --Clock (Phase A)
      Mbus ← [Instruction[WriteQuad, MRP, MBL, 0]]; 
      FOR i IN [0..3] DO
          WriteOneWord[i]
          ENDLOOP; 
      MProcDone ← TRUE}; -- WriteQuad
    
    ReadMap: PROC = {
      fault: BOOLEAN;
      --Clock (Phase A)
      Mbus ← [MapCommand[ReadMap, , , MVP]]; 
      fault ← WaitMapOpDone[]; 
      IF fault THEN SIGNAL PageFault; 
      MProcDone ← TRUE}; 
    
    ReadQuad: PROC = {
      ReadOneWord: PROC = {IF NOT Orph THEN ReadDataFromBus[@Data[DataIndex].D[PWord]]}; 
      success: BOOLEAN;
      --Clock (Phase A)
      Mbus ← [Instruction[ReadQuad, PRP, PBL, PWord]]; 
      WITH b: Mbus SELECT FROM 
        Instruction => {MRP ← b.rp; MBL ← b.bl; MWord ← b.word}
        ENDCASE => ERROR; 
      --Clock (Phase B, t2)
      MRq ← TRUE; 
      --Clock (Phase A, t3)
      [success, DataIndex] ← FullMatch[MRP, MBL]; 
      --Clock (Phase B, t4)
      Orph ← success; 
      IF NOT success THEN DataIndex ← VictimIndex; 
      --Clock (Phase A, t5)
      IF NOT success THEN 
         {Data[DataIndex].rp ← MRP; 
          Data[DataIndex].bl ← MBL}; 
      --Clock (Phase B, t6)
      Data[DataIndex].vp ← RqVP; 
      Data[DataIndex].Shared ← NARROW[SakuraRT.Get[SharedIn], REF BOOLEAN]↑; 
      VictimIndex ← (VictimIndex + 1) MOD DataArraySize; 
      --Clock (Phase A, t7)
      MCMD ← NARROW[SakuraRT.Get[CMDIn], REF BOOLEAN]↑; 
      ReadOneWord[]; 
      IF MCMD AND MInst = NotReady THEN WaitOneCycle[]; 
      MProcDone ← TRUE; 
      --Clock (Phase B, t8)
      ReadOneWord[]; 
      --Clock (Phase A, t9)
      PWord ← (PWord + 1) MOD 4; 
      --Clock (Phase B, t10)
      Data[DataIndex].TIP ← TRUE; 
      ReadOneWord[]; 
      MRq ← FALSE; 
      IF NOT Holding THEN NewRq ← TRUE; 
      --Clock (Phase A, t11)
      PWord ← (PWord + 1) MOD 4; 
      --Clock (Phase B, t12)
      ReadOneWord[]; 
      IF NOT Holding THEN NewRq ← FALSE; 
      --Clock (Phase A, t13)
      PWord ← (PWord + 1) MOD 4; 
      --Clock (Phase B, t14)
      ReadOneWord[]; 
      Data[DataIndex].TIP ← FALSE}; 
    
    SetRpDirty: PROC = {
      fault: BOOLEAN;
      --Clock (Phase A)
      Mbus ← [Instruction[SetRpDirty, PRP, PBL, PWord]]; 
      WITH b: Mbus SELECT FROM 
        Instruction => 
          {MRP ← b.rp; 
           MBL ← b.bl; 
           MWord ← b.word}
        ENDCASE => ERROR; 
      --Clock (Phase B)
      fault ← WaitMapOpDone[]; 
      IF fault THEN SIGNAL WriteViolation; 
      MProcDone ← TRUE}; 
    
    WriteSingle: PROC = {
      --Clock (Phase A)
      Mbus ← [Instruction[WriteSingle, PRP, PBL, PWord]]; 
      NewRq ← TRUE; 
      --Clock (Phase B)
      MWData ← Data[DataIndex].D[PWord]; 
      --Clock (Phase A)
      Mbus ← [Data[MWData]]; 
      MProcDone ← TRUE; 
      NewRq ← FALSE}; 
    
    DoMapOp: PROC = {
      fault: BOOLEAN;
      --Clock (Phase A)
      Mbus ← [MapCommand[DoMapOp, MapOp, , MVP]]; 
      MCMD ← TRUE; 
      --Clock (Phase B)
      MWData ← RqData; 
      --Clock (Phase A)
      Mbus ← [Data[MWData]]; 
      fault ← WaitMapOpDone[]; 
      IF fault THEN SIGNAL PageFault; 
      MProcDone ← TRUE; 
      ReadDataFromBus[@MRdData]; 
      DoMapOpTransportGo ← TRUE}; -- DoMapOp
    
    WaitMapOpDone: PROC RETURNS [fault: BOOLEAN] = {
      DO --Clock (Phase A)
         MCMD ← NARROW[SakuraRT.Get[CMDIn], REF BOOLEAN]↑; 
         [MInst, fault] ← DecodeMbusCommand[]; 
         IF MCMD AND MInst = SetRpDirtyDone THEN EXIT
         ENDLOOP}; 
    
    DecodeMbusCommand: PROC RETURNS [MInst: MbusCommand, fault: BOOLEAN] = {
      WITH b: Mbus SELECT FROM 
        MapCommand => {MInst ← b.command; fault ← b.fault}
        ENDCASE => ERROR}; 
    
    WaitOneCycle: PROC = {}; 
    --Clock (Phase B)
    --Clock (Phase A)
    


    -- Backdoor process procedures
    
    BackDoor: PROC = {
      DO -- clock (B)
         MCMD ← NARROW[SakuraRT.Get[CMDIn], REF BOOLEAN]↑; 
         [MInst, ] ← DecodeMbusCommand[]; 
         IF MCMD THEN 
            SELECT MInst FROM 
              ReadQuad => BDReadQuad[];
              WriteQuad => BDWriteQuad[];
              WriteSingle => BDWriteSingle[];
              ReadMap => NULL;
              SetRpDirty => BDSetRpDirty[];
              DoMapOp => NULL
              ENDCASE
         ENDLOOP}; 
    
    BDReadQuad: PROC = {
      success: BOOLEAN; 
      ReadOneWord: PROC = {
        -- clock (B)
        IF success THEN MWData ← Data[DataIndex].D[MWord]; 
        -- clock (A)
        IF success THEN Mbus ← [Data[MWData]]; 
        MWord ← (MWord + 1) MOD 4};
      [success, DataIndex] ← FullMatch[MRP, MBL]; 
      -- clock (A)
      IF success THEN MShared ← TRUE; 
      THROUGH [1..4] DO
          ReadOneWord[]
          ENDLOOP; 
      -- clock (B)
      MShared ← FALSE}; -- BDReadQuad
    
    BDWriteQuad: PROC = {
      success: BOOLEAN; 
      WriteOneWord: PROC = {
        -- clock (B)
        IF success THEN Data[DataIndex].D[MWord] ← MRdData; 
        -- clock (A)
        MWord ← MWord + 1; 
        ReadDataFromBus[@MRdData]};
      [success, DataIndex] ← FullMatch[MRP, MBL]; 
      -- clock (A)
      IF success THEN 
         {MShared ← TRUE; MWord ← 0; 
          ReadDataFromBus[@MRdData]}; 
      THROUGH [1..3] DO
          WriteOneWord[]
          ENDLOOP; 
      -- clock (B)
      IF success THEN Data[DataIndex].D[MWord] ← MRdData; 
      IF success THEN MShared ← FALSE}; -- BDWriteQuad
    
    BDWriteSingle: PROC = {
      success: BOOLEAN;
      [success, DataIndex] ← FullMatch[MRP, MBL]; 
      -- clock (A)
      ReadDataFromBus[@MRdData]; 
      -- clock (B)
      IF success THEN Data[DataIndex].D[MWord] ← MRdData}; -- BDWriteSingle
    
    BDSetRpDirty: PROC = {
      success: BOOLEAN;
      [success, DataIndex] ← FullMatch[MRP, MBL]; 
      -- clock (A)
      -- clock (B)
      IF success THEN Data[DataIndex].RpDirty ← TRUE}; -- BDSetRpDirty
    
    ReadDataFromBus: PROC [data: POINTER TO LONG CARDINAL] = {
      WITH b: Mbus SELECT FROM 
        Data => data↑ ← b.data
        ENDCASE => ERROR}; 
    


    -- Data array access procedures
    
    FullMatch: ENTRY PROC [vp: LONG CARDINAL, bl: CARDINAL] RETURNS [BOOLEAN, CARDINAL] = {
      FOR i: CARDINAL IN [0..DataArraySize) DO
          IF Data[i].VpValid AND Data[i].vp = vp AND Data[i].bl = bl THEN 
             RETURN [TRUE, i]
          ENDLOOP; 
      RETURN [FALSE, 0]}; 
    
    PartialMatch: ENTRY PROC [vp: LONG CARDINAL] RETURNS [BOOLEAN, CARDINAL] = {
      FOR i: CARDINAL IN [0..DataArraySize) DO
          IF Data[i].VpValid AND Data[i].vp = vp THEN 
             RETURN [TRUE, i]
          ENDLOOP; 
      RETURN [FALSE, 0]};
    

    {ENABLE {ABORTED => GO TO Aborted};
     {st1: PROC = {{PbusFetch[]; 
                    SakuraRT.ProcessEnd[]}}; 
      st2: PROC = {{FetchTransport[]; 
                    SakuraRT.ProcessEnd[]}}; 
      st3: PROC = {{StoreTransport[]; 
                    SakuraRT.ProcessEnd[]}}; 
      st4: PROC = {{DoMapOpTransport[]; 
                    SakuraRT.ProcessEnd[]}}; 
      st5: PROC = {{MProc[]; 
                    SakuraRT.ProcessEnd[]}}; 
      process1, process2, process3, process4, process5: PROCESS; 
      process1 ← SakuraRT.Fork[st1]; 
      process2 ← SakuraRT.Fork[st2]; 
      process3 ← SakuraRT.Fork[st3]; 
      process4 ← SakuraRT.Fork[st4]; 
      process5 ← SakuraRT.Fork[st5]; 
      BackDoor[]; 
      [] ← SakuraRT.Join[process1]; 
      [] ← SakuraRT.Join[process2]; 
      [] ← SakuraRT.Join[process3]; 
      [] ← SakuraRT.Join[process4]; 
      [] ← SakuraRT.Join[process5]}; 
     SakuraRT.ProcessEnd[]}
    EXITS
      Aborted => SakuraRT.ProcessEnd[]};
  


  END.