在使用之前无需重置设备

时间:2014-02-09 02:24:30

标签: simulation verilog

这里有愚蠢的问题。我在为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

这是工作刺激的模拟结果:

enter image description here

现在,当我将重置设置为零时,如果没有将它设置为高,则会发生以下情况:

enter image description here

显然,我正在使用重置线将控制器置于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;'删除注释的代码模拟:

enter image description here

它进入了与上面列出的不同的状态,我不知道为什么。 所以我想知道:

  1. 为什么它会进入未知状态,为什么我的未注释代码不起作用?
  2. 我能改变什么才能按照我的意愿工作?

1 个答案:

答案 0 :(得分:1)

您的问题是,如果没有重置或初始值,statenext_state将为X。分配给statename的案例陈述将采用默认分支并解码为???。由于您指定next_state的流程无法处理stateX的情况,因此会永远陷入此状态。

您尝试解决此问题无效:

state <= S_IDLE;
if(reset) state <= S_IDLE;
else state <= next_state;

当重置为低时,您要对state进行两次分配,第一次分配为S_IDLE,第二次分配为next_state。与toolic's answer相反,这是竞争条件。 Verilog标准规定:

  

非阻塞分配应按语句执行的顺序执行。

由于流程中的顺序语句不会重新排序事件队列,因此转换为最后一次分配胜利。因此,state <= S_IDLE;被有效优化,因为无论重置值如何,都会覆盖分配。

有两种方法可以解决这个问题,因此您无需重置:

1。使用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。

2。初始化变量

reg [2:0] state = S_IDLE;

对于某些合成目标(例如FPGA),这会将寄存器初始化为特定值,并且可以与复位一起使用或代替复位(请参阅Altera Documentation上电值)。

一些一般观点:

  • 根据您的综合工具,最好使用枚举,而不是明确定义状态值。这允许工具基于整体设计进行优化或使用编码的全局配置(例如安全,一热)。

  • 使用复位寄存器保持状态是标准做法,因此您应该仔细考虑确实是否要避免使用复位。