1
0
mirror of https://github.com/mist-devel/mist-board.git synced 2026-04-27 04:47:18 +00:00

Archie: 128bit SDRAM interface (by Sorgelig)

This commit is contained in:
Gyorgy Szombathelyi
2019-10-18 20:56:39 +02:00
parent 7122e993fc
commit 375c423a85
4 changed files with 220 additions and 315 deletions

View File

@@ -155,6 +155,7 @@ wire sirq_n;
wire ram_cs;
wire vid_we;
wire snd_ack, snd_req;
wire [31:0] cpu_dout;
memc MEMC(
@@ -170,7 +171,8 @@ memc MEMC(
.cpu_we ( cpu_we ),
.cpu_sel ( cpu_sel ),
.cpu_ack ( cpu_ack ),
.cpu_err ( cpu_err ),
.cpu_err ( cpu_err ),
.cpu_dout ( cpu_dout ),
// memory interface
.mem_addr_o ( MEM_ADDR_O ),
@@ -180,6 +182,7 @@ memc MEMC(
.mem_sel_o ( MEM_SEL_O ),
.mem_we_o ( MEM_WE_O ),
.mem_cti_o ( MEM_CTI_O ),
.mem_dat_i ( MEM_DAT_I ),
// vidc interface
.hsync ( HSYNC ),
@@ -390,7 +393,7 @@ assign cpu_dat_i = floppy_en ? {24'd0, floppy_dat_o} :
latches_en ? {24'd0, latches_dat_o} :
podules_en ? {16'd0, pod_dat_o} :
ioc_cs & ~ioc_sext ? {24'd0, ioc_dat_o} :
ram_cs ? MEM_DAT_I :
ram_cs ? cpu_dout :
32'hFFFF_FFFF;
assign I2C_CLOCK = ioc_cout[1];

View File

@@ -37,6 +37,7 @@ module memc(
input cpu_cyc,
output cpu_err,
output cpu_ack,
output [31:0] cpu_dout,
input [25:0] cpu_address,
input [3:0] cpu_sel,
@@ -47,6 +48,7 @@ module memc(
output mem_cyc_o,
output mem_we_o,
output [3:0] mem_sel_o,
input [31:0] mem_dat_i,
input mem_ack_i,
output [2:0] mem_cti_o, // burst / normal
@@ -127,13 +129,8 @@ localparam REG_SendN = 3'b101;
localparam REG_Sptr = 3'b110;
localparam REG_Ctrl = 3'b111;
wire table_valid;
wire err;
wire memw;
wire logcs;
wire vidc_cs;
wire mem_virtual;
wire[25:0] phys_address;
wire table_valid;
memc_translator PAGETABLES(
@@ -174,9 +171,21 @@ initial begin
end
always @(posedge clkcpu) begin
reg [31:0] cache_data[4];
reg cache_valid;
reg [23:4] cache_addr;
reg cache_ack;
if (rst_i == 1'b1) begin
assign cpu_dout = cache_data[caddr[3:2]];
always @(posedge clkcpu) begin
reg cache_rcv, cache_test;
reg [1:0] cache_cnt;
reg [1:0] cache_wraddr;
cache_ack <= 0;
if (rst_i) begin
vid_init <= INITIAL_SCREEN_BASE;
cur_init <= INITIAL_CURSOR_BASE;
@@ -191,15 +200,33 @@ always @(posedge clkcpu) begin
memc_control[11] <= 1'b0; // disable sound dma on reset.
dma_request_r <= 1'b0;
cache_rcv <= 0;
cache_valid <= 0;
cache_test <= 0;
end else begin
if(cache_rcv & mem_ack_i) begin
cache_data[cache_wraddr] <= mem_dat_i;
cache_wraddr <= cache_wraddr + 1'd1;
cache_cnt <= cache_cnt + 1'd1;
if(cache_cnt == 2) cache_ack <= 1;
if(&cache_cnt) begin
cache_rcv <= 0;
cache_valid <= 1;
end
end
dma_request_r <= dma_request;
// cpu cycle.
if (cpu_cyc & cpu_stb) begin
cache_test <= 1;
if(cache_valid & (cache_addr == caddr[23:4]) & ~cpu_mem_we) begin
// cache hit
if(~cache_test) cache_ack <= 1;
end
else begin
// logic to ensure that the rom overlay gets deactivated.
if (cpu_address[25:24] == 2'b11) begin
@@ -209,16 +236,18 @@ always @(posedge clkcpu) begin
// ensure no video cycle is active or about to start.
if (~dma_request_r & ~dma_in_progress) begin
cpu_load <= 1'b1;
if(~cpu_load) begin
if(cpu_mem_we) begin
if(cache_addr == caddr[23:4]) cache_valid <= 0;
end
else begin
{cache_addr,cache_wraddr} <= caddr[23:2];
cache_valid <= 0;
cache_rcv <= 1;
cache_cnt <= 0;
end
end
// prevent the cpu hogging the bus.
if (cpu_load & dma_request_r & (cpu_ack | cpu_err)) begin
cpu_load <= 1'b0;
end
if (memw) begin
@@ -233,11 +262,9 @@ always @(posedge clkcpu) begin
REG_Cinit: cur_init <= {cpu_address[16:2], 4'b0000};
REG_Sstart: begin
$display("Sstart: %x", {cpu_address[16:2], 4'b0000});
snd_next_valid <= 1'b1;
snd_start <= {cpu_address[16:2], 4'b0000};
end
REG_SendN: begin
@@ -269,11 +296,13 @@ always @(posedge clkcpu) begin
endcase
end
end
end else begin
cpu_load <= 1'b0;
cpu_load <= 0;
cache_rcv <= 0;
cache_test <= 0;
end
// video dma stuff.
@@ -338,12 +367,12 @@ always @(posedge clkcpu) begin
if ((vidak & vid_load) == 1'b1) begin
// advance the pointer to the next location.
vid_address <= vid_address + 4'd4;
vid_address <= vid_address + 19'd4;
end else if ((vidak & cur_load) == 1'b1) begin
// advance the cursor pointer to the next location.
cur_address <= cur_address + 4'd4;
cur_address <= cur_address + 19'd4;
end
@@ -364,7 +393,7 @@ always @(posedge clkcpu) begin
if ((sndak & snd_load) == 1'b1) begin
// advance the pointer to the next location.
snd_sptr <= snd_sptr + 4'd4;
snd_sptr <= snd_sptr + 19'd4;
end
end else begin
@@ -394,35 +423,38 @@ wire [21:2] ram_page = memc_control[3:2] == 2'b00 ? {3'd0, cpu_address[18:2]}:
assign mem_addr_o = vid_load ? {5'd0, vid_address[18:2]} :
cur_load ? {5'd0, cur_address[18:2]} :
snd_load ? {5'd0, snd_sptr[18:2]} :
phycs ? {2'd0, ram_page} : // use physical memory
caddr;
wire [23:2] caddr = phycs ? {2'd0, ram_page} : // use physical memory
romcs ? {3'b010, cpu_address[20:2]} : // use 2mb and up for rom space.
table_valid & logcs ? phys_address[23:2] : 22'd0; // use logical memory.
// does this cpu cycle need to go to external RAM/ROM?
assign cpu_ram_cycle = cpu_cyc & cpu_stb & (table_valid | phycs | romcs);
//assign cpu_ram_cycle = cpu_cyc & cpu_stb & (table_valid | phycs | romcs);
assign mem_cyc_o = cpu_load ? cpu_cyc : dma_in_progress;
assign mem_cyc_o = cpu_load ? cpu_cyc & ~err : dma_in_progress;
assign mem_stb_o = cpu_load ? cpu_stb : dma_in_progress;
assign mem_sel_o = cpu_load ? cpu_sel : 4'b1111;
assign mem_we_o = cpu_load ? cpu_we & (phycs & spvmd | table_valid & logcs) & ~romcs : 1'b0;
assign mem_cti_o = cpu_load ? 3'b000 : 3'b010;
assign mem_we_o = cpu_load ? cpu_mem_we : 1'b0;
assign mem_cti_o = 3'b010;
wire cpu_mem_we = cpu_we & ((phycs & spvmd) | (table_valid & logcs)) & ~romcs;
assign address_valid = (logcs & table_valid) | rom_low_cs| ioc_cs | memw | tablew | vidc_cs | (phycs & ~cpu_we) | (phycs & spvmd & cpu_we) | romcs;
assign err = ~address_valid;
wire err = ~address_valid;
assign cpu_ack = cpu_load ? mem_ack_i & ~err : 1'b0;
assign cpu_ack = (mem_we_o ? mem_ack_i : cache_ack) & ~err;
assign cpu_err = cpu_load ? mem_ack_i & err : 1'b0;
assign tablew = cpu_load & cpu_cyc & cpu_we & spvmd & (cpu_address[25:23] == 3'b111) & (cpu_address[12] == 0) & (cpu_address[7] == 0); // &3800000+
assign memw = cpu_load & cpu_cyc & cpu_we & spvmd & (cpu_address[25:21] == 5'b11011); // &3600000
wire memw = cpu_load & cpu_cyc & cpu_we & spvmd & (cpu_address[25:21] == 5'b11011); // &3600000
assign vidw = cpu_load & cpu_cyc & cpu_we & vidc_cs; // &3400000
// bus chip selects
assign logcs = cpu_address[25] == 1'b0; // 0000000-&1FFFFFF
wire logcs = cpu_address[25] == 1'b0; // 0000000-&1FFFFFF
assign phycs = cpu_address[25:24] == 2'b10; //&2000000 - &2FFFFFF
assign ioc_cs = spvmd & (cpu_address[25:22] == 4'b1100); //&3000000 - &33FFFFF
assign vidc_cs = spvmd & (cpu_address[25:21] == 5'b11010); // &3400000 - &35FFFFF (WE & SPVMD)
wire vidc_cs = spvmd & (cpu_address[25:21] == 5'b11010); // &3400000 - &35FFFFF (WE & SPVMD)
assign rom_low_cs = (cpu_address[25:22] == 4'b1101);
assign romcs = ((cpu_address[25:23] == 3'b111) | (cpu_address[25:19] == 7'h00) & rom_overlay);
@@ -433,6 +465,6 @@ assign sndak = cpu_load ? 1'b0 : sound_dma_ip & mem_ack_i;
assign sirq_n = snd_next_valid;
assign ram_cs = table_valid | phycs | romcs;
assign mem_virtual = table_valid & ~cpu_address[25];
//wire mem_virtual= table_valid & ~cpu_address[25];
endmodule

View File

@@ -1,47 +0,0 @@
/* sdram_defines.v
Copyright (c) 2013-2014, Stephen J. Leary
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Stephen J. Leary nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL STEPHEN J. LEARY BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
localparam RASCAS_DELAY = 3'd3; // tRCD=20ns -> 3 cycles@128MHz
localparam BURST_LENGTH = 3'b000; // 000=1, 001=2, 010=4, 011=8, 111 = continuous.
localparam ACCESS_TYPE = 1'b0; // 0=sequential, 1=interleaved
localparam CAS_LATENCY = 3'd3; // 2/3 allowed
localparam OP_MODE = 2'b00; // only 00 (standard operation) allowed
localparam NO_WRITE_BURST = 1'b1; // 0= write burst enabled, 1=only single access write
localparam WRITE_BURST = 1'b0; // 0= write burst enabled, 1=only single access write
localparam RFC_DELAY = 4'd7; // tRFC=66ns -> 9 cycles@128MHz
// all possible commands
localparam CMD_INHIBIT = 4'b1111;
localparam CMD_NOP = 4'b0111;
localparam CMD_ACTIVE = 4'b0011;
localparam CMD_READ = 4'b0101;
localparam CMD_WRITE = 4'b0100;
localparam CMD_BURST_TERMINATE = 4'b0110;
localparam CMD_PRECHARGE = 4'b0010;
localparam CMD_AUTO_REFRESH = 4'b0001;
localparam CMD_LOAD_MODE = 4'b0000;

View File

@@ -1,5 +1,4 @@
/* sdram_top.v
/*
Copyright (c) 2013-2014, Stephen J. Leary
All rights reserved.
@@ -26,105 +25,115 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
module sdram_top (
module sdram_top
(
// interface to the MT48LC16M16 chip
input sd_clk, // sdram is accessed at 128MHz
input sd_rst, // reset the sdram controller.
output sd_cke, // clock enable.
inout reg[15:0]sd_dq, // 16 bit bidirectional data bus
output reg[12:0]sd_addr, // 13 bit multiplexed address bus
output reg[1:0] sd_dqm = 2'b00, // two byte masks
output reg[1:0] sd_ba = 2'b00, // two banks
output sd_cs_n, // a single chip select
output sd_we_n, // write enable
output sd_ras_n, // row address select
output sd_cas_n, // columns address select
output reg sd_ready = 0, // sd ready.
input sd_clk, // sdram is accessed at 128MHz
input sd_rst, // reset the sdram controller.
output sd_cke, // clock enable.
inout reg[15:0] sd_dq, // 16 bit bidirectional data bus
output reg[12:0] sd_addr, // 13 bit multiplexed address bus
output [1:0] sd_dqm, // two byte masks
output reg [1:0] sd_ba, // two banks
output sd_cs_n, // a single chip select
output sd_we_n, // write enable
output sd_ras_n, // row address select
output sd_cas_n, // columns address select
output reg sd_ready, // sd ready.
// cpu/chipset interface
input wb_clk, // 32MHz chipset clock to which sdram state machine is synchonized
input [31:0] wb_dat_i, // data input from chipset/cpu
output reg[31:0]wb_dat_o = 0, // data output to chipset/cpu
output reg wb_ack = 0,
input [23:0] wb_adr, // lower 2 bits are ignored.
input [3:0] wb_sel, //
input [2:0] wb_cti, // cycle type.
input wb_stb, //
input wb_cyc, // cpu/chipset requests cycle
input wb_we // cpu/chipset requests write
input wb_clk, // 32MHz chipset clock to which sdram state machine is synchonized
input [31:0] wb_dat_i, // data input from chipset/cpu
output reg[31:0] wb_dat_o = 0, // data output to chipset/cpu
output reg wb_ack = 0,
input [23:0] wb_adr, // lower 2 bits are ignored.
input [3:0] wb_sel, //
input [2:0] wb_cti, // cycle type.
input wb_stb, //
input wb_cyc, // cpu/chipset requests cycle
input wb_we // cpu/chipset requests write
);
`include "sdram_defines.v"
localparam RASCAS_DELAY = 3'd3; // tRCD=20ns -> 3 cycles@128MHz
localparam BURST_LENGTH = 3'b011; // 000=1, 001=2, 010=4, 011=8, 111 = continuous.
localparam ACCESS_TYPE = 1'b0; // 0=sequential, 1=interleaved
localparam CAS_LATENCY = 3'd3; // 2/3 allowed
localparam OP_MODE = 2'b00; // only 00 (standard operation) allowed
localparam NO_WRITE_BURST = 1'b1; // 0= write burst enabled, 1=only single access write
localparam WRITE_BURST = 1'b0; // 0= write burst enabled, 1=only single access write
localparam RFC_DELAY = 4'd7; // tRFC=66ns -> 9 cycles@128MHz
// all possible commands
localparam CMD_NOP = 4'b0111;
localparam CMD_ACTIVE = 4'b0011;
localparam CMD_READ = 4'b0101;
localparam CMD_WRITE = 4'b0100;
localparam CMD_BURST_TERMINATE = 4'b0110;
localparam CMD_PRECHARGE = 4'b0010;
localparam CMD_AUTO_REFRESH = 4'b0001;
localparam CMD_LOAD_MODE = 4'b0000;
localparam MODE = { 3'b000, NO_WRITE_BURST, OP_MODE, CAS_LATENCY, ACCESS_TYPE, BURST_LENGTH};
reg [3:0] t;
reg [4:0] reset;
reg [3:0] t;
reg [4:0] reset;
reg[31:0] sd_dat[4]; // data output to chipset/cpu
reg[31:0] sd_dat = 0; // data output to chipset/cpu
reg[31:0] sd_dat_nxt = 0; // data output to chipset/cpu
reg sd_stb = 1'b0; // copy of the wishbone bus signal.
reg sd_we = 1'b0; // copy of the wishbone bus signal.
reg sd_cyc = 1'b0; // copy of the wishbone bus signal.
reg sd_burst = 1'b0;
reg [3:0] sd_cycle= 4'd0;
reg sd_done = 1'b0;
reg [3:0] sd_cmd = 4'd0; // current command sent to sd ram
reg [3:0] sd_cmd; // current command sent to sd ram
reg [9:0] sd_refresh = 10'd0;
reg sd_auto_refresh = 1'b0;
wire sd_req = wb_stb & wb_cyc & ~wb_ack;
reg [11:0] sd_active_row[3:0];
reg [3:0] sd_bank_active;
wire [1:0] sd_bank = wb_adr[22:21];
wire [11:0] sd_row = wb_adr[20:9];
wire sd_req = wb_stb & wb_cyc & ~wb_ack;
wire sd_reading = wb_stb & wb_cyc & ~wb_we;
wire sd_writing = wb_stb & wb_cyc & wb_we;
initial begin
t = 4'd0;
reset = 5'h1f;
sd_addr = 13'd0;
sd_cmd = CMD_INHIBIT;
t = 4'd0;
reset = 5'h1f;
sd_cmd = CMD_NOP;
sd_ready= 0;
end
localparam CYCLE_PRECHARGE = 4'd0;
localparam CYCLE_RAS_START = 4'd3;
localparam CYCLE_IDLE = 4'd0;
localparam CYCLE_RAS_START = CYCLE_IDLE;
localparam CYCLE_RFSH_START = CYCLE_RAS_START;
localparam CYCLE_CAS0 = CYCLE_RAS_START + RASCAS_DELAY;
localparam CYCLE_CAS1 = CYCLE_CAS0 + 4'd1;
localparam CYCLE_CAS2 = CYCLE_CAS1 + 4'd1;
localparam CYCLE_CAS3 = CYCLE_CAS2 + 4'd1;
localparam CYCLE_READ0 = CYCLE_CAS0 + CAS_LATENCY + 4'd1;
localparam CYCLE_READ1 = CYCLE_READ0+ 1'd1;
localparam CYCLE_READ2 = CYCLE_READ1+ 1'd1;
localparam CYCLE_READ3 = CYCLE_READ2+ 1'd1;
localparam CYCLE_END = CYCLE_READ3+ 1'd1;
localparam CYCLE_RFSH_END = CYCLE_RFSH_START + RFC_DELAY;
localparam CYCLE_CAS0 = CYCLE_RAS_START + RASCAS_DELAY;
localparam CYCLE_CAS1 = CYCLE_CAS0 + 4'd1;
localparam CYCLE_CAS2 = CYCLE_CAS1 + 4'd1;
localparam CYCLE_CAS3 = CYCLE_CAS2 + 4'd1;
localparam CYCLE_READ0 = CYCLE_CAS0 + CAS_LATENCY + 4'd1;
localparam CYCLE_READ1 = CYCLE_READ0+ 1'd1;
localparam CYCLE_READ2 = CYCLE_READ1+ 1'd1;
localparam CYCLE_READ3 = CYCLE_READ2+ 1'd1;
localparam CYCLE_READ4 = CYCLE_READ3+ 1'd1;
localparam CYCLE_READ5 = CYCLE_READ4+ 1'd1;
localparam CYCLE_READ6 = CYCLE_READ5+ 1'd1;
localparam CYCLE_READ7 = CYCLE_READ6+ 1'd1;
localparam CYCLE_RFSH_END = CYCLE_RFSH_START + RFC_DELAY;
localparam RAM_CLK = 128000000;
localparam REFRESH_PERIOD = (RAM_CLK / (16 * 8192)) - CYCLE_END;
`ifdef VERILATOR
reg [15:0] sd_q;
assign sd_dq = (sd_writing && (sd_cycle == CYCLE_CAS1 || sd_cycle == CYCLE_CAS2)) ? sd_q : 16'bZZZZZZZZZZZZZZZZ;
`endif
localparam RAM_CLK = 120000000;
localparam REFRESH_PERIOD = (RAM_CLK / (16 * 8192));
always @(posedge sd_clk) begin
reg sd_reqD, sd_reqD2;
reg sd_newreq;
reg [3:0] sd_cycle = CYCLE_IDLE;
reg [2:0] word;
`ifndef VERILATOR
sd_dq <= 16'bZZZZZZZZZZZZZZZZ;
`endif
sd_dq <= 16'bZZZZZZZZZZZZZZZZ;
sd_cmd <= CMD_NOP;
sd_reqD <= sd_req;
if(~sd_reqD & sd_req) sd_newreq <= 1;
if (sd_rst) begin
t <= 4'd0;
t <= 0;
reset <= 5'h1f;
sd_addr <= 13'd0;
sd_ready <= 0;
sd_addr <= 0;
sd_ready <= 0;
sd_ba <= 0;
end else begin
if (!sd_ready) begin
t <= t + 4'd1;
@@ -138,7 +147,7 @@ always @(posedge sd_clk) begin
if(reset == 13) begin
$display("precharging all banks");
sd_cmd <= CMD_PRECHARGE;
sd_addr[10] <= 1'b1; // precharge all banks
sd_addr[10] <= 1'b1; // precharge all banks
end
if(reset == 2) begin
@@ -152,222 +161,130 @@ always @(posedge sd_clk) begin
sd_addr <= MODE;
end
if(reset == 0) sd_ready <= 1;
if(!reset) sd_ready <= 1;
word <= 0;
end
end else begin
// bring the wishbone bus signal into the ram clock domain.
sd_we <= wb_we;
if (sd_req) begin
sd_stb <= wb_stb;
sd_cyc <= wb_cyc;
end
sd_refresh <= sd_refresh + 9'd1;
if(word) begin
word <= word + 1'd1;
sd_dat[word[2:1]][{word[0],4'b0000} +:16] <= sd_dq;
end
// this is the auto refresh code.
// it kicks in so that 8192 auto refreshes are
// issued in a 64ms period. Other bus operations
// are stalled during this period.
if ((sd_refresh > REFRESH_PERIOD) && (sd_cycle == 4'd0)) begin
sd_auto_refresh <= 1'b1;
if ((sd_refresh > REFRESH_PERIOD) && !sd_cycle) begin
sd_auto_refresh<= 1'b1;
sd_refresh <= 10'd0;
sd_cmd <= CMD_PRECHARGE;
sd_addr[10] <= 1;
sd_bank_active <= 0;
sd_cmd <= CMD_AUTO_REFRESH;
end else if (sd_auto_refresh) begin
// while the cycle is active count.
sd_cycle <= sd_cycle + 3'd1;
case (sd_cycle)
CYCLE_RFSH_START: begin
sd_cmd <= CMD_AUTO_REFRESH;
end
CYCLE_RFSH_END: begin
if(sd_cycle == CYCLE_RFSH_END) begin
// reset the count.
sd_auto_refresh <= 1'b0;
sd_cycle <= 4'd0;
sd_auto_refresh <= 0;
sd_cycle <= CYCLE_IDLE;
end
endcase
end
else begin
// count while the cycle is active
if(sd_cycle != CYCLE_IDLE) sd_cycle <= sd_cycle + 3'd1;
end else if (sd_cyc | (sd_cycle != 0) | (sd_cycle == 0 && sd_req)) begin
// while the cycle is active count.
sd_cycle <= sd_cycle + 3'd1;
case (sd_cycle)
CYCLE_PRECHARGE: begin
if (~sd_bank_active[sd_bank])
sd_cycle <= CYCLE_RAS_START;
else if (sd_active_row[sd_bank] == sd_row)
sd_cycle <= CYCLE_CAS0 - 1'd1; // FIXME: Why doesn't work without -1?
else begin
sd_cmd <= CMD_PRECHARGE;
sd_addr[10] <= 0;
sd_ba <= sd_bank;
end
end
CYCLE_RAS_START: begin
sd_cmd <= CMD_ACTIVE;
sd_addr <= { 1'b0, sd_row };
sd_ba <= sd_bank;
sd_active_row[sd_bank] <= sd_row;
sd_bank_active[sd_bank] <= 1;
if(sd_reading) begin
sd_dqm <= 2'b00;
end else begin
sd_dqm <= 2'b11;
case(sd_cycle)
CYCLE_IDLE: begin
if(sd_newreq) begin
sd_cmd <= CMD_ACTIVE;
sd_addr <= wb_adr[21:10];
sd_ba <= wb_adr[23:22];
sd_cycle <= sd_cycle + 3'd1;
end
end
// this is the first CAS cycle
CYCLE_CAS0: begin
// always, always read on a 32bit boundary and completely ignore the lsb of wb_adr.
sd_addr <= { 4'b0000, wb_adr[23], wb_adr[8:2], 1'b0 }; // no auto precharge
sd_dqm <= ~wb_sel[1:0];
sd_ba <= sd_bank;
sd_addr <= { 4'b0000, wb_adr[9:1] }; // no auto precharge
if (sd_reading) begin
sd_cmd <= CMD_READ;
sd_cmd <= CMD_READ;
sd_addr[10] <= 1; // auto precharge
end else if (sd_writing) begin
sd_cmd <= CMD_WRITE;
`ifdef VERILATOR
sd_q <= wb_dat_i[15:0];
`else
sd_dq <= wb_dat_i[15:0];
`endif
sd_cmd <= CMD_WRITE;
sd_addr[12:11] <= ~wb_sel[1:0];
sd_dq <= wb_dat_i[15:0];
end
end
CYCLE_CAS1: begin
// now we access the second part of the 32 bit location.
sd_addr <= { 4'b0000, wb_adr[23], wb_adr[8:2], 1'b1 }; // no auto precharge
sd_dqm <= ~wb_sel[3:2];
if (sd_reading) begin
sd_cmd <= CMD_READ;
if (burst_mode & can_burst) begin
sd_burst <= 1'b1;
end
end else if (sd_writing) begin
sd_cmd <= CMD_WRITE;
sd_done <= ~sd_done;
`ifdef VERILATOR
sd_q <= wb_dat_i[31:16];
`else
sd_dq <= wb_dat_i[31:16];
`endif
end
end
CYCLE_CAS2: begin
if (sd_burst) begin
// always, always read on a 32bit boundary and completely ignore the lsb of wb_adr.
sd_addr <= { 4'b0000, wb_adr[23], wb_adr[8:3], 2'b10 }; // no auto precharge
sd_dqm <= ~wb_sel[1:0];
if (sd_reading) begin
sd_cmd <= CMD_READ;
end
end
end
CYCLE_CAS3: begin
if (sd_burst) begin
// always, always read on a 32bit boundary and completely ignore the lsb of wb_adr.
sd_addr <= { 4'b0000, wb_adr[23], wb_adr[8:3], 2'b11 }; // no auto precharge
sd_dqm <= ~wb_sel[3:2];
if (sd_reading) begin
sd_cmd <= CMD_READ;
end
if (sd_writing) begin
sd_addr[10] <= 1; // auto precharge
sd_addr[0] <= 1;
sd_cmd <= CMD_WRITE;
sd_addr[12:11] <= ~wb_sel[3:2];
sd_done <= ~sd_done;
sd_newreq <= 0;
sd_dq <= wb_dat_i[31:16];
end
end
CYCLE_READ0: begin
if (sd_reading) begin
sd_dat[15:0] <= sd_dq;
sd_dat[0][15:0]<= sd_dq;
word <= 1;
end else begin
if (sd_writing) sd_cycle <= CYCLE_END;
end
if (sd_writing) sd_cycle <= CYCLE_IDLE;
end
end
CYCLE_READ1: begin
if (sd_reading) begin
sd_dat[31:16] <= sd_dq;
sd_done <= ~sd_done;
end
sd_done <= ~sd_done;
sd_newreq <= 0;
end
CYCLE_READ2: begin
if (sd_reading) begin
sd_dat_nxt[15:0] <= sd_dq;
end
end
CYCLE_READ3: begin
if (sd_reading) begin
sd_dat_nxt[31:16] <= sd_dq;
end
end
CYCLE_END: begin
sd_burst <= 1'b0;
sd_cyc <= 1'b0;
sd_stb <= 1'b0;
CYCLE_READ5: begin
sd_cycle <= CYCLE_IDLE;
end
endcase
end else begin
sd_cycle <= 4'd0;
sd_burst <= 1'b0;
end
end
end
end
reg wb_burst;
always @(posedge wb_clk) begin
reg sd_doneD;
reg [1:0] word;
sd_doneD <= sd_done;
wb_ack <= (sd_done ^ sd_doneD) & ~wb_ack;
wb_ack <= 0;
if(word) word <= word + 1'd1;
if (wb_stb & wb_cyc) begin
if ((sd_done ^ sd_doneD) & ~wb_ack) begin
wb_dat_o <= sd_dat;
wb_burst <= burst_mode;
wb_dat_o <= sd_dat[0];
word <= ~wb_cti[2] & (wb_cti[1] ^ wb_cti[0]); // burst constant/incremental
wb_ack <= 1;
end
if (word) begin
wb_dat_o <= sd_dat[word];
wb_ack <= 1;
end
if (wb_ack & wb_burst) begin
wb_ack <= 1'b1;
wb_burst <= 1'b0;
wb_dat_o <= sd_dat_nxt;
end
end else begin
wb_burst <= 1'b0;
end
else begin
word <= 0;
end
end
wire burst_mode = wb_cti == 3'b010;
wire can_burst = wb_adr[2] === 1'b0;
wire sd_reading = sd_stb & sd_cyc & ~sd_we;
wire sd_writing = sd_stb & sd_cyc & sd_we;
// drive control signals according to current command
assign sd_cs_n = sd_cmd[3];
assign sd_ras_n = sd_cmd[2];
assign sd_cas_n = sd_cmd[1];
assign sd_we_n = sd_cmd[0];
assign sd_cke = 1'b1;
assign sd_cke = 1'b1;
assign sd_dqm = sd_addr[12:11];
endmodule