Line 60... |
Line 60... |
// = 123 * sample ~= 128 * sample = sample << 7.
|
// = 123 * sample ~= 128 * sample = sample << 7.
|
//
|
//
|
// Thus, by shifting our input sample value a touch, we can multiply by
|
// Thus, by shifting our input sample value a touch, we can multiply by
|
// nearly the exact constant we want.
|
// nearly the exact constant we want.
|
//
|
//
|
|
// OSERDES:
|
|
// Okay, the first version was fun and worked ... okay, but ... can we do
|
|
// better? I mean, we lost over 10dB by undersampling, and most(many?)
|
|
// FPGA's have OSERDES components that will allow bits to toggle faster
|
|
// than the FPGA clock rate. In other words, if we have an 80MHz clock,
|
|
// we should be able to output samples at 320MHz, no?
|
|
//
|
|
// Let's do even one better than that: suppose we create outputs for a
|
|
// 2-bit DAC. Of course, our chip doesn't have a 2-bit DAC, much less a
|
|
// DAC at all, but could we create one from our I/O pins? For example,
|
|
// if two I/O pins both produced a 1, the resulting field would be
|
|
// stronger, right? What if they both produced a zero, same thing, right?
|
|
// Now, what if one produced a 1 and one produced a 0? Would the fields
|
|
// interfere with each other? You know, would they produce a sum field
|
|
// that was better than just the one-bit produced field? Perhaps, with
|
|
// only a 1-bit output, we get +/- 3. With a two bit output, we should
|
|
// be able to get { -3, 0, 3 }, right? (Ignore scaling ...)
|
|
//
|
|
// A better two-bit output would probably be something like
|
|
// { -3, -1, 1, 3 }. How can we produce something like that? Without a
|
|
// proper ADC? Can we connect a majority of the pins to the high order
|
|
// bit output, and some fewer number to the low order bit output?
|
|
// Would this create a better field?
|
|
//
|
|
// The component created with `define OSERDES is designed to allow such
|
|
// hypotheses to be tested.
|
|
//
|
// Creator: Dan Gisselquist, Ph.D.
|
// Creator: Dan Gisselquist, Ph.D.
|
// Gisselquist Technology, LLC
|
// Gisselquist Technology, LLC
|
//
|
//
|
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
//
|
//
|
Line 102... |
Line 129... |
input i_wb_addr;
|
input i_wb_addr;
|
input [31:0] i_wb_data;
|
input [31:0] i_wb_data;
|
output reg o_wb_ack;
|
output reg o_wb_ack;
|
output wire o_wb_stall;
|
output wire o_wb_stall;
|
output reg [31:0] o_wb_data;
|
output reg [31:0] o_wb_data;
|
|
`ifdef USE_OSERDES
|
|
output wire [7:0] o_tx;
|
|
`else
|
output wire o_tx;
|
output wire o_tx;
|
|
`endif
|
output reg o_int;
|
output reg o_int;
|
|
|
reg [31:0] nco_step, nco_phase;
|
reg [31:0] nco_step;
|
|
|
// How often shall we create an interrupt? Every reload_value clocks!
|
// How often shall we create an interrupt? Every reload_value clocks!
|
// If VARIABLE_RATE==0, this value will never change and will be kept
|
// If VARIABLE_RATE==0, this value will never change and will be kept
|
// at the default reload rate (44.1 kHz, for a 100 MHz clock)
|
// at the default reload rate (44.1 kHz, for a 100 MHz clock)
|
reg [15:0] reload_value;
|
reg [15:0] reload_value;
|
Line 166... |
Line 197... |
// interrupt controller.
|
// interrupt controller.
|
initial o_int = 1'b0;
|
initial o_int = 1'b0;
|
always @(posedge i_clk)
|
always @(posedge i_clk)
|
o_int <= (~next_valid);
|
o_int <= (~next_valid);
|
|
|
|
`ifdef USE_OSERDES
|
|
// If we use an OSERDES on our final output, we should be able to
|
|
// oversample by a factor of 4x (or perhaps more, but this works the
|
|
// 4x number). Here is an example of figuring out what both of those
|
|
// 4x oversamples are--first the primary, calculated as before, but then
|
|
// also the alternate.
|
|
reg [31:0] nco_phase, nco_phase_a, nco_phase_b, nco_phase_c,
|
|
sample_step, tripl_step, tripl_nco_step;
|
|
initial nco_base = 32'h00;
|
|
|
|
always @(posedge i_clk)
|
|
if (ztimer)
|
|
sample_step <= nco_step
|
|
+ { {(32-16-5){next_sample[15]}}, next_sample, 5'h00 };
|
|
|
|
// Multiply by three ... never that easy
|
|
always @(posedge i_clk)
|
|
tripl_nco_step <= nco_step + { nco_step[30:0], 1'b0 };
|
|
always @(posedge i_clk)
|
|
if (ztimer)
|
|
tripl_step <= tripl_nco_step
|
|
+ { {(32-16-5){next_sample[15]}}, next_sample, 5'h00 };
|
|
+ { {(32-16-6){next_sample[15]}}, next_sample, 6'h00 };
|
|
|
|
wire [31:0] base_step;
|
|
assign nco_base_step = sample_step;
|
|
always @(posedge i_clk)
|
|
nco_phase_a <= nco_phase + sample_step;
|
|
always @(posedge i_clk)
|
|
nco_phase_b <= nco_phase + { sample_step[30:0], 1'b0 };
|
|
always @(posedge i_clk)
|
|
nco_phase_c <= nco_phase + tripl_step;
|
|
always @(posedge i_clk)
|
|
nco_phase <= nco_phase + { sample_step[29:0], 2'b00 };
|
|
|
|
// Output a two-bit waveform. Send each bit to GPIO port(s), with
|
|
// roughly the same number of ports per bit.
|
|
assign o_tx = { nco_phase_a[31:30], nco_base_b[31:30],
|
|
nco_phase_c[31:30], nco_phase[31:30] };
|
|
`else
|
// Adjust the gain for a maximum frequency offset just greater than
|
// Adjust the gain for a maximum frequency offset just greater than
|
// 75 kHz. (We would've done 75kHz exactly, but it required a multiply
|
// 75 kHz. (We would've done 75kHz exactly, but it required a multiply
|
// and this doesn't.)
|
// and this doesn't.)
|
|
reg [31:0] nco_phase;
|
initial nco_phase = 32'h00;
|
initial nco_phase = 32'h00;
|
always @(posedge i_clk)
|
always @(posedge i_clk)
|
nco_phase <= nco_phase + nco_step
|
nco_phase <= nco_phase + nco_step
|
+ { {(32-16-7){sample_out[15]}}, sample_out, 7'h00 };
|
+ { {(32-16-7){sample_out[15]}}, sample_out, 7'h00 };
|
assign o_tx = nco_phase[31];
|
assign o_tx = nco_phase[31];
|
|
`endif
|
|
|
always @(posedge i_clk)
|
always @(posedge i_clk)
|
if (i_wb_addr)
|
if (i_wb_addr)
|
o_wb_data <= nco_step;
|
o_wb_data <= nco_step;
|
else
|
else
|