aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLeonard Kugis <leonard@kug.is>2021-02-11 14:31:02 +0100
committerLeonard Kugis <leonard@kug.is>2021-02-11 14:31:02 +0100
commit52f60a20c9d95ecf67b662589d4a4c1160ce0a2a (patch)
treeb0425a0acd757aa2a33c4db8ccaa897acea409b3
parent266f2ebc6322eeccf85fcc67eccc6d6200014dab (diff)
downloadturboswap-52f60a20c9d95ecf67b662589d4a4c1160ce0a2a.tar.gz
Reimplemented all HDL files
-rw-r--r--hdl/clock_divider.v147
-rw-r--r--hdl/fifo_buffer.v157
-rw-r--r--hdl/sdio_to_spi.v235
-rw-r--r--hdl/spi_controller.v290
-rw-r--r--hdl/turbo_top.v159
5 files changed, 988 insertions, 0 deletions
diff --git a/hdl/clock_divider.v b/hdl/clock_divider.v
new file mode 100644
index 0000000..adffe0e
--- /dev/null
+++ b/hdl/clock_divider.v
@@ -0,0 +1,147 @@
+`timescale 1ns / 1ps
+
+module clock_divider (
+ input wire clk_in,
+ input wire rst_n,
+
+ output wire clk_12mhz,
+ output wire clk_50mhz,
+ output wire clk_sdio,
+ output wire clk_spi
+);
+
+ reg [1:0] div_counter_50mhz;
+ reg [7:0] div_counter_sdio;
+ reg [7:0] div_counter_spi;
+
+ reg clk_50mhz_reg;
+ reg clk_sdio_reg;
+ reg clk_spi_reg;
+
+ reg [7:0] sdio_div_ratio;
+ reg [7:0] spi_div_ratio;
+
+ reg sdio_clk_en;
+ reg spi_clk_en;
+
+ assign clk_12mhz = clk_in;
+ assign clk_50mhz = clk_50mhz_reg;
+ assign clk_sdio = sdio_clk_en ? clk_sdio_reg : 1'b0;
+ assign clk_spi = spi_clk_en ? clk_spi_reg : 1'b0;
+
+ always @(posedge clk_in or negedge rst_n) begin
+ if (!rst_n) begin
+ div_counter_50mhz <= 2'b00;
+ clk_50mhz_reg <= 1'b0;
+ end else begin
+ div_counter_50mhz <= div_counter_50mhz + 1;
+
+ case (div_counter_50mhz)
+ 2'b00: clk_50mhz_reg <= 1'b1;
+ 2'b01: clk_50mhz_reg <= 1'b1;
+ 2'b10: clk_50mhz_reg <= 1'b0;
+ 2'b11: clk_50mhz_reg <= 1'b0;
+ endcase
+ end
+ end
+
+ always @(posedge clk_50mhz_reg or negedge rst_n) begin
+ if (!rst_n) begin
+ div_counter_sdio <= 8'd0;
+ clk_sdio_reg <= 1'b0;
+ sdio_div_ratio <= 8'd1;
+ end else if (sdio_clk_en) begin
+ if (div_counter_sdio >= sdio_div_ratio) begin
+ div_counter_sdio <= 8'd0;
+ clk_sdio_reg <= ~clk_sdio_reg;
+ end else begin
+ div_counter_sdio <= div_counter_sdio + 1;
+ end
+ end else begin
+ div_counter_sdio <= 8'd0;
+ clk_sdio_reg <= 1'b0;
+ end
+ end
+
+ always @(posedge clk_50mhz_reg or negedge rst_n) begin
+ if (!rst_n) begin
+ div_counter_spi <= 8'd0;
+ clk_spi_reg <= 1'b0;
+ spi_div_ratio <= 8'd1;
+ end else if (spi_clk_en) begin
+ if (div_counter_spi >= spi_div_ratio) begin
+ div_counter_spi <= 8'd0;
+ clk_spi_reg <= ~clk_spi_reg;
+ end else begin
+ div_counter_spi <= div_counter_spi + 1;
+ end
+ end else begin
+ div_counter_spi <= 8'd0;
+ clk_spi_reg <= 1'b0;
+ end
+ end
+
+ always @(posedge clk_50mhz_reg or negedge rst_n) begin
+ if (!rst_n) begin
+ sdio_clk_en <= 1'b0;
+ spi_clk_en <= 1'b0;
+ end else begin
+ sdio_clk_en <= 1'b1;
+ spi_clk_en <= 1'b1;
+ end
+ end
+
+ task set_sdio_frequency;
+ input [7:0] divider;
+ begin
+ sdio_div_ratio <= divider;
+ end
+ endtask
+
+ task set_spi_frequency;
+ input [7:0] divider;
+ begin
+ spi_div_ratio <= divider;
+ end
+ endtask
+
+ task enable_sdio_clock;
+ input enable;
+ begin
+ sdio_clk_en <= enable;
+ end
+ endtask
+
+ task enable_spi_clock;
+ input enable;
+ begin
+ spi_clk_en <= enable;
+ end
+ endtask
+
+ function [7:0] calc_sdio_divider;
+ input [7:0] freq_mhz;
+ begin
+ if (freq_mhz == 0)
+ calc_sdio_divider = 8'd0;
+ else
+ calc_sdio_divider = 8'd25 / freq_mhz;
+ end
+ endfunction
+
+ function [7:0] calc_spi_divider;
+ input [7:0] freq_mhz;
+ begin
+ if (freq_mhz == 0)
+ calc_spi_divider = 8'd0;
+ else
+ calc_spi_divider = 8'd25 / freq_mhz;
+ end
+ endfunction
+
+ initial begin
+ sdio_div_ratio = calc_sdio_divider(8'd25);
+ spi_div_ratio = calc_spi_divider(8'd25);
+ end
+
+endmodule \ No newline at end of file
diff --git a/hdl/fifo_buffer.v b/hdl/fifo_buffer.v
new file mode 100644
index 0000000..dd56537
--- /dev/null
+++ b/hdl/fifo_buffer.v
@@ -0,0 +1,157 @@
+`timescale 1ns / 1ps
+
+module fifo_buffer (
+ input wire wr_clk,
+ input wire [7:0] wr_data,
+ input wire wr_valid,
+ output wire wr_ready,
+
+ input wire rd_clk,
+ output wire [7:0] rd_data,
+ output wire rd_valid,
+ input wire rd_ready,
+
+ input wire rst_n,
+
+ output wire fifo_empty,
+ output wire fifo_full,
+ output wire [7:0] fifo_level
+);
+
+ parameter DEPTH = 512;
+ parameter ADDR_WIDTH = 9;
+
+ reg [7:0] mem [0:DEPTH-1];
+
+ reg [ADDR_WIDTH:0] wr_ptr;
+ reg [ADDR_WIDTH:0] wr_ptr_gray;
+ reg [ADDR_WIDTH:0] wr_ptr_gray_sync1;
+ reg [ADDR_WIDTH:0] wr_ptr_gray_sync2;
+
+ reg [ADDR_WIDTH:0] rd_ptr;
+ reg [ADDR_WIDTH:0] rd_ptr_gray;
+ reg [ADDR_WIDTH:0] rd_ptr_gray_sync1;
+ reg [ADDR_WIDTH:0] rd_ptr_gray_sync2;
+
+ wire [ADDR_WIDTH:0] wr_ptr_bin;
+ wire [ADDR_WIDTH:0] rd_ptr_bin;
+ wire [ADDR_WIDTH:0] fifo_count;
+
+ reg wr_en;
+ reg rd_en;
+
+ reg [7:0] rd_data_reg;
+ reg rd_valid_reg;
+
+ assign wr_ready = ~fifo_full;
+
+ always @(posedge wr_clk or negedge rst_n) begin
+ if (!rst_n) begin
+ wr_ptr <= 0;
+ wr_ptr_gray <= 0;
+ wr_en <= 1'b0;
+ end else begin
+ wr_en <= wr_valid && wr_ready;
+
+ if (wr_en) begin
+ mem[wr_ptr[ADDR_WIDTH-1:0]] <= wr_data;
+ wr_ptr <= wr_ptr + 1;
+ end
+
+ wr_ptr_gray <= binary_to_gray(wr_ptr);
+ end
+ end
+
+ always @(posedge wr_clk or negedge rst_n) begin
+ if (!rst_n) begin
+ rd_ptr_gray_sync1 <= 0;
+ rd_ptr_gray_sync2 <= 0;
+ end else begin
+ rd_ptr_gray_sync1 <= rd_ptr_gray;
+ rd_ptr_gray_sync2 <= rd_ptr_gray_sync1;
+ end
+ end
+
+ assign rd_data = rd_data_reg;
+ assign rd_valid = rd_valid_reg;
+
+ always @(posedge rd_clk or negedge rst_n) begin
+ if (!rst_n) begin
+ rd_ptr <= 0;
+ rd_ptr_gray <= 0;
+ rd_en <= 1'b0;
+ rd_data_reg <= 8'h00;
+ rd_valid_reg <= 1'b0;
+ end else begin
+ rd_en <= rd_ready && ~fifo_empty;
+
+ if (rd_en) begin
+ rd_data_reg <= mem[rd_ptr[ADDR_WIDTH-1:0]];
+ rd_valid_reg <= 1'b1;
+ rd_ptr <= rd_ptr + 1;
+ end else if (rd_ready) begin
+ rd_valid_reg <= 1'b0;
+ end
+
+ rd_ptr_gray <= binary_to_gray(rd_ptr);
+ end
+ end
+
+ always @(posedge rd_clk or negedge rst_n) begin
+ if (!rst_n) begin
+ wr_ptr_gray_sync1 <= 0;
+ wr_ptr_gray_sync2 <= 0;
+ end else begin
+ wr_ptr_gray_sync1 <= wr_ptr_gray;
+ wr_ptr_gray_sync2 <= wr_ptr_gray_sync1;
+ end
+ end
+
+ assign wr_ptr_bin = gray_to_binary(wr_ptr_gray_sync2);
+ assign rd_ptr_bin = gray_to_binary(rd_ptr_gray_sync2);
+
+ wire [ADDR_WIDTH:0] fifo_count_wr;
+ assign fifo_count_wr = wr_ptr - gray_to_binary(rd_ptr_gray_sync2);
+
+ wire [ADDR_WIDTH:0] fifo_count_rd;
+ assign fifo_count_rd = gray_to_binary(wr_ptr_gray_sync2) - rd_ptr;
+
+ assign fifo_full = (fifo_count_wr >= DEPTH);
+ assign fifo_empty = (fifo_count_rd == 0);
+
+ reg [7:0] fifo_level_reg;
+ always @(*) begin
+ if (fifo_count_wr > DEPTH)
+ fifo_level_reg = DEPTH;
+ else
+ fifo_level_reg = fifo_count_wr[7:0];
+ end
+
+ assign fifo_level = fifo_level_reg;
+
+ function [ADDR_WIDTH:0] binary_to_gray;
+ input [ADDR_WIDTH:0] binary;
+ begin
+ binary_to_gray = binary ^ (binary >> 1);
+ end
+ endfunction
+
+ function [ADDR_WIDTH:0] gray_to_binary;
+ input [ADDR_WIDTH:0] gray;
+ reg [ADDR_WIDTH:0] binary;
+ integer i;
+ begin
+ binary[ADDR_WIDTH] = gray[ADDR_WIDTH];
+ for (i = ADDR_WIDTH-1; i >= 0; i = i - 1)
+ binary[i] = binary[i+1] ^ gray[i];
+ gray_to_binary = binary;
+ end
+ endfunction
+
+ integer i;
+ initial begin
+ for (i = 0; i < DEPTH; i = i + 1)
+ mem[i] = 8'h00;
+ end
+
+endmodule \ No newline at end of file
diff --git a/hdl/sdio_to_spi.v b/hdl/sdio_to_spi.v
new file mode 100644
index 0000000..6e83ca0
--- /dev/null
+++ b/hdl/sdio_to_spi.v
@@ -0,0 +1,235 @@
+`timescale 1ns / 1ps
+
+module sdio_to_spi (
+ input wire sd_clk,
+ inout wire sd_cmd,
+ inout wire [3:0] sd_dat,
+
+ output reg sd_cmd_dir,
+ output reg [3:0] sd_dat_dir,
+
+ input wire clk_sys,
+ input wire rst_n,
+
+ output reg [7:0] cmd_opcode,
+ output reg [31:0] cmd_address,
+ output reg [15:0] cmd_length,
+ output reg cmd_valid,
+ input wire cmd_ready,
+
+ output reg [7:0] data_out,
+ output reg data_out_valid,
+ input wire data_out_ready,
+
+ input wire [7:0] data_in,
+ input wire data_in_valid,
+ output reg data_in_ready,
+
+ output reg [3:0] sdio_state,
+ output reg [3:0] status_reg
+);
+
+ localparam CMD0_GO_IDLE_STATE = 6'h00;
+ localparam CMD2_ALL_SEND_CID = 6'h02;
+ localparam CMD3_SEND_RELATIVE_ADDR = 6'h03;
+ localparam CMD7_SELECT_CARD = 6'h07;
+ localparam CMD8_SEND_IF_COND = 6'h08;
+ localparam CMD9_SEND_CSD = 6'h09;
+ localparam CMD12_STOP_TRANSMISSION = 6'h0C;
+ localparam CMD16_SET_BLOCKLEN = 6'h10;
+ localparam CMD17_READ_SINGLE_BLOCK = 6'h11;
+ localparam CMD18_READ_MULTIPLE_BLOCK = 6'h12;
+ localparam CMD24_WRITE_BLOCK = 6'h18;
+ localparam CMD25_WRITE_MULTIPLE_BLOCK = 6'h19;
+ localparam CMD55_APP_CMD = 6'h37;
+ localparam ACMD41_SD_SEND_OP_COND = 6'h29;
+
+ localparam STATE_IDLE = 4'h0;
+ localparam STATE_CMD_RX = 4'h1;
+ localparam STATE_CMD_PROCESS = 4'h2;
+ localparam STATE_RESP_TX = 4'h3;
+ localparam STATE_DATA_RX = 4'h4;
+ localparam STATE_DATA_TX = 4'h5;
+ localparam STATE_WAIT_SPI = 4'h6;
+ localparam STATE_ERROR = 4'h7;
+
+ localparam SPI_READ = 8'h03;
+ localparam SPI_FAST_READ = 8'h0B;
+ localparam SPI_PP = 8'h02;
+ localparam SPI_SE = 8'h20;
+ localparam SPI_BE = 8'hD8;
+ localparam SPI_CE = 8'hC7;
+ localparam SPI_RDSR = 8'h05;
+ localparam SPI_WREN = 8'h06;
+ localparam SPI_RDID = 8'h9F;
+
+ reg [47:0] cmd_reg;
+ reg [5:0] cmd_index;
+ reg cmd_start;
+ reg cmd_done;
+
+ reg [135:0] resp_reg;
+ reg [6:0] resp_bit_count;
+ reg resp_start;
+ reg resp_done;
+
+ reg [31:0] block_size;
+ reg [31:0] rca;
+ reg card_selected;
+ reg [3:0] dat_bus_width;
+
+ reg [15:0] data_counter;
+ reg [7:0] crc7;
+ reg [15:0] crc16;
+
+ always @(posedge sd_clk or negedge rst_n) begin
+ if (!rst_n) begin
+ cmd_reg <= 48'h0;
+ cmd_index <= 6'd0;
+ cmd_start <= 1'b0;
+ cmd_done <= 1'b0;
+ sd_cmd_dir <= 1'b1;
+ sdio_state <= STATE_IDLE;
+ end else begin
+ case (sdio_state)
+ STATE_IDLE: begin
+ cmd_start <= 1'b0;
+ cmd_done <= 1'b0;
+ sd_cmd_dir <= 1'b1;
+
+ if (sd_cmd === 1'b0) begin
+ sdio_state <= STATE_CMD_RX;
+ cmd_index <= 6'd1;
+ cmd_reg <= 48'h0;
+ end
+ end
+
+ STATE_CMD_RX: begin
+ if (cmd_index < 6'd48) begin
+ cmd_reg[47] <= sd_cmd;
+ cmd_reg <= cmd_reg << 1;
+ cmd_index <= cmd_index + 1;
+ end else begin
+ sdio_state <= STATE_CMD_PROCESS;
+ cmd_start <= 1'b1;
+ cmd_done <= 1'b0;
+ end
+ end
+
+ STATE_CMD_PROCESS: begin
+ cmd_start <= 1'b0;
+ if (cmd_ready && cmd_valid) begin
+ sdio_state <= STATE_RESP_TX;
+ resp_start <= 1'b1;
+ end
+ end
+
+ STATE_RESP_TX: begin
+ resp_start <= 1'b0;
+ sd_cmd_dir <= 1'b0;
+ if (resp_done) begin
+ if (cmd_reg[45:40] == CMD17_READ_SINGLE_BLOCK ||
+ cmd_reg[45:40] == CMD18_READ_MULTIPLE_BLOCK) begin
+ sdio_state <= STATE_DATA_TX;
+ end else if (cmd_reg[45:40] == CMD24_WRITE_BLOCK ||
+ cmd_reg[45:40] == CMD25_WRITE_MULTIPLE_BLOCK) begin
+ sdio_state <= STATE_DATA_RX;
+ end else begin
+ sdio_state <= STATE_IDLE;
+ end
+ end
+ end
+
+ STATE_DATA_RX: begin
+ end
+
+ STATE_DATA_TX: begin
+ end
+
+ STATE_WAIT_SPI: begin
+ end
+
+ STATE_ERROR: begin
+ end
+ endcase
+ end
+ end
+
+ always @(*) begin
+ cmd_valid = 1'b0;
+ cmd_opcode = 8'h00;
+ cmd_address = 32'h0;
+ cmd_length = 16'h0;
+
+ if (cmd_start && !cmd_done) begin
+ case (cmd_reg[45:40])
+ CMD17_READ_SINGLE_BLOCK: begin
+ cmd_valid = 1'b1;
+ cmd_opcode = SPI_READ;
+ cmd_address = cmd_reg[39:8];
+ cmd_length = block_size;
+ end
+
+ CMD24_WRITE_BLOCK: begin
+ cmd_valid = 1'b1;
+ cmd_opcode = SPI_PP;
+ cmd_address = cmd_reg[39:8];
+ cmd_length = block_size;
+ end
+
+ CMD9_SEND_CSD: begin
+ cmd_valid = 1'b0;
+ end
+
+ CMD2_ALL_SEND_CID: begin
+ cmd_valid = 1'b0;
+ end
+
+ default: begin
+ cmd_valid = 1'b0;
+ end
+ endcase
+ end
+ end
+
+ always @(posedge sd_clk) begin
+ if (resp_start) begin
+ case (cmd_reg[45:40])
+ CMD0_GO_IDLE_STATE: begin
+ resp_reg <= {136{1'b1}};
+ end
+
+ CMD8_SEND_IF_COND: begin
+ resp_reg <= {8'hAA, 24'h0, 8'h01};
+ end
+
+ CMD17_READ_SINGLE_BLOCK,
+ CMD24_WRITE_BLOCK: begin
+ resp_reg <= {8'h00, 120'h0};
+ end
+
+ default: begin
+ resp_reg <= {136{1'b1}};
+ end
+ endcase
+ end
+ end
+
+ always @(posedge sd_clk) begin
+ if (!rst_n) begin
+ data_out_valid <= 1'b0;
+ data_in_ready <= 1'b0;
+ sd_dat_dir <= 4'b1111;
+ end else begin
+ end
+ end
+
+ initial begin
+ block_size = 32'd512;
+ rca = 32'h00010000;
+ card_selected = 1'b0;
+ dat_bus_width = 4'b0001;
+ status_reg = 4'b0000;
+ end
+
+endmodule \ No newline at end of file
diff --git a/hdl/spi_controller.v b/hdl/spi_controller.v
new file mode 100644
index 0000000..5bc71f6
--- /dev/null
+++ b/hdl/spi_controller.v
@@ -0,0 +1,290 @@
+`timescale 1ns / 1ps
+
+module spi_controller (
+ input wire clk,
+ input wire rst_n,
+
+ input wire [7:0] cmd_opcode,
+ input wire [31:0] cmd_address,
+ input wire [15:0] cmd_length,
+ input wire cmd_valid,
+ output wire cmd_ready,
+
+ input wire [7:0] data_in,
+ input wire data_in_valid,
+ output wire data_in_ready,
+
+ output wire [7:0] data_out,
+ output wire data_out_valid,
+ input wire data_out_ready,
+
+ output wire spi0_sck,
+ output wire spi0_mosi,
+ input wire spi0_miso,
+ output wire spi0_cs,
+
+ output wire spi1_sck,
+ output wire spi1_mosi,
+ input wire spi1_miso,
+ output wire spi1_cs,
+
+ output reg [3:0] flash_select,
+ output reg [3:0] spi_state,
+ output reg [3:0] status_reg
+);
+
+ localparam STATE_IDLE = 4'h0;
+ localparam STATE_CMD = 4'h1;
+ localparam STATE_ADDR = 4'h2;
+ localparam STATE_DUMMY = 4'h3;
+ localparam STATE_DATA_RX = 4'h4;
+ localparam STATE_DATA_TX = 4'h5;
+ localparam STATE_WAIT = 4'h6;
+ localparam STATE_ERROR = 4'h7;
+
+ localparam MODE_STANDARD = 2'b00;
+ localparam MODE_FAST = 2'b01;
+ localparam MODE_DUAL = 2'b10;
+ localparam MODE_QUAD = 2'b11;
+
+ localparam CMD_WREN = 8'h06;
+ localparam CMD_WRDI = 8'h04;
+ localparam CMD_RDSR = 8'h05;
+ localparam CMD_WRSR = 8'h01;
+ localparam CMD_READ = 8'h03;
+ localparam CMD_FAST_READ = 8'h0B;
+ localparam CMD_DREAD = 8'h3B;
+ localparam CMD_QREAD = 8'h6B;
+ localparam CMD_PP = 8'h02;
+ localparam CMD_SE = 8'h20;
+ localparam CMD_BE32 = 8'h52;
+ localparam CMD_BE64 = 8'hD8;
+ localparam CMD_CE = 8'hC7;
+ localparam CMD_RDID = 8'h9F;
+ localparam CMD_RDFR = 8'h48;
+ localparam CMD_WRFR = 8'h42;
+
+ reg [7:0] spi_mode;
+ reg [2:0] spi_divider;
+ reg [31:0] current_addr;
+ reg [15:0] bytes_remaining;
+ reg [7:0] command_reg;
+ reg [3:0] state;
+ reg [3:0] next_state;
+
+ reg [7:0] tx_shift_reg;
+ reg [7:0] rx_shift_reg;
+ reg [3:0] bit_counter;
+ reg tx_active;
+ reg rx_active;
+
+ reg cs0_active;
+ reg cs1_active;
+ reg sck_active;
+
+ reg [23:0] delay_counter;
+
+ reg [7:0] clk_div_counter;
+ wire sck_posedge;
+ wire sck_negedge;
+
+ assign sck_posedge = (clk_div_counter == spi_divider);
+ assign sck_negedge = (clk_div_counter == (spi_divider >> 1));
+
+ assign spi0_sck = sck_active & cs0_active & ~spi0_cs;
+ assign spi1_sck = sck_active & cs1_active & ~spi1_cs;
+ assign spi0_cs = ~cs0_active;
+ assign spi1_cs = ~cs1_active;
+
+ assign spi0_mosi = tx_active ? tx_shift_reg[7] : 1'bz;
+ assign spi1_mosi = tx_active ? tx_shift_reg[7] : 1'bz;
+
+ assign cmd_ready = (state == STATE_IDLE);
+
+ assign data_in_ready = (state == STATE_DATA_TX) && tx_active && sck_negedge;
+ assign data_out_valid = (state == STATE_DATA_RX) && rx_active && sck_posedge;
+
+ always @(posedge clk or negedge rst_n) begin
+ if (!rst_n) begin
+ state <= STATE_IDLE;
+ spi_state <= STATE_IDLE;
+ cs0_active <= 1'b0;
+ cs1_active <= 1'b0;
+ sck_active <= 1'b0;
+ tx_active <= 1'b0;
+ rx_active <= 1'b0;
+ spi_mode <= MODE_STANDARD;
+ spi_divider <= 3'd1;
+ flash_select <= 2'b00;
+ status_reg <= 4'b0000;
+ end else begin
+ state <= next_state;
+ spi_state <= state;
+
+ case (state)
+ STATE_IDLE: begin
+ cs0_active <= 1'b0;
+ cs1_active <= 1'b0;
+ sck_active <= 1'b0;
+ tx_active <= 1'b0;
+ rx_active <= 1'b0;
+
+ if (cmd_valid && cmd_ready) begin
+ if (cmd_address[24] == 1'b0) begin
+ cs0_active <= 1'b1;
+ flash_select <= 2'b01;
+ end else begin
+ cs1_active <= 1'b1;
+ flash_select <= 2'b10;
+ end
+
+ command_reg <= cmd_opcode;
+ current_addr <= cmd_address;
+ bytes_remaining <= cmd_length;
+ next_state <= STATE_CMD;
+ end
+ end
+
+ STATE_CMD: begin
+ if (sck_negedge) begin
+ if (bit_counter == 4'd7) begin
+ bit_counter <= 4'd0;
+ if (command_reg == CMD_READ ||
+ command_reg == CMD_FAST_READ ||
+ command_reg == CMD_PP) begin
+ next_state <= STATE_ADDR;
+ end else if (command_reg == CMD_RDSR ||
+ command_reg == CMD_RDID) begin
+ next_state <= STATE_DATA_RX;
+ end else begin
+ next_state <= STATE_WAIT;
+ end
+ end else begin
+ tx_shift_reg <= tx_shift_reg << 1;
+ bit_counter <= bit_counter + 1;
+ end
+ end
+ end
+
+ STATE_ADDR: begin
+ if (sck_negedge) begin
+ if (bit_counter == 4'd7) begin
+ bit_counter <= 4'd0;
+ if (addr_byte_counter == 3'd2) begin
+ if (command_reg == CMD_FAST_READ ||
+ command_reg == CMD_DREAD ||
+ command_reg == CMD_QREAD) begin
+ next_state <= STATE_DUMMY;
+ end else if (command_reg == CMD_READ) begin
+ next_state <= STATE_DATA_RX;
+ end else if (command_reg == CMD_PP) begin
+ next_state <= STATE_DATA_TX;
+ end else begin
+ next_state <= STATE_WAIT;
+ end
+ end else begin
+ addr_byte_counter <= addr_byte_counter + 1;
+ end
+ end else begin
+ tx_shift_reg <= tx_shift_reg << 1;
+ bit_counter <= bit_counter + 1;
+ end
+ end
+ end
+
+ STATE_DATA_RX: begin
+ rx_active <= 1'b1;
+ if (sck_posedge) begin
+ rx_shift_reg <= {rx_shift_reg[6:0], (cs0_active ? spi0_miso : spi1_miso)};
+ if (bit_counter == 4'd7) begin
+ bit_counter <= 4'd0;
+ data_out <= rx_shift_reg;
+ if (bytes_remaining > 0) begin
+ bytes_remaining <= bytes_remaining - 1;
+ end else begin
+ next_state <= STATE_IDLE;
+ rx_active <= 1'b0;
+ end
+ end else begin
+ bit_counter <= bit_counter + 1;
+ end
+ end
+ end
+
+ STATE_DATA_TX: begin
+ tx_active <= 1'b1;
+ if (sck_negedge) begin
+ if (bit_counter == 4'd7) begin
+ bit_counter <= 4'd0;
+ if (data_in_valid && data_in_ready) begin
+ tx_shift_reg <= data_in;
+ if (bytes_remaining > 0) begin
+ bytes_remaining <= bytes_remaining - 1;
+ end else begin
+ next_state <= STATE_WAIT;
+ tx_active <= 1'b0;
+ end
+ end
+ end else begin
+ tx_shift_reg <= tx_shift_reg << 1;
+ bit_counter <= bit_counter + 1;
+ end
+ end
+ end
+
+ STATE_WAIT: begin
+ if (delay_counter == 24'hFFFFFF) begin
+ next_state <= STATE_IDLE;
+ end else begin
+ delay_counter <= delay_counter + 1;
+ end
+ end
+
+ STATE_ERROR: begin
+ cs0_active <= 1'b0;
+ cs1_active <= 1'b0;
+ sck_active <= 1'b0;
+ next_state <= STATE_IDLE;
+ end
+ endcase
+ end
+ end
+
+ always @(posedge clk or negedge rst_n) begin
+ if (!rst_n) begin
+ clk_div_counter <= 8'd0;
+ sck_active <= 1'b0;
+ end else begin
+ if (cs0_active || cs1_active) begin
+ if (clk_div_counter >= spi_divider) begin
+ clk_div_counter <= 8'd0;
+ sck_active <= ~sck_active;
+ end else begin
+ clk_div_counter <= clk_div_counter + 1;
+ end
+ end else begin
+ clk_div_counter <= 8'd0;
+ sck_active <= 1'b0;
+ end
+ end
+ end
+
+ reg [2:0] addr_byte_counter;
+
+ always @(posedge clk or negedge rst_n) begin
+ if (!rst_n) begin
+ addr_byte_counter <= 3'd0;
+ end else if (state == STATE_ADDR) begin
+ end else begin
+ addr_byte_counter <= 3'd0;
+ end
+ end
+
+ initial begin
+ tx_shift_reg = 8'h00;
+ rx_shift_reg = 8'h00;
+ bit_counter = 4'd0;
+ delay_counter = 24'h0;
+ end
+
+endmodule \ No newline at end of file
diff --git a/hdl/turbo_top.v b/hdl/turbo_top.v
new file mode 100644
index 0000000..eb17446
--- /dev/null
+++ b/hdl/turbo_top.v
@@ -0,0 +1,159 @@
+`timescale 1ns / 1ps
+
+module turbo_top (
+ input wire SD_CLK,
+ inout wire SD_CMD,
+ inout wire [3:0] SD_DAT,
+
+ output wire SPI0_SCK,
+ output wire SPI0_MOSI,
+ input wire SPI0_MISO,
+ output wire SPI0_CS,
+
+ output wire SPI1_SCK,
+ output wire SPI1_MOSI,
+ input wire SPI1_MISO,
+ output wire SPI1_CS,
+
+ output wire UART_TX,
+ input wire UART_RX,
+
+ output wire LED0,
+ output wire LED1,
+ output wire LED2,
+ output wire LED3,
+
+ input wire CRESET_B,
+ output wire CDONE,
+
+ input wire CLK_12MHZ,
+
+ output wire PWR_EN,
+ input wire PWR_GOOD
+);
+
+ wire clk_12mhz;
+ wire clk_50mhz;
+ wire clk_sdio;
+ wire clk_spi;
+
+ wire rst_n;
+ wire sdio_cmd_dir;
+ wire [3:0] sdio_dat_dir;
+
+ wire [7:0] cmd_opcode;
+ wire [31:0] cmd_address;
+ wire [15:0] cmd_length;
+ wire cmd_valid;
+ wire cmd_ready;
+
+ wire [7:0] data_in;
+ wire data_in_valid;
+ wire data_in_ready;
+
+ wire [7:0] data_out;
+ wire data_out_valid;
+ wire data_out_ready;
+
+ wire [3:0] sdio_state;
+ wire [3:0] spi_state;
+ wire [3:0] flash_select;
+
+ wire [7:0] status_reg;
+
+ clock_divider clock_div (
+ .clk_in(CLK_12MHZ),
+ .rst_n(CRESET_B),
+ .clk_12mhz(clk_12mhz),
+ .clk_50mhz(clk_50mhz),
+ .clk_sdio(clk_sdio),
+ .clk_spi(clk_spi)
+ );
+
+ sdio_to_spi sdio_converter (
+ .sd_clk(SD_CLK),
+ .sd_cmd(SD_CMD),
+ .sd_dat(SD_DAT),
+ .sd_cmd_dir(sdio_cmd_dir),
+ .sd_dat_dir(sdio_dat_dir),
+ .clk_sys(clk_50mhz),
+ .rst_n(rst_n),
+ .cmd_opcode(cmd_opcode),
+ .cmd_address(cmd_address),
+ .cmd_length(cmd_length),
+ .cmd_valid(cmd_valid),
+ .cmd_ready(cmd_ready),
+ .data_out(data_out),
+ .data_out_valid(data_out_valid),
+ .data_out_ready(data_out_ready),
+ .data_in(data_in),
+ .data_in_valid(data_in_valid),
+ .data_in_ready(data_in_ready),
+ .sdio_state(sdio_state),
+ .status_reg(status_reg[3:0])
+ );
+
+ spi_controller spi_ctrl (
+ .clk(clk_spi),
+ .rst_n(rst_n),
+ .cmd_opcode(cmd_opcode),
+ .cmd_address(cmd_address),
+ .cmd_length(cmd_length),
+ .cmd_valid(cmd_valid),
+ .cmd_ready(cmd_ready),
+ .data_in(data_in),
+ .data_in_valid(data_in_valid),
+ .data_in_ready(data_in_ready),
+ .data_out(data_out),
+ .data_out_valid(data_out_valid),
+ .data_out_ready(data_out_ready),
+ .spi0_sck(SPI0_SCK),
+ .spi0_mosi(SPI0_MOSI),
+ .spi0_miso(SPI0_MISO),
+ .spi0_cs(SPI0_CS),
+ .spi1_sck(SPI1_SCK),
+ .spi1_mosi(SPI1_MOSI),
+ .spi1_miso(SPI1_MISO),
+ .spi1_cs(SPI1_CS),
+ .flash_select(flash_select),
+ .spi_state(spi_state),
+ .status_reg(status_reg[7:4])
+ );
+
+ fifo_buffer data_fifo (
+ .wr_clk(clk_sdio),
+ .rd_clk(clk_spi),
+ .rst_n(rst_n),
+ .wr_data(data_out),
+ .wr_valid(data_out_valid),
+ .wr_ready(data_out_ready),
+ .rd_data(data_in),
+ .rd_valid(data_in_valid),
+ .rd_ready(data_in_ready),
+ .fifo_empty(),
+ .fifo_full(),
+ .fifo_level()
+ );
+
+ assign rst_n = CRESET_B & PWR_GOOD;
+
+ assign SD_CMD = sdio_cmd_dir ? 1'bz : 1'b0;
+ generate
+ genvar i;
+ for (i = 0; i < 4; i = i + 1) begin : dat_dir_gen
+ assign SD_DAT[i] = sdio_dat_dir[i] ? 1'bz : 1'b0;
+ end
+ endgenerate
+
+ assign LED0 = ~rst_n;
+ assign LED1 = |sdio_state;
+ assign LED2 = |spi_state;
+ assign LED3 = |flash_select;
+
+ assign UART_TX = UART_RX;
+
+ assign PWR_EN = 1'b1;
+
+ assign CDONE = 1'b1;
+
+endmodule \ No newline at end of file