diff options
| author | Julian Blake Kongslie | 2023-04-08 13:28:28 -0700 |
|---|---|---|
| committer | Julian Blake Kongslie | 2023-04-08 13:28:28 -0700 |
| commit | 4420244e9c0fdef41baf811c283d9f843265d254 (patch) | |
| tree | 68db1b02e937866da72025735753f4123a529ba1 /spin/snoopy.pml | |
| parent | questionable hacks (diff) | |
| download | biggolf-4420244e9c0fdef41baf811c283d9f843265d254.tar.xz | |
Diffstat (limited to 'spin/snoopy.pml')
| -rw-r--r-- | spin/snoopy.pml | 55 |
1 files changed, 39 insertions, 16 deletions
diff --git a/spin/snoopy.pml b/spin/snoopy.pml index 1aff5af..3d22d21 100644 --- a/spin/snoopy.pml +++ b/spin/snoopy.pml | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | mtype { | 1 | mtype { |
| 2 | I, M, // valid cache line states: invalid, modified | 2 | I, M, // valid cache line states: invalid, modified |
| 3 | U, X, // fill/snoop responses that are not valid cache line states: unknown (from DRAM only), fail (you have to try again) | 3 | U, X, // evict/fill/snoop responses that are not valid cache line states: unknown (from DRAM only), fail (you have to try again) |
| 4 | ItoM, MtoI, // commands from peers | 4 | ItoM, MtoI, // commands from peers |
| 5 | Busy, // used as both cache line state and evil buffer command to denote activity on behalf of other addresses | 5 | Busy, // used as both cache line state and evil buffer command to denote activity on behalf of other addresses |
| 6 | } | 6 | } |
| @@ -23,7 +23,7 @@ proctype cache(int recvqbase; int sendqbase; int npeers) | |||
| 23 | #define SQ(i) qs[sendqbase+i] | 23 | #define SQ(i) qs[sendqbase+i] |
| 24 | 24 | ||
| 25 | mtype cstate = I; // I, M, or Busy | 25 | mtype cstate = I; // I, M, or Busy |
| 26 | mtype ebcmd = I; // I, ItoM, or Busy | 26 | mtype ebcmd = I; // I, ItoM, MtoI, or Busy |
| 27 | int ebstate = I; // I, M, or U | 27 | int ebstate = I; // I, M, or U |
| 28 | int enotsent = 0; // bitmask of snoops remaining to send | 28 | int enotsent = 0; // bitmask of snoops remaining to send |
| 29 | int enotrcvd = 0; // bitmask of replies remaining to receive | 29 | int enotrcvd = 0; // bitmask of replies remaining to receive |
| @@ -36,26 +36,48 @@ proctype cache(int recvqbase; int sendqbase; int npeers) | |||
| 36 | :: atomic { RQ(i)?ItoM -> if | 36 | :: atomic { RQ(i)?ItoM -> if |
| 37 | :: cstate == M -> cstate = I -> SQ(i)!M | 37 | :: cstate == M -> cstate = I -> SQ(i)!M |
| 38 | :: cstate == I && ebcmd == I -> ebcmd = ItoM -> enotsent = ALLPEERS & ~(1 << i) -> esource = i | 38 | :: cstate == I && ebcmd == I -> ebcmd = ItoM -> enotsent = ALLPEERS & ~(1 << i) -> esource = i |
| 39 | :: cstate == I && ebcmd == MtoI && enotsent != 0 -> ERESET() -> SQ(i)!M | ||
| 39 | :: else -> SQ(i)!X | 40 | :: else -> SQ(i)!X |
| 40 | fi } | 41 | fi } |
| 41 | 42 | ||
| 43 | // Receive eviction request from peer | ||
| 44 | :: atomic { RQ(i)?MtoI -> if | ||
| 45 | :: cstate == M || ebcmd == MtoI -> assert(false) | ||
| 46 | :: cstate == I && ebcmd == I -> cstate = M -> SQ(i)!I | ||
| 47 | // :: cstate == I && ebcmd == ItoM -> ebstate = M -> enotsent = 0 -> SQ(i)!I // Missing logic: cut-through send to esource, clear out eb when enotrcvd is already 0 | ||
| 48 | :: cstate == I && ebcmd == ItoM -> SQ(i)!X | ||
| 49 | :: cstate == I && ebcmd == Busy -> cstate = M -> SQ(i)!I | ||
| 50 | :: cstate == Busy && ebcmd == I -> assert(i != npeers-1) -> ebcmd = MtoI -> enotsent = 1 << (npeers-1) -> SQ(i)!I | ||
| 51 | :: cstate == Busy && ebcmd != I -> SQ(i)!X | ||
| 52 | fi } | ||
| 53 | |||
| 42 | // Receive fill responses from peers | 54 | // Receive fill responses from peers |
| 43 | :: atomic { RQ(i)?I -> assert(ebcmd == ItoM && (enotsent & (1 << i)) == 0 && (enotrcvd & (1 << i)) != 0) -> enotrcvd = enotrcvd & ~(1 << i) } | 55 | :: atomic { RQ(i)?I -> assert((enotsent & (1 << i)) == 0 && (enotrcvd & (1 << i)) != 0) -> if |
| 44 | :: atomic { RQ(i)?M -> assert(ebcmd == ItoM && (enotsent & (1 << i)) == 0 && (enotrcvd & (1 << i)) != 0) -> enotrcvd = enotrcvd & ~(1 << i) -> ebstate = M -> if | 56 | :: ebcmd == ItoM -> enotrcvd = enotrcvd & ~(1 << i) |
| 57 | :: ebcmd == MtoI -> ERESET() | ||
| 58 | :: else -> assert(false) | ||
| 59 | fi } | ||
| 60 | :: atomic { RQ(i)?M -> assert(ebcmd == ItoM) -> assert((enotsent & (1 << i)) == 0 && (enotrcvd & (1 << i)) != 0) -> enotrcvd = enotrcvd & ~(1 << i) -> ebstate = M -> if | ||
| 45 | :: esource != -1 -> SQ(esource)!M | 61 | :: esource != -1 -> SQ(esource)!M |
| 46 | :: else /* ideally put it into the cache right away */ | 62 | :: else /* ideally put it into the cache right away */ |
| 47 | fi } | 63 | fi } |
| 48 | :: atomic { RQ(i)?U -> assert(ebcmd == ItoM && (enotsent & (1 << i)) == 0 && (enotrcvd & (1 << i)) != 0) -> enotrcvd = enotrcvd & ~(1 << i) -> if :: ebstate == I -> ebstate = U :: else fi } | 64 | :: atomic { RQ(i)?U -> assert(ebcmd == ItoM) -> assert((enotsent & (1 << i)) == 0 && (enotrcvd & (1 << i)) != 0) -> enotrcvd = enotrcvd & ~(1 << i) -> if :: ebstate == I -> ebstate = U :: else fi } |
| 49 | :: atomic { RQ(i)?X -> assert(ebcmd == ItoM && (enotsent & (1 << i)) == 0 && (enotrcvd & (1 << i)) != 0) -> enotrcvd = enotrcvd & ~(1 << i) -> enotsent = enotsent | (1 << i) -> ereverse = ereverse + 1 -> if | 65 | :: atomic { RQ(i)?X -> assert((enotsent & (1 << i)) == 0 && (enotrcvd & (1 << i)) != 0) -> enotrcvd = enotrcvd & ~(1 << i) -> enotsent = enotsent | (1 << i) -> if |
| 50 | // To prevent deadlock, if we get X'd too many times abandon the transaction and send X to its source | 66 | :: ebcmd == ItoM -> ereverse = ereverse + 1 -> if |
| 51 | :: atomic { ereverse > npeers && esource == -1 && enotsent == ALLPEERS -> ERESET() } | 67 | :: atomic { ereverse > npeers && esource == -1 && enotsent == ALLPEERS -> ERESET() } |
| 52 | :: atomic { ereverse > npeers && esource != -1 && enotsent == ALLPEERS & ~(1 << esource) -> SQ(esource)!X -> ERESET() } | 68 | :: atomic { ereverse > npeers - 1 && esource != -1 && enotsent == ALLPEERS & ~(1 << esource) -> SQ(esource)!X -> ERESET() } |
| 53 | :: else | 69 | :: else |
| 70 | fi | ||
| 71 | :: ebcmd == MtoI | ||
| 72 | :: else -> assert(false) | ||
| 54 | fi } | 73 | fi } |
| 55 | 74 | ||
| 56 | // Send not-yet-sent snoops | 75 | // Send not-yet-sent snoops |
| 57 | :: atomic { ebcmd == ItoM && (enotsent & (1 << i)) -> enotsent = enotsent & ~(1 << i) -> enotrcvd = enotrcvd | (1 << i) -> SQ(i)!ItoM } | 76 | :: atomic { ebcmd == ItoM && (enotsent & (1 << i)) -> enotsent = enotsent & ~(1 << i) -> enotrcvd = enotrcvd | (1 << i) -> SQ(i)!ItoM } |
| 58 | 77 | ||
| 78 | // Send not-yet-sent evictions | ||
| 79 | :: atomic { ebcmd == MtoI && (enotsent & (1 << i)) -> enotsent = enotsent & ~(1 << i) -> enotrcvd = enotrcvd | (1 << i) -> SQ(i)!MtoI } | ||
| 80 | |||
| 59 | // Complete requests from peers (sending response if not sent earlier) | 81 | // Complete requests from peers (sending response if not sent earlier) |
| 60 | :: atomic { ebcmd == ItoM && enotsent == 0 && enotrcvd == 0 && esource != -1 -> if | 82 | :: atomic { ebcmd == ItoM && enotsent == 0 && enotrcvd == 0 && esource != -1 -> if |
| 61 | :: ebstate == I -> SQ(esource)!I | 83 | :: ebstate == I -> SQ(esource)!I |
| @@ -70,20 +92,21 @@ proctype cache(int recvqbase; int sendqbase; int npeers) | |||
| 70 | :: else -> assert(false) | 92 | :: else -> assert(false) |
| 71 | fi -> ERESET() } | 93 | fi -> ERESET() } |
| 72 | 94 | ||
| 73 | #if 0 | ||
| 74 | // Simulate local cache being occupied by other address | 95 | // Simulate local cache being occupied by other address |
| 75 | :: atomic { if :: cstate == I -> cstate = Busy :: cstate == Busy -> cstate = I :: else fi } | 96 | :: atomic { cstate == I -> cstate = Busy } |
| 97 | :: atomic { cstate == Busy -> cstate = I } | ||
| 76 | 98 | ||
| 77 | // Simulate evilbuffers conducting transaction for other address | 99 | // Simulate evilbuffers conducting transaction for other address |
| 78 | :: atomic { if :: ebcmd == I -> ebcmd = Busy :: ebcmd == Busy -> ebcmd = I :: else fi } | 100 | :: atomic { ebcmd == I -> ebcmd = Busy } |
| 79 | #endif | 101 | :: atomic { ebcmd == Busy -> ebcmd = I } |
| 102 | |||
| 103 | // Simulate local cache spontaneously evicting | ||
| 104 | :: atomic { cstate == M && ebcmd == I -> cstate = I -> ebcmd = MtoI -> enotsent = 1 << (npeers-1) } | ||
| 80 | 105 | ||
| 81 | #if 0 | ||
| 82 | // If we're not in a transaction assert that the evil buffer is fully reset (just to make sure we've limited reachable states) | 106 | // If we're not in a transaction assert that the evil buffer is fully reset (just to make sure we've limited reachable states) |
| 83 | :: atomic { ebcmd == Busy || ebcmd == I -> assert(ebstate == I && enotsent == 0 && enotrcvd == 0 && ereverse == 0 && esource == -1) } | 107 | :: atomic { ebcmd == Busy || ebcmd == I -> assert(ebstate == I && enotsent == 0 && enotrcvd == 0 && ereverse == 0 && esource == -1) } |
| 84 | // If we're in ItoM transaction assert that it's not in the cache | 108 | // If we're in ItoM transaction assert that it's not in the cache |
| 85 | :: atomic { ebcmd == ItoM -> assert(cstate == I || cstate == Busy) } | 109 | :: atomic { ebcmd == ItoM -> assert(cstate == I || cstate == Busy) } |
| 86 | #endif | ||
| 87 | 110 | ||
| 88 | // Launch locally-requested fills; rely on deadlock avoidance above to protect from multiple peers simultaneously deciding to do this | 111 | // Launch locally-requested fills; rely on deadlock avoidance above to protect from multiple peers simultaneously deciding to do this |
| 89 | :: atomic { cstate != M && ebcmd == I -> ebcmd = ItoM -> enotsent = ALLPEERS -> esource = -1 } | 112 | :: atomic { cstate != M && ebcmd == I -> ebcmd = ItoM -> enotsent = ALLPEERS -> esource = -1 } |
