Skip to content

floppy_hwtest_top

Source: Verilog/fpga/nexys4ddr/floppy-hw-test/floppy_hwtest_top.v

Hierarchy: not instantiated by any of the 9 build tops (elaborated by yosys).

Module hierarchy - All modules

floppy_hwtest_top symbol

Schematic

Drawn from the Verilog: no build top uses this module, so it was elaborated from its own file with no defines and default parameters. 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).

floppy_hwtest_top schematic

Description

floppy_hwtest_top - SELF-CHECKING VERILOG TEST PROGRAM ON THE REAL NEXYS 4 DDR HARDWARE for the floppy DMA path (root cause + fix: ../HANDOFF-floppy-dma-investigation.md PART 0, retired - git c4896a4). WHAT IT CONTAINS - the real RTL under test, no CPU: ND_FLOPPY_DMA (the floppy controller) ND_DMA_MASTER (the bus master whose read capture had the fault) nd_storage + nd_storage_floppy_adapter + DDR2 + real SD card a block-RAM main memory answering the real ND-bus handshake a CONTENTION INJECTOR: before every memory read answer it drives two clock ticks of a rotating "CPU instruction fetch" word onto the shared BD lines - the exact flicker measured on the failing system (165562 = IOX 1562 etc.). This is what the CPU's polling loop does on the real board; without it a zero-word read cannot corrupt and the test would prove nothing. WHAT IT DOES, forever, ~ every 4 s: an FSM emulates the CPU's IOX accesses - preseeds memory, writes the 12-word command block at 3000 (octal; READ fmt 3, diskAddress 0, WORD-COUNT mode 1024 words, target 20000 octal - the exact first operation of the 1560& boot), kicks IOX 1565/1567/1563, polls IOX 1562 for ready, then checksums ALL 1024 transferred words in hardware and prints on the UART (9600 8N1): S ssssss E eeeeee F ffffff B b0 b1 b2 b3 C cccccc P (same field layout as tests/floppy-dma-test; P = HW-PASS, F = HW-FAIL, T = timeout). Golden values from the nd100x oracle: S 1000x0 E 000010 F 000000 B 000060 000057 000062 000015 C 125441 PASS requires: not timed out, error code bits 14:9 of E all zero, B0 = 000060, checksum = 125441. On the PRE-FIX ND_DMA_MASTER this test FAILS (the injector corrupts the zero-valued command-block words exactly as the CPU did); on the fixed RTL it PASSES. Ronny Hansen

Ports

Direction Width Name Description
input 1 clk100 E3, 100 MHz oscillator
input 1 cpu_resetn C12, red CPU RESET (active low)
input 1 btnc N17, centre button - second reset
input 1 uart_txd_in C4, PC -> FPGA (unused, kept for the pinout)
output 1 uart_rxd_out D4, FPGA -> PC
output 1 sd_reset E2, LOW powers the slot
input 1 sd_cd A1
output 1 sd_clk B1
inout 1 sd_cmd C1
inout 1 sd_dat0 C2
inout 1 sd_dat1 E1
inout 1 sd_dat2 F1
inout 1 sd_dat3 D2
output [7:0] led
inout [15:0] ddr2_dq
inout [1:0] ddr2_dqs_p
inout [1:0] ddr2_dqs_n (active low)
output [12:0] ddr2_addr
output [2:0] ddr2_ba
output 1 ddr2_ras_n (active low)
output 1 ddr2_cas_n (active low)
output 1 ddr2_we_n (active low)
output [0:0] ddr2_ck_p
output [0:0] ddr2_ck_n (active low)
output [0:0] ddr2_cke
output [0:0] ddr2_cs_n (active low)
output [1:0] ddr2_dm
output [0:0] ddr2_odt

Verilog source

Verilog/fpga/nexys4ddr/floppy-hw-test/floppy_hwtest_top.v on GitHub.

Show the Verilog of floppy_hwtest_top (360 lines)
/****************************************************************************
** floppy_hwtest_top - SELF-CHECKING VERILOG TEST PROGRAM ON THE REAL      **
** NEXYS 4 DDR HARDWARE for the floppy DMA path (root cause + fix:        **
** ../HANDOFF-floppy-dma-investigation.md PART 0, retired - git c4896a4).  **
**                                                                         **
** WHAT IT CONTAINS - the real RTL under test, no CPU:                     **
**   ND_FLOPPY_DMA (the floppy controller)                                 **
**   ND_DMA_MASTER (the bus master whose read capture had the fault)       **
**   nd_storage + nd_storage_floppy_adapter + DDR2 + real SD card          **
**   a block-RAM main memory answering the real ND-bus handshake           **
**   a CONTENTION INJECTOR: before every memory read answer it drives      **
**     two clock ticks of a rotating "CPU instruction fetch" word onto     **
**     the shared BD lines - the exact flicker measured on the failing     **
**     system (165562 = IOX 1562 etc.). This is what the CPU's polling     **
**     loop does on the real board; without it a zero-word read cannot     **
**     corrupt and the test would prove nothing.                           **
**                                                                         **
** WHAT IT DOES, forever, ~ every 4 s: an FSM emulates the CPU's IOX       **
** accesses - preseeds memory, writes the 12-word command block at 3000    **
** (octal; READ fmt 3, diskAddress 0, WORD-COUNT mode 1024 words, target   **
** 20000 octal - the exact first operation of the 1560& boot), kicks IOX   **
** 1565/1567/1563, polls IOX 1562 for ready, then checksums ALL 1024       **
** transferred words in hardware and prints on the UART (9600 8N1):        **
**                                                                         **
**   S ssssss E eeeeee F ffffff B b0 b1 b2 b3 C cccccc P                   **
**                                                                         **
** (same field layout as tests/floppy-dma-test; P = HW-PASS, F = HW-FAIL, **
** T = timeout). Golden values from the nd100x oracle:                     **
**   S 1000x0  E 000010  F 000000  B 000060 000057 000062 000015           **
**   C 125441                                                              **
** PASS requires: not timed out, error code bits 14:9 of E all zero,       **
** B0 = 000060, checksum = 125441. On the PRE-FIX ND_DMA_MASTER this      **
** test FAILS (the injector corrupts the zero-valued command-block words   **
** exactly as the CPU did); on the fixed RTL it PASSES.                    **
**                                                                         **
** Ronny Hansen                                                            **
*****************************************************************************/
`default_nettype none

module floppy_hwtest_top (
    input  wire clk100,       // E3, 100 MHz oscillator
    input  wire cpu_resetn,   // C12, red CPU RESET (active low)
    input  wire btnc,         // N17, centre button - second reset

    input  wire uart_txd_in,  // C4, PC -> FPGA (unused, kept for the pinout)
    output wire uart_rxd_out, // D4, FPGA -> PC

    output wire sd_reset,     // E2, LOW powers the slot
    input  wire sd_cd,        // A1
    output wire sd_clk,       // B1
    inout  wire sd_cmd,       // C1
    inout  wire sd_dat0,      // C2
    inout  wire sd_dat1,      // E1
    inout  wire sd_dat2,      // F1
    inout  wire sd_dat3,      // D2

    output wire [7:0] led,

    inout  wire [15:0] ddr2_dq,
    inout  wire [ 1:0] ddr2_dqs_p,
    inout  wire [ 1:0] ddr2_dqs_n,
    output wire [12:0] ddr2_addr,
    output wire [ 2:0] ddr2_ba,
    output wire        ddr2_ras_n,
    output wire        ddr2_cas_n,
    output wire        ddr2_we_n,
    output wire [ 0:0] ddr2_ck_p,
    output wire [ 0:0] ddr2_ck_n,
    output wire [ 0:0] ddr2_cke,
    output wire [ 0:0] ddr2_cs_n,
    output wire [ 1:0] ddr2_dm,
    output wire [ 0:0] ddr2_odt
);

  /*********************************************
  *  Clocks - same values as the ND-120 build  *
  *  (build.tcl clk=12: MMCM VCO 1000 MHz)     *
  **********************************************/
`ifndef HWTEST_CPU_DIV
  `define HWTEST_CPU_DIV 80.0
`endif
  wire clk_cpu_pre, clk_stor_pre, clk200_pre, clkfb_out, clkfb_in, mmcm_locked;
  wire clk_cpu, clk_stor, clk200;

  MMCME2_BASE #(
      .BANDWIDTH       ("OPTIMIZED"),
      .CLKFBOUT_MULT_F (10.0),          // 100 MHz x10 = 1000 MHz VCO
      .CLKIN1_PERIOD   (10.0),
      .CLKOUT0_DIVIDE_F(`HWTEST_CPU_DIV), // 1000/80 = 12.5 MHz (ND-120 clk=12)
      .CLKOUT1_DIVIDE  (37),            // 27.027 MHz - SD/FAT stack
      .CLKOUT2_DIVIDE  (5),             // 200 MHz    - DDR2 controller
      .DIVCLK_DIVIDE   (1)
  ) mmcm (
      .CLKIN1  (clk100),
      .CLKFBIN (clkfb_in),
      .CLKFBOUT(clkfb_out),
      .CLKOUT0 (clk_cpu_pre),
      .CLKOUT1 (clk_stor_pre),
      .CLKOUT2 (clk200_pre),
      .LOCKED  (mmcm_locked),
      .PWRDWN  (1'b0),
      .RST     (1'b0),
      .CLKOUT0B(), .CLKOUT1B(), .CLKOUT2B(), .CLKOUT3(), .CLKOUT3B(),
      .CLKOUT4(), .CLKOUT5(), .CLKOUT6(), .CLKFBOUTB()
  );

  BUFG bufg_fb  (.I(clkfb_out),    .O(clkfb_in));
  BUFG bufg_cpu (.I(clk_cpu_pre),  .O(clk_cpu));
  BUFG bufg_st  (.I(clk_stor_pre), .O(clk_stor));
  BUFG bufg_200 (.I(clk200_pre),   .O(clk200));

  wire rst_req_n = cpu_resetn & ~btnc & mmcm_locked;

  reg [3:0] rst_cpu_sh = 4'd0;
  always @(posedge clk_cpu)
    if (!rst_req_n) rst_cpu_sh <= 4'd0;
    else            rst_cpu_sh <= {rst_cpu_sh[2:0], 1'b1};
  wire rst_cpu_n = rst_cpu_sh[3];

  reg [3:0] rst_st_sh = 4'd0;
  always @(posedge clk_stor)
    if (!rst_req_n) rst_st_sh <= 4'd0;
    else            rst_st_sh <= {rst_st_sh[2:0], 1'b1};
  wire rst_stor_n = rst_st_sh[3];

  /*********************************************
  *  SD pads - same tristate rule as the top   *
  **********************************************/
  assign sd_reset = 1'b0;

  wire s_sd_clk_o, s_sd_cmd_o, s_sd_cmd_oe, s_sd_dat0_o, s_sd_dat0_oe;
  assign sd_clk  = s_sd_clk_o;
  assign sd_cmd  = s_sd_cmd_oe  ? s_sd_cmd_o  : 1'bz;
  assign sd_dat0 = s_sd_dat0_oe ? s_sd_dat0_o : 1'bz;
  assign sd_dat1 = 1'bz;
  assign sd_dat2 = 1'bz;
  assign sd_dat3 = 1'bz;

  /*********************************************
  *  DDR2 region - identical to the ND-120 top *
  **********************************************/
  wire        mem_start, mem_we, mem_busy, mem_done;
  wire [19:0] mem_addr;
  wire [31:0] mem_wdata, mem_rdata;

  wire          ui_clk, ui_rst, calib_done;
  wire          req_valid, req_we, req_ready, rsp_valid;
  wire [ 26:0]  req_addr;
  wire [127:0]  req_wdata, rsp_rdata;
  wire [ 15:0]  req_wmask;

  nd_ddr2_storage u_region (
      .stor_clk(clk_stor), .stor_rst_n(rst_stor_n),
      .mem_start(mem_start), .mem_we(mem_we), .mem_addr(mem_addr),
      .mem_wdata(mem_wdata), .mem_rdata(mem_rdata),
      .mem_busy(mem_busy), .mem_done(mem_done),
      .ui_clk(ui_clk), .ui_rst(ui_rst),
      .req_valid(req_valid), .req_we(req_we), .req_addr(req_addr),
      .req_wdata(req_wdata), .req_wmask(req_wmask), .req_ready(req_ready),
      .rsp_valid(rsp_valid), .rsp_rdata(rsp_rdata)
  );

  nd_ddr2_port u_ddr2 (
      .sys_clk_200(clk200), .rst_n(rst_req_n),
      .ui_clk(ui_clk), .ui_rst(ui_rst), .calib_done(calib_done),
      .req_valid(req_valid), .req_we(req_we), .req_addr(req_addr),
      .req_wdata(req_wdata), .req_wmask(req_wmask), .req_ready(req_ready),
      .rsp_valid(rsp_valid), .rsp_rdata(rsp_rdata),
      .ddr2_dq(ddr2_dq), .ddr2_dqs_p(ddr2_dqs_p), .ddr2_dqs_n(ddr2_dqs_n),
      .ddr2_addr(ddr2_addr), .ddr2_ba(ddr2_ba), .ddr2_ras_n(ddr2_ras_n),
      .ddr2_cas_n(ddr2_cas_n), .ddr2_we_n(ddr2_we_n),
      .ddr2_ck_p(ddr2_ck_p), .ddr2_ck_n(ddr2_ck_n), .ddr2_cke(ddr2_cke),
      .ddr2_cs_n(ddr2_cs_n), .ddr2_dm(ddr2_dm), .ddr2_odt(ddr2_odt)
  );

  /*********************************************
  *  nd_storage - same parameters the ND-120   *
  *  build resolves to (wrapper INCLUDE_TAPE/  *
  *  FLOPPY/WD => CACHE_MASK 11000000,         *
  *  FILE0 = BOOT.TAP)                         *
  **********************************************/
  localparam N = 8;
  wire [N-1:0]    open_req_w, open_ok_w, open_err_w, req_w, wr_w;
  wire [N-1:0]    busy_w, done_w, err_w, buf_we_w;
  wire [N*4-1:0]  err_code_w;
  wire [N*32-1:0] size_bytes_w;
  wire [N*16-1:0] block_w, buf_wdata_w, buf_rdata_w;
  wire [N*10-1:0] buf_addr_w;
  wire [1:0]      sd_status;

  nd_storage #(
      .SIMULATE   (0),
      .CACHE_MASK (8'b11000000),
      .FILE0_NAME ("BOOT.TAP"), .FILE0_LEN(8'd8)
  ) u_nd_storage (
      .clk_stor  (clk_stor),
      .rst_stor_n(rst_stor_n),
      .clk_cpu   (clk_cpu),
      .rst_cpu_n (rst_cpu_n),
      .sd_clk_o  (s_sd_clk_o),
      .sd_cmd_i  (sd_cmd),
      .sd_cmd_o  (s_sd_cmd_o),
      .sd_cmd_oe (s_sd_cmd_oe),
      .sd_dat0_i (sd_dat0),
      .sd_dat0_o (s_sd_dat0_o),
      .sd_dat0_oe(s_sd_dat0_oe),
      .mem_start (mem_start),
      .mem_we    (mem_we),
      .mem_addr  (mem_addr),
      .mem_wdata (mem_wdata),
      .mem_rdata (mem_rdata),
      .mem_busy  (mem_busy),
      .mem_done  (mem_done),
      .open_req  (open_req_w),
      .open_ok   (open_ok_w),
      .open_err  (open_err_w),
      .size_bytes(size_bytes_w),
      .req       (req_w),
      .wr        (wr_w),
      .block     (block_w),
      .busy      (busy_w),
      .done      (done_w),
      .err       (err_w),
      .err_code  (err_code_w),
      .dbg_state(), .dbg_lba(), .dbg_wdata(), .dbg_rdata(), .dbg_bufw(),
      .dbg_bufwe(), .dbg_fsec(), .dbg_rx_stb(), .dbg_rx_raw(),
      .dbg_rx_byte(), .dbg_past_eof(), .dbg_grant(),
      .buf_addr  (buf_addr_w),
      .buf_wdata (buf_wdata_w),
      .buf_we    (buf_we_w),
      .buf_rdata (buf_rdata_w),
      .sd_status (sd_status),
      .card_type (),
      .fs_type   ()
  );


  /*********************************************
  *  Client 1 = the real floppy adapter, now   *
  *  driven by the REAL ND_FLOPPY_DMA          *
  **********************************************/
  wire        fd_req, fd_wr;
  wire [15:0] fd_lsect;
  wire [ 1:0] fd_format, fd_drive;
  wire [10:0] fd_wc;
  wire        fd_done, fd_err;
  wire [3:0]  fd_code;
  wire [9:0]  fdb_addr;
  wire [15:0] fdb_wdata;
  wire        fdb_we;
  wire [15:0] fdb_rdata;

  wire        f_open_req, f_req, f_wr;
  wire [15:0] f_block, f_buf_rdata;

  // held open, exactly like the devices wrapper (gen_floppy)
  reg s_fopened = 1'b0;
  always @(posedge clk_cpu or negedge rst_cpu_n)
    if (!rst_cpu_n) s_fopened <= 1'b0;
    else if (open_ok_w[1]) s_fopened <= 1'b1;
  wire s_fopen_pulse = !s_fopened;

  nd_storage_floppy_adapter #(.DRIVE(2'd0)) u_floppy_adapter (
      .clk_cpu       (clk_cpu),
      .rst_n         (rst_cpu_n),
      .disk_req      (fd_req),
      .disk_wr       (fd_wr),
      .disk_lsect    (fd_lsect),
      .disk_format   (fd_format),
      .disk_drive    (fd_drive),
      .disk_wordcount(fd_wc),
      .disk_done     (fd_done),
      .disk_err      (fd_err),
      .disk_err_code (fd_code),
      .dbuf_addr     (fdb_addr),
      .dbuf_wdata    (fdb_wdata),
      .dbuf_we       (fdb_we),
      .dbuf_rdata    (fdb_rdata),
      .open_start    (s_fopen_pulse),
      .c_open_req    (f_open_req),
      .c_open_ok     (open_ok_w[1]),
      .c_open_err    (open_err_w[1]),
      .c_size_bytes  (size_bytes_w[63:32]),
      .c_req         (f_req),
      .c_wr          (f_wr),
      .c_block       (f_block),
      .c_busy        (busy_w[1]),
      .c_done        (done_w[1]),
      .c_err         (err_w[1]),
      .c_err_code    (err_code_w[7:4]),
      .c_buf_addr    (buf_addr_w[19:10]),
      .c_buf_wdata   (buf_wdata_w[31:16]),
      .c_buf_we      (buf_we_w[1]),
      .c_buf_rdata   (f_buf_rdata)
  );

  // Clients 0 (tape) and 6 (WD): held opens like the wrapper's sequencers.
  reg s_topened = 1'b0, s_wopened = 1'b0;
  always @(posedge clk_cpu or negedge rst_cpu_n)
    if (!rst_cpu_n) begin s_topened <= 1'b0; s_wopened <= 1'b0; end
    else begin
      if (open_ok_w[0]) s_topened <= 1'b1;
      if (open_ok_w[6]) s_wopened <= 1'b1;
    end

  assign open_req_w = {1'b0, !s_wopened, 4'b0000, f_open_req, !s_topened};
  assign req_w      = {6'b0, f_req, 1'b0};
  assign wr_w       = {6'b0, f_wr, 1'b0};
  assign block_w    = {96'd0, f_block, 16'd0};
  assign buf_rdata_w= {96'd0, f_buf_rdata, 16'd0};

  // media format is derived inside the core from the image size
  wire w_pass, w_timeout, w_idle;
  floppy_hwtest_core u_core (
      .clk_cpu   (clk_cpu),
      .rst_cpu_n (rst_cpu_n),
      .mount_ok  (open_ok_w[1]),
      .img_size  (size_bytes_w[63:32]),
      .fd_req    (fd_req),
      .fd_wr     (fd_wr),
      .fd_lsect  (fd_lsect),
      .fd_format (fd_format),
      .fd_drive  (fd_drive),
      .fd_wc     (fd_wc),
      .fd_done   (fd_done),
      .fd_err    (fd_err),
      .fd_code   (fd_code),
      .fdb_addr  (fdb_addr),
      .fdb_wdata (fdb_wdata),
      .fdb_we    (fdb_we),
      .fdb_rdata (fdb_rdata),
      .uart_txd  (uart_rxd_out),
      .st_pass   (w_pass),
      .st_timeout(w_timeout),
      .st_idle   (w_idle)
  );

  assign led[0] = mmcm_locked;
  assign led[1] = calib_done;
  assign led[2] = sd_status[0];
  assign led[3] = open_ok_w[1];
  assign led[4] = w_idle;
  assign led[5] = w_pass;
  assign led[6] = w_timeout | (|open_err_w);
  assign led[7] = sd_cd;

  /* verilator lint_off UNUSEDSIGNAL */
  wire _unused = &{1'b0, uart_txd_in, sd_dat1, sd_dat2, sd_dat3,
                   busy_w[0], busy_w[7:2], done_w[0], done_w[7:2],
                   err_w[0], err_w[7:2], err_code_w[3:0], err_code_w[31:8],
                   size_bytes_w[31:0], size_bytes_w[255:64], block_w,
                   buf_addr_w[9:0], buf_addr_w[159:20],
                   buf_wdata_w[15:0], buf_wdata_w[127:32],
                   buf_we_w[0], buf_we_w[7:2], open_ok_w, open_err_w,
                   ui_rst, 1'b0};
  /* verilator lint_on UNUSEDSIGNAL */

endmodule

`default_nettype wire