mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-11-23 22:29:15 +01:00
493 lines
16 KiB
Systemverilog
493 lines
16 KiB
Systemverilog
interface flash_scb ();
|
|
|
|
logic erase_pending;
|
|
logic erase_done;
|
|
logic [7:0] erase_block;
|
|
|
|
modport controller (
|
|
output erase_pending,
|
|
input erase_done,
|
|
output erase_block
|
|
);
|
|
|
|
modport flash (
|
|
input erase_pending,
|
|
output erase_done,
|
|
input erase_block
|
|
);
|
|
|
|
endinterface
|
|
|
|
|
|
module flash_qspi (
|
|
input clk,
|
|
input reset,
|
|
|
|
input start,
|
|
input finish,
|
|
output logic busy,
|
|
output logic valid,
|
|
input output_enable,
|
|
input quad_enable,
|
|
output logic [7:0] rdata,
|
|
input [7:0] wdata,
|
|
|
|
output logic flash_clk,
|
|
output logic flash_cs,
|
|
inout [3:0] flash_dq
|
|
);
|
|
|
|
logic flash_dq_oe_s;
|
|
logic flash_dq_oe_q;
|
|
logic [3:0] flash_dq_out;
|
|
|
|
assign flash_dq[0] = flash_dq_oe_s ? flash_dq_out[0] : 1'bZ;
|
|
assign flash_dq[3:1] = flash_dq_oe_q ? flash_dq_out[3:1] : 3'bZZZ;
|
|
|
|
logic ff_clk;
|
|
logic ff_cs;
|
|
logic ff_dq_oe_s;
|
|
logic ff_dq_oe_q;
|
|
logic [3:0] ff_dq_out;
|
|
logic [3:0] ff_dq_in;
|
|
|
|
always_ff @(posedge clk) begin
|
|
flash_clk <= ff_clk;
|
|
flash_cs <= ff_cs;
|
|
flash_dq_oe_s <= ff_dq_oe_s;
|
|
flash_dq_oe_q <= ff_dq_oe_q;
|
|
flash_dq_out <= ff_dq_out;
|
|
ff_dq_in <= flash_dq;
|
|
end
|
|
|
|
logic running;
|
|
logic exit;
|
|
logic valid_enable;
|
|
logic quad_mode;
|
|
logic [2:0] counter;
|
|
logic [7:0] output_shift;
|
|
logic [2:0] sample_s;
|
|
logic [2:0] sample_q;
|
|
logic [2:0] valid_ff;
|
|
|
|
assign ff_dq_out = quad_mode ? output_shift[7:4] : {3'bXXX, output_shift[7]};
|
|
|
|
always_ff @(posedge clk) begin
|
|
sample_s <= {sample_s[1:0], 1'b0};
|
|
sample_q <= {sample_q[1:0], 1'b0};
|
|
valid_ff <= {valid_ff[1:0], 1'b0};
|
|
if (reset) begin
|
|
ff_clk <= 1'b0;
|
|
ff_cs <= 1'b1;
|
|
ff_dq_oe_s <= 1'b0;
|
|
ff_dq_oe_q <= 1'b0;
|
|
busy <= 1'b0;
|
|
running <= 1'b0;
|
|
end else begin
|
|
if (running) begin
|
|
ff_clk <= ~ff_clk;
|
|
if (!ff_clk) begin
|
|
if (counter == 3'd0) begin
|
|
busy <= 1'b0;
|
|
valid_ff[0] <= valid_enable;
|
|
end
|
|
if (!quad_mode) begin
|
|
sample_s[0] <= 1'b1;
|
|
end else begin
|
|
sample_q[0] <= 1'b1;
|
|
end
|
|
end else begin
|
|
counter <= counter - 1'd1;
|
|
if (counter == 3'd0) begin
|
|
running <= 1'b0;
|
|
end
|
|
if (!quad_mode) begin
|
|
output_shift <= {output_shift[6:0], 1'bX};
|
|
end else begin
|
|
output_shift <= {output_shift[3:0], 4'bXXXX};
|
|
end
|
|
end
|
|
end
|
|
|
|
if (exit) begin
|
|
ff_cs <= 1'b1;
|
|
counter <= counter - 1'd1;
|
|
if (counter == 3'd0) begin
|
|
busy <= 1'b0;
|
|
exit <= 1'b0;
|
|
end
|
|
end
|
|
|
|
if (!busy) begin
|
|
if (start) begin
|
|
ff_clk <= 1'b0;
|
|
ff_cs <= 1'b0;
|
|
ff_dq_oe_s <= !quad_enable || (quad_enable && output_enable);
|
|
ff_dq_oe_q <= quad_enable && output_enable;
|
|
busy <= 1'b1;
|
|
running <= 1'b1;
|
|
valid_enable <= !output_enable;
|
|
quad_mode <= quad_enable;
|
|
counter <= quad_enable ? 3'd1 : 3'd7;
|
|
output_shift <= wdata;
|
|
end else if (finish) begin
|
|
busy <= 1'b1;
|
|
exit <= 1'b1;
|
|
counter <= wdata[2:0];
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
always_ff @(posedge clk) begin
|
|
valid <= 1'b0;
|
|
if (sample_s[2]) begin
|
|
rdata <= {rdata[6:0], ff_dq_in[1]};
|
|
end
|
|
if (sample_q[2]) begin
|
|
rdata <= {rdata[3:0], ff_dq_in};
|
|
end
|
|
if (valid_ff[2]) begin
|
|
valid <= 1'b1;
|
|
end
|
|
end
|
|
|
|
endmodule
|
|
|
|
|
|
module memory_flash (
|
|
input clk,
|
|
input reset,
|
|
|
|
flash_scb.flash flash_scb,
|
|
|
|
mem_bus.memory mem_bus,
|
|
|
|
output flash_clk,
|
|
output flash_cs,
|
|
inout [3:0] flash_dq
|
|
);
|
|
|
|
logic start;
|
|
logic finish;
|
|
logic busy;
|
|
logic valid;
|
|
logic output_enable;
|
|
logic quad_enable;
|
|
logic [7:0] rdata;
|
|
logic [7:0] wdata;
|
|
|
|
flash_qspi flash_qspi_inst (
|
|
.clk(clk),
|
|
.reset(reset),
|
|
|
|
.start(start),
|
|
.finish(finish),
|
|
.busy(busy),
|
|
.valid(valid),
|
|
.output_enable(output_enable),
|
|
.quad_enable(quad_enable),
|
|
.rdata(rdata),
|
|
.wdata(wdata),
|
|
|
|
.flash_clk(flash_clk),
|
|
.flash_cs(flash_cs),
|
|
.flash_dq(flash_dq)
|
|
);
|
|
|
|
typedef enum bit [7:0] {
|
|
FLASH_CMD_PAGE_PROGRAM = 8'h02,
|
|
FLASH_CMD_READ_STATUS_1 = 8'h05,
|
|
FLASH_CMD_WRITE_ENABLE = 8'h06,
|
|
FLASH_CMD_BLOCK_ERASE_64KB = 8'hD8,
|
|
FLASH_CMD_FAST_READ_QUAD_IO = 8'hEB
|
|
} e_flash_cmd;
|
|
|
|
typedef enum {
|
|
FLASH_STATUS_1_BUSY = 0
|
|
} e_flash_status_1;
|
|
|
|
typedef enum bit [3:0] {
|
|
STATE_IDLE,
|
|
STATE_WRITE_ENABLE,
|
|
STATE_ERASE,
|
|
STATE_PROGRAM_START,
|
|
STATE_PROGRAM,
|
|
STATE_PROGRAM_END,
|
|
STATE_WAIT,
|
|
STATE_READ_START,
|
|
STATE_READ,
|
|
STATE_READ_END
|
|
} e_state;
|
|
|
|
e_state state;
|
|
logic [2:0] counter;
|
|
logic valid_counter;
|
|
logic [23:0] current_address;
|
|
|
|
always_ff @(posedge clk) begin
|
|
start <= 1'b0;
|
|
finish <= 1'b0;
|
|
flash_scb.erase_done <= 1'b0;
|
|
mem_bus.ack <= 1'b0;
|
|
|
|
if (reset) begin
|
|
state <= STATE_IDLE;
|
|
end else begin
|
|
if (!busy && (start || finish)) begin
|
|
counter <= counter + 1'd1;
|
|
end
|
|
|
|
case (state)
|
|
STATE_IDLE: begin
|
|
output_enable <= 1'b1;
|
|
quad_enable <= 1'b0;
|
|
counter <= 3'd0;
|
|
if (flash_scb.erase_pending) begin
|
|
state <= STATE_WRITE_ENABLE;
|
|
end else if (mem_bus.request) begin
|
|
current_address <= {mem_bus.address[23:1], 1'b0};
|
|
if (mem_bus.write) begin
|
|
state <= STATE_WRITE_ENABLE;
|
|
end else begin
|
|
state <= STATE_READ_START;
|
|
end
|
|
end
|
|
end
|
|
|
|
STATE_WRITE_ENABLE: begin
|
|
case (counter)
|
|
3'd0: begin
|
|
start <= 1'b1;
|
|
wdata <= FLASH_CMD_WRITE_ENABLE;
|
|
end
|
|
3'd1: begin
|
|
finish <= 1'b1;
|
|
wdata <= 8'd4;
|
|
if (!busy) begin
|
|
counter <= 3'd0;
|
|
if (flash_scb.erase_pending) begin
|
|
state <= STATE_ERASE;
|
|
end else begin
|
|
state <= STATE_PROGRAM_START;
|
|
end
|
|
end
|
|
end
|
|
endcase
|
|
end
|
|
|
|
STATE_ERASE: begin
|
|
case (counter)
|
|
3'd0: begin
|
|
start <= 1'b1;
|
|
wdata <= FLASH_CMD_BLOCK_ERASE_64KB;
|
|
end
|
|
3'd1: begin
|
|
start <= 1'b1;
|
|
wdata <= flash_scb.erase_block;
|
|
end
|
|
3'd2: begin
|
|
start <= 1'b1;
|
|
wdata <= 8'd0;
|
|
end
|
|
3'd3: begin
|
|
start <= 1'b1;
|
|
wdata <= 8'd0;
|
|
end
|
|
3'd4: begin
|
|
finish <= 1'b1;
|
|
wdata <= 8'd4;
|
|
if (!busy) begin
|
|
flash_scb.erase_done <= 1'b1;
|
|
counter <= 3'd0;
|
|
state <= STATE_WAIT;
|
|
end
|
|
end
|
|
endcase
|
|
end
|
|
|
|
STATE_PROGRAM_START: begin
|
|
case (counter)
|
|
3'd0: begin
|
|
start <= 1'b1;
|
|
wdata <= FLASH_CMD_PAGE_PROGRAM;
|
|
end
|
|
3'd1: begin
|
|
start <= 1'b1;
|
|
wdata <= mem_bus.address[23:16];
|
|
end
|
|
3'd2: begin
|
|
start <= 1'b1;
|
|
wdata <= mem_bus.address[15:8];
|
|
end
|
|
3'd3: begin
|
|
start <= 1'b1;
|
|
wdata <= mem_bus.address[7:0];
|
|
if (!busy) begin
|
|
counter <= 3'd0;
|
|
state <= STATE_PROGRAM;
|
|
end
|
|
end
|
|
endcase
|
|
end
|
|
|
|
STATE_PROGRAM: begin
|
|
case (counter)
|
|
3'd0: begin
|
|
start <= 1'b1;
|
|
wdata <= mem_bus.wdata[15:8];
|
|
end
|
|
3'd1: begin
|
|
start <= 1'b1;
|
|
wdata <= mem_bus.wdata[7:0];
|
|
if (!busy) begin
|
|
mem_bus.ack <= 1'b1;
|
|
current_address <= current_address + 2'd2;
|
|
end
|
|
end
|
|
3'd2: begin
|
|
if (current_address[7:0] == 8'h00) begin
|
|
state <= STATE_PROGRAM_END;
|
|
end else if (flash_scb.erase_pending) begin
|
|
state <= STATE_PROGRAM_END;
|
|
end else if (mem_bus.request && !mem_bus.ack) begin
|
|
if (!mem_bus.write || (mem_bus.address[23:0] != current_address)) begin
|
|
state <= STATE_PROGRAM_END;
|
|
end else begin
|
|
counter <= 3'd0;
|
|
end
|
|
end
|
|
end
|
|
endcase
|
|
end
|
|
|
|
STATE_PROGRAM_END: begin
|
|
finish <= 1'b1;
|
|
wdata <= 8'd4;
|
|
if (!busy) begin
|
|
counter <= 3'd0;
|
|
state <= STATE_WAIT;
|
|
end
|
|
end
|
|
|
|
STATE_WAIT: begin
|
|
case (counter)
|
|
3'd0: begin
|
|
start <= 1'b1;
|
|
output_enable <= 1'b1;
|
|
wdata <= FLASH_CMD_READ_STATUS_1;
|
|
end
|
|
3'd1: begin
|
|
start <= 1'b1;
|
|
output_enable <= 1'b0;
|
|
end
|
|
3'd2: begin
|
|
finish <= 1'b1;
|
|
wdata <= 8'd0;
|
|
end
|
|
3'd3: begin
|
|
counter <= counter;
|
|
end
|
|
endcase
|
|
if (valid) begin
|
|
if (rdata[FLASH_STATUS_1_BUSY]) begin
|
|
counter <= 3'd0;
|
|
end else begin
|
|
state <= STATE_IDLE;
|
|
end
|
|
end
|
|
end
|
|
|
|
STATE_READ_START: begin
|
|
case (counter)
|
|
3'd0: begin
|
|
start <= 1'b1;
|
|
wdata <= FLASH_CMD_FAST_READ_QUAD_IO;
|
|
end
|
|
3'd1: begin
|
|
start <= 1'b1;
|
|
quad_enable <= 1'b1;
|
|
wdata <= mem_bus.address[23:16];
|
|
end
|
|
3'd2: begin
|
|
start <= 1'b1;
|
|
wdata <= mem_bus.address[15:8];
|
|
end
|
|
3'd3: begin
|
|
start <= 1'b1;
|
|
wdata <= mem_bus.address[7:0];
|
|
end
|
|
3'd4: begin
|
|
start <= 1'b1;
|
|
wdata <= 8'hFF;
|
|
end
|
|
3'd5: begin
|
|
start <= 1'b1;
|
|
end
|
|
3'd6: begin
|
|
start <= 1'b1;
|
|
if (!busy) begin
|
|
counter <= 3'd0;
|
|
valid_counter <= 1'b0;
|
|
state <= STATE_READ;
|
|
end
|
|
end
|
|
endcase
|
|
end
|
|
|
|
STATE_READ: begin
|
|
case (counter)
|
|
3'd0: begin
|
|
start <= 1'b1;
|
|
output_enable <= 1'b0;
|
|
end
|
|
3'd1: begin
|
|
start <= 1'b1;
|
|
end
|
|
3'd2: begin end
|
|
3'd3: begin
|
|
if (flash_scb.erase_pending) begin
|
|
state <= STATE_READ_END;
|
|
end else if (mem_bus.request && !mem_bus.ack) begin
|
|
if (mem_bus.write || (mem_bus.address[23:0] != current_address)) begin
|
|
state <= STATE_READ_END;
|
|
end else begin
|
|
start <= 1'b1;
|
|
counter <= 3'd0;
|
|
end
|
|
end
|
|
end
|
|
endcase
|
|
if (valid) begin
|
|
valid_counter <= ~valid_counter;
|
|
if (valid_counter) begin
|
|
mem_bus.ack <= 1'b1;
|
|
counter <= counter + 1'd1;
|
|
current_address <= current_address + 2'd2;
|
|
end
|
|
end
|
|
end
|
|
|
|
STATE_READ_END: begin
|
|
finish <= 1'b1;
|
|
wdata <= 8'd0;
|
|
if (!busy) begin
|
|
state <= STATE_IDLE;
|
|
end
|
|
end
|
|
|
|
default: begin
|
|
state <= STATE_IDLE;
|
|
end
|
|
endcase
|
|
end
|
|
end
|
|
|
|
always_ff @(posedge clk) begin
|
|
if (valid) begin
|
|
mem_bus.rdata <= {mem_bus.rdata[7:0], rdata};
|
|
end
|
|
end
|
|
|
|
endmodule
|