/*****************************************************************************\
|                      Copyright (C) 2021-2022 Luke Wren                      |
|                     SPDX-License-Identifier: Apache-2.0                     |
\*****************************************************************************/

// Single-ported top level file for Hazard3 CPU. This file instantiates the
// Hazard3 core, and arbitrates its instruction fetch and load/store signals
// down to a single AHB5 master port.

`ifdef HAZARD3_RVFI_STANDALONE
`include "hazard3_rvfi_standalone_defs.vh"
`endif

`default_nettype none

module hazard3_cpu_1port #(
`include "hazard3_config.vh"
) (
	// Global signals
	input wire                clk,
	input wire                clk_always_on,
	input wire                rst_n,

	`ifdef RISCV_FORMAL
	`RVFI_OUTPUTS ,
	`endif

	// Power control signals
	output wire               pwrup_req,
	input  wire               pwrup_ack,
	output wire               clk_en,
	output wire               unblock_out,
	input  wire               unblock_in,

	// AHB5 Master port
	output reg  [W_ADDR-1:0]  haddr,
	output reg                hwrite,
	output reg  [1:0]         htrans,
	output reg  [2:0]         hsize,
	output wire [2:0]         hburst,
	output reg  [3:0]         hprot,
	output wire               hmastlock,
	output reg  [7:0]         hmaster,
	output reg                hexcl,
	input  wire               hready,
	input  wire               hresp,
	input  wire               hexokay,
	output wire [W_DATA-1:0]  hwdata,
	input  wire [W_DATA-1:0]  hrdata,

	// Memory ordering signals
	output wire               fence_i_vld,
	output wire               fence_d_vld,
	input  wire               fence_rdy,

	// Debugger run/halt control
	input  wire               dbg_req_halt,
	input  wire               dbg_req_halt_on_reset,
	input  wire               dbg_req_resume,
	output wire               dbg_halted,
	output wire               dbg_running,
	// Debugger access to data0 CSR
	input  wire [W_DATA-1:0]  dbg_data0_rdata,
	output wire [W_DATA-1:0]  dbg_data0_wdata,
	output wire               dbg_data0_wen,
	// Debugger instruction injection
	input  wire [W_DATA-1:0]  dbg_instr_data,
	input  wire               dbg_instr_data_vld,
	output wire               dbg_instr_data_rdy,
	output wire               dbg_instr_caught_exception,
	output wire               dbg_instr_caught_ebreak,

	// Optional debug system bus access patch-through
	input  wire [W_ADDR-1:0]  dbg_sbus_addr,
	input  wire               dbg_sbus_write,
	input  wire [1:0]         dbg_sbus_size,
	input  wire               dbg_sbus_vld,
	output wire               dbg_sbus_rdy,
	output wire               dbg_sbus_err,
	input  wire [W_DATA-1:0]  dbg_sbus_wdata,
	output wire [W_DATA-1:0]  dbg_sbus_rdata,

	// Identification CSR values
	input  wire [W_DATA-1:0]  mhartid_val,
	input  wire [3:0]         eco_version,

	// Level-sensitive interrupt sources
	input wire [NUM_IRQS-1:0] irq,       // -> mip.meip
	input wire                soft_irq,  // -> mip.msip
	input wire                timer_irq  // -> mip.mtip
);

// ----------------------------------------------------------------------------
// Processor core

// Instruction fetch signals
wire              core_aph_req_i;
wire              core_aph_panic_i;
wire              core_aph_ready_i;
wire              core_dph_ready_i;
wire              core_dph_err_i;

wire [W_ADDR-1:0] core_haddr_i;
wire [2:0]        core_hsize_i;
wire              core_priv_i;
wire [W_DATA-1:0] core_rdata_i;


// Load/store signals
wire              core_aph_req_d;
wire              core_aph_excl_d;
wire              core_aph_ready_d;
wire              core_dph_ready_d;
wire              core_dph_err_d;
wire              core_dph_exokay_d;

wire [W_ADDR-1:0] core_haddr_d;
wire [2:0]        core_hsize_d;
wire              core_priv_d;
wire              core_hwrite_d;
wire [W_DATA-1:0] core_wdata_d;
wire [W_DATA-1:0] core_rdata_d;


hazard3_core #(
`include "hazard3_config_inst.vh"
) core (
	.clk                        (clk),
	.clk_always_on              (clk_always_on),
	.rst_n                      (rst_n),

	.pwrup_req                  (pwrup_req),
	.pwrup_ack                  (pwrup_ack),
	.clk_en                     (clk_en),
	.unblock_out                (unblock_out),
	.unblock_in                 (unblock_in),

	`ifdef RISCV_FORMAL
	`RVFI_CONN ,
	`endif

	.bus_aph_req_i              (core_aph_req_i),
	.bus_aph_panic_i            (core_aph_panic_i),
	.bus_aph_ready_i            (core_aph_ready_i),
	.bus_dph_ready_i            (core_dph_ready_i),
	.bus_dph_err_i              (core_dph_err_i),
	.bus_haddr_i                (core_haddr_i),
	.bus_hsize_i                (core_hsize_i),
	.bus_priv_i                 (core_priv_i),
	.bus_rdata_i                (core_rdata_i),

	.bus_aph_req_d              (core_aph_req_d),
	.bus_aph_excl_d             (core_aph_excl_d),
	.bus_aph_ready_d            (core_aph_ready_d),
	.bus_dph_ready_d            (core_dph_ready_d),
	.bus_dph_err_d              (core_dph_err_d),
	.bus_dph_exokay_d           (core_dph_exokay_d),
	.bus_haddr_d                (core_haddr_d),
	.bus_hsize_d                (core_hsize_d),
	.bus_priv_d                 (core_priv_d),
	.bus_hwrite_d               (core_hwrite_d),
	.bus_wdata_d                (core_wdata_d),
	.bus_rdata_d                (core_rdata_d),

	.fence_i_vld                (fence_i_vld),
	.fence_d_vld                (fence_d_vld),
	.fence_rdy                  (fence_rdy),

	.dbg_req_halt               (dbg_req_halt),
	.dbg_req_halt_on_reset      (dbg_req_halt_on_reset),
	.dbg_req_resume             (dbg_req_resume),
	.dbg_halted                 (dbg_halted),
	.dbg_running                (dbg_running),
	.dbg_data0_rdata            (dbg_data0_rdata),
	.dbg_data0_wdata            (dbg_data0_wdata),
	.dbg_data0_wen              (dbg_data0_wen),
	.dbg_instr_data             (dbg_instr_data),
	.dbg_instr_data_vld         (dbg_instr_data_vld),
	.dbg_instr_data_rdy         (dbg_instr_data_rdy),
	.dbg_instr_caught_exception (dbg_instr_caught_exception),
	.dbg_instr_caught_ebreak    (dbg_instr_caught_ebreak),

	.mhartid_val                (mhartid_val),
	.eco_version                (eco_version),

	.irq                        (irq),
	.soft_irq                   (soft_irq),
	.timer_irq                  (timer_irq)
);

// ----------------------------------------------------------------------------
// Arbitration state machine

wire      bus_gnt_i;
wire      bus_gnt_d;
wire      bus_gnt_s;

reg       bus_hold_aph;
reg [2:0] bus_gnt_ids_prev;

// Note use of clk_always_on: SBA may use this arbiter to access the bus
// whilst the core is asleep.

always @ (posedge clk_always_on or negedge rst_n) begin
	if (!rst_n) begin
		bus_hold_aph <= 1'b0;
		bus_gnt_ids_prev <= 3'h0;
	end else begin
		bus_hold_aph <= htrans[1] && !hready && !hresp;
		bus_gnt_ids_prev <= {bus_gnt_i, bus_gnt_d, bus_gnt_s};
	end
end

// Debug SBA access is lower priority than load/store, but higher than
// instruction fetch. This isn't ideal, but in a tight loop the core may be
// performing an instruction fetch or load/store on every single cycle, and
// this is a simple way to guarantee eventual success of debugger accesses. A
// more complex way would be to add a "panic timer" to boost a stalled sbus
// access over an instruction fetch.

// Note that, often, the sbus will be disconnected: it doesn't provide any
// increase in debugger bus throughput compared with the program buffer and
// autoexec. It's useful for "minimally intrusive" debug bus access(i.e. less
// intrusive than halting the core and resuming it) e.g. for Segger RTT.

reg bus_active_dph_s;

assign {bus_gnt_i, bus_gnt_d, bus_gnt_s} =
	bus_hold_aph                      ? bus_gnt_ids_prev :
	core_aph_panic_i                  ? 3'b100 :
	core_aph_req_d                    ? 3'b010 :
	dbg_sbus_vld && !bus_active_dph_s ? 3'b001 :
	core_aph_req_i                    ? 3'b100 :
	                                    3'b000 ;
reg bus_active_dph_i;
reg bus_active_dph_d;

always @ (posedge clk_always_on or negedge rst_n) begin
	if (!rst_n) begin
		bus_active_dph_i <= 1'b0;
		bus_active_dph_d <= 1'b0;
		bus_active_dph_s <= 1'b0;
	end else if (hready) begin
		bus_active_dph_i <= bus_gnt_i;
		bus_active_dph_d <= bus_gnt_d;
		bus_active_dph_s <= bus_gnt_s;
	end
end

// ----------------------------------------------------------------------------
// Address phase request muxing

localparam HTRANS_IDLE = 2'b00;
localparam HTRANS_NSEQ = 2'b10;

wire [3:0] hprot_data  = {
	2'b00,                  // Noncacheable/nonbufferable
	core_priv_d,            // Privileged or Normal as per core state
	1'b1                    // Data access
};

wire [3:0] hprot_instr = {
	2'b00,                  // Noncacheable/nonbufferable
	core_priv_i,            // Privileged or Normal as per core state
	1'b0                    // Instruction access
};

wire [3:0] hprot_sbus = {
	2'b00,         // Noncacheable/nonbufferable
	1'b1,          // Always privileged
	1'b1           // Data access
};

assign hburst = 3'b000;   // HBURST_SINGLE
assign hmastlock = 1'b0;

always @ (*) begin
	if (bus_gnt_s) begin
		htrans  = HTRANS_NSEQ;
		hexcl   = 1'b0;
		haddr   = dbg_sbus_addr;
		hsize   = {1'b0, dbg_sbus_size};
		hwrite  = dbg_sbus_write;
		hprot   = hprot_sbus;
		hmaster = 8'h01;
	end else if (bus_gnt_d) begin
		htrans  = HTRANS_NSEQ;
		hexcl   = core_aph_excl_d;
		haddr   = core_haddr_d;
		hsize   = core_hsize_d;
		hwrite  = core_hwrite_d;
		hprot   = hprot_data;
		hmaster = 8'h00;
	end else if (bus_gnt_i) begin
		htrans  = HTRANS_NSEQ;
		hexcl   = 1'b0;
		haddr   = core_haddr_i;
		hsize   = core_hsize_i;
		hwrite  = 1'b0;
		hprot   = hprot_instr;
		hmaster = 8'h00;
	end else begin
		htrans  = HTRANS_IDLE;
		hexcl   = 1'b0;
		haddr   = {W_ADDR{1'b0}};
		hsize   = 3'h0;
		hwrite  = 1'b0;
		hprot   = 4'h0;
		hmaster = 8'h00;
	end
end

assign hwdata = bus_active_dph_s ? dbg_sbus_wdata : core_wdata_d;

// ----------------------------------------------------------------------------
// Response routing

// Data buses directly connected
assign core_rdata_d   = hrdata;
assign core_rdata_i   = hrdata;
assign dbg_sbus_rdata = hrdata;

// Handhshake based on grant and bus stall
assign core_aph_ready_i = hready && bus_gnt_i;
assign core_dph_ready_i = bus_active_dph_i && hready;
assign core_dph_err_i   = bus_active_dph_i && hresp;

// D-side errors are reported even when not ready, so that the core can make
// use of the two-phase error response to cleanly squash a second load/store
// chasing the faulting one down the pipeline.
assign core_aph_ready_d  = hready && bus_gnt_d;
assign core_dph_ready_d  = bus_active_dph_d && hready;
assign core_dph_err_d    = bus_active_dph_d && hresp;
assign core_dph_exokay_d = bus_active_dph_d && hexokay;

assign dbg_sbus_err = bus_active_dph_s && hresp;
assign dbg_sbus_rdy = bus_active_dph_s && hready;

endmodule

`ifndef YOSYS
`default_nettype wire
`endif
