FreeCPU/RAM.v
GabrielTofvesson a3f7a48276 Fixed minor issues in ALU.v
Rewrote RAM.v
  RAM should now support read and write
  RAM CKE will be pulled low when no queries to it are being made
  Added bank-independent read/write systems
  Added parameterized per-bank clock-hogging
2018-10-17 02:34:03 +02:00

189 lines
7.1 KiB
Verilog

/*
* DEVICE INFORMATION:
* HY57V641620FTP-7:
* RAS latency: 2 or 3 cycles
* RCD latency: 2 or 3 cycles
* full address_select available for row selection
* address_select[7:0] available for col selection
*/
module RAM(
input wire clk, // Clock signal
output reg [10:0] RAM_addr, // RAM address buffer
output reg RAM_A10, // RAM address/auto-precharge
output reg [1:0] RAM_bank_sel, // RAM bank selection
inout wire [15:0] RAM_data, // RAM data bus
output wire RAM_clk, // RAM clock signal
output wire RAM_clk_enable, // RAM enable clock
output reg RAM_enable, // RAM chip enable
output reg RAM_strobe_row, // RAM row strobe
output reg RAM_strobe_col, // RAM column strobe
output reg RAM_write_enable, // RAM data bus write enable
input wire read_rq, // Read request (Internal)
input wire write_rq, // Write request (Internal)
input wire [1:0] access_bank, // Which bank to access
output wire op_trigger, // Event trigger wire
input wire [11:0] address_select, // Address selection when accessing RAM,
input wire stop_access, // Close a row
output reg [1:0] op_bank // Bank being accessed when op_trigger is pulled high
);
parameter CPB = 4; // Specifies in bit length how many cycles a bank burst can allocate for itself before other banks are checked
parameter tRCD = 3 + 1; // Assume RCD latency of 3 cycles
parameter tRAS = 3 + 1; // Assume CAS latency of 3 cycles
reg [CPB-1:0] acc_cycles; // Cycles used on current bank
reg [1:0] acc_bank; // Which bank is allocating read cycles
reg [1:0] acc_close[0:3]; // Requests a close of the current row
reg [3:0] acc_type; // The current access type of the bank (0: READ, 1: WRITE)
reg [3:0] acc_state [0:3]; // Current access state for each bank (0: READY, 1: OPENING, 2: tRCD, 3: READ, 4: STOPPING)
reg [3:0] acc_init_callback; // Pull this high to initiate a callback
reg [1:0] event_trigger; // Event triggers drive op_trigger
wire [3:0] acc_event; // Event update callback wire
wire [2:0] callback_timeout[0:3]; // Callback clock cycle definition
Callback #(.ISIZE(3)) cb0(clk, callback_timeout[0], acc_init_callback[0], acc_event[0]);
Callback #(.ISIZE(3)) cb1(clk, callback_timeout[1], acc_init_callback[1], acc_event[1]);
Callback #(.ISIZE(3)) cb2(clk, callback_timeout[2], acc_init_callback[2], acc_event[2]);
Callback #(.ISIZE(3)) cb3(clk, callback_timeout[3], acc_init_callback[3], acc_event[3]);
// Initialize the ram in an inactive state
initial RAM_enable = 1'b1;
initial RAM_strobe_row = 1'b1;
initial RAM_strobe_col = 1'b1;
initial RAM_write_enable = 1'b1;
initial acc_bank = 1'b1;
assign op_trigger = event_trigger[0] ^ event_trigger[1];
assign RAM_clk = clk;
assign RAM_clk_enable = (acc_state[0] | acc_state[1] | acc_state[2] | acc_state[3]) ? 1'b1 : 1'b0;
genvar n;
generate
for(n = 0; n < 4; n = n + 1) begin : gen_callback_timing
assign callback_timeout[n] = acc_state[n][1] ? tRAS : tRCD;
end
endgenerate
integer i;
always @(posedge read_rq or posedge write_rq or posedge acc_event or posedge stop_access or posedge clk) begin
if(read_rq || write_rq) begin
if(acc_state[access_bank] == 4'b0000) begin
RAM_enable <= 1'b0;
RAM_strobe_row <= 1'b0;
RAM_strobe_col <= 1'b1;
RAM_write_enable <= 1'b1;
RAM_bank_sel <= access_bank;
RAM_addr <= {address_select[11], address_select[9:0]};
RAM_A10 <= address_select[10];
acc_type[access_bank] <= read_rq ? 1'b0 : 1'b1;
acc_state[access_bank] <= 4'b0001;
acc_init_callback[access_bank] <= 1'b1;
acc_close[access_bank] <= 1'b0;
end
end
else if(stop_access) begin
if(acc_state[access_bank])
acc_close[access_bank] <= 1'b1;
end
else if(clk) begin
for(i = 0; i < 4; i = i + 1)
if(acc_state[i] == 4'b0010)
acc_init_callback[i] <= 1'b1;
if(~(acc_state[0] | acc_state[1] | acc_state[2] | acc_state[3]))
RAM_enable <= 1'b1;
// Bank access management
acc_cycles[acc_bank] <= acc_cycles[acc_bank] + 1'b1;
if(acc_cycles[acc_bank] == {CPB{1'b1}}) begin
// Close banks as needed
if(acc_close[0]) begin
acc_close[0] <= 1'b0;
RAM_bank_sel <= 2'b00;
acc_state[0] <= 4'b0000;
end
else if(acc_close[1]) begin
acc_close[1] <= 1'b0;
RAM_bank_sel <= 2'b01;
acc_state[1] <= 4'b0000;
end
else if(acc_close[2]) begin
acc_close[2] <= 1'b0;
RAM_bank_sel <= 2'b10;
acc_state[2] <= 4'b0000;
end
else if(acc_close[3]) begin
acc_close[3] <= 1'b0;
RAM_bank_sel <= 2'b11;
acc_state[3] <= 4'b0000;
end
// Increment bank tracker
if(~(acc_close[0] | acc_close[1] | acc_close[2] | acc_close[3])) begin
acc_bank <= acc_bank + 1;
acc_cycles <= {CPB{1'b0}};
end
else begin
// Trigger RAM row-close event
RAM_enable <= 1'b0;
RAM_strobe_row <= 1'b1;
RAM_strobe_col <= 1'b1;
RAM_write_enable <= 1'b0;
end
end
else begin
// Access bank
for(i = 0; i < 4; i = i + 1) begin
// Bank_{i} active and not closing and...
// Enough cycles left or when no other bank is active or...
// Bank_{i+1} at cycle limit and next banks inactive or bank is closing or...
// Bank_{i+2} at cycle limit and next bank inactive or bank is closing or...
// Bank_{i+3} at cycle limit or closing
if(!acc_close[i] && acc_state[i] == 4'b0100 && (
(acc_bank == i && (acc_cycles != {CPB{1'b1}} || (acc_state[(i+1)%4] != 4'b0100 && acc_state[(i+2)%4] != 4'b0100 && acc_state[(i+3)%4] != 4'b0100))) ||
(acc_bank == (i+1)%4 && ((acc_close[(i+1)%4] || acc_cycles == {CPB{1'b1}}) && acc_state[(i+2)%4] != 4'b0100 && acc_state[(i+3)%4] != 4'b0100)) ||
(acc_bank == (i+2)%4 && ((acc_close[(i+2)%4] || acc_cycles == {CPB{1'b1}}) && acc_state[(i+3)%4] != 4'b0100)) ||
(acc_bank == (i+3)%4 && (acc_close[(i+3)%4] || acc_cycles == {CPB{1'b1}}))
)) begin
RAM_bank_sel <= i;
RAM_addr <= {2'b0, RAM_addr[7:0]};
RAM_A10 <= 1'b1;
RAM_strobe_row <= 1'b1;
RAM_strobe_col <= 1'b0;
RAM_write_enable <= ~acc_type[i];
op_bank <= i;
event_trigger[0] <= event_trigger[1] ? 1'b0 : 1'b1;
end
end
end
end
else if(acc_event) begin
for(i = 0; i < 4; i = i + 1) begin
if(acc_state[i] == 4'b0001) begin
RAM_bank_sel <= i;
RAM_strobe_row <= 1'b1;
RAM_strobe_col <= 1'b0;
RAM_write_enable <= ~acc_type[i];
RAM_A10 <= 1;
acc_state[i] <= 4'b0010;
acc_init_callback[i] <= 1'b0;
end
else if(acc_state[i] == 4'b0010) begin
acc_init_callback[i] <= 1'b0;
acc_state[i] <= 4'b0100;
end
end
end
end
// Reset op_trigger by tracking posedge-driven event_trigger
always @(negedge clk) event_trigger[1] <= event_trigger[0] ? 1'b1 : 1'b0;
endmodule