2
\$\begingroup\$

When I try to synthesize the following Verilog code using Xilinx XST, I get the error, "Unsupported real constant". If I try wrapping that expression in an $rtoi function, XST gives a different error: "Unsupported System Function Call".

Using the Xilinx synthesis tools, is it possible to cast a real constant to an integer constant? If so, how?

module example(clk, n_rst, tick, done);
 parameter CLOCK_HZ = 50_000_000;
 parameter BAUD_RATE = 3_000_000;
 input clk, n_rst;
 output reg tick, done;
 reg [31:0] counter;
 always @(posedge clk, negedge n_rst) begin
 if (!n_rst) begin
 counter <= 32'h00000000;
 tick <= 0;
 done <= 0;
 end
 else if (counter == (0.5*CLOCK_HZ/BAUD_RATE) || // ERROR:Xst:850 - Unsupported real constant
 counter == (1.5*CLOCK_HZ/BAUD_RATE) || // ERROR:Xst:850 - Unsupported real constant
 counter == (2.5*CLOCK_HZ/BAUD_RATE) || // ERROR:Xst:850 - Unsupported real constant
 counter == (3.5*CLOCK_HZ/BAUD_RATE) || // ERROR:Xst:850 - Unsupported real constant
 counter == (4.5*CLOCK_HZ/BAUD_RATE) || // ERROR:Xst:850 - Unsupported real constant
 counter == (5.5*CLOCK_HZ/BAUD_RATE) || // ERROR:Xst:850 - Unsupported real constant
 counter == (6.5*CLOCK_HZ/BAUD_RATE) || // ERROR:Xst:850 - Unsupported real constant
 counter == (7.5*CLOCK_HZ/BAUD_RATE) || // ERROR:Xst:850 - Unsupported real constant
 counter == (8.5*CLOCK_HZ/BAUD_RATE) || // ERROR:Xst:850 - Unsupported real constant
 counter == (9.5*CLOCK_HZ/BAUD_RATE)) // ERROR:Xst:850 - Unsupported real constant
 begin
 counter <= counter + 1;
 tick <= 1;
 done <= 0;
 end
 else if (counter == 10*CLOCK_HZ/BAUD_RATE) begin
 counter <= 32'h00000000;
 tick <= 0;
 done <= 1;
 end
 else begin
 counter <= counter + 1;
 tick <= 0;
 done <= 0;
 end
 end
endmodule
asked Dec 11, 2011 at 19:00
\$\endgroup\$

2 Answers 2

0
\$\begingroup\$

Since no other answers are forthcoming, I'll suggest an alternative approach.

Instead of two parameters CLOCK_HZ and BAUD_RATE, just use a single parameter DIVIDE_RATIO.

Then the values for comparison can be calculated as DIVIDE_RATIO[n:1], DIVIDE_RATIO, (3*DIVIDE_RATIO)[n:1], etc. and no floating point value is ever created.

The disadvantage of this relative to what you have is that if the divide ratio isn't an exact integer, your approach would smooth out the errors in the tick rate over 10 cycles, whereas mine would have slightly more error in the tick rate compared to the "ideal" divide ratio.

In addition, although its not what you asked about, I'd suggest looking at alternate ways of arranging your counter all together. As your code stands you're using a 32-bit register to hold (and doing 32-bit comparisons on) a counter that will never count above 600 assuming the default values you used for the parameters aren't overridden by the caller. I think you could get all the same states with 9 or 10 state bits.

Edit -- an alternative approach:

Another way to go that solves both the floating point problem and the accuracy problem, is to use a jump counter. (freehand code, not tested):

parameter jump = xxx; /// jump = 2^32 * BAUD_RATE / CLOCK_HZ
reg [32:0] ctr;
always @ (posedge clk) begin
 ctr <= ctr[31:0] + jump; 
 tick <= ctr[32];
end

By allowing the counter to just roll over instead of resetting to 0 after the terminal count, you get a tick rate that averages out correctly to the limits of 32-bit integer math, but you don't have any floating point to confuse the Verilog compiler or synthesizer.

You'll need to add the reset logic and a second counter to count ticks and generate the 'done' signal. To get exactly what you had before you'll also need

wire real_tick;
assign real_tick = tick & ~done;

If the tick output has to be glitch-free, you'd have to do that in sequential logic.

answered Dec 12, 2011 at 2:11
\$\endgroup\$
1
  • \$\begingroup\$ Yeah, I thought about doing it that way, and I might end up doing that once I get my project working, but for now I want to keep the error low while I experiment. I sort of worked around it by testing "counter == 1*CLOCK_HZ/(2*BAUD_RATE)" instead of "counter == 0.5*CLOCK_HZ/BAUD_RATE", but what I really wanted to do was floor division. As for the 32-bit integer, my actual code computes a localparam COUNTER_WIDTH=clog2(2+(TOTAL_BITS+0.5)*CLOCK_HZ/BAUD_RATE), with clog2(x) being a nasty in-line function that computes ceil(log2(x)), but I left that out of my question for simplicity. :) \$\endgroup\$ Commented Dec 18, 2011 at 6:19
0
\$\begingroup\$

The tool is trying to tell you, politely, "Hey, dude, I'm not a C compiler." :)

In addition to picking an appropriate fixed-point representation for the numbers you're working with, you shouldn't synthesize counters in terms of additions and comparisons to a terminal count. Instead, start the counter at the terminal value and test it against zero. This will save an adder and shorten the logic path.

answered Dec 13, 2011 at 10:14
\$\endgroup\$
1
  • \$\begingroup\$ So how would I pick that fixed-point representation? In this example, I'd want a 32.0 representation. Also, starting the counter at the terminal value and then testing against zero is a great optimization, but I'm still figuring out what I'm doing so readability of the code is much more important to me than conserving FPGA resources at this point. \$\endgroup\$ Commented Dec 18, 2011 at 6:26

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.