Jump to content
    

Снова про метастабильность, цепочки триггеров и т.д.

Опыт использования показал, что даже стандартные ip ядра SPI из EDK от Xilinx тоже сбоят, справда довольно редко, и очень похоже на метастабильность. Там получалось что-то около одного сбоя на 1000-5000 транзакций, причем с повышением тактовой частоты SPI количество сбоев увеличивалось. Так что попробуйте еще на официальном форуме посмотреть, проблема получается не новая

...или похоже на банальные setup\hold виолейшены...

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

 

Share this post


Link to post
Share on other sites

Можно чуть подробнее что есть СТА и сетап-холд виолейшены?

 

какая - то фигня у меня творится окончательная

 

у меня есть такое место

 

 

task WriteTask
   begin
      NeedWrite <= 1'b0;
      DataWasWriten <=1'b0;     

      if((NeedWrite == 1'b1) && (addr == Reg1addr))
         begin
           Reg1 <= DataForReg;
           DataWasWriten <= 1'b1;
         end
     .... 
      //таких if много для разных адресов
     
   end
endtask

 

 

и есть

 

always @(posedge clk)
  begin
     WriteTask;

    //поиск фронта строба     
     Strob <= sync_strb;

     case (State)
        0:
         //если фронт строба
         if((Strob == 1'b0)&&(sync_strb == 1'b1))
           begin
               //подготавливаем запись если надо
               NeedWrite <= HaveDataToWrite1; 
               DataForReg <= Data;
               addr <= Addr;

               State <= 1;
            end 
        break;
   
        1:
           begin
               NeedWrite <= HaveDataToWrite2; 
               DataForReg <= Data2;
               addr <= Addr2;
               State <= 3;
            end
          break;

      .....
       7: State <= 0; break;
               
    endcase
  end

 

 

В Data данные верные, проверял

в Addr данные верные, проверял

HaveDataToWrite1 в 1, проверял

а вот флаг DataWasWriten в 1 после 0 состояния не подымается. Вернее не так. Иногда схема загружается так что он поднимается всегда, иногда загружается так что не поднимается никогда.

 

при этом вторая запись проходит штатно, та что по 1 состоянию

 

Эта не точный код, это схема. То есть есть таск который снимает флаги и если был флаг на запись производит запись и ставит флаг что все получилось. И есть некий код записанный в том же алвасе ниже, который если надо ставит флаг. И вот как-то так нелепо получается, что флаг не доходит до записи. Это все происходит под одним клоком. Причем я вижу что сигналы стоят, констраины все сошлись, а запись не прохоит, условие не выполняется... if((NeedWrite == 1'b1) && (addr == Reg1addr))

 

то есть к моменту клока, то ли addr не занимает правильное положение, то ли NeedWrite не становится 1. При этому и то что записывается в addr гарантированно верно и стабильно несколько клоков (при ошибке оно вечно стабильно, можно ждать хоть год). И все условия для выставления NeedWrite в 1 есть. Может у меня не хватает какого-то констраина, какого? какая-то нелепая ситуация...

 

вторая запись тем временем мего стабильно проходит, та что по сигналу HaveDataToWrite2, единственное что одновременно редко бывает чтобы стояли и 1 и 2, то есть если HaveDataToWrite1 == 1'b1, то HaveDataToWrite2 практически всегда 0. То есть как -то получается что к моменту проверки в таске if((NeedWrite == 1'b1) && (addr == Reg1addr)), в этом же клоке идет NeedWrite <= HaveDataToWrite2, то есть NeedWrite <= 0; но этот то 0 должен попасть в флаг на следующем такте! Я в панике! Что я делаю не так?

 

 

 

 

 

 

 

 

Share this post


Link to post
Share on other sites

STA это то, что проверяет констрейны, а сетап-холд виолейшены это виды нарушения этих констрейнов. Возможно, у Вас констрейны не все покрывают, что необходимо.

 

Но все это смахивает именно на некорректный междоменный переход. Лучше бы его конкретно показали бы, полностью, чем какие-то совершенно нечитаемые куски описаний внутренностей.

Share this post


Link to post
Share on other sites

UPD:

 

Кстати, вот у Вас тут и разница, в нулевом case стоит "if((Strob == 1'b0)&&(sync_strb == 1'b1))", в первом такого не стоит. Вот и отличие, которое, скорее всего, глючит (если sync_strb из другого домена например пришел, то в "особо удачном случае" можно пропустить фронт вообще - если длина пути разная от sync_strb до Strob и до всего остального, чем он рулит через это логическое выражение - на первом такте получите 0 и 0 в логическом выражении, но уже 1 на входе "Strob", а на втором такте сразу 1 и 1 в обоих).

 

И вообще, мой совет, просто для удобства и читаемости, разделите на два always эти две стадии вашего "конвейера". Может и глюков поубавится.

Share this post


Link to post
Share on other sites

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

 

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

 

.

always эти две стадии вашего "конвейера".

 

А как я их разделю? " стадии конвейера используют одни и те же регистры, я просто получу еррор о 2 источниках сигнала и все... или я чего-то не понял?

 

 

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

 

Очевидно надо какой-то констраин еще добавить, но какой....

Share this post


Link to post
Share on other sites

А как я их разделю? " стадии конвейера используют одни и те же регистры, я просто получу еррор о 2 источниках сигнала и все... или я чего-то не понял?

 

Элементарно. Первую стадию - регистры NeedWrite, DataForReg, addr, State - описать в одном always (State лучше отдельно даже в третьем, как описание автомата). А вторую стадию - использование данных из этих NeedWrite, addr и DataForReg - в своем, отдельном.

 

always @(posedge clk)
   Strob <= sync_strb;

always @(posedge clk)
  case (State)
        0:
         //если фронт строба
         if((Strob == 1'b0)&&(sync_strb == 1'b1))
.........
        1:
.........
         7: begin
                State <= 0; 
                NeedWrite <= 1'b0;
             end


always @(posedge clk)
   if((NeedWrite == 1'b1)
      begin
       if (addr == )
......
       if (addr == )
......
      end

 

 

Выложить переход между доменами не сложно, только там несколько экранов текста,

 

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

 

Вот некий условный пример междоменного перехода для данных, которые поступают с относительно низкой (с точки зрения clk_b) скоростью. Никаких станиц текста!

 

  reg [7:0] data_buf;
  reg interck_reg_a;
  reg [2:0] interck_reg_b;
  wire data_ready_b;
  reg clr_b;

  // buffer register - use data from it in domain "clk_b" when "data_ready_b"
   always @(posedge clk_a)
       if (data_ready_a)
          data_buf <= in_data;

  // ready flag initiator - clk_a domain sets, clk_b - clears
   always @(posedge clk_a or posedge clr_b)
      if (clr_b) interck_reg_a <= 1'b0;
      if (data_ready_a) interck_reg_a <= 1'b1;

  // ready flag pipe at clk_b side
   always @(posedge clk_b)
      interck_reg_b <= {interck_reg_b[1:0], interck_reg_a};

  // data ready flag for clk_b side
  assign data_ready_b = interck_reg_b[2:1] == 2'b01;

  // register for async clear of ready flag initiator 
   always @(posedge clk_b)
      clr_b <= data_ready_b;

Share this post


Link to post
Share on other sites

--------------------

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

 

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

 

//=================================================
//функция получения адреса чтения из абсолютного значения адреса
function [INTERFACE_REG_ADDR_SIZE - 1 : 0] GetReadAddres (input [INTERFACE_REG_ADDR_SIZE - 2 : 0] Addr);
  begin
    GetReadAddres = ({INTERFACE_READ_MODE, Addr});
  end    
endfunction
        
//функция получения адреса записи из абсолютного значения адреса
function [INTERFACE_REG_ADDR_SIZE - 1 : 0] GetWriteAddres (input [INTERFACE_REG_ADDR_SIZE - 2 : 0] Addr);
begin
   GetWriteAddres = ({~INTERFACE_READ_MODE, Addr});
end    
endfunction


task CommInterface;
begin
  //командный интерфейс
  //by default all signals and data is 0
  //if was reading/writing actions
  //we will override signals val
  comm_rd_ack <= 1'b0;
  comm_wr_ack <= 1'b0; 
  comm_data_out <= 0;

  //--------------------    
  //адрес запроса                
  if(comm_reg_select[DATA_ADDR_REG_ADDR] & comm_rd_strb) //read
    begin
      comm_data_out <= (COMM_FULL_DATA | CommRequestAddr);
      comm_rd_ack <= 1'b1;
    end    
  if(comm_reg_select[DATA_ADDR_REG_ADDR] & comm_wr_strb) //write
   begin
     CommRequestAddr <= comm_data_in[INTERFACE_REG_ADDR_SIZE - 1 : 0];
     NeedRWRegFlagTemp[COMM_INTERFACE_POS] <= 1'b1; //ставим флаг запроса
     comm_wr_ack <= 1'b1;
   end


  //--------------------
  //задание скорости, только запись, чтения нет    
  if(comm_reg_select[SPEED_REG_ADDR] & comm_wr_strb) //write
   begin
     //запрос передает данные
     CommRequestInData <= comm_data_in[STANDART_REG_SIZE - 1 : 0];
     //адрес устанавливается сразу на запись регистра задания скорости
     CommRequestAddr <= GetWriteAddres(SET_SPEED_REG[INTERFACE_REG_ADDR_SIZE - 2 : 0]);
     NeedRWRegFlagTemp[COMM_INTERFACE_POS] <= 1'b1; //ставим флаг запроса
     comm_wr_ack <= 1'b1;
   end
end
endtask

 

таск вызывается каждый раз

 

always @(posedge clk)
  begin
     CommInterface 
  end

 

 

при частом обращении в comm_reg_select[sPEED_REG_ADDR], где то в 1 случае из 10 в CommRequestAddr

попадает какая то белеберда. ВОТ БЛИН КАК!!!! как CommRequestAddr <= GetWriteAddres(SET_SPEED_REG[iNTERFACE_REG_ADDR_SIZE - 2 : 0]); при условии что SET_SPEED_REG - это константа, как в этом регистре может оказываться какая-то леватина? Нигде в проекте больше обращения в CommRequestAddr на запись нет, дальше только он присваивается...

 

При этом в CommRequestInData попали верные данные.

 

И даже если допустить что была какая-то путаница с дешифрацией адреса (хотя все тесты проходят верно), то должно выполниться верхнее присвоение CommRequestAddr <= comm_data_in[iNTERFACE_REG_ADDR_SIZE - 1 : 0]; но данные в

CommRequestInData <= comm_data_in[sTANDART_REG_SIZE - 1 : 0]; и

CommRequestAddr <= comm_data_in[iNTERFACE_REG_ADDR_SIZE - 1 : 0];

получились разные...

 

Я что-то не знаю про функции? Какой-то бред...

 

 

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

 

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

 

// Additional Comments: 
//
//    "spi_cs" and "data_rdy" signal must be in 
//		stable state before any "spi_clk" actions
//		min 2 "clk" for "data_rdy" 
//		and 2 "clk" for "spi_cs"
//
//////////////////////////////////////////////////////////////////////////////////
module SPIComModule_v4_00(
		clk, //основной клок

		spi_cs, //сигнал выбора SPI
		spi_clk, //SPI клок данных
		spi_in, //входные данные SPI
		spi_out, //выходные данные SPI

		module_sel, //выбор модуля
		submodule_sel, //выбор подмодуля
		data_to_module, //данные для модуля
		data_from_module, //данные от модуля

		wr_rd_sync, //сигнал синхронизации чтения - записи
		wr_strb, //строб записи
		wr_ack, //подтверждение записи
		rd_strb, //строб чтения
		rd_ack, //подтверждение чтения

		check_sum_ok, //сигнал подтверждения правильной контрольной суммы
		data_write_done, //сигнал подтверждения записи данных
		data_rdy //сигнал готовности данных для чтения
   );

	//================================================================================
==
	parameter MODULE_DATA_SIZE = 32; //размер данных модуля
	parameter MODULE_ADDR_SIZE = 4; //размер поля адреса 
	parameter MODULE_ADDR_VECTOR_SIZE = (32'b01 << MODULE_ADDR_SIZE); //размер вектора выбора модуля (4 бита адрес)
	parameter SUB_ADDR_SIZE = 3; //размер поля подадреса
	parameter SUB_ADDR_VECTOR_SIZE  = (32'b01 << SUB_ADDR_SIZE); //размер вектора выбора подмодуля (3 бита подадрес)

	parameter READ_DIRRECTION_VAL = 1; //значение бита при котором идет чтение

	parameter ADDR_FIELD_SIZE = 8; //8 бит поле адреса
	parameter CHECKSUM_FIELD_SIZE = 8; //8 бит поле контрольной суммы
	localparam DATA_FIELD_SIZE = MODULE_DATA_SIZE; //размер поля данных 

	//размер регистра расчета длинны сообщения для определения
	//окончания приема
	parameter MES_LEN_COUNTER_SIZE  = 6;  		

	//размер регистра расчета длинны слова контрольной суммы
	//для определения окончания приема очередного слова
	parameter CHECKSUM_LEN_COUNTER_SIZE = 3; 

	//контрольная сумма по всем сообщению должна дать 0
	//чтобы полностью нулевое сообщение не прошло как верное
	//начинаем считать сумму с этого значения 
	parameter START_CHECKSUM_VAL = 'hA5; 

	//================================================================================
==
	input clk;

	input spi_cs;
	input spi_clk;
	input spi_in;
	output spi_out;

	output reg [MODULE_ADDR_VECTOR_SIZE - 1 : 0] module_sel = 0;
	output reg [sUB_ADDR_VECTOR_SIZE - 1 : 0] submodule_sel = 0;
	output reg [MODULE_DATA_SIZE - 1 : 0] data_to_module = 0;
	input [MODULE_DATA_SIZE - 1 : 0] data_from_module;

	//этот сигнал должен быть лишен мета-стабильности
	//во внешних схемах
	input wr_rd_sync;

	output wr_strb;
	input wr_ack;
	output rd_strb;
	input rd_ack;

	output reg check_sum_ok = 0;
	output reg data_write_done = 0;
	output reg data_rdy = 0;

	//================================================================================
==		
	//регистры для задания состояние строба чтения и записи
	//эти регистры задает схема, а выходное значение получается 
	//с учетом сигнала синхронизации
	reg wr_strb_reg = 0;
	reg rd_strb_reg = 0;

	//если разрешена запись или чтение, выдаем наружу стробы
	assign wr_strb = (wr_strb_reg & wr_rd_sync);
	assign rd_strb = (rd_strb_reg & wr_rd_sync);

	//для синхронизации 2 клоковых доменов
	//основного и SPI используем систему семафоров
	//задание и подтверждение задания
	reg need_start_recive = 0; //необходимо начать прием с 1 клоком SPI
	reg need_start_recive_ack = 0; //подтверждение, прием начат

	reg recive_done = 0; //окончание приема
	//так как сигнал из другого клокового домена
	//пропустим его через 2 триггера, это создаст 
	//доп задержку на стабилизацию данных
	//по констрайнам частота процессора в 2 раза выше частоты SPI
	//значит через 2 клока данные SPI гарантированно верны
	reg recive_done_1 = 0;
	reg recive_done_2 = 0;
		reg recive_done_ack = 0; //подтверждение окончания приема

	reg need_start_transllate = 0; //необходимо начать передачу данных
	reg need_start_transllate_ack = 0; //передача данных начата


	//вектор входного сообщения
	localparam FULL_MESSAGE_LEN = ADDR_FIELD_SIZE + DATA_FIELD_SIZE + CHECKSUM_FIELD_SIZE;
	reg [FULL_MESSAGE_LEN - 1 : 0] input_data_vector = 0;
	//для удобства работы разделим поля входного вектора
	wire [DATA_FIELD_SIZE - 1 : 0] data_w; 
	assign data_w = input_data_vector[DATA_FIELD_SIZE + CHECKSUM_FIELD_SIZE - 1 -: DATA_FIELD_SIZE];
	wire [sUB_ADDR_SIZE - 1 : 0] subaddr_w;
	assign subaddr_w = input_data_vector[sUB_ADDR_SIZE + DATA_FIELD_SIZE + CHECKSUM_FIELD_SIZE - 1 -: SUB_ADDR_SIZE];
	wire [MODULE_ADDR_SIZE - 1 : 0 ] addr_w;
	assign addr_w = input_data_vector[MODULE_ADDR_SIZE + SUB_ADDR_SIZE + DATA_FIELD_SIZE + CHECKSUM_FIELD_SIZE - 1 -: MODULE_ADDR_SIZE];
	wire read_direction_w;
	assign read_direction_w	= input_data_vector[1 + MODULE_ADDR_SIZE + SUB_ADDR_SIZE + DATA_FIELD_SIZE + CHECKSUM_FIELD_SIZE - 1 -: 1];


	//вектор расчета контрольной суммы 
	reg [CHECKSUM_FIELD_SIZE - 1 : 0] checksum_calc_vector = START_CHECKSUM_VAL; 
	//вектор приема данных для расчета контрольной суммы
	reg [CHECKSUM_FIELD_SIZE - 1 : 0] checksum_vector = 0; 
	//вектор выходных данных, на 1 бит меньше чем данные
	//так как первый отправляемый бит в него не сохраняется
	reg [DATA_FIELD_SIZE - 2 : 0] output_vector = 0; 
	//значение выходного бита данных SPI должно быть готово
	//до первого клока, еще до защелки данных в сдвиговый регистр
	//поэтому первый бит берется из входного вектора
	//а последующие уже из сдвигового регистра
	//в начальный момент будет стоять семафор 
	assign spi_out = (need_start_transllate != need_start_transllate_ack) ? 
										data_from_module [MODULE_DATA_SIZE - 1] : //начальный момент
										output_vector[MODULE_DATA_SIZE - 2]; //последующие посылки (вектор на 1 бит меньше данных)


	//счетчик длинны сообщения
	reg [MES_LEN_COUNTER_SIZE - 1 : 0] mes_len_counter = 0;
	//счетчик длинны слова контрольной суммы
	reg [CHECKSUM_LEN_COUNTER_SIZE - 1 : 0] checksum_len_counter = 0;


	//================================================================================
==		
	//									обработка основного клока
	//================================================================================
==		
	always @(posedge clk)
		begin
			//-------------------- переход между клоковыми доменами
			recive_done_1 <= recive_done;
			recive_done_2 <= recive_done_1;


			//-------------------- обмен данными
			if(spi_cs == 1'b1) //если нет выбора SPI
				begin 
					//чип селект имеет гарантированную паузу, он асинхронный
					//гарантированная пауза приведет схему в правильное состояние
					//выборы адреса - one hot, один бит в векторе, 
					//при снятии не может так получиться что бит измениться
					//или возникнут еще какие-то соседние биты,
					//потому даже если адрес изменится до снятия wr(rd)_strb
					//так как сигнал cs асинхронный, это не страшно, другие
					//адреса не выберутся и неправильных транзакций не будет
					//значение битов ответов тоже не важно, если чип селект 
					//вверху их уже не проверяют

					//подготовимся к приему следующего сообщения
					//первый фронт SPI клока - начало приема
					need_start_recive <= ~need_start_recive_ack; 					

					//снимем все стробы
					wr_strb_reg <= 0; 
					rd_strb_reg <= 0; 	

					//снимем все выборы, 
					module_sel <= 0;
					submodule_sel <= 0;

					//снимаем все ответы
					check_sum_ok <= 0;
					data_write_done <= 0;
					data_rdy <= 0;

					//на всякий случай, если было поднятие чипселекта 
					//до окончания обмена, снимем флаг окончания приема
					recive_done_ack <= recive_done_2; 
				end
			else //если есть выбор SPI
				begin
					if(recive_done_2 != recive_done_ack) //если закончен прием
						begin	
							//ставим подтверждение обработки флага
							recive_done_ack <= recive_done_2;

							//если сошлась контрольная сумма
							//последние сложение надо сделать в этой процедуре
							//так как после приема последнего бита
							//нет больше тактов (клоков SPI) на выполнение 
							if((checksum_calc_vector + checksum_vector) == {CHECKSUM_FIELD_SIZE{1'b0}}) 
								begin
									check_sum_ok <= 1'b1; //ставим признак

									//ставим выбор модуля и подадреса
									//сразу производим частичную дешифрацию адреса
									module_sel <= ({{(MODULE_ADDR_VECTOR_SIZE - 1){1'b0}}, 1'b1} << $unsigned (addr_w));
									submodule_sel <= ({{(SUB_ADDR_VECTOR_SIZE - 1){1'b0}}, 1'b1} << $unsigned (subaddr_w));

									if(read_direction_w == READ_DIRRECTION_VAL) //если режим чтения
										begin
											rd_strb_reg <= 1'b1; //строб чтения
											//отмечаем что нужно будет начать передачу
											//захват данных в регистр произойдет по 
											//SPI клоку, а его выдаст мастер только после
											//получения сигнала готовности данных
											//так что это значение можно тут выставить
											//и не заботится о нем, даже если будет прерывание обмена
											//без фазы чтения
											need_start_transllate <= ~need_start_transllate_ack;
										end
									else //режим записи
										begin
											data_to_module <= data_w; //выставляем данные
											wr_strb_reg <= 1'b1; //строб записи
										end
								end //контрольная сумма
						end //закончен прием

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

					//если есть запрос на запись и подтверждение
					if((wr_strb_reg == 1'b1)&&(wr_ack == 1'b1))
						data_write_done <= 1'b1; //отмечаем что данные записаны

					//если есть запрос на чтение и подтверждение
					if((rd_strb_reg == 1'b1)&&(rd_ack == 1'b1))
						data_rdy <= 1'b1; //отмечаем что данные готовы

				end //есть выбор SPI
		end //конец обработки клока


	//================================================================================
==		
	//									обработка клока SPI
	//================================================================================
==		
	//выбор SPI не проверяем, так как без выбора не 
	//пройдет конец обмена, весь обмен идет внутри 
	//одного выбора, клоков вне обмена нет
	always @(posedge spi_clk)
		begin

			//----------------------------------------------------------------
			if (need_start_recive != need_start_recive_ack) //если начало приема ы
				begin
					//отмечаем начало приема
					need_start_recive_ack <= need_start_recive;
					//сохраняем первый принятый бит
					input_data_vector <= {{(FULL_MESSAGE_LEN - 1){1'b0}},spi_in};
					checksum_vector <= {{(CHECKSUM_FIELD_SIZE - 1){1'b0}},spi_in};
					//сохраняем начальное значение вектора расчета контрольной суммы
					checksum_calc_vector <= START_CHECKSUM_VAL;

					//заряжаем счетчики, первый бит уже принят, 
					//окончание приема всего сообщения по равенству нулю
					//счетчик всего сообщения на 1 бит меньше чтобы
					//он стал нулем в момент приема последнего бита
					//а не после приема, вместе с приемом последнего
					//бита выставиться и признак окончания приема						
					mes_len_counter <= (FULL_MESSAGE_LEN - 2);
					checksum_len_counter <= (CHECKSUM_FIELD_SIZE - 1);	

				end
			else //прием
				begin
					//уменьшаем счетчики с каждым принятым битом
					mes_len_counter <= mes_len_counter - 1'b1;
					checksum_len_counter <= checksum_len_counter - 1'b1;

					//прием сообщения, сдвигаем входной вектор дополняя входным битом
					input_data_vector <= {input_data_vector[FULL_MESSAGE_LEN - 2 : 0], spi_in};
					//параллельно сохраняем вектор контрольной суммы
					checksum_vector <= {checksum_vector [CHECKSUM_FIELD_SIZE - 2 : 0], spi_in};

					//если приняли все сообщение
					if(mes_len_counter == 0)
						recive_done <= ~recive_done_ack; //отмечаем окончание приема

					//если приняли очередное слово контрольной суммы
					if(checksum_len_counter == 0) 
						begin
							//заряжаем счетчик заново
							checksum_len_counter <= (CHECKSUM_FIELD_SIZE - 1);
							//добавляем принятый вектор в сумму
							checksum_calc_vector <= checksum_calc_vector + checksum_vector;
						end
				end //прием

			//-----------------------------------------------------------------
			//значение передаваемых данных во время приема не важны, 
			//потому всегда передаем, к фазе передачи данные будут
			//запрошены и корректно обновлены
			if (need_start_transllate != need_start_transllate_ack) //если начало передачи 
				begin
					//отмечаем начало передачи
					need_start_transllate_ack <= need_start_transllate;
					//сохраняем данные от модуля без старшего бита,
					//так как первый бит уже передан (взят из данных на отправку)
					//после этого такта, на выход будет передаваться старший бит
					//этого вектора
					output_vector <= data_from_module[DATA_FIELD_SIZE - 2 : 0];	 
				end
			else //передача	
				begin
					//просто сдвигаем вектор данных
					//значение дополнения не важно
					output_vector <= (output_vector << 1);	
				end //передача		

		end //конец обработки клока SPI

endmodule

Share this post


Link to post
Share on other sites

таск вызывается каждый раз

 

А это, простите, как, "каждый раз"? Такой task может быть так вызван не более одного раза.

 

понравиться берите себе мне не жалко.

Да что же тут может понравиться.... Мухи, котлеты, повара и клиенты, все в одном фарше :) Надо бы вынести междоменный переход в отдельный модуль что ли, который подключается между SPI и остальной частью... Чтобы четко видеть, нет ли лишних "левых" связей мимо этого перехода. Даже, более того, при внимательном рассмотрении в приведенном коде нет междоменного перехода, так как нет ни одного регистра, работающего в клоке другого домена.

Share this post


Link to post
Share on other sites

А это, простите, как, "каждый раз"? Такой task может быть так вызван не более одного раза.

имелось ввиду каждый клок. Каждый клок то он вызывается я надеюсь...

 

Надо бы вынести междоменный переход в отдельный модуль что ли

Даже, более того, при внимательном рассмотрении в приведенном коде нет междоменного перехода, так как нет ни одного регистра, работающего в клоке другого домена.

 

это вопрос религии... данные принимаются и отправляются по SPI клоку, после того как данные приняты по клоку SPI уже по клоку схемы

выставляются данные

data_to_module

 

ставятся сигналы

module_sel

submodule_sel

 

и стробы

wr_strb_reg

rd_strb_reg

 

если надо, то на отправку забираются данные из

data_from_module

 

и не надо думать ни о каких сигналах которые могут пойти в обход. Не могут. С одного конца идет асинхронный сигнал, с другого чисто синхронный всей схеме. Убежден что это правильная по архитектуре упаковка модуля.

 

Да и в целом это ничего не объясняет... Понятно что если что-то не так все винят асинхронные сигналы, но тут лажа именно в синхронной части, почему я не могу понять. Либо где-то тупейшая опечатка, либо что-то не так в форме записи, либо уже какие-то неправильные интерпритации записанного средой...

Share this post


Link to post
Share on other sites

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

 

А вот неправильные интерпретации средой я бы исключил. Их бы до Вас бы нашли давно. Хотя... Может я и не прав тут. Такие мудреные описания простых вещей я лично впервые вижу, хотя видел немало описаний. Возможно, их не встречалось еще и у синтезатора. Опечатка... Ну опечатка не вызывает работу/неработу от загрузки к загрузке. Она обычно дает стабильный глюк. Если, конечно, она не в междоменном переходе :) - что, скорее всего, и есть, только найти ее в таком коде, проще застрелиться.

Share this post


Link to post
Share on other sites

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

 

Я сейчас немного раздражен и не могу адекватно реагировать. Если знаете как описать данный функционал проще и есть время, покажите. Если нет, то давайте оставим тему что я своими программистическими описаниями запутал среду. Если у вас есть объяснение как может в строчке Reg <= Const. в Reg попадать что-то иное нежели константа объясните. По мне в приведенном выше примере, при любых значениях сигналов этого быть не может. Не хватает каких-то проверок, констраинов, каких не знаю...

 

 

Share this post


Link to post
Share on other sites

Если у вас есть объяснение как может в строчке Reg <= Const. в Reg попадать что-то иное нежели константа объясните. По мне в приведенном выше примере, при любых значениях сигналов этого быть не может. Не хватает каких-то проверок, констраинов, каких не знаю...

 

Объяснение простое. Там ведь не просто Reg <= Const, а он внутри кучи условий разных. Если то, да если это, то вот только тогда Reg <= Const. Вы не забывайте, что, очень часто, в зависимости от степени задействование локальных Clock Enable в разводке, синтезатор генерирует еще и цепь "а если иначе, то Reg <= Reg" - а вот в этом случае, каждый бит идет через свою логику, которая известна только синтезатору, и меняется от сборки к сборке, и может быть достаточно сложной и длинной. Таким образом, любой глюк, прошедший через цепи каких-то сигналов, участвующих в условиях разрешения записи в регистры, могут легко и непринужденно сбить само данное. Да и при заведении сигнала условия на Clock Enable - тоже - если регистр разбит на куски в разных областях кристалла, то время прохождения сигнала условия до разных CE тоже разное.

 

Констрейны обычно нужны для внешних сигналов, входящих в ПЛИС, и выходящих из нее. Для каждого входного пина нужен констрейн на сетап и холд, для выходного пина - на clock_to_out MIN и MAX. Для внутренних же сигналов, если все правильно спроектировано, констрейны обычно не нужны. Редко бывают нужны констрейны на max_delay на междоменных переходах, но это для 100МГц как правило лишнее.

 

PS

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

Share this post


Link to post
Share on other sites

я был уверен что если это стоит все под

always @(posedge clk)

 

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

Share this post


Link to post
Share on other sites

Разве синхронно меняющийся регистр может не успеть принять свое положение до следующего фронта, без явного не схождения констрайна по клоку в тайминг анализе?

 

Естественно, может. Это и называется "метастабильность", о чем и тема. Состояние, в которое впадает регистр, в результате которого он не успевает до окончания следующего такта прийти в какое-то устойчивое состояние. И часть приемников такого сигнала могут интерпретировать его как 0, другая часть - как 1.

Share this post


Link to post
Share on other sites

какая мета стабильность внутри ПЛИС?

 

 

always @(posedge clk)
begin
   if(reg_wr & reg_adr[CONST1])
     begin
       Reg <= CONST2;
     end
  end

при этом

 

reg_wr <= 1; - тоже под этим клоком

и reg_adr - вод этим клоком.

 

Это уже синхронное изменение от синхронно изменяемых сигналов... как тут может влезть мета стаибльность?

 

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.

×
×
  • Create New...