HierarchyFilesModulesSignalsTasksFunctionsHelp
//
// File:   jtag2mem.v
// Date:   09-Dec-05
// Author: I. Chuang <ichuang@mit.edu>;
//
// Verilog module to communicate between host PC and Xilinx FPGA using
// jtag interface.  Allows memory contents to be read out and written to
// by the PC.
//
// The JTAG interface can read/write two user registers in the FPGA,
// USER1 and USER2.  We use USER1 as a four-bit instruction register,
// and USER2 as a variable bit width data register.  See the code below
// for the various instructions.  The basic interaction model is a memory
// interface.  A counter provides the address and data reads and writes
// automatically increment the counter.  The counter can also be read
// from and written to, and initialized to zero.
//
// This code is part of the jtag2mem package, created for educational
// purposes for the MIT 6.111 Digital Systems Laboratory course (fall'05).
//
//----------------------------------------------------------------------------
// 
//  This file is part of jtag2mem, (c) 2005 Isaac Chuang <ichuang@mit.edu>;
// 
//  jtag2mem 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.
// 
//  jtag2mem 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 code; if not, write to the Free Software Foundation,
//  Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
//----------------------------------------------------------------------------

[Up: jtag2mem_6111 j2m]
module jtag2memIndex
  #(
    parameter DBITS = 8,
    parameter ABITS = 10
    )
   (
    input wire reset,			// system reset
    input wire clk,			// system clock
 
    input wire [DBITS-1:0] read_data,	// memory read data
    output reg [DBITS-1:0] write_data,	// memory write data
    output reg [ABITS-1:0] ram_addr,	// memory address
    output reg ram_we,			// memory write-enable (active high)
    output wire [3:0] ctrl_state	// for debugging
    );
   
   ////////////////////////////////////////////////////////////
   // instantiate low-level JTAG interface module

   wire       jtagCAPTURE;
   wire       jtagDRCK1;
   wire       jtagDRCK2;
   wire       jtagRESET;
   wire       jtagSEL1;
   wire       jtagSEL2;
   wire       jtagSHIFT;
   wire       jtagTDI;
   wire       jtagUPDATE;
   wire       jtagTDO1;
   wire       jtagTDO2;

   BSCAN_VIRTEX2 BSCAN_VIRTEX2_inst 
     ( .CAPTURE(jtagCAPTURE), 	// CAPTURE output from TAP controller 
       .DRCK1(jtagDRCK1), 	// Data register output - USER1 functions 
       .DRCK2(jtagDRCK2), 	// Data register output - USER2 functions 
       .RESET(jtagRESET), 	// Reset output from TAP controller
       .SEL1(jtagSEL1), 	// USER1 active output 
       .SEL2(jtagSEL2), 	// USER2 active output 
       .SHIFT(jtagSHIFT), 	// SHIFT output from TAP controller 
       .TDI(jtagTDI), 		// TDI output from TAP controller 
       .UPDATE(jtagUPDATE), 	// UPDATE output from TAP controller 
       .TDO1(jtagTDO1), 	// Data input for USER1 function
       .TDO2(jtagTDO2) 		// Data input for USER2 function 
       );

   ////////////////////////////////////////////////////////////
   // User register 1 is for instructions and control.
   // User register 2 is for data.
   //
   // The protocol is to receive one or more instructions to control
   // the address counter; for each data byte received / requested,
   // the memory address counter is incremented.  When in write-data
   // mode, data is written to the current address before the address
   // is incremented.  Data is read from each memory location and
   // returned through the JTAG port on TDO on every data register read.
   //
   // Instructions (4 bits):
   //
   //   0 = read data mode
   //   1 = zero the memory address counter
   //   2 = load the memory address counter from the next data reg load
   //   3 = read the memory address counter on the next data reg read
   //   4 = write-data mode (enable writes)
   //   5 = read checksum 
   //   
   // Note that we bit-shift in reverse of some JTAG conventions, so
   // that the xtclsh scripts bit strings read exactly as you
   // would expect (msb on left, lsb on right).
   //
   // Also note that after requesting the memory address counter to be
   // zero'ed, the data register should be accessed once to execute
   // this request.  The memory address counter is changed only after
   // the data register is updated.
   //
   // The checksum is computed as the DBITS-bit sum of all the data bits
   // transferred since the last time the memory address counter was
   // zero'ed or loaded.  As of 10-Dec-05, the checksum code is not yet
   // implemented.

   localparam CMD_READD = 0;	// read data
   localparam CMD_ZRADR = 1;
   localparam CMD_LDADR = 2;
   localparam CMD_RDADR = 3;
   localparam CMD_WRDAT = 4;

   reg [3:0] dr1;		// user data register 1
   wire [3:0] outdat1;		// data to output in dr1
 
   always @(posedge jtagDRCK1)
     dr1 <= jtagRESET ? 0 
		 : jtagSHIFT ? {dr1[2:0],jtagTDI}	// shift left
		 : outdat1;

   assign     jtagTDO1 = dr1[3];
   reg [3:0]  reg_ctrl;		// control register
   always @(posedge jtagUPDATE) reg_ctrl <= jtagRESET ? 0 
					    : jtagSEL1 ? dr1[3:0]
					    : reg_ctrl;

   // decode some control lines

   wire       data_adr_mux;	// mux to DR2: 0 = mem data (wr), 1 = mem addr
   assign     data_adr_mux = ( (reg_ctrl==CMD_RDADR) | (reg_ctrl==CMD_LDADR) );

   // data register

   reg [63:0] dr2;		// user data register 2
   wire [63:0] outdat2;		// data to output in dr2
   reg [ABITS-1:0] my_addr;	// current memory address
   reg [ABITS-1:0] last_addr;	// last memory address
 
   always @(posedge jtagDRCK2)
     dr2 <= jtagRESET ? 0 
		 : jtagSHIFT ? {dr2[62:0],jtagTDI}	// shift left
		 : outdat2;

   assign      jtagTDO2 = data_adr_mux ? dr2[ABITS-1] : dr2[DBITS-1];
   assign      outdat2 = data_adr_mux ? {{(64-ABITS){1'b0}}, ram_addr}
			 : {{(64-DBITS){1'b0}}, read_data};

   reg [DBITS-1:0] reg_data;		// data register
   reg 		   new_data;		// flag: flips whenever new data avail
   always @(posedge jtagUPDATE)
     begin
	reg_data <= ( jtagRESET ? 0 
		      : (jtagSEL2 & (reg_ctrl==CMD_WRDAT)) ? dr2[DBITS-1:0]
		      : reg_data );
	my_addr <= ( (jtagSEL2 & (reg_ctrl==CMD_ZRADR)) ? 0
		     : (jtagSEL2 & (reg_ctrl==CMD_LDADR)) ? dr2[ABITS-1:0]
		     : (jtagSEL2 & (reg_ctrl==CMD_RDADR)) ? my_addr // no incr
		     : jtagSEL2 ? my_addr + 1 
		     : my_addr );
	last_addr <= jtagSEL2 ? my_addr : last_addr;
	new_data <= (jtagSEL2 & (reg_ctrl==CMD_WRDAT)) ? ~new_data : new_data;
     end

   // synchronize address, data, nd, and instruction lines to system clock

   reg [ABITS-1:0] a_s[3:0];
   reg [ABITS-1:0] l_s[3:0];   
   reg [DBITS-1:0] d_s[3:0];
   reg [3:0] 	   c_s[3:0];
   reg  	   n_s[3:0];

   always @(posedge clk)
     begin
	{a_s[3],a_s[2],a_s[1],a_s[0]} <= {a_s[2],a_s[1],a_s[0],my_addr};
	{l_s[3],l_s[2],l_s[1],l_s[0]} <= {l_s[2],l_s[1],l_s[0],last_addr};
	{d_s[3],d_s[2],d_s[1],d_s[0]} <= {d_s[2],d_s[1],d_s[0],reg_data};
	{c_s[3],c_s[2],c_s[1],c_s[0]} <= {c_s[2],c_s[1],c_s[0],reg_ctrl};
	{n_s[3],n_s[2],n_s[1],n_s[0]} <= {n_s[2],n_s[1],n_s[0],new_data};
     end

   // Generate write enable signal for RAM
   //
   // Note on timing: we trigger a write when the address changes, but the
   // data is to be written to the _current_ address, not the next one.
   // Thus, the address lines are delayed; for stability, we
   // delay by two clock cycles.
   //
   // Timing diagram:
   //
   // Clk         1   2   3   4   5   6   7
   // cur_addr    a0  a0  a1  a1  a2  a2  a2
   // next_addr   a1  a1  a2  a2  a3  a3  a3
   // the_data    d0  d0  d1  d1  d2  d2  d2
   // new         0   0   1   1   0   0   0
   // the_we      0   0   1   0   1   0   0
   // ram_we      0   0   0   1   0   1   0
   // write_data  x   x   x   d1  d1  d2  d2
   // ram_addr    a1  a1  a1  a1  a2  a2  a3
   //
   // This guarantees that the address and data will be stable before and
   // after a write, for extra robustness in interfacing to user memories.

   wire [ABITS-1:0] next_addr = a_s[3];
   wire [ABITS-1:0] cur_addr = l_s[3];
   wire [DBITS-1:0] the_data = d_s[3];
   wire [3:0] 	    the_ctrl = c_s[3];
   reg 		    old_ns;
   wire 	    the_we = (old_ns != n_s[3]);

   always @(posedge clk)
     begin
	old_ns <= n_s[3];
	ram_we <= the_we;
	ram_addr <= the_we ? cur_addr : next_addr;
	write_data <= the_we ? the_data : write_data;
     end

   // debugging

   assign 	    ctrl_state = the_ctrl;

endmodule


HierarchyFilesModulesSignalsTasksFunctionsHelp

This page: Created:Sun Dec 11 09:59:58 2005
From: ./jtag2mem.v

Verilog converted to html by v2html 7.30 (written by Costas Calamvokis).Help