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

Slides:



Advertisements
Similar presentations
第1章第1章 PC/AT 系統 1 Chapter 1 Chapter 1 PC/AT 系統 001.
Advertisements

教员信息 姓 名:仲崇权 单 位:电子与信息工程学院 电 话: 办公室:创新院大厦720房间 :
程序的执行 程序执行和指令执行概述 数据通路基本结构和工作原理 流水线方式下指令的执行
广东省高新技术企业认定工作培训 关于专项审计报告的说明与解释.
TMS320F2812串行外设接口 Serial Peripheral Interface (SPI)
彰化縣教師會 導護問題知多少? 理事長:許麗芳老師 報告人:廖銘潭老師   .
Profibus Training Course
最新計算機概論 第3章 計算機組織.
Chapter 5 Sequential Logic Circuit
數位邏輯設計與實習 ch04 組合邏輯電路設計.
第八組 組員:07黃佩瑄 13吳姿毅 14葉芷芸 26黃欣蓮 34林思妤 48潘婷蓉
单片机原理与应用.
第七章 控制器 7.1 控制器的组成及指令的执行 7.2 控制方式和时序的产生 7.3 微程序控制器 7.4 微程序控制器及其微程序设计举例
电工电子实验中心.
CH.2 Introduction to Microprocessor-Based Control
第七章 单片机存储器的扩展.
汇编语言与接口技术 教师:范新民.
水煮FPGA 传统FPGA设计流程简介.
FC OB1 FB SFC 操作系统 SFB OBs 结构化编程 其它
Chapter 5 電腦元件 目標---- 研讀完本章後,你應該可以: 閱讀有關電腦的廣告以及了解它的專業用語(行話)。
项目2 2个LED发光二极管控制 知识与能力目标 熟悉单片机的I/O口功能与特性。
第10章 Verilog操作符 学习内容: 熟悉Verilog语言的操作符.
触发器和时序电路分析 刘鹏 浙江大学信息与电子工程学院 March 30, 2017 ZDMC.
PIC16F1827介紹 以微控器為基礎之電路設計實務-微處理器實驗室.
第4章 处理器(CPU) 4.1 引言 4.2 逻辑设计的一般方法 4.3 建立数据通路 4.4 一个简单的实现机制 4.5 多周期实现机制.
VHDL數位電路實習與專題設計 文魁資訊-UE301
Chapter 5 Verilog 硬體描述語言
计算机文化基础 第一章 计算机的基础知识.
Chapter 5 Verilog硬體描述語言
结构化编程 FC OB1 FB SFC 操作系统 SFB OBs 其它
1-1 微電腦系統單元 1-2 微電腦系統架構 1-3 微控制器(單晶片微電腦) 1-4 類比與數位訊號介面
1-1 微電腦系統單元 1-2 微電腦系統架構 1-3 微控制器(單晶片微電腦) 1-4 類比與數位訊號介面
Ch01-2 Verilog語法 資料流(DataFlow)設計 行為(Behavior)設計
第 6 章 存储系统 6.1 概述 存储器的层次结构 存储器的分类 存储器的基本组成
第17章 Verilog中的高级结构 学习内容: 任务和函数的定义和调用 怎样使用命名块 怎样禁止命名块和任务 有限状态机(FSM)及建模.
EDA技术 廖义奎.
EDA原理及应用 何宾
第四阶段实验 ISP器件的设计与应用 一、实验目的 二、实验内容与要求 三、ISP器件的开发流程 四、EDA Pro2K实验系统介绍
Block diagram BB —逻辑 —音频 —电源管理 HW interface. Block diagram BB —逻辑 —音频 —电源管理 HW interface.
欢迎参加VHDL培训 VHDL培训教程 浙江大学电子信息技术研究所 电子设计自动化(EDA)培训中心
数字系统设计复习 Digital System Design Summary
微处理器设计2 刘鹏 College of ISEE Zhejiang University
Verilog硬件描述语言基础.
第14章 其它DSP设计库 14.1 总线控制库 14.2 复数信号库 14.3 Gates库 14.4 状态机函数库
Programmable Logic Architecture Verilog HDL FPGA Design
重點 資料結構之選定會影響演算法 選擇對的資料結構讓您上天堂 程式.
陳慶瀚 機器智慧與自動化技術(MIAT)實驗室 國立中央大學資工系 2013年5月28日
TestBench的书写 TestBench的目标 TestBench的基本框架 书写TestBench的几种主要方法 HDL调试的主要方式
语法进阶.
时序电路设计 刘鹏 浙江大学信息与电子工程系 Apr. 24, 2011 EE141
数字集成电路设计入门 --从HDL到版图 于敦山 北大微电子学系.
精简指令集(RISC)CPU的构造原理和设计方法
本 章 重 点 单片机的结构特点 单片机的存储器特点 I/O端口的特点 CPU时序 课时安排:3个课时.
第二章 8086微处理器. 第二章 8086微处理器 微处理器的基本结构 8086微处理器的主要特性和内部结构 8086CPU的工作模式和引脚信号 8086的存储器管理 8086的总线操作和时序.
操作系统原理与设计 Operating Systems: Design and Implementation
第一次上机安排 第六周 第七周 周一晚(提高1、2,通信001~012) 周二上(通信014~085) 周四上(通信086~154)
数据块中的数据存储 目录 页 目标 …… 数据存储区 … ……… 数据块 (DB) … STEP 7数据类型概述 STEP 7基本数据类型
$15 文件操作. $15 文件操作 主要内容 文件系统概述 驱动器、目录和文件 文件流和数据流 自定义IO操作.
第一章.
计算机学院 数字逻辑实验的要求.
汽车单片机应用技术 学习情景1: 汽车空调系统的单片机控制 主讲:向楠.
计算机EDA设计 教 程 北航计算机学院 艾明晶.
資訊傳播工程學系 蔡奇偉 副教授 專業英文導讀 課程說明 資訊傳播工程學系 蔡奇偉 副教授
设计示例一 用门级结构描述D触发器:.
中五級電腦科 PASCAL檔案處理.
數位邏輯 第9章循序邏輯 9-1 正反器 9-2 暫存器 9-3 計數器.
FPGA组合逻辑 王安然.
Verilog HDL 基本语法 STEP 2016/12/3.
按键处理部分 王安然.
单片机原理及接口技术 前修课程:数模电、微机原理.
Presentation transcript:

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

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

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

输出格式化时间信息 若使用多个`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:显示前三项的最小宽度 在这个例子中,显示的时间为:180.00 ns

输出格式化时间信息 time realtime stime in1 o1 `timescale 1 ns / 10 ps 0 0.00ns 0 0 x 10 9.53ns 10 0 1 10 10.00ns 10 1 1 20 19.53ns 20 1 0 `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

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

显示信号值 — $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的十进制数

显示信号值—$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

显示信号值—$write和$strobe 下面是模块textio仿真的输出: $writeb输出: 0 xxxxxxxx x 注意data是32位数据,由8位十六进制数表示。时间以没有前导零的十进制形式输出。 缺省情况下,值以十进制显示,忽略前导零,与%0d格式符相同。可以在一个格式化符前插入一个0使Verilog忽略开头的零。 $displayh: 00000000000000f 00000010 1 注意当前时间,一个64位量,需要16个十六进制的数。 $display: 10 20 $strobe: 10 30 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

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

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

文件输出 $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相对应的文件中。

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

文件输出 . . . 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 always @( 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 */ always @( negedge reset) $fdisplay( broadcast, "system reset at time %d", $time); 必须声明为integer 通道0(编号为1)为标准输出及verilog.log

文件输入 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>);

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

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

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

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

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

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

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

并行块 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 0 | 8’b0000_0000 10 | 8’b0100_0101 30 | 8’b0100_0110 40 | 8’b0100_0111 45 | 8’b1000_1110 50 | 8’b1000_1111 60 | 8’b1001_0000 65 | 8’b0010_0000 70 | 8’b0010_0001 80 | 8’b0010_0010 85 | 8’b0100_0100 90 | 8’b0100_0101 100 | 8’b0100_0110 105 | 8’b1000_1100 110 | 8’b1000_1101 120 | 8’b1000_1110 125 | 8’b0001_1100 140 | 8’b0000_1111 上面的两个repeat循环从不同时间开始,并行执行。象这样的特殊的激励集在单个的begin…end块中将很难实现。

包含文件 包含文件用于读入代码的重复部分或公共数据。 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 在上面的例子中,公共参数在一个独立的文件中定义。此文件在不同的仿真中可被不同的测试文件调用。

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

线性激励 线性激励有以下特性: 只有变量的值改变时才列出 易于定义复杂的时序关系 对一个复杂的测试,测试基准(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

循环激励 从循环产生激励有以下特性: 在每一次循环,修改同一组激励变量 时序关系规则 代码紧凑 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

数组激励 从数组产生激励有以下特性: 在每次反复中,修改同一组激励变量 激励数组可以直接从文件中读取 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

矢量采样 在仿真过程中可以对激励和响应矢量进行采样,作为其它仿真的激励和期望结果。 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

矢量回放 保存在文件中的矢量反过来可以作为激励 使用矢量文件输入/输出的优点: 激励修改简单 设计反复验证时直接使用工具比较矢量文件。 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 00111000 00111001 00111010 00111100 00110000 00101000 00011000 01111000 10111000 . 使用矢量文件输入/输出的优点: 激励修改简单 设计反复验证时直接使用工具比较矢量文件。

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

强制激励 在过程块中,可以用两种持续赋值语句驱动一个值或表达式到一个信号。 过程持续赋值通常不可综合,所以它们通常用于测试基准描述。 对每一种持续赋值,都有对应的命令停止信号赋值。 不允许在赋值语句内部出现时序控制。 对一个寄存器使用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 ;

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

建立时钟 例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) 注意:在一些仿真器中,时钟与设计使用相同的抽象级描述时,仿真性能会好一些。

建立时钟 例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值。

建立时钟 例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值立刻被影响;而在结构描述中,在传播延时后才输出正确波形。

使用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

使用task 产生的波形

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

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

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

简单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); always @( addr or read_en_) if (! read_en_) read_data = mem[addr]; endmodule

简单的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; // 写 always @( posedge write) memory[addr] = data; endmodule 这个描述可综合,但许多工具仅仅产生一个寄存器堆,因此与一个真正的存储器相比耗费更多的面积。

参数化存储器描述 在下面的例子中,给出如何定义一个字长和地址均参数化的只读存储器件。 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];

存储器数据装入 可以使用循环或系统任务给存储器装入初始化数据 用循环给存储器的每个字赋值 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,可以通过初始化,而不是用不同的写周期给每个字装入数据以减少仿真时间。

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

双向端口建模 — 使用基本单元建模 信号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

双向端口建模 — 使用持续赋值建模 信号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

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

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