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] - Rev 6

Compare with Previous | Blame | View Log

//---------------------------------------------------------------------------
// PWM reader 
// ...for variable frequency PWM signals from real world sensors.
//
//
// Description: See description below (which suffices for IP core
//                                     specification document.)
//
// Copyright (C) 2002 John Clayton and OPENCORES.ORG (this Verilog version)
//
// This source file may be used and distributed without restriction provided
// that this copyright statement is not removed from the file and that any
// derivative work contains the original copyright notice and the associated
// disclaimer.
//
// This source file is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation;  either version 2.1 of the License, or
// (at your option) any later version.
//
// This source is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
// License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this source.
// If not, download it from http://www.opencores.org/lgpl.shtml
//
//---------------------------------------------------------------------------
//
// Author: John Clayton
// Date  : Jan  28, 2003
//
// (NOTE: Date formatted as day/month/year.)
// Update: 11/03/03 Saved "pwd_counter.v" under the new name "pwm_reader.v" so
//                  that the divide_uu module can be integrated into this new
//                  module.
// Update: 13/03/03 Finished coding module, began testing.
//
// Update: 03/06/03 Added ack_r signal, so that stb_o would not oscillate when
//                  connected directly to the ack_i input.
//
// Description
//---------------------------------------------------------------------------
// This module reads the pulse width of a repetitive variable duty cycle
// digital input.  Pulse Width Modulated (PWM) inputs are produced by many 
// devices, including certain real world sensors.
// In this case, the specific sensor for which this module was created
// is the Analog Devices ADXL202e (a +/- 2g. dual accelerometer.)
//
// Operation of this module is very simple:
//
// An up-counter (T) counts clocks in one period of the incoming PWM signal.
// The T counter is reset with each rising edge of the incoming PWM waveform.
//
// Another (clock-enabled) up-counter (S) counts the clocks during the time
// when the PWM signal input is high.
// The S counter is also reset at the rising edge of the incoming PWM wave.
//
// To derive the duty cycle as a fraction, a serial divider calculates S/T.
//
// Once the fraction is obtained, the output is presented at "dat_o."
// The signal "stb_o" (strobe output) indicates that a valid reading is
// present on the data bus output.  The strobe will remain high until the
// reading is acknowledged by pulsing "ack_i" for one clock cycle.
//
// If "stb_o" is tied directly to "ack_i" then each time a new reading is 
// obtained a pulse will be produced at "stb_o" which is one single clock wide.
//
// It is important to note that in the absence of any ack_i signal, the unit
// will hold the contents of the last reading, and any new readings from the
// PWM device will be missed.
//
//---------------------------------------------------------------------------
 
 
module pwm_reader (
                    clk_i,
                    clk_en_i,
                    rst_i,
                    pwm_signal_i,
                    ack_i,
                    dat_o,
                    stb_o
                    );
 
parameter COUNTER_WIDTH_PP     = 10; // Size to hold (max_pwm_period)*(Fclk_i)
parameter DAT_WIDTH_PP         = 8;  // Size of fraction from divider
parameter DIV_COUNT_WIDTH_PP   = 3;  // Must be enough bits to hold the number:
                                     // DAT_WIDTH_PP-1
 
input clk_i;
input clk_en_i;
input rst_i;
input pwm_signal_i;
input ack_i;
 
output [DAT_WIDTH_PP-1:0] dat_o;
output stb_o;
 
// Local signals
reg  [COUNTER_WIDTH_PP-1:0] s_cnt;
reg  [COUNTER_WIDTH_PP-1:0] t_cnt;
reg  pwm_ff_1;
reg  pwm_ff_2;
reg  ack_r;
 
wire rising_edge;
wire divide_done;
 
//-----------------------------------------------------------------------
// Try to avoid metastability by running the input signal through
// a D flip flop.
always @(posedge clk_i)
begin
  if (rst_i) pwm_ff_1 <= 0;
  else pwm_ff_1 <= pwm_signal_i;
end
 
// This is the rising edge detection
always @(posedge clk_i)
begin
  if (rst_i) pwm_ff_2 <= 0;
  else pwm_ff_2 <= pwm_ff_1;
end
assign rising_edge = pwm_ff_1 && ~pwm_ff_2;
 
// This is period counter
always @(posedge clk_i)
begin
  if (rst_i || rising_edge) t_cnt <= 0;
  else if (clk_en_i) t_cnt <= t_cnt + 1;
end
 
// This is duty cycle counter
always @(posedge clk_i)
begin
  if (rst_i || rising_edge) s_cnt <= 0;
  else if (clk_en_i && pwm_signal_i) s_cnt <= s_cnt + 1;
end
 
// This unit provides for the division s_cnt/t_cnt.
// The values in the counters are latched into the divider whenever
// the divide_i input is pulsed.
// The divider ignores the inputs whenever "divide_i" is not pulsed, so
// there is no problem with the counters continually advancing until
// the division is started.
//
// Since the result is expected to be purely fractional, the quotient part
// is all zero, and is thrown away on purpose.  (A special divider could be
// build possibly to avoid this?)
serial_divide_uu #(
                   COUNTER_WIDTH_PP,    // M_PP (dividend width)
                   COUNTER_WIDTH_PP,    // N_PP (divisor width)
                   DAT_WIDTH_PP,        // R_PP (remainder width)
                   COUNTER_WIDTH_PP,    // S_PP (skip integer part of quotient)
                   DIV_COUNT_WIDTH_PP,  // counter bits
                   1                    // need output holding buffers?
                   )
  divider_unit
  (
   .clk_i(clk_i),
   .clk_en_i(1'b1),
   .rst_i(rst_i),
   // New divide is not allowed until previous value is read or acknowledged
   .divide_i(rising_edge && ~stb_o),
   .dividend_i(s_cnt),
   .divisor_i(t_cnt),
   .quotient_o(dat_o),
   .done_o(divide_done)
  );
 
// This latches the ack_i input, so that at least one pulse of stb_o is produced
// before the ack_i takes it down low again, in case the stb_o output is wired
// directly to the ack_i input.
always @(posedge clk_i)
begin
  if (rst_i || (rising_edge && ~stb_o)) ack_r <= 0;
  else if (stb_o) ack_r <= ack_i;
end
 
assign stb_o = divide_done && ~ack_r;
 
endmodule
 
 
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

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