Инструменты пользователя

Инструменты сайта


verilog:notes

Verilog Notes

Метастабильность, сэмплирование фронтов внешнего сигнала CLK:

Внешний входной сигнал SCK, например от интерфейса SPI, и внутренний системный сигнал CLK микросхемы FPGA являются асинхронными друг относительно друга. Поэтому в какие-то моменты времени может возникать ситуация, когда переключения сигналов возникают в одно и тоже время. Т.е. фронт сигнала CLK, по которому работает логика в FPGA, совпадает с фронтом/спадом внешнего сигнала.

В таких случаях D-триггер не может достоверно "захватить" входной сигнал, возникает неопределенность на его выходе - напряжение не "0" и не "1". Это неопределенное состояние не может длиться долго, и через некоторое время за счет внутренних токов выход уплывет либо в "0", либо в "1". Но, в течение этого времени нельзя неопределенное состояние пропускать дальше в схему. Поэтому, используется второй и более D-триггеров в цепочке, которые не пропускают эту неопределенность дальше. Предполагается, что к моменту выхода из цепочки D-триггеров неопределенность должна исчезнуть.

module spi_slave(
    input clk,
    input SCK,
    ...
);

  reg [2:0] SCK_sync;
  always @(posedge clk) SCK_sync <= {SCK_sync[1:0], SCK};
  wire SCK_rising_edge  = (SCK_sync[2:1]==2'b01);   // detect SCK rising edges
  wire SCK_falling_edge = (SCK_sync[2:1]==2'b10);   // detect SCK falling edges
  
  ...
endmodule  

Links:

Присваивания

Непрерывное присваивание, =

  • используется для описания простой комбинационной логики
  • Вместе с оператором assign и при определении wire
  • Слева - wire или выходной порт модуля, справа - любые выходы и операции
  wire [1:0] a;
  wire [1:0] b = 2'b10;
  
  assign a = b + 2'b01;

Неблокирующее присваивание, ⇐

  • Используется только для последовательностной логики
  • Только внутри always блока, порядок присваивания не важен
  • Слева reg, справа - любые выходы и операции
  reg [1:0] a;
  reg [1:0] b;
  
  always @(posedge clk)
  begin
    if (swap_en)
    begin
      a <= b;
      b <= a;
    end;
  end

Блокирующее присваивание, =

  • Используется только для последовательностной логики
  • Только внутри always блока, важен порядок присваивания.
  • Пока выражения не зависят друг от друга - получается такая же схема, что и при неблокирующих присваиваниях
  always @(posedge clk) begin
    x = x + 1;
    y = x;
  end
  
  равнозначно
  
  always @(posedge clk) begin
    x <= x + 1;
    y <= x + 1;
  end

Links:

reg vs wire

Регистровый тип является логической сущностью Верилога и не всегда превращается в физический регистр при синтезе. Иногда регистровый тип нужен для того, чтобы обойти некоторые ограничения синтаксиса языка.

reg [7:0] data_in;

always
    case ({cpu_memr,cpu_inport,interrupt})
    3'b100:    data_in <= ram_data;
    3'b010:    data_in <= io_data;
    3'b001:    data_in <= 8'hFF;
    default:   data_in <= 8'hZZ;
    endcase

Несмотря на то, что переменная data_in формально является регистром, она будет синтезирована как обычная шина типа wire, а присоединена эта шина будет к выходу описанного оператором case мультиплексора. Переменная регистрового типа превращается в регистр только тогда, когда её присваивание происходит по перепаду тактового сигнала. В противном же случае она фактически является эквивалентом переменной типа wire.

Links:

Количество бит под значение, $clog2()

clog2(N) = ceil(log2(N))

Функция $clog2(N) возвращает количество бит, необходимых для хранения N значений. Или можно считать что это количество бит адреса, для памяти размером N.

Важно, что само число N может и не влезть в полученное количество бит! Например, $clog2(8) = 3. Эти три бита позволяют хранить 8-мь значений от 0 (3'b000) до 7 (3'b111), но само число 8 требует 4 бита для представления.

Поэтому для хранения самого числа N необходимо использовать $clog2(N+1):

N $clog2(N) $clog2(N+1) N Binary
0 0 0 1'b0
1 0 1 1'b0
2 1 2 2'b10
3 2 2 2'b11
4 2 3 3'b100
5 3 3 3'b101
6 3 3 3'b110
7 3 3 3'b111
8 3 4 4'b1000
9 4 4 4'b1001

Прочие функции: verilog-math-functions

Прореживание CLK с периодом (делитель частоты)

module CLK_Div_Period #(
  parameter CLK_PERIOD = 2
)
(
    input    clk,
    input    rst_n,

    output   o_clk
);


localparam CNT_BITS = $clog2(CLK_PERIOD+1);

reg [CNT_BITS-1:0]  count;
reg                 r_dv;

assign o_clk = r_dv;

always @(posedge clk or negedge rst_n)
begin
  if (~rst_n)
  begin
    count <= {CNT_BITS{1'b0}};
    r_dv <= 1'b0;
  end  
  else
  begin
    if (count == (CLK_PERIOD - 1))
    begin
      count <= {CNT_BITS{1'b0}};
      r_dv <= 1'b1;
    end
    else
    begin
      count <= count + 1'b1;
      r_dv <= 1'b0;
    end
  end
end  

endmodule

Сложение, синхронное vs асинхронное

Если верить этой ссылке Stackoverflow: Adding large numbers in FPGA in one clock cycle, то асинхронная схема дает меньшее быстродействие. Потому что, входные сигналы приходят на сумматор откуда-то "издалека" (где они формируются), и выходные сигналы уходят куда-то "вдаль", туда где они используются дальше. Эти задержки распространения снижают максимальную частоту работы схемы.

В случае с синхронной логикой, результат сложения "сохраняется" в ближайший регистр. Поэтому сумматор может работать на большей частоте.

Это относится не только к сумматору, но и к любой другой коммутационной схеме.

Умножение знаковое и беззнаковое

  • При перемножении беззнаковых чисел размерностью P и Q бит, результат получается размерностью P+Q бит.
  • При перемножении знаковых чисел размерностью P и Q бит, результат получается размерностью P+Q-1 бит. Два старших бита получаются одинаковыми и отражают знак результата.

Pipeline

CLK posedge

  • По переднему фронту CLK входные сигналы защелкиваются в триггере (регистре) и становятся валидными на его выходах.
  • Последующая комбинационная схема (сложение/умножение/…) должна отработать с этими выходными сигналами регистра и успеть сформировать окончательный ответ к следующему фронту CLK.
  • На фронте CLK выходы комбинационной схемы защелкиваются в следующем триггере.

Формирование Max и Min значения для заданного количества бит

wire [Y_DATA_BITS-1:0] y_max = {{1'b0},{Y_DATA_BITS-1{1'b1}}};
wire [Y_DATA_BITS-1:0] y_min = {{1'b1},{Y_DATA_BITS-1{1'b0}}};

Сравнение значений

При сравнении чисел разной разрядности будет расширено число с меньшей разрядностью. Старшим битом, если число знаковое, и 0-м если число беззнаковое.

Quartus and modelsim Warning 3016

Warning: (vsim-3016) Port type is incompatible with connection (port 'clock').

Warning выскочил при назначении сигнала clk на вход clock модуля altsyncram (мегафункция встроенной памяти). Решение нашлось тут: link

// ===========  SinMem.v (Generated by Quartus megafunction) =============
module SinMem (
	address,
	clock,
	q);

	input	[11:0]  address;
	input	  clock;
	output	[15:0]  q;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_off
`endif
	tri1	  clock;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_on
`endif

	wire [15:0] sub_wire0;
	wire [15:0] q = sub_wire0[15:0];

	altsyncram	altsyncram_component (
				.address_a (address),
				.clock0 (clock),
				.q_a (sub_wire0),
				.aclr0 (1'b0),
				.aclr1 (1'b0),
				.address_b (1'b1),
				.addressstall_a (1'b0),
				.addressstall_b (1'b0),
				.byteena_a (1'b1),
				.byteena_b (1'b1),
				.clock1 (1'b1),
				.clocken0 (1'b1),
				.clocken1 (1'b1),
				.clocken2 (1'b1),
				.clocken3 (1'b1),
				.data_a ({16{1'b1}}),
				.data_b (1'b1),
				.eccstatus (),
				.q_b (),
				.rden_a (1'b1),
				.rden_b (1'b1),
				.wren_a (1'b0),
				.wren_b (1'b0));
	defparam
		altsyncram_component.address_aclr_a = "NONE",
		altsyncram_component.clock_enable_input_a = "BYPASS",
		altsyncram_component.clock_enable_output_a = "BYPASS",
		altsyncram_component.init_file = "SinTable.mif",
		altsyncram_component.intended_device_family = "Cyclone V",
		altsyncram_component.lpm_hint = "ENABLE_RUNTIME_MOD=NO",
		altsyncram_component.lpm_type = "altsyncram",
		altsyncram_component.numwords_a = 4096,
		altsyncram_component.operation_mode = "ROM",
		altsyncram_component.outdata_aclr_a = "NONE",
		altsyncram_component.outdata_reg_a = "CLOCK0",
		altsyncram_component.widthad_a = 12,
		altsyncram_component.width_a = 16,
		altsyncram_component.width_byteena_a = 1;
endmodule



// =======  Implementation =======
// test.v
module test(
    input rst_n, 
    input clk, 
    ...
);


wire clk_fix = clk;

SinMem sinTable(
	.address(addr),
	.clock  (clk_fix),     // OK
        //.clock  (clk),   - Warning: (vsim-3016) Port type is incompatible with connection (port 'clock').
	.q      (data)
);

verilog/notes.txt · Последнее изменение: 2024/06/28 15:36 — vasco