Хотя нет, история начинается еще с 2015 года, когда я познакомился с ПЛИС. В своих первых простеньких работах я формировал нужный мне клок из счетчика и запитывал от него всю логику(естественно при условии что клок мне нужен медленнее чем подавался на ПЛИС, например UART и SPI). Естественно за такое меня гоняли, но у меня была простая отмазка но ведь работает же!, и действительно все работало. С тех пор у меня в голове закралась мысль а откуда вообще можно взять тактирующий сигнал?.
Вариантов источников взять клок не много. Либо взять из некого ClockWizard основанный на PLL или MMCM, либо сформировать из счетчика, либо сразу с ножки так сказать single ended. А что, если взять тактовый сигнал сформированный примитивом ПЛИС?
В рамках этой статьи я решил рассмотреть три варианта: мультиплексор(MUXF7), таблица истинности(LUT1) и замкнуть ножки ПЛИС сами на себя.
В случае с мультиплексором выход подаем на управляющий сигнал, а входные сигналы притягиваем к 0 и 1.
В случае с LUT замыкаем выход на вход и задаем инвертирующую таблицу истинности. При подаче 1 выдавать ноль, а при 0 выдавать единицу.
В случае с GPIO там все просто, выходному сигналу присваиваем инверсию входного:
assign s2 = ~s1; |
Цель эксперимента: сгенерировать частоту тремя способами и замерить ее.
Измерять частоту будем за счет счетчиков. Будет 4 счетчика: три на каждый вариант и один счетчик базовый, относительно которого все будет считаться. А смотреть эти счетчики будем через ChipScope.
А вот собственно весь код модуля:
module gen_clk( input clk_base, input s1, //gpio output s2 //gpio );//счетчик на входных-выходных контактахassign s2 = ~s1;wire clk_gpio = s1;reg [31:0] cnt_gpio = 0; (* MARK_DEBUG="true" *) reg [31:0] cnt_gpio_buf = 0;always@(posedge clk_gpio)begin if(cnt_gpio[2:0]==3'd0) cnt_gpio_buf<=cnt_gpio; cnt_gpio <= cnt_gpio + 1'b1;end//счетчик на мультиплексореwire clk_mux;MUXF7 MUXF7_inst( .O(clk_mux), .I0(1'b1), .I1(1'b0), .S(clk_mux));reg [31:0] cnt_mux = 0; (* MARK_DEBUG="true" *) reg [31:0] cnt_mux_buf = 0;always@(posedge clk_mux)begin if(cnt_mux[2:0]==3'd0) cnt_mux_buf<=cnt_mux; cnt_mux <= cnt_mux + 1'b1;end//счетчик на одном лутеwire clk_lut;LUT1#( .INIT(2'b01))LUT1_inst( .O(clk_lut), .I0(clk_lut));reg [31:0] cnt_lut = 0; (* MARK_DEBUG="true" *) reg [31:0] cnt_lut_buf = 0;always@(posedge clk_lut)begin if(cnt_lut[2:0]==3'd0) cnt_lut_buf<=cnt_lut; cnt_lut <= cnt_lut + 1'b1;end//базовый счетчик относительно которого будем считать (* MARK_DEBUG="true" *) reg [31:0] cnt_base = 'd0; always@(posedge clk_base)begin cnt_base <= cnt_base + 1'b1;end endmodule
Вот схематик проекта. В круг обведены примитивы, а стрелками указаны сигнал которые будут внесены в ChipScope для анализа частоты:
Практическая часть
В моем распоряжении есть три платы:
- KC705 Evaluation Kit
- ML507 Evaluation Kit
- Китайская плата Spartan-6 XC6SLX16
Забегая впередЗабегая вперед, скажу, что с последней платой не получилось нормального результата.
И так теперь собственно результаты
Kintex-7:
Так как проект начинал делать под него, то и проект был написан не сразу целиком, а поэтапно. Сначала подключил один LUT добавил сигналы в отладку и стал смотреть.
Базовый счетчик тактируется на 200 МГц, поэтому посчитать частоту клока сгенерированного на луте не сложно, во сколько раз больше дельта счетчика лута дельты базового счетчика за одно и тоже время, во столько раз больше его частота. В данном случае: получается частота генерируемая лутом 381.55 МГц.
Теперь к проекту добавим мультиплексор, и по аналогии как с одним лутом посчитаем частоту для него, ну и для лута (должно ведь что-то поменяться).
Первое на что бросается взгляд это на то, как сильно дребезжит счетчик. Это сказывается огромная частота мультиплексора, но в целом видно, что счетчик идет по нарастающей, а это значит, что его тоже можно взять и посчитать. В итоге:
- Частота на мультиплексоре: 5953.89 МГц
- Частота на луте(изменилась): 379.98 МГц
Ну и на конец добавим к проекту замкнутую петлю из пары GPIO. На плате KC705 есть SMA разъемы J13 и J14. Вот их то я и замкнул проводником длиной примерно 10 см. В результате:
- Частота на GPIO: 90.59 МГц
- Частота на мультиплексоре: 12994.13 МГц
- Частота на луте: 380.18 МГц
Заменим, эксперимента ради, проводник на более длинный, у меня имеется провод в два раза длиннее. В итоге частота упала до 85.29 МГц.
На данном этапе эксперимента можно отметить, что частота работы примитивов в ПЛИС не одинаковая. В случае, когда был только один лут то синтезатор выбрал самый быстрый лут и строил вокруг него схему, затем когда добавился мультиплексор синтезатор попытался найти ту супер позицию где и лут и мультиплексор работают максимально быстро, а это уже другие элемента и частоты уже медленнее. Когда добавились внешние пины то весь проект на кристалле в принципе передислоцировался к этим ножкам и проект стал синтезироваться на близ лежащих элементах, по какой-то причине в том месте частоты лута и мультиплексора заметно выросли, но не стоит забывать что на фоне всего этого к проекту подключён ChipScope глубиной 1024 и шиной данных от 64 до 128(от проекта к проекту меняется). Теперь перейдем к следующей плате.
Virtex-5:
Я не стал проходить весь путь что прошел с предыдущей платой, сразу добавил все 3 варианта генерации клока и посмотрел в ChipScope что получилось.
На рисунке видны две метки Х и О. А так же их значения в столбцах, формат чисел беззнаковый десятичный. Стоит отметить, что базовый счетчик теперь считает на частоте 100 МГц. И так результат:
- Частота на GPIO: 96.34 МГц
- Частота на мультиплексоре: 614.41 МГц
- Частота на луте: 5761.1 МГц
Видно что на этой плате лут оказался быстрее чем мультиплексор, а частота работы пинов оказалась выше чем у первой платы, возможно это потому что я соединил два пина не проводником 10 см а джампером, в итоге линия связи стала короче, а частота выше.
А теперь последний вариант с китайской платой.
Spartan-6:
В ChipScope появилось два базовых счетчика, на самом деле это один и тот же счетчик просто не хотел перенастраивать ChipScope. В данном проекте базовый счетчик тактируется на частоте 50 МГц.
В случае с этой платой все получилось гораздо сложнее. Во-первых, проект никак не хотел синтезироваться в том виде что за синтезировался в предыдущий вариантах. Во-вторых, в итоге пришлось выкинуть LUT, пытался его заменить на пятивходовый но тоже ни чего не дало. В общем вот результаты:
- Частота на GPIO: 51.77 МГц
- Частота на мультиплексоре: 3 490 504 МГц
- Частота на луте: не получилось собрать
Результаты, в исполнении этой платы, оказались совсем уж не радостные, и не только потому что лут не получилось использовать в качестве клока, но и из-за неимоверно огромной частоты мультиплексора. Что касательно клока генерируемого на ножках, то был использован проводник порядка 25-30 см, на конце замкнутый проволочкой, наверняка там образовались паразитные емкости и индуктивности которые и оказали свое влияние на генерацию клока.
Заключение
В целом, получилось сгенерировать тактовые сигналы на различных примитивах, а так же получилось увидеть(на примере Kintex-7), что примитивы имеют разную задержку работы в зависимости от расположения. От себя хочу добавить, что не считаю проведённый эксперимент полностью корректным, например не рассчитывалась разрядность счетчиков, не учитывался перенос сигнала из разных клоковых доменов(хотя я сделал чтобы сигнал в буфере держался несколько тактов), сам ChipScope в идеале нужно убрать и найти другой способ анализировать генерируемую частоту.
Встретившиеся проблемы:
В ходе эксперимента синтезаторы Vivado и
ISE ругались на комбинаторные петли, и трудности развода сигналов.
Эти трудности решаются добавлением парой строк в констрейн:
- set_property ALLOW_COMBINATORIAL_LOOPS TRUE [get_nets -of_objects [get_cells gen_clk_inst/LUT1_inst]]
- NET s1 CLOCK_DEDICATED_ROUTE = FALSE;