HardRock 0 16 апреля, 2020 Опубликовано 16 апреля, 2020 · Жалоба Всем привет. Начал изучение FPGA, есть ряд вопросов. Девборда с алиэкспресса на циклоне IV (EP4CE6E22C8) IDE: Quartus Prime 19.01 Lite Первым проектом хочу сделать PWM драйвер сервоприводов с управлением по SPI. Часть с ШИМом написал буквально сразу после прочтения "введения в верилог": //! Servo PWM channel module PWMChannel ( input wire in_clk, input wire[12:0] in_width, output wire out_pwm ); //! Clock per 1us `define CLK_PER_US 50 //! PWM cycle counter in usec reg [20:0] cycle = 0; //! Output level reg [1:0] level = 0; always @(posedge in_clk) begin if (cycle < 20000 * `CLK_PER_US) begin cycle <= cycle + 1'b1; // - Detect end of pulse if (cycle > in_width * `CLK_PER_US) level <= 0; end else begin // - Restart cycle cycle <= 0; level <= 1; end end // - Output assign out_pwm = level; endmodule А вот с SPI Slave есть проблемы. Не получается реализовать чтение из FPGA в контроллер. Не компилируется. С записью в FPGA всё в порядке. Примеры реализации SPI смотрел в интернете, но до конца не понимаю почему не компилится. Пробую максимально тупую отправку: module SPISlave( /* --- 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, output wire io_next_byte ); //! Number of bits since start of session reg [3:0] io_bits_count; reg out_bit; // - Detect session start always @(posedge SS) begin io_bits_count <= 0; out_bit <= tx_buffer[0]; end // - Send next bit always @(negedge SCLK) begin io_bits_count <= io_bits_count + 1; out_bit <= tx_buffer[io_bits_count]; end assign MISO = out_bit; endmodule Не компилися Error (10028): Can't resolve multiple constant drivers for net "io_bits_count[3]" at spi_slave.v(26) Error (10029): Constant driver at spi_slave.v(20) Error (10028): Can't resolve multiple constant drivers for net "io_bits_count[2]" at spi_slave.v(26) Error (10028): Can't resolve multiple constant drivers for net "io_bits_count[1]" at spi_slave.v(26) Error (10028): Can't resolve multiple constant drivers for net "io_bits_count[0]" at spi_slave.v(26) Error (10028): Can't resolve multiple constant drivers for net "out_bit" at spi_slave.v(26) Подскажите почему так и как сделать правильно. Нужно реализовать вывод на основе нескольких входных событий, которые разделены во времени, но конечно могут прийти одновременно (наверно поэтому не компилистся?) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Nick_K 0 16 апреля, 2020 Опубликовано 16 апреля, 2020 · Жалоба Типичная ошибка программиста. Вы обьявляете сигнал io_bits_count и пытаетесь использовать его в двух клоковых доменах (если по дизайну). А этого нельзя сделать (как подать в один клоковый порт 2 сигнала). Из-за этого ошибки. Совет - почитайте по цифровому дизайну чего-то толкового, на не как педалить на Verilog "введение в верилог". Нужно понимать что вы делаете и что Verilog - это инструмент описания цифровой техники и он описывает цифровую схему. А не программирует программу. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Flip-fl0p 4 16 апреля, 2020 Опубликовано 16 апреля, 2020 · Жалоба 2 часа назад, HardRock сказал: Хотите научиться работать с FPGA - изучите идеологию построения проектов в FPGA. Изучите что такое синхронный дизайн. Изучите основные временные соотношения, чтобы было понимание что такое Tsetup, Thold, MTBF. Изучите работу с симулятором (Modelsim, ActiveHDL). Изучите идеологию работы с асинхронными доменами. Изучите как правильно строить цифровые автоматы (FSM). И только потом приступайте к реализации протоколов. Начните с малого. Напишите сдвиговые регистры. Попробуйте счетчики описать с различным режимами( синхронная загрузка, с разрешением работы, с синхронным сбросом, с асинхронным сбросом и пр.). Напишите цифровой автомат светофора. Посимулируйте его. Пытаться сразу писать рабочий проект без этих знаний, всё равно что копать яму детской лопаткой вместо экскаватора. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
des00 25 16 апреля, 2020 Опубликовано 16 апреля, 2020 · Жалоба Причем есть книга "ПЛИС. Курс молодого бойца", она переведена на русский, читается легко, осиливается за неделю. Но....никто не читает их начинающих и студентов, как показывает моя практика) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
RobFPGA 27 16 апреля, 2020 Опубликовано 16 апреля, 2020 · Жалоба Приветствую! 24 minutes ago, Flip-fl0p said: пытаться сразу писать рабочий проект без этих знаний, всё равно что копать яму детской лопаткой вместо экскаватора. Ооо, навалились на новенького - тише, а то испугаете его и он уйдет в VHDLщики В Verilog програмировать впо 2 hours ago, Nick_K said: Вы обьявляете сигнал io_bits_count и пытаетесь использовать его в двух клоковых доменах (если по дизайну). А этого нельзя сделать (как подать в один клоковый порт 2 сигнала). Из-за этого ошибки. Если уж и ругаете то ругайте правильно - в Verilog нельзя (для синтеза) делать присвоение одной переменной в разных procedure блоках always. Как и нельзя делать одновременное присвоение одной переменное в assign и в always. А почему - потому что при синтезе невозможно однозначно конвертировать такой код в реальное железо. Правильно будет завести сигнал SS в клок домен SCLK выделить фронт и им уже сбрасывать io_bits_count. Ну или делать асинхронный сброс io_bits_count уровнем сигнала SS. Удачи! Rob. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
gosha-z 2 16 апреля, 2020 Опубликовано 16 апреля, 2020 · Жалоба Если это классический SPI slave, то почему SS - inout? И можно ли считать тут SCLK обычным клоком, ведь он есть не всегда... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Flip-fl0p 4 16 апреля, 2020 Опубликовано 16 апреля, 2020 · Жалоба 47 минут назад, RobFPGA сказал: Ооо, навалились на новенького - тише, а то испугаете его и он уйдет в VHDLщики Лучше сразу человека предупредить, что его ждет на пути освоения ПЛИС. Хотя ничего сложного я тут не вижу . По сути изучить набор простых правил. Понять как языковые конструкции реализуются в железе (понимать где будет LUT , где регистры, где память, где DSP блоки) т.е. просто в общих чертах понимать что пишешь. Всё равно вся работа с ПЛИС упирается в алгоритмы и знания интерфейсов. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Plain 167 16 апреля, 2020 Опубликовано 16 апреля, 2020 · Жалоба Наверное ещё надо сказать, что далеко не всё, что называется *clk, следует так же и понимать, пытаясь использовать напрямую — ведомый SPI вообще на FSM элементарно решается, т.е. интерпретатором на системной частоте, требование к которой в данном случае — заведомо вдвое больше SCLK. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
HardRock 0 16 апреля, 2020 Опубликовано 16 апреля, 2020 · Жалоба 50 минут назад, gosha-z сказал: Если это классический SPI slave, то почему SS - inout? И можно ли считать тут SCLK обычным клоком, ведь он есть не всегда... Скорее опечатка. Хотя изначально хотел сделать универсальный модуль, но потом понял что нужно сделать хотя бы слейв :) 1 час назад, Flip-fl0p сказал: Хотите научиться работать с FPGA - изучите идеологию построения проектов в FPGA. Изучите что такое синхронный дизайн. Изучите основные временные соотношения, чтобы было понимание что такое Tsetup, Thold, MTBF. Изучите работу с симулятором (Modelsim, ActiveHDL). Изучите идеологию работы с асинхронными доменами. Изучите как правильно строить цифровые автоматы (FSM). И только потом приступайте к реализации протоколов. Начните с малого. Напишите сдвиговые регистры. Попробуйте счетчики описать с различным режимами( синхронная загрузка, с разрешением работы, с синхронным сбросом, с асинхронным сбросом и пр.). Напишите цифровой автомат светофора. Посимулируйте его. Пытаться сразу писать рабочий проект без этих знаний, всё равно что копать яму детской лопаткой вместо экскаватора. Интереснее сразу учиться на прикладных задачах. Изучение с базовой теории конечно правильно, но требует слишком много времени. У меня есть некоторый бэкграунд по микроконтроллерам (STM32 / AVR) и весомый по разработке вообще в том числе системной. Есть практическая задача в рамках хобби - нужно сделать железку - многоканальный PMW контроллер с SPI интерфейсом + коммутация входов / выходов + оцифровка протокола похожего на UART чтобы потом забирать из SPI. Конечно на данный момент мне это намного проще собрать из готовых корпусов PWM, аналоговых ключей типа 4066 и взвалить чуть больше работы на МК, но думаю тут плису самое место, осталось самое простое - изучить и сделать. 37 минут назад, Plain сказал: Наверное ещё надо сказать, что далеко не всё, что называется *clk, следует так же и понимать, пытаясь использовать напрямую — ведомый SPI вообще на FSM элементарно решается, т.е. интерпретатором на системной частоте, требование к которой в данном случае — заведомо вдвое больше SCLK. Циклон тактируется на 50МГц, SPI что-то около 1МГц сейчас, в рабочем варианте должен до 8МГц чтобы использовать возможности STM32. SCLK это такт от мастера. На тему FSM, пробовал так: module SPISlave( /* --- 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, output wire io_next_byte ); //! Number of bits since start of session reg [3:0] io_bits_count; reg [1:0] state; initial begin state <= 0; end always @(SS, SCLK) begin case (state) // - IDLE state 0: begin // - Start session if (SS == 1 && SCLK == 0) begin io_bits_count = 0; state = 1; end end // - RUNNING state 1: begin if (SS == 1) begin // - Rising edge if (SCLK == 1) begin rx_buffer <= { rx_buffer[6:0], MOSI }; // - Falling edge end else begin io_bits_count <= io_bits_count + 1'b001; end end else begin state = 0; io_bits_count = 0; end end endcase end assign MISO = tx_buffer[io_bits_count]; endmodule Тоже не работает, ни на прием ни на передачу. С точки зрения "обычного" программиста выглядит корректно - ловим любые изменения SS или SCLK, если SS подняли, а клок пока ну нуле - это начало передачи. Потом если SS поднят и клок поднят - это передний фронт такта, если клок на нуле - это задний фронт такта, переходим к следующему биту и так пока SS не вернут на землю. С предыдущим понятно, нельзя изменять один регистр в двух блоках, что тут не так с точки зрения плиса? Если правильно понимаю, самый "правильный" способ - это тактироваться от системного клока и вручную искать фронты SPI сигнала? Но при этом сложность реализации сильно возрастает... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
des00 25 16 апреля, 2020 Опубликовано 16 апреля, 2020 · Жалоба 17 minutes ago, HardRock said: Тоже не работает, ни на прием ни на передачу. С точки зрения "обычного" программиста выглядит корректно - ловим любые изменения SS или SCLK, если SS подняли, а клок пока ну нуле - это начало передачи. Потом если SS поднят и клок поднят - это передний фронт такта, если клок на нуле - это задний фронт такта, переходим к следующему биту и так пока SS не вернут на землю. С предыдущим понятно, нельзя изменять один регистр в двух блоках, что тут не так с точки зрения плиса? Если правильно понимаю, самый "правильный" способ - это тактироваться от системного клока и вручную искать фронты SPI сигнала? Но при этом сложность реализации сильно возрастает... а с точки зрения ПЛИС разработчика, выглядит ужастно и работать не будет, что вы и имеете факт наблюдать. Рекомендую все же потратить 5 дней на изучение основ. Мало кому интересно на форуме лекции вам писать) ЗЫ. А сложность там не растет, наоборот все намного проще. Опять же с точки зрения ПЛИС разработчика. ЗЗЫ. В гугле SPI slave Verilog code сорцов как у дурака махорки (с). Может все же лучше сначала почитать, чем идти по своим граблям? :) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
HardRock 0 16 апреля, 2020 Опубликовано 16 апреля, 2020 · Жалоба Смотрел примеры из интернета, они либо очень странные либо очень мудреные с поддержкой разных режимов SPI. Так что только свои грабли! :) А можно всётаки в двух словах, без лекций, в чем ключевая ошибка? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
dvladim 0 16 апреля, 2020 Опубликовано 16 апреля, 2020 · Жалоба 1 минуту назад, HardRock сказал: А можно всётаки в двух словах, без лекций, в чем ключевая ошибка? А в двух словах: в комбинационном always меняется state, которого нет в списке чувствительности - в итоге латчи. Это так, для начала. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
RobFPGA 27 16 апреля, 2020 Опубликовано 16 апреля, 2020 · Жалоба Приветствую! Вот смотрите - когда вы пишите always @(...) вы говорите синтезатору что мол хочу сделать схему меняющую свое состояние по событиям заданными в скобках. То есть по вашему always @(SCLK, SS) .. при любом изменении сигнала SS или SCLK . Но в железе нет триггеров с несколькими тактовыми входами CLK. И какой сигнал будет тактовым в вашем случае? То есть вам надо так описать событие чтобы было однозначно понятно что будет клоком для триггеров. Есть несколько типовых шаблонов синтеза для этого - always @(posedge|negedge SCLK) begin if (SS) begin ... синхронная схема по положительному|отрицательному фронту SCLK .. end end always @(posedge|negedge SCLK, posedge|negedge SS) begin if (SS == 1|0) begin ...асинхронный ресет по высокому|низкому уровню SS ... end else begin ... синхронная схема по положительному|отрицательному фронту SCLK .. end end always @(*) begin .. чистая комбинаторная логика или коварный latch end assign ... чистая комбинаторная логика Добавьте к этому правило о присвоение значений переменной только в одном always блоке и считайте что вы постигли 50% таинств написания Verilog кода Вот пример счетчика с асинхронным сбросом и счетом по отрицательному фронту always @(negedge SCLK or posedge SS) besin if (SS==1) begin io_bits_count <= 0; end else begin io_bits_count <= io_bits_count+1; end end Удачи! Rob. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
HardRock 0 16 апреля, 2020 Опубликовано 16 апреля, 2020 · Жалоба Спасибо! Не знал что можно писать posedge|negedge, в описании языка таких конструкций не находил, там просто что "если вам нужно любое изменение, то пишите просто имя, без posedge negedge). Пока сделал так: 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, output wire io_next_byte ); //! Number of bits since start of session reg [3:0] io_bits_count; reg [3:0] state; initial begin io_bits_count = 0; state = 0; end always @(posedge clk) begin case (state) // - IDLE, detect start 0: begin if (SS == 1 && SCLK == 0) begin state <= 1; io_bits_count <= 0; end end // - SS UP, detect rising edge of SCLK, read bit 1: begin if (SS == 1) begin if (SCLK == 1) begin rx_buffer <= { rx_buffer[6:0], MOSI }; state <= 2; end end else begin state <= 0; end end // - Detect falling edge of SCLK, send bit 2: begin if (SS == 1) begin if (SCLK == 0) begin io_bits_count <= io_bits_count + 1; state <= 1; end end else begin state <= 0; end end endcase end assign MISO = tx_buffer[7 - io_bits_count]; endmodule Работает и прием и отправка, но не очень стабильно, иногда слетает синхронизация на 1-2 сессии. С ардуины но SPI дергаю 1 байт (чтение / запись) каждые 500мс. Нужно будет осциллографом посмотреть. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
RobFPGA 27 16 апреля, 2020 Опубликовано 16 апреля, 2020 · Жалоба Приветствую! 3 minutes ago, HardRock said: Работает и прием и отправка, но не очень стабильно, иногда слетает синхронизация на 1-2 сессии. А это уже другие премудрости - построение цифровой схемы Когда вы заводите внешние асинхронные к клоку clk сигналы (SS, SCLK) в синхронную схему обязательно их надо пропустить через синхронизатора (2-3 D триггера подряд) чтобы убрать эффект метастабильности. В железе в отличии от программы - сигнал физически имеет конечную скорость и не всегда успевает долететь до середины Днепра... до разных частей схемы вовремя. И когда вы пишете ... if (SS==1) ... это не значит что проверка идет физически в одном месте - реально может быть десятки/сотни физически разных мест где это строчка реализуется в железе. Поэтому пропуская через синхронизатор вы гарантируте что вся ваша синхронная схема будет получать чистый и один и тот же сигнал. Для этого кстати служать и timing constrain которые вы тоже должны задавать для компиляции в конкретную FPGA. Но это уже к Verilog отношение имеет опосредсвенно. Удачи! Rob. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться