【Verilog HDL】FPGA-testbench基础知识

🎉欢迎来到FPGA专栏~testbench基础知识


  • ☆* o(≧▽≦)o *☆~我是小夏与酒🍹
  • 博客主页:小夏与酒的博客
  • 🎈该系列文章专栏:FPGA学习之旅
  • 文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏
  • 📜 欢迎大家关注! ❤️
    FPGQ2

CSDN

🎉 目录-testbench基础知识

  • 一、说明
  • 二、testbench简单理解
  • 三、testbench文件结构
    • 3.1 声明仿真的单位和精度
    • 3.2 宏定义
    • 3.3 定义测试模块名
    • 3.4 声明信号
    • 3.5 模块实例化
    • 3.6 使用initial或always产生激励信号
    • 3.7 完整的testbench代码
  • 四、补充语法
    • 4.1 repeat循环
    • 4.2 forever循环
    • 4.3 仿真控制任务$stop

遇见未来

一、说明

📜在开发FPGA的过程中,需要掌握Verilog的核心语法并编写高质量的代码。【Verilog HDL】系列的文章便专注于Verilog语法的讲解和分析。文章参考书籍:《Verilog HDL设计实用教程》

Verilog文件基本结构的知识学习参考文章:【Verilog HDL】FPGA-Verilog文件的基本结构。

在学习FPGA的过程中,既要学会使用开发工具、软件,会编写Verilog HDL,更要学会编写测试激励文件来对模块进行测试。本编文章讲解testbench文件的基础知识点

二、testbench简单理解

testbench,即测试平台、测试模块,有时也称为测试激励文件

testbench的功能就是产生一些激励信号,施加到待测试的设计模块上,然后观察在这些激励信号的作用下模块的响应输出结果并分析正确性。(详细地说就是给待验证的设计添加激励,同时观察它的输出响应是否符合设计要求。)

testbench功能实现的大概流程图:
流程图
📜以简单的思维方式理解testbench:

🔸目前的待测模块就是一个黑箱子,你不清楚它的内部结构,这个黑箱子只留出了信号输入端口和信号输出端口,只能在输入端对它施加激励,并在输出端观察输出信号。

🔸而testbench就相当于一张实验室的桌子,桌子上放着信号产生器、程控电源等(激励信号);放着电路黑箱子(待测模块);示波器等观察信号的仪器(观察响应);以及若干杜邦线或者导线等。

🔸我们将信号产生器的信号输出接入黑箱子的信号输入端,将示波器的信号线与黑箱子的输出端相连,打开信号产生器和示波器,改变输入信号,并在示波器上观察结果。

三、testbench文件结构

testbench文件的基本结构如下所示:

待测模块_tb.v:

//------<testbench文件的基本结构>------

//`timescale 仿真单位/仿真精度
`timescale xx/xx

//仿真文件模块名
module xx_tb;
	
	//定义声明信号或变量
	...
	...
	
	//例化设计模块
	...
	...
	
	//使用always语句产生激励波形
	...
	...
	
	//使用initial产生激励波形
	initial begin
		...
		...
		...
		$stop;
	
	end

endmodule

🎈举个例子详细说明一下:
目前已经编写好了一个led闪烁的模块,我们需要对该模块进行测试,以确保模块设计的正确性,模块代码如下:

led_flash.v:

//------<模块代码>------
module led_flash(
	input 		Clk;
	input 		Rst_n;
	output reg	led
);
	
	//定义led状态
	parameter led_on  = 1'b0;
	parameter led_off = 1'b1; 
	
	//定义计数器寄存器
	reg [24:0]cnt;
	
	//定义计数器参数
	parameter cnt_max = 24'd24_999_999;
	parameter cnt_min = 24'd0;
	parameter cnt_add = 1'b1;
	
	//计数器
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			cnt <= cnt_min;
		else if(cnt == cnt_max)
			cnt <= cnt_min;
		else
			cnt <= cnt + cnt_add;
	end
	
	//led
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			led <= led_off;
		else if(cnt == cnt_max)
			led <= ~led;
		else
			led <= led;
	end

endmodule

接下来根据上述模块编写测试激励文件。

3.1 声明仿真的单位和精度

`timescale时间刻度指令用来说明模块工作的时间单位时间精度,基本语句如下:

`timescale 时间单位/时间精度

时间单位和时间精度可以以秒、毫秒、纳秒、皮秒或飞秒作为度量,具体数值可以选择1、10或100,如:

`timescale 10ns/1ns

该语句定义了当前模块中的仿真时间单位是10ns,仿真精度是1ns语法上要求时间精度必须小于等于时间单位,即前面的数值要大于等于后面的数值

测试模块中经常使用 #号 延迟来生成信号,例如:

`timescale 10ns/1ns
...
initial
	begin
		A = 0;B = 0;//初始值
		#4 A = 1;//4时间单位后,即40ns
		#5 B = 1;//5时间单位后,即50ns
		#6 A = 0;//6时间单位后,即60nd
		#7 $stop;//7时间单位后,即70ns
	end

又如:

`timescale 1ns/1ns

当代码中出现#10时,代表的意思是延时10ns,由于仿真的精度为1ns,所以最低的延时精度只能到1ns,如果想要延时10.001ns,则需要更改仿真的精度(1ns=1000ps),代码如下:

`timescale 1ns/1ps
#10.001 rst_n = 0;

为了测试上述led闪烁模块,定义如下时间单位和时间精度:

`timescale 1ns/1ns

3.2 宏定义

宏定义采用 `define 来进行指定,把某个指定的标识符用来代表一个字符串,整个标识符在整个文件中都表示所指代的字符串,语法如下:

`define 标识符 字符串  //注意句末无分号

对于上述led闪烁模块,需要产生一个时钟信号给它,为了方便进行全文的修改,我们对时钟信号的周期进行宏定义

`define clock_period 20

如果不想让宏定义生效,可以使用 `undef 指令取消前面定义的宏:

`define WIDTH 16

`undef WIDTH  //此条语句之后,WIDTH失效

reg [`WIDTH-1:0] data;  //报错,因为宏定义已经取消

3.3 定义测试模块名

定义模块名的关键字为module,同时测试模块以endmodule结束,代码如下:

module led_flash_tb;
	...
	...
	...
endmodule

模块名的命名方式一般在被测模块名后面加上 _tb,或者在被测模块名前面加上tb_,表示为哪个模块提供激励测试文件,通常激励文件不需要定义输入和输出端口。代码中定义的常量有时需要频繁的修改,为了方便修改,可以把常量定义成参数的形式,定义参数的关键字为 parameter

3.4 声明信号

在testbench中,信号常用的类型为reg类型wire类型reg类型表示激励信号,wire类型表示输出信号。

定义led闪烁测试模块中的输入和输出信号:

//时钟信号和复位信号均需要输入给待测模块
reg Clk;
reg Rst_n;

//led信号为待测模块的输出信号
wire led;

3.5 模块实例化

例化的设计模块是指被测模块,例化被测模块的目的是把被测模块和激励模块实例化起来,并且把被测模块的端口与激励模块的端口进行相应的连接,使得激励可以输入到被测模块。

如果被测模块是由多个模块组成的,激励模块中只需要例化多个模块的顶层模块

将led闪烁模块实例化:

led_flash led_flash0(
		.Clk(Clk);
		.Rst_n(Rst_n);
		.led(led)
);

在实例化模块中,左侧带“.” 的信号为 led 模块定义的端口信号,右侧括号内的信号为激励模块中定义的信号,其信号名可以和被测模块中的信号名一致,也可以不一致,命名一致的好处是便于理解激励模块和被测模块信号之间的对应关系

3.6 使用initial或always产生激励信号

产生时钟激励信号:

initial Clk = 1;
always#(`clock_period/2) Clk = ~Clk;

产生led闪烁测试模块的其余激励信号:

initial begin
	Rst_n = 0;
	#200
	
	Rst_n = 1;
	#(`clock_period*200)
	
	$stop;
end

3.7 完整的testbench代码

led_flash_tb.v:

//------<测试代码>------
`timescale 1ns/1ns
`define clock_period 20

module led_flash_tb;

	reg Clk;
	reg Rst_n;
	
	wire led;
	
	led_flash led_flash0(
		.Clk(Clk);
		.Rst_n(Rst_n);
		.led(led)
	);

	initial Clk = 1;
	always#(`clock_period/2) Clk = ~Clk;
	
	initial begin
		Rst_n = 0;
		#200
		
		Rst_n = 1;
		#(`clock_period*200)
		
		$stop;
	end

endmodule

四、补充语法

repeat循环和forever循环语句只适用于testbench的编写

4.1 repeat循环

repeat循环的功能是把循环体语句执行某些次数,基本格式:

repeat(次数)
begin
	循环体语句
end

该语句的基本使用案例链接:【FPGA零基础学习之旅#7】BCD计数器设计。

测试代码:

`timescale 1ns/1ns
`define clock_period 20

module BCD_Counter_tb;
	
	reg Clk;
	reg Rst_n;
	reg Cin;
	
	wire Cout;
	wire [3:0]q;
	
	BCD_Counter BCD_Counter0(
		.Clk(Clk),		//系统时钟信号
		.Rst_n(Rst_n),	//系统复位信号
		.Cin(Cin),		//进位输入信号
		.Cout(Cout),		//进位输出信号
		.q(q)			//计数器值
	);
	
	initial Clk = 1;
	always#(`clock_period/2) Clk = ~Clk;
	
	initial begin
		Rst_n = 1'b0;
		Cin = 1'b0;
		#(`clock_period*20);
		
		Rst_n = 1;
		#(`clock_period*20);
		
		repeat(30)begin
			Cin = 1'b1;
			#(`clock_period*1);
			Cin = 1'b0;
			#(`clock_period*5);
		end
		
		#(`clock_period*20);
		$stop;
	end
	
endmodule

其中,该部分代码表示执行循环体中的语句30次,且循环体的语句表示生成一个占空比为1:5的脉冲信号:

repeat(30)begin
			Cin = 1'b1;
			#(`clock_period*1);
			Cin = 1'b0;
			#(`clock_period*5);
		end

脉冲信号是一种离散信号,形状多种多样,与普通模拟信号(如正弦波)相比,波形之间在Y轴不连续(波形与波形之间有明显的间隔),但具有一定的周期性是它的特点。最常见的脉冲波是矩形波(也就是方波)。脉冲信号可以用来表示信息,也可以用来作为载波,比如脉冲调制中的脉冲编码调制(PCM)、脉冲宽度调制(PWM)等等,还可以作为各种数字电路、高性能芯片的时钟信号。

4.2 forever循环

forever循环表示永远循环,直到仿真结束为止,相当于判断条件永远为真。forever循环语句中需要添加时序控制,否则就会陷入死循环。

可以用forever循环语句来编写文章【FPGA零基础学习之旅#9】状态机基础知识中Hello例程的testbench:

reg [7:0]ASCII;

Hello Hello0(
	 .Clk(Clk),
	 .Rst_n(Rst_n),
	 .data(ASCII),
	 .led(led)
);

initial begin
Rst_n = 0;
ASCII = 0;
#(`clock_period*20);
Rst_n = 1;
#(`clock_period*20 + 1);

forever begin
	ASCII = "I";
	#(`clock_period);
	ASCII = "A";
	#(`clock_period);
	ASCII = "M";
	#(`clock_period);
	ASCII = "X";
	#(`clock_period);
	ASCII = "i";
	#(`clock_period);
	ASCII = "a";
	#(`clock_period);
	ASCII = "o"; 
	#(`clock_period);
	ASCII = "M";
	#(`clock_period);
	ASCII = "e";
	#(`clock_period);
	ASCII = "i";
	#(`clock_period);
	ASCII = "g";
	#(`clock_period);
	ASCII = "e"; 
	
	#(`clock_period);
	ASCII = "H";
	#(`clock_period);
	ASCII = "E";
	
	#(`clock_period);
	ASCII = "M";
	#(`clock_period);
	ASCII = "l";
	
	#(`clock_period);
	ASCII = "H";
	#(`clock_period);
	ASCII = "E";
	#(`clock_period);
	ASCII = "L";
	#(`clock_period);
	ASCII = "L";
	#(`clock_period);
	ASCII = "O";
	#(`clock_period);
	
	ASCII = "H";
	#(`clock_period);
	ASCII = "e";
	#(`clock_period);
	ASCII = "l";
	#(`clock_period);
	ASCII = "l";
	#(`clock_period);
	ASCII = "o";
	
	#(`clock_period);
	ASCII = "l"; 
	
	end
	
end

比较常见的还有使用forever循环生成时钟信号,如:

//---<forever循环生成时钟信号>---
initial begin
	Clk = 1;
	forever #10 Clk = ~Clk;
end

同时,使用always块生成时钟信号的代码:

//---<always块生成时钟信号>---
initial Clk = 1;
always #10 Clk = ~Clk;

上述的两种写法都是生成了周期为20个时间单位Clk信号,效果是一样的。两种写法均可以在测试模块中使用,并无优劣之分

4.3 仿真控制任务$stop

$stop的功能是停止当前仿真,注意是停止,而不是退出,仿真器会把仿真到该语句的仿真运行完,然后停止仿真(任务$finish的功能则是停止仿真并退出仿真器),等待下一步命令,此时依然停留在仿真器的仿真界面中,一些仿真窗口(例如波形窗口等)依然保留着,仿真的结果也是保留的。

设计者在自己的计算机上完成仿真而且代码规模较小时,一般都是使用$stop任务作为仿真结束的标志语句,然后根据仿真窗口来查看仿真结果。

需要注意的是,如果使用了forever循环语句产生持续不断的信号时,不需要在仿真中使用$stop,例如上述中Hello例程的完整testbench:

Hello_tb.v.:

`timescale 1ns/1ns
`define clock_period 20

module Hello_tb;
	
	reg Clk;
	reg Rst_n;
	reg [7:0]ASCII;
	
	wire led;	
	
	Hello Hello0(
		.Clk(Clk),
		.Rst_n(Rst_n),
		.data(ASCII),
		.led(led)
	);
	
	initial Clk = 1;
	always#(`clock_period/2)Clk = ~Clk;
	
	initial begin
		Rst_n = 0;
		ASCII = 0;
		#(`clock_period*200);
		Rst_n = 1;
		#(`clock_period*200 + 1);
		
		forever begin
			ASCII = "I";
			#(`clock_period);
			ASCII = "A";
			#(`clock_period);
			ASCII = "M";
			#(`clock_period);
			ASCII = "X";
			#(`clock_period);
			ASCII = "i";
			#(`clock_period);
			ASCII = "a";
			#(`clock_period);
			ASCII = "o";	
			#(`clock_period);
			ASCII = "M";
			#(`clock_period);
			ASCII = "e";
			#(`clock_period);
			ASCII = "i";
			#(`clock_period);
			ASCII = "g";
			#(`clock_period);
			ASCII = "e";	
			
			#(`clock_period);
			ASCII = "H";
			#(`clock_period);
			ASCII = "E";
			
			#(`clock_period);
			ASCII = "M";
			#(`clock_period);
			ASCII = "l";
			
			#(`clock_period);
			ASCII = "H";
			#(`clock_period);
			ASCII = "E";
			#(`clock_period);
			ASCII = "L";
			#(`clock_period);
			ASCII = "L";
			#(`clock_period);
			ASCII = "O";
			#(`clock_period);
			
			ASCII = "H";
			#(`clock_period);
			ASCII = "e";
			#(`clock_period);
			ASCII = "l";
			#(`clock_period);
			ASCII = "l";
			#(`clock_period);
			ASCII = "o";
			
			#(`clock_period);
			ASCII = "l";		
		end
	end

endmodule

csdn

🧸结尾


  • ❤️ 感谢您的支持和鼓励! 😊🙏
  • 📜您可能感兴趣的内容:
  • 【FPGA零基础学习之旅#9】状态机基础知识
  • 【FPGA零基础学习之旅#8】阻塞赋值与非阻塞赋值讲解
  • 【Arduino TinyGo】【最新】使用Go语言编写Arduino-环境搭建和点亮LED灯
  • 【全网首发开源教程】【Labview机器人仿真与控制】Labview与Solidworks多路支配关系-四足爬行机器人仿真与控制
    遇见未来

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/34381.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【网络原理之一】应用层协议、传输层协议UDP和TCP,TCP的三次握手和四次挥手以及TCP的可靠和效率机制

应用层协议XML协议JSONHTTP 传输层协议UDP协议UDP的特点UDP协议格式 TCP协议TCP的特点TCP协议格式TCP的安全和效率机制确认应答(可靠机制)超时重传(可靠机制)连接管理(可靠机制)三次握手(连接过程)四次挥手(断开的过程)状态的转化 滑动窗口(效率机制)流量控制(可靠机制)拥塞控制…

【码银送书第一期】通用人工智能:初心与未来

目录 前言 正文 内容简介 作者简介 译者简介 目录 前言 自20世纪50年代图灵在其划时代论文《计算机器与智能》中提出“图灵测试”以及之后的达特茅斯研讨会开始&#xff0c;用机器来模仿人类学习及其他方面的智能&#xff0c;即实现“人工智能”&#xff08;Artificial …

最优化问题matlab求解-fminsearch()和fmincon()函数的使用

matlab可以求解无约束最优化问题、有约束最优化问题和线性规划、二次型规划问题等&#xff0c;同时实现了最小二乘法的曲线拟合方法。matlab求解优化问题的步骤为&#xff1a; 写标准型描述目标函数&#xff1a;M-函数或匿名函数用fminunc()或fmincon()等函数求解原问题。检验…

【从删库到跑路】MySQL系列——详细讲解SQL的DDL,DML,DQL,DCL语句

&#x1f38a;专栏【MySQL】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【如愿】 大一同学小吉&#xff0c;欢迎并且感谢大家指出我的问题&#x1f970; 文章目录 &#x1f354;关系型数据库⭐概念⭐特点 &#x1f354;MySQL数…

开发工具-网络测试工具 POSTMAN 的脚本使用

开发工具-网络测试工具 POSTMAN 的脚本使用 postman 脚本应用基础设置变量、获取变量示例环境变量的使用测试 发送请求示例常用的变量、方法responseBody 获取响应体内容pm.response 获取响应信息pm.expect() JSON检查cheerio.load() 类似于 jquery 操作 html 元素 POSTMAN 是网…

RabbitMQ入门指南

人生永没有终点。只有等到你瞑目的那一刻&#xff0c;才能说你走完了人生路&#xff0c;在此之前&#xff0c;新的第一次始终有&#xff0c;新的挑战依然在&#xff0c;新的感悟不断涌现。 文章目录 一、MQ与RabbitMQ概述1. MQ简述2. MQ的优势3. MQ的劣势4. 常见的MQ产品5. Rab…

MySQL 数据库

文章目录 数据库的基本概念数据表数据库数据库管理系统数据库系统 数据库的发展史当今主流数据库介绍SQL Server &#xff08;微软公司产品&#xff09;Oracle &#xff08;甲骨文公司产品&#xff09;DB2 &#xff08;IBM公司产品&#xff09;MySQL &#xff08;甲骨文公司收购…

Spring Boot日志文件

目录 前言&#x1f36d; 一、日志的作用&#x1f36d; 1、日志真实使用案例&#xff1a;&#x1f349; 二、日志怎么用&#x1f36d; 1、自定义日志打印&#x1f349; Ⅰ、在程序中得到日志对象&#x1f353; 常见的日志框架说明&#xff08;了解&#xff09;&#x1f35…

保姆级AT32F437 配置RT-Thread 以太网(UDP/TCP Server)

好记性不如烂笔头&#xff0c;既然不够聪明&#xff0c;就乖乖的做笔记&#xff0c;温故而知新。 本文档用于本人对知识点的梳理和记录。 一、前言 ENV版本&#xff1a;v1.3.5 rt-thread版本&#xff1a;V5 开发板&#xff1a;雅特力AT32F437 AT-START-F437 二、ENV配置 E…

java--类变量与实例变量--实验设计--村庄种树

目录 设计要求 设计流程图 程序代码 类Village代码 类MainClass代码 可以直接运行的代码 运行结果 类变量与实例变量的区别和类方法与实例方法的区别 idea的详细使用方法 设计要求 编写程序模拟两个村庄共同拥有一片森林&#xff1b;编写一个Village类&#xff0c;该类…

python爬虫:爬取网站所有页面上某些内容

举例场景&#xff1a;爬取腾讯课堂中&#xff0c;查询python的所有课程的封面图、课程标题、课程数量、课程价格&#xff0c;这4个部分的内容。 代码如下&#xff1a; import requests # import lxml # 导入用于请求的包lxml from bs4 import BeautifulSoup # 导入用于请求…

Tomcat 8.5 环境搭建指南

文章目录 导言环境搭建总结 导言 欢迎阅读本篇博客&#xff0c;本文将为您提供关于如何搭建Tomcat 8.5环境的详细指南。Tomcat是一个流行的开源Java Servlet容器&#xff0c;它提供了一个运行Java Web应用程序的平台。无论您是新手还是有经验的开发人员&#xff0c;本文都将帮…

4、架构:Canvas VS DOM

在可视化搭建的低代码平台中&#xff0c;设计器是一个非常关键的模块&#xff0c;可以帮助用户通过拖拽、配置等方式快速搭建应用界面。 在技术选型方面&#xff0c;目前市面上主流的设计器技术包括基于 HTML/CSS/JavaScript 的 Web 设计器。 在渲染方案方面&#xff0c;主流…

王道《计算机网络》思维导图汇总

第一章 1.1.1 概念与功能 1.1.2 组成与分类 1.1.3 标准化工作及相关组织 1.1.4 性能指标 速率 带宽 吞吐量 时延 时延带宽积 往返时延RTT 利用率 1.2.1 分层结构、协议、接口、服务 1.2.2 OSI参考模型 应用层 表示层 会话层 传输层 网络层 数据链路层 物理层 1.2.4 TCP/IP 参…

Linux下的分布式迁移工具dsync使用

一、Linux下的分布式迁移工具dsync使用 dsync 是一个在 Linux 系统上用于迁移文件和目录的工具。它可以在不同的存储设备之间进行数据同步和迁移操作。dsync 是 rsync 工具的一个衍生版本&#xff0c;专注于在本地文件系统之间进行数据迁移。 这里是官网&#xff1a;dsync 1、…

游泳买耳机买什么的比较好,列举几款实战性好的游泳耳机

对于运动用户来说&#xff0c;在运动时都会选择听一些节奏感比较强的音乐&#xff0c;让自己运动是更有活力。现在已经是三伏天中的前伏期间&#xff0c;不少人会选择在三伏天的日子里进行减肥瘦身&#xff0c;耳游泳已经成为很多人都首选运动&#xff0c;游泳是非常好的有氧运…

吐血整理!可免费使用的国产良心软件分享,几乎满足你办公需求

在这个信息化时代&#xff0c;软件已经成为我们办公和生活的必备工具。然而&#xff0c;市面上的大部分国产软件都需要付费才能使用&#xff0c;给我们的经济负担增加了不少。幸运的是&#xff0c;国内有一些良心软件&#xff0c;它们质量上乘&#xff0c;功能强大&#xff0c;…

2023牛客网秋招国内大厂最牛的 Java 面试八股文合集(全彩版)

秋收即将来临&#xff0c;找工作的小伙伴比比皆是&#xff0c;很对小伙伴早早的就开始储备技术&#xff0c;准备秋招面试了。 为了帮助小伙伴更好的应对面试&#xff0c;我拉来十几个大佬&#xff0c;汇总一线大厂的情况&#xff0c;给你整了一套超全的面试资料&#xff1a; 16…

深度学习项目实战二: LetNet5网络结构搭建

深度学习项目实战二: LetNet5网络结构搭建 文章目录 深度学习项目实战二: LetNet5网络结构搭建@[TOC](文章目录)一、卷积基本运算公式二、LetNet5网络1. 网络结构![在这里插入图片描述](https://img-blog.csdnimg.cn/0008fe6e5886414eac09eed49556ad99.png)2. 导入相关包3. 代码…

熔断与降级 Hystrix

一、Hystrix(豪猪)简介 1、Hystrix的设计目的 &#xff08;1&#xff09;对依赖服务调用时出现的调用延迟和调用失败进行控制和容错保护。 &#xff08;2&#xff09;阻止某一个依赖服务的故障在整个系统中蔓延&#xff0c;服务A->服务B->服务C&#xff0c;服务C故障了…
最新文章