Skip to main content

通用工具函数(LuaUtils)

LuaUtils 是 Verilua 提供的通用工具模块,包含数值转换、位操作、字符串处理、枚举定义、随机数生成、环境变量读取等常用功能。它是验证代码中使用频率最高的基础工具集之一。

初始化

local utils = require "verilua.LuaUtils"

函数概览

数值与进制转换

函数描述
to_hex_str(t, separator?)将整数、cdata 或 table 转为十六进制字符串
to_hex(hex_table)将数值或 table 转为十六进制字符串(空格分隔)
hex_str_to_ull(hex_str)十六进制字符串转 uint64_t
hex_str_to_ll(hex_str)十六进制字符串转 int64_t
num_to_binstr(num)整数转二进制字符串
hex_to_bin(hex_str)十六进制字符串转二进制字符串
bin_str_to_hex_str(bin_str)二进制字符串转十六进制字符串

位操作

函数描述
bitfield32(begin, endd, val)提取 32 位值的指定位域
bitfield64(begin, endd, val)提取 64 位值的指定位域
bitfield_str(str, s, e, width?)从字符串表示的数值中提取位域
bitmask(n)返回 n 位全 1 掩码
reset_bits(value, start, length)将指定范围的位清零
to64bit(hi, lo)将高低 32 位合并为 64 位值
uint_to_onehot(uint_value)将无符号整数转为 one-hot 编码
bitpat_to_hex_str(bitpat_tbl, width?)将位模式表转为十六进制字符串
log2Ceil(n)计算 log₂ 的上取整

随机化

函数描述
rand_int(min, max)[min, max] 范围内生成整数
rand_bool(p?)按概率生成布尔值,p 默认为 0.5
rand_choice(choices, weights?)从非空数组中随机选择一个元素,可传相对权重
urandom64()生成 64 位随机无符号整数
urandom64_range(min, max)[min, max] 范围内生成 64 位随机数
shuffle(t)原地随机打乱 table
shuffle_bits(bitwidth)生成指定位宽的随机值
shuffle_bits_hex_str(bitwidth)生成指定位宽的随机十六进制字符串

字符串处理

函数描述
str_group_by(str, nr_group)将字符串按指定大小分组
str_sep(str, step, separator)每隔 step 个字符插入分隔符
compare_hex_str(str1, str2)比较两个十六进制字符串(忽略前导零和大小写)
compare_value_str(str1, str2)比较两个带前缀的数值字符串

枚举

函数描述
enum_define(enum_table)定义一个可反向查找的枚举
enum_search(t, v)在枚举表中按值查找键名

环境变量

函数描述
get_env_or_else(key, value_type, default)读取环境变量,不存在时返回默认值

控制流与执行

函数描述
execute_after(count, func, options?)在指定调用次数后执行函数
loadcode(code, env?, chunkname?)加载并执行 Lua 代码字符串
exclusive_call(func, lock_file_name?)互斥执行(基于文件锁)

文件与 IO

函数描述
read_file_str(filename)读取文件全部内容为字符串
get_scriptdir()获取调用者脚本所在的绝对目录路径
iorun(cmd)执行命令并返回输出

其他

函数描述
serialize(t, conn?)将 table 序列化为字符串
reverse_table(tab)反转数组
deepcopy(value)深拷贝 table,支持循环引用并保留 metatable
get_datetime_str()获取当前日期时间字符串
get_progress_bar(progress, length)生成文本进度条
cover_with_n(value, n)计算覆盖 value 需要多少个 n
report_luacov()生成 luacov 覆盖率报告

详细说明

数值与进制转换

to_hex_str(t, separator?)

将整数、uint64_t、cdata 多拍数据或 table 转为十六进制字符串。对于多拍数据,按 MSB → LSB 顺序拼接。

local utils = require "verilua.LuaUtils"

-- 普通整数
print(utils.to_hex_str(255)) --> "ff"

-- uint64_t
local val = 0x1234567890abcdefULL
print(utils.to_hex_str(val)) --> "1234567890abcdef"

-- table(多拍数据;按 MSB → LSB 输出,即反向拼接数组元素)
print(utils.to_hex_str({0xdead, 0xbeef}, "_")) --> "0000beef_0000dead"

hex_str_to_ull(hex_str) / hex_str_to_ll(hex_str)

将十六进制字符串转为 64 位整数。hex_str_to_ull 返回无符号,hex_str_to_ll 返回有符号。超过 64 位时截断低 64 位。

local val = utils.hex_str_to_ull("ffffffffffffffff")
print(val) --> 18446744073709551615ULL

local signed = utils.hex_str_to_ll("ffffffffffffffff")
print(signed) --> -1LL

hex_to_bin(hex_str) / bin_str_to_hex_str(bin_str)

十六进制与二进制字符串互转。输入不含 0x / 0b 前缀。

print(utils.hex_to_bin("a5")) --> "10100101"
print(utils.bin_str_to_hex_str("1010")) --> "a"

位操作

bitfield32(begin, endd, val) / bitfield64(begin, endd, val)

提取 val[begin, endd] 范围的位域。bitfield32 返回 Lua number,bitfield64 返回 uint64_t

local val = 0xDEADBEEF
print(string.format("%x", utils.bitfield32(0, 15, val))) --> "beef"
print(string.format("%x", utils.bitfield32(16, 31, val))) --> "dead"

bitfield_str(str, s, e, width?)

从字符串表示的数值中提取位域,支持 0x(十六进制)、0b(二进制)和无前缀十进制。返回二进制字符串。

local bits = utils.bitfield_str("0xAB", 0, 3)
print(bits) --> "1011" (低 4 位)

local bits2 = utils.bitfield_str("0b11001010", 2, 5)
print(bits2) --> "0010"

bitmask(n)

返回低 n 位全为 1 的掩码(uint64_t)。

print(utils.bitmask(4)) --> 0xfULL
print(utils.bitmask(64)) --> 0xffffffffffffffffULL

reset_bits(value, start, length)

value 中从 start 位开始的 length 个位清零。

local result = utils.reset_bits(0xFF, 4, 4)
print(string.format("%x", tonumber(result))) --> "f"

bitpat_to_hex_str(bitpat_tbl, width?)

将一组位模式(每个指定起始位 s、结束位 e 和值 v)合并为一个十六进制字符串。适用于构造寄存器值。

local hex = utils.bitpat_to_hex_str({
{ s = 0, e = 7, v = 0xAB },
{ s = 8, e = 15, v = 0xCD },
{ s = 16, e = 23, v = 0xEF },
}, 32)
print(hex) --> "0000000000efcdab"

uint_to_onehot(uint_value)

将无符号整数转为 one-hot 编码。

print(string.format("%x", tonumber(utils.uint_to_onehot(0)))) --> "1"
print(string.format("%x", tonumber(utils.uint_to_onehot(3)))) --> "8"

随机化

rand_int(min, max) / rand_bool(p?) / rand_choice(choices, weights?)

轻量级随机 helper,使用全局 math.random。在 Verilua 仿真环境中,全局随机种子会由 cfg.seed 或环境变量 SEED 初始化。rand_choiceweights 是可选的相对权重数组,允许 0 权重,但总权重必须大于 0。

local depth = utils.rand_int(1, 16) -- 1 ~ 16
local enable_delay = utils.rand_bool(0.25) -- 25% 概率为 true
local mode = utils.rand_choice({ "a", "b" }) -- 从非空数组中选择
local biased = utils.rand_choice({ "a", "b", "c" }, { 10, 1, 0 })

urandom64() / urandom64_range(min, max)

生成 64 位随机数。urandom64_range[min, max] 闭区间内均匀分布。

local r = utils.urandom64()
local r2 = utils.urandom64_range(100ULL, 200ULL)

shuffle(t)

Fisher-Yates 算法原地打乱数组。

local t = {1, 2, 3, 4, 5}
utils.shuffle(t)
-- t 现在是随机排列

shuffle_bits(bitwidth) / shuffle_bits_hex_str(bitwidth)

生成指定位宽范围内的随机值。shuffle_bits 返回 uint64_tshuffle_bits_hex_str 返回十六进制字符串。

local val = utils.shuffle_bits(16) -- 0 ~ 0xFFFF 之间的随机值
local hex = utils.shuffle_bits_hex_str(128) -- 128 位随机十六进制字符串

枚举

enum_define(enum_table)

定义一个枚举,支持正向访问(名称 → 值)和反向查找(值 → 名称)。访问不存在的键会触发错误。

local State = utils.enum_define {
name = "State",
IDLE = 0,
RUNNING = 1,
DONE = 2,
}

-- 正向访问
print(State.IDLE) --> 0
print(State.RUNNING) --> 1

-- 反向查找
print(State(0)) --> "IDLE"
print(State(1)) --> "RUNNING"

-- 访问不存在的键会报错
-- State.UNKNOWN --> error: Unknown enum value => UNKNOWN enum name => State

环境变量

get_env_or_else(key, value_type, default)

读取环境变量并转换为指定类型。如果环境变量未设置,返回 default 并打印警告日志。default 也可以是一个无参数函数;这种情况下会调用一次函数、检查返回值类型,并用 info 日志记录生成值。

  • value_type"string" | "boolean" | "number" | "integer"
  • "integer" 会严格要求值是无小数的 Lua number。
  • 布尔值识别:true/True/TRUE/1/ENABLE/ON/enable/ontruefalse/False/FALSE/0/DISABLE/OFF/disable/offfalse
local timeout = utils.get_env_or_else("VL_TIMEOUT", "number", 1000)
local verbose = utils.get_env_or_else("VL_VERBOSE", "boolean", false)
local name = utils.get_env_or_else("VL_NAME", "string", "default")

local outstanding = utils.get_env_or_else("VL_OUTSTANDING", "integer", function()
return utils.rand_int(1, 16)
end)

控制流与执行

execute_after(count, func, options?)

返回一个闭包,该闭包在被调用 count 次后执行 func

  • 默认只执行一次
  • options._repeat = true:每隔 count 次重复执行
  • options.times = N:执行 N 次后停止
local trigger = utils.execute_after(3, function()
print("triggered!")
end)

trigger() -- 无输出
trigger() -- 无输出
trigger() -- 输出: triggered!
trigger() -- 无输出(已完成)

-- 重复模式
local repeater = utils.execute_after(2, function()
print("tick")
end, { _repeat = true })

repeater() -- 无输出
repeater() -- 输出: tick
repeater() -- 无输出
repeater() -- 输出: tick

exclusive_call(func, lock_file_name?)

通过文件锁实现互斥执行,确保同一时刻只有一个线程/进程执行 func。常用于多进程仿真中的共享资源保护。

utils.exclusive_call(function()
-- 这里的代码在同一时刻只有一个进程执行
local f = io.open("shared_result.txt", "a")
f:write("result line\n")
f:close()
end, "my_lock_file")

loadcode(code, env?, chunkname?)

加载并执行 Lua 代码字符串,返回执行结果。可选设置执行环境。

local add = utils.loadcode("return function(a, b) return a + b end")
print(add(1, 2)) --> 3

文件与 IO

read_file_str(filename)

读取文件全部内容为字符串。路径会自动转为绝对路径。

local content = utils.read_file_str("config.txt")

get_scriptdir()

获取调用者脚本所在的绝对目录路径。类似于 xmake 的 os.scriptdir(),用于定位当前脚本的相对资源。

local utils = require "verilua.LuaUtils"

-- 假设当前脚本为 /home/user/project/tests/test_foo.lua
local dir = utils.get_scriptdir()
print(dir) --> "/home/user/project/tests"

-- 常见用法:拼接相对路径
local data_file = dir .. "/data/input.bin"

iorun(cmd)

执行 shell 命令并返回输出(stdout 或 stderr)。

local output = utils.iorun("ls -la")
print(output)

其他工具

compare_hex_str(str1, str2) / compare_value_str(str1, str2)

比较两个数值字符串是否相等,忽略前导零和大小写。compare_value_str 支持 0x0b 前缀和十进制。

print(utils.compare_hex_str("001A", "1a")) --> true
print(utils.compare_value_str("0x0ff", "0xFF")) --> true
print(utils.compare_value_str("0b0011", "0b11")) --> true

cover_with_n(value, n)

计算覆盖 value 需要多少个大小为 n 的组。等价于 ceil(value / n)

print(utils.cover_with_n(10, 4)) --> 3 (4+4+2 覆盖 10)
print(utils.cover_with_n(8, 4)) --> 2

deepcopy(value)

递归深拷贝 table 的 key 和 value,支持循环引用,拷贝后的 table 会保留原始 metatable。非 table 值会原样返回。

local src = { nested = { value = 1 } }
src.self = src

local copied = utils.deepcopy(src)
print(copied ~= src) --> true
print(copied.nested.value) --> 1
print(copied.self == copied) --> true

get_progress_bar(progress, length)

生成文本进度条字符串。progress 范围为 (0, 1)length 为进度条字符长度。

print(utils.get_progress_bar(0.5, 20))
-- ┃██████████▉▒▒▒▒▒▒▒▒▒▒┃

report_luacov()

生成 luacov 覆盖率报告。需要在代码开头 require "luacov",并在仿真结束时调用。内部使用 exclusive_call 保证多进程安全。

-- 在代码开头
require "luacov"

-- 在仿真结束时
final {
function()
local utils = require "verilua.LuaUtils"
utils.report_luacov()
end
}

与其他模块的关系

  • StrBitsUtilsLuaUtils 中的 hex_to_binbin_str_to_hex_str 等提供基础转换,而 StrBitsUtils 提供更完整的字符串位操作(移位、逻辑运算、位域设置等),适合处理超大位宽数据。
  • BitVecBitVec 基于数组存储,适合频繁读写的信号值操作。LuaUtils 的转换函数可用于准备 BitVec 的输入数据。
  • SymbolHelperLuaUtils.iorun 内部依赖 SymbolHelper 获取 C 函数指针。

注意事项

  • bitfield32 / bitfield64 的位索引遵循 LSB 0 规则(最低位为 0)。
  • to_hex_str 对 cdata 多拍数据的输出顺序为 MSB → LSB。
  • urandom64 系列函数基于 math.random,如需可重复的随机序列,请先调用 math.randomseed
  • exclusive_call 依赖 C 层的 acquire_lock / release_lock,仅在 Verilua 运行时环境中可用。
  • iorun 依赖 Verilua 运行时提供的 C 函数,不能在纯 LuaJIT 环境中使用。