diff --git a/docs/06_build_guide.md b/docs/06_build_guide.md index a0d2d39..07e1ff3 100644 --- a/docs/06_build_guide.md +++ b/docs/06_build_guide.md @@ -6,7 +6,9 @@ - [**Putting it together**](#putting-it-together) - [**Initial programming**](#initial-programming) - [**Troubleshooting**](#troubleshooting) - - [*`primer.py` threw error on `Bootloader -> SC64 FLASH` step*](#primerpy-threw-error-on-bootloader---sc64-flash-step) + - [*`primer.py` threw `No SC64 USB device found` error*](#primerpy-threw-no-sc64-usb-device-found-error) + - [*`primer.py` threw `SDRAM test error...` message*](#primerpy-threw-sdram-test-error-message) + - [*`primer.py` threw other error message*](#primerpy-threw-other-error-message) --- @@ -115,9 +117,23 @@ Congratulations! Your SC64 flashcart should be ready for use! ### **Troubleshooting** -#### *`primer.py` threw error on `Bootloader -> SC64 FLASH` step* +#### *`primer.py` threw `No SC64 USB device found` error* This issue can be attributed to incorrectly programmed FT232H EEPROM in the first programming step. Check again in `FT_PROG` application if device was configured properly. +Make sure default FTDI drivers are installed for the SC64 in the device manager (only on Windows OS). +Make sure you have correct access to `/dev/ttyUSBx` device and `ftdi_sio` and `usbserial` modules are loaded (only on Linux OS). + +#### *`primer.py` threw `SDRAM test error...` message* + +This issue shows up when there's a problem with the connection to the SDRAM chip or the chip itself is malfunctioning. +Check for any solder bridges and unconnected pins on U8/U9 chips. Once FPGA and microcontroller has been programmed successfully `primer.py` script needs to be run in special mode. -Please use command `python3 primer.py COMx sc64-firmware-{version}.bin --bootloader-only` to try programming bootloader again. +Please use command `python3 primer.py COMx sc64-firmware-{version}.bin --bootloader-only` to test SDRAM again and continue bootloader programming process. + +#### *`primer.py` threw other error message* +Due to multiple possible causes of the problem it's best to start visually inspecting SC64's board for any defects, like bad solder work or chips soldered backwards. +If visual inspection didn't yield any obvious culprits then next step would be to check if everything is connected correctly. +Check if TX/RX signals aren't swapped and if SC64 is getting power from the USB cable. Best place to check supply voltage are the exposed test pads on the left of U8 chip. +If everything at this point was checked and looked fine, then feel free to open new thread in the [*Discussions*](https://github.com/Polprzewodnikowy/SummerCart64/discussions) tab. +Make sure to describe your problem extensively, attach SC64 board photos **from the both sides**, and paste all logs/screenshots from the `primer.py` output. diff --git a/sw/tools/primer.py b/sw/tools/primer.py index 4f24016..6e76d61 100644 --- a/sw/tools/primer.py +++ b/sw/tools/primer.py @@ -5,10 +5,11 @@ import os import queue import serial import signal +import struct import sys import time from binascii import crc32 -from enum import IntEnum +from enum import IntEnum, StrEnum from serial.tools import list_ports from sys import exit from typing import Callable, Optional @@ -78,7 +79,7 @@ class SC64UpdateData: def __int_to_bytes(self, value: int) -> bytes: return value.to_bytes(4, byteorder='little') - + def __align(self, value: int) -> int: if (value % 16 != 0): value += (16 - (value % 16)) @@ -166,7 +167,7 @@ class SC64UpdateData: def get_primer_data(self) -> Optional[bytes]: return self.__primer_data - + def create_bootloader_only_firmware(self): if (self.__bootloader_data == None): raise SC64UpdateDataException('No bootloader data available for firmware creation') @@ -478,6 +479,8 @@ class SC64: __serial: Optional[serial.Serial] = None __packets = queue.Queue() + SDRAM_SIZE = 64 * 1024 * 1024 + class __UpdateStatus(IntEnum): MCU = 1 FPGA = 2 @@ -485,7 +488,8 @@ class SC64: DONE = 0x80 ERROR = 0xFF - def __init__(self) -> None: + def __init__(self, progress: Callable[[int, int, str], None]) -> None: + self.__progress = progress SC64_VID = 0x0403 SC64_PID = 0x6014 SC64_SID = "SC64" @@ -575,14 +579,36 @@ class SC64: return packet return None - def update_firmware(self, data: bytes) -> None: + def __cmd_state_reset(self) -> None: + self.__execute_command(b'R') + + def __cmd_memory_read(self, address: int, length: int) -> bytes: + return self.__execute_command(b'm', [address, length]) + + def __cmd_memory_write(self, address: int, data: bytes) -> None: + self.__execute_command(b'M', [address, len(data)], data) + + def __cmd_firmware_update(self, address: int, length: int) -> None: + self.__execute_command(b'F', [address, length]) + + def update_firmware(self, data: bytes, description: str) -> None: FIRMWARE_ADDRESS = 0x00100000 FIRMWARE_UPDATE_TIMEOUT = 90.0 + STEPS = 6 + + self.__progress(STEPS, 0, description) self.__reset() - self.__execute_command(b'R') - self.__execute_command(b'M', [FIRMWARE_ADDRESS, len(data)], data) - self.__execute_command(b'F', [FIRMWARE_ADDRESS, len(data)]) + self.__progress(STEPS, 1, description) + + self.__cmd_state_reset() + self.__progress(STEPS, 2, description) + + self.__cmd_memory_write(FIRMWARE_ADDRESS, data) + self.__progress(STEPS, 3, description) + + self.__cmd_firmware_update(FIRMWARE_ADDRESS, len(data)) + self.__progress(STEPS, 4, description) timeout = time.time() + FIRMWARE_UPDATE_TIMEOUT while True: @@ -596,11 +622,62 @@ class SC64: if (id != b'F'): raise SC64Exception('Unexpected packet id received') status = self.__UpdateStatus(int.from_bytes(packet_data[0:4], byteorder='big')) - if (status == self.__UpdateStatus.ERROR): - raise SC64Exception('Firmware update error') - if (status == self.__UpdateStatus.DONE): + if (status == self.__UpdateStatus.BOOTLOADER): + self.__progress(STEPS, 5, description) + elif (status == self.__UpdateStatus.DONE): + self.__progress(STEPS, 6, description) time.sleep(2) break + elif (status == self.__UpdateStatus.ERROR): + raise SC64Exception('Firmware update error') + + class __RamTestPattern(StrEnum): + OWN_ADDRESS = 'own address' + ALL_ZEROS = 'all zeros' + ALL_ONES = 'all ones' + RANDOM_DATA = 'random data' + + def __create_ram_test_pattern(self, pattern: __RamTestPattern) -> bytes: + if (pattern == self.__RamTestPattern.OWN_ADDRESS): + addresses = list(range(0, self.SDRAM_SIZE, 4)) + data = struct.pack(f'>{len(addresses)}I', *addresses) + elif (pattern == self.__RamTestPattern.ALL_ZEROS): + data = b'\x00' * self.SDRAM_SIZE + elif (pattern == self.__RamTestPattern.ALL_ONES): + data = b'\xFF' * self.SDRAM_SIZE + elif (pattern == self.__RamTestPattern.RANDOM_DATA): + data = os.urandom(self.SDRAM_SIZE) + return bytes(data) + + def sdram_test(self, description: str) -> None: + CHUNK_LENGTH = 1 * 1024 * 1024 + + self.__reset() + + self.__cmd_state_reset() + + for pattern in self.__RamTestPattern: + write_description = f'{description} / Write {pattern.value}' + check_description = f'{description} / Check {pattern.value}' + + test_data = self.__create_ram_test_pattern(pattern) + + self.__progress(self.SDRAM_SIZE, 0, write_description) + for offset in range(0, self.SDRAM_SIZE, CHUNK_LENGTH): + self.__cmd_memory_write(offset, test_data[offset:offset+CHUNK_LENGTH]) + self.__progress(self.SDRAM_SIZE, offset + CHUNK_LENGTH, write_description) + + self.__progress(self.SDRAM_SIZE, 0, check_description) + for offset in range(0, self.SDRAM_SIZE, CHUNK_LENGTH): + check_data = self.__cmd_memory_read(offset, CHUNK_LENGTH) + if (check_data != test_data[offset:offset+CHUNK_LENGTH]): + for chunk_offset in range(0, CHUNK_LENGTH, 4): + test_address = offset + chunk_offset + expected_value = int.from_bytes(test_data[test_address:test_address+4], byteorder='big') + read_value = int.from_bytes(check_data[chunk_offset:chunk_offset+4], byteorder='big') + if (read_value != expected_value or test_address == 0x00100000): + raise SC64Exception(f'SDRAM test error at 0x{test_address:08X}: read 0x{read_value:08X} != expected 0x{expected_value:08X}') + self.__progress(self.SDRAM_SIZE, offset + CHUNK_LENGTH, check_description) class SC64BringUp: @@ -621,7 +698,7 @@ class SC64BringUp: def start_bring_up(self, port: str, bootloader_only: bool=False) -> None: link = None - sc64 = SC64() + sc64 = SC64(self.__progress) try: if (not bootloader_only): @@ -651,11 +728,8 @@ class SC64BringUp: time.sleep(self.__INTERVAL_TIME) link.read_all() - bootloader_description = 'Bootloader -> SC64 FLASH (no progress reporting)' - bootloader_length = len(self.__bootloader_only_firmware) - self.__progress(bootloader_length, 0, bootloader_description) - sc64.update_firmware(self.__bootloader_only_firmware) - self.__progress(bootloader_length, bootloader_length, bootloader_description) + sc64.sdram_test('SC64 SDRAM test') + sc64.update_firmware(self.__bootloader_only_firmware, 'Bootloader -> SC64 FLASH') finally: if (link and link.is_open): link.close()