diff --git a/coding-style.md b/coding-style.md index 9c36981..ab542e9 100644 --- a/coding-style.md +++ b/coding-style.md @@ -1,9 +1,8 @@ # Verilog (SystemVerilog) Coding Style - ## Отступы и пробелы Отступ - 2 пробела. Табы (`\t`) запрещены. -Отступ нужны для обозначения "вложения". +Отступы нужны для обозначения "вложенности". Пример: ```systemverilog @@ -12,10 +11,12 @@ if( a ) ``` ### Пробелы -Пробел должен ставится: +Пробел должен ставиться: - после открывающейся `(` и перед закрывающейся скобкой `)` в конструкциях `if`, `for`, `always`, `function`, `task`, сложных логических условиях - перед и после элементов, требующих двух операндов ( `&`, `|`, `&&`, `||`, `=`, `<=`, `+`) - - перед и после использованием переменных и констант. Исключение: перед `,` и `;` пробел не ставится. + - перед и после переменных, констант. + +Исключение: перед `,` и `;` пробел не ставится. Примеры: ```systemverilog @@ -30,12 +31,11 @@ a = b && ( c > d ); a = ( b == c ) ? ( y ) : ( z ); ``` -### Пустые строчки -Любые логические вещи необходимо отделять пустой строчкой. +### Пустые строки +Любые логические вещи необходимо отделять пустой строкой. Под `логической вещью` понимается какой-то интерфейс или совокупность сигналов, которые имеют одинаковое назначение или цель. Пример: - ```systemverilog ... input clk_i, @@ -46,7 +46,7 @@ a = ( b == c ) ? ( y ) : ( z ); ... ``` -Здесь мы видим, что есть три разные сущности: +Здесь мы видим три разные сущности: - синхроимпульс `clk_i` - настройка какого-то режима работы `mode_i` и его разрешения `mode_en_i` - сигнал выходных данных `data_o` и валидность этого сигнала `data_valid_o` @@ -117,7 +117,6 @@ output data_valid_o больше пробелов, если это улучшает читаемость (как в примере выше). ### Пример #3 - Неправильно: ```systemverilog // BAD EXAMPLE @@ -163,7 +162,6 @@ end ``` ## Примеры использования конструкций и операторов - ### Логические выражения - Необходимо использовать логическое И/ИЛИ/НЕ (`&&`/`||`/`!`) при описании каких-то логических выражений, а не их побитовые аналоги (`&`/`|`/`~`). @@ -177,7 +175,6 @@ assign data_ready = ( pkt_word_cnt > 8'd5 ) && ( !data_enable ) && ( pkt_len <= ``` #### Пример #2 - ```systemverilog always_comb begin if( data_enable && ( fifo_bytes_empty >= pkt_size ) ) @@ -186,14 +183,12 @@ end ``` #### Пример #3 - ```systemverilog assign start_stb_o = ( ( state == RED_S ) && ( next_state != IDLE_S ) ) || ( ( state == YELLOW_S ) && ( next_state != GREEN_S ) ); ``` ### `if-else` - ```systemverilog if( a > 5 ) d = 5; @@ -202,7 +197,6 @@ else ``` ### `if-else` вместе с `begin/end` - ```systemverilog if( a > 5 ) begin c = 7; @@ -214,7 +208,6 @@ end ``` ### Вложенный `if-else` - ```systemverilog if( a > 5 ) c = 7; @@ -225,7 +218,6 @@ else ``` ### Тернарный оператор `?` - ```systemverilog assign y = ( a > c ) ? ( d ) : ( e ); ``` @@ -239,7 +231,6 @@ assign y = ( a > c ) ? ( cnt_gt_zero ): ``` ### `case` - Каждый из вариантов описывается в своем `begin/end` блоке, отделяя варианты друг от друга пустой строкой. @@ -283,7 +274,6 @@ endcase Заметьте, что здесь выровнено по `next_state` для облегчения чтения. ### `function` и `task` - ```systemverilog function int calc_sum( input int a, int b ); return a + b; @@ -306,7 +296,7 @@ task some_magic( ... endtask ``` -Однако, если у вас большое количество аргументов, возможно что-то вы делаете не так... +Однако, если у вас большое количество аргументов, возможно вы что-то делаете не так... ### Еще один пример ```systemverilog @@ -327,7 +317,7 @@ end Комментарии пишутся на английском языке. После знака комментария `//` ставится один пробел. -Желательно писать комментарии перед (возле) тем блоком, который необходимо пояснить: +Желательно писать комментарии перед (возле) тем, что необходимо пояснить: ```systemverilog // current packet word number @@ -335,7 +325,7 @@ always_ff @( posedge clk_i or posedge rst_i ) if( rst_i ) pkt_word <= 16'd0; else if( pkt_valid && pkt_eop ) // reset counter at last valid packet word - pkt_word <= 'd0; + pkt_word <= 1'd0; else if( pkt_valid ) pkt_word <= pkt_word + 1'd1; ``` @@ -343,14 +333,13 @@ always_ff @( posedge clk_i or posedge rst_i ) Примечание: - для описания комментариев необходимо пользоваться здравым смыслом и классическим рассуждением "лучший комментарий тот, которого нет". - Пример, который расположен выше, показывает *где* необходимо располагать + Пример выше, показывает *где* необходимо располагать комментарии, но не надо комментировать каждый блок и каждую строчку (с этой точки зрения пример не совсем корректен). ## Наименование переменных и модулей - - Стиль названия переменных, функций, тасков и модулей - `snake_case`, т.е. слова или префиксы - разделяются `_`, и всё пишется маленькими буквами. + разделяются `_`, всё пишется маленькими буквами. Пример: - `pkt_anlz` @@ -367,7 +356,7 @@ always_ff @( posedge clk_i or posedge rst_i ) Следует избегать чрезмерно длинных и, особенно, чрезмерно коротких названий (`rd_addr` лучше чем `ra`). - Названия портов должны содержать суффикс `_i` для входных, `_o` для выходных, `_io` для - двунаправленных сигналов. + двунаправленных портов. ## Описание клоков - Для описания клоков используется префикс `clk`. @@ -411,7 +400,7 @@ always_ff @( posedge clk_i or posedge rst_i ) - асинхронный `rst` - синхронный `srst` -Чаще всего это используется как вход для описываемого модуля: +Чаще всего используется как вход для описываемого модуля: ```systemverilog ... @@ -469,89 +458,19 @@ always_ff @( posedge clk_i or posedge rst_i ) ... ``` -## Описание конечного автомата (FSM) - -Для описания конечного автомата используется следующая схема (в интернете она ходит под названием `FSM 3 process`): - -```systemverilog -enum logic [1:0] { IDLE_S, - RUN_S, - WAIT_S } state, next_state; - -always_ff @( posedge clk_i or posedge rst_i ) - if( rst_i ) - state <= IDLE_S; - else - state <= next_state; - -always_comb - begin - next_state = state; - - case( state ) - IDLE_S: begin - if( ... ) - next_state = RUN_S; - end - - RUN_S: begin - if( ... ) - next_state = WAIT_S; - else if( ) - next_state = IDLE_S; - end - - WAIT_S: begin - if( ... ) - next_state = RUN_S; - end - - default: begin - next_state = IDLE_S; - end - - endcase - end - -// some logic, that generates output values on state and next_state signals - -``` - -Пояснение: - - `state` обозначает текущее состояние, `next_state` - то, которое будет в `state` на следующем такте. - - `IDLE_S` - дефолтное состояние, поэтому его устанавливают во время асинхронного сброса и `default` в `case`-блоке. - для удобства считаем считаем, что это состояние должно идти первым в `enum` списке. - -- Имена состояний FSM описываются большими буквами и должны содержать суффикс `_S` (`IDLE_S`, `TX_S`, `WAIT_S` и т.д.). - -Категорически **запрещается**: - - делать в одном модуле больше одного конечного автомата - - в блоке, где происходит назначение на `next_state` делать назначение еще каких-либо сигналов - - производить какие-либо другие операции, кроме операции сравнения (`==`, `!=`) с переменными `state`, `next_state`, например: - - ```systemverilog - assign b = ( state > RED_S ); - assign c = ( state + 3'd2 ) > ( IDLE_S ); - ``` - ## Размещение исходников - -Исходники размещаются в файлах. -Правило простое: один модуль, package, интерфейса - один файл. Название файла - название этого модуля. +Правило простое: один модуль, package, интерфейс - один файл. Название файла - название этого модуля. - Расширения: - Verilog: `.v` - SystemVerilog: `.sv` - Verilog Header: `.vh` - SystemVerilog Header: `.svh` - - VHDL: `.vhd` - Файлы содержащие только декларации package следует помечать окончанием `_pkg`: `func_pkg.sv` -- Файлы содержащие только константами, функции, таски, которые подгружаются в исходник при помощи include, следует именовать расширением .vh Пример -`defines.vh`. +- Файлы содержащие только константы, функции, таски, которые подгружаются в исходник при помощи include, следует именовать расширением `.vh`: `defines.vh` ## Описание логических блоков/элементов - Общее: - В одном блоке желательно описывать присваивание только в **одну** переменную. - Присваивания в переменную может происходить только в одном блоке. @@ -607,7 +526,6 @@ always_ff @( posedge clk_i or posedge rst_i ) ``` #### Создание триггеров с начальным ненулевым значением - ```systemverilog logic [7:0] cnt = 8'd5; @@ -619,7 +537,6 @@ always_ff @( posedge clk_i or posedge rst_i ) ``` ### Защелки (латчи) - Используется блок `always_latch`. Пример: @@ -634,8 +551,71 @@ always_latch Категорически **запрещается** - использовать/создавать латчи в синхронных схемах. -## Описание и объявление модулей +## Описание конечного автомата (FSM) +Для описания конечного автомата используется следующая схема (в интернете она ходит под названием `FSM 3 process`): + +```systemverilog +enum logic [1:0] { IDLE_S, + RUN_S, + WAIT_S } state, next_state; + +always_ff @( posedge clk_i or posedge rst_i ) + if( rst_i ) + state <= IDLE_S; + else + state <= next_state; + +always_comb + begin + next_state = state; + + case( state ) + IDLE_S: begin + if( ... ) + next_state = RUN_S; + end + + RUN_S: begin + if( ... ) + next_state = WAIT_S; + else if( ) + next_state = IDLE_S; + end + WAIT_S: begin + if( ... ) + next_state = RUN_S; + end + + default: begin + next_state = IDLE_S; + end + + endcase + end + +// some logic, that generates output values on state and next_state signals + +``` + +Пояснение: + - `state` обозначает текущее состояние, `next_state` - то, которое будет в `state` на следующем такте. + - `IDLE_S` - дефолтное состояние, поэтому его устанавливают во время асинхронного сброса и `default` в `case`-блоке. + для удобства считаем считаем, что это состояние должно идти первым в `enum` списке. + +- Имена состояний FSM описываются большими буквами и должны содержать суффикс `_S` (`IDLE_S`, `TX_S`, `WAIT_S` и т.д.). + +Категорически **запрещается**: + - делать в одном модуле больше одного конечного автомата + - в блоке, где происходит назначение на `next_state` делать назначение еще каких-либо сигналов + - производить какие-либо другие операции, кроме операции сравнения (`==`, `!=`) с переменными `state`, `next_state`, например: + + ```systemverilog + assign b = ( state > RED_S ); + assign c = ( state + 3'd2 ) > ( IDLE_S ); + ``` + +## Описание и объявление модулей ### Описание модуля Каждый модуль описывается в отдельном файле. @@ -669,8 +649,8 @@ endmodule - зарезервированные слова типа `parameter`, `input`, `output` - знак `=` в параметрах - - Сигналы не смешиваются в кучу. Те сигналы, которые формируют логический интерфейс - должны отделятся пустой строкой. + - Сигналы не смешиваются в кучу. Т.е. сигналы, которые формируют логический интерфейс + отделяются пустой строкой. - Предпочтительно сначала описывать входные сигналы, потом выходные, однако, первочердным должен соблюдаться принцип интерфейсов, который описан выше. @@ -678,7 +658,6 @@ endmodule - Первым в описании сигналов должно идти описание клоков/синхросигналов и сбросов. ### Инстанс модуля - ```systemverilog some_module #( .DATA_WIDTH ( 64 ), @@ -703,13 +682,12 @@ some_module #( - Чаще всего зкземпляр модуля называется так же как и сам модуль; если экземпляров одного модуля несколько, то их названия дополняются суффиксами `_inst0`, `_inst1` ( либо просто `0`, `1` ) и т.д. - Однако иногда названия получается слишком длинные и засчет вложенности модулей иерархический - путь может быть очень длинным, что приведет к неудобству смотрения в ModelSim или TimeQuest. + Однако иногда названия получается слишком длинными и засчет вложенности модулей иерархический + путь может быть очень длинным, что приведет к неудобству работы в ModelSim или TimeQuest. Поэтому допускается инстансы(!) модулей типа: `main_engine` сокращать до `me`. Однако без необходимости этим не злоупотреблять. ## Описание переменных - Переменные в модуле "создаются" после описания сигналов модуля и до начала "работы" с этими сигналами. ```systemverilog @@ -748,15 +726,14 @@ assign vlan_mpls_cnt = vlan_cnt + mpls_cnt; ``` ## Прочее - - Категорически запрещается реализовывать в устройствах сигналы с нулевым активным уровнем. Исключением могут быть стандартные интерфейсы, где такие - сигналы изначально предусмотрены (sram interface, к примеру). Допускается + сигналы изначально предусмотрены (SRAM interface, к примеру). Допускается наличие подобных сигналов на входах FPGA, но на верхнем уровне иерархии (в top-файле) они должны быть проинвертированы. - При наличии двунаправленных шин, направления передачи должны развязываться на - верхнем уровне иерархии (top-файле). + верхнем уровне иерархии (в top-файле). - В RTL описаниях использовать тип logic (за исключением тех случаев, когда необходимы множественные драйверы). Это позволит выявить ошибки, связанные с @@ -767,7 +744,7 @@ assign vlan_mpls_cnt = vlan_cnt + mpls_cnt; Это повысит максимальную скорость работы устройства. - Все критичные к быстродействию узлы по возможности следует размещать в - отдельном модуле. Это позволит оптимизировать его независимо от прочих узлов. + отдельном модуле. Это позволит оптимизировать их независимо от прочих узлов. - Все макроподстановки (`define) должны определяться в отдельном файле либо в модуле верхнего уровня иерархии. @@ -775,5 +752,5 @@ assign vlan_mpls_cnt = vlan_cnt + mpls_cnt; - Hard-code недопустим и, по возможности, модули должны быть параметризованы. Исключения возможны в тех случаях, когда параметризуемость делает программу менее читабельной и понятной. Главное требование - модуль не должен требовать - допиливания после присвоения нового значения параметру, т.е. должен полностью - сохранять функционал. + доработки после присвоения нового значения параметру, т.е. должен полностью + сохранять функционал.