Initial load of files
This commit is contained in:
464
src_Core/Core/CoreW.bsv
Normal file
464
src_Core/Core/CoreW.bsv
Normal file
@@ -0,0 +1,464 @@
|
||||
// Copyright (c) 2018-2019 Bluespec, Inc. All Rights Reserved.
|
||||
|
||||
package CoreW;
|
||||
|
||||
// ================================================================
|
||||
// This package defines:
|
||||
// Core_IFC
|
||||
// mkCore #(Core_IFC)
|
||||
// mkFabric_2x3 -- specialized AXI4 fabric used inside this core
|
||||
//
|
||||
// mkCoreW instantiates:
|
||||
// - mkProc (the RISC-V CPU, a version of MIT's RISCY-OOO)
|
||||
// - mkFabric_2x3
|
||||
// - mkPLIC_16_2_7
|
||||
// - mkTV_Encode (Tandem-Verification logic, optional: INCLUDE_TANDEM_VERIF)
|
||||
// - mkDebug_Module (RISC-V Debug Module, optional: INCLUDE_GDB_CONTROL)
|
||||
// and connects them all up.
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import Vector :: *;
|
||||
import FIFOF :: *;
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
import Connectable :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import Cur_Cycle :: *;
|
||||
import GetPut_Aux :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
// Main fabric
|
||||
import AXI4_Types :: *;
|
||||
import AXI4_Fabric :: *;
|
||||
import Fabric_Defs :: *; // for Wd_Id, Wd_Addr, Wd_Data, Wd_User
|
||||
import SoC_Map :: *;
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
import Debug_Module :: *;
|
||||
`endif
|
||||
|
||||
import Core_IFC :: *;
|
||||
import PLIC :: *;
|
||||
import PLIC_16_2_7 :: *;
|
||||
import Proc_IFC :: *;
|
||||
import Proc :: *;
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
import TV_Info :: *;
|
||||
import TV_Encode :: *;
|
||||
`endif
|
||||
|
||||
// TV_Taps needed when both GDB_CONTROL and TANDEM_VERIF are present
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
import TV_Taps :: *;
|
||||
`endif
|
||||
`endif
|
||||
|
||||
// ================================================================
|
||||
// The Core module
|
||||
|
||||
(* synthesize *)
|
||||
module mkCoreW (Core_IFC #(N_External_Interrupt_Sources));
|
||||
|
||||
// ================================================================
|
||||
// STATE
|
||||
|
||||
// System address map
|
||||
SoC_Map_IFC soc_map <- mkSoC_Map;
|
||||
|
||||
// McStriiv processor
|
||||
Proc_IFC proc <- mkProc;
|
||||
|
||||
// A 2x3 fabric for connecting {CPU, Debug_Module} to {Fabric, PLIC}
|
||||
Fabric_2x3_IFC fabric_2x3 <- mkFabric_2x3;
|
||||
|
||||
// PLIC (Platform-Level Interrupt Controller)
|
||||
PLIC_IFC_16_2_7 plic <- mkPLIC_16_2_7;
|
||||
|
||||
// Reset requests from SoC and responses to SoC
|
||||
FIFOF #(Bit #(0)) f_reset_reqs <- mkFIFOF;
|
||||
FIFOF #(Bit #(0)) f_reset_rsps <- mkFIFOF;
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
// The TV encoder transforms Trace_Data structures produced by the CPU and DM
|
||||
// into encoded byte vectors for transmission to the Tandem Verifier
|
||||
TV_Encode_IFC tv_encode <- mkTV_Encode;
|
||||
`endif
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// Debug Module
|
||||
Debug_Module_IFC debug_module <- mkDebug_Module;
|
||||
`endif
|
||||
|
||||
// ================================================================
|
||||
// RESET
|
||||
// There are two sources of reset requests to the CPU: externally
|
||||
// from the SoC and, optionally, the DM. The SoC requires a
|
||||
// response, the DM does not. When both requestors are present
|
||||
// (i.e., DM is present), we merge the reset requests into the CPU,
|
||||
// and we remember which one was the requestor in
|
||||
// f_reset_requestor, so that we know whether or not to respond to
|
||||
// the SoC.
|
||||
|
||||
Bit #(1) reset_requestor_dm = 0;
|
||||
Bit #(1) reset_requestor_soc = 1;
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
FIFOF #(Bit #(1)) f_reset_requestor <- mkFIFOF;
|
||||
`endif
|
||||
|
||||
// Reset-hart0 request from SoC
|
||||
rule rl_cpu_hart0_reset_from_soc_start;
|
||||
let req <- pop (f_reset_reqs);
|
||||
|
||||
proc.hart0_server_reset.request.put (?); // CPU
|
||||
plic.server_reset.request.put (?); // PLIC
|
||||
fabric_2x3.reset; // Local 2x3 Fabric
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// Remember the requestor, so we can respond to it
|
||||
f_reset_requestor.enq (reset_requestor_soc);
|
||||
`endif
|
||||
$display ("%0d: Core.rl_cpu_hart0_reset_from_soc_start", cur_cycle);
|
||||
endrule
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// Reset-hart0 from Debug Module
|
||||
rule rl_cpu_hart0_reset_from_dm_start;
|
||||
let req <- debug_module.hart0_get_reset_req.get;
|
||||
|
||||
proc.hart0_server_reset.request.put (?); // CPU
|
||||
plic.server_reset.request.put (?); // PLIC
|
||||
fabric_2x3.reset; // Local 2x3 fabric
|
||||
|
||||
// Remember the requestor, so we can respond to it
|
||||
f_reset_requestor.enq (reset_requestor_dm);
|
||||
$display ("%0d: Core.rl_cpu_hart0_reset_from_dm_start", cur_cycle);
|
||||
endrule
|
||||
`endif
|
||||
|
||||
rule rl_cpu_hart0_reset_complete;
|
||||
let rsp1 <- proc.hart0_server_reset.response.get; // CPU
|
||||
let rsp3 <- plic.server_reset.response.get; // PLIC
|
||||
|
||||
plic.set_addr_map (zeroExtend (soc_map.m_plic_addr_base),
|
||||
zeroExtend (soc_map.m_plic_addr_lim));
|
||||
|
||||
Bit #(1) requestor = reset_requestor_soc;
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
requestor <- pop (f_reset_requestor);
|
||||
`endif
|
||||
if (requestor == reset_requestor_soc)
|
||||
f_reset_rsps.enq (?);
|
||||
|
||||
// Start running the cores
|
||||
Bit #(64) startpc = 'h_0000_1000; // TODO: fixup
|
||||
Bit #(64) tohostAddr = 'h_8000_1000; // TODO: fixup
|
||||
Bit #(64) fromhostAddr = 0;
|
||||
proc.start (startpc, tohostAddr, fromhostAddr);
|
||||
|
||||
$display ("%0d: Core.rl_cpu_hart0_reset_complete; started running proc", cur_cycle);
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// Direct DM-to-CPU connections
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// DM to CPU connections for run-control and other misc requests
|
||||
mkConnection (debug_module.hart0_client_run_halt, proc.hart0_server_run_halt);
|
||||
mkConnection (debug_module.hart0_get_other_req, proc.hart0_put_other_req);
|
||||
`endif
|
||||
|
||||
// ================================================================
|
||||
// Other CPU/DM/TV connections
|
||||
// (depends on whether DM, TV or both are present)
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
// BEGIN SECTION: GDB and TV
|
||||
// ----------------------------------------------------------------
|
||||
// DM and TV both present. We instantiate 'taps' into connections
|
||||
// where the DM writes CPU GPRs, CPU FPRs, CPU CSRs, and main memory,
|
||||
// in order to produce corresponding writes for the Tandem Verifier.
|
||||
// Then, we merge the Trace_Data from these three taps with the
|
||||
// Trace_Data produced by the PROC.
|
||||
|
||||
FIFOF #(Trace_Data) f_trace_data_merged <- mkFIFOF;
|
||||
|
||||
// Connect merged trace data to trace encoder
|
||||
mkConnection (toGet (f_trace_data_merged), tv_encode.trace_data_in);
|
||||
|
||||
// Merge-in CPU's trace data.
|
||||
// This is equivalent to: mkConnection (proc.trace_data_out, toPut (f_trace_data_merged))
|
||||
// but using a rule allows us to name it in scheduling attributes.
|
||||
rule merge_cpu_trace_data;
|
||||
let tmp <- proc.trace_data_out.get;
|
||||
f_trace_data_merged.enq (tmp);
|
||||
endrule
|
||||
|
||||
// Create a tap for DM's memory-writes to the bus, and merge-in the trace data.
|
||||
DM_Mem_Tap_IFC dm_mem_tap <- mkDM_Mem_Tap;
|
||||
mkConnection (debug_module.master, dm_mem_tap.slave);
|
||||
let dm_master_local = dm_mem_tap.master;
|
||||
|
||||
rule merge_dm_mem_trace_data;
|
||||
let tmp <- dm_mem_tap.trace_data_out.get;
|
||||
f_trace_data_merged.enq (tmp);
|
||||
endrule
|
||||
|
||||
// Create a tap for DM's GPR writes to the CPU, and merge-in the trace data.
|
||||
DM_GPR_Tap_IFC dm_gpr_tap_ifc <- mkDM_GPR_Tap;
|
||||
mkConnection (debug_module.hart0_gpr_mem_client, dm_gpr_tap_ifc.server);
|
||||
mkConnection (dm_gpr_tap_ifc.client, proc.hart0_gpr_mem_server);
|
||||
|
||||
rule merge_dm_gpr_trace_data;
|
||||
let tmp <- dm_gpr_tap_ifc.trace_data_out.get;
|
||||
f_trace_data_merged.enq (tmp);
|
||||
endrule
|
||||
|
||||
`ifdef ISA_F_OR_D
|
||||
// Create a tap for DM's FPR writes to the CPU, and merge-in the trace data.
|
||||
DM_FPR_Tap_IFC dm_fpr_tap_ifc <- mkDM_FPR_Tap;
|
||||
mkConnection (debug_module.hart0_fpr_mem_client, dm_fpr_tap_ifc.server);
|
||||
mkConnection (dm_fpr_tap_ifc.client, proc.hart0_fpr_mem_server);
|
||||
|
||||
rule merge_dm_fpr_trace_data;
|
||||
let tmp <- dm_fpr_tap_ifc.trace_data_out.get;
|
||||
f_trace_data_merged.enq (tmp);
|
||||
endrule
|
||||
`endif
|
||||
// for ifdef ISA_F_OR_D
|
||||
|
||||
// Create a tap for DM's CSR writes, and merge-in the trace data.
|
||||
DM_CSR_Tap_IFC dm_csr_tap <- mkDM_CSR_Tap;
|
||||
mkConnection(debug_module.hart0_csr_mem_client, dm_csr_tap.server);
|
||||
mkConnection(dm_csr_tap.client, proc.hart0_csr_mem_server);
|
||||
|
||||
`ifdef ISA_F_OR_D
|
||||
(* descending_urgency = "merge_dm_fpr_trace_data, merge_dm_gpr_trace_data" *)
|
||||
`endif
|
||||
(* descending_urgency = "merge_dm_gpr_trace_data, merge_dm_csr_trace_data" *)
|
||||
(* descending_urgency = "merge_dm_csr_trace_data, merge_dm_mem_trace_data" *)
|
||||
(* descending_urgency = "merge_dm_mem_trace_data, merge_cpu_trace_data" *)
|
||||
rule merge_dm_csr_trace_data;
|
||||
let tmp <- dm_csr_tap.trace_data_out.get;
|
||||
f_trace_data_merged.enq(tmp);
|
||||
endrule
|
||||
|
||||
// END SECTION: GDB and TV
|
||||
`else
|
||||
// for ifdef INCLUDE_TANDEM_VERIF
|
||||
// ----------------------------------------------------------------
|
||||
// BEGIN SECTION: GDB and no TV
|
||||
|
||||
// Connect DM's GPR interface directly to CPU
|
||||
mkConnection (debug_module.hart0_gpr_mem_client, proc.hart0_gpr_mem_server);
|
||||
|
||||
`ifdef ISA_F_OR_D
|
||||
// Connect DM's FPR interface directly to CPU
|
||||
mkConnection (debug_module.hart0_fpr_mem_client, proc.hart0_fpr_mem_server);
|
||||
`endif
|
||||
|
||||
// Connect DM's CSR interface directly to CPU
|
||||
mkConnection (debug_module.hart0_csr_mem_client, proc.hart0_csr_mem_server);
|
||||
|
||||
// DM's bus master is directly the bus master
|
||||
let dm_master_local = debug_module.master;
|
||||
|
||||
// END SECTION: GDB and no TV
|
||||
`endif
|
||||
// for ifdef INCLUDE_TANDEM_VERIF
|
||||
|
||||
`else
|
||||
// for ifdef INCLUDE_GDB_CONTROL
|
||||
// BEGIN SECTION: no GDB
|
||||
|
||||
// No DM, so 'DM bus master' is dummy
|
||||
AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User)
|
||||
dm_master_local = dummy_AXI4_Master_ifc;
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
// ----------------------------------------------------------------
|
||||
// BEGIN SECTION: no GDB, TV
|
||||
|
||||
// Connect CPU's TV out directly to TV encoder
|
||||
mkConnection (proc.trace_data_out, tv_encode.trace_data_in);
|
||||
// END SECTION: no GDB, TV
|
||||
`endif
|
||||
`endif
|
||||
// for ifdef INCLUDE_GDB_CONTROL
|
||||
|
||||
// ================================================================
|
||||
// Connect the local 2x3 fabric
|
||||
|
||||
// Masters on the local 2x3 fabric
|
||||
mkConnection (proc.master1, fabric_2x3.v_from_masters [cpu_dmem_master_num]);
|
||||
mkConnection (dm_master_local, fabric_2x3.v_from_masters [debug_module_sba_master_num]);
|
||||
|
||||
// Slaves on the local 2x3 fabric
|
||||
// default slave is taken out directly to the Core interface
|
||||
mkConnection (fabric_2x3.v_to_slaves [plic_slave_num], plic.axi4_slave);
|
||||
|
||||
// TODO: This slave can be connected to mkLLCDmaConnect for Debug Module System Bus Access
|
||||
AXI4_Slave_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) dummy_slave = dummy_AXI4_Slave_ifc;
|
||||
mkConnection (fabric_2x3.v_to_slaves [near_mem_io_slave_num], dummy_slave);
|
||||
|
||||
// ================================================================
|
||||
// Connect external interrupt lines from PLIC to CPU
|
||||
|
||||
rule rl_relay_external_interrupts; // from PLIC
|
||||
Bool meip = plic.v_targets [0].m_eip;
|
||||
proc.m_external_interrupt_req (meip);
|
||||
|
||||
Bool seip = plic.v_targets [1].m_eip;
|
||||
proc.s_external_interrupt_req (seip);
|
||||
|
||||
// $display ("%0d: Core.rl_relay_external_interrupts: relaying: %d", cur_cycle, pack (x));
|
||||
endrule
|
||||
|
||||
// TODO: fixup. Need to combine NMIs from multiple sources (cache, fabric, devices, ...)
|
||||
rule rl_relay_non_maskable_interrupt;
|
||||
proc.non_maskable_interrupt_req (False);
|
||||
|
||||
// $display ("%0d: Core.rl_relay_non_maskable_interrupts: relaying: %d", cur_cycle, pack (x));
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// INTERFACE
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Debugging: set core's verbosity
|
||||
|
||||
method Action set_verbosity (Bit #(4) verbosity, Bit #(64) logdelay);
|
||||
// Warning: ignoring logdelay
|
||||
proc.set_verbosity (verbosity);
|
||||
endmethod
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Soft reset
|
||||
|
||||
interface Server cpu_reset_server = toGPServer (f_reset_reqs, f_reset_rsps);
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// AXI4 Fabric interfaces
|
||||
|
||||
// IMem to Fabric master interface
|
||||
interface AXI4_Master_IFC cpu_imem_master = proc.master0;
|
||||
|
||||
// DMem to Fabric master interface
|
||||
interface AXI4_Master_IFC cpu_dmem_master = fabric_2x3.v_to_slaves [default_slave_num];
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// External interrupt sources
|
||||
|
||||
interface core_external_interrupt_sources = plic.v_sources;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Optional TV interface
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
interface Get tv_verifier_info_get;
|
||||
method ActionValue #(Info_CPU_to_Verifier) get();
|
||||
match { .n, .v } <- tv_encode.tv_vb_out.get;
|
||||
return (Info_CPU_to_Verifier { num_bytes: n, vec_bytes: v });
|
||||
endmethod
|
||||
endinterface
|
||||
`endif
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Optional DM interfaces
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// ----------------
|
||||
// DMI (Debug Module Interface) facing remote debugger
|
||||
|
||||
interface DMI dm_dmi = debug_module.dmi;
|
||||
|
||||
// ----------------
|
||||
// Facing Platform
|
||||
|
||||
// Non-Debug-Module Reset (reset all except DM)
|
||||
interface Get dm_ndm_reset_req_get = debug_module.get_ndm_reset_req;
|
||||
`endif
|
||||
|
||||
endmodule: mkCoreW
|
||||
|
||||
// ================================================================
|
||||
// 2x3 Fabric for this Core
|
||||
// Masters: CPU DMem, Debug Module System Bus Access, External access
|
||||
|
||||
// ----------------
|
||||
// Fabric port numbers for masters
|
||||
|
||||
typedef 2 Num_Masters_2x3;
|
||||
|
||||
typedef Bit #(TLog #(Num_Masters_2x3)) Master_Num_2x3;
|
||||
|
||||
Master_Num_2x3 cpu_dmem_master_num = 0;
|
||||
Master_Num_2x3 debug_module_sba_master_num = 1;
|
||||
|
||||
// ----------------
|
||||
// Fabric port numbers for slaves
|
||||
|
||||
typedef 3 Num_Slaves_2x3;
|
||||
|
||||
typedef Bit #(TLog #(Num_Slaves_2x3)) Slave_Num_2x3;
|
||||
|
||||
Slave_Num_2x3 default_slave_num = 0;
|
||||
Slave_Num_2x3 plic_slave_num = 1;
|
||||
|
||||
// TODO: repurpose this for Debug Module System Bus Access to connect to mkLLCDramConnect
|
||||
Slave_Num_2x3 near_mem_io_slave_num = 2;
|
||||
|
||||
|
||||
// ----------------
|
||||
// Specialization of parameterized AXI4 fabric for 2x3 Core fabric
|
||||
|
||||
typedef AXI4_Fabric_IFC #(Num_Masters_2x3,
|
||||
Num_Slaves_2x3,
|
||||
Wd_Id,
|
||||
Wd_Addr,
|
||||
Wd_Data,
|
||||
Wd_User) Fabric_2x3_IFC;
|
||||
|
||||
// ----------------
|
||||
|
||||
(* synthesize *)
|
||||
module mkFabric_2x3 (Fabric_2x3_IFC);
|
||||
|
||||
// System address map
|
||||
SoC_Map_IFC soc_map <- mkSoC_Map;
|
||||
|
||||
// ----------------
|
||||
// Slave address decoder
|
||||
// Any addr is legal, and there is only one slave to service it.
|
||||
|
||||
function Tuple2 #(Bool, Slave_Num_2x3) fn_addr_to_slave_num_2x3 (Fabric_Addr addr);
|
||||
if ( (soc_map.m_near_mem_io_addr_base <= addr)
|
||||
&& (addr < soc_map.m_near_mem_io_addr_lim))
|
||||
return tuple2 (True, near_mem_io_slave_num);
|
||||
|
||||
else if ( (soc_map.m_plic_addr_base <= addr)
|
||||
&& (addr < soc_map.m_plic_addr_lim))
|
||||
return tuple2 (True, plic_slave_num);
|
||||
|
||||
else
|
||||
return tuple2 (True, default_slave_num);
|
||||
endfunction
|
||||
|
||||
AXI4_Fabric_IFC #(Num_Masters_2x3, Num_Slaves_2x3, Wd_Id, Wd_Addr, Wd_Data, Wd_User)
|
||||
fabric <- mkAXI4_Fabric (fn_addr_to_slave_num_2x3);
|
||||
|
||||
return fabric;
|
||||
endmodule: mkFabric_2x3
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
97
src_Core/Core/Core_IFC.bsv
Normal file
97
src_Core/Core/Core_IFC.bsv
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright (c) 2018-2019 Bluespec, Inc. All Rights Reserved.
|
||||
|
||||
package Core_IFC;
|
||||
|
||||
// ================================================================
|
||||
// This package defines the interface of a Core module which
|
||||
// contains:
|
||||
// - mkCPU (the RISC-V CPU)
|
||||
// - mkFabric_2x3
|
||||
// - mkNear_Mem_IO_AXI4
|
||||
// - mkPLIC_16_2_7
|
||||
// - mkTV_Encode (Tandem-Verification logic, optional: INCLUDE_TANDEM_VERIF)
|
||||
// - mkDebug_Module (RISC-V Debug Module, optional: INCLUDE_GDB_CONTROL)
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import Vector :: *;
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
// Main fabric
|
||||
import AXI4_Types :: *;
|
||||
import Fabric_Defs :: *;
|
||||
|
||||
// External interrupt request interface
|
||||
import PLIC :: *;
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
import TV_Info :: *;
|
||||
`endif
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
import Debug_Module :: *;
|
||||
`endif
|
||||
|
||||
// ================================================================
|
||||
// The Core interface
|
||||
|
||||
interface Core_IFC #(numeric type t_n_interrupt_sources);
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Debugging: set core's verbosity
|
||||
|
||||
method Action set_verbosity (Bit #(4) verbosity, Bit #(64) logdelay);
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Soft reset
|
||||
|
||||
interface Server #(Bit #(0), Bit #(0)) cpu_reset_server;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// AXI4 Fabric interfaces
|
||||
|
||||
// CPU IMem to Fabric master interface
|
||||
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) cpu_imem_master;
|
||||
|
||||
// CPU DMem to Fabric master interface
|
||||
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) cpu_dmem_master;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// External interrupt sources
|
||||
|
||||
interface Vector #(t_n_interrupt_sources, PLIC_Source_IFC) core_external_interrupt_sources;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Optional Tandem Verifier interface output tuples (n,vb),
|
||||
// where 'vb' is a vector of bytes
|
||||
// with relevant bytes in locations [0]..[n-1]
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
interface Get #(Info_CPU_to_Verifier) tv_verifier_info_get;
|
||||
`endif
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Optional Debug Module interfaces
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// ----------------
|
||||
// DMI (Debug Module Interface) facing remote debugger
|
||||
|
||||
interface DMI dm_dmi;
|
||||
|
||||
// ----------------
|
||||
// Facing Platform
|
||||
// Non-Debug-Module Reset (reset all except DM)
|
||||
|
||||
interface Get #(Bit #(0)) dm_ndm_reset_req_get;
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
88
src_Core/Core/Fabric_Defs.bsv
Normal file
88
src_Core/Core/Fabric_Defs.bsv
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright (c) 2018-2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
package Fabric_Defs;
|
||||
|
||||
// ================================================================
|
||||
// Defines key parameters of the AXI4/AXI4-Lite system interconnect
|
||||
// fabric to which the core connects, such as address bus width, data
|
||||
// bus width, etc.
|
||||
|
||||
// ***** WARNING! WARNING! WARNING! *****
|
||||
|
||||
// During system integration, these parameters should be checked to be
|
||||
// identical to the system interconnect settings. Strong
|
||||
// type-checking (EXACT match on bus widths) will do this; but some
|
||||
// languages/tools may silently ignore mismatched widths.
|
||||
|
||||
// ================================================================
|
||||
// BSV lib imports
|
||||
|
||||
// None
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import AXI4_Types :: *;
|
||||
|
||||
// ================================================================
|
||||
// Fabric parameters
|
||||
|
||||
// ----------------
|
||||
// Width of fabric 'id' buses
|
||||
typedef 4 Wd_Id;
|
||||
typedef Bit #(Wd_Id) Fabric_Id;
|
||||
|
||||
// ----------------
|
||||
// Width of fabric 'addr' buses
|
||||
`ifdef FABRIC64
|
||||
typedef 64 Wd_Addr;
|
||||
`else
|
||||
typedef 32 Wd_Addr;
|
||||
`endif
|
||||
|
||||
typedef Bit #(Wd_Addr) Fabric_Addr;
|
||||
typedef TDiv #(Wd_Addr, 8) Bytes_per_Fabric_Addr;
|
||||
|
||||
Integer bytes_per_fabric_addr = valueOf (Bytes_per_Fabric_Addr);
|
||||
|
||||
// ----------------
|
||||
// Width of fabric 'data' buses
|
||||
`ifdef FABRIC64
|
||||
typedef 64 Wd_Data;
|
||||
`else
|
||||
typedef 32 Wd_Data;
|
||||
`endif
|
||||
|
||||
typedef Bit #(Wd_Data) Fabric_Data;
|
||||
typedef Bit #(TDiv #(Wd_Data, 8)) Fabric_Strb;
|
||||
|
||||
typedef TDiv #(Wd_Data, 8) Bytes_per_Fabric_Data;
|
||||
Integer bytes_per_fabric_data = valueOf (Bytes_per_Fabric_Data);
|
||||
|
||||
// ----------------
|
||||
// Width of fabric 'user' datapaths
|
||||
typedef 0 Wd_User;
|
||||
typedef Bit #(Wd_User) Fabric_User;
|
||||
|
||||
// ----------------
|
||||
// Number of zero LSBs in a fabric address aligned to the fabric data width
|
||||
|
||||
typedef TLog #(Bytes_per_Fabric_Data) ZLSBs_Aligned_Fabric_Addr;
|
||||
Integer zlsbs_aligned_fabric_addr = valueOf (ZLSBs_Aligned_Fabric_Addr);
|
||||
|
||||
// ================================================================
|
||||
// AXI4 defaults for this project
|
||||
|
||||
Fabric_Id fabric_default_id = 0;
|
||||
AXI4_Burst fabric_default_burst = axburst_incr;
|
||||
AXI4_Lock fabric_default_lock = axlock_normal;
|
||||
AXI4_Cache fabric_default_arcache = arcache_dev_nonbuf;
|
||||
AXI4_Cache fabric_default_awcache = awcache_dev_nonbuf;
|
||||
AXI4_Prot fabric_default_prot = { axprot_2_data, axprot_1_secure, axprot_0_unpriv };
|
||||
AXI4_QoS fabric_default_qos = 0;
|
||||
AXI4_Region fabric_default_region = 0;
|
||||
Fabric_User fabric_default_user = ?;
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
736
src_Core/Core/TV_Encode.bsv
Normal file
736
src_Core/Core/TV_Encode.bsv
Normal file
@@ -0,0 +1,736 @@
|
||||
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved.
|
||||
|
||||
package TV_Encode;
|
||||
|
||||
// ================================================================
|
||||
// module mkTV_Encode is a transforming FIFO
|
||||
// converting Trace_Data into encoded byte vectors
|
||||
|
||||
// ================================================================
|
||||
// BSV lib imports
|
||||
|
||||
import Vector :: *;
|
||||
import FIFOF :: *;
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
import Connectable :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import GetPut_Aux :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import ISA_Decls :: *;
|
||||
import TV_Info :: *;
|
||||
|
||||
// ================================================================
|
||||
|
||||
interface TV_Encode_IFC;
|
||||
method Action reset;
|
||||
|
||||
// This module receives Trace_Data structs from the CPU and Debug Module
|
||||
interface Put #(Trace_Data) trace_data_in;
|
||||
|
||||
// This module produces tuples (n,vb),
|
||||
// where 'vb' is a vector of bytes
|
||||
// with relevant bytes in locations [0]..[n-1]
|
||||
interface Get #(Tuple2 #(Bit #(32), TV_Vec_Bytes)) tv_vb_out;
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
|
||||
(* synthesize *)
|
||||
module mkTV_Encode (TV_Encode_IFC);
|
||||
|
||||
Reg #(Bool) rg_reset_done <- mkReg (True);
|
||||
|
||||
// Keep track of last PC for more efficient encoding of incremented PCs
|
||||
// TODO: currently always sending full PC
|
||||
Reg #(WordXL) rg_last_pc <- mkReg (0);
|
||||
|
||||
FIFOF #(Trace_Data) f_trace_data <- mkFIFOF;
|
||||
FIFOF #(Tuple2 #(Bit #(32), TV_Vec_Bytes)) f_vb <- mkFIFOF;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// BEHAVIOR
|
||||
|
||||
rule rl_log_trace_RESET (rg_reset_done && (f_trace_data.first.op == TRACE_RESET));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_byte (te_op_hart_reset);
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nnN, .xN } = vsubst (nn1, x1, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_GPR_WRITE (rg_reset_done && (f_trace_data.first.op == TRACE_GPR_WRITE));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_byte (te_op_state_init);
|
||||
match { .n2, .vb2 } = encode_reg (fv_gpr_regnum (td.rd), td.word1);
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nnN, .xN } = vsubst (nn2, x2, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_FPR_WRITE (rg_reset_done && (f_trace_data.first.op == TRACE_FPR_WRITE));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_byte (te_op_state_init);
|
||||
match { .n2, .vb2 } = encode_reg (fv_fpr_regnum (td.rd), td.word1);
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nnN, .xN } = vsubst (nn2, x2, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_CSR_WRITE (rg_reset_done && (f_trace_data.first.op == TRACE_CSR_WRITE));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_byte (te_op_state_init);
|
||||
match { .n2, .vb2 } = encode_reg (fv_csr_regnum (truncate (td.word3)), td.word4);
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nnN, .xN } = vsubst (nn2, x2, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_MEM_WRITE (rg_reset_done && (f_trace_data.first.op == TRACE_MEM_WRITE));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
Bit #(2) mem_req_size = td.word1 [1:0];
|
||||
Byte size_and_mem_req_op = { 2'b0, mem_req_size, te_mem_req_op_Store };
|
||||
Byte result_and_size = { te_mem_result_success, 2'b0, mem_req_size };
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_byte (te_op_state_init);
|
||||
match { .n2, .vb2 } = encode_byte (te_op_mem_req);
|
||||
match { .n3, .vb3 } = encode_mlen (td.word3);
|
||||
match { .n4, .vb4 } = encode_byte (size_and_mem_req_op);
|
||||
match { .n5, .vb5 } = encode_mdata (mem_req_size, td.word2);
|
||||
//match { .n6, .vb6 } = encode_byte (te_op_mem_rsp);
|
||||
//match { .n7, .vb7 } = encode_byte (result_and_size);
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
|
||||
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
|
||||
match { .nn5, .x5 } = vsubst (nn4, x4, n5, vb5);
|
||||
//match { .nn6, .x6 } = vsubst (nn5, x5, n6, vb6);
|
||||
//match { .nn7, .x7 } = vsubst (nn6, x6, n7, vb7);
|
||||
//match { .nnN, .xN } = vsubst (nn7, x7, nN, vbN);
|
||||
match { .nnN, .xN } = vsubst (nn5, x5, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_OTHER (rg_reset_done && (f_trace_data.first.op == TRACE_OTHER));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_pc (td.pc);
|
||||
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nnN, .xN } = vsubst (nn2, x2, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_I_RD (rg_reset_done && (f_trace_data.first.op == TRACE_I_RD));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_pc (td.pc);
|
||||
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
|
||||
match { .n3, .vb3 } = encode_reg (fv_gpr_regnum (td.rd), td.word1);
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
|
||||
match { .nnN, .xN } = vsubst (nn3, x3, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_F_RD (rg_reset_done && (f_trace_data.first.op == TRACE_F_RD));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_pc (td.pc);
|
||||
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
|
||||
match { .n3, .vb3 } = encode_reg (fv_fpr_regnum (td.rd), td.word1);
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nnN, .xN } = vsubst (nn2, x2, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_I_LOAD (rg_reset_done && (f_trace_data.first.op == TRACE_I_LOAD));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_pc (td.pc);
|
||||
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
|
||||
match { .n3, .vb3 } = encode_reg (fv_gpr_regnum (td.rd), td.word1);
|
||||
match { .n4, .vb4 } = encode_eaddr (truncate (td.word3));
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
|
||||
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
|
||||
match { .nnN, .xN } = vsubst (nn4, x4, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_F_LOAD (rg_reset_done && (f_trace_data.first.op == TRACE_F_LOAD));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_pc (td.pc);
|
||||
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
|
||||
match { .n3, .vb3 } = encode_reg (fv_fpr_regnum (td.rd), td.word1);
|
||||
match { .n4, .vb4 } = encode_eaddr (truncate (td.word3));
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
|
||||
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
|
||||
match { .nnN, .xN } = vsubst (nn4, x4, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_STORE (rg_reset_done && (f_trace_data.first.op == TRACE_STORE));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
let funct3 = instr_funct3 (td.instr); // TODO: what if it's a 16b instr?
|
||||
let mem_req_size = funct3 [1:0];
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_pc (td.pc);
|
||||
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
|
||||
match { .n3, .vb3 } = encode_stval (mem_req_size, td.word2);
|
||||
match { .n4, .vb4 } = encode_eaddr (truncate (td.word3));
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
|
||||
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
|
||||
match { .nnN, .xN } = vsubst (nn4, x4, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_AMO (rg_reset_done && (f_trace_data.first.op == TRACE_AMO));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
let funct3 = instr_funct3 (td.instr); // TODO: what if it's a 16b instr?
|
||||
let mem_req_size = funct3 [1:0];
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_pc (td.pc);
|
||||
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
|
||||
match { .n3, .vb3 } = encode_reg (fv_gpr_regnum (td.rd), td.word1);
|
||||
match { .n4, .vb4 } = encode_stval (mem_req_size, td.word2);
|
||||
match { .n5, .vb5 } = encode_eaddr (truncate (td.word3));
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
|
||||
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
|
||||
match { .nn5, .x5 } = vsubst (nn4, x4, n5, vb5);
|
||||
match { .nnN, .xN } = vsubst (nn5, x5, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_CSRRX (rg_reset_done && (f_trace_data.first.op == TRACE_CSRRX));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_pc (td.pc);
|
||||
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
|
||||
match { .n3, .vb3 } = encode_reg (fv_gpr_regnum (td.rd), td.word1);
|
||||
match { .n4, .vb4 } = ((td.word2 == 0)
|
||||
? tuple2 (0, ?) // CSR was not written
|
||||
: encode_reg (fv_csr_regnum (truncate (td.word3)), td.word4));
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
|
||||
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
|
||||
match { .nnN, .xN } = vsubst (nn4, x4, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_TRAP (rg_reset_done && (f_trace_data.first.op == TRACE_TRAP));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Use new priv mode to decide which trap regs are updated (M, S or U priv)
|
||||
Priv_Mode priv = truncate (td.rd);
|
||||
CSR_Addr csr_addr_status = csr_addr_mstatus;
|
||||
CSR_Addr csr_addr_cause = csr_addr_mcause;
|
||||
CSR_Addr csr_addr_epc = csr_addr_mepc;
|
||||
CSR_Addr csr_addr_tval = csr_addr_mtval;
|
||||
if (priv == s_Priv_Mode) begin
|
||||
csr_addr_status = csr_addr_sstatus;
|
||||
csr_addr_cause = csr_addr_scause;
|
||||
csr_addr_epc = csr_addr_sepc;
|
||||
csr_addr_tval = csr_addr_stval;
|
||||
end
|
||||
else if (priv == u_Priv_Mode) begin
|
||||
csr_addr_status = csr_addr_ustatus;
|
||||
csr_addr_cause = csr_addr_ucause;
|
||||
csr_addr_epc = csr_addr_uepc;
|
||||
csr_addr_tval = csr_addr_utval;
|
||||
end
|
||||
|
||||
// Omit the instruction if cause is instruction fault since the instruction is then bogus
|
||||
Bool is_instr_fault = ( (truncate (td.word2) == exc_code_INSTR_ACCESS_FAULT)
|
||||
|| (truncate (td.word2) == exc_code_INSTR_PAGE_FAULT));
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_pc (td.pc);
|
||||
match { .n2, .vb2 } = (is_instr_fault
|
||||
? tuple2 (0, ?)
|
||||
: encode_instr (td.instr_sz, td.instr));
|
||||
match { .n3, .vb3 } = encode_priv (td.rd);
|
||||
match { .n4, .vb4 } = encode_reg (fv_csr_regnum (csr_addr_status), td.word1);
|
||||
match { .n5, .vb5 } = encode_reg (fv_csr_regnum (csr_addr_cause), td.word2);
|
||||
match { .n6, .vb6 } = encode_reg (fv_csr_regnum (csr_addr_epc), truncate (td.word3));
|
||||
match { .n7, .vb7 } = encode_reg (fv_csr_regnum (csr_addr_tval), td.word4);
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
|
||||
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
|
||||
match { .nn5, .x5 } = vsubst (nn4, x4, n5, vb5);
|
||||
match { .nn6, .x6 } = vsubst (nn5, x5, n6, vb6);
|
||||
match { .nn7, .x7 } = vsubst (nn6, x6, n7, vb7);
|
||||
match { .nnN, .xN } = vsubst (nn7, x7, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_INTR (rg_reset_done && (f_trace_data.first.op == TRACE_INTR));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Use new priv mode to decide which trap regs are updated (M, S or U priv)
|
||||
Priv_Mode priv = truncate (td.rd);
|
||||
CSR_Addr csr_addr_status = csr_addr_mstatus;
|
||||
CSR_Addr csr_addr_cause = csr_addr_mcause;
|
||||
CSR_Addr csr_addr_epc = csr_addr_mepc;
|
||||
CSR_Addr csr_addr_tval = csr_addr_mtval;
|
||||
if (priv == s_Priv_Mode) begin
|
||||
csr_addr_status = csr_addr_sstatus;
|
||||
csr_addr_cause = csr_addr_scause;
|
||||
csr_addr_epc = csr_addr_sepc;
|
||||
csr_addr_tval = csr_addr_stval;
|
||||
end
|
||||
else if (priv == u_Priv_Mode) begin
|
||||
csr_addr_status = csr_addr_ustatus;
|
||||
csr_addr_cause = csr_addr_ucause;
|
||||
csr_addr_epc = csr_addr_uepc;
|
||||
csr_addr_tval = csr_addr_utval;
|
||||
end
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_pc (td.pc);
|
||||
match { .n2, .vb2 } = encode_priv (td.rd);
|
||||
match { .n3, .vb3 } = encode_reg (fv_csr_regnum (csr_addr_status), td.word1);
|
||||
match { .n4, .vb4 } = encode_reg (fv_csr_regnum (csr_addr_cause), td.word2);
|
||||
match { .n5, .vb5 } = encode_reg (fv_csr_regnum (csr_addr_epc), truncate (td.word3));
|
||||
match { .n6, .vb6 } = encode_reg (fv_csr_regnum (csr_addr_tval), td.word4);
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
|
||||
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
|
||||
match { .nn5, .x5 } = vsubst (nn4, x4, n5, vb5);
|
||||
match { .nn6, .x6 } = vsubst (nn5, x5, n6, vb6);
|
||||
match { .nnN, .xN } = vsubst (nn6, x6, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_RET (rg_reset_done && (f_trace_data.first.op == TRACE_RET));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_pc (td.pc);
|
||||
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
|
||||
match { .n3, .vb3 } = encode_priv (td.rd);
|
||||
match { .n4, .vb4 } = encode_reg (fv_csr_regnum (csr_addr_mstatus), td.word1);
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
|
||||
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
|
||||
match { .nnN, .xN } = vsubst (nn4, x4, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// INTERFACE
|
||||
|
||||
method Action reset ();
|
||||
endmethod
|
||||
|
||||
interface Put trace_data_in = toPut (f_trace_data);
|
||||
interface Get tv_vb_out = toGet (f_vb);
|
||||
endmodule
|
||||
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
// Encoding Trace_Data into Byte vectors
|
||||
|
||||
// ================================================================
|
||||
// Encodings
|
||||
// cf. "Trace Protocol Specification Version 2018-09-12, Darius Rad, Bluespec, Inc."
|
||||
|
||||
Bit #(8) te_op_begin_group = 1;
|
||||
Bit #(8) te_op_end_group = 2;
|
||||
Bit #(8) te_op_incr_pc = 3;
|
||||
Bit #(8) te_op_full_reg = 4;
|
||||
Bit #(8) te_op_incr_reg = 5;
|
||||
Bit #(8) te_op_incr_reg_OR = 6;
|
||||
Bit #(8) te_op_addl_state = 7;
|
||||
Bit #(8) te_op_mem_req = 8;
|
||||
Bit #(8) te_op_mem_rsp = 9;
|
||||
Bit #(8) te_op_hart_reset = 10;
|
||||
Bit #(8) te_op_state_init = 11;
|
||||
Bit #(8) te_op_16b_instr = 16;
|
||||
Bit #(8) te_op_32b_instr = 17;
|
||||
|
||||
Bit #(4) te_mem_req_size_8 = 0;
|
||||
Bit #(4) te_mem_req_size_16 = 1;
|
||||
Bit #(4) te_mem_req_size_32 = 2;
|
||||
Bit #(4) te_mem_req_size_64 = 3;
|
||||
|
||||
Bit #(4) te_mem_req_op_Load = 0;
|
||||
Bit #(4) te_mem_req_op_Store = 1;
|
||||
Bit #(4) te_mem_req_op_LR = 2;
|
||||
Bit #(4) te_mem_req_op_SC = 3;
|
||||
Bit #(4) te_mem_req_op_AMO_swap = 4;
|
||||
Bit #(4) te_mem_req_op_AMO_add = 5;
|
||||
Bit #(4) te_mem_req_op_AMO_xor = 6;
|
||||
Bit #(4) te_mem_req_op_AMO_and = 7;
|
||||
Bit #(4) te_mem_req_op_AMO_or = 8;
|
||||
Bit #(4) te_mem_req_op_AMO_min = 9;
|
||||
Bit #(4) te_mem_req_op_AMO_max = 10;
|
||||
Bit #(4) te_mem_req_op_AMO_minu = 11;
|
||||
Bit #(4) te_mem_req_op_AMO_maxu = 12;
|
||||
Bit #(4) te_mem_req_op_ifetch = 13;
|
||||
|
||||
Bit #(4) te_mem_result_success = 0;
|
||||
Bit #(4) te_mem_result_failure = 1;
|
||||
|
||||
Bit #(8) te_op_addl_state_priv = 1;
|
||||
Bit #(8) te_op_addl_state_paddr = 2;
|
||||
Bit #(8) te_op_addl_state_eaddr = 3;
|
||||
Bit #(8) te_op_addl_state_data8 = 4;
|
||||
Bit #(8) te_op_addl_state_data16 = 5;
|
||||
Bit #(8) te_op_addl_state_data32 = 6;
|
||||
Bit #(8) te_op_addl_state_data64 = 7;
|
||||
Bit #(8) te_op_addl_state_mtime = 8;
|
||||
Bit #(8) te_op_addl_state_pc_paddr = 9;
|
||||
Bit #(8) te_op_addl_state_pc = 10;
|
||||
|
||||
// ================================================================
|
||||
// Architectural register address encodings
|
||||
// cf. "RISC-V External Debug Support"
|
||||
// 2018-10-02_riscv_debug_spec_v0.13_DRAFT_f2873e71
|
||||
// "Table 3.3 Abstract Register Numbers"
|
||||
|
||||
function Bit #(16) fv_csr_regnum (CSR_Addr csr_addr);
|
||||
return zeroExtend (csr_addr);
|
||||
endfunction
|
||||
|
||||
function Bit #(16) fv_gpr_regnum (RegName gpr_addr);
|
||||
return 'h1000 + zeroExtend (gpr_addr);
|
||||
endfunction
|
||||
|
||||
function Bit #(16) fv_fpr_regnum (RegName fpr_addr);
|
||||
return 'h1020 + zeroExtend (fpr_addr);
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// vsubst substitutes vb1[j1:j1+j2-1] with vb2[0:j2-1]
|
||||
|
||||
function Tuple2 #(Bit #(32),
|
||||
Vector #(TV_VB_SIZE, Byte))
|
||||
vsubst (Bit #(32) j1, Vector #(TV_VB_SIZE, Byte) vb1,
|
||||
Bit #(32) j2, Vector #(m, Byte) vb2);
|
||||
|
||||
function Byte f (Integer j);
|
||||
Byte x = vb1 [j];
|
||||
Bit #(32) jj = fromInteger (j);
|
||||
if ((j1 <= jj) && (jj < j1 + j2))
|
||||
x = vb2 [jj - j1];
|
||||
return x;
|
||||
endfunction
|
||||
|
||||
let v = genWith (f);
|
||||
let n = j1 + j2;
|
||||
|
||||
return tuple2 (n, v);
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Encoding of Trace_Data into byte vectors
|
||||
// Every function below returns:
|
||||
// (n, vb) :: Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte))
|
||||
// where vb is a vector of bytes with relevant bytes in vb[0]..vb[n-1]
|
||||
|
||||
// ================================================================
|
||||
|
||||
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_byte (Byte x);
|
||||
return tuple2 (1, replicate (x));
|
||||
endfunction
|
||||
|
||||
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_mlen (Bit #(64) word);
|
||||
Vector #(TV_VB_SIZE, Byte) vb = newVector;
|
||||
Bit #(32) n;
|
||||
vb [0] = word[7:0];
|
||||
vb [1] = word [15:8];
|
||||
vb [2] = word [23:16];
|
||||
vb [3] = word [31:24];
|
||||
vb [4] = word [39:32];
|
||||
vb [5] = word [47:40];
|
||||
vb [6] = word [55:48];
|
||||
vb [7] = word [63:56];
|
||||
`ifdef RV32
|
||||
n = 4; // MLEN = 32
|
||||
`ifdef SV34
|
||||
n = 5; // MLEN = 34
|
||||
`endif
|
||||
`else
|
||||
n = 8; // MLEN = 64
|
||||
`endif
|
||||
return tuple2 (n, vb);
|
||||
endfunction
|
||||
|
||||
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_mdata (MemReqSize mem_req_size, WordXL word);
|
||||
Vector #(TV_VB_SIZE, Byte) vb = newVector;
|
||||
Bit #(32) n;
|
||||
vb [0] = word[7:0];
|
||||
vb [1] = word [15:8];
|
||||
vb [2] = word [23:16];
|
||||
vb [3] = word [31:24];
|
||||
`ifdef RV64
|
||||
vb [4] = word [39:32];
|
||||
vb [5] = word [47:40];
|
||||
vb [6] = word [55:48];
|
||||
vb [7] = word [63:56];
|
||||
`endif
|
||||
n = case (mem_req_size)
|
||||
f3_SIZE_B: 1;
|
||||
f3_SIZE_H: 2;
|
||||
f3_SIZE_W: 4;
|
||||
f3_SIZE_D: 8;
|
||||
endcase;
|
||||
return tuple2 (n, vb);
|
||||
endfunction
|
||||
|
||||
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_instr (ISize isize, Bit #(32) instr);
|
||||
|
||||
Vector #(TV_VB_SIZE, Byte) vb = newVector;
|
||||
Bit #(32) n = ((isize == ISIZE16BIT) ? 3 : 5);
|
||||
vb [0] = ((isize == ISIZE16BIT) ? te_op_16b_instr : te_op_32b_instr);
|
||||
vb [1] = instr [7:0];
|
||||
vb [2] = instr [15:8];
|
||||
vb [3] = instr [23:16];
|
||||
vb [4] = instr [31:24];
|
||||
return tuple2 (n, vb);
|
||||
endfunction
|
||||
|
||||
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_reg (Bit #(16) regnum, WordXL word);
|
||||
Vector #(TV_VB_SIZE, Byte) vb = newVector;
|
||||
Bit #(32) n = 0;
|
||||
vb [0] = te_op_full_reg;
|
||||
vb [1] = regnum [7:0];
|
||||
vb [2] = regnum [15:8];
|
||||
vb [3] = word[7:0];
|
||||
vb [4] = word [15:8];
|
||||
vb [5] = word [23:16];
|
||||
vb [6] = word [31:24];
|
||||
n = 7;
|
||||
`ifdef RV64
|
||||
vb [7] = word [39:32];
|
||||
vb [8] = word [47:40];
|
||||
vb [9] = word [55:48];
|
||||
vb [10] = word [63:56];
|
||||
n = 11;
|
||||
`endif
|
||||
if (regnum == fv_gpr_regnum (0)) n = 0;
|
||||
return tuple2 (n, vb);
|
||||
endfunction
|
||||
|
||||
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_priv (Bit #(5) priv);
|
||||
Vector #(TV_VB_SIZE, Byte) vb = newVector;
|
||||
vb [0] = te_op_addl_state;
|
||||
vb [1] = te_op_addl_state_priv;
|
||||
vb [2] = zeroExtend (priv);
|
||||
return tuple2 (3, vb);
|
||||
endfunction
|
||||
|
||||
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_pc (WordXL word);
|
||||
Vector #(TV_VB_SIZE, Byte) vb = newVector;
|
||||
Bit #(32) n;
|
||||
vb [0] = te_op_addl_state;
|
||||
vb [1] = te_op_addl_state_pc;
|
||||
vb [2] = word [7:0];
|
||||
vb [3] = word [15:8];
|
||||
vb [4] = word [23:16];
|
||||
vb [5] = word [31:24];
|
||||
n = 6;
|
||||
`ifdef RV64
|
||||
vb [6] = word [39:32];
|
||||
vb [7] = word [47:40];
|
||||
vb [8] = word [55:48];
|
||||
vb [9] = word [63:56];
|
||||
n = 10;
|
||||
`endif
|
||||
return tuple2 (n, vb);
|
||||
endfunction
|
||||
|
||||
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_eaddr (WordXL word);
|
||||
Vector #(TV_VB_SIZE, Byte) vb = newVector;
|
||||
Bit #(32) n;
|
||||
vb [0] = te_op_addl_state;
|
||||
vb [1] = te_op_addl_state_eaddr;
|
||||
vb [2] = word [7:0];
|
||||
vb [3] = word [15:8];
|
||||
vb [4] = word [23:16];
|
||||
vb [5] = word [31:24];
|
||||
n = 6;
|
||||
`ifdef RV64
|
||||
vb [6] = word [39:32];
|
||||
vb [7] = word [47:40];
|
||||
vb [8] = word [55:48];
|
||||
vb [9] = word [63:56];
|
||||
n = 10;
|
||||
`endif
|
||||
return tuple2 (n, vb);
|
||||
endfunction
|
||||
|
||||
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_stval (MemReqSize mem_req_size, WordXL word);
|
||||
Vector #(TV_VB_SIZE, Byte) vb = newVector;
|
||||
Bit #(32) n;
|
||||
vb [0] = te_op_addl_state;
|
||||
vb [1] = case (mem_req_size)
|
||||
f3_SIZE_B: te_op_addl_state_data8;
|
||||
f3_SIZE_H: te_op_addl_state_data16;
|
||||
f3_SIZE_W: te_op_addl_state_data32;
|
||||
f3_SIZE_D: te_op_addl_state_data64;
|
||||
endcase;
|
||||
vb [2] = word [7:0];
|
||||
vb [3] = word [15:8];
|
||||
vb [4] = word [23:16];
|
||||
vb [5] = word [31:24];
|
||||
`ifdef RV64
|
||||
vb [6] = word [39:32];
|
||||
vb [7] = word [47:40];
|
||||
vb [8] = word [55:48];
|
||||
vb [9] = word [63:56];
|
||||
`endif
|
||||
n = case (mem_req_size)
|
||||
f3_SIZE_B: 2 + 1;
|
||||
f3_SIZE_H: 2 + 2;
|
||||
f3_SIZE_W: 2 + 4;
|
||||
f3_SIZE_D: 2 + 8;
|
||||
endcase;
|
||||
return tuple2 (n, vb);
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
239
src_Core/Core/TV_Taps.bsv
Normal file
239
src_Core/Core/TV_Taps.bsv
Normal file
@@ -0,0 +1,239 @@
|
||||
// Copyright (c) 2018-2019 Bluespec, Inc. All Rights Reserved.
|
||||
|
||||
package TV_Taps;
|
||||
|
||||
// ================================================================
|
||||
// This package defines 'taps' on connections between
|
||||
// - DM and CPU, on which DM accesses CPU GPRs, FPRs and CSRs
|
||||
// - DM and memory bus, on which DM accesses memory
|
||||
// Each tap snoops 'writes', and produces a corresponsing Trace_Data
|
||||
// write-memory command for the Tandem Verifier, so that it keeps its
|
||||
// GPRs, FPRs, CSRs and memories in sync.
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import Assert :: *;
|
||||
import BUtils :: *;
|
||||
import FIFOF :: *;
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
import Connectable :: *;
|
||||
import Memory :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import Semi_FIFOF :: *;
|
||||
import GetPut_Aux :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import ISA_Decls :: *;
|
||||
import TV_Info :: *;
|
||||
|
||||
import AXI4_Types :: *;
|
||||
import Fabric_Defs :: *;
|
||||
|
||||
// ================================================================
|
||||
// DM-to-memory tap
|
||||
|
||||
interface DM_Mem_Tap_IFC;
|
||||
interface AXI4_Slave_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) slave;
|
||||
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master;
|
||||
interface Get #(Trace_Data) trace_data_out;
|
||||
endinterface
|
||||
|
||||
(* synthesize *)
|
||||
module mkDM_Mem_Tap (DM_Mem_Tap_IFC);
|
||||
|
||||
// Transactor facing DM
|
||||
AXI4_Slave_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) slave_xactor <- mkAXI4_Slave_Xactor;
|
||||
|
||||
// Transactor facing memory bus
|
||||
AXI4_Master_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master_xactor <- mkAXI4_Master_Xactor;
|
||||
|
||||
// Tap output
|
||||
FIFOF #(Trace_Data) f_trace_data <- mkFIFOF;
|
||||
|
||||
// ----------------
|
||||
// AXI requests
|
||||
|
||||
// Snoop write requests
|
||||
rule write_reqs;
|
||||
let wr_addr = slave_xactor.o_wr_addr.first;
|
||||
slave_xactor.o_wr_addr.deq;
|
||||
|
||||
let wr_data = slave_xactor.o_wr_data.first;
|
||||
slave_xactor.o_wr_data.deq;
|
||||
|
||||
// Pass-through
|
||||
master_xactor.i_wr_addr.enq (wr_addr);
|
||||
master_xactor.i_wr_data.enq (wr_data);
|
||||
|
||||
// Tap
|
||||
Bit #(64) paddr = ?;
|
||||
Bit #(64) stval = ?;
|
||||
`ifdef FABRIC64
|
||||
if (wr_data.wstrb == 'h0f) begin
|
||||
paddr = zeroExtend (wr_addr.awaddr);
|
||||
stval = (wr_data.wdata & 'h_FFFF_FFFF);
|
||||
end
|
||||
else if (wr_data.wstrb == 'hf0) begin
|
||||
paddr = zeroExtend (wr_addr.awaddr);
|
||||
stval = ((wr_data.wdata >> 32) & 'h_FFFF_FFFF);
|
||||
end
|
||||
else
|
||||
dynamicAssert(False, "mkDM_Mem_Tap: unsupported byte enables");
|
||||
`else
|
||||
paddr = zeroExtend (wr_addr.awaddr);
|
||||
stval = zeroExtend (wr_data.wdata);
|
||||
`endif
|
||||
Trace_Data td = mkTrace_MEM_WRITE (f3_SIZE_W, truncate (stval), paddr);
|
||||
f_trace_data.enq (td);
|
||||
endrule
|
||||
|
||||
// Read requests, write responses and read responses are not snooped
|
||||
mkConnection (slave_xactor.o_rd_addr, master_xactor.i_rd_addr);
|
||||
mkConnection (slave_xactor.i_wr_resp, master_xactor.o_wr_resp);
|
||||
mkConnection (slave_xactor.i_rd_data, master_xactor.o_rd_data);
|
||||
|
||||
// ================================================================
|
||||
// INTERFACE
|
||||
|
||||
// Facing DM
|
||||
interface slave = slave_xactor.axi_side;
|
||||
// Facing bus
|
||||
interface master = master_xactor.axi_side;
|
||||
// Tap towards verifier
|
||||
interface Get trace_data_out = toGet (f_trace_data);
|
||||
|
||||
endmodule: mkDM_Mem_Tap
|
||||
|
||||
// ================================================================
|
||||
// DM-to-CPU GPR tap (for writes to GPRs)
|
||||
|
||||
interface DM_GPR_Tap_IFC;
|
||||
interface MemoryClient #(5, XLEN) client;
|
||||
interface MemoryServer #(5, XLEN) server;
|
||||
interface Get #(Trace_Data) trace_data_out;
|
||||
endinterface
|
||||
|
||||
(* synthesize *)
|
||||
module mkDM_GPR_Tap (DM_GPR_Tap_IFC);
|
||||
// req from DM
|
||||
FIFOF #(MemoryRequest #(5, XLEN)) f_req_in <- mkFIFOF;
|
||||
// req to CPU
|
||||
FIFOF #(MemoryRequest #(5, XLEN)) f_req_out <- mkFIFOF;
|
||||
// resp CPU->DM
|
||||
FIFOF #(MemoryResponse #(XLEN)) f_rsp <- mkFIFOF;
|
||||
// Tap to TV
|
||||
FIFOF #(Trace_Data) f_trace_data <- mkFIFOF;
|
||||
|
||||
rule request;
|
||||
let req <- pop (f_req_in);
|
||||
|
||||
// Pass-through to CPU
|
||||
f_req_out.enq(req);
|
||||
|
||||
// Snoop writes and send trace data to TV
|
||||
if (req.write) begin
|
||||
Trace_Data td;
|
||||
td = mkTrace_GPR_WRITE (req.address, req.data);
|
||||
f_trace_data.enq (td);
|
||||
end
|
||||
endrule
|
||||
|
||||
interface MemoryClient client = toGPClient (f_req_out, f_rsp);
|
||||
interface MemoryServer server = toGPServer (f_req_in, f_rsp);
|
||||
|
||||
interface Get trace_data_out = toGet (f_trace_data);
|
||||
endmodule: mkDM_GPR_Tap
|
||||
|
||||
// ================================================================
|
||||
// DM-to-CPU FPR tap (for writes to FPRs)
|
||||
|
||||
`ifdef ISA_F_OR_D
|
||||
|
||||
interface DM_FPR_Tap_IFC;
|
||||
interface MemoryClient #(5, FLEN) client;
|
||||
interface MemoryServer #(5, FLEN) server;
|
||||
interface Get #(Trace_Data) trace_data_out;
|
||||
endinterface
|
||||
|
||||
(* synthesize *)
|
||||
module mkDM_FPR_Tap (DM_FPR_Tap_IFC);
|
||||
// req from DM
|
||||
FIFOF #(MemoryRequest #(5, FLEN)) f_req_in <- mkFIFOF;
|
||||
// req to CPU
|
||||
FIFOF #(MemoryRequest #(5, FLEN)) f_req_out <- mkFIFOF;
|
||||
// resp CPU->DM
|
||||
FIFOF #(MemoryResponse #(FLEN)) f_rsp <- mkFIFOF;
|
||||
// Tap to TV
|
||||
FIFOF #(Trace_Data) f_trace_data <- mkFIFOF;
|
||||
|
||||
rule request;
|
||||
let req <- pop (f_req_in);
|
||||
|
||||
// Pass-through to CPU
|
||||
f_req_out.enq(req);
|
||||
|
||||
// Snoop writes and send trace data to TV
|
||||
if (req.write) begin
|
||||
Trace_Data td;
|
||||
td = mkTrace_FPR_WRITE (req.address, req.data);
|
||||
f_trace_data.enq (td);
|
||||
end
|
||||
endrule
|
||||
|
||||
interface MemoryClient client = toGPClient (f_req_out, f_rsp);
|
||||
interface MemoryServer server = toGPServer (f_req_in, f_rsp);
|
||||
|
||||
interface Get trace_data_out = toGet (f_trace_data);
|
||||
endmodule: mkDM_FPR_Tap
|
||||
|
||||
`endif
|
||||
|
||||
// ================================================================
|
||||
// DM-to-CPU CSR tap (for writes to CSRs)
|
||||
|
||||
interface DM_CSR_Tap_IFC;
|
||||
interface MemoryClient #(12, XLEN) client;
|
||||
interface MemoryServer #(12, XLEN) server;
|
||||
interface Get #(Trace_Data) trace_data_out;
|
||||
endinterface
|
||||
|
||||
(* synthesize *)
|
||||
module mkDM_CSR_Tap (DM_CSR_Tap_IFC);
|
||||
// req from DM
|
||||
FIFOF #(MemoryRequest #(12, XLEN)) f_req_in <- mkFIFOF;
|
||||
// req to CPU
|
||||
FIFOF #(MemoryRequest #(12, XLEN)) f_req_out <- mkFIFOF;
|
||||
// resp CPU->DM
|
||||
FIFOF #(MemoryResponse #(XLEN)) f_rsp <- mkFIFOF;
|
||||
// Tap to TV
|
||||
FIFOF #(Trace_Data) f_trace_data <- mkFIFOF;
|
||||
|
||||
rule request;
|
||||
let req <- pop (f_req_in);
|
||||
|
||||
// Pass-through to CPU
|
||||
f_req_out.enq(req);
|
||||
|
||||
// Snoop writes and send trace data to TV
|
||||
if (req.write) begin
|
||||
Trace_Data td = mkTrace_CSR_WRITE (req.address, req.data);
|
||||
f_trace_data.enq (td);
|
||||
end
|
||||
endrule
|
||||
|
||||
interface MemoryClient client = toGPClient (f_req_out, f_rsp);
|
||||
interface MemoryServer server = toGPServer (f_req_in, f_rsp);
|
||||
|
||||
interface Get trace_data_out = toGet (f_trace_data);
|
||||
endmodule: mkDM_CSR_Tap
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
Reference in New Issue
Block a user