Write in front
Before I prepared to write some simple verilog tutorials, I referred to many materials–this set of verilog tutorials on the Asic-World website is one of them. This set of tutorials is very well written, but it is not in Chinese, so I have to boldly translate it (adding my own understanding) and share it with everyone.
This is the original text of the website: Verilog Tutorial
This is the series navigation: Verilog tutorial series article navigation
Introduction to Finite State Machine FSM
State machines (FSM) are at the heart of many digital designs. Counters are a simple form of FSM. When I was learning Verilog, I used to wonder “how to code a FSM in Verilog” and “what is the best way to code it”. Next I will try to answer these two questions.
State machine type
There are two types of state machines classified according to their output type. The first is a Moore state machine, whose output is only a function of the current state; the second is a Mealy state machine, whose output is a function of the current state and the input.
Moore State Machine
Mealy state machine
State machines can also be classified based on the state encoding used. Encoding type is also a key factor in determining the speed and complexity of FSM. Binary encoding, Gray code, and one-hot code are the most common FSM state encoding types.
State machine modeling
One thing to remember when coding an FSM: combinational logic and sequential logic should be in two different always blocks. In the above two figures, next state logic is always combinational logic; while state logic and output logic are sequential logic. Any asynchronous signals to the next state logic must be synchronized before being fed to the FSM. You should try to keep the state machine FSM in a separate Verilog file.
Using constant declarations such as parameter or `define to define the state of the FSM makes the code more readable and easier to manage.
Example: Arbiter
Next, we will use the arbiter as a case to see how to implement a complete FSM.
The FSM code should contain three parts:
- Status coding part
- Combinational logic part
- Sequential logic part
Status coding
There are many ways to encode status, the most commonly used are:
- Binary Encoding
- One Hot Encoding
- Gray Encoding
One Hot Encoding
parameter [4:0] IDLE = 5'b0_0001; parameter [4:0] GNT0 = 5'b0_0010; parameter [4:0] GNT1 = 5'b0_0100; parameter [4:0] GNT2 = 5'b0_1000; parameter [4:0] GNT3 = 5'b1_0000;
Binary Encoding
parameter [2:0] IDLE = 3'b000; parameter [2:0] GNT0 = 3'b001; parameter [2:0] GNT1 = 3'b010; parameter [2:0] GNT2 = 3'b011; parameter [2:0] GNT3 = 3'b100;
Gray Encoding
parameter [2:0] IDLE = 3'b000; parameter [2:0] GNT0 = 3'b001; parameter [2:0] GNT1 = 3'b011; parameter [2:0] GNT2 = 3'b010; parameter [2:0] GNT3 = 3'b110;
Combination logic part
This part can be implemented using functions, assign statements or always blocks.
always @ (state or req_0 or req_1 or req_2 or req_3) begin next_state = 0; case(state) IDLE: if (req_0 == 1'b1) begin next_state = GNT0; end else if (req_1 == 1'b1) begin next_state= GNT1; end else if (req_2 == 1'b1) begin next_state= GNT2; end else if (req_3 == 1'b1) begin next_state= GNT3; end else begin next_state = IDLE; end GNT0: if (req_0 == 1'b0) begin next_state = IDLE; end else begin next_state = GNT0; end GNT1: if (req_1 == 1'b0) begin next_state = IDLE; end else begin next_state = GNT1; end GNT2: if (req_2 == 1'b0) begin next_state = IDLE; end else begin next_state = GNT2; end GNT3: if (req_3 == 1'b0) begin next_state = IDLE; end else begin next_state = GNT3; end default : next_state = IDLE; endcase end
Sequential logic part
This part can only be implemented using edge-sensitive logic such as the always block with posedge clock or negedge clock.
always @ (posedge clock) begin : OUTPUT_LOGIC if (reset == 1'b1) begin gnt_0 <= #1 1'b0; gnt_1 <= #1 1'b0; gnt_2 <= #1 1'b0; gnt_3 <= #1 1'b0; state <= #1 IDLE; end else begin state <= #1 next_state; case(state) IDLE: begin gnt_0 <= #1 1'b0; gnt_1 <= #1 1'b0; gnt_2 <= #1 1'b0; gnt_3 <= #1 1'b0; end GNT0: begin gnt_0 <= #1 1'b1; end GNT1: begin gnt_1 <= #1 1'b1; end GNT2: begin gnt_2 <= #1 1'b1; end GNT3: begin gnt_3 <= #1 1'b1; end default: begin state <= #1 IDLE; end endcase end end
Complete FSM code using binary encoding
module fsm_full( clock , // Clock reset, // Active high reset req_0 , // Active high request from agent 0 req_1 , // Active high request from agent 1 req_2 , // Active high request from agent 2 req_3 , // Active high request from agent 3 gnt_0 , // Active high grant to agent 0 gnt_1 , // Active high grant to agent 1 gnt_2 , // Active high grant to agent 2 gnt_3 // Active high grant to agent 3 ); // Port declaration here input clock; // Clock input reset; // Active high reset input req_0; // Active high request from agent 0 input req_1; // Active high request from agent 1 input req_2; // Active high request from agent 2 input req_3; // Active high request from agent 3 output gnt_0; // Active high grant to agent 0 output gnt_1; // Active high grant to agent 1 output gnt_2; // Active high grant to agent 2 output gnt_3; // Active high grant to agent // Internal Variables reg gnt_0; // Active high grant to agent 0 reg gnt_1; // Active high grant to agent 1 reg gnt_2; // Active high grant to agent 2 reg gnt_3; // Active high grant to agent parameter [2:0] IDLE = 3'b000; parameter [2:0] GNT0 = 3'b001; parameter [2:0] GNT1 = 3'b010; parameter [2:0] GNT2 = 3'b011; parameter [2:0] GNT3 = 3'b100; reg [2:0] state, next_state; always @ (state or req_0 or req_1 or req_2 or req_3) begin next_state = 0; case(state) IDLE: if (req_0 == 1'b1) begin next_state = GNT0; end else if (req_1 == 1'b1) begin next_state= GNT1; end else if (req_2 == 1'b1) begin next_state= GNT2; end else if (req_3 == 1'b1) begin next_state= GNT3; end else begin next_state = IDLE; end GNT0: if (req_0 == 1'b0) begin next_state = IDLE; end else begin next_state = GNT0; end GNT1: if (req_1 == 1'b0) begin next_state = IDLE; end else begin next_state = GNT1; end GNT2: if (req_2 == 1'b0) begin next_state = IDLE; end else begin next_state = GNT2; end GNT3: if (req_3 == 1'b0) begin next_state = IDLE; end else begin next_state = GNT3; end default : next_state = IDLE; endcase end always @ (posedge clock) begin : OUTPUT_LOGIC if (reset) begin gnt_0 <= #1 1'b0; gnt_1 <= #1 1'b0; gnt_2 <= #1 1'b0; gnt_3 <= #1 1'b0; state <= #1 IDLE; end else begin state <= #1 next_state; case(state) IDLE: begin gnt_0 <= #1 1'b0; gnt_1 <= #1 1'b0; gnt_2 <= #1 1'b0; gnt_3 <= #1 1'b0; end GNT0: begin gnt_0 <= #1 1'b1; end GNT1: begin gnt_1 <= #1 1'b1; end GNT2: begin gnt_2 <= #1 1'b1; end GNT3: begin gnt_3 <= #1 1'b1; end default: begin state <= #1 IDLE; end endcase end end endmodule
Test script
`include "fsm_full.v" module fsm_full_tb(); reg clock , reset ; reg req_0 , req_1 , req_2 , req_3; wire gnt_0 , gnt_1 , gnt_2 , gnt_3 ; initial begin $display("Time\t R0 R1 R2 R3 G0 G1 G2 G3"); $monitor("%g\t %b %b %b %b %b %b %b %b", $time, req_0, req_1, req_2, req_3, gnt_0, gnt_1, gnt_2, gnt_3); clock = 0; reset = 0; req_0 = 0; req_1 = 0; req_2 = 0; req_3 = 0; #10 reset = 1; #10 reset = 0; #10 req_0 = 1; #20 req_0 = 0; #10 req_1 = 1; #20 req_1 = 0; #10 req_2 = 1; #20 req_2 = 0; #10 req_3 = 1; #20 req_3 = 0; #10 $finish; end always #2 clock = ~clock; fsm_full U_fsm_full( clock , // Clock reset, // Active high reset req_0 , // Active high request from agent 0 req_1 , // Active high request from agent 1 req_2 , // Active high request from agent 2 req_3 , // Active high request from agent 3 gnt_0 , // Active high grant to agent 0 gnt_1 , // Active high grant to agent 1 gnt_2 , // Active high grant to agent 2 gnt_3 // Active high grant to agent 3 ); endmodule
Simulation results
- If you have any questions, you can communicate with me in the comment area!
- This article was originally created by Lonely Dandao and was first published on the CSDN platform, blog homepage: wuzhikai.blog.csdn.net
- Your support is my biggest motivation to continue creating! If this article is helpful to you, please like it, comment it and collect it!