OpenCores
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
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.