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

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

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

Для этого я думал использовать 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

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

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


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

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

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


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

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

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}

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

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


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

Похоже, что никак это ограничение не обойти средствами 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.

 

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


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

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

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


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

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;

 

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

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

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


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

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 и вручную исправлять ширину шины адреса. А могу и забыть исправить константу и потратить потом лишнее время на исправление этой ошибки.

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

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

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


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

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 не было

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


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

11 hours ago, BaN said:

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

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

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

$bits(S_ADDR_ENUM.num())

?

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


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

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.

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

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


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

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

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.

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


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

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. Может пройдет такое.

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


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

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

1 minute ago, BaN said:

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

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

 

Удачи!  Rob.

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


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

24 minutes ago, RobFPGA said:

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

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

$bits(expression)

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

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


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

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

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

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

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

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

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

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

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

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