| // |
| // ------------------------------------------------------------- |
| // Copyright 2011 Synopsys, Inc. |
| // All Rights Reserved Worldwide |
| // |
| // Licensed under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in |
| // compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in |
| // writing, software distributed under the License is |
| // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR |
| // CONDITIONS OF ANY KIND, either express or implied. See |
| // the License for the specific language governing |
| // permissions and limitations under the License. |
| // ------------------------------------------------------------- |
| // |
| |
| |
| `include "reg_model.svh" |
| |
| |
| class rx_isr_seq extends uvm_reg_sequence; |
| reg_dut regmodel; |
| |
| `uvm_object_utils(rx_isr_seq) |
| |
| function new(string name = ""); |
| super.new(name); |
| endfunction |
| |
| task pre_body(); |
| $cast(regmodel, model); |
| endtask |
| |
| task body; |
| uvm_status_e status; |
| |
| // Keep reading data until FIFO is empty |
| while (!regmodel.IntSrc.RxEmpty.get()) begin |
| regmodel.TxRx.mirror(status); |
| regmodel.IntSrc.mirror(status); |
| end |
| endtask |
| endclass |
| |
| |
| |
| typedef virtual tb_ctl_if tb_ctl_vif; |
| |
| class tb_env extends uvm_env; |
| |
| tb_ctl_vif vif; |
| |
| apb_agent apb; |
| reg_dut regmodel; |
| vip_sequencer tx_src; |
| uvm_seq_item_pull_port#(vip_tr) tx_src_seq_port; |
| |
| vip_agent vip; |
| |
| sym_sb ingress; // VIP->DUT |
| sym_sb egress; // DUT->VIP |
| apb2txrx adapt; |
| |
| `uvm_component_utils(tb_env) |
| |
| local uvm_status_e status; |
| local uvm_reg_data_t data; |
| local bit [1:0] m_isr; |
| typedef enum {TX_ISR, RX_ISR} m_isr_bit_e; |
| |
| function new(string name, uvm_component parent = null); |
| super.new(name, parent); |
| endfunction |
| |
| |
| function void build_phase(uvm_phase phase); |
| if (!uvm_config_db#(tb_ctl_vif)::get(this, "", "vif", vif)) begin |
| `uvm_fatal("TB/ENV/NOVIF", "No virtual interface specified for environment instance") |
| end |
| |
| apb = apb_agent::type_id::create("apb", this); |
| if (regmodel == null) begin |
| regmodel = reg_dut::type_id::create("regmodel",,get_full_name()); |
| regmodel.build(); |
| regmodel.lock_model(); |
| end |
| tx_src = vip_sequencer::type_id::create("tx_src", this); |
| tx_src_seq_port = new("tx_src_seq_port", this); |
| |
| vip = vip_agent::type_id::create("vip", this); |
| |
| ingress = sym_sb::type_id::create("ingress", this); |
| egress = sym_sb::type_id::create("egress", this); |
| adapt = apb2txrx::type_id::create("adapt", this); |
| |
| uvm_config_db #(uvm_object_wrapper)::set(this, "vip.sqr.main_phase", |
| "default_sequence", |
| vip_sentence_seq::type_id::get()); |
| uvm_config_db #(uvm_object_wrapper)::set(this, "tx_src.main_phase", |
| "default_sequence", |
| vip_sentence_seq::type_id::get()); |
| |
| m_isr = 0; |
| endfunction |
| |
| function void connect_phase(uvm_phase phase); |
| if (regmodel.get_parent() == null) begin |
| reg2apb_adapter reg2apb = new; |
| regmodel.default_map.set_sequencer(apb.sqr,reg2apb); |
| regmodel.default_map.set_auto_predict(1); |
| end |
| |
| tx_src_seq_port.connect(tx_src.seq_item_export); |
| |
| apb.mon.ap.connect(adapt.apb); |
| |
| vip.tx_mon.ap.connect(ingress.expected); |
| vip.rx_mon.ap.connect(egress.observed); |
| adapt.tx_ap.connect(egress.expected); |
| adapt.rx_ap.connect(ingress.observed); |
| endfunction |
| |
| |
| local bit m_in_shutdown = 0; |
| local process pull_from_RxFIFO_thread; |
| function void phase_started(uvm_phase phase); |
| string name = phase.get_name(); |
| |
| m_in_shutdown = 0; |
| |
| case (name) |
| "reset": |
| // OK to jump back to reset phase |
| if (pull_from_RxFIFO_thread != null) begin |
| pull_from_RxFIFO_thread.kill(); |
| pull_from_RxFIFO_thread = null; |
| end |
| |
| "main": |
| fork |
| begin |
| pull_from_RxFIFO_thread = process::self(); |
| pull_from_RxFIFO(phase); |
| end |
| join_none |
| |
| "shutdown": |
| m_in_shutdown = 1; |
| |
| endcase |
| endfunction |
| |
| |
| function void phase_ended(uvm_phase phase); |
| uvm_phase goto = phase.get_jump_target(); |
| |
| // This environment supports jump to RESET *only* |
| if (goto != null) begin |
| if (goto.get_name() != "reset") begin |
| `uvm_fatal("ENV/BADJMP", $sformatf("Environment does not support jumping to phase %s from phase %s. Only jumping to \"reset\" is supported", |
| goto.get_name(), phase.get_name())) |
| end |
| end |
| |
| case (phase.get_name()) |
| "main": |
| m_isr[TX_ISR] = 0; |
| |
| "shutdown": |
| begin |
| pull_from_RxFIFO_thread = null; |
| m_in_shutdown = 0; |
| end |
| endcase |
| endfunction |
| |
| |
| task pre_reset_phase(uvm_phase phase); |
| phase.raise_objection(this, "Waiting for reset to be valid"); |
| wait (vif.rst !== 1'bx); |
| phase.drop_objection(this, "Reset is no longer X"); |
| endtask |
| |
| |
| task reset_phase(uvm_phase phase); |
| phase.raise_objection(this, "Asserting reset for 10 clock cycles"); |
| |
| `uvm_info("TB/TRACE", "Resetting DUT...", UVM_NONE); |
| |
| vif.rst = 1'b1; |
| regmodel.reset(); |
| vip.reset_and_suspend(); |
| repeat (10) @(posedge vif.clk); |
| vif.rst = 1'b0; |
| vip.resume(); |
| |
| m_isr = 0; |
| tx_src.stop_sequences(); |
| |
| phase.drop_objection(this, "HW reset done"); |
| endtask |
| |
| |
| task pre_configure_phase(uvm_phase phase); |
| phase.raise_objection(this, "Letting the interfaces go idle"); |
| |
| `uvm_info("TB/TRACE", "Configuring DUT...", UVM_NONE); |
| |
| repeat (10) @(posedge vif.clk); |
| phase.drop_objection(this, "Ready to configure"); |
| endtask |
| |
| |
| task configure_phase(uvm_phase phase); |
| phase.raise_objection(this, "Programming DUT"); |
| regmodel.IntMask.SA.set(1); |
| |
| regmodel.TxStatus.TxEn.set(1); |
| regmodel.RxStatus.RxEn.set(1); |
| |
| regmodel.update(status); |
| |
| phase.drop_objection(this, "Everything is ready to go"); |
| endtask |
| |
| |
| task pre_main_phase(uvm_phase phase); |
| phase.raise_objection(this, "Waiting for VIPs and DUT to acquire SYNC"); |
| |
| `uvm_info("TB/TRACE", "Synchronizing interfaces...", UVM_NONE); |
| |
| fork |
| begin |
| repeat (100 * 8) @(posedge vif.sclk); |
| `uvm_fatal("TB/TIMEOUT", |
| "VIP and/or DUT failed to acquire syncs") |
| end |
| join_none |
| |
| // Wait until the VIP has acquired symbol syncs |
| while (!vip.rx_mon.is_in_sync()) begin |
| vip.rx_mon.wait_for_sync_change(); |
| end |
| while (!vip.tx_mon.is_in_sync()) begin |
| vip.tx_mon.wait_for_sync_change(); |
| end |
| |
| // Wait until the DUT has acquired symbol sync |
| regmodel.RxStatus.mirror(status); |
| if (!regmodel.RxStatus.Align.get()) begin |
| regmodel.IntMask.set('h000); |
| regmodel.IntMask.SA.set('b1); |
| regmodel.IntMask.update(status); |
| wait (vif.intr); |
| end |
| regmodel.IntMask.write(status, 'h000); |
| regmodel.IntSrc.write(status, -1); |
| |
| phase.drop_objection(this, "Everyone is in SYNC"); |
| endtask |
| |
| |
| // |
| // This task is a thread that will span the main and shutdown phase |
| // |
| task pull_from_RxFIFO(uvm_phase phase); |
| uvm_phase shutdown_ph = phase.find_by_name("shutdown"); |
| shutdown_ph.raise_objection(this, "Pulling data from RxFIFO"); |
| |
| forever begin |
| m_isr[RX_ISR] = 0; |
| |
| // When in shutdown, don't wait for the interrupt |
| wait (vif.intr || m_in_shutdown); |
| |
| m_isr[RX_ISR] = 1; |
| |
| regmodel.IntSrc.mirror(status); |
| if (regmodel.IntSrc.SA.get()) begin |
| `uvm_fatal("TB/DUT/SYNCLOSS", "DUT has lost SYNC"); |
| end |
| if (!regmodel.IntSrc.RxHigh.get() && !m_in_shutdown) begin |
| m_isr[RX_ISR] = 0; |
| wait (!m_isr); |
| continue; |
| end |
| |
| begin |
| uvm_reg_sequence rx_seq = rx_isr_seq::type_id::create("rx_seq",,get_full_name()); |
| rx_seq.model = regmodel; |
| rx_seq.start(null); |
| end |
| m_isr[RX_ISR] = 0; |
| |
| if (m_in_shutdown) begin |
| shutdown_ph.drop_objection(this, "All data pulled from RxFIFO"); |
| break; |
| end |
| end |
| endtask |
| |
| |
| task main_phase(uvm_phase phase); |
| |
| `uvm_info("TB/TRACE", "Applying primary stimulus...", UVM_NONE); |
| |
| fork |
| begin |
| uvm_objection obj; |
| #2000000; |
| obj = phase.get_objection(); |
| obj.display_objections(); |
| $finish; |
| end |
| begin |
| uvm_objection ph_obj = phase.get_objection(); |
| |
| phase.raise_objection(this, "Configuring ISR"); |
| regmodel.IntMask.set(0); |
| regmodel.IntMask.SA.set(1); |
| regmodel.IntMask.RxHigh.set(1); |
| regmodel.IntMask.TxLow.set(1); |
| regmodel.IntMask.update(status); |
| |
| forever begin |
| phase.drop_objection(this, "ISR ready DUT-> stimulus"); |
| |
| m_isr[TX_ISR] = 0; |
| |
| wait (vif.intr); |
| |
| m_isr[TX_ISR] = 1; |
| phase.raise_objection(this, "Applying DUT-> stimulus"); |
| |
| regmodel.IntSrc.mirror(status); |
| if (!regmodel.IntSrc.TxLow.get()) begin |
| m_isr[TX_ISR] = 0; |
| wait (!m_isr); |
| continue; |
| end |
| |
| m_isr[TX_ISR] = 0; |
| regmodel.IntMask.TxLow.set(0); |
| regmodel.IntMask.update(status); |
| |
| // Stop supplying data once it is full |
| // or the egress scoreboard has had enough |
| while (!regmodel.IntSrc.TxFull.get() > 0) begin |
| vip_tr tr; |
| |
| phase.drop_objection(this, "Waiting for DUT-> stimulus from tx_src sequencer"); |
| tx_src_seq_port.get_next_item(tr); |
| tx_src_seq_port.item_done(); |
| phase.raise_objection(this, "Applying DUT-> stimulus from tx_src sequencer"); |
| regmodel.TxRx.write(status, tr.chr); |
| |
| regmodel.IntSrc.mirror(status); |
| end |
| |
| regmodel.IntMask.TxLow.set(1); |
| regmodel.IntMask.update(status); |
| end |
| end |
| join |
| endtask |
| |
| |
| task shutdown_phase(uvm_phase phase); |
| phase.raise_objection(this, "Draining the DUT"); |
| |
| `uvm_info("TB/TRACE", "Draining the DUT...", UVM_NONE); |
| |
| if (!regmodel.IntSrc.TxEmpty.get()) begin |
| // Wait for TxFIFO to be empty |
| regmodel.IntMask.write(status, 'h001); |
| wait (vif.intr); |
| end |
| // Make sure the last symbol is transmitted |
| repeat (16) @(posedge vif.sclk); |
| |
| phase.drop_objection(this, "DUT is empty"); |
| endtask |
| |
| |
| function void report_phase(uvm_phase phase); |
| uvm_report_server svr; |
| svr = _global_reporter.get_report_server(); |
| |
| if (svr.get_severity_count(UVM_FATAL) + |
| svr.get_severity_count(UVM_ERROR) == 0) |
| $write("** UVM TEST PASSED **\n"); |
| else |
| $write("!! UVM TEST FAILED !!\n"); |
| endfunction |
| endclass |