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