7天搞定FPGA精录&总结Episode.4 复杂运算,板级体验【基于Robei与Altera QuartusⅡ】

发布于:2021-06-11 06:51:54

旅行与读书的关系极其微妙,读书常常在旅行之前很久就已经开始了,旅行却因读书而更加完美。所以,我总是固执地认为读书与旅行是人生最有价值的投资。
但若转眼看来,这个问题反而复杂,因为旅行可以是多样的。墙角冰冷的座椅,并不是被我们的体热所炙暖,虽然不透光,但我们无限的遐想确是可以穿越的。所以,当自己真正以读书学*,也就无关是否真正学到了安身立命之本还是那些所谓不正经的闲碎知识。当自己真正以心在旅行,也就无关是否需要真的在峡谷沧海边缘,等待初生朝阳。
盯着满是代码的屏幕久了,不禁想起,以前我也是坐在这里,春风微醺,写诗,弹琴,或者什么都不做,呆呆地望着窗外星空。只是时移事易,物件虽然还是它们,可人的心境却不如从前了。我们向前,却也在丢失,有些时候,还反以为荣。

这一部分有关于8位移位寄存器的设计、带小数的加法计算器设计和除法器设计。但是我觉得既有上一篇的案例,没必要再多弄几个复杂的了,万变不离其宗。如果不是多模块的电路设计,难度都大致相同,使用的语法也就那么几条,之前的案例已经足够涵盖。所以我更想深入思考一下约束文件这个东西,这一章的三个具体案例会以引用原文描述、图片的方式带过。


谈到约束文件,那么之前提到的FPGA管脚约束必定是最常见最基础的约束文件。诸如Robei或者QuartusⅡ,一定都是在代码的运行分析之后,进行管脚约束文件的操作。Robei中的管脚约束是通过新建Constrain文件得到的,同样也是需要通过联网选择FPGA芯片类型,支持Altera和Xilinx目前的所有主流芯片,同时生成的文件主要是代码形式的,我们在工程的整体综合时需要把这个管脚约束文件给搬过去。在QuartusⅡ或者Vivado里面直接做管脚约束是不需要搞文件搬运工作的,这也是为什么之前的约束工作我都是在工程EDA中完成而不是借助Robei。


FPGA管脚约束的意义:


1、管脚约束,在约束文件中设置管脚的电*标准,在管脚文件中设置上拉下拉并没有什么意义。


2、管脚约束,需要配合相应的外部电路一起。


3、管脚约束,相当于电路检查的文件。


不同公司的约束文件其构成和后缀都不一样,虽然具体的代码表示大致相同。以下就用Xilinx ISE、Vivado和Altera QuartusⅡ三个EDA为例分析约束文件的一些家常事。


?


一、Xilinx旗下EDA的约束文件你需要知道的东西:


在ISE时代,使用的是UCF约束文件。从Vivado开始,XDC成了唯一支持的约束标准。XDC除了遵循工业界的通行标准SDC(Synopsys Design Constraints)之外,还加入了XILINX FPGA特有的位置物理约束等特性。以下是在实际使用中,经历过一些经验教训后,体会的几点我们在组织XDC约束时需要注意的事项。


1. XDC约束条目是有先后顺序的


XDC里面每一行相当于一条指令,Vivado按照行序从前往后读取XDC指令,所以越后面的XDC指令,其优先级越高。比如当有2条XDC指令约束同一个东西时,后面指令会因为执行的比较晚,而覆盖前一条指令的效果,这点需要我们特别注意!


因为XDC中的指令有先后顺序,所以推荐的XDC文件组织方式一般是把timing约束放在前面,而把物理位置约束放在后面。如果更具体些,可以遵循下面的顺序:


## Timing Assertions Section
# Primary clocks
# Virtual clocks
# Generated clocks
# Clock Groups
# Input and output delay constraints


## Timing Exceptions Section
# False Paths
# Max Delay / Min Delay
# Multicycle Paths
# Case Analysis
# Disable Timing


## Physical Constraints Section


2. 一个Vivado工程中可以添加多个XDC文件


这样的好处是便于管理组织。因为XDC约束条目是有顺序要求的,所以XDC文件也是有先后顺序的,我们可以在GUI界面通过鼠标拖拽,来调整XDC文件的顺序。一般推荐把Timing约束,同物理位置属性约束,分别放到不同的文件当中,就如同下面所做的那样:



另外请注意,VIVADO不但可以支持多个XDC文件,还可以支持多组XDC文件集合,但是同时只能有1个集合起作用。如果我们一次create多个synthesis或者implementation,我们还可以为它们每一个分别制定使用哪个约束文件。这个特性在时序收敛阶段比较有用。


3. 关于XDC文件的指令和注释


set_propert空格PACKAGE_PIN空格C17空格[get_ports空格{led_out**无空格[**0]}] ? ? //Vivado XDC文件管脚约束的严格格式


需要注意不要漏掉或多添加空格,否则会警告“不支持的命令”报错“未定义的端口”


在XDC里面,每个完整的XDC约束指令不应当跨行,必须在一行之内表达完毕。同样,在一行之内也只允许存在一个约束指令,不可以把2个约束放在同一行。


关于注释(以“#”开头),唯一的一点限制是,在有效的XDC约束指令行末,不可以添加“#”开头的注释,否则Vivado工具会理解错误。这跟我们在其它语言中通常所理解的注释语法不太一样,可能需要建议XILINX改进。


例如下面两个例子,前者是错误的写法,后者是正确的写法。


错误写法:



?正确写法:



4.下面我们用一个具体的约束文件代码的例子来巩固这些提到的知识点:


下图中约束了一个复位信号CPU_RESET_0,复位信号管脚为AV40,一对输入的差分时钟信号SYSCLK_P_0和SYSCLK_N_0,


管脚分别为E19和E18。时钟频率200MHz,为下图中约束的时钟周期5ns。IOSTANDARD为管脚的电气标准,复位信号为LVCMOS18,


差分时钟信号为LVDS。


set_property IOSTANDARD LVCMOS18 [get_ports CPU_RESET_0]


设置特性 IO电*标准 1.8V 得到端口 端口号



需要注意的是,约束文件中如果对应的端口号是寄存器或数组类型的,应该在写约束文件的时候加上花括号。


如下: set_property PACKAGE_PIN V4 [get_ports{data_out[0]}]


set_property IOSTANDARD LVCMOS33 [get_ports{data_out [0]}]`


其中data_out【9:0】是一个10位的输出寄存器,所以在定义每一位端口所对应IO时应该用{}括起来


set_property PACKAGE_PIN V4 [get_ports {data_out[0]}]


设置特性 FPGA上的管脚 管脚号 得到端口 端口号


set_property IOSTANDARD LVCMOS33 [get_ports {data_out[0]}]


设置特性 IO电*标准 3.3V 得到端口 端口号


?


?


二、Altera QuartusⅡ中SDC约束文件你需要知道的东西:


.sdc 文件是在做时序分析是建立的,你在Tasks窗口的Compile??>TimeQuest Timing Analysis??>TimeQuest Timing Analyzer,双击TimeQuest Timing Analyzer,会弹出TimeQuest Timing Analyzer设置窗口,点击左上角的file,中有new SDC file,点击它就能生成.sdc文件。


1.QuartusⅡ的管脚约束是不需要建立新文件的,系统自动可以完成:


如上篇文章描述所言,管脚约束的话使用Pin planner,然后在Location中填入所有的绑定管脚的标志,之后直接关闭管脚约束界面即可,Altera会自动创建一个管脚约束的文件,添加到工程中并且给下一步综合设*惺褂谩O喽杂赩ivado感觉要方便一点,这也是QuartusⅡ EDA在相对于Vivado的简洁性上的优势。


2.QuartusⅡ的时序约束:


我们说一般的时序约束不借助IP核,借助IP核的话通常是利用PLL_IP核,将开发板晶振固定频率给分成好多种不同的我们需要的时钟频率以供选用。


PLL(Phase Locked Loop): 为锁相环,用来统一整合时钟信号,使高频器件正常工作,如内存的存取资料等。PLL用于振荡器中的反馈技术。 许多电子设备要正常工作,通常需要外部的输入信号与内部的振荡信号同步。一般的晶振由于工艺与成本原因,做不到很高的频率,而在需要高频应用时,由相应的器件VCO,实现转成高频,但并不稳定,故利用锁相环路就可以实现稳定且高频的时钟信号。锁相环的特点是:利用外部输入的参考信号控制环路内部振荡信号的频率和相位。因锁相环可以实现输出信号频率对输入信号频率的自动跟踪,所以锁相环通常用于闭环跟踪电路。锁相环在工作的过程中,当输出信号的频率与输入信号的频率相等时,输出电压与输入电压保持固定的相位差值,即输出电压与输入电压的相位被锁住,这就是锁相环名称的由来。锁相环通常由鉴相器(PD,Phase Detector)、环路滤波器(LF,Loop Filter)和压控振荡器(VCO,Voltage Controlled Oscillator)三部分组成,锁相环组成的原理框图如图:



在QuartusⅡ里面这个复杂功能已经被集成到了PLL_ip核中,通过一系列的设定可以调用这个IP核用来产生特定的时钟频率。当然,这需要先注册使用Quartus。 具体的时钟约束文件代码/手动实现是:


#**************************************************************


# Create Clock


#**************************************************************


create_clock -period 8 -name "ENET0_RX_CLK" [get_ports ENET0_RX_CLK]


create_clock -period 8 -name "ENET1_RX_CLK" [get_ports ENET1_RX_CLK]


create_clock -period 8 -name "ENET2_RX_CLK" [get_ports ENET2_RX_CLK]


?


注意get_ports,get_nets,get_pins区别


?


PLL时钟约束


# Uncommenting one of the following derive_pll_clocks lines


# will instruct the TimeQuest Timing Analyzer to automatically


# create derived clocks for all PLL outputs for all PLLs in a


# Quartus design.


# If the PLL inputs are constrained elsewhere, uncomment the


# next line to automatically constrain all PLL output clocks.


derive_pll_clocks


# If the PLL inputs are not constrained elsewhere, uncomment


# the next line to automatically constrain all PLL input and


# output clocks.


# derive_pll_clocks -create_base_clocks


?


方法 1 ? 自动创建基时钟和 PLL 输出时钟
这一方法使您能够自动地约束 PLL 的输入和输出时钟。ALTPLL megafunction 中指定的
所有 PLL 参数都用于约束 PLL 的输入和输出时钟。自动更新了 ALTPLL megafunction
的修改。当创建 PLL 的输入和输出时钟时,不必跟踪 PLL 参数的更改或指定正确的值。
为了自动约束所有输入和输出 , 要将 derive_pll_clocks 命令和 -create_base_clocks
选项一起使用。基于 PLL 的 MegaWizard TM? Plug-In Manager 例化,TimeQuest
analyzer 确定正确的设置。



derive_pll_clocks -create_base_clocks


?


方法 2 ? 手动创建基时钟和自动创建 PLL 输出时钟
通过这种方法 , 可以手动约束 PLL 的输入时钟并且使 TimeQuest analyzer 能够自动
约束 PLL 的输出时钟。除此之外 , 与 ALTPLL megafunction 中指定的输入时钟频率相
反,您可以指定一个不同的输入时钟频率。通过使用 ALTPLL megafunction 中指定的
参数自动创建 PLL 输出时钟。您可以尝试不同的输入时钟频率 , 同时保持相同的 PLL
输出时钟参数。
可以将此方法与 derive_pll_clocks 命令一起使用并且手动创建 PLL 的输入时钟。



create_clock -period 10.000 -name clk [get_ports {clk}]
derive_pll_clocks


?


方法 3 ? 手动创建基时钟和 PLL 输出时钟
通过这种方法 , 可以手动约束 PLL 的输入时钟和输出时钟。指定了所有的 PLL 参数并
且参数值可以不同于 ALTPLL megafunction 中指定的参数值。除此之外 , 您可以尝试
各种 PLL 输入和输出频率以及参数。
您可以将该方法与 create_clock 和 create_generate_clock 命令的组合一起使用。



create_clock -period 10.000 -name clk [get_ports {clk}]
create_generated_clock
-name PLL_C0
-source [get_pins {PLL|altpll_component|pll|inclk[0]}]
[get_pins {PLL|altpll_component|pll|clk[0]}]
create_generated_clock
-name PLL_C1
-multiply_by 2
-source [get_pins {PLL|altpll_component|pll|inclk[0]}]
[get_pins {PLL|altpll_component|pll|clk[1]}]


至于PLL_ip core的调用,既可在QuartusⅡ中,也可在Modelsim仿真中。具体的操作方式。下面这篇文章和撰文大佬已经解释的很清楚了,可以移步参看他总结的创建步骤。和我之前的文章一样,按照这里所说的步骤一点一点来一定能成功创建PLL核。


https://www.cnblogs.com/ninghechuan/p/7221059.html


3.TimeQuest与Set Clock Groups(感谢参考了部分由@Ambitious Roc. 总结的有关命令格式知识):


在默认情况下,TimeQuest认为设计中所有的时钟都是同步的,并把所有的时钟都放在同一个group里。如果设计中有异步时钟,就需要用命令把异步时钟分组并定义出来。
①一般来说,由不同时钟源(晶振)产生的,时钟之间无法保证对应关系的时钟,必然属于异步时钟;
②有同一个时钟源并且由同一个PLL产生的,不同分频倍频系数的不同频率的时钟,属于同步时钟,因为其相位关系是确定的。当然一个PLL产生的时钟最好是同频不同相位或者呈现倍数关系的时钟。若硬要说同一个PLL产生的一个19.9M一个6.7821M是同步时钟也不太合适,毕竟没有几个边沿能对齐。


第一种情况,不同时钟源的时钟信号必然属于不同的时钟域。


第二种情况,可认为是同一个时钟域下的,并将其划分到一个group中,但是在固件设计的时候需要考虑到这一差异,比如设计同步电路、FIFO、握手机制来确保不同频率数据传输无误。做异步处理之后相应的时序路径设为falsePath可以不去分析STA。若不做异步需要在SDC中添加保证时序的措施(调相位,设multicycle等)。


第三种情况,可认为是不是同一个时钟域下的,若划分到不同的group中。需要做类似上面的异步处理。但是无需再设定falsePath,因为不同group下不会去分析STA。


命令格式:


//模板
set_clock_groups [-h | -help] [-long_help] [-asynchronous] [-exclusive] -group [-logically_exclusive] [-physically_exclusive]
//实例
set_clock_groups -asynchronous -group [get_clocks {altera_reserved_tck}]?
set_clock_groups -logically_exclusive -group [get_clocks {tdc_main_pll_inst|altpll_component|auto_generated|pll1|clk[1] tdc_main_pll_inst|altpll_component|auto_generated|pll1|clk[2] tdc_main_pll_inst|altpll_component|auto_generated|pll1|clk[3] tdc_main_pll_inst|altpll_component|auto_generated|pll1|clk[4]}] -group [get_clocks {pll_s_clk|altpll_component|auto_generated|pll1|clk[0] pll_s_clk|altpll_component|auto_generated|pll1|clk[1] pll_s_clk|altpll_component|auto_generated|pll1|clk[2] pll_s_clk|altpll_component|auto_generated|pll1|clk[3] pll_s_clk|altpll_component|auto_generated|pll1|clk[4]}] -group [get_clocks {tdc_aid_pll_1_inst|altpll_component|auto_generated|pll1|clk[0]}] -group [get_clocks {tdc_aid_pll_2_inst|altpll_component|auto_generated|pll1|clk[0]}]?


P.S. -asynchronous: 时钟是异步不相关的,时钟有完全不同的时钟源;对于异步时钟来讲,两个时钟域的时钟沿有可能在任意时刻出现,相互之间不会有任何关系。如果一条timing path的起始点是在CLKA,而终点在CLKB,即这条timing path跨越了CLKA和CLKB两个时钟域,那么STA软件是不会对该timing path做分析的。实际上这等同于在这两个时钟之间设定了一条false path。
-logically_exclusive : 时钟是互斥的,即时钟不会再同一时刻同时有效;举个例子来讲,PCIE GEN2可以工作在GEN1和GEN2两种模式,在GEN1模式下,时钟为125MHz,在GEN2的模式下,时钟为250MHz,但在某一个特定时间里,时钟只可能为125MHz或者250MHz,这两个频率的时钟不会共存,相互之间也不会有相互作用。


虽然有逻辑互斥与异步不相干的这种区别,但是这两个约束的实际效果似乎差别不明显。
设置异步或者互斥时钟的效果与对各个时钟使用set_false_path的效果是完全一样的,不过结构远比set_false_path简洁,而且时钟越多效果越明显。
但是,set_clock_groups -async不可以完全代替set_false_path。因为伪路径并不一定的跨时钟域路径。
?



具体利用起来,我们需要设计一些东西,这就是前面提到的几个案例。


一、8位移位寄存器的设计:


1、设计目的:
要求掌握8位移位寄存器原理,并根据原理设计8位移位寄存器模块以及设计相关testbench,最后在Robei可视化仿真软件进行功能实现和仿真验证。


2、设计准备:
有一个8比特的数据(初值设为10011100)和一个移位设置数据s,根据s的值不同,产生不同的移位。这里规定移位的方向是向右,由于是8比特,因此s的变化范围为0到7。


3、代码展示:


①创建shift模块文件,并在Robei中键入以下代码:


always@(posedge clk or negedge clr)
begin: shift_reg
if(clr)
data_out <= 8’b0;
else if(en)
begin
case(set[2:0])
3’b0: data_out <= data_in[7:0];
3’b1: data_out <= {data_in[0],data_in[7:1]};
3’d2: data_out <= {data_in[1:0],data_in[7:2]};
3’d3: data_out <= {data_in[2:0],data_in[7:3]};
3’d4: data_out <= {data_in[3:0],data_in[7:4]};
3’d5: data_out <= {data_in[4:0],data_in[7:5]};
3’d6: data_out <= {data_in[5:0],data_in[7:6]};
3’d7: data_out <= {data_in[6:0],data_in[7]};
default: data_out <= data_in[7:0];
endcase
end
end


②创建shift_tb文件,并在其中键入以下测试代码,之后依照前述方法进行运行、保存、仿真、查看波形、验证通过等操作:


initial begin
clock=0;
clr=0;
en=1;
data=8’b10011100;
set=0;
#1 clr=1;
#2 clr=0;
#40 $finish;
end
always #1 clock=~clock;
always #2 set=set+1;


③后端设计利用QuartusⅡ进行即可,管脚约束仍然是参考个人所购买的FPGA开发板的管脚数据表,之后下载到FPGA开发板上验证即可


二、带符号位小数的加法设计


1、设计目的:
设计一个带符号位的小数加法器,该加数和被加数的总位数为32位,其中小数15位,整数占16位,剩下一位符号位。设计该加法器模块以及设计testbench,最后在Robei可视化仿真软件进行功能实现和仿真验证。


2、设计原理:
输入数据的最高位是符号位,其余的位数是数值位。首先通过比较两个输入数据的符号位判断输出数据的符号位:比如输入都是正数则结果一定是正数,输入都是负数则结果一定是负数,输入一正一负的话则比较两个数据的数值大小进行判定。
然后对两个数据的数值位进行计算,得到输出数据的数值位,最后给输出数据添加符号位完成全部运算。
模型格式:sum ?= ?addend ?+ ?addend
输入格式:
|1| ?<- N-Q-1 bits -> ?| ? ?|
|S| ? IIIIIIIIIIIIIIII ? |FFFFFFFFFFFFFFF|
输入数据:
a - addend 1 ? ? ? ? ? ? ?b - addend 2
输出格式:
|1| ?<- N-Q-1 bits -> ?| ? ?|
|S| ? IIIIIIIIIIIIIIII ? |FFFFFFFFFFFFFFF|
输出数据:
c - result



3、代码展示:


①创建qadd模块文件,并在Robei中键入以下代码:


parameter N = 4;
reg [N-1:0] res;
assign c = res;
always @(a or b)
begin
if(a[N-1] == b[N-1])
begin
res[N-2:0] = a[N-2:0] + b[N-2:0];
res[N-1] = a[N-1];
end
else if(a[N-1] == 0 && b[N-1] == 1)
begin
if( a[N-2:0] > b[N-2:0] )
begin
res[N-2:0] = a[N-2:0] - b[N-2:0];
res[N-1] = 0;
end
else
begin
res[N-2:0] = b[N-2:0] - a[N-2:0];
if (res[N-2:0] == 0)
res[N-1] = 0;
else
res[N-1] = 1;
end
end
else
begin
if( a[N-2:0] > b[N-2:0] )
begin
res[N-2:0] = a[N-2:0] - b[N-2:0];
if (res[N-2:0] == 0)
res[N-1] = 0;
else
res[N-1] = 1;
end
else
begin
res[N-2:0] = b[N-2:0] - a[N-2:0];
res[N-1] = 0;
end
end
end


②创建qadd_tb文件,并在其中键入以下测试代码,之后依照前述方法进行运行、保存、仿真、查看波形、验证通过等操作:


initial begin
a = 4’b0001;
b = 4’b0011;
#10
a = 4’b1011;
b = 4’b1011;
#10
a = 4’b0001;
b = 4’b1101;
#10
a = 4’b1110;
b = 4’b0011;
#10
$finish;
end


③后端设计利用QuartusⅡ进行即可,管脚约束仍然是参考个人所购买的FPGA开发板的管脚数据表,之后下载到FPGA开发板上验证即可


三、带符号位小数的加法设计:


1、设计目的:
要求掌握除法器原理,并根据原理设计除法器模块以及设计对应的测试模块,最后在 Robei可视化仿真软件经行功能实现和仿真验证。


2、设计原理:
这个除法器的设计为传统除法器,因此十分简单,易懂:
(1)先取除数和被除数的正负关系,然后正值化被除数,由于需要递减的除数,所以除数应取负值和补码形式。
(2)被除数每一次递减,商数递增。
(3)直到被除数小于除数,递减过程剩下的是余数。
(4)输出的结果根据除数和被除数的正负关系。
下图显示除法器模块的设计:
?



3、代码展示:


①创建qadd模块文件,并在Robei中键入以下代码:


reg[3:0] i;
reg[7:0] dsor;
reg[7:0] rd;
reg[7:0] dend;
reg[7:0] qent;
reg isneg;
reg isdone;
always @(posedge clk or negedge rst_n)
if(!rst_n)
begin
i<=4’d0;
dend<=8’d0;
dsor<=8’d0;
rd<=8’d0;
qent<=8’d0;
isneg<=1’b0;
isdone<=1’b0;
end
else if(start_sig)
case(i)
0: begin
dend<=dividend[7]?~dividend+1:dividend;
dsor<=divisor[7]?~divisor+1:divisor;
rd<=divisor[7]?divisor:(~divisor+1);
isneg<=dividend[7]^divisor[7];
qent<=8’d0;
i<=i+1;
end
1: begin
if(dendbegin
qent<=isneg?(~qent+1):qent;
i<=i+1;
end
else
begin
dend<=dend+rd;
qent<=qent+1;
end
end
2:begin
isdone<=1’b1;
i<=i+1;
end
3:begin
isdone<=1’b0;
i<=4’d0;
end
endcase
assign done_sig=isdone;
assign quotient=qent;
assign remainder=dend;


②创建qadd_tb文件,并在其中键入以下测试代码,之后依照前述方法进行运行、保存、仿真、查看波形、验证通过等操作:


initial
begin
clk=1’b0;
rst_n=1’b0;
#10 rst_n=1’b1;
#1000 $finish;
end
always #5 clk=~clk;
reg[3:0] i;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
dividend<=8’d0;
divisor<=8’d0;
start_sig<=1’b0;
i<=1’b0;
end
else
begin
case(i)
0:if(done_sig)
begin
start_sig<=1’b0;
i<=i+1;
end
else
begin
dividend<=8’d9;
divisor<=8’d3;
start_sig<=1’b1;
end
1:if(done_sig)
begin
start_sig<=1’b0;
i<=i+1;
end
else
begin
dividend<=8’d3;
divisor<=8’d9;
start_sig<=1’b1;
end
2:if(done_sig)
begin
start_sig<=1’b0;
i<=i+1;
end
else
begin
dividend<=8’d8;
divisor<=8’d2;
start_sig<=1’b1;
end
3:if(done_sig)
begin
start_sig<=1’b0;
i<=i+1;
end
else
begin
dividend<=8’d8;
divisor<=8’d4;
start_sig<=1’b1;
end
4:if(done_sig)
begin
start_sig<=1’b0;
i<=i+1;
end
else
begin
dividend<=8’d8;
divisor<=8’d3;
start_sig<=1’b1;
end
5:i<=4’d5;
endcase
end
end


③后端设计利用QuartusⅡ进行即可,管脚约束仍然是参考个人所购买的FPGA开发板的管脚数据表,之后下载到FPGA开发板上验证即可。这里多说一句:前面的所有module和testbench代码都是主要操作部分的代码,需要保存文件之后通过View?Codeview查看完整代码,完整代码才是可以用在所有EDA里面进行分析综合的代码!


?


?


?



?

相关推荐

最新更新

猜你喜欢