В прошлый раз я показал демо с двумя процессами, работающими параллельно, но без всякого взаимодействия. Давайте теперь добавим взаимодействие. Введём понятие сигнала. Поскольку я веду дело к моделированию цифровой логики, такой термин будет в самый раз.
Сигнал это по сути переменная, значение которой процессы могут использовать или изменять. С точки зрения хардвера сигнал состоит из одного или нескольких битов (в нашем случае до 64 битов). Но самое полезное в концепции сигнала - что процессы могут реагировать на его изменение. Обычно в моделировании каждый процесс определяет значение одного сигнала (выход), и при этом использует значения других сигналов (входы). Когда сигнал изменяет значение, процессы, чувствительные к нему, разблокируются и получают возможность перевычислить свои выходы.
Введём класс signal_t, реализующий сигналы. Создавать сигналы будем в виде глобальных переменных. Все процессы могут ссылаться на нужные им сигналы. Каждому сигналу дадим имя в виде строки - пригодится при отладке. Вторым аргументом конструктора сигналу можно присвоить начальное значение (необязательное). По умолчанию это ноль. Текущее значение сигнала можно опросить методом get(). Вот пример создания четырёх сигналов:
Введём класс sensitivity_t, каждый объект которого будет привязывать один сигнал к одному (текущему) процессу, по заданному фронту. Если нужна чувствительность к нескольким сигналам - процесс вправе создать несколько таких привязок.
Вот пример реализации 4-битного синхронного счётчика, со сбросом и сигналом разрешения. Как традиционно принято в цифровой логике, счетчик изменяет значение по переднему фронту синхросигнала:
Сигнал это по сути переменная, значение которой процессы могут использовать или изменять. С точки зрения хардвера сигнал состоит из одного или нескольких битов (в нашем случае до 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-битного синхронного счётчика, со сбросом и сигналом разрешения. Как традиционно принято в цифровой логике, счетчик изменяет значение по переднему фронту синхросигнала:
Для знакомых с языком Verilog - вышеуказанная процедура в точности соответствует следующему коду на Верилоге: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;
}
}
}
Также нам понадобится управляющий процесс, чтобы сформировать сигналы сброса и разрешения: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();
}
Исходники можно взять здесь: https://github.com/sergev/simulation-with-coroutines$ 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

no subject
Date: 2021-06-23 05:54 (UTC)