module spimemio_wb_tb;

    reg wb_clk_i;
	reg wb_rst_i;

    reg wb_flash_stb_i;
	reg wb_cfg_stb_i;
	reg wb_cyc_i;
	reg wb_we_i;
	reg [3:0]  wb_sel_i;
	reg [31:0] wb_adr_i;
	reg [31:0] wb_dat_i;

	wire wb_flash_ack_o;
    wire wb_cfg_ack_o;
	wire [31:0] wb_flash_dat_o;
	wire [31:0] wb_cfg_dat_o;

    wire flash_csb;
    wire flash_clk;

    wire flash_io0_oeb;
    wire flash_io1_oeb;
    wire flash_io2_oeb;
    wire flash_io3_oeb;

    wire flash_io0_di = 1'b 1;
    wire flash_io1_di = 1'b 1;
    wire flash_io2_di = 1'b 1;
    wire flash_io3_di = 1'b 1;

    initial begin
        wb_clk_i = 0; 
        wb_rst_i = 0;
        wb_flash_stb_i = 0;  
        wb_cfg_stb_i = 0; 
        wb_cyc_i = 0;   
        wb_we_i  = 0;
        wb_sel_i = 0;    
        wb_adr_i = 0;  
        wb_dat_i = 0;  
    end

    always #1 wb_clk_i = ~wb_clk_i;

    spimemio_wb uut(
        .wb_clk_i(wb_clk_i),
	    .wb_rst_i(wb_rst_i),
        
        .wb_flash_stb_i(wb_flash_stb_i),
	    .wb_cfg_stb_i(wb_cfg_stb_i),
	    .wb_cyc_i(wb_cyc_i),
    	.wb_we_i(wb_we_i),
	    .wb_sel_i(wb_sel_i),
	    .wb_adr_i(wb_adr_i), 
	    .wb_dat_i(wb_dat_i),
	    .wb_flash_ack_o(wb_flash_ack_o),
        .wb_cfg_ack_o(wb_cfg_ack_o),
	    .wb_flash_dat_o(wb_flash_dat_o),
	    .wb_cfg_dat_o(wb_cfg_dat_o),

        .flash_clk(flash_clk),
        .flash_csb(flash_csb),

        .flash_io0_oeb(flash_io0_oeb),
        .flash_io1_oeb(flash_io1_oeb),
        .flash_io2_oeb(flash_io2_oeb),
        .flash_io3_oeb(flash_io3_oeb),

        .flash_io0_di(flash_io0_di),
        .flash_io1_di(flash_io1_di),
	    .flash_io2_di(flash_io2_di),
	    .flash_io3_di(flash_io3_di)       
    );

    initial begin
        $dumpfile("spimemio_wb_tb.vcd");
        $dumpvars(0, spimemio_wb_tb);
        repeat (50) begin
            repeat (1000) @(posedge wb_clk_i);
        end
        $display("%c[1;31m",27);
        $display ("Monitor: Timeout, Test spimmemio Failed");
        $display("%c[0m",27);
        $finish;
    end

    integer i;
        
    wire [31:0] cfgreg_data;
    assign cfgreg_data = {
        1'b 1,
        8'b 0,
        3'b 111,
        4'b 1010,
        4'b 0,             // make sure is it tied to zero in the module itself
        {~flash_io3_oeb, ~flash_io2_oeb, ~flash_io1_oeb, ~flash_io0_oeb},
        2'b 0,
        flash_csb,
        flash_clk,
        {flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di}
    };

    initial begin
        // Reset Operation
        wb_rst_i = 1;
        #2;
        wb_rst_i = 0; 
        #2;

        // Read from flash
        for (i = `FLASH_BASE; i < `FLASH_BASE + 100 ; i = i + 4) begin
            read(i, 1, 0);
            if (wb_flash_dat_o !== 32'hFFFF_FFFF) begin
                $display("%c[1;31m",27);
                $display("Expected %0b, but Got %0b ",  32'hFFFF_FFFF, wb_flash_dat_o);
                $display("Monitor: Wishbone spimemio Failed");
            	$display("%c[0m",27);
                $finish;
            end
            #2;
        end 

        #6;
        // Write to Configuration register
        write(cfgreg_data, 0);
        #2;
        read(0, 0, 1);
        if (wb_cfg_dat_o !== cfgreg_data) begin
            $display("%c[1;31m",27);
            $display("Expected %0b, but Got %0b ",  cfgreg_data, wb_cfg_dat_o);
            $display("Monitor: Wishbone spimemio Failed");
            $display("%c[0m",27);
            $finish;
        end
        
        $display("Success!");
        $display("Monitor: Wishbone spimemio Passed");
        $finish;
    end
    
    task write;
        input [32:0] data;
        input [31:0] addr;
        begin 
            @(posedge wb_clk_i) begin
                wb_cfg_stb_i = 1'b 1;
                wb_flash_stb_i = 1'b 0;
                wb_cyc_i = 1'b 1;
                wb_sel_i = 4'b 1111; // complete word
                wb_we_i = 1'b 1;     // write enable
                wb_adr_i = addr;
                wb_dat_i = data;
            end

            wait_ack();
        end
    endtask
    
    task read;
        input [32:0] addr;
        input flash_stb;
        input cfg_stb;
        begin 
            wb_flash_stb_i = flash_stb;
            wb_cfg_stb_i = cfg_stb;

            wb_cyc_i = 1'b 1;
            wb_adr_i = addr;
            wb_dat_i = 24;
            wb_sel_i = 4'b 1111; // complete word
            wb_we_i = 1'b 0;     // read
            $display("Initiated Read transaction...");
            wait_ack();
        end
    endtask

    task wait_ack;
        // Wait for an ACK
        if (wb_cfg_stb_i == 1) begin
            @(posedge wb_cfg_ack_o) begin
                #2;  // To end the transaction on the falling edge of ack 
                wb_cyc_i = 1'b 0;
                wb_cfg_stb_i = 1'b 0;
                $display("Monitor: Received an ACK from slave");
            end
        end
        else begin
            @(posedge wb_flash_ack_o) begin
                #2;  // To end the transaction on the falling edge of ack 
                wb_cyc_i = 1'b 0;
                wb_flash_stb_i = 1'b 0;
                $display("Monitor: Received an ACK from slave");
            end
        end
    endtask

    // spiflash #(
	// 	.FILENAME("flash.hex")
	// ) spiflash (
	// 	.csb(flash_csb),
	// 	.clk(flash_clk),
	// 	.io0(flash_io0),
	// 	.io1(flash_io1),
	// 	.io2(flash_io2),
	// 	.io3(flash_io3)
	// );
    
endmodule