[chip, dv] Add chip_tap_straps

Signed-off-by: Weicai Yang <weicai@google.com>
diff --git a/hw/top_earlgrey/dv/chip_sim_cfg.hjson b/hw/top_earlgrey/dv/chip_sim_cfg.hjson
index 2e9c17b..84e6638 100644
--- a/hw/top_earlgrey/dv/chip_sim_cfg.hjson
+++ b/hw/top_earlgrey/dv/chip_sim_cfg.hjson
@@ -751,6 +751,25 @@
       en_run_modes: ["sw_test_mode_test_rom"]
       run_opts: ["+sw_test_timeout_ns=18000000"]
     }
+    {
+      name: chip_tap_straps_dev
+      uvm_test_seq: chip_tap_straps_vseq
+      en_run_modes: ["stub_cpu_mode"]
+      en_run_modes: ["sw_test_mode_test_rom"]
+      run_opts: ["+use_otp_image=LcStDev",
+                 "+create_jtag_riscv_map=1",
+                 // select DFT tap
+                 "+uart0_sel=0"]
+    }
+    {
+      name: chip_tap_straps_rma
+      uvm_test_seq: chip_tap_straps_vseq
+      en_run_modes: ["stub_cpu_mode"]
+      run_opts: ["+use_otp_image=LcStRma",
+                 "+create_jtag_riscv_map=1",
+                 // select DFT tap
+                 "+uart0_sel=0"]
+    }
   ]
 
   // List of regressions.
diff --git a/hw/top_earlgrey/dv/env/chip_env.core b/hw/top_earlgrey/dv/env/chip_env.core
index 8b5d5f3..00d06b1 100644
--- a/hw/top_earlgrey/dv/env/chip_env.core
+++ b/hw/top_earlgrey/dv/env/chip_env.core
@@ -40,6 +40,7 @@
       - seq_lib/chip_common_vseq.sv: {is_include_file: true}
       - seq_lib/chip_jtag_csr_rw_vseq.sv: {is_include_file: true}
       - seq_lib/chip_jtag_mem_vseq.sv: {is_include_file: true}
+      - seq_lib/chip_tap_straps_vseq.sv: {is_include_file: true}
       - seq_lib/chip_sw_base_vseq.sv: {is_include_file: true}
       - seq_lib/chip_sw_full_aon_reset_vseq.sv: {is_include_file: true}
       - seq_lib/chip_sw_main_power_glitch_vseq.sv: {is_include_file: true}
diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_base_vseq.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_base_vseq.sv
index 53d2cb2..6b85749 100644
--- a/hw/top_earlgrey/dv/env/seq_lib/chip_base_vseq.sv
+++ b/hw/top_earlgrey/dv/env/seq_lib/chip_base_vseq.sv
@@ -140,4 +140,48 @@
     end
   endfunction
 
+  task test_mem_rw(uvm_mem mem, int max_access = 2048);
+    uvm_reg_data_t rdata;
+    int wdata, exp_data[$]; // cause all data is 32bit wide in this test
+    int offmax = mem.get_size() - 1;
+    int sizemax = offmax / 4;
+    int st, sz;
+    int byte_addr;
+    st = $urandom_range(0, offmax);
+    // set the maximum transaction
+    if (sizemax > max_access) sizemax = max_access;
+
+    sz = $urandom_range(1, sizemax);
+    `uvm_info(`gfn, $sformatf("Mem write to %s  offset:%0d size: %0d",
+                              mem.get_full_name(), st, sz), UVM_MEDIUM)
+
+    for (int i = 0; i < sz; ++i) begin
+      wdata = $urandom();
+      exp_data.push_back(wdata);
+
+      if (mem.get_access() == "RW") begin
+        mem_wr(.ptr(mem), .offset((st + i) % (offmax + 1)), .data(wdata));
+      end else begin // if (mem.get_access() == "RW")
+        // deposit random data to rom
+        byte_addr = ((st + i) % (offmax + 1)) * 4;
+        cfg.mem_bkdr_util_h[Rom].rom_encrypt_write32_integ(.addr(byte_addr), .data(wdata),
+                                                           .key(RndCnstRomCtrlScrKey),
+                                                           .nonce(RndCnstRomCtrlScrNonce),
+                                                           .scramble_data(1));
+      end
+    end
+
+    `uvm_info(`gfn, $sformatf("write to %s is complete, read back start...",
+                                mem.get_full_name()), UVM_MEDIUM)
+    for (int i = 0; i < sz; ++i) begin
+      mem_rd(.ptr(mem), .offset((st + i) % (offmax + 1)), .data(rdata));
+      `DV_CHECK_EQ((int'(rdata)), exp_data[i],
+                   $sformatf("read back check for offset:%0d failed",
+                             ((st + i) % (offmax + 1))))
+    end
+    `uvm_info(`gfn, $sformatf("read check from %s is complete",
+                              mem.get_full_name()), UVM_MEDIUM)
+
+  endtask : test_mem_rw
+
 endclass : chip_base_vseq
diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_jtag_mem_vseq.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_jtag_mem_vseq.sv
index c0aae8d..d823bcb 100644
--- a/hw/top_earlgrey/dv/env/seq_lib/chip_jtag_mem_vseq.sv
+++ b/hw/top_earlgrey/dv/env/seq_lib/chip_jtag_mem_vseq.sv
@@ -42,52 +42,8 @@
     test_mems.shuffle();
 
     for (int i = 0;i < test_mems.size(); ++i) begin
-        access_mem(test_mems[i]);
+      test_mem_rw(test_mems[i]);
     end
   endtask : body
 
-  task access_mem(uvm_mem mem);
-    uvm_reg_data_t rdata;
-    int wdata, exp_data[$]; // cause all data is 32bit wide in this test
-    int offmax = mem.get_size() - 1;
-    int sizemax = offmax / 4;
-    int st, sz;
-    int byte_addr;
-    st = $urandom_range(0, offmax);
-    // set the maximum transaction per memory to 2K * 4Byte
-    if (sizemax > 2048) sizemax = 2048;
-
-    sz = $urandom_range(1, sizemax);
-    `uvm_info(`gfn, $sformatf("Mem write to %s  offset:%0d size: %0d",
-                              mem.get_full_name(), st, sz), UVM_MEDIUM)
-
-    for (int i = 0; i < sz; ++i) begin
-      wdata = $urandom();
-      exp_data.push_back(wdata);
-
-      if (mem.get_access() == "RW") begin
-        mem_wr(.ptr(mem), .offset((st + i) % (offmax + 1)), .data(wdata));
-      end else begin // if (mem.get_access() == "RW")
-        // deposit random data to rom
-        byte_addr = ((st + i) % (offmax + 1)) * 4;
-        cfg.mem_bkdr_util_h[Rom].rom_encrypt_write32_integ(.addr(byte_addr), .data(wdata),
-                                                           .key(RndCnstRomCtrlScrKey),
-                                                           .nonce(RndCnstRomCtrlScrNonce),
-                                                           .scramble_data(1));
-      end
-    end
-
-    `uvm_info(`gfn, $sformatf("write to %s is complete, read back start...",
-                                mem.get_full_name()), UVM_MEDIUM)
-    for (int i = 0; i < sz; ++i) begin
-      mem_rd(.ptr(mem), .offset((st + i) % (offmax + 1)), .data(rdata));
-      `DV_CHECK_EQ((int'(rdata)), exp_data[i],
-                   $sformatf("read back check for offset:%0d failed",
-                             ((st + i) % (offmax + 1))))
-    end
-    `uvm_info(`gfn, $sformatf("read check from %s is complete",
-                              mem.get_full_name()), UVM_MEDIUM)
-
-  endtask // run_mem_rw
-
 endclass
diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_stub_cpu_base_vseq.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_stub_cpu_base_vseq.sv
index 974f699..b682e92 100644
--- a/hw/top_earlgrey/dv/env/seq_lib/chip_stub_cpu_base_vseq.sv
+++ b/hw/top_earlgrey/dv/env/seq_lib/chip_stub_cpu_base_vseq.sv
@@ -11,12 +11,6 @@
   `uvm_object_new
 
   virtual task pre_start();
-    super.pre_start();
-    // Deselect JTAG interface.
-    if (cfg.jtag_riscv_map != null) cfg.tap_straps_vif.drive(SelectRVJtagTap);
-    else                            cfg.tap_straps_vif.drive(DeselectJtagTap);
-    enable_asserts_in_hw_reset_rand_wr = 0;
-
     // In top-level uart RX pin may be selected in pinmux. CSR tests may randomly enable line
     // loopback, which will connect TX with RX. If RX isn't enabled in pinmux, it will be 0.
     // moniter will start to check the TX data when it changes from 1 to 0. But the length of 0 may
@@ -24,6 +18,12 @@
     // In block-level, we always tie RX to 1 (idle) in CSR test so that we don't need to disable TX
     // monitor in block-level
     foreach (cfg.m_uart_agent_cfgs[i]) cfg.m_uart_agent_cfgs[i].en_tx_monitor = 0;
+
+    super.pre_start();
+    // Deselect JTAG interface.
+    if (cfg.jtag_riscv_map != null) cfg.tap_straps_vif.drive(SelectRVJtagTap);
+    else                            cfg.tap_straps_vif.drive(DeselectJtagTap);
+    enable_asserts_in_hw_reset_rand_wr = 0;
   endtask
 
   task post_start();
diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_tap_straps_vseq.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_tap_straps_vseq.sv
new file mode 100644
index 0000000..1364a29
--- /dev/null
+++ b/hw/top_earlgrey/dv/env/seq_lib/chip_tap_straps_vseq.sv
@@ -0,0 +1,191 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+// Verify pinmux can select the life_cycle, RISC-V, and DFT taps after reset.
+// Verify that in TEST_UNLOCKED* and RMA states, pinmux can switch between the three TAPs
+// without issuing reset.
+// Verify in PROD state, only the LC tap can be selected.
+// Verify in DEV state, only the LC tap and RISC-V taps can be selected.
+// Verify DFT test mode straps are sampled and output to AST via
+// top_earlgrey.dft_strap_test_o in TEST_UNLOCKED* and RMA states.
+// Verify pimux.dft_strap_test_o is always 0 in the states other than TEST_UNLOCKED* and
+// RMA, regardless of the value on DFT SW straps.
+class chip_tap_straps_vseq extends chip_common_vseq;
+  string path_dft_strap_test_o = {`DUT_HIER_STR, ".top_earlgrey.dft_strap_test_o"};
+  bit init_rv_dm_done;
+  lc_ctrl_state_pkg::lc_state_e cur_lc_state;
+
+  local uvm_reg lc_csrs[$];
+
+  `uvm_object_utils(chip_tap_straps_vseq)
+
+  `uvm_object_new
+
+  virtual task dut_init(string reset_kind = "HARD");
+    init_rv_dm_done = 0;
+    randomize_dft_straps();
+    `DV_CHECK_STD_RANDOMIZE_FATAL(select_jtag)
+    cfg.tap_straps_vif.drive(select_jtag);
+
+    super.dut_init(reset_kind);
+  endtask
+
+  virtual task body();
+    bit dft_straps_en = 1;
+    chip_tap_type_e allowed_taps_q[$];
+
+    `DV_CHECK_FATAL(uvm_hdl_check_path(path_dft_strap_test_o))
+
+    ral.lc_ctrl.get_registers(lc_csrs);
+
+    cur_lc_state = cfg.use_otp_image;
+    check_dft_straps();
+
+    // TODO, #12500 don't switch back to rv_dm again, limit to 2
+    // repeat (2) begin
+    //   random_enable_jtag_tap();
+    //   test_jtag_tap();
+    // end
+
+    // reproduce #12500
+    enable_jtag_tap(SelectRVJtagTap);
+    test_jtag_tap();
+    enable_jtag_tap(SelectLCJtagTap);
+    test_jtag_tap();
+    enable_jtag_tap(SelectRVJtagTap);
+    test_jtag_tap();
+
+    // check again, it shouldn't be changed
+    check_dft_straps();
+  endtask : body
+
+  virtual task random_enable_jtag_tap();
+    if (is_lc_in_unlocked_or_rma()) begin
+      `DV_CHECK_STD_RANDOMIZE_FATAL(select_jtag)
+    end else begin // switch won't take effect. tap_straps won't be sampled again
+      cfg.tap_straps_vif.drive($urandom());
+    end
+    enable_jtag_tap(select_jtag);
+  endtask
+
+  // TODO, add DFT tap
+  virtual task enable_jtag_tap(chip_tap_type_e tap);
+    select_jtag = tap;
+    cfg.tap_straps_vif.drive(select_jtag);
+    case (select_jtag)
+      SelectRVJtagTap: begin
+        if (!init_rv_dm_done) begin
+          init_rv_dm();
+        end
+        cfg.m_jtag_riscv_agent_cfg.is_rv_dm = 1;
+      end
+      SelectLCJtagTap:begin
+        cfg.m_jtag_riscv_agent_cfg.is_rv_dm = 0;
+      end
+      DeselectJtagTap: begin
+      end
+      default: begin
+        `uvm_fatal(`gfn, "Unexpected tap")
+      end
+    endcase
+  endtask
+
+  virtual task init_rv_dm();
+    jtag_riscv_dm_activation_seq jtag_dm_activation_seq =
+        jtag_riscv_dm_activation_seq::type_id::create("jtag_dm_activation_seq");
+
+    cfg.m_jtag_riscv_agent_cfg.allow_errors = 1;
+    jtag_dm_activation_seq.start(p_sequencer.jtag_sequencer_h);
+    cfg.m_jtag_riscv_agent_cfg.allow_errors = 0;
+  endtask
+
+  virtual task test_jtag_tap();
+    cfg.clk_rst_vif.wait_clks(100);
+    `uvm_info(`gfn, $sformatf("Testing jtag tab %s", select_jtag), UVM_LOW)
+    case (select_jtag)
+      SelectRVJtagTap: begin
+        test_rv_dm_access_via_jtag();
+      end
+      SelectLCJtagTap:begin
+        test_lc_access_via_jtag();
+      end
+      DeselectJtagTap: begin
+        test_no_tap_selected();
+      end
+      default: begin
+        `uvm_fatal(`gfn, "Unexpected tap")
+      end
+    endcase
+
+    cfg.clk_rst_vif.wait_clks(100);
+  endtask
+
+  virtual task test_rv_dm_access_via_jtag();
+    test_mem_rw(.mem(ral.sram_ctrl_main_ram.ram), .max_access(10));
+  endtask
+
+  virtual task test_lc_access_via_jtag();
+    foreach (ral.lc_ctrl.device_id[i]) begin
+      bit [31:0] act_device_id, exp_device_id;
+      ral.lc_ctrl.device_id[i].print(); // TODO, remove
+      csr_peek(ral.lc_ctrl.device_id[i], exp_device_id);
+      jtag_riscv_agent_pkg::jtag_read_csr(ral.lc_ctrl.device_id[i].get_offset(),
+                                          p_sequencer.jtag_sequencer_h,
+                                          act_device_id);
+      `DV_CHECK_EQ(act_device_id, exp_device_id, $sformatf("device_id index: %0d", i))
+    end
+  endtask
+
+  // if no tap is selected, expect to read all 0s
+  virtual task test_no_tap_selected();
+    bit [TL_DW-1:0] rdata;
+    repeat (10) begin
+      cfg.m_jtag_riscv_agent_cfg.is_rv_dm = $urandom_range(0, 1);
+      randcase
+        // enable rv_dm
+        1: begin
+          cfg.m_jtag_riscv_agent_cfg.is_rv_dm = 1;
+          all_csrs.shuffle();
+          csr_rd(all_csrs[0], rdata);
+        end
+        // enable LC
+        1: begin
+          cfg.m_jtag_riscv_agent_cfg.is_rv_dm = 0;
+          lc_csrs.shuffle();
+          jtag_riscv_agent_pkg::jtag_read_csr(lc_csrs[0].get_offset(),
+                                              p_sequencer.jtag_sequencer_h,
+                                              rdata);
+        end
+      endcase
+      $display("wcy is_rv_dm %0d", cfg.m_jtag_riscv_agent_cfg.is_rv_dm);
+      `DV_CHECK_EQ(rdata, 0)
+    end
+  endtask
+
+  virtual function void randomize_dft_straps();
+    bit [1:0] val = $urandom;
+
+    `uvm_info(`gfn, $sformatf("Drive dft straps to %0d", val), UVM_LOW)
+    cfg.dft_straps_vif.drive(val);
+  endfunction
+
+  virtual function void check_dft_straps();
+    bit [1:0] exp_val, act_val;
+
+    if (is_lc_in_unlocked_or_rma()) begin
+      exp_val = cfg.dft_straps_vif.sample();
+    end else begin
+      exp_val = 0;
+    end
+    `DV_CHECK_FATAL(uvm_hdl_read(path_dft_strap_test_o, act_val))
+
+    `DV_CHECK_EQ(act_val, exp_val)
+  endfunction
+
+  virtual function bit is_lc_in_unlocked_or_rma();
+    return cur_lc_state inside {lc_ctrl_state_pkg::LcStRma,
+        LcStTestUnlocked0, LcStTestUnlocked1, LcStTestUnlocked2, LcStTestUnlocked3,
+        LcStTestUnlocked4, LcStTestUnlocked5, LcStTestUnlocked6, LcStTestUnlocked7};
+  endfunction
+endclass
diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv
index 2597f49..9299b58 100644
--- a/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv
+++ b/hw/top_earlgrey/dv/env/seq_lib/chip_vseq_list.sv
@@ -8,6 +8,7 @@
 `include "chip_common_vseq.sv"
 `include "chip_jtag_csr_rw_vseq.sv"
 `include "chip_jtag_mem_vseq.sv"
+`include "chip_tap_straps_vseq.sv"
 // This needs to be listed prior to all sequences that derive from it.
 `include "chip_sw_base_vseq.sv"
 `include "chip_sw_full_aon_reset_vseq.sv"
diff --git a/hw/top_earlgrey/dv/tb/tb.sv b/hw/top_earlgrey/dv/tb/tb.sv
index 226c30a..d5451f9 100644
--- a/hw/top_earlgrey/dv/tb/tb.sv
+++ b/hw/top_earlgrey/dv/tb/tb.sv
@@ -94,8 +94,11 @@
   // in the agent/interface.
   wire ioc3;
   wire ioc4;
-  wire uart0_sel;
-  assign uart0_sel = 1'b1;
+  bit uart0_sel = 1;
+  initial begin
+    void'($value$plusargs("uart0_sel=%0b", uart0_sel));
+    $display("wcy uart0_sel %0d",uart0_sel );
+  end
   assign ioc3 = (uart0_sel) ? uart_if[0].uart_rx : dft_straps[0];
   assign ioc4 = (uart0_sel) ? 1'bz : dft_straps[1];
   assign uart_if[0].uart_tx = ioc4;