Jump to content

Search the Community

Showing results for tags 'uvm_reg_map'.



More search options

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

  • Accellera Systems Initiative
    • Information
    • Announcements
    • In the News
  • SystemC
    • SystemC Language
    • SystemC AMS (Analog/Mixed-Signal)
    • SystemC TLM (Transaction-level Modeling)
    • SystemC Verification (UVM-SystemC, SCV)
    • SystemC CCI (Configuration, Control & Inspection)
    • SystemC Datatypes
  • UVM (Universal Verification Methodology)
    • UVM 2017 - Methodology and BCL Forum
    • UVM SystemVerilog Discussions
    • UVM Simulator Specific Issues
    • UVM Commercial Announcements
    • UVM (Pre-IEEE) Methodology and BCL Forum
    • UVM 1.2 Public Review
  • Portable Stimulus
    • Portable Stimulus Pre-Release Discussion
    • Portable Stimulus 1.0
  • IP-XACT
    • IP-XACT Discussion
  • IEEE 1735/IP Encryption
    • IEEE 1735/IP Encryption Discussion
  • OCP (Open Core Protocol)
  • UCIS (Unified Coverage Interoperability Standard)
  • Commercial Announcements
    • Announcements

Categories

  • SystemC
  • UVM
  • UCIS
  • IEEE 1735/IP Encryption

Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


AIM


MSN


Website URL


ICQ


Yahoo


Jabber


Skype


Location


Interests


Biography


Location


Interests


Occupation


Company

Found 6 results

  1. I recently came upon an issue where attempting to write a memory via a backdoor yields a warning similar to the following: "reporter [RegModel] Memory 'tst_reg_blk.test_mem' is not contained within map 'Backdoor' (called from get_access())" In addition to the warning, it appears that due rw.map being overwritten (as explained in the second link), Read-Only memories can be written to via the backdoor, since get_access end up returning the default value of "RW". This appears to be in conflict with the Spec: Researching this warning turned up a couple prior forum posts from 2014: http://forums.accellera.org/topic/2104-question-about-backdoor-map/ http://forums.accellera.org/topic/2107-problem-with-backdoor-access-to-uvm_mem/ Neither of which had an associated answer. I came up with a test case to reproduce the warning: //####################################################################### // Backdoor accesses to a UVM_MEM that is a member of more than 1 map // causes a UVM_WARNING that that the memory is not contained within // the 'Backdoor' register map. // // This appears to happen due to the uvm_mem::Xcheck_accessX call in // uvm_mem::do_write, which replaces rw.map with the pseudo-map 'Backdoor'. // This replacement precedes the get_access(rw.map) call later in do_write. // The warning is generated when map membership is checked in get_local_map // which is called from get_access in the case that there is > 1 maps to // which the memory belongs. // // Below is a test case that reproduces the warning, as well as a workaround // that replaces the overwritten map (if WORKAROUND is defined). //####################################################################### package test_pkg; import uvm_pkg::*; `include "uvm_macros.svh" typedef class basic_test; typedef class test_env; typedef class test_reg_block; typedef class test_mem_class; //######################################### // Test //######################################### class basic_test extends uvm_test; test_env env; `uvm_component_utils(basic_test) //---- New Function ---- function new(string name = "basic_test", uvm_component parent); super.new(name, parent); endfunction : new //---- Build Phase ---- function void build_phase(uvm_phase phase); super.build_phase(phase); env = test_env::type_id::create("env", this); endfunction //---- Run Phase ---- virtual task run_phase(uvm_phase phase); uvm_reg_data_t data = 'hA5; uvm_reg_addr_t offset = 'h0000; uvm_status_e status; phase.raise_objection(this, "Starting Test"); `uvm_info(get_type_name(), "Got into Test Run_Phase.", UVM_LOW) `uvm_info(get_type_name(), $sformatf("Writing 0x%h.", data), UVM_LOW) //This causes a warning if WORKAROUND is not defined env.tst_reg_blk.test_mem.write(.status(status), .offset(offset), .value(data), .path(UVM_BACKDOOR), .map(env.tst_reg_blk.test_reg_map), .parent(null), .prior(-1), .extension(null), .fname(""), .lineno(0)); data = 'h0; //Warning does not occur on read env.tst_reg_blk.test_mem.read( .status(status), .offset(offset), .value(data), .path(UVM_BACKDOOR), .map(env.tst_reg_blk.test_reg_map), .parent(null), .prior(-1), .extension(null), .fname(""), .lineno(0)); `uvm_info(get_type_name(), $sformatf("Read 0x%h.", data), UVM_LOW) phase.drop_objection(this, "Test Is Done"); endtask : run_phase endclass //######################################### // Basic Environment //######################################### class test_env extends uvm_env; test_reg_block tst_reg_blk; `uvm_component_utils(test_env) //---- New Function ---- function new(input string name, input uvm_component parent=null); super.new(name,parent); endfunction //---- Build Phase ---- function void build_phase(uvm_phase phase); super.build_phase(phase); tst_reg_blk = test_reg_block::type_id::create("tst_reg_blk"); tst_reg_blk.configure(); tst_reg_blk.setup(); tst_reg_blk.set_hdl_path_root("test_tb"); endfunction : build_phase endclass //######################################### // Register Block Class //######################################### class test_reg_block extends uvm_reg_block; uvm_reg_map test_reg_map; uvm_reg_map test_reg_map_2; test_mem_class test_mem; `uvm_object_utils(test_reg_block) //---- New Function ---- function new(input string name="test_reg_block"); super.new(name); endfunction // new //Create Map, Add/Configure Memory, Lock Model function void setup(); test_reg_map = create_map("test_reg_map", 'h00000000, 4, UVM_LITTLE_ENDIAN, .byte_addressing(0)); test_reg_map_2 = create_map("test_reg_map", 'h00000000, 4, UVM_LITTLE_ENDIAN, .byte_addressing(0)); test_mem = test_mem_class::type_id::create("test_mem", , get_full_name()); test_mem.configure(this, "rtl_mem"); test_reg_map.add_mem( .mem(test_mem), .offset (32'h00000000), .rights ("RO"), .unmapped (0)); test_reg_map_2.add_mem( .mem(test_mem), .offset (32'h00000000), .rights ("RO"), .unmapped (0)); this.lock_model(); endfunction // setup endclass //######################################### // Register Block Class //######################################### class test_mem_class extends uvm_mem; `uvm_object_utils(test_mem_class) //---- New Function ---- function new(input string name="test_mem_class"); super.new(.name(name), .size(16), .n_bits(8), .access("RW"), .has_coverage(UVM_NO_COVERAGE)); endfunction // new //---- Bug Workaround ---- // Override the do_write task grab a local copy of the map. // Then override pre_write (which occurs after Xcheck_accessX) // to reassign the map with the local copy in the case of a // backdoor access. // // An actual fix would involve changing the behaviour of // Xcheck_accessX, which is non-virtual. `ifdef WORKAROUND protected uvm_reg_map backup_map; virtual task do_write (uvm_reg_item rw); backup_map = rw.map; super.do_write(rw); endtask virtual task pre_write (uvm_reg_item rw); super.pre_write(rw); if(rw.path == UVM_BACKDOOR) begin if(backup_map == null) rw.map = rw.local_map; else rw.map = backup_map; end endtask `endif endclass endpackage : test_pkg //######################################### // Basic TB //######################################### module test_tb(); import uvm_pkg::*; import test_pkg::*; `include "uvm_macros.svh" reg [7:0] rtl_mem [16]; initial run_test("basic_test"); endmodule I was able to come up with a work-around, but I feel that I am missing something. What is the actual purpose of uvm_reg_map::backdoor()? What is it's intended use case? Given that it has no parent block (thus not allowing memories or registers to be added), I do not know how it could be used. Thank you in advance for considering my question. //----------------- Edit ------------------------ I realized after the fact that this is a pre-IEEE issue. I Don't know if the topic can be moved to the correct forum. However, the question still stands; what is the intended purpose of the Backdoor pseudo-map?
  2. Hi , To whom may correspond I think there is some kind of error in the UVM 1.1d register model. I have been experimenting with the UVM register model and i have seen the following code in uvm_reg_map.svh task uvm_reg_map::do_bus_write (uvm_reg_item rw, uvm_sequencer_base sequencer, uvm_reg_adapter adapter); uvm_reg_addr_t addrs[$]; uvm_reg_map system_map = get_root_map(); int unsigned bus_width = get_n_bytes(); uvm_reg_byte_en_t byte_en = -1; uvm_reg_map_info map_info; int n_bits; int lsb; int skip; int unsigned curr_byte; int n_access_extra, n_access; int n_bits_init; Xget_bus_infoX(rw, map_info, n_bits_init, lsb, skip); addrs=map_info.addr; // if a memory, adjust addresses based on offset if (rw.element_kind == UVM_MEM) foreach (addrs[i]) addrs[i] = addrs[i] + map_info.mem_range.stride * rw.offset; foreach (rw.value[val_idx]) begin: foreach_value uvm_reg_data_t value = rw.value[val_idx]; /* calculate byte_enables */ if (rw.element_kind == UVM_FIELD) begin int temp_be; int idx; n_access_extra = lsb%(bus_width*8); n_access = n_access_extra + n_bits_init; temp_be = n_access_extra; value = value << n_access_extra; while(temp_be >= 8) begin byte_en[idx++] = 0; temp_be -= 8; end temp_be += n_bits_init; while(temp_be > 0) begin byte_en[idx++] = 1; temp_be -= 8; end byte_en &= (1<<idx)-1; for (int i=0; i<skip; i++) void'(addrs.pop_front()); while (addrs.size() > (n_bits_init/(bus_width*8) + 1)) void'(addrs.pop_back()); end curr_byte=0; n_bits= n_bits_init; The code continues but the interesting part is already there. Lets assume we have a register with 4 bytes and 1byte per address granularity (byte_addressing). Now, we do a FIELD access of 8bits length (the first byte of a register). The field is configured "individual_accessible, so UVM should only access that FIELD. The reg2bus should generate that byte request to be written. In other words, the vector "addrs" should have only one byte address. Going to the code, i see that initially the addrs has the 4 address ( the whole register) and when it comes to the "if (rw.element_kind == UVM_FIELD) begin" and it will pop_back()/remove all the exceeding bytes that doesn't need to complete "n_bits_init" of the field access. The problem is here: UVM has //while (addrs.size() > (n_bits_init/(bus_width*8) + 1)) and i think it should be while (addrs.size() > ((n_bits_init-1)/(bus_width*8) + 1)) //ejonalv possible error in UVM? check That is because in case we want to write 8 bits, it will calculate 8/8+1=2 address in the UVM version, but in fact it should require only 1 address. This is of course applicable for the READ variation. Did i misunderstand something? It is very hard to go through the register model without proper documentation in the code. I am looking forward your answer Best Regards Jonathan
  3. Hi, While debugging a prediction error returned by "Bit Bashing Test Sequence", I came to read the uvm_reg_map::do_bus_read/do_bus_write methods which seems incorrect when it comes to computing the "byte enable" for the bus accesses (ie data member "byte_en" of the object of type uvm_bus_reg_op created by those functions) I'm explain the issue that I think I found here-below, can you please give me your feedback (Am i wrong or not?) My understanding about "uvm_reg_map::do_bus_read/do_bus_write" methods about is that these methods are called when I request a frontdoor read/write access and I don't supply a user frontdoor sequence. these methods aimed at splitting the high level object uvm_reg_item into as many uvm_bus_reg_op as need on the real bus. part of their role is to compute bus lane "byte enable" based on length and base address for a UVM_REG and length, base address and field offset in the reg for a UVM_FIELD The "uvm_reg_map::do_bus_read/do_bus_write" code extract I'm interested in here given here below with focus on bus byte enable (rw_access.byte_en) computation: task uvm_reg_map::do_bus_write (uvm_reg_item rw, uvm_sequencer_base sequencer, uvm_reg_adapter adapter); [...] uvm_reg_byte_en_t byte_en = -1; // <= here the default value of byte_en is set to 'all ones' [...] foreach (rw.value[val_idx]) begin: foreach_value [...] /* calculate byte_enables */ if (rw.element_kind == UVM_FIELD) begin // <= here byte_en is re-computed for UVM_FIELD only, not for UVM_REG [...] end [...] foreach(addrs[i]) begin: foreach_addr [...] uvm_reg_bus_op rw_access; [...] if (rw.element_kind == UVM_FIELD) begin // <= here the slice of the computed byte_en corresponding to // the current bus address is applied to the bus (rw_access.byte_en) // for UVM_FIELD only, not for UVM_REG for (int z=0;z<bus_width;z++) rw_access.byte_en[z] = byte_en[curr_byte+z]; end [...] rw_access.byte_en = byte_en; // <= here for UVM_REG, the default 'all ones' byte_en value is applied // and for UVM_REG, the correct value of rw_access.byte_en assigned above is // erroneously overwritten [...] end: foreach_addr [...] end: foreach_value endtask: do_bus_write But the extract of code here-below shows two issues: for a read/write to a UVM_REG, the byte_en is hard coded to "all ones" which seems incorrect to me in at least 2 cases: if the bus is 4-Byte wide, a register R1 to write is 1-Byte wide and at address 1 and another register R0 is also 1-Byte wide and located at address 0, then trying to write R1, will also write R0. if the bus is 4-Byte wide, a register R0 to write is 6-Byte wide and at address 0 and another register R1 is 1-Byte wide and located at address 6, then trying to write R0, will also write R1. for a read/write to a UVM_FIELD, there's a complex computation of byte_en for the whole field , then another computation to extract the correct bus byte enable from the whole field byte_enable. Those 2 computations seems correct to me but the last assignemnt "rw_access.byte_en = byte_en;" breaks everything by systematically setting the same bus byte enables value. For a field spanning several bus addresses this is correct/acceptable for the first bus access but this incorrect for the other bus accesses. Any feedback welcome, thank you Jordan
  4. In our DUT, we have two separate independent physical interfaces (APB & I2C) (active 1 at a time) through which all registers can can be accessed, Also in our register model, we created two reg_maps, one for each APB & I2C. Now through testcase, we want only one physical interface at a time, to be subjected to default uvm sequences (i.e. uvm_reg_access_seq, uvm_reg_bit_bash_seq,etc) but it is not possible as uvm_sequence will get all the maps using get_maps(); So without over-riding the default uvm_reg_access_seq, is it possible to achieve such type of configuration in testcase itself via using uvm_reg_map or some other methods. Kindly refer following pseudo code //Pseudo Code for Scenario class dut_reg_test extends base_test; `uvm_component_uti ls(dut_reg_test) //Handle of default uvm register access sequence uvm_reg_access_seq my_reg_seq; // Select Physical Interface rand bit APB_I2C; uvm_reg_map test_map; dut_reg_model regmodel; task buid_phase (uvm_phase phase); super.build_phase(phase); // Select PHY Interface via commandline if ($value$plusargs("APB_I2C=%b", APB_I2C)) else APB_I2C = $random; if (APB_I2C) test_map = regmodel.apb else test_map = regmodel.i2c endtask : build_phase task main_phase (uvm_phase phase); //Create method for sequence my_reg_seq=uvm_single_access_seq::type_id::create("my_reg_seq"); //Randomize with selected map my_reg_seq.randomize with { maps == test_map;}); // Start default sequence my_reg_seq.start(NULL); endtask : main_phase endclass : dut_reg_test The above strategy cannot be implemented because uvm_reg_access sequence doesn't contain uvm_reg_map it's only present in uvm_reg_single_access_seq, Similar kind of limitations persists will all uvm_reg_bit_bash_seq & reset sequences. Can we have some strategy to resolve this issue ? Thanks Nikunj Hinsu
  5. In the UVM register model, if you create a register or a field which is wider than the bus width there is no way to control what order the multiple bus operations occur to update the register/field value. In my case I was using BIG_ENDIAN addressing and the register model would always write the largest address first. My design spec required that the largest address be written last. I searched in vain for a way to change this but ended up overriding the 'do_bus_write' function in 'uvm_reg_map'. If you've run into a similar issue, here's my workaround. reversed_reg_order_map.sv `ifndef __REVERSED_REG_ORDER_MAP_SV__ `define __REVERSED_REG_ORDER_MAP_SV__ class reversed_reg_order_map extends uvm_reg_map; `uvm_object_utils(reversed_reg_order_map) function new(string name="reversed_reg_order_map"); super.new(name); endfunction extern virtual task do_write_bus_ops(uvm_reg_bus_op bus_ops[$], uvm_reg_item rw, uvm_sequencer_base sequencer, uvm_reg_adapter adapter); extern virtual task do_bus_write(uvm_reg_item rw, uvm_sequencer_base sequencer, uvm_reg_adapter adapter); endclass task reversed_reg_order_map::do_write_bus_ops(uvm_reg_bus_op bus_ops[$], uvm_reg_item rw, uvm_sequencer_base sequencer, uvm_reg_adapter adapter); foreach (bus_ops[i]) begin uvm_sequence_item bus_req; uvm_reg_bus_op rw_access; adapter.m_set_item(rw); bus_req = adapter.reg2bus(bus_ops[i]); adapter.m_set_item(null); if (bus_req == null) `uvm_fatal("RegMem",{"adapter [",adapter.get_name(),"] didnt return a bus transaction"}); bus_req.set_sequencer(sequencer); rw.parent.start_item(bus_req,rw.prior); if (rw.parent != null && i == 0) rw.parent.mid_do(rw); rw.parent.finish_item(bus_req); bus_req.end_event.wait_on(); if (adapter.provides_responses) begin uvm_sequence_item bus_rsp; uvm_access_e op; // TODO: need to test for right trans type, if not put back in q rw.parent.get_base_response(bus_rsp); adapter.bus2reg(bus_rsp,rw_access); end else begin adapter.bus2reg(bus_req,rw_access); end if (rw.parent != null && i == bus_ops.size()-1) rw.parent.post_do(rw); rw.status = rw_access.status; `uvm_info(get_type_name(), $sformatf("Wrote 'h%0h at 'h%0h via map \"%s\": %s...", bus_ops[i].data, bus_ops[i].addr, rw.map.get_full_name(), rw.status.name()), UVM_FULL) if (rw.status == UVM_NOT_OK) break; end endtask task reversed_reg_order_map::do_bus_write (uvm_reg_item rw, uvm_sequencer_base sequencer, uvm_reg_adapter adapter); uvm_reg_addr_t addrs[$]; uvm_reg_map system_map = get_root_map(); int unsigned bus_width = get_n_bytes(); uvm_reg_byte_en_t byte_en = -1; uvm_reg_map_info map_info; int n_bits; int lsb; int skip; int unsigned curr_byte; int n_access_extra, n_access; int n_bits_init; uvm_reg_bus_op bus_ops[$]; Xget_bus_infoX(rw, map_info, n_bits_init, lsb, skip); addrs = map_info.addr; // `UVM_DA_TO_QUEUE(addrs,map_info.addr) // if a memory, adjust addresses based on offset if (rw.element_kind == UVM_MEM) foreach (addrs[i]) addrs[i] = addrs[i] + map_info.mem_range.stride * rw.offset; foreach (rw.value[val_idx]) begin: foreach_value uvm_reg_data_t value = rw.value[val_idx]; curr_byte = 0; /* calculate byte_enables */ if (rw.element_kind == UVM_FIELD) begin int temp_be; int idx; n_access_extra = lsb%(bus_width*8); n_access = n_access_extra + n_bits_init; temp_be = n_access_extra; value = value << n_access_extra; while(temp_be >= 8) begin byte_en[idx++] = 0; temp_be -= 8; end temp_be += n_bits_init; while(temp_be > 0) begin byte_en[idx++] = 1; temp_be -= 8; end byte_en &= (1<<idx)-1; for (int i=0; i<skip; i++) void'(addrs.pop_front()); while (addrs.size() > (n_bits_init/(bus_width*8) + 1)) void'(addrs.pop_back()); end curr_byte=0; n_bits= n_bits_init; foreach(addrs[i]) begin: foreach_addr uvm_reg_bus_op rw_access; uvm_reg_data_t data; data = (value >> (curr_byte*8)) & ((1'b1 << (bus_width * 8))-1); curr_byte += bus_width; `uvm_info(get_type_name(), $sformatf("Writing 'h%0h at 'h%0h via map \"%s\"...", data, addrs[i], rw.map.get_full_name()), UVM_FULL); if (rw.element_kind == UVM_FIELD) begin for (int z=0;z<bus_width;z++) rw_access.byte_en[z] = byte_en[curr_byte+z]; end rw_access.kind = rw.kind; rw_access.addr = addrs[i]; rw_access.data = data; rw_access.n_bits = (n_bits > bus_width*8) ? bus_width*8 : n_bits; rw_access.byte_en = byte_en; n_bits -= bus_width * 8; bus_ops.push_back(rw_access); end: foreach_addr foreach (addrs[i]) addrs[i] = addrs[i] + map_info.mem_range.stride; end: foreach_value // reverse the write order begin uvm_reg_bus_op reversed_bus_op_list[$]; foreach (bus_ops[i]) reversed_bus_op_list.push_front(bus_ops[i]); do_write_bus_ops(reversed_bus_op_list, rw, sequencer, adapter); end endtask `endif // `ifndef __REVERSED_REG_ORDER_MAP_SV__ Then I used a factory type override: set_type_override_by_type(uvm_reg_map::get_type(), reversed_reg_order_map::get_type());
  6. In uvm_reg_map::do_bus_read() task, after the call to adapter.bus2reg() function, do_bus_read() function checks for any Xs in data field. Code from uvm_reg_map: uvm_reg_bus_op rw_access; uvm_reg_data_logic_t data; data = rw_access.data & ((1<<bus_width*8)-1); rw.status = rw_access.status; if (rw.status == UVM_IS_OK && (^data) === 1'bx) rw.status = UVM_HAS_X; Here, rw_access.data is of type "bit" and is assigned to data which is of type "logic". and then data is checked for Xs. But, as rw_access.data is "bit" type, it will never contain Xs or Zs. As a result, data will never have Xs, so rw.status will not set to UVM_HAS_X. So even if bus2reg function samples Xs from the bus, those Xs never make upto tasks in uvm_reg_map class. Some supporting code:: uvm_reg_defines.svh: `define UVM_REG_DATA_TYPE bit uvm_reg_model.svh : typedef `UVM_REG_DATA_TYPE unsigned [`UVM_REG_DATA_WIDTH-1:0] uvm_reg_data_t ; uvm_reg_item.svh: typedef struct { uvm_access_e kind; uvm_reg_addr_t addr; uvm_reg_data_t data; int n_bits; uvm_reg_byte_en_t byte_en; uvm_status_e status; } uvm_reg_bus_op; In my testbench, I want uvm_reg_map to set the status to UVM_HAS_X whenever data received for a register read transaction from DUT is X. How do I achieve that?
×