[dv/otp] Add sequence for LC transition

This PR add a push-pull agent for otp_lc_program req.
Later PR will add more functionality to otp_lc_vseq.

Signed-off-by: Cindy Chen <chencindy@google.com>
diff --git a/hw/ip/otp_ctrl/data/otp_ctrl_testplan.hjson b/hw/ip/otp_ctrl/data/otp_ctrl_testplan.hjson
index 3e375b1..ccf31ff 100644
--- a/hw/ip/otp_ctrl/data/otp_ctrl_testplan.hjson
+++ b/hw/ip/otp_ctrl/data/otp_ctrl_testplan.hjson
@@ -102,7 +102,7 @@
             - Seq 3. Trigger escalation_en
             '''
       milestone: V2
-      tests: []
+      tests: ["otp_ctrl_lc"]
     }
     { name: otp_macro_errors
       desc: '''
diff --git a/hw/ip/otp_ctrl/dv/env/otp_ctrl_env.core b/hw/ip/otp_ctrl/dv/env/otp_ctrl_env.core
index 9e72de6..e2b07dd 100644
--- a/hw/ip/otp_ctrl/dv/env/otp_ctrl_env.core
+++ b/hw/ip/otp_ctrl/dv/env/otp_ctrl_env.core
@@ -24,6 +24,7 @@
       - seq_lib/otp_ctrl_common_vseq.sv: {is_include_file: true}
       - seq_lib/otp_ctrl_wake_up_vseq.sv: {is_include_file: true}
       - seq_lib/otp_ctrl_smoke_vseq.sv: {is_include_file: true}
+      - seq_lib/otp_ctrl_lc_vseq.sv: {is_include_file: true}
     file_type: systemVerilogSource
 
 generate:
diff --git a/hw/ip/otp_ctrl/dv/env/otp_ctrl_env.sv b/hw/ip/otp_ctrl/dv/env/otp_ctrl_env.sv
index 713b3f4..13effd8 100644
--- a/hw/ip/otp_ctrl/dv/env/otp_ctrl_env.sv
+++ b/hw/ip/otp_ctrl/dv/env/otp_ctrl_env.sv
@@ -16,6 +16,7 @@
   push_pull_agent#(.DeviceDataWidth(OTBN_DATA_SIZE))  m_otbn_pull_agent;
   push_pull_agent#(.DeviceDataWidth(FLASH_DATA_SIZE)) m_flash_addr_pull_agent;
   push_pull_agent#(.DeviceDataWidth(FLASH_DATA_SIZE)) m_flash_data_pull_agent;
+  push_pull_agent#(.DeviceDataWidth(1), .HostDataWidth(LC_PROG_DATA_SIZE)) m_lc_prog_pull_agent;
 
   function void build_phase(uvm_phase phase);
     super.build_phase(phase);
@@ -24,27 +25,33 @@
     for (int i = 0; i < NumSramKeyReqSlots; i++) begin
       string sram_agent_name = $sformatf("m_sram_pull_agent[%0d]", i);
       m_sram_pull_agent[i] = push_pull_agent#(.DeviceDataWidth(SRAM_DATA_SIZE))::type_id::create(
-        sram_agent_name, this);
+                             sram_agent_name, this);
       uvm_config_db#(push_pull_agent_cfg#(.DeviceDataWidth(SRAM_DATA_SIZE)))::set(this,
                      $sformatf("%0s*", sram_agent_name), "cfg", cfg.m_sram_pull_agent_cfg[i]);
     end
 
     // build otbn-otp pull agent
     m_otbn_pull_agent = push_pull_agent#(.DeviceDataWidth(OTBN_DATA_SIZE))::type_id::create(
-      "m_otbn_pull_agent", this);
+        "m_otbn_pull_agent", this);
     uvm_config_db#(push_pull_agent_cfg#(.DeviceDataWidth(OTBN_DATA_SIZE)))::set(
-      this, "m_otbn_pull_agent", "cfg", cfg.m_otbn_pull_agent_cfg);
+        this, "m_otbn_pull_agent", "cfg", cfg.m_otbn_pull_agent_cfg);
 
     // build flash-otp pull agent
     m_flash_addr_pull_agent = push_pull_agent#(.DeviceDataWidth(FLASH_DATA_SIZE))::type_id::create(
         "m_flash_addr_pull_agent", this);
     uvm_config_db#(push_pull_agent_cfg#(.DeviceDataWidth(FLASH_DATA_SIZE)))::set(
-      this, "m_flash_addr_pull_agent", "cfg", cfg.m_flash_addr_pull_agent_cfg);
+        this, "m_flash_addr_pull_agent", "cfg", cfg.m_flash_addr_pull_agent_cfg);
     m_flash_data_pull_agent = push_pull_agent#(.DeviceDataWidth(FLASH_DATA_SIZE))::type_id::create(
         "m_flash_data_pull_agent", this);
     uvm_config_db#(push_pull_agent_cfg#(.DeviceDataWidth(FLASH_DATA_SIZE)))::set(this, "m_flash_data_pull_agent",
         "cfg", cfg.m_flash_data_pull_agent_cfg);
 
+    // build lc-otp program pull agent
+    m_lc_prog_pull_agent = push_pull_agent#(.HostDataWidth(LC_PROG_DATA_SIZE), .DeviceDataWidth(1))
+        ::type_id::create("m_lc_prog_pull_agent", this);
+    uvm_config_db#(push_pull_agent_cfg#(.HostDataWidth(LC_PROG_DATA_SIZE), .DeviceDataWidth(1)))::
+        set(this, "m_lc_prog_pull_agent", "cfg", cfg.m_lc_prog_pull_agent_cfg);
+
     // config power manager pin
     if (!uvm_config_db#(pwr_otp_vif)::get(this, "", "pwr_otp_vif", cfg.pwr_otp_vif)) begin
       `uvm_fatal(get_full_name(), "failed to get pwr_otp_vif from uvm_config_db")
@@ -85,10 +92,12 @@
     virtual_sequencer.otbn_pull_sequencer_h       = m_otbn_pull_agent.sequencer;
     virtual_sequencer.flash_addr_pull_sequencer_h = m_flash_addr_pull_agent.sequencer;
     virtual_sequencer.flash_data_pull_sequencer_h = m_flash_data_pull_agent.sequencer;
+    virtual_sequencer.lc_prog_pull_sequencer_h    = m_lc_prog_pull_agent.sequencer;
     if (cfg.en_scb) begin
       m_otbn_pull_agent.monitor.req_port.connect(scoreboard.otbn_fifo.analysis_export);
       m_flash_addr_pull_agent.monitor.req_port.connect(scoreboard.flash_addr_fifo.analysis_export);
       m_flash_data_pull_agent.monitor.req_port.connect(scoreboard.flash_data_fifo.analysis_export);
+      m_lc_prog_pull_agent.monitor.req_port.connect(scoreboard.lc_prog_fifo.analysis_export);
     end
   endfunction
 
diff --git a/hw/ip/otp_ctrl/dv/env/otp_ctrl_env_cfg.sv b/hw/ip/otp_ctrl/dv/env/otp_ctrl_env_cfg.sv
index 8e5741c..f434806 100644
--- a/hw/ip/otp_ctrl/dv/env/otp_ctrl_env_cfg.sv
+++ b/hw/ip/otp_ctrl/dv/env/otp_ctrl_env_cfg.sv
@@ -9,6 +9,7 @@
   rand push_pull_agent_cfg#(.DeviceDataWidth(OTBN_DATA_SIZE))  m_otbn_pull_agent_cfg;
   rand push_pull_agent_cfg#(.DeviceDataWidth(FLASH_DATA_SIZE)) m_flash_data_pull_agent_cfg;
   rand push_pull_agent_cfg#(.DeviceDataWidth(FLASH_DATA_SIZE)) m_flash_addr_pull_agent_cfg;
+  rand push_pull_agent_cfg#(.DeviceDataWidth(1), .HostDataWidth(LC_PROG_DATA_SIZE)) m_lc_prog_pull_agent_cfg;
 
   // ext interfaces
   pwr_otp_vif              pwr_otp_vif;
@@ -32,21 +33,26 @@
     // create push_pull agent config obj
     for (int i = 0; i < NumSramKeyReqSlots; i++) begin
       string cfg_name = $sformatf("sram_pull_agent_cfg[%0d]", i);
-      m_sram_pull_agent_cfg[i] = push_pull_agent_cfg#(.DeviceDataWidth(SRAM_DATA_SIZE))::type_id::create(cfg_name);
+      m_sram_pull_agent_cfg[i] = push_pull_agent_cfg#(.DeviceDataWidth(SRAM_DATA_SIZE))::type_id
+                                 ::create(cfg_name);
       m_sram_pull_agent_cfg[i].agent_type = PullAgent;
     end
 
-    m_otbn_pull_agent_cfg = push_pull_agent_cfg#(.DeviceDataWidth(OTBN_DATA_SIZE))::type_id::create
-                            ("m_otbn_pull_agent_cfg");
+    m_otbn_pull_agent_cfg = push_pull_agent_cfg#(.DeviceDataWidth(OTBN_DATA_SIZE))::type_id
+                            ::create("m_otbn_pull_agent_cfg");
     m_otbn_pull_agent_cfg.agent_type = PullAgent;
 
-    m_flash_data_pull_agent_cfg = push_pull_agent_cfg#(.DeviceDataWidth(FLASH_DATA_SIZE))::type_id::create
-                                  ("m_flash_data_pull_agent_cfg");
+    m_flash_data_pull_agent_cfg = push_pull_agent_cfg#(.DeviceDataWidth(FLASH_DATA_SIZE))::type_id
+                                  ::create("m_flash_data_pull_agent_cfg");
     m_flash_data_pull_agent_cfg.agent_type = PullAgent;
-    m_flash_addr_pull_agent_cfg = push_pull_agent_cfg#(.DeviceDataWidth(FLASH_DATA_SIZE))::type_id::create
-                                  ("m_flash_addr_pull_agent_cfg");
+    m_flash_addr_pull_agent_cfg = push_pull_agent_cfg#(.DeviceDataWidth(FLASH_DATA_SIZE))::type_id
+                                  ::create("m_flash_addr_pull_agent_cfg");
     m_flash_addr_pull_agent_cfg.agent_type = PullAgent;
 
+    m_lc_prog_pull_agent_cfg = push_pull_agent_cfg#(.HostDataWidth(LC_PROG_DATA_SIZE),
+                               .DeviceDataWidth(1))::type_id::create("m_lc_prog_pull_agent_cfg");
+    m_lc_prog_pull_agent_cfg.agent_type = PullAgent;
+
     // set num_interrupts & num_alerts
     begin
       uvm_reg rg = ral.get_reg_by_name("intr_state");
diff --git a/hw/ip/otp_ctrl/dv/env/otp_ctrl_env_pkg.sv b/hw/ip/otp_ctrl/dv/env/otp_ctrl_env_pkg.sv
index 5dea654..c1f0d3b 100644
--- a/hw/ip/otp_ctrl/dv/env/otp_ctrl_env_pkg.sv
+++ b/hw/ip/otp_ctrl/dv/env/otp_ctrl_env_pkg.sv
@@ -62,11 +62,13 @@
                                    Secret1Size + Secret2Size)/ (TL_DW / 8);
 
   // sram rsp data has 1 bit for seed_valid, the rest are for key and nonce
-  parameter uint SRAM_DATA_SIZE  = 1 + SramKeyWidth + SramNonceWidth;
+  parameter uint SRAM_DATA_SIZE = 1 + SramKeyWidth + SramNonceWidth;
   // otbn rsp data has 1 bit for seed_valid, the rest are for key and nonce
-  parameter uint OTBN_DATA_SIZE  = 1 + OtbnKeyWidth + OtbnNonceWidth;
+  parameter uint OTBN_DATA_SIZE = 1 + OtbnKeyWidth + OtbnNonceWidth;
   // flash rsp data has 1 bit for seed_valid, the rest are for key
   parameter uint FLASH_DATA_SIZE = 1 + FlashKeyWidth;
+  // lc program data has lc_state data and lc_cnt data
+  parameter uint LC_PROG_DATA_SIZE = LcStateWidth + LcCountWidth;
 
   // scramble related parameters
   parameter uint SCRAMBLE_DATA_SIZE = 64;
diff --git a/hw/ip/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv b/hw/ip/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv
index dad00a9..b9a3d80 100644
--- a/hw/ip/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv
+++ b/hw/ip/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv
@@ -22,6 +22,8 @@
   uvm_tlm_analysis_fifo #(push_pull_item#(.DeviceDataWidth(OTBN_DATA_SIZE)))  otbn_fifo;
   uvm_tlm_analysis_fifo #(push_pull_item#(.DeviceDataWidth(FLASH_DATA_SIZE))) flash_addr_fifo;
   uvm_tlm_analysis_fifo #(push_pull_item#(.DeviceDataWidth(FLASH_DATA_SIZE))) flash_data_fifo;
+  uvm_tlm_analysis_fifo #(push_pull_item#(.DeviceDataWidth(1), .HostDataWidth(LC_PROG_DATA_SIZE)))
+                        lc_prog_fifo;
 
   // local queues to hold incoming packets pending comparison
 
@@ -35,6 +37,7 @@
     otbn_fifo       = new("otbn_fifo", this);
     flash_addr_fifo = new("flash_addr_fifo", this);
     flash_data_fifo = new("flash_data_fifo", this);
+    lc_prog_fifo    = new("lc_prog_fifo", this);
   endfunction
 
   function void connect_phase(uvm_phase phase);
diff --git a/hw/ip/otp_ctrl/dv/env/otp_ctrl_virtual_sequencer.sv b/hw/ip/otp_ctrl/dv/env/otp_ctrl_virtual_sequencer.sv
index cb396db..2db9250 100644
--- a/hw/ip/otp_ctrl/dv/env/otp_ctrl_virtual_sequencer.sv
+++ b/hw/ip/otp_ctrl/dv/env/otp_ctrl_virtual_sequencer.sv
@@ -14,4 +14,6 @@
   push_pull_sequencer#(.DeviceDataWidth(OTBN_DATA_SIZE))  otbn_pull_sequencer_h;
   push_pull_sequencer#(.DeviceDataWidth(FLASH_DATA_SIZE)) flash_data_pull_sequencer_h;
   push_pull_sequencer#(.DeviceDataWidth(FLASH_DATA_SIZE)) flash_addr_pull_sequencer_h;
+  push_pull_sequencer#(.DeviceDataWidth(1), .HostDataWidth(LC_PROG_DATA_SIZE))
+                       lc_prog_pull_sequencer_h;
 endclass
diff --git a/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_base_vseq.sv b/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_base_vseq.sv
index d526e2a..c74ba03 100644
--- a/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_base_vseq.sv
+++ b/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_base_vseq.sv
@@ -163,6 +163,20 @@
     `uvm_send(flash_data_pull_seq)
   endtask
 
+  virtual task req_lc_transition();
+    // TODO: this two variables are constraints to lc_prog_pull_seq once it supports data
+    // constraint
+    lc_ctrl_pkg::lc_state_e lc_state;
+    lc_ctrl_pkg::lc_cnt_e lc_cnt;
+    push_pull_host_seq#(.HostDataWidth(LC_PROG_DATA_SIZE), .DeviceDataWidth(1))
+                        lc_prog_pull_seq;
+    `uvm_create_on(lc_prog_pull_seq, p_sequencer.lc_prog_pull_sequencer_h);
+    `DV_CHECK_STD_RANDOMIZE_FATAL(lc_state);
+    `DV_CHECK_STD_RANDOMIZE_FATAL(lc_cnt)
+    `DV_CHECK_RANDOMIZE_FATAL(lc_prog_pull_seq)
+    `uvm_send(lc_prog_pull_seq)
+  endtask
+
   // first two or three LSB bits of DAI address can be randomized based on if it is secret
   virtual function bit [TL_AW-1:0] randomize_dai_addr(bit [TL_AW-1:0] dai_addr);
     if (is_secret(dai_addr)) begin
diff --git a/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_lc_vseq.sv b/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_lc_vseq.sv
new file mode 100644
index 0000000..873a67b
--- /dev/null
+++ b/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_lc_vseq.sv
@@ -0,0 +1,15 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+// otp_ctrl_lc_vseq is developed to generate lc_otp transitions
+class otp_ctrl_lc_vseq extends otp_ctrl_smoke_vseq;
+  `uvm_object_utils(otp_ctrl_lc_vseq)
+
+  `uvm_object_new
+
+  virtual task pre_start();
+    super.pre_start();
+    do_lc_trans = 1;
+  endtask
+endclass
diff --git a/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_smoke_vseq.sv b/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_smoke_vseq.sv
index 43a1e3b..8532c89 100644
--- a/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_smoke_vseq.sv
+++ b/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_smoke_vseq.sv
@@ -11,6 +11,8 @@
 
   `uvm_object_new
 
+  bit do_lc_trans;
+
   rand bit [TL_AW-1:0]               dai_addr;
   rand bit [TL_DW-1:0]               wdata0, wdata1;
   rand int                           num_dai_op;
@@ -110,6 +112,8 @@
 
       // check digest
       check_digests();
+
+      if (do_lc_trans) req_lc_transition();
     end
 
   endtask : body
diff --git a/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_vseq_list.sv b/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_vseq_list.sv
index 266b36b..f39e201 100644
--- a/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_vseq_list.sv
+++ b/hw/ip/otp_ctrl/dv/env/seq_lib/otp_ctrl_vseq_list.sv
@@ -6,3 +6,4 @@
 `include "otp_ctrl_wake_up_vseq.sv"
 `include "otp_ctrl_smoke_vseq.sv"
 `include "otp_ctrl_common_vseq.sv"
+`include "otp_ctrl_lc_vseq.sv"
diff --git a/hw/ip/otp_ctrl/dv/otp_ctrl_sim_cfg.hjson b/hw/ip/otp_ctrl/dv/otp_ctrl_sim_cfg.hjson
index d3a37fb..3d52156 100644
--- a/hw/ip/otp_ctrl/dv/otp_ctrl_sim_cfg.hjson
+++ b/hw/ip/otp_ctrl/dv/otp_ctrl_sim_cfg.hjson
@@ -60,6 +60,11 @@
       uvm_test_seq: otp_ctrl_smoke_vseq
     }
 
+    {
+      name: otp_ctrl_lc
+      uvm_test_seq: otp_ctrl_lc_vseq
+    }
+
     // TODO: add more tests here
   ]
 
diff --git a/hw/ip/otp_ctrl/dv/tb.sv b/hw/ip/otp_ctrl/dv/tb.sv
index acbe89b..1041df9 100644
--- a/hw/ip/otp_ctrl/dv/tb.sv
+++ b/hw/ip/otp_ctrl/dv/tb.sv
@@ -31,7 +31,6 @@
 
   //TODO: use push-pull agent once support
   wire otp_ctrl_pkg::otp_ast_req_t        ast_req;
-  wire otp_ctrl_pkg::lc_otp_program_rsp_t otp_prog;
   wire otp_ctrl_pkg::lc_otp_token_rsp_t   otp_token;
 
   // interfaces
@@ -39,9 +38,11 @@
   pins_if #(NUM_MAX_INTERRUPTS) intr_if(interrupts);
   pins_if #(1) devmode_if(devmode);
 
-  push_pull_if #(.DeviceDataWidth(SRAM_DATA_SIZE))  sram_if[NumSramKeyReqSlots] (.clk(clk),
-                                                                                 .rst_n(rst_n));
-  push_pull_if #(.DeviceDataWidth(OTBN_DATA_SIZE))  otbn_if(.clk(clk), .rst_n(rst_n));
+  push_pull_if #(.HostDataWidth(LC_PROG_DATA_SIZE), .DeviceDataWidth(1))
+                 lc_prog_if(.clk(clk), .rst_n(rst_n));
+  push_pull_if #(.DeviceDataWidth(SRAM_DATA_SIZE))
+                 sram_if[NumSramKeyReqSlots](.clk(clk), .rst_n(rst_n));
+  push_pull_if #(.DeviceDataWidth(OTBN_DATA_SIZE)) otbn_if(.clk(clk), .rst_n(rst_n));
   push_pull_if #(.DeviceDataWidth(FLASH_DATA_SIZE)) flash_addr_if(.clk(clk), .rst_n(rst_n));
   push_pull_if #(.DeviceDataWidth(FLASH_DATA_SIZE)) flash_data_if(.clk(clk), .rst_n(rst_n));
   push_pull_if #(.DeviceDataWidth(cip_base_pkg::EDN_DATA_WIDTH)) edn_if(.clk(clk), .rst_n(rst_n));
@@ -82,8 +83,8 @@
     .pwr_otp_i                 (pwr_otp[OtpPwrInitReq]),
     .pwr_otp_o                 (pwr_otp[OtpPwrDoneRsp:OtpPwrIdleRsp]),
     // lc
-    .lc_otp_program_i          ('0),
-    .lc_otp_program_o          (otp_prog),
+    .lc_otp_program_i          ({lc_prog_if.req, lc_prog_if.h_data}),
+    .lc_otp_program_o          ({lc_prog_if.d_data, lc_prog_if.ack}),
     .lc_otp_token_i            ('0),
     .lc_otp_token_o            (otp_token),
     .lc_escalate_en_i          (lc_ctrl_pkg::Off),
@@ -144,8 +145,10 @@
                    "*env.m_flash_data_pull_agent*", "vif", flash_data_if);
     uvm_config_db#(virtual push_pull_if#(.DeviceDataWidth(FLASH_DATA_SIZE)))::set(null,
                    "*env.m_flash_addr_pull_agent*", "vif", flash_addr_if);
-    uvm_config_db#(virtual push_pull_if#(.DeviceDataWidth(cip_base_pkg::EDN_DATA_WIDTH)))::set
-                  (null, "*env.m_edn_pull_agent*", "vif", edn_if);
+    uvm_config_db#(virtual push_pull_if#(.DeviceDataWidth(cip_base_pkg::EDN_DATA_WIDTH)))::
+                   set(null, "*env.m_edn_pull_agent*", "vif", edn_if);
+    uvm_config_db#(virtual push_pull_if#(.HostDataWidth(LC_PROG_DATA_SIZE), .DeviceDataWidth(1)))::
+                   set(null, "*env.m_lc_prog_pull_agent*", "vif", lc_prog_if);
 
     uvm_config_db#(intr_vif)::set(null, "*.env", "intr_vif", intr_if);
     uvm_config_db#(pwr_otp_vif)::set(null, "*.env", "pwr_otp_vif", pwr_otp_if);