Skip to content

Commit

Permalink
[SC64][SW] Added very basic SDRAM test in the primer before flashing …
Browse files Browse the repository at this point in the history
…bootloader
  • Loading branch information
Polprzewodnikowy committed Mar 17, 2024
1 parent f27f644 commit c156b72
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 20 deletions.
22 changes: 19 additions & 3 deletions docs/06_build_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

---

Expand Down Expand Up @@ -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.
108 changes: 91 additions & 17 deletions sw/tools/primer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
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
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -166,7 +167,7 @@ def get_bootloader_data(self) -> Optional[bytes]:

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')
Expand Down Expand Up @@ -478,14 +479,17 @@ class SC64:
__serial: Optional[serial.Serial] = None
__packets = queue.Queue()

SDRAM_SIZE = 64 * 1024 * 1024

class __UpdateStatus(IntEnum):
MCU = 1
FPGA = 2
BOOTLOADER = 3
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"
Expand Down Expand Up @@ -575,14 +579,36 @@ def __receive_data_packet(self) -> Optional[tuple[bytes, bytes]]:
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:
Expand All @@ -596,11 +622,62 @@ def update_firmware(self, data: bytes) -> None:
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:
Expand All @@ -621,7 +698,7 @@ def get_update_info(self) -> str:

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):
Expand Down Expand Up @@ -651,11 +728,8 @@ def start_bring_up(self, port: str, bootloader_only: bool=False) -> None:
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()
Expand Down

0 comments on commit c156b72

Please sign in to comment.