add new urjtag-based code, fjmem
[fleet.git] / src / edu / berkeley / fleet / fpga / fjmem / fjmem_core.vhd
1 -------------------------------------------------------------------------------
2 --
3 -- $Id: fjmem_core.vhd 1071 2008-02-21 20:34:04Z arniml $
4 --
5 -- jmem_core - a generic interface module for accessing on-chip and off-chip
6 --             memory and peripherals
7 --
8 -- For host software support visit
9 --   http://urjtag.org/
10 --
11 -- This program is free software; you can redistribute it and/or
12 -- modify it under the terms of the GNU General Public License
13 -- as published by the Free Software Foundation; either version 2
14 -- of the License, or (at your option) any later version.
15 --
16 -- This program is distributed in the hope that it will be useful,
17 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
18 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 -- GNU General Public License for more details.
20 --
21 -- You should have received a copy of the GNU General Public License
22 -- along with this program; if not, write to the Free Software
23 -- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
24 -- 02111-1307, USA.
25 --
26 -- Written by Arnim Laeuger <arniml@users.sourceforge.net>, 2008.
27 --
28 -------------------------------------------------------------------------------
29
30 library ieee;
31 use ieee.std_logic_1164.all;
32
33 use work.fjmem_config_pack.all;
34 use work.fjmem_pack.all;
35
36 entity fjmem_core is
37
38   port (
39     -- JTAG Interface ---------------------------------------------------------
40     clkdr_i  : in  std_logic;
41     trst_i   : in  std_logic;
42     shift_i  : in  std_logic;
43     update_i : in  std_logic;
44     tdi_i    : in  std_logic;
45     tdo_o    : out std_logic;
46     -- Memory Interface -------------------------------------------------------
47     clk_i    : in  std_logic;
48     res_i    : in  std_logic;
49     strobe_o : out std_logic;
50     read_o   : out std_logic;
51     write_o  : out std_logic;
52     ack_i    : in  std_logic;
53     cs_o     : out std_logic_vector(num_blocks_c-1 downto 0);
54     addr_o   : out std_logic_vector(max_addr_width_c-1 downto 0);
55     din_i    : in  std_logic_vector(max_data_width_c-1 downto 0);
56     dout_o   : out std_logic_vector(max_data_width_c-1 downto 0)
57   );
58
59 end fjmem_core;
60
61
62 library ieee;
63 use ieee.numeric_std.all;
64
65 architecture rtl of fjmem_core is
66
67   signal trst_s       : boolean;
68   signal shift_en_s,
69          capture_en_s : boolean;
70
71   signal shift_q  : std_logic_vector(shift_range_t);
72   signal update_q : std_logic_vector(shift_range_t);
73
74   signal res_s : boolean;
75
76   signal din_q : std_logic_vector(data_range_t);
77   signal ack_q,
78          ack_for_shift_q : std_logic;
79
80   signal instr_q : std_logic_vector(instr_range_t);
81   signal block_q : std_logic_vector(block_range_t);
82   signal strobe_toggle_q : std_logic;
83   signal addr_q  : std_logic_vector(addr_range_t);
84   signal dout_q  : std_logic_vector(data_range_t);
85
86   signal strobe_sync_q : std_logic_vector(1 downto 0);
87   signal strobe_edge_q : std_logic;
88
89 begin
90
91   -----------------------------------------------------------------------------
92   -- Mapping of input signals to internal flags
93   -----------------------------------------------------------------------------
94   trst_s       <= trst_i = trst_act_level_c;
95   shift_en_s   <= shift_i = shift_act_level_c;
96   capture_en_s <= shift_i /= shift_act_level_c;
97
98   res_s <= res_i = res_act_level_c;
99
100
101   -----------------------------------------------------------------------------
102   -- Process shift
103   --
104   -- Purpose:
105   --   Implements the shift register between tdi_i and tdo_o.
106   --
107   --   Instruction are handled as follows.
108   --   read   :
109   --   write  :
110   --   detect : a dedicated pattern is captured that allows that marks the
111   --            variable length fields:
112   --              * block field marked with '1'
113   --              * address field marked with '0'
114   --              * data field marked with '1'
115   --            This allows the host software to detect how these fields are
116   --            located inside the bit stream (total length of bitstream has
117   --            been determined previously).
118   --   query  : Based on the shifted block number, the used bits in the
119   --            address and data field are marked with '1'. This reports the
120   --            specific addr and data widths of the specified block.
121   --
122   shift: process (trst_s, clkdr_i)
123     variable addr_width_v,
124              data_width_v  : natural;
125     variable idx_v         : natural;
126   begin
127     if trst_s then
128       shift_q <= (others => '0');
129
130     elsif rising_edge(clkdr_i) then
131       if shift_en_s then
132         -- shift mode
133         shift_q(shift_width_c-2 downto 0) <= shift_q(shift_width_c-1 downto 1);
134         shift_q(shift_width_c-1) <= tdi_i;
135
136       else
137         -- capture mode
138         idx_v := to_integer(unsigned(shift_q(block_range_t)));
139         if idx_v < num_blocks_c then
140           addr_width_v := blocks_c(idx_v).addr_width;
141           data_width_v := blocks_c(idx_v).data_width;
142         else
143           addr_width_v := 0;
144           data_width_v := 0;
145         end if;
146
147         case instr_q is
148           when instr_read_c =>
149             shift_q(instr_range_t)   <= instr_q;
150             shift_q(shift_ack_pos_c) <= ack_for_shift_q;
151             shift_q(data_range_t)    <= din_q;
152
153           when instr_write_c =>
154             shift_q(instr_range_t) <= instr_q;
155
156           when instr_idle_c =>
157             shift_q <= (others => '0');
158
159           when instr_detect_c =>
160             shift_q                <= (others => '0');
161             shift_q(instr_range_t) <= instr_q;
162             -- mark block field with '1'
163             shift_q(block_range_t) <= (others => '1');
164             -- mark address field with '0'
165             shift_q(addr_range_t)  <= (others => '0');
166             -- mark data field with '1'
167             shift_q(data_range_t)  <= (others => '1');
168
169           when instr_query_c =>
170             if idx_v < num_blocks_c then
171               shift_q <= (others => '0');
172               -- mark used address bits of this block in the address field with '1'
173               for idx in addr_range_t loop
174                 if idx < shift_addr_pos_c + addr_width_v then
175                   shift_q(idx) <= '1';
176                 end if;
177               end loop;
178               -- mark used data bits of this block in the data field '1'
179               for idx in data_range_t loop
180                 if idx < shift_data_pos_c + data_width_v then
181                   shift_q(idx) <= '1';
182                 end if;
183               end loop;
184             else
185               -- unused block
186               shift_q <= (others => '0');
187             end if;
188             shift_q(instr_range_t) <= instr_q;
189
190           when others =>
191             shift_q <= (others => '-');
192             shift_q(instr_range_t) <= instr_q;
193         end case;
194
195       end if;
196     end if;
197   end process shift;
198   --
199   -----------------------------------------------------------------------------
200
201
202   -----------------------------------------------------------------------------
203   -- Process din
204   --
205   -- Purpose:
206   --   Stores the provided data at din_i for later capture.
207   --   The ack_i input is stored in a two-stage pipeline to allow din_q to
208   --   settle before ack is actually detected in the clkdr clock domain.
209   --
210   din: process (res_s, clk_i)
211   begin
212     if res_s then
213       din_q           <= (others => '0');
214       ack_q           <= '0';
215       ack_for_shift_q <= '0';
216
217     elsif rising_edge(clk_i) then
218       if ack_i = '1' then
219         din_q <= din_i;
220         ack_q <= '1';
221       end if;
222       ack_for_shift_q <= ack_q;
223
224       if ack_for_shift_q = '1' then
225         -- reset for the moment, functionality not yet complete
226         ack_q <= '0';
227       end if;
228
229     end if;
230   end process din;
231   --
232   -----------------------------------------------------------------------------
233
234
235   -----------------------------------------------------------------------------
236   -- Process dout
237   --
238   -- Purpose:
239   --   Stores the updated block, instruction, address and data fields.
240   --
241   dout: process (trst_s, update_i)
242   begin
243     if trst_s then
244       instr_q <= instr_idle_c;
245       block_q <= (others => '0');
246       addr_q  <= (others => '0');
247       dout_q  <= (others => '0');
248       strobe_toggle_q <= '0';
249
250     elsif rising_edge(update_i) then
251       instr_q <= shift_q(instr_range_t);
252       block_q <= shift_q(block_range_t);
253       addr_q  <= shift_q(addr_range_t);
254       dout_q  <= shift_q(data_range_t);
255
256       strobe_toggle_q <= not strobe_toggle_q;
257
258     end if;
259   end process dout;
260   --
261   -----------------------------------------------------------------------------
262
263
264   -----------------------------------------------------------------------------
265   -- Process strobe_sync
266   --
267   -- Purpose:
268   --   Implements the synchronizer for the strobe signal from clkdr_i to
269   --   clk_i domain. This is a toggle synchronizer.
270   --
271   strobe_sync: process (res_s, clk_i)
272   begin
273     if res_s then
274       strobe_sync_q <= (others => '0');
275       strobe_edge_q <= '0';
276
277     elsif rising_edge(clk_i) then
278       strobe_sync_q(1) <= strobe_toggle_q;
279       strobe_sync_q(0) <= strobe_sync_q(1);
280
281       strobe_edge_q    <= strobe_sync_q(0);
282     end if;
283   end process strobe_sync;
284   --
285   -----------------------------------------------------------------------------
286
287
288   -----------------------------------------------------------------------------
289   -- Process cs_gen
290   --
291   -- Purpose:
292   --   Generates the cs_o output vector.
293   --
294   cs_gen: process (block_q)
295   begin
296     for idx in 0 to num_blocks_c-1 loop
297       if idx = to_integer(unsigned(block_q)) then
298         cs_o(idx) <= '1';
299       else
300         cs_o(idx) <= '0';
301       end if;
302     end loop;
303   end process cs_gen;
304   --
305   -----------------------------------------------------------------------------
306
307
308   -----------------------------------------------------------------------------
309   -- Output mapping
310   -----------------------------------------------------------------------------
311   tdo_o    <= shift_q(0);
312   strobe_o <= strobe_sync_q(0) xor strobe_edge_q;
313   read_o   <= '1' when instr_q = instr_read_c  else '0';
314   write_o  <= '1' when instr_q = instr_write_c else '0';
315   addr_o   <= addr_q;
316   dout_o   <= dout_q;
317
318 end rtl;