HITDAQ/FPGA_firmware/q_sys/synthesis/submodules/read_master.v
2024-10-11 14:49:54 +02:00

796 lines
34 KiB
Verilog

// (C) 2001-2019 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other
// software and tools, and its AMPP partner logic functions, and any output
// files from 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 Intel Program License Subscription
// Agreement, Intel FPGA IP License Agreement, or other applicable
// license agreement, including, without limitation, that your use is for the
// sole purpose of programming logic devices manufactured by Intel and sold by
// Intel or its authorized distributors. Please refer to the applicable
// agreement for further details.
/*
This read master module is responsible for reading data from memory and writing
the contents out to a streaming source port. It is controlled by a streaming
sink port called the 'command port'. Any information that must be communicated
back to a host such as the state of the master (reset/stop) is made available by the
streaming source port called the 'response port'.
There are various parameters to control the synthesis of this hardware
either for functionality changes or speed/resource optimizations. Some
of the parameters will be hidden in the component GUI since they are derived
from some other parameters. When this master module is used in a MM to MM
transfer disable the packet support since the packet hardware is not needed.
In order to increase the Fmax you should enable only full accesses so that
the unaligned access and byte enable blocks can be reduced to wires. Also
only configure the length width to be as wide as you need as it will typically
be the critical path of this module.
Revision History:
1.0 Initial version which used a simple exported hand shake control scheme.
2.0 Added support for unaligned accesses, stride, and streaming
2.1 Fixed bugs in the control logic which was causing too many reads to be posted
2.2 Added burst support and renamed the top level module to read master
2.3 Added additional conditional code for 8-bit case to avoid synthesis issues.
2.4 Corrected burst bug that prevented full bursts from being presented to the
fabric. Corrected the stop/reset logic to ensure masters can be stopped
or reset while idle.
2.5 Added early done support for non unaligned or non packet based transfers
2.6 Fixed a flow control issue in the pending reads counter and too many reads pending
signal to avoid potential FIFO overflow issues. The read master now requires the
FIFO depth to be 4x the maximum burst count setting.
2.7 Added 64-bit addressing.
2.8 Fixed another flow control issue when unaligned accesses is enabled. Unaligned
accesses causes one more clock cycle of delay between the read data and the FIFO
so one more burst of room in the FIFO needs to be pre-allocated (3x instead of 2x)
*/
// synthesis translate_off
`timescale 1ns / 1ps
// synthesis translate_on
// turn off superfluous verilog processor warnings
// altera message_level Level1
// altera message_off 10034 10035 10036 10037 10230 10240 10030
module read_master (
clk,
reset,
// descriptor commands sink port
snk_command_data,
snk_command_valid,
snk_command_ready,
// response source port
src_response_data,
src_response_valid,
src_response_ready,
// data path sink port
src_data,
src_valid,
src_ready,
src_sop,
src_eop,
src_empty,
src_error,
src_channel,
// data path master port
master_address,
master_read,
master_byteenable,
master_readdata,
master_waitrequest,
master_readdatavalid,
master_burstcount
);
parameter UNALIGNED_ACCESSES_ENABLE = 0; // when enabled allows transfers to begin from off word boundaries
parameter ONLY_FULL_ACCESS_ENABLE = 0; // when enabled allows transfers to end with partial access, master achieve a much higher fmax when this is enabled
parameter STRIDE_ENABLE = 0; // stride support can only be enabled when unaligned accesses is disabled
parameter STRIDE_WIDTH = 1; // when stride support is enabled this value controls the rate in which the address increases (in words), the stride width + log2(byte enable width) + 1 cannot exceed address width
parameter PACKET_ENABLE = 0;
parameter ERROR_ENABLE = 0;
parameter ERROR_WIDTH = 8; // must be between 1-8, this will only be enabled in the GUI when error enable is turned on
parameter CHANNEL_ENABLE = 0;
parameter CHANNEL_WIDTH = 8; // must be between 1-8, this will only be enabled in the GUI when the channel enable is turned on
parameter DATA_WIDTH = 32;
parameter BYTE_ENABLE_WIDTH = 4; // set by the .tcl file (hidden in GUI)
parameter BYTE_ENABLE_WIDTH_LOG2 = 2; // set by the .tcl file (hidden in GUI)
parameter ADDRESS_WIDTH = 32; // set in the .tcl file (hidden in GUI) by the address span of the master
parameter LENGTH_WIDTH = 32; // GUI setting with warning if ADDRESS_WIDTH < LENGTH_WIDTH (waste of logic for the length counter)
parameter FIFO_DEPTH = 32;
parameter FIFO_DEPTH_LOG2 = 5; // set by the .tcl file (hidden in GUI)
parameter FIFO_SPEED_OPTIMIZATION = 1; // set by the .tcl file (hidden in GUI) The default will be on since it only impacts the latency of the entire transfer by 1 clock cycle and adds very little additional logic.
parameter SYMBOL_WIDTH = 8; // set in the .tcl file (hidden in GUI)
parameter NUMBER_OF_SYMBOLS = 4; // set in the .tcl file (hidden in GUI)
parameter NUMBER_OF_SYMBOLS_LOG2 = 2; // set by the .tcl file (hidden in GUI)
parameter BURST_ENABLE = 0; // when enabled stride must be disabled, 1 to enable, 0 to disable
parameter MAX_BURST_COUNT = 2; // must be a power of 2, when BURST_ENABLE = 0 set maximum_burst_count to 1 (will be automatically done by .tcl file)
parameter MAX_BURST_COUNT_WIDTH = 2; // set by the .tcl file (hidden in GUI) = log2(maximum_burst_count) + 1
parameter PROGRAMMABLE_BURST_ENABLE = 0; // when enabled the user must set the burst count, if 0 is set then the value "maximum_burst_count" will be used instead
parameter BURST_WRAPPING_SUPPORT = 1; // will only be used when bursting is enabled. This cannot be enabled with programmable burst capabilities. Enabling it will make sure the master gets back into burst alignment (data width in bytes * maximum burst count alignment)
localparam FIFO_USE_MEMORY = 1; // set to 0 to use LEs instead, not exposed since FPGAs have a lot of memory these days
localparam BIG_ENDIAN_ACCESS = 0; // hiding this since it can blow your foot off if you are not careful. It's big endian with respect to the write master width and not necessarily to the width of the data type used by a host CPU.
// handy mask for seperating the word address from the byte address bits, so for 32 bit masters this mask is 0x3, for 64 bit masters it'll be 0x7
localparam LSB_MASK = {BYTE_ENABLE_WIDTH_LOG2{1'b1}};
// when packet data is supported then we need to buffer the empty, eop, sop, error, and channel bits
localparam FIFO_WIDTH = DATA_WIDTH + NUMBER_OF_SYMBOLS_LOG2 + 2 + ERROR_WIDTH + CHANNEL_WIDTH;
localparam ADDRESS_INCREMENT_WIDTH = (BYTE_ENABLE_WIDTH_LOG2 + MAX_BURST_COUNT_WIDTH + STRIDE_WIDTH);
localparam FIXED_STRIDE = 1'b1; // default stride distance used when stride is disabled. 1 means increment the address by a word (i.e. sequential transfer)
input clk;
input reset;
// descriptor commands sink port
input [255:0] snk_command_data;
input snk_command_valid;
output reg snk_command_ready;
// response source port
output wire [255:0] src_response_data;
output reg src_response_valid;
input src_response_ready;
// data path source port
output wire [DATA_WIDTH-1:0] src_data;
output wire src_valid;
input src_ready;
output wire src_sop;
output wire src_eop;
output wire [NUMBER_OF_SYMBOLS_LOG2-1:0] src_empty;
output wire [ERROR_WIDTH-1:0] src_error;
output wire [CHANNEL_WIDTH-1:0] src_channel;
// master inputs and outputs
input master_waitrequest;
output wire [ADDRESS_WIDTH-1:0] master_address;
output wire master_read;
output wire [BYTE_ENABLE_WIDTH-1:0] master_byteenable;
input [DATA_WIDTH-1:0] master_readdata;
input master_readdatavalid;
output wire [MAX_BURST_COUNT_WIDTH-1:0] master_burstcount;
// internal signals
wire [63:0] descriptor_address;
wire [31:0] descriptor_length;
wire [15:0] descriptor_stride;
wire [7:0] descriptor_channel;
wire descriptor_generate_sop;
wire descriptor_generate_eop;
wire [7:0] descriptor_error;
wire [7:0] descriptor_programmable_burst_count;
wire descriptor_early_done_enable;
wire sw_stop_in;
wire sw_reset_in;
reg early_done_enable_d1;
reg [ERROR_WIDTH-1:0] error_d1;
reg [MAX_BURST_COUNT_WIDTH-1:0] programmable_burst_count_d1;
wire [MAX_BURST_COUNT_WIDTH-1:0] maximum_burst_count;
reg generate_sop_d1;
reg generate_eop_d1;
reg [ADDRESS_WIDTH-1:0] address_counter;
reg [LENGTH_WIDTH-1:0] length_counter;
reg [CHANNEL_WIDTH-1:0] channel_d1;
reg [STRIDE_WIDTH-1:0] stride_d1;
wire [STRIDE_WIDTH-1:0] stride_amount; // either set to be stride_d1 or hardcoded to 1 depending on the parameterization
reg [BYTE_ENABLE_WIDTH_LOG2-1:0] start_byte_address; // used to determine how far out of alignment the master starts
reg first_access; // used to determine if the first read is occuring
wire first_word_boundary_not_reached; // set when the first access doesn't reach the next word boundary
reg first_word_boundary_not_reached_d1;
reg [FIFO_DEPTH_LOG2:0] pending_reads_counter;
reg [FIFO_DEPTH_LOG2:0] pending_reads_mux;
wire [FIFO_WIDTH-1:0] fifo_write_data;
wire [FIFO_WIDTH-1:0] fifo_read_data;
wire fifo_write;
wire fifo_read;
wire fifo_empty;
wire fifo_full;
wire [FIFO_DEPTH_LOG2-1:0] fifo_used;
wire too_many_pending_reads;
wire read_complete; // handy signal for determining when a read has occured and completed
wire address_increment_enable;
wire [ADDRESS_INCREMENT_WIDTH-1:0] address_increment; // amount of bytes to increment the address
wire [ADDRESS_INCREMENT_WIDTH-1:0] bytes_to_transfer;
wire short_first_access_enable; // when starting unaligned and the amount of data to transfer reaches the next word boundary
wire short_last_access_enable; // when address is aligned (can be an unaligned buffer transfer) but the amount of data doesn't reach the next word boundary
wire short_first_and_last_access_enable; // when starting unaligned and the amount of data to transfer doesn't reach the next word boundary
wire [ADDRESS_INCREMENT_WIDTH-1:0] short_first_access_size;
wire [ADDRESS_INCREMENT_WIDTH-1:0] short_last_access_size;
wire [ADDRESS_INCREMENT_WIDTH-1:0] short_first_and_last_access_size;
reg [ADDRESS_INCREMENT_WIDTH-1:0] bytes_to_transfer_mux;
wire go;
wire done; // asserted when last read is issued
reg done_d1;
wire done_strobe;
wire all_reads_returned; // asserted when last read returns
reg all_reads_returned_d1;
wire all_reads_returned_strobe;
reg all_reads_returned_strobe_d1;
reg all_reads_returned_strobe_d2; // used to trigger src_response_ready later than when the last read returns since the MM to ST has two pipeline stages
wire [DATA_WIDTH-1:0] MM_to_ST_adapter_dataout;
wire [DATA_WIDTH-1:0] MM_to_ST_adapter_dataout_rearranged;
wire MM_to_ST_adapter_sop;
wire MM_to_ST_adapter_eop;
wire [NUMBER_OF_SYMBOLS_LOG2-1:0] MM_to_ST_adapter_empty;
wire masked_sop;
wire masked_eop;
reg flush;
reg stopped;
wire length_sync_reset;
wire set_src_response_valid;
reg master_read_reg;
/********************************************* REGISTERS **************************************************/
// registering descriptor information
always @ (posedge clk or posedge reset)
begin
if (reset)
begin
error_d1 <= 0;
generate_sop_d1 <= 0;
generate_eop_d1 <= 0;
channel_d1 <= 0;
stride_d1 <= 0;
programmable_burst_count_d1 <= 0;
early_done_enable_d1 <= 0;
end
else if (go == 1)
begin
error_d1 <= descriptor_error[ERROR_WIDTH-1:0];
generate_sop_d1 <= descriptor_generate_sop;
generate_eop_d1 <= descriptor_generate_eop;
channel_d1 <= descriptor_channel[CHANNEL_WIDTH-1:0];
stride_d1 <= descriptor_stride[STRIDE_WIDTH-1:0];
programmable_burst_count_d1 <= ((descriptor_programmable_burst_count == 0) | (descriptor_programmable_burst_count > MAX_BURST_COUNT)) ? MAX_BURST_COUNT : descriptor_programmable_burst_count;
early_done_enable_d1 <= ((UNALIGNED_ACCESSES_ENABLE == 1) | (PACKET_ENABLE == 1))? 0 : descriptor_early_done_enable; // early done cannot be used when unaligned data or packet support is enabled
end
end
// master word increment counter
always @ (posedge clk or posedge reset)
begin
if (reset)
begin
address_counter <= 0;
end
else
begin
if (go == 1)
begin
address_counter <= descriptor_address[ADDRESS_WIDTH-1:0];
end
else if (address_increment_enable == 1)
begin
address_counter <= address_counter + address_increment;
end
end
end
// master byte address, used to determine how far out of alignment the master began transfering data
always @ (posedge clk or posedge reset)
begin
if (reset)
begin
start_byte_address <= 0;
end
else if (go == 1)
begin
start_byte_address <= descriptor_address[BYTE_ENABLE_WIDTH_LOG2-1:0];
end
end
// first_access will be asserted only for the first read of a transaction, this will be used to determine what value will be used to increment the counters
always @ (posedge clk or posedge reset)
begin
if (reset == 1)
begin
first_access <= 0;
end
else
begin
if (go == 1)
begin
first_access <= 1;
end
else if ((first_access == 1) & (address_increment_enable == 1))
begin
first_access <= 0;
end
end
end
// this register is used to determine if the first word boundary will be reached
always @ (posedge clk or posedge reset)
begin
if (reset)
begin
first_word_boundary_not_reached_d1 <= 0;
end
else if (go == 1)
begin
first_word_boundary_not_reached_d1 <= first_word_boundary_not_reached;
end
end
// master length logic, this will typically be the critical path followed by the FIFO
always @ (posedge clk or posedge reset)
begin
if (reset)
begin
length_counter <= 0;
end
else
begin
if (length_sync_reset == 1)
begin
length_counter <= 0;
end
else if (go == 1)
begin
length_counter <= descriptor_length[LENGTH_WIDTH-1:0];
end
else if (address_increment_enable == 1)
begin
length_counter <= length_counter - bytes_to_transfer; // not using address_increment because stride might be enabled
end
end
end
// the pending reads counter is used to determine how many outstanding reads are posted. This will be used to determine
// if more reads can be posted based on the number of unused words in the FIFO.
always @ (posedge clk or posedge reset)
begin
if (reset)
begin
pending_reads_counter <= 0;
end
else
begin
pending_reads_counter <= pending_reads_mux;
end
end
always @ (posedge clk or posedge reset)
begin
if (reset)
begin
done_d1 <= 1; // master is done coming out of reset (need this to be set high so that done_strobe doesn't fire)
end
else
begin
done_d1 <= done;
end
end
// this is the 'final done' condition, since reads are pipelined need to make sure they have all returned before the master is really done.
always @ (posedge clk or posedge reset)
begin
if (reset)
begin
all_reads_returned_d1 <= 1;
end
else
begin
all_reads_returned_d1 <= all_reads_returned;
end
end
always @ (posedge clk or posedge reset)
begin
if (reset == 1)
begin
flush <= 0;
end
else
begin
if ((pending_reads_counter == 0) & (flush == 1))
begin
flush <= 0;
end
else if ((sw_reset_in == 1) & ((read_complete == 1) | (snk_command_ready == 1) | (master_read_reg == 0)))
begin
flush <= 1; // will be used to reset the length counter to 0 and flush out pending reads (by letting them return without buffering them)
end
end
end
always @ (posedge clk or posedge reset)
begin
if (reset)
begin
stopped <= 0;
end
else
begin
if ((sw_stop_in == 0) | (sw_reset_in == 1))
begin
stopped <= 0;
end
else if ((sw_stop_in == 1) & ((read_complete == 1) | (snk_command_ready == 1) | (master_read_reg == 0)))
begin
stopped <= 1;
end
end
end
always @ (posedge clk or posedge reset)
begin
if (reset)
begin
snk_command_ready <= 1; // have to start ready to take commands
end
else
begin
if (go == 1)
begin
snk_command_ready <= 0;
end
else if ((src_response_ready == 1) & (src_response_valid == 1)) // need to make sure the response is popped before accepting more commands
begin
snk_command_ready <= 1;
end
end
end
always @ (posedge clk or posedge reset)
begin
if (reset)
begin
all_reads_returned_strobe_d1 <= 0;
all_reads_returned_strobe_d2 <= 0;
end
else
begin
all_reads_returned_strobe_d1 <= all_reads_returned_strobe;
all_reads_returned_strobe_d2 <= all_reads_returned_strobe_d1;
end
end
always @ (posedge clk or posedge reset)
begin
if (reset)
begin
src_response_valid <= 0;
end
else
begin
if (flush == 1)
begin
src_response_valid <= 0;
end
else if (set_src_response_valid == 1) // all the reads have returned with MM to ST adapter latency taken into consideration
begin
src_response_valid <= 1; // will be set only once
end
else if ((src_response_valid == 1) & (src_response_ready == 1))
begin
src_response_valid <= 0; // will be reset only once when the dispatcher captures the data
end
end
end
always @ (posedge clk or posedge reset)
begin
if (reset)
begin
master_read_reg <= 0;
end
else
begin
if ((done == 0) & (too_many_pending_reads == 0) & (sw_stop_in == 0) & (sw_reset_in == 0))
begin
master_read_reg <= 1;
end
else if ((done == 1) | ((read_complete == 1) & ((too_many_pending_reads == 1) | (sw_stop_in == 1))))
begin
master_read_reg <= 0;
end
end
end
/******************************************* END REGISTERS ************************************************/
/************************************** MODULE INSTANTIATIONS *********************************************/
// This block is pipelined and can't throttle the reads
MM_to_ST_Adapter the_MM_to_ST_adapter (
.clk (clk),
.reset (reset),
.length (descriptor_length[LENGTH_WIDTH-1:0]),
.length_counter (length_counter),
.address (descriptor_address[ADDRESS_WIDTH-1:0]),
.reads_pending (pending_reads_counter),
.start (go),
.readdata (master_readdata),
.readdatavalid (master_readdatavalid),
.fifo_data (MM_to_ST_adapter_dataout),
.fifo_write (fifo_write),
.fifo_empty (MM_to_ST_adapter_empty),
.fifo_sop (MM_to_ST_adapter_sop),
.fifo_eop (MM_to_ST_adapter_eop)
);
defparam the_MM_to_ST_adapter.DATA_WIDTH = DATA_WIDTH;
defparam the_MM_to_ST_adapter.LENGTH_WIDTH = LENGTH_WIDTH;
defparam the_MM_to_ST_adapter.ADDRESS_WIDTH = ADDRESS_WIDTH;
defparam the_MM_to_ST_adapter.BYTE_ADDRESS_WIDTH = BYTE_ENABLE_WIDTH_LOG2;
defparam the_MM_to_ST_adapter.READS_PENDING_WIDTH = FIFO_DEPTH_LOG2 + 1;
defparam the_MM_to_ST_adapter.EMPTY_WIDTH = NUMBER_OF_SYMBOLS_LOG2;
defparam the_MM_to_ST_adapter.PACKET_SUPPORT = PACKET_ENABLE;
defparam the_MM_to_ST_adapter.UNALIGNED_ACCESS_ENABLE = UNALIGNED_ACCESSES_ENABLE;
defparam the_MM_to_ST_adapter.FULL_WORD_ACCESS_ONLY = ONLY_FULL_ACCESS_ENABLE;
// buffered sop, eop, empty, data (in that order). sop, eop, and empty are only buffered when packet support is enabled
scfifo the_master_to_st_fifo (
.aclr (reset),
.sclr (flush),
.clock (clk),
.data (fifo_write_data),
.full (fifo_full),
.empty (fifo_empty),
.usedw (fifo_used),
.q (fifo_read_data),
.rdreq (fifo_read),
.wrreq (fifo_write)
);
defparam the_master_to_st_fifo.lpm_width = FIFO_WIDTH;
defparam the_master_to_st_fifo.lpm_numwords = FIFO_DEPTH;
defparam the_master_to_st_fifo.lpm_widthu = FIFO_DEPTH_LOG2;
defparam the_master_to_st_fifo.lpm_showahead = "ON"; // slower but doesn't require complex control logic to time with waitrequest
defparam the_master_to_st_fifo.use_eab = (FIFO_USE_MEMORY == 1)? "ON" : "OFF";
defparam the_master_to_st_fifo.add_ram_output_register = (FIFO_SPEED_OPTIMIZATION == 1)? "ON" : "OFF";
defparam the_master_to_st_fifo.underflow_checking = "OFF";
defparam the_master_to_st_fifo.overflow_checking = "OFF";
// burst block that takes the length and short access enables and forms a burst count based on them. If any of the short access bits are asserted the block will default to a burst count of 1
read_burst_control the_read_burst_control (
.address (master_address),
.length (length_counter),
.maximum_burst_count (maximum_burst_count),
.short_first_access_enable (short_first_access_enable),
.short_last_access_enable (short_last_access_enable),
.short_first_and_last_access_enable (short_first_and_last_access_enable),
.burst_count (master_burstcount)
);
defparam the_read_burst_control.BURST_ENABLE = BURST_ENABLE;
defparam the_read_burst_control.BURST_COUNT_WIDTH = MAX_BURST_COUNT_WIDTH;
defparam the_read_burst_control.WORD_SIZE_LOG2 = (DATA_WIDTH == 8)? 0 : BYTE_ENABLE_WIDTH_LOG2; // need to make sure log2(word size) is 0 instead of 1 here when the data width is 8 bits
defparam the_read_burst_control.ADDRESS_WIDTH = ADDRESS_WIDTH;
defparam the_read_burst_control.LENGTH_WIDTH = LENGTH_WIDTH;
defparam the_read_burst_control.BURST_WRAPPING_SUPPORT = BURST_WRAPPING_SUPPORT;
/************************************ END MODULE INSTANTIATIONS *******************************************/
/******************************** CONTROL AND COMBINATIONAL SIGNALS ***************************************/
// breakout the descriptor information into more manageable names
assign descriptor_address = {snk_command_data[140:109], snk_command_data[31:0]}; // 64-bit addressing support
assign descriptor_length = snk_command_data[63:32];
assign descriptor_channel = snk_command_data[71:64];
assign descriptor_generate_sop = snk_command_data[72];
assign descriptor_generate_eop = snk_command_data[73];
assign descriptor_programmable_burst_count = snk_command_data[83:76];
assign descriptor_stride = snk_command_data[99:84];
assign descriptor_error = snk_command_data[107:100];
assign descriptor_early_done_enable = snk_command_data[108];
assign sw_stop_in = snk_command_data[74];
assign sw_reset_in = snk_command_data[75];
assign stride_amount = (STRIDE_ENABLE == 1)? stride_d1[STRIDE_WIDTH-1:0] : FIXED_STRIDE; // hardcoding to FIXED_STRIDE when stride capabilities are disabled
assign maximum_burst_count = (PROGRAMMABLE_BURST_ENABLE == 1)? programmable_burst_count_d1 : MAX_BURST_COUNT;
// swap the bytes if big endian is enabled
generate
if (BIG_ENDIAN_ACCESS == 1)
begin
genvar j;
for(j=0; j < DATA_WIDTH; j = j + 8)
begin: byte_swap
assign MM_to_ST_adapter_dataout_rearranged[j +8 -1: j] = MM_to_ST_adapter_dataout[DATA_WIDTH -j -1: DATA_WIDTH -j - 8];
end
end
else
begin
assign MM_to_ST_adapter_dataout_rearranged = MM_to_ST_adapter_dataout;
end
endgenerate
assign masked_sop = MM_to_ST_adapter_sop & generate_sop_d1;
assign masked_eop = MM_to_ST_adapter_eop & generate_eop_d1;
assign fifo_write_data = {error_d1, channel_d1, masked_sop, masked_eop, ((masked_eop == 1)? MM_to_ST_adapter_empty : {NUMBER_OF_SYMBOLS_LOG2{1'b0}} ), MM_to_ST_adapter_dataout_rearranged};
// Avalon-ST is network order (a.k.a. big endian) so we need to reverse the symbols before sending them to the data stream
generate
genvar i;
for(i = 0; i < DATA_WIDTH; i = i + SYMBOL_WIDTH) // the data width is always a multiple of the symbol width
begin: symbol_swap
assign src_data[i +SYMBOL_WIDTH -1: i] = fifo_read_data[DATA_WIDTH -i -1: DATA_WIDTH -i - SYMBOL_WIDTH];
end
endgenerate
// Format of internal FIFO data as following: {error, channel, sop, eop, empty, data}
assign src_empty = (PACKET_ENABLE == 1)? fifo_read_data[(DATA_WIDTH + NUMBER_OF_SYMBOLS_LOG2 - 1) : DATA_WIDTH] : 0;
assign src_eop = (PACKET_ENABLE == 1)? fifo_read_data[DATA_WIDTH + NUMBER_OF_SYMBOLS_LOG2] : 0;
assign src_sop = (PACKET_ENABLE == 1)? fifo_read_data[DATA_WIDTH + NUMBER_OF_SYMBOLS_LOG2 + 1] : 0;
assign src_channel = (CHANNEL_ENABLE == 1)? fifo_read_data[(DATA_WIDTH + NUMBER_OF_SYMBOLS_LOG2 + CHANNEL_WIDTH + 1): (DATA_WIDTH + NUMBER_OF_SYMBOLS_LOG2 + 2)] : 0;
// case:220874 mSGDMA read master connects the wrong bits for the source error
//assign src_error = (ERROR_ENABLE == 1)? fifo_read_data[(FIFO_WIDTH-1):(DATA_WIDTH + NUMBER_OF_SYMBOLS_LOG2 + ERROR_WIDTH + 2)] : 0;
assign src_error = (ERROR_ENABLE == 1)? fifo_read_data[(FIFO_WIDTH-1):(DATA_WIDTH + NUMBER_OF_SYMBOLS_LOG2 + CHANNEL_WIDTH + 2)] : 0;
assign short_first_access_size = BYTE_ENABLE_WIDTH - (address_counter & LSB_MASK);
assign short_last_access_size = length_counter & LSB_MASK;
assign short_first_and_last_access_size = length_counter & LSB_MASK;
/* special case transfer enables and counter increment values (address and length counter)
short_first_access_enable is for transfers that start unaligned but reach the next word boundary
short_last_access_enable is for transfers that are not the first transfer but don't end on a word boundary
short_first_and_last_access_enable is for transfers that start and end with a single transfer and don't end on a word boundary (aligned or unaligned)
*/
generate
if (UNALIGNED_ACCESSES_ENABLE == 1)
begin
assign short_first_access_enable = ((address_counter & LSB_MASK) != 0) & (first_access == 1) & (first_word_boundary_not_reached_d1 == 0);
assign short_last_access_enable = (first_access == 0) & (length_counter < BYTE_ENABLE_WIDTH);
assign short_first_and_last_access_enable = (first_access == 1) & (first_word_boundary_not_reached_d1 == 1);
assign bytes_to_transfer = bytes_to_transfer_mux;
assign address_increment = bytes_to_transfer_mux; // can't use stride when unaligned accesses are enabled
end
else if (ONLY_FULL_ACCESS_ENABLE == 1)
begin
assign short_first_access_enable = 0;
assign short_last_access_enable = 0;
assign short_first_and_last_access_enable = 0;
assign bytes_to_transfer = BYTE_ENABLE_WIDTH * master_burstcount;
if (STRIDE_ENABLE == 1)
begin
assign address_increment = BYTE_ENABLE_WIDTH * stride_amount * master_burstcount; // stride must be a static '1' when bursting is enabled
end
else
begin
assign address_increment = BYTE_ENABLE_WIDTH * master_burstcount; // stride must be a static '1' when bursting is enabled
end
end
else // must be aligned but can end with any number of bytes
begin
assign short_first_access_enable = 0;
assign short_last_access_enable = length_counter < BYTE_ENABLE_WIDTH; // less than a word to transfer
assign short_first_and_last_access_enable = 0;
assign bytes_to_transfer = bytes_to_transfer_mux;
if (STRIDE_ENABLE == 1)
begin
assign address_increment = BYTE_ENABLE_WIDTH * stride_amount * master_burstcount; // stride must be a static '1' when bursting is enabled
end
else
begin
assign address_increment = BYTE_ENABLE_WIDTH * master_burstcount; // stride must be a static '1' when bursting is enabled
end
end
endgenerate
// the burst count will be 1 for all short accesses
always @ (short_first_access_enable or short_last_access_enable or short_first_and_last_access_enable or short_first_access_size or short_last_access_size or short_first_and_last_access_size or master_burstcount)
begin
case ({short_first_and_last_access_enable, short_last_access_enable, short_first_access_enable})
3'b001: bytes_to_transfer_mux = short_first_access_size;
3'b010: bytes_to_transfer_mux = short_last_access_size;
3'b100: bytes_to_transfer_mux = short_first_and_last_access_size;
default: bytes_to_transfer_mux = BYTE_ENABLE_WIDTH * master_burstcount; // this is the only time master_burstcount can be a value other than 1
endcase
end
always @ (master_readdatavalid or read_complete or pending_reads_counter or master_burstcount)
begin
case ({master_readdatavalid, read_complete})
2'b00: pending_reads_mux = pending_reads_counter; // no read posted and no read data returned
2'b01: pending_reads_mux = (pending_reads_counter + master_burstcount); // read posted and no read data returned
2'b10: pending_reads_mux = (pending_reads_counter - 1'b1); // no read posted but read data returned
2'b11: pending_reads_mux = (pending_reads_counter + master_burstcount - 1'b1); // read posted and read data returned
endcase
end
assign src_valid = (fifo_empty == 0);
assign first_word_boundary_not_reached = (descriptor_length < BYTE_ENABLE_WIDTH) & // length is less than the word size
(((descriptor_length & LSB_MASK) + (descriptor_address & LSB_MASK)) < BYTE_ENABLE_WIDTH); // start address + length doesn't reach the next word boundary
assign go = (snk_command_valid == 1) & (snk_command_ready == 1); // go with be one cycle since done will be set to 0 on the next cycle (length will be non-zero)
assign done = (length_counter == 0); // all reads are posted but the master is not done since there could be reads pending
assign done_strobe = (done == 1) & (done_d1 == 0);
assign fifo_read = (src_valid == 1) & (src_ready == 1);
assign length_sync_reset = (flush == 1) & (pending_reads_counter == 0); // resetting the length counter will trigger the done condition
// need to adjust the read throttling based on unaligned accesses being enabled, when enabled there is an additional stage of pipelining before the FIFO that needs to be accounted for
generate
if (UNALIGNED_ACCESSES_ENABLE == 1)
begin
assign too_many_pending_reads = (({fifo_full,fifo_used} + pending_reads_counter) > (FIFO_DEPTH - (maximum_burst_count * 3))); // making sure a full burst can be posted, using 3x maximum_burst_count since the read signal is pipelined and there is a pipeline stage in the MM to ST adapter and so this signal will be late using maximum_burst_count alone
end
else
begin
assign too_many_pending_reads = (({fifo_full,fifo_used} + pending_reads_counter) > (FIFO_DEPTH - (maximum_burst_count * 2))); // making sure a full burst can be posted, using 2x maximum_burst_count since the read signal is pipelined and so this signal will be late using maximum_burst_count alone
end
endgenerate
assign read_complete = (master_read == 1) & (master_waitrequest == 0);
assign address_increment_enable = read_complete;
assign master_byteenable = {BYTE_ENABLE_WIDTH{1'b1}}; // master always asserts all byte enables and filters the data as it comes in (may lead to destructive reads in some cases)
generate if (DATA_WIDTH > 8)
begin
assign master_address = address_counter & { {(ADDRESS_WIDTH-BYTE_ENABLE_WIDTH_LOG2){1'b1}}, {BYTE_ENABLE_WIDTH_LOG2{1'b0}} }; // masking LSBs (byte offsets) since the address counter might not be aligned for the first transfer
end
else
begin
assign master_address = address_counter; // don't need to mask any bits as the address will only advance one byte at a time
end
endgenerate
assign master_read = master_read_reg & (done == 0); // need to mask the read with done so that it doesn't issue one extra read at the end
assign all_reads_returned = (done == 1) & (pending_reads_counter == 0);
assign all_reads_returned_strobe = (all_reads_returned == 1) & (all_reads_returned_d1 == 0);
// for now the done and early done strobes are the same. Both will be triggered when the last data returns
generate
if (UNALIGNED_ACCESSES_ENABLE == 1) // need to use the delayed strobe since there are two stages of pipelining in the MM to ST adapter
begin
assign src_response_data = {{252{1'b0}}, all_reads_returned_strobe_d2, done_strobe, stopped, flush}; // 252 zeros: done strobe: early done strobe: stop state: reset delayed
end
else
begin
assign src_response_data = {{252{1'b0}}, all_reads_returned_strobe, done_strobe, stopped, flush}; // 252 zeros: done strobe: early done strobe: stop state: reset delayed
end
endgenerate
assign set_src_response_valid = (UNALIGNED_ACCESSES_ENABLE == 1)? all_reads_returned_strobe_d2 : // all the reads have returned with MM to ST adapter latency taken into consideration
(early_done_enable_d1 == 1)? done_strobe : all_reads_returned_strobe; // when early done is enabled then the done strobe is sufficient to trigger the next command can enter, otherwise need to wait for the pending reads to return
/****************************** END CONTROL AND COMBINATIONAL SIGNALS *************************************/
endmodule