blob: ceb49b3c4c511d5d1701cc14219789f5e7b0c011 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
class i2c_device_driver extends i2c_driver;
`uvm_component_utils(i2c_device_driver)
bit [data_width-1:0] data_byte;
bit [addr_width-1:0] addr_byte;
bit rw_dir;
`uvm_component_new
virtual task run_phase(uvm_phase phase);
get_and_drive();
endtask : run_phase
// drive trans received from sequencer
virtual task get_and_drive();
forever begin
seq_item_port.get(req); // get req handler with type i2c_item
$cast(rsp, req.clone());
rsp.set_id_info(req);
`uvm_info(`gfn, $sformatf("i2c gets next item:\n%0s", req.sprint()), UVM_DEBUG)
// address phase
process_address();
// data phase
if (rw_dir == 1'b1 && cfg.en_monitor == 1'b1) process_read_data();
else if (rw_dir == 1'b0 && cfg.en_monitor == 1'b1) process_write_data();
// send rsp back to seq
`uvm_info(`gfn, "device item sent", UVM_DEBUG)
seq_item_port.put_response(rsp);
end
endtask : get_and_drive
task process_address();
// device waits for START/RESTART on bus
cfg.vif.wait_for_start_repstart_from_host(cfg.thd_sta, cfg.tsu_sta);
`uvm_info(`gfn, $sformatf("i2c request type %d (R/W=1/0)", addr_byte[0]), UVM_DEBUG)
// device gets address bits from host
for (int i = 1; i < addr_width; i++) begin
wait(cfg.vif.scl_i == 1'b1);
addr_byte[i] = cfg.vif.sda_i;
cfg.vif.bus_status = HostSendAddr;
end
// device gets r/w bit from host
wait(cfg.vif.scl_i == 1'b1);
addr_byte[0] = cfg.vif.sda_i;
rw_dir = cfg.vif.sda_i;
cfg.vif.bus_status = HostSendRWBit;
rsp.addr = addr_byte;
rsp.mem_addrs.push_front(addr_byte);
// device sends ack to host
cfg.vif.wait_for_dly(req.dly_to_send_ack);
cfg.vif.send_ack_by_device(cfg.tvd_ack);
endtask : process_address
task process_read_data();
`uvm_info(`gfn, $sformatf("i2c processes read data"), UVM_DEBUG)
// if mem_datas is empty then return a random read data to response host
data_byte = (req.mem_datas.size()>0) ? req.mem_datas.pop_back() : gen_rand_rd_data();
// bit serialization
for (int i = 0; i < data_width; i++) begin
cfg.vif.scl_o <= 1'b0;
// send data bit
cfg.vif.scl_o <= 1'b0;
cfg.vif.wait_for_dly(req.dly_to_send_data);
cfg.vif.scl_o <= 1'b1;
cfg.vif.sda_o <= data_byte[i];
cfg.vif.bus_status = HostReceiveData;
// device must ensure hold time thd_dat when sending read data
repeat(cfg.thd_dat) @(posedge cfg.vif.clk_i);
end
rsp.rd_data = data_byte;
// wait for nack from host
cfg.vif.wait_for_nack_from_host(cfg.tvd_ack);
// wait for stop/restart from host
cfg.vif.wait_for_stop_or_restart_from_host(cfg.tsu_sta, cfg.tsu_sto);
endtask : process_read_data
task process_write_data();
`uvm_info(`gfn, $sformatf("i2c processes write data"), UVM_DEBUG)
// bit concatenation
for (int i = 0; i < data_width; i++) begin
wait(cfg.vif.scl_i == 1'b1)
data_byte[i] = cfg.vif.sda_i;
cfg.vif.bus_status = HostWriteData;
end
rsp.wr_data = data_byte;
rsp.mem_datas.push_front(data_byte);
// device sends ack to host
cfg.vif.wait_for_dly(req.dly_to_send_ack);
cfg.vif.send_ack_by_device(cfg.tvd_ack);
// wait for stop/restart from host
cfg.vif.wait_for_dly(req.dly_to_send_stop);
cfg.vif.wait_for_stop_or_restart_from_host(cfg.tsu_sta, cfg.tsu_sto);
endtask : process_write_data
function gen_rand_rd_data();
return $urandom_range((1 << data_width) - 1, 0);
endfunction
endclass : i2c_device_driver