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