`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    09:25:36 11/21/2006 
// Design Name: 
// Module Name:    main 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////





module main(clk, sevseg, anod, ps2d, ps2c, r, g, b, hs, vs, sw, btn, ld,
				addr_sr,
				data_io_0_sr, data_io_1_sr,
				ce_0_sr, ce_1_sr,
				oe_sr,
				we_sr,
				ub_0_sr, lb_0_sr,
				ub_1_sr, lb_1_sr,
				rxd, txd,
				rst, clk_aux );

// input output declarations


input clk;
input rst;

input clk_aux;

// rs232 receive data and transfer data

input rxd;
output txd;

// 7-segment led block

output[7:0] sevseg;
output[3:0] anod;


// ps/2 input

input ps2d;

input ps2c;

// vga output

output r, g, b;

output hs, vs;

// button input

input[7:0] sw;
input[2:0] btn;

// led row

output[7:0] ld;

// sram interface section of input output;

output[17:0] addr_sr;

inout[15:0] data_io_0_sr;
inout[15:0] data_io_1_sr;

output ce_0_sr, ce_1_sr;
output oe_sr;
output we_sr;

output ub_0_sr, lb_0_sr;
output ub_1_sr, lb_1_sr;


// declaring registers and assigns

// leds (ld) and seven-segmend led (mastercnt)

reg[7:0] ld;

reg[15:0] mastercnt;

wire[15:0] mwire;

assign mwire = mastercnt;


// the buttons

wire[2:0] btnl;

wire btnc0, btnc1, btnc2;

reg btnc;

always @(posedge clk)
	btnc = btnc0 | btnc1 | btnc2;

wire btnu0, btnu1, btnu2;


// the vga clocks

wire clk1;  // clk1 is input to vgacontrol, output by takt0
wire clk2;  // clk2 is output from vgacontrol, derived from input clk1

wire locked;


// clk cpu

wire clk_cpu;

// the character memory of the vga card


// the memory-adaptor

wire[1:0] word_type;
assign word_type = 2'b00;


wire[23:0] vga_addrb;
wire[31:0] vga_din;

wire vga_en;

wire[31:0] addr_b_vga = {8'b0, vga_addrb};

wire enb_vga = vga_en;

wire[31:0] din_mem_b_vga;
wire[31:0] addr_mem_b_vga;
wire[3:0] enmem_b_vga;


/*
mem_adaptor_be_r memadap_read_b (.doutbus(vga_din), .abus(addr_b_vga),
								.enbus(enb_vga), .ssrbus (1'b0), .read_ack (rack_vga),
								.dinmem(din_mem_b_vga), .amem (addr_mem_b_vga),
								.enmem(enmem_b_vga),
								.type(word_type), .clk(clk2));
*/
assign vga_din = {din_mem_b_vga[7:0],din_mem_b_vga[15:8],din_mem_b_vga[23:16],din_mem_b_vga[31:24]};
assign addr_mem_b_vga = {8'b0,vga_addrb};
assign enmem_b_vga = {4{vga_en}};


//the vga controller

vgacontrol vgacontrol0 ( /*.auxin({8'b0,sw[7:0]}),*/ .en(vga_en), .addrout(vga_addrb), .datin(vga_din), 
							.r(r), .g(g), .b(b), .hso(hs), .vso(vs), .clk(clk), .clk2(clk2), .rst(rst) ); 




// the three buttons
				
//debouncer debounce0 ( .btnin(btn[0]), .bstate(btnl[0]), .btndown(btnc0), .btnup(btnu0), .clk(clk_cpu), .rst(rst));				
//debouncer debounce1 ( .btnin(btn[1]), .bstate(btnl[1]), .btndown(btnc1), .btnup(btnu1), .clk(clk_cpu), .rst(rst));				
debouncer debounce2 ( .btnin(btn[2]), .bstate(btnl[2]), .btndown(btnc2), .btnup(btnu2), .clk(clk_cpu), .rst(rst));				




// the cpu



reg[2:0] irq_aux;

initial begin
	irq_aux = 3'b0;
end

wire clk_sram;

wire clk_cpu_na;

takt takt0 (.clk(clk), .clk1(clk1), .clk_cpu(clk_cpu_na), .clk_sram(clk_sram), .locked(locked), .rst(rst) );


wire rst_sec; // secondary reset of cpu


reg r1;

reg rst_cpu;


always @(posedge clk_cpu or posedge rst)
	begin
		if (rst)
			r1 <= 1'b1;
		else
			r1 <= 1'b0;
	end
	
always @(posedge clk_cpu or posedge rst)
	begin
		if (rst)
			rst_cpu <= 1'b1;
		else
			rst_cpu <= r1;
	end
	
// the switches

reg[7:0] swr;

always @(posedge clk_cpu or posedge rst)
	begin
		if (rst)
			swr = 8'b0;
		else
			swr = sw;
	end





// wait logic	

/*
reg[3:0] wait_ext_sync;

initial begin
	wait_ext_sync = 4'b0;
end

always @(posedge clk_cpu)
	wait_ext_sync = {wait_ext_sync[2:0], sw[4]}; 
*/	
	
wire waitcpu_ext = swr[4];



		wire[31:0] debugout;
		wire[31:0] tosout;


		wire waitcpu_r = 0;
		wire waitcpu_w = 0;

		wire waitcpu = waitcpu_r | waitcpu_w | waitcpu_ext;

		wire rdack;
		wire wrtack;
	
		wire rdack_A;
		wire rdack_B;
		
		wire wrtack_A;
		wire wrtack_B;
		
		assign rdack = rdack_A | rdack_B;
		assign wrtack = wrtack_A | wrtack_B;
	
		wire sel_A;
		wire sel_B;
		wire sel_C;
		wire sel_D;
	
		wire[31:0] addr;
		wire[31:0] datain;
		wire[31:0] dataout;

		wire readmem;
		wire writemem;

		wire[2:0] irq_cpu;

		wire[1:0] type;
		
	
		cpu cpu0 (
        .addr(addr),
        .datain(datain),
		  .dataout(dataout),
        .readmem(readmem),
        .writemem(writemem),
				.waitrq(waitcpu),
        .irq(irq_aux),
        .type(type),
        .rst(rst_cpu),
        .clk(clk_cpu),
		  .tosout(tosout),
		  .debugout(debugout));
				
	

		
		wire[31:0] data_b_w;
		wire[31:0] data_b_r;

		wire[3:0] ssrmem;
		wire[3:0] ssrmem_w;

		wire[3:0] enmem;
		wire[3:0] enmem1;
			
		wire readmem_only = readmem & ~writemem;
		wire writemem_only = writemem & ~readmem;
			
		// assign data_b_w = (writemem_only & ~rdack_A) ? data : 32'bz;
		
		// assign data = rdack_A ? data_b_r : 32'bz;
		
		assign data_b_w = dataout;
		assign datain = data_b_r;
		
		wire[31:0] amem_w;
		wire[31:0] amem_r;

		wire[3:0] wmem;

		// bus section

		wire[3:0] enmem_bus;
		wire[3:0] wmem_bus;
		
		wire[31:0] data_o_bus;
		wire[31:0] data_i_bus;
				
		wire[31:0] addr_bus;



		mem_adaptor_be_w maw0_A (.dinbus(data_b_w), .abus(addr), .enbus(writemem_only), .webus(writemem_only),
														.ssrbus(1'b0), // .write_ack(wrtack_A),
												.doutmem(data_o_bus), .amem(amem_w), .enmem(enmem1),
													.wemem(wmem), .ssrmem(ssrmem_w), .type(type) ); //, .clk(clk));
		
		mem_adaptor_be_r mar0_A (.doutbus(data_b_r), .abus(addr), .enbus(readmem_only),
														.ssrbus(1'b0), .read_ack (rdack_A),
												.dinmem(data_i_bus), .amem(amem_r), .enmem(enmem),
													.ssrmem(ssrmem), .type(type), .clk(clk_cpu));



		// bus assigns	
		
		
		// bank selects

		wire[15:0] sel_regs;
		
		reg[3:0] sel_C_reg;
		reg[3:0] sel_A_reg;
		
		wire switch_A_C = 0;

/*		
		initial begin
			sel_A_reg = 4'h0;
			sel_C_reg = 4'hd;
		end
*/		

		always @(posedge clk_cpu or posedge rst_cpu)
			begin
				if (rst_cpu)
					begin
						if (~(swr[5]))
							begin
								sel_C_reg = 4'hd;
								sel_A_reg = 4'h0;
							end
						else
							begin
								sel_C_reg = 4'h0;
								sel_A_reg = 4'hd;
							end
					end
				else
					begin
						sel_C_reg = sel_C_reg;
						sel_A_reg = sel_A_reg;
					end
			end
		
		wire sel_A_1, sel_B_1, sel_C_1, sel_D_1;
		
		assign sel_D_1 = (addr_bus[31:28] == 4'he);
		assign sel_C_1 = (addr_bus[31:28] == sel_C_reg);
		assign sel_B_1 = (addr_bus[31:28] == 4'hf);
		assign sel_A_1 = (addr_bus[31:28] == sel_A_reg);
	
		assign sel_A = sel_A_1 & ~sel_B_1 & ~sel_C_1 & ~sel_D_1;
		assign sel_B = ~sel_A_1 & sel_B_1 & ~sel_C_1 & ~sel_D_1;
		assign sel_C = ~sel_A_1 & ~sel_B_1 & sel_C_1 & ~sel_D_1;
		assign sel_D = ~sel_A_1 & ~sel_B_1 & ~sel_C_1 & sel_D_1;
		
	
		assign enmem_bus = enmem | enmem1;
		assign wmem_bus = wmem;
	
		assign addr_bus = (|wmem_bus) ? amem_w : amem_r;


		wire rd_last_A;
		wire rd_last_B;
		wire rd_last_C;
		wire rd_last_D;
		
		
		
		// A block assigns
		
		wire[3:0] enmem_A	= enmem_bus & {4{sel_A}};
		wire[3:0] wmem_A = wmem_bus & {4{sel_A}};

		wire[31:0] datamem_A;
		
		assign datamem_A = ((|wmem_A) & (|enmem_A) & (~rd_last_A)) ? data_o_bus : 32'bz;
		
		
		delay delay_A ( .I((|enmem_A) & (~(|wmem_A))), .O(rd_last_A), .CLK(clk_cpu) );

	
		wire[31:0] dataout_A;
	
		mem_sync_CPU main_mem_A (.addr(addr_bus), .data(datamem_A), .dataout(dataout_A), .en(enmem_A), .wen(wmem_A), 
											.clk(clk_cpu), .rst(rst_cpu));




		// B block assigns

		wire[3:0] enmem_B = enmem_bus & {4{sel_B}};
		wire[3:0] wmem_B = wmem_bus & {4{sel_B}};

		wire[31:0] datamem_B;
		wire[31:0] dataout_B;
		
		assign datamem_B = ((|wmem_B) & (|enmem_B) & (~rd_last_B)) ? data_o_bus : 32'bz;
		
		
		delay delay_B ( .I((|enmem_B) & (~(|wmem_B))), .O(rd_last_B), .CLK(clk_cpu) );



		mem_sync_VGA main_mem_B ( .ena (enmem_B), .dia (datamem_B), .doa(dataout_B), .addra(addr_bus[14:0]), 
								.wea(wmem_B), .ssra(4'b0),
							.enb(enmem_b_vga), .dob(din_mem_b_vga), .addrb(addr_mem_b_vga[14:0]), 
							.clka(clk_cpu), .clkb(clk2));


	// C block assigns
	
		wire[3:0] enmem_C = enmem_bus & {4{sel_C}};
		wire[3:0] wmem_C = wmem_bus & {4{sel_C}};

		wire[31:0] datamem_C;
		
		assign datamem_C = ((|wmem_C) & (|enmem_C) & (~rd_last_C)) ? data_o_bus : 32'bz;
		
		
		delay delay_C ( .I((|enmem_C) & (~(|wmem_C))), .O(rd_last_C), .CLK(clk_cpu) );


	// D block assigns
	
		wire[3:0] enmem_D = enmem_bus & {4{sel_D}};
		wire[3:0] wmem_D = wmem_bus & {4{sel_D}};

		wire[31:0] datamem_D;
		
		
		assign datamem_D = ((|wmem_D) & (|enmem_D) & (~rd_last_D)) ? data_o_bus : 32'bz;
		
		
		wire rd_D;
		
		assign rd_D = (|enmem_D) & ~(|wmem_D);
		
		delay delay_D ( .I(rd_D), .O(rd_last_D), .CLK(clk_cpu) );

				
		wire[31:0] dataout_D;
		
		reg[7:0] outdata_D;
		
		assign dataout_D = {outdata_D, 8'b0, 8'b0, 8'b0};


/*
		rst_generator rst_gen0 ( .enmem(enmem_D), .wmem(wmem_D), .addr(addr_bus), 
																.rst_sec(rst_sec), .clk_cpu(clk_cpu), .clk_sram(clk_sram), .rst(rst_cpu) );
																
		bank_sel_switch select_mem0 ( .enmem(enmem_D), .wmem(wmem_D), .addr(addr_bus), .data(datamem_D), 
																.sel_regs(sel_regs), .clk_cpu(clk_cpu), .rst(rst_cpu) ); 

*/



		// the PS/2 controller

		wire[7:0] ps2data;
		wire ps2irq;
		
		ps2control ps2control0 ( .ps2c (ps2c), .ps2d (ps2d), .data (ps2data), .rdy(ps2irq), .clk(clk_cpu), .rst(rst_cpu));
		
				
		wire sel_keyb_contr;
		assign sel_keyb_contr = rd_D & (addr_bus[9:0] == 10'h8);



// the LED block


		reg[11:0] sevseg_ctrl;

		initial begin
			sevseg_ctrl = 12'b0000_1101_1100;
		end
		
		assign sevseg[7:0] = sevseg_ctrl[7:0];
		assign anod[3:0] = sevseg_ctrl[11:8];
		
		
		wire sel_sevsegled_reg;
		assign sel_sevsegled_reg = (|enmem_D) & (|wmem_D) & (addr_bus[9:0] == 10'hC);
		
		always @(posedge clk_cpu or posedge rst_cpu)
			begin
				if (rst_cpu)
					begin
						sevseg_ctrl <= 12'b0000_1101_1100;
					end
				else
					begin
						if (sel_sevsegled_reg)
							begin
								sevseg_ctrl <= {datamem_D[19:16], datamem_D[31:24]};
							end
					end
			end

// the rs232 receiver

		wire rxd_rcv_irq;
		wire[7:0] rxd_data;
		wire rxd_eofp;
		wire rxd_idle;

		async_receiver asr0 (.clk(clk_cpu), .rst (rst_cpu),
													.RxD(rxd), .RxD_data_ready(rxd_rcv_irq), 
													.RxD_data(rxd_data), 
													.RxD_endofpacket(rxd_eofp), .RxD_idle(rxd_idle));


		wire sel_rxd_rcv;
		assign sel_rxd_rcv = rd_D & (addr_bus[9:0] == 10'h10);


// the rs232 transmitter

		wire sel_txd_snd;
		assign sel_txd_snd = (|enmem_D) & (|wmem_D) & (addr_bus[9:0] == 10'h14);

		wire txd_busy;

		async_transmitter ast0 (.clk(clk_cpu), .rst(rst_cpu), .TxD_start(sel_txd_snd & ~txd_busy),
											.TxD_data(datamem_D[7:0]), .TxD(txd), .TxD_busy(txd_busy));






//		assign txd = 1'b1;

		// the main irq generating loop for all bank D devices

		wire no_irq;
		assign no_irq = ~(|irq_aux);

		always @(posedge clk_cpu or posedge rst_cpu)
			begin
				if (rst_cpu)
					begin
						irq_aux <= 3'b0;
					end
				else
					begin
						if (ps2irq & no_irq)
							begin
//							$display ("irq received ps2data = %h", ps2data );

								irq_aux <= 3'b1 & {1'b0,1'b0,swr[3]};
								outdata_D <= ps2data;
							end
						else if (rxd_rcv_irq & no_irq)
							begin
								$display ("irq received rxd_data = %h", rxd_data );

								irq_aux <= 3'b1 & {1'b0,1'b0,swr[7]};
								outdata_D <= rxd_data;
							end
						else if ((|irq_aux) & (sel_keyb_contr | sel_rxd_rcv))
							begin
								$display ( "irq resetted." );
								irq_aux <= 3'b0;
							end
					end
			end





// sram


wire[19:0] addrmem_sr = addr_bus[19:0];
wire[31:0] datain_sr = datamem_C;
wire[31:0] dataout_sr;


wire[3:0] en_sr = enmem_C;
wire[3:0] wen_sr = wmem_C;

/*
wire[17:0] addr_sr;

wire[15:0] data_io_0_sr;
wire[15:0] data_io_1_sr;

wire ce_0_sr, ce_1_sr;
wire oe_sr;
wire we_sr;

wire ub_0_sr, lb_0_sr;
wire ub_1_sr, lb_1_sr;
*/

		sram_block sram0 (.amem (addrmem_sr), .dmem_in(datain_sr), .dmem_out(dataout_sr), .en(en_sr), .wen(wen_sr),
								.addr_sr(addr_sr),
								.data_io_0(data_io_0_sr), .data_io_1(data_io_1_sr), 
								.ce_0(ce_0_sr), .ce_1(ce_1_sr), 
								.oe(oe_sr), 
								.we(we_sr), 
								.ub_0(ub_0_sr), .lb_0(lb_0_sr), 
								.ub_1(ub_1_sr), .lb_1(lb_1_sr),
									.clk_cpu(clk_cpu), .clk_sram ( clk_sram ), 
									.rst (rst) );



		// the read bus connect

		reg[31:0] data_i_bus_r;

		always @(rd_last_A, rd_last_B, rd_last_C, dataout_A, rd_last_D, dataout_sr, dataout_D)
			begin
				if (rd_last_A)
					data_i_bus_r = dataout_A;
				else if (rd_last_B)
					data_i_bus_r = dataout_B;
				else if (rd_last_C)
					data_i_bus_r = dataout_sr;
				else if (rd_last_D)
					data_i_bus_r = dataout_D;
				else
					data_i_bus_r = 32'bz;
			end
			
		assign data_i_bus = data_i_bus_r; 




// controls the leds

always @(posedge clk or posedge rst)
	begin
		if (rst)
			ld <= 8'h00;
		else
			//ld <= ps2data & (~sw);
			if (~swr[6])
				ld <= {debugout[7:0]};
			else
				ld <= {~txd_busy, txd, 6'b0};
	end


// sets mastercnt for debugging output



always @(posedge clk or posedge rst)
	begin
		if (rst)
			begin
				mastercnt <= 800 - 1;
			end
		else
			begin
				if (swr[7]) begin
					if (swr[6])
						mastercnt <= tosout[31:16];
					else
						mastercnt <= tosout[15:0];
				end else begin
//					if (sw[5])
//						mastercnt <= {12'b0,{4{locked}}};
//					else
//						mastercnt <= debugout[15:0];
				end		
			end
	end



endmodule


// a simple delay

module delay ( I, O, CLK );

input I;

output O;
reg O;

input CLK;

always @(posedge CLK)
	O <= I;
	
endmodule




//

/*

module bank_sel_switch ( enmem, wmem, addr, data, sel_regs, clk_cpu, rst ); 

	input[3:0] enmem;
	input[3:0] wmem;
	input[31:0] addr;
	input[31:0] data;


	output[15:0] sel_regs;
	reg[15:0] sel_regs;
	
	input clk_cpu;
	input rst;
	
	always @(posedge clk_cpu or posedge rst)
		begin
			if (rst)
				begin
					sel_regs = 16'hedf0;
				end
			else
				begin
					if (|enmem & |wmem & (addr[9:0] == 10'h4))
						begin
							sel_regs = data[15:0];
						end
				end
		end
	
endmodule

*/

// the reset generator

/*
module rst_generator ( enmem, wmem, addr, rst_sec, clk_cpu, clk_sram, rst ); 

	input[3:0] enmem;
	input[3:0] wmem;
	input[31:0] addr;

	output rst_sec;

	reg rst_sec;

	input clk_cpu;
	input clk_sram;
	input rst;


	wire reset_asked;

	always @(posedge clk_cpu or posedge rst)
		begin
			if (rst | rst_sec)
				begin
					reset_asked = 1'b0;
//					$display ( "reset asked zeroed = %h ", reset_asked );
//					$stop;
				end
			else
				begin
					if (|enmem & |wmem & (addr[9:0] == 10'b0))
						begin
							reset_asked = 1'b1;
//							$display ( "reset asked set = %h ", reset_asked );
//							$stop;
						end
					else
						reset_asked = reset_asked;
				end
		end
		
	reg clk_cpu_old;

	always @(posedge clk_sram)
		begin
			clk_cpu_old <= clk_cpu;
		end

	wire sram_cyc_1 = clk_cpu & ~clk_cpu_old;


	reg[6:0] res_sec_cnt;

	reg[6:0] res_sec_cnt_old;

	always @(posedge clk_sram or posedge rst)
		begin
			if (rst)
				begin
					res_sec_cnt = 0;
					rst_sec = 1'b0;
				end
			else
				begin
					if (sram_cyc_1 & reset_asked & (res_sec_cnt == 0))
						begin
							res_sec_cnt = 6;
							rst_sec = 1'b1;
						end
					else if (res_sec_cnt != 0)
						begin
//							$display ( "res_sec_cnt = %h sram_cyc_1 = %h", res_sec_cnt, sram_cyc_1 );
							res_sec_cnt = res_sec_cnt - 1;
							rst_sec = 1'b1;
						end
					else
						begin
							if (rst_sec)
//								$display ("sram_cyc_1 = %h clk_sram = %h reset_asked = %h", sram_cyc_1, clk_sram, reset_asked);

							rst_sec = 1'b0;
						end
					res_sec_cnt_old = res_sec_cnt;
				end
		end

endmodule
*/


module ps2control ( ps2c, ps2d, data, rdy, clk, rst);

input ps2c;
input ps2d;

output data;
output rdy;
reg rdy;

input clk, rst;

reg[10:0] psreg;

reg[7:0] data;

reg[1:0] psc;

always @(posedge clk or posedge rst)
	begin
		if (rst)
			psc <= 2'b11;
		else
			psc <= {psc[0], ps2c};
	end
	
wire ps0 = psreg[0];	
	
always @(posedge clk or posedge rst)
	begin
		if (rst)
			begin
				psreg <= 11'h7ff;
				rdy <= 1'b0;
			end
		else
			begin
				if ((psc[1] & ~psc[0]) & ps0)
					begin
//						$display ( "clock edge received. ");
						psreg <= {ps2d, psreg[10:1]};
						rdy <= 1'b0;
					end
				else if (~ps0)
					begin
						data <= psreg[8:1];
//						$display ("key data received = %h", psreg[8:1] );
						psreg <= 11'h7ff;
						rdy <= 1'b1;
					end
				else
					rdy <= 1'b0;				
			end
	end

endmodule

/*
module to_ascii_rom (ps2data, res1);

input[7:0] ps2data;
output[7:0] res1;

reg[7:0] res1;
reg[7:0] res;

always @(res)
	begin
		case (res)
			8'd32: res1 <= res;
			8'hff: res1 <= res;
			default: res1 <= res + 8'd32;
		endcase
	end


always @(ps2data)
begin
case (ps2data)
//a b c d e
8'h1C:res=8'd65;
8'h32:res=8'd66;
8'h21:res=8'd67;
8'h23:res=8'd68;
8'h24:res=8'd69;
//f g h i j
8'h2B:res=8'd70;
8'h34:res=8'd71;
8'h33:res=8'd72;
8'h43:res=8'd73;
8'h3B:res=8'd74;
//k l m n o
8'h42:res=8'd75;
8'h4B:res=8'd76;
8'h3A:res=8'd77;
8'h31:res=8'd78;
8'h44:res=8'd79;
//p q r s t
8'h4D:res=8'd80;
8'h15:res=8'd81;
8'h2D:res=8'd82;
8'h1B:res=8'd83;
8'h2C:res=8'd84;
//u v w x y
8'h3C:res=8'd85;
8'h2A:res=8'd86;
8'h1D:res=8'd87;
8'h22:res=8'd88;
8'h35:res=8'd90;
//z
8'h1A:res=8'd89;
//space
8'h29:res=8'd32;
default: res=8'hFF;
endcase
end
endmodule
*/
