Tidied up

This commit is contained in:
Philip Smart
2020-05-05 23:56:12 +01:00
parent ac1ba0f774
commit d7446ef06c
75 changed files with 0 additions and 510497 deletions

View File

@@ -1,576 +0,0 @@
---------------------------------------------------------------------
---- ----
---- WISHBONE revB2 I2C Master Core; bit-controller ----
---- ----
---- ----
---- Author: Richard Herveille ----
---- richard@asics.ws ----
---- www.asics.ws ----
---- ----
---- Downloaded from: http://www.opencores.org/projects/i2c/ ----
---- ----
---------------------------------------------------------------------
---- ----
---- Copyright (C) 2000 Richard Herveille ----
---- richard@asics.ws ----
---- ----
---- This source file may be used and distributed without ----
---- restriction provided that this copyright statement is not ----
---- removed from the file and that any derivative work contains ----
---- the original copyright notice and the associated disclaimer.----
---- ----
---- THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY ----
---- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED ----
---- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ----
---- FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR ----
---- OR CONTRIBUTORS 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. ----
---- ----
---------------------------------------------------------------------
-- CVS Log
--
-- $Id: i2c_master_bit_ctrl.vhd,v 1.17 2009-02-04 20:17:34 rherveille Exp $
--
-- $Date: 2009-02-04 20:17:34 $
-- $Revision: 1.17 $
-- $Author: rherveille $
-- $Locker: $
-- $State: Exp $
--
-- Change History:
-- $Log: not supported by cvs2svn $
-- Revision 1.16 2009/01/20 20:40:36 rherveille
-- Fixed type iscl_oen instead of scl_oen
--
-- Revision 1.15 2009/01/20 10:34:51 rherveille
-- Added SCL clock synchronization logic
-- Fixed slave_wait signal generation
--
-- Revision 1.14 2006/10/11 12:10:13 rherveille
-- Added missing semicolons ';' on endif
--
-- Revision 1.13 2006/10/06 10:48:24 rherveille
-- fixed short scl high pulse after clock stretch
--
-- Revision 1.12 2004/05/07 11:53:31 rherveille
-- Fixed previous fix :) Made a variable vs signal mistake.
--
-- Revision 1.11 2004/05/07 11:04:00 rherveille
-- Fixed a bug where the core would signal an arbitration lost (AL bit set), when another master controls the bus and the other master generates a STOP bit.
--
-- Revision 1.10 2004/02/27 07:49:43 rherveille
-- Fixed a bug in the arbitration-lost signal generation. VHDL version only.
--
-- Revision 1.9 2003/08/12 14:48:37 rherveille
-- Forgot an 'end if' :-/
--
-- Revision 1.8 2003/08/09 07:01:13 rherveille
-- Fixed a bug in the Arbitration Lost generation caused by delay on the (external) sda line.
-- Fixed a potential bug in the byte controller's host-acknowledge generation.
--
-- Revision 1.7 2003/02/05 00:06:02 rherveille
-- Fixed a bug where the core would trigger an erroneous 'arbitration lost' interrupt after being reset, when the reset pulse width < 3 clk cycles.
--
-- Revision 1.6 2003/02/01 02:03:06 rherveille
-- Fixed a few 'arbitration lost' bugs. VHDL version only.
--
-- Revision 1.5 2002/12/26 16:05:47 rherveille
-- Core is now a Multimaster I2C controller.
--
-- Revision 1.4 2002/11/30 22:24:37 rherveille
-- Cleaned up code
--
-- Revision 1.3 2002/10/30 18:09:53 rherveille
-- Fixed some reported minor start/stop generation timing issuess.
--
-- Revision 1.2 2002/06/15 07:37:04 rherveille
-- Fixed a small timing bug in the bit controller.\nAdded verilog simulation environment.
--
-- Revision 1.1 2001/11/05 12:02:33 rherveille
-- Split i2c_master_core.vhd into separate files for each entity; same layout as verilog version.
-- Code updated, is now up-to-date to doc. rev.0.4.
-- Added headers.
--
--
-------------------------------------
-- Bit controller section
------------------------------------
--
-- Translate simple commands into SCL/SDA transitions
-- Each command has 5 states, A/B/C/D/idle
--
-- start: SCL ~~~~~~~~~~~~~~\____
-- SDA XX/~~~~~~~\______
-- x | A | B | C | D | i
--
-- repstart SCL ______/~~~~~~~\___
-- SDA __/~~~~~~~\______
-- x | A | B | C | D | i
--
-- stop SCL _______/~~~~~~~~~~~
-- SDA ==\___________/~~~~~
-- x | A | B | C | D | i
--
--- write SCL ______/~~~~~~~\____
-- SDA XXX===============XX
-- x | A | B | C | D | i
--
--- read SCL ______/~~~~~~~\____
-- SDA XXXXXXX=XXXXXXXXXXX
-- x | A | B | C | D | i
--
-- Timing: Normal mode Fast mode
-----------------------------------------------------------------
-- Fscl 100KHz 400KHz
-- Th_scl 4.0us 0.6us High period of SCL
-- Tl_scl 4.7us 1.3us Low period of SCL
-- Tsu:sta 4.7us 0.6us setup time for a repeated start condition
-- Tsu:sto 4.0us 0.6us setup time for a stop conditon
-- Tbuf 4.7us 1.3us Bus free time between a stop and start condition
--
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity i2c_master_bit_ctrl is
port (
clk : in std_logic;
rst : in std_logic;
nReset : in std_logic;
ena : in std_logic; -- core enable signal
clk_cnt : in unsigned(15 downto 0); -- clock prescale value
cmd : in std_logic_vector(3 downto 0);
cmd_ack : out std_logic; -- command completed
busy : out std_logic; -- i2c bus busy
al : out std_logic; -- arbitration lost
din : in std_logic;
dout : out std_logic;
-- i2c lines
scl_i : in std_logic; -- i2c clock line input
scl_o : out std_logic; -- i2c clock line output
scl_oen : out std_logic; -- i2c clock line output enable, active low
sda_i : in std_logic; -- i2c data line input
sda_o : out std_logic; -- i2c data line output
sda_oen : out std_logic -- i2c data line output enable, active low
);
end entity i2c_master_bit_ctrl;
architecture structural of i2c_master_bit_ctrl is
constant I2C_CMD_NOP : std_logic_vector(3 downto 0) := "0000";
constant I2C_CMD_START : std_logic_vector(3 downto 0) := "0001";
constant I2C_CMD_STOP : std_logic_vector(3 downto 0) := "0010";
constant I2C_CMD_READ : std_logic_vector(3 downto 0) := "0100";
constant I2C_CMD_WRITE : std_logic_vector(3 downto 0) := "1000";
type states is (idle, start_a, start_b, start_c, start_d, start_e,
stop_a, stop_b, stop_c, stop_d, rd_a, rd_b, rd_c, rd_d, wr_a, wr_b, wr_c, wr_d);
signal c_state : states;
signal iscl_oen, isda_oen : std_logic; -- internal I2C lines
signal sda_chk : std_logic; -- check SDA status (multi-master arbitration)
signal dscl_oen : std_logic; -- delayed scl_oen signals
signal sSCL, sSDA : std_logic; -- synchronized SCL and SDA inputs
signal dSCL, dSDA : std_logic; -- delayed versions ofsSCL and sSDA
signal clk_en : std_logic; -- statemachine clock enable
signal scl_sync, slave_wait : std_logic; -- clock generation signals
signal ial : std_logic; -- internal arbitration lost signal
signal cnt : unsigned(15 downto 0); -- clock divider counter (synthesis)
begin
-- whenever the slave is not ready it can delay the cycle by pulling SCL low
-- delay scl_oen
process (clk, nReset)
begin
if (nReset = '0') then
dscl_oen <= '0';
elsif (clk'event and clk = '1') then
dscl_oen <= iscl_oen;
end if;
end process;
-- slave_wait is asserted when master wants to drive SCL high, but the slave pulls it low
-- slave_wait remains asserted until the slave releases SCL
process (clk, nReset)
begin
if (nReset = '0') then
slave_wait <= '0';
elsif (clk'event and clk = '1') then
slave_wait <= (iscl_oen and not dscl_oen and not sSCL) or (slave_wait and not sSCL);
end if;
end process;
-- master drives SCL high, but another master pulls it low
-- master start counting down its low cycle now (clock synchronization)
scl_sync <= dSCL and not sSCL and iscl_oen;
-- generate clk enable signal
gen_clken: process(clk, nReset)
begin
if (nReset = '0') then
cnt <= (others => '0');
clk_en <= '1';
elsif (clk'event and clk = '1') then
if ((rst = '1') or (cnt = 0) or (ena = '0') or (scl_sync = '1')) then
cnt <= clk_cnt;
clk_en <= '1';
elsif (slave_wait = '1') then
cnt <= cnt;
clk_en <= '0';
else
cnt <= cnt -1;
clk_en <= '0';
end if;
end if;
end process gen_clken;
-- generate bus status controller
bus_status_ctrl: block
signal cSCL, cSDA : std_logic_vector( 1 downto 0); -- capture SDA and SCL
signal fSCL, fSDA : std_logic_vector( 2 downto 0); -- filter inputs for SCL and SDA
signal filter_cnt : unsigned(13 downto 0); -- clock divider for filter
signal sta_condition : std_logic; -- start detected
signal sto_condition : std_logic; -- stop detected
signal cmd_stop : std_logic; -- STOP command
signal ibusy : std_logic; -- internal busy signal
begin
-- capture SCL and SDA
capture_scl_sda: process(clk, nReset)
begin
if (nReset = '0') then
cSCL <= "00";
cSDA <= "00";
elsif (clk'event and clk = '1') then
if (rst = '1') then
cSCL <= "00";
cSDA <= "00";
else
cSCL <= (cSCL(0) & scl_i);
cSDA <= (cSDA(0) & sda_i);
end if;
end if;
end process capture_scl_sda;
-- filter SCL and SDA; (attempt to) remove glitches
filter_divider: process(clk, nReset)
begin
if (nReset = '0') then
filter_cnt <= (others => '0');
elsif (clk'event and clk = '1') then
if ( (rst = '1') or (ena = '0') ) then
filter_cnt <= (others => '0');
elsif (filter_cnt = 0) then
filter_cnt <= clk_cnt(15 downto 2);
else
filter_cnt <= filter_cnt -1;
end if;
end if;
end process filter_divider;
filter_scl_sda: process(clk, nReset)
begin
if (nReset = '0') then
fSCL <= (others => '1');
fSDA <= (others => '1');
elsif (clk'event and clk = '1') then
if (rst = '1') then
fSCL <= (others => '1');
fSDA <= (others => '1');
elsif (filter_cnt = 0) then
fSCL <= (fSCL(1 downto 0) & cSCL(1));
fSDA <= (fSDA(1 downto 0) & cSDA(1));
end if;
end if;
end process filter_scl_sda;
-- generate filtered SCL and SDA signals
scl_sda: process(clk, nReset)
begin
if (nReset = '0') then
sSCL <= '1';
sSDA <= '1';
dSCL <= '1';
dSDA <= '1';
elsif (clk'event and clk = '1') then
if (rst = '1') then
sSCL <= '1';
sSDA <= '1';
dSCL <= '1';
dSDA <= '1';
else
sSCL <= (fSCL(2) and fSCL(1)) or
(fSCL(2) and fSCL(0)) or
(fSCL(1) and fSCL(0));
sSDA <= (fSDA(2) and fSDA(1)) or
(fSDA(2) and fSDA(0)) or
(fSDA(1) and fSDA(0));
dSCL <= sSCL;
dSDA <= sSDA;
end if;
end if;
end process scl_sda;
-- detect start condition => detect falling edge on SDA while SCL is high
-- detect stop condition => detect rising edge on SDA while SCL is high
detect_sta_sto: process(clk, nReset)
begin
if (nReset = '0') then
sta_condition <= '0';
sto_condition <= '0';
elsif (clk'event and clk = '1') then
if (rst = '1') then
sta_condition <= '0';
sto_condition <= '0';
else
sta_condition <= (not sSDA and dSDA) and sSCL;
sto_condition <= (sSDA and not dSDA) and sSCL;
end if;
end if;
end process detect_sta_sto;
-- generate i2c-bus busy signal
gen_busy: process(clk, nReset)
begin
if (nReset = '0') then
ibusy <= '0';
elsif (clk'event and clk = '1') then
if (rst = '1') then
ibusy <= '0';
else
ibusy <= (sta_condition or ibusy) and not sto_condition;
end if;
end if;
end process gen_busy;
busy <= ibusy;
-- generate arbitration lost signal
-- aribitration lost when:
-- 1) master drives SDA high, but the i2c bus is low
-- 2) stop detected while not requested (detect during 'idle' state)
gen_al: process(clk, nReset)
begin
if (nReset = '0') then
cmd_stop <= '0';
ial <= '0';
elsif (clk'event and clk = '1') then
if (rst = '1') then
cmd_stop <= '0';
ial <= '0';
else
if (clk_en = '1') then
if (cmd = I2C_CMD_STOP) then
cmd_stop <= '1';
else
cmd_stop <= '0';
end if;
end if;
if (c_state = idle) then
ial <= (sda_chk and not sSDA and isda_oen) or (sto_condition and not cmd_stop);
else
ial <= (sda_chk and not sSDA and isda_oen);
end if;
end if;
end if;
end process gen_al;
al <= ial;
-- generate dout signal, store dout on rising edge of SCL
gen_dout: process(clk, nReset)
begin
if (nReset = '0') then
dout <= '0';
elsif (clk'event and clk = '1') then
if (sSCL = '1' and dSCL = '0') then
dout <= sSDA;
end if;
end if;
end process gen_dout;
end block bus_status_ctrl;
-- generate statemachine
nxt_state_decoder : process (clk, nReset)
begin
if (nReset = '0') then
c_state <= idle;
cmd_ack <= '0';
iscl_oen <= '1';
isda_oen <= '1';
sda_chk <= '0';
elsif (clk'event and clk = '1') then
if (rst = '1' or ial = '1') then
c_state <= idle;
cmd_ack <= '0';
iscl_oen <= '1';
isda_oen <= '1';
sda_chk <= '0';
else
cmd_ack <= '0'; -- default no acknowledge
if (clk_en = '1') then
case (c_state) is
-- idle
when idle =>
case cmd is
when I2C_CMD_START => c_state <= start_a;
when I2C_CMD_STOP => c_state <= stop_a;
when I2C_CMD_WRITE => c_state <= wr_a;
when I2C_CMD_READ => c_state <= rd_a;
when others => c_state <= idle; -- NOP command
end case;
iscl_oen <= iscl_oen; -- keep SCL in same state
isda_oen <= isda_oen; -- keep SDA in same state
sda_chk <= '0'; -- don't check SDA
-- start
when start_a =>
c_state <= start_b;
iscl_oen <= iscl_oen; -- keep SCL in same state (for repeated start)
isda_oen <= '1'; -- set SDA high
sda_chk <= '0'; -- don't check SDA
when start_b =>
c_state <= start_c;
iscl_oen <= '1'; -- set SCL high
isda_oen <= '1'; -- keep SDA high
sda_chk <= '0'; -- don't check SDA
when start_c =>
c_state <= start_d;
iscl_oen <= '1'; -- keep SCL high
isda_oen <= '0'; -- set SDA low
sda_chk <= '0'; -- don't check SDA
when start_d =>
c_state <= start_e;
iscl_oen <= '1'; -- keep SCL high
isda_oen <= '0'; -- keep SDA low
sda_chk <= '0'; -- don't check SDA
when start_e =>
c_state <= idle;
cmd_ack <= '1'; -- command completed
iscl_oen <= '0'; -- set SCL low
isda_oen <= '0'; -- keep SDA low
sda_chk <= '0'; -- don't check SDA
-- stop
when stop_a =>
c_state <= stop_b;
iscl_oen <= '0'; -- keep SCL low
isda_oen <= '0'; -- set SDA low
sda_chk <= '0'; -- don't check SDA
when stop_b =>
c_state <= stop_c;
iscl_oen <= '1'; -- set SCL high
isda_oen <= '0'; -- keep SDA low
sda_chk <= '0'; -- don't check SDA
when stop_c =>
c_state <= stop_d;
iscl_oen <= '1'; -- keep SCL high
isda_oen <= '0'; -- keep SDA low
sda_chk <= '0'; -- don't check SDA
when stop_d =>
c_state <= idle;
cmd_ack <= '1'; -- command completed
iscl_oen <= '1'; -- keep SCL high
isda_oen <= '1'; -- set SDA high
sda_chk <= '0'; -- don't check SDA
-- read
when rd_a =>
c_state <= rd_b;
iscl_oen <= '0'; -- keep SCL low
isda_oen <= '1'; -- tri-state SDA
sda_chk <= '0'; -- don't check SDA
when rd_b =>
c_state <= rd_c;
iscl_oen <= '1'; -- set SCL high
isda_oen <= '1'; -- tri-state SDA
sda_chk <= '0'; -- don't check SDA
when rd_c =>
c_state <= rd_d;
iscl_oen <= '1'; -- keep SCL high
isda_oen <= '1'; -- tri-state SDA
sda_chk <= '0'; -- don't check SDA
when rd_d =>
c_state <= idle;
cmd_ack <= '1'; -- command completed
iscl_oen <= '0'; -- set SCL low
isda_oen <= '1'; -- tri-state SDA
sda_chk <= '0'; -- don't check SDA
-- write
when wr_a =>
c_state <= wr_b;
iscl_oen <= '0'; -- keep SCL low
isda_oen <= din; -- set SDA
sda_chk <= '0'; -- don't check SDA (SCL low)
when wr_b =>
c_state <= wr_c;
iscl_oen <= '1'; -- set SCL high
isda_oen <= din; -- keep SDA
sda_chk <= '0'; -- don't check SDA yet
-- Allow some more time for SDA and SCL to settle
when wr_c =>
c_state <= wr_d;
iscl_oen <= '1'; -- keep SCL high
isda_oen <= din; -- keep SDA
sda_chk <= '1'; -- check SDA
when wr_d =>
c_state <= idle;
cmd_ack <= '1'; -- command completed
iscl_oen <= '0'; -- set SCL low
isda_oen <= din; -- keep SDA
sda_chk <= '0'; -- don't check SDA (SCL low)
when others =>
end case;
end if;
end if;
end if;
end process nxt_state_decoder;
-- assign outputs
scl_o <= '0';
scl_oen <= iscl_oen;
sda_o <= '0';
sda_oen <= isda_oen;
end architecture structural;

View File

@@ -1,367 +0,0 @@
---------------------------------------------------------------------
---- ----
---- WISHBONE revB2 compl. I2C Master Core; byte-controller ----
---- ----
---- ----
---- Author: Richard Herveille ----
---- richard@asics.ws ----
---- www.asics.ws ----
---- ----
---- Downloaded from: http://www.opencores.org/projects/i2c/ ----
---- ----
---------------------------------------------------------------------
---- ----
---- Copyright (C) 2000 Richard Herveille ----
---- richard@asics.ws ----
---- ----
---- This source file may be used and distributed without ----
---- restriction provided that this copyright statement is not ----
---- removed from the file and that any derivative work contains ----
---- the original copyright notice and the associated disclaimer.----
---- ----
---- THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY ----
---- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED ----
---- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ----
---- FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR ----
---- OR CONTRIBUTORS 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. ----
---- ----
---------------------------------------------------------------------
-- CVS Log
--
-- $Id: i2c_master_byte_ctrl.vhd,v 1.5 2004-02-18 11:41:48 rherveille Exp $
--
-- $Date: 2004-02-18 11:41:48 $
-- $Revision: 1.5 $
-- $Author: rherveille $
-- $Locker: $
-- $State: Exp $
--
-- Change History:
-- $Log: not supported by cvs2svn $
-- Revision 1.4 2003/08/09 07:01:13 rherveille
-- Fixed a bug in the Arbitration Lost generation caused by delay on the (external) sda line.
-- Fixed a potential bug in the byte controller's host-acknowledge generation.
--
-- Revision 1.3 2002/12/26 16:05:47 rherveille
-- Core is now a Multimaster I2C controller.
--
-- Revision 1.2 2002/11/30 22:24:37 rherveille
-- Cleaned up code
--
-- Revision 1.1 2001/11/05 12:02:33 rherveille
-- Split i2c_master_core.vhd into separate files for each entity; same layout as verilog version.
-- Code updated, is now up-to-date to doc. rev.0.4.
-- Added headers.
--
--
------------------------------------------
-- Byte controller section
------------------------------------------
--
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity i2c_master_byte_ctrl is
port (
clk : in std_logic;
rst : in std_logic; -- synchronous active high reset (WISHBONE compatible)
nReset : in std_logic; -- asynchornous active low reset (FPGA compatible)
ena : in std_logic; -- core enable signal
clk_cnt : in unsigned(15 downto 0); -- 4x SCL
-- input signals
start,
stop,
read,
write,
ack_in : std_logic;
din : in std_logic_vector(7 downto 0);
-- output signals
cmd_ack : out std_logic; -- command done
ack_out : out std_logic;
i2c_busy : out std_logic; -- arbitration lost
i2c_al : out std_logic; -- i2c bus busy
dout : out std_logic_vector(7 downto 0);
-- i2c lines
scl_i : in std_logic; -- i2c clock line input
scl_o : out std_logic; -- i2c clock line output
scl_oen : out std_logic; -- i2c clock line output enable, active low
sda_i : in std_logic; -- i2c data line input
sda_o : out std_logic; -- i2c data line output
sda_oen : out std_logic -- i2c data line output enable, active low
);
end entity i2c_master_byte_ctrl;
architecture structural of i2c_master_byte_ctrl is
component i2c_master_bit_ctrl is
port (
clk : in std_logic;
rst : in std_logic;
nReset : in std_logic;
ena : in std_logic; -- core enable signal
clk_cnt : in unsigned(15 downto 0); -- clock prescale value
cmd : in std_logic_vector(3 downto 0);
cmd_ack : out std_logic; -- command done
busy : out std_logic; -- i2c bus busy
al : out std_logic; -- arbitration lost
din : in std_logic;
dout : out std_logic;
-- i2c lines
scl_i : in std_logic; -- i2c clock line input
scl_o : out std_logic; -- i2c clock line output
scl_oen : out std_logic; -- i2c clock line output enable, active low
sda_i : in std_logic; -- i2c data line input
sda_o : out std_logic; -- i2c data line output
sda_oen : out std_logic -- i2c data line output enable, active low
);
end component i2c_master_bit_ctrl;
-- commands for bit_controller block
constant I2C_CMD_NOP : std_logic_vector(3 downto 0) := "0000";
constant I2C_CMD_START : std_logic_vector(3 downto 0) := "0001";
constant I2C_CMD_STOP : std_logic_vector(3 downto 0) := "0010";
constant I2C_CMD_READ : std_logic_vector(3 downto 0) := "0100";
constant I2C_CMD_WRITE : std_logic_vector(3 downto 0) := "1000";
-- signals for bit_controller
signal core_cmd : std_logic_vector(3 downto 0);
signal core_ack, core_txd, core_rxd : std_logic;
signal al : std_logic;
-- signals for shift register
signal sr : std_logic_vector(7 downto 0); -- 8bit shift register
signal shift, ld : std_logic;
-- signals for state machine
signal go, host_ack : std_logic;
signal dcnt : unsigned(2 downto 0); -- data counter
signal cnt_done : std_logic;
begin
-- hookup bit_controller
bit_ctrl: i2c_master_bit_ctrl port map(
clk => clk,
rst => rst,
nReset => nReset,
ena => ena,
clk_cnt => clk_cnt,
cmd => core_cmd,
cmd_ack => core_ack,
busy => i2c_busy,
al => al,
din => core_txd,
dout => core_rxd,
scl_i => scl_i,
scl_o => scl_o,
scl_oen => scl_oen,
sda_i => sda_i,
sda_o => sda_o,
sda_oen => sda_oen
);
i2c_al <= al;
-- generate host-command-acknowledge
cmd_ack <= host_ack;
-- generate go-signal
go <= (read or write or stop) and not host_ack;
-- assign Dout output to shift-register
dout <= sr;
-- generate shift register
shift_register: process(clk, nReset)
begin
if (nReset = '0') then
sr <= (others => '0');
elsif (clk'event and clk = '1') then
if (rst = '1') then
sr <= (others => '0');
elsif (ld = '1') then
sr <= din;
elsif (shift = '1') then
sr <= (sr(6 downto 0) & core_rxd);
end if;
end if;
end process shift_register;
-- generate data-counter
data_cnt: process(clk, nReset)
begin
if (nReset = '0') then
dcnt <= (others => '0');
elsif (clk'event and clk = '1') then
if (rst = '1') then
dcnt <= (others => '0');
elsif (ld = '1') then
dcnt <= (others => '1'); -- load counter with 7
elsif (shift = '1') then
dcnt <= dcnt -1;
end if;
end if;
end process data_cnt;
cnt_done <= '1' when (dcnt = 0) else '0';
--
-- state machine
--
statemachine : block
type states is (st_idle, st_start, st_read, st_write, st_ack, st_stop);
signal c_state : states;
begin
--
-- command interpreter, translate complex commands into simpler I2C commands
--
nxt_state_decoder: process(clk, nReset)
begin
if (nReset = '0') then
core_cmd <= I2C_CMD_NOP;
core_txd <= '0';
shift <= '0';
ld <= '0';
host_ack <= '0';
c_state <= st_idle;
ack_out <= '0';
elsif (clk'event and clk = '1') then
if (rst = '1' or al = '1') then
core_cmd <= I2C_CMD_NOP;
core_txd <= '0';
shift <= '0';
ld <= '0';
host_ack <= '0';
c_state <= st_idle;
ack_out <= '0';
else
-- initialy reset all signal
core_txd <= sr(7);
shift <= '0';
ld <= '0';
host_ack <= '0';
case c_state is
when st_idle =>
if (go = '1') then
if (start = '1') then
c_state <= st_start;
core_cmd <= I2C_CMD_START;
elsif (read = '1') then
c_state <= st_read;
core_cmd <= I2C_CMD_READ;
elsif (write = '1') then
c_state <= st_write;
core_cmd <= I2C_CMD_WRITE;
else -- stop
c_state <= st_stop;
core_cmd <= I2C_CMD_STOP;
end if;
ld <= '1';
end if;
when st_start =>
if (core_ack = '1') then
if (read = '1') then
c_state <= st_read;
core_cmd <= I2C_CMD_READ;
else
c_state <= st_write;
core_cmd <= I2C_CMD_WRITE;
end if;
ld <= '1';
end if;
when st_write =>
if (core_ack = '1') then
if (cnt_done = '1') then
c_state <= st_ack;
core_cmd <= I2C_CMD_READ;
else
c_state <= st_write; -- stay in same state
core_cmd <= I2C_CMD_WRITE; -- write next bit
shift <= '1';
end if;
end if;
when st_read =>
if (core_ack = '1') then
if (cnt_done = '1') then
c_state <= st_ack;
core_cmd <= I2C_CMD_WRITE;
else
c_state <= st_read; -- stay in same state
core_cmd <= I2C_CMD_READ; -- read next bit
end if;
shift <= '1';
core_txd <= ack_in;
end if;
when st_ack =>
if (core_ack = '1') then
-- check for stop; Should a STOP command be generated ?
if (stop = '1') then
c_state <= st_stop;
core_cmd <= I2C_CMD_STOP;
else
c_state <= st_idle;
core_cmd <= I2C_CMD_NOP;
-- generate command acknowledge signal
host_ack <= '1';
end if;
-- assign ack_out output to core_rxd (contains last received bit)
ack_out <= core_rxd;
core_txd <= '1';
else
core_txd <= ack_in;
end if;
when st_stop =>
if (core_ack = '1') then
c_state <= st_idle;
core_cmd <= I2C_CMD_NOP;
-- generate command acknowledge signal
host_ack <= '1';
end if;
when others => -- illegal states
c_state <= st_idle;
core_cmd <= I2C_CMD_NOP;
report ("Byte controller entered illegal state.");
end case;
end if;
end if;
end process nxt_state_decoder;
end block statemachine;
end architecture structural;

View File

@@ -1,362 +0,0 @@
---------------------------------------------------------------------
---- ----
---- WISHBONE revB2 compl. I2C Master Core; top level ----
---- ----
---- ----
---- Author: Richard Herveille ----
---- richard@asics.ws ----
---- www.asics.ws ----
---- ----
---- Downloaded from: http://www.opencores.org/projects/i2c/ ----
---- ----
---------------------------------------------------------------------
---- ----
---- Copyright (C) 2000 Richard Herveille ----
---- richard@asics.ws ----
---- ----
---- This source file may be used and distributed without ----
---- restriction provided that this copyright statement is not ----
---- removed from the file and that any derivative work contains ----
---- the original copyright notice and the associated disclaimer.----
---- ----
---- THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY ----
---- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED ----
---- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ----
---- FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR ----
---- OR CONTRIBUTORS 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. ----
---- ----
---------------------------------------------------------------------
-- CVS Log
--
-- $Id: i2c_master_top.vhd,v 1.8 2009-01-20 10:38:45 rherveille Exp $
--
-- $Date: 2009-01-20 10:38:45 $
-- $Revision: 1.8 $
-- $Author: rherveille $
-- $Locker: $
-- $State: Exp $
--
-- Change History:
-- Revision 1.7 2004/03/14 10:17:03 rherveille
-- Fixed simulation issue when writing to CR register
--
-- Revision 1.6 2003/08/09 07:01:13 rherveille
-- Fixed a bug in the Arbitration Lost generation caused by delay on the (external) sda line.
-- Fixed a potential bug in the byte controller's host-acknowledge generation.
--
-- Revision 1.5 2003/02/01 02:03:06 rherveille
-- Fixed a few 'arbitration lost' bugs. VHDL version only.
--
-- Revision 1.4 2002/12/26 16:05:47 rherveille
-- Core is now a Multimaster I2C controller.
--
-- Revision 1.3 2002/11/30 22:24:37 rherveille
-- Cleaned up code
--
-- Revision 1.2 2001/11/10 10:52:44 rherveille
-- Changed PRER reset value from 0x0000 to 0xffff, conform specs.
--
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity i2c_master_top is
generic(
ARST_LVL : std_logic := '0' -- asynchronous reset level
);
port (
-- wishbone signals
wb_clk_i : in std_logic; -- master clock input
wb_rst_i : in std_logic := '0'; -- synchronous active high reset
arst_i : in std_logic := not ARST_LVL; -- asynchronous reset
wb_adr_i : in std_logic_vector(2 downto 0); -- lower address bits
wb_dat_i : in std_logic_vector(7 downto 0); -- Databus input
wb_dat_o : out std_logic_vector(7 downto 0); -- Databus output
wb_we_i : in std_logic; -- Write enable input
wb_stb_i : in std_logic; -- Strobe signals / core select signal
wb_cyc_i : in std_logic; -- Valid bus cycle input
wb_ack_o : out std_logic; -- Bus cycle acknowledge output
wb_inta_o : out std_logic; -- interrupt request output signal
-- i2c lines
scl_pad_i : in std_logic; -- i2c clock line input
scl_pad_o : out std_logic; -- i2c clock line output
scl_padoen_o : out std_logic; -- i2c clock line output enable, active low
sda_pad_i : in std_logic; -- i2c data line input
sda_pad_o : out std_logic; -- i2c data line output
sda_padoen_o : out std_logic -- i2c data line output enable, active low
);
end entity i2c_master_top;
architecture structural of i2c_master_top is
component i2c_master_byte_ctrl is
port (
clk : in std_logic;
rst : in std_logic; -- synchronous active high reset (WISHBONE compatible)
nReset : in std_logic; -- asynchornous active low reset (FPGA compatible)
ena : in std_logic; -- core enable signal
clk_cnt : in unsigned(15 downto 0); -- 4x SCL
-- input signals
start,
stop,
read,
write,
ack_in : std_logic;
din : in std_logic_vector(7 downto 0);
-- output signals
cmd_ack : out std_logic;
ack_out : out std_logic;
i2c_busy : out std_logic;
i2c_al : out std_logic;
dout : out std_logic_vector(7 downto 0);
-- i2c lines
scl_i : in std_logic; -- i2c clock line input
scl_o : out std_logic; -- i2c clock line output
scl_oen : out std_logic; -- i2c clock line output enable, active low
sda_i : in std_logic; -- i2c data line input
sda_o : out std_logic; -- i2c data line output
sda_oen : out std_logic -- i2c data line output enable, active low
);
end component i2c_master_byte_ctrl;
-- registers
signal prer : unsigned(15 downto 0); -- clock prescale register
signal ctr : std_logic_vector(7 downto 0); -- control register
signal txr : std_logic_vector(7 downto 0); -- transmit register
signal rxr : std_logic_vector(7 downto 0); -- receive register
signal cr : std_logic_vector(7 downto 0); -- command register
signal sr : std_logic_vector(7 downto 0); -- status register
-- internal reset signal
signal rst_i : std_logic;
-- wishbone write access
signal wb_wacc : std_logic;
-- internal acknowledge signal
signal iack_o : std_logic;
-- done signal: command completed, clear command register
signal done : std_logic;
-- command register signals
signal sta, sto, rd, wr, ack, iack : std_logic;
signal core_en : std_logic; -- core enable signal
signal ien : std_logic; -- interrupt enable signal
-- status register signals
signal irxack, rxack : std_logic; -- received aknowledge from slave
signal tip : std_logic; -- transfer in progress
signal irq_flag : std_logic; -- interrupt pending flag
signal i2c_busy : std_logic; -- i2c bus busy (start signal detected)
signal i2c_al, al : std_logic; -- arbitration lost
begin
-- generate internal reset signal
rst_i <= arst_i xor ARST_LVL;
-- generate acknowledge output signal
gen_ack_o : process(wb_clk_i)
begin
if (wb_clk_i'event and wb_clk_i = '1') then
iack_o <= wb_cyc_i and wb_stb_i and not iack_o; -- because timing is always honored
end if;
end process gen_ack_o;
wb_ack_o <= iack_o;
-- generate wishbone write access signal
wb_wacc <= wb_we_i and wb_cyc_i and wb_stb_i;-- and iack_o;
-- assign wb_dat_o
assign_dato : process(wb_clk_i)
begin
if (wb_clk_i'event and wb_clk_i = '1') then
if (wb_we_i = '0') and (wb_cyc_i = '1') and (wb_stb_i = '1') then
case wb_adr_i is
when "000" => wb_dat_o <= std_logic_vector(prer( 7 downto 0));
when "001" => wb_dat_o <= std_logic_vector(prer(15 downto 8));
when "010" => wb_dat_o <= ctr;
when "011" => wb_dat_o <= rxr; -- write is transmit register TxR
when "100" => wb_dat_o <= sr; -- write is command register CR
-- Debugging registers:
-- These registers are not documented.
-- Functionality could change in future releases
when "101" => wb_dat_o <= txr;
when "110" => wb_dat_o <= cr;
when "111" => wb_dat_o <= (others => '0');
when others => wb_dat_o <= (others => '0');
end case;
else
wb_dat_o <= (others => '0');
end if;
end if;
end process assign_dato;
-- generate registers (CR, SR see below)
gen_regs: process(rst_i, wb_clk_i)
begin
if (rst_i = '0') then
prer <= (others => '1');
ctr <= (others => '0');
txr <= (others => '0');
elsif (wb_clk_i'event and wb_clk_i = '1') then
if (wb_rst_i = '1') then
prer <= (others => '1');
ctr <= (others => '0');
txr <= (others => '0');
elsif (wb_wacc = '1') then
case wb_adr_i is
when "000" => prer( 7 downto 0) <= unsigned(wb_dat_i);
when "001" => prer(15 downto 8) <= unsigned(wb_dat_i);
when "010" => ctr <= wb_dat_i;
when "011" => txr <= wb_dat_i;
when "100" => null; --write to CR, avoid executing the others clause
-- illegal cases, for simulation only
when others =>
report ("Illegal write address, setting all registers to unknown.");
prer <= (others => 'X');
ctr <= (others => 'X');
txr <= (others => 'X');
end case;
end if;
end if;
end process gen_regs;
-- generate command register
gen_cr: process(rst_i, wb_clk_i)
begin
if (rst_i = '0') then
cr <= (others => '0');
elsif (wb_clk_i'event and wb_clk_i = '1') then
if (wb_rst_i = '1') then
cr <= (others => '0');
elsif (wb_wacc = '1') then
if ( (core_en = '1') and (wb_adr_i = "100") ) then
-- only take new commands when i2c core enabled
-- pending commands are finished
cr <= wb_dat_i;
end if;
else
if (done = '1' or i2c_al = '1') then
cr(7 downto 4) <= (others => '0'); -- clear command bits when command done or arbitration lost
end if;
cr(2 downto 1) <= (others => '0'); -- reserved bits, always '0'
cr(0) <= '0'; -- clear IRQ_ACK bit
end if;
end if;
end process gen_cr;
-- decode command register
sta <= cr(7);
sto <= cr(6);
rd <= cr(5);
wr <= cr(4);
ack <= cr(3);
iack <= cr(0);
-- decode control register
core_en <= ctr(7);
ien <= ctr(6);
-- hookup byte controller block
byte_ctrl: i2c_master_byte_ctrl
port map (
clk => wb_clk_i,
rst => wb_rst_i,
nReset => rst_i,
ena => core_en,
clk_cnt => prer,
start => sta,
stop => sto,
read => rd,
write => wr,
ack_in => ack,
i2c_busy => i2c_busy,
i2c_al => i2c_al,
din => txr,
cmd_ack => done,
ack_out => irxack,
dout => rxr,
scl_i => scl_pad_i,
scl_o => scl_pad_o,
scl_oen => scl_padoen_o,
sda_i => sda_pad_i,
sda_o => sda_pad_o,
sda_oen => sda_padoen_o
);
-- status register block + interrupt request signal
st_irq_block : block
begin
-- generate status register bits
gen_sr_bits: process (wb_clk_i, rst_i)
begin
if (rst_i = '0') then
al <= '0';
rxack <= '0';
tip <= '0';
irq_flag <= '0';
elsif (wb_clk_i'event and wb_clk_i = '1') then
if (wb_rst_i = '1') then
al <= '0';
rxack <= '0';
tip <= '0';
irq_flag <= '0';
else
al <= i2c_al or (al and not sta);
rxack <= irxack;
tip <= (rd or wr);
-- interrupt request flag is always generated
irq_flag <= (done or i2c_al or irq_flag) and not iack;
end if;
end if;
end process gen_sr_bits;
-- generate interrupt request signals
gen_irq: process (wb_clk_i, rst_i)
begin
if (rst_i = '0') then
wb_inta_o <= '0';
elsif (wb_clk_i'event and wb_clk_i = '1') then
if (wb_rst_i = '1') then
wb_inta_o <= '0';
else
-- interrupt signal is only generated when IEN (interrupt enable bit) is set
wb_inta_o <= irq_flag and ien;
end if;
end if;
end process gen_irq;
-- assign status register bits
sr(7) <= rxack;
sr(6) <= i2c_busy;
sr(5) <= al;
sr(4 downto 2) <= (others => '0'); -- reserved
sr(1) <= tip;
sr(0) <= irq_flag;
end block;
end architecture structural;

View File

@@ -1,25 +0,0 @@
-- This code is provided for free and may be used and --
-- distributed without restriction provided that the --
-- copyright statement is not removed from the file and --
-- that any derivative work contains the original --
-- copyright notice and the associated disclaimer. --
-- Comments and suggestions are always welcome --
The i2c_master core consists of three files:
- i2c_master_top -- top level
- i2c_master_byte_ctrl -- byte controller
- i2c_master_bit_ctrl -- bit controller
VHDL needs to be compiled in order. The files are listed
above in descending order.
I2C.VHD and tst_ds1621.vhd are not supported anymore.
They remain mostly for historical purposes, altough they
might prove usefull.
Richard Herveille
rherveille@opencores.org

View File

@@ -1,2 +0,0 @@
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) sdram.vhd ]
set_global_assignment -name SDC_FILE [file join $::quartus(qip_path) sdram.sdc ]

View File

@@ -1,19 +0,0 @@
derive_pll_clocks
#create_generated_clock -source [get_pins -compatibility_mode {*|pll|pll_inst|altera_pll_i|general[1].gpll~PLL_OUTPUT_COUNTER|divclk}]
create_generated_clock -source [get_pins -compatibility_mode {*mypll|altpll_component|auto_generated|generic_pll1~PLL_OUTPUT_COUNTER|divclk}] \
-name SDRAM_CLK [get_ports {SDRAM_CLK}]
derive_clock_uncertainty
# Set acceptable delays for SDRAM chip (See correspondent chip datasheet)
set_input_delay -max -clock SDRAM_CLK 6.4ns [get_ports SDRAM_DQ[*]]
set_input_delay -min -clock SDRAM_CLK 3.7ns [get_ports SDRAM_DQ[*]]
# -to [get_clocks {*|pll|pll_inst|altera_pll_i|general[0].gpll~PLL_OUTPUT_COUNTER|divclk}]
set_multicycle_path -from [get_clocks {SDRAM_CLK}] \
-to [get_clocks {*mypll|altpll_component|auto_generated|generic_pll1~PLL_OUTPUT_COUNTER|divclk}] \
-setup 2
set_output_delay -max -clock SDRAM_CLK 1.6ns [get_ports {SDRAM_D* SDRAM_ADDR* SDRAM_BA* SDRAM_CS SDRAM_WE SDRAM_RAS SDRAM_CAS SDRAM_CKE}]
set_output_delay -min -clock SDRAM_CLK -0.9ns [get_ports {SDRAM_D* SDRAM_ADDR* SDRAM_BA* SDRAM_CS SDRAM_WE SDRAM_RAS SDRAM_CAS SDRAM_CKE}]

View File

@@ -1,494 +0,0 @@
---------------------------------------------------------------------------------------------------------
--
-- Name: sdram.vhd
-- Created: September 2019
-- Original Author: Stephen J. Leary 2013-2014
-- VHDL Author: Philip Smart
-- Description: Original module written by Stephen J. Leary 2013-2014 in Verilog for use with the
-- MT48LC16M16 chip.
-- It has been translated into VHDL and undergoing extensive modifications to work
-- with the ZPU EVO processor, specifically burst tuning to enhance L2 Cache Fill
-- performance.
-- Credits:
-- Copyright: Copyright (c) 2013-2014, Stephen J. Leary, All rights reserved.
-- VHDL translation and enhancements (c) 2019 Philip Smart <philip.smart@net2net.org>
--
-- History: September 2019 - Initial module based on translaction of Stephen J. Leary's Verilog
-- source code.
--
---------------------------------------------------------------------------------------------------------
-- 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->.
---------------------------------------------------------------------------------------------------------
library ieee;
library pkgs;
library work;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.zpu_soc_pkg.all;
use work.zpu_pkg.all;
entity SDRAM is
generic (
MAX_DATACACHE_BITS : integer := 4 -- Maximum size in addr bits of 32bit datacache for burst transactions.
);
port (
-- SDRAM Interface
SD_CLK : in std_logic; -- sdram is accessed at 100MHz
SD_RST : in std_logic; -- reset the sdram controller.
SD_CKE : out std_logic; -- clock enable.
SD_DQ : inout std_logic_vector(15 downto 0); -- 16 bit bidirectional data bus
SD_ADDR : out std_logic_vector(12 downto 0); -- 13 bit multiplexed address bus
SD_DQM : out std_logic_vector(1 downto 0); -- two byte masks
SD_BA : out std_logic_vector(1 downto 0); -- two banks
SD_CS_n : out std_logic; -- a single chip select
SD_WE_n : out std_logic; -- write enable
SD_RAS_n : out std_logic; -- row address select
SD_CAS_n : out std_logic; -- columns address select
SD_READY : out std_logic; -- sd ready.
-- WishBone interface.
WB_CLK : in std_logic; -- Master clock at which the Wishbone interface operates.
WB_DAT_I : in std_logic_vector(WORD_32BIT_RANGE); -- Data input from Master
WB_DAT_O : out std_logic_vector(WORD_32BIT_RANGE); -- Data output to Master
WB_ACK_O : out std_logic;
WB_ADR_I : in std_logic_vector(23 downto 0); -- lower 2 bits are ignored.
WB_SEL_I : in std_logic_vector(3 downto 0);
WB_CTI_I : in std_logic_vector(2 downto 0); -- 000 Classic cycle, 001 Constant address burst cycle, 010 Incrementing burst cycle, 111 End-of-Burst
WB_STB_I : in std_logic;
WB_CYC_I : in std_logic; -- cpu/chipset requests cycle
WB_WE_I : in std_logic -- cpu/chipset requests write
);
end SDRAM;
architecture Structure of SDRAM is
-- Constants for register access.
--
constant RASCAS_DELAY : integer := 3; -- tRCD=20ns -> 2 cycles@100MHz
constant RFC_DELAY : integer := 70; -- tRFC=66ns time in nS for a autorefresh to complete.
constant RAM_CLK : integer := 100000000;
-- Command table from the Micron datasheet.
-- Name (Function) CS# RAS# CAS# WE# DQM ADDR DQ
-- COMMAND INHIBIT (NOP) H X X X X X X
-- NO OPERATION (NOP) L H H H X X X
-- ACTIVE (select bank and activate row) L L H H X Bank/row X
-- READ (select bank and column, and start READ burst) L H L H L/H Bank/col X
-- WRITE (select bank and column, and start WRITE burst) L H L L L/H Bank/col Valid
-- BURST TERMINATE L H H L X X Active
-- PRECHARGE (Deactivate row in bank or banks) L L H L X Code X
-- AUTO REFRESH or SELF REFRESH (enter self refresh mode) L L L H X X X
-- LOAD MODE REGISTER L L L L X Op-code X
-- Write enable/output enable X X X X L X Active
-- Write inhibit/output High-Z X X X X H X High-Z
constant CMD_INHIBIT : std_logic_vector(3 downto 0) := "1111";
constant CMD_NOP : std_logic_vector(3 downto 0) := "0111";
constant CMD_ACTIVE : std_logic_vector(3 downto 0) := "0011";
constant CMD_READ : std_logic_vector(3 downto 0) := "0101";
constant CMD_WRITE : std_logic_vector(3 downto 0) := "0100";
constant CMD_BURST_TERMINATE : std_logic_vector(3 downto 0) := "0110";
constant CMD_PRECHARGE : std_logic_vector(3 downto 0) := "0010";
constant CMD_AUTO_REFRESH : std_logic_vector(3 downto 0) := "0001";
constant CMD_LOAD_MODE : std_logic_vector(3 downto 0) := "0000";
-- Load Mode Register setting.
-- 12:10 = Reserved :
-- 9 = Write Burst Mode : 0 = Programmed Burst Length, 1 = Single Location Access
-- 8:7 = Operating Mode : 00 = Standard Operation, all other values reserved.
-- 6:4 = CAS Latency : 010 = 2, 011 = 3, all other values reserved.
-- 3 = Burst Type : 0 = Sequential, 1 = Interleaved.
-- 2:0 = Burst Length : When 000 = 1, 001 = 2, 010 = 4, 011 = 8, all others reserved except 111 when BT = 0 sets full page access.
-- | A12-A10 | A9  A8-A7 | A6 A5 A4 | A3Â  A2 A1 A0 |
-- | reserved| wr burst |reserved| CAS Ltncy|addr mode| burst len|
constant WRITE_BURST_MODE : std_logic := '1';
constant OP_MODE : std_logic_vector(1 downto 0) := "00";
constant CAS_LATENCY : std_logic_vector(2 downto 0) := "011";
constant BURST_TYPE : std_logic := '0';
constant BURST_LENGTH : std_logic_vector(2 downto 0) := "000";
constant MODE : std_logic_vector(12 downto 0) := "000" & WRITE_BURST_MODE & OP_MODE & CAS_LATENCY & BURST_TYPE & BURST_LENGTH;
-- FSM Cycle States.
constant CYCLE_PRECHARGE : integer := 0; -- 0
constant CYCLE_RAS_START : integer := 3; -- 3
constant CYCLE_RAS_NEXT : integer := CYCLE_RAS_START + 1; -- 4
constant CYCLE_CAS0 : integer := CYCLE_RAS_START + RASCAS_DELAY; -- 3 + RASCAS_DELAY
constant CYCLE_CAS1 : integer := CYCLE_CAS0 + 1; -- 4 + RASCAS_DELAY
constant CYCLE_CAS2 : integer := CYCLE_CAS1 + 1; -- 5 + RASCAS_DELAY
constant CYCLE_CAS3 : integer := CYCLE_CAS2 + 1; -- 6 + RASCAS_DELAY
constant CYCLE_READ0 : integer := CYCLE_CAS0 + to_integer(unsigned(CAS_LATENCY)) + 1; -- 3 + RASCAS_DELAY + CAS_LATENCY
constant CYCLE_READ1 : integer := CYCLE_READ0 + 1; -- 4 + RASCAS_DELAY + CAS_LATENCY
constant CYCLE_READ2 : integer := CYCLE_READ1 + 1; -- 5 + RASCAS_DELAY + CAS_LATENCY
constant CYCLE_READ3 : integer := CYCLE_READ2 + 1; -- 6 + RASCAS_DELAY + CAS_LATENCY
constant CYCLE_END : integer := CYCLE_READ3 + 1; -- 9 + RASCAS_DELAY + CAS_LATENCY
constant CYCLE_RFSH_START : integer := CYCLE_RAS_START; -- 3
constant CYCLE_RFSH_END : integer := CYCLE_RFSH_START + ((RFC_DELAY/RAM_CLK) * 10000000) + 1; -- 3 + RFC_DELAY in clock ticks.
-- Period in clock cycles between SDRAM refresh cycles.
constant REFRESH_PERIOD : integer := (RAM_CLK / (64 * 8192)) - CYCLE_END;
type BankArray is array(natural range 0 to 3) of std_logic_vector(12 downto 0);
-- Cache for holding burst reads to allow for differing speeds of WishBone Master.
type DataCacheArray is array(natural range 0 to ((2**(MAX_DATACACHE_BITS))-1)) of std_logic_vector(WORD_32BIT_RANGE);
signal readCache : DataCacheArray;
attribute ramstyle : string;
attribute ramstyle of readCache : signal is "logic";
signal cacheReadAddr : unsigned(MAX_DATACACHE_BITS-1 downto 0);
signal cacheWriteAddr : unsigned(MAX_DATACACHE_BITS-1 downto 0);
signal sd_dat : std_logic_vector(31 downto 0);
signal sd_dat_nxt : std_logic_vector(31 downto 0);
signal sd_stb : std_logic;
signal sd_we : std_logic;
signal sd_cyc : std_logic;
signal sd_burst : std_logic;
signal sd_cycle : integer range 0 to 31;
signal sd_done : std_logic;
signal sd_cmd : std_logic_vector(3 downto 0);
signal sd_refresh : unsigned(3 downto 0);
signal sd_auto_refresh : std_logic;
signal sd_req : std_logic_vector(2 downto 0);
signal sd_in_rst : unsigned(7 downto 0);
signal sd_rst_timer : unsigned(6 downto 0);
signal sd_active_row : BankArray;
signal sd_active_bank : std_logic_vector(1 downto 0);
signal sd_bank : natural range 0 to 3;
signal sd_row : std_logic_vector(12 downto 0);
signal sd_reading : std_logic;
signal sd_writing : std_logic;
signal sd_rdy : std_logic;
signal sd_mxadr : std_logic_vector(12 downto 0); -- 13 bit multiplexed address bus
signal sd_dout : std_logic_vector(15 downto 0);
signal sd_din : std_logic_vector(15 downto 0);
signal sd_done_last : std_logic;
signal burst_mode : std_logic;
signal can_burst : std_logic;
signal wb_ack : std_logic;
signal wb_burst : std_logic;
begin
-- Tri-state control of the SDRAM data bus.
process(sd_writing, SD_DQ, sd_dout)
begin
if (sd_writing = '0') then
SD_DQ <= (others => 'Z');
sd_din <= SD_DQ;
else
SD_DQ <= sd_dout;
sd_din <= SD_DQ;
end if;
end process;
-- Main FSM for SDRAM control and refresh.
process(SD_CLK, SD_RST)
begin
if (SD_RST = '1') then
sd_rst_timer <= (others => '0'); -- 0 upto 127
sd_in_rst <= (others => '1'); -- 255 downto 0
sd_mxadr <= (others => '0');
sd_auto_refresh <= '0';
sd_active_bank <= (others => '0');
sd_refresh <= (others => '0');
sd_active_row <= ((others => '0'), (others => '0'), (others => '0'), (others => '0'));
sd_rdy <= '0';
sd_cmd <= CMD_AUTO_REFRESH;
sd_stb <= '0';
sd_cyc <= '0';
sd_burst <= '0';
sd_we <= '0';
sd_cycle <= 0;
sd_done <= '0';
cacheWriteAddr <= (others => '0');
elsif rising_edge(SD_CLK) then
-- If no specific command given the default is NOP.
sd_cmd <= CMD_NOP;
-- Initialisation on power up or reset. The SDRAM must be given at least 100uS to initialise and a fixed setup pattern applied.
if (sd_rdy = '0') then
sd_rst_timer <= sd_rst_timer + 1;
-- 1uS timer.
if (sd_rst_timer = RAM_CLK/1000000) then
sd_rst_timer <= (others => '0');
sd_in_rst <= sd_in_rst - 1;
end if;
-- Every 1uS check for the next init action.
if (sd_rst_timer = 0) then
-- 100uS wait, no action as the SDRAM starts up.
-- ie. 255 downto 155
-- Precharge all banks
if(sd_in_rst = 155) then
sd_cmd <= CMD_PRECHARGE;
sd_mxadr(10) <= '1';
end if;
-- Load the Mode register with our parameters.
if(sd_in_rst = 148 or sd_in_rst = 147) then
sd_cmd <= CMD_LOAD_MODE;
sd_mxadr <= MODE;
end if;
-- 2 auto refresh commands as specified in datasheet. The RFS time is 60nS, so using a 1uS timer, issue one after
-- the other.
if(sd_in_rst = 145 or sd_in_rst = 140) then
sd_cmd <= CMD_AUTO_REFRESH;
end if;
-- SDRAM ready.
if(sd_in_rst = 135) then
sd_rdy <= '1';
end if;
end if;
else
-- bring the wishbone bus signal into the ram clock domain.
sd_we <= WB_WE_I;
if (sd_req = "111") then
sd_stb <= WB_STB_I;
sd_cyc <= WB_CYC_I;
end if;
sd_refresh <= sd_refresh + 1;
-- Auto refresh. On timeout 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) and (sd_cycle = 0)) then
sd_auto_refresh <= '1';
sd_refresh <= (others => '0');
sd_cmd <= CMD_PRECHARGE;
sd_mxadr(10) <= '1';
sd_active_bank <= (others => '0');
-- In auto refresh period.
elsif (sd_auto_refresh = '1') then
-- while the cycle is active count.
sd_cycle <= sd_cycle + 1;
case (sd_cycle) is
when CYCLE_RFSH_START =>
sd_cmd <= CMD_AUTO_REFRESH;
when CYCLE_RFSH_END =>
-- reset the count.
sd_auto_refresh <= '0';
sd_cycle <= 0;
when others =>
end case;
elsif (sd_cyc = '1' or (sd_cycle /= 0) or (sd_cycle = 0 and sd_req = "111")) then
-- while the cycle is active count.
sd_cycle <= sd_cycle + 1;
case (sd_cycle) is
when CYCLE_PRECHARGE =>
-- If the bank is not open then no need to precharge, move onto RAS.
if (sd_active_bank(sd_bank) = '0') then
sd_cycle <= CYCLE_RAS_START;
--The active row isnt being reset
-- If the requested row is already active, go to CAS for immediate access to this row.
elsif (sd_active_row(sd_bank) = sd_row) then
sd_cycle <= CYCLE_CAS0; -- - 1; -- FIXME: Why doesn't work without -1?
-- Otherwise we close out the open bank by issuing a PRECHARGE.
else
sd_cmd <= CMD_PRECHARGE;
sd_mxadr(10) <= '0';
SD_BA <= std_logic_vector(to_unsigned(sd_bank, SD_BA'length));
sd_active_bank(sd_bank) <= '0'; -- Store flag to indicate which bank is being made active.
end if;
-- Open the requested row.
when CYCLE_RAS_START =>
sd_cmd <= CMD_ACTIVE;
--sd_mxadr <= '0' & sd_row; -- 0 & Addr[20:9] presented to SDRAM as row address.
sd_mxadr <= sd_row; -- Addr[21:9] presented to SDRAM as row address.
SD_BA <= std_logic_vector(to_unsigned(sd_bank, SD_BA'length)); -- Addr[23:22]
sd_active_row(sd_bank) <= sd_row; -- Store number of row being made active
sd_active_bank(sd_bank) <= '1'; -- Store flag to indicate which bank is being made active.
when CYCLE_RAS_NEXT =>
sd_mxadr(12 downto 11) <= "11"; -- Set DQ to tri--state.
-- this is the first CAS cycle
when CYCLE_CAS0 =>
-- Process on a 32bit boundary, as this is a 16bit chip we need 2 accesses for a 32bit alignment.
--sd_mxadr <= "0000" & WB_ADR_I(23) & WB_ADR_I(8 downto 2) & '0'; -- CAS address = Addr[23,8:2] accessing first 16bit location within the 32bit external alignment with no auto precharge
sd_mxadr <= "0000" & WB_ADR_I(8 downto 1) & '0'; -- CAS address = Addr[23,8:2] accessing first 16bit location within the 32bit external alignment with no auto precharge
SD_BA <= std_logic_vector(to_unsigned(sd_bank, SD_BA'length)); -- Ensure bank is the correct one opened.
if (sd_reading = '1') then
sd_cmd <= CMD_READ;
elsif (sd_writing = '1') then
sd_cmd <= CMD_WRITE;
sd_mxadr(12 downto 11)<= not WB_SEL_I(1 downto 0); -- For writing, set DQM to the negated WB_SEL values, indicating which bytes to process.
sd_dout <= wb_dat_i(15 downto 0); -- Assign corresponding data to the SDRAM databus.
end if;
when CYCLE_CAS1 =>
--sd_mxadr <= "0000" & WB_ADR_I(23) & WB_ADR_I(8 downto 2) & '1'; -- As per CAS0 except we now access second 16bit location within the 32bit external alignment.
sd_mxadr <= "0000" & WB_ADR_I(8 downto 1) & '1'; -- As per CAS0 except we now access second 16bit location within the 32bit external alignment.
if (sd_reading = '1') then
sd_cmd <= CMD_READ;
if (burst_mode = '1' and can_burst = '1') then
sd_burst <= '1';
end if;
elsif (sd_writing = '1') then
sd_cmd <= CMD_WRITE;
sd_mxadr(12 downto 11)<= not WB_SEL_I(3 downto 2);
sd_done <= not sd_done;
sd_dout <= wb_dat_i(31 downto 16);
-- sd_cycle <= CYCLE_END;
end if;
-- CAS2/3 ... are to handle burst transfers according to programmed Mode register word.
when CYCLE_CAS2 =>
if (sd_burst = '1') then
--sd_mxadr <= "0000" & WB_ADR_I(23) & WB_ADR_I(8 downto 3) & "10"; -- no auto precharge
sd_mxadr <= "0000" & WB_ADR_I(8 downto 2) & "10"; -- no auto precharge
if (sd_reading = '1') then
sd_cmd <= CMD_READ;
end if;
end if;
when CYCLE_CAS3 =>
if (sd_burst = '1') then
--sd_mxadr <= "0000" & WB_ADR_I(23) & WB_ADR_I(8 downto 3) & "11"; -- no auto precharge
sd_mxadr <= "0000" & WB_ADR_I(8 downto 2) & "11"; -- no auto precharge
if (sd_reading = '1') then
sd_cmd <= CMD_READ;
end if;
end if;
-- Data is available CAS Latency clocks after the read request, so these read operations operate in parallel to the CAS
-- cycles requesting the data. ie. CL=2 then CYCLE_READ0 will be processed same time as CYCLE_CAS2.
when CYCLE_READ0 =>
if (sd_reading = '1') then
sd_dat(15 downto 0) <= sd_din;
else
if (sd_writing = '1') then
sd_cycle <= CYCLE_END;
end if;
end if;
when CYCLE_READ1 =>
if (sd_reading = '1') then
sd_dat(31 downto 16) <= sd_din;
sd_done <= not sd_done;
end if;
when CYCLE_READ2 =>
if (sd_reading = '1') then
sd_dat_nxt(15 downto 0)<= sd_din;
end if;
when CYCLE_READ3 =>
if (sd_reading = '1') then
sd_dat_nxt(31 downto 16)<= sd_din;
end if;
when CYCLE_END =>
sd_burst <= '0';
sd_cyc <= '0';
sd_stb <= '0';
when others =>
end case;
else
sd_cycle <= 0;
sd_burst <= '0';
end if;
end if;
end if;
end process;
-- WishBone interface for sending received data and setting up the correct ACK signal for any read/write activity.
process(SD_RST, WB_CLK, sd_rdy)
begin
if (SD_RST = '1') then
sd_done_last <= '0';
wb_ack <= '0';
wb_burst <= '0';
-- If the SDRAM isnt ready, we can only wait.
elsif sd_rdy = '0' then
elsif rising_edge(WB_CLK) then
-- Note SDRAM activity via a previous/last signal.
sd_done_last <= sd_done;
-- If there has been a change in the SDRAM activity and it hasnt been acknowleged, send the ACK else cancel any previous ACK.
if (sd_done xor sd_done_last) = '1' and wb_ack = '0' then
wb_ack <= '1';
else
wb_ack <= '0';
end if;
-- If we are in an active Cycle and the Strobe is activated, assign any read data to the WB bus.
if (WB_STB_I = '1' and WB_CYC_I = '1') then
-- If there has been a change in the SDRAM activity and it hasnt been acknowledged, send the current data held to the WB Bus.
if ((sd_done xor sd_done_last) = '1' and wb_ack = '0') then
wb_dat_o <= sd_dat;
wb_burst <= burst_mode;
end if;
-- If there has been an acknowledge due to sending of the first data word and we are in burst mode, then send the 2nd read value
-- whilst maintaining the ack.
if (wb_ack = '1' and wb_burst = '1') then
wb_ack <= '1';
wb_burst <= '0';
wb_dat_o <= sd_dat_nxt;
end if;
else
wb_burst <= '0';
end if;
end if;
end process;
sd_req <= WB_STB_I & WB_CYC_I & not wb_ack;
sd_bank <= to_integer(unsigned(WB_ADR_I(23 downto 22)));
sd_row <= WB_ADR_I(21 downto 9);
burst_mode <= '1' when WB_CTI_I = "010" else '0';
can_burst <= '1' when WB_ADR_I(2) = '0' else '0';
sd_reading <= '1' when sd_stb = '1' and sd_cyc = '1' and sd_we = '0' else '0';
sd_writing <= '1' when sd_stb = '1' and sd_cyc = '1' and sd_we = '1' else '0';
-- drive control signals according to current command
SD_CS_n <= sd_cmd(3);
SD_RAS_n <= sd_cmd(2);
SD_CAS_n <= sd_cmd(1);
SD_WE_n <= sd_cmd(0);
SD_CKE <= '1';
SD_DQM <= sd_mxadr(12 downto 11);
SD_ADDR <= sd_mxadr;
WB_ACK_O <= wb_ack;
SD_READY <= sd_rdy;
end Structure;

View File

@@ -1,2 +0,0 @@
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) wbsdram.vhd ]
#set_global_assignment -name SDC_FILE [file join $::quartus(qip_path) 48LC16M16.sdc ]

View File

@@ -1,90 +0,0 @@
# ------------------------------------------------------------------------------
# Constraints definition original author:
# 8/19/2014 D. W. Hawkings (dwh@ovro.caltech.edu)
# Adapted and enhanced for the Micron 48LC16M16 SDRAM by Philip Smart Dec 2019.
# ------------------------------------------------------------------------------
derive_pll_clocks
# -----------------------------------------------------------------
# SDRAM Clock
# Set these variables to the system and memory clock PLL paths for
# your board.
# -----------------------------------------------------------------
set sysclk_pll "mypll|altpll_component|auto_generated|generic_pll1~PLL_OUTPUT_COUNTER|divclk"
set memclk_pll "mypll|altpll_component|auto_generated|generic_pll2~PLL_OUTPUT_COUNTER|divclk"
create_generated_clock -name SDRAM_CLK -source $memclk_pll [get_ports {SDRAM_CLK}]
derive_clock_uncertainty
# -----------------------------------------------------------------
# SDRAM Constraints
# -----------------------------------------------------------------
#
# SDRAM timing parameters
#
# Generally, the command/address/data all have the same setup/hold
# time.
#
# SDRAM clock can lead System clock by min:
# tlead = tcoutmin(FPGA) th(SDRAM)
#
# SDRAM clock can lag System clock by min:
# tlag = toh(SDRAM) th(FPGA)
#
# tSU = Data Setup time (ie. tDS, tAS) on falling edge.
# tH = Hold time (ie. tDH, tAH) for SDRAM.
# tCOUT (min) = Data out hold time (ie. tOH)
# tCOUT (max) = Access time for CL in use (ie. tAC3).
#
set sdram_tsu 1.5
set sdram_th 0.8
set sdram_tco_min 3.0
set sdram_tco_max 5.4
# FPGA timing constraints
set sdram_input_delay_min $sdram_tco_min
set sdram_input_delay_max $sdram_tco_max
set sdram_output_delay_min -$sdram_th
set sdram_output_delay_max $sdram_tsu
# PLL to FPGA output (clear the unconstrained path warning)
#set_min_delay -from $memclk_pll -to [get_ports {SDRAM_CLK}] 1
#set_max_delay -from $memclk_pll -to [get_ports {SDRAM_CLK}] 6
# FPGA Outputs
set sdram_outputs [get_ports {
SDRAM_CKE
SDRAM_CS
SDRAM_RAS
SDRAM_CAS
SDRAM_WE
SDRAM_DQM[*]
SDRAM_BA[*]
SDRAM_ADDR[*]
SDRAM_DQ[*]
}]
set_output_delay -clock SDRAM_CLK -min $sdram_output_delay_min $sdram_outputs
set_output_delay -clock SDRAM_CLK -max $sdram_output_delay_max $sdram_outputs
# FPGA Inputs
set sdram_inputs [get_ports {
SDRAM_DQ[*]
}]
set_input_delay -clock SDRAM_CLK -min $sdram_input_delay_min $sdram_inputs
set_input_delay -clock SDRAM_CLK -max $sdram_input_delay_max $sdram_inputs
# -----------------------------------------------------------------
# SDRAM-to-FPGA multi-cycle constraint
# -----------------------------------------------------------------
# The PLL is configured so that SDRAM clock leads the system
# clock by ~90-degrees (0.25 period or 2.5ns for 100MHz clock).
# This will need changing for different clocks, in the PLL
# RTL file and the SoC contraints file.
# The following multi-cycle constraint declares to TimeQuest that
# the path between the SDRAM_CLK and the System Clock can be an
# extra clock period to the read path to ensure that the latch
# clock that occurs 1.25 periods after the launch clock is used in
# the timing analysis.
#
set_multicycle_path -setup -end -from SDRAM_CLK -to $sysclk_pll 2

View File

@@ -1,2 +0,0 @@
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) wbsdram_cached.vhd ]
#set_global_assignment -name SDC_FILE [file join $::quartus(qip_path) 48LC16M16.sdc ]

View File

@@ -1,2 +0,0 @@
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) wbsdram.vhd ]
#set_global_assignment -name SDC_FILE [file join $::quartus(qip_path) W9864G6.sdc ]

View File

@@ -1,90 +0,0 @@
# ------------------------------------------------------------------------------
# Constraints definition original author:
# 8/19/2014 D. W. Hawkings (dwh@ovro.caltech.edu)
# Adapted and enhanced for the Winbond W9864G6 SDRAM by Philip Smart Dec 2019.
# ------------------------------------------------------------------------------
derive_pll_clocks
# -----------------------------------------------------------------
# SDRAM Clock
# Set these variables to the system and memory clock PLL paths for
# your board.
# -----------------------------------------------------------------
set sysclk_pll "mypll|altpll_component|auto_generated|generic_pll1~PLL_OUTPUT_COUNTER|divclk"
set memclk_pll "mypll|altpll_component|auto_generated|generic_pll2~PLL_OUTPUT_COUNTER|divclk"
create_generated_clock -name SDRAM_CLK -source $memclk_pll [get_ports {SDRAM_CLK}]
derive_clock_uncertainty
# -----------------------------------------------------------------
# SDRAM Constraints
# -----------------------------------------------------------------
#
# SDRAM timing parameters
#
# Generally, the command/address/data all have the same setup/hold
# time.
#
# SDRAM clock can lead System clock by min:
# tlead = tcoutmin(FPGA) th(SDRAM)
#
# SDRAM clock can lag System clock by min:
# tlag = toh(SDRAM) th(FPGA)
#
# tSU = Data Setup time (ie. tDS, tAS) on falling edge.
# tH = Hold time (ie. tDH, tAH) for SDRAM.
# tCOUT (min) = Data out hold time (ie. tOH)
# tCOUT (max) = Access time for CL in use (ie. tAC3).
#
set sdram_tsu 1.5
set sdram_th 0.8
set sdram_tco_min 3.0
set sdram_tco_max 5.0
# FPGA timing constraints
set sdram_input_delay_min $sdram_tco_min
set sdram_input_delay_max $sdram_tco_max
set sdram_output_delay_min -$sdram_th
set sdram_output_delay_max $sdram_tsu
# PLL to FPGA output (clear the unconstrained path warning)
#set_min_delay -from $memclk_pll -to [get_ports {SDRAM_CLK}] 1
#set_max_delay -from $memclk_pll -to [get_ports {SDRAM_CLK}] 6
# FPGA Outputs
set sdram_outputs [get_ports {
SDRAM_CKE
SDRAM_CS
SDRAM_RAS
SDRAM_CAS
SDRAM_WE
SDRAM_DQM[*]
SDRAM_BA[*]
SDRAM_ADDR[*]
SDRAM_DQ[*]
}]
set_output_delay -clock SDRAM_CLK -min $sdram_output_delay_min $sdram_outputs
set_output_delay -clock SDRAM_CLK -max $sdram_output_delay_max $sdram_outputs
# FPGA Inputs
set sdram_inputs [get_ports {
SDRAM_DQ[*]
}]
set_input_delay -clock SDRAM_CLK -min $sdram_input_delay_min $sdram_inputs
set_input_delay -clock SDRAM_CLK -max $sdram_input_delay_max $sdram_inputs
# -----------------------------------------------------------------
# SDRAM-to-FPGA multi-cycle constraint
# -----------------------------------------------------------------
# The PLL is configured so that SDRAM clock leads the system
# clock by ~90-degrees (0.25 period or 2.5ns for 100MHz clock).
# This will need changing for different clocks, in the PLL
# RTL file and the SoC contraints file.
# The following multi-cycle constraint declares to TimeQuest that
# the path between the SDRAM_CLK and the System Clock can be an
# extra clock period to the read path to ensure that the latch
# clock that occurs 1.25 periods after the launch clock is used in
# the timing analysis.
#
set_multicycle_path -setup -end -from SDRAM_CLK -to $sysclk_pll 2

View File

@@ -1,2 +0,0 @@
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) wbsdram_cached.vhd ]
#set_global_assignment -name SDC_FILE [file join $::quartus(qip_path) W9864G6.sdc ]

View File

@@ -1,504 +0,0 @@
---------------------------------------------------------------------------------------------------------
--
-- Name: wbsdram.vhd
-- Created: September 2019
-- Author: Philip Smart
-- Description: A configurable cached sdram controller for use with the ZPU EVO Processor and SoC.
-- The module is instantiated with the parameters to describe the underlying SDRAM chip
-- and in theory should work with most 16/32 bit SDRAM chips if they adhere to the SDRAM
-- standard.
-- Credits: Stephen J. Leary 2013-2014 - Basic sdram cycle structure of this module was based on
-- the verilog MT48LC16M16 chip controller written by Stephen.
-- Copyright: (c) 2019-2020 Philip Smart <philip.smart@net2net.org>
--
-- History: September 2019 - Initial module translation to VHDL based on Stephen J. Leary's Verilog
-- source code.
-- November 2019 - Adapted for the system bus for use when no Wishbone interface is
-- instantiated in the ZPU Evo.
-- December 2019 - Extensive changes, metability stability, autorefresh to ACTIVE timing
-- and parameterisation.
--
---------------------------------------------------------------------------------------------------------
-- 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->.
---------------------------------------------------------------------------------------------------------
library ieee;
library pkgs;
library work;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.zpu_soc_pkg.all;
use work.zpu_pkg.all;
entity WBSDRAM is
generic (
MAX_DATACACHE_BITS : integer := 4; -- Maximum size in addr bits of 32bit datacache for burst transactions.
SDRAM_ROWS : integer := 4096; -- Number of Rows in the SDRAM.
SDRAM_COLUMNS : integer := 256; -- Number of Columns in an SDRAM page (ie. 1 row).
SDRAM_BANKS : integer := 4; -- Number of banks in the SDRAM.
SDRAM_DATAWIDTH : integer := 16; -- Data width of SDRAM chip (16, 32).
SDRAM_CLK_FREQ : integer := 100000000; -- Frequency of the SDRAM clock in Hertz.
SDRAM_tRCD : integer := 20; -- tRCD - RAS to CAS minimum period (in ns).
SDRAM_tRP : integer := 20; -- tRP - Precharge delay, min time for a precharge command to complete (in ns).
SDRAM_tRFC : integer := 70; -- tRFC - Auto-refresh minimum time to complete (in ns), ie. 66ns
SDRAM_tREF : integer := 64 -- tREF - period of time a complete refresh of all rows is made within (in ms).
);
port (
-- SDRAM Interface
SDRAM_CLK : in std_logic; -- SDRAM is accessed at given clock, frequency specified in RAM_CLK.
SDRAM_RST : in std_logic; -- Reset the sdram controller.
SDRAM_CKE : out std_logic; -- Clock enable.
SDRAM_DQ : inout std_logic_vector(SDRAM_DATAWIDTH-1 downto 0); -- Bidirectional data bus
SDRAM_ADDR : out std_logic_vector(log2ceil(SDRAM_ROWS) - 1 downto 0); -- Multiplexed address bus
SDRAM_DQM : out std_logic_vector(log2ceil(SDRAM_BANKS) - 1 downto 0); -- Number of byte masks dependent on number of banks.
SDRAM_BA : out std_logic_vector(log2ceil(SDRAM_BANKS) - 1 downto 0); -- Number of banks in SDRAM
SDRAM_CS_n : out std_logic; -- Single chip select
SDRAM_WE_n : out std_logic; -- Write enable
SDRAM_RAS_n : out std_logic; -- Row address select
SDRAM_CAS_n : out std_logic; -- Columns address select
SDRAM_READY : out std_logic; -- SD ready.
-- WishBone interface.
WB_CLK : in std_logic; -- Master clock at which the Wishbone interface operates.
WB_RST_I : in std_logic; -- high active sync reset
WB_DATA_I : in std_logic_vector(WORD_32BIT_RANGE); -- Data input from Master
WB_DATA_O : out std_logic_vector(WORD_32BIT_RANGE); -- Data output to Master
WB_ACK_O : out std_logic;
WB_ADR_I : in std_logic_vector(log2ceil(SDRAM_ROWS * SDRAM_COLUMNS * SDRAM_BANKS) downto 0);
WB_SEL_I : in std_logic_vector(3 downto 0);
WB_CTI_I : in std_logic_vector(2 downto 0); -- 000 Classic cycle, 001 Constant address burst cycle, 010 Incrementing burst cycle, 111 End-of-Burst
WB_STB_I : in std_logic;
WB_CYC_I : in std_logic; -- cpu/chipset requests cycle
WB_WE_I : in std_logic; -- cpu/chipset requests write
WB_TGC_I : in std_logic_vector(06 downto 0); -- cycle tag
WB_HALT_O : out std_logic; -- throttle master
WB_ERR_O : out std_logic -- abnormal cycle termination
);
end WBSDRAM;
architecture Structure of WBSDRAM is
-- Constants to define the structure of the SDRAM in bits for provisioning of signals.
constant SDRAM_ROW_BITS : integer := log2ceil(SDRAM_ROWS);
constant SDRAM_COLUMN_BITS : integer := log2ceil(SDRAM_COLUMNS);
constant SDRAM_ARRAY_BITS : integer := log2ceil(SDRAM_ROWS * SDRAM_COLUMNS);
constant SDRAM_BANK_BITS : integer := log2ceil(SDRAM_BANKS);
constant SDRAM_ADDR_BITS : integer := log2ceil(SDRAM_ROWS * SDRAM_COLUMNS * SDRAM_BANKS * (SDRAM_DATAWIDTH/8));
-- Command table for a standard SDRAM.
--
-- Name (Function) CKE CS# RAS# CAS# WE# DQM ADDR DQ
-- COMMAND INHIBIT (NOP) H H X X X X X X
-- NO OPERATION (NOP) H L H H H X X X
-- ACTIVE (select bank and activate row) H L L H H X Bank/row X
-- READ (select bank and column, and start READ burst) H L H L H L/H Bank/col X
-- WRITE (select bank and column, and start WRITE burst) H L H L L L/H Bank/col Valid
-- BURST TERMINATE H L H H L X X Active
-- PRECHARGE (Deactivate row in bank or banks) H L L H L X Code X
-- AUTO REFRESH or SELF REFRESH (enter self refresh mode) H L L L H X X X
-- LOAD MODE REGISTER H L L L L X Op-code X
-- Write enable/output enable H X X X X L X Active
-- Write inhibit/output High-Z H X X X X H X High-Z
-- Self Refresh Entry L L L L H X X X
-- Self Refresh Exit (Device is idle) H H X X X X X X
-- Self Refresh Exit (Device is in Self Refresh state) H L H H X X X X
-- Clock suspend mode Entry L X X X X X X X
-- Clock suspend mode Exit H X X X X X X X
-- Power down mode Entry (Device is idle) L H X X X X X X
-- Power down mode Entry (Device is Active) L L H H X X X X
-- Power down mode Exit (Any state) H H X X X X X X
-- Power down mode Exit (Device is powered down) H L H H X X X X
constant CMD_INHIBIT : std_logic_vector(4 downto 0) := "11111";
constant CMD_NOP : std_logic_vector(4 downto 0) := "10111";
constant CMD_ACTIVE : std_logic_vector(4 downto 0) := "10011";
constant CMD_READ : std_logic_vector(4 downto 0) := "10101";
constant CMD_WRITE : std_logic_vector(4 downto 0) := "10100";
constant CMD_BURST_TERMINATE : std_logic_vector(4 downto 0) := "10110";
constant CMD_PRECHARGE : std_logic_vector(4 downto 0) := "10010";
constant CMD_AUTO_REFRESH : std_logic_vector(4 downto 0) := "10001";
constant CMD_LOAD_MODE : std_logic_vector(4 downto 0) := "10000";
constant CMD_SELF_REFRESH_START : std_logic_vector(4 downto 0) := "00001";
constant CMD_SELF_REFRESH_END : std_logic_vector(4 downto 0) := "10110";
constant CMD_CLOCK_SUSPEND : std_logic_vector(4 downto 0) := "00000";
constant CMD_CLOCK_RESTORE : std_logic_vector(4 downto 0) := "10000";
constant CMD_POWER_DOWN : std_logic_vector(4 downto 0) := "01000";
constant CMD_POWER_RESTORE : std_logic_vector(4 downto 0) := "01100";
-- Load Mode Register setting for a standard SDRAM.
--
-- xx:10 = Reserved :
-- 9 = Write Burst Mode : 0 = Programmed Burst Length, 1 = Single Location Access
-- 8:7 = Operating Mode : 00 = Standard Operation, all other values reserved.
-- 6:4 = CAS Latency : 010 = 2, 011 = 3, all other values reserved.
-- 3 = Burst Type : 0 = Sequential, 1 = Interleaved.
-- 2:0 = Burst Length : When 000 = 1, 001 = 2, 010 = 4, 011 = 8, all others reserved except 111 when BT = 0 sets full page access.
-- | A12-A10 | A9  A8-A7 | A6 A5 A4 | A3Â  A2 A1 A0 |
-- | reserved| wr burst |reserved| CAS Ltncy|addr mode| burst len|
constant WRITE_BURST_MODE : std_logic := '1';
constant OP_MODE : std_logic_vector(1 downto 0) := "00";
constant CAS_LATENCY : std_logic_vector(2 downto 0) := "011";
constant BURST_TYPE : std_logic := '0';
constant BURST_LENGTH : std_logic_vector(2 downto 0) := "000";
constant MODE : std_logic_vector(SDRAM_ROW_BITS-1 downto 0) := std_logic_vector(to_unsigned(to_integer(unsigned("00" & WRITE_BURST_MODE & OP_MODE & CAS_LATENCY & BURST_TYPE & BURST_LENGTH)), SDRAM_ROW_BITS));
-- FSM Cycle States governed in units of time, the state changes location according to the configurable parameters to ensure correct actuation at the correct time.
--
constant CYCLE_PRECHARGE : integer := 0; -- ~0
constant CYCLE_RAS_START : integer := clockTicks(SDRAM_tRP, SDRAM_CLK_FREQ); -- ~3
constant CYCLE_CAS_START : integer := CYCLE_RAS_START + clockTicks(SDRAM_tRCD, SDRAM_CLK_FREQ); -- ~3 + tRCD
constant CYCLE_CAS_END : integer := CYCLE_CAS_START + 1; -- ~4 + tRCD
constant CYCLE_READ_START : integer := CYCLE_CAS_START + to_integer(unsigned(CAS_LATENCY)) + 1; -- ~3 + tRCD + CAS_LATENCY
constant CYCLE_READ_END : integer := CYCLE_READ_START + 1; -- ~4 + tRCD + CAS_LATENCY
constant CYCLE_END : integer := CYCLE_READ_END + 1; -- ~9 + tRCD + CAS_LATENCY
constant CYCLE_RFSH_START : integer := clockTicks(SDRAM_tRP, SDRAM_CLK_FREQ); -- ~tRP
constant CYCLE_RFSH_END : integer := CYCLE_RFSH_START + clockTicks(SDRAM_tRFC, SDRAM_CLK_FREQ) + clockTicks(SDRAM_tRP, SDRAM_CLK_FREQ) + 1; -- ~tRP (start) + tRFC (min autorefresh time) + tRP (end) in clock ticks.
-- Period in clock cycles between SDRAM refresh cycles. This equates to tREF / SDRAM_ROWS to evenly divide the time, then subtract the length of the refresh period as this is
-- the time it takes when a refresh starts until completion.
constant REFRESH_PERIOD : integer := (((SDRAM_tREF * SDRAM_CLK_FREQ ) / SDRAM_ROWS) - (SDRAM_tRFC * 1000)) / 1000;
-- Array of row addresses, one per bank, to indicate the row in use per bank.
type BankArray is array(natural range 0 to SDRAM_BANKS-1) of std_logic_vector(SDRAM_ROW_BITS-1 downto 0);
-- SDRAM domain signals.
signal sdBusy : std_logic;
signal sdCycle : integer range 0 to 31;
signal sdDataOut : std_logic_vector(WORD_32BIT_RANGE);
signal sdDone : std_logic;
shared variable sdCmd : std_logic_vector(4 downto 0);
signal sdRefreshCount : unsigned(11 downto 0);
signal sdAutoRefresh : std_logic;
signal sdResetTimer : unsigned(WORD_8BIT_RANGE);
signal sdInResetCounter : unsigned(WORD_8BIT_RANGE);
signal sdIsReady : std_logic;
signal sdActiveRow : BankArray;
signal sdActiveBank : std_logic_vector(1 downto 0);
-- CPU domain signals.
signal cpuBusy : std_logic;
signal cpuDQM : std_logic_vector(3 downto 0);
signal cpuDoneLast : std_logic;
signal cpuBank : natural range 0 to SDRAM_BANKS-1;
signal cpuRow : std_logic_vector(SDRAM_ROW_BITS-1 downto 0);
signal cpuCol : std_logic_vector(SDRAM_COLUMN_BITS-1 downto 0);
signal cpuDataIn : std_logic_vector(WORD_32BIT_RANGE);
signal cpuIsWriting : std_logic;
signal cpuDoneAck : std_logic;
signal wbACK : std_logic;
begin
-- Main FSM for SDRAM control and refresh.
process(ALL)
begin
if (SDRAM_RST = '1') then
sdResetTimer <= (others => '0'); -- 0 upto 255
sdInResetCounter <= (others => '1'); -- 255 downto 0
sdAutoRefresh <= '0';
sdRefreshCount <= (others => '0');
sdActiveBank <= (others => '0');
sdActiveRow <= ((others => '0'), (others => '0'), (others => '0'), (others => '0'));
sdIsReady <= '0';
sdCmd := CMD_AUTO_REFRESH;
SDRAM_DQM <= (others => '1');
sdCycle <= 0;
sdDone <= '0';
elsif rising_edge(SDRAM_CLK) then
-- Tri-state control of the SDRAM databus, when reading, the output drivers are disabled.
if (cpuIsWriting = '0') then
SDRAM_DQ <= (others => 'Z');
end if;
-- If no specific command given the default is NOP.
sdCmd := CMD_NOP;
-- Initialisation on power up or reset. The SDRAM must be given at least 200uS to initialise and a fixed setup pattern applied.
if (sdIsReady = '0') then
sdResetTimer <= sdResetTimer + 1;
-- 1uS timer.
if (sdResetTimer = SDRAM_CLK_FREQ/1000000) then
sdResetTimer <= (others => '0');
sdInResetCounter <= sdInResetCounter - 1;
end if;
-- Every 1uS check for the next init action.
if (sdResetTimer = 0) then
-- 200uS wait, no action as the SDRAM starts up.
-- ie. 255 downto 55
-- Precharge all banks
if(sdInResetCounter = 55) then
sdCmd := CMD_PRECHARGE;
SDRAM_ADDR(10) <= '1';
end if;
-- 8 auto refresh commands as specified in datasheet. The RFS time is 60nS, so using a 1uS timer, issue one after
-- the other.
if(sdInResetCounter >= 40 and sdInResetCounter <= 48) then
sdCmd := CMD_AUTO_REFRESH;
end if;
-- Load the Mode register with our parameters.
if(sdInResetCounter = 39) then
sdCmd := CMD_LOAD_MODE;
SDRAM_ADDR <= MODE;
end if;
-- 8 auto refresh commands as specified in datasheet. The RFS time is 60nS, so using a 1uS timer, issue one after
-- the other.
if(sdInResetCounter >= 30 and sdInResetCounter <= 38) then
sdCmd := CMD_AUTO_REFRESH;
end if;
-- SDRAM ready.
if(sdInResetCounter = 20) then
sdIsReady <= '1';
end if;
end if;
else
-- Counter to time periods between autorefresh.
sdRefreshCount <= sdRefreshCount + 1;
-- This mechanism is used to reduce the possibility of metastability issues due to differing clocks.
-- We only act after both Busy signals are high, thus one SDRAM clock after cpuBusy goes high.
sdBusy <= cpuBusy;
-- If the SDRAM has completed its transaction and the CPU has acknowledged it, remove the signals.
if (sdDone = '1' and cpuDoneAck = '1') then
sdDone <= '0';
end if;
-- Auto refresh. On timeout it kicks in so that ROWS auto refreshes are
-- issued in a tRFC period. Other bus operations are stalled during this period.
if (sdRefreshCount > REFRESH_PERIOD and sdCycle = 0) then
sdAutoRefresh <= '1';
sdRefreshCount <= (others => '0');
sdCmd := CMD_PRECHARGE;
SDRAM_ADDR(10) <= '1';
sdActiveBank <= (others => '0');
sdActiveRow <= ((others => '0'), (others => '0'), (others => '0'), (others => '0'));
-- In auto refresh period.
elsif (sdAutoRefresh = '1') then
-- while the cycle is active count.
sdCycle <= sdCycle + 1;
case (sdCycle) is
when CYCLE_RFSH_START =>
sdCmd := CMD_AUTO_REFRESH;
when CYCLE_RFSH_END =>
-- reset the count.
sdAutoRefresh <= '0';
sdCycle <= 0;
when others =>
end case;
elsif (((cpuBusy = '1' and sdBusy = '1') and sdCycle = 0) or sdCycle /= 0) then
-- while the cycle is active count.
sdCycle <= sdCycle + 1;
case (sdCycle) is
when CYCLE_PRECHARGE =>
-- If the bank is not open then no need to precharge, move onto RAS.
if (sdActiveBank(cpuBank) = '0') then
sdCycle <= CYCLE_RAS_START;
-- If the requested row is already active, go to CAS for immediate access to this row.
elsif (sdActiveRow(cpuBank) = cpuRow) then
sdCycle <= CYCLE_CAS_START;
-- Otherwise we close out the open bank by issuing a PRECHARGE.
else
sdCmd := CMD_PRECHARGE;
SDRAM_ADDR(10) <= '0';
SDRAM_BA <= std_logic_vector(to_unsigned(cpuBank, SDRAM_BA'length));
sdActiveBank(cpuBank) <= '0'; -- Store flag to indicate which bank is being made active.
end if;
-- Open the requested row.
when CYCLE_RAS_START =>
sdCmd := CMD_ACTIVE;
SDRAM_ADDR <= cpuRow; -- Addr presented to SDRAM as row address.
SDRAM_BA <= std_logic_vector(to_unsigned(cpuBank, SDRAM_BA'length)); -- Addr presented to SDRAM as bank select.
sdActiveRow(cpuBank) <= cpuRow; -- Store number of row being made active
sdActiveBank(cpuBank) <= '1'; -- Store flag to indicate which bank is being made active.
-- CAS start, for 32 bit chips, only 1 CAS cycle is needed, for 16bit chips we need 2 to read/write 2x16bit words.
when CYCLE_CAS_START =>
-- If writing, setup for a write with preset mask.
if (cpuIsWriting = '1') then
if SDRAM_DATAWIDTH = 32 then
SDRAM_ADDR <= std_logic_vector(to_unsigned(to_integer(unsigned(cpuCol(SDRAM_COLUMN_BITS-1 downto 2) & '0' & '0')), SDRAM_ROW_BITS)); -- CAS address = Address accessing 32bit data with no auto precharge
SDRAM_DQ <= cpuDataIn; -- Assign corresponding data to the SDRAM databus.
SDRAM_DQM <= not cpuDQM(3 downto 0);
sdCycle <= CYCLE_END;
elsif SDRAM_DATAWIDTH = 16 then
SDRAM_ADDR <= std_logic_vector(to_unsigned(to_integer(unsigned(cpuCol(SDRAM_COLUMN_BITS-1 downto 1) & '0')), SDRAM_ROW_BITS)); -- CAS address = Address accessing first 16bit location within the 32bit external alignment with no auto precharge
SDRAM_DQ <= cpuDataIn((SDRAM_DATAWIDTH*2)-1 downto SDRAM_DATAWIDTH); -- Assign corresponding data to the SDRAM databus.
SDRAM_DQM <= not cpuDQM(3 downto 2);
else
report "SDRAM datawidth parameter invalid, should be 16 or 32!" severity error;
end if;
sdCmd := CMD_WRITE;
else
-- Setup for a read.
sdCmd := CMD_READ;
SDRAM_ADDR <= std_logic_vector(to_unsigned(to_integer(unsigned(cpuCol(SDRAM_COLUMN_BITS-1 downto 1) & '0')), SDRAM_ROW_BITS)); -- CAS address = Address accessing first 16bit location within the 32bit external alignment with no auto precharge
SDRAM_DQM <= "00"; -- For reads dont mask the data output.
end if;
when CYCLE_CAS_END =>
SDRAM_ADDR <= std_logic_vector(to_unsigned(to_integer(unsigned(cpuCol(SDRAM_COLUMN_BITS-1 downto 1) & '1')), SDRAM_ROW_BITS)); -- CAS address = Next address accessing second 16bit location within the 32bit external alignment with no auto precharge
-- When writing, setup for a write with preset mask with the correct word.
if (cpuIsWriting = '1') then
SDRAM_DQM <= not cpuDQM(1 downto 0);
SDRAM_DQ <= cpuDataIn(SDRAM_DATAWIDTH-1 downto 0);
sdDone <= '1';
sdCycle <= CYCLE_END;
sdCmd := CMD_WRITE;
else
-- Setup for a read, change to write if flag set.
sdCmd := CMD_READ;
SDRAM_DQM <= "00"; -- For reads dont mask the data output.
end if;
-- Data is available CAS Latency clocks after the read request. For 32bit chips, only 1 cycle is needed, for 16bit we need 2 read cycles of
-- 16 bits each.
when CYCLE_READ_START =>
if SDRAM_DATAWIDTH = 32 then
sdDataOut <= SDRAM_DQ;
sdCycle <= CYCLE_END;
elsif SDRAM_DATAWIDTH = 16 then
sdDataOut((SDRAM_DATAWIDTH*2)-1 downto SDRAM_DATAWIDTH) <= SDRAM_DQ;
else
report "SDRAM datawidth parameter invalid, should be 16 or 32!" severity error;
end if;
-- Second and final read cycle for 16bit SDRAM chips to create a 32bit word.
when CYCLE_READ_END =>
sdDataOut(SDRAM_DATAWIDTH-1 downto 0) <= SDRAM_DQ;
when CYCLE_END =>
sdDone <= '1';
sdCycle <= 0;
-- Other states are wait states, waiting for the correct time slot for SDRAM access.
when others =>
end case;
else
sdCycle <= 0;
end if;
end if;
-- drive control signals according to current command
SDRAM_CKE <= sdCmd(4);
SDRAM_CS_n <= sdCmd(3);
SDRAM_RAS_n <= sdCmd(2);
SDRAM_CAS_n <= sdCmd(1);
SDRAM_WE_n <= sdCmd(0);
end if;
end process;
-- CPU/WishBone side logic. When the CPU initiates a transaction, capture the signals and the captured values are used within the SDRAM domain. This is to prevent
-- any changes CPU side or differing signal lengths due to CPU architecture or clock being propogated into the SDRAM domain. The CPU only needs to know
-- when the transation is complete and data read.
--
process(ALL)
begin
if (WB_RST_I = '1') then
cpuDoneLast <= '0';
cpuBusy <= '0';
cpuBank <= 0;
cpuRow <= (others => '0');
cpuCol <= (others => '0');
cpuDQM <= (others => '1');
cpuDoneAck <= '0';
cpuIsWriting <= '0';
wbACK <= '0';
-- Wait for the SDRAM to become ready by holding the CPU in a wait state.
elsif sdIsReady = '0' then
cpuBusy <= '1';
elsif rising_edge(WB_CLK) then
-- Detect a Wishbone cycle and commence an SDRAM access.
if (WB_STB_I = '1' and WB_CYC_I = '1' and cpuBusy = '0') then
cpuBusy <= '1';
cpuBank <= to_integer(unsigned(WB_ADR_I(SDRAM_ADDR_BITS-1 downto SDRAM_ARRAY_BITS+1)));
cpuRow <= std_logic_vector(to_unsigned(to_integer(unsigned(WB_ADR_I(SDRAM_ARRAY_BITS downto SDRAM_COLUMN_BITS+1))), SDRAM_ROW_BITS));
cpuCol <= WB_ADR_I(SDRAM_COLUMN_BITS downto 2) & '0';
cpuDQM <= WB_SEL_I;
cpuDataIn <= WB_DATA_I;
end if;
if (WB_STB_I = '1' and WB_CYC_I = '1' and cpuBusy = '1') then
cpuIsWriting <= WB_WE_I;
end if;
-- Note SDRAM activity via a previous/last signal.
cpuDoneLast <= sdDone;
-- If there has been a change in the SDRAM activity and it hasnt been acknowleged, send the ACK and use the cycle to latch the retrieved data.
if (cpuDoneLast = '0' and sdDone = '1') then
cpuDoneAck <= '1';
WB_DATA_O <= sdDataOut;
end if;
-- If we are at the end of an active Cycle and the acknowledge to the sdram fsm has been sent, clear all signals and assert the Wishbone Ack to
-- complete.
if (cpuDoneLast = '1' and sdDone = '0' and cpuDoneAck = '1' and wbACK = '0') then
cpuBusy <= '0';
cpuDoneAck <= '0';
cpuIsWriting <= '0';
wbACK <= '1';
else
wbACK <= '0';
end if;
end if;
end process;
-- System bus control signals.
SDRAM_READY <= sdIsReady;
-- Wishbone bus control signals.
WB_ACK_O <= wbACK;
--- Throttle not needed.
WB_HALT_O <= '0';
--- Error not yet implemented.
WB_ERR_O <= '0';
end Structure;

View File

@@ -1,712 +0,0 @@
---------------------------------------------------------------------------------------------------------
--
-- Name: wbsdram_cached.vhd
-- Created: September 2019
-- Author: Philip Smart
-- Description: A configurable cached sdram controller for use with the ZPU EVO Processor and SoC.
-- The module is instantiated with the parameters to describe the underlying SDRAM chip
-- and in theory should work with most 16/32 bit SDRAM chips if they adhere to the SDRAM
-- standard.
-- Credits: Stephen J. Leary 2013-2014 - Basic sdram cycle structure of this module was based on
-- the verilog MT48LC16M16 chip controller written by Stephen.
-- Copyright: (c) 2019-2020 Philip Smart <philip.smart@net2net.org>
--
-- History: September 2019 - Initial module translation to VHDL based on Stephen J. Leary's Verilog
-- source code.
-- November 2019 - Adapted for the system bus for use when no Wishbone interface is
-- instantiated in the ZPU Evo.
-- December 2019 - Extensive changes, metability stability, autorefresh to ACTIVE timing
-- and parameterisation.
--
---------------------------------------------------------------------------------------------------------
-- 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->.
---------------------------------------------------------------------------------------------------------
library ieee;
library pkgs;
library work;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.zpu_soc_pkg.all;
use work.zpu_pkg.all;
entity WBSDRAM is
generic (
MAX_DATACACHE_BITS : integer := 4; -- Maximum size in addr bits of 32bit datacache for burst transactions.
SDRAM_ROWS : integer := 4096; -- Number of Rows in the SDRAM.
SDRAM_COLUMNS : integer := 256; -- Number of Columns in an SDRAM page (ie. 1 row).
SDRAM_BANKS : integer := 4; -- Number of banks in the SDRAM.
SDRAM_DATAWIDTH : integer := 16; -- Data width of SDRAM chip (16, 32).
SDRAM_CLK_FREQ : integer := 100000000; -- Frequency of the SDRAM clock in Hertz.
SDRAM_tRCD : integer := 20; -- tRCD - RAS to CAS minimum period (in ns).
SDRAM_tRP : integer := 20; -- tRP - Precharge delay, min time for a precharge command to complete (in ns).
SDRAM_tRFC : integer := 70; -- tRFC - Auto-refresh minimum time to complete (in ns), ie. 66ns
SDRAM_tREF : integer := 64 -- tREF - period of time a complete refresh of all rows is made within (in ms).
);
port (
-- SDRAM Interface
SDRAM_CLK : in std_logic; -- SDRAM is accessed at given clock, frequency specified in RAM_CLK.
SDRAM_RST : in std_logic; -- Reset the sdram controller.
SDRAM_CKE : out std_logic; -- Clock enable.
SDRAM_DQ : inout std_logic_vector(SDRAM_DATAWIDTH-1 downto 0); -- Bidirectional data bus
SDRAM_ADDR : out std_logic_vector(log2ceil(SDRAM_ROWS) - 1 downto 0); -- Multiplexed address bus
SDRAM_DQM : out std_logic_vector(log2ceil(SDRAM_BANKS) - 1 downto 0); -- Number of byte masks dependent on number of banks.
SDRAM_BA : out std_logic_vector(log2ceil(SDRAM_BANKS) - 1 downto 0); -- Number of banks in SDRAM
SDRAM_CS_n : out std_logic; -- Single chip select
SDRAM_WE_n : out std_logic; -- Write enable
SDRAM_RAS_n : out std_logic; -- Row address select
SDRAM_CAS_n : out std_logic; -- Columns address select
SDRAM_READY : out std_logic; -- SD ready.
-- WishBone interface.
WB_CLK : in std_logic; -- Master clock at which the Wishbone interface operates.
WB_RST_I : in std_logic; -- high active sync reset
WB_DATA_I : in std_logic_vector(WORD_32BIT_RANGE); -- Data input from Master
WB_DATA_O : out std_logic_vector(WORD_32BIT_RANGE); -- Data output to Master
WB_ACK_O : out std_logic;
WB_ADR_I : in std_logic_vector(log2ceil(SDRAM_ROWS * SDRAM_COLUMNS * SDRAM_BANKS) downto 0);
WB_SEL_I : in std_logic_vector(3 downto 0);
WB_CTI_I : in std_logic_vector(2 downto 0); -- 000 Classic cycle, 001 Constant address burst cycle, 010 Incrementing burst cycle, 111 End-of-Burst
WB_STB_I : in std_logic;
WB_CYC_I : in std_logic; -- cpu/chipset requests cycle
WB_WE_I : in std_logic; -- cpu/chipset requests write
WB_TGC_I : in std_logic_vector(06 downto 0); -- cycle tag
WB_HALT_O : out std_logic; -- throttle master
WB_ERR_O : out std_logic -- abnormal cycle termination
);
end WBSDRAM;
architecture Structure of WBSDRAM is
-- Constants to define the structure of the SDRAM in bits for provisioning of signals.
constant SDRAM_ROW_BITS : integer := log2ceil(SDRAM_ROWS);
constant SDRAM_COLUMN_BITS : integer := log2ceil(SDRAM_COLUMNS);
constant SDRAM_ARRAY_BITS : integer := log2ceil(SDRAM_ROWS * SDRAM_COLUMNS);
constant SDRAM_BANK_BITS : integer := log2ceil(SDRAM_BANKS);
constant SDRAM_ADDR_BITS : integer := log2ceil(SDRAM_ROWS * SDRAM_COLUMNS * SDRAM_BANKS * (SDRAM_DATAWIDTH/8));
-- Command table for a standard SDRAM.
--
-- Name (Function) CKE CS# RAS# CAS# WE# DQM ADDR DQ
-- COMMAND INHIBIT (NOP) H H X X X X X X
-- NO OPERATION (NOP) H L H H H X X X
-- ACTIVE (select bank and activate row) H L L H H X Bank/row X
-- READ (select bank and column, and start READ burst) H L H L H L/H Bank/col X
-- WRITE (select bank and column, and start WRITE burst) H L H L L L/H Bank/col Valid
-- BURST TERMINATE H L H H L X X Active
-- PRECHARGE (Deactivate row in bank or banks) H L L H L X Code X
-- AUTO REFRESH or SELF REFRESH (enter self refresh mode) H L L L H X X X
-- LOAD MODE REGISTER H L L L L X Op-code X
-- Write enable/output enable H X X X X L X Active
-- Write inhibit/output High-Z H X X X X H X High-Z
-- Self Refresh Entry L L L L H X X X
-- Self Refresh Exit (Device is idle) H H X X X X X X
-- Self Refresh Exit (Device is in Self Refresh state) H L H H X X X X
-- Clock suspend mode Entry L X X X X X X X
-- Clock suspend mode Exit H X X X X X X X
-- Power down mode Entry (Device is idle) L H X X X X X X
-- Power down mode Entry (Device is Active) L L H H X X X X
-- Power down mode Exit (Any state) H H X X X X X X
-- Power down mode Exit (Device is powered down) H L H H X X X X
constant CMD_INHIBIT : std_logic_vector(4 downto 0) := "11111";
constant CMD_NOP : std_logic_vector(4 downto 0) := "10111";
constant CMD_ACTIVE : std_logic_vector(4 downto 0) := "10011";
constant CMD_READ : std_logic_vector(4 downto 0) := "10101";
constant CMD_WRITE : std_logic_vector(4 downto 0) := "10100";
constant CMD_BURST_TERMINATE : std_logic_vector(4 downto 0) := "10110";
constant CMD_PRECHARGE : std_logic_vector(4 downto 0) := "10010";
constant CMD_AUTO_REFRESH : std_logic_vector(4 downto 0) := "10001";
constant CMD_LOAD_MODE : std_logic_vector(4 downto 0) := "10000";
constant CMD_SELF_REFRESH_START : std_logic_vector(4 downto 0) := "00001";
constant CMD_SELF_REFRESH_END : std_logic_vector(4 downto 0) := "10110";
constant CMD_CLOCK_SUSPEND : std_logic_vector(4 downto 0) := "00000";
constant CMD_CLOCK_RESTORE : std_logic_vector(4 downto 0) := "10000";
constant CMD_POWER_DOWN : std_logic_vector(4 downto 0) := "01000";
constant CMD_POWER_RESTORE : std_logic_vector(4 downto 0) := "01100";
-- Load Mode Register setting for a standard SDRAM.
--
-- xx:10 = Reserved :
-- 9 = Write Burst Mode : 0 = Programmed Burst Length, 1 = Single Location Access
-- 8:7 = Operating Mode : 00 = Standard Operation, all other values reserved.
-- 6:4 = CAS Latency : 010 = 2, 011 = 3, all other values reserved.
-- 3 = Burst Type : 0 = Sequential, 1 = Interleaved.
-- 2:0 = Burst Length : When 000 = 1, 001 = 2, 010 = 4, 011 = 8, all others reserved except 111 when BT = 0 sets full page access.
-- | A12-A10 | A9  A8-A7 | A6 A5 A4 | A3Â  A2 A1 A0 |
-- | reserved| wr burst |reserved| CAS Ltncy|addr mode| burst len|
constant WRITE_BURST_MODE : std_logic := '1';
constant OP_MODE : std_logic_vector(1 downto 0) := "00";
constant CAS_LATENCY : std_logic_vector(2 downto 0) := "011";
constant BURST_TYPE : std_logic := '0';
constant BURST_LENGTH : std_logic_vector(2 downto 0) := "111";
constant MODE : std_logic_vector(SDRAM_ROW_BITS-1 downto 0) := std_logic_vector(to_unsigned(to_integer(unsigned("00" & WRITE_BURST_MODE & OP_MODE & CAS_LATENCY & BURST_TYPE & BURST_LENGTH)), SDRAM_ROW_BITS));
-- FSM Cycle States governed in units of time, the state changes location according to the configurable parameters to ensure correct actuation at the correct time.
--
constant CYCLE_PRECHARGE : integer := 0; -- ~0
constant CYCLE_RAS_START : integer := clockTicks(SDRAM_tRP, SDRAM_CLK_FREQ); -- ~3
constant CYCLE_CAS_START : integer := CYCLE_RAS_START + clockTicks(SDRAM_tRCD, SDRAM_CLK_FREQ); -- ~3 + tRCD
constant CYCLE_WRITE_END : integer := CYCLE_CAS_START + 1; -- ~4 + tRCD
constant CYCLE_READ_START : integer := CYCLE_CAS_START + to_integer(unsigned(CAS_LATENCY)) + 1; -- ~3 + tRCD + CAS_LATENCY
constant CYCLE_READ_END : integer := CYCLE_READ_START + 1; -- ~4 + tRCD + CAS_LATENCY
constant CYCLE_END : integer := CYCLE_READ_END + 1; -- ~9 + tRCD + CAS_LATENCY
constant CYCLE_RFSH_START : integer := clockTicks(SDRAM_tRP, SDRAM_CLK_FREQ); -- ~tRP
constant CYCLE_RFSH_END : integer := CYCLE_RFSH_START + clockTicks(SDRAM_tRFC, SDRAM_CLK_FREQ) + clockTicks(SDRAM_tRP, SDRAM_CLK_FREQ) + 1; -- ~tRP (start) + tRFC (min autorefresh time) + tRP (end) in clock ticks.
-- Period in clock cycles between SDRAM refresh cycles. This equates to tREF / SDRAM_ROWS to evenly divide the time, then subtract the length of the refresh period as this is
-- the time it takes when a refresh starts until completion.
constant REFRESH_PERIOD : integer := (((SDRAM_tREF * SDRAM_CLK_FREQ ) / SDRAM_ROWS) - (SDRAM_tRFC * 1000)) / 1000;
-- Array of row addresses, one per bank, to indicate the row in use per bank.
type BankArray is array(natural range 0 to SDRAM_BANKS-1) of std_logic_vector(SDRAM_ROW_BITS-1 downto 0);
type BankCacheArray is array(natural range 0 to SDRAM_BANKS-1) of std_logic_vector(((SDRAM_ROW_BITS-1)+SDRAM_BANK_BITS) downto 0);
-- SDRAM domain signals.
signal sdBusy : std_logic;
signal sdCycle : integer range 0 to 31;
signal sdDone : std_logic;
shared variable sdCmd : std_logic_vector(4 downto 0);
signal sdRefreshCount : unsigned(11 downto 0);
signal sdAutoRefresh : std_logic;
signal sdResetTimer : unsigned(WORD_8BIT_RANGE);
signal sdInResetCounter : unsigned(WORD_8BIT_RANGE);
signal sdIsReady : std_logic;
signal sdActiveRow : BankArray;
signal sdActiveBank : std_logic_vector(1 downto 0);
signal sdWriteColumnAddr : unsigned(SDRAM_COLUMN_BITS-1 downto 0); -- Address at byte level as bit 0 is used as part of the fifo write enable.
signal sdWriteCnt : integer range 0 to SDRAM_COLUMNS-1;
-- CPU domain signals.
signal cpuBusy : std_logic;
signal cpuDQM : std_logic_vector(3 downto 0);
signal cpuBank : natural range 0 to SDRAM_BANKS-1;
signal cpuRow : std_logic_vector(SDRAM_ROW_BITS-1 downto 0);
signal cpuCol : std_logic_vector(SDRAM_COLUMN_BITS-1 downto 0);
signal cpuDataOut : std_logic_vector(WORD_32BIT_RANGE);
signal cpuDataIn : std_logic_vector(WORD_32BIT_RANGE);
signal cpuDoneLast : std_logic;
signal cpuIsWriting : std_logic;
signal cpuLastEN : std_logic;
signal cpuCachedBank : std_logic_vector(SDRAM_BANK_BITS-1 downto 0);
signal cpuCachedRow : BankCacheArray;
signal wbACK : std_logic;
-- Infer a BRAM array for 4 banks of 16bit words. 32bit is created by 2 arrays.
type ramArray is array(natural range 0 to ((SDRAM_COLUMNS/2)*4)-1) of std_logic_vector(WORD_8BIT_RANGE);
-- Declare the BRAM arrays for 32bit as a set of 4 x 8bit banks.
shared variable fifoCache_3 : ramArray :=
(
others => X"00"
);
shared variable fifoCache_2 : ramArray :=
(
others => X"00"
);
shared variable fifoCache_1 : ramArray :=
(
others => X"00"
);
shared variable fifoCache_0 : ramArray :=
(
others => X"00"
);
-- Fifo control signals.
signal fifoDataOutHi : std_logic_vector(WORD_16BIT_RANGE);
signal fifoDataOutLo : std_logic_vector(WORD_16BIT_RANGE);
signal fifoDataInHi : std_logic_vector(WORD_16BIT_RANGE);
signal fifoDataInLo : std_logic_vector(WORD_16BIT_RANGE);
signal fifoSdWREN_1 : std_logic;
signal fifoSdWREN_0 : std_logic;
signal fifoCPUWREN_3 : std_logic;
signal fifoCPUWREN_2 : std_logic;
signal fifoCPUWREN_1 : std_logic;
signal fifoCPUWREN_0 : std_logic;
begin
-- Main FSM for SDRAM control and refresh.
process(ALL)
begin
if (SDRAM_RST = '1') then
sdResetTimer <= (others => '0'); -- 0 upto 127
sdInResetCounter <= (others => '1'); -- 255 downto 0
sdAutoRefresh <= '0';
sdRefreshCount <= (others => '0');
sdActiveBank <= (others => '0');
sdActiveRow <= ((others => '0'), (others => '0'), (others => '0'), (others => '0'));
sdIsReady <= '0';
sdCmd := CMD_AUTO_REFRESH;
SDRAM_DQM <= (others => '1');
sdCycle <= 0;
sdDone <= '0';
fifoSdWREN_0 <= '0';
fifoSdWREN_1 <= '0';
sdWriteColumnAddr <= (others => '0');
elsif rising_edge(SDRAM_CLK) then
-- Write Enables are only 1 clock wide, clear on each cycle.
fifoSdWREN_1 <= '0';
fifoSdWREN_0 <= '0';
-- Tri-state control, set the SDRAM databus to tri-state if we are not in write mode.
if (cpuIsWriting = '0') then
SDRAM_DQ <= (others => 'Z');
end if;
-- If no specific command given the default is NOP.
sdCmd := CMD_NOP;
-- Initialisation on power up or reset. The SDRAM must be given at least 200uS to initialise and a fixed setup pattern applied.
if (sdIsReady = '0') then
sdResetTimer <= sdResetTimer + 1;
-- 1uS timer.
if (sdResetTimer = SDRAM_CLK_FREQ/1000000) then
sdResetTimer <= (others => '0');
sdInResetCounter <= sdInResetCounter - 1;
end if;
-- Every 1uS check for the next init action.
if (sdResetTimer = 0) then
-- 200uS wait, no action as the SDRAM starts up.
-- ie. 255 downto 55
-- Precharge all banks
if(sdInResetCounter = 55) then
sdCmd := CMD_PRECHARGE;
SDRAM_ADDR(10) <= '1';
end if;
-- 8 auto refresh commands as specified in datasheet. The RFS time is 60nS, so using a 1uS timer, issue one after
-- the other.
if(sdInResetCounter >= 40 and sdInResetCounter <= 48) then
sdCmd := CMD_AUTO_REFRESH;
end if;
-- Load the Mode register with our parameters.
if(sdInResetCounter = 39) then
sdCmd := CMD_LOAD_MODE;
SDRAM_ADDR <= MODE;
end if;
-- 8 auto refresh commands as specified in datasheet. The RFS time is 60nS, so using a 1uS timer, issue one after
-- the other.
if(sdInResetCounter >= 30 and sdInResetCounter <= 38) then
sdCmd := CMD_AUTO_REFRESH;
end if;
-- SDRAM ready.
if(sdInResetCounter = 20) then
sdIsReady <= '1';
end if;
end if;
else
-- Counter to time periods between autorefresh.
sdRefreshCount <= sdRefreshCount + 1;
-- This mechanism is used to reduce the possibility of metastability issues due to differing clocks.
-- We only act after both Busy signals are high, thus one SDRAM clock after cpuBusy goes high.
sdBusy <= cpuBusy;
-- Auto refresh. On timeout it kicks in so that ROWS auto refreshes are
-- issued in a tRFC period. Other bus operations are stalled during this period.
if (sdRefreshCount > REFRESH_PERIOD and sdCycle = 0) then
sdAutoRefresh <= '1';
sdRefreshCount <= (others => '0');
sdCmd := CMD_PRECHARGE;
SDRAM_ADDR(10) <= '1';
sdActiveBank <= (others => '0');
sdActiveRow <= ((others => '0'), (others => '0'), (others => '0'), (others => '0'));
-- In auto refresh period.
elsif (sdAutoRefresh = '1') then
-- while the cycle is active count.
sdCycle <= sdCycle + 1;
case (sdCycle) is
when CYCLE_RFSH_START =>
sdCmd := CMD_AUTO_REFRESH;
when CYCLE_RFSH_END =>
-- reset the count.
sdAutoRefresh <= '0';
sdCycle <= 0;
when others =>
end case;
elsif ((cpuBusy = '1' and sdCycle = 0) or sdCycle /= 0) then -- or (sdCycle = 0 and CS = '1')) then
-- while the cycle is active count.
sdCycle <= sdCycle + 1;
case (sdCycle) is
when CYCLE_PRECHARGE =>
-- If the bank is not open then no need to precharge, move onto RAS.
if (sdActiveBank(cpuBank) = '0') then
sdCycle <= CYCLE_RAS_START;
-- If the requested row is already active, go to CAS for immediate access to this row.
elsif (sdActiveRow(cpuBank) = cpuRow) then
sdCycle <= CYCLE_CAS_START;
-- Otherwise we close out the open bank by issuing a PRECHARGE.
else
sdCmd := CMD_PRECHARGE;
SDRAM_ADDR(10) <= '0';
SDRAM_BA <= std_logic_vector(to_unsigned(cpuBank, SDRAM_BA'length));
sdActiveBank(cpuBank) <= '0'; -- Store flag to indicate which bank is being made active.
end if;
-- Open the requested row.
when CYCLE_RAS_START =>
sdCmd := CMD_ACTIVE;
SDRAM_ADDR <= cpuRow; -- Addr presented to SDRAM as row address.
SDRAM_BA <= std_logic_vector(to_unsigned(cpuBank, SDRAM_BA'length)); -- Addr presented to SDRAM as bank select.
sdActiveRow(cpuBank) <= cpuRow; -- Store number of row being made active
sdActiveBank(cpuBank) <= '1'; -- Store flag to indicate which bank is being made active.
-- CAS start, for 32 bit chips, only 1 CAS cycle is needed, for 16bit chips we need 2 to read/write 2x16bit words.
when CYCLE_CAS_START =>
-- If writing, setup for a write with preset mask.
if (cpuIsWriting = '1') then
sdCmd := CMD_WRITE;
if SDRAM_DATAWIDTH = 32 then
SDRAM_ADDR <= std_logic_vector(to_unsigned(to_integer(unsigned(cpuCol(SDRAM_COLUMN_BITS-1 downto 2) & '0' & '0')), SDRAM_ROW_BITS)); -- CAS address = Address accessing 32bit data with no auto precharge
SDRAM_DQ <= cpuDataIn; -- Assign corresponding data to the SDRAM databus.
SDRAM_DQM <= not cpuDQM(3 downto 0);
sdDone <= '1';
sdCycle <= CYCLE_END;
-- A fake statement used to convince Quartus Prime to infer block ram for the fifo and not use registers.
fifoDataInHi <= fifoDataOutHi;
fifoDataInLo <= fifoDataOutLo;
elsif SDRAM_DATAWIDTH = 16 then
SDRAM_ADDR <= std_logic_vector(to_unsigned(to_integer(unsigned(cpuCol(SDRAM_COLUMN_BITS-1 downto 1) & '0')), SDRAM_ROW_BITS)); -- CAS address = Address accessing first 16bit location within the 32bit external alignment with no auto precharge
SDRAM_DQ <= cpuDataIn((SDRAM_DATAWIDTH*2)-1 downto SDRAM_DATAWIDTH); -- Assign corresponding data to the SDRAM databus.
SDRAM_DQM <= not cpuDQM(3 downto 2);
-- A fake statement used to convince Quartus Prime to infer block ram for the fifo and not use registers.
fifoDataInHi <= fifoDataOutHi;
else
report "SDRAM datawidth parameter invalid, should be 16 or 32!" severity error;
end if;
else
-- Setup for a read.
sdCmd := CMD_READ;
SDRAM_ADDR <= (others => '0');
SDRAM_DQM <= "00"; -- For reads dont mask the data output.
sdWriteCnt <= SDRAM_COLUMNS-1;
sdWriteColumnAddr <= (others => '1');
end if;
-- For writes, this state writes out the second word of a 32bit word if we have a 16bit wide SDRAM chip.
--
when CYCLE_WRITE_END =>
-- When writing, setup for a write with preset mask with the correct word.
if (cpuIsWriting = '1') then
SDRAM_ADDR <= std_logic_vector(to_unsigned(to_integer(unsigned(cpuCol(SDRAM_COLUMN_BITS-1 downto 1) & '1')), SDRAM_ROW_BITS)); -- CAS address = Next address accessing second 16bit location within the 32bit external alignment with no auto precharge
sdCmd := CMD_WRITE;
SDRAM_DQM <= not cpuDQM(1 downto 0);
SDRAM_DQ <= cpuDataIn(SDRAM_DATAWIDTH-1 downto 0);
sdDone <= '1';
sdCycle <= CYCLE_END;
-- A fake statement used to convince Quartus Prime to infer block ram for the fifo and not use registers.
fifoDataInLo <= fifoDataOutLo;
end if;
-- Data is available after CAS Latency (2 or 3) clocks after the read request.
-- The data is read as a full page burst, 1 clock per word.
when CYCLE_READ_START =>
if SDRAM_DATAWIDTH = 32 then
fifoSdWREN_1 <= '1';
fifoSdWREN_0 <= '1';
sdWriteCnt <= sdWriteCnt - 2;
sdWriteColumnAddr <= sdWriteColumnAddr + 2;
fifoDataInHi <= SDRAM_DQ(WORD_UPPER_16BIT_RANGE);
fifoDataInLo <= SDRAM_DQ(WORD_LOWER_16BIT_RANGE);
if sdWriteCnt > 1 then
sdCycle <= CYCLE_READ_START;
end if;
elsif SDRAM_DATAWIDTH = 16 then
if fifoSdWREN_1 = '0' then
fifoSdWREN_1 <= '1';
fifoDataInHi <= SDRAM_DQ;
else
fifoSdWREN_0 <= '1';
fifoDataInLo <= SDRAM_DQ;
end if;
sdWriteCnt <= sdWriteCnt - 1;
sdWriteColumnAddr <= sdWriteColumnAddr + 1;
if sdWriteCnt > 0 then
sdCycle <= CYCLE_READ_START;
end if;
else
report "SDRAM datawidth parameter invalid, should be 16 or 32!" severity error;
end if;
when CYCLE_READ_END =>
sdDone <= '1';
when CYCLE_END =>
sdCycle <= 0;
sdDone <= '0';
-- Other states are wait states, waiting for the correct time slot for SDRAM access.
when others =>
end case;
else
sdCycle <= 0;
end if;
end if;
-- drive control signals according to current command
SDRAM_CKE <= sdCmd(4);
SDRAM_CS_n <= sdCmd(3);
SDRAM_RAS_n <= sdCmd(2);
SDRAM_CAS_n <= sdCmd(1);
SDRAM_WE_n <= sdCmd(0);
end if;
end process;
-- CPU/BUS side logic. When the CPU initiates a transaction, capture the signals and the captured values are used within the SDRAM domain. This is to prevent
-- any changes CPU side or differing signal lengths due to CPU architecture or clock being propogated into the SDRAM domain. The CPU only needs to know
-- when the transation is complete and data read.
--
process(ALL)
variable bank : std_logic_vector(1 downto 0);
variable row : std_logic_vector(SDRAM_ROW_BITS-1 downto 0);
variable writeThru : std_logic;
begin
-- Setup the bank and row as variables to make code reading easier.
bank := WB_ADR_I(SDRAM_ADDR_BITS-1) & WB_ADR_I((SDRAM_COLUMN_BITS+SDRAM_BANK_BITS-1) downto (SDRAM_COLUMN_BITS+1));
row := WB_ADR_I(SDRAM_ADDR_BITS-2 downto (SDRAM_COLUMN_BITS+SDRAM_BANK_BITS));
-- For write operations, if the cached page row for the current bank is the same as the row given by the cpu then we write to both the SDRAM and to the cache.
if cpuCachedBank(to_integer(unsigned(bank))) = '1' and cpuCachedRow(to_integer(unsigned(bank))) = WB_ADR_I(SDRAM_ADDR_BITS-1) & WB_ADR_I((SDRAM_COLUMN_BITS+SDRAM_BANK_BITS-1) downto (SDRAM_COLUMN_BITS+1)) & row then
writeThru := '1';
else
writeThru := '0';
end if;
-- Setup signals to initial state, critical they start at the right values.
if (WB_RST_I = '1') then
cpuDoneLast <= '0';
cpuBusy <= '0';
cpuBank <= 0;
cpuRow <= (others => '0');
cpuCol <= (others => '0');
cpuDQM <= (others => '1');
cpuLastEN <= '0';
cpuCachedBank <= (others => '0');
cpuCachedRow <= ( others => (others => '0') );
cpuIsWriting <= '0';
fifoCPUWREN_3 <= '0';
fifoCPUWREN_2 <= '0';
fifoCPUWREN_1 <= '0';
fifoCPUWREN_0 <= '0';
wbACK <= '0';
-- Wait for the SDRAM to become ready by holding the CPU in a wait state.
elsif sdIsReady = '0' then
cpuBusy <= '1';
elsif rising_edge(WB_CLK) then
-- CPU Cache writes are only 1 cycle wide, so clear any asserted write.
fifoCPUWREN_3 <= '0';
fifoCPUWREN_2 <= '0';
fifoCPUWREN_1 <= '0';
fifoCPUWREN_0 <= '0';
if wbACK = '1' then
wbACK <= '0';
end if;
-- Detect a Wishbone cycle and commence an SDRAM access.
if (WB_STB_I = '1' and WB_CYC_I = '1' and cpuBusy = '0' and wbACK = '0') then
-- Organisation of the memory is as follows:
--
-- Bank: [(SDRAM_ADDR_BITS-1) .. (SDRAM_ADDR_BITS-1)] & [((SDRAM_COLUMN_BITS+SDRAM_BANK_BITS-1) .. (SDRAM_COLUMN_BITS+1)]
-- Row: [(SDRAM_ADDR_BITS-2) .. (SDRAM_COLUMN_BITS+SDRAM_BANK_BITS)]
-- Column: [(SDRAM_COLUMN_BITS downto 2)]
-- The bank is split so that the Bank MSB splits the SDRAM in 2, upper and lower segment, this is because Stack normally resides in the top upper
-- segment and code in the bottom lower segment. The remaining bank bits are split at the page level such that 2 or more pages residing in different
-- banks are contiguous, hoping to gain a little performance benefit through having a wider spread for code caching and stack caching and write thru.
--
cpuBank <= to_integer(unsigned(bank));
cpuRow <= row;
cpuCol <= WB_ADR_I(SDRAM_COLUMN_BITS downto 2) & '0';
cpuDQM <= WB_SEL_I;
cpuDataIn <= WB_DATA_I;
-- For write operations, we write direct to memory. If the data is in cache then a write-thru is performed to preserve the cached bank.
if WB_WE_I = '1' then
-- If we are writing to a cached page, update the changed bytes in cache.
if writeThru = '1' then
if WB_SEL_I(0) then
fifoCPUWREN_0 <= '1';
end if;
if WB_SEL_I(1) then
fifoCPUWREN_1 <= '1';
end if;
if WB_SEL_I(2) then
fifoCPUWREN_2 <= '1';
end if;
if WB_SEL_I(3) then
fifoCPUWREN_3 <= '1';
end if;
end if;
-- Set the flags, cpuBusy indicates to the SDRAM FSM to perform an operation.
cpuIsWriting <= WB_WE_I;
cpuBusy <= '1';
-- For reads, if the row is cached then we just fall through to perform a read operation from cache otherwise the
-- SDRAM needs to be instructed to read a page into cache before reading.
--
elsif cpuCachedBank(to_integer(unsigned(bank))) = '0' or cpuCachedRow(to_integer(unsigned(bank))) /= WB_ADR_I(SDRAM_ADDR_BITS-1) & WB_ADR_I((SDRAM_COLUMN_BITS+SDRAM_BANK_BITS-1) downto (SDRAM_COLUMN_BITS+1)) & row then
cpuCachedBank(to_integer(unsigned(bank))) <= '1';
cpuCachedRow (to_integer(unsigned(bank))) <= WB_ADR_I(SDRAM_ADDR_BITS-1) & WB_ADR_I((SDRAM_COLUMN_BITS+SDRAM_BANK_BITS-1) downto (SDRAM_COLUMN_BITS+1)) & row;
-- Set the flags, cpuBusy indicates to the SDRAM FSM to perform an operation.
cpuBusy <= '1';
else
wbACK <= '1';
end if;
end if;
-- Note SDRAM activity via a previous/last signal.
cpuDoneLast <= sdDone;
-- A change in the Done signal then we end the SDRAM request and release the CPU.
if cpuDoneLast = '1' and sdDone = '0' then
cpuBusy <= '0';
cpuIsWriting <= '0';
wbACK <= '1';
end if;
end if;
end process;
-- System bus control signals.
SDRAM_READY <= sdIsReady;
-- Wishbone bus control signals.
WB_ACK_O <= wbACK;
--- Throttle not needed.
WB_HALT_O <= '0';
--- Error not yet implemented.
WB_ERR_O <= '0';
-------------------------------------------------------------------------------------------------------------------------
-- Inferred Dual Port RAM.
--
-- The dual port ram is used to buffer a full page within the SDRAM, one buffer for each bank. The addressing is such
-- that half of the banks appear in the lower segment of the address space and half in the top segment, the MSB of the
-- SDRAM address is used for the split. This is to cater for stack where typically, on the ZPU, the stack would reside
-- in the very top of memory working down and the applications would reside at the bottom of the memory working up.
--
-------------------------------------------------------------------------------------------------------------------------
-- SDRAM Side of dual port RAM.
-- For Read: fifoDataOutHi <= fifoCache_3(sdWriteColumnAddr)
-- fifoDataOutLo <= fifoCache_0(sdWriteColumnAddr)
-- For Write: fifoCache_3 _1 <= fifoDataIn when sdWriteColumnAddr(0) = '0'
-- fifoCache_2 _0 <= fifoDataIn when sdWriteColumnAddr(0) = '1'
-- fifoSdWREN must be asserted ('1') for write operations.
process(ALL)
variable cacheAddr : unsigned(SDRAM_COLUMN_BITS-2+SDRAM_BANK_BITS downto 0);
begin
-- Setup the address based on the index (sdWriteColumnAddr) and the bank (cpuBank) as the cache is linear for 4 banks.
--
cacheAddr := to_unsigned(cpuBank, SDRAM_BANK_BITS) & sdWriteColumnAddr(SDRAM_COLUMN_BITS-1 downto 1);
if rising_edge(SDRAM_CLK) then
if fifoSdWREN_1 = '1' then
fifoCache_3(to_integer(cacheAddr)) := fifoDataInHi(WORD_UPPER_16BIT_RANGE);
fifoCache_2(to_integer(cacheAddr)) := fifoDataInHi(WORD_LOWER_16BIT_RANGE);
else
fifoDataOutHi(WORD_UPPER_16BIT_RANGE) <= fifoCache_3(to_integer(cacheAddr));
fifoDataOutHi(WORD_LOWER_16BIT_RANGE) <= fifoCache_2(to_integer(cacheAddr));
end if;
if fifoSdWREN_0 = '1' then
fifoCache_1(to_integer(cacheAddr)) := fifoDataInLo(WORD_UPPER_16BIT_RANGE);
fifoCache_0(to_integer(cacheAddr)) := fifoDataInLo(WORD_LOWER_16BIT_RANGE);
else
fifoDataOutLo(WORD_UPPER_16BIT_RANGE) <= fifoCache_1(to_integer(cacheAddr));
fifoDataOutLo(WORD_LOWER_16BIT_RANGE) <= fifoCache_0(to_integer(cacheAddr));
end if;
end if;
end process;
-- CPU Side of dual port RAM, byte addressable.
-- For Read: DATA_OUT <= fifoCache(bank + ADDR(COLUMN_BITS .. 2))
-- For Write: fifoCache(0..3) <= cpuDataIn
process(ALL)
variable cacheAddr : unsigned(SDRAM_COLUMN_BITS-2+SDRAM_BANK_BITS downto 0);
begin
-- Setup the address based on the column address bits, 32 bit aligned and the bank (cpuBank) as the cache is linear for 4 banks.
--
cacheAddr := to_unsigned(cpuBank, SDRAM_BANK_BITS) & unsigned(WB_ADR_I(SDRAM_COLUMN_BITS downto 2));
if rising_edge(WB_CLK) then
if fifoCPUWREN_3 = '1' then
fifoCache_3(to_integer(cacheAddr)) := cpuDataIn(31 downto 24);
else
WB_DATA_O((SDRAM_DATAWIDTH*2)-1 downto ((SDRAM_DATAWIDTH*2)-(SDRAM_DATAWIDTH/2)))<= fifoCache_3(to_integer(unsigned(cacheAddr)));
end if;
if fifoCPUWREN_2 = '1' then
fifoCache_2(to_integer(cacheAddr)) := cpuDataIn(23 downto 16);
else
WB_DATA_O(((SDRAM_DATAWIDTH*2)-(SDRAM_DATAWIDTH/2))-1 downto SDRAM_DATAWIDTH) <= fifoCache_2(to_integer(unsigned(cacheAddr)));
end if;
if fifoCPUWREN_1 = '1' then
fifoCache_1(to_integer(cacheAddr)) := cpuDataIn(15 downto 8);
else
WB_DATA_O(SDRAM_DATAWIDTH-1 downto SDRAM_DATAWIDTH/2) <= fifoCache_1(to_integer(unsigned(cacheAddr)));
end if;
if fifoCPUWREN_0 = '1' then
fifoCache_0(to_integer(cacheAddr)) := cpuDataIn(7 downto 0);
else
WB_DATA_O((SDRAM_DATAWIDTH/2)-1 downto 0) <= fifoCache_0(to_integer(unsigned(cacheAddr)));
end if;
end if;
end process;
end Structure;

View File

@@ -1,159 +0,0 @@
---------------------------------------------------------------------------------------------------------
--
-- Name: sram.vhd
-- Created: September 2019
-- Author(s): Philip Smart
-- Description: WishBone encapsulation of BRAM memory.
--
-- Credits:
-- Copyright: (c) 2019 Philip Smart <philip.smart@net2net.org>
--
-- History: September 2019 - Initial creation.
--
---------------------------------------------------------------------------------------------------------
-- 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->.
---------------------------------------------------------------------------------------------------------
library ieee;
library pkgs;
library work;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.zpu_soc_pkg.all;
use work.zpu_pkg.all;
entity SRAM is
generic (
addrbits : integer := 16 -- Size, in bits (representing bytes), of total memory to allocate.
);
port (
-- Wishbone Bus --
WB_CLK_I : in std_logic; -- WishBone master clock
WB_RST_I : in std_logic; -- high active sync reset
WB_CYC_I : in std_logic;
WB_TGC_I : in std_logic_vector(06 downto 0); -- cycle tag
WB_ADR_I : in std_logic_vector(addrbits-1 downto 0); -- adr in
WB_DATA_I : in std_logic_vector(31 downto 0); -- write data
WB_DATA_O : out std_logic_vector(31 downto 0); -- read data
WB_SEL_I : in std_logic_vector(03 downto 0); -- data quantity
WB_WE_I : in std_logic; -- write enable
WB_STB_I : in std_logic; -- valid cycle
WB_ACK_O : out std_logic; -- acknowledge
WB_CTI_I : in std_logic_vector(2 downto 0); -- 000 Classic cycle, 001 Constant address burst cycle, 010 Incrementing burst cycle, 111 End-of-Burst
WB_HALT_O : out std_logic; -- throttle master
WB_ERR_O : out std_logic -- abnormal cycle termination
);
end SRAM;
architecture Behavioral of SRAM is
--- Muxed ACK signal.
signal WB_ACK_O_INT : std_logic;
-- Define memory as an array of 4x8bit blocks to allow for individual byte write/read.
type ramArray is array(natural range 0 to (2**(addrbits-2))-1) of std_logic_vector(7 downto 0);
shared variable RAM0 : ramArray :=
(
others => x"AA"
);
shared variable RAM1 : ramArray :=
(
others => x"55"
);
shared variable RAM2 : ramArray :=
(
others => x"AA"
);
shared variable RAM3 : ramArray :=
(
others => x"55"
);
begin
-- RAM Byte 0 - bits 7 to 0
process(WB_CLK_I)
begin
if rising_edge(WB_CLK_I) then
if WB_WE_I = '1' and WB_STB_I = '1' and WB_SEL_I(0) = '1' then
RAM0(to_integer(unsigned(WB_ADR_I(addrbits-1 downto 2)))) := WB_DATA_I(7 downto 0);
else
WB_DATA_O(7 downto 0) <= RAM0(to_integer(unsigned(WB_ADR_I(addrbits-1 downto 2))));
end if;
end if;
end process;
-- RAM Byte 1 - bits 15 to 8
process(WB_CLK_I)
begin
if rising_edge(WB_CLK_I) then
if WB_WE_I = '1' and WB_STB_I = '1' and WB_SEL_I(1) = '1' then
RAM1(to_integer(unsigned(WB_ADR_I(addrbits-1 downto 2)))) := WB_DATA_I(15 downto 8);
else
WB_DATA_O(15 downto 8) <= RAM1(to_integer(unsigned(WB_ADR_I(addrbits-1 downto 2))));
end if;
end if;
end process;
-- RAM Byte 2 - bits 23 to 16
process(WB_CLK_I)
begin
if rising_edge(WB_CLK_I) then
if WB_WE_I = '1' and WB_STB_I = '1' and WB_SEL_I(2) = '1' then
RAM2(to_integer(unsigned(WB_ADR_I(addrbits-1 downto 2)))) := WB_DATA_I(23 downto 16);
else
WB_DATA_O(23 downto 16) <= RAM2(to_integer(unsigned(WB_ADR_I(addrbits-1 downto 2))));
end if;
end if;
end process;
-- RAM Byte 3 - bits 31 to 24
process(WB_CLK_I)
begin
if rising_edge(WB_CLK_I) then
if WB_WE_I = '1' and WB_STB_I = '1' and WB_SEL_I(3) = '1' then
RAM3(to_integer(unsigned(WB_ADR_I(addrbits-1 downto 2)))) := WB_DATA_I(31 downto 24);
else
WB_DATA_O(31 downto 24) <= RAM3(to_integer(unsigned(WB_ADR_I(addrbits-1 downto 2))));
end if;
end if;
end process;
-- WishBone control.
WISHBONECTL: process(WB_CLK_I)
begin
if rising_edge(WB_CLK_I) then
--- ACK Control
if (WB_RST_I = '1') then
WB_ACK_O_INT <= '0';
elsif (WB_CTI_I = "000") or (WB_CTI_I = "111") then
WB_ACK_O_INT <= WB_STB_I and (not WB_ACK_O_INT);
else
WB_ACK_O_INT <= WB_STB_I;
end if;
end if;
end process;
--- ACK Signal
WB_ACK_O <= WB_ACK_O_INT;
--- Throttle
WB_HALT_O <= '0';
--- Error
WB_ERR_O <= '0';
end Behavioral;

View File

@@ -1,50 +0,0 @@
library ieee;
library pkgs;
library work;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.zpu_soc_pkg.all;
use work.zpu_pkg.all;
LIBRARY altera_mf;
USE altera_mf.altera_mf_components.all;
entity oddrff is
port (
CLK: in std_ulogic;
D0: in std_logic;
D1: in std_logic;
O: out std_ulogic
);
end entity oddrff;
architecture behave of oddrff is
signal D0_v, D1_v, O_v: std_logic_vector(0 downto 0);
begin
ALTDDIO_OUT_component : ALTDDIO_OUT
GENERIC MAP (
extend_oe_disable => "OFF",
intended_device_family => "Cyclone IV E",
invert_output => "OFF",
lpm_hint => "UNUSED",
lpm_type => "altddio_out",
oe_reg => "UNREGISTERED",
power_up_high => "OFF",
width => 1
)
PORT MAP (
datain_h => D1_v,
datain_l => D0_v,
outclock => CLK,
dataout => O_v
);
D1_v(0) <= D0;
D0_v(0) <= D1;
O <= O_v(0);
end behave;

View File

@@ -1,2 +0,0 @@
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) sdram.vhd ]
set_global_assignment -name SDC_FILE [file join $::quartus(qip_path) sdram.sdc ]

View File

@@ -1,26 +0,0 @@
derive_pll_clocks
#create_generated_clock -source [get_pins -compatibility_mode {*|pll|pll_inst|altera_pll_i|general[1].gpll~PLL_OUTPUT_COUNTER|divclk}]
#{*mypll|altpll_component|auto_generated|generic_pll1~PLL_OUTPUT_COUNTER|divclk}] \
#create_generated_clock -source [get_pins -compatibility_mode {mypll|altpll_component|pll|clk[1]}] \
# -name MEMCLK [get_ports {MEMCLK}]
#create_generated_clock -source [get_pins -compatibility_mode {mypll|altpll_component|pll|clk[1]}] -multiply_by 1 \
# -name MEMCLK [get_ports {MEMCLK}]
#create_generated_clock -name {MEMCLK} -source [get_ports {CLOCK_12M}] -duty_cycle 50.000 -multiply_by 25 -divide_by 2 -master_clock {clk_12} [get_nets {mypll|altpll_component|_clk1}]
#create_generated_clock -name {MEMCLK} -source [get_pins -compatibility_mode {mypll|altpll_component|pll|clk[1]}] -master_clock {MEMCLK} [get_ports {MEMCLK}]
derive_clock_uncertainty
# Set acceptable delays for SDRAM chip (See correspondent chip datasheet)
set_input_delay -max -clock MEMCLK 6.4ns [get_ports SDRAM_DQ[*]]
set_input_delay -min -clock MEMCLK 3.7ns [get_ports SDRAM_DQ[*]]
# -to [get_clocks {*|pll|pll_inst|altera_pll_i|general[0].gpll~PLL_OUTPUT_COUNTER|divclk}]
#set_multicycle_path -from [get_clocks {MEMCLK}] \
# -to [get_clocks {SYSCLK}] \
# -setup 2
set_output_delay -max -clock MEMCLK 1.6ns [get_ports {SDRAM_D* SDRAM_ADDR* SDRAM_BA* SDRAM_CS SDRAM_WE SDRAM_RAS SDRAM_CAS SDRAM_CKE}]
set_output_delay -min -clock MEMCLK -0.9ns [get_ports {SDRAM_D* SDRAM_ADDR* SDRAM_BA* SDRAM_CS SDRAM_WE SDRAM_RAS SDRAM_CAS SDRAM_CKE}]

View File

@@ -1,19 +0,0 @@
derive_pll_clocks
#create_generated_clock -source [get_pins -compatibility_mode {*|pll|pll_inst|altera_pll_i|general[1].gpll~PLL_OUTPUT_COUNTER|divclk}]
create_generated_clock -source [get_pins -compatibility_mode {*mypll|altpll_component|auto_generated|generic_pll1~PLL_OUTPUT_COUNTER|divclk}] \
-name SDRAM_CLK [get_ports {SDRAM_CLK}]
derive_clock_uncertainty
# Set acceptable delays for SDRAM chip (See correspondent chip datasheet)
set_input_delay -max -clock SDRAM_CLK 6.4ns [get_ports SDRAM_DQ[*]]
set_input_delay -min -clock SDRAM_CLK 3.7ns [get_ports SDRAM_DQ[*]]
# -to [get_clocks {*|pll|pll_inst|altera_pll_i|general[0].gpll~PLL_OUTPUT_COUNTER|divclk}]
#set_multicycle_path -from [get_clocks {SDRAM_CLK}] \
# -to [get_clocks {*mypll|altpll_component|auto_generated|generic_pll1~PLL_OUTPUT_COUNTER|divclk}] \
# -setup 2
set_output_delay -max -clock SDRAM_CLK 1.6ns [get_ports {SDRAM_D* SDRAM_ADDR* SDRAM_BA* SDRAM_CS SDRAM_WE SDRAM_RAS SDRAM_CAS SDRAM_CKE}]
set_output_delay -min -clock SDRAM_CLK -0.9ns [get_ports {SDRAM_D* SDRAM_ADDR* SDRAM_BA* SDRAM_CS SDRAM_WE SDRAM_RAS SDRAM_CAS SDRAM_CKE}]

View File

@@ -1,488 +0,0 @@
---------------------------------------------------------------------------------------------------------
--
-- Name: sdram.vhd
-- Created: September 2019
-- Original Author: Stephen J. Leary 2013-2014
-- VHDL Author: Philip Smart
-- Description: Original Wishbone module written by Stephen J. Leary 2013-2014 in Verilog for use
-- with the MT48LC16M16 chip.
-- It has been translated into VHDL and adapted for the system bus and undergoing
-- extensive modifications to work with the ZPU EVO processor, specifically burst
-- tuning to enhance L2 Cache Fill performance.
-- Credits:
-- Copyright: Copyright (c) 2013-2014, Stephen J. Leary, All rights reserved.
-- VHDL translation, sysbus adaptation and enhancements (c) 2019 Philip Smart
-- <philip.smart@net2net.org>
--
-- History: September 2019 - Initial module translation to VHDL based on Stephen J. Leary's Verilog
-- source code.
-- November 2019 - Adapted for the system bus for use when no Wishbone interface is
-- instantiated in the ZPU Evo.
--
---------------------------------------------------------------------------------------------------------
-- 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->.
---------------------------------------------------------------------------------------------------------
library ieee;
library pkgs;
library work;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.zpu_soc_pkg.all;
use work.zpu_pkg.all;
entity SDRAM is
generic (
MAX_DATACACHE_BITS : integer := 4; -- Maximum size in addr bits of 32bit datacache for burst transactions.
SDRAM_COLUMNS : integer := 256; -- Number of Columns in an SDRAM page (ie. 1 row).
SDRAM_ROWS : integer := 4096; -- Number of Rows in the SDRAM.
SDRAM_BANKS : integer := 4 -- Number of banks in the SDRAM.
);
port (
-- SDRAM Interface
SDRAM_CLK : in std_logic; -- sdram is accessed at 100MHz
SDRAM_RST : in std_logic; -- reset the sdram controller.
SDRAM_CKE : out std_logic; -- clock enable.
SDRAM_DQ : inout std_logic_vector(15 downto 0); -- 16 bit bidirectional data bus
SDRAM_ADDR : out std_logic_vector(log2ceil(SDRAM_ROWS) - 1 downto 0); -- Multiplexed address bus
SDRAM_DQM : out std_logic_vector(log2ceil(SDRAM_BANKS) - 1 downto 0); -- Number of byte masks dependent on number of banks.
SDRAM_BA : out std_logic_vector(log2ceil(SDRAM_BANKS) - 1 downto 0); -- Number of banks in SDRAM
SDRAM_CS_n : out std_logic; -- Single chip select
SDRAM_WE_n : out std_logic; -- write enable
SDRAM_RAS_n : out std_logic; -- row address select
SDRAM_CAS_n : out std_logic; -- columns address select
SDRAM_READY : out std_logic; -- sd ready.
-- CPU Interface
CLK : in std_logic; -- System master clock
RESET : in std_logic; -- high active sync reset
ADDR : in std_logic_vector(21 downto 0);
DATA_IN : in std_logic_vector(31 downto 0); -- write data
DATA_OUT : out std_logic_vector(31 downto 0); -- read data
WRITE_BYTE : in std_logic; -- write a single byte as specified in A1:A0
WRITE_HWORD : in std_logic; -- write a 16bit word as specified in A1
CS : in std_logic; -- Chip Select.
WREN : in std_logic; -- Write enable.
RDEN : in std_logic; -- Read enable.
BUSY : out std_logic -- Memory is busy, hold CPU.
);
end SDRAM;
architecture Structure of SDRAM is
-- Constants to define the structure of the SDRAM in bits for provisioning of signals.
constant SDRAM_ROW_BITS : integer := log2ceil(SDRAM_ROWS);
constant SDRAM_COLUMN_BITS: integer := log2ceil(SDRAM_COLUMNS);
constant SDRAM_ARRAY_BITS : integer := log2ceil(SDRAM_ROWS * SDRAM_COLUMNS);
constant SDRAM_BANK_BITS : integer := log2ceil(SDRAM_BANKS);
constant SDRAM_ADDR_BITS : integer := log2ceil(SDRAM_ROWS * SDRAM_COLUMNS * SDRAM_BANKS);
-- Constants for correct operation of the SDRAM, these values are taken from the datasheet of the target device.
--
constant tRCD : integer := 4; -- tRCD - RAS to CAS minimum period, ie. 20ns -> 2 cycles@100MHz
constant tRP : integer := 4; -- tRP - Precharge delay, min time for a precharge command to complete, ie. 15ns -> 2 cycles@100MHz
constant tRFC : integer := 70; -- tRFC - Auto-refresh minimum time to complete, ie. 66ns
constant tREF : integer := 64; -- tREF - period of time a complete refresh of all rows is made within.
constant RAM_CLK : integer := 50000000; -- SDRAM Clock in Hertz
-- Command table for a standard SDRAM.
--
-- Name (Function) CS# RAS# CAS# WE# DQM ADDR DQ
-- COMMAND INHIBIT (NOP) H X X X X X X
-- NO OPERATION (NOP) L H H H X X X
-- ACTIVE (select bank and activate row) L L H H X Bank/row X
-- READ (select bank and column, and start READ burst) L H L H L/H Bank/col X
-- WRITE (select bank and column, and start WRITE burst) L H L L L/H Bank/col Valid
-- BURST TERMINATE L H H L X X Active
-- PRECHARGE (Deactivate row in bank or banks) L L H L X Code X
-- AUTO REFRESH or SELF REFRESH (enter self refresh mode) L L L H X X X
-- LOAD MODE REGISTER L L L L X Op-code X
-- Write enable/output enable X X X X L X Active
-- Write inhibit/output High-Z X X X X H X High-Z
constant CMD_INHIBIT : std_logic_vector(3 downto 0) := "1111";
constant CMD_NOP : std_logic_vector(3 downto 0) := "0111";
constant CMD_ACTIVE : std_logic_vector(3 downto 0) := "0011";
constant CMD_READ : std_logic_vector(3 downto 0) := "0101";
constant CMD_WRITE : std_logic_vector(3 downto 0) := "0100";
constant CMD_BURST_TERMINATE : std_logic_vector(3 downto 0) := "0110";
constant CMD_PRECHARGE : std_logic_vector(3 downto 0) := "0010";
constant CMD_AUTO_REFRESH : std_logic_vector(3 downto 0) := "0001";
constant CMD_LOAD_MODE : std_logic_vector(3 downto 0) := "0000";
-- Load Mode Register setting for a standard SDRAM.
--
-- xx:10 = Reserved :
-- 9 = Write Burst Mode : 0 = Programmed Burst Length, 1 = Single Location Access
-- 8:7 = Operating Mode : 00 = Standard Operation, all other values reserved.
-- 6:4 = CAS Latency : 010 = 2, 011 = 3, all other values reserved.
-- 3 = Burst Type : 0 = Sequential, 1 = Interleaved.
-- 2:0 = Burst Length : When 000 = 1, 001 = 2, 010 = 4, 011 = 8, all others reserved except 111 when BT = 0 sets full page access.
-- | A12-A10 | A9  A8-A7 | A6 A5 A4 | A3Â  A2 A1 A0 |
-- | reserved| wr burst |reserved| CAS Ltncy|addr mode| burst len|
constant WRITE_BURST_MODE : std_logic := '1';
constant OP_MODE : std_logic_vector(1 downto 0) := "00";
constant CAS_LATENCY : std_logic_vector(2 downto 0) := "011";
constant BURST_TYPE : std_logic := '0';
constant BURST_LENGTH : std_logic_vector(2 downto 0) := "000";
constant MODE : std_logic_vector(SDRAM_ROW_BITS-1 downto 0) := std_logic_vector(to_unsigned(to_integer(unsigned("00" & WRITE_BURST_MODE & OP_MODE & CAS_LATENCY & BURST_TYPE & BURST_LENGTH)), SDRAM_ROW_BITS));
-- FSM Cycle States governed in units of time, the state changes location according to the configurable parameters to ensure correct actuation at the correct time.
--
constant CYCLE_PRECHARGE : integer := 0; -- 0
constant CYCLE_RAS_START : integer := tRP; -- 3
constant CYCLE_RAS_NEXT : integer := CYCLE_RAS_START + 1; -- 4
constant CYCLE_CAS0 : integer := CYCLE_RAS_START + tRCD; -- 3 + tRCD
constant CYCLE_CAS1 : integer := CYCLE_CAS0 + 1; -- 4 + tRCD
constant CYCLE_READ0 : integer := CYCLE_CAS0 + to_integer(unsigned(CAS_LATENCY)) + 1; -- 3 + tRCD + CAS_LATENCY
constant CYCLE_READ1 : integer := CYCLE_READ0 + 1; -- 4 + tRCD + CAS_LATENCY
constant CYCLE_END : integer := CYCLE_READ1 + 4; -- 9 + tRCD + CAS_LATENCY
constant CYCLE_RFSH_START : integer := CYCLE_RAS_START; -- 3
constant CYCLE_RFSH_END : integer := CYCLE_RFSH_START + ((tRFC/RAM_CLK) * 10000000) + 1; -- 3 + tRFC in clock ticks.
-- Period in clock cycles between SDRAM refresh cycles.
constant REFRESH_PERIOD : integer := (RAM_CLK / (tREF * SDRAM_ROWS)) - CYCLE_END;
type BankArray is array(natural range 0 to 3) of std_logic_vector(SDRAM_ROW_BITS-1 downto 0);
-- Cache for holding burst reads to allow for differing speeds of WishBone Master.
type DataCacheArray is array(natural range 0 to ((2**(MAX_DATACACHE_BITS))-1)) of std_logic_vector(WORD_32BIT_RANGE);
signal readCache : DataCacheArray;
attribute ramstyle : string;
attribute ramstyle of readCache : signal is "logic";
signal cacheReadAddr : unsigned(MAX_DATACACHE_BITS-1 downto 0);
signal cacheWriteAddr : unsigned(MAX_DATACACHE_BITS-1 downto 0);
signal sbBusy : std_logic;
signal sdCycle : integer range 0 to 31;
signal sdDone : std_logic;
signal sdCmd : std_logic_vector(3 downto 0);
signal sdRefreshCount : unsigned(9 downto 0);
signal sdAutoRefresh : std_logic;
signal sdResetTimer : unsigned(7 downto 0);
signal sdMuxAddr : std_logic_vector(SDRAM_ROW_BITS-1 downto 0); -- 12 bit multiplexed address bus
signal sdDoneLast : std_logic;
signal sdInResetCounter : unsigned(7 downto 0);
signal sdIsWriting : std_logic;
signal isReady : std_logic;
signal sdDataOut : std_logic_vector(15 downto 0);
signal sdDataIn : std_logic_vector(15 downto 0);
signal sdActiveRow : BankArray;
signal sdActiveBank : std_logic_vector(1 downto 0);
signal sdBank : natural range 0 to 3;
signal sdRow : std_logic_vector(SDRAM_ROW_BITS-1 downto 0);
signal sdCol : std_logic_vector(SDRAM_COLUMN_BITS-1 downto 0);
signal sdDQM : std_logic_vector(1 downto 0);
signal sdCKE : std_logic;
signal cpuDQM : std_logic_vector(3 downto 0);
signal dout : std_logic_vector(31 downto 0);
signal cpuDataIn : std_logic_vector(31 downto 0);
begin
-- Tri-state control of the SDRAM data bus.
process(sdIsWriting, SDRAM_DQ, sdDataOut)
begin
if (sdIsWriting = '1') then
SDRAM_DQ <= sdDataOut;
sdDataIn <= SDRAM_DQ;
else
SDRAM_DQ <= (others => 'Z');
sdDataIn <= SDRAM_DQ;
end if;
end process;
-- Main FSM for SDRAM control and refresh.
process(SDRAM_CLK, SDRAM_RST)
begin
if (SDRAM_RST = '1') then
sdResetTimer <= (others => '0'); -- 0 upto 127
sdInResetCounter <= (others => '1'); -- 255 downto 0
sdMuxAddr <= (others => '0');
sdAutoRefresh <= '0';
sdRefreshCount <= (others => '0');
sdActiveBank <= (others => '0');
sdActiveRow <= ((others => '0'), (others => '0'), (others => '0'), (others => '0'));
isReady <= '0';
sdCmd <= CMD_AUTO_REFRESH;
sdCKE <= '1';
sdDQM <= (others => '1');
sdCycle <= 0;
sdDone <= '0';
cacheWriteAddr <= (others => '0');
elsif rising_edge(SDRAM_CLK) then
-- If no specific command given the default is NOP.
sdCmd <= CMD_NOP;
-- Initialisation on power up or reset. The SDRAM must be given at least 200uS to initialise and a fixed setup pattern applied.
if (isReady = '0') then
sdResetTimer <= sdResetTimer + 1;
-- 1uS timer.
if (sdResetTimer = RAM_CLK/1000000) then
sdResetTimer <= (others => '0');
sdInResetCounter <= sdInResetCounter - 1;
end if;
-- Every 1uS check for the next init action.
if (sdResetTimer = 0) then
-- 200uS wait, no action as the SDRAM starts up.
-- ie. 255 downto 55
-- Precharge all banks
if(sdInResetCounter = 55) then
sdCmd <= CMD_PRECHARGE;
sdMuxAddr(10) <= '1';
end if;
-- 8 auto refresh commands as specified in datasheet. The RFS time is 60nS, so using a 1uS timer, issue one after
-- the other.
if(sdInResetCounter >= 40 and sdInResetCounter <= 48) then
sdCmd <= CMD_AUTO_REFRESH;
end if;
-- Load the Mode register with our parameters.
if(sdInResetCounter = 39) then
sdCmd <= CMD_LOAD_MODE;
sdMuxAddr <= MODE;
end if;
-- 8 auto refresh commands as specified in datasheet. The RFS time is 60nS, so using a 1uS timer, issue one after
-- the other.
if(sdInResetCounter >= 30 and sdInResetCounter <= 38) then
sdCmd <= CMD_AUTO_REFRESH;
end if;
-- SDRAM ready.
if(sdInResetCounter = 20) then
isReady <= '1';
end if;
end if;
else
sdRefreshCount <= sdRefreshCount + 1;
-- Auto refresh. On timeout it kicks in so that 8192 auto refreshes are
-- issued in a 64ms period. Other bus operations are stalled during this period.
if (sdRefreshCount > REFRESH_PERIOD and sdCycle = 0) then
sdAutoRefresh <= '1';
sdRefreshCount <= (others => '0');
sdCmd <= CMD_PRECHARGE;
sdMuxAddr(10) <= '1';
sdActiveBank <= (others => '0');
sdActiveRow <= ((others => '0'), (others => '0'), (others => '0'), (others => '0'));
-- In auto refresh period.
elsif (sdAutoRefresh = '1') then
-- while the cycle is active count.
sdCycle <= sdCycle + 1;
case (sdCycle) is
when CYCLE_RFSH_START =>
sdCmd <= CMD_AUTO_REFRESH;
when CYCLE_RFSH_END =>
-- reset the count.
sdAutoRefresh <= '0';
sdCycle <= 0;
when others =>
end case;
elsif ((sbBusy = '1' and sdCycle = 0) or sdCycle /= 0) then -- or (sdCycle = 0 and CS = '1')) then
-- while the cycle is active count.
sdCycle <= sdCycle + 1;
case (sdCycle) is
when CYCLE_PRECHARGE =>
-- If the bank is not open then no need to precharge, move onto RAS.
if (sdActiveBank(sdBank) = '0') then
sdCycle <= CYCLE_RAS_START;
-- If the requested row is already active, go to CAS for immediate access to this row.
elsif (sdActiveRow(sdBank) = sdRow) then
sdCycle <= CYCLE_CAS0;
-- Otherwise we close out the open bank by issuing a PRECHARGE.
else
sdCmd <= CMD_PRECHARGE;
sdMuxAddr(10) <= '0';
SDRAM_BA <= std_logic_vector(to_unsigned(sdBank, SDRAM_BA'length));
sdActiveBank(sdBank) <= '0'; -- Store flag to indicate which bank is being made active.
end if;
-- Open the requested row.
when CYCLE_RAS_START =>
sdCmd <= CMD_ACTIVE;
sdMuxAddr <= sdRow; -- Addr presented to SDRAM as row address.
SDRAM_BA <= std_logic_vector(to_unsigned(sdBank, SDRAM_BA'length)); -- Addr presented to SDRAM as bank select.
sdActiveRow(sdBank) <= sdRow; -- Store number of row being made active
sdActiveBank(sdBank) <= '1'; -- Store flag to indicate which bank is being made active.
when CYCLE_RAS_NEXT =>
sdDQM <= "11"; -- Set DQ to tri--state.
-- this is the first CAS cycle
when CYCLE_CAS0 =>
-- Process on a 32bit boundary, as this is a 16bit chip we need 2 accesses for a 32bit alignment.
sdMuxAddr <= std_logic_vector(to_unsigned(to_integer(unsigned(sdCol(SDRAM_COLUMN_BITS-1 downto 1) & '0')), SDRAM_ROW_BITS)); -- CAS address = Address accessing first 16bit location within the 32bit external alignment with no auto precharge
SDRAM_BA <= std_logic_vector(to_unsigned(sdBank, SDRAM_BA'length)); -- Ensure bank is the correct one opened.
-- If writing, setup for a write with preset mask.
if (sdIsWriting = '1') then
sdCmd <= CMD_WRITE;
sdDQM <= not cpuDQM(3 downto 2);
sdDataOut <= cpuDataIn(31 downto 16); -- Assign corresponding data to the SDRAM databus.
else
-- Setup for a read.
sdCmd <= CMD_READ;
sdDQM <= "00"; -- For reads dont mask the data output.
end if;
when CYCLE_CAS1 =>
sdMuxAddr <= std_logic_vector(to_unsigned(to_integer(unsigned(sdCol(SDRAM_COLUMN_BITS-1 downto 1) & '1')), SDRAM_ROW_BITS)); -- CAS address = Next address accessing second 16bit location within the 32bit external alignment with no auto precharge
SDRAM_BA <= std_logic_vector(to_unsigned(sdBank, SDRAM_BA'length)); -- Ensure bank is the correct one opened.
-- If writing, setup for a write with preset mask.
if (sdIsWriting = '1') then
sdCmd <= CMD_WRITE;
sdDQM <= not cpuDQM(1 downto 0);
sdDone <= not sdDone;
sdDataOut <= cpuDataIn(15 downto 0);
sdCycle <= CYCLE_END;
else
-- Setup for a read, change to write if flag set.
sdCmd <= CMD_READ;
sdDQM <= "00"; -- For reads dont mask the data output.
end if;
-- Data is available CAS Latency clocks after the read request.
when CYCLE_READ0 =>
-- If writing, then we are complete, exit else read the first word.
if (sdIsWriting = '1') then
sdCycle <= CYCLE_END;
else
dout(31 downto 16) <= sdDataIn;
end if;
when CYCLE_READ1 =>
-- If writing, then we are complete, exit else read the first word.
if (sdIsWriting = '1') then
sdCycle <= CYCLE_END;
else
dout(15 downto 0) <= sdDataIn;
sdDone <= not sdDone;
end if;
when CYCLE_END =>
when others =>
end case;
else
sdCycle <= 0;
end if;
end if;
end if;
end process;
-- CPU/BUS side logic. When the CPU initiates a transaction, capture the signals and the captured values are used within the SDRAM domain. This is to prevent
-- any changes CPU side or differing signal lengths due to CPU architecture or clock being propogated into the SDRAM domain. The CPU only needs to know
-- when the transation is complete and data read.
--
process(RESET, CLK, CS, WRITE_BYTE, WRITE_HWORD, ADDR, WREN, RDEN, isReady)
begin
if (RESET = '1') then
sdDoneLast <= '0';
sbBusy <= '0';
sdBank <= 0;
sdRow <= (others => '0');
sdCol <= (others => '0');
cpuDQM <= (others => '1');
sdIsWriting <= '0';
-- If the SDRAM isnt ready, we can only wait.
elsif isReady = '0' then
elsif rising_edge(CLK) then
-- Detect a Chip Select state change signalling access.
if CS = '1' and (WREN='1' or RDEN='1') then
sbBusy <= '1';
sdIsWriting <= WREN;
sdBank <= to_integer(unsigned(ADDR(SDRAM_ADDR_BITS-1 downto SDRAM_ARRAY_BITS)));
sdRow <= std_logic_vector(to_unsigned(to_integer(unsigned(ADDR(SDRAM_ARRAY_BITS + 1 - SDRAM_BANK_BITS downto SDRAM_COLUMN_BITS))), SDRAM_ROW_BITS));
sdCol <= ADDR(SDRAM_COLUMN_BITS-1 downto 0);
-- Preset the write selects according to the CPU signals. Let Quartus optimize as easier to read seeing all mask values.
if(WRITE_BYTE = '1') then
case ADDR(1 downto 0) is
when "00" => cpuDQM <= "1000";
cpuDataIn <= DATA_IN(7 downto 0) & X"000000";
when "01" => cpuDQM <= "0100";
cpuDataIn <= X"00" & DATA_IN(7 downto 0) & X"0000";
when "10" => cpuDQM <= "0010";
cpuDataIn <= X"0000" & DATA_IN(7 downto 0) & X"00";
when "11" => cpuDQM <= "0001";
cpuDataIn <= X"000000" & DATA_IN(7 downto 0);
when others =>
end case;
elsif(WRITE_HWORD = '1') then
case ADDR(1) is
when '0' => cpuDQM <= "1100";
cpuDataIn <= DATA_IN(15 downto 0) & X"0000";
when '1' => cpuDQM <= "0011";
cpuDataIn <= X"0000" & DATA_IN(15 downto 0);
end case;
else
-- Reads are always 32bit wide and if no part word signal is asserted, writes are 32bit.
cpuDataIn <= DATA_IN(31 downto 0);
cpuDQM <= "1111";
end if;
end if;
-- Note SDRAM activity via a previous/last signal.
sdDoneLast <= sdDone;
-- If there has been a change in the SDRAM activity reset the signals as initiated transaction is complete.
if (sdDone xor sdDoneLast) = '1' then
sbBusy <= '0';
sdIsWriting <= '0';
end if;
end if;
end process;
DATA_OUT <= dout;
-- drive control signals according to current command
SDRAM_CS_n <= sdCmd(3);
SDRAM_RAS_n <= sdCmd(2);
SDRAM_CAS_n <= sdCmd(1);
SDRAM_WE_n <= sdCmd(0);
SDRAM_CKE <= sdCKE;
SDRAM_DQM <= sdDQM;
SDRAM_ADDR <= sdMuxAddr;
-- System bus control signals.
BUSY <= sbBusy;
SDRAM_READY <= isReady;
end Structure;

View File

@@ -1,728 +0,0 @@
------------------------------------------------------
-- FSM for a SDRAM controller
--
-- Version 0.1 - Ready to simulate
--
-- Authors: Mike Field (hamster@snap.net.nz)
-- Alvaro Lopes (alvieboy@alvie.com)
--
-- Feel free to use it however you would like, but
-- just drop us an email to say thanks.
-------------------------------------------------------
library ieee;
library pkgs;
library work;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.zpu_soc_pkg.all;
use work.zpu_pkg.all;
entity sdram_controller is
generic (
HIGH_BIT: integer := 24;
MHZ: integer := 96;
REFRESH_CYCLES: integer := 4096;
ADDRESS_BITS: integer := 12
);
PORT (
clock_100: in std_logic;
clock_100_delayed_3ns: in std_logic;
rst: in std_logic;
-- Signals to/from the SDRAM chip
DRAM_ADDR : OUT STD_LOGIC_VECTOR (ADDRESS_BITS-1 downto 0);
DRAM_BA : OUT STD_LOGIC_VECTOR (1 downto 0);
DRAM_CAS_N : OUT STD_LOGIC;
DRAM_CKE : OUT STD_LOGIC;
DRAM_CLK : OUT STD_LOGIC;
DRAM_CS_N : OUT STD_LOGIC;
DRAM_DQ : INOUT STD_LOGIC_VECTOR(15 downto 0);
DRAM_DQM : OUT STD_LOGIC_VECTOR(1 downto 0);
DRAM_RAS_N : OUT STD_LOGIC;
DRAM_WE_N : OUT STD_LOGIC;
pending: out std_logic;
--- Inputs from rest of the system
address : IN STD_LOGIC_VECTOR (HIGH_BIT downto 2);
req_read : IN STD_LOGIC;
req_write : IN STD_LOGIC;
data_out : OUT STD_LOGIC_VECTOR (31 downto 0);
data_out_valid : OUT STD_LOGIC;
data_in : IN STD_LOGIC_VECTOR (31 downto 0);
data_mask : IN STD_LOGIC_VECTOR (3 downto 0)
);
end entity;
architecture rtl of sdram_controller is
type reg is record
address : std_logic_vector(ADDRESS_BITS-1 downto 0);
bank : std_logic_vector( 1 downto 0);
init_counter : unsigned(14 downto 0);
rf_counter : integer;
rf_pending : std_logic;
rd_pending : std_logic;
wr_pending : std_logic;
act_row : std_logic_vector(ADDRESS_BITS-1 downto 0);
act_ba : std_logic_vector(1 downto 0);
data_out_low : std_logic_vector(15 downto 0);
req_addr_q : std_logic_vector(HIGH_BIT downto 2);
req_data_write: std_logic_vector(31 downto 0);
req_mask : std_logic_vector(3 downto 0);
data_out_valid: std_logic;
dq_masks : std_logic_vector(1 downto 0);
tristate : std_logic;
end record;
signal r : reg;
signal n : reg;
signal rstate : std_logic_vector(8 downto 0);
signal nstate : std_logic_vector(8 downto 0);
signal rdata_write : std_logic_vector(15 downto 0);
signal ndata_write : std_logic_vector(15 downto 0);
-- Vectors for each SDRAM 'command'
--- CS_N, RAS_N, CAS_N, WE_N
constant cmd_nop : std_logic_vector(3 downto 0) := "0111";
constant cmd_read : std_logic_vector(3 downto 0) := "0101"; -- Must be sure A10 is low.
constant cmd_write : std_logic_vector(3 downto 0) := "0100";
constant cmd_act : std_logic_vector(3 downto 0) := "0011";
constant cmd_pre : std_logic_vector(3 downto 0) := "0010"; -- Must set A10 to '1'.
constant cmd_ref : std_logic_vector(3 downto 0) := "0001";
constant cmd_mrs : std_logic_vector(3 downto 0) := "0000"; -- Mode register set
-- State assignments
constant s_init_nop_id: std_logic_vector(4 downto 0) := "00000";
constant s_init_nop : std_logic_vector(8 downto 0) := s_init_nop_id & cmd_nop;
constant s_init_pre : std_logic_vector(8 downto 0) := s_init_nop_id & cmd_pre;
constant s_init_ref : std_logic_vector(8 downto 0) := s_init_nop_id & cmd_ref;
constant s_init_mrs : std_logic_vector(8 downto 0) := s_init_nop_id & cmd_mrs;
constant s_idle_id: std_logic_vector(4 downto 0) := "00001";
constant s_idle : std_logic_vector(8 downto 0) := s_idle_id & cmd_nop;
constant s_rf0_id: std_logic_vector(4 downto 0) := "00010";
constant s_rf0 : std_logic_vector(8 downto 0) := s_rf0_id & cmd_ref;
constant s_rf1_id: std_logic_vector(4 downto 0) := "00011";
constant s_rf1 : std_logic_vector(8 downto 0) := "00011" & cmd_nop;
constant s_rf2_id: std_logic_vector(4 downto 0) := "00100";
constant s_rf2 : std_logic_vector(8 downto 0) := "00100" & cmd_nop;
constant s_rf3_id: std_logic_vector(4 downto 0) := "00101";
constant s_rf3 : std_logic_vector(8 downto 0) := "00101" & cmd_nop;
constant s_rf4_id: std_logic_vector(4 downto 0) := "00110";
constant s_rf4 : std_logic_vector(8 downto 0) := "00110" & cmd_nop;
constant s_rf5_id: std_logic_vector(4 downto 0) := "00111";
constant s_rf5 : std_logic_vector(8 downto 0) := "00111" & cmd_nop;
constant s_ra0_id: std_logic_vector(4 downto 0) := "01000";
constant s_ra0 : std_logic_vector(8 downto 0) := "01000" & cmd_act;
constant s_ra1_id: std_logic_vector(4 downto 0) := "01001";
constant s_ra1 : std_logic_vector(8 downto 0) := "01001" & cmd_nop;
constant s_ra2_id: std_logic_vector(4 downto 0) := "01010";
constant s_ra2 : std_logic_vector(8 downto 0) := "01010" & cmd_nop;
constant s_dr0_id: std_logic_vector(4 downto 0) := "01011";
constant s_dr0 : std_logic_vector(8 downto 0) := "01011" & cmd_pre;
constant s_dr1_id: std_logic_vector(4 downto 0) := "01100";
constant s_dr1 : std_logic_vector(8 downto 0) := "01100" & cmd_nop;
constant s_wr0_id: std_logic_vector(4 downto 0) := "01101";
constant s_wr0 : std_logic_vector(8 downto 0) := "01101" & cmd_write;
constant s_wr1_id: std_logic_vector(4 downto 0) := "01110";
constant s_wr1 : std_logic_vector(8 downto 0) := "01110" & cmd_nop;
constant s_wr2_id: std_logic_vector(4 downto 0) := "01111";
constant s_wr2 : std_logic_vector(8 downto 0) := "01111" & cmd_nop;
constant s_wr3_id: std_logic_vector(4 downto 0) := "10000";
constant s_wr3 : std_logic_vector(8 downto 0) := "10000" & cmd_write;
constant s_rd0_id: std_logic_vector(4 downto 0) := "10001";
constant s_rd0 : std_logic_vector(8 downto 0) := "10001" & cmd_read;
constant s_rd1_id: std_logic_vector(4 downto 0) := "10010";
constant s_rd1 : std_logic_vector(8 downto 0) := "10010" & cmd_read;
constant s_rd2_id: std_logic_vector(4 downto 0) := "10011";
constant s_rd2 : std_logic_vector(8 downto 0) := "10011" & cmd_nop;
constant s_rd3_id: std_logic_vector(4 downto 0) := "10100";
constant s_rd3 : std_logic_vector(8 downto 0) := "10100" & cmd_read;
constant s_rd4_id: std_logic_vector(4 downto 0) := "10101";
constant s_rd4 : std_logic_vector(8 downto 0) := "10101" & cmd_read;
constant s_rd5_id: std_logic_vector(4 downto 0) := "10110";
constant s_rd5 : std_logic_vector(8 downto 0) := "10110" & cmd_read;
constant s_rd6_id: std_logic_vector(4 downto 0) := "10111";
constant s_rd6 : std_logic_vector(8 downto 0) := "10111" & cmd_nop;
constant s_rd7_id: std_logic_vector(4 downto 0) := "11000";
constant s_rd7 : std_logic_vector(8 downto 0) := "11000" & cmd_nop;
constant s_rd8_id: std_logic_vector(4 downto 0) := "11001";
constant s_rd8 : std_logic_vector(8 downto 0) := "11001" & cmd_nop;
constant s_rd9_id: std_logic_vector(4 downto 0) := "11011";
constant s_rd9 : std_logic_vector(8 downto 0) := "11011" & cmd_nop;
constant s_drdr0_id: std_logic_vector(4 downto 0) := "11101";
constant s_drdr0 : std_logic_vector(8 downto 0) := "11101" & cmd_pre;
constant s_drdr1_id: std_logic_vector(4 downto 0) := "11110";
constant s_drdr1 : std_logic_vector(8 downto 0) := "11110" & cmd_nop;
constant s_drdr2_id: std_logic_vector(4 downto 0) := "11111";
constant s_drdr2 : std_logic_vector(8 downto 0) := "11111" & cmd_nop;
signal addr_row : std_logic_vector(ADDRESS_BITS-1 downto 0);
signal addr_bank: std_logic_vector(1 downto 0);
constant COLUMN_HIGH: integer := HIGH_BIT - addr_row'LENGTH - addr_bank'LENGTH - 1; -- last 1 means 16 bit width
signal addr_col : std_logic_vector(7 downto 0);
signal captured : std_logic_vector(15 downto 0);
signal busy: std_logic;
constant tOPD: time := 1.4 ns;
constant tHZ: time := 8 ns;
signal dram_dq_dly : std_logic_vector(15 downto 0);
-- Debug only
signal debug_cmd: std_logic_vector(3 downto 0);
constant RELOAD: integer := (((64000000/REFRESH_CYCLES)*MHZ)/1000) - 10;
attribute IOB: string;
signal i_DRAM_CS_N: std_logic;
attribute IOB of i_DRAM_CS_N: signal is "true";
signal i_DRAM_RAS_N: std_logic;
attribute IOB of i_DRAM_RAS_N: signal is "true";
signal i_DRAM_CAS_N: std_logic;
attribute IOB of i_DRAM_CAS_N: signal is "true";
signal i_DRAM_WE_N: std_logic;
attribute IOB of i_DRAM_WE_N: signal is "true";
signal i_DRAM_ADDR: std_logic_vector(ADDRESS_BITS-1 downto 0);
attribute IOB of i_DRAM_ADDR: signal is "true";
signal i_DRAM_BA: std_logic_vector(1 downto 0);
attribute IOB of i_DRAM_BA: signal is "true";
signal i_DRAM_DQM: std_logic_vector(1 downto 0);
attribute IOB of i_DRAM_DQM: signal is "true";
attribute IOB of rdata_write: signal is "true";
attribute IOB of captured: signal is "true";
signal i_DRAM_CLK: std_logic;
attribute fsm_encoding: string;
attribute fsm_encoding of nstate: signal is "user";
attribute fsm_encoding of rstate: signal is "user";
begin
debug_cmd <= rstate(3 downto 0);
-- Addressing is in 32 bit words - twice that of the DRAM width,
-- so each burst of four access two system words.
--addr_row <= address(23 downto 11);
--addr_bank <= address(10 downto 9);
process(r.req_addr_q)
begin
addr_bank <= r.req_addr_q(HIGH_BIT downto (HIGH_BIT-addr_bank'LENGTH)+1);
-- (24-2) downto (24-2 - 2 - 13 - 1)
-- 22 downto 6
addr_row <= --r.req_addr_q(HIGH_BIT-addr_bank'LENGTH downto COLUMN_HIGH+2);
r.req_addr_q(ADDRESS_BITS-1+9 downto 9);
addr_col <= (others => '0');
addr_col <= --r.req_addr_q(COLUMN_HIGH+1 downto 2) & "0";
r.req_addr_q(8 downto 2) & "0";
end process;
clock: entity work.oddrff
port map (
D0 => '0',
D1 => '1',
O => i_DRAM_CLK,
CLK => clock_100
);
DRAM_CKE <= '1';
DRAM_CLK <= clock_100; -- transport i_DRAM_CLK after tOPD;
i_DRAM_CS_N <= transport rstate(3) after tOPD;
DRAM_CS_N <= i_DRAM_CS_N;
i_DRAM_RAS_N <= transport rstate(2) after tOPD;
DRAM_RAS_N <= i_DRAM_RAS_N;
i_DRAM_CAS_N <= transport rstate(1) after tOPD;
DRAM_CAS_N <= i_DRAM_CAS_N;
i_DRAM_WE_N <= transport rstate(0) after tOPD;
DRAM_WE_N <= i_DRAM_WE_N;
i_DRAM_ADDR <= transport r.address after tOPD;
DRAM_ADDR <= i_DRAM_ADDR;
i_DRAM_BA <= transport r.bank after tOPD;
DRAM_BA <= i_DRAM_BA;
i_DRAM_DQM <= transport r.dq_masks after tOPD;
DRAM_DQM <= i_DRAM_DQM;
DATA_OUT <= r.data_out_low & captured;--r.data_out_low & captured;
data_out_valid <= r.data_out_valid;
DRAM_DQ <= (others => 'Z') after tHZ when r.tristate='1' else rdata_write;
pending <= '1' when r.wr_pending='1' or r.rd_pending='1' else '0';
process (r, rstate, address, req_read, rdata_write, req_write, addr_row, addr_bank, addr_col, data_in, captured)
begin
-- copy the existing values
n <= r;
nstate <= rstate;
ndata_write <= rdata_write;
if req_read = '1' then
n.rd_pending <= '1';
if r.rd_pending='0' then
n.req_addr_q <= address;
end if;
end if;
if req_write = '1' then
n.wr_pending <= '1';
if r.wr_pending='0' then
n.req_addr_q <= address;
-- Queue data here
n.req_data_write <= data_in;
n.req_mask <= data_mask;
end if;
end if;
n.dq_masks <= "11";
-- first off, do we need to perform a refresh cycle ASAP?
if r.rf_counter = RELOAD then -- 781 = 64,000,000ns / 8192 / 10ns
n.rf_counter <= 0;
n.rf_pending <= '1';
else
-- only start looking for refreshes outside of the initialisation state.
if not(rstate(8 downto 4) = s_init_nop(8 downto 4)) then
n.rf_counter <= r.rf_counter + 1;
end if;
end if;
-- Set the data bus into HIZ, high and low bytes masked
--DRAM_DQ <= (others => 'Z');
n.tristate <= '0';
n.init_counter <= r.init_counter - 1;
--ndata_write <= (others => DontCareValue);
n.data_out_valid <= '0'; -- alvie- here, no ?
-- Process the FSM
case rstate(8 downto 4) is
when s_init_nop_id => --s_init_nop(8 downto 4) =>
nstate <= s_init_nop;
n.address <= (others => '0');
n.bank <= (others => '0');
n.act_ba <= (others => '0');
n.rf_counter <= 0;
-- n.data_out_valid <= '1'; -- alvie- not here
-- T-130, precharge all banks.
if r.init_counter = "000000010000010" then
nstate <= s_init_pre;
n.address(10) <= '1';
end if;
-- T-127, T-111, T-95, T-79, T-63, T-47, T-31, T-15, the 8 refreshes
if r.init_counter(14 downto 7) = 0 and r.init_counter(3 downto 0) = 15 then
nstate <= s_init_ref;
end if;
-- T-3, the load mode register
if r.init_counter = 3 then
nstate <= s_init_mrs;
-- Mode register is as follows:
-- resvd wr_b OpMd CAS=3 Seq bust=1
n.address <= "00" & "0" & "00" & "011" & "0" & "000";
-- resvd
n.bank <= "00";
end if;
-- T-1 The switch to the FSM (first command will be a NOP
if r.init_counter = 1 then
nstate <= s_idle;
end if;
------------------------------
-- The Idle section
------------------------------
when s_idle_id =>
nstate <= s_idle;
-- do we have to activate a row?
if r.rd_pending = '1' or r.wr_pending = '1' then
nstate <= s_ra0;
n.address <= addr_row;
n.act_row <= addr_row;
n.bank <= addr_bank;
end if;
-- refreshes take priority over everything
if r.rf_pending = '1' then
nstate <= s_rf0;
n.rf_pending <= '0';
end if;
------------------------------
-- Row activation
-- s_ra2 is also the "idle with active row" state and provides
-- a resting point between operations on the same row
------------------------------
when s_ra0_id =>
nstate <= s_ra1;
when s_ra1_id =>
nstate <= s_ra2;
when s_ra2_id=>
-- we can stay in this state until we have something to do
nstate <= s_ra2;
n.tristate<='0';
if r.rf_pending = '1' then
nstate <= s_dr0;
n.address(10) <= '1';
else
-- If there is a read pending, deactivate the row
if r.rd_pending = '1' or r.wr_pending = '1' then
nstate <= s_dr0;
n.address(10) <= '1';
end if;
-- unless we have a read to perform on the same row? do that instead
if r.rd_pending = '1' and r.act_row = addr_row and addr_bank=r.bank then
nstate <= s_rd0;
n.address <= (others => '0');
n.address(addr_col'HIGH downto 0) <= addr_col;
n.bank <= addr_bank;
n.act_ba <= addr_bank;
n.dq_masks <= "00";
n.rd_pending <= '0';
--n.tristate<='1';
end if;
-- unless we have a write on the same row? writes take priroty over reads
if r.wr_pending = '1' and r.act_row = addr_row and addr_bank=r.bank then
nstate <= s_wr0;
n.address <= (others => '0');
n.address(addr_col'HIGH downto 0) <= addr_col;
ndata_write <= r.req_data_write(31 downto 16);
n.bank <= addr_bank;
n.act_ba <= addr_bank;
n.dq_masks<= not r.req_mask(3 downto 2);
n.wr_pending <= '0';
--n.tristate <= '0';
end if;
end if;
-- nstate <= s_dr0;
-- n.address(10) <= '1';
-- n.rd_pending <= r.rd_pending;
-- n.wr_pending <= r.wr_pending;
--n.tristate <= '0';
--end if;
------------------------------------------------------
-- Deactivate the current row and return to idle state
------------------------------------------------------
when s_dr0_id =>
nstate <= s_dr1;
when s_dr1_id =>
nstate <= s_idle;
------------------------------
-- The Refresh section
------------------------------
when s_rf0_id =>
nstate <= s_rf1;
when s_rf1_id =>
nstate <= s_rf2;
when s_rf2_id =>
nstate <= s_rf3;
when s_rf3_id =>
nstate <= s_rf4;
when s_rf4_id =>
nstate <= s_rf5;
when s_rf5_id =>
nstate <= s_idle;
------------------------------
-- The Write section
------------------------------
when s_wr0_id =>
nstate <= s_wr3;
n.bank <= addr_bank;
n.address(0) <= '1';
ndata_write <= r.req_data_write(15 downto 0);--data_in(31 downto 16);
--DRAM_DQ <= rdata_write;
n.dq_masks<= not r.req_mask(1 downto 0);
n.tristate <= '0';
when s_wr1_id => null;
when s_wr2_id =>
nstate <= s_dr0;
n.address(10) <= '1';
when s_wr3_id =>
-- Default to the idle+row active state
nstate <= s_ra2;
--DRAM_DQ <= rdata_write;
n.data_out_valid<='1'; -- alvie- ack write
n.tristate <= '0';
n.dq_masks<= "11";
-- If there is a read or write then deactivate the row
--if r.rd_pending = '1' or r.wr_pending = '1' then
-- nstate <= s_dr0;
-- n.address(10) <= '1';
--end if;
-- But if there is a read pending in the same row, do that
--if r.rd_pending = '1' and r.act_row = addr_row and r.act_ba = addr_bank then
-- nstate <= s_rd0;
-- n.address <= (others => '0');
-- n.address(addr_col'HIGH downto 0) <= addr_col;
-- n.bank <= addr_bank;
-- --n.act_ba <= addr_bank;
-- n.dq_masks <= "00";
-- n.rd_pending <= '0';
--end if;
-- unless there is a write pending in the same row, do that
--if r.wr_pending = '1' and r.act_row = addr_row and r.act_ba = addr_bank then
-- nstate <= s_wr0;
-- n.address <= (others => '0');
-- n.address(addr_col'HIGH downto 0) <= addr_col;
-- n.bank <= addr_bank;
--n.act_ba <= addr_bank;
-- n.dq_masks<= "00";
-- n.wr_pending <= '0';
--end if;
-- But always try and refresh if one is pending!
if r.rf_pending = '1' then
nstate <= s_wr2; --dr0;
--n.address(10) <= '1';
end if;
------------------------------
-- The Read section
------------------------------
when s_rd0_id => -- 10001
nstate <= s_rd1;
n.tristate<='1';
n.dq_masks <= "00";
n.address(0)<='1';
when s_rd1_id => -- 10010
nstate <= s_rd2;
n.dq_masks <= "00";
n.tristate<='1';
if r.rd_pending = '1' and r.act_row = addr_row and r.act_ba=addr_bank then
nstate <= s_rd3; -- Another request came, and we can pipeline -
n.address <= (others => '0');
n.address(addr_col'HIGH downto 0) <= addr_col;
n.bank <= addr_bank;
n.act_ba <= addr_bank;
n.dq_masks<= "00";
n.rd_pending <= '0';
end if;
when s_rd2_id => -- 10011
nstate <= s_rd7;
n.dq_masks <= "00";
n.tristate<='1';
when s_rd3_id => -- 10100
nstate <= s_rd4;
n.dq_masks <= "00";
n.address(0) <= '1';
n.tristate<='1';
-- Data is still not ready...
when s_rd4_id => -- 10101
nstate <= s_rd5;
n.dq_masks <= "00";
--n.address(0)<='1';
n.tristate<='1';
if r.rd_pending = '1' and r.act_row = addr_row and r.act_ba=addr_bank then
nstate <= s_rd5; -- Another request came, and we can pipeline -
n.address <= (others => '0');
n.address(addr_col'HIGH downto 0) <= addr_col;
n.bank <= addr_bank;
n.act_ba <= addr_bank;
n.dq_masks<= "00";
n.rd_pending <= '0';
else
nstate <= s_rd6; -- NOTE: not correct
end if;
--if r.rf_pending = '1' then
-- nstate <= s_drdr0;
-- n.address(10) <= '1';
-- n.rd_pending <= r.rd_pending; -- Keep request
--end if;
n.data_out_low <= captured;
n.data_out_valid <= '1';
when s_rd5_id =>
-- If a refresh is pending then always deactivate the row
--if r.rf_pending = '1' then
-- nstate <= s_drdr0;
-- n.address(10) <= '1';
--end if;
n.address(0) <= '1';
nstate <= s_rd4; -- Another request came, and we can pipeline -
n.dq_masks <= "00";
n.tristate<='1';
when s_rd6_id =>
nstate <= s_rd7;
n.dq_masks<= "00";
n.tristate<='1';
when s_rd7_id =>
nstate <= s_ra2;
n.data_out_low <= captured;
n.data_out_valid <= '1';
n.tristate<='1';
when s_rd8_id => null;
when s_rd9_id => null;
-- The Deactivate row during read section
------------------------------
when s_drdr0_id =>
nstate <= s_drdr1;
when s_drdr1_id =>
nstate <= s_drdr2;
n.data_out_low <= captured;
n.data_out_valid <= '1';
when s_drdr2_id =>
nstate <= s_idle;
if r.rf_pending = '1' then
nstate <= s_rf0;
end if;
if r.rd_pending = '1' or r.wr_pending = '1' then
nstate <= s_ra0;
n.address <= addr_row;
n.act_row <= addr_row;
n.bank <= addr_bank;
end if;
when others =>
nstate <= s_init_nop;
end case;
end process;
--- The clock driven logic
process (clock_100, n)
begin
if clock_100'event and clock_100 = '1' then
if rst='1' then
rstate <= (others => '0');
r.address <= (others => '0');
r.bank <= (others => '0');
r.init_counter <= "100000000000000";
-- synopsys translate_off
r.init_counter <= "000000100000000";
-- synopsys translate_on
r.rf_counter <= 0;
r.rf_pending <= '0';
r.rd_pending <= '0';
r.wr_pending <= '0';
r.act_row <= (others => '0');
r.data_out_low <= (others => '0');
r.data_out_valid <= '0';
r.dq_masks <= "11";
r.tristate<='1';
else
r <= n;
rstate <= nstate;
rdata_write <= ndata_write;
end if;
end if;
end process;
dram_dq_dly <= transport dram_dq after 3.6 ns;--1.9 ns;
-- process (clock_100_delayed_3ns, dram_dq_dly)
-- begin
-- if clock_100_delayed_3ns'event and clock_100_delayed_3ns = '1' then
-- captured <= dram_dq_dly;
-- end if;
-- end process;
process (clock_100_delayed_3ns)
begin
if falling_edge(clock_100_delayed_3ns) then
captured <= dram_dq_dly;
end if;
end process;
end rtl;