PDA

Просмотр полной версии : VHDL process, обьясните как работает



bigral
30.09.2019, 11:51
Вот смотрю шпаргалку по vhdl тут - https://www.ics.uci.edu/~jmoorkan/vhdlref/process.html и в ней говорится что process бывает ''clocked" типа такого:


process
begin
wait until CLK'event and CLK='1';
Q1 <= D1;
end process;

и "combinational" типа такого:


process (A, B, SEL)
begin
Z <= B;
if SEL='1' then
Z <= A;
end if;
end process;


A у меня вот такой случай что надо и то и другое сразу, ну я и сочинил такое:


process(ppi_cs_n_int, reset_inp_n)
begin
if(reset_inp_n = '0') then
slots_enable <= '0';
else
if(ppi_cs_n_int'event and ppi_cs_n_int = '0') then
slots_enable <= '1';
end if;
end if;
end process;


ppi_cs_n_int - это сигнал доступа к порту i8255 который в MSX применяется как mapper памяти, и как токо процессор обращается к i8255 нужно включить этот самый mapper аж до момента сброса (reset_inp_n = '0')

Так вот вопрос, может ли процесс быть наполовину ''clocked" а наполовину ''combinational" (как я сочинил)? или это неправильно и если в "sensitivity list" есть сигнал который ''clocked" то данный процесс будет выполняться строго по "clock"-у этого сигнала? ну и тогда тот кусок надо бы переписать как:


process
begin
wait until ppi_cs_n_int'event and ppi_cs_n_int = '0';
slots_enable <= '1';
end process;

process(reset_inp_n)
begin
if(reset_inp_n = '0') then
slots_enable <= '0';
end if;
end process;


и таки интересно, как же насамом деле будет работать процесс в котором есть "clocked" сигнал но есть и другие сигналы в "sensitivity list", будет ли он запускаться при изменении этих сигналов тоже? или только по "clocked" сигналу?

IanPo
30.09.2019, 15:34
По последнему коду:
Управлять одним сигналом из двух процессов нельзя, компилятор не поймет.

По первому коду:
А то, что у вас, если не ошибаюсь - триггер с асинхронным сбросом. Навскидку, должно работать

svofski
30.09.2019, 17:32
По-моему простой способ понять процессы, это представлять себе их как в железе. Комбинаторный, или непрерывный процесс — это обычная логика. Клочный процесс — это запись результата работы логики в память (регистр) по фронту тактирующего сигнала.

Почти все описания клочных процессов включают в себя комбинаторику. Но можно записывать эти части и отдельно друг от друга. Посмотрите на реверснутые процессоры от Vslav, там как раз это видно. Поскольку это реальное железо, комбинаторика и регистры четко разделены. Люди так редко пишут.

OrionExt
30.09.2019, 19:20
Видимо bigral, имел ввиду вот такую штуку :)

70144

svofski
01.10.2019, 07:47
Это защелка, асинхронный элемент и как правило большой ататат в fpga.

IanPo
01.10.2019, 09:34
Про триггер с асинхронным сбросом:
https://www.kit-e.ru/articles/circuit/2009_1_96.php
Такая конструкция вполне себе синтезируема.

bigral
01.10.2019, 18:37
Про триггер с асинхронным сбросом:
https://www.kit-e.ru/articles/circuit/2009_1_96.php
Такая конструкция вполне себе синтезируема.

ОПА! вот это да... что verilog каким-то образом поддерживает процесс С ДВУМЯ КЛОКАМИ???


module async_resetFFstyle2 (rst_n, clk, asyncrst_n);
output rst_n;
input clk, asyncrst_n;
reg rst_n, rff1;

always @(posedge clk or negedge asyncrst_n)
if (!asyncrst_n) {rst_n,rff1} <= 2'b0;
else {rst_n,rff1} <= {rff1,1'b1};
endmodule

это ж по идее не соответствует приведенному vhdl эквиваленту:


library ieee;
use ieee.std_logic_1164.all;
entity asyncresetFFstyle is
port (
clk : in std_logic;
asyncrst_n : in std_logic;
rst_n : out std_logic);
end asyncresetFFstyle;
architecture rtl of asyncresetFFstyle is
signal rff1 : std_logic;
begin
process (clk, asyncrst_n)
begin
if (asyncrst_n = '0') then
rff1 <= '0';
rst_n <= '0';
elsif (clk'event and clk = '1') then
rff1 <= '1';
rst_n <= rff1;
end if;
end process;
end rtl;


- - - Добавлено - - -


Посмотрите на реверснутые процессоры от Vslav, там как раз это видно. Поскольку это реальное железо, комбинаторика и регистры четко разделены. Люди так редко пишут.

а можно сюда скинуть кусок текста оттуда, ато тяжело мне понять как "люди редко пишут" и почему...

- - - Добавлено - - -


Это защелка, асинхронный элемент и как правило большой ататат в fpga.

У меня как бы дизайн под CPLD altera epm3064. Но интересно само это утверждение, которое для меня концептуально значит что ВСЕГДА надо как минимум 1 pin тратить на INPUT для какого-то CLOCK-a... что правда чтоли? неужели вообще не будет работать "по старинке" как оно на 556рт1 например работает (без clock-a вообще)

Xrust
01.10.2019, 21:53
bigral, будет и без клока работать. Тут речь о том, что асинхронные триггеры сами по себе генераторы проблем :) Если в схеме на обычной логике эти баги можно пофиксить навешиванием кондерчиков туда-сюда, то в CPLD с этим сложно, и лучше изначально от этого пути отказаться.

bigral
02.10.2019, 10:06
bigral, будет и без клока работать. Тут речь о том, что асинхронные триггеры сами по себе генераторы проблем :) Если в схеме на обычной логике эти баги можно пофиксить навешиванием кондерчиков туда-сюда, то в CPLD с этим сложно, и лучше изначально от этого пути отказаться.

Концепцию "асинхронные триггеры сами по себе генераторы проблем" не понял. Из всего изложенного в той ссылке про сброс, я вижу такие ключевые моменты:

1. реальные FPGA очень быстрые по сравнению с логикой sn74xxx, изза этого очень короткая "иголка" 1...4нс длинной может вызвать срабатывание в то время как в обычной схеме на sn74xxx все работает нормально так как не хватает скорости реакции у микрух (ну и такое было у того же Vslav когда он подключал КР1801ВМ1 к плате ALTERA DE1, там были фантомные глюки, изза этих "коротких иголок", решилось все навешиванием кондеров на линии управляющей шины).
2. для надежного сброса всех внутренних триггеров во внутренней схеме fpga предлагается завести CLOCK сигнал и как токо снаружи появляется сигнал reset_n из него сформировать "внутренний сигнал сброса reset_int_n" который "продержится" еще 1... или больше тиков этого CLOCK... считается что так все внутренние триггеры гарантированно сбросятся и будут в одном и том же состоянии на следующем после "reset_int_n" тике этого самого CLOCK. (но для моего случая, видимо это смысла не имеет, у меня 1 триггер и нету CLOCK-a).

omercury
02.10.2019, 18:55
ОПА! вот это да... что verilog каким-то образом поддерживает процесс С ДВУМЯ КЛОКАМИ???
Это список чувствительности. Означает, что что-то произойдёт по указанному в скобках событию. Также, как и в VHDL, только чуть точнее в данном написании.
Чтоб совсем одинаково было, надо posedge/negedge убрать, тогда станут доступны оба перепада.
И да, always @ вовсе не гарантирует триггеров в цикле, например такая запись:


reg q;
always @(posedge clock) begin
q = a & b;
end
создаст честную комбинаторику.



это ж по идее не соответствует приведенному vhdl эквиваленту:
Вообще один-в-один.

Кстати, вместо clk'event and clk = '1' можно писать rising edge clk.

svofski
02.10.2019, 20:32
И да, always @ вовсе не гарантирует триггеров в цикле, например такая запись:
Код:
reg q;
always @(posedge clock) begin
q = a & b;
end
создаст честную комбинаторику.
Это в каком смысле "честную комбинаторику"? Нетрудно проверить:

(это можно скопипастить в https://www.tutorialspoint.com/compile_verilog_online.php)


/*
* Do not change Module name
*/
module main;
reg clk=0;
reg fastclk=0;

always #5 clk <= ~clk;
always #1 fastclk <= ~fastclk;

initial
begin
$display("Hello, World");
#20 $finish;
end


wire q_clocked;
wire q_unclocked;

reg [1:0] count = 0;
always #2 count <= count + 1;
wire a = count[0], b = count[1];

always @(posedge fastclk)
$display("clk=", clk, " a=", a, " b=",b,
" q_clocked=", q_clocked, " q_unclocked=", q_unclocked);

clocked boo1(clk, a, b, q_clocked);
unclocked boo2(a, b, q_unclocked);

endmodule

module clocked(input clock, input a, input b, output reg q);
always @(posedge clock) begin
q <= a & b;
end
endmodule

module unclocked(input a, input b, output reg q);
always @* begin
q <= a & b;
end
endmodule

omercury
02.10.2019, 22:57
Это в каком смысле "честную комбинаторику"? Нетрудно проверить:

(это можно скопипастить в https://www.tutorialspoint.com/compile_verilog_online.php)


/*
* Do not change Module name
*/
module main;
reg clk=0;
reg fastclk=0;

always #5 clk <= ~clk;
always #1 fastclk <= ~fastclk;

initial
begin
$display("Hello, World");
#20 $finish;
end


wire q_clocked;
wire q_unclocked;

reg [1:0] count = 0;
always #2 count <= count + 1;
wire a = count[0], b = count[1];

always @(posedge fastclk)
$display("clk=", clk, " a=", a, " b=",b,
" q_clocked=", q_clocked, " q_unclocked=", q_unclocked);

clocked boo1(clk, a, b, q_clocked);
unclocked boo2(a, b, q_unclocked);

endmodule

module clocked(input clock, input a, input b, output reg q);
always @(posedge clock) begin
q <= a & b;
end
endmodule

module unclocked(input a, input b, output reg q);
always @* begin
q <= a & b;
end
endmodule




Пример действительно поганенький.

https://c.radikal.ru/c28/1910/22/c606d69c07c8.jpg (https://radikal.ru)

svofski
02.10.2019, 23:21
Ну вот, триггер. А как иначе, если там posedge?

По-моему правильное утверждение -- это что-то вроде: "декларация сигнала регистром не достаточна для синтеза физического регистра, для этого необходимо, чтобы запись в него тактировалась posedge/negedge". В синтезируемом подмножестве verilog запись в регистр в процессе always без posedge/negedge, -- это почти то же самое, что непрерывное присваивание wire. В безклоковом случае отличие от assign по-моему только в том, что в блоке always можно расписать сложные выражения, пользуясь блокирующими присваиваниями.

На самом деле это уже как-то совсем оффтоп, хоть и проходящий мимо темы ТС по касательной. Прошу прощения.

omercury
02.10.2019, 23:35
А как иначе, если там posedge?
2 регистра, разумеется.


блокирующими присваиваниями
Именно в этом и "секрет")))

https://a.radikal.ru/a13/1910/19/825c02b73f0b.jpg (https://radikal.ru)

bigral
03.10.2019, 12:08
На самом деле это уже как-то совсем оффтоп, хоть и проходящий мимо темы ТС по касательной. Прошу прощения.

очень даже не оффтоп! а как же быть с ДВУМЯ клоками одновременно? как же так что verilog такое понимает а vhdl нет?

svofski
03.10.2019, 20:15
bigral, наверное не совсем так. Просто verilog, как и vhdl, создавался как язык для симуляции. К синтезу их притянули за уши потом. Фактически когда квартуз "компилирует" код, он находит соответствие увиденного какому-то набору шаблонов. Какие-то шаблоны допустимы, какие-то нет. Из-за этого страшная путаница и уже не первое поколение fpga-шников хочет выкинуть все и переписать с нуля.

В синтезируемом подмножестве с помощью always можно сделать несколько видов процессов. Пусть меня поправят настоящие сварщики, если я ошибаюсь.

1) Непрерывное присваивание.



reg r;
always @* r <= expression;


Это то же самое, что


wire w;
assign w = expression;


2) Защелка -- значение залипает пока не случится cond1 или cond2 (ататат! если это случайно -- надо синхронизировать с клоком).


reg r;
always @* begin
if (cond1) r <= 1;
else if (cond2) r <= 0;
// чтобы убрать защелочность достаточно добавить else
// else r <= 0;
end;


3) триггер


reg r;
always @(posedge clk) begin
r <= expression;
end


4) триггер с асинхронным сбросом


reg r;
always @(posedge clk or negedge reset_n)
if (!reset_n)
r <= 0;
else
r <= expression;


По-моему это всё. Все более сложные конструкции либо упрощаются до совпадения с одним из этих паттернов, либо приводят к ошибке синтеза. Каждый из этих случаев достаточно частный. Например, триггер с асинхронным сбросом возможен благодаря тому, что базовые логические элементы имеют асинхронный сброс и принципиально важно, что оба присваивания находятся во взаимоисключающих половинках if - else. Это не потому, что verilog позволяет тактировать один регистр двумя разными клоками. Просто туманность синтаксиса может ввести в заблуждение.

На VHDL регистры с асинхронным сбросом тоже возможны.


а можно сюда скинуть кусок текста оттуда, ато тяжело мне понять как "люди редко пишут" и почему...
Мне сейчас не с руки откапывать. Идея в том, что человек как правило напишет:



always @(posedge clk)
if (c) r <= 1;
else r <= a & b;

или


always @(posedge clk)
r <= (a & b) | c;


Но все эти хитрые конструкции упрощаются к виду:


wire wire123 = (a & b) | c;
always @(posedge clk)
r <= wire123;

Только обычно сильно более сложные и запутанные. И именно такие они получаются в результате реверса.

omercury
03.10.2019, 23:18
а как же быть с ДВУМЯ клоками одновременно?
Вас же не смущают 2 клока в VHDL

process (clk, asyncrst_n)
Отчего же смутили те же самые 2 клока в верилоге?

always @(posedge clk or negedge asyncrst_n)
И, кстати, это вовсе не клоки, это, грубо говоря, входы, на манипуляции с которыми должен реагировать синтезируемый элемент.
И даже называется списком чувствительности.


как же так что verilog такое понимает а vhdl нет?
А Вы опишите установку триггера из одного "process" а сброс из другого на VHDL.
А затем попробуйте проделать то же самое на Verilog.
Вы сильно удивитесь, кто из них что умеет на самом деле. И какую ахинею порой допускает.

bigral
04.10.2019, 10:12
Вас же не смущают 2 клока в VHDL
Отчего же смутили те же самые 2 клока в верилоге?

нету там в vhdl 2-х клоков



reg r;
always @(posedge clk or negedge reset_n)
if (!reset_n)
r <= 0;
else
r <= expression;

а этот кусок два триггера генерит (смотрю в RTL viewer quartus-a), но не при любых условиях в sense list, иногда пишет warning про то что не может сгенерить триггеры и генерит какуюто пачку нелепой комбинаторики (что вообще дикость... вместо ошибки генерить что попало, такого пожалуй не видел ни в одном компиляторе)

но фраза об изначальной "костыльности" verilog для синтеза конечно может обьяснить все что угодно, это как теория заговора - универсальное обоснование

IanPo
04.10.2019, 14:43
а этот кусок два триггера генерит
Там реакция идет на posedge и negedge, т.е. на перепады сигнала. Реакция на перепад сигнала синтезируется в триггер.

svofski
07.10.2019, 10:09
а этот кусок два триггера генерит (смотрю в RTL viewer quartus-a), но не при любых условиях в sense list, иногда пишет warning про то что не может сгенерить триггеры и генерит какуюто пачку нелепой комбинаторики (что вообще дикость... вместо ошибки генерить что попало, такого пожалуй не видел ни в одном компиляторе)
На самом деле интересно посмотреть на конкретные примеры. Особенно на случай, когда генерится пачка. Может быть мы смогли бы вместе разобраться в чем там дело.

bigral
07.10.2019, 16:14
ну вот проект на quartus 13 https://sites.google.com/site/bigralsbay/home/unknown-rom-images/async_res.zip?attredirects=0&d=1 тут два примера которые генерят по 2 триггера с асинхронным сбросом, но если помутить с этой фишкой что у sense list-a в verilog можно влепить больше одного "тактирующего сигнала" (сигнала у которого указан тип фронта posedge или negedge), например так:


module async_resetFFstyle2 (rst_n, clk, asyncrst_n, third_n);
output rst_n;
input clk, asyncrst_n, third_n;
reg rst_n, rff1;

always @(posedge clk or negedge asyncrst_n or posedge third_n)
if (!asyncrst_n) {rst_n,rff1} <= 2'b0;
else {rst_n,rff1} <= {rff1,1'b1};
endmodule


то quartus пишет "Critical Warning (10237): Verilog HDL warning at async_resetFFstyle2.v(8): can't infer register for assignment in edge-triggered always construct because the clock isn't obvious. Generated combinational logic instead"
хоть оно и critical, но таки же не error, и генерит одну из тех самых "пачек" мусора (но то такое, спишем на особенности конкретной реализации компилятора)

svofski
07.10.2019, 17:01
if (!asyncrst_n) есть, а else тут как понимать? Нет однозначности, Квартус об этом и написал. Небось нагенерил эквивалент непрерывного присваивания. Я тоже не понимаю, зачем генерировать невесть что вместо того, чтобы выдать ошибку. Селяви такова, что надо смотреть на Critical warnings и учиться так не делать.

В любом случае записывать в один и тот же регистр по двум клокам никак нельзя. Можно сделать клок из логики (derived clock), но это надо себя всерьез возненавидеть. Оно будет почти работать и отлаживать такое будет практически невозможно.

bigral
07.10.2019, 17:45
Можно сделать клок из логики (derived clock), но это надо себя всерьез возненавидеть. Оно будет почти работать и отлаживать такое будет практически невозможно.

Это как например?

svofski
08.10.2019, 13:59
Это как например?
Классика это что-нибудь типа:


reg [3:0] div;
always @(posedge clk) div <= div + 1'b1;
wire clk16 = reg[3];
always @(posedge clk16)
something <= something + 1;

Все с этого начинают. Я тоже так делал и очень ворчал, почему это у меня простейший Понг не может скомпилироваться два раза подряд во что-нибудь одинаковое. Дело в том, что клоки clk и clk16 теперь фактически два разных клочных домена.

Ну и формально не запрещено взять любой сигнал и назвать его клоком:


wire weirdclk = clk1 | clk2;
always @(posedge weirdclk) somereg <= somereg + 1;


Но все поголовно учат так не делать. Если хочется, чтобы дизайн от сборки к сборке был повторим, не слишком непредсказуемо себя вел при каких-то изменениях, итд итд, надо делать все синхронно — один клок и разные clock enable:


reg [3:0] div;
always @(posedge clk) div <= div + 1'b1;
wire ce16 = &reg[3:0]; // ==1 только один clk
always @(posedge clk)
if (ce16) something <= something + 1;



wire welrdce = ce1 | ce2; // ce по длительности не должны превышать период главного клока
always @(posedge clk) if (weirdce) somereg <= somereg + 1;


Клок же остается един для всех процессов.

Это с позиций FPGA. Наверное в мире CPLD, где часто приходится иметь дело с заковыристыми античными меандрами, дела обстоят иначе.