/*
 This is the top-level module for the LLRF board. Implements
 bus master and some local devices for testing.

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

 $Id: llrf.v,v 1.4 2012/11/12 10:39:19 dim Exp $
 $Date: 2012/11/12 10:39:19 $
 $Author: dim $
 $Revision: 1.4 $
 $Log: llrf.v,v $
 Revision 1.4  2012/11/12 10:39:19  dim
 Optimized for 8.64 ns

 Revision 1.3  2012/11/11 08:31:49  dim
 Optimized to meet 9 ns timing

 Revision 1.2  2012/11/11 06:03:31  dim
 Updates and fixes in 2009-2012 timeframe

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


*/

//`define RMS 1
`define IIR 1

module llrf(
  // FX2 interface
  input		usbclk_in,		// 48 MHz clock from IFCLK
  output	slrd,		// SLRD pin
  output	slwr,		// SLWR pin
  output	sloe,		// SLOE pin
  output	pktend,		// PKTEND pin
  input		flaga,
  input		flagb,
  input		rst,		// FLAGC used as reset
  output [1:0]	fifoadr,	// FIFOADR1 and FIFOADR0
  inout [15:0]	fd,		// 16-bit FIFO data
  
  // LEDs
  output [1:0]  led,
  
  // 6 bits on the 34-pin header, 2 outputs, 4 inputs
  inout [6:1]   GEEK_DIO,	// 6 - AD9512 sclk, 5 - AD9512 sdio
  
  // 4 bits on the 34-pin header
  (* IOB = "FALSE" *)
  output [4:1]	GEEK_LED,
  
  // 5V pin on the 34-pin header
  output	GEEK_PIN,
  
  // Signal-processing clock
  input		DSPCLK,
  
  // High-speed ADCs
  input [13:0]	IN3D,	input IN3OVR,	output IN3PD,
  input [13:0]	IN2D,	input IN2OVR,	output IN2PD,
  input [13:0]  IN1D,	input IN1OVR,	output IN1PD,
  input [13:0]  IN0D,	input IN0OVR,	output IN0PD,
  
  // High-speed DAC control
  output IF_SLEEP,
  
  // High-speed DAC outputs (DDR)
  output [13:0] ID,
  output [13:0] QD,
  
  // AD9512 control lines
  output DIV_CSB,	// AD9512 csb
  
  // AD5644 control lines
  output DAC_CLK,
  output [2:0] DAC_DIN,
  output DAC_CS,
  
  // MCP3208 interface
  output ADC_CLK,
  output ADC_SDI,
  input  ADC_SDO,
  output ADC_CS,
  
  // DS1822 interface
  inout  DALLAS,

  // LVDS interface
  output [7:0] LVDS_N,
  output [7:0] LVDS_P,

  // Trigger pins
  output TRIG1,
  output TRIG2
);
  // Local interconnect and registers
  wire usbclk;
  
  reg [24:0] counter = 25'b0;
  
  // Local bus interconnect
  wire [31:0] data_in, data_out, addr;
  wire strobe, rd, ack;
  
  wire [31:0] reg0data, block0data, block1data, ad9512data;
  wire [31:0] dcm0data, stat_data, scalardata, arbdata;
  wire [31:0] dds0data, dds1data, dds2data, iirdata, ad5644data, mcp3208data;
  wire [31:0] ds1822data, freqdata, fanmondata;
  wire [31:0] dds3data, dds4data;
  wire reg0ce, reg0ack, block0ce, block0ack, block1ce, block1ack;
  wire ad9512ce, ad9512ack, dacq_ce;
  wire dcm0ce, dcm0ack, stat_ce, stat_ack;
  wire scalarce, scalarack, arbce, arback, iirce, iirack, IQce;
  wire ad5644ce, ad5644ack, mcp3208ce, mcp208ack, ds1822ce, ds1822ack;
  wire freq_ce, fanmonce;
  wire freq_ack = strobe, fanmonack = strobe;
  wire [4:0] ddsce, ddsack;
  wire [3:0] rmsce; // rmsack not needed, use strobe
  wire [31:0] rms0data, rms1data, rms2data, rms3data;
  wire [31:0] IQdata;
  
  // Stretched strobe for the LED
  wire bus_active;
  
  // Interface reset resynchronization
  reg [1:0] rst_sync = 2'b0;
  
  // Clocking
  wire clkmiss, dcm_auto_rst, dcm_rst;
  wire psclk, psincdec, psdone, psen;
  wire clk_raw, clk, clk180, clk2, clk_lock, usbclk_lock;
  
  // Signal processing wires and registers
  (* IOB = "TRUE" *)
  reg [13:0] adcAu = 14'b0, adcBu = 14'b0, adcCu = 14'b0, adcDu = 14'b0;
  (* IOB = "TRUE" *)
  reg [3:0] adcOvr = 4'b0;
  wire [13:0] adcA, adcB, adcC, adcD;
  wire [27:0] dac_out;
  wire trig_sel;
  wire sat_iir;
  wire [1:0] dac0_sel, dac1_sel, iir_sel;
  
  // Clock receivers
  clk_ctrl clk_ctrl(.DSPCLK(DSPCLK),
                    .clk_rst(dcm_rst | dcm_auto_rst),
                    .clk_raw(clk_raw),
                    .clk(clk),
                    .clk180(clk180),
                    .clk2(clk2),
                    .clk_lock(clk_lock),
                    .psclk(psclk),
                    .psen(psen),
                    .psincdec(psincdec),
                    .psdone(psdone),
                    .usbclk_in(usbclk_in),
                    .usbclk(usbclk),
                    .usbclkfx(),
                    .usbclk_lock(usbclk_lock));

  always @(posedge usbclk)
    rst_sync <= {rst_sync[0], rst};
  
  fx2 fifo_ctrl(.clk(usbclk), .slrd(slrd), .slwr(slwr), .sloe(sloe),
                .pktend(pktend), .flag_ep2(flaga), .flag_ep6(flagb),
                .fifoadr(fifoadr), .fd(fd), .data_in(data_in),
                .data_out(data_out), .strobe(strobe), .rd(rd), .addr(addr),
                .ack(ack), .rst(rst_sync[1]));

  busmux32 busmux(.cebus({2'b0, fanmonce, freq_ce, ds1822ce, mcp3208ce,
                ad5644ce, iirce, 5'b0, IQce, ddsce, rmsce, arbce, scalarce,
                dcm0ce, stat_ce, ad9512ce, 1'b0, block1ce, block0ce,
                reg0ce}),

                .ackbus({2'b0, fanmonack, freq_ack, ds1822ack, mcp3208ack,
                 ad5644ack, iirack, {6{strobe}}, ddsack, {4{strobe}},
                 arback, scalarack, dcm0ack, stat_ack, ad9512ack, 1'b0,
                 block1ack, block0ack, reg0ack}),
                .strobe(strobe), .ack(ack),
                .d29(fanmondata), .d28(freqdata),
                .d27(ds1822data), .d26(mcp3208data), .d25(ad5644data),
                .d24(iirdata), .d18(IQdata),
                .d17(dds4data), .d16(dds3data),
                .d15(dds2data), .d14(dds1data), .d13(dds0data),
                .d12(rms3data), .d11(rms2data), .d10(rms1data), .d9(rms0data),
                .d8(arbdata),
                .d7(scalardata), .d6(dcm0data),
                .d5(stat_data), .d4(ad9512data), .d3(),
                .d2(block1data), .d1(block0data), .d0(reg0data),
                .data(data_out));
  addrdec decoder(.addr(addr),
                  .reg0ce(reg0ce),
                  .dacq_ce(dacq_ce),
                  .block0ce(block0ce),
                  .block1ce(block1ce),
                  .ad9512ce(ad9512ce),
                  .stat_ce(stat_ce),
                  .dcm0ce(dcm0ce),
                  .scalarce(scalarce),
                  .ddsce(ddsce),
                  .arbce(arbce),
                  .rmsce(rmsce),
                  .IQce(IQce),
                  .iirce(iirce),
                  .ad5644ce(ad5644ce),
                  .mcp3208ce(mcp3208ce),
                  .ds1822ce(ds1822ce),
                  .freq_ce(freq_ce),
                  .fanmonce(fanmonce));
                  
  // AD9512 serial port controller
  ad9512 ad9512(.clk(usbclk), .strobe(strobe), .ce(ad9512ce),
                .rd(rd), .ack(ad9512ack), .addr(addr[6:0]),
                .din(data_in[7:0]), .dout(ad9512data),
                .sclk(GEEK_DIO[6]), .sdio(GEEK_DIO[5]),
                .csb(DIV_CSB));

  // AD5644 controller
  ad5644 ad5644(.clk(usbclk), .strobe(strobe), .ce(ad5644ce),
                .rd(rd), .ack(ad5644ack), .addr(addr[3:0]),
                .din(data_in[13:0]), .dout(ad5644data),
                .sclk(DAC_CLK), .sdio(DAC_DIN), .cs_b(DAC_CS));

  // MCP3208 controller
  mcp3208 mcp3208(.clk(usbclk), .strobe(strobe), .ack(mcp3208ack),
                  .addr(addr[2:0]), .dout(mcp3208data), .sclk(ADC_CLK),
                  .sdo(ADC_SDO), .sdi(ADC_SDI), .cs(ADC_CS));

  // DS1822 interface
  wire dq_dir, dq_out;
  ds1822 ds1822(.clk(usbclk), .strobe(strobe), .ack(ds1822ack),
                  .addr(addr[1:0]), .dout(ds1822data), .dq_dir(dq_dir),
                  .dq_out(dq_out), .dq_in(DALLAS));
  assign DALLAS = dq_dir ? dq_out : 1'bz;
                  
  // Frequency counter
  freq_cnt freq_cnt(.clk(usbclk), .dout(freqdata), .dspclk(clk));
  
  // Fan monitor
  fanmon fanmon(.clk(usbclk), .addr(addr[0]), .dout(fanmondata),
                .fan(GEEK_DIO[2:1]));

  // CREG at 0x0: ADC/DAC power control, DCM reset
  creg reg0(.clk(usbclk), .strobe(strobe), .rd(rd), .ce(reg0ce),
            .ack(reg0ack), .din(data_in), .dout(reg0data));

  // Power control pins for ADCs and DACs, enable muxes
  assign IN0PD    = ~reg0data[0];
  assign IN1PD    = ~reg0data[1];
  assign IN2PD    = ~reg0data[2];
  assign IN3PD    = ~reg0data[3];
  assign IF_SLEEP = ~reg0data[4];
  assign dcm_rst  =  reg0data[5];
  assign trig_sel =  reg0data[6];
  assign dac0_sel =  reg0data[8:7];
  assign dac1_sel =  reg0data[10:9];
  assign iir_sel  =  reg0data[12:11];

  // Data acquisition trigger is from any access to register at 0x1
  assign acq_trig = dacq_ce & strobe;
  
  // Scalar CIC control register at 0x3
  creg scalarreg(.clk(usbclk), .strobe(strobe), .rd(rd), .ce(scalarce),
            .ack(scalarack), .din(data_in), .dout(scalardata));

  // Calibration DDS register at 0x4
  creg dds0(.clk(usbclk), .strobe(strobe), .rd(rd), .ce(ddsce[0]),
            .ack(ddsack[0]), .din(data_in), .dout(dds0data));
  // Calibration DDS register at 0x5
  creg dds1(.clk(usbclk), .strobe(strobe), .rd(rd), .ce(ddsce[1]),
            .ack(ddsack[1]), .din(data_in), .dout(dds1data));
  // Calibration DDS register at 0x6
  creg dds2(.clk(usbclk), .strobe(strobe), .rd(rd), .ce(ddsce[2]),
            .ack(ddsack[2]), .din(data_in), .dout(dds2data));
  // Reference DDS register at 0x7
  creg dds3(.clk(usbclk), .strobe(strobe), .rd(rd), .ce(ddsce[3]),
            .ack(ddsack[3]), .din(data_in), .dout(dds3data));
  // Reference DDS register at 0x8
  creg dds4(.clk(usbclk), .strobe(strobe), .rd(rd), .ce(ddsce[4]),
            .ack(ddsack[4]), .din(data_in), .dout(dds4data));

  // Status register
  sreg status(.clk(usbclk), .strobe(strobe), .ce(stat_ce), .rd(rd),
               .ack(stat_ack), .dout(stat_data),
               .bits({24'h0, adcOvr, sat_iir, ~usbclk_lock, 
                      ~clk_lock, clkmiss})
              );

  // DCM phase adjustment controller
  wire [8:0] psval;
  dcm_ctrl dcm_ctrl0(.clk(usbclk), .strobe(strobe), .ce(dcm0ce), .rd(rd),
                     .ack(dcm0ack), .din(data_in[8:0]), .dout(dcm0data),
                     .rst(dcm_rst | dcm_auto_rst), .locked(clk_lock),
                     .psclk(psclk), .psen(psen), .psincdec(psincdec),
                     .psdone(psdone), .psval(psval));
  
  // --------------- Signal processing ----------
  // Missing clock detector
  // Select divider bit 2 - compares clk/8 with usbclk
  // Range from 48*8/14 to 48*8/2 (27.4 to 192)
  // DAC clock is normally 2x
  clkdet #(.div_bit(2)) missdet(.clk(usbclk), 
                                .dspclk(clk_raw),
                                .clkmiss(clkmiss));
  
  // Automatic DMC reset on the falling edge of clkmiss
  dcm_rst rstgen(.clk(usbclk), .clkmiss(clkmiss), .rst(dcm_auto_rst));
  
  // Receive ADC data - separate MSBs to ensure IOB FF
  // synthesis by XST
  always @(posedge clk) begin
    adcAu  <= IN0D;
    adcBu  <= IN1D;
    adcCu  <= IN2D;
    adcDu  <= IN3D;
    adcOvr <= {IN3OVR, IN2OVR, IN1OVR, IN0OVR};
  end
  
  // Convert to two's complement
  assign adcA = IN0PD ? 14'b0 : {~adcAu[13], adcAu[12:0]};
  assign adcB = IN1PD ? 14'b0 : {~adcBu[13], adcBu[12:0]};
  assign adcC = IN2PD ? 14'b0 : {~adcCu[13], adcCu[12:0]};
  assign adcD = IN3PD ? 14'b0 : {~adcDu[13], adcDu[12:0]};

  wire acq_trig_pulse;
  reg [1:0] acq_trig1 = 2'b0;

  // Trigger sync
  reg rec_enable = 1'b0;
  reg [11:0] addr2 = 12'b0;
  
  detedge trig_det(.clk(clk), .in(acq_trig1[1]), .out(acq_trig_pulse));
  
  always @(posedge clk) begin
    acq_trig1 <= {acq_trig1[0], acq_trig};
    if (acq_trig_pulse) begin
      rec_enable <= 1'b1;
      addr2      <= 12'b0;
    end
    if (rec_enable)
      addr2 <= addr2 + 12'b1;
    if (addr2 == 12'hfff)
      rec_enable <= 1'b0;
  end

  wire [15:0] ch0, ch1, ch2, ch3;
  
  assign ch0= {{3{adcA[13]}}, adcA[12:0]};
  assign ch1= {{3{adcB[13]}}, adcB[12:0]};
//  assign ch0 = dds_sin;
//  assign ch1 = dds_cos;
  assign ch2= {{3{adcC[13]}}, adcC[12:0]};
  assign ch3= {{3{adcD[13]}}, adcD[12:0]};
  
  mem_rp4_inst rec0(.clk(usbclk), .strobe(strobe), .ce(block0ce),
                    .ack(block0ack), .rd(rd), .addr(addr[11:0]),
                    .din(data_in), .dout(block0data),
                    .clk2(clk), .ce2(rec_enable),
                    .addr2(addr2), .din2({ch1, ch0}));
  mem_rp4_inst rec1(.clk(usbclk), .strobe(strobe), .ce(block1ce),
                    .ack(block1ack), .rd(rd), .addr(addr[11:0]),
                    .din(data_in), .dout(block1data),
                    .clk2(clk), .ce2(rec_enable),
                    .addr2(addr2), .din2({ch3, ch2}));

  // RMS scalar detectors
  wire [26:0] sc_decim = scalardata[26:0];
  wire [3:0]  sc_shift = scalardata[31:27];

`ifdef RMS
  rms #(.iwidth(14)) rms0(.clk(clk), .in(adcA), .enable(1'b1),
        .usbclk(usbclk), .decim(sc_decim), .sft_in(sc_shift), .out(rms0data));
  rms #(.iwidth(14)) rms1(.clk(clk), .in(adcB), .enable(1'b1),
        .usbclk(usbclk), .decim(sc_decim), .sft_in(sc_shift), .out(rms1data));
  rms #(.iwidth(14)) rms2(.clk(clk), .in(adcC), .enable(1'b1),
        .usbclk(usbclk), .decim(sc_decim), .sft_in(sc_shift), .out(rms2data));
  rms #(.iwidth(14)) rms3(.clk(clk), .in(adcD), .enable(1'b1),
        .usbclk(usbclk), .decim(sc_decim), .sft_in(sc_shift), .out(rms3data));
`else
  assign rms0data = 32'b0;
  assign rms1data = 32'b0;
  assign rms2data = 32'b0;
  assign rms3data = 32'b0;
`endif

  // Calibration DDS register mapping  
  wire [16:0] cal_adv = dds0data[16:0];
  wire        square  = dds0data[17];
  wire [11:0] cal_mN  = dds0data[31:20]; // dds0: 0xMMMzS1PPPP
  wire [11:0] cal_rem = dds1data[11:0];
  wire [13:0] cal_amp = dds1data[29:16]; // dds1: 0xz3AAAzRRR
  wire [15:0] cal_I   = dds2data[15:0];
  wire [15:0] cal_Q   = dds2data[31:16]; // dds2: 0xQQQQIIII

  // Calibration DDS - generates DDR drive signals
  wire [13:0] ddr0, ddr1;
  dds_ddr cal_dds(.clk(clk), .square(square), .amplitude(cal_amp),
               .advance(cal_adv),
               .mN(cal_mN), .remainder(cal_rem),
               .vec_I(cal_I), .vec_Q(cal_Q),
               .ddr0(ddr0), .ddr1(ddr1));
               
  wire [27:0] dds_sig;
  assign dds_sig = {ddr0, ddr1};

  // Reference DDS register mapping  
  wire [16:0] ref_adv = dds3data[16:0];
  wire [11:0] ref_mN  = dds3data[31:20]; // dds0: 0xMMMzS1PPPP
  wire [11:0] ref_rem = dds4data[11:0];

  // Reference DDS
  wire [13:0] dds_sin, dds_cos;
  dds ref_dds(.clk(clk), .amplitude(14'd4970), .advance(ref_adv),
              .mN(ref_mN), .remainder(ref_rem), .sin(dds_sin), .cos(dds_cos));

  // Input channel monitoring: DDC+CIC
  ddc4cic ddc4(.clk(clk), .clk2(clk2), .in0(adcA), .in1(adcB), .in2(adcC), .in3(adcD),
               .sin(dds_sin), .cos(dds_cos), .decim(sc_decim),
               .usbclk(usbclk), .addr(addr[2:0]), .data(IQdata));

`ifdef IIR
  // IIR
  wire signed [13:0] iir_out;
  wire [13:0] iir_in = (iir_sel == 2'b00) ? adcA :
                       (iir_sel == 2'b01) ? adcB :
                       (iir_sel == 2'b10) ? adcC : adcD;
  iir iir(.clk(usbclk), .strobe(strobe), .ce(iirce), .rd(rd),
          .ack(iirack), .addr(addr[3:0]), .din(data_in), .dout(iirdata),
          .dspclk(clk), .in(iir_in), .setsel(1'b0), .sg(3'b0), .out(iir_out),
          .sat(sat_iir));
`else
  assign sat_iir = 1'b0;
  assign iir_out = 14'b0;
`endif
          
  // Arbitrary waveform generator
  drv_arb arb(.clk(usbclk), .strobe(strobe), .ce(arbce),
              .rd(rd), .ack(arback), .addr(addr[9:0]),
              .din(data_in), .dout(arbdata),
              .dspclk(clk), .dac_out(dac_out));

  // DAC output mux
  reg signed [13:0] iir_pipe;
  reg signed [14:0] iir_inter;
  reg [27:0] dac0wide, dac1wide;
  localparam DACSEL_IIR = 2'b00; localparam DACSEL_DDS = 2'b01;
  localparam DACSEL_ARB = 2'b10; localparam DACSEL_OFF = 2'b11;
  always @(posedge clk) begin
      iir_pipe <= iir_out;
      iir_inter <= iir_out + iir_pipe;
    case (dac0_sel)
      DACSEL_IIR: dac0wide <= {iir_inter[14:1], iir_pipe}; // ADC rate
      DACSEL_DDS: dac0wide <= dds_sig; // Full DAC rate
      DACSEL_ARB: dac0wide <= dac_out; // Full DAC rate
      DACSEL_OFF: dac0wide <= 28'b0;   // Shut off the DAC
    endcase
    case (dac1_sel)
      DACSEL_IIR: dac1wide <= {iir_inter[14:1], iir_pipe}; // ADC rate
      DACSEL_DDS: dac1wide <= dds_sig; // Full DAC rate
      DACSEL_ARB: dac1wide <= dac_out; // Full DAC rate
      DACSEL_OFF: dac1wide <= 28'b0;   // Shut off the DAC
    endcase
  end

  // DAC sections
  dac_ddr dac0(.dac(dac0wide), .dac_clk(clk), .dac_clk180(clk180),
               .dacout(ID));

  dac_ddr dac1(.dac(dac1wide), .dac_clk(clk), .dac_clk180(clk180),
               .dacout(QD));

  // --------------- Debugging I/O --------------
  always @(posedge usbclk)
    if (~rst)
      counter <= counter + 25'b1;
  
  // Stretch the strobe for bus_active
  stretch st_strobe(.clk(usbclk), .strobe(strobe), .bus_active(bus_active));
  
  assign led = {counter[24], bus_active};

  // LVDS tester
  reg [13:0] lvds_cnt = 14'b0;

  always @(posedge usbclk)
    lvds_cnt <= lvds_cnt + 14'b1;
  
  wire [7:0] lvds_out = lvds_cnt;
  wire [7:0] lvds_in;
  wire lvds_dir = reg0data[13];

  generate
  genvar i;
  for (i = 0; i < 8; i = i + 1) begin: lvds_  
    IOBUFDS obuf(.I(lvds_cnt[i]), .O(lvds_in[i]), .T(lvds_dir),
                 .IO(LVDS_P[i]), .IOB(LVDS_N[i]));
  end
  endgenerate

  assign TRIG2 = lvds_cnt[1];
  assign TRIG1 = lvds_cnt[2];
  
  assign GEEK_LED = 4'b0;
  assign GEEK_PIN = 1'b0;

endmodule // llrf
