函数可以在全局、包或组件范围内声明。函数可以是静态函数或实例(非静态)函数。全局函数或包函数始终是静态的。组件函数可以显式声明为 static。如果组件函数是非静态的,则每个函数调用都与该组件类型的特定实例相关联。
可以通过以下三种方式之一定义函数:
— 使用 PSS 过程语句,可能调用其他函数。
— 绑定到外部编程语言中的接口,例如 C/C++ 中的函数,或 SystemVerilog 中的函数/任务。这仅适用于静态函数; 实例函数不能绑定。
— 作为目标代码模板块。
以这三种方式之一定义函数可以与函数的初始声明耦合。定义也可以在没有声明的情况下提供。在这种情况下,定义也用作声明,定义可以与声明分开提供。函数的意图和语义由其声明固定,但其实现可能因不同的环境和上下文而异。但是,给定的 PSS 模型及其所有源单元应仅包含任何函数的一个定义。
可以从过程 exec 块(即 exec init_down、init_up、pre_solve、post_solve、body、run_start 和 run_end)中调用函数。从 exec init_down、init_up、pre_solve 和 post_solve 调用的函数在 solve 平台上进行评估; 而从 exec body、run_start 和 run_end 调用的函数在 target 平台上进行评估。如果函数被显式声明为 solve 或 target,则其使用仅限于相应种类的 exec 块的上下文中。
在组件范围内声明的静态函数可能会被派生组件中具有相同名称的函数声明所覆盖,该函数声明可以是静态的,也可以是非静态的。派生组件中的函数声明可能具有与基组件中的返回类型或参数不同。
在组件范围内声明的实例函数可能会被派生组件中具有相同名称的实例函数声明所覆盖。派生组件中的函数声明必须与基组件中的函数声明具有相同的返回类型和参数。
------------------------------------------------------------------------------------------------------------------------------
函数原型在 PSS 描述的 package 或 component 范围内声明。函数原型指定函数可用性是否仅限于 solve 或 target 平台、是否为静态、函数名称、返回类型和函数参数。请注意,此处显示的语法仅用于函数原型的声明,其中定义单独提供。还可以使用过程语句块、目标代码模板或 import function 语句立即声明和定义函数。在这些情况下,也使用相同的语法来指定原型。
在某些环境中,测试生成和执行是单独的活动。在这些环境中,某些函数可能仅在 solve 平台上的测试生成期间可用,而其他函数仅在 target 平台上的测试执行期间可用。例如,参考模型函数可能仅在测试生成期间可用,而对硬件设备进行编程的实用程序函数可能仅在测试执行期间可用。
假定非限定函数在测试生成和执行的所有阶段都可用。指定限定符以限制函数的可用性。不得从 target execs (即 body、run_start 和 run_end)直接或间接调用仅限于 solve 平台的函数。同样,限制在目标平台的函数也不能从 solve execs 中调用,即 init_down、init_up、pre_solve、post_solve 和 pre_body。
------------------------------------------------------------------------------------------------------------------------------
package arithmetic_pkg {
// Prototypes
solve function int solve_add(int a, int b);
target function int target_add(int a, int b);
// Definitions
solve function int solve_add(int a, int b) { // solve-only definition
return a+b;
}
target function int target_add(int a, int b) { // definition for any platform
return a+b;
}
function bool compare(int src1, src2) {
return src1 == src2;
}
};
component pss_top {
import std_pkg::*;
import arithmetic_pkg::*;
action add_test_a {
rand int base1;
rand int base2;
int solve_sum;
exec post_solve {
solve_sum = solve_add(base1, base2);
};
exec body {
int sum;
sum = target_add(base1, base2);
if (arithmetic_pkg::compare(solve_sum, sum)) {
message(NONE, "-----------------------\r\n");
} else {
message(NONE, "=======================\r\n");
}
}
};
};
------------------------------------------------------------------------------------------------------------------------------
外语程序接口
PSS 中的静态函数声明可能会公开并最终绑定到 target 平台和/或 solve 平台上可用的外语 API(函数、任务、过程等)。以前在 PSS 描述中声明的函数可以指定为 imported。从 PSS 过程上下文调用 import 的函数会以外语调用相应的 API。参数和结果传递受为该语言定义的类型映射的约束。
使用导入的函数定义
其他语言限定符将添加到导入的函数中,以便向工具提供有关函数实现方式的更多信息。在典型使用中,此类限定符在特定于环境的软件包(例如,特定于 UVM 环境的软件包或 C-test 特定的软件包)中指定。
在组件中声明的静态函数只能在相同组件类型的范围内导入。在派生或不相关的组件类型中导入以基组件类型声明的函数是非法的。
package memory_base_pkg {
// C implement
import target C function void hsi_write8(bit[32] addr, bit[8] data);
import target C function void hsi_write16(bit[32] addr, bit[16] data);
import target C function void hsi_write32(bit[32] addr, bit[32] data);
import target C function void hsi_write64(bit[32] addr, bit[64] data);
import target C function bit[8] hsi_read8(bit[32] addr);
import target C function bit[16] hsi_read16(bit[32] addr);
import target C function bit[32] hsi_read32(bit[32] addr);
import target C function bit[64] hsi_read64(bit[32] addr);
// System Verilog implement
import target SV function void scalar_write8(bit[32] addr, bit[8] data);
import target SV function void scalar_write32(bit[32] addr, bit[32] data);
import target SV function bit[8] scalar_read8(bit[32] addr);
import target SV function bit[32] scalar_read32(bit[32] addr);
};
component pss_top {
import memory_base_pkg::*;
exec declaration C = """
#define hsi_read8(addr) (*((volatile uint8_t *)(addr)))
#define hsi_read16(addr) (*((volatile uint16_t *)(addr)))
#define hsi_read32(addr) (*((volatile uint32_t *)(addr)))
#define hsi_read64(addr) (*((volatile uint64_t *)(addr)))
#define hsi_write8(addr, val) ((*((volatile uint8_t *)(addr))) = ((uint8_t)(val)))
#define hsi_write16(addr, val) ((*((volatile uint16_t *)(addr))) = ((uint16_t)(val)))
#define hsi_write32(addr, val) ((*((volatile uint32_t *)(addr))) = ((uint32_t)(val)))
#define hsi_write64(addr, val) ((*((volatile uint64_t *)(addr))) = ((uint64_t)(val)))
""";
exec declaration SV = """
export "DPI-C" task scalar_write8;
task scalar_write8(input longint unsigned addr, input byte unsigned data);
static string apb_master = {"*.", inst_name, ".master"};
apb_master_agent master;
master = find_apb_master_agent(apb_master);
apb_write(master, addr, 1, data);
endtask;
export "DPI-C" task scalar_write32;
task scalar_write32(input longint unsigned addr, input int unsigned data);
static string apb_master = {"*.", inst_name, ".master"};
apb_master_agent master;
master = find_apb_master_agent(apb_master);
apb_write(master, addr, 4, data);
endtask;
export "DPI-C" task scalar_read8;
task scalar_read8(input longint unsigned addr, output byte unsigned data);
static string apb_master = {"*.", inst_name, ".master"};
apb_master_agent master;
master = find_apb_master_agent(apb_master);
apb_read(master, addr, 1, data);
endtask;
export "DPI-C" task scalar_read32;
task scalar_read32(input longint unsigned addr, output int unsigned data);
static string apb_master = {"*.", inst_name, ".master"};
apb_master_agent master;
master = find_apb_master_agent(apb_master);
apb_read(master, addr, 4, data);
endtask;
""";
action hsi_write8_a {
rand bit[32] addr;
rand bit[8] data;
exec body {
hsi_write8(addr, data);
}
}
action hsi_read8_a {
rand bit[32] addr;
exec body {
bit[8] data;
data = hsi_read8(addr);
}
}
////////////////////////////////////////////////////
action scalar_write8_a {
rand bit[32] addr;
rand bit[8] data;
exec body {
hsi_write8(addr, data);
}
}
action scalar_read8_a {
rand bit[32] addr;
exec body {
bit[8] data;
data = scalar_read8(addr);
}
}
action scenario_test_a {
activity {
select {
do hsi_write8_a with { addr == 0x20000000; };
do scalar_write8_a with { addr == 0x20000000; };
}
select {
do hsi_read8_a with { addr == 0x20000000; };
do scalar_read8_a with { addr == 0x20000000; };
}
}
};
};
------------------------------------------------------------------------------------------------------------------------------
目标模板函数
当与没有“函数”概念的语言(如汇编语言)集成时,函数的实现可以由 target-template 代码字符串提供。
target-template 函数形式允许与不涉及过程接口的外部语言进行交互。例如,将汇编代码或全局变量注入生成的测试激励中。target-template 函数形式始终是 target 实现。变量引用只能在表达式位置使用。不应提供函数返回值。如果 target-template 函数是实例(非静态)函数,则嵌入在目标代码中的 PSS 表达式可以引用实例属性,可以选择使用 this。
以下内容也适用:
a) 目标模板函数的函数原型中应未指定参数方向。这意味着不应使用参数 direction (input,output,inout)。如果函数声明包含参数的方向,则此函数不应具有 target-template 实现。
b) 目标模板声明中指定的原型必须与函数声明中指定的原型匹配,如下所示:
1)参数的数量必须相同。
2)参数名称和类型必须相同。
3)返回类型必须相同。
package aarch64_base_pkg {
target C function void raw_write8(bit[32] addr, bit[8] val) = """
__asm__ __volatile__ ("strb %w0, [%1]" :: "r"({{val}}), "r"({{addr}}));
""";
target ASM function void set_vector_base(void) = """
switch_el x1, 3f, 2f, 1f
3: msr VBAR_EL3, x0
b 0f
2: msr VBAR_EL2, x0
b 0f
1: msr VBAR_EL1, x0
b 0f
0: ret
""";
};
component pss_top {
action write_a {
rand bit[32] addr;
constraint default addr == 0x80000000;
rand bit[8] data;
exec body {
raw_write8(addr, data);
}
}
};