Initial load of files
This commit is contained in:
487
src_Core/Debug_Module/DM_Abstract_Commands.bsv
Normal file
487
src_Core/Debug_Module/DM_Abstract_Commands.bsv
Normal 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
|
||||
512
src_Core/Debug_Module/DM_Common.bsv
Normal file
512
src_Core/Debug_Module/DM_Common.bsv
Normal 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
|
||||
343
src_Core/Debug_Module/DM_Run_Control.bsv
Normal file
343
src_Core/Debug_Module/DM_Run_Control.bsv
Normal 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
|
||||
663
src_Core/Debug_Module/DM_System_Bus.bsv
Normal file
663
src_Core/Debug_Module/DM_System_Bus.bsv
Normal 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
|
||||
287
src_Core/Debug_Module/Debug_Module.bsv
Normal file
287
src_Core/Debug_Module/Debug_Module.bsv
Normal 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
|
||||
162
src_Core/Debug_Module/README.txt
Normal file
162
src_Core/Debug_Module/README.txt
Normal 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;
|
||||
94
src_Core/Debug_Module/Test/Makefile
Normal file
94
src_Core/Debug_Module/Test/Makefile
Normal 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
|
||||
597
src_Core/Debug_Module/Test/Testbench.bsv
Normal file
597
src_Core/Debug_Module/Test/Testbench.bsv
Normal 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
|
||||
Reference in New Issue
Block a user