dm_mem.sv 16.4 KB
Newer Older
sakundu committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
/* 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_mem.sv
 * Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch>
 * Date:   11.7.2018
 *
 * Description: Memory module for execution-based debug clients
 *
 */

module dm_mem #(
    parameter int NrHarts     = -1
)(
    input  logic                             clk_i,       // Clock
    input  logic                             rst_ni,      // debug module reset

    output logic [NrHarts-1:0]               debug_req_o,
    input  logic [19:0]                      hartsel_i,
    // from Ctrl and Status register
    input  logic [NrHarts-1:0]               haltreq_i,
    input  logic [NrHarts-1:0]               resumereq_i,

    // state bits
    output logic [NrHarts-1:0]               halted_o,    // hart acknowledge halt
    output logic [NrHarts-1:0]               resuming_o,  // hart is resuming

    input  logic [dm::ProgBufSize-1:0][31:0] progbuf_i,    // program buffer to expose

    input  logic [dm::DataCount-1:0][31:0]   data_i,       // data in
    output logic [dm::DataCount-1:0][31:0]   data_o,       // data out
    output logic                             data_valid_o, // data out is valid
    // abstract command interface
    input  logic                             cmd_valid_i,
    input  dm::command_t                     cmd_i,
    output logic                             cmderror_valid_o,
    output dm::cmderr_t                      cmderror_o,
    output logic                             cmdbusy_o,
    // data interface

    // SRAM interface
    input  logic                             req_i,
    input  logic                             we_i,
    input  logic [63:0]                      addr_i,
    input  logic [63:0]                      wdata_i,
    input  logic [7:0]                       be_i,
    output logic [63:0]                      rdata_o
);

    localparam int HartSelLen = (NrHarts == 1) ? 1 : $clog2(NrHarts);
    localparam DbgAddressBits  = 12;
    localparam logic [DbgAddressBits-1:0] DataBase = (dm::DataAddr);
    localparam logic [DbgAddressBits-1:0] DataEnd = (dm::DataAddr + 4*dm::DataCount);
    localparam logic [DbgAddressBits-1:0] ProgBufBase = (dm::DataAddr - 4*dm::ProgBufSize);
    localparam logic [DbgAddressBits-1:0] ProgBufEnd = (dm::DataAddr - 1);
    localparam logic [DbgAddressBits-1:0] AbstractCmdBase = (ProgBufBase - 4*10);
    localparam logic [DbgAddressBits-1:0] AbstractCmdEnd = (ProgBufBase - 1);
    localparam logic [DbgAddressBits-1:0] WhereTo   = 'h300;
    localparam logic [DbgAddressBits-1:0] FlagsBase = 'h400;
    localparam logic [DbgAddressBits-1:0] FlagsEnd  = 'h7FF;


    localparam logic [DbgAddressBits-1:0] Halted    = 'h100;
    localparam logic [DbgAddressBits-1:0] Going     = 'h104;
    localparam logic [DbgAddressBits-1:0] Resuming  = 'h108;
    localparam logic [DbgAddressBits-1:0] Exception = 'h10C;

    logic [dm::ProgBufSize/2-1:0][63:0]   progbuf;
    logic [4:0][63:0]   abstract_cmd;
    logic [NrHarts-1:0] halted_d, halted_q;
    logic [NrHarts-1:0] resuming_d, resuming_q;
    logic               resume, go, going;

    logic [HartSelLen-1:0] hart_sel;
    logic exception, halted;
    logic unsupported_command;

    logic [63:0] rom_rdata;
    logic [63:0] rdata_d, rdata_q;
    // distinguish whether we need to forward data from the ROM or the FSM
    // latch the address for this
    logic fwd_rom_d, fwd_rom_q;
    dm::ac_ar_cmd_t ac_ar;

    // Abstract Command Access Register
    assign ac_ar       = dm::ac_ar_cmd_t'(cmd_i.control);
    assign hart_sel    = wdata_i[HartSelLen-1:0];
    assign debug_req_o = haltreq_i;
    assign halted_o    = halted_q;
    assign resuming_o  = resuming_q;

    // reshape progbuf
    assign progbuf = progbuf_i;

    enum logic [1:0] { Idle, Go, Resume, CmdExecuting } state_d, state_q;

    // hart ctrl queue
    always_comb begin
        cmderror_valid_o = 1'b0;
        cmderror_o       = dm::CmdErrNone;
        state_d          = state_q;
        go               = 1'b0;
        resume           = 1'b0;
        cmdbusy_o        = 1'b1;

        case (state_q)
            Idle: begin
                cmdbusy_o = 1'b0;
                if (cmd_valid_i && halted_q) begin
                    // give the go signal
                    state_d = Go;
                end else if (cmd_valid_i) begin
                    // hart must be halted for all requests
                    cmderror_valid_o = 1'b1;
                    cmderror_o = dm::CmdErrorHaltResume;
                end
                // CSRs want to resume, the request is ignored when the hart is
                // requested to halt or it didn't clear the resuming_q bit before
                if (resumereq_i && !resuming_q && !haltreq_i && halted_q) begin
                    state_d = Resume;
                end
            end

            Go: begin
                // we are already busy here since we scheduled the execution of a program
                cmdbusy_o = 1'b1;
                go        = 1'b1;
                // the thread is now executing the command, track its state
                if (going)
                    state_d = CmdExecuting;
            end

            Resume: begin
                cmdbusy_o = 1'b1;
                resume = 1'b1;
                if (resuming_o)
                    state_d = Idle;
            end

            CmdExecuting: begin
                cmdbusy_o = 1'b1;
                go        = 1'b0;
                // wait until the hart has halted again
                if (halted) begin
                    state_d = Idle;
                end
            end
        endcase

        if (exception) begin
            cmderror_valid_o = 1'b1;
            cmderror_o = dm::CmdErrorException;
        end

        if (unsupported_command) begin
            cmderror_valid_o = 1'b1;
            cmderror_o = dm::CmdErrNotSupported;
        end
    end

    // read/write logic
    always_comb begin
        automatic logic [63:0] data_bits;

        halted_d     = halted_q;
        resuming_d   = resuming_q;
        rdata_o      = fwd_rom_q ? rom_rdata : rdata_q;
        rdata_d      = rdata_q;
        // convert the data in bits representation
        data_bits    = data_i;
        // write data in csr register
        data_valid_o = 1'b0;
        exception    = 1'b0;
        halted       = 1'b0;
        going        = 1'b0;
        // The resume ack signal is lowered when the resume request is deasserted
        if (resumereq_i == 1'b0) begin
            resuming_d[hart_sel] = 1'b0;
        end
        // we've got a new request
        if (req_i) begin
            // this is a write
            if (we_i) begin
                unique case (addr_i[DbgAddressBits-1:0]) inside
                    Halted: begin
                        halted = 1'b1;
                        halted_d[hart_sel] = 1'b1;
                    end
                    Going: begin
                        going = 1'b1;
                    end
                    Resuming: begin
                        // clear the halted flag as the hart resumed execution
                        halted_d[hart_sel] = 1'b0;
                        // set the resuming flag which needs to be cleared by the debugger
                        resuming_d[hart_sel] = 1'b1;
                    end
                    // an exception occurred during execution
                    Exception: exception = 1'b1;
                    // core can write data registers
                    [(dm::DataAddr):DataEnd]: begin
                        data_valid_o = 1'b1;
                        for (int i = 0; i < $bits(be_i); i++) begin
                            if (be_i[i]) begin
                                data_bits[i*8+:8] = wdata_i[i*8+:8];
                            end
                        end
                    end
                endcase

            // this is a read
            end else begin
                unique case (addr_i[DbgAddressBits-1:0]) inside
                    // variable ROM content
                    WhereTo: begin
                        // variable jump to abstract cmd, program_buffer or resume
                        if (resumereq_i) begin
                            rdata_d = {32'b0, riscv::jal(0, dm::ResumeAddress[11:0]-WhereTo)};
                        end

                        // there is a command active so jump there
                        if (cmdbusy_o) begin
                            // transfer not set is a shortcut to the program buffer
                            if (!ac_ar.transfer) begin
                                rdata_d = {32'b0, riscv::jal(0, ProgBufBase-WhereTo)};
                            // this is a legit abstract cmd -> execute it
                            end else begin
                                rdata_d = {32'b0, riscv::jal(0, AbstractCmdBase-WhereTo)};
                            end
                        end
                    end

                    // TODO(zarubaf) change hard-coded values
                    [DataBase:DataEnd]: begin
                        rdata_d = {data_i[1], data_i[0]};
                    end

                    [ProgBufBase:ProgBufEnd]: begin
                        rdata_d = progbuf[(addr_i[DbgAddressBits-1:3] - ProgBufBase[DbgAddressBits-1:3])];
                    end

                    // two slots for abstract command
                    [AbstractCmdBase:AbstractCmdEnd]: begin
                        // return the correct address index
                        rdata_d = abstract_cmd[(addr_i[DbgAddressBits-1:3] - AbstractCmdBase[DbgAddressBits-1:3])];
                    end
                    // harts are polling for flags here
                    [FlagsBase:FlagsEnd]: begin
                        automatic logic [7:0][7:0] rdata;
                        rdata = '0;
                        // release the corresponding hart
                        if (({addr_i[DbgAddressBits-1:3], 3'b0} - FlagsBase[DbgAddressBits-1:0]) == {hartsel_i[DbgAddressBits-1:3], 3'b0}) begin
                            rdata[hartsel_i[2:0]] = {6'b0, resume, go};
                        end
                        rdata_d = rdata;
                    end
                    default: ;
                endcase
            end
        end

        data_o = data_bits;
    end

    always_comb begin : abstract_cmd_rom
        // this abstract command is currently unsupported
        unsupported_command = 1'b0;
        // default memory
        // if ac_ar.transfer is not set then we can take a shortcut to the program buffer
        abstract_cmd[0][31:0]  = riscv::illegal();
        // load debug module base address into a0, this is shared among all commands
        abstract_cmd[0][63:32] = riscv::auipc(10, 0);
        abstract_cmd[1][31:0]  = riscv::srli(10, 10, 12); // clear lowest 12bit to get base offset of DM
        abstract_cmd[1][63:32] = riscv::slli(10, 10, 12);
        abstract_cmd[2][31:0]  = riscv::nop();
        abstract_cmd[2][63:32] = riscv::nop();
        abstract_cmd[3][31:0]  = riscv::nop();
        abstract_cmd[3][63:32] = riscv::nop();
        abstract_cmd[4][31:0]  = riscv::csrr(riscv::CSR_DSCRATCH1, 10);
        abstract_cmd[4][63:32] = riscv::ebreak();

        // this depends on the command being executed
        unique case (cmd_i.cmdtype)
            // --------------------
            // Access Register
            // --------------------
            dm::AccessRegister: begin
                if (ac_ar.aarsize < 4 && ac_ar.transfer && ac_ar.write) begin
                    // store a0 in dscratch1
                    abstract_cmd[0][31:0] = riscv::csrw(riscv::CSR_DSCRATCH1, 10);
                    // this range is reserved
                    if (ac_ar.regno[15:14] != '0) begin
                        abstract_cmd[0][31:0] = riscv::illegal();
                    // GPR/FPR access
                    end else if (ac_ar.regno[12]) begin
                        // determine whether we want to access the floating point register or not
                        if (ac_ar.regno[5]) begin
                            abstract_cmd[2][31:0] = riscv::float_load(ac_ar.aarsize, ac_ar.regno[4:0], 10, dm::DataAddr);
                        end else begin
                            abstract_cmd[2][31:0] = riscv::load(ac_ar.aarsize, ac_ar.regno[4:0], 10, dm::DataAddr);
                        end
                    // CSR access
                    end else begin
                        // data register to CSR
                        // store s0 in dscratch
                        abstract_cmd[2][31:0]  = riscv::csrw(riscv::CSR_DSCRATCH0, 8);
                        // load from data register
                        abstract_cmd[2][63:32] = riscv::load(ac_ar.aarsize, 8, 10, dm::DataAddr);
                        // and store it in the corresponding CSR
                        abstract_cmd[3][31:0]  = riscv::csrw(riscv::csr_reg_t'(ac_ar.regno[11:0]), 8);
                        // restore s0 again from dscratch
                        abstract_cmd[3][63:32] = riscv::csrr(riscv::CSR_DSCRATCH0, 8);
                    end
                end else if (ac_ar.aarsize < 4 && ac_ar.transfer && !ac_ar.write) begin
                    // store a0 in dscratch1
                    abstract_cmd[0][31:0]  = riscv::csrw(riscv::CSR_DSCRATCH1, 10);
                    // this range is reserved
                    if (ac_ar.regno[15:14] != '0) begin
                        abstract_cmd[0][31:0] = riscv::illegal();
                    // GPR/FPR access
                    end else if (ac_ar.regno[12]) begin
                        // determine whether we want to access the floating point register or not
                        if (ac_ar.regno[5]) begin
                            abstract_cmd[2][31:0] = riscv::float_store(ac_ar.aarsize, ac_ar.regno[4:0], 10, dm::DataAddr);
                        end else begin
                            abstract_cmd[2][31:0] = riscv::store(ac_ar.aarsize, ac_ar.regno[4:0], 10, dm::DataAddr);
                        end
                    // CSR access
                    end else begin
                        // CSR register to data
                        // store s0 in dscratch
                        abstract_cmd[2][31:0]  = riscv::csrw(riscv::CSR_DSCRATCH0, 8);
                        // read value from CSR into s0
                        abstract_cmd[2][63:32] = riscv::csrr(riscv::csr_reg_t'(ac_ar.regno[11:0]), 8);
                        // and store s0 into data section
                        abstract_cmd[3][31:0]  = riscv::store(ac_ar.aarsize, 8, 10, dm::DataAddr);
                        // restore s0 again from dscratch
                        abstract_cmd[3][63:32] = riscv::csrr(riscv::CSR_DSCRATCH0, 8);
                    end
                end

                // check whether we need to execute the program buffer
                if (ac_ar.postexec) begin
                    // issue a nop, we will automatically run into the program buffer
                    abstract_cmd[4][63:32] = riscv::nop();
                end
            end
            // not supported at the moment
            // dm::QuickAccess:;
            // dm::AccessMemory:;
            default: begin
                unsupported_command = 1'b1;
            end
        endcase
    end

    debug_rom i_debug_rom (
        .clk_i,
        .req_i,
        .addr_i,
        .rdata_o (rom_rdata)
    );

    // ROM starts at the HaltAddress of the core e.g.: it immediately jumps to
    // the ROM base address
    assign fwd_rom_d = (addr_i[DbgAddressBits-1:0] >= dm::HaltAddress[DbgAddressBits-1:0]) ? 1'b1 : 1'b0;

    always_ff @(posedge clk_i or negedge rst_ni) begin
        if (~rst_ni) begin
            fwd_rom_q  <= 1'b0;
            rdata_q    <= '0;
            halted_q   <= 1'b0;
            resuming_q <= 1'b0;
            state_q    <= Idle;
        end else begin
            fwd_rom_q  <= fwd_rom_d;
            rdata_q    <= rdata_d;
            halted_q   <= halted_d;
            resuming_q <= resuming_d;
            state_q    <= state_d;
        end
    end

endmodule