Verification Martial Arts: A Verification Methodology Blog

Archive for the 'Memory Management' Category

Accessing Virtual Registers in RAL

Posted by Amit Sharma on 5th August 2010

Amit Sharma, Synopsys

In one of my previous posts on  Virtual Registers I talked about how you use RAL to model Virtual Registers or fields which are an efficient means of implementing large number of registers in  memory or RAM instead of individual flip-flops.  I also mentioned that they are  implemented as arrays associated with a memory.

In this post, I will talk about how you access these registers through RAL. Normal registers can be accessed using  the hierarchical name in RAL. For these, RAL  would generate the offsets and addresses required.  However, for virtual registers, along with  the virtual register name you need to provide the index ID to refer to them for read and write operations. Accordingly, RAL  generates the offset address based on the index ID of the virtual register.

Consider the following example:

image

The above RALF specification would translate into the following SV classes in the RAL model

 

image

‘DMA_BFRS’  which is the instance of the Virtual Registers class ‘ral_vreg_dut_DMA_BFRS’ is not an array in the RAL Model and is thus different from a typical register array modeled in RAL.

Now, how do you access the individual registers mapped to the memory?  You have to specify the index as the argument to the read/write methods. For example, To access the 4th index of this array, the access would like:

blk.DMA_BFRS.write(4, status, ‘hFF);

RAL would ultimately access the RAM to enable this access. Hence, the following are functionally equivalent:

blk.DMA_BFRS.write(4, status, ‘hFF);
and
blk.ram.write(4 * 0×0004 + 0×1000, status, ‘hFF);

The following illustration explains this point:

image

You can see that you now have an option to access these registers in different modes, but both will eventually go through the RAM. You can leverage the Virtual register Callbacks or the RAL memory callbacks for additional customizations and extensibility, in addition to other capabilities that you get when you are using VMM RAL.

Hope, this was useful.. do comment on any other specific functionality that you might look at when modeling these kind of registers.

Posted in Memory Management, Register Abstraction Model with RAL | No Comments »

Customizing VMM Memory Allocation Manager to cater to your requirements

Posted by Amit Sharma on 11th February 2010

Rakshit Singhal, Nvidia, Amit Sharma, Synopsys

The VMM MAM (Memory Allocation Manager) package offers the capability to dynamically manage memory shared across multiple clients. It helps to simulate HW memory usage patterns and guarantees memory block allocation based on constraints.

This utility is centered on a set of four base classes with configurable memory address ranges and allocation schemes.

  • vmm_cfg: This class is used to specify the memory managed by an instance of a “vmm_mam” memory allocation manager class.
  • vmm_mam: This class is a memory allocation management utility similar to C’s malloc() and free(). A single instance of this class is used to manage a single, contiguous address space.
  • vmm_mam_allocator: An instance of this class is randomized to determine the starting offset of a randomly allocated memory region. This class can be extended to provide additional constraints on the starting offset.
  • vmm_mam_region: This class is used by the memory allocation manager to describe allocated memory regions. Instances of this class should not be created directly.

The mode of operation is as follows. An instance of “vmm_mam” class would use the “vmm_cfg” class handle to determine the minimum, maximum addresses and difference allocation schemes (mode and locality) of a memory space. A memory space can be reconfigured with new min, max and allocation scheme at run-time through the “vmm_cfg” class with the exception that number of bytes per memory location cannot be modified once a “vmm_mam” instance has been constructed. Additionally the currently allocated regions must fall within the new address space. On every call of request_region() function “vmm_mam” randomizes “vmm_mam_allocator” instance to determine the ‘start_offset’ of the randomly allocated memory region. This region is represented by an instance of “vmm_mam_region” class. Now if this “vmm_mam” is associated with a “vmm_ral_mem” instance, this “vmm_mam_region” can be used to perform read/write operation on the actual memory block through RAL. Thus we have a simple and easy-to-use framework to constrain different kinds of allocation requests based on specific requirements.

However, there are generally additional requirements in different verification environments. For example, a memory map can have a non-contiguous space allocated as device memory and various devices can simply request a portion of this region. Also for a PC memory model, it is required that each memory region has a clear ownership so that we can verify the memory accesses from various clients in system follow the design rules. Moreover some system would work in multiple address modes such as 32-bit or 64-bit mode and depending on which addressing mode is selected the complete memory management and allocation changes. The other requirements were to provide memory owners some control over the alignment of the base addresses of the requested regions. For example a USB driver in a system may ask for a memory region with its base address aligned to cacheline size or page size. It may also be required to make memory allocation and access management to be self checking for ease of verification. For example a cacheable region is accessed only by the designated owners or to build some backdoor polling on specific memory locations to trap memory accesses etc. Portability and extendibility of such environment was also a big concern.

To sum it up all we needed three primary extensions to VMM MAM; one, the ability to independently constrain each allocation/de-allocation request such that memory attributes could be set to the memory map; two, the flexibility to hook up any memory model with VMM MAM to achieve a centralized memory allocation management which could handle multiple memory implementations; three, a place holder to built self checking routines, polling routines and front-door/back-door memory accesses such that we could automate the whole memory verification.

To allow individual clients a control over the attributes set to the memory regions and to choose a specific type of memory we extended the following VMM MAM classes.

1. vmm_mam_allocator: – This has been extended to include features like addressing mode, memory type and address alignment by adding in new variables. Addressing mode specifies the address size i.e. if the unit/system requires a 32bit, 64bit etc addressing. Memory type attribute specifies the allocation of memory of the similar type within a specified range. Example: All the MMIO, write-cache, write-back etc addresses can be restricted to a specific region in memory and allocation can be done for each type from within their sub-regions. Address alignment specifies if the required allocation has to be a cache-aligned, word-aligned etc

  1. typedef enum {R, RW, RSVD} mem_acc_t;
  2. typedef enum {NONE, Above4GB, Below4GB, PreFetch, Cacheable} mem_attr_t;
  3. typedef enum {PCIE, SATA, MAC, USB, USB3} mem_owner_t;
  4. typedef enum {BYTE_ALIGN, WORD_ALIGN, CACHE_ALIGN, PAGE_ALIGN}address_align_t;
  5. typedef enum {BIT32, BIT40, BIT64} address_mode_t;
  6. class nv_mam_allocator extends vmm_mam_allocator;
  7. address_align_t addr_align;
  8. .
  9. .
  10. .
  11. // system memory allocation
  12. constraint sys_mem_alloc_cons {
  13. this.start_offset > 64′hFFFF; // reserved memory area below FFFF
  14. (addr_align == WORD_ALIGN) -> {this.start_offset[1:0] == 2′b0}; // Allocate only word (4B) aligned addresses
  15. (addr_align == CACHE_ALIGN) -> {this.start_offset[7:0] == 8′b0}; // Allocate only cacheline (256B) aligned addresses
  16. }
  17. endclass : nv_mam_allocator

2. vmm_mam_region – this class has been extended to apply various user-defined attributes to memory regions and sub-regions. This has a customized vmm_mam::request_region() function which can take user variables as input arguments to constraint “vmm_mam_region” allocation. Addtionally, the psdisplay() of  “vmm_mam” and “vmm_mam_region” is overridden to print out values of these variable for debugging purpose. Ownership of a region could also be queried from testbench using an inbuilt function get_owner.

  1. class nv_mam_region extends vmm_mam_region;
  2. mem_owner_t mem_owner;
  3. function new(mem_addr_t start_offset, end_offset, offset_range, int unsigned n_bytes, nv_mam parent);
  4. super.new(start_offset, end_offset, offset_range, n_bytes, parent);
  5. this.n_bytes = n_bytes;
  6. endfunction
  7. function mem_owner_t get_owner();
  8. return this.mem_owner;
  9. endfunction
  10. endclass : nv_mam_region

3. vmm_mam – This class has been extended such that the features added to the above 2 classes are made use of in the allocate/de-allocate methods. The handles of the allocated regions are stored and used in the release regions method. There is also a provision to release the regions particular to an owner or a group of owners.

  1. class nv_mam extends vmm_mam;
  2. nv_mam_allocator default_nv_alloc;
  3. vmm_mam_region in_use[$];
  4. nv_mam_region nv_in_use[$];
  5. address_mode_t amode;
  6. // request region
  7. function nv_mam_region nv_request_region(int unsigned n_bytes, mem_owner_t mem_owner=UNKNOWN, address_align_t addr_align=WORD_ALIGN,mem_attr_t mem_type=NONE);
  8. vmm_mam_region region;
  9. nv_mam_allocator alloc;
  10. alloc=new(addr_align, this.amode, mem_type);
  11. region=super.request_region(n_bytes, alloc);
  12. nv_request_region = new(region.get_start_offset(),region.get_end_offset(), region.get_len(),region.get_n_bytes(),this);
  13. nv_request_region.mem_owner = mem_owner;
  14. this.in_use.push_back(region);
  15. this.nv_in_use.push_back(nv_request_region);
  16. endfunction
  17. // release OWNER specific regions
  18. function void release_mem_region(mem_owner_t mem_owner=UNKNOWN);
  19. vmm_mam_region region;
  20. `vmm_note(this.log, $psprintf(“Releasing all regions Owned by %s mem owner”, mem_owner.name()));
  21. foreach(this.nv_in_use[i]) begin
  22. `vmm_verbose(this.log,$psprintf(“REGION OWNER”,this.nv_in_use[i].mem_owner.name()));
  23. if(this.nv_in_use[i].mem_owner==mem_owner) begin
  24. super.release_region(this.in_use[i]);
  25. this.in_use.delete(i);
  26. this.nv_in_use.delete(i);
  27. break;
  28. end
  29. end
  30. endfunction
  31. .
  32. .
  33. endclass

Memory Model:

The above customized MAM can be wrapped around in a class in order to bind memory configuration class with specific HW/SW memory implementation, memory monitors, checker, fw_load functions and various read write tasks. In the code below,  sys_mam_trace could be called anytime an actual read/write operation is performed on the hw/sw memory. This function provides notifications to indicate read/write to a certain address location. Two race free blocking functions wait_for_rd and wait_for_wr are implemented around these which could be used anywhere in the verification system to trap memory accesses. Based on these trap a cache protocol checker or any other checker could be implemented easily. Function load_fw can read an image file and preload any firmware code at the desired location in system memory. It also reserves the memory region so as to prevent any read/writes or allocation of the same address range to some other client in the system.  Thus with the customizations, we made to the off-the-shelf application class, we were able to efficiently meet our verification requirements!

  1. class sys_mam_c extends vmm_subenv;
  2. rand vmm_mam_cfg cfg;
  3. nv_mam_allocator sys_malloc;
  4. nv_mam mam;
  5. // setting up memory traps
  6. event indicate_rd, indicate_wr;
  7. bit rd_access[*];
  8. bit wr_access[*];
  9. // memory model – theh memory is implemented as an associative array of class objects
  10. mem_loc sys_mem[*];
  11. // constraint memory configuration as per the selected addressing mode
  12. constraint sys_mam_valid_cons {
  13. // 40 bit address space each address pointing to 4 bytes.
  14. cfg.n_bytes == 1; //no of bytes each address points to.
  15. cfg.start_offset == 0; //
  16. (amode == BIT32) -> {cfg.end_offset == 64′hFFFFFFFF};
  17. (amode == BIT64) -> {cfg.end_offset == 64′hFFFFFFFFFFFFFF};
  18. cfg.mode == vmm_mam::GREEDY;
  19. cfg.locality == vmm_mam::BROAD;
  20. }
  21. .
  22. .
  23. .
  24. // debug/monitor/notification function
  25. function void sys_mam_c :: sys_mam_trace(bit rw, bit [63:0] addr, bit[31:0] data, bit [3:0] byte_en, string requester);
  26. // indicate mem read/write notifications
  27. if (rw) begin
  28. rd_access[addr] = 1;
  29. -> indicate_rd;
  30. end else begin
  31. wr_access[addr] = 1;
  32. -> indicate_wr;
  33. end
  34. // printing a range of addresses
  35. if (mem_trace_en | wr_trace_en | rd_trace_en) begin
  36. if (trace_sa < trace_ea) begin
  37. if ((addr >= trace_sa) && (addr < trace_ea+1)) begin
  38. // `vmm_debug(this.log, $psprintf(“Memory Trace Enabled [trace_sa=%h:trace_ea=%h]“, trace_sa, trace_ea));
  39. // run time print of memory read/writes
  40. if (!rw && (wr_trace_en | mem_trace_en)) begin
  41. `vmm_note(this.log, $psprintf(“MEM_WRITE: addr %h, data :%h byte_en :%b requester %s:”, addr, data, byte_en, requester));
  42. end else if (rw && (rd_trace_en | mem_trace_en)) begin
  43. `vmm_note(this.log, $psprintf(“MEM_READ: addr %h, data :%h, requester %s:”, addr, data, requester));
  44. end
  45. end
  46. end else begin
  47. // memory read/writes trace
  48. if (!rw && (wr_trace_en | mem_trace_en)) begin
  49. `vmm_note(this.log, $psprintf(“MEM_WRITE: addr %h, data :%h byte_en :%b requester %s:”, addr, data, byte_en, requester));
  50. end else if (rw && (rd_trace_en | mem_trace_en)) begin
  51. `              vmm_note(this.log, $psprintf(“MEM_READ: addr %h, data :%h, requester %s:”, addr, data, requester));
  52. end
  53. end
  54. end
  55. endfunction : sys_mam_trace
  56. // trap memory read accesses
  57. task sys_mam_c :: wait_for_rd(bit [63:0] mem_addr);
  58. `vmm_note(this.log, $psprintf(“Waiting For a Read Access to Mem Addr #%0h”, mem_addr));
  59. while (!this.rd_access.exists(mem_addr)) begin
  60. @(indicate_rd);
  61. end
  62. this.rd_access.delete(mem_addr);
  63. endtask: wait_for_rd
  64. // trap memory write accesses
  65. task sys_mam_c :: wait_for_wr(bit [63:0] mem_addr);
  66. `vmm_note(this.log, $psprintf(“Waiting For a Write Access to Mem Addr #%0h”, mem_addr));
  67. while (!this.wr_access.exists(mem_addr)) begin
  68. @(indicate_wr);
  69. end
  70. this.wr_access.delete(mem_addr);
  71. endtask: wait_for_wr
  72. .
  73. .
  74. .
  75. // load fw in memory with an img file
  76. function void sys_mam_c::load_fw(string fw_file_name);
  77. bit [31:0] mem [*];
  78. bit [63:0] addr;
  79. static bit addr_reserved[*];
  80. mem_loc load_loc;
  81. $readmemh(fw_file_name, mem);
  82. foreach (mem[i]) begin
  83. // i is word aligned address. Change it to byte aligned
  84. addr = i<<2;
  85. // reserved the address if not already reserved
  86. if (!addr_reserved[addr]) begin
  87. this.mam.nv_reserve_region(addr, 4);
  88. addr_reserved[addr] = 1;
  89. end
  90. write_dw(addr, mem[i], 4′b1111, “load_fw”);
  91. `vmm_note(this.log, $psprintf(“MEM i=%0h and ADDR=%0h Contains Data %0h”, i, addr, mem[i]));
  92. end
  93. endfunction : load_fw
  94. endclass

Posted in Customization, Memory Management, Reuse | 1 Comment »

Address alignment in Memory Allocation Manager

Posted by Janick Bergeron on 10th August 2009

jb_blog

Janick Bergeron, Synopsys Fellow

The VMM Memory Allocation Manager (vmm_mam) can be used to manage a shared address space. It does not physically allocate memory but dishes out address ranges that are guaranteed to be previously unallocated.

By default, the only constraints on the Memory Allocation Manager are 1) the entire address range must not be already allocated, 2) the starting offset must be greater than or equal the base offset of the memory and 3) the ending offset must be less than or equal to the offset limit.

That’s it.

So randomly allocating memory will result in memory regions starting at random positions. For example, the following code allocates ten 4-byte regions in a 256-byte memory:

program test;

`include “vmm.sv”
`include “vmm_ral.sv”

initial
begin
vmm_mam_cfg    cfg;
vmm_mam        mgr;
vmm_mam_region bfr;

cfg = new;
cfg.n_bytes      = 1;
cfg.start_offset = 8′h00;
cfg.end_offset   = 8′hFF;

mgr = new(“Mem Mgr”, cfg);

repeat (10) begin
bfr = mgr.request_region(4);
$write(“%s\n”, bfr.psdisplay());
end
endprogram

And produce the following results:

['h0000000000000080:'h0000000000000083]
['h0000000000000015:'h0000000000000018]
['h00000000000000bd:'h00000000000000c0]
['h00000000000000b9:'h00000000000000bc]
['h00000000000000e6:'h00000000000000e9]
['h000000000000005f:'h0000000000000062]
['h000000000000008d:'h0000000000000090]
['h0000000000000025:'h0000000000000028]
['h0000000000000051:'h0000000000000054]
['h0000000000000087:'h000000000000008a]

but what if you needed the region to be aligned on quad-word boundaries?

Simple!

You can add constraints by extending the vmm_mam_allocator object and supplying the new allocator to the request_region() call or making it the default allocation policy on the Memory Allocation Manager instance:

program test;

`include “vmm.sv”
`include “vmm_ral.sv”

class qword_aligned_allocator extends vmm_mam_allocator;
constraint qword_aligned {
start_offset[1:0] == 0;
}
endclass

initial
begin
vmm_mam_cfg    cfg;
vmm_mam        mgr;
vmm_mam_region bfr;

cfg = new;
cfg.n_bytes      = 1;
cfg.start_offset = 8′h00;
cfg.end_offset   = 8′hFF;

mgr = new(“Mem Mgr”, cfg);

begin
qword_aligned_allocator alloc = new;

repeat (5) begin
bfr = mgr.request_region(4, alloc);
$write(“%s\n”, bfr.psdisplay());
end

mgr.default_alloc = alloc;

repeat (5) begin
bfr = mgr.request_region(4);
$write(“%s\n”, bfr.psdisplay());
end
end

end

endprogram

The resulting regions are now QWORD aligned:

['h00000000000000b4:'h00000000000000b7]
['h0000000000000064:'h0000000000000067]
['h00000000000000c0:'h00000000000000c3]
['h0000000000000008:'h000000000000000b]
['h00000000000000b8:'h00000000000000bb]
['h0000000000000048:'h000000000000004b]
['h0000000000000010:'h0000000000000013]
['h0000000000000018:'h000000000000001b]
['h00000000000000d4:'h00000000000000d7]
['h00000000000000fc:'h00000000000000ff]

Other kind of constraints can you add via a customized allocator include:

  • Region must be within a 1-k address page
  • 75% of regions must be in the upper half of the address space
  • Regions must be allocated from a pool of pre-allocated regions

Remember that regions are allocated by randomizing the allocator object. That means that a highly directed and procedural allocation policy (such as the last one mentioned above) can be implemented using the post_randomize() method.

Once you have a region, if you have associated the Memory Allocation Manager with a memory RAL abstraction class, you may access it as if it were a tiny memory in and of itself. The integration of MAm and RAL effectively implements a mini virtual-memory system.

Posted in Customization, Memory Management, Tutorial | No Comments »