Jump to content

    
pinchemierda

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

Recommended Posts

Здравствуйте, знатоки ПЛИС!

Только что начал познавать ПЛИС и Verilog. Просмотрел несколько роликов для новичков, прочитал пару методичек университетских. Для закрепления материала попытался написать простенький контроллер SDRAM. Просимулировал в Qartus2, логика работы в принципе устраивает. Но очень смущает большое количество warning-ов, особенно "Latch has unsafe behavior". Помогите избавиться от защёлок!  Сильно не пинайте, слово "верилог" первый раз услышал две недели назад)))


`define SDRAM_IDLE 				3'd0
`define SDRAM_INIT      		3'd1
`define SDRAM_START_W 			3'd2
`define SDRAM_W_BYTE      		3'd3
`define SDRAM_STOP_WR 			3'd4
`define SDRAM_START_R     		3'd5
`define SDRAM_R_BYTE 			3'd6

module SDRAM(
							input  i_clk,				  //FPGA clock
							input	 i_STROB,			  //строб команды
							input	 [2:0]i_cmd,		  //индекс команды драйвера SDRAM
							output reg o_IRQ,		  	  //флаг события 
							
							input  [11:0]o_PAGE_ADR,  //Адрес строки (в обоих банках), 11ый бит - выбор банка (BA)
							input  [15:0]i_W_BUF,	  //Буфер для записи
							output reg [15:0]i_R_BUF, //Буфер для чтения
							
							//SDRAM pins
							output wire o_CLK,		  //SDRAM CLK
							output reg o_WE,
							output reg o_CAS,
							output reg o_RAS,
							output reg o_CS,
							output reg o_BA,
							output reg o_CKE,
							output reg [10:0]o_ADR,   //SDRAM ADRES
							inout  reg [15:0]io_DATA  //SDRAM DATA
				);
	
	reg [8:0]counter; 
	reg [2:0]cmd;
	reg e_CLK;	
	
	assign o_CLK = ~i_clk & e_CLK; //& pe_CLK;
	
	
	//Логика таймера 
	always @(posedge i_clk or posedge i_STROB)
	begin
		if(i_STROB)
		begin
			counter <= 9'd0;
		end
		else
		begin
			counter <= counter + 1'd1;
		end
	end
	
	//Логика разрешения тактирования SDRAM (e_CLK)
	always @(o_IRQ)
	begin
		if(o_IRQ) e_CLK <= 1'd0;
		else e_CLK <= 1'd1;
	end

	//Логика выхода o_IRQ и переключения режимов cmd
	always @(posedge i_clk or posedge i_STROB)
	begin
		
		if(i_STROB)
		begin
			cmd <= i_cmd;
			o_IRQ <= 1'd0;
		end
		else
		begin

			//Логика работы выхода o_IRQ 
			case(cmd)
				
				default: o_IRQ <= 1'd0; 

				`SDRAM_INIT:
				begin
					if(counter == 9'd8) o_IRQ <= 1'd1;
				end
				
				`SDRAM_START_W:
				begin
					if(counter == 9'd1) o_IRQ <= 1'd1;
				end
				
				`SDRAM_W_BYTE:
				begin
					if(counter == 9'd0) o_IRQ <= 1'd1;
				end
				
				`SDRAM_STOP_WR:
				begin
					if(counter == 9'd0) o_IRQ <= 1'd1;
				end
				
				`SDRAM_START_R:
				begin
					if(counter == 9'd3) o_IRQ <= 1'd1;
				end
				
				`SDRAM_R_BYTE:
				begin
					if(counter == 9'd0) o_IRQ <= 1'd1;
				end
				
			endcase
			
		end //else if(i_STROB)
		
	end //always @(posedge i_clk or posedge i_STROB)
	
	//Основной блок логики отработки режимов
	always @(negedge o_CLK or posedge i_STROB)
	begin
		
		case(cmd)
			
			default:	//Бездействие
			begin
				NOP_CMD (o_WE,o_CAS,o_RAS,o_CS,o_BA,o_CKE,o_ADR[10]);
				io_DATA <= 16'dZ;
			end //default
			
			`SDRAM_INIT:	//Инициализация
			begin
				io_DATA <= 16'dZ;
				case(counter)
					9'd0: NOP_CMD                    (o_WE,o_CAS,o_RAS,o_CS,o_BA,o_CKE,o_ADR[10]);
					9'd2: PRECHARGE_ALL_BANKS_CMD    (o_WE,o_CAS,o_RAS,o_CS,o_BA,o_CKE,o_ADR[10]);
					9'd3: AUTO_REFRESH_CMD           (o_WE,o_CAS,o_RAS,o_CS,o_BA,o_CKE,o_ADR[10]);
					9'd5: NOP_CMD                   (o_WE,o_CAS,o_RAS,o_CS,o_BA,o_CKE,o_ADR[10]);
					9'd6: MOD_REG_SET_CMD           (o_WE,o_CAS,o_RAS,o_CS,o_BA,o_CKE,o_ADR);
					9'd7: NOP_CMD                   (o_WE,o_CAS,o_RAS,o_CS,o_BA,o_CKE,o_ADR[10]); 
					default: NOP_CMD (o_WE,o_CAS,o_RAS,o_CS,o_BA,o_CKE,o_ADR[10]);
				endcase
			end //cmd = SDRAM_INIT
			
			`SDRAM_START_W: //Запись data[0]
			begin
				if(counter == 9'd0) 
				begin
					io_DATA <= 16'dZ;
					o_ADR <= o_PAGE_ADR[10:0];											
					o_BA <= o_PAGE_ADR[11];											
					ROW_ACTIVE_CMD(o_WE,o_CAS,o_RAS,o_CS,o_CKE);  
				end
				
				if(counter == 9'd1) 
				begin
					o_ADR <= 11'd0;											
					o_BA <= o_PAGE_ADR[11];	
					io_DATA <= i_W_BUF;														//записываем data[0]	
					WRITE_CMD(o_WE,o_CAS,o_RAS,o_CS,o_CKE); 
				end
			end //cmd = SDRAM_START_W
			
			`SDRAM_W_BYTE: //Запись data[1]...data[255]
			begin
				io_DATA <= i_W_BUF;			 												//записываем остальные байты
				NOP_CMD (o_WE,o_CAS,o_RAS,o_CS,o_BA,o_CKE,o_ADR[10]);
			end //cmd = SDRAM_W_BYTE	
			
			`SDRAM_STOP_WR: //Стоп запись/чтение
			begin
				io_DATA <= 16'dZ;
				o_ADR <= 11'd0;											
				o_BA <= o_PAGE_ADR[11];				
				PRECHARGE_ROW_CMD (o_WE,o_CAS,o_RAS,o_CS,o_CKE);
			end //cmd = SDRAM_STOP_WR	
			
			`SDRAM_START_R: //Чтение
			begin
				if(counter == 9'd0) 
				begin
					io_DATA <= 16'dZ;
					o_ADR <= o_PAGE_ADR[10:0];											
					o_BA <= o_PAGE_ADR[11];											
					ROW_ACTIVE_CMD(o_WE,o_CAS,o_RAS,o_CS,o_CKE);  
				end
				
				if(counter == 9'd1) 
				begin
					o_ADR <= 11'd0;											
					o_BA <= o_PAGE_ADR[11];		
					READ_CMD(o_WE,o_CAS,o_RAS,o_CS,o_CKE); 
				end
				
				if(counter == 9'd2) NOP_CMD (o_WE,o_CAS,o_RAS,o_CS,o_BA,o_CKE,o_ADR[10]);
				if(counter == 9'd4) i_R_BUF <= io_DATA;													//Читаем data[0] 
			end //cmd = SDRAM_START_R
			
			`SDRAM_R_BYTE: //Чтение data[1]...data[255]
			begin
				if(!o_IRQ) i_R_BUF <= io_DATA;															//Читаем остальное
				NOP_CMD (o_WE,o_CAS,o_RAS,o_CS,o_BA,o_CKE,o_ADR[10]);
			end //cmd = SDRAM_R_BYTE	
			
		endcase //case(i_cmd)
			
	end //always @(negedge o_CLK or posedge i_STROB)
	

//----------------------------------------- CMD -------------------------------------------
	task PRECHARGE_ALL_BANKS_CMD;
	output we,cas,ras,cs,ba,cke,a10_ap;
	begin
	cke <= 1; cs <= 0; ras <= 0; cas <= 1; ba <= 1'bX; a10_ap <= 1; we <= 0;
	end
	endtask
	
	task AUTO_REFRESH_CMD;
	output we,cas,ras,cs,ba,cke,a10_ap;
	begin
	cke <= 1; cs <= 0; ras <= 0; cas <= 0; ba <= 1'bX; a10_ap <= 1'bX; we <= 1;
	end
	endtask
	
	task NOP_CMD;
	output we,cas,ras,cs,ba,cke,a10_ap;
	begin
	cke <= 1; cs <= 1; ras <= 1; cas <= 1; ba <= 0; a10_ap <= 0; we <= 1;
	end
	endtask

	task MOD_REG_SET_CMD;				//Full page, Sequential, Latency 2 (11'h27)
	output we,cas,ras,cs,ba,cke;
	output reg [10:0]adr;
	begin
	cke <= 1; cs <= 0; ras <= 0; cas <= 0; ba <= 0; we <= 0;
	adr <= 11'h27;  
	end
	endtask
	
	task ROW_ACTIVE_CMD;				
	output we,cas,ras,cs,cke;
	begin
	cke <= 1; cs <= 0; ras <= 0; cas <= 1; we <= 1;
	end
	endtask
	
	task READ_CMD;
	output we,cas,ras,cs,cke;
	begin
	cke <= 1; cs <= 0; ras <= 1; cas <= 0; we <= 1;
	end
	endtask
	
	task WRITE_CMD;
	output we,cas,ras,cs,cke;
	begin
	cke <= 1; cs <= 0; ras <= 1; cas <= 0; we <= 0;
	end
	endtask
	
	task PRECHARGE_ROW_CMD;
	output we,cas,ras,cs,cke;
	begin
	cke <= 1; cs <= 0; ras <= 0; cas <= 1; we <= 0;
	end
	endtask
	
	task SELF_REF_ENTRY_CMD;
	output we,cas,ras,cs,ba,cke,a10_ap;
	begin
	cke <= 0; cs <= 0; ras <= 0; cas <= 0; ba <= 0; a10_ap <= 0; we <= 1;
	end
	endtask
	
	task SELF_REF_EXIT_CMD;
	output we,cas,ras,cs,ba,cke,a10_ap;
	begin
	cke <= 1; cs <= 1; ras <= 0; cas <= 0; ba <= 0; a10_ap <= 0; we <= 0;
	end
	endtask
//----------------------------------------- END CMD -------------------------------------------
	
endmodule //module EPM240_Test


  

Share this post


Link to post
Share on other sites

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

Мне именно нужно, чтобы содержимое этого  always блока выполнялось и при фронте i_clk и при фронте i_STROB.

Проверил ещё вариант у которого в списке чувствительности оба сигнала и при котором не образуются защёлки, но он в 2 раза больше ЛЕ занимает:

always @(posedge i_clk or posedge i_STROB)
begin
  if(i_STROB)
  begin
  //тут делаем всё что нужно
  end
  else
  begin
  //тут тупо делаем всё тоже самое
  end
end

Нужно чтобы логика выполнения содержимого always блока не зависело от уровня i_STROB, когда происходит событие posedge i_clk.

Share this post


Link to post
Share on other sites

а как тогда работают структуры типа:

always @(posedge clk or posedge reset)
begin
  if(reset)
  begin
  // асинхронный сброс (к примеру) чего-нибудь
  end
  else
  begin
  // какое-то действие на каждом фронте clk
  end
end

Содержимое блока чувствительно одновременно к двум фронтам, или ошибаюсь?

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites
33 минуты назад, pinchemierda сказал:

а как тогда работают структуры типа:

Работают как триггер со сбросом )).

Но никак не триггер с двумя клоками.

Тут нужно понимать, что когда активен сброс действия по клоку не выполняются. И сбросом вы можете присвоить регистру только константу.

 

PS. Сейчас конечно набегут люди и будут говорить, что не только константу, но другое только с серьезными извращениями. Типичное применение - как описано выше.

Share this post


Link to post
Share on other sites

Такой логики пытаюсь добиться (но без защёлок пока не выходит):

1686224234_.jpg.07d8370816d6c798a85694eaaa4e07a1.jpg

 

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

Добавил переменную wire trig и сделал так чтобы она косвенно зависела от  i_STROB и i_clk

wire trig;
assign trig = ~o_CLK & ~o_IRQ;

always @(posedge trig)
begin
  //........
end

в результате получил такую картинку (защёлок уже нет, как и нормальной работы модуля):

61873754_.thumb.jpg.1cb347eb64efc7fbc9dcf3972094e8de.jpg

 

Почему по фронту trig не выставляется соответствующая команда com (0x15)?

com = 0x15 выставляется по ближайшему фронту i_clk.

Edited by pinchemierda

Share this post


Link to post
Share on other sites
7 минут назад, Plain сказал:

Увеличьте тактовую частоту хотя бы на порядок.

Добавлю. И сделайте синхронный проект(искать в сети по этим словам), а не выкладывайте картинки из фильмов ужаса... Иначе эти "почему по фронту trig" будут Вас преследовать очень и очень долго...

Share this post


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

Увеличьте тактовую частоту хотя бы на порядок.

Ааа, ну так не интересно)) Если тактирование SDRAM делать с частотой хотя бы в 2 раза меньше тактовой CPLD это сильно упрощает реализацию.

Развлекаюсь с простенькой платкой с EPM240 (нет PLL) и генератором на 50 МГц. Не хотелось бы опускаться ниже этой частоты.

В общем ясно, я фигнёй занимаюсь. Просто если делать всё синхронно, то частота тактирования SDRAM снижается. Хотелось задействовать каждый такт генератора с максимальной пользой, так сказать... 

Share this post


Link to post
Share on other sites

То же самое другими словами — устраните в ТЗ все частоты больше тактовой. Конкретно речь про некий сигнал квитирования, асинхронный — следовательно, бесконечной частоты.

Edited by Plain

Share this post


Link to post
Share on other sites
54 минуты назад, pinchemierda сказал:

Развлекаюсь с простенькой платкой с EPM240 (нет PLL) и генератором на 50 МГц. Не хотелось бы опускаться ниже этой частоты.

В общем ясно, я фигнёй занимаюсь. Просто если делать всё синхронно, то частота тактирования SDRAM снижается. Хотелось задействовать каждый такт генератора с максимальной пользой, так сказать... 

На самом деле все это "по-Суворовски, через Альпы". При всем моем уважении к полководцу...

Как я понял ТС, у него есть задача: Научиться работать с ПЛИС. И, соответственно, должны быть пути ее решения. Так вот, в данном случае, абсолютно неверно выбран путь.  EPM240 абсолютно не годится для тренировок. Несколько десятков перепрошивок и кранты... Невозможно использовать встроенный логический анализатор. Сложности с укладкой проекта в кристалл. И зачем SDRAM к этой CPLD тоже не понятно... 

И далее из неправильных установок следуют неправильные выводы: "Если тактирование SDRAM делать с частотой хотя бы в 2 раза меньше тактовой CPLD это сильно упрощает реализацию."

А так, чисто по жизни, если нет умения делать RTL проекты, то и плата, как таковая не нужна... А уж купить недорогую FPGA, даже и не новой серии - сегодня вообще не проблема...

Share this post


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

Конкретно речь про некий сигнал квитирования, асинхронный — следовательно, бесконечной частоты.

Источником этого сигнала конкретно в этом проекте хотел сделать микроконтроллер, частота то его уж точно не выше частоты тактирования ПЛИС.

Задумывалось что-то типа переходника SDRAM<->SPI для подключения оперативки к мк без FMC (по SPI). И речь даже не идёт о том, что есть реальная задача, которую необходимо решить оптимальным путём.

Цитата

Несколько десятков перепрошивок и кранты...

В сотню попыток бы я уж уложился. Не мало важно для меня, что цена чипа на уровне стоимости чебурека. Всякие циклоны за 2к+ пока что не рассматриваю. Рассматриваю ПЛИС пока что, только как расширитель аппаратных возможностей микроконтроллера. 

 

Share this post


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

что-то типа переходника SDRAM<->SPI

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

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.