FPGA SDR(18)自作CICフィルタ(可変間引き率版)


IP CatalogのCICフィルタも自作CICフィルタも入力rateの値で間引き率を動的に変更できます。ただし平均の分母が常に最高間引き率固定なので、最高間引き率より小さい間引き率のときは出力データが小さくなってしまいます。段数が多いとべき乗で小さくなってしまいます。

AM/FMラジオの間引き率は、AM 1/1024、FM 1/64にしているので、1段のフィルタでもFMのときのCICフィルタの出力はAMのときの1/16になります。2段、3段にすると小さくなりすぎて復調できません。

そこで、間引き率で出力レベルが変化しないCICフィルタを作ってみました。

`timescale 1ns/1ns


module MyCIC
 #(
	parameter NUM_STAGES = 3,
	parameter MAX_RATE_WIDTH = 12,
	parameter IN_WIDTH = 19,
	parameter OUT_WIDTH = 19
 )
(
	input wire clk,
	input wire reset_n,
	input wire [MAX_RATE_WIDTH:0] rate,
	
	input wire signed [IN_WIDTH-1:0] in_data,
	output wire in_ready,
	input wire in_valid,
	input wire [1:0] in_error,

	output reg signed [OUT_WIDTH-1:0] out_data,
	input wire out_ready,
	output reg out_valid,
	output wire [1:0] out_error
);


	localparam WIDTH = MAX_RATE_WIDTH * NUM_STAGES + IN_WIDTH;
	
	integer i;

	reg signed [WIDTH-1:0] integ [0:NUM_STAGES];
	reg signed [WIDTH-1:0] diff [0:NUM_STAGES];
	reg signed [WIDTH-1:0] diff_d [0:NUM_STAGES];

	reg [MAX_RATE_WIDTH:0] count;
	reg [3:0] rate_width;
	reg next_out_valid;

	
	assign in_ready = 1'b1;
	assign out_error = 2'b00;
	
	always @(posedge clk)
	begin
		if (~reset_n)
		begin
			for (i = 1; i <= NUM_STAGES; i = i + 1) begin
				integ[i] <= 0;
			end
			
			diff[1] <= 0;
		end else
		begin
			integ[1] <= in_data + integ[1];
			
			for (i = 2; i <= NUM_STAGES; i = i + 1) begin
				integ[i] <= integ[i-1] + integ[i];
			end
			
			diff[1] <= integ[NUM_STAGES-1] + integ[NUM_STAGES];
		end
	end
	
	always @(posedge clk)
	begin
		if (~reset_n)
		begin
			count <= 0;
			next_out_valid <= 1'b1;
			
			rate_width <= 0;
		end else
		begin			
			if (count == rate - 1)
			begin
				count <= 0;
				next_out_valid <= 1'b1;
				
				if      (count >= 16384) rate_width <= 15;
				else if (count >= 8192) rate_width <= 14;
				else if (count >= 4096) rate_width <= 13;
				else if (count >= 2048) rate_width <= 12;
				else if (count >= 1024) rate_width <= 11;
				else if (count >= 512) rate_width <= 10;
				else if (count >= 256) rate_width <= 9;
				else if (count >= 128) rate_width <= 8;
				else if (count >= 64) rate_width <= 7;
				else if (count >= 32) rate_width <= 6;
				else if (count >= 16) rate_width <= 5;
				else if (count >= 8) rate_width <= 4;
				else if (count >= 4) rate_width <= 3;
				else if (count >= 2) rate_width <= 2;
				else if (count >= 1) rate_width <= 1;
				else rate_width <= 0;
			end else
			begin
				count <= count + 1;
				next_out_valid <= 1'b0;
			end
		end
	end
	
	always @(posedge clk)
	begin
		out_valid <= next_out_valid;
		
		if (~reset_n)
		begin
			for (i = 1; i <= NUM_STAGES; i = i + 1) begin
				diff_d[i] <= 0;
			end
			
			for (i = 2; i <= NUM_STAGES; i = i + 1) begin
				diff[i] <= 0;
			end
			
			out_data <= 0;
		end else
		begin
			if (next_out_valid)
			begin
				for (i = 1; i <= NUM_STAGES; i = i + 1) begin
					diff_d[i] <= diff[i];
				end
			
				for (i = 2; i <= NUM_STAGES; i = i + 1) begin
					diff[i] <= diff[i-1] - diff_d[i-1];
				end
								
				out_data <= (diff[NUM_STAGES] - diff_d[NUM_STAGES]) >>> (rate_width * NUM_STAGES + IN_WIDTH - OUT_WIDTH);
			end
		end
	end
	
endmodule