blob: 664222014b12888a392624746d502f48d3cdc72c [file] [log] [blame]
/* 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);
}
/******************************************************************************/