// Copyright (c) 2018-2020 Bluespec, Inc. All Rights Reserved. // //- // RVFI_DII + CHERI modifications: // Copyright (c) 2020-2022 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"). // // This material is based upon work supported by the DoD Information Analysis // Center Program Management Office (DoD IAC PMO), sponsored by the Defense // Technical Information Center (DTIC) under Contract No. FA807518D0004. Any // opinions, findings and conclusions or recommendations expressed in this // material are those of the author(s) and do not necessarily reflect the views // of the Air Force Installation Contracting Agency (AFICA). //- 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_CoreNumX2_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 FIFO :: *; import FIFOF :: *; import GetPut :: *; import ClientServer :: *; import Connectable :: *; import Clocks :: *; // ---------------- // BSV additional libs import Cur_Cycle :: *; import GetPut_Aux :: *; import Routable :: *; import AXI4 :: *; import AXI4Lite :: *; import SourceSink :: *; import TagControllerAXI :: *; import CacheCore :: *; // ================================================================ // Project imports // ---------------- // From RISCY-ooo import ProcTypes :: *; `ifdef PERFORMANCE_MONITORING import StatCounters::*; `endif // ---------------- // 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 WindCoreInterface :: *; import Proc_IFC :: *; import Proc :: *; import PLIC :: *; import PLIC_16_CoreNumX2_7 :: *; `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 typedef WindCoreMid #( // AXI lite subordinate control port parameters 21, 32, 0, 0, 0, 0, 0 // AXI manager 0 port parameters , TAdd #(Wd_MId, 1), Wd_Addr, Wd_Data, 0, 0, 0, 0, 0 // AXI manager 1 port parameters , TAdd #(Wd_MId, 1), Wd_Addr, Wd_Data, 0, 0, 0, 0, 0 // AXI subordinate 0 port parameters , Wd_CoreW_Bus_MId, Wd_Addr, Wd_Data , Wd_AW_User, Wd_W_User, Wd_B_User , Wd_AR_User, Wd_R_User // Number of interrupt lines , t_n_irq) CoreW_IFC #(numeric type t_n_irq); //(* synthesize *) module mkCoreW (CoreW_IFC #(t_n_irq)); Clock clk <- exposeCurrentClock; Reset rst <- exposeCurrentReset; let newRst <- mkReset (0, True, clk, reset_by rst); match {.otherRst, .ifc} <- mkCoreW_reset ( rst , reset_by newRst.new_rst); rule rl_forward_debug_reset (otherRst); newRst.assertReset; endrule return ifc; endmodule // The interface to this module is a convenience to avoid exposing the reset // hacks to the nicer outer interface, and not have to use a large amount of // reset_by to decouple the debug module from the rest... module mkCoreW_reset #(Reset porReset) (Tuple2#(PulseWire, CoreW_IFC #(t_n_irq))); // ================================================================ // 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 'porReset' (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 Vector #(CoreNum, MakeResetIfc) dm_harts_reset_controller <- replicateM(mkReset(hart_reset_duration, initial_reset_val, clk)); function Reset proj_new_rst (MakeResetIfc x) = x.new_rst; let all_harts_reset <- foldlM (mkResetEither, ndm_reset, map (proj_new_rst, dm_harts_reset_controller)); `else let all_harts_reset = ndm_reset; `endif // ================================================================ // STATE // System address map SoC_Map_IFC soc_map <- mkSoC_Map; // RISCY-OOO processor // TODO: could have separate resets for each core. Proc_IFC proc <- mkProc (reset_by all_harts_reset); // handle uncached interface let proc_uncached = prepend_AXI4_Master_id (0, zero_AXI4_Master_user (proc.master1)); // Bridge for uncached expernal bus transactions. let uncached_mem_shim <- mkAXI4ShimFF(reset_by all_harts_reset); // handle cached interface // AXI4 tagController TagControllerAXI #(Wd_MId, Wd_Addr, Wd_Data) tagController <- mkTagControllerAXI (reset_by all_harts_reset); // TODO double check if reseting like this is good enough mkConnection (proc.master0, tagController.slave, reset_by all_harts_reset); `ifdef PERFORMANCE_MONITORING rule report_tagController_events; EventsCacheCore cache_core_evts = tagController.events; EventsTGC evts = unpack(0); evts.evt_WRITE = zeroExtend(pack(cache_core_evts.evt_WRITE)); evts.evt_WRITE_MISS = zeroExtend(pack(cache_core_evts.evt_WRITE_MISS)); evts.evt_READ = zeroExtend(pack(cache_core_evts.evt_READ)); evts.evt_READ_MISS = zeroExtend(pack(cache_core_evts.evt_READ_MISS)); evts.evt_EVICT = zeroExtend(pack(cache_core_evts.evt_EVICT)); `ifdef USECAP evts.evt_SET_TAG_WRITE = zeroExtend(pack(cache_core_evts.evt_SET_TAG_WRITE)); evts.evt_SET_TAG_READ = zeroExtend(pack(cache_core_evts.evt_SET_TAG_READ)); `endif proc.events_tgc(evts); endrule `endif // PLIC (Platform-Level Interrupt Controller) PLIC_IFC_16_CoreNumX2_7 plic <- mkPLIC_16_CoreNumX2_7 (reset_by all_harts_reset); `ifdef INCLUDE_GDB_CONTROL // Debug Module Debug_Module_IFC debug_module <- mkDebug_Module (reset_by porReset); `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 function do_release (restartRunning) = action plic.set_addr_map (zeroExtend (soc_map.m_plic_addr_range.base), zeroExtend (rangeTop(soc_map.m_plic_addr_range))); proc.start (restartRunning, soc_map_struct.pc_reset_value, 0, 0); endaction; // ================================================================ // Hart-reset from DM Bool start_running = False; `ifdef INCLUDE_GDB_CONTROL Reg #(Bit #(8)) rg_harts_reset_delay <- mkReg (0); Reg #(Bit #(64)) rg_tohost_addr <- mkReg (0); Reg #(Bit #(64)) rg_fromhost_addr <- mkReg (0); for (Integer core = 0; core < valueOf(CoreNum); core = core + 1) rule rl_dm_harts_reset (rg_harts_reset_delay == 0); let x <- debug_module.harts_reset_client[core].request.get; dm_harts_reset_controller[core].assertReset; rg_harts_reset_delay <= fromInteger (hart_reset_duration + 200); // NOTE: heuristic $display ("%0d: %m.rl_dm_harts_reset: asserting harts reset for %0d cycles", cur_cycle, hart_reset_duration); endrule rule rl_dm_harts_reset_wait (rg_harts_reset_delay != 0); if (rg_harts_reset_delay == 1) begin let pc = soc_map_struct.pc_reset_value; proc.start (start_running, pc, rg_tohost_addr, rg_fromhost_addr); // We reset all the harts, so we indicate this to the DM, even though it's possible only one hart was requested to reset for (Integer core = 0; core < valueOf(CoreNum); core = core + 1) debug_module.harts_reset_client[core].response.put (start_running); $display ("%0d: %m.rl_dm_harts_reset_wait: proc.start (pc %0h, tohostAddr %0h, fromhostAddr %0h", cur_cycle, pc, rg_tohost_addr, rg_fromhost_addr); end rg_harts_reset_delay <= rg_harts_reset_delay - 1; endrule `endif // ================================================================ // Start the proc a suitable time after a PoR UInt#(8) initial_wait = 100; // heuristic -- better to wait till "all out of reset" received from corew Reg #(UInt#(8)) rg_corew_start_after_por <- mkReg(initial_wait, reset_by porReset); rule rl_step_0 (rg_corew_start_after_por != 0); let n = rg_corew_start_after_por - 1; rg_corew_start_after_por <= n; if (n==0) do_release(start_running); endrule `ifdef INCLUDE_GDB_CONTROL // ================================================================ // Direct DM-to-CPU connections for run-control and other misc requests mkConnection (debug_module.harts_client_run_halt, proc.harts_run_halt_server, reset_by porReset); mkConnection (debug_module.harts_get_other_req, proc.harts_put_other_req, reset_by porReset); `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.harts_gpr_mem_client, proc.harts_gpr_mem_server, reset_by porReset); `ifdef ISA_F_OR_D // Connect DM's FPR interface directly to CPU mkConnection (debug_module.harts_fpr_mem_client, proc.harts_fpr_mem_server, reset_by porReset); `endif // Connect DM's CSR interface directly to CPU mkConnection (debug_module.harts_csr_mem_client, proc.harts_csr_mem_server, reset_by porReset); // 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 // ================================================================ // new internal AXI4 manager from interace subordinate port let subShim <- mkAXI4Shim; // ================================================================ // Connect the local bus // Masters on the local bus Vector #( CoreW_Bus_Num_Masters , AXI4_Master #( Wd_CoreW_Bus_MId, Wd_Addr, Wd_Data , Wd_AW_User, Wd_W_User, Wd_B_User , Wd_AR_User, Wd_R_User)) master_vector = newVector; master_vector[cpu_uncached_master_num] = proc_uncached; master_vector[debug_module_sba_master_num] = dm_master_local; master_vector[sub_ifc_master_num] = subShim.master; // Slaves on the local bus // default slave is forwarded out directly to the Core interface Vector #( CoreW_Bus_Num_Slaves , AXI4_Slave #( Wd_CoreW_Bus_SId, Wd_Addr, Wd_Data , Wd_AW_User, Wd_W_User, Wd_B_User , Wd_AR_User, Wd_R_User)) slave_vector = newVector; slave_vector[default_slave_num] = uncached_mem_shim.slave; slave_vector[llc_slave_num] = proc.debug_module_mem_server; slave_vector[plic_slave_num] = zero_AXI4_Slave_user (plic.axi4_slave); function Vector #(CoreW_Bus_Num_Slaves, Bool) route (Bit #(Wd_Addr) addr); Vector #(CoreW_Bus_Num_Slaves, 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 (route, master_vector, slave_vector, reset_by all_harts_reset); // ================================================================ // Connect external interrupt lines from PLIC to CPU rule rl_relay_external_interrupts; // from PLIC Vector #(CoreNum, Bool) meips; Vector #(CoreNum, Bool) seips; for (Integer i = 0; i < valueof(CoreNum); i = i + 1) begin meips [i] = plic.v_targets [2 * i].m_eip; seips [i] = plic.v_targets [2 * i + 1].m_eip; end proc.m_external_interrupt_req (meips); proc.s_external_interrupt_req (seips); endrule // ================================================================ // Connect external debug module interface let dbgShim <- mkAXI4LiteShim (reset_by porReset); rule rl_debug_module_read_req; let arFlit <- get (dbgShim.master.ar); debug_module.dmi.read_addr (truncate (arFlit.araddr >> 2)); endrule rule rl_debug_module_read_rsp; let x <- debug_module.dmi.read_data; dbgShim.master.r.put(AXI4Lite_RFlit { rdata: x, rresp: OKAY, ruser: ?}); endrule rule rl_debug_module_write_req; let awFlit <- get (dbgShim.master.aw); let wFlit <- get (dbgShim.master.w); dbgShim.master.b.put(defaultValue); debug_module.dmi.write (truncate (awFlit.awaddr >> 2), wFlit.wdata); endrule let fromDbgReset <- mkPulseWire (reset_by porReset); Reg #(UInt #(8)) ndm_reset_delay <- mkReg (0, reset_by porReset); Reg #(Bool) ndm_reset_restart_running <- mkReg (False, reset_by porReset); rule rl_debug_module_send_reset (ndm_reset_delay == 0); let restartRunning <- debug_module.ndm_reset_client.request.get; ndm_reset_delay <= 110; ndm_reset_restart_running <= restartRunning; fromDbgReset.send; endrule rule rl_debug_module_count_reset_delay (ndm_reset_delay > 1); ndm_reset_delay <= ndm_reset_delay - 1; endrule rule rl_debug_module_ack_reset (ndm_reset_delay == 1); debug_module.ndm_reset_client.response.put (ndm_reset_restart_running); do_release (ndm_reset_restart_running); ndm_reset_delay <= 0; endrule // ================================================================ // Connect external interrupts to the PLIC and Proc Vector #(t_n_irq, Reg #(Bool)) irq_reg <- replicateM (mkReg (False)); Vector #(t_n_irq, Put #(Bool)) irq_ifc; for (Integer i = 0; i < valueof (t_n_irq); i = i + 1) begin irq_ifc [i] = interface Put; method put = writeReg (irq_reg[i]); endinterface; rule rl_connect_irq; plic.v_sources[i].m_interrupt_req (irq_reg[i]); endrule end let nmirq_reg <- mkReg (False); let nmirq_ifc = interface Put; method put = writeReg (nmirq_reg); endinterface; rule rl_connect_nmirq; proc.non_maskable_interrupt_req (nmirq_reg); endrule // ================================================================ // Connect other control and status signals let f_ctrl_reqs <- mkFIFO1; let f_ctrl_rsps <- mkFIFO1; rule rl_ctrl_req; case (f_ctrl_reqs.first) matches tagged ReleaseReq: do_release (False); tagged StatusReq: $display ("StatusReq not supported in Toooba"); endcase f_ctrl_reqs.deq; endrule rule rl_ctrl_rsp; f_ctrl_rsps.enq (StatusRsp(?)); endrule // ================================================================ // INTERFACE let ifc = interface CoreW_IFC; // debug related signals // --------------------- interface debug_subordinate = dbgShim.slave; // interrupt related signals // ------------------------- interface irq = irq_ifc; interface nmirq = nmirq_ifc; // other control and status signals // -------------------------------- interface controlStatusServer = toGPServer (f_ctrl_reqs, f_ctrl_rsps); // memory interfaces // ----------------- // Cached master to Fabric master interface interface manager_0 = tagController.master; // Uncached master to Fabric master interface interface manager_1 = prepend_AXI4_Master_id (0, zero_AXI4_Master_user (uncached_mem_shim.master)); interface subordinate_0 = subShim.slave; endinterface; /* `ifdef RVFI_DII interface Toooba_RVFI_DII_Server rvfi_dii_server = proc.rvfi_dii_server; `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 */ return tuple2 (fromDbgReset, ifc); endmodule: mkCoreW_reset // ================================================================ // internal bus for this Core // Masters: CPU DMem, Debug Module System Bus Access, External access // ---------------- // Fabric port numbers for masters CoreW_Bus_Master_Num cpu_uncached_master_num = 0; CoreW_Bus_Master_Num debug_module_sba_master_num = 1; CoreW_Bus_Master_Num sub_ifc_master_num = 2; // ---------------- // Fabric port numbers for slaves CoreW_Bus_Slave_Num default_slave_num = 0; // for I/O, uncached memory, etc. CoreW_Bus_Slave_Num plic_slave_num = 1; // PLIC mem-mapped registers CoreW_Bus_Slave_Num llc_slave_num = 2; // Normal cached memory (connects to coherent Last-Level Cache) // ================================================================ endpackage