当前位置: 首页 > news >正文

【UVM项目实战】异步fifo—uvm项目结构以及uvm环境搭建

本文章同步到我的个人博客网站:ElemenX-King:【UVM项目实战】异步fifo—uvm项目结构以及uvm环境搭建 希望大家能使用此网站来进行浏览效果更佳!!!


目录

  • 一、异步FIFO
    • 1.1 异步FIFO的定义
    • 1.2 亚稳态
    • 1.3 异步FIFO关键技术一
    • 1.3 异步FIFO关键技术二
    • 1.4 FIFO逻辑图
  • 二、UVM结构
    • 2.1 interface
    • 2.2 transaction
    • 2.3 driver
    • 2.3 moniter
    • 2.4 sequencer
    • 2.5 agent
    • 2.6 model
    • 2.7 scoreboard
    • 2.8 env
    • 2.9 base_test
    • 2.10 case_sequence
    • 2.11 top_tb
  • 三、UVM仿真环境的搭建
    • 3.1 Makefile文件的编写
    • 3.2 filelist文件的编写
    • 3.3 另辟蹊径
  • 总结

一、异步FIFO

1.1 异步FIFO的定义

异步时序设计指的是在设计中有两个或以上的时钟, 且时钟之间是同频不同相或不同频率的关系。而异步时序设计的关键就是把数据或控制信号正确地进行跨时钟域传输。
一个异步 FIFO 一般由如下部分组成:

  1. Memory, 作为数据的存储器;
  2. 写逻辑部分,主要负责产生写信号和地址;
  3. 读逻辑部分,主要负责产生读信号和地址;
  4. 地址比较部分,主要负责产生 FIFO 空、满的标志。

跟普通的FIFO相比,异步FIFO实际上多了读写地址的跨时钟域同步的逻辑,以及两个时钟域中读写信号的比较逻辑。

1.2 亚稳态

每一个触发器都有其规定的建立(setup)和保持(hold)时间参数, 在这个时间参数内, 输入信号在时钟的上升沿是不允许发生变的。 如果在信号的建立时间中对其进行采样, 得到的结果将是不可预知的,即亚稳态。
为了避免亚稳态。采用双锁存器可以改善这一问题:
双锁存器避免亚稳态

时钟域B两级同步的寄存器跟时钟域A的输出寄存器之间不能有组合逻辑。组合逻辑电路各个输入信号的不一致性以及组合逻辑内部路径的延时时间不一样,运算后的信号存在毛刺。
注意1

1.3 异步FIFO关键技术一

这里用到了一个很重要的概念“回卷”,通常判断读写的地址是否相同来判断空和满,这里使用回卷技术,在深度为8的fifo中多一位来代表回卷位,当fifo溢出之后,回卷位会被置1,当读时钟和写时钟的回卷位不同而其他位相同时,表示fifo已经满,因为写地址在溢出后的位置,而读时钟在溢出前。
回卷

1.3 异步FIFO关键技术二

将满和将空信号实际上表示更加保守的满和空信号。基本思路是,设定一个间隔值,当读写地址之间的间隔小于或等于该间隔就产生将空或将满信号。
对于异步FIFO而言,由于同步过来的地址信号都是格雷码表示的,我们不能直接用格雷码去判断上述的这个间隔,所以需要先对接受到的格雷码进行解码变为二进制,再和当前时钟域下的另一个地址进行将满和将空的生成。

1.4 FIFO逻辑图

FIFO
下面的四个寄存器一方面是对其时序(但是我不理解为什么时序是对其的)另一方面是防止时钟域带来的亚稳态。


二、UVM结构

该项目的UVM包括以下几个文件:
fifo_if.sv
fifo_case0.sv
base_test.sv
top_tb.sv
fifo_driver.sv
fifo_model.sv
fifo_transaction.sv
fifo_in_monitor.sv
fifo_env.sv
fifo_scoreboard.sv
fifo_chk_rst.sv
fifo_in_sequencer.sv
fifo_in_agent.sv
my_env.sv
fifo_out_monitor.sv
fifo_out_agent.sv
下面是本次UVM的整体框架:
在这里插入图片描述
现在对每一个文件进行解释。

2.1 interface

该文件主要是用于连接DUT的物理信号与UVM的事件信号,起主要是在top_tb中进行定义和连接,然后通过uvm_config_db进行定点发送。

`ifndef FIFO_IF__SV
`define FIFO_IF__SV// 括号里面是时钟信号
interface fifo_if(input wclk, input rclk, input wreset_b, input rreset_b);logic write,read;logic [31 : 0] wdata;logic [31 : 0] rdata;wire  wfull,rempty; // logic/***********时钟约束************/// wdata为inoutclocking ckw @(posedge wclk);input  wfull;inout  write;inout  wdata;endclocking// wdata为inputclocking ckim @(posedge wclk);input wfull;              inout  write;             input  wdata;endclocking// clocking ckom @(posedge rclk);input  rempty;inout  read;input  rdata;endclocking/***********方向约束************/// 普通模式modport DUT(input write,input read,input wdata,output rdata,output wfull,output rempty);// 读取模式modport DRV(clocking ckw,input read,input rdata,input rempty);modport OMON(clocking ckom,input wfull,input write,input wdata);
endinterface //interfacename`endif

clocking主要是定义每个logic信号的方向,同时制定该信号同步的时钟域。括号里信号的作用是控制 clocking 块内的所有信号的同步时序。时钟信号 的上升沿会触发对这些信号的采样或更新
modport的作用是定义了一个接口的访问模式,指定了如何访问时钟块 ckw 和接口中的信号。具体来说,它允许从外部访问 read、rdata 和 rempty 信号,并且会在时钟块 ckw 中进行同步。

2.2 transaction

transaction主要是用于对信号进行打包操作,本项目只有一个输入data_in,因此只需要生成一个随机数据。同时基于约束并注册(这是基本操作)。

`ifndef FIFO_TRANSACTION__SV
`define FIFO_TRANSACTION__SVclass fifo_transaction extends uvm_sequence_item;// 产生的随机数据rand bit[31:0] data_in[];// 约束constraint data_in_c {soft data_in.size inside {[1:300]};}// 将数据加入注册`uvm_object_utils_begin(fifo_transaction)`uvm_field_array_int(data_in,UVM_ALL_ON)`uvm_object_utils_end// 构造函数function new(string name = "fifo_transaction");super.new(name);endfunction
endclass
`endif

2.3 driver

驱动器是UVM中的核心,代码如下所示:

`ifndef FIFO_DRIVER__SV
`define FIFO_DRIVER__SVclass fifo_driver extends uvm_driver#(fifo_transaction);virtual fifo_if vif;//define 功能覆盖率 是否达到 空 满 状态covergroup cov_label;option.per_instance = 1;option.auto_bin_max = 2;coverpoint vif.wfull;coverpoint vif.rempty;endgroup// 注册`uvm_component_utils(fifo_driver)// 构造函数function new(string name = "fifo_driver", uvm_component parent = null);super.new(name, parent);cov_label = new(); // 创建覆盖率endfunction// 初始化函数virtual function void build_phase(uvm_phase phase);super.build_phase(phase);if(!uvm_config_db#(virtual fifo_if)::get(this,"","vif",vif))`uvm_fatal("fifo_driver","virtual interface must be set for vif!!!")endfunctionextern task main_phase(uvm_phase phase);extern task drive_one_pkt(fifo_transaction tr);
endclasstask fifo_driver::main_phase(uvm_phase phase);`uvm_info("fifo_driver","begin!",UVM_LOW)while(1) beginseq_item_port.get_next_item(req); // 开始drive_one_pkt(req);seq_item_port.item_done(); // 结束end
endtasktask fifo_driver::drive_one_pkt(fifo_transaction tr);int data_size,j;data_size = tr.data_in.size();`uvm_info("fifo_driver","begin to drive one pkt",UVM_LOW)for(int i = 0; i < data_size; i++) begin@(vif.ckw); // 表示ckw中的内容发生变化if((!vif.ckw.wfull) && (vif.ckw.write == 1)) begin cov_label.sample(); vif.ckw.wdata <= tr.data_in[i];`uvm_info("fifo_driver",$sformatf("%0d number is sent,number is %0h",j++,vif.ckw.wdata),UVM_LOW) end  else if((!vif.ckw.wfull) && (vif.ckw.write == 0)) beginvif.ckw.write <= 1;i--;end else begin // 满了vif.ckw.write <= 0;i--;endend  
endtask`endif

关于覆盖率这里我们先不做介绍,后续会单独做期来讲解覆盖率

需要注意几点,首先在初始化函数中使用uvm_config_db来获取DUT信号,用于对DUT的信号进行写入与读取。
其次是在main_phase中,使用driver自带的端口seq_item_port来获得一个包(就是刚才说的随机数据),这个端口会在agent中进行连接,来源就是sequencer,然后将包中的信息发送到DUT上。
最后就是在drive_one_pkt中,使用@(vif.ckw);来捕获时钟上升沿,然后通过wfull和write的状态来决定是否发送数据。

可以看到,write和wdata信号均有写入和读取的操作,因此在ckw中设置为inout信号

2.3 moniter

moniter的作用在于接受DUT的信息,有在输入和输出都有一个moniter。

`ifndef FIFO_IN_MONITOR__SV
`define FIFO_IN_MONITOR__SV
class fifo_in_monitor extends uvm_monitor;virtual fifo_if vif;uvm_analysis_port #(fifo_transaction)  ap;// 注册`uvm_component_utils(fifo_in_monitor)// 构造函数function new(string name = "fifo_in_monitor", uvm_component parent = null);super.new(name, parent);endfunction// 初始化函数virtual function void build_phase(uvm_phase phase);super.build_phase(phase);if(!uvm_config_db#(virtual fifo_if)::get(this, "", "vif", vif))`uvm_fatal("fifo_in_monitor", "virtual interface must be set for vif!!!")ap = new("ap", this);endfunctionextern virtual task main_phase(uvm_phase phase);extern task collect_one_pkt(fifo_transaction tr);
endclasstask fifo_in_monitor::main_phase(uvm_phase phase);fifo_transaction tr;repeat(2) begintr = new("tr");#4.001;collect_one_pkt(tr);ap.write(tr);end
endtasktask fifo_in_monitor::collect_one_pkt(fifo_transaction tr);int j,k;`uvm_info("in_monitor","begin to collect one pkt",UVM_LOW) while(1) begin @(vif.ckim);  if((!vif.ckim.wfull) && (vif.ckim.write == 1)) begin // 符合要求tr.data_in[j] = vif.ckim.wdata;`uvm_info("in_monitor",$sformatf("%0d number is received,number is %0h",k++,vif.ckim.wdata),UVM_LOW)if(j == 199)  break;   //seq1 发送200个数据 j++;end end`uvm_info("in_monitor","end collect one pkt",UVM_LOW)//tr.print();
endtask`endif

与driver一样,也要使用uvm_config_db来获取DUT信号。
但是这里定义了一个uvm_analysis_port端口其作用就是把接收到的数据transcation发送出。
这里的#4.001大概率是为了等待刚刚发送的数据发送,这里挖一个坑
后面的collect_one_pkt与前面相同,当符合要求后将wdata读到data_in中,但是这里使用的是ckim,原因如上所示。
输出的out_moniter与输入有两个区别,一个是没有#4.001,第二个就是使用的读时钟块ckom以及传输的是rdata。

2.4 sequencer

sequencer相当于sequence的下手,用于帮sequence传递数据的。

`ifndef FIFO_SEQUENCER__SV
`define FIFO_SEQUENCER__SVclass fifo_sequencer extends uvm_sequencer #(fifo_transaction);`uvm_component_utils(fifo_sequencer)function new(string name = "fifo_sequencer",uvm_component parent = null);super.new(name,parent);endfunctiontask main_phase(uvm_phase phase);`uvm_info("fifo_sequencer","main_phase begin",UVM_LOW)endtask
endclass`endif

代码里没有要讲的,仅仅就定义了一下自己。

2.5 agent

agent的作用仅仅就是将三巨头sequencer、driver和monitor进行连接。

`ifndef FIFO_IN_AGENT__SV
`define FIFO_IN_AGENT__SVclass fifo_in_agent extends uvm_agent ;// 三巨头fifo_sequencer       sqr;fifo_driver          drv;fifo_in_monitor      mon;uvm_analysis_port #(fifo_transaction)  ap; // 指向外面`uvm_component_utils(fifo_in_agent)function new(string name = "fifo_in_agent", uvm_component parent);super.new(name, parent);endfunction extern virtual function void build_phase(uvm_phase phase);extern virtual function void connect_phase(uvm_phase phase);
endclassfunction void fifo_in_agent::build_phase(uvm_phase phase);super.build_phase(phase);if(is_active == UVM_ACTIVE) begindrv = fifo_driver::type_id::create("i_drv",this);sqr = fifo_sequencer::type_id::create("i_sqr",this); endmon = fifo_in_monitor::type_id::create("i_mon",this);
endfunctionfunction void fifo_in_agent::connect_phase(uvm_phase phase);super.connect_phase(phase);if(is_active == UVM_ACTIVE) begindrv.seq_item_port.connect(sqr.seq_item_export);endap = mon.ap; // 调用mon的ap指针
endfunction	
`endif

需要注意的是,这里和moniter一样定义了一个uvm_analysis_port 端口,并且在connect_phase中使用ap = mon.ap;的方式将mon的ap的指针指向了该ap,实现外部直接调用mon.ap。
is_active 是agent的固有方法,用来区分输入和输出。但是本项目中还定义了一个out_agent,这个方法应该冗余了。在out_agent中的mon变为out_mon,其余不变。
在connect_phase连接中将sqr的输出与drv的输入连接,等待sqr提供数据。

2.6 model

model按理来说应该是一个参考,用来实现与DUT相同的操作,但这里仅仅定义了两个端口,从port端口到ap。

`ifndef FIFO_MODEL__SV
`define FIFO_MODEL__SVclass fifo_model extends uvm_component;uvm_blocking_get_port #(fifo_transaction)  port;uvm_analysis_port #(fifo_transaction)  ap;extern function new(string name, uvm_component parent);extern function void build_phase(uvm_phase phase);extern virtual  task main_phase(uvm_phase phase);`uvm_component_utils(fifo_model)
endclass function fifo_model::new(string name, uvm_component parent);super.new(name, parent);
endfunction function void fifo_model::build_phase(uvm_phase phase);super.build_phase(phase);port = new("port", this);ap = new("ap", this);
endfunctiontask fifo_model::main_phase(uvm_phase phase);fifo_transaction tr;fifo_transaction new_tr;super.main_phase(phase);while(1) beginport.get(tr);new_tr = new("new_tr");new_tr.copy(tr);`uvm_info("fifo_model", "get one transaction, copy and print it:", UVM_LOW)new_tr.print();ap.write(new_tr);end
endtask
`endif

2.7 scoreboard

scoreboard用于收集来自agent以及model的数据。

`ifndef FIFO_SCOREBOARD__SV
`define FIFO_SCOREBOARD__SV
class fifo_scoreboard extends uvm_scoreboard;fifo_transaction  expect_queue[$];uvm_blocking_get_port #(fifo_transaction)  exp_port;uvm_blocking_get_port #(fifo_transaction)  act_port;`uvm_component_utils(fifo_scoreboard)extern function new(string name, uvm_component parent = null);extern virtual function void build_phase(uvm_phase phase);extern virtual task main_phase(uvm_phase phase);
endclass function fifo_scoreboard::new(string name, uvm_component parent = null);super.new(name, parent);
endfunction function void fifo_scoreboard::build_phase(uvm_phase phase);super.build_phase(phase);exp_port = new("exp_port", this);act_port = new("act_port", this);
endfunction task fifo_scoreboard::main_phase(uvm_phase phase);fifo_transaction  get_expect,  get_actual, tmp_tran;bit result;super.main_phase(phase);fork while (1) beginexp_port.get(get_expect);expect_queue.push_back(get_expect);endwhile (1) beginact_port.get(get_actual);if(expect_queue.size() > 0) begintmp_tran = expect_queue.pop_front();result = get_actual.compare(tmp_tran);if(result) begin `uvm_info("fifo_scoreboard", "Compare SUCCESSFULLY", UVM_LOW);endelse begin`uvm_error("fifo_scoreboard", "Compare FAILED");$display("the expect pkt is");tmp_tran.print();$display("the actual pkt is");get_actual.print();endendelse begin`uvm_error("fifo_scoreboard", "Received from DUT, while Expect Queue is empty");$display("the unexpected pkt is");get_actual.print();end endjoin
endtask
`endif

这里定义了两个接受端口,分别是exp_port以及act_port,其中exp_port来自fifo_model的输入,表示参考的数据。act_port来自o_agt,表示DUT的输出值。将这两个数值进行比较,从而判断程序是否发生错误。

2.8 env

env是整个UVM最接近顶层的存在,其主要包括三个部分:agent、model以及scoreboard。

`ifndef MY_ENV__SV
`define MY_ENV__SVclass my_env extends uvm_env;fifo_in_agent     i_agt;fifo_out_agent    o_agt;fifo_model        mdl;fifo_scoreboard   scb;uvm_tlm_analysis_fifo #(fifo_transaction) agt_scb_fifo;uvm_tlm_analysis_fifo #(fifo_transaction) agt_mdl_fifo;uvm_tlm_analysis_fifo #(fifo_transaction) mdl_scb_fifo;function new(string name = "my_env", uvm_component parent);super.new(name, parent);endfunctionvirtual function void build_phase(uvm_phase phase);super.build_phase(phase);i_agt = fifo_in_agent::type_id::create("i_agt", this);o_agt = fifo_out_agent::type_id::create("o_agt", this);i_agt.is_active = UVM_ACTIVE;o_agt.is_active = UVM_PASSIVE;mdl = fifo_model::type_id::create("mdl", this);scb = fifo_scoreboard::type_id::create("scb", this);agt_scb_fifo = new("agt_scb_fifo", this);agt_mdl_fifo = new("agt_mdl_fifo", this);mdl_scb_fifo = new("mdl_scb_fifo", this);endfunctionextern virtual function void connect_phase(uvm_phase phase);`uvm_component_utils(my_env)
endclassfunction 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);mdl.ap.connect(mdl_scb_fifo.analysis_export);scb.exp_port.connect(mdl_scb_fifo.blocking_get_export);o_agt.ap.connect(agt_scb_fifo.analysis_export);scb.act_port.connect(agt_scb_fifo.blocking_get_export); 
endfunction`endif

这里面共定义了三个fifo,用来构建三者之间数据的缓冲,具体传输方向如下所示:
fifo_model -> mdl_scb_fifo -> fifo_scoreboard
fifo_out_agent-> agt_scb_fifo-> fifo_scoreboard
fifo_in_agent -> agt_scb_fifo -> fifo_model
从这里看也看到,在fifo_model 中传递的数据,其实是来自于fifo_in_agent ,当然,最为fifo输入和输出的数据确实是相同的。

2.9 base_test

base_test才是UVM真正的顶层,他的主要作用很简单,除了例化一个env之外,还可以进行项目错误的统计工作。

`ifndef BASE_TEST__SV
`define BASE_TEST__SVclass base_test extends uvm_test;my_env         env;function new(string name = "base_test", uvm_component parent = null);super.new(name,parent);endfunctionextern virtual function void build_phase(uvm_phase phase);extern virtual function void report_phase(uvm_phase phase);`uvm_component_utils(base_test)
endclassfunction void base_test::build_phase(uvm_phase phase);super.build_phase(phase);env  =  my_env::type_id::create("env", this);
endfunctionfunction 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 != 0) begin$display("TEST CASE FAILED");endelse begin$display("TEST CASE PASSED");end
endfunction`endif

在report_phase中定义了一个uvm_report_server 用于检测错误,根据错误的数目输出验证通过与否。

2.10 case_sequence

这个相当于独立于整个UVM树之外的一个部分,用于产生激励信号。

`ifndef FIFO_CASE0__SV
`define FIFO_CASE0__SVclass case0_sequence extends uvm_sequence #(fifo_transaction);fifo_transaction trans;`uvm_object_utils(case0_sequence)function new(string name = "case0_sequence");super.new(name);endfunctionvirtual task body();repeat(2) begin`uvm_info("case0_sequence","generate one transaction!",UVM_LOW)`uvm_do_with(trans,{trans.data_in.size == 200;})endendtask
endclassclass test_case0 extends base_test;`uvm_component_utils(test_case0)function new(string name = "test_case0", uvm_component parent = null);super.new(name,parent);endfunctionfunction void build_phase(uvm_phase phase);super.build_phase(phase);endfunctiontask main_phase(uvm_phase phase);case0_sequence seq;phase.raise_objection(this);#4;  seq = case0_sequence::type_id::create("l_seq");`uvm_info("case0_sequence","case1_sequence begin",UVM_LOW)seq.start(env.i_agt.sqr);#3000;phase.drop_objection(this);endtask
endclass`endif

在这里,使用uvm_do_with宏的方法实现将特定的数据传输到sequencer。但这里值得注意的是,在下面定义了一个从属于base_test的类test_case0 ,其也是UVM树的一部分,是从属于base_test的,因此与env的关系很近,但是这里其作用仅仅就是建立一个case0_sequence 并且运行,同时还是用phase.raise_objection(this)phase.drop_objection(this)定义整个sequence事件的开始与结束。在这里指定了env.i_agt.sqr为发射的地点。

2.11 top_tb

这就是最终的tb文件,代码如下,具体就不说了,无非就是例化初始化之类的操作,以及保存波形。

这里挖一个坑,针对这里asyn_fifo_chk_rst还没了解清楚。

`timescale 1ns/1ps
`include "uvm_macros.svh"import asyn_fifo_chk_rst::*;
import uvm_pkg::*;module top_tb;logic wclk,rclk,wreset_b,rreset_b;fifo_if my_if(wclk,rclk,wreset_b,rreset_b);fifo_top DUT
(.wclk		   (wclk),.rclk		   (rclk),.wreset_b	(wreset_b),     //wrst_n -> wreset_b.rreset_b	(rreset_b),	    //rrst_n -> rreset_b.write		(my_if.write),	//winc -> write.read		   (my_if.read),	//rinc -> read.wdata		(my_if.wdata),.wfull		(my_if.wfull),.rempty		(my_if.rempty),.rdata		(my_if.rdata)
);fifo_rst_mon fifo_rst_mon1;
fifo_chk_rst fifo_chk_rst1;
event reset_e_w;
event reset_e_r;initial beginwclk = 0;rclk = 0;wreset_b = 1;rreset_b = 1;#2 wreset_b = 0;rreset_b = 0;my_if.write = 0;my_if.read = 0;#2 wreset_b = 1;rreset_b = 1;
endalways #1 wclk = ~wclk;
always #3 rclk = ~rclk;// 参数传递
initial beginuvm_config_db#(virtual fifo_if)::set(null,"uvm_test_top.env.i_agt.i_drv","vif",my_if);uvm_config_db#(virtual fifo_if)::set(null,"uvm_test_top.env.i_agt.i_mon","vif",my_if);uvm_config_db#(virtual fifo_if)::set(null,"uvm_test_top.env.o_agt.o_mon","vif",my_if);
end// 参考?
initial beginfifo_rst_mon1 = new(reset_e_w,reset_e_r);fifo_chk_rst1 = new(reset_e_w,reset_e_r);fifo_rst_mon1.my_if6 = my_if;fifo_chk_rst1.my_if7 = my_if;forkfifo_rst_mon1.run();fifo_chk_rst1.run();join
endinitial begin$fsdbDumpfile("tb.fsdb");$fsdbDumpvars;$fsdbDumpon;
endinitial beginrun_test("test_case0");
endendmodule

三、UVM仿真环境的搭建

这里我简单说一下,因为在环境搭建的过程中踩了很多坑,在网上的教程很乱,也没有一个完美的答案,这里就介绍一下我搭建UVM的结构图:
结构图
可以看出,我这里建立了三个文件夹,有源码DUT部分,存放仿真文件以及启动文件的sim还有存放tb文件和uvm文件的testbench文件。最重要的就是启动文件filelist和Makefile的编写。

3.1 Makefile文件的编写

先给出我的Makefile文件:

.PHONY:file vcs sim verdi cleanVCS = vcs -full64 -cpp g++-4.8 -cc gcc-4.8 \-LDFLAGS -Wl,--no-as-needed \-f filelist.f \-timescale=1ns/1ps \-R \-debug_acc+all \+define+FSDB \-lca -kdb \-ntb_opts uvm-1.1 \-sverilog \+v2k \|tee vcs.logfile:find ../ -name "*.v" -o -name "*.sv" > file.fvcs:${VCS}sim:./simv -gui |tee sim.log &verdi:verdi -f filelist.f -sv -ssf tb.fsdb &clean:rm -rf csrc verdiLog simv.daidir \novas.* \vc_hdrs.h \simv \*.key \*.fsdb \*.log \inter.vpd \DVEfiles

这里包括了五个部分,分别是file、vcs、sim、verdi以及clean。我分开来介绍:

  • file:
    这个主要是用于生成filelist文件,但也不全是filelist,因为该脚本只能获取所有的.v和.sv文件,在filelist中的编写不仅仅要包含这个,而且还有uvm包的文件,并且这些文件的先后顺序有严格的要求,这个后面讲解。
  • vcs:
    这个主要是启动vcs对所有的文件进行编译,在Makefile中添加了很多附加选项,这些选项都是能够让vcs正常运行的选项,你可以使用vcs help来获取vcs命令手册,里面解释了所有符号的意义以及用法。
# 指定使用 VCS (Verilog Compiler Simulator) 进行仿真编译
VCS = vcs -full64 -cpp g++-4.8 -cc gcc-4.8 \# -full64: 使用 64 位编译选项,适用于 64 位操作系统# -cpp g++-4.8: 指定 C++ 编译器使用 g++ 版本 4.8# -cc gcc-4.8: 指定 C 编译器使用 gcc 版本 4.8-LDFLAGS -Wl,--no-as-needed \# -LDFLAGS: 启用链接器的标志# -Wl,--no-as-needed: 在链接时告诉链接器不要自动移除未使用的库-f filelist.f \# -f filelist.f: 指定仿真源文件列表,filelist.f 是一个包含所有待编译文件的文件列表-timescale=1ns/1ps \# -timescale=1ns/1ps: 设置仿真时间尺度为 1ns(纳秒)/ 1ps(皮秒)-R \# -R: 启用调试和恢复功能-debug_acc+all \# -debug_acc+all: 启用所有的调试访问器,允许调试仿真时查看所有信号和变量+define+FSDB \# +define+FSDB: 定义一个名为 FSDB 的宏,通常用于控制 FSDB 文件(仿真波形文件)的输出-lca -kdb \# -lca: 启用 LCA (Library Coverage Analysis),用于库覆盖分析# -kdb: 启用 KDB (Kernel Debugger),为调试目的启用内核调试功能-ntb_opts uvm-1.1 \# -ntb_opts uvm-1.1: 启用与 UVM 1.1 兼容的 NTB(Native Testbench)选项,适用于 UVM(Universal Verification Methodology)验证环境-sverilog \# -sverilog: 启用 SystemVerilog 编译支持+v2k \# +v2k: 启用 Verilog-2001(V2K)编译选项|tee vcs.log# |tee vcs.log: 使用 tee 命令将 VCS 编译过程的输出同时显示在终端并保存到 vcs.log 文件中
  • sim:用来使用vcs自带的仿真工具生成波形,这里我没用过。
  • verdi:用来查看fsdb的波形文件,这个是我最常用的,通过这个可以很方便的对波形进行追溯,调试起来很容易。
  • clean:用来删除生成了的文件。

以防有人不会用Makefile,说一下他的用法。在Makefile文件所在的目录打开终端,使用make [指令]的方式来运行,Makefile本质来说就是将指令进行了一个打包,我这里运行verdi -f filelist.f -sv -ssf tb.fsdb &make verdi的效果是一样的。

3.2 filelist文件的编写

下面是我针对本项目编写的filelist文件。

+incdir+$UVM_HOME/src
$UVM_HOME/src/uvm_pkg.sv../testbench/fifo_if.sv
../testbench/fifo_transaction.sv
../testbench/fifo_driver.sv
../testbench/fifo_in_sequencer.sv
../testbench/fifo_in_monitor.sv
../testbench/fifo_out_monitor.sv
../testbench/fifo_in_agent.sv
../testbench/fifo_out_agent.sv
../testbench/fifo_model.sv
../testbench/fifo_scoreboard.sv
../testbench/my_env.sv
../testbench/fifo_chk_rst.sv
../testbench/base_test.sv
../testbench/fifo_case0.sv
../testbench/top_tb.sv../DUT/pointer.v
../DUT/sync.v
../DUT/fifo_top.v
../DUT/memory.v
../DUT/comparator.v

可以分为三个部分,首先是前两行的+incdir+$UVM_HOME/src$UVM_HOME/src/uvm_pkg.sv,第一句话表示将$UVM_HOME/src中的所有文件加入到编译列表中,这个文件里基本上是所有uvm库所需要的文件,什么uvm_env呀之类的就是这里定义。然后uvm_pkg.sv相当于是所有文件的核心实现,在tob_tb中,只需要调用import uvm_pkg::*;便可以将所有uvm包含进来。

$UVM_HOME表示的是一个宏定义,在Lunix中主目录的.bashrc中定义,我这里的原话是export UVM_HOME=/home/wxm/uvm_study/uvm-1.1d
后面紧接着是testbench中的文件,注意一定要先写testbench再写dut,以防报错。然后在testbench内部也要按照顺序,从独立到树枝再到树根的顺序,比如献血interface和transaction,再从树枝的dirver开始写到树根base_case。因为在UVM编译的过程中是从上到下的顺序,如果你先编译base_case,编译器会报错说找不到env。最后就是top_tb以及其余的DUT文件。
但是貌似DUT文件没有内部顺序

3.3 另辟蹊径

还有一个别的方法,你只需要写两个文件就可以:一个是top_tb.sv;另一个是fifo_top.v,前提是你需要在这两个文件里面把其余所有的文件include一遍,就像这样:

`include "../testbench/my_driver.sv"
`include "../testbench/my_model.sv"
...

因为你的Makefile和filelist以及终端运行的位置都在sim文件夹,而其他文件在testbench和dut文件夹,因此你在include的时候需要以sim文件夹为根目录,使用../回到上一级,再使用/testbench/XXX.sv来调用这些文件。这样操作就不需要在filelist中调整顺序了。


总结

总而言之,这是一个很好的练习UVM的项目,因为其与《UVM实战》这本书的内容大差不差,很多在结构上都有相似的地方,我希望从这个项目为起点,依次加深我对IC验证这一领域的认识。后面我会对该项目的波形图进行研究,并通过调整UVM代码实现一些不一样的功能。

相关文章:

【UVM项目实战】异步fifo—uvm项目结构以及uvm环境搭建

本文章同步到我的个人博客网站&#xff1a;ElemenX-King&#xff1a;【UVM项目实战】异步fifo—uvm项目结构以及uvm环境搭建 希望大家能使用此网站来进行浏览效果更佳&#xff01;&#xff01;&#xff01; 目录 一、异步FIFO1.1 异步FIFO的定义1.2 亚稳态1.3 异步FIFO关键技术…...

【含文档+PPT+源码】基于SpringBoot+Vue的移动台账管理系统

项目介绍 本课程演示的是一款 基于SpringBootVue的移动台账管理系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运行本套系统 3.该…...

C语言——函数

C语言——函数 函数的概念 函数&#xff1a;也叫子程序。C语言中的函数就是一个完成某项特定任务的一小段代码。C语言函数分类&#xff1a; 按照用户使用&#xff1a;库函数&#xff0c;自定义函数 按照参数的形式&#xff1a;无参函数&#xff0c;有参函数 按照是否有返回值…...

网络安全 | F5 WAF 黑白名单配置实践指南

关注&#xff1a;CodingTechWork 引言 在现代网络安全架构中&#xff0c;F5 Web Application Firewall (WAF) 是保护 Web 应用免受攻击的重要工具。F5 WAF 提供了强大的黑白名单功能&#xff0c;结合 Data Group 和 iRules&#xff0c;可以实现更灵活、更高效的流量控制策略。…...

黑马 redis面试篇笔记

redis主从 version: "3.2"services:r1:image: rediscontainer_name: r1network_mode: "host"entrypoint: ["redis-server", "--port", "7001"]r2:image: rediscontainer_name: r2network_mode: "host"entrypoint:…...

报错_NoSuchMethodException: cn.mvc.entity.User.<init>()

org.springframework.beans.BeanInstantiationException: Failed to instantiate [cn.mvc.entity.User]: No default constructor found; nested exception is java.lang.NoSuchMethodException: cn.mvc.entity.User.<init>() 添加一个无参的构造器即可&#xff01; pub…...

【无人机】无人机遥控器设置与校准,飞行模式的选择,无线电控制 (RC) 设置

目录 1、遥控器校准 1.1、校准步骤 2、飞行模式选择&#xff0c;遥控器通道映射 2.1、配置步骤 1、遥控器校准 在校准无线电系统之前&#xff0c;必须连接/绑定接收器和发射器。绑定发射器和接收器对的过程是特定于硬件的&#xff08;有关说明&#xff0c;请参阅 RC 手册&…...

基于 EFISH-SBC-RK3588 的无人机环境感知与数据采集方案

一、核心硬件架构设计‌ ‌高性能算力引擎&#xff08;RK3588 处理器&#xff09;‌ ‌异构计算架构‌&#xff1a;集成 8 核 CPU&#xff08;4Cortex-A762.4GHz 4Cortex-A551.8GHz&#xff09;&#xff0c;支持动态调频与多任务并行处理&#xff0c;单线程性能较传统四核方案…...

上岸率85%+,25西电先进材料与纳米科技学院(考研录取情况)

1、先进材料与纳米科技学院各个方向 2、先进材料与纳米科技学院近三年复试分数线对比 学长、学姐分析 由表可看出&#xff1a; 1、材料科学与工程25年相较于24年上升10分&#xff0c;为290分 2、材料与化工&#xff08;专硕&#xff09;25年相较于24年下降20分&#xff0c;为…...

10天学会嵌入式技术之51单片机-day-7

第十六章 UART通信 16.1 UART 概述 16.1.1 前置基础 想要理解和使用 UART&#xff0c;需要先了解一些通讯领域的术语&#xff0c;如下 。 &#xff08;1&#xff09;串行通讯和并行通讯 串行通讯和并行通讯是数据传输的两种主要方式&#xff0c;两者的区别如下。 &#xff…...

w~视觉~合集3

我自己的原文哦~ https://blog.51cto.com/whaosoft/12327888 #几个论文 Fast Charging of Energy-dense Lithium-ion Batteries Real-time Short Video Recommendation on Mobile Devices Semantic interpretation for convolutional neural networks: What makes a ca…...

AI在论文评审中的应用与工具推荐

一、AI在论文评审中的核心应用场景 内容质量评估与逻辑校验 • 核心论点识别&#xff1a;AI可快速定位论文核心创新点&#xff0c;并验证其与文献综述、实验数据的逻辑一致性&#xff08;如知网研学的“文献矩阵分析”功能&#xff09;。 • 方法论合理性检测&#xff1a;自动识…...

怎样记忆Precision、Recall?

首先&#xff0c;明确符号&#xff1a; TP(True Posive)&#xff1a;标签为正&#xff0c;预测为正 TN(True Negative)&#xff1a;标签为负&#xff0c;预测为负 FP(False Positive)&#xff1a;标签为负&#xff0c;预测为正 FN(False Negative)&#xff1a;标签为正&#xf…...

【刷题】第三弹——二叉树篇(上)

文章目录 一.相同的树二. 另一棵树的子树三. 翻转二叉树四. 对称二叉树五. 平衡二叉树六. 二叉搜索树七. 二叉树的遍历 一.相同的树 检查两棵树是否相同 思路: 1.先比较结构 结构不一样直接false 2.结构一样,在比较值 public boolean isSameTree(TreeNode p, TreeNode q) {if…...

【Canvas与标志】红黄渐变阴影太极图

【成图】 120*120的png图标 大小图&#xff1a; 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>红黄渐变阴影太极图 Draft1&l…...

express的中间件,全局中间件,路由中间件,静态资源中间件以及使用注意事项 , 获取请求体数据

Express 中间件系统 的详细讲解&#xff0c;包括全局中间件、路由中间件、静态资源中间件、请求体解析中间件&#xff0c;以及使用注意事项&#x1f447; &#x1f310; 一、什么是中间件&#xff08;Middleware&#xff09;&#xff1f; 中间件是 函数&#xff0c;在请求到达…...

大数据去重

实验4 大数据去重 1.实验目的 通过Hadoop数据去重实验&#xff0c;学生可以掌握准备数据、伪分布式文件系统配置方法&#xff0c;以及在集成开发环境Eclipse中实现Hadoop数据去重方法。 2.实验要求 了解基于Hadoop处理平台的大数据去重过程&#xff0c;理解其主要功能&…...

高功率激光输出稳定性不足?OAS 光学软件来攻克

法布里珀罗干涉仪设计案例 简介 法布里珀罗干涉仪作为一种高分辨率光学仪器&#xff0c;基于多光束干涉原理构建。其核心结构由两块高度平行的反射镜组成谐振腔&#xff0c;当光进入该谐振腔后&#xff0c;会在镜面间进行多次反射。在这一过程中&#xff0c;透射光会形成干涉条…...

基于大模型的结肠癌全病程预测与诊疗方案研究

目录 一、引言 1.1 研究背景与意义 1.2 研究目的与创新点 二、结肠癌概述 2.1 流行病学特征 2.2 发病机制与危险因素 2.3 临床症状与诊断方法 三、大模型技术原理与应用现状 3.1 大模型的基本原理 3.2 在医疗领域的应用情况 3.3 在结肠癌预测中的潜力分析 四、术前…...

【Hive入门】Hive查询语言(DQL)完全指南:从基础查询到高级分析

目录 1 Hive查询执行架构全景 2 SELECT基础查询详解 2.1 基本查询结构 2.2 条件查询流程图 3 聚合函数与GROUP BY实战 3.1 聚合执行模型 3.2 GROUP BY数据流 4 排序操作深度解析 4.1 ORDER BY执行流程 4.2 排序算法对比 5 高级技巧与注意事项 5.1 嵌套查询与CTE 5.…...

【学习笔记】文件包含漏洞--相关习题

第一关 伪协议 用代码执行命令查看目录所含文件&#xff0c;发现flag.php 法一 题目代码含有include&#xff0c;写入文件包含指令&#xff1a; php://filter/readconvert.base64-encode/resourceflag.php 解码即可得到flag。 法二 法三 法二可以用php&#xff1a;//input&…...

蓝桥杯 20. 压缩变换

压缩变换 原题目链接 题目描述 小明最近在研究压缩算法。他知道&#xff0c;压缩时如果能够使数值很小&#xff0c;就能通过熵编码得到较高的压缩比。然而&#xff0c;要使数值变小是一个挑战。 最近&#xff0c;小明需要压缩一些正整数序列&#xff0c;这些序列的特点是&a…...

BY免费空间去掉?i=1

BY免费空间去掉?i1 使用说明 支持域名&#xff1a;tae.dpdns.org 前提绑定主机&#xff0c;申请主机–控制面板选择–子域名&#xff0c;绑定xxx.tae.dpdns.org子域名 默认开启DDoS防御&#xff0c;无防火墙规则&#xff0c;建议用.htaccess来防御 默认去掉访问统计?i1 …...

中篇:深入剖析 L2CAP 与 ATT 协议模块(约5000字)

引言 在 BLE 协议栈中,L2CAP 与 ATT 承担了关键的数据分发、协议复用与属性访问职责。对多协议并存和大数据场景的应用,深入理解这两层协议的分片重组、流控机制、MTU/MTU 协商和 ATT 操作流程,对于提升系统性能与稳定性至关重要。本篇将全面拆解 L2CAP 与 ATT 的原理与实战…...

【C语言】C语言结构体:从基础到高级特性

前言 在C语言的世界里&#xff0c;结构体是一种强大而灵活的自定义数据类型&#xff0c;它能够将不同类型的数据组合在一起&#xff0c;形成一个逻辑上的整体。从简单的数据聚合到复杂的内存对齐优化&#xff0c;再到高效的位段操作&#xff0c;结构体在系统编程、嵌入式开发和…...

电控---JTAG协议

一、物理层架构与信号特性 1. 引脚定义与电气规范 核心引脚&#xff1a; TCK&#xff08;测试时钟&#xff09;&#xff1a;频率范围0.1MHz至50MHz&#xff08;如Xilinx Spartan-6支持25MHz&#xff09;&#xff0c;上升沿采样数据。TMS&#xff08;测试模式选择&#xff09;…...

FreeRTOS【3】任务调度算法

重要概念 在运行的任务&#xff0c;被称为"正在使用处理器"&#xff0c;它处于运行状态。在单处理系统中&#xff0c;任何时间里只能有一个任务处于运行状态。 非运行状态的任务&#xff0c;它处于这 3 中状态之一&#xff1a;阻塞(Blocked)、暂停(Suspended)、就绪…...

高德地图API + three.js + Vue3基础使用与使用 + 标记不显示避坑

three.js小白的学习之路。 最近闲来无事&#xff0c;突然想起来之前好像项目有需求说是要将模型放在地图上。加上在浏览别的大佬写的博客时&#xff0c;也找到了一些大佬写的相关文章。基本上都是使用的高德地图开放平台的JS API。我也随之开启了自己的学习之路。 先简单学习…...

书籍推荐:《价值心法》一姜胡说

书名 &#xff1a;《价值心法》一姜胡说 摘录 每天问问自己&#xff0c;如果今天只做一件事&#xff0c;这件事是什么&#xff1f;找到它。拿出2—3个小时&#xff0c;专门处理这件事。其他所有事全部排在那2—3个小时之外。 集中一段时间用来做最重要的事。这段时…...

Linux GPIO驱动开发实战:Poll与异步通知双机制详解

1. 引言 在嵌入式Linux开发中&#xff0c;GPIO按键驱动是最基础也最典型的案例之一。本文将基于一个支持poll和异步通知双机制的GPIO驱动框架&#xff0c;深入剖析以下核心内容&#xff1a; GPIO中断与防抖处理环形缓冲区设计Poll机制实现异步通知(SIGIO)实现应用层交互方式 …...

x-cmd install | brows - 终端里的 GitHub Releases 浏览器,告别繁琐下载!

目录 核心功能与优势安装适用场景 还在为寻找 GitHub 项目的特定 Release 版本而苦恼吗&#xff1f;还在网页上翻来覆去地查找下载链接吗&#xff1f;现在&#xff0c;有了 brows&#xff0c;一切都将变得简单高效&#xff01; brows 是一款专为终端设计的 GitHub Releases 浏览…...

一天学完Servlet!!!(万字总结)

文章目录 前言Servlet打印Hello ServletServlet生命周期 HttpServletRequest对象常用api方法请求乱码问题请求转发request域对象 HttpServletResponse对象响应数据响应乱码问题请求重定向请求转发与重定向区别 Cookie对象Cookie的创建与获取Cookie设置到期时间Cookie注意点Cook…...

c#-命名和书写规范

文章目录 1. 接口名称以大写 I 开头2. 属性类型以单词 Attribute 结尾3. 枚举类型对非标记使用单数名词,对标记使用复数名词4. 标识符不应包含两个连续下划线(__)字符5. 对变量、方法和类使用有意义的描述性名称6. 将 PascalCase 用于类名和方法名称7. 对方法参数和局部变量…...

【双指针】和为s的两个数字

57. 和为target的两个数字 剑指 Offer 57. 和为s的两个数字 ​ 输入一个递增排序的数组和一个数字target&#xff0c;在数组中查找两个数&#xff0c;使得它们的和正好是target。如果有多对数字的和等于target&#xff0c;则输出任意一对即可。 示例 1&#xff1a; 输入&…...

【Vue】TypeScript与Vue3集成

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Vue 文章目录 1. 前言2. 环境准备与基础搭建2.1. 安装 Node.js 与 npm/yarn/pnpm2.2. 创建 Vue3 TypeScript 项目2.2.1. 使用 Vue CLI2.2.2. 使用 Vite&#xff08;推荐&#xff09;2.2.3. 目录结构简述 3. Vue3 TS 基础语法整…...

win11中wsl在自定义位置安装ubuntu20.04 + ROS Noetic

wsl的安装 环境自定义位置安装指定ubuntu版本VsCodeROS备份与重载备份重新导入 常用命令参考文章 环境 搜索 启用或关闭 Windows 功能 勾选这2个功能&#xff0c;然后重启 自定义位置安装指定ubuntu版本 从网上找到你所需要的相关wsl ubuntu版本的安装包&#xff0c;一般直…...

【数据可视化-29】食物营养成分数据可视化分析

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…...

手动实现legend 与 echarts图交互 通过js事件实现图标某项的高亮 显示与隐藏

通过html实现legend的样式 提供调用echarts的api实现与echarts图表交互的效果 实现饼图element实现类似于legend与echartstu表交互效果 效果图 配置代码 <template><div style"height: 400px; width: 500px;background-color: #CCC;"><v-chart:opti…...

C语言编程--16.删除链表的倒数第n个节点

题目&#xff1a; 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5] 示例 2&#xff1a; 输入&#xff1a;head [1], n 1 输出&#xff1a…...

centos7使用certbot完成nginx ssl证书续期

没有废话纯干货 yum源配置&#xff08;配置好的可以跳过&#xff09; #到/etc/yum.repos.d/下mkdir bak&#xff0c;将所用东西mv到bak下 cd /etc/yum.repos.d/ mkdir bak mv ./* bak/ wget https://mirrors.aliyun.com/repo/Centos-7.repo 没有安装nginx的话&#xff0c;配…...

ECharts学习之 toolbox 工具栏

toolbox: {show: true,feature: {//数据视图工具&#xff0c;可以展现当前图表所用的数据dataView: {title: "数据视图",readOnly: false, //是否不可编辑&#xff0c;即只读lang:[数据视图,关闭,刷新] //数据视图上有三个话术},magicType: {type: ["line"…...

修改el-select背景颜色

修改el-select背景颜色 /* 修改el-select样式--直接覆盖默认样式&#xff08;推荐&#xff09; */ ::v-deep .el-select .el-input__inner {background-color: #1d2b72 !important; /* 修改输入框背景色 */color: #fff; } ::v-deep .el-select .el-input__wrapper {background-…...

Qt 使用 MySQL 数据库的基本方法

在 Qt 中&#xff0c;使用 MySQL 数据库的基本方法主要是通过 QSqlDatabase、QSqlQuery 等类来进行数据库的连接、查询和数据操作。以下是 Qt 中连接和操作 MySQL 数据库的基本步骤。 1. 安装 MySQL 驱动 首先&#xff0c;确保您的 Qt 环境已经配置了 MySQL 驱动。通常&#…...

BLIP 系列论文(BLIP、BLIP-2、InstructBLIP)

BLIP BLIP 是 Salesforce 团队在多模态领域中的经典工作&#xff0c;影响力巨大&#xff0c;BLIP 系列包括&#xff1a;BLIP、BLIP-2、InstructBLIP。 BLIP 在多模态大模型之前&#xff0c;多模态领域中最流行的是视觉-语言预训练&#xff08;Vision-Language Pre-training,…...

【玩转全栈】—— 无敌前端究极动态组件库--Inspira UI

目录 Inspira UI 介绍 配置环境 使用示例 效果&#xff1a; Inspira UI 学习视频&#xff1a; 华丽优雅 | Inspira UI快速上手_哔哩哔哩_bilibili 官网&#xff1a;https://inspira-ui.com/ Inspira UI 介绍 Inspira UI 是一个设计精美、功能丰富的用户界面库&#xff0c;专为…...

Java24新增特性

Java 24&#xff08;Oracle JDK 24&#xff09;作为Java生态的重要更新&#xff0c;聚焦AI开发支持、后量子安全、性能优化及开发者效率提升&#xff0c;带来20余项新特性和数千项改进。以下是核心特性的分类解析&#xff1a; 一、语言特性增强&#xff1a;简化代码与模式匹配 …...

Git多人协作与企业级开发模型

目录 1.多人协作一 2.多人协作二 3.远程分⽀删除后&#xff0c;本地gitbranch-a依然能看到的解决办法 4.企业级开发模型 4.1.Git的重要性 4.2.系统开发环境 4.3.Git 分⽀设计规范 1.多人协作一 ⽬前&#xff0c;我们所完成的⼯作如下&#xff1a; 基本完成Git的所有本…...

Android学习总结之扩展基础篇(一)

一、IdleHandler工作原理 1. IdleHandler 接口定义 IdleHandler 是 MessageQueue 类中的一个接口&#xff0c;定义如下&#xff1a; public static interface IdleHandler {/*** 当消息队列空闲时会调用此方法。* return 如果返回 true&#xff0c;则该 IdleHandler 会保留在…...

C语言教程(十六): C 语言字符串详解

一、字符串的表示 在C语言中&#xff0c;字符串是由一系列字符组成&#xff0c;并且以空字符 \0 作为结束标志。字符串通常用字符数组来表示。例如&#xff1a; char str[] {H, e, l, l, o, \0};也可以使用字符串字面量来初始化字符数组&#xff1a;char str[] "Hello&…...

Redis LFU 策略参数配置指南

一、基础配置步骤‌ 设置内存上限‌ 在 redis.conf 配置文件中添加以下指令&#xff0c;限制 Redis 最大内存使用量&#xff08;例如设置为 4GB&#xff09;&#xff1a; maxmemory 4gb选择 LFU 淘汰策略‌ 根据键的作用域选择策略&#xff1a; # 所有键参与淘汰 maxmemory-…...