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

Subversion Repositories serial_div_uu

[/] [serial_div_uu/] [web_uploads/] [pwm_reader.v] - Blame information for rev 6

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 6 root
//---------------------------------------------------------------------------
2
// PWM reader 
3
// ...for variable frequency PWM signals from real world sensors.
4
//
5
//
6
// Description: See description below (which suffices for IP core
7
//                                     specification document.)
8
//
9
// Copyright (C) 2002 John Clayton and OPENCORES.ORG (this Verilog version)
10
//
11
// This source file may be used and distributed without restriction provided
12
// that this copyright statement is not removed from the file and that any
13
// derivative work contains the original copyright notice and the associated
14
// disclaimer.
15
//
16
// This source file is free software; you can redistribute it and/or modify
17
// it under the terms of the GNU Lesser General Public License as published
18
// by the Free Software Foundation;  either version 2.1 of the License, or
19
// (at your option) any later version.
20
//
21
// This source is distributed in the hope that it will be useful, but WITHOUT
22
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
23
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
24
// License for more details.
25
//
26
// You should have received a copy of the GNU Lesser General Public License
27
// along with this source.
28
// If not, download it from http://www.opencores.org/lgpl.shtml
29
//
30
//---------------------------------------------------------------------------
31
//
32
// Author: John Clayton
33
// Date  : Jan  28, 2003
34
//
35
// (NOTE: Date formatted as day/month/year.)
36
// Update: 11/03/03 Saved "pwd_counter.v" under the new name "pwm_reader.v" so
37
//                  that the divide_uu module can be integrated into this new
38
//                  module.
39
// Update: 13/03/03 Finished coding module, began testing.
40
//
41
// Update: 03/06/03 Added ack_r signal, so that stb_o would not oscillate when
42
//                  connected directly to the ack_i input.
43
//
44
// Description
45
//---------------------------------------------------------------------------
46
// This module reads the pulse width of a repetitive variable duty cycle
47
// digital input.  Pulse Width Modulated (PWM) inputs are produced by many 
48
// devices, including certain real world sensors.
49
// In this case, the specific sensor for which this module was created
50
// is the Analog Devices ADXL202e (a +/- 2g. dual accelerometer.)
51
//
52
// Operation of this module is very simple:
53
//
54
// An up-counter (T) counts clocks in one period of the incoming PWM signal.
55
// The T counter is reset with each rising edge of the incoming PWM waveform.
56
//
57
// Another (clock-enabled) up-counter (S) counts the clocks during the time
58
// when the PWM signal input is high.
59
// The S counter is also reset at the rising edge of the incoming PWM wave.
60
//
61
// To derive the duty cycle as a fraction, a serial divider calculates S/T.
62
//
63
// Once the fraction is obtained, the output is presented at "dat_o."
64
// The signal "stb_o" (strobe output) indicates that a valid reading is
65
// present on the data bus output.  The strobe will remain high until the
66
// reading is acknowledged by pulsing "ack_i" for one clock cycle.
67
//
68
// If "stb_o" is tied directly to "ack_i" then each time a new reading is 
69
// obtained a pulse will be produced at "stb_o" which is one single clock wide.
70
//
71
// It is important to note that in the absence of any ack_i signal, the unit
72
// will hold the contents of the last reading, and any new readings from the
73
// PWM device will be missed.
74
//
75
//---------------------------------------------------------------------------
76
 
77
 
78
module pwm_reader (
79
                    clk_i,
80
                    clk_en_i,
81
                    rst_i,
82
                    pwm_signal_i,
83
                    ack_i,
84
                    dat_o,
85
                    stb_o
86
                    );
87
 
88
parameter COUNTER_WIDTH_PP     = 10; // Size to hold (max_pwm_period)*(Fclk_i)
89
parameter DAT_WIDTH_PP         = 8;  // Size of fraction from divider
90
parameter DIV_COUNT_WIDTH_PP   = 3;  // Must be enough bits to hold the number:
91
                                     // DAT_WIDTH_PP-1
92
 
93
input clk_i;
94
input clk_en_i;
95
input rst_i;
96
input pwm_signal_i;
97
input ack_i;
98
 
99
output [DAT_WIDTH_PP-1:0] dat_o;
100
output stb_o;
101
 
102
// Local signals
103
reg  [COUNTER_WIDTH_PP-1:0] s_cnt;
104
reg  [COUNTER_WIDTH_PP-1:0] t_cnt;
105
reg  pwm_ff_1;
106
reg  pwm_ff_2;
107
reg  ack_r;
108
 
109
wire rising_edge;
110
wire divide_done;
111
 
112
//-----------------------------------------------------------------------
113
// Try to avoid metastability by running the input signal through
114
// a D flip flop.
115
always @(posedge clk_i)
116
begin
117
  if (rst_i) pwm_ff_1 <= 0;
118
  else pwm_ff_1 <= pwm_signal_i;
119
end
120
 
121
// This is the rising edge detection
122
always @(posedge clk_i)
123
begin
124
  if (rst_i) pwm_ff_2 <= 0;
125
  else pwm_ff_2 <= pwm_ff_1;
126
end
127
assign rising_edge = pwm_ff_1 && ~pwm_ff_2;
128
 
129
// This is period counter
130
always @(posedge clk_i)
131
begin
132
  if (rst_i || rising_edge) t_cnt <= 0;
133
  else if (clk_en_i) t_cnt <= t_cnt + 1;
134
end
135
 
136
// This is duty cycle counter
137
always @(posedge clk_i)
138
begin
139
  if (rst_i || rising_edge) s_cnt <= 0;
140
  else if (clk_en_i && pwm_signal_i) s_cnt <= s_cnt + 1;
141
end
142
 
143
// This unit provides for the division s_cnt/t_cnt.
144
// The values in the counters are latched into the divider whenever
145
// the divide_i input is pulsed.
146
// The divider ignores the inputs whenever "divide_i" is not pulsed, so
147
// there is no problem with the counters continually advancing until
148
// the division is started.
149
//
150
// Since the result is expected to be purely fractional, the quotient part
151
// is all zero, and is thrown away on purpose.  (A special divider could be
152
// build possibly to avoid this?)
153
serial_divide_uu #(
154
                   COUNTER_WIDTH_PP,    // M_PP (dividend width)
155
                   COUNTER_WIDTH_PP,    // N_PP (divisor width)
156
                   DAT_WIDTH_PP,        // R_PP (remainder width)
157
                   COUNTER_WIDTH_PP,    // S_PP (skip integer part of quotient)
158
                   DIV_COUNT_WIDTH_PP,  // counter bits
159
                   1                    // need output holding buffers?
160
                   )
161
  divider_unit
162
  (
163
   .clk_i(clk_i),
164
   .clk_en_i(1'b1),
165
   .rst_i(rst_i),
166
   // New divide is not allowed until previous value is read or acknowledged
167
   .divide_i(rising_edge && ~stb_o),
168
   .dividend_i(s_cnt),
169
   .divisor_i(t_cnt),
170
   .quotient_o(dat_o),
171
   .done_o(divide_done)
172
  );
173
 
174
// This latches the ack_i input, so that at least one pulse of stb_o is produced
175
// before the ack_i takes it down low again, in case the stb_o output is wired
176
// directly to the ack_i input.
177
always @(posedge clk_i)
178
begin
179
  if (rst_i || (rising_edge && ~stb_o)) ack_r <= 0;
180
  else if (stb_o) ack_r <= ack_i;
181
end
182
 
183
assign stb_o = divide_done && ~ack_r;
184
 
185
endmodule
186
 
187
 

powered by: WebSVN 2.1.0

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