////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 1995-2003 Xilinx, Inc.
// All Right Reserved.
////////////////////////////////////////////////////////////////////////////////
//   ____  ____ 
//  /   /\/   / 
// /___/  \  /    Vendor: Xilinx 
// \   \   \/     Version : 8.1.03i
//  \   \         Application : ISE
//  /   /         Filename : cpu_tb.tfw
// /___/   /\     Timestamp : Mon Nov 12 01:08:47 2007
// \   \  /  \ 
//  \___\/\___\ 
//
//Command: 
//Design Name: cpu_tb
//Device: Xilinx
//
`timescale 1ns/1ps

module cpu_tb;
    wire [31:0] addr;
    reg [31:0] data$inout$reg = 32'b00000000000000000000000000000000;
    // wire [31:0] data = data$inout$reg;
		wire [31:0] data;
    wire readmem;
    wire writemem;
    reg intrq = 1'b0;
    wire [1:0] type;
    reg rst = 1'b1;
    reg clk = 1'b0;

    parameter PERIOD = 20;
    // parameter real DUTY_CYCLE = 0.5;
    parameter OFFSET = 0;

    initial    // Clock process for clk. clk is 25MHz = 40ns cycle
    begin
        #OFFSET;
        forever
        begin
            clk = 1'b0;
//            #(PERIOD-(PERIOD*DUTY_CYCLE)) clk = 1'b1;
//            #(PERIOD*DUTY_CYCLE);
              #20 clk = 1'b1;
              #20;
       end
    end
		
	
	wire sevseg;
	wire anod;
	wire r, g, b;
	wire hs, vs;
	wire[7:0] sw;
	wire[3:0] btn;
	wire[7:0] ld;
	
	wire[17:0] addr_sr;
	wire[15:0] data_io_0_sr, 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;
	
	
	reg[2:0] irq;
	
	// the wiring of the keyboard serializer
	
	wire ps2c;
	wire ps2d;
	
	reg txs;
	
	reg[7:0] ps2_data;
	
	keyb_ser kb_ser0 ( .data_in(ps2_data), .tx_start(txs), .clk(clk), .ps2clk(ps2c), .ps2data(ps2d), .rst(rst) );
	

	// rs232 wiring	
	
	wire rxd;
	wire txd;

	wire tx_busy;

	async_transmitter async_tr0 (.clk(clk), .rst(rst), .TxD_start(txs & ~tx_busy), 
																		.TxD_data(ps2_data), .TxD(txd), .TxD_busy(tx_busy));
	
	
	// assignments
	
	assign sw = 8'b0000_1111; // switches sw[7:0]
	
	// the main unit
	
	
	
	main UUT (.clk(clk), .sevseg(sevseg), .anod(anod), 
		.ps2d(ps2d), .ps2c(ps2c), 
		.r(r), .g(g), .b(b), .hs(hs), .vs(vs), .sw(sw), 
		.btn(btn), .ld(ld),
		 
		.addr_sr(addr_sr),
		.data_io_0_sr(data_io_0_sr), .data_io_1_sr(data_io_1_sr),
		.ce_0_sr(ce_0_sr), .ce_1_sr(ce_1_sr),
		.oe_sr(oe_sr),
		.we_sr(we_sr),
		.ub_0_sr(ub_0_sr), .lb_0_sr(lb_0_sr),
		.ub_1_sr(ub_1_sr), .lb_1_sr(lb_1_sr),
		.rxd(txd), .txd(rxd),
		.rst(rst), .clk_aux(clk));


 	sram_ISSI sram0 ( .addr(addr_sr), .data_io(data_io_0_sr), 
											.ce(ce_0_sr), .oe(oe_sr), .we(we_sr),
											.ub(ub_0_sr), .lb(lb_0_sr) );
								
	sram_ISSI sram1 ( .addr(addr_sr), .data_io(data_io_1_sr), 
											.ce(ce_1_sr), .oe(oe_sr), .we(we_sr),
											.ub(ub_1_sr), .lb(lb_1_sr) );


    integer TX_FILE = 0;
    integer TX_ERROR = 0;

		initial
		begin

/*
		$monitor ("rxd_rcv_irq = %h, rxd_data = %h, UUT.rxd = %h, txs = %h, txd = %h", 
					UUT.rxd_rcv_irq, UUT.rxd_data, UUT.rxd, txs, txd);
					
*/
					

/*
		$monitor ("addr_sr = %h data_io_0_sr = %h data_io_1_sr = %h ce_0 = %h ce_1 = %h oe_sr = %h we_sr = %h",
		      addr_sr, data_io_0_sr, data_io_1_sr, ce_0_sr, ce_1_sr, oe_sr, we_sr );

*/


$monitor ( "addr = %h datain = %h dataout = %h readmem = %h writemem = %h aluOut = %h tp = %h regA = %h regC = %h rdack_A = %h tos = %h dsp = %h csp = %h micr_pc = %h pc = %h ir = %h",
								UUT.addr, UUT.datain, UUT.dataout, UUT.readmem, UUT.writemem, UUT.cpu0.aluOut, UUT.cpu0.tp, 
								UUT.cpu0.regA, UUT.cpu0.regC, UUT.rdack_A, UUT.cpu0.tos, UUT.cpu0.dsp, UUT.cpu0.csp, 
								UUT.cpu0.micr_pc, UUT.cpu0.pc, UUT.cpu0.ir ); 
								

/*
			$monitor ( 
"addr = %h addr_bus = %h data = %h data_b_r = %h readmem = %h writemem = %h enmem_bus = %h enmem_A = %h wmem_A = %h datamem_A = %h wmem_bus = %h rdack_A = %h rd_last_A = %h sel_A = %h sel_B = %h csp = %h mic_inst_raw = %h micr_pc = %h micr_pc_new = %h ir = %h clk_cpu = %h",
								UUT.addr, UUT.addr_bus, UUT.data, UUT.data_b_r, UUT.readmem, UUT.writemem, 
								UUT.enmem_bus, UUT.enmem_A, UUT.wmem_A, UUT.datamem_A, UUT.wmem_bus, UUT.rdack_A, UUT.rd_last_A, UUT.sel_A, UUT.sel_B, UUT.cpu0.csp, UUT.cpu0.mic_inst_raw,  
								UUT.cpu0.micr_pc, UUT.cpu0.micr_pc_new, UUT.cpu0.ir, UUT.clk_cpu ); 

*/

/*
			$monitor ( 
"addr = %h addr_bus = %h data = %h  readmem = %h writemem = %h enmem_bus = %h enmem_A = %h wmem_A = %h  sel_A = %h sel_B = %h rst = %h mic_inst = %h micr_pc = %h micr_pc_new = %h ir = %h clk_cpu = %h",
								UUT.addr, UUT.addr_bus, UUT.data,  UUT.readmem, UUT.writemem, 
								UUT.enmem_bus, UUT.enmem_A, UUT.wmem_A,  UUT.sel_A, UUT.sel_B, UUT.rst, UUT.cpu0.mic_inst,  
								UUT.cpu0.micr_pc, UUT.cpu0.micr_pc_new, UUT.cpu0.ir, UUT.clk_cpu ); 

*/
/*

			$monitor ( "micr_pc = %h data = %h template = %h clk = %h ", 
							UUT.cpu0.micr_pc, UUT.data_cpu,  UUT.cpu0.template, clk);

*/
/*

	 		$monitor ("addr = %h writemem = %h ir = %h pc = %h to_page_zero = %h pcb_source = %h template = %h regA = %h regB = %h dsp = %h tos = %h", 
					addr, writemem, UUT.ir, UUT.pc, UUT.to_page_zero, UUT.pcb_source, UUT.template, UUT.regA, UUT.regB, 
					UUT.dsp, UUT.tos );




			$monitor ("ir = %h", 
				UUT.cpu0.ir);



			$monitor ("ir = %h data = %h enbus = %h enbus_1 = %h rack = %h readmem = %h writemem = %h waitcpu_w = %h micr_pc = %h clk = %h", 
				UUT.ir, data, mar0.enbus, mar0.enbus_1, rdack, readmem, writemem, waitcpu_w, UUT.micr_pc, clk);
*/
/*
			$monitor ("outcnt = %h data=%b", kb_ser0.outcnt, kb_ser0.ps2data );
*/

		end


    
    initial begin  // Open the results file...
        TX_FILE = $fopen("results.txt");
        #25020 // old pFinal time:  1020 ns
				$displayh ( UUT.cpu0.ir );
        if (TX_ERROR == 0) begin
            $display("No errors or warnings.");
            $fdisplay(TX_FILE, "No errors or warnings.");
        end else begin
            $display("%d errors found in simulation.", TX_ERROR);
            $fdisplay(TX_FILE, "%d errors found in simulation.", TX_ERROR);
        end
        $fclose(TX_FILE);
        $stop;
    end

    initial begin
        // -------------  Current Time:  29ns
        //#25;
		  #700;
        rst = 1'b0;
        data$inout$reg = 32'bZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ;
        // -------------------------------------
    end

		wire[31:0] stopSimu, kp, kr;
		reg[31:0] kp_o, kr_o;
		
		parameter key_div = 12;
		
		reg[key_div:0] key_divider;
		
		initial begin		
				#0;
				key_divider = (1<<(key_div +1))-1;
				forever
					begin
						@(posedge clk);
						
						if (key_divider > 0)
							begin
								key_divider = key_divider - 1;
							end
						else	
							begin
								key_divider = (1<<(key_div +1))-1;
								
								txs <= 1'b0;
								kp_o = kp;
								kr_o = kr;

								#1;

								$get_c_data (stopSimu, kp, kr);

								if (stopSimu[0])
									begin
										$displayh ( UUT.irq_aux );
										// $close_window ();
        						if (TX_ERROR == 0) begin
            						$display("No errors or warnings.");
            						$fdisplay(TX_FILE, "No errors or warnings.");
        						end else begin
            						$display("%d errors found in simulation.", TX_ERROR);
            						$fdisplay(TX_FILE, "%d errors found in simulation.", TX_ERROR);
        						end
        						$fclose(TX_FILE);
        						$stop;
									end
								#1;
								if (!(kp == kp_o))
									begin
										$display ( "key pressed = %h txs = %h", kp, txs );
										if (kp != 32'hffffffff)
											begin
												if (~txs)
													begin
														ps2_data <= kp[7:0];
														txs <= 1'b1;							
													end
											end
									end
									#160;
									txs <= 1'b0;

								if (!(kr == kr_o))
									begin
										$display ( "key released = %h", kr );
									end
							end
						end

		end


		initial begin		
				#0;
				forever
					begin
					@(posedge clk);
					if (UUT.cpu0.ir[23:16] == 8'h3c)
						begin
							$display ("**** print %h ", UUT.cpu0.tos );
						end
					end
		end


    task CHECK_addr;
        input [31:0] NEXT_addr;

        #0 begin
            if (NEXT_addr !== addr) begin
                $display("Error at time=%dns addr=%b, expected=%b", $time, addr, NEXT_addr);
                $fdisplay(TX_FILE, "Error at time=%dns addr=%b, expected=%b", $time, addr, NEXT_addr);
                $fflush(TX_FILE);
                TX_ERROR = TX_ERROR + 1;
            end
        end
    endtask
    task CHECK_readmem;
        input NEXT_readmem;

        #0 begin
            if (NEXT_readmem !== readmem) begin
                $display("Error at time=%dns readmem=%b, expected=%b", $time, readmem, NEXT_readmem);
                $fdisplay(TX_FILE, "Error at time=%dns readmem=%b, expected=%b", $time, readmem, NEXT_readmem);
                $fflush(TX_FILE);
                TX_ERROR = TX_ERROR + 1;
            end
        end
    endtask
    task CHECK_writemem;
        input NEXT_writemem;

        #0 begin
            if (NEXT_writemem !== writemem) begin
                $display("Error at time=%dns writemem=%b, expected=%b", $time, writemem, NEXT_writemem);
                $fdisplay(TX_FILE, "Error at time=%dns writemem=%b, expected=%b", $time, writemem, NEXT_writemem);
                $fflush(TX_FILE);
                TX_ERROR = TX_ERROR + 1;
            end
        end
    endtask
    task CHECK_type;
        input [1:0] NEXT_type;

        #0 begin
            if (NEXT_type !== type) begin
                $display("Error at time=%dns type=%b, expected=%b", $time, type, NEXT_type);
                $fdisplay(TX_FILE, "Error at time=%dns type=%b, expected=%b", $time, type, NEXT_type);
                $fflush(TX_FILE);
                TX_ERROR = TX_ERROR + 1;
            end
        end
    endtask

endmodule


// keyb_ser is the serializer for simulating the output of a ps/2 keyboard


module keyb_ser ( data_in, tx_start, clk, ps2clk, ps2data, rst );

input[7:0] data_in;

input tx_start;

input clk;
input rst;

output ps2clk;
output ps2data;

reg ps2clk1;

//reg ps2data;

reg[8:0] divcnt;


always @(posedge clk or posedge rst)
	begin
		if (rst)
			begin
				divcnt <= 9'd255;
				ps2clk1 <= 1'b1;
			end
		else
			begin
				if (|divcnt)
					begin
						divcnt <= divcnt - 1;
					end
				else
					begin
						divcnt <= 9'd255;
						ps2clk1 <= ~ps2clk1;
					end
			end
	end

reg[10:0] ser_data;
reg[3:0] outcnt;

reg start_out;

always @(posedge clk or posedge rst)
	begin
		if (rst)
			begin
				ser_data <= 0;
				start_out <= 1'b0;
			end
		else
			begin
				if (tx_start & ~start_out)
					begin
						ser_data <= {1'b1, (^data_in) ^ 1'b1, data_in, 1'b0};
						start_out <= 1'b1;
						#1;
						$display ( "begin serialize. = %b", ser_data);
					end
				else if (~|outcnt)
					begin
						start_out <= 1'b0;
					end
			end
	end

reg wait_start;

assign ps2clk = ps2clk1 | wait_start;
assign ps2data = ser_data[0];


always @(posedge ps2clk1 or posedge rst)
	begin
		if (rst)
			begin
				outcnt <= 11;
				wait_start <= 1'b1;
			end
		else
			begin
				if (~wait_start & start_out) 
					begin
						$display ( "serialize ser_data = %b", ser_data );
						//ps2data <= ser_data[0];
						ser_data <= {ser_data[0],ser_data[10:1]};
						outcnt <= outcnt - 1;
					end
				else if (start_out & wait_start)
					begin
						outcnt <= 10;
						wait_start <= 1'b0;
					end
				else
					begin
						outcnt <= 11;
						wait_start <= 1'b1;
					end
			end
	end

endmodule


