replacing remaining pwmnum
[pinmux.git] / src / bsv / bsv_lib / pwm.bsv
1 /*
2 Copyright (c) 2013, IIT Madras
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8
9 * Redistributions of source code must retain the above copyright notice,
10 this list of conditions and the following disclaimer.
11 * Redistributions in binary form must reproduce the above copyright notice,
12 this list of conditions and the following disclaimer in the documentation
13 and/or other materials provided with the distribution.
14 * Neither the name of IIT Madras nor the names of its contributors may be
15 used to endorse or promote products derived from this software without
16 specific prior written permission.
17
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
24 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 -------------------------------------------------------------------------------------------------
30
31 Code inpired by the pwm module at: https://github.com/freecores/pwm
32
33 */
34 package pwm;
35 `define PWMWIDTH 32
36 /*=== Project imports ==*/
37 import Clocks::*;
38 /*======================*/
39 /*== Package imports ==*/
40 //import defined_types::*;
41 `include "instance_defines.bsv"
42 import ClockDiv::*;
43 import ConcatReg::*;
44 import Semi_FIFOF::*;
45 import BUtils ::*;
46 `ifdef PWM_AXI4Lite
47 import AXI4_Lite_Types::*;
48 `endif
49 `ifdef PWM_AXI4
50 import AXI4_Types::*;
51 `endif
52 /*======================*/
53
54 interface UserInterface;
55 method ActionValue#(Bool) write(Bit#(`PADDR) addr, Bit#(`Reg_width) data);
56 method Tuple2#(Bool, Bit#(`Reg_width)) read(Bit#(`PADDR) addr);
57 endinterface
58
59 interface PWMIO;
60 method Bit#(1) pwm_o;
61 endinterface
62
63 interface PWM;
64 interface UserInterface user;
65 interface PWMIO io;
66 endinterface
67
68 //(*synthesize*)
69 module mkPWM#(Clock ext_clock)(PWM);
70
71 //let pwmnum = valueOf(pwmnum_);
72
73 let bus_clock <- exposeCurrentClock;
74 let bus_reset <- exposeCurrentReset;
75
76 Reg#(Bit#(`PWMWIDTH)) period <- mkReg(0);
77 Reg#(Bit#(`PWMWIDTH)) duty_cycle <- mkReg(0);
78 Reg#(Bit#(`PWMWIDTH)) clock_divisor <- mkReg(0);
79 // =========== Control registers ================== //
80 Reg#(Bit#(1)) clock_selector <- mkReg(0); // bit-0
81 Reg#(Bit#(1)) pwm_enable <- mkReg(0); // bit-1
82 Reg#(Bit#(1)) pwm_start <- mkReg(0); // bit-2
83 Reg#(Bit#(1)) continous_once <- mkReg(0); // bit-3
84 Reg#(Bit#(1)) pwm_output_enable <- mkReg(0); // bit-4
85 Reg#(Bit#(1)) interrupt <- mkReg(0); // bit-5
86 Reg#(Bit#(1)) reset_counter <- mkReg(0); // bit-7
87 Reg#(Bit#(8)) control = concatReg8(reset_counter, readOnlyReg(0),
88 readOnlyReg(interrupt),
89 pwm_output_enable, continous_once,
90 pwm_start, pwm_enable,
91 clock_selector);
92 // ================================================ //
93
94 // Generate a reset signal is there is a reset from
95 // the bus interface of if the reset_counter
96 // bit in the control register is set. The new reset
97 // is called overall_reset. Only the counter
98 // and the output signals need to be reset by this.
99 MakeResetIfc control_reset <- mkReset(1,False, bus_clock);
100 rule generate_reset;
101 if(reset_counter==1)
102 control_reset.assertReset;
103 endrule
104 Reset overall_reset <- mkResetEither(bus_reset,control_reset.new_rst);
105
106 // Select between bus clock or external clock
107 MuxClkIfc clock_selection <- mkUngatedClockMux(ext_clock,bus_clock);
108 Reset async_reset <- mkAsyncResetFromCR(0,clock_selection.clock_out);
109 rule select_busclk_extclk;
110 clock_selection.select(clock_selector==1);
111 endrule
112
113 // The following register is required to transfer
114 // the divisor value from bus_clock to
115 // external clock domain. This is necessary if the
116 // clock divider needs to operate on the
117 // external clock. In this case, the divisor value
118 // should also come from the same clock domain.
119 Reg#(Bit#(`PWMWIDTH)) clock_divisor_sync <- mkSyncRegFromCC(0,
120 clock_selection.clock_out);
121 rule transfer_data_from_clock_domains;
122 clock_divisor_sync <= clock_divisor;
123 endrule
124
125 // The PWM can operate on a slowed-down clock.
126 // The following module generates a slowed-down
127 // clock based on the value given in register divisor.
128 // Since the clock_divider works on a muxed
129 // clock domain of the external clock or bus_clock,
130 // the divisor (which operates on the bus_clock
131 // will have to be synchronized and sent to the divider
132 Ifc_ClockDiv#(`PWMWIDTH) clock_divider <- mkClockDiv(
133 clocked_by clock_selection.clock_out,
134 reset_by async_reset);
135 let downclock = clock_divider.slowclock;
136 Reset downreset <- mkAsyncReset(0,overall_reset,downclock);
137 rule generate_slow_clock;
138 clock_divider.divisor(clock_divisor_sync);
139 endrule
140
141 // ======= Actual Counter and PWM signal generation ======== //
142 Reg#(Bit#(1)) pwm_output <- mkReg(0,clocked_by downclock,
143 reset_by downreset);
144 Reg#(Bit#(`PWMWIDTH)) rg_counter <-mkReg(0,clocked_by downclock,
145 reset_by downreset);
146
147 // create synchronizers for clock domain crossing.
148 Reg#(Bit#(1)) sync_pwm_output <- mkSyncRegToCC(0,downclock,downreset);
149 ReadOnly#(Bit#(1)) pwm_signal <- mkNullCrossingWire(bus_clock, pwm_output);
150 Reg#(Bit#(1)) sync_continous_once <- mkSyncRegFromCC(0,downclock);
151 Reg#(Bit#(`PWMWIDTH)) sync_duty_cycle <- mkSyncRegFromCC(0,downclock);
152 Reg#(Bit#(`PWMWIDTH)) sync_period <- mkSyncRegFromCC(0,downclock);
153 Reg#(Bit#(1)) sync_pwm_enable <- mkSyncRegFromCC(0,downclock);
154 Reg#(Bit#(1)) sync_pwm_start <- mkSyncRegFromCC(0,downclock);
155 rule sync_pwm_output_to_default_clock;
156 sync_pwm_output <= pwm_output;
157 endrule
158
159 // capture the synchronized values from the default
160 // clock domain to the downclock domain for
161 // actual timer and pwm functionality.
162 rule sync_from_default_to_downclock;
163 sync_continous_once <= continous_once;
164 sync_duty_cycle <= duty_cycle;
165 sync_period <= period;
166 sync_pwm_enable <= pwm_enable;
167 sync_pwm_start <= pwm_start;
168 endrule
169 let temp = sync_period==0?0:sync_period-1;
170
171 // This rule generates the interrupt in the timer
172 // mode and resets it if the user-write interface
173 // writes a value of 1 to the reset_counter bit.
174 rule generate_interrupt_in_timer_mode;
175 if(pwm_enable==0)
176 interrupt <= sync_pwm_output;
177 else if(reset_counter==1)
178 interrupt <= 0;
179 else
180 interrupt <= 0;
181 endrule
182
183 // This rule performs the actual pwm and the timer
184 // functionality. if pwm_enable is 1 then the
185 // PWM mode is selected. Every time the counter
186 // value equals/crosses the period value it is
187 // reset and the output pwm_output signal is toggled.
188 // The timer mode is selected when pwm_enable is 0.
189 // Here again 2 more modes are possible. if the
190 // continous_once bit is 0 then the timer is in one time.
191 // In this case once the counter reaches
192 // the period value it raises an interrupt and
193 // stops the counter. In the continuous mode
194 // however, when the counter reaches the period value
195 // the interrupt is raise, the counter is
196 // reset to 0 and continues counting.
197 // During continuous counting the interrupt can be cleared by
198 // the user but will be set back when the counter reaches the period value.
199 rule compare_and_generate_pwm(sync_pwm_start==1);
200 let cntr = rg_counter+1;
201 if(sync_pwm_enable==1)begin // PWM mode enabled
202 if(rg_counter >= temp)
203 rg_counter <= 0;
204 else
205 rg_counter <= cntr;
206 if(rg_counter < sync_duty_cycle)
207 pwm_output <= 1;
208 else
209 pwm_output <= 0;
210 end
211 else begin // Timer mode enabled.
212 if(sync_continous_once==0) begin // One time mode.
213 if(rg_counter >= temp)begin
214 pwm_output <= 1;
215 end
216 else
217 rg_counter <= cntr;
218 end
219 else begin // Continous mode.
220 if(rg_counter >= temp)begin
221 pwm_output <= 1;
222 rg_counter <= 0;
223 end
224 else begin
225 rg_counter <= cntr;
226 end
227 end
228 end
229 endrule
230
231 // ========================================================= //
232 interface user = interface UserInterface
233 method ActionValue#(Bool) write(Bit#(`PADDR) addr, Bit#(`Reg_width) data);
234 Bool err = False;
235 case(addr[4:2])
236 0: period <= truncate(data);
237 1: duty_cycle <= truncate(data);
238 2: begin control <= truncate(data);end
239 3: clock_divisor <= truncate(data);
240 default: err = True;
241 endcase
242 return err;
243 endmethod
244
245 method Tuple2#(Bool, Bit#(`Reg_width)) read(Bit#(`PADDR) addr);
246 Bool err = False;
247 Bit#(`Reg_width) data;
248 case(addr[4:2])
249 0: data = zeroExtend(period);
250 1: data = zeroExtend(duty_cycle);
251 2: data = zeroExtend(control);
252 3: data = zeroExtend(clock_divisor);
253 default: begin err = True; data = 0; end
254 endcase
255 return tuple2(err,data);
256 endmethod
257 endinterface;
258 interface io = interface PWMIO
259 method pwm_o=pwm_output_enable==1?pwm_signal:0;
260 endinterface;
261 endmodule
262
263 `ifdef PWM_AXI4Lite
264 // the following interface and module will add the
265 // AXI4Lite interface to the PWM module
266 interface Ifc_PWM_bus;
267 interface PWMIO pwm_io;
268 interface AXI4_Lite_Slave_IFC#(`PADDR, `Reg_width,
269 `USERSPACE) axi4_slave;
270 endinterface
271
272 //(*synthesize*)
273 module mkPWM_bus#(Clock ext_clock)(Ifc_PWM_bus);
274 PWM pwm <-mkPWM(ext_clock);
275 AXI4_Lite_Slave_Xactor_IFC#(`PADDR,`Reg_width, `USERSPACE)
276 s_xactor<-mkAXI4_Lite_Slave_Xactor();
277
278 rule read_request;
279 let req <- pop_o (s_xactor.o_rd_addr);
280 let {err,data} = pwm.user.read(req.araddr);
281 let resp= AXI4_Lite_Rd_Data {rresp:err?
282 AXI4_LITE_SLVERR:AXI4_LITE_OKAY,
283 rdata:data, ruser: ?};
284 s_xactor.i_rd_data.enq(resp);
285 endrule
286
287 rule write_request;
288 let addreq <- pop_o(s_xactor.o_wr_addr);
289 let datareq <- pop_o(s_xactor.o_wr_data);
290 let err <- pwm.user.write(addreq.awaddr, datareq.wdata);
291 let resp = AXI4_Lite_Wr_Resp {bresp: err?
292 AXI4_LITE_SLVERR:AXI4_LITE_OKAY,
293 buser: ?};
294 s_xactor.i_wr_resp.enq(resp);
295 endrule
296
297 interface pwm_io = pwm.io;
298 endmodule
299 `endif
300
301 `ifdef PWM_AXI4
302 // the following interface and module will add the
303 // AXI4 interface to the PWM module
304 interface Ifc_PWM_bus;
305 interface PWMIO pwm_io;
306 interface AXI4_Slave_IFC#(`PADDR, `Reg_width,`USERSPACE) axi4_slave;
307 endinterface
308
309 (*synthesize*)
310 module mkPWM_bus#(Clock ext_clock)(Ifc_PWM_bus);
311 PWM pwm <-mkPWM(ext_clock);
312 AXI4_Slave_Xactor_IFC#(`PADDR,`Reg_width,
313 `USERSPACE) s_xactor<-mkAXI4_Slave_Xactor();
314
315 rule read_request;
316 let req <- pop_o (s_xactor.o_rd_addr);
317 let {err,data} = pwm.user.read(req.araddr);
318 if(!(req.arsize == 2 && req.arlen == 0))
319 err = True;
320 let resp= AXI4_Rd_Data {rresp:err?AXI4_SLVERR:AXI4_OKAY,
321 rdata:data, ruser:
322 ?, rid:req.arid, rlast: True};
323 s_xactor.i_rd_data.enq(resp);
324 endrule
325
326 rule write_request;
327 let addreq <- pop_o(s_xactor.o_wr_addr);
328 let datareq <- pop_o(s_xactor.o_wr_data);
329 let err <- pwm.user.write(addreq.awaddr, datareq.wdata);
330 if(!(addreq.awsize == 2 && addreq.awlen == 0))
331 err = True;
332 let resp = AXI4_Wr_Resp {bresp: err?AXI4_SLVERR:AXI4_OKAY, buser: ?,
333 bid:datareq.wid};
334 s_xactor.i_wr_resp.enq(resp);
335 endrule
336
337 interface pwm_io = pwm.io;
338 endmodule
339 `endif
340 `ifdef PWM_TEST
341 (*synthesize*)
342 module mkTb(Empty);
343 let clk <- exposeCurrentClock;
344 PWM pwm <- mkPWM(clk, 32);
345 Reg#(Bit#(5)) rg_state <- mkReg(0);
346
347 rule state1(rg_state==0);
348 rg_state<=1;
349 let x <- pwm.user.write(0,'d4);
350 endrule
351 rule state2(rg_state==1);
352 rg_state<=2;
353 let x <- pwm.user.write('d4,'d3);
354 endrule
355 rule state3(rg_state==2);
356 rg_state<=3;
357 let x <- pwm.user.write('hc,'d4);
358 endrule
359 rule state4(rg_state==3);
360 rg_state<=4;
361 let x <- pwm.user.write(8,'b0001_0110);
362 endrule
363 endmodule
364 `endif
365 endpackage