Attention
This documentation is a work in progress. Expect to see errors and unfinished things.
iq_trace Source File
1`timescale 1ns / 1ns
2
3// One more try to package up the LBNL conveyor belt in a usable and
4// comprehensible form.
5
6// New things going on here:
7// 1. adc count is parameterized, and therefore a single input port
8// is nadc*dw bits wide; each block of dw bits is a signed number.
9// 2. Independent I and Q shift registers
10// 3. 32-bit wide memory read port can either hold the 20-bit full-resolution
11// measurement, or two packed 16-bit I and Q data
12
13// LCLS-II dissection of possible use cases
14// 1320/14 MHz raw ADC clock
15// /33 for base CIC, 2.857 MS/s
16// /8 to get 357 kS/s, suitable for loop characterization
17// or /16 to get 179 kS/s, and a channel-subset is streamable for close-in
18// noise characterization
19// 179 kS/s * 2 ADCs * 2 components * 20 bits = 14.3 Mbit/s
20// or without re-writing any infrastructure now,
21// 179 kS/s * 2 ADCs * 2 components * 64 bits = 45.7 Mbit/s
22// or /64 to get 44.64 kS/s, easily streamable and good for acoustic
23// characterization, including most SRF trip analysis
24// 44.64 kS/s * 8 cavities * 2 components * 16 bits = 11.4 Mbit/s
25// Useful to grab 8 packets at a time with 1024 octets each,
26// requires each half of a ping-pong buffer to be 4K x 16.
27
28// To support more than 33*16-point averaging, without losing resolution, you
29// need wider data paths in ccfilt, with its recently-added outw parameter.
30// 16 + log2(33*16/2)/2 = 20
31// where the /2 inside the log represents mean(sin(theta)^2), and the answer
32// comes out slightly more than 20 because we've ignored the intended
33// adjustment to lo_amp.
34
35// See nco_setup.py for help setting phase_step_h, phase_step_l, and modulo.
36
37module iq_trace #(
38 parameter dw = 16, // ADC input width
39 parameter oscw = 18, // Oscillator input width
40 parameter davr = 3, // Guard bits at output of mixer
41 parameter ow = 28, // second-order CIC data path width
42 parameter rw = 20, // result width out of ccfilt
43 parameter pcw = 13, // cic_period counter width
44 parameter shift_base = 7, // see ccfilt.v
45 parameter nadc = 8,
46 parameter aw = 13 // for circle_buf, see below
47) (
48 input clk,
49 input reset,
50 input trig,
51 input [1:0] trig_mode, // 0: free-run, 1: single-shot, 2: sync start, XXX not yet used
52 input [nadc*dw-1:0] adcs, // each dw-wide adc data is signed
53 input signed [oscw-1:0] cosa,
54 input signed [oscw-1:0] sina,
55 // Presumably host-settable parameters in the clk domain
56 input [pcw-1:0] cic_period, // expected values 33 to 33*128
57 input [3:0] cic_shift, // expected values 7 to 15
58 input [nadc-1:0] keep, // bit n :: channel n
59 // 18+10+4 = 32 so all of the last three parameters could be a single
60 // configuration word that fully defines output scaling
61 //
62 // host readout port, based on circle_buf. ro_data is:
63 // ro_addr[aw+1] == 1'b1 packed 16-bit {I, Q} results
64 // ro_addr[aw+1:aw] == 2'b10 long (rw bits) I result
65 // ro_addr[aw+1:aw] == 2'b11 long (rw bits) Q result
66 input ro_clk,
67 output ro_enable,
68 input ro_ack,
69 input [aw+1:0] ro_addr,
70 output [31:0] ro_data,
71 output [31:0] ro_status
72);
73
74 // Set up some global signals in the right clock domain
75 reg [1:0] trig_mode_r = 0;
76 reg trig_pending = 0;
77 wire boundary;
78
79 wire trig_actual = trig_pending & boundary;
80 always @(posedge clk) begin
81 if (boundary) trig_pending <= 0;
82 if (trig) trig_pending <= 1;
83
84 trig_mode_r <= trig_mode;
85 end
86
87 // ---------------------
88 // Instantiate Sampler
89 // ---------------------
90 wire cic_sample;
91
92 multi_sampler #(
93 .sample_period_wi (pcw))
94 i_multi_sampler (
95 .clk (clk),
96 .reset (reset),
97 .ext_trig (1'b1),
98 .sample_period (cic_period),
99 .dsample0_period (8'h1),
100 .dsample1_period (8'h1),
101 .dsample2_period (8'h1),
102 .sample_out (cic_sample),
103 .dsample0_stb (),
104 .dsample1_stb ()
105 );
106
107 // ---------------------
108 // Instantiate mixers to create I and Q streams
109 // ---------------------
110 wire signed [nadc*(dw+davr)-1:0] mixout_i, mixout_q;
111
112 iq_mixer_multichannel #(
113 .NCHAN (nadc),
114 .DWI (dw),
115 .DAVR (davr),
116 .DWLO (oscw)
117 ) i_iq_mixer_multichannel (
118 .clk (clk),
119 .adc (adcs),
120 .cos (cosa),
121 .sin (sina),
122 .mixout_i (mixout_i),
123 .mixout_q (mixout_q)
124 );
125
126 // ---------------------
127 // Instantiate two CIC_MULTICHANNEL, one for each stream
128 // ---------------------
129 wire strobe_cc;
130 wire signed [rw-1:0] result_i, result_q;
131
132 cic_multichannel #(
133 .n_chan (nadc),
134 // DI parameters
135 .di_dwi (dw+davr),
136 .di_rwi (ow),
137 .di_noise_bits (1), // NOTE: Setting to 1 to compensate for removed /2 from double_inte
138 .cc_outw (rw),
139 .cc_halfband (0),
140 .cc_use_delay (0),
141 .cc_shift_base (shift_base))
142 i_cic_multichannel_i
143 (
144 .clk (clk),
145 .reset (reset),
146 .stb_in (1'b1),
147 .d_in (mixout_i),
148 .cic_sample (cic_sample),
149 .cc_sample (1'b1),
150 .cc_shift (cic_shift),
151 .di_stb_out (), // Unused double-integrator tap
152 .di_sr_out (),
153 .cc_stb_out (strobe_cc),
154 .cc_sr_out (result_i)
155 );
156
157 cic_multichannel #(
158 .n_chan (nadc),
159 // DI parameters
160 .di_dwi (dw+davr),
161 .di_rwi (ow),
162 .di_noise_bits (1), // NOTE: Setting to 1 to compensate for removed /2 from double_inte
163 .cc_outw (rw),
164 .cc_halfband (0),
165 .cc_use_delay (0),
166 .cc_shift_base (shift_base))
167 i_cic_multichannel_q
168 (
169 .clk (clk),
170 .reset (reset),
171 .stb_in (1'b1),
172 .d_in (mixout_q),
173 .cic_sample (cic_sample),
174 .cc_sample (1'b1),
175 .cc_shift (cic_shift),
176 .di_stb_out (),
177 .di_sr_out (),
178 .cc_stb_out (), // Time-aligned with strobe_cc
179 .cc_sr_out (result_q)
180 );
181
182
183 // ---------------------
184 // Instantiate circle_buf_serial to store I and Q streams and handle channel masking
185 // ---------------------
186
187 assign boundary = ~strobe_cc;
188
189 // Run/stop mode based on trigger and trigger mode
190 wire buf_sync;
191 reg run=0;
192 always @(posedge clk) begin
193 if (trig_mode_r == 0) run <= 1;
194 if (trig_mode_r == 1 && buf_sync) run <= 0;
195 if (trig_mode_r != 0 && trig_actual) run <= 1;
196 end
197 wire circle_stb = strobe_cc & run;
198
199 // Circular buffer
200 // 2 * 20 bits from 2 * ccfilt, 8K depth * 2 I&Q channels means aw=13
201 // 2 * 8K * 2 * 20 = 640 kbits = 20 BRAM36 in Xilinx 7-series
202 wire [2*rw-1:0] circle_out;
203
204 circle_buf_serial #(
205 .n_chan (nadc),
206 .lsb_mask (1), // Right to left. LSB=CH0
207 .buf_aw (aw),
208 .buf_dw (2*rw),
209 .buf_auto_flip (0))
210 i_circle_buf_serial (
211 .iclk (clk),
212 .reset (reset),
213 .sr_in ({result_q,result_i}),
214 .sr_stb (circle_stb),
215 .chan_mask (keep),
216 .oclk (ro_clk),
217 .buf_sync (buf_sync),
218 .buf_transferred (),
219 .buf_stop (1'b0),
220 .buf_count (ro_status[31:16]),
221 .buf_stat2 (),
222 .buf_stat (ro_status[15:0]),
223 .debug_stat (),
224 .stb_out (ro_ack),
225 .enable (ro_enable),
226 .read_addr (ro_addr[aw-1:0]),
227 .d_out (circle_out)
228 );
229
230 // Form 32-bit output bus
231 reg [31:0] obus;
232 wire [1:0] obus_mode = ro_addr[aw+1:aw];
233 always @(*) casez (obus_mode)
234 2'b0?: obus = {circle_out[rw-1:rw-16], circle_out[2*rw-1:2*rw-16]};
235 2'b10: obus = {circle_out[ rw-1:0], {(32-rw){1'b0}}};
236 2'b11: obus = {circle_out[2*rw-1:rw], {(32-rw){1'b0}}};
237 endcase
238 assign ro_data = obus;
239
240endmodule