/env/master/vip_amba_apb_master_driver.sv

Code
// *******************************************************
// Date Created   : 31 May, 2019
// Author         : :P
// *******************************************************

class vip_amba_apb_master_driver extends uvm_driver#( vip_amba_apb_txn_item );
	
	`uvm_component_utils( vip_amba_apb_master_driver )

	virtual vip_amba_apb_interface driver_bus_intf0;
	virtual vip_amba_apb_bridge_bfm_interface master_bfm_intf0;

    // Environment Instance Number
    // ---------------------------
    int env_num;

    vip_amba_apb_env_config env_config;

    // Driver Variable Declarations
    // ----------------------------
    bit txn_consecutive_prev; // Holds the previous pkt's value for txn_consecutive which will determine if the new pkt will be driven continously or not.
    logic [32-1:0] rdata_local_aa[int];
    logic [32-1:0] pslverr_local_aa[int];
    int ready_rcvd;
    bit got_timeout;
    bit got_reset;

	
	// Debug Log Handle
	int master_debug;

	function new( string name="vip_amba_apb_master_driver", uvm_component parent );
		super.new( name,parent );
		//$display( "Driver Newed" );
	endfunction

	function void build_phase( uvm_phase phase );
		super.build_phase( phase );
		//$display( "Driver Built" );

        // :P TODO
        `ifdef VIP_MASTER_DEBUG
		    if( !uvm_config_db#( int )::get( 	.cntxt( this ),
		    				 	.inst_name( "" ),
		    					.field_name( $sformatf( "master_debug[%0d]", env_num ) ),
		    					.value( master_debug ) ) )
            begin
		    	`uvm_fatal( "APB_MST_DRV"," Failed to get the master_debug handle" )
            end

        `endif
	endfunction

	function void start_of_simulation_phase( uvm_phase phase );
		super.start_of_simulation_phase( phase );
		
		// Get the Bus Interface
		if( !uvm_config_db#( virtual vip_amba_apb_interface )::get( 	.cntxt( this ),
										.inst_name( "" ),
										.field_name( $sformatf( "bus_intf0[%0d]", env_num ) ),
										.value( driver_bus_intf0 ) ) )
        begin
			`uvm_fatal( "APB_MST_DRV", " Failed to get the UVM Bus Interface" )
        end
		
		if( !uvm_config_db#( virtual vip_amba_apb_bridge_bfm_interface )::get( 	.cntxt( this ),
											.inst_name( "" ),
											.field_name( $sformatf ( "master_bfm_intf0[%0d]", env_num ) ),
											.value( master_bfm_intf0 ) ) )
        begin
			`uvm_fatal( "APB_MST_DRV", " Failed to get the UVM BFM Interface" )
        end

        if( !uvm_config_db#( vip_amba_apb_env_config )::get( 	.cntxt( this ),
					 	                                        .inst_name( "*" ),
						                                        .field_name( $sformatf( "env_config[%0d]", env_num ) ),
						                                        .value( env_config ) ) )
        begin
            `uvm_fatal( "APB_SLV_BFM", $sformatf( " Failed to access DB env config object" ) );
        end
	endfunction

	task reset_phase( uvm_phase phase );
		phase.raise_objection( this );
		
        // Initial Reset Logic

		phase.drop_objection( this );
	endtask

	task main_phase( uvm_phase phase );

		vip_amba_apb_txn_item pkt;
		phase.raise_objection( this );
		
        // Waiting for the Initial Reset to De-Assert
        // ------------------------------------------
        wait( !driver_bus_intf0.PRESETn );
        `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "Initial Reset Asserted!" ), UVM_LOW )
        wait( driver_bus_intf0.PRESETn );
        `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "Initial Reset De-Asserted!" ), UVM_LOW )

		fork : DRIVER_GLUE_LOGIC_DUT_SPECIFIC
			forever
			begin
                `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "Waiting for packet!" ), UVM_LOW )
				seq_item_port.get_next_item( pkt );
                `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "Rcvd a packet!" ), UVM_LOW )

                // Driving Logic
                //display_pkt( pkt );
                
                // Error Injection Conditions
                // --------------------------
                if( pkt.read_txn_error )
                begin
                    env_config.error_read_data.push_front( 'd1 );
                    env_config.data = $urandom;
                end
                
                if( pkt.pslverr_err_inj )
                begin
                    env_config.pslverr_err_inj.push_front( 'd1 );
                end

                if( pkt.txn_consecutive )
                begin
                    ready_rcvd = 0;
                    master_glue_logic_driver_task( pkt );

                    wait( ready_rcvd == pkt.txn_ADDR_q.size() );
                    `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "Consecutive Received All The Expected Number Of READY Assertions!" ), UVM_LOW )
                    if( pkt.get_data )
                    begin
                        foreach( pkt.txn_ADDR_q[i] )
                        begin
                            if( !pkt.txn_rd_wr_q[i] ) // Read
                            begin
                                if( rdata_local_aa.exists( i ) )
                                begin
                                    pkt.txn_RDATA_q[i] = rdata_local_aa[i];
                                end
                                else
                                begin
                                    pkt.txn_RDATA_q[i] = 'd0;
                                end
                            end
                            if( pslverr_local_aa.exists( i ) )
                            begin
                                pkt.txn_slverr_q[i] = pslverr_local_aa[i];
                            end
                            else
                            begin
                                pkt.txn_slverr_q[i] = 'd0;
                            end
                        end
                        pslverr_local_aa.delete();
                        rdata_local_aa.delete();
                        ready_rcvd = 0;
                    end
                    repeat( 2 )
                    begin
                        @( posedge driver_bus_intf0.PCLK );
                    end
                end
                else
                begin
                    ready_rcvd = 0;
                    master_glue_logic_driver_task( pkt );
                    
                    wait( ready_rcvd == 1 || got_timeout || got_reset );
                    ready_rcvd = 0;
                    `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "Non Consecutive Received All The Expected Number Of READY Assertions!" ), UVM_LOW )

                    if( pkt.get_data && !pkt.txn_rd_wr && ready_rcvd == 1 )
                    begin
                        wait( rdata_local_aa.size() );

                        if( rdata_local_aa.exists( 0 ) )
                        begin
                            pkt.txn_RDATA = rdata_local_aa[0];
                        end
                        else
                        begin
                            pkt.txn_RDATA = 'd0;
                        end
                        if( pslverr_local_aa.exists( 0 ) )
                        begin
                            pkt.txn_slverr = pslverr_local_aa[0];
                        end
                        else
                        begin
                            pkt.txn_slverr = 'd0;
                        end
                        pslverr_local_aa.delete();
                        rdata_local_aa.delete();
                    end
                    repeat( 2 )
                    begin
                        @( posedge driver_bus_intf0.PCLK );
                    end
                end

                // TODO Sending this updated transaction back through RSP is still pending

				seq_item_port.item_done(rsp);
                `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "Item Done called..." ), UVM_LOW )
			end
            begin
                //slave_model_task(); // TODO : More Features Needed. Will someday be connected to a task based Master BFM.
            end
			begin
				//#10000;
				phase.drop_objection( this );
			end
		join : DRIVER_GLUE_LOGIC_DUT_SPECIFIC
	endtask

    function void display_pkt( ref vip_amba_apb_txn_item pkt );
        $display( ":P ---------------------- Packet Details" );
        $display( "   txn_ADDR      ::  %h",pkt.txn_ADDR );                
        $display( "   txn_WDATA     ::  %h",pkt.txn_WDATA );
        $display( "   txn_RDATA     ::  %h",pkt.txn_RDATA );
        $display( "   txn_rd_wr     ::  %d",pkt.txn_rd_wr );
        $display( "   txn_STRB      ::  %b",pkt.txn_STRB );
        $display( "   txn_slave_sel ::  %d",pkt.txn_slave_sel );
        $display( "   txn_slverr    ::  %d",pkt.txn_slverr );
        $display( "\n" );
    endfunction

    
    // ++++++++++++++++++++++++++++++++++++++++++++++++++
    // USER_MODIFYABLE CODE REGION ( GLUE LOGIC PORTION )
    // --------------------------------------------------
    // Glue Logic will connect the Seqr-Driver to the
    // DUT/BFM.
    // ++++++++++++++++++++++++++++++++++++++++++++++++++


    // Master CPU Driver task to initiate a transaction to the APB Master : NOTE This is a DUT/BFM specific logic. So the person integrating this is responsible for coding this logic.
    // --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    task automatic master_glue_logic_driver_task( vip_amba_apb_txn_item pkt );
        bit kill_fork;
        bit reset_detected;
        bit not_first_itr;

        `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "Driving a packet..." ), UVM_LOW )

        //display_pkt( pkt );

        got_reset = 0;

        fork
            begin : SIGNAL_DRIVING

                `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "Forever loops!" ), UVM_LOW )
                got_timeout = 0;

                fork
                    begin
                        if( pkt.presetn_initiate.size() && !kill_fork && !reset_detected )
                        begin
                            reset_detected = 1;
                            `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "Resetn Detected! [ %0d ]", pkt.presetn_initiate[0] ), UVM_LOW )
                            
                            repeat( pkt.presetn_initiate[0] + 2 )
                            begin
                                @( posedge driver_bus_intf0.PCLK );
                            end
                            `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "Kill fork asserted! [ %0d ]", pkt.presetn_initiate[0] ), UVM_LOW )
                            kill_fork = 1;
                            got_reset = 1;
                        end
                    end
                join_none

                //if( !txn_consecutive_prev )
                //    @( posedge driver_bus_intf0.PCLK );
                //else
                //    ; // Do Nothing

                if( pkt.txn_consecutive )
                begin
                    `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "Inside Consecutive Transactions Block" ), UVM_LOW )
                    foreach( pkt.txn_ADDR_q[i] )
                    begin
                        forever
                        begin
                            if( driver_bus_intf0.to_cpu_RDATA_valid_WDATA_done && not_first_itr )
                            begin
                                break;
                            end
                            else
                            begin
                                if( driver_bus_intf0.apb_ready_for_txn )
                                begin
                                    @( posedge driver_bus_intf0.PCLK );
                                    break;
                                end
                                else
                                begin
                                    @( posedge driver_bus_intf0.PCLK );
                                end
                            end
                        end

                        not_first_itr = 1;

                        begin
                            if( driver_bus_intf0.to_cpu_RDATA_valid_WDATA_done )
                            begin
                                driver_bus_intf0.from_cpu_slave_sel <= #1 1;
                            end
                            else if( driver_bus_intf0.from_cpu_slave_sel !== 'd1 )
                            begin
                                driver_bus_intf0.from_cpu_slave_sel <= #1 1;
                                @( negedge driver_bus_intf0.PCLK );
                            end
                            else
                            begin
                                driver_bus_intf0.from_cpu_slave_sel <= #1 1;
                            end
                            driver_bus_intf0.from_cpu_rd_wr <= #1 pkt.txn_rd_wr_q[i];
                            driver_bus_intf0.from_cpu_address <= #1 pkt.txn_ADDR_q[i];
                            if( pkt.txn_rd_wr_q[i] )
                            begin
                                driver_bus_intf0.from_cpu_wr_STRB <= #1 pkt.txn_STRB_q[i];
                                driver_bus_intf0.from_cpu_wr_WDATA <= #1 pkt.txn_WDATA_q[i];
                                `uvm_info( "APB_DRV_MST", " --------------------------", UVM_LOW )
                                `uvm_info( "APB_DRV_MST", " APB Write Transaction", UVM_LOW )
                                `uvm_info( "APB_DRV_MST", " --------------------------", UVM_LOW )
                            end
                            else
                            begin
                                `uvm_info( "APB_DRV_MST", " --------------------------", UVM_LOW )
                                `uvm_info( "APB_DRV_MST", " APB Read Transaction", UVM_LOW )
                                `uvm_info( "APB_DRV_MST", " --------------------------", UVM_LOW )
                            end
                            
                            driver_bus_intf0.from_cpu_valid_txn <= #1 1;

                            @( posedge driver_bus_intf0.PCLK );

                            driver_bus_intf0.from_cpu_valid_txn <= #1 0;
                            driver_bus_intf0.from_cpu_slave_sel <= #1 0;
                        end
                        
                        fork
                            if( !pkt.txn_rd_wr_q[i] )
                            begin
                                `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "Waiting for READ READY!" ), UVM_LOW );
                                forever
                                begin
                                    if( driver_bus_intf0.to_cpu_RDATA_valid_WDATA_done )
                                    begin
                                        rdata_local_aa[i] = driver_bus_intf0.to_cpu_RDATA;
                                        pslverr_local_aa[i] = driver_bus_intf0.to_cpu_txn_err;
                                        ready_rcvd++;
                                        break;
                                    end
                                    else
                                    begin
                                        @( posedge driver_bus_intf0.PCLK );
                                    end
                                end
                                `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "READY for Read RCVD!" ), UVM_LOW );
                            end
                            else
                            begin
                                `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "Waiting for WRITE READY!" ), UVM_LOW );
                                forever
                                begin
                                    if( driver_bus_intf0.to_cpu_RDATA_valid_WDATA_done )
                                    begin
                                        pslverr_local_aa[i] = driver_bus_intf0.to_cpu_txn_err;
                                        ready_rcvd++;
                                        break;
                                    end
                                    else
                                    begin
                                        @( posedge driver_bus_intf0.PCLK );
                                    end
                                end
                                `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "READY for Write RCVD!" ), UVM_LOW );
                            end
                        join_none
                    end
                end
                else
                begin
                    `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "Inside NON Consecutive Transactions Block" ), UVM_LOW )
                    forever
                    begin
                        if( driver_bus_intf0.to_cpu_RDATA_valid_WDATA_done && not_first_itr )
                        begin
                            break;
                        end
                        else
                        begin
                            if( driver_bus_intf0.apb_ready_for_txn )
                            begin
                                @( posedge driver_bus_intf0.PCLK );
                                break;
                            end
                            else
                            begin
                                @( posedge driver_bus_intf0.PCLK );
                            end
                        end
                    end

                    not_first_itr = 1;

                    begin
                        if( driver_bus_intf0.to_cpu_RDATA_valid_WDATA_done )
                        begin
                            driver_bus_intf0.from_cpu_slave_sel <= #1 1;
                        end
                        else if( driver_bus_intf0.from_cpu_slave_sel !== 'd1 )
                        begin
                            driver_bus_intf0.from_cpu_slave_sel <= #1 1;
                            @( negedge driver_bus_intf0.PCLK );
                        end
                        else
                        begin
                            driver_bus_intf0.from_cpu_slave_sel <= #1 1;
                        end
                        driver_bus_intf0.from_cpu_rd_wr <= #1 pkt.txn_rd_wr;
                        driver_bus_intf0.from_cpu_address <= #1 pkt.txn_ADDR;
                        if( pkt.txn_rd_wr )
                        begin
                            driver_bus_intf0.from_cpu_wr_STRB <= #1 pkt.txn_STRB;
                            driver_bus_intf0.from_cpu_wr_WDATA <= #1 pkt.txn_WDATA;
                            `uvm_info( "APB_DRV_MST", " --------------------------", UVM_LOW )
                            `uvm_info( "APB_DRV_MST", " APB Write Transaction", UVM_LOW )
                            `uvm_info( "APB_DRV_MST", " --------------------------", UVM_LOW )
                        end
                        else
                        begin
                            `uvm_info( "APB_DRV_MST", " --------------------------", UVM_LOW )
                            `uvm_info( "APB_DRV_MST", " APB Read Transaction", UVM_LOW )
                            `uvm_info( "APB_DRV_MST", " --------------------------", UVM_LOW )
                        end
                        
                        driver_bus_intf0.from_cpu_valid_txn <= #1 1;

                        @( posedge driver_bus_intf0.PCLK );

                        driver_bus_intf0.from_cpu_valid_txn <= #1 0;
                        driver_bus_intf0.from_cpu_slave_sel <= #1 0;
                    end
                    
                    fork
                        if( !pkt.txn_rd_wr )
                        begin
                            `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "Waiting for READ READY!" ), UVM_LOW );
                            forever
                            begin
                                if( driver_bus_intf0.to_cpu_RDATA_valid_WDATA_done )
                                begin
                                    rdata_local_aa[0] = driver_bus_intf0.to_cpu_RDATA;
                                    pslverr_local_aa[0] = driver_bus_intf0.to_cpu_txn_err;
                                    ready_rcvd++;
                                    break;
                                end
                                else
                                begin
                                    @( posedge driver_bus_intf0.PCLK );
                                end
                            end
                            `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "READY for Read RCVD!" ), UVM_LOW );
                        end
                        else
                        begin
                            `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "Waiting for WRITE READY!" ), UVM_LOW );
                            forever
                            begin
                                if( driver_bus_intf0.to_cpu_RDATA_valid_WDATA_done )
                                begin
                                    pslverr_local_aa[0] = driver_bus_intf0.to_cpu_txn_err;
                                    ready_rcvd++;
                                    break;
                                end
                                else
                                begin
                                    @( posedge driver_bus_intf0.PCLK );
                                end
                            end
                            `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "READY for Write RCVD!" ), UVM_LOW );
                        end
                    join_none
                end
                if( pkt.txn_consecutive )
                begin
                    wait( ready_rcvd == pkt.txn_ADDR_q.size() );
                end
                else
                begin
                    wait( ready_rcvd == 1 );
                end
                if( kill_fork )
                begin
                    wait( kill_fork == 0 );
                end
                `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "Signal Driving Thread Done!" ), UVM_LOW )
            end : SIGNAL_DRIVING
            begin : PRESETN_LOOP

                `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "Waiting for kill_fork!" ), UVM_LOW )
                wait( kill_fork == 'd1 );
                `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "PRESETn Asserted" ), UVM_LOW )
                driver_bus_intf0.PRESETn <= #4 0; // TODO Configurable Delayed PRESETn Assertion
                `uvm_info( "APB_DRV_MST", " PRESETn Asserted!", UVM_LOW )

                repeat( 20 )
                begin
                    @( posedge driver_bus_intf0.PCLK );
                end

                `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "PRESETn De-asserted" ), UVM_LOW )
                driver_bus_intf0.PRESETn <= #1 1;
                `uvm_info( "APB_DRV_MST", " PRESETn De-Asserted!", UVM_LOW )

            end : PRESETN_LOOP
            begin : PRDATA_SAMPLING
                forever
                begin
                    @( posedge driver_bus_intf0.PCLK );
                    if( driver_bus_intf0.to_cpu_RDATA_valid_WDATA_done && !pkt.txn_rd_wr ) // TODO Modification was done here. Review if any unruly behaviour is observed
                    begin
                        if( pkt.txn_consecutive )
                        begin
                            wait( ready_rcvd == pkt.txn_ADDR_q.size() );
                            break;
                        end
                        else
                        begin
                            `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "RDATA placed onto the to_CPU_bus!" ), UVM_LOW )
                            pkt.txn_RDATA = driver_bus_intf0.PRDATA;
                            wait( ready_rcvd == 1 );
                            break;
                        end
                    end
                end
                if( kill_fork )
                begin
                    wait( kill_fork == 0 );
                end
            end : PRDATA_SAMPLING
            begin : TIMEOUT_LOGIC

                // TODO : IMPLEMENT THE TIMEOUT PIN IN RTL AND THEN WAIT FOR IT'S ASSERTION! NEXT KILL THIS ENTIRE THREAD!
                wait( driver_bus_intf0.to_cpu_txn_timeout );
                got_timeout = 1;
                `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "PSELx timeout incurred!" ), UVM_LOW )

            end : TIMEOUT_LOGIC

        join_any

        `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "Fork Disabled!" ), UVM_LOW )
        disable fork;

        `UVM_INFO_MST( "VIP_MASTER_DEBUG_INFO", $sformatf( "Finished driving a packet..." ), UVM_LOW )
    endtask

endclass