Jump to content

    

Размер переменной по количеству элементов enum в SystemVerilog

Recommended Posts

BaN

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

Для этого я думал использовать enum для объявления флагов регистра и задать ширину регистра по количеству элементов enum:

enum {  STATUS_1,
        STATUS_2,
     	STATUS_3 } STATUS_ENUM;
logic [STATUS_ENUM.num-1:0] status;

Но Quartus ругается на строку с объявлением переменной:

Error: Verilog HDL error at : constant expression cannot contain a hierarchical identifier
Error: Verilog HDL error at : STATUS_ENUM is not a constant

Если нельзя использовать такую конструкцию, то есть какие-нибудь другие варианты реализации задачи?

Share this post


Link to post
Share on other sites

R6L-025

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

Share this post


Link to post
Share on other sites

BaN

Хочу таким же способом параметризировать размер шины адреса:

module tst
(
    // Memory mapped read/write slave interface
    input  logic [$clog2(S_ADDR_ENUM.last())-1:0] avs_s0_address,     // avs_s0.address
    input  logic                                  avs_s0_read,        // avs_s0.read
    input  logic                                  avs_s0_write,       // avs_s0.write
    output logic [S_DATA_W-1:0]                   avs_s0_readdata,    // avs_s0.readdata
    input  logic [S_DATA_W-1:0]                   avs_s0_writedata,   // avs_s0.writedata
    input  logic [(S_DATA_W/8)-1:0]               avs_s0_byteenable   // avs_s0.byteenable  
);

enum {  S_ADDR_CTRL,
        S_ADDR_STATUS } S_ADDR_ENUM;

parameter  S_DATA_W            = 32;

Но Platform Designer ругается на определение avs_s0_address:

Error: Width of port avs_s0_address is invalid: (non-static) - (0) + 1 (must be an integer or parameter name)
Info: s0: Ports with parameterized width: {avs_s0_address}

Есть ли какие-то способы обойти это ограничение?

Share this post


Link to post
Share on other sites

BaN

Похоже, что никак это ограничение не обойти средствами Quartus, только если внешней программой парсить значения enum и подставлять в размер шины после изменения файла, но такое не портируемое решение не подходит. Придется руками каждый раз править.

Само ограничение:

Quote

Error: Width of port <port name> is invalid: (non-static) - (0) + 1 (must be an integer or parameter name)

Description

You may see this error in the Quartus® II software version 12.0 or later when synthesizing your Qsys project that contains ports whose width is defined by complex arithmetic of parameter values.

 
 

Workaround/Fix

Only simple arithmetic of parameter values is supported for port width expressions. and bits2rep() are examples of complex functions that are not supported in hw.tcl. Although previous version of the Quartus II software did not report this error, complex functions have never been supported.

 

Share this post


Link to post
Share on other sites

Timmy

Можно попробовать написать аналог $clog2, через define с операторами ?: внутри, возможно, прокатит за простую арифметику.

Share this post


Link to post
Share on other sites

des00
8 hours ago, BaN said:

Хочу таким же способом параметризировать размер шины адреса:

module tst
(
    // Memory mapped read/write slave interface
    input  logic [$clog2(S_ADDR_ENUM.last())-1:0] avs_s0_address,     // avs_s0.address
    input  logic                                  avs_s0_read,        // avs_s0.read
    input  logic                                  avs_s0_write,       // avs_s0.write
    output logic [S_DATA_W-1:0]                   avs_s0_readdata,    // avs_s0.readdata
    input  logic [S_DATA_W-1:0]                   avs_s0_writedata,   // avs_s0.writedata
    input  logic [(S_DATA_W/8)-1:0]               avs_s0_byteenable   // avs_s0.byteenable  
);

enum {  S_ADDR_CTRL,
        S_ADDR_STATUS } S_ADDR_ENUM;

parameter  S_DATA_W            = 32;

 

По хорошему, тип должен быть определен до использования, а не после. Да зачем вообще так делать? у вас типовой модуль, шина адреса, шина данных. Нужны уникальные константы, чтобы не назначить одинаковый адрес - ну дык глобальный тип и ссылайтесь на него как на константу, более того, никто не мешает присвоить в типе адрес и компилятор сам будет отслеживать его уникальность.

А если вам просто нужна типизация - сделайте глобальный тип и положите его в глобальное простанство определений или в пакет

Share this post


Link to post
Share on other sites

BaN
1 hour ago, Timmy said:

Можно попробовать написать аналог $clog2, через define с операторами ?: внутри, возможно, прокатит за простую арифметику.

Не получится, там арифметика в ширине порта не то, чтобы $clog2 или ?:  не поддерживает, даже сдвиг логический не поддерживает, например, (S_DATA_W>>3) вместо (S_DATA_W/8) не работает. Видимо, работают только + - / *.

1 hour ago, des00 said:

Да зачем вообще так делать?

Мысль у меня была такая:

Например, нужно мне будет через какое-то время в этот модуль добавить еще пару регистров, добавлю я их спокойно в enum, чтобы назначились уникальные адреса:

enum {  S_ADDR_CTRL1,
        S_ADDR_CTRL2,
        S_ADDR_CTRL3,
        S_ADDR_STATUS } S_ADDR_ENUM;

Но потом мне нужно будет вручную смотреть, какое там максимальное значение адреса у последнего элемента enum и вручную исправлять ширину шины адреса. А могу и забыть исправить константу и потратить потом лишнее время на исправление этой ошибки.

Этого и хотелось избежать, чтобы не нужно было вручную потом править какие-то константы при добавлении новых адресов.

Edited by BaN

Share this post


Link to post
Share on other sites

des00
40 minutes ago, BaN said:

Например, нужно мне будет через какое-то время в этот модуль добавить еще пару регистров, добавлю я их спокойно в enum, чтобы назначились уникальные адреса:

enum {  S_ADDR_CTRL1,
        S_ADDR_CTRL2,
        S_ADDR_CTRL3,
        S_ADDR_STATUS } S_ADDR_ENUM;

Но потом мне нужно будет вручную смотреть, какое там максимальное значение адреса у последнего элемента enum и вручную исправлять ширину шины адреса. А могу и забыть исправить константу и потратить потом лишнее время на исправление этой ошибки.

Этого и хотелось избежать, чтобы не нужно было вручную потом править какие-то константы при добавлении новых адресов.

Ну я же вам написал, используйте типизацию в глобальном пространстве имен, в чем проблема

typedef enum {
  pipa_addr,
  popa_addr,
  pipa_popa_addr
} addr_t;

module pipa (input addr_t ipopa, output logic [2 : 0] opopa);

  assign opopa[0] = (ipopa == pipa_addr);
  assign opopa[1] = (ipopa == popa_addr);
  assign opopa[2] = (ipopa == pipa_popa_addr);

endmodule

А если потом у вас появятся дырки в адресном пространстве, сделаете вот так

typedef enum bit [31 : 0] {
  pipa_addr       = 32'h00_00,
  popa_addr       = 32'h00_10,
  pipa_popa_addr  = 32'h10_00
} addr_t;

без изменения кода.

Главное, в декларации enum в глобальном пространстве, чтобы у вас потом констант с именами равными полям enum не было

Share this post


Link to post
Share on other sites

Raven
11 hours ago, BaN said:

Но Platform Designer ругается на определение avs_s0_address:

Есть ли какие-то способы обойти это ограничение?

А если попробовать вот так:

$bits(S_ADDR_ENUM.num())

?

Share this post


Link to post
Share on other sites

BaN
1 hour ago, des00 said:
output logic [2 : 0] opopa

Но ведь проблема та же и останется, нужно будет вручную изменять ширину шины [2:0] при добавлении элементов enum.

Или я неправильно понимаю идею?

22 minutes ago, Raven said:

А если попробовать вот так:

Так тоже не работает, даже если там без всякой математики указать просто enum:

module tst
(
    input  logic [S_ADDR_STATUS:0] avs_s0_address,     // avs_s0.address
);

enum {  S_ADDR_CTRL,
        S_ADDR_STATUS } S_ADDR_ENUM;

То пишет ту же ошибку, он не резолвит enum.

Edited by BaN

Share this post


Link to post
Share on other sites

RobFPGA

Приветствую!

26 minutes ago, Raven said:

$bits(S_ADDR_ENUM.num())

Получите число бит в числе возвращаемого методом .num()  -  фактически это будет $bits(int)  :scratch_one-s_head:

12 hours ago, BaN said:

Есть ли какие-то способы обойти это ограничение?

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

module tst (avs_s0_address, avs_s0_read, ...);

enum {  S_ADDR_CTRL,
        S_ADDR_STATUS } S_ADDR_ENUM;

localparam S_ADDR_ENUM_N = $clog2(S_ADDR_ENUM.num());

// Memory mapped read/write slave interface
input  logic [S_ADDR_ENUM_N-1:0] avs_s0_address;     // avs_s0.address
input  logic                        avs_s0_read;     // avs_s0.read

Либо  объявить в теле модуля свою константную функцию  которую   использовать  для  задания  разрядности  портов, типа

module tst
(
    // Memory mapped read/write slave interface
    input  logic [SA_WH()-1:0] avs_s0_address,     // avs_s0.address
...
);

enum {  S_ADDR_CTRL,
        S_ADDR_STATUS } S_ADDR_ENUM;

function  int SA_WH ();
  return $clog2(S_ADDR_ENUM.num());
endfunctin
  
  

Но  тут могут быть сложности так как константность функции не всегда можно добиться.    

IMHO проще и правильнее все же, как и советовал @des00 выше, вынести такие определения во внешний package  и использовать их  по мере необходимости. Например  так

package tst_pkg;
  enum {  
    S_ADDR_CTRL,
    S_ADDR_STATUS 
  } S_ADDR_ENUM;

  localparam SA_N = $clog2(S_ADDR_ENUM.num()); 
endpackage
  
module tst
(
    // Memory mapped read/write slave interface
    input  logic [tst_pkg::SA_N-1:0] avs_s0_address,     // avs_s0.address
...
);

 

 

Удачи!  Rob.

Share this post


Link to post
Share on other sites

BaN
7 minutes ago, RobFPGA said:

Приветствую!

Получите число бит в числе возвращаемого методом .num()  -  фактически это будет $bits(int)  :scratch_one-s_head:

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

module tst (avs_s0_address, avs_s0_read, ...);

enum {  S_ADDR_CTRL,
        S_ADDR_STATUS } S_ADDR_ENUM;

localparam S_ADDR_ENUM_N = $clog2(S_ADDR_ENUM.num());

// Memory mapped read/write slave interface
input  logic [S_ADDR_ENUM_N-1:0] avs_s0_address;     // avs_s0.address
input  logic                        avs_s0_read;     // avs_s0.read

Либо  объявить в теле модуля свою константную функцию  которую   использовать  для  задания  разрядности  портов, типа

module tst
(
    // Memory mapped read/write slave interface
    input  logic [SA_WH()-1:0] avs_s0_address,     // avs_s0.address
...
);

enum {  S_ADDR_CTRL,
        S_ADDR_STATUS } S_ADDR_ENUM;

function  int SA_WH ();
  return $clog2(S_ADDR_ENUM.num());
endfunctin
  
  

Но  тут могут быть сложности так как константность функции не всегда можно добиться.    

IMHO проще и правильнее все же, как и советовал @des00 выше, вынести такие определения во внешний package  и использовать их  по мере необходимости

Удачи!  Rob.

Попробовал оба варианта - та же проблема.

Думаю еще над вариантом подключить свой модуль не в виде компонента в Platform Designer, а в модуле верхнего уровня в проекте и сделать определение typedef enum, чтобы оно было доступно из моего модуля и из модуля верхнего уровня проекта. И уже в модуле верхнего уровня проекта задать параметр ширины шины моего модуля на основе этого enum. Может пройдет такое.

Share this post


Link to post
Share on other sites

RobFPGA

Приветствую!

1 minute ago, BaN said:

Попробовал оба варианта - та же проблема

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

 

Удачи!  Rob.

Share this post


Link to post
Share on other sites

Raven
24 minutes ago, RobFPGA said:

Получите число бит в числе возвращаемого методом .num()  -  фактически это будет $bits(int)  :scratch_one-s_head:

Ну, все же не $bits(int). Здесь мы имеем вариант применения

$bits(expression)

т.е., должно возвращать как раз кол-во битов, достаточное для представления результата выражения в скобках. Но, раз по словам ТС это не работает, проблема опять в особенностях/ограничениях реализации SystemVerilog в конкретном инструменте.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.