`include "const.sv"

interface processor_port(
	input clk, rst,

	// RAM
	output [23:0]	ram_addr,
	output [15:0] 	ram_wr_data,
	input  [15:0] 	ram_rd_data,
	output 			ram_wr_en,
	output			ram_rd_en,
	input			ram_busy,
	input			ram_rd_ready,
	input			ram_rd_ack,

	// COM
	output [7:0]	com_addr,
	output [7:0]	com_wr,
	input  [7:0]	com_rd,
	input  			com_interrupt
	);

endinterface

module com_block(
	input clk, rst,
	// Communication to processor
	input  wire [7:0]	addr,
	input  reg  [7:0]	in_data,
	output reg  [7:0]	out_data,
	output reg			interrupt,

	// IO
	output reg  [7:0]	leds,
	input  wire [3:0]	switches,
	output wire			uart0_tx,
	input  wire			uart0_rx,
	input  wire			key1
);

	/* UART */
	reg [3:0] uart0_reg;
	// UART REG
	// 0: Is Recieving
	// 1: Is Transmitting
	// 2: Echo ON
	// 3: Recieved and waiting 
	reg uart0_recv;
	reg uart0_transmit;
	reg [7:0] tx_byte, rx_byte, rx_buf;
	// Clock divide = 1e6 / (9600 * 4) = 26
	// for 115200 clock divide = 2
	//uart#(.CLOCK_DIVIDE(1302)) uart0(
	uart#(.CLOCK_DIVIDE(`UART0_CLK_DIV)) uart0(
			.clk(clk), 
			.rst(0), 
			.rx(uart0_rx),
			.tx(uart0_tx),
			.tx_byte(tx_byte),
			.rx_byte(rx_byte),
			.received(uart0_recv),
			`ifdef SYNTHESIS
			.is_receiving(uart0_reg[0]),
			.is_transmitting(uart0_reg[1]),
			`endif
			.transmit(uart0_transmit)
	);
	
	initial uart0_reg[1:0] = 2'b00;
	//reg [7:0] reset_str [7];
	//reg [2:0] reset_seq;
	//always_comb begin
	//		reset_str[0] = 8'h72;
	//		reset_str[1] = 8'h65;
	//		reset_str[2] = 8'h73;
	//		reset_str[3] = 8'h65;
	//		reset_str[4] = 8'h74;
	//		reset_str[5] = 8'h2e;
	//		reset_str[6] = 8'h10;
	//end
	
	reg interrupt_reg;
	assign interrupt = (key1 & interrupt_reg);
	always_ff@(posedge clk) begin
		if(rst) begin 
			//reset_seq <= 0;
			uart0_reg[2] <= 0;
			uart0_reg[3] <= 0;
			//interrupt <= 0;
			interrupt_reg <= 0;
			leds <= 'b0000_0000;
			rx_buf <= 0;
		end
		//else if(~uart0_reg[2] && reset_seq != 7) reset_seq <= reset_seq + 1;
		else begin
			case(addr)
				8'h03: uart0_reg[2] <= in_data[2]; 
				//8'h06: leds <= in_data;
			endcase
			if(~key1) interrupt_reg <= 1;
			if(interrupt) interrupt_reg <= 0;
			leds <= {3'b0, uart0_recv, uart0_reg};
			if(uart0_recv) begin 
					uart0_reg[3] <= 1;
					rx_buf <= rx_byte;
			end
			if(~uart0_recv & addr == 8'h09) uart0_reg[3] <= 0;
		end
	end

	always_comb begin
		//tx_byte = 8'h23;
		//uart0_transmit = 1;
		uart0_transmit = (addr == 8'h05) || (uart0_recv && uart0_reg[2]);
		tx_byte = (uart0_recv && uart0_reg[2]) ? rx_byte : in_data;
		//tx_byte = in_data;
		case(addr)
			8'h03: out_data = in_data; 				// Set uart0 flags
			8'h04: out_data = {4'b0, uart0_reg};  	// Read uart0 flags
			8'h05: out_data = in_data;  			// Write to uart0
			8'h07: out_data = leds;					// Read current LEDs
			8'h08: out_data = {4'b0, switches};		// Read DIP
			8'h09: out_data = rx_buf;  			// Write to uart0
			default: out_data = 0;
		endcase
	end
endmodule


module sdram_block(
	input mclk, fclk, rst_n,

	// SDRAM Control
	input [23:0]	ram_addr,
	input [15:0] 	ram_wr_data,
	output reg [15:0] 	ram_rd_data,
	input 			ram_wr_en,
	input			ram_rd_en,
	output			ram_busy,
	output			ram_rd_ready,
	output			ram_rd_ack,
	

	// SDRAM I/O
	inout  [15:0]	DRAM_DQ,	// Data
	output [12:0] 	DRAM_ADDR,	// Address
	output [1:0]	DRAM_DQM,	// Byte Data Mask
	output			DRAM_CLK,	// Clock
	output			DRAM_CKE,	// Clock Enable
	output			DRAM_WE_N,	// Write Enable
	output			DRAM_CAS_N, // Column Address Strobe
	output			DRAM_RAS_N, // Row Address Strobe
	output			DRAM_CS_N,	// Chip Select
	output [1:0] 	DRAM_BA		// Bank Address
);
	/* SDRAM */

	// 1 MHz side wires
	//wire [39:0] wr_fifo;// Address 24-bit and 16-bit Data
	//wire wr_enable;		// wr_enable ] <-> [ wr 	: wr_enable to push fifo
	//wire wr_full;		// wr_full   ] <-> [ full 	: signal that we are full
	//wire rd_enable;		// rd_enable - wr 			: rd_enable to push rd addr to fifo
	//wire rdaddr_full;	// rdaddr_full - full 		: signal we cannot read more
	//wire [15:0] rddo_fifo;
	//wire ctrl_rd_ready;	// wr - rd_ready 			: push data from dram to fifo

	//// 100MHz side wires
	//wire [39:0] wro_fifo;
	//wire ctrl_busy;       	// rd ] <-> [ busy 		: pop fifo when ctrl not busy
	//wire ctrl_wr_enable;  	// empty_n - wr_enable 	: signal ctrl data is ready
	//wire [23:0] rdao_fifo;
	//wire ctrl_rd_enable;	// empty_n - rd_enable 	: signal ctrl addr ready
	//wire [15:0] rddata_fifo;
	//wire rd_ready;   		// rd_ready - empty_n 	: signal interface data ready
	//wire rd_ack;     		// rd_ack - rd     		: pop fifo after data read

	//wire busy;				// RAM is busy because RW FIFO is full
	//assign busy = wr_full | rdaddr_full;


	//fifo #(.BUS_WIDTH(40)) dram_wr_fifo (
	//    .wr_clk        (mclk),
	//    .rd_clk        (fclk),
	//    .wr_data       (wr_fifo),
	//    .rd_data       (wro_fifo),
	//    .rd            (ctrl_busy),
	//    .wr            (wr_enable),
	//    .full          (wr_full),
	//    .empty_n       (ctrl_wr_enable),
	//    .rst_n         (rst_n)
	//);
	//
	//fifo #(.BUS_WIDTH(24)) dram_rd_addr_fifo (
	//    .wr_clk        (mclk),
	//    .rd_clk        (fclk),
	//    .wr_data       (wr_fifo[39:16]),
	//    .rd_data       (rdao_fifo),
	//    .rd            (ctrl_busy),
	//    .wr            (rd_enable),
	//    .full          (rdaddr_full),
	//    .empty_n       (ctrl_rd_enable),
	//    .rst_n         (rst_n)
	//);
	//
	//fifo #(.BUS_WIDTH(16)) dram_rd_data_fifo (
	//    .wr_clk        (fclk),
	//    .rd_clk        (mclk),
	//    .wr_data       (rddo_fifo),
	//    .rd_data       (rddata_fifo),
	//    .rd            (rd_ack),
	//    .wr            (ctrl_rd_ready),
	//    .empty_n       (rd_ready),
	//    .rst_n         (rst_n)
	//);
	
	reg busy, rd_ready, rd_en, wr_en;
	reg [1:0] state;
	reg [15:0] wr_data, rd_data, rd_data_reg;
	always_ff@(posedge fclk) begin
		if(~rst_n) begin
			state <= 0;
			wr_en <= 0;
			rd_en <= 0;
		end
		else begin
			if(rd_ready) ram_rd_data <= rd_data;
			if(mclk & (state == 0)) state <= 1; 
			//if(rd_ready) rd_data_reg <= rd_data;
			else if(state == 1 && ~busy) begin
				wr_data <= ram_wr_data;
				wr_en <= ram_wr_en;
				rd_en <= ram_rd_en;
				state <= (ram_wr_en | ram_rd_en) ? 2 : 1;
			end else if (state == 2) begin
				wr_en <= 0;
				rd_en <= 0;
				state <= 3;
			end
			if(~mclk & (state == 3))  state <= 0;
		end

	end
	
	//always_ff@(posedge mclk) begin
		//state <= 0;
		//ram_rd_data <= rd_data_reg;
	//end

	//assign ram_rd_data = rd_data_reg;

	// Setting SDRAM clock to 100MHz
	assign DRAM_CLK = fclk;
	
	`ifdef SYNTHESIS
	sdram_controller sdram_control0 (
	    // HOST INTERFACE
	    .wr_addr       (ram_addr),
	    .wr_data       (wr_data),
	    .wr_enable     (wr_en), 
	
	    .rd_addr       (ram_addr), 
	    .rd_data       (rd_data),
	    .rd_ready      (rd_ready),
	    .rd_enable     (rd_en),
	    
	    .busy          (busy),
	    .rst_n         (rst_n),
	    .clk           (fclk),
	
	    // SDRAM SIDE
	    .addr          (DRAM_ADDR),
	    .bank_addr     (DRAM_BA),
	    .data          (DRAM_DQ),
	    .clock_enable  (DRAM_CKE),
	    .cs_n          (DRAM_CS_N),
	    .ras_n         (DRAM_RAS_N),
	    .cas_n         (DRAM_CAS_N),
	    .we_n          (DRAM_WE_N),
		.data_mask_low (DRAM_DQM[0]),
		.data_mask_high(DRAM_DQM[1])
	);
	`else
	reg [15:0] rd_data0;
	memory#(.WIDTH(16), .LENGTH(2**8)) mock_sdram0(
		.clk(fclk),
		.wr_en(wr_en),
		.rd_en(rd_en),
		.wd(wr_data),
		.rd(rd_data0),
		.addr(ram_addr[7:0])
	);
	always_ff@(posedge fclk) begin
		if(~rst_n) busy <= 0;
		else begin
			rd_data <= rd_data0;
			rd_ready <= rd_en;
			busy <= rd_en | wr_en;
		end
	end
	`endif
	//// Assign inputs	
	//assign wr_fifo[39:16] 	= ram_addr;
    //assign wr_fifo[15:0] 	= ram_wr_data;
	//assign wr_enable 		= ram_wr_en;
	//assign rd_enable 		= ram_rd_en;
	//
	//// Assign outputs
	//assign ram_rd_data 		= rddata_fifo;
 	//assign ram_busy 		= busy;
	//assign ram_rd_ready 	= rd_ready;
	//assign ram_rd_ack 		= rd_ack;
 		
endmodule

/**
 * Testbench for sdram_controller modules, simulates:
 *  - Iinit
 *  - Write
 *  - Read
 */
module sdram_controller_tb();

    //vlog_tb_utils vlog_tb_utils0();

    /* HOST CONTROLLS */
    reg [23:0]  haddr;
    reg [15:0]  data_input;
    wire [15:0] data_output;
    wire busy; 
    reg rd_enable, wr_enable, rst_n, clk;

    /* SDRAM SIDE */
    wire [12:0] addr;
    wire [1:0] bank_addr;
    wire [15:0] data; 
    wire clock_enable, cs_n, ras_n, cas_n, we_n, data_mask_low, data_mask_high;

    reg [15:0] data_r;

    assign data = data_r;


    initial 
    begin
        haddr = 24'd0;
        data_input = 16'd0;
        rd_enable = 1'b0;
        wr_enable = 1'b0;
        rst_n = 1'b1;
        clk = 1'b0;
        data_r = 16'hzzzz;
    end

    always
        #1 clk <= ~clk;
      
    initial
    begin
      #3 rst_n = 1'b0;
      #3 rst_n = 1'b1;
      
      #120 haddr = 24'hfedbed;
      data_input = 16'd3333;
      
      #3 wr_enable = 1'b1;
      #6 wr_enable = 1'b0;
      haddr = 24'd0;
      data_input = 16'd0;  
      
      #120 haddr = 24'hbedfed;
      #3 rd_enable = 1'b1;
      #6 rd_enable = 1'b0;
      haddr = 24'd0;
      
      #8 data_r = 16'hbbbb;
      #2 data_r = 16'hzzzz;
      
      #1000 $finish;
    end


sdram_controller sdram_controlleri (
    /* HOST INTERFACE */
    .wr_addr(haddr), 
    .wr_data(data_input),
    .rd_data(data_output),
    .busy(busy), .rd_enable(rd_enable), .wr_enable(wr_enable), .rst_n(rst_n), .clk(clk),

    /* SDRAM SIDE */
    .addr(addr), .bank_addr(bank_addr), .data(data), .clock_enable(clock_enable), .cs_n(cs_n), .ras_n(ras_n), .cas_n(cas_n), .we_n(we_n), .data_mask_low(data_mask_low), .data_mask_high(data_mask_high)
);

endmodule
