Can't program CW305

I am having great difficulty trying to program the CW305 target board.

I previously had a lot of trouble updating the FW on the connected Chipwhisperer Lite board,
which arrived with old FW (i.e. kept getting the message to upgrade its FW, else problems).
Well the documentation describing how to do this (e.g. using auto_program) didn’t work.
After several dozens different attempts (and dozens of crashes of the Chipwhisperer Jupyter VM),
SOMEHOW I managed to get it updated!

But now it seems like the CW305 board is also running the capture board FW, not the target FW,
and I cannot seem to get it to load the target board FW.

I believe this based upon running the following commands:

    import chipwhisperer as cw
    scope = cw.scope()

giving the following response:

   <Traceback not shown, since it doesn't add much>
    Warning: Multiple chipwhisperers connected, but device and/or serial number not specified.
    Devices:
    ['sn = 50203120324136503130343133333034 (ChipWhisperer Lite)', 'sn = 
    50203120355448513230353237313038 (ChipWhisperer Lite)']

So I determined that the real ChipWhisperer Lite has the first serial number, and I assume that
the second serial number is for the CW305 board.

I tried many different techniques to write a proper CW305_SAM3UFW.bin file to the CW305
(including trying BOSSA,and program_target).

Here is an example set of commands:
import chipwhisperer as cw
import os
print(os.getcwd())
# scope = cw.scope(sn=“50203120324136503130343133333034”)
scope = cw.scope(sn=“50203120355448513230353237313038”)
# scope = cw.scope();

cw.program_target(scope, cw.programmers.AVRProgrammer, 'CW305_SAM3UFW.bin')
# programmer = cw.SAMFWLoader(scope=None)
# programmer.program("/dev/tty1", "cw305_top.bit");
# cw.program_target(scope, cw.programmers.STM32FProgrammer,
#                  r"../hardware/capture/chipwhispererlite/sam3u_fw/SAM3U_VendorExample/
#                       Debug/SAM3U_CW1173.bin")

and the response (sorry it is big):
/home/vagrant/work/projects/chipwhisperer/jupyter/archive
Traceback (most recent call last):
File “/home/vagrant/work/projects/chipwhisperer/software/chipwhisperer/hardware/naeusb/naeusb.py”, line 316, in txrx
response = self.open(serial_number=payload)
File “/home/vagrant/work/projects/chipwhisperer/software/chipwhisperer/hardware/naeusb/naeusb.py”, line 370, in open
dev.set_configuration()
File “/home/vagrant/.pyenv/versions/3.6.7/envs/cw/lib/python3.6/site-packages/pyusb-1.0.2-py3.6.egg/usb/core.py”, line 869, in set_configuration
self._ctx.managed_set_configuration(self, configuration)
File “/home/vagrant/.pyenv/versions/3.6.7/envs/cw/lib/python3.6/site-packages/pyusb-1.0.2-py3.6.egg/usb/core.py”, line 102, in wrapper
return f(self, *args, **kwargs)
File “/home/vagrant/.pyenv/versions/3.6.7/envs/cw/lib/python3.6/site-packages/pyusb-1.0.2-py3.6.egg/usb/core.py”, line 148, in managed_set_configuration
self.backend.set_configuration(self.handle, cfg.bConfigurationValue)
File “/home/vagrant/.pyenv/versions/3.6.7/envs/cw/lib/python3.6/site-packages/pyusb-1.0.2-py3.6.egg/usb/backend/libusb0.py”, line 493, in set_configuration
_check(_lib.usb_set_configuration(dev_handle, config_value))
File “/home/vagrant/.pyenv/versions/3.6.7/envs/cw/lib/python3.6/site-packages/pyusb-1.0.2-py3.6.egg/usb/backend/libusb0.py”, line 431, in _check
raise USBError(errmsg, ret)
usb.core.USBError: [Errno None] b’could not set config 1: Device or resource busy’
/home/vagrant/work/projects/chipwhisperer/software/chipwhisperer/common/utils/util.py:491: UserWarning: getName function is deprecated use get_name instead.
warnings.warn(’{} function is deprecated use {} instead.’.format(cc_func, func.name))
---------------------------------------------------------------------------
USBError Traceback (most recent call last)
~/work/projects/chipwhisperer/software/chipwhisperer/capture/scopes/openadc_interface/naeusbchip.py in con(self, sn)
86 #sn = None
—> 87 found_id = self.dev.con(idProduct=nae_products, serial_number=sn)
88 except (IOError, ValueError):

~/work/projects/chipwhisperer/software/chipwhisperer/capture/scopes/cwhardware/ChipWhispererLite.py in con(self, *args, **kwargs)
     49     def con(self, *args, **kwargs):
---> 50         return self._cwusb.con(*args, **kwargs)
     51 

~/work/projects/chipwhisperer/software/chipwhisperer/hardware/naeusb/naeusb.py in con(self, idProduct, connect_to_first, serial_number)
    633 
--> 634         self.usbseralizer.open(dev['sn'])
    635         foundId = dev['pid']

~/work/projects/chipwhisperer/software/chipwhisperer/hardware/naeusb/naeusb.py in open(self, serial_number)
    160         cmdpacket = self.make_cmd(self.OPEN, serial_number)
--> 161         return self.process_rx(self.txrx(tx=cmdpacket))
    162 

~/work/projects/chipwhisperer/software/chipwhisperer/hardware/naeusb/naeusb.py in process_rx(self, inp)
    153         if resp == self.ERROR:
--> 154             raise payload
    155 

USBError: [Errno None] None

During handling of the above exception, another exception occurred:

Warning                                   Traceback (most recent call last)
<ipython-input-6-4a5fbb9822bf> in <module>
      8 print(os.getcwd())
      9 # scope = cw.scope(sn="50203120324136503130343133333034")
---> 10 scope = cw.scope(sn="50203120355448513230353237313038")
     11 # scope = cw.scope();
     12 

~/work/projects/chipwhisperer/software/chipwhisperer/__init__.py in scope(scope_type, sn)
    209         scope_type = get_cw_type(sn)
    210     scope = scope_type()
--> 211     scope.con(sn)
    212     return scope
    213 

~/work/projects/chipwhisperer/software/chipwhisperer/capture/scopes/base.py in con(self, sn)
     56 
     57     def con(self, sn=None):
---> 58         if self._con(sn):
     59             self.connectStatus = True
     60 

~/work/projects/chipwhisperer/software/chipwhisperer/capture/scopes/OpenADC.py in _con(self, sn)
    216     def _con(self, sn=None):
    217         if self.scopetype is not None:
--> 218             self.scopetype.con(sn)
    219 
    220             self.qtadc.sc.usbcon = self.scopetype.ser._usbdev

~/work/projects/chipwhisperer/software/chipwhisperer/capture/scopes/openadc_interface/naeusbchip.py in con(self, sn)
     87                 found_id = self.dev.con(idProduct=nae_products, serial_number=sn)
     88             except (IOError, ValueError):
---> 89                 raise Warning('Could not connect to "%s". It may have been disconnected, is in an error state, or is being used by another tool.' % self.getName())
     90 
     91             if found_id != self.last_id:

Warning: Could not connect to "NewAE USB (CWLite/CW1200)". It may have been disconnected, is in an error state, or is being used by another tool.

Setup details:
Have a current CW305 board and ChipWhisperer Lite board (bought one month ago).
Both boards are connected via USB cables to a Windows 10 Laptop, and are also connected
together using the 20 pin cable.
The commands were run using Google Chrome version 88 (running under Windows 10)
talking using port 8888 to a local VirtualBox VM running the ChipWhisperer Jupyter VM
(the “standard one” - as of Nov. 5/2020).
The CW305 S1 switches (on the back side) have M0, M1 and M2 all being 1 (ON).
The CW305 S2 switches have J16 being 0 and K16, K15 and L14 all set to 1.
The CW305 LED4 (FPGA_DONE) is solid red.
The CW305 LED5, LED6 and LED7 are all off.

By the way, the API documentation for “program_target” is as follows:

program_target ( scope , prog_type , fw_path , **kwargs )
Program the target using the programmer
Programmers can be found in the programmers submodule
Parameters

* **scope**  ( *ScopeTemplate* ) – Connected scope object to use for programming
* **prog_type**  ( *Programmer* ) – Programmer to use. See chipwhisperer.programmers for available programmers
* **fw_path**  ( *str* ) – Path to hex file to program

BUT, I couldn’t find any description of the three programmers:
* programmers.STM32FProgrammer
* programmers.XMEGAProgrammer
* programmers.AVRProgrammer

and MOST importantly which programmer should/(MUST?) be used for the CW305 processor!

Also, I expect you MIGHT say, update your ChipWhisperer Jupyter VirtualBox VM.
I tried but was NOT successful either in finding a newer VM, NOR building one from scratch.
I will try to add a new different topic on that subject, when I can.

Thanx Barry.

Hi Barry,

Are you on Windows? If so we released a “native windows” version of the firmware update due to such issues, you can see it on the Releases Page

This requires hte device be in “hardware bootloader” mode - in which case the device enumerates as a COM port. If you are in bootloader mode, you would just run (replace COM3 with the com port of the device):

cw_firmware_updater_5_4_0.exe COM3 cw305

How to get into bootloader mode easily? If you short JP5 while power cycling the board (use the power switch) OR hitting the ‘USB RST’ button it should erase the flash & go to bootloader mode:

image

You can short it with a piece of wire loosely inserted, paperclip, or pair of tweezers. It only needs to be shorted for a moment at reset - once the device is in bootloader mode it waits forever for new firmware.

BUT, I couldn’t find any description of the three programmers:

Ah - so those “programmers” are only if you are using a ChipWhisperer-attached target like the STM32Fxxx, XMEGA, or AVR. If using the CW305 it’s not needed - the USB interface on the CW305 is “it’s own” programmer.

Most “basic” ChipWhisperer targets are fully controlled from the ChipWhisperer - the CW305 is instead controlled by it’s own module.

Let me know if this gets you back up!

PS - all the above steps were missing from the CW305 docs at CW305 Artix FPGA Target - NewAE Hardware Product Documentation - am in process of fixing this so hopefully no-body else is as frustrated in the future. We had only recently released a firmware update for the microcontroller side of the product so we never really needed to worry about it before…

Thanks,

-Colin

OK. So with your help (and a short piece of jumper wire), I eventually seem to have programmed
the CW305 processor so it now thinks it is a CW305 target board and not a ChipWhisperer-Lite
capture board.

I also managed to upgrade my Jupyter VM to the latest git repo (devel branch I think?).

By the way, I suspect you already discovered this, but the repo as of 2-3 days ago is BROKEN.
I fixed it by commenting out line 14 (“from .CW305_ECC import CW305_ECC”) in the
file “…/capture/targets/init.py”.

OK. So now I need to load a bitstream into the Xilinx part. My target board uses the XC100T part.
I previous built my very own Xilinix bitstream using the exact same default AES design files.
For this bitstream, used the current Xilinx Vivado tool suite - Xilinx Vivado 2020.2, with the
target device set to xc7a100tftg256-1. However, neither this bitstream, NOR the included prebuilt
“100T” bit stream works!

Note that the path names to the bit stream files is very short, because I copy these files
into the current working directory (which is why I generally print the CWD at the
start of most of my commands).

Jupyter commands:
import chipwhisperer as cw
import os

print(os.getcwd())
scope = cw.scope()

# scope.clock.adc_src = "extclk_x4"
# scope.clock.reset_adc() # make sure the DCM is locked
# assert (scope.clock.adc_locked), "ADC failed to lock"

# bitstream = r"work/projects/chipwhisperer/jupyter/archive/cw305_top.bit"
bitstream = r"cw305_top.bit"

target = cw.target(scope, cw.targets.CW305, bsfile=bitstream, force=False) # , fpga_id='100t')

target.vccint_set(1.0)
# we only need PLL1:
target.pll.pll_enable_set(True)
target.pll.pll_outenable_set(False, 0)
target.pll.pll_outenable_set(True, 1)
target.pll.pll_outenable_set(False, 2)

# run at 10 MHz:
target.pll.pll_outfreq_set(10E6, 1)

# optional, but reduces power trace noise
# 1ms is plenty of idling time
target.clkusbautooff = True
target.clksleeptime = 1

Response:
WARNING:root:Your firmware is outdated - latest is 0.23. Suggested to update firmware, as you may experience errors See https://chipwhisperer.readthedocs.io/en/latest/api.html#firmware-update

/home/vagrant/work/projects/chipwhisperer/jupyter/archive

WARNING:root:FPGA Done pin failed to go high, check bitstream is for target device.

I also tried writing the SPI flash, but this also failed, as shown here:
import chipwhisperer as cw
import os

print(os.getcwd())

fpga = cw.target(None, cw.targets.CW305, fpga_id='100t') #for CW305_100t
# fpga = cw.target(None, cw.targets.CW305, fpga_id='35t') #for CW305_35t
spi = fpga.spi_mode()

spi.erase_chip() # can also use spi.erase_block() for smaller/faster erases
with open('cw305_top.bit', 'rb') as f:
    data = list(f.read())
    spi.program(data) # also verifies by default

BY THE way, I never saw any documentation on this (doesn’t mean it doesn’t exist though),
but I found out through painful trial and error, that whenever I execute some Jupyter commands,
and one of them fails, I MUST select “Restart” under the “Kernel” menu. Also I MAY need to
disconnect the boards (I just disconnect a USB Hub that connects to both boards) and reconnect
them before I restart the kernel. By the way, doing this disconnect will cause the Jupyter
VirtualBox VM to “abort” occasionally.

Hmm not great! I just noticed in your first post this note:

Sorry for not catching that earlier -

The actual setting is flagged with silkscreen to the upper right - these switches should be set to the right (‘1’ silkscreen) mark. The “on” setting is actually connecting them to ground (aka setting them to ‘0’).

Let me know if that lets it configure ok!

You might be seeing a “double connect” error - basically what happens is:

  1. You are connected to the target.
  2. Something fails so you re-run a Jupyter block.
  3. That jupyter block includes a “connect” command (like cw.target(None, cw.targets.CW305, fpga_id='100t').
  4. The connect command tries to connect to the USB - but it’s already in use (by the “old” object).
  5. The new connect command fails - which stops your code execution. The solution is to make sure you connect only once. In the CW-Lite there is some “magic script” that handles that you can call too - it will be a little smarter about not attempting to re-connect.

Thanks,

-Colin

OK I now see on the schematic that the M0, M1 and M2 switches, when turned on, connect the
switch pin to ground - unlike the other switches like J16, K16 etc switches. A little confusing,
probably needs to be explicitly called out in the documentation instead of hoping people notice
the schematic.

OK so that fixed the bitstream loading problem, but next I could NOT ever get a proper Trace
from the CW305 board (using the ChipWhsiperer-Lite). Strangely, at first the error was that
each and every one of the 5000 traces returned failure. Then at some point it instead failed
the ciphertext assertion - again every time - which stops the process.
I have included the full set of commands I use to run the traces (starting from the beginning).:

from tqdm import trange
from tqdm import notebook
import chipwhisperer as cw
import os
import numpy as np
import time
from Crypto.Cipher import AES

print(os.getcwd())
scope = cw.scope()
scope.default_setup()

bitstream = r"cw305_top.bit"
target = cw.target(None, cw.targets.CW305, bsfile=bitstream, force=False) # , fpga_id='100t')

target.vccint_set(1.0)
# we only need PLL1:
target.pll.pll_enable_set(True)
target.pll.pll_outenable_set(False, 0)
target.pll.pll_outenable_set(True, 1)
target.pll.pll_outenable_set(False, 2)

# run at 10 MHz:
target.pll.pll_outfreq_set(10E6, 1)

scope.clock.adc_src = "extclk_x1"
scope.clock.reset_adc()
assert (scope.clock.adc_locked), "ADC failed to lock"

ktp = cw.ktp.Basic()

traces = []
textin = []
keys = []
N = 5 # 5000  # Number of traces

# initialize cipher to verify DUT result:
key, text = ktp.next()
cipher = AES.new(bytes(key), AES.MODE_ECB)

# for i in tnrange(N, desc='Capturing traces'):
for i in notebook.trange(N, desc='Capturing traces'):
    # run aux stuff that should come before trace here

    key, text = ktp.next()  # manual creation of a key, text pair can be substituted here
    textin.append(text)
    keys.append(key)

ret = cw.capture_trace(scope, target, text, key)
if not ret:
    print("Failed capture")
    print(ret)
    continue

assert (list(ret.textout) == list(cipher.encrypt(bytes(text)))), "Incorrect encryption result!\nGot {}\nExp {}\n".format(ret.textout, list(text))
#trace += scope.getLastTrace()
    
traces.append(ret.wave)
project.traces.append(ret)

Here is the result:

/home/vagrant/work/projects/chipwhisperer/jupyter/archive
Capturing traces: 0%
0/5 [00:00<?, ?it/s]

---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-1-0fd42051844c> in <module>
     54         continue
     55 
---> 56     assert (list(ret.textout) == list(cipher.encrypt(bytes(text)))), "Incorrect encryption result!\nGot {}\nExp {}\n".format(ret.textout, list(text))
     57     #trace += scope.getLastTrace()
     58 

AssertionError: Incorrect encryption result!
Got array('B', [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
Exp [233, 245, 4, 12, 166, 207, 29, 82, 95, 122, 4, 168, 183, 161, 92, 234]

You will notice that I played with changing the number of traces, the 4x versus 1x clk,
importing different things from tqdm, etc. Also notice NO more “old FW version” messages.
And yes the red fpga done led goes out, and the two small red/green leds near the X6 connect
continually blink.
I THOUGHT this might be timing related, but the Xilinx tool seems to show plenty of timing
margin.
Unfortunately I do NOT have a Xilinx JTAG cable/adapter so I am not sure how to debug
this further.

Do you think I should buy one of those $59 low end Xilinx Jtag cable/adapters?
The proper Xilinx part is $270 or $591 - which is a bit expensive unless they are MUCH better
than the $59 version.

By the way, when connecting those SMA connectors to an oscilloscope, should I use the
50 ohm or the 1 M ohm input setting?

(p.s. sorry it took so long, but I suffered around 8-10 crashes of my VirtualBox Jupyter VM. Each
crash happened when disconnecting my USB hub from my laptop).

Hi Barry,

I’m not sure about the cheaper Xilinx programmers - I’m using a proper Xilinx one. I’ve used cheaper ones from Digilent before but can’t recall whether I was using that with ILAs or not. But before you get to ILAs, there’s a few things we can check out.

Can you put a logic analyzer on the IO4 trigger line and see if it ever goes high? Getting all zeros back as the encryption result suggests a communication problem with the FPGA. Yet you are able to connect and set PLLs… Are you sure that J16 is set to 0? If not, then the FPGA isn’t getting a clock, which would explain what you’re seeing.

It would also be a good idea to revert to our supplied bitfile, rather than the one you generated yourself, to eliminate one more variable.

Jean-Pierre