Skip to content

CPU_MMU_24

Source: Verilog/CPU-BOARD-3202/circuit/CPU_MMU_24.v

Where it sits (Simulation): ND120_TOP > ND120_CORE > ND3202D > CPU_15 > CPU_MMU_24 - instance path: CORE.CPU_BOARD.CPU.MMU

Used in: CPU_15 (all tops)

Contains: CPU_MMU_CACHE_25, CPU_MMU_CSR_26, CPU_MMU_HIT_27, CPU_MMU_PPNX_28, CPU_MMU_PT_29, CPU_MMU_PTIDB_30, PAL_44306A

Module hierarchy - All modules

CPU_MMU_24 symbol

Schematic

Drawn from the Verilog: the yosys netlist of the Simulation (Verilator) build, instance CORE.CPU_BOARD.CPU.MMU. Sub-modules are boxes (click the picture to open it full size; there every sub-module box links to its page, and every wire shows its Verilog name).

CPU_MMU_24 schematic

Description

ND120 CPU, MM&M CPU/MMU MMU TOP LEVEL SHEET 24 of 50 Last reviewed: 2-FEB-2025 Ronny Hansen

Ports

Direction Width Name Description
input 1 sysclk System clock in FPGA
input 1 sys_rst_n (active low) System reset in FPGA
input 1 BRK_n (active low) CPU Break signal
input [10:0] CA_10_0 Cache address, 11 bits
input 1 CC2_n (active low) Cycle clock 2
input 1 CCLR_n (active low) Cache clear
input 1 CUP Cache updated
input 1 CWR Cache write
input 1 CYD Cycle done
input 1 DOUBLE Extended Adressing Mode (SEXI)
input 1 DT_n (active low) Data transfer
input 1 DVACC_n (active low) DGA access qualifier, active low (see comment above)
input 1 ECSR_n (active low) Enable cache status register
input 1 EDO_n (active low) Enable data output
input 1 EMCL_n (active low) Enable master clear
input 1 EMPID_n (active low) Interrupt disable
input 1 EORF_n (active low) End of Read Flag
input 1 ESTOF_n (active low) Enable store of Fault
input 1 FMISS Force miss
input [10:0] LA_20_10 Logical address, 11 bits
input 1 LCS_n (active low) Load control store
input 1 LSHADOW Load shadow signal
input 1 PD2 Power down 2
input 1 RT_n (active low) Return
input 1 STP Stop signal
input 1 SW1_CONSOLE Switch on the console (on/off)
input 1 UCLK User clock
input 1 UCLK_EN UCLK clock-enable pulse (FPGA_FF_MODE, else 0)
input 1 WCHIM_n (active low) Write cache inhibit
input 1 WRITE Write enable
input [15:0] IDB_15_0_IN Internal data bus input, 16 bits
output [15:0] IDB_15_0_OUT Internal data bus output, 16 bits
input [15:0] CD_15_0_IN Cache data input, 16 bits
output [15:0] CD_15_0_OUT Cache data output, 16 bits
input [15:0] PPN_25_10_IN Physical page number input, 16 bits
output [15:0] PPN_25_10_OUT Physical page number output, 16 bits
output 1 BEDO_n (active low) Buffered Enable IDB "data out" from CGA
output 1 BEMPID_n (active low) Buffered EMPID - Interrupt Disable (EPIC.LDMPIE->set mask reg:inh all ints)
output 1 BLCS_n (active low) Bus LCS (Load Control Store)
output 1 BSTP Bus Stop
output 1 HIT Cache hit signal, indicates a successful cache lookup
output 1 LAPA_n (active low) Latch Page Address, controls latching of the page address
output [6:0] PT_15_9_OUT Page Table data output, top 7 bits
output 1 WCA_n (active low) Write Cache Address, controls writing to the cache address register
output [7:0] DBG_CACHE
output 1 LED1 UNKNOWN: believed to indicate cache enabled, never traced. See Verilog/docs/SIGNALS.md
output [15:0] DBG_PTW
output 1 DBG_PTW_LVL

Verilog source

Verilog/CPU-BOARD-3202/circuit/CPU_MMU_24.v on GitHub.

Show the Verilog of CPU_MMU_24 (665 lines)
/**************************************************************************
** ND120 CPU, MM&M                                                       **
** CPU/MMU                                                               **
** MMU TOP LEVEL                                                         **
** SHEET 24 of 50                                                        **
**                                                                       **
** Last reviewed: 2-FEB-2025                                             **
** Ronny Hansen                                                          **
***************************************************************************/

module CPU_MMU_24 (
    input sysclk,    // System clock in FPGA
    input sys_rst_n, // System reset in FPGA

    input        BRK_n,          //! CPU Break signal
    input [10:0] CA_10_0,        //! Cache address, 11 bits
    input        CC2_n,          //! Cycle clock 2
    input        CCLR_n,         //! Cache clear
    input        CUP,            //! Cache updated
    input        CWR,            //! Cache write
    input        CYD,            //! Cycle done
    input        DOUBLE,         //! Extended Adressing Mode (SEXI)
    input        DT_n,           //! Data transfer
    // DVACC_n comes from the DECODER gate array (DECODE_DGA_COMM.v flip-flop
    // A227 on CLK2) via IO_37, and is the access qualifier for the page-table
    // control PAL below: PAL_44306A uses it (input I2) together with WRITE,
    // DOUBLE, WCA_n and LSHADOW to decide ECD_n / LAPA_n, i.e. when the page
    // table is actually addressed. NOT the CGA's VACC (CGA_DCD.v) - different
    // net, same name.
    input        DVACC_n,        //! DGA access qualifier, active low (see comment above)
    input        ECSR_n,         //! Enable cache status register
    input        EDO_n,          //! Enable data output
    input        EMCL_n,         //! Enable master clear
    input        EMPID_n,        //! Interrupt disable
    input        EORF_n,         //! End of Read Flag
    input        ESTOF_n,        //! Enable store of Fault
    input        FMISS,          //! Force miss
    input [10:0] LA_20_10,       //! Logical address, 11 bits
    input        LCS_n,          //! Load control store
    input        LSHADOW,        //! Load shadow signal
    input        PD2,            //! Power down 2
    input        RT_n,           //! Return
    input        STP,            //! Stop signal
    input        SW1_CONSOLE,    //! Switch on the console (on/off)
    input        UCLK,           //! User clock
    input        UCLK_EN,        //! UCLK clock-enable pulse (FPGA_FF_MODE, else 0)
    input        WCHIM_n,        //! Write cache inhibit
    input        WRITE,          //! Write enable

    input  [15:0] IDB_15_0_IN,   //! Internal data bus input, 16 bits
    output [15:0] IDB_15_0_OUT,  //! Internal data bus output, 16 bits

    input  [15:0] CD_15_0_IN,    //! Cache data input, 16 bits
    output [15:0] CD_15_0_OUT,   //! Cache data output, 16 bits

    input  [15:0] PPN_25_10_IN,  //! Physical page number input, 16 bits
    output [15:0] PPN_25_10_OUT, //! Physical page number output, 16 bits

    output BEDO_n,               //! Buffered Enable IDB "data out" from CGA
    output BEMPID_n,             //! Buffered EMPID - Interrupt Disable (EPIC.LDMPIE->set mask reg:inh all ints)
    output BLCS_n,               //! Bus LCS (Load Control Store)
    output BSTP,                 //! Bus Stop
    output HIT,                  //! Cache hit signal, indicates a successful cache lookup
    output LAPA_n,               //! Latch Page Address, controls latching of the page address
    output [6:0] PT_15_9_OUT,    //! Page Table data output, top 7 bits
    output WCA_n,                //! Write Cache Address, controls writing to the cache address register
    //! DBG_CACHE - the six signals that gate a cache write, brought out so an
    //! ILA can say which one is actually blocking it. Added 28-AUG-2026.
    //!
    //! WHY. On the Nexys 4 DDR the machine's own diagnostic CACHE-1X0-A00,
    //! test 2, reports the cache totally inert: data and instructions are
    //! never COPIED INTO the cache when read, and never TAKEN FROM it when
    //! present, both with paging off and on. Nothing is ever written, so
    //! nothing can ever hit, so CUP never sets.
    //!
    //! A cache write happens when PAL_44402D asserts WCA, and its PALASM
    //! (DesignDocuments/PAL-Code/SRC/44402D.txt) says
    //!
    //!   WCA = /RT * DT * EWC * CYD * /FMISS * /LSHADOW
    //!       + RT * /IHIT * EWC * CYD * /FMISS * /LSHADOW
    //!
    //! Both terms need EWC and CYD high and FMISS and LSHADOW low. The PAL
    //! itself is transcribed correctly - checked against that listing on
    //! 28-AUG-2026, both product terms and the registered/combinational split
    //! (WCA is "=", combinational; only IHIT/NUBI/NUBD are ":=" - which is
    //! exactly the mistake that had been made in PAL_44511A). CON is tied
    //! high in ND120_CORE.v, so it is not the blocker either. That leaves one
    //! of WCINH_n, BRK_n, CYD, FMISS or LSHADOW, and reading the source
    //! cannot choose between them - it has to be measured while it runs.
    //!
    //! FMISS is the standing suspect: it comes off flip-flop A160 in
    //! DECODE_DGA_COMM.v, whose D input runs back through A177 =
    //! NAND(LCS_n, MREQ, FMISS) - a self-hold. Once FMISS sets it stays set
    //! while MREQ is asserted, and the PAL's own history note says "WCA
    //! SHOULD NOT APPEAR WHEN FMISS (TSET FAILS)". SUSPECT, NOT VERIFIED.
    //!
    //! Bit layout, low to high:
    //!   [0] LSHADOW  [1] FMISS  [2] CYD  [3] BRK_n  [4] WCINH_n  [5] WCA_n
    //!   [6] WCLIM_n - inhibit-RAM write strobe (added after the first capture)
    //!   [7] PPN25 - the DATA being written into the inhibit RAM (second capture)
    output [7:0] DBG_CACHE,
    output LED1,                 //! UNKNOWN: believed to indicate cache enabled, never traced. See Verilog/docs/SIGNALS.md
    //! DEBUG: page-table WRITE stream (23-AUG-2026, zero-read campaign).
    //! On every PT-chip write strobe (EPT_n low & WMAP_n low) two words are
    //! emitted on consecutive sysclks, 16'h0000 otherwise:
    //!   word A = {2'b10, addr[10:0], data[15:13]}   (addr = LA_20_10 index)
    //!   word B = {2'b11, data[12:0], 1'b0}
    //! Same pattern as MEM_43's DBG_MEMW; consumed by TANG_PTWR_CAPTURE.
    output [15:0] DBG_PTW,
    //! DEBUG level (27-AUG, wrong-PPN option-1 probe): PT-chip write strobe
    //! conjunction LIVE (~EPT_n & ~WMAP_n) - unlike DBG_PTW's edge-detected
    //! stream, this stays high for the WHOLE elongated strobe, so the top
    //! can AND it with MEM_HOLD to measure write-during-freeze overlap.
    output DBG_PTW_LVL
);


  /*******************************************************************************
   ** The wires are defined here                                                 **
   *******************************************************************************/
  wire [10:0] s_la_20_10;
  wire [15:0] s_idb_15_0_out;

  wire [13:0] s_hit_cpn_23_10_in;
  wire [10:0] s_ca_10_0;

  wire [ 1:0] s_hit_1_0_n;

  wire        s_ecd_n;
  wire        s_bstp;
  wire        s_bedo_n;
  wire        s_con;
  wire        s_ept_n;
  wire        s_empid_n;
  wire        s_lcs_n;
  wire        s_ecsr_n;
  wire        s_lapa_n;
  wire        s_wclim_n;
  wire        s_uclk;
  wire        s_cwr;
  wire        s_wchim_n;
  wire        s_hit;
  wire        s_eipur_n;
  wire        s_eipl_n;
  wire        s_con_n;
  wire        s_cyd;
  wire        s_pd2;
  wire        s_sw1_console;
  wire        s_wcinh_n;
  wire        s_eipu_n;
  wire        s_eorf_n;
  wire        s_epmap_n;
  wire        s_estof_n;
  wire        s_cup;
  wire        s_dvacc_n;
  wire        s_emcl_n;
  wire        s_epti_n;
  wire        s_bempid_n;
  wire        s_blcs_n;
  wire        s_stp;
  wire        s_edo_n;
  wire        s_dt_n;
  wire        s_brk_n;
  wire        s_wmap_n;
  wire        s_rt_n;
  wire        s_cc2_n;
  wire        s_cclr_n;
  wire        s_fmiss;
  wire        s_double;
  wire        s_write;
  wire        s_lshadow;
  wire        s_wca_n;
  wire        s_led1;

  // PPN
  wire [15:0] s_ppn_25_10_in;

  // PT
  // PT PPN
  wire [15:0] s_pt_ppn_25_10_out;
  wire [15:0] s_pt_ppn_25_10_in;

  // PT PT
  wire [15:0] s_pt_pt_15_0_out;
  wire [15:0] s_pt_pt_15_0_in;



  // CPN
  wire [13:0] s_cache_cpn_23_10_out;
  wire [13:0] s_cache_cpn_23_10_in;

  wire [15:0] s_cache_cd_15_0_in;
  wire [15:0] s_cache_cd_15_0_out;

  // PTIDB
  wire [15:0] s_ptidb_pt_15_0_in;
  wire [15:0] s_ptidb_pt_15_0_out;

  wire [15:0] s_ptidb_idb_15_0_in;
  wire [15:0] s_ptidb_idb_15_0_out;

  // PPNX
  wire [15:0] s_ppnx_idb_15_0_in;
  wire [15:0] s_ppnx_idb_15_0_out;

  wire [15:0] s_ppnx_ppn_25_10_in;
  wire [15:0] s_ppnx_ppn_25_10_out;

  // WCA
  wire [13:0] s_wca_ppn_23_10_in;
  wire [13:0] s_wca_cpn_23_10_out;

  // CSR
  wire [ 3:0] s_csr_idb_3_0_out;

  /*******************************************************************************
   ** Here all input connections are defined                                     **
   *******************************************************************************/


  assign s_la_20_10[10:0] = LA_20_10;
  assign s_ca_10_0[10:0] = CA_10_0;
  assign s_empid_n = EMPID_n;
  assign s_lcs_n = LCS_n;
  assign s_ecsr_n = ECSR_n;
  assign s_uclk = UCLK;
  assign s_cwr = CWR;
  assign s_wchim_n = WCHIM_n;
  assign s_cyd = CYD;
  //! See the DBG_CACHE port comment for what this bus is and why it exists.
  //! Bit 6 is WCLIM_n, added 29-AUG-2026 after the first capture. That one
  //! measured WCINH_n LOW - the page marked cache-inhibited - in 914 of 1024
  //! samples, while FMISS and LSHADOW were 0 throughout and WCA_n did fire
  //! whenever WCINH_n happened to be high. So the inhibit BIT is the question
  //! now, and the first half of it is whether the inhibit RAM is ever written
  //! at all: IMS1403_25 has no reset (the commented-out loop there says Vivado
  //! would not take one), so an untouched cell reads whatever the block RAM
  //! powers up as - which would produce "inhibited nearly everywhere" with the
  //! CPU never involved. WCLIM_n is that RAM's write strobe, so triggering on
  //! it going low says directly whether anything ever writes the bit.
  //! Bit 7 is the DATA the inhibit RAM is being written with - PPN bit 25 on
  //! the bus that addresses CHIP_20G. Added 29-AUG-2026 after the WCLIM_n
  //! capture, which proved the RAM IS written (69 write strobes in one
  //! 1024-sample window), killing the "it is just uninitialised block RAM"
  //! theory. So the bit is written and it still reads INHIBITED almost
  //! everywhere, and the question becomes what value is going in.
  //!
  //! It cannot be read off WCINH_n. IMS1403_25.v:34 is
  //!     assign Q = (!CE_n && W_n) ? data_out : 1'b0;
  //! so the RAM's output is FORCED LOW for the whole of a write. Every one of
  //! those 69 samples showed WCINH_n = 0 for that reason alone, and reading
  //! "inhibited" from them would be reading the model's own artefact.
  //! Sampling the data input is the only way to see what is stored.
  assign DBG_CACHE = {s_pt_ppn_25_10_in[15], s_wclim_n, s_wca_n, s_wcinh_n,
                      s_brk_n, s_cyd, s_fmiss, s_lshadow};
  assign s_pd2 = PD2;
  assign s_sw1_console = SW1_CONSOLE;
  assign s_eorf_n = EORF_n;
  assign s_estof_n = ESTOF_n;
  assign s_cup = CUP;
  assign s_dvacc_n = DVACC_n;
  assign s_emcl_n = EMCL_n;
  assign s_stp = STP;
  assign s_edo_n = EDO_n;
  assign s_dt_n = DT_n;
  assign s_brk_n = BRK_n;
  assign s_rt_n = RT_n;
  assign s_cc2_n = CC2_n;
  assign s_cclr_n = CCLR_n;
  assign s_fmiss = FMISS;
  assign s_double = DOUBLE;
  assign s_write = WRITE;
  assign s_lshadow = LSHADOW;
  assign s_ppn_25_10_in = PPN_25_10_IN;
  assign s_cache_cd_15_0_in = CD_15_0_IN;

  /*******************************************************************************
   ** Here all output connections are defined                                    **
   *******************************************************************************/
  assign BEDO_n = s_bedo_n;
  assign BEMPID_n = s_bempid_n;
  assign BLCS_n = s_blcs_n;
  assign BSTP = s_bstp;
  assign CD_15_0_OUT = s_cache_cd_15_0_out[15:0];
  assign HIT = s_hit;
  assign IDB_15_0_OUT = s_idb_15_0_out[15:0];
  assign LAPA_n = s_lapa_n;
  assign PPN_25_10_OUT = s_pt_ppn_25_10_out | s_ppnx_ppn_25_10_out;
  assign PT_15_9_OUT = s_pt_pt_15_0_out[15:9] | s_ptidb_pt_15_0_out[15:9];
  assign WCA_n = s_wca_n;
  assign LED1 = s_led1;

  // Connect PT[15:0] between PT and PDIDB components
  assign s_pt_pt_15_0_in = s_ptidb_pt_15_0_out | s_ptidb_idb_15_0_in;
  assign s_ptidb_pt_15_0_in = s_pt_pt_15_0_out;

  // Connect PPN INPUT signals from PPN IN or with (PT or PPNX out)
  assign s_pt_ppn_25_10_in = s_ppn_25_10_in | s_ppnx_ppn_25_10_out;
  assign s_ppnx_ppn_25_10_in = s_ppn_25_10_in | s_pt_ppn_25_10_out;

  // Assign input and output signals for IDB
  assign s_ptidb_idb_15_0_in = IDB_15_0_IN;
  assign s_ppnx_idb_15_0_in = IDB_15_0_IN;

  assign s_idb_15_0_out[15:0] =
        s_ppnx_idb_15_0_out  |
        s_ptidb_idb_15_0_out |
        {12'b0, s_csr_idb_3_0_out[3:0]};


  // BUS SIGNALS
  assign s_wca_ppn_23_10_in =
         s_ppn_25_10_in[13:0]       | // Input to module
         s_ppnx_ppn_25_10_out[13:0] |  // output from PPNX module
         s_pt_ppn_25_10_out[13:0];     // output from PPN module

  /*******************************************************************************
   ** Here all normal components are defined                                     **
   *******************************************************************************/
  assign s_wclim_n = s_wchim_n | s_eorf_n;
  assign s_wmap_n = ~(s_lshadow & s_write & s_cyd);

  /*******************************************************************************
   ** Here all sub-circuits are defined                                          **
   *******************************************************************************/

  // HIT DETECTOR MODULE: This module, CPU_MMU_HIT_27, is responsible for determining cache hit status.
  // It compares the provided physical page number (PPN) and cache page number (CPN) inputs to detect
  // if there is a match, indicating a cache hit. The module takes in 14-bit inputs for both PPN and CPN,
  // along with control signals LSHADOW, FMISS, and CON_n. It outputs two signals, HIT0_n and HIT1_n,
  // which represent the negated hit status for different conditions. A low output on these signals
  // indicates a cache hit, while a high output indicates a miss.
  CPU_MMU_HIT_27 MMU_HIT
  (
    // Input signals
    .CPN_23_10_IN(s_hit_cpn_23_10_in[13:0]),
    // The PPN(25:10) bus on sheet 24 is ONE node: the PT map RAM output, the
    // PPNX output and the external PPN input all sit on it, and the vertical
    // into the HIT block drops off that shared bus. This passed only
    // s_ppn_25_10_in, which per CPU_15.v:391-393 is the LAPA latch alone and
    // is ZERO whenever LAPA~ is high - i.e. on every MAPPED access. So the
    // cache hit decision for paged memory was made by comparing the stored
    // tag against a constant 0. s_wca_ppn_23_10_in (built at :234-237) is the
    // same merge the WCA feed already uses, and is the bus the drawing shows.
    .PPN_23_10_IN(s_wca_ppn_23_10_in[13:0]),

    .LSHADOW(s_lshadow),
    .FMISS  (s_fmiss),
    .CON_n  (s_con_n),

    // Output signals
    .HIT0_n(s_hit_1_0_n[0]),
    .HIT1_n(s_hit_1_0_n[1])
  );

  // The CPU_MMU_PPNX_28 module is responsible for handling the translation and manipulation
  // of the Physical Page Number (PPN) and the Internal Data Bus (IDB) signals. It takes in
  // control signals such as EIPL_n, EIPUR_n, EIPU_n, and ESTOF_n to determine the direction
  // and conditions under which data is transferred between the PPN and IDB. The module
  // outputs the modified PPN and IDB values, facilitating the interaction between the
  // memory management unit and other components of the CPU.
  CPU_MMU_PPNX_28 PPNX
  (
    // Input signals
    .EIPL_n(s_eipl_n),
    .EIPUR_n(s_eipur_n),
    .EIPU_n(s_eipu_n),
    .ESTOF_n(s_estof_n),

     // Bus signals (in and out)
    .IDB_15_0_IN(s_ppnx_idb_15_0_in[15:0]),
    .IDB_15_0_OUT(s_ppnx_idb_15_0_out[15:0]),

    .PPN_25_10_IN(s_ppnx_ppn_25_10_in[15:0]),
    .PPN_25_10_OUT(s_ppnx_ppn_25_10_out[15:0])
  );

  // The CPU_MMU_PTIDB_30 module is responsible for interfacing between the Page Table (PT) and the Internal Data Bus (IDB).
  // It manages the data flow between these components, allowing for the reading and writing of page table entries.
  // The module takes in control signals such as EPTI_n and WRITE to determine the operation mode, and it handles
  // 16-bit data inputs and outputs for both the IDB and PT. This module is crucial for maintaining the integrity
  // and efficiency of memory management operations within the CPU.
  CPU_MMU_PTIDB_30 PTIDB
  (
    .WRITE(s_write), // Direction
    .EPTI_n(s_epti_n), // Output enable

    // Bus signals (in and out)
    .IDB_15_0_IN(s_ptidb_idb_15_0_in[15:0]),
    .IDB_15_0_OUT(s_ptidb_idb_15_0_out[15:0]),

    .PT_15_0_IN(s_ptidb_pt_15_0_in),
    .PT_15_0_OUT(s_ptidb_pt_15_0_out)
  );



  // CPU_MMU_WCA_31.v is replaced by this line
  // if s_lapa_n is high, output is high-impedance
  assign s_wca_cpn_23_10_out[13:0] = s_wca_n ? 14'b0 : s_wca_ppn_23_10_in[13:0];

  // Combine the CPN output from WCA and CACHE
  assign s_hit_cpn_23_10_in = s_wca_cpn_23_10_out | s_cache_cpn_23_10_out;

  // Assign the correct bits to the CACHE cpn in bits
  assign s_cache_cpn_23_10_in = s_wca_cpn_23_10_out;

  // Cache Status Register (CSR)
  //
  // The CPU_MMU_CSR_26 module serves as the Cache Status Register (CSR) within the Memory Management Unit (MMU).
  // It is responsible for managing and outputting various status signals related to cache operations.
  // The module takes several input signals, including STP, EMPID_n, EDO_n, LCS_n, PD2, CUP, CON, and ECSR_n,
  // which represent different control and status conditions of the CPU and cache system.
  // Based on these inputs, the CSR module generates output signals such as BSTP, BEMPID_n, BEDO_n, BLCS_n,
  // and a 4-bit IDB output (IDB_3_0), which are used to control and monitor the cache's behavior and status.
  CPU_MMU_CSR_26 CSR
  (
    // Input signals
    .STP(s_stp),
    .EMPID_n(s_empid_n),
    .EDO_n(s_edo_n),
    .LCS_n(s_lcs_n),
    .PD2(s_pd2),

    .CUP(s_cup),
    .CON(s_con),
    .ECSR_n(s_ecsr_n),

    // Output signals
    .BSTP(s_bstp),
    .BEMPID_n(s_bempid_n),
    .BEDO_n(s_bedo_n),
    .BLCS_n(s_blcs_n),
    .IDB_3_0(s_csr_idb_3_0_out[3:0])
  );

  // Cache
  //
  // The CPU_MMU_CACHE_25 module is responsible for managing the cache operations within the Memory Management Unit (MMU).
  // It interfaces with various input signals such as system clock, reset, and control signals like BRK_n, CWR, and FMISS,
  // which are crucial for cache control and data flow. The module handles both input and output bus signals, including
  // CD_15_0 and CPN_23_10, to facilitate data transfer between the cache and other components. Additionally, it generates
  // output signals like CON, HIT, and LED1, which indicate the cache's operational status and hit/miss results. This module
  // plays a vital role in optimizing memory access times by storing frequently accessed data, thereby improving overall
  // system performance.
  CPU_MMU_CACHE_25 CACHE
  (
    .sysclk   (sysclk),    // System clock in FPGA
    .sys_rst_n(sys_rst_n), // System reset in FPGA

    // Input signals
    .BRK_n(s_brk_n),
    .CA_10_0(s_ca_10_0[10:0]),
    .CCLR_n(s_cclr_n),
    .CWR(s_cwr),
    .CYD(s_cyd),
    .DT_n(s_dt_n),
    .ECD_n(s_ecd_n),
    .FMISS(s_fmiss),
    .HIT_1_0_n(s_hit_1_0_n[1:0]),
    .LSHADOW(s_lshadow),
    .PD2(s_pd2),
    .RT_n(s_rt_n),
    .SW1_CONSOLE(s_sw1_console),
    .UCLK(s_uclk),
    .UCLK_EN(UCLK_EN),
    .WCINH_n(s_wcinh_n),

    // Bus signals (in and out)
    .CD_15_0_IN(s_cache_cd_15_0_in),
    .CD_15_0_OUT(s_cache_cd_15_0_out),

    .CPN_23_10_IN(s_cache_cpn_23_10_in[13:0]),
    .CPN_23_10_OUT(s_cache_cpn_23_10_out[13:0]),

    // Output signals
    .CON(s_con),
    .CON_n(s_con_n),
    .HIT(s_hit),
    .WCA_n(s_wca_n),
    .LED1(s_led1)
  );

  // The PAL_44306A module, labeled as PAL_44306_UNOCTL, is a programmable array logic component
  // that manages various control signals within the Memory Management Unit (MMU). It takes multiple
  // input signals, such as cache address, write enable, and data valid acknowledge, to generate
  // specific output control signals. These outputs, like ECD_n and LAPA_n, are crucial for coordinating
  // the operations of the MMU, including enabling or disabling certain functions and managing data flow.
  // The module plays a key role in ensuring the correct sequencing and control of memory operations.
  PAL_44306A PAL_44306_UNOCTL (
      .EIPUR_n(s_eipur_n),     //B0
      .EIPU_n (s_eipu_n),      //B1
      .EIPL_n (s_eipl_n),      //B2
      .EPTI_n (s_epti_n),      //B3
      .EPMAP_n(s_epmap_n),     //B4
      .EPT_n  (s_ept_n),       //B5
      .CA0    (s_ca_10_0[0]),  //I0
      .WRITE  (s_write),       //I1
      .DVACC_n(s_dvacc_n),     //I2
      .RT_n   (s_rt_n),        //I3
      .WCHIM_n(s_wchim_n),     //I4
      .DOUBLE (s_double),      //I5
      .EMCL_n (s_emcl_n),      //I6
      .CC2_n  (s_cc2_n),       //I7
      .WCA_n  (s_wca_n),       //I8
      .LSHADOW(s_lshadow),     //I9
      .ECD_n  (s_ecd_n),       //Y0
      .LAPA_n (s_lapa_n)       //Y1
  );

  // Page Table (PT)
  //
  // This module, CPU_MMU_PT_29, is responsible for managing the Page Table (PT) operations within the Memory Management Unit (MMU).
  // It interfaces with the system clock and reset signals to ensure synchronized operations.
  // The module handles 11-bit addressing for PT chips, enabling or disabling the EPMAP and PT chips based on control signals.
  // It manages write operations to RAM chips, specifically targeting the high bit of the Page Physical Number (PPN).
  // The module also processes bidirectional signals for both PPN and PT data buses, facilitating data flow in and out.
  // Additionally, it outputs a write control inhibit signal, which is active low, to regulate write operations.
  CPU_MMU_PT_29 PT
  (
    // Inputs
    .sysclk(sysclk),             // System clock in FPGA
    .sys_rst_n(sys_rst_n),       // System reset in FPGA
    .LA_20_10(s_la_20_10[10:0]), // 11 bit addressing into PT chips
    .EPMAP_n(s_epmap_n),         // Enable EPMAP chips (Extended map?)
    .EPT_n(s_ept_n),             // Enable PT chips (Chip select for PT chips)
    .WCLIM_n(s_wclim_n),         // Write to RAM chip with 1 bit Data being PPN hi bit (bit ppn 25)
    .WMAP_n(s_wmap_n),           // Write MAPPING signal

    // Bus signals (in and out)
    .PPN_25_10_IN(s_pt_ppn_25_10_in[15:0]), // Bidirectional PPN (in)
    .PPN_25_10_OUT(s_pt_ppn_25_10_out[15:0]), // Bidirectional PPN (out)

    .PT_15_0_IN(s_pt_pt_15_0_in), // Bidirectional PT (in)
    .PT_15_0_OUT(s_pt_pt_15_0_out), // Bidirectional PT (out)

    // Outputs
    .WCINH_n(s_wcinh_n)         // Write control inhibit (active low)
  );

  // ---- DBG_PTW: the page-table write stream (see the port comment) --------
  // Edge-detected on the strobe conjunction so one write emits exactly one
  // A/B word pair however long the strobe lasts.
  //
  // 23-AUG second revision: ALSO emit an ATTEMPT word (tag 01) on the IDB->PT
  // transfer (EPTI_n low & WRITE) - the step a shadow-area page-table write
  // reaches even when the RAM strobe never fires. First silicon run recorded
  // ZERO write strobes across a whole boot while the same-RTL FF-mode
  // sim boot recorded 3053 (Verilator); attempts-present-with-strobes-absent
  // separates a broken EPT/WMAP conjunction from a dead probe, and
  // attempts-absent moves the question up to the shadow-address decode.
  wire       s_ptw_wr  = ~s_ept_n & ~s_wmap_n;
  assign DBG_PTW_LVL = s_ptw_wr;
  wire       s_ptw_att = ~s_epti_n & s_write;
  reg        r_ptw_wr_d = 1'b0;
  reg        r_ptw_att_d = 1'b0;
  reg [1:0]  r_ptw_phase = 2'd0;
  reg [10:0] r_ptw_addr = 11'd0;
  reg [15:0] r_ptw_data = 16'd0;
  always @(posedge sysclk) begin
    r_ptw_wr_d  <= s_ptw_wr;
    r_ptw_att_d <= s_ptw_att;
    if (s_ptw_wr && !r_ptw_wr_d) begin
      r_ptw_addr  <= s_la_20_10;
      r_ptw_data  <= s_pt_pt_15_0_in;
      r_ptw_phase <= 2'd1;
    end else if (r_ptw_phase == 2'd1) begin
      r_ptw_phase <= 2'd2;
    end else if (s_ptw_att && !r_ptw_att_d) begin
      // one attempt word; an in-progress write pair wins the bus
      r_ptw_addr  <= s_la_20_10;
      r_ptw_data  <= s_ptidb_idb_15_0_in;
      r_ptw_phase <= 2'd3;
    end else begin
      r_ptw_phase <= 2'd0;
    end
  end
  assign DBG_PTW = (r_ptw_phase == 2'd1) ? {2'b10, r_ptw_addr, r_ptw_data[15:13]}
                 : (r_ptw_phase == 2'd2) ? {2'b11, r_ptw_data[12:0], 1'b0}
                 : (r_ptw_phase == 2'd3) ? {2'b01, r_ptw_addr, r_ptw_data[15:13]}
                 : 16'h0000;

`ifdef PTDBG
  // Issue-D probe (inert unless -DPTDBG): log all program-visible page-table
  // traffic so the PAGING test-3 PGU/WIP failure can be pinned - does the
  // trap handler WRITE the PT entry with the PGU/WIP status bits set, and
  // does the test's read-back RETURN them?
  //   [pt] WR  = a PT-chip write strobe (EPT_n low + WMAP_n low): addr + data
  //   [pt] RDI = PT entry driven onto the IDB (EPTI_n low, read direction)
  //   [pt] WRI = IDB driven onto the PT bus (EPTI_n low, write direction)
  // Each is edge/change-detected so one strobe logs once.
  reg        r_ptdbg_wr_d, r_ptdbg_rdi_d, r_ptdbg_wri_d;
  reg [10:0] r_ptdbg_addr_d;
  reg [15:0] r_ptdbg_data_d;
  always @(posedge sysclk) begin
    r_ptdbg_wr_d   <= (!s_ept_n && !s_wmap_n);
    r_ptdbg_rdi_d  <= (!s_epti_n && !s_write);
    r_ptdbg_wri_d  <= (!s_epti_n &&  s_write);
    r_ptdbg_addr_d <= s_la_20_10;
    r_ptdbg_data_d <= s_pt_pt_15_0_in;
    if ((!s_ept_n && !s_wmap_n) &&
        (!r_ptdbg_wr_d || r_ptdbg_addr_d != s_la_20_10 || r_ptdbg_data_d != s_pt_pt_15_0_in))
      $display("[pt] t=%0t WR  addr=%04o data=%06o (pt15_9=%03o)",
               $time, s_la_20_10, s_pt_pt_15_0_in, s_pt_pt_15_0_in[15:9]);
    if ((!s_epti_n && !s_write) && !r_ptdbg_rdi_d)
      $display("[pt] RDI addr=%04o data=%06o (pt15_9=%03o)",
               s_la_20_10, s_ptidb_pt_15_0_in, s_ptidb_pt_15_0_in[15:9]);
    if ((!s_epti_n && s_write) && !r_ptdbg_wri_d)
      // v24 (23-AUG-2026): the strobe legs, printed AT the write attempt.
      // WMAP_n = ~(LSHADOW & WRITE & CYD) (this file, the s_wmap_n assign);
      // the status-bank RAM write needs EPT_n low AND WMAP_n low
      // (CPU_MMU_PT_29.v CHIP_24G/25G: CS_n=EPT_n, W_n=WMAP_n). Printing all
      // legs at the attempt separates: (a) LSHADOW never asserted,
      // (b) LSHADOW&WRITE without CYD, (c) all three but EPT_n high.
      $display("[pt] WRI addr=%04o idb=%06o (pt15_9=%03o) EPTn=%b WMAPn=%b LSH=%b WR=%b CYD=%b DBL=%b CA0=%b",
               s_la_20_10, s_ptidb_idb_15_0_in, s_ptidb_idb_15_0_in[15:9],
               s_ept_n, s_wmap_n, s_lshadow, s_write, s_cyd, s_double,
               s_ca_10_0[0]);
  end

  // v24 (23-AUG-2026): WMAP-leg coincidence census. Counts cycles where
  // LSHADOW & WRITE is true, split by whether CYD coincided. Printed at every
  // 2^20th event and at each WRI attempt, so the failing leg is named by
  // NUMBERS: lshwr_cyd==0 forever = CYD never coincides (leg b);
  // both counters 0 while WRI attempts exist = LSHADOW never asserted (leg a);
  // healthy counts with [pt] WR still absent = EPT_n high at the instant (leg c).
  reg [31:0] r_lshwr_nocyd = 0;
  reg [31:0] r_lshwr_cyd   = 0;
  always @(posedge sysclk) begin
    if (s_lshadow & s_write) begin
      if (s_cyd) r_lshwr_cyd   <= r_lshwr_cyd + 1;
      else       r_lshwr_nocyd <= r_lshwr_nocyd + 1;
      if (((r_lshwr_cyd + r_lshwr_nocyd) & 32'hFFFFF) == 32'd0)
        $display("[ptleg] lshwr_cyd=%0d lshwr_nocyd=%0d", r_lshwr_cyd, r_lshwr_nocyd);
    end
    if ((!s_epti_n && s_write) && !r_ptdbg_wri_d)
      $display("[ptleg] at-WRI lshwr_cyd=%0d lshwr_nocyd=%0d",
               r_lshwr_cyd, r_lshwr_nocyd);
  end

  // Zero-entry translation probe: log every PT translation read (EPT active,
  // not a write) that returns an entry with ALL-ZERO status bits - the
  // signature of the spurious PV trap (access through an unmapped entry).
  // Logs the PT INDEX (= PIT + virtual page), which the trap probes cannot see.
  reg        r_ptdbg_z_d;
  reg [10:0] r_ptdbg_zaddr_d;
  always @(posedge sysclk) begin
    r_ptdbg_z_d     <= (!s_ept_n && s_wmap_n && s_pt_pt_15_0_out[15:9] == 7'd0);
    r_ptdbg_zaddr_d <= s_la_20_10;
    if ((!s_ept_n && s_wmap_n && s_pt_pt_15_0_out[15:9] == 7'd0) &&
        (!r_ptdbg_z_d || r_ptdbg_zaddr_d != s_la_20_10))
      // $time added 17-AUG-2026 so this log can be JOINED with the [acc] probe
      // in CGA.v, which carries the access class (FETCH/READ/WRITE/IND) and the
      // addressing mode. Those signals never leave the CGA, so the correlation
      // is done on the timestamp rather than by plumbing ports down here.
      // pit/vpn are split out because the whole question is which page TABLE
      // was used - `addr` alone hides it.
      $display("[pt] Z t=%0t addr=%04o pit=%02o vpn=%02o data=%06o DBL=%b WR=%b LSH=%b CYD=%b",
               $time, s_la_20_10, s_la_20_10[10:6], s_la_20_10[5:0],
               s_pt_pt_15_0_out, s_double, s_write, s_lshadow, s_cyd);
  end
`endif

endmodule