305 lines
9.6 KiB
Systemverilog
305 lines
9.6 KiB
Systemverilog
module sdram_adapter(
|
|
input logic i_cpuclk,
|
|
input logic i_arst, // Async Reset
|
|
input logic i_sysclk, // Controller Clock (100MHz)
|
|
input logic i_sdrclk, // t_su and t_wd clock (200MHz)
|
|
input logic i_tACclk, // t_ac clock (200MHz)
|
|
|
|
input logic i_cs, // Chip select
|
|
input logic i_rwb, // Read/Write. Write is low
|
|
input logic [24:0] i_addr, // Input address. Byte addressed
|
|
|
|
input logic [7:0] i_data, // Input Data
|
|
output logic [7:0] o_data, // Output data
|
|
|
|
output o_sdr_init_done,
|
|
|
|
output o_wait,
|
|
|
|
output o_sdr_CKE,
|
|
output o_sdr_n_CS,
|
|
output o_sdr_n_RAS,
|
|
output o_sdr_n_CAS,
|
|
output o_sdr_n_WE,
|
|
output [1:0] o_sdr_BA,
|
|
output [12:0] o_sdr_ADDR,
|
|
output [15:0] o_sdr_DATA,
|
|
output [15:0] o_sdr_DATA_oe,
|
|
input [15:0] i_sdr_DATA,
|
|
output [1:0] o_sdr_DQM
|
|
);
|
|
|
|
logic [1:0] w_sdr_CKE;
|
|
logic [1:0] w_sdr_n_CS;
|
|
logic [1:0] w_sdr_n_RAS;
|
|
logic [1:0] w_sdr_n_CAS;
|
|
logic [1:0] w_sdr_n_WE;
|
|
logic [3:0] w_sdr_BA;
|
|
logic [25:0] w_sdr_ADDR;
|
|
logic [31:0] w_sdr_DATA;
|
|
logic [31:0] w_sdr_DATA_oe;
|
|
logic [3:0] w_sdr_DQM;
|
|
|
|
assign o_sdr_CKE = w_sdr_CKE[0]; //Using SOFT ddio, ignore second cycle
|
|
assign o_sdr_n_CS = w_sdr_n_CS[0];
|
|
assign o_sdr_n_RAS = w_sdr_n_RAS[0];
|
|
assign o_sdr_n_CAS = w_sdr_n_CAS[0];
|
|
assign o_sdr_n_WE = w_sdr_n_WE[0];
|
|
assign o_sdr_BA = w_sdr_BA[0+:2];
|
|
assign o_sdr_ADDR = w_sdr_ADDR[0+:13];
|
|
assign o_sdr_DATA = w_sdr_DATA[0+:16];
|
|
assign o_sdr_DATA_oe = w_sdr_DATA_oe[0+:16];
|
|
assign o_sdr_DQM = w_sdr_DQM[0+:2];
|
|
|
|
// What should happen when the cpu writes something?
|
|
// 1. Address should already be calculated from the memory mapper, don't need to worry about it
|
|
// 2. Data byte position needs to be determined. Each write is 32 bits, so the dm bits need to
|
|
// be set and the byte shifted to the correct position
|
|
// 3. write enable and last should be set high. Only ever do bursts of 1.
|
|
// 4. Sample wr_ack and when it goes high, release write_enable and last
|
|
|
|
// What should happen when the cpu reads something?
|
|
// 1. Address should already be calculated from the memory mapper, don't need to worry about it
|
|
// 2. read_enable and last should be set high. Only ever to bursts of 1.
|
|
// 3. Sample rd_ack and when it goes high, release read_enable and last
|
|
// 4. Sample read_valid signal. When it is high, grab the data on the on the bus.
|
|
// The returned data will be 16 bit, so you need to extract the correct byte. (or will it be 32?)
|
|
|
|
// when writing, the write data is only valid on a falling edge.
|
|
// Really all of this should be done on falling edges.
|
|
// But basically if we are in access, and cpuclk goes low, go back to wait.
|
|
// If something actually happened, we would be in one of the read/write states.
|
|
|
|
enum bit [1:0] {ACCESS, READ_WAIT, WRITE_WAIT, WAIT} state, next_state;
|
|
|
|
logic w_read, w_write, w_last;
|
|
logic [23:0] w_addr, r_addr;
|
|
logic [31:0] w_data_i, w_data_o;
|
|
logic [3:0] w_dm, r_dm;
|
|
|
|
logic w_wr_ack, w_rd_ack, w_rd_valid;
|
|
|
|
logic [7:0] data, _data;
|
|
logic w_data_valid;
|
|
|
|
logic [31:0] r_write_data;
|
|
|
|
logic [1:0] counter, next_counter;
|
|
|
|
always @(posedge i_sysclk) begin
|
|
if (i_arst) begin
|
|
state <= WAIT;
|
|
counter <= '0;
|
|
end else begin
|
|
state <= next_state;
|
|
counter <= next_counter;
|
|
r_write_data <= w_data_i;
|
|
r_addr <= w_addr;
|
|
r_dm <= w_dm;
|
|
end
|
|
|
|
if (w_data_valid)
|
|
o_data <= _data;
|
|
end
|
|
|
|
logic r_wait;
|
|
logic _r_wait;
|
|
assign o_wait = r_wait;
|
|
|
|
// we need to assert rdy low until a falling edge if a reset happens
|
|
|
|
always @(posedge i_sysclk or posedge i_arst) begin
|
|
if (i_arst == '1) begin
|
|
r_wait <= '0;
|
|
_r_wait <= '0;
|
|
end else begin
|
|
if (o_dbg_ref_req) begin
|
|
r_wait <= '1;
|
|
end else if (i_cpuclk == '1) begin
|
|
_r_wait <= '1;
|
|
end
|
|
|
|
if (i_cpuclk == '0) begin
|
|
if (_r_wait) begin
|
|
_r_wait <= '0;
|
|
r_wait <= '0;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
//because of timing issues, We really need to trigger
|
|
//the write on the falling edge of phi2. There is a 2ns
|
|
//delay between the rising edge of phi2 and data valid
|
|
//Since the address is valid on the previous falling edge,
|
|
//Reads can occur on the rising edge I guess.
|
|
|
|
|
|
//so basically cpu clock goes high when cs goes high we go into a priming state
|
|
//where we wait until cs goes low. when cpu clock is low, do the actual write.
|
|
|
|
//in terms of the existing state, the access state needs to only do something
|
|
//if selected AND cpu_clock is low. If cpu clock is high, we should be in wait,
|
|
//and after the read/write is complete we should also go back to wait.
|
|
|
|
//actually that may only apply to writes, since reads should occur at the rising
|
|
//edge of i_cpuclk
|
|
|
|
//Starts out in state 0 with cpuclk low and cs high.
|
|
//Then, cpuclk goes high. This is now a valid time to read
|
|
//After this, cpuclk goes low again, this is now a valid time to write.
|
|
//so basically if cpuclk goes low when cs is low, go to wait state
|
|
|
|
//what I am thinking is basically 2 states like before. wait and access.
|
|
//we go to access when cpuclk is high and cs is high.
|
|
//we can read as soon as we want if rwn is high.
|
|
//BUT if rwb is low then we have to wait untl cpuclk goes low again.
|
|
|
|
|
|
always_comb begin
|
|
next_state = state;
|
|
next_counter = counter;
|
|
|
|
w_addr = '0;
|
|
w_dm = '0;
|
|
w_read = '0;
|
|
w_write = '0;
|
|
w_last = '0;
|
|
w_data_i = '0;
|
|
w_data_valid = '0;
|
|
_data = 0;
|
|
|
|
unique case (state)
|
|
WAIT: begin
|
|
if (i_cs & i_cpuclk)
|
|
next_state = ACCESS;
|
|
end
|
|
|
|
ACCESS: begin
|
|
// only do something if selected
|
|
if (i_cs) begin
|
|
w_addr = {{i_addr[24:2]}, {1'b0}};; // divide by 2, set last bit to 0
|
|
|
|
if (i_rwb) begin //read
|
|
w_read = '1;
|
|
w_last = '1;
|
|
// dm is not needed for reads?
|
|
if (w_rd_ack) next_state = READ_WAIT;
|
|
end else begin //write
|
|
w_data_i = i_data << (8*i_addr[1:0]);
|
|
//w_data_i = {4{i_data}}; //does anything get through?
|
|
w_dm = ~(4'b1 << i_addr[1:0]);
|
|
if (~i_cpuclk) begin
|
|
w_write = '1;
|
|
w_last = '1;
|
|
next_state = WRITE_WAIT;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
WRITE_WAIT: begin
|
|
// stay in this state until write is acknowledged.
|
|
w_write = '1;
|
|
w_last = '1;
|
|
w_data_i = r_write_data;
|
|
w_dm = r_dm;
|
|
w_addr = r_addr;
|
|
if (w_wr_ack) next_state = WAIT;
|
|
end
|
|
|
|
READ_WAIT: begin
|
|
if (w_rd_valid) begin
|
|
w_data_valid = '1;
|
|
_data = w_data_o[8*i_addr[1:0]+:8];
|
|
end
|
|
|
|
// you must wait until the next cycle!
|
|
if (~i_cpuclk) begin
|
|
next_state = WAIT;
|
|
end
|
|
end
|
|
|
|
endcase
|
|
end
|
|
|
|
//this seems scuffed
|
|
logic [23:0] addr_mux_out;
|
|
always_comb begin
|
|
if (state == ACCESS) begin
|
|
addr_mux_out = w_addr;
|
|
end else begin
|
|
addr_mux_out = r_addr;
|
|
end
|
|
end
|
|
|
|
logic o_dbg_tRTW_done;
|
|
logic o_dbg_ref_req;
|
|
logic o_dbg_wr_ack;
|
|
logic o_dbg_rd_ack;
|
|
logic [1:0] o_dbg_n_CS;
|
|
logic [1:0] o_dbg_n_RAS;
|
|
logic [1:0] o_dbg_n_CAS;
|
|
logic [1:0] o_dbg_n_WE;
|
|
logic [3:0] o_dbg_BA;
|
|
logic [25:0] o_dbg_ADDR;
|
|
logic [31:0] o_dbg_DATA_out;
|
|
logic [31:0] o_dbg_DATA_in;
|
|
logic sdr_init_done;
|
|
logic [3:0] o_sdr_state;
|
|
|
|
assign o_ref_req = o_dbg_ref_req;
|
|
assign o_sdr_init_done = sdr_init_done;
|
|
|
|
|
|
sdram_controller u_sdram_controller(
|
|
.i_arst(i_arst), //Positive Controller Reset
|
|
.i_sysclk(i_sysclk), //Controller Clock (100MHz)
|
|
.i_sdrclk(i_sdrclk), //t_su and t_ac clock. Double sysclk (200MHz)
|
|
.i_tACclk(i_tACclk), //t_ac clock. Also double sysclk, but different pll for tuning
|
|
.i_pll_locked(1'b1), //There exists a pll locked output from the pll, not sure why they don't use it.
|
|
|
|
.i_we(w_write), //Write enable. Can only be de-asserted if i_last is asserted and o_wr_ack is sampled high.
|
|
.i_re(w_read), //Read enable. Can only be de-asserted if i_last is asserted and o_rd_ack is sampled high.
|
|
.i_last(w_last), //Set to high to indicate the last transfer of a burst write or read.
|
|
.i_addr(addr_mux_out), //SDRAM physical address B R C. For half rate, only even addresses.
|
|
.i_din(r_write_data), //Data to write to SDRAM. Twice normal width when running at half speed (hence the even addresses)
|
|
.i_dm(r_dm), //dm (r_dm)
|
|
.o_dout(w_data_o), //Data read from SDRAM, doubled as above.
|
|
.o_sdr_init_done(sdr_init_done), //Indicates that the SDRAM initialization is done.
|
|
.o_wr_ack(w_wr_ack), //Write acknowledge, handshake with we
|
|
.o_rd_ack(w_rd_ack), //Read acknowledge, handshake with re
|
|
.o_rd_valid(w_rd_valid),//Read valid. The data on o_dout is valid
|
|
|
|
.o_sdr_CKE(w_sdr_CKE),
|
|
.o_sdr_n_CS(w_sdr_n_CS),
|
|
.o_sdr_n_RAS(w_sdr_n_RAS),
|
|
.o_sdr_n_CAS(w_sdr_n_CAS),
|
|
.o_sdr_n_WE(w_sdr_n_WE),
|
|
.o_sdr_BA(w_sdr_BA),
|
|
.o_sdr_ADDR(w_sdr_ADDR),
|
|
.o_sdr_DATA(w_sdr_DATA),
|
|
.o_sdr_DATA_oe(w_sdr_DATA_oe),
|
|
.i_sdr_DATA({{16'b0}, {i_sdr_DATA}}),
|
|
.o_sdr_DQM(w_sdr_DQM),
|
|
|
|
//Does include debug signals.
|
|
|
|
.o_sdr_state(o_sdr_state),
|
|
|
|
.o_dbg_tRTW_done ( o_dbg_tRTW_done ),
|
|
.o_dbg_ref_req ( o_dbg_ref_req ),
|
|
.o_dbg_wr_ack ( o_dbg_wr_ack ),
|
|
.o_dbg_rd_ack ( o_dbg_rd_ack ),
|
|
.o_dbg_n_CS ( o_dbg_n_CS ),
|
|
.o_dbg_n_RAS ( o_dbg_n_RAS ),
|
|
.o_dbg_n_CAS ( o_dbg_n_CAS ),
|
|
.o_dbg_n_WE ( o_dbg_n_WE ),
|
|
.o_dbg_BA ( o_dbg_BA ),
|
|
.o_dbg_ADDR ( o_dbg_ADDR ),
|
|
.o_dbg_DATA_out ( o_dbg_DATA_out ),
|
|
.o_dbg_DATA_in ( o_dbg_DATA_in )
|
|
);
|
|
|
|
endmodule
|