Initial load of files

This commit is contained in:
rsnikhil
2019-03-26 14:49:40 -04:00
parent bc62f17032
commit ee24a93944
1008 changed files with 354221 additions and 224 deletions

View File

@@ -0,0 +1,487 @@
// Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved.
package DM_Abstract_Commands;
// ================================================================
// This package implements the 'Abstract Command' part of the RISC-V
// Debug Module, i.e., read/write access to RISC-V GPRs, FPRs and CSRs.
// ================================================================
// BSV library imports
import Memory :: *;
import FIFOF :: *;
import GetPut :: *;
import ClientServer :: *;
// ----------------
// Other library imports
import GetPut_Aux :: *;
import Semi_FIFOF :: *;
import Cur_Cycle :: *;
// ================================================================
import ISA_Decls :: *;
import DM_Common :: *;
// ================================================================
// Interface
interface DM_Abstract_Commands_IFC;
method Action reset;
// ----------------
// DMI facing GDB/host
method ActionValue #(DM_Word) av_read (DM_Addr dm_addr);
method Action write (DM_Addr dm_addr, DM_Word dm_word);
// ----------------
// Facing CPU/hart
interface MemoryClient #(5, XLEN) hart0_gpr_mem_client;
interface MemoryClient #(12, XLEN) hart0_csr_mem_client;
endinterface
// ================================================================
(* synthesize *)
module mkDM_Abstract_Commands (DM_Abstract_Commands_IFC);
Integer verbosity = 0; // Normally 0; non-zero for debugging
// ----------------------------------------------------------------
Reg #(Bool) rg_start_reg_access <- mkReg (False);
// FIFOs for request/response to access GPRs
FIFOF #(MemoryRequest #(5, XLEN)) f_hart0_gpr_reqs <- mkFIFOF1;
FIFOF #(MemoryResponse #( XLEN)) f_hart0_gpr_rsps <- mkFIFOF1;
// FIFOs for request/response to access CSRs
FIFOF #(MemoryRequest #(12, XLEN)) f_hart0_csr_reqs <- mkFIFOF1;
FIFOF #(MemoryResponse #( XLEN)) f_hart0_csr_rsps <- mkFIFOF1;
// ----------------------------------------------------------------
// rg_data0
Reg #(DM_Word ) rg_data0 <- mkRegU;
`ifdef RV64
Reg #(DM_Word ) rg_data1 <- mkRegU;
`endif
// ----------------------------------------------------------------
// rg_data2..11: not implemented
// rg_abstractauto: not implemented
// rg_progbuf0..15: not implemented
// ----------------------------------------------------------------
// rg_abstractcs
Reg #(Bool) rg_abstractcs_busy <- mkRegU;
Reg #(DM_abstractcs_cmderr) rg_abstractcs_cmderr <- mkRegU;
Bit #(5) abstractcs_progsize = 0;
Bit #(5) abstractcs_datacount = 0;
DM_Word virt_rg_abstractcs = {3'b0,
abstractcs_progsize,
11'b0,
pack (rg_abstractcs_busy),
1'b0,
pack (rg_abstractcs_cmderr),
3'b0,
abstractcs_datacount};
function Action fa_rg_abstractcs_write (DM_Word dm_word);
action
if (rg_abstractcs_busy) begin
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_BUSY;
$display ("(%0d): DM_Abstract_Commands.write: [abstractcs] <= 0x%08h: ERROR", cur_cycle, dm_word);
$display (" DM is busy with a previous abstract command");
end
else if (fn_abstractcs_cmderr (dm_word) != DM_ABSTRACTCS_CMDERR_NONE) begin
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_NONE;
if (verbosity != 0)
$display ("(%0d): DM_Abstract_Commands.write [abstractcs]: clearing cmderr", cur_cycle);
end
else begin
if (verbosity != 0)
$display ("(%0d): DM_Abstract_Commands.write [abstractcs]: cmderr unchanged", cur_cycle);
end
endaction
endfunction
// ----------------------------------------------------------------
// rg_command
// cmdtype no register, since we only support 'access reg'
// size no register, since we only support 'lower 32b' for RV32
// and 'lower 64b' for RV64
// postexec no register, since we don't support Program Buffer
// transfer no register, since we always do transfers
Reg #(Bool) rg_command_access_reg_write <- mkRegU;
// regno: we only implement lower 13 bits of this 16-bit field
Reg #(Bit #(13)) rg_command_access_reg_regno <- mkRegU;
DM_Word virt_rg_command = fn_mk_command_access_reg (
DM_COMMAND_ACCESS_REG_SIZE_LOWER32
, False // postexec
, True // transfer
, rg_command_access_reg_write
, zeroExtend (rg_command_access_reg_regno));
function Action fa_rg_command_write (DM_Word dm_word);
action
// TODO: check that CPU is halted, else set cmderr = DM_ABSTRACTCS_CMDERR_HALT_RESUME
DM_abstractcs_cmderr cmderr = rg_abstractcs_cmderr;
let size = fn_command_access_reg_size (dm_word);
// Ignore if 'cmderr' is non-zero
if (cmderr != DM_ABSTRACTCS_CMDERR_NONE) begin
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: ERROR", cur_cycle, dm_word);
$display (" Ignoring since 'cmderr' is 0x%0h", cmderr);
end
else begin
if (rg_abstractcs_busy) begin
cmderr = DM_ABSTRACTCS_CMDERR_BUSY;
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: ERROR", cur_cycle, dm_word);
$display (" DM is busy with a previous abstract command");
end
// Only 'Access Reg' cmdtype is supported
else if (fn_command_cmdtype (dm_word) != DM_COMMAND_CMDTYPE_ACCESS_REG) begin
cmderr = DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED;
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: ERROR", cur_cycle, dm_word);
$display (" ", fshow (fn_command_cmdtype (dm_word)), " not supported");
end
`ifdef RV32
// Only lower 32-bit access is supported
else if (size != DM_COMMAND_ACCESS_REG_SIZE_LOWER32) begin
cmderr = DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED;
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: ERROR", cur_cycle, dm_word);
$display (" For DM_COMMAND_CMDTYPE_ACCESS_REG, ",
fshow (fn_command_access_reg_size (dm_word)), " not supported in RV32 mode");
end
`endif
`ifdef RV64
// Only lower 32-bit and 64-bit access is supported
else if (size != DM_COMMAND_ACCESS_REG_SIZE_LOWER64)
begin
cmderr = DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED;
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: ERROR", cur_cycle, dm_word);
$display (" For DM_COMMAND_CMDTYPE_ACCESS_REG, ",
fshow (fn_command_access_reg_size (dm_word)), " not supported in RV64 mode");
end
`endif
// 'postexec' is not supported
else if (fn_command_access_reg_postexec (dm_word) == True) begin
cmderr = DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED;
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: ERROR", cur_cycle, dm_word);
$display (" For DM_COMMAND_CMDTYPE_ACCESS_REG, postexec not supported");
end
// non-'transfer' is not supported
else if (fn_command_access_reg_transfer (dm_word) == False) begin
cmderr = DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED;
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: ERROR", cur_cycle, dm_word);
$display (" For DM_COMMAND_CMDTYPE_ACCESS_REG, no-transfer not supported");
end
else begin
Bool is_write = fn_command_access_reg_write (dm_word);
Bit #(13) regno = truncate (fn_command_access_reg_regno (dm_word));
rg_command_access_reg_write <= is_write;
rg_command_access_reg_regno <= regno;
rg_abstractcs_busy <= True;
rg_start_reg_access <= True;
cmderr = DM_ABSTRACTCS_CMDERR_NONE;
if (verbosity != 0)
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: OKAY", cur_cycle, dm_word);
end
rg_abstractcs_cmderr <= cmderr;
end
endaction
endfunction
// ----------------------------------------------------------------
// Start reads/writes
Bool is_csr = ( (fromInteger (dm_command_access_reg_regno_csr_0) <= rg_command_access_reg_regno)
&& (rg_command_access_reg_regno <= fromInteger (dm_command_access_reg_regno_csr_FFF)));
Bool is_gpr = ( (fromInteger (dm_command_access_reg_regno_gpr_0) <= rg_command_access_reg_regno)
&& (rg_command_access_reg_regno <= fromInteger (dm_command_access_reg_regno_gpr_1F)));
Bit #(12) csr_addr = truncate (rg_command_access_reg_regno - fromInteger (dm_command_access_reg_regno_csr_0));
Bit #(5) gpr_addr = truncate (rg_command_access_reg_regno - fromInteger (dm_command_access_reg_regno_gpr_0));
// ----------------------------------------------------------------
// Read/Write CSR
rule rl_start_write_csr ( rg_abstractcs_busy
&& rg_start_reg_access
&& rg_command_access_reg_write
&& is_csr);
if (verbosity != 0)
`ifdef RV32
$display ("(%0d): DM_Abstract_Commands.write [command]: write CSR [0x%0h] <= 0x%08h", cur_cycle,
csr_addr, rg_data0);
`endif
`ifdef RV64
$display ("(%0d): DM_Abstract_Commands.write [command]: write CSR [0x%0h] <= 0x%016h", cur_cycle,
csr_addr, {rg_data1, rg_data0});
`endif
let req = MemoryRequest {write: True, byteen: '1, address: csr_addr,
`ifdef RV32
data: rg_data0
`endif
`ifdef RV64
data: {rg_data1, rg_data0}
`endif
};
f_hart0_csr_reqs.enq (req);
rg_start_reg_access <= False;
rg_abstractcs_busy <= False;
endrule
rule rl_start_read_csr ( rg_abstractcs_busy
&& rg_start_reg_access
&& (! rg_command_access_reg_write)
&& is_csr);
if (verbosity != 0)
$display ("(%0d): DM_Abstract_Commands.write [command]: read CSR [0x%0h]", cur_cycle, csr_addr);
let req = MemoryRequest {write: False, byteen: '1, address: csr_addr, data: ?};
f_hart0_csr_reqs.enq (req);
rg_start_reg_access <= False;
endrule
rule rl_finish_csr_read (rg_abstractcs_busy);
let rsp <- pop (f_hart0_csr_rsps);
`ifdef RV32
rg_data0 <= rsp.data;
`endif
`ifdef RV64
rg_data0 <= truncate (rsp.data);
rg_data1 <= rsp.data[63:32];
`endif
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_NONE;
if (verbosity != 0)
`ifdef RV32
$display ("(%0d): DM_Abstract_Commands: csr read data is 0x%08h", cur_cycle, rsp.data);
`endif
`ifdef RV64
$display ("(%0d): DM_Abstract_Commands: csr read data is 0x%016h", cur_cycle, rsp.data);
`endif
rg_abstractcs_busy <= False;
endrule
// ----------------------------------------------------------------
// Read/Write GPR
rule rl_start_write_gpr ( rg_abstractcs_busy
&& rg_start_reg_access
&& rg_command_access_reg_write
&& is_gpr);
if (verbosity != 0)
`ifdef RV32
$display ("(%0d): DM_Abstract_Commands.write [command]: write GPR [0x%0h] <= 0x%08h", cur_cycle,
gpr_addr, rg_data0);
`endif
`ifdef RV64
$display ("(%0d): DM_Abstract_Commands.write [command]: write GPR [0x%0h] <= 0x%016h", cur_cycle,
gpr_addr, {rg_data1, rg_data0});
`endif
let req = MemoryRequest {write: True,
byteen: '1,
address: gpr_addr,
`ifdef RV32
data: rg_data0
`endif
`ifdef RV64
data: {rg_data1, rg_data0}
`endif
};
f_hart0_gpr_reqs.enq (req);
rg_start_reg_access <= False;
rg_abstractcs_busy <= False;
endrule
rule rl_start_read_gpr ( rg_abstractcs_busy
&& rg_start_reg_access
&& (! rg_command_access_reg_write)
&& is_gpr);
if (verbosity != 0)
$display ("(%0d): DM_Abstract_Commands.write [command]: read GPR [0x%0h]", cur_cycle, gpr_addr);
let req = MemoryRequest {
write: False
, byteen: '1
, address: gpr_addr
, data: ?
};
f_hart0_gpr_reqs.enq (req);
rg_start_reg_access <= False;
endrule
rule rl_finish_gpr_read (rg_abstractcs_busy);
let rsp <- pop (f_hart0_gpr_rsps);
`ifdef RV32
rg_data0 <= rsp.data;
`endif
`ifdef RV64
rg_data0 <= truncate (rsp.data);
rg_data1 <= rsp.data[63:32];
`endif
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_NONE;
if (verbosity != 0)
`ifdef RV32
$display ("(%0d): DM_Abstract_Commands: gpr read data is 0x%08h", cur_cycle, rsp.data);
`endif
`ifdef RV64
$display ("(%0d): DM_Abstract_Commands: gpr read data is 0x%016h", cur_cycle, rsp.data);
`endif
rg_abstractcs_busy <= False;
endrule
// ----------------
// Read/Write unknown address
rule rl_start_write_unknown ( rg_abstractcs_busy
&& rg_start_reg_access
&& rg_command_access_reg_write
&& (! is_csr) && (! is_gpr));
if (verbosity != 0)
$display ("(%0d): DM_Abstract_Commands.write [command]: write unknown RISC-V regno [0x%0h] <= 0x%08h", cur_cycle,
rg_command_access_reg_regno, rg_data0);
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_OTHER;
rg_start_reg_access <= False;
rg_abstractcs_busy <= False;
endrule
rule rl_start_read_unknown ( rg_abstractcs_busy
&& rg_start_reg_access
&& (! rg_command_access_reg_write)
&& (! is_csr) && (! is_gpr));
if (verbosity != 0)
$display ("(%0d): DM_Abstract_Commands.write [command]: read unknown RISC-V regno [0x%0h] => ...", cur_cycle,
rg_command_access_reg_regno);
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_OTHER;
rg_start_reg_access <= False;
rg_abstractcs_busy <= False;
endrule
// ----------------------------------------------------------------
// Finish CSR and GPR reads
// ================================================================
// INTERFACE
method Action reset;
rg_start_reg_access <= False;
f_hart0_gpr_reqs.clear;
f_hart0_gpr_rsps.clear;
f_hart0_csr_reqs.clear;
f_hart0_csr_rsps.clear;
rg_abstractcs_busy <= False;
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_NONE;
rg_command_access_reg_write <= False;
rg_command_access_reg_regno <= fromInteger (dm_command_access_reg_regno_gpr_0);
rg_data0 <= 0;
`ifdef RV64
rg_data1 <= 0;
`endif
if (verbosity != 0)
$display ("(%0d): DM_Abstract_Commands: reset", cur_cycle);
endmethod
// ----------------
// Facing DMI/GDB
method ActionValue #(DM_Word) av_read (DM_Addr dm_addr);
actionvalue
let dm_addr_name = fshow_dm_addr (dm_addr);
DM_Word dm_word = case (dm_addr)
dm_addr_abstractcs: virt_rg_abstractcs;
dm_addr_command: virt_rg_command;
dm_addr_data0: rg_data0;
`ifdef RV64
dm_addr_data1: rg_data1;
`endif
// dm_addr_data2..data3
// dm_addr_abstractauto
// dm_addr_progbuf0..15
endcase;
if (verbosity != 0)
$display ("(%0d): DM_Abstract_Commands.av_read: [", cur_cycle, dm_addr_name, "] => 0x%08h", dm_word);
return dm_word;
endactionvalue
endmethod
method Action write (DM_Addr dm_addr, DM_Word dm_word);
action
let dm_addr_name = fshow_dm_addr (dm_addr);
if (dm_addr == dm_addr_abstractcs)
fa_rg_abstractcs_write (dm_word);
else if (rg_abstractcs_cmderr != DM_ABSTRACTCS_CMDERR_NONE) begin
if (verbosity != 0) begin
$display ("(%0d): DM_Abstract_Commands.write: [", cur_cycle, dm_addr_name, "] <= 0x%08h: ERROR", dm_word);
$display (" Ignoring: previous cmderr ", fshow (rg_abstractcs_cmderr));
end
end
else if (dm_addr == dm_addr_command)
fa_rg_command_write (dm_word);
else if (dm_addr == dm_addr_data0) begin
rg_data0 <= dm_word;
if (verbosity != 0)
$display ("(%0d): DM_Abstract_Commands.write: [", cur_cycle, dm_addr_name, "] <= 0x%08h", dm_word);
end
`ifdef RV64
else if (dm_addr == dm_addr_data1) begin
rg_data1 <= dm_word;
if (verbosity != 0)
$display ("(%0d): DM_Abstract_Commands.write: [", cur_cycle, dm_addr_name, "] <= 0x%08h", dm_word);
end
`endif
else begin
// dm_addr_data2..12
// dm_addr_abstractauto
// dm_addr_progbuf0..15
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED;
$display ("(%0d): DM_Abstract_Commands.write: [", cur_cycle, dm_addr_name,
"] <= 0x%08h: ERROR: not supported", dm_word);
end
endaction
endmethod
// ----------------
// Facing CPU/hart
interface MemoryClient hart0_gpr_mem_client = toGPClient (f_hart0_gpr_reqs, f_hart0_gpr_rsps);
interface MemoryClient hart0_csr_mem_client = toGPClient (f_hart0_csr_reqs, f_hart0_csr_rsps);
endmodule
// ================================================================
endpackage

View File

@@ -0,0 +1,512 @@
// Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved.
package DM_Common;
// ================================================================
// This package has non-implementation-specific definitions, i.e.,
// just encodes things in the spec.
// ================================================================
// BSV library imports
// None
// ================================================================
// Project imports
// None
// ================================================================
// Debug Module Interface (DMI) addresses and data.
// Note: data is always 32b, whether the connected CPU is RV32, RV64 or RV128.
typedef Bit #(7) DM_Addr;
DM_Addr max_DM_Addr = 'h5F;
typedef Bit #(32) DM_Word;
// ================================================================
// Debug Module address map
// ----------------
// Run Control
DM_Addr dm_addr_dmcontrol = 'h10;
DM_Addr dm_addr_dmstatus = 'h11;
DM_Addr dm_addr_hartinfo = 'h12;
DM_Addr dm_addr_haltsum = 'h13;
DM_Addr dm_addr_hawindowsel = 'h14;
DM_Addr dm_addr_hawindow = 'h15;
DM_Addr dm_addr_devtreeaddr0 = 'h19;
DM_Addr dm_addr_authdata = 'h30;
DM_Addr dm_addr_haltregion0 = 'h40;
DM_Addr dm_addr_haltregion31 = 'h5F;
DM_Addr dm_addr_verbosity = 'h60; // Non-standard (not in spec)
// ----------------
// Abstract commands (read/write RISC-V registers and RISC-V CSRs)
DM_Addr dm_addr_abstractcs = 'h16;
DM_Addr dm_addr_command = 'h17;
DM_Addr dm_addr_data0 = 'h04;
DM_Addr dm_addr_data1 = 'h05;
DM_Addr dm_addr_data2 = 'h06;
DM_Addr dm_addr_data3 = 'h07;
DM_Addr dm_addr_data4 = 'h08;
DM_Addr dm_addr_data5 = 'h09;
DM_Addr dm_addr_data6 = 'h0a;
DM_Addr dm_addr_data7 = 'h0b;
DM_Addr dm_addr_data8 = 'h0c;
DM_Addr dm_addr_data9 = 'h0d;
DM_Addr dm_addr_data10 = 'h0d;
DM_Addr dm_addr_data11 = 'h0f;
DM_Addr dm_addr_abstractauto = 'h18;
DM_Addr dm_addr_progbuf0 = 'h20;
// ----------------
// System Bus access (read/write RISC-V memory/devices)
DM_Addr dm_addr_sbcs = 'h38;
DM_Addr dm_addr_sbaddress0 = 'h39;
DM_Addr dm_addr_sbaddress1 = 'h3a;
DM_Addr dm_addr_sbaddress2 = 'h3b;
DM_Addr dm_addr_sbdata0 = 'h3c;
DM_Addr dm_addr_sbdata1 = 'h3d;
DM_Addr dm_addr_sbdata2 = 'h3e;
DM_Addr dm_addr_sbdata3 = 'h3f;
// ================================================================
function Fmt fshow_dm_addr (DM_Addr dm_addr);
return case (dm_addr)
// Run Control
dm_addr_dmcontrol: $format ("dm_addr_dmcontrol");
dm_addr_dmstatus: $format ("dm_addr_dmstatus");
dm_addr_hartinfo: $format ("dm_addr_hartinfo");
dm_addr_haltsum: $format ("dm_addr_haltsum");
dm_addr_hawindowsel: $format ("dm_addr_hawindowsel");
dm_addr_hawindow: $format ("dm_addr_hawindow");
dm_addr_devtreeaddr0: $format ("dm_addr_devtreeaddr0");
dm_addr_authdata: $format ("dm_addr_authdata");
dm_addr_haltregion0: $format ("dm_addr_haltregion0");
dm_addr_haltregion31: $format ("dm_addr_haltregion31");
dm_addr_verbosity: $format ("dm_addr_verbosity");
// Abstract Commands
dm_addr_abstractcs: $format ("dm_addr_abstractcs");
dm_addr_command: $format ("dm_addr_command");
dm_addr_data0: $format ("dm_addr_data0");
dm_addr_data1: $format ("dm_addr_data1");
dm_addr_data2: $format ("dm_addr_data2");
dm_addr_data3: $format ("dm_addr_data3");
dm_addr_data4: $format ("dm_addr_data4");
dm_addr_data5: $format ("dm_addr_data5");
dm_addr_data6: $format ("dm_addr_data6");
dm_addr_data7: $format ("dm_addr_data7");
dm_addr_data8: $format ("dm_addr_data8");
dm_addr_data9: $format ("dm_addr_data9");
dm_addr_data10: $format ("dm_addr_data10");
dm_addr_data11: $format ("dm_addr_data11");
dm_addr_abstractauto: $format ("dm_addr_abstractauto");
dm_addr_progbuf0: $format ("dm_addr_progbuf0");
// System Bus
dm_addr_sbcs: $format ("dm_addr_sbcs");
dm_addr_sbaddress0: $format ("dm_addr_sbaddress0");
dm_addr_sbaddress1: $format ("dm_addr_sbaddress1");
dm_addr_sbaddress2: $format ("dm_addr_sbaddress2");
dm_addr_sbdata0: $format ("dm_addr_sbdata0");
dm_addr_sbdata1: $format ("dm_addr_sbdata1");
dm_addr_sbdata2: $format ("dm_addr_sbdata2");
dm_addr_sbdata3: $format ("dm_addr_sbdata3");
default: $format ("<Unknown dm_abstract_command dm_addr 0x%0h>", dm_addr);
endcase;
endfunction
// ================================================================
// Run Control DM register fields
// ----------------------------------------------------------------
// 'dmcontrol' register
function DM_Word fn_mk_dmcontrol (Bool haltreq,
Bool resumereq,
Bool hartreset,
Bool hasel,
Bit #(10) hartsel,
Bool ndmreset,
Bool dmactive);
return {pack (haltreq),
pack (resumereq),
pack (hartreset),
2'b0,
pack (hasel),
hartsel,
14'b0,
pack (ndmreset),
pack (dmactive)};
endfunction
function Bool fn_dmcontrol_haltreq (DM_Word dm_word);
return unpack (dm_word [31]);
endfunction
function Bool fn_dmcontrol_resumereq (DM_Word dm_word);
return unpack (dm_word [30]);
endfunction
function Bool fn_dmcontrol_hartreset (DM_Word dm_word);
return unpack (dm_word [29]);
endfunction
function Bool fn_dmcontrol_hasel (DM_Word dm_word);
return unpack (dm_word [26]);
endfunction
function Bit #(10) fn_dmcontrol_hartsel (DM_Word dm_word);
return dm_word [25:16];
endfunction
function Bool fn_dmcontrol_ndmreset (DM_Word dm_word);
return unpack (dm_word [1]);
endfunction
function Bool fn_dmcontrol_dmactive (DM_Word dm_word);
return unpack (dm_word [0]);
endfunction
// ----------------------------------------------------------------
// 'dmstatus' register
function Bool fn_dmstatus_allresumeack (DM_Word x);
return unpack (x [17]);
endfunction
function Bool fn_dmstatus_anyresumeack (DM_Word x);
return unpack (x [16]);
endfunction
function Bool fn_dmstatus_allnonexistent (DM_Word x);
return unpack (x [15]);
endfunction
function Bool fn_dmstatus_anynonexistent (DM_Word x);
return unpack (x [14]);
endfunction
function Bool fn_dmstatus_allunavail (DM_Word x);
return unpack (x [13]);
endfunction
function Bool fn_dmstatus_anyunavail (DM_Word x);
return unpack (x [12]);
endfunction
function Bool fn_dmstatus_allrunning (DM_Word x);
return unpack (x [11]);
endfunction
function Bool fn_dmstatus_anyrunning (DM_Word x);
return unpack (x [10]);
endfunction
function Bool fn_dmstatus_allhalted (DM_Word x);
return unpack (x [9]);
endfunction
function Bool fn_dmstatus_anyhalted (DM_Word x);
return unpack (x [8]);
endfunction
function Bool fn_dmstatus_authenticated (DM_Word x);
return unpack (x [7]);
endfunction
function Bool fn_dmstatus_authbusy (DM_Word x);
return unpack (x [6]);
endfunction
function Bool fn_dmstatus_devtreevalid (DM_Word x);
return unpack (x [4]);
endfunction
function Bit #(4) fn_dmstatus_version (DM_Word x);
return unpack (x [3:0]);
endfunction
function Fmt fshow_dmstatus (DM_Word x);
Fmt fmt_version = ( (x[3:0] == 0)
? $format ("v.none")
: ( (x[3:0] == 1)
? $format ("v0.11")
: ( (x[3:0] == 2)
? $format ("v0.13")
: $format ("v??"))));
return ( $format ("(all/any) ")
+ $format ("resumeack %0d/%0d ", x[17], x[16])
+ $format ("nonexistent %0d/%0d ", x[15], x[14])
+ $format ("unavail %0d/%0d ", x[13], x[12])
+ $format ("running %0d/%0d ", x[11], x[10])
+ $format ("halted %0d/%0d ", x[9], x[8])
+ $format ("authenticated %0d ", x[7])
+ $format ("authbusy %0d ", x[6])
+ $format ("devtreevalid %0d ", x[4])
+ fmt_version);
endfunction
// ================================================================
// Abstract Command register fields
// ----------------------------------------------------------------
// 'dm_abstractcs' register
typedef enum {DM_ABSTRACTCS_CMDERR_NONE, // 0
DM_ABSTRACTCS_CMDERR_BUSY, // 1
DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED, // 2
DM_ABSTRACTCS_CMDERR_EXCEPTION, // 3
DM_ABSTRACTCS_CMDERR_HALT_RESUME, // 4
DM_ABSTRACTCS_CMDERR_UNDEF5, // 5
DM_ABSTRACTCS_CMDERR_UNDEF6, // 6
DM_ABSTRACTCS_CMDERR_OTHER // 7
} DM_abstractcs_cmderr
deriving (Bits, Eq, FShow);
// The following is used in writes, to clear cmderr
DM_abstractcs_cmderr dm_cmderr_w1c = DM_ABSTRACTCS_CMDERR_OTHER;
function DM_Word fn_mk_abstractcs (DM_abstractcs_cmderr cmderr);
return { 0, pack (cmderr), 8'h0 };
endfunction
function Bit #(5) fn_abstractcs_progsize (DM_Word dm_word);
return unpack (dm_word [28:24]);
endfunction
function Bool fn_abstractcs_busy (DM_Word dm_word);
return unpack (dm_word [12]);
endfunction
function DM_abstractcs_cmderr fn_abstractcs_cmderr (DM_Word dm_word);
return unpack (dm_word [10:8]);
endfunction
function Bit #(5) fn_abstractcs_datacount (DM_Word dm_word);
return unpack (dm_word [4:0]);
endfunction
// ----------------------------------------------------------------
// 'command' register
typedef enum {DM_COMMAND_CMDTYPE_ACCESS_REG,
DM_COMMAND_CMDTYPE_QUICK_ACCESS
} DM_command_cmdtype
deriving (Bits, Eq, FShow);
typedef enum {DM_COMMAND_ACCESS_REG_SIZE_UNDEF0, // 0
DM_COMMAND_ACCESS_REG_SIZE_UNDEF1, // 1
DM_COMMAND_ACCESS_REG_SIZE_LOWER32, // 2
DM_COMMAND_ACCESS_REG_SIZE_LOWER64, // 3
DM_COMMAND_ACCESS_REG_SIZE_LOWER128, // 4
DM_COMMAND_ACCESS_REG_SIZE_UNDEF5, // 5
DM_COMMAND_ACCESS_REG_SIZE_UNDEF6, // 6
DM_COMMAND_ACCESS_REG_SIZE_UNDEF7 // 7
} DM_command_access_reg_size
deriving (Bits, Eq, FShow);
Integer dm_command_access_reg_regno_csr_0 = 'h0000;
Integer dm_command_access_reg_regno_csr_FFF = 'h0FFF;
Integer dm_command_access_reg_regno_gpr_0 = 'h1000;
Integer dm_command_access_reg_regno_gpr_1F = 'h101F;
Integer dm_command_access_reg_regno_fpr_0 = 'h1020;
Integer dm_command_access_reg_regno_fpr_1F = 'h103F;
function DM_Word fn_mk_command_access_reg (DM_command_access_reg_size size,
Bool postexec,
Bool transfer,
Bool write,
Bit #(16) regno);
Bit #(8) b8_cmdtype = zeroExtend (pack (DM_COMMAND_CMDTYPE_ACCESS_REG));
Bit #(3) b3_size = pack (size);
return {b8_cmdtype,
1'b0,
b3_size,
1'b0,
pack (postexec),
pack (transfer),
pack (write),
regno};
endfunction
function DM_command_cmdtype fn_command_cmdtype (DM_Word dm_word);
return unpack (truncate (dm_word [31:24]));
endfunction
function DM_command_access_reg_size fn_command_access_reg_size (DM_Word dm_word);
return unpack (dm_word [22:20]);
endfunction
function Bool fn_command_access_reg_postexec (DM_Word dm_word);
return unpack (dm_word [18]);
endfunction
function Bool fn_command_access_reg_transfer (DM_Word dm_word);
return unpack (dm_word [17]);
endfunction
function Bool fn_command_access_reg_write (DM_Word dm_word);
return unpack (dm_word [16]);
endfunction
function Bit #(16) fn_command_access_reg_regno (DM_Word dm_word);
return dm_word [15:0];
endfunction
// ================================================================
// System Bus Access DM register fields
// ----------------------------------------------------------------
// 'dm_sbcs' register
typedef enum {DM_SBACCESS_8_BIT,
DM_SBACCESS_16_BIT,
DM_SBACCESS_32_BIT,
DM_SBACCESS_64_BIT,
DM_SBACCESS_128_BIT
} DM_sbaccess
deriving (Bits, Eq, FShow);
function Integer fn_sbaccess_to_addr_incr (DM_sbaccess sbaccess);
case (sbaccess)
DM_SBACCESS_8_BIT: return 1;
DM_SBACCESS_16_BIT: return 2;
DM_SBACCESS_32_BIT: return 4;
DM_SBACCESS_64_BIT: return 8;
DM_SBACCESS_128_BIT: return 16;
endcase
endfunction
typedef enum {DM_SBERROR_NONE, // 0
DM_SBERROR_TIMEOUT, // 1
DM_SBERROR_BADADDR, // 2
DM_SBERROR_OTHER, // 3
DM_SBERROR_BUSY_STALE, // 4
DM_SBERROR_UNDEF5, // 5
DM_SBERROR_UNDEF6, // 6
DM_SBERROR_UNDEF7_W1C // 7, used in writes, to clear sberror
} DM_sberror
deriving (Bits, Eq, FShow);
// Constructor
function DM_Word fn_mk_sbcs_val (Bit #(3) sbversion,
Bool sbbusyerror,
Bool sbbusy,
Bool sbreadonaddr,
DM_sbaccess sbaccess,
Bool sbautoincrement,
Bool sbreadondata,
DM_sberror sberror,
Bit #(7) sbasize,
Bit #(1) sbaccess128,
Bit #(1) sbaccess64,
Bit #(1) sbaccess32,
Bit #(1) sbaccess16,
Bit #(1) sbaccess8);
return {sbversion,
6'b0,
pack (sbbusyerror),
pack (sbbusy),
pack (sbreadonaddr),
pack (sbaccess),
pack (sbautoincrement),
pack (sbreadondata),
pack (sberror),
sbasize,
sbaccess128,
sbaccess64,
sbaccess32,
sbaccess16,
sbaccess8};
endfunction
// Selectors
function Bit #(3) fn_sbcs_sbversion (DM_Word dm_word); return unpack (dm_word [31:29]); endfunction
function Bool fn_sbcs_sbbusyerror (DM_Word dm_word); return unpack (dm_word [22]); endfunction
function Bool fn_sbcs_sbbusy (DM_Word dm_word); return unpack (dm_word [21]); endfunction
function Bool fn_sbcs_sbreadonaddr (DM_Word dm_word); return unpack (dm_word [20]); endfunction
function DM_sbaccess fn_sbcs_sbaccess (DM_Word dm_word); return unpack (dm_word [19:17]); endfunction
function Bool fn_sbcs_sbautoincrement (DM_Word dm_word); return unpack (dm_word [16]); endfunction
function Bool fn_sbcs_sbreadondata (DM_Word dm_word); return unpack (dm_word [15]); endfunction
function DM_sberror fn_sbcs_sberror (DM_Word dm_word); return unpack (dm_word [14:12]); endfunction
function Bit #(7) fn_sbcs_sbasize (DM_Word dm_word); return dm_word [11:5]; endfunction
function Bool fn_sbcs_sbaccess128 (DM_Word dm_word); return unpack (dm_word [4]); endfunction
function Bool fn_sbcs_sbaccess64 (DM_Word dm_word); return unpack (dm_word [3]); endfunction
function Bool fn_sbcs_sbaccess32 (DM_Word dm_word); return unpack (dm_word [2]); endfunction
function Bool fn_sbcs_sbaccess16 (DM_Word dm_word); return unpack (dm_word [1]); endfunction
function Bool fn_sbcs_sbaccess8 (DM_Word dm_word); return unpack (dm_word [0]); endfunction
// Debugging
function Fmt fshow_sbcs (DM_Word dm_word);
return ( $format ("SBCS{")
+ $format ("sbversion %0d", fn_sbcs_sbversion (dm_word))
+ $format (" sbbusyerror %0d", fn_sbcs_sbbusyerror (dm_word))
+ $format (" sbbusy %0d", fn_sbcs_sbbusy (dm_word))
+ $format (" sbreadonaddr ") + fshow (fn_sbcs_sbreadonaddr (dm_word))
+ $format (" sbaccess ") + fshow (fn_sbcs_sbaccess (dm_word))
+ $format (" sbautoincrement ") + fshow (fn_sbcs_sbautoincrement (dm_word))
+ $format (" sbreadondata ") + fshow (fn_sbcs_sbreadondata (dm_word))
+ $format (" sberror ") + fshow (fn_sbcs_sberror (dm_word))
+ $format (" sbasize %0d", fn_sbcs_sbasize (dm_word))
+ $format (" sbaccess")
+ ((fn_sbcs_sbaccess128 (dm_word)) ? $format ("_128") : $format ("x"))
+ ((fn_sbcs_sbaccess64 (dm_word)) ? $format ("_64") : $format ("x"))
+ ((fn_sbcs_sbaccess32 (dm_word)) ? $format ("_32") : $format ("x"))
+ ((fn_sbcs_sbaccess16 (dm_word)) ? $format ("_16") : $format ("x"))
+ ((fn_sbcs_sbaccess8 (dm_word)) ? $format ("_8") : $format ("x"))
+ $format ("}"));
endfunction
// ================================================================
// DCSR 'cause' field values
typedef enum {DCSR_CAUSE_RESERVED0,
DCSR_CAUSE_EBREAK,
DCSR_CAUSE_TRIGGER,
DCSR_CAUSE_HALTREQ,
DCSR_CAUSE_STEP,
DCSR_CAUSE_RESERVED5,
DCSR_CAUSE_RESERVED6,
DCSR_CAUSE_RESERVED7
} DCSR_Cause
deriving (Bits, Eq, FShow);
// ================================================================
// Sub-interface of the Debug Module facing the remote debugger (e.g. GDB)
interface DMI;
method Action read_addr (DM_Addr dm_addr);
method ActionValue #(DM_Word) read_data;
method Action write (DM_Addr dm_addr, DM_Word dm_word);
endinterface
// A dummy interface to tie off DMI if it is not used.
DMI dummy_DMI_ifc = interface DMI;
method Action read_addr (DM_Addr dm_addr) = noAction;
method ActionValue #(DM_Word) read_data = actionvalue
return 0;
endactionvalue;
method Action write (DM_Addr dm_addr, DM_Word dm_word) = noAction;
endinterface;
// ================================================================
endpackage

View File

@@ -0,0 +1,343 @@
// Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved.
package DM_Run_Control;
// ================================================================
// This package implements the 'Run Control' part of the RISC-V Debug
// Module, i.e., reset platform, reset hart, run/halt hart
// ================================================================
// BSV library imports
import FIFOF :: *;
import GetPut :: *;
import ClientServer :: *;
// ================================================================
// Project imports
import ISA_Decls :: *;
import DM_Common :: *;
// ================================================================
// Interface
interface DM_Run_Control_IFC;
method Bool dmactive;
method Action reset;
// ----------------
// DMI facing GDB/host
method ActionValue #(DM_Word) av_read (DM_Addr dm_addr);
method Action write (DM_Addr dm_addr, DM_Word dm_word);
// ----------------
// Facing a hart: reset and run-control
interface Get #(Token) hart0_get_reset_req;
interface Client #(Bool, Bool) hart0_client_run_halt;
interface Get #(Bit #(4)) hart0_get_other_req;
// ----------------
// Facing Platform: Non-Debug-Module Reset (reset all except DM)
interface Get #(Token) get_ndm_reset_req;
endinterface
// ================================================================
(* synthesize *)
module mkDM_Run_Control (DM_Run_Control_IFC);
Integer verbosity = 0; // Normally 0; non-zero for debugging
// ----------------------------------------------------------------
// NDM Reset
FIFOF #(Token) f_ndm_reset_reqs <- mkFIFOF;
// ----------------------------------------------------------------
// Hart0 run control
Reg #(Bool) rg_hart0_running <- mkRegU;
// Reset requests to hart
FIFOF #(Token) f_hart0_reset_reqs <- mkFIFOF;
// Run/halt requests to hart and responses
FIFOF #(Bool) f_hart0_run_halt_reqs <- mkFIFOF;
FIFOF #(Bool) f_hart0_run_halt_rsps <- mkFIFOF;
// Non-standard requests to hart and responses
// Currently only verbosity
FIFOF #(Bit #(4)) f_hart0_other_reqs <- mkFIFOF;
// ----------------------------------------------------------------
Bit #(32) haltregion0 = { 31'h0, pack (! rg_hart0_running) };
Bit #(32) haltsum = { 31'h0, pack (! rg_hart0_running) };
// ----------------------------------------------------------------
// rg_dmstatus
// Since we currently support only 1 hart,
// 'anyXX' = 'allXX'
// 'allrunning' = NOT 'allhalted'
Reg#(Bool) rg_dmstatus_allresumeack <- mkRegU;
Bool dmstatus_allresumeack = rg_dmstatus_allresumeack;
Bool dmstatus_anyresumeack = rg_dmstatus_allresumeack;
Bool dmstatus_allnonexistent = False;
Bool dmstatus_anynonexistent = dmstatus_allnonexistent;
Bool dmstatus_allunavail = False;
Bool dmstatus_anyunavail = dmstatus_allunavail;
Bool dmstatus_allrunning = rg_hart0_running;
Bool dmstatus_anyrunning = dmstatus_allrunning;
Bool dmstatus_allhalted = (! rg_hart0_running);
Bool dmstatus_anyhalted = dmstatus_allhalted;
DM_Word virt_rg_dmstatus = {14'b0,
pack (dmstatus_allresumeack),
pack (dmstatus_anyresumeack),
pack (dmstatus_allnonexistent),
pack (dmstatus_anynonexistent),
pack (dmstatus_allunavail),
pack (dmstatus_anyunavail),
pack (dmstatus_allrunning),
pack (dmstatus_anyrunning),
pack (dmstatus_allhalted),
pack (dmstatus_anyhalted),
pack (True), // authenticated
pack (False), // authbusy
1'b0,
pack (False), // devtreevalid
4'h2}; // version
// ----------------------------------------------------------------
// rg_dmcontrol
Reg #(Bool) rg_dmcontrol_haltreq <- mkRegU;
// resumereq is a W1 field, no need for a register
Reg #(Bool) rg_dmcontrol_hartreset <- mkRegU;
Reg #(Bool) rg_dmcontrol_ndmreset <- mkRegU;
Reg #(Bool) rg_dmcontrol_dmactive <- mkReg (False);
DM_Word virt_rg_dmcontrol = {2'b0, // haltreq, resumereq (w-o)
pack (rg_dmcontrol_hartreset),
2'b0,
pack (False), // hasel
10'b0, // hartsel
14'b0,
pack (rg_dmcontrol_ndmreset),
pack (rg_dmcontrol_dmactive)};
function Action fa_rg_dmcontrol_write (DM_Word dm_word);
action
let haltreq = fn_dmcontrol_haltreq (dm_word);
let resumereq = fn_dmcontrol_resumereq (dm_word);
let hartreset = fn_dmcontrol_hartreset (dm_word);
let hasel = fn_dmcontrol_hasel (dm_word);
let hartsel = fn_dmcontrol_hartsel (dm_word);
let ndmreset = fn_dmcontrol_ndmreset (dm_word);
let dmactive = fn_dmcontrol_dmactive (dm_word);
rg_dmcontrol_haltreq <= haltreq;
rg_dmcontrol_hartreset <= hartreset;
rg_dmcontrol_ndmreset <= ndmreset;
rg_dmcontrol_dmactive <= dmactive;
// Debug Module reset
if (! dmactive) begin
// Reset the DM module itself
$display ("DM_Run_Control.write: dmcontrol 0x%08h (dmactive=0): resetting Debug Module",
dm_word);
// Error-checking
if (ndmreset) begin
$display ("DM_Run_Control.write: WARNING: in word written to dmcontrol (0x%08h):",
dm_word);
$display (" [1] (ndmreset) and [0] (dmactive) both asserted");
$display (" dmactive has priority; ignoring ndmreset");
end
if (hartreset) begin
$display ("DM_Run_Control.write: WARNING: in word written to dmcontrol (0x%08h):",
dm_word);
$display (" [29] (hartreset) and [0] (dmactive) both asserted");
$display (" dmactive has priority; ignoring hartreset");
end
// No action here; other rules will fire (see method dmactive, Debug_Module.rl_reset)
noAction;
end
// Platform reset (non-Debug Module)
else if (ndmreset) begin
$display ("DM_Run_Control.write: dmcontrol 0x%08h: ndmreset=1: resetting platform",
dm_word);
f_ndm_reset_reqs.enq (?);
// Error-checking
if (hartreset) begin
$display ("DM_Run_Control.write: WARNING: in word written to dmcontrol (0x%08h):",
dm_word);
$display (" Both ndmreset (bit 1) and hartreset (bit 29) are asserted");
$display (" ndmreset has priority; ignoring hartreset");
end
end
else begin
// Deassert platform reset
if ((verbosity != 0) && rg_dmcontrol_ndmreset)
$display ("DM_Run_Control.write: dmcontrol 0x%08h: clearing ndmreset", dm_word);
// Hart reset
if (hartreset) begin
if (verbosity != 0)
$display ("DM_Run_Control.write: dmcontrol 0x%08h: hartreset=1: resetting hart",
dm_word);
f_hart0_reset_reqs.enq (?);
end
else begin
// Deassert hart reset
if ((verbosity != 0) && rg_dmcontrol_hartreset)
$display ("DM_Run_Control.write: dmcontrol 0x%08h: clearing hartreset", dm_word);
if (hasel)
$display ("DM_Run_Control.write: ERROR: dmcontrol 0x%08h: 'hasel' is not supported",
dm_word);
if (hartsel != 0)
$display ("DM_Run_Control.write: ERROR: dmcontrol 0x%08h: hartsel 0x%0h not supported",
dm_word, hartsel);
if (haltreq && resumereq) begin
$display ("DM_Run_Control.write: ERROR: dmcontrol 0x%08h: haltreq=1 and resumereq=1",
dm_word);
$display (" This behavior is 'undefined' in the spec; ignoring");
end
// Resume hart(s) if not running
else if (resumereq && (! rg_hart0_running)) begin
f_hart0_run_halt_reqs.enq (True);
rg_dmstatus_allresumeack <= False;
$display ("DM_Run_Control.write: hart0 resume request");
end
// Halt hart(s)
else if (haltreq && !rg_dmcontrol_haltreq) begin
f_hart0_run_halt_reqs.enq (False);
$display ("DM_Run_Control.write: hart0 halt request");
end
end
end
endaction
endfunction
// ----------------------------------------------------------------
// Unimplemented
// dm_addr_hartinfo
// dm_addr_hawindowsel
// dm_addr_hawindow
// dm_addr_devtreeaddr0..3
// dm_addr_authdata
// dm_addr_haltregion1..31
// ----------------------------------------------------------------
// rg_verbosity: non-standard
Reg #(Bit #(4)) rg_verbosity <- mkRegU;
// ----------------------------------------------------------------
rule rl_hart0_run_rsp;
let x = f_hart0_run_halt_rsps.first;
f_hart0_run_halt_rsps.deq;
rg_hart0_running <= x;
if (x) begin
rg_dmstatus_allresumeack <= True;
$display ("DM_Run_Control: hart0 running");
end
else begin
$display ("DM_Run_Control: hart0 halted");
end
endrule
// ----------------------------------------------------------------
// INTERFACE
method Bool dmactive;
return rg_dmcontrol_dmactive;
endmethod
method Action reset;
f_ndm_reset_reqs.clear;
rg_hart0_running <= False; // Must be same as initial state of CPU
f_hart0_reset_reqs.clear;
f_hart0_run_halt_reqs.clear;
f_hart0_run_halt_rsps.clear;
rg_dmcontrol_haltreq <= False;
rg_dmcontrol_hartreset <= False;
rg_dmcontrol_ndmreset <= False;
rg_dmcontrol_dmactive <= True; // DM module is now active
rg_dmstatus_allresumeack <= False;
rg_verbosity <= 0;
if (verbosity != 0)
$display ("DM_Run_Control: reset");
endmethod
// ----------------
// DMI facing GDB/host
method ActionValue #(DM_Word) av_read (DM_Addr dm_addr);
actionvalue
DM_Word dm_word = case (dm_addr)
dm_addr_dmcontrol: virt_rg_dmcontrol;
dm_addr_dmstatus: virt_rg_dmstatus;
dm_addr_haltsum: haltsum;
dm_addr_haltregion0: haltregion0;
dm_addr_verbosity: extend (rg_verbosity);
endcase;
if (verbosity != 0)
$display ("DM_Run_Control.av_read: [", fshow_dm_addr (dm_addr), "] => 0x%08h", dm_word);
return dm_word;
endactionvalue
endmethod
method Action write (DM_Addr dm_addr, DM_Word dm_word);
action
if (verbosity != 0)
$display ("DM_Run_Control.write: [", fshow_dm_addr (dm_addr), "] <= 0x%08h", dm_word);
case (dm_addr)
dm_addr_dmcontrol: fa_rg_dmcontrol_write (dm_word);
dm_addr_verbosity: begin
rg_verbosity <= truncate (dm_word);
f_hart0_other_reqs.enq (truncate (dm_word));
end
default: noAction;
endcase
endaction
endmethod
// ----------------
// Facing Hart: Reset, Run-control, etc.
interface Get hart0_get_reset_req = toGet (f_hart0_reset_reqs);
interface Client hart0_client_run_halt = toGPClient (f_hart0_run_halt_reqs, f_hart0_run_halt_rsps);
interface Get hart0_get_other_req = toGet (f_hart0_other_reqs);
// ----------------
// Facing Platform: Non-Debug-Module Reset (reset all except DM)
interface Get get_ndm_reset_req = toGet (f_ndm_reset_reqs);
endmodule
// ================================================================
endpackage

View File

@@ -0,0 +1,663 @@
// Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved.
package DM_System_Bus;
// ================================================================
// This package implements the 'System Bus Access' part of the RISC-V
// Debug Module, i.e., read/write access to RISC-V system memory.
// ================================================================
// BSV library imports
import FIFOF :: *;
// ----------------
// Other library imports
import Semi_FIFOF :: *;
// ================================================================
// Project Imports
import ISA_Decls :: *;
import DM_Common :: *;
import AXI4_Types :: *;
import Fabric_Defs :: *;
// ================================================================
// Interface
interface DM_System_Bus_IFC;
method Action reset;
// ----------------
// DMI facing GDB/host
method ActionValue #(DM_Word) av_read (DM_Addr dm_addr);
method Action write (DM_Addr dm_addr, DM_Word dm_word);
// ----------------
// Facing System
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master;
endinterface
// ================================================================
// Local definitions
// ----------------
// Convert DM code for access size to AXI4 code for access size
function AXI4_Size fn_DM_sbaccess_to_AXI4_Size (DM_sbaccess sbaccess);
AXI4_Size axi4_size = case (sbaccess)
DM_SBACCESS_8_BIT: axsize_1;
DM_SBACCESS_16_BIT: axsize_2;
DM_SBACCESS_32_BIT: axsize_4;
DM_SBACCESS_64_BIT: axsize_8;
endcase;
return axi4_size;
endfunction
// ----------------
// Extract bytes from raw word read from fabric.
// The bytes of interest are offset according to LSBs of addr.
// Arguments:
// - a DM_sbaccess (indicating size of access)
// - a byte-address
// - a load-word from fabric
// result:
// - word with correct byte(s) shifted into LSBs and zero extended
function Bit #(64) fn_extract_and_extend_bytes (DM_sbaccess sbaccess,
Bit #(64) read_addr,
Bit #(64) word64);
Bit #(3) addr_lsbs = read_addr [2:0];
if (valueOf (Wd_Data) == 32)
addr_lsbs = (addr_lsbs & 'h3);
Bit #(64) result = 0;
case (sbaccess)
DM_SBACCESS_8_BIT: case (addr_lsbs)
'h0: result = zeroExtend (word64 [ 7: 0]);
'h1: result = zeroExtend (word64 [15: 8]);
'h2: result = zeroExtend (word64 [23:16]);
'h3: result = zeroExtend (word64 [31:24]);
'h4: result = zeroExtend (word64 [39:32]);
'h5: result = zeroExtend (word64 [47:40]);
'h6: result = zeroExtend (word64 [55:48]);
'h7: result = zeroExtend (word64 [63:56]);
endcase
DM_SBACCESS_16_BIT: case (addr_lsbs)
'h0: result = zeroExtend (word64 [15: 0]);
'h2: result = zeroExtend (word64 [31:16]);
'h4: result = zeroExtend (word64 [47:32]);
'h6: result = zeroExtend (word64 [63:48]);
endcase
DM_SBACCESS_32_BIT: case (addr_lsbs)
'h0: result = zeroExtend (word64 [31: 0]);
'h4: result = zeroExtend (word64 [63:32]);
endcase
DM_SBACCESS_64_BIT: case (addr_lsbs)
'h0: result = word64;
endcase
endcase
return result;
endfunction
// ----------------
// Compute address, data and strobe (byte-enables) for writes to fabric
function Tuple4 #(Fabric_Addr, // addr is 32b- or 64b-aligned
Fabric_Data, // data is lane-aligned
Fabric_Strb, // strobe
AXI4_Size) // 8 for 8-byte writes, else 4
fn_to_fabric_write_fields (DM_sbaccess sbaccess, // size of access
Bit #(64) addr,
Bit #(64) word64); // data is in lsbs
// First compute addr, data and strobe for a 64b-wide fabric
Bit #(8) strobe64 = 0;
Bit #(3) shift_bytes = addr [2:0];
Bit #(6) shift_bits = { shift_bytes, 3'b0 };
AXI4_Size axsize = axsize_128; // Will be updated in 'case' below
case (sbaccess)
DM_SBACCESS_8_BIT: begin
word64 = (word64 << shift_bits);
strobe64 = ('b_1 << shift_bytes);
axsize = axsize_1;
end
DM_SBACCESS_16_BIT: begin
word64 = (word64 << shift_bits);
strobe64 = ('b_11 << shift_bytes);
axsize = axsize_2;
end
DM_SBACCESS_32_BIT: begin
word64 = (word64 << shift_bits);
strobe64 = ('b_1111 << shift_bytes);
axsize = axsize_4;
end
DM_SBACCESS_64_BIT: begin
strobe64 = 'b_1111_1111;
axsize = axsize_8;
end
endcase
// Adjust for 32b fabrics
if ((valueOf (Wd_Data) == 32) && (addr [2] == 1'b1)) begin
word64 = { 32'h0, word64 [63:32] };
strobe64 = { 4'h0, strobe64 [7:4] };
end
// Finally, create fabric addr/data/strobe
Fabric_Addr fabric_addr = truncate (addr);
Fabric_Data fabric_data = truncate (word64);
Fabric_Strb fabric_strobe = truncate (strobe64);
return tuple4 (fabric_addr, fabric_data, fabric_strobe, axsize);
endfunction: fn_to_fabric_write_fields
// ----------------
// System Bus access states
typedef enum {SB_NOTBUSY,
SB_READ_FINISH,
SB_WRITE_FINISH
} SB_State
deriving (Bits, Eq, FShow);
// ================================================================
// Module implementation
(* synthesize *)
module mkDM_System_Bus (DM_System_Bus_IFC);
Integer verbosity = 0; // Normally 0; non-zero for debugging
// ----------------------------------------------------------------
// Interface to memory fabric
AXI4_Master_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master_xactor <- mkAXI4_Master_Xactor_2;
// ----------------------------------------------------------------
// System Bus state
Reg #(SB_State) rg_sb_state <- mkRegU;
Bool sbbusy = (rg_sb_state != SB_NOTBUSY);
// ----------------------------------------------------------------
// rg_sbaddress0,1 (2 not implemented)
// rg_sbdata0 (1, 2, 3 not implemented)
// Support for RV64. Instead of defining in terms of XLEN, defining using
// DM_Word which is always Bit#(32). 64-bit addressing supported for RV64 but
// only 32-bit data accesses are supported from debugger
Reg #(DM_Word) rg_sbaddress0 <- mkReg (0);
Reg #(DM_Word) rg_sbaddress1 <- mkReg (0); // Will always be zero for RV32
// Saved address during a read rg_sbaddress0/1 may be autoincremented,
// but we need original addr byte-lane extraction from response
Reg #(Bit #(64)) rg_sbaddress_reading <- mkRegU;
Bit #(64) sbaddress = { rg_sbaddress1, rg_sbaddress0 };
Reg #(DM_Word) rg_sbdata0 <- mkRegU;
// ----------------------------------------------------------------
// rg_sbcs
Reg #(Bool) rg_sbcs_sbbusyerror <- mkRegU;
Reg #(Bool) rg_sbcs_sbreadonaddr <- mkRegU;
Reg #(DM_sbaccess) rg_sbcs_sbaccess <- mkRegU;
Reg #(Bool) rg_sbcs_sbautoincrement <- mkRegU;
Reg #(Bool) rg_sbcs_sbreadondata <- mkRegU;
Reg #(DM_sberror) rg_sbcs_sberror <- mkRegU;
UInt #(3) sbversion = 1;
DM_Word virt_rg_sbcs = {pack (sbversion),
6'b0,
pack (rg_sbcs_sbbusyerror),
pack (sbbusy),
pack (rg_sbcs_sbreadonaddr),
pack (rg_sbcs_sbaccess),
pack (rg_sbcs_sbautoincrement),
pack (rg_sbcs_sbreadondata),
pack (rg_sbcs_sberror),
`ifdef RV64
7'd64, // sbasize -- address size
`endif
`ifdef RV32
7'd32, // sbasize -- address size
`endif
1'b0, // sbaccess128
1'b0, // sbaccess64
1'b1, // sbaccess32
1'b1, // sbaccess16
1'b1}; // sbaccess8
// ----------------
// Local defs and help functions
Integer addr_incr = fn_sbaccess_to_addr_incr (rg_sbcs_sbaccess);
function Action fa_sbaddress_incr (Bit #(64) addr64);
action
Bit #(64) next_sbaddress = addr64 + fromInteger (addr_incr);
`ifdef RV64
rg_sbaddress1 <= next_sbaddress [63:32];
`else
rg_sbaddress1 <= 0;
`endif
rg_sbaddress0 <= next_sbaddress [31:0];
if (verbosity != 0)
$display (" Increment sbaddr 0x%08h -> 0x%08h", addr64, next_sbaddress);
endaction
endfunction
// ----------------
// Construction and sending of fabric read-requests
function Action fa_fabric_send_read_req (Bit #(64) addr64);
action
Fabric_Addr fabric_addr = truncate (addr64);
let rda = AXI4_Rd_Addr {arid: fabric_default_id,
araddr: fabric_addr,
arlen: 0, // burst len = arlen+1
arsize: fn_DM_sbaccess_to_AXI4_Size (rg_sbcs_sbaccess),
arburst: fabric_default_burst,
arlock: fabric_default_lock,
arcache: fabric_default_arcache,
arprot: fabric_default_prot,
arqos: fabric_default_qos,
arregion: fabric_default_region,
aruser: fabric_default_user};
master_xactor.i_rd_addr.enq (rda);
// Save read-address for byte-lane extraction from later response
// (since rg_sbaddress may be incremented by then).
rg_sbaddress_reading <= addr64;
rg_sb_state <= SB_READ_FINISH;
if (verbosity != 0) begin
$display (" DM_System_Bus.fa_fabric_send_read_req, and => SB_READ_FINISH ");
$display (" ", fshow (rda));
end
endaction
endfunction
// ----------------
// Construction and sending of fabric write-requests
function Action fa_fabric_send_write_req (Bit #(64) data64);
action
match {.fabric_addr,
.fabric_data,
.fabric_strb,
.fabric_size} = fn_to_fabric_write_fields (rg_sbcs_sbaccess, sbaddress, data64);
// fabric_addr is always fabric-data-width aligned
// fabric_data is properly lane-adjusted
// fabric_strb identifies the lanes to be written
// awsize is always the fabric width
let wra = AXI4_Wr_Addr {awid: fabric_default_id,
awaddr: fabric_addr,
awlen: 0, // burst len = awlen+1
awsize: fabric_size,
awburst: fabric_default_burst,
awlock: fabric_default_lock,
awcache: fabric_default_awcache,
awprot: fabric_default_prot,
awqos: fabric_default_qos,
awregion: fabric_default_region,
awuser: fabric_default_user};
master_xactor.i_wr_addr.enq (wra);
let wrd = AXI4_Wr_Data {wid: fabric_default_id,
wdata: fabric_data,
wstrb: fabric_strb,
wlast: True,
wuser: fabric_default_user};
master_xactor.i_wr_data.enq (wrd);
if (verbosity != 0) begin
$display (" DM_System_Bus.fa_fabric_send_write_req:");
$display (" ", fshow (wra));
$display (" ", fshow (wrd));
end
endaction
endfunction
// ================================================================
// Writes to sbcs
function Action fa_rg_sbcs_write (DM_Word dm_word);
action
Bool sbbusyerror = unpack (dm_word [22]);
Bool sbreadonaddr = unpack (dm_word [20]);
DM_sbaccess sbaccess = unpack (dm_word [19:17]);
Bool sbautoincrement = unpack (dm_word [16]);
Bool sbreadondata = unpack (dm_word [15]);
DM_sberror sberror = unpack (dm_word [14:12]);
// No-op if not clearing existing sberror
if ((rg_sbcs_sberror != DM_SBERROR_NONE) && (sberror == DM_SBERROR_NONE)) begin
// Existing error is not being cleared
$display ("DM_System_Bus.sbcs_write <= 0x%08h: ERROR", dm_word);
$display (" ERROR: existing sberror (0x%0h) is not being cleared.", rg_sbcs_sberror);
$display (" Must be cleared to re-enable system bus access.");
end
// No-op if not clearing existing sbbusyerror
else if (rg_sbcs_sbbusyerror && (! sbbusyerror)) begin
$display ("DM_System_Bus.sbcs_write <= 0x%08h: ERROR", dm_word);
$display (" ERROR: existing sbbusyerror (%0d) is not being cleared.", rg_sbcs_sbbusyerror);
$display (" Must be cleared to re-enable system bus access.");
end
// Check that requested access size is supported
else if ( (sbaccess == DM_SBACCESS_128_BIT)
|| (sbaccess == DM_SBACCESS_64_BIT))
begin
rg_sbcs_sberror <= DM_SBERROR_OTHER;
$display ("DM_System_Bus.sbcs_write <= 0x%08h: ERROR", dm_word);
$display (" ERROR: sbaccess ", fshow (sbaccess), " not supported");
end
// Ok
else begin
if (verbosity != 0) begin
$display (" DM_System_Bus.sbcs_write: ", fshow_sbcs (dm_word));
if (rg_sbcs_sberror != DM_SBERROR_NONE)
$display (" Clearing sbcs.sberror");
if (rg_sbcs_sbbusyerror)
$display (" Clearing sbcs.sbbusyerror");
end
rg_sbcs_sbbusyerror <= False;
rg_sbcs_sbreadonaddr <= sbreadonaddr;
rg_sbcs_sbaccess <= sbaccess;
rg_sbcs_sbautoincrement <= sbautoincrement;
rg_sbcs_sbreadondata <= sbreadondata;
rg_sbcs_sberror <= DM_SBERROR_NONE;
end
endaction
endfunction: fa_rg_sbcs_write
// ================================================================
// rg_sbaddress0, rg_sbaddress1 writes
function Action fa_rg_sbaddress_write (DM_Addr dm_addr, DM_Word dm_word);
action
// Debug announce
if (verbosity != 0) begin
$write ("DM_System_Bus.sbaddress.write: [0x%08h] <= 0x%08h", dm_addr, dm_word);
if (rg_sbcs_sbreadonaddr) begin
$write ("; readonaddr");
if (rg_sbcs_sbautoincrement)
$write ("; autoincrement");
end
$display ("");
end
if (sbbusy) begin
$display ("DM_System_Bus.sbaddress.write: busy, setting sbbusyerror");
rg_sbcs_sbbusyerror <= True;
end
else if (rg_sbcs_sbbusyerror)
$display ("DM_System_Bus.sbaddress.write: ignoring due to sbbusyerror");
else if (rg_sbcs_sberror != DM_SBERROR_NONE)
$display ("DM_System_Bus.sbaddress.write: ignoring due to sberror = 0x%0h",
rg_sbcs_sberror);
else if (dm_addr == dm_addr_sbaddress0) begin
Bit #(64) addr64 = { rg_sbaddress1, dm_word };
if (rg_sbcs_sbreadonaddr) begin
fa_fabric_send_read_req (addr64);
if (rg_sbcs_sbautoincrement)
fa_sbaddress_incr (addr64);
else
rg_sbaddress0 <= dm_word;
end
else
rg_sbaddress0 <= dm_word;
end
else begin // (dm_addr == dm_addr_sbaddress1)
`ifdef RV32
rg_sbaddress1 <= 0;
if (verbosity != 0)
$display ("DM_System_Bus.write: [sbaddress1] <= 0 (RV32: ignoring arg value 0x%08h)",
dm_word);
`else
rg_sbaddress1 <= dm_word;
if (verbosity != 0)
$display ("DM_System_Bus.write: [sbaddress1] <= 0x%08h", dm_word);
`endif
end
endaction
endfunction
// ================================================================
// rg_sbdata0, rg_sbdata1 reads
function ActionValue #(DM_Word) fav_rg_sbdata_read (DM_Addr dm_addr);
actionvalue
DM_Word result = 0;
if (sbbusy) begin
$display ("DM_System_Bus.sbdata.read: busy, setting sbbusyerror");
rg_sbcs_sbbusyerror <= True;
end
else if (rg_sbcs_sbbusyerror)
$display ("DM_System_Bus.sbdata.read: ignoring due to sbbusyerror");
else if (rg_sbcs_sberror != DM_SBERROR_NONE)
$display ("DM_System_Bus.sbdata.read: ignoring due to sberror = 0x%0h", rg_sbcs_sberror);
else begin
if (dm_addr == dm_addr_sbdata0)
result = rg_sbdata0;
/* FUTURE: when supporting DM_SBACCESS_64_BIT
else if (dm_addr == dm_addr_sbdata1)
result = rg_sbdata1;
*/
// Increment sbaddress if needed
if (rg_sbcs_sbautoincrement)
fa_sbaddress_incr (sbaddress);
// Auto-read next data if needed
if (rg_sbcs_sbreadondata && (dm_addr == dm_addr_sbdata0))
fa_fabric_send_read_req (sbaddress);
end
return result;
endactionvalue
endfunction
// ----------------
// Finish read request (handle fabric response)
(* descending_urgency = "rl_sb_read_finish, reset" *)
(* descending_urgency = "rl_sb_read_finish, write" *)
rule rl_sb_read_finish ( (rg_sb_state == SB_READ_FINISH)
&& (rg_sbcs_sberror == DM_SBERROR_NONE));
let rdr <- pop_o (master_xactor.o_rd_data);
if (verbosity != 0)
$display ("DM_System_Bus.rule_sb_read_finish: rdr = ", fshow (rdr));
// Extract relevant bytes from fabric data
Bit #(64) rdata64 = zeroExtend (rdr.rdata);
Bit #(64) data = fn_extract_and_extend_bytes (rg_sbcs_sbaccess, rg_sbaddress_reading, rdata64);
if (rdr.rresp != axi4_resp_okay) begin
$display ("DM_System_Bus.rule_sb_read_finish: setting rg_sbcs_sberror to DM_SBERROR_OTHER\n");
$display (" rdr = ", fshow (rdr));
rg_sbcs_sberror <= DM_SBERROR_OTHER;
end
rg_sbdata0 <= data [31:0];
/* FUTURE: when supporting DM_SBACCESS_64_BIT
rg_sbdata1 <= data [63:32];
*/
if (verbosity != 0) begin
$display ("DM_System_Bus.rule_sb_read_finish: addr 0x%0h, sbaccess %0d (%0d bytes)",
rg_sbaddress_reading, rg_sbcs_sbaccess, addr_incr);
$display (" rg_sbdata0 <= 0x%0h", data);
$display (" module state => SB_NOTBUSY");
end
rg_sb_state <= SB_NOTBUSY;
endrule
// ================================================================
// rg_sbdata0, rg_sbdata1 writes
function Action fa_rg_sbdata_write (DM_Addr dm_addr, DM_Word dm_word);
action
if (sbbusy) begin
$display ("DM_System_Bus.sbdata.write: busy, setting sbbusyerror");
rg_sbcs_sbbusyerror <= True;
end
else if (rg_sbcs_sbbusyerror) begin
$display ("DM_System_Bus.sbdata.write: ignoring due to sbbusyerror");
end
else if (rg_sbcs_sberror != DM_SBERROR_NONE) begin
$display ("DM_System_Bus.sbdata.write: ignoring due to sberror = 0x%0h",
rg_sbcs_sberror);
end
else begin
if (verbosity != 0)
$display (" DM_System_Bus.fa_rg_sbdata_write: dm_addr 0x%08h dm_word 0x%08h",
dm_addr, dm_word);
if (dm_addr == dm_addr_sbdata0)
rg_sbdata0 <= dm_word;
/* FUTURE: when supporting DM_SBACCESS_64_BIT
else if (dm_addr == dm_addr_sbdata1)
rg_sbdata1 <= dm_word;
*/
// Initiate system bus write if writing to sbdata0
if (dm_addr == dm_addr_sbdata0) begin
fa_fabric_send_write_req (zeroExtend (dm_word));
// Increment sbaddr ifneeded
if (rg_sbcs_sbautoincrement)
fa_sbaddress_incr (sbaddress);
end
end
endaction
endfunction
// ----------------
// Consume write-responses
rule rl_sb_write_response;
let wrr <- pop_o (master_xactor.o_wr_resp);
if (wrr.bresp != axi4_resp_okay)
rg_sbcs_sberror <= DM_SBERROR_OTHER;
endrule
// ================================================================
// INTERFACE
method Action reset;
// master_xactor.reset; // TODO: introduces a scheduling cycle: fix this
rg_sb_state <= SB_NOTBUSY;
rg_sbcs_sbbusyerror <= False;
rg_sbcs_sbreadonaddr <= False;
rg_sbcs_sbaccess <= DM_SBACCESS_32_BIT;
rg_sbcs_sbautoincrement <= False;
rg_sbcs_sbreadondata <= False;
rg_sbcs_sberror <= DM_SBERROR_NONE;
rg_sbaddress0 <= 0;
rg_sbaddress1 <= 0;
rg_sbdata0 <= 0;
if (verbosity != 0)
$display ("DM_System_Bus: reset");
endmethod
// ----------------
// DMI facing GDB/host
// The predicate on read allows communication flow control to
// throttle requests. This achieves better performance, but is not
// workable for a true JTAG transport.
method ActionValue #(DM_Word) av_read (DM_Addr dm_addr) if (!sbbusy);
actionvalue
DM_Word dm_word = 0;
if (dm_addr == dm_addr_sbcs) begin
dm_word = virt_rg_sbcs;
if (verbosity != 0)
$display ("DM_System_Bus.read: [sbcs] => ", fshow_sbcs (dm_word));
end
else if (dm_addr == dm_addr_sbaddress0) begin
dm_word = rg_sbaddress0;
if (verbosity != 0)
$display ("DM_System_Bus.read: [sbaddress0] => 0x%08h", dm_word);
end
else if (dm_addr == dm_addr_sbaddress1) begin
dm_word = rg_sbaddress1;
if (verbosity != 0)
$display ("DM_System_Bus.read: [sbaddress1] => 0x%08h", dm_word);
end
else if (dm_addr == dm_addr_sbdata0) begin
dm_word <- fav_rg_sbdata_read (dm_addr_sbdata0);
end
else begin
// Unsupported dm address
dm_word = 0;
$display ("DM_System_Bus.read: [", fshow_dm_addr (dm_addr), "] not supported");
end
return dm_word;
endactionvalue
endmethod
method Action write (DM_Addr dm_addr, DM_Word dm_word);
action
if (dm_addr == dm_addr_sbcs)
fa_rg_sbcs_write (dm_word);
else if ((dm_addr == dm_addr_sbaddress0) || (dm_addr == dm_addr_sbaddress1))
fa_rg_sbaddress_write (dm_addr, dm_word);
else if (dm_addr == dm_addr_sbdata0) // FUTURE: || (dm_addr == dm_addr_sbdata1)
fa_rg_sbdata_write (dm_addr, dm_word);
else begin
// Unsupported dm_addr
let addr_name = fshow_dm_addr (dm_addr);
$display ("DM_System_Bus.write: [", addr_name, "] <= 0x%08h; addr not supported", dm_word);
end
endaction
endmethod
// ----------------
// Facing System
interface AXI4_Master_IFC master = master_xactor.axi_side;
endmodule
// ================================================================
endpackage

View File

@@ -0,0 +1,287 @@
// Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved.
package Debug_Module;
// ================================================================
// This is the top-level package of a collection that implements the
// "Debug Module" specified in:
//
// "RISC-V External Debug Support"
// Version 0.13
// 7c760b0151e43523ab3d2180e7852cd6f27d942c
// Mon Jul 3 17:04:59 2017 -0700
// Note: the spec actually represents three (almost) completely
// independent functionalities:
// Run Control: to start/stop harts, query their start/stop status, etc.
// Abstract Commands: to read/write RISC-V registers and RISC-V CSRs
// System Bus Access: to read/write RISC-V memory/devices
// The only exception to complete independence is that the Run Control
// part has a 'reset' for the Debug Module itself, which includes all
// three parts.
// Unfortunately the spec is not written to make this three-part
// organization clear--aspects of the three parts are completely
// intermingled. Even the address map mixes registers from the three
// parts.
// Here, this top-level package is merely a wrapper that dispatches
// DMI requests to the three packages that implement the three parts:
// DM_Run_Control
// DM_Abstract_Commands
// DM_System_Bus
// DM_Common contains common spec-level (implementation-independent) definitions.
// ================================================================
// Our Debug Module (DM) has a two sides:
// - GDB/Host-facing
// - CPU/Platform-facing
// The GDB/Host-facing side is called DMI (Debug Module Interface) in
// the spec, and is a simple memory-like read/write interface, into a
// debug module address space (unrelated to the RISC-V memory address
// space).
// The CPU/Platform-facing side has request/response interfaces for
// CPU run-control, CPU register/CSR access and RISC-V system memory
// access.
// ================================================================
// BSV library imports
import Memory :: *;
import FIFOF :: *;
import GetPut :: *;
import ClientServer :: *;
import SpecialFIFOs :: *;
// ----------------
// Other library imports
import Semi_FIFOF :: *;
import Cur_Cycle :: *;
// ================================================================
// Project imports
import ISA_Decls :: *;
import AXI4_Types :: *;
import Fabric_Defs :: *;
import DM_Common :: *;
import DM_Run_Control :: *;
import DM_Abstract_Commands :: *;
import DM_System_Bus :: *;
// ================================================================
export DM_Common :: *;
export Debug_Module_IFC (..);
export mkDebug_Module;
// ================================================================
// Interface to the Debug Module
interface Debug_Module_IFC;
// ----------------
// DMI (Debug Module Interface) facing remote debugger
interface DMI dmi;
// ----------------
// Facing CPU
// This section replicated for additional harts.
// Reset and run-control
interface Get #(Token) hart0_get_reset_req;
interface Client #(Bool, Bool) hart0_client_run_halt;
interface Get #(Bit #(4)) hart0_get_other_req;
// GPR access
interface MemoryClient #(5, XLEN) hart0_gpr_mem_client;
// CSR access
interface MemoryClient #(12, XLEN) hart0_csr_mem_client;
// ----------------
// Facing Platform
// Non-Debug-Module Reset (reset all except DM)
interface Get #(Token) get_ndm_reset_req;
// Read/Write RISC-V memory
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master;
endinterface
// ================================================================
(* synthesize *)
module mkDebug_Module (Debug_Module_IFC);
// The three parts
DM_Run_Control_IFC dm_run_control <- mkDM_Run_Control;
DM_Abstract_Commands_IFC dm_abstract_commands <- mkDM_Abstract_Commands;
DM_System_Bus_IFC dm_system_bus <- mkDM_System_Bus;
FIFOF#(DM_Addr) f_read_addr <- mkBypassFIFOF;
// ================================================================
// Reset all three parts when dm_run_control.dmactive is low
rule rl_reset (! dm_run_control.dmactive);
$display ("%0d: Debug_Module reset", cur_cycle);
dm_run_control.reset;
dm_abstract_commands.reset;
dm_system_bus.reset;
endrule
// ================================================================
// INTERFACE
// ----------------
// Facing GDB/DMI (Debug Module Interface)
interface DMI dmi;
method Action read_addr (DM_Addr dm_addr);
f_read_addr.enq(dm_addr);
endmethod
method ActionValue #(DM_Word) read_data;
let dm_addr = f_read_addr.first;
f_read_addr.deq;
DM_Word dm_word = ?;
if ( (dm_addr == dm_addr_dmcontrol)
|| (dm_addr == dm_addr_dmstatus)
|| (dm_addr == dm_addr_hartinfo)
|| (dm_addr == dm_addr_haltsum)
|| (dm_addr == dm_addr_hawindowsel)
|| (dm_addr == dm_addr_hawindow)
|| (dm_addr == dm_addr_devtreeaddr0)
|| (dm_addr == dm_addr_authdata)
|| (dm_addr == dm_addr_haltregion0)
|| (dm_addr == dm_addr_haltregion31)
|| (dm_addr == dm_addr_verbosity))
dm_word <- dm_run_control.av_read (dm_addr);
else if ( (dm_addr == dm_addr_abstractcs)
|| (dm_addr == dm_addr_command)
|| (dm_addr == dm_addr_data0)
|| (dm_addr == dm_addr_data1)
|| (dm_addr == dm_addr_data2)
|| (dm_addr == dm_addr_data3)
|| (dm_addr == dm_addr_data4)
|| (dm_addr == dm_addr_data5)
|| (dm_addr == dm_addr_data6)
|| (dm_addr == dm_addr_data7)
|| (dm_addr == dm_addr_data8)
|| (dm_addr == dm_addr_data9)
|| (dm_addr == dm_addr_data10)
|| (dm_addr == dm_addr_data11)
|| (dm_addr == dm_addr_abstractauto)
|| (dm_addr == dm_addr_progbuf0))
dm_word <- dm_abstract_commands.av_read (dm_addr);
else if ( (dm_addr == dm_addr_sbcs)
|| (dm_addr == dm_addr_sbaddress0)
|| (dm_addr == dm_addr_sbaddress1)
|| (dm_addr == dm_addr_sbaddress2)
|| (dm_addr == dm_addr_sbdata0)
|| (dm_addr == dm_addr_sbdata1)
|| (dm_addr == dm_addr_sbdata2)
|| (dm_addr == dm_addr_sbdata3))
dm_word <- dm_system_bus.av_read (dm_addr);
else begin
// TODO: set error status?
dm_word = 0;
end
return dm_word;
endmethod
method Action write (DM_Addr dm_addr, DM_Word dm_word);
if ( (dm_addr == dm_addr_dmcontrol)
|| (dm_addr == dm_addr_dmstatus)
|| (dm_addr == dm_addr_hartinfo)
|| (dm_addr == dm_addr_haltsum)
|| (dm_addr == dm_addr_hawindowsel)
|| (dm_addr == dm_addr_hawindow)
|| (dm_addr == dm_addr_devtreeaddr0)
|| (dm_addr == dm_addr_authdata)
|| (dm_addr == dm_addr_haltregion0)
|| (dm_addr == dm_addr_haltregion31)
|| (dm_addr == dm_addr_verbosity))
dm_run_control.write (dm_addr, dm_word);
else if ( (dm_addr == dm_addr_abstractcs)
|| (dm_addr == dm_addr_command)
|| (dm_addr == dm_addr_data0)
|| (dm_addr == dm_addr_data1)
|| (dm_addr == dm_addr_data2)
|| (dm_addr == dm_addr_data3)
|| (dm_addr == dm_addr_data4)
|| (dm_addr == dm_addr_data5)
|| (dm_addr == dm_addr_data6)
|| (dm_addr == dm_addr_data7)
|| (dm_addr == dm_addr_data8)
|| (dm_addr == dm_addr_data9)
|| (dm_addr == dm_addr_data10)
|| (dm_addr == dm_addr_data11)
|| (dm_addr == dm_addr_abstractauto)
|| (dm_addr == dm_addr_progbuf0))
dm_abstract_commands.write (dm_addr, dm_word);
else if ( (dm_addr == dm_addr_sbcs)
|| (dm_addr == dm_addr_sbaddress0)
|| (dm_addr == dm_addr_sbaddress1)
|| (dm_addr == dm_addr_sbaddress2)
|| (dm_addr == dm_addr_sbdata0)
|| (dm_addr == dm_addr_sbdata1)
|| (dm_addr == dm_addr_sbdata2)
|| (dm_addr == dm_addr_sbdata3))
dm_system_bus.write (dm_addr, dm_word);
else begin
// TODO: set error status?
noAction;
end
endmethod
endinterface
// ----------------
// Facing CPU/hart0
// Reset and run-control
interface Get hart0_get_reset_req = dm_run_control.hart0_get_reset_req;
interface Client hart0_client_run_halt = dm_run_control.hart0_client_run_halt;
interface Get hart0_get_other_req = dm_run_control.hart0_get_other_req;
// GPR access
interface MemoryClient hart0_gpr_mem_client = dm_abstract_commands.hart0_gpr_mem_client;
// CSR access
interface MemoryClient hart0_csr_mem_client = dm_abstract_commands.hart0_csr_mem_client;
// ----------------
// Facing Platform
// Non-Debug-Module Reset (reset all except DM)
interface Get get_ndm_reset_req = dm_run_control.get_ndm_reset_req;
// Read/Write RISC-V memory
interface AXI4_Master_IFC master = dm_system_bus.master;
endmodule
// ================================================================
endpackage

View File

@@ -0,0 +1,162 @@
'Debug_Module' implements a Debug Module for RISC-V processors in
accordance with the RISC-V standard "External Debug Support" spec:
RISC-V External Debug Support
Version 0.13-DRAFT
dd8d8714184970031fa447a452068223f257b51c
Mon Dec 18 13:54:14 2017 -0800
Note: the spec is independent of any particular RISC-V CPU
implementation. It just specifies the standard registers in the Debug
Module that can be read and written by an external debugger (such as
GDB). It specifies the address map of these registers, and the
semantics, i.e., what happens when one reads or writes these
registers. The spec does not say anything about how this spec is
implemented.
Please see comments in Debug_Module.bsv for more details on our
implementation of the Debug Module spec. This implementation is also
not specific to any particular CPU implementation. We use it in
Bluespec RISC-V CPUs, but it could be used with other CPUs as well.
We use this Debug Module in the Bluespec RISC-V Verification Factory
(BRVF).
// ================================================================
What follows is a concise cheat-sheet on the registers in the Debug
Module based on the spec.
// ----------------
// Run Control
DM_Addr dm_addr_dmcontrol = 'h10;
31.30.29.28| 27.26.25.24| 23.22.21.20| 19.18.17.16| 15.14.13.12| 11.10.9.8| 7.6.5.4| 3.2.1.0|
| | | | |------------10--------------| | |dmactive
| | | | |hartsel |ndmreset
| | | |hasel
| | | 0: Single hart selected (hartsel)
| | | 1: Multiple harts selected (hartsel + hart array mask)
| | |hartreset
| |resumereq
|haltreq
DM_Addr dm_addr_dmstatus = 'h11;
31.30.29.28| 27.26.25.24| 23.22.21.20| 19.18.17.16| 15.14.13.12| 11.10.9.8| 7.6.5.4| 3.2.1.0|
| | | | | | | | | | | | | | | | | | | | | | | | |--4--|version
| | | | | | | | | | | | | | | | | | | | | | | | | 0: no DM present
| | | | | | | | | | | | | | | | | | | | | | | | | 1: DM v011
| | | | | | | | | | | | | | | | | | | | | | | | | 2: DM v013
| | | | | | | | | | | | | | | | | | | | | | | | | 15: DM vUnknown
| | | | | | | | | | | | | | | | | | | | | | | |devtreevalid
| | | | | | | | | | | | | | | | | | | | | | |0
| | | | | | | | | | | | | | | | | | | | | |authbusy
| | | | | | | | | | | | | | | | | | | | |authenticated
| | | | | | | | | | | | | | | | | | | |anyhalted
| | | | | | | | | | | | | | | | | | |allhalted
| | | | | | | | | | | | | | | | | |anyrunning
| | | | | | | | | | | | | | | | |allrunning
| | | | | | | | | | | | | | | |anyunavail
| | | | | | | | | | | | | | |allunavail
| | | | | | | | | | | | | |anynonexistent
| | | | | | | | | | | | |allnonexistent
| | | | | | | | | | | |anyresumeack
| | | | | | | | | | |allresumeack
| | | | | | | | | |anyhavereset
| | | | | | | | |allhavereset
| | | | | | |--|0
| | | | | |impebreak
| | | | | 0 No implicit EBREAK at end of PB
| | | | | 1 Implicit EBREAK at end of PB
| | | | |0
| | |--3--|dmerr
| | 0 No error
| | 1 bad addr
| | 7 other error
|----- 5-----|0
DM_Addr dm_addr_hartinfo = 'h12;
DM_Addr dm_addr_haltsum = 'h13;
DM_Addr dm_addr_hawindowsel = 'h14;
DM_Addr dm_addr_hawindow = 'h15;
DM_Addr dm_addr_devtreeaddr0 = 'h19;
DM_Addr dm_addr_authdata = 'h30;
DM_Addr dm_addr_haltregion0 = 'h40;
DM_Addr dm_addr_haltregion31 = 'h5F;
// ----------------
// Abstract commands (read/write RISC-V registers and RISC-V CSRs)
DM_Addr dm_addr_abstractcs = 'h16;
31.30.29.28| 27.26.25.24| 23.22.21.20| 19.18.17.16| 15.14.13.12| 11.10.9.8| 7.6.5.4| 3.2.1.0|
|-----5------|progsize |busy |-3-|cmderr |----5---|datacount
DM_Addr dm_addr_command = 'h17;
31.30.29.28| 27.26.25.24| 23.22.21.20| 19.18.17.16| 15.14.13.12| 11.10.9.8| 7.6.5.4| 3.2.1.0|
| | | | | |-----------------16------------------|regno
| | | | | | 0x0000-0x0FFF CSRs (dpc => PC)
| | | | | | 0x1000-0x101F GPRs
| | | | | | 0x1020-0x103F Floating Point Regs
| | | | | | 0xC000-0xFFFF Reserved
| | | | |write
| | | | 0: specified reg -> arg0 of data
| | | | 1: specified reg <- arg0 of data
| | | |transfer
| | | 0 Don't do the 'write' op
| | | 1 Do the 'write' op
| | | Allows exec of PB without valid vals in 'size' and 'regno'
| | |postexec
| | 1 exec Program Buffer exactly once after the xfer
| |--3--|size
| 2 Lowest 32b of reg
| 3 Lowest 64b of reg
| 4 Lowest 128b of reg
|---------8-----------|cmdtype
0 ACCESS_REG
1 QUICK_ACCESS
DM_Addr dm_addr_data0 = 'h04;
DM_Addr dm_addr_data1 = 'h05;
DM_Addr dm_addr_data2 = 'h06;
DM_Addr dm_addr_data3 = 'h07;
DM_Addr dm_addr_data4 = 'h08;
DM_Addr dm_addr_data5 = 'h09;
DM_Addr dm_addr_data6 = 'h0a;
DM_Addr dm_addr_data7 = 'h0b;
DM_Addr dm_addr_data8 = 'h0c;
DM_Addr dm_addr_data9 = 'h0d;
DM_Addr dm_addr_data10 = 'h0d;
DM_Addr dm_addr_data11 = 'h0f;
DM_Addr dm_addr_abstractauto = 'h18;
DM_Addr dm_addr_progbuf0 = 'h20;
// ----------------
// System Bus access (read/write RISC-V memory/devices)
DM_Addr dm_addr_sbcs = 'h38;
31.30.29.28| 27.26.25.24| 23.22.21.20| 19.18.17.16| 15.14.13.12| 11.10.9.8| 7.6.5.4| 3.2.1.0|
| | | | | | | | | | | |sbaccess8
| | | | | | | | | | |sbaccess16
| | | | | | | | | |sbaccess32
| | | | | | | | |sbaccess64
| | | | | | | |sbaccess128
| | | | | | |-----7-------|sbasize
| | | | |--3--|sberror
| | | | | 0: no bus err
| | | | | 1: timeout
| | | | | 2: bad addr
| | | | | 3: other err (e.g., alignment)
| | | | | 4: busy
| | | |sbautoread
| | |sbautoincrement
| |--3--|sbaccess
| | 0:8b, 1:16b, 2:32b, 3:64b, 4:128b
|singleread
1 triggers single read at sbaddress of size sbaccess
DM_Addr dm_addr_sbaddress0 = 'h39;
DM_Addr dm_addr_sbaddress1 = 'h3a;
DM_Addr dm_addr_sbaddress2 = 'h3b;
DM_Addr dm_addr_sbdata0 = 'h3c;
DM_Addr dm_addr_sbdata1 = 'h3d;
DM_Addr dm_addr_sbdata2 = 'h3e;
DM_Addr dm_addr_sbdata3 = 'h3f;

View File

@@ -0,0 +1,94 @@
default: compile link
all: compile link simulate
TOP = Testbench
TOPFILE = $(TOP).bsv
TOPMODULE = mk$(TOP)
# BSCFLAGS = -keep-fires -aggressive-conditions -no-warn-action-shadowing -no-inline-rwire
# BSCFLAGS = -keep-fires -aggressive-conditions -no-inline-rwire -show-range-conflict -show-schedule
BSCFLAGS = -D RV32 \
-keep-fires \
-aggressive-conditions \
-suppress-warnings G0020 \
-show-schedule
# ----------------------------------------------------------------
# FOR BLUESIM
ISA_DECLS_DIR = $(HOME)/Projects/RISCV/Bluespec_RISCV/ISA
TRX_DIR = $(HOME)/Projects/RISCV/Bluespec_RISCV/Fabrics/TRX
ADDL_LIBS_DIR = $(HOME)/Projects/RISCV/Bluespec_RISCV/BSV_Additional_Libs/BSV
BSCDIRS_BSIM = -simdir build_bsim -bdir build -info-dir build
BSCPATH_BSIM = -p .:..:$(ISA_DECLS_DIR):$(TRX_DIR):$(ADDL_LIBS_DIR):%/Prelude:%/Libraries
build_bsim:
mkdir -p $@
build:
mkdir -p $@
.PHONY: compile
compile: build_bsim build
@echo Compiling...
bsc -u -sim $(BSCDIRS_BSIM) $(BSCFLAGS) $(BSCPATH_BSIM) $(TOPFILE)
@echo Compilation finished
.PHONY: link
link:
@echo Linking...
bsc -e $(TOPMODULE) $(BSCFLAGS) -parallel-sim-link 8 -sim -o ./$(TOP)_bsim_exe $(BSCDIRS_BSIM) $(BSCPATH_BSIM)
@echo Linking finished
.PHONY: simulate
simulate:
@echo Simulation...
logsave bsim.log ./$(TOP)_bsim_exe -V
@echo Simulation finished
# ----------------------------------------------------------------
# FOR VERILOG
BSCDIRS_V = -vdir verilog -bdir build_v -info-dir build_v
BSCPATH_V = -p .:./$(SRC_BSV):%/Prelude:%/Libraries:%/Libraries/TLM3
# Set VSIM to desired Verilog simulator
# VSIM = modelsim
VSIM ?= cvc
# VSIM ?= iverilog
build_v:
mkdir -p $@
verilog:
mkdir -p $@
.PHONY: rtl
rtl: build_v verilog
@echo Verilog generation ...
bsc -u -elab -verilog $(BSCDIRS_V) $(BSCFLAGS) $(BSCPATH_V) $(TOPFILE)
@echo Verilog generation finished
.PHONY: vlink
vlink:
bsc -v -e $(TOPMODULE) -verilog -o ./out_v -vdir verilog -vsim $(VSIM) -keep-fires \
verilog/$(TOPMODULE).v
.PHONY: vsim
vsim:
@echo Simulation...
./out_v
@echo Simulation finished
# ----------------------------------------------------------------
.PHONY: clean
clean:
rm -f *~ src_*/*~ src_*/*.o build/* build_bsim/* build_v/* *.cxx *.h *.o
.PHONY: full_clean
full_clean: clean
rm -r -f *_bsim_exe *.so out_v verilog build build_bsim dump.vcd bsim.log

View File

@@ -0,0 +1,597 @@
package Testbench;
// ================================================================
// Testbench for basic sanity-check testing of the Debug Module.
// ================================================================
// BSV library imports
import FIFOF :: *;
import GetPut :: *;
import ClientServer :: *;
import Connectable :: *;
import StmtFSM :: *;
// ----------------
// Other library imports
import Semi_FIFOF :: *;
// ================================================================
import ISA_Decls :: *;
import TRX :: *;
import Debug_Module :: *;
// ================================================================
Integer csr_addr_dcsr = 'h7b0;
Integer csr_addr_dpc = 'h7b1;
Integer csr_addr_dscratch0 = 'h7b2;
Integer csr_addr_dscratch1 = 'h7b3;
// ================================================================
(* synthesize *)
module mkTestbench (Empty);
// ================================================================
// Cycle-counter and cycle-limit termination
Reg #(Bit #(32)) rg_cycle <- mkReg (0);
Integer cycle_limit = 100;
rule rl_count_cycles;
if (rg_cycle == fromInteger (100)) begin
$display ("Testench: stopping at cycle %0d", cycle_limit);
$finish (0);
end
rg_cycle <= rg_cycle +1;
endrule
// ================================================================
// The Debug Module
Debug_Module_IFC dm <- mkDebug_Module;
// ================================================================
// Model of a hart, and connections to dm
Hart_DM_IFC hart0 <- mkHart_Model (0);
// Reset
mkConnection (dm.hart0_reset_req, hart0.hart_reset_req);
// Run-control
mkConnection (dm.hart0_run_req_rsp, hart0.hart_run_req_rsp);
// GPR access
mkConnection (dm.master_for_gprs, hart0.slave_for_gprs);
// CSR access
mkConnection (dm.master_for_csrs, hart0.slave_for_csrs);
// ================================================================
// Over-simplified model of platform reset (all except DM)
rule rl_ndm_reset;
let x <- dm.ndm_reset_req.get;
$display ("Testbench.rl_ndm_reset: Resetting all platform except Debug Module");
endrule
// ================================================================
// Over-simplified model of system (RISC-V) memory
// On reads, return addr + 2.
rule rl_mem_read;
let rda <- pop_o (dm.master.fo_rda);
let data = rda.addr + 2; // Bogus data, for now
let rdr = TRX_RdR {trans_id: rda.trans_id,
status : TRX_OKAY,
data : data};
dm.master.fi_rdr.enq (rdr);
$display ("Testbench: memory read [0x%08h] => 0x%08h", rda.addr, data);
endrule
rule rl_mem_write;
let wra <- pop_o (dm.master.fo_wra);
let wrd <- pop_o (dm.master.fo_wrd);
let wrr = TRX_WrR {trans_id: wra.trans_id,
status : TRX_OKAY};
dm.master.fi_wrr.enq (wrr);
$display ("Testbench: memory write [0x%08h] <= 0x%08h", wra.addr, wrd.data);
endrule
// ================================================================
// Abstract command sequences (read/write GPR/CSR)
Reg #(Bit #(32)) rg_abstractcs <- mkRegU;
// Read a register
function Stmt fn_stmt_read_reg (Bit #(16) regno);
return
seq
$display ("----------------\nRead RISC-V reg");
// Clear any prior error status
dm.write (dm_addr_abstractcs, fn_mk_abstractcs (dm_cmderr_w1c));
// Perform the read
dm.write (dm_addr_command,
fn_mk_command_access_reg (DM_COMMAND_ACCESS_REG_SIZE_LOWER32,
False, // postexec
True, // transfer
False, // write
regno));
// Read status to check no error
action
let x <- dm.av_read (dm_addr_abstractcs);
rg_abstractcs <= x;
endaction
while (fn_abstractcs_busy (rg_abstractcs)) seq
$display ("Testbench: read reg: busy");
action
let x <- dm.av_read (dm_addr_abstractcs);
rg_abstractcs <= x;
endaction
endseq
if (fn_abstractcs_cmderr (rg_abstractcs) != DM_ABSTRACTCS_CMDERR_NONE)
$display ("Testbench: read reg => ", fshow (fn_abstractcs_cmderr (rg_abstractcs)));
else action
let x <- dm.av_read (dm_addr_data0);
$display ("Testbench: read reg => 0x%08h", x);
endaction
endseq;
endfunction
// Write a register
function Stmt fn_stmt_write_reg (Bit #(16) regno, Bit #(32) data);
return
seq
$display ("----------------\nWrite RISC-V reg");
// Clear any prior error status
dm.write (dm_addr_abstractcs, fn_mk_abstractcs (dm_cmderr_w1c));
// Write data0
dm.write (dm_addr_data0, data);
// Perform the write
dm.write (dm_addr_command,
fn_mk_command_access_reg (DM_COMMAND_ACCESS_REG_SIZE_LOWER32,
False, // postexec
True, // transfer
True, // write
regno));
// Read status to check no error
action
let x <- dm.av_read (dm_addr_abstractcs);
rg_abstractcs <= x;
endaction
while (fn_abstractcs_busy (rg_abstractcs)) seq
$display ("Testbench: write reg: busy");
action
let x <- dm.av_read (dm_addr_abstractcs);
rg_abstractcs <= x;
endaction
endseq
$display ("Testbench: write reg => ", fshow (fn_abstractcs_cmderr (rg_abstractcs)));
endseq;
endfunction
// ================================================================
// System Bus access sequences (read/write RISC-V memory)
Reg #(Bool) rg_busy <- mkRegU;
Reg #(Bit #(32)) rg_j <- mkRegU;
Reg #(Bit #(32)) rg_addr <- mkRegU;
Reg #(Bit #(32)) rg_data <- mkRegU;
Stmt stmt_wait_for_sb_nonbusy = (
seq
rg_busy <= True;
while (rg_busy) seq
delay (1);
action
let x <- dm.av_read (dm_addr_sbcs);
let sberror = fn_sbcs_sberror (x);
rg_busy <= (sberror == DM_SBERROR_BUSY_STALE);
if ( (sberror != DM_SBERROR_NONE)
&& (sberror != DM_SBERROR_BUSY_STALE))
begin
$display ("Testbench: stmt_wait_for_sb_nonbusy: ", fshow (sberror));
$finish (1);
end
endaction
endseq
endseq);
// Do a single-read from memory
Stmt stmt_mem_read_1 = (
seq
dm.write (dm_addr_sbaddress0, 'h1_0000);
dm.write (dm_addr_sbcs, fn_mk_sbcs (True, // sbsingleread
DM_SBACCESS_32_BIT,
False, // sbautoincrement
False, // sbautoread
DM_SBERROR_UNDEF7_W1C)); // clear sberror
stmt_wait_for_sb_nonbusy;
action
let x <- dm.av_read (dm_addr_sbdata0);
$display ("stmt_mem_read_1: read-data = 0x%08h", x);
endaction
endseq);
// Do a multiple-read from memory
Stmt stmt_mem_read_4 = (
seq
dm.write (dm_addr_sbaddress0, 'h1_0000);
dm.write (dm_addr_sbcs, fn_mk_sbcs (True, // sbsingleread
DM_SBACCESS_32_BIT,
True, // sbautoincrement
True, // sbautoread
DM_SBERROR_UNDEF7_W1C)); // clear sberror
for (rg_j <= 0; rg_j < 3; rg_j <= rg_j + 1) seq
stmt_wait_for_sb_nonbusy;
action
let x <- dm.av_read (dm_addr_sbdata0);
$display ("stmt_mem_read_4: read-data [%0d] = 0x%08h", rg_j, x);
endaction
endseq
dm.write (dm_addr_sbcs, fn_mk_sbcs (False, // sbsingleread
DM_SBACCESS_32_BIT,
False, // sbautoincrement
False, // sbautoread
DM_SBERROR_UNDEF7_W1C)); // clear sberror
stmt_wait_for_sb_nonbusy;
action
let x <- dm.av_read (dm_addr_sbdata0);
$display ("stmt_mem_read_4: read-data [%0d] = 0x%08h", rg_j, x);
endaction
endseq);
// Do a single-write to memory
Stmt stmt_mem_write_1 = (
seq
dm.write (dm_addr_sbcs, fn_mk_sbcs (False, // sbsingleread
DM_SBACCESS_32_BIT,
False, // sbautoincrement
False, // sbautoread
DM_SBERROR_UNDEF7_W1C)); // clear sberror
stmt_wait_for_sb_nonbusy;
dm.write (dm_addr_sbaddress0, 'h1_0000);
dm.write (dm_addr_sbdata0, 'h_BEEF);
endseq);
// Do a multiple-write to memory
Stmt stmt_mem_write_4 = (
seq
dm.write (dm_addr_sbcs, fn_mk_sbcs (False, // sbsingleread
DM_SBACCESS_32_BIT,
True, // sbautoincrement
False, // sbautoread
DM_SBERROR_UNDEF7_W1C)); // clear sberror
stmt_wait_for_sb_nonbusy;
action
rg_addr <= 'h_2000;
rg_data <= 'h_DAFA_0000;
endaction
dm.write (dm_addr_sbaddress0, rg_addr);
for (rg_j <= 0; rg_j < 4; rg_j <= rg_j + 1) seq
stmt_wait_for_sb_nonbusy;
action
$display ("stmt_mem_write_4: [0x%08h] x = 0x%08h", rg_addr + rg_j, rg_data);
dm.write (dm_addr_sbdata0, rg_data);
rg_data <= rg_data + 1;
endaction
endseq
endseq);
// ================================================================
// Run-control test sequences (reset, run, halt, single-step)
let dmcontrol_dm_reset
= fn_mk_dmcontrol (False, // haltreq
False, // resumereq
False, // hartreset
False, // hasel
0, // hartsel,
False, // ndmreset
False); // dmactive; assert reset
let dmcontrol_ndmreset
= fn_mk_dmcontrol (False, // haltreq
False, // resumereq
False, // hartreset
False, // hasel,
0, // hartsel
True, // ndmreset
True); // dmactive
let dmcontrol_err_hasel
= fn_mk_dmcontrol (False, // haltreq
False, // resumereq
False, // hartreset
True, // hasel,
0, // hartsel
False, // ndmreset
True); // dmactive
let dmcontrol_err_hartsel
= fn_mk_dmcontrol (False, // haltreq
False, // resumereq
False, // hartreset
False, // hasel,
3, // hartsel
False, // ndmreset
True); // dmactive
let dmcontrol_hartreset
= fn_mk_dmcontrol (False, // haltreq
False, // resumereq
True, // hartreset
False, // hasel,
0, // hartsel
False, // ndmreset
True); // dmactive
let dmcontrol_err_haltreq_resumereq
= fn_mk_dmcontrol (True, // haltreq
True, // resumereq
False, // hartreset
False, // hasel,
0, // hartsel
False, // ndmreset
True); // dmactive
let dmcontrol_haltreq
= fn_mk_dmcontrol (True, // haltreq
False, // resumereq
False, // hartreset
False, // hasel,
0, // hartsel
False, // ndmreset
True); // dmactive
let dmcontrol_resumereq
= fn_mk_dmcontrol (False, // haltreq
True, // resumereq
False, // hartreset
False, // hasel,
0, // hartsel
False, // ndmreset
True); // dmactive
function Stmt fn_stmt_run_control (DM_Word dm_word);
return seq
dm.write (dm_addr_dmcontrol, dm_word);
delay (5);
// Check and show status
action
let x <- dm.av_read (dm_addr_dmstatus);
$display (" ", fshow_dmstatus (x));
endaction
endseq;
endfunction
// ----------------
// For single-step, set 'step' bit in DCSR, then run
let dcsr_step = {4'h4, // xdebugver
12'b0,
1'b0, // ebreakm
1'b0,
1'b0, // ebreaks
1'b0, // ebreaku
1'b0, // stepie
1'b0, // stepcount
1'b0, // steptime
3'b0, // cause
3'b0,
1'b1, // step
2'h3};
Stmt stmt_single_step = (
seq
// set 'step' in dcsr
fn_stmt_write_reg (fromInteger (dm_command_access_reg_regno_csr_0 + csr_addr_dcsr),
dcsr_step); // priv
fn_stmt_run_control (dmcontrol_resumereq);
endseq);
// ================================================================
// Top-level test. Comment/Uncomment desired parts.
Stmt test = seq
// Reset DM
$display ("----------------\n'Testbench: Reset DM'");
fn_stmt_run_control (dmcontrol_dm_reset);
/*
$display ("----------------\n'Testbench: Reset Platform'");
fn_stmt_run_control (dmcontrol_ndmreset);
$display ("----------------\n'Testbench: Err hasel'");
fn_stmt_run_control (dmcontrol_err_hasel);
$display ("----------------\n'Testbench: Err hartsel'");
fn_stmt_run_control (dmcontrol_err_hartsel);
$display ("----------------\n'Testbench: Reset hart'");
fn_stmt_run_control (dmcontrol_hartreset);
$display ("----------------\n'Testbench: Err haltreq and resumereq'");
fn_stmt_run_control (dmcontrol_err_haltreq_resumereq);
$display ("----------------\n'Testbench: Continue'");
fn_stmt_run_control (dmcontrol_resumereq);
$display ("----------------\n'Testbench: Halt'");
fn_stmt_run_control (dmcontrol_haltreq);
$display ("----------------\n'Testbench: Single step'");
stmt_single_step;
*/
$display ("----------------\n'Testbench: Read GPR'");
fn_stmt_read_reg (fromInteger (dm_command_access_reg_regno_gpr_0 + 5));
$display ("----------------\n'Testbench: Read CSR'");
fn_stmt_read_reg (fromInteger (dm_command_access_reg_regno_csr_0 + 3));
$display ("----------------\n'Testbench: Write GPR'");
fn_stmt_write_reg (fromInteger (dm_command_access_reg_regno_gpr_0 + 5), 'h_AAAA_0005);
$display ("----------------\n'Testbench: Write CSR'");
fn_stmt_write_reg (fromInteger (dm_command_access_reg_regno_csr_0 + 3), 'h_CCCC_0003);
/*
$display ("----------------\n'Testbench: Read 1'");
stmt_mem_read_1;
$display ("----------------\n'Testbench: Write 1'");
stmt_mem_write_1;
$display ("----------------\n'Testbench: Read 4'");
stmt_mem_read_4;
$display ("----------------\n'Testbench: Write 4'");
stmt_mem_write_4;
*/
await (False);
endseq;
mkAutoFSM (test);
endmodule
// ================================================================
// Over-simplified model of a hart (reset, run/halt, read/write GPR/CSR)
interface Hart_DM_IFC;
// Reset
interface Put #(Token) hart_reset_req;
// Run-control
interface Server #(Bool, Bool) hart_run_req_rsp;
// GPR access
interface TRX_Slave_IFC #(5,32,0) slave_for_gprs;
// CSR access
interface TRX_Slave_IFC #(12,32,0) slave_for_csrs;
endinterface
(* synthesize *)
module mkHart_Model #(parameter Bit #(10) hart_id) (Hart_DM_IFC);
Reg #(Bool) rg_hart_running <- mkReg (False);
FIFOF #(Token) f_hart_reset_reqs <- mkFIFOF;
FIFOF #(Bool) f_hart_run_reqs <- mkFIFOF;
FIFOF #(Bool) f_hart_run_rsps <- mkFIFOF;
// TRX interface to gprs
TRX_Buffer_IFC #(5,32,0) trx_buf_gprs <- mkTRX_Buffer;
// TRX interface to crs
TRX_Buffer_IFC #(12,32,0) trx_buf_csrs <- mkTRX_Buffer;
// ----------------------------------------------------------------
// BEHAVIOR
// ----------------
// Reset
rule rl_hart_reset;
let x = f_hart_reset_reqs.first;
f_hart_reset_reqs.deq;
$display ("Testbench.hart [%0d]: reset", hart_id);
endrule
// ----------------
// Run-control
rule rl_resume_hart (f_hart_run_reqs.first);
f_hart_run_reqs.deq;
rg_hart_running <= True;
if (rg_hart_running)
$display ("Testbench.hart [%0d].rl_resume_hart: already running", hart_id);
else
$display ("Testbench.hart [%0d].rl_resume_hart: resuming", hart_id);
f_hart_run_rsps.enq (True);
endrule
rule rl_halt_hart (! f_hart_run_reqs.first);
f_hart_run_reqs.deq;
rg_hart_running <= False;
if (rg_hart_running)
$display ("Testbench.hart [%0d].rl_halt_hart: halting", hart_id);
else
$display ("Testbench.hart [%0d].rl_halt_hart: already halted", hart_id);
f_hart_run_rsps.enq (False);
endrule
// ----------------
// GPR access
rule rl_read_gpr;
let rda <- pop_o (trx_buf_gprs.master.fo_rda);
Bit #(32) data = extend (rda.addr) + 'h1000;
let rdr = TRX_RdR {trans_id: rda.trans_id,
status: TRX_OKAY,
data: data};
trx_buf_gprs.master.fi_rdr.enq (rdr);
$display ("Testbench.hart [%0d]: Read GPR [%0h] => 0x%08h", hart_id, rda.addr, data);
endrule
rule rl_read_csr;
let rda <- pop_o (trx_buf_csrs.master.fo_rda);
Bit #(32) data = extend (rda.addr) + 'h2000;
let rdr = TRX_RdR {trans_id: rda.trans_id,
status: TRX_OKAY,
data: data};
trx_buf_csrs.master.fi_rdr.enq (rdr);
$display ("Testbench.hart [%0d]: Read CSR [%0h] => 0x%08h", hart_id, rda.addr, data);
endrule
rule rl_write_gpr;
let wra <- pop_o (trx_buf_gprs.master.fo_wra);
let wrd <- pop_o (trx_buf_gprs.master.fo_wrd);
let wrr = TRX_WrR {trans_id: wra.trans_id, status: TRX_OKAY};
trx_buf_gprs.master.fi_wrr.enq (wrr);
$display ("Testbench.hart [%0d]: Write GPR [%0h] <= 0x%08h", hart_id, wra.addr, wrd.data);
endrule
rule rl_write_csr;
let wra <- pop_o (trx_buf_csrs.master.fo_wra);
let wrd <- pop_o (trx_buf_csrs.master.fo_wrd);
let wrr = TRX_WrR {trans_id: wra.trans_id, status: TRX_OKAY};
trx_buf_csrs.master.fi_wrr.enq (wrr);
$display ("Testbench.hart [%0d]: Write CSR [%0h] <= 0x%08h", hart_id, wra.addr, wrd.data);
endrule
// ----------------------------------------------------------------
// INTERFACE
// Reset
interface Put hart_reset_req = toPut (f_hart_reset_reqs);
// Run-control
interface Server hart_run_req_rsp = toGPServer (f_hart_run_reqs, f_hart_run_rsps);
// GPR access
interface TRX_Slave_IFC slave_for_gprs = trx_buf_gprs.slave;
// CSR access
interface TRX_Slave_IFC slave_for_csrs = trx_buf_csrs.slave;
endmodule
// ================================================================
endpackage