module User_Logic (

		////////// CLOCK //////////
		OSC_100,

		////////// LED //////////
		LED,

		////////// BUTTON //////////
		BUTTON,

		////////// HSTC 2/3, connect to CLR Board //////////
		CLR_RXCC,
		CLR_RXCC_EN,
		CLR_RXSERTFG_EN,
		CLR_RXSERTFG,
		CLR_RXSERTC,
		CLR_RXSERTC_EN,
		CLR_RX_BASE,
		CLR_RXCLK_BASE,
		CLR_RXCLK_FULL,
		CLR_RX2CC,
		CLR_RX2CC_EN,
		CLR_RX2SERTFG_EN,
		CLR_RX2SERTFG,
		CLR_RX2SERTC,
		CLR_RX2SERTC_EN,
		CLR_RX_FULL,
		CLR_RXCLK_MEDIUM,
		CLR_HSTC_SDA,
		CLR_HSTC_SCL,
		CLR_RX_MEDIUM,

		// PCI Clock & Reset
		iCLK,
		iRST_n,

		//=====  User PCI Interrupt Request & acknowledge
		oCTRL_INT_REQ,
		iCTRL_INT_ACK,
		//=====  User Reigster signals

		//  User Register Input, PC -> FPGA
		iCMD,
		iUART_WRITE,
		iUART_WRITE_DATA,
		iUART_READ,

		//  User Register Output, FPGA -> PC
		oSTATUS,
		oUART_READ_DATA,
		oUART_STATUS,


		//==================== DDR2 Interface
		// Global Write Port Ready
		iWRITE_PORT_READY,

        //  Read signal of read port
        iREAD_ACK_r1,

		//***** Write Port 1 Control Signals
		oCLK_w1,
		oWRITE_DATA_w1,
		oRST_n_w1,
		oFLUSH_REQ_w1,
		iFLUSH_BUSY_w1,
		iFULL_w1,
		oWRITE_w1
		
		,line_counter,l_cnt,state

	);
output  [31:0]line_counter;
output  [31:0]l_cnt;
output  [2:0]state; 

//------------------------------------
//	PORT declarations
//------------------------------------
////////// CLOCK //////////
input                     		OSC_100;

////////// LED //////////
output    	[3:0]           	LED;

////////// BUTTON //////////
input     	[1:0]           	BUTTON;

////////// HSTC 2/3, connect to CLR Board //////////
input     	[27:0]          	CLR_RX_BASE;
output    	[4:1]           	CLR_RXCC;
output                    		CLR_RXCC_EN;
output                    		CLR_RXSERTFG_EN;
input                     		CLR_RXSERTFG;
output                    		CLR_RXSERTC;
output                    		CLR_RXSERTC_EN;
input                     		CLR_RXCLK_BASE;
input                     		CLR_RXCLK_FULL;
input     	[27:0]          	CLR_RX_FULL;
output    	[4:1]           	CLR_RX2CC;
output                    		CLR_RX2CC_EN;
output                    		CLR_RX2SERTFG_EN;
input                     		CLR_RX2SERTFG;
output                    		CLR_RX2SERTC;
output                    		CLR_RX2SERTC_EN;
input                     		CLR_RXCLK_MEDIUM;
inout                     		CLR_HSTC_SDA;
output                    		CLR_HSTC_SCL;
input     	[27:0]          	CLR_RX_MEDIUM;

// PCI Clock & Reset
input			iCLK;
input			iRST_n;


//=====  User PCI Interrupt Request & acknowledge
output			oCTRL_INT_REQ;
input			iCTRL_INT_ACK;
//=====  User Reigster signals

//  User Register Input, PC -> FPGA
input	[31:0]	iCMD;
input	[31:0]	iUART_WRITE;
input	[31:0]	iUART_WRITE_DATA;
input	[31:0]	iUART_READ;

//  User Register Output, FPGA -> PC
output	[31:0]	oSTATUS;
output	[31:0]	oUART_READ_DATA;
output	[31:0]	oUART_STATUS;


//==================== DDR2 Interface
// Global Write Port Ready
input			iWRITE_PORT_READY;

// Read signal of read port
input           iREAD_ACK_r1;

//***** Write Port 1 Control Signals    8* 256 * 7400
output			oCLK_w1;
output	[63:0]	oWRITE_DATA_w1;
output			oRST_n_w1;
output			oFLUSH_REQ_w1;
input			iFLUSH_BUSY_w1;
input			iFULL_w1;
output			oWRITE_w1;



//------------------------------------
//	Add your design here.
//------------------------------------
reg     oCTRL_INT_REQ;

parameter   LVAL_ACTIVE_CYCLES   =   3700;

wire    USER_RESET;
wire    LVAL;
wire    DVAL;
wire    BASE_CLK;
wire    [ 7:0]PORT_A_DATA;
wire    [ 7:0]PORT_B_DATA;

//  Camera Bits Assignment
assign      USER_RESET      =   BUTTON[0] & START_CMD;      // Combine with Start Command
assign      BASE_CLK        =   CLR_RXCLK_BASE;
assign      LVAL            =   CLR_RX_BASE[24];
assign      DVAL            =   CLR_RX_BASE[26];  
assign      PORT_A_DATA     =   { CLR_RX_BASE[5], CLR_RX_BASE[27], CLR_RX_BASE[6], CLR_RX_BASE[4:0] }; 
assign      PORT_B_DATA     =   { CLR_RX_BASE[11:10], CLR_RX_BASE[14:12], CLR_RX_BASE[9:7] }; 

//--------------------------------
//          START Command
//--------------------------------    
    
    //  Eising edge of the DVAL signal
    wire    dval_ris;
    reg     dval_new;
    reg     dval_old;
    always@(posedge OSC_100)
        begin
            dval_new <= DVAL;
            dval_old <= dval_new;
        end
    assign  dval_ris =  ~dval_old & dval_new;
    
    wire    START_BIT;
    assign  START_BIT = iCMD[0];   // high -> START, low -> STOP 
     
    //test
    reg test;
    always@(posedge OSC_100)
        if (~BUTTON[0])
            test <= 0;
        else if (~BUTTON[1])
            test <= 1;
        
     
     
    reg     START_CMD;
    always@(posedge OSC_100)
        begin
            if (dval_ris)
                begin
                   START_CMD <= START_BIT;
                end              
        end


//---------------------------------------------------
//  Combine 8 bytes to 64-bit & Write into the FIFO
//---------------------------------------------------
    reg [3:0]comb_count;
    always@(posedge BASE_CLK)
        begin
            if (~USER_RESET)
                comb_count <= 0;
            else if (LVAL & DVAL)
                begin
                    if (comb_count == 7)
                        comb_count <= 0;
                    else 
                        comb_count <= comb_count + 1;
                end
            else 
                comb_count <= 0;
        end
        
    //  Combine 64bits
    reg fifo_wrt;
    always@(posedge BASE_CLK)    
        begin
            if (~USER_RESET)
                fifo_wrt <= 0;
            else if (comb_count == 7)
                fifo_wrt <= 1;
            else
                fifo_wrt <= 0;
        end
        
    reg [63:0]comb64_data;
    always@(posedge BASE_CLK)   
        begin
            if (~USER_RESET)
                comb64_data <= 0;
            else if (LVAL & DVAL)
                case (comb_count)
                    0:  comb64_data[7:0]    <= PORT_A_DATA;
                    1:  comb64_data[15:8]   <= PORT_A_DATA;
                    2:  comb64_data[23:16]  <= PORT_A_DATA;
                    3:  comb64_data[31:24]  <= PORT_A_DATA;
                    4:  comb64_data[39:32]  <= PORT_A_DATA;
                    5:  comb64_data[47:40]  <= PORT_A_DATA;
                    6:  comb64_data[55:48]  <= PORT_A_DATA;
                    7:  comb64_data[63:56]  <= PORT_A_DATA;
                    default:    comb64_data[7:0]    <= PORT_A_DATA;
                endcase    
        end
    
    // Port A Clock Crossing FIFO
    reg     fifo_rd;
    wire    [63:0]fifo_q;
    wire    fifo_empty;
    PORT_A_FIFO FIFO0(
            
            .aclr(~USER_RESET),
            .wrclk(BASE_CLK),
            .wrreq(fifo_wrt),
            .data(comb64_data),
            
            .rdclk(OSC_100),
            .rdreq(real_fifo_rd),          
            .q(fifo_q),
            .rdempty(fifo_empty),
            .wrfull()
            
            );    
    
    //  FIFO Read & Multi-Port Write Signal
    always@(posedge OSC_100)
        begin
            if ( (~USER_RESET) & (~iWRITE_PORT_READY) )
                fifo_rd <= 0;
            else if (~fifo_empty)
                fifo_rd <= 1;
            else
                fifo_rd <= 0;
        end
      
    wire    real_fifo_rd;  
    assign  real_fifo_rd    =   fifo_rd & ~fifo_empty;    
        
    assign  oCLK_w1         =   OSC_100;    
    assign  oRST_n_w1       =   USER_RESET;
    assign  oWRITE_w1       =   (bus_sw)?     ddr2_wrt          :   real_fifo_rd;
    assign  oWRITE_DATA_w1  =   (bus_sw)?     ram_to_ddr_data   :   fifo_q;
    assign  oFLUSH_REQ_w1   =   0;
    
    //  Falling Edge of LVAL
    reg fe_lval_new;
    reg fe_lval_old;
    always@(posedge OSC_100)  
        begin
            fe_lval_new <= LVAL;
            fe_lval_old <= fe_lval_new;
        end
        
    wire    fall_lval;
    assign  fall_lval   =   ~fe_lval_new &  fe_lval_old;         
        
    //  Delay 5 clock cycles
    reg [9:0]delay_flval;
    always@(posedge OSC_100)
        begin
            delay_flval[0] <=   fall_lval;
            if (delay_flval[0])
                delay_flval[1] <=   1;
            else
                delay_flval[1] <=   0;
                
            if (delay_flval[1])
                delay_flval[2] <=   1;
            else
                delay_flval[2] <=   0;
                
            if (delay_flval[2])
                delay_flval[3] <=   1;
            else
                delay_flval[3] <=   0;
                
            if (delay_flval[3])
                delay_flval[4] <=   1;
            else
                delay_flval[4] <=   0;
                
            if (delay_flval[4])
                delay_flval[5] <=   1;
            else
                delay_flval[5] <=   0;
                
            if (delay_flval[5])
                delay_flval[6] <=   1;
            else
                delay_flval[6] <=   0;
                
            if (delay_flval[6])
                delay_flval[7] <=   1;
            else
                delay_flval[7] <=   0; 
                
            if (delay_flval[7])
                delay_flval[8] <=   1;
            else
                delay_flval[8] <=   0;
                
            if (delay_flval[8])             // to align with delay_rd2
                delay_flval[9] <=   1;
            else
                delay_flval[9] <=   0;                                 
        end
    
    //  Multi-Port Bus Switch
    reg bus_sw;
    always@(posedge OSC_100)
        begin
            if (~USER_RESET)
                bus_sw <= 0;
            else if (delay_flval[4])
                bus_sw <= 1;
            else if (dval_ris)
                bus_sw <= 0;    
        end
    
//-----------------------------------------------------------
//                      Camera Port B     
//-----------------------------------------------------------    
    
    //  Write signal of RAM
    reg ram_wr;
    reg [7:0]wr_data;
    always@(posedge BASE_CLK)
        begin
            if (~USER_RESET)
                ram_wr <= 0;
            else if (LVAL & DVAL)
                begin
                    ram_wr <= 1;
                    wr_data <= PORT_B_DATA;
                end
            else
                 ram_wr <= 0;
        end
    
    //  Write Address of RAM
    reg [11:0]ram_wr_addr;
    always@(posedge BASE_CLK)
        begin
            if (~USER_RESET)
                ram_wr_addr <= 0;
            else if (ram_wr)  
                begin
                    if (ram_wr_addr == 3699)
                        ram_wr_addr <= 0;
                    else
                        ram_wr_addr <= ram_wr_addr + 1;                         
                end
        end
    
    
    
    
    //  Port B RAM   
    wire    [63:0]ram_qout; 
    Port_B_RAM RAM0(
            
            .wrclock(BASE_CLK),
            .wraddress(ram_wr_addr),
            .wren(ram_wr),
            .data(wr_data),
            
            .rdclock(OSC_100),
            .rdaddress(ram_rd_addr),
            .rden(ram_rd),     
            .q(ram_qout)
            
            );

    //  Read signal of RAM
    reg     ram_rd;
    always@(posedge OSC_100)
        begin
            if (~USER_RESET)
                ram_rd <= 0;
            else if (bus_sw)
                begin
                    if (ram_rd_addr == 0)
                        ram_rd <= 0;
                    else 
                        ram_rd <= 1;
                end
        end
    
    //  Address signal of RAM
    reg     [8:0]ram_rd_addr;
    always@(posedge OSC_100)
        begin
            if (~USER_RESET)
                ram_rd_addr <= 462;
            else if (delay_flval[4])
                ram_rd_addr <= 462;
            else if (ram_rd)
                begin
                    if (ram_rd_addr == 0)
                        ram_rd_addr <= 0;
                    else
                        ram_rd_addr <= ram_rd_addr - 1;                
                end
                
        end
    
    //  Delay 2 clock cycles to wait real data output of RAM
    reg delay_rd0;
    reg delay_rd1;
    reg delay_rd2;
    always@(posedge OSC_100)
        begin
            delay_rd0 <= ram_rd;
            delay_rd1 <= delay_rd0;
            delay_rd2 <= delay_rd1;
        end

    //  Re-assign Port B Data
    reg [63:0]ram_to_ddr_data; 
    always@(posedge OSC_100)
        begin
            if (~USER_RESET)
                ram_to_ddr_data <= 0;
            else if (delay_rd1)
                begin
                    if (delay_flval[8])
                        begin
                             ram_to_ddr_data[7:0]   <=  comb64_data[7:0];
                             ram_to_ddr_data[15:8]  <=  comb64_data[15:8];  
                             ram_to_ddr_data[23:16] <=  comb64_data[23:16];   
                             ram_to_ddr_data[31:24] <=  comb64_data[31:24];  
                             ram_to_ddr_data[39:32] <=  ram_qout[31:24];   
                             ram_to_ddr_data[47:40] <=  ram_qout[23:16]; 
                             ram_to_ddr_data[55:48] <=  ram_qout[15:8]; 
                             ram_to_ddr_data[63:56] <=  ram_qout[7:0]; 
                        end                
                    else 
                        begin
                            ram_to_ddr_data[7:0]    <=  ram_qout[63:56]; 
                            ram_to_ddr_data[15:8]   <=  ram_qout[55:48];
                            ram_to_ddr_data[23:16]  <=  ram_qout[47:40];
                            ram_to_ddr_data[31:24]  <=  ram_qout[39:32];
                            ram_to_ddr_data[39:32]  <=  ram_qout[31:24]; 
                            ram_to_ddr_data[47:40]  <=  ram_qout[23:16];
                            ram_to_ddr_data[55:48]  <=  ram_qout[15:8];
                            ram_to_ddr_data[63:56]  <=  ram_qout[7:0];
                        end 
                end
        end

    //  Multi-Port Write Signal
    reg ddr2_wrt;
    always@(posedge OSC_100)
        begin
            if (~USER_RESET)    
                ddr2_wrt <= 0;
            else if (delay_rd1)
                ddr2_wrt <= 1;
            else
                ddr2_wrt <= 0;
        end
            
    //  pixel counter
    reg [31:0]wrt_count;
    always@(posedge OSC_100)
        begin
            if (~BUTTON[0])
                wrt_count <= 0;
            else if (oWRITE_w1)
                begin
                    if (wrt_count == 924)
                        wrt_count <= 0;
                    else
                        wrt_count <= wrt_count + 1;
                end
//            else if (wrt_count == 924)
//                wrt_count <= 0;
        end
        
    //  Write line counter
    reg [63:0]line_counter;
    always@(posedge OSC_100)
       begin
            if (~BUTTON[0])
                line_counter <= 0;
            else if ( (wrt_count == 924) & (oWRITE_w1) )
                line_counter <= line_counter + 1;
       end
        

    //  Read pixel counter
    reg [31:0]rd_count;
    always@(posedge iCLK)
        begin
            if (~BUTTON[0])
                rd_count <= 0;
            else if (iREAD_ACK_r1)
                begin
                    if (rd_count == 924)
                        rd_count <= 0;
                    else
                        rd_count <= rd_count + 1;
                end  
   
        end

    //  Read line counter
    reg [63:0]rd_line_counter;
    always@(posedge iCLK)
       begin
            if (~BUTTON[0])
                rd_line_counter <= 0;
            else if ( (rd_count == 924) & (iREAD_ACK_r1) )
                rd_line_counter <= rd_line_counter + 1;
       end

    assign  oSTATUS = line_counter - rd_line_counter;
    
 //----------------------------------------------------//
 //                UART Control Logic                  //
 //----------------------------------------------------//
 
    //  Detecting write pluse
    reg wrt_old;
    reg wrt_new;
    always@(posedge iCLK)
        begin
            wrt_new <= iUART_WRITE[0];
            wrt_old <= wrt_new;
        end
 
    wire    wrt_pluse;
    assign  wrt_pluse = ~wrt_old & wrt_new;
 
    //  Detecting read pluse
    reg rd_old;
    reg rd_new;
    always@(posedge iCLK)
        begin
            rd_new <= iUART_READ[0];
            rd_old <= rd_new;
        end
 
    wire    rd_pluse;
    assign  rd_pluse = ~rd_old & rd_new;   
 
    reg rd_reg;
    reg [31:0]oUART_READ_DATA;
    always@(posedge iCLK)
        begin
            rd_reg <= rd_pluse;
            if (rd_pluse)
                oUART_READ_DATA[7:0] <= uart_rdata;               
        end
 
 
    //  25MHz clock generator
    reg [1:0]div100_cnt;
    always@(posedge OSC_100)
        div100_cnt <= div100_cnt + 1;
 
 
    wire    [7:0]uart_rdata;
 
 // UART Controller
uart_control UART0(

	.clk(iCLK),
	.reset_n(BUTTON[0]),
	// tx
	.write(wrt_pluse),
	.writedata(iUART_WRITE_DATA[7:0]),
	.wrfull(oUART_STATUS[1]),
	// rx
	.read(rd_reg),
	.readdata(uart_rdata),
	.rdempty(oUART_STATUS[0]),
	//
	.uart_clk_25m(div100_cnt[1]),
	.uart_tx(CLR_RXSERTC),
	.uart_rx(CLR_RXSERTFG)
	
);
  
// FULL DUPLEX MODE
assign CLR_RXSERTFG_EN = 1'b0;
assign CLR_RXSERTC_EN  = 1'b1;    
    

endmodule
