sin/cos cordic partially working
authorMichael Nolan <mtnolan2640@gmail.com>
Tue, 31 Mar 2020 20:35:50 +0000 (16:35 -0400)
committerMichael Nolan <mtnolan2640@gmail.com>
Tue, 31 Mar 2020 20:35:50 +0000 (16:35 -0400)
src/ieee754/cordic/sin_cos.py
src/ieee754/cordic/test/python_sin_cos.py [new file with mode: 0644]
src/ieee754/cordic/test/test_sincos.py

index 7e72808b253e7f75db22cd7c8112b826e20a8bc8..911880dba17fc9555ab2fc4310bb5de7c7bfafd0 100644 (file)
@@ -1,4 +1,4 @@
-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
@@ -41,15 +41,15 @@ class CORDIC(Elaboratable):
         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
@@ -73,13 +73,16 @@ class CORDIC(Elaboratable):
         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)
@@ -88,17 +91,22 @@ class CORDIC(Elaboratable):
                 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):
diff --git a/src/ieee754/cordic/test/python_sin_cos.py b/src/ieee754/cordic/test/python_sin_cos.py
new file mode 100644 (file)
index 0000000..771e8a8
--- /dev/null
@@ -0,0 +1,36 @@
+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)
index 07ebc80af7349f71266024b208e563da2201bf34..c17b86faad35c46651f15100ba1991e3509f7f39 100644 (file)
@@ -3,13 +3,15 @@ from nmigen.back.pysim import Simulator, Delay
 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())
@@ -30,12 +32,44 @@ class SinCosTestCase(FHDLTestCase):
         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()