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

Модуль с произвольным числом портов произвольной ширины на верилоге

Привет!

 

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

 

Итак, задача:

1. Сделать модуль, у которого можно настраивать параметрами количество портов, причем каждый порт может иметь свою ширину.

2. Модуль должен пригоден к повторному использованию без необходимости его редактирования.

 

Основной принцип:

1. Задаем количество портов, которые будут созданы.

2. Создаем массив размеров портов (на самом деле это не массив, но в контексте задачи можно сказать и так).

3. Создаем массив смещений для каждого порта (т.н. "карта портов").

4. Регистрируем порты в модуле.

5. Пользуемся :)

 

* Конкретно мой модуль предназначен для сериализации разноразмерных входных портов в поток байтов. Вы можете, при необходимости, сделать свою логику работы. Цель приведенного кода - показать основной принцип подхода к решению задачи.

 

Код:

`define register_port(n) port_sizes[n * 8 +: 8]

module data_packer
    #(
        parameter nports = 8,

        parameter port_sizes = { 8'd6,  8'd1,  8'd8, 8'd4, 8'd2, 8'd3, 8'd2, 8'd1},
        parameter port_map   = {8'd21, 8'd20, 8'd12, 8'd8, 8'd6, 8'd3, 8'd1, 8'd0},

        parameter ports_list =  `register_port(0) +
                                `register_port(1) +
                                `register_port(2) +
                                `register_port(3) +
                                `register_port(4) +
                                `register_port(5) +
                                `register_port(6) +
                                `register_port(7) ,

        parameter max_port_size = 128, // max port size, bytes

        parameter total_bits_len = ports_list * 8
    )
    (
        input [total_bits_len - 1 : 0] datain,
        input [nports - 1 : 0] port_ready,

        input reset,
        input clk,

        input mode, // 0 - forced mode (any port_ready[*] signal raise serialization),
                    // 1 - wait mode (packer will be wait for all port_ready[*] signals before serialization,
                    //     port_ready[*] signals can come in any order and time)

        output reg [7:0] byte,
        output reg byte_ready,
        output reg data_packed
    );

    reg [total_bits_len - 1 : 0] buffer;
    reg [nports - 1 : 0] ports_stored;
    reg [$clog2(max_port_size * nports) : 0] byte_cnt;

    wire all_ports_stored = &ports_stored;
    wire serialize_en = byte_cnt > 0;
    wire serialization_done = {byte_ready, serialize_en} == 2'b10;

    integer n;
    genvar i, j;

    generate
        for(i = 0; i < nports; i = i + 1)
            begin: port
                localparam port_pos  = port_map[i*8 +: 8] * 8;
                localparam port_size = port_sizes[i*8 +: 8] * 8;

                always @(posedge clk, posedge reset) begin
                    if(reset) begin
                        buffer[port_pos +: port_size] <= 0;
                        ports_stored[i] <= 0;
                    end
                    else begin
                        if(port_ready[i]) begin
                            if(mode) buffer[port_pos +: port_size] <= datain[port_pos +: port_size];
                            else buffer <= datain[port_pos +: port_size];
                            ports_stored[i] <= 1;
                        end
                        if(byte_ready) ports_stored[i] <= 0;
                        if(serialization_done) buffer[port_pos +: port_size] <= 0;
                    end
                end
            end
    endgenerate

    always @(posedge clk, posedge reset) begin
        if(reset) begin
            byte <= 0;
            byte_ready <= 0;
            data_packed <= 0;
            byte_cnt <= 0;
        end
        else begin
            for(n = 0; n < nports; n = n + 1) begin
                if(port_ready[n] & !mode) byte_cnt <= port_sizes[n*8 +: 8];
            end

            if(mode & all_ports_stored) byte_cnt <= (total_bits_len >> 3);
            if(byte_cnt > 0) byte_cnt <= byte_cnt - 1;

            byte <= serialize_en ? buffer[((byte_cnt - 1) * 8) +: 8] : byte;
            byte_ready <= serialize_en;
            data_packed <= serialization_done;
        end
    end

endmodule

 

Пример его использования:

    reg [31:0] port0;
    reg [7:0] port1;
    reg [15:0] port2;
    reg [2:0] rdy_port;

    `define register_port(n) port_sizes[n * 8 +: 8]

    localparam nports = 3;
    localparam port_sizes = {8'd2, 8'd1, 8'd4};
    localparam port_map   = {8'd5, 8'd4, 8'd0};
    localparam port_list   = `register_port(0) + `register_port(1) + `register_port(2);

    data_packer dp
        (
            .reset          (reset),
            .clk            (sys_clk),

            .mode           (mode),

            .datain         ({port2, port1, port0}),
            .port_ready     (rdy_port),

            .byte           (),
            .byte_ready     (),
            .data_packed    ()
        );
        defparam
            dp.nports = nports,
            dp.port_sizes = port_sizes,
            dp.port_map   = port_map,
            dp.ports_list = port_list;

 

Как это работает:

1. Все порты в итоге классически объединяются в одну толстую шину.

2. Чтобы посчитать общую ширину толстой шины, необходимо просуммировать размеры всех портов. Тут верилог подкладывает свинью, не позволяя переопределить параметр на этапе разворачивания кода, чтобы можно было посчитать сумму.

3. В итоге, как ни крути, сумму придется все равно считать вручную. Но можно это достаточно красиво обернуть в дефайн (см. `register_port(n)).

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

5. Чтобы нужный порт отобразился в нужные биты буфера (в моей задаче), нужно задать смещение для каждого порта. Для этого испоьзуется параметр-псевдомассив port_map, в котором задаются стартовые индексы портов. На вход модуля сигналы должны подключаться в таком же порядке (см. пример использования).

6. Далее, в generate-цикле маппим входы модуля в буфер.

7. А в for-цикле загружаем байтовый счетчик для сериализации соответствующего порта.

 

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

Если что непонятно, спрашивайте.

Если есть предложения по улучшению - рад выслушать.

 

Всем спасибо.

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

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


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

Мощно! Респект!

 

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

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


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

а сумму посчитать функцией нельзя и запихать ее в параметр?

 

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

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


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

5. Чтобы нужный порт отобразился в нужные биты буфера (в моей задаче), нужно задать смещение для каждого порта. Для этого испоьзуется параметр-псевдомассив port_map, в котором задаются стартовые индексы портов. На вход модуля сигналы должны подключаться в таком же порядке (см. пример использования).

судя по коду port_map и port_sizes связаны однозначно (позиционно через generate), тогда какой смысл вводить 2 переменных, рискуя ошибиться (port_map постоянно увеличивается, в зависимости от port_size) если это можно рассчитать по месту ?

 

 

А у функции в верилоге разве может быть произвольное число входных параметров?

а у вас нет произвольного числа параметров, у вас есть вектор port_sizes, длинна которого определенна в момент передачи параметра и nports который тоже определен при передаче параметра до создания параметра port_list.

 

Если будут проблемы с синтезом или пониманием синтезатора, то сделайте красивый финт. А именно, old-style Verilog 95 объявление, позволяет использовать в качестве разрядностей векторов localparam.

 

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

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


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

Можно пояснить этот момент?

`define register_port(n) port_sizes[n * 8 +: 8]

Это что означает? Первый раз такое вижу.

+:

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


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

Это что означает? Первый раз такое вижу.

+:

IEEE Std 1364™-2005 -> IEEE Standard for Verilog Hardware Description Language -> 5. Expressions -> 5.2 Operands -> 5.2.1 Vector bit-select and part-select addressing

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


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

Это что означает? Первый раз такое вижу.

если в 2 словах, без ссылки на стандарт

 

это значит от левого значения n*8 в плюс 8 бит, есть такое же -:

 

то есть [5:2] - равнозначно [5 -: 4], а [1:2] === [1 +: 2], помогает когда лень считать 2 цифру, а известна длинна выбираемого вектора.

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


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

то есть [5:3] - равнозначно [5 -: 2], а [1:3] === [1 +: 2], помогает когда лень считать 2 цифру, а известна длинна выбираемого вектора.

немного добавлю, если вы не против :)

[5:3] == [5 -:2 ] == [3 +: 2]

[1:3] == [1 +:2] == [3 -: 2]

 

 

А у функции в верилоге разве может быть произвольное число входных параметров?

Совсем забыл, статическая функция может вообще не иметь входных параметров(хотя тот же ква будет ругаться), она может обращаться к параметрам и сигналам верхнего уровня иерархии, т.е. вектор переменной длинны, можно вообще не передавать. Передать только nports.

 

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


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

немного добавлю, если вы не против sm.gif

[5:3] == [5 -:2 ] == [3 +: 2]

[1:3] == [1 +:2] == [3 -: 2]

 

вот тут я немного несогласен... потому что почти всегда вектор объявленный как [5:0] не захочет приравниваться к вырезанному куску вектора [0:5],

то есть как бы направление индексов должно совпадать.

 

Я надеюсь понятно о чем я говорю, потому даже когда удобно использовать [3 +: 2], все равно приходиться использовать [5 -: 2], потому и не стал бы приравнивать. То есть фактически они да равны, но по использованию не эквивалентны....

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


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

вот тут я немного несогласен...

Никогда бы не подумал что скажу вам это : Учите матчасть. Документ и раздел матчасти я в теме указал.

все равно приходиться использовать [5 -: 2], потому и не стал бы приравнивать.

никогда не приходилось. все всегда работало.

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


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

[5:3] == [5 -:2 ] == [3 +: 2]

[1:3] == [1 +:2] == [3 -: 2]

Тут, как я понимаю, описка.

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


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

да [5 : 3] = [5 -: 3] = [3 +: 3]

биты 5,4,3 я ошибся в оригинальном сообщении, потом исправил на 5:2, чтобы получить 5 -: 4, не было повторяющейся тройки которая могла сбить, а des00 скопировал видать начальное сообщение. В общем изначально моя опечатка:)

 

Никогда бы не подумал что скажу вам это : Учите матчасть. Документ и раздел матчасти я в теме указал.

Почем мне нельзя такое сказать%) я очень поверхностно к моему стыду знаю формат, подчитываю что могу, но еще очень долек до идеала...

 

По сути вопроса я наверное плохо объясняю. Во всяком случае ISE 14.4 часто ругается когда меняется порядок индексов, то есть если объявляли регистр как 3:0, то попытки в каких то действиях запихать в него обратный порядок вызывает ошибку. Тут надо специалиста который поймет о чем я несу и нормально сформулирует :)... я что-то не могу сразу примера вспомнить....

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


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

Мощно! Респект!

 

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

 

Согласен, да вот все никак не хватает времени. Пока приходится крутиться в классическом верилоге.

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


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

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

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

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

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

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

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

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

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

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