|  | // Copyright lowRISC contributors. | 
|  | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | // CSR suite of sequences that do writes and reads to csrs | 
|  | // includes hw_reset, rw, bit_bash and aliasing tests for csrs, and mem_walk for uvm_mems | 
|  | // TODO: when mem backdoor is implemented, add uvm_mem_access_seq for backdoor rd | 
|  | // The sequences perform csr writes and reads and follow the standard csr test suite. If external | 
|  | // checker is enabled, then the external entity is required to update the mirrored value on | 
|  | // writes. If not enabled, the sequences themselves call predict function to update the mirrored | 
|  | // value. Consequently, the read values are checked against the mirrored value and not the | 
|  | // previously written value. This approach is better since it takes care of special | 
|  | // register and field access policies. Also, we use csr_rd_check task instead of csr_mirror to take | 
|  | // field exclusions into account. | 
|  | // | 
|  | // Csrs to be tested is accumulated and shuffled from the supplied reg models. | 
|  | // What / how many csrs to test can be further controlled in 3 ways - | 
|  | // 1. Externally add specific csrs to test_csrs queue (highest prio) | 
|  | // 2. Set num_test_csrs test a randomly picked set of csrs from the supplied models | 
|  | // 3. Set / pass via plusarg, num_csr_chunks / test_csr_chunk | 
|  | // | 
|  | // Exclusions are to be provided using the csr_excl_item item (see class for more details). | 
|  | class csr_base_seq extends uvm_reg_sequence #(uvm_sequence #(uvm_reg_item)); | 
|  | `uvm_object_utils(csr_base_seq) | 
|  |  | 
|  | uvm_reg_block   models[$]; | 
|  | uvm_reg         all_csrs[$]; | 
|  | uvm_reg         test_csrs[$]; | 
|  | csr_excl_item   m_csr_excl_item; | 
|  |  | 
|  | // By default, assume external checker (example, scoreboard) is turned off. If that is the case, | 
|  | // then writes are followed by call to predict function to update the mirrored value. Reads are | 
|  | // then checked against the mirrored value using csr_rd_check task. If external checker is | 
|  | // enabled, then we let the external checker do the predict and compare. | 
|  | // In either case, we should be able to do completely non-blocking writes and reads. | 
|  | bit external_checker = 1'b0; | 
|  |  | 
|  | // either use num_test_csrs or {test_csr_chunk, num_csr_chunks} to test slice of all csrs | 
|  | int num_test_csrs = 0; | 
|  | int test_csr_chunk = 1; | 
|  | int num_csr_chunks = 1; | 
|  |  | 
|  | `uvm_object_new | 
|  |  | 
|  | // pre_start | 
|  | virtual task pre_start(); | 
|  | super.pre_start(); | 
|  |  | 
|  | // create test_csrs list only if its empty | 
|  | if (test_csrs.size() == 0) set_csr_test_range(); | 
|  |  | 
|  | // create dummy m_csr_excl_item if not supplied | 
|  | if (m_csr_excl_item == null) begin | 
|  | `uvm_info(`gtn, "m_csr_excl_item is null, creating a dummy one locally", UVM_LOW) | 
|  | m_csr_excl_item = csr_excl_item::type_id::create("m_csr_excl_item"); | 
|  | end | 
|  | endtask | 
|  |  | 
|  | // post_start | 
|  | virtual task post_start(); | 
|  | super.post_start(); | 
|  | wait_no_outstanding_access(); | 
|  | test_csrs.delete(); | 
|  | endtask | 
|  |  | 
|  | function void set_csr_excl_item(csr_excl_item item); | 
|  | this.m_csr_excl_item = item; | 
|  | endfunction | 
|  |  | 
|  | // extract csrs and split and prune to a specified test_csr_chunk | 
|  | virtual function void set_csr_test_range(); | 
|  | int   start_idx; | 
|  | int   end_idx; | 
|  | int   chunk_size; | 
|  |  | 
|  | // extract all csrs from the model | 
|  | // TODO: add and use function here instead that allows pre filtering csrs | 
|  | all_csrs.delete(); | 
|  | foreach (models[i]) begin | 
|  | models[i].get_registers(all_csrs); | 
|  | end | 
|  |  | 
|  | if (num_test_csrs != 0) begin | 
|  | num_csr_chunks = all_csrs.size / num_test_csrs + 1; | 
|  | `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(test_csr_chunk, | 
|  | test_csr_chunk inside {[1:num_csr_chunks]};) | 
|  | end | 
|  | else begin | 
|  | // extract test_csr_chunk, num_csr_chunks from plusargs | 
|  | void'($value$plusargs("test_csr_chunk=%0d", test_csr_chunk)); | 
|  | void'($value$plusargs("num_csr_chunks=%0d", num_csr_chunks)); | 
|  | end | 
|  |  | 
|  | if (!(test_csr_chunk inside {[1:num_csr_chunks]})) begin | 
|  | `uvm_fatal(`gtn, $sformatf({{"invalid opt +test_csr_chunk=%0d, +num_csr_chunks=%0d "}, | 
|  | {"(1 <= test_csr_chunk <= num_csr_chunks)"}}, | 
|  | test_csr_chunk, num_csr_chunks)) | 
|  | end | 
|  | chunk_size = (num_test_csrs != 0) ? num_test_csrs : (all_csrs.size / num_csr_chunks + 1); | 
|  | start_idx = (test_csr_chunk - 1) * chunk_size; | 
|  | end_idx = test_csr_chunk * chunk_size; | 
|  | if (end_idx >= all_csrs.size()) | 
|  | end_idx = all_csrs.size() - 1; | 
|  |  | 
|  | test_csrs = all_csrs[start_idx:end_idx]; | 
|  | `uvm_info(`gtn, $sformatf("testing %0d csrs [%0d - %0d] in all supplied models", | 
|  | test_csrs.size(), start_idx, end_idx), UVM_MEDIUM) | 
|  | foreach (test_csrs[i]) begin | 
|  | `uvm_info(`gtn, $sformatf("test_csrs list: %0s, reset: 0x%0x", test_csrs[i].get_full_name(), | 
|  | test_csrs[i].get_mirrored_value()), UVM_HIGH) | 
|  | end | 
|  | test_csrs.shuffle(); | 
|  | endfunction | 
|  |  | 
|  | endclass | 
|  |  | 
|  | //-------------------------------------------------------------------------------------------------- | 
|  | // Class: csr_hw_reset_seq | 
|  | // Brief Description: This sequence reads all CSRs and checks it against the reset value provided | 
|  | // in the RAL specification. Note that this does not sufficiently qualify as the CSR HW reset test. | 
|  | // The 'full' CSR HW reset test is constructed externally by running the csr_write_seq below first, | 
|  | // issuing reset and only then running this sequence. | 
|  | //-------------------------------------------------------------------------------------------------- | 
|  | class csr_hw_reset_seq extends csr_base_seq; | 
|  | `uvm_object_utils(csr_hw_reset_seq) | 
|  |  | 
|  | `uvm_object_new | 
|  |  | 
|  | virtual task body(); | 
|  | foreach (test_csrs[i]) begin | 
|  | uvm_reg_data_t compare_mask; | 
|  |  | 
|  | // check if parent block or register is excluded from init check | 
|  | if (m_csr_excl_item.is_excl(test_csrs[i], CsrExclInitCheck, CsrHwResetTest)) begin | 
|  | `uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclInitCheck exclusion", | 
|  | test_csrs[i].get_full_name()), UVM_MEDIUM) | 
|  | continue; | 
|  | end | 
|  |  | 
|  | `uvm_info(`gtn, $sformatf("Verifying reset value of register %0s", | 
|  | test_csrs[i].get_full_name()), UVM_MEDIUM) | 
|  |  | 
|  | compare_mask = get_mask_excl_fields(test_csrs[i], CsrExclInitCheck, CsrHwResetTest, | 
|  | m_csr_excl_item); | 
|  | csr_rd_check(.ptr           (test_csrs[i]), | 
|  | .blocking      (0), | 
|  | .compare       (!external_checker), | 
|  | .compare_vs_ral(1'b1), | 
|  | .compare_mask  (compare_mask)); | 
|  | end | 
|  | endtask | 
|  |  | 
|  | endclass | 
|  |  | 
|  | //-------------------------------------------------------------------------------------------------- | 
|  | // Class: csr_write_seq | 
|  | // Brief Description: This sequence writes a random value to all CSRs. It does not perform any | 
|  | // checks. It is run as the first step of the CSR HW reset test. | 
|  | //-------------------------------------------------------------------------------------------------- | 
|  | class csr_write_seq extends csr_base_seq; | 
|  | `uvm_object_utils(csr_write_seq) | 
|  |  | 
|  | `uvm_object_new | 
|  |  | 
|  | virtual task body(); | 
|  | uvm_reg_data_t wdata; | 
|  |  | 
|  | foreach (test_csrs[i]) begin | 
|  | // check if parent block or register is excluded from write | 
|  | if (m_csr_excl_item.is_excl(test_csrs[i], CsrExclWrite, CsrHwResetTest)) begin | 
|  | `uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclWrite exclusion", | 
|  | test_csrs[i].get_full_name()), UVM_MEDIUM) | 
|  | continue; | 
|  | end | 
|  |  | 
|  | `uvm_info(`gtn, $sformatf("Writing random data to register %0s", | 
|  | test_csrs[i].get_full_name()), UVM_MEDIUM) | 
|  |  | 
|  | `DV_CHECK_STD_RANDOMIZE_FATAL(wdata) | 
|  | wdata &= get_mask_excl_fields(test_csrs[i], CsrExclWrite, CsrHwResetTest, m_csr_excl_item); | 
|  | csr_wr(.csr(test_csrs[i]), .value(wdata), .blocking(0)); | 
|  | end | 
|  | endtask | 
|  |  | 
|  | endclass | 
|  |  | 
|  |  | 
|  | //-------------------------------------------------------------------------------------------------- | 
|  | // Class: csr_rw_seq | 
|  | // Brief Description: This seq writes a random value to a CSR and reads it back. The read value | 
|  | // is checked for correctness while adhering to its access policies. A random choice is made between | 
|  | // reading back the CSR as a whole or reading fields individually, so that partial accesses are made | 
|  | // into the DUT as well. | 
|  | //-------------------------------------------------------------------------------------------------- | 
|  | class csr_rw_seq extends csr_base_seq; | 
|  | `uvm_object_utils(csr_rw_seq) | 
|  |  | 
|  | `uvm_object_new | 
|  |  | 
|  | rand bit do_csr_rd_check; | 
|  | rand bit do_csr_field_rd_check; | 
|  |  | 
|  | constraint csr_or_field_rd_check_c { | 
|  | // at least one of them should be set | 
|  | do_csr_rd_check || do_csr_field_rd_check; | 
|  | } | 
|  |  | 
|  | virtual task body(); | 
|  | foreach (test_csrs[i]) begin | 
|  | uvm_reg_data_t wdata; | 
|  | uvm_reg_data_t compare_mask; | 
|  | uvm_reg_field  test_fields[$]; | 
|  |  | 
|  | // check if parent block or register is excluded from write | 
|  | if (m_csr_excl_item.is_excl(test_csrs[i], CsrExclWrite, CsrRwTest)) begin | 
|  | `uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclWrite exclusion", | 
|  | test_csrs[i].get_full_name()), UVM_MEDIUM) | 
|  | continue; | 
|  | end | 
|  |  | 
|  | `uvm_info(`gtn, $sformatf("Verifying register read/write for %0s", | 
|  | test_csrs[i].get_full_name()), UVM_MEDIUM) | 
|  |  | 
|  | `DV_CHECK_FATAL(randomize(do_csr_rd_check, do_csr_field_rd_check)) | 
|  | `DV_CHECK_STD_RANDOMIZE_FATAL(wdata) | 
|  | wdata &= get_mask_excl_fields(test_csrs[i], CsrExclWrite, CsrRwTest, m_csr_excl_item); | 
|  |  | 
|  | // if external checker is not enabled and writes are made non-blocking, then we need to | 
|  | // pre-predict so that the mirrored value will be updated. if we dont, then csr_rd_check task | 
|  | // might pick up stale mirrored value | 
|  | // the pre-predict also needs to happen after the register is being written, to make sure the | 
|  | // register is getting the updated access information. | 
|  | csr_wr(.csr(test_csrs[i]), .value(wdata), .blocking(0), .predict(!external_checker)); | 
|  |  | 
|  | // check if parent block or register is excluded from read-check | 
|  | if (m_csr_excl_item.is_excl(test_csrs[i], CsrExclWriteCheck, CsrRwTest)) begin | 
|  | `uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclWriteCheck exclusion", | 
|  | test_csrs[i].get_full_name()), UVM_MEDIUM) | 
|  | continue; | 
|  | end | 
|  |  | 
|  | compare_mask = get_mask_excl_fields(test_csrs[i], CsrExclWriteCheck, CsrRwTest, | 
|  | m_csr_excl_item); | 
|  | if (do_csr_rd_check) begin | 
|  | csr_rd_check(.ptr           (test_csrs[i]), | 
|  | .blocking      (0), | 
|  | .compare       (!external_checker), | 
|  | .compare_vs_ral(1'b1), | 
|  | .compare_mask  (compare_mask)); | 
|  | end | 
|  | if (do_csr_field_rd_check) begin | 
|  | test_csrs[i].get_fields(test_fields); | 
|  | test_fields.shuffle(); | 
|  | foreach (test_fields[j]) begin | 
|  | bit compare = !m_csr_excl_item.is_excl(test_fields[j], CsrExclWriteCheck, CsrRwTest); | 
|  | csr_rd_check(.ptr           (test_fields[j]), | 
|  | .blocking      (0), | 
|  | .compare       (!external_checker && compare), | 
|  | .compare_vs_ral(1'b1)); | 
|  | end | 
|  | end | 
|  | end | 
|  | endtask | 
|  |  | 
|  | endclass | 
|  |  | 
|  | //-------------------------------------------------------------------------------------------------- | 
|  | // Class: csr_bit_bash_seq | 
|  | // Brief Description: This sequence walks a 1 through each CSR by writing one bit at a time and | 
|  | // reading the CSR back. The read value is checked for correctness while adhering to its access | 
|  | // policies. This verifies that there is no aliasing within the fields / bits of a CSR. | 
|  | //-------------------------------------------------------------------------------------------------- | 
|  | class csr_bit_bash_seq extends csr_base_seq; | 
|  | `uvm_object_utils(csr_bit_bash_seq) | 
|  |  | 
|  | `uvm_object_new | 
|  |  | 
|  | virtual task body(); | 
|  | foreach (test_csrs[i]) begin | 
|  | // check if parent block or register is excluded from write | 
|  | if (m_csr_excl_item.is_excl(test_csrs[i], CsrExclWrite, CsrBitBashTest) || | 
|  | m_csr_excl_item.is_excl(test_csrs[i], CsrExclWriteCheck, CsrBitBashTest)) begin | 
|  | `uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclWrite/WriteCheck exclusion", | 
|  | test_csrs[i].get_full_name()), UVM_MEDIUM) | 
|  | continue; | 
|  | end | 
|  |  | 
|  | `uvm_info(`gtn, $sformatf("Verifying register bit bash for %0s", | 
|  | test_csrs[i].get_full_name()), UVM_MEDIUM) | 
|  |  | 
|  | begin | 
|  | uvm_reg_field   fields[$]; | 
|  | string          mode[`UVM_REG_DATA_WIDTH]; | 
|  | uvm_reg_data_t  dc_mask;  // dont write or read | 
|  | uvm_reg_data_t  cmp_mask; // read but dont compare | 
|  | int             n_bits; | 
|  | string          field_access; | 
|  | int             next_lsb; | 
|  |  | 
|  | n_bits = test_csrs[i].get_n_bytes() * 8; | 
|  |  | 
|  | // Let's see what kind of bits we have... | 
|  | test_csrs[i].get_fields(fields); | 
|  |  | 
|  | next_lsb = 0; | 
|  | dc_mask  = 0; | 
|  | cmp_mask = 0; | 
|  |  | 
|  | foreach (fields[j]) begin | 
|  | int lsb, w, dc, cmp; | 
|  |  | 
|  | field_access = fields[j].get_access(test_csrs[i].get_default_map()); | 
|  | cmp = (fields[j].get_compare() == UVM_NO_CHECK); | 
|  | lsb = fields[j].get_lsb_pos(); | 
|  | w   = fields[j].get_n_bits(); | 
|  |  | 
|  | // Exclude write-only fields from compare because you are not supposed to read them | 
|  | case (field_access) | 
|  | "WO", "WOC", "WOS", "WO1", "NOACCESS", "": cmp = 1; | 
|  | endcase | 
|  |  | 
|  | // skip fields that are wr-excluded | 
|  | if (m_csr_excl_item.is_excl(fields[j], CsrExclWrite, CsrBitBashTest)) begin | 
|  | `uvm_info(`gtn, $sformatf("Skipping field %0s due to CsrExclWrite exclusion", | 
|  | fields[j].get_full_name()), UVM_MEDIUM) | 
|  | dc = 1; | 
|  | end | 
|  |  | 
|  | // ignore fields that are init or rd-excluded | 
|  | cmp = m_csr_excl_item.is_excl(fields[j], CsrExclInitCheck, CsrBitBashTest) || | 
|  | m_csr_excl_item.is_excl(fields[j], CsrExclWriteCheck, CsrBitBashTest) ; | 
|  |  | 
|  | // Any unused bits on the right side of the LSB? | 
|  | while (next_lsb < lsb) mode[next_lsb++] = "RO"; | 
|  |  | 
|  | repeat (w) begin | 
|  | mode[next_lsb] = field_access; | 
|  | dc_mask[next_lsb] = dc; | 
|  | cmp_mask[next_lsb] = cmp; | 
|  | next_lsb++; | 
|  | end | 
|  | end | 
|  |  | 
|  | // Any unused bits on the left side of the MSB? | 
|  | while (next_lsb < `UVM_REG_DATA_WIDTH) | 
|  | mode[next_lsb++] = "RO"; | 
|  |  | 
|  | // Bash the kth bit | 
|  | for (int k = 0; k < n_bits; k++) begin | 
|  | // Cannot test unpredictable bit behavior | 
|  | if (dc_mask[k]) continue; | 
|  | bash_kth_bit(test_csrs[i], k, mode[k], cmp_mask); | 
|  | end | 
|  | end | 
|  | end | 
|  |  | 
|  | endtask | 
|  |  | 
|  | task bash_kth_bit(uvm_reg       rg, | 
|  | int            k, | 
|  | string         mode, | 
|  | uvm_reg_data_t mask); | 
|  |  | 
|  | uvm_reg_data_t val; | 
|  | string          err_msg; | 
|  |  | 
|  | `uvm_info(`gtn, $sformatf("bashing %0s bit #%0d", mode, k), UVM_HIGH) | 
|  | repeat (2) begin | 
|  | val = rg.get(); | 
|  | val[k]  = ~val[k]; | 
|  | err_msg = $sformatf("Wrote %0s[%0d]: %0b", rg.get_full_name(), k, val[k]); | 
|  | csr_wr(.csr(rg), .value(val), .blocking(1)); | 
|  |  | 
|  | // if external checker is not enabled and writes are made non-blocking, then we need to | 
|  | // pre-predict so that the mirrored value will be updated. if we dont, then csr_rd_check task | 
|  | // might pick up stale mirrored value | 
|  | if (!external_checker) begin | 
|  | void'(rg.predict(.value(val), .kind(UVM_PREDICT_WRITE))); | 
|  | end | 
|  |  | 
|  | // TODO, outstanding access to same reg isn't supported in uvm_reg. Need to add another seq | 
|  | // uvm_reg waits until transaction is completed, before start another read/write in same reg | 
|  | csr_rd_check(.ptr           (rg), | 
|  | .blocking      (0), | 
|  | .compare       (!external_checker), | 
|  | .compare_vs_ral(1'b1), | 
|  | .compare_mask  (~mask), | 
|  | .err_msg       (err_msg)); | 
|  | end | 
|  | endtask: bash_kth_bit | 
|  |  | 
|  | endclass | 
|  |  | 
|  | //-------------------------------------------------------------------------------------------------- | 
|  | // Class: csr_aliasing_seq | 
|  | // Brief Description: For each CSR, this sequence writes a random value to it and reads ALL CSRs | 
|  | // back. The read value of the CSR that was written is checked for correctness while adhering to its | 
|  | // access policies. The read value of all other CSRs are compared against their previous values. | 
|  | // This verifies that there is no aliasing across the address bits within the valid CSR space. | 
|  | //-------------------------------------------------------------------------------------------------- | 
|  | class csr_aliasing_seq extends csr_base_seq; | 
|  | `uvm_object_utils(csr_aliasing_seq) | 
|  |  | 
|  | `uvm_object_new | 
|  |  | 
|  | virtual task body(); | 
|  | foreach(test_csrs[i]) begin | 
|  | uvm_reg_data_t wdata; | 
|  |  | 
|  | // check if parent block or register is excluded | 
|  | if (m_csr_excl_item.is_excl(test_csrs[i], CsrExclWrite, CsrAliasingTest)) begin | 
|  | `uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclWrite exclusion", | 
|  | test_csrs[i].get_full_name()), UVM_MEDIUM) | 
|  | continue; | 
|  | end | 
|  |  | 
|  | `uvm_info(`gtn, $sformatf("Verifying register aliasing for %0s", | 
|  | test_csrs[i].get_full_name()), UVM_MEDIUM) | 
|  |  | 
|  | `DV_CHECK_STD_RANDOMIZE_FATAL(wdata) | 
|  | wdata &= get_mask_excl_fields(test_csrs[i], CsrExclWrite, CsrAliasingTest, m_csr_excl_item); | 
|  | csr_wr(.csr(test_csrs[i]), .value(wdata), .blocking(0)); | 
|  |  | 
|  | // if external checker is not enabled and writes are made non-blocking, then we need to | 
|  | // pre-predict so that the mirrored value will be updated. if we dont, then csr_rd_check task | 
|  | // might pick up stale mirrored value | 
|  | if (!external_checker) begin | 
|  | void'(test_csrs[i].predict(.value(wdata), .kind(UVM_PREDICT_WRITE))); | 
|  | end | 
|  |  | 
|  | all_csrs.shuffle(); | 
|  | foreach (all_csrs[j]) begin | 
|  | uvm_reg_data_t compare_mask; | 
|  |  | 
|  | // check if parent block or register is excluded | 
|  | if (m_csr_excl_item.is_excl(all_csrs[j], CsrExclInitCheck, CsrAliasingTest) || | 
|  | m_csr_excl_item.is_excl(all_csrs[j], CsrExclWriteCheck, CsrAliasingTest)) begin | 
|  | `uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclInit/WriteCheck exclusion", | 
|  | all_csrs[j].get_full_name()), UVM_MEDIUM) | 
|  | continue; | 
|  | end | 
|  |  | 
|  | compare_mask = get_mask_excl_fields(all_csrs[j], CsrExclWriteCheck, CsrAliasingTest, | 
|  | m_csr_excl_item); | 
|  | csr_rd_check(.ptr           (all_csrs[j]), | 
|  | .blocking      (0), | 
|  | .compare       (!external_checker), | 
|  | .compare_vs_ral(1'b1), | 
|  | .compare_mask  (compare_mask)); | 
|  | end | 
|  | wait_no_outstanding_access(); | 
|  | end | 
|  | endtask | 
|  |  | 
|  | endclass | 
|  |  | 
|  | //-------------------------------------------------------------------------------------------------- | 
|  | // Class: csr_mem_walk_seq | 
|  | // Brief Description: This seq walks through each address of the memory by running the default | 
|  | // UVM mem walk sequence. | 
|  | //-------------------------------------------------------------------------------------------------- | 
|  | class csr_mem_walk_seq extends csr_base_seq; | 
|  | uvm_mem_walk_seq mem_walk_seq; | 
|  |  | 
|  | `uvm_object_utils(csr_mem_walk_seq) | 
|  |  | 
|  | `uvm_object_new | 
|  |  | 
|  | virtual task body(); | 
|  | mem_walk_seq = uvm_mem_walk_seq::type_id::create("mem_walk_seq"); | 
|  | foreach (models[i]) begin | 
|  | mem_walk_seq.model = models[i]; | 
|  | mem_walk_seq.start(null); | 
|  | end | 
|  | endtask : body | 
|  |  | 
|  | endclass |