mor1kx-bemicrocv/ip/altera/ddr3/altera_jtag_streaming.v

635 lines
26 KiB
Verilog

// (C) 2001-2015 Altera Corporation. All rights reserved.
// Your use of Altera Corporation's design tools, logic functions and other
// software and tools, and its AMPP partner logic functions, and any output
// files any of the foregoing (including device programming or simulation
// files), and any associated documentation or information are expressly subject
// to the terms and conditions of the Altera Program License Subscription
// Agreement, Altera MegaCore Function License Agreement, or other applicable
// license agreement, including, without limitation, that your use is for the
// sole purpose of programming logic devices manufactured by Altera and sold by
// Altera or its authorized distributors. Please refer to the applicable
// agreement for further details.
// synopsys translate_off
`timescale 1 ns / 1 ns
// synopsys translate_on
module altera_jtag_streaming #(
parameter PURPOSE = 0,
parameter UPSTREAM_FIFO_SIZE = 0,
parameter DOWNSTREAM_FIFO_SIZE = 0,
parameter MGMT_CHANNEL_WIDTH = -1
) (
// JTAG Signals
input wire tck,
input wire tdi,
output reg tdo,
input wire [2:0] ir_in,
input wire virtual_state_cdr,
input wire virtual_state_sdr,
input wire virtual_state_udr,
input wire reset_n,
// Source Signals
output wire [7:0] source_data,
output wire source_valid,
// Sink Signals
input wire [7:0] sink_data,
input wire sink_valid,
output wire sink_ready,
// Clock Debug Signals
input wire clock_to_sample,
input wire reset_to_sample,
// Resetrequest signal
output reg resetrequest,
// Debug reset and management channel
output wire debug_reset,
output reg mgmt_valid,
output reg [(MGMT_CHANNEL_WIDTH>0?MGMT_CHANNEL_WIDTH:1)-1:0] mgmt_channel,
output reg mgmt_data
);
// function to calculate log2, floored.
function integer flog2;
input [31:0] Depth;
integer i;
begin
i = Depth;
if ( i <= 0 ) flog2 = 0;
else begin
for(flog2 = -1; i > 0; flog2 = flog2 + 1)
i = i >> 1;
end
end
endfunction // flog2
localparam UPSTREAM_ENCODED_SIZE = flog2(UPSTREAM_FIFO_SIZE);
localparam DOWNSTREAM_ENCODED_SIZE = flog2(DOWNSTREAM_FIFO_SIZE);
localparam TCK_TO_SYSCLK_SYNC_DEPTH = 8;
localparam SYSCLK_TO_TCK_SYNC_DEPTH = 3;
// IR values determine the operating modes
localparam DATA = 0;
localparam LOOPBACK = 1;
localparam DEBUG = 2;
localparam INFO = 3;
localparam CONTROL = 4;
localparam MGMT = 5;
// Operating Modes:
// Data - To send data which its size and valid position are encoded in the header bytes of the data stream
// Loopback - To become a JTAG loopback with a bypass register
// Debug - To read the values of the clock sensing, clock sampling and reset sampling
// Info - To read the parameterized values that describe the components connected to JTAG Phy which is of great interest to the driver
// Control - To set the offset of bit-padding and to do a reset request
// Mgmt - Send management commands (resets mostly) to agents
localparam IRWIDTH = 3;
// State machine encoding for write_state
localparam ST_BYPASS = 'h0;
localparam ST_HEADER_1 = 'h1;
localparam ST_HEADER_2 = 'h2;
localparam ST_WRITE_DATA = 'h3;
// State machine encoding for read_state
localparam ST_HEADER = 'h0;
localparam ST_PADDED = 'h1;
localparam ST_READ_DATA = 'h2;
reg [1:0] write_state = ST_BYPASS;
reg [1:0] read_state = ST_HEADER;
reg [ 7:0] dr_data_in = 'b0;
reg [ 7:0] dr_data_out = 'b0;
reg dr_loopback = 'b0;
reg [ 2:0] dr_debug = 'b0;
reg [10:0] dr_info = 'b0;
reg [ 8:0] dr_control = 'b0;
reg [MGMT_CHANNEL_WIDTH+2:0] dr_mgmt = 'b0;
reg [ 8:0] padded_bit_counter = 'b0;
reg [ 7:0] bypass_bit_counter = 'b0;
reg [ 2:0] write_data_bit_counter = 'b0;
reg [ 2:0] read_data_bit_counter = 'b0;
reg [ 3:0] header_in_bit_counter = 'b0;
reg [ 3:0] header_out_bit_counter = 'b0;
reg [18:0] scan_length_byte_counter = 'b0;
reg [18:0] valid_write_data_length_byte_counter = 'b0;
reg write_data_valid = 'b0;
reg read_data_valid = 'b0;
reg read_data_all_valid = 'b0;
reg decode_header_1 = 'b0;
reg decode_header_2 = 'b0;
wire write_data_byte_aligned;
wire read_data_byte_aligned;
wire padded_bit_byte_aligned;
wire bytestream_end;
assign write_data_byte_aligned = (write_data_bit_counter == 1);
assign read_data_byte_aligned = (read_data_bit_counter == 1);
assign padded_bit_byte_aligned = (padded_bit_counter[2:0] == 'b0);
assign bytestream_end = (scan_length_byte_counter == 'b0);
reg [ 7:0] offset = 'b0;
reg [15:0] header_in = 'b0;
reg [9:0] scan_length = 'b0;
reg [2:0] read_data_length = 'b0;
reg [2:0] write_data_length = 'b0;
wire [7:0] idle_inserter_sink_data;
wire idle_inserter_sink_valid;
wire idle_inserter_sink_ready;
wire [7:0] idle_inserter_source_data;
reg idle_inserter_source_ready = 'b0;
reg [7:0] idle_remover_sink_data = 'b0;
reg idle_remover_sink_valid = 'b0;
wire [7:0] idle_remover_source_data;
wire idle_remover_source_valid;
assign source_data = idle_remover_source_data;
assign source_valid = idle_remover_source_valid;
assign sink_ready = idle_inserter_sink_ready;
assign idle_inserter_sink_data = sink_data;
assign idle_inserter_sink_valid = sink_valid;
reg clock_sensor = 'b0;
reg clock_to_sample_div2 = 'b0;
(* altera_attribute = {"-name GLOBAL_SIGNAL OFF"}*) reg clock_sense_reset_n = 'b1;
wire data_available;
assign data_available = sink_valid;
wire [18:0] decoded_scan_length;
wire [18:0] decoded_write_data_length;
wire [18:0] decoded_read_data_length;
assign decoded_scan_length = { scan_length, {8{1'b1}} };
// +-------------------+----------------+---------------------+
// | scan_length | Length (bytes) | decoded_scan_length |
// +-------------------+----------------+---------------------+
// | 0x0 | 256 | 0x0ff (255) |
// | 0x1 | 512 | 0x1ff (511) |
// | 0x2 | 768 | 0x2ff (767) |
// | . | . | . |
// | 0x3ff | 256k | 0x3ff (256k-1) |
// +-------------------+----------------+---------------------+
// TODO: use look up table to save LEs?
// Decoded value is correct except for 0x7
assign decoded_write_data_length = (write_data_length == 0) ? 19'h0 : (19'h00080 << write_data_length);
assign decoded_read_data_length = (read_data_length == 0) ? 19'h0 : (19'h00080 << read_data_length);
// +-------------------+---------------+---------------------------+
// | read_data_length | Length | decoded_read_data_length |
// | write_data_length | (bytes) | decoded_write_data_length |
// +-------------------+---------------+---------------------------+
// | 0x0 | 0 | 0x0000 (0) |
// | 0x1 | 256 | 0x0100 (256) |
// | 0x2 | 512 | 0x0200 (512) |
// | 0x3 | 1k | 0x0400 (1024) |
// | 0x4 | 2k | 0x0800 (2048) |
// | 0x5 | 4k | 0x1000 (4096) |
// | 0x6 | 8k | 0x2000 (8192) |
// | 0x7 | scan_length | invalid |
// +-------------------+---------------+---------------------------+
wire clock_sensor_sync;
wire reset_to_sample_sync;
wire clock_to_sample_div2_sync;
wire clock_sense_reset_n_sync;
altera_std_synchronizer #(.depth(SYSCLK_TO_TCK_SYNC_DEPTH)) clock_sensor_synchronizer (
.clk(tck),
.reset_n(1'b1),
.din(clock_sensor),
.dout(clock_sensor_sync));
altera_std_synchronizer #(.depth(SYSCLK_TO_TCK_SYNC_DEPTH)) reset_to_sample_synchronizer (
.clk(tck),
.reset_n(1'b1),
.din(reset_to_sample),
.dout(reset_to_sample_sync));
altera_std_synchronizer #(.depth(SYSCLK_TO_TCK_SYNC_DEPTH)) clock_to_sample_div2_synchronizer (
.clk(tck),
.reset_n(1'b1),
.din(clock_to_sample_div2),
.dout(clock_to_sample_div2_sync));
altera_std_synchronizer #(.depth(TCK_TO_SYSCLK_SYNC_DEPTH)) clock_sense_reset_n_synchronizer (
.clk(clock_to_sample),
.reset_n(clock_sense_reset_n),
.din(1'b1),
.dout(clock_sense_reset_n_sync));
always @ (posedge clock_to_sample or negedge clock_sense_reset_n_sync) begin
if (~clock_sense_reset_n_sync) begin
clock_sensor <= 1'b0;
end else begin
clock_sensor <= 1'b1;
end
end
always @ (posedge clock_to_sample) begin
clock_to_sample_div2 <= ~clock_to_sample_div2;
end
always @ (posedge tck) begin
idle_remover_sink_valid <= 1'b0;
idle_inserter_source_ready <= 1'b0;
// Data mode sourcing (write)
// offset(rounded 8) m-i i 16 offset
// +------------+-----------+------------------+--------+------------+
// tdi -> | padded_bit | undefined | valid_write_data | header | bypass_bit |
// +------------+-----------+------------------+--------+------------+
// Data mode DR data stream write format (as seen by hardware)
//
if (ir_in == DATA) begin
if (virtual_state_cdr) begin
if (offset == 'b0) begin
write_state <= ST_HEADER_1;
end else begin
write_state <= ST_BYPASS;
end
// 8-bit bypass_bit_counter
bypass_bit_counter <= offset;
// 4-bit header_in_bit_counter
header_in_bit_counter <= 15;
// 3-bit write_data_bit_counter
write_data_bit_counter <= 0;
// Reset the registers
// TODO: not necessarily all, reduce LE
decode_header_1 <= 1'b0;
decode_header_2 <= 1'b0;
read_data_all_valid <= 1'b0;
valid_write_data_length_byte_counter <= 0;
end
if (virtual_state_sdr) begin
// Discard bypass bits, then decode the 16-bit header
// 3 3 10
// +-------------------+------------------+-------------+
// | write_data_length | read_data_length | scan_length |
// +-------------------+------------------+-------------+
// Header format
case (write_state)
ST_BYPASS: begin
// Discard the bypass bit
bypass_bit_counter <= bypass_bit_counter - 1'b1;
if (bypass_bit_counter == 1) begin
write_state <= ST_HEADER_1;
end
end
// Shift the scan_length and read_data_length
ST_HEADER_1: begin
// TODO: header_in can be shorter
// Shift into header_in
header_in <= {tdi, header_in[15:1]};
header_in_bit_counter <= header_in_bit_counter - 1'b1;
if (header_in_bit_counter == 3) begin
read_data_length <= {tdi, header_in[15:14]};
scan_length <= header_in[13:4];
write_state <= ST_HEADER_2;
decode_header_1 <= 1'b1;
end
end
// Shift the write_data_length
ST_HEADER_2: begin
// Shift into header_in
header_in <= {tdi, header_in[15:1]};
header_in_bit_counter <= header_in_bit_counter - 1'b1;
// Decode read_data_length and scan_length
if (decode_header_1) begin
decode_header_1 <= 1'b0;
// Set read_data_all_valid
if (read_data_length == 3'b111) begin
read_data_all_valid <= 1'b1;
end
// Load scan_length_byte_counter
scan_length_byte_counter <= decoded_scan_length;
end
if (header_in_bit_counter == 0) begin
write_data_length <= {tdi, header_in[15:14]};
write_state <= ST_WRITE_DATA;
decode_header_2 <= 1'b1;
end
end
// Shift the valid_write_data
ST_WRITE_DATA: begin
// Shift into dr_data_in
dr_data_in <= {tdi, dr_data_in[7:1]};
// Decode write_data_length
if (decode_header_2) begin
decode_header_2 <= 1'b0;
// Load valid_write_data_length_byte_counter
case (write_data_length)
3'b111: valid_write_data_length_byte_counter <= decoded_scan_length + 1'b1;
3'b000: valid_write_data_length_byte_counter <= 'b0;
default: valid_write_data_length_byte_counter <= decoded_write_data_length;
endcase
end
write_data_bit_counter <= write_data_bit_counter - 1'b1;
write_data_valid <= (valid_write_data_length_byte_counter != 0);
// Feed the data to the idle remover
if (write_data_byte_aligned && write_data_valid) begin
valid_write_data_length_byte_counter <= valid_write_data_length_byte_counter - 1'b1;
idle_remover_sink_valid <= 1'b1;
idle_remover_sink_data <= {tdi, dr_data_in[7:1]};
end
end
endcase
end
end
// Data mode sinking (read)
// i m-i offset(rounded 8) 16
// +-----------------+-----------+------------+--------+
// | valid_read_data | undefined | padded_bit | header | -> tdo
// +-----------------+-----------+------------+--------+
// Data mode DR data stream read format (as seen by hardware)
//
if (ir_in == DATA) begin
if (virtual_state_cdr) begin
read_state <= ST_HEADER;
// Offset is rounded to nearest ceiling x8 to byte-align padded bits
// 9-bit padded_bit_counter
if (|offset[2:0]) begin
padded_bit_counter[8:3] <= offset[7:3] + 1'b1;
padded_bit_counter[2:0] <= 3'b0;
end else begin
padded_bit_counter <= {1'b0, offset};
end
// 4-bit header_out_bit_counter
header_out_bit_counter <= 0;
// 3-bit read_data_bit_counter
read_data_bit_counter <= 0;
// Load the data_available bit into header
dr_data_out <= {{7{1'b0}}, data_available};
read_data_valid <= 0;
end
if (virtual_state_sdr) begin
// 10 1
// +-----------------------------------+----------------+
// | reserved | data_available |
// +-----------------------------------+----------------+
// Header format
dr_data_out <= {1'b0, dr_data_out[7:1]};
case (read_state)
// Shift the scan_length and read_data_length
ST_HEADER: begin
header_out_bit_counter <= header_out_bit_counter - 1'b1;
// Retrieve data from idle inserter for the next shift if no paddded bits
if (header_out_bit_counter == 2) begin
if (padded_bit_counter == 0) begin
idle_inserter_source_ready <= read_data_all_valid;
end
end
if (header_out_bit_counter == 1) begin
if (padded_bit_counter == 0) begin
read_state <= ST_READ_DATA;
read_data_valid <= read_data_all_valid || (scan_length_byte_counter<=decoded_read_data_length+1);
dr_data_out <= read_data_all_valid ? idle_inserter_source_data : 8'h4a;
end else begin
read_state <= ST_PADDED;
padded_bit_counter <= padded_bit_counter - 1'b1;
idle_inserter_source_ready <= 1'b0;
dr_data_out <= 8'h4a;
end
end
end
ST_PADDED: begin
padded_bit_counter <= padded_bit_counter - 1'b1;
if (padded_bit_byte_aligned) begin
// Load idle character into data register
dr_data_out <= 8'h4a;
end
// Retrieve data from idle inserter for the next shift when padded bits finish
if (padded_bit_counter == 1) begin
idle_inserter_source_ready <= read_data_all_valid;
end
if (padded_bit_counter == 0) begin // TODO: might make use of (padded_bit_counter[8:3]&padded_bit_byte_aligned)
read_state <= ST_READ_DATA;
read_data_valid <= read_data_all_valid || (scan_length_byte_counter<=decoded_read_data_length+1);
dr_data_out <= read_data_all_valid ? idle_inserter_source_data : 8'h4a;
end
end
ST_READ_DATA: begin
read_data_bit_counter <= read_data_bit_counter - 1'b1;
// Retrieve data from idle inserter just before read_data_byte_aligned
if (read_data_bit_counter == 2) begin
// Assert ready to retrieve data from idle inserter only when the bytestream has not ended,
// data is valid (idle_inserter is always valid) and data is needed (read_data_valid)
idle_inserter_source_ready <= bytestream_end ? 1'b0 : read_data_valid;
end
if (read_data_byte_aligned) begin
// Note that bytestream_end is driven by scan_length_byte_counter
if (~bytestream_end) begin
scan_length_byte_counter <= scan_length_byte_counter - 1'b1;
end
read_data_valid <= read_data_all_valid || (scan_length_byte_counter<=decoded_read_data_length+1);
// Load idle character if bytestream has ended, else get data from the idle inserter
dr_data_out <= (read_data_valid & ~bytestream_end) ? idle_inserter_source_data : 8'h4a;
end
end
endcase
end
end
// Loopback mode
if (ir_in == LOOPBACK) begin
if (virtual_state_cdr) begin
dr_loopback <= 1'b0; // capture 0
end
if (virtual_state_sdr) begin
// Shift dr_loopback
dr_loopback <= tdi;
end
end
// Debug mode
if (ir_in == DEBUG) begin
if (virtual_state_cdr) begin
dr_debug <= {clock_sensor_sync, clock_to_sample_div2_sync, reset_to_sample_sync};
end
if (virtual_state_sdr) begin
// Shift dr_debug
dr_debug <= {1'b0, dr_debug[2:1]}; // tdi is ignored
end
if (virtual_state_udr) begin
clock_sense_reset_n <= 1'b0;
end else begin
clock_sense_reset_n <= 1'b1;
end
end
// Info mode
if (ir_in == INFO) begin
if (virtual_state_cdr) begin
dr_info <= {PURPOSE[2:0], UPSTREAM_ENCODED_SIZE[3:0], DOWNSTREAM_ENCODED_SIZE[3:0]};
end
if (virtual_state_sdr) begin
// Shift dr_info
dr_info <= {1'b0, dr_info[10:1]}; // tdi is ignored
end
end
// Control mode
if (ir_in == CONTROL) begin
if (virtual_state_cdr) begin
dr_control <= 'b0; // capture 0
end
if (virtual_state_sdr) begin
// Shift dr_control
dr_control <= {tdi, dr_control[8:1]};
end
if (virtual_state_udr) begin
// Update resetrequest and offset
{resetrequest, offset} <= dr_control;
end
end
end
always @ * begin
if (virtual_state_sdr) begin
case (ir_in)
DATA: tdo <= dr_data_out[0];
LOOPBACK: tdo <= dr_loopback;
DEBUG: tdo <= dr_debug[0];
INFO: tdo <= dr_info[0];
CONTROL: tdo <= dr_control[0];
MGMT: tdo <= dr_mgmt[0];
default: tdo <= 1'b0;
endcase
end else begin
tdo <= 1'b0;
end
end
// Idle Remover
altera_avalon_st_idle_remover idle_remover (
// Interface: clk
.clk (tck),
.reset_n (reset_n),
// Interface: ST in
.in_ready (), // left disconnected
.in_valid (idle_remover_sink_valid),
.in_data (idle_remover_sink_data),
// Interface: ST out
.out_ready (1'b1), // downstream is expected to be always ready
.out_valid (idle_remover_source_valid),
.out_data (idle_remover_source_data)
);
// Idle Inserter
altera_avalon_st_idle_inserter idle_inserter (
// Interface: clk
.clk (tck),
.reset_n (reset_n),
// Interface: ST in
.in_ready (idle_inserter_sink_ready),
.in_valid (idle_inserter_sink_valid),
.in_data (idle_inserter_sink_data),
// Interface: ST out
.out_ready (idle_inserter_source_ready),
.out_valid (),
.out_data (idle_inserter_source_data)
);
generate
if (MGMT_CHANNEL_WIDTH > 0)
begin : has_mgmt
reg [MGMT_CHANNEL_WIDTH+2:0] mgmt_out = 'b0;
reg mgmt_toggle = 1'b0;
wire mgmt_toggle_sync;
reg mgmt_toggle_prev;
always @ (posedge tck) begin
// Debug mode
if (ir_in == MGMT) begin
if (virtual_state_cdr) begin
dr_mgmt <= 'b0;
dr_mgmt[MGMT_CHANNEL_WIDTH+2] <= 1'b1;
end
if (virtual_state_sdr) begin
// Shift dr_debug
dr_mgmt <= {tdi, dr_mgmt[MGMT_CHANNEL_WIDTH+2:1]};
end
if (virtual_state_udr) begin
mgmt_out <= dr_mgmt;
mgmt_toggle <= mgmt_out[MGMT_CHANNEL_WIDTH+2] ? 1'b0 : ~mgmt_toggle;
end
end
end
altera_std_synchronizer #(.depth(TCK_TO_SYSCLK_SYNC_DEPTH)) debug_reset_synchronizer (
.clk(clock_to_sample),
.reset_n(1'b1),
.din(mgmt_out[MGMT_CHANNEL_WIDTH+2]),
.dout(debug_reset));
altera_std_synchronizer #(.depth(TCK_TO_SYSCLK_SYNC_DEPTH)) mgmt_toggle_synchronizer (
.clk(clock_to_sample),
.reset_n(1'b1),
.din(mgmt_toggle),
.dout(mgmt_toggle_sync));
always @ (posedge clock_to_sample or posedge debug_reset) begin
if (debug_reset) begin
mgmt_valid <= 1'b0;
mgmt_toggle_prev <= 1'b0;
end else begin
if ((mgmt_toggle_sync ^ mgmt_toggle_prev) && mgmt_out[MGMT_CHANNEL_WIDTH+1]) begin
mgmt_valid <= 1'b1;
mgmt_channel <= mgmt_out[MGMT_CHANNEL_WIDTH:1];
mgmt_data <= mgmt_out[0];
end else begin
mgmt_valid <= 1'b0;
end
mgmt_toggle_prev <= mgmt_toggle_sync;
end
end
end
else
begin : no_mgmt
always @ (posedge tck) begin
dr_mgmt[0] <= 1'b0;
end
assign debug_reset = 1'b0;
always @ (posedge clock_to_sample) begin
mgmt_valid <= 1'b0;
mgmt_data <= 'b0;
mgmt_channel <= 'b0;
end
end
endgenerate
endmodule