Hello all, I am interested in the following functionality. I haven't found it referenced in documentation anywhere, but wondering if anyone knows of a way to do this or has tried anything similar.

I have a setup with a 2821 ttl card connected to a Kasli core. I would like to send an 8-bit binary number to each of the 8 ttl outputs on the card and have all the ttls output corresponding pulse sequences in parallel, at a pre-set frequency.

The methods I have found to control ttl outputs from the core are on(), off(), pulse(), or set_o(). If I wish to convert a binary number to a pulse sequence using any of these methods, I appear to have to use logic, for example (in pseudocode):

for i in binarynumber:
if i = 1:
ttl.pulse(period)
else:
delay(period)

This technique appears to run very slowly, even when I run it in the core's digital memory access. This slowness becomes a problem when I try to send pulses along multiple channels in parallel; if I try to make the period any shorter than 5 MHz, I run into RTIO underflow errors. Ideally, I would like to send pulses at a frequency of 25 MHz. Is there any way to use a binary number to directly switch a ttl on and off? This is an option on simpler FPGA's, which is the main reason that I would expect I could do something similar on sinara. Thanks!

    mpr2 This is an option on simpler FPGA's

    Sure, you can also program the FPGA on Kasli like you program those "simpler" ones.

    What are you actually trying to do? The TTL interface was not designed for data transfer.

    • mpr2 replied to this.

      Thanks, sb10q. Is there any place I could find the methods you allude to? Ideally I am looking for a method that switches a ttl between high and low depending on whether it's input is 1 or 0, or something similar.

      This is not formal data transfer per se. I am attempting to program a device that has to receive the same series of 8 bit numbers along five channels with a particular kind of time synchronization between the channels.

      In practice, I would like to feed the core a list of time delays for each ttl channel. The core will then iterate through that list of delays, changing the ttl's state after every delay has elapsed.

      By 'digital memory access', did you mean direct memory access (DMA)? What exactly did you try? How long it takes to calculate/generate a pulse sequence principally should have no influence on how long it takes to play it back: unless I'm completely wrong a DMA trace only records operations, not how you decided on them. How far in advance are these binary numbers available?

      There is the internal method rtio_output in coredevice.rtio, which directly takes 0 or 1, and is synonymous to off() or on() when used with a self.ttl.target_o, but I'm not sure that's the solution to your problem.

      Yes, apologies for the typo I do mean direct memory access.

      For future reference, I found a way to do the logic-free conversion from a binary string to a pulse sequence that I was initially asking for by running:

      for i in bins:
         ttl.set_o(bool(bins[i])
          delay(period)

      where bins is a list with entries corresponding to each digit in the binary number. I’m sure I could skip the list step and do this even more simply directly from the binary number if I so desired. As @architeuthis and @sb10q suggested though, this does not allow me to program pulses any faster without running into an underflow.

      Instead, I can program pulses much faster by simplifying how I program the sequence; rather than sending one pulse per bit of each 8 bit word, I instead assemble a list of delays; each delay corresponds to the amount of time that passes between a ttl switching from high to low, or vice versa. This list is much shorter than the word and by using it, I can send pulse sequences at a bitrate of over 70 Mhz without running into underflows; this is more than enough for my purposes.

      The funny thing is that although I can send a pulse sequence at frequencies exceeding 70 MHz without triggering an underflow, I do trigger an underflow if I try to send that sequence multiple times in a row with less than about .5 second delay in between rounds. This appears counter to the basic purpose of DMA, and leads me to conclude that I must be misusing it in some way. The relevant structure of my code is:

      @kernel
      def record(self):
        with self.core_dma.record("pulses"):
           for i in range(0,len(self.delays)):
                delay(float(self.delays[i])*period)
                if i%2 != 0:
                     self.ttl.on()
                 else:
                      self.ttl.off()  
      
       
      
      @kernel
      def run(self):
          self.core.reset()
          self.record()
          pulses_handle = self.core_dma.get_handle("pulses")
          self.core.break_realtime()
          while True:
              delay(self.wait_time)    
              self.core_dma.playback_handle(pulses_handle)

      Is there anything in my record method that is slowing me down unnecessarily? Nothing stands out to me.

        mpr2 This list is much shorter than the word and by using it, I can send pulse sequences at a bitrate of over 70 Mhz without running into underflows

        70MHz sounds way too fast and I think this measurement is incorrect.

        Anyway if you're trying to do data communication you're better off with the SPI core or custom gateware, not TTL.