• 【芯片设计- RTL 数字逻辑设计入门 5 -- RTL 全加器实现及验证】


    1.1 RTL 开发流程

    1.1.1 组合逻辑

    always @(*)begin
    	if(sel==0)
    		c = a + b;
    	else
    		c = a + d;
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这种条件信号变化结果立即变化的 always 语句被称为“组合逻辑” 。

    1.1.2 时序逻辑

    always @(posedge clk or negedge rst_n)begin
    	if(rst_n==1'b0)begin
    		c <= 0;
    	end
    	else if(sel==0)
    		c <= a + b;
    	else
    		c <= a + d;
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    上述代码的敏感列表是“posedge clk or negedge rst_n” , 也就是说, 当 clk 由 0 变成 1 的瞬间, 或者 rst_n 由 1 变化 0 的瞬间, 执行一次程序代码, 即第 2 至 8 行, 其他时刻 c 的值保持不变。这种信号边沿触发, 即信号上升沿或者下降沿才变化的 always, 被称为“时序逻辑” , 此时信号 clk 是时钟。 注意: 识别信号是不是时钟不是看名称, 而是看这个信号放在哪里, 只有放在敏感列
    表并且是边沿触发的才是时钟。

    注意以下几点
    1、 组合逻辑的 always 语句中敏感变量必须写全, 或者用“*” 代替。
    2、 组合逻辑器件的赋值采用阻塞赋值 “=”, 时序逻辑器件的赋值语句采用非阻塞赋值<=” ,

    1.1.1 DUT Code

    以实现一个全加器为例子,

    • 功能

      • 真值表
        在这里插入图片描述
    • 验证

      • 功能完整性
      • 穷举法
      • 代码覆盖率
    • lab01

      • 编译
      • 仿真
      • 产看波形
    //-------------------------------------------------------------
    // FileName: full_adder.v
    // Creator: demo
    // E-mail: demo@demo.com
    // 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 方式1: 使用门级网表的方式来描述:在这里插入图片描述

    • 方式2:使用 RTL 设计, assign 表示用金属线连接,主要用于组合电路;其中的花括号可以认为是拼接符号,将高位放在前面,将低位放在后面;一位的全加器会形成两位的输出,所以花括号里是两位的。可以看到使用 “+” 来描述比门级网表的方式要清晰。

    assign 语句的功能属于组合逻辑的范畴, 应用范围可以概括为一下几点:

    • 持续赋值;
    • 连线;
    • 对 wire 型变量赋值, wire 是线网, 相当于实际的连接线, 如果要用 assign 直接连接, 就用 wire 型变量, wire 型变量的值随时发生变化。

    需要说明的是, 多条 assign 连续赋值语句之间互相独立、 并行执行。

    • 方式3:使用RTL 设计,使用“always”语句来定义输出,注意always 语句左侧必须是 reg 类型,

    1.1.2 Testbench

    如何验证一个全加器呢?

    //-------------------------------------------------------------
    // 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, count;
    
    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 (count)     //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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    initial begin 表示初始化,也即只执行一次:

    • clk 逻辑: 设置时钟周期 CLK_PERIOD =20 ns, 也即时钟为50M, 开始时刻 clk 为0, 然后 进行 延时10ns(CLK_PERIOD/2)后再将 clk 信号取反,这样时钟就会反复循环10ns 高 和 10ns低的效果。
    • reset 逻辑:reset_n 先拉低,然后延时 100ns 后再拉高

    1.1.3 自动化编译:Makefile

    # 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    首先了解下上面 Makefile 中的 三种赋值方式:

    • := 属于强制赋值;
    • += 属于追加赋值,如果TB已经有值,会在其后追加当前的值;
    • ?= 属于判断赋值方式,如果SEED已经有值,这个赋值就不会执行;

    其次了解下编译参数

    • -sverilog 用于识别 SystemVerlog 语法;
    • -debug_all 可以将 debug 的信息全部保存下来,比如波形文件;
    • -l com.log 将百衲衣过程生成的日志全部写入 com.log 文件。

    vcs 编译完成后会生成一个 simv 的可执行文件。

    在 terminal 中输入 make 命令:

    1. make; 回到当前目录找Makefile 文件,并all 开始执行;
    2. make all
    3. make compile; make simulate 相当于执行了 make all
    4. make run_dev
    5. make clean; make all

    用户可以指定要 make 的 target, 即要做哪件事情,如:make clean,那么就会执行 Makefile 文件中的 删除文件操作。

    1.1.4 Debug 方法

    1.2 逻辑综合工具 - Design Compile

    逻辑综合(Synthesis)工具主要用于检查 RTL 代码是否可以综合成电路(与门、非门、或门、FF),具体来说的化就是在 RTL Code freeze 之后将前端设计工程师写的RTL code,映射到特定工艺库上(TSMC/UMC/SMIC),通过添加约束信息,对RTL 代码进行逻辑优化,形成门级网表。

    其中约束信息包含(PPA):

    • 时序,比如设计100M;
    • 功耗;
    • 面积;
      PPA 信息添加完成后 工具就会进行优化,优化完成后会形成门级网表。

    主要包括下面三部:
    Trannslation + mapping + optimized

    逻辑综合只做了解

    1.2.1 逻辑综合流程

    • Load library and design
    • Apply timing constraints and design rules constraints
    • Synthesis the design
    • Analyze the results
    • Write out the design data(netlist)

    逻辑综合完成后需要将 netlist + SDC 给到 backend,做物理版图(layout)

    1.2.2 逻辑综合方法

    一、启动 DC 工具

    • 方式1:使用GUI方式;
    • 方式2:involve DC, dc_shell
    • 方式3:工程上常用 dc_shell 吃入一个 *.tcl(tool command language) 脚本,很多工具都支持 tcl 语言,
      dc_shell -f syn.tcl | tee -i syn.log

    二、设置搜索路径(search_patch)

    set_app_var search_patch "$search_patch" ./rtl ./scripts ./libs"
    
    • 1

    三、libray setup(mapping)

    • target libray
    • link libray

    四、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

    • Timing
    • Cell count
    • Area:组合电路的面积,非组合电路的面积,线网的面积

    十、report_timing
    report_timing 静态时序分析

    十一、output

    推荐阅读:
    https://www.bilibili.com/video/BV1WY411D7So?p=9&spm_id_from=pageDriver&vd_source=a354e64412a97e828c2f4b7ebe7c3606

  • 相关阅读:
    java毕业设计软件缺陷管理系统源码+lw文档+mybatis+系统+mysql数据库+调试
    .NET6 + EF Core + MySQL 创建实体和数据库、EFCore 数据迁移
    C++---哈希
    【Kaggle竞赛总结】ECCV2020全球小麦检测(Global Wheat Detection)竞赛总结
    05设计模式-建造型模式-建造者模式
    Java代码审计-Filter核心技术
    Redis面试(二)
    Ue5 websocket控制Character前后左右动作
    Easyx图片操作
    mysql sql解释执行优化
  • 原文地址:https://blog.csdn.net/sinat_32960911/article/details/133700331