Nano Hash - криптовалюты, майнинг, программирование

Передача параметров между модулями Verilog

Я довольно новичок в Verilog и изучаю веревки. У меня есть код, который генерирует 8-битный счетчик вверх (модуль counter.v), который затем вызывается верхним модулем (top_module.v). Существует тестовая фикстура моделирования (test_fixture.v), которая вызывает верхний модуль для тестирования.

Я пытался определить ширину счетчика с помощью параметра (параметр COUNTER_WIDTH) и столкнулся с трудностями при этом. Коллега исправил код для меня, и теперь он действительно работает, но я хочу понять несколько вещей, чтобы понять, что на самом деле происходит.

Вот код модуля счетчика:

module counter
#(parameter COUNTER_WIDTH = 8)
(
    input wire CLK,
    input wire RST,
    input wire CE,
    output reg[COUNTER_WIDTH-1:0] out = {COUNTER_WIDTH{1'b0}}
    );


   always @(posedge CLK) begin
      if (RST == 1) begin
         out <= {COUNTER_WIDTH{1'b0}};
      end else begin
      if (CE == 1) begin
         out <= out + 1'b1;
      end
    end
  end
endmodule

Верхний модуль:

module top_module
#(parameter COUNTER_WIDTH = 8)
(
    input wire CLK,
    input wire CE,
    input wire RST,
    output wire[COUNTER_WIDTH-1:0] out
    );

counter #(
    .COUNTER_WIDTH(COUNTER_WIDTH)
    )
counter_inst(
    .CLK(CLK),
    .RST(RST),
    .CE(CE),
    .out(out)
);

endmodule

И тестовое приспособление:

module test_fixture();

parameter COUNTER_WIDTH = 8;

// inputs
reg CLK = 0;
reg RST = 0;
reg CE = 0;

// outputs
wire [COUNTER_WIDTH-1:0] Q;

// instance of top module to be tested
top_module #(
    .COUNTER_WIDTH(COUNTER_WIDTH)
) 

test_inst(
    .CLK(CLK),
    .RST(RST),
    .CE(CE),
    .out(Q)
);
endmodule

Я думаю, что у меня все в порядке с модулем счетчика, но у меня есть вопрос о том, что происходит в верхнем модуле/тестовом приспособлении:

  • Похоже, что параметр COUNTER_WIDTH объявлен в каждом модуле (#(параметр COUNTER_WIDTH = 8)), а затем «подключен» (если это правильное выражение) к объявлению параметра в другом модуле (например, #(.COUNTER_WIDTH(COUNTER_WIDTH ))
  • Правильно ли это понимание? Если да, то почему мы должны объявлять параметр в модуле и связывать его с параметром в другом модуле?

Заранее спасибо за вашу помощь!


Ответы:


1

Думайте о параметре как о особом виде постоянных входных данных, значение которых фиксируется во время компиляции. Первоначально в Verilog параметры были константами, которые можно было переопределить извне модуля (используя устаревшее выражение defparam). Итак, параметр Verilog имел две роли:

  1. локальная константа
  2. способ настройки модуля извне

Затем, в версии стандарта 2001 года (IEEE 1364-2001), был введен синтаксис, который вы используете ниже (так называемый "стиль ANSI"). Также IEEE 1364-2001 представил localparams (которые выполняют первую из этих двух ролей, потому что они не могут быть переопределены извне), поэтому оставлены параметры для обработки второй из этих двух ролей (настройки). В синтаксисе в стиле ANSI вы переопределяете параметр при создании экземпляра модуля. Вы можете связать параметр с любым статическим значением, например параметром родительского модуля, localparam, genvar или литералом ( жестко запрограммированное значение в вашем коде).

Из-за этой исторической двойной роли в Verilog вы должны присвоить параметру значение по умолчанию, даже если это не имеет смысла. (И это ограничение снято в SystemVerilog.)

Помогает ли вашему пониманию присвоение параметрам разных имен и значений по умолчанию?

                //    the default value
                //          |
module counter  //          V
#(parameter COUNTER_WIDTH = 1)
(
    input wire CLK,
    input wire RST,
    input wire CE,
    output reg[COUNTER_WIDTH-1:0] out = {COUNTER_WIDTH{1'b0}}
    );


   always @(posedge CLK) begin
      if (RST == 1) begin
         out <= {COUNTER_WIDTH{1'b0}};
      end else begin
      if (CE == 1) begin
         out <= out + 1'b1;
      end
    end
  end
endmodule

Затем в верхнем модуле давайте дадим параметру другое имя:

                   //    the default value
                   //          |
module top_module  //          V
#(parameter COUNTER_NUM_BITS = 2)
(
    input wire CLK,
    input wire CE,
    input wire RST,
    output wire[COUNTER_NUM_BITS-1:0] out
    );

counter #(
    .COUNTER_WIDTH(COUNTER_NUM_BITS)
    )
counter_inst(
    .CLK(CLK),
    .RST(RST),
    .CE(CE),
    .out(out)
);

endmodule

И снова в тестовом приспособлении:

module test_fixture();

localparam COUNTER_SIZE = 8;   // This is not overridden from outside, so using
                               // a localparam would be better here.
                               // (A localparam being a kind of parameter that
                               //  cannot be overridden from outside. Normal 
                               //  languages would call it a constant.)    
// inputs
reg CLK = 0;
reg RST = 0;
reg CE = 0;

// outputs
wire [COUNTER_SIZE-1:0] Q;

// instance of top module to be tested
top_module #(
    .COUNTER_NUM_BITS(COUNTER_SIZE)
) 

test_inst(
    .CLK(CLK),
    .RST(RST),
    .CE(CE),
    .out(Q)
);
endmodule
22.11.2019
  • Спасибо, это проясняет мое понимание. Правильно ли я говорю, что параметры объявляются (со значением) в каждом модуле, чтобы гарантировать, что модуль имеет некоторое значение для работы, но впоследствии они переопределяются путем подключения параметров к другому параметру в экземпляр другого модуля? 22.11.2019
  • Просто в дополнение к моему собственному комментарию после того, как я поигрался с кодом... Значения параметров передаются вниз по иерархии (от test_fixture к top_module, к счетчику), а не вверх, что имеет смысл. Таким образом, если размер счетчика установлен в test_fixture, скажем, на 32, а в другом месте установлен на 8, вы получите 32-битный вывод. Если вы установите размер счетчика в подмодуле счетчика на 32 и 8 в другом месте, вы получите 8-битный вывод. Еще раз спасибо за помощь, Мэтью. 22.11.2019
  • @McKendrigo Я отредактировал свой ответ, чтобы добавить дополнительную информацию о параметрах, например, почему они должны иметь значения по умолчанию и с чем вы можете их связать (я бы сказал, связать их с). 22.11.2019

  • 2

    почему мы должны объявлять параметр в модуле и связывать его с параметром в другом модуле?

    Потому что вы НЕ ДОЛЖНЫ присваивать параметру значение при экземпляре модуля. В этом случае инструменты должны иметь хотя бы некоторое значение для работы.

    Итак, где вы делаете:

    counter #(
        .COUNTER_WIDTH(COUNTER_WIDTH)
        )
    counter_inst(
        .CLK(CLK),
        .RST(RST),
        .CE(CE),
        .out(out)
    );
    

    Вы также можете использовать:

    counter
    counter_inst(
        .CLK(CLK),
        .RST(RST),
        .CE(CE),
        .out(out)
    );
    

    В этом случае используется значение по умолчанию.

    22.11.2019
  • Спасибо за ответ, могу я еще раз проверить, правильно ли я понял? В моем исходном посте, а также в первом блоке кода, который вы показали выше, в top_module сначала объявляется параметр COUNTER_WIDTH (#(параметр COUNTER_WIDTH = 8), а затем это подключается к одноименному параметру в счетчике. модуль v, который заменяет значение любым значением в модуле counter.v.В вашем втором примере, поскольку он не связан с объявлением параметра в counter.v, он сохраняет значение по умолчанию, которое было объявлено ранее . Это правильно? 22.11.2019
  • Да, в компаниях, где я работал, значение по умолчанию устанавливалось дизайнером как наиболее «разумное». 22.11.2019
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

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

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..