struct union enum
1.定义一个枚举类型port_id_e,使用二值逻辑,指定位宽能够容纳 4 个端口(PORT0, PORT1, PORT2, PORT3),并显式赋值。
typedef enum bit [1:0]{ PORT0 = 2'b00 , PORT1 = 2'b01 , PORT2 = 2'b10 , PORT3 = 2'b11 } 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_access+all -kdb: 开启全部调试权限并生成KDB数据库,这是Verdi能看波形和代码的关键 VCS = vcs -sverilog -debug_access+all -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_id;8 位的字段
data。
module tb_top; typedef enum bit [1:0]{ PORT0 = 2'b00, PORT1 = 2'b01, PORT2 = 2'b10, PORT3 = 2'b11 }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 = 1'b0; my_packet.my_port = PORT0; my_packet.data = 8'h00; #10; my_packet.valid = 1'b1; my_packet.my_port = PORT2; my_packet.data = 8'h5A; #10; my_packet.valid = 1'b0; my_packet.my_port = PORT3; my_packet.data = 8'h3B; $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) begin
a = 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 = '{8'h10, 8'h10, 8'h10}; typedef struct packed{ bit [7:0] r; bit [7:0] g; bit [7:0] b; } pixel_s; pixel_s my_pixel; my_pixel = {8'h10,8'h10,8'h10, };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 = {8'h10,8'h20,8'h30}; $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