├── README.md ├── arduino_test_code └── test_spi_sorter │ ├── Makefile │ └── test_spi_sorter.ino ├── fast_serial_sort.qpf ├── fast_serial_sort.qsf ├── fast_serial_sort.sv ├── sorting_cell.sv ├── spi_slave_interface.sv └── spi_wrapper_main.sv /README.md: -------------------------------------------------------------------------------- 1 | # fpga_linear_time_sorter 2 | 3 | a parallel sorting algorithm implemented in hardware that sorts data in linear time as it arrives serially 4 | 5 | This algorithm is _fast_, since the sorting itself is done in parallel while the data is being input serially. 6 | It is sorted and ready to be read back immediately following the last SPI write transfer. 7 | 8 | The current project consists of stricly SystemVerilog files and uses no IP cores and is, hence, vendor-independent. 9 | Altera Quartus project files are left as an example implementation. 10 | 11 | ## Tunable Parameters 12 | 13 | * __DATA_WIDTH__ is width of the input value in bits. 14 | While __DATA_WIDTH__ can be anywhere from 1 to N bits, the spi wrapper limits bit values to a multiple of bytes (8, 16, 32) 15 | such that each element can be transferred as a series of 1 or more bytes within an spi frame. 16 | 17 | * __SIZE__ is the maximum number of elements that can be stored in this peripheral. 18 | An input unsorted may be any length of words up to SIZE elements long. 19 | 20 | * __signed and unsigned__ input data, provided that you change the inputs/outputs like so 21 | 22 | __UNSIGNED Version:__ 23 | 24 | module sorting_cell 25 | #(parameter DATA_WIDTH = 8) 26 | ( input logic clk, reset, enable, 27 | input logic prev_cell_data_pushed, 28 | input logic prev_cell_state, 29 | input logic shift_up, 30 | input logic [(DATA_WIDTH-1):0] prev_cell_data, 31 | input logic [(DATA_WIDTH-1):0] new_data, 32 | input logic [(DATA_WIDTH-1):0] next_cell_data, 33 | output logic cell_data_is_pushed, 34 | output logic cell_state, 35 | output logic [(DATA_WIDTH-1):0] cell_data); 36 | 37 | __SIGNED Version:__ 38 | 39 | module sorting_cell 40 | #(parameter DATA_WIDTH = 8) 41 | ( input logic clk, reset, enable, 42 | input logic prev_cell_data_pushed, 43 | input logic prev_cell_state, 44 | input logic shift_up, 45 | input logic signed [(DATA_WIDTH-1):0] prev_cell_data, 46 | input logic signed [(DATA_WIDTH-1):0] new_data, 47 | input logic signed [(DATA_WIDTH-1):0] next_cell_data, 48 | output logic cell_data_is_pushed, 49 | output logic cell_state, 50 | output logic signed [(DATA_WIDTH-1):0] cell_data); 51 | 52 | ## Interface 53 | 54 | Unsorted list data is transferred serially over an SPI interface. 55 | An extra gpio pin (__write__) is devoted to indicate whether or not the interface is being used to write data (the unsorted list) or read data back (the sorted list). An extra pin is also devoted to resetting the peripheral. 56 | The __write__ pin could be removed and the read/write signal could be input as the first SPI transfer, but it's an optimization for later. 57 | 58 | The __reset__ pin must currently be toggled before sending a new input data. 59 | 60 | SPI transfers are performed in __SPI_MODE_0__. 61 | 62 | I mocked up some Arduino code to demo the interface. 63 | 64 | __Note:__ I had to use shielded cable for the MISO, MOSI, and SCK lines to prevent crosstalk. 65 | 66 | ## FPGA Pinouts 67 | 68 | | Pin | type | description | 69 | |-------|--------|--------------------------------------------------------------------------------------------| 70 | | RESET | INPUT | system reset | 71 | | MISO | OUTPUT | (SPI) for outputting the sorted list elements | 72 | | MOSI | INPUT | (SPI) for inputting the unsorted list elements | 73 | | SCK | INPUT | (SPI) clock | 74 | | CS | INPUT | (SPI) chip-select | 75 | | WRITE | INPUT | indicates whether or not spi transfers are inputs (unsorted list) or outputs (sorted list) | 76 | 77 | 78 | ## FPGA Resources 79 | 80 | This isn't the most resource-thrifty project, but it hasn't been optimized as such. 81 | It's current state is intended for clarity and readability. 82 | A 256-length, 8-bit sorter costs approximately 5500 logical elements on an Altera Cyclone IV (or 25% of the total space). 83 | 84 | This project kicked off as a thought experiment in a notebook on a winter airplane ride. 85 | I'm glad to see it through. 86 | -------------------------------------------------------------------------------- /arduino_test_code/test_spi_sorter/Makefile: -------------------------------------------------------------------------------- 1 | # Arduino Makefile. Refer to https://github.com/sudar/Arduino-Makefile 2 | # Teensy Only! 3 | USB_TYPE = USB_SERIAL 4 | 5 | # WARNING: Trailing whitespace will cause options to be read incorrectly. 6 | BOARD_TAG = teensy31 7 | 8 | # Additional user-defined libraries may be added to this list 9 | ARDUINO_LIBS += SPI 10 | 11 | # Default user-defined libraries directory 12 | #USER_LIB_PATH += ../../../teensy_lib 13 | 14 | #BRANCH := $(shell git branch | grep "*") 15 | #BUILD_DATE := $(shell date) 16 | #AUTHOR := $(shell whoami) 17 | # 18 | #CPPFLAGS += -DBRANCH='"$(BUILD_VERSION_MAIN_GIT)"' \ 19 | # -DBUILD_DATE='"$(BUILD_DATE)"' \ 20 | # -DAUTHOR='"$(AUTHOR)"' 21 | 22 | # Teensy explicitly requires Arduino 1.0.x as of Nov 2014 23 | # ARDUINO_DIR should be defined in your Makefile. Otherwise, you may 24 | # define it here. 25 | 26 | # teensy3 and teensy31 explicitly require their own Makefile. 27 | include $(ARDMK_DIR)/Teensy.mk 28 | #include $(ARDMK_DIR)/Arduino.mk 29 | -------------------------------------------------------------------------------- /arduino_test_code/test_spi_sorter/test_spi_sorter.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * \project test_spi 3 | * \author Joshua Vasquez 4 | * \date September 2015 5 | */ 6 | 7 | #include 8 | 9 | #define CS 10 10 | #define WRITE 14 11 | #define FPGA_RESET 15 12 | 13 | const size_t LIST_SIZE = 20; 14 | 15 | /* 16 | uint8_t unsorted_list[LIST_SIZE] = {10, 18, 9, 1, 13, 12, 5, 14, 16, 17, 17 | 19, 8, 20, 2, 4, 11, 5, 6, 3, 7}; 18 | */ 19 | uint8_t unsorted_list[LIST_SIZE] = {10, 18, 9, 1, 13, 12, 5, 14, 5, 17, 20 | 5, 8, 20, 2, 4, 11, 5, 6, 3, 7}; 21 | uint8_t sorted_list[LIST_SIZE]; 22 | 23 | void setup() 24 | { 25 | Serial.begin(115200); 26 | 27 | pinMode(CS, OUTPUT); 28 | digitalWrite(CS, HIGH); 29 | 30 | pinMode(WRITE, OUTPUT); 31 | digitalWrite(WRITE, HIGH); 32 | 33 | pinMode(FPGA_RESET, OUTPUT); 34 | digitalWrite(FPGA_RESET, HIGH); 35 | digitalWrite(FPGA_RESET, LOW); 36 | 37 | SPI.begin(); 38 | SPI.setClockDivider(SPI_CLOCK_DIV4); 39 | delay(1000); 40 | 41 | } 42 | 43 | 44 | void loop() 45 | { 46 | // Put the FPGA in a known state 47 | digitalWrite(FPGA_RESET, HIGH); 48 | delay(1); 49 | digitalWrite(FPGA_RESET, LOW); 50 | 51 | delay(5000); 52 | digitalWrite(WRITE, HIGH); 53 | digitalWrite(CS, LOW); 54 | for (uint8_t unsorted_list_index = 0; unsorted_list_index < LIST_SIZE; 55 | ++unsorted_list_index) 56 | { 57 | SPI.transfer(unsorted_list[unsorted_list_index]); 58 | } 59 | delayMicroseconds(10); 60 | digitalWrite(CS, HIGH); 61 | Serial.println("Unsorted List:"); 62 | for (uint8_t i = 0; i < LIST_SIZE; ++i) 63 | { 64 | Serial.println(unsorted_list[i]); 65 | } 66 | Serial.println(); 67 | digitalWrite(CS, HIGH); 68 | 69 | 70 | delay(1000); 71 | 72 | 73 | /// Read back sorted data. 74 | digitalWrite(WRITE, LOW); 75 | digitalWrite(CS, LOW); 76 | for (uint8_t sorted_list_index = 0; sorted_list_index < LIST_SIZE; 77 | ++sorted_list_index) 78 | { 79 | sorted_list[sorted_list_index] = SPI.transfer(0xff); 80 | } 81 | delayMicroseconds(10); 82 | Serial.println("Sorted List:"); 83 | for (uint8_t i = 0; i < LIST_SIZE; ++i) 84 | { 85 | Serial.println(sorted_list[i]); 86 | } 87 | Serial.println(); 88 | digitalWrite(CS, HIGH); 89 | delay(2000); 90 | } 91 | -------------------------------------------------------------------------------- /fast_serial_sort.qpf: -------------------------------------------------------------------------------- 1 | # -------------------------------------------------------------------------- # 2 | # 3 | # Copyright (C) 1991-2013 Altera Corporation 4 | # Your use of Altera Corporation's design tools, logic functions 5 | # and other software and tools, and its AMPP partner logic 6 | # functions, and any output files from any of the foregoing 7 | # (including device programming or simulation files), and any 8 | # associated documentation or information are expressly subject 9 | # to the terms and conditions of the Altera Program License 10 | # Subscription Agreement, Altera MegaCore Function License 11 | # Agreement, or other applicable license agreement, including, 12 | # without limitation, that your use is for the sole purpose of 13 | # programming logic devices manufactured by Altera and sold by 14 | # Altera or its authorized distributors. Please refer to the 15 | # applicable agreement for further details. 16 | # 17 | # -------------------------------------------------------------------------- # 18 | # 19 | # Quartus II 32-bit 20 | # Version 13.1.0 Build 162 10/23/2013 SJ Web Edition 21 | # Date created = 23:47:12 September 26, 2014 22 | # 23 | # -------------------------------------------------------------------------- # 24 | 25 | QUARTUS_VERSION = "13.1" 26 | DATE = "23:47:12 December 31, 2015" 27 | 28 | # Revisions 29 | 30 | PROJECT_REVISION = "fast_serial_sort" 31 | -------------------------------------------------------------------------------- /fast_serial_sort.qsf: -------------------------------------------------------------------------------- 1 | # -------------------------------------------------------------------------- # 2 | # 3 | # Copyright (C) 1991-2013 Altera Corporation 4 | # Your use of Altera Corporation's design tools, logic functions 5 | # and other software and tools, and its AMPP partner logic 6 | # functions, and any output files from any of the foregoing 7 | # (including device programming or simulation files), and any 8 | # associated documentation or information are expressly subject 9 | # to the terms and conditions of the Altera Program License 10 | # Subscription Agreement, Altera MegaCore Function License 11 | # Agreement, or other applicable license agreement, including, 12 | # without limitation, that your use is for the sole purpose of 13 | # programming logic devices manufactured by Altera and sold by 14 | # Altera or its authorized distributors. Please refer to the 15 | # applicable agreement for further details. 16 | # 17 | # -------------------------------------------------------------------------- # 18 | # 19 | # Quartus II 32-bit 20 | # Version 13.1.0 Build 162 10/23/2013 SJ Web Edition 21 | # Date created = 23:47:12 September 26, 2014 22 | # 23 | # -------------------------------------------------------------------------- # 24 | # 25 | # Notes: 26 | # 27 | # 1) The default values for assignments are stored in the file: 28 | # fast_serial_sort_assignment_defaults.qdf 29 | # If this file doesn't exist, see file: 30 | # assignment_defaults.qdf 31 | # 32 | # 2) Altera recommends that you do not modify this file. This 33 | # file is updated automatically by the Quartus II software 34 | # and any changes you make may be lost or overwritten. 35 | # 36 | # -------------------------------------------------------------------------- # 37 | 38 | 39 | set_global_assignment -name FAMILY "Cyclone IV E" 40 | set_global_assignment -name DEVICE EP4CE22F17C6 41 | set_global_assignment -name TOP_LEVEL_ENTITY spi_wrapper_main 42 | set_global_assignment -name ORIGINAL_QUARTUS_VERSION 13.1 43 | set_global_assignment -name PROJECT_CREATION_TIME_DATE "23:47:12 SEPTEMBER 26, 2014" 44 | set_global_assignment -name LAST_QUARTUS_VERSION 15.0.0 45 | set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files 46 | set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0 47 | set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85 48 | set_global_assignment -name ERROR_CHECK_FREQUENCY_DIVISOR 1 49 | set_global_assignment -name NOMINAL_CORE_SUPPLY_VOLTAGE 1.2V 50 | set_global_assignment -name EDA_SIMULATION_TOOL "ModelSim-Altera (SystemVerilog)" 51 | set_global_assignment -name EDA_TIME_SCALE "1 ps" -section_id eda_simulation 52 | set_global_assignment -name EDA_OUTPUT_DATA_FORMAT "SYSTEMVERILOG HDL" -section_id eda_simulation 53 | set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top 54 | set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top 55 | set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top 56 | set_global_assignment -name STRATIX_DEVICE_IO_STANDARD "2.5 V" 57 | set_location_assignment PIN_T9 -to cs 58 | set_location_assignment PIN_T15 -to mosi 59 | set_location_assignment PIN_F13 -to sck 60 | set_location_assignment PIN_T12 -to miso 61 | set_global_assignment -name POWER_PRESET_COOLING_SOLUTION "23 MM HEAT SINK WITH 200 LFPM AIRFLOW" 62 | set_global_assignment -name POWER_BOARD_THERMAL_MODEL "NONE (CONSERVATIVE)" 63 | set_location_assignment PIN_R8 -to clk 64 | set_location_assignment PIN_C3 -to reset 65 | set_instance_assignment -name IO_STANDARD "3.3-V LVCMOS" -to miso 66 | set_instance_assignment -name IO_STANDARD "3.3-V LVCMOS" -to mosi 67 | set_instance_assignment -name IO_STANDARD "3.3-V LVCMOS" -to sck 68 | set_instance_assignment -name IO_STANDARD "3.3-V LVCMOS" -to cs 69 | 70 | set_global_assignment -name SYSTEMVERILOG_FILE spi_wrapper_main.sv 71 | set_global_assignment -name SYSTEMVERILOG_FILE sorting_cell.sv 72 | set_global_assignment -name SYSTEMVERILOG_FILE fast_serial_sort.sv 73 | set_global_assignment -name SYSTEMVERILOG_FILE spi_slave_interface.sv 74 | set_global_assignment -name SYSTEMVERILOG_FILE spi.sv 75 | set_global_assignment -name VERILOG_INPUT_VERSION SYSTEMVERILOG_2005 76 | set_global_assignment -name VERILOG_SHOW_LMF_MAPPING_MESSAGES OFF 77 | 78 | set_location_assignment PIN_D3 -to write 79 | set_instance_assignment -name IO_STANDARD "3.3-V LVCMOS" -to write 80 | set_location_assignment PIN_D9 -to leds_out 81 | set_instance_assignment -name IO_STANDARD "3.3-V LVCMOS" -to leds_out 82 | set_instance_assignment -name IO_STANDARD "3.3-V LVCMOS" -to reset 83 | set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top -------------------------------------------------------------------------------- /fast_serial_sort.sv: -------------------------------------------------------------------------------- 1 | /** 2 | * cell 3 | * Joshua Vasquez 4 | * December 31, 2015 5 | */ 6 | 7 | /** 8 | * \brief top level architecure 9 | * \details DATA_WIDTH defines the width of the data being stored 10 | * \param enable enables internal logic 11 | * \param write indicates if unsorted data is being written or sorted data is 12 | * being read out while module is enabled. 13 | * \param unsorted_data serial input for unsorted list elements 14 | * \param sorted_data serial output for sorted list elements 15 | */ 16 | module fast_serial_sort 17 | #(parameter DATA_WIDTH = 8, 18 | parameter SIZE = 3) 19 | ( input logic clk, reset, enable, 20 | input logic write, 21 | input logic [(DATA_WIDTH-1):0] unsorted_data, 22 | output logic [(DATA_WIDTH-1):0] sorted_data); 23 | 24 | 25 | // Generate Chain of cell elements. 26 | logic [SIZE-1:0] prev_cell_state; 27 | logic [DATA_WIDTH-1:0] prev_cell_data[SIZE-1:0]; 28 | logic [SIZE-1:0] cell_data_is_pushed; 29 | logic [SIZE-1:0] cell_state; 30 | logic [DATA_WIDTH-1:0] cell_data[SIZE-1:0]; 31 | genvar i; 32 | 33 | generate for (i = 0; i < SIZE; i++) begin: cell_array 34 | if (i == 0) 35 | begin 36 | // Starting cell has parameters fixed to force it to accept the 37 | // the first value. 38 | sorting_cell #(.DATA_WIDTH(DATA_WIDTH)) 39 | cell_instance (.clk(clk), .reset(reset), .enable(enable), 40 | .prev_cell_data_pushed('b0), // pseudo-previous cell acts unpushed 41 | .prev_cell_state('b1), // pseudo-previous cell acts full 42 | .shift_up(~write), 43 | .prev_cell_data('b0), // irrelevant 44 | .new_data(unsorted_data), 45 | .next_cell_data(cell_data[i + 1]), 46 | .cell_data_is_pushed(cell_data_is_pushed[i]), 47 | .cell_state(cell_state[i]), 48 | .cell_data(cell_data[i])); 49 | 50 | end 51 | else if (i == SIZE-1) 52 | // Final cell has parameters that we don't care about. 53 | begin 54 | sorting_cell #(.DATA_WIDTH(DATA_WIDTH)) 55 | cell_instance (.clk(clk), .reset(reset), .enable(enable), 56 | .prev_cell_data_pushed(cell_data_is_pushed[i-1]), 57 | .prev_cell_state(cell_state[i-1]), 58 | .shift_up(~write), 59 | .prev_cell_data(cell_data[i-1]), 60 | .new_data(unsorted_data), 61 | .next_cell_data('b0), // irrelevant 62 | .cell_data_is_pushed(), // irrelevant 63 | .cell_state(cell_state[i]), 64 | .cell_data(cell_data[i])); 65 | end 66 | else 67 | // all other parameters... 68 | begin 69 | sorting_cell #(.DATA_WIDTH(DATA_WIDTH)) 70 | cell_instance (.clk(clk), .reset(reset), .enable(enable), 71 | .prev_cell_data_pushed(cell_data_is_pushed[i-1]), 72 | .prev_cell_state(cell_state[i-1]), 73 | .shift_up(~write), 74 | .prev_cell_data(cell_data[i-1]), 75 | .new_data(unsorted_data), 76 | .next_cell_data(cell_data[i+1]), 77 | .cell_data_is_pushed(cell_data_is_pushed[i]), 78 | .cell_state(cell_state[i]), 79 | .cell_data(cell_data[i])); 80 | end 81 | end endgenerate 82 | 83 | assign sorted_data = cell_data[0]; 84 | endmodule 85 | -------------------------------------------------------------------------------- /sorting_cell.sv: -------------------------------------------------------------------------------- 1 | /** 2 | * sorting_cell 3 | * Joshua Vasquez 4 | * December 31, 2015 5 | */ 6 | 7 | /** 8 | * \brief a single cell unit 9 | * \details DATA_WIDTH defines the width of the data being stored 10 | * \param clk 11 | * \param reset 12 | * \param enable 13 | * \param prev_cell_data_pushed indicates that cell must take prev_cell_data 14 | * \param shift_up indicates that sorting_cell should lock in next_cell_data 15 | * (only used when data (now sorted) is being clocked out) 16 | * \param prev_cell_data 17 | * \param new_data 18 | * \param next_cell_data taken when data (now sorted) is being clocked out 19 | * \param cell_data_is_pushed true if this cell's data is set to be pushed out 20 | * on the next clock cycle 21 | * \param cell_state 0 (EMPTY) or 1 (OCCUPIED) 22 | * \param cell_data 23 | */ 24 | module sorting_cell 25 | #(parameter DATA_WIDTH = 8) 26 | ( input logic clk, reset, enable, 27 | input logic prev_cell_data_pushed, 28 | input logic prev_cell_state, 29 | input logic shift_up, 30 | input logic [(DATA_WIDTH-1):0] prev_cell_data, 31 | input logic [(DATA_WIDTH-1):0] new_data, 32 | input logic [(DATA_WIDTH-1):0] next_cell_data, 33 | output logic cell_data_is_pushed, 34 | output logic cell_state, 35 | output logic [(DATA_WIDTH-1):0] cell_data); 36 | 37 | // Cell State FSM states: 38 | parameter EMPTY = 0; 39 | parameter OCCUPIED = 1; 40 | 41 | logic new_data_fits; 42 | assign new_data_fits = (new_data < cell_data) || (cell_state == EMPTY); 43 | 44 | assign cell_data_is_pushed = new_data_fits & (cell_state == OCCUPIED); 45 | 46 | logic [4:0] priority_vector; 47 | assign priority_vector [4:0] = {shift_up, new_data_fits, prev_cell_data_pushed, 48 | cell_state, prev_cell_state}; 49 | 50 | always_ff @ (posedge clk, posedge reset) 51 | begin 52 | if (reset) 53 | begin 54 | cell_state <= EMPTY; 55 | end 56 | else if (enable) 57 | begin 58 | case (cell_state) 59 | EMPTY: 60 | cell_state <= prev_cell_data_pushed ? 61 | OCCUPIED : 62 | prev_cell_state ? // prev cell OCCUPIED ? 63 | OCCUPIED : 64 | EMPTY; 65 | OCCUPIED: 66 | cell_state <= OCCUPIED; 67 | endcase 68 | end 69 | end 70 | 71 | always_ff @ (posedge clk, posedge reset) 72 | begin 73 | if (reset) 74 | begin 75 | cell_data <= 'b0; 76 | end 77 | else if (enable) 78 | begin 79 | //{shift_up, new_data_fits, prev_cell_data_pushed, cell_state, prev_cell_state} 80 | casez (priority_vector) 81 | 'b0?1??: cell_data <= prev_cell_data; 82 | 'b0101?: cell_data <= new_data; 83 | 'b0?001: cell_data <= new_data; 84 | 'b1????: cell_data <= next_cell_data; 85 | default: cell_data <= cell_data; 86 | endcase 87 | end 88 | else 89 | begin 90 | cell_data <= cell_data; 91 | end 92 | end 93 | 94 | endmodule 95 | -------------------------------------------------------------------------------- /spi_slave_interface.sv: -------------------------------------------------------------------------------- 1 | /** 2 | * synthesizable spi peripheral 3 | * Joshua Vasquez 4 | * September 26 - October 8, 2014 5 | */ 6 | 7 | /** 8 | * \brief an spi slave module that both receives input values and clocks out 9 | * output values according to SPI Mode0. 10 | * \details note that the dataToSend input is sampled whenever the set_new_data 11 | * signal is asserted. 12 | */ 13 | 14 | 15 | module spi_slave_interface 16 | #(parameter DATA_WIDTH = 16) 17 | ( input logic clk, 18 | input logic cs, sck, mosi, 19 | output logic miso, 20 | input logic clear_new_data_flag, 21 | output logic synced_new_data_flag, 22 | input logic [(DATA_WIDTH-1):0] data_to_send, 23 | output logic [(DATA_WIDTH-1):0] synced_data_received); 24 | 25 | 26 | logic new_data_flag; 27 | logic [(DATA_WIDTH-1):0] shift_reg; 28 | logic [(DATA_WIDTH-1):0] data_received; 29 | 30 | logic valid_clk; 31 | 32 | assign valid_clk = cs ? 1'b0 : 33 | sck; 34 | 35 | logic set_new_data; 36 | spi_data_ctrl #(DATA_WIDTH) spi_data_ctrl_instance( 37 | .cs(cs), .sck(sck), 38 | .set_new_data(set_new_data)); 39 | 40 | 41 | always_ff @ (negedge valid_clk, posedge set_new_data) 42 | begin 43 | if (set_new_data) 44 | begin 45 | int i; 46 | for(i = 0; i < DATA_WIDTH; i++) 47 | begin 48 | shift_reg[DATA_WIDTH-(i+1)] <= data_to_send[i]; 49 | end 50 | end 51 | else begin 52 | // Handle Output. 53 | shift_reg[(DATA_WIDTH-1):0] <= (shift_reg[(DATA_WIDTH-1):0] >> 1); 54 | end 55 | end 56 | 57 | 58 | always_ff @ (posedge valid_clk) 59 | begin 60 | // Handle Input. 61 | data_received[(DATA_WIDTH-1):0] <= 62 | (data_received[(DATA_WIDTH-1):0] << 1); 63 | data_received[0] <= mosi; 64 | end 65 | 66 | 67 | assign miso = shift_reg[0]; 68 | 69 | 70 | // Handle external synchronization into output's clock domain. 71 | synchronizer #(DATA_WIDTH) data_synchronizer( 72 | .clk(clk), 73 | .unsynced_data(data_received[DATA_WIDTH-1:0]), 74 | .synced_data(synced_data_received[DATA_WIDTH-1:0])); 75 | 76 | always_ff @ (posedge set_new_data, posedge clear_new_data_flag) 77 | begin 78 | if (clear_new_data_flag) 79 | begin 80 | new_data_flag <= 'b0; 81 | end 82 | else begin 83 | new_data_flag <= 'b1; 84 | end 85 | end 86 | 87 | 88 | 89 | // Synchronize new_data output signal 90 | synchronizer #(1) new_data_synchronizer( 91 | .clk(clk), 92 | .unsynced_data(new_data_flag), 93 | //.unsynced_data(set_new_data), 94 | .synced_data(synced_new_data_flag)); 95 | endmodule 96 | 97 | 98 | 99 | module synchronizer 100 | #(parameter DATA_WIDTH = 16) 101 | ( input logic clk, 102 | input logic [DATA_WIDTH - 1:0] unsynced_data, 103 | output logic [DATA_WIDTH - 1:0] synced_data); 104 | 105 | logic [DATA_WIDTH - 1: 0] synchronizer; 106 | 107 | always_ff @ (posedge clk) 108 | begin 109 | synchronizer[DATA_WIDTH - 1:0] <= unsynced_data[DATA_WIDTH - 1:0]; 110 | synced_data[DATA_WIDTH - 1:0] <= synchronizer[DATA_WIDTH - 1:0]; 111 | end 112 | 113 | endmodule 114 | 115 | 116 | 117 | 118 | /** 119 | * \brief handles when data should be loaded into the spi module and parses 120 | * the first byte received for both the starting address and 121 | * the read/write bit. Starting address is output on the addressOut 122 | * signal. If a write is being signaled by the master, 123 | * the dataCtrl module also asserts the writeEnable signal. Finally, 124 | * the setNewData signal prevents new dat from being written to the 125 | * spi output while data is being sent. 126 | */ 127 | module spi_data_ctrl 128 | #(parameter DATA_WIDTH = 16) 129 | ( input logic cs, sck, 130 | output logic set_new_data); 131 | 132 | logic [10:0] bitCount; 133 | logic byteOut; // indicates one byte has been received 134 | logic byteOutNegEdge; 135 | 136 | 137 | /// byteOut logic: 138 | always_ff @ (posedge sck, posedge cs) 139 | begin 140 | if (cs) 141 | begin 142 | bitCount <= 'b0; 143 | byteOut <= 'b1; 144 | end 145 | else 146 | begin 147 | bitCount <= bitCount + 'b1; 148 | // AND all the lower bits together. 149 | byteOut <= &bitCount[$clog2(DATA_WIDTH)-1:0]; 150 | //byteOut <= byteOutTmp; 151 | 152 | end 153 | end 154 | 155 | /// byteOutNegEdge logic: 156 | always_ff @ (negedge sck, posedge cs) 157 | begin 158 | if (cs) 159 | byteOutNegEdge <= 'b1; 160 | else 161 | byteOutNegEdge <= byteOut; 162 | end 163 | 164 | always_latch 165 | begin 166 | if (byteOutNegEdge) 167 | begin 168 | set_new_data<= byteOut; 169 | end 170 | end 171 | 172 | endmodule 173 | -------------------------------------------------------------------------------- /spi_wrapper_main.sv: -------------------------------------------------------------------------------- 1 | /** 2 | * fast_serial_sort demo with an SPI wrapper 3 | * Joshua Vasquez 4 | * December 31, 2015 5 | */ 6 | 7 | /** 8 | * \brief spi wrapper to test with a micocontroller 9 | * \details DATA_WIDTH defines the width of the data being stored (should 10 | * be a power of 2 in this case so it fits in an SPI frame) 11 | */ 12 | module spi_wrapper_main 13 | #(parameter DATA_WIDTH = 8, 14 | parameter SIZE = 20) 15 | ( input logic clk, reset, 16 | input logic sck, mosi, cs, 17 | input logic write, 18 | output logic miso); 19 | 20 | logic enable; 21 | logic [DATA_WIDTH-1:0] unsorted_data; 22 | logic [DATA_WIDTH-1:0] sorted_data; 23 | 24 | fast_serial_sort #(.DATA_WIDTH(DATA_WIDTH), 25 | .SIZE(SIZE)) 26 | fast_serial_sort_inst(.clk(clk), .reset(reset), 27 | .enable(enable), 28 | .write(write), 29 | .unsorted_data(unsorted_data), 30 | .sorted_data(sorted_data)); 31 | 32 | // SPI slave logic 33 | logic [1:0] new_data_pulse_gen; 34 | logic new_data; 35 | 36 | logic [DATA_WIDTH-1:0] spi_data_received; 37 | 38 | assign enable = new_data_pulse_gen[1] & ~new_data_pulse_gen[0]; 39 | 40 | 41 | /// Clear new data as soon as it arrives to prevent it from being 42 | /// continuously loaded into the buffer. 43 | always_ff @ (posedge clk, posedge reset) 44 | begin 45 | if (reset) 46 | begin 47 | new_data_pulse_gen[1:0] <= 'b0; 48 | end 49 | else begin 50 | new_data_pulse_gen[0] <= new_data_pulse_gen[1]; 51 | new_data_pulse_gen[1] <= new_data; 52 | end 53 | end 54 | 55 | spi_slave_interface #(DATA_WIDTH) 56 | spi_inst(.clk(clk), 57 | .cs(cs), 58 | .sck(sck), 59 | .mosi(mosi), 60 | .miso(miso), 61 | .clear_new_data_flag(enable), 62 | .synced_new_data_flag(new_data), 63 | .data_to_send(sorted_data), 64 | .synced_data_received(unsorted_data)); 65 | 66 | endmodule 67 | --------------------------------------------------------------------------------