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.