# Cоглашение по оформлению кода на языке Verilog ## Организация файлов в проекте - В ISE/VIVADO файлы исходного кода должны располагаться в папке src корневого каталога проекта. - Папка src может содержать вложенные папки, в которых содержится исходный код больших функциональных блоков. Названия этих папок должны соответствовать названию модуля верхнего уровня функционального блока. - Хорошей практикой является описание одного Verilog-модуля в одном файле. Исключением могут быть файлы библиотек, содержащие несколько модулей. - Имя файла должно соответствовать имени модуля. - Имя модуля должно отражать основную функцию, выполняемую модулем. - Использование пробелов в именах файлов исходного кода, а также в именах папок, вложенных в src запрещено. Там где это необходимо, нужно использовать символ подчеркивания. ## Именование переменных - Объявление портов должно быть выполнено в стиле Verilog 2001. Если это необходимо, можно использовать стиль Verilog 1995. ```verilog //Verilog 2001 Style module Top ( input Rst_i, input Clk_i, output Led_o ); ``` ```verilog //Verilog 1995 Style module Top (Rst_i, Clk_i, Led_i); input Rst_i; input Clk_i; output Led_o; ``` - Наименования переменных, портов, шин по возможности должны содержать в себе назначение сигнала и оформляться в соответствии с Camel-нотацией. - Наименования портов, состояний автоматов, параметров модуля должны оформляться в UpperCamelCase. - Наименования локальных переменных, шин, регистров должны оформляться в lowerCamelCase. - Порты и сигналы имеющие стандартное назначение дополняются постфиксом через подчеркивание, который указывает на назначение порта или сигнала. ### Список постфиксов для портов **_i** - вход **_o** - выход **_io** - вход/выход ### Список постфиксов для сигналов, регистров **_d1** - сигнал, задержанный на 1 такт **_d2** - сигнал, задержанный на 2 такта **ShReg** - сдвиговый регистр, инициализируемый сигналом ```verilog reg dataValid; reg dataValid_d1; reg dataValid_d2; always @(posedge Clk_i) begin dataValid_d1 <= dataValid; dataValid_d2 <= dataValid_d1; end reg [3:0] dataValidShReg; always @(posedge Clk_i) begin dataValidShReg <= {dataValidShReg[2:0], dataValid}; end ``` ### Табуляция и пробелы При сдвиге новой строки используется табуляция, между словами одной строки используются пробелы. В настройках используемого редактора необходимо в настройках Табуляции указать размер табуляции равный 4. Для улучшения читаемости кода, допускается использование табуляции между словами одной строки. ### Имя сигнала сброса Имя сигнала сброса должно содержать в себе "Rst". Помимо этого в названии может отображаться дополнительная информация о полярности сигнала сброса, является он синхронным или асинхронным, локальным или глобальным и т.п. Например: ```verilog input Rst_i, // simple reset definition input RstGlbl_i, // global reset input RstAsync_i // asynchronous reset ``` ### Имя тактового сигнала Имя тактового сигнала должно содержать в себе "Clk". Помимо этого в названии может отображаться дополнительная информация о тактовой частоте, является ли сигнал однополярным или дифференциальным и т.п. ```verilog input Clk_i, // simple clock definition input Clk50Mhz_i, // 50 MHz clk input Clk_pi, Clk_ni, //differential clock ``` ## Группировка портов Служебные входы (сигналы сброса и тактовые сигналы) обычно объявляются в начале списка портов модуля. Исключение составляют случаи, когда эти сигналы являются неотъемлемой частью функциональной или логической группы сигналов (например, шина AXI содержит в себе и тактовый сигнал, и сигнал сброса, которые следует объявить рядом с остальными сигналами этой шины). Порты модуля могут образовывать функциональные или логические группы: - служебные входы и выходы (сигналы сброса, тактовые сигналы, сигналы захвата частоты и т.п.) - шины передачи данных - блоки сигналов, относящихся к определенному интерфейсу - ... Такие группы следует: - начинать с одного префикса - отделять друг от друга пустой строчкой Например: ```verilog module spi ( input Rst_i, input Clk_i, input [7:0] DataToSpi_i, input DataToSpiNd_i, output [7:0] DataFromSpi_o, output DataFromSpiValid_o, output SpiClk_o, output SpiMosi_o, output SpiCs_o, input SpiMiso_i ); ``` ## Активный уровень логических сигналов По умолчанию - активным уровнем сигнала считаем '1'. Исключением являются случаи использования стандартизированных интерфейсов, интерфейсов IP-ядер и т.п. ## Следование правилам синхронного дизайна Архитектура ПЛИС подразумевает работу преимущественно в рамках правил синхронного дизайна, которые включают следующее: - избегание использования gated-clock (тактовый сигнал, порожденный логической схемой); - избегание использования защелок(latch); - использование входов триггеров clock-enable вместо большого количества тактовых частот; - правильная синхронизация всех асинхронных сигналов; - использование сигнала синхронного сброса. ## Оформление блоков if Во избежание некорректной привязки блока **else** условного оператора **if** всегда обрамляем блоки кода в **if**-блоке на уровнях выше самого низкого границами **begin** **end**. Например: ```verilog if (condition1) begin if (condition2) begin doSomething; end end else begin if (condition3) begin if (condition4) begin doSomething; end else begin doSomething; end end else begin doSomething; end end ``` ## Создание платформонезависимого кода Для упрощения переносимости/повторного использования кода между различными семействами ПЛИС одного производителя или между ПЛИС разных производителей рекомендуется там, где это допустимо, использовать синтезируемое соответствующим образом описание примитивов. В основном это относится к аппаратным умножителям и блоковой памяти. Например, для описания двухпортовой памяти с одним портом на запись и одним на чтение с независимыми тактовыми сигналами вместо явного объявления BRAM можно использовать описание вида: ```verilog parameter DataWidth = 36; parameter AddrWidth = 10; reg [RAM_WIDTH-1:0] ramBlock[(2**AddrWidth)-1:0]; reg [RAM_WIDTH-1:0] dataOut; wire [AddrWidth-1:0] addrA, addrB; wire [DataWidth-1:0] inDataA; wire [DataWidth-1:0] outDataB; wire wrEnA; always @(posedge clkA) begin if (wrEnA) begin ramBlock[addrA] <= inDataA; end end always @(posedge clkB) begin outDataB <= ramBlock[addrB]; end ``` Также, вместо явного описания блока DSP, используемого для простого умножения двух 18-битных знаковых чисел, можно применить следующее описание: ```verilog wire signed [17:0] aIn; wire signed [17:0] bIn; reg signed [35:0] p; always @(posedge ) begin p <= aIn * bIn; end ``` **Замечание:** При использовании описанного подхода необходимо удостовериться: - в правильности описания поведения блока (т.е. оно должно соответствовать поведению аппаратного примитива; должна существовать возможность так сконфигурировать примитив, чтобы его поведение описывалось представленным кодом) - в том что используемые средства разработки(в основном это касается средств синтеза) допускают неявное описание примитивов и корректно его интерпретируют **Замечание:** В случае невозможности использования неявного описания примитивов допустимо использовать явное их описание. Для упрощения переносимости кода рекомендуется обернуть описание этого примитива промежуточным модулем-оберткой (wrapper) с интерфейсом, содержащим входы и выходы логически и функционально значимые для описываемого блока. Например, если мы описываем таким образом блок умножителя: ```verilog module multiplierWrapper ( input Rst_i, input Clk_i, input signed [17:0] A_i, input signed [17:0] B_i, output signed [35:0] P_o ); DSP48A1 # ( //... //Parameters definition //... ) dsp48Inst ( //... //Port connections //... ); endmodule ``` Такой подход при переходе на другую архитектуру позволяет просто заменить блок с аппаратным примитивом на аналогичный для новой архитектуры ПЛИС без изменения интерфейса подключения.