前面不知道施主们感觉到没有,老僧一直在把大伙儿从电路往 Verilog 语言上拉。这才是正路,很多人却不晓得,可悲啊。
前面学到的语言要素已经可以支持基本的、工程中的数字逻辑系统设计了。我们所缺少的就是算法和经验。算法这个东东呢,需要数学知识,所谓的“熟读唐诗三百首,不会作诗也会吟”;经验的来源是实践,包括对芯片外围的理解,这是“读万卷书不如行万里路”。这些实践包含按键与复位处理、多时钟域处理等,《玩转 IP core》/《IP 核芯志》里面介绍过的内容,也有老衲以前忽略的可变移位和有限状态机设计问题。对于后面两个问题,这里来个“亡羊补牢,为时不晚”。
1. 变宽移位,流水实现
这一讲呢,老衲不务正业了。给大伙儿讲一下,如何实现移位位数可变的移位操作。
在前面关于移位操作的内容里面,那是千叮咛万嘱咐啊:移位的位数必须是常数。大多数人被这个说法迷惑了,认为可变位数的移位操作是不可实现的,是数字逻辑界的永动机。其实这是误解,想想人家 CPU 里面早就可以实现这个功能了。他们可以,我们也自然可以,不可妄自菲薄。鄙人,在这里就要和大家一起突破一下这个语言的限制了,所谓:语言是死的,人是活的。语言上说不成,仅仅是说不能直接用语言 ---- 或者说,简单的电路 ---- 实现这个功能。换句话说,需要用更加复杂的电路来实现,需要用复杂到综合软件无能为力的电路来实现。
然后,会被联系到的实现变量位数移位方法就是:每次移位 1 比特直至移到需要的位数位置。这个属于时序电路的实现了,有流水方法和时分复用方法两种。具体讨论,如果真的象上面的算法,那么根据需要移位的位数的不同,输出结果的时刻是不一样的。一般时序电路完成的操作不喜欢时快时慢的结果输出,可能需要插入一些无效的时序来达到输出时刻的统一。
存在很大的难度,至少老衲目前还没想通如何实现。下来是揭晓正规答案的时候了,请看图 6.15。这个设计还利用了移位操作的分配率,即:
以及 shift_width 的二进制编码特性。这样,
这个权值操作,对于时分复用也是可以采用的,可以节约处理时延。
图 1 流水线、带权值移动的对应电路
对应代码如例 1 所示。
【例 1】流水线、带权值移动的对应代码
module variable_shift_pipeline
(
input CLK, input RST,
input[7:0] a,
input[2:0] shift_width,
output[7:0] shifted_a
);
//Definition for Variables in the module
wire[2:0] width_0;
reg[1:0] width_1;
reg width_2;
//Shift_width chain
wire[7:0] a0;
reg[7:0] a1, a2, a3;
//Operates chain
//Load other module(s)
//Logical
assign a0 = a;
assign width_0 = shift_width;
assign shifted_a = a3;
always @(posedge CLK or negedge RST)
//Shift_width chain
begin
if (!RST)
//Reset
begin
width_1 <= 2'b00;
width_2 <= 1'b0;
end
else
begin
width_1 <= width_0[2:1];
width_2 <= width_1[1];
end
end
always @(posedge CLK or negedge RST)
//Operation
begin
if (!RST)
begin
a1 <= 8'h0;
end
else
begin
if (width_0[0])
begin
a1 <= a0 << 1;
end
else
begin
a1 <= a0;
end
end
end
always @(posedge CLK or negedge RST)
//Operation
begin
if (!RST)
begin
a2 <= 8'h0;
end
else
begin
if (width_1[0])
begin
a2 <= a1 << 2;
end
else
begin
a2 <= a1;
end
end
end
always @(posedge CLK or negedge RST)
//Operation
begin
if (!RST)
begin
a3 <= 8'h0;
end
else
begin
if (width_2)
begin
a3 <= a2 << 4;
end
else
begin
a3 <= a2;
end
end
end
endmodule
2. 有限状态,同步变化
数学家,以及很像数学家的理论物理学家,孜孜不倦地追求统一场论,至今未遂。数字逻辑领域,倒是得到了一个统一的模型,这就是有限状态机(Finite-state machine,FSM)。理论上,所有数字逻辑电路系统都可以被看做这个状态机的一个实例。所以呢,这一讲在下给大家详细介绍有限状态机。
欲了解有限状态机,必先了解状态转移图;欲了解状态转移图,必先知道图中各个单元和文字的含义。图 2 是一个学生在上课的状态转移图,假设到校后上四节课,之后放学;再到校,再上四节课,再放学;如此往复。图里面的一个圆圈就是一个状态,圆的上方的文字是状态名称,下方是该状态对应的操作。例如,状态“上课”的操作就是“听讲”。如果圆的下方没有文字,就是没有具体操作。带有箭头的曲线代表状态的转移,线上的文字是状态转移的条件。例如,“到校”后,听到“打铃”就开始“上课”。如果线上没有文字,则表示无条件转移。状态转移的节拍,就是系统输入时钟的周期。
图 2 学生上学的状态图
忽略复杂的数学理论,直接介绍有限状态机实现。有限状态机,对于实现非常重要的抽象模型图图分享给给为施主,见图 3。
图 3 有限状态机的抽象模型
莫急莫急,这就到了一段、两段、三四段的介绍了。什么叫四段啊?看看,被你催的,一着急敲错了。
所谓“一段式”如就是“不管三七二十一”,把所有逻辑写到一起。这种风格,可以借用程序语言里面的一个术语叫做“一团面条”。
“二段式”属于一种比“一段式”更加奇怪的做法。看图 6.19,二段式有两种方式。总结起来都属于乱来。按照排列组合 C_3^2=3,有三种配法:或者把“状态转移条件”和“状态”配对,或者让“状态”与“输出逻辑”结合,还有“状态转移条件”与“输出逻辑”的路数。还是那句话:不建议,不鼓励。
有限状态机的“三段式”描述方法。有限状态机 和其他设计一样,最好使用同步时序方式设计,以提高设计的稳定性,消除毛刺。状态机实现后,一般来说,状态转移部分是同步时序电路而状态的转移条件的判断是组合逻辑。三段式之所以比一段和二段式编码合理,就在于三段式编码将不同功能分别放到不同的 always 程序块中实现。这样做的好处不仅仅是便于阅读、理解、维护,更重要的是利于综合器优化代码,利于用户添加合适的时序约束条件,利于布局布线器实现设计。
说的很抽象,现在用一个实例的不同代码风格表演一下下。
“序列检测器可用于检测由二进制码组成的脉冲序列信号。当序列检测器连续收到一组串行二进制码后,如果这组序列码与检测器中预先设置的序列码相同,则输出 1,否则输出 0。这种检测的关键是必须收到连续的正确码,所以要求检测器必须对前一次接受到的序列码做记忆分析,直到在连续检测中所收到的每一位二进制码都与预置序列码对应相同。在检测过程中,只要有一位不相等都将回到初始状态重新开始检测。不考虑重叠的可能。”
为了真正达到可以实现的程度,再来细化一下输入输出和具体条件。先来定义同步字长度,就 4 比特吧。虽然没有任何实际价值,判决失误率很高;但是代码短一点,该有的全有了,更常的宽度也只是力气活了。还有输入是一个时钟周期,输入一个比特的数据,低比特在前面。如果匹配到同步字,才输出高电平“1”;其他时间,输出低电平“0”。
先设计系统的状态转移图,如图 3 所示。每次检测到一个比特,状态前进一次;没有检测到,则状态回退到空闲 /IDLE。
图 3 比特序列检测单元的状态转移图
例 2 是这个模块的二段式代码,例 3 是这个模块的三段式代码。
【例 2】比特序列检测单元的代码(二段式)
`define STATE_IDLE 0
`define STATE_BIT0 1
`define STATE_BIT1 2
`define STATE_BIT2 3
`define STATE_BIT3 4
//State defination
`define SYNC_CODE 4'b1001
//Sequence to be detected
module sequence_detect
(
input clk,
input Reset,
input data,
output reg detected
);
//Defination for Varables in the module
reg[3:0] state;
wire[3:0] detecting_sequnce;
//Logicals
//Combanitory logicals
assign detecting_sequnce = `SYNC_CODE;
//Timing
always @ (posedge clk or negedge Reset)
//Statement management part
begin
if (!Reset)
//Reset enable
begin
state <= `STATE_IDLE;
end
else
//state change
begin
case (state)
`STATE_IDLE:
//Idle statement, waiting for bit 0
begin
if ( data == detecting_sequnce[0])
//Bit 0 detected
begin
state <= `STATE_BIT0;
end
else
begin
end
end
`STATE_BIT0:
//Bit0 statement, waiting for bit 1 or return idle
begin
if ( data == detecting_sequnce[1])
//Bit 1 detected
begin
state <= `STATE_BIT1;
end
else
//Return idle statement
begin
state <= `STATE_IDLE;
end
end
`STATE_BIT1:
//Bit1 statement, waiting for bit 2 or return idle
begin
if ( data == detecting_sequnce[2])
//Bit 2 detected
begin
state <= `STATE_BIT2;
end
else
//Return idle statement
begin
state <= `STATE_IDLE;
end
end
`STATE_BIT2:
//Bit2 statement, waiting for bit 3 or return idle
begin
if ( data == detecting_sequnce[3])
//Bit 2 detected
begin
state <= `STATE_BIT3;
end
else
//Return idle statement
begin
state <= `STATE_IDLE;
end
end
`STATE_BIT3:
//Bit3 statement, return idle
begin
//Return idle statement
state <= `STATE_IDLE;
end
default:
begin
//Return idle statement
state <= `STATE_IDLE;
end
endcase
end
end
always @ (posedge clk or negedge Reset)
//Output part
begin
if (!Reset)
//Reset enable
begin
detected <= 1'b0;
end
else if (`STATE_BIT3 == state)
//Sequence detected
begin
detected <= 1'b1;
end
else
//Idle and detecting
begin
detected <= 1'b0;
end
end
endmodule
【例 3】比特序列检测单元的代码(三段式)
……
//Defination for Varables in the module
reg[3:0] state;
reg[3:0] next_state;
//State variables
……
//Timing
always @ (posedge clk or negedge Reset)
//Statement management part
begin
if (!Reset)
//Reset enable
begin
next_state <= `STATE_IDLE;
end
else
//state change
begin
case (state)
`STATE_IDLE:
//Idle statement, waiting for bit 0
begin
if ( data == detecting_sequnce[0])
//Bit 0 detected
begin
next_state <= `STATE_BIT0;
end
else
begin
end
end
`STATE_BIT0:
//Bit0 statement, waiting for bit 1 or return idle
begin
if ( data == detecting_sequnce[1])
//Bit 1 detected
begin
next_state <= `STATE_BIT1;
end
else
//Return idle statement
begin
next_state <= `STATE_IDLE;
end
end
`STATE_BIT1:
//Bit1 statement, waiting for bit 2 or return idle
begin
if ( data == detecting_sequnce[2])
//Bit 2 detected
begin
next_state <= `STATE_BIT2;
end
else
//Return idle statement
begin
next_state <= `STATE_IDLE;
end
end
`STATE_BIT2:
//Bit2 statement, waiting for bit 3 or return idle
begin
if ( data == detecting_sequnce[3])
//Bit 2 detected
begin
next_state <= `STATE_BIT3;
end
else
//Return idle statement
begin
next_state <= `STATE_IDLE;
end
end
`STATE_BIT3:
//Bit3 statement, return idle
begin
//Return idle statement
next_state <= `STATE_IDLE;
end
default:
begin
//Return idle statement
next_state <= `STATE_IDLE;
end
endcase
end
end
always @ (posedge clk or negedge Reset)
//State part
begin
if (!Reset)
//Reset enable
begin
state <= `STATE_IDLE;
end
else
//State change
begin
state <= next_state;
end
end
……
endmodule
三段式比二段式多了一个 next_state 这个过渡的寄存器,其他写法非常类似。
其他课题还有很多,等到施主遇到的时候,在单独讨论好了。
这正是:
“
工程问题万万千,理论知识无极限。系统设计百般变,电路描述有语言。
数学早有研究远,都会教材根本源。模式变化数字三,有限状态模型间。
”
与非网原创内容,谢绝转载!
系列汇总: