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

Начинаю изучать FPGA (verilog)

Всем привет. Начал изучение FPGA, есть ряд вопросов.

Девборда с  алиэкспресса на циклоне IV  (EP4CE6E22C8)

IDE: Quartus Prime 19.01 Lite

 

Первым проектом хочу сделать PWM драйвер сервоприводов с управлением по SPI. Часть с ШИМом написал буквально сразу после прочтения "введения в верилог":

//! Servo PWM channel
module PWMChannel (
	input		wire 			in_clk,
	input		wire[12:0]	in_width,
	output	wire			out_pwm
);

	//! Clock per 1us
	`define CLK_PER_US 50

	//! PWM cycle counter in usec
	reg [20:0]		cycle = 0;
	
	//! Output level
	reg [1:0]		level = 0;
	
	always @(posedge in_clk) begin
		if (cycle < 20000 * `CLK_PER_US) begin
			cycle <= cycle + 1'b1;
			
			// - Detect end of pulse
			if (cycle > in_width * `CLK_PER_US) level <= 0;	
		end else begin
			// - Restart cycle
			cycle <= 0;
			level	<= 1;
		end
	end
	
	// - Output
	assign out_pwm = level;
endmodule

 

А вот с SPI Slave есть проблемы. Не получается реализовать чтение из FPGA в контроллер. Не компилируется. С записью в FPGA всё в порядке. Примеры реализации SPI смотрел в интернете, но до конца не понимаю почему не компилится.

Пробую максимально тупую отправку:

module SPISlave(
	/* --- SPI Connection --- */
	inout 	wire 	SS,
	input 	wire 	SCLK,
	input 	wire 	MOSI,
	output	wire 	MISO,
	
	/* --- RxTx Buffers --- */
	output	reg[7:0]		rx_buffer = 8'b00000000,
	input		wire[7:0]	tx_buffer,
	
	output	wire			io_next_byte
);
	
	//! Number of bits since start of session
	reg [3:0]	io_bits_count;
	reg			out_bit;

	// - Detect session start
	always @(posedge SS) begin
		io_bits_count 	<= 0;
		out_bit			<= tx_buffer[0];
	end
	
	// - Send next bit
	always @(negedge SCLK) begin 
		io_bits_count 	<= io_bits_count + 1;
		out_bit			<= tx_buffer[io_bits_count];
	end

	assign MISO = out_bit;
endmodule

 

Не компилися

Error (10028): Can't resolve multiple constant drivers for net "io_bits_count[3]" at spi_slave.v(26)
Error (10029): Constant driver at spi_slave.v(20)
Error (10028): Can't resolve multiple constant drivers for net "io_bits_count[2]" at spi_slave.v(26)
Error (10028): Can't resolve multiple constant drivers for net "io_bits_count[1]" at spi_slave.v(26)
Error (10028): Can't resolve multiple constant drivers for net "io_bits_count[0]" at spi_slave.v(26)
Error (10028): Can't resolve multiple constant drivers for net "out_bit" at spi_slave.v(26)

 

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

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


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

Типичная ошибка программиста.

Вы обьявляете сигнал io_bits_count и пытаетесь использовать его в двух клоковых доменах (если по дизайну). А этого нельзя сделать (как подать в один клоковый порт 2 сигнала). Из-за этого ошибки.

Совет - почитайте по цифровому дизайну чего-то толкового, на не как педалить на Verilog "введение в верилог". Нужно понимать что вы делаете и что Verilog - это инструмент описания цифровой техники и он описывает цифровую схему. А не программирует программу.

 

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


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

2 часа назад, HardRock сказал:

 

Хотите научиться работать с FPGA  - изучите идеологию построения проектов в FPGA. Изучите что такое синхронный дизайн. Изучите основные временные соотношения, чтобы было понимание что такое Tsetup, Thold, MTBF. Изучите работу с симулятором (Modelsim, ActiveHDL). Изучите идеологию работы с асинхронными доменами. Изучите как правильно строить цифровые автоматы (FSM). И только потом приступайте к реализации протоколов. Начните с малого. Напишите сдвиговые регистры. Попробуйте счетчики описать с различным режимами( синхронная загрузка, с разрешением работы, с синхронным сбросом, с асинхронным сбросом и пр.). Напишите цифровой автомат светофора. Посимулируйте его. Пытаться сразу писать рабочий проект без этих знаний, всё равно что копать яму детской лопаткой вместо экскаватора. 

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


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

Причем есть книга "ПЛИС. Курс молодого бойца", она переведена на русский, читается легко, осиливается за неделю. Но....никто не читает их начинающих и студентов, как показывает моя практика) 

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


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

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

24 minutes ago, Flip-fl0p said:

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

Ооо,   навалились на новенького -  тише,  а  то испугаете его  и он уйдет в VHDLщики   :biggrin: 

В Verilog  програмировать впо

2 hours ago, Nick_K said:

Вы обьявляете сигнал io_bits_count и пытаетесь использовать его в двух клоковых доменах (если по дизайну). А этого нельзя сделать (как подать в один клоковый порт 2 сигнала). Из-за этого ошибки.

Если уж и ругаете то ругайте правильно -  в Verilog  нельзя (для синтеза)  делать присвоение одной переменной  в разных  procedure блоках  always. Как и нельзя делать одновременное присвоение одной переменное в assign  и в always. А почему - потому что при синтезе невозможно однозначно конвертировать такой код  в реальное железо. 

Правильно будет завести сигнал   SS в клок домен SCLK  выделить фронт и им уже сбрасывать  io_bits_count. Ну или делать асинхронный сброс io_bits_count уровнем сигнала SS. 

Удачи! Rob.

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


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

Если это классический SPI slave, то почему SS - inout? И можно ли считать тут SCLK обычным клоком, ведь он есть не всегда...

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


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

47 минут назад, RobFPGA сказал:

Ооо,   навалились на новенького -  тише,  а  то испугаете его  и он уйдет в VHDLщики   :biggrin: 

Лучше сразу человека предупредить, что его ждет на пути освоения ПЛИС. Хотя ничего сложного я тут не вижу . По сути изучить набор простых правил. Понять как языковые конструкции реализуются в железе (понимать где будет LUT , где регистры, где память, где DSP блоки) т.е. просто в общих чертах понимать что пишешь. Всё равно вся работа с ПЛИС упирается в алгоритмы и знания интерфейсов.

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


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

Наверное ещё надо сказать, что далеко не всё, что называется *clk, следует так же и понимать, пытаясь использовать напрямую — ведомый SPI вообще на FSM элементарно решается, т.е. интерпретатором на системной частоте, требование к которой в данном случае — заведомо вдвое больше SCLK.

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


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

50 минут назад, gosha-z сказал:

Если это классический SPI slave, то почему SS - inout? И можно ли считать тут SCLK обычным клоком, ведь он есть не всегда...

Скорее опечатка. Хотя изначально хотел сделать универсальный модуль, но потом понял что нужно сделать хотя бы слейв :)

1 час назад, Flip-fl0p сказал:

Хотите научиться работать с FPGA  - изучите идеологию построения проектов в FPGA. Изучите что такое синхронный дизайн. Изучите основные временные соотношения, чтобы было понимание что такое Tsetup, Thold, MTBF. Изучите работу с симулятором (Modelsim, ActiveHDL). Изучите идеологию работы с асинхронными доменами. Изучите как правильно строить цифровые автоматы (FSM). И только потом приступайте к реализации протоколов. Начните с малого. Напишите сдвиговые регистры. Попробуйте счетчики описать с различным режимами( синхронная загрузка, с разрешением работы, с синхронным сбросом, с асинхронным сбросом и пр.). Напишите цифровой автомат светофора. Посимулируйте его. Пытаться сразу писать рабочий проект без этих знаний, всё равно что копать яму детской лопаткой вместо экскаватора. 

Интереснее сразу учиться на прикладных задачах. Изучение с базовой теории конечно правильно, но требует слишком много времени. У меня есть некоторый бэкграунд по микроконтроллерам (STM32 / AVR) и весомый по разработке вообще в том числе системной. Есть практическая задача в рамках хобби - нужно сделать железку - многоканальный PMW контроллер с SPI интерфейсом + коммутация входов / выходов + оцифровка протокола похожего на UART чтобы потом забирать из SPI. Конечно на данный момент мне это намного проще собрать из готовых корпусов PWM, аналоговых ключей типа 4066 и взвалить чуть больше работы на МК, но думаю тут плису самое место, осталось самое простое - изучить и сделать.

37 минут назад, Plain сказал:

Наверное ещё надо сказать, что далеко не всё, что называется *clk, следует так же и понимать, пытаясь использовать напрямую — ведомый SPI вообще на FSM элементарно решается, т.е. интерпретатором на системной частоте, требование к которой в данном случае — заведомо вдвое больше SCLK.

Циклон тактируется на 50МГц, SPI что-то около  1МГц сейчас, в рабочем варианте должен до 8МГц чтобы использовать возможности STM32.

SCLK это такт от мастера.

 

На тему FSM, пробовал так:

module SPISlave(
    /* --- SPI Connection --- */
    inout     wire     SS,
    input     wire     SCLK,
    input     wire     MOSI,
    output    wire     MISO,
    
    /* --- RxTx Buffers --- */
    output    reg[7:0]        rx_buffer = 8'b00000000,
    input        wire[7:0]    tx_buffer,
    
    output    wire            io_next_byte
);
    //! Number of bits since start of session
    reg [3:0]    io_bits_count;
    reg [1:0]    state;
    
    initial begin
        state <= 0;
    end
    
    always @(SS, SCLK) begin
        case (state)
            // - IDLE state
            0:    begin
                // - Start session
                if (SS == 1 && SCLK == 0) begin
                    io_bits_count = 0;
                    state = 1;
                end
            end
            
            // - RUNNING state
            1: begin
                if (SS == 1) begin
                    // - Rising edge
                    if (SCLK == 1) begin
                        rx_buffer <= { rx_buffer[6:0], MOSI };
                        
                    // - Falling edge
                    end else begin
                        io_bits_count <= io_bits_count + 1'b001;
                    end
                end else begin
                    state = 0;
                    io_bits_count = 0;
                end
            end
        endcase
        
    end

    
    assign MISO = tx_buffer[io_bits_count];
endmodule
 

 

Тоже не работает, ни на прием ни на передачу. С точки зрения "обычного" программиста выглядит корректно -  ловим любые изменения SS или SCLK, если SS подняли, а клок пока ну нуле - это начало передачи. Потом если SS поднят и клок поднят - это передний фронт такта, если клок на нуле - это задний фронт такта, переходим к следующему биту и так пока SS не вернут на землю.

С предыдущим понятно, нельзя изменять один регистр в двух блоках, что тут не так с точки зрения плиса?

Если правильно понимаю, самый "правильный" способ - это тактироваться от системного клока и вручную искать фронты SPI сигнала? Но при этом сложность реализации сильно возрастает...

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


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

17 minutes ago, HardRock said:

Тоже не работает, ни на прием ни на передачу. С точки зрения "обычного" программиста выглядит корректно -  ловим любые изменения SS или SCLK, если SS подняли, а клок пока ну нуле - это начало передачи. Потом если SS поднят и клок поднят - это передний фронт такта, если клок на нуле - это задний фронт такта, переходим к следующему биту и так пока SS не вернут на землю.

С предыдущим понятно, нельзя изменять один регистр в двух блоках, что тут не так с точки зрения плиса?

Если правильно понимаю, самый "правильный" способ - это тактироваться от системного клока и вручную искать фронты SPI сигнала? Но при этом сложность реализации сильно возрастает...

а с точки зрения ПЛИС разработчика, выглядит ужастно и работать не будет, что вы и имеете факт наблюдать. Рекомендую все же потратить 5 дней на изучение основ. Мало кому интересно на форуме лекции вам писать) 

ЗЫ. А сложность там не растет, наоборот все намного проще. Опять же с точки зрения ПЛИС разработчика. 

ЗЗЫ. В гугле SPI slave Verilog code сорцов как у дурака махорки (с). Может все же лучше сначала почитать, чем идти по своим граблям? :) 

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


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

Смотрел примеры из интернета, они либо очень странные либо очень мудреные с поддержкой разных режимов SPI. Так что только свои грабли! :)

А можно всётаки в двух словах, без лекций, в чем ключевая ошибка?

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


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

1 минуту назад, HardRock сказал:

А можно всётаки в двух словах, без лекций, в чем ключевая ошибка?

А в двух словах: в комбинационном always меняется state, которого нет в списке чувствительности - в итоге латчи.

Это так, для начала.

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


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

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

Вот смотрите  - когда вы пишите  always @(...)  вы говорите синтезатору что мол хочу  сделать схему  меняющую свое состояние по событиям заданными в скобках.  То есть по вашему   always @(SCLK, SS) .. при любом изменении сигнала SS или SCLK .
Но в железе  нет триггеров с несколькими тактовыми входами CLK. И какой сигнал будет тактовым в вашем случае?  То есть вам надо так описать событие чтобы было однозначно понятно что будет клоком для триггеров. 
Есть несколько типовых шаблонов синтеза для этого - 

always @(posedge|negedge  SCLK) begin
  if (SS) begin
    ... синхронная схема по положительному|отрицательному фронту SCLK ..
  end
end  
      
always @(posedge|negedge  SCLK, posedge|negedge SS) begin
  if (SS == 1|0) begin 
     ...асинхронный ресет по высокому|низкому уровню SS ... 
  end  
  else begin 
   ... синхронная схема по положительному|отрицательному фронту SCLK ..
  end
end
  
always @(*) begin
  .. чистая комбинаторная логика или коварный latch 
end

assign ... чистая комбинаторная логика

Добавьте к этому правило о присвоение значений переменной  только в одном always блоке  и  считайте что вы постигли 50% таинств написания Verilog кода :biggrin:

Вот пример счетчика с асинхронным сбросом и счетом по отрицательному фронту 

always @(negedge SCLK or posedge SS) besin
  if (SS==1) begin
   io_bits_count <= 0;
  end
  else begin
   io_bits_count <= io_bits_count+1;
  end
end

Удачи!  Rob.

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


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

Спасибо! Не знал что можно писать posedge|negedge, в описании языка таких конструкций не находил, там просто что "если вам нужно любое изменение, то пишите просто имя, без posedge negedge).

Пока сделал так:

module SPISlave(
	input 	wire 	clk,
	/* --- SPI Connection --- */
	inout 	wire 	SS,
	input 	wire 	SCLK,
	input 	wire 	MOSI,
	output	wire 	MISO,
	
	/* --- RxTx Buffers --- */
	output	reg[7:0]		rx_buffer = 8'b00000000,
	input		wire[7:0]	tx_buffer,
	
	output	wire			io_next_byte
);
	//! Number of bits since start of session
	reg [3:0]	io_bits_count;
	reg [3:0]   state;
	
	initial begin
		io_bits_count 	= 0;
		state 			= 0;
	end
	
	always @(posedge clk) begin
		case (state)
			// - IDLE, detect start
			0: begin
				if (SS == 1 && SCLK == 0) begin
					state 			<= 1;
					io_bits_count 	<= 0;
				end
			end
				
			// - SS UP, detect rising edge of SCLK, read bit
			1: begin
				if (SS == 1) begin
					if (SCLK == 1) begin 
						rx_buffer <= { rx_buffer[6:0], MOSI };
						state <= 2;
					end
				end else begin
					state <= 0;
				end
			end
			
			// - Detect falling edge of SCLK, send bit
			2: begin
				if (SS == 1) begin
					if (SCLK == 0) begin
						io_bits_count <= io_bits_count + 1;
						state <= 1;
					end
				end else begin
					state <= 0;
				end
			end
		endcase
	end

	assign MISO = tx_buffer[7 - io_bits_count];

endmodule

 

Работает и прием и отправка, но не очень стабильно, иногда  слетает синхронизация на 1-2 сессии. С ардуины но SPI дергаю 1 байт (чтение / запись) каждые 500мс. Нужно будет осциллографом посмотреть.

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


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

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

3 minutes ago, HardRock said:

Работает и прием и отправка, но не очень стабильно, иногда  слетает синхронизация на 1-2 сессии.

А это  уже другие  премудрости - построение цифровой схемы  Когда  вы заводите внешние асинхронные к клоку clk сигналы (SS, SCLK) в синхронную схему   обязательно их надо  пропустить через синхронизатора (2-3 D триггера  подряд)  чтобы убрать эффект метастабильности. В железе  в отличии от программы - сигнал физически имеет конечную скорость и не всегда успевает долететь до середины Днепра... до разных частей схемы вовремя. И когда вы пишете ... if (SS==1) ...  это не значит что проверка идет физически в одном месте - реально может быть десятки/сотни физически разных мест где это строчка реализуется в железе. Поэтому  пропуская через синхронизатор вы гарантируте что вся ваша синхронная схема  будет получать чистый и один и тот же сигнал.


Для этого кстати служать и timing constrain которые вы тоже должны задавать для компиляции в конкретную FPGA.  Но это уже  к Verilog отношение  имеет опосредсвенно. 

Удачи! Rob.

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


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

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

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

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

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

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

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

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

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

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