在使用寄存器模型的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;endforeach (m_fields[i]) beginrw.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));endrw.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 enabledif (!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的使用,如果解析有錯誤歡迎打臉討論。