/*
 This module implements the basic control registers. One 32-bit wide
 location, zero wait-state read and write (creg). It also implements
 a two-register bank used for filter coefficients.

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

 $Id: creg.v,v 1.4 2013/09/05 03:19:20 dim Exp $
 $Date: 2013/09/05 03:19:20 $
 $Author: dim $
 $Revision: 1.4 $
 $Log: creg.v,v $
 Revision 1.4  2013/09/05 03:19:20  dim
 Redesigned cfregsync to respond to local bus even with DSP clock is absent

 Revision 1.3  2012/09/25 20:03:32  dim
 Added synchronous write coefficient register (LLRF), parametrized width

 Revision 1.2  2009/08/29 19:54:11  dim
 Added timescale

 Revision 1.1.1.1  2009/02/05 07:20:51  dim
 Initial import

 Revision 1.1  2007/03/14 08:37:48  dim
 Gateware revision 1.01: first release of full iGp functionality


*/

`timescale 1ns/100ps

`include "bus_width.h"

module creg(
  input		clk,
  input		strobe,
  input		rd,
  input		ce,
  output	ack,
  input [`IFACE-1:0] din,
  output reg [`IFACE-1:0] dout
);
  initial begin
    dout <= `IFACE'b0;
  end
  
  assign ack = strobe;
  always @(posedge clk)
    if (strobe & ce & ~rd)
      dout <= din;
endmodule // creg

// Coefficient registers: dual
module cfreg2 (
	clk,	// Bus clock
	strobe,	// Bus strobe
	ce,	// Module select
	rd,	// Read/write direction
	ack,	// Bus ack
        addr,   // Address bit
	din,	// Input data bus
	dout,	// Output data bus
	setsel,	// Coefficient set select
	cout	// Coefficient output
	);
  parameter width=16;

  // Inputs
  input clk, strobe, ce, rd, addr, setsel;
  input [`IFACE-1:0] din;

  // Outputs
  output ack;
  output signed [`IFACE-1:0] dout;
  
  output [width-1:0] cout;
  
  // Registers
  reg ack = 1'b0;
  reg signed [width-1:0] reg1 = {width{1'b0}}, reg2 = {width{1'b0}};

  always @(posedge clk) begin
    if (strobe & ce & ~ack)
      if (~rd) begin
        if (addr)
          reg2 <= din[width-1:0];
        else
          reg1 <= din[width-1:0];
        ack <= 1'b1;
      end
      else
        ack <= 1'b1;
    if (ack) ack <= 1'b0;
  end
  
  assign dout = (addr) ? reg2 : reg1;
  
  assign cout = (setsel) ? reg2 : reg1;
endmodule // cfreg2

// Coefficient registers: dual, with synchronous write
// This register design hangs the bus if dspclk is not present. Unacceptable in
// a real system.
module cfreg2sync0 (
	clk,	// Bus clock
	strobe,	// Bus strobe
	ce,	// Module select
	rd,	// Read/write direction
	ack,	// Bus ack
        addr,   // Address bit
	din,	// Input data bus
	dout,	// Output data bus
	setsel,	// Coefficient set select
	dspclk, // Processing clock for synchronous writes
	cout	// Coefficient output
	);
  parameter width=16;

  // Inputs
  input clk, strobe, ce, rd, addr, setsel, dspclk;
  input [`IFACE-1:0] din;

  // Outputs
  output ack;
  output signed [`IFACE-1:0] dout;
  
  output [width-1:0] cout;
  
  // Registers
  reg ack = 1'b0;
  reg [2:0] write = 3'b0;
  reg signed [width-1:0] coeff[1:0];
  reg [width-1:0] cout = {width{1'b0}};
  reg [width-1:0] din_sync = {width{1'b0}};
  
  initial begin
    coeff[0] <= {width{1'b0}};
    coeff[1] <= {width{1'b0}};
  end

  always @(posedge clk) begin
    if (strobe & ce & ~ack & ~write[0])
      if (~rd) begin
        write[0] <= 1'b1;
        din_sync <= din[width-1:0];
      end
      else
        ack <= 1'b1;
    if (write[2] & write[0]) begin
      write[0] <= 1'b0;
      ack <= 1'b1;
    end
    if (ack) ack <= 1'b0;
  end
  
  always @(posedge dspclk) begin
    write[2:1] <= write[1:0];
    if (write[1] & ~write[2])
      coeff[addr] <= din_sync;
    
    cout <= coeff[setsel];
  end

  assign dout = coeff[addr];
endmodule // cfreg2sync0

// Coefficient registers: dual, with synchronous write
// Local bus registers are written and read independently from the DSP clock. Write
// to a registers sets "dirty" bit, which is cleared when the data is written in the
// DSP clock domain.
module cfreg2sync #(parameter width=16) (
  input		clk,	// Bus clock
  input		strobe,	// Bus strobe
  input		ce,	// Module select
  input		rd,	// Read/write direction
  output reg	ack,	// Bus ack
  input		addr,   // Address bit
  input [31:0]	din,	// Input data bus
  output signed [31:0]	dout,	// Output data bus
  input		setsel,	// Coefficient set select
  input		dspclk, // Processing clock for synchronous writes
  output reg [width-1:0] cout // Coefficient output
);
  // Registers
  reg [2:0] dirty[1:0];
  reg signed [width-1:0] coeff[1:0], coeff_dsp[1:0];
  
  initial begin
    coeff[0] <= {width{1'b0}};
    coeff[1] <= {width{1'b0}};
    coeff_dsp[0] <= {width{1'b0}};
    coeff_dsp[1] <= {width{1'b0}};
    dirty[0] <= 3'b0;
    dirty[1] <= 3'b0;
    cout     <= {width{1'b0}};
    ack      <= 1'b0;
  end

  always @(posedge clk) begin
    if (strobe & ce & ~ack)
      if (~rd) begin
        dirty[addr][0] <= 1'b1;
        coeff[addr]    <= din[width-1:0];
        ack            <= 1'b1;
      end
      else
        ack <= 1'b1;
    if (ack) ack <= 1'b0;
    
    // MSB set - data has been copied, reset LSB */
    if (dirty[0][2]) dirty[0][0] <= 1'b0;
    if (dirty[1][2]) dirty[1][0] <= 1'b0;
  end
  
  always @(posedge dspclk) begin
    dirty[0][2:1] <= dirty[0][1:0];
    dirty[1][2:1] <= dirty[1][1:0];
    if (~dirty[0][2] & dirty[0][1])
      coeff_dsp[0] <= coeff[0];
    if (~dirty[1][2] & dirty[1][1])
      coeff_dsp[1] <= coeff[1];
    
    cout <= coeff_dsp[setsel];
  end

  assign dout = coeff[addr];
endmodule // cfreg2sync
