b760f814ef1eefbdc10e3cd191f271567fb49912
[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 /*=== Project imports ==*/
36 import Clocks::*;
37 /*======================*/
38 /*== Package imports ==*/
39 //import defined_types::*;
40 `include "instance_defines.bsv"
41 import ClockDiv::*;
42 import ConcatReg::*;
43 import Semi_FIFOF::*;
44 import BUtils ::*;
45 `ifdef PWM_AXI4Lite
46 import AXI4_Lite_Types::*;
47 `endif
48 `ifdef PWM_AXI4
49 import AXI4_Types::*;
50 `endif
51 /*======================*/
52
53 interface UserInterface;
54 method ActionValue#(Bool) write(Bit#(`PADDR) addr, Bit#(`Reg_width) data);
55 method Tuple2#(Bool, Bit#(`Reg_width)) read(Bit#(`PADDR) addr);
56 endinterface
57
58 interface PWMIO;
59 method Bit#(1) pwm_o;
60 endinterface
61
62 interface PWM;
63 interface UserInterface user;
64 interface PWMIO io;
65 endinterface
66
67 //(*synthesize*)
68 module mkPWM#(Clock ext_clock, numeric type pwmnum_)(PWM);
69
70 let pwmnum = valueOf(pwmnum_);
71
72 let bus_clock <- exposeCurrentClock;
73 let bus_reset <- exposeCurrentReset;
74
75 Reg#(Bit#(pwmnum_)) period <- mkReg(0);
76 Reg#(Bit#(pwmnum_)) duty_cycle <- mkReg(0);
77 Reg#(Bit#(pwmnum_)) clock_divisor <- mkReg(0);
78 // =========== Control registers ================== //
79 Reg#(Bit#(1)) clock_selector <- mkReg(0); // bit-0
80 Reg#(Bit#(1)) pwm_enable <- mkReg(0); // bit-1
81 Reg#(Bit#(1)) pwm_start <- mkReg(0); // bit-2
82 Reg#(Bit#(1)) continous_once <- mkReg(0); // bit-3
83 Reg#(Bit#(1)) pwm_output_enable <- mkReg(0); // bit-4
84 Reg#(Bit#(1)) interrupt <- mkReg(0); // bit-5
85 Reg#(Bit#(1)) reset_counter <- mkReg(0); // bit-7
86 Reg#(Bit#(8)) control = concatReg8(reset_counter, readOnlyReg(0),
87 readOnlyReg(interrupt),
88 pwm_output_enable, continous_once,
89 pwm_start, pwm_enable,
90 clock_selector);
91 // ================================================ //
92
93 // Generate a reset signal is there is a reset from
94 // the bus interface of if the reset_counter
95 // bit in the control register is set. The new reset
96 // is called overall_reset. Only the counter
97 // and the output signals need to be reset by this.
98 MakeResetIfc control_reset <- mkReset(1,False, bus_clock);
99 rule generate_reset;
100 if(reset_counter==1)
101 control_reset.assertReset;
102 endrule
103 Reset overall_reset <- mkResetEither(bus_reset,control_reset.new_rst);
104
105 // Select between bus clock or external clock
106 MuxClkIfc clock_selection <- mkUngatedClockMux(ext_clock,bus_clock);
107 Reset async_reset <- mkAsyncResetFromCR(0,clock_selection.clock_out);
108 rule select_busclk_extclk;
109 clock_selection.select(clock_selector==1);
110 endrule
111
112 // The following register is required to transfer
113 // the divisor value from bus_clock to
114 // external clock domain. This is necessary if the
115 // clock divider needs to operate on the
116 // external clock. In this case, the divisor value
117 // should also come from the same clock domain.
118 Reg#(Bit#(pwmnum_)) clock_divisor_sync <- mkSyncRegFromCC(0,
119 clock_selection.clock_out);
120 rule transfer_data_from_clock_domains;
121 clock_divisor_sync <= clock_divisor;
122 endrule
123
124 // The PWM can operate on a slowed-down clock.
125 // The following module generates a slowed-down
126 // clock based on the value given in register divisor.
127 // Since the clock_divider works on a muxed
128 // clock domain of the external clock or bus_clock,
129 // the divisor (which operates on the bus_clock
130 // will have to be synchronized and sent to the divider
131 Ifc_ClockDiv#(pwmnum_) clock_divider <- mkClockDiv(
132 clocked_by clock_selection.clock_out,
133 reset_by async_reset);
134 let downclock = clock_divider.slowclock;
135 Reset downreset <- mkAsyncReset(0,overall_reset,downclock);
136 rule generate_slow_clock;
137 clock_divider.divisor(clock_divisor_sync);
138 endrule
139
140 // ======= Actual Counter and PWM signal generation ======== //
141 Reg#(Bit#(1)) pwm_output <- mkReg(0,clocked_by downclock,
142 reset_by downreset);
143 Reg#(Bit#(pwmnum_)) rg_counter <-mkReg(0,clocked_by downclock,
144 reset_by downreset);
145
146 // create synchronizers for clock domain crossing.
147 Reg#(Bit#(1)) sync_pwm_output <- mkSyncRegToCC(0,downclock,downreset);
148 ReadOnly#(Bit#(1)) pwm_signal <- mkNullCrossingWire(bus_clock, pwm_output);
149 Reg#(Bit#(1)) sync_continous_once <- mkSyncRegFromCC(0,downclock);
150 Reg#(Bit#(pwmnum_)) sync_duty_cycle <- mkSyncRegFromCC(0,downclock);
151 Reg#(Bit#(pwmnum_)) sync_period <- mkSyncRegFromCC(0,downclock);
152 Reg#(Bit#(1)) sync_pwm_enable <- mkSyncRegFromCC(0,downclock);
153 Reg#(Bit#(1)) sync_pwm_start <- mkSyncRegFromCC(0,downclock);
154 rule sync_pwm_output_to_default_clock;
155 sync_pwm_output <= pwm_output;
156 endrule
157
158 // capture the synchronized values from the default
159 // clock domain to the downclock domain for
160 // actual timer and pwm functionality.
161 rule sync_from_default_to_downclock;
162 sync_continous_once <= continous_once;
163 sync_duty_cycle <= duty_cycle;
164 sync_period <= period;
165 sync_pwm_enable <= pwm_enable;
166 sync_pwm_start <= pwm_start;
167 endrule
168 let temp = sync_period==0?0:sync_period-1;
169
170 // This rule generates the interrupt in the timer
171 // mode and resets it if the user-write interface
172 // writes a value of 1 to the reset_counter bit.
173 rule generate_interrupt_in_timer_mode;
174 if(pwm_enable==0)
175 interrupt <= sync_pwm_output;
176 else if(reset_counter==1)
177 interrupt <= 0;
178 else
179 interrupt <= 0;
180 endrule
181
182 // This rule performs the actual pwm and the timer
183 // functionality. if pwm_enable is 1 then the
184 // PWM mode is selected. Every time the counter
185 // value equals/crosses the period value it is
186 // reset and the output pwm_output signal is toggled.
187 // The timer mode is selected when pwm_enable is 0.
188 // Here again 2 more modes are possible. if the
189 // continous_once bit is 0 then the timer is in one time.
190 // In this case once the counter reaches
191 // the period value it raises an interrupt and
192 // stops the counter. In the continuous mode
193 // however, when the counter reaches the period value
194 // the interrupt is raise, the counter is
195 // reset to 0 and continues counting.
196 // During continuous counting the interrupt can be cleared by
197 // the user but will be set back when the counter reaches the period value.
198 rule compare_and_generate_pwm(sync_pwm_start==1);
199 let cntr = rg_counter+1;
200 if(sync_pwm_enable==1)begin // PWM mode enabled
201 if(rg_counter >= temp)
202 rg_counter <= 0;
203 else
204 rg_counter <= cntr;
205 if(rg_counter < sync_duty_cycle)
206 pwm_output <= 1;
207 else
208 pwm_output <= 0;
209 end
210 else begin // Timer mode enabled.
211 if(sync_continous_once==0) begin // One time mode.
212 if(rg_counter >= temp)begin
213 pwm_output <= 1;
214 end
215 else
216 rg_counter <= cntr;
217 end
218 else begin // Continous mode.
219 if(rg_counter >= temp)begin
220 pwm_output <= 1;
221 rg_counter <= 0;
222 end
223 else begin
224 rg_counter <= cntr;
225 end
226 end
227 end
228 endrule
229
230 // ========================================================= //
231 interface user = interface UserInterface
232 method ActionValue#(Bool) write(Bit#(`PADDR) addr, Bit#(`Reg_width) data);
233 Bool err = False;
234 case(addr[4:2])
235 0: period <= truncate(data);
236 1: duty_cycle <= truncate(data);
237 2: begin control <= truncate(data);end
238 3: clock_divisor <= truncate(data);
239 default: err = True;
240 endcase
241 return err;
242 endmethod
243
244 method Tuple2#(Bool, Bit#(`Reg_width)) read(Bit#(`PADDR) addr);
245 Bool err = False;
246 Bit#(`Reg_width) data;
247 case(addr[4:2])
248 0: data = zeroExtend(period);
249 1: data = zeroExtend(duty_cycle);
250 2: data = zeroExtend(control);
251 3: data = zeroExtend(clock_divisor);
252 default: begin err = True; data = 0; end
253 endcase
254 return tuple2(err,data);
255 endmethod
256 endinterface;
257 interface io = interface PWMIO
258 method pwm_o=pwm_output_enable==1?pwm_signal:0;
259 endinterface;
260 endmodule
261
262 `ifdef PWM_AXI4Lite
263 // the following interface and module will add the
264 // AXI4Lite interface to the PWM module
265 interface Ifc_PWM_bus;
266 interface PWMIO pwm_io;
267 interface AXI4_Lite_Slave_IFC#(`PADDR, `Reg_width,
268 `USERSPACE) axi4_slave;
269 endinterface
270
271 //(*synthesize*)
272 module mkPWM_bus#(Clock ext_clock, numeric type pwmnum)(Ifc_PWM_bus);
273 PWM pwm <-mkPWM(ext_clock, pwmnum);
274 AXI4_Lite_Slave_Xactor_IFC#(`PADDR,`Reg_width, `USERSPACE)
275 s_xactor<-mkAXI4_Lite_Slave_Xactor();
276
277 rule read_request;
278 let req <- pop_o (s_xactor.o_rd_addr);
279 let {err,data} = pwm.user.read(req.araddr);
280 let resp= AXI4_Lite_Rd_Data {rresp:err?
281 AXI4_LITE_SLVERR:AXI4_LITE_OKAY,
282 rdata:data, ruser: ?};
283 s_xactor.i_rd_data.enq(resp);
284 endrule
285
286 rule write_request;
287 let addreq <- pop_o(s_xactor.o_wr_addr);
288 let datareq <- pop_o(s_xactor.o_wr_data);
289 let err <- pwm.user.write(addreq.awaddr, datareq.wdata);
290 let resp = AXI4_Lite_Wr_Resp {bresp: err?
291 AXI4_LITE_SLVERR:AXI4_LITE_OKAY,
292 buser: ?};
293 s_xactor.i_wr_resp.enq(resp);
294 endrule
295
296 interface pwm_io = pwm.io;
297 endmodule
298 `endif
299
300 `ifdef PWM_AXI4
301 // the following interface and module will add the
302 // AXI4 interface to the PWM module
303 interface Ifc_PWM_bus;
304 interface PWMIO pwm_io;
305 interface AXI4_Slave_IFC#(`PADDR, `Reg_width,`USERSPACE) axi4_slave;
306 endinterface
307
308 (*synthesize*)
309 module mkPWM_bus#(Clock ext_clock, numeric type pwmnum)(Ifc_PWM_bus);
310 PWM pwm <-mkPWM(ext_clock, pwmnum);
311 AXI4_Slave_Xactor_IFC#(`PADDR,`Reg_width,
312 `USERSPACE) s_xactor<-mkAXI4_Slave_Xactor();
313
314 rule read_request;
315 let req <- pop_o (s_xactor.o_rd_addr);
316 let {err,data} = pwm.user.read(req.araddr);
317 if(!(req.arsize == 2 && req.arlen == 0))
318 err = True;
319 let resp= AXI4_Rd_Data {rresp:err?AXI4_SLVERR:AXI4_OKAY,
320 rdata:data, ruser:
321 ?, rid:req.arid, rlast: True};
322 s_xactor.i_rd_data.enq(resp);
323 endrule
324
325 rule write_request;
326 let addreq <- pop_o(s_xactor.o_wr_addr);
327 let datareq <- pop_o(s_xactor.o_wr_data);
328 let err <- pwm.user.write(addreq.awaddr, datareq.wdata);
329 if(!(addreq.awsize == 2 && addreq.awlen == 0))
330 err = True;
331 let resp = AXI4_Wr_Resp {bresp: err?AXI4_SLVERR:AXI4_OKAY, buser: ?,
332 bid:datareq.wid};
333 s_xactor.i_wr_resp.enq(resp);
334 endrule
335
336 interface pwm_io = pwm.io;
337 endmodule
338 `endif
339 `ifdef PWM_TEST
340 (*synthesize*)
341 module mkTb(Empty);
342 let clk <- exposeCurrentClock;
343 PWM pwm <- mkPWM(clk, 32);
344 Reg#(Bit#(5)) rg_state <- mkReg(0);
345
346 rule state1(rg_state==0);
347 rg_state<=1;
348 let x <- pwm.user.write(0,'d4);
349 endrule
350 rule state2(rg_state==1);
351 rg_state<=2;
352 let x <- pwm.user.write('d4,'d3);
353 endrule
354 rule state3(rg_state==2);
355 rg_state<=3;
356 let x <- pwm.user.write('hc,'d4);
357 endrule
358 rule state4(rg_state==3);
359 rg_state<=4;
360 let x <- pwm.user.write(8,'b0001_0110);
361 endrule
362 endmodule
363 `endif
364 endpackage