Taylor Series in Verilog
I am doing my first student Project in Verilog. My project is to calculate log base 2 using "Taylor Series" in Fixed-Point arithmetic (s4.27). I have implemented the Horner's method as well in my code.
Overall it looks like this:
`timescale 10ns/10ns module log (q ,clk,log_result); input clk; input [31:0] q; // q=x-a; x=user input, a=1.5 (Taylor series is calculated around point "a") output [31:0] log_result; localparam con= 32'h0B8AA3B0; //1.44269504088895 localparam c0 = 32'h033E647C; //0.40546510810816 localparam c1 = 32'h05555558; //0.66666666666666 localparam c2 = 32'hFE38E38E; //-0.222222222222 localparam c3 = 32'h00CA4588; //0.0987654321 localparam c4 = 32'hFF9ADD3C; //-0.04938271605 localparam c5 = 32'h0035F068; //0.02633744856 localparam c6 = 32'hFFE208AA; //-0.01463191587 wire [31:0] x0,x1,x2,x3,x4,x5,x6; wire [31:0] y0,y1,y2,y3,y4,y5,y6; multiplier #(27,32) m6(.i_multiplicand(q),.i_multiplier(c6),.o_result(x6)); adder #(27,32) a6 (.a(x6),.b(c5),.c(y6)); multiplier #(27,32) m5(.i_multiplicand(q),.i_multiplier(y6),.o_result(x5)); adder #(27,32) a5 (.a(x5),.b(c4),.c(y5)); multiplier #(27,32) m4(.i_multiplicand(q),.i_multiplier(y5),.o_result(x4)); adder #(27,32) a4 (.a(x4),.b(c3),.c(y4)); multiplier #(27,32) m3(.i_multiplicand(q),.i_multiplier(y4),.o_result(x3)); adder #(27,32) a3 (.a(x3),.b(c2),.c(y3)); multiplier #(27,32) m2(.i_multiplicand(q),.i_multiplier(y3),.o_result(x2)); adder #(27,32) a2 (.a(x2),.b(c1),.c(y2)); multiplier #(27,32) m1(.i_multiplicand(q),.i_multiplier(y2),.o_result(x1)); adder #(27,32) a1 (.a(x1),.b(c0),.c(y1)); multiplier #(27,32) (.i_multiplicand(con),.i_multiplier(y1),.o_result(x0)); assign log_result = x0; endmodule
Test bench code:
`timescale 10ns/10ns module tb_log (); reg clk; reg [ 31 : 0 ] q; wire [ 31 : 0 ] log_result; log log_i ( .q(q), .clk(clk), .log_result(log_result) ); parameter CLKPERIODE = 100; initial clk = 1'b1; always #(CLKPERIODE/2) clk = !clk; initial begin $dumpfile("log_wave.vcd"); $dumpvars(1); $monitor ("Q=%h,Log2=%h ", q, log_result); #1 #(CLKPERIODE) q = 32'hFC000000; // q=1-1.5=-0.5; $finish(); end endmodule
So I am expecting a result close to zero. But unfortunately I am getting 9B917CED. When I tried to include clock an error naming "Malformed Statement" occurred. I am using Icarus verilog for compiling.
I am sure there are bugs but currently my rookie eyes are unable to notice it. What am I missing?
I suggest you do just one multiplication and one adder and check the result of that. Fixed point multiplications should be very well checked for overflowing, which I very strongly suspect is the case here!
It is a problem I have seen again and again: multipliers in all Verilog examples take two N bit numbers and produce a 2N result. Very few sources tackle e.g. a 20 stage FIR filter where you have twenty multipliers each using two 16 bit numbers. Following "example" code you end up with a 52 bit wide result.
To prevent overflow each argument of the multiplication should be <16 bits. (As you are using 32 bits results). Then every time you add you need another extra bit. Thus either your data gets wider further down the stream, or you have to start sufficiently small that it fits in the en result of 32 bits.
You can compensate if you have constant values and know what range of the result is. e.g. in a digital filter all coefficients add up to 1 and they are often positive, negative,positive, negative so the additions do not all increment all the time.
Welcome to the world of hardware integer arithmetics!