Files
Toooba/src_Core/Core/CoreW.bsv
Jessica Clarke 68d3bd484e Provide opt-in wedge debugging info
When DEBUG_WEDGE is defined, expose the last committed and next in the
reorder buffer PC and corresponding instruction via DMI registers, since
even when the core is wedged and we can't read GPRs etc we can still
interact with the debug module itself. Hopefully this proves useful for
debugging wedges.
2020-07-07 23:59:35 +01:00

514 lines
18 KiB
Plaintext

// Copyright (c) 2018-2020 Bluespec, Inc. All Rights Reserved.
//
//-
// RVFI_DII + CHERI modifications:
// Copyright (c) 2020 Alexandre Joannou
// Copyright (c) 2020 Peter Rugg
// Copyright (c) 2020 Jonathan Woodruff
// All rights reserved.
//
// This software was developed by SRI International and the University of
// Cambridge Computer Laboratory (Department of Computer Science and
// Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
// DARPA SSITH research programme.
//
// This work was supported by NCSC programme grant 4212611/RFA 15971 ("SafeBet").
//-
package CoreW;
// ================================================================
// This package is called 'CoreW' for 'Core Wrapper'
// and corresponds to 'Core' in Piccolo and Flute.
//
// Here in Toooba, we use the name 'CoreW' to avoid a name-clash with
// an inner module called 'Core' in MIT's RISCY-OOO.
//
// The specific correspondence with Piccolo/Flute structure is:
// Piccolo/Flute Toooba
// mkCore mkCoreW
// mkProc
// mkCPU mkCore
// This package defines:
// Core_IFC
// mkCore #(Core_IFC)
//
// mkCoreW instantiates:
// - mkProc (the RISC-V CPU, a version of MIT's RISCY-OOO)
// - 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 :: *;
import Clocks :: *;
// ----------------
// BSV additional libs
import Cur_Cycle :: *;
import GetPut_Aux :: *;
import Routable :: *;
import AXI4 :: *;
import TagControllerAXI :: *;
// ================================================================
// Project imports
// ----------------
// From RISCY-ooo
import ProcTypes :: *;
// ----------------
// From Toooba
// Main fabric
import Fabric_Defs :: *; // for Wd_Id, Wd_Addr, Wd_Data...
import SoC_Map :: *;
`ifdef INCLUDE_GDB_CONTROL
import Debug_Module :: *;
`endif
import CoreW_IFC :: *;
import PLIC :: *;
import PLIC_16_2_7 :: *;
import Proc_IFC :: *;
import Proc :: *;
`ifdef INCLUDE_TANDEM_VERIF
import TV_Info :: *;
import Trace_Data2 :: *;
import TV_Encode :: *;
import Trace_Data2_to_Trace_Data :: *;
`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
import DM_CPU_Req_Rsp ::*;
// ================================================================
// The Core module
(* synthesize *)
module mkCoreW #(Reset dm_power_on_reset)
(CoreW_IFC #(N_External_Interrupt_Sources));
// ================================================================
// Notes on 'reset'
// This module's default reset (Verilog RST_N) is a
// 'non-debug-module reset', or 'ndm-reset': it resets everything
// in mkCoreW other than the optional RISC-V Debug Module (DM).
// DM is reset ONLY by 'dm_power_on_reset' (parameter of this module).
// This is expected to be performed exactly once, on power-up.
// Note: DM has an internal functionality that the DM spec calls
// 'dm_reset'. This is not really an electrical reset, it is just
// a module initializer wholly within the DM to put it into a
// known state. To be able to do a dm_reset, the DM has to be
// working already, at least to the point that it can field DMI
// requests from the external debugger asking the DM to proform a
// dm_reset.
// DM can ask the environment to perform an 'ndm-reset', which the
// environment does by asserting the default reset (RST_N). At the
// same time, the environment may also reset part or all of the
// rest of the SoC.
// DM can also individually reset each hart in mkCPU.
// 'hart' = hardware thread = independent PC and fetch-and-execute pipeline.
// mkCPU (instantiated in this module) has one or more harts.
// This hart-reset logic is entirely within this module.
// ================================================================
// The CPU's (hart's) reset is the ``or'' of the default reset
// (power-on reset) and the Debug Module's 'hart_reset' control.
let ndm_reset <- exposeCurrentReset;
`ifdef INCLUDE_GDB_CONTROL
let clk <- exposeCurrentClock;
Bool initial_reset_val = False;
Integer hart_reset_duration = 10; // NOTE: assuming 10 cycle reset enough for hart
let dm_hart0_reset_controller <- mkReset(hart_reset_duration, initial_reset_val, clk);
let hart0_reset <- mkResetEither (ndm_reset, dm_hart0_reset_controller.new_rst);
`else
let hart0_reset = ndm_reset;
`endif
// ================================================================
// STATE
// System address map
SoC_Map_IFC soc_map <- mkSoC_Map;
// RISCY-OOO processor
// TODO (when we do multicore): need resets for each core.
Proc_IFC proc <- mkProc (reset_by hart0_reset);
// handle uncached interface
let proc_uncached <- toAXI4_Master_Synth(extendIDFields(zeroMasterUserFields(proc.master1), 0));
// Bridge for uncached expernal bus transactions.
let uncached_mem_shim <- mkAXI4ShimFF(reset_by hart0_reset);
let uncached_mem_master <- toAXI4_Master_Synth(extendIDFields(zeroMasterUserFields(uncached_mem_shim.master), 0), reset_by hart0_reset);
// handle cached interface
// AXI4 tagController
TagControllerAXI#(Wd_MId, Wd_Addr, Wd_Data) tagController <- mkTagControllerAXI(reset_by hart0_reset); // TODO double check if reseting like this is good enough
AXI4_Master#(Wd_MId, Wd_Addr, Wd_Data, Wd_AW_User, Wd_W_User, Wd_B_User, Wd_AR_User, Wd_R_User)
tmp2 <- fromAXI4_Master_Synth(proc.master0, reset_by hart0_reset);
mkConnection(tmp2, tagController.slave, reset_by hart0_reset);
// PLIC (Platform-Level Interrupt Controller)
PLIC_IFC_16_2_7 plic <- mkPLIC_16_2_7;
`ifdef INCLUDE_GDB_CONTROL
// Debug Module
Debug_Module_IFC debug_module <- mkDebug_Module (reset_by dm_power_on_reset);
`endif
`ifdef INCLUDE_TANDEM_VERIF
// The following are a superscalar-wide set of transformers from RISCY-OOO output Trace_Data2
// to Trace_Data which is input to the TV encoder
Vector #(SupSize, Trace_Data2_to_Trace_Data_IFC) v_td2_to_td <- replicateM (mkTrace_Data2_to_Trace_Data);
// The TV encoder transforms Trace_Data structures from the CPU and DM
// into encoded byte vectors for transmission to the Tandem Verifier
TV_Encode_IFC tv_encode <- mkTV_Encode;
`endif
// ================================================================
// Hart-reset from DM
`ifdef INCLUDE_GDB_CONTROL
Reg #(Bit #(8)) rg_hart0_reset_delay <- mkReg (0);
Reg #(Bit #(64)) rg_tohost_addr <- mkReg (0);
Reg #(Bit #(64)) rg_fromhost_addr <- mkReg (0);
rule rl_dm_hart0_reset (rg_hart0_reset_delay == 0);
let x <- debug_module.hart0_reset_client.request.get;
dm_hart0_reset_controller.assertReset;
rg_hart0_reset_delay <= fromInteger (hart_reset_duration + 200); // NOTE: heuristic
$display ("%0d: %m.rl_dm_hart0_reset: asserting hart0 reset for %0d cycles",
cur_cycle, hart_reset_duration);
endrule
rule rl_dm_hart0_reset_wait (rg_hart0_reset_delay != 0);
if (rg_hart0_reset_delay == 1) begin
let pc = soc_map_struct.pc_reset_value;
Bool is_running = True;
proc.start (is_running, pc, rg_tohost_addr, rg_fromhost_addr);
debug_module.hart0_reset_client.response.put (is_running);
$display ("%0d: %m.rl_dm_hart0_reset_wait: proc.start (pc %0h, tohostAddr %0h, fromhostAddr %0h",
cur_cycle, pc, rg_tohost_addr, rg_fromhost_addr);
end
rg_hart0_reset_delay <= rg_hart0_reset_delay - 1;
endrule
`endif
`ifdef INCLUDE_GDB_CONTROL
// ================================================================
// Direct DM-to-CPU connections for run-control and other misc requests
mkConnection (debug_module.hart0_client_run_halt, proc.hart0_run_halt_server);
mkConnection (debug_module.hart0_get_other_req, proc.hart0_put_other_req);
`ifdef DEBUG_WEDGE
mkConnection (proc.hart0_last_inst, debug_module.hart0_last_inst);
mkConnection (proc.hart0_next_inst, debug_module.hart0_next_inst);
`endif
`endif
`ifdef INCLUDE_TANDEM_VERIF
// ================================================================
// Direct CPU-to-TV connections for TV trace data
for (Integer j = 0; j < valueOf (SupSize); j = j + 1) begin
// CPU Trace_Data2 output streams to Trace_Data2_to_Trace_Data converters
mkConnection (proc.v_to_TV [j], v_td2_to_td [j].in);
// Trace_Data2_to_Trace_Data converters to TV encoder
mkConnection (v_td2_to_td [j].out, tv_encode.v_cpu_in [j]);
end
`endif
`ifdef INCLUDE_GDB_CONTROL
`ifdef INCLUDE_TANDEM_VERIF
// ================================================================
// BEGIN SECTION: DM and TV both present
// We instantiate 'taps' into connections where DM writes CPU GPRs,
// FPRs, CSRs, and main memory. The tap outputs go the TV encoder,
// to keep the tandem verifier in sync with DM updates to the CPU.
// 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 rl_merge_dm_mem_trace_data;
let tmp <- dm_mem_tap.trace_data_out.get;
tv_encode.dm_in.put (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 rl_merge_dm_gpr_trace_data;
let tmp <- dm_gpr_tap_ifc.trace_data_out.get;
tv_encode.dm_in.put (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 rl_merge_dm_fpr_trace_data;
let tmp <- dm_fpr_tap_ifc.trace_data_out.get;
tv_encode.dm_in.put (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);
rule rl_merge_dm_csr_trace_data;
let tmp <- dm_csr_tap.trace_data_out.get;
tv_encode.dm_in.put(tmp);
endrule
`ifdef ISA_F_OR_D
(* descending_urgency = "rl_merge_dm_fpr_trace_data, rl_merge_dm_gpr_trace_data" *)
`endif
(* descending_urgency = "rl_merge_dm_gpr_trace_data, rl_merge_dm_csr_trace_data" *)
(* descending_urgency = "rl_merge_dm_csr_trace_data, rl_merge_dm_mem_trace_data" *)
rule rl_bogus_for_sched_attributes;
endrule
// END SECTION: DM and TV
// ================================================================
`else // of ifdef INCLUDE_TANDEM_VERIF
// ================================================================
// BEGIN SECTION: DM, 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: DM, no TV
// ================================================================
`endif // for ifdef INCLUDE_TANDEM_VERIF
// ================================================================
`else // for ifdef INCLUDE_GDB_CONTROL
// ================================================================
// BEGIN SECTION: no DM
// No DM, so 'DM bus master' is AXI4 dummy
let dm_master_local = culDeSac;
`ifdef INCLUDE_TANDEM_VERIF
// TV, no DM: stub out the dm input to TV
Get #(Trace_Data) gs = getstub;
mkConnection (tv_encode.dm_in, gs);
`endif
`endif // for ifdef INCLUDE_GDB_CONTROL
// ================================================================
// Connect the local 2x3 fabric
// Masters on the local 2x3 fabric
Vector#(Num_Masters_2x3,
AXI4_Master_Synth #(Wd_MId_2x3, Wd_Addr, Wd_Data,
Wd_AW_User, Wd_W_User, Wd_B_User,
Wd_AR_User, Wd_R_User))
master_vector = newVector;
//let master_vector = newVector;
master_vector[cpu_uncached_master_num] = proc_uncached;
master_vector[debug_module_sba_master_num] = dm_master_local;
// Slaves on the local 2x3 fabric
// default slave is forwarded out directly to the Core interface
Vector#(Num_Slaves_2x3,
AXI4_Slave_Synth #(Wd_SId_2x3, Wd_Addr, Wd_Data,
Wd_AW_User, Wd_W_User, Wd_B_User,
Wd_AR_User, Wd_R_User))
slave_vector = newVector;
//let slave_vector = newVector;
slave_vector[default_slave_num] <- toAXI4_Slave_Synth(uncached_mem_shim.slave);
slave_vector[llc_slave_num] = proc.debug_module_mem_server;
slave_vector[plic_slave_num] = plic.axi4_slave;
function Vector#(Num_Slaves_2x3, Bool) route_2x3 (Bit#(Wd_Addr) addr);
Vector#(Num_Slaves_2x3, Bool) res = replicate(False);
if (inRange(soc_map.m_mem0_controller_addr_range, addr))
res[llc_slave_num] = True;
else if (inRange(soc_map.m_plic_addr_range, addr))
res[plic_slave_num] = True;
else
res[default_slave_num] = True;
//Bit #(24) topBits = truncateLSB(addr); //XXX TODO Tag controller masks to 40 bits
//if (topBits != 0) res = replicate(False);
return res;
endfunction
mkAXI4Bus_Synth (route_2x3, master_vector, slave_vector);
let cached_mem_master <- toAXI4_Master_Synth(tagController.master);
// ================================================================
// 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
// ================================================================
// INTERFACE
// ----------------------------------------------------------------
// Debugging: set core's verbosity, htif addrs
method Action set_verbosity (Bit #(4) verbosity, Bit #(64) logdelay);
// Warning: ignoring logdelay
proc.set_verbosity (verbosity);
endmethod
// ----------------------------------------------------------------
// Start
method Action start (Bool is_running, Bit #(64) tohost_addr, Bit #(64) fromhost_addr);
plic.set_addr_map (zeroExtend (soc_map.m_plic_addr_range.base),
zeroExtend (rangeTop(soc_map.m_plic_addr_range)));
let pc = soc_map_struct.pc_reset_value;
proc.start (is_running, pc, tohost_addr, fromhost_addr);
`ifdef INCLUDE_GDB_CONTROL
// Save for potential future use by rl_dm_hart0_reset
rg_tohost_addr <= tohost_addr;
rg_fromhost_addr <= fromhost_addr;
`endif
$display ("%0d: %m.method start: proc.start (pc %0h, tohostAddr %0h, fromhostAddr %0h)",
cur_cycle, pc, tohost_addr, fromhost_addr);
endmethod
// ----------------------------------------------------------------
// AXI4 Fabric interfaces
// Cached master to Fabric master interface
interface cpu_imem_master = cached_mem_master;
// Uncached master to Fabric master interface
interface cpu_dmem_master = uncached_mem_master;
// ----------------------------------------------------------------
// External interrupt sources
interface core_external_interrupt_sources = plic.v_sources;
// ----------------------------------------------------------------
// Non-maskable interrupt request
method Action nmi_req (Bool set_not_clear);
// TODO: fixup; passing const False for now
proc.non_maskable_interrupt_req (False);
endmethod
`ifdef RVFI_DII
interface Toooba_RVFI_DII_Server rvfi_dii_server = proc.rvfi_dii_server;
`endif
`ifdef INCLUDE_GDB_CONTROL
// ----------------------------------------------------------------
// Optional DM interfaces
// ----------------
// DMI (Debug Module Interface) facing remote debugger
interface DMI dmi = debug_module.dmi;
// ----------------
// Facing Platform
// Non-Debug-Module Reset (reset all except DM)
interface Client ndm_reset_client = debug_module.ndm_reset_client;
`endif
`ifdef INCLUDE_TANDEM_VERIF
// ----------------------------------------------------------------
// Optional TV interface
interface Get tv_verifier_info_get;
method ActionValue #(Info_CPU_to_Verifier) get();
match { .n, .v } <- tv_encode.out.get;
return (Info_CPU_to_Verifier { num_bytes: n, vec_bytes: v });
endmethod
endinterface
`endif
endmodule: mkCoreW
// ================================================================
// 2x3 Fabric for this Core
// Masters: CPU DMem, Debug Module System Bus Access, External access
// ----------------
// Fabric port numbers for masters
Master_Num_2x3 cpu_uncached_master_num = 0;
Master_Num_2x3 debug_module_sba_master_num = 1;
// ----------------
// Fabric port numbers for slaves
Slave_Num_2x3 default_slave_num = 0; // for I/O, uncached memory, etc.
Slave_Num_2x3 plic_slave_num = 1; // PLIC mem-mapped registers
Slave_Num_2x3 llc_slave_num = 2; // Normal cached memory (connects to coherent Last-Level Cache)
// ================================================================
endpackage