blob: 9e9a7962a88a23df05159dfc2344551ffba5800d [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// By default this test will use a randomly selected instance i2c_idx.
// To make a dedicated test for instance i, where i is in {0,1,2}, the test
// shuld run with an option:
// run_opts: ["+i2c_idx=i"]
class chip_sw_i2c_device_tx_rx_vseq extends chip_sw_i2c_tx_rx_vseq;
`uvm_object_utils(chip_sw_i2c_device_tx_rx_vseq)
`uvm_object_new
int exp_rd_id = 0;
int tran_id = 0;
i2c_item read_txn_q[$];
rand int i2c_idx;
constraint i2c_idx_c {
i2c_idx inside {[0:NUM_I2CS-1]};
}
rand int byte_count;
constraint i2c_byte_count_c {
byte_count inside {[30:60]};
}
rand bit [6:0] i2c_device_address_0;
rand bit [6:0] i2c_device_address_1;
rand bit [6:0] i2c_device_mask_0;
rand bit [6:0] i2c_device_mask_1;
rand bit choose_address;
rand bit [6:0] r;
virtual task cpu_init();
bit[7:0] clock_period_nanos_arr[1] = {clock_period_nanos};
bit[7:0] rise_fall_nanos_arr[1] = {rise_fall_nanos};
bit[7:0] i2c_clock_period_nanos_arr[4] = {<<byte{i2c_clock_period_nanos}};
bit[7:0] i2c_idx_arr[1];
bit[7:0] i2c_device_address_0_arr[1];
bit[7:0] i2c_device_mask_0_arr[1];
bit[7:0] i2c_device_address_1_arr[1];
bit[7:0] i2c_device_mask_1_arr[1];
bit[7:0] byte_count_arr[1];
void'($value$plusargs("i2c_idx=%0d", i2c_idx));
`DV_CHECK(i2c_idx inside {[0:NUM_I2CS-1]})
i2c_idx_arr = {i2c_idx};
i2c_device_address_0_arr = {i2c_device_address_0};
i2c_device_address_1_arr = {i2c_device_address_1};
// if an address bit is 1, the corresponding mask bit must also be 1
i2c_device_mask_0_arr = {i2c_device_address_0 | i2c_device_mask_0};
i2c_device_mask_1_arr = {i2c_device_address_1 | i2c_device_mask_1};
byte_count_arr = {byte_count};
super.cpu_init();
// need to figure out a better way to calculate this based on tb clock frequency
sw_symbol_backdoor_overwrite("kClockPeriodNanos", clock_period_nanos_arr);
sw_symbol_backdoor_overwrite("kI2cRiseFallNanos", rise_fall_nanos_arr);
sw_symbol_backdoor_overwrite("kI2cClockPeriodNanos", i2c_clock_period_nanos_arr);
sw_symbol_backdoor_overwrite("kI2cIdx", i2c_idx_arr);
sw_symbol_backdoor_overwrite("kI2cDeviceAddress0", i2c_device_address_0_arr);
sw_symbol_backdoor_overwrite("kI2cDeviceMask0", i2c_device_mask_0_arr);
sw_symbol_backdoor_overwrite("kI2cDeviceAddress1", i2c_device_address_1_arr);
sw_symbol_backdoor_overwrite("kI2cDeviceMask1", i2c_device_mask_1_arr);
sw_symbol_backdoor_overwrite("kI2cByteCount", byte_count_arr);
// sw_symbol_backdoor_overwrite("expected_data", i2c_device_tx_data);
endtask
virtual task body();
bit [7:0] i2c_device_tx_data[$];
super.body();
// enable the monitor
cfg.m_i2c_agent_cfgs[i2c_idx].en_monitor = 1'b1;
cfg.m_i2c_agent_cfgs[i2c_idx].if_mode = Host;
// Enbale appropriate interface
cfg.chip_vif.enable_i2c(.inst_num(i2c_idx), .enable(1));
// tClockLow needs to be "slightly" shorter than the actual clock low period
cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tClockLow = half_period_cycles - 2;
// tClockPulse needs to be "slightly" longer than the clock period.
cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tClockPulse = half_period_cycles + 1;
// tHoldStart and tClockStart need to be non-zero to make the start condition detectable.
cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tHoldStart = half_period_cycles;
cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tClockStart = half_period_cycles;
// tClockStop, tSetupStop, and tHoldStop have to be non-zero to make the stop condition
// detectable.
cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tClockStop = half_period_cycles;
cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tSetupStop = half_period_cycles - 2;
cfg.m_i2c_agent_cfgs[i2c_idx].timing_cfg.tHoldStop = half_period_cycles;
// Wait for i2c_device to fill tx_fifo
`DV_WAIT(cfg.sw_logger_vif.printed_log == "Data written to fifo");
send_i2c_host_rx_data(i2c_device_tx_data, byte_count);
endtask
virtual task send_i2c_host_rx_data(ref bit [7:0] device_data[$],input int size);
i2c_target_base_seq m_i2c_host_seq;
i2c_item txn_q[$];
`uvm_create_obj(i2c_target_base_seq, m_i2c_host_seq)
create_txn(txn_q, size);
fetch_txn(txn_q, m_i2c_host_seq.req_q, .is_read(1));
m_i2c_host_seq.start(p_sequencer.i2c_sequencer_hs[i2c_idx]);
// Wait for reads to complete.
`DV_WAIT(cfg.m_i2c_agent_cfgs[i2c_idx].sent_rd_byte > 0)
`DV_WAIT(cfg.m_i2c_agent_cfgs[i2c_idx].sent_rd_byte ==
cfg.m_i2c_agent_cfgs[i2c_idx].rcvd_rd_byte)
`uvm_info(`gfn, "Reads complete.", UVM_MEDIUM)
// Play transactions back.
txn_q.delete();
while (p_sequencer.i2c_rd_fifos[i2c_idx].can_get()) begin
i2c_item txn;
`DV_CHECK(p_sequencer.i2c_rd_fifos[i2c_idx].try_get(txn))
foreach (txn.data_q[i]) begin
i2c_item wr_txn;
`uvm_create_obj(i2c_item, wr_txn)
wr_txn.drv_type = HostData;
wr_txn.wdata = txn.data_q[i];
txn_q.push_back(wr_txn);
end
end
fetch_txn(txn_q, m_i2c_host_seq.req_q, .is_read(0));
m_i2c_host_seq.start(p_sequencer.i2c_sequencer_hs[i2c_idx]);
endtask
// Create byte transaction (payload) to read or write.
// Transaction type (read, write) is decided in 'fetch_txn'.
// This function and `fetch_txn` are largely duplicates of the code in `i2c_base_vseq`, so at
// some point it may be worth refactoring the common parts into a separate class or package to
// deduplicate code.
virtual function void create_txn(ref i2c_item myq[$], int num_bytes);
bit [7:0] wdata_q[$];
i2c_item txn;
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(wdata_q,
wdata_q.size() == num_bytes;)
for (int i = 0; i < wdata_q.size(); i++) begin
`uvm_create_obj(i2c_item, txn)
txn.drv_type = HostData;
txn.wdata = wdata_q[i];
myq.push_back(txn);
end
endfunction
// Receive byte stream from 'create_txn' and forward to driver's request q,
// with adding 'Start and Stop'.
// Expected transaction is created in the task.
// For write transaction and address transaction, expected transaction mimics acq read data.
// For read transaction, all read bytes for one transaction is accumulated to 'full_txn'
// and compared with received transaction at the scoreboard.
virtual function void fetch_txn(ref i2c_item src_q[$], i2c_item dst_q[$], input bit is_read);
i2c_item txn;
i2c_item rs_txn;
i2c_item exp_txn;
i2c_item full_txn;
int read_size;
bit [6:0] address_0;
bit [6:0] address_1;
`uvm_create_obj(i2c_item, full_txn)
// Add 'START' to the front
`uvm_create_obj(i2c_item, txn)
txn.drv_type = HostStart;
dst_q.push_back(txn);
full_txn.start = 1;
if (is_read) full_txn.tran_id = this.exp_rd_id++;
// Address
`uvm_create_obj(i2c_item, txn)
`uvm_create_obj(i2c_item, exp_txn)
// randomly select any matching address
address_0 = i2c_device_address_0 | (r & ~i2c_device_mask_0);
address_1 = i2c_device_address_1 | (r & ~i2c_device_mask_1);
txn.drv_type = HostData;
txn.start = 1;
txn.wdata[7:1] = choose_address ? address_0 : address_1;
txn.wdata[0] = is_read;
txn.tran_id = this.tran_id++;
`downcast(exp_txn, txn.clone());
dst_q.push_back(txn);
full_txn.addr = txn.wdata[7:1];
full_txn.read = is_read;
read_size = byte_count;
cfg.m_i2c_agent_cfgs[i2c_idx].sent_rd_byte += read_size;
// Data
while (src_q.size() > 0) begin
`uvm_create_obj(i2c_item, txn)
txn = src_q.pop_front();
if (txn.drv_type != HostRStart) begin
// Restart only has empty data for address holder
full_txn.data_q.push_back(txn.wdata);
end
// RS creates 2 extra acq entry
// one for RS
// the other for a new start acq_entry with address
if (txn.drv_type == HostRStart) begin
bit prv_read = 0;
`uvm_create_obj(i2c_item, exp_txn)
exp_txn.drv_type = HostRStart;
exp_txn.rstart = 1;
exp_txn.tran_id = this.tran_id++;
`uvm_create_obj(i2c_item, rs_txn)
`downcast(rs_txn, txn.clone())
dst_q.push_back(txn);
rs_txn.drv_type = HostData;
rs_txn.start = 1;
rs_txn.rstart = 0;
rs_txn.wdata[7:1] = choose_address ? address_0 : address_1;
prv_read = is_read;
is_read = rs_txn.read;
rs_txn.wdata[0] = is_read;
dst_q.push_back(rs_txn);
// create a separate stat/addr entry for exp
`uvm_create_obj(i2c_item, exp_txn)
`downcast(exp_txn, rs_txn.clone());
exp_txn.tran_id = this.tran_id++;
// fetch previous full_txn and creat a new one
if (prv_read) begin
full_txn.stop = 1;
end
`uvm_create_obj(i2c_item, full_txn)
`downcast(full_txn, rs_txn);
if (is_read) begin
full_txn.tran_id = exp_rd_id++;
end
end else begin
if (is_read) begin
i2c_item read_txn;
`uvm_create_obj(i2c_item, read_txn)
`downcast(read_txn, txn.clone())
full_txn.num_data++;
if (src_q.size() == 0) begin
txn.drv_type = HostNAck;
end else begin
// if your next item is restart Do nack
if (src_q[0].drv_type == HostRStart) txn.drv_type = HostNAck;
else txn.drv_type = HostAck;
end
dst_q.push_back(txn);
read_txn_q.push_back(read_txn);
end else begin
`downcast(exp_txn, txn.clone());
// Add RS transaction to driver only
// and create address transaction after
dst_q.push_back(txn);
exp_txn.tran_id = this.tran_id++;
end
end
end // while (src_q.size() > 0)
// Stop
`uvm_create_obj(i2c_item, txn)
`uvm_create_obj(i2c_item, exp_txn)
txn.tran_id = this.tran_id++;
txn.stop = 1;
txn.drv_type = HostStop;
`downcast(exp_txn, txn.clone());
dst_q.push_back(txn);
full_txn.stop = 1;
endfunction
endclass : chip_sw_i2c_device_tx_rx_vseq