///////////////////////////////////////////
// wallyTracer.sv
//
// A component of the Wally configurable RISC-V project.
// 
// Copyright (C) 2021 Harvey Mudd College & Oklahoma State University
//
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
//
// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file 
// except in compliance with the License, or, at your option, the Apache License version 2.0. You 
// may obtain a copy of the License at
//
// https://solderpad.org/licenses/SHL-2.1/
//
// Unless required by applicable law or agreed to in writing, any work distributed under the 
// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
// either express or implied. See the License for the specific language governing permissions 
// and limitations under the License.
////////////////////////////////////////////////////////////////////////////////////////////////

`include "wally-config.vh"

`define NUM_REGS 32
`define NUM_CSRS 4096

`define PRINT_PC_INSTR 0
`define PRINT_MOST 0
`define PRINT_ALL 0
`define PRINT_CSRS 0

module wallyTracer(rvviTrace rvvi);

  localparam NUMREGS = `E_SUPPORTED ? 16 : 32;
  
  // wally specific signals
  logic 						 reset;
  logic 						 clk;
  logic                          InstrValidD, InstrValidE;
  logic                          StallF, StallD;
  logic                          STATUS_SXL, STATUS_UXL;
  logic [`XLEN-1:0] 			 PCNextF, PCF, PCD, PCE, PCM, PCW;
  logic [`XLEN-1:0] 			 InstrRawD, InstrRawE, InstrRawM, InstrRawW;
  logic 						 InstrValidM, InstrValidW;
  logic 						 StallE, StallM, StallW;
  logic 						 FlushD, FlushE, FlushM, FlushW;
  logic 						 TrapM, TrapW;
  logic 						 IntrF, IntrD, IntrE, IntrM, IntrW;
  logic 						 HaltM, HaltW;
  logic [1:0] 					 PrivilegeModeW;
  logic [`XLEN-1:0] 			 rf[NUMREGS];
  logic [NUMREGS-1:0] 			 rf_wb;
  logic [4:0] 					 rf_a3;
  logic 						 rf_we3;
  logic [`XLEN-1:0] 			 frf[32];
  logic [`NUM_REGS-1:0] 		 frf_wb;
  logic [4:0] 					 frf_a4;
  logic 						 frf_we4;
  logic [`XLEN-1:0]              CSRArray [logic[11:0]];
  logic [`XLEN-1:0] 			 CSRArrayOld [logic[11:0]];
  logic [`NUM_CSRS-1:0] 		 CSR_W;
  logic 						 CSRWriteM, CSRWriteW;
  logic [11:0] 					 CSRAdrM, CSRAdrW;
  
  assign clk = testbench.dut.clk;
  //  assign InstrValidF = testbench.dut.core.ieu.InstrValidF;  // not needed yet
  assign InstrValidD    = testbench.dut.core.ieu.c.InstrValidD;
  assign InstrValidE    = testbench.dut.core.ieu.c.InstrValidE;
  assign InstrValidM    = testbench.dut.core.ieu.InstrValidM;
  assign InstrRawD      = testbench.dut.core.ifu.InstrRawD;
  assign PCNextF        = testbench.dut.core.ifu.PCNextF;
  assign PCF            = testbench.dut.core.ifu.PCF;
  assign PCD            = testbench.dut.core.ifu.PCD;
  assign PCE            = testbench.dut.core.ifu.PCE;
  assign PCM            = testbench.dut.core.ifu.PCM;
  assign reset          = testbench.reset;
  assign StallF         = testbench.dut.core.StallF;
  assign StallD         = testbench.dut.core.StallD;
  assign StallE         = testbench.dut.core.StallE;
  assign StallM         = testbench.dut.core.StallM;
  assign StallW         = testbench.dut.core.StallW;
  assign FlushD         = testbench.dut.core.FlushD;
  assign FlushE         = testbench.dut.core.FlushE;
  assign FlushM         = testbench.dut.core.FlushM;
  assign FlushW         = testbench.dut.core.FlushW;
  assign TrapM          = testbench.dut.core.TrapM;
  assign HaltM          = testbench.DCacheFlushStart;
  assign PrivilegeModeW = testbench.dut.core.priv.priv.privmode.PrivilegeModeW;
  assign STATUS_SXL     = testbench.dut.core.priv.priv.csr.csrsr.STATUS_SXL;
  assign STATUS_UXL     = testbench.dut.core.priv.priv.csr.csrsr.STATUS_UXL;

  logic valid;

  always_comb begin
	// Since we are detected the CSR change by comparing the old value we need to
	// ensure the CSR is detected when the pipeline's Writeback stage is not
	// stalled.  If it is stalled we want CSRArray to hold the old value.
	if(valid) begin 
	  // machine CSRs
	  // *** missing PMP and performance counters.
	  CSRArray[12'h300] = testbench.dut.core.priv.priv.csr.csrm.MSTATUS_REGW;
	  CSRArray[12'h310] = testbench.dut.core.priv.priv.csr.csrm.MSTATUSH_REGW;
	  CSRArray[12'h305] = testbench.dut.core.priv.priv.csr.csrm.MTVEC_REGW;
	  CSRArray[12'h341] = testbench.dut.core.priv.priv.csr.csrm.MEPC_REGW;
	  CSRArray[12'h306] = testbench.dut.core.priv.priv.csr.csrm.MCOUNTEREN_REGW;
	  CSRArray[12'h320] = testbench.dut.core.priv.priv.csr.csrm.MCOUNTINHIBIT_REGW;
	  CSRArray[12'h302] = testbench.dut.core.priv.priv.csr.csrm.MEDELEG_REGW;
	  CSRArray[12'h303] = testbench.dut.core.priv.priv.csr.csrm.MIDELEG_REGW;
	  CSRArray[12'h344] = testbench.dut.core.priv.priv.csr.csrm.MIP_REGW;
	  CSRArray[12'h304] = testbench.dut.core.priv.priv.csr.csrm.MIE_REGW;
	  CSRArray[12'h301] = testbench.dut.core.priv.priv.csr.csrm.MISA_REGW;
	  CSRArray[12'hF14] = testbench.dut.core.priv.priv.csr.csrm.MHARTID_REGW;
	  CSRArray[12'h340] = testbench.dut.core.priv.priv.csr.csrm.MSCRATCH_REGW;
	  CSRArray[12'h342] = testbench.dut.core.priv.priv.csr.csrm.MCAUSE_REGW;
	  CSRArray[12'h343] = testbench.dut.core.priv.priv.csr.csrm.MTVAL_REGW;
	  CSRArray[12'hF11] = 0;
	  CSRArray[12'hF12] = 0;
	  CSRArray[12'hF13] = `XLEN'h100;
	  CSRArray[12'hF15] = 0;
	  CSRArray[12'h34A] = 0;
	  // MCYCLE and MINSTRET
	  CSRArray[12'hB00] = testbench.dut.core.priv.priv.csr.counters.counters.HPMCOUNTER_REGW[0];
	  CSRArray[12'hB02] = testbench.dut.core.priv.priv.csr.counters.counters.HPMCOUNTER_REGW[2];
	  // supervisor CSRs
	  CSRArray[12'h100] = testbench.dut.core.priv.priv.csr.csrs.csrs.SSTATUS_REGW;
	  CSRArray[12'h104] = testbench.dut.core.priv.priv.csr.csrm.MIE_REGW & 12'h222;
	  CSRArray[12'h105] = testbench.dut.core.priv.priv.csr.csrs.csrs.STVEC_REGW;
	  CSRArray[12'h141] = testbench.dut.core.priv.priv.csr.csrs.csrs.SEPC_REGW;
	  CSRArray[12'h106] = testbench.dut.core.priv.priv.csr.csrs.csrs.SCOUNTEREN_REGW;
	  CSRArray[12'h180] = testbench.dut.core.priv.priv.csr.csrs.csrs.SATP_REGW;
	  CSRArray[12'h140] = testbench.dut.core.priv.priv.csr.csrs.csrs.SSCRATCH_REGW;
	  CSRArray[12'h143] = testbench.dut.core.priv.priv.csr.csrs.csrs.STVAL_REGW;
	  CSRArray[12'h142] = testbench.dut.core.priv.priv.csr.csrs.csrs.SCAUSE_REGW;
	  CSRArray[12'h144] = testbench.dut.core.priv.priv.csr.csrm.MIP_REGW & & 12'h222 & testbench.dut.core.priv.priv.csr.csrm.MIDELEG_REGW;
	  // user CSRs
	  CSRArray[12'h001] = testbench.dut.core.priv.priv.csr.csru.csru.FFLAGS_REGW;
	  CSRArray[12'h002] = testbench.dut.core.priv.priv.csr.csru.csru.FRM_REGW;
	  CSRArray[12'h003] = {testbench.dut.core.priv.priv.csr.csru.csru.FRM_REGW, testbench.dut.core.priv.priv.csr.csru.csru.FFLAGS_REGW};
	end else begin // hold the old value if the pipeline is stalled.
	  CSRArray[12'h300] = CSRArrayOld[12'h300];
	  CSRArray[12'h310] = CSRArrayOld[12'h310];
	  CSRArray[12'h305] = CSRArrayOld[12'h305];
	  CSRArray[12'h341] = CSRArrayOld[12'h341];
	  CSRArray[12'h306] = CSRArrayOld[12'h306];
	  CSRArray[12'h320] = CSRArrayOld[12'h320];
	  CSRArray[12'h302] = CSRArrayOld[12'h302];
	  CSRArray[12'h303] = CSRArrayOld[12'h303];
	  CSRArray[12'h344] = CSRArrayOld[12'h344];
	  CSRArray[12'h304] = CSRArrayOld[12'h304];
	  CSRArray[12'h301] = CSRArrayOld[12'h301];
	  CSRArray[12'hF14] = CSRArrayOld[12'hF14];
	  CSRArray[12'h340] = CSRArrayOld[12'h340];
	  CSRArray[12'h342] = CSRArrayOld[12'h342];
	  CSRArray[12'h343] = CSRArrayOld[12'h343];
	  CSRArray[12'hF11] = CSRArrayOld[12'hF11];
	  CSRArray[12'hF12] = CSRArrayOld[12'hF12];
	  CSRArray[12'hF13] = CSRArrayOld[12'hF13];
	  CSRArray[12'hF15] = CSRArrayOld[12'hF15];
	  CSRArray[12'h34A] = CSRArrayOld[12'h34A];
	  // MCYCLE and MINSTRET
	  CSRArray[12'hB00] = CSRArrayOld[12'hB00];
	  CSRArray[12'hB02] = CSRArrayOld[12'hB02];
	  // supervisor CSRs
	  CSRArray[12'h100] = CSRArrayOld[12'h100];
	  CSRArray[12'h104] = CSRArrayOld[12'h104];
	  CSRArray[12'h105] = CSRArrayOld[12'h105];
	  CSRArray[12'h141] = CSRArrayOld[12'h141];
	  CSRArray[12'h106] = CSRArrayOld[12'h106];
	  CSRArray[12'h180] = CSRArrayOld[12'h180];
	  CSRArray[12'h140] = CSRArrayOld[12'h140];
	  CSRArray[12'h143] = CSRArrayOld[12'h143];
	  CSRArray[12'h142] = CSRArrayOld[12'h142];
	  CSRArray[12'h144] = CSRArrayOld[12'h144];
	  // user CSRs
	  CSRArray[12'h001] = CSRArrayOld[12'h001];
	  CSRArray[12'h002] = CSRArrayOld[12'h002];
	  CSRArray[12'h003] = CSRArrayOld[12'h003];
	end	  
  end

  genvar 							index;
  assign rf[0] = '0;
  for(index = 1; index < NUMREGS; index += 1) 
	assign rf[index] = testbench.dut.core.ieu.dp.regf.rf[index];

  assign rf_a3  = testbench.dut.core.ieu.dp.regf.a3;
  assign rf_we3 = testbench.dut.core.ieu.dp.regf.we3;
  
  always_comb begin
	rf_wb <= '0;
	if(rf_we3)
	  rf_wb[rf_a3] <= 1'b1;
  end

  for(index = 0; index < NUMREGS; index += 1) 
	assign frf[index] = testbench.dut.core.fpu.fpu.fregfile.rf[index];
  
  assign frf_a4  = testbench.dut.core.fpu.fpu.fregfile.a4;
  assign frf_we4 = testbench.dut.core.fpu.fpu.fregfile.we4;
  
  always_comb begin
	frf_wb <= '0;
	if(frf_we4)
	  frf_wb[frf_a4] <= 1'b1;
  end

  assign CSRAdrM  = testbench.dut.core.priv.priv.csr.CSRAdrM;
  assign CSRWriteM = testbench.dut.core.priv.priv.csr.CSRWriteM;
  
  // pipeline to writeback stage
  flopenrc #(`XLEN) InstrRawEReg (clk, reset, FlushE, ~StallE, InstrRawD, InstrRawE);
  flopenrc #(`XLEN) InstrRawMReg (clk, reset, FlushM, ~StallM, InstrRawE, InstrRawM);
  flopenrc #(`XLEN) InstrRawWReg (clk, reset, FlushW, ~StallW, InstrRawM, InstrRawW);
  flopenrc #(`XLEN) PCWReg (clk, reset, FlushW, ~StallW, PCM, PCW);
  flopenrc #(1)     InstrValidMReg (clk, reset, FlushW, ~StallW, InstrValidM, InstrValidW);
  flopenrc #(1)     TrapWReg (clk, reset, 1'b0, ~StallW, TrapM, TrapW);
  flopenrc #(1)     HaltWReg (clk, reset, 1'b0, ~StallW, HaltM, HaltW);

  flopenrc #(1)     IntrFReg (clk, reset, 1'b0, ~StallF, TrapM, IntrF);
  flopenrc #(1)     IntrDReg (clk, reset, FlushD, ~StallD, IntrF, IntrD);
  flopenrc #(1)     IntrEReg (clk, reset, FlushE, ~StallE, IntrD, IntrE);
  flopenrc #(1)     IntrMReg (clk, reset, FlushM, ~StallM, IntrE, IntrM);
  flopenrc #(1)     IntrWReg (clk, reset, FlushW, ~StallW, IntrM, IntrW);

  flopenrc #(12)    CSRAdrWReg (clk, reset, FlushW, ~StallW, CSRAdrM, CSRAdrW);
  flopenrc #(1)     CSRWriteWReg (clk, reset, FlushW, ~StallW, CSRWriteM, CSRWriteW);

  // Initially connecting the writeback stage signals, but may need to use M stage
  // and gate on ~FlushW.

  assign valid  = InstrValidW & ~StallW;
  assign rvvi.clk = clk;
  assign #1 rvvi.valid[0][0]    = valid;
  assign rvvi.order[0][0]    = CSRArray[12'hB02];  // TODO: IMPERAS Should be event order
  assign rvvi.insn[0][0]     = InstrRawW;
  assign rvvi.pc_rdata[0][0] = PCW;
  assign rvvi.trap[0][0]     = 0; // TODO: IMPERAS TrapW;
  assign rvvi.halt[0][0]     = HaltW;
  assign rvvi.intr[0][0]     = IntrW;
  assign rvvi.mode[0][0]     = PrivilegeModeW;
  assign rvvi.ixl[0][0]      = PrivilegeModeW == 2'b11 ? 2'b10 :
					           PrivilegeModeW == 2'b01 ? STATUS_SXL : STATUS_UXL;
  assign rvvi.pc_wdata[0][0] = ~FlushW ? PCM :
						       ~FlushM ? PCE :
						       ~FlushE ? PCD :
						       ~FlushD ? PCF : PCNextF;

  for(index = 0; index < `NUM_REGS; index += 1) begin
	assign rvvi.x_wdata[0][0][index] = rf[index];
	assign rvvi.x_wb[0][0][index]    = rf_wb[index];
	assign rvvi.f_wdata[0][0][index] = frf[index];
	assign rvvi.f_wb[0][0][index]    = frf_wb[index];
  end

  // record previous csr value.
  integer index4;
  always_ff @(posedge clk) begin
	for (index4 = 0; index4 < `NUM_CSRS; index4 += 1) begin
// IMPERAS
	  //CSR_W[index4] = (CSRArrayOld[index4] != CSRArray[index4]) ? 1 : 0;
	  CSRArrayOld[index4] = CSRArray[index4];
	end
  end
  
  // check for csr value change.
  genvar index5;
  for(index5 = 0; index5 < `NUM_CSRS; index5 += 1) begin
	// CSR_W should only indicate the change when the Writeback stage is not stalled and valid.
    assign #2 CSR_W[index5] = (CSRArrayOld[index5] != CSRArray[index5]) ? 1 : 0;
    assign rvvi.csr_wb[0][0][index5] = CSR_W[index5];
    assign rvvi.csr[0][0][index5]    = CSRArray[index5];
  end
  
//  always @rvvi.clk $display("%t @rvvi.clk=%X", $time, rvvi.clk);
//  always @rvvi.csr[0][0]['h300] $display("%t rvvi.csr[0][0]['h300]=%X", $time, rvvi.csr[0][0]['h300]);
//  always @rvvi.csr_wb[0][0]['h300] $display("%t rvvi.csr_wb[0][0]['h300]=%X", $time, rvvi.csr_wb[0][0]['h300]);
//  always @rvvi.valid[0][0] $display("%t rvvi.valid[0][0]=%X", $time, rvvi.valid[0][0]);
  
  // *** implementation only cancel? so sc does not clear?
  assign rvvi.lrsc_cancel[0][0] = '0;

  integer index2;

  always_ff @(posedge clk) begin
	if(rvvi.valid[0][0]) begin
	  if(`PRINT_PC_INSTR & !(`PRINT_ALL | `PRINT_MOST))
		$display("order = %08d, PC = %08x, insn = %08x", rvvi.order[0][0], rvvi.pc_rdata[0][0], rvvi.insn[0][0]);
	  else if(`PRINT_MOST & !`PRINT_ALL)
		$display("order = %08d, PC = %010x, insn = %08x, trap = %1d, halt = %1d, intr = %1d, mode = %1x, ixl = %1x, pc_wdata = %010x, x%02d = %016x, f%02d = %016x, csr%03x = %016x", 
		        rvvi.order[0][0], rvvi.pc_rdata[0][0], rvvi.insn[0][0], rvvi.trap[0][0], rvvi.halt[0][0], rvvi.intr[0][0], rvvi.mode[0][0], rvvi.ixl[0][0], rvvi.pc_wdata[0][0], rf_a3, rvvi.x_wdata[0][0][rf_a3], frf_a4, rvvi.f_wdata[0][0][frf_a4], CSRAdrW, rvvi.csr[0][0][CSRAdrW]);
	  else if(`PRINT_ALL) begin
		$display("order = %08d, PC = %08x, insn = %08x, trap = %1d, halt = %1d, intr = %1d, mode = %1x, ixl = %1x, pc_wdata = %08x", 
				 rvvi.order[0][0], rvvi.pc_rdata[0][0], rvvi.insn[0][0], rvvi.trap[0][0], rvvi.halt[0][0], rvvi.intr[0][0], rvvi.mode[0][0], rvvi.ixl[0][0], rvvi.pc_wdata[0][0]);
	  	for(index2 = 0; index2 < `NUM_REGS; index2 += 1) begin
		  $display("x%02d = %08x", index2, rvvi.x_wdata[0][0][index2]);
		end
		for(index2 = 0; index2 < `NUM_REGS; index2 += 1) begin
		  $display("f%02d = %08x", index2, rvvi.f_wdata[0][0][index2]);
		end
	  end
	  if (`PRINT_CSRS) begin
		for(index2 = 0; index2 < `NUM_CSRS; index2 += 1) begin
		  if(CSR_W[index2]) begin
			$display("%t: CSR %03x = %x", $time(), index2, CSRArray[index2]);
		  end
		end
	  end
	end
    if(HaltW) $finish;
  end



endmodule