├── .gitignore ├── .gitmodules ├── Makefile ├── README.md ├── adc.v ├── adc_model.v ├── bram.v ├── decimator.v ├── fft.gtkw ├── gamma.hex ├── gamma.py ├── mv-dev.pcf ├── pwm.v ├── test_tb.v ├── testtone.hex └── top.v /.gitignore: -------------------------------------------------------------------------------- 1 | yosys.log 2 | nextpnr.log 3 | fft-core 4 | *bin 5 | *vcd 6 | *hex 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "dblclockfft"] 2 | path = dblclockfft 3 | url = https://github.com/ZipCPU/dblclockfft 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PROJ = fft 2 | 3 | DEVICE = hx8k 4 | PIN_DEF = mv-dev.pcf 5 | PACKAGE = tq144:4k 6 | 7 | #DEVICE = up5k 8 | #PIN_DEF = icebreaker.pcf 9 | #PACKAGE = sg48 10 | 11 | BUILD_DIR = . 12 | PI_ADDR = pi@fpga.local 13 | FOMU_FLASH = ~/fomu-flash/fomu-flash 14 | 15 | SEED = 8 16 | 17 | PULSE_TOOL=~/work/kneesonic/tools/gen_pulse.py 18 | FFT_CORE = bimpy.v bitreverse.v butterfly.v convround.v fftmain.v fftstage.v hwbfly.v laststage.v longbimpy.v qtrstage.v shiftaddmpy.v 19 | FFT_SRC = $(addprefix fft-core/, $(FFT_CORE)) 20 | LOCAL_SRC = top.v adc.v pwm.v bram.v decimator.v 21 | SRC = $(FFT_SRC) $(LOCAL_SRC) 22 | 23 | all: fft-core gamma.hex $(PROJ).bin 24 | 25 | gamma.hex: 26 | python3 ./gamma.py 8 27 | 28 | testtone.hex: 29 | # $(PULSE_TOOL) --wave sweep --amp 600 --amp2 600 --freq 62500 --freq2 125000 --sample-rate 2000000 --plot --file testtone.hex --length 2000 30 | $(PULSE_TOOL) --wave sine --amp 600 --freq 31250 --sample-rate 2000000 --file testtone.hex --length 2000 31 | # $(PULSE_TOOL) --wave sine --amp 600 --freq 93750 --sample-rate 2000000 --plot --file testtone.hex --length 2000 32 | # $(PULSE_TOOL) --wave sine --amp 600 --freq 125000 --sample-rate 2000000 --plot --file testtone.hex --length 2000 33 | 34 | # link from Dan's core 35 | fft-core: 36 | make -C dblclockfft 37 | ./dblclockfft/sw/fftgen -f 8 -n 12 -m 12 -k 3 -d fft-core 38 | ln -sf fft-core/cmem_8.hex . 39 | ln -sf fft-core/cmem_16.hex . 40 | ln -sf fft-core/cmem_32.hex . 41 | #ln -sf fft-core/cmem_64.hex . 42 | 43 | debug: testtone.hex 44 | iverilog -DDEBUG -o test test_tb.v adc_model.v $(SRC) 45 | vvp test 46 | gtkwave test.vcd fft.gtkw 47 | 48 | # needs -dsp to infer multiplies 49 | %.json: $(SRC) 50 | yosys -l yosys.log -p 'synth_ice40 -top top -json $@' $^ 51 | 52 | %.asc: %.json $(PIN_DEF) 53 | nextpnr-ice40 -l nextpnr.log --seed $(SEED) --freq 32 --package $(PACKAGE) --$(DEVICE) --asc $@ --pcf $(PIN_DEF) --json $< 54 | 55 | %.bin: %.asc 56 | icepack $< $@ 57 | 58 | prog-fpga: $(BUILD_DIR)/$(PROJ).bin 59 | scp $< $(PI_ADDR):/tmp/$(PROJ).bin 60 | ssh $(PI_ADDR) "sudo $(FOMU_FLASH) -f /tmp/$(PROJ).bin" 61 | 62 | prog-flash: $(BUILD_DIR)/$(PROJ).bin 63 | scp $< $(PI_ADDR):/tmp/$(PROJ).bin 64 | ssh $(PI_ADDR) "sudo $(FOMU_FLASH) -w /tmp/$(PROJ).bin; sudo $(FOMU_FLASH) -r" 65 | 66 | clean: 67 | rm $(PROJ).bin 68 | 69 | .phony: testtone.hex clean 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Demo of Dan's FFT Core 2 | 3 | https://zipcpu.com/dsp/2018/10/02/fft.html 4 | https://github.com/ZipCPU/dblclockfft 5 | 6 | This demo uses a 32 bin 12 bit core generated with this command: 7 | 8 | ./fftgen -f 32 -n 12 -m 12 -k 16 9 | 10 | Runs at 32MHz, with -k 16 fft only runs once per 16 cycles (to reduce number of 11 | multipliers). So throughput is 2MHz. 12 | 13 | Only the first 8 bins are displayed on the LEDs. 14 | 15 | # Build 16 | 17 | Dev board targetted is [here](https://github.com/mattvenn/first-fpga-pcb) 18 | 19 | * run make debug to see trace of a generated sine 20 | * run make prog-fpga to build a bin and copy to dev board plugged to raspi 21 | 22 | # Resource usage 23 | 24 | ## HX8k 25 | 26 | ### ./fftgen -f 64 -n 12 -m 12 27 | 28 | === top === 29 | 30 | Number of wires: 5026 31 | Number of wire bits: 32754 32 | Number of public wires: 5026 33 | Number of public wire bits: 32754 34 | Number of memories: 0 35 | Number of memory bits: 0 36 | Number of processes: 0 37 | Number of cells: 20790 38 | SB_CARRY 4011 39 | SB_DFF 6903 40 | SB_DFFE 301 41 | SB_DFFESR 46 42 | SB_DFFSR 215 43 | SB_DFFSS 1 44 | SB_LUT4 9285 45 | SB_RAM40_4K 28 46 | 47 | ### ./fftgen -f 64 -n 12 -m 12 -k 16 48 | 49 | -k # Sets # clocks per sample, used to minimize multiplies. Also 50 | sets one sample in per i_ce clock (opt -1) 51 | 52 | 97% fpga usage on 8k 53 | 54 | === top === 55 | 56 | Number of wires: 3309 57 | Number of wire bits: 18949 58 | Number of public wires: 3309 59 | Number of public wire bits: 18949 60 | Number of memories: 0 61 | Number of memory bits: 0 62 | Number of processes: 0 63 | Number of cells: 10583 64 | SB_CARRY 1763 65 | SB_DFF 3634 66 | SB_DFFE 301 67 | SB_DFFESR 46 68 | SB_DFFSR 236 69 | SB_DFFSS 1 70 | SB_LUT4 4574 71 | SB_RAM40_4K 28 72 | 73 | 74 | ### ./fftgen -f 32 -n 12 -m 12 -k 16 75 | 76 | Info: Device utilisation: 77 | Info: ICESTORM_LC: 6053/ 7680 78% 78 | Info: ICESTORM_RAM: 20/ 32 62% 79 | Info: SB_IO: 40/ 256 15% 80 | Info: SB_GB: 8/ 8 100% 81 | Info: ICESTORM_PLL: 0/ 2 0% 82 | Info: SB_WARMBOOT: 0/ 1 0% 83 | 84 | === top === 85 | 86 | Number of wires: 2815 87 | Number of wire bits: 15018 88 | Number of public wires: 2815 89 | Number of public wire bits: 15018 90 | Number of memories: 0 91 | Number of memory bits: 0 92 | Number of processes: 0 93 | Number of cells: 8445 94 | SB_CARRY 1377 95 | SB_DFF 2790 96 | SB_DFFE 301 97 | SB_DFFESR 33 98 | SB_DFFSR 181 99 | SB_DFFSS 1 100 | SB_LUT4 3742 101 | SB_RAM40_4K 20 102 | 103 | ### ./fftgen -f 32 -n 12 -m 12 -k 16 -p 6 104 | 105 | using hardware dsps on up5k 106 | 107 | Info: ICESTORM_LC: 3092/ 5280 58% 108 | Info: ICESTORM_RAM: 14/ 30 46% 109 | Info: SB_IO: 40/ 96 41% 110 | Info: SB_GB: 8/ 8 100% 111 | Info: ICESTORM_PLL: 0/ 1 0% 112 | Info: SB_WARMBOOT: 0/ 1 0% 113 | Info: ICESTORM_DSP: 5/ 8 62% 114 | Info: ICESTORM_HFOSC: 0/ 1 0% 115 | Info: ICESTORM_LFOSC: 0/ 1 0% 116 | Info: SB_I2C: 0/ 2 0% 117 | Info: SB_SPI: 0/ 2 0% 118 | Info: IO_I3C: 0/ 2 0% 119 | Info: SB_LEDDA_IP: 0/ 1 0% 120 | Info: SB_RGBA_DRV: 0/ 1 0% 121 | Info: ICESTORM_SPRAM: 0/ 4 0% 122 | 123 | ### ./fftgen -f 64 -n 12 -m 12 -k 16 -p 6 124 | 125 | using hardware dsps on up5k 126 | 127 | Info: ICESTORM_LC: 3809/ 5280 72% 128 | Info: ICESTORM_RAM: 20/ 30 66% 129 | Info: SB_IO: 40/ 96 41% 130 | Info: SB_GB: 8/ 8 100% 131 | Info: ICESTORM_PLL: 0/ 1 0% 132 | Info: SB_WARMBOOT: 0/ 1 0% 133 | Info: ICESTORM_DSP: 6/ 8 75% 134 | Info: ICESTORM_HFOSC: 0/ 1 0% 135 | Info: ICESTORM_LFOSC: 0/ 1 0% 136 | Info: SB_I2C: 0/ 2 0% 137 | Info: SB_SPI: 0/ 2 0% 138 | Info: IO_I3C: 0/ 2 0% 139 | Info: SB_LEDDA_IP: 0/ 1 0% 140 | Info: SB_RGBA_DRV: 0/ 1 0% 141 | Info: ICESTORM_SPRAM: 0/ 4 0% 142 | 143 | ### ./fftgen -f 32 -n 12 -m 12 -k 16 144 | 145 | for now working with this one 146 | 147 | Info: Device utilisation: 148 | Info: ICESTORM_LC: 6762/ 7680 88% 149 | Info: ICESTORM_RAM: 20/ 32 62% 150 | Info: SB_IO: 13/ 256 5% 151 | Info: SB_GB: 8/ 8 100% 152 | Info: ICESTORM_PLL: 1/ 2 50% 153 | Info: SB_WARMBOOT: 0/ 1 0% 154 | 155 | ### fftgen -f 8 -n 12 -m 12 -k 3 -d fft-core 156 | 157 | Info: Device utilisation: 158 | Info: ICESTORM_LC: 3923/ 7680 51% 159 | Info: ICESTORM_RAM: 12/ 32 37% 160 | Info: SB_IO: 13/ 256 5% 161 | Info: SB_GB: 8/ 8 100% 162 | Info: ICESTORM_PLL: 1/ 2 50% 163 | Info: SB_WARMBOOT: 0/ 1 0% 164 | 165 | -------------------------------------------------------------------------------- /adc.v: -------------------------------------------------------------------------------- 1 | /* 2 | Serial ADC reader for [ADS7883](http://www.ti.com/lit/ds/symlink/ads7883.pdf) 3 | 4 | Matt Venn 2017 5 | 6 | ADC starts sample on CS low. First 2 clocks are blank and then sample is 7 | clocked out MSB first. 8 | 9 | */ 10 | `default_nettype none 11 | module adc ( 12 | input wire clk, 13 | input wire reset, 14 | output wire adc_clk, 15 | output wire adc_cs, 16 | output reg ready, 17 | input wire adc_sd, 18 | output reg [11:0] data, 19 | output reg [11:0] max 20 | ); 21 | 22 | initial begin 23 | ready = 0; 24 | data = 0; 25 | max = 0; 26 | end 27 | 28 | // outputs only valid not in reset 29 | assign adc_clk = clk; 30 | assign adc_cs = (cnt == 0 && reset == 0) ? 1 : 0; 31 | 32 | reg [11:0] serial_data = 0; 33 | reg [3:0] cnt = 0; 34 | reg capture = 0; 35 | 36 | always @(posedge clk) begin 37 | 38 | if(reset) begin 39 | 40 | max <= 0; 41 | serial_data <= 12'b0; 42 | cnt <= 0; 43 | data <= 12'b0; 44 | ready <= 0; 45 | 46 | end else begin 47 | 48 | cnt<=cnt+1; 49 | 50 | if(cnt == 0) begin 51 | ready <= 0; 52 | end 53 | 54 | if(cnt > 1 && cnt <= 13) 55 | serial_data <= { serial_data[10:0], adc_sd }; 56 | if(cnt == 0) 57 | serial_data <= 0; 58 | 59 | if(cnt == 14) begin 60 | data <= serial_data; 61 | ready <= 1; 62 | if(serial_data > max) 63 | max <= serial_data; 64 | end 65 | 66 | // ready signal is only valid for 1 clock, used for clock enable 67 | if(cnt == 15) 68 | ready <= 0; 69 | end 70 | end 71 | 72 | `ifdef FORMAL_ADC 73 | // all this mostly from https://zipcpu.com/blog/2017/10/19/formal-intro.html 74 | 75 | // past valid signal 76 | reg f_past_valid = 0; 77 | always @(posedge clk) 78 | f_past_valid <= 1'b1; 79 | 80 | // start in reset 81 | initial restrict(reset); 82 | 83 | // force: clock to change, only allow inputs to change on rising clock 84 | reg f_last_clk = 0; 85 | always @($global_clock) begin 86 | restrict(clk == !f_last_clk); 87 | f_last_clk <= clk; 88 | if (!$rose(clk)) begin 89 | assume($stable(reset)); 90 | end 91 | end 92 | 93 | // check everything is zeroed on the reset signal 94 | always @(posedge clk) 95 | if (f_past_valid) 96 | if ($past(reset)) begin 97 | assert(max == 0); 98 | assert(serial_data == 0); 99 | assert(cnt == 0); 100 | assert(data == 0); 101 | assert(ready == 0); 102 | end 103 | 104 | // ready signal is asserted 105 | // check data doesn't change when the ready signal is given 106 | always @(posedge clk) 107 | if(cnt == 15) begin 108 | assert(ready == 1); 109 | assert(data == $past(serial_data)); 110 | end 111 | 112 | // ready signal only one clock 113 | always @(posedge clk) 114 | if (f_past_valid) 115 | if($past(ready)) 116 | assert(ready == 0); 117 | 118 | // cs signal 119 | always @(posedge clk) 120 | if(!reset && cnt == 0) 121 | assert(adc_cs == 1); 122 | 123 | `endif 124 | 125 | endmodule 126 | -------------------------------------------------------------------------------- /adc_model.v: -------------------------------------------------------------------------------- 1 | /* 2 | model of the ADC that the adc.v core reads 3 | Serial ADC [ADS7883](http://www.ti.com/lit/ds/symlink/ads7883.pdf) 4 | */ 5 | `default_nettype none 6 | module adc_model #( 7 | parameter MAX_LEN = 128, 8 | parameter WIDTH = 12, 9 | parameter SAMPLE_FILE = "sample.list" 10 | ) 11 | ( 12 | input wire clk, 13 | input wire cs, 14 | input wire run, 15 | output done, 16 | output reg sd 17 | ); 18 | 19 | reg [$clog2(MAX_LEN)-1:0] sample_count = 0; 20 | reg [$clog2(WIDTH)-1:0] bit_count = 0; 21 | reg [WIDTH-1:0] sample_data [MAX_LEN-1:0]; 22 | 23 | assign done = state == STATE_IDLE; 24 | 25 | initial begin 26 | if (SAMPLE_FILE) $readmemh(SAMPLE_FILE, sample_data); 27 | sd <= 0; 28 | bit_count <= 0; 29 | sample_count <= 0; 30 | end 31 | 32 | localparam STATE_IDLE = 0; 33 | localparam STATE_WAIT_CS= 1; 34 | localparam STATE_RUN = 2; 35 | 36 | reg [2:0] state = STATE_IDLE; 37 | 38 | always @(negedge clk) 39 | case(state) 40 | STATE_IDLE: begin 41 | sd <= 0; 42 | sample_count <= 0; 43 | bit_count <= 0; 44 | if(run) 45 | state <= STATE_WAIT_CS; 46 | end 47 | 48 | STATE_WAIT_CS: begin // sync to adc reader 49 | if(cs == 1) 50 | state <= STATE_RUN; 51 | end 52 | 53 | STATE_RUN: begin 54 | if(cs == 1 && sample_count < MAX_LEN - 1) begin // reset counters 55 | // $display("sample = %d", sample_data[sample_count]); 56 | bit_count <= 0; 57 | sample_count <= sample_count + 1; 58 | sd <= 0; 59 | end else if (bit_count <= WIDTH ) begin // clock out data on negedge of clock until end of the sample file 60 | bit_count <= bit_count + 1; 61 | if(bit_count == 0) // ADC outputs a leading 0 before the sample data follows 62 | sd <= 0; 63 | else 64 | sd <= sample_data[sample_count][WIDTH-bit_count]; 65 | end else if (sample_count == MAX_LEN - 1) 66 | state <= STATE_IDLE; 67 | end 68 | endcase 69 | 70 | endmodule 71 | -------------------------------------------------------------------------------- /bram.v: -------------------------------------------------------------------------------- 1 | `default_nettype none 2 | module bram 3 | #( 4 | parameter ADDR_W = 9, 5 | parameter DATA_W = 8, 6 | parameter FILE = "", 7 | parameter ZERO = 0 8 | ) 9 | ( 10 | input wire r_clk, 11 | input wire w_clk, 12 | input wire [ADDR_W-1:0] r_addr, 13 | input wire [ADDR_W-1:0] w_addr, 14 | input wire w_en, 15 | input wire r_en, 16 | input wire [DATA_W-1:0] w_data, 17 | output reg [DATA_W-1:0] r_data 18 | ); 19 | 20 | reg [DATA_W-1:0] bram [(1 << ADDR_W)-1:0]; 21 | 22 | integer j; 23 | initial begin 24 | if (FILE) $readmemh(FILE, bram); 25 | if (ZERO) 26 | for(j = 0; j < (2<> BIN_DIV) < 255 ? (abs >> BIN_DIV) : 255; // scale & limit to 255 88 | end 89 | end 90 | end 91 | 92 | // Dan's fft core 93 | wire fft_ce; 94 | wire [WIDTH-1:0] decimated_data; 95 | decimator #(.WIDTH(WIDTH)) decimator_0(.clk(clk_32m), .ce(adc_ready), .data_in(adc_data), .data_out(decimated_data), .new_sample(fft_ce)); 96 | fftmain fft_0(.i_clk(clk_32m), .i_reset(reset), .i_ce(fft_ce), .i_sample({decimated_data, sample_imag}), .o_result({output_real, output_imag}), .o_sync(sync)); 97 | 98 | // pwm and gamma correction for each LED 99 | generate 100 | genvar i; 101 | for(i = 0; i < BINS; i = i + 1) begin 102 | bram #(.ADDR_W(PWM_WIDTH), .DATA_W(PWM_WIDTH), .FILE("gamma.hex")) gamma_LUT (.r_clk(clk_32m), .r_addr(bins[i]), .r_data(corrected_pwm_level[i]), .r_en(1'b1)); 103 | pwm #(.WIDTH(PWM_WIDTH), .INVERT(1'b0)) pwm_inst (.clk(clk_32m), .level(corrected_pwm_level[i]), .pwm(leds[i])); 104 | end 105 | endgenerate 106 | 107 | endmodule 108 | --------------------------------------------------------------------------------