Hi,
I tried to use Husky with different adc_mul factor. I found the recorded data with adc_mul=1 and adc_mul=4 are just the same. This is not what I expected: I expected that the lower plot (adc_mul=4) shall be a “zoom-in” of the above one (adc_mul=1) because husky records 4 samples per cycle.
The value of adc_clock is 29MHz and target is 7.37MHz. The clock setting looks good so did I misunderstand anything?
Yes, it should be. Show me your Python code?
The code is modularized so it is hard to paste everything here. I share some parts I think related.
def set_clock(
self,
clock_speed:int=7.38E6,
clock_source:str='clkgen_x1',
husky_clock_src:str='system',
husky_clock_mul:int=1):
"""
Set the clock of CW's scope and the baud rate of
the target to read the values from it.
Currently, there are 2 options of clock_source
for CW Lite: clkgen_x1 and clkgen_x4
For the Husky scope, it is mainly used
scope.clock.clkgen_src and scope.clock.adc_mul for its
adc_freq; The clock.adc_src is only for backward compatibility.
2 options for husky_clock_src: system and external.
"""
self.scope.clock.clkgen_freq = clock_speed
if self.name == 'Husky':
self.scope.clock.clkgen_src = husky_clock_src
self.scope.clock.adc_mul = husky_clock_mul
time.sleep(0.1)
print('adc clock :', self.scope.clock.adc_freq)
print('target clock:', self.scope.clock.clkgen_freq)
else:
self.scope.clock.adc_src = clock_source
self.target.baud = int(clock_speed / 7.37E6 * 38400)
def set_adc(
self,
decimate:int=1,
samples:int=20_000,
offset:int=0,
enable_stream:bool=False,
bits_per_sample:int=12):
"""Set ADC of the scope"""
self.scope.default_setup()
if self.name == 'Husky':
self.scope.adc.stream_mode = enable_stream
# self.scope.adc.bits_per_sample = bits_per_sample
self.scope.adc.decimate = decimate
self.scope.adc.samples = samples
self.scope.adc.offset = offset
### instSampleMultiplier decides how many power samples
### I need for capturing for each instruction.
instSampleMultiplier = 4
### The product of number of instruction number and
### the inst/sample multiplier is the total power samples
### for capturing
targetPowerSamples = instCount * instSampleMultiplier
### The maximum buffer size of ChipWhisperer Husky
### is 100k samples. If the target power samples
### are larger than the buffer size, we will need
### to repeat the same procedure for multiple times
bufferSize = 100_000
numChunks = targetPowerSamples // bufferSize
numChunks = (
numChunks+1
if targetPowerSamples % bufferSize
else numChunks)
cw = CWrapper(
platform=PLATFORM,
name='Husky')
cw.reset()
offset = 0
cw.set_clock(
clock_speed=7.38E6,
husky_clock_src='system',
husky_clock_mul=instSampleMultiplier)
cw.set_adc(
decimate=1,
samples=bufferSize,
offset=offset)
cw.program_target(
hex_name=HEX_FILENAME)
fullPowerTraces = np.zeros(numChunks * bufferSize)
print('fullPowerTraces.shape:', fullPowerTraces.shape)
print('numChunks:', numChunks)
for n in range(numChunks):
output, powerTrace = cw.capture_by_inference(
input_data=[1],
write_input_register_byte='p',
output_size=4,
read_output_register_byte='r',
capture_power=True)
print(powerTrace.shape)
fullPowerTraces[offset:offset+bufferSize] = powerTrace
offset += bufferSize
cw.set_adc(
decimate=1,
samples=bufferSize,
offset=offset)
## lapse shall set with value at least higher than 0.001
cw.reset(lapse=0.1)
This is a bit hard to follow and yet incomplete (e.g. instSampleMultiplier
isn’t defined).
Please reduce this to a minimal reproducible example… something like this:
- connect to Husky and target
- capture trace with adc_mul = 1
- capture trace with adc_mul = 4
- compare results
I suspect that in doing so you’ll find your issue!
Ok, I will make a simple code to test again
I simplified the code a bit, but the issue remains
import struct
import numpy as np
import chipwhisperer as cw
import chipwhisperer.capture.targets as CWTargets
scope = cw.scope()
target = cw.target(
scope,
CWTargets.SimpleSerial)
mul = 4
scope.clock.clkgen_freq = 7.38e6
scope.clock.clkgen_src = 'system'
scope.clock.adc_mul = mul
target.baud = int(7.38e6 / 7.37E6 * 38400)
scope.default_setup()
scope.adc.decimate = 1
scope.adc.samples = 100_000
scope.adc.offset = 0
cw.program_target(
scope,
cw.programmers.STM32FProgrammer,
'main-CWLITEARM.hex')
inputBytes = bytearray([])
inputData = [1]
for d in inputData:
d = struct.pack('i', d)
inputBytes += d
target.simpleserial_write('p', inputBytes)
scope.arm()
scope.capture()
trace = scope.get_last_trace()
outputData = target.simpleserial_read('r', 4, timeout=0)
np.save(f"test_x{mul}.npy", trace)
scope.default_setup()
changes a bunch of your scope settings, including scope.clock.adc_mul
.
So, no matter what you set mul
to, the capture from this code will be with scope.clock.adc_mul = 4
.
This is why we made scope.default_setup()
tell you what it’s changing:
If you run the code interactively, you would see:
>>> scope.clock.adc_mul = 1
>>> scope.default_setup()
scope.clock.adc_mul changed from 1 to 4
scope.clock.adc_freq changed from 7370129.87012987 to 29480519.48051948
scope.clock.extclk_tolerance changed from 1715241718.189958 to 19629383676.79548
Hi, thanks for the reply.
I check your code: if I set scope.clock.adc_mul = 4
, even the scope.default_setup()
is call after, scope.clock.adc_mul
remains 4.
In addition, if I comment out the defaut_setup(), the program complains:
OSError: Could not detect STM32F, check connections, BOOT MODE entry setup
Note: My ChipWhisperer version is 5.7 and the result of scope.fw_version
is {'major': 1, 'minor': 4, 'debug': 0}
.
Hi,
After some changes, I now can get the different result. (Note: not so sure this is the correct or not)
The change I made is as following:
scope = cw.scope()
target = cw.target(
scope,
CWTargets.SimpleSerial)
scope.default_setup()
cw.program_target(
scope,
cw.programmers.STM32FProgrammer,
'main-CWLITEARM.hex')
mul = 4
scope.clock.clkgen_freq = 7.38e6
scope.clock.clkgen_src = 'system'
scope.clock.adc_mul = mul
target.baud = int(7.38e6 / 7.37E6 * 38400)
scope.adc.decimate = 1
scope.adc.samples = 100_000
scope.adc.offset = 0
scope.io.nrst = "low"
time.sleep(0.05)
scope.io.nrst = "high"
I first use default_setup
before program_target
so that the connection between host and target can work. Then I change the clock and adc speed after the program process is finished. Once this change is made, I reset target by rewriting nrst
pin.
However, I am still a bit confused that when the clock update really happen. Does it occur once I assign 7.38e6
to scope.clock.clkgen_freq
? Or everything only takes effect when the target is reset by nrst
?
scope.default_setup()
does a few things that most of our targets need, like turning on the clock to the target on HS2. The idea is to:
- connect to the scope
- call
default_setup()
- then customize anything you want.
You can (and should) have a look at any of our Jupyter tutorials if you have a doubt on how to set up your target for capture.
Setting scope.clock.clkgen_freq
has an immediate effect. Some targets may require a reset after its input clock is changed.
I’m working with a ChipWhisperer Husky and trying to configure the clock settings. Specifically, I want to understand the role of adc_mul
in the sampling frequency and why I’m encountering a warning when setting it higher than 4.
Here’s a snippet of my code:
if TARGET_PLATFORM in ['CW312T_A35', 'CW312T_ICE40']:
scope.clock.clkgen_freq = 7.37e6
scope.io.hs2 = 'clkgen'
if scope._is_husky:
print("Husky")
scope.clock.clkgen_src = 'system'
scope.clock.adc_mul = 4
scope.clock.reset_dcms()
else:
scope.clock.adc_src = "clkgen_x4"
import time
time.sleep(0.1)
target._ss2_test_echo()
However, when I attempt to set adc_mul
higher than 4, I receive the following warning:
(ChipWhisperer Scope WARNING|File ChipWhispererHuskyClock.py:663)
Could not calculate pll settings for the requested frequency (7363636);
generating a 7333333 clock instead.
It may be possible to get closer to the requested frequency
with a different adc_mul.
It may also be possible to get closer to the requested
frequency if you set scope.clock.pll._allow_rdiv to True;
however this can result in an inconsistent clock phase between
the target and ADC clocks; use at your own risk!
(ChipWhisperer Scope WARNING|File ChipWhispererHuskyClock.py:703)
Target clock may drop; you may need to reset your target.
Questions:
- What exactly does
adc_mul
control? Does it determine the sampling frequency, or is it related to something else?
- Why am I seeing this warning when setting
adc_mul
above 4? Are there hardware/PLL limitations preventing a higher value?
- Can I safely set
adc_mul
higher than 4? If so, are there any workarounds or configurations that would allow it?
You may set scope.clock.adc_mul
to whatever integer value you like, as long as the resulting sampling clock frequency is within the supported range (5 - 200 MHz). If you exceed this range, you will get a specific warning about that.
Here you are getting two warnings.
The first tells you that although you have requested a 7.37 MHz clock, the best we can do is 7.33 MHz. You can request any frequency you like; we try to find clock generation parameters that generate a clock frequency as close as possible to what you requested, and if it’s further than some small delta, the warning is issued.
The second warning is telling you that in setting the clocks that you requested, the target clock may have been dropped (left idle) for a brief moment. For certain targets, this may cause them to lock up and require a reset.
Both of these warnings are simply informational; they are marked as “warnings” to ensure you don’t ignore them.
1 Like
Thank you for the reply. It worked