使用synplify综合遇到的两点疑问
1. synplify不能综合状态机的“when others”状态:我用的是VHDL,编码风格中要求对case语句的使用要做到取值完全覆盖,即要加一个“when others”的判断(我想Verilog也一样吧,用default?),用case 来判断状态机的状态,然而用synplify综合时出warning:OTHERS clause is not synthesized
为什么没有综合呢?
2. synplify不能综合package库文件: VHDL中可以写一个用户库文件,包含工程中用到的自定义数据类型、函数、元件例化等,这样其他模块只要在开始处加上“use work.*.all;”就可以使用用户库文件里的各种定义,类似于c里的“include”,我用ISE都能够正常综合,然而用synplify综合时却报错:Ignoring use clause - element_pack not found ...
其中element_pack 是我工程中用户库的名字,由于被“Ignoring”了,我在element_pack 中定义的数据类型、函数和例化的元件都无法识别,导致报了一大堆错,我只好将这些定义和例化直接移到相应的设计文件中才通过综合。
不知道synplify为什么忽略自定义库文件呢?
关键词: 使用 synplify 综合 遇到 两点 疑问
FSM 分两大类:米里型和摩尔型,组成要素有输入(包括复位),状态(包括当前状态的操作),状态转移条件,状态的输出条件,图1为状态机结构图。设计FSM 的方法和技巧多种多样,但是总结起来有两大类:第一种,将状态转移和状态的操作和判断等写到一个模块(process、block)中。另一种是将状态转移单独写成一个模块,将状态的操作和判断等写到另一个模块中(在Verilog代码中,相当于使用两个“always” block)。其中较好的方式是后者。其原因如下:
首先FSM 和其他设计一样,最好使用同步时序方式设计,好处不再赘述。而状态机实现后,状态转移是用寄存器实现的,是同步时序部分。状态的转移条件的判断是通过组合逻辑判断实现的,之所以第二种比第一种编码方式合理,就在于第二种编码将同步时序和组合逻辑分别放到不同的程序块(process,block)中实现。这样做的好处不仅仅是便于阅读、理解、维护,更重要的是利于综合器优化代码,利于用户添加合适的时序约束条件,利于布局布线器实现设计。
显式的FSM 描述方法可以描述任意的FSM(参考Verilog 第四版P181 有限状态机的说明)。两个always 模块。其中一个是时序模块,一个为组合逻辑。时序模块设计与书上完全一致,表示状态转移,可分为同步与异步复位。
同步:
always @(posedge clk)
if (!reset)
…………
异步:
always @(posedge clk or negedge reset)
if (!reset)
…………
组合逻辑用case 语句,sensitive list 包括当然状态(current state)和输入(a,b,c…)。
编者注:以下是编者从“State Machine Coding Styles for Synthesis”一文中摘取的程序代码,是一个简单状态机的示例,采用两个always块的方法:
module bm1_s (err, n_o1, o2, o3, o4,i1, i2, i3, i4, clk, rst);
output err, n_o1, o2, o3, o4;
input i1, i2, i3, i4, clk, rst;
reg err, n_o1, o2, o3, o4;
parameter [2:0] //可以在此处添加综合约束属性来限定状态机的编码:binary,one-hot,gray,etc…
IDLE = 3'd0,
S1 = 3'd1,
S2 = 3'd2,
S3 = 3'd3,
ERROR = 3'd4;
reg [2:0] state, next;
always @(posedge clk or posedge rst)//异步复位,时序逻辑
if (rst) state <= IDLE;
else state <= next;
always @(state or i1 or i2 or i3 or i4) begin //组合逻辑,敏感列表包含当前状态以及所有的状态机输入
next = 3'bx;//设置默认值,以便防止因为if或者case语句不完整综合生成锁存器
err = 0; n_o1 = 1;
o2 = 0; o3 = 0; o4 = 0;
case (state)
IDLE: begin
next = ERROR;//如果下面所有的if条件都不符合,则对next赋该默认值
if (!i1) next = IDLE;
if (i1 & i2) next = S1;
if (i1 & !i2 & i3) next = S2;
end
S1: begin
next = ERROR;
if (!i2) next = S1;
if (i2 & i3) next = S2;
if (i2 & !i3 & i4) next = S3;
n_o1 = 0;
o2 = 1;
end
S2: begin
next = ERROR;
if (i3) next = S2;
if (!i3 & i4) next = S3;
o2 = 1;
o3 = 1;
end
S3: begin
next = S3;
if (!i1) next = IDLE;
if (i1 & i2) next = ERROR;
o4 = 1;
end
endmodule
对于状态机的输出可以通过寄存器寄存一下,消除毛刺,这将另外需要一个always块,也就是状态机三个always块的写法。
编码风格:
1. 避免生成锁存器
一个完备的状态机(健壮性强)应该具备初始化(reset)状态和默认(default)状态。当芯片加电或者复位后,状态机应该能够自动将所有判断条件复位,并进入初始化状态。需要注
明的一点是,大多数FPGA 有GSR(Global Set/Reset)信号,当FPGA 加电后,GSR 信号拉高,对所有的寄存器,RAM 等单元复位/置位,这时配置于FPGA 的逻辑并未生效,所以不能保证正确的进入初始化状态。所以使用GSR 进入FPGA 的初始化状态,常常会产生种种不必一定的麻烦。一般简单方便的方法是采用异步复位信号,当然也可以使用同步复位,但是要注意同步复位的逻辑设计。状态机也应该有一个默认(default)状态,当转移条件不满足,或者状态发生了突变时,要能保证逻辑不会陷入“死循环”。这是对状态机健壮性的一个重要要求,也就是常说的要具备“自恢复”功能。对应于编码就是对case,if-else 语句要特别注意,要写完备的条件判断语句。VHDL 中,当使用CASE 语句的时候,要使用“When Others”建立默认状态。使用“IF...THEN...ELSE”语句的时候,要在“ELSE”指定默认状态。Verilog 中,使用“case”语句的时候要用“default”建立默认状态,使用“if...else”语句的注意事项相似。
另外有一个技巧:大多数综合器都支持Verilog 编码状态机的完备状态属性--“full case”。这个属性用于指定将状态机综合成完备的状态,如Synplicity 的综合工具(Synplify/Synplify Pro)支持的命令格式如下:
case (current_state) // synthesis full_case
2’b00 : next_state <= 2’b01;
2’b01 : next_state <= 2’b11;
2’b11 : next_state <= 2’b00;
//这两段代码等效
case (current_state)
2’b00 : next_state <= 2’b01;
2’b01 : next_state <= 2’b11;
2’b11 : next_state <= 2’b00;
default : next_state <= 2bx;
Synplicity 还有一个关于状态机的综合属性,叫“// synthesis parallel_case”其功能是检查所有的状态是“并行的”(parallel),也就是说在同一时间只有一个状态能够成立。
编者注:使用上述两个综合约束属性会造成综合前后仿真结果的不一致,请慎重使用。
2. 参数定义用parameter
状态的定义用parameter 定义,不推荐使用`define 宏定义的方式,因为‘define 宏定义在编译时自动替换整个设计中所定义的宏,而parameter 仅仅定义模块内部的参数,定义的参数不会与模块外的其他状态机混淆。
3. 时序电路中一定要使用”<=”非阻塞赋值方式
Verilog的非阻塞行赋值模拟的是实际硬件中串行寄存器的行为,排除了很多潜在的竞争冒险。在使用非阻塞赋值的时候,很多设计者采用"intra-assignment timing delay"(在非阻塞赋值前人为加入一个很小单位的延时)。如下例:
always @(posedge clk or posedge rst)
if (rst)
state <= #1 IDLE;
else
state <= #1 nextstate;
关于这种写法的阐释有下面几点:
I. 首先,这种描述是行为级描述方式,仅仅在仿真时起作用,在综合时会自动被综合器忽略。也就是通常所说的延时描述是不可综合的。
II. 这种描述的好处之一是:它简单模拟了赋值过程寄存器内部的clock-to-output 的延时,在做行为级功能仿真的时候,也可以发现一些由于寄存器固有延迟造成的时序和功能问题。
III. 避免了由RTL 级代码向门级描述转变过程中的一些潜在错误,如保持时间带来的问题。
4 不论是二进制编码的FSM,还是独热码FSM,复位时状态寄存器应该赋值为IDLE状态,如下:
always@( posedge clk or posedge rst )
if( rst ) state <= #1 IDLE;
else state <= #1 next;
如果实现简化的独热码FSM,复位时状态寄存器首先赋值为全零,然后再立即置位状态寄存器的IDLE位。注意:两个非阻塞性赋值作用在同一位上。这完全符合Verilog标准
规定,在此情况下,最后的非阻塞性赋值会代替前面所有的非阻塞性赋值,即更新状态机的IDLE位,如下所示:
always@( posedge clk or posedge rst )
if( rst ) begin
state <= 5'b0;
state[IDLE] <= 1'b1;
end
else state <= next;
5 状态机中组合逻辑块的赋值
在组合always块中只能使用阻塞性赋值。来自同步always块的状态寄存器和状态机的所有输入触发组合always块,该块更新下一状态。应该在组合always块的最前面为下一状态赋一个默认值,块内的case语句会更新该默认值。通常下一状态有三种默认值:全x;预定的恢复状态,如IDLE;状态寄存器的当前值。如果默认值为全x,那么前仿真模型会使状态机在没有任何明确的状态转移时,输出未知。这不仅有利于状态机的调试,还有利于综合,因为综合工具将x作为"无关"。一些设计(如卫星应用,媒体应用,需要使用FSM触发器作为检测扫描链的设计和使用正规验证工具做等价检测的设计)要求下一状态是已知的,而不是全x。通常这些设计下一状态的默认值应该是IDLE或全零,才能满足设计的要求。将下一状态
初始化为默认值,可能比在每个case语句中指定明确的状态转移更简单。
6 Synplify中状态机设计:
可以在Synplify中添加在state定义时添加如下约束属性来限定状态机的编码:
reg [2:0] state /* synthesis syn_encoding = "onehot" */;
Synplify中包含一个强大的FSM编辑器,可以产生在时间和面积上均得到优化的状态机设计,但这将忽略一些状态机中未定义的状态(invalid state),如果必须在状态机进入了未定义的状态后能自动回到有效状态,可以在状态机生成时添加一个安全属性(safe),使得到达无效状态时能回到初始状态,这对电路的时间和面积产生很小的影响:reg [2:0] state /* synthesis syn_encoding = "safe,onehot" */;
这种方法可能与源代码中实际描述的不一致,对于大多数设计来说这不会产生问题,但如果必须与源代码中描述的状态机流程图相吻合,可以通过约束属性关掉FSM编辑器:reg [4:0] state /* synthesis syn_preserve=1 */;但这将严重影响电路的时间和面积特性,下表是将三种方法应用在Altera Flex10k 和 Xilinx Virtex上的比较:
编者注:对第5条和第6条在Synplicity公司的Application Note ”Designing Safe Verilog State Machines with Synplify”中有更详细的说明。
7 几种状态机编码比较:
状态机编码有gray、binary、one-hot 等,其中Binary、gray-code 编码使用最少的触发器,较多的组合逻辑。而one-hot 编码反之。由于CPLD 更多的提供组合逻辑资源,而FPGA 更多的提供触发器资源,所以CPLD 多使用gray-code,而FPGA 多使用one-hot 编码。另一方面,对于小型设计使用gray-code 和binary 编码更有效,而大型状态机使用one-hot更高效。看synplicity 的文档,推荐在24 个状态以上会用格雷码,在5~24 个状态会用独热码,在4 个状态以内用二进制码,肯定独热码比二进制码在实现FSM 部分会占更多资源,但是译码输出控制简单,所以如果状态不是太多,独热码较好。状态太少译码不会太复杂,二进制就可以了。状态太多,前面独热码所占资源太多,综合考虑就用格雷码了。在代码中添加综合器的综合约束属性或者在图形界面下设置综合约束属性可以比较方便的改变状态的编码。在synplify 综合时,把FSM compile 勾上,就算用binary 表示,综合器会自动综合成one-hot 模式。
也可在coding 时直接添加描述:
VHDL
attribute TYPE_ENCODING_style of <typename> : type is ONEHOT;
Verilog
Reg[2:0] state; // synthesis syn_encoding = "value"(onehot)
其中one-hot编码又分为几种:verbose one-hot 、simplified one-hot、one-hot with zero-idle,关于其具体区别请参考“State Machine Coding Styles for Synthesis“一文。
8 同步状态机输入变量的处理
所有的输入变量都必须与状态机时钟同步,否则,状态机的实际运行可能出现奇怪的现
象,会莫名其妙地出现非法状态。所有的状态机都不可避免地可能死锁,在运行中间歇地中止。为什么呢?这是因为,实际上所有的物理状态机是用物理门实现的,而物理门的传播延时不为0。输入信号经过不同的物理门组合到不同的状态触发器输入端D的传播延时有细微的差别。如果输入信号正好在错误的时间改变,那么较快的通路会探测到变化,而较慢的通路则不会。时钟是不变的,所以必定有一个触发器会出现错误的电平。这样就导致状态转移错
误,整个状态机电路就会误动作。这种设计错误通常不被人注意,因为误动作的几率很小。所以,一定要注意异步输入。解决异步输入的办法是同步器,通常二级同步器能够预防错误的状态入口。虽然可能性很小,还是必须注意一个统计问题,即异步输入还是有可能传播通过同步器。因为两个同步阶段的出现,这种可能性被大大地减小了。一些空间或军事设计需要使用三级同步器,但是对于商业和工业级设计,二级同步器已经足够了。为了最大的减小延迟,第一级使用下降沿触发,第二级使用上升沿触发。
9 FSM输出产生
FSM的输出逻辑可以编码为一个独立的连续赋值块,也可以编码为一个组合逻辑always块。如果输出赋值编码到组合always块中,那么输出赋值就可以放到Verilog的task中,每
个case语句调用task。将输出赋值与组合always块分开,不仅有利于在需要更改时改变输出逻辑,还可以避免综合工具产生意外的锁存器。双always块状态机中,如果将输出赋值放在组合always块内,那么应该在always块的顶部初始化输出为默认值,任何case语句将输出改变为合适值。通常,这种方法使用的代码比在每个case语句中为输出赋值更少,并且可以突出输出的变化。Mealy和寄存输出使用连续赋值或在组合always块中,很容易添加Mealy输出,具体如下:
assign rd_out = ( state == READ ) & !rd_strobe_n;
case( state )
...
READ: if( ! rd_strobe_n ) rd_out = 1'b1;
需要注意的是经过状态转移组合输出会出现很窄的毛刺。由于时钟的倾斜,不等的时钟到输出延时Tco和不等的组合逻辑传播延时,毛刺会不断出现。这种毛刺可能很窄,也可能在大多数逻辑分析仪和一些示波器上看见。被这些输出驱动的电路的敏感沿肯定有毛刺,并且可能导致电路错误。更糟糕的是,毛刺的宽度随着温度和功耗变化。为此我们可以在时序always块内使用非阻塞性赋值实现寄存输出。FSM可以使用一个时序always块,也可以在第二个时序always块添加相应的代码。此外如果状态比较简单,还可以定义这样一种状态机,它的输出就是状态位:assign {OUT3, OUT2, OUT1, OUT0} = state[3:0];
这样每个输出的时序都可预测,并且传播延时最小。这样的输出是没有毛刺的,因为假设输入的建立时间Ts和保持时间Th是可观测的,那么每个时钟最多只有一个触发器变化。如果一个输出对于两个连续的状态都是逻辑1,并且输入采取同步方法,那么就可以保证输出的下降沿不会出现毛刺。
一、 时序电路的状态是一个状态变量集合,这些状态变量在任意时刻的值都包含了为确定电路的未来行为而必需考虑的所有历史信息。 状态机采用VerilogHDL语言编码,建议分为三个always段完成。 三段式建模描述FSM的状态机输出时,只需指定case敏感表为次态寄存器,然后直接在每个次态的case分支中描述该状态的输出即可,不用考虑状态转移条件。 三段式描述方法虽然代码结构复杂了一些,但是换来的优势是使FSM做到了同步寄存器输出,消除了组合逻辑输出的不稳定与毛刺的隐患,而且更利于时序路径分组,一般来说在FPGA/CPLD等可编程逻辑器件上的综合与布局布线效果更佳。 示列如下: always @ (posedge clk or negedge rst_n) //异步复位 if(!rst_n) current_state <= IDLE; else current_state <= next_state; //注意,使用的是非阻塞赋值 always @ (current_state) //电平触发 begin next_state = x; //初始化,使得系统复位后能进入正确的状态 case(current_state) S1: if(...) next_state = S2; //阻塞赋值 ... endcase end always @ (posedge clk or negedge rst_n) ...//初始化 case(next_state) S1: out1 <= 1'b1; //注意是非阻塞逻辑 S2: out2 <= 1'b1; default:... //default的作用是免除综合工具综合出锁存器。 endcase end 三段式并不是一定要写为3个always块,如果状态机更复杂,就不止3段了。 二、 1 状态机的编码
a.状态机的编码。Biary、gray-code编码使用最少的触发器,较多的组合逻辑。而one-hot编码反之。由于CPLD更多的提供组合逻辑资源,而FPGA更多的提供触发器资源,所以CPLD多使用gray-code,而FPGA多使用one-hot编码。另一方面,对于小型设计使用gray-code和binary编码更有效,而大型状态机使用one-hot更高效。
b.在代码中添加综合器的综合约束属性或者在图形界面下设置综合约束属性可以比较方便的改变状态的编码。
如VHDL的示例:
Synplicity:
attribute syn_encoding : string;
attribute syn_encoding of <signal_name> : type is "value ";
-- The syn_encoding attribute has 4 values : sequential, onehot, gray and safe.
Exemplar:
-- Declare TYPE_ENCODING_STYLE attribute
-- Not needed if the exemplar_1164 package is used
type encoding_style is (BINARY, ONEHOT, GRAY, RANDOM, AUTO);
attribute TYPE_ENCODING_STYLE : encoding_style;
...
attribute TYPE_ENCODING_STYLE of <typename> : type is ONEHOT;
Verilog示例:
Synplicity:
Reg[2:0] state; /* synthesis syn_encoding = "value" */;
// The syn_encoding attribute has 4 values : sequential, onehot, gray and safe.
Exemplar:
Parameter /* exemplar enum <type_name> */ s0 = 0, s1 = 1, s2 = 2, s3 = 3, S4 = 4;
Reg [2:0] /* exemplar enum <type_name> */ present_state, next_state ;
2:状态机的编码风格
a.关于FSM的编码方法。FSM分两大类:米勒型和摩尔型。组成要素有输入(包括复位),状态(包括当前状态的操作),状态转移条件,状态的输出条件。
设计FSM的方法和技巧多种多样,但是总结起来有两大类:第一种,将状态转移和状态的操作和判断等写到一个模块(process、block)中。另一种是将状态转移单独写成一个模块,将状态的操作和判断等写到另一个模块中(在Verilog代码中,相当于使用两个“always” block)。其中较好的方式是后者。其原因如下。
首先FSM和其他设计一样,最好使用同步时序方式设计,好处不再累述。而状态机实现后,状态转移是用寄存器实现的,是同步时序部分。状态的转移条件的判断是通过组合逻辑判断实现的,之所以第二种比第一种编码方式合理,就在于第二种编码将同步时序和组合逻辑分别放到不同的程序块(process,block)中实现。这样做的好处不仅仅是便于阅读、理解、维护,更重要的是利于综合器优化代码,利于用户添加合适的时序约束条件,利于布局布线器实现设计。
b.初始化状态和默认状态。
一个完备的状态机(健壮性强)应该具备初始化状态和默认状态。当芯片加电或者复位后,状态机应该能够自动将所有判断条件复位,并进入初始化状态。需要注明的一点是,大多数FPGA有GSR(Global Set/Reset)信号,当FPGA加电后,GSR信号拉高,对所有的寄存器,RAM等单元复位/置位,这是配置于FPGA的逻辑并未生效,所以不能保证正确的进入初始化状态。所以使用GSR企图进入FPGA的初始化状态,常常会产生种种不必一定的麻烦。一般的方法是采用异步复位信号,当然也可以使用同步复位,但是要注意同步复位的逻辑设计。解决这个问题的另一种方法是将默认的初始状态的编码设为全零,这样GSR复位后,状态机自动进入初始状态。
令一方面状态机也应该有一个默认(default)状态,当转移条件不满足,或者状态发生了突变时,要能保证逻辑不会陷入“死循环”。这是对状态机健壮性的一个重要要求,也就是常说的要具备“自恢复”功能。对应于编码就是对case,if-else语句要特别注意,要写完备的条件判断语句。VHDL中,当使用CASE语句的时候,要使用“When Others”建立默认状态。使用“IF...THEN...ELSE”语句的时候,要用在“ELSE”指定默认状态。Verilog中,使用“case”语句的时候要用“default”建立默认状态,使用“if...else”语句的注意事项相似。
另外提一个技巧:大多数综合器都支持Verilog编码状态机的完备状态属性--“full case”。这个属性用于指定将状态机综合成完备的状态,如Synplicity的综合工具(Synplify/Synplify Pro,Amplify,etc)支持的命令格式如下:
case (current_state) // synthesis full_case
2’b00 : next_state <= 2’b01;
2’b01 : next_state <= 2’b11;
2’b11 : next_state <= 2’b00;
//这两段代码等效
case (current_state)
2’b00 : next_state <= 2’b01;
2’b01 : next_state <= 2’b11;
2’b11 : next_state <= 2’b00;
default : next_state <= 2bx;
Synplicity还有一个关于状态机的综合属性,叫“// synthesis parallel_case”其功能是检查所有的状态是“并行的”(parallel),也就是说在同一时间只有一个状态能够成立。
c.使用完备的“if...else”还有一个重要的好处,就是可以避免生成非目的性的“锁存器”(Latch)。详见westor的一篇文章“为什么XST与Synplify的综合结果不一样?”
c.使用完备的“if...else”还有一个重要的好处,就是可以避免生成非目的性的“锁存器”(Latch)。详见westor的一篇文章“为什么XST与Synplify的综合结果不一样?”
d.状态机的定义可以用parameter定义,但是不推荐使用`define宏定义的方式,因为‘define宏定义在编译时自动替换整个设计中所定义的宏,而parameter仅仅定义模块内部的参数,定义的参数不会与模块外的其他状态机混淆。
第一个问题:我觉得是不是你的case已经把所有的情况都覆盖了,那么when others自然不用综合了,所以给出了waining。
至于第二个就不知道了
回复
有奖活动 | |
---|---|
【有奖活动】分享技术经验,兑换京东卡 | |
话不多说,快进群! | |
请大声喊出:我要开发板! | |
【有奖活动】EEPW网站征稿正在进行时,欢迎踊跃投稿啦 | |
奖!发布技术笔记,技术评测贴换取您心仪的礼品 | |
打赏了!打赏了!打赏了! |
打赏帖 | |
---|---|
vscode+cmake搭建雅特力AT32L021开发环境被打赏30分 | |
【换取逻辑分析仪】自制底板并驱动ArduinoNanoRP2040ConnectLCD扩展板被打赏47分 | |
【分享评测,赢取加热台】RISC-V GCC 内嵌汇编使用被打赏38分 | |
【换取逻辑分析仪】-基于ADI单片机MAX78000的简易MP3音乐播放器被打赏48分 | |
我想要一部加热台+树莓派PICO驱动AHT10被打赏38分 | |
【换取逻辑分析仪】-硬件SPI驱动OLED屏幕被打赏36分 | |
换逻辑分析仪+上下拉与多路选择器被打赏29分 | |
Let'sdo第3期任务合集被打赏50分 | |
换逻辑分析仪+Verilog三态门被打赏27分 | |
换逻辑分析仪+Verilog多输出门被打赏24分 |