Skip to content

ND_TAPE_400

Source: Verilog/ND-BUS-DEVICES/TAPE-400/circuit/ND_TAPE_400.v

Where it sits (Simulation): ND120_TOP > ND120_CORE > ND_TAPE_400 - instance path: CORE.gen_tape.TAPE_400

Used in: ND120_CORE (Simulation, Tang, Nexys, MiSTer, MEGA65 R6, MEGA65 R3, QMTECH)

Contains: no other modules.

Module hierarchy - All modules

ND_TAPE_400 symbol

Schematic

Drawn from the Verilog: the yosys netlist of the Simulation (Verilator) build, instance CORE.gen_tape.TAPE_400. 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).

ND_TAPE_400 schematic

Description

ND-100 PAPERTAPE READER, DEVICE 400 (octal) Register core matching the C reference model in simDevices/NDDevices.cpp (class PaperTape), which runSim boots with today: IOX 400 read data returns the character buffer, clears RFT IOX 401 write data (papertape WRITER only - ignored) IOX 402 read status bit0 = interrupt enabled bit2 = read active bit3 = ready for transfer (RFT) IOX 403 write control bit0 = interrupt enable bit2 = activate (fetch next byte) bit3 = ready for transfer preset bit4 = device clear Ident code 02 (octal), interrupt level 12. Interrupt pending = interruptEnabled AND readyForTransfer. IDENT on level 12 with pending interrupt returns the ident code and clears BOTH the pending flag and the interrupt-enable bit. The byte source is a port: the sim testbench serves a file image, the hardware serves an SD-FAT stream. The C model fetches a byte instantly on activate; here RFT rises when byte_valid arrives, which the polling loader tolerates by design (it polls status bit 3). ==== WHY THIS CARD REPORTS NO STORAGE ERROR ========================== The other three ND storage controllers in this tree map an SD-FAT failure reason (Verilog/SD-FAT/circuit/nd_storage_status.vh) onto a status bit or error code their own manual defines: ND_WINCHESTER status +4 bits b6/b7/b8/b9 (ND-11.015.01 sec 3.5) ND_SMD status bits b5/b6/b7/b8/b10 (ND-11.020.01 sec 2.5) ND_FLOPPY_DMA Status Word 1 b9-14 error code, oct 16/20 and 5 (ND-11.021.01 sec 3.4/3.9) THIS card has nowhere to put one. Its whole interface is the four registers above: the status word (402) carries interrupt-enabled, read-active and ready-for-transfer, and nothing else - there is no error bit and no error code anywhere in the device. A real ND-100 paper tape reader physically cannot say "the medium is faulty"; it simply never becomes ready. Adding a bit here would be inventing a register the card does not have, which the rule in nd_storage_status.vh forbids. So the behaviour the guest sees is unchanged and correct: on any storage failure the source stops answering byte_req, RFT stays low, and the loader waits exactly as it would at the end of the tape. The REASON is published one level down instead, on the sticky diagnostic pair nd_storage_tape_adapter.fault / .fault_code, brought out of nd_storage_devices as TDISK_FAULT / TDISK_ERR_CODE. No ND logic reads those - they exist for a testbench, a probe or a board LED. Without them "the SD card fell out" and "you reached the end of the tape" are the same observation from every point in the design. Last reviewed: 11-JUL-2026 Ronny Hansen

Parameters

Parameter Default
BASE_ADDR 16'o000400
IDENT_CODE 16'o000002
INT_LEVEL 4'd12

Ports

Direction Width Name Description
input 1 sysclk CPU + bus + device domain (ND3202D sysclk/CLOCK_1/CLOCK_2) (from ND120_CORE.clk_cpu)
input 1 sys_rst_n (active low) Active-low reset, from the board's power-on reset (from ND120_CORE.sys_rst_n)
input [15:0] iox_addr captured IOX address (from ND_BUS_SLAVE.iox_addr)
input 1 iox_wr 1-cycle write strobe (from ND_BUS_SLAVE.iox_wr)
input [15:0] iox_wdata write data, valid with iox_wr (from ND_BUS_SLAVE.iox_wdata)
input 1 iox_rd 1-cycle read strobe (from ND_BUS_SLAVE.iox_rd)
output [15:0] iox_rdata OR-bus: 0 when not addressed
output 1 iox_sel 1 = this core owns the captured IOX address
output [3:0] int_pending {lvl13,lvl12,lvl11,lvl10}
input 1 ident_strobe 1-cycle IDENT poll (from ND_BUS_SLAVE.ident_strobe)
input [3:0] ident_level binary 10..13, valid with ident_strobe (from ND_BUS_SLAVE.ident_level)
input 1 ident_grant_in daisy chain: 1 = we may answer
output 1 ident_grant_out pass on when we don't answer
output 1 ident_hit OR-bus contribution
output [15:0] ident_code OR-bus contribution
output 1 byte_req 1-cycle pulse: fetch next byte
input 1 byte_valid 1-cycle pulse: byte_data is the byte
input [7:0] byte_data
output 1 source_rewind 1-cycle pulse on device clear

Verilog source

Verilog/ND-BUS-DEVICES/TAPE-400/circuit/ND_TAPE_400.v on GitHub.

Show the Verilog of ND_TAPE_400 (174 lines)
/**************************************************************************
** ND-100 PAPERTAPE READER, DEVICE 400 (octal)                           **
**                                                                       **
** Register core matching the C reference model in                       **
** simDevices/NDDevices.cpp (class PaperTape), which runSim boots        **
** with today:                                                           **
**                                                                       **
**   IOX 400  read data      returns the character buffer, clears RFT    **
**   IOX 401  write data     (papertape WRITER only - ignored)           **
**   IOX 402  read status    bit0 = interrupt enabled                    **
**                           bit2 = read active                          **
**                           bit3 = ready for transfer (RFT)             **
**   IOX 403  write control  bit0 = interrupt enable                     **
**                           bit2 = activate (fetch next byte)           **
**                           bit3 = ready for transfer preset            **
**                           bit4 = device clear                         **
**                                                                       **
** Ident code 02 (octal), interrupt level 12.                            **
** Interrupt pending = interruptEnabled AND readyForTransfer.            **
** IDENT on level 12 with pending interrupt returns the ident code and   **
** clears BOTH the pending flag and the interrupt-enable bit.            **
**                                                                       **
** The byte source is a port: the sim testbench serves a file image,     **
** the hardware serves an SD-FAT stream. The C model fetches a byte      **
** instantly on activate; here RFT rises when byte_valid arrives, which  **
** the polling loader tolerates by design (it polls status bit 3).       **
**                                                                       **
** ==== WHY THIS CARD REPORTS NO STORAGE ERROR ==========================**
** The other three ND storage controllers in this tree map an SD-FAT     **
** failure reason (Verilog/SD-FAT/circuit/nd_storage_status.vh) onto a   **
** status bit or error code their own manual defines:                    **
**   ND_WINCHESTER  status +4 bits b6/b7/b8/b9  (ND-11.015.01 sec 3.5)   **
**   ND_SMD         status bits b5/b6/b7/b8/b10 (ND-11.020.01 sec 2.5)   **
**   ND_FLOPPY_DMA  Status Word 1 b9-14 error code, oct 16/20 and 5      **
**                                (ND-11.021.01 sec 3.4/3.9)             **
** THIS card has nowhere to put one. Its whole interface is the four     **
** registers above: the status word (402) carries interrupt-enabled,     **
** read-active and ready-for-transfer, and nothing else - there is no    **
** error bit and no error code anywhere in the device. A real ND-100     **
** paper tape reader physically cannot say "the medium is faulty"; it    **
** simply never becomes ready. Adding a bit here would be inventing a    **
** register the card does not have, which the rule in                    **
** nd_storage_status.vh forbids.                                         **
**                                                                       **
** So the behaviour the guest sees is unchanged and correct: on any      **
** storage failure the source stops answering byte_req, RFT stays low,   **
** and the loader waits exactly as it would at the end of the tape.      **
** The REASON is published one level down instead, on the sticky         **
** diagnostic pair nd_storage_tape_adapter.fault / .fault_code, brought  **
** out of nd_storage_devices as TDISK_FAULT / TDISK_ERR_CODE. No ND    **
** logic reads those - they exist for a testbench, a probe or a board    **
** LED. Without them "the SD card fell out" and "you reached the end of  **
** the tape" are the same observation from every point in the design.    **
**                                                                       **
** Last reviewed: 11-JUL-2026                                            **
** Ronny Hansen                                                          **
***************************************************************************/

module ND_TAPE_400 #(
    parameter [15:0] BASE_ADDR  = 16'o000400,
    parameter [15:0] IDENT_CODE = 16'o000002,
    parameter [3:0]  INT_LEVEL  = 4'd12
) (
    input wire sysclk,                   //! CPU + bus + device domain (ND3202D sysclk/CLOCK_1/CLOCK_2) (from ND120_CORE.clk_cpu)
    input wire sys_rst_n,                //! Active-low reset, from the board's power-on reset (from ND120_CORE.sys_rst_n)

    // Device bus (from ND_BUS_SLAVE)
    input  wire [15:0] iox_addr,         //! captured IOX address (from ND_BUS_SLAVE.iox_addr)
    input  wire        iox_wr,           //! 1-cycle write strobe (from ND_BUS_SLAVE.iox_wr)
    input  wire [15:0] iox_wdata,        //! write data, valid with iox_wr (from ND_BUS_SLAVE.iox_wdata)
    input  wire        iox_rd,           //! 1-cycle read strobe (from ND_BUS_SLAVE.iox_rd)
    output wire [15:0] iox_rdata,     // OR-bus: 0 when not addressed
    output wire        iox_sel,       // 1 = this core owns the captured IOX address
    output wire [3:0]  int_pending,   // {lvl13,lvl12,lvl11,lvl10}
    input  wire        ident_strobe,     //! 1-cycle IDENT poll (from ND_BUS_SLAVE.ident_strobe)
    input  wire [3:0]  ident_level,      //! binary 10..13, valid with ident_strobe (from ND_BUS_SLAVE.ident_level)
    input  wire        ident_grant_in,   // daisy chain: 1 = we may answer
    output wire        ident_grant_out,  // pass on when we don't answer
    output wire        ident_hit,        // OR-bus contribution
    output wire [15:0] ident_code,       // OR-bus contribution

    // Byte source (file model in sim, SD-FAT streamer on hardware)
    output reg         byte_req,     // 1-cycle pulse: fetch next byte
    input  wire        byte_valid,   // 1-cycle pulse: byte_data is the byte
    input  wire [7:0]  byte_data,
    output reg         source_rewind // 1-cycle pulse on device clear
);

  // Status register bits (same layout as control word, per the C model)
  reg s_int_enabled;    // bit 0
  reg s_read_active;    // bit 2
  reg s_rft;            // bit 3 - ready for transfer
  reg [7:0] s_char_buffer;

  wire [15:0] s_status = {12'd0, s_rft, s_read_active, 1'b0, s_int_enabled};

  // Address decode: 400 (+0) data, 402 (+2) status, 403 (+3) control
  wire s_addressed  = (iox_addr[15:2] == BASE_ADDR[15:2]);
  assign iox_sel    = s_addressed;   // slave gates its BDRY response on this
  wire s_rd_data    = iox_rd && s_addressed && (iox_addr[1:0] == 2'd0);
  wire s_rd_status  = iox_rd && s_addressed && (iox_addr[1:0] == 2'd2);
  wire s_wr_control = iox_wr && s_addressed && (iox_addr[1:0] == 2'd3);

  // OR-bus read data (drive 0 when not addressed - FPGA rule)
  assign iox_rdata = s_rd_data   ? {8'd0, s_char_buffer} :
                     s_rd_status ? s_status : 16'd0;

  // Interrupt pending: enabled AND ready (evaluated continuously,
  // like SetInterruptStatus after every state change in the C model)
  wire s_pending = s_int_enabled && s_rft;
  assign int_pending = {(INT_LEVEL == 4'd13) && s_pending,
                        (INT_LEVEL == 4'd12) && s_pending,
                        (INT_LEVEL == 4'd11) && s_pending,
                        (INT_LEVEL == 4'd10) && s_pending};

  // IDENT daisy chain: answer only with a pending interrupt on OUR level
  wire s_ident_answer = ident_strobe && ident_grant_in &&
                        (ident_level == INT_LEVEL) && s_pending;
  assign ident_hit       = s_ident_answer;
  assign ident_code      = s_ident_answer ? IDENT_CODE : 16'd0;
  assign ident_grant_out = ident_grant_in && !s_ident_answer;

  always @(posedge sysclk or negedge sys_rst_n) begin
    if (!sys_rst_n) begin
      s_int_enabled <= 1'b0;
      s_read_active <= 1'b0;
      s_rft         <= 1'b0;
      s_char_buffer <= 8'd0;
      byte_req      <= 1'b0;
      source_rewind <= 1'b0;
    end else begin
      byte_req      <= 1'b0;
      source_rewind <= 1'b0;

      // IOX 400: reading the data register clears ready-for-transfer
      if (s_rd_data) begin
        s_rft <= 1'b0;
      end

      // IOX 403: control word
      if (s_wr_control) begin
        s_int_enabled <= iox_wdata[0];
        s_read_active <= iox_wdata[2];
        s_rft         <= iox_wdata[3];

        if (iox_wdata[4]) begin
          // Device clear: stop, empty the buffer, rewind the tape
          s_read_active <= 1'b0;
          s_rft         <= 1'b0;
          s_char_buffer <= 8'd0;
          source_rewind <= 1'b1;
        end else if (iox_wdata[2]) begin
          // Activate: fetch the next byte; RFT rises on byte_valid
          s_rft    <= 1'b0;
          byte_req <= 1'b1;
        end
      end

      // Byte arrived from the source
      if (byte_valid) begin
        s_char_buffer <= byte_data;
        s_rft         <= 1'b1;
        s_read_active <= 1'b0;
      end

      // IDENT answered: clear pending (RFT stays - the DATA is still
      // there; the C model clears via the interruptEnabled bit)
      if (s_ident_answer) begin
        s_int_enabled <= 1'b0;
      end
    end
  end

endmodule