UVM中打印信息的控制+内存分配算法+C语言fork()函数+使用uvm自带的reg做默认值检查+uvm_resource_db+ral手动更新+vim操作

UVM中打印信息的控制

非uvm平台控制的module中的uvm info使用*号通配,而找不到对应的uvm_top,起始点不是harness/top。使用ID可以匹配到对应的信息。
https://blog.csdn.net/Bonnie_89/article/details/128897812

使用uvm_set_action,将某ID和某严重性的行为重载

  // +uvm_set_action=<comp>,<id>,<severity>,<action[|action]>
  // +uvm_set_action=uvm_test_top.env0.*,_ALL_,UVM_ERROR,UVM_NO_ACTION

行为有以下表示:

UVM_NO_ACTION - No action is taken
UVM_DISPLAY   - Sends the report to the standard output
UVM_LOG       - Sends the report to the file(s) for this (severity,id) pair
UVM_COUNT     - Counts the number of reports with the COUNT attribute. When this value reaches max_quit_count, the simulation terminates
UVM_EXIT      - Terminates the simulation immediately.
UVM_CALL_HOOK - Callback the report hook methods 
UVM_STOP      - Causes ~$stop~ to be executed, putting the simulation into interactive mode.
UVM_RM_RECORD - Sends the report to the recorder

默认的行为:

set_severity_action(UVM_INFO,    UVM_DISPLAY);
set_severity_action(UVM_WARNING, UVM_DISPLAY);
set_severity_action(UVM_ERROR,   UVM_DISPLAY | UVM_COUNT);
set_severity_action(UVM_FATAL,   UVM_DISPLAY | UVM_EXIT);

uvm_set_verbosity设置冗余度,可以使用某个phase,或时间

~+uvm_set_verbosity=<comp>,<id>,<verbosity>,<phase>~
~+uvm_set_verbosity=<comp>,<id>,<verbosity>,time,<time>~ 
 <sim command> +uvm_set_verbosity=uvm_test_top.env0.agent1.*,_ALL_,UVM_FULL,time,800

phase不需要加_phase后缀,在uvm_runtime_phases.svh中,其定义的phase名称也是没有加_phase的。

class uvm_pre_shutdown_phase extends uvm_task_phase; 
   virtual task exec_task(uvm_component comp, uvm_phase phase); 
      comp.pre_shutdown_phase(phase); 
   endtask
   local static uvm_pre_shutdown_phase m_inst; 
   static const string type_name = "uvm_pre_shutdown_phase"; 

   // Function: get
   // Returns the singleton phase handle 
   static function uvm_pre_shutdown_phase get(); 
      if(m_inst == null)
         m_inst = new; 
      return m_inst; 
   endfunction
   protected function new(string name="pre_shutdown"); 
      super.new(name); 
   endfunction
   virtual function string get_type_name(); 
      return type_name; 
   endfunction
endclass

重载严重度 uvm_set_severity

+uvm_set_severity=<comp>,<id>,<current severity>,<new severity>

内存分配算法

伙伴(Buddy)分配算法

伙伴分配算法的原理就是:将所有的空闲页框分组为 11 个块链表,每个块链表分别包含大小为 1,2,4,8,16,32,64,128,256,512 和 1024 个连续页的页块。最大可以申请 1024 个连续页,对应 4MB 大小的连续内存。假设需要申请 4 个页,但是长度为 4 个连续页块链表没有空闲的页块,伙伴系统会从连续 8 个框块的链表获取一个,并将其拆分为两个连续 4 个页块,取其中一个,另外一个放入连续 4 个页块的空闲链表中。释放的时候会检查,释放的这几个页前后的页框是否空闲,能否组成下一级长度的块。
https://bbs.huaweicloud.com/blogs/291867

cat /proc/buddyinfo #查看buddy分配结果

Slab 算法

将内核中经常使用的对象放到高速缓存中,并且由系统保持为初始的可利用状态。比如进程描述符,内核中会频繁对此数据进行申请和释放。Slab 内存分配器是对伙伴分配算法的补充,Slab 分配器为每一种对象建立高速缓存,通过将内存按使用对象不同再划分成不同大小的空间,应用于内核对象的缓存,内核对该对象的分配和释放均是在这块高速缓存中操作。每个kmem_cache 通常是一段连续的内存块,包含了三种类型的 slabs 链表:
● slabs_full:完全分配的 slab 链表
● slabs_partial:部分分配的 slab 链表
● slabs_empty:没有被分配对象的 slab 链表

cat /proc/slabinfo #查看slab内存信息

内核态和用户态

在内核态申请内存比在用户态申请内存要更为直接,它没有采用用户态那种延迟分配(通过缺页机制来反馈)内存技术。一旦有内核函数申请内存,那么就必须立刻满足该申请内存的请求,并且这个请求一定是正确合理的。相反,对于用户态申请内存的请求,内核总是尽量延后分配物理内存,用户进程总是先获得一个虚拟内存区的使用权,最终通过缺页异常获得一块真正的物理内存。

内核态分配函数kmalloc 和 vmalloc

kmalloc 和 vmalloc 分别用于分配不同映射区的虚拟内存。vmalloc 分配的虚拟地址区间,位于 vmalloc_start 与 vmalloc_end 之间的动态内存映射区。一般用分配大块内存,释放内存对应于 vfree,分配的虚拟内存地址连续,物理地址上不一定连续。kmalloc() 所申请内存的线性地址与物理地址都是连续的。
image

用户态内存分配

用户态内存分配函数:
● alloca 是向栈申请内存,因此无需释放。
● malloc 所分配的内存空间未被初始化,调用 malloc 函数时,它沿 free_chuck_list 连接表寻找一个大到足以满足用户请求所需要的内存块。
● calloc 会将所分配的内存空间中的每一位都初始化为零。
● realloc 扩展现有内存空间大小。
○ 如果当前连续内存块足够 realloc 的话,只是将 p 所指向的空间扩大,并返回 p 的指针地址。这个时候 q 和 p 指向的地址是一样的。
○ 如果当前连续内存块不够长度,再找一个足够长的地方,分配一块新的内存,q,并将 p 指向的内容 copy 到 q,返回 q。并将 p 所指向的内存空间删除。

用户进程内存分配示例

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>


int main(void)
{
    char *s;
    unsigned long int i;

    s = strdup("test_memory");
    if (NULL == s) {
        fprintf(stderr, "Can't allocate mem with malloc.\n");
        return EXIT_FAILURE;
    }

    i = 0;
    while (s) {
        printf("[%lu] %s (%p)\n", i, s, (void *)s);
        sleep(1);
        i++;
    }
    return EXIT_SUCCESS;
}

在 Linux 操作系统中,可以通过 /proc/[pid]/mem 访问和修改进程的内存页,可以通过 /proc/[pid]/maps 看到进程当前已映射的内存区域。

cat /proc/18539/maps #查看栈空间起始地址

修改栈空间,以修改其它程序运行。
修改程序运行在内核态,而执行程序需要运行在用户态。

#!/usr/bin/env python3
'''
Locates and replaces the first occurrence of a string in the heap
of a process

Usage: ./read_write_heap.py PID search_string replace_by_string
Where:
- PID is the pid of the target process
- search_string is the ASCII string you are looking to overwrite
- replace_by_string is the ASCII string you want to replace
  search_string with
'''

import sys

def print_usage_and_exit():
    print('Usage: {} pid search write'.format(sys.argv[0]))
    sys.exit(1)

# check usage
if len(sys.argv) != 4:
    print_usage_and_exit()

# get the pid from args
pid = int(sys.argv[1])
if pid <= 0:
    print_usage_and_exit()
search_string = str(sys.argv[2])
if search_string  == "":
    print_usage_and_exit()
write_string = str(sys.argv[3])
if search_string  == "":
    print_usage_and_exit()

# open the maps and mem files of the process
maps_filename = "/proc/{}/maps".format(pid)
print("[*] maps: {}".format(maps_filename))
mem_filename = "/proc/{}/mem".format(pid)
print("[*] mem: {}".format(mem_filename))

# try opening the maps file
try:
    maps_file = open('/proc/{}/maps'.format(pid), 'r')
except IOError as e:
    print("[ERROR] Can not open file {}:".format(maps_filename))
    print("        I/O error({}): {}".format(e.errno, e.strerror))
    sys.exit(1)

for line in maps_file:
    sline = line.split(' ')
    # check if we found the heap
    if sline[-1][:-1] != "[heap]":
        continue
    print("[*] Found [heap]:")

    # parse line
    addr = sline[0]
    perm = sline[1]
    offset = sline[2]
    device = sline[3]
    inode = sline[4]
    pathname = sline[-1][:-1]
    print("\tpathname = {}".format(pathname))
    print("\taddresses = {}".format(addr))
    print("\tpermisions = {}".format(perm))
    print("\toffset = {}".format(offset))
    print("\tinode = {}".format(inode))

    # check if there is read and write permission
    if perm[0] != 'r' or perm[1] != 'w':
        print("[*] {} does not have read/write permission".format(pathname))
        maps_file.close()
        exit(0)

    # get start and end of the heap in the virtual memory
    addr = addr.split("-")
    if len(addr) != 2: # never trust anyone, not even your OS :)
        print("[*] Wrong addr format")
        maps_file.close()
        exit(1)
    addr_start = int(addr[0], 16)
    addr_end = int(addr[1], 16)
    print("\tAddr start [{:x}] | end [{:x}]".format(addr_start, addr_end))

    # open and read mem
    try:
        mem_file = open(mem_filename, 'rb+')
    except IOError as e:
        print("[ERROR] Can not open file {}:".format(mem_filename))
        print("        I/O error({}): {}".format(e.errno, e.strerror))
        maps_file.close()
        exit(1)

    # read heap
    mem_file.seek(addr_start)
    heap = mem_file.read(addr_end - addr_start)

    # find string
    try:
        i = heap.index(bytes(search_string, "ASCII"))
    except Exception:
        print("Can't find '{}'".format(search_string))
        maps_file.close()
        mem_file.close()
        exit(0)
    print("[*] Found '{}' at {:x}".format(search_string, i))

    # write the new string
    print("[*] Writing '{}' at {:x}".format(write_string, addr_start + i))
    mem_file.seek(addr_start + i)
    mem_file.write(bytes(write_string, "ASCII"))

    # close files
    maps_file.close()
    mem_file.close()

    # there is only one heap in our example
    break

C语言fork()函数

1)在父进程中,fork返回新创建子进程的进程ID;
2)在子进程中,fork返回0;
3)如果出现错误,fork返回一个负值;

/*linux下:*/
 
#include <stdio.h>
#include <unistd.h>
 
int main() {
    pid_t pid;
    pid = fork();
    if(pid  == 0) //返回子进程
    {
        printf("child pid: %d\n", getpid());
    } else {
        printf("pid: %d\n", pid);//父进程中返回子进程的pid
        printf("father pid: %d\n", getpid());
    }
}

输出:父子进程在第8行之后同时执行。

pid: 5989
father pid: 5988
child pid: 5989

使用uvm自带的reg做默认值检查

继承uvm_reg_hw_reset_seq,然后在pre_body中设定model即可。
此外还有读写函数uvm_reg_bit_bash_seq。
https://blog.csdn.net/weixin_39662684/article/details/110133060

uvm_resource_db

https://www.cnblogs.com/csjt/p/15556969.html
uvm_resource_db虽然也是一种用来共享数据的类,但是层次关系在该类中没有作用; 与uvm_config_db相比,尽管uvm_resource_db也有内建的数据库,可以通过字符串或类型来索引配置数据,但缺点是层次的缺失和因此带来的自顶向下的配置覆盖关系的缺失.
(2.1) uvm_resource_db采用的是”last write wins”,即对同一配置,最后的写入有效;
(2.2) uvm_config_db采用的是”parent wins + last write wins”(假设在env中已经有配置,而test的级别高于env).
(3) uvm_config_db与uvm_resource_db共享同一套database; 因此可能会出现通过uvm_config_db::set()往database中存入信息,但用uvm_resource_db::read_by_name()从database中读取信息;

uvm_resource_db#(type)::set("scope", "name", value, accessor);
//path=scope+name, default value of accessor is null;
uvm_resource_db#(int)::set("test", "loop_cnt", 10, this);

https://www.cnblogs.com/csjt/p/15556136.html
使用.get_full_name()的方法对REG::设定值,以忽略测试的位置。设定的Lable根据UVM源码获取,如:(最低粒度是uvm_reg,无法将单独的域区分开)

uvm_resource_db#(bit)::set(
     {"REG::",rm.reg.get_full_name()},
    "NO_REG_BIT_BASH_TEST", 
    1, 
    this
)

ral手动更新

对ral的map设置set_auto_predict(0)
编写更新函数,读取时候使用get_mirrored_value
写入的时候使用predict,
https://east1203.github.io/2020/06/06/Verification/UVM/UVM——关于RAL的一些方法/

vim操作

<>:g/^/m 0 # vim倒序
%          # 匹配的括号前后切换
^          # 移动到行首非空字符位置
posted @ 2024-06-13 08:27  大浪淘沙、  阅读(78)  评论(0编辑  收藏  举报