Sample Source Code

timer.cxx Source File

// Copyright (C) 2001 Red Hat
//
// Description: A complete version of the example given in the
// Component Developers' Kit (CDK) reference guide.
//
// Noteworthy remarks about the code in this source file:
// * For clarity, the C++ "using" keyword is used sparingly and
//   all members
//   of the "sid::" namespace appear fully qualified.
// * Documentation for the Standard Template Library, plus any obscure
//   C++ syntax you may encounter can be discovered in the 3rd edition 
//   of ``The C++ Programming Language'', by Stroustrup. 
//   ISBN 0-201-88954-4.

#include <sidcomp.h>
#include <sidso.h>
#include <sidtypes.h>

#include <set>
#include <string>
#include <vector>


// Wrap everything in a unique namespace 
namespace timer_example
{


// Import standard types into this namespace.
using namespace std;


// None of the sid API routines is allowed to throw an exception.
#define NT throw()


class Timer: public virtual sid::component
{
public:
  Timer()
    :scheduler_pin(0), clock_pin(this), bus(this), enabled(false) { }
  
  // Provide implementations for abstract methods in sid::component.
  // See include/sidcomp.h.
  
  vector<string> pin_names() NT;
  sid::pin* find_pin(const string& name) NT;
  sid::component::status connect_pin(const string& name, sid::pin* pin) NT;
  sid::component::status disconnect_pin(const string& name, sid::pin* pin) NT;
  vector<sid::pin*> connected_pins(const string& name) NT;
  
  vector<string> accessor_names() NT;
  sid::component::status connect_accessor(const string& name, sid::bus* bus) NT;
  sid::component::status disconnect_accessor(const string& name, sid::bus* bus) NT;
  
  vector<string> bus_names() NT;
  sid::bus* find_bus(const string& name) NT;
  sid::bus* connected_bus(const string& name) NT;
  
  vector<string> attribute_names() NT;
  vector<string> attribute_names(const string& category) NT;
  string attribute_value(const string& name) NT;
  sid::component::status set_attribute_value(const string& name, const string& value) NT;
  
  vector<string> relationship_names() NT;
  sid::component::status relate(const string& rel, sid::component* c) NT;
  sid::component::status unrelate(const string& rel, sid::component* c) NT;
  vector<sid::component*> related_components(const string& rel) NT;
  
private:
  // A netlist, which tracks pins connected to the interrupt pin.
  typedef set<sid::pin*> netlist_t;
  netlist_t intpin_netlist;

  // A handle to the scheduler's "control" pin.
  // The scheduler is a preexisting SID component which can schedule
  // and deliver events to a component via an "event" pin.
  // See schedule() for more details.
  sid::pin* scheduler_pin;

  // Schedule an event for some time in the future.
  void schedule(sid::host_int_4 time);

  // Cancel any pending events.
  void cancel();

  // Reschedule an event.
  void reset_schedule();

  // This method is called whenever the scheduler delivers an event,
  // because the "divided-clock-event" pin will be connected to the
  // scheduler.  It is a specialized pin with these fixed semantics.
  // Refer to class clock_pin_t below.
  void tick();

  // Drive a value on the interrupt pin (which propagates to all
  // connected pins).
  void drive_interrupt(sid::host_int_4 value);

  class clock_pin_t: public sid::pin
  {
    // clock_pin_t is a specialized pin.
    // It calls timer->tick() whenever a value is driven. 
  public:
    clock_pin_t(Timer* t): timer(t) { }
    void driven(sid::host_int_4 value) NT { timer->tick(); }
  private:
    Timer* timer;
  };
  friend class clock_pin_t;
  clock_pin_t clock_pin;


  // register_bus is a specialized bus.
  // It handles the majority of the component's functionality, since
  // that is mostly controlled by the timer's register set.

  class register_bus: public sid::bus
  {
  public:
    register_bus(Timer* t): timer(t) { }

    // Prototypes for bus read/write methods of all kinds.

    sid::bus::status read(sid::host_int_4 addr, sid::little_int_1& data) NT;
    sid::bus::status read(sid::host_int_4 addr, sid::big_int_1& data) NT;
    sid::bus::status read(sid::host_int_4 addr, sid::little_int_2& data) NT;
    sid::bus::status read(sid::host_int_4 addr, sid::big_int_2& data) NT;
    sid::bus::status read(sid::host_int_4 addr, sid::little_int_4& data) NT;
    sid::bus::status read(sid::host_int_4 addr, sid::big_int_4& data) NT;
    sid::bus::status read(sid::host_int_4 addr, sid::little_int_8& data) NT;
    sid::bus::status read(sid::host_int_4 addr, sid::big_int_8& data) NT;

    sid::bus::status write(sid::host_int_4 addr, sid::little_int_1 data) NT;
    sid::bus::status write(sid::host_int_4 addr, sid::big_int_1 data) NT;
    sid::bus::status write(sid::host_int_4 addr, sid::little_int_2 data) NT;
    sid::bus::status write(sid::host_int_4 addr, sid::big_int_2 data) NT;
    sid::bus::status write(sid::host_int_4 addr, sid::little_int_4 data) NT;
    sid::bus::status write(sid::host_int_4 addr, sid::big_int_4 data) NT;
    sid::bus::status write(sid::host_int_4 addr, sid::little_int_8 data) NT;
    sid::bus::status write(sid::host_int_4 addr, sid::big_int_8 data) NT;

  private:
    Timer* timer;
  };
  friend class register_bus;
  register_bus bus;

  // Data members that represent the timer's internal state.

  bool enabled;
  sid::host_int_2 load_value, prescale, counter;
  enum timer_mode { PERIODIC, FREERUNNING } mode;
};


// Return a list of pin names which are visible to other components.

vector<string>
Timer::pin_names() NT
{
  vector<string> pins;
  pins.push_back("divided-clock-event");
  return pins;
}


// Return a list of pins that are connected to a named pin.
// We recognize "interrupt" and "divided-clock-control".

vector<sid::pin*>
Timer::connected_pins(const string& name) NT
{
  vector<sid::pin*> pins;
  netlist_t::const_iterator it;

  if (name == "interrupt")
    {
      for (it = intpin_netlist.begin(); it != intpin_netlist.end();
           it++)
        {
          pins.push_back(*it);
        }
      return pins;
    }
  else if (name == "divided-clock-control")
    {
      pins.push_back(scheduler_pin);
      return pins;
    }
  return vector<sid::pin*>();
}


// Connect a pin to a named pin.
// We recognize "interrupt" and "divided-clock-control".

// We allow multiple pins to be connected to the interrupt pin (with
// infinite fan-out!), so these are kept in a netlist.  For
// efficiency, the STL container chosen for the netlist ensures that
// no duplicate pin handles are stored.

sid::component::status
Timer::connect_pin(const string& name, sid::pin* pin) NT
{
  if (name == "interrupt")
    {
      // Add this pin to the netlist.
      intpin_netlist.insert(intpin_netlist.end(), pin);
      return sid::component::ok;
    }
  else if (name == "divided-clock-control")
    {
      // Reassign the scheduler pin.
      scheduler_pin = pin;
      return sid::component::ok;
    }
  return sid::component::not_found;
}

// Disconnect a pin from a named pin.

sid::component::status
Timer::disconnect_pin(const string& name, sid::pin* pin) NT
{
  if (name == "interrupt")
    {
      // Remove this pin from the netlist.
      if (intpin_netlist.erase(pin) > 0)
        return sid::component::ok;
    }
  else if (name == "divided-clock-control"& & scheduler_pin == pin)
    {
      // Elsewhere, we make sure to not use this pin if it's null.
      scheduler_pin = 0;
      return sid::component::ok;
    }
  return sid::component::not_found;
}


// Find a pin of a given name.
// We only recognize "divided-clock-event".

sid::pin*
Timer::find_pin(const string& name) NT
{
  if (name == "divided-clock-event")
    return& clock_pin;

  return 0;
}


vector<string>
Timer::accessor_names() NT
{
  // No accessors.
  return vector<string>();
}


sid::component::status
Timer::connect_accessor(const string& name, sid::bus* bus) NT
{
  // No accessors; any name is unknown.
  return sid::component::not_found;
}


sid::component::status
Timer::disconnect_accessor(const string& name, sid::bus* bus) NT
{
  // No accessors; any name is unknown.
  return sid::component::not_found;
}


// Return a list of bus names. We have just one--"registers".

vector<string>
Timer::bus_names() NT
{
  vector<string> buses;
  buses.push_back("registers");
  return buses;
}

sid::bus*
Timer::find_bus(const string& name) NT
{
  if (name == "registers")
    return& bus;
  return 0;
}


sid::bus*
Timer::connected_bus(const string& name) NT
{
  // No connected buses; return a null pointer.
  return 0;
}


vector<string>
Timer::attribute_names() NT
{
  // No attributes; return an empty vector.
  return vector<string>();
}

vector<string>
Timer::attribute_names(const string& category) NT
{
  // No attributes, regardless of category. Return an empty vector.
  return vector<string>();
}

string
Timer::attribute_value(const string& name) NT
{
  // No attributes--return the empty string for any attribute value.
  return string();
}

sid::component::status
Timer::set_attribute_value(const string& name, const string& value) NT
{
  // No attributes--return not_found regardless of attribute name.
  return sid::component::not_found;
}


vector<sid::component*>
Timer::related_components(const string& rel) NT
{
  // No related components.
  return vector<sid::component*>();
}

sid::component::status
Timer::unrelate(const string& rel, sid::component* c) NT
{
  // No related components; always unfound.
  return sid::component::not_found;
}

sid::component::status
Timer::relate(const string& rel, sid::component* c) NT
{
  // No related components; always unfound.
  return sid::component::not_found;
}

vector<string>
Timer::relationship_names() NT
{
  // No relations.
  return vector<string>();
}



void
Timer::drive_interrupt(sid::host_int_4 value) NT
{
  // Iterate the netlist, driving the value to all pins connected to
  // the interrupt pin.

  for (netlist_t::const_iterator it = intpin_netlist.begin();
       it != intpin_netlist.end();
       it++)
    {
      (*it)->driven(value);
    }
}


// Schedule an event to be delivered at a later time.

void
Timer::schedule(sid::host_int_4 time)
{
  // The scheduler component tests bit 31 of a value carried on its
  // "control" pin.  If this bit is set, the event will be delivered
  // routinely at the specified interval.  Otherwise, the event will
  // only occur once.

  assert ((time&  0x80000000) == 0);
  assert ((time&  0x7FFFFFFF) != 0);
  
  if (scheduler_pin)
    scheduler_pin->driven(0x80000000 | time);
}


// Cancel any pending event.

void
Timer::cancel()
{
  // Cancel the event by driving a zero value to the scheduler.

  if (scheduler_pin)
    scheduler_pin->driven(0);
}


// Reset the schedule, in case the timer's enable or divisor registers
// have been altered.

void
Timer::reset_schedule()
{
  cancel();
  
  if (!enabled)
    return;
  
  assert (prescale <= 2);
  unsigned divisor = 1 << (prescale * 4);
  
  schedule(divisor);
}


// Handle 32-bit (little endian) reads.
// If the address is not 32-bit aligned or does not match any register
// address, return an error.

sid::bus::status
Timer::register_bus::read(sid::host_int_4 addr, sid::little_int_4& data) NT
{
  if (addr % 4 != 0)
    return sid::bus::misaligned;
  
  switch (addr)
    { 
    case 0x0:
      data = timer->load_value;
      break;
      
    case 0x4:
      data = timer->counter;
      break;
      
    case 0x8:
      data =
        (timer->enabled << 7) | 
        (timer->mode << 6) | 
        (timer->prescale << 2);
      break;
      
    case 0xC:
      break;
      
    default:
      return sid::bus::unmapped;
    }

  return sid::bus::ok;
}


// Handle 32-bit (big endian) reads.
// Just do a little endian read and rearrange the result.

sid::bus::status
Timer::register_bus::read(sid::host_int_4 addr, sid::big_int_4& data) NT
{
  sid::little_int_4 le_data;
  sid::bus::status st = read(addr, le_data);
  data.set_target_memory_value (le_data.target_memory_value ());
  return st;
}


// Handle 32-bit (little endian) writes.
// If the address is not 32-bit aligned or does not match any register
// address, return an error.

sid::bus::status
Timer::register_bus::write(sid::host_int_4 addr, sid::little_int_4 data) NT
{
  if (addr % 4 != 0)
    return sid::bus::misaligned;
  
  switch (addr)
    {
    case 0x0:
      // A write to LOAD_REG.
      // Clear top 16 bits when loading a new value.
      timer->load_value = data&  0xFFFF;
      // Reset the counter value.
      timer->counter = timer->load_value;
      break;

    case 0x4:
      break;

    case 0x8:
      // A write to CTL_REG.
      timer->prescale = (data&  0x0C) >> 2;
      timer->enabled = ((data&  0x80) == 0x80);
      timer->mode = (data&  0x40) ? PERIODIC : FREERUNNING;
      timer->reset_schedule();
      break;

    case 0xC:
      timer->drive_interrupt(0);
      break;

    default:
      return sid::bus::unmapped;
    }

  return sid::bus::ok;
}


// Handle 32-bit (big endian) writes.
// Just rearrange the data and do a little endian write.

sid::bus::status
Timer::register_bus::write(sid::host_int_4 addr, sid::big_int_4 data) NT
{
  sid::little_int_4 le_data;
  le_data.set_target_memory_value (data.target_memory_value ());
  return write(addr, le_data);
}

// For simplicity, bus accesses that are not 32-bits wide are not
// handled.  Ideally, different widths should be handled sensibly.
// For example, a one-byte write to location n+1, where n is 32-bit
// aligned, should behave as you might expect.

sid::bus::status
Timer::register_bus::read(sid::host_int_4 addr, sid::little_int_1& data) NT
{
  return sid::bus::unpermitted;
}

sid::bus::status
Timer::register_bus::read(sid::host_int_4 addr, sid::big_int_1& data) NT
{
  return sid::bus::unpermitted;
}

sid::bus::status
Timer::register_bus::read(sid::host_int_4 addr, sid::little_int_2& data) NT
{
  return sid::bus::unpermitted;
}

sid::bus::status
Timer::register_bus::read(sid::host_int_4 addr, sid::big_int_2& data) NT
{
  return sid::bus::unpermitted;
}

sid::bus::status
Timer::register_bus::read(sid::host_int_4 addr, sid::little_int_8& data) NT
{
  return sid::bus::unpermitted;
}

sid::bus::status
Timer::register_bus::read(sid::host_int_4 addr, sid::big_int_8& data) NT
{
  return sid::bus::unpermitted;
}

sid::bus::status
Timer::register_bus::write(sid::host_int_4 addr, sid::little_int_1 data) NT
{
  return sid::bus::unpermitted;
}

sid::bus::status
Timer::register_bus::write(sid::host_int_4 addr, sid::big_int_1 data) NT
{
  return sid::bus::unpermitted;
}

sid::bus::status
Timer::register_bus::write(sid::host_int_4 addr, sid::little_int_2 data) NT
{
  return sid::bus::unpermitted;
}

sid::bus::status
Timer::register_bus::write(sid::host_int_4 addr, sid::big_int_2 data) NT
{
  return sid::bus::unpermitted;
}

sid::bus::status
Timer::register_bus::write(sid::host_int_4 addr, sid::little_int_8 data) NT
{
  return sid::bus::unpermitted;
}

sid::bus::status
Timer::register_bus::write(sid::host_int_4 addr, sid::big_int_8 data) NT
{
  return sid::bus::unpermitted;
}

// Called when the scheduled event arrives.
// Decrement the counter register and check for an interrupt
// condition--and if so, drive the interrupt pin.

void
Timer::tick()
{
  if (!enabled) return;
  
  counter--;
  if (counter == 0)
    {
      if (mode == PERIODIC)
        {
          counter = load_value;
          drive_interrupt(1);
        }
      else
        {
          // Rolls over from maximum value; no interrupts.
          counter = 0xFFFF;
        }
    }
}


// Return a list of component types supported by this library.

static vector<string>
TimerListTypes() NT
{
  vector<string> types;
  types.push_back("hw-timer-example");
  return types;
}

// Instantiate a component, given a specified component type.

static sid::component*
TimerCreate(const string& typeName) NT
{
  if (typeName == "hw-timer-example")
    return new Timer();

  return 0;
}

// Destruct a component instance.

static void
TimerDelete(sid::component* c) NT
{
  delete dynamic_cast<Timer*>(c);
}


} // end namespace


// This symbol is used by the library loader to validate the library
// and instantiate components of the types supported by this library.

extern const sid::component_library example_component_library;

const sid::component_library example_component_library DLLEXPORT =
{
  sid::COMPONENT_LIBRARY_MAGIC,
  & timer_example::TimerListTypes,
  & timer_example::TimerCreate,
  & timer_example::TimerDelete
};