Как я могу присвоить значение "не заботиться" на выходе в комбинационном модуле в Verilog

Представьте, что мы хотим описать комбинационную схему, которая удовлетворяет следующей таблице истинности:

a b | s0 s1 s2 s3
-----------------
0 0 |  1  d  d  d
0 1 |  0  1  d  d
1 0 |  0  0  1  d
1 1 |  0  0  0  1

(где d означает значение "не заботится", то есть нам не важно, имеет ли значение этого вывода 0 или 1)

Если мы пройдем традиционный дизайн, мы сможем воспользоваться этими "не заботимся" и присвоить им наиболее удобные значения, чтобы полученные уравнения (и, следовательно, схема) были самыми простыми. Например, мы могли бы изменить предыдущую таблицу истинности на эту:

a b | s0 s1 s2 s3
-----------------
0 0 |  1  1  1  1
0 1 |  0  1  0  1
1 0 |  0  0  1  1
1 1 |  0  0  0  1

И окончательные уравнения были бы (с использованием нотации Verilog):

s0 = ~a & ~b;
s1 = ~a;
s2 = ~b;
s3 = 1;

(помните, когда вам приходилось выбирать значения для ваших выходов на K-карте, чтобы вы могли группировать столько клеток, сколько сможете)

Но что, если я захочу его спроектировать с помощью Verilog? Я не могу этого сделать:

module encoder (
  input wire a,
  input wire b,
  output reg [3:0] s
  );

  always @* begin
    case ({a,b})
      2'b00  : s = 4'b1ddd;
      2'b01  : s = 4'b01dd;
      2'b10  : s = 4'b001d;
      2'b11  : s = 4'b0001;
      default: s = 4'bdddd; 
    endcase
  end
endmodule

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

Поэтому мне приходится выбирать во время разработки, которые значения (1 или 0) я хочу вывести, и эти значения не должны давать самую оптимизированную схему:

module encoder (
  input wire a,
  input wire b,
  output reg [3:0] s
  );

  always @* begin
    case ({a,b})
      2'b00  : s = 4'b1000;
      2'b01  : s = 4'b0100;
      2'b10  : s = 4'b0010;
      2'b11  : s = 4'b0001;
      default: s = 4'b0000; 
    endcase
  end
endmodule

Что приводит к этим уравнениям (игнорируя предложение default на данный момент):

s0 = ~a & ~b;
s1 = ~a & b;
s2 = a & ~b;
s3 = a & b;

Или эта реализация (взята из вывода YOSIS 0.3.0 на EdaPlayGround):

Implementation of non optimizer encoder

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

Используя синтезатор XST, нацеленный на FPGA Spartan 3E-100k, в этом модуле используется 2 фрагмента и 4 LUT.

Я предполагаю, что Verilog (или любой другой HDL) должен освободить разработчика от необходимости делать такие выборы, поэтому синтезатор может применять любые оптимизации, если дизайнер позволяет ему выбирать наиболее удобное значение для данного вывода и для заданный набор входов. Если бы это было так, то предыдущий проект мог быть оптимизирован так, чтобы выглядеть так:

Encoder optimized

Направляя те же FPGA, что и выше, он использует 2 среза и 3 LUT.

В этом примере я смог сделать оптимизацию вручную, но рассмотрю модуль контроллера с десятком выходов в модуль данных. Могут быть выходные сигналы от контроллера, которые могут не иметь значения для заданного состояния контроллера.

Например: контроллер выводит сигнал на select из регистра A или регистр B и другой сигнал для включения load регистра C, поэтому регистр C может быть загружен либо A, либо B, либо сохранить его текущее значение.

Datapath

Если load равно 0, я действительно не забочусь о значении select, поэтому каждый раз в описании контроллера я выводим load = 0, я должен иметь возможность выводить "не заботьтесь" select.

Итак, мои вопросы:

  • Есть ли способ написать описание Verilog (не SystemVerilog), чтобы я мог давать "не заботятся значения" для выходов из комбинационных блоков?

  • Если это не так, это ограничение на языке, или это вопрос "вы должны сделать ваши проекты так" неважно ", что значения не нужны"?


ДОПОЛНЕНИЕ

К моему удивлению, XST распознает `x` как допустимый результат. Он синтезирует и, кажется, ведет себя так, как я ожидал, в результате чего одна и та же схема будет реализована с помощью 2 срезов и 3 LUT. YOSIS, с другой стороны, похоже, игнорирует его и производит тот же результат, что и не оптимизированный дизайн.

Исправление: я тестировал XST с другой конструкцией: схема, которая создает эту таблицу истинности:

a b | s0 s1 s2 s3
-----------------
0 0 |  0  d  d  d
0 1 |  1  0  d  d
1 0 |  1  1  0  d
1 1 |  1  1  1  0

Соответствующий модуль Verilog, написанный без особого внимания, может быть записан несколькими способами, например:

module encoder (
  input wire a,
  input wire b,
  output reg [3:0] s
  );

  always @* begin
    case ({a,b})
      2'b00  : s = 4'b0111;
      2'b01  : s = 4'b1011;
      2'b10  : s = 4'b1101;
      2'b11  : s = 4'b1110;
      default: s = 4'b1111; 
    endcase
  end
endmodule

Что дает худший результат с точки зрения минимизации (2 среза, 4 LUT в спартанском 3P FPGA)

Оптимизированную вручную версию можно получить, начиная с этой таблицы истинности:

a b | s0 s1 s2 s3
-----------------
0 0 |  0  0  0  0
0 1 |  1  0  1  0
1 0 |  1  1  0  0
1 1 |  1  1  1  0

Здесь легко заметить, что 3 из 4 выходов могут быть получены без единого логического затвора. Таким образом, XST сообщает 1 срез, 1 LUT (единственное, что необходимо для вычисления s0)

module encoder (
  input wire a,
  input wire b,
  output reg [3:0] s
  );

  always @* begin
    case ({a,b})
      2'b00  : s = 4'b0000;
      2'b01  : s = 4'b1010;
      2'b10  : s = 4'b1100;
      2'b11  : s = 4'b1110;
      default: s = 4'b1110; // yes, same output as above
    endcase
  end
endmodule

Если использовать x грязный трюк как "не заботьтесь":

module encoder (
  input wire a,
  input wire b,
  output reg [3:0] s
  );

  always @* begin
    case ({a,b})
      2'b00  : s = 4'b0xxx;
      2'b01  : s = 4'b10xx;
      2'b10  : s = 4'b110x;
      2'b11  : s = 4'b1110;
      default: s = 4'bxxxx; 
    endcase
  end
endmodule

Конструкция синтезирует, но результат не минимален. XST сообщает 1 срез, 2 LUT.

Ссылки на бумагу @Tim в его комментарии очень понятны в этом вопросе: не используйте x в своих проектах. Но в соответствии с этим примером язык не позволяет нам помогать синтезатору минимизировать схему.

Сохранение одного или двух LUT может быть не очень большим, но если экономия позволяет этому модулю оставаться внутри среза, у P & R будет меньше работы, чтобы разместить его там, где он хочет.

Ответ 1

Когда я использую Quartus II версии 15.0, установка "не заботится" на вывод в порядке и генерирует эффективную площадь.

Например, если я синтезирую этот код, то:

module test1 (
input wire a,
input wire b,
output reg [3:0] s
);

always @* begin
  case ({a,b})
    2'b00  : s = 4'b1000;
    2'b01  : s = 4'b0100;
    2'b10  : s = 4'b0010;
    2'b11  : s = 4'b0001;
    default: s = 4'b0000; 
  endcase
end
endmodule

Quartus создал схему, в которой используются 5 логических элементов. не оптимизировано

Однако, если я использую назначение "не заботясь" в приведенном выше коде:

module test1 (
input wire a,
input wire b,
output reg [3:0] s
);

always @* begin
  case ({a,b})
    2'b00  : s = 4'b1xxx;
    2'b01  : s = 4'b01xx;
    2'b10  : s = 4'b001x;
    2'b11  : s = 4'b0001;
    default: s = 4'b0000; 
  endcase
end
endmodule

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

Мне было интересно, правильно ли построена схема. Поэтому я запустил симулятор Quartus с контуром, который использует "не волнует". Результат - простейшая схема, которую мы хотим. введите описание изображения здесь

Ответ 2

Я бы подумал, что поставка x на выход сделает трюк - "unknown" должен делать именно то, что вы хотите. Я считаю, что вы можете подключить его напрямую к выходу, но если это запрещено, вы можете сгенерировать его, проводя как 1, так и 0 на выход.