这里有愚蠢的问题。我在为8位乘法器实现控制器块时遇到了麻烦。它正常工作,但只有当我打开复位线,然后关闭,例如在下面的刺激中(工作正常):
`timescale 1ns / 100ps
module Controller_tb(
);
reg reset;
reg START;
reg clk;
reg LSB;
wire STOP;
wire ADD_cmd;
wire SHIFT_cmd;
wire LOAD_cmd;
Controller dut (.reset(reset),
.START(START),
.clk(clk),
.LSB(LSB),
.STOP(STOP),
.ADD_cmd(ADD_cmd),
.SHIFT_cmd(SHIFT_cmd),
.LOAD_cmd(LOAD_cmd)
);
always
begin
clk <= 0;
#25;
clk <= 1;
#25;
end
initial
begin
LSB <= 0;
START <= 0;
reset <= 1;
#55;
reset <= 0;
#10;
START <= 1;
#100;
START <= 0;
LSB <= 1;
#200;
#20;
#100;
end
initial
$monitor ("stop,shift_cmd,load_cmd, add_cmd: " , STOP,SHIFT_cmd,LOAD_cmd,ADD_cmd);
endmodule
这是工作刺激的模拟结果:
现在,当我将重置设置为零时,如果没有将它设置为高,则会发生以下情况:
显然,我正在使用重置线将控制器置于IDLE状态。这是控制器块的代码:
`timescale 1ns / 1ps
module Controller(
input reset,
input START,
output STOP,
input clk,
input LSB,
output ADD_cmd,
output SHIFT_cmd,
output LOAD_cmd
);
//Five states:
//IDLE : 000 , INIT: 001, TEST: 011, ADD: 010, SHIFT: 110
localparam [2:0] S_IDLE = 0;
localparam [2:0] S_INIT = 1;
localparam [2:0] S_TEST = 2;
localparam [2:0] S_ADD = 3;
localparam [2:0] S_SHIFT = 4;
reg [2:0] state,next_state;
reg [3:0] count;
// didn't assign the outputs to wire.. if not work, check this.
assign ADD_cmd = (state == S_ADD);
assign SHIFT_cmd = (state == S_SHIFT);
assign LOAD_cmd = (state == S_INIT);
assign STOP = (state == S_IDLE);
always @(*) begin
case(state)
S_INIT: begin
count = 3'b000;
end
S_SHIFT: begin
count = count + 1;
end
endcase
end
always @(*)
begin
next_state = state;
case (state)
S_IDLE: next_state = START ? S_INIT : S_IDLE;
S_INIT: next_state = S_TEST;
S_TEST: next_state = LSB ? S_ADD : S_SHIFT;
S_ADD: next_state = S_SHIFT;
S_SHIFT: next_state = (count == 8) ? S_IDLE : S_TEST;
endcase
end
always @(posedge clk)
begin
//state <= S_IDLE;
if(reset) state <= S_IDLE;
else state <= next_state;
end
reg [8*6-1:0] statename;
always @* begin
case( state )
S_IDLE: statename <= "IDLE";
S_INIT: statename <= "INIT";
S_TEST: statename <= "TEST";
S_ADD: statename <= "ADD";
S_SHIFT: statename <= "SHIFT";
default: statename <= "???";
endcase
end
endmodule
问题是,我不知道如何解决这个问题。从上面的代码中可以看出,有一个注释部分基本上总是将状态初始化为IDLE。但即使这样也行不通。以下是从'//状态&lt; = S_IDLE;'删除注释的代码模拟:
它进入了与上面列出的不同的状态,我不知道为什么。 所以我想知道:
答案 0 :(得分:1)
您的问题是,如果没有重置或初始值,state
和next_state
将为X
。分配给statename
的案例陈述将采用默认分支并解码为???
。由于您指定next_state
的流程无法处理state
为X
的情况,因此会永远陷入此状态。
您尝试解决此问题无效:
state <= S_IDLE;
if(reset) state <= S_IDLE;
else state <= next_state;
当重置为低时,您要对state
进行两次分配,第一次分配为S_IDLE
,第二次分配为next_state
。与toolic's answer相反,这是不竞争条件。 Verilog标准规定:
非阻塞分配应按语句执行的顺序执行。
由于流程中的顺序语句不会重新排序事件队列,因此转换为最后一次分配胜利。因此,state <= S_IDLE;
被有效优化,因为无论重置值如何,都会覆盖分配。
有两种方法可以解决这个问题,因此您无需重置:
default
子句使状态机安全always @(*)
begin
next_state = state;
case (state)
S_IDLE: next_state = START ? S_INIT : S_IDLE;
S_INIT: next_state = S_TEST;
S_TEST: next_state = LSB ? S_ADD : S_SHIFT;
S_ADD: next_state = S_SHIFT;
S_SHIFT: next_state = (count == 8) ? S_IDLE : S_TEST;
default: next_state = S_IDLE;
endcase
end
如果state是非编码值(包括X
),这将确保您的状态机“安全”并进入S_IDLE。
reg [2:0] state = S_IDLE;
对于某些合成目标(例如FPGA),这会将寄存器初始化为特定值,并且可以与复位一起使用或代替复位(请参阅Altera Documentation上电值)。
一些一般观点:
根据您的综合工具,最好使用枚举,而不是明确定义状态值。这允许工具基于整体设计进行优化或使用编码的全局配置(例如安全,一热)。
使用复位寄存器保持状态是标准做法,因此您应该仔细考虑确实是否要避免使用复位。