<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      同步FIFO

      一、原理介紹

      FIFO(First in, First out),顧名思義是先入先出存儲器,數據的寫入順序和讀出順序一致。

      一條數據流中有兩個模塊A和B,B接收A處理好的數據。假如A處理10個數據的時間,B只能處理5個數據,那么就會丟失5個數據,FIFO的作用就是存儲A處理好的數據,B每處理完一個數據,就從FIFO中取下一個處理。

      1.1 同步FIFO原理

      同步FIFO和異步FIFO相比,核心區別是讀寫使用同一個時鐘。

      核心功能是:作為一個數據緩沖區,允許數據以寫入的順序被讀出,同時通過空滿狀態標志來防止數據讀空或溢出。

      1.2 同步FIFO的構成

      1. 雙端口RAM:讀寫操作獨立,可同時進行;
      2. 寫指針:總是指向下一個寫入數據的存儲地址,如果已經寫入了4個數據,則指向地址5;
      3. 讀指針:總是指向下一個要被讀取數據的存儲地址,如果已經讀取了8個數據,則指向地址9;
      4. 滿標志:寫入的數據加上未被讀出的數據個數等于FIFO的深度,輸出滿信號,阻止上游模塊繼續寫入數據;
      5. 空標志:FIFO中所有數據被讀出后,輸出空信號,阻止下游模塊繼續讀出數據。

      1.3 FIFO的精華:空滿狀態的判斷

      本文使用計數器的方法實現空滿狀態判斷。

      定義一個計數器,計數器的量程等于FIFO的深度,即假設FIFO深度為8,那么計數器的計數范圍是0~8。

      計數器自增自減有如下5種情況:

      1. 上下游模塊同時讀寫FIFO,且FIFO非空非滿:計數器不變;
      2. 上游模塊寫FIFO,且非滿:計數器自增;
      3. 下游模塊讀FIFO,且非空:計數器自減;
      4. 上游模塊寫FIFO,但滿:計數器不變;
      5. 下游模塊讀FIFO,但空:計數器不變。

      空滿狀態判斷:

      • 當計數器等于FIFO深度,輸出滿信號;
      • 當計數器等于0,輸出空信號。

      二、同步FIFO代碼

      2.1 代碼

      `timescale 1ns/1ns
      module sfifo1 #(
          parameter DATA_WIDTH = 8,
          parameter FIFO_DEPTH = 8
      )(
          input  wire                     clk_i       ,
          input  wire                     rstn_i      ,
      
          input  wire                     wr_en_i     ,
          input  wire [DATA_WIDTH-1:0]    wr_data_i   ,
          output wire                     fifo_full_o ,
      
          input  wire                     rd_en_i     ,
          output wire [DATA_WIDTH-1:0]    rd_data_o   ,
          output wire                     fifo_empty_o
      );
      
      reg                         wr_able_d1;
      reg                         rd_able_d1;
      reg  [DATA_WIDTH-1:0]       wr_data_d1;
      reg  [$clog2(FIFO_DEPTH):0] fifo_cnt_d1;
      
      wire                        wr_able  = wr_en_i && !fifo_full_o;
      wire                        rd_able  = rd_en_i && !fifo_empty_o;
      wire [$clog2(FIFO_DEPTH):0] fifo_cnt = (wr_able && rd_able) ? fifo_cnt_d1 :
                                             (wr_able)            ? fifo_cnt_d1 + 1'b1 :
                                             (rd_able)            ? fifo_cnt_d1 - 1'b1 : fifo_cnt_d1;
      
      always @(posedge clk_i or negedge rstn_i) if(!rstn_i) wr_able_d1  <= 1'b0; else wr_able_d1  <= wr_able;
      always @(posedge clk_i or negedge rstn_i) if(!rstn_i) rd_able_d1  <= 1'b0; else rd_able_d1  <= rd_able;
      always @(posedge clk_i or negedge rstn_i) if(!rstn_i) wr_data_d1  <= 'd0 ; else wr_data_d1  <= wr_data_i;
      always @(posedge clk_i or negedge rstn_i) if(!rstn_i) fifo_cnt_d1 <= 'd0 ; else fifo_cnt_d1 <= fifo_cnt;
      //--------------------------------------------------------------------------------------------------
      reg  [DATA_WIDTH-1:0] mem [0:FIFO_DEPTH-1];
      reg  [DATA_WIDTH-1:0]         rd_data;
      reg  [$clog2(FIFO_DEPTH)-1:0] wr_ptr_d2;
      reg  [$clog2(FIFO_DEPTH)-1:0] rd_ptr_d2;
      
      wire [$clog2(FIFO_DEPTH)-1:0] wr_ptr   = (wr_able_d1) ? wr_ptr_d2 + 1'b1 : wr_ptr_d2;
      wire [$clog2(FIFO_DEPTH)-1:0] rd_ptr   = (rd_able_d1) ? rd_ptr_d2 + 1'b1 : rd_ptr_d2;
      
      always @(posedge clk_i)                                                    if(wr_able_d1) mem[wr_ptr_d2] <= wr_data_d1;
      always @(posedge clk_i or negedge rstn_i) if(!rstn_i) rd_data <= 'd0; else if(rd_able_d1) rd_data        <= mem[rd_ptr_d2];
      
      always @(posedge clk_i or negedge rstn_i) if(!rstn_i) wr_ptr_d2 <= 'd0; else wr_ptr_d2  <= wr_ptr;
      always @(posedge clk_i or negedge rstn_i) if(!rstn_i) rd_ptr_d2 <= 'd0; else rd_ptr_d2  <= rd_ptr;
      //--------------------------------------------------------------------------------------------------
      assign fifo_full_o  = (fifo_cnt_d1 == FIFO_DEPTH);
      assign fifo_empty_o = (fifo_cnt_d1 == 0);
      assign rd_data_o    = rd_data;
      
      endmodule
      

      2.2 仿真

      `timescale 1ns/1ns
      module sfifo_tb();
      reg             clk_i;
      reg             rstn_i;
      
      reg             wr_en_i;
      reg  [7:0]      wr_data_i;
      wire            fifo_full_o;
      
      reg             rd_en_i;
      wire [7:0]      rd_data_o;
      wire            fifo_empty_o;
      
      sfifo #(
          .DATA_WIDTH(8),
          .FIFO_DEPTH(8)
      )my_sfifo(
          .clk_i          (clk_i),
          .rstn_i         (rstn_i),
          .wr_en_i        (wr_en_i),
          .wr_data_i      (wr_data_i),
          .fifo_full_o    (fifo_full_o),
          .rd_en_i        (rd_en_i),
          .rd_data_o      (rd_data_o),
          .fifo_empty_o   (fifo_empty_o)
      );
      
      always #10 clk_i = ~clk_i;
      
      initial begin
          clk_i = 0;
          rstn_i = 1;
          wr_en_i = 0;
          wr_data_i = 0;
          rd_en_i = 0;
          #20 rstn_i = 0;
          #20 rstn_i = 1;
          @(negedge clk_i);
      
          pop();
      
          push(1);
      
          pop();
      
          @(posedge clk_i) $finish;
      end
      
      task push(input [7:0] data);
          if(fifo_full_o) begin
              wr_en_i = 1'b1;
              wr_data_i = data;
              $display("FULL! Can't push now!");
              @(negedge clk_i) wr_en_i = 1'b0;
          end else begin
              wr_en_i   = 1'b1;
              wr_data_i = data;
              $display("Push: %0d", data);
              @(negedge clk_i) wr_en_i = 1'b0;
          end
      endtask
      
      task pop();
          if(fifo_empty_o) begin
              $display("Empty! Can't pop now!");
          end else begin
              rd_en_i = 1'b1;
              @(negedge clk_i) 
              rd_en_i = 1'b0;
              $display("Pop: %0d", rd_data_o);
          end
      endtask
      
      endmodule
      

      2.6 部分仿真結果

      寫入8個數據,
      image

      讀出8個數據,
      image

      同時讀寫,
      image

      三、UVM驗證

      3.1 UVM結構

      	                                uvm_test_top
      	                                 (my_casen)
      	                                     |
      	                                    env
      	                                (sfifo_env)
          		                             |
          		         ----------------------------------------
          		        |                    |                   |
          	           agt                  mdl                 scb
                    (sfifo_agent)        (sfifo_model)     (sfifo_scoreboard)
            		        |
          ----------------------------------
         |				|                 |
         drv		     i/o_mon             sqr
      (sfifo_driver) (sfifo_monitor)(sfifo_sequencer)
      

      3.2 Top

      `timescale 1ns/1ns
      `include "uvm_macros.svh"
      
      import uvm_pkg::*;
      `include "sfifo_if.sv"
      `include "sfifo_transaction.sv"
      `include "sfifo_sequencer.sv"
      `include "sfifo_driver.sv"
      `include "sfifo_out_monitor.sv"
      `include "sfifo_in_monitor.sv"
      `include "sfifo_agent.sv"
      `include "sfifo_model.sv"
      `include "sfifo_scoreboard.sv"
      `include "sfifo_env.sv"
      `include "base_test.sv"
      `include "my_case0.sv"
      
      module top;
      reg             clk_i;
      reg             rstn_i;
      
      sfifo_if itf(clk_i, rstn_i);
      
      sfifo #(
          .DATA_WIDTH(8),
          .FIFO_DEPTH(1024)
      )my_sfifo(
          .clk_i          (clk_i),
          .rstn_i         (rstn_i),
          .wr_en_i        (itf.wr_en_i),
          .wr_data_i      (itf.wr_data_i),
          .fifo_full_o    (itf.fifo_full_o),
          .rd_en_i        (itf.rd_en_i),
          .rd_data_o      (itf.rd_data_o),
          .fifo_empty_o   (itf.fifo_empty_o)
      );
      
      initial begin
          $fsdbDumpfile("test.fsdb");
          $fsdbDumpvars(0, top);
      end
      
      initial begin
          clk_i = 0;
          rstn_i = 1;
          #20 rstn_i = 0;
          #20 rstn_i = 1;
      end
      
      always #10 clk_i = ~clk_i;
      
      initial begin
          uvm_config_db#(virtual sfifo_if)::set(null, "uvm_test_top.env.agt.drv", "itf", itf); //pass itf to "uvm_config_db*get()" which has the same third parameter;
          uvm_config_db#(virtual sfifo_if)::set(null, "uvm_test_top.env.agt.o_mon", "itf", itf);
          uvm_config_db#(virtual sfifo_if)::set(null, "uvm_test_top.env.agt.i_mon", "itf", itf);
      end
      
      initial begin
          run_test("my_case0");
      end
      
      endmodule
      

      3.2.1 factory機制

      initial begin
          run_test("sfifo_env");
      end
      

      使用factory機制后,run_test根據字符串自動創建一個sfifo_env的實例,并調用該類里面的所有函數、任務等。每個實例都有一個名字,而經過run_test創建的實例都叫uvm_test_top,可以通過這個實例名env追溯其下游的其它實例(比如uvm_test_top.my_monitor)。

      3.3 Driver

      Driver負責輸出激勵。

      `ifndef SFIFO_DRIVER__SV
      `define SFIFO_DRIVER__SV
      
      class sfifo_driver extends uvm_driver #(sfifo_transaction);
          `uvm_component_utils(sfifo_driver);       //factory mechanism
      
          virtual sfifo_if itf;                     //can't declare interface in class, so here use virtual interface
      
          function new(string name = "sfifo_driver", uvm_component parent = null);
              super.new(name, parent);
          endfunction
      
          extern virtual function void build_phase(uvm_phase phase);
      
          extern virtual task main_phase(uvm_phase phase);
          extern virtual task push(input [7:0] data);
          extern virtual task pop();
      endclass
      
      function void sfifo_driver::build_phase(uvm_phase phase);
          super.build_phase(phase);
      
          if(!uvm_config_db#(virtual sfifo_if)::get(this, "", "itf", itf))
              `uvm_fatal("sfifo_driver", "you should pass itf");
      endfunction
      
      task sfifo_driver::main_phase(uvm_phase phase);
          super.main_phase(phase);
          itf.wr_en_i = 1'b0;                                     
          itf.wr_data_i = 8'd0;
          itf.rd_en_i = 1'b0;
          wait(itf.rstn_i == 0);
          wait(itf.rstn_i == 1);
      
          @(negedge itf.clk_i);
      
          while(1) begin
              seq_item_port.get_next_item(req);
      
              if(req.wr_en_i) begin
                  push(req.rand_pixels);
              end
              if(req.rd_en_i) begin
                  pop();
              end 
      
              seq_item_port.item_done();
          end
      endtask
      
      task sfifo_driver::push(input [7:0] data);
          if(itf.fifo_full_o) begin
              itf.wr_en_i = 1'b1;
              itf.wr_data_i = data;
              //`uvm_info("sfifo_driver", "FULL! Can't push now!", UVM_LOW);
              @(negedge itf.clk_i) itf.wr_en_i = 1'b0;
          end else begin
              itf.wr_en_i   = 1'b1;
              itf.wr_data_i = data;
              `uvm_info("sfifo_driver", $sformatf("Push: %0d", data), UVM_LOW); //system function "$sformatf()" is used to pass a string with parameters 
              @(negedge itf.clk_i) itf.wr_en_i = 1'b0;
          end
      endtask
      
      task sfifo_driver::pop();
          if(itf.fifo_empty_o) begin
              itf.rd_en_i = 1'b1;
              @(negedge itf.clk_i);
              itf.rd_en_i = 1'b0;
              //`uvm_info("sfifo_driver", "Empty! Can't pop now", UVM_LOW);
          end else begin
              itf.rd_en_i = 1'b1;
              @(negedge itf.clk_i) 
              itf.rd_en_i = 1'b0;
              `uvm_info("sfifo_driver", $sformatf("Pop: %0d", itf.rd_data_o), UVM_LOW);
          end
      endtask
      `endif
      

      3.3.1 phase基礎

      1. sfifo_driver繼承uvm_driver的內容,最主要的是main_phase。
      2. phase分為function phase和task phase。function phase不消耗時間,每個時刻只有一個function phase執行;task phase消耗時間,所有task phase并行執行。
      3. 最常用的三個phase是build_phase、connect_phase和main_phase,只有main_phase是task phase。
      4. task phase的核心是reset、configure、main、shutdown四個phase。
      5. 每個class主要要執行的代碼都寫在main_phase里,應該是類似于主函數。
      6. build_phase是function phase,用于通過config_db的set和get操作來數據以及實例化成員變量等。

      3.3.2 factory機制

      `uvm_component_utils(sfifo_driver);       //factory mechanism
      

      將sfifo_driver注冊到uvm內部的表中,這里的主要目的是Top中可以自動聲明類(類由run_test中的字符串決定),并執行類中的task和function。

      需要注意的是,使用run_test聲明類后,Top中不能直接引用類中的變量和函數等(即top.my_driver.xxx),因為run_test聲明了一個脫離top的層次結構。因此uvm引入了uvm_config_db機制,可以將想傳遞的數據通過set發送出去,然后類中通過get接收數據。

      3.3.3 objection機制

      task sfifo_driver::main_phase(uvm_phase phase);
          phase.raise_objection(this);                            //if no use raise_objection, uvm think  
          				                                        //there is no work in the main_phase, so it will be killed.
          /*
          * program
          */
          
          phase.drop_objection(this);                             //raise_objection and drop_objection must appear in pairs
      endtask
      

      最終objection機制不在driver中啟停。

      使用factory機制后,uvm就會通過objection機制控制phase的運行。如果phase中沒有raise_objection,那么uvm就會認為這個phase沒有任何工作需要做,執行完第一次消耗時間之前的代碼就會kill掉這個phase。

      因此要讓phase正常運行,需要在第一個消耗時間的代碼之前phase.raise_objection(this),然后在最后phase.drop_objection(this)。

      raise和drop成對出現。

      在加入sequence機制后,driver就只負責接收transaction,驅動dut,不負責別的工作,所以就不需要這部分了。

      3.3.4 uvm_config_db機制

      3.2.2中提到使用run_test后,Top中不能直接引用類中的變量和函數,需要使用uvm_config_db機制的set和get接收數據。

      //top
      initial begin
          run_test("sfifo_driver");
      end
      
      initial begin
          uvm_config_db#(virtual sfifo_if)::set(null, "uvm_test_top.drv", "itf", itf); //pass itf to "uvm_config_db*get()" which has the same third parameter;
      end
      
      //sfifo_driver
      function void sfifo_driver::build_phase(uvm_phase phase);
          super.build_phase(phase);
      
          if(!uvm_config_db#(virtual sfifo_if)::get(this, "", "itf", itf))
              `uvm_fatal("sfifo_driver", "you should pass itf");
      endfunction
      

      3.3.4.1 set

      uvm_config_db#(virtual sfifo_if)::set(null, "uvm_test_top.drv", "itf", itf); 
      
      1. #()中是要傳遞的數據的數據類型,這里是itf的數據類型virtual sfifo_if(見3.3);
      2. set的第一個參數是uvm_component實例的指針。如果用this,那么第二個參數可以為空;如果為null,會被自動替換為uvm_root::get(),即uvm_top;
      3. 前兩個參數組合起來形成路徑,將數據傳到這個路徑下的phase中,具體看哪個phase可以接收這個數據;
      4. 由于run_test中實例化sfifo_env,所以uvm_test_top.drv就是sfifo_driver,會將數據傳給sfifo_driver;
      5. 找到路徑后,還需要保證set和get的第三個參數一樣,才能完成數據傳輸。

      3.3.4.2 get

      uvm_config_db#(virtual sfifo_if)::get(this, "", "itf", itf)
      

      get的參數含義跟set大致一樣。

      第四個參數是將set的第四個參數賦值給get的第四個參數。

      3.3.5 接收transaction

      seq_item_port.get_next_item(req);
      
      seq_item_port.item_done();
      

      通過第一行代碼將transaction賦給req;

      通過第三行代碼表示完成使用這個transaction驅動dut。

      3.4 Interface

      `ifndef SFIFO_IF__SV
      `define SFIFO_IF__SV
      interface sfifo_if(input clk_i, input rstn_i);
          logic                   wr_en_i;
          logic   [7:0]           wr_data_i;
          logic                   fifo_full_o;
          logic                   rd_en_i;
          logic   [7:0]           rd_data_o;
          logic                   fifo_empty_o;
      endinterface
      `endif
      

      3.5 Transaction

      `ifndef SFIFO_TRANSACTION__SV
      `define SFIFO_TRANSACTION__SV
      
      class sfifo_transaction extends uvm_sequence_item;
          rand bit [7:0]  rand_pixels;
               bit        rstn_i;
          rand bit        wr_en_i;
               bit [7:0]  wr_data_i;
               bit        fifo_full_o;
          rand bit        rd_en_i;
               bit [7:0]  rd_data_o;
               bit        fifo_empty_o;
      
          `uvm_object_utils_begin(sfifo_transaction)
              `uvm_field_int(rand_pixels,  UVM_ALL_ON);
              `uvm_field_int(rstn_i,       UVM_ALL_ON);
              `uvm_field_int(wr_en_i,      UVM_ALL_ON);
              `uvm_field_int(wr_data_i,    UVM_ALL_ON);
              `uvm_field_int(fifo_full_o,  UVM_ALL_ON);
              `uvm_field_int(rd_en_i,      UVM_ALL_ON);
              `uvm_field_int(rd_data_o,    UVM_ALL_ON);
              `uvm_field_int(fifo_empty_o, UVM_ALL_ON);
          `uvm_object_utils_end
      
          constraint pixels_count{
              pixels.size == 640;
          }
      
          function new(string name = "sfifo_transaction");
              super.new(name);
          endfunction
      endclass
      
      `endif
      

      uvm_object_utils和uvm_component_utils的區別:

      1. uvm_object_utils用于數據單元,而uvm_component_utils用于組件;
      2. 使用uvm_object_utils就沒有層次結構的概念,因此new函數只有一個參數string name;
      3. uvm_object_utils有生命周期,生命周期結束后會被銷毀;uvm_component_utils從仿真開始到仿真結束一直存在。

      3.6 Env

      env是uvm驗證平臺所有組件(driver、monitor、agent、scoreboard等)的頂層容器。

      `ifndef SFIFO_ENV__SV
      `define SFIFO_ENV__SV
      
      class sfifo_env extends uvm_env;
          `uvm_component_utils(sfifo_env);
      
          uvm_tlm_analysis_fifo #(sfifo_transaction) agt_mdl_fifo;
          uvm_tlm_analysis_fifo #(sfifo_transaction) agt_scb_fifo;
          uvm_tlm_analysis_fifo #(sfifo_transaction) mdl_scb_fifo;
      
          sfifo_agent      agt;
          sfifo_model      mdl;
          sfifo_scoreboard scb;
      
          function new(string name = "sfifo_env", uvm_component parent = null);
              super.new(name, parent);
          endfunction
      
          extern virtual function void build_phase(uvm_phase phase);
          extern virtual function void connect_phase(uvm_phase phase);
      endclass
      
      function void sfifo_env::build_phase(uvm_phase phase);
          super.build_phase(phase);
              
          agt = sfifo_agent::type_id::create("agt", this);
          mdl = sfifo_model#(.DATA_WIDTH(8), .FIFO_DEPTH(1024))::type_id::create("mdl", this);
          scb = sfifo_scoreboard::type_id::create("scb", this);
          agt.is_active = UVM_ACTIVE;
          agt_mdl_fifo = new("agt_mdl_fifo", this);
          agt_scb_fifo = new("agt_scb_fifo", this);
          mdl_scb_fifo = new("mdl_scb_fifo", this);
          uvm_config_db#(uvm_object_wrapper)::set(this, "agt.sqr.main_phase", "default_sequence", sfifo_sequence::type_id::get());
      endfunction
      
      function void sfifo_env::connect_phase(uvm_phase phase);
          super.connect_phase(phase);
          
          agt.ap.connect(agt_mdl_fifo.analysis_export);
          mdl.port.connect(agt_mdl_fifo.blocking_get_export);
      
          agt.o_ap.connect(agt_scb_fifo.analysis_export);
          scb.from_monitor.connect(agt_scb_fifo.blocking_get_export);
      
          mdl.ap.connect(mdl_scb_fifo.analysis_export);
          scb.from_model.connect(mdl_scb_fifo.blocking_get_export);
      endfunction
      
      `endif
      

      build_phase的執行順序是自頂向下。因此在每個組件的build_phase中定義自己的子節點,那么父組件的build_phase一定先于子組件的build_phase執行,UVM樹就從根到枝葉按序建立,保證UVM樹的建立是完整的。

      建立子節點的方法是在build_phase中使用

      type_name:: type_id::create("name",this);
      
      1. type_name要替換成子節點的類型;
      2. type_id是子節點類型的標識符,直接寫type_id即可;
      3. name是子節點的名字;
      4. this代表子節點掛在該節點下。
      agt = sfifo_agent::type_id::create("agt", this);
      agt.is_active = UVM_ACTIVE;
      

      agt中包含driver和monitor兩種組件,這個agt可能有兩種應用場景:

      1. 用在輸入端,driver和monitor都要使用;
      2. 用在輸出端,只有monitor使用;

      所以為了控制是否使用driver,加入了變量is_active,根據is_active的值,在agt的build_phase中決定是否創建driver節點。

      引入了uvm_tlm_analysis_fifo類型的fifo,用于連接agent和model。

      uvm_config_db#(uvm_object_wrapper)::set(this, "agt.sqr.main_phase", "default_sequence", sfifo_sequence::type_id::get());
      

      這行代碼的功能就是通過default sequence的方式啟動sequence。

      set的前兩個參數指明了sequence把transaction傳給哪個sequencer的main_phase(這個main_phase不需要在sequencer中寫出來),其他的兩個參數以及#()中的參數都是固定的。

      3.7 Monitor

      `ifndef SFIFO_IN_MONITOR__SV
      `define SFIFO_IN_MONITOR__SV
      
      class sfifo_in_monitor extends uvm_monitor;
          `uvm_component_utils(sfifo_in_monitor);
          
          uvm_analysis_port #(sfifo_transaction) ap;
      
          virtual sfifo_if itf;
      
          extern         function      new        (string name = "sfifo_in_monitor", uvm_component parent = null);
          extern virtual function void build_phase(uvm_phase phase);
          extern virtual task          main_phase (uvm_phase phase);
      endclass
      
      function sfifo_in_monitor::new(string name = "sfifo_in_monitor", uvm_component parent = null);
          super.new(name, parent);
      endfunction
      
      function void sfifo_in_monitor::build_phase(uvm_phase phase);
          super.build_phase(phase);
      
          if(!uvm_config_db#(virtual sfifo_if)::get(this, "", "itf", itf))
              `uvm_fatal("sfifo_in_monitor", "error");
          ap = new("ap", this);
      endfunction
      
      task sfifo_in_monitor::main_phase(uvm_phase phase);
          sfifo_transaction tr;
      
          super.main_phase(phase);
          while(1) begin
              @(posedge itf.clk_i);
              tr = new("tr");
              tr.rstn_i       =   itf.rstn_i;
              tr.wr_en_i      =   itf.wr_en_i;
              tr.wr_data_i    =   itf.wr_data_i;
              tr.rd_en_i      =   itf.rd_en_i;
              ap.write(tr);
          end
      endtask
      `endif
      
      `ifndef SFIFO_OUT_MONITOR__SV
      `define SFIFO_OUT_MONITOR__SV
      
      class sfifo_out_monitor extends uvm_monitor;
          `uvm_component_utils(sfifo_out_monitor);
      
          uvm_analysis_port#(sfifo_transaction) ap;
      
          virtual sfifo_if itf;
      
          function new(string name = "sfifo_out_monitor", uvm_component parent = null);
              super.new(name, parent);
          endfunction
      
          extern virtual function void build_phase(uvm_phase phase);
          extern virtual task          main_phase (uvm_phase phase);
      endclass
      
      function void sfifo_out_monitor::build_phase(uvm_phase phase);
          super.build_phase(phase);
      
          if(!uvm_config_db#(virtual sfifo_if)::get(this, "", "itf", itf))
              `uvm_fatal("sfifo_out_monitor", "monitor must pass itf");
          ap = new("ap", this);
      endfunction
      
      task sfifo_out_monitor::main_phase(uvm_phase phase);
          sfifo_transaction tr;
          
          super.main_phase(phase);
          while(1) begin
              @(posedge itf.clk_i);
              tr = new("tr");
              tr.fifo_full_o  = itf.fifo_full_o;
              tr.rd_data_o    = itf.rd_data_o;
              tr.fifo_empty_o = itf.fifo_empty_o;
              ap.write(tr);
          end
      endtask
      
      `endif
      

      在in_monitor中,每個時鐘的上升沿廣播出去一個sfifo_transaction類型的tr,廣播到uvm_tlm_analysis_fifo的輸入端。

      3.7.1 是否raise_objection?

      monitor中不應該使用raise_objection。

      raise_objection的本質是控制仿真phase何時結束的。raise_objection告訴uvm還有組件在運行;drop_objection告訴uvm我運行完了。當所有組件都drop_objection,uvm就結束仿真。

      一般來說,monitor需要時刻接收數據,永不停歇,所以永遠執行不到drop_objection,因此可能導致仿真永遠結束不了。

      并且monitor是一個被動的組件,只接收DUT的輸入輸出端數據,所以不需要monitor決定仿真什么時候結束。可以把monitor想象成一只嗜睡的寵物,driver raise時,相當于人把它給一拳打醒;driver執行到pop時,它睜開眼睛接收信息;driver drop后,它再次陷入沉睡。

      3.8 Agent

      agent是把功能類似或者處理同一種協議的組件封裝起來。

      `ifndef SFIFO_AGENT__SV
      `define SFIFO_AGENT__SV
      
      class sfifo_agent extends uvm_agent;
          `uvm_component_utils(sfifo_agent);
      
          uvm_analysis_port#(sfifo_transaction) ap;
          uvm_analysis_port#(sfifo_transaction) o_ap;
      
          sfifo_driver  drv;
          sfifo_out_monitor o_mon;
          sfifo_in_monitor i_mon;
          sfifo_sequencer sqr;
      
          function new(string name = "sfifo_agent", uvm_component parent = null);
              super.new(name, parent);
          endfunction
      
          extern virtual function void build_phase  (uvm_phase phase); 
          extern virtual function void connect_phase(uvm_phase phase);
      endclass
      
      function void sfifo_agent::build_phase(uvm_phase phase);
          super.build_phase(phase);
      
          if(is_active == UVM_ACTIVE) begin
              drv = sfifo_driver::type_id::create("drv", this);
              sqr = sfifo_sequencer::type_id::create("sqr", this);
          end
      
          i_mon = sfifo_in_monitor::type_id::create("i_mon", this);
          o_mon = sfifo_out_monitor::type_id::create("o_mon", this);
      endfunction
      
      function void sfifo_agent::connect_phase(uvm_phase phase);
          super.connect_phase(phase);
          ap = i_mon.ap;
          o_ap = o_mon.ap;
          if(is_active == UVM_ACTIVE) drv.seq_item_port.connect(sqr.seq_item_export);
      endfunction
      
      `endif
      

      當is_active等于UVM_ACTIVE時,才建立drv節點。由于我這里只有一個agt,并且driver驅動輸入端,所以is_active設為UVM_ACTIVE;i_mon監測輸入端的輸入信號,通過ap廣播出去。

      driver接收transaction端是drv.seq_item_port;sequencer發送transaction端是sqr.seq_item_export。

      3.9 Model

      在《UVM實戰》這本書中,model部分完成的功能是復制monitor組件中的transaction到model中,并在在后續與DUT的結果比較(暫時沒看到后面,所以這里是我猜的),所以其實這里的Model是沒有意義的。但是由于用到了一些TLM的東西,所以我先把自己對于這部分的理解記錄下來,再嘗試寫一個能用的model。

      3.9.1 對于《UVM實戰》的model的理解

      首先要明確一點,這個model的transaction來自于monitor。

      monitor中使用

      uvm_analysis_port #(my_transaction)  ap;
      

      定義了一個非阻塞的廣播port,當tr計算完畢就用ap.write(tr)將tr發送出去。值得注意的是,在agent中

      uvm_analysis_port #(my_transaction)  ap;
      
      function void my_agent::connect_phase(uvm_phase phase);
         super.connect_phase(phase);
         ap = mon.ap;
      endfunction
      

      意味著agent的ap和monitor的ap直接相連。

      model中使用

      uvm_blocking_get_port #(my_transaction)  port;
      port.get(tr);
      

      將monitor發送出來的tr通過阻塞式接收port得到。

      那么tr的發送端和接收端都有了,代表著萬事大吉了嗎?并不是。

      由于發送端是非阻塞的,接收端是阻塞的,所以當發送一個tr時,接收端可能因為當前的工作沒做完而脫不開身,需要一個緩沖區存儲暫時無法被接收的tr,因此在env中引入了

      uvm_tlm_analysis_fifo #(my_transaction) agt_mdl_fifo;
      

      這樣一個fifo。使用該fifo還有一個好處是,接收端完成了工作,想取下一個tr,但是還沒有新的tr被發送出來時,model可以掛起或者去做別的事情。

      fifo應該有輸入端和輸出端,很容易想到,fifo的輸入端連接agent的ap,輸出端連接model的port,

      function void my_env::connect_phase(uvm_phase phase);
         super.connect_phase(phase);
         i_agt.ap.connect(agt_mdl_fifo.analysis_export);       //輸入端
         mdl.port.connect(agt_mdl_fifo.blocking_get_export);   //輸出端
      endfunction
      

      3.9.2 我的model

      `ifndef SFIFO_MODEL__SV
      `define SFIFO_MODEL__SV
      
      class sfifo_model#(int DATA_WIDTH = 8, int FIFO_DEPTH = 1024) extends uvm_component;
          `uvm_component_utils(sfifo_model);
      
          uvm_blocking_get_port #(sfifo_transaction) port;
          uvm_analysis_port #(sfifo_transaction) ap;
      
          extern function new(string name = "sfifo_model", uvm_component parent = null);
          extern function void build_phase(uvm_phase phase);
          extern virtual task main_phase(uvm_phase phase);
      endclass
      
      function sfifo_model::new(string name = "sfifo_model", uvm_component parent = null);
          super.new(name, parent);
      endfunction
      
      function void sfifo_model::build_phase(uvm_phase phase);
          super.build_phase(phase);
          port = new("port", this);
          ap = new("ap", this);
      endfunction
      
      task sfifo_model::main_phase(uvm_phase phase);
          sfifo_transaction tr;
          sfifo_transaction mdl_tr;
      
          bit [DATA_WIDTH-1:0] mem [0:FIFO_DEPTH-1];
          bit                          fifo_full;
          bit                          fifo_empty;
          bit [$clog2(FIFO_DEPTH)-1:0] wr_ptr;
          bit [$clog2(FIFO_DEPTH)-1:0] rd_ptr;
          bit [$clog2(FIFO_DEPTH):0]   fifo_cnt;
          bit [DATA_WIDTH-1:0]         rd_data;
      
          super.main_phase(phase);
          while(1) begin
              mdl_tr = new("mdl_tr");
              port.get(tr);
              if(!tr.rstn_i) begin
                  wr_ptr     =  'd0;
                  rd_ptr     =  'd0;
                  rd_data    =  'd0;
                  fifo_cnt   =  'd0;
                  fifo_full  = 1'b0;
                  fifo_empty = 1'b1;
              end else begin
                  if(!fifo_full  && tr.wr_en_i) mem[wr_ptr] = tr.wr_data_i;
                  if(!fifo_empty && tr.rd_en_i) rd_data     = mem[rd_ptr];
      
                  if(!fifo_full  && tr.wr_en_i) wr_ptr++;
                  if(!fifo_empty && tr.rd_en_i) rd_ptr++;
      
                  if     ((!fifo_full  && tr.wr_en_i) && (!fifo_empty && tr.rd_en_i)) fifo_cnt = fifo_cnt;
                  else if( !fifo_full  && tr.wr_en_i)                                 fifo_cnt++;
                  else if( !fifo_empty && tr.rd_en_i)                                 fifo_cnt--;
                  else                                                                fifo_cnt = fifo_cnt;
      
                  if(fifo_cnt == 0)          fifo_empty = 1;
                  else                       fifo_empty = 0;
                  if(fifo_cnt == FIFO_DEPTH) fifo_full  = 1;
                  else                       fifo_full  = 0;
              end
      
              mdl_tr.rstn_i       = tr.rstn_i;
              mdl_tr.wr_en_i      = tr.wr_en_i;
              mdl_tr.wr_data_i    = tr.wr_data_i;
              mdl_tr.fifo_full_o  =    fifo_full;
              mdl_tr.rd_en_i      = tr.rd_en_i;
              mdl_tr.rd_data_o    =    rd_data;
              mdl_tr.fifo_empty_o =    fifo_empty;
              ap.write(mdl_tr);
          end
      endtask
      `endif
      

      uvm_blocking_get_port類型的port通過get把fifo里的transaction賦給tr。

      3.10 Scoreboard

      `ifndef SFIFO_SCOREBOARD__SV
      `define SFIFO_SCOREBOARD__SV
      
      class sfifo_scoreboard extends uvm_scoreboard;
          `uvm_component_utils(sfifo_scoreboard);
      
          uvm_blocking_get_port #(sfifo_transaction) from_model;
          uvm_blocking_get_port #(sfifo_transaction) from_monitor;
          extern function new(string name = "sfifo_scoreboard", uvm_component parent = null);
          extern virtual function void build_phase(uvm_phase phase);
          extern virtual task main_phase(uvm_phase phase);
      endclass
      
      function sfifo_scoreboard::new(string name = "sfifo_scoreboard", uvm_component parent = null);
          super.new(name, parent);
      endfunction
      
      function void sfifo_scoreboard::build_phase(uvm_phase phase);
          super.build_phase(phase);
          from_monitor = new("from_monitor", this);
          from_model   = new("from_model", this);
      endfunction
      
      task sfifo_scoreboard::main_phase(uvm_phase phase);
          sfifo_transaction mdl_tr;
          sfifo_transaction mnt_tr;
          bit [7:0] mdl_q [$];
          bit [7:0] mnt_q [$];
          int i = 1;
          bit [7:0] mdl_tmp;
          bit [7:0] mnt_tmp;
          super.main_phase(phase);
      
          while(1) begin
              mdl_tr = new("mdl_tr");
              mnt_tr = new("mnt_tr");
              from_model.get(mdl_tr);
              from_monitor.get(mnt_tr);
      
              mdl_q.push_back(mdl_tr.rd_data_o);
              mnt_q.push_back(mnt_tr.rd_data_o);
      
              if(i) begin 
                  mnt_tmp = mnt_q.pop_front();
                  i--;
              end
      
              if(mnt_q.size() > 0) begin
                  mnt_tmp = mnt_q.pop_front();
                  mdl_tmp = mdl_q.pop_front();
                  if(mnt_tmp == mdl_tmp) begin
                      `uvm_info("sfifo_scoreboard", "Success!", UVM_LOW);
                  end else begin
                      `uvm_info("sfifo_scoreboard", "Fail!", UVM_LOW);
                  end
              end
          end
      endtask
      `endif
      

      3.11 Sequencer

      `ifndef SFIFO_SEQUENCER__SV
      `define SFIFO_SEQUENCER__SV
      
      class sfifo_sequencer extends uvm_sequencer #(sfifo_transaction);
          `uvm_component_utils(sfifo_sequencer);
      
          extern function new(string name = "sfifo_sequencer", uvm_component parent = null);
      endclass
      
      function sfifo_sequencer::new(string name = "sfifo_sequencer", uvm_component parent = null);
          super.new(name, parent);
      endfunction
      
      `endif
      

      sequencer是一把槍,sequence是一個彈夾,transaction是子彈。

      sequencer屬于sequence和driver的中間者,因此sequencer算是通道,有一個仲裁機制:

      1. sequence想發送一個transaction,driver想接收一個transaction,sequencer就批準雙方接收;
      2. sequence想發送一個transaction,但driver不想接收,sequencer就讓sequence等;
      3. sequence不想發送transaction,但driver想接收,sequencer就讓driver等。

      sequencer的通道不需要寫什么代碼,只需要在agent中將sequencer的輸出端和driver的輸入端連起來,見3.8;在env中將sequence和sequencer連起來,見3.6。

      3.12 Case and Sequence

      `ifndef MY_CASE0__SV
      `define MY_CASE0__SV
      
      class case0_sequence extends uvm_sequence #(sfifo_transaction);
          `uvm_object_utils(case0_sequence);
          sfifo_transaction tr;
          `uvm_declare_p_sequencer(sfifo_sequencer);
      
          extern function new(string name = "case0_sequence");
          extern virtual task body();
      endclass 
      
      function case0_sequence::new(string name = "case0_sequence");
          super.new(name);
          set_automatic_phase_objection(1);
      endfunction
      
      task case0_sequence::body();
          repeat(10) begin
              `uvm_do_with(tr, {tr.wr_en_i == 1;tr.rd_en_i == 0;});
          end
          repeat(10) begin
              `uvm_do_with(tr, {tr.wr_en_i == 0;tr.rd_en_i == 1;});
          end
          #100;
      endtask
      
      class my_case0 extends base_test;
          `uvm_component_utils(my_case0);
      
          extern function new(string name = "my_case0", uvm_component parent = null);
          extern virtual function void build_phase(uvm_phase phase);
      endclass
      
      function my_case0::new(string name = "my_case0", uvm_component parent = null);
          super.new(name, parent);
      endfunction
      
      function void my_case0::build_phase(uvm_phase phase);
          super.build_phase(phase);
          uvm_config_db#(uvm_object_wrapper)::set(this, "env.agt.sqr.main_phase", "default_sequence", case0_sequence::type_id::get());
      endfunction
      
      `endif
      

      正如3.11中提到sequence是彈夾,所以當子彈transaction打光時,sequence的壽命就結束了,所以sequence應當是`uvm_object_utils。

      sequence的主要作用就是把transaction發送給sequencer,在body中完成。并且在sequence中需要對transaction做一些處理:

      1. new一次transaction的實例;
      2. 對transaction里的隨機變量做隨機化。

      這兩件事都在宏`uvm_do(tr)中執行。

      注意:這個body中要重復發10次不同的tr,難道這10個tr是連續不斷的發送嗎?并不是。每發送完一個tr,uvm_do會停下來,等到driver執行了item_done()后,再再次發送tr或者結束。

      在使用了default_sequence后就去掉了原本env的main_phase中的raise/drop_objection,而這東西是必要的,現在把它們移到sequence中。《UVM實戰》中提到starting_phase的使用功能變化,去問AI后推薦我用自動phase objection機制,即new中的

      set_automatic_phase_objection(1);
      

      作用就是自動在sequence開始時raise,結束時drop。

      為了使自動phase objection機制生效,必須聲明p_sequencer,

      `uvm_declare_p_sequencer(sfifo_sequencer);
      

      3.13 Base_test

      `ifndef BASE_TEST__SV
      `define BASE_TEST__SV
      
      class base_test extends uvm_test;
          `uvm_component_utils(base_test);
      
          sfifo_env env;
      
          extern function new(string name = "base_test", uvm_component parent = null);
          extern virtual function void build_phase(uvm_phase phase);
          extern virtual function void report_phase(uvm_phase phase);
      endclass
      
      function base_test::new(string name = "base_test", uvm_component parent = null);
          super.new(name, parent);
      endfunction
      
      function void base_test::build_phase(uvm_phase phase);
          super.build_phase(phase);
          env = sfifo_env::type_id::create("env", this);
      endfunction
      
      function void base_test::report_phase(uvm_phase phase);
          uvm_report_server server;
          int err_num;
          super.report_phase(phase);
      
          server = get_report_server();
          err_num = server.get_severity_count(UVM_ERROR);
      
          if(!err_num) begin
              `uvm_info("base_test", "PASSED", UVM_LOW);
          end else begin
              `uvm_info("base_test", "FAILED", UVM_LOW);
          end
      endfunction
      
      `endif
      

      其實我覺得base_test的功能完全可以放在case里面,但是AI說如果是大項目,這樣的可重用性太低了。base_test和case可以放在一起理解,case擴展于base_test,他們的主要目的是作為uvm環境的頂層,把sequence和sequencer連起來,當然還要生出env子節點。

      base_test負責完成所有case的公共操作,比如生出env子節點,以及報告uvm是否運行成功。

      case負責完成自己的獨有工作,比如指定自己的sequence與哪一個sequencer連接起來;并且在case文件中順便把case_sequence定義了,每個case都有不同的sequence,負責告訴driver執行不同的功能,最重要的是要在sequence中執行自動phase objection機制。

      四、邏輯綜合

      set search_path [list /opt/synopsys/syn_2022.03/T-2022.03-SP2/libraries/syn]
      set target_library {lsi_10k.db}
      set link_library {* lsi_10k.db}
      set symbol_library {lsi_10k.sdb}
      
      read_verilog ~/Desktop/user/learning/SFIFO/sfifo1.v
      
      current_design sfifo1; #Specify the subsequent logic synthesis commands targeting the sfifo
      link; #Connect all the instances in the design
      uniquify; #Create unique copies for the same module that is instantiated multiple times in the design
      
      set_max_area 0
      
      create_clock -period 20 [get_ports clk_i]
      set_dont_touch_network [get_clocks clk_i]
      
      set_input_delay -max 6 -clock clk_i [remove_from_collection [all_inputs] [get_ports clk_i]]
      set_output_delay -max 6 -clock clk_i [all_outputs]
      
      set_load [load_of lsi_10k/AN2/A] [all_outputs]
      set_driving_cell -lib_cell AN2 [all_inputs]
      
      set_operating_conditions -max WCCOM -min BCCOM
      set_wire_load_model -name "20x20"
      
      set_max_fanout 4 [current_design]
      compile_ultra 
      
      report_timing -max_paths 10 > ~/Desktop/user/learning/SFIFO/reports/timing.rpt
      report_area > ~/Desktop/user/learning/SFIFO/reports/area.rpt
      report_power > ~/Desktop/user/learning/SFIFO/reports/power.rpt
      report_constraint -all_violators > ~/Desktop/user/learning/SFIFO/reports/constraint.rpt
      report_cell > ~/Desktop/user/learning/SFIFO/reports/cell.rpt
      
      exit
      

      最差路徑:

      ****************************************
      Report : timing
              -path full
              -delay max
              -max_paths 10
      Design : sfifo1
      Version: T-2022.03-SP2
      Date   : Thu Oct 30 16:40:11 2025
      ****************************************
      
      Operating Conditions: WCCOM   Library: lsi_10k
      Wire Load Model Mode: top
      
        Startpoint: fifo_cnt_d1_reg[1]
                    (rising edge-triggered flip-flop clocked by clk_i)
        Endpoint: fifo_cnt_d1_reg[2]
                  (rising edge-triggered flip-flop clocked by clk_i)
        Path Group: clk_i
        Path Type: max
      
        Des/Clust/Port     Wire Load Model       Library
        ------------------------------------------------
        sfifo1             20x20                 lsi_10k
      
        Point                                    Incr       Path
        -----------------------------------------------------------
        clock clk_i (rise edge)                  0.00       0.00
        clock network delay (ideal)              0.00       0.00
        fifo_cnt_d1_reg[1]/CP (FD2)              0.00       0.00 r
        fifo_cnt_d1_reg[1]/Q (FD2)               4.26       4.26 r
        U345/Z (NR2P)                            0.51       4.76 f
        U346/Z (ND2)                             2.32       7.08 r
        U350/Z (NR2P)                            0.57       7.65 f
        U351/Z (IVP)                             1.02       8.67 r
        U352/Z (ND2P)                            0.89       9.56 f
        U354/Z (NR2P)                            2.10      11.65 r
        U283/Y (IVDA)                            0.95      12.60 f
        U366/Z (NR2)                             2.67      15.27 r
        U367/Z (AO2)                             1.16      16.43 f
        U267/Z (AO2)                             2.43      18.87 r
        fifo_cnt_d1_reg[2]/D (FD2)               0.00      18.87 r
        data arrival time                                  18.87
      
        clock clk_i (rise edge)                 20.00      20.00
        clock network delay (ideal)              0.00      20.00
        fifo_cnt_d1_reg[2]/CP (FD2)              0.00      20.00 r
        library setup time                      -0.85      19.15
        data required time                                 19.15
        -----------------------------------------------------------
        data required time                                 19.15
        data arrival time                                 -18.87
        -----------------------------------------------------------
        slack (MET)                                         0.28
      

      聲明

      本文的第一節和第二節參考自<掰開揉碎講 FIFO(同步FIFO和異步FIFO) - Doreen的FPGA自留地 - 博客園>
      http://www.rzrgm.cn/DoreenLiu/p/17348480.html

      posted @ 2025-10-10 19:34  Holybanban  閱讀(15)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲国产精品一区二区久| 国产欧美性成人精品午夜| 亚洲gay片在线gv网站| 日韩精品卡1卡2日韩在线| 国产精品成人久久电影| av无码小缝喷白浆在线观看| 精品乱人伦一区二区三区| 日韩成av在线免费观看| 国产成人av乱码在线观看| 一区天堂中文最新版在线 | 国产自产视频一区二区三区| 国产亚洲精品日韩香蕉网| 亚洲精品一区二区天堂| 亚洲综合av男人的天堂| 亚洲精中文字幕二区三区| 在线看无码的免费网站| 亚洲偷偷自拍码高清视频| 国产国产乱老熟女视频网站97| 久久久久香蕉国产线看观看伊| 激情综合网激情综合网激情| 亚洲欧美牲交| 天堂8中文在线最新版在线| 日韩精品卡一卡二卡三卡四| 久久精品久久电影免费理论片| 亚洲国产av无码精品无广告| 国产成人无码av大片大片在线观看| 久草热8精品视频在线观看| 久久精品无码精品免费专区| 亚洲国产免费图区在线视频| 国产一区二区日韩经典| 千阳县| 精品国产污污免费网站入口| 精品国偷自产在线视频99| 四虎影视永久在线精品| 久久成人国产精品免费软件| 永久免费AV无码网站大全| 免费无码中文字幕A级毛片| 九色综合国产一区二区三区| 亚洲日韩av无码中文字幕美国| 亚洲一级特黄大片一级特黄| 日本丰满少妇高潮呻吟|