mirror of
https://github.com/Gehstock/Mist_FPGA.git
synced 2026-01-25 03:25:52 +00:00
Kickman: new dual-port SDRAM controller, load sound ROM to SDRAM
This commit is contained in:
parent
b707a5dc83
commit
fd0ecf2c7c
@ -87,8 +87,8 @@ set_input_delay -clock [get_clocks {pll|altpll_component|auto_generated|pll1|clk
|
||||
#**************************************************************
|
||||
|
||||
set_output_delay -add_delay -clock_fall -clock [get_clocks {SPI_SCK}] 1.000 [get_ports {SPI_DO}]
|
||||
set_output_delay -add_delay -clock_fall -clock [get_clocks {pll|altpll_component|auto_generated|pll1|clk[1]}] 1.000 [get_ports {AUDIO_L}]
|
||||
set_output_delay -add_delay -clock_fall -clock [get_clocks {pll|altpll_component|auto_generated|pll1|clk[1]}] 1.000 [get_ports {AUDIO_R}]
|
||||
set_output_delay -add_delay -clock_fall -clock [get_clocks {pll|altpll_component|auto_generated|pll1|clk[0]}] 1.000 [get_ports {AUDIO_L}]
|
||||
set_output_delay -add_delay -clock_fall -clock [get_clocks {pll|altpll_component|auto_generated|pll1|clk[0]}] 1.000 [get_ports {AUDIO_R}]
|
||||
set_output_delay -add_delay -clock_fall -clock [get_clocks {pll|altpll_component|auto_generated|pll1|clk[0]}] 1.000 [get_ports {LED}]
|
||||
set_output_delay -add_delay -clock_fall -clock [get_clocks {pll|altpll_component|auto_generated|pll1|clk[0]}] 1.000 [get_ports {VGA_*}]
|
||||
|
||||
@ -116,9 +116,6 @@ set_clock_groups -asynchronous -group [get_clocks {SPI_SCK}] -group [get_clocks
|
||||
set_multicycle_path -to {VGA_*[*]} -setup 2
|
||||
set_multicycle_path -to {VGA_*[*]} -hold 1
|
||||
|
||||
set_multicycle_path -from [get_clocks {pll|altpll_component|auto_generated|pll1|clk[0]}] -to [get_clocks {pll|altpll_component|auto_generated|pll1|clk[1]}] -setup 2
|
||||
set_multicycle_path -from [get_clocks {pll|altpll_component|auto_generated|pll1|clk[0]}] -to [get_clocks {pll|altpll_component|auto_generated|pll1|clk[1]}] -hold 1
|
||||
|
||||
#**************************************************************
|
||||
# Set Maximum Delay
|
||||
#**************************************************************
|
||||
|
||||
@ -4,6 +4,10 @@
|
||||
-- 3 November 2019
|
||||
--
|
||||
-- VGA Only
|
||||
-- KICKMAN.ROM file : Main ROM + Sound ROM
|
||||
-- 1200-a-ur.b3 + 1300-b-ur.b4 + 1400-c-ur.b5 + 1500-d-ur.d4 + 1600-e-ur.d5 + 1700-f-ur.d6 +
|
||||
-- 4200-a.a7 + 4300-b.a8 + 4400-c.a9 + 4500-d.a10
|
||||
|
||||
---------------------------------------------------------------------------------
|
||||
-- DE10_lite Top level for Kick (Midway MCR) by Dar (darfpga@aol.fr) (19/10/2019)
|
||||
-- http://darfpga.blogspot.fr
|
||||
|
||||
Binary file not shown.
@ -59,6 +59,7 @@ localparam CONF_STR = {
|
||||
|
||||
assign LED = ~ioctl_downl;
|
||||
assign SDRAM_CLK = clk_sys;
|
||||
assign SDRAM_CKE = 1;
|
||||
|
||||
wire clk_sys;
|
||||
wire pll_locked;
|
||||
@ -83,6 +84,9 @@ wire [3:0] g, r, b;
|
||||
wire [14:0] rom_addr;
|
||||
wire [15:0] rom_do;
|
||||
wire rom_rd;
|
||||
wire [13:0] snd_addr;
|
||||
wire [15:0] snd_do;
|
||||
wire snd_rd;
|
||||
wire ioctl_downl;
|
||||
wire [7:0] ioctl_index;
|
||||
wire ioctl_wr;
|
||||
@ -100,20 +104,57 @@ data_io data_io(
|
||||
.ioctl_addr ( ioctl_addr ),
|
||||
.ioctl_dout ( ioctl_dout )
|
||||
);
|
||||
|
||||
sdram cart(
|
||||
|
||||
reg port1_req, port2_req;
|
||||
sdram sdram(
|
||||
.*,
|
||||
.init ( ~pll_locked ),
|
||||
.init_n ( pll_locked ),
|
||||
.clk ( clk_sys ),
|
||||
.wtbt ( 2'b00 ),
|
||||
.dout ( rom_do ),
|
||||
.din ( {ioctl_dout, ioctl_dout} ),
|
||||
.addr ( ioctl_downl ? ioctl_addr : rom_addr ),
|
||||
.we ( ioctl_downl & ioctl_wr ),
|
||||
.rd ( !ioctl_downl & rom_rd),
|
||||
.ready()
|
||||
|
||||
// port1 used for main CPU
|
||||
.port1_req ( port1_req ),
|
||||
.port1_ack (),
|
||||
.port1_a ( ioctl_downl ? ioctl_addr[23:1] : rom_addr[14:1] ),
|
||||
.port1_ds ( ioctl_downl ? {ioctl_addr[0], ~ioctl_addr[0]} : 2'b11 ),
|
||||
.port1_we ( ioctl_downl ),
|
||||
.port1_d ( {ioctl_dout, ioctl_dout} ),
|
||||
.port1_q ( rom_do ),
|
||||
|
||||
// port2 for sound board
|
||||
.port2_req ( port2_req ),
|
||||
.port2_ack (),
|
||||
.port2_a ( ioctl_downl ? ioctl_addr[23:1] - 16'h3000 : snd_addr[13:1] ),
|
||||
.port2_ds ( ioctl_downl ? {ioctl_addr[0], ~ioctl_addr[0]} : 2'b11 ),
|
||||
.port2_we ( ioctl_downl ),
|
||||
.port2_d ( {ioctl_dout, ioctl_dout} ),
|
||||
.port2_q ( snd_do )
|
||||
);
|
||||
|
||||
always @(posedge clk_sys) begin
|
||||
reg [14:1] rom_addr_last;
|
||||
reg [13:1] snd_addr_last;
|
||||
reg ioctl_wr_last = 0;
|
||||
|
||||
ioctl_wr_last <= ioctl_wr;
|
||||
if (ioctl_downl) begin
|
||||
snd_addr_last <= 13'h1fff;
|
||||
rom_addr_last <= 14'h3fff;
|
||||
if (~ioctl_wr_last && ioctl_wr) begin
|
||||
port1_req <= ~port1_req;
|
||||
port2_req <= ~port2_req;
|
||||
end
|
||||
end else begin
|
||||
if (rom_rd && rom_addr_last != rom_addr[14:1]) begin
|
||||
rom_addr_last <= rom_addr[14:1];
|
||||
port1_req <= ~port1_req;
|
||||
end
|
||||
if (snd_rd && snd_addr_last != snd_addr[13:1]) begin
|
||||
snd_addr_last <= snd_addr[13:1];
|
||||
port2_req <= ~port2_req;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
reg reset = 1;
|
||||
reg rom_loaded = 0;
|
||||
always @(posedge clk_sys) begin
|
||||
@ -146,8 +187,11 @@ kick kick(
|
||||
.btn_left(m_left),
|
||||
.btn_right(m_right),
|
||||
.cpu_rom_addr ( rom_addr ),
|
||||
.cpu_rom_do ( rom_do[7:0] ),
|
||||
.cpu_rom_rd ( rom_rd )
|
||||
.cpu_rom_do ( rom_addr[0] ? rom_do[15:8] : rom_do[7:0] ),
|
||||
.cpu_rom_rd ( rom_rd ),
|
||||
.snd_rom_addr ( snd_addr ),
|
||||
.snd_rom_do ( snd_addr[0] ? snd_do[15:8] : snd_do[7:0] ),
|
||||
.snd_rom_rd ( snd_rd )
|
||||
);
|
||||
|
||||
mist_video #(.COLOR_DEPTH(4), .SD_HCNT_WIDTH(10)) mist_video(
|
||||
|
||||
@ -171,7 +171,11 @@ port(
|
||||
dbg_cpu_addr : out std_logic_vector(15 downto 0);
|
||||
cpu_rom_addr : out std_logic_vector(14 downto 0);
|
||||
cpu_rom_do : in std_logic_vector(7 downto 0);
|
||||
cpu_rom_rd : out std_logic
|
||||
cpu_rom_rd : out std_logic;
|
||||
|
||||
snd_rom_addr : out std_logic_vector(13 downto 0);
|
||||
snd_rom_do : in std_logic_vector(7 downto 0);
|
||||
snd_rom_rd : out std_logic
|
||||
);
|
||||
end kick;
|
||||
|
||||
@ -744,7 +748,7 @@ port map(
|
||||
);
|
||||
|
||||
cpu_rom_addr <= cpu_addr(14 downto 0);
|
||||
cpu_rom_rd <= '1' when cpu_mreq_n = '0' and cpu_addr(15 downto 12) < X"7" else '0';
|
||||
cpu_rom_rd <= '1' when cpu_mreq_n = '0' and cpu_rd_n = '0' and cpu_addr(15 downto 12) < X"7" else '0';
|
||||
|
||||
-- working RAM 0x7000-0x77FF
|
||||
wram : entity work.gen_ram
|
||||
@ -880,6 +884,10 @@ port map(
|
||||
audio_out_l => audio_out_l,
|
||||
audio_out_r => audio_out_r,
|
||||
|
||||
cpu_rom_addr => snd_rom_addr,
|
||||
cpu_rom_do => snd_rom_do,
|
||||
cpu_rom_rd => snd_rom_rd,
|
||||
|
||||
dbg_cpu_addr => open --dbg_cpu_addr
|
||||
);
|
||||
|
||||
|
||||
@ -71,7 +71,11 @@ port(
|
||||
|
||||
audio_out_l : out std_logic_vector(15 downto 0);
|
||||
audio_out_r : out std_logic_vector(15 downto 0);
|
||||
|
||||
|
||||
cpu_rom_addr : out std_logic_vector(13 downto 0);
|
||||
cpu_rom_do : in std_logic_vector(7 downto 0);
|
||||
cpu_rom_rd : out std_logic;
|
||||
|
||||
dbg_cpu_addr : out std_logic_vector(15 downto 0)
|
||||
);
|
||||
end kick_sound_board;
|
||||
@ -98,7 +102,7 @@ architecture struct of kick_sound_board is
|
||||
signal cpu_irq_n : std_logic;
|
||||
signal cpu_m1_n : std_logic;
|
||||
|
||||
signal cpu_rom_do : std_logic_vector( 7 downto 0);
|
||||
-- signal cpu_rom_do : std_logic_vector( 7 downto 0);
|
||||
|
||||
signal wram_we : std_logic;
|
||||
signal wram_do : std_logic_vector( 7 downto 0);
|
||||
@ -430,12 +434,15 @@ port map(
|
||||
);
|
||||
|
||||
-- cpu program ROM 0x0000-0x3FFF
|
||||
rom_cpu : entity work.kick_sound_cpu
|
||||
port map(
|
||||
clk => clock_sndn,
|
||||
addr => cpu_addr(13 downto 0),
|
||||
data => cpu_rom_do
|
||||
);
|
||||
cpu_rom_addr <= cpu_addr(13 downto 0);
|
||||
cpu_rom_rd <= '1' when cpu_mreq_n = '0' and cpu_rd_n = '0' and cpu_addr(15 downto 14) = "00" else '0'; -- 0x0000-0x3FFF
|
||||
|
||||
--rom_cpu : entity work.kick_sound_cpu
|
||||
--port map(
|
||||
-- clk => clock_sndn,
|
||||
-- addr => cpu_addr(13 downto 0),
|
||||
-- data => cpu_rom_do
|
||||
--);
|
||||
|
||||
-- working RAM 0x8000-0x83FF
|
||||
wram : entity work.gen_ram
|
||||
|
||||
11
Arcade_MiST/Midway MCR 1/Kickman_MiST/rtl/pll_mist.ppf
Normal file
11
Arcade_MiST/Midway MCR 1/Kickman_MiST/rtl/pll_mist.ppf
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE pinplan>
|
||||
<pinplan intended_family="Cyclone III" variation_name="pll_mist" megafunction_name="ALTPLL" specifies="all_ports">
|
||||
<global>
|
||||
<pin name="areset" direction="input" scope="external" />
|
||||
<pin name="inclk0" direction="input" scope="external" source="clock" />
|
||||
<pin name="c0" direction="output" scope="external" source="clock" />
|
||||
<pin name="locked" direction="output" scope="external" />
|
||||
|
||||
</global>
|
||||
</pinplan>
|
||||
@ -1,79 +1,126 @@
|
||||
//
|
||||
// sdram.v
|
||||
//
|
||||
// Static RAM controller implementation using SDRAM MT48LC16M16A2
|
||||
// sdram controller implementation for the MiST board
|
||||
// https://github.com/mist-devel/mist-board
|
||||
//
|
||||
// Copyright (c) 2013 Till Harbaum <till@harbaum.org>
|
||||
// Copyright (c) 2019 Gyorgy Szombathelyi
|
||||
//
|
||||
// Copyright (c) 2015,2016 Sorgelig
|
||||
//
|
||||
// Some parts of SDRAM code used from project:
|
||||
// http://hamsterworks.co.nz/mediawiki/index.php/Simple_SDRAM_Controller
|
||||
//
|
||||
// This source file is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published
|
||||
// by the Free Software Foundation, either version 3 of the License, or
|
||||
// This source file is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published
|
||||
// by the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
//
|
||||
// This source file is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ------------------------------------------
|
||||
//
|
||||
// v2.1 - Add universal 8/16 bit mode.
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
module sdram
|
||||
(
|
||||
input init, // reset to initialize RAM
|
||||
input clk, // clock ~100MHz
|
||||
//
|
||||
// SDRAM_* - signals to the MT48LC16M16 chip
|
||||
inout reg [15:0] SDRAM_DQ, // 16 bit bidirectional data bus
|
||||
output reg [12:0] SDRAM_A, // 13 bit multiplexed address bus
|
||||
output reg SDRAM_DQML, // two byte masks
|
||||
output reg SDRAM_DQMH, //
|
||||
output reg [1:0] SDRAM_BA, // two banks
|
||||
output SDRAM_nCS, // a single chip select
|
||||
output SDRAM_nWE, // write enable
|
||||
output SDRAM_nRAS, // row address select
|
||||
output SDRAM_nCAS, // columns address select
|
||||
output SDRAM_CKE, // clock enable
|
||||
//
|
||||
input [1:0] wtbt, // 16bit mode: bit1 - write high byte, bit0 - write low byte,
|
||||
// 8bit mode: 2'b00 - use addr[0] to decide which byte to write
|
||||
// Ignored while reading.
|
||||
//
|
||||
input [24:0] addr, // 25 bit address for 8bit mode. addr[0] = 0 for 16bit mode for correct operations.
|
||||
output [15:0] dout, // data output to cpu
|
||||
input [15:0] din, // data input from cpu
|
||||
input we, // cpu requests write
|
||||
input rd, // cpu requests read
|
||||
output reg ready // dout is valid. Ready to accept new read/write.
|
||||
module sdram (
|
||||
|
||||
// interface to the MT48LC16M16 chip
|
||||
inout reg [15:0] SDRAM_DQ, // 16 bit bidirectional data bus
|
||||
output reg [12:0] SDRAM_A, // 13 bit multiplexed address bus
|
||||
output reg SDRAM_DQML, // two byte masks
|
||||
output reg SDRAM_DQMH, // two byte masks
|
||||
output reg [1:0] SDRAM_BA, // two banks
|
||||
output SDRAM_nCS, // a single chip select
|
||||
output SDRAM_nWE, // write enable
|
||||
output SDRAM_nRAS, // row address select
|
||||
output SDRAM_nCAS, // columns address select
|
||||
|
||||
// cpu/chipset interface
|
||||
input init_n, // init signal after FPGA config to initialize RAM
|
||||
input clk, // sdram clock
|
||||
|
||||
input port1_req,
|
||||
output reg port1_ack,
|
||||
input port1_we,
|
||||
input [23:1] port1_a,
|
||||
input [1:0] port1_ds,
|
||||
input [15:0] port1_d,
|
||||
output [15:0] port1_q,
|
||||
|
||||
input port2_req,
|
||||
output reg port2_ack,
|
||||
input port2_we,
|
||||
input [23:1] port2_a,
|
||||
input [1:0] port2_ds,
|
||||
input [15:0] port2_d,
|
||||
output [15:0] port2_q
|
||||
);
|
||||
|
||||
assign SDRAM_nCS = command[3];
|
||||
assign SDRAM_nRAS = command[2];
|
||||
assign SDRAM_nCAS = command[1];
|
||||
assign SDRAM_nWE = command[0];
|
||||
assign SDRAM_CKE = cke;
|
||||
localparam RASCAS_DELAY = 3'd2; // tRCD=20ns -> 2 cycles@<100MHz
|
||||
localparam BURST_LENGTH = 3'b000; // 000=1, 001=2, 010=4, 011=8
|
||||
localparam ACCESS_TYPE = 1'b0; // 0=sequential, 1=interleaved
|
||||
localparam CAS_LATENCY = 3'd2; // 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
|
||||
|
||||
// no burst configured
|
||||
localparam BURST_LENGTH = 3'b000; // 000=1, 001=2, 010=4, 011=8
|
||||
localparam ACCESS_TYPE = 1'b0; // 0=sequential, 1=interleaved
|
||||
localparam CAS_LATENCY = 3'd2; // 2 for < 100MHz, 3 for >100MHz
|
||||
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 MODE = {3'b000, NO_WRITE_BURST, OP_MODE, CAS_LATENCY, ACCESS_TYPE, BURST_LENGTH};
|
||||
localparam MODE = { 3'b000, NO_WRITE_BURST, OP_MODE, CAS_LATENCY, ACCESS_TYPE, BURST_LENGTH};
|
||||
|
||||
localparam sdram_startup_cycles= 14'd12100;// 100us, plus a little more, @ 100MHz
|
||||
localparam cycles_per_refresh = 14'd186; // (64000*36)/8192-1 Calc'd as (64ms @ 36MHz)/8192 rose
|
||||
localparam startup_refresh_max = 14'b11111111111111;
|
||||
// 64ms/8192 rows = 7.8us -> 842 cycles@108MHz
|
||||
localparam RFRSH_CYCLES = 10'd842;
|
||||
|
||||
// SDRAM commands
|
||||
// ---------------------------------------------------------------------
|
||||
// ------------------------ cycle state machine ------------------------
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
SDRAM state machine for 2 bank interleaved access
|
||||
1 word burst, CL2
|
||||
cmd issued registered
|
||||
0 RAS0 cas1
|
||||
1 ras0
|
||||
2 CAS0 data1 returned
|
||||
3 RAS1 cas0
|
||||
4 ras1
|
||||
5 CAS1 data0 returned
|
||||
*/
|
||||
|
||||
localparam STATE_RAS0 = 3'd0; // first state in cycle
|
||||
localparam STATE_RAS1 = 3'd3; // Second ACTIVE command after RAS0 + tRRD (15ns)
|
||||
localparam STATE_CAS0 = STATE_RAS0 + RASCAS_DELAY; // CAS phase - 3
|
||||
localparam STATE_CAS1 = STATE_RAS1 + RASCAS_DELAY; // CAS phase - 5
|
||||
localparam STATE_READ0 = STATE_CAS0 + CAS_LATENCY + 1'd1; // 7
|
||||
localparam STATE_READ1 = 3'd2;
|
||||
localparam STATE_LAST = 3'd5;
|
||||
|
||||
reg [2:0] t;
|
||||
|
||||
always @(posedge clk) begin
|
||||
t <= t + 1'd1;
|
||||
if (t == STATE_LAST) t <= STATE_RAS0;
|
||||
end
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// --------------------------- startup/reset ---------------------------
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// wait 1ms (32 8Mhz cycles) after FPGA config is done before going
|
||||
// into normal operation. Initialize the ram in the last 16 reset cycles (cycles 15-0)
|
||||
reg [4:0] reset;
|
||||
reg init = 1'b1;
|
||||
always @(posedge clk, negedge init_n) begin
|
||||
if(!init_n) begin
|
||||
reset <= 5'h1f;
|
||||
init <= 1'b1;
|
||||
end else begin
|
||||
if((t == STATE_LAST) && (reset != 0)) reset <= reset - 5'd1;
|
||||
init <= !(reset == 0);
|
||||
end
|
||||
end
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// ------------------ generate ram control signals ---------------------
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// all possible commands
|
||||
localparam CMD_INHIBIT = 4'b1111;
|
||||
localparam CMD_NOP = 4'b0111;
|
||||
localparam CMD_ACTIVE = 4'b0011;
|
||||
@ -84,171 +131,149 @@ localparam CMD_PRECHARGE = 4'b0010;
|
||||
localparam CMD_AUTO_REFRESH = 4'b0001;
|
||||
localparam CMD_LOAD_MODE = 4'b0000;
|
||||
|
||||
reg [13:0] refresh_count = startup_refresh_max - sdram_startup_cycles;
|
||||
reg [3:0] command = CMD_INHIBIT;
|
||||
reg cke = 0;
|
||||
reg [24:0] save_addr;
|
||||
reg [15:0] data;
|
||||
reg [3:0] sd_cmd; // current command sent to sd ram
|
||||
|
||||
assign dout = save_addr[0] ? {data[7:0], data[15:8]} : {data[15:8], data[7:0]};
|
||||
typedef enum
|
||||
{
|
||||
STATE_STARTUP,
|
||||
STATE_OPEN_1,
|
||||
STATE_WRITE,
|
||||
STATE_READ,
|
||||
STATE_IDLE, STATE_IDLE_1, STATE_IDLE_2, STATE_IDLE_3,
|
||||
STATE_IDLE_4, STATE_IDLE_5, STATE_IDLE_6, STATE_IDLE_7
|
||||
} state_t;
|
||||
// drive control signals according to current command
|
||||
assign SDRAM_nCS = sd_cmd[3];
|
||||
assign SDRAM_nRAS = sd_cmd[2];
|
||||
assign SDRAM_nCAS = sd_cmd[1];
|
||||
assign SDRAM_nWE = sd_cmd[0];
|
||||
|
||||
state_t state = STATE_STARTUP;
|
||||
reg [24:1] addr_latch[2];
|
||||
reg [24:1] addr_latch_next[2];
|
||||
reg [15:0] din_latch[2];
|
||||
reg [1:0] oe_latch;
|
||||
reg [1:0] we_latch;
|
||||
reg [1:0] ds[2];
|
||||
|
||||
localparam PORT_NONE = 1'b0;
|
||||
localparam PORT_REQ = 1'b1;
|
||||
|
||||
reg next_port[2];
|
||||
|
||||
reg refresh;
|
||||
reg [10:0] refresh_cnt;
|
||||
wire need_refresh = (refresh_cnt >= RFRSH_CYCLES);
|
||||
|
||||
// PORT1: bank 0,1
|
||||
always @(*) begin
|
||||
if (port1_req ^ port1_ack) begin
|
||||
next_port[0] = PORT_REQ;
|
||||
addr_latch_next[0] = { 1'b0, port1_a };
|
||||
end else begin
|
||||
next_port[0] = PORT_NONE;
|
||||
addr_latch_next[0] = addr_latch[0];
|
||||
end
|
||||
end
|
||||
|
||||
// PORT1: bank 2,3
|
||||
always @(*) begin
|
||||
if ((port2_req ^ port2_ack) && !refresh) begin
|
||||
next_port[1] = PORT_REQ;
|
||||
addr_latch_next[1] = { 1'b1, port2_a };
|
||||
end else begin
|
||||
next_port[1] = PORT_NONE;
|
||||
addr_latch_next[1] = addr_latch[1];
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
reg old_we, old_rd;
|
||||
reg [CAS_LATENCY:0] data_ready_delay;
|
||||
|
||||
reg [15:0] new_data;
|
||||
reg [1:0] new_wtbt;
|
||||
reg new_we;
|
||||
reg new_rd;
|
||||
reg save_we = 1;
|
||||
|
||||
|
||||
command <= CMD_NOP;
|
||||
refresh_count <= refresh_count+1'b1;
|
||||
|
||||
data_ready_delay <= {1'b0, data_ready_delay[CAS_LATENCY:1]};
|
||||
|
||||
if(data_ready_delay[0]) data <= SDRAM_DQ;
|
||||
|
||||
case(state)
|
||||
STATE_STARTUP: begin
|
||||
//------------------------------------------------------------------------
|
||||
//-- This is the initial startup state, where we wait for at least 100us
|
||||
//-- before starting the start sequence
|
||||
//--
|
||||
//-- The initialisation is sequence is
|
||||
//-- * de-assert SDRAM_CKE
|
||||
//-- * 100us wait,
|
||||
//-- * assert SDRAM_CKE
|
||||
//-- * wait at least one cycle,
|
||||
//-- * PRECHARGE
|
||||
//-- * wait 2 cycles
|
||||
//-- * REFRESH,
|
||||
//-- * tREF wait
|
||||
//-- * REFRESH,
|
||||
//-- * tREF wait
|
||||
//-- * LOAD_MODE_REG
|
||||
//-- * 2 cycles wait
|
||||
//------------------------------------------------------------------------
|
||||
cke <= 1;
|
||||
SDRAM_DQ <= 16'bZZZZZZZZZZZZZZZZ;
|
||||
SDRAM_DQML <= 1;
|
||||
SDRAM_DQMH <= 1;
|
||||
SDRAM_A <= 0;
|
||||
SDRAM_BA <= 0;
|
||||
|
||||
// All the commands during the startup are NOPS, except these
|
||||
if(refresh_count == startup_refresh_max-31) begin
|
||||
// ensure all rows are closed
|
||||
command <= CMD_PRECHARGE;
|
||||
SDRAM_A[10] <= 1; // all banks
|
||||
SDRAM_BA <= 2'b00;
|
||||
end else if (refresh_count == startup_refresh_max-23) begin
|
||||
// these refreshes need to be at least tREF (66ns) apart
|
||||
command <= CMD_AUTO_REFRESH;
|
||||
end else if (refresh_count == startup_refresh_max-15)
|
||||
command <= CMD_AUTO_REFRESH;
|
||||
else if (refresh_count == startup_refresh_max-7) begin
|
||||
// Now load the mode register
|
||||
command <= CMD_LOAD_MODE;
|
||||
SDRAM_A <= MODE;
|
||||
end
|
||||
|
||||
//------------------------------------------------------
|
||||
//-- if startup is complete then go into idle mode,
|
||||
//-- get prepared to accept a new command, and schedule
|
||||
//-- the first refresh cycle
|
||||
//------------------------------------------------------
|
||||
if(!refresh_count) begin
|
||||
state <= STATE_IDLE;
|
||||
ready <= 1;
|
||||
refresh_count <= 0;
|
||||
end
|
||||
end
|
||||
|
||||
STATE_IDLE_7: state <= STATE_IDLE_6;
|
||||
STATE_IDLE_6: state <= STATE_IDLE_5;
|
||||
STATE_IDLE_5: state <= STATE_IDLE_4;
|
||||
STATE_IDLE_4: state <= STATE_IDLE_3;
|
||||
STATE_IDLE_3: state <= STATE_IDLE_2;
|
||||
STATE_IDLE_2: state <= STATE_IDLE_1;
|
||||
STATE_IDLE_1: begin
|
||||
SDRAM_DQ <= 16'bZZZZZZZZZZZZZZZZ;
|
||||
state <= STATE_IDLE;
|
||||
// mask possible refresh to reduce colliding.
|
||||
if(refresh_count > cycles_per_refresh) begin
|
||||
//------------------------------------------------------------------------
|
||||
//-- Start the refresh cycle.
|
||||
//-- This tasks tRFC (66ns), so 2 idle cycles are needed @ 36MHz
|
||||
//------------------------------------------------------------------------
|
||||
state <= STATE_IDLE_2;
|
||||
command <= CMD_AUTO_REFRESH;
|
||||
refresh_count <= refresh_count - cycles_per_refresh + 1'd1;
|
||||
end
|
||||
end
|
||||
|
||||
STATE_IDLE: begin
|
||||
// Priority is to issue a refresh if one is outstanding
|
||||
if(refresh_count > (cycles_per_refresh<<1)) state <= STATE_IDLE_1;
|
||||
else if(new_rd | new_we) begin
|
||||
new_we <= 0;
|
||||
new_rd <= 0;
|
||||
save_addr<= addr;
|
||||
save_we <= new_we;
|
||||
state <= STATE_OPEN_1;
|
||||
command <= CMD_ACTIVE;
|
||||
SDRAM_A <= addr[13:1];
|
||||
SDRAM_BA <= addr[24:23];
|
||||
end
|
||||
end
|
||||
|
||||
// ACTIVE-to-READ or WRITE delay >20ns (1 cycle @ 36 MHz)(-75)
|
||||
STATE_OPEN_1: begin
|
||||
SDRAM_A <= {4'b0010, save_addr[22:14]};
|
||||
SDRAM_DQML <= save_we & (new_wtbt ? ~new_wtbt[0] : save_addr[0]);
|
||||
SDRAM_DQMH <= save_we & (new_wtbt ? ~new_wtbt[1] : ~save_addr[0]);
|
||||
state <= save_we ? STATE_WRITE : STATE_READ;
|
||||
end
|
||||
|
||||
STATE_READ: begin
|
||||
state <= STATE_IDLE_5;
|
||||
command <= CMD_READ;
|
||||
SDRAM_DQ <= 16'bZZZZZZZZZZZZZZZZ;
|
||||
|
||||
// Schedule reading the data values off the bus
|
||||
data_ready_delay[CAS_LATENCY] <= 1;
|
||||
end
|
||||
|
||||
STATE_WRITE: begin
|
||||
state <= STATE_IDLE_5;
|
||||
command <= CMD_WRITE;
|
||||
SDRAM_DQ <= new_wtbt ? new_data : {new_data[7:0], new_data[7:0]};
|
||||
ready <= 1;
|
||||
end
|
||||
endcase
|
||||
// permanently latch ram data to reduce delays
|
||||
SDRAM_DQ <= 16'bZZZZZZZZZZZZZZZZ;
|
||||
{ SDRAM_DQMH, SDRAM_DQML } <= 2'b11;
|
||||
sd_cmd <= CMD_NOP; // default: idle
|
||||
refresh_cnt <= refresh_cnt + 1'd1;
|
||||
|
||||
if(init) begin
|
||||
state <= STATE_STARTUP;
|
||||
refresh_count <= startup_refresh_max - sdram_startup_cycles;
|
||||
// initialization takes place at the end of the reset phase
|
||||
if(t == STATE_RAS0) begin
|
||||
|
||||
if(reset == 15) begin
|
||||
sd_cmd <= CMD_PRECHARGE;
|
||||
SDRAM_A[10] <= 1'b1; // precharge all banks
|
||||
end
|
||||
|
||||
if(reset == 10 || reset == 8) begin
|
||||
sd_cmd <= CMD_AUTO_REFRESH;
|
||||
end
|
||||
|
||||
if(reset == 2) begin
|
||||
sd_cmd <= CMD_LOAD_MODE;
|
||||
SDRAM_A <= MODE;
|
||||
SDRAM_BA <= 2'b00;
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
// RAS phase
|
||||
// bank 0,1
|
||||
if(t == STATE_RAS0) begin
|
||||
addr_latch[0] <= addr_latch_next[0];
|
||||
{ oe_latch[0], we_latch[0] } <= 2'b00;
|
||||
if (next_port[0] != PORT_NONE) begin
|
||||
sd_cmd <= CMD_ACTIVE;
|
||||
SDRAM_A <= addr_latch_next[0][22:10];
|
||||
SDRAM_BA <= addr_latch_next[0][24:23];
|
||||
{ oe_latch[0], we_latch[0] } <= { ~port1_we, port1_we };
|
||||
ds[0] <= port1_ds;
|
||||
din_latch[0] <= port1_d;
|
||||
end
|
||||
end
|
||||
|
||||
// bank 2,3
|
||||
if(t == STATE_RAS1) begin
|
||||
refresh <= 1'b0;
|
||||
addr_latch[1] <= addr_latch_next[1];
|
||||
{ oe_latch[1], we_latch[1] } <= 2'b00;
|
||||
if (next_port[1] != PORT_NONE) begin
|
||||
sd_cmd <= CMD_ACTIVE;
|
||||
SDRAM_A <= addr_latch_next[1][22:10];
|
||||
SDRAM_BA <= addr_latch_next[1][24:23];
|
||||
{ oe_latch[1], we_latch[1] } <= { ~port1_we, port1_we };
|
||||
ds[1] <= port2_ds;
|
||||
din_latch[1] <= port2_d;
|
||||
end
|
||||
|
||||
if (next_port[1] == PORT_NONE && need_refresh && !we_latch[0] && !oe_latch[0]) begin
|
||||
refresh <= 1'b1;
|
||||
refresh_cnt <= 0;
|
||||
sd_cmd <= CMD_AUTO_REFRESH;
|
||||
end
|
||||
end
|
||||
|
||||
// CAS phase
|
||||
if(t == STATE_CAS0 && (we_latch[0] || oe_latch[0])) begin
|
||||
sd_cmd <= we_latch[0]?CMD_WRITE:CMD_READ;
|
||||
{ SDRAM_DQMH, SDRAM_DQML } <= ~ds[0];
|
||||
if (we_latch[0]) begin
|
||||
SDRAM_DQ <= din_latch[0];
|
||||
port1_ack <= port1_req;
|
||||
end
|
||||
SDRAM_A <= { 4'b0010, addr_latch[0][9:1] }; // auto precharge
|
||||
SDRAM_BA <= addr_latch[0][24:23];
|
||||
end
|
||||
|
||||
if(t == STATE_CAS1 && (we_latch[1] || oe_latch[1])) begin
|
||||
sd_cmd <= we_latch[1]?CMD_WRITE:CMD_READ;
|
||||
{ SDRAM_DQMH, SDRAM_DQML } <= ~ds[1];
|
||||
if (we_latch[1]) begin
|
||||
SDRAM_DQ <= din_latch[1];
|
||||
port2_ack <= port2_req;
|
||||
end
|
||||
SDRAM_A <= { 4'b0010, addr_latch[1][9:1] }; // auto precharge
|
||||
SDRAM_BA <= addr_latch[1][24:23];
|
||||
end
|
||||
|
||||
// Data returned
|
||||
if(t == STATE_READ0 && oe_latch[0]) begin
|
||||
port1_q <= SDRAM_DQ;
|
||||
port1_ack <= port1_req;
|
||||
end
|
||||
if(t == STATE_READ1 && oe_latch[1]) begin
|
||||
port2_q <= SDRAM_DQ;
|
||||
port2_ack <= port2_req;
|
||||
end
|
||||
end
|
||||
|
||||
old_we <= we;
|
||||
old_rd <= rd;
|
||||
if(we & ~old_we) {ready, new_we, new_data, new_wtbt} <= {1'b0, 1'b1, din, wtbt};
|
||||
else
|
||||
if((rd & ~old_rd) || (rd & old_rd & (save_addr != addr))) {ready, new_rd} <= {1'b0, 1'b1};
|
||||
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user