/* Copyright 2018 ETH Zurich and University of Bologna.
 * Copyright and related rights are licensed under the Solderpad Hardware
 * License, Version 0.51 (the “License”); you may not use this file except in
 * compliance with the License.  You may obtain a copy of the License at
 * http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
 * or agreed to in writing, software, hardware and materials distributed under
 * this 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.
 *
 * File:   dm_top.sv
 * Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch>
 * Date:   30.6.2018
 *
 * Description: Top-level of debug module (DM). This is an AXI-Slave.
 *              DTM protocol is equal to SiFives debug protocol to leverage
 *              SW infrastructure re-use. As of version 0.13
 */

module dm_top #(
    parameter int NrHarts      = -1,
    parameter int AxiIdWidth   = -1,
    parameter int AxiAddrWidth = -1,
    parameter int AxiDataWidth = -1,
    parameter int AxiUserWidth = -1
) (
    input  logic               clk_i,       // clock
    input  logic               rst_ni,      // asynchronous reset active low, connect PoR here, not the system reset
    input  logic               testmode_i,
    output logic               ndmreset_o,  // non-debug module reset
    output logic               dmactive_o,  // debug module is active
    output logic [NrHarts-1:0] debug_req_o, // async debug request
    input  logic [NrHarts-1:0] unavailable_i, // communicate whether the hart is unavailable (e.g.: power down)

    // bus slave, for an execution based technique
    input  ariane_axi::req_t   axi_s_req_i,
    output ariane_axi::resp_t  axi_s_resp_o,

    // bus master, for system bus accesses
    output ariane_axi::req_t   axi_m_req_o,
    input  ariane_axi::resp_t  axi_m_resp_i,

    // Connection to DTM - compatible to RocketChip Debug Module
    input  logic               dmi_rst_ni,
    input  logic               dmi_req_valid_i,
    output logic               dmi_req_ready_o,
    input  dm::dmi_req_t       dmi_req_i,

    output logic               dmi_resp_valid_o,
    input  logic               dmi_resp_ready_i,
    output dm::dmi_resp_t      dmi_resp_o
);

    // Debug CSRs
    dm::hartinfo_t [NrHarts-1:0]      hartinfo;
    logic [NrHarts-1:0]               halted;
    // logic [NrHarts-1:0]               running;
    logic [NrHarts-1:0]               resumeack;
    logic [NrHarts-1:0]               haltreq;
    logic [NrHarts-1:0]               resumereq;
    logic                             cmd_valid;
    dm::command_t                     cmd;

    logic                             req;
    logic                             we;
    logic [63:0]                      addr;
    logic [7:0]                       be;
    logic [63:0]                      wdata;
    logic [63:0]                      rdata;

    logic                             cmderror_valid;
    dm::cmderr_t                      cmderror;
    logic                             cmdbusy;
    logic [dm::ProgBufSize-1:0][31:0] progbuf;
    logic [dm::DataCount-1:0][31:0]   data_csrs_mem;
    logic [dm::DataCount-1:0][31:0]   data_mem_csrs;
    logic                             data_valid;
    logic [19:0]                      hartsel;
    // System Bus Access Module
    logic [63:0]                      sbaddress_csrs_sba;
    logic [63:0]                      sbaddress_sba_csrs;
    logic                             sbaddress_write_valid;
    logic                             sbreadonaddr;
    logic                             sbautoincrement;
    logic [2:0]                       sbaccess;
    logic                             sbreadondata;
    logic [63:0]                      sbdata_write;
    logic                             sbdata_read_valid;
    logic                             sbdata_write_valid;
    logic [63:0]                      sbdata_read;
    logic                             sbdata_valid;
    logic                             sbbusy;
    logic                             sberror_valid;
    logic [2:0]                       sberror;

    // Debug Ctrl for each hart -> I haven't found a better way to
    // parameterize this
    for (genvar i = 0; i < NrHarts; i++) begin : dm_hart_ctrl
        assign hartinfo[i] = ariane_pkg::DebugHartInfo;
    end

    dm_csrs #(
        .NrHarts(NrHarts)
    ) i_dm_csrs (
        .clk_i                   ( clk_i                 ),
        .rst_ni                  ( rst_ni                ),
        .testmode_i              ( testmode_i            ),
        .dmi_rst_ni,
        .dmi_req_valid_i,
        .dmi_req_ready_o,
        .dmi_req_i,
        .dmi_resp_valid_o,
        .dmi_resp_ready_i,
        .dmi_resp_o,
        .ndmreset_o              ( ndmreset_o            ),
        .dmactive_o              ( dmactive_o            ),
        .hartsel_o               ( hartsel               ),
        .hartinfo_i              ( hartinfo              ),
        .halted_i                ( halted                ),
        .unavailable_i,
        .resumeack_i             ( resumeack             ),
        .haltreq_o               ( haltreq               ),
        .resumereq_o             ( resumereq             ),
        .cmd_valid_o             ( cmd_valid             ),
        .cmd_o                   ( cmd                   ),
        .cmderror_valid_i        ( cmderror_valid        ),
        .cmderror_i              ( cmderror              ),
        .cmdbusy_i               ( cmdbusy               ),
        .progbuf_o               ( progbuf               ),
        .data_i                  ( data_mem_csrs         ),
        .data_valid_i            ( data_valid            ),
        .data_o                  ( data_csrs_mem         ),
        .sbaddress_o             ( sbaddress_csrs_sba    ),
        .sbaddress_i             ( sbaddress_sba_csrs    ),
        .sbaddress_write_valid_o ( sbaddress_write_valid ),
        .sbreadonaddr_o          ( sbreadonaddr          ),
        .sbautoincrement_o       ( sbautoincrement       ),
        .sbaccess_o              ( sbaccess              ),
        .sbreadondata_o          ( sbreadondata          ),
        .sbdata_o                ( sbdata_write          ),
        .sbdata_read_valid_o     ( sbdata_read_valid     ),
        .sbdata_write_valid_o    ( sbdata_write_valid    ),
        .sbdata_i                ( sbdata_read           ),
        .sbdata_valid_i          ( sbdata_valid          ),
        .sbbusy_i                ( sbbusy                ),
        .sberror_valid_i         ( sberror_valid         ),
        .sberror_i               ( sberror               )
    );

    dm_sba i_dm_sba (
        .clk_i                   ( clk_i                 ),
        .rst_ni                  ( rst_ni                ),
        .dmactive_i              ( dmactive_o            ),
        .axi_req_o               ( axi_m_req_o           ),
        .axi_resp_i              ( axi_m_resp_i          ),
        .sbaddress_i             ( sbaddress_csrs_sba    ),
        .sbaddress_o             ( sbaddress_sba_csrs    ),
        .sbaddress_write_valid_i ( sbaddress_write_valid ),
        .sbreadonaddr_i          ( sbreadonaddr          ),
        .sbautoincrement_i       ( sbautoincrement       ),
        .sbaccess_i              ( sbaccess              ),
        .sbreadondata_i          ( sbreadondata          ),
        .sbdata_i                ( sbdata_write          ),
        .sbdata_read_valid_i     ( sbdata_read_valid     ),
        .sbdata_write_valid_i    ( sbdata_write_valid    ),
        .sbdata_o                ( sbdata_read           ),
        .sbdata_valid_o          ( sbdata_valid          ),
        .sbbusy_o                ( sbbusy                ),
        .sberror_valid_o         ( sberror_valid         ),
        .sberror_o               ( sberror               )
    );

    dm_mem #(
        .NrHarts (NrHarts)
    ) i_dm_mem (
        .clk_i                   ( clk_i                 ),
        .rst_ni                  ( rst_ni                ),
        .debug_req_o             ( debug_req_o           ),
        .hartsel_i               ( hartsel               ),
        .haltreq_i               ( haltreq               ),
        .resumereq_i             ( resumereq             ),
        .halted_o                ( halted                ),
        .resuming_o              ( resumeack             ),
        .cmd_valid_i             ( cmd_valid             ),
        .cmd_i                   ( cmd                   ),
        .cmderror_valid_o        ( cmderror_valid        ),
        .cmderror_o              ( cmderror              ),
        .cmdbusy_o               ( cmdbusy               ),
        .progbuf_i               ( progbuf               ),
        .data_i                  ( data_csrs_mem         ),
        .data_o                  ( data_mem_csrs         ),
        .data_valid_o            ( data_valid            ),
        .req_i                   ( req                   ),
        .we_i                    ( we                    ),
        .addr_i                  ( addr                  ),
        .wdata_i                 ( wdata                 ),
        .be_i                    ( be                    ),
        .rdata_o                 ( rdata                 )
    );

    AXI_BUS #(
        .AXI_ID_WIDTH   ( AxiIdWidth   ),
        .AXI_ADDR_WIDTH ( AxiAddrWidth ),
        .AXI_DATA_WIDTH ( AxiDataWidth ),
        .AXI_USER_WIDTH ( AxiUserWidth )
    ) slave();

    axi_slave_connect_rev i_axi_slave_connect_rev (
      .axi_req_i (axi_s_req_i),
      .axi_resp_o(axi_s_resp_o),
      .slave(slave));

    axi2mem #(
        .AXI_ID_WIDTH   ( AxiIdWidth   ),
        .AXI_ADDR_WIDTH ( AxiAddrWidth ),
        .AXI_DATA_WIDTH ( AxiDataWidth ),
        .AXI_USER_WIDTH ( AxiUserWidth )
    ) i_axi2mem (
        .clk_i      ( clk_i    ),
        .rst_ni     ( rst_ni   ),
        .slave      ( slave    ),
        .req_o      ( req      ),
        .we_o       ( we       ),
        .addr_o     ( addr     ),
        .be_o       ( be       ),
        .data_o     ( wdata    ),
        .data_i     ( rdata    )
    );

endmodule