// SPDX-License-Identifier: Apache-2.0
// Copyright 2019-2020 Western Digital Corporation or its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// 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.

//********************************************************************************
// $Id$
//
// Function: SweRVolf SoC-level controller
// Comments:
//
//********************************************************************************

module swervolf_syscon
  #(parameter [31:0] clk_freq_hz = 0)
  (input wire i_clk,
   input wire 	     i_rst,

   input wire [63:0] i_gpio,
   output reg [63:0] o_gpio,
   output reg 	     o_timer_irq,
   output wire 	     o_sw_irq3,
   output wire 	     o_sw_irq4,
   input wire 	     i_ram_init_done,
   input wire 	     i_ram_init_error,
   output reg [31:0] o_nmi_vec,
   output wire 	     o_nmi_int,

   input wire [5:0]  i_wb_adr,
   input wire [31:0] i_wb_dat,
   input wire [3:0]  i_wb_sel,
   input wire 	     i_wb_we,
   input wire 	     i_wb_cyc,
   input wire 	     i_wb_stb,
   output reg [31:0] o_wb_rdt,
   output reg 	     o_wb_ack);

   reg [63:0] 	      mtime;
   reg [63:0] 	      mtimecmp;

   reg 		 sw_irq3;
   reg 		 sw_irq3_edge;
   reg 		 sw_irq3_pol;
   reg 		 sw_irq3_timer;
   reg 		 sw_irq4;
   reg 		 sw_irq4_edge;
   reg 		 sw_irq4_pol;
   reg 		 sw_irq4_timer;

   reg 		 irq_timer_en;
   reg [31:0] 	 irq_timer_cnt;

   reg 		 nmi_int;
   reg 		 nmi_int_r;

`ifdef SIMPRINT
   reg [1023:0]  signature_file;
   integer 	f = 0;
   initial begin
      if ($value$plusargs("signature=%s", signature_file)) begin
	 $display("Writing signature to %0s", signature_file);
	 f = $fopen(signature_file, "w");
      end
   end
`endif

`ifndef VERSION_DIRTY
 `define VERSION_DIRTY 1
`endif
`ifndef VERSION_MAJOR
 `define VERSION_MAJOR 255
`endif
`ifndef VERSION_MINOR
 `define VERSION_MINOR 255
`endif
`ifndef VERSION_REV
 `define VERSION_REV 255
`endif
`ifndef VERSION_SHA
 `define VERSION_SHA deadbeef
`endif

   wire [31:0] version;

   assign version[31]    = `VERSION_DIRTY;
   assign version[30:24] = `VERSION_REV;
   assign version[23:16] = `VERSION_MAJOR;
   assign version[15: 8] = `VERSION_MINOR;
   assign version[ 7: 0] = `VERSION_PATCH;

   assign o_sw_irq4 = sw_irq4^sw_irq4_pol;
   assign o_sw_irq3 = sw_irq3^sw_irq3_pol;

   assign o_nmi_int = nmi_int | nmi_int_r;

   wire reg_we = i_wb_cyc & i_wb_stb & i_wb_we & !o_wb_ack;

   //00 = ver
   //04 = sha
   //08 = simprint
   //09 = simexit
   //0A = RAM status
   //0B = sw_irq
   //10 = gpio
   //20 = timer/timecmp
   //40 = SPI
   always @(posedge i_clk) begin
      o_wb_ack <= i_wb_cyc & !o_wb_ack;

      if (sw_irq3_edge)
	sw_irq3 <= 1'b0;
      if (sw_irq4_edge)
	sw_irq4 <= 1'b0;

      if (irq_timer_en)
	irq_timer_cnt <= irq_timer_cnt - 1;

      nmi_int   <= 1'b0;
      nmi_int_r <= nmi_int;

      if (irq_timer_cnt == 32'd1) begin
	 irq_timer_en <= 1'b0;
	 if (sw_irq3_timer)
	   sw_irq3 <= 1'b1;
	 if (sw_irq4_timer)
	   sw_irq4 <= 1'b1;
	 if (!(sw_irq3_timer | sw_irq4_timer))
	   nmi_int <= 1'b1;
      end

      if (reg_we)
	case (i_wb_adr[5:2])
	  2: begin //0x08-0x0B
`ifdef SIMPRINT
	     if (i_wb_sel[0]) begin
		if (|f) $fwrite(f, "%c", i_wb_dat[7:0]);
		$write("%c", i_wb_dat[7:0]);
	     end
	     if (i_wb_sel[1]) begin
		$display("\nFinito");
		$finish;
	     end
`endif
	     if (i_wb_sel[3]) begin
		sw_irq4       <= i_wb_dat[31];
		sw_irq4_edge  <= i_wb_dat[30];
		sw_irq4_pol   <= i_wb_dat[29];
		sw_irq4_timer <= i_wb_dat[28];
		sw_irq3       <= i_wb_dat[27];
		sw_irq3_edge  <= i_wb_dat[26];
		sw_irq3_pol   <= i_wb_dat[25];
		sw_irq3_timer <= i_wb_dat[24];
	     end
	  end
	  3: begin //0x0C-0x0F
	     if (i_wb_sel[0]) o_nmi_vec[7:0]   <= i_wb_dat[7:0];
	     if (i_wb_sel[1]) o_nmi_vec[15:8]  <= i_wb_dat[15:8];
	     if (i_wb_sel[2]) o_nmi_vec[23:16] <= i_wb_dat[23:16];
	     if (i_wb_sel[3]) o_nmi_vec[31:24] <= i_wb_dat[31:24];
	  end
	  4 : begin //0x10-0x13
	     if (i_wb_sel[0]) o_gpio[7:0]   <= i_wb_dat[7:0]  ;
	     if (i_wb_sel[1]) o_gpio[15:8]  <= i_wb_dat[15:8] ;
	     if (i_wb_sel[2]) o_gpio[23:16] <= i_wb_dat[23:16];
	     if (i_wb_sel[3]) o_gpio[31:24] <= i_wb_dat[31:24];
	  end
	  5: begin //0x14-0x17
	     if (i_wb_sel[0]) o_gpio[39:32] <= i_wb_dat[7:0];
	     if (i_wb_sel[1]) o_gpio[47:40] <= i_wb_dat[15:8];
	     if (i_wb_sel[2]) o_gpio[55:48] <= i_wb_dat[23:16];
	     if (i_wb_sel[3]) o_gpio[63:56] <= i_wb_dat[31:24];
	  end
	  10 : begin //0x28-0x2B
	     if (i_wb_sel[0]) mtimecmp[7:0]   <= i_wb_dat[7:0];
	     if (i_wb_sel[1]) mtimecmp[15:8]  <= i_wb_dat[15:8];
	     if (i_wb_sel[2]) mtimecmp[23:16] <= i_wb_dat[23:16];
	     if (i_wb_sel[3]) mtimecmp[31:24] <= i_wb_dat[31:24];
	  end
	  11 : begin //0x2C-0x2F
	     if (i_wb_sel[0]) mtimecmp[39:32] <= i_wb_dat[7:0];
	     if (i_wb_sel[1]) mtimecmp[47:40] <= i_wb_dat[15:8];
	     if (i_wb_sel[2]) mtimecmp[55:48] <= i_wb_dat[23:16];
	     if (i_wb_sel[3]) mtimecmp[63:56] <= i_wb_dat[31:24];
	  end
	  12 : begin //0x30-3f
	     if (i_wb_sel[0]) irq_timer_cnt[7:0]   <= i_wb_dat[7:0]  ;
	     if (i_wb_sel[1]) irq_timer_cnt[15:8]  <= i_wb_dat[15:8] ;
	     if (i_wb_sel[2]) irq_timer_cnt[23:16] <= i_wb_dat[23:16];
	     if (i_wb_sel[3]) irq_timer_cnt[31:24] <= i_wb_dat[31:24];
	  end
	  13 : begin
	     if (i_wb_sel[0])
	       irq_timer_en <= i_wb_dat[0];
	  end
	endcase

      case (i_wb_adr[5:2])
	//0x00-0x03
	0 : o_wb_rdt <= version;
	//0x04-0x07
	1 : o_wb_rdt <= 32'h`VERSION_SHA;
	//0x08-0x0C
	2 : begin
	   //0xB
	   o_wb_rdt[31:28] <= {sw_irq4, sw_irq4_edge, sw_irq4_pol, sw_irq4_timer};
	   o_wb_rdt[27:24] <= {sw_irq3, sw_irq3_edge, sw_irq3_pol, sw_irq3_timer};
	   //0xA
	   o_wb_rdt[23:18] <= 6'd0;
	   o_wb_rdt[17:16] <= {i_ram_init_error, i_ram_init_done};
	   //0x8-0x9
	   o_wb_rdt[15:0]  <= 16'd0;
	end
	//0xC-0xF
	3 : o_wb_rdt <= o_nmi_vec;
	//0x10-0x13
	4 : o_wb_rdt <= i_gpio[31:0];
	//0x14-0x17
	5 : o_wb_rdt <= i_gpio[63:32];
	//0x20-0x23
	8 : o_wb_rdt <= mtime[31:0];
	//0x24-0x27
	9 : o_wb_rdt <= mtime[63:32];
	//0x28-0x2B
	10 : o_wb_rdt <= mtimecmp[31:0];
	//0x2C-0x2F
	11 : o_wb_rdt <= mtimecmp[63:32];
	//0x30-0x33
	12 : o_wb_rdt <= irq_timer_cnt;
	//0x34-0x37
	13 : o_wb_rdt <= {31'd0, irq_timer_en};
	//0x3C
	15 : o_wb_rdt <= clk_freq_hz;
      endcase

      mtime <= mtime + 64'd1;
      o_timer_irq <= (mtime >= mtimecmp);

      if (i_rst) begin
	 mtime <= 64'd0;
	 mtimecmp <= 64'd0;
	 o_wb_ack <= 1'b0;
      end
   end
endmodule
