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