summaryrefslogtreecommitdiffstats
path: root/zpu/hdl/tap/jtagx.vhd
blob: 89c2075caac11a3a91fb0c067288a118d039b528 (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
----------------------------------------------------------------------------
-- Simple JTAG controller, enhanced version
--
-- $Id: jtag.vhdl 35 2008-09-05 23:31:00Z strubi $
--
-- (c) 2005, 2006, 2011
-- Martin Strubel // <hackfin@section5.ch>
----------------------------------------------------------------------------

-- Functionality:
--
-- This module implements a JTAG controller with a instruction register (IR)
-- and a data register (DR).
-- Data is clocked into the IR register MSB first,
--                 into the DR register LSB first.
--
-- The reason for this inconsistent behaviour is, that this controller
-- allows variable sizes of data registers, depending on the IR value.
-- 
-- (Actually, the Blackfin CPU JTAG controller does it the same odd way)
--
-- The IR and DR register size is specified in the parameters:
--
-- IRSIZE (default 4)
-- DRSIZE (default 8)
--
-- All special functionality must be encoded outside this module, using
-- the IR values.
-- There is one exception: The Instruction "1111" is reserved for the 
-- IR_BYPASS mode. In this mode, the TDI bit is passed onto TDO with a delay
-- of one bit, according to the JTAG standard.
--
-- The design is tested using the JTAG library coming with the ICEbear
-- USB JTAG adapter.
--

library ieee;
use ieee.std_logic_1164.all;
use IEEE.std_logic_unsigned.all;
use IEEE.numeric_std.all; -- TO_INTEGER

library work;
use work.jtag.all;

entity JtagController is
	generic (IRSIZE : natural := 4;
	         DRSIZE : natural := 8);
	port (
		tck,                           -- Tap Clock
		trst,                          -- Tap Reset
		tms,                           -- Tap mode select
		tdi   : in std_logic;          -- Tap data in
		tdo   : out std_logic;         -- Tap data out
		state : out jtag_state_type;   -- JTAG machine state
-- Data register input:
		dr_in  : in std_logic_vector (DRSIZE-1 downto 0);
-- Configureable DR size:
		msbpos : in bitpos_type;
-- Data register output:
		dr_out : out std_logic_vector (DRSIZE-1 downto 0);
-- Instruction register:
		ir_out : out std_logic_vector (IRSIZE-1 downto 0)
	);
end JtagController;

architecture behaviour of JtagController is

-- The only fixed instruction: All ones. Reserved for bypassing.
	constant IR_BYPASS : std_logic_vector (IRSIZE-1 downto 0) :=
		(others => '1');

	signal mystate : jtag_state_type  := TEST_LOGIC_RESET;
	signal next_state : jtag_state_type;

	signal s_dr         : std_logic_vector (DRSIZE-1 downto 0);
	signal s_ir         : std_logic_vector (IRSIZE-1 downto 0) :=
		(others => '1');

	signal msb          : bitpos_type;

	-- Disabled: Buffered register
	-- signal ir           : std_logic_vector (IRSIZE-1 downto 0);

begin

nextstate_decode:
	process (mystate, tms)
	begin
		case mystate is
			when CAPTURE_DR =>
				if (tms = '1') then
					next_state <= EXIT1_DR;
				else
					next_state <= SHIFT_DR;
				end if;
			when CAPTURE_IR =>
				if (tms = '1') then
					next_state <= EXIT1_IR;
				else
					next_state <= SHIFT_IR;
				end if;
			when EXIT1_DR =>
				if (tms = '1') then
					next_state <= UPDATE_DR;
				else
					next_state <= PAUSE_DR;
				end if;
			when EXIT1_IR =>
				if (tms = '1') then
					next_state <= UPDATE_IR;
				else
					next_state <= PAUSE_IR;
				end if;
			when EXIT2_DR =>
				if (tms = '1') then
					next_state <= UPDATE_DR;
				else
					next_state <= SHIFT_DR;
				end if;
			when EXIT2_IR =>
				if (tms = '1') then
					next_state <= UPDATE_IR;
				else
					next_state <= SHIFT_IR;
				end if;
			when PAUSE_DR =>
				if (tms = '1') then
					next_state <= EXIT2_DR;
				else
					next_state <= PAUSE_DR;
				end if;
			when PAUSE_IR =>
				if (tms = '1') then
					next_state <= EXIT2_IR;
				else
					next_state <= PAUSE_IR;
				end if;
			when RUN_TEST_IDLE =>
				if (tms = '1') then
					next_state <= SELECT_DR;
				else
					next_state <= RUN_TEST_IDLE;
				end if;
			when SELECT_DR =>
				if (tms = '1') then
					next_state <= SELECT_IR;
				else
					next_state <= CAPTURE_DR;
				end if;
			when SELECT_IR =>
				if (tms = '1') then
					next_state <= TEST_LOGIC_RESET;
				else
					next_state <= CAPTURE_IR;
				end if;
			when SHIFT_DR =>
				if (tms = '1') then
					next_state <= EXIT1_DR;
				else
					next_state <= SHIFT_DR;
				end if;
			when SHIFT_IR =>
				if (tms = '1') then
					next_state <= EXIT1_IR;
				else
					next_state <= SHIFT_IR;
				end if;
			when TEST_LOGIC_RESET =>
				if (tms = '1') then
					next_state <= TEST_LOGIC_RESET;
				else
					next_state <= RUN_TEST_IDLE;
				end if;
			when UPDATE_DR =>
				if (tms = '1') then
					next_state <= SELECT_DR;
				else
					next_state <= RUN_TEST_IDLE;
				end if;
			when UPDATE_IR =>
				if (tms = '1') then
					next_state <= SELECT_DR;
				else
					next_state <= RUN_TEST_IDLE;
				end if;
			when others =>
		end case;
	end process;

-- When we're in BYPASS, use MSB 0
	msb <= 0 when s_ir = IR_BYPASS else msbpos;

tdo_encode:
	process (mystate, s_ir, s_dr)
	begin
		case mystate is
		when SHIFT_IR =>
			tdo <= s_ir(0);                  -- Shift out LSB
		when SHIFT_DR =>
			tdo <= s_dr(msb);                -- Take MSB
		when others =>
			tdo <= '1';
		end case;
	end process;

state_advance:
	process (tck, trst)
	begin
		if (trst = '0') then
			mystate <= TEST_LOGIC_RESET;
		elsif rising_edge(tck) then
			mystate <= next_state;  -- Advance to next state
		end if;
	end process;

process_ir_dr:
	process (tck)
	begin
		if rising_edge(tck) then
-- takes effect when entering the concerning state
			case next_state is 
			-- When resetting, go into BYPASS mode
			when TEST_LOGIC_RESET =>
				s_ir <= (others => '1');
				s_dr <= (others => '0');
			when others =>
			end case;

-- Mystate is the current state, process takes effect on rising TCK when IN
-- the concerning state.
			case mystate is
			when SHIFT_IR =>
				s_ir <= tdi & s_ir(IRSIZE-1 downto 1);  -- Shift in from MSB
			when SHIFT_DR =>
				s_dr <= s_dr(DRSIZE-2 downto 0) & tdi;  -- likewise from LSB 
			when CAPTURE_DR =>
-- We could move this BYPASS check to a higher level module. But since
-- it's a reserved command, we leave it in here.
				if (s_ir /= IR_BYPASS) then
					s_dr <= dr_in; -- Latch!
				end if;
			when others =>
			end case;	
		end if;
	end process;

	-- always assign state to output
	-- We assign nextstate which is valid on the rising_edge of tck
	state <= next_state;

	ir_out <= s_ir;
	dr_out <= s_dr;

end behaviour;
OpenPOWER on IntegriCloud