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
-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
@@ -44,10 +44,11 @@ class CORDIC(Elaboratable):
     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.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)
@@ -98,15 +99,29 @@ class CORDIC(Elaboratable):
         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 += z_fixed.eq(z_in.m << (self.fracbits - z_in.rmw))
+                sync += self.ready.eq(0)
         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 += z.eq(z_fixed)
             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)
index 25b97a56733c4cdeeddb22a74aac9943de9ca9dc..1f050960c9bd094a688ed38f98667cd8f3fb1be1 100644 (file)
@@ -16,7 +16,7 @@ class SinCosTestCase(FHDLTestCase):
 
         m = Module()
 
-        m.submodules.dut = dut = CORDIC(32)
+        m.submodules.dut = dut = CORDIC(16)
         z = Signal(dut.z0.width)
         start = Signal()
 
@@ -35,6 +35,7 @@ class SinCosTestCase(FHDLTestCase):
         sim.add_clock(1e-6)
 
         def process():
+            asserted = False
             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
+                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)
@@ -54,20 +61,21 @@ class SinCosTestCase(FHDLTestCase):
         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)
 
-    # 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()