`timescale 1ns/1ps
`include "uvm_macros.svh"
import uvm_pkg::*;

class width_item extends uvm_sequence_item;
  bit write;
  bit [31:0] data;
  function new(string name="width_item"); super.new(name); endfunction
endclass

class width_adapter extends uvm_reg_adapter;
  function new(string name="width_adapter"); super.new(name); endfunction
  function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
    width_item item = new();
    uvm_reg_data_t data = rw.data;
`ifdef REVIEW_UVM20
    if ($test$plusargs("MIGRATED") && rw.kind == UVM_WRITE) data = {<< byte {data}};
`endif
    item.write = rw.kind == UVM_WRITE;
    item.data = data;
    return item;
  endfunction
  function void bus2reg(uvm_sequence_item bus, ref uvm_reg_bus_op rw);
    width_item item;
    if (!$cast(item, bus)) `uvm_fatal("WIDTH", "Unexpected bus item")
    rw.kind = item.write ? UVM_WRITE : UVM_READ;
    rw.data = item.data;
`ifdef REVIEW_UVM20
    if ($test$plusargs("MIGRATED") && !item.write) rw.data = {<< byte {rw.data}};
`endif
    rw.status = UVM_IS_OK;
  endfunction
endclass

class width_driver extends uvm_driver#(width_item);
  bit [31:0] hardware;
  int transfers;
  function new(string name, uvm_component parent); super.new(name, parent); endfunction
  task run_phase(uvm_phase phase);
    width_item item;
    forever begin
      seq_item_port.get_next_item(item);
      #1;
      if (item.write) hardware = item.data;
      else item.data = hardware;
      transfers++;
      seq_item_port.item_done();
    end
  endtask
endclass

class width_register extends uvm_reg;
  uvm_reg_field value;
  function new(string name="reg32"); super.new(name, 32, UVM_NO_COVERAGE); endfunction
  function void build();
    value = new("value");
    value.configure(this, 32, 0, "RW", 0, 0, 1, 0, 1);
  endfunction
endclass

class width_block extends uvm_reg_block;
  width_register reg32;
  function new(string name="model"); super.new(name, UVM_NO_COVERAGE); endfunction
  function void build();
    reg32 = new(); reg32.configure(this); reg32.build();
    default_map = create_map("map", 0, 4, UVM_BIG_ENDIAN, 1);
    default_map.add_reg(reg32, 0, "RW");
    lock_model();
  endfunction
endclass

class width_test extends uvm_test;
  `uvm_component_utils(width_test)
  uvm_sequencer#(width_item) sequencer;
  width_driver driver;
  width_adapter adapter;
  width_block model;
  function new(string name, uvm_component parent); super.new(name, parent); endfunction
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    sequencer = new("sequencer", this); driver = new("driver", this);
    adapter = new(); model = new(); model.build();
  endfunction
  function void connect_phase(uvm_phase phase);
    driver.seq_item_port.connect(sequencer.seq_item_export);
    model.default_map.set_sequencer(sequencer, adapter);
    model.default_map.set_auto_predict(1);
  endfunction
  task run_phase(uvm_phase phase);
    uvm_status_e status;
    uvm_reg_data_t value;
    phase.raise_objection(this);
    model.reg32.write(status, 'h12345678);
    $display("REVIEW|write_ok|%0d", status == UVM_IS_OK);
    $display("REVIEW|hardware|%08h", driver.hardware);
    driver.hardware = 'h12345678;
    model.reg32.read(status, value);
    $display("REVIEW|read_ok|%0d", status == UVM_IS_OK);
    $display("REVIEW|read_value|%08h", value[31:0]);
    $display("REVIEW|transfers|%0d", driver.transfers);
    $display("REVIEW|done|1");
    phase.drop_objection(this);
  endtask
endclass

module reg_width_probe;
  initial run_test("width_test");
endmodule
