RAL INTERGRATION OF I2C
REG BLOCK
STEP 1
NOTE: UVM provides default “uvm_reg_block”
Take instances for the Registers
Build:
Create map for the default maps
Create objects for all registers
Configure the registers
Call the build method
Map the register in the reg map
class i2c_reg_block extends uvm_reg_block;
`uvm_object_utils_begin(i2c_reg_block)
`uvm_object_utils_end
rand i2c_reg_0 h_i2c_reg_0;
rand i2c_reg_1 h_i2c_reg_1;
function new(string name = "i2c_reg_block");
[Link](name);
`uvm_info("I2C_REG_BLOCK","I2c_register_block build method",UVM_NONE);
endfunction
virtual function void build();
default_map =
create_map(.name("default_map"),.base_addr(8'h00),.n_bytes(1),.endian(UVM_BIG_ENDIAN),.by
te_addressing(1));
h_i2c_reg_0 = i2c_reg_0::type_id::create("h_i2c_reg_0");
h_i2c_reg_0.configure(this, null,"h_i2c_reg_0");
h_i2c_reg_0.build();
default_map.add_reg(h_i2c_reg_0, 'h3, "RW");
h_i2c_reg_1 = i2c_reg_1::type_id::create("h_i2c_reg_1");
h_i2c_reg_1.configure(this, null,"h_i2c_reg_1");
h_i2c_reg_1.build();
default_map.add_reg(h_i2c_reg_1, 'h4, "RW");
endfunction
STEP 2
Include Reg block in the Environment
Take handle for the Reg block
Build phase:
Create object for the Reg block
Call the build function in reg block
Call the lock_model in the reg block
class i2c_env extends uvm_env;
i2c_reg_block h_i2c_reg_block;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("TAG","INSIDE BUILD PHASE",UVM_NONE);
h_i2c_reg_block = i2c_reg_block :: type_id :: create("h_i2c_reg_block",this);
h_i2c_reg_block.build();
h_i2c_reg_block.lock_model();
endfunction : build_phase
STEP 3
Registers
Take instances for the reg fields in the corresponding register
Inside build function, create object for the reg fields
Configure the reg fields
class i2c_reg_0 extends uvm_reg;
`uvm_object_utils_begin(i2c_reg_0)
`uvm_object_utils_end
rand uvm_reg_field field_0;
virtual function void build();
field_0 = uvm_reg_field::type_id::create("field_0");
field_0.configure(.parent(this),
.size(8),
.lsb_pos(0),
.access("rw"),
.volatile(0),
.reset(0),
.has_reset(1),
.is_rand(1),
.individually_accessible(0)
);
endfunction
endclass : i2c_reg_0
STEP 4
Include the reg block in package
Compile
Run
ADAPTER
STEP 1
NOTE: UVM provides default class “uvm_reg_bus_op”
2 methods
Reg2bus
Create sequence item
Assign default values for the variables in sequence item
Update the item according to read and write
Bus2reg
Create sequence item
Update the register according to read and write
class i2c_reg_adaptor extends uvm_reg_adapter;
`uvm_object_utils(i2c_reg_adaptor)
function new(string name = "i2c_reg_adaptor");
[Link](name);
`uvm_info("I2C_REG_ADAPTOR","I2c_register_adaptor build method",UVM_NONE);
endfunction
virtual function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
i2c_sequence_item item = i2c_sequence_item :: type_id :: create("item");
[Link] = 1;
item.start_dr_en = 1;
item.slave_id_dr_en = 1;
item.opcode_dr_en = 1;
item.reg_addr_dr_en = 1;
item.reg_data_master_dr_en = 1;
item.stop_dr_en = 1;
item.rd_ack_dr_en = 1;
if([Link] == UVM_READ) begin
[Link] = READ;
item.slave_id = 'h10;
item.reg_addr_dr_en = 0;
item.reg_data_master_dr_en = 0;
end else if([Link] == UVM_WRITE) begin
[Link] = WRITE;
item.data_q.push_back([Link]);
[Link] = [Link];
item.slave_id = 'h10;
end
return item;
endfunction : reg2bus
virtual function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
i2c_sequence_item item;
if( ! $cast(item,bus_item)) begin
`uvm_fatal("NOT_WB_TYPE","Provided bus_item is not of the correct type");
end
if([Link] == READ) begin
[Link] = UVM_READ;
[Link] = [Link];
if(item.data_q.size !=0) [Link] = item.data_q.pop_front();
end else if([Link] == WRITE) begin
[Link] = UVM_WRITE;
[Link] = [Link];
[Link] = [Link];
end
endfunction : bus2reg
endclass: i2c_reg_adaptor
STEP 2
Environment:
Take handle for the adapter
Build phase:
Create object for the adapter
Connect phase:
Connect the adapter with the default map in the reg map
Set auto predict
STEP 3
Include the adapter in package
Compile
Run
class i2c_env extends uvm_env;
`uvm_component_utils(i2c_env)
i2c_reg_adaptor h_i2c_adaptor;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("TAG","INSIDE BUILD PHASE",UVM_NONE);
h_i2c_adaptor = i2c_reg_adaptor::type_id::create("h_i2c_adaptor",this);
endfunction : build_phase
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
`uvm_info("TAG","INSIDE Connect phase",UVM_NONE);
h_i2c_reg_block.default_map.set_sequencer(h_i2c_master_ag.h_master_sequencer,h_i2c_a
daptor);
h_i2c_reg_prd.adapter = h_i2c_adaptor;
h_i2c_master_ag.h_master_monitor.h_trans_item_collected_ap.connect(h_i2c_reg_prd.bus_
in);
endfunction
PREDICTOR
STEP 1
Environment:
Take handle for the Predictor, adapter and reg block
UVM provides uvm_reg_predictor. Using this as a type declare handle
for the predictor
Build phase:
Create object for the Predictor
Connect phase:
Connect the map in the Predictor with the default map in the reg block
Connect the adapter in the predictor with the adapter
Connect the port in the monitor with the default port(bus_in) predictor
class i2c_env extends uvm_env;
`uvm_component_utils(i2c_env)
typedef uvm_reg_predictor#(i2c_sequence_item) i2c_reg_predictor;
i2c_reg_predictor h_i2c_reg_prd;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("TAG","INSIDE BUILD PHASE",UVM_NONE);
h_i2c_reg_prd = i2c_reg_predictor::type_id::create("h_i2c_reg_prd",this);
endfunction : build_phase
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
`uvm_info("TAG","INSIDE Connect phase",UVM_NONE);
h_i2c_reg_prd.map = h_i2c_reg_block.default_map;
h_i2c_reg_prd.adapter = h_i2c_adaptor;
h_i2c_master_ag.h_master_monitor.h_trans_item_collected_ap.connect(h_i2c_reg_prd.bus_in);
endfunction
STEP 2
Compile
Run
VIRTUAL SEQUENCE LIB
STEP 1
Write individual virtual task for read and write in the base virtual
sequence
virtual task write(bit7_t slave_id, bit8_t address, bit8_t w_data_q[$]);
i2c_write_sequence h_seq;
int length = w_data_q.size();
`uvm_do_on_with(h_seq, p_sequencer.h_master_sqr,{
h_seq.l_slave_id == slave_id;
h_seq.l_address == address;
h_seq.l_length == length;
foreach(w_data_q[i]) h_seq.l_data_q[i] == w_data_q[i];
});
`uvm_info(TAG,$sformatf("PKT = %0s",h_seq.sprint),UVM_NONE);
endtask : write
virtual task read(bit7_t slave_id, bit8_t address,int length, ref bit8_t r_data_q[$]);
i2c_read_sequence h_seq;
r_data_q = {};
write(slave_id,address,r_data_q);
`uvm_do_on_with(h_seq, p_sequencer.h_master_sqr,{
h_seq.l_slave_id == slave_id;
h_seq.l_length == length;
});
`uvm_info(TAG,$sformatf("PKT = %0s",h_seq.sprint),UVM_NONE);
foreach(h_seq.l_data_q[i]) r_data_q.push_back(h_seq.l_data_q[i]);
endtask : read
STEP 2
Write multiple virtual sequences derived from the base virtual sequence
Overwrite the seq_specific_task and call the write or read method in
base virtual sequence
Write virtual sequence for each and every feature[single write/read ,
burst write/read, multiple write/read,etc..]
// I2C SINGLE WRITE READ VSEQ
class i2c_single_write_read_vseq extends i2c_base_vseq;
bit7_t slave_id = 'h10;
bit8_t address;
int length;
bit8_t w_data_q[$];
bit8_t r_data_q[$];
`uvm_object_utils(i2c_single_write_read_vseq)
virtual task seq_specific_task();
// slave_id = $urandom_range(16,20); // multiple slave
address = $urandom_range(1,10); // random address
w_data_q.push_back($urandom_range(1,10)); // random data
length = w_data_q.size();
write(slave_id,address,w_data_q);
read(slave_id,address,length,r_data_q);
endtask : seq_specific_task
endclass : i2c_single_write_read_vseq
STEP 3
Run the required virtual sequence on virtual sequencer in test