|
| 1 | +#!/usr/bin/env python3 |
| 2 | +""" |
| 3 | +RISC-V program loader for Basys3 |
| 4 | +Sends a .hex file over UART to the uart_program_loader module. |
| 5 | +
|
| 6 | +Usage: |
| 7 | + python3 load_program.py program.hex |
| 8 | + python3 load_program.py program.hex --port /dev/ttyUSB1 |
| 9 | + python3 load_program.py program.hex --port COM3 --baud 115200 |
| 10 | +""" |
| 11 | + |
| 12 | +import serial |
| 13 | +import time |
| 14 | +import sys |
| 15 | +import argparse |
| 16 | + |
| 17 | +# ------------------------------------------------------- |
| 18 | +# Argument parsing |
| 19 | +# ------------------------------------------------------- |
| 20 | +parser = argparse.ArgumentParser(description="Load a RISC-V hex program onto Basys3 over UART") |
| 21 | +parser.add_argument("hex_file", help="Path to the .hex program file") |
| 22 | +parser.add_argument("--port", default="/dev/ttyUSB1", help="Serial port (default: /dev/ttyUSB0)") |
| 23 | +parser.add_argument("--baud", default=115200, type=int, help="Baud rate (default: 115200)") |
| 24 | +parser.add_argument("--delay", default=0.01, type=float, help="Delay between words in seconds (default: 0.01)") |
| 25 | +args = parser.parse_args() |
| 26 | + |
| 27 | +# ------------------------------------------------------- |
| 28 | +# Read hex file |
| 29 | +# ------------------------------------------------------- |
| 30 | +words = [] |
| 31 | +try: |
| 32 | + with open(args.hex_file, 'r') as f: |
| 33 | + for lineno, line in enumerate(f, 1): |
| 34 | + line = line.strip() |
| 35 | + if not line or line.startswith("//") or line.startswith("#"): |
| 36 | + continue |
| 37 | + try: |
| 38 | + word = int(line, 16) |
| 39 | + words.append(word) |
| 40 | + except ValueError: |
| 41 | + print(f"Warning: skipping invalid line {lineno}: '{line}'") |
| 42 | +except FileNotFoundError: |
| 43 | + print(f"Error: file '{args.hex_file}' not found") |
| 44 | + sys.exit(1) |
| 45 | + |
| 46 | +if not words: |
| 47 | + print("Error: no valid words found in hex file") |
| 48 | + sys.exit(1) |
| 49 | + |
| 50 | +print(f"Loaded {len(words)} words ({len(words) * 4} bytes) from '{args.hex_file}'") |
| 51 | + |
| 52 | +# ------------------------------------------------------- |
| 53 | +# Send over UART |
| 54 | +# ------------------------------------------------------- |
| 55 | +try: |
| 56 | + with serial.Serial(args.port, args.baud, timeout=2) as ser: |
| 57 | + print(f"Opened {args.port} at {args.baud} baud") |
| 58 | + print("Sending program...") |
| 59 | + |
| 60 | + for i, word in enumerate(words): |
| 61 | + # Little-endian: LSB first |
| 62 | + data = word.to_bytes(4, byteorder='little') |
| 63 | + ser.write(data) |
| 64 | + time.sleep(args.delay) |
| 65 | + |
| 66 | + # Progress bar |
| 67 | + pct = (i + 1) / len(words) * 100 |
| 68 | + bar = '█' * int(pct / 2) + '░' * (50 - int(pct / 2)) |
| 69 | + print(f"\r[{bar}] {pct:.1f}% ({i+1}/{len(words)} words)", end='', flush=True) |
| 70 | + |
| 71 | + print() # newline after progress bar |
| 72 | + |
| 73 | + # Send end sequence: 4x 0xFF |
| 74 | + print("Sending end sequence (0xFF 0xFF 0xFF 0xFF)...") |
| 75 | + ser.write(bytes([0xFF, 0xFF, 0xFF, 0xFF])) |
| 76 | + time.sleep(0.1) |
| 77 | + |
| 78 | + print("Done! CPU should now be running your program.") |
| 79 | + |
| 80 | +except serial.SerialException as e: |
| 81 | + print(f"Serial error: {e}") |
| 82 | + print(f"Make sure the board is connected and '{args.port}' is correct") |
| 83 | + sys.exit(1) |
0 commit comments