// retroDE_ps2 — tb_de25_nano_psmct32_raster_demo_top (Ch149)
//
// Smoke TB for the DE25-Nano board wrapper. Exercises the
// board-shaped path end-to-end:
//
//   1. Drive CLOCK2_50 (the only used clock).
//   2. Hold KEY[0] LOW (active-low push button "pressed") for a
//      handful of cycles, then release HIGH.
//   3. Verify the board top's reset-release sequencer pulses
//      `core_go` exactly once after the reset deassert + GO_DELAY
//      cycles.
//   4. Verify the three sticky status outputs (LED[2:0]) all
//      eventually latch. (The fall-edge order is actually
//      frame_seen → core_halt → dma_done_seen because frame_seen
//      fires on the first empty PCRTC frame after reset; the TB
//      doesn't lock the order — see the rationale below.)
//   5. Verify the raw video pins are driven (DE goes high inside
//      the active region).
//
// This is NOT a re-verification of the Ch146 wrapper — that's
// already covered by tb_top_psmct32_raster_demo. The Ch149 TB only
// validates the BOARD plumbing (clock/reset/core_go sequencer +
// LED polarity + raw video pass-through).

`timescale 1ns/1ps

module tb_de25_nano_psmct32_raster_demo_top;

    // ----------------------------------------------------------------
    // Board-shaped DUT inputs.
    // ----------------------------------------------------------------
    logic        CLOCK0_50;
    logic        CLOCK1_50;
    logic        CLOCK2_50;
    logic [1:0]  KEY;
    logic [3:0]  SW;

    // Tied off — only CLOCK2_50 + KEY[0] are exercised.
    initial CLOCK0_50 = 1'b0;
    initial CLOCK1_50 = 1'b0;
    always #10 CLOCK2_50 = ~CLOCK2_50;        // 50 MHz
    initial CLOCK2_50 = 1'b0;
    initial SW        = 4'b0000;

    // ----------------------------------------------------------------
    // Board-shaped DUT outputs.
    // ----------------------------------------------------------------
    logic [7:0]  LED;
    logic [7:0]  VIDEO_R;
    logic [7:0]  VIDEO_G;
    logic [7:0]  VIDEO_B;
    logic        VIDEO_HSYNC;
    logic        VIDEO_VSYNC;
    logic        VIDEO_DE;

    // Ch164 HDMI data path (board-real outputs); TB doesn't
    // observe them — just elaborates them so the wrapper is
    // valid.
    logic        HDMI_TX_CLK;
    logic [23:0] HDMI_TX_D;
    logic        HDMI_TX_HS;
    logic        HDMI_TX_VS;
    logic        HDMI_TX_DE;

    // Ch165 ADV7513 control bus. Sim doesn't model the I²C
    // pull-ups or the chip's slave-side ACK; the bus floats and
    // the wake-up FSM walks the LUT taking many milliseconds of
    // sim time (the 5 ms TB timeout doesn't cover full LUT
    // completion — that's intentional, this TB pre-dates the
    // wake-up FSM and only smoke-tests the wrapper). HDMI_TX_INT
    // is held high (deasserted active-low interrupt) so the FSM
    // doesn't retrigger init mid-walk.
    wire         HDMI_I2C_SCL;
    wire         HDMI_I2C_SDA;
    logic        HDMI_TX_INT;
    logic        HDMI_MCLK;
    initial HDMI_TX_INT = 1'b1;

    // Ch170 — HPS + LPDDR4A ports the qsys platform shell needs.
    // Sim doesn't model the HPS hard IP behavior; all inputs hold a
    // safe constant and all outputs/inouts are just elaborated so the
    // wrapper is valid. The qsys_top instance still sits inside the
    // DUT — if anything breaks elaboration we see it here.
    logic        LPDDR4A_REFCLK_p;
    wire         LPDDR4A_CS_n;
    wire [5:0]   LPDDR4A_CA;
    wire         LPDDR4A_CK;
    wire         LPDDR4A_CKE;
    wire         LPDDR4A_CK_n;
    wire [3:0]   LPDDR4A_DM;
    wire [31:0]  LPDDR4A_DQ;
    wire [3:0]   LPDDR4A_DQS;
    wire [3:0]   LPDDR4A_DQS_n;
    wire         LPDDR4A_RESET_n;
    logic        LPDDR4A_RZQ;
    initial      LPDDR4A_REFCLK_p = 1'b0;
    initial      LPDDR4A_RZQ      = 1'b0;

    logic        HPS_CLK_25;
    wire         HPS_ENET_MDC;
    wire         HPS_ENET_MDIO;
    logic        HPS_ENET_RX_CLK;
    logic        HPS_ENET_RX_CTL;
    logic [3:0]  HPS_ENET_RX_DATA;
    wire         HPS_ENET_TX_CLK;
    wire         HPS_ENET_TX_CTL;
    wire [3:0]   HPS_ENET_TX_DATA;
    wire         HPS_GSENSOR_I2C_EN;
    wire         HPS_GSENSOR_INT;
    wire         HPS_I2C_SCL;
    wire         HPS_I2C_SDA;
    wire         HPS_KEY;
    wire         HPS_LED;
    wire         HPS_SD_CLK;
    wire         HPS_SD_CMD;
    wire [3:0]   HPS_SD_DATA;
    logic        HPS_UART_RX;
    wire         HPS_UART_TX;
    logic        HPS_USB_CLK;
    wire [7:0]   HPS_USB_DATA;
    logic        HPS_USB_DIR;
    logic        HPS_USB_NXT;
    wire         HPS_USB_STP;
    initial begin
        HPS_CLK_25       = 1'b0;
        HPS_ENET_RX_CLK  = 1'b0;
        HPS_ENET_RX_CTL  = 1'b0;
        HPS_ENET_RX_DATA = 4'b0;
        HPS_UART_RX      = 1'b1;
        HPS_USB_CLK      = 1'b0;
        HPS_USB_DIR      = 1'b0;
        HPS_USB_NXT      = 1'b0;
    end

    de25_nano_psmct32_raster_demo_top dut (
        .CLOCK0_50(CLOCK0_50),
        .CLOCK1_50(CLOCK1_50),
        .CLOCK2_50(CLOCK2_50),
        .KEY(KEY),
        .SW(SW),
        .LED(LED),
        .VIDEO_R(VIDEO_R),
        .VIDEO_G(VIDEO_G),
        .VIDEO_B(VIDEO_B),
        .VIDEO_HSYNC(VIDEO_HSYNC),
        .VIDEO_VSYNC(VIDEO_VSYNC),
        .VIDEO_DE(VIDEO_DE),
        .HDMI_TX_CLK(HDMI_TX_CLK),
        .HDMI_TX_D(HDMI_TX_D),
        .HDMI_TX_HS(HDMI_TX_HS),
        .HDMI_TX_VS(HDMI_TX_VS),
        .HDMI_TX_DE(HDMI_TX_DE),
        .HDMI_I2C_SCL(HDMI_I2C_SCL),
        .HDMI_I2C_SDA(HDMI_I2C_SDA),
        .HDMI_TX_INT(HDMI_TX_INT),
        .HDMI_MCLK(HDMI_MCLK),

        // Ch170 — HPS/LPDDR4A pass-through to qsys_top inside DUT.
        .LPDDR4A_REFCLK_p   (LPDDR4A_REFCLK_p),
        .LPDDR4A_CS_n       (LPDDR4A_CS_n),
        .LPDDR4A_CA         (LPDDR4A_CA),
        .LPDDR4A_CK         (LPDDR4A_CK),
        .LPDDR4A_CKE        (LPDDR4A_CKE),
        .LPDDR4A_CK_n       (LPDDR4A_CK_n),
        .LPDDR4A_DM         (LPDDR4A_DM),
        .LPDDR4A_DQ         (LPDDR4A_DQ),
        .LPDDR4A_DQS        (LPDDR4A_DQS),
        .LPDDR4A_DQS_n      (LPDDR4A_DQS_n),
        .LPDDR4A_RESET_n    (LPDDR4A_RESET_n),
        .LPDDR4A_RZQ        (LPDDR4A_RZQ),
        .HPS_CLK_25         (HPS_CLK_25),
        .HPS_ENET_MDC       (HPS_ENET_MDC),
        .HPS_ENET_MDIO      (HPS_ENET_MDIO),
        .HPS_ENET_RX_CLK    (HPS_ENET_RX_CLK),
        .HPS_ENET_RX_CTL    (HPS_ENET_RX_CTL),
        .HPS_ENET_RX_DATA   (HPS_ENET_RX_DATA),
        .HPS_ENET_TX_CLK    (HPS_ENET_TX_CLK),
        .HPS_ENET_TX_CTL    (HPS_ENET_TX_CTL),
        .HPS_ENET_TX_DATA   (HPS_ENET_TX_DATA),
        .HPS_GSENSOR_I2C_EN (HPS_GSENSOR_I2C_EN),
        .HPS_GSENSOR_INT    (HPS_GSENSOR_INT),
        .HPS_I2C_SCL        (HPS_I2C_SCL),
        .HPS_I2C_SDA        (HPS_I2C_SDA),
        .HPS_KEY            (HPS_KEY),
        .HPS_LED            (HPS_LED),
        .HPS_SD_CLK         (HPS_SD_CLK),
        .HPS_SD_CMD         (HPS_SD_CMD),
        .HPS_SD_DATA        (HPS_SD_DATA),
        .HPS_UART_RX        (HPS_UART_RX),
        .HPS_UART_TX        (HPS_UART_TX),
        .HPS_USB_CLK        (HPS_USB_CLK),
        .HPS_USB_DATA       (HPS_USB_DATA),
        .HPS_USB_DIR        (HPS_USB_DIR),
        .HPS_USB_NXT        (HPS_USB_NXT),
        .HPS_USB_STP        (HPS_USB_STP)
    );

    // ----------------------------------------------------------------
    // Observers.
    // ----------------------------------------------------------------
    int  core_go_pulse_count;
    int  errors;
    bit  saw_video_de;

    // Latch the falling-edge time of each status LED so the
    // post-test order check is real (the sequential `wait` block
    // below would pass even if the LEDs lit out of order — the
    // test would just block on the slowest one).
    time         t_led0_fall, t_led1_fall, t_led2_fall;
    logic [2:0]  led_d;

    // Ch151 — capture the rising-edge times of pll_locked,
    // core_rst_n, and core_go. The hardware contract is:
    //   pll_locked rises  <  core_rst_n rises  <  core_go fires
    // i.e., the PLL must lock before the reset bridge can release,
    // and core_go can only pulse after reset deasserts.
    time t_pll_locked_rise, t_core_rst_n_rise, t_core_go_rise;
    logic pll_locked_d, core_rst_n_d, core_go_dbg_d;

    initial begin
        core_go_pulse_count = 0;
        errors              = 0;
        saw_video_de        = 1'b0;
        t_led0_fall         = 64'h0;
        t_led1_fall         = 64'h0;
        t_led2_fall         = 64'h0;
        t_pll_locked_rise   = 64'h0;
        t_core_rst_n_rise   = 64'h0;
        t_core_go_rise      = 64'h0;
    end

    // Count rising edges of dut.core_go to verify exactly one pulse.
    logic core_go_d;
    always_ff @(posedge CLOCK2_50) begin
        core_go_d <= dut.core_go;
        if (dut.core_go && !core_go_d)
            core_go_pulse_count <= core_go_pulse_count + 1;
    end

    // Capture rising-edge timestamps for the Ch151 PLL contract.
    always_ff @(posedge CLOCK2_50) begin
        pll_locked_d  <= dut.pll_locked;
        core_rst_n_d  <= dut.core_rst_n;
        core_go_dbg_d <= dut.core_go;
        if (dut.pll_locked && !pll_locked_d && t_pll_locked_rise == 0)
            t_pll_locked_rise <= $time;
        if (dut.core_rst_n && !core_rst_n_d && t_core_rst_n_rise == 0)
            t_core_rst_n_rise <= $time;
        if (dut.core_go && !core_go_dbg_d && t_core_go_rise == 0)
            t_core_go_rise <= $time;
    end

    // Watch for video DE — proves the wrapper's PCRTC scanned
    // out at least one active pixel.
    always_ff @(posedge CLOCK2_50) begin
        if (VIDEO_DE) saw_video_de <= 1'b1;
    end

    // Capture the first falling edge of each status LED.
    always_ff @(posedge CLOCK2_50) begin
        led_d <= LED[2:0];
        if (led_d[0] && !LED[0] && t_led0_fall == 0) t_led0_fall <= $time;
        if (led_d[1] && !LED[1] && t_led1_fall == 0) t_led1_fall <= $time;
        if (led_d[2] && !LED[2] && t_led2_fall == 0) t_led2_fall <= $time;
    end

    // ----------------------------------------------------------------
    // Drive sequence.
    // ----------------------------------------------------------------
    initial begin
        // Reset: KEY[0] LOW (button "pressed").
        KEY = 2'b10;
        // Wait for ninit_done to drop (the inline stub's 16-cycle
        // timer) + a few extra cycles, then release the button.
        repeat (32) @(posedge CLOCK2_50);
        KEY[0] = 1'b1;        // release

        // Ch251 — the production bootlet now LOOPS FOREVER instead of
        // SYSCALL-halting after one DMAC kick. The boot-success
        // indicators changed accordingly:
        //   - LED[1] (dma_done_seen) — DMAC completed at least one
        //     transfer, same as before.
        //   - LED[2] (frame_seen) — PCRTC produced a frame, same.
        //   - LED[0] (core_halt) — **stays unlit** in steady state for
        //     the animated demo. We don't wait on it here.
        // The TB lets the loop run long enough that we observe both
        // LED[1] and LED[2] latch, then samples final state. The
        // EE-loop liveness assertion in CORE_STATUS via the bridge is
        // outside this TB's scope.
        wait (LED[1] == 1'b0);
        $display("[tb_de25_nano] LED[1] (dma_done_seen) lit");

        wait (LED[2] == 1'b0);
        $display("[tb_de25_nano] LED[2] (frame_seen) lit");

        // Settle a few more cycles, then check status / counters.
        repeat (50) @(posedge CLOCK2_50);

        // Exactly one core_go pulse fired.
        if (core_go_pulse_count != 1) begin
            $error("core_go pulse_count=%0d (expected 1)", core_go_pulse_count);
            errors = errors + 1;
        end

        // Ch251 — LED[1:2] light during the animated demo; LED[0]
        // must STAY UNLIT (core_halt=0, loop running). LED[7:3] are
        // expected OFF (no DS2/HDMI alarms in the sim path, no
        // gamepad buttons held).
        if (LED[0] !== 1'b1) begin
            $error("LED[0]=%b (expected 1=unlit: animated bootlet loops, core_halt=0)", LED[0]);
            errors = errors + 1;
        end
        if (LED[7:3] !== 5'b11111) begin
            $error("LED[7:3]=%b (expected 5'b11111 = all OFF)", LED[7:3]);
            errors = errors + 1;
        end

        // LED[1] and LED[2] must have latched at least once. LED[0]
        // is intentionally not in this check (Ch251: loop runs, never
        // halts — see LED ledger in the runbook).
        if (t_led1_fall == 0 || t_led2_fall == 0) begin
            $error("status LEDs LED[1]/LED[2] did not latch (t_led1=%0t t_led2=%0t)",
                   t_led1_fall, t_led2_fall);
            errors = errors + 1;
        end

        // Ch151 PLL contract: pll_locked rises < core_rst_n rises < core_go.
        if (t_pll_locked_rise == 0) begin
            $error("pll_locked never rose");
            errors = errors + 1;
        end
        if (t_core_rst_n_rise == 0) begin
            $error("core_rst_n never deasserted");
            errors = errors + 1;
        end
        if (t_core_go_rise == 0) begin
            $error("core_go never pulsed");
            errors = errors + 1;
        end
        if (t_pll_locked_rise != 0 && t_core_rst_n_rise != 0
            && t_pll_locked_rise >= t_core_rst_n_rise) begin
            $error("PLL contract: pll_locked (%0t) must rise before core_rst_n (%0t)",
                   t_pll_locked_rise, t_core_rst_n_rise);
            errors = errors + 1;
        end
        if (t_core_rst_n_rise != 0 && t_core_go_rise != 0
            && t_core_rst_n_rise >= t_core_go_rise) begin
            $error("PLL contract: core_rst_n (%0t) must rise before core_go (%0t)",
                   t_core_rst_n_rise, t_core_go_rise);
            errors = errors + 1;
        end

        // Video pipe drove DE at some point.
        if (!saw_video_de) begin
            $error("VIDEO_DE never asserted");
            errors = errors + 1;
        end

        $display("[tb_de25_nano_psmct32_raster_demo_top] LED=0b%b core_go_pulses=%0d saw_video_de=%0b t_led=(%0t,%0t,%0t) t_pll/rstn/go=(%0t,%0t,%0t) errors=%0d",
                 LED, core_go_pulse_count, saw_video_de,
                 t_led0_fall, t_led1_fall, t_led2_fall,
                 t_pll_locked_rise, t_core_rst_n_rise, t_core_go_rise, errors);
        if (errors == 0)
            $display("[tb_de25_nano_psmct32_raster_demo_top] PASS");
        else
            $display("[tb_de25_nano_psmct32_raster_demo_top] FAIL");
        $finish;
    end

    // Watchdog. Ch169 — bumped from 20 ms to 30 ms because the board
    // wrapper now overrides PCRTC timing to VGA 640x480@60Hz; a full
    // frame is H_TOTAL*V_TOTAL = 800*525 = 420k pixels, which at the
    // sim clock (50 MHz, since the sim pass-through PLL stub mirrors
    // CLOCK2_50) takes ~8.4 ms simulated. `frame_seen` is sticky and
    // latches only on end-of-frame, so we need to leave room for that
    // plus the post-frame checks.
    initial begin
        #30000000;
        $error("[tb_de25_nano_psmct32_raster_demo_top] TIMEOUT");
        $finish;
    end

endmodule : tb_de25_nano_psmct32_raster_demo_top
