Architecture of a script

This chapter will give you a more exhaustive view of the base construction blocks of a script. It will address the different kind of interactions that can take place between your script and ScanaStudio software.

A script is composed of three main parts:

  • Meta-information
  • One or more entry point functions
  • User code, that is, all the rest of your script.

Any script must contain some meta-information, which is some text that describes what your script does, what’s its version or who is the author. Think of the Meta-information as the ID card of your script.

A script must also contain at least one entry point function. Entry point functions are called by ScanaStudio when it’s time for your script to execute the task it was made for.

Both meta-information and entry point functions will be discussed in details in the following sections.

Meta-information

As stated above, meta-information is like an ID card of your script. It can be placed anywhere in the script, but for the sake of harmonization, we recommend that you place it at the very top of your script. Meta-information is composed of a commented bloc of code, containing meta tags. Each tag encapsulates a specific information as shown in the example below:

/*
<NAME> My script name </NAME>
<DESCRIPTION>
Description of my script. It can decode my custom protocol,
and it can generate trigger sequences for that specific protocol.
</DESCRIPTION>
<VERSION> 1.1.2 </VERSION>
<AUTHOR_NAME>	My name </AUTHOR_NAME>
<AUTHOR_URL> my_website_or_email.com </AUTHOR_URL>
<HELP_URL> link to a documentation for your script, e.g.:
https://github.com/ikalogic/ScanaStudio-scripts-v3/wiki/SPI-script-documentation </HELP_URL>
<COPYRIGHT> Copyright 2018 Myself or my company </COPYRIGHT>
<LICENSE>	This code is distributed under the terms of the
GNU General Public License GPLv3 </LICENSE>
<RELEASE_NOTES>
V1.1.2:  Fixed anther bug.
V1.1.1:  Fixed some bug.
V1.1.0:  Added some feature.
V1.0.0:  Initial release.
</RELEASE_NOTES>
*/

As you may have noticed, this is inspired from the way HTML or XML tags work. It’s highly recommended that each script implements this full list of meta-info tags. This will allow ScanaStudio to provide consistent and harmonized information to the user of your script.

Entry points functions

Each and every script (that is to be supported by ScanaStudio) need entry point functions to operate. These are called by ScanaStudio when a specific function is to be carried by your script.

A summary of the entry point functions is listed in the following table:

Function Script Context Description
on_decode_signals() Protocol decoder Called when ScanaStudio needs to decode captured signals.
on_draw_gui_decoder() Protocol decoder Called when ScanaStudio needs to show protocol decoder configuration GUI.
on_eval_gui_decoder() Protocol decoder Called when ScanaStudio needs to evaluate if the decoder GUI configuration is valid
on_build_trigger() Trigger Called when ScanaStudio need to generate a trigger sequence.
on_draw_gui_trigger() Trigger Called when ScanaStudio needs to show the trigger GUI
on_eval_gui_trigger() Trigger Called when ScanaStudio needs to evaluate if the Trigger GUI configuration is valid.
on_build_signals() Signal builder Called when the user requests signals to be built.
on_build_demo_signals() Signal builder Called when the user tries to run a workspace that has your script without any device connected.
on_draw_gui_signal_builder() Signal builder Called when ScanaStudio needs to show the signal builder GUI
on_eval_gui_signal_builder() Signal builder Called when ScanaStudio needs to evaluate if the signal builder GUI configuration is valid.

Note: Each and every one of those functions will be discussed in details in dedicated sections of this document.

Not all of those entry point functions need to be implemented in your script. It all depends on what features you want your script to have. ScanaStudio will parse your script searching for those “special functions”, and will automatically detect which features are supported by your script.

Obviously, those entry point function’s names are reserved only for this purpose. You should never use the names of those functions as regular user function. Also, you should never call entry point functions from within the script as this can lead to unexpected behavior.

You may notice the word “Script context” was mentioned in the table above. The Script context simply defines what your script is allowed to do when a particular entry point function is called. In other words, some of the functions exposed by the ScanaStudio object are only available for a particular context.

For instance, if the on_decode_signals function is called - in the Protocol decoder context - the ScanaStudio object will expose functions that allows fetching captured signals and creating decoded data elements (that appears on the waveform). Example of a function available in this context:

ScanaStudio.trs_reset(0); // reset the transitions iterator for channel 0

Similarly, in the protocol decoder context, functions related to building signal cannot be called. For example, the following code is invalid if called from protocol decoder context:

ScanaStudio.builder_add_samples(0,1,200); //Add 200 HIGH LEVEL samples on channel 0

However, some functions in the ScanaStudio objects are available across all contexts (global context), like the functions used to display console messages:

ScanaStudio.console_info_msg("Hello World"); //Valid in all contexts

As you may have noticed, the entry point function called to launch your script defines the context during the whole time the script is executed. Context can never be changed unless another entry function is called.

Usually, you don’t need to worry about entry points and their contexts: you should never need to use signal building functions when decoding signals, and vice-versa. If at some point you feel limited by the context of the script, that probably means that you’re doing something wrong and that it’s time to rethink the architecture of your script.

Along this document, all function available through ScanaStudio object are described and its context clearly stated.

Anatomy of a simple script

Before we get deeper into the exhaustive list of functions and methods used to build sophisticated scripts, let’s analyze a very simple script to get a global view of the architecture of a script and how different parts, work together. Let’s imagine we want to build a script that will calculate the frequency of signals, and display the frequency on the waveform as shown below.

Result of Frequency decoder script

Such a script would have two mandatory entry-point functions:

  • A function to draw the GUI that allows the user of the script to select which channel to decode (this entry-point function needs to be named on_draw_gui_decoder())
  • A function to decode the logic signals into meaningful, frequency values expressed in Hz, Khe or Mhz. This entry-function, as you may have guessed, needs to be named on_decode_signals(resume)

Building the GUI

For that simple script, the GUI is very minimalistic, since there is only one option that needs to be set (or changed) by the user of the script, which is the channel which shall be analyzed by this decoder.

Frequency decoder GUI

To create such a GUI, first, we have to create the on_draw_gui_decoder() function: the entry-point function which will be called by ScanaStudio when the GUI needs to be redrawn.

function on_draw_gui_decoder()
{
  ScanaStudio.gui_add_ch_selector("freq_ch","Channel to analyze","Freq");
}

The function ScanaStudio.gui_add_ch_selector(...) simply tells ScanaStudio to add a GUI component called “ch_selector” (Channel selector). As the name implies, this adds a combo box with the list of channels supported by the device currently used. This function takes 3 parameters:

  1. The ID of the GUI element. This should be a unique text string, and it will be used later to retrieve the value of the combo box (the user’s choice). In our example, we used “freq_ch”.
  2. The text label to show next to the combo box.
  3. The default (new) channel name. A decoder can prompt the user to rename the labels of logic analyzer channels to something more meaningful than the standard “Channel n” label. In our case, we can prompt the user to rename the channel to “freq”.

Note: The title of the GUI cannot be changed via the on_draw_gui_decoder() function. It is automatically generated from the meta-information provided by the script.

Decoding the signals

Another entry-point function needs to be implemented: the on_decode_signals(resume) function. This function is called each time ScanaStudio needs to decode logic signals using that script. In some situations, this function may be called repeatedly as new logic signals come in (in case of a logic analyzer that supports live data stream). The function argument “resume” is true if it’s a repeated function call for the same capture. At the very first call to that function, the resume parameter is false. All global variables retain their value between different calls with resume == true.

function on_decode_signals(resume)
{
  if (!resume) //If resume == false, it's the first call to this function.
  {
      //initialization code goes here, ex:
      ScanaStudio.console_info_msg("Frequency analyzer initialized");
      state_machine = 0;
      sampling_rate = ScanaStudio.get_capture_sample_rate();
      freq_ch = ScanaStudio.gui_get_value("freq_ch");
      ScanaStudio.trs_reset(freq_ch); //reset the trs iterator.
      last_falling_edge = last_rising_edge = -1;
  }

  while (ScanaStudio.abort_is_requested() == false)
  {
    if (!ScanaStudio.trs_is_not_last(freq_ch))
    {
      break;
    }
    switch (state_machine)
    {
      case 0: //search for rising edge
        trs = ScanaStudio.trs_get_next(freq_ch); //fetch next transition
        if (trs.value == 1) { //Rising edge found?
          if (last_rising_edge != -1){ //if it's not the very first rising edge
            period = (trs.sample_index - last_rising_edge) / sampling_rate; //period in [s]
            frequency = 1/period; //in [Hz]
            ScanaStudio.dec_item_new(freq_ch,last_rising_edge,trs.sample_index);
            ScanaStudio.dec_item_add_content("F = " + ScanaStudio.engineering_notation(frequency,3)         + "Hz");
          }
          last_rising_edge = trs.sample_index;
          state_machine++;
        }
        break;
      case 1: //search for falling edge
        trs = ScanaStudio.trs_get_next(freq_ch); //fetch next transition
        if (trs.value == 0){ //Falling edge found?
          last_falling_edge = trs.sample_index;
          state_machine = 0;
        }
        break;
      default:
        state_machine = 0;
    }
  }
}

We’re not going to analyze this script in detail for the time being, but it’s worth noting that it’s fully functional.

Putting the finishing touches

Finally you should add a meta-information bloc (or update it if you only modified the script). For our example, we’ll write the following meta-information bloc:

/*
<NAME> Frequency decoder </NAME>
<DESCRIPTION>
Analyze logic signals to shows its frequency. This script's main aim
is to provide a simple example to be inspired from when creating a
new decoder.
</DESCRIPTION>
<VERSION> 0.0 </VERSION>
<AUTHOR_NAME>  Ibrahim KAMAL </AUTHOR_NAME>
<AUTHOR_URL> i.kamal@ikalogic.com </AUTHOR_URL>
<COPYRIGHT> Copyright Ibrahim KAMAL </COPYRIGHT>
<LICENSE>  This code is distributed under the terms of the GNU General Public License GPLv3 </LICENSE>
<RELEASE_NOTES>
V0.0:  Initial release.
</RELEASE_NOTES>
*/