axi_dac_interpolate: Improve the ctrl logic

1. Simplify the control logic by adding a state machine.
The improvements are on code readability and reliability.

2.Add a flush feature which can be used to clean the data from the DMA fifo.
This is useful when the DMA is programmed in cyclic mode and
data transmission is stopped by dma_transfer_suspend flag
The software intervention is reduced at setting the flag(dma_flush_en).
Flushing can also be done when activating the raw value with dma_flush_en active.

3. Add raw value support. Through this changes a user can set
the dac output to a fixed predefined value in the following two cases:
  1. direct, without using the dma.
  2. with dma, as a hold value. The fixed value will be kipped after a cyclic
buffer is stopped by axi_dac_interpolate, through dma_transfer_suspend
register/signal.
The raw value ca be set and transmitted independently on each channel.
The predefined value is stored in reg 0x19(0x64). For more details se
the documentation available at
https://wiki.analog.com/resources/fpga/docs/axi_dac_interpolate
main
AndreiGrozav 2023-09-22 14:13:50 +03:00 committed by AndreiGrozav
parent 6998cc99b4
commit 39b2a2b8bb
3 changed files with 151 additions and 53 deletions

View File

@ -124,6 +124,7 @@ module axi_dac_interpolate #(
wire [ 2:0] filter_mask_b;
wire dma_transfer_suspend;
wire flush_dma_s;
wire start_sync_channels;
wire dac_correction_enable_a;
@ -152,6 +153,9 @@ module axi_dac_interpolate #(
wire underflow_b;
wire stop_sync_channels;
wire [ 1:0] raw_transfer_en;
wire [15:0] dac_raw_ch_a_data;
wire [15:0] dac_raw_ch_b_data;
// signal name changes
@ -215,7 +219,6 @@ module axi_dac_interpolate #(
.dac_data (dac_data_a),
.dac_valid (dac_valid_a),
.dac_valid_out (dac_valid_out_a),
.sync_stop_channels (stop_sync_channels),
.dac_enable (dac_enable_a),
.dac_int_data (dac_int_data_a),
@ -226,6 +229,10 @@ module axi_dac_interpolate #(
.interpolation_ratio (interpolation_ratio_a),
.dma_transfer_suspend (dma_transfer_suspend),
.start_sync_channels (start_sync_channels),
.sync_stop_channels (stop_sync_channels),
.flush_dma_in (flush_dma_s),
.raw_transfer_en (raw_transfer_en[0]),
.dac_raw_ch_data (dac_raw_ch_a_data),
.trigger (trigger),
.trigger_active (trigger_active),
.en_start_trigger (en_start_trigger),
@ -244,7 +251,6 @@ module axi_dac_interpolate #(
.dac_data (dac_data_b),
.dac_valid (dac_valid_b),
.dac_valid_out (dac_valid_out_b),
.sync_stop_channels (stop_sync_channels),
.underflow (underflow_b),
.dac_enable (dac_enable_b),
@ -255,6 +261,10 @@ module axi_dac_interpolate #(
.interpolation_ratio (interpolation_ratio_b),
.dma_transfer_suspend (dma_transfer_suspend),
.start_sync_channels (start_sync_channels),
.sync_stop_channels (stop_sync_channels),
.flush_dma_in (flush_dma_s),
.raw_transfer_en (raw_transfer_en[1]),
.dac_raw_ch_data (dac_raw_ch_b_data),
.trigger (trigger),
.trigger_active (trigger_active),
.en_start_trigger (en_start_trigger),
@ -274,6 +284,10 @@ module axi_dac_interpolate #(
.dac_filter_mask_b (filter_mask_b),
.dma_transfer_suspend (dma_transfer_suspend),
.flush_dma_out (flush_dma_s),
.raw_transfer_en (raw_transfer_en),
.dac_raw_ch_a_data (dac_raw_ch_a_data),
.dac_raw_ch_b_data (dac_raw_ch_b_data),
.start_sync_channels (start_sync_channels),
.dac_correction_enable_a(dac_correction_enable_a),
.dac_correction_enable_b(dac_correction_enable_b),

View File

@ -49,7 +49,6 @@ module axi_dac_interpolate_filter #(
output reg [15:0] dac_int_data,
output dma_ready,
output dac_valid_out,
input sync_stop_channels,
output underflow,
input [ 2:0] filter_mask,
@ -58,6 +57,10 @@ module axi_dac_interpolate_filter #(
input dac_correction_enable,
input dma_transfer_suspend,
input start_sync_channels,
input sync_stop_channels,
input flush_dma_in,
input raw_transfer_en,
input [15:0] dac_raw_ch_data,
input trigger,
input trigger_active,
input en_start_trigger,
@ -66,7 +69,14 @@ module axi_dac_interpolate_filter #(
input dma_valid_adjacent
);
// internal signals
// local parameters
localparam [1:0] IDLE = 0;
localparam [1:0] WAIT = 1;
localparam [1:0] TRANSFER = 2;
localparam [1:0] FLUSHING = 3;
// internal registers
reg dac_int_ready;
reg dac_filt_int_valid;
@ -75,15 +85,20 @@ module axi_dac_interpolate_filter #(
reg cic_change_rate;
reg [31:0] interpolation_counter;
reg transmit_ready = 1'b0;
reg dma_data_valid = 1'b0;
reg dma_data_valid_adjacent = 1'b0;
reg filter_enable = 1'b0;
reg transfer = 1'b0;
reg [15:0] dma_valid_m = 16'd0;
reg stop_transfer = 1'd0;
reg transfer = 1'd0;
reg [ 1:0] transfer_sm = 2'd0;
reg [ 1:0] transfer_sm_next = 2'd0;
reg raw_dma_n = 1'd0;
// internal signals
wire dac_valid_corrected;
wire [15:0] dac_data_corrected;
wire dac_fir_valid;
@ -94,6 +109,28 @@ module axi_dac_interpolate_filter #(
wire dma_valid_ch_sync;
wire dma_valid_ch;
wire flush_dma;
wire [15:0] iqcor_data_in;
wire iqcor_valid_in;
wire transfer_start;
wire transfer_ready;
// Once enabled the raw value will be selected until the DMA has valid data.
// This is a workaround for when DAC channels are start/stopped independent
// of each other and working in cyclic mode at a lower samplerate. It is
// used to solve a system limitation(delay) between the application and
// Linux kernel, which resulted in a pulse at the beginning of a new buffer
// consisting in the last sample on the DMA bus.
always @(posedge dac_clk) begin
raw_dma_n <= raw_transfer_en | flush_dma ? 1'b1 : raw_dma_n & !dma_valid;
end
assign reset_filt = !raw_dma_n & dma_transfer_suspend;
assign iqcor_data_in = raw_dma_n ? dac_raw_ch_data : dac_data;
assign iqcor_valid_in = raw_dma_n ? 1'b1 : dac_valid;
ad_iqcor #(
.Q_OR_I_N (0),
@ -101,8 +138,8 @@ module axi_dac_interpolate_filter #(
.SCALE_ONLY(1)
) i_ad_iqcor (
.clk (dac_clk),
.valid (dac_valid),
.data_in (dac_data),
.valid (iqcor_valid_in),
.data_in (iqcor_data_in),
.data_iq (16'h0),
.valid_out (dac_valid_corrected),
.data_out (dac_data_corrected),
@ -113,7 +150,7 @@ module axi_dac_interpolate_filter #(
fir_interp fir_interpolation (
.clk (dac_clk),
.clk_enable (dac_cic_valid),
.reset (dac_rst | dma_transfer_suspend),
.reset (dac_rst | reset_filt),
.filter_in (dac_data_corrected),
.filter_out (dac_fir_data),
.ce_out (dac_fir_valid));
@ -121,28 +158,13 @@ module axi_dac_interpolate_filter #(
cic_interp cic_interpolation (
.clk (dac_clk),
.clk_enable (dac_valid_corrected),
.reset (dac_rst | cic_change_rate | dma_transfer_suspend),
.reset (dac_rst | cic_change_rate | reset_filt),
.rate (interp_rate_cic),
.load_rate (1'b0),
.filter_in (dac_fir_data[30:0]),
.filter_out (dac_cic_data),
.ce_out (dac_cic_valid));
assign dma_valid_ch_sync = sync_stop_channels ?
dma_valid & dma_valid_adjacent & !dma_transfer_suspend :
dma_valid & !dma_transfer_suspend;
assign dma_valid_ch = dma_valid_ch_sync & !stop_transfer;
always @(posedge dac_clk) begin
if (dac_rst == 1'b1) begin
dma_valid_m <= 'd0;
end else begin
dma_valid_m <= {dma_valid_m[14:0], dma_valid_ch};
end
end
assign dac_valid_out = dma_valid_m[4'h5];
always @(posedge dac_clk) begin
filter_mask_d1 <= filter_mask;
if (filter_mask_d1 != filter_mask) begin
@ -157,9 +179,7 @@ module axi_dac_interpolate_filter #(
// paths randomly ready, only when using data buffers
always @(posedge dac_clk) begin
if (dac_filt_int_valid &
(!start_sync_channels & dma_valid |
(dma_valid & dma_valid_adjacent))) begin
if (dac_filt_int_valid & transfer_ready) begin
if (interpolation_counter == interpolation_ratio) begin
interpolation_counter <= 0;
dac_int_ready <= 1'b1;
@ -173,26 +193,71 @@ module axi_dac_interpolate_filter #(
end
end
assign transfer_ready = start_sync_channels ?
dma_valid & dma_valid_adjacent :
dma_valid;
assign transfer_start = !(en_start_trigger ^ trigger) & transfer_ready;
always @(posedge dac_clk) begin
if (dma_transfer_suspend == 1'b0) begin
transfer <= trigger ? 1'b1 : transfer | !(trigger_active & en_start_trigger);
end else begin
stop_transfer <= transfer_sm == IDLE ? 1'b0 :
stop_transfer |
dma_transfer_suspend |
(en_stop_trigger & trigger) |
(sync_stop_channels & dma_valid & dma_valid_adjacent);
end
// transfer state machine
always @(posedge dac_clk) begin
case (transfer_sm)
IDLE: begin
transfer <= 1'b0;
if (dac_int_ready) begin
transfer_sm_next <= WAIT;
end
if (start_sync_channels == 1'b0) begin
transmit_ready <= dma_valid & transfer;
end
WAIT: begin
transfer <= 1'b0;
if (transfer_start) begin
transfer_sm_next <= TRANSFER;
end
end
TRANSFER: begin
transfer <= 1'b1;
if (stop_transfer) begin
if (flush_dma_in) begin
transfer_sm_next <= FLUSHING;
end else begin
transmit_ready <= dma_valid & dma_valid_adjacent & transfer;
transfer_sm_next <= WAIT;
end
end else if (dma_valid == 1'b0) begin
transfer_sm_next <= IDLE;
end
end
FLUSHING: begin
transfer <= 1'b1;
if (dma_valid == 1'b0) begin
transfer_sm_next <= IDLE;
end
end
endcase
transfer_sm <= transfer_sm_next;
end
assign flush_dma = transfer_sm_next == FLUSHING ? 1'b1 : raw_transfer_en & flush_dma_in;
assign dma_ready = transfer_sm_next == TRANSFER ? dac_int_ready : flush_dma;
assign underflow = dac_enable & dma_ready & ~dma_valid & !flush_dma;
assign dma_valid_ch = transfer | raw_transfer_en;
always @(posedge dac_clk) begin
stop_transfer <= !en_stop_trigger | dma_transfer_suspend ? 1'b0 :
stop_transfer | (trigger_active & trigger & transfer);
if (dac_rst == 1'b1) begin
dma_valid_m <= 'd0;
end else begin
dma_valid_m <= {dma_valid_m[14:0], dma_valid_ch};
end
end
assign dma_ready = transmit_ready ? dac_int_ready : 1'b0;
assign underflow = dac_enable & dma_ready & ~dma_valid;
assign dac_valid_out = dma_valid_m[2];
always @(posedge dac_clk) begin
case (filter_mask)

View File

@ -44,6 +44,10 @@ module axi_dac_interpolate_reg(
output [31:0] dac_interpolation_ratio_b,
output [ 2:0] dac_filter_mask_b,
output dma_transfer_suspend,
output flush_dma_out,
output [ 1:0] raw_transfer_en,
output [15:0] dac_raw_ch_a_data,
output [15:0] dac_raw_ch_b_data,
output start_sync_channels,
output stop_sync_channels,
output dac_correction_enable_a,
@ -68,7 +72,7 @@ module axi_dac_interpolate_reg(
// internal registers
reg [31:0] up_version = {16'h0002, /* MAJOR */
8'h02, /* MINOR */
8'h03, /* MINOR */
8'h00}; /* PATCH */
reg [31:0] up_scratch = 32'h0;
@ -76,17 +80,21 @@ module axi_dac_interpolate_reg(
reg [ 2:0] up_filter_mask_a = 3'h0;
reg [31:0] up_interpolation_ratio_b = 32'h0;
reg [ 2:0] up_filter_mask_b = 3'h0;
reg [2:0] up_flags = 3'h2;
reg [1:0] up_config = 2'h0;
reg [ 5:0] up_flags = 6'ha;
reg [ 1:0] up_config = 2'h0;
reg [15:0] up_correction_coefficient_a = 16'h0;
reg [15:0] up_correction_coefficient_b = 16'h0;
reg [19:0] up_trigger_config = 20'h0;
reg [15:0] up_dac_raw_ch_a_data;
reg [15:0] up_dac_raw_ch_b_data;
wire [ 2:0] flags;
wire [ 5:0] flags;
assign dma_transfer_suspend = flags[0];
assign start_sync_channels = flags[1];
assign stop_sync_channels = flags[2];
assign flush_dma_out = flags[3];
assign raw_transfer_en = flags[5:4]; //5-b; 4-a
always @(negedge up_rstn or posedge up_clk) begin
if (up_rstn == 0) begin
@ -96,11 +104,13 @@ module axi_dac_interpolate_reg(
up_filter_mask_a <= 'd0;
up_interpolation_ratio_b <= 'd0;
up_filter_mask_b <= 'd0;
up_flags <= 'd2;
up_flags <= 'ha;
up_config <= 'd0;
up_correction_coefficient_a <= 'd0;
up_correction_coefficient_b <= 'd0;
up_trigger_config <= 'd0;
up_dac_raw_ch_a_data <= 16'd0;
up_dac_raw_ch_b_data <= 16'd0;
end else begin
up_wack <= up_wreq;
if ((up_wreq == 1'b1) && (up_waddr[4:0] == 5'h1)) begin
@ -119,7 +129,7 @@ module axi_dac_interpolate_reg(
up_filter_mask_b <= up_wdata[2:0];
end
if ((up_wreq == 1'b1) && (up_waddr[4:0] == 5'h14)) begin
up_flags <= {29'h0,up_wdata[2:0]};
up_flags <= up_wdata[5:0];
end
if ((up_wreq == 1'b1) && (up_waddr[4:0] == 5'h15)) begin
up_config <= up_wdata[1:0];
@ -133,6 +143,10 @@ module axi_dac_interpolate_reg(
if ((up_wreq == 1'b1) && (up_waddr[4:0] == 5'h18)) begin
up_trigger_config <= up_wdata[19:0];
end
if ((up_wreq == 1'b1) && (up_waddr[4:0] == 5'h19)) begin
up_dac_raw_ch_a_data <= up_wdata[15:0];
up_dac_raw_ch_b_data <= up_wdata[31:16];
end
end
end
@ -152,11 +166,12 @@ module axi_dac_interpolate_reg(
5'h11: up_rdata <= {29'h0,up_filter_mask_a};
5'h12: up_rdata <= up_interpolation_ratio_b;
5'h13: up_rdata <= {29'h0,up_filter_mask_b};
5'h14: up_rdata <= {29'h0,up_flags};
5'h14: up_rdata <= {26'h0,up_flags};
5'h15: up_rdata <= {30'h0,up_config};
5'h16: up_rdata <= {16'h0,up_correction_coefficient_a};
5'h17: up_rdata <= {16'h0,up_correction_coefficient_b};
5'h18: up_rdata <= {12'h0,up_trigger_config};
5'h19: up_rdata <= {up_dac_raw_ch_b_data, up_dac_raw_ch_a_data};
default: up_rdata <= 0;
endcase
end else begin
@ -166,7 +181,7 @@ module axi_dac_interpolate_reg(
end
up_xfer_cntrl #(
.DATA_WIDTH(127)
.DATA_WIDTH(162)
) i_xfer_cntrl (
.up_rstn (up_rstn),
.up_clk (up_clk),
@ -175,7 +190,9 @@ module axi_dac_interpolate_reg(
up_correction_coefficient_b,// 16
up_correction_coefficient_a,// 16
up_trigger_config, // 20
up_flags, // 3
up_flags, // 6
up_dac_raw_ch_a_data, // 16
up_dac_raw_ch_b_data, // 16
up_interpolation_ratio_b, // 32
up_interpolation_ratio_a, // 32
up_filter_mask_b, // 3
@ -189,7 +206,9 @@ module axi_dac_interpolate_reg(
dac_correction_coefficient_b, // 16
dac_correction_coefficient_a, // 16
trigger_config, // 20
flags, // 3
flags, // 6
dac_raw_ch_a_data, // 16
dac_raw_ch_b_data, // 16
dac_interpolation_ratio_b, // 32
dac_interpolation_ratio_a, // 32
dac_filter_mask_b, // 3