Cordic-Based Sine Computer (MyHDL) PDF
Cordic-Based Sine Computer (MyHDL) PDF
Old revisions
Recent changes
Introduction
On this page we will present the design of a sine
and cosine computer.
Table of Contents
Introduction
What you will learn
Specification
Unit test
Algorithm
Design
Automatic conversion to Verilog
A discussion about some convertor
features
Taking advantage of the
elaboration phase
Handling negative numbers
Verilog co-simulation
Implementation
You will learn about the following MyHDL features and techniques:
using the intbv class to model negative numbers elegantly
how the Verilog convertor takes care of the tricky issues with the signed
representation in Verilog
how to take advantage of the fact that only code inside generator functions
gets converted to Verilog
how to use co-simulation to verify the Verilog code generated by the
convertor
Specification
We will assume that the input angle is represented in radians, and that it is in the
range between -/2 and /2. Other angles can be handled by adding a factor first
(modulo 2) and changing the sign of the sine and cosine result.
The floating point numbers are represented as integers. Technically, this means
rounding them to a fixed number of bits, and then multiplying them with the factor
2**F, where F is the number of bits after the point.
Both the sine and the cosine of the input angle will be computed. The interface of
the module looks as follows:
def SineComputer(cos_z0, sin_z0, done, z0, start, clock, reset):
This module computes the sine and cosine of an input angle. The
floating point numbers are represented as integers by scaling them
up with a factor corresponding to the number of bits after the
point.
Ports:
----cos_z0: cosine of the input angle
sin_z0: sine of the input angle
done: output flag indicating completion of the computation
z0: input angle; -pi/2 <= z0 <= pi/2
start: input that starts the computation on a posedge
clock: clock input
reset: reset input
"""
Unit test
We will first write a unit test for the design. The idea is to use the cos and sin
functions from the math module to compute the expected results on a number of
input angles, and to compare them with the outputs from the design under test.
Here is the code:
from math import pi, sin, cos, log
import random
"""
# maximum angle
ZMAX = int(round(M*pi/2))
# signals
cos_z0 = Signal(intbv(0, min=-D, max=M+D))
sin_z0 = Signal(intbv(0, min=-M-D, max=M+D))
z0 = Signal(intbv(0, min=-ZMAX, max=ZMAX+1))
done = Signal(False)
start = Signal(False)
clock = Signal(bool(0))
reset = Signal(True)
# clock generator
@always(delay(10))
def clockgen():
clock.next = not clock
# actual test
@instance
def check():
yield clock.negedge
reset.next = False
for z in testAngles:
yield clock.negedge
z0.next = int(round(M*z))
start.next = True
yield clock.negedge
start.next = False
yield done.posedge
exp_cos_z0 = int(round(cos(z)*M))
exp_sin_z0 = int(round(sin(z)*M))
assert abs(cos_z0 - exp_cos_z0) < D
raise StopSimulation
Algorithm
To implement the design, we will use the Cordic algorithm, a very popular algorithm
to compute trigonometric functions in hardware. On this page, we are mainly
interested in the mechanical characteristics of the algorithm and their hardware
implications. For more information and background on the algorithm itself, please
this paper by Ray Andraka.
consult other sources, such as
The Cordic algorithm is an iterative algorithm based on vector rotations over
elementary angles. The algorithm normally operates in one of two modes. In
rotation mode, it rotates a vector (x 0, y0) in the Cartesian plane over an input
angle z0. The Cordic equations for this mode are:
xi +1 = xi - yi d i 2 -i
yi +1 = yi - xi d i 2 -i
zi +1 = yi - d i tan -i (2 -i )
where
d i = -1 if zi < 0, else +1.
These equations can be implemented with relatively simple hardware. This is the
characteristic that makes the Cordic algorithm attractive. In particular,
multiplications with a factor 2 -i are simply shift-right operations. Also, the
arctangent values tan -i (2 -i ) can be precomputed and stored in a small look-up
table.
The Cordic equations can be used for a variety of computations. For our purposes, it
can be shown that
xn = cos z0
yn = sin z0
for the following initial conditions:
x 0 = 1 / An
y0 = 0
where
An = Product[ sqrt(1 + 2 -2i ) ] with i = 0 n-1
Design
The Cordic algorithm can be implemented in many ways, with various characteristics
and advantages. On this page, we will implement a parallel, iterative processor,
which is a fairly straightforward mapping of the equations into a bit-parallel data
path and a state machine.
from math import atan, sqrt, ceil, floor, pi
This module computes the sine and cosine of an input angle. The
floating point numbers are represented as integers by scaling them
up with a factor corresponding to the number of bits after the
point.
Ports:
----cos_z0: cosine of the input angle
sin_z0: sine of the input angle
done: output flag indicated completion of the computation
z0: input angle
start: input that starts the computation on a posedge
clock: clock input
reset: reset input
"""
# calculate X0
An = 1.0
for i in range(N):
An *= (sqrt(1 + 2**(-2*i)))
# X0
X0 = int(round(M*1/An))
while True:
yield clock.posedge, reset.posedge
if reset:
state = t_State.WAITING
cos_z0.next = 1
sin_z0.next = 0
done.next = False
x[:] = 0
y[:] = 0
z[:] = 0
i[:] = 0
else:
if state == t_State.WAITING:
if start:
x[:] = X0
y[:] = 0
z[:] = z0
i[:] = 0
done.next = False
state = t_State.CALCULATING
return processor
The actual computation is done by the processor generator. Note that outside the
generator function, we calculate some data such as the X0 constant, and the lookup table of elementary arctangents, represented by the angles tuple.
The internal number variables are represented by intbv instances. The dual nature
of this class comes in very handy. On the one hand, we can constrain the instances
as integer subtypes by specifying the valid integer range at construction time. On
the other hand, we can access their two's complement representation as a bit
vector, for example for slicing or right-shifting.
It seems obvious that a type that unifies the integer and the bit vector views should
be very useful for hardware design. One would therefore expect a similar feature in
other HDLs. However, I believe that it is actually a unique capability offered by
MyHDL. Other HDLs seem to try to solve the issues by creating more and more
integer and bit-vector like types. In MyHDL, a single type does it all - the intbv
class.
py.test confirms that this is a valid impementation:
> py.test
end
else begin
// synthesis parallel_case full_case
casez (state)
1'b0: begin
if (start) begin
x = 159188;
y = 0;
z = z0;
i = 0;
done <= 0;
state = 1'b1;
end
end
1'b1: begin
dx = $signed(y >>> $signed({1'b0, i}));
dy = $signed(x >>> $signed({1'b0, i}));
// synthesis parallel_case full_case
case (i)
0: dz = 205887;
1: dz = 121542;
2: dz = 64220;
3: dz = 32599;
4: dz = 16363;
5: dz = 8189;
6: dz = 4096;
7: dz = 2048;
8: dz = 1024;
9: dz = 512;
10: dz = 256;
11: dz = 128;
12: dz = 64;
13: dz = 32;
14: dz = 16;
15: dz = 8;
16: dz = 4;
17: dz = 2;
default: dz = 1;
endcase
if ((z >= 0)) begin
x = x - dx;
y = y + dy;
z = z - dz;
end
else begin
x = x + dx;
y = y - dy;
z = z + dz;
end
if ((i == (19 - 1))) begin
cos_z0 <= x;
sin_z0 <= y;
state = 1'b0;
done <= 1;
end
else begin
i = i + 1;
end
end
endcase
end
end
endmodule
Verilog co-simulation
Clearly we will want to verify that the Verilog output from the convertor is correct.
For this purpose, MyHDL supports co-simulation with Verilog.
To set up a co-simulation, we need to create a Cosimulation object for the Verilog
design. The Verilog convertor makes this task easier. In addition to the Verilog code
for the design itself, it also generates a Verilog test bench stub that defines an
interface between the Verilog design and a Cosimulation object.
The following function creates a Cosimulation object for our design:
def SineComputer_v(cos_z0, sin_z0, done, z0, start, clock, reset):
toVerilog(SineComputer, cos_z0, sin_z0, done, z0, start, clock,
reset)
cmd = "cver -q +loadvpi=myhdl_vpi:vpi_compat_bootstrap " + \
"SineComputer.v tb_SineComputer.v"
return Cosimulation(cmd, **locals())
We start by doing the Verilog conversion itself first. Then, we define the command
to start up the Verilog simulator. This is of course simulator-specific. The command
shown is for the open-source Cver simulator. It loads a vpi module that defines the
interface between the MyHDL simulator and the Verilog simulator. Also, both the
Verilog code for the design and the test bench stub are compiled.
The Cosimulation object is then constructed with the command as its first
parameter, followed by a number of keyword arguments. The keyword arguments
make the link between signal names declared in the Verilog test bench stub and
signals in the MyHDL code. When the signal names in MyHDL and Verilog are
identical we can use a little trick and simply pass the local namespace dictionary
locals() to the constructor.
In the test bench code, we replace the instantiation of the MyHDL module with this
function:
Implementation
To confirm synthesizablity, this example was synthesized with Xilinx ISE and
targeted to a Spartan FPGA. For detailed information, you can review the synthesis
report.
cookbook/sinecomp.txt Last modified: 2006/05/04 09:12 by jandecaluwe
Show pagesource
Old revisions
Media Manager
Except where otherwise noted, content on this wiki is licensed under the following license:
Unported
Login
Sitemap
Back to top