Непростая мигалка или Дао счетчика-делителя.

Следовать стандартам в начале обучения обычно сложно. Разобраться бы со свалившимся на голову потоком информации. Перепробовать кучу примеров из различных источников. И заполняется папка Projects кучей проектов с непонятными названиями, то цифирными, то одно слово с разными приставками, этот, вроде, настраивался под Cyclone II, этот вроде под Cyclone III. Заглянешь в папку какого-нибудь проекта, там куча различных файлов, отчетов, резервных копий и где-то среди них затерялся исходник, который все это богатство породил. Раз откопаешь, два откопаешь… на двадцать третий раз появляется желание заранее создавать папку для исходников внутри проекта и складывать их туда, а заодно перестать давать проектам названия "от фонаря".
Хватает на первое время. Появляется в Projects еще проект, другой. Замечаешь, что уже не первый раз пишешь такой кусок кода и появляется желание завести библиотеку под разные полезности.
Библиотека — это хорошо, только не всегда исходник подходит под конкретную задачу. Периодически приходится делать копии файлов и менять в них нужные значения. Можно ли еще что-нибудь в жизни упорядочить, чтобы не хвататься каждый раз за калькулятор и копипасту?
Для эксперимента возьмем, что нибудь простое. Есть такая часто встречающаяся задача при проектировании схем для ПЛИС — получение импульсов необходимой частоты из исходной, которую обеспечивает "кварц" на плате. Делается это при помощи различных схем счетчиков. Проще всего устроены те, которые делят исходную частоту на числа, являющиеся степенью двойки.

module Minimal (input  wire clock_in,
                output wire clock_out);
reg [3:0] c;
assign clock_out = c[3];
always @ (posedge clock_in) c = c + 1;

endmodule



То есть, мы просто прибавляем прибавляем к числу в регистре единицу с каждым тактом и соединяем с выходом старший разряд. Размер регистра задает делитель.
Если нужно поделить на произвольное число, то приходится усложнять схему.

module Minimal (input  wire clock_in,
                output wire clock_out);
reg [4:0]  c;
reg      out;
assign clock_out = out;
always @ (posedge clock_in)
  begin
  c   <=  (c < 17)  ? (c + 1) : 0;
  out <= (c < 17/2) ?    1    : 0;
  end

endmodule



Второй регистр и дополнительные условия понадобились потому, что в данном случае нам не получить ровные и красивые импульсы только со старшего бита. Можем ли мы как-то вернуться к более простому варианту с одним регистром и с одной управляющей конструкцией? Да. Есть способ. Для этого необходимо расположить наше некруглое число в центре диапазона регистра.

module Minimal (input  wire clock_in,
                output wire clock_out);
reg [4:0] c;
assign clock_out = c[4];
always @ (posedge clock_in) c <= (c < 24) ? (c + 1) : 7;
//   c <= (c < (17+((31-17)/2))) ? (c + 1) : ((31-17)/2);
endmodule



Вроде и схему упростили(после синтеза схема занимает меньше логических элементов, чем вариант с двумя регистрами и условиями), но и вычислений прибавилось, а схема, не смотря на меньший размер, потеряла наглядность, да и надоели эти однообразные упражнения с калькулятором. Как можно увековечить нашу аналитическую работу, чтобы не заниматься механической? Ответ — использовать параметры.

/*В этом и следующем примере строка после решетки не является комментарием!*/
module u_clock #(parameter c = 0)
                (input wire clock_in, output wire clock_out);

  function [63:0]  pwr;
    input  [63:0]    n;
    reg    [63:0] v, p;
  begin
    v = 0; p = 0;
    while (v < n) begin v = 2**p; p = p + 1; end
    pwr = p - 1;
  end
  endfunction
  
  parameter a = pwr(c), b = 2**a, x = (b + c)/2, y = (b - c)/2;
  
  reg [(a-1):0] R; assign clock_out = R[a-1];
  
  always @ (posedge clock_in)
            R <= ((R < x) && (R >= y)) ? (R + 1) : y;
  
endmodule


Самый простой способ применения параметров — давать символьные имена числам в модуле, чтобы повысить наглядность кода. Также можно упростить себе жизнь за счет того, что все значения в модуле которые можно менять находятся в одном месте и не надо их выискивать. Ну, а наибольший эффект конечно дает обеспечение доступа к нужным параметрам за счет помещения их в заголовок модуля. В модуле u_clock в заголовок вынесен параметр «c» со значением по умолчанию «0». Это делитель на который наш модуль будет делить исходную частоту. Все остальные параметры получаются расчетным путем. Количество задействованных элементов после синтеза получается таким же как и у модулей в первом и втором примере.
Так выглядит пример использования u_clock в схеме.

module blinker (input wire clock, output wire [2:0] led);
  
  parameter clock_in = 50*(10**6);//Hz
  parameter c_out_div = 2**20;
  parameter w2_div = ((3 * clock_in)/c_out_div)/10;
  parameter w3_div = ((5 * clock_in)/c_out_div)/10;
  parameter w4_div = ((7 * clock_in)/c_out_div)/10;
  
  wire clock_out;
  u_clock #(c_out_div) W1 (clock, clock_out);
  
  u_clock #(w2_div) W2 (clock_out, led[2]);
  u_clock #(w3_div) W3 (clock_out, led[1]);
  u_clock #(w4_div) W4 (clock_out, led[0]);

endmodule


Схема мигает тремя диодами с периодом 0.3, 0.5, 0.7 с.
На сайте журнала «Компоненты и технологии» есть must-read-статья на данную тему. Можно было бы и не писать свою, но там все примеры действий с параметрами очень простые: подставить, сложить, поделить. И не понятно как осуществляются более сложные расчеты. Что бы написать модуль u_clock пришлось дополнительно покопаться в интернетах и поэкспериментировать.
  • +1
  • 03 июля 2011, 23:13
  • digides

Комментарии (11)

RSS свернуть / развернуть
А можно спросить? Я в ПЛИС ни бум-бум, только почитываю матчасть иногда ради интереса. Но.

Вот в самой первой схеме (как и в остальных) в глаза бросается сумматор, который в этой схеме практически бесполезен, ибо того же самого эффекта можно достичь соединив в цепочку 4 триггера, каждый из которых будет делить частоту пополам. Чтобы было так, надо это все самому сваять в этом модуле, или компилятор с оптимизатором до этого и так догадаются?
0
Скорее всего догадаются. Те картинки, которые иллюстрируют примеры в моей статье, сгенерированы так, чтобы максимально соответствовать коду. А оптимизированный вариант выглядит иначе. Он составлен из отдельных однобитных регистров, соединенных между собой логическими функциями. Я пробовал синтезировать схему на основе трех вариантов. Самого первого, параметризируемого модуля(который у меня в конце статьи) и библиотечного модуля счетчика, который предлагает производитель ПЛИС. Количество регистров само собой совпадало и общее количество логических функций тоже совпадало, хотя соединены они были каждый раз по-разному.
0
Использовать аналог счётчика на тригерах конечно можно :) но у него будет проявляться задержка — чем дальше регистр — тем больше задержка и потом сигнал будет с глюками… а так с суматором — всё пучком на один раз… Прибавило или отняло…
0
Интересно а на чем еще можно построить счетчик кроме триггеров? Если делать на логике, то получим сумматор константа плюс 1, который будет впустую тратить LUTы.
0
:) я думаю что счётчик без тригеров построит нельзя. Точнее мне непонятно зачем? Тригеры в ПЛИС есть в каждом элементе… Рекомендую сходить к радиокотам в обучалку… Там именно розжовано построение счётчика схематикой…
0
Перепутал сумматор с счетчиком, при больших модулях счета сумматор дофига ресурсов съест.
0
Сам суматор неберёт много(грубо говоря это один элемент «исключающее или»)… там много берёт схемы переноса…
0
Ну так правильно, а в ПЛИС процентов 25 ячеек с логикой ускоренного переноса, так что ресурс ценный, а если сумматор будет реализовываться на простых, то будет медленный.
0
Понятно, то есть если интересует просто деление частоты на степень двойки, надо написать код так, чтобы просто соединить сколько надо триггеров, строб каждого к выходу предыдущего, как в старой доброй К155ИЕ5. Но так делать нельзя если волнует задержка, и состояние выходов всех триггеров будет нестабильно в момент переключения (см К155ИЕ7 :) ).
0
А кто бы порекомендовал простенькое руководство по той среде разработки Xilinx, которую дают бесплатно? Я вот скачал WebPack, a там е-мое, 4 гига «чего-то», три разных среды разработки, пока нашел ту, на которую собственно дается бесплатная лицензия (ISE, но не EDK и не SDK). И в ней просто горы всего. То есть интересует руководство как создать проект с нуля, добавить то, это нажать сюда, и получится мигающий светодиод, который при наличии девайса можно заливать в железо. Ну и как на симуляторе это запустить. Можно на английском. Девайс идет почтой такой — www.xilinx.com/products/boards-and-kits/AES-S6MB-LX9.htm, судя по всему он со средой разработки должен дружить напрямую.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.