понедельник, 27 апреля 2015 г.

Бабочка БПФ на Verilog

Я продолжаю делить задачу расчета БПФ на части. В наличии уже есть сдвиговый регистр, внутри которого реализовано умножение на оконную функцию. Теперь реализуем бабочку БПФ. Смысл очень прост: бабочка БПФ это есть ДПФ, по скольку основание БПФ кратно степени 2, то БПФ можно разбить на ДПФ (бабочки) по основанию 2 и затем объединить их.
ДПФ2 на pyhon:

    def FSM_BF2(self, i_clk, i_en, o_rd_addr, i_data, o_we, o_data, o_wr_addr, FFT_SIZE):
        LEN = len(i_data)
        MY_LEN = LEN/2
        WIDTH = MY_LEN - 1
        WIDTH_SUM = WIDTH + 1
        
        r_rd_addr = [Signal(modbv(0,0,FFT_SIZE)) for i in range(2)]
        r_wr_addr = Signal(modbv(0,0,FFT_SIZE))

        #r_addr = Signal(modbv(0,0,DEPTH))
        r_we = [Signal(bool(0)) for i in range(3)]
        r_data = Signal(intbv(0,-2**WIDTH, 2**WIDTH))
        r_in_data = Signal(intbv(0,-2**WIDTH, 2**WIDTH))
        r_out_data = [Signal(intbv(0,-2**WIDTH, 2**WIDTH)) for i in range(2)]

        w_sum = Signal(intbv(0, -2**WIDTH_SUM, 2**WIDTH_SUM))
        w_sub = Signal(intbv(0, -2**WIDTH_SUM, 2**WIDTH_SUM))
        @always_comb
        def f_sum():
            w_sum.next = r_in_data + i_data[LEN:MY_LEN].signed()
        @always_comb
        def f_sub():
            w_sub.next = r_in_data - i_data[LEN:MY_LEN].signed()

        @always_comb
        def f_data():
            o_data.next = r_out_data[r_wr_addr[0]]
        @always_comb
        def f_rd_addr():
            o_rd_addr.next = r_rd_addr[0]
        @always_comb
        def f_wr_addr():
            o_wr_addr.next = r_wr_addr
        @always_comb
        def f_we():
            o_we.next = r_we[0]

           
        @always(i_clk.posedge)
        def seq():
            if i_en == 1:
                r_rd_addr[1].next = r_rd_addr[0]
                if r_rd_addr[0] != FFT_SIZE - 1:
                    r_rd_addr[0].next = r_rd_addr[0] + 1
#                if r_rd_addr[0] != r_rd_addr[0].max-1:
#                    r_rd_addr[0].next = r_rd_addr[0] + 1
#
                if r_rd_addr[1] == 1:
                    r_we[0].next = 1
                if r_wr_addr == r_wr_addr.max - 1:
                    r_we[0].next = 0
#
                if r_we[0] == 1:
                    r_wr_addr.next = r_wr_addr + 1

                if r_rd_addr[1][0]  == 0:
                    r_in_data.next = i_data[LEN:MY_LEN].signed()
                else:
                    r_out_data[0].next = w_sum[:1].signed()
                    r_out_data[1].next = w_sub[:1].signed()
            else:
                r_rd_addr[0].next = 0
                r_rd_addr[1].next = 0
                r_out_data[0].next = 0
                r_out_data[1].next = 0
                r_we[0].next = 0
                r_we[1].next = 0
        return  f_sum, f_sub, f_data, f_rd_addr, f_wr_addr, f_we, seq


Поехали сверху вниз:
i_clk - тактовый вход
i_en - в данном случае выполняет функцию синхронного сброса
o_rd_addr  - адрес чтения из памяти
i_data - вход данных (берутся из памяти)
o_data - выходные данные (пишутся в память)
o_we - выход разрешения записи в память
o_wr_addr - адрес записи в память
FFT_SIZE - константа не используется

константы:
LEN - длины входных данных, вида {RE,IM}. В моем случае 32 бита
MY_LEN - длина RE, 16 бит
WIDTH - количество бит для знакового представления числа
WIDTH_SUM - количество бит для представления суммы 2 знаковых чисел

Тут стоить сказать о нюансах. Если посчитать ДПФ8 от синусоиды с амплитудой 1, то мы получим 8. Если ДПФ16 - 16. То есть в зависимости от основания ДПФ меняется его результат. Чтобы результаты не менялись я буду делить на 2 результаты после каждой стадии, что аналогично умножению на 1/N в формуле ДПФ.

r_rd_addr[][2] - регистр адреса чтения из памяти (2 переменных). Первый регистр используется, чтобы выставить адрес на линии для ram, второй регистр (на один такт задержанный первый) используется, чтобы правильно интерпретировать входные данные.
r_wr_addr - регистр адреса записи
r_we[2] - регистр записи в память. Второй бит не используется
r_in_data - регистр входных данных
r_out_data[][2] - выход бабочки по основанию 2
w_sum, w_sub - комбинаторные выходы бабочки

Дальше требует пояснения строка 27: r_out_data[r_wr_addr[0]]. Есть два регистра r_out_data[], мультиплексором управляет нулевой бит r_wr_addr.
[LEN:MY_LEN] в строках 20,23, 57 вытаскивает из входных данных RE

Порт на verilog:

assign fsm_bf2_w_sum = (fsm_bf2_r_in_data + $signed(o_w_dualram_data[32-1:16]));



assign fsm_bf2_w_sub = (fsm_bf2_r_in_data - $signed(o_w_dualram_data[32-1:16]));



assign o_w_bf2_data = fsm_bf2_r_out_data[fsm_bf2_r_wr_addr[0]];



assign o_w_bf2_rd_addr = fsm_bf2_r_rd_addr[0];



assign o_w_bf2_wr_addr = fsm_bf2_r_wr_addr;



assign o_w_bf2_we = fsm_bf2_r_we[0];


always @(posedge i_clk) begin: FFT_FSM_FSM_BF2_SEQ
    if ((r_en_bf2 == 1)) begin
        fsm_bf2_r_rd_addr[1] <= fsm_bf2_r_rd_addr[0];
        if (($signed({1'b0, fsm_bf2_r_rd_addr[0]}) != (16 - 1))) begin
            fsm_bf2_r_rd_addr[0] <= (fsm_bf2_r_rd_addr[0] + 1);
        end
        if ((fsm_bf2_r_rd_addr[1] == 1)) begin
            fsm_bf2_r_we[0] <= 1;
        end
        if (($signed({1'b0, fsm_bf2_r_wr_addr}) == (16 - 1))) begin
            fsm_bf2_r_we[0] <= 0;
        end
        if ((fsm_bf2_r_we[0] == 1)) begin
            fsm_bf2_r_wr_addr <= (fsm_bf2_r_wr_addr + 1);
        end
        if ((fsm_bf2_r_rd_addr[1][0] == 0)) begin
            fsm_bf2_r_in_data <= $signed(o_w_dualram_data[32-1:16]);
        end
        else begin
            fsm_bf2_r_out_data[0] <= $signed(fsm_bf2_w_sum[17-1:1]);
            fsm_bf2_r_out_data[1] <= $signed(fsm_bf2_w_sub[17-1:1]);
        end
    end
    else begin
        fsm_bf2_r_rd_addr[0] <= 0;
        fsm_bf2_r_rd_addr[1] <= 0;
        fsm_bf2_r_out_data[0] <= 0;
        fsm_bf2_r_out_data[1] <= 0;
        fsm_bf2_r_we[0] <= 0;
        fsm_bf2_r_we[1] <= 0;
    end
end