Search code examples
compilationverilogsystem-verilogquartus

always_comb construct does not infer purely combinational logic


I have this FSM that controls different events in a game I'm building (in Quartus Prime Lite Edition 17.0). When I try to compile it, I get this error:

Error (10166): SystemVerilog RTL Coding error at GameFSM.sv(40): always_comb construct does not infer purely combinational logic.

I have all the outputs assigned as default values, so no latches should be inferred I believe (I don't totally understand how it works).

//Game FSM, controls the game events

module GameFSM (
    input logic clk, resetN, NPCCollisions, Dead_NPCs, start_game, pause_game, continue_game, delay_indicator,
    
    output logic Message_main_menu,
    output logic Message_pause,
    output logic Message_win,
    output logic Message_lose,
    output logic Display_Main_Menu,
    output logic MainMenu_Music,
    output logic BackGround_Music, 
    output logic Paused,
    output logic Win_Music,
    output logic Lose_Music
   );

enum logic [2:0] {main_Menu, three_hearts, two_hearts, one_heart, delay_state, pause, win, lose} prState, nxtState;

logic [1:0] return_state;
    
always @(posedge clk or negedge resetN)
  begin
       
    if ( !resetN ) begin // Asynchronic reset
        prState <= main_Menu;
    end
        
    else    begin   // Synchronic logic FSM
        prState <= nxtState;
    end
        
end // always

always_comb // Update next state and outputs
    begin
        nxtState = prState; // default values 
        Message_main_menu = 1'b0;
        Message_pause = 1'b0;
        Message_win = 1'b0;
        Message_lose = 1'b0;
        MainMenu_Music = 1'b0;
        BackGround_Music = 1'b0;
        Paused = 1'b0;
        Win_Music = 1'b0;
        Lose_Music = 1'b0;
        Display_Main_Menu = 1'b0;
            
        case (prState)
                
            main_Menu: begin
                Message_main_menu = 1'b1;
                MainMenu_Music = 1'b1;
                Display_Main_Menu = 1'b1;
                if(start_game)
                    nxtState = three_hearts; 
            end
            
            three_hearts: begin
                BackGround_Music = 1'b1;
                return_state = 2'h3;
                if(NPCCollisions) //Corony hits an npc while he has 3 hearts left
                    nxtState = delay_state;
                else if(Dead_NPCs) //all npc's are dead
                    nxtState = win;
                else if(pause_game)
                    nxtState = pause;
            end
            
            two_hearts: begin
                BackGround_Music = 1'b1;
                return_state = 2'h2;
                if(NPCCollisions) //Corony hits an npc while he has 2 hearts left
                    nxtState = delay_state;
                else if(Dead_NPCs) //all npc's are dead
                    nxtState = win;
                else if(pause_game)
                    nxtState = pause; 
            end
            
            one_heart: begin
                BackGround_Music = 1'b1;
                return_state = 2'h1;
                if(NPCCollisions) //Corony lost all his hearts
                    nxtState = lose;
                else if(Dead_NPCs) //all npc's are dead
                    nxtState = win;
                else if(pause_game)
                    nxtState = pause;
            end
            
            delay_state: begin
                if(delay_indicator) begin
                    if(return_state == 2'h3)
                        nxtState = two_hearts;
                    else if(return_state == 2'h2)
                        nxtState = one_heart;
                end
            end
            
            pause: begin
                Paused = 1'b1;
                Message_pause = 1'b1;
                if(continue_game) begin
                    if(return_state == 2'h3)
                        nxtState = three_hearts;
                    else if(return_state == 2'h2)
                        nxtState = two_hearts;
                    else
                        nxtState = one_heart;
                end
            end
            
            win: begin
                Win_Music = 1'b1;
                Message_win = 1'b1;
            end
                        
            lose: begin
                Lose_Music = 1'b1;
                Message_lose = 1'b1;
                end 
                
            
                
            endcase
    end // always comb
    
endmodule

return_state is used so I can know which state delivered me to the pause state, so when I want to leave the pause state, I'd know where I should return to.


Solution

  • The problem is that you read and assign to the counter signal in side the always_comb block:

                    counter = counter - 1;
    

    This can create a combinational feedback loop.

    Similarly for:

                    counter = DELAY_TIME;
    

    Perhaps you should use sequential logic for counter: always @(posedge clk ...)

    Another problem signal might be return_state. I'm not sure why my simulator doesn't complain about this one. I think return_state should be assigned in its own sequential logic block as well.