mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-12-29 14:31:54 +01:00
302 lines
8.9 KiB
Verilog
302 lines
8.9 KiB
Verilog
`include "../constants.vh"
|
|
|
|
module flashram_controller (
|
|
input i_clk,
|
|
input i_reset,
|
|
|
|
input [23:0] i_save_address,
|
|
|
|
output o_flashram_read_mode,
|
|
|
|
input i_request,
|
|
input i_write,
|
|
output o_busy,
|
|
output reg o_ack,
|
|
input [14:0] i_address,
|
|
output reg [31:0] o_data,
|
|
input [31:0] i_data,
|
|
|
|
output reg o_mem_request,
|
|
output reg o_mem_write,
|
|
input i_mem_busy,
|
|
input i_mem_ack,
|
|
output reg [3:0] o_mem_bank,
|
|
output reg [23:0] o_mem_address,
|
|
output reg [31:0] o_mem_data,
|
|
input [31:0] i_mem_data
|
|
);
|
|
|
|
// State machine and command decoder
|
|
|
|
localparam [31:0] FLASH_TYPE_ID = 32'h1111_8001;
|
|
localparam [31:0] FLASH_MODEL_ID = 32'h00C2_001D;
|
|
|
|
localparam [7:0] CMD_STATUS_MODE = 8'hD2;
|
|
localparam [7:0] CMD_READID_MODE = 8'hE1;
|
|
localparam [7:0] CMD_READ_MODE = 8'hF0;
|
|
localparam [7:0] CMD_ERASE_SECTOR = 8'h4B;
|
|
localparam [7:0] CMD_ERASE_CHIP = 8'h3C;
|
|
localparam [7:0] CMD_WRITE_MODE = 8'hB4;
|
|
localparam [7:0] CMD_ERASE_START = 8'h78;
|
|
localparam [7:0] CMD_WRITE_START = 8'hA5;
|
|
|
|
localparam STATE_STATUS = 0;
|
|
localparam STATE_ID = 1;
|
|
localparam STATE_READ = 2;
|
|
localparam STATE_ERASE = 3;
|
|
localparam STATE_WRITE = 4;
|
|
localparam STATE_EXECUTE = 5;
|
|
|
|
localparam [1:0] EXECUTE_WRITE = 2'b00;
|
|
localparam [1:0] EXECUTE_ERASE_SECTOR = 2'b10;
|
|
localparam [1:0] EXECUTE_ERASE_CHIP = 2'b11;
|
|
|
|
reg [5:0] r_flashram_state;
|
|
reg [9:0] r_sector_offset;
|
|
reg [1:0] r_execute_mode;
|
|
reg r_execute_start;
|
|
reg r_execute_done;
|
|
|
|
assign o_flashram_read_mode = r_flashram_state[STATE_READ];
|
|
|
|
wire w_cmd_request = i_request && i_write && i_address[14] && !r_flashram_state[STATE_EXECUTE];
|
|
wire [7:0] w_cmd_op = i_data[31:24];
|
|
|
|
always @(posedge i_clk) begin
|
|
r_execute_start <= 1'b0;
|
|
|
|
if (i_reset || r_execute_done) begin
|
|
r_flashram_state <= (1'b1 << STATE_STATUS);
|
|
end else begin
|
|
if (w_cmd_request) begin
|
|
r_flashram_state <= 6'b000000;
|
|
|
|
if (w_cmd_op == CMD_STATUS_MODE) begin
|
|
r_flashram_state[STATE_STATUS] <= 1'b1;
|
|
end
|
|
|
|
if (w_cmd_op == CMD_READID_MODE) begin
|
|
r_flashram_state[STATE_ID] <= 1'b1;
|
|
end
|
|
|
|
if (w_cmd_op == CMD_READ_MODE) begin
|
|
r_flashram_state[STATE_READ] <= 1'b1;
|
|
end
|
|
|
|
if (w_cmd_op == CMD_ERASE_SECTOR) begin
|
|
r_flashram_state[STATE_ERASE] <= 1'b1;
|
|
r_sector_offset <= {i_data[9:7], 7'd0};
|
|
r_execute_mode <= EXECUTE_ERASE_SECTOR;
|
|
end
|
|
|
|
if (w_cmd_op == CMD_ERASE_CHIP) begin
|
|
r_flashram_state[STATE_ERASE] <= 1'b1;
|
|
r_sector_offset <= 10'd0;
|
|
r_execute_mode <= EXECUTE_ERASE_CHIP;
|
|
end
|
|
|
|
if (w_cmd_op == CMD_WRITE_MODE) begin
|
|
r_flashram_state[STATE_WRITE] <= 1'b1;
|
|
end
|
|
|
|
if (w_cmd_op == CMD_ERASE_START) begin
|
|
if (r_flashram_state[STATE_ERASE]) begin
|
|
r_flashram_state[STATE_EXECUTE] <= 1'b1;
|
|
r_execute_start <= 1'b1;
|
|
end
|
|
end
|
|
|
|
if (w_cmd_op == CMD_WRITE_START) begin
|
|
r_flashram_state[STATE_EXECUTE] <= 1'b1;
|
|
r_sector_offset <= i_data[9:0];
|
|
r_execute_mode <= EXECUTE_WRITE;
|
|
r_execute_start <= 1'b1;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
// Status controller
|
|
|
|
reg r_erase_busy;
|
|
reg r_erase_done;
|
|
|
|
reg r_write_busy;
|
|
reg r_write_done;
|
|
|
|
wire [3:0] w_status = {r_erase_done, r_write_done, r_erase_busy, r_write_busy};
|
|
|
|
wire w_status_write_request = i_request && i_write && !i_address[14] && r_flashram_state[STATE_STATUS];
|
|
|
|
always @(posedge i_clk) begin
|
|
if (i_reset) begin
|
|
r_erase_busy <= 1'b0;
|
|
r_write_busy <= 1'b0;
|
|
r_erase_done <= 1'b0;
|
|
r_write_done <= 1'b0;
|
|
end else begin
|
|
if (w_status_write_request) begin
|
|
r_erase_done <= r_erase_done & i_data[3];
|
|
r_write_done <= r_write_done & i_data[1];
|
|
end
|
|
|
|
if (r_execute_start) begin
|
|
if (r_execute_mode == EXECUTE_WRITE) begin
|
|
r_write_busy <= 1'b1;
|
|
r_write_done <= 1'b0;
|
|
end else begin
|
|
r_erase_busy <= 1'b1;
|
|
r_erase_done <= 1'b0;
|
|
end
|
|
end
|
|
|
|
if (r_execute_done) begin
|
|
if (r_execute_mode == EXECUTE_WRITE) begin
|
|
r_write_busy <= 1'b0;
|
|
r_write_done <= 1'b1;
|
|
end else begin
|
|
r_erase_busy <= 1'b0;
|
|
r_erase_done <= 1'b1;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
// Bus controller
|
|
|
|
assign o_busy = 1'b0;
|
|
|
|
wire [31:0] w_write_buffer_o_data;
|
|
|
|
wire w_flashram_controller_read_request = i_request && !i_write && !o_busy && !r_flashram_state[STATE_READ];
|
|
|
|
always @(posedge i_clk) begin
|
|
o_ack <= 1'b0;
|
|
|
|
if (w_flashram_controller_read_request) begin
|
|
o_ack <= 1'b1;
|
|
o_data <= {12'h000, w_status, 12'h000, w_status};
|
|
|
|
if (r_flashram_state[STATE_ID]) begin
|
|
o_data <= i_address[0] ? FLASH_MODEL_ID : FLASH_TYPE_ID;
|
|
end
|
|
|
|
if (r_flashram_state[STATE_WRITE]) begin
|
|
o_data <= w_write_buffer_o_data;
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
// Page write buffer
|
|
|
|
reg [4:0] r_write_buffer_address;
|
|
|
|
wire [31:0] w_write_buffer_o_mem_data;
|
|
|
|
wire w_write_buffer_request = i_request && i_write && !o_busy && !i_address[14] && r_flashram_state[STATE_WRITE];
|
|
|
|
ram_flashram_write_buffer ram_flashram_write_buffer_inst (
|
|
.clock(i_clk),
|
|
|
|
.wren_a(w_write_buffer_request),
|
|
.address_a(i_address[4:0]),
|
|
.data_a(i_data),
|
|
.q_a(w_write_buffer_o_data),
|
|
|
|
.wren_b(i_mem_ack),
|
|
.address_b(r_write_buffer_address),
|
|
.data_b(i_mem_data & w_write_buffer_o_mem_data),
|
|
.q_b(w_write_buffer_o_mem_data)
|
|
);
|
|
|
|
|
|
// Memory controller
|
|
|
|
reg [15:0] r_items_left;
|
|
|
|
wire w_in_execution = !r_execute_start && r_flashram_state[STATE_EXECUTE];
|
|
wire w_read_phase_done = w_in_execution && (r_write_buffer_address == 5'h1F) && !o_mem_write && i_mem_ack;
|
|
wire w_write_phase_done = w_in_execution && (r_items_left == 16'd0) && o_mem_write;
|
|
|
|
wire w_mem_request_successful = o_mem_request && !i_mem_busy;
|
|
wire w_address_reset = r_execute_start || w_read_phase_done;
|
|
wire w_write_buffer_address_increment = o_mem_write ? w_mem_request_successful : i_mem_ack;
|
|
|
|
always @(*) begin
|
|
r_execute_done = w_write_phase_done;
|
|
end
|
|
|
|
always @(posedge i_clk) begin
|
|
if (w_address_reset) begin
|
|
case (r_execute_mode)
|
|
EXECUTE_WRITE: r_items_left <= 16'h20;
|
|
EXECUTE_ERASE_SECTOR: r_items_left <= 16'h1000;
|
|
EXECUTE_ERASE_CHIP: r_items_left <= 16'h8000;
|
|
default: r_items_left <= 16'd0;
|
|
endcase
|
|
end
|
|
|
|
if (w_mem_request_successful) begin
|
|
r_items_left <= r_items_left - 1'd1;
|
|
end
|
|
end
|
|
|
|
always @(posedge i_clk) begin
|
|
if (i_reset) begin
|
|
o_mem_request <= 1'b0;
|
|
end else begin
|
|
if (r_items_left > 16'd0) begin
|
|
o_mem_request <= 1'b1;
|
|
end
|
|
|
|
if (w_mem_request_successful) begin
|
|
o_mem_request <= 1'b0;
|
|
end
|
|
end
|
|
end
|
|
|
|
always @(posedge i_clk) begin
|
|
if (r_execute_start) begin
|
|
if (r_execute_mode == EXECUTE_WRITE) begin
|
|
o_mem_write <= 1'b0;
|
|
end else begin
|
|
o_mem_write <= 1'b1;
|
|
end
|
|
end
|
|
|
|
if (w_read_phase_done) begin
|
|
o_mem_write <= 1'b1;
|
|
end
|
|
end
|
|
|
|
always @(posedge i_clk) begin
|
|
o_mem_bank <= `BANK_SDRAM;
|
|
end
|
|
|
|
always @(posedge i_clk) begin
|
|
if (w_address_reset) begin
|
|
o_mem_address <= i_save_address + {9'd0, r_sector_offset, 5'd0};
|
|
r_write_buffer_address <= 5'd0;
|
|
end else begin
|
|
if (w_mem_request_successful) begin
|
|
o_mem_address <= o_mem_address + 1'd1;
|
|
end
|
|
|
|
if (w_write_buffer_address_increment) begin
|
|
r_write_buffer_address <= r_write_buffer_address + 1'd1;
|
|
end
|
|
end
|
|
end
|
|
|
|
always @(*) begin
|
|
o_mem_data = 32'hFFFF_FFFF;
|
|
if (r_execute_mode == EXECUTE_WRITE) begin
|
|
o_mem_data = w_write_buffer_o_mem_data;
|
|
end
|
|
end
|
|
|
|
endmodule
|