Use higher precision arithmetic when calculating the atan2 table
[ieee754fpu.git] / src / ieee754 / cordic / fpsin_cos.py
1 # This is an unpipelined version of an sin/cos cordic, which will
2 # later be used to verify the operation of a pipelined version
3
4 # see http://bugs.libre-riscv.org/show_bug.cgi?id=208
5 from nmigen import Module, Elaboratable, Signal, Memory, Cat, Repl, Mux
6 from nmigen.cli import rtlil
7 import math
8 from enum import Enum, unique
9 from ieee754.fpcommon.fpbase import FPNumBaseRecord, FPNumDecode
10
11 import gmpy2
12 from gmpy2 import mpfr
13
14
15
16 @unique
17 class CordicState(Enum):
18 WAITING = 0
19 INIT = 1
20 RUNNING = 2
21
22
23 class CordicROM(Elaboratable):
24 def __init__(self, fracbits, iterations):
25 self.fracbits = fracbits
26 self.iterations = iterations
27
28 M = 1 << fracbits
29 self.addr = Signal(range(iterations))
30 self.data = Signal(range(-M, M-1))
31
32 angles = []
33 gmpy2.get_context().precision = 150
34 for i in range(self.iterations):
35 x = mpfr(2) ** -i
36 x = gmpy2.atan(x)
37 x = x/(gmpy2.const_pi()/mpfr(2))
38 x = x * mpfr(M)
39 angles.append(int(round(x)))
40
41 self.mem = Memory(width=self.data.width,
42 depth=self.iterations,
43 init=angles)
44
45 def elaborate(self, platform):
46 m = Module()
47 m.submodules.rdport = rdport = self.mem.read_port()
48 m.d.comb += rdport.addr.eq(self.addr)
49 m.d.comb += self.data.eq(rdport.data)
50 return m
51
52
53 class CORDIC(Elaboratable):
54 def __init__(self, width):
55
56 self.z0 = Signal(width, name="z0")
57 self.z_record = FPNumBaseRecord(self.z0.width, False, name="z_record")
58 self.fracbits = 2 * self.z_record.m_width
59 self.M = M = (1 << self.fracbits)
60 self.ZMAX = int(round(self.M * math.pi/2))
61 self.z_out = Signal(range(-self.ZMAX, self.ZMAX-1))
62
63 # sin/cos output in 0.ffffff format
64 self.cos = Signal(range(-M, M+1), reset=0)
65 self.sin = Signal(range(-M, M+1), reset=0)
66 # angle input
67
68 # cordic start flag
69 self.start = Signal(reset_less=True)
70 # cordic done/ready for input
71 self.ready = Signal(reset=True)
72
73 self.width = self.z0.width
74 self.iterations = self.fracbits - 1
75
76 def elaborate(self, platform):
77 m = Module()
78 comb = m.d.comb
79 sync = m.d.sync
80
81 m.submodules.z_in = z_in = FPNumDecode(None, self.z_record)
82 comb += z_in.v.eq(self.z0)
83
84 z_fixed = Signal(range(-self.ZMAX, self.ZMAX-1),
85 reset_less=True)
86
87 # Calculate initial amplitude?
88 An = 1.0
89 for i in range(self.iterations):
90 An *= math.sqrt(1 + 2**(-2*i))
91
92 X0 = int(round(self.M*1/An))
93 x = Signal(self.sin.shape())
94 y = Signal(self.sin.shape())
95 z = Signal(z_fixed.shape())
96 dx = Signal(self.sin.shape())
97 dy = Signal(self.sin.shape())
98 dz = Signal(z_fixed.shape())
99 i = Signal(range(self.iterations))
100 state = Signal(CordicState, reset=CordicState.WAITING)
101
102 m.submodules.anglerom = anglerom = \
103 CordicROM(self.fracbits, self.iterations)
104
105 comb += dx.eq(y >> i)
106 comb += dy.eq(x >> i)
107 comb += dz.eq(anglerom.data)
108 comb += self.cos.eq(x)
109 comb += self.sin.eq(y)
110 with m.If(state == CordicState.WAITING):
111 with m.If(self.start):
112 z_intermed = Signal(z_fixed.shape())
113 shifter = Signal(z_in.e.width)
114 comb += shifter.eq(-z_in.e)
115 # This converts z_in.m to a large fixed point
116 # integer. Right now, I'm ignoring denormals but they
117 # will be added back in when I convert this to the
118 # pipelined implementation (and I can use FPAddDenormMod)
119 comb += z_intermed.eq(Cat(Repl(0, self.fracbits - z_in.rmw),
120 z_in.m[:-1], 1))
121 sync += z_fixed.eq(z_intermed >> shifter)
122 sync += state.eq(CordicState.INIT)
123 sync += self.ready.eq(0)
124 with m.If(state == CordicState.INIT):
125 z_temp = Signal(z.shape(), reset_less=True)
126 comb += z_temp.eq(Mux(z_in.s, ~z_fixed + 1, z_fixed))
127 sync += z.eq(z_temp)
128 sync += self.z_out.eq(z_temp)
129 sync += x.eq(X0)
130 sync += y.eq(0)
131 sync += i.eq(0)
132 sync += state.eq(CordicState.RUNNING)
133 sync += anglerom.addr.eq(1)
134 with m.If(state == CordicState.RUNNING):
135 with m.If(z >= 0):
136 sync += x.eq(x - dx)
137 sync += y.eq(y + dy)
138 sync += z.eq(z - dz)
139 with m.Else():
140 sync += x.eq(x + dx)
141 sync += y.eq(y - dy)
142 sync += z.eq(z + dz)
143 with m.If(i == self.iterations - 1):
144 sync += state.eq(CordicState.WAITING)
145 sync += self.ready.eq(1)
146 sync += anglerom.addr.eq(0)
147 with m.Else():
148 sync += i.eq(i+1)
149 sync += anglerom.addr.eq(i+2)
150 return m
151
152 def ports(self):
153 lst = [self.cos, self.sin,
154 self.ready, self.start]
155 lst.extend(self.z0)
156 return lst
157
158
159 if __name__ == '__main__':
160 dut = CORDIC(8)
161 vl = rtlil.convert(dut, ports=dut.ports())
162 with open("cordic.il", "w") as f:
163 f.write(vl)