在使用寄存器模型的adapter进行数据转换时,会直接对uvm_reg_bus_op进行操作,其包含变量有操作类型、地址、数据等,除byte_en的描述不太清晰外,其他几个都比较好懂。从字面意思来看,该变量似乎是对数据按字节操作进行使能,但实际并不一定,本文将从源码的角度来解读byte_en。
//
//定义在uvm_reg_item.svh//
/
typedef struct {
// Variable: kind
//
// Kind of access: READ or WRITE.
//
uvm_access_e kind;
// Variable: addr
//
// The bus address.
//
uvm_reg_addr_t addr;
// Variable: data
//
// The data to write. If the bus width is smaller than the register or
// memory width, ~data~ represents only the portion of ~value~ that is
// being transferred this bus cycle.
//
uvm_reg_data_t data;
// Variable: n_bits
//
// The number of bits of <uvm_reg_item::value> being transferred by
// this transaction.
int n_bits;
/*
constraint valid_n_bits {
n_bits > 0;
n_bits <= `UVM_REG_DATA_WIDTH;
}
*/
// Variable: byte_en
//
// Enables for the byte lanes on the bus. Meaningful only when the
// bus supports byte enables and the operation originates from a field
// write/read.
//
uvm_reg_byte_en_t byte_en;
// Variable: status
//
// The result of the transaction: UVM_IS_OK, UVM_HAS_X, UVM_NOT_OK.
// See <uvm_status_e>.
//
uvm_status_e status;
} uvm_reg_bus_op;
先看定义,可见该变量的位宽为总线位宽/8,每一位代表对应字节的使能。
///
//uvm_reg_defines.svh//
///
// Macro: `UVM_REG_DATA_WIDTH
//
// Maximum data width in bits
//
// Default value is 64. Used to define the <uvm_reg_data_t> type.
//
`ifndef UVM_REG_DATA_WIDTH
`define UVM_REG_DATA_WIDTH 64
`endif
// Macro: `UVM_REG_BYTENABLE_WIDTH
//
// Maximum number of byte enable bits
//
// Default value is one per byte in <`UVM_REG_DATA_WIDTH>.
// Used to define the <uvm_reg_byte_en_t> type.
//
`ifndef UVM_REG_BYTENABLE_WIDTH
`define UVM_REG_BYTENABLE_WIDTH ((`UVM_REG_DATA_WIDTH-1)/8+1)
`endif
/
//uvm_reg_model.svh//
/
// Type: uvm_reg_byte_en_t
//
// 2-state byte_enable value with <`UVM_REG_BYTENABLE_WIDTH> bits
//
typedef bit unsigned [`UVM_REG_BYTENABLE_WIDTH-1:0] uvm_reg_byte_en_t ;
该变量看起来似乎是在对寄存器读写的时候可以对寄存器的某些byte进行单独的读写,但我在源码中只发现了do_predict()函数对byte_en有直接使用,只对寄存器模型进行了预测,而不是对总线前门访问数据的byte进行使能。现对uvm_reg.svh和uvm_reg_field.svh中的使用进行分析。
///
//uvm_reg.svh//
///
function void uvm_reg::do_predict(uvm_reg_item rw,
uvm_predict_e kind = UVM_PREDICT_DIRECT,
uvm_reg_byte_en_t be = -1);
uvm_reg_data_t reg_value = rw.value[0];
m_fname = rw.fname;
m_lineno = rw.lineno;
rw.status = UVM_IS_OK;
if (m_is_busy && kind == UVM_PREDICT_DIRECT) begin
`uvm_warning("RegModel", {"Trying to predict value of register '",
get_full_name(),"' while it is being accessed"})
rw.status = UVM_NOT_OK;
return;
end
foreach (m_fields[i]) begin
rw.value[0] = (reg_value >> m_fields[i].get_lsb_pos()) &
((1 << m_fields[i].get_n_bits())-1);
m_fields[i].do_predict(rw, kind, be>>(m_fields[i].get_lsb_pos()/8));
end
rw.value[0] = reg_value;
endfunction: do_predict
/
//uvm_reg_field.svh//
/
function void uvm_reg_field::do_predict(uvm_reg_item rw,
uvm_predict_e kind = UVM_PREDICT_DIRECT,
uvm_reg_byte_en_t be = -1);
uvm_reg_data_t field_val = rw.value[0] & ((1 << m_size)-1);
if (rw.status != UVM_NOT_OK)
rw.status = UVM_IS_OK;
// Assume that the entire field is enabled
if (!be[0])
return;
...
...
endfunction: do_predict
在uvm_reg.svh的do_predict可以看到,有一个对be(byte_en)的移位操作,这里get_lsb_pos获取的是field在reg中的首位置,整个移位操作就是看field在哪个byte。比如field是[3:1],那么就相当于是be>>(1/8)=be;如果是[9:8]那么就相当于be>>(8/8)=be>>1,右移一位,移动后的be被传到了uvm_reg_field.svh的do_predict里面,该do_predict被field调用。
在uvm_reg_field.svh中,前几行有对be的判断,看be[0]是否为0,如果是0就return,不进行操作,就相当于不使能这个field。be的默认值是-1,因为是unsigned类型,所以是全1,默认不屏蔽。
单这么说有点抽象,举个例子,假如有一个寄存器位宽32,里面4个field,每个field占1个byte,be的值是4'b1101,对于第一个field,它收到的be是be>>(0/8)=be>>0=4'b1101,它的be[0]是1,那么就代表它不会被屏蔽,能够进行后续操作;而对于第二个field,他收到的be是be>>(8/8)=be>>1=4'b110,它的be[0]就是0,被屏蔽,没有操作;第三个第四个be[0]都是1,能够被操作。所以在这种情况下,就相当于对第二个byte没使能。
但对于一个reg的field,很多情况下不一定是一个完整的byte,假如field1是[1:0],field2是[5:2],field3是[9:6],be依然是4'b1101的情况下,field123的be都是4'b1101,因为field3的be为be>>(6/8)=be>>0=4'b1101,此时field3就不会被屏蔽,即第二个byte的前两位(field3[9:8])没有屏蔽,此时按byte使能实际就不是byte了,这里也是命名的歧义所在,也是使用限制。
其他地方暂时还没有找到对byte_en的使用,如果解析有错误欢迎打脸讨论。