/******************************************************************************* * Module: ahci_dma_rd_fifo * Date:2016-01-01 * Author: Andrey Filippov * Description: cross clocks, word-realign, 64->32 * Convertion from x64 QWORD-aligned AXI data @hclk to * 32-bit word-aligned data at mclk * * Copyright (c) 2016 Elphel, Inc . * ahci_dma_rd_fifo.v 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 3 of the License, or * (at your option) any later version. * * ahci_dma_rd_fifo.v 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 program. If not, see . * * Additional permission under GNU GPL version 3 section 7: * If you modify this Program, or any covered work, by linking or combining it * with independent modules provided by the FPGA vendor only (this permission * does not extend to any 3-rd party modules, "soft cores" or macros) under * different license terms solely for the purpose of generating binary "bitstream" * files and/or simulating the code, the copyright holders of this Program give * you the right to distribute the covered work without those independent modules * as long as the source code for them is available from the FPGA vendor free of * charge, and there is no dependence on any encrypted modules for simulating of * the combined code. This permission applies to you if the distributed code * contains all the components and scripts required to completely simulate it * with at least one of the Free Software programs. *******************************************************************************/ `timescale 1ns/1ps module ahci_dma_rd_fifo#( parameter WCNT_BITS = 21, parameter ADDRESS_BITS = 3 )( input mrst, input hrst, input mclk, input hclk, // hclk domain input [WCNT_BITS-1:0] wcnt, // decrementing word counter, 0- based (0 need 1, 1 - need 2, ...) valid @ start input [1:0] woffs, // 2 LSBs of the initial word address - valid @ start input start, // start transfer input [63:0] din, input din_av, input din_av_many, input last_prd, // last prd, flush partial dword if there were odd number of words transferred. valid @ start // Or maybe use "last_prd"? output din_re, output reg done, // this PRD data sent to cross-clock FIFO (may result in only half-dword sent out), // OK to fetch another PRD (if it was not the last) output done_flush, // finished last PRD (indicated by last_prd @ start), data left module // mclk domain output [31:0] dout, output dout_vld, input dout_re ); localparam ADDRESS_NUM = (1<>1)-1:0],fifo_full[ADDRESS_NUM-1: ADDRESS_NUM>>1]}; reg fifo_dav; // @mclk reg fifo_dav2; // @mclk reg fifo_half_hclk; // Half Fifo is empty, OK to write reg [1:0] woffs_r; wire [63:0] fifo_di= woffs_r[1]?(woffs_r[0] ? {din[47:0],din_prev[63:48]} : {din[31:0],din_prev[63:32]}): (woffs_r[0] ? {din[15:0],din_prev[63:16]} : din[63:0]); wire [3:0] fifo_di_vld; wire [1:0] fifo_di_flush; // Assign wire [63:0] fifo_do = fifo_ram [raddr[ADDRESS_BITS:1]]; wire [3:0] fifo_do_vld = vld_ram [raddr[ADDRESS_BITS:1]]; wire [1:0] fifo_do_flush = flush_ram[raddr[ADDRESS_BITS:1]]; reg din_av_safe_r; reg en_fifo_wr; reg [3:0] last_mask; reg flush_r; wire done_flush_mclk; assign din_re = busy && fifo_half_hclk && din_av_safe_r; assign fifo_wr = en_fifo_wr && fifo_half_hclk && (din_av_safe_r || !busy); assign fifo_di_vld = (busy && (!extra_in || (qwcntr != 0)))? 4'hf : last_mask ; assign fifo_di_flush = ((busy && (!extra_in || (qwcntr != 0))) || !flush_r)? 2'h0 : {|last_mask[3:2], ~(|last_mask[3:2])} ; always @ (posedge hclk) begin if (hrst) busy <= 0; else if (start) busy <= 1; else if (din_re && (qwcntr == 0)) busy <= 0; done <= busy && din_re && (qwcntr == 0); if (hrst) en_fifo_wr <= 0; else if (start) en_fifo_wr <= (wcnt[1:0] == 0); else if (din_re || fifo_wr) en_fifo_wr <= busy && ((qwcntr != 0) || extra_in); if (start) qwcntr <= wcnt[WCNT_BITS-1:2]; else if (din_re) qwcntr <= qwcntr - 1; if (start) extra_in <= end_offs[2]; if (start) woffs_r <= woffs; if (hrst) fifo_full <= 0; else if (fifo_wr) fifo_full <= {fifo_full[ADDRESS_NUM-2:0],waddr[ADDRESS_BITS]}; if (hrst) waddr <= 0; else if (fifo_wr) waddr <= waddr+1; fifo_half_hclk <= fifo_nempty [waddr[ADDRESS_BITS-1:0]] ^ waddr[ADDRESS_BITS]; if (din_re) din_prev[63:16] <= din[63:16]; if (fifo_wr) fifo_ram[waddr[ADDRESS_BITS-1:0]] <= fifo_di; if (fifo_wr) vld_ram [waddr[ADDRESS_BITS-1:0]] <= fifo_di_vld; if (fifo_wr) flush_ram[waddr[ADDRESS_BITS-1:0]] <= fifo_di_flush; if (hrst) din_av_safe_r <= 0; else din_av_safe_r <= din_av && (din_av_many || !din_re); if (start) last_mask <= {&wcnt, wcnt[1], |wcnt, 1'b1}; if (start) flush_r <= last_prd; end always @ (posedge mclk) begin hrst_mclk <= hrst; if (hrst_mclk) raddr <= 0; else if (fifo_rd) raddr <= raddr + 1; if (hrst_mclk) fifo_nempty <= {{(ADDRESS_NUM>>1){1'b0}},{(ADDRESS_NUM>>1){1'b1}}};// 8'b00001111 else if (fifo_rd && raddr[0]) fifo_nempty <= {fifo_nempty[ADDRESS_NUM-2:0],raddr[ADDRESS_BITS+1] ^ raddr[ADDRESS_BITS]}; fifo_dav <= fifo_full [raddr[ADDRESS_BITS:1]] ^ raddr[ADDRESS_BITS+1]; fifo_dav2 <= fifo_full2[raddr[ADDRESS_BITS:1]]; end ahci_dma_rd_stuff ahci_dma_rd_stuff_i ( .rst (mrst), // input .clk (mclk), // input .din_av (fifo_dav), // input .din_avm (fifo_dav2), // input .flush (raddr[0]?fifo_do_flush[1]:fifo_do_flush[0]), // input .din (raddr[0]?fifo_do[63:32]: fifo_do[31:0]), // input[31:0] .dm (raddr[0]?fifo_do_vld[3:2]:fifo_do_vld[1:0]), // input[1:0] .din_re (fifo_rd), // output .flushed (done_flush_mclk), // output reg: flush (end of last PRD is finished - data left module) .dout (dout), // output[31:0] reg .dout_vld (dout_vld), // output .dout_re (dout_re) // input ); pulse_cross_clock #( .EXTRA_DLY(0) ) done_flush_i ( .rst (mrst), // input .src_clk (mclk), // input .dst_clk (hclk), // input .in_pulse (done_flush_mclk), // input .out_pulse (done_flush), // output .busy() // output ); endmodule