/*! * Module:mult_saxi_wr_pointers * @file mult_saxi_wr_pointers.v * @date 2015-07-10 * @author Andrey Filippov * * @brief Process pointers for mult_saxi_wr * * @copyright Copyright (c) 2015 Elphel, Inc . * * License: * * mult_saxi_wr_pointers.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. * * mult_saxi_wr_pointers.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 mult_saxi_wr_pointers#( parameter MULT_SAXI_BSLOG0 = 4, // number of bits to represent burst size (4 - b.s. = 16, 0 - b.s = 1) parameter MULT_SAXI_BSLOG1 = 4, parameter MULT_SAXI_BSLOG2 = 4, parameter MULT_SAXI_BSLOG3 = 4 )( input mclk, // system clock input mrst, // input aclk, // global clock to run s_axi (@150MHz?) input arst, // input [3:0] chn_en_mclk, // enable this channle ( 0 - reset) input [29:0] sa_len_di, // input data to write pointers address/data input [ 2:0] sa_len_wa, // channel address to write sa/lengths input sa_len_we, // write enable sa/length data input irq_log_we, // write log2 of number of written dwords to generate interrupt input [ 1:0] chn, // selected channel number, valid with start input start, // start address generation/pointer increment output busy, // suspend new accesses (check latencies) // provide address and burst length for AXI @aclk, will stay until ackn output reg [29:0] axi_addr, output reg [3:0] axi_len, // write data to external pointer memory (to be read out by PIO) @ aclk // alternatively - read out directly from ptr_ram? output [29:0] pntr_wd, // @aclk output [1:0] pntr_wa, output pntr_we, output [3:0] irqs // @mclk, single-clock pulses to generate channel interrupts ); reg [3:0] chn_en_mclk_r; reg [3:0] chn_en_aclk; wire rst = !(|chn_en_mclk); // just for simulation wire rst_aclk = !(|chn_en_aclk); // just for simulation wire [3:0] chn_wr_mclk = {(sa_len_wa[2:1]==3),(sa_len_wa[2:1]==2),(sa_len_wa[2:1]==1),(sa_len_wa[2:1]==0)}; wire [3:0] rst_pntr_mclk = (chn_en_mclk & ~chn_en_mclk_r) | (sa_len_we ? chn_wr_mclk : 4'b0); wire [3:0] rst_pntr_aclk; wire start_resetting_w; reg [1:0] resetting; // resetting chunk_pointer and eof_pointer reg busy_r; 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 en_aclk = |chn_en_aclk; reg [1:0] chn_r; // registered channel being processed (or reset) reg [1:0] seq; // 1-hot sequence of address generation wire [29:0] sa_len_ram_out; reg [4:0] irq_log_r; wire [29:0] irq_log_decoded_w; reg gen_irq; reg [3:0] irqs_aclk; wire [29:0] ptr_ram_out; wire [2:0] sa_len_ra; reg ptr_we; // write to the pointer memory reg [29:0] ptr_inc; // incremented pointer reg [30:0] ptr_rollover; reg [4:0] burst_size; // ROM wire [29:0] ptr_wd; reg arst_d; //delayed version of arst assign reset_rq_enc = {reset_rq_pri[3] | reset_rq_pri[2], reset_rq_pri[3] | reset_rq_pri[1]}; assign start_resetting_w = en_aclk && !busy_r && !resetting[0] && (|reset_rq); assign busy = busy_r; //? assign ptr_wd = resetting[1] ? 30'b0 : (ptr_rollover[30]? ptr_inc : ptr_rollover[29:0]); assign pntr_wd = ptr_wd; assign pntr_we = ptr_we; assign pntr_wa = chn_r; assign sa_len_ra = {chn_r,seq[1]}; always @ (posedge mclk) begin chn_en_mclk_r <= chn_en_mclk; end // 8x30 RAM for address/length reg [29:0] sa_len_ram[0:7]; // start chunk/num cunks in a buffer (write port @mclk) // 4*5 RAM to store number of dword conters that need to chnage to generate interrupt reg [4:0] irq_log_ram[0:3]; always @ (posedge mclk) begin if (sa_len_we) sa_len_ram[sa_len_wa] <= sa_len_di; if (irq_log_we) irq_log_ram[sa_len_wa[1:0]] <= sa_len_di[4:0]; end assign sa_len_ram_out = sa_len_ram[sa_len_ra]; // 4 x 30 RAM for current pointers reg [29:0] ptr_ram[0:3]; // start chunk/num cunks in a buffer (write port @mclk) always @ (posedge aclk) begin if (ptr_we) ptr_ram[chn_r] <= ptr_wd; end assign ptr_ram_out = ptr_ram[chn_r]; always @ (posedge aclk) if (start) case (chn) // small ROM 'h0 : burst_size <= 1 << MULT_SAXI_BSLOG0; 'h1 : burst_size <= 1 << MULT_SAXI_BSLOG1; 'h2 : burst_size <= 1 << MULT_SAXI_BSLOG2; 'h3 : burst_size <= 1 << MULT_SAXI_BSLOG3; endcase always @ (posedge aclk) begin chn_en_aclk <= chn_en_mclk; arst_d <= arst; // probably not needed? if (arst) reset_rq <= ~0; // or ~0 ? Add auto reset after // else reset_rq <= {4{arst_d}} | rst_pntr_aclk | (reset_rq & ~({4{resetting[0] &~ resetting[1]}} & reset_rq_pri)); else reset_rq <= rst_pntr_aclk | (reset_rq & ~({4{resetting[0] &~ resetting[1]}} & reset_rq_pri)); 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 (rst_aclk) resetting <= 0; else resetting <= {resetting[0], start_resetting_w | (resetting[0] & ~resetting[1])}; if (rst_aclk) busy_r <= 0; else if (start_resetting_w || start) busy_r <= 1; else if (ptr_we) busy_r <= 0; if (rst_aclk) seq <= 0; else seq <= {seq[0],start}; if (resetting == 2'b1) chn_r[1:0] <= reset_rq_enc; // during reset pointers else if (start) chn_r[1:0] <= chn; // during normal address generation if (seq[0]) axi_addr <= sa_len_ram_out + ptr_ram_out; if (seq[0]) case (chn_r) // small ROM 'h0 : axi_len <= (1 << MULT_SAXI_BSLOG0) - 1; 'h1 : axi_len <= (1 << MULT_SAXI_BSLOG1) - 1; 'h2 : axi_len <= (1 << MULT_SAXI_BSLOG2) - 1; 'h3 : axi_len <= (1 << MULT_SAXI_BSLOG3) - 1; endcase if (seq[0]) ptr_inc <= ptr_ram_out + burst_size; if (seq[1]) ptr_rollover <= {1'b0, ptr_inc} -sa_len_ram_out; //sa_len_ram_out is now length ptr_we <= resetting[0] || seq[1]; if (seq[0]) irq_log_r <= irq_log_ram[chn_r]; gen_irq <= seq[1] && (|((ptr_inc ^ ptr_ram_out) & irq_log_decoded_w)); irqs_aclk <= gen_irq? {chn_r[1] & chn_r[0], chn_r[1] & ~chn_r[0], ~chn_r[1] & chn_r[0], ~chn_r[1] & ~chn_r[0]}:4'b0; // add one extra register layer here? end generate genvar i; for (i = 0; i < 30; i = i+1) begin: decoder_block assign irq_log_decoded_w[i] = i >= irq_log_r; end endgenerate pulse_cross_clock #(.EXTRA_DLY(1)) rst_pntr_aclk0_i (.rst(rst), .src_clk(mclk), .dst_clk(aclk), .in_pulse(rst_pntr_mclk[0]), .out_pulse(rst_pntr_aclk[0]),.busy()); pulse_cross_clock #(.EXTRA_DLY(1)) rst_pntr_aclk1_i (.rst(rst), .src_clk(mclk), .dst_clk(aclk), .in_pulse(rst_pntr_mclk[1]), .out_pulse(rst_pntr_aclk[1]),.busy()); pulse_cross_clock #(.EXTRA_DLY(1)) rst_pntr_aclk2_i (.rst(rst), .src_clk(mclk), .dst_clk(aclk), .in_pulse(rst_pntr_mclk[2]), .out_pulse(rst_pntr_aclk[2]),.busy()); pulse_cross_clock #(.EXTRA_DLY(1)) rst_pntr_aclk3_i (.rst(rst), .src_clk(mclk), .dst_clk(aclk), .in_pulse(rst_pntr_mclk[3]), .out_pulse(rst_pntr_aclk[3]),.busy()); pulse_cross_clock irqs0_i (.rst(rst_aclk), .src_clk(aclk), .dst_clk(mclk), .in_pulse(irqs_aclk[0]), .out_pulse(irqs[0]),.busy()); pulse_cross_clock irqs1_i (.rst(rst_aclk), .src_clk(aclk), .dst_clk(mclk), .in_pulse(irqs_aclk[1]), .out_pulse(irqs[1]),.busy()); pulse_cross_clock irqs2_i (.rst(rst_aclk), .src_clk(aclk), .dst_clk(mclk), .in_pulse(irqs_aclk[2]), .out_pulse(irqs[2]),.busy()); pulse_cross_clock irqs3_i (.rst(rst_aclk), .src_clk(aclk), .dst_clk(mclk), .in_pulse(irqs_aclk[3]), .out_pulse(irqs[3]),.busy()); endmodule