ilkz 0 4 сентября, 2014 Опубликовано 4 сентября, 2014 (изменено) · Жалоба Привет! Периодически встречаю вопросы в духе "Как на верилоге сделать модуль, у которого будет параметризируемым количество портов и их ширина?". В целом, все ответы сводятся либо к "Делайте много портов и много параметров на каждый порт в отдельности.", либо к "Используйте фиксированную ширину портов", либо к "Верилог не позволяет такое сделать - переходите на СистемВерилог". И вот пришлось и мне с таким вопросом столкнуться, но вникать в СистемВерилог было лень, поэтому решил заморочиться и решить-таки эту задачу. Предлагаю в копилку свое решение. Итак, задача: 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-цикле загружаем байтовый счетчик для сериализации соответствующего порта. В принципе, все. Подход и код в итоге оказался достаточно прост и удобен для использования. Также подход хорош тем, что дает пользователю минимум бюрократии по заданию всяких параметров и настроек. Если что непонятно, спрашивайте. Если есть предложения по улучшению - рад выслушать. Всем спасибо. Изменено 4 сентября, 2014 пользователем ilkz Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
dxp 65 4 сентября, 2014 Опубликовано 4 сентября, 2014 · Жалоба Мощно! Респект! Но всё же возьму на себя смелость предложить вам посмотреть на интерфейсы. Они проще и в использовании, гибче и позволяют передавать структурированные данные, что очень удобно на прикладном уровне. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Golikov 0 4 сентября, 2014 Опубликовано 4 сентября, 2014 · Жалоба а сумму посчитать функцией нельзя и запихать ее в параметр? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ilkz 0 4 сентября, 2014 Опубликовано 4 сентября, 2014 · Жалоба а сумму посчитать функцией нельзя и запихать ее в параметр? А у функции в верилоге разве может быть произвольное число входных параметров? Вся загвоздка-то верилога именно в том, что нельзя посчитать выражение (например, сумму) на этапе разворачивания кода перед дальнейшим синтезом. А если б можно было бы - так хоть функции, хоть макросы, хоть что угодно использовать можно было бы. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
des00 25 4 сентября, 2014 Опубликовано 4 сентября, 2014 · Жалоба 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. Остальное будут вычисляемые параметры. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Jackov 1 4 сентября, 2014 Опубликовано 4 сентября, 2014 · Жалоба Можно пояснить этот момент? `define register_port(n) port_sizes[n * 8 +: 8] Это что означает? Первый раз такое вижу. +: Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
des00 25 4 сентября, 2014 Опубликовано 4 сентября, 2014 · Жалоба Это что означает? Первый раз такое вижу. +: 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 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Golikov 0 4 сентября, 2014 Опубликовано 4 сентября, 2014 · Жалоба Это что означает? Первый раз такое вижу. если в 2 словах, без ссылки на стандарт это значит от левого значения n*8 в плюс 8 бит, есть такое же -: то есть [5:2] - равнозначно [5 -: 4], а [1:2] === [1 +: 2], помогает когда лень считать 2 цифру, а известна длинна выбираемого вектора. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
des00 25 4 сентября, 2014 Опубликовано 4 сентября, 2014 · Жалоба то есть [5:3] - равнозначно [5 -: 2], а [1:3] === [1 +: 2], помогает когда лень считать 2 цифру, а известна длинна выбираемого вектора. немного добавлю, если вы не против :) [5:3] == [5 -:2 ] == [3 +: 2] [1:3] == [1 +:2] == [3 -: 2] А у функции в верилоге разве может быть произвольное число входных параметров? Совсем забыл, статическая функция может вообще не иметь входных параметров(хотя тот же ква будет ругаться), она может обращаться к параметрам и сигналам верхнего уровня иерархии, т.е. вектор переменной длинны, можно вообще не передавать. Передать только nports. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Jackov 1 4 сентября, 2014 Опубликовано 4 сентября, 2014 · Жалоба Интересно, надо будет запомнить. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Golikov 0 4 сентября, 2014 Опубликовано 4 сентября, 2014 · Жалоба немного добавлю, если вы не против sm.gif [5:3] == [5 -:2 ] == [3 +: 2] [1:3] == [1 +:2] == [3 -: 2] вот тут я немного несогласен... потому что почти всегда вектор объявленный как [5:0] не захочет приравниваться к вырезанному куску вектора [0:5], то есть как бы направление индексов должно совпадать. Я надеюсь понятно о чем я говорю, потому даже когда удобно использовать [3 +: 2], все равно приходиться использовать [5 -: 2], потому и не стал бы приравнивать. То есть фактически они да равны, но по использованию не эквивалентны.... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
des00 25 5 сентября, 2014 Опубликовано 5 сентября, 2014 · Жалоба вот тут я немного несогласен... Никогда бы не подумал что скажу вам это : Учите матчасть. Документ и раздел матчасти я в теме указал. все равно приходиться использовать [5 -: 2], потому и не стал бы приравнивать. никогда не приходилось. все всегда работало. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
doom13 0 5 сентября, 2014 Опубликовано 5 сентября, 2014 · Жалоба [5:3] == [5 -:2 ] == [3 +: 2] [1:3] == [1 +:2] == [3 -: 2] Тут, как я понимаю, описка. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Golikov 0 5 сентября, 2014 Опубликовано 5 сентября, 2014 · Жалоба да [5 : 3] = [5 -: 3] = [3 +: 3] биты 5,4,3 я ошибся в оригинальном сообщении, потом исправил на 5:2, чтобы получить 5 -: 4, не было повторяющейся тройки которая могла сбить, а des00 скопировал видать начальное сообщение. В общем изначально моя опечатка:) Никогда бы не подумал что скажу вам это : Учите матчасть. Документ и раздел матчасти я в теме указал. Почем мне нельзя такое сказать%) я очень поверхностно к моему стыду знаю формат, подчитываю что могу, но еще очень долек до идеала... По сути вопроса я наверное плохо объясняю. Во всяком случае ISE 14.4 часто ругается когда меняется порядок индексов, то есть если объявляли регистр как 3:0, то попытки в каких то действиях запихать в него обратный порядок вызывает ошибку. Тут надо специалиста который поймет о чем я несу и нормально сформулирует :)... я что-то не могу сразу примера вспомнить.... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ilkz 0 5 сентября, 2014 Опубликовано 5 сентября, 2014 · Жалоба Мощно! Респект! Но всё же возьму на себя смелость предложить вам посмотреть на интерфейсы. Они проще и в использовании, гибче и позволяют передавать структурированные данные, что очень удобно на прикладном уровне. Согласен, да вот все никак не хватает времени. Пока приходится крутиться в классическом верилоге. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться