Возможно, вы захотите разрешить более трех состояний, если вы пытаетесь смоделировать аппаратные линии. Вот что Altera использует в симуляторе FPGA:
- 1: Сильный Высокий (транзистор, приводимый в движение VDD)
- 0: Сильный низкий (транзистор, управляемый на VSS)
- H: Слабый Высокий (сопротивление резистора до VDD)
- L: Weak Low (сопротивление резистора до VSS)
- Z: высокий импеданс (ненаправленная линия)
- X: неизвестно
- W: Слабый Неизвестный
- U: неинициализированный
- DC: не волнует
Возможно, вам не нужны W, U и DC. Вы можете бросить H, L и Z, если ваши автобусы всегда ездят.
Verilog использует еще больше уровней для моделирования на уровне ворот , с семью мощностями привода для каждого логического уровня. Дополнительные уровни моделируют емкостное воздействие на сигнальные линии. Это, вероятно, больше, чем вам нужно.
РЕДАКТИРОВАТЬ: Поскольку вы упомянули векторы битов, я должен сказать, что, ИМХО, вы не найдете такую библиотеку для публичного использования и постоянно обновлялись, потому что 1) там просто не так много программистов нуждаются в такой вещи, и 2) даже среди них, из-за вышеупомянутых опций для моделирования линейных уровней, есть небольшая совместимость. Трибоолы Boost могут быть запущены в эксплуатацию, но они не будут быстрыми, поскольку операции будут поэлементными, а хранилище не будет оптимизировано, но они могут быть вашим единственным выбором, если у кого-то есть аллергия на написание собственной библиотеки, которая делает именно то, что вам нужно.
Например, допустим, вам нужен класс, который представляет векторы битов с четырьмя возможными уровнями: 1, 0, X и Z. Во-первых, вы должны определить эквивалентные битовые комбинации для каждого уровня (например, X = 00, Z = 01, 0 = 10, 1 = 11; X был выбран в качестве состояния сброса)
Для каждой операции вы должны записать таблицу истинности, предпочтительно в Карта Карно форма:
op: & | X (00) | Z (01) | 1 (11) | 0 (10)
-------+--------+--------+--------+--------
X (00) | X (00) | X (00) | X (00) | X (00)
-------+--------+--------+--------+--------
Z (01) | X (00) | X (00) | X (00) | X (00)
-------+--------+--------+--------+--------
1 (11) | X (00) | X (00) | 1 (11) | 0 (10)
-------+--------+--------+--------+--------
0 (10) | X (00) | X (00) | 0 (10) | 0 (10)
(Обратите внимание, что X много выигрывает. Это верно для большинства операций.)
Затем выведите булевы уравнения из K-карты:
C = A & B
=> C1 = A1 & B1
C0 = A1 & B1 & A0 & B0 = C1 & A0 & B0
Наконец, переведите это на C ++:
template<size_t NBits> class BitVector
{private:
enum { NWords = (NBits+31)/32 };
int32_t storage[NWords][2];
public:
BitVector<NBits> operator &(BitVector<NBits>& rhs)
{ BitVector<NBits> result;
for(unsigned k = 0; k < NWords; ++k)
{ int32_t x = storage[k][1] & rhs.storage[k][0];
result.storage[k][1] = x;
result.storage[k][0] = storage[k][0] & rhs.storage[k][0] & x;
}
return result;
}
};
(Примечание: я не проверял код выше, поэтому используйте на свой страх и риск.)
Все должно быть переделано, если набор разрешенных уровней изменяется. Вот почему эти библиотеки, как правило, слишком специализированы, чтобы помещать их в библиотеки общего назначения, такие как Boost.
EDIT2: Меня только что осенило, что шаблонный класс BitVector имеет один из немногих случаев использования, где имеет смысл перегрузка оператора запятой:
template<size_t NBitsR>
BitVector<NBits+NBitsR> operator ,(const BitVector<NBitsR>& rhs);
Это позволяет объединять битовые векторы:
BitVector<8> a("1110 0111");
BitVector<4> b("0000");
BitVector<12> c = (a, b); // == BitVector<12>("0000 1110 0111")
... который кажется наиболее интуитивным способом дополнить один вектор размером другого (легко показать, что такое заполнение должно не быть неявным, ever ) или объединить векторы.
EDIT3: Меня просто осенило (да, я медленный), что, если бы вы действительно хотели сделать обобщенную версию этого, вы могли бы сделать это с основанный на политике дизайн :
struct TwoLevelLogic
{ enum
{ kNumPlanes = 1
};
static void And(int32_t[] result, int32_t[] lhs, int32_t[] rhs)
{ result[0] = lhs[0] & rhs[0];
}
};
struct FourLevelLogic
{ enum
{ kNumPlanes = 2
};
static void And(int32_t[] result, int32_t[] lhs, int32_t[] rhs)
{ int32_t x = lhs[1] & rhs[1];
result[1] = x;
result[0] = lhs[0] & rhs[0] & x;
}
};
template<typename LogicType, size_t NBits>
class BitVector
{private:
enum { NWords = (NBits+31)/32 };
int32_t storage[NWords][LogicType::kNumPlanes];
public:
BitVector<LogicType, NBits> operator &(BitVector<LogicType, NBits>& rhs)
{ BitVector<LogicType, NBits> result;
for(unsigned k = 0; k < NWords; ++k)
LogicType::And(result.storage[k], storage[k], rhs.storage[k]);
return result;
}
};
template<size_t NBits>
class BitVector4L: public BitVector<FourLevelLogic, NBits> {};
Тогда, если вы хотите использовать другое логическое представление, скажем, девять уровней или даже два, вы можете определить новые политики для поддержки этих форматов. Кроме того, вы можете вычислить различные политики в разных областях вашей проблемы (скажем, 4 уровня для вашей платы, 9 для чипа и 2 для симулятора процессора) и определить функции преобразования для преодоления пробелов.
Опять же, я не пытался построить это, поэтому я не уверен, что это оптимизирует идеально.