../src/pulp-platform_riscv-dbg_0.1_0/pulp_riscv_dbg/src/dm_mem.sv Cov: 99.3%
1: /* Copyright 2018 ETH Zurich and University of Bologna.
2: * Copyright and related rights are licensed under the Solderpad Hardware
3: * License, Version 0.51 (the “License”); you may not use this file except in
4: * compliance with the License. You may obtain a copy of the License at
5: * http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
6: * or agreed to in writing, software, hardware and materials distributed under
7: * this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR
8: * CONDITIONS OF ANY KIND, either express or implied. See the License for the
9: * specific language governing permissions and limitations under the License.
10: *
11: * File: dm_mem.sv
12: * Author: Florian Zaruba
13: * Date: 11.7.2018
14: *
15: * Description: Memory module for execution-based debug clients
16: *
17: */
18:
19: module dm_mem #(
20: parameter int unsigned NrHarts = 1,
21: parameter int unsigned BusWidth = 32,
22: parameter logic [NrHarts-1:0] SelectableHarts = {NrHarts{1'b1}}
23: ) (
24: input logic clk_i, // Clock
25: input logic rst_ni, // debug module reset
26:
27: output logic [NrHarts-1:0] debug_req_o,
28: input logic [19:0] hartsel_i,
29: // from Ctrl and Status register
30: input logic [NrHarts-1:0] haltreq_i,
31: input logic [NrHarts-1:0] resumereq_i,
32: input logic clear_resumeack_i,
33:
34: // state bits
35: output logic [NrHarts-1:0] halted_o, // hart acknowledge halt
36: output logic [NrHarts-1:0] resuming_o, // hart is resuming
37:
38: input logic [dm::ProgBufSize-1:0][31:0] progbuf_i, // program buffer to expose
39:
40: input logic [dm::DataCount-1:0][31:0] data_i, // data in
41: output logic [dm::DataCount-1:0][31:0] data_o, // data out
42: output logic data_valid_o, // data out is valid
43: // abstract command interface
44: input logic cmd_valid_i,
45: input dm::command_t cmd_i,
46: output logic cmderror_valid_o,
47: output dm::cmderr_e cmderror_o,
48: output logic cmdbusy_o,
49: // data interface
50:
51: // SRAM interface
52: input logic req_i,
53: input logic we_i,
54: input logic [BusWidth-1:0] addr_i,
55: input logic [BusWidth-1:0] wdata_i,
56: input logic [BusWidth/8-1:0] be_i,
57: output logic [BusWidth-1:0] rdata_o
58: );
59: localparam int unsigned DbgAddressBits = 12;
60: localparam int unsigned HartSelLen = (NrHarts == 1) ? 1 : $clog2(NrHarts);
61: localparam int unsigned NrHartsAligned = 2**HartSelLen;
62: localparam int unsigned MaxAar = (BusWidth == 64) ? 4 : 3;
63:
64: localparam logic [DbgAddressBits-1:0] DataBaseAddr = (dm::DataAddr);
65: localparam logic [DbgAddressBits-1:0] DataEndAddr = (dm::DataAddr + 4*dm::DataCount);
66: localparam logic [DbgAddressBits-1:0] ProgBufBaseAddr = (dm::DataAddr - 4*dm::ProgBufSize);
67: localparam logic [DbgAddressBits-1:0] ProgBufEndAddr = (dm::DataAddr - 1);
68: localparam logic [DbgAddressBits-1:0] AbstractCmdBaseAddr = (ProgBufBaseAddr - 4*10);
69: localparam logic [DbgAddressBits-1:0] AbstractCmdEndAddr = (ProgBufBaseAddr - 1);
70:
71: localparam logic [DbgAddressBits-1:0] WhereToAddr = 'h300;
72: localparam logic [DbgAddressBits-1:0] FlagsBaseAddr = 'h400;
73: localparam logic [DbgAddressBits-1:0] FlagsEndAddr = 'h7FF;
74:
75: localparam logic [DbgAddressBits-1:0] HaltedAddr = 'h100;
76: localparam logic [DbgAddressBits-1:0] GoingAddr = 'h104;
77: localparam logic [DbgAddressBits-1:0] ResumingAddr = 'h108;
78: localparam logic [DbgAddressBits-1:0] ExceptionAddr = 'h10C;
79:
80: logic [dm::ProgBufSize/2-1:0][63:0] progbuf;
81: logic [7:0][63:0] abstract_cmd;
82: logic [NrHarts-1:0] halted_d, halted_q;
83: logic [NrHarts-1:0] resuming_d, resuming_q;
84: logic resume, go, going;
85:
86: logic exception;
87: logic unsupported_command;
88:
89: logic [63:0] rom_rdata;
90: logic [63:0] rdata_d, rdata_q;
91: logic word_enable32_q;
92:
93: // this is needed to avoid lint warnings related to array indexing
94: // resize hartsel to valid range
95: logic [HartSelLen-1:0] hartsel, wdata_hartsel;
96:
97: assign hartsel = hartsel_i[HartSelLen-1:0];
98: assign wdata_hartsel = wdata_i[HartSelLen-1:0];
99:
100: logic [NrHartsAligned-1:0] resumereq_aligned, haltreq_aligned,
101: halted_d_aligned, halted_q_aligned,
102: halted_aligned, resumereq_wdata_aligned,
103: resuming_d_aligned, resuming_q_aligned;
104:
105: assign resumereq_aligned = NrHartsAligned'(resumereq_i);
106: assign haltreq_aligned = NrHartsAligned'(haltreq_i);
107: assign resumereq_wdata_aligned = NrHartsAligned'(resumereq_i);
108:
109: assign halted_q_aligned = NrHartsAligned'(halted_q);
110: assign halted_d = NrHarts'(halted_d_aligned);
111: assign resuming_q_aligned = NrHartsAligned'(resuming_q);
112: assign resuming_d = NrHarts'(resuming_d_aligned);
113:
114: // distinguish whether we need to forward data from the ROM or the FSM
115: // latch the address for this
116: logic fwd_rom_d, fwd_rom_q;
117: dm::ac_ar_cmd_t ac_ar;
118:
119: // Abstract Command Access Register
120: assign ac_ar = dm::ac_ar_cmd_t'(cmd_i.control);
121: assign debug_req_o = haltreq_i;
122: assign halted_o = halted_q;
123: assign resuming_o = resuming_q;
124:
125: // reshape progbuf
126: assign progbuf = progbuf_i;
127:
128: typedef enum logic [1:0] { Idle, Go, Resume, CmdExecuting } state_e;
129: state_e state_d, state_q;
130:
131: // hart ctrl queue
132: always_comb begin : p_hart_ctrl_queue
133: cmderror_valid_o = 1'b0;
134: cmderror_o = dm::CmdErrNone;
135: state_d = state_q;
136: go = 1'b0;
137: resume = 1'b0;
138: cmdbusy_o = 1'b1;
139:
140: unique case (state_q)
141: Idle: begin
142: cmdbusy_o = 1'b0;
143: if (cmd_valid_i && halted_q_aligned[hartsel] && !unsupported_command) begin
144: // give the go signal
145: state_d = Go;
146: end else if (cmd_valid_i) begin
147: // hart must be halted for all requests
148: cmderror_valid_o = 1'b1;
149: cmderror_o = dm::CmdErrorHaltResume;
150: end
151: // CSRs want to resume, the request is ignored when the hart is
152: // requested to halt or it didn't clear the resuming_q bit before
153: if (resumereq_aligned[hartsel] && !resuming_q_aligned[hartsel] &&
154: !haltreq_aligned[hartsel] && halted_q_aligned[hartsel]) begin
155: state_d = Resume;
156: end
157: end
158:
159: Go: begin
160: // we are already busy here since we scheduled the execution of a program
161: cmdbusy_o = 1'b1;
162: go = 1'b1;
163: // the thread is now executing the command, track its state
164: if (going) begin
165: state_d = CmdExecuting;
166: end
167: end
168:
169: Resume: begin
170: cmdbusy_o = 1'b1;
171: resume = 1'b1;
172: if (resuming_q_aligned[hartsel]) begin
173: state_d = Idle;
174: end
175: end
176:
177: CmdExecuting: begin
178: cmdbusy_o = 1'b1;
179: go = 1'b0;
180: // wait until the hart has halted again
181: if (halted_aligned[hartsel]) begin
182: state_d = Idle;
183: end
184: end
185:
186: default: ;
187: endcase
188:
189: // only signal once that cmd is unsupported so that we can clear cmderr
190: // in subsequent writes to abstractcs
191: if (unsupported_command && cmd_valid_i) begin
192: cmderror_valid_o = 1'b1;
193: cmderror_o = dm::CmdErrNotSupported;
194: end
195:
196: if (exception) begin
197: cmderror_valid_o = 1'b1;
198: cmderror_o = dm::CmdErrorException;
199: end
200: end
201:
202: // word mux for 32bit and 64bit buses
203: logic [63:0] word_mux;
204: assign word_mux = (fwd_rom_q) ? rom_rdata : rdata_q;
205:
206: if (BusWidth == 64) begin : gen_word_mux64
207: assign rdata_o = word_mux;
208: end else begin : gen_word_mux32
209: assign rdata_o = (word_enable32_q) ? word_mux[32 +: 32] : word_mux[0 +: 32];
210: end
211:
212: // read/write logic
213: logic [63:0] data_bits;
214: logic [7:0][7:0] rdata;
215: always_comb begin : p_rw_logic
216:
217: halted_d_aligned = NrHartsAligned'(halted_q);
218: resuming_d_aligned = NrHartsAligned'(resuming_q);
219: rdata_d = rdata_q;
220: // convert the data in bits representation
221: data_bits = data_i;
222: rdata = '0;
223:
224: // write data in csr register
225: data_valid_o = 1'b0;
226: exception = 1'b0;
227: halted_aligned = '0;
228: going = 1'b0;
229:
230: // The resume ack signal is lowered when the resume request is deasserted
231: if (clear_resumeack_i) begin
232: resuming_d_aligned[hartsel] = 1'b0;
233: end
234: // we've got a new request
235: if (req_i) begin
236: // this is a write
237: if (we_i) begin
238: unique case (addr_i[DbgAddressBits-1:0]) inside
239: HaltedAddr: begin
240: halted_aligned[wdata_hartsel] = 1'b1;
241: halted_d_aligned[wdata_hartsel] = 1'b1;
242: end
243: GoingAddr: begin
244: going = 1'b1;
245: end
246: ResumingAddr: begin
247: // clear the halted flag as the hart resumed execution
248: halted_d_aligned[wdata_hartsel] = 1'b0;
249: // set the resuming flag which needs to be cleared by the debugger
250: resuming_d_aligned[wdata_hartsel] = 1'b1;
251: end
252: // an exception occurred during execution
253: ExceptionAddr: exception = 1'b1;
254: // core can write data registers
255: [(dm::DataAddr):DataEndAddr]: begin
256: data_valid_o = 1'b1;
257: for (int i = 0; i < $bits(be_i); i++) begin
258: if (be_i[i]) begin
259: data_bits[i*8+:8] = wdata_i[i*8+:8];
260: end
261: end
262: end
263: default ;
264: endcase
265:
266: // this is a read
267: end else begin
268: unique case (addr_i[DbgAddressBits-1:0]) inside
269: // variable ROM content
270: WhereToAddr: begin
271: // variable jump to abstract cmd, program_buffer or resume
272: if (resumereq_wdata_aligned[wdata_hartsel]) begin
273: rdata_d = {32'b0, dm::jal('0, 21'(dm::ResumeAddress[11:0])-21'(WhereToAddr))};
274: end
275:
276: // there is a command active so jump there
277: if (cmdbusy_o) begin
278: // transfer not set is shortcut to the program buffer if postexec is set
279: // keep this statement narrow to not catch invalid commands
280: if (cmd_i.cmdtype == dm::AccessRegister &&
281: !ac_ar.transfer && ac_ar.postexec) begin
282: rdata_d = {32'b0, dm::jal('0, 21'(ProgBufBaseAddr)-21'(WhereToAddr))};
283: // this is a legit abstract cmd -> execute it
284: end else begin
285: rdata_d = {32'b0, dm::jal('0, 21'(AbstractCmdBaseAddr)-21'(WhereToAddr))};
286: end
287: end
288: end
289:
290: [DataBaseAddr:DataEndAddr]: begin
291: rdata_d = {
292: data_i[$clog2(dm::ProgBufSize)'(addr_i[DbgAddressBits-1:3] -
293: DataBaseAddr[DbgAddressBits-1:3] + 1'b1)],
294: data_i[$clog2(dm::ProgBufSize)'(addr_i[DbgAddressBits-1:3] -
295: DataBaseAddr[DbgAddressBits-1:3])]
296: };
297: end
298:
299: [ProgBufBaseAddr:ProgBufEndAddr]: begin
300: rdata_d = progbuf[$clog2(dm::ProgBufSize)'(addr_i[DbgAddressBits-1:3] -
301: ProgBufBaseAddr[DbgAddressBits-1:3])];
302: end
303:
304: // two slots for abstract command
305: [AbstractCmdBaseAddr:AbstractCmdEndAddr]: begin
306: // return the correct address index
307: rdata_d = abstract_cmd[3'(addr_i[DbgAddressBits-1:3] -
308: AbstractCmdBaseAddr[DbgAddressBits-1:3])];
309: end
310: // harts are polling for flags here
311: [FlagsBaseAddr:FlagsEndAddr]: begin
312: // release the corresponding hart
313: if (({addr_i[DbgAddressBits-1:3], 3'b0} - FlagsBaseAddr[DbgAddressBits-1:0]) ==
314: (DbgAddressBits'(hartsel) & {{(DbgAddressBits-3){1'b1}}, 3'b0})) begin
315: rdata[DbgAddressBits'(hartsel) & DbgAddressBits'(3'b111)] = {6'b0, resume, go};
316: end
317: rdata_d = rdata;
318: end
319: default: ;
320: endcase
321: end
322: end
323:
324: data_o = data_bits;
325: end
326:
327: always_comb begin : p_abstract_cmd_rom
328: // this abstract command is currently unsupported
329: unsupported_command = 1'b0;
330: // default memory
331: // if ac_ar.transfer is not set then we can take a shortcut to the program buffer
332: abstract_cmd[0][31:0] = dm::illegal();
333: // load debug module base address into a0, this is shared among all commands
334: abstract_cmd[0][63:32] = dm::auipc(5'd10, '0);
335: abstract_cmd[1][31:0] = dm::srli(5'd10, 5'd10, 6'd12); // clr lowest 12b -> DM base offset
336: abstract_cmd[1][63:32] = dm::slli(5'd10, 5'd10, 6'd12);
337: abstract_cmd[2][31:0] = dm::nop();
338: abstract_cmd[2][63:32] = dm::nop();
339: abstract_cmd[3][31:0] = dm::nop();
340: abstract_cmd[3][63:32] = dm::nop();
341: abstract_cmd[4][31:0] = dm::csrr(dm::CSR_DSCRATCH1, 5'd10);
342: abstract_cmd[4][63:32] = dm::ebreak();
343: abstract_cmd[7:5] = '0;
344:
345: // this depends on the command being executed
346: unique case (cmd_i.cmdtype)
347: // --------------------
348: // Access Register
349: // --------------------
350: dm::AccessRegister: begin
351: if (32'(ac_ar.aarsize) < MaxAar && ac_ar.transfer && ac_ar.write) begin
352: // store a0 in dscratch1
353: abstract_cmd[0][31:0] = dm::csrw(dm::CSR_DSCRATCH1, 5'd10);
354: // this range is reserved
355: if (ac_ar.regno[15:14] != '0) begin
356: abstract_cmd[0][31:0] = dm::ebreak(); // we leave asap
357: unsupported_command = 1'b1;
358: // A0 access needs to be handled separately, as we use A0 to load
359: // the DM address offset need to access DSCRATCH1 in this case
360: end else if (ac_ar.regno[12] && (!ac_ar.regno[5]) &&
361: (ac_ar.regno[4:0] == 5'd10)) begin
362: // store s0 in dscratch
363: abstract_cmd[2][31:0] = dm::csrw(dm::CSR_DSCRATCH0, 5'd8);
364: // load from data register
365: abstract_cmd[2][63:32] = dm::load(ac_ar.aarsize, 5'd8, 5'd10, dm::DataAddr);
366: // and store it in the corresponding CSR
367: abstract_cmd[3][31:0] = dm::csrw(dm::CSR_DSCRATCH1, 5'd8);
368: // restore s0 again from dscratch
369: abstract_cmd[3][63:32] = dm::csrr(dm::CSR_DSCRATCH0, 5'd8);
370: // GPR/FPR access
371: end else if (ac_ar.regno[12]) begin
372: // determine whether we want to access the floating point register or not
373: if (ac_ar.regno[5]) begin
374: abstract_cmd[2][31:0] =
375: dm::float_load(ac_ar.aarsize, ac_ar.regno[4:0], 5'd10, dm::DataAddr);
376: end else begin
377: abstract_cmd[2][31:0] =
378: dm::load(ac_ar.aarsize, ac_ar.regno[4:0], 5'd10, dm::DataAddr);
379: end
380: // CSR access
381: end else begin
382: // data register to CSR
383: // store s0 in dscratch
384: abstract_cmd[2][31:0] = dm::csrw(dm::CSR_DSCRATCH0, 5'd8);
385: // load from data register
386: abstract_cmd[2][63:32] = dm::load(ac_ar.aarsize, 5'd8, 5'd10, dm::DataAddr);
387: // and store it in the corresponding CSR
388: abstract_cmd[3][31:0] = dm::csrw(dm::csr_reg_t'(ac_ar.regno[11:0]), 5'd8);
389: // restore s0 again from dscratch
390: abstract_cmd[3][63:32] = dm::csrr(dm::CSR_DSCRATCH0, 5'd8);
391: end
392: end else if (32'(ac_ar.aarsize) < MaxAar && ac_ar.transfer && !ac_ar.write) begin
393: // store a0 in dscratch1
394: abstract_cmd[0][31:0] = dm::csrw(dm::CSR_DSCRATCH1, 5'd10);
395: // this range is reserved
396: if (ac_ar.regno[15:14] != '0) begin
397: abstract_cmd[0][31:0] = dm::ebreak(); // we leave asap
398: unsupported_command = 1'b1;
399: // A0 access needs to be handled separately, as we use A0 to load
400: // the DM address offset need to access DSCRATCH1 in this case
401: end else if (ac_ar.regno[12] && (!ac_ar.regno[5]) &&
402: (ac_ar.regno[4:0] == 5'd10)) begin
403: // store s0 in dscratch
404: abstract_cmd[2][31:0] = dm::csrw(dm::CSR_DSCRATCH0, 5'd8);
405: // read value from CSR into s0
406: abstract_cmd[2][63:32] = dm::csrr(dm::CSR_DSCRATCH1, 5'd8);
407: // and store s0 into data section
408: abstract_cmd[3][31:0] = dm::store(ac_ar.aarsize, 5'd8, 5'd10, dm::DataAddr);
409: // restore s0 again from dscratch
410: abstract_cmd[3][63:32] = dm::csrr(dm::CSR_DSCRATCH0, 5'd8);
411: // GPR/FPR access
412: end else if (ac_ar.regno[12]) begin
413: // determine whether we want to access the floating point register or not
414: if (ac_ar.regno[5]) begin
415: abstract_cmd[2][31:0] =
416: dm::float_store(ac_ar.aarsize, ac_ar.regno[4:0], 5'd10, dm::DataAddr);
417: end else begin
418: abstract_cmd[2][31:0] =
419: dm::store(ac_ar.aarsize, ac_ar.regno[4:0], 5'd10, dm::DataAddr);
420: end
421: // CSR access
422: end else begin
423: // CSR register to data
424: // store s0 in dscratch
425: abstract_cmd[2][31:0] = dm::csrw(dm::CSR_DSCRATCH0, 5'd8);
426: // read value from CSR into s0
427: abstract_cmd[2][63:32] = dm::csrr(dm::csr_reg_t'(ac_ar.regno[11:0]), 5'd8);
428: // and store s0 into data section
429: abstract_cmd[3][31:0] = dm::store(ac_ar.aarsize, 5'd8, 5'd10, dm::DataAddr);
430: // restore s0 again from dscratch
431: abstract_cmd[3][63:32] = dm::csrr(dm::CSR_DSCRATCH0, 5'd8);
432: end
433: end else if (32'(ac_ar.aarsize) >= MaxAar || ac_ar.aarpostincrement == 1'b1) begin
434: // this should happend when e.g. ac_ar.aarsize >= MaxAar
435: // Openocd will try to do an access with aarsize=64 bits
436: // first before falling back to 32 bits.
437: abstract_cmd[0][31:0] = dm::ebreak(); // we leave asap
438: unsupported_command = 1'b1;
439: end
440:
441: // Check whether we need to execute the program buffer. When we
442: // get an unsupported command we really should abort instead of
443: // still trying to execute the program buffer, makes it easier
444: // for the debugger to recover
445: if (ac_ar.postexec && !unsupported_command) begin
446: // issue a nop, we will automatically run into the program buffer
447: abstract_cmd[4][63:32] = dm::nop();
448: end
449: end
450: // not supported at the moment
451: // dm::QuickAccess:;
452: // dm::AccessMemory:;
453: default: begin
454: abstract_cmd[0][31:0] = dm::ebreak();
455: unsupported_command = 1'b1;
456: end
457: endcase
458: end
459:
460: logic [63:0] rom_addr;
461: assign rom_addr = 64'(addr_i);
462: debug_rom i_debug_rom (
463: .clk_i,
464: .req_i,
465: .addr_i ( rom_addr ),
466: .rdata_o ( rom_rdata )
467: );
468:
469: // ROM starts at the HaltAddress of the core e.g.: it immediately jumps to
470: // the ROM base address
471: assign fwd_rom_d = logic'(addr_i[DbgAddressBits-1:0] >= dm::HaltAddress[DbgAddressBits-1:0]);
472:
473: always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
474: if (!rst_ni) begin
475: fwd_rom_q <= 1'b0;
476: rdata_q <= '0;
477: state_q <= Idle;
478: word_enable32_q <= 1'b0;
479: end else begin
480: fwd_rom_q <= fwd_rom_d;
481: rdata_q <= rdata_d;
482: state_q <= state_d;
483: word_enable32_q <= addr_i[2];
484: end
485: end
486:
487: always_ff @(posedge clk_i or negedge rst_ni) begin
488: if (!rst_ni) begin
489: halted_q <= 1'b0;
490: resuming_q <= 1'b0;
491: end else begin
492: halted_q <= SelectableHarts & halted_d;
493: resuming_q <= SelectableHarts & resuming_d;
494: end
495: end
496:
497: endmodule : dm_mem
498: