Files
Toooba/src_Testbench/Top/Top_HW_Side.bsv
Jessica Clarke 0516db5e8d Bump BSV-RVFI-DII and refactor to support new parcel-based interface
This means FetchStage should now behave in the same way with RVFI-DII as
with an I-Cache. A Dii_Parcel_Id is fed alongside PC everywhere relevant
and follows very similar logic, but, importantly it's just extra state
on the side, it doesn't affect what we do with the branch predictor and
parcel combining/instruction decoding logic.
2020-07-21 14:36:36 +01:00

477 lines
16 KiB
Plaintext

// Copyright (c) 2013-2020 Bluespec, Inc. All Rights Reserved.
//
//-
// RVFI_DII + CHERI modifications:
// 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 Top_HW_Side;
// ================================================================
// mkTop_HW_Side is the top-level system for simulation.
// mkMem_Model is a memory model.
// **** CAVEAT FOR IVERILOG USERS: The 'C_Imports' sections below are
// disabled for IVerilog until we find a clean solution. They depend
// on imported C which is non-trivial in IVerilog because IVerilog
// still depends on the older Verilog VPI standard instead of the
// newer DPI-C standard. C-imported functions are used for:
// UART input polling and character-reading
// Writing tandem-verfication encoded trace data
// (Note: UART output does not depend on C-imported functions and so
// will work ok even in IVerilog)
// ================================================================
// BSV lib imports
`include "ProcConfig.bsv"
import FIFOF :: *;
import GetPut :: *;
import ClientServer :: *;
import Connectable :: *;
import Clocks :: *;
// ----------------
// BSV additional libs
import Cur_Cycle :: *;
import GetPut_Aux :: *;
// ================================================================
// Project imports
import ISA_Decls :: *;
import TV_Info :: *;
import SoC_Top :: *;
import Mem_Controller :: *;
import Mem_Model :: *;
import Fabric_Defs :: *;
import PLIC :: *;
`ifndef IVERILOG
import C_Imports :: *;
`endif
`ifdef INCLUDE_GDB_CONTROL
import External_Control :: *;
import Debug_Module :: *;
`endif
`ifdef RVFI_DII
import RVFI_DII :: *;
import Types :: *;
import ProcTypes :: *;
`endif
// ================================================================
// Top-level module.
// Instantiates the SoC.
// Instantiates a memory model.
`ifndef RVFI_DII
(* synthesize *)
module mkTop_HW_Side (Empty);
`else
module mkPre_Top_HW_Side (Toooba_RVFI_DII_Server);
`endif
// ================================================================
// The RISC-V Debug Module is at the following point in the module hierarchy:
// soc_top.corew.debug_module
// (instances of mkSoC_Top, mkCoreW, mkDebug_Module)
// The Debug Module is reset only once, on power-up, hence we pass
// its reset down from here.
// (power-on reset) and the Debug Module's 'hart_reset' control.
let power_on_reset <- exposeCurrentReset;
let dm_power_on_reset = power_on_reset;
// The rest of the system (soc_top and mem_model) are reset:
// - on power-on, and
// - when the Debug Module requests an NDM reset (for non-DebugModule).
`ifdef INCLUDE_GDB_CONTROL
let clk <- exposeCurrentClock;
Bool initial_reset_val = False;
Integer ndm_reset_duration = 10; // NOTE: assuming 10 cycle reset enough for NDM
let ndm_reset_controller <- mkReset(ndm_reset_duration, initial_reset_val, clk);
let ndm_reset <- mkResetEither (power_on_reset, ndm_reset_controller.new_rst);
`else
let ndm_reset = power_on_reset;
`endif
// ================================================================
// STATE
SoC_Top_IFC soc_top <- mkSoC_Top (dm_power_on_reset, reset_by ndm_reset);
Mem_Model_IFC mem_model <- mkMem_Model (reset_by ndm_reset);
// Connect SoC to raw memory
let memCnx <- mkConnection (soc_top.to_raw_mem, mem_model.mem_server, reset_by ndm_reset);
// ================================================================
// Actions on reset
function Action fa_reset_actions;
action
`ifndef RVFI_DII
$display ("================================================================");
$display ("Bluespec RISC-V standalone system simulation v1.2");
$display ("Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved.");
$display ("================================================================");
`endif
// Set CPU verbosity and logdelay (simulation only)
Bool v1 <- $test$plusargs ("v1");
Bool v2 <- $test$plusargs ("v2");
Bit #(4) verbosity = ((v2 ? 2 : (v1 ? 1 : 0)));
Bit #(64) logdelay = 0; // # of instructions after which to set verbosity
soc_top.set_verbosity (verbosity, logdelay);
// ----------------
// Load optional tohost and fromhost addrs from symbol-table file
Fabric_Addr tohost_addr = 0;
Fabric_Addr fromhost_addr = 0;
Bool watch_tohost <- $test$plusargs ("tohost");
`ifndef IVERILOG
// Note: see 'CAVEAT FOR IVERILOG USERS' above
if (watch_tohost) begin
let tha <- c_get_symbol_val ("tohost");
tohost_addr = truncate (tha);
let fha <- c_get_symbol_val ("fromhost");
fromhost_addr = truncate (fha);
end
`endif
$display ("INFO: watch_tohost %d, tohost_addr = 0x%0h, fromhost_addr = 0x%0h",
watch_tohost, tohost_addr, fromhost_addr);
soc_top.start (tohost_addr, fromhost_addr);
endaction
endfunction
// ================================================================
`ifdef INCLUDE_GDB_CONTROL
// ================================================================
// NDM reset from DM
Reg #(Bit #(8)) rg_ndm_reset_delay <- mkReg (0);
rule rl_ndm_reset (rg_ndm_reset_delay == 0);
let x <- soc_top.ndm_reset_client.request.get;
ndm_reset_controller.assertReset;
rg_ndm_reset_delay <= fromInteger (ndm_reset_duration + 200); // NOTE: heuristic
$display ("%0d: %m.rl_ndm_reset: asserting NDM reset (for non-DebugModule) for %0d cycles",
cur_cycle, ndm_reset_duration);
endrule
rule rl_ndm_reset_wait (rg_ndm_reset_delay != 0);
if (rg_ndm_reset_delay == 1) begin
fa_reset_actions;
Bool is_running = True;
soc_top.ndm_reset_client.response.put (is_running);
$display ("%0d: %m.rl_ndm_reset_wait: sent NDM reset ack (for non-DebugModule) to Debug Module",
cur_cycle);
end
rg_ndm_reset_delay <= rg_ndm_reset_delay - 1;
endrule
// ================================================================
`endif
// ================================================================
// BEHAVIOR
Reg #(Bool) rg_banner_printed <- mkReg (False);
// Display a banner
rule rl_step0 (! rg_banner_printed);
rg_banner_printed <= True;
fa_reset_actions;
// ----------------
// Open file for Tandem Verification trace output
`ifdef INCLUDE_TANDEM_VERIF
`ifndef IVERILOG
// Note: see 'CAVEAT FOR IVERILOG USERS' above
let success <- c_trace_file_open ('h_AA);
if (success == 0) begin
$display ("ERROR: Top_HW_Side.rl_step0: error opening trace file.");
$display (" Aborting.");
$finish (1);
end
else
$display ("Top_HW_Side.rl_step0: opened trace file.");
`else
$display ("Warning: tandem verification output logs not available in IVerilog");
`endif
`endif
// ----------------
// Open connection to remote debug client
`ifdef INCLUDE_GDB_CONTROL
`ifndef IVERILOG
// Note: see 'CAVEAT FOR IVERILOG USERS' above
let dmi_status <- c_debug_client_connect (dmi_default_tcp_port);
if (dmi_status != dmi_status_ok) begin
$display ("ERROR: Top_HW_Side.rl_step0: error opening debug client connection.");
$display (" Aborting.");
$finish (1);
end
`else
$display ("Warning: Debug client connection not available in IVerilog");
`endif
`endif
endrule
// ================================================================
// Tandem verifier: drain and output vectors of bytes
`ifdef INCLUDE_TANDEM_VERIF
rule rl_tv_vb_out;
let tv_info <- soc_top.tv_verifier_info_get.get;
let n = tv_info.num_bytes;
let vb = tv_info.vec_bytes;
`ifndef IVERILOG
Bit #(32) success = 1;
for (Bit #(32) j = 0; j < fromInteger (valueOf (TV_VB_SIZE)); j = j + 8) begin
Bit #(64) w64 = { vb [j+7], vb [j+6], vb [j+5], vb [j+4], vb [j+3], vb [j+2], vb [j+1], vb [j] };
let success1 <- c_trace_file_load_word64_in_buffer (j, w64);
end
if (success == 0)
$display ("ERROR: Top_HW_Side.rl_tv_vb_out: error loading %0d bytes into buffer", n);
else begin
// Send the data
success <- c_trace_file_write_buffer (n);
if (success == 0)
$display ("ERROR: Top_HW_Side.rl_tv_vb_out: error writing out bytevec data buffer (%0d bytes)", n);
end
if (success == 0) begin
$finish (1);
end
`endif
endrule
`endif
// ================================================================
// UART console I/O
// Relay system console output to terminal
rule rl_relay_console_out;
let ch <- soc_top.get_to_console.get;
$write ("%c", ch);
$fflush (stdout);
endrule
// Poll terminal input and relay any chars into system console input.
// Note: rg_console_in_poll is used to poll only every N cycles, whenever it wraps around to 0.
// Note: see 'CAVEAT FOR IVERILOG USERS' above for why this is ifdef'd out for iVerilog users.
`ifndef IVERILOG
Reg #(Bit #(12)) rg_console_in_poll <- mkReg (0);
rule rl_relay_console_in;
if (rg_console_in_poll == 0) begin
Bit #(8) ch <- c_trygetchar (?);
if (ch != 0) begin
soc_top.put_from_console.put (ch);
/*
$write ("%0d: Top_HW_Side.bsv.rl_relay_console: ch = 0x%0h", cur_cycle, ch);
if (ch >= 'h20) $write (" ('%c')", ch);
$display ("");
*/
end
end
rg_console_in_poll <= rg_console_in_poll + 1;
endrule
`endif
// ================================================================
// Interaction with remote debug client
`ifdef INCLUDE_GDB_CONTROL
FIFOF #(Control_Req) f_external_control_reqs <- mkFIFOF;
FIFOF #(Control_Rsp) f_external_control_rsps <- mkFIFOF;
rule rl_debug_client_request_recv;
Bit #(64) req <- c_debug_client_request_recv ('hAA);
Bit #(8) status = req [63:56];
Bit #(32) data = req [55:24];
Bit #(16) addr = req [23:8];
Bit #(8) op = req [7:0];
if (status == dmi_status_err) begin
$display ("%0d: Top_HW_Side.rl_debug_client_request_recv: receive error; aborting",
cur_cycle);
$finish (1);
end
else if (status == dmi_status_ok) begin
// $write ("%0d: Top_HW_Side.rl_debug_client_request_recv:", cur_cycle);
if (op == dmi_op_read) begin
// $display (" READ 0x%0h", addr);
let control_req = Control_Req {op: external_control_req_op_read_control_fabric,
arg1: zeroExtend (addr),
arg2: 0};
f_external_control_reqs.enq (control_req);
end
else if (op == dmi_op_write) begin
// $display (" WRITE 0x%0h 0x%0h", addr, data);
let control_req = Control_Req {op: external_control_req_op_write_control_fabric,
arg1: zeroExtend (addr),
arg2: zeroExtend (data)};
f_external_control_reqs.enq (control_req);
end
else if (op == dmi_op_shutdown) begin
$display ("Top_HW_Side.rl_debug_client_request_recv: SHUTDOWN");
$finish (0);
end
else if (op == dmi_op_start_command) begin // For debugging only
// $display (" START COMMAND ================================");
end
else
$display (" Top_HW_Side.rl_debug_client_request_recv: UNRECOGNIZED OP %0d; ignoring", op);
end
endrule
rule rl_debug_client_response_send;
let control_rsp <- pop (f_external_control_rsps);
// $display ("Top_HW_Side.rl_debug_client_response_send: 0x%0h", control_rsp.result);
let status <- c_debug_client_response_send (truncate (control_rsp.result));
if (status == dmi_status_err) begin
$display ("%0d: Top_HW_Side.rl_debug_client_response_send: send error; aborting",
cur_cycle);
$finish (1);
end
endrule
// ----------------------------------------------------------------
// External debug requests and responses
Control_Req req = f_external_control_reqs.first;
Integer dmi_verbosity = 0; // For debugging
rule rl_handle_external_req_read_request (req.op == external_control_req_op_read_control_fabric);
f_external_control_reqs.deq;
soc_top.dmi.read_addr (truncate (req.arg1));
if (dmi_verbosity != 0) begin
$display ("%0d: %m.rl_handle_external_req_read_request", cur_cycle);
$display (" ", fshow (req));
end
endrule
rule rl_handle_external_req_read_response;
let x <- soc_top.dmi.read_data;
let rsp = Control_Rsp {status: external_control_rsp_status_ok, result: signExtend (x)};
f_external_control_rsps.enq (rsp);
if (dmi_verbosity != 0) begin
$display ("%0d: %m.rl_handle_external_req_read_response", cur_cycle);
$display (" ", fshow (rsp));
end
endrule
rule rl_handle_external_req_write (req.op == external_control_req_op_write_control_fabric);
f_external_control_reqs.deq;
soc_top.dmi.write (truncate (req.arg1), truncate (req.arg2));
// let rsp = Control_Rsp {status: external_control_rsp_status_ok, result: 0};
// f_external_control_rsps.enq (rsp);
if (dmi_verbosity != 0) begin
$display ("%0d: %m.rl_handle_external_req_write", cur_cycle);
$display (" ", fshow (req));
end
endrule
rule rl_handle_external_req_err ( (req.op != external_control_req_op_read_control_fabric)
&& (req.op != external_control_req_op_write_control_fabric));
f_external_control_reqs.deq;
let rsp = Control_Rsp {status: external_control_rsp_status_err, result: 0};
f_external_control_rsps.enq (rsp);
$display ("%0d: %m.rl_handle_external_req_err: unknown req.op", cur_cycle);
$display (" ", fshow (req));
endrule
(* descending_urgency = "rl_handle_external_req_read_request, rl_handle_external_req_read_response" *)
(* descending_urgency = "rl_handle_external_req_read_response, rl_handle_external_req_write" *)
(* descending_urgency = "rl_handle_external_req_read_response, rl_handle_external_req_err" *)
(* descending_urgency = "rl_handle_external_req_write, rl_handle_external_req_err" *)
rule rl_handle_external_dummy_for_urgency_attribs_only;
endrule
`endif
// ================================================================
// INTERFACE
// None (this is top-level)
// Except RVFI_DII interface if enabled
`ifdef RVFI_DII
return soc_top.rvfi_dii_server;
`endif
endmodule
// ================================================================
`ifdef RVFI_DII
// ================================================================
// mkPiccolo_RVFI_DII instantiates the toplevel with the RVFI_DII
// interfaces enabled, allowing testing with directly
// ================================================================
(* synthesize *)
module mkTop_HW_Side(Empty)
provisos (Add#(a__, TDiv#(DataSz,8), 8), Add#(b__, DataSz, 64), Add#(c__, TDiv#(DataSz,8), 8), Add#(d__, DataSz, 64));
Reg #(Bool) rg_banner_printed <- mkReg (False);
// Display a banner
rule rl_step0 (! rg_banner_printed);
$display ("================================================================");
$display ("Bluespec RISC-V standalone system simulation v1.2");
$display ("Copyright (c) 2017-2018 Bluespec, Inc. All Rights Reserved.");
$display ("================================================================");
rg_banner_printed <= True;
endrule
RVFI_DII_Bridge #(DataSz, DataSz, TMul#(SupSize, 2), SupSize) bridge <- mkRVFI_DII_Bridge("", 5001);
let dut <- mkPre_Top_HW_Side(reset_by bridge.new_rst);
mkConnection(bridge.client.report, dut.trace_report);
rule rl_provide_instr;
Dii_Parcel_Id req <- dut.seqReqFirst.get;
Dii_Parcel_Resps resps <- bridge.client.getParcels(req);
dut.parcelResps.put(resps);
endrule
endmodule
`endif
// ================================================================
endpackage: Top_HW_Side