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