Аппаратный сортировщик чисел на verilog-е
Определяем модуль всей цепочки. у него есть входы, выходы и внутренние модули, всё это надо связать в единую сеть с помощью комбинационной схемы. Регистров тут в явном виде нет, они инкапсулированы внутри сортировочных ячеек. // Заголовок module Sorting_Stack (clk, hold, is_input, data_in, data_out);
// Числовые параметры parameter HBIT= 15; // size of number in bits parameter R_SZ= 256; // capacity, max sequence size parameter _R_SZ= (R_SZ+1)/2; // not to modify
// Перечисляем входы-выходы схемы // Тактовый сигнал input clk; … // входы-выходы данных input [HBIT:0] data_in; // load one number per clock output [HBIT:0] data_out; // while is_input==0, max value popping out here // в квадратных скобках перед именем — разрядность … // Промежуточные точки схемы wire [HBIT:0] in_prev[_R_SZ]; wire [HBIT:0] in_next[_R_SZ]; wire [HBIT:0] out[_R_SZ]; // в квадратных скобках после имени — размерности массивов
// Внутренние подмодули модуля // Здесь определён целый массив подмодулей // storage Cell_Compare #(HBIT) ribbon[_R_SZ] (clk, hold, is_input, in_prev, in_next, out); Рассмотрим подробнее подстановку одного модуля в другойCell_Compare — тип модуля#(HBIT) — установка числовых параметровribbon — имя экземпляра[_R_SZ] — это массив, указываем количество (clk, hold, is_input, — общие сигналы для всехin_prev, in_next, out); — специфические сигналы для каждого элемента массива.Далее generate — полезная конструкция, которая позволяетприменять циклы и т.д. при описании комбинационных схем.
// Соединяем некоторые точки схемы generate genvar i, j; for (i=0; i<_R_SZ-1; i=i+1) begin : block_name01 assign in_prev[i+1]= out[i]; assign in_next[i]= out[i+1]; end assign in_prev[0]= data_in; assign data_out= out[0]; assign in_next[_R_SZ-1]= 0; endgenerate
endmodule Теперь модуль сортировочной ячейки. module Cell_Compare (clk, hold, is_input, in_prev, in_next, out);
parameter HBIT= 15;
input clk; input hold;
input is_input;
input [HBIT:0] in_prev; input [HBIT:0] in_next; Здесь начинается собственно логика сортировки. Каждая ячейка хранит два числа, одно меньше другого. Каждый такт (если не hold) ячейка сравнивает число, пришедшее от соседа со своим «чемпионом». Чемпион — максимальное число при записи и минимальное при чтении. Проигравший покинет ячейку на следующем такте. В результате, данные движутся по цепочке сначала в одном направлении, потом в другом. Количество чисел, помещающихся в устройстве равно удвоенному количеству ячеек.Вернёмся к исходному коду. Здесь при описании выхода применён селектор, (скомпилируется в мультиплексор). is_input определяет, читаем мы данные или пишем, от него зависит, в каком направлении движутся данные по цепочке.
output [HBIT:0] out= is_input? lower: higher;
// Хранилище. Это все регистры, что есть в сортировщике bit [HBIT:0] higher; bit [HBIT:0] lower;
// Здесь определяется, что будет в хранилище на следующем такте. // В зависимости от направления движения данных, это будет // либо higher и in_prev (lower выталкивается к хвосту), // либо lower и in_next (higher выталкивается к голове) wire [HBIT:0] cand_h= is_input? higher: lower; wire [HBIT:0] cand_l= is_input? in_prev: in_next;
// Далее описывается синхронная логика. // В отличие от комбинационной схемы, от времени не зависещей, // в регистры можно писать только в определённые моменты. always@(posedge clk) if (~hold) begin // Здесь мы наконец сравниваем два числа-кандидата. higher <= ( cand_h >= cand_l) ? cand_h: cand_l; lower <= ( cand_h >= cand_l) ? cand_l: cand_h; end endmodule Всё.