Assert that fpsin_cos converts floats to fixed correctly
authorMichael Nolan <mtnolan2640@gmail.com>
Thu, 16 Apr 2020 17:14:43 +0000 (13:14 -0400)
committerMichael Nolan <mtnolan2640@gmail.com>
Thu, 16 Apr 2020 17:18:34 +0000 (13:18 -0400)
src/ieee754/cordic/fpsin_cos.py
src/ieee754/cordic/test/test_fpsin_cos.py

index 96df0d45b9d8623c3efd1e661735f215072df0b0..fa7ed8c1c16217fdbc187d1604d420d6aa0adde2 100644 (file)
@@ -2,7 +2,7 @@
 # later be used to verify the operation of a pipelined version
 
 # see http://bugs.libre-riscv.org/show_bug.cgi?id=208
 # later be used to verify the operation of a pipelined version
 
 # see http://bugs.libre-riscv.org/show_bug.cgi?id=208
-from nmigen import Module, Elaboratable, Signal, Memory
+from nmigen import Module, Elaboratable, Signal, Memory, Cat, Repl, Mux
 from nmigen.cli import rtlil
 import math
 from enum import Enum, unique
 from nmigen.cli import rtlil
 import math
 from enum import Enum, unique
@@ -44,10 +44,11 @@ class CORDIC(Elaboratable):
     def __init__(self, width):
 
         self.z0 = Signal(width, name="z0")
     def __init__(self, width):
 
         self.z0 = Signal(width, name="z0")
-        self.z_record = FPNumBaseRecord(self.z0.width, m_extra=True)
+        self.z_record = FPNumBaseRecord(self.z0.width, False, name="z_record")
         self.fracbits = 2 * self.z_record.m_width
         self.M = M = (1 << self.fracbits)
         self.ZMAX = int(round(self.M * math.pi/2))
         self.fracbits = 2 * self.z_record.m_width
         self.M = M = (1 << self.fracbits)
         self.ZMAX = int(round(self.M * math.pi/2))
+        self.z_out = Signal(range(-self.ZMAX, self.ZMAX-1))
 
         # sin/cos output in 0.ffffff format
         self.cos = Signal(range(-M, M+1), reset=0)
 
         # sin/cos output in 0.ffffff format
         self.cos = Signal(range(-M, M+1), reset=0)
@@ -98,15 +99,29 @@ class CORDIC(Elaboratable):
         comb += self.sin.eq(y)
         with m.If(state == CordicState.WAITING):
             with m.If(self.start):
         comb += self.sin.eq(y)
         with m.If(state == CordicState.WAITING):
             with m.If(self.start):
+                z_intermed = Signal(z_fixed.shape())
+                shifter = Signal(z_in.e.width)
+                comb += shifter.eq(-z_in.e)
+                # This converts z_in.m to a large fixed point
+                # integer. Right now, I'm ignoring denormals but they
+                # will be added back in when I convert this to the
+                # pipelined implementation (and I can use FPAddDenormMod)
+                comb += z_intermed.eq(Cat(Repl(0, self.fracbits - z_in.rmw),
+                                          z_in.m[:-1], 1))
+                sync += z_fixed.eq(z_intermed >> shifter)
                 sync += state.eq(CordicState.INIT)
                 sync += state.eq(CordicState.INIT)
-                sync += z_fixed.eq(z_in.m << (self.fracbits - z_in.rmw))
+                sync += self.ready.eq(0)
         with m.If(state == CordicState.INIT):
         with m.If(state == CordicState.INIT):
+            z_temp = Signal(z.shape(), reset_less=True)
+            comb += z_temp.eq(Mux(z_in.s, ~z_fixed + 1, z_fixed))
+            sync += z.eq(z_temp)
+            sync += self.z_out.eq(z_temp)
             sync += x.eq(X0)
             sync += y.eq(0)
             sync += x.eq(X0)
             sync += y.eq(0)
-            sync += z.eq(z_fixed)
             sync += i.eq(0)
             sync += state.eq(CordicState.RUNNING)
             sync += anglerom.addr.eq(1)
             sync += i.eq(0)
             sync += state.eq(CordicState.RUNNING)
             sync += anglerom.addr.eq(1)
+            sync += self.ready.eq(1)  # debug
         with m.If(state == CordicState.RUNNING):
             with m.If(z >= 0):
                 sync += x.eq(x - dx)
         with m.If(state == CordicState.RUNNING):
             with m.If(z >= 0):
                 sync += x.eq(x - dx)
index 25b97a56733c4cdeeddb22a74aac9943de9ca9dc..1f050960c9bd094a688ed38f98667cd8f3fb1be1 100644 (file)
@@ -16,7 +16,7 @@ class SinCosTestCase(FHDLTestCase):
 
         m = Module()
 
 
         m = Module()
 
-        m.submodules.dut = dut = CORDIC(32)
+        m.submodules.dut = dut = CORDIC(16)
         z = Signal(dut.z0.width)
         start = Signal()
 
         z = Signal(dut.z0.width)
         start = Signal()
 
@@ -35,6 +35,7 @@ class SinCosTestCase(FHDLTestCase):
         sim.add_clock(1e-6)
 
         def process():
         sim.add_clock(1e-6)
 
         def process():
+            asserted = False
             yield z.eq(zin.get_bits())
             yield start.eq(1)
 
             yield z.eq(zin.get_bits())
             yield start.eq(1)
 
@@ -43,6 +44,12 @@ class SinCosTestCase(FHDLTestCase):
             yield
             for i in range(fracbits * 3):
                 rdy = yield ready
             yield
             for i in range(fracbits * 3):
                 rdy = yield ready
+                zo = yield dut.z_out
+                if rdy and not asserted:
+                    frac = self.get_frac(zo, dut.z_out.width - 2)
+                    print(f"{zo:x} {frac}")
+                    self.assertEqual(str(frac), zin.__str__())
+                    asserted = True
                 yield
 
         sim.add_sync_process(process)
                 yield
 
         sim.add_sync_process(process)
@@ -54,20 +61,21 @@ class SinCosTestCase(FHDLTestCase):
         self.run_test(zin=z, fracbits=fracbits)
 
     def test_1(self):
         self.run_test(zin=z, fracbits=fracbits)
 
     def test_1(self):
-        x = Float32(1.0)
+        x = Float16(.31212)
+        print(x)
         self.run_test_assert(x)
 
     # def test_neg(self):
     #     self.run_test_assert(-6)
 
         self.run_test_assert(x)
 
     # def test_neg(self):
     #     self.run_test_assert(-6)
 
-    # def test_rand(self):
-    #     fracbits = 16
-    #     M = (1 << fracbits)
-    #     ZMAX = int(round(M * math.pi/2))
-    #     for i in range(500):
-    #         z = random.randrange(-ZMAX, ZMAX-1)
-    #         self.run_test_assert(z, fracbits=fracbits)
+    def test_rand(self):
+        for i in range(500):
+            z = random.uniform(-1, 1)
+            f = Float16(z)
+            self.run_test_assert(f)
 
 
+    def get_frac(self, value, bits):
+        return value/(1 << bits)
 
 if __name__ == "__main__":
     unittest.main()
 
 if __name__ == "__main__":
     unittest.main()