// NOTE: The first byte is used for syncing due to using different clock domains `define SYNC_2FF module spi_interface( input logic rst, input logic sys_clk, input logic mosi, input logic cs, input logic sclk, input logic rx_ready, input logic tx_valid, input logic [7:0] tx_byte, input logic [1:0] tx_src, input logic [1:0] packet_size, output logic miso, output logic tx_ready, output logic rx_valid, output logic [7:0] rx_byte, output logic [7:0] rx_dest, output logic [7:0] rx_cmd, output logic rx_cmd_valid); timeunit 1ns; timeprecision 1ps; // SPI logic logic sclk_rising_edge; logic sclk_falling_edge; async_get_clk_edges sync (.rst(rst), .ext_clk(sclk), .sys_clk(sys_clk), .clk_rising_edge(sclk_rising_edge), .clk_falling_edge(sclk_falling_edge)); int bit_cnt = 0; logic [7:0] rx_shift; logic [7:0] tx_shift = 8'b00101010; logic [7:0] rx_buff = '0; logic byte_ready = 0; always_ff @ (posedge sclk_rising_edge or posedge rst) begin if (rst) begin rx_shift <= '0; rx_buff <= '0; bit_cnt <= '0; byte_ready <= 0; end else begin if (cs) begin rx_shift <= 0; rx_buff <= 0; bit_cnt <= 0; end else begin rx_shift <= {rx_shift[6:0], mosi}; bit_cnt <= bit_cnt + 1; if (bit_cnt == 7) begin bit_cnt <= 0; rx_buff <= {rx_shift[6:0], mosi}; byte_ready <= 1; end else byte_ready <= 0; end // else: !if(cs) end // else: !if(rst) $display("[%0d] current rx_shift: %b", $time, rx_shift); $display("[%0d] current bit_cnt: %0d", $time, bit_cnt); $display("[%0d] current rx_buff: %b", $time, rx_buff); end // always_ff @ (posedge sclk) always_ff @ (posedge sclk_falling_edge) begin if (rst) begin tx_shift <= 0; end else begin if (cs) begin tx_shift <= 0; end else begin if (bit_cnt == 0) begin tx_shift <= rx_buff[7:0]; end else begin tx_shift <= {tx_shift[6:0], 1'b0}; end end end // else: !if(rst) $display("last bit sent: %b", miso); $display("[%0d] current tx_shift: %b", $time, tx_shift); $display("-----------------------------------------"); end // always_ff @ (negedge sclk) assign miso = tx_shift[7]; // RX and TX logic logic [9:0] rx_queue_head = 0; logic [9:0] rx_queue_tail = 0; logic [10:0] rx_size = 0; logic rx_queue_write = 0; logic [7:0] rx_read; logic [7:0] dest_read; logic packet_sending; logic rx_queue_empty; assign rx_size = (rx_queue_tail + 11'd1024 - rx_queue_head) & 11'h3FF; assign rx_queue_empty = ~(|rx_size); rx_queue_bram rx_queue (.sys_clk(sys_clk), .write_enable(rx_queue_write), .read_addr(rx_queue_head), .write_addr(rx_queue_tail), .write_data(rx_buff), .read_data(rx_read), .read_dest(dest_read)); always_ff @ (posedge sys_clk) begin if (rst) begin rx_queue_head <= '0; rx_queue_tail <= '0; rx_queue_write <= '0; rx_read <= '0; packet_sending <= 0; end else begin if (byte_ready) rx_queue_write <= 1; if (rx_queue_write) begin rx_queue_write <= 0; rx_queue_tail <= rx_queue_tail + 1; end if (!packet_sending) begin if (rx_size > 2 && rx_ready) begin rx_byte <= rx_read; rx_dest <= dest_read; rx_valid <= 1; end else rx_valid <= 0; end else begin if (is_packet_complete(rx_queue_head, packet_size)) packet_sending <= 0; else if (rx_size > 0) begin rx_byte <= rx_read; rx_dest <= dest_read; rx_valid <= 1; end end end end // always_ff @ (posedge sys_clk) logic [13:0] tx_queue_head; logic [13:0] tx_queue_tail; endmodule // spi_interface module async_get_clk_edges( input logic rst, input logic ext_clk, input logic sys_clk, output logic clk_rising_edge, output logic clk_falling_edge); timeunit 1ns; timeprecision 1ps; `ifdef SYNC_2FF logic sync_0 = 0; logic sync_1 = 0; always_ff @ (posedge sys_clk) begin if (rst) begin sync_0 <= 0; sync_1 <= 0; end else begin sync_0 <= ext_clk; sync_1 <= sync_0; end end assign clk_rising_edge = sync_0 & ~sync_1; assign clk_falling_edge = ~sync_0 & sync_1; `else // !`ifdef SYNC_2FF logic [2:0] clk_sync = 0; always_ff @ (posedge sys_clk) begin if (rst) clk_sync <= {clk_sync[1:0], ext_clk}; end assign clk_rising_edge = (clk_sync[2:1] == 2'b01); assign clk_falling_edge = (clk_sync[2:1] == 2'b10); `endif // !`ifdef SYNC_2FF endmodule // async_get_clk_edges module rx_queue_bram ( input logic sys_clk, input logic write_enable, input logic [9:0] read_addr, input logic [9:0] write_addr, input logic [7:0] write_data, output logic [7:0] read_data, output logic [7:0] read_dest); timeunit 1ns; timeprecision 1ps; logic [7:0] mem [1023:0]; always_ff @ (posedge sys_clk) begin if (write_enable) mem[write_addr] <= write_data; read_data <= mem[read_addr]; read_dest <= mem[read_addr + 1]; end endmodule // rx_queue_bram module tx_queue_bram(input logic sys_clk, input logic write_enable, input logic [13:0] read_addr, input logic [13:0] write_addr, input logic [7:0] write_data, output logic [7:0] read_data); timeunit 1ns; timeprecision 1ps; logic [7:0] mem [16 * 1023:0]; always_ff @ (posedge sys_clk) begin if (write_enable) mem[write_addr] <= write_data; read_data <= mem[read_addr]; end endmodule // tx_queue_bram function automatic logic is_packet_complete(input logic [9:0] head, input logic [1:0] packet_size); case(packet_size) 2'b00: return &(head & 'd64); 2'b01: return &(head & 'd128); 2'b10: return &(head & 'd256); 2'b11: return &head; endcase // case (packet_size) endfunction // packet_complete