/*
 A 4-channel version of the CIC decimator. The idea is to combine
 differentiator sections, since these can be done sequentially.

 CIC decimator with adjustable decimation factor and parameterized input
 data width. Enable signal allows single-shot or CW operation. On a rising
 edge of enable, integration starts (from the next clock cycle). If enable
 is set when integration is completed (dec_trigger is issued), CIC continues
 running, providing a CW mode of operation.
 
 Decimation factor "decim" is 27-bit wide, actual decimation is "decim+2". 
 Useful range is up to 2^27 (set "decim" to 2^27-2). Minimum value depends
 on the clk/usbclk rates. For clk at 125 MHz and usbclk at 50 MHz, minimum
 decimation is 15 (set "decim" to 13). Since this module is intended for
 polled scalar data acquisition, typical decimation factors will be much
 longer.

 Copyright (c) 2008 Dimtel, Inc., All Rights Reserved

 $Id: cic4.v,v 1.1.1.1 2009/06/02 20:26:02 dim Exp $
 $Date: 2009/06/02 20:26:02 $
 $Author: dim $
 $Revision: 1.1.1.1 $
 $Log: cic4.v,v $
 Revision 1.1.1.1  2009/06/02 20:26:02  dim
 Initial import


*/

`timescale 1ns/100ps

module cic4 #(parameter iwidth=14) (
  input clk,			// Signal processing clock
  input [iwidth-1:0] in0,	// Channel 0
  input [iwidth-1:0] in1,	// Channel 1
  input [iwidth-1:0] in2,	// Channel 2
  input [iwidth-1:0] in3,	// Channel 3
  input [26:0] decim,		// Decimation factor (N_d-2)
  input enable,			// Enable
  
  input usbclk,			// Local bus clock
  output reg [iwidth+26:0] out0,	// Decimated output 0
  output reg [iwidth+26:0] out1,	// Decimated output 1
  output reg [iwidth+26:0] out2,	// Decimated output 2
  output reg [iwidth+26:0] out3,	// Decimated output 3
  output reg strobe		// Data update strobe
);

  parameter IDLE = 1'b1; parameter INTEGRATING = 1'b0;
  reg state = IDLE;
  reg [27:0] dec_cntr = 28'b0;
  reg [iwidth+26:0] integrator0 = 0, integrator1 = 0, integrator2 = 0, integrator3 = 0;
  reg [iwidth+26:0] delay0 = 0, delay1 = 0, delay2 = 0, delay3 = 0;
  reg [iwidth+26:0] delay4 = 0, delay5 = 0, delay6 = 0, delay7 = 0;
  reg [iwidth+26:0] delta = 0;
  reg [iwidth+26:0] out_acq0 = 0, out_acq1 = 0, out_acq2 = 0, out_acq3 = 0;
  reg [3:0] sft = 4'b0;
  reg [2:0] diff_cnt = 3'b0;
  wire dec_trigger;
  
  reg [2:0] sft_clk;

  initial begin
    strobe <= 1'b0;
    out0   <= 32'b0;
    out1   <= 32'b0;
    out2   <= 32'b0;
    out3   <= 32'b0;
  end

  /*
    Integrator state machine. We start in the IDLE state. If enable
    arrives, start integrating. When decimation is finished, we
    go back to idle state, unless enable is set.
  */
  always @(posedge clk) begin
    // CIC integrator
    integrator0 <= integrator0 + {{28{in0[iwidth-1]}}, in0};
    integrator1 <= integrator1 + {{28{in1[iwidth-1]}}, in1};
    integrator2 <= integrator2 + {{28{in2[iwidth-1]}}, in2};
    integrator3 <= integrator3 + {{28{in3[iwidth-1]}}, in3};
    case (state)
      IDLE: 
        if (enable) begin
          state    <= INTEGRATING;
          delay0   <= integrator0;	// Save the current value at start
          delay1   <= integrator1;
          delay2   <= integrator2;
          delay3   <= integrator3;
          dec_cntr <= decim;
        end
      INTEGRATING: begin
        if (dec_trigger) begin
          dec_cntr <= decim;
          if (~enable)
            state    <= IDLE;
        end
        else
          dec_cntr <= dec_cntr - 27'b1;
      end
    endcase
    // Differentiator pipeline
    if (dec_trigger) begin
      delay0 <= integrator0;
      delay1 <= integrator1;
      delay2 <= integrator2;
      delay3 <= integrator3;
      delay4 <= delay0;
      delay5 <= delay1;
      delay6 <= delay2;
      delay7 <= delay3;
      diff_cnt <= 3'b111;
    end

    // Serialized differentiation
    if (diff_cnt[2]) begin
      diff_cnt <= diff_cnt - 3'b1;
      delay1 <= delay0;
      delay2 <= delay1;
      delay3 <= delay2;
      delay4 <= delay3;
      delay5 <= delay4;
      delay6 <= delay5;
      delay7 <= delay6;
    end
    case (diff_cnt[1:0])
      2'b00: out_acq1 <= delta;
      2'b01: out_acq2 <= delta;
      2'b10: out_acq3 <= delta;
      2'b11: out_acq0 <= delta;
    endcase
    
    // Differentiation is done, shift out the results
    if (diff_cnt == 3'b011) begin 
      diff_cnt <= 3'b0;
      sft      <= 12'b11;
    end
    else
      sft <= {sft[2:0], 1'b0};
      
    // CIC differentiator: inputs serialized!
    delta <= delay0 - delay4;
  end
  
  // Decimation trigger signal
  assign dec_trigger = dec_cntr[27];

  // Transfer to the local bus clock domain
  always @(posedge usbclk) begin
    sft_clk <= {sft_clk[1:0], sft[3]};
    if (~sft_clk[2] & sft_clk[1]) begin // Rising edge
      out0 <= out_acq0;
      out1 <= out_acq1;
      out2 <= out_acq2;
      out3 <= out_acq3;
      strobe <= 1'b1;
    end
    if (strobe) strobe <= 1'b0;
  end
endmodule // cic
