/*
 Copyright (C) 2016 Cedric Orban

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 /*
 
 This module instantiates all the logic necessary to form tiles
 and pipelines them using block RAM. In addition, it breaks out an
 interface for a module higher in the design hierarchy.
 
*/

`include "DeepGATE_include.v"

module tile_network (

input	clk_i,
input en_i,							//active high, initiates processing
input [7:0] data_i,				//input data
input data_rd_i,					//active high, signals reading of network output data
output wire data_rd_o,			//active high, signals the module is reading data_i
output wire network_idle_o,	//active high if ready to accept input
output wire [7:0] data_o,		//network output data
output wire proc_complete_o,	//active high, indicates processing is complete

input [7:0] weight_data_i,
input [NUM_TILES - 1'b1 : 0] weight_wr_i,
output wire [NUM_TILES - 1'b1 : 0] pipeline_lock_o

);

`include "param_include.v"
 
genvar i,j;
generate

	for(i = 0; i < NUM_TILES; i = i + 1) begin: TILES
	 
		parameter [31:0] TILE_SIZE  		= TILE_SIZE_ARRAY[`ID32(i)];
		parameter [31:0] TILE_LOOP   		= TILE_LOOP_ARRAY[`ID32(i)];
		parameter [31:0] TILE_PRV_SIZE  	= TILE_PRV_SIZE_ARRAY[`ID32(i)];
		parameter [31:0] TILE_PRV_LOOP  	= TILE_PRV_LOOP_ARRAY[`ID32(i)];
		parameter [31:0] TILE_NXT_LOOP	= TILE_NXT_LOOP_ARRAY[`ID32(i)];
		parameter [31:0] TILE_BIAS 		= TILE_BIAS_ARRAY[`ID32(i)];
		 
		wire [TILE_SIZE*WEIGHT_WIDTH - 1'b1:0] weight_bus;
		  
		wire en;
		wire ram_wr;
		wire ram_rd;
		wire proc_unit_rst;
		wire ram_full;
		wire data_available;
		wire [7:0] tile_data;
		wire [7:0] buffer_data;
		wire [$clog2(TILE_SIZE) - 1'b1:0] sig_mux_sel;

		//if first tile, connect idle and route data_available to module port
		if(i == 0) begin

			tile_control #(
			
				.SIZE				(TILE_SIZE), 
				.LOOP				(TILE_LOOP), 
				.PRV_SIZE		(TILE_PRV_SIZE), 
				.PRV_LOOP		(TILE_PRV_LOOP)
				
			) 

			TILE_CONTROL(
				
				.clk_i				(clk_i),
				.rst_i				(1'd0),
				.data_available_i	(en_i),
				.ram_full_i     	(ram_full),
				.pipeline_lock_i	(pipeline_lock_o[i]),
				.en_o     			(en),
				.ram_wr_o    		(ram_wr),
				.idle_o    			(network_idle_o),
				.ram_rd_o    		(ram_rd),
				.proc_unit_rst_o  (proc_unit_rst),
				.sig_mux_sel_o		(sig_mux_sel)

			);
		  
		end
		
		else begin

			tile_control #(
			
				.SIZE				(TILE_SIZE), 
				.LOOP				(TILE_LOOP), 
				.PRV_SIZE		(TILE_PRV_SIZE), 
				.PRV_LOOP		(TILE_PRV_LOOP)
				
			) 

			TILE_CONTROL(

				.clk_i				(clk_i),
				.rst_i				(1'b0),
				.data_available_i	(TILES[PREV_INDEX[`ID32(i)]].data_available),
				.ram_full_i     	(ram_full),
				.pipeline_lock_i	(pipeline_lock_o[i]),
				.en_o     			(en),
				.ram_wr_o    		(ram_wr),
				.ram_rd_o    		(ram_rd),
				.proc_unit_rst_o  (proc_unit_rst),
				.sig_mux_sel_o		(sig_mux_sel)

			);
		  
		end

		tile_datapath #(
		
			.SIZE					(TILE_SIZE), 
			.LOOP					(TILE_LOOP), 
			.PRV_SIZE			(TILE_PRV_SIZE), 
			.PRV_LOOP			(TILE_PRV_LOOP), 
			.BIAS					(TILE_BIAS), 
			.WEIGHT_WIDTH		(WEIGHT_WIDTH)
		
		)
		  
		TILE_DATA(

			.clk_i   			(clk_i),
			.rst_i				(proc_unit_rst),
			.en_i  				(en),
			.weight_bus_i 		(weight_bus),
			.sig_mux_sel_i		(sig_mux_sel),
			.data_i  			(i == 0 ? data_i[`ID8(0)] : TILES[PREV_INDEX[`ID32(i)]].buffer_data), //simplifies to TILES[i-1].buffer_data
			.data_o  			(tile_data)

		);
		
		weight_RAM #(

			.SIZE					(TILE_SIZE_ARRAY[`ID32(i)]), 
			.MAX_DEPTH			(MEM_MAX_DEPTH_ARRAY[`ID32(i)]),
			.DIVIDER				(MEM_DIVIDER_ARRAY[`ID32(i)]),
			.WEIGHT_WIDTH		(WEIGHT_WIDTH)
			
		)
				  
		weight_RAM(
		  
			.clk_i				(clk_i),
			.rd_i					(ram_rd),
			.wr_i					(weight_wr_i[i]),
			.data_i				(weight_data_i),
			.pipeline_lock_o	(pipeline_lock_o[i]),
			.data_o				(weight_bus)
		  
		);
		
		if(i != NUM_TILES - 1'b1) begin //connect read signal differently for last layer
		  
			parameterized_RAM #(
			
				.SIZE					(TILE_SIZE), 
				.LOOP					(TILE_LOOP), 
				.NXT_LOOP			(TILE_NXT_LOOP)
			
			)

			TILE_BUFFER(

				.clk_i					(clk_i),
				.rd_i    				(TILES[NEXT_INDEX[`ID32(i)]].ram_rd),
				.wr_i   					(ram_wr),
				.data_i	   			(tile_data),
				.data_o   				(buffer_data),
				.ram_full_o   			(ram_full),
				.data_available_o		(data_available)
			  
			);
			
		end
		
		else begin
		
			parameterized_RAM_last #(
			
				.SIZE					(TILE_SIZE), 
				.LOOP					(TILE_LOOP)
				
			)

			TILE_BUFFER(

				.clk_i				(clk_i),
				.rd_i    			(data_rd_i),
				.wr_i   				(ram_wr),
				.data_i   			(tile_data),
				.data_o   			(data_o),
				.ram_full_o   		(ram_full),
				.data_available_o	(proc_complete_o)
			  
			);
		
			/*
			output_arbiter #(
			
				.SIZE					(TILE_SIZE), 
				.LOOP					(TILE_LOOP)
			
			)
			
			outputBuffer(
				
				.clk					(clk_i),
				.read    			(data_rd_i),
				.write   			(transfer),
				.data_i   			(activatedData),
				.data_o   			(networkOut),
				.ramFull   			(ramFull),
				.dataAvailable		(proc_complete_o)
			
			);
			*/
		
		end
	 
	end
 
endgenerate

assign data_rd_o = TILES[0].ram_rd;

endmodule
