ubuntu写wordpress/长沙电商优化
3.1 Verilog 连续赋值
关键词:assign, 全加器
连续赋值语句是 Verilog 数据流建模的基本语句,用于对 wire 型变量进行赋值。:
assign LHS_target = RHS_expression ;
LHS(left hand side) 指赋值操作的左侧,RHS(right hand side)指赋值操作的右侧。
assign 为关键词,任何已经声明 wire 变量的连续赋值语句都是以 assign 开头,例如:
wire Cout, A, B ;
assign Cout = A & B ; //实现计算A与B的功能
-
LHS_target 必须是一个标量或者线型向量,而不能是寄存器类型。
-
RHS_expression 的类型没有要求,可以是标量或线型或存器向量,也可以是函数调用。
-
只要 RHS_expression 表达式的操作数有事件发生(值的变化)时,RHS_expression 就会立刻重新计算,同时赋值给 LHS_target。
Verilog 还提供了另一种对 wire 型赋值的简单方法,即在 wire 型变量声明的时候同时对其赋值。
wire 型变量只能被赋值一次,因此该种连续赋值方式也只能有一次。例如下面赋值方式和上面的赋值例子的赋值方式,效果都是一致的。
全加器
下面采用数据流描述方式,来设计一个 1bit 全加器。
设 Ai,Bi,Ci 分别为被加数、加数和相邻低位的进位数,So, Co 分别为本位和与向相邻高位的进位数。
真值表如下:
全加器的表达式为:
So = Ai ⊕ Bi ⊕ Ci ;
Co = AiBi + Ci(Ai+Bi)
rtl 代码(full_adder1.v)如下:
module full_adder1(input Ai, Bi, Ci,output So, Co);assign So = Ai ^ Bi ^ Ci ;assign Co = (Ai & Bi) | (Ci & (Ai | Bi));
endmodule
当然,更为贴近加法器的代码描述可以为:
module full_adder1(input Ai, Bi, Cioutput So, Co);assign {Co, So} = Ai + Bi + Ci ;
endmodule
testbench(test.sv)参考如下:
`timescale 1ns/1nsmodule test ;reg Ai, Bi, Ci ;wire So, Co ;initial begin{Ai, Bi, Ci} = 3'b0;forever begin#10 ;{Ai, Bi, Ci} = {Ai, Bi, Ci} + 1'b1;endendfull_adder1 u_adder(.Ai (Ai),.Bi (Bi),.Ci (Ci),.So (So),.Co (Co));initial beginforever begin#100;//$display("---gyc---%d", $time);if ($time >= 1000) begin$finish ;endendendendmodule
代码解释:
这段代码是用Verilog HDL(硬件描述语言)编写的,用于描述一个数字电路模块。Verilog常用于模拟和描述电子系统,尤其是数字电路和系统。以下是对代码的逐行解释:
时序和模块声明
timescale 1ns/1ns
module test ;
timescale 1ns/1ns:定义时间单位和时间精度都为1纳秒。
module test ;:定义一个名为test的模块。
变量声明
reg Ai, Bi, Ci ;
wire So, Co ;
reg Ai, Bi, Ci ;:声明三个寄存器变量Ai、Bi和Ci。
wire So, Co ;:声明两个线网变量So和Co。
初始化块1
initial begin {Ai, Bi, Ci} = 3'b0; forever begin #10 ; {Ai, Bi, Ci} = {Ai, Bi, Ci} + 1'b1; end
end
initial begin … end:定义了一个初始化块,该块中的代码在仿真开始时执行一次。
{Ai, Bi, Ci} = 3’b0;:将Ai、Bi和Ci的初始值设为二进制0。
forever begin … end:一个无限循环。
#10 ;:等待10个时间单位(即10纳秒)。
{Ai, Bi, Ci} = {Ai, Bi, Ci} + 1’b1;:将Ai、Bi和Ci的值加1(二进制进位)。
实例化全加器
full_adder1 u_adder( .Ai (Ai), .Bi (Bi), .Ci (Ci), .So (So), .Co (Co));
实例化一个名为full_adder1的全加器模块,并命名为u_adder。
.Ai (Ai), .Bi (Bi), .Ci (Ci), .So (So), .Co (Co):将模块的端口与前面声明的变量连接起来。
初始化块2
initial begin forever begin #100; // $display("---gyc---%d", $time); if ($time >= 1000) begin $finish ; end end
end
这是第二个初始化块。
#100;:等待100个时间单位(即100纳秒)。
if ( t i m e > = 1000 ) b e g i n . . . e n d :如果仿真时间达到或超过 1000 纳秒,则执行 time >= 1000) begin ... end:如果仿真时间达到或超过1000纳秒,则执行 time>=1000)begin...end:如果仿真时间达到或超过1000纳秒,则执行finish命令,结束仿真。
模块结束
endmodule
endmodule:表示模块定义结束。
这个模块主要描述了一个全加器full_adder1的使用。通过两个初始化块,一个用于改变全加器的输入,另一个用于控制仿真的结束时间。
然而,注意这里没有显示full_adder1模块的具体实现,它应该在其他地方定义。
3.2 Verilog 时延
时延, 惯性时延
连续赋值延时语句中的延时,用于控制任意操作数发生变化到语句左端赋予新值之间的时间延时。
时延一般是不可综合的。
寄存器的时延也是可以控制的,这部分在时序控制里加以说明。
连续赋值时延一般可分为普通赋值时延、隐式时延、声明时延。
下面 3 个例子实现的功能是等效的,分别对应 3 种不同连续赋值时延的写法。
//普通时延,A&B计算结果延时10个时间单位赋值给Z
wire Z, A, B ;
assign #10 Z = A & B ;
//隐式时延,声明一个wire型变量时对其进行包含一定时延的连续赋值。
wire A, B;
wire #10 Z = A & B;
//声明时延,声明一个wire型变量是指定一个时延。因此对该变量所有的连续赋值都会被推迟到指定的时间。除非门级建模中,一般不推荐使用此类方法建模。
wire A, B;
wire #10 Z ;
assign Z =A & B
惯性时延
A 或 B 任意一个变量发生变化,那么在 Z 得到新的值之前,会有 10 个时间单位的时延。
如果在这 10 个时间单位内,即在 Z 获取新的值之前,A 或 B 任意一个值又发生了变化,那么计算 Z 的新值时会取 A 或 B 当前的新值。
所以称之为惯性时延,即信号脉冲宽度小于时延时,对输出没有影响。
因此仿真时,时延一定要合理设置,防止某些信号不能进行有效的延迟。
对一个有延迟的与门逻辑进行时延仿真。
module time_delay_module(input ai, bi,output so_lose, so_get, so_normal);assign #20 so_lose = ai & bi ;assign #5 so_get = ai & bi ;assign so_normal = ai & bi ;
endmodule
testbench 参考如下:
`timescale 1ns/1nsmodule test ;reg ai, bi ;wire so_lose, so_get, so_normal ;initial beginai = 0 ;#25 ; ai = 1 ;#35 ; ai = 0 ; //60ns#40 ; ai = 1 ; //100ns#10 ; ai = 0 ; //110nsendinitial beginbi = 1 ;#70 ; bi = 0 ;#20 ; bi = 1 ;endtime_delay_module u_wire_delay(.ai (ai),.bi (bi),.so_lose (so_lose),.so_get (so_get),.so_normal (so_normal));initial beginforever begin#100;//$display("---gyc---%d", $time);if ($time >= 1000) begin$finish ;endendendendmodule
仿真结果如下:
信号 so_normal 为正常的与逻辑。
由于所有的时延均大于 5ns,所以信号 so_get 的结果为与操作后再延迟 5ns 的结果。
信号 so_lose 前一段是与操作后再延迟 20ns 的结果。
由于信号 ai 第二个高电平持续时间小于 20ns,so_lose 信号会因惯性时延而漏掉对这个脉冲的延时检测,所以后半段 so_lose 信号仍然为 0。
4.1 Verilog 过程结构
过程结构语句有 2 种,initial 与 always 语句。它们是行为级建模的 2 种基本语句。
一个模块中可以包含多个 initial 和 always 语句,但 2 种语句不能嵌套使用。
这些语句在模块间并行执行,与其在模块的前后顺序没有关系。
但是 initial 语句或 always 语句内部可以理解为是顺序执行的(非阻塞赋值除外)。
每个 initial 语句或 always 语句都会产生一个独立的控制流,执行时间都是从 0 时刻开始。
initial语句
-
initial 语句从 0 时刻开始执行,只执行一次,多个 initial 块之间是相互独立的。
-
如果 initial 块内包含多个语句,需要使用关键字 begin 和 end 组成一个块语句。
-
如果 initial 块内只要一条语句,关键字 begin 和 end 可使用也可不使用。
-
initial 理论上来讲是不可综合的,多用于初始化、信号检测等。
对上一节代码稍作修改,进行仿真,代码如下。
`timescale 1ns/1nsmodule test ;reg ai, bi ;initial beginai = 0 ;#25 ; ai = 1 ;#35 ; ai = 0 ; //absolute 60ns#40 ; ai = 1 ; //absolute 100ns#10 ; ai = 0 ; //absolute 110nsendinitial beginbi = 1 ;#70 ; bi = 0 ; //absolute 70ns#20 ; bi = 1 ; //absolute 90nsend//at proper time stop the simulationinitial beginforever begin#100;//$display("---gyc---%d", $time);if ($time >= 1000) begin$finish ;endendendendmodule
仿真结果如下:
可以看出,2 个 initial 进程语句分别给信号 ai,bi 赋值时,相互间并没有影响。
信号 ai,bi 的值按照赋值顺序依次改变,所以 initial 内部语句也可以看做是顺序执行。
always 语句
与 initial 语句相反,always 语句是重复执行的。
always 语句块从 0 时刻开始执行其中的行为语句;当执行完最后一条语句后,便再次执行语句块中的第一条语句,如此循环反复。
由于循环执行的特点,always 语句多用于仿真时钟的产生,信号行为的检测等。
下面用 always 产生一个 100MHz 时钟源,并在 1010ns 时停止仿真代码如下。
代码如下:
`timescale 1ns/1nsmodule test ;parameter CLK_FREQ = 100 ; //100MHzparameter CLK_CYCLE = 1e9 / (CLK_FREQ * 1e6) ; //switch to nsreg clk ;initial clk = 1'b0 ; //clk is initialized to "0"always # (CLK_CYCLE/2) clk = ~clk ; //generating a real clock by reversingalways begin#10;if ($time >= 1000) begin$finish ;endendendmodule
仿真结果如下:
可见,时钟周期是我们想要得到的 100MHz。而且仿真在 1010ns 时停止。
4.2 Verilog 过程赋值
过程性赋值是在 initial 或 always 语句块里的赋值,赋值对象是寄存器、整数、实数等类型。
这些变量在被赋值后,其值将保持不变,直到重新被赋予新值。
-
连续性赋值总是处于激活状态,任何操作数的改变都会影响表达式的结果;
-
过程赋值只有在语句执行的时候,才会起作用。这是连续性赋值与过程性赋值的区别。
Verilog 过程赋值包括 2 种语句:阻塞赋值与非阻塞赋值。
阻塞赋值
阻塞赋值属于顺序执行,即下一条语句执行前,当前语句一定会执行完毕。
阻塞赋值语句使用等号 = 作为赋值符。
前面的仿真中,initial 里面的赋值语句都是用的阻塞赋值。
非阻塞赋值
非阻塞赋值属于并行执行语句,即下一条语句的执行和当前语句的执行是同时进行的,它不会阻塞位于同一个语句块中后面语句的执行。
非阻塞赋值语句使用小于等于号 <= 作为赋值符。
利用下面代码,对阻塞、非阻塞赋值进行仿真,来说明 2 种过程赋值的区别。
`timescale 1ns/1nsmodule test ;reg [3:0] ai, bi ;reg [3:0] ai2, bi2 ;reg [3:0] value_blk ;reg [3:0] value_non ;reg [3:0] value_non2 ;initial beginai = 4'd1 ; //(1)bi = 4'd2 ; //(2)ai2 = 4'd7 ; //(3)bi2 = 4'd8 ; //(4)#20 ; //(5)//non-block-assigment with block-assignmentai = 4'd3 ; //(6)bi = 4'd4 ; //(7)value_blk = ai + bi ; //(8)value_non <= ai + bi ; //(9)//non-block-assigment itselfai2 <= 4'd5 ; //(10)bi2 <= 4'd6 ; //(11)value_non2 <= ai2 + bi2 ; //(12)end//stop the simulationalways begin#10 ;if ($time >= 1000) $finish ;endendmodule
仿真结果如下:
语句(1)-(8)都是阻塞赋值,按照顺序执行。
20ns 之前,信号 ai,bi 值改变。由于过程赋值的特点,value_blk = ai + bi 并没有执行到,所以 20ns 之前,value_blk 值为 X(不确定状态)。
20ns 之后,信号 ai,bi 值再次改变。执行到 value_blk = ai + bi,信号 value_blk 利用信号 ai,bi 的新值得到计算结果 7。
语句(9)-(12)都是非阻塞赋值,并行执行。
首先,(9)-(12)虽然都是并发执行,但是执行顺序也是在(8)之后,所以信号 value_non = ai + bi 计算是也会使用信号 ai,bi 的新值,结果为 7。
其次,(10)-(12)是并发执行,所以 value_non2 = ai2 + bi2 计算时,并不关心信号 ai2,bi2 的最新非阻塞赋值结果。即 value_non2 计算时使用的是信号 ai2,bi2 的旧值,结果为 4’hF。
使用非阻塞赋值避免竞争冒险
上述仿真代码只是为了让读者更好的理解阻塞赋值与非阻塞赋值的区别。实际 Verilog 代码设计时,切记不要在一个过程结构中混合使用阻塞赋值与非阻塞赋值。两种赋值方式混用时,时序不容易控制,很容易得到意外的结果。
更多时候,在设计电路时,always 时序逻辑块中多用非阻塞赋值,always 组合逻辑块中多用阻塞赋值;在仿真电路时,initial 块中一般多用阻塞赋值。
如下所示,为实现在时钟上升沿交换 2 个寄存器值的功能,在 2 个 always 块中使用阻塞赋值。
因为 2 个 always 块中的语句是同时进行的,但是 a=b 与 b=a 是无法判定执行顺序的,这就造成了竞争的局面。
但不管哪个先执行(和编译器等有关系),不考虑 timing 问题时,他们执行顺序总有先后,最后 a 与 b 的值总是相等的。没有达到交换 2 个寄存器值的效果。
always @(posedge clk) begina = b ;
endalways @(posedge clk) beginb = a;
end
但是,如果在 always 块中使用非阻塞赋值,则可以避免上述竞争冒险的情况。
如下所示,2 个 always 块中语句并行执行,赋值操作右端操作数使用的是上一个时钟周期的旧值,此时 a<=b 与 b<=a 就可以相互不干扰的执行,达到交换寄存器值的目的。
always @(posedge clk) begina <= b ;
endalways @(posedge clk) beginb <= a;
end
当然,利用下面代码也可以实现交换寄存器值的功能,但是显然不如在 always 块中直接用非阻塞赋值简单直观。
always @(posedge clk) begintemp = a ;a = b ;b = temp ;
end
4.3 Verilog 时序控制
Verilog 提供了 2 大类时序控制方法:时延控制和事件控制。
事件控制主要分为边沿触发事件控制与电平敏感事件控制。
时延控制
基于时延的时序控制出现在表达式中,它指定了语句从开始执行到执行完毕之间的时间间隔。
时延可以是数字、标识符或者表达式。
根据在表达式中的位置差异,时延控制又可以分为常规时延与内嵌时延。
常规时延
遇到常规延时时,该语句需要等待一定时间,然后将计算结果赋值给目标信号。
格式为:#delay procedural_statement,例如:
reg value_test ;
reg value_general ;
#10 value_general = value_test ;
该时延方式的另一种写法是直接将井号 # 独立成一个时延执行语句,例如:
#10 ;
value_ single = value_test ;
内嵌时延
遇到内嵌延时时,该语句先将计算结果保存,然后等待一定的时间后赋值给目标信号。
内嵌时延控制加在赋值号之后。例如:
reg value_test ;
reg value_embed ;
value_embed = #10 value_test ;
需要说明的是,这 2 种时延控制方式的效果是有所不同的。
当延时语句的赋值符号右端是常量时,2 种时延控制都能达到相同的延时赋值效果。
当延时语句的赋值符号右端是变量时,2 种时延控制可能会产生不同的延时赋值效果。
例如下面仿真代码:
`timescale 1ns/1nsmodule test ;reg value_test ;reg value_general, value_embed, value_single ;//signal sourceinitial beginvalue_test = 0 ;#25 ; value_test = 1 ;#35 ; value_test = 0 ; //absolute 60ns#40 ; value_test = 1 ; //absolute 100ns#10 ; value_test = 0 ; //absolute 110nsend//(1)general delay controlinitial beginvalue_general = 1;#10 value_general = value_test ; //10ns, value_test=0#45 value_general = value_test ; //55ns, value_test=1#30 value_general = value_test ; //85ns, value_test=0#20 value_general = value_test ; //105ns, value_test=1end//(2)embedded delay controlinitial beginvalue_embed = 1;value_embed = #10 value_test ; //0ns, value_test=0value_embed = #45 value_test ; //10ns, value_test=0value_embed = #30 value_test ; //55ns, value_test=1value_embed = #20 value_test ; //85ns, value_test=0end//(3)single delay controlinitial beginvalue_single = 1;#10 ;value_single = value_test ; //10ns, value_test=0#45 ;value_single = value_test ; //55ns, value_test=1#30 ;value_single = value_test ; //85ns, value_test=0#20 ;value_single = value_test ; //105ns, value_test=1endalways begin#10;if ($time >= 150) begin$finish ;endendendmodule
仿真结果如下,由图可知:
- (1)一般延时的两种表达方式执行的结果都是一致的。
- (2)一般时延赋值方式:遇到延迟语句后先延迟一定的时间,然后将当前操作数赋值给目标信号,并没有"惯性延迟"的特点,不会漏掉相对较窄的脉冲。
- (3)内嵌时延赋值方式:遇到延迟语句后,先计算出表达式右端的结果,然后再延迟一定的时间,赋值给目标信号。
下面分析下内嵌延时的赋值过程:
value_embed = #10 value_test ; //0ns, value_test=0
0ns 时,执行此延时语句。
先将 0 赋值给信号 value_embed, 延迟 10ns 输出为 0;
value_embed = #45 value_test ; //10ns, value_test=0
10ns 时,执行此延时语句。
由于此时 value_test 仍然为 0,所以 value_embed 值不变。
即到 55ns 时,value_embed 值仍然保持为 0。
value_embed = #30 value_test ; //55ns, value_test=1
同理,55ns 时,value_test 值为 1,将其赋值给 value_embed 并延迟 30ns 输出。
所以 85ns 时,value_embed 输出为 1。
value_embed = #20 value_test ; //85ns, value_test=0
同理,105ns 时,value_embed 输出为 0。
边沿触发事件控制
在 Verilog 中,事件是指某一个 reg 或 wire 型变量发生了值的变化。
基于事件触发的时序控制又主要分为以下几种。
一般事件控制
事件控制用符号 @ 表示。
语句执行的条件是信号的值发生特定的变化。
关键字 posedge 指信号发生边沿正向跳变,negedge 指信号发生负向边沿跳变,未指明跳变方向时,则 2 种情况的边沿变化都会触发相关事件。例如:
//信号clk只要发生变化,就执行q<=d,双边沿D触发器模型
always @(clk) q <= d ;
//在信号clk上升沿时刻,执行q<=d,正边沿D触发器模型
always @(posedge clk) q <= d ;
//在信号clk下降沿时刻,执行q<=d,负边沿D触发器模型
always @(negedge clk) q <= d ;
//立刻计算d的值,并在clk上升沿时刻赋值给q,不推荐这种写法
q = @(posedge clk) d ;
命名事件控制
用户可以声明 event(事件)类型的变量,并触发该变量来识别该事件是否发生。
命名事件用关键字 event 来声明,触发信号用 -> 表示。例如:
event start_receiving ;
always @( posedge clk_samp) begin-> start_receiving ; //采样时钟上升沿作为时间触发时刻
endalways @(start_receiving) begindata_buf = {data_if[0], data_if[1]} ; //触发时刻,对多维数据整合
end
敏感列表
当多个信号或事件中任意一个发生变化都能够触发语句的执行时,Verilog 中使用"或"表达式来描述这种情况,用关键字 or 连接多个事件或信号。
这些事件或信号组成的列表称为"敏感列表"。当然,or 也可以用逗号 , 来代替。例如:
//带有低有效复位端的D触发器模型
always @(posedge clk or negedge rstn) begin
//always @(posedge clk , negedge rstn) begin
//也可以使用逗号陈列多个事件触发if(! rstn)beginq <= 1'b ; endelse beginq <= d ;end
end
当组合逻辑输入变量很多时,那么编写敏感列表会很繁琐。
此时,更为简洁的写法是 @* 或 @(*),表示对语句块中的所有输入变量的变化都是敏感的。例如:
always @(*) begin
//always @(a, b, c, d, e, f, g, h, i, j, k, l, m) begin
//两种写法等价assign s = a? b+c : d ? e+f : g ? h+i : j ? k+l : m ;
end
电平敏感事件控制
前面所讨论的事件控制都是需要等待信号值的变化或事件的触发,使用 @+敏感列表 的方式来表示的。
Verilog 中还支持使用电平作为敏感信号来控制时序,即后面语句的执行需要等待某个条件为真。
Verilog 中使用关键字 wait 来表示这种电平敏感情况。例如:
initial beginwait (start_enable) ; //等待 start 信号forever begin//start信号使能后,在clk_samp上升沿,对数据进行整合@(posedge clk_samp) ;data_buf = {data_if[0], data_if[1]} ; end
end
4.4 Verilog 语句块
Verilog 语句块提供了将两条或更多条语句组成语法结构上相当于一条一句的机制。主要包括两种类型:顺序块和并行块。
顺序块
顺序块用关键字 begin 和 end 来表示。
顺序块中的语句是一条条执行的。当然,非阻塞赋值除外。
顺序块中每条语句的时延总是与其前面语句执行的时间相关。
在本节之前的仿真中,initial 块中的阻塞赋值,都是顺序块的实例。
并行块
并行块有关键字 fork 和 join 来表示。
并行块中的语句是并行执行的,即便是阻塞形式的赋值。
并行块中每条语句的时延都是与块语句开始执行的时间相关。
顺序块与并行块的区别显而易见,下面用仿真说明。
仿真代码如下:
`timescale 1ns/1nsmodule test ;reg [3:0] ai_sequen, bi_sequen ;reg [3:0] ai_paral, bi_paral ;reg [3:0] ai_nonblk, bi_nonblk ;//============================================================////(1)Sequence blockinitial begin#5 ai_sequen = 4'd5 ; //at 5ns#5 bi_sequen = 4'd8 ; //at 10nsend//(2)fork blockinitial fork#5 ai_paral = 4'd5 ; //at 5ns#5 bi_paral = 4'd8 ; //at 5nsjoin//(3)non-block blockinitial fork#5 ai_nonblk <= 4'd5 ; //at 5ns#5 bi_nonblk <= 4'd8 ; //at 5nsjoinendmodule
仿真结果如下:
如图所示,顺序块顺序执行,第 10ns 时,信号 bi_sequen 才赋值为 8。
而并行块,ai_paral 与 bi_paral 的赋值是同时执行的,所以均在 5ns 时被赋值。
而非阻塞赋值,也能达到和并行块同等的赋值效果。
嵌套块
顺序块和并行块还可以嵌套使用。
`timescale 1ns/1nsmodule test ;reg [3:0] ai_sequen2, bi_sequen2 ;reg [3:0] ai_paral2, bi_paral2 ;initial beginai_sequen2 = 4'd5 ; //at 0nsfork#10 ai_paral2 = 4'd5 ; //at 10ns#15 bi_paral2 = 4'd8 ; //at 15nsjoin#20 bi_sequen2 = 4'd8 ; //at 35nsendendmodule
仿真结果如下:
并行块语句块内是并行执行,所以信号 ai_paral2 和信号 bi_paral2 分别在 10ns, 15ns 时被赋值。
而并行块中最长的执行时间为 15ns,所以顺序块中的信号 bi_sequen2 在 35ns 时被赋值。
命名块
我们可以给块语句结构命名。
命名的块中可以声明局部变量,通过层次名引用的方法对变量进行访问。
`timescale 1ns/1nsmodule test;initial begin: runoob //命名模块名字为runoob,分号不能少integer i ; //此变量可以通过test.runoob.i 被其他模块使用i = 0 ;forever begin#10 i = i + 10 ; endendreg stop_flag ;initial stop_flag = 1'b0 ;always begin : detect_stopif ( test.runoob.i == 100) begin //i累加10次,即100ns时停止仿真$display("Now you can stop the simulation!!!");stop_flag = 1'b1 ;end#10 ;endendmodule
仿真结果如下:
命名的块也可以被禁用,用关键字 disable 来表示。
disable 可以终止命名块的执行,可以用来从循环中退出、处理错误等。
与 C 语言中 break 类似,但是 break 只能退出当前所在循环,而 disable 可以禁用设计中任何一个命名的块。
`timescale 1ns/1nsmodule test;initial begin: runoob_d //命名模块名字为runoob_dinteger i_d ;i_d = 0 ;while(i_d<=100) begin: runoob_d2# 10 ;if (i_d >= 50) begin //累加5次停止累加disable runoob_d3.clk_gen ;//stop 外部block: clk_gendisable runoob_d2 ; //stop 当前block: runoob_d2endi_d = i_d + 10 ;endendreg clk ;initial begin: runoob_d3while (1) begin: clk_gen //时钟产生模块clk=1 ; #10 ;clk=0 ; #10 ;endendendmodule
仿真结果如下:
由图可知,信号 i_d 累加到 50 以后,便不再累加,以后 clk 时钟也不再产生。
可见,disable 退出了当前的 while 块。
需要说明的是,disable 在 always 或 forever 块中使用时只能退出当前回合,下一次语句还是会在 always 或 forever 中执行。
因为 always 块和 forever 块是一直执行的,此时的 disable 有点类似 C 语言中的 continue 功能。
4.5 Verilog 条件语句
条件语句
条件(if)语句用于控制执行语句要根据条件判断来确定是否执行。
条件语句用关键字 if 和 else 来声明,条件表达式必须在圆括号中。
条件语句使用结构说明如下:
if (condition1) true_statement1 ;
else if (condition2) true_statement2 ;
else if (condition3) true_statement3 ;
else default_statement ;
- if 语句执行时,如果 condition1 为真,则执行 true_statement1 ;如果 condition1 为假,condition2 为真,则执行 true_statement2;依次类推。
- else if 与 else 结构可以省略,即可以只有一个 if 条件判断和一组执行语句 ture_statement1 就可以构成一个执行过程。
- else if 可以叠加多个,不仅限于 1 或 2 个。
- ture_statement1 等执行语句可以是一条语句,也可以是多条。如果是多条执行语句,则需要用 begin 与 end 关键字进行说明。
下面代码实现了一个 4 路选择器的功能。
module mux4to1(input [1:0] sel ,input [1:0] p0 ,input [1:0] p1 ,input [1:0] p2 ,input [1:0] p3 ,output [1:0] sout);reg [1:0] sout_t ;always @(*) beginif (sel == 2'b00)sout_t = p0 ;else if (sel == 2'b01)sout_t = p1 ;else if (sel == 2'b10)sout_t = p2 ;elsesout_t = p3 ;endassign sout = sout_t ;endmodule
testbench 代码如下:
`timescale 1ns/1nsmodule test ;reg [1:0] sel ;wire [1:0] sout ;initial beginsel = 0 ;#10 sel = 3 ;#10 sel = 1 ;#10 sel = 0 ;#10 sel = 2 ;endmux4to1 u_mux4to1 (.sel (sel),.p0 (2'b00), //path0 are assigned to 0.p1 (2'b01), //path1 are assigned to 1.p2 (2'b10), //path2 are assigned to 2.p3 (2'b11), //path3 are assigned to 3.sout (sout));//finish the simulationalways begin#100;if ($time >= 1000) $finish ;endendmodule
仿真结果如下。
由图可知,输出信号与选择信号、输入信号的状态是相匹配的。
事例中 if 条件每次执行的语句只有一条,没有使用 begin 与 end 关键字。
但如果是 if-if-else 的形式,即便执行语句只有一条,不使用 begin 与 end 关键字也会引起歧义。
例如下面代码,虽然格式上加以区分,但是 else 对应哪一个 if 条件,是有歧义的。
if(en)if(sel == 2'b1)sout = p1s ;elsesout = p0 ;
当然,编译器一般按照就近原则,使 else 与最近的一个 if(例子中第二个 if)相对应。
但显然这样的写法是不规范且不安全的。
所以条件语句中加入 begin 与 and 关键字就是一个很好的习惯。
例如上述代码稍作修改,就不会再有书写上的歧义。
if(en) beginif(sel == 2'b1) beginsout = p1s ;endelse beginsout = p0 ;end
end
4.6 Verilog 多路分支语句
case 语句是一种多路条件分支的形式,可以解决 if 语句中有多个条件选项时使用不方便的问题。
case 语句
case 语句格式如下:
case(case_expr)condition1 : true_statement1 ;condition2 : true_statement2 ;……default : default_statement ;
endcase
case 语句执行时,如果 condition1 为真,则执行 true_statement1 ; 如果 condition1 为假,condition2 为真,则执行 true_statement2;依次类推。如果各个 condition 都不为真,则执行 default_statement 语句。
default 语句是可选的,且在一个 case 语句中不能有多个 default 语句。
条件选项可以有多个,不仅限于 condition1、condition2 等,而且这些条件选项不要求互斥。虽然这些条件选项是并发比较的,但执行效果是谁在前且条件为真谁被执行。
ture_statement1 等执行语句可以是一条语句,也可以是多条。如果是多条执行语句,则需要用 begin 与 end 关键字进行说明。
case 语句支持嵌套使用。
下面用 case 语句代替 if 语句实现了一个 4 路选择器的功能。仿真结果与 testbench 可参考条件语句一章,两者完全一致。
module mux4to1(input [1:0] sel ,input [1:0] p0 ,input [1:0] p1 ,input [1:0] p2 ,input [1:0] p3 ,output [1:0] sout);reg [1:0] sout_t ;always @(*)case(sel)2'b00: begin sout_t = p0 ;end2'b01: sout_t = p1 ;2'b10: sout_t = p2 ;default: sout_t = p3 ;endcaseassign sout = sout_t ;endmodule
case 语句中的条件选项表单式不必都是常量,也可以是 x 值或 z 值。
当多个条件选项下需要执行相同的语句时,多个条件选项可以用逗号分开,放在同一个语句块的候选项中。
但是 case 语句中的 x 或 z 的比较逻辑是不可综合的,所以一般不建议在 case 语句中使用 x 或 z 作为比较值。
例如,对 4 路选择器的 case 语句进行扩展,举例如下:
case(sel)2'b00: sout_t = p0 ;2'b01: sout_t = p1 ;2'b10: sout_t = p2 ;2'b11: sout_t = p3 ;2'bx0, 2'bx1, 2'bxz, 2'bxx, 2'b0x, 2'b1x, 2'bzx :sout_t = 2'bxx ;2'bz0, 2'bz1, 2'bzz, 2'b0z, 2'b1z :sout_t = 2'bzz ;default: $display("Unexpected input control!!!");
endcase
casex/casez 语句
casex、 casez 语句是 case 语句的变形,用来表示条件选项中的无关项。
casex 用 “x” 来表示无关值,casez 用问号 “?” 来表示无关值。
两者的实现的功能是完全一致的,语法与 case 语句也完全一致。
但是 casex、casez 一般是不可综合的,多用于仿真。
例如用 casez 语句来实现一个 4bit 控制端的 4 路选择选择器。
module mux4to1(input [3:0] sel ,input [1:0] p0 ,input [1:0] p1 ,input [1:0] p2 ,input [1:0] p3 ,output [1:0] sout);reg [1:0] sout_t ;always @(*)casez(sel)4'b???1: sout_t = p0 ;4'b??1?: sout_t = p1 ;4'b?1??: sout_t = p2 ;4'b1???: sout_t = p3 ; default: sout_t = 2'b0 ;endcaseassign sout = sout_t ;endmodule
4.7 Verilog 循环语句
Verilog 循环语句有 4 种类型,分别是 while,for,repeat,和 forever 循环。循环语句只能在 always 或 initial 块中使用,但可以包含延迟表达式。
while 循环
while 循环语法格式如下:
while (condition) begin…
end
while 循环中止条件为 condition 为假。
如果开始执行到 while 循环时 condition 已经为假,那么循环语句一次也不会执行。
当然,执行语句只有一条时,关键字 begin 与 end 可以省略。
下面代码执行时,counter 执行了 11 次。
`timescale 1ns/1nsmodule test ;reg [3:0] counter ;initial begincounter = 'b0 ;while (counter<=10) begin#10 ;counter = counter + 1'b1 ;endend//stop the simulationalways begin#10 ; if ($time >= 1000) $finish ;endendmodule
for 循环
for 循环语法格式如下:
for(initial_assignment; condition ; step_assignment) begin…
end
initial_assignment 为初始条件。
condition 为终止条件,condition 为假时,立即跳出循环。
step_assignment 为改变控制变量的过程赋值语句,通常为增加或减少循环变量计数。
一般来说,因为初始条件和自加操作等过程都已经包含在 for 循环中,所以 for 循环写法比 while 更为紧凑,但也不是所有的情况下都能使用 for 循环来代替 while 循环。
下面 for 循环的例子,实现了与 while 循环中例子一样的效果。需要注意的是,i = i + 1 不能像 C 语言那样写成 i++ 的形式,i = i -1 也不能写成 i – 的形式。
// for 循环语句
integer i ;
reg [3:0] counter2 ;
initial begincounter2 = 'b0 ;for (i=0; i<=10; i=i+1) begin#10 ;counter2 = counter2 + 1'b1 ;end
end
repeat 循环
repeat 循环语法格式如下:
repeat (loop_times) begin…
end
repeat 的功能是执行固定次数的循环,它不能像 while 循环那样用一个逻辑表达式来确定循环是否继续执行。
repeat 循环的次数必须是一个常量、变量或信号。
如果循环次数是变量信号,则循环次数是开始执行 repeat 循环时变量信号的值。
即便执行期间,循环次数代表的变量信号值发生了变化,repeat 执行次数也不会改变。
下面 repeat 循环例子,实现了与 while 循环中的例子一样的效果。
// repeat 循环语句
reg [3:0] counter3 ;
initial begincounter3 = 'b0 ;repeat (11) begin //重复11次#10 ;counter3 = counter3 + 1'b1 ;end
end
下面 repeat 循环例子,实现了连续存储 8 个数据的功能:
always @(posedge clk or negedge rstn) beginj = 0 ;if (!rstn) beginrepeat (8) beginbuffer[j] <= 'b0 ; //没有延迟的赋值,即同时赋值为0j = j + 1 ;endendelse if (enable) beginrepeat (8) begin@(posedge clk) buffer[j] <= counter3 ; //在下一个clk的上升沿赋值j = j + 1 ;endend
end
仿真结果如下图。
由图可知,rstn 拉高时,buffer 的 8 个向量同时赋值为 0。
第二个时钟周期后,buffer 依次被 counter3 赋值,实现了连续存储 8 个数据的功能。
forever 循环
forever 循环语法格式如下:
forever begin…
end
forever 语句表示永久循环,不包含任何条件表达式,一旦执行便无限的执行下去,系统函数 $finish 可退出 forever。
forever 相当于 while(1) 。
通常,forever 循环是和时序控制结构配合使用的。
例如,使用 forever 语句产生一个时钟:
reg clk ;
initial beginclk = 0 ;forever beginclk = ~clk ;#5 ;end
end
例如,使用 forever 语句实现一个时钟边沿控制的寄存器间数据传输功能:
reg clk ;
reg data_in, data_temp ;
initial beginforever @(posedge clk) data_temp = data_in ;
end
4.8 Verilog 过程连续赋值
过程连续赋值是过程赋值的一种。这种赋值语句能够替换其他所有 wire 或 reg 的赋值,改写了 wire 或 reg 型变量的当前值。
与过程赋值不同的是,过程连续赋值的表达式能被连续的驱动到 wire 或 reg 型变量中,即过程连续赋值发生作用时,右端表达式中任意操作数的变化都会引起过程连续赋值语句的重新执行。
过程连续性赋值主要有 2 种,assign-deassign 和 force-release。
assign, deassign
assign(过程赋值操作)与 deassign (取消过程赋值操作)表示第一类过程连续赋值语句。
赋值对象只能是寄存器或寄存器组,而不能是 wire 型变量。
赋值过程中对寄存器连续赋值,寄存器中的值被保留直到被重新赋值。
例如,一个带复位端的 D 触发器可以用下面代码描述:
module dff_normal(input rstn,input clk,input D,output reg Q);always @(posedge clk or negedge rstn) beginif(!rstn) begin //Q = 0 after reset effectiveQ <= 1'b0 ;endelse beginQ <= D ; //Q = D at posedge of clockendendendmodule
下面,用 assign 与 deassign 改写,完成相同的功能。
即在复位信号为 0 时,Q 端被 assign 语句赋值,始终输出为 0。
复位信号为 1 时,Q 端被 deassign 语句取消赋值,在时钟上升沿被重新赋值。
module dff_assign(input rstn,input clk,input D,output reg Q);always @(posedge clk) beginQ <= D ; //Q = D at posedge of clockendalways @(negedge rstn) beginif(!rstn) beginassign Q = 1'b0 ; //change Q value when reset effectiveendelse begin //cancel the Q value overlay,deassign Q ; //and Q remains 0-value until the coming of clock posedgeendendendmodule
force, release
force (强制赋值操作)与 release(取消强制赋值)表示第二类过程连续赋值语句。
使用方法和效果,和 assign 与 deassign 类似,但赋值对象可以是 reg 型变量,也可以是 wire 型变量。
因为是无条件强制赋值,一般多用于交互式调试过程,不要在设计模块中使用。
当 force 作用在寄存器上时,寄存器当前值被覆盖;release 时该寄存器值将继续保留强制赋值时的值。之后,该寄存器的值可以被原有的过程赋值语句改变。
当 force 作用在线网上时,线网值也会被强制赋值。但是,一旦 release 该线网型变量,其值马上变为原有的驱动值。
为直观的观察两种类型变量强制赋值的区别,利用第一节中的计数器 counter10 作为设计模块,testbench 设计如下。
`timescale 1ns/1nsmodule test ;reg rstn ;reg clk ;reg [3:0] cnt ;wire cout ;counter10 u_counter (.rstn (rstn),.clk (clk),.cnt (cnt),.cout (cout));initial beginclk = 0 ;rstn = 0 ;#10 ;rstn = 1'b1 ;wait (test.u_counter.cnt_temp == 4'd4) ;@(negedge clk) ;force test.u_counter.cnt_temp = 4'd6 ;force test.u_counter.cout = 1'b1 ;#40 ;@(negedge clk) ;release test.u_counter.cnt_temp ;release test.u_counter.cout ;endinitial beginclk = 0 ;forever #10 clk = ~ clk ;end//finish the simulationalways begin#1000;if ($time >= 1000) $finish ;endendmodule // test
仿真结果如下。
由图可知,在 cnt_temp 等于 4 时(80ns), cnt_temp 被强制赋值为 6,cout 被强制赋值为 1。
release 时(120ns), cnt_temp 为寄存器类型,仍然保持原有值不变,直到时钟上升沿对其进行加法赋值操作,值才变为 7 。
而 120ns 时,由于 cout 是线网型变量,其值不能保存。原码 counter10 模型中存在驱动语句: assign cout = (cnt_temp==4’d9) ,所以 cout 值变为 0 。
相关文章:

Verilog基础【二】
3.1 Verilog 连续赋值 关键词:assign, 全加器 连续赋值语句是 Verilog 数据流建模的基本语句,用于对 wire 型变量进行赋值。: assign LHS_target RHS_expression ;LHS(left hand side)…...

定时推送任务 Apache HttpClient/okhttp3
定时推送任务 需求 需要定时推送我方的数据到对方那边 方法1 Apache HttpClient 此方法指定推送过去的信息转为utf-8格式的json字符串 Scheduled(initialDelay 1000 * 120, fixedDelay 1000 * 60 * 5) public void diseaseInterface() {String lockKey "lock:dise…...

centos7 安装 mysql
命令记录,未整理; 1. 下载mysql5.7的安装包,上传到linux系统某个目录中 2. 使用tar -xvf 解压 tar包 tar -xvf mysql-5.7.35-1.el7.x86_64.rpm-bundle.tar 3. 使用 rpm -ivh 安装 mysql rpm -ivh mysql-community-common-5.7.35-1.el7.x8…...

src挖掘技巧总结分享
src挖洞技术分享 src推荐刚入门的新手首选公益src如漏洞盒子、补天src,因为漏洞盒子收录范围广,只要是国内的站点都收入,相比其它src平台挖掘难度非常适合新手。后续可以尝试先从一些小的src厂商入手。 首先是熟能生巧,我一开始挖…...

【面试八股总结】传输控制协议TCP(一)
一、什么是TCP协议 TCP是传输控制协议Transmission Control Protocol TCP 是面向连接的、可靠的、基于字节流的传输层通信协议。 面向连接的:每条TCP连接杜只能有两个端点,每一条TCP连接只能是点对点的(一对一)可靠的:…...

【系统架构师】-第13章-层次式架构设计
层次式体系结构设计是将系统组成一个层次结构,每一层 为上层服务 ,并作为下层客户。 在一些层次系统中,除了一些精心挑选的输出函数外, 内部的层接口只对相邻的层可见 。 连接件通过决定层间如何交互的协议来定义,拓扑…...

【操作系统】想要更好的学习计算机,操作系统的知识必不可少!!!
操作系统的概念 导言一、日常生活中的操作系统二、计算机系统层次结构三、操作系统的定义3.1 控制和管理计算机资源3.2 组织、调度计算机的工作与资源的分配3.3 给用户和其他软件提供方便接口与环境3.4 总结 四、操作系统的目标和功能4.1 作为管理者4.1.1 处理机管理4.1.2 存储…...
AtCoder Grand Contest 066 B. Decreasing Digit Sums(构造 打表找规律)
题目 给定一个n(n<50),记f(x)是x各数位的加和,例如f(331)3317 要求输出一个x(),且对于任意i∈[1,n],均有成立 思路来源 jiangly B站讲解 题解 首先n没啥用,构造一个n50成立的case即可, 给定一个x…...

Hadoop系列总结
一、Hadoop linux基本操作 前提掌握Linux基本操作 参考 Linux基本操作-CSDN博客 1、查看hadoop指定路径下文件大小前6的文件信息 hdfs dfs[hadoop fs] -du -h /path/to/directory|sort -hr|head -n 6...

【第三方登录】Twitter
创建应用 APPID 和 相关回调配置 重新设置api key 和 api secret 设置回调和网址 还有 APP的类型 拿到ClientID 和 Client Secret 源码实现 获取Twitter 的登录地址 public function twitterUrl() {global $db,$request,$comId;require "inc/twitter_client/twitte…...

C++经典面试题目(十七)
1、请解释拷贝构造函数的作用,并说明其调用时机。 拷贝构造函数是一种特殊的构造函数,用于创建一个新对象,其内容与另一个对象相同。它将一个已存在的对象作为参数,并使用该对象的值来初始化新创建的对象。拷贝构造函数的调用时机…...

DFS2 C++
一、指数型枚举 1、题目: 从 1∼n1∼ 这 n 个整数中随机选取任意多个,输出所有可能的选择方案。 输入格式 输入一个整数 n。 输出格式 每行输出一种方案。 同一行内的数必须升序排列,相邻两个数用恰好 11 个空格隔开。 对于没有选任何…...
2021-08-06
yarn的简介: Yarn是facebook发布的一款取代npm的包管理工具。 yarn的特点: 速度超快。 Yarn 缓存了每个下载过的包,所以再次使用时无需重复下载。 同时利用并行下载以最大化资源利用率,因此安装速度更快。超级安全。 在执行代码…...

Centos服务器Open Gauss 部署
近期很多的项目由于信创要求使用一些国产的数据库,比如OpenGauss。OpenGuass是华为高斯DB的开源版,内核还是PostgreSQL,商业版是收费的。这里记录一下是如何安装部署 的。 官方中文文档 官方下载地址 部署要求 操作系统要求 ARMÿ…...

Vue与Electron融合之道:从Web App到桌面App的华丽转身
Vue与Electron融合之道:从Web App到桌面App的华丽转身 引言Vue 与 Electron 的天然契合共享技术栈高效开发与部署用户体验升级 Vue 与 Electron 融合实战初始化项目调整Vue项目结构利用 Electron API 增强功能定制桌面应用外观构建与部署 结语 引言 Vue.js 作为一款…...

Higress 基于自定义插件访问 Redis
作者:钰诚 简介 基于 wasm 机制,Higress 提供了优秀的可扩展性,用户可以基于 Go/C/Rust 编写 wasm 插件,自定义请求处理逻辑,满足用户的个性化需求,目前插件已经支持 redis 调用,使得用户能够…...

Mysql的库函数
MySQL是一个流行的开源关系型数据库管理系统,它提供了大量的内置库函数,用于在查询时执行各种操作。这些函数可以帮助开发者在数据检索、转换和处理过程中实现更复杂的逻辑。 1 字符串函数 函数描述ASCII(s)返回字符串 s 的第一个字符的 ASCII 码。CHA…...

绿联 安装onlyoffice容器并启用Cloudreve的office在线预览与编辑功能
原文:绿联 安装onlyoffice容器并启用Cloudreve的office在线预览与编辑功能 本篇教程以配合Cloudreve扩展文档预览/编辑为目的编写,OnlyOffice的其他用途未深入研究也不做探讨,仅使用WOPI能力。 硬件要求 镜像大小:2.91Gb或更大 …...

金钱卦起卦
前言 本文目的: 1.了解什么是《易经》 2.了解什么是八卦/六十四卦 3.金钱卦起卦方法 4.如何解卦 一、什么是易经? 1、易经就是一本书、一本著作 2、它的成书经历了三个阶段 第一阶段:在5000多年前,由人类的始祖伏羲通过观察时…...

学透Spring Boot 003 —— Spring 和 Spring Boot 常用注解(附面试题和思维导图)
这是 学透 Spring Boot 专栏 的第三篇,欢迎关注我,与我一起学习和探讨 Spring Boot 相关知识,学透 Spring Boot。 从面试题说起 今天我们通过一道和Spring Boot有关的常见面试题入手。 面试题:说说 Spring Boot 中有哪些常用注解…...

新能源汽车充电桩常见类型及充电桩站场的智能监管方案
随着新能源汽车市场的迅猛发展,充电桩作为支持其运行的基础设施,也呈现出多样化的类型。这些充电桩不仅在外形和功能上存在差异,更在充电速度、充电方式以及使用场景等方面展现出独特的优势。 一、充电桩类型及区别 1、慢充桩(交…...

让工作自动化起来!无所不能的Python
文章目录 前言一、Python是办公自动化的重要工具二、Python是提升职场竞争力的利器三、Python是企业数字化的重要平台四、Python是AI发展的重要通道之一编辑推荐内容简介作者简介目录前言为什么要写这本书 读者对象如何阅读本书赠书活动 前言 随着我国企业数字化和信息化的深入…...

Facebook轮播广告是什么?投放过程中有哪些需要注意的吗?
轮播广告是Facebook广告形式中的一种,可以把3—5个广告合并到一个可滚动的广告单元中。轮播广告会出现在新鲜事即News Feed中,是独立站卖家常用的一种广告形式 为什么选择轮播广告? 转化率更高:相较于单图广告,轮播广…...

3、jvm基础知识(三)
如何判断堆上的对象没有被引用? 常见的有两种判断方法:引用计数法和可达性分析法。 引用计数法会为每个对象维护一个引用计数器,当对象被引用时加1,取消引用时减1。 引用计数法的优点是实现简单,缺点有两点࿱…...

leetcode414-Third Maximum Number
这道题目求第三大数,如果第三大数不存在则返回最大数,且该数字最大值不超过2^31-1。从这个提示上就可以看出这个数字是用int类型表示的。我们当然可以通过排序的方式先给数组排序然后很容易的就能求解。但是有没有什么更好的办法呢?对于数组来…...

解决Quartus与modelsim联合仿真问题:# Error loading design解决,是tb文件中没加:`timescale 1ns/1ns
解决Quartus与modelsim联合仿真问题:# Error loading design解决,是tb文件中没加:timescale 1,一直走下来,在modelsim中出现了下面问题2,rtl文件、tb文件2.1,rtl代码2.2,tb测试2.3&a…...

vue使用elementui组件的的对话框;使用ref
1.第一步,先在父组件中引用,设置ref的值 <el-dialog :visible.sync"dialogEditVisible"append-to-body width"1000px" title"编辑":close-on-click-modal"false"><dialog-edit v-if"dialogEditV…...

第十四届蓝桥杯(八题C++ 题目+代码+注解)
目录 题目一(日期统计 纯暴力): 代码: 题目二(01串的熵 模拟): 代码: 题目三(治炼金属): 代码: 题目四(飞机降落 深度…...

HTTP协议格式详解之报头(HTTP header)、请求正文(body)
在之前文章中我们已经介绍了HTTP的首行信息,HTTP协议格式详解之首行信息解析-CSDN博客这篇文章中我们继续介绍HTTP协议的报头部分。 一、报头(HTTP header) a)请求头部(Request Headers): Host:指定服务器…...

[yolox]ubuntu上部署yolox的ncnn模型
首先转换pytorch->onnx->param模型,这个过程可以查资料步骤有点多,参考blog.51cto.com/u_15660370/6408303,这里重点讲解转换后部署。 测试环境: ubuntu18.04 opencv3.4.4(编译过程省略,参考我其他博客) 安装…...