Attention

This documentation is a work in progress. Expect to see errors and unfinished things.

ssb_out Source File

 1`timescale 1ns / 1ns
 2// SSB stands for Single Side Band.
 3// https://en.wikipedia.org/wiki/Single-sideband_modulation
 4// (Almost) pin compatible with second_if_out,
 5// but this one relies on interpolators (afterburner) to up-sample the output
 6// data to DDR so it can drive a double-frequency DAC.
 7//
 8// It nominally produces an I and Q drive output but single-drive usage is possible
 9// by simply leaving dac2_out{0,1} floating, letting the synthesizer optimize-away
10// the redundant code.
11//
12// Directly uses the provided LO (cosa,sina), thus output IF will be determined by
13// the LO frequency
14
15module ssb_out (
16     input clk,
17     input [1:0] div_state,      // div_state [0] I-Q signal
18     input signed [17:0] drive,  // Baseband interleaved I-Q
19     input enable,               // Set output on enable else 0
20     input ssb_flip,             // Flips sign of dac2_out output pair
21     input [15:0] aftb_coeff,    // Coefficient to correct for the linear interpolation between
22                                 // two consecutive samples; see afterburner.v for details.
23                                 // Example based on FNAL test:
24                                 // 1313 MHz LO as timebase, / 16 to get 82.0625 MHz ADC clk
25                                 // IF is 13 MHz, 16/101 = 0.1584 of ADC clk
26                                 // aftb_coeff = ceil(32768*0.5*sec(2*pi*16/101/2)) = 18646
27     // local oscillator
28     input signed [17:0] cosa,
29     input signed [17:0] sina,
30     // DDR on both DACs
31     output signed [15:0] dac1_out0, // Nominally in-phase component
32     output signed [15:0] dac1_out1,
33     output signed [15:0] dac2_out0, // Nominally quadrature component
34     output signed [15:0] dac2_out1
35);
36
37wire iq = div_state[0];
38
39// Bring input I and Q to full data rate
40wire signed [16:0] drive_i, drive_q;
41fiq_interp interp(.clk(clk),
42     .a_data(drive[17:2]), .a_gate(1'b1), .a_trig(iq),
43     .i_data(drive_i), .q_data(drive_q));
44
45wire signed [15:0] out1, out2;
46
47// SSB modulation scheme (Hartley modulator) using dot-product for LSB selection:
48// (I, Q) . (cos(wLO*t), sin(wLO*t)) = I*cos(wLO*t) + Q*sin(wLO*t)
49
50// In-phase portion of SSB drive signal
51flevel_set level1(.clk(clk),
52     .cosd(cosa), .sind(sina),
53     .i_data(drive_i), .i_gate(1'b1), .i_trig(1'b1),
54     .q_data(drive_q), .q_gate(1'b1), .q_trig(1'b1),
55     .o_data(out1));
56
57wire signed [15:0] outk1 = enable ? out1 : 0;
58
59wire [15:0] dac1_ob0, dac1_ob1;  // offset binary outputs from afterburner
60afterburner afterburner1(.clk(clk),
61     .data({outk1,1'b0}), .coeff(aftb_coeff),
62     .data_out0(dac1_ob0), .data_out1(dac1_ob1));
63
64// Second (optional) dot-product with 90deg-rotated drive IQ to obtain quadrature component
65
66// Quadrature portion of SSB drive signal
67flevel_set level2(.clk(clk),
68     .cosd(cosa), .sind(sina),
69     .i_data(~drive_q), .i_gate(1'b1), .i_trig(1'b1),
70     .q_data(drive_i), .q_gate(1'b1), .q_trig(1'b1),
71     .o_data(out2));
72
73wire signed [15:0] outf2 = ssb_flip ? ~out2 : out2;
74wire signed [15:0] outk2 = enable ? outf2 : 0;
75
76wire [15:0] dac2_ob0, dac2_ob1;  // offset binary outputs from afterburner
77afterburner afterburner2(.clk(clk),
78     .data({outk2,1'b0}), .coeff(aftb_coeff),
79     .data_out0(dac2_ob0), .data_out1(dac2_ob1));
80
81// afterburner returns offset-binary DAC words, but this module uses signed
82// (twos-complement) for its output.  Thus the conversions below.
83assign dac1_out0 = {~dac1_ob0[15], dac1_ob0[14:0]};
84assign dac1_out1 = {~dac1_ob1[15], dac1_ob1[14:0]};
85assign dac2_out0 = {~dac2_ob0[15], dac2_ob0[14:0]};
86assign dac2_out1 = {~dac2_ob1[15], dac2_ob1[14:0]};
87
88endmodule