diff --git a/_config.yml b/_config.yml index d8be17a..3dd107e 100644 --- a/_config.yml +++ b/_config.yml @@ -110,9 +110,9 @@ deploy: - type: git repo: josh@101.43.227.73:/home/josh/documents/josh-blog-deploy.git branch: master - # - type: git - # repo: josh@172.245.154.153:/home/josh/documents/josh-blog.git - # branch: master + - type: git + repo: josh@172.245.154.153:/home/josh/documents/josh-blog-deploy.git + branch: master - type: git repo: git@e.coding.net:josh-gao/josh-blog/josh-blog.git branch: master diff --git a/source/_posts/2023-04-16-josh-verilog-part-6.md b/source/_posts/2023-04-16-josh-verilog-part-6.md index bc3f8b3..8c4e4f7 100644 --- a/source/_posts/2023-04-16-josh-verilog-part-6.md +++ b/source/_posts/2023-04-16-josh-verilog-part-6.md @@ -29,7 +29,7 @@ description: 状态机是逻辑设计的重要内容,其设计水平直接反 本节的重点在于理解状态机的基本概念和应用场合。 -## 1.1. 状态机是一种思想方法 +## 1.1. 状态机是一种思想方法 大多数工科生在学习数字电路时都学习过状态机的基本概念,了解一些使用状态机描述时序电路的基本方法。但是**状态机不仅仅是一种时序电路设计工具,它更是一种思想方法**。 @@ -38,7 +38,9 @@ description: 状态机是逻辑设计的重要内容,其设计水平直接反 先看一个简单的例子。在大学生活中,某同学的在校生活可以简单地概括为宿舍、教室、食堂之间的周而复始,可以用[图 6-1](#fig.6-1) 形象地表现出来。这张图并不是要讨论这个学生是否是一个“乖乖”类型学生。 {% note info %} + 请注意,如果将图中的“地点”认为是“状态”,将“功能“认为是状态的“输出”,这张图就是一张标准的状态转移图,也就是说,用状态机的方式清晰地描述了这个学生的在校生活方式。 + {% endnote %} @@ -52,7 +54,9 @@ description: 状态机是逻辑设计的重要内容,其设计水平直接反 {width=600} {% note info %} + 同样如果将图中的“地点”认为是“状态”,将“功能”认为是状态的“输出”,将“条件”认为是状态转移的“输入条件”,[图 6-2](#fig.6-2) 也是一张标准的状态转移图,通过状态机的方式再次清晰地描述了另一个学生的在校生活方式。 + {% endnote %} 事实上使用状态机可以细致入微地描述任何一位同学的在校生活方式。通过前面两个简单举例可以发现,**状态机特别适合描述有发生有先后顺序,或者有逻辑规律的事情**——这就是状态机的本质——对具有逻辑顺序或时序规律事件的一种描述方法。这个论断最重要的两个词就是“**逻辑顺序**”和“**时序规律**”,这两点就是状态机所要描述的核心和强项,换言之,**所有具有逻辑顺序和时序规律的事情都适合用状态机描述**。 @@ -63,7 +67,7 @@ description: 状态机是逻辑设计的重要内容,其设计水平直接反 ## 1.2. 状态机的基本要素与分类 -状态机的基本要素有 3 个,其实在[第一小节](#11__7)的举例中都有涉及,只是没有点明,它们是:**状态**、**输出**和**输入**。 +状态机的基本要素有 3 个,其实在[第一小节](#toc.1.1)的举例中都有涉及,只是没有点明,它们是:**状态**、**输出**和**输入**。 - **状态**:也叫状态变量。在逻辑设计中,使用状态划分逻辑顺序和时序规律。比如,设计伪随机码发生器时,可以用移位寄存器序列作为状态;在设计电机控制电路时,可以以电机的不同转速作为状态;在设计通信系统时,可以用信号的状态作为状态变量等。 @@ -77,7 +81,7 @@ description: 状态机是逻辑设计的重要内容,其设计水平直接反 - **米里型状态机**:米里型状态机的**输出不仅依赖于当前状态,而且取决于该状态的输入条件**。例如[图 6-2](#fig.6-2) 所示的例子,将图中的“地点”认为是“状态”,将“功能”认为是状态的“输出”,将“条件”认为是状态转移的“输入条件”,可以发现,该学生到达什么地方,做什么事情都是由当前状态和输入条件共同决定,所以它是一个米里型状态机。 -根据状态机的数掀是否为有限个,可将状态机分为有限状态机 (Finite State Machine, FSM) 和无限状态机 (Infinite State Machine, ISM)。**逻辑设计中一般所涉及的状态都是有限的,所以以后所说的状态机都指有限状态机,用 FSM 表示。** +根据状态机的数掀是否为有限个,可将状态机分为有限状态机 (Finite State Machine, FSM) 和无限状态机 (Infinite State Machine, ISM)。**逻辑设计中一般所涉及的状态都是有限的,所以以后所说的状态机都指有限状态机**,用 FSM 表示。 ## 1.3. 状态机的基本描述方式 @@ -85,7 +89,7 @@ description: 状态机是逻辑设计的重要内容,其设计水平直接反 ### 1.3.1. 状态转移图 -状态转移图是状态机描述的最自然的方式。如本章第一节[图 6-1](#fig.6-1)、[图 6-2](#fig.6-2) 都使用了状态转移图这一描述方式。状态转移图经常在设计规划阶段定义逻辑功能时使用,也可以在分析代码中状态机时使用,通过图形化的方式非常有助于理解设计意图。 +状态转移图是状态机描述的最自然的方式。如[图 6-1](#fig.6-1)、[图 6-2](#fig.6-2) 都使用了状态转移图这一描述方式。状态转移图经常在设计规划阶段定义逻辑功能时使用,也可以在分析代码中状态机时使用,通过图形化的方式非常有助于理解设计意图。 另外值得一提的是目前有一些 EDA 工具支持状态转移图作为逻辑设计的输入,如 StateCAD。在该工具中设计者只要画出状态转移图就可以了, StateCAD 能自动将状态转移图翻译成 HDL 语言代码,而且翻译出来的代码规范、可读性较好、可综合、易维护。 StateCAD 还能自动检测状态机的完备性和正确性,对状态转移图中的冗余状态、自锁状态、歧义转移条件和不完备状态机等隐含错误都会报警,并协助设计者更正错误。最后 StateCAD 会自动生成设计的测试激励,并调用仿真程序,验证状态机的正确性,这个测试激励甚至可在后仿真中使用。总之, StateCAD 提供了状态机的输入、翻译、检测、优化和测试等一条龙的服务,使状态机的设计变得安全、可靠、快速、便捷。这类自动转换状态转移图为 HDL 源代码的工具对设计、分析一些规模较小的状态机非常有效,但是由于自动反应的代码过于程式化,效率不是很高,所以对于较大规模的逻辑设计,一般还是推荐使用 HDL 语言描述。 @@ -127,7 +131,7 @@ description: 状态机是逻辑设计的重要内容,其设计水平直接反 不规范的 FSM 写法很难让其他人解读,甚至过一段时间后设计者也发现很难维护。 -需要说明的是以上所列的各项标准,特别是前 3 项标准绝不是割裂的,它们直接有紧密的内在联系。前面花了相当长的篇幅论述 FPGA/CPLD 设计评判的两个基本标准:面积和速度。这里“面积”是指一个设计所消耗 FPGA/CPLD 的逻辑资源数量;“速度”指设计在芯片上稳定运行所能够达到的最高频率。两者是对立统一的矛盾体,要求 一 个设计同时具备设计面积最小,运行频率最高,这是不现实的。科学的设计目标应该是:**在满足设计时序要求(包含对设计最高频率的要求)的前提下,占用最小的芯片面积,或者在所规定的面积下,使设计的时序余量更大,频率更高**。 +需要说明的是以上所列的各项标准,特别是前 3 项标准绝不是割裂的,它们直接有紧密的内在联系。前面花了相当长的篇幅论述 FPGA/CPLD 设计评判的两个基本标准:面积和速度。这里“面积”是指一个设计所消耗 FPGA/CPLD 的逻辑资源数量;“速度”指设计在芯片上稳定运行所能够达到的最高频率。两者是对立统一的矛盾体,要求一个设计同时具备设计面积最小,运行频率最高,这是不现实的。科学的设计目标应该是:**在满足设计时序要求(包含对设计最高频率的要求)的前提下,占用最小的芯片面积,或者在所规定的面积下,使设计的时序余量更大,频率更高**。 另外,如果要求 FSM 安全,则很多时候需要使用“full case”的编码方式,即将状态转移变量的所有向量组合情况都在 FSM 中有相应的处理,这势必意味若要多花更多的设计资源,有时也会影响 FSM 的频率。 @@ -135,7 +139,7 @@ description: 状态机是逻辑设计的重要内容,其设计水平直接反 ## 2.2. RTL 级状态机描述常用语法 -在[《Part 2——Verilog 语言基础》](https://josh-gao.top/posts/fd2ca242.html)和[《Part 3——描述方式和设计层次》](https://josh-gao.top/posts/fd117896.html)中论述了 Verilog 的基本语法和常用关键字,其中 RTL 级设计可综合的FSM 相关的常用关键字如下: +在 [Part 2——Verilog 语言基础][]和 [Part 3——描述方式和设计层次][]中论述了 Verilog 的基本语法和常用关键字,其中 RTL 级设计可综合的FSM 相关的常用关键字如下: 1. `wire`、`reg` 等 @@ -145,7 +149,7 @@ description: 状态机是逻辑设计的重要内容,其设计水平直接反 用于描述状态名称,增强源代码可读性,简化描述。 - 例:某状态机使用初始值为 `0` 的独热码(one-hot)编码方式定义的 4 bit 宽度的状态变量 `NS`(代表 Next State,下一状态)和 `CS`(代表 Current State,当前状态),且状态机包含 5 个具体状态 `IDLE`(空闲状态)、`S1`(工作状态 1)、 `S2`(工作状态 2)、 `S3`(工作状态 3)、`ERROR`(告警状态),则代码如下: + 例:某状态机使用初始值为 `0` 的**独热码**(one-hot)编码方式定义的 4 bit 宽度的状态变量 `NS`(代表 Next State,下一状态)和 `CS`(代表 Current State,当前状态),且状态机包含 5 个具体状态 `IDLE`(空闲状态)、`S1`(工作状态 1)、 `S2`(工作状态 2)、 `S3`(工作状态 3)、`ERROR`(告警状态),则代码如下: ``` verilog reg [3:0] NS,CS; @@ -166,7 +170,7 @@ description: 状态机是逻辑设计的重要内容,其设计水平直接反 ``` verilog // sequential state transition always @(posedge elk or negedge nrst) - if (! nrst) + if (!nrst) CS <= IDLE; else CS <= NS; @@ -181,7 +185,7 @@ description: 状态机是逻辑设计的重要内容,其设计水平直接反 `case/endcase` 是 FSM 描述中最重要的语法关键字 ,要详细讨论一下。 `case/endcase` 的基本语法结构如下: ``` verilog - case (case_ expression) + case (case_expression) case_item1 : case_item_statement1; case_item2 : case_item_statement2; case_item3 : case_item_statement3; @@ -190,7 +194,13 @@ description: 状态机是逻辑设计的重要内容,其设计水平直接反 endcase ``` - 其中,`case_expression` 就是 `case` 的判断条件表达式,在 FSM 描述中,它一般为当前状态寄存器;每个 `case_item` 是 `case` 语句的分支列表,在 FSM 描述中,它一般为 FSM 中的所有状态的罗列,从中还可以分析出状态的编码方式; `case_item_statement` 为进入每个 `case_item` 的对应操作,在 FSM 中,即为每个状态对应的状态转移或者输出,如果 `case_item_statement` 包含的操作不只一条,可以用 `begin/end` 嵌套多条操作; `default` 是个可选的关键字,用以指明当所列的所有 `case_item` 与 `case_expression` 都不匹配时的操作,在 FSM 设计中,为了提高设计的安全性,排除所设计的FSM 进入死循环,一般要求加上 `default` 关键字来描述 FSM 所需状态的补集状态下的操作。另外 Verilog 还支持 `casex` 和 `casez` 等不同关键字,但是由于综合器对这两个关键字的支持情况略有差异,所以笔者建议初学者使用完整的 `case` 结构而不使用 `casex` 或 `casez`。 + 其中: + + - `case_expression` 就是 `case` 的判断条件表达式,在 FSM 描述中,它一般为当前状态寄存器;每个 `case_item` 是 `case` 语句的分支列表,在 FSM 描述中,它一般为 FSM 中的所有状态的罗列,从中还可以分析出状态的编码方式; + - `case_item_statement` 为进入每个 `case_item` 的对应操作,在 FSM 中,即为每个状态对应的状态转移或者输出,如果 `case_item_statement` 包含的操作不只一条,可以用 `begin/end` 嵌套多条操作; + - `default` 是可选的关键字,用以指明当所列的所有 `case_item` 与 `case_expression` 都不匹配时的操作,在 FSM 设计中,为了提高设计的安全性,排除所设计的FSM 进入死循环,一般要求加上 `default` 关键字来描述 FSM 所需状态的补集状态下的操作。 + + 另外 Verilog 还支持 `casex` 和 `casez` 等不同关键字,但是由于综合器对这两个关键字的支持情况略有差异,所以建议初学者使用完整的 `case` 结构而不使用 `casex` 或 `casez`。 例:某 FSM 的状态转移用 `case/endcase` 结构描述如下: @@ -232,11 +242,11 @@ description: 状态机是逻辑设计的重要内容,其设计水平直接反 {% endnote %} -5. `task/ endtask` +5. `task/endtask` `task/endtask` 在描述状态机时主要用途是将不同状态对应的输出用 `task/endtask` 封装,增强了代码的可维护性和可读性。 - 例:某状态机 IDLE 状态的输出可以用 `task/endtask` 封装为 `IDEL_out` 任务: + 例:某状态机 IDLE 状态的输出可以用 `task/endtask` 封装为 `IDLE_out` 任务: ``` verilog task IDLE_out; @@ -267,7 +277,7 @@ description: 状态机是逻辑设计的重要内容,其设计水平直接反 在这个范例中将用一段式、两段式、三段式分别描述例子的状态转移,如[图 6-3](#fig.6-3) 所示。这里选用了一个非常典型的米里型状态机,共有 4 个状态: `IDEL`、`S1`、`S2`、`ERROR`;输入信号为时钟 `clk`,低电平异步复位信号 `nrst`, 输入信号 `i1`、`i2`,输出信号为 `o1`、`o2` 和 `err`,状态关系如[图 6-3](#fig.6-3) 所示。状态的输出如下: ``` verilog -IDLE 状态的输出为: {o1, o2, err} = 3'6000; +IDLE 状态的输出为: {o1, o2, err} = 3'b000; S1 状态的输出为: {o1, o2, err} = 3'b100; S2 状态的输出为: {o1, o2, err} = 3'b010; ERROR 状态的输出为: {o1, o2, err} = 3'b111。 @@ -283,7 +293,7 @@ ERROR 状态的输出为: {o1, o2, err} = 3'b111。 ``` verilog state1.v // state1.v -// 1-paragraph method to decribe FSM +// 1-paragraph method to describe FSM // Describe state transition, state output, state input condition in only 1 // always block // Westor, Dec. 2006 @@ -460,7 +470,7 @@ endmodule 本例中,同步时序描述状态转移的 `always` 模块代码如下: -``` verilog +``` verilog state2.v always @(posedge clk or negedge nrst) if (!nrst) CS <= IDLE; @@ -472,7 +482,7 @@ always @(posedge clk or negedge nrst) 本例中,另一个采用组合逻辑判断状态转移条件的 `always` 模块代码如下: -```verilog +``` verilog state2.v // combinational condition judgment always @(nrst or CS or i1 or i2) begin NS = 3'bx; @@ -507,13 +517,13 @@ end 这个使用组合逻辑判断状态转移条件的 `always` 模块也**可以看成格式化的书写结构**。其中 `always` 的敏感列表为当前状态 `CS`, 复位信号和输入条件(**如果是米里状态机,则必须有输入条件;如果是摩尔状态机,一般敏感表和后续逻辑判定没有输入**),请大家注意**电平敏感表必须列完整**。本例中这段电平敏感列表为: -``` verilog +``` verilog state2.v always @(nrst or CS or i1 or i2) ``` 一般来说,在这个组合 `always` 敏感表下先写一个默认的下一状态 `NS` 的描述,然后根据实际的状态转移条件由内部的 `case` 或者 `if ... else` 条件判断确定正确的转移。如本例中下面这段代码, -``` verilog +``` verilog state2.v // ... begin NS = 3'bx; @@ -522,7 +532,7 @@ begin // ... ``` -推荐**在敏感表下的默认状态为不定状态 `X`**, 这样描述的好处有两个:第一在仿真时可以很好地考察所设计的 FSM 的完备性,如果所设计的 FSM 不完备,则会进入任意状态,仿真很容易发现;第二个好处是综合器对不定态 `X` 的处理是 “Don't Care”,即任何没有定义的状态寄存器向最都会被忽略。这里赋值不定态的效果和使用 `casez` 或 `casex` 替代 case 的效果非常相似。 +推荐**在敏感表下的默认状态为不定状态 `X`**, 这样描述的好处有两个:第一在仿真时可以很好地考察所设计的 FSM 的完备性,如果所设计的 FSM 不完备,则会进入任意状态,仿真很容易发现;第二个好处是综合器对不定态 `X` 的处理是 “Don't Care”,即**任何没有定义的状态寄存器向最都会被忽略**。这里赋值不定态的效果和使用 `casez` 或 `casex` 替代 case 的效果非常相似。 在每个 `case` 模块内部的结构也非常相似,都是先描述当前状态的组合逻辑输出,然后根据输入条件(米里 FSM) 判定下一个状态。 @@ -656,7 +666,7 @@ end 大家可能会问,一段式写法也是用寄存器同步了 FSM 的输出,为什么前面介绍一段式的输出代码容易混淆,不利于维护呢?请大家对比一下这段一段式输出的代码, -```verilog +``` verilog case(NS) IDLE: begin if (~i1) begin{o1,o2,err}<=3'b000;NS <= IDLE; end @@ -681,11 +691,13 @@ case(NS) #### 2.3.4.1. n 段式描述方法和 `always` 语法块的个数 -通过学习,大家知道标准的一段式、两段式、三段式 FSM 描述方法分别使用了 1、2、3 个 `always` 语法块。但是请注意,这个命题的反命题不成立,不能说一段 FSM 的描述中使用了 n 个 `always` 语法块,就是 n 段式描述方法。这是因为**特指的一段式、两段式、三段式 FSM 描述方法中每个 `always` 语法块都有固定的描述内容和格式化的结构**,其实也就是通过这些特定的描述内容和格式化的结构,确立了 3 种 FSM 建模方式。例如两段式写法中,第一个 `always` 模块格式化地使用同步时序电路描述次态寄存器到现态寄存器的转移;而第二个 `always` 模块格式化地使用纯组合逻辑描述状态转移条件。 也就是说两段式描述对应的建模方式的硬件电路就是[图 6-5](#fig.6-5) 所示的电路结构。其实从语法角度说,可以将一个 `always` 模块拆分成多个 `always` 模块,或者反之将多个 `always` 模块合并为一个 `always` 模块。所以请注意,**n 段式 FSM 描述方法强调的是一种建模思路,绝不是简单的 `always` 语法块个数**。 +通过学习,大家知道标准的一段式、两段式、三段式 FSM 描述方法分别使用了 1、2、3 个 `always` 语法块。但是请注意,这个命题的反命题不成立,不能说一段 FSM 的描述中使用了 n 个 `always` 语法块,就是 n 段式描述方法。这是因为**特指的一段式、两段式、三段式 FSM 描述方法中每个 `always` 语法块都有固定的描述内容和格式化的结构**,其实也就是通过这些特定的描述内容和格式化的结构,确立了 3 种 FSM 建模方式。 + +例如两段式写法中,第一个 `always` 模块格式化地使用同步时序电路描述次态寄存器到现态寄存器的转移;而第二个 `always` 模块格式化地使用纯组合逻辑描述状态转移条件。 也就是说两段式描述对应的建模方式的硬件电路就是[图 6-5](#fig.6-5) 所示的电路结构。其实从语法角度说,可以将一个 `always` 模块拆分成多个 `always` 模块,或者反之将多个 `always` 模块合并为一个 `always` 模块。所以请注意,**n 段式 FSM 描述方法强调的是一种建模思路,绝不是简单的 `always` 语法块个数**。 #### 2.3.4.2. FSM 的建模方式 -反复强调的 n 段式描述方法其实是 FSM 的 3 种建模方式。大家回顾一下状态[转移图 6-3](#fig.6-3) 描述的 FSM,在学习本节之前,大家可能会产生各种不同的描述思路,通过本节的学习,希望大家能够自然而然地想到用[图 6-5](#fig.6-5)(对应两段式思路)和[图 6-6](#fig.6-6)(对应三段式思路)的结构建模。其实对于绝大多数 FSM,都可以采用[图 6-4](#fig.6-4)、图 6-5](#fig.6-5) 或[图 6-6](#fig.6-6) 的结构建模。一般来说,推**荐使用后两种结构建模。**这是因为:两段式思路建模结构清晰,描述简洁,便于约束,而且如果允许输出逻辑、允许插入一个节拍,就可以通过插入输出寄存器改善输出逻辑的时序并避免组合逻辑的毛刺;三段式思路建模结构清晰,结构格式化,解决了不改变时序要求的前提下用寄存器做状态输出的问题。请大家仔细研究[图 6-4](#fig.6-4)、[图 6-5](#fig.6-5) 和[图 6-6](#fig.6-6) 体会 3 种建模方式。 +反复强调的 n 段式描述方法其实是 FSM 的 3 种建模方式。大家回顾一下状态[转移图 6-3](#fig.6-3) 描述的 FSM,在学习本节之前,大家可能会产生各种不同的描述思路,通过本节的学习,希望大家能够自然而然地想到用[图 6-5](#fig.6-5)(对应两段式思路)和[图 6-6](#fig.6-6)(对应三段式思路)的结构建模。其实对于绝大多数 FSM,都可以采用[图 6-4](#fig.6-4)、[图 6-5](#fig.6-5) 或[图 6-6](#fig.6-6) 的结构建模。一般来说,推**荐使用后两种结构建模**。这是因为:两段式思路建模结构清晰,描述简洁,便于约束,而且如果允许输出逻辑、允许插入一个节拍,就可以通过插入输出寄存器改善输出逻辑的时序并避免组合逻辑的毛刺;三段式思路建模结构清晰,结构格式化,解决了不改变时序要求的前提下用寄存器做状态输出的问题。请大家仔细研究[图 6-4](#fig.6-4)、[图 6-5](#fig.6-5) 和[图 6-6](#fig.6-6) 体会 3 种建模方式。 #### 2.3.4.3. 一段式建模和三段式建模的关系 @@ -704,10 +716,10 @@ case(NS) 一般来说,3 种 FSM 描述方法可以用下表进行比较。但是请注意,任何一种描述的优劣只是一般规律,而不是绝对性规律。例如,一般来说不推荐一段式描述,但是如果 FSM 的结构十分简单,状态很少,状态转移条件和状态输出都十分简化,则使用一段式建模的效率很高。这些经验需要逐步积累,但是在这里推荐的是一般性规律,请结合电路,体会这几种 FSM 建模方法。 |