From 6cdb96c5e0135308a4b4a99a749dc35896c414bd Mon Sep 17 00:00:00 2001
From: Robert Jordens <rj@m-labs.hk>
Date: Tue, 13 Dec 2016 18:31:15 +0100
Subject: [PATCH] rtio: add support for latency compensation in phy

* if multiple RTIO channels influence the same data stream and physical
output channel (see SAWG) differential latency needs to be compensated
* this is a NOP for phys with zero delay (default)
* if delay==1, it adds one timestamp-wide register
* if delay >1, it adds one adder and one register
* latency compensation using (~10-50 deep) delay lines is about as
expensive as a single adder+register but very tedious to implement
---
 artiq/gateware/rtio/core.py   | 22 +++++++++++++++++++---
 artiq/gateware/rtio/rtlink.py | 20 +++++++++++++++-----
 2 files changed, 34 insertions(+), 8 deletions(-)

diff --git a/artiq/gateware/rtio/core.py b/artiq/gateware/rtio/core.py
index 674ba761f..47cefdd8a 100644
--- a/artiq/gateware/rtio/core.py
+++ b/artiq/gateware/rtio/core.py
@@ -159,10 +159,18 @@ class _OutputManager(Module):
             )
         self.comb += fifo.re.eq(fifo.readable & (~dout_stb | dout_ack))
 
+        # latency compensation
+        if interface.delay:
+            counter_rtio = Signal.like(counter.value_rtio)
+            self.sync.rio += counter_rtio.eq(counter.value_rtio -
+                                             interface.delay + 1)
+        else:
+            counter_rtio = counter.value_rtio
+
         # FIFO read through buffer
         self.comb += [
             dout_ack.eq(
-                dout.timestamp[fine_ts_width:] == counter.value_rtio),
+                dout.timestamp[fine_ts_width:] == counter_rtio),
             interface.stb.eq(dout_stb & dout_ack)
         ]
 
@@ -210,14 +218,22 @@ class _InputManager(Module):
             fifo_out.raw_bits().eq(fifo.dout)
         ]
 
+        # latency compensation
+        if interface.delay:
+            counter_rtio = Signal.like(counter.value_rtio)
+            self.sync.rio += counter_rtio.eq(counter.value_rtio -
+                                             interface.delay + 1)
+        else:
+            counter_rtio = counter.value_rtio
+
         # FIFO write
         if data_width:
             self.comb += fifo_in.data.eq(interface.data)
         if interface.timestamped:
             if fine_ts_width:
-                full_ts = Cat(interface.fine_ts, counter.value_rtio)
+                full_ts = Cat(interface.fine_ts, counter_rtio)
             else:
-                full_ts = counter.value_rtio
+                full_ts = counter_rtio
             self.comb += fifo_in.timestamp.eq(full_ts)
         self.comb += fifo.we.eq(interface.stb)
 
diff --git a/artiq/gateware/rtio/rtlink.py b/artiq/gateware/rtio/rtlink.py
index d52e38ad3..a4fb3ebf9 100644
--- a/artiq/gateware/rtio/rtlink.py
+++ b/artiq/gateware/rtio/rtlink.py
@@ -3,7 +3,8 @@ from migen import *
 
 class OInterface:
     def __init__(self, data_width, address_width=0,
-                 fine_ts_width=0, enable_replace=True):
+                 fine_ts_width=0, enable_replace=True,
+                 delay=0):
         self.stb = Signal()
         self.busy = Signal()
 
@@ -16,17 +17,22 @@ class OInterface:
 
         self.enable_replace = enable_replace
 
+        if delay < 0:
+            raise ValueError("only positive delays allowed", delay)
+        self.delay = delay
+
     @classmethod
     def like(cls, other):
         return cls(get_data_width(other),
                    get_address_width(other),
                    get_fine_ts_width(other),
-                   other.enable_replace)
+                   other.enable_replace,
+                   other.delay)
 
 
 class IInterface:
     def __init__(self, data_width,
-                 timestamped=True, fine_ts_width=0):
+                 timestamped=True, fine_ts_width=0, delay=0):
         self.stb = Signal()
 
         if data_width:
@@ -34,14 +40,18 @@ class IInterface:
         if fine_ts_width:
             self.fine_ts = Signal(fine_ts_width)
 
-        self.timestamped = timestamped
         assert(not fine_ts_width or timestamped)
+        self.timestamped = timestamped
+        if delay < 0:
+            raise ValueError("only positive delays")
+        self.delay = delay
 
     @classmethod
     def like(cls, other):
         return cls(get_data_width(other),
                    other.timestamped,
-                   get_fine_ts_width(other))
+                   get_fine_ts_width(other),
+                   delay)
 
 
 class Interface: