0
mirror of https://gitlab.com/hyperglitch/jellyfish.git synced 2025-11-09 21:27:59 +00:00
jellyfish-powersupply/gw/src/qspi_decoder.v

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