我们现在就UVM TLM FIFO 类来拆解uvm_tlm_fifo 的相关方法和功能,代码中包含了uvm_tlm_fifo 和 uvm_tlm_analysis_fifo。它们用于在 UVM 验证环境中不同组件之间传输事务。让我们分别分析其功能:
- uvm_tlm_fifo 类:
功能: 这是一个通用的 TLM FIFO,用于在两个进程之间缓存事务。它具有有限的容量(可以通过 size 参数指定,默认为 1),并按照先进先出 (FIFO) 的原则处理事务。
关键方法:
new(): 构造函数,初始化 FIFO 的大小。如果 size 为 0,则 FIFO 容量无限大。
put(): 将事务放入 FIFO。如果 FIFO 已满,则阻塞直到有空间。
get(): 从 FIFO 获取事务。如果 FIFO 为空,则阻塞直到有事务可用。
peek(): 查看 FIFO 中下一个事务,但不将其移除。
try_put(), try_get(), try_peek(): 非阻塞版本的 put, get, peek 方法。如果操作成功则返回 1,否则返回 0.
size(): 返回 FIFO 的容量。
used(): 返回 FIFO 中已使用空间的大小。
is_empty(): 检查 FIFO 是否为空。
is_full(): 检查 FIFO 是否已满。
can_put(): 检查是否可以放入事务。
can_get(): 检查是否可以获取事务。
can_peek(): 检查是否可以查看事务。
flush(): 清空 FIFO。
内部机制: 使用 mailbox #(T) 作为底层存储机制。mailbox 是 UVM 提供的用于进程间通信的组件,它提供了线程安全的读写操作。
- uvm_tlm_analysis_fifo 类:
功能: 这是一个基于分析接口的 FIFO,其容量无限大。它主要用于连接分析端口 (uvm_analysis_port) 和 TLM 目标组件。这种 FIFO 能够在不阻塞的情况下写入事务,因为它的容量是无限的。
关键方法:
new(): 构造函数,初始化一个无限容量的 FIFO,并创建一个 analysis_export 接口。
write(): 将事务写入 FIFO。由于容量无限大,此操作总是成功的。
与 uvm_tlm_fifo 的区别:
容量:uvm_tlm_analysis_fifo 容量无限大,而 uvm_tlm_fifo 容量有限。
接口:uvm_tlm_analysis_fifo 提供了 analysis_export 接口,用于连接分析端口。
阻塞:uvm_tlm_analysis_fifo 的 write 操作是非阻塞的,而 uvm_tlm_fifo 的 put 操作可能是阻塞的。
typedef class uvm_tlm_event; // 定义一个 uvm_tlm_event 类类型的别名
//------------------------------------------------------------------------------
// 标题:TLM FIFO 类
//
// 本节定义基于 TLM 的 FIFO 类。
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// 类:uvm_tlm_fifo
//
// 此类在两个独立运行的进程之间提供事务的存储。事务通过 ~put_export~ 放入 FIFO。
// 事务按到达的顺序从 FIFO 中获取,通过 ~get_peek_export~ 获取。~put_export~ 和
// ~get_peek_export~ 继承自 <uvm_tlm_fifo_base #(T)> 超类,这些导出提供的接口方法由
// <uvm_tlm_if_base #(T1,T2)> 类定义。
//------------------------------------------------------------------------------
class uvm_tlm_fifo #(type T=int) extends uvm_tlm_fifo_base #(T); // 定义一个名为 uvm_tlm_fifo 的类,参数化类型为 T (默认是 int),继承自 uvm_tlm_fifo_base #(T)
const static string type_name = "uvm_tlm_fifo #(T)"; // 定义类的类型名称
local mailbox #( T ) m; // 定义一个局部邮箱,用于存储事务,类型为 T
local int m_size; // 定义FIFO的大小
protected int m_pending_blocked_gets; // 保护型变量,跟踪阻塞的get操作数量
// 函数:new
//
// ~name~ 和 ~parent~ 是标准的 uvm_component 构造函数参数。如果 <uvm_tlm_fifo> 将
// 用于静态详细阐述的结构(例如,模块),则 ~parent~ 应为 null。~size~ 指示 FIFO 的
// 最大大小;值为零表示没有上限。
function new(string name, uvm_component parent = null, int size = 1); // 构造函数
super.new(name, parent); // 调用父类的构造函数
m = new( size ); // 创建一个大小为 size 的邮箱
m_size = size; // 设置 FIFO 大小
endfunction
virtual function string get_type_name(); // 获取类名
return type_name;
endfunction
// 函数:size
//
// 返回 FIFO 的容量,即 FIFO 能够容纳的条目数。返回值 0 表示 FIFO 容量没有限制。
virtual function int size();
return m_size;
endfunction
// 函数:used
//
// 返回放入 FIFO 的条目数。
virtual function int used();
return m.num(); // 返回邮箱中事务的数量
endfunction
// 函数:is_empty
//
// 当 FIFO 中没有条目时返回 1,否则返回 0。
virtual function bit is_empty();
return (m.num() == 0); // 判断邮箱是否为空
endfunction
// 函数:is_full
//
// 当 FIFO 中的条目数等于其 <size> 时返回 1,否则返回 0。
virtual function bit is_full();
return (m_size != 0) && (m.num() == m_size); // 判断邮箱是否已满
endfunction
virtual task put( input T t ); // 将事务 t 放入 FIFO
m.put( t ); // 将事务放入邮箱
put_ap.write( t ); // 将事务写入put接口
endtask
virtual task get( output T t ); // 从 FIFO 获取事务
m_pending_blocked_gets++; // 阻塞的get操作计数加1
m.get( t ); // 从邮箱获取事务
m_pending_blocked_gets--; // 阻塞的get操作计数减1
get_ap.write( t ); // 将事务写入get接口
endtask
virtual task peek( output T t ); // 查看FIFO中的事务,但不移除
m.peek( t ); // 查看邮箱中的事务
endtask
virtual function bit try_get( output T t ); // 尝试从FIFO获取事务,失败返回0
if( !m.try_get( t ) ) begin // 尝试从邮箱获取事务
return 0; // 获取失败
end
get_ap.write( t ); // 将事务写入get接口
return 1; // 获取成功
endfunction
virtual function bit try_peek( output T t ); // 尝试查看FIFO中的事务,失败返回0
if( !m.try_peek( t ) ) begin // 尝试查看邮箱中的事务
return 0; // 查看失败
end
return 1; // 查看成功
endfunction
virtual function bit try_put( input T t ); // 尝试将事务放入FIFO,失败返回0
if( !m.try_put( t ) ) begin // 尝试将事务放入邮箱
return 0; // 放入失败
end
put_ap.write( t ); // 将事务写入put接口
return 1; // 放入成功
endfunction
virtual function bit can_put(); // 判断是否可以放入事务
return m_size == 0 || m.num() < m_size; // 如果大小为0或未满则可以放入
endfunction
virtual function bit can_get(); // 判断是否可以获取事务
return m.num() > 0 && m_pending_blocked_gets == 0; // 如果邮箱不为空且没有阻塞的get操作,则可以获取
endfunction
virtual function bit can_peek(); // 判断是否可以查看事务
return m.num() > 0; // 如果邮箱不为空,则可以查看
endfunction
// 函数:flush
//
// 删除 FIFO 中的所有条目,之后 <used> 返回 0,<is_empty> 返回 1。
virtual function void flush(); // 清空FIFO
T t;
bit r;
r = 1;
while( r ) r = try_get( t ) ; // 循环尝试获取事务直到FIFO为空
if( m.num() > 0 && m_pending_blocked_gets != 0 ) begin // 判断是否有阻塞的get操作
uvm_report_error("flush failed" , // 报告错误
"there are blocked gets preventing the flush", UVM_NONE); // 错误信息
end
endfunction
endclass
//------------------------------------------------------------------------------
// 类:uvm_tlm_analysis_fifo
//
// analysis_fifo 是一个大小无界且具有写入接口的 <uvm_tlm_fifo>。它可以在任何使用
// <uvm_analysis_imp> 的地方使用。典型的用法是作为启动器组件中的 <uvm_analysis_port>
// 和 TLM1 目标组件之间的缓冲区。
//------------------------------------------------------------------------------
class uvm_tlm_analysis_fifo #(type T = int) extends uvm_tlm_fifo #(T); // 定义一个名为 uvm_tlm_analysis_fifo 的类,继承自 uvm_tlm_fifo
// 端口:analysis_export #(T)
//
// analysis_export 向所有连接的分析端口和父导出提供 write 方法:
//
//| function void write (T t)
//
// 通过绑定到此导出的端口进行访问是写入分析 FIFO 的正常机制。
// 有关更多信息,请参见 <uvm_tlm_if_base #(T1,T2)> 的 write 方法。
uvm_analysis_imp #(T, uvm_tlm_analysis_fifo #(T)) analysis_export; // 定义一个分析接口
// 函数:new
//
// 这是标准的 uvm_component 构造函数。~name~ 是此组件的本地名称。当此组件在
// 静态详细阐述的结构中实例化时,应不指定 ~parent~,而当此组件是另一个 UVM
// 组件的子组件时,必须指定 ~parent~。
function new(string name , uvm_component parent = null); // 构造函数
super.new(name, parent, 0); // analysis fifo must be unbounded // 调用父类的构造函数,大小设置为0,表示无界
analysis_export = new("analysis_export", this); // 创建分析接口
endfunction
const static string type_name = "uvm_tlm_analysis_fifo #(T)"; // 定义类的类型名称
virtual function string get_type_name(); // 获取类名
return type_name;
endfunction
function void write(input T t); // 写入事务到FIFO
void'(this.try_put(t)); // unbounded => must succeed // 因为是无界FIFO,所以写入一定成功
endfunction
endclass
总结:
这两个类为 UVM 验证环境提供了灵活的 TLM 事务缓存机制。uvm_tlm_fifo 提供了有限容量的 FIFO,适合需要控制缓冲区大小的场景;uvm_tlm_analysis_fifo 提供了无限容量的 FIFO,并通过分析接口与其他组件通信,适合用于连接分析端口和目标组件,减少阻塞,提高效率。 它们都使用了 mailbox 来保证线程安全。 m_pending_blocked_gets 变量用于跟踪阻塞的 get 操作,这对于 flush 函数的正确性至关重要,以避免在存在阻塞的 get 的情况下,错误地清空 FIFO。