Husky: CW312 iCE40 custom NEORV32 target not responding over UART

Hello everyone,

I am trying to capture power traces using ChipWhisperer Husky but I cannot get any UART communication with my custom FPGA target. I don’t use a ChipWhisperer reference hardware design, but a custom RISC-V–based FPGA design running on the CW312T iCE40(NEORV32 softcore).

I wrote the following script to load the FPGA bitstream manually:

However, even after loading the bitstream, target.read() doesn’t return any data, and UART communication appears to be completely non-functional.

As a result, I encounter the following errors during uploading the firmware(simpleserial-aes.c) to the target:

OSError:b”Sync error with bootloader- invalid response to “h“ command()”

On the firmware side, I am using the standard ChipWhisperer simpleserial-aes.c firmware without functional modifications.

Environment & Setup

  • OS: Linux

  • Tool: ChipWhisperer Husky (Ver. 5.7.0) + CW313 baseboard

  • Target board: CW312T (Lattice iCE40)

  • Target core: custom NEORV32 (RISC-V)

  • Crypto: AES (TINYAES128C)

I am using SimpleSerial1_1:

SCOPETYPE = 'OPENADC'
PLATFORM = 'CW308_NEORV32'
CRYPTO_TARGET='TINYAES128C'
SS_VER='SS_VER_1_1'

For the pin mapping, I used the CW312 iCE40 constraints from the hdl/constraints repository as a reference (board iCE40CW312, constraints.pcf) and then created my own constraint file for my build flow (UART + trigger pins based on that reference).

Below is the constraint file I am currently using.

Constrait file
# Radiant pin mapping for CW312T-iCE40 board

## Clock
ldc_set_location -site B3 [get_ports clk_i]

## UART
ldc_set_location -site E5 [get_ports uart_txd_o]
ldc_set_location -site D5 [get_ports uart_rxd_i]

## SPI - on-board flash
ldc_set_location -site F1 [get_ports flash_sdo_o]
ldc_set_location -site D1 [get_ports flash_sck_o]
ldc_set_location -site C1 [get_ports flash_csn_o]
ldc_set_location -site E1 [get_ports flash_sdi_i]

## GPIO - on-board low-active LEDs
ldc_set_location -site A5 [get_ports gpio_o{0}]
ldc_set_location -site A1 [get_ports gpio_o{1}]


##set_io --warn-no-port iCE40CW312_CLK B3

#> UART
##set_io --warn-no-port iCE40CW312_TX E5
##set_io --warn-no-port iCE40CW312_RX D5


#> SPI [user port]
##set_io --warn-no-port iCE40CW312_SPI_SDO F1
##set_io --warn-no-port iCE40CW312_SPI_SCK D1
##set_io --warn-no-port iCE40CW312_SPI_CSN C1
##set_io --warn-no-port iCE40CW312_SPI_SDI E1


#> GPIO [input]
##set_io --warn-no-port iCE40CW312_GPIO_I[0] A5
##set_io --warn-no-port iCE40CW312_GPIO_I[1] A1


#> GPIO [output]
##set_io --warn-no-port iCE40CW312_GPIO_O[0] A5
##set_io --warn-no-port iCE40CW312_GPIO_O[1] A1

Goal
Capture power traces of a single AES encryption.
The trigger should be asserted during the encryption phase.

What I already tried

  • Uncommenting putch(‘h’);putch(‘e’); putch(‘l’); putch(‘l’); putch(‘o’); putch(‘\n’); in simpleseria-aes.c and manually testing communication with target.read()

But so far that haven’t resolved the issue.

At this point, I suspect a pin mapping or IO constraint issue, but I would appreciate any guidance on what to verify next or what might be missing in my setup.

Thank you in advance.

It sounds like you could be running into this issue.

First, I recommend upgrading to CW 6.0, and programming the target as shown in the linked post.

Then, if you still get the bootloader error, that is what needs to be resolved first; there is no point in attempting communication with the target firmware until that is fixed.

1 Like

Thank you for your quick response! I upgraded to CW 6.0 and I tried running the script below based on the linked post, but the error “b"Sync error with bootloader - invalid response to ‘h’ command (Aborted.\r\n\r\nType ‘h’ for help.\r\nCMD:> )“ still remains.

Could you please check whether there is anything wrong with this script?

SCOPETYPE = 'OPENADC'
PLATFORM = 'CW308_NEORV32'
CRYPTO_TARGET='TINYAES128C'
SS_VER='SS_VER_1_1'
%run "../Setup_Scripts/Setup_Generic.ipynb"
%%bash -s "$PLATFORM" "$CRYPTO_TARGET" "$SS_VER"
cd ../../firmware/mcu/simpleserial-aes
make PLATFORM=$1 CRYPTO_TARGET=$2 SS_VER=$3 -j
import chipwhisperer as cw
neorv = cw.programmers.Neorv32Programmer(scope)
fw_path = "../../firmware/mcu/simpleserial-aes/simpleserial-aes-CW308_NEORV32.bin"
neorv.open_port()
neorv.program(fw_path, bsfile="lattice_ice40_impl_1.new.bin")
neorv.close_port()

I noticed your comment mentioning that there may be some issue with the GitHub Actions–generated bitfile. To make sure I understand correctly: is it likely that this problem is caused by the pre-built NEORV32 bitstream itself (in my case “lattice_ice40_impl_1.new.bin“)? The bitstream we are using was built locally by us.

I suspect both.

I suspect that at some point since when we built the FPGA binary that’s included in our repo (early 2022), something in the neorv32 repository changed that broke our programmer’s interaction with the bootloader. We haven’t had time to figure out why yet.