/*
 * vga.v
 * 
 * VGA - FPGA class project
 * 
 * 
 * Authors: Dario De Micco - Marta Martelossi
 * Contact: <email???>
 * Release date: oct 2013
 * 
 * References: "Tetris" class project
 *
 * 
 * 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 2 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, write to the Free Software
 * 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * 
 * MA 02110-1301, USA.
 * 
 */


// pixel clock per la vga, 25mhz
module clk_div(clk_25, clk_50, reset);

input clk_50, reset;
output reg clk_25;

reg count;

always @ (posedge clk_50)
begin
	clk_25<=clk_25+1'b1;
end

endmodule



// modulo arbitro
// due porte
// porta A sola lettura, prioritaria, con lock 
// porta B sola scrittura


module sram_arbiter(
	clock, reset,
	sram_address, sram_data, sram_we_n, sram_oe_n, sram_ce_n,
	port_a_address, port_a_data, port_a_req, port_a_ack, lock,
	port_b_address, port_b_data, port_b_req, port_b_ack);

input clock, reset;


// segnali verso la memoria fisica
inout [15:0] sram_data;
reg [15:0] sram_data_reg;
wire [15:0] sram_data_bus;
output [17:0] sram_address;

output reg sram_we_n,sram_oe_n;
output sram_ce_n;

// segnali della porta a
output reg [15:0] port_a_data;
input [17:0] port_a_address;
input port_a_req;
output reg port_a_ack;
input lock;	

// segnali della porta b
input [15:0] port_b_data;
input [17:0] port_b_address;
input port_b_req;
output reg port_b_ack;

reg 	[17:0] sram_addr_reg;
assign sram_ce_n=1'b0;


assign sram_data = (sram_oe_n)? (sram_data_reg) :16'bZZZZZZZZZZZZZZZZ;
assign sram_address = ((port_a_req & ~port_b_ack) | port_a_ack)?(port_a_address):(sram_addr_reg);



reg [3:0] arbiter_status;

always @ (posedge clock or posedge reset)
begin
	if (reset) begin
		arbiter_status<= 0;
		sram_oe_n<=1'b1;
		port_a_ack<=1'b0;
		port_b_ack<=1'b0;
		
	end else begin
		case (arbiter_status)
			4'd0:
				begin
					// arbiter has finished processing previous request
					if (port_a_req) begin
						// req on port a (first, so it has priority)
						sram_oe_n <=1'b0;
						arbiter_status<=2;
						port_a_ack<=1'b1;
						
					end else if (port_b_req & ~lock) begin
						// if lock is not requested by portA and portA is not requesting...
						sram_we_n <=1'b0;
						sram_data_reg <= port_b_data;
						port_b_ack<=1'b1;
						sram_addr_reg <= port_b_address;
						arbiter_status<=6;
						
					end
					
				end
			
			4'd1:
				begin
					// a req
					
					arbiter_status<=2;
					
					
					
				end
			4'd2:
				begin
					// a req
					sram_oe_n <=1'b1;
					arbiter_status<=0;
					port_a_ack<=1'b0;
					port_a_data<=sram_data;
					
				end
			4'd6:
				begin
					arbiter_status<=7;
				end
			4'd7:
				begin
					// breq
					sram_we_n <=1'b1;
					port_b_ack <= 1'b0;
					arbiter_status<=0;
					
				end
		endcase	
					
	end

end


endmodule



// this module convert 4bit input (16 colors) to 12 bit output (4 bits per channel, RGB)

module palette(
	vinput, rout, bout, gout
);

input [3:0] vinput;

output wire [3:0] rout, bout, gout;

reg [11:0] colorrgb;

assign rout=colorrgb[3:0];
assign gout=colorrgb[7:4];
assign bout=colorrgb[11:8];


// standard Windows 4 bit/16 colours palette
/*
 * 0 — black 	8 — gray
 * 1 — maroon 	9 — red
 * 2 — green 	10 — lime
 * 3 — olive 	11 — yellow
 * 4 — navy 	12 — blue
 * 5 — purple 	13 — fuchsia
 * 6 — teal 	14 — aqua
 * 7 — silver 	15 — white
 */
always  @ (vinput)
	case (vinput)
		4'd00: colorrgb=12'h000;		// black
		4'd01: colorrgb=12'h008;		// dark red/maroon
		4'd02: colorrgb=12'h080;		// green
		4'd03: colorrgb=12'h088;		// dark yellow/olive
		4'd04: colorrgb=12'h800;		// dark blue
		4'd05: colorrgb=12'h808;		// violet/purple
		4'd06: colorrgb=12'h880;		// teal
		4'd07: colorrgb=12'hCCC;		// grey 25%/silver
		4'd08: colorrgb=12'h888;		// grey 50%
		4'd09: colorrgb=12'h00F;		// red
		4'd10: colorrgb=12'h0F0;		// bright green/lime
		4'd11: colorrgb=12'h0FF;		// yellow
		4'd12: colorrgb=12'hF00;		// blue
		4'd13: colorrgb=12'hF0F;		// pink/fuchsia
		4'd14: colorrgb=12'hFF0;		// turquoise/aqua
		4'd15: colorrgb=12'hFFF;		// white
		default: colorrgb=12'h000;
	endcase

endmodule


// Modulo VGA
// this module generates the timing and reads pixel data from FIFO

module vgaoutput(
	r,g,b,
	HSync,VSync,
	sram_address, sram_data, sram_readreq, sram_readack, fifo_warning,
	bufselect, calibrate,
	clock, fast_clock, reset);

	
input clock, fast_clock, reset;
output reg [3:0] r, g, b;

input bufselect, calibrate;

output reg HSync,VSync;

reg [9:0] Hcount, Vcount;			// Contatori per il pixel attivo
wire VideoEnabledH, VideoEnabledV;	// Linea/colonna, se abilitata
wire VideoEnabled;					// Zona abilitata del video
wire VideoEnabled2;					// Zona abilitata del video

input [15:0] sram_data;
output reg [17:0] sram_address;

output reg sram_readreq;
input sram_readack;
output fifo_warning;

reg [18:0] contapixel;

wire [3:0] rout, gout, bout;

	
wire fifo_readreq;
wire fifo_readempty;
wire [15:0] fifo_data;
reg fifo_writereq;
reg [15:0] data_from_fb;
wire fifo_writefull;
assign fifo_readreq=(~fifo_readempty)&VideoEnabled2&((pixel_clock==2'b01)?1'b1:1'b0)&(clock);
//wire VideoEnabled2;
reg [1:0] pixel_clock;
wire fifo_reset;

reg fifo_reset_req;

assign fifo_reset = fifo_reset_req | reset;

reg [1:0] fifo_byte;

wire [7:0] paletteinput;

// process thru palette data from fifo (with byte select)
assign paletteinput=(fifo_byte[1])?fifo_data[15:8]:fifo_data[7:0];
palette	pal0 (.vinput((fifo_byte[0])?paletteinput[7:4]:paletteinput[3:0]),.rout(rout),.gout(gout),.bout(bout));


fifovga fifo_per_vga(
	.data(data_from_fb),
	.rdreq(fifo_readreq),
	.clock(fast_clock),
	.wrreq(fifo_writereq),
	.q(fifo_data),
	.almost_empty(fifo_warning),
	.almost_full(fifo_writefull),
	.aclr(fifo_reset),
	.empty(fifo_readempty),
	.full());


reg [1:0] fsm_framebuffer;

//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// Assegnamento continuo:
//===============================================================
// Video enable:
// Scrittura su video abilitata da 0 a 639 pixel Orizz., idem per il verticale da 0 a 480
assign VideoEnabledH = ((Hcount >= (48)) && Hcount < (48+640))?1'b1:1'b0; // if in linea ---> risultato=(condizione)?(severo):(sefalso)
assign VideoEnabledV = ((Vcount >= (10)) && Vcount < (10+480))?1'b1:1'b0; 

//===============================================================
assign VideoEnabled = VideoEnabledH & VideoEnabledV;

assign VideoEnabled2 = ((Hcount >= (47)) && Hcount < (47+640) && (Vcount >= (10)) && Vcount < (10+480))?1'b1:1'b0; 
//===============================================================


always @(posedge clock or posedge reset)
begin
	if (reset)
	begin
		pixel_clock<=0;
	end else if (VideoEnabled2) begin
		pixel_clock<=pixel_clock + 2'd1;	// pixelclock
	
	end
end
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// Blocco che genera i timing per la vga
always @(posedge clock or posedge reset)
begin
	if (reset)
	begin
		HSync<=1'b1;
		VSync<=1'b1;
		Hcount<=10'd0;
		Vcount<=10'd0;
		
		
	end 	else	begin

		//===============================================================
		// Sincronismo orizzontale:
		// Incr. contatore del pixel attivo sulla linea:
		if(Hcount < 799) Hcount <= Hcount + 1'b1;	// 798 e' la somma di tutti i pixel reali o virtuali 
		else if (Hcount == 799) Hcount <= 10'd0;
		// Def. zona di Sincronia Orizzontale HS => linea bassa:
		if(Hcount >= (799-95))	// 95 pixel di HS. 
			HSync <=1'b0;
		else HSync <= 1'b1;
		//===============================================================

		//===============================================================
		// Sincronismo verticale:
		// Incr. contatore del pixel attivo sul frame:
		if(Vcount >= 524 && Hcount >= (799-95)) begin	// In fondo al frame, e nella zona HS,... Vcount > 524 && Hcount >= 699
			Vcount <= 10'd0;		// ...bisogna resettare Vcount. torna con la y a inizio schermo
			fifo_reset_req <= 1'b1;
		end else begin
			fifo_reset_req <= 1'b0;
			if(Hcount == (799-95)) Vcount <= Vcount + 1'b1;	// A fine riga, si inizia la riga dopo. Hcount == 699
		end
		
		// Def. zona di Sincronia Verticale VS => linea bassa:
		if(Vcount >= (524-2))	// 2 pixel. 523 e 524
			VSync <= 1'b0;
		else VSync <= 1'b1;
		//===============================================================
		
		
	end
end

// pixel data output
always @ (posedge clock or posedge reset)
begin
	if (reset) begin
		r<=4'h0;
		g<=4'h0;
		b<=4'h0;
		fifo_byte<=0;
	end else if (VideoEnabled) begin
		
		fifo_byte=fifo_byte+2'd1;	// toggles upper or lower byte
		//video_data<=fifo_data;
		if (calibrate) begin
			
			r<=4'hF;
			g<=4'hF;
			b<=4'hF;
		end else begin
			r<=rout;
			g<=gout;
			b<=bout;
		end
	end else if  (VideoEnabled == 1'b0) begin
		r<=4'h0;
		g<=4'h0;
		b<=4'h0;
	end 
	
end


// reads data from sram and copy it to fifo (if not full)
// we can do it in one cycle!
always @ (posedge fast_clock or posedge fifo_reset)
begin
	if (fifo_reset) begin
		fsm_framebuffer <= 2'd0;
		sram_address<=((bufselect)?18'd76800:18'd0)+18'd1;
		sram_readreq<=1'b0;
	end else begin
		case (fsm_framebuffer)
			2'd0:
				begin
					if (~fifo_writefull & ~sram_readack) begin
						sram_address<=((bufselect)?18'd76800:18'd0)+18'd1;
						//fsm_framebuffer<=2'd2;
						sram_readreq<=1'b1;
					end else
						sram_readreq<=1'b0;
					fsm_framebuffer<=2'd2;
				end
			
			2'd1:
				begin

					
				
					data_from_fb<=sram_data;
					if (~fifo_writefull & ~sram_readack)	begin				
						fifo_writereq<=1'b1;
						if (sram_address!=18'd76799) begin 
							sram_address<=sram_address+18'd1;
							sram_readreq<=1'b1;
						end else
							sram_readreq<=1'b0;
						fsm_framebuffer<=2'd2;
					end
				end
			2'd2:
				begin
					
					
					if (sram_readack) begin
						sram_readreq<=1'b0;
						fsm_framebuffer<=2'd1;
					end
					fifo_writereq<=1'b0;
					
				end

		endcase
	end
end

endmodule
