mirror of
https://gitlab.com/hyperglitch/jellyfish.git
synced 2025-11-09 21:27:59 +00:00
411 lines
11 KiB
Verilog
411 lines
11 KiB
Verilog
// SPDX-FileCopyrightText: 2025 Igor Brkic <igor@hyperglitch.com>
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
`default_nettype none
|
|
module qspi_decoder
|
|
#(
|
|
parameter integer ADDRESS_WIDTH = 24,
|
|
parameter integer ADDRESS_LINES = 4,
|
|
parameter integer COMMAND_WIDTH = 8,
|
|
parameter integer COMMAND_LINES = 1,
|
|
parameter integer DUMMY_CYCLES = 2,
|
|
parameter integer DATA_WIDTH = 8,
|
|
parameter integer DATA_LINES = 4,
|
|
parameter integer DATA_BANKS = 2
|
|
)(
|
|
input i_rst,
|
|
input i_ncs,
|
|
input i_clk,
|
|
|
|
input i_main_clk,
|
|
|
|
inout i_bk1_io0,
|
|
inout i_bk1_io1,
|
|
inout i_bk1_io2,
|
|
inout i_bk1_io3,
|
|
inout i_bk2_io0,
|
|
inout i_bk2_io1,
|
|
inout i_bk2_io2,
|
|
inout i_bk2_io3,
|
|
|
|
input i_fifo_wr_en,
|
|
input [DATA_WIDTH*DATA_BANKS-1:0] i_fifo_wr_data,
|
|
|
|
output reg o_cmd_rdy,
|
|
output reg[COMMAND_WIDTH-1:0] o_command,
|
|
|
|
output reg o_addr_rdy,
|
|
output reg[ADDRESS_WIDTH-1:0] o_address,
|
|
|
|
output reg o_data_rdy,
|
|
output reg[DATA_WIDTH*DATA_BANKS-1:0] o_data,
|
|
input [DATA_WIDTH*DATA_BANKS-1:0] i_data,
|
|
|
|
output o_data_fifo_full,
|
|
output o_data_fifo_empty,
|
|
|
|
output reg dbgled
|
|
);
|
|
|
|
localparam [2:0] NCS_WAIT = 3'd0;
|
|
localparam [2:0] CMD_READ = 3'd1;
|
|
localparam [2:0] ADDR_READ = 3'd2;
|
|
localparam [2:0] DUMMY_WAIT = 3'd3;
|
|
localparam [2:0] DATA_READ = 3'd4;
|
|
localparam [2:0] DATA_WRITE = 3'd5;
|
|
localparam [2:0] CMD_DONE = 3'd6;
|
|
|
|
reg oe = 1'b0;
|
|
|
|
reg [2:0] current_state = NCS_WAIT;
|
|
|
|
reg [DATA_WIDTH*DATA_BANKS-1:0] read_data = 0;
|
|
|
|
reg [$clog2(ADDRESS_WIDTH)-1:0] address_counter = 0;
|
|
reg [$clog2(COMMAND_WIDTH)-1:0] command_counter = 0;
|
|
reg [$clog2(DUMMY_WAIT)-1:0] dummy_counter = 0;
|
|
reg [$clog2(DATA_WIDTH+1)-1:0] data_counter = DATA_LINES;
|
|
|
|
reg cmd_rdy, cmd_rdy_presync, cmd_rdy_sync;
|
|
reg addr_rdy, addr_rdy_presync, addr_rdy_sync;
|
|
reg data_rdy, data_rdy_presync, data_rdy_sync;
|
|
|
|
reg data_write_done = 0;
|
|
|
|
reg [COMMAND_WIDTH-1:0] command;
|
|
reg [ADDRESS_WIDTH-1:0] address;
|
|
reg [DATA_WIDTH*DATA_BANKS-1:0] data;
|
|
//reg [DATA_WIDTH*DATA_BANKS-1:0] data_wr;
|
|
wire [DATA_WIDTH*DATA_BANKS-1:0] data_wr;
|
|
|
|
reg write_data_10;
|
|
reg write_data_11;
|
|
reg write_data_12;
|
|
reg write_data_13;
|
|
reg write_data_20;
|
|
reg write_data_21;
|
|
reg write_data_22;
|
|
reg write_data_23;
|
|
|
|
wire i_bk1_io0_read;
|
|
wire i_bk1_io1_read;
|
|
wire i_bk1_io2_read;
|
|
wire i_bk1_io3_read;
|
|
wire i_bk2_io0_read;
|
|
wire i_bk2_io1_read;
|
|
wire i_bk2_io2_read;
|
|
wire i_bk2_io3_read;
|
|
|
|
// bidirectional data buffers (6'b1010_01 is bidirectional with tri-state)
|
|
SB_IO #(
|
|
.PIN_TYPE(6'b1010_01),
|
|
.PULLUP(1'b1)
|
|
) iob_bk1_io0 (
|
|
.PACKAGE_PIN(i_bk1_io0),
|
|
.OUTPUT_ENABLE(oe),
|
|
.D_OUT_0(write_data_10),
|
|
.D_IN_0(i_bk1_io0_read)
|
|
);
|
|
SB_IO #(
|
|
.PIN_TYPE(6'b1010_01),
|
|
.PULLUP(1'b1)
|
|
) iob_bk1_io1 (
|
|
.PACKAGE_PIN(i_bk1_io1),
|
|
.OUTPUT_ENABLE(oe),
|
|
.D_OUT_0(write_data_11),
|
|
.D_IN_0(i_bk1_io1_read)
|
|
);
|
|
SB_IO #(
|
|
.PIN_TYPE(6'b1010_01),
|
|
.PULLUP(1'b1)
|
|
) iob_bk1_io2 (
|
|
.PACKAGE_PIN(i_bk1_io2),
|
|
.OUTPUT_ENABLE(oe),
|
|
.D_OUT_0(write_data_12),
|
|
.D_IN_0(i_bk1_io2_read)
|
|
);
|
|
SB_IO #(
|
|
.PIN_TYPE(6'b1010_01),
|
|
.PULLUP(1'b1)
|
|
) iob_bk1_io3 (
|
|
.PACKAGE_PIN(i_bk1_io3),
|
|
.OUTPUT_ENABLE(oe),
|
|
.D_OUT_0(write_data_13),
|
|
.D_IN_0(i_bk1_io3_read)
|
|
);
|
|
|
|
SB_IO #(
|
|
.PIN_TYPE(6'b1010_01),
|
|
.PULLUP(1'b1)
|
|
) iob_bk2_io0 (
|
|
.PACKAGE_PIN(i_bk2_io0),
|
|
.OUTPUT_ENABLE(oe),
|
|
.D_OUT_0(write_data_20),
|
|
.D_IN_0(i_bk2_io0_read)
|
|
);
|
|
SB_IO #(
|
|
.PIN_TYPE(6'b1010_01),
|
|
.PULLUP(1'b1)
|
|
) iob_bk2_io1 (
|
|
.PACKAGE_PIN(i_bk2_io1),
|
|
.OUTPUT_ENABLE(oe),
|
|
.D_OUT_0(write_data_21),
|
|
.D_IN_0(i_bk2_io1_read)
|
|
);
|
|
SB_IO #(
|
|
.PIN_TYPE(6'b1010_01),
|
|
.PULLUP(1'b1)
|
|
) iob_bk2_io2 (
|
|
.PACKAGE_PIN(i_bk2_io2),
|
|
.OUTPUT_ENABLE(oe),
|
|
.D_OUT_0(write_data_22),
|
|
.D_IN_0(i_bk2_io2_read)
|
|
);
|
|
SB_IO #(
|
|
.PIN_TYPE(6'b1010_01),
|
|
.PULLUP(1'b1)
|
|
) iob_bk2_io3 (
|
|
.PACKAGE_PIN(i_bk2_io3),
|
|
.OUTPUT_ENABLE(oe),
|
|
.D_OUT_0(write_data_23),
|
|
.D_IN_0(i_bk2_io3_read)
|
|
);
|
|
|
|
|
|
// TODO:
|
|
// utilize the SRAM (256k 16bit words) for the FIFO
|
|
// https://github.com/michaelengel/iCE40HX1K-EVB/blob/master/sram/sram.v
|
|
// https://github.com/aabzel/FIFO-On-SRAM
|
|
async_fifo #(
|
|
.DATA_SIZE(16),
|
|
.ADDR_SIZE(8)
|
|
) data_fifo (
|
|
.w_clk(i_main_clk),
|
|
.w_rst(i_rst),
|
|
.w_en(i_fifo_wr_en),
|
|
.w_data(i_fifo_wr_data),
|
|
//.w_full(o_data_fifo_full),
|
|
.w_almost_full(o_data_fifo_full),
|
|
.r_clk(i_clk),
|
|
.r_rst(i_rst),
|
|
.r_en(data_write_done),
|
|
.r_data(data_wr),
|
|
.r_empty(o_data_fifo_empty),
|
|
.dbgled(dbgled)
|
|
);
|
|
|
|
// sync signals to main clock
|
|
always @(posedge i_main_clk) begin
|
|
cmd_rdy_presync <= cmd_rdy;
|
|
addr_rdy_presync <= addr_rdy;
|
|
data_rdy_presync <= data_rdy;
|
|
|
|
cmd_rdy_sync <= cmd_rdy_presync;
|
|
addr_rdy_sync <= addr_rdy_presync;
|
|
data_rdy_sync <= data_rdy_presync;
|
|
|
|
if (cmd_rdy_sync == 1'b0 && cmd_rdy_presync == 1'b1) begin
|
|
o_cmd_rdy <= 1'b1;
|
|
o_command <= command;
|
|
end else begin
|
|
o_cmd_rdy <= 1'b0;
|
|
end
|
|
|
|
if (addr_rdy_sync == 1'b0 && addr_rdy_presync == 1'b1) begin
|
|
o_addr_rdy <= 1'b1;
|
|
o_address <= address;
|
|
end else begin
|
|
o_addr_rdy <= 1'b0;
|
|
end
|
|
|
|
if (data_rdy_sync == 1'b0 && data_rdy_presync == 1'b1) begin
|
|
o_data_rdy <= 1'b1;
|
|
o_data <= data;
|
|
end else begin
|
|
o_data_rdy <= 1'b0;
|
|
end
|
|
|
|
end
|
|
|
|
// state machine and reset on rising edge of NCS
|
|
always @(posedge i_clk or posedge i_ncs) begin
|
|
if (i_ncs == 1'b1) begin
|
|
current_state <= CMD_READ;
|
|
command_counter <= 0;
|
|
data_rdy <= 0;
|
|
cmd_rdy <= 0;
|
|
addr_rdy <= 0;
|
|
o_data <= 0;
|
|
command <= 0;
|
|
address <= 0;
|
|
data_write_done <= 1'b0;
|
|
|
|
oe <= 1'b0; // disable output
|
|
|
|
end else begin
|
|
|
|
data_write_done <= 1'b0;
|
|
|
|
// reset the data ready pulse
|
|
case (current_state)
|
|
|
|
CMD_READ: begin
|
|
if (COMMAND_LINES == 1) begin
|
|
command[COMMAND_WIDTH-1-command_counter] <= i_bk1_io0_read;
|
|
|
|
end else if (COMMAND_LINES == 2) begin
|
|
command[COMMAND_WIDTH-1-command_counter] <= i_bk1_io1_read;
|
|
command[COMMAND_WIDTH-1-command_counter-1] <= i_bk1_io0_read;
|
|
|
|
end else if (COMMAND_LINES == 4) begin
|
|
command[COMMAND_WIDTH-1-command_counter] <= i_bk1_io3_read;
|
|
command[COMMAND_WIDTH-1-command_counter-1] <= i_bk1_io2_read;
|
|
command[COMMAND_WIDTH-1-command_counter-2] <= i_bk1_io1_read;
|
|
command[COMMAND_WIDTH-1-command_counter-3] <= i_bk1_io0_read;
|
|
end
|
|
|
|
command_counter <= command_counter + COMMAND_LINES;
|
|
if (command_counter == COMMAND_WIDTH-COMMAND_LINES) begin
|
|
address_counter <= 0;
|
|
address <= 0;
|
|
current_state <= ADDR_READ;
|
|
cmd_rdy <= 1'b1;
|
|
end
|
|
end
|
|
|
|
ADDR_READ: begin
|
|
if (ADDRESS_LINES == 1) begin
|
|
address[ADDRESS_WIDTH-1-address_counter] <= i_bk1_io0_read;
|
|
|
|
end else if (ADDRESS_LINES == 2) begin
|
|
address[ADDRESS_WIDTH-1-address_counter] <= i_bk1_io1_read;
|
|
address[ADDRESS_WIDTH-1-address_counter-1] <= i_bk1_io0_read;
|
|
|
|
end else if (ADDRESS_LINES == 4) begin
|
|
address[ADDRESS_WIDTH-1-address_counter] <= i_bk1_io3_read;
|
|
address[ADDRESS_WIDTH-1-address_counter-1] <= i_bk1_io2_read;
|
|
address[ADDRESS_WIDTH-1-address_counter-2] <= i_bk1_io1_read;
|
|
address[ADDRESS_WIDTH-1-address_counter-3] <= i_bk1_io0_read;
|
|
end
|
|
|
|
address_counter <= address_counter + ADDRESS_LINES;
|
|
|
|
if (address_counter == ADDRESS_WIDTH-ADDRESS_LINES) begin
|
|
addr_rdy <= 1'b1;
|
|
dummy_counter <= 0;
|
|
current_state <= DUMMY_WAIT;
|
|
end
|
|
end
|
|
|
|
DUMMY_WAIT: begin
|
|
dummy_counter <= dummy_counter + 1;
|
|
if (dummy_counter == DUMMY_CYCLES-1) begin
|
|
read_data <= 0;
|
|
|
|
if (command==8'h01) begin
|
|
current_state <= DATA_WRITE;
|
|
data_counter <= DATA_LINES;
|
|
oe <= 1'b1; // set data pins as outputs
|
|
data_write_done <= 1'b1; // pull the next data from the buffer and copy it to data_wr
|
|
|
|
end else if (command==8'had) begin
|
|
current_state <= DATA_READ;
|
|
|
|
end else begin
|
|
current_state <= CMD_DONE;
|
|
|
|
end
|
|
end
|
|
end
|
|
|
|
DATA_READ: begin
|
|
if (DATA_LINES == 2) begin
|
|
read_data[DATA_WIDTH-1-data_counter] <= i_bk1_io1_read;
|
|
read_data[DATA_WIDTH-1-data_counter-1] <= i_bk1_io0_read;
|
|
|
|
if (DATA_BANKS == 2) begin
|
|
read_data[2*DATA_WIDTH-1-data_counter-1] <= i_bk2_io1_read;
|
|
read_data[2*DATA_WIDTH-1-data_counter-2] <= i_bk2_io0_read;
|
|
end
|
|
|
|
end else if (DATA_LINES == 4) begin
|
|
read_data[DATA_WIDTH-1-data_counter] <= i_bk1_io3_read;
|
|
read_data[DATA_WIDTH-1-data_counter-1] <= i_bk1_io2_read;
|
|
read_data[DATA_WIDTH-1-data_counter-2] <= i_bk1_io1_read;
|
|
read_data[DATA_WIDTH-1-data_counter-3] <= i_bk1_io0_read;
|
|
|
|
read_data[2*DATA_WIDTH-1-data_counter] <= i_bk2_io3_read;
|
|
read_data[2*DATA_WIDTH-1-data_counter-1] <= i_bk2_io2_read;
|
|
read_data[2*DATA_WIDTH-1-data_counter-2] <= i_bk2_io1_read;
|
|
read_data[2*DATA_WIDTH-1-data_counter-3] <= i_bk2_io0_read;
|
|
end
|
|
|
|
data_counter <= data_counter + DATA_LINES;
|
|
|
|
if (data_counter == DATA_WIDTH) begin
|
|
data_counter <= DATA_LINES;
|
|
o_data <= read_data;
|
|
read_data <= 0;
|
|
data_rdy <= 1;
|
|
end
|
|
end
|
|
|
|
DATA_WRITE: begin
|
|
if (DATA_LINES == 4) begin
|
|
|
|
if (data_counter==DATA_LINES) begin
|
|
// data_counter = 4, cycle 1
|
|
//address <= address + 1;
|
|
//addr_rdy <= 1'b1;
|
|
|
|
// bk1, bits 11:8
|
|
write_data_13 <= data_wr[11];
|
|
write_data_12 <= data_wr[10];
|
|
write_data_11 <= data_wr[9];
|
|
write_data_10 <= data_wr[8];
|
|
|
|
// bk2, bits 3:0
|
|
write_data_23 <= data_wr[3];
|
|
write_data_22 <= data_wr[2];
|
|
write_data_21 <= data_wr[1];
|
|
write_data_20 <= data_wr[0];
|
|
|
|
end else begin
|
|
// data_counter = 8, cycle 2
|
|
// bk1, bits 15:12
|
|
write_data_13 <= data_wr[15];
|
|
write_data_12 <= data_wr[14];
|
|
write_data_11 <= data_wr[13];
|
|
write_data_10 <= data_wr[12];
|
|
|
|
// bk2, bits 7:4
|
|
write_data_23 <= data_wr[7];
|
|
write_data_22 <= data_wr[6];
|
|
write_data_21 <= data_wr[5];
|
|
write_data_20 <= data_wr[4];
|
|
|
|
end
|
|
end
|
|
|
|
data_counter <= data_counter + DATA_LINES;
|
|
|
|
if (data_counter == DATA_WIDTH) begin
|
|
data_counter <= DATA_LINES;
|
|
data_write_done <= 1'b1; // pull the next data from the buffer and copy it to data_wr
|
|
end
|
|
|
|
end
|
|
|
|
CMD_DONE: begin
|
|
data_rdy <= 0;
|
|
end
|
|
|
|
default: begin
|
|
// should never happen
|
|
current_state <= NCS_WAIT;
|
|
end
|
|
endcase
|
|
end
|
|
end
|
|
endmodule
|