For a project, I have to determine the RPM of a turning resolver. I can correctly read the angular position of the resolver as as a hex number via an RDC. This number is then converted into a 14-bit decimal number and then sent to a 7-segment display. To give an idea how this works, when the resolver is at 180 degrees, the RDC will return 0x8000, when it is at 359 degrees, the RDC will return 0xFFFF, and at 0 degrees, the RDC will return 0x0000. The separate module then converts the 16-bit values to a 14-bit value.

I am calculating the RPM in its own Verilog module called RPM_calc. The angular position of the resolver is being read at 1000Hz. Inside RPM_calc, I have clock that samples the position 4 times a second every posedge. Every time this posedge hits, it reads the position data being fed in and moves the previous position into a different register. These two registers are subtracted, and the position difference over time (0.25s) is then used to calculate the RPM. The RPM direction is not displayed, and if the direction results in a negative number, it is converted from 2s complement to a regular number. The res input just determines whether I want a 10 or 12-bit resolution from the RDC. My verilog code is shown below.

module RPM_calc(
input clk_i,                //1000Hz sample clock
input res,
input [13:0] position_i,
output [13:0] RPM_o,
output sample_o

reg [8:0] clk_4hz_counter = 0;
reg clk_4hz = 0;

reg [13:0] position_reg;
reg [13:0] prev_position_reg = 0;

always@(posedge clk_i) begin  //4Hz clock generator
    clk_4hz_counter <= clk_4hz_counter + 1;
    if(clk_4hz_counter == 125) begin
        clk_4hz_counter <= 0;
        clk_4hz = ~clk_4hz;

always@(posedge clk_4hz) begin                    
    position_reg <= position_i;
    prev_position_reg <= position_reg; 
//2s complement wires
wire [13:0] deltaP;
wire [13:0] abs_deltaP;

assign deltaP = position_reg-prev_position_reg;
assign abs_deltaP = (deltaP[13])?(~deltaP+1):(deltaP);

//if res is 0, divide by 2^12, else divide by 2^10. 240 = 60*(4Hz)
assign RPM_o = res? 240*(abs_deltaP)/1024: 240*(abs_deltaP)/4096; 
assign sample_o = clk_i; 


The problem I ran into is when the RDC rolls over at 360 degrees. For example, if I sample at 350 degrees, and 0.25s later, I sample at 10 degrees when travelling clockwise, my module thinks I traveled 340 degrees in 0.25s, which is incorrect. I need a way of determining what the correct RPM is when two samples happen with a rollover in between. Does anyone have an idea of how to do this?


  • \$\begingroup\$ Can we assume that the travel is never more than 180 degrees in 250 ms? \$\endgroup\$
    – The Photon
    Commented Apr 15, 2019 at 17:56
  • \$\begingroup\$ @ThePhoton Unfortunately, we can't assume that. \$\endgroup\$
    – SD'Anc
    Commented Apr 15, 2019 at 22:44
  • \$\begingroup\$ Then you'll need to sample the position more often then every 250 ms. What's the maximum sampling interval that allows you to assume no more than 180 degrees travel since the previous sample? \$\endgroup\$
    – The Photon
    Commented Apr 15, 2019 at 23:09
  • \$\begingroup\$ I could sample at 16Hz and safely assume that. \$\endgroup\$
    – SD'Anc
    Commented Apr 15, 2019 at 23:33

1 Answer 1


First, it's not good practice to generate a clock with logic in an FPGA. Instead of generating a 4 Hz clock, generate a 4 Hz strobe, and use it to enable logic that's clocked by the 1000 Hz clock:

reg [7:0] ctr_4Hz;
reg strobe_4Hz;
always @(posedge clk_i) begin
    if(ctr_4Hz == 250) begin
        ctr_4Hz <= 0;
        strobe_4Hz <= 1;
    else begin
        ctr_4Hz <= ctr_4Hz + 1;
        strobe_4Hz <= 0;

always@(posedge clk_i) begin
    if(strobe_4Hz) begin
        position_reg <= position_i;
        prev_position_reg <= position_reg; 

// etc

To solve your actual problem, the easiest way is to assume there can't be more than 180 degrees of travel in 250 ms. If you can make this assumption (because of the mechanics of the system, for example), then just keep track of whether the msb of your position register changes. If the old msb was high and the new msb is low, add 360 degrees (or 0x10000 binary angle units) to the new position before calculating deltaP.

If you need to deal with motion in both directions, then you'll need to keep track of which quadrant the old and new positions are in (this depends only on the 2 msb's of the binary angle) and only use special treatment for transitions from IV to I quadrant or I to IV quadrant (add 0x10000 to whichever position was in the I quadrant).


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.