summaryrefslogtreecommitdiff
path: root/gdb/arc-board.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/arc-board.c')
-rw-r--r--gdb/arc-board.c1996
1 files changed, 1996 insertions, 0 deletions
diff --git a/gdb/arc-board.c b/gdb/arc-board.c
new file mode 100644
index 00000000000..664222014b1
--- /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);
+}
+
+/******************************************************************************/