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
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
9 from enum
import Enum
, unique
10 from ieee754
.fpcommon
.fpbase
import FPNumBaseRecord
, FPNumDecode
12 from bigfloat
import BigFloat
16 class CordicState(Enum
):
22 class CordicROM(Elaboratable
):
23 def __init__(self
, fracbits
, iterations
):
24 self
.fracbits
= fracbits
25 self
.iterations
= iterations
28 self
.addr
= Signal(range(iterations
))
29 self
.data
= Signal(signed(fracbits
+ 2))
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)
37 angles
.append(int(round(x
)))
39 self
.mem
= Memory(width
=self
.data
.width
,
40 depth
=self
.iterations
,
43 def elaborate(self
, platform
):
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
)
51 class CORDIC(Elaboratable
):
52 def __init__(self
, width
):
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))
60 self
.z_out
= Signal(signed(self
.fracbits
+ 2))
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)
68 self
.start
= Signal(reset_less
=True)
69 # cordic done/ready for input
70 self
.ready
= Signal(reset
=True)
72 self
.width
= self
.z0
.width
73 self
.iterations
= self
.fracbits
- 1
75 def elaborate(self
, platform
):
80 m
.submodules
.z_in
= z_in
= FPNumDecode(None, self
.z_record
)
81 comb
+= z_in
.v
.eq(self
.z0
)
83 z_fixed
= Signal(signed(self
.fracbits
+ 2),
86 # Calculate initial amplitude?
88 for i
in range(self
.iterations
):
89 An
*= math
.sqrt(1 + 2**(-2*i
))
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
)
101 m
.submodules
.anglerom
= anglerom
= \
102 CordicROM(self
.fracbits
, self
.iterations
)
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
),
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
))
127 sync
+= self
.z_out
.eq(z_temp
)
131 sync
+= state
.eq(CordicState
.RUNNING
)
132 sync
+= anglerom
.addr
.eq(1)
133 with m
.If(state
== CordicState
.RUNNING
):
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)
148 sync
+= anglerom
.addr
.eq(i
+2)
152 lst
= [self
.cos
, self
.sin
,
153 self
.ready
, self
.start
]
158 if __name__
== '__main__':
160 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
161 with
open("cordic.il", "w") as f
: