//
// 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
//
//----------------------------------------------------------------------------
module jtag2mem
#(
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
This page: |
Created: | Sun Dec 11 09:59:58 2005 |
|
From: |
./jtag2mem.v |