Skip to content

Commit

Permalink
[i2c,dv] target mode clock stretch tests
Browse files Browse the repository at this point in the history
stretch test

Signed-off-by: Jaedon Kim <[email protected]>
  • Loading branch information
jdonjdon committed Nov 15, 2022
1 parent ee506b4 commit 95fba94
Show file tree
Hide file tree
Showing 15 changed files with 361 additions and 33 deletions.
19 changes: 18 additions & 1 deletion hw/dv/sv/i2c_agent/i2c_agent_cfg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,24 @@ class i2c_agent_cfg extends dv_base_agent_cfg;

bit host_scl_start;
bit host_scl_stop;
event got_stop;

// In i2c test, between every transaction, assuming a new timing
// parameter is programmed. This means during a transaction,
// test should not update timing parameter.
// This variable indicates target DUT receives the end of the transaction
// and allow tb to program a new timing parameter.
bit got_stop = 0;

// In target mode i2c test, sequence finishes when all host transactions are fetched.
// However, test has to wait until all read data transferred from DUT to test bench.
// If test create a scinario to trigger clock stretch, TB has to wait indefinte time
// (because clock stretch froze transmit clock).
// This varialbe to indicate all read / write data fetch and trasmitted.
// Used only for stretched mode test.
bit use_seq_term = 0;

int sent_rd_byte = 0;
int rcvd_rd_byte = 0;

// this variables can be configured from host test
uint i2c_host_min_data_rw = 1;
Expand Down
2 changes: 1 addition & 1 deletion hw/dv/sv/i2c_agent/i2c_driver.sv
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class i2c_driver extends dv_base_driver #(i2c_item, i2c_agent_cfg);
i2c_item req;
@(posedge cfg.vif.rst_ni);
forever begin
release_bus();
if (cfg.if_mode == Device) release_bus();
// driver drives bus per mode
seq_item_port.get_next_item(req);
fork
Expand Down
21 changes: 20 additions & 1 deletion hw/dv/sv/i2c_agent/i2c_if.sv
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ interface i2c_if(

string msg_id = "i2c_if";

int scl_spinwait_timeout_ns = 10_000_000; // 10ms

clocking cb @(posedge clk_i);
input scl_i;
input sda_i;
Expand All @@ -43,6 +45,21 @@ interface i2c_if(
wait(cb.scl_i == 1);
endtask

task automatic sample_target_data(timing_cfg_t tc, output bit data);
bit sample[16];
int idx = 0;
int su_idx;

wait(cb.scl_i == 0);
while (cb.scl_i == 0) begin
@(posedge clk_i);
sample[idx] = cb.sda_i;
idx = (idx + 1) % 16;
end
su_idx = (idx + 16 - 1 - tc.tSetupBit) % 16;
data = sample[su_idx];
endtask // sample_target_data

task automatic wait_for_dly(int dly);
repeat (dly) @(posedge clk_i);
endtask : wait_for_dly
Expand Down Expand Up @@ -236,6 +253,7 @@ interface i2c_if(
endtask: get_bit_data

task automatic host_start(ref timing_cfg_t tc);
`DV_WAIT(scl_i === 1'b1,, scl_spinwait_timeout_ns, "host_start")
sda_o = 1'b0;
wait_for_dly(tc.tHoldStart);
scl_o = 1'b0;
Expand All @@ -261,11 +279,12 @@ interface i2c_if(
endtask: host_data

task automatic host_stop(ref timing_cfg_t tc);
wait(scl_i === 1'b1);
sda_o = 1'b0;
wait_for_dly(tc.tClockStop);
scl_o = 1'b1;
wait_for_dly(tc.tSetupStop);
sda_o = 1'b0;
sda_o = 1'b1;
wait_for_dly(tc.tHoldStop);
endtask: host_stop

Expand Down
42 changes: 25 additions & 17 deletions hw/dv/sv/i2c_agent/i2c_monitor.sv
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,11 @@ class i2c_monitor extends dv_base_monitor #(
mon_dut_item.bus_op = (r_bit) ? BusOpRead : BusOpWrite;

// expect target ack
cfg.vif.p_edge_scl();
r_bit = cfg.vif.cb.sda_i;
cfg.vif.sample_target_data(cfg.timing_cfg, r_bit);

`DV_CHECK_CASE_EQ(r_bit, 1'b0)

if (mon_dut_item.bus_op == BusOpRead)
target_read();
if (mon_dut_item.bus_op == BusOpRead) target_read();
else target_write();

// send rsp_item to scoreboard
Expand Down Expand Up @@ -241,16 +240,22 @@ class i2c_monitor extends dv_base_monitor #(
// update of_to_end to prevent sim finished when there is any activity on the bus
// ok_to_end = 0 (bus busy) / 1 (bus idle)
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);
if (cfg.if_mode == Host) begin
int scl_cnt = 0;
if (cfg.en_monitor) begin
if (cfg.if_mode == Host) begin
// TODO: set end condition if necessary
end else begin
ok_to_end = (cfg.vif.scl_i == 1'b1) && (cfg.vif.sda_i == 1'b1);
end
end else begin
ok_to_end = 1;
ok_to_end = 0;
end
wait(cfg.use_seq_term == 0);
forever begin
@(cfg.vif.cb);
if (cfg.vif.scl_i) scl_cnt++;
else scl_cnt = 0;
if (scl_cnt > 100) ok_to_end = 1;
end
end else begin
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);
end
end
endtask : monitor_ready_to_end
Expand All @@ -266,8 +271,7 @@ class i2c_monitor extends dv_base_monitor #(
// ask driver response read data
mon_dut_item.drv_type = RdData;
for (int i = 7; i >= 0; i--) begin
cfg.vif.p_edge_scl();
mon_data[i] = cfg.vif.cb.sda_i;
cfg.vif.sample_target_data(cfg.timing_cfg, mon_data[i]);
`uvm_info(`gfn, $sformatf("\nmonitor, target_read, trans %0d, byte %0d, bit[%0d] %0b",
mon_dut_item.tran_id, mon_dut_item.num_data+1, i, mon_data[i]), UVM_HIGH)
end
Expand All @@ -279,6 +283,9 @@ class i2c_monitor extends dv_base_monitor #(
`DV_CHECK_NE_FATAL({mon_dut_item.ack, mon_dut_item.nack}, 2'b11)
`uvm_info(`gfn, $sformatf("\nmonitor, target_read detect HOST %s",
(mon_dut_item.ack) ? "ACK" : "NO_ACK"), UVM_MEDIUM)

cfg.rcvd_rd_byte++;

// if nack is issued, next bit must be stop or rstart
if (mon_dut_item.nack) begin
cfg.vif.wait_for_host_stop_or_rstart(cfg.timing_cfg,
Expand All @@ -287,7 +294,7 @@ class i2c_monitor extends dv_base_monitor #(
`DV_CHECK_NE_FATAL({mon_dut_item.rstart, mon_dut_item.stop}, 2'b11)
`uvm_info(`gfn, $sformatf("\nmonitor, target_read, detect HOST %s",
(mon_dut_item.stop) ? "STOP" : "RSTART"), UVM_MEDIUM)
if (mon_dut_item.stop) ->cfg.got_stop;
if (mon_dut_item.stop) cfg.got_stop = 1;
end
end
endtask
Expand All @@ -307,14 +314,15 @@ class i2c_monitor extends dv_base_monitor #(
`uvm_info(`gfn, $sformatf("\nmonitor, target_write detect HOST %s",
(!r_bit) ? "ACK" : "NO_ACK"), UVM_MEDIUM)
// if nack is issued, next bit must be stop or rstart

if (!r_bit) begin
cfg.vif.wait_for_host_stop_or_rstart(cfg.timing_cfg,
mon_dut_item.rstart,
mon_dut_item.stop);
`DV_CHECK_NE_FATAL({mon_dut_item.rstart, mon_dut_item.stop}, 2'b11)
`uvm_info(`gfn, $sformatf("\nmonitor, rd_data, detect HOST %s",
(mon_dut_item.stop) ? "STOP" : "RSTART"), UVM_MEDIUM)
if (mon_dut_item.stop) ->cfg.got_stop;
if (mon_dut_item.stop) cfg.got_stop = 1;
end
end
endtask // target_write
Expand Down
23 changes: 19 additions & 4 deletions hw/ip/i2c/data/i2c_testplan.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -351,11 +351,10 @@

Checking:
- During read transaction, ensure tx_empty interrupt is asserted when no data left
in tx_fifo
otherwise tx_empty interrupt must be asserted
in tx_fifo otherwise tx_empty interrupt must be de-asserted
'''
stage: V2
tests: []
tests: ["i2c_target_stress_rd"]
}
{
name: target_fifo_reset
Expand Down Expand Up @@ -387,7 +386,7 @@
- Check fifo full states by reading status register
'''
stage: V2
tests: [""]
tests: ["i2c_target_stress_wr"]
}
{
name: target_timeout
Expand All @@ -406,5 +405,21 @@
stage: V2
tests: [""]
}
{
name: target_clock_stretch
desc: '''
Test clock stretch feature of DUT Target mode.
For the write and address transaction, when acq fifo is full, DUT starts to stretch clock.
For the read transaction, when dut receives read command, the tx fifo is empty,
DUT starts to stretch clock.
Using read / write mixed traffic, trigger stretch condition by slowing down acq / tx
fifo process.

Checking:
Ensure all read /write data received correct on the other side without dropping any data.
'''
stage: V2
tests: ["i2c_target_stretch"]
}
]
}
3 changes: 3 additions & 0 deletions hw/ip/i2c/dv/env/i2c_env.core
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ filesets:
- seq_lib/i2c_host_error_intr_vseq.sv: {is_include_file: true}
- seq_lib/i2c_host_stress_all_vseq.sv: {is_include_file: true}
- seq_lib/i2c_target_smoke_vseq.sv: {is_include_file: true}
- seq_lib/i2c_target_stress_wr_vseq.sv: {is_include_file: true}
- seq_lib/i2c_target_stress_rd_vseq.sv: {is_include_file: true}
- seq_lib/i2c_target_stretch_vseq.sv: {is_include_file: true}
file_type: systemVerilogSource

generate:
Expand Down
1 change: 1 addition & 0 deletions hw/ip/i2c/dv/env/i2c_env.sv
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class i2c_env extends cip_base_env #(
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);
cfg.m_i2c_agent_cfg.en_cov = cfg.en_cov;
cfg.m_i2c_agent_cfg.en_monitor = 1'b1;
endfunction : build_phase

function void connect_phase(uvm_phase phase);
Expand Down
1 change: 1 addition & 0 deletions hw/ip/i2c/dv/env/i2c_env_cfg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class i2c_env_cfg extends cip_base_env_cfg #(.RAL_T(i2c_reg_block));

tran_type_e trans_type = ReadWrite;

int spinwait_timeout_ns = 10_000_000; // 10ms
int sent_acq_cnt;
int rcvd_acq_cnt;

Expand Down
Loading

0 comments on commit 95fba94

Please sign in to comment.