Jump to content
  • 0

Using Adept SDK to handle JTAG on ZC706 board: problems.


Pavel_47

Question

Hello,

Here is extract of JTAG sequence for reading status register of Zynq-7000 (picked from UG470 - 7-Series configuration guide):

ug470-status-register-readback-sequence.

Here is fragment of my code for executing:

  • 1st step (until Shift the first five bits ...)
  • the beginning of 2nd step (just Shift CFG_IN instruction)

Declaration part:

HIF hif;
char jtag_version[20];
BYTE move_Shift_IR[] = { 0xDF, 0x00 };
BYTE shft_CFG_IN[] = {0x11, 0x08, 0x00};

Execution part:

		private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {
			DjtgGetVersion(jtag_version);
			if (!DmgrOpen(&hif, "JtagSmt2")) {
				printf("Error: Could not open device. Check device name\n");
				ErrorExit();
			}
			if (!DjtgEnable(hif)) {
				printf("Error: DjtgEnable failed\n");
				ErrorExit();
			}

			// Put JTAG in Shift-IR state
			if (!DjtgPutTmsBits(hif, fTrue, move_Shift_IR, NULL, 10, fFalse))
			{
				printf("DjtgPutTmsBits failed\n");
				ErrorExit();
			}

			if (!DjtgPutTdiBits(hif, fTrue, shft_CFG_IN, NULL, 6, fFalse)) {
				printf("DjtgPutTdiBits failed\n");
				ErrorExit();
			}

I control execution of the code with LeCroy logic analyzer and when I look at waveforms I feel a little embarrassed: 1st step is visible while 2nd - not at all.

Le-Croy-register-readback-sequence-begin

Any comments ?

Thanks.

Pavel.

ug470_status_register_readback_sequence.jpg

Link to comment
Share on other sites

13 answers to this question

Recommended Posts

Hi Jon,

Thank you for feedback.

Have you looked through the PDF documents in the Adept SDK folder?

Sure, I did it ... and examined two examples dedicated to JTAG control.

Here the problem does not concern a particular development board (ZC706 or one else).

Xilinx development boards let's say medium range (probably also "high-end" boards) use Digilent JTAG-SMTx modules as principal programming interface (at least ZC706 (Zynq-7045) and KCU105 (Kintex Ultrascale) that I'm working with, use JTAG-SMTx).

So, the problem concerns rather manipulating of the JTAG-SMTx using Adept SDK.

As I showed in my previous mail, the 1st function call (DjtgPutTmsBits) produces activity on JTAG lines (TCK and TMS) while 2nd function call (DjtgPutTdiBits) has no any effect.

And yet my control sequence is done in full compliance with table 6-2 of the example taken from the Xilinx Series 7 configuration guide.

So, either the sequence from table 6-2 is erroneous, either I misinterpreted it with Adept SDK and 1st function call (DjtgPutTmsBits) didn't put JTAG TAP controller in Shift-IR state, the state where CFG_IN instruction is executed.

As you can state from waveforms in LeCroy Logic Studio software (I connected the probes of LeCroy logic analyzer directlly to the JTAG ports of  JTAG-SMTx) the DjtgPutTdiBits call (2nd function call) has no any effect ... even on TCK line I don't see any pulse.

Sincerely,

Pavel.

Link to comment
Share on other sites

Hi @Pavel_47,

I'm curious as to why you chose your implementation of the DjtgPutTdiBits that you kept the TMS pin at a high state (second parameter in the function) when the table 6.2 states you need to keep the TMS line in a low state and why you are attempting to shift in 6 bits when the beginning of the second step only requires 5 bits which seem to be 00101. Based on your declared shft_CFG_IN array, you also seem to be wanting to shift in 3 bytes worth of data rather than the total of 10 bits for the second step.

To be fair, I do not know why you do not see anything on the TCK line; I do not know if that is due to how JTAG expects things to be done or if it is because you have extra bits left over in the byte that are not being shifted in.

Based on the table you referenced and the DJTG Programmer's Reference Manual, I believe (I have not tested this for myself) you would want the following for the second step:

//following the coding style provided
//Declarations, though these don't need to be arrays
BYTE shft_CFG_IN_part1[] = {0x05}; //corresponds to the 00101
BYTE shft_CFG_IN_part2_and_part3[] = {0x00}; //single 0 and two don't cares
BYTE shft_CFG_IN_part4 = {0x00}; //two don't cares

//Executions
//first 5 bits, with TMS held low
if (!DjtgPutTdiBits(hif, fFalse, shft_CFG_IN, NULL, 5, fFalse)) {
				printf("DjtgPutTdiBits part1 failed\n");
				ErrorExit();
			}
//second part and third part, with TMS held high
if (!DjtgPutTdiBits(hif, fTrue, shft_CFG_IN, NULL, 3, fFalse)) {
				printf("DjtgPutTdiBits part2 and 3 failed\n");
				ErrorExit();
			}
//fourth part, with TMS held low
if (!DjtgPutTdiBits(hif, fFalse, shft_CFG_IN, NULL, 2, fFalse)) {
				printf("DjtgPutTdiBits part4 failed\n");
				ErrorExit();
			}

Thanks,
JColvin

Link to comment
Share on other sites

On 12/4/2018 at 4:38 PM, Pavel_47 said:

Any comments ?
 

Hi,

just had a thought on this: Is it possible that the 2nd command is simply off-screen?

The clock is 10 MHz so this is clearly generated on the board on a 100 ns grid. But I'd expect that different commands in the SW will go each in its own USB (micro)frame, putting them on a 125000 ns grid or slower.

The reference manual mentions "selectable clock frequency" - I'd simply try to run this at 8 kHz instead of 10 MHz.

Link to comment
Share on other sites

6 hours ago, xc6lx45 said:

Hi,

just had a thought on this: Is it possible that the 2nd command is simply off-screen?

The clock is 10 MHz so this is clearly generated on the board on a 100 ns grid. But I'd expect that different commands in the SW will go each in its own USB (micro)frame, putting them on a 125000 ns grid or slower.

The reference manual mentions "selectable clock frequency" - I'd simply try to run this at 8 kHz instead of 10 MHz.

Yes, it was exactly the case !

The time laps between 2 consequent function calls are so long (why ?) that the 2nd call occurs outside of the time scope, covered by LeCroy software.

I've modified a little my code in order to follow step-by-step the JTAG sequence:

jtag-exploring-visual-studio-form.jpg

So, executing each instruction separately, I can control what is going on at JTAG lines.

Link to comment
Share on other sites

10 hours ago, JColvin said:

Hi @Pavel_47,

I'm curious as to why you chose your implementation of the DjtgPutTdiBits that you kept the TMS pin at a high state (second parameter in the function) when the table 6.2 states you need to keep the TMS line in a low state and why you are attempting to shift in 6 bits when the beginning of the second step only requires 5 bits which seem to be 00101. Based on your declared shft_CFG_IN array, you also seem to be wanting to shift in 3 bytes worth of data rather than the total of 10 bits for the second step.

To be fair, I do not know why you do not see anything on the TCK line; I do not know if that is due to how JTAG expects things to be done or if it is because you have extra bits left over in the byte that are not being shifted in.

Based on the table you referenced and the DJTG Programmer's Reference Manual, I believe (I have not tested this for myself) you would want the following for the second step:

Thanks,
JColvin

Thanks JColvin,

Yes, you were right - I was wrong trying to pass CFG_IN instruction that way. Since my code has undergone many changes. Here how it looks now:

Declaration part:

HIF hif;
char jtag_version[20];
BYTE reset_JTAG_tms[] = { 0x1F };
BYTE move_Shift_IR_tms[] = { 0x0C };
BYTE shift_IR_tmstdi[] = { 0x11, 0x08 };
BYTE move_Shift_DR_tms[] = { 0x03 };
BYTE shift_Packets[] = { 0x55, 0x99, 0xAA, 0x66, 0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x07, 0x80, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00 };
BYTE shift_Packets_last[] = { 0x00, 0x80 };
BYTE move_shift_IR1[] = { 0x07 };
BYTE shift_IR1[] = { 0x10, 0x08 };
BYTE shift_zeros[] = { 0x00, 0x00, 0x00, 0x00 };
BYTE recv_buff[4];

Execution part:

	private: System::Void btn_Reset_Click(System::Object^  sender, System::EventArgs^  e) {
		DjtgGetVersion(jtag_version);
		if (!DmgrOpen(&hif, "JtagSmt2")) {
			lbl_error->Text = "Error: Could not open device. Check device name";
		}

		if (!DjtgEnable(hif)) {
			lbl_error->Text = "Error: DjtgEnable failed";
		}

		// Put JTAG in TLR state
		if (!DjtgPutTmsBits(hif, fFalse, reset_JTAG_tms, NULL, 5, fFalse))
		{
			lbl_error->Text = "JTAG reset failed";
		}
	}

	private: System::Void btn_Clear_Click(System::Object^  sender, System::EventArgs^  e) {
		lbl_error->Text = "";
	}
	private: System::Void btn_GetPortProperties_Click(System::Object^  sender, System::EventArgs^  e) {
		DWORD properties = 0;
		if (!DjtgGetPortProperties(hif, 0, &properties)) {
			lbl_error->Text = "ERROR: failed to get JTAG port 0 properties, erc = " + DmgrGetLastError();
		}
		lbl_GetPortProperties->Text = properties.ToString();
	}

	private: System::Void btn_MoveShiftIR_Click(System::Object^  sender, System::EventArgs^  e) {
		if (!DjtgPutTmsBits(hif, fFalse, move_Shift_IR_tms, NULL, 5, fFalse))
		{
			lbl_error->Text = "moving to Shift-IR failed";
		}
	}
	
	private: System::Void btn_ShiftIR_Click(System::Object^  sender, System::EventArgs^  e) {
		if (!DjtgPutTmsTdiBits(hif, shift_IR_tmstdi, NULL, 6, fFalse)) {
			lbl_error->Text = "Shifing IR failed";
		}
	}

	private: System::Void btn_MoveShiftDR_Click(System::Object^  sender, System::EventArgs^  e) {
		if (!DjtgPutTmsBits(hif, fFalse, move_Shift_DR_tms, NULL, 4, fFalse))
		{
			lbl_error->Text = "moving to Shift-DR failed";
		}
	}

	private: System::Void btn_ShiftDR_click(System::Object^  sender, System::EventArgs^  e) {
		if (!DjtgPutTdiBits(hif, fFalse, shift_Packets, NULL, 152, fFalse))
		{
			lbl_error->Text = "Shifting DR failed";
		}
	}

	private: System::Void btn_ShiftDR_last_Click(System::Object^  sender, System::EventArgs^  e) {
		if (!DjtgPutTmsTdiBits(hif, shift_Packets_last, NULL, 8, fFalse)) {
			lbl_error->Text = "Shifing DR last failed";
		}
	}

	private: System::Void btn_ShiftIR1_Click(System::Object^  sender, System::EventArgs^  e) {
		//move_shift_IR1
		if (!DjtgPutTmsBits(hif, fFalse, move_shift_IR1, NULL, 5, fFalse))
		{
			lbl_error->Text = "moving to Shift-IR failed";
		}
	}

	private: System::Void btn_ShiftIR1_Click_1(System::Object^  sender, System::EventArgs^  e) {
		if (!DjtgPutTmsTdiBits(hif, shift_IR1, NULL, 6, fFalse)) {
			lbl_error->Text = "Shifing IR failed";
		}
	}

	private: System::Void btn_GetStatReg_Click(System::Object^  sender, System::EventArgs^  e) {
		if (!DjtgPutTdiBits(hif, fFalse, shift_zeros, recv_buff, 32, fFalse))
		{
			lbl_error->Text = "Shifting DR failed";
		}
		else {
			lbl_error->Text = recv_buff[3].ToString() + recv_buff[2].ToString() + recv_buff[1].ToString() + recv_buff[0].ToString();
			lbl_Byte0_val->Text = recv_buff[0].ToString();
			lbl_Byte1_val->Text = recv_buff[1].ToString();
			lbl_Byte2_val->Text = recv_buff[2].ToString();
			lbl_Byte3_val->Text = recv_buff[3].ToString();
		}
	}

As you can state I used DjtgPutTmsTdiBits function to pass CFG_IN instruction (because it affects both TMS and TDI)

I can control executing of the each instruction separately using LeCroy software.

According to my observation the ensemble of these sequences corresponds to the "golden sequence" that I picked form executing fpga -config-status instruction in Xilinx SDK that is supposed to do the same thing. Despite the fact that both sequences (picked from execution of my code and that, picked from execution of fpga -config-status), the result is not the same: I display the content of the FPGA status register in lbl_Byte0_val ... lbl_Byte3_val windows form controls ... and can state that this value (unfortunately) is quite different from that, obtained from fpga -config-status.

Sincerely,

Pavel.

Link to comment
Share on other sites

Hi,

another thing just came to mind: You're dealing with a Zynq, there are two devices in the JTAG chain (the ARM and the FPGA).

What I mean is, send 0xFFFFFFFF... to IR (repetitions of standardized BYPASS opcode 6'b111111), then send 0x01 to DR.

For a single device the response is 0x02 (one device => one register delayed). For Zynq it's 0x04 (two devices).

Link to comment
Share on other sites

On 12/7/2018 at 4:05 PM, xc6lx45 said:

Hi,

another thing just came to mind: You're dealing with a Zynq, there are two devices in the JTAG chain (the ARM and the FPGA).

What I mean is, send 0xFFFFFFFF... to IR (repetitions of standardized BYPASS opcode 6'b111111), then send 0x01 to DR.

For a single device the response is 0x02 (one device => one register delayed). For Zynq it's 0x04 (two devices).

Hi,

I'm afraid I didn't properly understand what you mean.

If I push BYPASS code into IR, how it will help me to resolve the problem ? In the "golden sequence" (i.e. fpga -config-status) I don't see.

One more observation: the execution of dadutil enum -t produces the following:

No devices found.

So, if my comprehension is correct, digilent utility dadutil doesn't see Xilinx chips?

I've tried it on two boards: XC706 (Zynq-7045) and KCU105 (Kintex Ultrascale) and the result is the same.

BR.

Link to comment
Share on other sites

Hi,

I think there are two devices in the Zynq JTAG chain: First the ARM processor, then the 7-series FPGA. The easiest way to see this is in Xilinx hardware manager: Just plug in the Zynq board, "open hardware" (near "generate bitstream") and "auto connect". It'll show the JTAG chain with ARM at index 0 and FPGA at index 1.

The ARM processor has an instruction register length of 4 so I believe we need to send 10 bits into the combined IR to get IDCODE working (not 6, as for a standalone Xilinx FPGA).

I mentioned BYPASS because

  • it is an easy way to determine the number of JTAG devices on a chain (a semi-standardized hack: send a long 1 bit chain into IR, then a single 1 bit into DR, flush with 0s and count how long it remains in transit)
  • you'll probably have to put the ARM processor into BYPASS to do anything useful via JTAG with the FPGA.

My bet is that most developers would first bring up the BYPASS opcode and not move on, until it works as expected.

Link to comment
Share on other sites

Hi,

Yes!

Probably have a reason. After a closer look to the "golden waveforms" I see that it doesn't correspond to 100% to the sequence,described in the table 6-2 of UG470.

Here is the beginning of the "golden sequence" picked with LeCroy:

Le-Croy-register-readback-sequence-begin

I've divided this beginning part in 6 phases:

  • phase 0: don't understand what is it for
  • phase 1: put TAP controller in TLR state (because with TMS = 1 five consecutive pulses are sufficient to return to TLR from whatever state)
  • phase 2: put TAP controller in Shift-IR state
  • phase 3: shift CFG_IN (000101) instruction into IR register
  • phase 4: ... and it's here that it becomes interesting: being in Shift-IR state (because TMS = 0), TAP continue to inject'1' into IR register ... so probably to push 000101 further in the JTAG chain ? And 4 supplementary bits correspond to 4 bits of IR of ARM (BTW where did you learn that ... i.e 4 bits for ARM ?)
  • phase 5: given that the phase 4 ends up in Exit1-IR state (TMS = 1 on last pulse) TAP controller goes (through Update-IR, RTI, Select-DR, Capture-DR) to the Shift-DR state.
  • phase 6: being in the state, te TAP controller starts to inject configuration packets into DR register starting from synchronization word 0xAA995566.

So if this analysis is correct I just need to insert addition shift of 4 bits into IR register ?

Do you have any idea about result of execution of dadutil command ?

Thanks.

P.S. Here is JTAG controller diagram:

JTAG-architecture.jpg

Link to comment
Share on other sites

>> phase 4: ... and it's here that it becomes interesting: being in Shift-IR state (because TMS = 0), TAP continue to inject'1' into IR register ... so probably to push 000101 further in the JTAG chain ? And 4 supplementary bits correspond to 4 bits of IR of ARM (BTW where did you learn that ... i.e 4 bits for ARM ?)

yes, exactly. Could be ARM ABORT, see https://www.xilinx.com/support/documentation/user_guides/ug585-Zynq-7000-TRM.pdf page 724

Link to comment
Share on other sites

I did a quick experiment: My Artix-based bitstream uploader works just fine with a Zynq if I pad every 6-bit IR word with another 4 zero bits. "0000" for the arm is invalid, which is equivalent to BYPASS (doesn't matter for the uploader since it's not trying to read).

Note, I have one reply above awaiting moderation (for unknown reasons, it's strictly technical).

 

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...