convert pwm to get/put
[shakti-peripherals.git] / src / peripherals / pwm / 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 GetPut::*;
43 import ClockDiv::*;
44 import ConcatReg::*;
45 import Semi_FIFOF::*;
46 import BUtils ::*;
47 `ifdef PWM_AXI4Lite
48 import AXI4_Lite_Types::*;
49 `endif
50 `ifdef PWM_AXI4
51 import AXI4_Types::*;
52 `endif
53 /*======================*/
54
55 interface UserInterface;
56 method ActionValue#(Bool) write(Bit#(`PADDR) addr, Bit#(`Reg_width) data);
57 method Tuple2#(Bool, Bit#(`Reg_width)) read(Bit#(`PADDR) addr);
58 endinterface
59
60 interface PWMIO;
61 interface Get#(Bit#(1)) pwm_o;
62 endinterface
63
64 interface PWM;
65 interface UserInterface user;
66 interface PWMIO io;
67 endinterface
68
69 (*synthesize*)
70 module mkPWM#(Clock ext_clock)(PWM);
71
72 let bus_clock <- exposeCurrentClock;
73 let bus_reset <- exposeCurrentReset;
74
75 Reg#(Bit#(`PWMWIDTH)) period <- mkReg(0);
76 Reg#(Bit#(`PWMWIDTH)) duty_cycle <- mkReg(0);
77 Reg#(Bit#(`PWMWIDTH)) 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#(`PWMWIDTH)) 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#(`PWMWIDTH) 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#(`PWMWIDTH)) 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#(`PWMWIDTH)) sync_duty_cycle <- mkSyncRegFromCC(0,downclock);
151 Reg#(Bit#(`PWMWIDTH)) 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 interface pwm_o = interface Get
259 method ActionValue#(Bit#(1)) get;
260 return pwm_output_enable==1?pwm_signal:0;
261 endmethod
262 endinterface;
263
264 endinterface;
265 endmodule
266
267 `ifdef PWM_AXI4Lite
268 // the following interface and module will add the
269 // AXI4Lite interface to the PWM module
270 interface Ifc_PWM_bus;
271 interface PWMIO pwm_io;
272 interface AXI4_Lite_Slave_IFC#(`PADDR, `Reg_width,
273 `USERSPACE) axi4_slave;
274 endinterface
275
276 (*synthesize*)
277 module mkPWM_bus#(Clock ext_clock)(Ifc_PWM_bus);
278 PWM pwm <-mkPWM(ext_clock);
279 AXI4_Lite_Slave_Xactor_IFC#(`PADDR,`Reg_width, `USERSPACE)
280 s_xactor<-mkAXI4_Lite_Slave_Xactor();
281
282 rule read_request;
283 let req <- pop_o (s_xactor.o_rd_addr);
284 let {err,data} = pwm.user.read(req.araddr);
285 let resp= AXI4_Lite_Rd_Data {rresp:err?
286 AXI4_LITE_SLVERR:AXI4_LITE_OKAY,
287 rdata:data, ruser: ?};
288 s_xactor.i_rd_data.enq(resp);
289 endrule
290
291 rule write_request;
292 let addreq <- pop_o(s_xactor.o_wr_addr);
293 let datareq <- pop_o(s_xactor.o_wr_data);
294 let err <- pwm.user.write(addreq.awaddr, datareq.wdata);
295 let resp = AXI4_Lite_Wr_Resp {bresp: err?
296 AXI4_LITE_SLVERR:AXI4_LITE_OKAY,
297 buser: ?};
298 s_xactor.i_wr_resp.enq(resp);
299 endrule
300
301 interface pwm_io = pwm.io;
302 interface axi4_slave = s_xactor.axi_side;
303 endmodule
304 `endif
305
306 `ifdef PWM_AXI4
307 // the following interface and module will add the
308 // AXI4 interface to the PWM module
309 interface Ifc_PWM_bus;
310 interface PWMIO pwm_io;
311 interface AXI4_Slave_IFC#(`PADDR, `Reg_width,`USERSPACE) axi4_slave;
312 endinterface
313
314 (*synthesize*)
315 module mkPWM_bus#(Clock ext_clock)(Ifc_PWM_bus);
316 PWM pwm <-mkPWM(ext_clock);
317 AXI4_Slave_Xactor_IFC#(`PADDR,`Reg_width,
318 `USERSPACE) s_xactor<-mkAXI4_Slave_Xactor();
319
320 rule read_request;
321 let req <- pop_o (s_xactor.o_rd_addr);
322 let {err,data} = pwm.user.read(req.araddr);
323 if(!(req.arsize == 2 && req.arlen == 0))
324 err = True;
325 let resp= AXI4_Rd_Data {rresp:err?AXI4_SLVERR:AXI4_OKAY,
326 rdata:data, ruser:
327 ?, rid:req.arid, rlast: True};
328 s_xactor.i_rd_data.enq(resp);
329 endrule
330
331 rule write_request;
332 let addreq <- pop_o(s_xactor.o_wr_addr);
333 let datareq <- pop_o(s_xactor.o_wr_data);
334 let err <- pwm.user.write(addreq.awaddr, datareq.wdata);
335 if(!(addreq.awsize == 2 && addreq.awlen == 0))
336 err = True;
337 let resp = AXI4_Wr_Resp {bresp: err?AXI4_SLVERR:AXI4_OKAY, buser: ?,
338 bid:datareq.wid};
339 s_xactor.i_wr_resp.enq(resp);
340 endrule
341
342 interface pwm_io = pwm.io;
343 endmodule
344 `endif
345 `ifdef PWM_TEST
346 (*synthesize*)
347 module mkTb(Empty);
348 let clk <- exposeCurrentClock;
349 PWM pwm <- mkPWM(clk, 32);
350 Reg#(Bit#(5)) rg_state <- mkReg(0);
351
352 rule state1(rg_state==0);
353 rg_state<=1;
354 let x <- pwm.user.write(0,'d4);
355 endrule
356 rule state2(rg_state==1);
357 rg_state<=2;
358 let x <- pwm.user.write('d4,'d3);
359 endrule
360 rule state3(rg_state==2);
361 rg_state<=3;
362 let x <- pwm.user.write('hc,'d4);
363 endrule
364 rule state4(rg_state==3);
365 rg_state<=4;
366 let x <- pwm.user.write(8,'b0001_0110);
367 endrule
368 endmodule
369 `endif
370 endpackage