Sub-decoder scripts
A sub-decoder is a decoder that is called by another “high level” decoder (also known as a nested decoder). By “high level”, we mean a decoder that is accessed by the user, for which a GUI is displayed. A typical application of sub-decoders, is to build a temperature sensor protocol decoder that is based on a low level I2C decoder. Being able to call a sub-decoder from the high level decoder prevent the high level script from (re)implementing the whole I2C decoding layer. In that specific scenario, the low level sub-decoder would interpret the logic signals and output I2C packets. The high level temperature sensor decoder would interpret I2C packets provided by the sub-decoder and provide meaningful temperature information. Below is an example architecture of decoder/sub-decoder system.
Note: Along this documents, the terms “sub-decoder” and “low level decoders” are both used and have the same meaning.
The process of having a sub-decoder interpret the logic signals and provide decoded packets for another high level decoder is referred to as “pre-decoding”.
Any decoder can become a sub-decoder provided that it’s called by another decoder.
A decoder used as a sub-decoder is not “aware” of that situation, thus, it’s the high level decoder (the one calling the sub-decoder) to provide all the information needed by the sub-decoder to operate properly. This information is nothing more than GUI values that the sub-decoder would normally rely on to operate. As described in the GUI functions chapter, a sub-decoder cannot display it’s GUI to the user, but special GUI elements called “hidden elements” can be used to trick the sub-decoder into behaving as if its GUI was displayed and configured by the user.
Let’s look at things from a programming perspective: a high-level script must ensure that when a low level scripts calls the function gui_get_value
for a specific GUI ID, it gets a correct value. There are two ways to do that:
- Create hidden elements with the exact same ID as the one used in the sub-decoder’s GUI (the method described above).
- Use the same GUI IDs in the high level decoder and sub-decoder, when this is possible and makes sense.
Of course, it’s also possible to mix those two solutions. For example, the SDA and SCL channels for an I2C decoder is a shared information between the high-level decoder and sub-decoder, so it would be wise to give the channel selector GUI elements the same ID chosen by the sub-decoder; this way they can share the same GUI element.
Next comes the most important part of the process, the pre-decoding: calling the sub-decoder and making use of the decoded data. This is simply done by calling the pre-decode()
function as described below. This function will usually take some time to execute (depending on the quantity of logic signals to be analyzed). Then, it will return the newly decoded data packets. Those packets are called “decoder items” and are simply objects of the type dec_item_t
as defined by this JavaScript constructor:
//dev_item_t constructor
function dec_item_t(channel_index, start_sample_index, end_sample_index, content)
{
this.channel_index = channel_index;
this.start_sample_index = start_sample_index;
this.end_sample_index = end_sample_index;
this.content = content;
}
As you may have recognized, this data type simply encapsulates all the information that the script provides for ScanaStudio to draw the decoder items on the waveforms. Once you get hold of this array of dec_item_t
, you can very quickly work on your high level decoding without worrying about the details of low level decoding. Another big advantage of this technique, is that your high-level decoder benefits from all the evolutions and bug corrections that are made over the time to the low level decoder. This has one downside though: you may need - from time to time - to adjust your high level decoder script if some modifications are made to the low level script that are not backward compatible with older versions.
Please note that a sub-decoder may only create decoder items. Any attempts from a sub decoder to add items to packet view or hex view will be silently ignored without generating errors or warnings.
Also when a sub-decoder adds content to a decoder item, only the first version of that content is taken into consideration, e.g.:
ScanaStudio.dec_item_add_content("ACKNOWLEDGE"); //Only this line will be considered when pre-decoding
ScanaStudio.dec_item_add_content("ACK"); //Will be ignored when pre-decoding
ScanaStudio.dec_item_add_content("A"); //Will be ignored when pre-decoding
A decoder script can detect if it’s called directly from ScanaStudio, or if it’s called by another script using the function is_pre_decoding()
. This can be used to adapt the output of a decoder to be easily used by another script.
ScanaStudio.pre_decode(“dec_name”,resume)
Description: This function calls the on_decode_signals(resume)
function in the decoder dec_name
Parameters:
- “dec_name” : The name of the script where the
on_decode_signals
function should be called. This name must include the extension *.js, for example: “i2c.js”. - resume : The resume parameter as described in the
on_decode_signals
documentation.
Return value: Returns an array of dec_item_t
with the newly decoded packets.
Context: Protocol decoder.
Example
Here is an example of implementation of the pre_decode
function that shows how simple a script can get, provided that you use a sub-decoder to do all the complicated low level processing:
function on_decode_signals(resume)
{
var decoder_items = ScanaStudio.pre_decode("my_low_level_decoder.js",resume);
for (i=0; i < decoder_items.length; i++)
{
//Interpret decoder_items[i] and create new decoder items with your interpreted data
ScanaStudio.dec_item_new(decoder_items[i].channel_index,decoder_items[i].start_sample_index,decoder_items[i].end_sample_index);
var my_new_data = decoder_items[i].data + 1; //create new data based on low level data
ScanaStudio.dec_item_set_data(my_new_data);
}
}
ScanaStudio.is_pre_decoding()
Description: This function returns true if the script’s on_decode_signals
function is being called by another script, i.e. using the pre_decode
function.
Context: Protocol decoder
Implementing a sub-decoder in your decoder
In this section, we’re going to present a standard methodology that we recommend when one needs to build a decoder based on another (sub) decoder. The process we recommend can be split in several steps:
-
Start by copying the content of the
on_draw_gui_decoder()
function into your own script. -
Create a
on_decode_signals()
function that simply displays the sub-decoder items without any processing, e.g.:
function on_decode_signals(resume)
{
items = ScanaStudio.pre_decode("sm-bus.js",resume);
var i;
for (i = 0; i < items.length; i++)
{
ScanaStudio.dec_item_new(items[i].channel_index,items[i].start_sample_index,items[i].end_sample_index);
ScanaStudio.dec_item_add_content(items[i].content);
}
}
- Test the decoder with some arbitrary signals, and ensure signals are displayed correctly.
- Start modifying the
on_draw_gui_decoder()
andon_decode_signals()
functions to implement your high-level decoder that processes the low-level decoder items. When modifyingon_draw_gui_decoder()
, ensure that each GUI item you remove is replaced by a “hidden” GUI value (to ensure the low level decoder has access to all the information needed to operate correctly).