vak: (Default)
[personal profile] vak
В прошлый раз я показал демо с двумя процессами, работающими параллельно, но без всякого взаимодействия. Давайте теперь добавим взаимодействие. Введём понятие сигнала. Поскольку я веду дело к моделированию цифровой логики, такой термин будет в самый раз.

Сигнал это по сути переменная, значение которой процессы могут использовать или изменять. С точки зрения хардвера сигнал состоит из одного или нескольких битов (в нашем случае до 64 битов). Но самое полезное в концепции сигнала - что процессы могут реагировать на его изменение. Обычно в моделировании каждый процесс определяет значение одного сигнала (выход), и при этом использует значения других сигналов (входы). Когда сигнал изменяет значение, процессы, чувствительные к нему, разблокируются и получают возможность перевычислить свои выходы.

Введём класс signal_t, реализующий сигналы. Создавать сигналы будем в виде глобальных переменных. Все процессы могут ссылаться на нужные им сигналы. Каждому сигналу дадим имя в виде строки - пригодится при отладке. Вторым аргументом конструктора сигналу можно присвоить начальное значение (необязательное). По умолчанию это ноль. Текущее значение сигнала можно опросить методом get(). Вот пример создания четырёх сигналов:
signal_t clk("clock");     // Синхросигнал
signal_t reset("reset"); // Общий сброс
signal_t enable("enable"); // Сигнал разрешения для счётчика
signal_t count("count"); // 4-битный счётчик
В предыдущем посте упоминался процесс - генератор синхросигнала. Вот он в законченном виде. Метод simulator::set() изменяет значение нужного сигнала на указанное значение.
co_void_t do_clock(simulator_t &sim)
{
for (;;) {
sim.set(clk, 1);
co_await sim.delay(1);
sim.set(clk, 0);
co_await sim.delay(1);
}
}
Но установка значений сигналов это еще не взаимодействие процессов. Взаимодействие начинается, когда процессы реагируют на изменение сигналов. Каждый процесс определяет для себя, какие сигналы его интересуют, и по какому фронту: переднему, заднему или обоим.

Введём класс sensitivity_t, каждый объект которого будет привязывать один сигнал к одному (текущему) процессу, по заданному фронту. Если нужна чувствительность к нескольким сигналам - процесс вправе создать несколько таких привязок.

Вот пример реализации 4-битного синхронного счётчика, со сбросом и сигналом разрешения. Как традиционно принято в цифровой логике, счетчик изменяет значение по переднему фронту синхросигнала:
co_void_t do_counter(simulator_t &sim)
{
sensitivity_t hook(sim, clk, POSEDGE);
for (;;) {
co_await co_await_t{};
if (reset.get() != 0) {
sim.set(count, 0);
} else if (enable.get() != 0) {
sim.set(count, (count.get() + 1) & 15);
std::cout << '(' << sim.time() << ") Increment Counter " << count.get() << std::endl;
}
}
}
Для знакомых с языком Verilog - вышеуказанная процедура в точности соответствует следующему коду на Верилоге:
always @(posedge clk)
if (reset)
count <= 0;
else if (enable)
count <= (count + 1) & 15;
Также нам понадобится управляющий процесс, чтобы сформировать сигналы сброса и разрешения:
co_void_t master(simulator_t &sim)
{
std::cout << '(' << sim.time() << ") Started" << std::endl;
co_await sim.delay(10);
sim.set(reset, 1);
std::cout << '(' << sim.time() << ") Asserting Reset" << std::endl;
co_await sim.delay(20);
sim.set(reset, 0);
std::cout << '(' << sim.time() << ") De-Asserting Reset" << std::endl;
co_await sim.delay(10);
std::cout << '(' << sim.time() << ") Asserting Enable" << std::endl;
sim.set(enable, 1);
co_await sim.delay(40);
std::cout << '(' << sim.time() << ") De-Asserting Enable" << std::endl;
sim.set(enable, 0);
std::cout << '(' << sim.time() << ") Terminating simulation" << std::endl;
sim.finish();
}
Главная программа создаёт три процесса и запускает симуляцию:
int main()
{
simulator_t sim;
sim.make_process("clock", do_clock);
sim.make_process("counter", do_counter);
sim.make_process("master", master);
sim.run();
}
Компилируем и запускаем:
$ make demo2
g++-10 -std=c++20 -fcoroutines -c demo2.cpp
g++-10 -std=c++20 -fcoroutines -c simulator.cpp
g++-10 -std=c++20 -fcoroutines demo2.o simulator.o -o demo2
$ ./demo2
(0) Started
(10) Asserting Reset
(30) De-Asserting Reset
(40) Asserting Enable
(40) Increment Counter 0
(42) Increment Counter 1
(44) Increment Counter 2
(46) Increment Counter 3
(48) Increment Counter 4
(50) Increment Counter 5
(52) Increment Counter 6
(54) Increment Counter 7
(56) Increment Counter 8
(58) Increment Counter 9
(60) Increment Counter 10
(62) Increment Counter 11
(64) Increment Counter 12
(66) Increment Counter 13
(68) Increment Counter 14
(70) Increment Counter 15
(72) Increment Counter 0
(74) Increment Counter 1
(76) Increment Counter 2
(78) Increment Counter 3
(80) De-Asserting Enable
(80) Terminating simulation
Исходники можно взять здесь: https://github.com/sergev/simulation-with-coroutines
This account has disabled anonymous posting.
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

If you are unable to use this captcha for any reason, please contact us by email at support@dreamwidth.org