This repository has been archived on 2024-01-14. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
vivado-202401/adi_library/axi_dmac/axi_dmac_burst_memory.v
T
2023-04-13 13:33:41 +08:00

459 lines
15 KiB
Verilog

// ***************************************************************************
// ***************************************************************************
// Copyright 2014 - 2018 (c) Analog Devices, Inc. All rights reserved.
//
// In this HDL repository, there are many different and unique modules, consisting
// of various HDL (Verilog or VHDL) components. The individual modules are
// developed independently, and may be accompanied by separate and unique license
// terms.
//
// The user should read each of these license terms, and understand the
// freedoms and responsibilities that he or she has by using this source/core.
//
// This core 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.
//
// Redistribution and use of source or resulting binaries, with or without modification
// of this file, are permitted under one of the following two license terms:
//
// 1. The GNU General Public License version 2 as published by the
// Free Software Foundation, which can be found in the top level directory
// of this repository (LICENSE_GPL2), and also online at:
// <https://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
//
// OR
//
// 2. An ADI specific BSD license, which can be found in the top level directory
// of this repository (LICENSE_ADIBSD), and also on-line at:
// https://github.com/analogdevicesinc/hdl/blob/master/LICENSE_ADIBSD
// This will allow to generate bit files and not release the source code,
// as long as it attaches to an ADI device.
//
// ***************************************************************************
// ***************************************************************************
`timescale 1ns/100ps
module axi_dmac_burst_memory #(
parameter DATA_WIDTH_SRC = 64,
parameter DATA_WIDTH_DEST = 64,
parameter ID_WIDTH = 3,
parameter MAX_BYTES_PER_BURST = 128,
parameter ASYNC_CLK = 1,
parameter BYTES_PER_BEAT_WIDTH_SRC = $clog2(DATA_WIDTH_SRC/8),
parameter BYTES_PER_BURST_WIDTH = $clog2(MAX_BYTES_PER_BURST),
parameter DMA_LENGTH_ALIGN = 3,
parameter ENABLE_DIAGNOSTICS_IF = 0,
parameter ALLOW_ASYM_MEM = 0
) (
input src_clk,
input src_reset,
input src_data_valid,
input [DATA_WIDTH_SRC-1:0] src_data,
input src_data_last,
input [BYTES_PER_BEAT_WIDTH_SRC-1:0] src_data_valid_bytes,
input src_data_partial_burst,
output [ID_WIDTH-1:0] src_data_request_id,
input dest_clk,
input dest_reset,
output dest_data_valid,
input dest_data_ready,
output [DATA_WIDTH_DEST-1:0] dest_data,
output dest_data_last,
output [DATA_WIDTH_DEST/8-1:0] dest_data_strb,
output [BYTES_PER_BURST_WIDTH-1:0] dest_burst_info_length,
output dest_burst_info_partial,
output [ID_WIDTH-1:0] dest_burst_info_id,
output reg dest_burst_info_write = 1'b0,
output [ID_WIDTH-1:0] dest_request_id,
input [ID_WIDTH-1:0] dest_data_request_id,
output [ID_WIDTH-1:0] dest_data_response_id,
// Diagnostics interface
output [7:0] dest_diag_level_bursts
);
localparam DATA_WIDTH_MEM = DATA_WIDTH_SRC > DATA_WIDTH_DEST ?
DATA_WIDTH_SRC : DATA_WIDTH_DEST;
localparam MEM_RATIO = DATA_WIDTH_SRC > DATA_WIDTH_DEST ?
DATA_WIDTH_SRC / DATA_WIDTH_DEST : DATA_WIDTH_DEST / DATA_WIDTH_SRC;
/* A burst can have up to 256 beats */
localparam BURST_LEN = MAX_BYTES_PER_BURST / (DATA_WIDTH_MEM / 8);
localparam BURST_LEN_WIDTH = BURST_LEN > 128 ? 8 :
BURST_LEN > 64 ? 7 :
BURST_LEN > 32 ? 6 :
BURST_LEN > 16 ? 5 :
BURST_LEN > 8 ? 4 :
BURST_LEN > 4 ? 3 :
BURST_LEN > 2 ? 2 : 1;
localparam AUX_FIFO_SIZE = 2**(ID_WIDTH-1);
localparam MEM_RATIO_WIDTH =
(ALLOW_ASYM_MEM == 0 || MEM_RATIO == 1) ? 0 :
MEM_RATIO == 2 ? 1 :
MEM_RATIO == 4 ? 2 : 3;
localparam BURST_LEN_WIDTH_SRC = BURST_LEN_WIDTH +
(DATA_WIDTH_SRC < DATA_WIDTH_MEM ? MEM_RATIO_WIDTH : 0);
localparam BURST_LEN_WIDTH_DEST = BURST_LEN_WIDTH +
(DATA_WIDTH_DEST < DATA_WIDTH_MEM ? MEM_RATIO_WIDTH : 0);
localparam DATA_WIDTH_MEM_SRC = DATA_WIDTH_MEM >>
(DATA_WIDTH_SRC < DATA_WIDTH_MEM ? MEM_RATIO_WIDTH : 0);
localparam DATA_WIDTH_MEM_DEST = DATA_WIDTH_MEM >>
(DATA_WIDTH_DEST < DATA_WIDTH_MEM ? MEM_RATIO_WIDTH : 0);
localparam ADDRESS_WIDTH_SRC = BURST_LEN_WIDTH_SRC + ID_WIDTH - 1;
localparam ADDRESS_WIDTH_DEST = BURST_LEN_WIDTH_DEST + ID_WIDTH - 1;
localparam BYTES_PER_BEAT_WIDTH_MEM_SRC = BYTES_PER_BURST_WIDTH - BURST_LEN_WIDTH_SRC;
localparam BYTES_PER_BEAT_WIDTH_DEST = BYTES_PER_BURST_WIDTH - BURST_LEN_WIDTH_DEST;
/*
* The burst memory is separated into 2**(ID_WIDTH-1) segments. Each segment can
* hold up to BURST_LEN beats. The addresses that are used to access the memory
* are split into two parts. The MSBs index the segment and the LSBs index a
* beat in a specific segment.
*
* src_id and dest_id are used to index the segment of the burst memory on the
* write and read side respectively. The IDs are 1 bit wider than the address of
* the burst memory. So we can't use them directly as an index into the burst
* memory. Since the IDs are gray counted we also can't just leave out the MSB
* like with a binary counter. But XOR-ing the two MSBs of a gray counter gives
* us a gray counter of 1 bit less. Use this to generate the segment index.
* These addresses are captured in the src_id_reduced and dest_id_reduced
* signals.
*
* src_beat_counter and dest_beat_counter are used to index the beat on the
* write and read side respectively. They will be incremented for each beat that
* is written/read. Note that the beat counters are not reset to 0 on the last
* beat of a burst. This means the first beat of a burst might not be stored at
* offset 0 in the segment memory. But this is OK since the beat counter
* increments modulo the segment size and both the write and read side agree on
* the order.
*/
reg [ID_WIDTH-1:0] src_id_next;
reg [ID_WIDTH-1:0] src_id = 'h0;
reg src_id_reduced_msb = 1'b0;
reg [BURST_LEN_WIDTH_SRC-1:0] src_beat_counter = 'h00;
reg [ID_WIDTH-1:0] dest_id_next = 'h0;
reg dest_id_reduced_msb_next = 1'b0;
reg dest_id_reduced_msb = 1'b0;
reg [ID_WIDTH-1:0] dest_id = 'h0;
reg [BURST_LEN_WIDTH_DEST-1:0] dest_beat_counter = 'h00;
wire [BURST_LEN_WIDTH_DEST-1:0] dest_burst_len;
reg dest_valid = 1'b0;
reg dest_mem_data_valid = 1'b0;
reg dest_mem_data_last = 1'b0;
reg [DATA_WIDTH_MEM_DEST/8-1:0] dest_mem_data_strb = {DATA_WIDTH_MEM_DEST/8{1'b1}};
reg [BYTES_PER_BURST_WIDTH+1-1-DMA_LENGTH_ALIGN:0] burst_len_mem[0:AUX_FIFO_SIZE-1];
wire [BYTES_PER_BURST_WIDTH+1-1:0] src_burst_len_data;
reg [BYTES_PER_BURST_WIDTH+1-1:0] dest_burst_len_data = {DMA_LENGTH_ALIGN{1'b1}};
wire src_beat;
wire src_last_beat;
wire [ID_WIDTH-1:0] src_dest_id;
wire [ADDRESS_WIDTH_SRC-1:0] src_waddr;
wire [ID_WIDTH-2:0] src_id_reduced;
wire src_mem_data_valid;
wire src_mem_data_last;
wire [DATA_WIDTH_MEM_SRC-1:0] src_mem_data;
wire [BYTES_PER_BEAT_WIDTH_MEM_SRC-1:0] src_mem_data_valid_bytes;
wire src_mem_data_partial_burst;
wire dest_beat;
wire dest_last_beat;
wire dest_last;
wire [ID_WIDTH-1:0] dest_src_id;
wire [ADDRESS_WIDTH_DEST-1:0] dest_raddr;
wire [ID_WIDTH-2:0] dest_id_reduced_next;
wire [ID_WIDTH-1:0] dest_id_next_inc;
wire [ID_WIDTH-2:0] dest_id_reduced;
wire dest_burst_valid;
wire dest_burst_ready;
wire dest_ready;
wire [DATA_WIDTH_MEM_DEST-1:0] dest_mem_data;
wire dest_mem_data_ready;
`include "inc_id.vh"
generate if (ID_WIDTH >= 3) begin
assign src_id_reduced = {src_id_reduced_msb,src_id[ID_WIDTH-3:0]};
assign dest_id_reduced_next = {dest_id_reduced_msb_next,dest_id_next[ID_WIDTH-3:0]};
assign dest_id_reduced = {dest_id_reduced_msb,dest_id[ID_WIDTH-3:0]};
end else begin
assign src_id_reduced = src_id_reduced_msb;
assign dest_id_reduced_next = dest_id_reduced_msb_next;
assign dest_id_reduced = dest_id_reduced_msb;
end endgenerate
assign src_beat = src_mem_data_valid;
assign src_last_beat = src_beat & src_mem_data_last;
assign src_waddr = {src_id_reduced,src_beat_counter};
assign src_data_request_id = src_dest_id;
always @(*) begin
if (src_last_beat == 1'b1) begin
src_id_next <= inc_id(src_id);
end else begin
src_id_next <= src_id;
end
end
always @(posedge src_clk) begin
if (src_reset == 1'b1) begin
src_id <= 'h00;
src_id_reduced_msb <= 1'b0;
end else begin
src_id <= src_id_next;
src_id_reduced_msb <= ^src_id_next[ID_WIDTH-1-:2];
end
end
always @(posedge src_clk) begin
if (src_reset == 1'b1 || src_last_beat == 1'b1) begin
src_beat_counter <= 'h00;
end else if (src_beat == 1'b1) begin
src_beat_counter <= src_beat_counter + 1'b1;
end
end
always @(posedge src_clk) begin
if (src_last_beat == 1'b1) begin
burst_len_mem[src_id_reduced] <= src_burst_len_data[BYTES_PER_BURST_WIDTH:DMA_LENGTH_ALIGN];
end
end
assign dest_ready = ~dest_mem_data_valid | dest_mem_data_ready;
assign dest_last = dest_beat_counter == dest_burst_len;
assign dest_beat = dest_valid & dest_ready;
assign dest_last_beat = dest_last & dest_beat;
assign dest_raddr = {dest_id_reduced,dest_beat_counter};
assign dest_burst_valid = dest_data_request_id != dest_id_next;
assign dest_burst_ready = ~dest_valid | dest_last_beat;
/*
* The data valid signal for the destination side is asserted if there are one
* or more pending bursts. It is de-asserted if there are no more pending burst
* and it is the last beat of the current burst
*/
always @(posedge dest_clk) begin
if (dest_reset == 1'b1) begin
dest_valid <= 1'b0;
end else if (dest_burst_valid == 1'b1) begin
dest_valid <= 1'b1;
end else if (dest_last_beat == 1'b1) begin
dest_valid <= 1'b0;
end
end
/*
* The output register of the memory creates a extra clock cycle of latency on
* the data path. We need to handle this more the handshaking signals. If data
* is available in the memory it will be available one clock cycle later in the
* output register.
*/
always @(posedge dest_clk) begin
if (dest_reset == 1'b1) begin
dest_mem_data_valid <= 1'b0;
end else if (dest_valid == 1'b1) begin
dest_mem_data_valid <= 1'b1;
end else if (dest_mem_data_ready == 1'b1) begin
dest_mem_data_valid <= 1'b0;
end
end
/*
* This clears dest_data_last after the last beat. Strictly speaking this is not
* necessary if this followed AXI handshaking rules since dest_data_last would
* be qualified by dest_data_valid and it is OK to retain the previous value of
* dest_data_last when dest_data_valid is not asserted. But clearing the signal
* here doesn't cost much and can simplify some of the more congested
* combinatorical logic further up the pipeline since we can assume that
* fifo_last == 1'b1 implies fifo_valid == 1'b1.
*/
always @(posedge dest_clk) begin
if (dest_reset == 1'b1) begin
dest_mem_data_last <= 1'b0;
end else if (dest_beat == 1'b1) begin
dest_mem_data_last <= dest_last;
end else if (dest_mem_data_ready == 1'b1) begin
dest_mem_data_last <= 1'b0;
end
end
always @(posedge dest_clk) begin
if (dest_beat == 1'b1) begin
if (dest_last == 1'b1) begin
dest_mem_data_strb <= {DATA_WIDTH_MEM_DEST/8{1'b1}} >> ~dest_burst_len_data[BYTES_PER_BEAT_WIDTH_DEST-1:0];
end else begin
dest_mem_data_strb <= {DATA_WIDTH_MEM_DEST/8{1'b1}};
end
end
end
assign dest_id_next_inc = inc_id(dest_id_next);
always @(posedge dest_clk) begin
if (dest_reset == 1'b1) begin
dest_id_next <= 'h00;
dest_id_reduced_msb_next <= 1'b0;
end else if (dest_burst_valid == 1'b1 && dest_burst_ready == 1'b1) begin
dest_id_next <= dest_id_next_inc;
dest_id_reduced_msb_next <= ^dest_id_next_inc[ID_WIDTH-1-:2];
end
end
always @(posedge dest_clk) begin
if (dest_burst_valid == 1'b1 && dest_burst_ready == 1'b1) begin
dest_burst_len_data[BYTES_PER_BURST_WIDTH:DMA_LENGTH_ALIGN] <= burst_len_mem[dest_id_reduced_next];
end
end
always @(posedge dest_clk) begin
if (dest_burst_ready == 1'b1) begin
dest_id <= dest_id_next;
dest_id_reduced_msb <= dest_id_reduced_msb_next;
end
end
always @(posedge dest_clk) begin
if (dest_reset == 1'b1 || dest_last_beat == 1'b1) begin
dest_beat_counter <= 'h00;
end else if (dest_beat == 1'b1) begin
dest_beat_counter <= dest_beat_counter + 1'b1;
end
end
assign dest_burst_info_length = dest_burst_len_data[BYTES_PER_BURST_WIDTH-1:0];
assign dest_burst_info_partial = dest_burst_len_data[BYTES_PER_BURST_WIDTH];
assign dest_burst_info_id = dest_id;
always @(posedge dest_clk) begin
dest_burst_info_write <= (dest_burst_valid == 1'b1 && dest_burst_ready == 1'b1);
end
assign dest_burst_len = dest_burst_len_data[BYTES_PER_BURST_WIDTH-1 -: BURST_LEN_WIDTH_DEST];
axi_dmac_resize_src #(
.DATA_WIDTH_SRC (DATA_WIDTH_SRC),
.BYTES_PER_BEAT_WIDTH_SRC (BYTES_PER_BEAT_WIDTH_SRC),
.DATA_WIDTH_MEM (DATA_WIDTH_MEM_SRC),
.BYTES_PER_BEAT_WIDTH_MEM (BYTES_PER_BEAT_WIDTH_MEM_SRC)
) i_resize_src (
.clk (src_clk),
.reset (src_reset),
.src_data_valid (src_data_valid),
.src_data (src_data),
.src_data_last (src_data_last),
.src_data_valid_bytes (src_data_valid_bytes),
.src_data_partial_burst (src_data_partial_burst),
.mem_data_valid (src_mem_data_valid),
.mem_data (src_mem_data),
.mem_data_last (src_mem_data_last),
.mem_data_valid_bytes (src_mem_data_valid_bytes),
.mem_data_partial_burst (src_mem_data_partial_burst)
);
assign src_burst_len_data = {src_mem_data_partial_burst,
src_beat_counter,
src_mem_data_valid_bytes};
ad_mem_asym #(
.A_ADDRESS_WIDTH (ADDRESS_WIDTH_SRC),
.A_DATA_WIDTH (DATA_WIDTH_MEM_SRC),
.B_ADDRESS_WIDTH (ADDRESS_WIDTH_DEST),
.B_DATA_WIDTH (DATA_WIDTH_MEM_DEST)
) i_mem (
.clka (src_clk),
.wea (src_beat),
.addra (src_waddr),
.dina (src_mem_data),
.clkb (dest_clk),
.reb (dest_beat),
.addrb (dest_raddr),
.doutb (dest_mem_data)
);
axi_dmac_resize_dest #(
.DATA_WIDTH_DEST (DATA_WIDTH_DEST),
.DATA_WIDTH_MEM (DATA_WIDTH_MEM_DEST)
) i_resize_dest (
.clk (dest_clk),
.reset (dest_reset),
.mem_data_valid (dest_mem_data_valid),
.mem_data_ready (dest_mem_data_ready),
.mem_data (dest_mem_data),
.mem_data_last (dest_mem_data_last),
.mem_data_strb (dest_mem_data_strb),
.dest_data_valid (dest_data_valid),
.dest_data_ready (dest_data_ready),
.dest_data (dest_data),
.dest_data_last (dest_data_last),
.dest_data_strb (dest_data_strb)
);
sync_bits #(
.NUM_OF_BITS (ID_WIDTH),
.ASYNC_CLK (ASYNC_CLK)
) i_dest_sync_id (
.in_bits (src_id),
.out_clk (dest_clk),
.out_resetn (1'b1),
.out_bits (dest_src_id)
);
sync_bits #(
.NUM_OF_BITS (ID_WIDTH),
.ASYNC_CLK (ASYNC_CLK)
) i_src_sync_id (
.in_bits (dest_id),
.out_clk (src_clk),
.out_resetn (1'b1),
.out_bits (src_dest_id)
);
assign dest_request_id = dest_src_id;
assign dest_data_response_id = dest_id;
generate if (ENABLE_DIAGNOSTICS_IF == 1) begin
reg [ID_WIDTH-1:0] _dest_diag_level_bursts = 'h0;
// calculate buffer fullness in bursts
always @(posedge dest_clk) begin
if (dest_reset == 1'b1) begin
_dest_diag_level_bursts <= 'h0;
end else begin
_dest_diag_level_bursts <= g2b(dest_src_id) - g2b(dest_id);
end
end
assign dest_diag_level_bursts = {{{8-ID_WIDTH}{1'b0}},_dest_diag_level_bursts};
end else begin
assign dest_diag_level_bursts = 'h0;
end
endgenerate
endmodule