// retroDE_ps2 — tb_gs_lpddr_axi_master (Ch318)
//
// Drives a synthetic PSMCT16 tile-flush stream into gs_lpddr_axi_master on the GS
// clock, and an AXI4-256 slave MODEL (a byte memory) with BACKPRESSURE on the
// (genuinely asynchronous) f2sdram clock. Proves:
//   * the GS→AXI CDC path delivers every pixel,
//   * AXI protocol: AWSIZE=5 (32 B), AWBURST=INCR, AWLEN=0, WLAST=1, no 4 KiB cross,
//   * per-byte WSTRB writes the right 16-bit lanes,
//   * backpressure on AW/W/B is honoured (no lost/dup beats),
//   * counters: 16 beats / 16 bursts per tile, 0 bresp errors, 0 FIFO overflow,
//   * readback of the slave memory == the source pattern.

`timescale 1ns/1ps

module tb_gs_lpddr_axi_master;
    localparam int  PITCH = 128;          // 64-px PSMCT16 row
    localparam int  MEM_BYTES = 8192;
    localparam logic [31:0] FB_BASE = 32'h0000_0000;

    // two ASYNCHRONOUS clocks
    logic gs_clk=0;  always #5  gs_clk=~gs_clk;   // 100 MHz
    logic axi_clk=0; always #7  axi_clk=~axi_clk;  // ~71 MHz, async to gs_clk
    logic gs_rst_n, axi_rst_n;
    // Ch352 — control-commit toggle. The module now latches {arm,canary,fb_base} on this edge (axi_clk snapshot).
    // Free-running here (async to both clocks) so the unit TB keeps continuously re-latching the live controls;
    // the faithful one-commit-per-control-write handshake is exercised in the integration TB.
    logic ctrl_commit=0; always #13 ctrl_commit=~ctrl_commit;

    logic        enable, arm, px_emit;
    logic [31:0] fb_base_dut = FB_BASE;   // Ch352 — drivable so the stability test can move the base mid-flight
    logic [31:0] px_addr;
    logic [15:0] px_pix16;
    logic        saw_awvalid, saw_wvalid;   // gate-off witnesses

    logic [31:0] awaddr; logic [7:0] awlen; logic [2:0] awsize; logic [1:0] awburst;
    logic [4:0]  awid;   logic awvalid, awready;
    logic [255:0] wdata; logic [31:0] wstrb; logic wlast, wvalid, wready;
    logic bvalid, bready; logic [1:0] bresp;
    logic [31:0] beats_written, bursts_issued, bresp_err_count, fifo_overflow_count;
    logic        idle;

    gs_lpddr_axi_master #(.FIFO_DEPTH(16)) dut (
        .gs_clk(gs_clk), .gs_rst_n(gs_rst_n), .enable(enable),
        .arm(arm), .canary(1'b0), .fb_base(fb_base_dut), .ctrl_commit(ctrl_commit),
        .px_emit(px_emit), .px_addr(px_addr), .px_pix32({16'd0, px_pix16}), .flush(1'b0),
        .axi_clk(axi_clk), .axi_rst_n(axi_rst_n),
        .awaddr(awaddr), .awlen(awlen), .awsize(awsize), .awburst(awburst), .awid(awid),
        .awvalid(awvalid), .awready(awready),
        .wdata(wdata), .wstrb(wstrb), .wlast(wlast), .wvalid(wvalid), .wready(wready),
        .bvalid(bvalid), .bready(bready), .bresp(bresp),
        .beats_written(beats_written), .bursts_issued(bursts_issued),
        .bresp_err_count(bresp_err_count), .fifo_overflow_count(fifo_overflow_count), .idle(idle)
    );

    // ---- canary-cap proof: an identical master with MAX_BEATS=1 (always-ready slave)
    //      fed the SAME stream must issue EXACTLY one 32-byte beat. ----
    logic        can_awvalid, can_wvalid;
    logic [31:0] can_beats, can_bursts;
    gs_lpddr_axi_master #(.FIFO_DEPTH(16)) dut_canary (
        .gs_clk(gs_clk), .gs_rst_n(gs_rst_n), .enable(enable),
        .arm(arm), .canary(1'b1), .fb_base(32'h0), .ctrl_commit(ctrl_commit),
        .px_emit(px_emit), .px_addr(px_addr), .px_pix32({16'd0, px_pix16}), .flush(1'b0),
        .axi_clk(axi_clk), .axi_rst_n(axi_rst_n),
        .awaddr(), .awlen(), .awsize(), .awburst(), .awid(), .awvalid(can_awvalid), .awready(1'b1),
        .wdata(), .wstrb(), .wlast(), .wvalid(can_wvalid), .wready(1'b1),
        .bvalid(1'b1), .bready(), .bresp(2'b00),
        .beats_written(can_beats), .bursts_issued(can_bursts),
        .bresp_err_count(), .fifo_overflow_count(), .idle()
    );

    // ---- Ch353 PSMCT32 DUT (PIX_BYTES=4) + BACKPRESSURED capturing slave (Codex #3) ----
    logic        px32_emit=0, px32_flush=0;
    logic [31:0] px32_addr=0, px32_data=0;
    logic [255:0] w32_data; logic [31:0] w32_strb, w32_awaddr; logic w32_awvalid, w32_wvalid, w32_bready, w32_drained;
    logic [31:0] beats32, bursts32, ovf32, bresp_err32;
    logic force_stall = 1'b0;              // Codex saturation test — hold AXI fully off to fill the FIFO
    wire aw32_rdy = !force_stall && lfsr[1];   // intermittent AW backpressure (off entirely while stalled)
    wire w32_rdy  = !force_stall && (lfsr[3] | lfsr[5]);
    logic b32_valid; logic [2:0] b32_pending;   // delayed B response
    always_ff @(posedge axi_clk or negedge axi_rst_n) begin
        if (!axi_rst_n) begin b32_valid<=0; b32_pending<=0; end
        else begin
            if (w32_wvalid && w32_rdy) b32_pending <= 3'd3;
            else if (b32_pending != 0)  b32_pending <= b32_pending - 3'd1;
            if (b32_pending == 3'd1)         b32_valid <= 1;
            else if (b32_valid && w32_bready) b32_valid <= 0;
        end
    end
    gs_lpddr_axi_master #(.FIFO_DEPTH(16), .PIX_BYTES(4)) dut32 (
        .gs_clk(gs_clk), .gs_rst_n(gs_rst_n), .enable(enable),
        .arm(1'b1), .canary(1'b0), .fb_base(32'h0), .ctrl_commit(ctrl_commit),
        .px_emit(px32_emit), .px_addr(px32_addr), .px_pix32(px32_data), .flush(px32_flush),
        .axi_clk(axi_clk), .axi_rst_n(axi_rst_n),
        .awaddr(w32_awaddr), .awlen(), .awsize(), .awburst(), .awid(),
        .awvalid(w32_awvalid), .awready(aw32_rdy),
        .wdata(w32_data), .wstrb(w32_strb), .wlast(), .wvalid(w32_wvalid), .wready(w32_rdy),
        .bvalid(b32_valid), .bready(w32_bready), .bresp(2'b00),
        .beats_written(beats32), .bursts_issued(bursts32),
        .bresp_err_count(bresp_err32), .fifo_overflow_count(ovf32), .idle(), .frame_drained(w32_drained)
    );
    logic [7:0] smem32 [0:1023];
    logic [31:0] aw32_latch;
    always_ff @(posedge axi_clk) begin
        if (w32_awvalid && aw32_rdy) aw32_latch <= w32_awaddr;
        if (w32_wvalid && w32_rdy) for (int i=0;i<32;i++) if (w32_strb[i]) begin
            int a32; a32 = aw32_latch + i; if (a32>=0 && a32<1024) smem32[a32] <= w32_data[i*8 +: 8];
        end
    end

    // ---------------- AXI4 slave model (byte memory) + backpressure ----------------
    logic [7:0] smem [0:MEM_BYTES-1];
    int errors; initial errors=0;
    int prot_err; initial prot_err=0;
    logic [31:0] aw_latch;
    // simple LFSR backpressure
    logic [7:0] lfsr;
    always_ff @(posedge axi_clk or negedge axi_rst_n)
        if (!axi_rst_n) lfsr<=8'hA5; else lfsr<={lfsr[6:0], lfsr[7]^lfsr[5]^lfsr[4]^lfsr[3]};
    assign awready = lfsr[0];               // intermittent AW backpressure
    assign wready  = lfsr[2] | lfsr[6];     // intermittent W backpressure

    // AW capture + protocol checks
    always_ff @(posedge axi_clk or negedge axi_rst_n) begin
        if (!axi_rst_n) begin aw_latch<=0; end
        else if (awvalid && awready) begin
            aw_latch <= awaddr;
            if (awsize  !== 3'd5)  begin $error("[axi] AWSIZE=%0d (want 5)", awsize); prot_err++; end
            if (awburst !== 2'b01) begin $error("[axi] AWBURST=%b (want INCR)", awburst); prot_err++; end
            if (awlen   !== 8'd0)  begin $error("[axi] AWLEN=%0d (want 0)", awlen); prot_err++; end
            if (awaddr[4:0] !== 5'd0) begin $error("[axi] AWADDR=%08x not 32B-aligned", awaddr); prot_err++; end
            if ((awaddr[11:0] + 32) > 13'd4096 && (awaddr[11:0]!=12'd4064))
                ; // 32B line never crosses 4KiB (aligned); informational
        end
    end
    // W commit (single beat) — write enabled bytes at aw_latch
    int wa;
    always_ff @(posedge axi_clk) begin
        if (axi_rst_n && wvalid && wready) begin
            if (!wlast) begin $error("[axi] WLAST not set on 1-beat burst"); prot_err++; end
            for (int i=0;i<32;i++)
                if (wstrb[i]) begin
                    wa = (aw_latch - FB_BASE) + i;
                    if (wa>=0 && wa<MEM_BYTES) smem[wa] <= wdata[i*8 +: 8];
                end
        end
    end
    // B response (delayed a couple cycles after each accepted W)
    logic [2:0] b_pending;
    always_ff @(posedge axi_clk or negedge axi_rst_n) begin
        if (!axi_rst_n) begin bvalid<=0; bresp<=0; b_pending<=0; end
        else begin
            if (wvalid && wready) b_pending <= 3'd3;          // schedule B
            else if (b_pending != 0) b_pending <= b_pending - 3'd1;
            if (b_pending == 3'd1) begin bvalid<=1; bresp<=2'b00; end
            else if (bvalid && bready) bvalid<=0;
        end
    end

    function automatic logic [15:0] pat(input int x, input int y);
        pat = 16'((x*13 + y*97 + 5) & 16'hFFFF);
    endfunction
    task automatic emit1(input logic [31:0] a, input logic [15:0] p);
        @(negedge gs_clk); px_emit=1'b1; px_addr=a; px_pix16=p;
    endtask
    task automatic emit32(input logic [31:0] a, input logic [31:0] d);
        @(negedge gs_clk); px32_emit=1'b1; px32_addr=a; px32_data=d; px32_flush=1'b0;
    endtask

    // disarm witness: latch any AXI write activity (must stay 0 while arm=0)
    always_ff @(posedge axi_clk) begin
        if (awvalid) saw_awvalid <= 1'b1;
        if (wvalid)  saw_wvalid  <= 1'b1;
    end

    // Ch352 (Codex) — AMBA hold monitor: once VALID is asserted it must stay high and the payload (AW address /
    // W data+strb) must NOT change until the matching READY. Runs every cycle (the LFSR backpressure naturally
    // creates AWVALID/WVALID stalls), so a config change while AWVALID && !AWREADY — or an arm-deassert mid
    // handshake — would be caught here.
    logic        s_aw_held; logic [31:0]  s_awaddr;
    logic        s_w_held;  logic [255:0] s_wdata;  logic [31:0] s_wstrb;
    always_ff @(posedge axi_clk or negedge axi_rst_n) begin
        if (!axi_rst_n) begin s_aw_held<=1'b0; s_w_held<=1'b0; end
        else begin
            if (s_aw_held) begin
                if (!awvalid)               begin $error("[axi] AWVALID dropped before AWREADY"); prot_err++; end
                else if (awaddr !== s_awaddr) begin $error("[axi] AWADDR moved during AWVALID stall"); prot_err++; end
            end
            s_aw_held <= awvalid && !awready;  s_awaddr <= awaddr;
            if (s_w_held) begin
                if (!wvalid)                begin $error("[axi] WVALID dropped before WREADY"); prot_err++; end
                else if (wdata!==s_wdata || wstrb!==s_wstrb) begin $error("[axi] WDATA/WSTRB moved during WVALID stall"); prot_err++; end
            end
            s_w_held <= wvalid && !wready;  s_wdata <= wdata; s_wstrb <= wstrb;
        end
    end

    int x,y; logic [15:0] got;
    initial begin
        enable=1; arm=0; px_emit=0; px_addr=0; px_pix16=0; gs_rst_n=0; axi_rst_n=0;
        saw_awvalid=0; saw_wvalid=0;
        for (int i=0;i<MEM_BYTES;i++) smem[i]=8'h00;
        repeat(4) @(posedge gs_clk); gs_rst_n=1; axi_rst_n=1; repeat(2) @(posedge gs_clk);
        enable=1; @(posedge gs_clk);

        // ===== DISARMED TEST — arm=0: drive a full tile-row,
        // the master must NOT issue a single AXI write (Linux-safety gate). =====
        for (x=0;x<16;x++) emit1(32'(x*2), pat(x,0));
        @(negedge gs_clk); px_emit=1'b0;
        repeat(400) @(posedge axi_clk);
        if (saw_awvalid || saw_wvalid) begin $error("[axi] GATE: AXI write issued while disarmed"); errors++; end
        if (beats_written !== 0)       begin $error("[axi] GATE: beats=%0d while disarmed", beats_written); errors++; end
        $display("[axi] DISARMED ok: no awvalid/wvalid, beats=%0d (arm=0)", beats_written);

        arm=1; repeat(4) @(posedge gs_clk);    // arm (sync into gs_clk) for the main test

        // drive ONE 16x16 tile at screen origin, tile-local raster order (256 px),
        // contiguous in time (1 px/gs_clk) — exactly the real flush sweep.
        for (y=0;y<16;y++)
            for (x=0;x<16;x++)
                emit1(32'(y*PITCH + x*2), pat(x,y));
        @(negedge gs_clk); px_emit=1'b0;

        // wait for the AXI side to drain the FIFO
        repeat(4000) @(posedge axi_clk);
        if (!idle) begin $error("[axi] not idle after drain"); errors++; end

        // readback the slave memory == source pattern
        for (y=0;y<16;y++)
            for (x=0;x<16;x++) begin
                got = {smem[y*PITCH + x*2 + 1], smem[y*PITCH + x*2]};
                if (got !== pat(x,y)) begin
                    if (errors<10) $error("[axi] (%0d,%0d) addr=%0d got=%04x exp=%04x", x,y,y*PITCH+x*2,got,pat(x,y));
                    errors++;
                end
            end

        if (beats_written !== 32'd16) begin $error("[axi] beats=%0d (want 16)", beats_written); errors++; end
        if (bursts_issued !== 32'd16) begin $error("[axi] bursts=%0d (want 16)", bursts_issued); errors++; end
        if (bresp_err_count !== 0)    begin $error("[axi] bresp_err=%0d", bresp_err_count); errors++; end
        if (fifo_overflow_count !== 0) begin $error("[axi] fifo_overflow=%0d", fifo_overflow_count); errors++; end
        if (prot_err !== 0)           begin $error("[axi] %0d AXI protocol violations", prot_err); errors++; end
        // canary: writes ONLY the offset-0 beat from the same 256-px stream -> exactly 1
        if (can_bursts !== 32'd1)     begin $error("[axi] canary issued %0d bursts (want 1 = offset-0 only)", can_bursts); errors++; end
        else $display("[axi] CANARY ok: canary mode wrote %0d beat (offset-0, 32 bytes)", can_bursts);

        // ===== Ch352 STABILITY TEST (Codex): change arm + fb_base WHILE transactions are in flight (S_AW/S_W
        // under LFSR backpressure). The AMBA hold monitor must see zero violations and every admitted beat must
        // still complete (idle reached). Pre-fix, fb_base fed awaddr combinationally and arm gated AWVALID/WVALID,
        // so this would have moved awaddr mid-AW or stranded a transaction. =====
        begin
            int beats0; beats0 = beats_written;
            arm=1; fb_base_dut=32'h0000_1000; repeat(6) @(posedge gs_clk);   // new base, re-latched via commit
            for (y=0;y<16;y++) for (x=0;x<16;x++) emit1(32'(y*PITCH + x*2), pat(x,y));  // full tile -> 16 real beats
            @(negedge gs_clk); px_emit=1'b0;
            repeat(20) @(posedge axi_clk); arm=1'b0; fb_base_dut=32'h0000_2000;  // perturb arm + base mid-drain
            repeat(20) @(posedge axi_clk); arm=1'b1;                              // re-admit the remaining beats
            repeat(5000) @(posedge axi_clk);
            if (!idle) begin $error("[axi] STABILITY: not idle after perturbed drain"); errors++; end
            if ((beats_written-beats0) !== 32'd16) begin $error("[axi] STABILITY: %0d beats completed (want 16) — stranded?", beats_written-beats0); errors++; end
            if (prot_err !== 0) begin $error("[axi] STABILITY: %0d protocol violations under arm/base perturbation", prot_err); errors++; end
            else $display("[axi] STABILITY ok: %0d beats completed under arm/base perturbation, no AMBA-hold violations", beats_written-beats0);
        end

        // ===== Ch353 PSMCT32 TEST: 8 px -> one FULL beat; 5 px + flush -> one PARTIAL beat =====
        begin
            for (int i=0;i<256;i++) smem32[i]=8'h00;
            // 8 contiguous PSMCT32 px -> lanes 0..7 of beat 0 -> completes naturally (all 32 strb bits)
            for (int k=0;k<8;k++) emit32(32'(k*4), 32'hA000_0000 | 32'(k));
            @(negedge gs_clk); px32_emit=1'b0;
            begin int d=0; while(beats32<1 && d<4000) begin @(posedge axi_clk); d++; end end  // backpressure-tolerant
            if (beats32 !== 32'd1) begin $error("[axi32] 8px: beats=%0d (want 1 full beat)", beats32); errors++; end
            for (int k=0;k<8;k++) begin
                logic [31:0] g; g = {smem32[k*4+3],smem32[k*4+2],smem32[k*4+1],smem32[k*4]};
                if (g !== (32'hA000_0000 | 32'(k))) begin $error("[axi32] beat0 lane%0d got=%08x", k, g); errors++; end
            end
            // 5 px at beat 1 (addr 32..48) -> PARTIAL (lanes 0..4); no auto-flush -> pulse flush
            for (int k=0;k<5;k++) emit32(32'(32 + k*4), 32'hB000_0000 | 32'(k));
            @(negedge gs_clk); px32_emit=1'b0;
            @(negedge gs_clk); px32_flush=1'b1;        // end-of-scene partial-beat flush + EOF marker
            @(negedge gs_clk); px32_flush=1'b0;
            // wait on the ORDERED drain ack (robust under backpressure — set after the last beat's BRESP)
            begin int d=0; while(!w32_drained && d<8000) begin @(posedge axi_clk); d++; end end
            if (!w32_drained)      begin $error("[axi32] frame_drained never asserted under backpressure"); errors++; end
            if (beats32 !== 32'd2) begin $error("[axi32] partial+flush: beats=%0d (want 2)", beats32); errors++; end
            for (int k=0;k<5;k++) begin
                logic [31:0] g; g = {smem32[32+k*4+3],smem32[32+k*4+2],smem32[32+k*4+1],smem32[32+k*4]};
                if (g !== (32'hB000_0000 | 32'(k))) begin $error("[axi32] beat1 lane%0d got=%08x", k, g); errors++; end
            end
            for (int k=5;k<8;k++) begin      // unstrobed lanes must be UNTOUCHED (still 0)
                logic [31:0] g; g = {smem32[32+k*4+3],smem32[32+k*4+2],smem32[32+k*4+1],smem32[32+k*4]};
                if (g !== 32'd0) begin $error("[axi32] beat1 unstrobed lane%0d got=%08x (want 0)", k, g); errors++; end
            end
            if (ovf32 !== 0)       begin $error("[axi32] FIFO overflow=%0d under backpressure", ovf32); errors++; end
            if (bresp_err32 !== 0) begin $error("[axi32] bresp_err=%0d", bresp_err32); errors++; end
            if (errors==0) $display("[axi32] PSMCT32+backpressure ok: full beat + partial-flush survive AW/W/B stalls; ovf=0 bresp_err=0; ordered drain ack");
        end

        // ===== Ch353 (Codex) SATURATION: hold AXI off until the FIFO is FULL, then flush. The partial beat and the
        // EOF marker must NOT be dropped (their state is retained, gated on !fifo_wfull); on release they push in
        // order and frame_drained asserts only after the last BRESP. =====
        begin
            for (int i=0;i<1024;i++) smem32[i]=8'h00;
            force_stall = 1'b1;                         // AXI fully off -> the FIFO saturates as we emit
            for (int bt=0;bt<24;bt++) for (int k=0;k<8;k++) emit32(32'(bt*32 + k*4), 32'hC000_0000 | 32'(bt*8+k));
            @(negedge gs_clk); px32_emit=1'b0;
            for (int k=0;k<5;k++) emit32(32'(30*32 + k*4), 32'hD000_0000 | 32'(k));   // a PARTIAL beat at a distinct line
            @(negedge gs_clk); px32_emit=1'b0;
            @(negedge gs_clk); px32_flush=1'b1; @(negedge gs_clk); px32_flush=1'b0;   // flush while the FIFO is FULL
            repeat(80) @(posedge axi_clk);
            if (w32_drained) begin $error("[axi32-sat] frame_drained asserted while AXI stalled (EOF popped early)"); errors++; end
            force_stall = 1'b0;                         // release -> drain -> the GATED partial + marker finally push
            begin int d=0; while(!w32_drained && d<60000) begin @(posedge axi_clk); d++; end end
            if (!w32_drained) begin $error("[axi32-sat] frame_drained never asserted after release (partial/EOF DROPPED under full FIFO)"); errors++; end
            for (int k=0;k<5;k++) begin                 // the PARTIAL beat must SURVIVE the full-FIFO flush
                logic [31:0] g; g = {smem32[30*32+k*4+3],smem32[30*32+k*4+2],smem32[30*32+k*4+1],smem32[30*32+k*4]};
                if (g !== (32'hD000_0000 | 32'(k))) begin $error("[axi32-sat] partial lane%0d got=%08x (DROPPED under saturation)", k, g); errors++; end
            end
            if (errors==0) $display("[axi32-sat] SATURATION ok: FIFO full at flush; partial+EOF retained until accepted; frame_drained after BRESP");
        end

        // ===== Ch353 (Codex round 3) NEAR-FULL: scene ENDS ON A FULL BEAT, NO partial, flush immediately after the
        // final normal beat. The EOF-marker enqueue must not race the prior normal fifo_wr (registered fifo_wfull) —
        // gated on !fifo_wr && !fifo_wfull. =====
        begin
            for (int i=0;i<1024;i++) smem32[i]=8'h00;
            force_stall = 1'b1;                         // AXI off -> FIFO saturates as we emit
            for (int bt=0;bt<20;bt++) for (int k=0;k<8;k++) emit32(32'(bt*32 + k*4), 32'hE000_0000 | 32'(bt*8+k));
            // flush IMMEDIATELY after the final FULL beat — no partial (has_data=0 once the last beat completes)
            @(negedge gs_clk); px32_emit=1'b0; px32_flush=1'b1; @(negedge gs_clk); px32_flush=1'b0;
            repeat(40) @(posedge axi_clk);
            if (w32_drained) begin $error("[axi32-nf] frame_drained asserted while AXI stalled"); errors++; end
            force_stall = 1'b0;                         // release -> drain -> the EOF marker pushes + pops
            begin int d=0; while(!w32_drained && d<60000) begin @(posedge axi_clk); d++; end end
            if (!w32_drained) begin $error("[axi32-nf] frame_drained never asserted (EOF DROPPED when scene ended on a full beat)"); errors++; end
            if (errors==0) $display("[axi32-nf] NEAR-FULL ok: scene ends on full beat, no partial; EOF survives, frame_drained after BRESP");
        end

        $display("[tb_gs_lpddr_axi_master] beats=%0d bursts=%0d bresp_err=%0d fifo_ovf=%0d prot_err=%0d idle=%0b errors=%0d",
                 beats_written, bursts_issued, bresp_err_count, fifo_overflow_count, prot_err, idle, errors);
        if (errors==0) $display("[tb_gs_lpddr_axi_master] PASS");
        else           $display("[tb_gs_lpddr_axi_master] FAIL");
        $finish;
    end
    initial begin #2000000; $error("[tb_gs_lpddr_axi_master] TIMEOUT"); $finish; end
endmodule : tb_gs_lpddr_axi_master
