
Сообщение от
gid
Что-то как-то слишком сложно и запутанно.
Увы, я не очень хороший объясняльщик
Не стесняйтесь, задавайте уточняющие вопросы, попробую ответить.

Сообщение от
gid
Т.е. получается, что возвращаемые значения берутся из снапшота предыдущего состояния, до вычисления блоков always. Либо после assign, но до вычисления блоков always?
Это зависит от того чем является возвращаемое значение - assign или always. Assign - это логическая комбинационная функция. Если assign - то надо вернуть результат немедленно, если always - то из снапшота.
Я старался писать в такой парадигме - assign это логика, always - триггеры. Вроде нигде этот принцип не нарушал (там ниже будет особый случай always - асинхронный сброс).
Код:
assign y = ~(x1 & x2);
Это аналог ЛA3. Никакой памяти предыдущего состояния. Есть на входе набор переменных - на выходе получаем однозначные результаты. Но таких assign-ов несколько, часть выходов одних может являться входами для других - это микросхемки ЛА3 соединили последовательно.
Код:
always @(posedge clk) q<= d;
Это аналог триггера, типа ТМ2, например.

Сообщение от
gid
А результат вычислений блоков always станет выходным значением при следующем вызове?
Можно и сразу, главное не передавать их другим модулям до того как они отработают свои always. То есть - вызывали метод события клока, модуль внутри взял свой снапшот, вычислил все always, вычислил все assign до статического состояния (когда больше в комбинационных функциях ничего не меняется, а замкнутых петель, порождающих генерацию, нет - на них квартус ругался бы), сохранил новое состояние в актуальных переменных, снапшот выбросил в мусорку и пока все - аналогично вызываем события клока для остальных модулей в системе. А потом уже начинаем обрабатывать связи между модулями как assign-ы, сообщая им новые вычисленные состояния других модулей. При этом могут возникнуть события по типу асинхронного сброса (установки вроде нигде в ВМ1 не применял), но такое как комбинационная логика работает - можно (и наиболее просто) обработать как обычный assign.

Сообщение от
gid
Ещё такой момент.
Код:
assign tve_zero = (tve_count == 16'h0000) & ~tve_csr[1];
assign tve_back = tve_csr[4] & tve_csr[1] & tve_spclk & tve_tclk4;
assign tve_load = tve_zero & tve_tclk4 & ~tve_csr[1];
В assign tve_load используется какое значение tve_zero, которое вычислено в вышеидущем assign tve_zero, или предыдущее его значение, до этого assignа?
Это assign - значит берется вычисленное новое значение. Можно этот блок взять в цикл и крутить его до тех пор пока выходные значения не перестанут меняться - то есть все вычислено окончательно:
Код:
do
{
tve_zero_prev = tve_zero;
tve_back_prev = tve_back;
tve_load_prev = tve_load;
tve_zero = (tve_count == 16'h0000) & ~tve_csr[1];
tve_back = tve_csr[4] & tve_csr[1] & tve_spclk & tve_tclk4;
tve_load = tve_zero & tve_tclk4 & ~tve_csr[1];
}
while( (tve_zero_prev != tve_zero)
||(tve_back_prev != tve_back)
||(tve_load_prev != tve_load));
Этот цикл необязателен и неоптимален, просто демонстрация что результат его выполнения не будет зависеть от порядка операторов присвоения значений, только количество итераций. А если все проанализировать и выбрать верный порядок - то только одна итерация, второй уже фактически не будет - выходные значения не меняются, произойдет выход из цикла.

Сообщение от
gid
Очень не помешал бы пример реализации.
Хорошо, для начала пример синхронного сдвигового регистра с асинхронным сбросом подойдет? Возьму модуль из одного триггера, соединю модулей несколько в цепочку, и рассмотрим обработку?

Сообщение от
gid
Я смотрел код, который строит Verilator, там всё очень и очень сложно и похоже на то, что вы описываете. Но уж очень сложный код там строится, мне почему то кажется, что можно это сделать проще, тем более если подойти к модулям индивидуально.
Конечно, можно проще, мы же не пишем универсальный моделятор произвольного Верилога, а вполне конкретную модель вполне конкретного процессора написанного в едином стиле без особых HDL-ухищрений, поэтому подходим индивидуально. А Верилятор просто не очень хорошо с анализом справился, видимо.

Сообщение от
gid
А если assignы взаимно модифицируют значения друг-друга, то мы зациклимся навсегда, или это признак неправильно построенной схемы?
Не зациклимся, квартус за такое по голове бьет, поэтому петель обратной связи быть не должно. Вообще, если циклов вычисления assign понадобится больше одного до "устаканивания" - это значит просто что мы неоптимально выбрали порядок их вычисления. На Верилоге порядок значения не имеет, а вот для программного последовательного исполнения - важно.

Сообщение от
gid
Вторая итерация
Да, уже лучше. Такой момент - события reset проще рассматривать как комбинационные, это НЕ тактовый сигнал, это просто такая форма описания асинхронного сброса (через always). То есть, во всех блоках always что я применял, этот сброс работает как асинхронный - если пришел, то выход немедленно обнуляется и удерживается в нуле несмотря на все события. Это как вход R в микросхемке ТМ2. То есть - тактовых в модели ВМ1 всего два события - положительный и отрицательный фронты клока - posedge vm_clk_p и posedge vm_clk_n.