diff --git a/dv/uvm/core_ibex/common/irq_agent/irq_request_driver.sv b/dv/uvm/core_ibex/common/irq_agent/irq_request_driver.sv index 27859b01..7e138062 100644 --- a/dv/uvm/core_ibex/common/irq_agent/irq_request_driver.sv +++ b/dv/uvm/core_ibex/common/irq_agent/irq_request_driver.sv @@ -6,7 +6,7 @@ class irq_request_driver extends uvm_driver #(irq_seq_item); // The virtual interface used to drive and view HDL signals. protected virtual irq_if vif; -`uvm_component_utils(irq_request_driver) + `uvm_component_utils(irq_request_driver) `uvm_component_new function void build_phase(uvm_phase phase); @@ -21,6 +21,8 @@ class irq_request_driver extends uvm_driver #(irq_seq_item); wait (vif.driver_cb.reset === 1'b0); forever begin fork : drive_irq + // Setup a single get_REQ -> drive -> send_RSP long-running task. + // This seq_item contains all signals on the interface at once. get_and_drive(); wait (vif.driver_cb.reset === 1'b1); join_any @@ -42,6 +44,8 @@ class irq_request_driver extends uvm_driver #(irq_seq_item); reset_signals(); endtask + // Every cycle, check for a new REQ and drive it. + // Simultaneously, return the same REQ as a RSP back to the sequence. virtual protected task get_and_drive(); forever begin seq_item_port.try_next_item(req); diff --git a/dv/uvm/core_ibex/env/core_ibex_vseqr.sv b/dv/uvm/core_ibex/env/core_ibex_vseqr.sv index ba981efc..7f6e6e5f 100644 --- a/dv/uvm/core_ibex/env/core_ibex_vseqr.sv +++ b/dv/uvm/core_ibex/env/core_ibex_vseqr.sv @@ -9,7 +9,7 @@ class core_ibex_vseqr extends uvm_sequencer; ibex_mem_intf_response_sequencer data_if_seqr; ibex_mem_intf_response_sequencer instr_if_seqr; - irq_request_sequencer irq_seqr; + irq_request_sequencer irq_seqr; `uvm_component_utils(core_ibex_vseqr) `uvm_component_new diff --git a/dv/uvm/core_ibex/tests/core_ibex_base_test.sv b/dv/uvm/core_ibex/tests/core_ibex_base_test.sv index 2462bce1..83017d85 100644 --- a/dv/uvm/core_ibex/tests/core_ibex_base_test.sv +++ b/dv/uvm/core_ibex/tests/core_ibex_base_test.sv @@ -210,11 +210,16 @@ class core_ibex_base_test extends uvm_test; virtual task wait_for_mem_txn(input bit[ibex_mem_intf_agent_pkg::ADDR_WIDTH-1:0] ref_addr, input signature_type_t ref_type); ibex_mem_intf_seq_item mem_txn; + `uvm_info(`gfn, $sformatf("Awaiting riscv-dv handshake at 0x%0h, Type : %0s", + ref_addr, ref_type), UVM_HIGH) forever begin // The first write to this address is guaranteed to contain the signature type in bits [7:0] item_collected_port.get(mem_txn); - if (mem_txn.addr == ref_addr && mem_txn.data[7:0] === ref_type && + if (mem_txn.addr == ref_addr && + mem_txn.data[7:0] === ref_type && mem_txn.read_write == WRITE) begin + `uvm_info(`gfn, $sformatf("riscv-dv handshake received at 0x%0h, Type : %0s", + ref_addr, ref_type), UVM_HIGH) signature_data = mem_txn.data; case (ref_type) // The very first write to the signature address in every test is guaranteed to be a write diff --git a/dv/uvm/core_ibex/tests/core_ibex_new_seq_lib.sv b/dv/uvm/core_ibex/tests/core_ibex_new_seq_lib.sv index baa1603b..5c793a64 100644 --- a/dv/uvm/core_ibex/tests/core_ibex_new_seq_lib.sv +++ b/dv/uvm/core_ibex/tests/core_ibex_new_seq_lib.sv @@ -1,72 +1,87 @@ -typedef enum bit [1:0] { - SingleRun, // Singl iteration - InfiniteRuns, // Run forever until stop is specified - MultipleRuns, // Multiple runs with configurable or randomizable iteration count - InvalidRuns // Default state - } run_type_e; - -typedef enum bit [1:0] { - IsideErr, // Inject error in instruction side memory. - DsideErr, // Inject error in data side memory. - PickErr // Pick which memory to inject error in. - } error_type_e; +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 class core_base_new_seq #(type REQ = uvm_sequence_item) extends uvm_sequence #(REQ); - // Default set to 50% for zero delay to be picked. Set to 100 if zero delay is required always. - int unsigned zero_delay_pct = 50; - rand bit zero_delays; - rand int delay = 0; - int unsigned iteration_cnt = 0; - int unsigned max_delay = 500; - virtual clk_rst_if clk_vif; + + // Virtual interfaces for driving stimulus directly + virtual clk_rst_if clk_vif; + virtual core_ibex_dut_probe_if dut_vif; + bit stop_seq; bit seq_finished; - rand run_type_e num_of_iterations = InvalidRuns; - virtual core_ibex_dut_probe_if dut_vif; - // Use this bit to start any unique sequence once - bit start_seq = 0; + + rand bit zero_delays; + // CONTROL_KNOB: Randomly override the delay between stimulus items to be zero + // Default set to 50% for zero delay to be picked. Set to 100 if zero delay is required always. + int unsigned zero_delay_pct = 50; + constraint zero_delays_c { + zero_delays dist {1 :/ zero_delay_pct, + 0 :/ 100 - zero_delay_pct}; + } + + rand int unsigned stimulus_delay_cycles; + // CONTROL_KNOB: Delay the start of each stimulus a randomized amount (transaction to transaction delay) + // Can be randomly overwritten to no delay at all with 'zero_delays' + int unsigned stimulus_delay_cycles_min = 200; + int unsigned stimulus_delay_cycles_max = 400; + constraint reasonable_delay_c { + stimulus_delay_cycles inside {[stimulus_delay_cycles_min : stimulus_delay_cycles_max]}; + } + + // CONTROL_KNOB: Set this per-instance to make the stimulus generation repeat : {Once, Multiple, Forever} + run_type_e iteration_modes = MultipleRuns; + + rand int unsigned iteration_cnt; + // CONTROL_KNOB: This controls the number of stimulus items generated in a loop for {Multiple} cfg + int unsigned iteration_cnt_max = 20; + constraint iterations_cnt_c { + iteration_cnt inside {[1:iteration_cnt_max]}; + } `uvm_object_param_utils(core_base_new_seq#(REQ)) function new (string name = ""); super.new(name); if(!uvm_config_db#(virtual clk_rst_if)::get(null, "", "clk_if", clk_vif)) begin - `uvm_fatal(get_full_name(), "Cannot get clk_if") + `uvm_fatal(`gfn, "Cannot get clk_if") end if (!uvm_config_db#(virtual core_ibex_dut_probe_if)::get(null, "", "dut_if", dut_vif)) begin - `uvm_fatal(get_full_name(), "Cannot get dut_if") + `uvm_fatal(`gfn, "Cannot get dut_if") end endfunction - constraint zero_delays_c { - zero_delays dist {1 :/ zero_delay_pct, - 0 :/ 100 - zero_delay_pct}; - } - constraint reasonable_delay_c { - delay inside {[0 : max_delay]}; - } + virtual task pre_body(); + // Randomize once before starting to ensure all unininitialized rand variables have a valid starting value + this.randomize(); + endtask: pre_body virtual task body(); - if(num_of_iterations == InvalidRuns) begin - `DV_CHECK_MEMBER_RANDOMIZE_WITH_FATAL(num_of_iterations, num_of_iterations != InvalidRuns;) - end - `uvm_info(`gfn, $sformatf("Doing %s", num_of_iterations.name()), UVM_LOW) - case (num_of_iterations) + // core_base_new_seq::body() provides a flexible sequence scheduler, where + // 'iteration_modes' allows different tests to change the frequency + // stimulus is generated. + + `uvm_info(`gfn, $sformatf("Running the \"%s\" schedule for stimulus generation", + iteration_modes.name()), UVM_LOW) + case (iteration_modes) SingleRun: begin - drive_stimulus(delay); + drive_stimulus(); end MultipleRuns: begin + // Randomize iteration_cnt if not specified externally by test if(iteration_cnt == 0) begin `DV_CHECK_MEMBER_RANDOMIZE_WITH_FATAL(iteration_cnt, - iteration_cnt > 0 && iteration_cnt <= 20; ) + // with {"CONSTRAINTS"} + iteration_cnt > 0;) end + `uvm_info(`gfn, $sformatf("Number of stimulus iterations = %0d", iteration_cnt), UVM_LOW) for (int i = 0; i <= iteration_cnt; i++) begin - drive_stimulus(delay); + drive_stimulus(); end end InfiniteRuns: begin while (!stop_seq) begin - drive_stimulus(delay); + drive_stimulus(); end seq_finished = 1'b1; end @@ -76,28 +91,30 @@ class core_base_new_seq #(type REQ = uvm_sequence_item) extends uvm_sequence #(R endcase endtask: body - task drive_stimulus(int unsigned delay); - if(delay == 0) begin - `DV_CHECK_MEMBER_RANDOMIZE_FATAL(zero_delays) - if(zero_delays) begin - delay = 0; - end else begin - `DV_CHECK_MEMBER_RANDOMIZE_FATAL(delay) - end + task drive_stimulus(); + `DV_CHECK_MEMBER_RANDOMIZE_FATAL(zero_delays) + if(!zero_delays) begin + // Delay for a randomized amount of cycles + `DV_CHECK_MEMBER_RANDOMIZE_FATAL(stimulus_delay_cycles) + `uvm_info(`gfn, $sformatf("stimulus_delay_cycles = %0d", stimulus_delay_cycles), UVM_HIGH) + clk_vif.wait_clks(stimulus_delay_cycles); end - clk_vif.wait_clks(delay); `uvm_info(get_full_name(), "Starting sequence...", UVM_MEDIUM) send_req(); `uvm_info(get_full_name(), "Exiting sequence", UVM_MEDIUM) endtask: drive_stimulus + // Generate the stimulus with a sequence-specific request + // Seqs can each implement send_req() differently, such as sending a seq_item, + // or driving the interface directly. virtual task send_req(); `uvm_fatal(get_full_name(), "This task must be implemented in the extended class") endtask + // Can be called by the sequencer to break out of infinite-loops in body() virtual task stop(); stop_seq = 1'b1; - `uvm_info(get_full_name(), "Stopping sequence", UVM_MEDIUM) + `uvm_info(`gfn, "Stopping sequence", UVM_MEDIUM) wait (seq_finished == 1'b1); endtask @@ -108,13 +125,16 @@ class irq_new_seq extends core_base_new_seq #(irq_seq_item); `uvm_object_utils(irq_new_seq) `uvm_object_new + // Set these 'no_*' bits at the test-level to disable the randomizer from + // generating each particular type of irq. bit no_nmi; bit no_fast; bit no_external; bit no_timer; bit no_software; - int max_delay = 500; - int min_delay = 50; + + int unsigned max_delay = 500; + int unsigned min_delay = 50; rand int interval = min_delay; @@ -125,29 +145,25 @@ class irq_new_seq extends core_base_new_seq #(irq_seq_item); virtual task send_req(); irq_seq_item irq; irq = irq_seq_item::type_id::create("irq"); - // Raise interrupts + + // Raise randomized num of interrupts start_item(irq); - `DV_CHECK_RANDOMIZE_WITH_FATAL(irq, num_of_interrupt > 1; - if (no_nmi) { - irq_nm == 0; - } - if (no_fast) { - irq_fast == '0; - } - if (no_external) { - irq_external == 0; - } - if (no_timer) { - irq_timer == 0; - } - if (no_software) { - irq_software == 0; - }) + `DV_CHECK_RANDOMIZE_WITH_FATAL(irq, + // with {"CONSTRAINTS"} + num_of_interrupt inside {[1:5]}; + no_nmi -> irq_nm == 0; + no_fast -> irq_fast == '0; + no_external -> irq_external == 0; + no_timer -> irq_timer == 0; + no_software -> irq_software == 0;) finish_item(irq); get_response(irq); + + // Delay a randomized amount while interrupts are active `DV_CHECK_MEMBER_RANDOMIZE_FATAL(interval) clk_vif.wait_clks(interval); - // Drop interrupts + + // Drop all interrupts start_item(irq); `DV_CHECK_RANDOMIZE_WITH_FATAL(irq, num_of_interrupt == 0;) finish_item(irq); @@ -163,23 +179,24 @@ class debug_new_seq extends core_base_new_seq#(irq_seq_item); `uvm_object_utils(debug_new_seq) `uvm_object_new - int max_delay = 500; - int min_delay = 75; - rand int unsigned drop_delay = 0; + rand int unsigned pulse_length_cycles; + int unsigned pulse_length_cycles_min = 75; + int unsigned pulse_length_cycles_max = 500; + + constraint reasonable_pulse_length_c { + pulse_length_cycles inside {[pulse_length_cycles_min : pulse_length_cycles_max]}; + } virtual task body(); dut_vif.dut_cb.debug_req <= 1'b0; - if(drop_delay == 0) begin - `DV_CHECK_MEMBER_RANDOMIZE_WITH_FATAL(drop_delay, - drop_delay inside {[min_delay : max_delay]};) - end super.body(); - endtask + endtask: body virtual task send_req(); - `uvm_info(get_full_name(), "Sending debug request", UVM_HIGH) + `uvm_info(`gfn, "Sending debug request", UVM_HIGH) dut_vif.dut_cb.debug_req <= 1'b1; - clk_vif.wait_clks(drop_delay); + `DV_CHECK_MEMBER_RANDOMIZE_FATAL(pulse_length_cycles); + clk_vif.wait_clks(pulse_length_cycles); dut_vif.dut_cb.debug_req <= 1'b0; endtask @@ -187,8 +204,10 @@ endclass class memory_error_seq extends core_base_new_seq#(irq_seq_item); core_ibex_vseq vseq; - rand error_type_e err_type = PickErr; rand bit choose_side; + bit start_seq = 0; // Use this bit to start any unique sequence once + + rand error_type_e err_type = PickErr; `uvm_object_utils(memory_error_seq) `uvm_declare_p_sequencer(core_ibex_vseqr) @@ -233,26 +252,28 @@ class fetch_enable_seq extends core_base_new_seq#(irq_seq_item); ibex_pkg::fetch_enable_t fetch_enable; int unsigned on_bias_pc = 50; - int max_delay = 500; - int min_delay = 75; + int unsigned max_delay = 500; + int unsigned min_delay = 75; rand int unsigned off_delay = 0; virtual task body(); dut_vif.dut_cb.fetch_enable <= ibex_pkg::FetchEnableOn; if(off_delay == 0) begin `DV_CHECK_MEMBER_RANDOMIZE_WITH_FATAL(off_delay, - off_delay inside {[min_delay : max_delay]};) + // with {"CONSTRAINTS"} + off_delay inside {[min_delay : max_delay]};) end super.body(); - endtask + endtask: body virtual task send_req(); - `uvm_info(get_full_name(), "Sending fetch enable request", UVM_LOW) `DV_CHECK_MEMBER_RANDOMIZE_WITH_FATAL(fetch_enable, - fetch_enable dist {ibex_pkg::FetchEnableOn :/ on_bias_pc, - [0:15] :/ 100 - on_bias_pc}; - ) + // with {"CONSTRAINTS"} + fetch_enable dist {ibex_pkg::FetchEnableOn :/ on_bias_pc, + [0:15] :/ 100 - on_bias_pc};) + `uvm_info(`gfn, "Sending fetch enable request", UVM_LOW) `uvm_info(`gfn, $sformatf("fetch_enable = %d", fetch_enable), UVM_LOW) + dut_vif.dut_cb.fetch_enable <= fetch_enable; clk_vif.wait_clks(off_delay); dut_vif.dut_cb.fetch_enable <= ibex_pkg::FetchEnableOn; diff --git a/dv/uvm/core_ibex/tests/core_ibex_seq_lib.sv b/dv/uvm/core_ibex/tests/core_ibex_seq_lib.sv index adc72bda..bbb4cdfc 100644 --- a/dv/uvm/core_ibex/tests/core_ibex_seq_lib.sv +++ b/dv/uvm/core_ibex/tests/core_ibex_seq_lib.sv @@ -36,6 +36,10 @@ class core_base_seq #(type REQ = uvm_sequence_item) extends uvm_sequence#(REQ); end `DV_CHECK_MEMBER_RANDOMIZE_FATAL(delay) clk_vif.wait_clks(delay); + + // Loop around until 'stop_seq' is set to 1 externally, or "num_of_iterations" times, + // whichever is longer. + // Send a request() each time with a randomized interval between items. `uvm_info(get_full_name(), "Starting sequence...", UVM_LOW) if (!is_started) is_started = 1'b1; while (!stop_seq) begin @@ -98,17 +102,16 @@ class irq_raise_seq extends irq_base_seq; bit no_fast; virtual function void randomize_item(irq_seq_item irq); - `DV_CHECK_RANDOMIZE_WITH_FATAL(irq, num_of_interrupt > 1; - if (no_nmi) { - irq_nm == 0; - } - if (no_fast) { - irq_fast == '0; - }) + `DV_CHECK_RANDOMIZE_WITH_FATAL(irq, + // with {"CONSTRAINTS"} + num_of_interrupt > 1; + no_nmi -> irq_nm == 0; + no_fast -> irq_fast == '0;) endfunction endclass +// Irq sequence that raises a single interrupt input signal class irq_raise_single_seq extends irq_base_seq; `uvm_object_utils(irq_raise_single_seq) @@ -118,25 +121,26 @@ class irq_raise_single_seq extends irq_base_seq; bit no_fast; virtual function void randomize_item(irq_seq_item irq); - `DV_CHECK_RANDOMIZE_WITH_FATAL(irq, num_of_interrupt == 1; - if (no_nmi) { - irq_nm == 0; - } - if (no_fast) { - irq_fast == '0; - }) + `DV_CHECK_RANDOMIZE_WITH_FATAL(irq, + // with {"CONSTRAINTS"} + num_of_interrupt == 1; + no_nmi -> irq_nm == 0; + no_fast -> irq_fast == '0;) endfunction endclass +// Irq sequence that ONLY raises the nmi signal class irq_raise_nmi_seq extends irq_base_seq; `uvm_object_utils(irq_raise_nmi_seq) `uvm_object_new virtual function void randomize_item(irq_seq_item irq); - `DV_CHECK_RANDOMIZE_WITH_FATAL(irq, num_of_interrupt == 1; - irq_nm == 1;) + `DV_CHECK_RANDOMIZE_WITH_FATAL(irq, + // with {"CONSTRAINTS"} + num_of_interrupt == 1; + irq_nm == 1;) endfunction endclass diff --git a/dv/uvm/core_ibex/tests/core_ibex_test_pkg.sv b/dv/uvm/core_ibex/tests/core_ibex_test_pkg.sv index c1b26d76..bb4ebac0 100644 --- a/dv/uvm/core_ibex/tests/core_ibex_test_pkg.sv +++ b/dv/uvm/core_ibex/tests/core_ibex_test_pkg.sv @@ -25,6 +25,19 @@ package core_ibex_test_pkg; typedef class core_ibex_vseq; + // For new_seq_lib... + + typedef enum bit [1:0] { + SingleRun, // Single iteration + InfiniteRuns, // Run forever until stop is specified + MultipleRuns // Multiple runs with configurable or randomizable iteration count + } run_type_e; + typedef enum bit [1:0] { + IsideErr, // Inject error in instruction side memory. + DsideErr, // Inject error in data side memory. + PickErr // Pick which memory to inject error in. + } error_type_e; + `include "core_ibex_report_server.sv" `include "core_ibex_seq_lib.sv" `include "core_ibex_new_seq_lib.sv" diff --git a/dv/uvm/core_ibex/tests/core_ibex_vseq.sv b/dv/uvm/core_ibex/tests/core_ibex_vseq.sv index 135359e2..ea75f1c9 100644 --- a/dv/uvm/core_ibex/tests/core_ibex_vseq.sv +++ b/dv/uvm/core_ibex/tests/core_ibex_vseq.sv @@ -8,8 +8,8 @@ class core_ibex_vseq extends uvm_sequence; - ibex_mem_intf_response_seq instr_intf_seq; - ibex_mem_intf_response_seq data_intf_seq; + ibex_mem_intf_response_seq instr_intf_seq; + ibex_mem_intf_response_seq data_intf_seq; mem_model_pkg::mem_model mem; irq_raise_seq irq_raise_seq_h; irq_raise_single_seq irq_raise_single_seq_h; @@ -30,6 +30,16 @@ class core_ibex_vseq extends uvm_sequence; data_intf_seq.is_dmem_seq = 1'b1; endfunction + // Start the memory-model sequences, which run forever() loops to respond to bus events + virtual task pre_body(); + instr_intf_seq.m_mem = mem; + data_intf_seq.m_mem = mem; + fork + instr_intf_seq.start(p_sequencer.instr_if_seqr); + data_intf_seq.start(p_sequencer.data_if_seqr); + join_none + endtask // pre_body + virtual task body(); if (cfg.enable_irq_single_seq) begin irq_raise_single_seq_h = irq_raise_single_seq::type_id::create("irq_single_seq_h"); @@ -70,12 +80,6 @@ class core_ibex_vseq extends uvm_sequence; debug_seq_single_h.interval.rand_mode(0); debug_seq_single_h.interval = 0; end - instr_intf_seq.m_mem = mem; - data_intf_seq.m_mem = mem; - fork - instr_intf_seq.start(p_sequencer.instr_if_seqr); - data_intf_seq.start(p_sequencer.data_if_seqr); - join_none endtask virtual task stop();