From 7abc092356eea8e3a29cadf31999108b81e088d7 Mon Sep 17 00:00:00 2001 From: Kate Temkin Date: Mon, 9 Dec 2019 17:58:05 -0700 Subject: [PATCH] firmware: jtag: commit missing backend class code --- firmware/greatfet_usb/classes/jtag.c | 403 ++++++++++++++++++++++----- 1 file changed, 329 insertions(+), 74 deletions(-) diff --git a/firmware/greatfet_usb/classes/jtag.c b/firmware/greatfet_usb/classes/jtag.c index 7c093f4e..1cf8d47d 100644 --- a/firmware/greatfet_usb/classes/jtag.c +++ b/firmware/greatfet_usb/classes/jtag.c @@ -2,116 +2,371 @@ * This file is part of GreatFET */ +#include + #include -#include -#include #include +#include +#include + +#include #include +#include #define CLASS_NUMBER_SELF (0x10B) -static int jtag_verb_setup(struct command_transaction *trans) -{ - (void)trans; - jtag_setup(); - return 0; -} +// For now, we're using a very simple bit-banged implementation of JTAG. +// I have an SGPIO-based implementation, but it winds up with very inconvenient +// pin locations (and an inability to go below ~50 kHz). It's likely we'll eventually +// include both implementations as "jtag" and "fast_jtag", but it's worth mainlining +// this simple implementation first. ~ktemkin +#define MAX_ACHIEVABLE_FREQUENCY (400 * 1000) // 400 kHz -static int jtag_verb_stop(struct command_transaction *trans) -{ - (void)trans; - jtag_stop(); - return 0; -} +/** + * Define each of our JTAG pins as GPIO. We'll avoid using + * the SGPIO until it's proven necessary here; as ths pin layouts + * are a lot cleaner using GPIO, and this gives us a lot of remapping + * flexibility if we want to, later. + */ +static const gpio_pin_t tdo_gpio = { .port = 0, .pin = 0 }; // J1_P3 +static const gpio_pin_t tdi_gpio = { .port = 5, .pin = 13 }; // J1_P4 +static const gpio_pin_t tck_gpio = { .port = 0, .pin = 1 }; // J1_P6 +static const gpio_pin_t tms_gpio = { .port = 5, .pin = 14 }; // J1_P5 -static int jtag_verb_ir_shift(struct command_transaction *trans) + +/** + * JTAG configuration variables. + */ +uint32_t half_period_delay = 0; +uint32_t next_edge_time = 0; + + +static int verb_configure(struct command_transaction *trans) { - uint8_t in = comms_argument_parse_uint8_t(trans); - comms_response_add_uint8_t(trans, jtag_ir_shift_8(in)); + const gpio_pin_t pins_to_configure[] = { tdo_gpio, tdi_gpio, tck_gpio, tms_gpio}; + uint32_t frequency = comms_argument_parse_uint32_t(trans); + + if (!comms_transaction_okay(trans)) { + return EBADMSG; + } + + // FIXME: reserve these with our pin manager! + + // Configure each of the pins to be used as GPIO. + for (unsigned i = 0; i < ARRAY_SIZE(pins_to_configure); ++i) { + int rc = gpio_configure_pinmux(pins_to_configure[i]); + + uint8_t port = pins_to_configure[i].port; + uint8_t pin = pins_to_configure[i].pin; + if (rc) { + pr_error("error: jtag: failed to grab GPIO pin %u[%u]\n", port, pin); + return rc; + } + } + + // Set the directions of each our JTAG pins. + gpio_set_pin_direction(tdo_gpio, false); + gpio_set_pin_direction(tdi_gpio, true); + gpio_set_pin_direction(tms_gpio, true); + gpio_set_pin_direction(tck_gpio, true); + + // Compute how many delays we'll need per cycle. + if (frequency > MAX_ACHIEVABLE_FREQUENCY) { + half_period_delay = 0; + } else { + // Figure out the half-period time, rounding up to the next whole microsecond. + half_period_delay = ((1000000 + (frequency - 1)) / frequency) / 2; + } + pr_info("jtag: using half-period delay of %u microseconds to achieve frequency of %u\n", half_period_delay, frequency); + next_edge_time = get_time() + half_period_delay; + + // Return the total number of bits we can shift per single call. + // We'll assume we can fit essentially a full buffer, so we'll say 4000 * 8 to allow for argument overhead. + comms_response_add_uint32_t(trans, 4090 * 8); return 0; } -static int jtag_verb_dr_shift(struct command_transaction *trans) + +static inline void wait_for_next_edge_time(void) { - uint16_t in = comms_argument_parse_uint16_t(trans); - comms_response_add_uint16_t(trans, jtag_dr_shift_16(in)); - return 0; + // If we don't have a delay, return immediately. + if (likely(!half_period_delay)) { + return; + } + + while (get_time() < next_edge_time); + next_edge_time = get_time() + half_period_delay; } -static int jtag_verb_reset_tap(struct command_transaction *trans) + +/** + * Generates a single cycle of bit-banged JTAG. + */ +static inline bool jtag_tick(bool tdi) { - (void)trans; - jtag_reset_tap(); - return 0; + bool tdo; + + wait_for_next_edge_time(); + + tdo = gpio_get_pin_value(tdo_gpio); + gpio_set_pin(tck_gpio); + + // Apply our TDI and TMS values. + gpio_set_pin_value(tdi_gpio, tdi); + + // Create a falling edge. + wait_for_next_edge_time(); + gpio_clear_pin(tck_gpio); + + // Return the scanned in value from TDI. + return tdo; } -static int jtag_verb_reset_target(struct command_transaction *trans) +// +// Note that there's a lot of redundancy in these three functions. +// This is because GCC's not particularly good at splitting a single function with +// constants into speed-critical properties; even with proper annotations. +// (LLVM does much better, but gcc support is neccessary, so c'est la vie). +// + + +static int verb_scan(struct command_transaction *trans) { - (void)trans; - jtag_reset_tap(); - jtag_reset_target(); + uint8_t transmit_byte = 0, receive_byte = 0; + uint8_t bits_left_in_tx_byte = 0, bits_left_in_rx_byte = 8; + + uint32_t bit_count = comms_argument_parse_uint32_t(trans); + uint32_t advance_state = comms_argument_parse_bool(trans); + + // Ensure TMS isn't set, so we don't advance through the FSM. + gpio_clear_pin(tms_gpio); + + // While there are any bits remaining to transfer, do so. + while (bit_count) { + + bool to_transmit, received; + bool should_pulse_tms = advance_state && (bit_count == 1); + + // If we don't have any bits queued to process for the current byte, + // get a new byte, and commit the data we've already worked with. + if (!bits_left_in_tx_byte) { + transmit_byte = comms_argument_parse_uint8_t(trans); + bits_left_in_tx_byte = 8; + } + if (!bits_left_in_rx_byte) { + comms_response_add_uint8_t(trans, receive_byte); + bits_left_in_rx_byte = 8; + } + + // Grab the LSB, and queue it for transmit. + to_transmit = transmit_byte & 1; + transmit_byte >>= 1; + + // If this is the final bit and we're advancing state, set TMS. + if (unlikely(should_pulse_tms)) { + gpio_set_pin(tms_gpio); + } + + // Perform out transmission. + received = jtag_tick(to_transmit); + + // Merge the received bit into our active rx byte. + receive_byte = (receive_byte >> 1) | (received ? 0x80 : 0); + + --bit_count; + --bits_left_in_tx_byte; + --bits_left_in_rx_byte; + } + + // If we have bits left in the byte we're building, and we're expecting a response, + // shift our data into the LSB spot and then add it to our response. + receive_byte >>= bits_left_in_rx_byte; + comms_response_add_uint8_t(trans, receive_byte); + return 0; } -static int jtag_verb_detect_ir_width(struct command_transaction *trans) + +static int verb_scan_out(struct command_transaction *trans) { - jtag_reset_tap(); - comms_response_add_uint16_t(trans, jtag_detect_ir_width()); + uint8_t transmit_byte = 0; + uint8_t bits_left_in_tx_byte = 0; + + uint32_t bit_count = comms_argument_parse_uint32_t(trans); + uint32_t advance_state = comms_argument_parse_bool(trans); + + // Ensure TMS isn't set, so we don't advance through the FSM. + gpio_clear_pin(tms_gpio); + + // While there are any bits remaining to transfer, do so. + while (bit_count) { + + bool to_transmit; + bool should_pulse_tms = advance_state && (bit_count == 1); + + // If we don't have any bits queued to process for the current byte, + // get a new byte, and commit the data we've already worked with. + if (!bits_left_in_tx_byte) { + transmit_byte = comms_argument_parse_uint8_t(trans); + bits_left_in_tx_byte = 8; + } + + // Grab the LSB, and queue it for transmit. + to_transmit = transmit_byte & 1; + transmit_byte >>= 1; + + // If this is the final bit and we're advancing state, set TMS. + if (unlikely(should_pulse_tms)) { + gpio_set_pin(tms_gpio); + } + + // Perform out transmission. + jtag_tick(to_transmit); + + --bit_count; + --bits_left_in_tx_byte; + } + return 0; } -static int jtag_verb_detect_chain_length(struct command_transaction *trans) + +static int verb_scan_in(struct command_transaction *trans) { - jtag_reset_tap(); - comms_response_add_uint16_t(trans, jtag_detect_chain_length()); + uint8_t receive_byte = 0; + uint8_t bits_left_in_rx_byte = 8; + + uint32_t bit_count = comms_argument_parse_uint32_t(trans); + uint32_t advance_state = comms_argument_parse_bool(trans); + + // Ensure TMS isn't set, so we don't advance through the FSM. + gpio_clear_pin(tms_gpio); + + // While there are any bits remaining to transfer, do so. + while (bit_count) { + + bool received; + bool should_pulse_tms = advance_state && (bit_count == 1); + + // If we don't have any bits queued to process for the current byte, + // get a new byte, and commit the data we've already worked with. + if (!bits_left_in_rx_byte) { + comms_response_add_uint8_t(trans, receive_byte); + bits_left_in_rx_byte = 8; + } + + // If this is the final bit and we're advancing state, set TMS. + if (unlikely(should_pulse_tms)) { + gpio_set_pin(tms_gpio); + } + + // Perform out transmission. + received = jtag_tick(0); + + // Merge the received bit into our active rx byte. + receive_byte = (receive_byte >> 1) | (received ? 0x80 : 0); + + --bit_count; + --bits_left_in_rx_byte; + } + + // If we have bits left in the byte we're building, and we're expecting a response, + // shift our data into the LSB spot and then add it to our response. + receive_byte >>= bits_left_in_rx_byte; + comms_response_add_uint8_t(trans, receive_byte); + return 0; } -static int jtag_verb_get_device_id(struct command_transaction *trans) + +static int verb_run_clock(struct command_transaction *trans) { - uint16_t chip = comms_argument_parse_uint16_t(trans); - jtag_reset_tap(); - comms_response_add_uint32_t(trans, jtag_get_device_id(chip)); + uint32_t bit_count = comms_argument_parse_uint32_t(trans); + uint32_t advance_state = comms_argument_parse_bool(trans); + + // Ensure TMS isn't set, so we don't advance through the FSM. + gpio_clear_pin(tms_gpio); + + while(bit_count--) { + bool should_pulse_tms = advance_state && !bit_count; + + // If this is the final bit and we're advancing state, set TMS. + if (unlikely(should_pulse_tms)) { + gpio_set_pin(tms_gpio); + } + + jtag_tick(0); + } + return 0; } -/* + +/** * Verbs for the firmware API. */ static struct comms_verb _verbs[] = { - { .name = "setup", .handler = jtag_verb_setup, - .in_signature = "", .out_signature = "", - .doc = "Initialise and setup JTAG process." }, - { .name = "stop", .handler = jtag_verb_stop, - .in_signature = "", .out_signature = "", - .doc = "Stop JTAG process." }, - { .name = "ir_shift", .handler = jtag_verb_ir_shift, - .in_signature = "