/******************************************************************************* * Module: cmprs_afi_mux_ptr * Date:2015-06-28 * Author: Andrey Filippov * Description: Maintain 4-channel chunk pointers (before AXI) * Advance 32-byte chunk pointers for each AXI burst and each frame (4*2=8 pointers) * * Copyright (c) 2015 Elphel, Inc. * cmprs_afi_mux_ptr.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. * * cmprs_afi_mux_ptr.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 cmprs_afi_mux_ptr( input hclk, // global clock to run axi_hp @ 150MHz, shared by all compressor channels // Write dual port 8x27 channel start address/length RAM input [26:0] sa_len_di, // data to write per-channle buffer sa/length in chunks input [ 2:0] sa_len_wa, // channel address to write sa/lengths input sa_len_we, // write enable sa/length data input en, // 0 - resets, 0->1 resets all pointers. While reset allows write response input [ 3:0] reset_pointers, // per-channel - reset pointers input pre_busy_w, // combinatorial signal - one before busy[0] (depends on ptr_resetting) input [ 1:0] winner_channel, // channel that won arbitration for AXI access, valid @ pre_busy_w input need_to_bother, // whants to start access if address and data FIFO permit input [1:0] chunk_inc_want_m1, // how much to increment chunk pointer (0..3) +1 - valid with busy[0] (w/o rollover) input last_burst_in_frame, // valid with busy[0] (last_burst_in_frame<=last_chunk_w[winner2]) input [ 3:0] busy, // one cycle less than sending 1-4 bursts, [1] - delayed by 1, [2] - by 2 output ptr_resetting, // pointers will be reset next cycle (2-cycle-long pulse) output reg [26:0] chunk_addr, // chunk absolute address, valid with busy[1] input [ 2:0] chunk_ptr_ra, // chunk pointer read address {eof, chn[1:0]} output [26:0] chunk_ptr_rd, // chunk pointer read data (non-registered // output [ 2:0] max_inc // maximal increment to rollover (limited by 4) output [ 2:0] max_wlen // maximal wlen[3:2], MSB - limited by rollover ); reg [3:0] reset_rq; // request to reset pointers when ready reg [3:0] reset_rq_pri; // one-hot reset rq wire [1:0] reset_rq_enc; // encoded reset_rq_pri wire start_resetting_w; reg [1:0] resetting; // resetting chunk_pointer and eof_pointer reg [2:0] ptr_wa; // pointer memory write port address, msb - eof/current, 2 LSB - channel reg ptr_we; // pointer memory write enable reg [26:0] ptr_ram[0:7]; // pointer (current and eof) memory (in 32-byte chunks wire [26:0] ptr_ram_di; // data to be written to ptr_ram reg [26:0] sa_len_ram[0:7]; // start chunk/num cunks in a buffer (write port @mclk) reg [26:0] chunk_ptr_inc; // incremented by 1..4 chunk pointer // reg [27:0] rollover_r; // incremented chunk pointer, decremented by length (MSB - sign) reg en_d; //enable delayed by 1 cycle wire [ 2:0] sa_len_ra; // start/len read address (0..3 - start addresses, 4..7 - lengths) reg [ 2:0] max_inc_ram[0:3]; // maximal increment to rollover (limited by 4) wire [ 1:0] pre_chunk_inc_m1; reg [ 2:0] chunk_inc; wire [26:0] chunks_to_rollover; reg [3:0] chunks_to_rollover_r; // [3] >=8 wire [3:0] chunks_to_rollover_m1; reg max_inc_ram_we; reg [1:0] max_inc_ram_wa; wire rollover_w; // this cycle causes rollover - valid at pre_busy_w reg rollover_r; // this cycle causes rollover - valid at busy[0] and late wire ptr_ram_wa = ptr_ram[ptr_wa]; // SuppressThisWarning VEditor debug - just to view assign ptr_resetting = resetting[0]; assign sa_len_ra= {busy[1],ptr_wa[1:0]}; assign reset_rq_enc = {reset_rq_pri[3] | reset_rq_pri[2], reset_rq_pri[3] | reset_rq_pri[1]}; // assign ptr_ram_di= resetting[1] ? 27'b0 : (chunk_ptr_rovr[27] ? chunk_ptr_inc : chunk_ptr_rovr[26:0]); assign ptr_ram_di= (resetting[1] ||rollover_r) ? 27'b0 : chunk_ptr_inc ; assign chunk_ptr_rd = ptr_ram[chunk_ptr_ra]; assign start_resetting_w = en && !busy[0] && !resetting[0] && (|reset_rq) && !need_to_bother; // assign max_inc = max_inc_ram[winner_channel]; assign max_wlen = max_inc_ram[winner_channel]; // valid @pre_busy_w //chunk_inc_want_m1 assign pre_chunk_inc_m1 = (max_wlen[1:0] >= chunk_inc_want_m1)? chunk_inc_want_m1 : max_wlen[1:0]; assign rollover_w = !max_wlen[2] && (max_wlen[1:0] <= chunk_inc_want_m1); assign chunks_to_rollover = sa_len_ram[sa_len_ra] - ptr_ram_di; assign chunks_to_rollover_m1 = chunks_to_rollover_r -1; always @ (posedge hclk) begin en_d <= en; // ===== calculate and rollover channel addresses ==== // clear (during "resetting" or update 8x27 RAM that holds chunk pointers for the current burst and currenty frame // uses (reads) additional memory with per-channel start addresses and lengths (both measured in 32-byte chunks // read port of the ptr_ram will be used to monitor pointers through the status network // TODO: Make sure correct RAM modules are inferred // write length RAM if (sa_len_we) sa_len_ram[sa_len_wa] <= sa_len_di; if (start_resetting_w) reset_rq_pri <= {reset_rq[3] & ~(|reset_rq[2:0]), reset_rq[2] & ~(|reset_rq[1:0]), reset_rq[1] & ~reset_rq[0], reset_rq[0]}; if (en && !en_d) reset_rq <= 4'hf; else reset_rq <= reset_pointers | (reset_rq & ~({4{resetting[0] &~ resetting[1]}} & reset_rq_pri)); if (!en) resetting <= 0; else resetting <= {resetting[0], start_resetting_w | (resetting[0] & ~resetting[1])}; if (resetting == 2'b1) ptr_wa[1:0] <= reset_rq_enc; else if (pre_busy_w) ptr_wa[1:0] <= winner_channel; if (start_resetting_w || pre_busy_w) ptr_wa[2] <= 0; else if (ptr_we) ptr_wa[2] <= 1; ptr_we <= resetting[0] || // a pair of cycles to reset chunk pointer and frame chunk pointer (busy[1] && !busy[2]) || // always update chunk pointer (busy[2] && !busy[3] && last_burst_in_frame); // optionally update frame chunk pointer (same value) if (busy[0] && !busy[1]) begin // first clock of busy // calculate full address for current AXI burst (valid 1 clk after busy) chunk_addr <= ptr_ram[ptr_wa] + sa_len_ram[sa_len_ra]; chunk_ptr_inc <= ptr_ram[ptr_wa] + chunk_inc; end // if (busy[1] && !busy[2]) begin // first clock of busy // chunk_ptr_rovr <={1'b0,chunk_ptr_inc} - {1'b0,sa_len_ram[sa_len_ra]}; // sa_len_ra now points at length // end // write to ptr_ram (1 or 2 locations - if eof) if (ptr_we) ptr_ram[ptr_wa] <= ptr_ram_di; if (pre_busy_w) chunk_inc <= {1'b0, pre_chunk_inc_m1} + 1; if (pre_busy_w) rollover_r <= rollover_w; // wire [26:0] chunks_to_rollover; // reg [3:0] chunks_to_rollover_r; // [3] >=8 if (ptr_we) chunks_to_rollover_r <= {|chunks_to_rollover[26:3],chunks_to_rollover[2:0]}; max_inc_ram_we <= ptr_we & ~ptr_wa[2]; max_inc_ram_wa <= ptr_wa[1:0]; if (max_inc_ram_we) max_inc_ram[max_inc_ram_wa] <= (|chunks_to_rollover_m1[3:2])?3'h7:{1'b0,chunks_to_rollover_m1[1:0]}; end endmodule