mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-12-26 13:01:55 +01:00
4eaa0b3353
Flash memory module had an issue when ending address was not page (256 bytes) aligned. Now it's possible to write single bytes to the Flash instead of being forced to do 16 bit aligned writes.
501 lines
16 KiB
Systemverilog
501 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 ((start || finish) && !busy) 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
|
|
current_address[0] <= (~mem_bus.wmask[1]);
|
|
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'd5;
|
|
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'd5;
|
|
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 <= current_address[23:16];
|
|
end
|
|
3'd2: begin
|
|
start <= 1'b1;
|
|
wdata <= current_address[15:8];
|
|
end
|
|
3'd3: begin
|
|
start <= 1'b1;
|
|
wdata <= current_address[7:0];
|
|
if (!busy) begin
|
|
counter <= 3'd0 + current_address[0];
|
|
state <= STATE_PROGRAM;
|
|
end
|
|
end
|
|
endcase
|
|
end
|
|
|
|
STATE_PROGRAM: begin
|
|
case (counter)
|
|
3'd0: begin
|
|
start <= 1'b1;
|
|
wdata <= mem_bus.wdata[15:8];
|
|
if (start && !busy) begin
|
|
current_address <= current_address + 1'd1;
|
|
if (!mem_bus.wmask[0]) begin
|
|
counter <= 3'd2;
|
|
mem_bus.ack <= 1'b1;
|
|
end
|
|
end
|
|
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 + 1'd1;
|
|
end
|
|
end
|
|
3'd2: begin
|
|
if (current_address[7:0] == 8'h00) begin
|
|
state <= STATE_PROGRAM_END;
|
|
end else if (mem_bus.request && !mem_bus.ack) begin
|
|
if (mem_bus.write && mem_bus.wmask[1] && (mem_bus.address[23:0] == current_address)) begin
|
|
counter <= 3'd0;
|
|
end else begin
|
|
state <= STATE_PROGRAM_END;
|
|
end
|
|
end else if (!busy) begin
|
|
state <= STATE_PROGRAM_END;
|
|
end
|
|
end
|
|
endcase
|
|
end
|
|
|
|
STATE_PROGRAM_END: begin
|
|
finish <= 1'b1;
|
|
wdata <= 8'd5;
|
|
if (finish && !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 <= current_address[23:16];
|
|
end
|
|
3'd2: begin
|
|
start <= 1'b1;
|
|
wdata <= current_address[15:8];
|
|
end
|
|
3'd3: begin
|
|
start <= 1'b1;
|
|
wdata <= current_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
|