-from nmigen import Module, Elaboratable, Signal, Memory
+from nmigen import Module, Elaboratable, Signal, Memory, signed
from nmigen.cli import rtlil
import math
from enum import Enum, unique
self.ZMAX = ZMAX = int(round(self.M * math.pi/2))
# sin/cos output in 0.ffffff format
- self.cos = Signal(range(-M, M-1))
- self.sin = Signal(range(-M, M-1))
+ self.cos = Signal(range(-M, M-1), reset=0)
+ self.sin = Signal(range(-M, M-1), reset=0)
# angle input
self.z0 = Signal(range(-ZMAX, ZMAX), reset_less=True)
# cordic start flag
self.start = Signal(reset_less=True)
# cordic done/ready for input
- self.ready = Signal()
+ self.ready = Signal(reset=True)
self.width = self.z0.width
self.iterations = self.width - 1
dy = Signal(self.sin.shape())
dz = Signal(self.z0.shape())
i = Signal(range(self.iterations))
-
- state = Signal(CordicState)
+ state = Signal(CordicState, reset=CordicState.WAITING)
m.submodules.anglerom = anglerom = \
CordicROM(self.fracbits, self.iterations)
- comb += anglerom.addr.eq(i)
+ comb += dx.eq(y >> i)
+ comb += dy.eq(x >> i)
+ comb += dz.eq(anglerom.data)
+ comb += self.cos.eq(x)
+ comb += self.sin.eq(y)
with m.If(state == CordicState.WAITING):
with m.If(self.start):
sync += x.eq(X0)
sync += i.eq(0)
sync += self.ready.eq(0)
sync += state.eq(CordicState.RUNNING)
+ sync += anglerom.addr.eq(1)
with m.If(state == CordicState.RUNNING):
- sync += dx.eq(x >> i)
- sync += dx.eq(y >> i)
- sync += dz.eq(anglerom.data)
+ with m.If(z >= 0):
+ sync += x.eq(x - dx)
+ sync += y.eq(y + dy)
+ sync += z.eq(z - dz)
+ with m.Else():
+ sync += x.eq(x + dx)
+ sync += y.eq(y - dy)
+ sync += z.eq(z + dz)
with m.If(i == self.iterations - 1):
- sync += self.cos.eq(x)
- sync += self.sin.eq(y)
sync += state.eq(CordicState.WAITING)
sync += self.ready.eq(1)
with m.Else():
sync += i.eq(i+1)
+ sync += anglerom.addr.eq(i+2)
return m
def ports(self):
--- /dev/null
+import math
+
+
+def run_cordic(z0, fracbits=8, log=True):
+ M = 1<<fracbits
+ N = fracbits+1
+ An = 1.0
+ for i in range(N):
+ An *= math.sqrt(1 + 2**(-2*i))
+
+ X0 = int(round(M*1/An))
+
+ x = X0
+ y = 0
+ z = z0
+ angles = tuple([int(round(M*math.atan(2**(-i)))) for i in range(N)])
+
+ for i in range(N):
+ dx = y >> i
+ dy = x >> i
+ dz = angles[i]
+
+
+ if z >= 0:
+ x -= dx
+ y += dy
+ z -= dz
+ else:
+ x += dx
+ y -= dy
+ z += dz
+ if log:
+ print("iteration {}".format(i))
+ print("dx: {}, dy: {}, dz: {}".format(dx, dy, dz))
+ print("x: {}, y: {}, z: {}".format(x, y, z))
+ return (y, x)
from nmigen.test.utils import FHDLTestCase
from ieee754.cordic.sin_cos import CORDIC
+from python_sin_cos import run_cordic
import unittest
+import math
+import random
class SinCosTestCase(FHDLTestCase):
- def test_sincos(self):
- m = Module()
+ def run_test(self, zin=0, fracbits=8, expected_sin=0, expected_cos=0):
- fracbits = 8
+ m = Module()
m.submodules.dut = dut = CORDIC(fracbits)
z = Signal(dut.z0.shape())
sim.add_clock(1e-6)
def process():
+ yield z.eq(zin)
+ yield start.eq(1)
+
+ yield
+ yield start.eq(0)
+ yield
for i in range(10):
- yield
+ rdy = yield ready
+ if rdy == 1:
+ result = yield sin
+ msg = "sin: {}, expected {}".format(result, expected_sin)
+ assert result == expected_sin, msg
+ result = yield cos
+ msg = "cos: {}, expected {}".format(result, expected_cos)
+ assert result == expected_cos, msg
+ else:
+ yield
+
sim.add_sync_process(process)
with sim.write_vcd("sin_cos.vcd", "sin_cos.gtkw", traces=[
z, cos, sin, ready, start]):
sim.run()
+ def run_test_assert(self, z, fracbits=8):
+ (sin, cos) = run_cordic(z, fracbits=8, log=False)
+ self.run_test(zin=z, fracbits=8,
+ expected_sin=sin, expected_cos=cos)
+ def test_0(self):
+ self.run_test_assert(0)
+ def test_rand(self):
+ fracbits = 8
+ M = (1 << fracbits)
+ ZMAX = int(round(M * math.pi/2))
+ for i in range(100):
+ z = random.randrange(-ZMAX, ZMAX-1)
+ self.run_test_assert(z, fracbits=fracbits)
+
+
+
if __name__ == "__main__":
unittest.main()