В определенный момент, например при фильтрации, может понадобиться очень большой буфер для хранения отсчетов сигнала, который будет невозможно реализовать за счет регистров. Здесь нам на помощь приходит внутренняя память (RAM) ПЛИС, которую можно превратить в сдвиговый регистр, реализовав модуль обертку над ней.
Писать будем на Python с помощью MyHDL. Начну немного с того: с реализации ROM:
Поставим за правило: все необходимые параметры модуля выдергивать из входных данных. Например, если входной адрес состоит из 2 бит, то максимально можно адресовать 4 слова. Другой вариант применительно для ROM: начальная инициализация памяти массивом. По факту из примера можно выкинуть 2 и 3 строчки - ничего бы не изменилось. Теперь про RAM:
Здесь описана dual port память. В общем случае, наверно, разрядность адресов на двух портах может быть разной. Поэтому глубина памяти (количество ячеек) считается исходя из "худшего" случая. Разрядность данных такая, потому что они считаются знаковыми. На четвертой строчке инициализируем память нулями и на пятой строчке объявляем выходной регистр. Двух портовая память потому что нужно по одному адресу читать и писать в определенное время (так можно сделать и с обычной памятью, но я не стал пробовать). Дальше все в принципе понятно, если нет, то разобраться можно, прочтя страницу проекта MyHDL.
Теперь поехали дальше: сдвиговый регистр или обертка над RAM. Сразу пример кода чего хотим:
Тут вся магия в мультиплексоре на строках 30-34. Фактически w_din обратная связь, которая идет на выход и на вход RAM. Эта обертка сдвигает адреса и ставит запись так, чтобы первый отсчет в один такт помещался в выходной регистр и в память записывалось новое значение на входе, а на следующем такте старое значение переписывалось в ячейку с увеличенным на единицу адресом и так до конца. Теперь можно легко реализовать фильтр как в прошлой статье, используя сдвиговый регистр и ROM с коэффициентами фильтра + добавить умножение с накоплением. Я же в качестве еще одного примера покажу процедуру умножения сдвигового регистра на оконную функцию для БПФ. Благодаря такому ходу можно разделить задачу подготовки данных для БПФ, алгоритм или конечный автомат его подсчета и подготовку данных на выкачку.
Дальше будем разбираться с конечными автоматами на MyHDL
Писать будем на Python с помощью MyHDL. Начну немного с того: с реализации ROM:
def ROM(self, i_clk, i_rst, i_en, i_read_adr, o_data, CONTENT): DEPTH = len(CONTENT) WIDTH = len(o_data) - 1 @always_comb def comb(): o_data.next = CONTENT[i_read_adr] return comb#, seq
Поставим за правило: все необходимые параметры модуля выдергивать из входных данных. Например, если входной адрес состоит из 2 бит, то максимально можно адресовать 4 слова. Другой вариант применительно для ROM: начальная инициализация памяти массивом. По факту из примера можно выкинуть 2 и 3 строчки - ничего бы не изменилось. Теперь про RAM:
def RAM(self, i_clk, i_rst, i_en, i_read_adr, i_write_adr, i_we, i_data, o_data): DEPTH = max(2**len(i_read_adr), 2**len(i_write_adr)) WIDTH = len(i_data) - 1 mem = [Signal(intbv(0, -2**WIDTH, 2**WIDTH)) for i in range(DEPTH)] r_dout = Signal(intbv(0, -2**WIDTH, 2**WIDTH)) @always_comb def exits(): o_data.next = r_dout @always(i_clk.posedge, i_rst.negedge) def seq(): if i_rst == 0: r_dout.next = 0 elif i_en == 1: r_dout.next = mem[i_read_adr] if i_we == 1: mem[i_write_adr].next = i_data return exits, seq
Здесь описана dual port память. В общем случае, наверно, разрядность адресов на двух портах может быть разной. Поэтому глубина памяти (количество ячеек) считается исходя из "худшего" случая. Разрядность данных такая, потому что они считаются знаковыми. На четвертой строчке инициализируем память нулями и на пятой строчке объявляем выходной регистр. Двух портовая память потому что нужно по одному адресу читать и писать в определенное время (так можно сделать и с обычной памятью, но я не стал пробовать). Дальше все в принципе понятно, если нет, то разобраться можно, прочтя страницу проекта MyHDL.
Теперь поехали дальше: сдвиговый регистр или обертка над RAM. Сразу пример кода чего хотим:
def Shift_reg(self, i_clk, i_rst, i_en, i_new_data, i_din, o_active, o_dout, o_addr, DEPTH): WIDTH = len(i_din) - 1 w_din = Signal(intbv(0, -2**WIDTH, 2**WIDTH)) w_dout = Signal(intbv(0, -2**WIDTH, 2**WIDTH)) ### FFT_len = DEPTH ### n = int(np.log2(FFT_len)) r_addr = Signal(intbv(0,0,2**n)) r_we = Signal(bool(0)) ram = self.RAM(i_clk, i_rst, i_en, r_addr, r_addr, r_we, w_din, w_dout) @always(i_clk.posedge, i_rst.negedge) def seq(): if not i_rst: r_we.next = 0 r_addr.next = 0 elif i_en: if i_new_data: r_we.next = 1 r_addr.next = 0 else: if r_addr != r_addr.max - 1: r_addr.next = r_addr + 1 r_we.next = 1 else: r_we.next = 0 @always_comb def comb(): if r_addr == 0: w_din.next = i_din else: w_din.next = w_dout @always_comb def dout(): o_dout.next = w_din @always_comb def active(): o_active.next = r_we @always_comb def addr(): o_addr.next = r_addr return ram, seq, comb, dout, active,addr
def FFT_input(self, i_clk, i_rst, i_en, i_new_data, i_din, o_dout, o_active_write, o_addr, FFT_SIZE): WIDTH = len(i_din) - 1 WINDOW_WIDTH = WIDTH - 1 DEPTH = FFT_SIZE wo_union = Signal(bool(0)) wo_shift_data = Signal(intbv(0,-2**WIDTH, 2**WIDTH)) w_dout = Signal(intbv(0,-2**WIDTH, 2**WIDTH)) wo_addr = Signal(intbv(0,0,FFT_SIZE)) shift = self.Shift_reg(i_clk, i_rst, i_en, i_new_data, i_din, wo_union, wo_shift_data, wo_addr,FFT_SIZE) wo_window = Signal(intbv(0,-2**WIDTH, 2**WIDTH)) W_blackman = np.blackman(DEPTH) W_blackman = W_blackman / max(W_blackman) CONTENT = tuple([int(W_blackman[i]*(2**WINDOW_WIDTH)) for i in range(DEPTH)]) print CONTENT rom = self.ROM(i_clk, i_rst, i_en, wo_addr, wo_window, CONTENT) MULT_WIDTH = 2*len(i_din) - 1 w_mult = Signal(intbv(0,-2**MULT_WIDTH, 2**MULT_WIDTH)) # SHIFT_VAL = np.log2(max(CONTENT)) # print SHIFT_VAL @always_comb def fmult(): w_mult.next = (wo_shift_data*wo_window)# >> (WIDTH-1) @always_comb def fw_dout(): w_dout.next = w_mult[len(i_din)+WINDOW_WIDTH:WINDOW_WIDTH] @always_comb def fo_dout(): o_dout.next = w_dout @always_comb def fo_active_write(): o_active_write.next = wo_union @always_comb def fo_addr(): o_addr.next = wo_addr return shift, rom, fmult, fw_dout, fo_dout, fo_active_write, fo_addr