Enabling MTS

Hi,

Can anyone offer any advice on enabling MTS using PYNQ?

Previously I was running a bare metal application which had adcMTS and dacMTS functions defined.

I’ve seen this post:

and this one:

That talk about editing the xrfdc init.py (I assume that is init.py in /usr/local/share/pynq-venv/lib/python3.8/site-packages/xrfdc?)

I’ve attempted that method but i’m getting the following error when I attempt to RunMTS:

rfdc.RunMTS()


KeyError Traceback (most recent call last)
/usr/local/share/pynq-venv/lib/python3.8/site-packages/cffi/api.py in _typeof(self, cdecl, consider_function_as_funcptr)
182 try:
→ 183 result = self._parsed_types[cdecl]
184 except KeyError:

KeyError: ‘XRFdc_MultiConverter_Sync_Config*’

During handling of the above exception, another exception occurred:

ParseError Traceback (most recent call last)
/usr/local/share/pynq-venv/lib/python3.8/site-packages/cffi/cparser.py in _parse(self, csource)
335 try:
→ 336 ast = _get_parser().parse(fullcsource)
337 except pycparser.c_parser.ParseError as e:

/usr/lib/python3/dist-packages/pycparser/c_parser.py in parse(self, text, filename, debuglevel)
148 self._last_yielded_token = None
→ 149 return self.cparser.parse(
150 input=text,

/usr/lib/python3/dist-packages/ply/yacc.py in parse(self, input, lexer, debug, tracking, tokenfunc)
332 else:
→ 333 return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc)
334

/usr/lib/python3/dist-packages/ply/yacc.py in parseopt_notrack(self, input, lexer, debug, tracking, tokenfunc)
1200 self.state = state
→ 1201 tok = call_errorfunc(self.errorfunc, errtoken, self)
1202 if self.errorok:

/usr/lib/python3/dist-packages/ply/yacc.py in call_errorfunc(errorfunc, token, parser)
191 _restart = parser.restart
→ 192 r = errorfunc(token)
193 try:

/usr/lib/python3/dist-packages/pycparser/c_parser.py in p_error(self, p)
1844 if p:
→ 1845 self._parse_error(
1846 ‘before: %s’ % p.value,

/usr/lib/python3/dist-packages/pycparser/plyparser.py in _parse_error(self, msg, coord)
66 def _parse_error(self, msg, coord):
—> 67 raise ParseError(“%s: %s” % (coord, msg))
68

ParseError: :2:33: before: *

During handling of the above exception, another exception occurred:

CDefError Traceback (most recent call last)
in
----> 1 rfdc.RunMTS()

/usr/local/share/pynq-venv/lib/python3.8/site-packages/xrfdc/init.py in RunMTS(self)
404
405 def RunMTS(self):
→ 406 self._DAC_Sync_Config = _ffi.new(“XRFdc_MultiConverter_Sync_Config*”)
407 self._ADC_Sync_Config = _ffi.new(“XRFdc_MultiConverter_Sync_Config*”)
408 _lib.XRFdc_MultiConverter_Init(self._DAC_Sync_Config, _ffi.new(“int*”), _ffi.new(“int*”))

/usr/local/share/pynq-venv/lib/python3.8/site-packages/cffi/api.py in new(self, cdecl, init)
264 “”"
265 if isinstance(cdecl, basestring):
→ 266 cdecl = self._typeof(cdecl)
267 return self._backend.newp(cdecl, init)
268

/usr/local/share/pynq-venv/lib/python3.8/site-packages/cffi/api.py in _typeof(self, cdecl, consider_function_as_funcptr)
184 except KeyError:
185 with self._lock:
→ 186 result = self._typeof_locked(cdecl)
187 #
188 btype, really_a_function_type = result

/usr/local/share/pynq-venv/lib/python3.8/site-packages/cffi/api.py in _typeof_locked(self, cdecl)
169 cdecl = cdecl.encode(‘ascii’)
170 #
→ 171 type = self._parser.parse_type(cdecl)
172 really_a_function_type = type.is_raw_function
173 if really_a_function_type:

/usr/local/share/pynq-venv/lib/python3.8/site-packages/cffi/cparser.py in parse_type(self, cdecl)
550
551 def parse_type(self, cdecl):
→ 552 return self.parse_type_and_quals(cdecl)[0]
553
554 def parse_type_and_quals(self, cdecl):

/usr/local/share/pynq-venv/lib/python3.8/site-packages/cffi/cparser.py in parse_type_and_quals(self, cdecl)
553
554 def parse_type_and_quals(self, cdecl):
→ 555 ast, macros = self._parse(‘void __dummy(\n%s\n);’ % cdecl)[:2]
556 assert not macros
557 exprnode = ast.ext[-1].type.args.params[0]

/usr/local/share/pynq-venv/lib/python3.8/site-packages/cffi/cparser.py in _parse(self, csource)
336 ast = _get_parser().parse(fullcsource)
337 except pycparser.c_parser.ParseError as e:
→ 338 self.convert_pycparser_error(e, csource)
339 finally:
340 if lock is not None:

/usr/local/share/pynq-venv/lib/python3.8/site-packages/cffi/cparser.py in convert_pycparser_error(self, e, csource)
365 else:
366 msg = ‘parse error\n%s’ % (msg,)
→ 367 raise CDefError(msg)
368
369 def parse(self, csource, override=False, packed=False, pack=None,

CDefError: cannot parse “XRFdc_MultiConverter_Sync_Config*”
:2:33: before: *

2 Likes

Hi, In order to do MTS designs you have to read PG269 and become familiar with the clock distribution of the RFSoC device. That document has a section on Multi Tile Sync.

MTS involves using the XRFDC library to enable MTS. PYNQ provides a Python wrapper around the XRFDC library, so you can use Python to set up MTS mode. I don’t know of any examples online that provide working Python code to do that. Invoking the mode is really not all that complicated, understanding how to do the hardware design and work with the clock restrictions of MTS is a little more complicated.

There are no complete tutorials here on this forum yet. But there is a post for the 2x2 that discusses some of the fundamentals but is not entirely complete and ignores using Python to set the mode:

If you have a good hardware design and you have bare metal C working, then look through the PYNQ xrfdc library for the methods that you are using now. If they don’t exist then you will have to wrap them yourself just as in the example that you posted above.

It’s been on my chore list to create a MTS tutorial hardware design that works on a PYNQ RFSoC board but I have not found the time to do it. Don’t hold your breath for me :slight_smile:

Kind regards

2 Likes

Hi @pynqzen

That’s what I thought I was doing. I can’t understand why i’m getting the:
CDefError: cannot parse “XRFdc_MultiConverter_Sync_Config*”
error. If i’ve understood it correctly, the struct “XRFdc_MultiConverter_Sync_Config” exists in the xrfdc.h file that’s being compiled in to libxrfdc.so which is loaded by the init.py using:
_lib = _ffi.dlopen(os.path.join(_THIS_DIR, ‘libxrfdc.so’))

After looking at this further, it looks like I need to add the missing structures to the xrfdc_functions.c file.

1 Like

So I had to add the following to xrfdc_functions.c (located /usr/local/share/pynq-venv/lib/python3.8/site-packages/xrfdc/xrfdc_functions.c):

typedef struct {
u32 RefTile;
u32 IsPLL;
int Target[4];
int Scan_Mode;
int DTC_Code[4];
int Num_Windows[4];
int Max_Gap[4];
int Min_Gap[4];
int Max_Overlap[4];
} XRFdc_MTS_DTC_Settings;

typedef struct {
u32 RefTile;
u32 Tiles;
int Target_Latency;
int Offset[4];
int Latency[4];
int Marker_Delay;
int SysRef_Enable;
XRFdc_MTS_DTC_Settings DTC_Set_PLL;
XRFdc_MTS_DTC_Settings DTC_Set_T1;
} XRFdc_MultiConverter_Sync_Config;

and the following function definitions:

void XRFdc_MultiConverter_Init(XRFdc_MultiConverter_Sync_Config *ConfigPtr, int *PLL_CodesPtr, int *T1_CodesPtr);
u32 XRFdc_MultiConverter_Sync(XRFdc *InstancePtr, u32 Type, XRFdc_MultiConverter_Sync_Config *ConfigPtr);

and then in _init_.py I added the following to the RFdc class:

def dacMTS(self, tiles):
    factor = _ffi.new("unsigned int*")
    self._DAC_Sync_Config = _ffi.new('XRFdc_MultiConverter_Sync_Config*')
    _lib.XRFdc_MultiConverter_Init(self._DAC_Sync_Config, _ffi.new("int*"), _ffi.new("int*"))
    self._DAC_Sync_Config.Tiles = tiles
    print("DAC_Sync_Config.Tiles: {0:#x}".format(tiles))
    status = _lib.XRFdc_MultiConverter_Sync(self._instance, _lib.XRFDC_DAC_TILE,self._DAC_Sync_Config)
    if status != _lib.XRFDC_MTS_OK:
        print("XRFdc_MultiConverter_Sync() FAILED with status = {}".format(status))
    else:
        print("========== DAC Multi-Tile Sync Report ==========")
        for i in range(0,4):
            if (1<<i) & self._DAC_Sync_Config.Tiles:
                _lib.XRFdc_GetInterpolationFactor(self._instance, i, 0, factor)
                print("DAC{}: Latency(T1) = {}, Adjusted Delay \
                        Offset({}) = {}, Marker Delay = {}".format(i, self._DAC_Sync_Config.Latency[i],
                        factor, self._DAC_Sync_Config.Offset[i], self._DAC_Sync_Config.Marker_Delay))
                print("=== MTS DAC Tile{} PLL Report ===".format(i))
                print("    DAC{}: PLL DTC Code ={} ".format(i, self._DAC_Sync_Config.DTC_Set_PLL.DTC_Code[i]))
                print("    DAC{}: PLL Num Windows ={} ".format(i, self._DAC_Sync_Config.DTC_Set_PLL.Num_Windows[i]))
                print("    DAC{}: PLL Max Gap ={} ".format(i, self._DAC_Sync_Config.DTC_Set_PLL.Max_Gap[i]))
                print("    DAC{}: PLL Min Gap ={} ".format(i, self._DAC_Sync_Config.DTC_Set_PLL.Min_Gap[i]))
                print("    DAC{}: PLL Max Overlap ={} ".format(i, self._DAC_Sync_Config.DTC_Set_PLL.Max_Overlap[i]))
                print("=== MTS DAC Tile{} T1 Report ===".format(i))
                print("    DAC{}: T1 DTC Code ={} ".format(i, self._DAC_Sync_Config.DTC_Set_T1.DTC_Code[i]))
                print("    DAC{}: T1 Num Windows ={} ".format(i, self._DAC_Sync_Config.DTC_Set_T1.Num_Windows[i]))
                print("    DAC{}: T1 Max Gap ={} ".format(i, self._DAC_Sync_Config.DTC_Set_T1.Max_Gap[i]))
                print("    DAC{}: T1 Min Gap ={} ".format(i, self._DAC_Sync_Config.DTC_Set_T1.Min_Gap[i]))
                print("    DAC{}: T1 Max Overlap ={}".format(i, self._DAC_Sync_Config.DTC_Set_T1.Max_Overlap[i]))

        print("DAC Multi-Tile Synchronization is complete.")
        print("###############################################")

and a similar function for the ADCs. I also added:

def printMTSLatency(self):
    for i in range(4):
        print("DAC-Latency: {}".format((self._DAC_Sync_Config.Latency[i])))
        print("DAC-Offset: {}".format((self._DAC_Sync_Config.Offset[i])))
    for i in range(4):
        print("ADC-Latency: {}".format((self._ADC_Sync_Config.Latency[i])))
        print("ADC-Offset: {}".format((self._ADC_Sync_Config.Offset[i])))

As suggested in another post.

Now if I run:

rfdc.dacMTS(0xf)

I get:

DAC_Sync_Config.Tiles: 0xf
========== DAC Multi-Tile Sync Report ==========
DAC0: Latency(T1) = 720, Adjusted Delay Offset(<cdata ‘unsigned int *’ owning 4 bytes>) = 0, Marker Delay = 15
=== MTS DAC Tile0 PLL Report ===
DAC0: PLL DTC Code =-1
DAC0: PLL Num Windows =0
DAC0: PLL Max Gap =0
DAC0: PLL Min Gap =0
DAC0: PLL Max Overlap =0
=== MTS DAC Tile0 T1 Report ===
DAC0: T1 DTC Code =11
DAC0: T1 Num Windows =5
DAC0: T1 Max Gap =21
DAC0: T1 Min Gap =20
DAC0: T1 Max Overlap =1
DAC1: Latency(T1) = 720, Adjusted Delay Offset(<cdata ‘unsigned int *’ owning 4 bytes>) = 0, Marker Delay = 15
=== MTS DAC Tile1 PLL Report ===
DAC1: PLL DTC Code =-1
DAC1: PLL Num Windows =0
DAC1: PLL Max Gap =0
DAC1: PLL Min Gap =0
DAC1: PLL Max Overlap =0
=== MTS DAC Tile1 T1 Report ===
DAC1: T1 DTC Code =13
DAC1: T1 Num Windows =5
DAC1: T1 Max Gap =20
DAC1: T1 Min Gap =18
DAC1: T1 Max Overlap =1
DAC2: Latency(T1) = 720, Adjusted Delay Offset(<cdata ‘unsigned int *’ owning 4 bytes>) = 0, Marker Delay = 15
=== MTS DAC Tile2 PLL Report ===
DAC2: PLL DTC Code =-1
DAC2: PLL Num Windows =0
DAC2: PLL Max Gap =0
DAC2: PLL Min Gap =0
DAC2: PLL Max Overlap =0
=== MTS DAC Tile2 T1 Report ===
DAC2: T1 DTC Code =37
DAC2: T1 Num Windows =5
DAC2: T1 Max Gap =21
DAC2: T1 Min Gap =20
DAC2: T1 Max Overlap =1
DAC3: Latency(T1) = 720, Adjusted Delay Offset(<cdata ‘unsigned int *’ owning 4 bytes>) = 0, Marker Delay = 15
=== MTS DAC Tile3 PLL Report ===
DAC3: PLL DTC Code =-1
DAC3: PLL Num Windows =0
DAC3: PLL Max Gap =0
DAC3: PLL Min Gap =0
DAC3: PLL Max Overlap =0
=== MTS DAC Tile3 T1 Report ===
DAC3: T1 DTC Code =11
DAC3: T1 Num Windows =5
DAC3: T1 Max Gap =21
DAC3: T1 Min Gap =20
DAC3: T1 Max Overlap =1
DAC Multi-Tile Synchronization is complete.
###############################################

However, for the ADC:

rfdc.adcMTS(0xf)

ADC_Sync_Config.Tiles: 0xf
XRFdc_MultiConverter_Sync() FAILED with status = 128

Return code 128 is
#define XRFDC_MTS_DTC_INVALID 128U

Does anyone know what that means?

and if I do:

rfdc.printMTSLatency()

DAC-Latency: 720
DAC-Offset: 0
DAC-Latency: 720
DAC-Offset: 0
DAC-Latency: 720
DAC-Offset: 0
DAC-Latency: 720
DAC-Offset: 0
ADC-Latency: 120
ADC-Offset: 2
ADC-Latency: 120
ADC-Offset: 2
ADC-Latency: 120
ADC-Offset: 0
ADC-Latency: 120
ADC-Offset: 2

I’m obviously wary of the ADC results since enabling MTS appeared to fail. Does anyone know if those DAC results are ok?

1 Like

Hi adi8v,

There is not enough information in your post here for me to fully debug your issue. Can you confirm that you are complying with the SYSREF and other clocking requirements described in the SYSREF Signal Requirements section of PG269:

https://docs.xilinx.com/r/en-US/pg269-rf-data-converter/SYSREF-Signal-Requirements

Also, you may find the audience to be more knowledgeable about MTS on the AMD forums for RFSoC.

Kind regards

1 Like

One other thought that came to mind to share. When you are wrapping some of the XRFDC libraries yourself, be sure to keep an eye on any #defines in the headers as well. The XRFDC source supports multiple generations of part variations, baremetal, petalinux and microblaze too.

Kind regards

1 Like

I am also facing similar problem trying to setup MTS on ZCU216 running PYNQ 3.0.
I got the same error 128 return from XRFdc_MultiConverter_Sync()

@adi8v Have you got the problem solved? Would you please update a little bit?

@pynqzen 1. How can we get the more detailed debug information as mentioned on the xilinx forum to debug this? https://support.xilinx.com/s/article/71068?language=en_US

The question has been raised on xilinx forum, but got no answer yet.
https://support.xilinx.com/s/question/0D52E00007H5jkCSAR/rfsoc-pynq-enable-metallog-adc-mts-failure?language=en_US

I tried
#define ENABLE_METAL_PRINTS 7
in the xrfdc_functions.c and it doesn’t help.

  1. How can I verify the version of the libxrfdc.so? The declaration of the
    XRFdc_MultiConverter_Init have a little change between 2.4 and 2.6. I think that should match the version of the RFDC IP.