Plain 321 April 17, 2020 Posted April 17, 2020 · Report post 2 часа назад, HardRock сказал: spireg, он используется и для приема и для передачи Таков стандарт (Motorola), MISO и MOSI — это просто вход и выход одного сдвигового регистра, чтобы устройства можно было соединять цепочкой. Можно почитать любую бумажку на микроконтроллер, например, PIC — на картинке MSSP всё видно. 2 часа назад, HardRock сказал: работает А не должно было, там же вроде спорное присваивание, я очень редко ПЛИС пользуюсь. Quote Share this post Link to post Share on other sites More sharing options...
andrew_b 23 April 17, 2020 Posted April 17, 2020 · Report post 23 минуты назад, Plain сказал: там же вроде спорное присваивание Там ничего спорного нет. «То есть абсолютно». Quote Share this post Link to post Share on other sites More sharing options...
Nick_K 0 April 17, 2020 Posted April 17, 2020 · Report post 3 hours ago, yes said: а почему никто не советует какой-нибудь HLS - у альтеры же есть своя подобная штука? положим я олдфаг и HLS-ом этим никогда не пользовался. Я пытался вникнуть в эти дебри, но по факту там от "программистского" только язык описания/написания. В остальном те же грабли в виде констрейнов, регистров, гонки сигналлов. Ах да, и там нельзя опустится до регистрового уровня в прямом смысле этой фразы. Можно только пользоваться некими пресетними примитивами и структурами (так, например вся AXI шина обьявляется простым классом, а работа с потоками происходит через переменные) и никаких нюансов. Хотите SPI - обьявляете элемент класса SPI заводите в переменную, которая одновременно служит выходным net'ом с модуля и ведёте куда нужно. Но настроить времянки, типы передачи, количество регистров пайплайна - очень сложно и потребует ещё одной жизни Quote Share this post Link to post Share on other sites More sharing options...
HardRock 0 April 17, 2020 Posted April 17, 2020 · Report post С 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, при этом второй модуль можно считать эту единицу, а потом записать туда нолик и первый модуль увидит его. Так можно? И ещё вопрос: как текущее решение, годное? и как рекомендуете делать "прерывания"? Quote Share this post Link to post Share on other sites More sharing options...
Maverick_ 17 April 17, 2020 Posted April 17, 2020 · Report post псевдокод: if signal_write_1 = 1 then register <= 1 elsif signal_write_0 = 1 then register <= 0 end if оберните в олвейс с клоком и будет Вам " В моём понимании идеальным решением было бы "расшарить" регистр между модулями. В одном модуле в него пишут 1, при этом второй модуль можно считать эту единицу, а потом записать туда нолик и первый модуль увидит его. " Quote Share this post Link to post Share on other sites More sharing options...
HardRock 0 April 17, 2020 Posted April 17, 2020 · Report post Не очевидно как это будет выглядеть. Компилятор требует чтобы хотя бы одной стороны был wire, который нельзя назначить в always. При этом если сделать inout reg то "Error (12014): Net "test", which fans out to "x", cannot be assigned more than one value" если читать писать в разных модулях. А с третьим состоянием не получилось - не работает. Quote Share this post Link to post Share on other sites More sharing options...
Flip-fl0p 4 April 17, 2020 Posted April 17, 2020 · Report post 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). После удачной симуляции дизайна приступаете к написанию констрейнов. Quote Share this post Link to post Share on other sites More sharing options...
HardRock 0 April 17, 2020 Posted April 17, 2020 (edited) · Report post 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. Внутри модуля всё ок, все размерности и циклы работают автоматом, а как быть снаружи? Edited April 17, 2020 by HardRock Quote Share this post Link to post Share on other sites More sharing options...
Flip-fl0p 4 April 17, 2020 Posted April 17, 2020 · Report post Вы понимаете что такое двунаправленные шины ? Что такое Z состояние ? Где оно применяется ? Зачем ? Quote Share this post Link to post Share on other sites More sharing options...
HardRock 0 April 17, 2020 Posted April 17, 2020 · Report post Это состояние когда "провод висит в воздухе", не подключен ни к земле, ни к плюсу. Позволяет отключиться от шины чтобы не мешать другому драйверу изменять состояние шины :) А при чем тут вообще шины и SPI? Quote Share this post Link to post Share on other sites More sharing options...
Flip-fl0p 4 April 17, 2020 Posted April 17, 2020 · Report post Только что, HardRock сказал: Это состояние когда "провод висит в воздухе", не подключен ни к земле, ни к плюсу. Позволяет отключиться от шины чтобы не мешать другому драйверу изменять состояние шины :) А при чем тут вообще шины и SPI? Вот и я задаюсь этим вопросом. Потому-что: Цитата Не очевидно как это будет выглядеть. Компилятор требует чтобы хотя бы одной стороны был wire, который нельзя назначить в always. При этом если сделать inout reg то "Error (12014): Net "test", which fans out to "x", cannot be assigned more than one value" если читать писать в разных модулях. А с третьим состоянием не получилось - не работает. inout - это и есть двунаправленная шина. Т.е шина с Z состоянием. Quote Share this post Link to post Share on other sites More sharing options...
HardRock 0 April 17, 2020 Posted April 17, 2020 · Report post Ну да, согласен, затупил. Если модуль отключен от шины то ему наверно не получится выставить уровень извне. Попробовал передавать несколько байт за сессию. Объявил массив 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. Почему? Quote Share this post Link to post Share on other sites More sharing options...
andrew_b 23 April 18, 2020 Posted April 18, 2020 · Report post 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 Quote Share this post Link to post Share on other sites More sharing options...
HardRock 0 April 18, 2020 Posted April 18, 2020 · Report post 5 часов назад, andrew_b сказал: Как, по-вашему, это должно работать? Добрый совет: отвыкайте от програмистского стиля. Не запутывайте ни себя, ни тех, кто будет читать ваш код. Пишите явно взаимоисключающие условия. По задумке: 1. Цикл разворачивается в параллельный сброс всех выставленных бит в spi_ivt_reset 2. Дальше если выставлены биты статусе IRQ_IO_START / IRQ_IO_BYTE, то в spi_ivt_reset пишется 1. А по факту похоже тут всё это выполняется одновременно и со значениями, полученными на предыдущем такте т.к. использовано неблокирующее присваивание? Мне нужно занулить значение в spi_ivt_reset чтобы сбросить снова т.к. триггер сброса реагирует на изменение 0-1. И как вообще писать когда нужно увеличить счетчик, и сразу использовать его с новым значением? Quote Share this post Link to post Share on other sites More sharing options...
RobFPGA 58 April 18, 2020 Posted April 18, 2020 · Report post Приветствую! 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. Quote Share this post Link to post Share on other sites More sharing options...