OpenCores
URL https://opencores.org/ocsvn/wbpwmaudio/wbpwmaudio/trunk

Subversion Repositories wbpwmaudio

[/] [wbpwmaudio/] [trunk/] [rtl/] [wbpwmaudio.v] - Blame information for rev 4

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 4 dgisselq
////////////////////////////////////////////////////////////////////////////////
2 2 dgisselq
//
3
// Filename:    wbpwmaudio.v
4 4 dgisselq
//
5 2 dgisselq
// Project:     A Wishbone Controlled PWM (audio) controller
6
//
7
// Purpose:     This PWM controller was designed with audio in mind, although
8
//              it should be sufficient for many other purposes.  Specifically,
9
//      it creates a pulse-width modulated output, where the amount of time
10
//      the output is 'high' is determined by the pulse width data given to
11
//      it.  Further, the 'high' time is spread out in bit reversed order.
12
//      In this fashion, a halfway point will alternate between high and low,
13
//      rather than the normal fashion of being high for half the time and then
14
//      low.  This approach was chosen to move the PWM artifacts to higher,
15
//      inaudible frequencies and hence improve the sound quality.
16
//
17
//      The interface supports two addresses:
18
//
19
//      Addr[0] is the data register.  Writes to this register will set
20
//              a 16-bit sample value to be produced by the PWM logic.
21
//              Reads will also produce, in the 17th bit, whether the interrupt
22
//              is set or not.  (If set, it's time to write a new data value
23
//              ...)
24
//
25 4 dgisselq
//      Addr[1] is a timer reload value, used to determine how often the
26 2 dgisselq
//              PWM logic needs its next value.  This number should be set
27
//              to the number of clock cycles between reload values.  So,
28
//              for example, an 80 MHz clock can generate a 44.1 kHz audio
29
//              stream by reading in a new sample every (80e6/44.1e3 = 1814)
30
//              samples.  After loading a sample, the device is immediately
31
//              ready to load a second.  Once the first sample completes,
32
//              the second sample will start going to the output, and an
33
//              interrupt will be generated indicating that the device is
34
//              now ready for the third sample.  (The one sample buffer
35
//              allows some flexibility in getting the new sample there fast
36
//              enough ...)
37
//
38
//
39
//      If you read through the code below, you'll notice that you can also
40
//      set the timer reload value to an immutable constant by changing the
41
//      VARIABLE_RATE parameter to 0.  When VARIABLE_RATE is set to zero,
42
//      both addresses become the same, Addr[0] or the data register, and the
43
//      reload value can no longer be changed--forcing the sample rate to
44
//      stay constant.
45
//
46
//
47
//      Of course, if you don't want to deal with the interrupts or sample
48
//      rates, you can still get a pseudo analog output by just setting the
49
//      value to the analog output you would like and then not updating
50
//      it.  In this case, you could also shut the interrupt down at the
51
//      controller, to keep that from bothering you as well.
52
//
53
// Creator:     Dan Gisselquist, Ph.D.
54
//              Gisselquist Technology, LLC
55
//
56 4 dgisselq
////////////////////////////////////////////////////////////////////////////////
57 2 dgisselq
//
58
// Copyright (C) 2015, Gisselquist Technology, LLC
59
//
60
// This program is free software (firmware): you can redistribute it and/or
61 4 dgisselq
// modify it under the terms of the GNU General Public License as published
62 2 dgisselq
// by the Free Software Foundation, either version 3 of the License, or (at
63
// your option) any later version.
64
//
65
// This program is distributed in the hope that it will be useful, but WITHOUT
66
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
67
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
68
// for more details.
69
//
70
// You should have received a copy of the GNU General Public License along
71
// with this program.  (It's in the $(ROOT)/doc directory.  Run make with no
72
// target there if the PDF file isn't present.)  If not, see
73
// <http://www.gnu.org/licenses/> for a copy.
74
//
75
// License:     GPL, v3, as defined and found on www.gnu.org,
76
//              http://www.gnu.org/licenses/gpl.html
77
//
78
//
79 4 dgisselq
////////////////////////////////////////////////////////////////////////////////
80
//
81
//
82
`default_nettype        none
83
//
84
module  wbpwmaudio(i_clk, i_reset,
85 2 dgisselq
                // Wishbone interface
86
                i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data,
87
                        o_wb_ack, o_wb_stall, o_wb_data,
88 3 dgisselq
                o_pwm, o_aux, o_int);
89 4 dgisselq
        parameter       DEFAULT_RELOAD = 16'd1814, // about 44.1 kHz @  80MHz
90
                        //DEFAULT_RELOAD = 16'd2268,//about 44.1 kHz @ 100MHz
91 2 dgisselq
                        NAUX=2, // Dev control values
92 4 dgisselq
                        VARIABLE_RATE=0,
93
                        TIMING_BITS=16;
94
        input   wire            i_clk, i_reset;
95
        input   wire            i_wb_cyc, i_wb_stb, i_wb_we;
96
        input   wire            i_wb_addr;
97
        input   wire    [31:0]   i_wb_data;
98 2 dgisselq
        output  reg             o_wb_ack;
99
        output  wire            o_wb_stall;
100
        output  wire    [31:0]   o_wb_data;
101
        output  reg             o_pwm;
102
        output  reg     [(NAUX-1):0]     o_aux;
103 4 dgisselq
        output  wire            o_int;
104 2 dgisselq
 
105
 
106
        // How often shall we create an interrupt?  Every reload_value clocks!
107
        // If VARIABLE_RATE==0, this value will never change and will be kept
108 3 dgisselq
        // at the default reload rate (defined up top)
109
        wire    [(TIMING_BITS-1):0]      w_reload_value;
110 2 dgisselq
        generate
111
        if (VARIABLE_RATE != 0)
112
        begin
113 3 dgisselq
                reg     [(TIMING_BITS-1):0]      r_reload_value;
114 2 dgisselq
                initial r_reload_value = DEFAULT_RELOAD;
115
                always @(posedge i_clk) // Data write
116 4 dgisselq
                        if ((i_wb_stb)&&(i_wb_addr)&&(i_wb_we))
117
                                r_reload_value <= i_wb_data[(TIMING_BITS-1):0] - 1'b1;
118 2 dgisselq
                assign  w_reload_value = r_reload_value;
119
        end else begin
120
                assign  w_reload_value = DEFAULT_RELOAD;
121
        end endgenerate
122
 
123 4 dgisselq
        //
124
        // The next value timer
125
        //
126
        // We'll want a new sample every w_reload_value clocks.  When the
127
        // timer hits zero, the signal ztimer (zero timer) will also be
128
        // set--allowing following logic to depend upon it.
129
        //
130
        reg                             ztimer;
131 3 dgisselq
        reg     [(TIMING_BITS-1):0]      timer;
132 2 dgisselq
        initial timer = DEFAULT_RELOAD;
133 4 dgisselq
        initial ztimer= 1'b0;
134 2 dgisselq
        always @(posedge i_clk)
135 4 dgisselq
                if (i_reset)
136
                        ztimer <= 1'b0;
137
                else
138
                        ztimer <= (timer == { {(TIMING_BITS-1){1'b0}}, 1'b1 });
139
 
140
        always @(posedge i_clk)
141
                if ((ztimer)||(i_reset))
142 3 dgisselq
                        timer <= w_reload_value;
143 2 dgisselq
                else
144 3 dgisselq
                        timer <= timer - {{(TIMING_BITS-1){1'b0}},1'b1};
145 2 dgisselq
 
146 4 dgisselq
        //
147
        // Whenever the timer runs out, accept the next value from the single
148
        // sample buffer.
149
        //
150 2 dgisselq
        reg     [15:0]   sample_out;
151
        always @(posedge i_clk)
152 4 dgisselq
                if (ztimer)
153 2 dgisselq
                        sample_out <= next_sample;
154
 
155
 
156 4 dgisselq
        //
157
        // Control what's in the single sample buffer, next_sample, as well as
158
        // whether or not it's a valid sample.  Specifically, if next_valid is
159
        // false, then the sample buffer needs a new value.  Once the buffer
160
        // has a value within it, further writes will just quietly overwrite
161
        // this value.
162 2 dgisselq
        reg     [15:0]   next_sample;
163
        reg             next_valid;
164
        initial next_valid = 1'b1;
165
        initial next_sample = 16'h8000;
166
        always @(posedge i_clk) // Data write
167 4 dgisselq
                if ((i_wb_stb)&&(i_wb_we)
168
                                &&((!i_wb_addr)||(VARIABLE_RATE==0)))
169 2 dgisselq
                begin
170
                        // Write with two's complement data, convert it
171 4 dgisselq
                        // internally to an unsigned binary offset
172
                        // representation
173
                        next_sample <= { !i_wb_data[15], i_wb_data[14:0] };
174 2 dgisselq
                        next_valid <= 1'b1;
175
                        if (i_wb_data[16])
176
                                o_aux <= i_wb_data[(NAUX+20-1):20];
177 4 dgisselq
                end else if (ztimer)
178 2 dgisselq
                        next_valid <= 1'b0;
179
 
180 4 dgisselq
        // If the value in our sample buffer isn't valid, create an interrupt
181
        // that can be sent to a processor to know when to send a new sample.
182
        // This output can also be used to control a read from a FIFO as well,
183
        // depending on how you wish to use the core.
184
        assign  o_int = (!next_valid);
185 2 dgisselq
 
186 4 dgisselq
        //
187
        // To generate our waveform, we'll compare our sample value against
188
        // a bit reversed counter.  This counter is kept in pwm_counter.
189
        // The choice of a 16-bit counter is arbitrary, but it was made to
190
        // match the sixteen bits of the input
191 2 dgisselq
        reg     [15:0]   pwm_counter;
192
        initial pwm_counter = 16'h00;
193
        always @(posedge i_clk)
194 4 dgisselq
                if (i_reset)
195
                        pwm_counter <= 16'h0;
196
                else
197
                        pwm_counter <= pwm_counter + 16'h01;
198 2 dgisselq
 
199 4 dgisselq
        // Bit-reverse the counter
200 2 dgisselq
        wire    [15:0]   br_counter;
201
        genvar  k;
202
        generate for(k=0; k<16; k=k+1)
203
        begin : bit_reversal_loop
204
                assign br_counter[k] = pwm_counter[15-k];
205
        end endgenerate
206
 
207 4 dgisselq
        // Apply our comparison to determine the next output bit
208 2 dgisselq
        always @(posedge i_clk)
209
                o_pwm <= (sample_out >= br_counter);
210
 
211 4 dgisselq
        //
212
        // Handle the bus return traffic.
213 2 dgisselq
        generate
214
        if (VARIABLE_RATE == 0)
215
        begin
216 4 dgisselq
                // If we are running off of a fixed rate, then just return
217
                // the current setting of the aux registers, the current
218
                // interrupt value, and the current sample we are outputting.
219 2 dgisselq
                assign o_wb_data = { {(12-NAUX){1'b0}}, o_aux,
220
                                        3'h0, o_int, sample_out };
221
        end else begin
222 4 dgisselq
                // On the other hand, if we have been built to support a
223
                // variable sample rate, then return the reload value for
224
                // address one but otherwise the data value (above) for address
225
                // zero.
226 2 dgisselq
                reg     [31:0]   r_wb_data;
227
                always @(posedge i_clk)
228
                        if (i_wb_addr)
229 4 dgisselq
                                r_wb_data <= { (32-TIMING_BITS),w_reload_value};
230 2 dgisselq
                        else
231
                                r_wb_data <= { {(12-NAUX){1'b0}}, o_aux,
232
                                                3'h0, o_int, sample_out };
233
                assign  o_wb_data = r_wb_data;
234
        end endgenerate
235
 
236 4 dgisselq
        // Always ack on the clock following any request
237 2 dgisselq
        initial o_wb_ack = 1'b0;
238
        always @(posedge i_clk)
239 4 dgisselq
                o_wb_ack <= (i_wb_stb);
240
 
241
        // Never stall
242 2 dgisselq
        assign  o_wb_stall = 1'b0;
243
 
244 4 dgisselq
        // Make Verilator happy.  Since we aren't using all of the bits from
245
        // the bus, Verilator -Wall will complain.  This just informs
246
        // V*rilator that we already know these bits aren't being used.
247
        //
248
        // verilator lint_off UNUSED
249
        wire    [14:0] unused;
250
        assign  unused = { i_wb_cyc, i_wb_data[31:21], i_wb_data[19:17] };
251
        // verilator lint_on  UNUSED
252
 
253 2 dgisselq
endmodule

powered by: WebSVN 2.1.0

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