以实现一个全加器为例子,
//-------------------------------------------------------------
// FileName: full_adder.v
// Creator: demo
// E-mail: [email protected]
// Function: one bit full adder
// Update:
// Copyright: www.demo.demo.com
//--------------------------------------------------------------
module full_adder (
// module head: verillog-2001 format
input wire a_in,
input wire b_in,
input wire c_in, //carry in
output wire sum_out,
output wire c_out //carry out,
);
// mehtod 1 Gate Level describe
assign sum_out = a_in ^ b_in ^ c_in;
assign c_out = (a_in & b_in) | (b_in & c_in) | (a_in & c_in);
// method 2 RTL design for Adder with the keyword "assign"
// behaviro of the adder can be synthesizable
// "assign" means connectivity, which is used to describe a combinational circuit
// assign {c_out, sum_out} = a_in + b_in + c_in;
// method 3 RTL design for Adder wiht the keyword "always"
//reg c_o, sum_o;
//always @ (a_in, b_in, c_in) begin
// {c_o, sum_o} = a_in + b_in + c_in; // the reg type variable is required in the always blocks
//end
// assign {c_out, sum_out} = {c_O, sum_o};
endmoudle
如何验证一个全加器呢?
//-------------------------------------------------------------
// File header
//-------------------------------------------------------------
module full_adder_tb;
// driver the input port with the reg type
reg ain, bin, cin; //给 DUT 输出驱动,驱动的类型要是 reg 类型;
// sample the output port with the wire type
wire sumout, cout;
full_adder u_full_addr( // instance, 实列化,真正的物理电路是实例化以后,课可以例化很多加法器。
// task 1. how to create an instance
// moudle head: verillog-2001 format
/* input wire */ .a_in (ain), //testbench 的信号和 DUT的信号通过显示方式进行连接
/* input wire */ .a_in (bin),
/* input wire */ .a_in (cin),
/* output wire */ .sumout (sumout), //carry in
/* output wire */ .c_out (cout) //carry out
);
// behavior of the adder can ben synthesizeable
// "assign" means connectivity
// assign {c_out, sum_out} = a_in + b_in + c_in;
// task 2. clock and reset generator
parameter CLK_PERIOD = 20;
reg clk, reset_n; // reset_n: active low
initial begin
clk = 0;
forever begin
#(CLK_PERIOD/2) clk = ~clk;
end
end
initial begin
reset_n = 0;
#100
reset_n = 1;
end
// task 3. driver the stimulus and caputre the response
// here is a testcase
initial begin
#110 ain = 0; bin = 0 ; cin = 0; //00
#20 ain = 0; bin = 1 ; cin = 0; //01
#20 ain = 1; bin = 0 ; cin = 0; //01
#20 ain = 1; bin = 1 ; cin = 0; //10
#20 ain = 0; bin = 0 ; cin = 1; //01
#20 ain = 0; bin = 1 ; cin = 1; //10
#20 ain = 1; bin = 0 ; cin = 1; //10
//#20 ain = 1; bin = 1 ; cin = 1; //11
#20 ain = 1; bin = 1 ; cin = 0; //10
#50 $finish; // here is a system task which can stop the simulation
end
// task 4. check the result
always @ {possedge clk} begin
if (!reset_n) begin
$dispaly("%t: %m: resetting..., $time")// counter5 clock
end
else begin
$dispaly("%t: %m: resetting finish!, $time")// the 6th clock
end
end
initial begin
#115 if({count, sumout}!=2'b00) $display("Error:{count, sumout}=%b,ain=%b,bin=%b,cin=%b",{count,sumout},ain,bin,cin);
#20 if({count, sumout}!=2'b01) $display("Error:{count, sumout}=%b,ain=%b,bin=%b,cin=%b",{count,sumout},ain,bin,cin);
#20 if({count, sumout}!=2'b01) $display("Error:{count, sumout}=%b,ain=%b,bin=%b,cin=%b",{count,sumout},ain,bin,cin);
#20 if({count, sumout}!=2'b10) $display("Error:{count, sumout}=%b,ain=%b,bin=%b,cin=%b",{count,sumout},ain,bin,cin);
#20 if({count, sumout}!=2'b01) $display("Error:{count, sumout}=%b,ain=%b,bin=%b,cin=%b",{count,sumout},ain,bin,cin);
#20 if({count, sumout}!=2'b10) $display("Error:{count, sumout}=%b,ain=%b,bin=%b,cin=%b",{count,sumout},ain,bin,cin);
#20 if({count, sumout}!=2'b10) $display("Error:{count, sumout}=%b,ain=%b,bin=%b,cin=%b",{count,sumout},ain,bin,cin);
#20 if({count, sumout}!=2'b11) $display("Error:{count, sumout}=%b,ain=%b,bin=%b,cin=%b",{count,sumout},ain,bin,cin);
#20 if({count, sumout}!=2'b11) $display("Error:{count, sumout}=%b,ain=%b,bin=%b,cin=%b",{count,sumout},ain,bin,cin);
end
// task 5. dump waveform with the compile opton -debug_all
inital begin
$vcdplusson;
end
endmoudle
initial begin 表示初始化,也即只执行一次:
CLK_PERIOD =20 ns
, 也即时钟为50M
, 开始时刻 clk 为0, 然后 进行 延时10ns(CLK_PERIOD/2)后再将 clk 信号取反,这样时钟就会反复循环10ns
高 和 10ns
低的效果。reset_n
先拉低,然后延时 100ns
后再拉高# Makefile for simulate the full_adder.v with the simulator VCS
#-----------------------------------------------------------------
# Macro variables
RTL := ./full_addder.v
TB += ./full_adder_tb.v
SEED ?= $(shell data +%s)
# Target: Dependency
all: compile simulate
compile:
vcs -sverilog -debug_all timescale.v $(RTL) $(TB) -l com.log #编译文件
simulate:
./simv +ntb_random_seed=$(SEED) -l sim.log # 执行仿真
run_dev:
dev -vpd vcdplus.vpd # 查看仿真波形
clean: rm -rf *.log csrc simv* *.key *.vpd DVEfiles coverage *.vdb
首先了解下上面 Makefile 中的 三种赋值方式:
其次了解下编译参数:
-sverilog
用于识别 SystemVerlog 语法;-debug_all
可以将 debug 的信息全部保存下来,比如波形文件;-l com.log
将百衲衣过程生成的日志全部写入 com.log 文件。vcs 编译完成后会生成一个simv的可执行文件。
在 terminal 中输入 make 命令:
make
; 回到当前目录找Makefile 文件,并all 开始执行;make all
make compile
; make simulate
相当于执行了 make allmake run_dev
make clean
; make all
用户可以指定要 make
的 target, 即要做哪件事情,如:make clean
,那么就会执行 Makefile 文件中的 删除文件操作。
逻辑综合(Synthesis)工具主要用于检查 RTL 代码是否可以综合成电路(与门、非门、或门、FF),具体来说的化就是在 RTL Code freeze 之后将前端设计工程师写的RTL code,映射到特定工艺库上(TSMC/UMC/SMIC),通过添加约束信息,对RTL 代码进行逻辑优化,形成门级网表。
其中约束信息包含(PPA):
主要包括下面三部:
Trannslation + mapping + optimized
逻辑综合只做了解
逻辑综合完成后需要将 netlist + SDC 给到 backend,做物理版图(layout)
一、启动 DC 工具
dc_shell
dc_shell -f syn.tcl | tee -i syn.log
二、设置搜索路径(search_patch)
set_app_var search_patch "$search_patch" ./rtl ./scripts ./libs"
三、libray setup(mapping)
四、read_verilog
read_verilog "TOP.v A.v B.v"
五、current_design
current_design TOP
//设置顶层文件六、timing constrain
clock period;
clock skew 时钟上升或者下降的坡度;
clock transition 时钟从0->1 或者从 1->0 需要的时间;
clock latency 时钟从源发出到接收点的时间;
Input delay
set_input_delay
output delay
set_out_delay -max 0.8 -clock Clk [get_ports B]
七、environment constraint
set_input_transition
set_load
八、compile/compile_ultra
compile
compile_ultra
九、report qor
十、report_timing
report_timing
静态时序分析
十一、output
推荐阅读:
https://www.bilibili.com/video/BV1WY411D7So?p=9&spm_id_from=pageDriver&vd_source=a354e64412a97e828c2f4b7ebe7c3606