/*
* Copyright (C) 2009 Kamil Dudka <xdudka00@stud.fit.vutbr.cz>
*
* This file is part of nucad (Non-Uniform CA Designer).
*
* nucad is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* nucad 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with nucad. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file CaFactory.cpp
* Implementation of basic circuits shipped with nucad, all registered in a
* factory class CaEvaluatorFactory.
*/
#include "config.h"
#include "CaFactory.h"
#include "Ca.h"
#include "Color.h"
#ifndef BUILDING_DOX
# include <map>
# include <set>
# include <stdexcept>
# include <sstream>
# define DOX_FAKE_PARENT(PARENT)
#else
# define DOX_FAKE_PARENT(PARENT) : public PARENT
#endif
#ifndef GF_INCLUDE_TRIVIAL
# define GF_INCLUDE_TRIVIAL 0
#endif
#ifndef GF_INCLUDE_COMPLEX
# define GF_INCLUDE_COMPLEX 0
#endif
/**
* Default evaluation parameters used by PrivateFactory if not specified
* otherwise.
*/
struct DefParams: public CaEvalParams {
DefParams() {
caSize = 5;
nSteps = 6;
}
};
/**
* Collection of universal binders for specified count of inputs/outputs.
* @param N_IN Count of input signals to bind.
* @param N_OUT Count of output signals to bind.
*/
template <int N_IN, int N_OUT>
struct Binder
{
/* no tag - this will fail intentionally */
};
/// generic binder - @b 2 input signals, @b 1 output signal
template <> struct Binder<2,1> DOX_FAKE_PARENT(Binder)
{
typedef BindInput<0, 1, 0,
BindInput<1, 3, 0,
BindOutput<0, 2, 4>
> > tag;
};
/// generic binder - @b 3 input signals, @b 1 output signal
template <> struct Binder<3,1> DOX_FAKE_PARENT(Binder)
{
typedef BindInput<0, 0, 0,
BindInput<1, 2, 0,
BindInput<2, 4, 0,
BindOutput<0, 2, 4>
> > > tag;
};
/// generic binder - @b 5 input signals, @b 1 output signal
template <> struct Binder<5,1> DOX_FAKE_PARENT(Binder)
{
typedef BindInput<0, 0, 0,
BindInput<1, 1, 0,
BindInput<2, 2, 0,
BindInput<3, 3, 0,
BindInput<4, 4, 0,
BindOutput<0, 2, 4>
> > > > > tag;
};
/// generic binder - @b 2 input signals, @b 2 output signals
template <> struct Binder<2,2> DOX_FAKE_PARENT(Binder)
{
typedef BindInput<0, 1, 0,
BindInput<1, 3, 0,
BindOutput<0, 1, 4,
BindOutput<1, 3, 4>
> > > tag;
};
/// generic binder - @b 3 input signals, @b 3 output signals
template <> struct Binder<3,3> DOX_FAKE_PARENT(Binder)
{
typedef BindInput<0, 0, 0,
BindInput<1, 2, 0,
BindInput<2, 4, 0,
BindOutput<0, 0, 4,
BindOutput<1, 2, 4,
BindOutput<2, 4, 4>
> > > > > tag;
};
/// generic binder - @b 4 input signals, @b 4 output signals
template <> struct Binder<4,4> DOX_FAKE_PARENT(Binder)
{
typedef BindInput<0, 0, 1,
BindInput<1, 0, 3,
BindInput<2, 1, 0,
BindInput<3, 3, 0,
BindOutput<0, 4, 3,
BindOutput<1, 4, 1,
BindOutput<2, 3, 4,
BindOutput<3, 1, 4>
> > > > > > > tag;
};
/**
* Static-only evaluator factory used as basic block of other (more specialised)
* factories.
* @param CIRCUIT IGate implementation to use as a circuit.
* @param BINDING IGateBinding implementation to use for circuit/CA binding.
* @param PARAMS CaEvalParams class specifying extra evaluation parameters if
* needed.
*/
template <class CIRCUIT, class BINDING, class PARAMS = DefParams>
class PrivateFactory {
public:
/**
* Return newly created instance of CaEvaluator object.
*/
static CaEvaluator* create() {
return new CaEvaluator(CIRCUIT(), BINDING(), PARAMS());
}
};
/**
* Static-only factory creating evaluators for GATE circuits.
* @param GATE Template specifying the gate. This parameter is a template
* accepting one parameter (count of input wires).
* @param N Count of input wires.
*/
template <template <int> class GATE, int N>
class GateFactory {
public:
typedef GATE<N> TGate;
typedef typename Binder<N,1>::tag TBinding;
public:
/**
* Return newly created instance of CaEvaluator object.
*/
static CaEvaluator* create() {
return PrivateFactory<TGate, TBinding>::create();
}
private:
/// abort compilation if the input binding is inappropriate
CT_ASSERT<TBinding::BOUND_INPUTS + 1 == 1 << N>
___COMPILE_TIME_ERROR__INAPPROPRIATE_INPUT_BINDING_FOR_GATE___;
/// abort compilation if the output binding is inappropriate
CT_ASSERT<TBinding::BOUND_OUTPUTS == 1>
___COMPILE_TIME_ERROR__INAPPROPRIATE_OUTPUT_BINDING_FOR_GATE___;
};
/**
* Static-only factory creating evaluators for wire-cross circuits.
* @param GATE Template specifying the gate. This parameter is a template
* accepting one parameter (count of input wires).
* @param N Count of input wires.
*/
template <int N, template <int> class GATE = CrossGate>
class WireCrossFactory {
public:
typedef GATE<N> TGate;
typedef typename Binder<N,N>::tag TBinding;
public:
/**
* Return newly created instance of CaEvaluator object.
*/
static CaEvaluator* create() {
return PrivateFactory<TGate, TBinding>::create();
}
private:
/// abort compilation if the input binding is inappropriate
CT_ASSERT<TBinding::BOUND_INPUTS + 1 == 1 << N>
___COMPILE_TIME_ERROR__INAPPROPRIATE_INPUT_BINDING_FOR_CROSS___;
/// abort compilation if the output binding is inappropriate
CT_ASSERT<TBinding::BOUND_OUTPUTS + 1 == 1 << N>
___COMPILE_TIME_ERROR__INAPPROPRIATE_OUTPUT_BINDING_FOR_CROSS___;
};
/**
* IGate implementation of N-inputs multiplexer (hard-wired).
* @todo fully generic mux definition in CaFactory.h
* @param N @b total count of inputs (data wires + select wires).
*/
template <int N> class MuxGate;
/**
* mux2 implementation (2+1 inputs, 1 output).
*/
template <>
class MuxGate<3>: public AbstractGate<3,1> {
public:
virtual IGate* clone() const {
return new MuxGate(*this);
}
virtual TBus operator[] (TBus in) const {
bool result = (in & 4)
? (in & 2)
: (in & 1);
return result;
}
};
/**
* mux4 implementation (4+2 inputs, 1 output).
*/
template <>
class MuxGate<6>: public AbstractGate<6,1> {
public:
virtual IGate* clone() const {
return new MuxGate(*this);
}
virtual TBus operator[] (TBus in) const {
const TBus select = (in & 0x30) >> 4;
switch (select) {
case 0: return static_cast<bool>(in & (1 << 0));
case 1: return static_cast<bool>(in & (1 << 1));
case 2: return static_cast<bool>(in & (1 << 2));
case 3: return static_cast<bool>(in & (1 << 3));
default:
assert(false);
}
}
};
/**
* Static-only factory creating evaluators for mux4 circuits.
*/
class Mux4Factory {
private:
#ifndef BUILDING_DOX
struct Mux4Params: public CaEvalParams {
Mux4Params() {
caSize = 3;
nSteps = 4;
}
};
#endif
public:
typedef MuxGate<6> TGate;
typedef BindInput<0, 0, 0,
BindInput<1, 0, 2,
BindInput<2, 2, 0,
BindInput<3, 2, 1,
BindInput<4, 0, 1,
BindInput<5, 1, 1,
BindOutput<0, 2, 2>
> > > > > > TBinding;
typedef Mux4Params TParams;
public:
/**
* Return newly created instance of CaEvaluator object.
*/
static CaEvaluator* create() {
return PrivateFactory<TGate, TBinding, TParams>::create();
}
private:
/// abort compilation if the input binding is inappropriate
CT_ASSERT<TBinding::BOUND_INPUTS == 63>
___COMPILE_TIME_ERROR__INAPPROPRIATE_INPUT_BINDING_FOR_MUX4___;
/// abort compilation if the output binding is inappropriate
CT_ASSERT<TBinding::BOUND_OUTPUTS == 1>
___COMPILE_TIME_ERROR__INAPPROPRIATE_OUTPUT_BINDING_FOR_MUX4___;
};
/**
* Collection of mul2x3/mul3x3 binders.
* @param N Count of input/output signals to bind.
* @param CA_SIZE CA's size in one direction.
*/
template <int N, int CA_SIZE>
struct MulBinder {
/* no tag - this will fail intentionally */
};
/// mul2x3 binder (CA 3x3)
template <> struct MulBinder<5, 3> DOX_FAKE_PARENT(MulBinder)
{
typedef BindInput<0, 0, 2,
BindInput<1, 0, 1,
BindInput<2, 2, 2,
BindInput<3, 2, 1,
BindInput<4, 2, 0,
BindOutput<0, 0, 2,
BindOutput<1, 2, 2,
BindOutput<2, 0, 1,
BindOutput<3, 2, 1,
BindOutput<4, 2, 0>
> > > > > > > > > tag;
};
/// mul3x3-ca3x3 binder
template <> struct MulBinder<6, 3> DOX_FAKE_PARENT(MulBinder)
{
typedef BindInput<0, 1, 2,
BindInput<1, 0, 1,
BindInput<2, 2, 0,
BindInput<3, 2, 2,
BindInput<4, 1, 1,
BindInput<5, 0, 0,
BindOutput<0, 1, 2,
BindOutput<1, 0, 2,
BindOutput<2, 0, 1,
BindOutput<3, 2, 1,
BindOutput<4, 2, 0,
BindOutput<5, 1, 0>
> > > > > > > > > > > tag;
};
/// mul3x3-ca4x4 binder
template <> struct MulBinder<6, 4> DOX_FAKE_PARENT(MulBinder)
{
typedef BindInput<0, 2, 3,
BindInput<1, 1, 2,
BindInput<2, 0, 1,
BindInput<3, 3, 2,
BindInput<4, 2, 1,
BindInput<5, 1, 0,
BindOutput<0, 2, 3,
BindOutput<1, 3, 2,
BindOutput<2, 1, 2,
BindOutput<3, 2, 1,
BindOutput<4, 0, 1,
BindOutput<5, 1, 0>
> > > > > > > > > > > tag;
};
/**
* Static-only factory creating evaluators for mul2x3/mul3x3 circuits.
* @param N Count of input/output signals to bind.
* @param CA_SIZE CA's size in one direction.
*/
template <int N, int CA_SIZE>
class MulFactory {
private:
static const size_t MASK = (1 << N) - 1;
#ifndef BUILDING_DOX
struct MulParams: public CaEvalParams {
MulParams() {
caSize = CA_SIZE;
nSteps = 13;
}
};
#endif
public:
typedef MulGate<N> TGate;
typedef typename MulBinder<N, CA_SIZE>::tag TBinding;
typedef MulParams TParams;
public:
/**
* Return newly created instance of CaEvaluator object.
*/
static CaEvaluator* create() {
return PrivateFactory<TGate, TBinding, TParams>::create();
}
private:
/// abort compilation if the input binding is inappropriate
CT_ASSERT<TBinding::BOUND_INPUTS == MASK>
___COMPILE_TIME_ERROR__INAPPROPRIATE_INPUT_BINDING_FOR_MUX4___;
/// abort compilation if the output binding is inappropriate
CT_ASSERT<TBinding::BOUND_OUTPUTS == MASK>
___COMPILE_TIME_ERROR__INAPPROPRIATE_OUTPUT_BINDING_FOR_MUX4___;
};
// /////////////////////////////////////////////////////////////////////////////
// CaEvaluatorFactory implementation
#ifndef BUILDING_DOX
struct CaEvaluatorFactory::Private {
typedef std::map<std::string, CaEvaluator* (*)()> TMap;
typedef std::set<std::string> TSet;
TMap map;
TSet trivialSet;
TSet complexSet;
};
#endif
CaEvaluatorFactory::CaEvaluatorFactory():
d(new Private)
{
// 2-inputs gates
d->map["and2"] = GateFactory <AndGate, 2> ::create;
d->map[ "or2"] = GateFactory < OrGate, 2> ::create;
d->map["xor2"] = GateFactory <XorGate, 2> ::create;
// 3-inputs gates
d->map["mux2"] = GateFactory <MuxGate, 3> ::create;
d->map["and3"] = GateFactory <AndGate, 3> ::create;
d->map[ "or3"] = GateFactory < OrGate, 3> ::create;
d->map["xor3"] = GateFactory <XorGate, 3> ::create;
// 5-inputs gates
d->map["and5"] = GateFactory <AndGate, 5> ::create;
d->map[ "or5"] = GateFactory < OrGate, 5> ::create;
// xor5 converges immediately with incremental nStep
// TODO: implement incremental nStep as an option
d->map["xor5"] = GateFactory <XorGate, 5> ::create;
// wire crossing
d->map["cross2"] = WireCrossFactory <2> ::create;
d->map["cross3"] = WireCrossFactory <3> ::create;
d->map["cross2x2"] = WireCrossFactory <4, BusGate> ::create;
// 4-input mux converges immediately with incremental caStep
// TODO: implement incremental nStep as an option
d->map["mux4"] = Mux4Factory::create;
// mul
d->map["mul2x3"] = MulFactory<5, 3>::create;
d->map["mul3x3-ca3x3"] = MulFactory<6, 3>::create;
d->map["mul3x3-ca4x4"] = MulFactory<6, 4>::create;
// mark 2-inputs gates as trivial
d->trivialSet.insert("and2");
d->trivialSet.insert( "or2");
d->trivialSet.insert("xor2");
d->trivialSet.insert("cross2");
// mark 4+ inputs gates as complex
d->complexSet.insert("cross2x2");
d->complexSet.insert("mux4");
d->complexSet.insert("and5");
d->complexSet.insert( "or5");
d->complexSet.insert("xor5");
d->complexSet.insert("mul2x3");
d->complexSet.insert("mul3x3-ca3x3");
d->complexSet.insert("mul3x3-ca4x4");
}
CaEvaluatorFactory::~CaEvaluatorFactory() {
delete d;
}
CaEvaluator* CaEvaluatorFactory::create(const std::string &what) {
// circuit lookup
Private::TMap::iterator i = d->map.find(what);
if (i != d->map.end())
return (i->second)();
// circuit not found
// throw an exception containing list of available circuits
std::ostringstream str;
str << "invalid circuit: "
<< Color(C_LIGHT_RED) << what << Color(C_NO_COLOR)
<< " (implemented circuits are: " << Color(C_LIGHT_BLUE);
for (i = d->map.begin(); i != d->map.end(); ++i) {
if (i != d->map.begin())
str << ", ";
str << i->first;
}
str << Color(C_NO_COLOR) << ")";
throw std::runtime_error(str.str());
}
void CaEvaluatorFactory::printList(std::ostream &str) {
Private::TMap::iterator i;
for (i = d->map.begin(); i != d->map.end(); ++i) {
#if !GF_INCLUDE_TRIVIAL
if (d->trivialSet.end() != d->trivialSet.find(i->first))
continue;
#endif
#if !GF_INCLUDE_COMPLEX
if (d->complexSet.end() != d->complexSet.find(i->first))
continue;
#endif
str << i->first << std::endl;
}
}