【IC每日一题:IC验证常见问题】

科技   2024-11-12 12:19   陕西  

本篇文章介绍IC验证面试时常问的面试题;

1 SV基础语法类

1.1 Q1:定宽数组、动态数组、关联数组和队列各自的特点和使用场景?

1.定宽数组:在声明时即确定了数组的大小(编译),即宽度和深度固定;
场景:适用固定大小的数组;
常用方法:索引访问、循环遍历和初始化;

//data_type array_name[array_size];

logic [7:0] fixed_array[7:0];
foreach (fixed_array[i]) begin
fixed_array[i] = 8;
end

2.动态数组:在声明时使用[]不需要指定大小,在程序运行时进行动态的分配和释放内存;
适用场景:需要在运行时动态调整数组大小的场景;
常用方法:分配内存、索引访问、循环遍历和释放内存。

//data_type dyna_name[];
logic [7:0] dynamic_array[];
// 分配10个元素的内存空间
dynamic_array = new[10];

// 初始化数组元素
for (int i = 0; i < 10; i++) begin
dynamic_array[i] = i;
end

// 打印数组元素
for (int i = 0; i < 10; i++) begin
$display("dynamic_array[%0d] = %0d", i, dynamic_array[i]);
end

// 释放内存
delete dynamic_array;

3.关联数组:关联数组是一种特殊的数组,它使用索引值(可以是整数或字符串)来访问数组元素,索引值和元素之间是一对一的映射关系
场景:适用用来保存稀疏数组的元素,节省极大空间;其主要针对需要超大空间但又不是全部需要所有数据的时候使用,类似于hash;
常用方法:分配内存、索引访问、循环遍历、检查索引存在性和删除元素。

//=====================================associative_array
logic [7:0] associative_array[string];

initial begin
// 初始化数组元素
associative_array["apple"] = 8'h01;
associative_array["banana"] = 8'h02;
associative_array["cherry"] = 8'h03;

// 打印数组元素
foreach (associative_array[index]) begin
$display("associative_array[%s] = %0d", index, associative_array[index]);
end

// 检查索引存在性
if (associative_array.exists("banana")) begin
$display("Index 'banana' exists in the array.");
end

// 删除元素
associative_array.delete("cherry");

// 打印数组元素
foreach (associative_array[index]) begin
$display("associative_array[%s] = %0d", index, associative_array[index]);
end
end

4.队列:一种先进先出(FIFO)的数据结构,它允许在队列的两端进行插入和删除操作。
场景:数组缓存等;
队列的常用方法包括:

  • 插入元素:使用push_back方法在队列的末尾插入一个元素。例如,queue_name.push_back(element)表示将元素element插入到队列queue_name的末尾。

  • 删除元素:使用pop_front方法从队列的头部删除一个元素。例如,queue_name.pop_front()表示从队列queue_name的头部删除一个元素。

  • 获取队列大小:使用size方法获取队列中元素的个数。例如,queue_name.size()表示获取队列queue_name中元素的个数。

  • 检查队列是否为空:使用empty方法检查队列是否为空。例如,if (queue_name.empty())表示检查队列queue_name是否为空。

  • 访问队列元素:使用索引值访问队列中的元素,但需要注意队列的索引是从0开始的。例如,queue_name[index]表示访问队列queue_name中第index个元素。

int queue[$];

initial begin
// 插入元素
queue.push_back(1);
queue.push_back(2);
queue.push_back(3);

// 打印队列元素
$display("Queue size: %0d", queue.size());
foreach (queue[i]) begin
$display("queue[%0d] = %0d", i, queue[i]);
end

// 删除元素
queue.pop_front();

// 打印队列元素
$display("Queue size: %0d", queue.size());
foreach (queue[i]) begin
$display("queue[%0d] = %0d", i, queue[i]);
end
end

1.2 Q2:多线程fork join/fork join_any/fork join_none的用法差异

fork…join:父进程会阻塞所有子线程结束。
fork…join_any:父进程会阻塞到任意一个线程结束。
fork…join_none:父进程会继续与所有子线程并发执行。
wait fork: 使父线程等待所有子线程执行完毕;一般用来确保所有子进程(调用进程产生的进程,也即一级子进程)执行都已经结束。
disable fork: 当执行disable fork时,会终止所有子线程的执行;

1.3 Q3: 多线程间的同步调度方法

1.事件:事件主要用于两个线程之间的一个同步运行,通过事件触发和事件等待进行两个线程间的运行同步。使用@(event)或者 wait(event.trigger)进行等待,->进行触发。

event event_name;

initial begin
// 触发事件
-> event_name;
end

initial begin
// 等待事件
@event_name;
$display("Event triggered.");
end

2.旗语:一种用于控制线程对共享资源访问的同步机制,通过key的获取和返回实现一个线程对资源的一个访问;使用new操作符创建旗语实例,并使用get和put方法来获取和释放资源。

semaphore sem;

initial begin
sem = new(1); // 初始化旗语,允许一个线程访问资源
end

initial begin
// 获取资源
sem.get();
$display("Resource acquired.");
// 释放资源
sem.put();
end

3.邮箱:邮箱是一种用于线程间通信的机制,主要用于两个线程之间的数据通信,通过put函数和 get 函数还有peek函数进行数据的发送和获取;

//====mailbox
mailbox mbx;

initial begin
mbx = new();
end

initial begin
// 发送消息
mbx.put(10);
end

initial begin
// 接收消息
int msg;
mbx.get(msg);
$display("Received message: %0d", msg);
end

1.4 SV中深拷贝和浅拷贝的区别

浅拷贝:浅拷贝创建一个新的对象,但新对象中的数据成员(特别是指向动态分配内存的指针)仍然指向原对象中的相同内存地址。这意味着,如果原对象中的数据发生变化,新对象中的数据也会受到影响。采用类似“引用”的方式,浅拷贝前后共用同一内存空间。浅拷贝通常通过简单的赋值操作来实现。
特点:

  • 新对象和原对象共享相同的内存。

  • 对于动态分配的内存,浅拷贝不会重新分配新的内存。

  • 修改其中一个对象的数据会影响另一个对象

深拷贝:在深拷贝中,原始对象和复制对象是完全独立的,深拷贝创建一个新的对象,并且新对象中的数据成员(特别是指向动态分配内存的指针)会指向新的、独立的内存地址。这意味着,新对象和原对象的数据是完全独立的,修改其中一个对象的数据不会影响另一个对象。

class MyClass;
int data;
MyClass nestedObj;

function new(int d, MyClass n);
data = d;
nestedObj = n;
endfunction
endclass

module copy_example;
MyClass obj1, obj2;

initial begin
obj1 = new(10, null);
obj2 = new(20, obj1);

// 浅拷贝
obj1 = obj2;
// 现在 obj1 和 obj2 指向同一个对象,包括嵌套对象

// 深拷贝
obj1 = new obj2;
// 现在 obj1 是 obj2 的一个完全独立的副本,包括嵌套对象
end
endmodule

1.5 类中的public、proteced和local的区别

  • 如果没有指明访问类型,那么成员的默认类型是public,子类和外部对象均可以访问成员。
    public成员通常用于定义类的接口,供外部代码使用

  • 如果指明了访问类型是protected,那么只有该类或者子类可以访问成员,而外部对象无法访问。
    protected成员通常用于在类的继承层次中共享数据和方法;

  • 如果指明了访问类型是local,那么只有该类可以访问成员,子类和外部均无法访问。
    local成员通常用于类的内部实现细节,不希望被外部或子类访问。

class BaseClass;
// public 成员
int public_var;
function void public_func();
$display("This is a public function in BaseClass");
endfunction

// protected 成员
protected int protected_var;
protected function void protected_func();
$display("This is a protected function in BaseClass");
endfunction

// local 成员
local int local_var;
local function void local_func();
$display("This is a local function in BaseClass");
endfunction

// 构造函数
function new();
public_var = ½;
protected_var = 1;
local_var = 2;
endfunction
endclass

class DerivedClass extends BaseClass;
// 派生类中可以访问 protected 成员
function void accessProtectedMembers();
$display("Accessing protected_var in DerivedClass: %0d", protected_var);
protected_func();
endfunction

// 派生类中不能访问 local 成员
function void tryAccessLocalMembers();
// 下面这两行会导致编译错误
// $display("Accessing local_var in DerivedClass: %0d", local_var);
// local_func();
endfunction

// 构造函数
function new();
super.new();
endfunction
endclass

module tb;
initial begin
BaseClass base = new();
DerivedClass derived = new();

// 外部可以直接访问 public 成员
$display("Accessing public_var in BaseClass: %0d", base.public_var);
base.public_func();

// 外部不能直接访问 protected 成员
// 下面这两行会导致编译错误
// $display("Accessing protected_var in BaseClass: %0d", base.protected_var);
// base.protected_func();

// 外部不能直接访问 local 成员
// 下面这两行会导致编译错误
// $display("Accessing local_var in BaseClass: %0d", base.local_var);
// base.local_func();

// 派生类可以访问 protected 成员
derived.accessProtectedMembers();

// 派生类不能访问 local 成员
// derived.tryAccessLocalMembers(); // 这一行会导致编译错误
end
endmodule

  • public_var 和 public_func:可以在类的外部、派生类以及类的内部访问。

  • protected_var 和 protected_func:可以在类的内部和派生类中访问,但在类的外部不可直接访问。

  • local_var 和 local_func:只能在类的内部访问,派生类和类的外部均不可访问

【REF】
1.https://blog.csdn.net/graymount/article/details/121391722

路科验证
专注于数字芯片验证的系统思想和前沿工程领域。
 最新文章