| From 2cbdb09aca95a2a8a30077826c5596e0f6843917 Mon Sep 17 00:00:00 2001 |
| From: Michael Schaffner <msf@google.com> |
| Date: Thu, 17 Oct 2019 19:02:22 -0700 |
| Subject: [PATCH 2/9] [lint/cleanup] Change indentation from 4 to 2 spaces per |
| tab |
| |
| This helps in reducing the amount of overly long lines, which cause lint warnings. |
| --- |
| src/dm_csrs.sv | 1102 +++++++++++++++++++++---------------------- |
| src/dm_mem.sv | 890 +++++++++++++++++----------------- |
| src/dm_pkg.sv | 730 ++++++++++++++-------------- |
| src/dm_sba.sv | 321 +++++++------ |
| src/dm_top.sv | 418 ++++++++-------- |
| src/dmi_cdc.sv | 72 +-- |
| src/dmi_jtag.sv | 480 +++++++++---------- |
| src/dmi_jtag_tap.sv | 613 ++++++++++++------------ |
| 8 files changed, 2309 insertions(+), 2317 deletions(-) |
| |
| diff --git a/src/dm_csrs.sv b/src/dm_csrs.sv |
| index f23ea9d..808a95d 100644 |
| --- a/src/dm_csrs.sv |
| +++ b/src/dm_csrs.sv |
| @@ -16,591 +16,587 @@ |
| */ |
| |
| module dm_csrs #( |
| - parameter int NrHarts = 1, |
| - parameter int BusWidth = 32, |
| - parameter logic [NrHarts-1:0] SelectableHarts = 1 |
| + parameter int NrHarts = 1, |
| + parameter int BusWidth = 32, |
| + parameter logic [NrHarts-1:0] SelectableHarts = 1 |
| ) ( |
| - input logic clk_i, // Clock |
| - input logic rst_ni, // Asynchronous reset active low |
| - input logic testmode_i, |
| - input logic dmi_rst_ni, // Debug Module Intf reset active-low |
| - input logic dmi_req_valid_i, |
| - output logic dmi_req_ready_o, |
| - input dm::dmi_req_t dmi_req_i, |
| - // every request needs a response one cycle later |
| - output logic dmi_resp_valid_o, |
| - input logic dmi_resp_ready_i, |
| - output dm::dmi_resp_t dmi_resp_o, |
| - // global ctrl |
| - output logic ndmreset_o, // non-debug module reset active-high |
| - output logic dmactive_o, // 1 -> debug-module is active, |
| - // 0 -> synchronous re-set |
| - // hart status |
| - input dm::hartinfo_t [NrHarts-1:0] hartinfo_i, // static hartinfo |
| - input logic [NrHarts-1:0] halted_i, // hart is halted |
| - input logic [NrHarts-1:0] unavailable_i, // e.g.: powered down |
| - input logic [NrHarts-1:0] resumeack_i, // hart acknowledged resume request |
| - // hart control |
| - output logic [19:0] hartsel_o, // hartselect to ctrl module |
| - output logic [NrHarts-1:0] haltreq_o, // request to halt a hart |
| - output logic [NrHarts-1:0] resumereq_o, // request hart to resume |
| - output logic clear_resumeack_o, |
| - |
| - output logic cmd_valid_o, // debugger writing to cmd field |
| - output dm::command_t cmd_o, // abstract command |
| - input logic cmderror_valid_i, // an error occured |
| - input dm::cmderr_e cmderror_i, // this error occured |
| - input logic cmdbusy_i, // cmd is currently busy executing |
| - |
| - output logic [dm::ProgBufSize-1:0][31:0] progbuf_o, // to system bus |
| - output logic [dm::DataCount-1:0][31:0] data_o, |
| - |
| - input logic [dm::DataCount-1:0][31:0] data_i, |
| - input logic data_valid_i, |
| - // system bus access module (SBA) |
| - output logic [BusWidth-1:0] sbaddress_o, |
| - input logic [BusWidth-1:0] sbaddress_i, |
| - output logic sbaddress_write_valid_o, |
| - // control signals in |
| - output logic sbreadonaddr_o, |
| - output logic sbautoincrement_o, |
| - output logic [2:0] sbaccess_o, |
| - // data out |
| - output logic sbreadondata_o, |
| - output logic [BusWidth-1:0] sbdata_o, |
| - output logic sbdata_read_valid_o, |
| - output logic sbdata_write_valid_o, |
| - // read data in |
| - input logic [BusWidth-1:0] sbdata_i, |
| - input logic sbdata_valid_i, |
| - // control signals |
| - input logic sbbusy_i, |
| - input logic sberror_valid_i, // bus error occurred |
| - input logic [2:0] sberror_i // bus error occurred |
| + input logic clk_i, // Clock |
| + input logic rst_ni, // Asynchronous reset active low |
| + input logic testmode_i, |
| + input logic dmi_rst_ni, // Debug Module Intf reset active-low |
| + input logic dmi_req_valid_i, |
| + output logic dmi_req_ready_o, |
| + input dm::dmi_req_t dmi_req_i, |
| + // every request needs a response one cycle later |
| + output logic dmi_resp_valid_o, |
| + input logic dmi_resp_ready_i, |
| + output dm::dmi_resp_t dmi_resp_o, |
| + // global ctrl |
| + output logic ndmreset_o, // non-debug module reset active-high |
| + output logic dmactive_o, // 1 -> debug-module is active, |
| + // 0 -> synchronous re-set |
| + // hart status |
| + input dm::hartinfo_t [NrHarts-1:0] hartinfo_i, // static hartinfo |
| + input logic [NrHarts-1:0] halted_i, // hart is halted |
| + input logic [NrHarts-1:0] unavailable_i, // e.g.: powered down |
| + input logic [NrHarts-1:0] resumeack_i, // hart acknowledged resume request |
| + // hart control |
| + output logic [19:0] hartsel_o, // hartselect to ctrl module |
| + output logic [NrHarts-1:0] haltreq_o, // request to halt a hart |
| + output logic [NrHarts-1:0] resumereq_o, // request hart to resume |
| + output logic clear_resumeack_o, |
| + |
| + output logic cmd_valid_o, // debugger writing to cmd field |
| + output dm::command_t cmd_o, // abstract command |
| + input logic cmderror_valid_i, // an error occured |
| + input dm::cmderr_e cmderror_i, // this error occured |
| + input logic cmdbusy_i, // cmd is currently busy executing |
| + |
| + output logic [dm::ProgBufSize-1:0][31:0] progbuf_o, // to system bus |
| + output logic [dm::DataCount-1:0][31:0] data_o, |
| + |
| + input logic [dm::DataCount-1:0][31:0] data_i, |
| + input logic data_valid_i, |
| + // system bus access module (SBA) |
| + output logic [BusWidth-1:0] sbaddress_o, |
| + input logic [BusWidth-1:0] sbaddress_i, |
| + output logic sbaddress_write_valid_o, |
| + // control signals in |
| + output logic sbreadonaddr_o, |
| + output logic sbautoincrement_o, |
| + output logic [2:0] sbaccess_o, |
| + // data out |
| + output logic sbreadondata_o, |
| + output logic [BusWidth-1:0] sbdata_o, |
| + output logic sbdata_read_valid_o, |
| + output logic sbdata_write_valid_o, |
| + // read data in |
| + input logic [BusWidth-1:0] sbdata_i, |
| + input logic sbdata_valid_i, |
| + // control signals |
| + input logic sbbusy_i, |
| + input logic sberror_valid_i, // bus error occurred |
| + input logic [2:0] sberror_i // bus error occurred |
| ); |
| - // the amount of bits we need to represent all harts |
| - localparam HartSelLen = (NrHarts == 1) ? 1 : $clog2(NrHarts); |
| - dm::dtm_op_e dtm_op; |
| - assign dtm_op = dm::dtm_op_e'(dmi_req_i.op); |
| - |
| - logic resp_queue_full; |
| - logic resp_queue_empty; |
| - logic resp_queue_push; |
| - logic resp_queue_pop; |
| - logic [31:0] resp_queue_data; |
| - |
| - localparam dm::dm_csr_e DataEnd = dm::dm_csr_e'((dm::Data0 + {4'b0, dm::DataCount})); |
| - localparam dm::dm_csr_e ProgBufEnd = dm::dm_csr_e'((dm::ProgBuf0 + {4'b0, dm::ProgBufSize})); |
| - |
| - logic [31:0] haltsum0, haltsum1, haltsum2, haltsum3; |
| - logic [((NrHarts-1)/2**5 + 1) * 32 - 1 : 0] halted; |
| - logic [(NrHarts-1)/2**5:0][31:0] halted_reshaped0; |
| - logic [NrHarts/2**10:0][31:0] halted_reshaped1; |
| - logic [NrHarts/2**15:0][31:0] halted_reshaped2; |
| - logic [(NrHarts/2**10+1)*32-1:0] halted_flat1; |
| - logic [(NrHarts/2**15+1)*32-1:0] halted_flat2; |
| - logic [32-1:0] halted_flat3; |
| - |
| - // haltsum0 |
| - always_comb begin |
| - halted = '0; |
| - halted[NrHarts-1:0] = halted_i; |
| - halted_reshaped0 = halted; |
| - haltsum0 = halted_reshaped0[hartsel_o[19:5]]; |
| + // the amount of bits we need to represent all harts |
| + localparam HartSelLen = (NrHarts == 1) ? 1 : $clog2(NrHarts); |
| + dm::dtm_op_e dtm_op; |
| + assign dtm_op = dm::dtm_op_e'(dmi_req_i.op); |
| + |
| + logic resp_queue_full; |
| + logic resp_queue_empty; |
| + logic resp_queue_push; |
| + logic resp_queue_pop; |
| + logic [31:0] resp_queue_data; |
| + |
| + localparam dm::dm_csr_e DataEnd = dm::dm_csr_e'((dm::Data0 + {4'b0, dm::DataCount})); |
| + localparam dm::dm_csr_e ProgBufEnd = dm::dm_csr_e'((dm::ProgBuf0 + {4'b0, dm::ProgBufSize})); |
| + |
| + logic [31:0] haltsum0, haltsum1, haltsum2, haltsum3; |
| + logic [((NrHarts-1)/2**5 + 1) * 32 - 1 : 0] halted; |
| + logic [(NrHarts-1)/2**5:0][31:0] halted_reshaped0; |
| + logic [NrHarts/2**10:0][31:0] halted_reshaped1; |
| + logic [NrHarts/2**15:0][31:0] halted_reshaped2; |
| + logic [(NrHarts/2**10+1)*32-1:0] halted_flat1; |
| + logic [(NrHarts/2**15+1)*32-1:0] halted_flat2; |
| + logic [32-1:0] halted_flat3; |
| + |
| + // haltsum0 |
| + always_comb begin |
| + halted = '0; |
| + halted[NrHarts-1:0] = halted_i; |
| + halted_reshaped0 = halted; |
| + haltsum0 = halted_reshaped0[hartsel_o[19:5]]; |
| + end |
| + |
| + // haltsum1 |
| + always_comb begin : p_reduction1 |
| + halted_flat1 = '0; |
| + for (int k=0; k<NrHarts/2**5+1; k++) begin |
| + halted_flat1[k] = |halted_reshaped0[k]; |
| end |
| - |
| - // haltsum1 |
| - always_comb begin : p_reduction1 |
| - halted_flat1 = '0; |
| - for (int k=0; k<NrHarts/2**5+1; k++) begin |
| - halted_flat1[k] = |halted_reshaped0[k]; |
| - end |
| - halted_reshaped1 = halted_flat1; |
| - haltsum1 = halted_reshaped1[hartsel_o[19:10]]; |
| + halted_reshaped1 = halted_flat1; |
| + haltsum1 = halted_reshaped1[hartsel_o[19:10]]; |
| + end |
| + // haltsum2 |
| + always_comb begin : p_reduction2 |
| + halted_flat2 = '0; |
| + for (int k=0; k<NrHarts/2**10+1; k++) begin |
| + halted_flat2[k] = |halted_reshaped1[k]; |
| end |
| - // haltsum2 |
| - always_comb begin : p_reduction2 |
| - halted_flat2 = '0; |
| - for (int k=0; k<NrHarts/2**10+1; k++) begin |
| - halted_flat2[k] = |halted_reshaped1[k]; |
| - end |
| - halted_reshaped2 = halted_flat2; |
| - haltsum2 = halted_reshaped2[hartsel_o[19:15]]; |
| + halted_reshaped2 = halted_flat2; |
| + haltsum2 = halted_reshaped2[hartsel_o[19:15]]; |
| + end |
| + // haltsum3 |
| + always_comb begin : p_reduction3 |
| + halted_flat3 = '0; |
| + for (int k=0; k<NrHarts/2**15+1; k++) begin |
| + halted_flat3[k] = |halted_reshaped2[k]; |
| end |
| - // haltsum3 |
| - always_comb begin : p_reduction3 |
| - halted_flat3 = '0; |
| - for (int k=0; k<NrHarts/2**15+1; k++) begin |
| - halted_flat3[k] = |halted_reshaped2[k]; |
| + haltsum3 = halted_flat3; |
| + end |
| + |
| + |
| + dm::dmstatus_t dmstatus; |
| + dm::dmcontrol_t dmcontrol_d, dmcontrol_q; |
| + dm::abstractcs_t abstractcs; |
| + dm::cmderr_e cmderr_d, cmderr_q; |
| + dm::command_t command_d, command_q; |
| + logic cmd_valid_d, cmd_valid_q; |
| + dm::abstractauto_t abstractauto_d, abstractauto_q; |
| + dm::sbcs_t sbcs_d, sbcs_q; |
| + logic [63:0] sbaddr_d, sbaddr_q; |
| + logic [63:0] sbdata_d, sbdata_q; |
| + |
| + logic [NrHarts-1:0] havereset_d, havereset_q; |
| + // program buffer |
| + logic [dm::ProgBufSize-1:0][31:0] progbuf_d, progbuf_q; |
| + // because first data address starts at 0x04 |
| + logic [({3'b0, dm::DataCount} + dm::Data0 - 1):(dm::Data0)][31:0] data_d, data_q; |
| + |
| + logic [HartSelLen-1:0] selected_hart; |
| + |
| + // a successful response returns zero |
| + assign dmi_resp_o.resp = dm::DTM_SUCCESS; |
| + assign dmi_resp_valid_o = ~resp_queue_empty; |
| + assign dmi_req_ready_o = ~resp_queue_full; |
| + assign resp_queue_push = dmi_req_valid_i & dmi_req_ready_o; |
| + // SBA |
| + assign sbautoincrement_o = sbcs_q.sbautoincrement; |
| + assign sbreadonaddr_o = sbcs_q.sbreadonaddr; |
| + assign sbreadondata_o = sbcs_q.sbreadondata; |
| + assign sbaccess_o = sbcs_q.sbaccess; |
| + assign sbdata_o = sbdata_q[BusWidth-1:0]; |
| + assign sbaddress_o = sbaddr_q[BusWidth-1:0]; |
| + |
| + assign hartsel_o = {dmcontrol_q.hartselhi, dmcontrol_q.hartsello}; |
| + |
| + always_comb begin : csr_read_write |
| + // -------------------- |
| + // Static Values (R/O) |
| + // -------------------- |
| + // dmstatus |
| + dmstatus = '0; |
| + dmstatus.version = dm::DbgVersion013; |
| + // no authentication implemented |
| + dmstatus.authenticated = 1'b1; |
| + // we do not support halt-on-reset sequence |
| + dmstatus.hasresethaltreq = 1'b0; |
| + // TODO(zarubaf) things need to change here if we implement the array mask |
| + dmstatus.allhavereset = havereset_q[selected_hart]; |
| + dmstatus.anyhavereset = havereset_q[selected_hart]; |
| + |
| + dmstatus.allresumeack = resumeack_i[selected_hart]; |
| + dmstatus.anyresumeack = resumeack_i[selected_hart]; |
| + |
| + dmstatus.allunavail = unavailable_i[selected_hart]; |
| + dmstatus.anyunavail = unavailable_i[selected_hart]; |
| + |
| + // as soon as we are out of the legal Hart region tell the debugger |
| + // that there are only non-existent harts |
| + dmstatus.allnonexistent = (hartsel_o > (NrHarts - 1)) ? 1'b1 : 1'b0; |
| + dmstatus.anynonexistent = (hartsel_o > (NrHarts - 1)) ? 1'b1 : 1'b0; |
| + |
| + // We are not allowed to be in multiple states at once. This is a to |
| + // make the running/halted and unavailable states exclusive. |
| + dmstatus.allhalted = halted_i[selected_hart] & ~unavailable_i[selected_hart]; |
| + dmstatus.anyhalted = halted_i[selected_hart] & ~unavailable_i[selected_hart]; |
| + |
| + dmstatus.allrunning = ~halted_i[selected_hart] & ~unavailable_i[selected_hart]; |
| + dmstatus.anyrunning = ~halted_i[selected_hart] & ~unavailable_i[selected_hart]; |
| + |
| + // abstractcs |
| + abstractcs = '0; |
| + abstractcs.datacount = dm::DataCount; |
| + abstractcs.progbufsize = dm::ProgBufSize; |
| + abstractcs.busy = cmdbusy_i; |
| + abstractcs.cmderr = cmderr_q; |
| + |
| + // abstractautoexec |
| + abstractauto_d = abstractauto_q; |
| + abstractauto_d.zero0 = '0; |
| + |
| + // default assignments |
| + havereset_d = havereset_q; |
| + dmcontrol_d = dmcontrol_q; |
| + cmderr_d = cmderr_q; |
| + command_d = command_q; |
| + progbuf_d = progbuf_q; |
| + data_d = data_q; |
| + sbcs_d = sbcs_q; |
| + sbaddr_d = sbaddress_i; |
| + sbdata_d = sbdata_q; |
| + |
| + resp_queue_data = 32'b0; |
| + cmd_valid_d = 1'b0; |
| + sbaddress_write_valid_o = 1'b0; |
| + sbdata_read_valid_o = 1'b0; |
| + sbdata_write_valid_o = 1'b0; |
| + clear_resumeack_o = 1'b0; |
| + |
| + // reads |
| + if (dmi_req_ready_o && dmi_req_valid_i && dtm_op == dm::DTM_READ) begin |
| + unique case ({1'b0, dmi_req_i.addr}) inside |
| + [(dm::Data0):DataEnd]: begin |
| + if (dm::DataCount > 0) begin |
| + resp_queue_data = data_q[dmi_req_i.addr[4:0]]; |
| + end |
| + if (!cmdbusy_i) begin |
| + // check whether we need to re-execute the command (just give a cmd_valid) |
| + cmd_valid_d = abstractauto_q.autoexecdata[dmi_req_i.addr[3:0] - |
| + int'(dm::Data0)]; |
| + end |
| + end |
| + dm::DMControl: resp_queue_data = dmcontrol_q; |
| + dm::DMStatus: resp_queue_data = dmstatus; |
| + dm::Hartinfo: resp_queue_data = hartinfo_i[selected_hart]; |
| + dm::AbstractCS: resp_queue_data = abstractcs; |
| + dm::AbstractAuto: resp_queue_data = abstractauto_q; |
| + // command is read-only |
| + dm::Command: resp_queue_data = '0; |
| + [(dm::ProgBuf0):ProgBufEnd]: begin |
| + resp_queue_data = progbuf_q[dmi_req_i.addr[4:0]]; |
| + if (!cmdbusy_i) begin |
| + // check whether we need to re-execute the command (just give a cmd_valid) |
| + // TODO(zarubaf): check if offset is correct: without it this may assign Xes |
| + cmd_valid_d = abstractauto_q.autoexecprogbuf[dmi_req_i.addr[3:0]+16]; |
| + end |
| + end |
| + dm::HaltSum0: resp_queue_data = haltsum0; |
| + dm::HaltSum1: resp_queue_data = haltsum1; |
| + dm::HaltSum2: resp_queue_data = haltsum2; |
| + dm::HaltSum3: resp_queue_data = haltsum3; |
| + dm::SBCS: begin |
| + resp_queue_data = sbcs_q; |
| + end |
| + dm::SBAddress0: begin |
| + // access while the SBA was busy |
| + if (sbbusy_i) begin |
| + sbcs_d.sbbusyerror = 1'b1; |
| + end else begin |
| + resp_queue_data = sbaddr_q[31:0]; |
| + end |
| + end |
| + dm::SBAddress1: begin |
| + // access while the SBA was busy |
| + if (sbbusy_i) begin |
| + sbcs_d.sbbusyerror = 1'b1; |
| + end else begin |
| + resp_queue_data = sbaddr_q[63:32]; |
| + end |
| + end |
| + dm::SBData0: begin |
| + // access while the SBA was busy |
| + if (sbbusy_i) begin |
| + sbcs_d.sbbusyerror = 1'b1; |
| + end else begin |
| + sbdata_read_valid_o = (sbcs_q.sberror == '0); |
| + resp_queue_data = sbdata_q[31:0]; |
| + end |
| + end |
| + dm::SBData1: begin |
| + // access while the SBA was busy |
| + if (sbbusy_i) begin |
| + sbcs_d.sbbusyerror = 1'b1; |
| + end else begin |
| + resp_queue_data = sbdata_q[63:32]; |
| + end |
| end |
| - haltsum3 = halted_flat3; |
| + default:; |
| + endcase |
| end |
| |
| - |
| - dm::dmstatus_t dmstatus; |
| - dm::dmcontrol_t dmcontrol_d, dmcontrol_q; |
| - dm::abstractcs_t abstractcs; |
| - dm::cmderr_e cmderr_d, cmderr_q; |
| - dm::command_t command_d, command_q; |
| - logic cmd_valid_d, cmd_valid_q; |
| - dm::abstractauto_t abstractauto_d, abstractauto_q; |
| - dm::sbcs_t sbcs_d, sbcs_q; |
| - logic [63:0] sbaddr_d, sbaddr_q; |
| - logic [63:0] sbdata_d, sbdata_q; |
| - |
| - logic [NrHarts-1:0] havereset_d, havereset_q; |
| - // program buffer |
| - logic [dm::ProgBufSize-1:0][31:0] progbuf_d, progbuf_q; |
| - // because first data address starts at 0x04 |
| - logic [({3'b0, dm::DataCount} + dm::Data0 - 1):(dm::Data0)][31:0] data_d, data_q; |
| - |
| - logic [HartSelLen-1:0] selected_hart; |
| - |
| - // a successful response returns zero |
| - assign dmi_resp_o.resp = dm::DTM_SUCCESS; |
| - assign dmi_resp_valid_o = ~resp_queue_empty; |
| - assign dmi_req_ready_o = ~resp_queue_full; |
| - assign resp_queue_push = dmi_req_valid_i & dmi_req_ready_o; |
| - // SBA |
| - assign sbautoincrement_o = sbcs_q.sbautoincrement; |
| - assign sbreadonaddr_o = sbcs_q.sbreadonaddr; |
| - assign sbreadondata_o = sbcs_q.sbreadondata; |
| - assign sbaccess_o = sbcs_q.sbaccess; |
| - assign sbdata_o = sbdata_q[BusWidth-1:0]; |
| - assign sbaddress_o = sbaddr_q[BusWidth-1:0]; |
| - |
| - assign hartsel_o = {dmcontrol_q.hartselhi, dmcontrol_q.hartsello}; |
| - |
| - always_comb begin : csr_read_write |
| - // -------------------- |
| - // Static Values (R/O) |
| - // -------------------- |
| - // dmstatus |
| - dmstatus = '0; |
| - dmstatus.version = dm::DbgVersion013; |
| - // no authentication implemented |
| - dmstatus.authenticated = 1'b1; |
| - // we do not support halt-on-reset sequence |
| - dmstatus.hasresethaltreq = 1'b0; |
| - // TODO(zarubaf) things need to change here if we implement the array mask |
| - dmstatus.allhavereset = havereset_q[selected_hart]; |
| - dmstatus.anyhavereset = havereset_q[selected_hart]; |
| - |
| - dmstatus.allresumeack = resumeack_i[selected_hart]; |
| - dmstatus.anyresumeack = resumeack_i[selected_hart]; |
| - |
| - dmstatus.allunavail = unavailable_i[selected_hart]; |
| - dmstatus.anyunavail = unavailable_i[selected_hart]; |
| - |
| - // as soon as we are out of the legal Hart region tell the debugger |
| - // that there are only non-existent harts |
| - dmstatus.allnonexistent = (hartsel_o > (NrHarts - 1)) ? 1'b1 : 1'b0; |
| - dmstatus.anynonexistent = (hartsel_o > (NrHarts - 1)) ? 1'b1 : 1'b0; |
| - |
| - // We are not allowed to be in multiple states at once. This is a to |
| - // make the running/halted and unavailable states exclusive. |
| - dmstatus.allhalted = halted_i[selected_hart] & ~unavailable_i[selected_hart]; |
| - dmstatus.anyhalted = halted_i[selected_hart] & ~unavailable_i[selected_hart]; |
| - |
| - dmstatus.allrunning = ~halted_i[selected_hart] & ~unavailable_i[selected_hart]; |
| - dmstatus.anyrunning = ~halted_i[selected_hart] & ~unavailable_i[selected_hart]; |
| - |
| - // abstractcs |
| - abstractcs = '0; |
| - abstractcs.datacount = dm::DataCount; |
| - abstractcs.progbufsize = dm::ProgBufSize; |
| - abstractcs.busy = cmdbusy_i; |
| - abstractcs.cmderr = cmderr_q; |
| - |
| - // abstractautoexec |
| - abstractauto_d = abstractauto_q; |
| - abstractauto_d.zero0 = '0; |
| - |
| - // default assignments |
| - havereset_d = havereset_q; |
| - dmcontrol_d = dmcontrol_q; |
| - cmderr_d = cmderr_q; |
| - command_d = command_q; |
| - progbuf_d = progbuf_q; |
| - data_d = data_q; |
| - sbcs_d = sbcs_q; |
| - sbaddr_d = sbaddress_i; |
| - sbdata_d = sbdata_q; |
| - |
| - resp_queue_data = 32'b0; |
| - cmd_valid_d = 1'b0; |
| - sbaddress_write_valid_o = 1'b0; |
| - sbdata_read_valid_o = 1'b0; |
| - sbdata_write_valid_o = 1'b0; |
| - clear_resumeack_o = 1'b0; |
| - |
| - // reads |
| - if (dmi_req_ready_o && dmi_req_valid_i && dtm_op == dm::DTM_READ) begin |
| - unique case ({1'b0, dmi_req_i.addr}) inside |
| - [(dm::Data0):DataEnd]: begin |
| - if (dm::DataCount > 0) begin |
| - resp_queue_data = data_q[dmi_req_i.addr[4:0]]; |
| - end |
| - if (!cmdbusy_i) begin |
| - // check whether we need to re-execute the command (just give a cmd_valid) |
| - cmd_valid_d = abstractauto_q.autoexecdata[dmi_req_i.addr[3:0] - |
| - int'(dm::Data0)]; |
| - end |
| - end |
| - dm::DMControl: resp_queue_data = dmcontrol_q; |
| - dm::DMStatus: resp_queue_data = dmstatus; |
| - dm::Hartinfo: resp_queue_data = hartinfo_i[selected_hart]; |
| - dm::AbstractCS: resp_queue_data = abstractcs; |
| - dm::AbstractAuto: resp_queue_data = abstractauto_q; |
| - // command is read-only |
| - dm::Command: resp_queue_data = '0; |
| - [(dm::ProgBuf0):ProgBufEnd]: begin |
| - resp_queue_data = progbuf_q[dmi_req_i.addr[4:0]]; |
| - if (!cmdbusy_i) begin |
| - // check whether we need to re-execute the command (just give a cmd_valid) |
| - // TODO(zarubaf): check if offset is correct: without it this may assign Xes |
| - cmd_valid_d = abstractauto_q.autoexecprogbuf[dmi_req_i.addr[3:0]+16]; |
| - end |
| - end |
| - dm::HaltSum0: resp_queue_data = haltsum0; |
| - dm::HaltSum1: resp_queue_data = haltsum1; |
| - dm::HaltSum2: resp_queue_data = haltsum2; |
| - dm::HaltSum3: resp_queue_data = haltsum3; |
| - dm::SBCS: begin |
| - resp_queue_data = sbcs_q; |
| - end |
| - dm::SBAddress0: begin |
| - // access while the SBA was busy |
| - if (sbbusy_i) begin |
| - sbcs_d.sbbusyerror = 1'b1; |
| - end else begin |
| - resp_queue_data = sbaddr_q[31:0]; |
| - end |
| - end |
| - dm::SBAddress1: begin |
| - // access while the SBA was busy |
| - if (sbbusy_i) begin |
| - sbcs_d.sbbusyerror = 1'b1; |
| - end else begin |
| - resp_queue_data = sbaddr_q[63:32]; |
| - end |
| - end |
| - dm::SBData0: begin |
| - // access while the SBA was busy |
| - if (sbbusy_i) begin |
| - sbcs_d.sbbusyerror = 1'b1; |
| - end else begin |
| - sbdata_read_valid_o = (sbcs_q.sberror == '0); |
| - resp_queue_data = sbdata_q[31:0]; |
| - end |
| - end |
| - dm::SBData1: begin |
| - // access while the SBA was busy |
| - if (sbbusy_i) begin |
| - sbcs_d.sbbusyerror = 1'b1; |
| - end else begin |
| - resp_queue_data = sbdata_q[63:32]; |
| - end |
| - end |
| - default:; |
| - endcase |
| + // write |
| + if (dmi_req_ready_o && dmi_req_valid_i && dtm_op == dm::DTM_WRITE) begin |
| + unique case (dm::dm_csr_e'({1'b0, dmi_req_i.addr})) inside |
| + [(dm::Data0):DataEnd]: begin |
| + // attempts to write them while busy is set does not change their value |
| + if (!cmdbusy_i && dm::DataCount > 0) begin |
| + data_d[dmi_req_i.addr[4:0]] = dmi_req_i.data; |
| + // check whether we need to re-execute the command (just give a cmd_valid) |
| + cmd_valid_d = abstractauto_q.autoexecdata[dmi_req_i.addr[3:0] - |
| + int'(dm::Data0)]; |
| + end |
| end |
| - |
| - // write |
| - if (dmi_req_ready_o && dmi_req_valid_i && dtm_op == dm::DTM_WRITE) begin |
| - unique case (dm::dm_csr_e'({1'b0, dmi_req_i.addr})) inside |
| - [(dm::Data0):DataEnd]: begin |
| - // attempts to write them while busy is set does not change their value |
| - if (!cmdbusy_i && dm::DataCount > 0) begin |
| - data_d[dmi_req_i.addr[4:0]] = dmi_req_i.data; |
| - // check whether we need to re-execute the command (just give a cmd_valid) |
| - cmd_valid_d = abstractauto_q.autoexecdata[dmi_req_i.addr[3:0] - |
| - int'(dm::Data0)]; |
| - end |
| - end |
| - dm::DMControl: begin |
| - automatic dm::dmcontrol_t dmcontrol; |
| - dmcontrol = dm::dmcontrol_t'(dmi_req_i.data); |
| - // clear the havreset of the selected hart |
| - if (dmcontrol.ackhavereset) begin |
| - havereset_d[selected_hart] = 1'b0; |
| - end |
| - dmcontrol_d = dmi_req_i.data; |
| - end |
| - dm::DMStatus:; // write are ignored to R/O register |
| - dm::Hartinfo:; // hartinfo is R/O |
| - // only command error is write-able |
| - dm::AbstractCS: begin // W1C |
| - // Gets set if an abstract command fails. The bits in this |
| - // field remain set until they are cleared by writing 1 to |
| - // them. No abstract command is started until the value is |
| - // reset to 0. |
| - automatic dm::abstractcs_t a_abstractcs; |
| - a_abstractcs = dm::abstractcs_t'(dmi_req_i.data); |
| - // reads during abstract command execution are not allowed |
| - if (!cmdbusy_i) begin |
| - cmderr_d = dm::cmderr_e'(~a_abstractcs.cmderr & cmderr_q); |
| - end else if (cmderr_q == dm::CmdErrNone) begin |
| - cmderr_d = dm::CmdErrBusy; |
| - end |
| - |
| - end |
| - dm::Command: begin |
| - // writes are ignored if a command is already busy |
| - if (!cmdbusy_i) begin |
| - cmd_valid_d = 1'b1; |
| - command_d = dm::command_t'(dmi_req_i.data); |
| - // if there was an attempted to write during a busy execution |
| - // and the cmderror field is zero set the busy error |
| - end else if (cmderr_q == dm::CmdErrNone) begin |
| - cmderr_d = dm::CmdErrBusy; |
| - end |
| - end |
| - dm::AbstractAuto: begin |
| - // this field can only be written legally when there is no command executing |
| - if (!cmdbusy_i) begin |
| - abstractauto_d = 32'b0; |
| - abstractauto_d.autoexecdata = 12'(dmi_req_i.data[dm::DataCount-1:0]); |
| - abstractauto_d.autoexecprogbuf = 16'(dmi_req_i.data[dm::ProgBufSize-1+16:16]); |
| - |
| - end else if (cmderr_q == dm::CmdErrNone) begin |
| - cmderr_d = dm::CmdErrBusy; |
| - end |
| - end |
| - [(dm::ProgBuf0):ProgBufEnd]: begin |
| - // attempts to write them while busy is set does not change their value |
| - if (!cmdbusy_i) begin |
| - progbuf_d[dmi_req_i.addr[4:0]] = dmi_req_i.data; |
| - // check whether we need to re-execute the command (just give a cmd_valid) |
| - // this should probably throw an error if executed during another command |
| - // was busy |
| - // TODO(zarubaf): check if offset is correct - without it this may |
| - // assign Xes |
| - cmd_valid_d = abstractauto_q.autoexecprogbuf[dmi_req_i.addr[3:0]+16]; |
| - end |
| - end |
| - dm::SBCS: begin |
| - // access while the SBA was busy |
| - if (sbbusy_i) begin |
| - sbcs_d.sbbusyerror = 1'b1; |
| - end else begin |
| - automatic dm::sbcs_t sbcs; |
| - sbcs = dm::sbcs_t'(dmi_req_i.data); |
| - sbcs_d = sbcs; |
| - // R/W1C |
| - sbcs_d.sbbusyerror = sbcs_q.sbbusyerror & (~sbcs.sbbusyerror); |
| - sbcs_d.sberror = sbcs_q.sberror & (~sbcs.sberror); |
| - end |
| - end |
| - dm::SBAddress0: begin |
| - // access while the SBA was busy |
| - if (sbbusy_i) begin |
| - sbcs_d.sbbusyerror = 1'b1; |
| - end else begin |
| - sbaddr_d[31:0] = dmi_req_i.data; |
| - sbaddress_write_valid_o = (sbcs_q.sberror == '0); |
| - end |
| - end |
| - dm::SBAddress1: begin |
| - // access while the SBA was busy |
| - if (sbbusy_i) begin |
| - sbcs_d.sbbusyerror = 1'b1; |
| - end else begin |
| - sbaddr_d[63:32] = dmi_req_i.data; |
| - end |
| - end |
| - dm::SBData0: begin |
| - // access while the SBA was busy |
| - if (sbbusy_i) begin |
| - sbcs_d.sbbusyerror = 1'b1; |
| - end else begin |
| - sbdata_d[31:0] = dmi_req_i.data; |
| - sbdata_write_valid_o = (sbcs_q.sberror == '0); |
| - end |
| - end |
| - dm::SBData1: begin |
| - // access while the SBA was busy |
| - if (sbbusy_i) begin |
| - sbcs_d.sbbusyerror = 1'b1; |
| - end else begin |
| - sbdata_d[63:32] = dmi_req_i.data; |
| - end |
| - end |
| - default:; |
| - endcase |
| + dm::DMControl: begin |
| + automatic dm::dmcontrol_t dmcontrol; |
| + dmcontrol = dm::dmcontrol_t'(dmi_req_i.data); |
| + // clear the havreset of the selected hart |
| + if (dmcontrol.ackhavereset) begin |
| + havereset_d[selected_hart] = 1'b0; |
| + end |
| + dmcontrol_d = dmi_req_i.data; |
| end |
| - // hart threw a command error and has precedence over bus writes |
| - if (cmderror_valid_i) begin |
| - cmderr_d = cmderror_i; |
| + dm::DMStatus:; // write are ignored to R/O register |
| + dm::Hartinfo:; // hartinfo is R/O |
| + // only command error is write-able |
| + dm::AbstractCS: begin // W1C |
| + // Gets set if an abstract command fails. The bits in this |
| + // field remain set until they are cleared by writing 1 to |
| + // them. No abstract command is started until the value is |
| + // reset to 0. |
| + automatic dm::abstractcs_t a_abstractcs; |
| + a_abstractcs = dm::abstractcs_t'(dmi_req_i.data); |
| + // reads during abstract command execution are not allowed |
| + if (!cmdbusy_i) begin |
| + cmderr_d = dm::cmderr_e'(~a_abstractcs.cmderr & cmderr_q); |
| + end else if (cmderr_q == dm::CmdErrNone) begin |
| + cmderr_d = dm::CmdErrBusy; |
| + end |
| end |
| - |
| - // update data registers |
| - if (data_valid_i) |
| - data_d = data_i; |
| - |
| - // set the havereset flag when we did a ndmreset |
| - if (ndmreset_o) begin |
| - havereset_d = '1; |
| + dm::Command: begin |
| + // writes are ignored if a command is already busy |
| + if (!cmdbusy_i) begin |
| + cmd_valid_d = 1'b1; |
| + command_d = dm::command_t'(dmi_req_i.data); |
| + // if there was an attempted to write during a busy execution |
| + // and the cmderror field is zero set the busy error |
| + end else if (cmderr_q == dm::CmdErrNone) begin |
| + cmderr_d = dm::CmdErrBusy; |
| + end |
| end |
| - // ------------- |
| - // System Bus |
| - // ------------- |
| - // set bus error |
| - if (sberror_valid_i) begin |
| - sbcs_d.sberror = sberror_i; |
| + dm::AbstractAuto: begin |
| + // this field can only be written legally when there is no command executing |
| + if (!cmdbusy_i) begin |
| + abstractauto_d = 32'b0; |
| + abstractauto_d.autoexecdata = 12'(dmi_req_i.data[dm::DataCount-1:0]); |
| + abstractauto_d.autoexecprogbuf = 16'(dmi_req_i.data[dm::ProgBufSize-1+16:16]); |
| + end else if (cmderr_q == dm::CmdErrNone) begin |
| + cmderr_d = dm::CmdErrBusy; |
| + end |
| end |
| - // update read data |
| - if (sbdata_valid_i) begin |
| - sbdata_d = 64'(sbdata_i); |
| + [(dm::ProgBuf0):ProgBufEnd]: begin |
| + // attempts to write them while busy is set does not change their value |
| + if (!cmdbusy_i) begin |
| + progbuf_d[dmi_req_i.addr[4:0]] = dmi_req_i.data; |
| + // check whether we need to re-execute the command (just give a cmd_valid) |
| + // this should probably throw an error if executed during another command |
| + // was busy |
| + // TODO(zarubaf): check if offset is correct - without it this may |
| + // assign Xes |
| + cmd_valid_d = abstractauto_q.autoexecprogbuf[dmi_req_i.addr[3:0]+16]; |
| + end |
| end |
| - |
| - // dmcontrol |
| - // TODO(zarubaf) we currently do not implement the hartarry mask |
| - dmcontrol_d.hasel = 1'b0; |
| - // we do not support resetting an individual hart |
| - dmcontrol_d.hartreset = 1'b0; |
| - dmcontrol_d.setresethaltreq = 1'b0; |
| - dmcontrol_d.clrresethaltreq = 1'b0; |
| - dmcontrol_d.zero1 = '0; |
| - dmcontrol_d.zero0 = '0; |
| - // Non-writeable, clear only |
| - dmcontrol_d.ackhavereset = 1'b0; |
| - if (!dmcontrol_q.resumereq && dmcontrol_d.resumereq) begin |
| - clear_resumeack_o = 1'b1; |
| + dm::SBCS: begin |
| + // access while the SBA was busy |
| + if (sbbusy_i) begin |
| + sbcs_d.sbbusyerror = 1'b1; |
| + end else begin |
| + automatic dm::sbcs_t sbcs; |
| + sbcs = dm::sbcs_t'(dmi_req_i.data); |
| + sbcs_d = sbcs; |
| + // R/W1C |
| + sbcs_d.sbbusyerror = sbcs_q.sbbusyerror & (~sbcs.sbbusyerror); |
| + sbcs_d.sberror = sbcs_q.sberror & (~sbcs.sberror); |
| + end |
| + end |
| + dm::SBAddress0: begin |
| + // access while the SBA was busy |
| + if (sbbusy_i) begin |
| + sbcs_d.sbbusyerror = 1'b1; |
| + end else begin |
| + sbaddr_d[31:0] = dmi_req_i.data; |
| + sbaddress_write_valid_o = (sbcs_q.sberror == '0); |
| + end |
| + end |
| + dm::SBAddress1: begin |
| + // access while the SBA was busy |
| + if (sbbusy_i) begin |
| + sbcs_d.sbbusyerror = 1'b1; |
| + end else begin |
| + sbaddr_d[63:32] = dmi_req_i.data; |
| + end |
| end |
| - if (dmcontrol_q.resumereq && resumeack_i) begin |
| - dmcontrol_d.resumereq = 1'b0; |
| + dm::SBData0: begin |
| + // access while the SBA was busy |
| + if (sbbusy_i) begin |
| + sbcs_d.sbbusyerror = 1'b1; |
| + end else begin |
| + sbdata_d[31:0] = dmi_req_i.data; |
| + sbdata_write_valid_o = (sbcs_q.sberror == '0); |
| + end |
| end |
| - // static values for dcsr |
| - sbcs_d.sbversion = 3'b1; |
| - sbcs_d.sbbusy = sbbusy_i; |
| - sbcs_d.sbasize = BusWidth; |
| - sbcs_d.sbaccess128 = 1'b0; |
| - sbcs_d.sbaccess64 = BusWidth == 64; |
| - sbcs_d.sbaccess32 = BusWidth == 32; |
| - sbcs_d.sbaccess16 = 1'b0; |
| - sbcs_d.sbaccess8 = 1'b0; |
| - sbcs_d.sbaccess = BusWidth == 64 ? 2'd3 : 2'd2; |
| + dm::SBData1: begin |
| + // access while the SBA was busy |
| + if (sbbusy_i) begin |
| + sbcs_d.sbbusyerror = 1'b1; |
| + end else begin |
| + sbdata_d[63:32] = dmi_req_i.data; |
| + end |
| + end |
| + default:; |
| + endcase |
| end |
| - |
| - // output multiplexer |
| - always_comb begin |
| - selected_hart = hartsel_o[HartSelLen-1:0]; |
| - // default assignment |
| - haltreq_o = '0; |
| - resumereq_o = '0; |
| - haltreq_o[selected_hart] = dmcontrol_q.haltreq; |
| - resumereq_o[selected_hart] = dmcontrol_q.resumereq; |
| + // hart threw a command error and has precedence over bus writes |
| + if (cmderror_valid_i) begin |
| + cmderr_d = cmderror_i; |
| end |
| |
| - assign dmactive_o = dmcontrol_q.dmactive; |
| - assign cmd_o = command_q; |
| - assign cmd_valid_o = cmd_valid_q; |
| - assign progbuf_o = progbuf_q; |
| - assign data_o = data_q; |
| - |
| - assign resp_queue_pop = dmi_resp_ready_i & ~resp_queue_empty; |
| - |
| - assign ndmreset_o = dmcontrol_q.ndmreset; |
| - |
| - // response FIFO |
| - fifo_v2 #( |
| - .dtype ( logic [31:0] ), |
| - .DEPTH ( 2 ) |
| - ) i_fifo ( |
| - .clk_i ( clk_i ), |
| - .rst_ni ( dmi_rst_ni ), // reset only when system is re-set |
| - .flush_i ( 1'b0 ), // we do not need to flush this queue |
| - .testmode_i ( testmode_i ), |
| - .full_o ( resp_queue_full ), |
| - .empty_o ( resp_queue_empty ), |
| - .alm_full_o ( ), |
| - .alm_empty_o ( ), |
| - .data_i ( resp_queue_data ), |
| - .push_i ( resp_queue_push ), |
| - .data_o ( dmi_resp_o.data ), |
| - .pop_i ( resp_queue_pop ) |
| - ); |
| + // update data registers |
| + if (data_valid_i) |
| + data_d = data_i; |
| |
| - always_ff @(posedge clk_i or negedge rst_ni) begin |
| - // PoR |
| - if (!rst_ni) begin |
| - dmcontrol_q <= '0; |
| - // this is the only write-able bit during reset |
| - cmderr_q <= dm::CmdErrNone; |
| - command_q <= '0; |
| - abstractauto_q <= '0; |
| - progbuf_q <= '0; |
| - data_q <= '0; |
| - sbcs_q <= '0; |
| - sbaddr_q <= '0; |
| - sbdata_q <= '0; |
| - end else begin |
| - // synchronous re-set of debug module, active-low, except for dmactive |
| - if (!dmcontrol_q.dmactive) begin |
| - dmcontrol_q.haltreq <= '0; |
| - dmcontrol_q.resumereq <= '0; |
| - dmcontrol_q.hartreset <= '0; |
| - dmcontrol_q.zero1 <= '0; |
| - dmcontrol_q.hasel <= '0; |
| - dmcontrol_q.hartsello <= '0; |
| - dmcontrol_q.hartselhi <= '0; |
| - dmcontrol_q.zero0 <= '0; |
| - dmcontrol_q.setresethaltreq <= '0; |
| - dmcontrol_q.clrresethaltreq <= '0; |
| - dmcontrol_q.ndmreset <= '0; |
| - // this is the only write-able bit during reset |
| - dmcontrol_q.dmactive <= dmcontrol_d.dmactive; |
| - cmderr_q <= dm::CmdErrNone; |
| - command_q <= '0; |
| - cmd_valid_q <= '0; |
| - abstractauto_q <= '0; |
| - progbuf_q <= '0; |
| - data_q <= '0; |
| - sbcs_q <= '0; |
| - sbaddr_q <= '0; |
| - sbdata_q <= '0; |
| - end else begin |
| - dmcontrol_q <= dmcontrol_d; |
| - cmderr_q <= cmderr_d; |
| - command_q <= command_d; |
| - cmd_valid_q <= cmd_valid_d; |
| - abstractauto_q <= abstractauto_d; |
| - progbuf_q <= progbuf_d; |
| - data_q <= data_d; |
| - sbcs_q <= sbcs_d; |
| - sbaddr_q <= sbaddr_d; |
| - sbdata_q <= sbdata_d; |
| - end |
| - end |
| + // set the havereset flag when we did a ndmreset |
| + if (ndmreset_o) begin |
| + havereset_d = '1; |
| + end |
| + // ------------- |
| + // System Bus |
| + // ------------- |
| + // set bus error |
| + if (sberror_valid_i) begin |
| + sbcs_d.sberror = sberror_i; |
| + end |
| + // update read data |
| + if (sbdata_valid_i) begin |
| + sbdata_d = 64'(sbdata_i); |
| + end |
| + |
| + // dmcontrol |
| + // TODO(zarubaf) we currently do not implement the hartarry mask |
| + dmcontrol_d.hasel = 1'b0; |
| + // we do not support resetting an individual hart |
| + dmcontrol_d.hartreset = 1'b0; |
| + dmcontrol_d.setresethaltreq = 1'b0; |
| + dmcontrol_d.clrresethaltreq = 1'b0; |
| + dmcontrol_d.zero1 = '0; |
| + dmcontrol_d.zero0 = '0; |
| + // Non-writeable, clear only |
| + dmcontrol_d.ackhavereset = 1'b0; |
| + if (!dmcontrol_q.resumereq && dmcontrol_d.resumereq) begin |
| + clear_resumeack_o = 1'b1; |
| + end |
| + if (dmcontrol_q.resumereq && resumeack_i) begin |
| + dmcontrol_d.resumereq = 1'b0; |
| end |
| + // static values for dcsr |
| + sbcs_d.sbversion = 3'b1; |
| + sbcs_d.sbbusy = sbbusy_i; |
| + sbcs_d.sbasize = BusWidth; |
| + sbcs_d.sbaccess128 = 1'b0; |
| + sbcs_d.sbaccess64 = BusWidth == 64; |
| + sbcs_d.sbaccess32 = BusWidth == 32; |
| + sbcs_d.sbaccess16 = 1'b0; |
| + sbcs_d.sbaccess8 = 1'b0; |
| + sbcs_d.sbaccess = BusWidth == 64 ? 2'd3 : 2'd2; |
| + end |
| + |
| + // output multiplexer |
| + always_comb begin |
| + selected_hart = hartsel_o[HartSelLen-1:0]; |
| + // default assignment |
| + haltreq_o = '0; |
| + resumereq_o = '0; |
| + haltreq_o[selected_hart] = dmcontrol_q.haltreq; |
| + resumereq_o[selected_hart] = dmcontrol_q.resumereq; |
| + end |
| + |
| + assign dmactive_o = dmcontrol_q.dmactive; |
| + assign cmd_o = command_q; |
| + assign cmd_valid_o = cmd_valid_q; |
| + assign progbuf_o = progbuf_q; |
| + assign data_o = data_q; |
| + |
| + assign resp_queue_pop = dmi_resp_ready_i & ~resp_queue_empty; |
| + |
| + assign ndmreset_o = dmcontrol_q.ndmreset; |
| + |
| + // response FIFO |
| + fifo_v2 #( |
| + .dtype ( logic [31:0] ), |
| + .DEPTH ( 2 ) |
| + ) i_fifo ( |
| + .clk_i ( clk_i ), |
| + .rst_ni ( dmi_rst_ni ), // reset only when system is re-set |
| + .flush_i ( 1'b0 ), // we do not need to flush this queue |
| + .testmode_i ( testmode_i ), |
| + .full_o ( resp_queue_full ), |
| + .empty_o ( resp_queue_empty ), |
| + .alm_full_o ( ), |
| + .alm_empty_o ( ), |
| + .data_i ( resp_queue_data ), |
| + .push_i ( resp_queue_push ), |
| + .data_o ( dmi_resp_o.data ), |
| + .pop_i ( resp_queue_pop ) |
| + ); |
| + |
| + always_ff @(posedge clk_i or negedge rst_ni) begin |
| + // PoR |
| + if (!rst_ni) begin |
| + dmcontrol_q <= '0; |
| + // this is the only write-able bit during reset |
| + cmderr_q <= dm::CmdErrNone; |
| + command_q <= '0; |
| + abstractauto_q <= '0; |
| + progbuf_q <= '0; |
| + data_q <= '0; |
| + sbcs_q <= '0; |
| + sbaddr_q <= '0; |
| + sbdata_q <= '0; |
| + end else begin |
| + // synchronous re-set of debug module, active-low, except for dmactive |
| + if (!dmcontrol_q.dmactive) begin |
| + dmcontrol_q.haltreq <= '0; |
| + dmcontrol_q.resumereq <= '0; |
| + dmcontrol_q.hartreset <= '0; |
| + dmcontrol_q.zero1 <= '0; |
| + dmcontrol_q.hasel <= '0; |
| + dmcontrol_q.hartsello <= '0; |
| + dmcontrol_q.hartselhi <= '0; |
| + dmcontrol_q.zero0 <= '0; |
| + dmcontrol_q.setresethaltreq <= '0; |
| + dmcontrol_q.clrresethaltreq <= '0; |
| + dmcontrol_q.ndmreset <= '0; |
| + // this is the only write-able bit during reset |
| + dmcontrol_q.dmactive <= dmcontrol_d.dmactive; |
| + cmderr_q <= dm::CmdErrNone; |
| + command_q <= '0; |
| + cmd_valid_q <= '0; |
| + abstractauto_q <= '0; |
| + progbuf_q <= '0; |
| + data_q <= '0; |
| + sbcs_q <= '0; |
| + sbaddr_q <= '0; |
| + sbdata_q <= '0; |
| + end else begin |
| + dmcontrol_q <= dmcontrol_d; |
| + cmderr_q <= cmderr_d; |
| + command_q <= command_d; |
| + cmd_valid_q <= cmd_valid_d; |
| + abstractauto_q <= abstractauto_d; |
| + progbuf_q <= progbuf_d; |
| + data_q <= data_d; |
| + sbcs_q <= sbcs_d; |
| + sbaddr_q <= sbaddr_d; |
| + sbdata_q <= sbdata_d; |
| + end |
| + end |
| + end |
| |
| |
| - for (genvar k = 0; k < NrHarts; k++) begin : gen_havereset |
| - always_ff @(posedge clk_i or negedge rst_ni) begin |
| - if (!rst_ni) begin |
| - havereset_q[k] <= 1'b1; |
| - end else begin |
| - havereset_q[k] <= SelectableHarts[k] ? havereset_d[k] : 1'b0; |
| - end |
| - end |
| + for (genvar k = 0; k < NrHarts; k++) begin : gen_havereset |
| + always_ff @(posedge clk_i or negedge rst_ni) begin |
| + if (!rst_ni) begin |
| + havereset_q[k] <= 1'b1; |
| + end else begin |
| + havereset_q[k] <= SelectableHarts[k] ? havereset_d[k] : 1'b0; |
| + end |
| end |
| + end |
| |
| /////////////////////////////////////////////////////// |
| // assertions |
| /////////////////////////////////////////////////////// |
| |
| - |
| //pragma translate_off |
| `ifndef VERILATOR |
| - haltsum: assert property ( |
| - @(posedge clk_i) disable iff (!rst_ni) |
| - (dmi_req_ready_o && dmi_req_valid_i && dtm_op == dm::DTM_READ) |-> |
| - !({1'b0, dmi_req_i.addr} inside |
| - {dm::HaltSum0, dm::HaltSum1, dm::HaltSum2, dm::HaltSum3})) |
| - else $warning("Haltsums have not been properly tested yet."); |
| + haltsum: assert property ( |
| + @(posedge clk_i) disable iff (!rst_ni) |
| + (dmi_req_ready_o && dmi_req_valid_i && dtm_op == dm::DTM_READ) |-> |
| + !({1'b0, dmi_req_i.addr} inside |
| + {dm::HaltSum0, dm::HaltSum1, dm::HaltSum2, dm::HaltSum3})) |
| + else $warning("Haltsums have not been properly tested yet."); |
| `endif |
| //pragma translate_on |
| |
| - |
| endmodule |
| diff --git a/src/dm_mem.sv b/src/dm_mem.sv |
| index c09126c..1ecc878 100644 |
| --- a/src/dm_mem.sv |
| +++ b/src/dm_mem.sv |
| @@ -1,470 +1,466 @@ |
| /* 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 |
| - * |
| - */ |
| +* 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, |
| - parameter int BusWidth = -1, |
| - parameter logic [NrHarts-1:0] SelectableHarts = -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, |
| - input logic clear_resumeack_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_e cmderror_o, |
| - output logic cmdbusy_o, |
| - // data interface |
| - |
| - // SRAM interface |
| - input logic req_i, |
| - input logic we_i, |
| - input logic [BusWidth-1:0] addr_i, |
| - input logic [BusWidth-1:0] wdata_i, |
| - input logic [BusWidth/8-1:0] be_i, |
| - output logic [BusWidth-1:0] rdata_o |
| + parameter int NrHarts = -1, |
| + parameter int BusWidth = -1, |
| + parameter logic [NrHarts-1:0] SelectableHarts = -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, |
| + input logic clear_resumeack_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_e cmderror_o, |
| + output logic cmdbusy_o, |
| + // data interface |
| + |
| + // SRAM interface |
| + input logic req_i, |
| + input logic we_i, |
| + input logic [BusWidth-1:0] addr_i, |
| + input logic [BusWidth-1:0] wdata_i, |
| + input logic [BusWidth/8-1:0] be_i, |
| + output logic [BusWidth-1:0] rdata_o |
| ); |
| |
| - localparam int HartSelLen = (NrHarts == 1) ? 1 : $clog2(NrHarts); |
| - localparam int MaxAar = (BusWidth == 64) ? 4 : 3; |
| - 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 [NrHarts-1:0] halted; |
| - |
| - logic [HartSelLen-1:0] hart_sel; |
| - logic exception; |
| - logic unsupported_command; |
| - |
| - logic [63:0] rom_rdata; |
| - logic [63:0] rdata_d, rdata_q; |
| - logic word_enable32_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; |
| - |
| - typedef enum logic [1:0] { Idle, Go, Resume, CmdExecuting } state_e; |
| - state_e 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[hartsel_i]) 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[hartsel_i] && !resuming_q[hartsel_i] && |
| - !haltreq_i[hartsel_i] && halted_q[hartsel_i]) 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[hartsel_i]) |
| - state_d = Idle; |
| - end |
| - |
| - CmdExecuting: begin |
| - cmdbusy_o = 1'b1; |
| - go = 1'b0; |
| - // wait until the hart has halted again |
| - if (halted[hartsel_i]) begin |
| - state_d = Idle; |
| - end |
| - end |
| - endcase |
| - |
| - // only signal once that cmd is unsupported so that we can clear cmderr |
| - // in subsequent writes to abstractcs |
| - if (unsupported_command && cmd_valid_i) begin |
| - cmderror_valid_o = 1'b1; |
| - cmderror_o = dm::CmdErrNotSupported; |
| + localparam int HartSelLen = (NrHarts == 1) ? 1 : $clog2(NrHarts); |
| + localparam int MaxAar = (BusWidth == 64) ? 4 : 3; |
| + 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 [NrHarts-1:0] halted; |
| + |
| + logic [HartSelLen-1:0] hart_sel; |
| + logic exception; |
| + logic unsupported_command; |
| + |
| + logic [63:0] rom_rdata; |
| + logic [63:0] rdata_d, rdata_q; |
| + logic word_enable32_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; |
| + |
| + typedef enum logic [1:0] { Idle, Go, Resume, CmdExecuting } state_e; |
| + state_e 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[hartsel_i]) 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 |
| - |
| - if (exception) begin |
| - cmderror_valid_o = 1'b1; |
| - cmderror_o = dm::CmdErrorException; |
| - end |
| - |
| - end |
| - |
| - // read/write logic |
| - always_comb begin |
| - automatic logic [63:0] data_bits; |
| - |
| - halted_d = halted_q; |
| - resuming_d = resuming_q; |
| - rdata_o = (BusWidth == 64) ? |
| - (fwd_rom_q ? rom_rdata : rdata_q) : |
| - (word_enable32_q ? |
| - (fwd_rom_q ? rom_rdata[63:32] : rdata_q[63:32]) : |
| - (fwd_rom_q ? rom_rdata[31: 0] : rdata_q[31: 0])); |
| - 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 = '0; |
| - going = 1'b0; |
| - // The resume ack signal is lowered when the resume request is deasserted |
| - if (clear_resumeack_i) begin |
| - resuming_d[hartsel_i] = 1'b0; |
| + // 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[hartsel_i] && !resuming_q[hartsel_i] && |
| + !haltreq_i[hartsel_i] && halted_q[hartsel_i]) begin |
| + state_d = Resume; |
| 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[hart_sel] = 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 |
| - default ; |
| - 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[hart_sel]) begin |
| - rdata_d = {32'b0, dm::jal('0, dm::ResumeAddress[11:0]-WhereTo)}; |
| - end |
| - |
| - // there is a command active so jump there |
| - if (cmdbusy_o) begin |
| - // transfer not set is shortcut to the program buffer if postexec is set |
| - // keep this statement narrow to not catch invalid commands |
| - if (cmd_i.cmdtype == dm::AccessRegister && |
| - !ac_ar.transfer && ac_ar.postexec) begin |
| - rdata_d = {32'b0, dm::jal('0, ProgBufBase-WhereTo)}; |
| - // this is a legit abstract cmd -> execute it |
| - end else begin |
| - rdata_d = {32'b0, dm::jal('0, AbstractCmdBase-WhereTo)}; |
| - end |
| - end |
| - end |
| - |
| - [DataBase:DataEnd]: begin |
| - rdata_d = { |
| - data_i[(addr_i[DbgAddressBits-1:3] - DataBase[DbgAddressBits-1:3] + 1)], |
| - data_i[(addr_i[DbgAddressBits-1:3] - DataBase[DbgAddressBits-1:3])] |
| - }; |
| - 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 |
| + |
| + 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[hartsel_i]) |
| + state_d = Idle; |
| + end |
| + |
| + CmdExecuting: begin |
| + cmdbusy_o = 1'b1; |
| + go = 1'b0; |
| + // wait until the hart has halted again |
| + if (halted[hartsel_i]) begin |
| + state_d = Idle; |
| end |
| + end |
| + endcase |
| + |
| + // only signal once that cmd is unsupported so that we can clear cmderr |
| + // in subsequent writes to abstractcs |
| + if (unsupported_command && cmd_valid_i) begin |
| + cmderror_valid_o = 1'b1; |
| + cmderror_o = dm::CmdErrNotSupported; |
| + end |
| |
| - data_o = data_bits; |
| + if (exception) begin |
| + cmderror_valid_o = 1'b1; |
| + cmderror_o = dm::CmdErrorException; |
| end |
| + end |
| + |
| + // read/write logic |
| + always_comb begin |
| + automatic logic [63:0] data_bits; |
| + |
| + halted_d = halted_q; |
| + resuming_d = resuming_q; |
| + rdata_o = (BusWidth == 64) ? |
| + (fwd_rom_q ? rom_rdata : rdata_q) : |
| + (word_enable32_q ? |
| + (fwd_rom_q ? rom_rdata[63:32] : rdata_q[63:32]) : |
| + (fwd_rom_q ? rom_rdata[31: 0] : rdata_q[31: 0])); |
| + 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 = '0; |
| + going = 1'b0; |
| + // The resume ack signal is lowered when the resume request is deasserted |
| + if (clear_resumeack_i) begin |
| + resuming_d[hartsel_i] = 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[hart_sel] = 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 |
| + default ; |
| + endcase |
| |
| - 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] = dm::illegal(); |
| - // load debug module base address into a0, this is shared among all commands |
| - abstract_cmd[0][63:32] = dm::auipc(5'd10, '0); |
| - abstract_cmd[1][31:0] = dm::srli(5'd10, 5'd10, 6'd12); // clr lowest 12b -> DM base offset |
| - abstract_cmd[1][63:32] = dm::slli(5'd10, 5'd10, 6'd12); |
| - abstract_cmd[2][31:0] = dm::nop(); |
| - abstract_cmd[2][63:32] = dm::nop(); |
| - abstract_cmd[3][31:0] = dm::nop(); |
| - abstract_cmd[3][63:32] = dm::nop(); |
| - abstract_cmd[4][31:0] = dm::csrr(dm::CSR_DSCRATCH1, 5'd10); |
| - abstract_cmd[4][63:32] = dm::ebreak(); |
| - |
| - // this depends on the command being executed |
| - unique case (cmd_i.cmdtype) |
| - // -------------------- |
| - // Access Register |
| - // -------------------- |
| - dm::AccessRegister: begin |
| - if (ac_ar.aarsize < MaxAar && ac_ar.transfer && ac_ar.write) begin |
| - // store a0 in dscratch1 |
| - abstract_cmd[0][31:0] = dm::csrw(dm::CSR_DSCRATCH1, 5'd10); |
| - // this range is reserved |
| - if (ac_ar.regno[15:14] != '0) begin |
| - abstract_cmd[0][31:0] = dm::ebreak(); // we leave asap |
| - unsupported_command = 1'b1; |
| - // A0 access needs to be handled separately, as we use A0 to load |
| - // the DM address offset need to access DSCRATCH1 in this case |
| - end else if (ac_ar.regno[12] && (!ac_ar.regno[5]) && |
| - (ac_ar.regno[4:0] == 5'd10)) begin |
| - // store s0 in dscratch |
| - abstract_cmd[2][31:0] = dm::csrw(dm::CSR_DSCRATCH0, 5'd8); |
| - // load from data register |
| - abstract_cmd[2][63:32] = dm::load(ac_ar.aarsize, 5'd8, 5'd10, dm::DataAddr); |
| - // and store it in the corresponding CSR |
| - abstract_cmd[3][31:0] = dm::csrw(dm::CSR_DSCRATCH1, 5'd8); |
| - // restore s0 again from dscratch |
| - abstract_cmd[3][63:32] = dm::csrr(dm::CSR_DSCRATCH0, 5'd8); |
| - // 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] = |
| - dm::float_load(ac_ar.aarsize, ac_ar.regno[4:0], 5'd10, dm::DataAddr); |
| - end else begin |
| - abstract_cmd[2][31:0] = |
| - dm::load(ac_ar.aarsize, ac_ar.regno[4:0], 5'd10, dm::DataAddr); |
| - end |
| - // CSR access |
| - end else begin |
| - // data register to CSR |
| - // store s0 in dscratch |
| - abstract_cmd[2][31:0] = dm::csrw(dm::CSR_DSCRATCH0, 5'd8); |
| - // load from data register |
| - abstract_cmd[2][63:32] = dm::load(ac_ar.aarsize, 5'd8, 5'd10, dm::DataAddr); |
| - // and store it in the corresponding CSR |
| - abstract_cmd[3][31:0] = dm::csrw(dm::csr_reg_t'(ac_ar.regno[11:0]), 5'd8); |
| - // restore s0 again from dscratch |
| - abstract_cmd[3][63:32] = dm::csrr(dm::CSR_DSCRATCH0, 5'd8); |
| - end |
| - end else if (ac_ar.aarsize < MaxAar && ac_ar.transfer && !ac_ar.write) begin |
| - // store a0 in dscratch1 |
| - abstract_cmd[0][31:0] = dm::csrw(dm::CSR_DSCRATCH1, 5'd10); |
| - // this range is reserved |
| - if (ac_ar.regno[15:14] != '0) begin |
| - abstract_cmd[0][31:0] = dm::ebreak(); // we leave asap |
| - unsupported_command = 1'b1; |
| - // A0 access needs to be handled separately, as we use A0 to load |
| - // the DM address offset need to access DSCRATCH1 in this case |
| - end else if (ac_ar.regno[12] && (!ac_ar.regno[5]) && |
| - (ac_ar.regno[4:0] == 5'd10)) begin |
| - // store s0 in dscratch |
| - abstract_cmd[2][31:0] = dm::csrw(dm::CSR_DSCRATCH0, 5'd8); |
| - // read value from CSR into s0 |
| - abstract_cmd[2][63:32] = dm::csrr(dm::CSR_DSCRATCH1, 5'd8); |
| - // and store s0 into data section |
| - abstract_cmd[3][31:0] = dm::store(ac_ar.aarsize, 5'd8, 5'd10, dm::DataAddr); |
| - // restore s0 again from dscratch |
| - abstract_cmd[3][63:32] = dm::csrr(dm::CSR_DSCRATCH0, 5'd8); |
| - // 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] = |
| - dm::float_store(ac_ar.aarsize, ac_ar.regno[4:0], 5'd10, dm::DataAddr); |
| - end else begin |
| - abstract_cmd[2][31:0] = |
| - dm::store(ac_ar.aarsize, ac_ar.regno[4:0], 5'd10, dm::DataAddr); |
| - end |
| - // CSR access |
| - end else begin |
| - // CSR register to data |
| - // store s0 in dscratch |
| - abstract_cmd[2][31:0] = dm::csrw(dm::CSR_DSCRATCH0, 5'd8); |
| - // read value from CSR into s0 |
| - abstract_cmd[2][63:32] = dm::csrr(dm::csr_reg_t'(ac_ar.regno[11:0]), 5'd8); |
| - // and store s0 into data section |
| - abstract_cmd[3][31:0] = dm::store(ac_ar.aarsize, 5'd8, 5'd10, dm::DataAddr); |
| - // restore s0 again from dscratch |
| - abstract_cmd[3][63:32] = dm::csrr(dm::CSR_DSCRATCH0, 5'd8); |
| - end |
| - end else if (ac_ar.aarsize >= MaxAar || ac_ar.aarpostincrement == 1'b1) begin |
| - // this should happend when e.g. ac_ar.aarsize >= MaxAar |
| - // Openocd will try to do an access with aarsize=64 bits |
| - // first before falling back to 32 bits. |
| - abstract_cmd[0][31:0] = dm::ebreak(); // we leave asap |
| - unsupported_command = 1'b1; |
| - |
| - end |
| - |
| - // Check whether we need to execute the program buffer. When we |
| - // get an unsupported command we really should abort instead of |
| - // still trying to execute the program buffer, makes it easier |
| - // for the debugger to recover |
| - if (ac_ar.postexec && !unsupported_command) begin |
| - // issue a nop, we will automatically run into the program buffer |
| - abstract_cmd[4][63:32] = dm::nop(); |
| - end |
| + // 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[hart_sel]) begin |
| + rdata_d = {32'b0, dm::jal('0, dm::ResumeAddress[11:0]-WhereTo)}; |
| + end |
| |
| + // there is a command active so jump there |
| + if (cmdbusy_o) begin |
| + // transfer not set is shortcut to the program buffer if postexec is set |
| + // keep this statement narrow to not catch invalid commands |
| + if (cmd_i.cmdtype == dm::AccessRegister && |
| + !ac_ar.transfer && ac_ar.postexec) begin |
| + rdata_d = {32'b0, dm::jal('0, ProgBufBase-WhereTo)}; |
| + // this is a legit abstract cmd -> execute it |
| + end else begin |
| + rdata_d = {32'b0, dm::jal('0, AbstractCmdBase-WhereTo)}; |
| + end |
| end |
| - // not supported at the moment |
| - // dm::QuickAccess:; |
| - // dm::AccessMemory:; |
| - default: begin |
| - abstract_cmd[0][31:0] = dm::ebreak(); |
| - unsupported_command = 1'b1; |
| + end |
| + |
| + [DataBase:DataEnd]: begin |
| + rdata_d = { |
| + data_i[(addr_i[DbgAddressBits-1:3] - DataBase[DbgAddressBits-1:3] + 1)], |
| + data_i[(addr_i[DbgAddressBits-1:3] - DataBase[DbgAddressBits-1:3])] |
| + }; |
| + 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 |
| |
| - logic [63:0] rom_addr; |
| - assign rom_addr = 64'(addr_i); |
| - debug_rom i_debug_rom ( |
| - .clk_i, |
| - .req_i, |
| - .addr_i ( rom_addr ), |
| - .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; |
| - state_q <= Idle; |
| - word_enable32_q <= 1'b0; |
| - end else begin |
| - fwd_rom_q <= fwd_rom_d; |
| - rdata_q <= rdata_d; |
| - state_q <= state_d; |
| - word_enable32_q <= addr_i[2]; |
| - end |
| - end |
| - |
| - for (genvar k = 0; k < NrHarts; k++) begin : gen_halted |
| - always_ff @(posedge clk_i or negedge rst_ni) begin |
| - if (!rst_ni) begin |
| - halted_q[k] <= 1'b0; |
| - resuming_q[k] <= 1'b0; |
| + 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] = dm::illegal(); |
| + // load debug module base address into a0, this is shared among all commands |
| + abstract_cmd[0][63:32] = dm::auipc(5'd10, '0); |
| + abstract_cmd[1][31:0] = dm::srli(5'd10, 5'd10, 6'd12); // clr lowest 12b -> DM base offset |
| + abstract_cmd[1][63:32] = dm::slli(5'd10, 5'd10, 6'd12); |
| + abstract_cmd[2][31:0] = dm::nop(); |
| + abstract_cmd[2][63:32] = dm::nop(); |
| + abstract_cmd[3][31:0] = dm::nop(); |
| + abstract_cmd[3][63:32] = dm::nop(); |
| + abstract_cmd[4][31:0] = dm::csrr(dm::CSR_DSCRATCH1, 5'd10); |
| + abstract_cmd[4][63:32] = dm::ebreak(); |
| + |
| + // this depends on the command being executed |
| + unique case (cmd_i.cmdtype) |
| + // -------------------- |
| + // Access Register |
| + // -------------------- |
| + dm::AccessRegister: begin |
| + if (ac_ar.aarsize < MaxAar && ac_ar.transfer && ac_ar.write) begin |
| + // store a0 in dscratch1 |
| + abstract_cmd[0][31:0] = dm::csrw(dm::CSR_DSCRATCH1, 5'd10); |
| + // this range is reserved |
| + if (ac_ar.regno[15:14] != '0) begin |
| + abstract_cmd[0][31:0] = dm::ebreak(); // we leave asap |
| + unsupported_command = 1'b1; |
| + // A0 access needs to be handled separately, as we use A0 to load |
| + // the DM address offset need to access DSCRATCH1 in this case |
| + end else if (ac_ar.regno[12] && (!ac_ar.regno[5]) && |
| + (ac_ar.regno[4:0] == 5'd10)) begin |
| + // store s0 in dscratch |
| + abstract_cmd[2][31:0] = dm::csrw(dm::CSR_DSCRATCH0, 5'd8); |
| + // load from data register |
| + abstract_cmd[2][63:32] = dm::load(ac_ar.aarsize, 5'd8, 5'd10, dm::DataAddr); |
| + // and store it in the corresponding CSR |
| + abstract_cmd[3][31:0] = dm::csrw(dm::CSR_DSCRATCH1, 5'd8); |
| + // restore s0 again from dscratch |
| + abstract_cmd[3][63:32] = dm::csrr(dm::CSR_DSCRATCH0, 5'd8); |
| + // 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] = |
| + dm::float_load(ac_ar.aarsize, ac_ar.regno[4:0], 5'd10, dm::DataAddr); |
| + end else begin |
| + abstract_cmd[2][31:0] = |
| + dm::load(ac_ar.aarsize, ac_ar.regno[4:0], 5'd10, dm::DataAddr); |
| + end |
| + // CSR access |
| + end else begin |
| + // data register to CSR |
| + // store s0 in dscratch |
| + abstract_cmd[2][31:0] = dm::csrw(dm::CSR_DSCRATCH0, 5'd8); |
| + // load from data register |
| + abstract_cmd[2][63:32] = dm::load(ac_ar.aarsize, 5'd8, 5'd10, dm::DataAddr); |
| + // and store it in the corresponding CSR |
| + abstract_cmd[3][31:0] = dm::csrw(dm::csr_reg_t'(ac_ar.regno[11:0]), 5'd8); |
| + // restore s0 again from dscratch |
| + abstract_cmd[3][63:32] = dm::csrr(dm::CSR_DSCRATCH0, 5'd8); |
| + end |
| + end else if (ac_ar.aarsize < MaxAar && ac_ar.transfer && !ac_ar.write) begin |
| + // store a0 in dscratch1 |
| + abstract_cmd[0][31:0] = dm::csrw(dm::CSR_DSCRATCH1, 5'd10); |
| + // this range is reserved |
| + if (ac_ar.regno[15:14] != '0) begin |
| + abstract_cmd[0][31:0] = dm::ebreak(); // we leave asap |
| + unsupported_command = 1'b1; |
| + // A0 access needs to be handled separately, as we use A0 to load |
| + // the DM address offset need to access DSCRATCH1 in this case |
| + end else if (ac_ar.regno[12] && (!ac_ar.regno[5]) && |
| + (ac_ar.regno[4:0] == 5'd10)) begin |
| + // store s0 in dscratch |
| + abstract_cmd[2][31:0] = dm::csrw(dm::CSR_DSCRATCH0, 5'd8); |
| + // read value from CSR into s0 |
| + abstract_cmd[2][63:32] = dm::csrr(dm::CSR_DSCRATCH1, 5'd8); |
| + // and store s0 into data section |
| + abstract_cmd[3][31:0] = dm::store(ac_ar.aarsize, 5'd8, 5'd10, dm::DataAddr); |
| + // restore s0 again from dscratch |
| + abstract_cmd[3][63:32] = dm::csrr(dm::CSR_DSCRATCH0, 5'd8); |
| + // 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] = |
| + dm::float_store(ac_ar.aarsize, ac_ar.regno[4:0], 5'd10, dm::DataAddr); |
| end else begin |
| - halted_q[k] <= SelectableHarts[k] ? halted_d[k] : 1'b0; |
| - resuming_q[k] <= SelectableHarts[k] ? resuming_d[k] : 1'b0; |
| + abstract_cmd[2][31:0] = |
| + dm::store(ac_ar.aarsize, ac_ar.regno[4:0], 5'd10, dm::DataAddr); |
| end |
| + // CSR access |
| + end else begin |
| + // CSR register to data |
| + // store s0 in dscratch |
| + abstract_cmd[2][31:0] = dm::csrw(dm::CSR_DSCRATCH0, 5'd8); |
| + // read value from CSR into s0 |
| + abstract_cmd[2][63:32] = dm::csrr(dm::csr_reg_t'(ac_ar.regno[11:0]), 5'd8); |
| + // and store s0 into data section |
| + abstract_cmd[3][31:0] = dm::store(ac_ar.aarsize, 5'd8, 5'd10, dm::DataAddr); |
| + // restore s0 again from dscratch |
| + abstract_cmd[3][63:32] = dm::csrr(dm::CSR_DSCRATCH0, 5'd8); |
| + end |
| + end else if (ac_ar.aarsize >= MaxAar || ac_ar.aarpostincrement == 1'b1) begin |
| + // this should happend when e.g. ac_ar.aarsize >= MaxAar |
| + // Openocd will try to do an access with aarsize=64 bits |
| + // first before falling back to 32 bits. |
| + abstract_cmd[0][31:0] = dm::ebreak(); // we leave asap |
| + unsupported_command = 1'b1; |
| end |
| + // Check whether we need to execute the program buffer. When we |
| + // get an unsupported command we really should abort instead of |
| + // still trying to execute the program buffer, makes it easier |
| + // for the debugger to recover |
| + if (ac_ar.postexec && !unsupported_command) begin |
| + // issue a nop, we will automatically run into the program buffer |
| + abstract_cmd[4][63:32] = dm::nop(); |
| + end |
| + end |
| + // not supported at the moment |
| + // dm::QuickAccess:; |
| + // dm::AccessMemory:; |
| + default: begin |
| + abstract_cmd[0][31:0] = dm::ebreak(); |
| + unsupported_command = 1'b1; |
| + end |
| + endcase |
| + end |
| + |
| + logic [63:0] rom_addr; |
| + assign rom_addr = 64'(addr_i); |
| + debug_rom i_debug_rom ( |
| + .clk_i, |
| + .req_i, |
| + .addr_i ( rom_addr ), |
| + .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; |
| + state_q <= Idle; |
| + word_enable32_q <= 1'b0; |
| + end else begin |
| + fwd_rom_q <= fwd_rom_d; |
| + rdata_q <= rdata_d; |
| + state_q <= state_d; |
| + word_enable32_q <= addr_i[2]; |
| + end |
| + end |
| + |
| + for (genvar k = 0; k < NrHarts; k++) begin : gen_halted |
| + always_ff @(posedge clk_i or negedge rst_ni) begin |
| + if (!rst_ni) begin |
| + halted_q[k] <= 1'b0; |
| + resuming_q[k] <= 1'b0; |
| + end else begin |
| + halted_q[k] <= SelectableHarts[k] ? halted_d[k] : 1'b0; |
| + resuming_q[k] <= SelectableHarts[k] ? resuming_d[k] : 1'b0; |
| + end |
| end |
| + end |
| |
| endmodule |
| diff --git a/src/dm_pkg.sv b/src/dm_pkg.sv |
| index b2593d6..49e77be 100644 |
| --- a/src/dm_pkg.sv |
| +++ b/src/dm_pkg.sv |
| @@ -17,370 +17,370 @@ |
| */ |
| |
| package dm; |
| - localparam logic [3:0] DbgVersion013 = 4'h2; |
| - // size of program buffer in junks of 32-bit words |
| - localparam logic [4:0] ProgBufSize = 5'h8; |
| - |
| - // amount of data count registers implemented |
| - localparam logic [3:0] DataCount = 4'h2; |
| - |
| - // address to which a hart should jump when it was requested to halt |
| - localparam logic [63:0] HaltAddress = 64'h800; |
| - localparam logic [63:0] ResumeAddress = HaltAddress + 4; |
| - localparam logic [63:0] ExceptionAddress = HaltAddress + 8; |
| - |
| - // address where data0-15 is shadowed or if shadowed in a CSR |
| - // address of the first CSR used for shadowing the data |
| - localparam logic [11:0] DataAddr = 12'h380; // we are aligned with Rocket here |
| - |
| - // debug registers |
| - typedef enum logic [7:0] { |
| - Data0 = 8'h04, |
| - Data1 = 8'h05, |
| - Data2 = 8'h06, |
| - Data3 = 8'h07, |
| - Data4 = 8'h08, |
| - Data5 = 8'h09, |
| - Data6 = 8'h0A, |
| - Data7 = 8'h0B, |
| - Data8 = 8'h0C, |
| - Data9 = 8'h0D, |
| - Data10 = 8'h0E, |
| - Data11 = 8'h0F, |
| - DMControl = 8'h10, |
| - DMStatus = 8'h11, // r/o |
| - Hartinfo = 8'h12, |
| - HaltSum1 = 8'h13, |
| - HAWindowSel = 8'h14, |
| - HAWindow = 8'h15, |
| - AbstractCS = 8'h16, |
| - Command = 8'h17, |
| - AbstractAuto = 8'h18, |
| - DevTreeAddr0 = 8'h19, |
| - DevTreeAddr1 = 8'h1A, |
| - DevTreeAddr2 = 8'h1B, |
| - DevTreeAddr3 = 8'h1C, |
| - NextDM = 8'h1D, |
| - ProgBuf0 = 8'h20, |
| - ProgBuf15 = 8'h2F, |
| - AuthData = 8'h30, |
| - HaltSum2 = 8'h34, |
| - HaltSum3 = 8'h35, |
| - SBAddress3 = 8'h37, |
| - SBCS = 8'h38, |
| - SBAddress0 = 8'h39, |
| - SBAddress1 = 8'h3A, |
| - SBAddress2 = 8'h3B, |
| - SBData0 = 8'h3C, |
| - SBData1 = 8'h3D, |
| - SBData2 = 8'h3E, |
| - SBData3 = 8'h3F, |
| - HaltSum0 = 8'h40 |
| - } dm_csr_e; |
| - |
| - // debug causes |
| - localparam logic [2:0] CauseBreakpoint = 3'h1; |
| - localparam logic [2:0] CauseTrigger = 3'h2; |
| - localparam logic [2:0] CauseRequest = 3'h3; |
| - localparam logic [2:0] CauseSingleStep = 3'h4; |
| - |
| - typedef struct packed { |
| - logic [31:23] zero1; |
| - logic impebreak; |
| - logic [21:20] zero0; |
| - logic allhavereset; |
| - logic anyhavereset; |
| - logic allresumeack; |
| - logic anyresumeack; |
| - logic allnonexistent; |
| - logic anynonexistent; |
| - logic allunavail; |
| - logic anyunavail; |
| - logic allrunning; |
| - logic anyrunning; |
| - logic allhalted; |
| - logic anyhalted; |
| - logic authenticated; |
| - logic authbusy; |
| - logic hasresethaltreq; |
| - logic devtreevalid; |
| - logic [3:0] version; |
| - } dmstatus_t; |
| - |
| - typedef struct packed { |
| - logic haltreq; |
| - logic resumereq; |
| - logic hartreset; |
| - logic ackhavereset; |
| - logic zero1; |
| - logic hasel; |
| - logic [25:16] hartsello; |
| - logic [15:6] hartselhi; |
| - logic [5:4] zero0; |
| - logic setresethaltreq; |
| - logic clrresethaltreq; |
| - logic ndmreset; |
| - logic dmactive; |
| - } dmcontrol_t; |
| - |
| - typedef struct packed { |
| - logic [31:24] zero1; |
| - logic [23:20] nscratch; |
| - logic [19:17] zero0; |
| - logic dataaccess; |
| - logic [15:12] datasize; |
| - logic [11:0] dataaddr; |
| - } hartinfo_t; |
| - |
| - typedef enum logic [2:0] { CmdErrNone, CmdErrBusy, CmdErrNotSupported, |
| - CmdErrorException, CmdErrorHaltResume, |
| - CmdErrorBus, CmdErrorOther = 7 |
| - } cmderr_e; |
| - |
| - typedef struct packed { |
| - logic [31:29] zero3; |
| - logic [28:24] progbufsize; |
| - logic [23:13] zero2; |
| - logic busy; |
| - logic zero1; |
| - cmderr_e cmderr; |
| - logic [7:4] zero0; |
| - logic [3:0] datacount; |
| - } abstractcs_t; |
| - |
| - typedef enum logic [7:0] { |
| - AccessRegister = 8'h0, |
| - QuickAccess = 8'h1, |
| - AccessMemory = 8'h2 |
| - } cmd_e; |
| - |
| - typedef struct packed { |
| - cmd_e cmdtype; |
| - logic [23:0] control; |
| - } command_t; |
| - |
| - typedef struct packed { |
| - logic [31:16] autoexecprogbuf; |
| - logic [15:12] zero0; |
| - logic [11:0] autoexecdata; |
| - } abstractauto_t; |
| - |
| - typedef struct packed { |
| - logic zero1; |
| - logic [22:20] aarsize; |
| - logic aarpostincrement; |
| - logic postexec; |
| - logic transfer; |
| - logic write; |
| - logic [15:0] regno; |
| - } ac_ar_cmd_t; |
| - |
| - // DTM |
| - typedef enum logic [1:0] { |
| - DTM_NOP = 2'h0, |
| - DTM_READ = 2'h1, |
| - DTM_WRITE = 2'h2 |
| - } dtm_op_e; |
| - |
| - typedef struct packed { |
| - logic [31:29] sbversion; |
| - logic [28:23] zero0; |
| - logic sbbusyerror; |
| - logic sbbusy; |
| - logic sbreadonaddr; |
| - logic [19:17] sbaccess; |
| - logic sbautoincrement; |
| - logic sbreadondata; |
| - logic [14:12] sberror; |
| - logic [11:5] sbasize; |
| - logic sbaccess128; |
| - logic sbaccess64; |
| - logic sbaccess32; |
| - logic sbaccess16; |
| - logic sbaccess8; |
| - } sbcs_t; |
| - |
| - localparam logic[1:0] DTM_SUCCESS = 2'h0; |
| - |
| - typedef struct packed { |
| - logic [6:0] addr; |
| - dtm_op_e op; |
| - logic [31:0] data; |
| - } dmi_req_t; |
| - |
| - typedef struct packed { |
| - logic [31:0] data; |
| - logic [1:0] resp; |
| - } dmi_resp_t; |
| - |
| - // privilege levels |
| - typedef enum logic[1:0] { |
| - PRIV_LVL_M = 2'b11, |
| - PRIV_LVL_S = 2'b01, |
| - PRIV_LVL_U = 2'b00 |
| - } priv_lvl_t; |
| - |
| - // debugregs in core |
| - typedef struct packed { |
| - logic [31:28] xdebugver; |
| - logic [27:16] zero2; |
| - logic ebreakm; |
| - logic zero1; |
| - logic ebreaks; |
| - logic ebreaku; |
| - logic stepie; |
| - logic stopcount; |
| - logic stoptime; |
| - logic [8:6] cause; |
| - logic zero0; |
| - logic mprven; |
| - logic nmip; |
| - logic step; |
| - priv_lvl_t prv; |
| - } dcsr_t; |
| - |
| - // CSRs |
| - typedef enum logic [11:0] { |
| - // Floating-Point CSRs |
| - CSR_FFLAGS = 12'h001, |
| - CSR_FRM = 12'h002, |
| - CSR_FCSR = 12'h003, |
| - CSR_FTRAN = 12'h800, |
| - // Supervisor Mode CSRs |
| - CSR_SSTATUS = 12'h100, |
| - CSR_SIE = 12'h104, |
| - CSR_STVEC = 12'h105, |
| - CSR_SCOUNTEREN = 12'h106, |
| - CSR_SSCRATCH = 12'h140, |
| - CSR_SEPC = 12'h141, |
| - CSR_SCAUSE = 12'h142, |
| - CSR_STVAL = 12'h143, |
| - CSR_SIP = 12'h144, |
| - CSR_SATP = 12'h180, |
| - // Machine Mode CSRs |
| - CSR_MSTATUS = 12'h300, |
| - CSR_MISA = 12'h301, |
| - CSR_MEDELEG = 12'h302, |
| - CSR_MIDELEG = 12'h303, |
| - CSR_MIE = 12'h304, |
| - CSR_MTVEC = 12'h305, |
| - CSR_MCOUNTEREN = 12'h306, |
| - CSR_MSCRATCH = 12'h340, |
| - CSR_MEPC = 12'h341, |
| - CSR_MCAUSE = 12'h342, |
| - CSR_MTVAL = 12'h343, |
| - CSR_MIP = 12'h344, |
| - CSR_PMPCFG0 = 12'h3A0, |
| - CSR_PMPADDR0 = 12'h3B0, |
| - CSR_MVENDORID = 12'hF11, |
| - CSR_MARCHID = 12'hF12, |
| - CSR_MIMPID = 12'hF13, |
| - CSR_MHARTID = 12'hF14, |
| - CSR_MCYCLE = 12'hB00, |
| - CSR_MINSTRET = 12'hB02, |
| - CSR_DCACHE = 12'h701, |
| - CSR_ICACHE = 12'h700, |
| - |
| - CSR_TSELECT = 12'h7A0, |
| - CSR_TDATA1 = 12'h7A1, |
| - CSR_TDATA2 = 12'h7A2, |
| - CSR_TDATA3 = 12'h7A3, |
| - CSR_TINFO = 12'h7A4, |
| - |
| - // Debug CSR |
| - CSR_DCSR = 12'h7b0, |
| - CSR_DPC = 12'h7b1, |
| - CSR_DSCRATCH0 = 12'h7b2, // optional |
| - CSR_DSCRATCH1 = 12'h7b3, // optional |
| - |
| - // Counters and Timers |
| - CSR_CYCLE = 12'hC00, |
| - CSR_TIME = 12'hC01, |
| - CSR_INSTRET = 12'hC02 |
| - } csr_reg_t; |
| - |
| - |
| - // Instruction Generation Helpers |
| - function automatic logic [31:0] jal (logic[4:0] rd, logic [20:0] imm); |
| - // OpCode Jal |
| - return {imm[20], imm[10:1], imm[11], imm[19:12], rd, 7'h6f}; |
| - endfunction |
| - |
| - function automatic logic [31:0] jalr (logic[4:0] rd, logic[4:0] rs1, logic [11:0] offset); |
| - // OpCode Jal |
| - return {offset[11:0], rs1, 3'b0, rd, 7'h67}; |
| - endfunction |
| - |
| - function automatic logic [31:0] andi (logic[4:0] rd, logic[4:0] rs1, logic [11:0] imm); |
| - // OpCode andi |
| - return {imm[11:0], rs1, 3'h7, rd, 7'h13}; |
| - endfunction |
| - |
| - function automatic logic [31:0] slli (logic[4:0] rd, logic[4:0] rs1, logic [5:0] shamt); |
| - // OpCode slli |
| - return {6'b0, shamt[5:0], rs1, 3'h1, rd, 7'h13}; |
| - endfunction |
| - |
| - function automatic logic [31:0] srli (logic[4:0] rd, logic[4:0] rs1, logic [5:0] shamt); |
| - // OpCode srli |
| - return {6'b0, shamt[5:0], rs1, 3'h5, rd, 7'h13}; |
| - endfunction |
| - |
| - function automatic logic [31:0] load (logic [2:0] size, logic[4:0] dest, logic[4:0] base, logic [11:0] offset); |
| - // OpCode Load |
| - return {offset[11:0], base, size, dest, 7'h03}; |
| - endfunction |
| - |
| - function automatic logic [31:0] auipc (logic[4:0] rd, logic [20:0] imm); |
| - // OpCode Auipc |
| - return {imm[20], imm[10:1], imm[11], imm[19:12], rd, 7'h17}; |
| - endfunction |
| - |
| - function automatic logic [31:0] store (logic [2:0] size, logic[4:0] src, logic[4:0] base, logic [11:0] offset); |
| - // OpCode Store |
| - return {offset[11:5], src, base, size, offset[4:0], 7'h23}; |
| - endfunction |
| - |
| - function automatic logic [31:0] float_load (logic [2:0] size, logic[4:0] dest, logic[4:0] base, logic [11:0] offset); |
| - // OpCode Load |
| - return {offset[11:0], base, size, dest, 7'b00_001_11}; |
| - endfunction |
| - |
| - function automatic logic [31:0] float_store (logic [2:0] size, logic[4:0] src, logic[4:0] base, logic [11:0] offset); |
| - // OpCode Store |
| - return {offset[11:5], src, base, size, offset[4:0], 7'b01_001_11}; |
| - endfunction |
| - |
| - function automatic logic [31:0] csrw (csr_reg_t csr, logic[4:0] rs1); |
| - // CSRRW, rd, OpCode System |
| - return {csr, rs1, 3'h1, 5'h0, 7'h73}; |
| - endfunction |
| - |
| - function automatic logic [31:0] csrr (csr_reg_t csr, logic [4:0] dest); |
| - // rs1, CSRRS, rd, OpCode System |
| - return {csr, 5'h0, 3'h2, dest, 7'h73}; |
| - endfunction |
| - |
| - function automatic logic [31:0] branch(logic [4:0] src2, logic [4:0] src1, logic [2:0] funct3, logic [11:0] offset); |
| - // OpCode Branch |
| - return {offset[11], offset[9:4], src2, src1, funct3, offset[3:0], offset[10], 7'b11_000_11}; |
| - endfunction |
| - |
| - function automatic logic [31:0] ebreak (); |
| - return 32'h00100073; |
| - endfunction |
| - |
| - function automatic logic [31:0] wfi (); |
| - return 32'h10500073; |
| - endfunction |
| - |
| - function automatic logic [31:0] nop (); |
| - return 32'h00000013; |
| - endfunction |
| - |
| - function automatic logic [31:0] illegal (); |
| - return 32'h00000000; |
| - endfunction |
| - |
| + localparam logic [3:0] DbgVersion013 = 4'h2; |
| + // size of program buffer in junks of 32-bit words |
| + localparam logic [4:0] ProgBufSize = 5'h8; |
| + |
| + // amount of data count registers implemented |
| + localparam logic [3:0] DataCount = 4'h2; |
| + |
| + // address to which a hart should jump when it was requested to halt |
| + localparam logic [63:0] HaltAddress = 64'h800; |
| + localparam logic [63:0] ResumeAddress = HaltAddress + 4; |
| + localparam logic [63:0] ExceptionAddress = HaltAddress + 8; |
| + |
| + // address where data0-15 is shadowed or if shadowed in a CSR |
| + // address of the first CSR used for shadowing the data |
| + localparam logic [11:0] DataAddr = 12'h380; // we are aligned with Rocket here |
| + |
| + // debug registers |
| + typedef enum logic [7:0] { |
| + Data0 = 8'h04, |
| + Data1 = 8'h05, |
| + Data2 = 8'h06, |
| + Data3 = 8'h07, |
| + Data4 = 8'h08, |
| + Data5 = 8'h09, |
| + Data6 = 8'h0A, |
| + Data7 = 8'h0B, |
| + Data8 = 8'h0C, |
| + Data9 = 8'h0D, |
| + Data10 = 8'h0E, |
| + Data11 = 8'h0F, |
| + DMControl = 8'h10, |
| + DMStatus = 8'h11, // r/o |
| + Hartinfo = 8'h12, |
| + HaltSum1 = 8'h13, |
| + HAWindowSel = 8'h14, |
| + HAWindow = 8'h15, |
| + AbstractCS = 8'h16, |
| + Command = 8'h17, |
| + AbstractAuto = 8'h18, |
| + DevTreeAddr0 = 8'h19, |
| + DevTreeAddr1 = 8'h1A, |
| + DevTreeAddr2 = 8'h1B, |
| + DevTreeAddr3 = 8'h1C, |
| + NextDM = 8'h1D, |
| + ProgBuf0 = 8'h20, |
| + ProgBuf15 = 8'h2F, |
| + AuthData = 8'h30, |
| + HaltSum2 = 8'h34, |
| + HaltSum3 = 8'h35, |
| + SBAddress3 = 8'h37, |
| + SBCS = 8'h38, |
| + SBAddress0 = 8'h39, |
| + SBAddress1 = 8'h3A, |
| + SBAddress2 = 8'h3B, |
| + SBData0 = 8'h3C, |
| + SBData1 = 8'h3D, |
| + SBData2 = 8'h3E, |
| + SBData3 = 8'h3F, |
| + HaltSum0 = 8'h40 |
| + } dm_csr_e; |
| + |
| + // debug causes |
| + localparam logic [2:0] CauseBreakpoint = 3'h1; |
| + localparam logic [2:0] CauseTrigger = 3'h2; |
| + localparam logic [2:0] CauseRequest = 3'h3; |
| + localparam logic [2:0] CauseSingleStep = 3'h4; |
| + |
| + typedef struct packed { |
| + logic [31:23] zero1; |
| + logic impebreak; |
| + logic [21:20] zero0; |
| + logic allhavereset; |
| + logic anyhavereset; |
| + logic allresumeack; |
| + logic anyresumeack; |
| + logic allnonexistent; |
| + logic anynonexistent; |
| + logic allunavail; |
| + logic anyunavail; |
| + logic allrunning; |
| + logic anyrunning; |
| + logic allhalted; |
| + logic anyhalted; |
| + logic authenticated; |
| + logic authbusy; |
| + logic hasresethaltreq; |
| + logic devtreevalid; |
| + logic [3:0] version; |
| + } dmstatus_t; |
| + |
| + typedef struct packed { |
| + logic haltreq; |
| + logic resumereq; |
| + logic hartreset; |
| + logic ackhavereset; |
| + logic zero1; |
| + logic hasel; |
| + logic [25:16] hartsello; |
| + logic [15:6] hartselhi; |
| + logic [5:4] zero0; |
| + logic setresethaltreq; |
| + logic clrresethaltreq; |
| + logic ndmreset; |
| + logic dmactive; |
| + } dmcontrol_t; |
| + |
| + typedef struct packed { |
| + logic [31:24] zero1; |
| + logic [23:20] nscratch; |
| + logic [19:17] zero0; |
| + logic dataaccess; |
| + logic [15:12] datasize; |
| + logic [11:0] dataaddr; |
| + } hartinfo_t; |
| + |
| + typedef enum logic [2:0] { |
| + CmdErrNone, CmdErrBusy, CmdErrNotSupported, |
| + CmdErrorException, CmdErrorHaltResume, |
| + CmdErrorBus, CmdErrorOther = 7 |
| + } cmderr_e; |
| + |
| + typedef struct packed { |
| + logic [31:29] zero3; |
| + logic [28:24] progbufsize; |
| + logic [23:13] zero2; |
| + logic busy; |
| + logic zero1; |
| + cmderr_e cmderr; |
| + logic [7:4] zero0; |
| + logic [3:0] datacount; |
| + } abstractcs_t; |
| + |
| + typedef enum logic [7:0] { |
| + AccessRegister = 8'h0, |
| + QuickAccess = 8'h1, |
| + AccessMemory = 8'h2 |
| + } cmd_e; |
| + |
| + typedef struct packed { |
| + cmd_e cmdtype; |
| + logic [23:0] control; |
| + } command_t; |
| + |
| + typedef struct packed { |
| + logic [31:16] autoexecprogbuf; |
| + logic [15:12] zero0; |
| + logic [11:0] autoexecdata; |
| + } abstractauto_t; |
| + |
| + typedef struct packed { |
| + logic zero1; |
| + logic [22:20] aarsize; |
| + logic aarpostincrement; |
| + logic postexec; |
| + logic transfer; |
| + logic write; |
| + logic [15:0] regno; |
| + } ac_ar_cmd_t; |
| + |
| + // DTM |
| + typedef enum logic [1:0] { |
| + DTM_NOP = 2'h0, |
| + DTM_READ = 2'h1, |
| + DTM_WRITE = 2'h2 |
| + } dtm_op_e; |
| + |
| + typedef struct packed { |
| + logic [31:29] sbversion; |
| + logic [28:23] zero0; |
| + logic sbbusyerror; |
| + logic sbbusy; |
| + logic sbreadonaddr; |
| + logic [19:17] sbaccess; |
| + logic sbautoincrement; |
| + logic sbreadondata; |
| + logic [14:12] sberror; |
| + logic [11:5] sbasize; |
| + logic sbaccess128; |
| + logic sbaccess64; |
| + logic sbaccess32; |
| + logic sbaccess16; |
| + logic sbaccess8; |
| + } sbcs_t; |
| + |
| + localparam logic[1:0] DTM_SUCCESS = 2'h0; |
| + |
| + typedef struct packed { |
| + logic [6:0] addr; |
| + dtm_op_e op; |
| + logic [31:0] data; |
| + } dmi_req_t; |
| + |
| + typedef struct packed { |
| + logic [31:0] data; |
| + logic [1:0] resp; |
| + } dmi_resp_t; |
| + |
| + // privilege levels |
| + typedef enum logic[1:0] { |
| + PRIV_LVL_M = 2'b11, |
| + PRIV_LVL_S = 2'b01, |
| + PRIV_LVL_U = 2'b00 |
| + } priv_lvl_t; |
| + |
| + // debugregs in core |
| + typedef struct packed { |
| + logic [31:28] xdebugver; |
| + logic [27:16] zero2; |
| + logic ebreakm; |
| + logic zero1; |
| + logic ebreaks; |
| + logic ebreaku; |
| + logic stepie; |
| + logic stopcount; |
| + logic stoptime; |
| + logic [8:6] cause; |
| + logic zero0; |
| + logic mprven; |
| + logic nmip; |
| + logic step; |
| + priv_lvl_t prv; |
| + } dcsr_t; |
| + |
| + // CSRs |
| + typedef enum logic [11:0] { |
| + // Floating-Point CSRs |
| + CSR_FFLAGS = 12'h001, |
| + CSR_FRM = 12'h002, |
| + CSR_FCSR = 12'h003, |
| + CSR_FTRAN = 12'h800, |
| + // Supervisor Mode CSRs |
| + CSR_SSTATUS = 12'h100, |
| + CSR_SIE = 12'h104, |
| + CSR_STVEC = 12'h105, |
| + CSR_SCOUNTEREN = 12'h106, |
| + CSR_SSCRATCH = 12'h140, |
| + CSR_SEPC = 12'h141, |
| + CSR_SCAUSE = 12'h142, |
| + CSR_STVAL = 12'h143, |
| + CSR_SIP = 12'h144, |
| + CSR_SATP = 12'h180, |
| + // Machine Mode CSRs |
| + CSR_MSTATUS = 12'h300, |
| + CSR_MISA = 12'h301, |
| + CSR_MEDELEG = 12'h302, |
| + CSR_MIDELEG = 12'h303, |
| + CSR_MIE = 12'h304, |
| + CSR_MTVEC = 12'h305, |
| + CSR_MCOUNTEREN = 12'h306, |
| + CSR_MSCRATCH = 12'h340, |
| + CSR_MEPC = 12'h341, |
| + CSR_MCAUSE = 12'h342, |
| + CSR_MTVAL = 12'h343, |
| + CSR_MIP = 12'h344, |
| + CSR_PMPCFG0 = 12'h3A0, |
| + CSR_PMPADDR0 = 12'h3B0, |
| + CSR_MVENDORID = 12'hF11, |
| + CSR_MARCHID = 12'hF12, |
| + CSR_MIMPID = 12'hF13, |
| + CSR_MHARTID = 12'hF14, |
| + CSR_MCYCLE = 12'hB00, |
| + CSR_MINSTRET = 12'hB02, |
| + CSR_DCACHE = 12'h701, |
| + CSR_ICACHE = 12'h700, |
| + |
| + CSR_TSELECT = 12'h7A0, |
| + CSR_TDATA1 = 12'h7A1, |
| + CSR_TDATA2 = 12'h7A2, |
| + CSR_TDATA3 = 12'h7A3, |
| + CSR_TINFO = 12'h7A4, |
| + |
| + // Debug CSR |
| + CSR_DCSR = 12'h7b0, |
| + CSR_DPC = 12'h7b1, |
| + CSR_DSCRATCH0 = 12'h7b2, // optional |
| + CSR_DSCRATCH1 = 12'h7b3, // optional |
| + |
| + // Counters and Timers |
| + CSR_CYCLE = 12'hC00, |
| + CSR_TIME = 12'hC01, |
| + CSR_INSTRET = 12'hC02 |
| + } csr_reg_t; |
| + |
| + |
| + // Instruction Generation Helpers |
| + function automatic logic [31:0] jal (logic[4:0] rd, logic [20:0] imm); |
| + // OpCode Jal |
| + return {imm[20], imm[10:1], imm[11], imm[19:12], rd, 7'h6f}; |
| + endfunction |
| + |
| + function automatic logic [31:0] jalr (logic[4:0] rd, logic[4:0] rs1, logic [11:0] offset); |
| + // OpCode Jal |
| + return {offset[11:0], rs1, 3'b0, rd, 7'h67}; |
| + endfunction |
| + |
| + function automatic logic [31:0] andi (logic[4:0] rd, logic[4:0] rs1, logic [11:0] imm); |
| + // OpCode andi |
| + return {imm[11:0], rs1, 3'h7, rd, 7'h13}; |
| + endfunction |
| + |
| + function automatic logic [31:0] slli (logic[4:0] rd, logic[4:0] rs1, logic [5:0] shamt); |
| + // OpCode slli |
| + return {6'b0, shamt[5:0], rs1, 3'h1, rd, 7'h13}; |
| + endfunction |
| + |
| + function automatic logic [31:0] srli (logic[4:0] rd, logic[4:0] rs1, logic [5:0] shamt); |
| + // OpCode srli |
| + return {6'b0, shamt[5:0], rs1, 3'h5, rd, 7'h13}; |
| + endfunction |
| + |
| + function automatic logic [31:0] load (logic [2:0] size, logic[4:0] dest, logic[4:0] base, logic [11:0] offset); |
| + // OpCode Load |
| + return {offset[11:0], base, size, dest, 7'h03}; |
| + endfunction |
| + |
| + function automatic logic [31:0] auipc (logic[4:0] rd, logic [20:0] imm); |
| + // OpCode Auipc |
| + return {imm[20], imm[10:1], imm[11], imm[19:12], rd, 7'h17}; |
| + endfunction |
| + |
| + function automatic logic [31:0] store (logic [2:0] size, logic[4:0] src, logic[4:0] base, logic [11:0] offset); |
| + // OpCode Store |
| + return {offset[11:5], src, base, size, offset[4:0], 7'h23}; |
| + endfunction |
| + |
| + function automatic logic [31:0] float_load (logic [2:0] size, logic[4:0] dest, logic[4:0] base, logic [11:0] offset); |
| + // OpCode Load |
| + return {offset[11:0], base, size, dest, 7'b00_001_11}; |
| + endfunction |
| + |
| + function automatic logic [31:0] float_store (logic [2:0] size, logic[4:0] src, logic[4:0] base, logic [11:0] offset); |
| + // OpCode Store |
| + return {offset[11:5], src, base, size, offset[4:0], 7'b01_001_11}; |
| + endfunction |
| + |
| + function automatic logic [31:0] csrw (csr_reg_t csr, logic[4:0] rs1); |
| + // CSRRW, rd, OpCode System |
| + return {csr, rs1, 3'h1, 5'h0, 7'h73}; |
| + endfunction |
| + |
| + function automatic logic [31:0] csrr (csr_reg_t csr, logic [4:0] dest); |
| + // rs1, CSRRS, rd, OpCode System |
| + return {csr, 5'h0, 3'h2, dest, 7'h73}; |
| + endfunction |
| + |
| + function automatic logic [31:0] branch(logic [4:0] src2, logic [4:0] src1, logic [2:0] funct3, logic [11:0] offset); |
| + // OpCode Branch |
| + return {offset[11], offset[9:4], src2, src1, funct3, offset[3:0], offset[10], 7'b11_000_11}; |
| + endfunction |
| + |
| + function automatic logic [31:0] ebreak (); |
| + return 32'h00100073; |
| + endfunction |
| + |
| + function automatic logic [31:0] wfi (); |
| + return 32'h10500073; |
| + endfunction |
| + |
| + function automatic logic [31:0] nop (); |
| + return 32'h00000013; |
| + endfunction |
| + |
| + function automatic logic [31:0] illegal (); |
| + return 32'h00000000; |
| + endfunction |
| |
| endpackage |
| diff --git a/src/dm_sba.sv b/src/dm_sba.sv |
| index f85aa75..fa9d401 100644 |
| --- a/src/dm_sba.sv |
| +++ b/src/dm_sba.sv |
| @@ -1,173 +1,172 @@ |
| /* 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_sba.sv |
| - * Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch> |
| - * Date: 1.8.2018 |
| - * |
| - * Description: System Bus Access Module |
| - * |
| - */ |
| +* 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_sba.sv |
| +* Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch> |
| +* Date: 1.8.2018 |
| +* |
| +* Description: System Bus Access Module |
| +* |
| +*/ |
| module dm_sba #( |
| - parameter int BusWidth = -1 |
| + parameter int BusWidth = -1 |
| ) ( |
| - input logic clk_i, // Clock |
| - input logic rst_ni, |
| - input logic dmactive_i, // synchronous reset active low |
| - |
| - output logic master_req_o, |
| - output logic [BusWidth-1:0] master_add_o, |
| - output logic master_we_o, |
| - output logic [BusWidth-1:0] master_wdata_o, |
| - output logic [BusWidth/8-1:0] master_be_o, |
| - input logic master_gnt_i, |
| - input logic master_r_valid_i, |
| - input logic [BusWidth-1:0] master_r_rdata_i, |
| - |
| - input logic [BusWidth-1:0] sbaddress_i, |
| - input logic sbaddress_write_valid_i, |
| - // control signals in |
| - input logic sbreadonaddr_i, |
| - output logic [BusWidth-1:0] sbaddress_o, |
| - input logic sbautoincrement_i, |
| - input logic [2:0] sbaccess_i, |
| - // data in |
| - input logic sbreadondata_i, |
| - input logic [BusWidth-1:0] sbdata_i, |
| - input logic sbdata_read_valid_i, |
| - input logic sbdata_write_valid_i, |
| - // read data out |
| - output logic [BusWidth-1:0] sbdata_o, |
| - output logic sbdata_valid_o, |
| - // control signals |
| - output logic sbbusy_o, |
| - output logic sberror_valid_o, // bus error occurred |
| - output logic [2:0] sberror_o // bus error occurred |
| + input logic clk_i, // Clock |
| + input logic rst_ni, |
| + input logic dmactive_i, // synchronous reset active low |
| + |
| + output logic master_req_o, |
| + output logic [BusWidth-1:0] master_add_o, |
| + output logic master_we_o, |
| + output logic [BusWidth-1:0] master_wdata_o, |
| + output logic [BusWidth/8-1:0] master_be_o, |
| + input logic master_gnt_i, |
| + input logic master_r_valid_i, |
| + input logic [BusWidth-1:0] master_r_rdata_i, |
| + |
| + input logic [BusWidth-1:0] sbaddress_i, |
| + input logic sbaddress_write_valid_i, |
| + // control signals in |
| + input logic sbreadonaddr_i, |
| + output logic [BusWidth-1:0] sbaddress_o, |
| + input logic sbautoincrement_i, |
| + input logic [2:0] sbaccess_i, |
| + // data in |
| + input logic sbreadondata_i, |
| + input logic [BusWidth-1:0] sbdata_i, |
| + input logic sbdata_read_valid_i, |
| + input logic sbdata_write_valid_i, |
| + // read data out |
| + output logic [BusWidth-1:0] sbdata_o, |
| + output logic sbdata_valid_o, |
| + // control signals |
| + output logic sbbusy_o, |
| + output logic sberror_valid_o, // bus error occurred |
| + output logic [2:0] sberror_o // bus error occurred |
| ); |
| |
| - typedef enum logic [2:0] { Idle, Read, Write, WaitRead, WaitWrite } state_e; |
| - state_e state_d, state_q; |
| - |
| - logic [BusWidth-1:0] address; |
| - logic req; |
| - logic gnt; |
| - logic we; |
| - logic [BusWidth/8-1:0] be; |
| - |
| - assign sbbusy_o = (state_q != Idle) ? 1'b1 : 1'b0; |
| - |
| - always_comb begin |
| - req = 1'b0; |
| - address = sbaddress_i; |
| - we = 1'b0; |
| - be = '0; |
| - |
| - sberror_o = '0; |
| - sberror_valid_o = 1'b0; |
| - sbaddress_o = sbaddress_i; |
| - |
| - state_d = state_q; |
| - |
| - case (state_q) |
| - Idle: begin |
| - // debugger requested a read |
| - if (sbaddress_write_valid_i && sbreadonaddr_i) state_d = Read; |
| - // debugger requested a write |
| - if (sbdata_write_valid_i) state_d = Write; |
| - // perform another read |
| - if (sbdata_read_valid_i && sbreadondata_i) state_d = Read; |
| - end |
| - |
| - Read: begin |
| - req = 1'b1; |
| - if (gnt) state_d = WaitRead; |
| - end |
| - |
| - Write: begin |
| - req = 1'b1; |
| - we = 1'b1; |
| - // generate byte enable mask |
| - case (sbaccess_i) |
| - 3'b000: begin |
| - if (BusWidth == 64) be[ sbaddress_i[2:0]] = '1; |
| - else be[ sbaddress_i[1:0]] = '1; |
| - end |
| - 3'b001: begin |
| - if (BusWidth == 64) be[{sbaddress_i[2:1], 1'b0} +: 2] = '1; |
| - else be[{sbaddress_i[1:1], 1'b0} +: 2] = '1; |
| - end |
| - 3'b010: begin |
| - if (BusWidth == 64) be[{sbaddress_i[2:2], 2'b0} +: 4] = '1; |
| - else be = '1; |
| - end |
| - 3'b011: be = '1; |
| - default:; |
| - endcase |
| - if (gnt) state_d = WaitWrite; |
| - end |
| - |
| - WaitRead: begin |
| - if (sbdata_valid_o) begin |
| - state_d = Idle; |
| - // auto-increment address |
| - if (sbautoincrement_i) sbaddress_o = sbaddress_i + (1'b1 << sbaccess_i); |
| - end |
| - end |
| - |
| - WaitWrite: begin |
| - if (sbdata_valid_o) begin |
| - state_d = Idle; |
| - // auto-increment address |
| - if (sbautoincrement_i) sbaddress_o = sbaddress_i + (1'b1 << sbaccess_i); |
| - end |
| - end |
| - |
| - default:; |
| + typedef enum logic [2:0] { Idle, Read, Write, WaitRead, WaitWrite } state_e; |
| + state_e state_d, state_q; |
| + |
| + logic [BusWidth-1:0] address; |
| + logic req; |
| + logic gnt; |
| + logic we; |
| + logic [BusWidth/8-1:0] be; |
| + |
| + assign sbbusy_o = (state_q != Idle) ? 1'b1 : 1'b0; |
| + |
| + always_comb begin |
| + req = 1'b0; |
| + address = sbaddress_i; |
| + we = 1'b0; |
| + be = '0; |
| + |
| + sberror_o = '0; |
| + sberror_valid_o = 1'b0; |
| + sbaddress_o = sbaddress_i; |
| + |
| + state_d = state_q; |
| + |
| + case (state_q) |
| + Idle: begin |
| + // debugger requested a read |
| + if (sbaddress_write_valid_i && sbreadonaddr_i) state_d = Read; |
| + // debugger requested a write |
| + if (sbdata_write_valid_i) state_d = Write; |
| + // perform another read |
| + if (sbdata_read_valid_i && sbreadondata_i) state_d = Read; |
| + end |
| + |
| + Read: begin |
| + req = 1'b1; |
| + if (gnt) state_d = WaitRead; |
| + end |
| + |
| + Write: begin |
| + req = 1'b1; |
| + we = 1'b1; |
| + // generate byte enable mask |
| + case (sbaccess_i) |
| + 3'b000: begin |
| + if (BusWidth == 64) be[ sbaddress_i[2:0]] = '1; |
| + else be[ sbaddress_i[1:0]] = '1; |
| + end |
| + 3'b001: begin |
| + if (BusWidth == 64) be[{sbaddress_i[2:1], 1'b0} +: 2] = '1; |
| + else be[{sbaddress_i[1:1], 1'b0} +: 2] = '1; |
| + end |
| + 3'b010: begin |
| + if (BusWidth == 64) be[{sbaddress_i[2:2], 2'b0} +: 4] = '1; |
| + else be = '1; |
| + end |
| + 3'b011: be = '1; |
| + default:; |
| endcase |
| - |
| - // handle error case |
| - if (sbaccess_i > 3 && state_q != Idle) begin |
| - req = 1'b0; |
| - state_d = Idle; |
| - sberror_valid_o = 1'b1; |
| - sberror_o = 3'd3; |
| + if (gnt) state_d = WaitWrite; |
| + end |
| + |
| + WaitRead: begin |
| + if (sbdata_valid_o) begin |
| + state_d = Idle; |
| + // auto-increment address |
| + if (sbautoincrement_i) sbaddress_o = sbaddress_i + (1'b1 << sbaccess_i); |
| end |
| - // further error handling should go here ... |
| - end |
| + end |
| |
| - always_ff @(posedge clk_i or negedge rst_ni) begin |
| - if (!rst_ni) begin |
| - state_q <= Idle; |
| - end else begin |
| - state_q <= state_d; |
| + WaitWrite: begin |
| + if (sbdata_valid_o) begin |
| + state_d = Idle; |
| + // auto-increment address |
| + if (sbautoincrement_i) sbaddress_o = sbaddress_i + (1'b1 << sbaccess_i); |
| end |
| - end |
| + end |
| |
| - assign master_req_o = req; |
| - assign master_add_o = address[BusWidth-1:0]; |
| - assign master_we_o = we; |
| - assign master_wdata_o = sbdata_i[BusWidth-1:0]; |
| - assign master_be_o = be[BusWidth/8-1:0]; |
| - assign gnt = master_gnt_i; |
| - assign sbdata_valid_o = master_r_valid_i; |
| - assign sbdata_o = master_r_rdata_i[BusWidth-1:0]; |
| - |
| - |
| - //pragma translate_off |
| - `ifndef VERILATOR |
| - // maybe bump severity to $error if not handled at runtime |
| - dm_sba_access_size: assert property(@(posedge clk_i) disable iff (dmactive_i !== 1'b0) |
| - (state_d != Idle) |-> (sbaccess_i < 4)) |
| - else |
| - $warning ("accesses > 8 byte not supported at the moment"); |
| - `endif |
| - //pragma translate_on |
| + default:; |
| + endcase |
| + |
| + // handle error case |
| + if (sbaccess_i > 3 && state_q != Idle) begin |
| + req = 1'b0; |
| + state_d = Idle; |
| + sberror_valid_o = 1'b1; |
| + sberror_o = 3'd3; |
| + end |
| + // further error handling should go here ... |
| + end |
| + |
| + always_ff @(posedge clk_i or negedge rst_ni) begin |
| + if (!rst_ni) begin |
| + state_q <= Idle; |
| + end else begin |
| + state_q <= state_d; |
| + end |
| + end |
| + |
| + assign master_req_o = req; |
| + assign master_add_o = address[BusWidth-1:0]; |
| + assign master_we_o = we; |
| + assign master_wdata_o = sbdata_i[BusWidth-1:0]; |
| + assign master_be_o = be[BusWidth/8-1:0]; |
| + assign gnt = master_gnt_i; |
| + assign sbdata_valid_o = master_r_valid_i; |
| + assign sbdata_o = master_r_rdata_i[BusWidth-1:0]; |
| + |
| + |
| + //pragma translate_off |
| + `ifndef VERILATOR |
| + // maybe bump severity to $error if not handled at runtime |
| + dm_sba_access_size: assert property(@(posedge clk_i) disable iff (dmactive_i !== 1'b0) |
| + (state_d != Idle) |-> (sbaccess_i < 4)) |
| + else $warning ("accesses > 8 byte not supported at the moment"); |
| + `endif |
| + //pragma translate_on |
| |
| endmodule |
| diff --git a/src/dm_top.sv b/src/dm_top.sv |
| index ad20535..03ac112 100644 |
| --- a/src/dm_top.sv |
| +++ b/src/dm_top.sv |
| @@ -1,222 +1,222 @@ |
| /* 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 |
| - */ |
| +* 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 BusWidth = 32, |
| - parameter logic [NrHarts-1:0] SelectableHarts = 1 // Bitmask to select physically available harts for systems |
| - // that don't use hart numbers in a contiguous fashion. |
| + parameter int NrHarts = 1, |
| + parameter int BusWidth = 32, |
| + parameter logic [NrHarts-1:0] SelectableHarts = 1 // Bitmask to select physically available harts for systems |
| + // that don't use hart numbers in a contiguous fashion. |
| |
| ) ( |
| - 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) |
| - dm::hartinfo_t [NrHarts-1:0] hartinfo_i, |
| - |
| - input logic slave_req_i, |
| - input logic slave_we_i, |
| - input logic [BusWidth-1:0] slave_addr_i, |
| - input logic [BusWidth/8-1:0] slave_be_i, |
| - input logic [BusWidth-1:0] slave_wdata_i, |
| - output logic [BusWidth-1:0] slave_rdata_o, |
| - |
| - output logic master_req_o, |
| - output logic [BusWidth-1:0] master_add_o, |
| - output logic master_we_o, |
| - output logic [BusWidth-1:0] master_wdata_o, |
| - output logic [BusWidth/8-1:0] master_be_o, |
| - input logic master_gnt_i, |
| - input logic master_r_valid_i, |
| - input logic [BusWidth-1:0] master_r_rdata_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 |
| + 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) |
| + dm::hartinfo_t [NrHarts-1:0] hartinfo_i, |
| + |
| + input logic slave_req_i, |
| + input logic slave_we_i, |
| + input logic [BusWidth-1:0] slave_addr_i, |
| + input logic [BusWidth/8-1:0] slave_be_i, |
| + input logic [BusWidth-1:0] slave_wdata_i, |
| + output logic [BusWidth-1:0] slave_rdata_o, |
| + |
| + output logic master_req_o, |
| + output logic [BusWidth-1:0] master_add_o, |
| + output logic master_we_o, |
| + output logic [BusWidth-1:0] master_wdata_o, |
| + output logic [BusWidth/8-1:0] master_be_o, |
| + input logic master_gnt_i, |
| + input logic master_r_valid_i, |
| + input logic [BusWidth-1:0] master_r_rdata_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 |
| - 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 clear_resumeack; |
| - logic cmd_valid; |
| - dm::command_t cmd; |
| - |
| - logic cmderror_valid; |
| - dm::cmderr_e 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 [BusWidth-1:0] sbaddress_csrs_sba; |
| - logic [BusWidth-1:0] sbaddress_sba_csrs; |
| - logic sbaddress_write_valid; |
| - logic sbreadonaddr; |
| - logic sbautoincrement; |
| - logic [2:0] sbaccess; |
| - logic sbreadondata; |
| - logic [BusWidth-1:0] sbdata_write; |
| - logic sbdata_read_valid; |
| - logic sbdata_write_valid; |
| - logic [BusWidth-1:0] sbdata_read; |
| - logic sbdata_valid; |
| - logic sbbusy; |
| - logic sberror_valid; |
| - logic [2:0] sberror; |
| - |
| - |
| - dm_csrs #( |
| - .NrHarts(NrHarts), |
| - .BusWidth(BusWidth), |
| - .SelectableHarts(SelectableHarts) |
| - ) 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_i ), |
| - .halted_i ( halted ), |
| - .unavailable_i, |
| - .resumeack_i ( resumeack ), |
| - .haltreq_o ( haltreq ), |
| - .resumereq_o ( resumereq ), |
| - .clear_resumeack_o ( clear_resumeack ), |
| - .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 #( |
| - .BusWidth(BusWidth) |
| - ) i_dm_sba ( |
| - .clk_i ( clk_i ), |
| - .rst_ni ( rst_ni ), |
| - .dmactive_i ( dmactive_o ), |
| - |
| - .master_req_o ( master_req_o ), |
| - .master_add_o ( master_add_o ), |
| - .master_we_o ( master_we_o ), |
| - .master_wdata_o ( master_wdata_o ), |
| - .master_be_o ( master_be_o ), |
| - .master_gnt_i ( master_gnt_i ), |
| - .master_r_valid_i ( master_r_valid_i ), |
| - .master_r_rdata_i ( master_r_rdata_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), |
| - .BusWidth(BusWidth), |
| - .SelectableHarts(SelectableHarts) |
| - ) 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 ), |
| - .clear_resumeack_i ( clear_resumeack ), |
| - .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 ( slave_req_i ), |
| - .we_i ( slave_we_i ), |
| - .addr_i ( slave_addr_i ), |
| - .wdata_i ( slave_wdata_i ), |
| - .be_i ( slave_be_i ), |
| - .rdata_o ( slave_rdata_o ) |
| - ); |
| + // Debug CSRs |
| + 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 clear_resumeack; |
| + logic cmd_valid; |
| + dm::command_t cmd; |
| + |
| + logic cmderror_valid; |
| + dm::cmderr_e 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 [BusWidth-1:0] sbaddress_csrs_sba; |
| + logic [BusWidth-1:0] sbaddress_sba_csrs; |
| + logic sbaddress_write_valid; |
| + logic sbreadonaddr; |
| + logic sbautoincrement; |
| + logic [2:0] sbaccess; |
| + logic sbreadondata; |
| + logic [BusWidth-1:0] sbdata_write; |
| + logic sbdata_read_valid; |
| + logic sbdata_write_valid; |
| + logic [BusWidth-1:0] sbdata_read; |
| + logic sbdata_valid; |
| + logic sbbusy; |
| + logic sberror_valid; |
| + logic [2:0] sberror; |
| + |
| + |
| + dm_csrs #( |
| + .NrHarts(NrHarts), |
| + .BusWidth(BusWidth), |
| + .SelectableHarts(SelectableHarts) |
| + ) 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_i ), |
| + .halted_i ( halted ), |
| + .unavailable_i, |
| + .resumeack_i ( resumeack ), |
| + .haltreq_o ( haltreq ), |
| + .resumereq_o ( resumereq ), |
| + .clear_resumeack_o ( clear_resumeack ), |
| + .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 #( |
| + .BusWidth(BusWidth) |
| + ) i_dm_sba ( |
| + .clk_i ( clk_i ), |
| + .rst_ni ( rst_ni ), |
| + .dmactive_i ( dmactive_o ), |
| + |
| + .master_req_o ( master_req_o ), |
| + .master_add_o ( master_add_o ), |
| + .master_we_o ( master_we_o ), |
| + .master_wdata_o ( master_wdata_o ), |
| + .master_be_o ( master_be_o ), |
| + .master_gnt_i ( master_gnt_i ), |
| + .master_r_valid_i ( master_r_valid_i ), |
| + .master_r_rdata_i ( master_r_rdata_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), |
| + .BusWidth(BusWidth), |
| + .SelectableHarts(SelectableHarts) |
| + ) 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 ), |
| + .clear_resumeack_i ( clear_resumeack ), |
| + .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 ( slave_req_i ), |
| + .we_i ( slave_we_i ), |
| + .addr_i ( slave_addr_i ), |
| + .wdata_i ( slave_wdata_i ), |
| + .be_i ( slave_be_i ), |
| + .rdata_o ( slave_rdata_o ) |
| + ); |
| |
| |
| `ifndef VERILATOR |
| - initial begin |
| - assert (BusWidth == 32 || BusWidth == 64) |
| - else $fatal(1, "DM needs a bus width of either 32 or 64 bits"); |
| - end |
| + initial begin |
| + assert (BusWidth == 32 || BusWidth == 64) |
| + else $fatal(1, "DM needs a bus width of either 32 or 64 bits"); |
| + end |
| `endif |
| |
| endmodule |
| diff --git a/src/dmi_cdc.sv b/src/dmi_cdc.sv |
| index 98b15a8..ba856df 100644 |
| --- a/src/dmi_cdc.sv |
| +++ b/src/dmi_cdc.sv |
| @@ -1,46 +1,46 @@ |
| /* 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: axi_riscv_debug_module.sv |
| - * Author: Andreas Traber <atraber@iis.ee.ethz.ch> |
| - * Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch> |
| - * |
| - * Description: Clock domain crossings for JTAG to DMI very heavily based |
| - * on previous work by Andreas Traber for the PULP project. |
| - * This is mainly a wrapper around the existing CDCs. |
| - */ |
| +* 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: axi_riscv_debug_module.sv |
| +* Author: Andreas Traber <atraber@iis.ee.ethz.ch> |
| +* Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch> |
| +* |
| +* Description: Clock domain crossings for JTAG to DMI very heavily based |
| +* on previous work by Andreas Traber for the PULP project. |
| +* This is mainly a wrapper around the existing CDCs. |
| +*/ |
| module dmi_cdc ( |
| - // JTAG side (master side) |
| - input logic tck_i, |
| - input logic trst_ni, |
| + // JTAG side (master side) |
| + input logic tck_i, |
| + input logic trst_ni, |
| |
| - input dm::dmi_req_t jtag_dmi_req_i, |
| - output logic jtag_dmi_ready_o, |
| - input logic jtag_dmi_valid_i, |
| + input dm::dmi_req_t jtag_dmi_req_i, |
| + output logic jtag_dmi_ready_o, |
| + input logic jtag_dmi_valid_i, |
| |
| - output dm::dmi_resp_t jtag_dmi_resp_o, |
| - output logic jtag_dmi_valid_o, |
| - input logic jtag_dmi_ready_i, |
| + output dm::dmi_resp_t jtag_dmi_resp_o, |
| + output logic jtag_dmi_valid_o, |
| + input logic jtag_dmi_ready_i, |
| |
| - // core side (slave side) |
| - input logic clk_i, |
| - input logic rst_ni, |
| + // core side (slave side) |
| + input logic clk_i, |
| + input logic rst_ni, |
| |
| - output dm::dmi_req_t core_dmi_req_o, |
| - output logic core_dmi_valid_o, |
| - input logic core_dmi_ready_i, |
| + output dm::dmi_req_t core_dmi_req_o, |
| + output logic core_dmi_valid_o, |
| + input logic core_dmi_ready_i, |
| |
| - input dm::dmi_resp_t core_dmi_resp_i, |
| - output logic core_dmi_ready_o, |
| - input logic core_dmi_valid_i |
| - ); |
| + input dm::dmi_resp_t core_dmi_resp_i, |
| + output logic core_dmi_ready_o, |
| + input logic core_dmi_valid_i |
| +); |
| |
| cdc_2phase #(.T(dm::dmi_req_t)) i_cdc_req ( |
| .src_rst_ni ( trst_ni ), |
| diff --git a/src/dmi_jtag.sv b/src/dmi_jtag.sv |
| index f177551..083ed59 100644 |
| --- a/src/dmi_jtag.sv |
| +++ b/src/dmi_jtag.sv |
| @@ -1,262 +1,262 @@ |
| /* 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: axi_riscv_debug_module.sv |
| - * Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch> |
| - * Date: 19.7.2018 |
| - * |
| - * Description: JTAG DMI (debug module interface) |
| - * |
| - */ |
| +* 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: axi_riscv_debug_module.sv |
| +* Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch> |
| +* Date: 19.7.2018 |
| +* |
| +* Description: JTAG DMI (debug module interface) |
| +* |
| +*/ |
| |
| module dmi_jtag #( |
| - parameter logic [31:0] IdcodeValue = 32'h00000001 |
| + parameter logic [31:0] IdcodeValue = 32'h00000001 |
| ) ( |
| - input logic clk_i, // DMI Clock |
| - input logic rst_ni, // Asynchronous reset active low |
| - input logic testmode_i, |
| - |
| - output logic dmi_rst_no, // hard reset |
| - output dm::dmi_req_t dmi_req_o, |
| - output logic dmi_req_valid_o, |
| - input logic dmi_req_ready_i, |
| - |
| - input dm::dmi_resp_t dmi_resp_i, |
| - output logic dmi_resp_ready_o, |
| - input logic dmi_resp_valid_i, |
| - |
| - input logic tck_i, // JTAG test clock pad |
| - input logic tms_i, // JTAG test mode select pad |
| - input logic trst_ni, // JTAG test reset pad |
| - input logic td_i, // JTAG test data input pad |
| - output logic td_o, // JTAG test data output pad |
| - output logic tdo_oe_o // Data out output enable |
| + input logic clk_i, // DMI Clock |
| + input logic rst_ni, // Asynchronous reset active low |
| + input logic testmode_i, |
| + |
| + output logic dmi_rst_no, // hard reset |
| + output dm::dmi_req_t dmi_req_o, |
| + output logic dmi_req_valid_o, |
| + input logic dmi_req_ready_i, |
| + |
| + input dm::dmi_resp_t dmi_resp_i, |
| + output logic dmi_resp_ready_o, |
| + input logic dmi_resp_valid_i, |
| + |
| + input logic tck_i, // JTAG test clock pad |
| + input logic tms_i, // JTAG test mode select pad |
| + input logic trst_ni, // JTAG test reset pad |
| + input logic td_i, // JTAG test data input pad |
| + output logic td_o, // JTAG test data output pad |
| + output logic tdo_oe_o // Data out output enable |
| ); |
| - assign dmi_rst_no = rst_ni; |
| - |
| - logic test_logic_reset; |
| - logic shift_dr; |
| - logic update_dr; |
| - logic capture_dr; |
| - logic dmi_access; |
| - logic dtmcs_select; |
| - logic dmi_reset; |
| - logic dmi_tdi; |
| - logic dmi_tdo; |
| - |
| - dm::dmi_req_t dmi_req; |
| - logic dmi_req_ready; |
| - logic dmi_req_valid; |
| - |
| - dm::dmi_resp_t dmi_resp; |
| - logic dmi_resp_valid; |
| - logic dmi_resp_ready; |
| - |
| - typedef struct packed { |
| - logic [6:0] address; |
| - logic [31:0] data; |
| - logic [1:0] op; |
| - } dmi_t; |
| - |
| - typedef enum logic [1:0] { |
| - DMINoError = 2'h0, DMIReservedError = 2'h1, |
| - DMIOPFailed = 2'h2, DMIBusy = 2'h3 |
| - } dmi_error_e; |
| - |
| - typedef enum logic [2:0] { Idle, Read, WaitReadValid, Write, WaitWriteValid } state_e; |
| - state_e state_d, state_q; |
| - |
| - logic [$bits(dmi_t)-1:0] dr_d, dr_q; |
| - logic [6:0] address_d, address_q; |
| - logic [31:0] data_d, data_q; |
| - |
| - dmi_t dmi; |
| - assign dmi = dmi_t'(dr_q); |
| - assign dmi_req.addr = address_q; |
| - assign dmi_req.data = data_q; |
| - assign dmi_req.op = (state_q == Write) ? dm::DTM_WRITE : dm::DTM_READ; |
| - // we'will always be ready to accept the data we requested |
| - assign dmi_resp_ready = 1'b1; |
| - |
| - logic error_dmi_busy; |
| - dmi_error_e error_d, error_q; |
| - |
| - always_comb begin |
| - error_dmi_busy = 1'b0; |
| - // default assignments |
| - state_d = state_q; |
| - address_d = address_q; |
| - data_d = data_q; |
| - error_d = error_q; |
| - |
| - dmi_req_valid = 1'b0; |
| - |
| - case (state_q) |
| - Idle: begin |
| - // make sure that no error is sticky |
| - if (dmi_access && update_dr && (error_q == DMINoError)) begin |
| - // save address and value |
| - address_d = dmi.address; |
| - data_d = dmi.data; |
| - if (dm::dtm_op_e'(dmi.op) == dm::DTM_READ) begin |
| - state_d = Read; |
| - end else if (dm::dtm_op_e'(dmi.op) == dm::DTM_WRITE) begin |
| - state_d = Write; |
| - end |
| - // else this is a nop and we can stay here |
| - end |
| - end |
| - |
| - Read: begin |
| - dmi_req_valid = 1'b1; |
| - if (dmi_req_ready) begin |
| - state_d = WaitReadValid; |
| - end |
| - end |
| - |
| - WaitReadValid: begin |
| - // load data into register and shift out |
| - if (dmi_resp_valid) begin |
| - data_d = dmi_resp.data; |
| - state_d = Idle; |
| - end |
| - end |
| - |
| - Write: begin |
| - dmi_req_valid = 1'b1; |
| - // got a valid answer go back to idle |
| - if (dmi_req_ready) begin |
| - state_d = Idle; |
| - end |
| - end |
| - |
| - default: begin |
| - // just wait for idle here |
| - if (dmi_resp_valid) begin |
| - state_d = Idle; |
| - end |
| - end |
| - endcase |
| - |
| - // update_dr means we got another request but we didn't finish |
| - // the one in progress, this state is sticky |
| - if (update_dr && state_q != Idle) begin |
| - error_dmi_busy = 1'b1; |
| + assign dmi_rst_no = rst_ni; |
| + |
| + logic test_logic_reset; |
| + logic shift_dr; |
| + logic update_dr; |
| + logic capture_dr; |
| + logic dmi_access; |
| + logic dtmcs_select; |
| + logic dmi_reset; |
| + logic dmi_tdi; |
| + logic dmi_tdo; |
| + |
| + dm::dmi_req_t dmi_req; |
| + logic dmi_req_ready; |
| + logic dmi_req_valid; |
| + |
| + dm::dmi_resp_t dmi_resp; |
| + logic dmi_resp_valid; |
| + logic dmi_resp_ready; |
| + |
| + typedef struct packed { |
| + logic [6:0] address; |
| + logic [31:0] data; |
| + logic [1:0] op; |
| + } dmi_t; |
| + |
| + typedef enum logic [1:0] { |
| + DMINoError = 2'h0, DMIReservedError = 2'h1, |
| + DMIOPFailed = 2'h2, DMIBusy = 2'h3 |
| + } dmi_error_e; |
| + |
| + typedef enum logic [2:0] { Idle, Read, WaitReadValid, Write, WaitWriteValid } state_e; |
| + state_e state_d, state_q; |
| + |
| + logic [$bits(dmi_t)-1:0] dr_d, dr_q; |
| + logic [6:0] address_d, address_q; |
| + logic [31:0] data_d, data_q; |
| + |
| + dmi_t dmi; |
| + assign dmi = dmi_t'(dr_q); |
| + assign dmi_req.addr = address_q; |
| + assign dmi_req.data = data_q; |
| + assign dmi_req.op = (state_q == Write) ? dm::DTM_WRITE : dm::DTM_READ; |
| + // we'will always be ready to accept the data we requested |
| + assign dmi_resp_ready = 1'b1; |
| + |
| + logic error_dmi_busy; |
| + dmi_error_e error_d, error_q; |
| + |
| + always_comb begin |
| + error_dmi_busy = 1'b0; |
| + // default assignments |
| + state_d = state_q; |
| + address_d = address_q; |
| + data_d = data_q; |
| + error_d = error_q; |
| + |
| + dmi_req_valid = 1'b0; |
| + |
| + case (state_q) |
| + Idle: begin |
| + // make sure that no error is sticky |
| + if (dmi_access && update_dr && (error_q == DMINoError)) begin |
| + // save address and value |
| + address_d = dmi.address; |
| + data_d = dmi.data; |
| + if (dm::dtm_op_e'(dmi.op) == dm::DTM_READ) begin |
| + state_d = Read; |
| + end else if (dm::dtm_op_e'(dmi.op) == dm::DTM_WRITE) begin |
| + state_d = Write; |
| + end |
| + // else this is a nop and we can stay here |
| end |
| + end |
| |
| - // if capture_dr goes high while we are in the read state |
| - // or in the corresponding wait state we are not giving back a valid word |
| - // -> throw an error |
| - if (capture_dr && state_q inside {Read, WaitReadValid}) begin |
| - error_dmi_busy = 1'b1; |
| + Read: begin |
| + dmi_req_valid = 1'b1; |
| + if (dmi_req_ready) begin |
| + state_d = WaitReadValid; |
| end |
| + end |
| |
| - if (error_dmi_busy) begin |
| - error_d = DMIBusy; |
| + WaitReadValid: begin |
| + // load data into register and shift out |
| + if (dmi_resp_valid) begin |
| + data_d = dmi_resp.data; |
| + state_d = Idle; |
| end |
| - // clear sticky error flag |
| - if (dmi_reset && dtmcs_select) begin |
| - error_d = DMINoError; |
| - end |
| - end |
| + end |
| |
| - // shift register |
| - assign dmi_tdo = dr_q[0]; |
| - |
| - always_comb begin |
| - dr_d = dr_q; |
| - |
| - if (capture_dr) begin |
| - if (dmi_access) begin |
| - if (error_q == DMINoError && !error_dmi_busy) begin |
| - dr_d = {address_q, data_q, DMINoError}; |
| - // DMI was busy, report an error |
| - end else if (error_q == DMIBusy || error_dmi_busy) begin |
| - dr_d = {address_q, data_q, DMIBusy}; |
| - end |
| - end |
| + Write: begin |
| + dmi_req_valid = 1'b1; |
| + // got a valid answer go back to idle |
| + if (dmi_req_ready) begin |
| + state_d = Idle; |
| end |
| + end |
| |
| - if (shift_dr) begin |
| - if (dmi_access) dr_d = {dmi_tdi, dr_q[$bits(dr_q)-1:1]}; |
| + default: begin |
| + // just wait for idle here |
| + if (dmi_resp_valid) begin |
| + state_d = Idle; |
| end |
| + end |
| + endcase |
| |
| - if (test_logic_reset) begin |
| - dr_d = '0; |
| - end |
| + // update_dr means we got another request but we didn't finish |
| + // the one in progress, this state is sticky |
| + if (update_dr && state_q != Idle) begin |
| + error_dmi_busy = 1'b1; |
| + end |
| + |
| + // if capture_dr goes high while we are in the read state |
| + // or in the corresponding wait state we are not giving back a valid word |
| + // -> throw an error |
| + if (capture_dr && state_q inside {Read, WaitReadValid}) begin |
| + error_dmi_busy = 1'b1; |
| + end |
| + |
| + if (error_dmi_busy) begin |
| + error_d = DMIBusy; |
| end |
| + // clear sticky error flag |
| + if (dmi_reset && dtmcs_select) begin |
| + error_d = DMINoError; |
| + end |
| + end |
| + |
| + // shift register |
| + assign dmi_tdo = dr_q[0]; |
| |
| - always_ff @(posedge tck_i or negedge trst_ni) begin |
| - if (!trst_ni) begin |
| - dr_q <= '0; |
| - state_q <= Idle; |
| - address_q <= '0; |
| - data_q <= '0; |
| - error_q <= DMINoError; |
| - end else begin |
| - dr_q <= dr_d; |
| - state_q <= state_d; |
| - address_q <= address_d; |
| - data_q <= data_d; |
| - error_q <= error_d; |
| + always_comb begin |
| + dr_d = dr_q; |
| + |
| + if (capture_dr) begin |
| + if (dmi_access) begin |
| + if (error_q == DMINoError && !error_dmi_busy) begin |
| + dr_d = {address_q, data_q, DMINoError}; |
| + // DMI was busy, report an error |
| + end else if (error_q == DMIBusy || error_dmi_busy) begin |
| + dr_d = {address_q, data_q, DMIBusy}; |
| end |
| + end |
| end |
| |
| - // --------- |
| - // TAP |
| - // --------- |
| - dmi_jtag_tap #( |
| - .IrLength (5), |
| - .IdcodeValue(IdcodeValue) |
| - ) i_dmi_jtag_tap ( |
| - .tck_i, |
| - .tms_i, |
| - .trst_ni, |
| - .td_i, |
| - .td_o, |
| - .tdo_oe_o, |
| - .testmode_i ( testmode_i ), |
| - .test_logic_reset_o ( test_logic_reset ), |
| - .shift_dr_o ( shift_dr ), |
| - .update_dr_o ( update_dr ), |
| - .capture_dr_o ( capture_dr ), |
| - .dmi_access_o ( dmi_access ), |
| - .dtmcs_select_o ( dtmcs_select ), |
| - .dmi_reset_o ( dmi_reset ), |
| - .dmi_error_i ( error_q ), |
| - .dmi_tdi_o ( dmi_tdi ), |
| - .dmi_tdo_i ( dmi_tdo ) |
| - ); |
| - |
| - // --------- |
| - // CDC |
| - // --------- |
| - dmi_cdc i_dmi_cdc ( |
| - // JTAG side (master side) |
| - .tck_i, |
| - .trst_ni, |
| - .jtag_dmi_req_i ( dmi_req ), |
| - .jtag_dmi_ready_o ( dmi_req_ready ), |
| - .jtag_dmi_valid_i ( dmi_req_valid ), |
| - .jtag_dmi_resp_o ( dmi_resp ), |
| - .jtag_dmi_valid_o ( dmi_resp_valid ), |
| - .jtag_dmi_ready_i ( dmi_resp_ready ), |
| - // core side |
| - .clk_i, |
| - .rst_ni, |
| - .core_dmi_req_o ( dmi_req_o ), |
| - .core_dmi_valid_o ( dmi_req_valid_o ), |
| - .core_dmi_ready_i ( dmi_req_ready_i ), |
| - .core_dmi_resp_i ( dmi_resp_i ), |
| - .core_dmi_ready_o ( dmi_resp_ready_o ), |
| - .core_dmi_valid_i ( dmi_resp_valid_i ) |
| - ); |
| + if (shift_dr) begin |
| + if (dmi_access) dr_d = {dmi_tdi, dr_q[$bits(dr_q)-1:1]}; |
| + end |
| + |
| + if (test_logic_reset) begin |
| + dr_d = '0; |
| + end |
| + end |
| + |
| + always_ff @(posedge tck_i or negedge trst_ni) begin |
| + if (!trst_ni) begin |
| + dr_q <= '0; |
| + state_q <= Idle; |
| + address_q <= '0; |
| + data_q <= '0; |
| + error_q <= DMINoError; |
| + end else begin |
| + dr_q <= dr_d; |
| + state_q <= state_d; |
| + address_q <= address_d; |
| + data_q <= data_d; |
| + error_q <= error_d; |
| + end |
| + end |
| + |
| + // --------- |
| + // TAP |
| + // --------- |
| + dmi_jtag_tap #( |
| + .IrLength (5), |
| + .IdcodeValue(IdcodeValue) |
| + ) i_dmi_jtag_tap ( |
| + .tck_i, |
| + .tms_i, |
| + .trst_ni, |
| + .td_i, |
| + .td_o, |
| + .tdo_oe_o, |
| + .testmode_i ( testmode_i ), |
| + .test_logic_reset_o ( test_logic_reset ), |
| + .shift_dr_o ( shift_dr ), |
| + .update_dr_o ( update_dr ), |
| + .capture_dr_o ( capture_dr ), |
| + .dmi_access_o ( dmi_access ), |
| + .dtmcs_select_o ( dtmcs_select ), |
| + .dmi_reset_o ( dmi_reset ), |
| + .dmi_error_i ( error_q ), |
| + .dmi_tdi_o ( dmi_tdi ), |
| + .dmi_tdo_i ( dmi_tdo ) |
| + ); |
| + |
| + // --------- |
| + // CDC |
| + // --------- |
| + dmi_cdc i_dmi_cdc ( |
| + // JTAG side (master side) |
| + .tck_i, |
| + .trst_ni, |
| + .jtag_dmi_req_i ( dmi_req ), |
| + .jtag_dmi_ready_o ( dmi_req_ready ), |
| + .jtag_dmi_valid_i ( dmi_req_valid ), |
| + .jtag_dmi_resp_o ( dmi_resp ), |
| + .jtag_dmi_valid_o ( dmi_resp_valid ), |
| + .jtag_dmi_ready_i ( dmi_resp_ready ), |
| + // core side |
| + .clk_i, |
| + .rst_ni, |
| + .core_dmi_req_o ( dmi_req_o ), |
| + .core_dmi_valid_o ( dmi_req_valid_o ), |
| + .core_dmi_ready_i ( dmi_req_ready_i ), |
| + .core_dmi_resp_i ( dmi_resp_i ), |
| + .core_dmi_ready_o ( dmi_resp_ready_o ), |
| + .core_dmi_valid_i ( dmi_resp_valid_i ) |
| + ); |
| |
| endmodule |
| diff --git a/src/dmi_jtag_tap.sv b/src/dmi_jtag_tap.sv |
| index cab01ca..19d876f 100644 |
| --- a/src/dmi_jtag_tap.sv |
| +++ b/src/dmi_jtag_tap.sv |
| @@ -17,327 +17,328 @@ |
| */ |
| |
| module dmi_jtag_tap #( |
| - parameter int IrLength = 5, |
| - // JTAG IDCODE Value |
| - parameter logic [31:0] IdcodeValue = 32'h00000001 |
| - // xxxx version |
| - // xxxxxxxxxxxxxxxx part number |
| - // xxxxxxxxxxx manufacturer id |
| - // 1 required by standard |
| -)( |
| - input logic tck_i, // JTAG test clock pad |
| - input logic tms_i, // JTAG test mode select pad |
| - input logic trst_ni, // JTAG test reset pad |
| - input logic td_i, // JTAG test data input pad |
| - output logic td_o, // JTAG test data output pad |
| - output logic tdo_oe_o, // Data out output enable |
| - input logic testmode_i, |
| - output logic test_logic_reset_o, |
| - output logic shift_dr_o, |
| - output logic update_dr_o, |
| - output logic capture_dr_o, |
| - |
| - // we want to access DMI register |
| - output logic dmi_access_o, |
| - // JTAG is interested in writing the DTM CSR register |
| - output logic dtmcs_select_o, |
| - // clear error state |
| - output logic dmi_reset_o, |
| - input logic [1:0] dmi_error_i, |
| - // test data to submodule |
| - output logic dmi_tdi_o, |
| - // test data in from submodule |
| - input logic dmi_tdo_i |
| - |
| + parameter int IrLength = 5, |
| + // JTAG IDCODE Value |
| + parameter logic [31:0] IdcodeValue = 32'h00000001 |
| + // xxxx version |
| + // xxxxxxxxxxxxxxxx part number |
| + // xxxxxxxxxxx manufacturer id |
| + // 1 required by standard |
| +) ( |
| + input logic tck_i, // JTAG test clock pad |
| + input logic tms_i, // JTAG test mode select pad |
| + input logic trst_ni, // JTAG test reset pad |
| + input logic td_i, // JTAG test data input pad |
| + output logic td_o, // JTAG test data output pad |
| + output logic tdo_oe_o, // Data out output enable |
| + input logic testmode_i, |
| + output logic test_logic_reset_o, |
| + output logic shift_dr_o, |
| + output logic update_dr_o, |
| + output logic capture_dr_o, |
| + |
| + // we want to access DMI register |
| + output logic dmi_access_o, |
| + // JTAG is interested in writing the DTM CSR register |
| + output logic dtmcs_select_o, |
| + // clear error state |
| + output logic dmi_reset_o, |
| + input logic [1:0] dmi_error_i, |
| + // test data to submodule |
| + output logic dmi_tdi_o, |
| + // test data in from submodule |
| + input logic dmi_tdo_i |
| ); |
| |
| - // to submodule |
| - assign dmi_tdi_o = td_i; |
| - |
| - typedef enum logic [3:0] { TestLogicReset, RunTestIdle, SelectDrScan, |
| - CaptureDr, ShiftDr, Exit1Dr, PauseDr, Exit2Dr, |
| - UpdateDr, SelectIrScan, CaptureIr, ShiftIr, |
| - Exit1Ir, PauseIr, Exit2Ir, UpdateIr } tap_state_e; |
| - tap_state_e tap_state_q, tap_state_d; |
| - |
| - typedef enum logic [IrLength-1:0] { |
| - BYPASS0 = 'h0, |
| - IDCODE = 'h1, |
| - DTMCSR = 'h10, |
| - DMIACCESS = 'h11, |
| - BYPASS1 = 'h1f |
| - } ir_reg_e; |
| - |
| - typedef struct packed { |
| - logic [31:18] zero1; |
| - logic dmihardreset; |
| - logic dmireset; |
| - logic zero0; |
| - logic [14:12] idle; |
| - logic [11:10] dmistat; |
| - logic [9:4] abits; |
| - logic [3:0] version; |
| - } dtmcs_t; |
| - |
| - // ---------------- |
| - // IR logic |
| - // ---------------- |
| - logic [IrLength-1:0] jtag_ir_shift_d, jtag_ir_shift_q; // shift register |
| - ir_reg_e jtag_ir_d, jtag_ir_q; // IR register -> this gets captured from shift register upon update_ir |
| - logic capture_ir, shift_ir, pause_ir, update_ir; |
| - |
| - always_comb begin |
| - jtag_ir_shift_d = jtag_ir_shift_q; |
| - jtag_ir_d = jtag_ir_q; |
| - |
| - // IR shift register |
| - if (shift_ir) begin |
| - jtag_ir_shift_d = {td_i, jtag_ir_shift_q[IrLength-1:1]}; |
| - end |
| - |
| - // capture IR register |
| - if (capture_ir) begin |
| - jtag_ir_shift_d = 'b0101; |
| - end |
| - |
| - // update IR register |
| - if (update_ir) begin |
| - jtag_ir_d = ir_reg_e'(jtag_ir_shift_q); |
| - end |
| - |
| - // synchronous test-logic reset |
| - if (test_logic_reset_o) begin |
| - jtag_ir_shift_d = '0; |
| - jtag_ir_d = IDCODE; |
| - end |
| + // to submodule |
| + assign dmi_tdi_o = td_i; |
| + |
| + typedef enum logic [3:0] { |
| + TestLogicReset, RunTestIdle, SelectDrScan, |
| + CaptureDr, ShiftDr, Exit1Dr, PauseDr, Exit2Dr, |
| + UpdateDr, SelectIrScan, CaptureIr, ShiftIr, |
| + Exit1Ir, PauseIr, Exit2Ir, UpdateIr |
| + } tap_state_e; |
| + |
| + tap_state_e tap_state_q, tap_state_d; |
| + |
| + typedef enum logic [IrLength-1:0] { |
| + BYPASS0 = 'h0, |
| + IDCODE = 'h1, |
| + DTMCSR = 'h10, |
| + DMIACCESS = 'h11, |
| + BYPASS1 = 'h1f |
| + } ir_reg_e; |
| + |
| + typedef struct packed { |
| + logic [31:18] zero1; |
| + logic dmihardreset; |
| + logic dmireset; |
| + logic zero0; |
| + logic [14:12] idle; |
| + logic [11:10] dmistat; |
| + logic [9:4] abits; |
| + logic [3:0] version; |
| + } dtmcs_t; |
| + |
| + // ---------------- |
| + // IR logic |
| + // ---------------- |
| + logic [IrLength-1:0] jtag_ir_shift_d, jtag_ir_shift_q; // shift register |
| + ir_reg_e jtag_ir_d, jtag_ir_q; // IR register -> this gets captured from shift register upon update_ir |
| + logic capture_ir, shift_ir, pause_ir, update_ir; |
| + |
| + always_comb begin |
| + jtag_ir_shift_d = jtag_ir_shift_q; |
| + jtag_ir_d = jtag_ir_q; |
| + |
| + // IR shift register |
| + if (shift_ir) begin |
| + jtag_ir_shift_d = {td_i, jtag_ir_shift_q[IrLength-1:1]}; |
| end |
| |
| - always_ff @(posedge tck_i, negedge trst_ni) begin |
| - if (!trst_ni) begin |
| - jtag_ir_shift_q <= '0; |
| - jtag_ir_q <= IDCODE; |
| - end else begin |
| - jtag_ir_shift_q <= jtag_ir_shift_d; |
| - jtag_ir_q <= jtag_ir_d; |
| - end |
| + // capture IR register |
| + if (capture_ir) begin |
| + jtag_ir_shift_d = 'b0101; |
| end |
| |
| - // ---------------- |
| - // TAP DR Regs |
| - // ---------------- |
| - // - Bypass |
| - // - IDCODE |
| - // - DTM CS |
| - logic [31:0] idcode_d, idcode_q; |
| - logic idcode_select; |
| - logic bypass_select; |
| - dtmcs_t dtmcs_d, dtmcs_q; |
| - logic bypass_d, bypass_q; // this is a 1-bit register |
| - |
| - assign dmi_reset_o = dtmcs_q.dmireset; |
| - |
| - always_comb begin |
| - idcode_d = idcode_q; |
| - bypass_d = bypass_q; |
| - dtmcs_d = dtmcs_q; |
| - |
| - if (capture_dr_o) begin |
| - if (idcode_select) idcode_d = IdcodeValue; |
| - if (bypass_select) bypass_d = 1'b0; |
| - if (dtmcs_select_o) begin |
| - dtmcs_d = '{ |
| - zero1 : '0, |
| - dmihardreset : 1'b0, |
| - dmireset : 1'b0, |
| - zero0 : '0, |
| - idle : 'd1, // 1: Enter Run-Test/Idle and leave it immediately |
| - dmistat : dmi_error_i, // 0: No error, 1: Op failed, 2: too fast |
| - abits : 'd7, // The size of address in dmi |
| - version : 'd1 // Version described in spec version 0.13 (and later?) |
| - }; |
| - end |
| - end |
| - |
| - if (shift_dr_o) begin |
| - if (idcode_select) idcode_d = {td_i, idcode_q[31:1]}; |
| - if (bypass_select) bypass_d = td_i; |
| - if (dtmcs_select_o) dtmcs_d = {td_i, dtmcs_q[31:1]}; |
| - end |
| - |
| - if (test_logic_reset_o) begin |
| - idcode_d = IdcodeValue; |
| - bypass_d = 1'b0; |
| - end |
| + // update IR register |
| + if (update_ir) begin |
| + jtag_ir_d = ir_reg_e'(jtag_ir_shift_q); |
| end |
| |
| - // ---------------- |
| - // Data reg select |
| - // ---------------- |
| - always_comb begin |
| - dmi_access_o = 1'b0; |
| - dtmcs_select_o = 1'b0; |
| - idcode_select = 1'b0; |
| - bypass_select = 1'b0; |
| - case (jtag_ir_q) |
| - BYPASS0: bypass_select = 1'b1; |
| - IDCODE: idcode_select = 1'b1; |
| - DTMCSR: dtmcs_select_o = 1'b1; |
| - DMIACCESS: dmi_access_o = 1'b1; |
| - BYPASS1: bypass_select = 1'b1; |
| - default: bypass_select = 1'b1; |
| - endcase |
| + // synchronous test-logic reset |
| + if (test_logic_reset_o) begin |
| + jtag_ir_shift_d = '0; |
| + jtag_ir_d = IDCODE; |
| + end |
| + end |
| + |
| + always_ff @(posedge tck_i, negedge trst_ni) begin |
| + if (!trst_ni) begin |
| + jtag_ir_shift_q <= '0; |
| + jtag_ir_q <= IDCODE; |
| + end else begin |
| + jtag_ir_shift_q <= jtag_ir_shift_d; |
| + jtag_ir_q <= jtag_ir_d; |
| + end |
| + end |
| + |
| + // ---------------- |
| + // TAP DR Regs |
| + // ---------------- |
| + // - Bypass |
| + // - IDCODE |
| + // - DTM CS |
| + logic [31:0] idcode_d, idcode_q; |
| + logic idcode_select; |
| + logic bypass_select; |
| + dtmcs_t dtmcs_d, dtmcs_q; |
| + logic bypass_d, bypass_q; // this is a 1-bit register |
| + |
| + assign dmi_reset_o = dtmcs_q.dmireset; |
| + |
| + always_comb begin |
| + idcode_d = idcode_q; |
| + bypass_d = bypass_q; |
| + dtmcs_d = dtmcs_q; |
| + |
| + if (capture_dr_o) begin |
| + if (idcode_select) idcode_d = IdcodeValue; |
| + if (bypass_select) bypass_d = 1'b0; |
| + if (dtmcs_select_o) begin |
| + dtmcs_d = '{ |
| + zero1 : '0, |
| + dmihardreset : 1'b0, |
| + dmireset : 1'b0, |
| + zero0 : '0, |
| + idle : 'd1, // 1: Enter Run-Test/Idle and leave it immediately |
| + dmistat : dmi_error_i, // 0: No error, 1: Op failed, 2: too fast |
| + abits : 'd7, // The size of address in dmi |
| + version : 'd1 // Version described in spec version 0.13 (and later?) |
| + }; |
| + end |
| end |
| |
| - // ---------------- |
| - // Output select |
| - // ---------------- |
| - logic tdo_mux; |
| - |
| - always_comb begin |
| - // we are shifting out the IR register |
| - if (shift_ir) begin |
| - tdo_mux = jtag_ir_shift_q[0]; |
| - // here we are shifting the DR register |
| - end else begin |
| - case (jtag_ir_q) // synthesis parallel_case |
| - IDCODE: tdo_mux = idcode_q[0]; // Reading ID code |
| - DTMCSR: tdo_mux = dtmcs_q[0]; |
| - DMIACCESS: tdo_mux = dmi_tdo_i; // Read from DMI TDO |
| - default: tdo_mux = bypass_q; // BYPASS instruction |
| - endcase |
| - end |
| - |
| + if (shift_dr_o) begin |
| + if (idcode_select) idcode_d = {td_i, idcode_q[31:1]}; |
| + if (bypass_select) bypass_d = td_i; |
| + if (dtmcs_select_o) dtmcs_d = {td_i, dtmcs_q[31:1]}; |
| end |
| |
| - // DFT |
| - logic tck_n, tck_ni; |
| - |
| - cluster_clock_inverter i_tck_inv ( |
| - .clk_i ( tck_i ), |
| - .clk_o ( tck_ni ) |
| - ); |
| - |
| - pulp_clock_mux2 i_dft_tck_mux ( |
| - .clk0_i ( tck_ni ), |
| - .clk1_i ( tck_i ), // bypass the inverted clock for testing |
| - .clk_sel_i ( testmode_i ), |
| - .clk_o ( tck_n ) |
| - ); |
| - |
| - // TDO changes state at negative edge of TCK |
| - always_ff @(posedge tck_n, negedge trst_ni) begin |
| - if (!trst_ni) begin |
| - td_o <= 1'b0; |
| - tdo_oe_o <= 1'b0; |
| - end else begin |
| - td_o <= tdo_mux; |
| - tdo_oe_o <= (shift_ir | shift_dr_o); |
| - end |
| + if (test_logic_reset_o) begin |
| + idcode_d = IdcodeValue; |
| + bypass_d = 1'b0; |
| end |
| - // ---------------- |
| - // TAP FSM |
| - // ---------------- |
| - // Determination of next state; purely combinatorial |
| - always_comb begin |
| - test_logic_reset_o = 1'b0; |
| - |
| - capture_dr_o = 1'b0; |
| - shift_dr_o = 1'b0; |
| - update_dr_o = 1'b0; |
| - |
| - capture_ir = 1'b0; |
| - shift_ir = 1'b0; |
| - pause_ir = 1'b0; |
| - update_ir = 1'b0; |
| - |
| - case (tap_state_q) |
| - TestLogicReset: begin |
| - tap_state_d = (tms_i) ? TestLogicReset : RunTestIdle; |
| - test_logic_reset_o = 1'b1; |
| - end |
| - RunTestIdle: begin |
| - tap_state_d = (tms_i) ? SelectDrScan : RunTestIdle; |
| - end |
| - // DR Path |
| - SelectDrScan: begin |
| - tap_state_d = (tms_i) ? SelectIrScan : CaptureDr; |
| - end |
| - CaptureDr: begin |
| - capture_dr_o = 1'b1; |
| - tap_state_d = (tms_i) ? Exit1Dr : ShiftDr; |
| - end |
| - ShiftDr: begin |
| - shift_dr_o = 1'b1; |
| - tap_state_d = (tms_i) ? Exit1Dr : ShiftDr; |
| - end |
| - Exit1Dr: begin |
| - tap_state_d = (tms_i) ? UpdateDr : PauseDr; |
| - end |
| - PauseDr: begin |
| - tap_state_d = (tms_i) ? Exit2Dr : PauseDr; |
| - end |
| - Exit2Dr: begin |
| - tap_state_d = (tms_i) ? UpdateDr : ShiftDr; |
| - end |
| - UpdateDr: begin |
| - update_dr_o = 1'b1; |
| - tap_state_d = (tms_i) ? SelectDrScan : RunTestIdle; |
| - end |
| - // IR Path |
| - SelectIrScan: begin |
| - tap_state_d = (tms_i) ? TestLogicReset : CaptureIr; |
| - end |
| - // In this controller state, the shift register bank in the |
| - // Instruction Register parallel loads a pattern of fixed values on |
| - // the rising edge of TCK. The last two significant bits must always |
| - // be "01". |
| - CaptureIr: begin |
| - capture_ir = 1'b1; |
| - tap_state_d = (tms_i) ? Exit1Ir : ShiftIr; |
| - end |
| - // In this controller state, the instruction register gets connected |
| - // between TDI and TDO, and the captured pattern gets shifted on |
| - // each rising edge of TCK. The instruction available on the TDI |
| - // pin is also shifted in to the instruction register. |
| - ShiftIr: begin |
| - shift_ir = 1'b1; |
| - tap_state_d = (tms_i) ? Exit1Ir : ShiftIr; |
| - end |
| - Exit1Ir: begin |
| - tap_state_d = (tms_i) ? UpdateIr : PauseIr; |
| - end |
| - PauseIr: begin |
| - pause_ir = 1'b1; |
| - tap_state_d = (tms_i) ? Exit2Ir : PauseIr; |
| - end |
| - Exit2Ir: begin |
| - tap_state_d = (tms_i) ? UpdateIr : ShiftIr; |
| - end |
| - // In this controller state, the instruction in the instruction |
| - // shift register is latched to the latch bank of the Instruction |
| - // Register on every falling edge of TCK. This instruction becomes |
| - // the current instruction once it is latched. |
| - UpdateIr: begin |
| - update_ir = 1'b1; |
| - tap_state_d = (tms_i) ? SelectDrScan : RunTestIdle; |
| - end |
| - default: tap_state_d = TestLogicReset; // can't actually happen |
| + end |
| + |
| + // ---------------- |
| + // Data reg select |
| + // ---------------- |
| + always_comb begin |
| + dmi_access_o = 1'b0; |
| + dtmcs_select_o = 1'b0; |
| + idcode_select = 1'b0; |
| + bypass_select = 1'b0; |
| + case (jtag_ir_q) |
| + BYPASS0: bypass_select = 1'b1; |
| + IDCODE: idcode_select = 1'b1; |
| + DTMCSR: dtmcs_select_o = 1'b1; |
| + DMIACCESS: dmi_access_o = 1'b1; |
| + BYPASS1: bypass_select = 1'b1; |
| + default: bypass_select = 1'b1; |
| + endcase |
| + end |
| + |
| + // ---------------- |
| + // Output select |
| + // ---------------- |
| + logic tdo_mux; |
| + |
| + always_comb begin |
| + // we are shifting out the IR register |
| + if (shift_ir) begin |
| + tdo_mux = jtag_ir_shift_q[0]; |
| + // here we are shifting the DR register |
| + end else begin |
| + case (jtag_ir_q) // synthesis parallel_case |
| + IDCODE: tdo_mux = idcode_q[0]; // Reading ID code |
| + DTMCSR: tdo_mux = dtmcs_q[0]; |
| + DMIACCESS: tdo_mux = dmi_tdo_i; // Read from DMI TDO |
| + default: tdo_mux = bypass_q; // BYPASS instruction |
| endcase |
| end |
| - |
| - always_ff @(posedge tck_i or negedge trst_ni) begin |
| - if (!trst_ni) begin |
| - tap_state_q <= RunTestIdle; |
| - idcode_q <= IdcodeValue; |
| - bypass_q <= 1'b0; |
| - dtmcs_q <= '0; |
| - end else begin |
| - tap_state_q <= tap_state_d; |
| - idcode_q <= idcode_d; |
| - bypass_q <= bypass_d; |
| - dtmcs_q <= dtmcs_d; |
| - end |
| + end |
| + |
| + // DFT |
| + logic tck_n, tck_ni; |
| + |
| + cluster_clock_inverter i_tck_inv ( |
| + .clk_i ( tck_i ), |
| + .clk_o ( tck_ni ) |
| + ); |
| + |
| + pulp_clock_mux2 i_dft_tck_mux ( |
| + .clk0_i ( tck_ni ), |
| + .clk1_i ( tck_i ), // bypass the inverted clock for testing |
| + .clk_sel_i ( testmode_i ), |
| + .clk_o ( tck_n ) |
| + ); |
| + |
| + // TDO changes state at negative edge of TCK |
| + always_ff @(posedge tck_n, negedge trst_ni) begin |
| + if (!trst_ni) begin |
| + td_o <= 1'b0; |
| + tdo_oe_o <= 1'b0; |
| + end else begin |
| + td_o <= tdo_mux; |
| + tdo_oe_o <= (shift_ir | shift_dr_o); |
| end |
| - |
| + end |
| + // ---------------- |
| + // TAP FSM |
| + // ---------------- |
| + // Determination of next state; purely combinatorial |
| + always_comb begin |
| + test_logic_reset_o = 1'b0; |
| + |
| + capture_dr_o = 1'b0; |
| + shift_dr_o = 1'b0; |
| + update_dr_o = 1'b0; |
| + |
| + capture_ir = 1'b0; |
| + shift_ir = 1'b0; |
| + pause_ir = 1'b0; |
| + update_ir = 1'b0; |
| + |
| + case (tap_state_q) |
| + TestLogicReset: begin |
| + tap_state_d = (tms_i) ? TestLogicReset : RunTestIdle; |
| + test_logic_reset_o = 1'b1; |
| + end |
| + RunTestIdle: begin |
| + tap_state_d = (tms_i) ? SelectDrScan : RunTestIdle; |
| + end |
| + // DR Path |
| + SelectDrScan: begin |
| + tap_state_d = (tms_i) ? SelectIrScan : CaptureDr; |
| + end |
| + CaptureDr: begin |
| + capture_dr_o = 1'b1; |
| + tap_state_d = (tms_i) ? Exit1Dr : ShiftDr; |
| + end |
| + ShiftDr: begin |
| + shift_dr_o = 1'b1; |
| + tap_state_d = (tms_i) ? Exit1Dr : ShiftDr; |
| + end |
| + Exit1Dr: begin |
| + tap_state_d = (tms_i) ? UpdateDr : PauseDr; |
| + end |
| + PauseDr: begin |
| + tap_state_d = (tms_i) ? Exit2Dr : PauseDr; |
| + end |
| + Exit2Dr: begin |
| + tap_state_d = (tms_i) ? UpdateDr : ShiftDr; |
| + end |
| + UpdateDr: begin |
| + update_dr_o = 1'b1; |
| + tap_state_d = (tms_i) ? SelectDrScan : RunTestIdle; |
| + end |
| + // IR Path |
| + SelectIrScan: begin |
| + tap_state_d = (tms_i) ? TestLogicReset : CaptureIr; |
| + end |
| + // In this controller state, the shift register bank in the |
| + // Instruction Register parallel loads a pattern of fixed values on |
| + // the rising edge of TCK. The last two significant bits must always |
| + // be "01". |
| + CaptureIr: begin |
| + capture_ir = 1'b1; |
| + tap_state_d = (tms_i) ? Exit1Ir : ShiftIr; |
| + end |
| + // In this controller state, the instruction register gets connected |
| + // between TDI and TDO, and the captured pattern gets shifted on |
| + // each rising edge of TCK. The instruction available on the TDI |
| + // pin is also shifted in to the instruction register. |
| + ShiftIr: begin |
| + shift_ir = 1'b1; |
| + tap_state_d = (tms_i) ? Exit1Ir : ShiftIr; |
| + end |
| + Exit1Ir: begin |
| + tap_state_d = (tms_i) ? UpdateIr : PauseIr; |
| + end |
| + PauseIr: begin |
| + pause_ir = 1'b1; |
| + tap_state_d = (tms_i) ? Exit2Ir : PauseIr; |
| + end |
| + Exit2Ir: begin |
| + tap_state_d = (tms_i) ? UpdateIr : ShiftIr; |
| + end |
| + // In this controller state, the instruction in the instruction |
| + // shift register is latched to the latch bank of the Instruction |
| + // Register on every falling edge of TCK. This instruction becomes |
| + // the current instruction once it is latched. |
| + UpdateIr: begin |
| + update_ir = 1'b1; |
| + tap_state_d = (tms_i) ? SelectDrScan : RunTestIdle; |
| + end |
| + default: tap_state_d = TestLogicReset; // can't actually happen |
| + endcase |
| + end |
| + |
| + |
| + always_ff @(posedge tck_i or negedge trst_ni) begin |
| + if (!trst_ni) begin |
| + tap_state_q <= RunTestIdle; |
| + idcode_q <= IdcodeValue; |
| + bypass_q <= 1'b0; |
| + dtmcs_q <= '0; |
| + end else begin |
| + tap_state_q <= tap_state_d; |
| + idcode_q <= idcode_d; |
| + bypass_q <= bypass_d; |
| + dtmcs_q <= dtmcs_d; |
| + end |
| + end |
| |
| endmodule |
| -- |
| 2.24.0.rc0.303.g954a862665-goog |
| |