Initial load of files

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

464
src_Core/Core/CoreW.bsv Normal file
View 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

View 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

View 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
View 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
View 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