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

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

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

spireg, он используется и для приема и для передачи

Таков стандарт (Motorola), MISO и MOSI — это просто вход и выход одного сдвигового регистра, чтобы устройства можно было соединять цепочкой. Можно почитать любую бумажку на микроконтроллер, например, PIC — на картинке MSSP всё видно.

 

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

работает

А не должно было, там же вроде спорное присваивание, я очень редко ПЛИС пользуюсь.

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


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

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

там же вроде спорное присваивание

Там ничего спорного нет. «То есть абсолютно».

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


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

3 hours ago, yes said:

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

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

Ах да, и там нельзя опустится до регистрового уровня в прямом смысле этой фразы. Можно только пользоваться некими пресетними примитивами и структурами (так, например вся AXI шина обьявляется простым классом, а работа с потоками происходит через переменные) и никаких нюансов. Хотите SPI - обьявляете элемент класса SPI заводите в переменную, которая одновременно служит выходным net'ом с модуля и ведёте куда нужно. Но настроить времянки, типы передачи, количество регистров пайплайна - очень сложно и потребует ещё одной жизни :wink:

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


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

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

Пробовал сделать на INOUT, но что-то не получилось. Сделал пока так (тест):


//! Interrupt module
module IR (
  input   wire  in_clk,   // - System clock
  input   wire  in_set,   // - Set command
  input   wire  in_reset, // - Reset command
  output  wire  out_state // - Current state
);
  reg       state;  
  reg [1:0] setpulse;
  reg [1:0] resetpulse;

  assign out_state = state;
  
  always @(posedge in_clk) begin
    // - Update pulses
    setpulse    <= { setpulse[0], in_set };
    resetpulse  <= { resetpulse[0], in_reset };
    
    // - Detect set
    if (setpulse == 2'b01) state <= 1'b1;
    
    // - Detect reset
    if (resetpulse == 2'b01) state <= 1'b0;
  end  
endmodule


//! Device using interrupts
module Device (
  input   wire  clk,        // - System clock
  output  wire  IVT_STATE,  // - Interrupt state
  input   wire  IVT_RESET   // - Interrupt reset line
);
  integer  counter;
 
  //! Set interrupt register
  reg __IVT_SET__;
 
  // - Interrupt module
  IR interrupt(clk, __IVT_SET__, IVT_RESET, IVT_STATE);
  
  // - Main loop
  always @(posedge clk) begin
    // - Reset (SET) command
    if (__IVT_SET__) __IVT_SET__ <= 0;
    
    // - Run timer only when interrupt not active
    if(IVT_STATE == 0) begin
      // - Count 2 seconds
      if (counter < 100000000) counter <= counter + 1;
      else  begin
        counter <= 0;
        
        // - Trigger interrupt
        __IVT_SET__ <= 1;
      end
    end
  end
endmodule

 

и "внешний код"

 


  reg     blink;
  integer blink_count;

   
  wire  DEV_IVT_STATE;
  reg   DEV_IVT_RESET;
  
  // - Device
  Device device(clk, DEV_IVT_STATE, DEV_IVT_RESET);
  
  always @(posedge clk) begin
    // - Drop reset state
    if (DEV_IVT_RESET) DEV_IVT_RESET <= 0;
    
    // - Wait for interrupt
    if (DEV_IVT_STATE == 1) begin
      // - Reset interrupt using button
      if (button == 0)  begin
        DEV_IVT_RESET <= 1;
      end
    end
    
    // - Just blink always :)
    if (blink_count < 25000000) blink_count = blink_count + 1;
    else begin
      blink_count <= 0;
      blink <= ~blink;
    end
  end
  
  
  assign out_pwm_channel_1 = ~DEV_IVT_STATE; // - Debug interrupt state
  assign out_pwm_channel_2 = 1; // - Off
  assign out_pwm_channel_3 = 1; // - Off
  assign out_pwm_channel_4 = blink; // - Blink always

 

Что мне тут не нравится - после установки / сброса прерывания, например __IVT_SET__ <= 0, на следующем клоке нужно вернуть __IVT_SET__ в ноль. Ещё в идеале передавать бы только одну шину. Не то чтобы сильно мешается, но как - то костыльно выглядит :)
В моём понимании идеальным решением было бы "расшарить" регистр между модулями. В одном модуле в него пишут 1, при этом второй модуль можно считать эту единицу, а потом записать туда нолик и первый модуль увидит его.

Так можно?

И ещё вопрос: как текущее решение, годное? и как рекомендуете делать "прерывания"?

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


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

псевдокод:

if signal_write_1 = 1 then

register <= 1

elsif signal_write_0 = 1 then

register <= 0

end if

оберните в олвейс с клоком и будет Вам

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

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


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

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

При этом если сделать inout reg то "Error (12014): Net "test", which fans out to "x", cannot be assigned more than one value" если читать писать в разных модулях. А с третьим состоянием не получилось - не работает. 
 

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


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

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

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

При этом если сделать inout reg то "Error (12014): Net "test", which fans out to "x", cannot be assigned more than one value" если читать писать в разных модулях. А с третьим состоянием не получилось - не работает. 
 

Ваш код уже выглядит не очевидно. Хуже уже не будет. 

А ответьте мне на вопрос. Где Вы в SPI нашли двунаправленные шины ? Если это не какой-то специальный хитро-выдуманный SPI, то там никаких двунаправленных шин быть не должно. Только 4 линии: MOSI, MISO, SCLK, CS_n. Я в вашем коде не увидел никаких синхронизаторов для фильтрации метастабильности. Рекомендацию про Небольшую фильтрацию дребезга Вы тоже проигнорировали. На столе конечно может это и будет работать. Но не более. 

Вообще тут уже советовали Вам, но я всё же повторюсь. Начните проектирование Вашего устройства на бумаге: сначала описываете структурную схему. Потом каждый блок в этой схеме реализуете на HDL, затем все соединяете. Каждый блок желательно просимулировать в Modelsim (или ActiveHDL). После удачной симуляции дизайна приступаете к написанию констрейнов. 

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


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

20 минут назад, Flip-fl0p сказал:

А ответьте мне на вопрос. Где Вы в SPI нашли двунаправленные шины ? Если это не какой-то специальный хитро-выдуманный SPI, то там никаких двунаправленных шин быть не должно. Только 4 линии: MOSI, MISO, SCLK, CS_n.

Так это и есть двунаправленный интерфейс, на каждый такт SCLK мастер читает бит из MISO и пишет в MOSI. Всё остальное - протокол общения с железкой, а биты в любом случае летают. Ну или можно не подключать физически MOSI / MISO если какое-то направление не нужно.

Сейчас рабочий вариант SPI Slave выглядит так:

//! Interrupt module
module Interrupt (
  input   wire  in_clk,   // - System clock
  input   wire  in_set,   // - Set command
  input   wire  in_reset, // - Reset command
  output  wire  out_state // - Current state
);
  reg       state;  
  reg [1:0] setpulse;
  reg [1:0] resetpulse;

  assign out_state = state;
  
  always @(posedge in_clk) begin
    // - Update pulses
    setpulse    <= { setpulse[0], in_set };
    resetpulse  <= { resetpulse[0], in_reset };
    
    // - Detect set
    if (setpulse == 2'b01) state <= 1'b1;
    
    // - Detect reset
    if (resetpulse == 2'b01) state <= 1'b0;
  end  
endmodule

//! SPI Slave module
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,
	
  /* --- Interrupts --- 
    | NAME          | SET   | RESET | Desctiption
    ----------------------------------------------------------
    | IRQ_IO_BYTE   | HW    | SW    | Byte boundary reached
    | IRQ_IO_START  | HW    | SW    | Session started
    | IRQ_IO_STOP   | HW    | SW    | Session stopped
    | IRQ_IO_ERROR  | HW    | SW    | IO error, session broken
  */
  //! Interrupts status vector
  output  wire[ENV_IRQ_COUNT-1:0] IVT_STATUS,
  
  //! Interrupts reset vector
  input   wire[ENV_IRQ_COUNT-1:0] IVT_RESET
);

  /* --- Parameters :: Environment --- */
  //! Number of interrupts
  parameter ENV_IRQ_COUNT   = 4;
  
  /* --- Parameters :: Interrupts --- */
  parameter IRQ_IO_BYTE     = 0;
  parameter IRQ_IO_START    = 1;
  parameter IRQ_IO_STOP     = 2;
  parameter IRQ_IO_ERROR    = 3;

  //! SS state buffer
  reg [1:0] ss_state;
  
  //! SCLK state buffer
  reg [1:0] sclk_state;
  
  //! MOSI state buffer
  reg [7:0] mosi_buffer;
  
  //! IO buffer
  reg [7:0] io_buffer;
  
  //! Number of bits transfered inside byte boundary
  reg [4:0] io_bits_count;
  
  //! Set interrupt register
  reg [ENV_IRQ_COUNT-1:0] IVT_SET;
  
  // - Connect MISO only when session strated
  assign MISO = (ss_state[1]) ? io_buffer[7] : 1'bZ;
  
  // - Generate interrupt drivers
  generate
    genvar i;
    for (i = 0; i < ENV_IRQ_COUNT; i = i + 1) begin : Interrupts
      Interrupt IR(clk, IVT_SET[i], IVT_RESET[i], IVT_STATUS[i]);
    end
  endgenerate
  
 
  // - Run
  integer ix;
  always @(posedge clk) begin
    // - Drop interrupt set requests
    for (ix = 0; ix < ENV_IRQ_COUNT; ix = ix + 1) begin : Interrupts_Drop_SET
      if (IVT_SET[ix]) IVT_SET[ix] <= 0;
    end
   
    // - Process IO ony outside of interrupts (TODO: all interrupts)
    if (IVT_STATUS[0] == 1'b0) begin
      // - Update state buffers
      ss_state    <= { ss_state[0],     SS    };
      sclk_state  <= { sclk_state[0],   SCLK  };
      mosi_buffer <= { mosi_buffer[0],  MOSI  };
    
      // - Detect SS rising edge, start session
      if (ss_state == 2'b01) begin
        // - Copy TX buffer into IO buffer
        io_buffer <= tx_buffer;
        
        // - Reset IO bits count
        io_bits_count <= 0;
        
        // - Rise IRQ_IO_START
        IVT_SET[IRQ_IO_START] <= 1'b1;
      end
      
      // - Detect SS falling edge, end session
      if (ss_state == 2'b10) begin
        // - Rise IRQ_IO_STOP
        IVT_SET[IRQ_IO_STOP] <= 1'b1;
      end
      
      // - Detect SCLK rising edge
      if (sclk_state == 2'b01) begin
        // - Receive next bit and shitf io_buffer 
        io_buffer <= { io_buffer[6:0], mosi_buffer[0] };
      end
      
      // - Detect SCLK falling edge
      if (sclk_state == 2'b10) begin
        // - Go to next bit
        io_bits_count <= io_bits_count + 1;
        
        // - Check byte boundary (bits count eq 8)
        if (io_bits_count == 7) begin
          // - Copy IO buffer into RX buffer
          rx_buffer <= io_buffer;
          
          // - Load TX bufer into IO buffer
          io_buffer <= tx_buffer;
         
          // - Reset bits count
          io_bits_count <= 0;
          
          // - Rise IRQ_IO_BYTE interrup
          IVT_SET[IRQ_IO_BYTE] <= 1'b1;
        end
      end
    end
  end

endmodule

и используется так

  //! SPI Slave
  wire  [7:0]   spi_rx_buffer;
  reg   [7:0]   spi_tx_buffer;
  wire  [3:0]   spi_ivt_status;
  reg   [3:0]   spi_ivt_reset;
  integer       i;

  SPISlave spi_port(clk, in_SPI_SS, in_SPI_SCLK, in_SPI_MOSI, out_SPI_MISO, spi_rx_buffer, spi_tx_buffer, spi_ivt_status, spi_ivt_reset);
  
  always @(posedge clk) begin
    // - Drop interrupt set requests
    for (i = 0; i < 4; i = i + 1) begin
      if (spi_ivt_reset[i]) spi_ivt_reset[i] <= 0;
    end

    // - Wait IRQ_IO_BYTE
    if (spi_ivt_status[0]) begin    
      if (spi_rx_buffer == 8'h55) begin
        spi_tx_buffer <= 8'hAA;
      end else begin
        spi_tx_buffer <= spi_rx_buffer;
      end
      
      spi_ivt_reset[0] <= 1'b1;
    end
    
  end

Основан на методе от Plain и работает весьма стабильно, ну тоесть по меньше мере на столе не глючит как его не дёргай и за что не трогай :)

На осциллографе выглядит немного рискованно, но в целом весьма не плохо.

 

Кстати, как снаружи модуля можно получить размер шины или значения параметров модуля? Например  в модуле на данный момент 4 перывания parameter ENV_IRQ_COUNT   = 4. Внутри модуля всё ок, все размерности и циклы работают автоматом, а как быть снаружи?

Изменено пользователем HardRock

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


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

Вы понимаете что такое двунаправленные шины ? Что такое Z состояние ? Где оно применяется ? Зачем ?

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


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

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

А при чем тут вообще шины и SPI? 

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


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

Только что, HardRock сказал:

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

А при чем тут вообще шины и SPI? 

Вот и я задаюсь этим вопросом. Потому-что:

Цитата

 

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

При этом если сделать inout reg то "Error (12014): Net "test", which fans out to "x", cannot be assigned more than one value" если читать писать в разных модулях. А с третьим состоянием не получилось - не работает.

 

inout - это и есть двунаправленная шина. Т.е шина с Z состоянием.

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


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

Ну да, согласен, затупил. Если модуль отключен от шины то ему наверно  не получится выставить уровень извне.

Попробовал передавать несколько байт за сессию. Объявил массив

reg   [7:0]   memory[4];

  always @(posedge clk) begin
    // - Drop interrupt set requests
    for (i = 0; i < 4; i = i + 1) begin
      if (spi_ivt_reset[i]) spi_ivt_reset[i] <= 0;
    end
    
    // - IRQ_IO_START
    if (spi_ivt_status[1]) begin
      memory[0] <= 8'hDE;
      memory[1] <= 8'hAD;
      memory[2] <= 8'hBE;
      memory[3] <= 8'hEF;
    
      id <= 0;
      spi_tx_buffer <= memory[id];
      spi_ivt_reset[1] <= 1'b1;
    end
    
    // - Wait IRQ_IO_BYTYE
    if (spi_ivt_status[0]) begin    
      if (spi_rx_buffer == 8'h55) begin
        id <= id + 1;
        spi_tx_buffer <= memory[id];
      end else begin
        spi_tx_buffer <= spi_rx_buffer;
      end
      
      spi_ivt_reset[0] <= 1'b1;
    end
    
  end

Приходит EF DE BE AD. Почему?

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


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

7 часов назад, HardRock сказал:

Ну да, согласен, затупил. Если модуль отключен от шины то ему наверно  не получится выставить уровень извне.

Попробовал передавать несколько байт за сессию. Объявил массив


reg   [7:0]   memory[4];

  always @(posedge clk) begin
    // - Drop interrupt set requests
    for (i = 0; i < 4; i = i + 1) begin
      if (spi_ivt_reset[i]) spi_ivt_reset[i] <= 0;
    end
    
    // - IRQ_IO_START
    if (spi_ivt_status[1]) begin
      spi_ivt_reset[1] <= 1'b1;
    end
    
    // - Wait IRQ_IO_BYTYE
    if (spi_ivt_status[0]) begin    
      spi_ivt_reset[0] <= 1'b1;
    end
    
  end

 

Как, по-вашему, это должно работать?

Добрый совет: отвыкайте от програмистского стиля. Не запутывайте ни себя, ни тех, кто будет читать ваш код. Пишите явно взаимоисключающие условия.
 

if ()

else if ()

else

 

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


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

5 часов назад, andrew_b сказал:

Как, по-вашему, это должно работать?

Добрый совет: отвыкайте от програмистского стиля. Не запутывайте ни себя, ни тех, кто будет читать ваш код. Пишите явно взаимоисключающие условия.

По задумке:

1. Цикл разворачивается в параллельный сброс всех выставленных бит в spi_ivt_reset

2. Дальше если выставлены биты статусе IRQ_IO_START / IRQ_IO_BYTE, то в spi_ivt_reset пишется 1. 

 

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

Мне нужно занулить значение в spi_ivt_reset чтобы сбросить снова т.к. триггер сброса реагирует на изменение 0-1.

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

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


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

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

59 minutes ago, HardRock said:

1. Цикл разворачивается в параллельный сброс всех выставленных бит в spi_ivt_reset

2. Дальше если выставлены биты статусе IRQ_IO_START / IRQ_IO_BYTE, то в spi_ivt_reset пишется 1. 

Если вы хотите это в одном такте сделать   то либо пишите битовую комбинаторную логику  

logic [3:0] spi_ivt_reset;

always @(posedge clk) begin
  spi_ivt_reset <='0;
  spi_ivt_reset[0] <= spi_ivt_status[1] || spi_ivt_status[0];
  ...
end

 

59 minutes ago, HardRock said:

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

Либо "запутывайте себя программистским стилем"  и  блокирующими присвоениями.  Хотя и при этом вам не стоит забывать что этой "программой" вы описываете  поведение вашей схемы  во времени. И надо понимать во что такая конструкция выльется при синтезе.  

Удачи! Rob.

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


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

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

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

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

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

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

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

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

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

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