/*
 Downconverter module

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

 $Id: ddc.v,v 1.2 2012/11/11 06:03:30 dim Exp $
 $Date: 2012/11/11 06:03:30 $
 $Author: dim $
 $Revision: 1.2 $
 $Log: ddc.v,v $
 Revision 1.2  2012/11/11 06:03:30  dim
 Updates and fixes in 2009-2012 timeframe

 Revision 1.1.1.1  2009/06/02 20:26:02  dim
 Initial import


*/

`timescale 1ns/100ps

module ddc (
  input clk,			// Signal processing clock
  input signed [13:0] in,	// Waveform input
  
  input signed [13:0] sin,	// Reference input: sine
  input signed [13:0] cos,	// Reference input: cosine
  
  output reg signed [15:0] i,	// I component
  output reg signed [15:0] q	// Q component
);
  reg signed [13:0] sin_old, cos_old;
  reg signed [13:0] in_old;
  reg signed [14:0] nodc[1:0];
  reg signed [28:0] prod_11, prod_12, prod_21, prod_22;

  // [ I ]   [  sin -sin_old ] [ in_old ]
  // |   | = |               | |        |
  // [ Q ]   [ -cos  cos_old ] [   in   ]
  always @(posedge clk) begin
    // DC removal filter
    in_old  <= in;
    nodc[0] <= in - in_old;
    nodc[1] <= nodc[0];
    sin_old <= sin;
    cos_old <= cos;
    prod_11 <= sin * nodc[1];
    prod_12 <= sin_old * nodc[0];
    prod_21 <= cos * nodc[1];
    prod_22 <= cos_old * nodc[0];
  end

  always @(posedge clk) begin
    i <= {prod_11[28], prod_11[28:14]} -  {prod_12[28], prod_12[28:14]};
    q <= {prod_22[28], prod_22[28:14]} -  {prod_21[28], prod_21[28:14]};
  end
endmodule // ddc

// Four-channel DDC with CIC filtering and address decoding
module ddc4cic (
  input               clk,	// Signal processing clock
  input               clk2,	// Half-rate clock for CIC
  input signed [13:0] in0,	// Waveform input
  input signed [13:0] in1,	// Waveform input
  input signed [13:0] in2,	// Waveform input
  input signed [13:0] in3,	// Waveform input
  
  input signed [13:0] sin,	// Reference input: sine
  input signed [13:0] cos,	// Reference input: cosine

  input [26:0]        decim,	// Decimation factor
  
  input		      usbclk,   // Local bus clock
  input [2:0] 	      addr,	// Address
  output reg   [31:0] data	// Readback data
);
  reg signed [13:0] in0p, in1p, in2p, in3p;
  wire [15:0] I[3:0], Q[3:0];
  wire [42:0] cici[3:0], cicq[3:0];

  always @(posedge clk) begin
    in0p <= in0;
    in1p <= in1;
    in2p <= in2;
    in3p <= in3;
  end

  ddc ddc0(.clk(clk), .in(in0p), .sin(sin), .cos(cos), .i(I[0]), .q(Q[0]));
  ddc ddc1(.clk(clk), .in(in1p), .sin(sin), .cos(cos), .i(I[1]), .q(Q[1]));
  ddc ddc2(.clk(clk), .in(in2p), .sin(sin), .cos(cos), .i(I[2]), .q(Q[2]));
  ddc ddc3(.clk(clk), .in(in3p), .sin(sin), .cos(cos), .i(I[3]), .q(Q[3]));

  // CIC filters
  cic #(.iwidth(16)) cic0i(.clk(clk), .in(I[0]), .decim(decim),
        .enable(1'b1), .usbclk(usbclk), .out(cici[0]));
  cic #(.iwidth(16)) cic1i(.clk(clk), .in(I[1]), .decim(decim),
        .enable(1'b1), .usbclk(usbclk), .out(cici[1]));
  cic #(.iwidth(16)) cic2i(.clk(clk), .in(I[2]), .decim(decim),
        .enable(1'b1), .usbclk(usbclk), .out(cici[2]));
  cic #(.iwidth(16)) cic3i(.clk(clk), .in(I[3]), .decim(decim),
        .enable(1'b1), .usbclk(usbclk), .out(cici[3]));
  cic #(.iwidth(16)) cic0q(.clk(clk), .in(Q[0]), .decim(decim),
        .enable(1'b1), .usbclk(usbclk), .out(cicq[0]));
  cic #(.iwidth(16)) cic1q(.clk(clk), .in(Q[1]), .decim(decim),
        .enable(1'b1), .usbclk(usbclk), .out(cicq[1]));
  cic #(.iwidth(16)) cic2q(.clk(clk), .in(Q[2]), .decim(decim),
        .enable(1'b1), .usbclk(usbclk), .out(cicq[2]));
  cic #(.iwidth(16)) cic3q(.clk(clk), .in(Q[3]), .decim(decim),
        .enable(1'b1), .usbclk(usbclk), .out(cicq[3]));

  always @(addr or cici[0] or cici[1] or cici[2] or cici[3] or
                   cicq[0] or cicq[1] or cicq[2] or cicq[3])
    case (addr)
      3'b000: data = cici[0][42:11];
      3'b001: data = cicq[0][42:11];
      3'b010: data = cici[1][42:11];
      3'b011: data = cicq[1][42:11];
      3'b100: data = cici[2][42:11];
      3'b101: data = cicq[2][42:11];
      3'b110: data = cici[3][42:11];
      3'b111: data = cicq[3][42:11];
    endcase

endmodule // ddc4
