第九篇 有限状态机

实验九 有限状态机

9.1 实验目的

  1. 学习有限状态机的组成与类型;

  2. 掌握有限状态机的设计方式;

  3. 学习有限状态机的编码方式;

  4. 掌握使用有限状态机进行设计的方法。

9.2 原理介绍

9.2.1 有限状态机的基本概念

有限状态机(Finite State Machine,FSM)通常又称为状态机,是时序逻辑电路设计中经常采用的一种方式,也是数字系统设计的重要组成部分,尤其适用于设计数字系统的控制模块。在一些需要控制高速器件的场合,用状态机进行设计是解决问题的一种很好的方案,具有速度快、结构简单、可靠性高等优点。同时有限状态机也是时序电路的通用模型,任何时序电路都可以表示为有限状态机。

状态机的组成

有限状态机一般包括寄存器逻辑和组合逻辑两部分,寄存器用于存储状态,组合逻辑用于状态译码和产生输出信号,其电路的结构如图9.1所示。寄存器(存储电路)部分接收组合逻辑电路的内部输出信号,由时钟信号控制,存储的现态在时钟作用下变为次态。组合逻辑电路接收的是输入信号和存储电路的现态,在现态和输入信号的共同作用下产生次态和输出信号。

image-20211109144847300

图9.1 有限状态机结构框图

状态机的分类

根据输出信号产生方式的不同,有限状态机可以分为米利型(Mealy)和摩尔型(Moore)两类。Mealy型状态机的输出不仅取决于电路当前状态,还取决于电路的输入信号,其输出是在输入变化后立即变化的,不依赖时钟信号的同步,其结构图如图9.2所示;Moore型状态机的输出仅依赖当前状态,而与输入信号无关,但输出发生变化时还需等待时钟的到来,必须等待状态发生变化时才能导致输出变化,因此比Mealy型要多等待一个时钟周期,其结构图如图9.3所示。

image-20211109150735071

图9.2 Mealy型状态机结构图

image-20211109151127376

图9.3 Moore型状态机结构图

状态机的状态图表示法

一般来说,状态机有三种表示方法:状态图、状态表和算法状态机图。实际上,这三种表示方法是等价的,相互之间可以进行任意转换。在用Verilog HDL描述状态机时,通常会用到状态图,下面介绍状态图的表示方法。

状态图是以信号流图方式表示出电路的状态转换过程。在状态图中,每个状态用一个圆圈(或者椭圆圈)表示,圆圈内有指示状态的符号。用带箭头的方向线指示状态转换的方向,当方向的起点和终点都在同一个圆圈上,则表示状态不变。标在方向线旁斜线左、右两侧的二进制数分别表示引起状态转移的输入信号以及当前输出信号。

图9.4是Mealy状态图的一个例子,其中A、B、C、D表示电路四个不同的状态,方向线旁边的X/Y表示引起状态转移的输入信号以及当前输出信号。一般来说,状态机中的状态转移有两种方式:无条件转移和有条件转移。在图9.4中,从状态A转移到状态B为无条件转移,其他状态之间的转移都是有条件要求的,例如,如果状态机的当前状态(现态)为B,当输入X=1时,状态机将从状态B转移到状态A;当X=0时,状态机将从状态B转移到状态C。引起状态发生改变的输入条件通常标在方向线的旁边,电路的输出结果也写在方向线的旁边,用斜线对输入和输出进行分隔,输入放在斜线左边,输出放在斜线右边。

需要强调的是,在Mealy状态图中,输出信号的表示方法容易引起读者的误解。当状态机处于所在的状态,并且在所示输入的作用下,就会产生输出,并非在状态机转移到下一状态时才会出现输出。例如,图9.4中,当状态机处于状态C时,输出Y只依赖于当前状态C和输入X,若X=1,则Y=0;若X=0,则Y=1。可见,输出信号Y是在状态转移之前产生的,与次态无关。

image-20211112153334489

图9.4 Mealy状态图

由于Moore状态机的输出只依赖于状态机的当前状态,它的状态图与Mealy状态图略有不同,通常将输出写在圆圈的内部,即将输出和状态标注在一起,二者以斜线隔开,所以Moore状态图的圆圈内的内容为“状态/输出”。图9.5是Moore状态图的一个例子。若当前状态为C,那么当输入X=0时,下一状态转换为状态D;当输入X=1时,下一状态转换为状态B。

image-20211109160247438

图9.5 Moore状态图

状态机的设计步骤

一般来说,状态机的设计步骤如下所示:

  1. 根据具体的设计原则,确定采用Moore状态机还是Mealy状态机。

  2. 分析设计要求,列出状态机的所有状态,并对每一个状态进行状态编码。

  3. 根据状态转移关系和输出函数,画出状态图。

  4. 根据所画的状态图,采用硬件描述语言对状态机进行描述。

在上面的设计步骤中,第3步是最困难也是最有创造性的一步。对同一个设计问题来说,不同的人可能会构造出不同的状态图。状态图直观地反映了状态机各个状态之间的转换关系以及转换条件,因而有利于理解状态机的工作机理,但此时要求设计的状态个数不能太多。对于状态个数较多的状态机,一般采用状态表的方法列出状态机的转移条件。如果输出信号较多,可以采用输出逻辑真值表进行表示。

9.2.2 有限状态机的Verilog HDL描述

下面通过一个例子来介绍状态机的设计过程。

【例子】

设计一个序列检测器电路,当检测到输入信号s出现二进制序列1010(自左至右的顺序)时,电路输出z=1,否则z=0。注意要考虑序列重叠的问题,如101010相当于出现两个1010序列。

【设计过程】

  1. 根据9.1小节介绍的状态机的设计步骤,首先要确定采用Mealy状态机还是Moore状态机。因为该电路在连续收到1010序列时,输出为1,否则输出0,即输出与输入信号有关,所以我们采用Mealy状态机。

  2. 第二步是列出状态机的所有状态,并进行状态编码。我们来分析电路的状态,根据设计要求,该电路必须能记忆收到的输入数据1、连续收到前两个数据10、连续收到前三个数据101、连续收到1010后的状态,可见该电路至少应该有四个状态,我们分别用S_1、S_2、S_3、S_4表示,若电路的初始状态用S_0表示,那么该电路有五个状态。

  3. 然后我们分析电路的状态转移关系,进而画出状态图。我们从第一个状态S_0开始分析,即从初始状态开始跳转。在分析时,我们也考虑到了序列重叠的可能性,例如101010相当于出现了两次1010序列。

    ① 开始时,电路处于S_0状态,此时会有两种情况,一种情况是收到第一个数据s=0时,电路仍旧处于S_0状态;另一种情况是当收到第一个有效数据s=1时,电路输出z=0,并且进入S_1状态,如图9.6所示。

    image-20211117140813119

    图9.6 序列检测器状态跳转1

    ② 然后我们分析S_1状态的跳转情况,在S_1状态下同样有两种情况,一种情况是电路收到第二个有效数据s=0,即电路连续收到序列10,则电路进入S_2状态,输出z=0;另一种情况是电路收到第二个数据s=1时,即电路连续收到序列11,考虑序列重叠的情况,电路仍旧处于S_1状态,如图9.7所示。

    image-20211117140910031

    图9.7 序列检测器状态跳转2

    ③ 接着我们分析S_2状态的跳转情况,在S_2状态下同样有两种情况,如果第三个数据s=0,即电路连续接收到序列100,那么电路的输出z=0,并且电路应该返回到初始状态S_0,重新开始检测;而如果第三个数据s=1,即电路连续接收到序列101,则电路转换到S_3状态,z=0,如图9.8所示。

    image-20211117140940273

    图9.8 序列检测器状态跳转3

    ④ 下面我们该分析S_3状态的跳转情况了,在S_3状态下同样有两种情况,若s=0,那么电路已经连续收到四个有效数据1010,电路应输出z=1,此时若时钟信号的有效沿到来,则电路应转向S_4状态;而若s=1,即电路连续接收到序列1011,考虑序列重叠的情况,相当于电路连续接收到序列1,所以电路输出z=0,并且电路应该转入S_1状态,如图9.9所示。

    image-20211117141008725

    图9.9 序列检测器状态跳转4

    ⑤ 最后我们看S_4状态的跳转,若s=0,电路连续收到序列10100,相当于电路检测完成一次序列1010后,又检测到0信号输入,即未检测到序列所需的有效数据,因此返回初始状态S_0,重新检测,并且z=0;而当s=1时,电路连续收到序列10101,考虑序列重叠的情况,相当于电路连续接收到序列101,所以电路应进入S_3状态,并且z=0,此时的状态跳转如图9.10所示。

    image-20211117141040989

    图9.10 序列检测器状态跳转5

    ⑥ 至此,我们已经完成了序列检测器的状态跳转分析,图9.10为序列检测器的状态图。通过该图可以看出,当状态机处于S_2、S_4时,如果输入s=0,电路会转移到相同的次态S_0,并且输出z都为0;如果s=1,电路会转移到相同的次态S_3,输出z都为0。所以S_2、S_4为等价的状态,可以用S_2状态代替S_4,于是我们可以得到序列检测器的简化状态图,如图9.11所示。

    实际上,可以直接用图9.10进行电路设计,不过此时会多用一个触发器。如果用内部触发器资源较多的FPGA器件实现该状态机,未简化的状态图是完全可以的。

    image-20211109170636532

    图9.11 序列检测器简化状态图

  4. 状态图完成后,就可以使用硬件描述语言对状态图进行描述了。

    利用Verilog HDL语言描述状态图主要包含四部分内容:

    ① 利用参数定义语句 parameter 描述状态机中各个状态的名称,并指定状态编码。例如,对序列检测器的状态分配可以使用最简单的自然二进制码,其描述如下:

    parameter S0 = 2'b00, S1 = 2'b01, S2 = 2'b10, S3 = 2'b11;

    ② 用 always 块描述状态触发器,实现状态存储;

    ③ 使用 case 语句(也可以使用 if-else 语句)描述状态转移逻辑;

    ④ 描述状态机的输出逻辑。

常见的描述状态图的方法有三种:一段式状态机、两段式状态机、三段式状态机。接下来我们将分别使用这三种状态机来实现刚刚介绍的序列检测器。

一段式状态机

将整个状态机写到一个always模块内部,在该模块中既描述时钟控制的状态转移,又描述状态机的下一状态和输出,这种写法被称为一段式状态机。这种写法仅仅适用于非常简单的状态机设计,不符合将时序逻辑和组合逻辑分开描述的代码风格,而且在描述当前状态时还要考虑下一个状态的逻辑,整个代码的结构不清晰,不利于修改和维护,不利于时序约束条件的加入,也不利于综合器对设计的优化,所以我们不推荐这种写法。

上述检测1010序列的一段式状态机代码如下:

module detector_1(input       clk,input       rst_n,input       s,output  reg z
);// 状态声明及编码
parameter   S0  =   2'b00,S1  =   2'b01,S2  =   2'b10,S3  =   2'b11;// 变量声明
reg [1:0]   state;always@(posedge clk, negedge rst_n)
beginif(~rst_n)beginz       <=  1'b0;   // 异步复位,输出为0state   <=  S0;     // 异步复位,状态跳变为S0endelsecase(state)         // 状态跳转及输出S0:beginz       =   1'b0;state   =   (s == 1) ? S1 : S0;endS1:beginz       =   1'b0;state   =   (s == 0) ? S2 : S1;endS2:beginz       =   1'b0;state   =   (s == 1) ? S3 : S0;endS3:beginif(s == 1'b0)beginz   =   1'b1;state   =   S2;endelsebeginz   =   1'b0;state   =   S1;end     endendcase
endendmodule

代码9.1 序列检测器一段式状态机

我们写的状态机综合器是可以识别出来的,点击Quartus Prime软件菜单栏的Tools --> Netlist Viewers --> State Machine Viewer,就会弹出如图9.12所示的状态图。另外,当我们打开RTL Viewer的时候看到的如图9.13所示的黄色块就是综合器自动识别出的状态机,双击进去也可以查看状态图,打开后显示的状态图与图9.12所示的状态图是一致的。

image-20210705102925520

图9.12 代码9.1的State Machine Viewer

image-20210705102822833

图9.13 代码9.1的RTL Viewer

我们再来看这段代码(代码9.1),严格地说,对序列检测器电路用单个always块进行描述存在着一个隐含的错误,即输出信号z的描述存在错误。本来信号z是由状态机的当前状态和输入信号共同决定的(该序列检测器是Mealy状态机),它是一个组合逻辑(具体可见图9.2),如果状态机的当前状态不变,而输入信号变了,信号z应该立即变化,但是按照上面的描述(代码9.1),信号z只有等到时钟上升沿到来时才会变化。在实际应用中,为了消除组合逻辑输出信号中的毛刺,在时序允许的情况下,通常允许Mealy状态机中输出信号通过寄存器输出。

总之,一段式状态机的写法仅仅适用于Moore状态机,它的电路结构可以用图9.14所示框图进行概述。

image-20211109180717503

图9.14 单个always块描述的FSM的结构图

两段式状态机

所谓两段式状态机就是采用两个always模块来实现状态机的功能。其中一个always模块采用同步时序逻辑描述状态转移,而另一个always模块采用组合逻辑来判断状态转移条件,描述状态转移规律和电路的输出。两段式状态机是值得推荐的写法之一,它的电路结构可以用图9.15所示的框图进行概括,每个方框用一个always块描述。

image-20211110091208043

图9.15 两个always块描述的FSM的结构图

上述检测1010序列的两段式状态机代码如下:

module detector_2(input       clk,input       rst_n,input       s,output  reg z
);// 状态声明及编码
parameter   S0  =   2'b00,S1  =   2'b01,S2  =   2'b10,S3  =   2'b11;// 变量声明
reg [1:0]   cs, ns;// 时序逻辑,描述状态转移
always@(posedge clk, negedge rst_n)
beginif(~rst_n)cs  <=  S0;elsecs  <=  ns;
end// 组合逻辑,描述下一状态和输出
always@(cs, s)
beginns  =   2'bxx;z   =   1'b0;case(cs)S0:beginz   =   1'b0;ns  =   (s == 1) ? S1 : S0;endS1:beginz   =   1'b0;ns  =   (s == 0) ? S2 : S1;endS2:beginz   =   1'b0;ns  =   (s == 1) ? S3 : S0;endS3:beginif(s == 1'b0)beginz   =   1'b1;ns  =   S2;endelsebeginz   =   1'b0;ns  =   S1;endendendcase
endendmodule

代码9.2 序列检测器两段式状态机

上述写法通过两个并行执行的always结构描述电路的功能,通过公共变量进行相互通信。第一个时序型always块采用非阻塞赋值,使用边沿触发事件描述了状态机的触发器部分;第二个组合逻辑性always块采用阻塞赋值,使用电平敏感事件描述了状态机下一个状态逻辑和输出逻辑部分。

第一个always块说明了异步复位到初始状态S_0和同步时钟完成的操作,语句 cs <= ns 仅在时钟的上升沿被执行,这意味着第二个always块内部 ns 的值变化会在时钟上升沿到来时被传送给 cs。

第二个always块把现态 cs 和输入数据 s 作为敏感变量,只要其中的任何一个变量发生变化,就会执行顺序语句块内部的case语句,跟在case语句后面的各分支项说明了图9.11中状态的转换以及输出信号。

使用Quartus Prime综合后,代码9.2的状态机如图9.16所示,该状态图与代码9.1的状态图(图9.12)一致。另外,我们也可以查看其RTL Viewer,如图9.17所示,与代码9.1的RTL Viewer(图9.13)相比,其输出z未通过寄存器输出,可能会有毛刺出现。

image-20210705103741540

图9.16 代码9.2的State Machine Viewer

image-20210705103656841

图9.17 代码9.2的RTL Viewer

另外一个值得注意的是,在第二个always块敏感列表下面一行应该写出下一状态 ns 的默认赋值,然后根据当前的状态和当前的输入由后面的case或if-else语句确定正确的转移。

...
beginns = 2'bxx;z = 1'b0;case(cs)...
end

代码9.3 两段式状态机中第二个always块中ns的默认值

对下一状态ns的默认赋值有三种方式:全部设置成不定状态(x)、设置成预先规定的初始状态、设置成FSM中的某一有效状态,推荐将敏感列表后面的默认状态设置成不定状态(x),它的优点有两点,一是在仿真时可以很好地考察所涉及的FSM的完备性,若设计的FSM不完备,则会进入任意状态,仿真时容易发现;二是综合器对代码进行逻辑综合时,会忽略没有定义的状态触发器向量。

三段式状态机

使用三个always块描述状态机的功能,这种写法被称为三段式状态机,它的结构图如图9.18所示。第一个always模块采用同步时序逻辑方式描述状态转移(中间方框),第二个always模块采用组合逻辑方式描述状态转移规律(第一个方框),第三个always模块描述电路的输出信号(第三个方框),并且在时序允许的情况下,通常让输出信号经过一个寄存器再输出,保证输出信号没有毛刺。

image-20211110094413124

图9.18 三个always块描述的FSM的结构图

上述检测1010序列的三段式状态机代码如下:

module detector_3(input       clk,input       rst_n,input       s,output  reg z
);// 状态声明及编码
parameter   S0  =   2'b00,S1  =   2'b01,S2  =   2'b10,S3  =   2'b11;// 变量声明
reg [1:0]   cs, ns;// 时序逻辑,描述状态转移
always@(posedge clk, negedge rst_n)
beginif(~rst_n)cs  <=  S0;elsecs  <=  ns;
end// 组合逻辑,描述下一状态
always@(cs, s)
beginns  =   2'bxx;case(cs)S0:ns  =   (s == 1) ? S1 : S0;S1:ns  =   (s == 0) ? S2 : S1;S2:ns  =   (s == 1) ? S3 : S0;S3:ns  =   (s == 0) ? S2 : S1;endcase
end// 输出逻辑,让输出信号经过一个寄存器再送出,可以消除z信号中的毛刺
always@(posedge clk, negedge rst_n)
beginif(~rst_n)z   =   1'b0;elsebeginz   =   1'b0;case(cs)S0:z   =   1'b0;S1:z   =   1'b0;S2:z   =   1'b0;S3:z   =   (s == 1'b0) ? 1'b1 : 1'b0;endcaseend
endendmodule

代码9.4 序列检测器三段式状态机

使用Quartus Prime综合后,代码9.4的状态机如图9.19所示,该状态图与代码9.1、9.2的状态图(图9.12、图9.16)一致。另外,我们也可以查看其RTL Viewer,如图9.20所示,可以看出其输出z通过寄存器输出,可以克服输出逻辑出现毛刺的问题,这在一些将输出信号作为控制逻辑的场合使用,可有效避免产生错误控制动作的可能性。

image-20210705104211520

图9.19 代码9.4的State Machine Viewer

image-20210705104128002

图9.20 代码9.4的RTL Viewer

9.2.3 有限状态机的状态编码

在状态机设计中,有一个重要的问题就是状态的编码,通常用 parameter 指定状态编码。状态机编码方式很多,由此产生的电路也不相同,常用的编码方式有三种:二进制编码、格雷码和独热(One-Hot)编码。表9.1给出了对六个状态进行编码的三种不同编码方式。

表9.1 三种编码方式的对比

状态二进制编码格雷编码独热码
state0000000000001
state1001001000010
state2010011000100
state3011010001000
state4100110010000
state5101111100000

二进制编码采用顺序的二进制数编码的每个状态。一个有 N 种状态的状态机至少需要 log_2{N} 个触发器来存储状态编码。例如,有8种状态的机器将至少需要3个触发器,这种方式使用的触发器最少。二进制编码的缺点是从一个状态转换到相邻状态时,有可能有多个比特位同时发生变化,如表9.1中,从state3转换到state4(从011变为100)时,三个比特位都需要同时发生变化,瞬变次数多,容易产生毛刺,引发逻辑错误。

格雷码可用相同的位数来实现编码,其特点是两个相邻码值之前仅有1位不同,用它来编码状态可减少瞬变的次数,也减少了产生毛刺和一些暂态的可能。

独热(One-Hot)编码使用n位状态触发器表示具有n个状态的状态机,每个状态与一个独立的触发器相对应,并且在任何时刻其中只有一个触发器有效(其值为1)。独热编码是一种流行的编码方式,虽然这种编码方案会使用较多的触发器,但这使得独热码状态机中的译码逻辑使用较少的门,因为它只是对寄存器中的一位进行译码,而不是一个矢量,所以独热码状态机可以有更快的速度,并且由于增加触发器而占用的面积可用简单的译码电路省下来的面积抵消。另外,修改独热码设计也非常容易,因为增加或去除一个状态不会影响到其余状态的编码和电路的性能。因此,推荐使用独热编码方式。

在设计过程中,可通过综合器指定编码方式,如在Quartus Prime软件中,选择菜单Assignments --> Settings,在Settings页面的Category栏中选Compiler Settings选项,单击Advanced Settings(Synthesis)...按钮,在出现的对话框的State Machine Processing栏中选择需要的编码方式,可选的编码方式有Auto、Gray、Johnson、Minimal Bits、One-Hot、Sequential、User-Encoded等几种,如图9.21所示,可以根据需要选择合适的编码方式。

image-20210705002836397

图9.21 在Quartus Prime中选择编码方式

9.3 实验目标

  • 使用Verilog HDL设计有限状态机,作为实验八实现的计算器的控制器;

9.4 设计实现

9.4.1 设计思路
系统框图

回顾实验8的系统框图:

image-20240527164140662

图9.23 实验8计算器系统框图

图9.23中只画出了计算器的数据通路部分,而计算器的控制器是使用有限状态机来实现,图9.24是使用有限状态机作为控制器的计算器的系统框图。该计算器的输入和输出端口如下所述:

  • SW3 ~ SW0:计算器的数据输入;

  • *SW16:选择进行加法运算还是减法运算;

  • KEY2:用于使能数字钟,按下按键KEY2,数字钟停止计时,释放KEY2后,数字钟继续计时;

  • FPGA_CLK1_50:50MHz时钟,为寄存器提供时钟信号;

  • KEY0:系统复位信号;

  • KEY1:寄存器使能信号;

  • SW9 ~ SW7:功能选择信号;

  • LEDR7 ~ LEDR0:显示计算器的计算结果,以二进制形式显示在红色LED上;

  • HEX5 HEX0:显示计算器数字钟计时结果,HEX1 ~ HEX0显示计时结果的厘秒,HEX3 ~ HEX2显示计时结果的秒,HEX5 ~ HEX4显示计时结果的分钟。

    image-20240528100611161

图9.24 计算器系统框图

该计算器的功能如下:

  1. 拨动滑动开关SW9 ~ SW7,选择要实现的运算;

    ① *SW9 ~ SW7位置为"down、down、down",计算器实现与运算;

    SW9 ~ SW7位置为"down、down、up",计算器实现或运算;

    SW9 ~ SW7位置为"down、up、down",计算器实现异或运算;

    SW9 ~ SW7位置为"down、up、up",计算器实现非(位取反)运算;

    SW9 ~ SW7位置为"up、down、down",计算器实现加/减运算;

    SW9 ~ SW7位置为"up、down、up",计算器实现乘法运算;

    SW9 ~ SW7位置为"up、up、down",计算器实现除2运算;

  2. 拨动滑动开关SW3 ~ SW0,输入第一个操作数;

  3. 按一次KEY1并释放;

  4. 对于 F = ~ A(位取反) 和 F = A / 2(除2) 运算,运算结果显示到LEDR7 ~ LEDR0

  5. 对于其他运算,拨动滑动开关SW3 ~ SW0,输入第二个操作数;

  6. 按一次KEY1并释放,其他运算结果显示到LEDR7 ~ LEDR0上;

  7. 运算结果显示到LEDR7 ~ LEDR0上后,重新回到步骤1等待下一次运算;

  8. 数字钟实现计数功能,并将计数结果显示到HEX5 ~ HEX0上,按下KEY2不松开,数字钟停止计时;释放KEY2后,数字钟从它上次停止的时刻继续计时。

计算器控制器(状态机)的实现

计算器的数据通路我们已在前面的实验中实现,在这个实验中,我们重点来看怎样用有限状态机来实现计算器的控制器。从图9.24可以看出,该控制器的输入信号有时钟clk、复位rst_n、寄存器使能en和功能选择sel[2:0],输出信号有输入寄存器的使能信号en1、结果寄存器的使能信号en2和计算器所进行的运算选择信号ope[2:0]。根据计算器的功能,我们来分析控制器(状态机)的功能:

  • 在初始状态S_0下,计算器不进行运算,状态机的输出信号en1=0、en2=0;

  • 当输入信号en=1(按键KEY1被按下),状态机转向下一状态S_1,此时状态机的输出信号en1=1、en2=0;该状态会一直保持,直到输入信号en=0(按键KEY1被释放)后,会跳转到下一状态S_2;

  • 在状态S_2下,根据输入信号sel[2:0],跳转到下一状态(S_3 ~ S_9);

  • 对于 F = ~ AF = A / 2 运算(仅需一个操作数),结果显示到LEDR7 ~ LEDR0上,故结果寄存器的使能信号en2=1,并且状态机跳转回初始状态S_0;

  • 对于其他运算,需要等待第二个操作数的输入;

  • 当输入信号en第二次等于1(按键KEY1第二次被按下)时,状态机跳转到下一状态S_{10},在该状态下,结果寄存器的使能信号en2=1,并且状态机跳转回初始状态S_0。

然后我们来进行状态机的设计,根据9.2.1小节介绍的状态机的设计步骤,该状态机的设计过程如下:

  1. 首先要确定采用Mealy状态机还是Moore状态机。从上述状态机的功能可以知道,输入信号en与两个输出信号en1、en2密切相关,故我们采用Mealy状态机;

  2. 第二步是列出状态机的所有状态,并进行状态编码。根据上述状态机的功能分析可知,该状态机共有十一个状态,我们分别使用 S_0 ~ S_{10}来表示。并且,由于我们使用的DE-Cloud开发板的FPGA资源丰富,我们采用独热编码方式,其Verilog HDL描述如下:

    // 状态声明及编码,使能独热码
    parameter   S0  =   11'b000_0000_0001,S1  =   11'b000_0000_0010,S2  =   11'b000_0000_0100,S3  =   11'b000_0000_1000,S4  =   11'b000_0001_0000,S5  =   11'b000_0010_0000,S6  =   11'b000_0100_0000,S7  =   11'b000_1000_0000,S8  =   11'b001_0000_0000,S9  =   11'b010_0000_0000,S10 =   11'b100_0000_0000;
  3. 第三步是分析电路的状态转移关系,进而画出状态图。

    ① 开始时,电路处于初始状态S_0,此时会有两种情况,一种情况是输入信号en=0(按键KEY1未被按下)时,电路仍旧处于S_0状态,输出信号en1=0、en2=0;另一种情况是输入信号en=1(按键KEY1被按下)时,电路进入状态S_1;

    ② 然后我们分析S_1状态的跳转情况,在S_1状态下同样有两种情况,一种情况是输入信号en=1(即按键KEY1被按下后未释放),电路仍处于S_1状态,输出信号en1=1、en2=0;另外一种情况是输入信号en=0(即按键KEY1被按下后释放),电路进入状态S_2;

    ③ 在S_2状态下,根据输入信号sel[2:0](功能选择信号),跳转到不同的状态(S_3 ~ S_9);

    ④ 在S_6和S_9状态下,输出信号en1=0、en2=1,并且会跳转回初始状态S_0,注意这次状态转移为无条件跳转;

    ⑤ 在S_3、S_4、S_5、S_7、S_8状态下,会有两种情况,一种情况是输入信号en=0(按键KEY1未被按下)时,电路仍旧处于当前的状态;另一种情况是输入信号en=1(按键KEY1第二次被按下)时,电路进入状态S_{10};

    ⑥ 在S_{10}状态下,输出信号en1=0、en2=1,并且会跳转回初始状态S_0,注意这次状态转移也是无条件跳转。

    根据上述状态转移关系,我们可以画出该状态机的状态图,如图9.25所示。

    image-20211117102133518

    图9.25 计算器的状态机的状态图

  4. 状态图完成后,就可以使用Verilog HDL进行描述了。为了避免毛刺的出现,我们采用三段式状态机。

    ① 首先是状态机端口的声明,从图9.24可以看出,该控制器的输入信号有时钟clk、复位rst_n、寄存器使能en和功能选择sel[2:0],输出信号有输入寄存器的使能信号en1、结果寄存器的使能信号en2。

    module fsmctrl(input               clk,        // 时钟信号input               rst_n,      // 复位信号,低电平有效input               en,         // 寄存器使能信号input       [2:0]   sel,        // 功能选择信号,选择计算器进行的运算output  reg         en1,        // 输入寄存器的使能信号output  reg         en2,        // 结果寄存器的使能信号output  reg [2:0]   ope         // 计算器进行的运算选择信号
    );

    代码9.5 状态机端口声明

    ② 然后是状态编码,我们使用独热编码方式;还有就是一些变量的声明,现态cs[10:0]、次态ns[10:0]。

    // 状态声明及编码,使能独热码
    parameter   S0  =   11'b000_0000_0001,S1  =   11'b000_0000_0010,S2  =   11'b000_0000_0100,S3  =   11'b000_0000_1000,S4  =   11'b000_0001_0000,S5  =   11'b000_0010_0000,S6  =   11'b000_0100_0000,S7  =   11'b000_1000_0000,S8  =   11'b001_0000_0000,S9  =   11'b010_0000_0000,S10 =   11'b100_0000_0000;// 变量声明
    reg [10:0] cs;
    reg [10:0] ns;

    代码9.6 状态声明及变量声明

    ③ 然后就是三段式状态机的主体实现部分了,第一个always模块采用同步时序逻辑方式描述状态转移,在时钟信号的作用下,ns 的值会在时钟上升沿到来时被传送给 cs。

    // 第一段状态机,描述状态转换
    always@(posedge clk, negedge rst_n)
    beginif(~rst_n)cs  <= S0;elsecs  <= ns;
    end

    代码9.7 三段式状态机第一个always块

    ④ 第二个always模块采用组合逻辑方式描述状态转移,

    // 第二段状态机,组合逻辑,描述下一状态
    always@(cs, en, sel)
    begincase(cs)S0:beginif(en == 1'b1)      // 如果en=1,跳转到S1ns = S1;        // 否则仍处于S0elsens = S0;endS1:beginif(en == 1'b0)      // 如果en=0,跳转到S2ns = S2;        // 否则仍处于S1elsens = S1;endS2:begincase(sel)                   // 根据sel的值,跳转到不同的状态S3~S9,进行不同的运算3'b000: ns = S3;    // 与运算3'b001: ns = S4;    // 或运算3'b010: ns = S5;    // 异或运算3'b011: ns = S6;    // 位取反运算3'b100: ns = S7;    // 加/减法运算3'b101: ns = S8;    // 乘法运算3'b110: ns = S9;    // 除2运算default: ns = S3;endcaseendS3:beginif(en == 1'b1)      // 与运算,如果en第二次为1,跳转到S10ns = S10;       // 否则仍处于S3elsens = S3;endS4:beginif(en == 1'b1)      // 或运算,如果en第二次为1,跳转到S10ns = S10;       // 否则仍处于S4elsens = S4;endS5:beginif(en == 1'b1)      // 异或运算,如果en第二次为1,跳转到S10ns = S10;       // 否则仍处于S5elsens = S5;endS6:beginns = S0;            // 位取反运算,计算完成,返回S0endS7:beginif(en == 1'b1)      // 加/减法运算,如果en第二次为1,跳转到S10ns = S10;       // 否则仍处于S7elsens = S7;endS8:beginif(en == 1'b1)      // 乘法运算,如果en第二次为1,跳转到S10ns = S10;       // 否则仍处于S8elsens = S8;endS9:beginns = S0;            // 除2运算,计算完成,返回S0endS10:beginns = S0;            // 计算完成,返回S0enddefault: ns = S0;endcase
    end

    代码9.7 三段式状态机第二个always块

    ⑤ 第三个always模块描述电路的输出信号,这里我们使用了寄存器输出的方式。

    // 第三段状态机,时序逻辑,描述输出
    always@(posedge clk, negedge rst_n)
    beginif(~rst_n)beginen1 <= 1'b0;en2 <= 1'b0;ope <= 3'b000;endelsebegincase(ns)S0, S2:beginen1 <= 1'b0;        // S0、S2状态,两个寄存器都不使能en2 <= 1'b0;endS1:beginen1 <= 1'b1;        // 使能输入寄存器en2 <= 1'b0;endS3:beginope <= 3'b000;      // 与运算选择信号endS4:beginope <= 3'b001;      // 或运算选择信号endS5:beginope <= 3'b010;      // 异或运算选择信号endS6:beginen1 <= 1'b0;en2 <= 1'b1;        // 使能结果寄存器ope <= 3'b011;      // 位取反运算选择信号endS7:beginope <= 3'b100;      // 加/减法运算选择信号endS8:beginope <= 3'b101;      // 乘法运算选择信号endS9:beginen1 <= 1'b0;en2 <= 1'b1;        // 使能结果寄存器ope <= 3'b110;      // 除2运算选择信号endS10:beginen1 <= 1'b0;en2 <= 1'b1;        // 使能结果寄存器enddefault:beginen1 <= 1'b0;en2 <= 1'b0;ope <= 3'b000;endendcaseend
    end

    代码9.7 三段式状态机第三个always块

9.4.2 代码实现
计算器控制器(状态机)fsmctrl.v
module fsmctrl(input               clk,        // 时钟信号input               rst_n,      // 复位信号,低电平有效input               en,         // 寄存器使能信号input       [2:0]   sel,        // 功能选择信号,选择计算器进行的运算output  reg         en1,        // 输入寄存器的使能信号output  reg         en2,        // 结果寄存器的使能信号output  reg [2:0]   ope         // 计算器进行的运算选择信号
);// 状态声明及编码,使能独热码
parameter   S0  =   11'b000_0000_0001,S1  =   11'b000_0000_0010,S2  =   11'b000_0000_0100,S3  =   11'b000_0000_1000,S4  =   11'b000_0001_0000,S5  =   11'b000_0010_0000,S6  =   11'b000_0100_0000,S7  =   11'b000_1000_0000,S8  =   11'b001_0000_0000,S9  =   11'b010_0000_0000,S10 =   11'b100_0000_0000;// 变量声明
reg [10:0] cs;
reg [10:0] ns;// 第一段状态机,描述状态转换
always@(posedge clk, negedge rst_n)
beginif(~rst_n)cs  <= S0;elsecs  <= ns;
end// 第二段状态机,组合逻辑,描述下一状态
always@(cs, en, sel)
begincase(cs)S0:beginif(en == 1'b1)      // 如果en=1,跳转到S1ns = S1;        // 否则仍处于S0elsens = S0;endS1:beginif(en == 1'b0)      // 如果en=0,跳转到S2ns = S2;        // 否则仍处于S1elsens = S1;endS2:begincase(sel)                   // 根据sel的值,跳转到不同的状态S3~S9,进行不同的运算3'b000: ns = S3;    // 与运算3'b001: ns = S4;    // 或运算3'b010: ns = S5;    // 异或运算3'b011: ns = S6;    // 位取反运算3'b100: ns = S7;    // 加/减法运算3'b101: ns = S8;    // 乘法运算3'b110: ns = S9;    // 除2运算default: ns = S3;endcaseendS3:beginif(en == 1'b1)      // 与运算,如果en第二次为1,跳转到S10ns = S10;       // 否则仍处于S3elsens = S3;endS4:beginif(en == 1'b1)      // 或运算,如果en第二次为1,跳转到S10ns = S10;       // 否则仍处于S4elsens = S4;endS5:beginif(en == 1'b1)      // 异或运算,如果en第二次为1,跳转到S10ns = S10;       // 否则仍处于S5elsens = S5;endS6:beginns = S0;            // 位取反运算,计算完成,返回S0endS7:beginif(en == 1'b1)      // 加/减法运算,如果en第二次为1,跳转到S10ns = S10;       // 否则仍处于S7elsens = S7;endS8:beginif(en == 1'b1)      // 乘法运算,如果en第二次为1,跳转到S10ns = S10;       // 否则仍处于S8elsens = S8;endS9:beginns = S0;            // 除2运算,计算完成,返回S0endS10:beginns = S0;            // 计算完成,返回S0enddefault: ns = S0;endcase
end// 第三段状态机,时序逻辑,描述输出
always@(posedge clk, negedge rst_n)
beginif(~rst_n)beginen1 <= 1'b0;en2 <= 1'b0;ope <= 3'b000;endelsebegincase(ns)S0, S2:beginen1 <= 1'b0;        // S0、S2状态,两个寄存器都不使能en2 <= 1'b0;endS1:beginen1 <= 1'b1;        // 使能输入寄存器en2 <= 1'b0;endS3:beginope <= 3'b000;      // 与运算选择信号endS4:beginope <= 3'b001;      // 或运算选择信号endS5:beginope <= 3'b010;      // 异或运算选择信号endS6:beginen1 <= 1'b0;en2 <= 1'b1;        // 使能结果寄存器ope <= 3'b011;      // 位取反运算选择信号endS7:beginope <= 3'b100;      // 加/减法运算选择信号endS8:beginope <= 3'b101;      // 乘法运算选择信号endS9:beginen1 <= 1'b0;en2 <= 1'b1;        // 使能结果寄存器ope <= 3'b110;      // 除2运算选择信号endS10:beginen1 <= 1'b0;en2 <= 1'b1;        // 使能结果寄存器enddefault:beginen1 <= 1'b0;en2 <= 1'b0;ope <= 3'b000;endendcaseend
endendmodule

代码9.8 fsmctrl.v

用Quartus Prime软件编译fsmctrl.v后,查看其State Machine Viewer如图9.26所示,这与我们画的状态图(图9.25)一致。

image-20211111090823285

图9.26 fsmctrl.v的State Machine Viewer

计算器模块calculator.v
module calculator(input           clk,            // 时钟信号input           rst_n,          // 复位信号,低电平有效input   [3:0]   a,              // 操作数,实验九修改为只有一个数据输入input           carry_in,       // 加/减法的进位input   [2:0]   sel,            // 功能选择,选择计算器进行的运算input           en,             // 计算器中寄存器的使能控制信号input           dig_clk_en,     // 计算器数字钟功能的使能控制信号// 0: 数字钟停止计时 1: 数字钟继续计时output	[ 7: 0] ledr_out,       // 显示计算器的计算结果,以二进制显示output	[ 6: 0] hex0_out,       // hex5_out ~ hex0_out显示数字钟计时结果output	[ 6: 0]	hex1_out,       // hex1_out ~ hex0_out显示计时结果的厘秒output	[ 6: 0]	hex2_out,       // hex3_out ~ hex2_out显示计时结果的秒output	[ 6: 0]	hex3_out,       // hex5_out ~ hex4_out显示计时结果的分钟output	[ 6: 0]	hex4_out,       output	[ 6: 0]	hex5_out      // output	[ 6: 0]	hex6_out,// output	[ 6: 0]	hex7_out
);// 变量声明
// 状态机的输出信号
wire            reg_en1;
wire            reg_en2;
wire    [2:0]   operation_sel;
// 两个操作数
wire    [3:0]   operator_a;
wire    [3:0]   operator_b;
// 各种运算的结果变量
wire    [3:0]   f1;
wire    [3:0]   f2;
wire    [3:0]   f3;
wire    [3:0]   f4;
wire    [5:0]   f5;     // overflow, carryout, sum[3:0]
wire    [7:0]   f6;     // multiply
wire    [3:0]   f7;     // divide by 2
wire    [3:0]   m0;
wire    [3:0]   m1;
wire    [3:0]   s0;
wire    [3:0]   s1;
wire    [3:0]   cs0;
wire    [3:0]   cs1;
wire    [7:0]   f;
wire    [7:0]   g;// 状态机做控制器
fsmctrl fsmctrl_inst(.clk    (clk),.rst_n  (rst_n),.en     (en),.sel    (sel),.en1    (reg_en1),.en2    (reg_en2),.ope    (operation_sel)
);// 输入信号赋值
assign  operator_a      =   a;
// 将操作数寄存
reg4bits reg4bits_inst(.clk    (clk),.rst_n  (rst_n),.en     (~reg_en1),.d      (a),.q      (operator_b)
);// 与运算
c1  c1_inst(.a  (operator_a),.b  (operator_b),.f  (f1)
);
// 或运算
c2  c2_inst(.a  (operator_a),.b  (operator_b),.f  (f2)
);
// 异或运算
c3  c3_inst(.a  (operator_a),.b  (operator_b),.f  (f3)
);
// 非运算
c4  c4_inst(.a  (operator_a),.f  (f4)
);
// 加(减)运算
c5 c5_inst (.a  (operator_a), .b  (operator_b), .ci (carry_in), .f  (f5)
);
// 乘法运算
c6 c6_inst (.a  (operator_a), .b  (operator_b), .f  (f6)
);
// 除以2
c7 c7_inst (.clk    (clk),.rst_n  (rst_n),.a      (operator_a),.f      (f7)
);
// 数字钟
c8 c8_inst (.clk    (clk),.rst_n  (rst_n),.en     (dig_clk_en), .m1     (m1),.m0     (m0),.s1     (s1),.s0     (s0),.cs1    (cs1),.cs0    (cs0)
);// 在f1,f2,f3,f4,f5,f6,f7七种运算结果中,选择一个运算结果f
// 若位数不足8位,则需要高位补0
mux8x1 mux8x1_inst(.r  ({4'd0, f1}),.t  ({4'd0, f2}),.u  ({4'd0, f3}),.v  ({4'd0, f4}),.w  ({2'd0, f5}),.x  (f6), .y  ({4'd0, f7}), .z  (8'd0),.s  (operation_sel),.m  (f)
);// 将选出的运算结果f存入寄存器中
reg8bits reg8bits_inst(.clk    (clk),.rst_n  (rst_n),.en     (~reg_en2),.d      (f),.q      (g)
);// 将g[7:0]以二进制的形式显示在ledr_out上
assign ledr_out = g;// 数字钟的显示
// hex1_out ~ hex0_out显示数字钟计时结果的厘秒
// hex3_out ~ hex2_out显示数字钟计时结果的秒
// hex5_out ~ hex4_out显示数字钟计时结果的分钟
decod7seg decod7seg_inst7 (.hex(m1),  .display(hex5_out));
decod7seg decod7seg_inst6 (.hex(m0),  .display(hex4_out));
decod7seg decod7seg_inst5 (.hex(s1),  .display(hex3_out));
decod7seg decod7seg_inst4 (.hex(s0),  .display(hex2_out));
decod7seg decod7seg_inst3 (.hex(cs1), .display(hex1_out));
decod7seg decod7seg_inst2 (.hex(cs0), .display(hex0_out));endmodule

代码9.9 calculator.v

calculator.v的RTL Viewer如图9.27所示,这与我们在9.4.1小节画的系统框图(图9.24)一致。

image-20240527174339973

图9.27 calculator.v的RTL Viewer

9.5 实验步骤

9.5.1 创建工程和 共代码输入
  1. 点击电脑右下角的开始菜单找到Quartus软件,双击Quartus (Quartus Prime 17.1)打开Quartus Prime软件。

  2. 点击菜单File-->New Project Wizard弹出工程创建的对话框。在弹出的对话框中点击Next。

  3. 在您的DE1-SOC 工作文件夹下创建一个lab9的文件夹,并将工程路径指向该文件夹,且工程的名称也命名calculator。如图9.28所示。

    image-20240527170825305

    图9.28-1 创建lab9 工程

    image-20240527171010482

    图9.28-2 创建lab9 工程

  4. 创建Quartus工程报告如下。

    image-20240527171138471

    图9.29 创建Quartus工程的报告

  5. 创建完Quartus工程后,左侧会打开实验步骤手册,右侧会在Quartus Prime中打开lab9工程,如图9.30所示。

    image-20240527171202234

    图9.30 Quartus中打开lab9工程

  6. 浏览并打开lab9工程路径(默认为~/Desktop/UserDisk/Labs/Digital_Logic/lab9),点击右上侧的

    Screenshot from 2021-11-11 10-36-54

    按钮,然后选择New folder按钮,在弹出的New Folder对话框中输入v并点击Create按钮,这样就完成了在lab9文件夹下新建名为v的文件夹,如图9.31所示。

    image-20240527171303890

    图9.31 在lab9文件夹下新建v文件夹

  7. 将实验八中的c1.v、c2.v、c3.v、c4.v、c5.v、c6.v、c7.v、c8.v、calculator.v、counter.v、decod7seg.v、fa.v、mux2x1.v、mux8x1.v、reg4bits.v、reg8bits.v文件拷贝至lab9的v文件夹中,如图9.32所示。

    image-20240527171603908

    图9.32 拷贝文件至v文件夹

  8. 点击Quartus Prime工具栏的Assignments --> Settings --> Files,将v文件夹中的所有文件添加至本实验的Quartus工程,如图9.34所示。

    image-20240527171813028

    图9.34 将v文件夹中的所有文件添加至Quartus工程

  9. 点击Quartus Prime工具栏的File --> New,在弹出的New窗口中选择Verilog HDL File,点击OK,新建一个空白的Verilog HDL文件,如图9.35所示。

    image-20240527172723350

    图9.35 新建空白的Verilog HDL文件

  10. 拷贝代码9.8(fsmctrl.v)至新建的Verilog HDL文件,如图9.36所示。

    image-20240527172922332

    图9.36 将代码9.8拷贝至新建的Verilog HDL文件

  11. 点击File --> Save As...,在弹出的Save As窗口中选择至lab9/v目录,在File name选项中输入fsmctrl.v,点击Save,将文件保存为fsmctrl.v,如图9.37所示。

    image-20240527173008764

    图9.37 将文件保存为fsmctrl.v

  12. 在Quartus Prime的左上角,将Project Navigator由Hierarchy切换至Files,在Files栏中找到calculator.v,双击打开该文件;修改calculator.v文件的内容为代码9.9并保存,如图9.38所示。

    image-20240527174459725

    图9.38 修改calculator.v

  13. 点击Quartus Prime工具栏的Processing --> Start --> Start Analysis & Synthesis或点击

    image-20210603145513555

    按钮对Verilog HDL代码执行语法检查和综合。如果在该过程中提示有错误,请检查Verilog HDL代码语法,确保与上述代码块完全一致。

    image-20240527174553350

    图9.40 对Verilog HDL代码进行分析和综合

9.5.2 仿真
  1. 点击Quartus Prime工具栏的File --> New --> Verilog HDL File,点击OK,新建一个空白Verilog HDL文件;

  2. 在新建的Verilog HDL文件中添加如下代码,如图9.41所示。

    `timescale 1ns/1ps
    module calculator_tb();// 产生50MHz时钟信号
    reg clk;
    localparam  PERIOD = 20;                // 50MHz,周期为20ns
    initial 
    beginclk = 1'b0;forever #(PERIOD/2) clk = ~clk;     // 每隔10ns,clk翻转一次
    end// 产生复位信号,用作6位寄存器的异步复位
    reg rst_n;
    initial 
    beginrst_n = 1'b0;#(PERIOD)   rst_n = 1'b1;
    end// 产生计算器的操作数a、运算操作选择sel和寄存器的使能信号en
    reg [3:0] a;
    reg       carry_in;
    reg [2:0] sel;
    reg       en;
    reg       dig_clk_en;
    initial
    begin#0a           = 4'd0;carry_in    = 1'b0;en          = 1'b0;dig_clk_en  = 1'b1;sel = 3'b000;   // 与运算,1010 & 0010 = 0010#(2 * PERIOD)a   = 4'b1010;en  = 1'b1;     // 第一次en=1#(PERIOD)en  = 1'b0;#(3*PERIOD)a   = 4'b0010;en  = 1'b1;     // 第二次en=1#(PERIOD)en  = 1'b0;#(5 * PERIOD)sel = 3'b001;   // 或运算,1010 | 0010 = 1010#(PERIOD)a   = 4'b1010;en  = 1'b1;     // 第一次en=1#(PERIOD)en  = 1'b0;#(3*PERIOD)a   = 4'b0010;en  = 1'b1;     // 第二次en=1#(PERIOD)en  = 1'b0;#(5 * PERIOD)sel = 3'b010;   // 异或运算,1010 ^ 0010 = 1000#(PERIOD)a   = 4'b1010;en  = 1'b1;     // 第一次en=1#(PERIOD)en  = 1'b0;#(3*PERIOD)a   = 4'b0010;en  = 1'b1;     // 第二次en=1#(PERIOD)en  = 1'b0;#(5 * PERIOD)sel = 3'b011;   // 位取反运算#(PERIOD)a   = 4'b1010;  // ~1010 = 0101en  = 1'b1;     // 第一次en=1#(PERIOD)en  = 1'b0;#(3*PERIOD)a   = 4'b0010;  // ~0010 = 1101en  = 1'b1;     // 第二次en=1#(PERIOD)en  = 1'b0;#(5 * PERIOD)sel = 3'b100;   // 加(减)运算,1010 + 0010 = 1100#(PERIOD)a   = 4'b1010;en  = 1'b1;     // 第一次en=1#(PERIOD)en  = 1'b0;#(3*PERIOD)a   = 4'b0010;en  = 1'b1;     // 第二次en=1#(PERIOD)en  = 1'b0;#(5 * PERIOD)sel = 3'b101;   // 乘法运算,1010 * 0010 = 10100#(PERIOD)a   = 4'b1010;en  = 1'b1;     // 第一次en=1#(PERIOD)en  = 1'b0;#(3*PERIOD)a   = 4'b0010;en  = 1'b1;     // 第二次en=1#(PERIOD)en  = 1'b0;#(5 * PERIOD)  sel = 3'b110;   // 除2运算#(PERIOD)a   = 4'b1010;  // 1010 / 2 = 0101en  = 1'b1;     // 第一次en=1#(PERIOD)en  = 1'b0;#(3*PERIOD)a   = 4'b0010;  // 0010 /2 = 0001en  = 1'b1;     // 第二次en=1#(PERIOD)en  = 1'b0;#(8 * PERIOD)  $stop();
    end// 例化 calculator
    wire    [7:0]   ledr_out;
    wire    [6:0]   hex0_out;
    wire    [6:0]   hex1_out;
    wire    [6:0]   hex2_out;
    wire    [6:0]   hex3_out;
    wire    [6:0]   hex4_out;
    wire    [6:0]   hex5_out;
    //wire    [6:0]   hex6_out;
    //wire    [6:0]   hex7_out;
    calculator calculator_inst(.clk        (clk),.rst_n      (rst_n),.a          (a),.carry_in   (carry_in),.sel        (sel),.en         (en),.dig_clk_en (dig_clk_en),.ledr_out   (ledr_out),.hex0_out   (hex0_out),.hex1_out   (hex1_out),.hex2_out   (hex2_out),.hex3_out   (hex3_out),.hex4_out   (hex4_out),.hex5_out   (hex5_out)// .hex6_out   (hex6_out),// .hex7_out   (hex7_out)
    );endmodule

    代码9.12 calculator_tb.v

    image-20240527175108808

    图9.41 在新建的Verilog HDL文件中添加代码9.12

  3. 点击File --> Save As...,在弹出的Save As窗口中选择至lab9/v目录,在File name选项中输入calculator_tb.v,点击Save,将文件保存为calculator_tb.v,如图9.42所示。

    image-20240527175151859

    图9.42 将文件保存为calculator_tb.v

  4. 点击Quartus Prime工具栏的Assignments --> Settings,在弹出的Settings窗口中,在左侧选中Simulation栏,在右侧的Simulation页面下,Tool name选择ModelSim-Altera,如图9.43所示,Quartus Prime将会调用ModelSim进行仿真。

    image-20240527175740875

    图9.43 设置仿真工具

  5. 依旧是在Simulation页面下,依次执行下面的步骤设置TestBench为calculator_tb:

    ① 选中Compile test bench,然后点击右侧的Test Benches...按钮,如图9.44所示;

    image-20240527175402641

    图9.44 设置TestBench(步骤1)

    ② 在弹出的Test Benches窗口中点击New...

    image-20240527175330754

    图9.45 设置TestBench(步骤2)

    ③ 在弹出的New Test Bench Settings窗口中,在Test bench name栏输入calculator_tb

    ④ 点击File name右侧的

    图标;

    image-20240528101123661

    图9.46 设置TestBench(步骤3、4)

    ⑤ 在弹出的Select File窗口中选择lab9/v目录下的calculator_tb.v

    ⑥ 点击Open

    image-20240528101209082

    图9.47 设置TestBench(步骤5、6)

    ⑦ 然后在File name栏会出现选中的v/calculator_tb.v

    ⑧ 点击File name右侧的Add按钮;

    image-20240528101239845

    图9.48 设置TestBench(步骤7、8)

    ⑨ File name栏中的v/calculator_tb.v会出现在下面的框格中;

    ⑩ 点击OK,退出New Test Bench Settings窗口;

    image-20240528101308172

    图9.49 设置TestBench(步骤9、10)

    ⑪ 返回Test Benches窗口,在Existing test bench settings框格中会出现前面设置的calculator_tb

    ⑫ 点击OK,退出Test Benches窗口;

    image-20240528101344064

    图9.50 设置TestBench(步骤11、12)

    ⑬ 返回Simulation页面,可以看到Compile test bench栏成功添加了calculator_tb仿真文件;

    ⑭ 点击OK完成设置。

    image-20240528101427534

    图9.51 设置TestBench(步骤13、14)

  6. 点击Quartus Prime工具栏的Tools --> Run Simulation Tool --> RTL Simulation,Quartus会启动ModelSim进行仿真,仿真完成后,停在$stop()任务处,如图9.52所示。

    image-20240528102259330

    图9.52 仿真完成后停在$stop()任务处

  7. 点击Wave切换至仿真波形窗口,如图9.53所示,再点击Wave窗口右上角如图9.54所示的Zoom/Unzoom按钮将将窗口最大化,方便观察信号。

    图9.53 切换至仿真波形窗口

    image-20240528102649427

    图9.54 Zoom/Unzoom按钮

  8. 在Wave窗口中,点击如图9.55所示的Zoom Full按钮显示完整的波形。

    54

    图9.55 Zoom Full按钮

  9. 完整的仿真波形如图9.56所示。

    image-20240529162224684

    56_已标记

    图9.56 完整的仿真波形

下面对仿真波形进行分析:

本次仿真的重点是仿真计算器控制器的功能,因此将两个操作数固定为 (1010)_B 和 (0010)_B,即a的值第一次为(1010)_B,第二次为(0010)_B。

⓪ 20ns处,复位信号rst_n由0变为1,复位完成,计算器正常工作;

① 40ns ~ 240ns,sel=(000)_B,做与运算,(1010)_B \ \& \ (0010)_B = (0010)_B;在连续两次en=1后,ledr\_out = (0000\_0010)_B;

② 240ns ~ 460ns,sel=(001)_B,做与运算,(1010)_B \ | \ (0010)_B = (1010)_B;在连续两次en=1后,ledr\_out = (0000\_1010)_B;

③ 460ns ~ 680ns,sel=(010)_B,做异或运算,(1010)_B \ \wedge \ (0010)_B = (1000)_B;在连续两次en=1后,ledr\_out = (0000\_1000)_B;

④ 680ns ~ 900ns,sel=(011)_B,做非运算,\sim(1010)_B = (0101)_B,\sim(0010)_B = (1101)_B;由于非运算只需要一个操作数,在第一次en=1后,ledr\_out = (0000\_0101)_B;在第二次en=1后,ledr\_out = (0000\_1101)_B;

⑤ 900ns ~ 1120ns,sel=(100)_B,carry\_in \ = \ (0)_B,做加法运算,(1010)_B \ + \ (0010)_B \ + \ (0)_B = (1100)_B;连续两次en=1后,ledr\_out = (0000\_1100)_B;

⑥ 1120ns ~ 1340ns,sel=(101)_B,做乘法运算,(1010)_B \ * \ (0010)_B \ = (1\_0100)_B;连续两次en=1后,$ledr_out = (0001_0100)_B

⑦ 1340ns ~ 仿真结束,sel=(110)_B,做除2运算,(1010)_B \ / \ 2 = (0101)_B,(0010)_B \ / \ 2 = (0001)_B;由于除2运算只需要一个操作数,在第一次en=1后,ledr\_out = (0000\_0101)_B;在第二次en=1后,ledr\_out = (0000\_0001)_B;

9.5.3 引脚分配、全编译与烧录
  1. 点击Quartus菜单Assignments——Pin Planner进行引脚分配。

    img

    图4.41

    关于引脚分配信息可以查看DE1-SoC_v.5.1.3_HWrevF.revG_SystemCD\UserManual\DE1-SoC_User_manual.pdf第 22、25、26、28页或者E:\CD_Package\01-DE1-SoC\DE1-SoC_v.5.1.3_HWrevF.revG_SystemCD\Schematic\DE1-SoC.pdf的第3页

    img

    image-20240517165745573

    img

    image-20240527141737677

    image-20240521091242725

    image-20240524175232636

    image-20240524175307929

image-20240527142911770

image-20240527143003702

image-20240527143039056

  1. 其实实验9的引脚信息跟实验8是一样的,没有改动。所以为了节省时间,我们可以打开lab8\calculator.qsf文件,拷贝如下内容到lab8\calculator.qsf文件的最后面:

set_location_assignment PIN_AF14 -to clk
set_location_assignment PIN_AF10 -to a[3]
set_location_assignment PIN_AF9 -to a[2]
set_location_assignment PIN_AC12 -to a[1]
set_location_assignment PIN_AE11 -to carry_in
set_location_assignment PIN_AB12 -to a[0]
set_location_assignment PIN_AA15 -to en
set_location_assignment PIN_AA14 -to rst_n
set_location_assignment PIN_AE12 -to sel[2]
set_location_assignment PIN_AD10 -to sel[1]
set_location_assignment PIN_AC9 -to sel[0]
set_location_assignment PIN_W20 -to ledr_out[7]
set_location_assignment PIN_Y19 -to ledr_out[6]
set_location_assignment PIN_W19 -to ledr_out[5]
set_location_assignment PIN_W17 -to ledr_out[4]
set_location_assignment PIN_V18 -to ledr_out[3]
set_location_assignment PIN_V17 -to ledr_out[2]
set_location_assignment PIN_W16 -to ledr_out[1]
set_location_assignment PIN_V16 -to ledr_out[0]
set_location_assignment PIN_W15 -to dig_clk_en
set_location_assignment PIN_AH28 -to hex0_out[6]
set_location_assignment PIN_AG28 -to hex0_out[5]
set_location_assignment PIN_AF28 -to hex0_out[4]
set_location_assignment PIN_AG27 -to hex0_out[3]
set_location_assignment PIN_AE28 -to hex0_out[2]
set_location_assignment PIN_AE27 -to hex0_out[1]
set_location_assignment PIN_AE26 -to hex0_out[0]
set_location_assignment PIN_AD27 -to hex1_out[6]
set_location_assignment PIN_AF30 -to hex1_out[5]
set_location_assignment PIN_AF29 -to hex1_out[4]
set_location_assignment PIN_AG30 -to hex1_out[3]
set_location_assignment PIN_AH30 -to hex1_out[2]
set_location_assignment PIN_AH29 -to hex1_out[1]
set_location_assignment PIN_AJ29 -to hex1_out[0]
set_location_assignment PIN_AC30 -to hex2_out[6]
set_location_assignment PIN_AC29 -to hex2_out[5]
set_location_assignment PIN_AD30 -to hex2_out[4]
set_location_assignment PIN_AC28 -to hex2_out[3]
set_location_assignment PIN_AD29 -to hex2_out[2]
set_location_assignment PIN_AE29 -to hex2_out[1]
set_location_assignment PIN_AB23 -to hex2_out[0]
set_location_assignment PIN_AB22 -to hex3_out[6]
set_location_assignment PIN_AB25 -to hex3_out[5]
set_location_assignment PIN_AB28 -to hex3_out[4]
set_location_assignment PIN_AC25 -to hex3_out[3]
set_location_assignment PIN_AD25 -to hex3_out[2]
set_location_assignment PIN_AC27 -to hex3_out[1]
set_location_assignment PIN_AD26 -to hex3_out[0]
set_location_assignment PIN_W25 -to hex4_out[6]
set_location_assignment PIN_V23 -to hex4_out[5]
set_location_assignment PIN_W24 -to hex4_out[4]
set_location_assignment PIN_W22 -to hex4_out[3]
set_location_assignment PIN_Y24 -to hex4_out[2]
set_location_assignment PIN_Y23 -to hex4_out[1]
set_location_assignment PIN_AA24 -to hex4_out[0]
set_location_assignment PIN_AA25 -to hex5_out[6]
set_location_assignment PIN_AA26 -to hex5_out[5]
set_location_assignment PIN_AB26 -to hex5_out[4]
set_location_assignment PIN_AB27 -to hex5_out[3]
set_location_assignment PIN_Y27 -to hex5_out[2]
set_location_assignment PIN_AA28 -to hex5_out[1]
set_location_assignment PIN_V25 -to hex5_out[0]
set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top
  1. Quartus Prime工具栏的Processing --> Start Compilation或点击

    image-20210603145755433

    按钮编译工程,编译完成后,如图9.57所示。此外还可以看到在lab9/output_files文件夹中生成了calculator.sof文件,如图9.58所示。

image-20240528170337349

图9.57 lab9编译完成

image-20240528170417119

图9.58 lab9.sof

  1. 使用上一步生成的calculator.sof文件对FPGA进行编程。用USB Blaster线将DE1-SOC开发板和PC连接起来,给开发板上电。

  2. 点击Quartus Prime工具栏的Tools --> Programmer或点击

    image-20210603150014201

    按钮打开Programmer窗口,如图9.59所示。

image-20240528170452280

图9.59 Programmer窗口

  1. 在Programmer窗口中,点击Hardware Setup...打开Hardware Setup窗口,在Currently selected hardware的下拉框中选择"DE-SoC[USB-1]",点击Close,如图9.60所示。

image-20240528170526403

图9.60 选择USB-Blaster

  1. 点击Auto Detect按钮,在弹出的Select Device窗口中,选择5CSEMA5(DE1-SoC开发板上的FPGA器件为Cyclone V SE 5CSEMA5F31C6),并点击OK,如图9.61所示;在弹出的Quartus Prime提示窗口中,点击Yes,如图9.62所示;然后在Programmer窗口会出现SOCVHPS和FPGA两个器件,如图9.63所示。

    image-20240528170559357

    图9.61 选择5CSEBA6器件

    image-20240528170708057

    图9.62 提示窗口

    image-20240528170623757

    图9.63 Programmer窗口的SOCVHPS和FPGA两个器件

  2. 左键单击选中5CSEMA5器件,然后点击Change File按钮,如图9.64所示。

    image-20240528170756611

    图9.64 点击Change File按钮

  3. 在弹出的Select New Programming File窗口中,选择lab9/output_files目录下的calculator.sof文件,点击Open,如图9.65所示,添加calculator.sof文件。

    image-20240528170818702

    图9.65 添加lab9.sof文件

  4. 添加calculator.sof文件完成后会自动返回Programmer窗口,勾选Program/Configure,点击Start按钮,开始烧录calculator.sof文件,如图9.66所示。

    image-20240528170842043

    图9.66 开始烧录lab9.sof文件

  5. 烧录完成后,Progress进度条显示100%,如图9.67所示。

    image-20240528170908189

    图9.67 烧录lab9.sof完成

9.5.4 实验现象观察
  1. 通过切换滑动开关SW9 ~ SW7SW3 ~ SW0到 up 或 down 位置,并按下按键KEY1,观察LEDR7~LEDR0的状态来测试计算器控制器的功能。

    本实验重点验证计算器控制器的功能,因此将两个操作数固定为 (1010)_B 和 (0010)_B,即滑动开关SW3 ~ SW0只会有两种输入:"up、down、up、down"和"down、down、up、down",而我们是通过切换滑动开关SW9 ~ SW7控制计算器进行不同的运算。

    ① 先验证与运算:

    • 切换滑动开关SW9~ SW7位置为"down、down、down",SW3 ~ SW0位置为"up、down、up、down",按下按键KEY1LEDR7~LEDR0显示为全熄灭,如图9.71所示;

      image-20240603161530062

      图9.71 与运算实验现象1

    • 切换滑动开关SW3 ~ SW0位置为"down、down、up、down",按下按键KEY1LEDR7 ~ LEDR0显示为熄灭、熄灭、熄灭、熄灭、熄灭、熄灭、点亮、熄灭,如图9.72所示;

      即(1010)_B \ \& \ (0010)_B = (0010)_B = (2)_H;

      即只有连续两次按下按键KEY1LEDR7 ~ LEDR0 上才会显示与运算的结果。

      image-20240603165024092

      图9.72 与运算实验现象2

    ② 我们继续验证或运算:

    • 切换滑动开关SW9 ~ SW7位置为"down、down、up",SW3 ~ SW0位置为"up、down、up、down",按下按键KEY1LEDR7 ~ LEDR0上仍旧显示上一步与运算的运算结果,即显示为熄灭、熄灭、熄灭、熄灭、熄灭、熄灭、点亮、熄灭,仍旧显示上一步与运算的运算结果,如图9.73所示;

      image-20240603161652420

      图9.73 或运算实验现象1

    • 切换滑动开关SW3 ~ SW0位置为"down、down、up、down",按下按键KEY1LEDR7 ~ LEDR0显示发生变化,即显示为熄灭、熄灭、熄灭、熄灭、点亮、熄灭、点亮、熄灭,如图9.74所示;

      即(1010)_B \ | \ (0010)_B = (1010)_B = (A)_H;

      即只有连续两次按下按键KEY1LEDR7 ~ LEDR0 上才会显示或运算的结果,而只按下一次按键KEY1LEDR7 ~ LEDR0上依旧保持上一次与运算的运算结果。

      image-20240603161830557

      图9.74 或运算实验现象2

    ③ 我们继续验证异或运算:

    • 切换滑动开关SW9 ~ SW7位置为"down、up、down",SW3 ~ SW0位置为"up、down、up、down",按下按键KEY1LEDR7 ~ LEDR0上仍旧显示上一步或运算的运算结果,即显示为熄灭、熄灭、熄灭、熄灭、点亮、熄灭、点亮、熄灭,,仍旧显示上一步或运算的运算结果,如图9.75所示;

      image-20240603164614750

      图9.75 异或运算实验现象1

    • 切换滑动开关SW3 ~ SW0位置为"down、down、up、down",按下按键KEY1LEDR7 ~ LEDR0显示发生变化,即显示为熄灭、熄灭、熄灭、熄灭、点亮、熄灭、熄灭、熄灭,如图9.76所示;

      即(1010)_B \ \wedge \ (0010)_B = (1000)_B = (8)_H;

      即只有连续两次按下按键KEY1LEDR7 ~ LEDR0 上才会显示异或运算的结果,而只按下一次按键KEY1LEDR7 ~ LEDR0 上依旧保持上一次或运算的运算结果。

      image-20240603164459710

      图9.76 异或运算实验现象2

    ④ 我们继续验证非(位取反)运算:

    • 切换滑动开关SW9 ~ SW7位置为"down、up、up",SW3 ~ SW0位置为"up、down、up、down",按下按键KEY1LEDR7 ~ LEDR0显示发生变化,即显示为熄灭、熄灭、熄灭、熄灭、熄灭、点亮、熄灭、点亮,如图9.77所示;

      即\sim(1010)_B = (0101)_B = (5)_H;

      由于非运算只需要一个操作数,所以在第一次按下按键KEY1后,非运算的运算结果就会显示到 LEDR7 ~ LEDR0

      image-20240603164336128

      图9.77 非运算实验现象1

    • 切换滑动开关SW3 ~ SW0位置为"down、down、up、down",按下按键KEY1LEDR7 ~ LEDR0显示发生变化,即显示为熄灭、熄灭、熄灭、熄灭、点亮、点亮、熄灭、点亮,如图9.78所示;

      即\sim(0010)_B = (1101)_B = (d)_H;

      即对于非运算在第一次按下按键KEY1后,非运算的运算结果就会显示到 LEDR7 ~ LEDR0 上。

      image-20240603164214463

      图9.78 非运算实验现象2

    ⑤ 我们再来验证加/减法运算:

    • 切换滑动开关SW9 ~ SW7位置为"up、down、down",SW3 ~ SW0位置为"up、down、up、down",SW11位置为"down",按下按键KEY1LEDR7 ~ LEDR0上仍旧显示上一步非运算的运算结果,即显示为熄灭、熄灭、熄灭、熄灭、点亮、点亮、熄灭、点亮,仍旧显示上一步非运算的运算结果,即显示十六进制数字0d,如图9.79所示;

      image-20240603164045502

      图9.79 加/减法运算实验现象1

    • 切换滑动开关SW3 ~ SW0位置为"down、down、up、down",按下按键KEY1LEDR7 ~ LEDR0显示发生变化,即显示为熄灭、熄灭、熄灭、熄灭、点亮、点亮、熄灭、熄灭,即显示十六进制数字0C,如图9.80所示;

      即(1010)_B \ + \ (0010)_B \ + \ (0)_B = (1100)_B = (C)_H;

      即只有连续两次按下按键KEY1LEDR7 ~ LEDR0HEX1 ~ HEX0 上才会显示加/减运算的结果,而只按下一次按键KEY1LEDR7 ~ LEDR0 上依旧保持上一次非运算的运算结果。

      image-20240603163853226

      图9.80 加/减法运算实验现象2

    ⑥ 在整个实验过程中,数字钟一直保持计数,HEX5 ~ HEX0上显示的数据会一直发生变化,如果按下KEY2不松开,数字钟停止计时;释放KEY2后,数字钟从它上次停止的时刻继续计时。

9.6 实验小结

通过本实验,我们学习了状态机的组成、分类、表示方法、状态编码以及设计步骤,同时还结合了常见的序列检测器的例子来介绍怎样设计状态机。最后,我们将状态机用作计算器的控制器,来控制计算器的整个运算过程,而利用状态机作为系统的控制模块,在数字系统设计中也是经常使用的方法。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux(centos7)开机自启jar文件

问题 之前参考网上说的直接在/etc/rc.local文件中增加sh文件启动语句&#xff0c;但是没有效果&#xff1a; /root/dashboard/dashboard_backend/start_dashboard.sh 权限也增加了&#xff0c;还是不行&#xff1a; chmod x /etc/rc.local 排查 排查了一下&#xff1a; 查…

5分钟带你了解海外仓备货系统:它能做的,可不止备货这么简单

现在跨境电商的发展速度确实还是非常快的&#xff0c;线上购物也越来越变成更多人的主流购物模式。这种情况下&#xff0c;对外贸行业来说是好事&#xff0c;不过对跨境电商的服务环节——海外仓&#xff0c;带来的压力可不小。 首先来说&#xff0c;现在各个电商平台对商家发…

B站内核隔离技术的应用与实践之大数据混部篇

背景 随着B站大数据业务的高速发展&#xff0c;各类业务资源需求也随之快速增长。与此同时&#xff0c;大数据集群有效的资源利用率低于预期&#xff0c;究其原因主要有以下两点&#xff0c; 业务出于性能、稳定性考量会向平台申请过量的系统资源&#xff0c;导致平台不会调度更…

别慌!不知道如何处理#开头的字符串时,需要先了解一下什么是NCR

最近进行接口测试时抓包发现请求响应中有类似下面这些字符 起初试图对这些编码尝试各种decoder操作来一探其真身&#xff0c;遗憾的是均已失败告终&#xff08;后来发现&#xff0c;这些编码可以在浏览器中正常显示&#xff09;。最后得知这种奇怪的编码格式并不是编码,而是一种…

红酒:红酒保存的理想温度与湿度

对于云仓酒庄雷盛红酒&#xff0c;保存方法尤为重要。而保存红酒的关键在于控制温度与湿度这两个因素。以下是关于红酒保存的理想温度与湿度的详细解释。 首先&#xff0c;谈到雷盛红酒的保存温度&#xff0c;关键的是要避免不好温度。高温会加速化学反应&#xff0c;使红酒更快…

【二叉树】Leetcode 530. 二叉搜索树的最小绝对差【简单】

二叉搜索树的最小绝对差 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中任意两不同节点值之间的最小差值 。 差值是一个正数&#xff0c;其数值等于两值之差的绝对值。 示例 1&#xff1a; 输入&#xff1a;root [4,2,6,1,3] 输出&#xff1a;1 解题思路 中序遍…

MDK5.10 安装手册

1.MDK5.10 安装 打开开发板光盘&#xff1a; 6 &#xff0c;软件资料 \ 软件 \MDK5 &#xff0c;双击 mdk_510.exe &#xff0c;进行安装。这里我们 将其安装到 D 盘&#xff0c; MDK5.10 文件夹下&#xff0c;需要设置安装路径&#xff0c;如图 1.1 所示&#xff1a; …

JAVA流程控制--增强for循环

1.JAVA5引入了一种主要用于数组或集合的增强型for循环 2.JAVA增强for循环语法格式如下&#xff1a; for(声明语句&#xff1a;表达式&#xff09; { //代码句子 } 3.声明语句&#xff1a;声明新的局部变量&#xff0c;该变量的类型必须和数组元素的类型匹配。其作用域限定在循…

常见的多态面试题

多态的概念及其构成条件 多态概念&#xff1a;对不同的对象会有不同的实现方法&#xff0c;即为多种形态。 构成条件&#xff1a; 派生类要进行虚函数的重写&#xff08;父子类虚函数需要三同&#xff0c;三同指函数名、参数、返回值&#xff09;要用父类的指针或引用去调用虚…

Spring Boot自动配置原理和应用

我们知道&#xff0c;基于Spring Boot&#xff0c;我们只需要在类路径中引入一组第三方框架的starter组件&#xff0c;就能在Spring容器中使用这些框架所提供的各项功能。这在当下的开发过程中已经习以为常&#xff0c;但在Spring Boot还没有诞生之前却是不可想象的。如果我们使…

在电脑里养一只小猫,工作越忙它跑的越快

在电脑里养一只小猫&#xff0c;工作越忙它跑的越快 话说每个程序员都会比较关注自己电脑的运行状况吧&#xff1f;我也是这样&#xff0c;无论是编译代码还是浏览网页&#xff0c;都会实时监测 CPU 占用情况&#xff0c;看看有没有奇怪的进程占用过多的 CPU&#xff0c;影响我…

Monaco Editor系列(六)Range详解、Uri 自动匹配语言模型、缩略图 miniMap 配置

前情回顾&#xff1a; 一鼓作气&#xff0c;再鼓&#xff0c;再鼓&#xff01;&#xff01;哈哈哈。争取早日占领 Monaco 领地。 上一篇文章讲到的三个功能分别是 Position 类型、设置 markers、指定位置插入或替换内容 涉及到的知识点&#xff1a; ⛈️ 获取光标位置&#x…

从MLP到卷积

1.从MLP到卷积层 最近要做多通道的实验&#xff0c;所以重新将处理图像的基础模型回顾一下&#xff0c;什么是卷积&#xff1f;卷积本质是是一种特殊的全连接层。 1.1怎么w的权重从一个值变成了4维呢?可以这样理解&#xff0c;在此举一个例子&#xff1a; 其实本质可以看成&…

6.5 比赛设备独家揭秘 | 2024高通边缘智能创新应用大赛公开课

2024高通边缘智能创新应用大赛系列公开课热度不减&#xff0c;第三期即将火爆开启&#xff0c;广翼智联FV01边缘智能物联网开发板迎来赛程期间首次公开亮相&#xff01; 在这期的直播中&#xff0c;广翼智联高级产品市场经理伍理化将亲自担任主讲&#xff0c;为大家一一揭晓这…

探索大模型技术及其前沿应用——TextIn文档解析技术

前言 中国图象图形大会&#xff08;CCIG 2024&#xff09;于近期在西安召开&#xff0c;此次大会将面向开放创新、交叉融合的发展趋势&#xff0c;为图像图形相关领域的专家学者和产业界同仁&#xff0c;搭建一个展示创新成果、展望未来发展&#xff0c;集高度、深度、广度三位…

探索气象数据的多维度三维可视化:PM2.5、风速与高度分析

探索气象数据的多维度可视化&#xff1a;PM2.5、风速与高度分析 摘要 在现代气象学中&#xff0c;数据可视化是理解复杂气象模式和趋势的关键工具。本文将介绍一种先进的数据可视化技术&#xff0c;它能够将PM2.5浓度、风速和高度等多维度数据以直观和动态的方式展现出来。 …

ChatTTS 如何安装可视化操作

可视化一键安装下载地址&#xff1a; 百度网盘 Download from GitHub 从 GitHub 下载代码。 git clone https://github.com/2noise/ChatTTS 下载地址 Install Dependencies 在开始之前&#xff0c;请确保已安装必要的软件包。如果您尚未安装它们&#xff0c;可以使用 pip …

Android百度人脸识别3.0配置

JDK 必须是16的版本 如果报错的错误是"opens java.io" org.gradle.jvmargs -Xmx2048M -Dkotlin.daemon.jvm.options\"-Xmx2048M" --add-exportsjava.base/sun.nio.chALL-UNNAMED --add-opensjava.base/java.langALL-UNNAMED --add-opensjava.base/java.…

智能售货机投资指南:从成本预算到市场策略的全方位解析

现代化智能设施的典范&#xff0c;智能售货机以其丰富的商品选项与无缝购物体验著称。然而&#xff0c;涉足此领域前&#xff0c;一番周密的投资考量不可或缺。 首要因素聚焦于售货机本身的购置费用&#xff0c;该费用弹性颇大&#xff0c;依据型号与功能差异而定。基础的小型…

Linux 36.3 + JetPack v6.0@jetson-inference之语义分割

Linux 36.3 JetPack v6.0jetson-inference之语义分割 1. 源由2. segNet2.1 命令选项2.2 下载模型2.2.1 Cityscapes2.2.2 DeepScene2.2.3 MHP2.2.4 VOC2.2.5 SUN 2.3 操作示例2.3.1 单张照片2.3.2 多张照片2.3.3 视频 3. 代码3.1 Python3.2 C 4. 参考资料 1. 源由 分类和目标识…