Basically, a FSM consists fo three parts: Combinational logic, Sequential logic (flip-flops), Output logic. Purposes of these three parts are:
Combinational logic - to decide the next state of a FSM
Sequential logic - to store the state of a FSM
Output logic - to specify data processing based on the cuurent state of FSM and in the implementation sense is a mix of the combinational and the sequential logic.
The best way is to separate these parts from each other. But if a FSM is simple, it is possible to combine either the Combinational and the Sequential parts or even all three parts in a single always-block.
Example 1:
reg [1:0] state, state_next;
localparam StIdle = 2'b00;
localparam StWait = 2'b01;
localparam StShift = 2'b10;
localparam lpBufferWidth = 1 + pDataWidth + pParity;
reg [lpBufferWidth-1:0] buffer;
localparam lpPacketWidth = lpBufferWidth + pStopBits;
reg [$clog2(lpPacketWidth)-1:0] BitCnt;
reg [15:0] BitCellCnt;
reg done = 0;
assign oDone = done & ~en;
// Combinational logic
always @(*) begin
state_next = state;
if (rst) begin
state_next = StIdle;
end
case(state)
StIdle:begin
if (en) state_next = StWait;
end
StWait:begin
if (BitCellCnt == lpBitCellWidth)
state_next = StShift;
end
StShift:begin
if (BitCnt == lpPacketWidth-1)
state_next = StIdle;
else
state_next = StWait;
end
default: begin
state_next = StIdle;
end
endcase
end
// Sequential logic
always @(posedge clk) state <= state_next;
// Output logic
always @(posedge rst, posedge clk)
if (rst) begin
BitCnt <= 0;
BitCellCnt <= 0;
buffer <= {lpBufferWidth{1'b1}};
done <= 1;
end
else begin
case(state)
default: begin
state <= StIdle;
end
StIdle:begin
BitCnt <= 0;
BitCellCnt <= 0;
done <= 1;
if (iEnStr) begin
buffer <= load(parity);
done <= 0;
end
end
StWait:begin
if (BitCellCnt == lpBitCellWidth)
BitCellCnt <= 0;
else
BitCellCnt <= BitCellCnt + 1;
end
StShift:begin
buffer <= {1'b1, buffer[lpBufferWidth-1:1]};
if (BitCnt == lpPacketWidth-1) begin
BitCnt <= 0;
done <= 1;
end else
BitCnt <= BitCnt + 1;
end
endcase
end
function [lpBufferWidth - 1:0] load (input integer parity);
begin
if (parity) load = {^data, data, 1'b0};
else load = {data, 1'b0};
end
endfunction
Example 2:
reg [1:0] state;
localparam StIdle = 2'b00;
localparam StWait = 2'b01;
localparam StShift = 2'b10;
localparam lpBufferWidth = 1 + pDataWidth + pParity;
reg [lpBufferWidth-1:0] buffer;
localparam lpPacketWidth = lpBufferWidth + pStopBits;
reg [$clog2(lpPacketWidth)-1:0] BitCnt;
reg [15:0] BitCellCnt;
reg done = 0;
assign oDone = done & ~en;
always @(posedge rst, posedge clk)
if (rst) begin
BitCnt <= 0;
BitCellCnt <= 0;
buffer <= {lpBufferWidth{1'b1}};
done <= 1;
state <= StIdle;
end
else begin
case(state)
StIdle:begin
BitCnt <= 0;
BitCellCnt <= 0;
done <= 1;
if (en) begin
buffer <= load(pParity);
done <= 0;
state <= StWait;
end
end
StWait:begin
if (BitCellCnt == lpBitCellWidth) begin
BitCellCnt <= 0;
state <= StShift;
end else
BitCellCnt <= BitCellCnt + 1;
end
StShift:begin
buffer <= {1'b1, buffer[lpBufferWidth-1:1]};
if (BitCnt == lpPacketWidth-1) begin
BitCnt <= 0;
done <= 1;
state <= StIdle;
end else begin
BitCnt <= BitCnt + 1;
state <= StWait;
end
end
default: begin
state <= StIdle;
end
endcase
end
function [lpBufferWidth - 1:0] load (input integer parity);
begin
if (parity) load = {^iData, iData, 1'b0};
else load = {iData, 1'b0};
end
endfunction