Увы, я не очень хороший объясняльщик. Кроме того, хотя я также являюсь и программистом, мне тоже приходится по ходу дискуссии обдумывать как это все лучше реализовать программно. Так что польза от обсуждения обоюдная :)
Да, по сущности это инклуд/макрос. Включаются внутренние блоки модуля. Но, синтаксически, сами сигналы можно рассматривать не как переменные, а как функции. Типа:
Тогда очень уж сильного разворачивания можно избежать и использовать много готового кода из имеющегося верилога. И часть модулей можно будет представить как функции. Вот vm1_pli и vm1_plm содержат только assign - их можно представить как фукнции. vm1_timer - уже имеет внутри триггеры, его как функцию не представить.Код:plm_ena_fc_var = plm_ena_fc(....);
Нет, Вы правильно сказали, модуль надо рассматривать как include или макрос, в том смысле что его внутренние блоки включаются в проект и внутренний текст разворачивается - все внутренние assign и always включаются в текущий уровень вложений. То есть модуль - это НЕ вызов функции/функций в общем случае. Отдельный assign - да, можно рассмотреть как вызов программной функции. Кстати, в Верилоге есть свои таски и функции, хорошо что я их тут не применял :)
always не может быть сложным и развеститым, его суть - указать триггеры которые назначаются по событию клока. То, что там внутри понаписаны какие-то функции - это просто синтаксическое удобство, внос неявного assigna. Я просто предложил сделать их явными. Тогда в always вообще не придется ничего вычислять, просто тупо переписать в триггеры по событию клока результаты из массива вычисленных значений функций. Никаких снапшотов и раздумий что и откуда брать.
Получится, абсолютно весь проект сводится к парам функция/триггер. И все функции имеют в качестве аргументов внешние входные значения (для модуля процессора это внешние пины) и триггеры. Если входным значением вдруг является значение другой функции - то эта другая все равно в итоге доходит до внешних параметров и триггеров. То есть любая функция в проекте зависит только от внешних сигналов и состояния триггеров. Все функции-аргменты являются промежуточными и в итоге тоже приходят к зависимости только от внешних сигналов и триггеров.
Эту промежуточную функцию-аргумент можно или вычислить ее значение и "закешировать" для использования в других выражениях (как аргумент других функций) - то есть назначить результат какой-то переменной, или для начала, для простоты, тупо вычислять каждый раз - тогда не нужен цикл, все результаты будут актуальными сразу.
Да, цикл он только для иллюстрации был, как метод вычисления, независящий от порядка операторов. Не лучшая идея, согласен.
---------- Post added at 18:26 ---------- Previous post was at 17:51 ----------
Давайте вместе подумаем на совсем простом примере, я вот набросал 155ИР13, синхронный сдвиговый регистр, с параллельной загрузкой и асинхронным сбросом, чтобы не возиться с описанием взял стандартную микросхему.
Теперь надо подумать как это представить на С++, можете шаблон создать? Я в плюсах писатель не очень.Код:module ir13
(
input rst_n, // async reset, active low
input clk, // sync clock, raising edge
input [2:1] s, // shift mode
input [7:0] d, // parallel load data
input dsr, dsl; // new shifted in data bit
output reg [7:0] q // output data
);
wire [7:0] q_rc;
//
// assign form is used deliberately for illustrative purposes
//
assign q_rc[0] = (s == 2'b00) & q[0] // storage
| (s == 2'b01) & q[1] // shift left
| (s == 2'b10) & dsr // shift right
| (s == 2'b11) & d[0]; // parallel load
assign q_rc[1] = (s == 2'b00) & q[1] // storage
| (s == 2'b01) & q[2] // shift left
| (s == 2'b10) & q[0] // shift right
| (s == 2'b11) & d[1]; // parallel load
assign q_rc[2] = (s == 2'b00) & q[2] // storage
| (s == 2'b01) & q[3] // shift left
| (s == 2'b10) & q[1] // shift right
| (s == 2'b11) & d[2]; // parallel load
assign q_rc[3] = (s == 2'b00) & q[3] // storage
| (s == 2'b01) & q[4] // shift left
| (s == 2'b10) & q[2] // shift right
| (s == 2'b11) & d[3]; // parallel load
assign q_rc[4] = (s == 2'b00) & q[4] // storage
| (s == 2'b01) & q[5] // shift left
| (s == 2'b10) & q[3] // shift right
| (s == 2'b11) & d[4]; // parallel load
assign q_rc[5] = (s == 2'b00) & q[5] // storage
| (s == 2'b01) & q[6] // shift left
| (s == 2'b10) & q[4] // shift right
| (s == 2'b11) & d[5]; // parallel load
assign q_rc[6] = (s == 2'b00) & q[6] // storage
| (s == 2'b01) & q[7] // shift left
| (s == 2'b10) & q[5] // shift right
| (s == 2'b11) & d[6]; // parallel load
assign q_rc[7] = (s == 2'b00) & q[7] // storage
| (s == 2'b01) & dsl // shift left
| (s == 2'b10) & q[6] // shift right
| (s == 2'b11) & d[7]; // parallel load
always @(posedge clk or negedge rst_n)
begin
if (~rst_n)
q <= 8'b00000000;
else
q <= q_rc;
end
endmodule

