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() 所申请内存的线性地址与物理地址都是连续的。
用户态内存分配
用户态内存分配函数:
● 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倒序
% # 匹配的括号前后切换
^ # 移动到行首非空字符位置
Le vent se lève! . . . il faut tenter de vivre!
Le vent se lève! . . . il faut tenter de vivre!