root/src/dps8/dps8_cache.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. lruLoaded
  2. lruNewer
  3. lruVictim
  4. main
  5. columnOf
  6. keyOf
  7. levelOf
  8. levelMask
  9. findLevel
  10. dirRead
  11. dirWrite
  12. dirInvalidate
  13. dirFill
  14. cacheLocksInit
  15. cacheLock
  16. cacheUnlock
  17. cacheOn
  18. bypass
  19. sinceInstr
  20. pendingClears
  21. startClear
  22. cacheable
  23. takeFetch
  24. realAddress
  25. cacheAddr
  26. absKeyOfReal
  27. purgeOne
  28. cachePurgeOthers
  29. cacheStoreNotifyOn
  30. cacheStoreBadParity
  31. cachePurgeAllOn
  32. cacheInvalidate
  33. cacheReset
  34. cacheCpuReset
  35. cacheConfigured
  36. cacheConfigChanged
  37. cacheUpdateAnyOn
  38. cacheClearStarted
  39. cacheStats
  40. cacheDirWord
  41. dumpMode
  42. dumpRead
  43. dirReadout
  44. badWordsAt
  45. parityBits
  46. cacheParityFault
  47. cacheParity
  48. blockCacheable
  49. readThrough
  50. ccheck
  51. readWord
  52. readAndClear
  53. cache_read_lock_on
  54. cache_read_on
  55. cache_read2_on
  56. cache_readN_on
  57. cache_writeN_on
  58. cache_write2_on

   1 /*
   2  * vim: filetype=c:tabstop=4:ai:expandtab
   3  * SPDX-License-Identifier: ICU
   4  * scspell-id: 35dce9de-df7a-11f1-97d6-80ee73e9b8e7
   5  *
   6  * ---------------------------------------------------------------------------
   7  *
   8  * Copyright (c) 2026 Charles Anthony
   9  * Copyright (c) 2026 Eric J. Swenson
  10  * Copyright (c) 2021-2026 The DPS8M Development Team
  11  *
  12  * This software is made available under the terms of the ICU License.
  13  * See the LICENSE.md file at the top-level directory of this distribution.
  14  *
  15  * ---------------------------------------------------------------------------
  16  */
  17 
  18 //-V::536
  19 
  20 // The DPS 8M (and L68) processor cache.
  21 //
  22 // $ clang dps8_cache.c -O0 -g -o cache -D SELF_TEST && ./cache
  23 //   (SELF_TEST checks the replacement bits against PM893's values)
  24 //
  25 // DPS 8M: 8K words, 512 columns x 4 levels of 4-word blocks. The column is
  26 // Y13-21, the word in the block Y22-23 and the directory address Y0-12.
  27 // CSH1 enables levels 0-1, CSH2 levels 2-3. Store through: a store hit
  28 // updates the cached word, a store miss does not load. Each column has 6
  29 // "least recently loaded" bits, changed only by block loads; a load goes
  30 // to an empty level of the enabled halves, else to the least recently
  31 // loaded one. T&D pages PM893 (cache logic) and PM894 (write
  32 // notification) test all of this.
  33 //
  34 // The cache is used only when it is configured, i.e. the enable_cache
  35 // switch and the enable_wam tweak are both set (the T&D and ISOLTS inis
  36 // set them; ordinary Multics runs don't).
  37 
  38 // Building with -DCCHECK compares every word read through the cache with
  39 // memory and warns about a stale entry. It is a development check: it
  40 // reads memory a second time on every cached read, hits included.
  41 
  42 // Parity fault subtype bits the cache adds: found by the cache (which
  43 // makes dps8_faults.c set fault register bits 7/8 for PARU/PARL), cache
  44 // storage (bit 33), block load (bit 35); and fault register bit 32
  45 #define PAR_CACHE     04000u
  46 #define PAR_CACHE_STR 01000u
  47 #define PAR_CACHE_BLK 02000u
  48 #define FR_CPAR_DIR   (((word36) 1) << (35 - 32))
  49 
  50 #if defined(SELF_TEST)
  51 # include <stdio.h>
  52 # include <stdint.h>
  53 # include <stdbool.h>
  54 typedef unsigned int uint;
  55 # define N_LEVELS 4
  56 #else
  57 # include <sched.h>
  58 # include "dps8.h"
  59 # include "dps8_cpu.h"
  60 # include "dps8_faults.h"
  61 # include "dps8_iom.h"
  62 # include "dps8_cable.h"
  63 # include "dps8_cache.h"
  64 #endif
  65 
  66 ////////////////////////////////////////////////////////////////////////////////
  67 //
  68 // Replacement: 6 bits per column, one per pair of levels (i,j), i < j, in
  69 // readout word bits 25-30: (0,1) (0,2) (0,3) (1,2) (1,3) (2,3), i.e. bit 5
  70 // down to bit 0 of the value. A pair's bit is 1 when level i was loaded
  71 // more recently than level j. Loading level L sets each (L,j) and clears
  72 // each (i,L), whichever halves are enabled (PM893 07C: with CSH1 only,
  73 // loading level 0 after a clear gives 70); hits, stores and clears leave
  74 // the bits alone.
  75 
  76 static const uint8_t pairBit[N_LEVELS][N_LEVELS] = {
  77   { 0, 040, 020, 010 },
  78   { 0,   0,  04,  02 },
  79   { 0,   0,   0,  01 },
  80   { 0,   0,   0,   0 },
  81 };
  82 
  83 // Update the bits for a block load into LEVEL
  84 static uint8_t lruLoaded (uint8_t lru, uint level) {
     /* [previous][next][first][last][top][bottom][index][help] */
  85   for (uint j = level + 1; j < N_LEVELS; j ++)
  86     lru |= pairBit[level][j];
  87   for (uint i = 0; i < level; i ++)
  88     lru &= (uint8_t) ~pairBit[i][level];
  89   return lru;
  90 }
  91 
  92 // Was level A loaded more recently than level B?
  93 static bool lruNewer (uint8_t lru, uint a, uint b) {
     /* [previous][next][first][last][top][bottom][index][help] */
  94   return a < b ? (lru & pairBit[a][b]) != 0 : (lru & pairBit[b][a]) == 0;
  95 }
  96 
  97 // The least recently loaded level among those in MASK (bit L = level L)
  98 static uint lruVictim (uint8_t lru, uint mask) {
     /* [previous][next][first][last][top][bottom][index][help] */
  99   for (uint l = 0; l < N_LEVELS; l ++) {
 100     if (! (mask & (1u << l)))
 101       continue;
 102     bool oldest = true;
 103     for (uint m = 0; m < N_LEVELS; m ++)
 104       if (m != l && (mask & (1u << m)) && ! lruNewer (lru, m, l)) {
 105         oldest = false;
 106         break;
 107       }
 108     if (oldest)
 109       return l;
 110   }
 111   for (uint l = 0; l < N_LEVELS; l ++)
 112     if (mask & (1u << l))
 113       return l;
 114   return 0;
 115 }
 116 
 117 #if defined(SELF_TEST)
 118 
 119 int main (void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 120   int fails = 0;
 121   // PM893: column 0 after loading levels 0, 1, 2, 3 in turn
 122   static const uint8_t want[N_LEVELS] = { 070, 036, 013, 000 };
 123   uint8_t lru = 0;
 124   for (uint l = 0; l < N_LEVELS; l ++) {
 125     lru = lruLoaded (lru, l);
 126     if (lru != want[l]) {
 127       printf ("load %u: %02o, expected %02o\n", l, lru, want[l]);
 128       fails ++;
 129     }
 130   }
 131   // Loaded in order 0..3: the victims are 0, 1, 2, 3, 0, ...
 132   for (uint n = 0; n < 8; n ++) {
 133     uint v = lruVictim (lru, 017);
 134     if (v != n % 4) {
 135       printf ("victim %u: %u, expected %u\n", n, v, n % 4);
 136       fails ++;
 137     }
 138     lru = lruLoaded (lru, v);
 139   }
 140   // CSH2 only: levels 2 and 3 alternate
 141   lru = 0;
 142   uint v1 = lruVictim (lru, 014); lru = lruLoaded (lru, v1);
 143   uint v2 = lruVictim (lru, 014); lru = lruLoaded (lru, v2);
 144   uint v3 = lruVictim (lru, 014);
 145   if (v1 != 2 || v2 != 3 || v3 != 2) {
 146     printf ("CSH2 victims %u %u %u, expected 2 3 2\n", v1, v2, v3);
 147     fails ++;
 148   }
 149   // PM893 07C, CSH1 only: level 0 after a clear -> 70; then the victim
 150   // among levels 0-1 is level 1
 151   lru = lruLoaded (0, 0);
 152   if (lru != 070 || lruVictim (lru, 003) != 1) {
 153     printf ("CSH1 only: %02o victim %u, expected 70 1\n", lru, lruVictim (lru, 003));
 154     fails ++;
 155   }
 156   printf ("%s\n", fails ? "FAIL" : "pass");
 157   return fails != 0;
 158 }
 159 
 160 #else // ! SELF_TEST
 161 
 162 # define L68 cpu.tweaks.l68_mode
 163 
 164 ////////////////////////////////////////////////////////////////////////////////
 165 //
 166 // Geometry. These take a real address (the M index, after the CPU's SCU
 167 // address map) for lookups, or the CPU's absolute address Y for readout:
 168 // the column and level bits are the same in both.
 169 
 170 static inline uint columnOf (cpu_state_t * cpup, word24 address) {
     /* [previous][next][first][last][top][bottom][index][help] */
 171   return L68 ? (address >> 2) % L68_N_COLUMNS : (address >> 2) % DPS8_N_COLUMNS;
 172 }
 173 
 174 static inline word24 keyOf (cpu_state_t * cpup, word24 address) {
     /* [previous][next][first][last][top][bottom][index][help] */
 175   return L68 ? address >> 9 : address >> 11;
 176 }
 177 
 178 // Y11-12 (L68: Y13-14) select a level for the readout and the dump
 179 static inline uint levelOf (cpu_state_t * cpup, word24 address) {
     /* [previous][next][first][last][top][bottom][index][help] */
 180   return L68 ? (address >> 9) & 3 : (address >> 11) & 3;
 181 }
 182 
 183 // The levels the CMR enables: CSH1 levels 0 and 1, CSH2 levels 2 and 3
 184 static inline uint levelMask (cpu_state_t * cpup) {
     /* [previous][next][first][last][top][bottom][index][help] */
 185   return (cpu.CMR.csh1_on ? 003u : 0u) | (cpu.CMR.csh2_on ? 014u : 0u);
 186 }
 187 
 188 ////////////////////////////////////////////////////////////////////////////////
 189 //
 190 // Directory operations; the caller holds the CPU's cache lock.
 191 
 192 // The level in MASK holding the block of real address ADDRESS, or -1
 193 static int findLevel (cpu_state_t * cpup, word24 address, uint mask) {
     /* [previous][next][first][last][top][bottom][index][help] */
 194   uint col = columnOf (cpup, address);
 195   word24 key = keyOf (cpup, address);
 196   for (uint l = 0; l < N_LEVELS; l ++) {
 197     directory * e = & cpu.directories[col][l];
 198     if ((mask & (1u << l)) && e->FE && ! e->badParity && e->key == key)
 199       return (int) l;
 200   }
 201   return -1;
 202 }
 203 
 204 // A read hit: no change to the replacement bits
 205 // A directory entry with bad parity (06A) sets fault register bit 32 (CPAR
 206 // DIR) when its column is looked up and is taken as no match, without a
 207 // fault; the block is loaded again into another level. *BAD: the hit word
 208 // was loaded with bad parity (06D).
 209 static bool dirRead (cpu_state_t * cpup, word24 address, uint mask, word36 * data, bool * bad) {
     /* [previous][next][first][last][top][bottom][index][help] */
 210   cpu.cacheProbes ++;
 211   uint col = columnOf (cpup, address);
 212   for (uint l = 0; l < N_LEVELS; l ++) {
 213     directory * e = & cpu.directories[col][l];
 214     if ((mask & (1u << l)) && e->FE && e->badParity)
 215       cpu.faultRegister[0] |= FR_CPAR_DIR;
 216   }
 217   int l = findLevel (cpup, address, mask);
 218   if (l < 0)
 219     return false;
 220   cpu.cacheHits ++;
 221   directory * e = & cpu.directories[col][l];
 222   * data = e->block[address % BLOCK_SZ];
 223   * bad = (e->badWord >> (address % BLOCK_SZ)) & 1;
 224   return true;
 225 }
 226 
 227 // A store hit updates the cached word, in the enabled halves only: a half
 228 // that is off is not written (PM893 03B: a store made after the cache is
 229 // switched off leaves the cached word as it was). Switching a half back on
 230 // clears the whole cache, so the stale word is never used. The word
 231 // written has good parity (a store with incorrect parity sets it bad
 232 // afterwards: cacheStoreBadParity).
 233 static void dirWrite (cpu_state_t * cpup, word24 address, word36 data) {
     /* [previous][next][first][last][top][bottom][index][help] */
 234   int l = findLevel (cpup, address, levelMask (cpup));
 235   if (l >= 0) {
 236     directory * e = & cpu.directories[columnOf (cpup, address)][l];
 237     e->block[address % BLOCK_SZ] = data;
 238     e->badWord &= (uint8_t) ~(1u << (address % BLOCK_SZ));
 239   }
 240 }
 241 
 242 // Clear the block (F off), at whatever level it is. The directory address
 243 // stays (PM894 reads it back) and the replacement bits don't change.
 244 static void dirInvalidate (cpu_state_t * cpup, word24 address) {
     /* [previous][next][first][last][top][bottom][index][help] */
 245   int l = findLevel (cpup, address, 017);
 246   if (l >= 0)
 247     cpu.directories[columnOf (cpup, address)][l].FE = false;
 248 }
 249 
 250 // Load the block of real address ADDRESS (BLOCK, read from memory) into a
 251 // level in MASK: an empty one if there is one, else the least recently
 252 // loaded. CPUADDR is the address as this CPU saw it, for the readout.
 253 // HALF: write only that half of the block (0: words 0-1, 1: words 2-3) and
 254 // leave the other half's old contents; -1: the whole block.
 255 static void dirFill (cpu_state_t * cpup, word24 address, word24 cpuAddr,
     /* [previous][next][first][last][top][bottom][index][help] */
 256                      const word36 block[BLOCK_SZ], uint mask, int half) {
 257   if (! mask)
 258     return;
 259   uint col = columnOf (cpup, address);
 260   int found = findLevel (cpup, address, mask);
 261   uint lvl;
 262   if (found >= 0) {
 263     lvl = (uint) found;
 264   } else {
 265     lvl = N_LEVELS;
 266     for (uint l = 0; l < N_LEVELS; l ++)
 267       if ((mask & (1u << l)) && ! cpu.directories[col][l].FE) {
 268         lvl = l;
 269         break;
 270       }
 271     if (lvl == N_LEVELS)
 272       lvl = lruVictim (cpu.cacheLRU[col], mask);
 273   }
 274   directory * e = & cpu.directories[col][lvl];
 275   for (uint w = 0; w < BLOCK_SZ; w ++)
 276     if (half < 0 || (int) (w / 2) == half)
 277       e->block[w] = block[w];
 278   e->key = keyOf (cpup, address);
 279   e->dirAddr = (word13) (keyOf (cpup, cpuAddr) & 017777);
 280   e->FE = true;
 281   e->badParity = false;
 282   e->badWord = 0;
 283   cpu.cacheLRU[col] = lruLoaded (cpu.cacheLRU[col], lvl);
 284   // CSH1 alone: once levels 0 and 1 are both full, the bits for the pairs
 285   // reaching levels 2 and 3 read 0. PM893 07C expects 70, 00, 40, 00 after
 286   // loading levels 0, 1, 0, 1 from a clear; the plain rule gives 70, 36,
 287   // 76, 36. With CSH2 alone (07D) the plain rule gives what is expected.
 288   if (mask == 003 && cpu.directories[col][0].FE && cpu.directories[col][1].FE)
 289     cpu.cacheLRU[col] &= pairBit[0][1];
 290 }
 291 
 292 
 293 ////////////////////////////////////////////////////////////////////////////////
 294 //
 295 // Locking and write notification
 296 //
 297 // Each CPU's cache directory is changed by its own thread (reads, fills,
 298 // stores) and by the threads of the other CPUs and the IOMs (purges), so
 299 // each CPU has a lock that is held around every directory operation. It
 300 // is never held across a memory access: those can fault (NEA, parity),
 301 // and doFault does not return.
 302 //
 303 // Blocks are keyed by the real memory address (the M index, after the
 304 // CPU's SCU address map), not by the CPU's absolute address, so that a
 305 // store by another CPU or an IOM, which knows only the real address, finds
 306 // the block whatever address this CPU sees it at (with useMap, as in T&D
 307 // and ISOLTS, the CPUs see the same memory at different addresses).
 308 //
 309 // Write notification (the SCU clears other ports' caches on a store): the
 310 // writer stores to memory first and then purges the block from the other
 311 // caches; the two need not be atomic. The one window that matters is a
 312 // fill that has read the block from memory before the store but installs
 313 // it after the purge: it would cache the old data for good. So each CPU
 314 // counts purges per column, and a fill installs its block only if no
 315 // purge hit the column while it was reading memory. Otherwise it reads
 316 // the block again and retries: the purge was usually for another block of
 317 // the column, and skipping the fill would leave the read uncached, which
 318 // the hardware never does (under ISOLTS, Multics' and the IOMs' stores to
 319 // the rest of memory made PM893 07H's loads miss now and then).
 320 
 321 # if defined(THREADZ) || defined(LOCKLESS)
 322 #  include <pthread.h>
 323 static pthread_mutex_t cacheLocks [N_CPU_UNITS_MAX];
 324 static pthread_once_t cacheLocksOnce = PTHREAD_ONCE_INIT;
 325 static void cacheLocksInit (void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 326   for (uint i = 0; i < N_CPU_UNITS_MAX; i ++)
 327     pthread_mutex_init (& cacheLocks[i], NULL);
 328 }
 329 static inline void cacheLock (cpu_state_t * cpup) {
     /* [previous][next][first][last][top][bottom][index][help] */
 330   (void) pthread_once (& cacheLocksOnce, cacheLocksInit);
 331   pthread_mutex_lock (& cacheLocks[cpup - cpus]);
 332 }
 333 static inline void cacheUnlock (cpu_state_t * cpup) {
     /* [previous][next][first][last][top][bottom][index][help] */
 334   pthread_mutex_unlock (& cacheLocks[cpup - cpus]);
 335 }
 336 # else
 337 #  define cacheLock(cpup)   (void) (cpup)
 338 #  define cacheUnlock(cpup) (void) (cpup)
 339 # endif
 340 
 341 // Purges per CPU per column (DPS8_N_COLUMNS covers the L68's 128 too);
 342 // read and changed only with the CPU's lock held
 343 static uint32_t purgeCount [N_CPU_UNITS_MAX] [DPS8_N_COLUMNS];
 344 
 345 // Is the cache configured on this CPU?  Write notification from other
 346 // ports depends only on this, not on the CMR.
 347 static inline bool cacheOn (cpu_state_t * cpup) {
     /* [previous][next][first][last][top][bottom][index][help] */
 348   return cpu.switches.enable_cache && cpu.tweaks.enable_wam;
 349 }
 350 
 351 // Bypass for this access:
 352 // - An access in append mode whose SDW has C off ("data from the segment
 353 //   may not be placed in the cache"), unless (DPS 8M) CMR bit 68 is set:
 354 //   AL39 DPS 8M cache note a, and bit 68 "enables the bypass option of
 355 //   SDW.C when set OFF". PM893 07I/07J run in append mode and expect
 356 //   SDW.C off to inhibit loads. A bit-29 reference made in absolute mode
 357 //   is cached whatever the SDW says: PM894 05A loads that way through an
 358 //   SDW with C off, CMR bit 68 off, and expects the blocks cached.
 359 // - The operand of cioc, a system controller command (PM894: the cioc's
 360 //   operand must not displace a cached block).
 361 // The appending unit's own reads (DSPTW, SDW, PTW) are core_* reads and
 362 // never come here.
 363 static bool bypass (cpu_state_t * cpup) {
     /* [previous][next][first][last][top][bottom][index][help] */
 364   if (cpu.currentInstruction.opcode10 == 0015) // cioc
 365     return true;
 366   if (get_addr_mode (cpup) == APPEND_mode && cpu.SDW && ! cpu.SDW->C &&
 367       (L68 || ! cpu.CMR.bypass_cache))
 368     return true;
 369   return false;
 370 }
 371 
 372 // Instructions executed since instrCnt was START (the statistics display
 373 // can reset instrCnt; count that as long ago)
 374 static inline unsigned long long sinceInstr (cpu_state_t * cpup, unsigned long long start) {
     /* [previous][next][first][last][top][bottom][index][help] */
 375   return cpu.instrCnt >= start ? cpu.instrCnt - start : ~0ULL;
 376 }
 377 
 378 // A general clear started by lcpr (a half switched on) takes time: the
 379 // cache is not used until it ends. PM893 07I and 07K load an operand one to
 380 // three instructions after such an lcpr and expect nothing loaded; every
 381 // other subtest first runs a chain of 240 transfers (M15100), after which
 382 // loads must work. The clear's real length is unknown; 128 instructions is
 383 // a value well inside those bounds.
 384 # define CACHE_CLEAR_INSTRUCTIONS 128
 385 
 386 // The directory clear of a read-and-clear instruction lands two
 387 // instructions after it, and only in a half that is on by then. PM893 02G
 388 // empties levels with sznc and, after a few transfers, expects them
 389 // reloaded into the emptied levels; 02F (ldac) and 07H (ldac, ldqc, sznc)
 390 // switch both halves off with the very next instruction, an lcpr, and
 391 // expect every block still full, the lcpr's own operand load choosing its
 392 // level as if none had been emptied.
 393 # define CACHE_CLEAR_DELAY 2
 394 
 395 // Do the pending directory clears that are due (all of them if ALL)
 396 static void pendingClears (cpu_state_t * cpup, bool all) {
     /* [previous][next][first][last][top][bottom][index][help] */
 397   cacheLock (cpup);
 398   uint n = 0;
 399   while (n < cpu.cachePendN &&
 400          (all || sinceInstr (cpup, cpu.cachePendAt[n]) >= CACHE_CLEAR_DELAY)) {
 401     int l = findLevel (cpup, cpu.cachePendAddr[n], levelMask (cpup));
 402     if (l >= 0)
 403       cpu.directories[columnOf (cpup, cpu.cachePendAddr[n])][l].FE = false;
 404     n ++;
 405   }
 406   for (uint i = n; i < cpu.cachePendN; i ++) {
 407     cpu.cachePendAddr[i - n] = cpu.cachePendAddr[i];
 408     cpu.cachePendAt[i - n] = cpu.cachePendAt[i];
 409   }
 410   cpu.cachePendN -= n;
 411   cacheUnlock (cpup);
 412 }
 413 
 414 # if defined(LOCKLESS)
 415 // Start the directory clear of a read-and-clear of the block whose
 416 // directory address (cacheAddr) is ADDR
 417 static void startClear (cpu_state_t * cpup, word24 addr) {
     /* [previous][next][first][last][top][bottom][index][help] */
 418   if (cpu.cachePendN == CACHE_PENDING_CLEARS)
 419     pendingClears (cpup, true);
 420   cacheLock (cpup);
 421   cpu.cachePendAddr[cpu.cachePendN] = addr;
 422   cpu.cachePendAt[cpu.cachePendN] = cpu.instrCnt;
 423   cpu.cachePendN ++;
 424   cacheUnlock (cpup);
 425 }
 426 # endif /* if defined(LOCKLESS) */
 427 
 428 // Does this read use the cache?  FETCH: an instruction fetch, cached only
 429 // with INST ON. (The L68's OPND ON gates operands; the DPS 8M ignores it.)
 430 static bool cacheable (cpu_state_t * cpup, bool fetch) {
     /* [previous][next][first][last][top][bottom][index][help] */
 431   if (cpu.cacheClearing) { // general clear in progress
 432     if (sinceInstr (cpup, cpu.cacheClearStart) < CACHE_CLEAR_INSTRUCTIONS)
 433       return false;
 434     cpu.cacheClearing = false;
 435   }
 436   if (! cacheOn (cpup) || ! levelMask (cpup))
 437     return false;
 438   if (fetch ? ! cpu.CMR.inst_on : (L68 && ! cpu.CMR.opnd_on))
 439     return false;
 440   return ! bypass (cpup);
 441 }
 442 
 443 // Is the read now being done an instruction fetch?  (Consumes the flag
 444 // the fetch routines set; doFault clears it too.)
 445 static void cacheParityFault (cpu_state_t * cpup, uint bits, const char * why);
 446 
 447 static inline bool takeFetch (cpu_state_t * cpup) {
     /* [previous][next][first][last][top][bottom][index][help] */
 448   bool fetch = cpu.cacheFetch;
 449   cpu.cacheFetch = false;
 450   if (cpu.cachePendN)
 451     pendingClears (cpup, false);
 452   if (cpu.cacheParPending) {
 453     uint bits = cpu.cacheParPending;
 454     cpu.cacheParPending = 0;
 455     cacheParityFault (cpup, bits, "cache block load parity");
 456   }
 457   return fetch;
 458 }
 459 
 460 // The real memory address of a CPU address, as core_read finds it (this
 461 // faults where core_read would; call it before taking the lock)
 462 static inline word24 realAddress (cpu_state_t * cpup, word24 addr) {
     /* [previous][next][first][last][top][bottom][index][help] */
 463   word24 real = addr;
 464   SC_MAP_ADDR (addr, real);
 465   return real;
 466 }
 467 
 468 // The directory keeps a block by the real address of its words, as a
 469 // store by another CPU or an IOM finds it. With SCU internal interlace
 470 // (sc_fine_map) the stores alternate double words, so a block's words are
 471 // two double words in two stores, not four consecutive words of M. The
 472 // cache itself, in the CPU, sees only absolute addresses and caches such
 473 // a block like any other (PM894 05A loads every column and level from an
 474 // interlaced store), so it is kept by its absolute address instead,
 475 // tagged with ABS_KEY so that it can't match a block kept by real
 476 // address; a store or purge, which knows the real address, finds it
 477 // through sc_fine_unmap. (The column and key are taken from the tagged
 478 // address as from a real one; the tag is above the column bits.)
 479 # define ABS_KEY ((word24) 1 << 24)
 480 
 481 // Where the directory keeps the word at CPU address ADDR, real address REAL
 482 static inline word24 cacheAddr (cpu_state_t * cpup, word24 addr, word24 real) {
     /* [previous][next][first][last][top][bottom][index][help] */
 483   if (cpu.sc_fine && sc_fine_interlaced (cpup, addr))
 484     return addr | ABS_KEY;
 485   return real;
 486 }
 487 
 488 // The directory address of the interlaced block holding the word at real
 489 // address REAL, if there is one
 490 static inline bool absKeyOfReal (cpu_state_t * cpup, word24 real, word24 * caddr) {
     /* [previous][next][first][last][top][bottom][index][help] */
 491   word24 a;
 492   if (! cpu.sc_fine || ! sc_fine_unmap (cpup, real, & a))
 493     return false;
 494   * caddr = a | ABS_KEY;
 495   return true;
 496 }
 497 
 498 // Drop the block holding real address ADDR from one CPU's cache
 499 static void purgeOne (cpu_state_t * cpup, word24 addr) {
     /* [previous][next][first][last][top][bottom][index][help] */
 500   if (! cacheOn (cpup))
 501     return;
 502   word24 caddr;
 503   bool fine = absKeyOfReal (cpup, addr, & caddr);
 504   cacheLock (cpup);
 505   dirInvalidate (cpup, addr);
 506   purgeCount[cpup - cpus][columnOf (cpup, addr)] ++;
 507   if (fine) {
 508     dirInvalidate (cpup, caddr);
 509     purgeCount[cpup - cpus][columnOf (cpup, caddr)] ++;
 510   }
 511   cacheUnlock (cpup);
 512 }
 513 
 514 // A CPU stored to real address ADDR: purge the block from every other
 515 // CPU's cache. The writer is skipped by identity, not by SCU port: with a
 516 // port expander several CPUs share a port, and they must all be purged.
 517 void cachePurgeOthers (cpu_state_t * cpup, word24 addr) {
     /* [previous][next][first][last][top][bottom][index][help] */
 518   uint self = (uint) (cpup - cpus);
 519   for (uint i = 0; i < cpu_dev.numunits && i < N_CPU_UNITS_MAX; i ++)
 520     if (i != self)
 521       purgeOne (& cpus[i], addr);
 522 }
 523 
 524 // This CPU stored DATA to real address ADDR (every core_write* path calls
 525 // this after the store, including the appending unit's direct PTW/SDW
 526 // updates): update its own cached copy, if any (the cache is store
 527 // through), then purge the block from the other CPUs' caches.
 528 void cacheStoreNotifyOn (cpu_state_t * cpup, word24 addr, word36 data) {
     /* [previous][next][first][last][top][bottom][index][help] */
 529   if (cacheOn (cpup)) {
 530     word24 caddr;
 531     bool fine = absKeyOfReal (cpup, addr, & caddr);
 532     cacheLock (cpup);
 533     dirWrite (cpup, addr, data);
 534     if (fine)
 535       dirWrite (cpup, caddr, data);
 536     cacheUnlock (cpup);
 537   }
 538   cachePurgeOthers (cpup, addr);
 539 }
 540 
 541 // T&D: this CPU stored to CPU address ADDR with incorrect parity, which the
 542 // store unit keeps (bad_parity_mark). The store updated this CPU's cached
 543 // copy, if any, as any store does (cacheStoreNotify); that copy has the
 544 // incorrect parity too, so a later hit on it is a cache storage parity
 545 // error. A half that is off was not written, and its word keeps the parity
 546 // it had. The other CPUs' copies were purged by the store.
 547 void cacheStoreBadParity (cpu_state_t * cpup, word24 addr) {
     /* [previous][next][first][last][top][bottom][index][help] */
 548   if (! cacheOn (cpup))
 549     return;
 550   word24 caddr = cacheAddr (cpup, addr, realAddress (cpup, addr));
 551   cacheLock (cpup);
 552   int l = findLevel (cpup, caddr, levelMask (cpup));
 553   if (l >= 0)
 554     cpu.directories[columnOf (cpup, caddr)][l].badWord |= (uint8_t) (1u << (caddr % BLOCK_SZ));
 555   cacheUnlock (cpup);
 556 }
 557 
 558 // A store from outside the CPUs (an IOM, DIA, simh deposit): purge every
 559 // CPU
 560 void cachePurgeAllOn (word24 addr) {
     /* [previous][next][first][last][top][bottom][index][help] */
 561   for (uint i = 0; i < cpu_dev.numunits && i < N_CPU_UNITS_MAX; i ++)
 562     purgeOne (& cpus[i], addr);
 563 }
 564 
 565 // Clear the block of CPU address ADDR from this CPU's cache (the
 566 // appending unit's direct PTW/SDW updates, after storing to ADDR, so
 567 // mapping it can't fault). Nothing reads the directory while the cache is
 568 // off, and switching it on in the configuration clears it.
 569 void cacheInvalidate (cpu_state_t * cpup, word24 addr) {
     /* [previous][next][first][last][top][bottom][index][help] */
 570   if (! cacheOn (cpup))
 571     return;
 572   word24 caddr = cacheAddr (cpup, addr, realAddress (cpup, addr));
 573   cacheLock (cpup);
 574   dirInvalidate (cpup, caddr);
 575   cacheUnlock (cpup);
 576 }
 577 
 578 // General clear (CSH1/CSH2 off to on, ldbr, CPU reset): every level empty,
 579 // with directory address 0 (PM893 07I, 07K read back 0 after one), and
 580 // every column's replacement bits 0. (A selective clear keeps the address.)
 581 void cacheReset (cpu_state_t * cpup) {
     /* [previous][next][first][last][top][bottom][index][help] */
 582   cacheLock (cpup);
 583   for (uint c = 0; c < N_COLUMNS; c ++) {
 584     for (uint l = 0; l < N_LEVELS; l ++) {
 585       cpu.directories[c][l].FE = false;
 586       cpu.directories[c][l].badParity = false;
 587       cpu.directories[c][l].badWord = 0;
 588       cpu.directories[c][l].key = 0;
 589       cpu.directories[c][l].dirAddr = 0;
 590     }
 591     cpu.cacheLRU[c] = 0;
 592   }
 593   cpu.cachePendN = 0;
 594   cacheUnlock (cpup);
 595 }
 596 
 597 // CPU reset (initialize): an empty cache, and nothing left over from
 598 // before the reset that could act afterwards: a deferred block-load
 599 // parity fault, a pending read-and-clear, a general clear in progress, a
 600 // pending fetch or directory-readout flag.
 601 void cacheCpuReset (cpu_state_t * cpup) {
     /* [previous][next][first][last][top][bottom][index][help] */
 602   cacheReset (cpup);
 603   cpu.cacheParPending = 0;
 604   cpu.cacheFetch = false;
 605   cpu.cacheDirReadout = false;
 606   cpu.cacheClearing = false;
 607   cpu.cacheClearStart = 0;
 608 }
 609 
 610 // Is the cache configured on this CPU (enable_cache and enable_wam)?
 611 bool cacheConfigured (cpu_state_t * cpup) {
     /* [previous][next][first][last][top][bottom][index][help] */
 612   return cacheOn (cpup);
 613 }
 614 
 615 // The CPU's enable_cache or enable_wam setting was changed, and the cache
 616 // was configured (WAS) before. While the cache is not configured, stores
 617 // and purges don't reach the directory, so entries left from before would
 618 // be stale when it is configured again: empty it whenever the setting
 619 // changes.
 620 void cacheConfigChanged (cpu_state_t * cpup, bool was) {
     /* [previous][next][first][last][top][bottom][index][help] */
 621   if (cacheOn (cpup) != was)
 622     cacheReset (cpup);
 623   cacheUpdateAnyOn ();
 624 }
 625 
 626 // See dps8_cache.h. Called when a CPU's configuration changes and when the
 627 // simulation starts running (some configuration changes, such as ISOLTS
 628 // mode, set the enables directly). While no cache was on, the fetch and
 629 // directory-readout flags were not taken; drop them when one comes on.
 630 # if defined(THREADZ) || defined(LOCKLESS)
 631 atomic_bool cacheAnyOn = false;
 632 # else
 633 bool cacheAnyOn = false;
 634 # endif
 635 
 636 void cacheUpdateAnyOn (void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 637   bool on = false;
 638   for (uint i = 0; i < N_CPU_UNITS_MAX; i ++)
 639     if (cacheOn (& cpus[i]))
 640       on = true;
 641   if (on && ! CACHE_ANY_ON ()) //-V652
 642     for (uint i = 0; i < N_CPU_UNITS_MAX; i ++) {
 643       cpus[i].cacheFetch = false;
 644       cpus[i].cacheDirReadout = false;
 645     }
 646 # if defined(THREADZ) || defined(LOCKLESS)
 647   atomic_store_explicit (& cacheAnyOn, on, memory_order_relaxed);
 648 # else
 649   cacheAnyOn = on;
 650 # endif
 651 }
 652 
 653 void cacheClearStarted (cpu_state_t * cpup) {
     /* [previous][next][first][last][top][bottom][index][help] */
 654   cpu.cacheClearing = true;
 655   cpu.cacheClearStart = cpu.instrCnt;
 656 }
 657 
 658 void cacheStats (uint cpuNo) {
     /* [previous][next][first][last][top][bottom][index][help] */
 659   (void)fflush(stdout);
 660   (void)fflush(stderr);
 661   sim_msg ("\r\n|  Cache Probes: %15llu  |\r\n", (unsigned long long) cpus[cpuNo].cacheProbes);
 662   sim_msg ("|  Cache Hits:   %15llu  |\r\n", (unsigned long long) cpus[cpuNo].cacheHits);
 663   sim_msg ("+---------------------------------+");
 664   (void)fflush(stdout);
 665   (void)fflush(stderr);
 666 }
 667 
 668 ////////////////////////////////////////////////////////////////////////////////
 669 //
 670 // Directory readout (scpr ...,06) and cache-to-register (dump) mode
 671 
 672 // The directory fields of the scpr ,06 odd word for CPU absolute address
 673 // Y: column Y13-21, level Y11-12, Y22 = 0 primary, 1 duplicate.
 674 //   bits 0-12  directory address        (primary and duplicate)
 675 //   bit  15    directory parity error   (primary)
 676 //   bit  16    level full               (primary and duplicate)
 677 //   bits 25-30 the column's replacement bits (primary; 0 in the duplicate)
 678 // The caller ORs these into the CMR fields (18-24, 34-35). The duplicate
 679 // directory, which exists for write notification, is shown as a copy of
 680 // the primary's address and full bit.
 681 word36 cacheDirWord (cpu_state_t * cpup, word24 y) {
     /* [previous][next][first][last][top][bottom][index][help] */
 682   if (! cacheOn (cpup))
 683     return 0;
 684   if (cpu.cachePendN)
 685     pendingClears (cpup, false);
 686   uint col = columnOf (cpup, y);
 687   uint lvl = levelOf (cpup, y);
 688   bool dup = (y >> 1) & 1;
 689   cacheLock (cpup);
 690   directory * e = & cpu.directories[col][lvl];
 691   word36 w = ((word36) (e->dirAddr & 017777)) << (35 - 12);
 692   if (e->FE)
 693     w |= ((word36) 1) << (35 - 16);
 694   if (! dup) {
 695     if (e->badParity)
 696       w |= ((word36) 1) << (35 - 15);
 697     w |= ((word36) (cpu.cacheLRU[col] & 077)) << (35 - 30);
 698   }
 699   cacheUnlock (cpup);
 700   return w;
 701 }
 702 
 703 // Cache-to-register mode: with CSH REG set, a double-word operations unit
 704 // load reads two words straight from the cache array: level Y11-12, column
 705 // Y13-21, pair Y22; no directory match, no load, no change to the
 706 // replacement bits. It works with the halves off (PM893 dumps the cache
 707 // after switching it off). Only ldaq is known to be such a load (PM893
 708 // uses only ldaq; whether dfld and the like also qualify is not known).
 709 static bool dumpMode (cpu_state_t * cpup, bool fetch) {
     /* [previous][next][first][last][top][bottom][index][help] */
 710   return ! fetch && cpu.CMR.csh_reg && cacheOn (cpup) &&
 711          cpu.currentInstruction.opcode10 == 0237; // ldaq
 712 }
 713 
 714 static void dumpRead (cpu_state_t * cpup, word24 y, word36 * even, word36 * odd) {
     /* [previous][next][first][last][top][bottom][index][help] */
 715   uint col = columnOf (cpup, y);
 716   uint lvl = levelOf (cpup, y);
 717   uint pair = (y >> 1) & 1;
 718   cacheLock (cpup);
 719   * even = cpu.directories[col][lvl].block[pair * 2];
 720   * odd  = cpu.directories[col][lvl].block[pair * 2 + 1];
 721   cacheUnlock (cpup);
 722 }
 723 
 724 // A scpr ,06 store: add the directory fields to the odd word
 725 static inline void dirReadout (cpu_state_t * cpup, word24 y, word36 * odd) {
     /* [previous][next][first][last][top][bottom][index][help] */
 726   if (cpu.cacheDirReadout) {
 727     cpu.cacheDirReadout = false;
 728     * odd |= cacheDirWord (cpup, y);
 729   }
 730 }
 731 
 732 ////////////////////////////////////////////////////////////////////////////////
 733 //
 734 //  cache layer entry points
 735 //
 736 // ADDR is the CPU's absolute address; the core_* routines map it to the
 737 // real address and, for stores, purge the other CPUs' caches. The cache
 738 // directory is keyed by the real address (see "Locking" above).
 739 
 740 # if defined(CCHECK)
 741 static int inconsCnt = 32;
 742 # endif
 743 
 744 // Read one word through the cache, using the levels in MASK: a hit is read
 745 // from the cache; a miss reads the block from memory (unlocked) and loads
 746 // it, reading it again if a purge hit its column meanwhile (the word first
 747 // read is returned).
 748 // T&D: which words of the block at BLK carry bad parity in memory
 749 // (bad_parity_mark), bit w = word w
 750 static uint badWordsAt (cpu_state_t * cpup, word24 blk) {
     /* [previous][next][first][last][top][bottom][index][help] */
 751   if (! nBadParity || ! cpu.tweaks.tandd_mode)
 752     return 0;
 753   uint bad = 0;
 754   for (uint w = 0; w < BLOCK_SZ; w ++)
 755     if (bad_parity_marked (blk + w))
 756       bad |= 1u << w;
 757   return bad;
 758 }
 759 
 760 // The processor parity bits for a bad word in the pair at even word W of a
 761 // block whose bad words are BAD, read at word READW: PARU/PARL as
 762 // bad_parity_read sets them (an odd word read sets both)
 763 static uint parityBits (uint bad, uint w, uint readw) {
     /* [previous][next][first][last][top][bottom][index][help] */
 764   bool upper = (bad >> w) & 1, lower = (bad >> (w + 1)) & 1;
 765   if (readw & 1)
 766     upper = lower = true;
 767   return (upper ? 0100u : 0u) | (lower ? 0200u : 0u);
 768 }
 769 
 770 // A parity error the cache found: the fault, or with the parity mask on
 771 // only the indicator. The fault also switches both halves off: after one,
 772 // PM893 06B-06D store into the cached block (restoring absolute 0-3) with
 773 // the CMR untouched and expect the cache still to hold the old data, as a
 774 // store into a half that is off leaves it (03B).
 775 static void cacheParityFault (cpu_state_t * cpup, uint bits, const char * why) {
     /* [previous][next][first][last][top][bottom][index][help] */
 776   cpu.CMR.csh1_on = 0;
 777   cpu.CMR.csh2_on = 0;
 778   cpu.cacheFetch = false;
 779   doFault (FAULT_PAR, (_fault_subtype) {.bits=bits | PAR_CACHE}, why);
 780 }
 781 
 782 static void cacheParity (cpu_state_t * cpup, uint bits, const char * why) {
     /* [previous][next][first][last][top][bottom][index][help] */
 783   SETF (cpu.cu.IR, I_PERR);
 784   if (! TST_I_PMASK)
 785     cacheParityFault (cpup, bits, why);
 786 }
 787 
 788 // Can the block at CPU address BLK, whose first word is at real address
 789 // REALBLK, be kept by its real address?  The entry holds four consecutive
 790 // words of M, found (and purged) by the real address of any of them. The
 791 // bank map keeps blocks whole, and so does sc_fine_map without interlace
 792 // (in 32K units); this is a check. Interlaced blocks are kept by absolute
 793 // address (cacheAddr) and don't come here.
 794 static bool blockCacheable (cpu_state_t * cpup, word24 blk, word24 realBlk) {
     /* [previous][next][first][last][top][bottom][index][help] */
 795   if (! cpu.sc_fine)
 796     return true;
 797   if (realBlk % BLOCK_SZ)
 798     return false;
 799   for (uint w = 1; w < BLOCK_SZ; w ++)
 800     if (realAddress (cpup, blk + w) != realBlk + w)
 801       return false;
 802   return true;
 803 }
 804 
 805 // CTX: what the read is for, as for core_read; NULL for the cache check's
 806 // own reads, which are not reported to the debugger's read watch.
 807 static void readThrough (cpu_state_t * cpup, word24 addr, word36 * data, uint mask,
     /* [previous][next][first][last][top][bottom][index][help] */
 808                          const char * ctx) {
 809   (void) ctx; // only the read watch uses it, and SPEED builds have none
 810   word24 real = realAddress (cpup, addr);
 811   word24 caddr = cacheAddr (cpup, addr, real);
 812   uint col = columnOf (cpup, caddr);
 813   bool bad = false;
 814   cacheLock (cpup);
 815   bool hit = dirRead (cpup, caddr, mask, data, & bad);
 816   uint32_t count = purgeCount[cpup - cpus][col];
 817   cacheUnlock (cpup);
 818   if (hit) {
 819 # if !defined(SPEED)
 820     // A hit is reported to a read watch on the word as a memory read is
 821     // (core_read)
 822     if (ctx)
 823       cpu_watch_read (cpup, real, * data, ctx);
 824 # endif
 825     // A word loaded with bad parity: cache storage parity, bit 33 (06D)
 826     if (bad)
 827       cacheParity (cpup, parityBits (1u << (addr % BLOCK_SZ), (addr % BLOCK_SZ) & ~1u,
 828                                      addr % BLOCK_SZ) | PAR_CACHE_STR, "cache storage parity");
 829     return;
 830   }
 831   // A block load reads the half holding the requested word first, then the
 832   // other half (AL39 section 9). Bad parity in the first half faults with
 833   // the processor parity bits once the block is loaded (06B: the block is
 834   // full afterwards); in the second half the instruction gets its word and
 835   // completes, and the fault, with bit 35 (CPAR BLK) too, follows (06C).
 836   // With the parity mask on there is no fault, and the bad words are
 837   // cached as such (06D).
 838   word24 blk = BLK_ADDR (addr);
 839   uint req = (addr % BLOCK_SZ) & 2;
 840   uint badWords = badWordsAt (cpup, blk);
 841   word36 block[BLOCK_SZ];
 842   if (badWords) {
 843     // read the words without faulting again: bad_parity_read only sets
 844     // the indicator while the parity mask is on
 845     word18 ir = cpu.cu.IR;
 846     SETF (cpu.cu.IR, I_PMASK);
 847     core_readN (cpup, blk, block, BLOCK_SZ, __func__);
 848     cpu.cu.IR = ir;
 849     SETF (cpu.cu.IR, I_PERR);
 850     if (! (badWords & (3u << req)) && (badWords & (3u << (req ^ 2))) && ! TST_I_PMASK)
 851       cpu.cacheParPending = parityBits (badWords, req ^ 2, req ^ 2) | PAR_CACHE_BLK;
 852   } else {
 853     core_readN (cpup, blk, block, BLOCK_SZ, __func__);
 854   }
 855   * data = block[addr % BLOCK_SZ];
 856   // The block's words were all read above, so mapping them can't fault
 857   word24 cblk = BLK_ADDR (caddr) | (caddr & ABS_KEY);
 858   bool keep = (caddr & ABS_KEY) || blockCacheable (cpup, blk, realAddress (cpup, blk));
 859   // An lcpr ,02 whose operand turns both halves off has switched the cache
 860   // off before the second half arrives: only the first half is written,
 861   // though the directory entry is updated (PM893 03B: the lcpr's own
 862   // operand block).
 863   int half = -1;
 864   if (cpu.currentInstruction.opcode10 == 0674 && cpu.currentInstruction.tag == 02 &&
 865       ((* data >> (35 - 19)) & 3) == 0) // CSH1, CSH2 (word bits 18, 19) both off
 866     half = (int) ((addr % BLOCK_SZ) / 2);
 867   cacheLock (cpup);
 868   // A purge hit the column while the block was being read: read it again
 869   // (a few times at most; then leave it uncached)
 870   for (int retry = 0; retry < 4 && purgeCount[cpup - cpus][col] != count; retry ++) {
 871     count = purgeCount[cpup - cpus][col];
 872     cacheUnlock (cpup);
 873     if (badWords) {
 874       word18 ir = cpu.cu.IR;
 875       SETF (cpu.cu.IR, I_PMASK);
 876       core_readN (cpup, blk, block, BLOCK_SZ, __func__);
 877       cpu.cu.IR = ir;
 878     } else {
 879       core_readN (cpup, blk, block, BLOCK_SZ, __func__);
 880     }
 881     cacheLock (cpup);
 882   }
 883   if (keep && purgeCount[cpup - cpus][col] == count) {
 884     dirFill (cpup, cblk, blk, block, mask, half);
 885     // A load with the mode register's "store bad ZAC parity" bit set
 886     // writes the directory entry with bad parity (PM893 06A)
 887     if (cpu.MR.separ || badWords) {
 888       for (uint l = 0; l < N_LEVELS; l ++) {
 889         directory * e = & cpu.directories[col][l];
 890         if (e->FE && ! e->badParity && e->key == keyOf (cpup, cblk)) {
 891           e->badParity = cpu.MR.separ;
 892           e->badWord = (uint8_t) badWords;
 893         }
 894       }
 895     }
 896   }
 897   cacheUnlock (cpup);
 898   if (badWords & (3u << req))
 899     cacheParity (cpup, parityBits (badWords, req, addr % BLOCK_SZ), "block load parity");
 900 }
 901 
 902 # if defined(CCHECK)
 903 // Compare a word read through the cache with memory. Another CPU or an
 904 // IOM may store to the word between the two reads, so on a mismatch read
 905 // both again: only a cache that still differs from an unchanged memory
 906 // word is stale.
 907 static void ccheck (cpu_state_t * cpup, word24 addr, word36 data, uint mask, const char * who) {
     /* [previous][next][first][last][top][bottom][index][help] */
 908   word36 w0, w1, c1;
 909   if (nBadParity && cpu.tweaks.tandd_mode) // reading memory could fault
 910     return;
 911   core_read (cpup, addr, & w0, __func__);
 912   if (w0 == data)
 913     return;
 914   // A store by another CPU or an IOM puts the word in memory first and
 915   // purges this cache after, so a hit can differ from memory for a
 916   // moment (harmless: that writer's later stores become visible only
 917   // after the purge). Give the writer time to finish before calling the
 918   // entry stale.
 919   for (int i = 0; i < 1000; i ++) {
 920     sched_yield ();
 921     readThrough (cpup, addr, & c1, mask, NULL);
 922     core_read (cpup, addr, & w1, __func__);
 923     if (c1 == w1 || w1 != w0 || c1 != data)
 924       return; // memory or the cache changed meanwhile: a race, not staleness
 925   }
 926   if (inconsCnt > 0) {
 927     inconsCnt --;
 928     sim_warn ("%s inconsistent %08o core: %012llo cache: %012llo\r\n", who, addr, w0, data);
 929 #  if defined(TESTING)
 930     hdbgNote (__func__, "%s inconsistent %08o core: %012llo cache: %012llo\r\n", who, addr, w0, data);
 931 #  endif
 932   }
 933 }
 934 # endif
 935 
 936 // One word, after the fetch flag has been taken
 937 static void readWord (cpu_state_t * cpup, word24 addr, word36 * data, bool fetch, const char * ctx) {
     /* [previous][next][first][last][top][bottom][index][help] */
 938   if (! cacheable (cpup, fetch)) {
 939     core_read (cpup, addr, data, ctx);
 940     return;
 941   }
 942   uint mask = levelMask (cpup);
 943   readThrough (cpup, addr, data, mask, ctx);
 944 # if defined(CCHECK)
 945   ccheck (cpup, addr, * data, mask, ctx);
 946 # endif
 947 }
 948 
 949 # if defined(LOCKLESS)
 950 // Read-and-clear and the gate instructions (AL39, DPS 8M cache note 3b:
 951 // "LDAC, LDQC, STAC, STACQ, and SZNC automatically bypass cache"). They
 952 // read memory, and a matching cached block is cleared (note 3c), with the
 953 // delay described at CACHE_CLEAR_DELAY. Until then the rewrite updates the
 954 // cached word like any store, so the cache stays coherent.
 955 static bool readAndClear (cpu_state_t * cpup) {
     /* [previous][next][first][last][top][bottom][index][help] */
 956   switch (cpu.currentInstruction.opcode10) {
 957     case 0034: // ldac
 958     case 0032: // ldqc
 959     case 0214: // sznc
 960     case 0354: // stac
 961     case 0654: // stacq
 962       return true;
 963     default:
 964       return false;
 965   }
 966 }
 967 
 968 int cache_read_lock_on (cpu_state_t * cpup, word24 addr, word36 *data, const char * ctx) {
     /* [previous][next][first][last][top][bottom][index][help] */
 969   (void) takeFetch (cpup);
 970   if (! cacheOn (cpup))
 971     return core_read_lock (cpup, addr, data, ctx);
 972   if (readAndClear (cpup)) {
 973     int r = core_read_lock (cpup, addr, data, ctx);
 974     startClear (cpup, cacheAddr (cpup, addr, realAddress (cpup, addr)));
 975     return r;
 976   }
 977   if (! cacheable (cpup, false))
 978     return core_read_lock (cpup, addr, data, ctx);
 979   // An ordinary read-alter-rewrite uses the cache like any read: a hit is
 980   // read from the cache, a miss loads the block; the rewrite
 981   // (cache_write_unlock) updates the cached word and stores through. Lock
 982   // the memory word too, as core_read_lock does.
 983   word36 w;
 984   int rc = core_read_lock (cpup, addr, & w, ctx);
 985   readThrough (cpup, addr, data, levelMask (cpup), ctx);
 986 #  if defined(CCHECK)
 987   if (w != * data && inconsCnt > 0) {
 988     inconsCnt --;
 989     sim_warn ("cache_read_lock inconsistent %08o core: %012llo cache: %012llo\r\n", addr, w, *data);
 990   }
 991 #  endif
 992   return rc;
 993 }
 994 # endif /* if defined(LOCKLESS) */
 995 
 996 
 997 // Stores: the core_* routines store to memory and then (cacheStoreNotify)
 998 // update this CPU's cached copy and purge the other CPUs' caches.
 999 
1000 
1001 int cache_read_on (cpu_state_t * cpup, word24 addr, word36 *data, const char * ctx) {
     /* [previous][next][first][last][top][bottom][index][help] */
1002   bool fetch = takeFetch (cpup);
1003   readWord (cpup, addr, data, fetch, ctx);
1004   return 0;
1005 }
1006 
1007 
1008 int cache_read2_on (cpu_state_t * cpup, word24 addr, word36 *even, word36 *odd, const char * ctx) {
     /* [previous][next][first][last][top][bottom][index][help] */
1009   bool fetch = takeFetch (cpup);
1010   addr &= (word24) ~1; // a pair is even/odd, as in core_read2
1011   if (dumpMode (cpup, fetch)) {
1012     dumpRead (cpup, addr, even, odd);
1013     return 0;
1014   }
1015   if (! cacheable (cpup, fetch))
1016     return core_read2 (cpup, addr, even, odd, ctx);
1017   readWord (cpup, addr, even, fetch, ctx);
1018   readWord (cpup, addr + 1, odd, fetch, ctx);
1019   return 0;
1020 }
1021 
1022 
1023 void cache_readN_on (cpu_state_t * cpup, word24 addr, word36 * data, uint n, UNUSED const char * ctx) {
     /* [previous][next][first][last][top][bottom][index][help] */
1024   bool fetch = takeFetch (cpup);
1025   if (n == 2 && dumpMode (cpup, fetch)) {
1026     dumpRead (cpup, addr & (word24) ~1, data, data + 1);
1027     return;
1028   }
1029   if (! cacheable (cpup, fetch)) {
1030     core_readN (cpup, addr, data, n, ctx);
1031     return;
1032   }
1033   for (uint i = 0; i < n; i ++)
1034     readWord (cpup, addr + i, data + i, fetch, ctx);
1035 }
1036 
1037 
1038 void cache_writeN_on (cpu_state_t * cpup, word24 addr, word36 * data, uint n, UNUSED const char * ctx) {
     /* [previous][next][first][last][top][bottom][index][help] */
1039   if (n == 2)
1040     dirReadout (cpup, addr, data + 1);
1041   for (uint i = 0; i < n; i ++)
1042     (void) cache_write (cpup, addr + i, data[i], ctx);
1043 }
1044 
1045 
1046 int cache_write2_on (cpu_state_t * cpup, word24 addr, word36 even, word36 odd, const char * ctx) {
     /* [previous][next][first][last][top][bottom][index][help] */
1047   dirReadout (cpup, addr, & odd);
1048   return core_write2 (cpup, addr, even, odd, ctx);
1049 }
1050 
1051 #endif // ! SELF_TEST

/* [previous][next][first][last][top][bottom][index][help] */