WiseEye2 Firmware Flashing Guide
Overview
This document outlines the procedure for programming a binary firmware image file into the WiseEye2 (WE2) MCU's quad-SPI flash. The process uses WE2’s in-system programming (ISP) mode via I²C, which enables the MCU to write data to its external flash. This procedure can be used on an unprogrammed or programmed WiseEye2 flash device.
Programming firmware via script
Firmware can be programmed onto the WiseEye2 by using the script cts_we2_util.py located in the src/ctsgen3 directory of the ctsgen3-x.x.x.tar.gz.
Example usage of the script alone:
poetry run python cts_we2_util.py flash-fw --bitfile=./path_to/firmware.bin
Example usage of flashing in a python script
import ctsgen3.cts_we2_util
import ft4222
deva = ft4222.openByDescription("FT4222 A")
device_A.i2cMaster_Init(ctsgen3.cts_we2_util.I2C_SPEED)
ctsgen3.cts_we2_util.slot_pb_start_programming(device_A, ctsgen3.cts_we2_util.ISP_I2C_MODE, firmware_filename)
Flash Procedure Details
This section provides more information about the procedure used in the script cts_we2_util.py. Details of each register and I2C functions used in the procedure can be found in the appendix.
ISP mode flashing uses an I²C interface on the WiseEye2 (slave ID = 0x28) to program the quad-SPI system flash connected to the device.
1. Initialize I²C Communication
- The FT4222 is initialized as I²C master (maximum 1000 kbps).
- Device with description
"FT4222 A"is opened.
2. Enable ISP Test Mode
Enable ISP test mode via register ISP_ENABLE_REG_ADDR (0xD8) using function I2C_burst_write.
I2C_burst_write(
ft4222_dev,
ISP_CONTROL_I2C_SLVID, # 0x28
[ISP_ENABLE_REG_ADDR], # [0xD8]
[REG_D8_TEST_MODE] # [0x02]
)
3. Read Clock Configuration
Read 32-bit value from ISP register ISP_SRC_PERI_CLK_CTRL (0x51010020) using the function i2c_single_read. Bits 12-15 determine the clock source (PLL, RC24, XTAL, etc.) and appropriate sclk/clkdiv values for the ISP settings.
| ISP_SRC_PERI_CLK_CTRL[15:12] | Clock Source | sclk |
clkdiv |
|---|---|---|---|
| 0x0 | RC24 | 1 | 0 |
| 0xC | PLL | 0 | 10 |
| 0x4 | RC9648 | 0 | 0 |
| All other cases | XTAL24 | 1 | 0 |
4. Disable ISP Test Mode
Restore ISP_ENABLE_REG_ADDR (0xD8) to 0x00 using function I2C_burst_write.
5. Construct and Send ispsetting Word
Build the 32-bit configuration word based on the clock settings from step 3. Typical setting values are shown below along with building the 32-bit ispsetting word.
crc_en = 1
dummycycle = 0
endian = 1
erasemode = 0
ercmdset = 0x20
ercmdseten = 0
fastread = 0
flashtype = 1
pp4en = 1
ppen = 1
qread = 1
ispsetting = (
0x01
| (crc_en << 1)
| (sclk << 2)
| (ppen << 3)
| (erasemode << 4)
| (ercmdseten << 6)
| (pp4en << 7)
| (ercmdset << 8)
| (clkdiv << 16)
| (qread << 19)
| (fastread << 20)
| (dummycycle << 21)
| (flashtype << 23)
| (endian << 25)
)
Write the ispsetting word to register ISP_CONTROL_ADDR (0x51010000) using the function i2c_single_write.
6. Enable ISP Mode
Enable ISP mode via register ISP_ENABLE_REG_ADDR (0xD8) using function I2C_burst_write.
I2C_burst_write(
ft4222_dev,
ISP_CONTROL_I2C_SLVID, # 0x28
[ISP_ENABLE_REG_ADDR], # [0xD8]
[REG_D8_ISP_EN] # [0x01]
)
7. Write Firmware in 256-Byte Blocks
- Build a packet with a header of
[0xFA]followed by a 4-byte address (starting at addressISP_MEM_OUT_ADDR(0x38000000)) - Append 256 bytes of data from the bit-file to the packet.
- Write the raw packet to the slave (no special I2C function is used).
- Read the register
ISP_PPDONE_COUNTERusing the functioni2c_single_read.- Verify the count value mask matches the current loop count.
- Check CRC error bits. Assert an error if CRC fail is detected.
- Repeat this procedure for the entire bit-file, incrementing the address and data slice accordingly.
8. Exit ISP Mode
Restore ISP_ENABLE_REG_ADDR (0xD8) to 0x00 using function I2C_burst_write.
9. Hard Reset
Execute a hard reset of the MCU to load the new firmware image from flash.
Appendix
ISP Enable Register
The ISP enable register 0xD8 is used at various times throughout the procedure. The bit definitions are below:
- Register Address:
0xD8
| Bit Position | Mask | Name | Description |
|---|---|---|---|
| 0 | 0x01 |
REG_D8_ISP_EN |
ISP enable: allows ISP commands to program/erase flash |
| 1 | 0x02 |
REG_D8_TEST_MODE |
SCU test mode: unlocks SCU registers (e.g. clock config) |
| 2 | 0x04 |
REG_D8_SPI_DO_EN |
SPI Data-Output enable: routes flash I/O over SPI DO pin |
| 3–7 | — |
Reserved |
ISP Control Register Map
| Register Name | Address | Mask | Description |
|---|---|---|---|
| ISP_CONTROL_ADDR | 0x51010000 |
0x03FFFFFF |
ISP control address |
0x00000001 |
Reserved | ||
0x00000002 |
Enable CRC checks | ||
0x00000004 |
Select clock source (sclk) |
||
0x00000008 |
Enable page-program count | ||
0x00000030 |
Erase mode: 0:sector, 1:32k, 2:64k, 3:chip | ||
0x00000040 |
Erase command set enable | ||
0x00000080 |
Enable 4-page program mode | ||
0x0000FF00 |
Erase-command set value | ||
0x00070000 |
Clock divider value | ||
0x00080000 |
Enable quad-read | ||
0x00100000 |
Enable fast-read | ||
0x00E00000 |
dummy cycles - 0:8, 1:6, 2:8, 3:10 | ||
0x01800000 |
0:MXIC, 1:WINBOND | ||
0x02000000 |
Data endianness (0:little, 1:big) | ||
| CRC_WOUT_ADDR | 0x51010004 |
0xFFFFFFFF |
CRC write-out address pointer |
| CRC_ROUT_ADDR | 0x51010008 |
0xFFFFFFFF |
CRC read-out address pointer |
| ISP_SRC_PERI_CLK_CTRL | 0x51010020 |
0xFFFFFFFF |
ISP peripheral clock control |
| ISP_STATUS_CLR | 0x51010030 |
0xFFFFFFFF |
Write-1-to-clear ISP/CRC status bits |
| ISP_PPDONE_COUNTER | 0x51010040 |
0x300FFFFF |
Page-program done |
0x000FFFFF |
Page-program done count | ||
0x10000000 |
CRC error flag 0 | ||
0x20000000 |
CRC error flag 1 | ||
| ISP_PPDONE | 0x51010050 |
0xFFFFFFFF |
Page-program done/diagnostic conflict status |
I²C Functions Overview
Summary Table (Cheat Sheet)
| Function | Input Address Type | Data Width | Use Case |
|---|---|---|---|
I2C_burst_write |
8-bit | N × 8-bit | Single/multiple register write |
I2C_burst_read |
8-bit | N × 8-bit | Sequential register read |
i2c_single_write |
32-bit | 32-bit | ISP config, control registers |
i2c_single_read |
32-bit | 32-bit | Clock source, CRC, status registers |
I2C_burst_write(dev, slave_id, addr_list, data_list)
Purpose: Write one or more bytes to consecutive (or mapped) 8-bit I²C addresses.
Example:
I2C_burst_write(dev, 0x28, [0xD8], [0x02])
# → Writes 0x02 to address 0xD8 on slave 0x28.
Internals:
- Concatenates the address and data bytes
- Sends them together as one I²C write packet
I2C_burst_read(dev, slave_id, addr, size)
Purpose:
Reads a block of size bytes starting from the specified 8-bit address.
Example:
val = I2C_burst_read(dev, 0x28, [0xD8], 4)
# → Reads 4 bytes starting at address 0xD8.
Internals:
- Issue an address‐phase write (
START) - Perform a repeated start followed by a read
i2c_single_write(dev, slave_id, addr[4], data[4])
Purpose:
Write a 4-byte value to a 4-byte register address over I²C.
Transactions:
- 8 × 2-byte writes + 1 × 2-byte ack.
- Poll for completion.
Example:
i2c_single_write(
dev,
0x28,
[0x00, 0x00, 0x10, 0x51],
[0x12, 0x34, 0x56, 0x78]
)
Procedure:
Address and data are written in two byte chunks with each write consisting of an index byte and data byte.
Two-Byte Write Chunks
| Order | Index Byte | Data Byte | Description |
|---|---|---|---|
| 1 | 0x00 |
A0 | Address byte 0 (LSB) |
| 2 | 0x01 |
A1 | Address byte 1 |
| 3 | 0x02 |
A2 | Address byte 2 |
| 4 | 0x03 |
A3 | Address byte 3 (MSB) |
| 5 | 0x04 |
D0 | Data byte 0 (LSB) |
| 6 | 0x05 |
D1 | Data byte 1 |
| 7 | 0x06 |
D2 | Data byte 2 |
| 8 | 0x07 |
D3 | Data byte 3 (MSB) |
| 9 | 0x0C |
0x01 |
Write-ack command |
Poll for Completion
- Repeatedly write
0x0F - Then read 1-byte response:
0x00= write complete- Others = still busy
- If
0x00received within timeout, write succeeded
i2c_single_read(dev, slave_id, addr[4])
Purpose:
Read a 4-byte value from a 4-byte register address.
Transactions:
- 4 × 2-byte writes + 1 × 2-byte ack
- Poll for completion
- 4 × 2-byte reads.
Example:
val = i2c_single_read(
dev,
0x28,
[0x20, 0x00, 0x10, 0x51]
)
Procedure:
Two-Byte Write Chunks
| Order | Index Byte | Data Byte | Description |
|---|---|---|---|
| 1 | 0x00 |
A0 | Address byte 0 (LSB) |
| 2 | 0x01 |
A1 | Address byte 1 |
| 3 | 0x02 |
A2 | Address byte 2 |
| 4 | 0x03 |
A3 | Address byte 3 (MSB) |
Read-Acknowledge Packet
| Order | Index Byte | Data Byte | Description |
|---|---|---|---|
| 5 | 0x0C |
0x00 |
Read-ack command |
Poll for Completion
- Same as in i2c_single_write
Read Result (One Byte at a Time)
| Step | Operation | Value | Description |
|---|---|---|---|
| 7 | WRITE | 0x08 |
Index byte 0 |
| 8 | READ | D0 | Read byte 0 |
| 9 | WRITE | 0x09 |
Index byte 1 |
| 10 | READ | D1 | Read byte 1 |
| 11 | WRITE | 0x0A |
Index byte 2 |
| 12 | READ | D2 | Read byte 2 |
| 13 | WRITE | 0x0B |
Index byte 3 |
| 14 | READ | D3 | Read byte 3 |
result = D0 + (D1 << 8) + (D2 << 16) + (D3 << 24)