// Copyright (C) 2005 Peio Azkarate, peio@opencores.org
//
//   This source file is free software; you can redistribute it
//   and/or modify it under the terms of the GNU Lesser General
//   Public License as published by the Free Software Foundation;
//   either version 2.1 of the License, or (at your option) any
//   later version.
//

(* signal_encoding = "user" *)
(* safe_implementation = "yes" *)

module pciwbsequ_new ( clk_i, nrst_i, cmd_i, cbe_i, frame_i, irdy_i, devsel_o, 
	trdy_o, adrcfg_i, adrmem_i, pciadrLD_o, pcidOE_o, parOE_o, wbdatLD_o, 
	wbrgdMX_o, wbd16MX_o, wrcfg_o, rdcfg_o, wb_sel_o, wb_we_o, wb_stb_o, 
	wb_cyc_o, wb_ack_i, wb_err_i, debug_init, debug_access );

   	// General 
	input clk_i;
	input nrst_i;
	// pci 
	// adr_i
	input [3:0] cmd_i;
	input [3:0] cbe_i;
	input frame_i;
	input irdy_i;
	output devsel_o;
	output trdy_o;
	// control
	input adrcfg_i;
	input adrmem_i;
	output pciadrLD_o;
	output pcidOE_o;
	output reg parOE_o;
	output wbdatLD_o;
	output wbrgdMX_o;
	output wbd16MX_o;
	output wrcfg_o;
	output rdcfg_o;
	// whisbone
	output [1:0] wb_sel_o;
	output wb_we_o;
	inout wb_stb_o;
	output wb_cyc_o;
	input wb_ack_i;
	input wb_err_i;
	// debug signals
	output reg debug_init;
	output reg debug_access;

	//type PciFSM is ( PCIIDLE, B_BUSY, S_DATA1, S_DATA2, TURN_AR );	
  	//wire pst_pci 		: PciFSM;
  	//wire nxt_pci 		: PciFSM;

	//	typedef enum reg [2:0] {
	//		RED, GREEN, BLUE, CYAN, MAGENTA, YELLOW
	//	} color_t;
	//
	//	color_t   my_color = GREEN;

	// parameter PCIIDLE    = 2'b00;
	// parameter B_BUSY     = 2'b01;
	// parameter S_DATA1    = 2'b10;
	// parameter S_DATA2    = 2'b11;
	// parameter TURN_AR    = 3'b100;

  	reg [2:0] pst_pci;
  	reg [2:0] nxt_pci;

	parameter [2:0] 
		PCIIDLE    = 3'b000,
		B_BUSY     = 3'b001,
		S_DATA1    = 3'b010,
		S_DATA2    = 3'b011,
		TURN_AR    = 3'b100;


	initial begin
		pst_pci = 3'b000;
	end

	initial begin
		nxt_pci = 3'b000;
	end
	
  	wire sdata1;
  	wire sdata2;
	wire idleNX;
	wire sdata1NX;
	wire sdata2NX;
	wire turnarNX;
	wire idle;
  	reg  devselNX_n;
  	reg  trdyNX_n;
  	reg  devsel;
  	reg  trdy;
  	wire adrpci;
  	wire acking;
  	wire rdcfg;
	reg  targOE;
	reg  pcidOE;

	// always @(nrst_i or clk_i or nxt_pci)
	always @(negedge nrst_i or posedge clk_i)
	begin
    	if( nrst_i == 0 )
			pst_pci <= PCIIDLE;
  		else 
			pst_pci <= nxt_pci; 
	end

	// always @(negedge nrst_i or posedge clk_i)
	always @( pst_pci or frame_i or irdy_i or adrcfg_i or adrpci or acking )
	begin
		devselNX_n 	<= 1'b1;
		trdyNX_n 	<= 1'b1;	
		case (pst_pci)
            PCIIDLE : 
			begin
				if ( frame_i == 0 )
					nxt_pci <= B_BUSY; 	
				else
					nxt_pci <= PCIIDLE;
			end
           	B_BUSY:
				if ( adrpci == 0 )
					nxt_pci <= TURN_AR;
				else
					begin
					nxt_pci <= S_DATA1;
					devselNX_n <= 0; 
					end
			S_DATA1:
				if ( acking == 1 )
					begin
					nxt_pci 	<= S_DATA2;
					devselNX_n 	<= 0; 					
					trdyNX_n 	<= 0;	
					end
				else
					begin
					nxt_pci <= S_DATA1;
					devselNX_n <= 0; 					
					end
			S_DATA2:
	       		if ( frame_i == 1 && irdy_i == 0 )
					nxt_pci <= TURN_AR;
				else
					begin
					nxt_pci <= S_DATA2;
					devselNX_n <= 0;
					trdyNX_n <= 0;
					end
			TURN_AR:
				if ( frame_i == 1 )
					nxt_pci <= PCIIDLE;
				else
					nxt_pci <= TURN_AR;
	    endcase
	end

	// FSM control signals
	assign adrpci = adrmem_i;
	
	assign acking = (
		( wb_ack_i == 1 || wb_err_i == 1 ) || 
		( adrcfg_i == 1 &&   irdy_i == 0)
	) ? 1'b1 : 1'b0; 

	// FSM derived Control signals
	assign idle 		= ( pst_pci <= PCIIDLE ) ? 1'b1 : 1'b0;
	assign sdata1 		= ( pst_pci <= S_DATA1 ) ? 1'b1 : 1'b0;
	assign sdata2 		= ( pst_pci <= S_DATA2 ) ? 1'b1 : 1'b0;
	assign idleNX 		= ( nxt_pci <= PCIIDLE ) ? 1'b1 : 1'b0;
	assign sdata1NX 	= ( nxt_pci <= S_DATA1 ) ? 1'b1 : 1'b0;
	assign sdata2NX 	= ( nxt_pci <= S_DATA2 ) ? 1'b1 : 1'b0;
	assign turnarNX 	= ( nxt_pci <= TURN_AR ) ? 1'b1 : 1'b0;

	// PCI Data Output Enable
	// always @( nrst_i or clk_i or cmd_i [0] or sdata1NX or turnarNX )
	always @(negedge nrst_i or posedge clk_i)
	begin
    	if ( nrst_i == 0 )
			pcidOE <= 0;
  		else
			if ( sdata1NX == 1 && cmd_i [0] == 0 )
				pcidOE <= 1;
			else 
				if ( turnarNX == 1 )
					pcidOE <= 0;
	end

	assign pcidOE_o = pcidOE;

	// PAR Output Enable
	// PCI Read data phase
	// PAR is valid 1 cicle after data is valid
	// always @( nrst_i or clk_i or cmd_i [0] or sdata2NX or turnarNX )
	always @(negedge nrst_i or posedge clk_i)
	begin
    	if ( nrst_i == 0 )
			parOE_o <= 0;
  		else
			if ( ( sdata2NX == 1 || turnarNX == 1 ) && cmd_i [0] == 0 )
				parOE_o <= 1;
			else
				parOE_o <= 0;
	end

	// Target s/t/s signals OE control
	// targOE <= '1' when ( idle = '0' and adrpci = '1' ) else '0';
	// always @( nrst_i or clk_i or sdata1NX or idleNX )
	always @(negedge nrst_i or posedge clk_i)
	begin
    	if ( nrst_i == 0 )
			targOE <= 0;
		else
			if ( sdata1NX == 1 )
				targOE <= 1;
			else 
				if ( idleNX == 1 )
					targOE <= 0;
	end
		
    // WHISBONE outs
	assign wb_cyc_o = (adrmem_i == 1 && sdata1 == 1) ? 1'b1 : 1'b0;
    assign wb_stb_o = (adrmem_i == 1 && sdata1 == 1 && irdy_i == 0 ) ? 1'b1 : 1'b0;

	// PCI(Little endian) to WB(Big endian)
	assign wb_sel_o [1] = (! cbe_i [0]) || (! cbe_i [2]);
	assign wb_sel_o [0] = (! cbe_i [1]) || (! cbe_i [3]);	

	assign wb_we_o = cmd_i [0];

	// Syncronized PCI outs
	always @(negedge nrst_i or posedge clk_i)
	begin
		if( nrst_i == 0 )
			begin
			devsel 	<= 1;
			trdy 	<= 1;
			end
		else
			begin
			devsel <= devselNX_n;
			trdy   <= trdyNX_n;
			end
	end

	assign devsel_o = ( targOE == 1 ) ? devsel : 1'bZ;
	assign trdy_o   = ( targOE == 1 ) ? trdy   : 1'bZ;

	// rd/wr Configuration Space Registers
	assign wrcfg_o = (
		adrcfg_i == 1 &&
		cmd_i [0] == 1 &&
		sdata2 == 1
	) ? 1'b1 : 1'b0;

	assign rdcfg = (
		adrcfg_i == 1 &&
		cmd_i [0] == 0 &&
		(sdata1 == 1 || sdata2 == 1)
	) ? 1'b1 : 1'b0;

	assign rdcfg_o = rdcfg;

	// LoaD enable signals
	assign pciadrLD_o = ! frame_i;
	assign wbdatLD_o  = wb_ack_i;

	// Mux control signals
	assign wbrgdMX_o = ! rdcfg;
	assign wbd16MX_o = (cbe_i [3] == 0 || cbe_i [2] == 0) ? 1'b1 : 1'b0;
	
	// debug outs 
	always @(negedge nrst_i or posedge clk_i)
	begin
		if ( nrst_i == 0 )
			debug_init <= 0;
		else
			if (devsel == 0)
				debug_init <= 1;
	end

	always @(negedge nrst_i or posedge clk_i)
	begin
		if ( nrst_i == 0 )
			debug_access <= 0;
		else
			if (wb_stb_o == 1)
				debug_access <= 1;
	end

endmodule