[i2c, dv] Add i2c_sanity_vseq for V1
1) i2c_sanity_vseq generates following transaction types
- b2b-read trans without STOP bit at the end (issue repSTART after)
- b2b-read trans with STOP bit at the end (issue START after)
- b2b-write trans without STOP bit at the end (issue repSTART after)
- b2b-write trans with STOP bit at the end (issue START after)
- interleave read and write transactions (randomly change direction)
- support clock streching by device
2) i2c_scoreboard
- verify transaction types (generated by i2c_sanity_vseq)
3) fix monitor_ready_to_end task in i2c_monitor.sv to avoid timeout
TODO: b2b-read chained trans
Signed-off-by: Tung Hoang <tung.hoang.290780@gmail.com>
diff --git a/hw/dv/sv/i2c_agent/i2c_driver.sv b/hw/dv/sv/i2c_agent/i2c_driver.sv
index 03fad7e..5c05325 100644
--- a/hw/dv/sv/i2c_agent/i2c_driver.sv
+++ b/hw/dv/sv/i2c_agent/i2c_driver.sv
@@ -8,14 +8,14 @@
`uvm_component_new
virtual task reset_signals();
- `uvm_info(`gtn, "driver in reset progress", UVM_HIGH)
- @(posedge !cfg.vif.rst_ni);
+ `uvm_info(`gfn, "driver in reset progress", UVM_HIGH)
+ @(negedge cfg.vif.rst_ni);
cfg.vif.scl_o <= 1'b0;
cfg.vif.sda_o <= 1'b0;
@(posedge cfg.vif.rst_ni);
cfg.vif.scl_o <= 1'b1;
cfg.vif.sda_o <= 1'b1;
- `uvm_info(`gtn, "driver out of reset", UVM_HIGH)
+ `uvm_info(`gfn, "driver out of reset", UVM_HIGH)
endtask : reset_signals
endclass : i2c_driver
diff --git a/hw/dv/sv/i2c_agent/i2c_monitor.sv b/hw/dv/sv/i2c_agent/i2c_monitor.sv
index 8edf08e..6903306 100644
--- a/hw/dv/sv/i2c_agent/i2c_monitor.sv
+++ b/hw/dv/sv/i2c_agent/i2c_monitor.sv
@@ -174,8 +174,7 @@
virtual task monitor_ready_to_end();
forever begin
@(cfg.vif.scl_i or cfg.vif.sda_i or cfg.vif.scl_o or cfg.vif.sda_o);
- ok_to_end = (cfg.vif.scl_i == 1'b1) && (cfg.vif.sda_i == 1'b1) &&
- (cfg.vif.scl_o == 1'b1) && (cfg.vif.sda_o == 1'b1);
+ ok_to_end = (cfg.vif.scl_i == 1'b1) && (cfg.vif.sda_i == 1'b1);
end
endtask : monitor_ready_to_end
diff --git a/hw/dv/sv/i2c_agent/seq_lib/i2c_base_seq.sv b/hw/dv/sv/i2c_agent/seq_lib/i2c_base_seq.sv
index 1a8ec0f..073e42b 100644
--- a/hw/dv/sv/i2c_agent/seq_lib/i2c_base_seq.sv
+++ b/hw/dv/sv/i2c_agent/seq_lib/i2c_base_seq.sv
@@ -12,7 +12,7 @@
`uvm_object_new
virtual task body();
- `uvm_fatal(`gtn, "Need to override this when you extend from this class!")
+ `uvm_fatal(`gfn, "Need to override this when you extend from this class!")
endtask
endclass : i2c_base_seq
diff --git a/hw/ip/i2c/data/i2c.hjson b/hw/ip/i2c/data/i2c.hjson
index b4fec12..b81e4d0 100644
--- a/hw/ip/i2c/data/i2c.hjson
+++ b/hw/ip/i2c/data/i2c.hjson
@@ -14,10 +14,10 @@
// INTERRUPT pins
interrupt_list: [
{ name: "fmt_watermark"
- desc: "raised when the FMT FIFO depth falls below the lowwater mark."
+ desc: "raised when the FMT FIFO depth falls below the low watermark."
}
{ name: "rx_watermark"
- desc: "raised if the RX FIFO is past the highwater mark."
+ desc: "raised if the RX FIFO is past the high watermark."
}
{ name: "fmt_overflow"
desc: "raised if the FMT FIFO has overflowed."
diff --git a/hw/ip/i2c/data/i2c_testplan.hjson b/hw/ip/i2c/data/i2c_testplan.hjson
index 2b4de9e..a6d8d65 100644
--- a/hw/ip/i2c/data/i2c_testplan.hjson
+++ b/hw/ip/i2c/data/i2c_testplan.hjson
@@ -19,7 +19,7 @@
Checking:
- Check the timing behavior of START, STOP, ACK, NACK, and "repeated" START.
- Read and write transfer matching.'''
- milestone: V2
+ milestone: V1
tests: ["i2c_sanity"]
}
{
diff --git a/hw/ip/i2c/dv/env/i2c_env.core b/hw/ip/i2c/dv/env/i2c_env.core
index 8af27c3..e1a3ba9 100644
--- a/hw/ip/i2c/dv/env/i2c_env.core
+++ b/hw/ip/i2c/dv/env/i2c_env.core
@@ -21,6 +21,8 @@
- seq_lib/i2c_vseq_list.sv: {is_include_file: true}
- seq_lib/i2c_base_vseq.sv: {is_include_file: true}
- seq_lib/i2c_common_vseq.sv: {is_include_file: true}
+ - seq_lib/i2c_rx_tx_vseq.sv: {is_include_file: true}
+ - seq_lib/i2c_sanity_vseq.sv: {is_include_file: true}
file_type: systemVerilogSource
generate:
diff --git a/hw/ip/i2c/dv/env/i2c_env.sv b/hw/ip/i2c/dv/env/i2c_env.sv
index 16e65a3..5d91633 100644
--- a/hw/ip/i2c/dv/env/i2c_env.sv
+++ b/hw/ip/i2c/dv/env/i2c_env.sv
@@ -18,16 +18,16 @@
super.build_phase(phase);
m_i2c_agent = i2c_agent::type_id::create("m_i2c_agent", this);
uvm_config_db#(i2c_agent_cfg)::set(this, "m_i2c_agent*", "cfg", cfg.m_i2c_agent_cfg);
- endfunction
+ endfunction : build_phase
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
if (cfg.en_scb) begin
- m_i2c_agent.monitor.analysis_port.connect(scoreboard.i2c_fifo.analysis_export);
+ m_i2c_agent.monitor.rd_item_port.connect(scoreboard.rd_item_fifo.analysis_export);
+ m_i2c_agent.monitor.wr_item_port.connect(scoreboard.wr_item_fifo.analysis_export);
end
- if (cfg.m_i2c_agent_cfg.is_active) begin
- virtual_sequencer.i2c_sequencer_h = m_i2c_agent.sequencer;
- end
+
+ virtual_sequencer.i2c_sequencer_h = m_i2c_agent.sequencer;
endfunction
endclass : i2c_env
diff --git a/hw/ip/i2c/dv/env/i2c_env_cfg.sv b/hw/ip/i2c/dv/env/i2c_env_cfg.sv
index 2ba74c9..e7a0dd8 100644
--- a/hw/ip/i2c/dv/env/i2c_env_cfg.sv
+++ b/hw/ip/i2c/dv/env/i2c_env_cfg.sv
@@ -4,7 +4,10 @@
class i2c_env_cfg extends cip_base_env_cfg #(.RAL_T(i2c_reg_block));
- // ext component cfgs
+ // TODO: various knobs to enable certain routines
+ bit do_rd_overflow = 1'b0;
+ bit do_wr_overflow = 1'b0;
+
rand i2c_agent_cfg m_i2c_agent_cfg;
`uvm_object_utils_begin(i2c_env_cfg)
@@ -19,10 +22,7 @@
virtual function void initialize(bit [TL_AW-1:0] csr_base_addr = '1);
super.initialize(csr_base_addr);
- // create i2c agent config obj
m_i2c_agent_cfg = i2c_agent_cfg::type_id::create("m_i2c_agent_cfg");
-
- // set num_interrupts & num_alerts which will be used to create coverage and more
num_interrupts = ral.intr_state.get_n_used_bits();
endfunction
diff --git a/hw/ip/i2c/dv/env/i2c_env_cov.sv b/hw/ip/i2c/dv/env/i2c_env_cov.sv
index fef5f70..8dcb285 100644
--- a/hw/ip/i2c/dv/env/i2c_env_cov.sv
+++ b/hw/ip/i2c/dv/env/i2c_env_cov.sv
@@ -5,11 +5,9 @@
class i2c_env_cov extends cip_base_env_cov #(.CFG_T(i2c_env_cfg));
`uvm_component_utils(i2c_env_cov)
- // TODO: define covergroups
-
+ // TODO: instantiate all covergroups here
function new(string name, uvm_component parent);
super.new(name, parent);
- // TODO: instantiate all covergroups here
endfunction : new
endclass
diff --git a/hw/ip/i2c/dv/env/i2c_env_pkg.sv b/hw/ip/i2c/dv/env/i2c_env_pkg.sv
index db633c9..b97bbc4 100644
--- a/hw/ip/i2c/dv/env/i2c_env_pkg.sv
+++ b/hw/ip/i2c/dv/env/i2c_env_pkg.sv
@@ -36,16 +36,24 @@
parameter uint I2C_ADDR_MAP_SIZE = 128;
// for constrains
- parameter uint I2C_MIN_ADDR = 0;
- parameter uint I2C_MAX_ADDR = 127;
- parameter uint I2C_MIN_DATA = 0;
- parameter uint I2C_MAX_DATA = 255;
- parameter uint I2C_MIN_DLY = 1;
- parameter uint I2C_MAX_DLY = 2;
- parameter uint I2C_MIN_TIMING = 2;
- parameter uint I2C_MAX_TIMING = 2;
- parameter bit I2C_FLAG_ON = 1'b1;
- parameter bit I2C_FLAG_OFF = 1'b0;
+ parameter uint I2C_MIN_TRAN = 10;
+ parameter uint I2C_MAX_TRAN = 200;
+ parameter uint I2C_MIN_ADDR = 0;
+ parameter uint I2C_MAX_ADDR = 127;
+ parameter uint I2C_MIN_DLY = 0;
+ parameter uint I2C_MAX_DLY = 2;
+ parameter uint I2C_MIN_DATA = 0;
+ parameter uint I2C_MAX_DATA = 255;
+ parameter uint I2C_MIN_TIMING = 1; // at least 1
+ parameter uint I2C_MAX_TIMING = 5;
+ parameter uint I2C_TIME_RANGE = I2C_MAX_TIMING - I2C_MIN_TIMING;
+ parameter uint I2C_TIMEOUT_ENB = 1;
+ parameter uint I2C_MIN_TIMEOUT = 1;
+ parameter uint I2C_MAX_TIMEOUT = 2;
+ parameter uint I2C_IDLE_TIME = 1200;
+
+ // ok_to_end_delay_ns for EoT
+ parameter uint DELAY_FOR_EOT_NS = 5000;
// functions
// get the number of bytes that triggers watermark interrupt
@@ -86,3 +94,4 @@
`include "i2c_vseq_list.sv"
endpackage : i2c_env_pkg
+
diff --git a/hw/ip/i2c/dv/env/i2c_scoreboard.sv b/hw/ip/i2c/dv/env/i2c_scoreboard.sv
index 3fdb05a..12f5ef0 100644
--- a/hw/ip/i2c/dv/env/i2c_scoreboard.sv
+++ b/hw/ip/i2c/dv/env/i2c_scoreboard.sv
@@ -9,43 +9,52 @@
);
`uvm_component_utils(i2c_scoreboard)
- //****************************************************************
- // TODO: this class will be completed later with i2c_sanity test
- // TODO: no need for review
- //****************************************************************
- virtual i2c_if i2c_vif;
+ virtual i2c_if i2c_vif;
- // TLM agent fifos
- uvm_tlm_analysis_fifo #(i2c_item) i2c_fifo;
+ local i2c_item exp_rd_item;
+ local i2c_item exp_wr_item;
+ local i2c_item rd_pending_item;
+ local i2c_item rd_pending_q[$];
+ local i2c_item exp_rd_q[$];
+ local i2c_item exp_wr_q[$];
+ local uint rd_wait;
+
+ local bit host_init = 1'b0;
+ local uint rdata_cnt = 0;
+ local uint tran_id = 0;
+ local uint num_exp_tran = 0;
+
+ // use seperate fifos for dut_rd and dut_wr item
+ uvm_tlm_analysis_fifo #(i2c_item) rd_item_fifo;
+ uvm_tlm_analysis_fifo #(i2c_item) wr_item_fifo;
`uvm_component_new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
- i2c_fifo = new("i2c_fifo", this);
- endfunction
-
- function void connect_phase(uvm_phase phase);
- super.connect_phase(phase);
- endfunction
+ rd_item_fifo = new("rd_item_fifo", this);
+ wr_item_fifo = new("wr_item_fifo", this);
+ rd_pending_item = new("rd_pending_item" );
+ exp_rd_item = new("exp_rd_item");
+ exp_wr_item = new("exp_wr_item");
+ endfunction : build_phase
task run_phase(uvm_phase phase);
super.run_phase(phase);
fork
- process_i2c_fifo();
- join_none
- endtask
-
- virtual task process_i2c_fifo();
- // TODO
- endtask : process_i2c_fifo
+ compare_trans(BusOpWrite);
+ compare_trans(BusOpRead);
+ join
+ endtask : run_phase
virtual task process_tl_access(tl_seq_item item, tl_channels_e channel = DataChannel);
- uvm_reg csr;
+ uvm_reg csr;
+ i2c_item sb_exp_wr_item;
+ i2c_item sb_exp_rd_item;
bit do_read_check = 1'b0;
bit write = item.is_write();
- uvm_reg_addr_t csr_addr = get_normalized_addr(item.a_addr);
+ uvm_reg_addr_t csr_addr = get_normalized_addr(item.a_addr);
// if access was to a valid csr, get the csr handle
if (csr_addr inside {cfg.csr_addrs}) begin
csr = ral.default_map.get_reg_by_offset(csr_addr);
@@ -54,52 +63,204 @@
`uvm_fatal(`gfn, $sformatf("access unexpected addr 0x%0h", csr_addr))
end
- if (channel == AddrChannel) begin
- // if incoming access is a write to a valid csr, then make updates right away
- if (write) begin
- void'(csr.predict(.value(item.a_data), .kind(UVM_PREDICT_WRITE), .be(item.a_mask)));
+ sb_exp_wr_item = new();
+ sb_exp_rd_item = new();
+ if (write && channel == AddrChannel) begin
+ // incoming access is a write to a valid csr, then make updates right away
+ void'(csr.predict(.value(item.a_data), .kind(UVM_PREDICT_WRITE), .be(item.a_mask)));
+
+ // process the csr req
+ // for write, update local variable and fifo at address phase
+ // for read, update predication at address phase and compare at data phase
+ case (csr.get_name())
+ // add individual case item for each csr
+ "ctrl": begin
+ host_init = ral.ctrl.enablehost.get_mirrored_value();
+ end
+
+ "fdata": begin
+ bit [7:0] fbyte;
+ bit start, stop, read, rcont, nakok;
+
+ if (host_init) begin
+ fbyte = get_field_val(ral.fdata.fbyte, item.a_data);
+ start = bit'(get_field_val(ral.fdata.start, item.a_data));
+ stop = bit'(get_field_val(ral.fdata.stop, item.a_data));
+ read = bit'(get_field_val(ral.fdata.read, item.a_data));
+ rcont = bit'(get_field_val(ral.fdata.rcont, item.a_data));
+ nakok = bit'(get_field_val(ral.fdata.nakok, item.a_data));
+
+ // transaction is being started/rstarted, target address is programmed
+ if (start) begin
+ tran_id++;
+ if (exp_wr_item.start && !sb_exp_wr_item.stop &&
+ exp_wr_item.bus_op == BusOpWrite) begin
+ // this is a write end with rstart, write to sb_wr_fifo
+ exp_wr_item.rstart = 1'b1;
+ `downcast(sb_exp_wr_item, exp_wr_item.clone());
+ sb_exp_wr_item.stop = 1'b1;
+ exp_wr_item.clear_all();
+ end
+ `DV_CHECK_EQ(read, 1'b0)
+ `DV_CHECK_EQ(stop, 1'b0)
+ if (fbyte[0]) begin
+ // read transaction
+ exp_rd_item.tran_id = tran_id;
+ exp_rd_item.bus_op = BusOpRead;
+ exp_rd_item.addr = fbyte[7:1];
+ exp_rd_item.start = start;
+ exp_rd_item.stop = stop;
+ end else begin
+ // write transaction
+ exp_wr_item.tran_id = tran_id;
+ exp_wr_item.bus_op = BusOpWrite;
+ exp_wr_item.addr = fbyte[7:1];
+ exp_wr_item.start = start;
+ exp_wr_item.stop = stop;
+ end
+ end else begin // transaction has been started/rstarted
+ // write transaction
+ if (exp_wr_item.start && exp_wr_item.bus_op == BusOpWrite) begin
+ exp_wr_item.data_q.push_back(fbyte);
+ exp_wr_item.num_data++;
+ exp_wr_item.stop = stop;
+ if (exp_wr_item.stop) begin
+ // this is a write end with stop, write to the sb_wr_fifo
+ `downcast(sb_exp_wr_item, exp_wr_item.clone());
+ exp_wr_item.clear_all();
+ end
+ end
+ // read transaction
+ if (exp_rd_item.start && exp_rd_item.bus_op == BusOpRead) begin
+ if (read) begin // read flag
+ i2c_item tmp_rd_item;
+
+ // get the number of byte to read, fmt_read flag is set
+ exp_rd_item.num_data = (fbyte == 8'd0) ? 256 : fbyte;
+ exp_rd_item.stop = stop;
+ `DV_CHECK_EQ(read, 1'b1)
+ exp_rd_item.read = read;
+ exp_rd_item.rcont = rcont;
+ exp_rd_item.nakok = nakok;
+ exp_rd_item.nack = ~exp_rd_item.rcont;
+ exp_rd_item.rstart = (exp_rd_item.stop) ? 1'b0 : 1'b1;
+ //push into temporal rd_pending_q (data still empty)
+ `downcast(tmp_rd_item, exp_rd_item.clone());
+ rd_pending_q.push_back(tmp_rd_item);
+ exp_rd_item.start = 0;
+ exp_rd_item.clear_data();
+ end
+ end
+ end
+ end
+ end
+ endcase
+
+ // push sb_exp_wr_item into exp_fifo
+ if (host_init && sb_exp_wr_item.start && sb_exp_wr_item.stop) begin
+ //exp_wr_port.write(sb_exp_wr_item);
+ exp_wr_q.push_back(sb_exp_wr_item);
+ num_exp_tran++;
+ `uvm_info(`gfn, $sformatf("\n\nscoreboard, exp_wr_item\n\%s",
+ sb_exp_wr_item.sprint()), UVM_DEBUG)
end
end
- // process the csr req
- // for write, update local variable and fifo at address phase
- // for read, update predication at address phase and compare at data phase
- case (csr.get_name())
- // add individual case item for each csr
- default: begin
- //`uvm_fatal(`gfn, $sformatf("invalid csr: %0s", csr.get_full_name()))
- end
- endcase
-
// On reads, if do_read_check, is set, then check mirrored_value against item.d_data
if (!write && channel == DataChannel) begin
if (do_read_check) begin
`DV_CHECK_EQ(csr.get_mirrored_value(), item.d_data,
- $sformatf("reg name: %0s", csr.get_full_name()))
+ $sformatf("reg name: %0s", csr.get_full_name()))
end
void'(csr.predict(.value(item.d_data), .kind(UVM_PREDICT_READ)));
- end
- endtask
- function void compare(i2c_item act, i2c_item exp, string dir = "TX");
- if (!act.compare(exp)) begin
- `uvm_error(`gfn, $sformatf("%s item mismatch!\nexp:\n%0s\nobs:\n%0s",
- dir, exp.sprint(), act.sprint()))
+ case (csr.get_name())
+ "rdata": begin
+ if (host_init) begin
+ if (rdata_cnt == 0) begin
+ `DV_CHECK_GE_FATAL(rd_pending_q.size(), 0)
+ rd_pending_item = rd_pending_q.pop_front();
+ end
+ // collect read data byte
+ rd_pending_item.data_q.push_back(ral.rdata.get_mirrored_value());
+ rdata_cnt++;
+ // get all read data bytes
+ if (rdata_cnt == rd_pending_item.num_data) begin
+ rd_pending_item.stop = 1'b1;
+ `downcast(sb_exp_rd_item, rd_pending_item.clone());
+ rdata_cnt = 0;
+ end
+ end
+ end
+ endcase
+
+ // push sb_exp_rd_item into exp_fifo
+ if (host_init && sb_exp_rd_item.start && sb_exp_rd_item.stop) begin
+ //exp_rd_port.write(sb_exp_rd_item);
+ exp_rd_q.push_back(sb_exp_rd_item);
+ num_exp_tran++;
+ `uvm_info(`gfn, $sformatf("\n\nscoreboard exp_rd_item\n\%s",
+ sb_exp_rd_item.sprint()), UVM_DEBUG)
+ end
end
- else begin
- `uvm_info(`gfn, $sformatf("%s item match!\nexp:\n%0s\nobs:\n%0s",
- dir, exp.sprint(), act.sprint()), UVM_HIGH)
+ endtask : process_tl_access
+
+ task compare_trans(bus_op_e dir = BusOpWrite);
+ i2c_item exp_item;
+ i2c_item dut_trn;
+ forever begin
+ if (dir == BusOpWrite) begin
+ wr_item_fifo.get(dut_trn);
+ wait(exp_wr_q.size() > 0);
+ exp_item = exp_wr_q.pop_front();
+ end else begin // BusOpRead
+ rd_item_fifo.get(dut_trn);
+ wait(exp_rd_q.size() > 0);
+ exp_item = exp_rd_q.pop_front();
+ end
+ if (!dut_trn.compare(exp_item)) begin
+ `uvm_error(`gfn, $sformatf("%s item mismatch!\n--> EXP:\n%0s\--> DUT:\n%0s",
+ (dir == BusOpWrite) ? "WRITE" : "READ", exp_item.sprint(), dut_trn.sprint()))
+ end else begin
+ `uvm_info(`gfn, $sformatf("direction %s item match!\n--> EXP:\n%0s\--> DUT:\n%0s",
+ (dir == BusOpWrite) ? "WRITE" : "READ", exp_item.sprint(), dut_trn.sprint()), UVM_DEBUG)
+ end
end
- endfunction : compare
+ endtask : compare_trans
virtual function void reset(string kind = "HARD");
- // reset local fifos queues and variables
super.reset(kind);
+ // reset local fifos queues and variables
+ rd_item_fifo.flush();
+ wr_item_fifo.flush();
+ exp_rd_q.delete();
+ exp_wr_q.delete();
+ rd_pending_q.delete();
+ host_init = 1'b0;
+ tran_id = 0;
+ rdata_cnt = 0;
+ num_exp_tran = 0;
endfunction : reset
+ function void report_phase(uvm_phase phase);
+ string str;
+
+ super.report_phase(phase);
+ `uvm_info(`gfn, $sformatf("%s", cfg.convert2string()), UVM_DEBUG)
+ if (cfg.en_scb) begin
+ str = {$sformatf("\n\n*** SCOREBOARD CHECK\n")};
+ str = {str, $sformatf(" - Total checked trans %0d\n", num_exp_tran)};
+ `uvm_info(`gfn, $sformatf("%s", str), UVM_LOW)
+ end
+ endfunction : report_phase
+
function void check_phase(uvm_phase phase);
super.check_phase(phase);
- // post test checks - ensure that all local fifos and queues are empty
+ `DV_EOT_PRINT_Q_CONTENTS(i2c_item, exp_wr_q)
+ `DV_EOT_PRINT_Q_CONTENTS(i2c_item, exp_rd_q)
+ `DV_EOT_PRINT_Q_CONTENTS(i2c_item, rd_pending_q)
+ `DV_EOT_PRINT_TLM_FIFO_CONTENTS(i2c_item, rd_item_fifo)
+ `DV_EOT_PRINT_TLM_FIFO_CONTENTS(i2c_item, wr_item_fifo)
endfunction
endclass : i2c_scoreboard
diff --git a/hw/ip/i2c/dv/env/i2c_virtual_sequencer.sv b/hw/ip/i2c/dv/env/i2c_virtual_sequencer.sv
index e5a1a3c..3e45d99 100644
--- a/hw/ip/i2c/dv/env/i2c_virtual_sequencer.sv
+++ b/hw/ip/i2c/dv/env/i2c_virtual_sequencer.sv
@@ -4,10 +4,9 @@
class i2c_virtual_sequencer extends cip_base_virtual_sequencer #(.CFG_T(i2c_env_cfg),
.COV_T(i2c_env_cov));
- `uvm_component_utils(i2c_virtual_sequencer)
-
i2c_sequencer i2c_sequencer_h;
+ `uvm_component_utils(i2c_virtual_sequencer)
`uvm_component_new
endclass : i2c_virtual_sequencer
diff --git a/hw/ip/i2c/dv/env/seq_lib/i2c_base_vseq.sv b/hw/ip/i2c/dv/env/seq_lib/i2c_base_vseq.sv
index a5e0a8b..e8dfef8 100644
--- a/hw/ip/i2c/dv/env/seq_lib/i2c_base_vseq.sv
+++ b/hw/ip/i2c/dv/env/seq_lib/i2c_base_vseq.sv
@@ -10,8 +10,207 @@
);
`uvm_object_utils(i2c_base_vseq)
+ // class property
+ local timing_cfg_t timing_cfg;
+ bit [7:0] rd_data;
+ bit do_interrupt = 1'b1;
+ bit force_use_incorrect_config = 1'b0;
+ // random property
+ rand uint fmt_fifo_access_dly;
+ rand uint rx_fifo_access_dly;
+
+ rand bit [NumI2cIntr-1:0] en_intr;
+ rand uint num_trans;
+ rand uint num_wr_bytes;
+ rand uint num_rd_bytes;
+ rand bit [7:0] wr_data;
+ rand bit [6:0] addr;
+ rand bit rw_bit;
+ i2c_item fmt_item;
+
+ // timing property
+ rand bit [15:0] thigh; // high period of the SCL in clock units
+ rand bit [15:0] tlow; // low period of the SCL in clock units
+ rand bit [15:0] t_r; // rise time of both SDA and SCL in clock units
+ rand bit [15:0] t_f; // fall time of both SDA and SCL in clock units
+ rand bit [15:0] thd_sta; // hold time for (repeated) START in clock units
+ rand bit [15:0] tsu_sta; // setup time for repeated START in clock units
+ rand bit [15:0] tsu_sto; // setup time for STOP in clock units
+ rand bit [15:0] tsu_dat; // data setup time in clock units
+ rand bit [15:0] thd_dat; // data hold time in clock units
+ rand bit [15:0] t_buf; // bus free time between STOP and START in clock units
+ rand bit [30:0] t_timeout; // max time target may stretch the clock
+ rand bit e_timeout; // max time target may stretch the clock
+
+ // constraints
+ constraint rw_bit_c { rw_bit inside {0, 1}; }
+ constraint addr_c { addr inside {[I2C_MIN_ADDR : I2C_MAX_ADDR]}; }
+ constraint wr_data_c { wr_data inside {[I2C_MIN_DATA : I2C_MAX_DATA]}; }
+ constraint num_trans_c { num_trans inside {[I2C_MIN_TRAN : I2C_MAX_TRAN]}; }
+
+ constraint timing_val_c {
+ thigh inside { [I2C_MIN_TIMING : I2C_MAX_TIMING] };
+ t_r inside { [I2C_MIN_TIMING : I2C_MAX_TIMING] };
+ t_f inside { [I2C_MIN_TIMING : I2C_MAX_TIMING] };
+ thd_sta inside { [I2C_MIN_TIMING : I2C_MAX_TIMING] };
+ tsu_sto inside { [I2C_MIN_TIMING : I2C_MAX_TIMING] };
+ tsu_dat inside { [I2C_MIN_TIMING : I2C_MAX_TIMING] };
+ thd_dat inside { [I2C_MIN_TIMING : I2C_MAX_TIMING] };
+ t_timeout inside { [I2C_MIN_TIMING : I2C_MAX_TIMING] };
+ e_timeout inside { [0 : I2C_TIMEOUT_ENB] };
+
+ solve t_r, tsu_dat, thd_dat before tlow;
+ solve t_r before t_buf;
+ if (force_use_incorrect_config) {
+ // force derived timing parameters to be negative (incorrect DUT config)
+ tsu_sta == t_r + t_buf + 1; // negative tHoldStop
+ tlow == 2; // negative tClockLow
+ t_buf == 2;
+ } else {
+ tsu_sta inside { [I2C_MIN_TIMING : I2C_MAX_TIMING] };
+ // force derived timing parameters to be positive (correct DUT config)
+ tlow inside { [(t_r + tsu_dat + thd_dat + 1) :
+ (t_r + tsu_dat + thd_dat + 1) + I2C_TIME_RANGE] };
+ t_buf inside { [(tsu_sta - t_r + 1) :
+ (tsu_sta - t_r + 1) + I2C_TIME_RANGE] };
+ }
+ }
+
+ constraint num_wr_bytes_c {
+ num_wr_bytes dist {
+ 1 :/ 2,
+ [2:5] :/ 5,
+ [6:9] :/ 5,
+ [9:12] :/ 2
+ };
+ }
+ constraint num_rd_bytes_c {
+ num_rd_bytes dist {
+ 0 :/ 1,
+ 1 :/ 2,
+ [2:5] :/ 5,
+ [6:9] :/ 5,
+ [9:12] :/ 2
+ };
+ }
+ constraint en_intr_c {
+ en_intr inside {[0: ((1 << NumI2cIntr) - 1)]};
+ }
+ constraint fmt_fifo_access_dly_c {
+ fmt_fifo_access_dly inside {[1:5]};
+ }
+ constraint rx_fifo_access_dly_c {
+ rx_fifo_access_dly inside {[1:5]};
+ }
+
`uvm_object_new
- // TODO: this vseq will be updated later (with i2c_sanity)
+ virtual task device_init();
+ i2c_device_seq m_dev_seq;
+ m_dev_seq = i2c_device_seq::type_id::create("m_dev_seq");
+ `uvm_info(`gfn, "start i2c_device sequence", UVM_DEBUG)
+ fork
+ m_dev_seq.start(p_sequencer.i2c_sequencer_h);
+ join_none
+ endtask : device_init
+
+ virtual task host_init();
+ `uvm_info(`gfn, "initialize i2c host registers", UVM_DEBUG)
+ ral.ctrl.enablehost.set(1'b1);
+ csr_update(ral.ctrl);
+ if (do_interrupt) begin
+ ral.intr_enable.set(en_intr);
+ csr_update(ral.intr_enable);
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(ral.fifo_ctrl.rxilvl, value <= 4;)
+ `DV_CHECK_RANDOMIZE_FATAL(ral.fifo_ctrl.fmtilvl)
+ csr_update(ral.fifo_ctrl);
+ end
+ endtask : host_init
+
+ function automatic void get_timing_values();
+ // derived timing parameters
+ timing_cfg.enbTimeOut = e_timeout;
+ timing_cfg.tTimeOut = t_timeout;
+ timing_cfg.tSetupStart = t_r + tsu_sta;
+ timing_cfg.tHoldStart = t_f + thd_sta;
+ timing_cfg.tClockStart = thd_dat;
+ timing_cfg.tClockLow = tlow - t_r - tsu_dat - thd_dat;
+ timing_cfg.tSetupBit = t_r + tsu_dat;
+ timing_cfg.tClockPulse = t_r + thigh + t_f;
+ timing_cfg.tHoldBit = t_f + thd_dat;
+ timing_cfg.tClockStop = t_f + tlow - thd_dat;
+ timing_cfg.tSetupStop = t_r + tsu_sto;
+ timing_cfg.tHoldStop = t_r + t_buf - tsu_sta;
+ // ensure these parameter must be greater than zeros
+ if (!force_use_incorrect_config) begin
+ `DV_CHECK_GT_FATAL(timing_cfg.tClockLow, 0)
+ `DV_CHECK_GT_FATAL(timing_cfg.tClockStop, 0)
+ `DV_CHECK_GT_FATAL(timing_cfg.tHoldStop, 0)
+ end
+ endfunction : get_timing_values
+
+ virtual task program_timing_regs();
+ csr_wr(.csr(ral.timing0), .value({tlow, thigh}));
+ csr_wr(.csr(ral.timing1), .value({t_f, t_r}));
+ csr_wr(.csr(ral.timing2), .value({thd_sta, tsu_sta}));
+ csr_wr(.csr(ral.timing3), .value({thd_dat, tsu_dat}));
+ csr_wr(.csr(ral.timing4), .value({t_buf, tsu_sto}));
+ csr_wr(.csr(ral.timeout_ctrl), .value({e_timeout, t_timeout}));
+ // configure i2c_agent_cfg
+ cfg.m_i2c_agent_cfg.timing_cfg = timing_cfg;
+ // set time to stop test
+ cfg.m_i2c_agent_cfg.ok_to_end_delay_ns = DELAY_FOR_EOT_NS;
+ endtask : program_timing_regs
+
+ virtual task program_format_flag(i2c_item item, string msg="");
+ bit [TL_DW-1:0] reg_val;
+
+ csr_spinwait(.ptr(ral.status.fmtfull), .exp_data(1'b0));
+ reg_val = {19'd0, item.nakok, item.rcont, item.read, item.stop, item.start, item.fbyte};
+ csr_wr(.csr(ral.fdata), .value(reg_val));
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(fmt_fifo_access_dly)
+ cfg.m_i2c_agent_cfg.vif.wait_for_dly(fmt_fifo_access_dly);
+ print_format_flag(item, msg);
+ endtask : program_format_flag
+
+ task print_format_flag(i2c_item item, string msg="");
+ string str;
+
+ str = {str, $sformatf("\n%s, format flags 0x%h \n", msg,
+ {item.nakok, item.rcont, item.read, item.stop, item.start, item.fbyte})};
+ if (item.start) begin
+ str = {str, $sformatf(" | %5s | %5s | %5s | %5s | %5s | %8s | %3s |\n",
+ "nakok", "rcont", "read", "stop", "start", "addr", "r/w")};
+ str = {str, $sformatf(" | %5d | %5d | %5d | %5d | %5d | %8x | %3s |",
+ item.nakok, item.rcont, item.read, item.stop, item.start, item.fbyte[7:1],
+ (item.fbyte[0]) ? "R" : "W")};
+ end else begin
+ str = {str, $sformatf(" | %5s | %5s | %5s | %5s | %5s | %8s |\n",
+ "nakok", "rcont", "read", "stop", "start", "fbyte")};
+ str = {str, $sformatf(" | %5d | %5d | %5d | %5d | %5d | %8x |",
+ item.nakok, item.rcont, item.read, item.stop, item.start, item.fbyte)};
+ end
+ `uvm_info(`gfn, $sformatf("%s", str), UVM_DEBUG)
+ endtask : print_format_flag
+
+ //*******************************************
+ // TODO: below functions/tasks
+ //*******************************************
+ virtual task clear_all_interrupts();
+ bit [TL_DW-1:0] data;
+
+ foreach (intr_state_csrs[i]) begin
+ csr_rd(.ptr(intr_state_csrs[i]), .value(data));
+ `uvm_info(`gfn, $sformatf("%s 0x%08h", intr_state_csrs[i].get_name(), data), UVM_DEBUG)
+ if (data != 0) begin
+ csr_wr(.csr(intr_state_csrs[i]), .value(data));
+ csr_rd(.ptr(intr_state_csrs[i]), .value(data));
+ // TODO: check the initial value fmt_watermark interrupt in/out of reset
+ //`DV_CHECK_EQ(data, 0)
+ end
+ end
+ // TODO:
+ //`DV_CHECK_EQ(cfg.intr_vif.sample(), {NUM_MAX_INTERRUPTS{1'b0}})
+ endtask : clear_all_interrupts
endclass : i2c_base_vseq
diff --git a/hw/ip/i2c/dv/env/seq_lib/i2c_common_vseq.sv b/hw/ip/i2c/dv/env/seq_lib/i2c_common_vseq.sv
index 15b2ba5..f964d3c 100644
--- a/hw/ip/i2c/dv/env/seq_lib/i2c_common_vseq.sv
+++ b/hw/ip/i2c/dv/env/seq_lib/i2c_common_vseq.sv
@@ -17,8 +17,8 @@
virtual task body();
// disable i2c_monitor since it can not handle this test
- `uvm_info(`gfn, $sformatf("disable i2c_monitor"), UVM_DEBUG)
- cfg.m_i2c_agent_cfg.en_monitor <= 1'b0;
+ `uvm_info(`gfn, $sformatf("disable i2c_monitor and i2c_scoreboard"), UVM_DEBUG)
+ cfg.m_i2c_agent_cfg.en_monitor = 1'b0;
run_common_vseq_wrapper(num_trans); // inherit from cip_base_vseq.sv
endtask : body
diff --git a/hw/ip/i2c/dv/env/seq_lib/i2c_rx_tx_vseq.sv b/hw/ip/i2c/dv/env/seq_lib/i2c_rx_tx_vseq.sv
new file mode 100644
index 0000000..283aa3d
--- /dev/null
+++ b/hw/ip/i2c/dv/env/seq_lib/i2c_rx_tx_vseq.sv
@@ -0,0 +1,128 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+class i2c_rx_tx_vseq extends i2c_base_vseq;
+ `uvm_object_utils(i2c_rx_tx_vseq)
+
+ `uvm_object_new
+
+ virtual task host_init();
+ bit fmtrst, rxrst;
+ bit [1:0] fmtilvl, rxilvl;
+ bit [TL_DW-1:0] reg_val;
+
+ super.host_init();
+ // diable override
+ ral.ovrd.txovrden.set(1'b0);
+ csr_update(ral.ovrd);
+ // reset and set level of rx and fmt fifos
+ rxrst = 1'b1;
+ fmtrst = 1'b1;
+ rxilvl = 2'd3;
+ fmtilvl = 2'd3;
+ reg_val = {24'd0, fmtilvl, rxilvl, fmtrst, rxrst};
+ csr_wr(.csr(ral.fifo_ctrl), .value(reg_val));
+ endtask : host_init
+
+ virtual task host_send_trans(int num_trans);
+ bit last_tran;
+
+ // setup config
+ force_use_incorrect_config = 1'b0;
+ fmt_item = new("fmt_item");
+ `DV_CHECK_RANDOMIZE_FATAL(this)
+ get_timing_values();
+ program_timing_regs();
+
+ for (uint cur_tran = 1; cur_tran <= num_trans; cur_tran++) begin
+ last_tran = (cur_tran == num_trans) ? 1'b1 : 1'b0;
+ // not program address for chained reads
+ host_program_target_address();
+ `uvm_info(`gfn, $sformatf("start sending %s transaction %0d/%0d",
+ (rw_bit) ? "READ" : "WRITE", cur_tran, num_trans), UVM_DEBUG)
+ if (rw_bit) host_read_trans(last_tran);
+ else host_write_trans(last_tran);
+ `uvm_info(`gfn, $sformatf("finish sending %s transaction, %0s at the end, %0d/%0d, ",
+ (rw_bit) ? "read" : "write",
+ (fmt_item.stop) ? "stop" : "rstart", cur_tran, num_trans), UVM_DEBUG)
+ end
+ endtask : host_send_trans
+
+ virtual task host_program_target_address();
+ // programm fmt flags for address
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(fmt_item)
+ fmt_item.start = 1'b1;
+ fmt_item.stop = 1'b0;
+ fmt_item.read = 1'b0;
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(addr)
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(rw_bit)
+ fmt_item.fbyte = (rw_bit) ? {addr, BusOpRead} : {addr, BusOpWrite};
+ program_format_flag(fmt_item, "host_program_target_address");
+ endtask : host_program_target_address
+
+ virtual task host_read_trans(bit last_tran);
+ uint real_rd_bytes;
+ uint total_rd_bytes = 0;
+
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(num_rd_bytes)
+ fork
+ begin
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(fmt_item)
+ fmt_item.fbyte = num_rd_bytes;
+ fmt_item.start = 1'b0;
+ fmt_item.read = 1'b1;
+ fmt_item.rcont = 1'b0; // TODO: no chained read support
+ // for the last write byte of last tran., stop flag must be set to issue stop bit (stimulus end)
+ // otherwise, stop can be randomly set/unset to issue stop/rstart bit respectively
+ fmt_item.stop = (last_tran) ? 1'b1 : $urandom_range(0, 1);
+ real_rd_bytes = (num_rd_bytes) ? num_rd_bytes : 256;
+ total_rd_bytes += real_rd_bytes;
+ program_format_flag(fmt_item, "program number of bytes to read");
+ end
+ begin
+ if (!cfg.do_rd_overflow) begin
+ while (total_rd_bytes > 0) begin
+ csr_spinwait(.ptr(ral.status.rxempty), .exp_data(1'b0));
+ csr_rd(.ptr(ral.rdata), .value(rd_data));
+ total_rd_bytes--;
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(rx_fifo_access_dly)
+ cfg.m_i2c_agent_cfg.vif.wait_for_dly(rx_fifo_access_dly);
+ end
+ end
+ end
+ join
+ endtask : host_read_trans
+
+ virtual task host_write_trans(bit last_tran);
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(num_wr_bytes)
+ for (int i = 1; i <= num_wr_bytes; i++) begin
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(fmt_item)
+ fmt_item.start = 1'b0;
+ fmt_item.read = 1'b0;
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(wr_data)
+ fmt_item.fbyte = wr_data;
+ // last write byte of last tran., stop flag must be set to issue stop bit
+ // last write byte of other tran., stop is randomly set/unset to issue stop/rstart bit
+ fmt_item.stop = (i != num_wr_bytes) ? 1'b0 : ((last_tran) ? 1'b1 : fmt_item.stop);
+ program_format_flag(fmt_item, "host_write_trans");
+ end
+ endtask : host_write_trans
+
+ virtual task process_interrupts();
+ bit [TL_DW-1:0] intr_status, clear_intr;
+ bit clear_rx_intr, clear_tx_intr;
+
+ csr_rd(.ptr(ral.intr_state), .value(intr_status));
+ `uvm_info(`gfn, $sformatf("intr_state 0x%08h", intr_status), UVM_HIGH)
+ // TODO: interrupts must be properly handled before EoT
+ endtask : process_interrupts
+
+ virtual task post_start();
+ bit [TL_DW-1:0] intr_status;
+
+ do_dut_shutdown = 0;
+ super.post_start();
+ endtask : post_start
+
+endclass : i2c_rx_tx_vseq
diff --git a/hw/ip/i2c/dv/env/seq_lib/i2c_sanity_vseq.sv b/hw/ip/i2c/dv/env/seq_lib/i2c_sanity_vseq.sv
new file mode 100644
index 0000000..e0fa0b5
--- /dev/null
+++ b/hw/ip/i2c/dv/env/seq_lib/i2c_sanity_vseq.sv
@@ -0,0 +1,29 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+// basic sanity test vseq
+class i2c_sanity_vseq extends i2c_rx_tx_vseq;
+ `uvm_object_utils(i2c_sanity_vseq)
+ `uvm_object_new
+
+ constraint num_wr_bytes_c { num_wr_bytes inside {[1 : 5]}; }
+ constraint num_rd_bytes_c { num_rd_bytes inside {[1 : 5]}; }
+ constraint num_trans_c { num_trans inside {100}; }
+
+ task pre_start();
+ super.pre_start();
+ do_interrupt = 1'b0;
+ endtask : pre_start
+
+ task body();
+ device_init();
+ host_init();
+
+ fork
+ while (do_interrupt) process_interrupts();
+ host_send_trans(num_trans);
+ join
+ endtask : body
+
+endclass : i2c_sanity_vseq
diff --git a/hw/ip/i2c/dv/env/seq_lib/i2c_vseq_list.sv b/hw/ip/i2c/dv/env/seq_lib/i2c_vseq_list.sv
index cb6b288..478df59 100644
--- a/hw/ip/i2c/dv/env/seq_lib/i2c_vseq_list.sv
+++ b/hw/ip/i2c/dv/env/seq_lib/i2c_vseq_list.sv
@@ -4,3 +4,5 @@
`include "i2c_base_vseq.sv"
`include "i2c_common_vseq.sv"
+`include "i2c_rx_tx_vseq.sv"
+`include "i2c_sanity_vseq.sv"
diff --git a/hw/ip/i2c/dv/i2c_sim_cfg.hjson b/hw/ip/i2c/dv/i2c_sim_cfg.hjson
index 66c829c..34ed3d4 100644
--- a/hw/ip/i2c/dv/i2c_sim_cfg.hjson
+++ b/hw/ip/i2c/dv/i2c_sim_cfg.hjson
@@ -29,6 +29,7 @@
// Common CIP test lists
"{proj_root}/hw/dv/data/tests/csr_tests.hjson",
"{proj_root}/hw/dv/data/tests/intr_test.hjson",
+ //TODO: enable later in V2
//"{proj_root}/hw/dv/data/tests/stress_tests.hjson",
"{proj_root}/hw/dv/data/tests/tl_access_tests.hjson"]
@@ -51,10 +52,10 @@
]
// List of regressions.
- //regressions: [
- // {
- // name: sanity
- // tests: ["i2c_sanity"]
- // }
- //]
+ regressions: [
+ {
+ name: sanity
+ tests: ["i2c_sanity"]
+ }
+ ]
}