yosys记录


ALU实验

参考https://nju-projectn.github.io/dlco-lecture-note/exp/03.html
写了个实验3的代码

top.v

`timescale 1ns/1ns
module top (
    input          clk,
    input [2:0]    op,
    input [3:0]    A,
    input [3:0]    B,

    output [3:0]   O,
    output         C_OUT
);

    reg empty_reg = 0;
    always @(posedge clk) begin
        empty_reg <= ~empty_reg;
    end

    wire inv_B_flag;
    assign inv_B_flag = (op == 3'b001) | (op == 3'b110) | (op == 3'b111);

    wire [3:0]  oped_B;
    assign oped_B = (inv_B_flag) ? (~B) : B;

    wire c_in;
    assign c_in = inv_B_flag;

    wire [3:0] s_out;
    wire c_out;
    assign C_OUT = c_out;

    adder u_adder(
        .a_in  ( A        ),
        .b_in  ( oped_B   ),
        .c_in  ( c_in     ),
        .s_out ( s_out    ),
        .c_out ( c_out    )
    );

    assign O =  (op == 3'b000) ? s_out :
                (op == 3'b001) ? s_out :
                (op == 3'b010) ? ~A :
                (op == 3'b011) ? A & B :
                (op == 3'b100) ? A | B :
                (op == 3'b101) ? A ^ B : 
                (op == 3'b110) ? {3'b0, s_out[3]} : 
                (op == 3'b111) ? {3'b0, ~(|s_out)} : 0 ;

    initial begin
        $display("hello, this is adder");

        // $display("[%0t] Tracing to logs/vlt_dump.vcd...\n", $time);
        $dumpfile("logs/vlt_dump.vcd");
        $dumpvars();
        // $display("[%0t] Model running...\n", $time);
    end
endmodule

adder.v
adder.v 使用了超前进位c,参考https://blog.csdn.net/weixin_39921131/article/details/111178335

module adder (
    input   [3:0]       a_in,
    input   [3:0]       b_in,
    input               c_in,

    output  [3:0]       s_out,
    output              c_out
);
wire [3:0]G;
wire [3:0]P;
wire [4:0]C;

assign P = a_in ^ b_in;
assign G = a_in & b_in;

assign C[0] = c_in;
assign C[1] = G[0] | (P[0] & c_in);
assign C[2] = G[1] | (P[1] & G[0]) | (P[1] & P[0] & c_in);
assign C[3] = G[2] | (P[2] & G[1]) | (P[2] & P[1] & G[0]) | (P[2] & P[1] & P[0] & c_in);
assign C[4] = G[3] | (P[3] & G[2]) | (P[3] & P[2] & G[1]) | (P[3] & P[2] & P[1] & G[0]) | (P[3] & P[2] & P[1] & P[0] & c_in);

assign s_out = P ^ C[3:0];
assign c_out = C[4];

endmodule

sim.cpp
sim.cpp 类似于 vivado中的tb.v类似,只不过这个是通过cpp来生成激励
在开启trace之后,如果需要查看波形,则
tfp->dump(contextp->time()); contextp->timeInc(1);
是必须的,只有例化tfp并调用了dump数据才会落实到最后wave.vcd文件中去

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <iostream>
#include <string>

// Include common routines
#include <verilated.h>

// Include model header, generated from Verilating "top.v"
#include "Vtop.h"
#include "verilated.h"
#include "verilated_vcd_c.h"

std::string INT_OP_2_STR_OP[] = {"+", "-", "~", "&", "|", "^", "lt", "eq"};


void print_info(const Vtop* top) {
    std::string str_op = INT_OP_2_STR_OP[(const int)top->op];
    std::bitset<4> bin_A(top->A);
    std::bitset<4> bin_B(top->B);
    std::bitset<4> bin_O(top->O);

    if (str_op == "~") {
        std::cout << str_op << " A" << " = " << str_op << " " << bin_A << " = " << bin_O << std::endl;
    } else {
        std::cout << "A " <<  str_op << " B" << " = " ;
        std::cout << bin_A << " " << str_op << " " << bin_B << " = " << bin_O << std::endl;
    }
}

int main(int argc, char** argv) {
    // See a similar example walkthrough in the verilator manpage.

    // This is intended to be a minimal example.  Before copying this to start a
    // real project, it is better to start with a more complete example,
    // e.g. examples/c_tracing.

    // Construct a VerilatedContext to hold simulation time, etc.
    VerilatedContext* contextp = new VerilatedContext;
    VerilatedVcdC* tfp = new VerilatedVcdC(); //初始化VCD对象指针

    contextp->commandArgs(argc, argv);
    Vtop* top = new Vtop{contextp};

    contextp->traceEverOn(true); //打开追踪功能
    top->trace(tfp, 0); 
    tfp->open("wave.vcd"); //设置输出的文件wave.vcd

    top->op = 0b000;
    top->A = 0b1010;
    top->B = 0b0101;
    top->eval();
    print_info(top);
    tfp->dump(contextp->time());
    contextp->timeInc(1);

    top->op = 0b001;
    top->A = 0b1010;
    top->B = 0b1101;
    top->eval();
    print_info(top);
    tfp->dump(contextp->time());
    contextp->timeInc(1);

    top->op = 0b010;
    top->A = 0b1010;
    top->B = 0b1101;
    top->eval();
    print_info(top);
    tfp->dump(contextp->time());
    contextp->timeInc(1);

    top->op = 0b011;
    top->A = 0b1010;
    top->B = 0b1101;
    top->eval();
    print_info(top);
    tfp->dump(contextp->time());
    contextp->timeInc(1);

    top->op = 0b100;
    top->A = 0b1010;
    top->B = 0b1101;
    top->eval();
    print_info(top);
    tfp->dump(contextp->time());
    contextp->timeInc(1);

    top->op = 0b101;
    top->A = 0b1010;
    top->B = 0b1101;
    top->eval();
    print_info(top);
    tfp->dump(contextp->time());
    contextp->timeInc(1);

    printf("\nis lt: \n");

    top->op = 0b110;
    top->A = 0b1010;
    top->B = 0b1101;
    top->eval();
    print_info(top);
    tfp->dump(contextp->time());
    contextp->timeInc(1);

    top->op = 0b110;
    top->A = 0b1110;
    top->B = 0b1001;
    top->eval();
    print_info(top);
    tfp->dump(contextp->time());
    contextp->timeInc(1);

    top->op = 0b110;
    top->A = 0b1110;
    top->B = 0b1110;
    top->eval();
    print_info(top);
    tfp->dump(contextp->time());
    contextp->timeInc(1);

    printf("\nis eq: \n");

    top->op = 0b111;
    top->A = 0b1010;
    top->B = 0b1101;
    top->eval();
    print_info(top);
    tfp->dump(contextp->time());
    contextp->timeInc(1);

    top->op = 0b111;
    top->A = 0b1110;
    top->B = 0b1001;
    top->eval();
    print_info(top);
    tfp->dump(contextp->time());
    contextp->timeInc(1);

    top->op = 0b111;
    top->A = 0b1110;
    top->B = 0b1110;
    top->eval();
    print_info(top);
    tfp->dump(contextp->time());
    contextp->timeInc(1);

    top->eval();
    tfp->dump(contextp->time());
    contextp->timeInc(1);

    // Final model cleanup
    top->final();
    tfp->close();

    // Destroy model
    delete top;
    delete contextp;

    // Return good completion status
    return 0;
}

Makefile
此处Makefile是通过verilator生成,sim的可执行文件top
其中–trace参数在需要查看波形的时候必须要提供
verilator的结果将全部存放在build/目录下

TOPNAME = top
INC_PATH ?=

ifeq ($(VERILATOR_ROOT),)
VERILATOR = verilator
else
export VERILATOR_ROOT
VERILATOR = $(VERILATOR_ROOT)/bin/verilator
endif

VERILATOR_CFLAGS += -MMD --build -cc --exe\
                --x-assign fast --x-initial fast \
                --assert --trace -Wall --build --coverage
# -O3

BUILD_DIR = ./build
OBJ_DIR = $(BUILD_DIR)/obj_dir
LOG_DIR = $(BUILD_DIR)/log
BIN = $(BUILD_DIR)/$(TOPNAME)

default: $(BIN)

$(shell mkdir -p $(BUILD_DIR))
$(shell mkdir -p $(LOG_DIR))

# project source
VSRCS = $(shell find $(abspath ./vsrc) -name "*.v")
CSRCS = $(shell find $(abspath ./csrc) -name "*.c" -or -name "*.cc" -or -name "*.cpp")

# rules for verilator
INCFLAGS = $(addprefix -I, $(INC_PATH))
CXXFLAGS += $(INCFLAGS) -DTOP_NAME="\"V$(TOPNAME)\""


$(BIN): $(VSRCS) $(CSRCS)
    @rm -rf $(OBJ_DIR)
    $(VERILATOR) $(VERILATOR_CFLAGS) \
        --top-module $(TOPNAME) $^ \
        $(addprefix -CFLAGS , $(CXXFLAGS)) $(addprefix -LDFLAGS , $(LDFLAGS)) \
        --Mdir $(OBJ_DIR) \
        -o $(abspath $(BIN))

all: default

run: $(BIN)
    @$^

clean:
    rm -rf $(BUILD_DIR)

.PHONY: default all clean run

iEDAMakefile
这个Makefile拷贝了yosys-sta的环境到本目录
make -f iEDAMakefile init
之后的make -f iEDAMakefile sta是通过iEDA对verilator生成的综合后文件做静态时序分析
所有结果将存放在result/目录下

PROJ_PATH = $(shell pwd)
PROJ_NAME = $(notdir $(PROJ_PATH))
YOSYS_PATH = /home/odjvnrij/yosys-sta
IEDA_PATH = /home/odjvnrij/yosys-sta/bin/iEDA


DESIGN ?= top
export CLK_FREQ_MHZ ?= 500

LIB_DIR = $(PROJ_PATH)/lib
SCRIPT_DIR = $(PROJ_PATH)/scripts
NANGATE45_DIR = $(PROJ_PATH)/nangate45
RESULT_DIR = $(PROJ_PATH)/result/$(DESIGN)-$(CLK_FREQ_MHZ)MHz

SDC_FILE ?= $(PROJ_PATH)/$(DESIGN).sdc
RTL_FILES ?= $(shell find $(PROJ_PATH)/vsrc -name "*.v")

NETLIST_SYN_V   = $(RESULT_DIR)/$(DESIGN).netlist.syn.v
NETLIST_FIXED_V = $(RESULT_DIR)/$(DESIGN).netlist.fixed.v
TIMING_RPT = $(RESULT_DIR)/$(DESIGN).rpt

init: 
    mkdir -p $(NANGATE45_DIR)
    cp -r $(YOSYS_PATH)/nangate45/* $(NANGATE45_DIR)

    mkdir -p $(SCRIPT_DIR)
    cp -r $(YOSYS_PATH)/scripts/* $(SCRIPT_DIR)

syn: init $(NETLIST_SYN_V)
$(NETLIST_SYN_V): $(RTL_FILES) $(SCRIPT_DIR)/yosys.tcl
    mkdir -p $(@D)
    echo tcl $(SCRIPT_DIR)/yosys.tcl $(DESIGN) \"$(RTL_FILES)\" $@ | yosys -l $(@D)/yosys.log -s -

fix-fanout: init $(NETLIST_FIXED_V)
$(NETLIST_FIXED_V): $(SCRIPT_DIR)/fix-fanout.tcl $(SDC_FILE) $(NETLIST_SYN_V)
    $(IEDA_PATH) -script $^ $(DESIGN) $@ 2>&1 | tee $(RESULT_DIR)/fix-fanout.log

sta: init $(TIMING_RPT)
$(TIMING_RPT): $(SCRIPT_DIR)/sta.tcl $(SDC_FILE) $(NETLIST_FIXED_V)
    $(IEDA_PATH) -script $^ $(DESIGN) 2>&1 | tee $(RESULT_DIR)/sta.log

clean:
    -rm -rf result/


.PHONY: syn fix-fanout sta clean

top.sdc
约束文件,貌似set_input_delay中只支持固定值,不支持类似-min -max等参数
set_output_delay也不支持,会报错

set clk_port_name clk
set CLK_FREQ_MHZ 500

if {[info exists env(CLK_FREQ_MHZ)]} {
  set CLK_FREQ_MHZ $::env(CLK_FREQ_MHZ)
} else {
  puts "Warning: Environment CLK_FREQ_MHZ is not defined. Use $CLK_FREQ_MHZ MHz by default."
}
set clk_io_pct 0.2

set clk_port [get_ports $clk_port_name]

create_clock -name core_clock -period [expr 1000.0 / $CLK_FREQ_MHZ] $clk_port
set_clock_uncertainty -setup 0.2 [get_clocks core_clock]
set_clock_uncertainty -hold 0.1 [get_clocks core_clock]


set_input_delay 1 -clock [get_clocks core_clock] [get_ports {op[0]}]
set_input_delay 1 -clock [get_clocks core_clock] [get_ports {op[1]}]
set_input_delay 1 -clock [get_clocks core_clock] [get_ports {op[2]}]

set_input_delay 2 -clock [get_clocks core_clock] [get_ports {A[0]}] 
set_input_delay 2 -clock [get_clocks core_clock] [get_ports {A[1]}] 
set_input_delay 2 -clock [get_clocks core_clock] [get_ports {A[2]}] 
set_input_delay 2 -clock [get_clocks core_clock] [get_ports {A[3]}] 

set_input_delay 1 -clock [get_clocks core_clock] [get_ports {B[0]}] 
set_input_delay 1 -clock [get_clocks core_clock] [get_ports {B[1]}] 
set_input_delay 1 -clock [get_clocks core_clock] [get_ports {B[2]}] 
set_input_delay 1 -clock [get_clocks core_clock] [get_ports {B[3]}] 

ALU实验静态时序分析结果

make -f iEDAMakefile sta之后,会结合工艺库进行编译,在result/top.v就能看到结合工艺库之后的门级网表
由于没有使用时序电路,只加了个寄存器翻转的always电路,中间可能综合器给优化掉了,所有没有分析出时钟路径


nangate45库

nangate45/lib/merged.lib 存放了工艺库下所有期间的各种参数

作者:odjvnrij  创建时间:2024-09-21 16:14
最后编辑:odjvnrij  更新时间:2024-11-01 21:09