Verilog/SystemVerilog中如何将文本读取的字符串替换为对应参数值
问题结论
Verilog和SystemVerilog不存在原生机制可以自动将运行时读取到的标识符字符串,直接映射为编译阶段定义的同名parameter取值。
核心原因是两类操作的生效阶段完全隔离:
parameter是编译、细化(elaboration)阶段就会被固化为常量值的标识符,仿真运行阶段仿真器不会默认保留“标识符名-常量值”的映射关系$fscanf读外部文件是仿真运行时才执行的操作,读到的"L2"本质是ASCII编码的字符序列,不会被自动识别为代码里定义过的参数名
直接把读出来的字符串变量赋值给location,拿到的只会是字符串的二进制编码,绝对不会自动对应到3'b010的参数值。
可行实现方案
最通用的思路是手动加一层字符串到参数值的显式映射,两种常用写法如下:
通用Verilog写法(所有版本兼容)
读入字符串后用case语句做匹配,手动绑定字符串和对应参数值即可,示例代码:
// 预定义寄存器地址参数 parameter L1 = 3'b000; parameter L2 = 3'b010; reg [2:0] location; reg [8*3-1:0] loc_str; // 缓冲区长度按最长标识符长度设置,这里L1/L2都是2字符,留余量设3字符 integer cmd_file; initial begin cmd_file = $fopen("cmd.txt", "r"); // 按输入文件格式解析:跳过"let",读位置字符串,跳过"set"和后续的value字段 $fscanf(cmd_file, "let %s set %*s", loc_str); $fclose(cmd_file); // 显式做字符串到参数值的映射 case(loc_str) "L1": location = L1; "L2": location = L2; default: begin $display("Error: 非法的位置标识符 %s", loc_str); location = 3'bxxx; end endcase // 此时如果输入文件里写的是L2,location就会被赋值为3'b010 end
SystemVerilog简化写法
SV支持string类型和关联数组,可以把映射逻辑写得更简洁易维护:
// 预定义寄存器地址参数 parameter bit [2:0] L1 = 3'b000; parameter bit [2:0] L2 = 3'b010; bit [2:0] location; string loc_str; // 初始化常量查找表,绑定字符串和对应参数值 const bit [2:0] loc_lookup[string] = '{"L1":L1, "L2":L2}; initial begin int cmd_file = $fopen("cmd.txt", "r"); void'($fscanf(cmd_file, "let %s set %*s", loc_str)); $fclose(cmd_file); if(loc_lookup.exists(loc_str)) begin location = loc_lookup[loc_str]; end else begin $fatal(1, "非法的位置标识符 %s", loc_str); end end
注意避坑
不要尝试用`define宏替换实现这个需求:宏是预编译阶段做文本替换的,根本接触不到仿真运行时才从文件里读出来的字符串内容,完全起不到作用。
内容的提问来源于stack exchange,提问作者Nisharg
相关产品推荐
相关产品推荐

