aboutsummaryrefslogtreecommitdiff
path: root/src/target/openrisc/or1k_tap_vjtag.c
blob: 513d8a74cf273ab7d1f9c49897815fc63c704af1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
/***************************************************************************
 *   Copyright (C) 2013 by Franck Jullien                                  *
 *   elec4fun@gmail.com                                                    *
 *                                                                         *
 *   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 2 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, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "or1k_tap.h"
#include "or1k.h"

#include <jtag/jtag.h>

/* Contains constants relevant to the Altera Virtual JTAG
 * device, which are not included in the BSDL.
 * As of this writing, these are constant across every
 * device which supports virtual JTAG.
 */

/* These are commands for the FPGA's IR. */
#define ALTERA_CYCLONE_CMD_USER1	0x0E
#define ALTERA_CYCLONE_CMD_USER0	0x0C

/* These defines are for the virtual IR (not the FPGA's)
 * The virtual TAP was defined in hardware to match the OpenCores native
 * TAP in both IR size and DEBUG command.
 */
#define ALT_VJTAG_IR_SIZE		4
#define ALT_VJTAG_CMD_DEBUG		0x8

/* SLD node ID. */
#define JTAG_TO_AVALON_NODE_ID		0x84
#define VJTAG_NODE_ID			0x08
#define SIGNAL_TAP_NODE_ID		0x00
#define SERIAL_FLASH_LOADER_NODE_ID	0x04

#define VER(x)				((x >> 27) & 0x1f)
#define NB_NODES(x)			((x >> 19) & 0xff)
#define ID(x)				((x >> 19) & 0xff)
#define MANUF(x)			((x >> 8)  & 0x7ff)
#define M_WIDTH(x)			((x >> 0)  & 0xff)
#define INST_ID(x)			((x >> 0)  & 0xff)

/* tap instructions - Mohor JTAG TAP */
#define OR1K_TAP_INST_IDCODE 0x2
#define OR1K_TAP_INST_DEBUG 0x8

static char *id_to_string(unsigned char id)
{
	switch (id) {
	case VJTAG_NODE_ID:
		return "Virtual JTAG";
	case JTAG_TO_AVALON_NODE_ID:
		return "JTAG to avalon bridge";
	case SIGNAL_TAP_NODE_ID:
		return "Signal TAP";
	case SERIAL_FLASH_LOADER_NODE_ID:
		return "Serial Flash Loader";
	}
	return "unknown";
}

static unsigned char guess_addr_width(unsigned char number_of_nodes)
{
	unsigned char width = 0;

	while (number_of_nodes) {
		number_of_nodes >>= 1;
		width++;
	}

	return width;
}

static int or1k_tap_vjtag_init(struct or1k_jtag *jtag_info)
{
	LOG_DEBUG("Initialising Altera Virtual JTAG TAP");

	/* Put TAP into state where it can talk to the debug interface
	 * by shifting in correct value to IR.
	 */

	/* Ensure TAP is reset - maybe not necessary*/
	jtag_add_tlr();

	/* You can use a custom JTAG controller to discover transactions
	 * necessary to enumerate all Virtual JTAG megafunction instances
	 * from your design atruntime. All SLD nodes and the virtual JTAG
	 * registers that they contain are targeted by two Instruction Register
	 * values, USER0 and USER1.
	 *
	 * The USER1 instruction targets the virtual IR of either the sld_hub
	 * or a SLD node. That is,when the USER1 instruction is issued to
	 * the device, the subsequent DR scans target a specific virtual
	 * IR chain based on an address field contained within the DR scan.
	 * The table below shows how the virtual IR, the DR target of the
	 * USER1 instruction is interpreted.
	 *
	 * The VIR_VALUE in the table below is the virtual IR value for the
	 * target SLD node. The width of this field is m bits in length,
	 * where m is the length of the largest VIR for all of the SLD nodes
	 * in the design. All SLD nodes with VIR lengths of fewer than m
	 * bits must pad VIR_VALUE with zeros up to a length of m.
	 *
	 * -------------------------------+-------------------------------
	 * m + n - 1                   m  |  m -1                       0
	 * -------------------------------+-------------------------------
	 *     ADDR [(n – 1)..0]          |     VIR_VALUE [(m – 1)..0]
	 * -------------------------------+-------------------------------
	 *
	 * The ADDR bits act as address values to signal the active SLD node
	 * that the virtual IR shift targets. ADDR is n bits in length, where
	 * n bits must be long enough to encode all SLD nodes within the design,
	 * as shown below.
	 *
	 * n = CEIL(log2(Number of SLD_nodes +1))
	 *
	 * The SLD hub is always 0 in the address map.
	 *
	 * Discovery and enumeration of the SLD instances within a design
	 * requires interrogation of the sld_hub to determine the dimensions
	 * of the USER1 DR (m and n) and associating each SLD instance, specifically
	 * the Virtual JTAG megafunction instances, with an address value
	 * contained within the ADDR bits of the USER1 DR.
	 *
	 * The SLD hub contains the HUB IP Configuration Register and SLD_NODE_INFO
	 * register for each SLD node in the design. The HUB IP configuration register provides
	 * information needed to determine the dimensions of the USER1 DR chain. The
	 * SLD_NODE_INFO register is used to determine the address mapping for Virtual
	 * JTAG instance in your design. This register set is shifted out by issuing the
	 * HUB_INFO instruction. Both the ADDR bits for the SLD hub and the HUB_INFO
	 * instruction is 0 × 0.
	 * Because m and n are unknown at this point, the DR register
	 * (ADDR bits + VIR_VALUE) must be filled with zeros. Shifting a sequence of 64 zeroes
	 * into the USER1 DR is sufficient to cover the most conservative case for m and n.
	 */

	uint8_t t[4];
	struct scan_field field;
	struct jtag_tap *tap = jtag_info->tap;

	/* Select VIR */
	buf_set_u32(t, 0, tap->ir_length, ALTERA_CYCLONE_CMD_USER1);
	field.num_bits = tap->ir_length;
	field.out_value = t;
	field.in_value = NULL;
	jtag_add_ir_scan(tap, &field, TAP_IDLE);

	/* Select the SLD Hub */
	field.num_bits = 64;
	field.out_value = NULL;
	field.in_value = NULL;
	jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);

	/* HUB IP Configuration Register
	 *
	 * When the USER1 and HUB_INFO instruction sequence is issued, the
	 * USER0 instruction must be applied to enable the target register
	 * of the HUB_INFO instruction. The HUB IP configuration register
	 * is shifted out using eight four-bit nibble scans of the DR register.
	 * Each four-bit scan must pass through the UPDATE_DR state before
	 * the next four-bit scan. The 8 scans are assembled into a 32-bit
	 * value with the definitions shown in the table below.
	 *
	 * --------------------------------------------------------------------------------
	 *  NIBBLE7 | NIBBLE6 | NIBBLE5 | NIBBLE4 | NIBBLE3 | NIBBLE2 | NIBBLE1 | NIBBLE0
	 * ----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+-----
	 *     |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
	 * ----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+-----
	 * HUB IP version|         N         | ALTERA_MFG_ID (0x06E)  |     SUM (m, n)
	 * --------------+-------------------+------------------------+--------------------
	 */

	/* Select VDR */
	buf_set_u32(t, 0, tap->ir_length, ALTERA_CYCLONE_CMD_USER0);
	field.num_bits = tap->ir_length;
	field.out_value = t;
	field.in_value = NULL;
	jtag_add_ir_scan(tap, &field, TAP_IDLE);

	int retval = jtag_execute_queue();
	if (retval != ERROR_OK)
		return retval;

	uint8_t nibble;
	uint32_t hub_info = 0;

	for (int i = 0; i < 8; i++) {
		field.num_bits = 4;
		field.out_value = NULL;
		field.in_value = &nibble;
		jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
		retval = jtag_execute_queue();
		if (retval != ERROR_OK)
			return retval;
		hub_info = ((hub_info >> 4) | ((nibble & 0xf) << 28));
	}

	int nb_nodes = NB_NODES(hub_info);
	int m_width = M_WIDTH(hub_info);

	LOG_DEBUG("SLD HUB Configuration register");
	LOG_DEBUG("------------------------------");
	LOG_DEBUG("m_width         = %d", m_width);
	LOG_DEBUG("manufacturer_id = 0x%02" PRIx32, MANUF(hub_info));
	LOG_DEBUG("nb_of_node      = %d", nb_nodes);
	LOG_DEBUG("version         = %" PRId32, VER(hub_info));
	LOG_DEBUG("VIR length      = %d", guess_addr_width(nb_nodes) + m_width);

	/* Because the number of SLD nodes is now known, the Nodes on the hub can be
	 * enumerated by repeating the 8 four-bit nibble scans, once for each Node,
	 * to yield the SLD_NODE_INFO register of each Node. The DR nibble shifts
	 * are a continuation of the HUB_INFO DR shift used to shift out the Hub IP
	 * Configuration register.
	 *
	 * The order of the Nodes as they are shifted out determines the ADDR
	 * values for the Nodes, beginning with, for the first Node SLD_NODE_INFO
	 * shifted out, up to and including, for the last node on the hub. The
	 * tables below show the SLD_NODE_INFO register and a their functional descriptions.
	 *
	 *  --------------+-----------+---------------+----------------
	 *   31        27 | 26     19 | 18          8 | 7            0
	 *  --------------+-----------+---------------+----------------
	 *   Node Version |  NODE ID  |  NODE MFG_ID  |  NODE INST ID
	 *
	 */

	int vjtag_node_address = -1;
	int node_index;
	uint32_t node_info = 0;
	for (node_index = 0; node_index < nb_nodes; node_index++) {

		for (int i = 0; i < 8; i++) {
			field.num_bits = 4;
			field.out_value = NULL;
			field.in_value = &nibble;
			jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
			retval = jtag_execute_queue();
			if (retval != ERROR_OK)
				return retval;
			node_info = ((node_info >> 4) | ((nibble & 0xf) << 28));
		}

		LOG_DEBUG("Node info register");
		LOG_DEBUG("--------------------");
		LOG_DEBUG("instance_id     = %" PRId32, ID(node_info));
		LOG_DEBUG("manufacturer_id = 0x%02" PRIx32, MANUF(node_info));
		LOG_DEBUG("node_id         = %" PRId32 " (%s)", ID(node_info),
						       id_to_string(ID(node_info)));
		LOG_DEBUG("version         = %" PRId32, VER(node_info));

		if (ID(node_info) == VJTAG_NODE_ID)
			vjtag_node_address = node_index + 1;
	}

	if (vjtag_node_address < 0) {
		LOG_ERROR("No VJTAG TAP instance found !");
		return ERROR_FAIL;
	}

	/* Select VIR */
	buf_set_u32(t, 0, tap->ir_length, ALTERA_CYCLONE_CMD_USER1);
	field.num_bits = tap->ir_length;
	field.out_value = t;
	field.in_value = NULL;
	jtag_add_ir_scan(tap, &field, TAP_IDLE);

	/* Send the DEBUG command to the VJTAG IR */
	int dr_length = guess_addr_width(nb_nodes) + m_width;
	buf_set_u32(t, 0, dr_length, (vjtag_node_address << m_width) | ALT_VJTAG_CMD_DEBUG);
	field.num_bits = dr_length;
	field.out_value = t;
	field.in_value = NULL;
	jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);

	/* Select the VJTAG DR */
	buf_set_u32(t, 0, tap->ir_length, ALTERA_CYCLONE_CMD_USER0);
	field.num_bits = tap->ir_length;
	field.out_value = t;
	field.in_value = NULL;
	jtag_add_ir_scan(tap, &field, TAP_IDLE);

	return jtag_execute_queue();
}

static struct or1k_tap_ip vjtag_tap = {
	.name = "vjtag",
	.init = or1k_tap_vjtag_init,
};

int or1k_tap_vjtag_register(void)
{
	list_add_tail(&vjtag_tap.list, &tap_list);
	return 0;
}