capture1() called from script or console are blocking

Edit (24-Nov-16): That setting a capture from a script or the python console using the API is blocking is the obvious, necessary behavior to avoid wreaking havoc. I blame lack of sleep. However, it’s useful to think about how the blocking is implemented.

Edit (25-Nov-16): Upon the excellent advice of another user, I wrote simple Auxiliary Module to send serial data to target during capture. See below.

I noticed when working through Tutorial B3-2 (Timing Analysis with Power for Attacking TSB) that when a call is made to self.api.capture1() to set a capture AND when the triggering event is the subsequent beginning of a serial transmission (using self.api.getTarget().ser.write()), for example:

    # Stuff. I omitting stuff for brevity's sake
    def run(self):
        # User commands
        
        self.api.connect()

        # Set the relevant parameter in list format including the following
        
        ['OpenADC', 'Trigger Setup', 'Mode', 'falling edge'],
        ['CW Extra Settings', 'Target IOn Pins', 'Target IO1', 'Serial TXD'],
        ['CW Extra Settings', 'Target IOn Pins', 'Target IO2', 'Serial RXD'],
        ['CW Extra Settings', 'Trigger Pins', 'Target IO1 (Serial TXD)', True],
        ['CW Extra Settings', 'Trigger Pins', 'Target IO4 (Trigger Line)', False],

        # More stuff. Then iterative setting of parameters

         # Here is the code which sets the capture and begins serial transmission
        self.api.capture1()
        self.api.getTarget().ser.write('UUUU')

        # More stuff

Then 1) the capture1() call blocks the transmission, 2) the capture is forced triggered at end of timeout period, 3) then the serial data is sent. In other words capture1() cannot be configured to trigger on transmission from the CW since the call to capture1() blocks waiting for a triggering event. I replicated this also through the Python console. This behavior contradicts the description of self.api.capture1() given in Tutorial B3-1:

"The API allows us to press the Capture 1 button and view the power trace without using the GUI. There are two relevant commands here:

self.api.capture1() acts as if we’ve just pressed the Capture 1 button;
"
However, through the GUI setting the capture using the button does not block the CW from sending serial messages (and doing anything else) and the triggering works just fine. So I deduce from my experiments that capture1() can only be used when the triggering event is set before the capture1() call or is external event. And importantly there is some difference between pushing the button in the GUI and using capture1() in a script/from the console.

My question is this: Is there a non-blocking way to set a single capture from a script or the console using capture1() that is then triggered by the execution of code (say, to assert a pin or begin serial transmission) that follow immediately after the call to self.api.capture1()?

I hope this is clear! And I apologize if I’ve left out too many details.

Thanks for your assistance!

Gabe

You may want to create your own Auxiliary module to be executed before, during or after the capture. Take a look at:
chipwhisperer/software/chipwhisperer/capture/auxiliary/_base.py - base class
chipwhisperer/software/chipwhisperer/capture/auxiliary/ResetAVR.py - simple example. You just need to create a similar file and override the methods that you want in the base class.
wiki.newae.com/Adding_Modules/Parameters - tutorial

Tell me later if it works for you.

Ah, excellent! Thanks for the idea, I hadn’t considered that. I will give it a try this weekend and let you know the outcome.

Thanks again,
Gabe

Thanks again for the suggestion. I wrote a simple module that works well for my purpose. I’m glad I was confused about the overall capture process and you suggested adding a module, because it inspired me to really examine the code and understand the process.

Here is module (designed from base class and copying sleep methods from ResetSW1173Read):

#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2013-2014, NewAE Technology Inc
# All rights reserved.
#
# Authors: Colin O'Flynn
#
# Find this and more at newae.com - this file is part of the chipwhisperer
# project, http://www.assembla.com/spaces/chipwhisperer
#
#    This file is part of chipwhisperer.
#
#    chipwhisperer is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    chipwhisperer is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Lesser General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with chipwhisperer.  If not, see <http://www.gnu.org/licenses/>.
#=================================================
import logging

import time
from chipwhisperer.capture.auxiliary._base import AuxiliaryTemplate
from chipwhisperer.common.api.CWCoreAPI import CWCoreAPI
from chipwhisperer.common.utils import util, timer


class SerialBeforeArm(AuxiliaryTemplate):
    '''
    This auxillary module allows for serial data to be sent to the target
    during the capture process i.e., before and after arming the scope
    and after the trace has recorded. This enables the ability for the
    scope to trigger on serial communication that need to start after a
    reset but before the scope is armed and after.
    
    Compare to SimpleSerial send Go Command.
    
    TODO:
    Parser to chop up multiple commands sent in single string seperated by
    whitespace. Modify to iterate over returned lists.

    Uses non-blocking sleep methods poached from ResetCW1173Read.
    
    Gabe 25-NOV-16
    '''

    _name = "Send Serial During Capture"

    def __init__(self):
        AuxiliaryTemplate.__init__(self)
        self.getParams().addChildren([
            {'name':'Pre-Arm Message', 'type':'str', 'key':'prearmmssg', 'value':''},
            {'name':'Post-Arm Message', 'type':'str', 'key':'postarmmssg', 'value':''},
            {'name':'Post-Capture Message', 'type':'str', 'key':'postcapmssg', 'value':''},
            {'name':'Delay (Pre-Message)' , 'type':'int',  'key':'predelay',  'limits':(0, 10E3), 'value':0, 'suffix':' ms'},
            {'name':'Delay (Post-Message)', 'type':'int',  'key':'postdelay', 'limits':(0, 10E3), 'value':0, 'suffix':' ms'},
            {'name':'Test Reset', 'type':'action', 'action':self.testSend}
        ])

    def close(self):
        """Close target, disconnect if required"""
        pass

    def captureInit(self):
        """Called once before each api grouping, do connect etc"""
        pass

    def captureComplete(self):
        """Called once complete api is complete"""
        pass

    def traceArm(self):
        """Before arming the scope, send some serial messages and wait"""
        string = self.findParam('prearmmssg').getValue()
        self.sendSerial(string)

    def traceArmPost(self):
        """After arming the scope, send some serial message and wait"""
        string = self.findParam('postarmmssg').getValue()
        self.sendSerial(string)

    def traceDone(self):
        """After the trace is captured, send some serial messages and wait"""
        string = self.findParam('postcapmssg').getValue()
        self.sendSerial(string)

    def parser(self, string):
        '''Removes whitespace from strings and returns list of multiple commands'''
        pass

    def sendSerial(self, string):
        # Send a string!
        #if CWCoreAPI.getInstance().getTarget().connectStatus.value()==False:
            #raise Warning("Can't write to the target while disconected. Connect to it first.")

        if string is None or len(string) == 0:
            return

        dly = self.findParam('predelay').getValue()
        if dly > 0:
            self.nonblockingSleep(dly / 1000.0)

        CWCoreAPI.getInstance().getTarget().ser.write(string)
        
        dly = self.findParam('postdelay').getValue()
        if dly > 0:
            self.nonblockingSleep(dly / 1000.0)

    def nonblockingSleep_done(self):
        self._sleeping = False

    def nonblockingSleep(self, stime):
        """Sleep for given number of seconds (~50mS resolution), but don't block GUI while we do it"""
        timer.Timer.singleShot(stime * 1000, self.nonblockingSleep_done)
        self._sleeping = True
        while(self._sleeping):
            time.sleep(0.01)
            util.updateUI()

    def testSend(self, _=None):
        self.sendSerial('Hello')

Maybe this will be useful to others.

Gabe

:smiley: