
1.定义一个枚举类型port_id_e使用二值逻辑指定位宽能够容纳 4 个端口PORT0, PORT1, PORT2, PORT3并显式赋值。typedef enum bit [1:0]{ PORT0 2b00 , PORT1 2b01 , PORT2 2b10 , PORT3 2b11 } port_id_e; port_id_e my_port; initial begin my_port PORT1; $display([TB_MSG] Current active port is:%s(value %0d),my_port.name(),my_port); endmy_port.name(),SV 内置的枚举方法。Makefile模板# # 标准基础 Makefile 模板 # # 1. 定义当前要测试的源文件下次你换别的文件只需要改这一个地方 FS_SRC enum_1.sv # 2. 定义工具链命令与参数 # -sverilog: 支持系统Verilog语法 # -debug_accessall -kdb: 开启全部调试权限并生成KDB数据库这是Verdi能看波形和代码的关键 VCS vcs -sverilog -debug_accessall -kdb -l vcs.log VERDI verdi -sv $(FS_SRC) -ssf tb_top.fsdb .PHONY: compile sim wave clean all # 默认执行编译和仿真 all: compile sim # 编译步骤 compile: $(VCS) $(FS_SRC) # 仿真运行步骤 sim: ./simv -l sim.log # 一键拉起 Verdi wave: $(VERDI) # 清理垃圾文件 clean: rm -rf csrc simv* *.log *.fsdb ucli.key verdiLog Novas*my_port.name()打印出来的是带有可读性的文本定义一个合并结构体typedef struct packed名为packet_s里面包含三个成员1 位的有效信号valid你刚刚定义的枚举类型port_id8 位的字段data。module tb_top; typedef enum bit [1:0]{ PORT0 2b00, PORT1 2b01, PORT2 2b10, PORT3 2b11 }port_id_e; typedef struct packed{ bit valid; port_id_e my_port; bit [7:0] data; }packet_s; packet_s my_packet; initial begin $display(simulation started); my_packet.valid 1b0; my_packet.my_port PORT0; my_packet.data 8h00; #10; my_packet.valid 1b1; my_packet.my_port PORT2; my_packet.data 8h5A; #10; my_packet.valid 1b0; my_packet.my_port PORT3; my_packet.data 8h3B; $display([TB_INFO] my_port is %s (num %0d) data 0x%h, my_packet.my_port.name(), my_packet.my_port, my_packet.data ); $display([TB_INFO]my_packet raw bits stream %b,my_packet); #10; $display(simulation finished); $finish; end initial begin $fsdbDumpfile(tb_top.fsdb); $fsdbDumpvars(0,tb_top); end endmodule3.always (posedge clk) begina a 1;$display(Display a %0d, a);$strobe (Strobe a %0d, a);end$display和$strobe打印出来的a的值会是一样的吗如果不一样哪个打印出的是旧值哪个打印出的是变化后的新值为什么这个内容涉及到了 Verilog/SV 的仿真调度事件队列Event Queue是验证工程师必懂的底层逻辑。不一样,$display打印出来的是旧值或者是中间变化值。$strobe打印出来的是变化后的新值最终稳定值。代码写得不符合硬件规范,在时钟沿触发的always块里工业界标准必须使用非阻塞赋值a a 1;。1.Active区域在 Active 区域非阻塞赋值只会“右边采样”它知道a将要变成 1但还没真正塞给左边。此时执行$display它去内存里读读到的依然是旧值0。NBA 区域Nonblocking Assignment, 非阻塞赋值更新区域如果用了a a 1;仿真器在这个区域才会真正把1赋给a。Postponed 区域延后区域这是整个时间步Time-slot的最后最后一步。无论前面是用阻塞赋值还是非阻塞赋值无论中间经历了多少次抖动$strobe都会冷眼旁观,直到这个时间点要结束的最后一刻它才去瞄一眼内存把最终稳定下来的新值1打印出来。当我们在时钟沿(posedge clk)想要观察一组信号稳定后的最终状态时必须且只能使用$strobe。它能完美避开任何仿真调度导致的竞争确保你看到的和波形图上稳定后的值绝对一致。结构体和合并结构体代码typedef struct { bit [7:0] r; bit [7:0] g; bit [7:0] b; } pixel_s; pixel_s my_pixel; my_pixel {8h10, 8h10, 8h10}; typedef struct packed{ bit [7:0] r; bit [7:0] g; bit [7:0] b; } pixel_s; pixel_s my_pixel; my_pixel {8h10,8h10,8h10, };module tb_top; typedef struct packed{ bit [7:0] r ; bit [7:0] g ; bit [7:0] b ; } pixel_s; pixel_s my_pixel; initial begin #10; my_pixel {8h10,8h20,8h30}; $display([TB_INFO] r%0h,g%0h,b%0h,my_pixel.r,my_pixel.g,my_pixel.b); #10; $finish; end initial begin $fsdbDumpfile(tb_top.fsdb); $fsdbDumpvars(0,tb_top); end endmodule