mirror of
https://github.com/MikronMIK32/mik32-uploader.git
synced 2026-01-01 13:37:03 +03:00
Добавление в функцию загрузки прошивки (секторами с драйвером) команд сброса внешней флеш-памяти из всех режимов в стандартный Single SPI, иначе прошивка не заливается.
462 lines
16 KiB
Python
462 lines
16 KiB
Python
import datetime
|
|
from enum import Enum
|
|
import os
|
|
import pathlib
|
|
import sys
|
|
from typing import Dict, List, Union
|
|
import time
|
|
from tclrpc import TclException
|
|
from tclrpc import OpenOcdTclRpc
|
|
import mik32_debug_hal.registers.memory_map as mem_map
|
|
import mik32_debug_hal.registers.bitfields.spifi as spifi_fields
|
|
import mik32_debug_hal.dma as dma
|
|
import flash_drivers.generic_flash as generic_flash
|
|
|
|
|
|
class SpifiError(Exception):
|
|
def __init__(self, value):
|
|
self.value = value
|
|
|
|
def __str__(self):
|
|
return ("ERROR: " + repr(self.value))
|
|
|
|
|
|
def spifi_intrq_clear(openocd: OpenOcdTclRpc):
|
|
openocd.write_word(mem_map.SPIFI_CONFIG_STAT, openocd.read_word(mem_map.SPIFI_CONFIG_STAT) |
|
|
spifi_fields.SPIFI_CONFIG_STAT_INTRQ_M)
|
|
|
|
|
|
INIT_DELAY = 0.001
|
|
|
|
TIMEOUT = 1.0
|
|
|
|
|
|
def init_periphery(openocd: OpenOcdTclRpc):
|
|
openocd.write_word(mem_map.SPIFI_CONFIG_STAT, openocd.read_word(mem_map.SPIFI_CONFIG_STAT) |
|
|
# SPIFI_CONFIG_STAT_INTRQ_M |
|
|
spifi_fields.SPIFI_CONFIG_STAT_RESET_M)
|
|
# openocd.write_word(SPIFI_CONFIG_CTRL, openocd.read_word(
|
|
# SPIFI_CONFIG_CTRL) | (7 << SPIFI_CONFIG_CTRL_SCK_DIV_S))
|
|
openocd.write_word(mem_map.SPIFI_CONFIG_ADDR, 0x00)
|
|
openocd.write_word(mem_map.SPIFI_CONFIG_IDATA, 0x00)
|
|
openocd.write_word(mem_map.SPIFI_CONFIG_CLIMIT, 0x00)
|
|
|
|
time.sleep(INIT_DELAY)
|
|
|
|
|
|
def init(openocd: OpenOcdTclRpc):
|
|
print("MCU clock init", flush=True)
|
|
|
|
init_periphery(openocd)
|
|
|
|
control = openocd.read_word(mem_map.SPIFI_CONFIG_CTRL)
|
|
control |= spifi_fields.SPIFI_CONFIG_CTRL_DMAEN_M
|
|
openocd.write_word(mem_map.SPIFI_CONFIG_CTRL, control)
|
|
|
|
time.sleep(INIT_DELAY)
|
|
|
|
|
|
def init_memory(openocd: OpenOcdTclRpc):
|
|
openocd.write_word(mem_map.SPIFI_CONFIG_STAT, openocd.read_word(mem_map.SPIFI_CONFIG_STAT) |
|
|
spifi_fields.SPIFI_CONFIG_STAT_INTRQ_M |
|
|
spifi_fields.SPIFI_CONFIG_STAT_RESET_M)
|
|
# openocd.write_word(SPIFI_CONFIG_CTRL, openocd.read_word(
|
|
# SPIFI_CONFIG_CTRL) | (7 << SPIFI_CONFIG_CTRL_SCK_DIV_S))
|
|
openocd.write_word(mem_map.SPIFI_CONFIG_ADDR, 0x00)
|
|
openocd.write_word(mem_map.SPIFI_CONFIG_IDATA, 0x00)
|
|
openocd.write_word(mem_map.SPIFI_CONFIG_CLIMIT, 0x00)
|
|
openocd.write_word(mem_map.SPIFI_CONFIG_MCMD, (0 << spifi_fields.SPIFI_CONFIG_MCMD_INTLEN_S) |
|
|
(spifi_fields.SPIFI_CONFIG_CMD_FIELDFORM_ALL_SERIAL << spifi_fields.SPIFI_CONFIG_MCMD_FIELDFORM_S) |
|
|
(spifi_fields.SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_3ADDR << spifi_fields.SPIFI_CONFIG_MCMD_FRAMEFORM_S) |
|
|
(generic_flash.READ_DATA_COMMAND << spifi_fields.SPIFI_CONFIG_MCMD_OPCODE_S))
|
|
|
|
time.sleep(INIT_DELAY)
|
|
|
|
|
|
def spifi_wait_intrq_timeout(openocd: OpenOcdTclRpc, error_message: str):
|
|
time_end = time.perf_counter() + TIMEOUT
|
|
while time.perf_counter() < time_end:
|
|
if (openocd.read_word(mem_map.SPIFI_CONFIG_STAT) & spifi_fields.SPIFI_CONFIG_STAT_INTRQ_M) != 0:
|
|
return
|
|
raise SpifiError(error_message)
|
|
|
|
|
|
class Frameform(Enum):
|
|
RESERVED = 0
|
|
OPCODE_NOADDR = 1
|
|
OPCODE_1ADDR = 2
|
|
OPCODE_2ADDR = 3
|
|
OPCODE_3ADDR = 4
|
|
OPCODE_4ADDR = 5
|
|
NOOPCODE_3ADDR = 6
|
|
NOOPCODE_4ADDR = 7
|
|
|
|
|
|
class Fieldform(Enum):
|
|
ALL_SERIAL = 0
|
|
DATA_PARALLEL = 1
|
|
OPCODE_SERIAL = 2
|
|
ALL_PARALLEL = 3
|
|
|
|
|
|
class Direction(Enum):
|
|
READ = 0
|
|
WRITE = 1
|
|
|
|
|
|
def send_command(
|
|
openocd: OpenOcdTclRpc,
|
|
cmd: int,
|
|
frameform: Frameform,
|
|
fieldform: Fieldform,
|
|
byte_count=0,
|
|
address=0,
|
|
idata=0,
|
|
cache_limit=0,
|
|
idata_length=0,
|
|
direction=Direction.READ,
|
|
data: List[int] = [],
|
|
dma: Union[dma.DMA, None] = None
|
|
) -> List[int]:
|
|
if (dma is not None) and (direction == Direction.WRITE):
|
|
openocd.write_memory(0x02003F00, 8, data)
|
|
|
|
dma.channels[0].start(
|
|
0x02003F00,
|
|
mem_map.SPIFI_CONFIG_DATA32,
|
|
255
|
|
)
|
|
elif (dma is not None) and (direction == Direction.READ):
|
|
dma.channels[1].start(
|
|
mem_map.SPIFI_CONFIG_DATA32,
|
|
0x02003F00,
|
|
255
|
|
)
|
|
|
|
openocd.write_memory(mem_map.SPIFI_CONFIG_ADDR, 32, [address, idata])
|
|
|
|
cmd_write_value = ((cmd << spifi_fields.SPIFI_CONFIG_CMD_OPCODE_S) |
|
|
(frameform.value << spifi_fields.SPIFI_CONFIG_CMD_FRAMEFORM_S) |
|
|
(fieldform.value << spifi_fields.SPIFI_CONFIG_CMD_FIELDFORM_S) |
|
|
(byte_count << spifi_fields.SPIFI_CONFIG_CMD_DATALEN_S) |
|
|
(idata_length << spifi_fields.SPIFI_CONFIG_CMD_INTLEN_S) |
|
|
(direction.value << spifi_fields.SPIFI_CONFIG_CMD_DOUT_S))
|
|
|
|
openocd.write_memory(mem_map.SPIFI_CONFIG_CMD, 32, [cmd_write_value])
|
|
|
|
if direction == Direction.READ:
|
|
out_list = []
|
|
if dma is not None:
|
|
dma.dma_wait(dma.channels[1], 0.1)
|
|
out_list.extend(openocd.read_memory(0x02003F00, 8, byte_count))
|
|
|
|
return out_list
|
|
else:
|
|
for i in range(byte_count):
|
|
out_list.append(openocd.read_memory(
|
|
mem_map.SPIFI_CONFIG_DATA32, 8, 1)[0])
|
|
return out_list
|
|
|
|
if direction == Direction.WRITE:
|
|
if dma is not None:
|
|
dma.dma_wait(dma.channels[0], 0.1)
|
|
else:
|
|
if (byte_count % 4) == 0:
|
|
for i in range(0, byte_count, 4):
|
|
openocd.write_memory(mem_map.SPIFI_CONFIG_DATA32, 32, [
|
|
data[i] + data[i+1] * 256 + data[i+2] * 256 * 256 + data[i+3] * 256 * 256 * 256])
|
|
else:
|
|
for i in range(byte_count):
|
|
openocd.write_memory(
|
|
mem_map.SPIFI_CONFIG_DATA32, 8, [data[i]])
|
|
|
|
return []
|
|
|
|
|
|
def write(openocd: OpenOcdTclRpc, address: int, data: List[int], data_len: int):
|
|
if data_len > 256:
|
|
raise SpifiError("Byte count more than 256")
|
|
|
|
generic_flash.page_program(openocd, address, data, data_len)
|
|
|
|
print("written")
|
|
|
|
|
|
def write_file(bytes: List[int], openocd: OpenOcdTclRpc):
|
|
# print(bytes)
|
|
print(f"Write {len(bytes)} bytes")
|
|
|
|
openocd.halt()
|
|
init(openocd)
|
|
generic_flash.erase(openocd)
|
|
print("bin_data_len = ", len(bytes))
|
|
address = 0
|
|
|
|
for address in range(0, len(bytes), 256):
|
|
if ((address + 256) > len(bytes)):
|
|
break
|
|
print("address = ", address)
|
|
write(openocd, address, bytes, 256)
|
|
if generic_flash.read_data(openocd, address, 256, bytes) == 1:
|
|
return 1
|
|
|
|
if (len(bytes) % 256) != 0:
|
|
print(
|
|
f"address = {address}, +{len(bytes) - address-1}[{address + len(bytes) - address-1}]")
|
|
write(openocd, address, bytes, len(bytes) - address)
|
|
if generic_flash.read_data(openocd, address, len(bytes) - address, bytes) == 1:
|
|
return 1
|
|
print("end")
|
|
|
|
return 0
|
|
|
|
|
|
def get_segments_list(pages_offsets: List[int], segment_size: int) -> List[int]:
|
|
segments = set()
|
|
for offset in pages_offsets:
|
|
segments.add(offset & ~(segment_size - 1))
|
|
return sorted(list(segments))
|
|
|
|
|
|
def dma_config(openocd: OpenOcdTclRpc) -> dma.DMA:
|
|
dma_instance = dma.DMA(openocd)
|
|
dma_instance.init()
|
|
|
|
dma_instance.channels[0].write_buffer = 0
|
|
|
|
dma_instance.channels[0].channel = dma.ChannelIndex.CHANNEL_0
|
|
dma_instance.channels[0].priority = dma.ChannelPriority.VERY_HIGH
|
|
|
|
dma_instance.channels[0].read_mode = dma.ChannelMode.MEMORY
|
|
dma_instance.channels[0].read_increment = dma.ChannelIncrement.ENABLE
|
|
dma_instance.channels[0].read_size = dma.ChannelSize.WORD
|
|
dma_instance.channels[0].read_burst_size = 2
|
|
dma_instance.channels[0].read_request = dma.ChannelRequest.SPIFI_REQUEST
|
|
dma_instance.channels[0].read_ack = dma.ChannelAck.DISABLE
|
|
|
|
dma_instance.channels[0].write_mode = dma.ChannelMode.PERIPHERY
|
|
dma_instance.channels[0].write_increment = dma.ChannelIncrement.DISABLE
|
|
dma_instance.channels[0].write_size = dma.ChannelSize.WORD
|
|
dma_instance.channels[0].write_burst_size = 2
|
|
dma_instance.channels[0].write_request = dma.ChannelRequest.SPIFI_REQUEST
|
|
dma_instance.channels[0].write_ack = dma.ChannelAck.DISABLE
|
|
|
|
dma_instance.channels[1].write_buffer = 0
|
|
|
|
dma_instance.channels[1].channel = dma.ChannelIndex.CHANNEL_1
|
|
dma_instance.channels[1].priority = dma.ChannelPriority.VERY_HIGH
|
|
|
|
dma_instance.channels[1].write_mode = dma.ChannelMode.MEMORY
|
|
dma_instance.channels[1].write_increment = dma.ChannelIncrement.ENABLE
|
|
dma_instance.channels[1].write_size = dma.ChannelSize.WORD
|
|
dma_instance.channels[1].write_burst_size = 2
|
|
dma_instance.channels[1].write_request = dma.ChannelRequest.SPIFI_REQUEST
|
|
dma_instance.channels[1].write_ack = dma.ChannelAck.DISABLE
|
|
|
|
dma_instance.channels[1].read_mode = dma.ChannelMode.PERIPHERY
|
|
dma_instance.channels[1].read_increment = dma.ChannelIncrement.DISABLE
|
|
dma_instance.channels[1].read_size = dma.ChannelSize.WORD
|
|
dma_instance.channels[1].read_burst_size = 2
|
|
dma_instance.channels[1].read_request = dma.ChannelRequest.SPIFI_REQUEST
|
|
dma_instance.channels[1].read_ack = dma.ChannelAck.DISABLE
|
|
|
|
return dma_instance
|
|
|
|
|
|
def check_pages(pages: Dict[int, List[int]], openocd: OpenOcdTclRpc, use_quad_spi=False, use_chip_erase=False):
|
|
result = 0
|
|
|
|
openocd.halt()
|
|
init(openocd)
|
|
|
|
# Сбрасываем микросхему в режиме QPI из всех состояний в нормальный SPI режим.
|
|
generic_flash.chip_reset_qpi(openocd)
|
|
|
|
# Сбрасываем микросхему в режиме SPI из всех состояний в нормальный SPI режим.
|
|
generic_flash.chip_reset(openocd)
|
|
|
|
JEDEC_ID = send_command(openocd, generic_flash.JEDEC_ID_COMMAND,
|
|
Frameform.OPCODE_NOADDR, Fieldform.ALL_SERIAL, 3)
|
|
|
|
print(f"JEDEC ID = {JEDEC_ID[0]:02x} {JEDEC_ID[1]:02x} {JEDEC_ID[2]:02x}")
|
|
|
|
dma_instance = dma_config(openocd)
|
|
|
|
if (use_quad_spi):
|
|
print("Using Quad SPI")
|
|
generic_flash.quad_enable(openocd)
|
|
else:
|
|
print("Using Single SPI")
|
|
# spifi_quad_disable(openocd)
|
|
|
|
pages_offsets = list(pages)
|
|
|
|
for index, page_offset in enumerate(pages_offsets):
|
|
print(
|
|
f"Check page {page_offset:#010x}... {(index*100)//pages_offsets.__len__()}%", flush=True)
|
|
page_bytes = pages[page_offset]
|
|
|
|
result = generic_flash.read_data(
|
|
openocd, page_offset, 256, page_bytes, dma=dma_instance, use_quad_spi=use_quad_spi)
|
|
|
|
if result == 1:
|
|
print("Data error")
|
|
# if (use_quad_spi):
|
|
# spifi_quad_disable(openocd)
|
|
return result
|
|
|
|
if result == 0:
|
|
print("SPIFI pages checking completed", flush=True)
|
|
return 0
|
|
|
|
|
|
def write_pages(pages: Dict[int, List[int]], openocd: OpenOcdTclRpc, use_quad_spi=False, use_chip_erase=False):
|
|
result = 0
|
|
|
|
openocd.halt()
|
|
init(openocd)
|
|
|
|
# Сбрасываем микросхему в режиме QPI из всех состояний в нормальный SPI режим.
|
|
generic_flash.chip_reset_qpi(openocd)
|
|
|
|
# Сбрасываем микросхему в режиме SPI из всех состояний в нормальный SPI режим.
|
|
generic_flash.chip_reset(openocd)
|
|
|
|
JEDEC_ID = send_command(openocd, generic_flash.JEDEC_ID_COMMAND,
|
|
Frameform.OPCODE_NOADDR, Fieldform.ALL_SERIAL, 3)
|
|
|
|
print(f"JEDEC ID = {JEDEC_ID[0]:02x} {JEDEC_ID[1]:02x} {JEDEC_ID[2]:02x}")
|
|
|
|
dma_instance = dma_config(openocd)
|
|
|
|
if use_chip_erase:
|
|
generic_flash.erase(openocd, generic_flash.EraseType.CHIP_ERASE)
|
|
else:
|
|
generic_flash.erase(openocd, generic_flash.EraseType.SECTOR_ERASE,
|
|
get_segments_list(list(pages), 4*1024))
|
|
|
|
print("Quad Enable", generic_flash.check_quad_enable(openocd))
|
|
|
|
if (use_quad_spi):
|
|
print("Using Quad SPI")
|
|
generic_flash.quad_enable(openocd)
|
|
else:
|
|
print("Using Single SPI")
|
|
# spifi_quad_disable(openocd)
|
|
|
|
# print("SREG1", spifi_read_sreg(openocd, SREG_Num.SREG1))
|
|
# print("SREG2", spifi_read_sreg(openocd, SREG_Num.SREG2))
|
|
|
|
pages_offsets = list(pages)
|
|
|
|
for index, page_offset in enumerate(pages_offsets):
|
|
page_bytes = pages[page_offset]
|
|
|
|
if (use_quad_spi):
|
|
generic_flash.quad_page_program(
|
|
openocd, page_offset, page_bytes, 256, f"{(index*100)//pages_offsets.__len__()}%", dma=dma_instance)
|
|
else:
|
|
generic_flash.page_program(openocd, page_offset, page_bytes,
|
|
256, f"{(index*100)//pages_offsets.__len__()}%", dma=dma_instance)
|
|
|
|
result = generic_flash.read_data(
|
|
openocd, page_offset, 256, page_bytes, dma=dma_instance, use_quad_spi=use_quad_spi)
|
|
|
|
if result == 1:
|
|
print("Data error")
|
|
return result
|
|
|
|
if result == 0:
|
|
# Прошивка страниц флеш памяти по SPIFI была завершена
|
|
print("Flashing of flash memory pages via SPIFI has been completed", flush=True)
|
|
return 0
|
|
|
|
|
|
def wait_halted(openocd: OpenOcdTclRpc, timeout_seconds: float = 2):
|
|
openocd.run(f'wait_halt {int(timeout_seconds * 1000)}')
|
|
|
|
|
|
def write_pages_by_sectors(pages: Dict[int, List[int]],
|
|
openocd: OpenOcdTclRpc,
|
|
driver_path: str,
|
|
use_quad_spi=False,
|
|
use_chip_erase=False,
|
|
):
|
|
result = 0
|
|
|
|
openocd.halt()
|
|
openocd.run("riscv.cpu set_reg {mstatus 0 mie 0}") # Отключение прерываний
|
|
|
|
init(openocd)
|
|
# openocd.run("rwp")
|
|
|
|
# Сбрасываем микросхему в режиме QPI из всех состояний в нормальный SPI режим.
|
|
generic_flash.chip_reset_qpi(openocd)
|
|
|
|
# Сбрасываем микросхему в режиме SPI из всех состояний в нормальный SPI режим.
|
|
generic_flash.chip_reset(openocd)
|
|
|
|
JEDEC_ID = send_command(
|
|
openocd, 0x9F, Frameform.OPCODE_NOADDR, Fieldform.ALL_SERIAL, 3)
|
|
print(f"JEDEC_ID {JEDEC_ID[0]:02x} {JEDEC_ID[1]:02x} {JEDEC_ID[2]:02x}")
|
|
|
|
dma_instance = dma_config(openocd)
|
|
|
|
sectors_list = get_segments_list(list(pages), 4*1024)
|
|
|
|
openocd.halt()
|
|
pathname = os.path.dirname(sys.argv[0])
|
|
|
|
openocd.run("wp 0x2003000 4 w")
|
|
|
|
print("Uploading driver... ", end="", flush=True)
|
|
openocd.run(f"load_image {{{pathlib.Path(driver_path)}}}")
|
|
print("OK!", flush=True)
|
|
|
|
openocd.resume(0x2000000)
|
|
wait_halted(openocd)
|
|
|
|
print("Writing Flash by sectors...", flush=True)
|
|
|
|
for i, sector in enumerate(sectors_list):
|
|
ByteAddress = sector
|
|
progress = f"{(i*100)//len(sectors_list)}%"
|
|
print(f" {ByteAddress:#010x} {progress:>4}", end="", flush=True)
|
|
bytes_list: List[int] = []
|
|
for page in range(16):
|
|
page = pages.get(page * 256 + sector)
|
|
if page is not None:
|
|
bytes_list.extend(page)
|
|
else:
|
|
bytes_list.extend([0]*256)
|
|
|
|
openocd.write_memory(0x02002000, 8, bytes_list)
|
|
openocd.run(f"set_reg {{t6 {sector}}}")
|
|
openocd.resume()
|
|
wait_halted(openocd, 10) # ждем, когда watchpoint сработает
|
|
# watchpoint ловит до изменения слова
|
|
openocd.run("step") # делаем шаг, чтобы прочитать новое слово
|
|
|
|
result = openocd.read_memory(0x2003000, 32, 1)[0]
|
|
|
|
if result == 0:
|
|
print(" OK!", flush=True)
|
|
else:
|
|
print(" FAIL!", flush=True)
|
|
print("result =", result)
|
|
break
|
|
if result == 0:
|
|
print(f" {sectors_list[-1]:#010x} 100% OK!", flush=True)
|
|
|
|
openocd.run("rwp 0x02003000")
|
|
init_memory(openocd)
|
|
|
|
if result == 0:
|
|
# Прошивка страниц флеш памяти по SPIFI была завершена
|
|
print("SPIFI writing successfully completed!", flush=True)
|
|
else:
|
|
print(f"SPIFI writing failed!", flush=True)
|
|
return 1
|
|
|
|
return result
|