[i2c,dv] Extend i2c_agent to verify host clock stretching Host can stretch its clock (scl_o) in case device needs more time to proceed a received byte. Meaning that, the device pulls down scl_i after clock cycle 9th then the host can hold its scl_o at high until the device releases scl_i. To enable clock stretching feature for the host, the TIMEOUT_CTRL.EN must be set, and TIMEOUT_CTRL.VAL must be programmed to a value which is higher than the basic clock pulse. Once the host clock is stretched longer than the basic clock pulse, the intr_stretch_timeout_o will be asserted. Signed-off-by: Tung Hoang <tung.hoang.290780@gmail.com>
diff --git a/hw/dv/sv/i2c_agent/i2c_device_driver.sv b/hw/dv/sv/i2c_agent/i2c_device_driver.sv index 362880e..fc500d8 100644 --- a/hw/dv/sv/i2c_agent/i2c_device_driver.sv +++ b/hw/dv/sv/i2c_agent/i2c_device_driver.sv
@@ -11,7 +11,9 @@ constraint rd_data_c { rd_data inside {[0 : 127]}; } virtual task get_and_drive(); + int num_stretch_host_clks; i2c_item rsp_item; + @(posedge cfg.vif.rst_ni); forever begin cfg.vif.scl_o = 1'b1; @@ -20,7 +22,23 @@ seq_item_port.get_next_item(rsp_item); unique case (rsp_item.drv_type) DevAck: begin - cfg.vif.device_send_ack(cfg.timing_cfg); + fork + begin + // host clock stretching allows a high-speed host to communicate + // with a low-speed device by setting TIMEOUT_CTRL.EN bit + // the device ask host clock stretching its clock scl_i by pulling down scl_o + // the host clock pulse is extended until device scl_o is pulled up + // once scl_o is pulled down longer than TIMEOUT_CTRL.VAL field, + // intr_stretch_timeout_o is asserted (ref. https://www.i2c-bus.org/clock-stretching) + if (cfg.timing_cfg.enbTimeOut) begin + num_stretch_host_clks = gen_num_stretch_host_clks(cfg.timing_cfg); + cfg.vif.device_stretch_host_clk(cfg.timing_cfg, num_stretch_host_clks); + end + end + begin + cfg.vif.device_send_ack(cfg.timing_cfg); + end + join end RdData: begin `DV_CHECK_MEMBER_RANDOMIZE_FATAL(rd_data) @@ -38,5 +56,13 @@ end endtask : get_and_drive + function int gen_num_stretch_host_clks(ref timing_cfg_t tc); + // By randomly pulling down scl_o "offset" within [0:2*tc.tTimeOut], + // intr_stretch_timeout_o interrupt would be generated uniformly + // To test this feature more regressive, there might need a dedicated vseq (V2) + // in which TIMEOUT_CTRL.EN is always set. + return $urandom_range(tc.tClockPulse, tc.tClockPulse + 2*tc.tTimeOut); + endfunction : gen_num_stretch_host_clks + endclass : i2c_device_driver
diff --git a/hw/dv/sv/i2c_agent/i2c_if.sv b/hw/dv/sv/i2c_agent/i2c_if.sv index a96a1f8..cdd872a 100644 --- a/hw/dv/sv/i2c_agent/i2c_if.sv +++ b/hw/dv/sv/i2c_agent/i2c_if.sv
@@ -138,7 +138,6 @@ endtask: wait_for_device_ack task automatic device_send_ack(ref timing_cfg_t tc); - device_stretch_clk(tc); sda_o = 1'b1; wait_for_dly(tc.tClockLow); sda_o = 1'b0; @@ -150,7 +149,6 @@ task automatic device_send_bit(ref timing_cfg_t tc, input bit bit_i); - device_stretch_clk(tc); sda_o = 1'b1; wait_for_dly(tc.tClockLow); sda_o = bit_i; @@ -160,13 +158,13 @@ sda_o = 1'b1; endtask: device_send_bit - task automatic device_stretch_clk(ref timing_cfg_t tc); - if (tc.enbTimeOut) begin - scl_o = 1'b0; - wait_for_dly(tc.tTimeOut); - scl_o = 1'b1; - end - endtask : device_stretch_clk + task automatic device_stretch_host_clk(ref timing_cfg_t tc, + input int num_stretch_host_clks); + wait_for_dly(tc.tClockLow + tc.tSetupBit); + scl_o = 1'b0; + wait_for_dly(num_stretch_host_clks); + scl_o = 1'b1; + endtask : device_stretch_host_clk task automatic get_bit_data(string src = "host", ref timing_cfg_t tc,