Skip to content

IDT6168A_20

Source: Verilog/Shared/support/IDT6168A_20.v

Where it sits (Simulation): ND120_TOP > ND120_CORE > ND3202D > CPU_15 > CPU_CS_16 > CPU_CS_WCS_21_22 > IDT6168A_20 - instance path: CORE.CPU_BOARD.CPU.CS.WCS.CHIP_25C

Used in: CPU_CS_WCS_21_22 (all tops)

Contains: no other modules.

Module hierarchy - All modules

IDT6168A_20 symbol

Schematic

Drawn from the Verilog: the yosys netlist of the Simulation (Verilator) build, instance CORE.CPU_BOARD.CPU.CS.WCS.CHIP_16C. 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).

IDT6168A_20 schematic

Description

ND120 Shared IDT6168A 16K (4Kx4) Static RAM (using BLOCK RAM) Last reviewed: 29-JAN-2025 Ronny Hansen

Parameters

Parameter Default
INIT_FILE ""

Ports

Direction Width Name Description
input 1 clk Clock input (BLOCK RAM MUST HAVE CLOCK)
input 1 reset_n (active low) Active-low reset
input [11:0] A_11_0 Address input
input 1 CE_n (active low) Chip enable (active low)
input 1 WE_n (active low) Write enable (active low)
input [3:0] D_3_0_IN Data input for write
output [3:0] D_3_0_OUT Data output for read

Verilog source

Verilog/Shared/support/IDT6168A_20.v on GitHub.

Show the Verilog of IDT6168A_20 (245 lines)
/**************************************************************************
** ND120 Shared                                                          **
**                                                                       **
**  IDT6168A                                                             **
**  16K (4Kx4) Static RAM  (using BLOCK RAM)                             **
**                                                                       **
** Last reviewed: 29-JAN-2025                                             **
** Ronny Hansen                                                          **
***************************************************************************/


/*

The IDT6168 is a 16,384-bit high-speed static RAM organized as 4K x 4.
It is fabricated using IDT’s high-performance, high-reliability CMOS technology.
This state-of-the-art technology, combined with innovative circuit design techniques,
provides a cost-effective approach forhigh-speed memory applications.Access times as fast 15ns are available.

The circuit also offers areduced power standby mode.
When CS goes HIGH, the circuit willautomatically go to, and remain in, a standby mode as long as CS remainsHIGH.

This capability provides significant system-level power and coolingsavings.
The low-power (LA) version also offers a battery backup dataretention  capability  where  the  circuit  typically
consumes only 1μW operating off a 2V battery. 

All inputs and outputs of the IDT6168 areTTL-compatible and operate from a single 5V supply.
The IDT6168 is packaged in either a space saving 20-pin, 300-milceramic or plastic DIP or a 20-pin LCC providing 
high board-level packing densities.

Military grade product is manufactured in compliance with the latest revision of MIL-STD-883, Class B,
making it ideally suited tomilitary temperature applications demanding the highest level of performance and reliability.


High-speed (equal access and cycle time)
  –   Military: 25/45ns (max.)
  –   Industrial: 25ns (max.)
  –   Commercial: 15/20/25ns (max.)

Documentation: https://www.alldatasheet.com/datasheet-pdf/view/65830/IDT/IDT6168.html


NOTE!! The access time is not immediate, meaning it takes 15-20 nanoseconds after the address has changed for the data to become valid on the output.

*/
module IDT6168A_20 #(
    // Optional preload image (one 4-bit hex nibble per line, 4096 lines).
    // Empty string = no preload (default). Used to pre-fill the WCS so the
    // runtime microcode load can be skipped - see docs/skip-wcs-load.md.
    parameter INIT_FILE = ""
) (
    input wire clk,     // Clock input (BLOCK RAM MUST HAVE CLOCK)
    input wire reset_n, // Active-low reset

    input  wire [11:0] A_11_0,    // Address input
    input  wire        CE_n,      // Chip enable (active low)
    input  wire        WE_n,      // Write enable (active low)
    input  wire [ 3:0] D_3_0_IN,  // Data input for write
    output wire [ 3:0] D_3_0_OUT  // Data output for read
);

`ifdef QUARTUS_RAM_INFER

  // ---------------------------------------------------------------------
  // Quartus arm (01-SEP-2026): the SAME array and the SAME 1-clock
  // write-first behaviour as the reference below, only restructured into a
  // shape Quartus 17.0 will map onto M10K. PLAIN VERILOG on purpose.
  //
  // HISTORY - the arm this replaced. The first Quartus arm (31-AUG-2026,
  // deleted 01-SEP-2026 once this one had booted the board) was an explicit
  // altsyncram megafunction. It shipped with outdata_reg_a="CLOCK0", which
  // put a SECOND register on the output: altsyncram ALWAYS registers the
  // address in synchronous mode, so the WCS read took TWO clocks against
  // this model's ONE. Every microinstruction arrived a clock late on that
  // board alone, the microsequencer ran one step out of alignment with the
  // cycle controller, and a nested microsubroutine return popped the wrong
  // address (001015 instead of the 002027 MACL pushed) - the CPU looped
  // forever in the interrupt-register microcode. See
  // docs/mister-microcode-loop.md. Nothing simulates a megafunction, so no
  // simulation could ever have caught it; the equivalence check of the day
  // compared it against a hand-written stub that was wrong by the same
  // clock. That is why the arm below is plain Verilog and the megafunction
  // is gone: Shared/support/sim/run_quartus_ram_equiv.sh compiles and runs
  // BOTH this arm and the reference and proves them cycle-identical. An arm
  // that can be tested is worth more than an arm that is "obviously" right.
  //
  // What Quartus objects to in the reference arm (measured, build v46):
  //     Info (276007): ... uninferred due to asynchronous read logic
  // for all 32 WCS chips, then
  //     Error (276003): Cannot convert all sets of registers into RAM ...
  // because 32 x 4K x 4 bits of flip-flops does not fit any device.
  // Nothing reads the array outside the clocked block, so the read is not
  // actually asynchronous. What it cannot map is the OUTPUT REGISTER: in
  // the reference, data_out loads from the ARRAY on a read but from
  // D_3_0_IN on a write. A register fed from a non-memory source cannot be
  // the M10K's own output register, so the array read is left with nowhere
  // synchronous to land - and M10K has no asynchronous read port (the same
  // limit that forces the async cache RAMs onto MLAB).
  //
  // NOTE this is a reading of the evidence, not a rule quoted from Intel's
  // documentation. The build is the proof, and build v47 (01-SEP-2026)
  // delivered it: uninferred RAM 34 -> 1, all 32 WCS chips in M10K, and the
  // board boots to the OPCOM prompt on this arm.
  //
  // The fix: give the array read a register of its own that does nothing
  // but hold array data, and rebuild the write-first bypass outside it from
  // ordinary flip-flops.
  //
  // ramstyle "no_rw_check": a read and a write can hit the same address in
  // the same cycle. The bypass mux already supplies the new data there, so
  // whatever the M10K hands back on that cycle is discarded. Saying so
  // stops Quartus building bypass hardware we do not need.
  (* ramstyle = "no_rw_check, M10K" *)
  reg [3:0] idt_memory_array[0:4095];

  // Same optional preload as the reference arm. Quartus honours an
  // `initial $readmemh` as M10K bitstream init - unlike the old megafunction
  // arm, which needed a separately generated .mif per chip
  // (fpga/mister/tools/wcs_hex_to_mif.py, deleted together with it).
`ifdef SKIP_WCS_LOAD
  initial begin
    if (INIT_FILE != "") $readmemh(INIT_FILE, idt_memory_array);
  end
`endif

  reg [3:0] mem_q   = 4'h0;   // pure array read - THIS is the M10K output reg
  reg [3:0] byp_q   = 4'h0;   // write data, held one clock to match mem_q
  reg       byp_sel = 1'b0;   // the last enabled cycle was a write
  reg       regCE_n = 1'b1;
  reg       regWE_n = 1'b1;

  always @(posedge clk) begin
    regCE_n <= CE_n;
    regWE_n <= WE_n;
    if (!CE_n) begin
      // Read on EVERY enabled cycle, with nothing but the enable gating it.
      // On a write cycle the fetched value is thrown away by the mux below.
      mem_q <= idt_memory_array[A_11_0];

      if (!WE_n) idt_memory_array[A_11_0] <= D_3_0_IN;

      byp_q   <= D_3_0_IN;
      byp_sel <= ~WE_n;
    end
    // CE_n high: mem_q, byp_q and byp_sel all hold, so the output holds -
    // exactly as data_out holds in the reference arm.
  end

  // Write-first bypass, rebuilt outside the memory read register.
  wire [3:0] data_out = byp_sel ? byp_q : mem_q;

  // Same output mask as the reference arm.
  assign D_3_0_OUT = (!regCE_n && regWE_n) ? data_out : 4'b0000;

`else

  // Memory array - 4K x 4-bit. Marked for block RAM inference on FPGA targets.
  // This is the REFERENCE arm: Vivado (ram_style), Gowin (syn_ramstyle) plus
  // plus Verilator and iverilog, all take it and all infer block RAM from
  // it. Quartus does not - it takes the QUARTUS_RAM_INFER arm above instead.
  // (Keep "Verilator" off the START of a comment line: Verilator reads a
  // comment beginning with that word as a lint metacomment and dies with
  // "Unknown verilator comment", which broke every sim build 01-SEP-2026.)
  //
  // MEASURED 01-SEP-2026 (build v46), correcting an earlier guess of mine:
  // adding Quartus's own "ramstyle" spelling here does NOT make Quartus
  // infer the array. It still reports every instance as
  //   Info (276007): ... uninferred due to asynchronous read logic
  // and fails Analysis & Synthesis trying to build them from flip-flops.
  // The attribute spelling was never the reason inference failed; the
  // structure of the output register is (see the QUARTUS_RAM_INFER arm).
  // "ramstyle" is left in place because it is the correct spelling to state
  // for Quartus and costs nothing, not because it fixes anything.
  //
  // Quartus also warns Warning (10306) on syn_ramstyle="block_ram": it does
  // recognise Synplify's attribute NAME but not Gowin's VALUE for it. That
  // warning is harmless - Quartus never builds this arm.
  (* syn_ramstyle = "block_ram", ram_style = "block", ramstyle = "M10K" *)
  reg [3:0] idt_memory_array[0:4095];

  // Optional block-RAM preload (bitstream INIT on FPGA; $readmemh in sim).
  // Only active under SKIP_WCS_LOAD; preserves BRAM inference (Xilinx + Gowin).
`ifdef SKIP_WCS_LOAD
  initial begin
    if (INIT_FILE != "") $readmemh(INIT_FILE, idt_memory_array);
  end
`endif

  // -----------------------------------------------------------------------
  // Unified sim/FPGA model: posedge-clk write-first synchronous RAM.
  //
  // Both branches now use the SAME timing semantics — no `ifdef VERILATOR_SIM`
  // divergence. Read latency is 1 sysclk: address change at posedge clk N
  // produces the corresponding data at posedge clk N+1.
  //
  // Why 1 sysclk and not zero/combinational:
  //
  //   With zero-delay reads, the WCS feedback loop (WCS→CSBITS→SC5/SC6→
  //   regREP→regW→CSA→LUA→WCS) collapses entirely within one MCLK=0 idle
  //   period. The TVEC dispatch chain (o000017→o000016→o002001) resolves
  //   in delta time without intermediate MCLK pulses, and o000016 LDLC is
  //   skipped (R81 in CGA_DCD captures the wrong CSCOMM).
  //
  //   The 1-sysclk read delay forces each TVEC chain step to take its own
  //   MCLK cycle, matching how the original ASIC's WCS RAM access time
  //   (15-20ns) was a significant fraction of the original cycle period.
  //
  // Why posedge-only (not negedge writes / negedge reads):
  //
  //   The previous model had VERILATOR_SIM doing reads on posedge but
  //   writes on negedge, while the FPGA branch did BOTH on negedge — the
  //   half-sysclk shift created a sim/FPGA divergence. Unifying both to
  //   posedge eliminates the divergence and gives the same timing model
  //   in iverilog, Verilator, and the FPGA block RAM.
  //
  // Block RAM inference: Vivado and Gowin both recognise the
  // "always @(posedge clk) begin if (we) mem[a] <= d; out <= mem[a]; end"
  // pattern as a write-first BRAM template.
  // -----------------------------------------------------------------------
  reg [3:0] data_out  = 4'h0;
  reg       regCE_n   = 1'b1;
  reg       regWE_n   = 1'b1;

  always @(posedge clk) begin
    regCE_n <= CE_n;
    regWE_n <= WE_n;
    if (!CE_n) begin
      if (!WE_n) begin
        // Write happens at posedge clk N. Read of the SAME address at the
        // same edge returns the just-written value (write-first behavior).
        idt_memory_array[A_11_0] <= D_3_0_IN;
        data_out                 <= D_3_0_IN;
      end else begin
        // Pure read: capture memory[A] into data_out for the next cycle.
        data_out                 <= idt_memory_array[A_11_0];
      end
    end
  end

  // Output: tri-state-equivalent. Drives data_out only when the registered
  // CE_n is asserted AND the registered WE_n is high (read mode).
  assign D_3_0_OUT = (!regCE_n && regWE_n) ? data_out : 4'b0000;

`endif

endmodule