diff options
Diffstat (limited to 'gdb/arc-board.c')
-rw-r--r-- | gdb/arc-board.c | 1996 |
1 files changed, 1996 insertions, 0 deletions
diff --git a/gdb/arc-board.c b/gdb/arc-board.c new file mode 100644 index 0000000..6642220 --- /dev/null +++ b/gdb/arc-board.c @@ -0,0 +1,1996 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Authors: + Tim Gore + Tom Pennello <tom.pennello@arc.com> + Justin Wilde <justin.wilde@arc.com> + Phil Barnard <phil.barnard@arc.com> + Richard Stuckey <richard.stuckey@arc.com> + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This module implements operations for controlling an ARC target board. */ +/* */ +/* These operations are: */ +/* 1) configuring ("blasting") an FPGA target with the contents of an */ +/* XBF file; */ +/* 2) checking whether a target has been so configured; */ +/* 3) setting the clock frequency of the target; */ +/* 4) setting the clock sources of the target. */ +/* */ +/* Notes: */ +/* The blast_board function implements an ARC-specific command; hence its */ +/* 'args' parameter contains data entered by the debugger user, which */ +/* must be checked for validity. */ +/* */ +/* Target Board: */ +/* It is assumed that the target board is actually an ARCangel 4 (AA4). */ +/* */ +/* See */ +/* ARCangel 4 Development System */ +/* User's Guide */ +/* 5801-001 */ +/* */ +/* for a full description of the target. */ +/* */ +/* The AA4 contains a Configurable Programmable Logic Device (CPLD) which */ +/* is used to control the system services on the board; this includes the */ +/* configuration of the board's PLL (Phase Lock Loop) clock chip, and */ +/* clock routing. */ +/* */ +/* The AA4 also has a 48 MHz crystal oscillator module, and has a number */ +/* of DIP switches which may be set manually: these may be used to select */ +/* a divisor (1, 2, 4 or 8) which may be applied to the crystal frequency */ +/* to obtain a lower frequency. */ +/* */ +/* The target FPGA has 4 global clock pins (GCLK0-3); a different clock */ +/* source may be routed to each of these by the CPLD. The available */ +/* sources are: */ +/* */ +/* crystal : the physical crystal */ +/* dips : the physical crystal divided by the DIP switch divisors */ +/* highimp : high impedance */ +/* host : use the STR (strobe) input of the host interface */ +/* mclk : use a clock provided by the PLL */ +/* vclk : use a clock provided by the PLL */ +/* */ +/* Note that "high impedance" (also referred to as "Tri-state") means, in */ +/* effect, that the clock is switched off. */ +/* */ +/* It is also possible to specify that the PLL clock should be a Harvard */ +/* clock generator. */ +/* */ +/* The main clock for the target's ARC processor is provided by GLCK3. */ +/* */ +/* The PLL is assumed to be a Cypress Semiconductor Corporation ICD2061A */ +/* Dual Programmable Graphics Clock Generator; the Data Sheet describing */ +/* this device may be readily found on the Web, and should be consulted */ +/* for an understanding of how the clock is programmed. */ +/* */ +/* The ICD2061A actually provides two independent clocks: MCLK (Memory or */ +/* I/O Timing Clock) and VCLK (Video Clock). It is recommended that the */ +/* frequency of one clock should not be an integer multiple of that of */ +/* other, in order to avoid clock signal degradation through jitter. */ +/* */ +/******************************************************************************/ + +/* system header files */ +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <math.h> + +/* gdb header files */ +#include "defs.h" +#include "completer.h" +#include "objfiles.h" +#include "gdbcmd.h" + +/* ARC header files */ +#include "arc-board.h" +#include "arc-architecture.h" +#include "arc-registers.h" +#include "arc-gpio.h" +#include "arc-jtag.h" +#include "arc-jtag-ops.h" + + +/* -------------------------------------------------------------------------- */ +/* local types */ +/* -------------------------------------------------------------------------- */ + +typedef enum +{ + CLOCK_SOURCE_HIGH_IMPEDANCE, + CLOCK_SOURCE_PLL_MCLK, + CLOCK_SOURCE_PLL_VCLK, + CLOCK_SOURCE_CRYSTAL, + CLOCK_SOURCE_PLL_MCLK_HARVARD, + CLOCK_SOURCE_PLL_VCLK_HARVARD, + CLOCK_SOURCE_HOST_STROBE, + CLOCK_SOURCE_CRYSTAL_DIVIDED +} ClockSource; + +typedef enum +{ + PLL_MCLK = 0, + PLL_VCLK = 1, + NO_PLL_CLK = 2 +} PLL_ClockId; + +typedef unsigned int GlobalClockId; // 0 .. 3 + +typedef double MegaHertz; + +typedef struct global_clock +{ + ClockSource source; + Boolean set; + PLL_ClockId PLL_clock; +} GlobalClock; + +typedef struct pll_clock +{ + MegaHertz requested_frequency; + MegaHertz actual_frequency; + Boolean in_use; +} PLL_Clock; + +typedef struct pll_clock_info +{ + const char *name; + unsigned int PLL_register; + MegaHertz MIN_VCO_FREQ; + MegaHertz MAX_VCO_FREQ; +} PLL_ClockInfo; + + +/* -------------------------------------------------------------------------- */ +/* local data */ +/* -------------------------------------------------------------------------- */ + +#define ARC_SET_CLOCK_FREQUENCY_COMMAND "arc-set-clock-frequency" +#define ARC_SET_CLOCK_SOURCE_COMMAND "arc-set-clock-source" +#define ARC_CLOCK_SETTINGS_COMMAND "arc-clock-settings" +#define ARC_BLAST_BOARD_COMMAND "arc-blast-board" +#define ARC_FPGA_COMMAND "arc-fpga" + +#define ARC_SET_CLOCK_FREQUENCY_COMMAND_USAGE "Usage: " ARC_SET_CLOCK_FREQUENCY_COMMAND " [ <CLOCK> = ] <FREQUENCY> |\n" \ + " " \ + " <FREQUENCY> , <FREQUENCY>\n" + +#define ARC_SET_CLOCK_SOURCE_COMMAND_USAGE "Usage: " ARC_SET_CLOCK_SOURCE_COMMAND " gclk[N] = <SOURCE> |\n" \ + " " \ + " gclks = <SOURCE> { , <SOURCE> } |\n" \ + " " \ + " harvard\n" +#define ARC_CLOCK_SETTINGS_COMMAND_USAGE "Usage: info " ARC_CLOCK_SETTINGS_COMMAND "\n" +#define ARC_BLAST_BOARD_COMMAND_USAGE "Usage: " ARC_BLAST_BOARD_COMMAND " <FILE>\n" +#define ARC_FPGA_COMMAND_USAGE "Usage: info " ARC_FPGA_COMMAND "\n" + + +#define MAX_MAX_BURST 256 + +#define S_XOR (Byte) 0x80 /* XOR value with this to get all bits positive. */ +#define C_XOR (Byte) 0x0b /* with respect to the signal values. */ + +/* Control bits in the masks for the Control port. */ +#define STR (Byte) 0x01 // strobe +#define CNT (Byte) 0x02 +#define SS0 (Byte) 0x04 +#define SS1 (Byte) 0x08 +#define BI (Byte) 0x20 // bi-directional? + +/* Control bits in the masks for the Status port. */ +#define OP (Byte) 0x20 +#define ACK (Byte) 0x40 +#define BUSY (Byte) 0x80 + +/* Special meanings of some of those bits. */ +#define FPA_CFG_DONE OP +#define CFG_FROM_ROM BUSY +#define PAR_CFG_MODE ACK + + +/* Constants for the PLL. */ +#define MREG_ADDRESS 3 +#define VCLK_SETUP_REG_NO 0 /* which of Reg0 .. Reg2 is used to set the VClock freq. */ +#define MCLK_RESET_FREQUENCY (MegaHertz) 25.0 +#define VCLK_RESET_FREQUENCY (MegaHertz) 25.0 + +#define NUM_GLOBAL_CLOCKS 4 +#define NUM_PLL_CLOCKS 2 + +#define UNDEFINED_FREQUENCY (MegaHertz) (-1.0) + + +static const char *CLOCK_SOURCE_STRINGS[] = +{ + "High Impedance", + "PLL MCLK", + "PLL VCLK", + "Crystal", + "High Impedance", + "High Impedance", + "Host Strobe", + "Crystal With Division" +}; + +static const char *GCLOCK3_SOURCE_STRINGS[] = +{ + "High Impedance", + "PLL MCLK", + "PLL VCLK", + "Crystal", + "PLL MCLK (+Harvard)", + "PLL VCLK (+Harvard)", + "Host Strobe", + "Crystal With Division (+Harvard)" +}; + + +static const ClockSource default_GCLK_sources[] = +{ + CLOCK_SOURCE_HIGH_IMPEDANCE, // GCLK0 + CLOCK_SOURCE_CRYSTAL, // GCLK1 + CLOCK_SOURCE_HOST_STROBE, // GCLK2 + CLOCK_SOURCE_CRYSTAL_DIVIDED // GCLK3 +}; + + +static const MegaHertz VCO_PRESET_BOUNDARIES[] = {50.0, 51.0, 53.2, 58.5, 60.7, 64.4, 66.8, + 73.5, 75.6, 80.9, 83.2, 91.5, 100.0, 120.0}; + + +/* Unchanging information for the two PLL clocks. */ +static const PLL_ClockInfo PLL_clock_fixed_info[NUM_PLL_CLOCKS] = +{ + { "MCLK", MREG_ADDRESS, 52.0, 120.0 }, + { "VCLK", VCLK_SETUP_REG_NO, 65.0, 165.0 } +}; + + +/* Data describing the 2 PLL clocks and the 4 global clock sources. */ +static PLL_Clock PLL_clocks [NUM_PLL_CLOCKS]; +static GlobalClock global_clocks[NUM_GLOBAL_CLOCKS]; +static Boolean harvard; + + +/* -------------------------------------------------------------------------- */ +/* local macros */ +/* -------------------------------------------------------------------------- */ + +#define IS_SET(bit, byte) (((bit) & (byte)) == (bit)) +#define __MIN(X, Y) ((X) < (Y) ? (X) : (Y)) +#define __MAX(X, Y) ((X) < (Y) ? (Y) : (X)) + +#define FREQUENCY(clock) ((PLL_clocks[clock].in_use) ? PLL_clocks[clock].requested_frequency \ + : UNDEFINED_FREQUENCY) + +#define PLL_CLOCK_NAME(clock) PLL_clock_fixed_info[clock].name + + +/* -------------------------------------------------------------------------- */ +/* local functions */ +/* -------------------------------------------------------------------------- */ + +/* Sleep for the given number of milliseconds. */ + +static void +Sleep (unsigned int milliseconds) +{ + usleep((unsigned long) (1000 * milliseconds)); +} + + +/* Read a byte of data from the Status port. */ + +static Byte +read_status_port (void) +{ + return gpio_read(STATUS_PORT) ^ S_XOR; +} + + +/* Write a byte of data to the Control port, then sleep for the given delay. */ + +static void +write_control_port (Byte data, unsigned int delay) +{ + Byte value = data ^ C_XOR; + + gpio_write(CONTROL_PORT, value); + Sleep(delay); +} + + +/* Write a byte of data to the Data port. */ + +static void +write_data_port (Byte value) +{ + gpio_write(DATA_PORT, value); +} + + +/* Extract the value from a string containing a name/value pair of the form + + [ <name> = ] <value> + + Return 0 if the string is not of the given form + 1 if the string is of the form <value> + 2 if the string is of the form <name> = <value> +*/ + +static int +name_value_pair (char *args, char **value) +{ + char *equals = strchr(args, '='); + + if (equals) + { + char *val = equals + 1; + + /* If the key is missing from the argument string. */ + if (equals == args) + return 0; + + equals--; + while (*equals == ' ') equals--; + equals[1] = '\0'; + + while (*val == ' ') val++; + if (*val == '\0') + return 0; + + *value = val; + return 2; + } + + return 1; +} + + +/* -------------------------------------------------------------------------- */ +/* local functions for FPGA blasting */ +/* -------------------------------------------------------------------------- */ + +/* Initialize the FPGA ready for blasting. + Return TRUE if the initialization is successful. */ + +static Boolean +initialize_FPGA (void) +{ + Byte status; + Byte iOriginalState; + Byte iControlState; + unsigned int cpld_rev; + + ENTERMSG; + + /* snapshot the control port. */ + iOriginalState = gpio_read(CONTROL_PORT); + + /* Initialize FPGA by taking SS0 and SS1 low (all other ctrl's low as well). */ + iControlState = iOriginalState & (0xFFFFFFFF ^ (SS0 | SS1 | CNT | STR | BI)); + write_control_port(iControlState, 51); + + /* Tri-state port outputs so we can read CPLD revision number. */ + iControlState = iControlState | BI; + write_control_port(iControlState, 1); + + // Read the CPLD revision number LSB. */ + cpld_rev = (unsigned int) gpio_read(DATA_PORT); + + /* Set CNT high and read CPLD revision number MSB. */ + iControlState = iControlState | CNT; + write_control_port(iControlState, 1); + + cpld_rev += (unsigned int) gpio_read(DATA_PORT) << 8; + + /* Test the CPLD rev no; if it is 0xffff then this CPLD may not support + parallel blasting. */ + if ((cpld_rev & 0xffff) == 0xffff) + { + warning(_("old board type (AA2), not supported")); + gpio_close(); + return FALSE; + } + else + { + char rev_string[32]; + unsigned int temp = cpld_rev; + unsigned int char_pos = 0; + unsigned int i; + + for (i = 0; i < 16; i++) + { + if (temp & 0x8000) + rev_string[char_pos++] = '1'; + else + rev_string[char_pos++] = '0'; + + temp <<= 1; + if ((i % 4) == 3) + rev_string[char_pos++] = ' '; + } + + rev_string[char_pos] = '\0'; + + printf_filtered(_("\nCPLD Revision = %20s\n"), rev_string); + } + + /* Take CNT low. */ + iControlState = iControlState & (0xFFFFFFFF ^ (SS0 | SS1 | CNT | STR | BI)); + + /* Now take STR high, CNT low and SS0 high to enter FPGA download mode. */ + iControlState = iControlState | STR; + write_control_port(iControlState, 1); + + iControlState = iControlState | SS0; + write_control_port(iControlState, 3); + + /* Check that FPA_CFG_DONE=0. */ + status = read_status_port(); + + if (IS_SET(FPA_CFG_DONE, status)) + { + warning(_("FPGA is not responding - status = 0x%08x"), status); + gpio_close(); + return FALSE; + } + + LEAVEMSG; + + return TRUE; +} + + +/* Try to send data to the target in a parallel stream. + Return TRUE if it is sent. */ + +static Boolean +parallel_send_data (Byte *buffer, unsigned int count) +{ + GPIO_Pair arr[MAX_MAX_BURST * 3 + 1]; + + /* Work out how many bytes we can send in one PIO program. */ + unsigned const int bytes_per_burst = 127; + + /* Initialize offsets into config data buffer. */ + unsigned int burst_start = 0; + unsigned int burst_end = bytes_per_burst - 1; + + /* Snapshot the control port. */ + Byte iOriginalState = gpio_read(CONTROL_PORT); + Byte iControlState = (iOriginalState | SS0) & (0xFFFFFFFF ^ (SS1 | CNT | STR | BI)); + + while (TRUE) + { + GPIO_Pair *gpio = arr; + unsigned int i; + + /* Do not try to write more data than is in the buffer. */ + if (burst_end > (count - 1)) + burst_end = count - 1; + + /* Initialize the gpio driver instruction stream. */ + for (i = burst_start; i <= burst_end; i++) + { + gpio->port = DATA_PORT; + gpio->data = buffer[i]; + gpio++; + gpio->port = CONTROL_PORT; + gpio->data = iControlState ^ C_XOR; + gpio++; + gpio->port = CONTROL_PORT; + gpio->data = (iControlState | STR) ^ C_XOR; + gpio++; + } + + gpio_write_array(arr, gpio - arr); + + /* Last block of data written. */ + if (burst_end == count - 1) + break; + + burst_start = burst_end + 1; + burst_end = burst_start + bytes_per_burst - 1; + } + + return TRUE; +} + + +/* Try to send data to the target in a serial stream. + Return TRUE if it is sent. */ + +static Boolean +serial_send_data (Byte *buff, unsigned int count) +{ + /* There is code which implements serial blasting in the SeeCode debugger + file os/arc/connect/par/arc/aa3blast.cpp, which is intended to work with + either Win95 or WinNT. If serial blasting is required for Linux, this + code would have to be re-written to use Linux O/S operations. However, + there is currently no requirement for that. */ + warning(_("sorry, serial download is not supported")); + return FALSE; +} + + + +/* Try to blast the target board FPGA with the contents of an XBF file. + Return TRUE if the blast is succcessful. */ + +static Boolean +blast_FPGA (FILE *xbf) +{ + Boolean parallel_cfg; + unsigned long file_size; + unsigned long five_percent; + unsigned long bytes_sent = 0; + unsigned long twentieths_complete = 0; + Byte status; + + ENTERMSG; + + /* Get parallel port status, and see whether the board is expecting parallel + or serial blast. */ + status = read_status_port(); + + if ((status & CFG_FROM_ROM) == CFG_FROM_ROM) + { + /* Oops - FPGA is configured from ROM! */ + if (IS_SET(FPA_CFG_DONE, status)) + printf_filtered(_("FPGA is configured from ROM")); + else + warning(_("FPGA should be configured from ROM - BUT IT IS NOT!")); + return FALSE; + } + + parallel_cfg = ((status & PAR_CFG_MODE) == PAR_CFG_MODE); + + /* Find the length of the file (could use fstat instead here). */ + (void) fseek(xbf, 0, SEEK_END); + file_size = (unsigned long) ftell(xbf); + (void) fseek(xbf, 0, SEEK_SET); + + five_percent = file_size / 20; + + /* Read file and blast. */ + + while (TRUE) + { + Byte data_buffer[1024]; + size_t n_bytes = fread(data_buffer, 1, sizeof(data_buffer), xbf); + + if (gpio_port_error) + error(_("Error in accessing JTAG port (device " GPIO_DEVICE ")")); + + /* End of file reached? (fread returns 0 for both EOF and error!). */ + if (n_bytes == 0) + { + if (!feof(xbf)) + { + warning(_("error in reading XBF file")); + return FALSE; + } + break; + } + + if (!(((parallel_cfg) ? parallel_send_data + : serial_send_data) (data_buffer, (unsigned int) n_bytes))) + break; + + bytes_sent += n_bytes; + if (bytes_sent == file_size) + break; + + if ((bytes_sent / five_percent) > twentieths_complete) + { + twentieths_complete++; + printf_filtered(_("*")); + gdb_flush (gdb_stdout); + } + } + + printf_filtered(_("\n")); + + /* Check for the ConfigDone signal. */ + status = read_status_port(); + + if (!IS_SET(FPA_CFG_DONE, status)) + { + warning(_("FPGA configuration failed")); + return FALSE; + } + + printf_filtered(_("FPGA configured\n")); + + /* Set SS0 and SS1 high to take board out of reset. */ + { + Byte iControlState = (gpio_read(CONTROL_PORT) | SS0 | SS1 | STR) & (0xFFFFFFFF ^ (CNT | BI)); + + write_control_port(iControlState, 1); + } + + LEAVEMSG; + + return TRUE; +} + + +/* -------------------------------------------------------------------------- */ +/* local functions for setting clocks */ +/* -------------------------------------------------------------------------- */ + +/* Reset the clock configuration information to its default values (i.e. the + values that the h/w has after a hard reset of the target. */ + +static void +reset_clock_configuration (void) +{ + unsigned int i; + + for (i = 0; i < ELEMENTS_IN_ARRAY(PLL_clocks); i++) + { + PLL_clocks[i].requested_frequency = MCLK_RESET_FREQUENCY; + PLL_clocks[i].actual_frequency = VCLK_RESET_FREQUENCY; + PLL_clocks[i].in_use = FALSE; + } + + for (i = 0; i < ELEMENTS_IN_ARRAY(global_clocks); i++) + { + global_clocks[i].source = default_GCLK_sources[i]; + global_clocks[i].set = FALSE; + global_clocks[i].PLL_clock = NO_PLL_CLK; + } + + harvard = FALSE; +} + + +/* Calculate the control word required to set a PLL clock to a particular frequency. + + Parameters: + requested_frequency : the frequency we want + min_vco_frequency : the minimum VCO frequency for this clock + max_vco_frequency : the maximum VCO frequency for this clock + actual_frequency : the frequency we actually get + + Result: the control word; 0 if no frequency can be set + + The PLL consists of a VCO and 3 counters that divide by p, q and 2^d. The + VCO runs at 2*RefClk*p/q. This is divided by 2^d to give the PLL output. + + There are several contraints on the various values: + 4 <= p <= 130 + 3 <= q <= 129 + 0 <= d <= 7 + 0.2 <= ref_clk / q <= 1.0 (200kHz .. 1MHz) + + This method is a bit of a palaver - very procedural. Basically it uses + trial and error to find the best values for d, p and q, within the given + contraints. */ + +static unsigned int +calculate_ctrl_word (const MegaHertz requested_frequency, + const MegaHertz min_vco_frequency, + const MegaHertz max_vco_frequency, + MegaHertz *actual_frequency) +{ + const unsigned int MIN_P = 4; + const unsigned int MAX_P = 130; + const unsigned int MIN_Q = 3; + const unsigned int MAX_Q = 129; + const unsigned int MIN_D = 0; + const unsigned int MAX_D = 7; + const MegaHertz MIN_REF_CLK_OVER_Q = 0.2; + const MegaHertz MAX_REF_CLK_OVER_Q = 1.0; + const MegaHertz REF_CLK = 14.31818; // input to PLL + +#define NUM_PRESETS ELEMENTS_IN_ARRAY(VCO_PRESET_BOUNDARIES) + + unsigned int index, p = 0, q = 0, d = MIN_D; /* PLL parameters (see ICD2061A Data Sheet). */ + unsigned int trial_p, trial_q; /* Temp vars for p & q values that we are trying out. */ + unsigned int first_q, last_q; + unsigned int ctrl_word; + double min_delta = 1.0; /* Smallest error so far. */ + MegaHertz vco_frequency = requested_frequency; /* Freq at which the VCO will run. */ + double p_over_q; + + /* Find a value of d which gives a VCO frequency that is within limits (VCO + output is divided by 2^d). */ + while ((vco_frequency < min_vco_frequency) && (d < MAX_D)) + { + vco_frequency *= 2; + d++; + } + + DEBUG("request = %g, vco = %g, min = %g, max = %g, d = %d\n", + requested_frequency, vco_frequency, min_vco_frequency, max_vco_frequency, d); + + /* Check that we have found a suitable value for d. */ + if ((vco_frequency < min_vco_frequency) || (vco_frequency > max_vco_frequency)) + { + DEBUG("frequency is out of range\n"); + return 0; + } + + /* Calculate the ratio needed for p/q, to get vco_frequency from ref_clk. */ + p_over_q = vco_frequency / (2.0 * REF_CLK); + + /* Now use some brute force and ignorance to find the best values for p & q: + we look for p & q such that p / q is the best approximation to p_over_q. */ + + /* Calculate range of values allowed for q. */ + first_q = __MAX((unsigned int) (REF_CLK / MAX_REF_CLK_OVER_Q + 0.999999), MIN_Q); + last_q = __MIN((unsigned int) (REF_CLK / MIN_REF_CLK_OVER_Q), MAX_Q); + + /* Look at each possible value of q. */ + for (trial_q = first_q; trial_q <= last_q; trial_q++) + { + /* Calculate the value of p needed with this q value. */ + double raw_p = p_over_q * (double) trial_q; + double delta; + + /* Round the raw value for p to the nearest integer. */ + trial_p = (unsigned int) (raw_p + 0.5); + + /* Range check the required p value: note that because trial_q is + increasing, trial_p is also increasing, so if it is less than MIN_P + we may find a suitable value in a later iteration, whereas if it is + greater than MAX_P we will never find a suitable value in a later + iteration. */ + if (trial_p < MIN_P) + continue; + if (trial_p > MAX_P) + break; + + /* See how much error is caused by p being an integer. */ + delta = fabs (1.0 - ((double) trial_p / raw_p)); + + /* If this is the most accurate so far, then keep track of it. */ + if (delta < min_delta) + { + p = trial_p; + q = trial_q; + + /* If it is exact then quit (we won't be able to find a better approximation!). */ + if (min_delta == 0.0) + break; + + min_delta = delta; + } + } + + /* Just in case. */ + if (p == 0) + { + DEBUG("loop failed to find p & q!"); + return 0; + } + + /* Have sorted out values for p, q & d - now form them into a control word. */ + + /* First, look up the value for Index (VCO preset). */ + for (index = 0; index < NUM_PRESETS; index++) + { + if (VCO_PRESET_BOUNDARIES[index] > vco_frequency) + break; + } + + // make sure we have found a suitable value for I + if ((index == 0) || (index == NUM_PRESETS)) + { + DEBUG("can not find preset for %g\n", vco_frequency); + return 0; + } + + /* The index must now be in the range 1 .. 13; so subtract 1, to change the + range to 0 .. 12 as required by the encoding. */ + index--; + + /* Return the frequency calculated as best approximation to the one requested. */ + *actual_frequency = (2.0 * REF_CLK * p / q) / (1 << d); + + DEBUG("p = %d, q = %d, d = %d, I = %d\n", p, q, d, index); + + /* The ranges for p, q & d are: + + I : 0 .. 12 + p : 4 .. 130 + d : 0 .. 7 + q : 3 .. 129 + + Subtracting a bias of 3 from p and 2 from q converts these to: + + I : 0 .. 12 which can be held in 4 bits + p : 1 .. 127 which can be held in 7 bits + d : 0 .. 7 which can be held in 3 bits + q : 1 .. 126 which can be held in 7 bits + + which gives a control word with bitfields: + + 00000000000IIIIPPPPPPPDDDQQQQQQQ + + Note that 0 is not a valid value for the control word, which is why + it is safe to return 0 from this function in the error cases. */ + + ctrl_word = (index & 0xf); + ctrl_word = (ctrl_word << 7) | ((p - 3) & 0x7f); + ctrl_word = (ctrl_word << 3) | (d & 0x7); + ctrl_word = (ctrl_word << 7) | ((q - 2) & 0x7f); + + return ctrl_word; +} + + +/* Write a control word to the PLL clock control register whose address is given. + Return TRUE if the write is successful. */ + +static Boolean +write_PLL_register (unsigned int address, unsigned int ctrl_word) +{ + const Byte S0S1_FINAL_STATE[] = {(Byte) 0x0, (Byte) 0x1, (Byte) 0x2}; + const Byte PLL_CLK_BIT = (Byte) 0x08; + const Byte PLL_DATA_BIT = (Byte) 0x10; + unsigned int i; + Byte data; + Byte iControlState; + Byte iOriginalState; + int manchester_bitstream[64]; + + DEBUG("writing 0x%08X to PLL register %d\n", ctrl_word, address); + + /* Add the address in the MSBs of the ctrl_word: this gives us a 24-bit value + with the fields AAAIIIIPPPPPPPDDDQQQQQQQ */ + + ctrl_word = ((address & 0x7) << 21) | (ctrl_word & 0x1fffff); + + /* Create a bit stream at twice the data rate that incorporates the pseudo + Manchester encoding for the data and also the unlock sequence. */ + for (i = 0; i < 11; i++) + manchester_bitstream[i] = 1; + + /* The start bit. */ + manchester_bitstream[11] = 0; + manchester_bitstream[12] = 0; + manchester_bitstream[13] = 0; + + i = 14; + while (i < 62) + { + if ((ctrl_word & 0x1) == 0) + { + manchester_bitstream[i++] = 1; + manchester_bitstream[i++] = 0; + } + else + { + manchester_bitstream[i++] = 0; + manchester_bitstream[i++] = 1; + } + + ctrl_word >>= 1; + } + + /* The stop bit. */ + manchester_bitstream[62] = 1; + manchester_bitstream[63] = 1; + + /* Snapshot the control port state. */ + iOriginalState = gpio_read(CONTROL_PORT); + + /* Set the parallel port data to 0, in preparation for sending config data. */ + write_data_port((Byte) 0); + Sleep(2); + + /* Set CPLD into config mode. */ + + /* Set SS0=1, SS1=1, CNT=1, BIDir=0. */ + iControlState = (iOriginalState | SS0 | SS1 | CNT | BI) ^ BI; + write_control_port(iControlState, 2); + + /* Ensure STROBE is high. */ + iControlState = iControlState | STR; + write_control_port(iControlState, 2); + + /* Set CPLD into config mode by setting SS0=1, SS1=0, CNT=1. */ + iControlState = iControlState ^ SS1; + write_control_port(iControlState, 2); + + /* Now send the double rate data stream. */ + + // Set the clock high and data low. */ + data = PLL_CLK_BIT & ~PLL_DATA_BIT; + write_data_port(data); + //Sleep(1); + + for (i = 0; i < 64; i++) + { + /* Put the next Manchester code bit out. */ + if (manchester_bitstream[i] == 1) + data = data | PLL_DATA_BIT; + else + data = data & ~PLL_DATA_BIT; + + write_data_port(data); + //Sleep(1); + + /* Toggle the clock bit. */ + data = data ^ PLL_CLK_BIT; + write_data_port(data); + //Sleep(1); + } + + /* Set data/clock (alias s1/s0) to select the programmed divisor register + for the video clock. */ + write_data_port(S0S1_FINAL_STATE[VCLK_SETUP_REG_NO]); + //Sleep(1); + + /* Set CPLD into ARC-Run Host-Read mode. */ + iControlState = iControlState | SS1 | BI; + write_control_port(iControlState, 2); + + return TRUE; +} + + +/* Configure the target board's CPLD. */ + +static void +configure_CPLD (void) +{ + const Byte CPLD_CLK_BIT = (Byte) 0x01; + const Byte CPLD_DATA_BIT = (Byte) 0x02; + const Byte CPLD_SET_BIT = (Byte) 0x04; + const unsigned int NUM_OF_CPLD_CFG_BITS = 16; + unsigned int cpldConfigData = 0; + Byte iControlState; + unsigned int i; + + /* Snapshot the control port. */ + Byte iOriginalState = gpio_read(CONTROL_PORT); + + /* Set the parallel port data to 0, in preparation for sending config data. */ + write_data_port((Byte) 0); + Sleep(1); + + /* Set CPLD into configuration mode. */ + + /* Set SS0=1, SS1=1, CNT=1, BIDir=0. */ + iControlState = (iOriginalState | SS0 | SS1 | CNT | BI) ^ BI; + write_control_port(iControlState, 2); + + /* Ensure STROBE is high. */ + iControlState = iControlState | STR; + write_control_port(iControlState, 2); + + /* Set CPLD into config mode by setting SS0=1, SS1=0, CNT=1. */ + iControlState = iControlState ^ SS1; + write_control_port(iControlState, 2); + + /* Now send the config data stream with set low. */ + + /* Set clock high and data low. */ + + for (i = 0; i < ELEMENTS_IN_ARRAY(global_clocks); i++) + cpldConfigData += (unsigned int) global_clocks[i].source << (3 * i); + + for (i = 0; i < NUM_OF_CPLD_CFG_BITS; i++) + { + Byte value; + + /* See if the next cfg bit is 0 or 1. */ + if ((cpldConfigData & 0x1) == 0x1) + value = CPLD_DATA_BIT; + else + value = (Byte) 0; + + /* Put data bit out to parallel port. */ + write_data_port(value); + + /* And toggle the clock line. */ + value |= CPLD_CLK_BIT; + write_data_port(value); + + value &= ~CPLD_CLK_BIT; + write_data_port(value); + + cpldConfigData >>= 1; + } + + /* Now take the clock and set bits high. */ + write_data_port(CPLD_CLK_BIT | CPLD_SET_BIT); + + /* Finally, take the clock low. */ + write_data_port(CPLD_SET_BIT); + + /* And put the CPLD into ARC run mode, SS0=1, SS1= 1, CNT=x. */ + iControlState = iControlState | SS1 | BI; + write_control_port(iControlState, 2); +} + + +/* Try to set the frequency of a PLL clock. + + Parameters: + clock : the identity of the clock (MCLK or VCLK) + requested_frequency: the desired frequency fro the clock + inform : TRUE if a message should be output if the clock is set + emit_warning : TRUE if a warning should be output if the clock is not set + + Returns TRUE if the clock is set. +*/ + +static Boolean +set_PLL_clock_frequency (PLL_ClockId clock, + MegaHertz requested_frequency, + Boolean inform, + Boolean emit_warning) +{ + /* First need to work out the control words for the frequencies set. */ + MegaHertz actual_frequency = UNDEFINED_FREQUENCY; + unsigned int ctrl_word = calculate_ctrl_word (requested_frequency, + PLL_clock_fixed_info[clock].MIN_VCO_FREQ, + PLL_clock_fixed_info[clock].MAX_VCO_FREQ, + &actual_frequency); + Boolean set; + + DEBUG("set_PLL_clock_frequency: %s ctrl_word = %08X, freq = %.2lf MHz\n", + PLL_CLOCK_NAME(clock), ctrl_word, requested_frequency); + + if (ctrl_word == 0) + { + if (emit_warning) + warning(_("it is not possible to set %s to %.2lf"), + PLL_CLOCK_NAME(clock), requested_frequency); + return FALSE; + } + + DEBUG("set_PLL_clock_frequency: %s %.2lf, %.2lf, %.2lf\n", + PLL_CLOCK_NAME(clock), + requested_frequency, + actual_frequency, + PLL_clocks[clock].actual_frequency); + + if (actual_frequency != PLL_clocks[clock].actual_frequency) + { + /* Set up the PLL chip. We program the MREG, the REG0/1/2 - whichever + is selected to control VCLK. */ + set = write_PLL_register (PLL_clock_fixed_info[clock].PLL_register, ctrl_word); + + if (set) + { + PLL_clocks[clock].requested_frequency = requested_frequency; + PLL_clocks[clock].actual_frequency = actual_frequency; + } + else + if (emit_warning) + warning(_("PLL programming failed")); + } + else + set = TRUE; + + if (set && inform) + printf_filtered(_("PLL clock %s set to %.2lf MHz.\n"), PLL_CLOCK_NAME(clock), actual_frequency); + + return set; +} + + +/* Check the frequencies of the two PLL clocks, and emit a warning if necessary. + + The ICD2061A Data Sheet recommends that the two clocks should not be set to + frequencies such that one is an integer multiple of the other, in order to + avoid jitter. */ + +static void +check_PLL_clock_frequencies (void) +{ + DEBUG("check_PLL_clock_frequencies\n"); + + /* If both clocks are in use. */ + if (PLL_clocks[PLL_MCLK].in_use && PLL_clocks[PLL_VCLK].in_use) + { + /* Check whether the two chosen clocks are divisible by one another, in + which case print a warning. */ + double multiplier = PLL_clocks[PLL_VCLK].actual_frequency / + PLL_clocks[PLL_MCLK].actual_frequency; + double modulus; + + if (multiplier < 1.00) + multiplier = 1 / multiplier; + + modulus = multiplier - ((int) multiplier); + + /* Check also for near multiples. */ + if ((modulus < 0.02) || (modulus > 0.98)) + warning(_("PLL MCLK and PLL VCLK frequencies are (near) multiples of each other.\n" + "This may lead to clock degradation.")); + } + else if (PLL_clocks[PLL_MCLK].in_use || PLL_clocks[PLL_VCLK].in_use) + { + MegaHertz requested_frequency; + MegaHertz actual_frequency; + PLL_ClockId clock; + + /* If we now are only using one PLL clock then ensure that the second + clock's frequency is not a multiple of the first's (M == V is OK). */ + if (PLL_clocks[PLL_MCLK].in_use) + { + clock = PLL_VCLK; + actual_frequency = PLL_clocks[PLL_MCLK].actual_frequency; + } + else + { + clock = PLL_MCLK; + actual_frequency = PLL_clocks[PLL_VCLK].actual_frequency; + } + + if (actual_frequency < VCO_PRESET_BOUNDARIES[0]) + requested_frequency = actual_frequency * 1.43; + else + requested_frequency = actual_frequency; + + if (!set_PLL_clock_frequency(clock, requested_frequency, TRUE, FALSE)) + (void) set_PLL_clock_frequency(clock, actual_frequency, TRUE, TRUE); + } +} + + +/* Try to set the source of the given global clock to be a PLL clock set to the + given frequency. + + If one of the PLL clocks is already set to the given frequency, we use that + as the source; otherwise, if the global clock's source is a PLL clock, and + no other global clock is using that PLL clock as its source, we change its + frequency to the required frequency; otherwise, if the other PLL clock is not + already in use, we set that other clock to the required frequency and use it + as the source; otherwise (both PLL clocks are in use), we find the clock + whose frequency is closest to the required frequency and use that clock as + the source. */ + +static void +use_PLL_clock (GlobalClockId clockId, MegaHertz clockValue) +{ + PLL_ClockId PLL_clock_id = NO_PLL_CLK; + PLL_ClockId free_PLL_clock_id = NO_PLL_CLK; + unsigned int i; + + /* Is this global clock not already using a PLL clock? */ + if (global_clocks[clockId].PLL_clock == NO_PLL_CLK) + { + /* Has this frequency already been assigned to a PLL clock? */ + for (i = 0; i < ELEMENTS_IN_ARRAY(PLL_clocks); i++) + { + PLL_Clock* clock = &PLL_clocks[i]; + + if (clock->in_use) + { + /* The actual frequency to which the clock is set may differ + slightly from the frequency that was requested - so check + both. */ + if (clockValue == clock->requested_frequency || + clockValue == clock->actual_frequency) + { + PLL_clock_id = (PLL_ClockId) i; + break; + } + } + else + { + /* Use MCLK (first in array) in preference to VCLK. */ + if (free_PLL_clock_id == NO_PLL_CLK) + free_PLL_clock_id = (PLL_ClockId) i; + } + } + } + else + { + PLL_ClockId this_clock = global_clocks[clockId].PLL_clock; + int users = 0; + + /* How many global clocks are using this PLL clock? */ + for (i = 0; i < ELEMENTS_IN_ARRAY(global_clocks); i++) + { + if (global_clocks[i].PLL_clock == this_clock) + users++; + } + + if (users == 1) + { + /* Just this one - so we can change its frequency without affecting + any other global clocks. */ + free_PLL_clock_id = this_clock; + } + else + { + /* Look at the other clock - if it is not already in use, we can use it. */ + PLL_ClockId other_clock = (this_clock == PLL_MCLK) ? PLL_VCLK : PLL_MCLK; + + if (!PLL_clocks[other_clock].in_use) + free_PLL_clock_id = other_clock; + } + } + + /* Do we need another PLL clock? */ + if (PLL_clock_id == NO_PLL_CLK) + { + /* If so, and there aren't any which are not in use. */ + if (free_PLL_clock_id == NO_PLL_CLK) + { + MegaHertz M_delta = fabs(PLL_clocks[PLL_MCLK].actual_frequency - clockValue); + MegaHertz V_delta = fabs(PLL_clocks[PLL_VCLK].actual_frequency - clockValue); + + /* Which clock has the closet frequency to what we want? */ + PLL_clock_id = (M_delta <= V_delta) ? PLL_MCLK : PLL_VCLK; + + warning(_("can not set GCLK%d to %.2lf MHz - " + "there are no more PLL clocks available.\n" + "Using closest match instead (%s @ %.2lf MHz)."), + clockId, clockValue, + PLL_CLOCK_NAME(PLL_clock_id), + PLL_clocks[PLL_clock_id].actual_frequency); + } + else + { + /* Otherwise, use a free PLL clock. */ + PLL_clock_id = free_PLL_clock_id; + + if (set_PLL_clock_frequency(PLL_clock_id, clockValue, TRUE, TRUE)) + { + PLL_clocks[PLL_clock_id].in_use = TRUE; + check_PLL_clock_frequencies(); + } + } + } + + global_clocks[clockId].PLL_clock = PLL_clock_id; + global_clocks[clockId].source = (PLL_clock_id == PLL_MCLK) ? CLOCK_SOURCE_PLL_MCLK : CLOCK_SOURCE_PLL_VCLK; + global_clocks[clockId].set = TRUE; +} + + +/* Set the source of the given global clock as specified. + This may be one of: crystal, dips, highimp, host, mclk, vclk or <frequency>. + Specifying an explicit frequency means that the source should be a PLL clock + set to that frequency. */ + +static void +set_global_clock (GlobalClockId clockId, const char *clockData) +{ + static const struct table_entry + { + ClockSource source; + PLL_ClockId clock; + const char *name; + Boolean harvard; + } table[] = + { { CLOCK_SOURCE_CRYSTAL, NO_PLL_CLK, "crystal", FALSE }, + { CLOCK_SOURCE_CRYSTAL_DIVIDED, NO_PLL_CLK, "dips", FALSE }, + { CLOCK_SOURCE_HIGH_IMPEDANCE, NO_PLL_CLK, "highimp", FALSE }, + { CLOCK_SOURCE_HOST_STROBE, NO_PLL_CLK, "host", FALSE }, + { CLOCK_SOURCE_PLL_MCLK_HARVARD, PLL_MCLK, "mclk", TRUE }, + { CLOCK_SOURCE_PLL_VCLK_HARVARD, PLL_MCLK, "vclk", TRUE }, + { CLOCK_SOURCE_PLL_MCLK, PLL_MCLK, "mclk", FALSE }, + { CLOCK_SOURCE_PLL_VCLK, PLL_VCLK, "vclk", FALSE } }; + + MegaHertz clockValue; + unsigned int i; + + /* Look at each possible clock source in the table. */ + for (i = 0; i < ELEMENTS_IN_ARRAY(table); i++) + { + const struct table_entry *entry = &table[i]; + + if (strcasecmp(clockData, entry->name) == 0) + { + /* N.B. the order of the entries in the table is important! */ + if (entry->harvard && !harvard) + continue; + + global_clocks[clockId].source = entry->source; + global_clocks[clockId].PLL_clock = entry->clock; + global_clocks[clockId].set = TRUE; + + /* N.B. "high impedance" effectively means "off". */ + if ((clockId == 3) && (entry->source == CLOCK_SOURCE_HIGH_IMPEDANCE)) + { + warning(_("GCLK3 must be valid in order for the ARC processor's debug interface to interact with the processor.")); + } + else if ((clockId == 2) && (entry->source != CLOCK_SOURCE_HOST_STROBE)) + { + warning(_("GCLK2 must be %s for the JTAG clock to be connected to the ARC processor's debug interface."), + CLOCK_SOURCE_STRINGS[CLOCK_SOURCE_HOST_STROBE]); + } + + return; + } + } + + /* We did not find a match in the table - so the given source may be a frequency. */ + if (sscanf(clockData, "%lf", &clockValue) == 1) + { + if (clockId == 2) + { + warning(_("GCLK2 must be %s for the JTAG clock to be connected to the ARC processor's debug interface."), + CLOCK_SOURCE_STRINGS[CLOCK_SOURCE_HOST_STROBE]); + } + + use_PLL_clock(clockId, clockValue); + } + else + warning(_("'%s' is not a valid source for clock %d\n"), clockData, clockId); +} + + +/* Enable Harvard clock to drive global clock GLK3. */ + +static void +enable_Harvard_clock (void) +{ + /* Does GCLK3 come from the PLL? */ + if (global_clocks[3].PLL_clock != NO_PLL_CLK) + { + /* Save existing settings. */ + const PLL_ClockId saved_clock[] = {global_clocks[0].PLL_clock, + global_clocks[1].PLL_clock, + global_clocks[2].PLL_clock, + global_clocks[3].PLL_clock}; + const MegaHertz saved_value[] = {PLL_clocks[PLL_MCLK].actual_frequency, + PLL_clocks[PLL_VCLK].actual_frequency}; + GlobalClockId clockId; + + printf_filtered(_("Configuring clocks to drive Harvard Ctl_Clk.\n")); + + reset_clock_configuration(); + harvard = TRUE; + + /* Now re-assign the Harvard inputs and double the requested frequency. */ + use_PLL_clock(3, 2 * saved_value[saved_clock[3]]); + + /* Now ensure GCLK3 is configured as a Harvard generator. */ + if (saved_clock[3] == PLL_MCLK) + global_clocks[3].source = CLOCK_SOURCE_PLL_MCLK_HARVARD; + else + global_clocks[3].source = CLOCK_SOURCE_PLL_VCLK_HARVARD; + + /* Re-assign any existing PLL clocks. */ + for (clockId = 0; clockId < 3; clockId++) + { + if (saved_clock[clockId] != NO_PLL_CLK) + use_PLL_clock(saved_clock[clockId], saved_value[saved_clock[clockId]]); + } + } +} + + +/* Print out the settings of the PLL clocks and the global clock sources. + + Parameters: + with_PLL_clocks : if TRUE, print the settings of the PLL clocks + with_global_only_if_using_PLL: if TRUE, print the sources of the global + clocks only if at least one of those sources + is a PLL clock + */ + +static void +print_clock_settings (Boolean with_PLL_clocks, Boolean with_global_only_if_using_PLL) +{ + Boolean with_global_clocks = TRUE; + unsigned int i; + + if (with_global_only_if_using_PLL) + { + with_global_clocks = FALSE; + + for (i = 0; i < ELEMENTS_IN_ARRAY(global_clocks); i++) + { + if (global_clocks[i].PLL_clock != NO_PLL_CLK) + { + with_global_clocks = TRUE; + break; + } + } + } + + if (with_PLL_clocks) + { + printf_filtered(_("PLL clock %s : %.2lf MHz.\n"), PLL_CLOCK_NAME(PLL_MCLK), PLL_clocks[PLL_MCLK].actual_frequency); + printf_filtered(_("PLL clock %s : %.2lf MHz.\n"), PLL_CLOCK_NAME(PLL_VCLK), PLL_clocks[PLL_VCLK].actual_frequency); + } + + if (with_global_clocks) + { + for (i = 0; i < ELEMENTS_IN_ARRAY(global_clocks); i++) + { + GlobalClock clock = global_clocks[i]; + const char *format = "GCLK%d << %s @ %.2lf MHz\n"; + const char *source; + MegaHertz value; + + switch (clock.source) + { + case CLOCK_SOURCE_PLL_MCLK: + source = CLOCK_SOURCE_STRINGS[clock.source]; + value = PLL_clocks[PLL_MCLK].actual_frequency; + break; + + case CLOCK_SOURCE_PLL_VCLK: + source = CLOCK_SOURCE_STRINGS[clock.source]; + value = PLL_clocks[PLL_VCLK].actual_frequency; + break; + + case CLOCK_SOURCE_PLL_MCLK_HARVARD: + source = GCLOCK3_SOURCE_STRINGS[clock.source]; + value = PLL_clocks[PLL_MCLK].actual_frequency / 2.0; + break; + + case CLOCK_SOURCE_PLL_VCLK_HARVARD: + source = GCLOCK3_SOURCE_STRINGS[clock.source]; + value = PLL_clocks[PLL_VCLK].actual_frequency / 2.0; + break; + + default: + format = "GCLK%d << %s\n"; + source = ((i == 3) ? GCLOCK3_SOURCE_STRINGS : CLOCK_SOURCE_STRINGS)[clock.source]; + value = 0.0; + break; + } + + printf_filtered(format, i, source, value); + } + } +} + + +/* Set the two PLL clocks to the given frequencies. */ + +static void +set_PLL_clocks (MegaHertz requested_MCLK_frequency, + MegaHertz requested_VCLK_frequency) +{ + DEBUG("set_PLL_clocks: MCLK = %.2lf MHz, VCLK = %.2lf MHz\n", + requested_MCLK_frequency, requested_VCLK_frequency); + + /* Configure PLL clocks. */ + + if (requested_MCLK_frequency != UNDEFINED_FREQUENCY) + { + if (set_PLL_clock_frequency(PLL_MCLK, requested_MCLK_frequency, TRUE, TRUE)) + PLL_clocks[PLL_MCLK].in_use = TRUE; + } + + if (requested_VCLK_frequency != UNDEFINED_FREQUENCY) + { + if (set_PLL_clock_frequency(PLL_VCLK, requested_VCLK_frequency, TRUE, TRUE)) + PLL_clocks[PLL_VCLK].in_use = TRUE; + } +} + + +/* Set the clock settings. + + The PLL clocks are set only if this is being done after the target board + FPGA has been blasted. + If any of the global clock sources needs to be set, the target CPLD is + configured, and the given message is printed out. */ + +static void +program_clock_settings (const char *message, Boolean after_blast) +{ + unsigned int i; + + /* If the FPGA has been blasted, configure the PLL clocks. */ + if (after_blast) + set_PLL_clocks(FREQUENCY(PLL_MCLK), FREQUENCY(PLL_VCLK)); + + /* Do any of the global clocks need to be set? */ + for (i = 0; i < ELEMENTS_IN_ARRAY(global_clocks); i++) + { + if (global_clocks[i].set) + { + /* Print status message only if there is something to be done. */ + printf_filtered("%s\n", message); + + if (harvard) + enable_Harvard_clock(); + + configure_CPLD(); + + print_clock_settings(after_blast, FALSE); + break; + } + } + + /* Reset the JTAG Test Access Port Controller. */ + arc_jtag_ops.reset(); +} + + +/* -------------------------------------------------------------------------- */ +/* local functions for blasting the FPGA */ +/* -------------------------------------------------------------------------- */ + +/* Try to blast the target board FPGA. + Return TRUE if blasting is done. */ + +static Boolean +blast_board (char *args, int from_tty) +{ + /* Check that a file name has been given. */ + if (args == NULL) + printf_filtered (_(ARC_BLAST_BOARD_COMMAND_USAGE)); + else + { + char *suffix = strrchr(args, '.'); + + /* Check the file is an .xbf file. */ + if ((suffix != NULL) && (strcasecmp(suffix, ".xbf") == 0)) + { + FILE *fp; + + /* Check that the JTAG interface (which opens the GPIO driver) is open + (do this before opening the file, as this function does not return + here if the interface is not open). */ + arc_jtag_ops.check_open(); + + fp = fopen(args, "rb"); + + if (fp) + { + char *message = NULL; + + if (initialize_FPGA()) + { + if (blast_FPGA(fp)) + { + /* Reset the JTAG Test Access Port Controller. */ + arc_jtag_ops.reset(); + + program_clock_settings(_("Reconfiguring clock settings after FPGA blast."), TRUE); + + return TRUE; + } + else + message = _("Can not blast FPGA"); + } + else + message = _("Can not initialize FPGA for blasting"); + + (void) fclose(fp); + + if (message) + error("%s", message); + } + else + error(_("Can not open file '%s': %s"), args, strerror(errno)); + } + else + error(_("Filename does not have suffix .xbf, so is presumably not an XBF file")); + } + + return FALSE; +} + + +/* -------------------------------------------------------------------------- */ +/* local functions implementing commands */ +/* -------------------------------------------------------------------------- */ + +/* Command: <command> <XBF_file> + + Blast the target board's FPGA with an XBF file. */ + +static void +arc_blast_board_FPGA (char *args, int from_tty) +{ + if (blast_board(args, from_tty)) + { + /* We no longer know what the target processor is. */ + arc_architecture_is_unknown(); + + /* So find it out again. */ + arc_update_architecture(arc_read_jtag_aux_register); + + /* And check that it matches the aux registers and the executable file. */ + ARCHITECTURE_CHECK(current_gdbarch, + (current_objfile) ? current_objfile->obfd : NULL); + } +} + + +/* Command: <command> [ <clock> = ] <frequency> [ , <frequency> ] + + Set the frequency of one or both PLL clocks. */ + +static void +arc_set_clock_frequency (char *args, int from_tty) +{ + MegaHertz MCLK_frequency = UNDEFINED_FREQUENCY; + MegaHertz VCLK_frequency = UNDEFINED_FREQUENCY; + int result; + char *value; + + if (args == NULL) + { + printf_filtered (_(ARC_SET_CLOCK_FREQUENCY_COMMAND_USAGE)); + return; + } + + result = name_value_pair(args, &value); + + if (result == 0) + { + printf_filtered (_(ARC_SET_CLOCK_FREQUENCY_COMMAND_USAGE)); + return; + } + + if (result == 1) + { + char *comma = strchr(args, ','); + + if (comma) + { + *comma = '\0'; + MCLK_frequency = strtod(args, NULL); + VCLK_frequency = strtod(comma + 1, NULL); + } + else + MCLK_frequency = strtod(args, NULL); + + } + else if (result == 2) + { + char *comma = strchr(value, ','); + + if (comma) + { + printf_filtered (_(ARC_SET_CLOCK_FREQUENCY_COMMAND_USAGE)); + return; + } + + if (strcasecmp(args, "mclk") == 0) + MCLK_frequency = strtod(value, NULL); + else if (strcasecmp(args, "vclk") == 0) + VCLK_frequency = strtod(value, NULL); + else + { + warning(_("invalid PLL clock '%s'"), args); + return; + } + } + + /* strtod returns 0 for an invalid argument - and 0 is not a valid clock + frequency anyway! */ + if (MCLK_frequency == 0.0 || VCLK_frequency == 0.0) + { + warning(_("invalid clock frequency")); + } + else + { + DEBUG(_("MCLK : %.2lf MHz.\n"), MCLK_frequency); + DEBUG(_("VCLK : %.2lf MHz.\n"), VCLK_frequency); + + /* Check that the JTAG interface (which opens the GPIO driver) is open. */ + arc_jtag_ops.check_open(); + + set_PLL_clocks(MCLK_frequency, VCLK_frequency); + check_PLL_clock_frequencies(); + print_clock_settings(FALSE, TRUE); + + /* Reset the JTAG Test Access Port Controller. */ + arc_jtag_ops.reset(); + } +} + + +/* Command: <command> gclk<N> = <source> + gclk = <source> + gclks = <source> , { <source> } + harvard + + Set the source of one or more global clocks. */ + +static void +arc_set_clock_source (char *args, int from_tty) +{ + Boolean invalid = FALSE; + + if (args) + { + int result; + char *value; + + /* Check that the JTAG interface (which opens the GPIO driver) is open. */ + arc_jtag_ops.check_open(); + + result = name_value_pair(args, &value); + + if (result == 1) + { + if (strcasecmp(args, "harvard") == 0) + harvard = TRUE; + else + invalid = TRUE; + } + else if (result == 2) + { + char *key = args; + + DEBUG("key = %s, value = %s\n", key, value); + + if (strncasecmp(key, "gclk", 4) == 0) + { + size_t keylength = strlen(key); + + if (keylength == 4) + set_global_clock(3, value); + else if (keylength == 5) + { + if (key[4] == 's' || key[4] == 'S') + { + GlobalClockId clockId = 0; + char *clockData = strtok(value, " ,"); + + do + { + if (clockId == NUM_GLOBAL_CLOCKS) + { + warning(_("too many clock sources specified")); + return; + } + + set_global_clock(clockId++, clockData); + clockData = strtok(NULL, " ,"); + } while (clockData != NULL); + } + else if ('0' <= key[4] && key[4] < '0' + (char) NUM_GLOBAL_CLOCKS) + { + DEBUG("gclkN found\n"); + set_global_clock((GlobalClockId) (key[4] - (char) '0'), value); + } + else + { + warning(_("'%c' is not a valid clock number"), key[4]); + return; + } + } + else + invalid = TRUE; + } + else + invalid = TRUE; + } + else + invalid = TRUE; + } + else + invalid = TRUE; + + if (invalid) + printf_filtered (_(ARC_SET_CLOCK_SOURCE_COMMAND_USAGE)); + else + program_clock_settings(_("Attempting to set clocks."), FALSE); +} + + +/* Command: <command> + + Show the current clock settings. */ + +static void +arc_print_clock_settings (char *args, int from_tty) +{ + if (args) + { + printf_filtered (_(ARC_CLOCK_SETTINGS_COMMAND_USAGE)); + return; + } + + /* Check that the JTAG interface (which opens the GPIO driver) is open. */ + arc_jtag_ops.check_open(); + + print_clock_settings(TRUE, FALSE); +} + + +/* Command: <command> + + Show the current target board FPGA status. */ + +static void +arc_check_FPGA_configuration (char *args, int from_tty) +{ + if (args) + { + printf_filtered (_(ARC_FPGA_COMMAND_USAGE)); + return; + } + + switch (arc_is_FPGA_configured()) + { + case INACCESSIBLE: + break; + case CONFIGURED: + printf_filtered(_("FPGA is configured.\n")); + break; + case UNCONFIGURED: + printf_filtered(_("FPGA is not configured.\n")); + break; + } +} + + +/* -------------------------------------------------------------------------- */ +/* externally visible functions */ +/* -------------------------------------------------------------------------- */ + +/* Blast the target board FPGA. */ + +void +arc_blast_board (char *args, int from_tty) +{ + (void) blast_board(args, from_tty); +} + + +/* Reset the target board. */ + +void +arc_reset_board (void) +{ + /* Toggle the SS1 line - this should do a soft reset. */ + + write_control_port(SS1 | SS0 | CNT, 0); + write_control_port( SS0 | CNT, 200); /* TBH 18 JUN 2003 delay needed by slower simulations. */ + write_control_port(SS1 | SS0 | CNT, 0); + + /* Reset the PLL clocks and the global clock sources - this should be done + by the soft reset, but that does not appear to happen! */ + reset_clock_configuration(); + + (void) set_PLL_clock_frequency(PLL_MCLK, MCLK_RESET_FREQUENCY, FALSE, FALSE); + (void) set_PLL_clock_frequency(PLL_VCLK, VCLK_RESET_FREQUENCY, FALSE, FALSE); + + configure_CPLD(); +} + + +/* Check whether the FPGA has been configured (i.e. blasted with an XBF). */ + +FPGA_Status +arc_is_FPGA_configured (void) +{ + FPGA_Status result; + + ENTERMSG; + + /* Try to open the JTAG interface (which opens the GPIO driver). */ + if (arc_jtag_ops.open(arc_aux_find_register_number("MEMSUBSYS", ARC_HW_MEMSUBSYS_REGNUM))) + { + /* Get the current state of the control register. */ + Byte origCTRL = gpio_read(CONTROL_PORT) ^ C_XOR; + Byte newCTRL; + Byte status; + + /* If SS0 is low, bring this high first (to protect against reset). */ + if (SS0 != (origCTRL & SS0)) + { + /* Output new control state. */ + newCTRL = (origCTRL | SS0); + write_control_port(newCTRL, 1); + } + + /* Ensure that SS0 is high, and SS1 and CNT are low. */ + newCTRL = (origCTRL | SS0) & 0xF5; // 11110101 + newCTRL = newCTRL | BI; + write_control_port(newCTRL, 1); + + /* Read the OP input. */ + status = read_status_port(); + + /* If SS1 was originally high then bring high now (to protect against reset). */ + if (SS1 == (origCTRL & SS1)) + { + /* Output new control state (Gray code transition). */ + newCTRL = (origCTRL | SS1); + write_control_port(newCTRL, 1); + } + + /* Restore the control register. */ + write_control_port(origCTRL, 1); + + /* Reset the JTAG Test Access Port Controller. */ + arc_jtag_ops.reset(); + + result = IS_SET(FPA_CFG_DONE, status) ? CONFIGURED : UNCONFIGURED; + } + else + result = INACCESSIBLE; + + LEAVEMSG; + return result; +} + + +/* Initialize the module. This function is called from the gdb core on start-up. */ + +void +_initialize_arc_board (void) +{ + struct cmd_list_element* c; + + /* Reset the configuration info to its default state. */ + reset_clock_configuration(); + + /* Add support for blasting an FPGA board (ARCangel). */ + c = add_cmd (ARC_BLAST_BOARD_COMMAND, + class_obscure, + arc_blast_board_FPGA, + _("Blast the ARC board FPGA.\n" + ARC_BLAST_BOARD_COMMAND_USAGE + "<FILE> is the filepath of an XBF (eXtended Binary Format) file.\n"), + &cmdlist); + set_cmd_completer (c, filename_completer); + + /* Add support for setting the CPU clock frequency. */ + (void) add_cmd (ARC_SET_CLOCK_FREQUENCY_COMMAND, + class_obscure, + arc_set_clock_frequency, + _("Set the PLL frequency on the ARC board.\n" + ARC_SET_CLOCK_FREQUENCY_COMMAND_USAGE + "<CLOCK> is 'mclk' or 'vclk'; if omitted, and only one frequency is given, it defaults to 'mclk'.\n" + "<FREQUENCY> is a number (interpreted as MegaHertz).\n"), + &cmdlist); + + /* Add support for setting the CPU clock sources. */ + (void) add_cmd (ARC_SET_CLOCK_SOURCE_COMMAND, + class_obscure, + arc_set_clock_source, + _("Set the clock sources on the ARC board.\n" + ARC_SET_CLOCK_SOURCE_COMMAND_USAGE + "N is in the range 0 .. 3; if omitted, it defaults to 3.\n" + "<SOURCE> is 'crystal', 'dips', 'highimp', 'host', 'mclk', 'vclk' or a number (interpreted as MegaHertz). \n"), + &cmdlist); + + /* Add support for showing the clock settings. */ + (void) add_cmd (ARC_CLOCK_SETTINGS_COMMAND, + class_info, + arc_print_clock_settings, + _("Show the clock settings on the ARC board.\n" + ARC_CLOCK_SETTINGS_COMMAND_USAGE), + &infolist); + + /* Add support for checking whether the FPGA board has been configured. */ + (void) add_cmd (ARC_FPGA_COMMAND, + class_info, + arc_check_FPGA_configuration, + _("Check ARC board FPGA configuration.\n" + ARC_FPGA_COMMAND_USAGE), + &infolist); +} + +/******************************************************************************/ |