Jump to content

    
pinchemierda

Первый проект на Verilog. Прошу помощи

Recommended Posts

4 hours ago, dxp said:

2-3 часа достаточно для того, чтобы пройти по шагам инструкции, но совершенно недостаточно для того, чтобы что-то значимое осело в  голове и появилось нормальное понимание и представление.

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

Ну а так, как говорю всем студентам что были у меня, ПЛИСовод выращивается около 4-5 лет. Есть уникумы проходящие этот срок за 2-3 года, но меньше никак. А вот я, все никак не вырасту, хотя уже 20 лет прошло(

Share this post


Link to post
Share on other sites

В общем пытаясь освоить ModelSim, написал какой-то тестбенч, который как минимум позволяет проверить работу MISO выхода. 

Посоветуйте пожалуйста, где можно почитать (на русском) про составление тестбенчей на verilog.   

В итоге получил такие же диаграммы, как и во внутреннем симуляторе (функциональном) квартуса. Есть ли принципиальная разница, где делать функциональную симуляцию?

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


`timescale 1ns / 1ns
module spi_slave_tb;


reg i_core_clk;	//FPGA clock
reg i_cs;
reg i_clk;
reg [7:0]i_tx_buf;
reg i_mosi;

wire o_miso;
wire o_irq;
wire [7:0]o_rx_buf;



spi_slave spi(			
					i_core_clk,
					i_cs,
					i_clk,
					i_mosi,
					o_miso,
					o_irq,
					o_rx_buf,
					i_tx_buf	
);


always
#10 i_core_clk = ~i_core_clk;

always
#33 i_clk = ~i_clk;


initial
begin
  i_core_clk = 0;	
  i_cs = 1;
  i_clk = 0;
  i_tx_buf = 8'h00;
	
  #130 i_tx_buf = 8'hAB;
  #27 i_cs = 0;
  #20 i_tx_buf = 8'h00;
  
  
  
end
  
initial
begin
  #3000 $finish;
end

initial
begin
  $dumpfile("out.vcd");
end

initial
  $monitor($stime,, i_core_clk,, i_cs,,, i_clk,, i_mosi,, o_miso,, o_irq,, o_rx_buf,, i_tx_buf);
endmodule

 

Share this post


Link to post
Share on other sites
44 minutes ago, pinchemierda said:

В итоге получил такие же диаграммы, как и во внутреннем симуляторе (функциональном) квартуса. Есть ли принципиальная разница, где делать функциональную симуляцию?

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

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

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

//Детектирование фронта CLK
	always @(posedge i_core_clk)
	begin
		sync_clk <= i_clk;
		
		if(!counter && sync_clk) 
		begin
			o_irq <= 1'd1;
			o_rx_buf <= sr_rx;
		end
		else o_irq <= 1'd0;
	end
		
	always @(posedge sync_clk or posedge i_cs)
	begin
		if(i_cs) counter <= 3'd0;
		else 
		begin
			counter <= counter + 1'd1;
			sr_rx <= {sr_rx[6:0], i_mosi};
		end	
	end

И еще, важный момент вашей разработки, которая вам будет нужна.: разработка ТЗ на ваш модуль. Да, этот модуль занимает 20-30 строчек, но ТЗ на него вам лучше оформить. ТЗ это не предложение: "хочу SPI, 4 провода и все чтоб работало". А указание по пунктам, например: "После принятия посылки из 8 бит, модуль должен сформировать сигнал прерывания длительностью 1 такт и выдать принятые данные на внешний порт" и т.д.

Еще, будет очень вам полезно, сделать вейвформы вашего модуля, с указанием воздействий. Например вот типа таких https://wavedrom.com/editor.html

Когда будет готово ТЗ, если к тому моменту, ваши ошибки будут не очевидны и код вы не исправите, будем решать по ситуации)

ЗЫ. вот небольшой пример тестирования со сценарием и проверкой. http://www.asic-world.com/verilog/art_testbench_writing.html

Share this post


Link to post
Share on other sites

Хорошо, спасибо большое, буду действовать по инструкции))

Цитата

разбирая, например, вот эти ваши места:

Вы намекаете на то что сигналы i_core_clk и sync_clk синхронны только с точки зрения функциональной логики? В реальности из-за таймингов лог. элементов они рассинхронизированы?

Share this post


Link to post
Share on other sites
18 minutes ago, pinchemierda said:

Хорошо, спасибо большое, буду действовать по инструкции))

Вы намекаете на то что сигналы i_core_clk и sync_clk синхронны только с точки зрения функциональной логики? В реальности из-за таймингов лог. элементов они рассинхронизированы?

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

Share this post


Link to post
Share on other sites

ТЗ: 

1. После принятия посылки из 8 бит по линии i_mosi, модуль должен сформировать сигнал o_irq длительностью 1 такт и выдать принятые данные на внешний порт o_rx_buf.

2. Спустя минимальное количество времени после спада i_cs или фронта o_irq должны защёлкиваться 8 бит на параллельном входе i_tx_buf. Затем этот байт должен быть передан последовательно (в соответствии с протоколом SPI CPHA=0, CPOL=0) по линии o_miso.  

Ну сейчас то уж синхронней некуда)):

module spi_slave(
			input i_core_clk,	//FPGA clock
			
			input i_cs,
			input i_clk,
			input i_mosi,
			output reg o_miso,
				
			output reg o_irq,
			output reg [7:0]o_rx_buf,
			input      [7:0]i_tx_buf	
);

	reg [2:0]counter;							//счётчик битов
	

//-------------------------- MOSI ---------------------------------		
	
	reg [7:0]sr_rx;							//сдвиговый приёмный регистр 
	reg clk_flag, rx_flag;

	always @(posedge i_core_clk or posedge i_cs)
	begin
		if(i_cs) counter <= 3'd0;
		else
		begin
			
			if(i_clk && !clk_flag)
			begin
				clk_flag <= 1'd1;
				
				if(counter == 3'd7) rx_flag <= 1'd1; 
				
				counter <= counter + 1'd1;
				sr_rx <= {sr_rx[6:0], i_mosi};
			end
			
			if(o_irq) o_irq <= 1'd0;
			
			if(rx_flag) 
			begin
				rx_flag <= 1'd0;
				o_irq <= 1'd1;
				o_rx_buf <= sr_rx;
			end
			
			if(!i_clk) clk_flag <= 1'd0;
		end	
	end

//-------------------------- MISO ---------------------------------	

	reg [7:0]sr_tx;							//сдвиговый регистр отправки
	reg [2:0]tmp;
	reg cs_flag;
	
	always @(*)
	begin
		if(i_cs) o_miso = 1'dZ;
		else o_miso = sr_tx[7];  
	end
	
	always @(negedge i_core_clk)
	begin
		tmp <= counter;	
		if(counter != tmp) sr_tx <= sr_tx << 1'd1;
		
		if(!i_cs && !cs_flag || o_irq)
		begin
			cs_flag <= 1'd1;
			sr_tx <= i_tx_buf;
		end
	
		if(i_cs) cs_flag <= 1'd0;	 
	end
	
endmodule

на выходе порта опять светомузыка, голову сломал уже...

Share this post


Link to post
Share on other sites
6 hours ago, pinchemierda said:

Ну сейчас то уж синхронней некуда)):

на выходе порта опять светомузыка, голову сломал уже...

Т.е. вы хотите сказать, что в ModelSim это работает?

Share this post


Link to post
Share on other sites
11 часов назад, pinchemierda сказал:

Ну сейчас то уж синхронней некуда)):

Зря вы так думаете.

	always @(posedge i_core_clk or posedge i_cs)
	begin
		if(i_cs) counter <= 3'd0;
		else

i_cs ни разу не синхронен i_core_clk.

Тут надо или переносить i_cs и i_clk на i_core_clk, если частота позволяет, или принимать на i_clk, а по окончании приёма перенести принятое на i_clk.

 

Share this post


Link to post
Share on other sites
Цитата

Т.е. вы хотите сказать, что в ModelSim это работает?

Функциональную симуляцию делал только. Да, работает в симуляции.

Цитата

i_cs ни разу не синхронен i_core_clk

Да цель и не в том, чтобы было супер синхронно. Цель в том, чтобы было правильно и в железе заработало.

Вроде нет ошибки в том что я присваиваю константу в этом месте (асинхронный сброс counter). Вполне допустимый приём и часто используется. Или опять ошибаюсь? 

До этого пытался запустить вариант, найденный в интернете. Но проблема ровно такая же. В симуляторе всё хорошо, а в железе хрень.

Там как раз было реализовано так, что принималось на SPI CLK, а по окончании приёма переносилось принятое на FPGA CLK. Но я подумал, что именно из-за этого то и не работает. Как мне тут уже замечание делали, в списке чувствительности асинхронный сигнал (SPI CLK). И, якобы, из этого следует, что он бесконечной частоты и это всё очень плохо и т.д....

Вот этот пример реализации. В общем в железе он тоже не работает:


module SPI_Slave
  (
   // Control/Data Signals,
   input            i_Clk,      // FPGA Clock
   output reg       o_RX_DV,    // Data Valid pulse (1 clock cycle)
   output reg [7:0] o_RX_Byte,  // Byte received on MOSI
   input      [7:0] i_TX_Byte,  // Byte to serialize to MISO.

   // SPI Interface
   input       i_SPI_Clk,
   output  		o_SPI_MISO,
   input       i_SPI_MOSI,
   input       i_SPI_CS_n
   );

  wire w_SPI_MISO_Mux;
  
  reg [2:0] r_RX_Bit_Count;
  reg [2:0] r_TX_Bit_Count;
  reg [7:0] r_Temp_RX_Byte;
  reg [7:0] r_RX_Byte;
  reg r_RX_Done, r2_RX_Done, r3_RX_Done;
  reg r_SPI_MISO_Bit, r_Preload_MISO;

  // Purpose: Recover SPI Byte in SPI Clock Domain
  // Samples line on correct edge of SPI Clock
  always @(posedge i_SPI_Clk or posedge i_SPI_CS_n)
  begin
    if (i_SPI_CS_n)
    begin
      r_RX_Bit_Count <= 3'd0;
      r_RX_Done      <= 1'b0;
    end
    else
    begin
      r_RX_Bit_Count <= r_RX_Bit_Count + 3'd1;

      // Receive in LSB, shift up to MSB
      r_Temp_RX_Byte <= {r_Temp_RX_Byte[6:0], i_SPI_MOSI};
    
      if (r_RX_Bit_Count == 3'b111)
      begin
        r_RX_Done <= 1'b1;
        r_RX_Byte <= {r_Temp_RX_Byte[6:0], i_SPI_MOSI};
      end
      else if (r_RX_Bit_Count == 3'b010)
      begin
        r_RX_Done <= 1'b0;        
      end

    end // else: !if(i_SPI_CS_n)
  end // always @ (posedge i_SPI_Clk or posedge i_SPI_CS_n)



  // Purpose: Cross from SPI Clock Domain to main FPGA clock domain
  // Assert o_RX_DV for 1 clock cycle when o_RX_Byte has valid data.
  always @(posedge i_Clk)
  begin
      // Here is where clock domains are crossed.
      // This will require timing constraint created, can set up long path.
      r2_RX_Done <= r_RX_Done;

      r3_RX_Done <= r2_RX_Done;

      if (r3_RX_Done == 1'b0 && r2_RX_Done == 1'b1) // rising edge
      begin
        o_RX_DV   <= 1'b1;  // Pulse Data Valid 1 clock cycle
        o_RX_Byte <= r_RX_Byte;
      end
      else
      begin
        o_RX_DV <= 1'b0;
      end
  end // always @ (posedge i_Bus_Clk)


  // Control preload signal.  Should be 1 when CS is high, but as soon as
  // first clock edge is seen it goes low.
  always @(posedge i_SPI_Clk or posedge i_SPI_CS_n)
  begin
    if (i_SPI_CS_n)
    begin
      r_Preload_MISO <= 1'b1;
    end
    else
    begin
      r_Preload_MISO <= 1'b0;
    end
  end


  // Purpose: Transmits 1 SPI Byte whenever SPI clock is toggling
  // Will transmit read data back to SW over MISO line.
  // Want to put data on the line immediately when CS goes low.
  always @(posedge i_SPI_Clk or posedge i_SPI_CS_n)
  begin
    if (i_SPI_CS_n)
	 begin
      r_TX_Bit_Count <= 3'b111;  // Send MSb first
      r_SPI_MISO_Bit <= i_TX_Byte[3'b111];  // Reset to MSb
    end
    else
    begin
      r_TX_Bit_Count <= r_TX_Bit_Count - 1'd1;

      // Here is where data crosses clock domains from i_Clk to i_SPI_Clk
      // Can set up a timing constraint with wide margin for data path.
      r_SPI_MISO_Bit <= i_TX_Byte[r_TX_Bit_Count];

    end // else: !if(i_SPI_CS_n)
  end // always @ (negedge i_SPI_Clk or posedge i_SPI_CS_n_SW)

  // Preload MISO with top bit of send data when preload selector is high.
  // Otherwise just send the normal MISO data
  assign w_SPI_MISO_Mux = r_Preload_MISO ? i_TX_Byte[3'b111] : r_SPI_MISO_Bit;

  // Tri-statae MISO when CS is high.  Allows for multiple slaves to talk.
  assign o_SPI_MISO = i_SPI_CS_n ? 1'bZ : w_SPI_MISO_Mux;

endmodule // SPI_Slave

И тут наблюдается такое же поведение в железе, как и в моём варианте:

Если не делать никаких действий с параллельным входом модуля (i_tx_buf у меня), т.е. не выводить его наружу и ничего в него не писать, то байты по линии i_mosi принимаются правильно. Так же приём работает правильно, если закомментировать в коде всё что связано с отправкой по o_miso.

В железе тестирую следующим образом:

По mosi шлю микроконтроллером байт каждые 50 мс в бесконечном цикле от 0 до 255. И наблюдаю за светодиодами, подключёнными к порту o_rx_buf.

В результате биты этого порта будто бы местами меняются в хаотичном порядке (с частотой 100 - 1000 мс). 

Share this post


Link to post
Share on other sites
13 часов назад, pinchemierda сказал:

ТЗ: 

1. После принятия посылки из 8 бит по линии i_mosi, модуль должен сформировать сигнал o_irq длительностью 1 такт и выдать принятые данные на внешний порт o_rx_buf

Пару месяцев назад уже была тема с практически таким же ТЗ и практически так же называлась.

Share this post


Link to post
Share on other sites
Цитата

Пару месяцев назад уже была тема с практически таким же ТЗ и практически так же называлась

Этот пример вообще не понял, с дебаунсами какими-то, под 100 ЛЕ ещё занимает. Особо не вникал, просто скопировал. В симуляции он вообще ни на какие входные сигналы у меня не реагировал сначала. В приёмном буфере всегда 8'd0. Оказывается, чтобы заработало надо на CS единицу подать (лол!). 

Share this post


Link to post
Share on other sites

Зато в железе он принимает байты правильно, но отправляет всё равно не то. Да и моему тз он мягко говоря не очень соответствует. Не хочу чужой под себя переделывать. Хочу свой до ума довести и уложиться в 35-50 ЛЕ, всё таки.

Share this post


Link to post
Share on other sites
1 hour ago, pinchemierda said:

Зато в железе он принимает байты правильно, но отправляет всё равно не то. Да и моему тз он мягко говоря не очень соответствует. Не хочу чужой под себя переделывать. Хочу свой до ума довести и уложиться в 35-50 ЛЕ, всё таки.

Хотите до ума довести - доводите.
Вопрос #1 - у вас reset генератор на плате есть?

Share this post


Link to post
Share on other sites

Прошу прощения за тупой вопрос, что за reset генератор? 

На плате кварцевый генератор 50МГц, его вход разрешения enable жёстко заведён на 3.3в, к плис не подключен. Или Вы не про это?

Share this post


Link to post
Share on other sites
1 hour ago, pinchemierda said:

Прошу прощения за тупой вопрос, что за reset генератор? 

На плате кварцевый генератор 50МГц, его вход разрешения enable жёстко заведён на 3.3в, к плис не подключен. Или Вы не про это?

Плата типа такой? Cхему можете прикрепить?

Cyclone.jpg

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.