Verilog基础(五):时序逻辑
时序逻辑 (Sequential Login)
锁存器与触发器
- D-触发器(D flip-flops)
D-触发器可以存储一个bit数据并根据时钟信号周期的更新数据,一般是由正边沿触发.
D-触发器由逻辑合成器(Logic synthesizer)在使用"Always block"时创建(参见AlwaysBlock2).D-触发器是"组合逻辑块之后连接触发器"的最简单形式,其中组合逻辑部分只是一个wire类型变量.
创建一个D-触发器.
- Module Declaration
module top_module (
input clk,
input [7:0] d,
output [7:0] q
);
- Solution
module top_module (
input clk, // Clocks are used in sequential circuits
input d,
output reg q );//
// Use a clocked always block 使用Always block
// copy d to q at every positive edge of clk 在时钟信号上升沿时将q复制给d
// Clocked always blocks should use non-blocking assignments
/*在Always block内永远都不要使用赋值(Assignment),即assign关键字*/
always @(posedge clk) begin
q = d;
end
endmodule
- D触发器组合
创建8个D触发器,所有的D触发器均由时钟上升沿触发.
- Module Declaration
module top_module (
input clk,
input [7:0] d,
output [7:0] q
);
- Solution
module top_module (
input clk,
input [7:0] d,
output [7:0] q
);
always @(posedge clk) begin
q = d;
end
endmodule
- 可复位(Reset)的DFF
使用主动高位同步复位信号(Reset)创建8个D-触发器.
所有的触发器均由时钟上升沿信号触发.
- Module Declaration
module top_module (
input clk,
input reset, // Synchronous reset
input [7:0] d,
output [7:0] q
);
- Solution
module top_module (
input clk,
input reset, // Synchronous reset
input [7:0] d,
output [7:0] q
);
always @(posedge clk) begin
q <= d & {8{~reset}};
end
endmodule
- 可复位为特殊值的D-触发器
使用主动高位同步复位信号(Reset)创建8个D-触发器.
触发器必须重置为0x34而不是零.
所有DFF都应该由CLK的下降沿触发.
- Module Declaration
module top_module (
input clk,
input reset,
input [7:0] d,
output [7:0] q
);
- Solution
module top_module (
input clk,
input reset,
input [7:0] d,
output [7:0] q
);
always @(negedge clk) begin
if(reset) begin
q <= {8{6'h34}};
end
else begin
q <= d;
end
end
endmodule
- 可异步复位的触发器(Asynchronous Reset)
使用主动高位异步复位信号创建8个D-触发器.
所有DFF都应该由时钟上升沿触发.
- Module Declaration
module top_module (
input clk,
input areset, // active high asynchronous reset
input [7:0] d,
output [7:0] q
);
- Solution
module top_module (
input clk,
input areset, // active high asynchronous reset
input [7:0] d,
output [7:0] q
);
always @(posedge clk or posedge areset) begin
if(areset == 1) begin
q <= 0;
end
else begin
q <= d;
end
end
endmodule
- 有开关的触发器
创建16个D-触发器.有时我们希望只修改触发器组的部分值.输入的开关字节控制16个寄存器的每个字节是否应在该循环中写入.
byteena[1]
控制上字节d[15:8]
,而byteena[0]
控制下字节d[7:0]
.
resetn
是一个同步的、主动的低重置.
所有DFF都应该由时钟的上升沿触发.
- Module Delclaration
module top_module (
input clk,
input resetn,
input [1:0] byteena,
input [15:0] d,
output [15:0] q
);
- Solution
module top_module (
input clk,
input resetn,
input [1:0] byteena,
input [15:0] d,
output [15:0] q
);
always @(posedge clk) begin
if(resetn == 1) begin
if(byteena[0] == 1) begin
q[7:0] <= d[7:0];
end
if(byteena[1] == 1) begin
q[15:8] <= d[15:8];
end
end
else if(resetn == 0) begin
q <= 0;
end
end
endmodule
- D-锁存器(D Latch)
实现如下电路:
请注意,这是一个锁存器,因此Quartus会报锁存警告
- Module Declaration
module top_module (
input d,
input ena,
output q);
module top_module (
input d,
input ena,
output q);
wire in,out1,out2;
always @(*) begin
out1 = ~(out2 | (~d)&ena);
out2 = ~(out1 | d&ena);
q = out1;
end
endmodule
- DFF练习1
请实现如下电路:
- Module Declaration
module top_module (
input clk,
input d,
input ar, // asynchronous reset
output q);
- Solution
module top_module (
input clk,
input d,
input ar, // asynchronous reset
output q);
wire out1,out2;
always @(posedge clk or posedge ar) begin
if(ar == 1) begin
q <= 0;
end
else if(ar == 0) begin
q <= d;
end
end
endmodule
- DFF2
实现如下电路:
- Module Declaration
module top_module (
input clk,
input d,
input r, // synchronous reset
output q);
- Solution
module top_module (
input clk,
input d,
input r, // synchronous reset
output q);
always @(posedge clk) begin
if(r) begin
q <= 0;
end
else begin
q = d;
end
end
endmodule
- DFF和gate
实现如下电路:
- Module Declaration
module top_module (
input clk,
input in,
output out);
- Solution
module top_module (
input clk,
input in,
output out);
wire out1,out2;
always @(posedge clk) begin
out1 = out2 ^ in;
out2 = out1;
out = out2;
end
endmodule
- 多路选择器与触发器1
考虑如下时序电路:
假设您想为如下电路实现层级化(Hierarchical)的代码,使用3个子级模块的实例,子级模块由触发器与多路选择器构成.
您的顶级模块应当由含子级模块实现.
注: 这是一种封装的思想
- Module Declaration
module top_module (
input clk,
input L,
input r_in,
input q_in,
output reg Q);
- Solution
module top_module (
input clk,
input L,
input r_in,
input q_in,
output reg Q);
wire out1;
always @(*) begin
case (L)
1'b0: begin
out1 <= q_in;
end
1'b1: begin
out1 <= r_in;
end
endcase
end
always @(posedge clk) begin
Q <= out1;
end
endmodule
- 多路选择器与触发器2
考虑如下n位变换寄存器电路:
为这个电路实现一个一阶模块,并命名为top_module
,可供此电路调用.你的模块应包括触发器与多路选择器.
- Module Declaration
module top_module (
input clk,
input w, R, E, L,
output Q
);
- Solution
module top_module (
input clk,
input w, R, E, L,
output Q
);
wire out1,out2;
always @(posedge clk) begin
case (E)
1'b0: begin
out1 = Q;
end
1'b1: begin
out1 = w;
end
endcase
case (L)
1'b0: begin
Q = out1;
end
1'b1: begin
Q = R;
end
endcase
end
endmodule
- DFFs和gates
给定如下有限状态机电路,假设D-触发器在状态机开始前被初始化为0.
构建如下电路.
- Module Declaration
module top_module (
input clk,
input x,
output z
);
- Solution
module top_module (
input clk,
input x,
output z
);
reg out1, out2, out3;
initial z = 1;
always @(posedge clk) begin
out1 = x ^ out1;
out2 = x & ~out2;
out3 = x | ~out3;
z = ~(out1 | out2 | out3);
end
endmodule
- 根据真值表实现电路
JK触发器有如下真值表.通过一个D-触发器和几个gate实现一个JK触发器.
注意:Qold是D-触发器在上升沿之前的输出.
J | K | Q |
---|---|---|
0 | 0 | Qold |
0 | 1 | 0 |
1 | 0 | 1 |
1 | 1 | ~Qold |
- Module Declaration
module top_module (
input clk,
input j,
input k,
output Q);
- Solution
module top_module (
input clk,
input j,
input k,
output Q);
wire old = Q;
always @(posedge clk) begin
if(j^k) begin
Q = j;
end
else begin
Q = j?~old:old;
end
end
endmodule
- 边缘检测(Detect an edge)
对于每一个8位容器,在一个时钟周期内,当输入信号从0,变为下个周期的1时进行检测.(类似于上升沿检测).
当对应位从0变为1时应当设置输出位.
以下为波形样例,为了清楚起见,in[1]
与pedge[1]
被分别显示.
解释一下:
首先,让我们解释一下这段描述中的关键概念。
-
8位容器与上升沿检测
- 8位容器:这里指的是一个能够存储8位(bit)数据的寄存器或存储单元。每一位(bit)可以独立地存储0或1的值。
- 上升沿检测:在数字电路中,上升沿指的是一个信号从低电平(通常是0)变为高电平(通常是1)的瞬间。这段描述中提到的“当输入信号从0,变为下个周期的1时进行检测”,就是在说我们需要检测这种上升沿事件。
-
输入信号与输出位
- 输入信号:这里指的是8位容器接收到的输入数据。
- 输出位:在检测到特定的输入条件(即某一位的上升沿)时,需要被设置的位。
-
工作原理
根据描述,系统会在每个时钟周期内检查8位容器的每一位,看看是否有从0变为1的情况(即检测上升沿)。如果检测到某一位的输入信号从0变为1,那么对应的输出位应当被设置(通常是置为1)。
-
波形样例解释
为了更直观地理解,描述中提到了
in[1]
与pedge[1]
被分别显示。这里:in[1]
:表示8位容器中第1位(通常是从0开始计数)的输入信号波形。pedge[1]
:表示第1位的上升沿检测输出波形。当in[1]
从0变为1时,pedge[1]
会在相应的时刻产生一个信号(通常是一个短暂的脉冲或置为高电平),表示检测到了上升沿。
-
示例
假设在某个时钟周期内,
in[1]
的值从0变为1,而其他位保持不变。那么,在这个周期结束时或下一个周期开始时(取决于具体的实现细节),pedge[1]
会被设置为1(或产生一个脉冲),表示第1位检测到了上升沿。如果in[1]
的值在接下来的周期中没有再次从0变为1,那么pedge[1]
将不会再次被设置,直到下一次这样的上升沿事件发生。这种机制在数字电路设计中非常常见,用于同步信号、触发事件或进行状态转换等。
- Module Declaration
module top_module (
input clk,
input [7:0] in,
output [7:0] pedge
);
- Solution
module top_module(
input clk,
input [7:0] in,
output reg [7:0] pedge);
reg [7:0] d_last;
always @(posedge clk) begin
d_last <= in; // Remember the state of the previous cycle
pedge <= in & ~d_last; // A positive edge occurred if input was 0 and is now 1.
end
endmodule
- 边缘检测2
对于每一个8位容器,在一个时钟周期内,
当输入信号从0,变为下个周期的1时进行检测.(上升沿与下降沿均触发).
当对应位从0变为1时应当设置输出位.
以下为波形样例,为了清楚起见,in[1]
与anyedge[1]
被分别显示.
- Module Declaration
module top_module (
input clk,
input [7:0] in,
output [7:0] anyedge
);
- Solution
module top_module (
input clk,
input [7:0] in,
output [7:0] anyedge
);
reg [7:0] temp;
always @(posedge clk) begin
temp <= in;
anyedge <= temp ^ in;
end
endmodule
- 边缘捕获寄存器(Edge capture register)
对于32位Vector中的每一位,当输入信号在一个时钟周期内从1变为下一个时钟周期的0时进行捕获.
"捕获"表示输出将保持1,直到寄存器复位(同步复位 – Synchronous Reset).
每个输出位的行为类似于SR触发器:在发生1到0的转换之后,输出位应该被设置为1.
当复位为高电平时,输出位应在上升沿复位(至0).
如果上述两个事件同时发生,则重置(Reset)具有优先权.在下面示例波形的最后4个周期中,"Reset"事件比"Set"事件早一个周期,因此这里没有冲突.
这里描述的是一个针对32位向量的特定捕获逻辑,主要用于检测输入信号从1变到0的转换,并将这一转换事件的结果保存在输出寄存器中,直到收到复位信号为止。这种逻辑常用于状态检测、事件记录或触发其他电路动作。下面是对各个部分的详细解释:
- 输入信号检测:
对于32位Vector中的每一位,系统监测输入信号。
当某个位的输入信号在一个时钟周期内从1变为0(即从高电平变为低电平),则认为发生了“1到0的转换”。 - 捕获机制:
一旦检测到某个位发生了“1到0的转换”,相应的输出位就被“捕获”并设置为1。
“捕获”意味着这个输出位的状态会被保持为1,即便输入信号再次变化,输出位的状态也不会改变,除非接收到复位信号。 - 输出位的行为:
每个输出位的行为类似于一个置位/复位(Set/Reset, SR)触发器。
在检测到“1到0的转换”后,相应的输出位被置为1(Set)。 - 复位机制:
当复位信号(Reset)为高电平时,并且在下一个时钟的上升沿(时钟信号从0变为1的瞬间),所有输出位将被复位(即设置为0)。
这意味着复位信号具有最高的优先级,它能够在任何时候覆盖并清除之前的捕获状态。 - 事件优先级:
如果在某一个时钟周期内,同时发生了“1到0的转换”和复位信号的有效(即复位信号为高电平),那么复位信号将优先执行。
这意味着即便是在检测到“1到0的转换”后,如果紧接着在下一个时钟周期(或更早,取决于复位信号的触发边缘)收到复位信号,输出位还是会被复位为0。 - 示例波形解释:
在提供的示例波形中,最后4个周期展示了复位信号和“1到0的转换”之间的关系。
由于复位信号比“1到0的转换”引起的置位(Set)事件早一个周期发生,因此在这个场景中,复位信号有效地阻止了输出位的置位,所有相关的输出位都被复位为0。
这展示了复位信号的优先级以及它是如何覆盖之前可能发生的状态变化的。
总结来说,这种逻辑设计允许系统监测并记录特定信号的变化,同时提供了一个可靠的复位机制来清除这些记录,确保了系统的稳定性和可控性。
- Module Declaration
module top_module (
input clk,
input reset,
input [31:0] in,
output [31:0] out
);
- Solution
module top_module (
input clk,
input reset,
input [31:0] in,
output [31:0] out
);
reg [31:0] old,result,temp;
initial temp = 0;
always @(posedge clk) begin
if(reset) begin
old = in;
result = 0;
out = 0;
end
else begin
temp = (in ^ old) & old;
result = result | temp;
out = result;
old = in;
end
end
endmodule
- 双边沿出发(Dualedge)
你现在已经比较熟悉上升沿或者下降沿出发的触发器了.而双边触发器在时钟上升沿与下降沿都会触发.但是FPGA并没有双边沿触发器,并且always @(posedge clk or negedge clk)
是非法的.
构建一个电路来实现类似于双边触发器的功能:
注意:并不需要完全等效:在这里触发器的输出没什么毛病,但是在更大的电路中模拟这种行为可能会有故障,我们在这里忽略这个细节.
-
Hint:
- 你不能在FPGA上创建双边沿触发器,但是组合上升沿与下降沿触发器.
- 这个问题是一个中等难度的电路设计问题,但只需要基本的verilog语言特性.(这是一个电路设计问题,而不是编码问题.)在试图对电路进行编码之前,先用手画出电路草图可能会有所帮助.
-
Module Declaraction
module top_module (
input clk,
input d,
output q
);
- Solution
module top_module (
input clk,
input d,
output q
);
reg [1:0] status;
always @(posedge clk) begin
status[0] = d;
end
always @(negedge clk) begin
status[1] = d;
end
assign q = clk ? status[0] : status[1];
endmodule
计数器(Counters)
- 四位二进制计数器
构建一个0-15的四位二进制计数器,句点为16.重置输入是同步的,应将计数器重置为0.
- Module Declaration
module top_module (
input clk,
input reset, // Synchronous active-high reset
output [3:0] q);
- Solution
module top_module (
input clk,
input reset, // Synchronous active-high reset
output [3:0] q);
always @(posedge clk) begin
if(!reset) begin
if(q == 4'hf) begin
q = 0;
end
else begin
q = q + 1;
end
end
else begin
q = 0;
end
end
endmodule
- 十进制计数器
建立一个从0到9的十进制计数器,句点为10,重置输入是同步的,应该将计数器重置为0.
- Module Declaration
input clk,
input reset, // Synchronous active-high reset
output [3:0] q);
- Solution
module top_module (
input clk,
input reset, // Synchronous active-high reset
output [3:0] q);
always @(posedge clk) begin
if(reset) begin
q <= 0;
end
else begin
if(q = 4'd10) begin
q = 0;
end
else begin
q = q + 1;
end
end
end
endmodule
- 十进制计数器1
建立一个从0到9的十进制计数器,句点为10,重置输入是同步的,应该将计数器重置为1.
- Module Declaration
module top_module (
input clk,
input reset,
output [3:0] q);
- Solution
module top_module (
input clk,
input reset,
output [3:0] q);
always @(posedge clk) begin
if(reset) begin
q <= 1;
end
else begin
if(q == 4'ha) begin
q = 1;
end
else begin
q = q + 1;
end
end
end
endmodule
- 慢序十进制计数器
构建一个0到9的十进制计数器,句点为10.reset信号是同步的,将计数器置零.我们希望能够暂停计数器,而不是在每个时钟周期内增加,因此slowena控制计数器的增加.
- Module Declaration
module top_module (
input clk,
input slowena,
input reset,
output [3:0] q);
- Solution
module top_module (
input clk,
input slowena,
input reset,
output [3:0] q);
always @(posedge clk) begin
if(reset) begin
q <= 0;
end
else begin
if(slowena) begin
if(q == 4'd9) begin
q = 0;
end
else begin
q = q + 1;
end
end
else begin
q = q + 0;
end
end
end
endmodule
- Counter1-12
设计一个具有以下输入输出的Counter:
- 同步高位
Reset
信号将寄存器重置为0 - 当
enable
为高位时Counter进行工作 - 时钟信号上升沿触发
Q[3:0]
为counter的输出c_enable, c_load, c_d[3:0]
三个控制信号发送到所提供的4-bit Counter模块,以便检验正确操作.
你可以使用如下组件:
如下4位计数器,enable
与load
同步并行输入(load的优先级高于enable).这个count4模块提供给你,在你的模块中实例化他.
module count4(
input clk,
input enable,
input load,
input [3:0] d,
output reg [3:0] Q
);
- Module Declaration
module top_module (
input clk,
input reset,
input enable,
output [3:0] Q,
output c_enable,
output c_load,
output [3:0] c_d
);
- Solution
module top_module (
input clk,
input reset,
input enable,
output [3:0] Q,
output c_enable,
output c_load,
output [3:0] c_d
); //
wire [3:0] Q_tmp;
assign c_enable = enable;
assign c_d = c_load ? 1 : 0;
always @(posedge clk) begin
if(reset) begin
Q <= 1;
Q_tmp <= 1;
end
else begin
if(enable) begin
if(Q == 12) begin
Q <= 1;
Q_tmp <= 1;
end
else begin
Q <= Q + 1;
Q_tmp <= Q_tmp + 1;
end
end
end
end
always @(*) begin
if(reset || (Q == 12 && c_enable)) begin
c_load <= 1;
end
else begin
c_load <= 0;
end
end
count4 the_counter (clk, c_enable, c_load, c_d, Q_tmp);
endmodule
- 算数左右移
构建一个64为算数寄存器,具有同步load信号.这个shifter根据amount
信号选择,可左右移1位或8位.
在算数右移时,若符号位(q[63])为1则需要复制位,而并非简单的逻辑右移.另一种考虑方式,假定被移位的数字有符号并保留符号,这样算术右移将有符号数字除以2的幂.
算数左移与逻辑左移没有区别.
-
load
: 寄存器载入数据(优先级最高) -
ena
: 决定是否发生位移 -
amount
: 决定位移的方向- 2’b00: 左移一位
- 2’b01: 左移8位
- 2’b10: 右移一位
- 2’b11: 右移8位
-
q
: shifter的内容 -
Module Declaration
module top_module(
input clk,
input load,
input ena,
input [1:0] amount,
input [63:0] data,
output reg [63:0] q);
- Solution
module top_module(
input clk,
input load,
input ena,
input [1:0] amount,
input [63:0] data,
output reg [63:0] q);
always @(posedge clk) begin
if(load) begin
q <= data;
end
else begin
if(ena) begin
case (amount)
2'b00: begin
q <= q << 1;
end
2'b01: begin
q <= q << 8;
end
2'b10: begin
q <= q >> 1;
if(q[63] == 1) begin
q[63] <= 1;
end
end
2'b11: begin
q <= q >> 8;
if(q[63] == 1) begin
q[63:56] <= {8{1'b1}};
end
end
endcase
end
end
end
endmodule
有限状态机
- 简单FSM1
实现如下简单摩尔状态机:
- Module Declaraction
module top_module(
input clk,
input areset, // Asynchronous reset to state B
input in,
output out);
- Solution
module top_module(
input clk,
input areset, // Asynchronous reset to state B
input in,
output out);//
parameter A=0, B=1;
reg state, next_state;
always @(*) begin // This is a combinational always block
// State transition logic
case (state)
A: next_state <= in?A:B;
B: next_state <= in?B:A;
endcase
end
always @(posedge clk, posedge areset) begin // This is a sequential always block
// State flip-flops with asynchronous reset
if(areset) begin
state <= B;
end
else begin
state <= next_state;
end
end
// Output logic
// assign out = (state == ...);
assign out = (state==B);
// always @(*) begin
// case (state)
// A: out <= 0;
// B: out <= 1;
// endcase
// end
endmodule
- Fsm1s
实现如下电路:
-
- Module Declaraction
module top_module(clk, reset, in, out);
input clk;
input reset; // Synchronous reset to state B
input in;
output out;
- Solution
// Note the Verilog-1995 module declaration syntax here:
module top_module(clk, reset, in, out);
input clk;
input reset; // Synchronous reset to state B
input in;
output out;//
reg out;
// Fill in state name declarations
reg present_state, next_state;
parameter A = 0, B = 1;
always @(posedge clk) begin
if (reset) begin
present_state <= B;
end else begin
present_state <= next_state;
end
end
always @(*) begin
case (present_state)
A: next_state <= in ? A : B;
B: next_state <= in ? B : A;
endcase
end
assign out = (present_state == B);
endmodule
- 旅鼠
旅鼠是一种头脑简单的动物.非常简单,我们将用一个有限状态机对它进行建模.
在旅鼠的二维世界中,旅鼠可以处于两种状态之一:
向左行走或向右行走.如果碰到障碍物,它会改变方向.尤其是,如果一只旅鼠在左边撞了,它就会向右走.如果它撞到右边,它就会向左走.如果它在两侧同时发生碰撞,它仍然会改变方向.
实现一个具有两个状态、两个输入和一个输出的摩尔状态机来模拟这种行为.
- Module Declaraction
module top_module(
input clk,
input areset, // Freshly brainwashed Lemmings walk left.
input bump_left,
input bump_right,
output walk_left,
output walk_right);
- Solution
module top_module(
input clk,
input areset, // Freshly brainwashed Lemmings walk left.
input bump_left,
input bump_right,
output walk_left,
output walk_right); //
parameter LEFT=0, RIGHT=1;
reg state, next_state;
always @(*) begin
// State transition logic
case (state)
LEFT: next_state <= bump_left ? RIGHT : LEFT;
RIGHT: next_state <= bump_right ? LEFT : RIGHT;
endcase
end
always @(posedge clk, posedge areset) begin
// State flip-flops with asynchronous reset
if(areset) begin
state <= LEFT;
end else begin
state <= next_state;
end
end
// Output logic
assign walk_left = (state == LEFT);
assign walk_right = (state == RIGHT);
endmodule
- 旅鼠2
除了向左和向右走,如果地面消失了旅鼠会下落并大喊aaah!.
当ground=0时,除了左右行走和改变方向外,旅鼠还会跌倒并说“aaah!”.当地面重新出现(ground=1)时,旅鼠将恢复以与坠落前相同的方向行走.下落时被撞击不影响行走方向,与地面消失同一周期被撞击(但尚未坠落),或下落时地面再次出现,也不影响行走方向.
建立一个模拟这种行为的有限状态机.
- Module Declaraction
module top_module(
input clk,
input areset, // Freshly brainwashed Lemmings walk left.
input bump_left,
input bump_right,
input ground,
output walk_left,
output walk_right,
output aaah );
- Solution
module top_module(
input clk,
input areset,
input bump_left,
input bump_right,
input ground,
output walk_left,
output walk_right,
output aaah
);
parameter LEFT = 2'd0, LEFT_GROUND = 2'd1, RIGHT = 2'd2, RIGHT_GROUND = 2'd3;
reg [1:0] curr_dir, next_dir;
always @(posedge clk or posedge areset) begin
// Freshly brainwashed Lemmings walk left.
if (areset) begin
curr_dir <= LEFT;
end
else begin
curr_dir <= next_dir;
end
end
always @(*) begin
case (curr_dir)
LEFT: begin
if (ground) begin
next_dir = bump_left ? RIGHT : LEFT;
end
else begin
next_dir = LEFT_GROUND;
end
end
RIGHT: begin
if (ground) begin
next_dir = bump_right ? LEFT : RIGHT;
end
else begin
next_dir = RIGHT_GROUND;
end
end
LEFT_GROUND: begin
if (ground) begin
next_dir = LEFT;
end
else begin
next_dir = LEFT_GROUND;
end
end
RIGHT_GROUND: begin
if (ground) begin
next_dir = RIGHT;
end
else begin
next_dir = RIGHT_GROUND;
end
end
endcase
end
assign walk_left = curr_dir == LEFT;
assign walk_right = curr_dir == RIGHT;
assign aaah = (curr_dir == LEFT_GROUND) || (curr_dir == RIGHT_GROUND);
endmodule
- 旅鼠3
除了行走和摔倒之外,有时还可以告诉旅鼠做一些有用的事情,比如挖洞(dig=1时开始挖洞).如果一只旅鼠正在地面上行走(ground=1),它可以继续挖掘直到到达另一边(ground=0).在那一点上,由于没有地面,它会掉下来(aaah!),然后在它再次落地后继续沿着它原来的方向行走.和坠落一样,在挖掘时被撞击没有效果,在坠落或没有地面时被告知挖掘是被忽略的.
换言之,一个行尸走肉的旅鼠可能会摔倒、dig或改变方向.如果满足这些条件中的一个以上,则fall的优先级高于dig,dig的优先级高于切换方向.
扩展有限状态机来模拟这种行为.
- Module Declaration
module top_module(
input clk,
input areset, // Freshly brainwashed Lemmings walk left.
input bump_left,
input bump_right,
input ground,
input dig,
output walk_left,
output walk_right,
output aaah,
output digging );
- Solution
module top_module(
input clk,
input areset, // Freshly brainwashed Lemmings walk left.
input bump_left,
input bump_right,
input ground,
input dig,
output walk_left,
output walk_right,
output aaah,
output digging );
reg [2:0] state, next_state;
parameter LEFT=0, RIGHT=1, LEFT_GROUND=3, RIGHT_GROUND=4, DIG_LEFT=5, DIG_RIGHT=6;
always @(*) begin
case (state)
LEFT: begin
if(ground) begin
if(dig) begin
next_state <= DIG_LEFT;
end else begin
next_state <= bump_left ? RIGHT : LEFT;
end
end else begin
next_state <= LEFT_GROUND;
end
end
RIGHT: begin
if(ground) begin
if(dig) begin
next_state <= DIG_RIGHT;
end else begin
next_state <= bump_right ? LEFT : RIGHT;
end
end else begin
next_state <= RIGHT_GROUND;
end
end
LEFT_GROUND: begin
if(ground) begin
next_state <= LEFT;
end else begin
next_state <= LEFT_GROUND;
end
end
RIGHT_GROUND: begin
if(ground) begin
next_state <= RIGHT;
end else begin
next_state <= RIGHT_GROUND;
end
end
DIG_LEFT: begin
if(ground) begin
next_state <= DIG_LEFT;
end else begin
next_state <= LEFT_GROUND;
end
end
DIG_RIGHT: begin
if(ground) begin
next_state <= DIG_RIGHT;
end else begin
next_state <= RIGHT_GROUND;
end
end
endcase
end
always @(posedge clk or posedge areset) begin
if( areset) begin
state <= LEFT;
end else begin
state <= next_state;
end
end
assign walk_left = (state == LEFT);
assign walk_right = (state == RIGHT);
assign aaah = (state == RIGHT_GROUND)||(state == LEFT_GROUND);
assign digging = (state == DIG_LEFT)||(state == DIG_RIGHT);
endmodule