Lets talk about quadrature decoders and how to create one with verilog HDL. As with most things programming related there is more than one way to accomplish this. Some implementations are smaller than others, however some more concise implementations can be hard to understand when reading the code. You can make them with the bare minimum of just a register to hold the count value or more sophisticated by adding current speed as well and pretty much whatever you like. For now I just need something simple that can count the edges of the signal, either up or down depending on the direction of travel.

I will be using this quadrature decoder for controlling a robot so it needs to have no errors when direction reversal happens.

The quadrature decoder needs to be able to correctly handle all eight possible direction reversal cases, shown below, without error.

Quadrature Direction Reversal A
Quadrature Direction Reversal B
Quadrature Direction Reversal C
Quadrature Direction Reversal D
Quadrature Direction Reversal E
Quadrature Direction Reversal F
Quadrature Direction Reversal G
Quadrature Direction Reversal H

Quadrature Decoder Verilog

Below we will be looking at two examples of creating a quadrature decoder with Verilog. The first uses a Finiste State Machine based approach and the second is size optimized, however as you will see the size optimized implementation is hard to understand when looking at the Verilog.

FSM based Quadrature Decoder

We can quickly draw up a state machine to visualize how the states relate to each other. Thankfully as far as state machines go this one is simple. By the very nature of how the A&B channels relate to each other each state has only two possible paths and the next-state to which we change also points to the direction of rotation.

Quadrature Decoder State Machine

You can read a very usefull paper from Xilinx on the Mealy and Moore state machines and how to implement them here Moore and Mealy FSM.

Ok, enough talking, here is a quadrature decoder with syncronous reset that uses an FSM based approach. The states are represented as S00, S11, S01 and S10. It uses 64 LUTS and 38 Registers on a Spartan 7.

module Quad( input clk, input reset, input A, input B, output reg signed [31:0] count ); (*ASYNC_REG="TRUE"*)reg[1:0] sync, AB; // synchronization registers reg[1:0] state; localparam S00=2'b00, S01=2'b01, S10=2'b10, S11=2'b11; // states always @ (posedge clk) // two-stage input synchronizer begin sync <= {A,B}; AB <= sync; end always @(posedge clk) // always block to compute output begin if(reset) begin state <= S00; count <= 0; end else case(state) S00: if(AB == 2'b01) begin count <= count-1; state <= S01; end else if(AB == 2'b10) begin count <= count+1; state <= S10; end S01: if(AB == 2'b00) begin count <= count+1; state <= S00; end else if(AB == 2'b11) begin count <= count-1; state <= S11; end S10: if(AB == 2'b00) begin count <= count-1; state <= S00; end else if(AB == 2'b11) begin count <= count+1; state <= S11; end S11: if(AB == 2'b01) begin count <= count+1; state <= S01; end else if(AB == 2'b10) begin count <= count-1; state <= S10; end endcase end endmodule
Code language: Verilog (verilog)

And here is the simulation waveform for this quadrature decoder module showing all direction reversals being handled correctly.

Quadrature Decoder Direction Reversal Waveform

Compact Quadrature Decoder

We can accomplish the same thing with this more compact version, this implementation uses nearly 50% less LUTS at 36 LUTS and 38 Registers.

module Quad_compact( input clk, input A, input B, input reset, output reg signed[31:0] count); (*ASYNC_REG="TRUE"*)reg[1:0] sync, AB; // synchronization registers reg[1:0] os; // old state wire[1:0] tmp, ns/*new state*/; always @(posedge clk) // two-stage input synchronizer begin sync <= {A,B}; AB <= sync; end // OR signal[0] with signal[1] - creates pulses on every edge of quad signal assign tmp = {AB[1],AB[1] ^ AB[0]}; assign ns = tmp - os; // Get new state always @(posedge clk) begin if(reset) begin count <= 0; os <= 0; end else if(ns[0] == 1'b1) begin os = os + ns; // set current state to old state if (ns[1] == 1'b1) begin count <= count - 1'b1; end else begin count <= count + 1'b1; end end end endmodule
Code language: Verilog (verilog)

In a future post we will look at adding some more functionality and also how to prepare this module to be used in the Vivado IP integrator.

Leave a Reply