Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use JTAG to invoke offline mode #28

Merged
merged 11 commits into from
Oct 12, 2023
12 changes: 3 additions & 9 deletions apollo_fpga/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ def create_jtag_spi(self, jtag_chain):



def out_request(self, number, value=0, index=0, data=None, timeout=5000):
def out_request(self, number, value=0, index=0, data=None, timeout=500):
""" Helper that issues an OUT control request to the debugger. """

request_type = usb.ENDPOINT_OUT | usb.RECIP_DEVICE | usb.TYPE_VENDOR
Expand All @@ -228,18 +228,12 @@ def set_led_pattern(self, number):

def soft_reset(self):
""" Resets the target (FPGA/etc) connected to the debug controller. """
try:
self.out_request(self.REQUEST_RECONFIGURE)
except usb.core.USBError:
pass
self.out_request(self.REQUEST_RECONFIGURE)


def force_fpga_offline(self):
""" Resets the target (FPGA/etc) connected to the debug controller. """
try:
self.out_request(self.REQUEST_FORCE_FPGA_OFFLINE)
except usb.core.USBError:
pass
self.out_request(self.REQUEST_FORCE_FPGA_OFFLINE)

def close(self):
""" Closes the USB device so it can be reused, possibly by another ApolloDebugger """
Expand Down
9 changes: 4 additions & 5 deletions apollo_fpga/commands/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ def erase_flash(device, args):


def program_flash(device, args):
ensure_unconfigured(device)

with device.jtag as jtag:
programmer = device.create_jtag_programmer(jtag)
offset = ast.literal_eval(args.offset) if args.offset else 0
Expand All @@ -155,9 +157,9 @@ def program_flash(device, args):

programmer.flash(bitstream, offset=offset)

device.soft_reset()

def read_back_flash(device, args):
ensure_unconfigured(device)

# XXX abstract this?
length = ast.literal_eval(args.length) if args.length else (4 * 1024 * 1024)
Expand Down Expand Up @@ -212,9 +214,6 @@ def reconfigure_fpga(device, args):
def force_fpga_offline(device, args):
""" Command that requests the attached ECP5 be held unconfigured. """
device.force_fpga_offline()
logging.warning("\nWARNING: Forced the FPGA into an unconfigured state!\n")
logging.warning("Configuration will not work properly until you run 'apollo reconfigure' or reset the device.")
logging.warning("Flashing the FPGA's configuration SPI flash will still work as intended.\n\n")


def _do_debug_spi(device, spi, args, *, invert_cs):
Expand Down Expand Up @@ -305,7 +304,7 @@ def main():
Command("reconfigure", handler=reconfigure_fpga,
help="Requests the attached ECP5 reconfigure itself from its SPI flash."),
Command("force-offline", handler=force_fpga_offline,
help="Forces the board's FPGA offline; useful for recovering a \"bricked\" JTAG connection."),
help="Forces the board's FPGA offline."),

# SPI debug exchanges
Command("spi", args=["bytes"], handler=debug_spi,
Expand Down
2 changes: 0 additions & 2 deletions apollo_fpga/ecp5.py
Original file line number Diff line number Diff line change
Expand Up @@ -713,8 +713,6 @@ def flash(self, bitstream, erase_first=True, disable_protections=False, offset=0
self._flash_write_page(address, chunk)
address += len(chunk)

self.trigger_reconfiguration()


def read_flash(self, length, offset=0):
""" Reads the contents of the attached FPGA's configuration flash. """
Expand Down
6 changes: 6 additions & 0 deletions firmware/src/boards/cynthion_d11/apollo_board.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
#include <hal/include/hal_gpio.h>
#include <stdbool.h>

#define BOARD_HAS_PROGRAM_BUTTON

#if (((_BOARD_REVISION_MAJOR_ == 0) && (_BOARD_REVISION_MINOR_ >= 6)) || (_BOARD_REVISION_MAJOR_ == 1))
#define BOARD_HAS_USB_SWITCH
#endif

/**
* GPIO pins for each of the microcontroller LEDs.
*/
Expand Down
29 changes: 15 additions & 14 deletions firmware/src/boards/cynthion_d11/fpga.c
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
/**
* Code for basic FPGA interfacing.
*
* This file is part of LUNA.
*
* Copyright (c) 2020 Great Scott Gadgets <[email protected]>
* Copyright (c) 2020-2023 Great Scott Gadgets <[email protected]>
* SPDX-License-Identifier: BSD-3-Clause
*/

#include <bsp/board_api.h>
#include <hal/include/hal_gpio.h>

#include <apollo_board.h>
#include "apollo_board.h"
#include "jtag.h"

/**
* Sets up the I/O pins needed to configure the FPGA.
Expand All @@ -28,6 +27,18 @@ void fpga_io_init(void)
*/
void trigger_fpga_reconfiguration(void)
{
/*
* If the JTAG TAP was left in certain states, pulsing PROGRAMN has no
* effect, so we reset the state first.
*/
jtag_init();
jtag_go_to_state(STATE_TEST_LOGIC_RESET);
jtag_wait_time(2);
jtag_deinit();
Comment on lines +34 to +37
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of these blocks? Could you add a comment?
Besides that, I think the jtag_go_to_state(STATE_TEST_LOGIC_RESET) is redundant, as this is done implicitly in jtag_init().

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jtag_init() does jtag_set_current_state(STATE_TEST_LOGIC_RESET), not jtag_go_to_state(STATE_TEST_LOGIC_RESET), so it isn't redundant, but perhaps jtag_init() really should call jtag_go_to_state(STATE_TEST_LOGIC_RESET). I've opened #29 to track that as a separate issue.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added the comments you suggested.


/*
* Now pulse PROGRAMN to instruct the FPGA to configure itself.
*/
gpio_set_pin_direction(FPGA_PROGRAM, GPIO_DIRECTION_OUT);
gpio_set_pin_level(FPGA_PROGRAM, false);

Expand All @@ -36,13 +47,3 @@ void trigger_fpga_reconfiguration(void)
gpio_set_pin_level(FPGA_PROGRAM, true);
gpio_set_pin_direction(FPGA_PROGRAM, GPIO_DIRECTION_IN);
}


/**
* Requests that we hold the FPGA in an unconfigured state.
*/
void force_fpga_offline(void)
{
gpio_set_pin_direction(FPGA_PROGRAM, GPIO_DIRECTION_OUT);
gpio_set_pin_level(FPGA_PROGRAM, false);
}
72 changes: 1 addition & 71 deletions firmware/src/boards/cynthion_d11/jtag.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
/**
* This file is part of LUNA.
*
* Copyright (c) 2020 Great Scott Gadgets <[email protected]>
* Copyright (c) 2020-2023 Great Scott Gadgets <[email protected]>
* SPDX-License-Identifier: BSD-3-Clause
*/

Expand All @@ -13,17 +11,6 @@

#include <jtag.h>

extern uint8_t jtag_in_buffer[256];
extern uint8_t jtag_out_buffer[256];

/**
* Flags for our JTAG commands.
*/
enum {
FLAG_ADVANCE_STATE = 0b01,
FLAG_FORCE_BITBANG = 0b10
};


/**
* Hook that performs hardware-specific initialization.
Expand All @@ -50,60 +37,3 @@ void jtag_platform_deinit(void)
// Restore use of our connection to a default of being a UART.
uart_configure_pinmux();
}


/**
* Request that performs the actual JTAG scan event.
* Arguments:
* wValue: the number of bits to scan; total
* wIndex:
* - 1 if the given command should advance the FSM
* - 2 if the given command should be sent using the slow method
*/
bool handle_jtag_request_scan(uint8_t rhport, tusb_control_request_t const* request)
{
// Our bulk method can only send whole bytes; so send as many bytes as we can
// using the fast method; and then send the remainder using our slow method.
size_t bytes_to_send_bulk = request->wValue / 8;
size_t bits_to_send_slow = request->wValue % 8;

// We can't handle 0-byte transfers; fail out.
if (!bits_to_send_slow && !bytes_to_send_bulk) {
return false;
}

// If this would scan more than we have buffer for, fail out.
if (bytes_to_send_bulk > sizeof(jtag_out_buffer)) {
return false;
}

// If we've been asked to send data the slow way, honor that, and send all of our bits
// using the slow method.
if (request->wIndex & FLAG_FORCE_BITBANG) {
bytes_to_send_bulk = 0;
bits_to_send_slow = request->wValue;
}

// If we're going to advance state, always make sure the last bit is sent using the slow method,
// so we can handle JTAG TAP state advancement on the last bit. If we don't have any bits to send slow,
// send the last byte slow.
if (!bits_to_send_slow && (request->wIndex & FLAG_ADVANCE_STATE)) {
bytes_to_send_bulk--;
bits_to_send_slow = 8;
}

// Switch to SPI mode, and send the bulk of the transfer using it.
if (bytes_to_send_bulk) {
spi_configure_pinmux(SPI_FPGA_JTAG);
spi_send(SPI_FPGA_JTAG, jtag_out_buffer, jtag_in_buffer, bytes_to_send_bulk);
}

// Switch back to GPIO mode, and send the remainder using the slow method.
spi_release_pinmux(SPI_FPGA_JTAG);
if (bits_to_send_slow) {
jtag_tap_shift(jtag_out_buffer + bytes_to_send_bulk, jtag_in_buffer + bytes_to_send_bulk,
bits_to_send_slow, request->wIndex);
}
return tud_control_xfer(rhport, request, NULL, 0);
}

28 changes: 15 additions & 13 deletions firmware/src/boards/cynthion_d21/fpga.c
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
/**
* Code for basic FPGA interfacing.
*
* This file is part of LUNA.
*
* Copyright (c) 2020 Great Scott Gadgets <[email protected]>
* Copyright (c) 2020-2023 Great Scott Gadgets <[email protected]>
* SPDX-License-Identifier: BSD-3-Clause
*/

#include <bsp/board_api.h>
#include <hal/include/hal_gpio.h>

#include "jtag.h"

// List of pins used for FPGA interfacing.
enum {
DONE_GPIO = PIN_PA15,
Expand Down Expand Up @@ -44,6 +44,18 @@ void fpga_io_init(void)
*/
void trigger_fpga_reconfiguration(void)
{
/*
* If the JTAG TAP was left in certain states, pulsing PROGRAMN has no
* effect, so we reset the state first.
*/
jtag_init();
jtag_go_to_state(STATE_TEST_LOGIC_RESET);
jtag_wait_time(2);
jtag_deinit();

/*
* Now pulse PROGRAMN to instruct the FPGA to configure itself.
*/
gpio_set_pin_direction(PIN_PROG, GPIO_DIRECTION_OUT);
gpio_set_pin_level(PIN_PROG, false);

Expand All @@ -52,13 +64,3 @@ void trigger_fpga_reconfiguration(void)
gpio_set_pin_level(PIN_PROG, true);
gpio_set_pin_direction(PIN_PROG, GPIO_DIRECTION_IN);
}


/**
* Requests that we hold the FPGA in an unconfigured state.
*/
void force_fpga_offline(void)
{
gpio_set_pin_direction(PIN_PROG, GPIO_DIRECTION_OUT);
gpio_set_pin_level(PIN_PROG, false);
}
53 changes: 1 addition & 52 deletions firmware/src/boards/cynthion_d21/jtag.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
/**
* This file is part of LUNA.
*
* Copyright (c) 2020 Great Scott Gadgets <[email protected]>
* Copyright (c) 2020-2023 Great Scott Gadgets <[email protected]>
* SPDX-License-Identifier: BSD-3-Clause
*/

Expand All @@ -12,9 +10,6 @@

#include <jtag.h>

extern uint8_t jtag_in_buffer[256];
extern uint8_t jtag_out_buffer[256];


/**
* Hook that performs hardware-specific initialization.
Expand All @@ -41,49 +36,3 @@ void jtag_platform_deinit(void)
{
gpio_set_pin_direction(SIDEBAND_PHY_RESET, GPIO_DIRECTION_IN);
}


/**
* Request that performs the actual JTAG scan event.
* Arguments:
* wValue: the number of bits to scan; total
* wIndex: 1 if the given command should advance the FSM
*/
bool handle_jtag_request_scan(uint8_t rhport, tusb_control_request_t const* request)
{
// Our bulk method can only send whole bytes; so send as many bytes as we can
// using the fast method; and then send the remainder using our slow method.
size_t bytes_to_send_bulk = request->wValue / 8;
size_t bits_to_send_slow = request->wValue % 8;

// We can't handle 0-byte transfers; fail out.
if (!bits_to_send_slow && !bytes_to_send_bulk) {
return false;
}

// If this would scan more than we have buffer for, fail out.
if (bytes_to_send_bulk > sizeof(jtag_out_buffer)) {
return false;
}

// If we're going to advance state, always make sure the last bit is sent using the slow method,
// so we can handle JTAG TAP state advancement on the last bit. If we don't have any bits to send slow,
// send the last byte slow.
if (!bits_to_send_slow && request->wIndex) {
bytes_to_send_bulk--;
bits_to_send_slow = 8;
}

// Switch to SPI mode, and send the bulk of the transfer using it.
spi_configure_pinmux(SPI_FPGA_JTAG);
spi_send(SPI_FPGA_JTAG, jtag_out_buffer, jtag_in_buffer, bytes_to_send_bulk);

// Switch back to GPIO mode, and send the remainder using the slow method.
spi_release_pinmux(SPI_FPGA_JTAG);
if (bits_to_send_slow) {
jtag_tap_shift(jtag_out_buffer + bytes_to_send_bulk, jtag_in_buffer + bytes_to_send_bulk,
bits_to_send_slow, request->wIndex);
}
return tud_control_xfer(rhport, request, NULL, 0);
}

31 changes: 0 additions & 31 deletions firmware/src/boards/cynthion_d21/usb_switch.c

This file was deleted.

4 changes: 1 addition & 3 deletions firmware/src/boards/daisho/fpga.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
/**
* Code for basic FPGA interfacing.
*
* This file is part of LUNA.
*
* Copyright (c) 2020 Great Scott Gadgets <[email protected]>
* Copyright (c) 2020-2023 Great Scott Gadgets <[email protected]>
* SPDX-License-Identifier: BSD-3-Clause
*/

Expand Down
Loading