Presentation is loading. Please wait.

Presentation is loading. Please wait.

第14章 对验证的支持 学习内容 理解Verilog文本输出 理解不同的读取仿真时间的系统函数 理解 Verilog文件I/O功能.

Similar presentations


Presentation on theme: "第14章 对验证的支持 学习内容 理解Verilog文本输出 理解不同的读取仿真时间的系统函数 理解 Verilog文件I/O功能."— Presentation transcript:

1 第14章 对验证的支持 学习内容 理解Verilog文本输出 理解不同的读取仿真时间的系统函数 理解 Verilog文件I/O功能

2 验证系统中的任务(task)及函数(function)
Verilog读取当前仿真时间的系统函数 $time $stime $realtime Verilog支持文本输出的系统任务: $display $strobe $write $monitor

3 仿真时间 访问仿真时间 $time,$realtime,和$stime函数返回当前仿真时间。
这些函数的返回值使用调用模块中`timescale定义的时间单位 $time返回一个64位整数时间值。 $stime返回一个32位整数时间值。 $realtime返回一个实数时间值。 $stime函数返回一个32位整数时间值。对大于232的时间,返回模232的值。使用它可以节省显示及打印空间。

4 输出格式化时间信息 若使用多个`timescale,以最小的时间精度显示时间值。
可用系统任务$timeformat结合格式符%t全局控制时间显示方式。 $timeformat系统任务的语法为: $timeformat(<unit>,<precision>,<suffix>,<min_width>); `timescale 10ns / 100ps module top; reg in1; not m1( o1, in1); initial begin $timeformat(-9, 2, "ns", 10); in1 = 0; #8 in1 = 1; #10 $display("%t %b %b", $realtime, in1, o1); #10 $finish; end endmodule unit:0(s)到-15(fs)之间的整数,表示时间度量 precision:要显示的十进制小数位数。 suffix:在时间值后显示的字符串 min_width:显示前三项的最小宽度 在这个例子中,显示的时间为: ns

5 输出格式化时间信息 time realtime stime in1 o1 `timescale 1 ns / 10 ps
ns x ns ns ns `timescale 1 ns / 10 ps module top; reg in1; not #9.53 n1 (o1, in1); initial begin $display("time realtime stime \t in1 \t o1 "); $timeformat(-9, 2, "ns", 10); $monitor("%d %t %d \t %b \t %b", $time, $realtime, $stime, in1, o1); in1 = 0; #10 in1 = 1; #10 $finish; end endmodule

6 输出格式化时间信息 对#延迟,Verilog将延迟值舍入最近(四舍五入)时间精度值。 例如,上面的例子修改为:
`timescale 1ns/ 100ps not #9.49 n1 (o1, in1); 结果为: time realtime stime in1 o1 ns x ns ns ns `timescale 1ns/ 100ps not #9.42 n1 (o1, in1); 结果为: time realtime stime in1 o1 ns x ns ns ns

7 显示信号值 — $display %h %o %d %b %c %s %v %m %t
语法:$display([“ format_specifiers”,] <argument_ list>) $display输出时自动换行。 $display ($ time, “%b \t %h \t %d \t %o”, sig1, sig2, sig3, sig4); $display ($ time, “%b \t”, sig1, “%h \t”, sig2, “% d \t”, sig3, “%o”, sig4); $display支持二进制、八进制、十进制和十六进制。缺省基数为十进制。 $display (sig1, sig2, sig3, sig4); $displayb (sig1, sig2, sig3, sig4); $displayo (sig1, sig2, sig3, sig4); $displayh (sig1, sig2, sig3, sig4); 格式符 %h %o %d %b %c %s %v %m %t hex octal decimal binary ASCII string strength module time 转义符 \t \n \\ \" \< 1-3 digit octal number> %0d tab 换行 反斜杠 双引号 上述的ASCII表示 无前导0的十进制数

8 显示信号值—$write和$strobe
$write与$display相同,不同的是不会自动换行。 $write($time, “%b \t %h \t %d \t %o \t”, sig1, sig2, sig3, sig4); $strobe与$display相同,不同的是在仿真时间前进之前的信号值。而$display和$write立即显示信号值。也就是说$strobe显示稳定状态信号值,而$display和$write可以显示信号的中间状态值。 $strobe($time, “%b \t %h \t %d \t %o \t”, sig1, sig2, sig3, sig4); $write和$strobe都支持多种数基,缺省为十进制。 $writeb $strobeb $writeo $strobeo $writeh $strobeh

9 显示信号值—$write和$strobe
下面是模块textio仿真的输出: $writeb输出: 0 xxxxxxxx x 注意data是32位数据,由8位十六进制数表示。时间以没有前导零的十进制形式输出。 缺省情况下,值以十进制显示,忽略前导零,与%0d格式符相同。可以在一个格式化符前插入一个0使Verilog忽略开头的零。 $displayh: f 注意当前时间,一个64位量,需要16个十六进制的数。 $display: $strobe: module textio; reg flag; reg [31: 0] data; initial begin $writeb("%d", $time, ,"%h \t", data, , flag, "\n"); #15 flag = 1; data = 16; $displayh($time, ,data, , flag); end #10 data = 20; $strobe($time, , data); $display($time, , data); data = 30; endmodule

10 监视信号值—$monitor $monitor持续监视参数列表中的变量。
可以用系统任务$monitoron和$monitoroff控制持续监视。 $monitor支持多种基数。缺省为十进制。 $monitor ($ time, “%b \t %h \t %d \t %o”, sig1, sig2, sig3, sig4);

11 监示信号值—$monitor $monitor是唯一的不断输出信号值的系统任务。其它系统任务在返回值之后就结束。
$monitor和$strobe一样,显示参数列表中信号的稳定状态值,也就是在仿真时间前进之前显示信号。在一个时间步中,参数列表中信号值的任何变化将触发$monitor 。但$time,$stime,$realtime不能触发。 任何后续的$monitor覆盖前面调用的$monitor。只有新的$monitor的参数列表中的信号被监视,而前面的$monitor的参数则不被监视。 可以用$monitoron和$monitoroff系统任务控制持续监视,使用户可以在仿真时只监视特定时间段的信号。 $monitor参数列表的形式与$display相同。 $monitor支持多种基数。缺省为十进制。 $monitorb $monitoro $monitorh

12 文件输出 $fopen打开一个文件并返回一个多通道描述符(MCD)。 以$f开始的显示系统任务将输出写入与MCD相对应的文件中。 . . .
integer MCD1; MCD1 = $fopen("<name_of_file>"); $fdisplay( MCD1, P1, P2, .., Pn); $fwrite( MCD1, P1, P2, .., Pn); $fstrobe( MCD1, P1, P2, .., Pn); $fmonitor( MCD1, P1, P2, .., Pn); $fclose( MCD1); $fopen打开一个文件并返回一个多通道描述符(MCD)。 MCD是与文件唯一对应的32位无符号整数。 如果文件不能打开并进行写操作,MCD将等于0。 如果文件成功打开,MCD中的一位将被置位。 以$f开始的显示系统任务将输出写入与MCD相对应的文件中。

13 文件输出 $fopen打开参数中指定的文件并返回一个32位无符号 整数MCD,MCD是与文件一一对应的多通道描述符。如果文件不能打开并进行写操作,它返回0。 $fclose关闭MCD指定的通道。 输出信息到log文件和标准输出的四个格式化显示任务($display, $write, $monitor, $strobe)都有相对应的任务用于向指定文件输出。 这些对应的任务($fdisplay,$fwrite,$fmonitor,$fstrobe)的参数形式与对应的任务相同,只有一个例外:第一个参数必须是一个指定向何哪个文件输出的MCD。MCD可以是一个表达式,但其值必须是一个32位的无符号整数。这个值决定了该任务向哪个打开的文件写入。 MCD可以看作由32个标志构成的组,每个标志代表一个单一的输出通道。

14 文件输出 . . . integer messages, broadcast, cpu_chann, alu_chann; initial
begin cpu_chann = $fopen(" cpu.dat"); if(! cpu_chann) $finish; alu_chann = $fopen(" alu.dat"); if(! alu_chann) $finish; // channel to both cpu. dat and alu. dat messages = cpu_chann | alu_chann; // channel to both files, standard out, and verilog. log broadcast = 1 | messages; end posedge clock) // print the following to alu. dat $fdisplay( alu_chann, "acc= %h f=%h a=%h b=%h", acc, f, a, b); /* at every reset print a message to alu. dat, cpu. dat, standard output and the verilog. log file */ negedge reset) $fdisplay( broadcast, "system reset at time %d", $time); 必须声明为integer 通道0(编号为1)为标准输出及verilog.log

15 文件输入 Verilog中有两个系统任务可以将数据文件读入寄存器组。一个读取二进制数据,另一个读取十六进制数据: $readmemb
$readmemb ("file_name", <memory_name>); $readmemb ("file_name", <memory_name>, <start_addr>); $readmemb ("file_name", <memory_name>, <start_addr>, <finish_addr>); $readmemh $readmemh (" file_name", <memory_name>); $readmemh (" file_name", <memory_name>, <start_addr>); $readmemh (" file_name", <memory_name>, <start_addr>, <finish_addr>);

16 文件输入 系统任务$readmemb和$readmemh从一个文本文件读取数据并写入存储器。
filename指定要调入的文件。 mem_name指定存储器名。 start和finish决定存储器将被装载的地址。Start为开始地址,finish为结束地址。如果不指定开始和结束地址,$readmem按从低端开始读入数据,与说明顺序无关。

17 文件输入 $readmemb和$readmemh的文件格式 : $readmemb("mem_file. txt", mema);
声明的存储器组 reg [0:7] mema[0:1023] 文本文件:mem_file.txt 0000_0000 0110_ _0010 // 地址3~255没有定义 @100 // hex 1111_1100 //地址257~1022没有定义 @3FF 1110_0010 1 3 256 1023 module readmem; reg [0:7] mema [0:1023] initial $readmemb(“mem_file.txt”, mema); endmodule

18 文件输入 可以指定二进制(b)或十六进制(h)数 用下划线(_)提高可读性。 可以包含单行或多行注释。 可以用空格和换行区分存储器字。
$readmemb和$readmemh的文件格式 : $readmemb("mem_file. txt", mema); 可以指定二进制(b)或十六进制(h)数 用下划线(_)提高可读性。 可以包含单行或多行注释。 可以用空格和换行区分存储器字。 可以给后面的值设定一个特定的地址,格式为: @(hex_address) 十六进制地址的大小写不敏感。

19 复习 问题: 哪个系统任务显示参数列表中信号的稳定状态值? 每次能打开多少个输出文件? 解答:
系统任务$monitor和$strobe显示参数列表中信号的稳定状态值。这些任务在时间前进之前输出信号值。 每次只能打开一个输出文件,包括已由仿真器打开的任何log文件。

20 第十五章 Verilog Test Bench使用简介
学习内容: 用一个复杂的test bench复习设计的组织与仿真 建立test bench通常使用的编码风格及方法

21 设计组织 虚线表示编译时检测输入文件是否存在及可读并允许生成输出文件。

22 test bench组织 stimulus 简单的test bench向要验证的设计提供向量,人工验证输出。
激励 验证结果 要验证的设计 复杂的test bench 简单的test bench向要验证的设计提供向量,人工验证输出。 复杂的test bench是自检测的,其结果自动验证。

23 并行块 fork…join块在测试文件中很常用。他们的并行特性使用户可以说明绝对时间,并且可以并行的执行复杂的过程结构,如循环或任务。
module inline_ tb; reg [7: 0] data_ bus; // instance of DUT initial fork data_bus = 8'b00; #10 data_bus = 8'h45; #20 repeat (10) #10 data_bus = data_bus + 1; #25 repeat (5) #20 data_bus = data_bus << 1; #140 data_bus = 8'h0f; join endmodule Time | data_ bus | 8’b0000_0000 | 8’b0100_0101 | 8’b0100_0110 | 8’b0100_0111 | 8’b1000_1110 | 8’b1000_1111 | 8’b1001_0000 | 8’b0010_0000 | 8’b0010_0001 | 8’b0010_0010 | 8’b0100_0100 | 8’b0100_0101 | 8’b0100_0110 | 8’b1000_1100 | 8’b1000_1101 | 8’b1000_1110 | 8’b0001_1100 | 8’b0000_1111 上面的两个repeat循环从不同时间开始,并行执行。象这样的特殊的激励集在单个的begin…end块中将很难实现。

24 包含文件 包含文件用于读入代码的重复部分或公共数据。 module clk_gen (clk); output clk; reg clk;
`include "common.txt" initial begin while ($ time < sim_end) begin clk = initial_clock; #(period/2) clk = !initial_clock; #(period/2); end $finish; endmodule // common. txt // clock and simulator constants parameter initial_clock = 1; parameter period = 15; parameter max_cyc = 100; parameter sim_end = period * max_cyc 在上面的例子中,公共参数在一个独立的文件中定义。此文件在不同的仿真中可被不同的测试文件调用。

25 施加激励 产生激励并加到设计有很多 种方法。一些常用的方法有: 从一个initial块中施加线激励 从一个循环或always块施加激励
从一个向量或整数数组施加激励 记录一个仿真过程,然后在另一个仿真中回放施加激励

26 线性激励 线性激励有以下特性: 只有变量的值改变时才列出 易于定义复杂的时序关系
对一个复杂的测试,测试基准(test bench)可能非常大 module inline_ tb; reg [7: 0] data_ bus, addr; wire [7: 0] results; DUT u1 (results, data_ bus, addr); initial fork data_bus = 8'h00; addr = 8'h3f; #10 data_ bus = 8'h45; #15 addr = 8'hf0; #40 data_ bus = 8'h0f; #60 $finish; join endmodule

27 循环激励 从循环产生激励有以下特性: 在每一次循环,修改同一组激励变量 时序关系规则 代码紧凑 module loop_tb;
reg clk; reg [7:0] stimulus; wire [7:0] results; integer i; DUT u1 (results, stimulus); always begin // clock generation clk = 1; #5 clk = 0; #5 end initial begin for (i = 0; i < 256; i = i + 1) @( negedge clk) stimulus = i; #20 $finish; endmodule

28 数组激励 从数组产生激励有以下特性: 在每次反复中,修改同一组激励变量 激励数组可以直接从文件中读取 module array_ tb;
reg [7: 0] data_ bus, stim_ array[ 0: 15]; // 数组 integer i; DUT u1 (results, stimulus); initial begin // 从数组读入数据 #20 stimulus = stim_array[0]; #30 stimulus = stim_array[15]; // 线激励 #20 stimulus = stim_array[1]; for (i = 14; i > 1; i = i - 1) // 循环 #50 stimulus = stim_array[i] ; #30 $finish; end endmodule

29 矢量采样 在仿真过程中可以对激励和响应矢量进行采样,作为其它仿真的激励和期望结果。 module capture_tb;
parameter period = 20 reg [7:0] in_vec, out_vec; integer RESULTS, STIMULUS; DUT u1 (out_ vec, in_ vec); initial begin STIMULUS = $fopen("stimulus. txt") ; RESULTS = $fopen("results. txt") ; fork if (STIMULUS != 0 ) forever #( period/2) $fstrobeb (STIMULUS, "%b", in_vec); if (RESULTS != 0 ) #( period/2) forever #( period/2) $fstrobeb (RESULTS, "%b", out_vec); join end endmodule

30 矢量回放 保存在文件中的矢量反过来可以作为激励 使用矢量文件输入/输出的优点: 激励修改简单 设计反复验证时直接使用工具比较矢量文件。
module read_file_tb; parameter num_vecs = 256; reg [7:0] data_bus; reg [7:0] stim [num_vecs-1:0]; integer i; DUT u1 (results, data_bus) initial begin // Vectors are loaded $readmemb ("vec. txt", stim); for (i =0; i < num_vecs ; i = i + 1) #50 data_bus = stim[i]; end endmodule // 激励文件vec.txt . 使用矢量文件输入/输出的优点: 激励修改简单 设计反复验证时直接使用工具比较矢量文件。

31 错误及警告报告 使用文本或文件输出类的系统任务报告错误及警告 一个更为复杂的test bench可以:
posedge par_err) $display (" error-bus parity errors detected"); posedge cor_err) $display("warning-correctable error detected"); 一个更为复杂的test bench可以: 不但能报告错误,而能进行一些动作,如取消一个激励块并跳转到下一个激励。 在内部保持错误跟踪,并在每次测试结束时产生一个错误报告。

32 强制激励 在过程块中,可以用两种持续赋值语句驱动一个值或表达式到一个信号。 过程持续赋值通常不可综合,所以它们通常用于测试基准描述。
对每一种持续赋值,都有对应的命令停止信号赋值。 不允许在赋值语句内部出现时序控制。 对一个寄存器使用assign和deassign,将覆盖所有其他在该信号上的赋值。这个寄存器可以是RTL设计中的一个节点或测试基准中在多个地方赋值的信号等。 initial begin #10 assign top.dut.fsm1.state_reg = `init_state ; #20 deassign top.dut.fsm1.state_reg ; end 在register和net上(例如一个门级扫描寄存器的输出)使用force和release,将覆盖该信号上的所有其他驱动。 #10 force top. dut. counter. scan_ reg. q = 0 ; #20 release top. dut. counter. scan_ reg. q ;

33 强制激励 在上面两个例子中,在 net或register上所赋的常数值,覆盖所有在时刻10和时刻20之间可能发生在该信号上的其他任何赋值或驱动。如果所赋值是一个表达式,则该表达式将被持续计算。 可以强制(force)并释放一个信号的指定位、部分位或连接,但位的指定不能是一个变量(例如out_vec[i]) 不能对register的一位或部分位使用assign和deassign 对同一个信号,force覆盖assign。 后面的assign或force语句覆盖以前相同类型的语句。 如果对一个信号先assign然后force,它将保持force值。在对其进行release后,信号为assign值。 如果在一个信号上force多个值,然后release该信号,则不出现任何force值。

34 建立时钟 例1:虽然有时候在设计中给出时钟,但通常时钟是测试基准中建立。 下面介绍如何产生不同的时钟波形。同时给出用门级和行为级描述方法
下面是一个简单对称时钟的例子: reg go; wire ck; nand #( period/2) u1 (ck, ck, go); initial begin go = 0; #( period/2) go = 1; end reg ck; always begin #( period/2) ck = 0; #( period/2) ck = 1; end 产生的波形(假定period为20) 注意:在一些仿真器中,时钟与设计使用相同的抽象级描述时,仿真性能会好一些。

35 建立时钟 例2:有启动延时的对称时钟的例子: reg ck; initial begin ck = 0; #( period)
forever #( period/2) ck = !ck; end reg go; wire ck; nand #( period/2) u1 (ck, ck, go); initial begin go = 0; #(period) go = 1; end 产生的波形(假定period为20) 注意:在行为描述中,在时间0将CK初始化为0;而在结构描述中,直到period/2才影响CK值。当go信号在时间0初始化时,CK值到period/2才变化。可以使用特殊命令force和release立即影响CK值。

36 建立时钟 例3:有不规则启动延时的不对称时钟的例子: reg ck; initial begin #(period + 1) ck = 1;
forever begin #(period/4) ck = 0; #(3*period/4) ck = 1; end reg go; wire ck; nand #(3*period/4, period/4) u1(ck, ck, go); initial begin #(period/4 + 1) go = 0; #(5*period/4 – 1) go = 1; end 产生的波形(假定period为20) 注意:在行为描述中,CK值立刻被影响;而在结构描述中,在传播延时后才输出正确波形。

37 使用task 在test bench中使用task可以压缩重复操作,提高代码效率。 task cpu_driver;
input [7:0] data_in; begin #30 data_valid = 1; wait (data_rd == 1); #20 data = data_ in; wait (data_rd == 0); #20 data = 8'hzz; #30 data_valid = 0; end endtask endmodule module bus_ctrl_tb; reg [7: 0] data; reg data_valid, data_rd; cpu u1 (data_valid, data,data_rd); initial begin cpu_driver (8'b0000_0000); cpu_driver (8'b1010_1010); cpu_driver (8'b0101_0101); end

38 使用task 产生的波形

39 复习 问题: 什么操作可以容易的在fork…join块做到,而不容易在begin…end块做到? 通常怎样产生规则激励和不规则激励?
从一个文件中读取激励时使用什么数据类型? 在行为级时钟模型中能做哪些在门级时钟模型中很难或不能作到的事? 解答: fork…join块中不但能够赋值,还可以并行执行循环、条件语句、任务或函数调用。 循环或always块能有效地产生规则激励,不规则激励适合用在initial块产生。 用寄存器组(存储器)并用$readmem系统任务从一个文件以读取向量。 行为级代码可以很容易地产生一个启动时间不规则的时钟波形,并且可以在时刻零初始化时钟。

40 第16章 存储器建模 学习内容: 如何描述存储器 如何描述双向端口

41 存储器件建模 描述存储器必须做两件事: 说明一个适当容量的存储器。 提供内容访问的级别,例如: 只读 读和写 写同时读
多个读操作,同时进行单个写操作 同时有多个读和多个写操作,有保证一致性的方法

42 简单ROM描述 下面的ROM描述中使用二维寄存器组定义了一个存储器mem。ROM的数 据单独保存在文件my_rom_data中,如右边所示。通常用这种方法使ROM 数据独立于ROM描述。 my_rom_data 0000 0101 1100 0011 1101 0010 1111 1000 1001 0001 1010 `timescale 1ns/10ps module myrom (read_data, addr, read_en_); input read_en_; input [3:0] addr; output [3:0] read_data; reg [3:0] read_data; reg [3:0] mem [0:15]; initial $readmemb ("my_rom_data", mem); addr or read_en_) if (! read_en_) read_data = mem[addr]; endmodule

43 简单的RAM描述 RAM描述比ROM略微复杂,因为必须既有读功能又有写功能,而读写通常使用同一数据总线。这要求使用新的处理双向数据线的建模技术。在下面的例子中,若读端口未使能,则模型不驱动数据总线;此时若数据总线没有写数据驱动,则总线为高阻态Z。这避免了RAM写入时的冲突。 `timescale 1ns /1ns module mymem (data, addr, read, write); inout [3:0] data; input [3:0] addr; input read, write; reg [3:0] memory [0:15]; // 16*4 // 读 assign data = read ? memory[addr] : 4'bz; // 写 posedge write) memory[addr] = data; endmodule 这个描述可综合,但许多工具仅仅产生一个寄存器堆,因此与一个真正的存储器相比耗费更多的面积。

44 参数化存储器描述 在下面的例子中,给出如何定义一个字长和地址均参数化的只读存储器件。
module scalable_ROM (mem_word, address); parameter addr_bits = 8; // 地址总线宽度 parameter wordsize = 8; // 字宽 parameter words = (1 << addr_bits); // mem容量 output [wordsize:1] mem_word; // 存储器字 input [addr_bits:1] address; // 地址总线 reg [wordsize:1] mem [0 : words-1]; // mem声明 // 输出存储器的一个字 wire [wordsize:1] mem_word = mem[address]; endmodule 例中存储器字范围从0而不是1开始,因为存储器直接用地址线确定地址。也 可以用下面的方式声明存储器并寻址。 reg [wordsize:1] mem [1:words]; // 从地址1开始的存储器 // 存储器寻址时地址必须加1 wire [wordsize:1] mem_word = mem[ address + 1];

45 存储器数据装入 可以使用循环或系统任务给存储器装入初始化数据 用循环给存储器的每个字赋值
for (i= 0; i < memsize; i = i+ 1) // initialize memory mema[ i] = {wordsize{ 1'b1}}; 调用系统任务$readmem $readmemb("mem_file. txt", mema); 可以用 系统任务$readmem给一个ROM或RAM加载数据。对于ROM,开始时写入的数据就是其实际内容。对于RAM,可以通过初始化,而不是用不同的写周期给每个字装入数据以减少仿真时间。

46 使用双向端口 用关键词inout声明一个双向端口 inout [7:0] databus; 双向端口声明遵循下列规则:
inout端口不能声明为寄存器类型,只能是net类型。 这样仿真器若有多个驱动时可以确定结果值。 对inout端口可以从任意一个方向驱动数据。端口数据类型缺省为net类型。不能对net进行过程赋值,只能在过程块外部持续赋值,或将它连接到基本单元。 在同一时间应只从一个方向驱动inout端口。 例如:在RAM模型中,如果使用双向数据总线读取RAM数据,同时在数据总线上驱动写数据,则会产生逻辑冲突,使数据总线变为未知。 必须设计与inout端口相关的逻辑以确保正确操作。当把该端口作为输入使用时,必须禁止输出逻辑。

47 双向端口建模 — 使用基本单元建模 信号en_a_b和en_b_a控制使能
module bus_xcvr( bus_a, bus_b, en_a_b, en_b_a); inout bus_a, bus_b; input en_a_b, en_b_a; bufif1 b1 (bus_b, bus_a, en_a_b); bufif1 b2 (bus_a, bus_b, en_b_a); // Structural module logic endmodule 若en_a_b=1,基本单元b1使能,bus_a数据传送到bus_b 若en_b_a=1,基本单元b2使能,bus_b数据传送到bus_a

48 双向端口建模 — 使用持续赋值建模 信号en_a_b和en_b_a控制使能
module bus_xcvr( bus_a, bus_b, en_a_b, en_b_a); inout bus_a, bus_b; input en_a_b, en_b_a; assign bus_b = en_a_b ? bus_a : 'bz; assign bus_a = en_b_a ? bus_b : 'bz; // Structural module logic endmodule 若en_a_b=1,赋值语句驱动bus_a数据到bus_b 若en_b_a=1,赋值语句驱动bus_b值到bus_a

49 双向端口建模 — 存储器端口建模 module ram_cell( databus, rd, wr); inout databus;
input rd, wr; reg datareg; assign databus = rd ? datareg : 'bz; negedge wr) datareg <= databus; endmodule 当rd=1时,datareg的值赋值databus 在wr下降沿,databus数据写入datareg

50 复习 问题: 解答: 在Verilog中用什么结构定义一个存储器组? 如何向存储器加载数据? 如何通过一个双向(inout)端口传送数据?
可以用系统任务$readmem或$readmemb或用过程赋值向存储器加载数据 因为inout两端信号必须都是net数据类型,因此只能使用基本单元,子模块,或持续赋值驱动数据。同时还必须注意确保在任何一端不要发生驱动冲突。


Download ppt "第14章 对验证的支持 学习内容 理解Verilog文本输出 理解不同的读取仿真时间的系统函数 理解 Verilog文件I/O功能."

Similar presentations


Ads by Google