Skip to content

Commit 4b27f54

Browse files
add shrike-ctl utility : flasher utility for fpga from host PC (#21)
* add shrike-ctl utility -Raised the serial transfer chunk size from 64 bytes to the full .bin size. -Transfer time results (file size: 46,408 bytes): -64-byte chunks → ~10.7 seconds -full-file chunk(46,408-byte) → ~3.5 seconds
1 parent c86b6f0 commit 4b27f54

File tree

5 files changed

+179
-0
lines changed

5 files changed

+179
-0
lines changed

utils/shrike-ctl/CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
cmake_minimum_required(VERSION 3.13)
2+
include(/PATH/TO/PICO_SDK/pico_sdk_import.cmake)
3+
project(shrike-ctl)
4+
pico_sdk_init()
5+
add_executable(shrike-ctl main.c)
6+
target_link_libraries(shrike-ctl
7+
pico_stdlib
8+
hardware_spi
9+
)
10+
pico_enable_stdio_usb(shrike-ctl 1)
11+
pico_add_extra_outputs(shrike-ctl)

utils/shrike-ctl/README.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Shrike-ctl
2+
3+
A utility to program the FPGA on the Shrike development board directly from your PC.
4+
5+
## Overview
6+
7+
The flashing utility uses these key files:
8+
9+
1. `shrike-ctl.py` – Python host-side tool running on the PC to send the FPGA bitstream over a serial port.
10+
2. `shrike-ctl.uf2` – Prebuilt firmware image for the RP2040 MCU on board Shrike ; when flashed, it turns the board into an FPGA programmer.
11+
12+
## Prerequisite: Flashing the UF2 Firmware
13+
14+
### Drag-and-drop UF2
15+
16+
- Put Shrike in BOOTSEL mode (hold BOOTSEL while plugging in USB).
17+
- A USB mass-storage device appears on the PC.
18+
- Drag and drop `shrike-ctl.uf2` which can be found [here](./shrike-ctl.uf2) onto this drive.
19+
- The board reboots and now runs the FPGA flasher firmware.
20+
- This method uses the provided `.uf2` built from `shrike-ctl.c` and `CMakeLists.txt`.
21+
22+
23+
## Programming the FPGA from PC
24+
25+
Once the board is running the shrike-ctl firmware, the FPGA can be programmed from the PC using `shrike-ctl.py`.
26+
27+
### Requirements
28+
29+
1. Python version 3.12 or above installed on the host PC.
30+
2. `shrike-ctl.py` script.
31+
3. FPGA bitstream file generated by your FPGA tools.
32+
33+
### Usage
34+
35+
Execute the Python script with arguments for the serial port and bitstream path,
36+
37+
for example:
38+
39+
```
40+
python shrike-ctl.py <PORT> <BitstreamFilePath>`
41+
```
42+
43+
Where:
44+
45+
- `<PORT>` is the serial port name (e.g., `COM5` on Windows, `/dev/ttyACM0` or `/dev/ttyUSB0` on Linux).
46+
- `<BitstreamFilePath>` is the path to your FPGA bitstream file.
47+
48+
### Build guide
49+
```note
50+
In your CMakeLists.txt file, update the path to pico-sdk.
51+
Replace your path at `include(/PATH/TO/PICO_SDK/pico_sdk_import.cmake)`
52+
```
53+
Download `PICO-SDK` [here](https://github.com/raspberrypi/pico-sdk/blob/master/README.md)

utils/shrike-ctl/main.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#include <stdio.h>
2+
#include "pico/stdlib.h"
3+
#include "hardware/spi.h"
4+
5+
// -----------------SPI Definaltions-----------------------------
6+
#define SPI_INSTANCE (spi0) // SPI0
7+
#define SPI_CLOCK (1000*16000) // FPGA support 16MHZ clock
8+
#define PIN_MISO 0 // Not used (receive only)
9+
#define PIN_MOSI 3
10+
#define PIN_SCK 2
11+
#define PIN_SS 1
12+
#define PIN_PWR 12 // Power PIN
13+
#define PIN_EN 13 // Enable PIN
14+
#define BUF_SIZE 64
15+
uint8_t tx_buf[BUF_SIZE];
16+
size_t tx_len = 0;
17+
18+
int main(){
19+
stdio_init_all();
20+
//---------------------SPI PIN Setup------------------------------------
21+
spi_init(SPI_INSTANCE, SPI_CLOCK);
22+
gpio_set_function(PIN_MISO, GPIO_FUNC_SPI);
23+
gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI);
24+
gpio_set_function(PIN_SCK, GPIO_FUNC_SPI);
25+
gpio_set_function(PIN_SS, GPIO_FUNC_SPI);
26+
27+
//--------------------- PIN Initilization--------------------------------
28+
gpio_init(PIN_SS);
29+
gpio_init(PIN_PWR);
30+
gpio_init(PIN_EN);
31+
32+
//-----------------------PINS Set as a output----------------------------
33+
gpio_set_dir(PIN_PWR, GPIO_OUT);
34+
gpio_set_dir(PIN_EN, GPIO_OUT);
35+
gpio_set_dir(PIN_SS, GPIO_OUT);
36+
37+
//----------------------FPGA RESET---------------------------------------
38+
gpio_put(PIN_PWR, 0);
39+
gpio_put(PIN_EN, 0);
40+
gpio_put(PIN_SS, 1);
41+
sleep_ms(3);
42+
43+
//--------------------FPGA Initilization---------------------------------
44+
gpio_put(PIN_PWR, 1);
45+
gpio_put(PIN_EN, 1);
46+
gpio_put(PIN_SS, 0);
47+
sleep_ms(3);
48+
gpio_put(PIN_SS, 1);
49+
sleep_us(3);
50+
51+
while (true) {
52+
53+
int ch = getchar_timeout_us(1000);
54+
if (ch >= 0 && ch <= 255){
55+
tx_buf[tx_len++] = (uint8_t)ch;
56+
57+
if (tx_len == BUF_SIZE){
58+
gpio_put(PIN_SS, 0);
59+
spi_write_blocking(SPI_INSTANCE, tx_buf, tx_len);
60+
gpio_put(PIN_SS, 1);
61+
tx_len = 0;
62+
}
63+
}
64+
}
65+
}

utils/shrike-ctl/shrike-ctl.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import serial
2+
import time
3+
import os
4+
import sys
5+
6+
#Configuration Setup
7+
if len(sys.argv) > 1:
8+
PORT = sys.argv[1]
9+
else:
10+
PORT = input("Enter serial port (e.g., /dev/ttyACM0 or COM3): ").strip()
11+
12+
BAUDRATE = 115200 # Match Shrike Baud Rate
13+
CHUNK_SIZE = 46408 # Size of Bitstream
14+
15+
if len(sys.argv) > 2:
16+
file_paths = sys.argv[2:]
17+
else:
18+
FILE_PATH = input("Enter firmware file path : ").strip()
19+
file_paths = [FILE_PATH]
20+
21+
for FILE_PATH in file_paths:
22+
if not os.path.exists(FILE_PATH):
23+
print(f" Error: File not found: {FILE_PATH}")
24+
continue
25+
if os.path.isdir(FILE_PATH):
26+
print(f" Error: '{FILE_PATH}' is a DIRECTORY!")
27+
continue
28+
if not os.path.isfile(FILE_PATH):
29+
print(f" Error: '{FILE_PATH}' is not a file")
30+
continue
31+
32+
file_size = os.path.getsize(FILE_PATH)
33+
print(f" Uploading: {os.path.basename(FILE_PATH)} ({file_size} bytes)")
34+
35+
ser = serial.Serial(PORT, BAUDRATE, timeout=1, rtscts=False, dsrdtr=False)
36+
file_size = os.path.getsize(FILE_PATH)
37+
print(f"File size: {file_size} bytes")
38+
sent = 0
39+
with open(FILE_PATH, "rb") as file:
40+
while sent < file_size:
41+
data = file.read(CHUNK_SIZE)
42+
if not data:
43+
break
44+
ser.write(data)
45+
sent += len(data)
46+
print(f"Sent {sent}/{file_size} bytes", end='\r')
47+
time.sleep(0.01) # 10ms delay
48+
49+
print(f"\nFile transfer complete. Total: {sent} bytes")
50+
ser.close()

utils/shrike-ctl/shrike-ctl.uf2

50.5 KB
Binary file not shown.

0 commit comments

Comments
 (0)