URL
https://opencores.org/ocsvn/wbpwmaudio/wbpwmaudio/trunk
Subversion Repositories wbpwmaudio
[/] [wbpwmaudio/] [trunk/] [demo-rtl/] [toplevel.v] - Rev 4
Compare with Previous | Blame | View Log
//////////////////////////////////////////////////////////////////////////////// // // Filename: toplevel.v // // Project: A Wishbone Controlled PWM (audio) controller // // Purpose: This is the top level file in Verilog demonstration illustrating // the differences between a traditional PWM generated audio // output signal, generated by traditionalpwm.v, and a less conventional // PWM generation method more appropriately termed PDM provided by // wbpwmaudio.v. // // Creator: Dan Gisselquist, Ph.D. // Gisselquist Technology, LLC // //////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2017, Gisselquist Technology, LLC // // This program is free software (firmware): you can redistribute it and/or // modify it under the terms of the GNU General Public License as published // by the Free Software Foundation, either version 3 of the License, or (at // your option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License along // with this program. (It's in the $(ROOT)/doc directory. Run make with no // target there if the PDF file isn't present.) If not, see // <http://www.gnu.org/licenses/> for a copy. // // License: GPL, v3, as defined and found on www.gnu.org, // http://www.gnu.org/licenses/gpl.html // // //////////////////////////////////////////////////////////////////////////////// // // `default_nettype none // module toplevel(i_clk, i_sw, o_led, o_shutdown_n, o_gain, o_pwm); input wire i_clk, i_sw; output wire o_led; output wire o_shutdown_n, o_gain; output reg o_pwm; // 8 second repeating test sequence // // 0: Off // 1: Off // 2: 440 Hz // 3: 440 Hz (continued) // 4: off // 5: Sweep, starting at 110Hz // 6: Sweep (continued) // 7: Sweep continued, but with a small discontinuity reg [36:0] test_params [0:7]; reg [36:0] params; initial begin // Sweep starts from 110 Hz test_params[0] = { 2'b11, // Start at 110 Hz 34'd18_898, // 0x49d2 // Sweep 1'b1 }; test_params[1] = { 2'b11, 34'd158_1398, 1'b1 }; test_params[2] = { 2'b11, 34'd314_3898, 1'b1 }; // One second of silence test_params[3] = { 2'b00, 34'd0, 1'b0 }; // 440 Hz tone for two seconds test_params[4] = { 2'b11, // 440Hz Tone frequency / 100MHz clock rate * 2^34 34'd75_591, // No sweep rate 1'b0 }; test_params[5] = { 2'b11, 34'd75_591, 1'b0 }; // Off again for two seconds prior to the next step test_params[6] = { 2'b00, 34'd0, 1'b0 }; test_params[7] = { 2'b00, 34'd0, 1'b0 }; end reg seq_sweep; reg [1:0] seq_aux; reg [33:0] seq_step; reg [31:0] sub_second_counter; reg next_second; // We'll step through the sequence once per second. Hence, our // full test-sequence will take 8-seconds. // // Artificially start just before the top of a second initial next_second = 0; initial sub_second_counter = 32'hffff_fff0; always @(posedge i_clk) // 32'd43 is taken from 2^32 / CLOCK_FREQUENCY and then rounded // to the nearest integer. Overflows set next_second for one // clock period only. { next_second, sub_second_counter } <= sub_second_counter + 32'd43; // Artificially start at the beginning of the frequency sweep. reg test_reset; reg [2:0] test_state; initial test_state = 3'b000; always @(posedge i_clk) // Advance the test on the top of each second if (next_second) test_state <= test_state + 1'b1; // At the top of the last second, apply a reset to everything, so we // know we come back to the same values. In reality, all this signal // is going to do is to make sure our two components under test start // in the exact same configuration. always @(posedge i_clk) test_reset <= (next_second)&&(test_state == 3'h7); // Read our parameters for the next test. These will be applied at the // top of the next second. always @(posedge i_clk) params <= test_params[test_state]; reg sub_sweep_overflow; reg [5:0] sub_sweep; initial sub_sweep = 0; initial sub_sweep_overflow = 0; always @(posedge i_clk) if (next_second) begin // seq_aux controls the gain and shutdown values seq_aux <= params[36:35]; // seq_step controls our output frequency. It's // defined as a step in phase seq_step <= params[34:1]; // seq_sweep is a single bit indicating whether or not // we are sweeping or not. seq_sweep <= params[0]; end else begin // At each second, add one to the frequency if our // sweep step counter overflowed. seq_step <= seq_step + {{(33){1'b0}}, sub_sweep_overflow}; // Calculate a sweep step counter. This is a six // bit counter (0...63). When it overflows, we'll // increase our phase step amount (seq_step) { sub_sweep_overflow, sub_sweep } <= sub_sweep + { {(5){1'b0}}, (seq_sweep) }; end // Generating either a tone or a swept tone requires a phase, which we // can then use to calculate a sin() and cos(). The change in this // phase value is proportional to the frequency of this phase. reg [33:0] test_phase; always @(posedge i_clk) test_phase <= test_phase + seq_step; // We're not going to use all of the capabilities of our CORDIC // sin() and cos() generator. In particular, we're going to ignore // the sin() output and the aux synchronization output. Since // V*rilator will otherwise complain about this if we turn -Wall on // (Thank you, verilator), we'll tell it during the definition of // these values that we aren't going to use them. // // verilator lint_off UNUSED wire ignore_aux; wire [15:0] geny; // verilator lint_on UNUSED // The result we want from the cordic, though, is the cos() result. // this will be used. wire signed [15:0] signal; // Here's a straight CORDIC. The inputs to this should be fairly // self-explanatory, with the exception of the 16'h6de0. This value // is the gain necessary to cause the CORDIC to produce a full-range // output value. This gain is calculated internal to the CORDIC // core generator, but can also be found within the cordic.v code // following the cordic_angle[] array initialization. In our case, // we've taken the top 15-bits of the gain annihilator, prepended // a zero sign bit, and used that for our value here. See the // discussion on CORDIC's, and the CORDIC file for more info. // // The value was dropped from 16'h6dec to 16'h6de8 to provide some // cushion against overflow. localparam [15:0] FULL_SCALE = 16'h6de8, SMALL_SCALE= 16'h006e; localparam [15:0] ACTUAL_SCALE = SMALL_SCALE; cordic gentone(i_clk, test_reset, 1'b1, ACTUAL_SCALE, 16'h0, test_phase[33:9], 1'b0, signal, geny, ignore_aux); // Our goal is to calculate two outputs: one from the PDM, one from // the PWM. wire pdm_out, pwm_out; // verilator lint_off UNUSED wire ignore_ack, ignore_stall, ignore_int, trad_ack, trad_stall, trad_int; wire [31:0] ignore_data, trad_data; wire ignore_pwm_aux, trad_pwm_aux; // verilator lint_on UNUSED // Here's our component under test. // // DEFAULT_RELOAD of 3125 comes from a request to do 32kHz sampling. // 100e6/32e3 = 3125 // VARIABLE_RATE(0) keeps us from needing to adjust (or set) this // sampling rate for this test // The NAUX value is normally used to control the shutdown and gain // pins. Since we're handling theses via another fashion, we'll just // set this to (1) and ignore the aux pins. // // As for the wishbone interface, if all we do is set CYC, STB, and WE // then the component will accept a value anytime it is ready for the // next sample. This allows us to ignore the rest of the wishbone // interface (ack, stall, data, etc.) Finally, we'll also ignore the // interrupt output from the core simply because we don't need it for // this test setup. // localparam signed [15:0] DR_44_1KHZ = 16'd2268, DR_32KHZ = 16'd3125, DR_8KHZ = 16'd12500; localparam [15:0] DEF_RELOAD = DR_8KHZ; localparam signed [15:0] HALF_DR = DEF_RELOAD[15:1] - 3; wbpwmaudio #(.DEFAULT_RELOAD(DEF_RELOAD), .VARIABLE_RATE(0), .NAUX(1)) genpwm(i_clk, test_reset, 1'b1, 1'b1, 1'b1, 1'b0, { 16'h0, signal[14:0], 1'b0 }, ignore_ack, ignore_stall, ignore_data, pdm_out, ignore_pwm_aux, ignore_int); // // Now for the control. // // The hypothesis under test is that the waveform above will more // faithfully generate an audio signal (tone or swept tone) than this // control signal does. To make this claim, we need to compare it // against something. Hence, our traditional PWM component. // One of the difficulties of traditional PWM signals is that they // can only handle values between zero and the number of clocks in // their period. Hence, we'll multiply this by just less than half // of 3125 to get us into the range of -3125/2 to 3125/2. Inside the // component, 3125/2 will be added to the value. Because 3125/2 isn't // an integer, this will produce a DC bias. reg signed [31:0] scaled_signal; always @(posedge i_clk) scaled_signal <= signal * HALF_DR; // Since we're only going to use the top 16-bits, let's tell V*rilator // that the bottom sixteen bits are unused. // // Well, ... not quite. We're only using bits 29:14. Hence we need // to tell Verilator that the other bits are unused. Since adjusting // for the scale factor, the number of "unused" bits declared below is a // touch more than the actual unused bits, but this should just allow // us to modify things without Verilator complaining at us. // // verilator lint_off UNUSED wire [17:0] unused; assign unused = { scaled_signal[31:30], scaled_signal[15:0] }; // verilator lint_on UNUSED // // Here's the traditional PWM component. It's interface is nearly // identical to the component above. traditionalpwm #(.DEFAULT_RELOAD(DEF_RELOAD), .VARIABLE_RATE(0), .NAUX(1)) regpwm(i_clk, test_reset, 1'b1, 1'b1, 1'b1, 1'b0, { 16'h0, scaled_signal[29:14] }, trad_ack, trad_stall, trad_data, pwm_out, trad_pwm_aux, trad_int); // As a last step, use the switch to control which value actually goes // out the output port. always @(posedge i_clk) if (i_sw) o_pwm <= pdm_out; else o_pwm <= pwm_out; // Turn the LED "on" if we are producing the improved signal, leave // it off otherwise. assign o_led = i_sw; // Finally, use the seq_aux values to control the two audio control // pins. assign o_shutdown_n = seq_aux[1]; assign o_gain = seq_aux[0]; endmodule