Archived
initial commit
This commit is contained in:
@@ -0,0 +1,458 @@
|
||||
// ***************************************************************************
|
||||
// ***************************************************************************
|
||||
// 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
|
||||
Reference in New Issue
Block a user