//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Filename : Opb_driver.vr // // Description : Basic Opb bus driver // //----------------------------------------------------------------------------- #include #include #include "OpbPort_if.vri" #include "OpbMasterStateDefine.vrh" #include "OpbMasterDynamicSize.vri" typedef class Opb_driver; class Opb_driver_callbacks extends rvm_xactor_callbacks { //-- event event trans_driven; //-- Called before a transaction is executed virtual task pre_trans_t(Opb_driver master, Opb_bus_transaction tr, var bit drop) { } //-- Called after a transaction has been executed virtual task post_trans_t(Opb_driver master, Opb_bus_transaction tr) { } } //-- Adding a definition for: //-- Opb_driver class, derives from rvm_xactor //-- Opb data channel (in_chan) //-- Opb port (iport) class Opb_driver extends rvm_xactor { //-- Bit vector to store the prev address in case of a seqAddr access bit [`OPB_ADDR_WIDTH-1:0] OpbMasterPrevAddr; bit [`OPB_ADDR_WIDTH-1:0] TransactionAddress; //-- An object of the Master Info Class OpbMasterInfo MasterInfo; bit SeqAddr; //-- Bit to store master dev id integer devId; //-- Bit to indicate the locked state of the transfer bit lock; //-- Bit to indicate the spl data transfer state bit SplCond; //-- Bit to indiacte direction of transfer bit OPB_RNW; //-- Bit to indiacte retry of transfer bit Retried; //-- Integer to detect LatencyCounter integer LatencyCounter; //-- Bit vector for Write Data Bus bit [`OPB_DBus_WIDTH-1:0] Opb_DBus; bit [`OPB_DBus_WIDTH-1:0] Mn_DBus; bit [`OPB_DBus_WIDTH-1:0] TransactionData; bit TransactionDirection; //-- Bit vector to track Master Driver State Machine bit [2:0] MasterState; //-- Input Variables bit Opb_MGrant; bit Opb_xferAck; bit Opb_hwAck; bit Opb_fwAck; bit Opb_dwAck; bit Opb_errAck; bit Opb_retry; bit Opb_timeout; bit OPB_pendReqn; //-- Other vars bit HWORD; bit FWORD; bit DWORD; //-- Integer to Mark the Master's Capacity integer MSize; //-- Integer to Mark the Slave's Capacity integer TSize; //-- Integer to indicate how many extra bus cycle required integer DynamicRequirement; //-- Input rvm_channel: this is where transactions to be sent stand Opb_bus_transaction_channel in_chan; //-- Interface to Opb Bus HDL signals local OpbMaster_port iport; //-- RVM_Xactor Constructor and Data Members task new(string instance, integer stream_id, integer devId, OpbMaster_port iport, Opb_bus_transaction_channel in_chan = null, OpbMasterInfo MasterInfo); virtual task reset_xactor(integer rst_type = 0); protected virtual task main_t(); local function Opb_bus_transaction OpbMasterWaitForTransaction(); local virtual task OpbMasterTransferWidthCalculate(Opb_bus_transaction tr); local virtual task OpbMasterDriverStateMachine(Opb_bus_transaction data); local virtual task OpbMasterDriveTransaction(Opb_bus_transaction data); local virtual task OpbMasterResetDrive(Opb_bus_transaction data); local virtual task OpbMasterInputSample(); local virtual function integer OpbMasterWaitForAck(); local virtual task OpbMasterStoreData(Opb_bus_transaction tr); local virtual task OpbMasterDynamicBusSizing(Opb_bus_transaction tr); local virtual task OpbMasterProcessDynamicRequirement(Opb_bus_transaction tr); local virtual task OpbMasterSetSlaveCapacity(); local virtual task OpbMasterReverse(integer Addr); } task Opb_driver::new(string instance, integer stream_id, integer devId, OpbMaster_port iport, Opb_bus_transaction_channel in_chan = null, OpbMasterInfo MasterInfo) { //-- Adding a call to super.new //-- Name is "Opb Driver" //-- Alsom instance and stream_id super.new("Opb Driver", instance, stream_id); //-- Adding the details to the new function //-- Save the iport //-- Save in_chan //-- Allocate a channel if in_chan is null //-- Drive the signals this.MasterInfo = MasterInfo; this.iport = iport; this.devId = devId; this.in_chan = in_chan; if (in_chan==null) { in_chan = new("Opb Driver Input Channel", instance); } } task Opb_driver::OpbMasterInputSample() { //-- Store the values this.Opb_MGrant = this.iport.$OPB_MnGrant; this.Opb_retry = this.iport.$OPB_retry; this.Opb_xferAck = this.iport.$OPB_xferAck; this.Opb_errAck = this.iport.$OPB_errAck; this.Opb_hwAck = this.iport.$OPB_hwAck; this.Opb_fwAck = this.iport.$OPB_fwAck; this.Opb_dwAck = this.iport.$OPB_dwAck; this.Opb_timeout = this.iport.$OPB_timeout; this.Opb_DBus = this.iport.$OPB_DBus; this.OPB_pendReqn = this.iport.$OPB_pendReqn; this.SplCond = this.iport.$SplCond; this.lock = this.iport.$Lock; } task Opb_driver::OpbMasterResetDrive( Opb_bus_transaction tr) { //-- Drive Initial Values this.iport.$Mn_ABus = tr.Mn_ABus; this.iport.$Mn_busLock = tr.Mn_busLock; this.iport.$Mn_select = tr.Mn_select; this.iport.$Mn_DBus = tr.Mn_DBus; this.iport.$Mn_DBusEn = tr.Mn_DBusEn; this.iport.$Mn_RNW = tr.Mn_RNW; this.iport.$Mn_beXfer = tr.Mn_beXfer; this.iport.$Mn_hwXfer = tr.Mn_hwXfer; this.iport.$Mn_fwXfer = tr.Mn_fwXfer; this.iport.$Mn_dwXfer = tr.Mn_dwXfer; this.iport.$Mn_seqAddr = tr.Mn_seqAddr; } task Opb_driver::OpbMasterDriveTransaction( Opb_bus_transaction tr ) { //-- Drive Transaction Values this.iport.$Mn_ABus = tr.Mn_ABus; this.iport.$Mn_busLock = tr.Mn_busLock; this.iport.$Mn_select = tr.Mn_select; this.iport.$Mn_DBus = tr.Mn_DBus; this.iport.$Mn_DBusEn = tr.Mn_DBusEn; this.iport.$Mn_RNW = tr.Mn_RNW; this.iport.$Mn_hwXfer = tr.Mn_hwXfer; this.iport.$Mn_fwXfer = tr.Mn_fwXfer; this.iport.$Mn_dwXfer = tr.Mn_dwXfer; this.iport.$Mn_beXfer = tr.Mn_beXfer; this.iport.$Mn_seqAddr = tr.Mn_seqAddr; this.OpbMasterPrevAddr = tr.Mn_ABus; } task Opb_driver::main_t() { Opb_bus_transaction tr; fork super.main_t(); join none //-- Calling the Driver State Machine. Writing a state machine is //-- often provedto be very useful. The are two main reasons for this //-- 1. Tracking the Driver state is easier. //-- 2. Coverage information can be got easily OpbMasterDriverStateMachine(tr); } //-- This function waits in the Channel from the Aplication Layer //-- (Generator Class) for any valid Transaction. If any, returns it function Opb_bus_transaction Opb_driver::OpbMasterWaitForTransaction( ) { Opb_bus_transaction tr; // Get a transaction from the mailbox // and cast to a Opb_bus_transaction cast_assign(tr, this.next_transaction_t(this.in_chan)); //-- Return it OpbMasterWaitForTransaction = tr; } //-- This is the OPB Master Driver State Machine. task Opb_driver::OpbMasterDriverStateMachine ( Opb_bus_transaction tr ) { //-- Integer //-- For saving Slave's type of Acknowledgement integer SlaveFeedBack = 0; //-- Default OK integer count = 0; //-- For reporting how many transfers integer TransferNumber = 0; //-- Bit for showinfg the existance of penalty during lock bit LockDeAsserted = 0; //-- Bit to mark that the Locked transfer is Single bit Marked = 1; //-- Loop forever Until the Test Bench stops Via Environment Parameter //-- (See Opb_env.vr) for Details while ( 1 ) { case ( MasterState [2:0] ) { `MASTIDLE : { if ( !this.iport.$OPB_reset ) { //-- Drive reset values //-- %% Constraint %% tr.randomize () with { Mn_hwXfer == 0 && Mn_fwXfer == 0 && Mn_dwXfer == 0 && Mn_request == 0 && Mn_select == 0 && Mn_BE == 0 && Mn_DBusEn == 0 && Mn_DBusEn32_63 == 0 && Mn_busLock == 0 && Mn_seqAddr == 0;}; OpbMasterResetDrive(tr); //-- Next State Assignment MasterState = `MASTIDLE; //-- Advance the Clock @(posedge this.iport.$Mn_clk); } else { //-- First Sample the Input lines OpbMasterInputSample(); //-- If there is any default grant if ( this.Opb_MGrant ) { if (this.lock == 1) { //-- Wait for a transaction tr = OpbMasterWaitForTransaction(); //-- %% Constraint %% tr.randomize () with {Mn_busLock == 1;}; if ( tr.Mn_seqAddr == 1'b1 ) { //-- Ensure that it is Sequential Transaction SeqAddr = 1; //-- Change the state MasterState = `MASTLOCK; } else { //-- Ensure it is not Sequential Transaction SeqAddr = 0; //-- Change the state MasterState = `MASTLOCK; } //-- First Choose Randomly How many to transfer TransferNumber = random() % `MAXTRNS; //-- If only one transfer, mark it if ( TransferNumber == 1 ) Marked = 1; else Marked = 0; } else { //-- Do not request this.iport.$Mn_request = 1'b0; //-- Normal Transaction --// //-- Adjust SeqAddr State tr.Mn_seqAddr = 1'b0; //-- Change the State MasterState = `MASTTRNS; } } else { //-- Wait for a transaction tr = OpbMasterWaitForTransaction(); //-- State Assignment MasterState = `MASTREQ; } } } `MASTREQ : { if ( !this.iport.$OPB_reset ) { //-- Drive reset values //-- %% Constraint %% tr.randomize () with { Mn_hwXfer == 0 && Mn_fwXfer == 0 && Mn_dwXfer == 0 && Mn_request == 0 && Mn_select == 0 && Mn_BE == 0 && Mn_DBusEn == 0 && Mn_DBusEn32_63 == 0 && Mn_busLock == 0 && Mn_seqAddr == 0;}; OpbMasterResetDrive(tr); //-- Next State Assignment MasterState = `MASTIDLE; //-- Advance the Clock @(posedge this.iport.$Mn_clk); } else { //-- Drive reset values with request //-- %% Constraint %% tr.randomize () with { Mn_hwXfer == 0 && Mn_fwXfer == 0 && Mn_dwXfer == 0 && Mn_request == 1 && Mn_select == 0 && Mn_BE == 0 && Mn_DBusEn == 0 && Mn_DBusEn32_63 == 0 && Mn_busLock == 0 && Mn_seqAddr == 0;}; OpbMasterResetDrive(tr); //-- State ssignment MasterState = `MASTWAITFGRNT; //-- Advance the Clock @(posedge this.iport.$Mn_clk); } } `MASTWAITFGRNT : { if ( !this.iport.$OPB_reset ) { //-- Drive reset values //-- %% Constraint %% tr.randomize () with { Mn_hwXfer == 0 && Mn_fwXfer == 0 && Mn_dwXfer == 0 && Mn_request == 0 && Mn_select == 0 && Mn_BE == 0 && Mn_DBusEn == 0 && Mn_DBusEn32_63 == 0 && Mn_busLock == 0 && Mn_seqAddr == 0;}; OpbMasterResetDrive(tr); //-- Next State Assignment MasterState = `MASTIDLE; //-- Advance the Clock @(posedge this.iport.$Mn_clk); } else { //-- Sample the Inputs OpbMasterInputSample(); //-- Check for grant //-- If granted, Check for the transaction type //-- and change the state accordingly //-- If not granted, advance the clock and check if ( this.Opb_MGrant ) { if ( tr.Mn_busLock == 1'b1 ) { if ( tr.Mn_seqAddr == 1'b1 ) { //-- Make sure that the SeqAddr is set SeqAddr = 1; //-- Change the state MasterState = `MASTLOCK; } else { //-- Indicate that it is not a seqaddr trnsfer SeqAddr = 0; //-- Change the state MasterState = `MASTLOCK; } //-- Calculate Randomly How many to //-- transfer TransferNumber = random() % `MAXTRNS; //-- If only one transfer, mark it if ( TransferNumber == 1 ) Marked = 1; else Marked = 0; } else { //-- Do not request //this.iport.$Mn_request = 1'b0; //-- Normal Transaction --// //-- Adjust SeqAddr Value tr.Mn_seqAddr = 1'b0; //-- Change the State MasterState = `MASTTRNS; //-- Normal Transaction --// } } //-- If no grant simply advance the clock else { @(posedge this.iport.$Mn_clk); } } } `MASTTRNS : { //-- If reset go back to the Idle state if ( !this.iport.$OPB_reset ) { //-- Drive reset values //-- %% Constraint %% tr.randomize () with { Mn_hwXfer == 0 && Mn_fwXfer == 0 && Mn_dwXfer == 0 && Mn_request == 0 && Mn_select == 0 && Mn_BE == 0 && Mn_DBusEn == 0 && Mn_DBusEn32_63 == 0 && Mn_busLock == 0 && Mn_seqAddr == 0;}; OpbMasterResetDrive(tr); //-- Next State Assignment MasterState = `MASTIDLE; //-- Advance the Clock @(posedge this.iport.$Mn_clk); } else { tr = OpbMasterWaitForTransaction(); //-- Now Calculate which Transfer OpbMasterTransferWidthCalculate(tr); //-- This spl condition activates some specific check //-- on the data bus //-- %% Constraint %% if (this.SplCond) { tr.randomize () with {Mn_ABus [31:30] + Be >= 6 && Mn_ABus [31:30] + Be <= 12 && Mn_busLock == 0 && Mn_seqAddr == 0 && Mn_request == 0 && Mn_select == 1;}; } else { tr.randomize () with { Mn_busLock == 0 && Mn_seqAddr == 0 && Mn_request == 0 && Mn_select == 1;}; } //-- Task to provide data according to the //-- status of MnABus [31:30] + Be OpbMasterDynamicBusSizing (tr); OpbMasterDriveTransaction(tr); //-- Now wait for Slave Response SlaveFeedBack = OpbMasterWaitForAck(); //-- Now Work according to the Slave's feedback if ( SlaveFeedBack == 0 ) { //-- Normal Ack if ( tr.Mn_RNW ) { //-- Take the read data Opb_DBus = this.iport.$OPB_DBus; //-- Assign to tr tr.Mn_DBus = Opb_DBus; } //-- Reset the values if not granted again if ( !this.iport.$OPB_MnGrant ) { //-- Drive reset values //-- %% Constraint %% tr.randomize () with { Mn_hwXfer == 0 && Mn_fwXfer == 0 && Mn_dwXfer == 0 && Mn_request == 0 && Mn_select == 0 && Mn_BE == 0 && Mn_DBusEn == 0 && Mn_DBusEn32_63 == 0 && Mn_busLock == 0 && Mn_seqAddr == 0;}; OpbMasterResetDrive(tr); } //-- Change the master State MasterState = `MASTIDLE; } else if ( SlaveFeedBack == 1 ) { //-- Retry //-- Drive Reset Values //-- Drive reset values //-- %% Constraint %% tr.randomize () with { Mn_hwXfer == 0 && Mn_fwXfer == 0 && Mn_dwXfer == 0 && Mn_request == 0 && Mn_select == 0 && Mn_BE == 0 && Mn_DBusEn == 0 && Mn_DBusEn32_63 == 0 && Mn_busLock == 0 && Mn_seqAddr == 0;}; OpbMasterResetDrive(tr); //-- Advance the clock @(posedge this.iport.$Mn_clk); //-- Change the master State MasterState = `MASTIDLE; } else if ( SlaveFeedBack == 2 || SlaveFeedBack == 3 ) { //-- No feedback for //-- long time //-- Timeout || Master Abort //-- Drive Reset Values //-- Drive reset values //-- %% Constraint %% tr.randomize () with { Mn_hwXfer == 0 && Mn_fwXfer == 0 && Mn_dwXfer == 0 && Mn_request == 0 && Mn_select == 0 && Mn_BE == 0 && Mn_DBusEn == 0 && Mn_DBusEn32_63 == 0 && Mn_busLock == 0 && Mn_seqAddr == 0;}; OpbMasterResetDrive(tr); //-- Advance the clock @(posedge this.iport.$Mn_clk); //-- Change the master State MasterState = `MASTIDLE; } } } `MASTLOCK : { //-- If there is a reset, come back to the idle state if ( !this.iport.$OPB_reset ) { //-- Drive reset values //-- %% Constraint %% tr.randomize () with { Mn_hwXfer == 0 && Mn_fwXfer == 0 && Mn_dwXfer == 0 && Mn_request == 0 && Mn_select == 0 && Mn_BE == 0 && Mn_DBusEn == 0 && Mn_DBusEn32_63 == 0 && Mn_busLock == 0 && Mn_seqAddr == 0;}; OpbMasterResetDrive(tr); //-- Next State Assignment MasterState = `MASTIDLE; //-- Advance the Clock @(posedge this.iport.$Mn_clk); } else { //-- Now Calculate which Transfer OpbMasterTransferWidthCalculate(tr); //-- If it is the penaltimate transfer //-- Usually lower the Lock except some cases //-- (penalty) determined by (random() % 5) if (TransferNumber == 1 && !(random() % 5) && !Marked) { //-- This spl condition activates some specific check //-- on the data bus //-- %% Constraint %% if (this.SplCond) { tr.randomize () with {Mn_ABus [31:30] + Be >= 6 && Mn_ABus [31:30] + Be <= 12 && Mn_busLock == 0 && Mn_seqAddr == 0 && Mn_request == 0 && Mn_select == 1;}; } //-- Task to provide data according to the //-- status of MnABus [31:30] + Be OpbMasterDynamicBusSizing (tr); //-- Indicate LockDeAsserted = 1; } else { //-- This spl condition activates some specific check //-- on the data bus //-- %% Constraint %% if (this.SplCond) { tr.randomize () with {Mn_ABus [31:30] + Be >= 6 && Mn_ABus [31:30] + Be <= 12 && Mn_select == 1 ;}; } //-- Task to provide data according to the //-- status of MnABus [31:30] + Be OpbMasterDynamicBusSizing (tr); } //-- Now drive the transaction OpbMasterDriveTransaction(tr); SlaveFeedBack = OpbMasterWaitForAck(); //-- Now Work according to the feedback if ( SlaveFeedBack == 0 ) { //-- Normal Ack //-- Adjust the counter TransferNumber = TransferNumber - 1; LatencyCounter = LatencyCounter + 1; //-- Added for PendReq if (LatencyCounter >= this.MasterInfo.LatencyRegister) { if ( OPB_pendReqn == 1 ) { //-- Just de-assert Lock and Req //-- Drive Select Reset Values //-- %% Constraint %% tr.randomize () with { Mn_busLock == 0 && Mn_request == 0;}; LatencyCounter = 0; //-- Drive Master Response OpbMasterDriveTransaction(tr); } SeqAddr = 0; MasterState = `MASTIDLE; } if ( tr.Mn_RNW ) { //-- Take the read data Opb_DBus = this.iport.$OPB_DBus; //-- Assign to tr tr.Mn_DBus = Opb_DBus; } if ( TransferNumber == 0 && LockDeAsserted ) { //-- If not granted drive reset if ( !this.iport.$OPB_MnGrant ) { //-- Drive reset values //-- %% Constraint %% tr.randomize () with { Mn_hwXfer == 0 && Mn_fwXfer == 0 && Mn_dwXfer == 0 && Mn_request == 0 && Mn_select == 0 && Mn_BE == 0 && Mn_DBusEn == 0 && Mn_DBusEn32_63 == 0 && Mn_busLock == 0 && Mn_seqAddr == 0;}; OpbMasterResetDrive(tr); this.iport.$Mn_request = 1'b0; } SeqAddr = 0; MasterState = `MASTIDLE; } else if ( TransferNumber == 0 && !LockDeAsserted ) { //-- Normal case OpbMasterResetDrive(tr); this.iport.$Mn_request = 1'b0; SeqAddr = 0; @(posedge this.iport.$Mn_clk); //-- Penalty Cycle MasterState = `MASTIDLE; } else { //-- Wait For Transaction tr = OpbMasterWaitForTransaction(); tr.Mn_busLock = 1; //-- Change the master State MasterState = `MASTLOCK; } } else if ( SlaveFeedBack == 1 ) { //-- Retry //-- Drive Select Reset Values //-- %% Constraint %% tr.randomize () with {Mn_select == 0 && Mn_busLock == 0 && Mn_request == 0;}; //-- Drive Master Response to Arbiter Timeout OpbMasterDriveTransaction(tr); //-- Advance the clock @(posedge this.iport.$Mn_clk); SeqAddr = 0; //-- Change the master State MasterState = `MASTIDLE; } else if ( SlaveFeedBack == 2 ) { //-- Timeout //-- Drive Select Reset Values //-- %% Constraint %% tr.randomize () with {Mn_select == 0 && Mn_busLock == 0 && Mn_request == 0;}; //-- Drive Master Response to Arbiter Timeout OpbMasterDriveTransaction(tr); //-- Advance the clock @(posedge this.iport.$Mn_clk); //-- Change the master State MasterState = `MASTLOCK; } else if ( SlaveFeedBack == 3 ) { //-- Master Abort //-- Drive Select Reset Values //-- %% Constraint %% tr.randomize () with {Mn_select == 0 && Mn_busLock == 0;}; //-- Drive Master Response to Arbiter Timeout OpbMasterDriveTransaction(tr); //-- Advance the clock @(posedge this.iport.$Mn_clk); //-- Change the master State MasterState = `MASTIDLE; } } } } } } //--**************************************************************** //-- This task actually calculates which width to be used in a //-- transfer according to its Width //--**************************************************************** task Opb_driver::OpbMasterTransferWidthCalculate(Opb_bus_transaction tr) { if ( `HWMASTER ) { this.HWORD = 1; this.FWORD = 0; this.DWORD = 0; tr.Mn_hwXfer = 'b1; tr.Mn_fwXfer = 'b0; tr.Mn_dwXfer = 'b0; //-- Set the Master's Capacity required for Dynamic Sizing MSize = 16; } else if ( `FWMASTER ) { this.HWORD = 0; this.FWORD = 1; this.DWORD = 0; tr.Mn_hwXfer = 'b1; tr.Mn_fwXfer = 'b1; tr.Mn_dwXfer = 'b0; //-- Set the Master's Capacity required for Dynamic Sizing MSize = 32; } else if ( `DWMASTER ) { this.HWORD = 0; this.FWORD = 0; this.DWORD = 1; tr.Mn_hwXfer = 'b1; tr.Mn_fwXfer = 'b1; tr.Mn_dwXfer = 'b1; //-- Set the Master's Capacity required for Dynamic Sizing MSize = 64; } else { this.HWORD = 0; this.FWORD = 0; this.DWORD = 0; tr.Mn_hwXfer = 'b0; tr.Mn_fwXfer = 'b0; tr.Mn_dwXfer = 'b0; //-- Set the Master's Capacity required for Dynamic Sizing MSize = 8; } } //--************************************************************** //-- Function which returns the SlaveFeedBack during a transaction //--************************************************************** function integer Opb_driver::OpbMasterWaitForAck() { while ( 1 ) { //-- First Advance the Clock @(posedge this.iport.$Mn_clk); //-- If master wants to abort if ( !( random() % 21 ) ) { OpbMasterWaitForAck = 3; return; } //-- then Sample Input OpbMasterInputSample(); //-- If normal Ack if ( this.Opb_xferAck ) { OpbMasterWaitForAck = 0; return; } //-- If Retry else if ( this.Opb_retry ) { OpbMasterWaitForAck = 1; return; } //-- If timeout occurs else if ( this.Opb_timeout ) { OpbMasterWaitForAck = 2; return; } } } //--************************************************************** //-- This is the task for Reset Drive. //--************************************************************** task Opb_driver::reset_xactor(integer rst_type = 0) { super.reset_xactor(rst_type); this.in_chan.flush(); this.iport.$Mn_ABus = 0; this.iport.$Mn_DBus = 0; this.iport.$Mn_DBusEn = 0; this.iport.$Mn_UABus = 0; this.iport.$Mn_RNW = 0; }