Bladeren bron

projects/usb_amr: Add the RFI module and wire it up

This modules detects large amplitude signals and check if they
are within a given range of frequency. This is used in combination
with the codec ring voltage detect signal to see if the line is
ringing.

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
Sylvain Munaut 1 jaar geleden
bovenliggende
commit
94ff9d4c5d

+ 2 - 0
projects/usb_amr/Makefile

@@ -5,6 +5,7 @@ PROJ_DEPS := no2usb no2misc no2ice40
 PROJ_RTL_SRCS := $(addprefix rtl/, \
 	mc97.v \
 	mc97_fifo.v \
+	mc97_rfi.v \
 	mc97_wb.v \
 	dfu_helper.v \
 	picorv32.v \
@@ -18,6 +19,7 @@ PROJ_RTL_SRCS := $(addprefix rtl/, \
 )
 PROJ_TESTBENCHES := \
 	mc97_tb \
+	mc97_rfi_tb \
 	$(NULL)
 PROJ_PREREQ = \
 	$(BUILD_TMP)/boot.hex

+ 1 - 0
projects/usb_amr/doc/mem-map.md

@@ -3,6 +3,7 @@ AC97 controller registers
 
 ```
 `0x00`	CSR
+		[3]     (R)  Ring Frequency Indicator
 		[2]	(RW) GPIO ena
 		[1]	(RW) AC97_reset_n
 		[0]	(RW) Run

+ 14 - 0
projects/usb_amr/rtl/mc97.v

@@ -40,6 +40,8 @@ module mc97 (
 	// User interface - Misc
 	input  wire        cfg_run,
 
+	output wire        rfi,
+
 	output wire        stat_codec_ready,
 	output reg  [12:0] stat_slot_valid,
 	output reg  [12:0] stat_slot_req,
@@ -270,6 +272,18 @@ module mc97 (
 		pcm_in_stb <= sui_ack & seq_rd_slot[5] & fc_slotvalid[5];
 
 
+	// Ring Frequency Indicator
+	// ------------------------
+
+	mc97_rfi rfi_I (
+		.pcm_data (pcm_in_data),
+		.pcm_stb  (pcm_in_stb),
+		.rfi      (rfi),
+		.clk      (clk),
+		.rst      (rst)
+	);
+
+
 	// GPIO (slot 12)
 	// ----
 

+ 160 - 0
projects/usb_amr/rtl/mc97_rfi.v

@@ -0,0 +1,160 @@
+/*
+ * mc97_rfi.v
+ *
+ * Detects 10-100 Hz ringing foR ring indication
+ *
+ * vim: ts=4 sw=4
+ *
+ * Copyright (C) 2021  Sylvain Munaut <tnt@246tNt.com>
+ * SPDX-License-Identifier: CERN-OHL-P-2.0
+ */
+
+`default_nettype none
+
+module mc97_rfi #(
+	parameter integer CLK_FREQ = 24_000_000,
+	parameter integer F_MIN = 10,	/* Hz */
+	parameter integer F_MAX = 100	/* Hz */
+)(
+	// PCM tap
+	input  wire [15:0] pcm_data,
+	input  wire        pcm_stb,
+
+	// Ring Frequency Indication
+	output reg  rfi,
+
+	// Clock / Reset
+	input  wire clk,
+	input  wire rst
+);
+
+	localparam integer CYC_MIN = CLK_FREQ / F_MAX;
+	localparam integer CYC_MAX = CLK_FREQ / F_MIN;
+
+	localparam integer WMIN = $clog2(CYC_MIN);
+	localparam integer WMAX = $clog2(CYC_MAX);
+
+	localparam [WMIN-1:0] K_MIN = CYC_MIN;
+	localparam [WMAX-1:0] K_MAX = CYC_MAX;
+
+
+	// Signals
+	// -------
+
+	// Rising edge detector
+	wire       rid_is_neg;
+	wire       rid_is_pos;
+	reg  [2:0] rid_cnt;
+	wire       rid_cnt_max;
+	reg        rid_armed;
+	reg        rid_stb;
+
+	// Interval counter
+	reg [WMIN:0] ic_min;
+	reg [WMAX:0] ic_max;
+	reg          ic_max_msb_r;
+	reg          ic_armed;
+	reg          ic_stb_ok;
+	reg          ic_stb_err;
+
+	// Validation
+	reg [1:0] vs_cnt_err;
+	reg [1:0] vs_cnt_ok;
+
+
+	// Detect "Rising edges"
+	// ---------------------
+
+		// Anything going for < -16384 to >= 16384
+		// in less than 8 samples
+
+	assign rid_is_neg = (pcm_data[15:14] == 2'b10);
+	assign rid_is_pos = (pcm_data[15:14] == 2'b01);
+
+	always @(posedge clk or posedge rst)
+		if (rst)
+			rid_cnt <= 0;
+		else if (pcm_stb)
+			casez ({rid_is_neg, rid_cnt})
+			4'b1zzz: rid_cnt <= 3'b000;
+			4'b0000: rid_cnt <= 3'b001;
+			4'b0001: rid_cnt <= 3'b010;
+			4'b0010: rid_cnt <= 3'b011;
+			4'b0011: rid_cnt <= 3'b100;
+			4'b0100: rid_cnt <= 3'b101;
+			4'b0101: rid_cnt <= 3'b110;
+			4'b0110: rid_cnt <= 3'b111;
+			4'b0111: rid_cnt <= 3'b111;
+			default: rid_cnt <= 3'bxxx;
+			endcase
+
+	assign rid_cnt_max = (rid_cnt == 3'b111);
+
+	always @(posedge clk or posedge rst)
+		if (rst)
+			rid_armed <= 0;
+		else if (pcm_stb)
+			rid_armed <= (rid_armed | rid_is_neg) & ~(rid_cnt_max | rid_is_pos);
+
+	always @(posedge clk)
+		rid_stb <= rid_armed & rid_is_pos & ~rid_cnt_max & pcm_stb;
+
+
+	// Interval counter
+	// ----------------
+
+		// Measure interval between rising edge and detects :
+		// - underflow : Rising edge less than 10 ms appart
+		// - overflow  : No rising edge for more than 100 ms
+		// - ok        : Rising edge occured withing expected interval
+
+	always @(posedge clk)
+		if (rid_stb) begin
+			ic_min <= { 1'b1, K_MIN };
+			ic_max <= { 1'b1, K_MAX };
+		end else begin
+			ic_min <= ic_min - ic_min[WMIN];
+			ic_max <= ic_max[WMAX] ? (ic_max - 1) : { 1'b1, K_MAX }; // Auto-repeat if no edge
+		end
+
+	always @(posedge clk)
+		ic_max_msb_r <= ic_max[WMAX];
+
+	always @(posedge clk)
+		if (rst)
+			ic_armed <= 1'b0;
+		else
+			ic_armed <= (ic_armed & ic_max[WMAX]) | rid_stb;
+
+	always @(posedge clk)
+	begin
+		ic_stb_ok  <=  ic_armed & rid_stb & ~ic_min[WMIN] & ic_max[WMAX];
+		ic_stb_err <= (ic_armed & rid_stb &  ic_min[WMIN]) | (ic_max_msb_r & ~ic_max[WMAX]);
+	end
+
+
+	// Validation
+	// ----------
+
+		// Assert   on 4 succesive valid
+		// Deassert on 4 succesive invalid ones
+
+	always @(posedge clk)
+		if (rst)
+			vs_cnt_err <= 0;
+		else
+			vs_cnt_err <= (vs_cnt_err + ic_stb_err) & {2{~ic_stb_ok}};
+
+	always @(posedge clk)
+		if (rst)
+			vs_cnt_ok <= 0;
+		else
+			vs_cnt_ok <= (vs_cnt_ok + ic_stb_ok) & {2{~ic_stb_err}};
+
+	always @(posedge clk)
+		if (rst)
+			rfi <= 0;
+		else
+			rfi <= (rfi & ~&vs_cnt_err) | &vs_cnt_ok;
+
+endmodule // mc97_rfi

+ 3 - 1
projects/usb_amr/rtl/mc97_wb.v

@@ -98,6 +98,7 @@ module mc97_wb (
 
 	// LL Misc interface
 	reg         ll_run;
+	wire        ll_rfi;
 	wire        ll_stat_codec_ready;
 	wire [12:0] ll_stat_slot_valid;
 	wire [12:0] ll_stat_slot_req;
@@ -143,7 +144,7 @@ module mc97_wb (
 			wb_rdata <= 32'h00000000;
 		else
 			casez (wb_addr[2:0])
-				3'h0:    wb_rdata <= { 29'h0, ll_gpio_ena, mc97_reset_n, ll_run };
+				3'h0:    wb_rdata <= { 28'h0, ll_rfi, ll_gpio_ena, mc97_reset_n, ll_run };
 				3'h1:    wb_rdata <= { ll_stat_codec_ready, 2'h0, ll_stat_slot_req, 3'h0, ll_stat_slot_valid };
 				3'h2:    wb_rdata <= { ll_reg_valid, ll_reg_we, ll_reg_rerr_r, 7'h0, ll_reg_addr, ll_reg_rdata_r };
 				3'h4:    wb_rdata <= { 12'h0, ll_gpio_in  };
@@ -299,6 +300,7 @@ module mc97_wb (
 		.reg_we          (ll_reg_we),
 		.reg_ack         (ll_reg_ack),
 		.cfg_run         (ll_run),
+		.rfi             (ll_rfi),
 		.stat_codec_ready(ll_stat_codec_ready),
 		.stat_slot_valid (ll_stat_slot_valid),
 		.stat_slot_req   (ll_stat_slot_req),

+ 102 - 0
projects/usb_amr/sim/mc97_rfi_tb.v

@@ -0,0 +1,102 @@
+/*
+ * mc97_rfi_tb.v
+ *
+ * vim: ts=4 sw=4
+ *
+ */
+
+`default_nettype none
+
+module mc97_rfi_tb;
+
+	localparam integer GEN_FREQ = 15; /* Hz */
+
+	// Signals
+	// -------
+
+	// Tick
+	reg  [15:0] tick_cnt;
+	wire        tick;
+
+	// Tone Generation
+	real phase;
+	real phase_inc = 6.28 * GEN_FREQ / 8000;
+	real tmp;
+
+	// DUT connections
+	reg  [15:0] pcm_data = 0;
+	wire        pcm_stb;
+
+	wire        rfi;
+
+	// Clock reset
+	reg clk = 1'b0;
+	reg rst = 1'b1;
+
+
+	// Setup recording
+	// ---------------
+
+	initial begin
+		$dumpfile("mc97_rfi_tb.vcd");
+		$dumpvars(0,mc97_rfi_tb);
+		# 600000000 $finish;
+	end
+
+	always #500 clk <= !clk; // 1 MHz
+
+	initial begin
+		#2000 rst = 0;
+	end
+
+
+	// Generate tone
+	// -------------
+
+	// Tick at 8000 Hz
+	always @(posedge clk)
+		if (rst)
+			tick_cnt <= 0;
+		else
+			tick_cnt <= tick ? 16'd123 : (tick_cnt - 1);
+
+	assign tick = tick_cnt[15];
+
+	// Tone
+	always @(posedge clk)
+		if (rst)
+			phase <= 0.0;
+		else if (tick)
+			phase <= phase + phase_inc;
+
+	always @(*)
+	begin
+		tmp = $cos(phase) * (1 << 24) / GEN_FREQ;
+
+		if (tmp > 32767)
+			pcm_data = 32767;
+		else if (tmp < -32768)
+			pcm_data = -32768;
+		else
+			pcm_data = tmp;
+	end
+
+	assign pcm_stb = tick;
+
+
+	// DUT
+	// ---
+
+	mc97_rfi #(
+		.CLK_FREQ(1_000_000),
+		.F_MIN(10),
+		.F_MAX(100)
+	) rfi_I (
+		.pcm_data (pcm_data),
+		.pcm_stb  (pcm_stb),
+		.rfi      (rfi),
+		.clk      (clk),
+		.rst      (rst)
+	);
+
+endmodule // mc97_rfi_tb