/* Baseband FIR filter block
* Steve Harwood, PSU EE572, Fall 1999
*
* Here the FIR RAM address is generated, based on whether 
* we are in program mode or not. 
* The two 6-operand accumulators are instantiated, as are 
* the two accumulator pipeline registers, and these partial
* sums are added together in the final summation.  The final summation
* is large enough (by default) so that no overflow will occur.
*/

module bbfilt(clk,rst,idata,coeff,
	     we,filt_out,
	     acsel,acld,acclk,ac_reg_en,
	     hen,fstate, paddr, prog);

 input clk, rst;
 input [1:0] acsel,fstate;
 input acld,acclk,ac_reg_en;
 input [11:0] idata, coeff,hen, we;
 input [3:0] paddr;
 input prog;

 parameter ACC_WIDTH = 17;
 parameter OUT_WIDTH = 18;

 output [OUT_WIDTH-1:0] filt_out;
 
 reg [ACC_WIDTH-1:0] actop_reg,acbot_reg;
 wire [ACC_WIDTH-1:0] actop,acbot;

 reg [11:0] pa[5:0];
 reg [11:0] pb[5:0];
 wire [11:0] t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11;
 wire [3:0] addr0, addr1, addr2, addr3, addr4, addr5;
 wire [3:0] addr6, addr7, addr8, addr9, addr10, addr11;

 // Ram address generator, program mode or operation mode
 assign addr0 = prog ? paddr : {hen[0],fstate,idata[0]};
 assign addr1 = prog ? paddr : {hen[1],fstate,idata[1]};
 assign addr2 = prog ? paddr : {hen[2],fstate,idata[2]};
 assign addr3 = prog ? paddr : {hen[3],fstate,idata[3]};
 assign addr4 = prog ? paddr : {hen[4],fstate,idata[4]};
 assign addr5 = prog ? paddr : {hen[5],fstate,idata[5]};
 assign addr6 = prog ? paddr : {hen[6],fstate,idata[6]};
 assign addr7 = prog ? paddr : {hen[7],fstate,idata[7]};
 assign addr8 = prog ? paddr : {hen[8],fstate,idata[8]};
 assign addr9 = prog ? paddr : {hen[9],fstate,idata[9]};
 assign addr10 = prog ? paddr : {hen[10],fstate,idata[10]};
 assign addr11 = prog ? paddr : {hen[11],fstate,idata[11]};

 //Filter Taps
 ram9x12 h0(clk,we[0],addr0,coeff,t0),
 	 h1(clk,we[1],addr1,coeff,t1),
 	 h2(clk,we[2],addr2,coeff,t2),
 	 h3(clk,we[3],addr3,coeff,t3),
 	 h4(clk,we[4],addr4,coeff,t4),
 	 h5(clk,we[5],addr5,coeff,t5),
 	 h6(clk,we[6],addr6,coeff,t6),
 	 h7(clk,we[7],addr7,coeff,t7),
 	 h8(clk,we[8],addr8,coeff,t8),
 	 h9(clk,we[9],addr9,coeff,t9),
 	 h10(clk,we[10],addr10,coeff,t10),
 	 h11(clk,we[11],addr11,coeff,t11);

// Make temporary copies of filter outputs
 always @(t0 or t1 or t2 or t3 or t4 or t5)
  begin
   pa[0] = t0;
   pa[1] = t1;
   pa[2] = t2;
   pa[3] = t3;
   pa[4] = t4;
   pa[5] = t5;
  end
 always @(t6 or t7 or t8 or t9 or t10 or t11)
  begin
   pb[0] = t6;
   pb[1] = t7;
   pb[2] = t8;
   pb[3] = t9;
   pb[4] = t10;
   pb[5] = t11;
  end

 // Accumulators
 acc 
     // for taps h0:h5
     a1(.ta(pa[0]),.tb(pa[1]),.tc(pa[2]),.td(pa[3]),.te(pa[4]),.tf(pa[5]),
        .sel(acsel),.ld(acld),.clk(acclk),.rst(rst),.sum(acbot) ),

     // for taps h6:h11
     a2(.ta(pb[0]),.tb(pb[1]),.tc(pb[2]),.td(pb[3]),.te(pb[4]),.tf(pb[5]),
        .sel(acsel),.ld(acld),.clk(acclk),.rst(rst),.sum(actop) );

 //Register the accumulator output prior to the "filt_out" summation
 always @(posedge acclk or posedge rst)
  begin
   if (rst)
    begin
     acbot_reg <= 0;
     actop_reg <= 0;
    end
   else
    begin
     if(ac_reg_en)
      begin
       acbot_reg <= acbot;
       actop_reg <= actop;
      end
    end
  end

 assign filt_out = actop_reg + acbot_reg;

endmodule
