目录
1、verilog 源文件(RTL coding)是 .v 文件。
2、仿真要有testbench, 也是.v 文件, 简称_tb.v
EDA 工具
EDA,全称电子设计自动化(Electronics Design Automation),在数字、模拟和混合集成电路的各个开发阶段都发挥着重要作用。常用的EDA工具主要来自三家公司:Synopsys、Cadence和Mentor(已被Siemens收购)。以下是这些工具的详细介绍:
🌟 RTL功能仿真:VCS+Verdi套装或ModelSim是常用的功能仿真工具。VCS用于仿真,Verdi用于波形debug,在Linux下非常好用;ModelSim则兼具仿真和波形debug功能,在Windows下常用。功能仿真阶段不区分FPGA或ASIC,工具通用;这些工具也可以进行后仿,即动态时序仿真。
🌟 逻辑综合:Synopsys的Design Compiler(DC)是常用的逻辑综合工具。通常使用TCL脚本(工具命令语言),网上有丰富的相关资料。综合阶段还可以在工具内进行timing等分析,但结果不如PT准确。(RTL 转成网表)
布局布线:Synopsys的ICC/ICC2是常用的布局布线工具,功能强大,可以根据用户的floorplan自动完成布局优化和布线优化。工具还支持timing、DRC、LVS检查等功能,但不是sign-off工具。
形式验证:Synopsys的Formality是常用的sign-off工具,主要用于静态时序分析、功耗分析、设计规则检查和版图一致性验证,确保芯片在流片前满足项目规格。
仿真有三种形式:
RTL级,门级,时序仿真。
RTL行为级仿真:这个阶段的仿真可以用来检查代码中的语法错误以及代码行为的正确性,其中不包括延时信息。 需要的文件:写好的RTL代码.v和测试代码_tb.v文件。
综合后门级功能仿真:
1、verilog 源文件(RTL coding)是 .v 文件。
SR.v 触发器。先知道了门级结构,才有coding
module SR(
output Q,Qn,
input S,R
);
wire q,qn; //内部连线
assign #1 Q=q; //将内部连线,连到Q Qn 上
assign #1 Qn = qn;
assign #10 q= ~(S & qn); // 电路结构
assign #10 qn = ~(R & q);
endmodule
XorNor 异或门
module XorNor(
output X, Y,
input A, B, C
);
wire x;
assign #1 X=x; // wire connection
assign #10 x= A ^ B;
assign #10 Y= ~(x|C);
endmodule // XorNor
Top 层
使用top 层指明设计的顶层结构,
module Intro_Top(
output X,Y,Z;
input A,B,C,D
);
wire ab, bc, q, qn; //内部的连线
assign #1 Z = ~qn;//反相器
AndOr InputCom01( //实例化,instance
.X(ab), // X是本身的, X(ab) 表示X 要连接到ab上
.Y(bc),
.A(A),
.B(B),
.C(C)
);
SR SR01( //这个代码的连接 是框架图延申的
.Q(q),
.Qn(qn),
.S(bc),
.R(D)
);
XorNor OutputCom01 (
.X(X),
.Y(Y),
.A(ab),
.B(q),
.C(qn)
);
endmodule
2、仿真要有testbench, 也是.v 文件, 简称_tb.v
initial 不可综合的,
// `timescale 1ns / 1ps
module tb_counter; //testbench 没有port,即没有输入输出
reg Astim,Bstim,Cstim,Dstim; //内部变量
wire Xwatch,Ywatch,Zwatch;
initial begin //testcase 顺序执行的
$dumpportsall;
$dumpfile("VSC_.VCD"); // .vcd 是标准的waveform format
$dumpvars;
#1 Astim=1'b0; //initial和always 里的左值必须是reg 类型
#1 Bstim=1'b0;
#1 Cstim=1'b0;
#1 Dstim=1'b0;
#50 $finish; //仿真终止
end
initial begin // 多个initial 是并发执行的
$vcdpluson();
end
Intro_Top Topper01 (
.X(Xwatch), //把 X Y Z的输出,接到 Xwatch 线上,来观察
.Y(Ywatch),
.Z(Zwatch),
.A(Astim), //A B C D 是输入信号,reg 类型的Astim 激励 用来驱动
.B(Bstim),
.C(Cstim),
.D(Dstim)
);
endmodule;
3、先compile , 再simulate
- 先运行源文件,输出.out 文件(VScode 里的方法)
- 再ctrl + shift +p, 选择testbench,生成testbench 例子。
- 用testbench 和 源文件,生成 vvp: iverilog -o test_1.vvp test_1.v test_tb.v
- vvp .\test_1.vvp 生成 .vcd 波形文件
- 点击.vcd 添加信号,查看波形
4、老师案例:
make 调用仿真工具,(vcs 命令)
dve 调出来GUI 界面,打开波形文件
根据testbench , check 波形对不对(verdi 这个工具也可以看波形)
dc_shell ,使用.tcl 调用综合工具,输出综合网表
多文件仿真
先编译文件:
- iverilog -o param_counter_top.out -y ./ .\param_counter_top.v
- iverilog -o param_counter_top_tb.out -y ./ .\param_counter_top_tb.v
再生成 vvp(源文件+tb 文件)
iverilog -o test_1.vvp test_1.v test_tb.v
6 iverilog -o param_counter_top.vvp -y ./ .\ .\param_counter_top.v .\param_counter_top_tb.v
7 iverilog -o param_counter_top.vvp -y ./ .\param_counter_top_tb.v
再生成vcd 文件
9 vvp .\param_counter_top.vvp
其中tb.v 文件里要添加
initial
begin
$dumpfile("param_counter_top.vcd"); //生成的波形 vcd文件名称
$dumpvars(0, tb); //参与仿真的tb模块名称
end
endmodule//tb
Design Compiler
一种由Synopsys公司开发的电子设计自动化(EDA)工具,主要用于将硬件描述语言(如Verilog或VHDL)编写的高层次电路设计转换为优化的门级网表。
主要功能: 该工具能够进行逻辑综合、设计约束设置、时序分析等操作,以满足设计的性能和面积要求。
Design Compiler (二)——DC综合与Tcl语法结构概述_Design Compiler-CSDN专栏
dc_shell: DC以命令行的格式启动:$dc_shel,需要知道一些tcl的简单语法。
.tcl 是个脚本
综合后需要查看timing、area 符不符合需要;
综合后的多个文件需要写到一个ddc 文件里,或者一个netlist
lab2:
vector:
理解:信号线有顺序,有方向。指定是reg 或者wire, [0:15]与[15:0] 方向不一样。
`timescale 1ns/100ps// `是键盘左上角的~键,不是单引号'。`'这俩不一样
module Vector;
reg [0:15] Mybus; //0~15
wire [15:0] mybus; //15~0
wire mybit;
reg [47:0] My48bits;
assign mybus = 16'heeee;// e=4'b1110; assign 当做一个连线,把信号线连上
assign mybit = 1'b0;
initial
begin
$display("at %0t: mybus=16'b %0b", $time,mybus); //%0t 对应时间.%d
#10 Mybus=16'h3333;
$display("at %0t: Mybus=16'b %0h", $time,Mybus); //%h
$display("at %0t: Mybus[0:2]=3'b %0b", $time,Mybus[0:2]);//[取部分位]
$display("at %0t: mybus[10:8]=3'b %0b", $time,mybus[10:8]);//[取部分位]
#10 Mybus[0:2]=mybus[10:8];
$display("after Mybus[0:2]=mybus[10:8]");
$display("at %0t: Mybus[0:2]=3'b %0b", $time,Mybus[0:2]);//[取部分位]
$display("at %0t: mybus[10:8]=3'b %0b", $time,mybus[10:8]);//[取部分位]
#10 Mybus=mybus; //全匹配
$display("after Mybus=mybus");
$display("at %0t: Mybus=16'b %0b", $time,Mybus);
$display("at %0t: mybus=16'b %0b", $time,mybus);
/*以下是位扩展*/
#10 Mybus=16'hxxxx;
#5 My48bits= 'bz; $display("at %0t: My48bits=48'b %b", $time,My48bits);
#5 My48bits= 'bx; $display("at %0t: My48bits=48'b %b", $time,My48bits);
#5 My48bits= 'b0; $display("at %0t: My48bits=48'b %b", $time,My48bits);
#5 My48bits= 'b1; $display("at %0t: My48bits=48'b %b", $time,My48bits);
#5 My48bits= 1'bz; $display("at %0t: My48bits=48'b %b", $time,My48bits);
#5 My48bits= 1'bx; $display("at %0t: My48bits=48'b %b", $time,My48bits);
#5 My48bits= 1'b0; $display("at %0t: My48bits=48'b %b", $time,My48bits);
#5 My48bits= 1'b1; $display("at %0t: My48bits=48'b %b", $time,My48bits);
#5 $finish;
end
endmodule
输出结果:
[Running] Vector.v at 0: [15:0]mybus=16'b 1110111011101110 at 10: [0:15]Mybus=16'b 3333 at 10: Mybus[0:2]=3'b 1 at 10: mybus[10:8]=3'b 110 after Mybus[0:2]=mybus[10:8] at 20: Mybus[0:2]=3'b 110 at 20: mybus[10:8]=3'b 110 after Mybus=mybus at 30: Mybus=16'b 1110111011101110 at 30: mybus=16'b 1110111011101110 at 45: My48bits=48'b zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz at 50: My48bits=48'b xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx at 55: My48bits=48'b 000000000000000000000000000000000000000000000000 at 60: My48bits=48'b 000000000000000000000000000000000000000000000001 at 65: My48bits=48'b 00000000000000000000000000000000000000000000000z at 70: My48bits=48'b 00000000000000000000000000000000000000000000000x at 75: My48bits=48'b 000000000000000000000000000000000000000000000000 at 80: My48bits=48'b 000000000000000000000000000000000000000000000001 Vector.v:43: $finish called at 85 (1ns) [Done] exit with code=0 in 0.147 seconds
扩展
VCS 安装
VCS(Synopsys Verification Compiler Simulator)的安装主要包括下载安装包、配置环境及破解License等步骤,具体方法因操作系统和版本而异。
VCS安装步骤概述
下载安装包:
- 访问Synopsys官网或通过授权渠道获取VCS安装镜像(如2023.12版本)。1
- 若使用虚拟机(如VMware),需提前准备Linux系统环境。
安装依赖项:
- 在Linux系统中安装必要工具(如
csh
、lsb-core
)以支持VCS运行。2- 通过Synopsys Installer工具完成VCS和SCL(License管理工具)的安装。2
配置License:
- 使用
scl_keygen
生成Synopsys.dat
文件,并确保其路径正确。2- 验证License有效性,若失败需检查网卡名称或HostID匹配问题。2
环境变量设置:
- 编辑
~/.bashrc
文件,添加VCS和SCL的路径,并通过source
命令生效。2注意事项
- 空间需求:VCS安装包较大(约40GB),建议分配充足磁盘空间。
- 版本兼容性:不同版本的VCS可能对操作系统或依赖项有特定要求,需参考官方文档。1
- 虚拟机安装:若在虚拟机中运行,需确保共享文件夹配置正确以传输安装文件。
lab3:
counter.v
// this is a unit:up-count, 异步复位,clock gating, 时序电路里用非阻塞赋值
module counter #(parameter WIDTH=4)(
output [WIDTH-1:0] count,
input clk, count_enable,count_reset
);
reg[WIDTH-1:0] count_reg;
wire clock_wire;
assign #1 count=count_reg; //输出要寄存,要连到 reg 上
assign clock_wire = (count_enable==1'b1)? clk:1'b0; //连接clock 线
always @(posedge clock_wire, posedge count_reset) //异步复位:在敏感列表里加 reset 信号
begin
if(count_reset == 1'b1)
count_reg<='b0; //不要1'b0, 用'b0 可以忽略位宽
else
count_reg <= count_reg + 1'b1; // 加1
end
endmodule //counter
truncate.v 截断
integer Int=0; //一个整型 = 32bit 。integer 有符号,分正负。 reg 无符号,负数是2进制补码。
reg[007:0] Byte='b0;
reg[031:0] Word='b0; // 一个word = 32bit,half word = 16bit
reg[063:0] Long='b0; //long = 64bit
reg[127:0] Dlong='b0; //double word =128bit
#20 $finish 要加 #20 时间延迟,如果不加,因为是并发执行,一开始就会finish, 就无法运行其它#20 的代码段
//filename:truncate.v
`timescale 1ns/1ns
module truncate;
integer Int=0; //一个整型 = 32bit 。integer 有符号,分正负。
reg[007:0] Byte='b0; //reg 无符号
reg[031:0] Word='b0;// 一个word = 32bit,half word = 16bit
reg[063:0] Long='b0;//long = 64bit
reg[127:0] Dlong='b0; //double word =128bit
initial
begin
#20 //%h,没有数值的位数,会补0
$display("the value is 6'd1");
Int=6'd1;$display("at %t: Int=32'h%h",$time,Int);
Byte=6'd1;$display("at %t: Byte=8'h%h",$time,Byte);
Word=6'd1;$display("at %t: Word=32'h%h",$time,Word);
Long=6'd1;$display("at %t: Long=64'h%h",$time,Long);
Dlong=6'd1;$display("at %t: Dlong=128'h%h",$time,Dlong);
#20 //负号
$display("the value is -6'd1");
Int=-6'd1;$display("at %t: Int=32'h%h",$time,Int);
Int=-6'd1;$display("at %t: Int=32'd%d",$time,Int);
Int=-6'd1;$display("at %t: Int=32'b%b",$time,Int);
Byte=-6'd1;$display("at %t: Byte=8'h%h",$time,Byte);//无符号应当显示为二进制补码
Word=-6'd1;$display("at %t: Word=32'h%h",$time,Word);
Long=-6'd1;$display("at %t: Long=64'h%h",$time,Long);
Dlong=-6'd1;$display("at %t: Dlong=128'h%h",$time,Dlong);
#20 $finish;
end
endmodule
输出结果:
16进制输出,4个bit 为一个位置,8bit=2个位置
在16进制、2进制中,负数都用“补码”表示,十进制有符号,用负数表示。
因为定义的Dlong 为[127:0], 所以就算Dlong=6'd1; 打印的%h,仍然要占用128bit. 即32个位置。
[Running] truncate.v
the value is 6'd1
at 20: Int=32'h00000001 //32bit= 4*8, 8个位置
at 20: Byte=8'h01 //8bit=2个位置
at 20: Word=32'h00000001
at 20: Long=64'h0000000000000001
at 20: Dlong=128'h00000000000000000000000000000001
the value is -6'd1
at 40: Int=32'hffffffff
at 40: Int=32'd -1
at 40: Int=32'b11111111111111111111111111111111
at 40: Byte=8'hff
at 40: Word=32'hffffffff
at 40: Long=64'hffffffffffffffff
at 40: Dlong=128'hffffffffffffffffffffffffffffffff
truncate.v:30: $finish called at 60 (1ns)
截断赋值:
代码段:
//截断输出
#10
Word =32'h 7eee_777f;
Byte=Word;$display("at %t: Byte=8'h%h",$time,Byte);//长值赋给短值,则只保留低位
Int=Word;$display("at %t: Int=32'h%h",$time,Int);
Long=Word;$display("at %t: Long=64'h%h",$time,Long);//短值赋给长值,不够的补0
Dlong=Word;$display("at %t: Dlong=128'h%h",$time,Dlong);
结果:
[Running] truncate.v
at 10: Byte=8'h7f
at 10: Int=32'h7eee777f
at 10: Long=64'h000000007eee777f
at 10: Dlong=128'h0000000000000000000000007eee777f
truncate.v:39: $finish called at 30 (1ns)
integers.v 整型和reg 型
iX是整型,最终计算结果是整型。
rA rB rX 是无符号型,正数就是正数,负数就是补码。
reg 在运算时直接转为整型,输出就是整型。
整型和reg 型也可以混合运算。
#号作用:
在Verilog中,#有2种作用。
第一种作用是指定时间延时,如#10,表示延时10个时间单位;
第二种作用在模块定义时指定常量型参数的默认值,在模块实例化时传递常量型参数的指定值。
converter.v
//用到数值的地方都需要加上 size 和 进制
//output input 默认是wire 类型
// this is a design,功能是在输出的bus 上加 一组pad 线
module converter #(parameter WIDTH=4,PAD_NUM=1)//
( output[WIDTH-1+PAD_NUM:0] out_bus, //默认wire 类型,插入pad_num 线
input [WIDTH-1:0] in_bus,
input enable
);
reg[WIDTH-1+PAD_NUM:0] bus_reg; //count value ,内部变量
reg[WIDTH-1+PAD_NUM:0] out_bus_gate; //存储3态
/*main code,一直更新bus_reg,always 是可综合的*/
always @(in_bus) begin //@ 表示敏感,() 里为电平信号
if(PAD_NUM!=1'b0) begin
bus_reg[WIDTH-1+PAD_NUM:WIDTH]= 'd0; //高位置为0
bus_reg[WIDTH-1:0]=in_bus; //低位接到 in_bus
end
else //有if 就要有else, 否则出现latch
bus_reg[WIDTH-1:0]=in_bus;
end
//使能的话,输出bus_reg , 否则为高阻态
always @(enable,bus_reg) begin
if(enable == 1'b1)
#2 out_bus_gate=bus_reg;
else #1 out_bus_gate= 'bz; //z用二进制,设置高阻态
end
assign out_bus=out_bus_gate; //输出的drive 连接到输出的pin脚
endmodule //converter 在end 写一个module name 标识
param_counter_top.v
参数化设计
//
`define PADWIDTH 3
//top最顶层
module param_counter_top #(parameter WIDTH=`PADWIDTH, PAD_NUM=5)(
output [WIDTH-1+PAD_NUM:0] count,
input clk, count_enable,count_reset,out_enable
);
wire[WIDTH-1:0] Xfer; //连接模块instance
//实例化 //u 表示实例,端口通过显式端口连接(.port_name(signal_name))与顶层模块的信号关联。
//counter #(.WIDTH(WIDTH)) u_counter(Xfer, clk, count_enable,count_reset);
counter #(.WIDTH(WIDTH))
u_counter(
.count (Xfer),
.clk (clk),
.count_enable (count_enable),
.count_reset (count_reset)
);
converter #(.WIDTH(WIDTH), .PAD_NUM(PAD_NUM))
u_converter01(
.out_bus (count),
.in_bus(Xfer),
.enable(out_enable)
);
endmodule //param_counter_top
module tb;// 不综合
parameter WIDTH=`PADWIDTH,PAD_NUM=3;
wire [WIDTH-1+PAD_NUM:0] CountTest;
reg count_enableTest, clockTest,CountResetTest,OutEnableTest;
//instance
param_counter_top #(.WIDTH(WIDTH),.PAD_NUM(PAD_NUM))
u_param_counter_top(
.count(CountTest),
.clk(clockTest),
.count_enable(count_enableTest),
.count_reset(CountRestTest),
.out_enable(OutEnableTest)
);
initial begin
#01 count_enableTest=1'b0;
clockTest=1'b0;
CountResetTest=1'b0;
OutEnableTest=1'b0;
#02 CountResetTest=1'b1;
CountResetTest=1'b0;
#01 count_enableTest=1'b1;
#02 OutEnableTest=1'b1;
#05 clockTest=1'b1;
#05 clockTest=1'b0;
#05 clockTest=1'b1;
#05 clockTest=1'b0;
#01 OutEnableTest=1'b0;
#02 OutEnableTest=1'b1;
#05 clockTest=1'b1;
#05 clockTest=1'b0;
#05 clockTest=1'b1;
#05 clockTest=1'b0;
#05 clockTest=1'b1;
#05 clockTest=1'b0;
#05 clockTest=1'b1;
#05 clockTest=1'b0;
#05 clockTest=1'b1;
#05 clockTest=1'b0;
#05 clockTest=1'b1;
#05 clockTest=1'b0;
#10 $finish;
end
endmodule//tb
输出波形:
@号作用
Verilog中的@符号用于定义always块的执行条件。
如果是 * 号,
- always @(*) // 自动把用到的所有变量都加进来!
- “只要块中用到的任何输入信号发生变化,这个 always 块就会自动执行一次。”