// Copyright © 2019-2023
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

`ifndef NOPAE
`include "afu_json_info.vh"
`else
`include "vortex_afu.vh"
`endif
`include "VX_define.vh"

module vortex_afu import ccip_if_pkg::*; import local_mem_cfg_pkg::*; import VX_gpu_pkg::*; #(
    parameter NUM_LOCAL_MEM_BANKS = 2
) (
    // global signals
    input wire clk,
    input wire reset,

    // IF signals between CCI and AFU
    input   t_if_ccip_Rx  cp2af_sRxPort,
    output  t_if_ccip_Tx  af2cp_sTxPort,

    // Avalon signals for local memory access
    output  t_local_mem_data      avs_writedata [NUM_LOCAL_MEM_BANKS],
    input   t_local_mem_data      avs_readdata [NUM_LOCAL_MEM_BANKS],
    output  t_local_mem_addr      avs_address [NUM_LOCAL_MEM_BANKS],
    input   wire                  avs_waitrequest [NUM_LOCAL_MEM_BANKS],
    output  wire                  avs_write [NUM_LOCAL_MEM_BANKS],
    output  wire                  avs_read [NUM_LOCAL_MEM_BANKS],
    output  t_local_mem_byte_mask avs_byteenable [NUM_LOCAL_MEM_BANKS],
    output  t_local_mem_burst_cnt avs_burstcount [NUM_LOCAL_MEM_BANKS],
    input   wire                  avs_readdatavalid [NUM_LOCAL_MEM_BANKS]
);

    localparam LMEM_DATA_WIDTH    = $bits(t_local_mem_data);
    localparam LMEM_DATA_SIZE     = LMEM_DATA_WIDTH / 8;
    localparam LMEM_ADDR_WIDTH    = $bits(t_local_mem_addr);
    localparam LMEM_BURST_CTRW    = $bits(t_local_mem_burst_cnt);

    localparam CCI_DATA_WIDTH     = $bits(t_ccip_clData);
    localparam CCI_DATA_SIZE      = CCI_DATA_WIDTH / 8;
    localparam CCI_ADDR_WIDTH     = $bits(t_ccip_clAddr);

    localparam AVS_RD_QUEUE_SIZE  = 32;
    localparam _VX_MEM_TAG_WIDTH  = `VX_MEM_TAG_WIDTH;
    localparam _AVS_REQ_TAGW_VX   = _VX_MEM_TAG_WIDTH + `CLOG2(LMEM_DATA_WIDTH) - `CLOG2(`VX_MEM_DATA_WIDTH);
    localparam _AVS_REQ_TAGW_VX2  = `MAX(_VX_MEM_TAG_WIDTH, _AVS_REQ_TAGW_VX);
    localparam _AVS_REQ_TAGW_CCI  = CCI_ADDR_WIDTH + `CLOG2(LMEM_DATA_WIDTH) - `CLOG2(CCI_DATA_WIDTH);
    localparam _AVS_REQ_TAGW_CCI2 = `MAX(CCI_ADDR_WIDTH, _AVS_REQ_TAGW_CCI);
    localparam AVS_REQ_TAGW       = `MAX(_AVS_REQ_TAGW_VX2, _AVS_REQ_TAGW_CCI2);

    localparam CCI_RD_WINDOW_SIZE = 8;
    localparam CCI_RW_PENDING_SIZE= 256;

    localparam AFU_ID_L           = 16'h0002;      // AFU ID Lower
    localparam AFU_ID_H           = 16'h0004;      // AFU ID Higher 

    localparam CMD_MEM_READ       = `AFU_IMAGE_CMD_MEM_READ;
    localparam CMD_MEM_WRITE      = `AFU_IMAGE_CMD_MEM_WRITE;
    localparam CMD_DCR_WRITE      = `AFU_IMAGE_CMD_DCR_WRITE;
    localparam CMD_RUN            = `AFU_IMAGE_CMD_RUN;
    localparam CMD_TYPE_WIDTH     = `CLOG2(`AFU_IMAGE_CMD_MAX_VALUE+1);

    localparam MMIO_CMD_TYPE      = `AFU_IMAGE_MMIO_CMD_TYPE; 
    localparam MMIO_CMD_ARG0      = `AFU_IMAGE_MMIO_CMD_ARG0;
    localparam MMIO_CMD_ARG1      = `AFU_IMAGE_MMIO_CMD_ARG1;
    localparam MMIO_CMD_ARG2      = `AFU_IMAGE_MMIO_CMD_ARG2;
    localparam MMIO_STATUS        = `AFU_IMAGE_MMIO_STATUS;

    localparam COUT_TID_WIDTH     = `CLOG2(`VX_MEM_BYTEEN_WIDTH); 
    localparam COUT_QUEUE_DATAW   = COUT_TID_WIDTH + 8;
    localparam COUT_QUEUE_SIZE    = 64; 

    localparam MMIO_DEV_CAPS      = `AFU_IMAGE_MMIO_DEV_CAPS;
    localparam MMIO_ISA_CAPS      = `AFU_IMAGE_MMIO_ISA_CAPS;

    localparam CCI_RD_QUEUE_SIZE  = 2 * CCI_RD_WINDOW_SIZE;
    localparam CCI_RD_QUEUE_TAGW  = `CLOG2(CCI_RD_WINDOW_SIZE);
    localparam CCI_RD_QUEUE_DATAW = CCI_DATA_WIDTH + CCI_ADDR_WIDTH;

    localparam STATE_IDLE         = 0;
    localparam STATE_MEM_WRITE    = 1;
    localparam STATE_MEM_READ     = 2;
    localparam STATE_RUN          = 3;
    localparam STATE_DCR_WRITE    = 4;
    localparam STATE_WIDTH        = `CLOG2(STATE_DCR_WRITE+1);

    wire [127:0] afu_id = `AFU_ACCEL_UUID;

    wire [63:0] dev_caps = {16'b0,
                            8'(`SM_ENABLED ? `SMEM_LOG_SIZE : 0),
                            16'(`NUM_CORES * `NUM_CLUSTERS), 
                            8'(`NUM_WARPS), 
                            8'(`NUM_THREADS), 
                            8'(`IMPLEMENTATION_ID)};

    wire [63:0] isa_caps = {32'(`MISA_EXT), 
                            2'(`CLOG2(`XLEN)-4), 
                            30'(`MISA_STD)};

    reg [STATE_WIDTH-1:0] state;

    // Vortex ports ///////////////////////////////////////////////////////////////

    wire vx_mem_req_valid;
    wire vx_mem_req_rw;
    wire [`VX_MEM_BYTEEN_WIDTH-1:0] vx_mem_req_byteen;
    wire [`VX_MEM_ADDR_WIDTH-1:0]   vx_mem_req_addr;
    wire [`VX_MEM_DATA_WIDTH-1:0]   vx_mem_req_data;
    wire [`VX_MEM_TAG_WIDTH-1:0]    vx_mem_req_tag;
    wire vx_mem_req_ready;

    wire vx_mem_rsp_valid;
    wire [`VX_MEM_DATA_WIDTH-1:0] vx_mem_rsp_data;
    wire [`VX_MEM_TAG_WIDTH-1:0]  vx_mem_rsp_tag;
    wire vx_mem_rsp_ready;

    // CMD variables //////////////////////////////////////////////////////////////

    reg [2:0][63:0] cmd_args;

    t_ccip_clAddr cmd_io_addr;
    assign cmd_io_addr = t_ccip_clAddr'(cmd_args[0]);

    wire [CCI_ADDR_WIDTH-1:0] cmd_mem_addr  = CCI_ADDR_WIDTH'(cmd_args[1]);
    wire [CCI_ADDR_WIDTH-1:0] cmd_data_size = CCI_ADDR_WIDTH'(cmd_args[2]);

    wire [`VX_DCR_ADDR_WIDTH-1:0] cmd_dcr_addr = `VX_DCR_ADDR_WIDTH'(cmd_args[0]);
    wire [`VX_DCR_DATA_WIDTH-1:0] cmd_dcr_data = `VX_DCR_DATA_WIDTH'(cmd_args[1]);

    // MMIO controller ////////////////////////////////////////////////////////////

    t_ccip_c0_ReqMmioHdr mmio_hdr;
    assign mmio_hdr = t_ccip_c0_ReqMmioHdr'(cp2af_sRxPort.c0.hdr);
    `UNUSED_VAR (mmio_hdr)

    `STATIC_ASSERT(($bits(t_ccip_c0_ReqMmioHdr)-$bits(mmio_hdr.address)) == 12, ("Oops!"))

    t_if_ccip_c2_Tx mmio_tx;
    assign af2cp_sTxPort.c2 = mmio_tx;

`ifdef SCOPE

    localparam MMIO_SCOPE_READ  = `AFU_IMAGE_MMIO_SCOPE_READ;
    localparam MMIO_SCOPE_WRITE = `AFU_IMAGE_MMIO_SCOPE_WRITE;

    reg [63:0] cmd_scope_rdata;
    reg [63:0] cmd_scope_wdata;

    reg cmd_scope_reading;
    reg cmd_scope_writing;

    reg  scope_bus_in;
    wire scope_bus_out;
    
    reg [5:0] scope_bus_ctr;

    wire scope_reset = reset;

    always @(posedge clk) begin
        if (reset) begin
            cmd_scope_reading <= 0;
            cmd_scope_writing <= 0;
            scope_bus_in <= 0;
        end else begin
            if (scope_bus_out) begin
                cmd_scope_reading <= 1;
                scope_bus_ctr     <= 63;
            end
            scope_bus_in <= 0;
            if (cp2af_sRxPort.c0.mmioWrValid 
             && (MMIO_SCOPE_WRITE == mmio_hdr.address)) begin                
                cmd_scope_wdata   <= 64'(cp2af_sRxPort.c0.data);
                cmd_scope_writing <= 1;
                scope_bus_ctr     <= 63;
                scope_bus_in      <= 1;
            end
        end
        if (cmd_scope_writing) begin
            scope_bus_in  <= 1'(cmd_scope_wdata >> scope_bus_ctr);
            scope_bus_ctr <= scope_bus_ctr - 1;
            if (scope_bus_ctr == 0) begin
                cmd_scope_writing <= 0;
            end
        end                
        if (cmd_scope_reading) begin
            cmd_scope_rdata <= {cmd_scope_rdata[62:0], scope_bus_out};
            scope_bus_ctr   <= scope_bus_ctr - 1;
            if (scope_bus_ctr == 0) begin
                cmd_scope_reading <= 0;
            end
        end
    end

`endif

    wire [COUT_QUEUE_DATAW-1:0] cout_q_dout;
    wire cout_q_full, cout_q_empty;

`ifdef SIMULATION
`ifndef VERILATOR
    // disable assertions until full reset
    reg [`CLOG2(`RESET_DELAY+1)-1:0] assert_delay_ctr;
    initial begin
        $assertoff;  
    end
    always @(posedge clk) begin
        if (reset) begin
            assert_delay_ctr <= '0;
        end else begin
            assert_delay_ctr <= assert_delay_ctr + $bits(assert_delay_ctr)'(1);
            if (assert_delay_ctr == (`RESET_DELAY-1)) begin
                $asserton; // enable assertions
            end
        end
    end
`endif
`endif

    always @(posedge clk) begin
        if (reset) begin
            mmio_tx.mmioRdValid <= 0;
            mmio_tx.hdr         <= '0;
        end else begin
            mmio_tx.mmioRdValid <= cp2af_sRxPort.c0.mmioRdValid; 
            mmio_tx.hdr.tid     <= mmio_hdr.tid;    
        end
        // serve MMIO write request
        if (cp2af_sRxPort.c0.mmioWrValid) begin
            case (mmio_hdr.address)
            MMIO_CMD_ARG0: begin
                cmd_args[0] <= 64'(cp2af_sRxPort.c0.data);
            `ifdef DBG_TRACE_AFU 
                `TRACE(2, ("%d: MMIO_CMD_ARG0: data=0x%0h\n", $time, 64'(cp2af_sRxPort.c0.data)));
            `endif
            end
            MMIO_CMD_ARG1: begin
                cmd_args[1] <= 64'(cp2af_sRxPort.c0.data);
            `ifdef DBG_TRACE_AFU
                `TRACE(2, ("%d: MMIO_CMD_ARG1: data=0x%0h\n", $time, 64'(cp2af_sRxPort.c0.data)));
            `endif
            end
            MMIO_CMD_ARG2: begin
                cmd_args[2] <= 64'(cp2af_sRxPort.c0.data);
            `ifdef DBG_TRACE_AFU
                `TRACE(2, ("%d: MMIO_CMD_ARG2: data=%0d\n", $time, 64'(cp2af_sRxPort.c0.data)));
            `endif
            end
            MMIO_CMD_TYPE: begin
            `ifdef DBG_TRACE_AFU
                `TRACE(2, ("%d: MMIO_CMD_TYPE: data=%0d\n", $time, 64'(cp2af_sRxPort.c0.data)));
            `endif
            end
            `ifdef SCOPE
            MMIO_SCOPE_WRITE: begin
            `ifdef DBG_TRACE_AFU
                `TRACE(2, ("%d: MMIO_SCOPE_WRITE: data=0x%0h\n", $time, cmd_scope_wdata));
            `endif
            end
            `endif
            default: begin
                `ifdef DBG_TRACE_AFU
                `TRACE(2, ("%d: Unknown MMIO Wr: addr=0x%0h, data=0x%0h\n", $time, mmio_hdr.address, 64'(cp2af_sRxPort.c0.data)));
                `endif
            end
            endcase
        end

        // serve MMIO read requests
        if (cp2af_sRxPort.c0.mmioRdValid) begin
            case (mmio_hdr.address)
            // AFU header
            16'h0000: mmio_tx.data <= {
                4'b0001, // Feature type = AFU
                8'b0,    // reserved
                4'b0,    // afu minor revision = 0
                7'b0,    // reserved
                1'b1,    // end of DFH list = 1 
                24'b0,   // next DFH offset = 0
                4'b0,    // afu major revision = 0
                12'b0    // feature ID = 0
            };
            AFU_ID_L: mmio_tx.data <= afu_id[63:0];   // afu id low
            AFU_ID_H: mmio_tx.data <= afu_id[127:64]; // afu id hi
            16'h0006: mmio_tx.data <= 64'h0; // next AFU
            16'h0008: mmio_tx.data <= 64'h0; // reserved
            MMIO_STATUS: begin
                mmio_tx.data <= 64'({cout_q_dout, !cout_q_empty, 8'(state)});
            `ifdef DBG_TRACE_AFU
                if (state != STATE_WIDTH'(mmio_tx.data)) begin
                    `TRACE(2, ("%d: MMIO_STATUS: addr=0x%0h, state=%0d\n", $time, mmio_hdr.address, state));
                end
            `endif
            end
            `ifdef SCOPE
            MMIO_SCOPE_READ: begin
                mmio_tx.data <= cmd_scope_rdata;
            `ifdef DBG_TRACE_AFU
                `TRACE(2, ("%d: MMIO_SCOPE_READ: data=0x%0h\n", $time, cmd_scope_rdata));
            `endif
            end
            `endif
            MMIO_DEV_CAPS: begin
                mmio_tx.data <= dev_caps;
            `ifdef DBG_TRACE_AFU
                `TRACE(2, ("%d: MMIO_DEV_CAPS: data=0x%0h\n", $time, dev_caps));
            `endif
            end      
            MMIO_ISA_CAPS: begin
                mmio_tx.data <= isa_caps;
            `ifdef DBG_TRACE_AFU
                if (state != STATE_WIDTH'(mmio_tx.data)) begin
                    `TRACE(2, ("%d: MMIO_ISA_CAPS: data=%0d\n", $time, isa_caps));
                end
            `endif
            end
            default: begin
                mmio_tx.data <= 64'h0;
            `ifdef DBG_TRACE_AFU
                `TRACE(2, ("%d: Unknown MMIO Rd: addr=0x%0h\n", $time, mmio_hdr.address));
            `endif
            end
            endcase
        end
    end

    // COMMAND FSM ////////////////////////////////////////////////////////////////

    wire cmd_mem_rd_done;
    reg  cmd_mem_wr_done;

    reg  vx_busy_wait;
    reg  vx_running;
    wire vx_busy;

    reg [`CLOG2(`RESET_DELAY+1)-1:0] vx_reset_ctr;
    always @(posedge clk) begin
        if (state == STATE_RUN) begin
            vx_reset_ctr <= vx_reset_ctr + $bits(vx_reset_ctr)'(1);
        end else begin
            vx_reset_ctr <= '0;
        end
    end

    wire is_mmio_wr_cmd = cp2af_sRxPort.c0.mmioWrValid && (MMIO_CMD_TYPE == mmio_hdr.address);
    wire [CMD_TYPE_WIDTH-1:0] cmd_type = is_mmio_wr_cmd ? 
        CMD_TYPE_WIDTH'(cp2af_sRxPort.c0.data) : CMD_TYPE_WIDTH'(0);

    always @(posedge clk) begin
        if (reset) begin
            state        <= STATE_IDLE;
            vx_busy_wait <= 0;
            vx_running   <= 0; 
        end else begin
            case (state)
            STATE_IDLE: begin             
                case (cmd_type)
                CMD_MEM_READ: begin     
                `ifdef DBG_TRACE_AFU
                    `TRACE(2, ("%d: STATE MEM_READ: ia=0x%0h addr=0x%0h size=%0d\n", $time, cmd_io_addr, cmd_mem_addr, cmd_data_size));
                `endif
                    state <= STATE_MEM_READ;   
                end 
                CMD_MEM_WRITE: begin      
                `ifdef DBG_TRACE_AFU
                    `TRACE(2, ("%d: STATE MEM_WRITE: ia=0x%0h addr=0x%0h size=%0d\n", $time, cmd_io_addr, cmd_mem_addr, cmd_data_size));
                `endif
                    state <= STATE_MEM_WRITE;
                end
                CMD_DCR_WRITE: begin      
                `ifdef DBG_TRACE_AFU
                    `TRACE(2, ("%d: STATE DCR_WRITE: addr=0x%0h data=%0d\n", $time, cmd_dcr_addr, cmd_dcr_data));
                `endif
                    state <= STATE_DCR_WRITE;
                end
                CMD_RUN: begin        
                `ifdef DBG_TRACE_AFU
                    `TRACE(2, ("%d: STATE RUN\n", $time));
                `endif  
                    state <= STATE_RUN;      
                    vx_running <= 0;
                end
                default: begin
                    state <= state;
                end
                endcase
            end
            STATE_MEM_READ: begin
                if (cmd_mem_rd_done) begin
                    state <= STATE_IDLE;
                `ifdef DBG_TRACE_AFU
                    `TRACE(2, ("%d: STATE IDLE\n", $time));
                `endif
                end
            end
            STATE_MEM_WRITE: begin
                if (cmd_mem_wr_done) begin
                    state <= STATE_IDLE;
                `ifdef DBG_TRACE_AFU
                    `TRACE(2, ("%d: STATE IDLE\n", $time));
                `endif
                end
            end
            STATE_DCR_WRITE: begin
                state <= STATE_IDLE;
            `ifdef DBG_TRACE_AFU
                `TRACE(2, ("%d: STATE IDLE\n", $time));
            `endif
            end
            STATE_RUN: begin
                if (vx_running) begin
                    if (vx_busy_wait) begin
                        // wait until the gpu goes busy
                        if (vx_busy) begin
                            vx_busy_wait <= 0;
                        end
                    end else begin
                        // wait until the gpu is not busy
                        if (~vx_busy) begin                            
                            state <= STATE_IDLE;
                        `ifdef DBG_TRACE_AFU
                            `TRACE(2, ("%d: AFU: End execution\n", $time));
                            `TRACE(2, ("%d: STATE IDLE\n", $time));
                        `endif
                        end
                    end
                end else begin
                    // wait until the reset sequence is complete
                    if (vx_reset_ctr == (`RESET_DELAY-1)) begin
                    `ifdef DBG_TRACE_AFU
                        `TRACE(2, ("%d: AFU: Begin execution\n", $time));
                    `endif
                        vx_running   <= 1;
                        vx_busy_wait <= 1;
                    end  
                end        
            end
            default:;
            endcase
        end
    end

    // AVS Controller /////////////////////////////////////////////////////////////

    wire cci_mem_rd_req_valid;
    wire cci_mem_wr_req_valid;
    wire [CCI_RD_QUEUE_DATAW-1:0] cci_rdq_dout;

    wire cci_mem_req_valid;
    wire cci_mem_req_rw;
    wire [CCI_ADDR_WIDTH-1:0] cci_mem_req_addr;
    wire [CCI_DATA_WIDTH-1:0] cci_mem_req_data;
    wire [CCI_ADDR_WIDTH-1:0] cci_mem_req_tag;
    wire cci_mem_req_ready;

    wire cci_mem_rsp_valid;        
    wire [CCI_DATA_WIDTH-1:0] cci_mem_rsp_data;
    wire [CCI_ADDR_WIDTH-1:0] cci_mem_rsp_tag;
    wire cci_mem_rsp_ready;

    //--

    VX_mem_bus_if #(
        .DATA_SIZE  (LMEM_DATA_SIZE),
        .ADDR_WIDTH (LMEM_ADDR_WIDTH),
        .TAG_WIDTH  (AVS_REQ_TAGW)
    ) cci_vx_mem_bus_if[2]();

    VX_mem_adapter #(
        .SRC_DATA_WIDTH (CCI_DATA_WIDTH), 
        .DST_DATA_WIDTH (LMEM_DATA_WIDTH), 
        .SRC_ADDR_WIDTH (CCI_ADDR_WIDTH),  
        .DST_ADDR_WIDTH (LMEM_ADDR_WIDTH),         
        .SRC_TAG_WIDTH  (CCI_ADDR_WIDTH),
        .DST_TAG_WIDTH  (AVS_REQ_TAGW),
        .OUT_REG_REQ    (0),
        .OUT_REG_RSP    (0)
    ) cci_mem_adapter (
        .clk                (clk),
        .reset              (reset),

        .mem_req_valid_in   (cci_mem_req_valid),
        .mem_req_addr_in    (cci_mem_req_addr),
        .mem_req_rw_in      (cci_mem_req_rw),
        .mem_req_byteen_in  ({CCI_DATA_SIZE{1'b1}}),
        .mem_req_data_in    (cci_mem_req_data),
        .mem_req_tag_in     (cci_mem_req_tag), 
        .mem_req_ready_in   (cci_mem_req_ready), 

        .mem_rsp_valid_in   (cci_mem_rsp_valid), 
        .mem_rsp_data_in    (cci_mem_rsp_data), 
        .mem_rsp_tag_in     (cci_mem_rsp_tag), 
        .mem_rsp_ready_in   (cci_mem_rsp_ready),

        .mem_req_valid_out  (cci_vx_mem_bus_if[1].req_valid),
        .mem_req_addr_out   (cci_vx_mem_bus_if[1].req_data.addr),
        .mem_req_rw_out     (cci_vx_mem_bus_if[1].req_data.rw),
        .mem_req_byteen_out (cci_vx_mem_bus_if[1].req_data.byteen),
        .mem_req_data_out   (cci_vx_mem_bus_if[1].req_data.data),
        .mem_req_tag_out    (cci_vx_mem_bus_if[1].req_data.tag),
        .mem_req_ready_out  (cci_vx_mem_bus_if[1].req_ready), 

        .mem_rsp_valid_out  (cci_vx_mem_bus_if[1].rsp_valid), 
        .mem_rsp_data_out   (cci_vx_mem_bus_if[1].rsp_data.data), 
        .mem_rsp_tag_out    (cci_vx_mem_bus_if[1].rsp_data.tag), 
        .mem_rsp_ready_out  (cci_vx_mem_bus_if[1].rsp_ready)
    );

    //--

    wire vx_mem_is_cout;
    wire vx_mem_req_valid_qual;
    wire vx_mem_req_ready_qual;

    assign vx_mem_req_valid_qual = vx_mem_req_valid && ~vx_mem_is_cout;

    VX_mem_adapter #(
        .SRC_DATA_WIDTH (`VX_MEM_DATA_WIDTH),
        .DST_DATA_WIDTH (LMEM_DATA_WIDTH),  
        .SRC_ADDR_WIDTH (`VX_MEM_ADDR_WIDTH),    
        .DST_ADDR_WIDTH (LMEM_ADDR_WIDTH),
        .SRC_TAG_WIDTH  (`VX_MEM_TAG_WIDTH),
        .DST_TAG_WIDTH  (AVS_REQ_TAGW),
        .OUT_REG_REQ    (0),
        .OUT_REG_RSP    (2)
    ) vx_mem_adapter (
        .clk                (clk),
        .reset              (reset),

        .mem_req_valid_in   (vx_mem_req_valid_qual),
        .mem_req_addr_in    (vx_mem_req_addr),
        .mem_req_rw_in      (vx_mem_req_rw),
        .mem_req_byteen_in  (vx_mem_req_byteen),
        .mem_req_data_in    (vx_mem_req_data),
        .mem_req_tag_in     (vx_mem_req_tag), 
        .mem_req_ready_in   (vx_mem_req_ready_qual), 

        .mem_rsp_valid_in   (vx_mem_rsp_valid), 
        .mem_rsp_data_in    (vx_mem_rsp_data), 
        .mem_rsp_tag_in     (vx_mem_rsp_tag), 
        .mem_rsp_ready_in   (vx_mem_rsp_ready),

        .mem_req_valid_out  (cci_vx_mem_bus_if[0].req_valid),
        .mem_req_addr_out   (cci_vx_mem_bus_if[0].req_data.addr),
        .mem_req_rw_out     (cci_vx_mem_bus_if[0].req_data.rw),
        .mem_req_byteen_out (cci_vx_mem_bus_if[0].req_data.byteen),
        .mem_req_data_out   (cci_vx_mem_bus_if[0].req_data.data),
        .mem_req_tag_out    (cci_vx_mem_bus_if[0].req_data.tag),
        .mem_req_ready_out  (cci_vx_mem_bus_if[0].req_ready), 

        .mem_rsp_valid_out  (cci_vx_mem_bus_if[0].rsp_valid), 
        .mem_rsp_data_out   (cci_vx_mem_bus_if[0].rsp_data.data), 
        .mem_rsp_tag_out    (cci_vx_mem_bus_if[0].rsp_data.tag), 
        .mem_rsp_ready_out  (cci_vx_mem_bus_if[0].rsp_ready)
    );

    //--
    VX_mem_bus_if #(
        .DATA_SIZE  (LMEM_DATA_SIZE),
        .ADDR_WIDTH (LMEM_ADDR_WIDTH),
        .TAG_WIDTH  (AVS_REQ_TAGW+1)
    ) mem_bus_if[1]();

    `RESET_RELAY (mem_arb_reset, reset);

    VX_mem_arb #(
        .NUM_INPUTS  (2),
        .DATA_SIZE   (LMEM_DATA_SIZE),
        .ADDR_WIDTH  (LMEM_ADDR_WIDTH),
        .TAG_WIDTH   (AVS_REQ_TAGW),
        .ARBITER     ("P"),
        .OUT_REG_REQ (0),
        .OUT_REG_RSP (0)
    ) mem_arb (
        .clk        (clk),
        .reset      (mem_arb_reset),
        .bus_in_if  (cci_vx_mem_bus_if),
        .bus_out_if (mem_bus_if)
    );

    //--

    `RESET_RELAY (avs_adapter_reset, reset);

    VX_avs_adapter #(
        .DATA_WIDTH    (LMEM_DATA_WIDTH), 
        .ADDR_WIDTH    (LMEM_ADDR_WIDTH),
        .BURST_WIDTH   (LMEM_BURST_CTRW),
        .NUM_BANKS     (NUM_LOCAL_MEM_BANKS),
        .TAG_WIDTH     (AVS_REQ_TAGW + 1),
        .RD_QUEUE_SIZE (AVS_RD_QUEUE_SIZE),
        .OUT_REG_REQ   (2),
        .OUT_REG_RSP   (0)
    ) avs_adapter (
        .clk              (clk),
        .reset            (avs_adapter_reset),

        // Memory request 
        .mem_req_valid    (mem_bus_if[0].req_valid),
        .mem_req_rw       (mem_bus_if[0].req_data.rw),
        .mem_req_byteen   (mem_bus_if[0].req_data.byteen),
        .mem_req_addr     (mem_bus_if[0].req_data.addr),
        .mem_req_data     (mem_bus_if[0].req_data.data),
        .mem_req_tag      (mem_bus_if[0].req_data.tag),
        .mem_req_ready    (mem_bus_if[0].req_ready),

        // Memory response  
        .mem_rsp_valid    (mem_bus_if[0].rsp_valid),
        .mem_rsp_data     (mem_bus_if[0].rsp_data.data),
        .mem_rsp_tag      (mem_bus_if[0].rsp_data.tag),
        .mem_rsp_ready    (mem_bus_if[0].rsp_ready),

        // AVS bus
        .avs_writedata    (avs_writedata),
        .avs_readdata     (avs_readdata),
        .avs_address      (avs_address),
        .avs_waitrequest  (avs_waitrequest),
        .avs_write        (avs_write),
        .avs_read         (avs_read),
        .avs_byteenable   (avs_byteenable),
        .avs_burstcount   (avs_burstcount),
        .avs_readdatavalid(avs_readdatavalid)
    );

    // CCI-P Read Request ///////////////////////////////////////////////////////////

    reg [CCI_ADDR_WIDTH-1:0] cci_mem_wr_req_ctr;
    wire [CCI_ADDR_WIDTH-1:0] cci_mem_wr_req_addr;
    reg [CCI_ADDR_WIDTH-1:0] cci_mem_wr_req_addr_base;

    wire cci_rd_req_fire;
    t_ccip_clAddr cci_rd_req_addr;
    reg cci_rd_req_valid, cci_rd_req_wait;
    reg [CCI_ADDR_WIDTH-1:0] cci_rd_req_ctr;
    wire [CCI_ADDR_WIDTH-1:0] cci_rd_req_ctr_next;
    wire [CCI_RD_QUEUE_TAGW-1:0] cci_rd_req_tag;

    wire [CCI_RD_QUEUE_TAGW-1:0] cci_rd_rsp_tag;
    reg [CCI_RD_QUEUE_TAGW-1:0] cci_rd_rsp_ctr;

    wire cci_rdq_push, cci_rdq_pop;
    wire [CCI_RD_QUEUE_DATAW-1:0] cci_rdq_din;
    wire cci_rdq_empty;

    always @(*) begin
        af2cp_sTxPort.c0.valid       = cci_rd_req_fire;
        af2cp_sTxPort.c0.hdr         = t_ccip_c0_ReqMemHdr'(0);
        af2cp_sTxPort.c0.hdr.address = cci_rd_req_addr;  
        af2cp_sTxPort.c0.hdr.mdata   = t_ccip_mdata'(cci_rd_req_tag);
    end

    wire cci_mem_wr_req_fire = cci_mem_wr_req_valid && cci_mem_req_ready;

    wire cci_rd_rsp_fire = cp2af_sRxPort.c0.rspValid 
                        && (cp2af_sRxPort.c0.hdr.resp_type == eRSP_RDLINE);

    assign cci_rd_req_tag = CCI_RD_QUEUE_TAGW'(cci_rd_req_ctr);
    assign cci_rd_rsp_tag = CCI_RD_QUEUE_TAGW'(cp2af_sRxPort.c0.hdr.mdata);

    assign cci_rdq_push = cci_rd_rsp_fire;
    assign cci_rdq_pop  = cci_mem_wr_req_fire;
    assign cci_rdq_din  = {cp2af_sRxPort.c0.data, cci_mem_wr_req_addr_base + CCI_ADDR_WIDTH'(cci_rd_rsp_tag)};

    wire [`CLOG2(CCI_RD_QUEUE_SIZE+1)-1:0] cci_pending_reads;
    wire cci_pending_reads_full;
    VX_pending_size #( 
        .SIZE (CCI_RD_QUEUE_SIZE)
    ) cci_rd_pending_size (
        .clk   (clk),
        .reset (reset),
        .incr  (cci_rd_req_fire),
        .decr  (cci_rdq_pop),
        .full  (cci_pending_reads_full),
        .size  (cci_pending_reads),
        `UNUSED_PIN (empty)
    );

    `UNUSED_VAR (cci_pending_reads)

    assign cci_rd_req_ctr_next = cci_rd_req_ctr + CCI_ADDR_WIDTH'(cci_rd_req_fire ? 1 : 0);

    assign cci_rd_req_fire = cci_rd_req_valid && !(cci_rd_req_wait || cci_pending_reads_full);

    assign cci_mem_wr_req_valid = !cci_rdq_empty;

    assign cci_mem_wr_req_addr = cci_rdq_dout[CCI_ADDR_WIDTH-1:0];

    // Send read requests to CCI
    always @(posedge clk) begin
        if (reset) begin
            cci_rd_req_valid <= 0;
            cci_rd_req_wait  <= 0;
        end else begin              
            if ((STATE_IDLE == state) 
             && (CMD_MEM_WRITE == cmd_type)) begin
                cci_rd_req_valid <= (cmd_data_size != 0);
                cci_rd_req_wait  <= 0;
            end

            cci_rd_req_valid <= (STATE_MEM_WRITE == state)                       
                             && (cci_rd_req_ctr_next != cmd_data_size)
                             && !cp2af_sRxPort.c0TxAlmFull;    

            if (cci_rd_req_fire 
             && (cci_rd_req_tag == CCI_RD_QUEUE_TAGW'(CCI_RD_WINDOW_SIZE-1))) begin
                cci_rd_req_wait <= 1; // end current request batch
            end

            if (cci_rd_rsp_fire 
             && (cci_rd_rsp_ctr == CCI_RD_QUEUE_TAGW'(CCI_RD_WINDOW_SIZE-1))) begin
                cci_rd_req_wait <= 0; // begin new request batch
            end    
        end

        if ((STATE_IDLE == state) 
         && (CMD_MEM_WRITE == cmd_type)) begin
            cci_rd_req_addr    <= cmd_io_addr;
            cci_rd_req_ctr     <= '0;
            cci_rd_rsp_ctr     <= '0;
            cci_mem_wr_req_ctr <= '0;
            cci_mem_wr_req_addr_base <= cmd_mem_addr;
            cmd_mem_wr_done     <= 0;
        end

        if (cci_rd_req_fire) begin  
            cci_rd_req_addr <= cci_rd_req_addr + 1;
            cci_rd_req_ctr  <= cci_rd_req_ctr + $bits(cci_rd_req_ctr)'(1);
        `ifdef DBG_TRACE_AFU
            `TRACE(2, ("%d: CCI Rd Req: addr=0x%0h, tag=0x%0h, rem=%0d, pending=%0d\n", $time, cci_rd_req_addr, cci_rd_req_tag, (cmd_data_size - cci_rd_req_ctr - 1), cci_pending_reads));
        `endif
        end

        if (cci_rd_rsp_fire) begin
            cci_rd_rsp_ctr <= cci_rd_rsp_ctr + CCI_RD_QUEUE_TAGW'(1);
            if (CCI_RD_QUEUE_TAGW'(cci_rd_rsp_ctr) == CCI_RD_QUEUE_TAGW'(CCI_RD_WINDOW_SIZE-1)) begin
                cci_mem_wr_req_addr_base <= cci_mem_wr_req_addr_base + CCI_ADDR_WIDTH'(CCI_RD_WINDOW_SIZE);
            end
        `ifdef DBG_TRACE_AFU
            `TRACE(2, ("%d: CCI Rd Rsp: idx=%0d, ctr=%0d, data=0x%0h\n", $time, cci_rd_rsp_tag, cci_rd_rsp_ctr, cp2af_sRxPort.c0.data));
        `endif
        end 

        if (cci_rdq_pop) begin
        `ifdef DBG_TRACE_AFU
            `TRACE(2, ("%d: CCI Rd Queue Pop: pending=%0d\n", $time, cci_pending_reads));
        `endif
        end

        if (cci_mem_wr_req_fire) begin    
            cci_mem_wr_req_ctr <= cci_mem_wr_req_ctr + CCI_ADDR_WIDTH'(1);
            if (cci_mem_wr_req_ctr == (cmd_data_size-1)) begin
                cmd_mem_wr_done <= 1;
            end
        end
    end

    `RESET_RELAY (cci_rdq_reset, reset);

    VX_fifo_queue #(
        .DATAW (CCI_RD_QUEUE_DATAW),
        .DEPTH (CCI_RD_QUEUE_SIZE)
    ) cci_rd_req_queue (
        .clk      (clk),
        .reset    (cci_rdq_reset),
        .push     (cci_rdq_push),
        .pop      (cci_rdq_pop),
        .data_in  (cci_rdq_din),
        .data_out (cci_rdq_dout),
        .empty    (cci_rdq_empty),
        `UNUSED_PIN (full),
        `UNUSED_PIN (alm_empty),
        `UNUSED_PIN (alm_full),
        `UNUSED_PIN (size)
    );

`DEBUG_BLOCK(
    reg [CCI_RD_WINDOW_SIZE-1:0] dbg_cci_rd_rsp_mask;
    always @(posedge clk) begin
        if (reset) begin
            dbg_cci_rd_rsp_mask <= '0;
        end else begin
            if (cci_rd_rsp_fire) begin      
                if (cci_rd_rsp_ctr == 0) begin
                    dbg_cci_rd_rsp_mask <= (CCI_RD_WINDOW_SIZE'(1) << cci_rd_rsp_tag);               
                end else begin                
                    assert(!dbg_cci_rd_rsp_mask[cci_rd_rsp_tag]);
                    dbg_cci_rd_rsp_mask[cci_rd_rsp_tag] <= 1;
                end 
            end
        end
    end
)

    // CCI-P Write Request //////////////////////////////////////////////////////////

    reg [CCI_ADDR_WIDTH-1:0] cci_mem_rd_req_ctr;
    reg [CCI_ADDR_WIDTH-1:0] cci_mem_rd_req_addr;
    reg cci_mem_rd_req_done;

    reg [CCI_ADDR_WIDTH-1:0] cci_wr_req_ctr;
    reg           cci_wr_req_fire;
    t_ccip_clAddr cci_wr_req_addr;
    t_ccip_clData cci_wr_req_data;
    reg cci_wr_req_done;

    always @(*) begin
        af2cp_sTxPort.c1.valid       = cci_wr_req_fire;
        af2cp_sTxPort.c1.hdr         = t_ccip_c1_ReqMemHdr'(0);
        af2cp_sTxPort.c1.hdr.sop     = 1; // single line write mode
        af2cp_sTxPort.c1.hdr.address = cci_wr_req_addr;
        af2cp_sTxPort.c1.data        = cci_wr_req_data;  
    end 

    wire cci_mem_rd_req_fire = cci_mem_rd_req_valid && cci_mem_req_ready;
    wire cci_mem_rd_rsp_fire = cci_mem_rsp_valid && cci_mem_rsp_ready;

    wire cci_wr_rsp_fire = (STATE_MEM_READ == state) 
                        && cp2af_sRxPort.c1.rspValid 
                        && (cp2af_sRxPort.c1.hdr.resp_type == eRSP_WRLINE);

    wire [`CLOG2(CCI_RW_PENDING_SIZE+1)-1:0] cci_pending_writes;
    wire cci_pending_writes_empty;
    wire cci_pending_writes_full;

    VX_pending_size #( 
        .SIZE (CCI_RW_PENDING_SIZE)
    ) cci_wr_pending_size (
        .clk   (clk),
        .reset (reset),
        .incr  (cci_mem_rd_rsp_fire),
        .decr  (cci_wr_rsp_fire),
        .empty (cci_pending_writes_empty),
        .full  (cci_pending_writes_full),
        .size  (cci_pending_writes)
    );

    `UNUSED_VAR (cci_pending_writes)

    assign cci_mem_rd_req_valid = (STATE_MEM_READ == state) 
                               && ~cci_mem_rd_req_done;

    assign cci_mem_rsp_ready = ~cp2af_sRxPort.c1TxAlmFull 
                            && ~cci_pending_writes_full;

    assign cmd_mem_rd_done = cci_wr_req_done
                          && cci_pending_writes_empty;

    // Send write requests to CCI
    always @(posedge clk) begin
        if (reset) begin
            cci_wr_req_fire <= 0;
        end else begin
            cci_wr_req_fire <= cci_mem_rd_rsp_fire;
        end
            
        if ((STATE_IDLE == state) 
        &&  (CMD_MEM_READ == cmd_type)) begin
            cci_mem_rd_req_ctr  <= '0;
            cci_mem_rd_req_addr <= cmd_mem_addr;
            cci_mem_rd_req_done <= 0;
            cci_wr_req_ctr      <= cmd_data_size;
            cci_wr_req_done     <= 0;
        end  

        if (cci_mem_rd_req_fire) begin
            cci_mem_rd_req_addr <= cci_mem_rd_req_addr + CCI_ADDR_WIDTH'(1);       
            cci_mem_rd_req_ctr  <= cci_mem_rd_req_ctr + CCI_ADDR_WIDTH'(1);
            if (cci_mem_rd_req_ctr == (cmd_data_size-1)) begin
                cci_mem_rd_req_done <= 1;
            end  
        end

        cci_wr_req_addr <= cmd_io_addr + t_ccip_clAddr'(cci_mem_rsp_tag);
        cci_wr_req_data <= t_ccip_clData'(cci_mem_rsp_data);  

        if (cci_wr_req_fire) begin
            `ASSERT(cci_wr_req_ctr != 0, ("runtime error"));               
            cci_wr_req_ctr <= cci_wr_req_ctr - CCI_ADDR_WIDTH'(1);
            if (cci_wr_req_ctr == CCI_ADDR_WIDTH'(1)) begin
            cci_wr_req_done <= 1;
            end
        `ifdef DBG_TRACE_AFU
            `TRACE(2, ("%d: CCI Wr Req: addr=0x%0h, rem=%0d, pending=%0d, data=0x%0h\n", $time, cci_wr_req_addr, (cci_wr_req_ctr - 1), cci_pending_writes, af2cp_sTxPort.c1.data));
        `endif
        end

        if (cci_wr_rsp_fire) begin 
        `ifdef DBG_TRACE_AFU     
            `TRACE(2, ("%d: CCI Wr Rsp: pending=%0d\n", $time, cci_pending_writes));  
        `endif    
        end
    end

    //--

    assign cci_mem_req_rw = state[0];
    `STATIC_ASSERT(STATE_MEM_WRITE == 1, ("invalid value")); // 01
    `STATIC_ASSERT(STATE_MEM_READ  == 2, ("invalid value")); // 10

    assign cci_mem_req_valid = cci_mem_req_rw ? cci_mem_wr_req_valid : cci_mem_rd_req_valid;
    assign cci_mem_req_addr  = cci_mem_req_rw ? cci_mem_wr_req_addr : cci_mem_rd_req_addr;
    assign cci_mem_req_data  = cci_rdq_dout[CCI_RD_QUEUE_DATAW-1:CCI_ADDR_WIDTH];
    assign cci_mem_req_tag   = cci_mem_req_rw ? cci_mem_wr_req_ctr : cci_mem_rd_req_ctr;

    // Vortex ///////////////////////////////////////////////////////////////////

    wire                          vx_dcr_wr_valid = (STATE_DCR_WRITE == state);
    wire [`VX_DCR_ADDR_WIDTH-1:0] vx_dcr_wr_addr  = cmd_dcr_addr;
    wire [`VX_DCR_DATA_WIDTH-1:0] vx_dcr_wr_data  = cmd_dcr_data;
    
    `SCOPE_IO_SWITCH (2)

    Vortex vortex (
        `SCOPE_IO_BIND  (1)

        .clk            (clk),
        .reset          (reset || ~vx_running),

        // Memory request 
        .mem_req_valid  (vx_mem_req_valid),
        .mem_req_rw     (vx_mem_req_rw),
        .mem_req_byteen (vx_mem_req_byteen),
        .mem_req_addr   (vx_mem_req_addr),
        .mem_req_data   (vx_mem_req_data),
        .mem_req_tag    (vx_mem_req_tag),
        .mem_req_ready  (vx_mem_req_ready),

        // Memory response  
        .mem_rsp_valid  (vx_mem_rsp_valid),
        .mem_rsp_data   (vx_mem_rsp_data),
        .mem_rsp_tag    (vx_mem_rsp_tag),
        .mem_rsp_ready  (vx_mem_rsp_ready),

        // DCR write request
        .dcr_wr_valid   (vx_dcr_wr_valid),
        .dcr_wr_addr    (vx_dcr_wr_addr),
        .dcr_wr_data    (vx_dcr_wr_data),
        
        // Status
        .busy           (vx_busy)
    );

    // COUT HANDLING //////////////////////////////////////////////////////////////

    wire [COUT_TID_WIDTH-1:0] cout_tid;

    VX_onehot_encoder #(
        .N (`VX_MEM_BYTEEN_WIDTH)
    ) cout_tid_enc (
        .data_in  (vx_mem_req_byteen),
        .data_out (cout_tid),
        `UNUSED_PIN (valid_out)
    );

    wire [`VX_MEM_ADDR_WIDTH-1:0] io_cout_addr_b = `VX_MEM_ADDR_WIDTH'(`IO_COUT_ADDR >> `CLOG2(`MEM_BLOCK_SIZE));

    assign vx_mem_is_cout = (vx_mem_req_addr == io_cout_addr_b);

    assign vx_mem_req_ready = vx_mem_is_cout ? ~cout_q_full : vx_mem_req_ready_qual;

    wire [`VX_MEM_BYTEEN_WIDTH-1:0][7:0] vx_mem_req_data_m = vx_mem_req_data;

    wire [7:0] cout_char = vx_mem_req_data_m[cout_tid];

    wire cout_q_push = vx_mem_req_valid && vx_mem_is_cout && ~cout_q_full;

    wire cout_q_pop = cp2af_sRxPort.c0.mmioRdValid 
                   && (mmio_hdr.address == MMIO_STATUS)
                   && ~cout_q_empty;

    VX_fifo_queue #(
        .DATAW (COUT_QUEUE_DATAW),
        .DEPTH (COUT_QUEUE_SIZE)
    ) cout_queue (
        .clk      (clk),
        .reset    (reset),
        .push     (cout_q_push),
        .pop      (cout_q_pop),
        .data_in  ({cout_tid, cout_char}),
        .data_out (cout_q_dout),
        .empty    (cout_q_empty),
        .full     (cout_q_full),
        `UNUSED_PIN (alm_empty),
        `UNUSED_PIN (alm_full),
        `UNUSED_PIN (size)
    );

    // SCOPE //////////////////////////////////////////////////////////////////////

`ifdef DBG_SCOPE_AFU
`ifdef SCOPE
    wire mem_req_fire = mem_bus_if[0].req_valid && mem_bus_if[0].req_ready;
    wire mem_rsp_fire = mem_bus_if[0].rsp_valid && mem_bus_if[0].rsp_ready;
    wire avs_write_fire = avs_write[0] && ~avs_waitrequest[0];
    wire avs_read_fire = avs_read[0] && ~avs_waitrequest[0];
    wire [$bits(t_local_mem_addr)-1:0] mem_bus_if_addr = mem_bus_if[0].req_data.addr;

    reg [STATE_WIDTH-1:0] state_prev;
    always @(posedge clk) begin
        state_prev <= state;
    end
    wire state_changed = (state != state_prev);

    VX_scope_tap #(
        .SCOPE_ID (0),
        .TRIGGERW (24),
        .PROBEW   (431)
    ) scope_tap (
        .clk(clk),
        .reset(scope_reset_w[0]),
        .start(1'b0),
        .stop(1'b0),
        .triggers({
            reset,
            state_changed,
            mem_req_fire, 
            mem_rsp_fire, 
            avs_write_fire, 
            avs_read_fire, 
            avs_waitrequest[0], 
            avs_readdatavalid[0], 
            cp2af_sRxPort.c0.mmioRdValid, 
            cp2af_sRxPort.c0.mmioWrValid, 
            cp2af_sRxPort.c0.rspValid, 
            cp2af_sRxPort.c1.rspValid, 
            af2cp_sTxPort.c0.valid, 
            af2cp_sTxPort.c1.valid, 
            cp2af_sRxPort.c0TxAlmFull, 
            cp2af_sRxPort.c1TxAlmFull, 
            af2cp_sTxPort.c2.mmioRdValid, 
            cci_wr_req_fire, 
            cci_wr_rsp_fire, 
            cci_rd_req_fire, 
            cci_rd_rsp_fire,
            cci_pending_reads_full, 
            cci_pending_writes_empty,
            cci_pending_writes_full
        }),
        .probes({
            cmd_type,
            state,
            mmio_hdr.address,
            mmio_hdr.length,
            cp2af_sRxPort.c0.hdr.mdata,
            af2cp_sTxPort.c0.hdr.address,
            af2cp_sTxPort.c0.hdr.mdata,
            af2cp_sTxPort.c1.hdr.address,
            avs_address[0],
            avs_byteenable[0],
            avs_burstcount[0],
            cci_mem_rd_req_ctr,
            cci_mem_wr_req_ctr,
            cci_rd_req_ctr,
            cci_rd_rsp_ctr,
            cci_wr_req_ctr,
            mem_bus_if_addr
        }),
        .bus_in(scope_bus_in_w[0]),
        .bus_out(scope_bus_out_w[0])
    );
`endif
`else
    `SCOPE_IO_UNUSED_W(0)
`endif

    ///////////////////////////////////////////////////////////////////////////////

`ifdef DBG_TRACE_AFU
    always @(posedge clk) begin
        for (integer i = 0; i < NUM_LOCAL_MEM_BANKS; ++i) begin
            if (avs_write[i] && ~avs_waitrequest[i]) begin
                `TRACE(2, ("%d: AVS Wr Req [%0d]: addr=0x%0h, byteen=0x%0h, burst=0x%0h, data=0x%0h\n", $time, i, `TO_FULL_ADDR(avs_address[i]), avs_byteenable[i], avs_burstcount[i], avs_writedata[i]));
            end
            if (avs_read[i] && ~avs_waitrequest[i]) begin   
                `TRACE(2, ("%d: AVS Rd Req [%0d]: addr=0x%0h, byteen=0x%0h,  burst=0x%0h\n", $time, i, `TO_FULL_ADDR(avs_address[i]), avs_byteenable[i], avs_burstcount[i]));
            end 
            if (avs_readdatavalid[i]) begin
                `TRACE(2, ("%d: AVS Rd Rsp [%0d]: data=0x%0h\n", $time, i, avs_readdata[i]));
            end
        end
    end
`endif

endmodule
