Skip to content

CPU_MMU_PPNX_28

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

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

Used in: CPU_MMU_24 (all tops)

Contains: no other modules.

Module hierarchy - All modules

CPU_MMU_PPNX_28 symbol

Schematic

Drawn from the Verilog: the yosys netlist of the Simulation (Verilator) build, instance CORE.CPU_BOARD.CPU.MMU.PPNX. 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_PPNX_28 schematic

Description

ND120 CPU, MM&M CPU/MMU/PPNX PPN TO IDB SHEET 28 of 50 Last reviewed: 2-FEB-2025 Ronny Hansen

Ports

Direction Width Name Description
input 1 EIPU_n (active low) Enable IDB upper bits
input 1 EIPL_n (active low) Enable IDB lower bits
input 1 ESTOF_n (active low) Input signal for direction control (PPN<->IDB)
input 1 EIPUR_n (active low) Mask away the PROTECT BITS in PPN (PPN 25:19 == 000000)
input [15:0] IDB_15_0_IN Internal data bus input, 16 bits (from CPU_MMU_24.IDB_15_0_IN)
output [15:0] IDB_15_0_OUT
input [15:0] PPN_25_10_IN
output [15:0] PPN_25_10_OUT

Verilog source

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

Show the Verilog of CPU_MMU_PPNX_28 (139 lines)
/**************************************************************************
** ND120 CPU, MM&M                                                       **
** CPU/MMU/PPNX                                                          **
** PPN TO IDB                                                            **
** SHEET 28 of 50                                                        **
**                                                                       **
** Last reviewed: 2-FEB-2025                                            **
** Ronny Hansen                                                          **
***************************************************************************/

module CPU_MMU_PPNX_28 (  
  input EIPU_n,   //! Enable IDB upper bits
  input EIPL_n,   //! Enable IDB lower bits
  input ESTOF_n,  //! Input signal for direction control (PPN<->IDB)

  input EIPUR_n,  //! Mask away the PROTECT BITS in PPN (PPN 25:19 == 000000)

  input  [15:0] IDB_15_0_IN,  //! Internal data bus input, 16 bits (from CPU_MMU_24.IDB_15_0_IN)
  output [15:0] IDB_15_0_OUT,

  input  [15:0] PPN_25_10_IN,
  output [15:0] PPN_25_10_OUT
);


  wire DIR = ESTOF_n;

  reg [15:0] PPN_reg;
  reg [15:0] IDB_reg;

  always @(*)
  begin
    IDB_reg = IDB_15_0_IN;
    PPN_reg = PPN_25_10_IN;

    if (EIPUR_n == 0) begin
      PPN_reg[15:8] = {7'b0, IDB_15_0_IN[8]};
      PPN_reg[7:0]  = PPN_25_10_IN[7:0];
    end


    // 2x 74245 (CHIP 10B (UPPER) and 9B (LOWER))
    //always @(*) begin


    // Upper 8 bits - CHIP 10B
    if (EIPU_n == 0) begin
      if (DIR) begin
        // Data flows from A to B
        IDB_reg[15:8] = PPN_reg[15:8];
      end else begin
        // Data flows from B to A

        if (EIPUR_n == 0) begin
          PPN_reg[15:8] = {7'b0, IDB_15_0_IN[8]};
        end else begin
          PPN_reg[15:8] = IDB_15_0_IN[15:8];
        end
      end
      /*end else begin
       PPN_reg[15:8] = PPN_25_10[15:8];
       IDB_reg[15:8] = IDB_15_0[15:8];
   */
    end

    // Lower 8 bits - CHIP 9B
    if (EIPL_n == 0) begin
      if (DIR) begin
        IDB_reg[7:0] = PPN_reg[7:0];  // Data flows from A to B
      end else begin
        PPN_reg[7:0] = IDB_15_0_IN[7:0];  // Data flows from B to A
      end
      /*
   end else begin
       PPN_reg[7:0] = PPN_25_10[7:0];
       IDB_reg[7:0] = IDB_15_0[7:0];
   */
    end
  end

  // Assign the bidirectional bus with respect to OE.
  //
  // Sheet 28 of the 3202D drawing has three chips: 10B (74PCT245, IDB15-8 <->
  // PPN25-18, /G = EIPU_n), 9B (74PCT245, IDB7-0 <-> PPN17-10, /G = EIPL_n)
  // and 8B (74LS244, /G = EIPUR_n, PPN25-19 grounded and PPN18 = IDB8). DIR
  // is shared and comes from ESTOF_n: 1 = A to B = PPN to IDB.
  //
  // Both output assignments used to publish the working registers with NO
  // enable term, and those registers are seeded from this module's own
  // inputs - so a disabled byte, or the opposite direction, echoed the input
  // straight back out. That put PPN_25_10_IN permanently on PPN_25_10_OUT and
  // IDB_15_0_IN permanently on IDB_15_0_OUT, which the parents merge with a
  // wired-OR, feeding the CGA its own IDB output back into its IDB input
  // (CPU_15.v:331 -> here -> CPU_15.v:336) and the shadow RAM its own read
  // data back into its write data (CPU_MMU_24.v:221 -> here -> :220).
  // The sibling sheet 30 (CPU_MMU_PTIDB_30.v:46-49) always gated its pair
  // correctly, as does the reference model Shared/support/TTL_74245.v:25-28.
  // A disabled driver puts 0 on the bus - inside the FPGA there is no z.
  //
  // PPN_reg stays as the internal A-side node: the DIR=1 branches above read
  // it to forward the masked or raw PPN byte onto IDB.
  //
  // 20-AUG-2026: publish the SELECTED SOURCE, not the working register.
  // IDB_reg is seeded with IDB_15_0_IN and only overwritten from PPN_reg in
  // the (!EIPU_n && DIR) branch - so functionally the seed was never
  // published. Structurally, though, synthesis cannot prove that, and saw an
  // unconditional edge IDB_15_0_IN -> IDB_reg -> IDB_15_0_OUT. Through the
  // parents' wired-OR (CPU_15.v:331 -> here -> CPU_15.v:336) that edge closed
  // a combinational loop, and Vivado's DRC (LUTLP-1) blocks bitstream
  // generation on it. Same shape as the shared 'internalBus' node removed
  // from Shared/support/TTL_74245.v on the same day.
  //
  // PPN_reg[15:8] is exactly what IDB_reg[15:8] held whenever this output is
  // enabled, so this is the same value with no shared node.
  assign IDB_15_0_OUT[15:8]  = (!EIPU_n && DIR) ? PPN_reg[15:8] : 8'b0;
  assign IDB_15_0_OUT[7:0]   = (!EIPL_n && DIR) ? PPN_reg[7:0]  : 8'b0;

  // PPN is driven by 10B/9B in the B-to-A direction, and by 8B whenever
  // EIPUR_n is low, independently of the two transceivers. Where 8B and 10B
  // both drive the high byte the mask wins, which is what the code above
  // already resolves.
  // Same treatment on the PPN side: PPN_reg is seeded with PPN_25_10_IN, and
  // in every case where these outputs are enabled it has been overwritten
  // from the IDB side (or from the 8B mask). Naming the source directly
  // removes the PPN_25_10_IN -> PPN_reg -> PPN_25_10_OUT edge.
  //   8B (EIPUR_n) forces PPN18 = IDB8 with PPN25-19 grounded, and wins over
  //   10B where both drive - which is the priority the always block above
  //   already resolves.
  assign PPN_25_10_OUT[15:8] = !EIPUR_n            ? {7'b0, IDB_15_0_IN[8]}
                             : (!EIPU_n && !DIR)   ? IDB_15_0_IN[15:8]
                                                   : 8'b0;
  assign PPN_25_10_OUT[7:0]  = (!EIPL_n && !DIR)   ? IDB_15_0_IN[7:0] : 8'b0;


  // Output to A when receiving from B with respect to OE (OE_n==1 means "isolated". Don't write to A or B)
  //assign A = (OE_n == 0 && DIR == 0) ? internalBus : 8'b0;


endmodule