Hi,
I have 32-bit uvm_regs that contains uvm_reg_fields that are the only fields in their byte lane. I configure the uvm_reg_field instances with individually_accessible=1. In my uvm_reg_adapter I set supports_byte_enable=1. My APB bus has byte enable bits and as far as I can tell I have them connected and implemented correctly. I should be able to write these fields/bytes individually using uvm_reg_field::write(). This works fine for me with UVM 1.2, but not with UVM IEEE 1800.2-2017. I have tried the UVM implementations from my vendor and direct from Accellera (uvm-1.2 and 1800.2-2017-1.1).
With IEEE what appears to be happening is the uvm_reg_map::Xget_bus_infoX() and uvm_reg_map::do_bus_access() subroutines don’t calculate the bit and byte shifts and offsets correctly so when I attempt a uvm_reg_field::write(), the write data is shifted into the wrong byte lanes so the wrong data is written to the register.
I have a small test case based on the examples/simple/registers/primer/ code from UVM 1.2.
Here’s my register definition with four sub-byte fields of varying widths, configured with individually_accessible=1:
class reg_slave_USER1 extends uvm_reg;
uvm_reg_field ALPHA;
uvm_reg_field BRAVO;
uvm_reg_field CHARLIE;
uvm_reg_field DELTA;
function new(string name = "slave_USER1");
super.new(name,32,UVM_NO_COVERAGE);
endfunction
virtual function void build();
this.ALPHA = uvm_reg_field::type_id::create("ALPHA");
this.BRAVO = uvm_reg_field::type_id::create("BRAVO");
this.CHARLIE = uvm_reg_field::type_id::create("CHARLIE");
this.DELTA = uvm_reg_field::type_id::create("DELTA");
this.ALPHA.configure(this, 1, 0, "RW", 0, 1'h0, 1, 0, 1);
this.BRAVO.configure(this, 2, 8, "RW", 0, 2'h0, 1, 0, 1);
this.CHARLIE.configure(this, 4, 16,"RW", 0, 4'h0, 1, 0, 1);
this.DELTA.configure(this, 8, 24,"RW", 0, 8'h00, 1, 0, 1);
endfunction
`uvm_object_utils(reg_slave_USER1)
endclass
Here’s my uvm_reg_adapter with byte enables assigned and supports_byte_enable = 1’b1:
class reg2apb_adapter extends uvm_reg_adapter;
`uvm_object_utils(reg2apb_adapter)
function new(string name = "reg2apb_adapter");
super.new(name);
supports_byte_enable = 1'b1;
endfunction
virtual function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
apb_rw apb = apb_rw::type_id::create("apb_rw");
apb.kind = (rw.kind == UVM_READ) ? apb_rw::READ : apb_rw::WRITE;
apb.addr = rw.addr;
apb.data = rw.data;
apb.byte_en = rw.byte_en;
return apb;
endfunction
virtual function void bus2reg(uvm_sequence_item bus_item,
ref uvm_reg_bus_op rw);
apb_rw apb;
if (!$cast(apb,bus_item)) begin
`uvm_fatal("NOT_APB_TYPE","Provided bus_item is not of the correct type")
return;
end
rw.kind = apb.kind == apb_rw::READ ? UVM_READ : UVM_WRITE;
rw.addr = apb.addr;
rw.data = apb.data;
rw.status = UVM_IS_OK;
rw.byte_en = apb.byte_en;
endfunction
endclass
Here’s an excerpt from my DUT showing register byte writes and reads:
always @ (posedge apb.pclk)
begin
if (rst) begin
INDEX <= 'h00;
ALPHA <= 'h00;
BRAVO <= 'h00;
CHARLIE <= 'h00;
DELTA <= 'h00;
pr_data <= 32'h0;
end
else begin
// Wait for a SETUP+READ or ENABLE+WRITE cycle
if (apb.psel == 1'b1 && apb.penable == apb.pwrite) begin
pr_data <= 32'h0;
if (apb.pwrite) begin
casex (apb.paddr)
16'h0800: begin
if (apb.pstrb[0]) ALPHA <= apb.pwdata[0+:1];
if (apb.pstrb[1]) BRAVO <= apb.pwdata[8+:2];
if (apb.pstrb[2]) CHARLIE <= apb.pwdata[16+:4];
if (apb.pstrb[3]) DELTA <= apb.pwdata[24+:8];
end
endcase
end
else begin
casex (apb.paddr)
16'h0800: pr_data <= {DELTA, {4'h0, CHARLIE}, {6'h00, BRAVO}, {7'h00, ALPHA}};
endcase
end
end
end
end
My uvm_driver makes the connection between the transaction byte_en signals and the APB pstrb signals.
Has anyone else seen this problem in UVM IEEE?
Thanks very much.