Перейти к содержанию
    

Расширение функциональности модуля

Будьте добры, объясните как привильно добавлять новые возможности в проект.

 

К примеру есть базовый модуль

 

    module Child ( 
        input wire i_clk,
        input wire [7:0]  i_cmd,
        output reg [7:0]  o_result );
    // Вместо этого примитивного примера здесь может быть сложный код
    always  @( posedge i_clk )
        o_result = (i_cmd >>> 4) + 4;
    endmodule

 

Нужно перехватить вызов и обработать его на уровне выше.

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

 

module Parent(
    input wire i_clk,
    input wire i_rst,
    input wire [7:0]  i_cmd,
    output reg [7:0]  o_result );

reg        enable_child;
wire        i_cild_clk;
wire        o_chd_result;

assign i_cild_clk = i_clk & enable_child;

Child child_0( 
    .i_clk(i_cild_clk),
    .i_rst(i_rst),
    .i_cmd(i_cmd),
    .o_result(o_chd_result) );

always  @( posedge i_clk )
begin
    casex (i_cmd)
    8'b01110xxx:
        begin
        enable_child = 0;
        o_result = 8'b01010101;
        end
    default:
        begin
        enable_child = 1;
        o_result = o_chd_result;
        end
    endcase
end

endmodule

 

А как это же самое сделать правильно или лучше?

Изменено пользователем alman

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Правильно enable_child заводить в child отдельным проводом(clk_en) и в child писать

always @(posedge clk)
begin
  if(clk_en)
  begin
   //работаем только тут
  end
end

Сам клок нельзя прерывать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Нужно перехватить вызов и обработать его на уровне выше.

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

 

А как это же самое сделать правильно или лучше?

 

В самой постановке вопроса - ошибка...

Когда работаете с ПЛИС, то надо помнить, что это не программа. И тут нет "перехватов". Это описание железа и не более. Если Вы представляете себе, как устроен Ваш вычислитель, то вот так и описывайте.

Посмотрите у меня на сайте, "Краткий Курс", главу об автоматах. Там описано, как осуществлять взаимодействие автоматов. А синхронизация, как Вам уже написали должна быть общей..

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А как это же самое сделать правильно или лучше?

 

Правильно - в самом модуле ввести сигнал разрешения его работы, как это посоветовал Timmy, и завести его на разрешение записи в нужные регистры:

 

module Child (
        input wire i_clk,
        input wire i_ena,
        input wire [7:0]  i_cmd,
        output reg [7:0]  o_result );
    // Вместо этого примитивного примера здесь может быть сложный код
    always  @( posedge i_clk )
        if (i_ena)
            o_result = (i_cmd >>> 4) + 4;
    endmodule

 

Ну а дальше, я думаю понятно.

 

Касаемо Вашей реализации с clock gating, она тоже вполне имеет право на жизнь, но для FPGA такую схему применять нежелательно (исходя из структуры ее клоковых деревьев). Кстати, отдельно взятые синтезаторы умеют clock gating конвертировать в сигналы enable.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вороторизирование клока иногда применяют для снижения энергопотребления и в FPGA тоже. Недостаток я так понимаю некий сдвиг фронта клока после ворот относительно исходного клока, так?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вороторизирование клока

Ух какой термин! Откуда взяли? Первый раз слышу что-то русскоязычное, кроме "клок, пропущенный через вентиль".

 

В ASIC никакого сдвига это не вносит, так как дерево клоков балансируется с учетом задержек этих вентилей, просто оказывается, что в каком-то месте дерева вместо буфера втыкается какой нибудь там AND/NAND/OR/NOR.

 

В FPGA, да, некая задержка и доп. джиттер добавляется обязательно.

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Огромное спасибо всем ответившим!

 

Как перехватывать сигнал стало понятно. Да, я хотел отключить клок для экономии энергии, но судя по сложности устранения побочных эффектов, от этого придётся отказаться.

 

А вот как быть если появилась необходимость подсунуть свою "команду" субмодулю?

Немного изменил пример. Все команды константы взяты случайно просто для примера.

 

module Parent(
    input wire i_clk,
    input wire [7:0]  i_cmd,
    output reg [7:0]  o_result );

reg        enable_child;
reg        sub_cmd;
wire        o_chd_result;

Child child_0(
    .i_clk( i_clk),
    .i_ena(enable_child),        
    .i_cmd(sub_cmd),        // Подстановка команд
    .o_result(o_chd_result) );

always  @( posedge i_clk )
begin
    casex (i_cmd)
    8'b01110xxx:
        begin
        enable_child = 0;
        o_result = 8'b01010101;
        end
    8'b11001100:   // Новая команда, требующая подстановки
        begin
        sub_cmd = 8'b00110011;
        enable_child = 1;
        o_result = o_chd_result + 8'b00000101;
        end
    default:
        begin
        sub_cmd = i_cmd;
        enable_child = 1;
        o_result = o_chd_result;
        end
    endcase
end

endmodule

 

Можно ли так делать?

Конечные автоматы это хорошо, но хочется всё поместить в один такт. Могут ли тут быть проблемы?

Изменено пользователем alman

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Можно ли так делать?

Конечные автоматы это хорошо, но хочется всё поместить в один такт. Могут ли тут быть проблемы?

 

Можно, и нужно. Только, чтобы все это "поместить в 1 такт", надо мультиплексор sub_cmd описать в отдельном always@*, а так получается конвейер на два такта, на первом защелкивается cmd, так как из-за того, что он внутри always @(posedge...), он получается с регистром на выходе, а на втором такте уже защелкнется result, полученный от этого cmd.

 

И вообще, когда каждая сущность (мультиплексор, дешифратор, арифметический блок, и т.п.) описана в отдельном своем always, то, как правило, описание схемы устройства читается и понимается лучше. Ну, разумеется, именно "как правило", а вовсе не всегда. По крайней мере при таком подходе к описанию устройства всегда наглядно понятно, на выходе какого блока есть регистр, а на выходе какого - нет.

 

И не ставьте блокирующие присваивания там, где они совершенно не нужны.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Можно, и нужно. Только, чтобы все это "поместить в 1 такт", надо мультиплексор sub_cmd описать в отдельном always@*, а так получается конвейер на два такта, на первом защелкивается cmd, так как из-за того, что он внутри always @(posedge...), он получается с регистром на выходе, а на втором такте уже защелкнется result, полученный от этого cmd.

 

Простите за назойливость. Хочется понять раз и навсегда. Сделал отдельный always.

 

И не ставьте блокирующие присваивания там, где они совершенно не нужны.

 

Заменил все блокирующие прерывания на неблокирующие. Догадываюсь, что поступил глупо, поэтому вся надежда на указание, где и почему в этом примере понадобятся блокирующие присваивания.

 

module Parent(
    input wire i_clk,
    input wire i_rst,
    input wire [7:0]  i_cmd,
    output reg [7:0]  o_result );

reg        enable_child;
reg        sub_cmd;
wire        o_chd_result;

Child child_0(
    .i_clk( i_clk),
    .i_rst( i_rst),
    .i_ena(enable_child),        
    .i_cmd(sub_cmd),       
    .o_result(o_chd_result) );


always  @ *
begin
    casex (i_cmd)
    8'b01110xxx:
        begin
        enable_child <= 0;
        end
    8'b11001100:
        begin
        sub_cmd <= 8'b00110011;
        enable_child <= 1;
        end
    default:
        begin
        sub_cmd <= i_cmd;
        enable_child <= 1;
        end
    endcase
end

always  @( posedge i_clk )
begin
    casex (i_cmd)
    8'b01110xxx:
        begin
        o_result <= 8'b01010101;
        end
    8'b11001100:
        begin
        o_result <= o_chd_result + 8'b00000101;
        end
    default:
        begin
        o_result <= o_chd_result;
        end
    endcase
end

endmodule

 

И вообще, когда каждая сущность (мультиплексор, дешифратор, арифметический блок, и т.п.) описана в отдельном своем always, то, как правило, описание схемы устройства читается и понимается лучше. Ну, разумеется, именно "как правило", а вовсе не всегда. По крайней мере при таком подходе к описанию устройства всегда наглядно понятно, на выходе какого блока есть регистр, а на выходе какого - нет.

 

Будьте добры, скажите что неправильно в последнем примере. Очень хочется понять, как заставить пример работать за один такт.

 

И ещё вопрос - на что повлияет тип присваивания в субмодуле? Как он относится к возможности запустить схему за такт?

 

 

Спасибо.

Изменено пользователем alman

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Будьте добры, скажите что неправильно в последнем примере. Очень хочется понять, как заставить пример работать за один такт.

 

Неправильно то, что в первом always Вы поставили always @( posedge i_clk ) вместо того, что я советовал - always @*, таким образом Вы получили тот же мультиплексор с регистром на выходе, как и в предыдушем коде, вместо комбинаторного мультиплексора и комбинаторной логики.

 

Тут работает простейшее правило - хотите на выходе блока поставить триггер (синхронный регистр), пишите always @(posedge clock), не хотите - пишите always @* (внимательно следя, чтобы во всех case были default или атрибут full_case, и все ветки IF что-то присваивали, иначе асинхронный регистр-защелка получится).

 

И ещё вопрос - на что повлияет тип присваивания в субмодуле? Как он относится к возможности запустить схему за такт?

В данном контексте ни на что не повлияет, а к количеству тактов, так вообще никак не относится. Блокирующее присваивание нужно довольно редко, когда для следующего присваивания внутри одного блока нужно значение переменной, полученное в предыдущем присваивании, то есть, в основном, в каких-то сложных комбинаторных итерационных алгоритмах. А в остальных случаях может привести к некорректной симуляции при корректной работе в железе. В общем, грубо говоря, суть блокирующего присваивания при синтезе в том, что для вычисления правой части выражения берется текущее значение переменной, после последней, предыдущей операции. А неблокирующего - всегда для правой части берется то значение, которое было на момент начала блока, что бы Вы там с ним не делали до этого. А при моделировании может принести много интересных сюрпризов, особенно, если результат от блокирующего присваивания используется в неблокирующем в другом always, и не задана транспортная задержка...

 

вот простой пример:

 

  reg [7:0] i,j;
  always @(posedge clk)
    begin
       i <= i+2'd3;
       i <= i+2'd2;
       i <= i+2'd1;

       j = j+2'd3;
       j = j+2'd2;
       j = j+2'd1;
    end

 

в этом коде результатом для i будет i=i+1, так как оно было последним в блоке, а +3 и +2 будут проигнорированы, а для j - будет j=j+6, так как будет сформировано подряд три последовательных сумматора, +3, +2 и +1

 

Почему я не рекомендую ставить блокирующие "=" где попало, лишь потому, что они могут привести к неоднозначности между моделированием и железом, других причин нет. Но есть такие варианты, когда неблокирующие присваивания очень даже помогают сократить количество писанины, например, при описании комбинаторного умножителя или делителя чисел, или быстрого сдвигателя (barrel shifter, честно, я не знаю, почему он barrel - или это фамилия такая, или от слова "бочка" :) :) - буду должен :beer: , если кто подскажет историю этого барреля), ну а для переменных цикла, так они вообще незаменимы. Чтобы понять, откуда берется неоднозначность моделирование и железа - тут уже глубоко копать надо, изучать стандарт, как он предписывает их моделировать, и в рамках этого ответа оно будет лишним.

 

В общем и целом, применяйте везде, где можно, неблокирующие присваивания потому, что это так принято (кошерно, халяльно, круто, по понятиям, и т.п., кому как нравится)... Вот как-то так...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

блокирующее и не блокирующее это как пост и пред инкремент

 

к примеру

 

always @(posedge clk)
begin
   i = i + 1;
   if (i == 2)
      done <= 1;
end

сработает на 2 клоке

на первом клоке i перейдет из 0 в 1, и проверится условие что 1 не равно 2

на втором клоке i перейдет из 1 в 2, и проверится условие что 2 == 2 и сигнал done выставиться

 

always @(posedge clk)
begin
   j <= j + 1;
   if (j == 2)
      done <= 1;
end

а тут сработает на 3 клоке

на 1 клоке j перейдет из 0 в 1, и одновременно проверится условие что 0 не равен 2

на 2 клоке j перейдет из 1 в 2, и одновременно проверится условие что 1 не равно 2

на 3 клоке j перейдет из 2 в 3, и одновременно проверится условие что 2 == 2, и сигнал выставится...

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Уважаемые SM, Golikov A. и все ответившие в теме.

 

Большое спасибо за исчерпывающую информацию - буду испольлзовать эту тему как справочник.

 

Разрешите уточнить ещё вопрос относительно сигнала сброса.

 

Можно ли все блоки always поместить в ветку else? Т.е. как-то так:

 

if( i_rst ) begin

// Код инициализации

end else begin

always @ *

// что-то делаем

always @ posedge clk

// заканчиваем что-то делать

end

 

SM, c Вашего позволения исправлю always в последнем примере, чтобы не сбивать с толку случайных посетителей темы.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

мне кажется (SM любит это слово:)) так не выйдет...

нельзя просто ставить if(rst) - в какой момент? Всегда? а как это реализовать?

Надо понимать что если вы хотите что-то делать то оно должно быть под always

 

то есть можно

 

always @(posedge clk)
begin
   if(reset)
     begin
         //действия при ресете
     end
   else
     begin
         //что то по программе
     end
end

 

 

без always идут только присвоения

 

assign out =  (reset) ? 0 :  signal;

типа если ресет, то выдать 0, иначе выдавать сигнал

 

еще есть always который выполняется всегда без клоков, но там надо указывать по "изменению" каких сигналов

always (rst, signal, signal_b)
begin
   if(rst)
      signal <= 1;
   else
      signal <= (signal & signal_b);
end

 

в остальных случает, кажется, даже синтезатор вас обругает....

 

 

кстати есть хитрость в верилоге, я не очень ее люблю мне кажется она снижает понимаемость, но SM сказал - это определено стандартом, и все должны уважать

 

конструкции вида

 

always....
if(condition)
  signal <= A;
else
  signal <=B

могут быть записаны в виде

 

always....
  signal <=B

  if(condition)
    signal <= A;

 

то есть как бы вы избавляетесь от else begin - end, меньше отступов, компактнее код. Меня правда интуитивно от этой конструкции плющит, и придерживаюсь по старинке if - else, но читаемость, надо признать, возрастает.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Можно ли все блоки always поместить в ветку else? Т.е. как-то так:

 

Нельзя. Так как получится, как минимум, что функциональность одного и того же регистра описана в двух разных always, что недопустимо, да и вообще использовать if/case вне процедурных блоков нельзя.

 

И вообще, никогда не надо пытаться разбивать один функциональный блок на несколько always. Вот Ваш пример, он состоит из:

- логического блока формирования сигнала enable_child

- мультиплексора, переключающего на шину sub_cmd разные варианты сигналов.

- модуля, который что-то делает с sub_cmd и enable, и формирует свой выходной сигнал.

- мультиплексора с регистром, который коммутирует в регистр выход модуля, или другие сигналы.

 

Вот и описывайте каждую из этих сущностей ОТДЕЛЬНО в своем always, пока Вы начинающий. И для каждого always ставьте комментарий - тут мультиплексор sub_cmd, он делает это, тут - логика формирования сигнала enable_child, она такая, а тут мультиплексор выходного сигнала с регистром. Так будет лучше всего.

 

Не надо мешать мух с котлетами, у вас очень понятная схема устройства, зачем ее запутывать какими то перемешиваниями always-ов с if-ами? Чтобы враг не догадался?

 

у вас там еще элементарных глюков хватает, хотя бы то, что sub_cmd и o_chd_result объявлены однобитными. А Вы еще какую-то мегапутаницу хотите в описание ввести.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...