aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/rfnoc/dds_wrapper.v
blob: d9151afa8d0b420dc1bfe08d7789b0bcce723493 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//
// Copyright 2021 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: dds_wrapper
//
// Description:
//
//   This module computes the complex number e^(j*2*pi*phase). The phase input
//   can be thought of as a 24-bit unsigned fixed-point value with 24
//   fractional bits. In other words, the integer range of the input maps to a
//   phase in the range [0, 1.0). The output consists of two 16-bit signed
//   fixed-point values with 14 fractional bits. The value sin(2*pi*phase) is
//   in the upper 16 bits and cos(2*pi*phase) is in the lower 16-bits. This
//   puts the output in {Q,I} order.
//
//   This is a wrapper for the dds_sin_cos_lut_only IP, which is based on the
//   Xilinx DDS Compiler. This IP has the undesirable behavior that input must
//   be provided to flush out any data stuck in its pipeline. This wrapper
//   hides that behavior so that every input causes a corresponding output,
//   even if the input stops.
//
//   NOTE: The DDS IP requires at least 2 cycles of reset.
//
// Parameters:
//
//   The parameters in this module should not be modified. They match the IP
//   configuration.
//


module dds_wrapper #(
  parameter PHASE_W  = 24,
  parameter OUTPUT_W = 32
) (
  input  wire                clk,
  input  wire                rst,

  // Phase input
  input  wire [ PHASE_W-1:0] s_axis_phase_tdata,
  input  wire                s_axis_phase_tvalid,
  input  wire                s_axis_phase_tlast,
  output wire                s_axis_phase_tready,

  // IQ output (Q in the upper, I in the lower bits)
  output wire [OUTPUT_W-1:0] m_axis_data_tdata,
  output wire                m_axis_data_tvalid,
  output wire                m_axis_data_tlast,
  input  wire                m_axis_data_tready
);

  // Width of number needed to represent the DDS fullness. This value was
  // determined experimentally. The max fullness was 33.
  localparam FULLNESS_W = 6;

  wire [PHASE_W-1:0] phase_tdata;
  wire               phase_tvalid;
  wire               phase_tlast;
  wire               phase_tready;

  wire [OUTPUT_W-1:0] dds_tdata;
  wire                dds_tvalid;
  wire                dds_tlast;
  wire                dds_tready;


  //---------------------------------------------------------------------------
  // DDS Fullness Counter
  //---------------------------------------------------------------------------
  //
  // Count the number of valid samples in the DDS's data pipeline.
  //
  //---------------------------------------------------------------------------

  // The fullness counter must be large enough for DDS's latency.
  reg [FULLNESS_W-1:0] fullness     = 0;
  reg                  dds_has_data = 0;

  wire increment = s_axis_phase_tvalid & s_axis_phase_tready;
  wire decrement = m_axis_data_tvalid  & m_axis_data_tready;

  always @(posedge clk) begin
    if (rst) begin
      fullness     <= 0;
      dds_has_data <= 0;
    end else begin
      if (increment && !decrement) begin
        //synthesis translate_off
        if (fullness+1'b1 == 1'b0) begin
          $display("ERROR: Fullness overflowed!");
        end
        //synthesis translate_on
        fullness <= fullness + 1;
        dds_has_data <= 1;
      end else if (decrement && !increment) begin
        //synthesis translate_off
        if (fullness-1'b1 > fullness) begin
          $display("ERROR: Fullness underflowed!");
        end
        //synthesis translate_on
        fullness <= fullness - 1;
        dds_has_data <= (fullness > 1);
      end else begin
        dds_has_data <= (fullness > 0);
      end
    end
  end


  //---------------------------------------------------------------------------
  // Input Logic
  //---------------------------------------------------------------------------

  assign s_axis_phase_tready = phase_tready;
  assign phase_tlast         = s_axis_phase_tlast;
  assign phase_tdata         = s_axis_phase_tdata;

  // Always input something when the DDS has data stuck inside it so that all
  // data gets flushed out automatically.
  assign phase_tvalid = s_axis_phase_tvalid || dds_has_data;


  //---------------------------------------------------------------------------
  // DDS IP
  //---------------------------------------------------------------------------

  // Use the TUSER path on the DDS IP to indicate if the sample is empty and is
  // just to flush the output.
  wire flush_in = ~s_axis_phase_tvalid; // It's a flush if input is not valid
  wire flush_out;

  dds_sin_cos_lut_only dds_sin_cos_lut_only_i (
    .aclk                (clk),
    .aresetn             (~rst),
    .s_axis_phase_tvalid (phase_tvalid),
    .s_axis_phase_tready (phase_tready),
    .s_axis_phase_tdata  (phase_tdata),
    .s_axis_phase_tlast  (phase_tlast),
    .s_axis_phase_tuser  (flush_in),
    .m_axis_data_tvalid  (dds_tvalid),
    .m_axis_data_tready  (dds_tready),
    .m_axis_data_tdata   (dds_tdata),
    .m_axis_data_tlast   (dds_tlast),
    .m_axis_data_tuser   (flush_out)
  );


  //---------------------------------------------------------------------------
  // Output Logic
  //---------------------------------------------------------------------------

  assign m_axis_data_tdata = dds_tdata;
  assign m_axis_data_tlast = dds_tlast;

  // Discard the current sample if it was for flushing.
  assign m_axis_data_tvalid = dds_tvalid & ~flush_out;
  assign dds_tready         = m_axis_data_tready | flush_out;

endmodule