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

Date: 2021-06-23 05:03 (UTC)
spamsink: (Default)
From: [personal profile] spamsink
А что Verilator делает, тебя не устраивает?

Date: 2021-06-23 05:54 (UTC)
spamsink: (Default)
From: [personal profile] spamsink
Он же там всю семантику верилога симулирует без какой-либо оптимизации, так что простительно.

Date: 2021-06-26 06:37 (UTC)
spamsink: (Default)
From: [personal profile] spamsink
Compiler version S-2022.06-Alpha_Full64; Runtime version S-2022.06-Alpha_Full64;  Jun 25 23:35 2021
aa94b18fe01dc1f8 312f8177e1554de4 51fb4c711ff4f827
$finish called from file "random.v", line 2546.
$finish at simulation time             12090001
           V C S   S i m u l a t i o n   R e p o r t 
Time: 12090001
CPU Time:     22.670 seconds;       Data structure size:   0.1Mb
Fri Jun 25 23:36:15 2021
CPU time: 1.854 seconds to compile + .709 seconds to elab + 6.398 seconds to link + 22.744 seconds in simulation

Edited Date: 2021-06-26 06:45 (UTC)

Date: 2021-06-23 07:24 (UTC)
norian: (Default)
From: [personal profile] norian
прикольно что если использовать булевские переменные (или инты) в качестве сигналов и имплементировать оператор , код на крестах будет отличацца от верилога только в замене @ на hook, то есть простеньким макропроцессором можно гонять его туда-обратно

Date: 2021-06-24 21:53 (UTC)
norian: (Default)
From: [personal profile] norian
не то что вроде, кресты дают возможность отжать производительность до максимальной

правда, сложность с удалением в лес растёт нелинейно, но это обычное дело для софта