-- from comp.lang.vhdl: -- -- Here's a DRAM model that I developed for use here. -- --+----------------------------+----------------------------+ --| Shannon Hill | em: hill@synnet.com | --| 3COM Switching Division | fx: 508-670-9014 | --| 85 Rangeway Road | ph: 508-262-1420 | --| North Billerica, MA 01862 | (email or fax preferred) | --| gender=M +----------------------------+ --+----------------------------+ -- library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; use ieee.std_logic_arith.all; use std.textio.all; use ieee.std_logic_textio.all; entity dram is generic( ABWIDTH : INTEGER := 10 ; -- address bus width DBWIDTH : INTEGER := 32 ; -- data bus width ODDELAY : TIME := 20 ns ; -- output data delay TSDELAY : TIME := 10 ns ; -- output turnoff delay UNDEF : STD_LOGIC := 'X' ); port( dram_addr : in std_logic_vector( ABWIDTH-1 downto 0 ) ; dram_data : inout std_logic_vector( DBWIDTH-1 downto 0 ) ; dras_l : in std_logic ; dcas_l : in std_logic ; dwe_l : in std_logic ; doe_l : in std_logic ); end dram; architecture simulate of dram is constant ERROR_WAIT : TIME := 80 ns ; -- assertion error holdoff delay -- internal memory address width constant MAWIDTH : INTEGER := ABWIDTH * 2 ; -- virtual word number width (address lsbs) constant WNWIDTH : INTEGER := 10 ; -- 1024 entries per page -- virtual page number width (address msbs) constant PNWIDTH : INTEGER := (MAWIDTH - WNWIDTH) ; -- memory address subtype VMEM_ADDR is std_logic_vector( (MAWIDTH - 1) downto 0 ) ; subtype VROW_ADDR is std_logic_vector( (ABWIDTH - 1) downto 0 ) ; subtype VCOL_ADDR is std_logic_vector( (ABWIDTH - 1) downto 0 ) ; -- memory data subtype VMEM_DATA is std_logic_vector( (DBWIDTH - 1) downto 0 ) ; constant UNDEFINED : VMEM_DATA := (others=>UNDEF); -- one page of memory data; addressed by virtual word number type VMEM_PAGE_T is ARRAY( 0 to (2**WNWIDTH)-1 ) of VMEM_DATA ; type VMEM_PAGE_P is ACCESS VMEM_PAGE_T ; -- pointer to one memory page -- table of pointers to pages of memory data words type VMEM_PAGE_TAB_T is ARRAY( 0 to (2**PNWIDTH)-1 ) of VMEM_PAGE_P ; type VMEM_PAGE_TAB_P is ACCESS VMEM_PAGE_TAB_T ; -- pointer to vpn->ppn table signal mem_adr : VMEM_ADDR ; begin address: process( dras_l, dcas_l, dram_addr ) begin if( (dras_l = '0') and dras_l'EVENT ) then -- capture row address mem_adr( VMEM_ADDR'HIGH downto VROW_ADDR'LENGTH ) <= dram_addr ; -- init row adr mem_adr( VCOL_ADDR'HIGH downto 0 ) <= dram_addr ; -- init col adr assert not is_x(dram_addr) report "?dram_addr unknown at ras assertion." severity ERROR; elsif( (dras_l = '0') and (dcas_l = '1') ) then -- pass column address mem_adr( VCOL_ADDR'HIGH downto 0 ) <= dram_addr ; elsif( (dras_l = '0') and (dcas_l = '0') and dcas_l'EVENT ) then -- latch column address assert not is_x(mem_adr) report "?row/col address unknown at cas assertion." severity ERROR; end if; end process address; memory: process( mem_adr,dras_l,dcas_l,dwe_l,doe_l,dram_data ) variable adr_is_x : BOOLEAN ; variable rd_is_x : BOOLEAN ; variable wr_is_x : BOOLEAN ; variable table : VMEM_PAGE_TAB_P := NULL ; variable page : VMEM_PAGE_P ; variable pageno : INTEGER range 0 to (2**PNWIDTH)-1 ; variable wordno : INTEGER range 0 to (2**WNWIDTH)-1 ; variable madr : VMEM_ADDR ; variable mdat : VMEM_DATA ; variable mem_wr : BOOLEAN ; variable mem_oe : BOOLEAN ; variable mem_off : BOOLEAN ; begin adr_is_x := is_x( mem_adr ); rd_is_x := is_x( doe_l ); wr_is_x := is_x( dwe_l ); madr := mem_adr ; mdat := dram_data ; mem_wr := (dras_l='0') and (dcas_l='0') and (dwe_l='0') and (doe_l='1') ; mem_oe := (dras_l='0') and (dcas_l='0') and (dwe_l='1') and (doe_l='0') ; mem_off := (dras_l='1') or ((dwe_l='1') and (doe_l='1')) ; if( adr_is_x ) then pageno := 0 ; wordno := 0 ; else pageno := CONV_INTEGER( madr( (MAWIDTH-1) downto WNWIDTH ) ); wordno := CONV_INTEGER( madr( (WNWIDTH-1) downto 0 ) ); end if; -- write if( mem_wr and not adr_is_x ) then if( table = NULL ) then -- first write ever. table := new VMEM_PAGE_TAB_T ; -- allocate page table for pn in VMEM_PAGE_TAB_T'RANGE loop table( pn ) := NULL ; -- invalidate all pointers end loop; end if; if( table( pageno ) = NULL ) then -- target page previously allocated? page := new VMEM_PAGE_T ; -- allocate a new page. table( pageno ) := page ; -- save the pointer to the new page. for word in VMEM_PAGE_T'RANGE loop page( word ) := UNDEFINED ; -- invalidate all data in the page end loop; end if; page := table( pageno ); -- get pointer to target page if( is_x( mdat ) ) then assert FALSE report "?data undefined at write assertion." severity WARNING; page( wordno ) := UNDEFINED ; else page( wordno ) := mdat ; -- write the new data end if; end if; -- read if( mem_off ) then dram_data <= (others=>'Z') after TSDELAY ; elsif( mem_oe and adr_is_x ) then -- undefined address dram_data <= UNDEFINED after ODDELAY ; elsif( mem_oe ) then -- read -- if page has been written, then get the data; else just emit X's. if(( table /= NULL ) and (table( pageno ) /= NULL )) then page := table( pageno ); -- get the pointer to the page of data. dram_data <= page( wordno ) after ODDELAY ; -- and extract the data else dram_data <= UNDEFINED after ODDELAY ; -- this page never written! assert FALSE report "?read of uninitialized page." severity ERROR; end if ; end if; end process memory; a_monitor: process( dram_addr, dras_l, dcas_l, dwe_l, doe_l ) variable last_adr_event : TIME := 0 ns ; variable last_ras_event : TIME := 0 ns ; variable last_cas_event : TIME := 0 ns ; begin if( NOW > ERROR_WAIT ) then if( (dcas_l='0') and (dcas_l'EVENT) and (dwe_l='0' )) then assert not ( (NOW - last_adr_event) < 1 ns ) report "?address setup to write assertion < 1 ns." severity ERROR; end if; if( dram_addr'EVENT ) then last_adr_event := NOW ; assert not ( ((NOW - last_ras_event) < 10 ns ) and (dras_l='0') ) report "?address changed within 10 ns of ras fall." severity ERROR; assert not ( ((NOW - last_cas_event) < 15 ns ) and (dras_l='0') and (dcas_l='0') ) report "?address changed within 15 ns or cas fall." severity ERROR; end if; assert not ((doe_l = '0') and (dwe_l = '0')) report "?doe_l and dwe_l both asserted?" severity ERROR; if( dras_l'EVENT and (dras_l = '0') and (dcas_l = '0') ) then assert (doe_l = '1') report "?doe_l on during cas-before-ras refresh." severity ERROR; assert (dwe_l = '1') report "?dwe_l on during cas-before-ras refresh." severity ERROR; end if; end if; if( dcas_l'EVENT and ( dcas_l = '0') ) then last_cas_event := NOW ; end if; if( dras_l'EVENT and ( dras_l = '0') ) then last_ras_event := NOW ; end if; end process a_monitor; d_monitor: process( dram_data, dram_data'STABLE(1 ns), dras_l, dcas_l, dwe_l ) variable last_wenb_event : TIME := 0 ns ; variable last_data_event : TIME := 0 ns ; begin if( NOW > ERROR_WAIT ) then if( (dras_l='0') and (dcas_l='0') and (dcas_l'EVENT) and (dwe_l='0') ) then last_wenb_event := NOW ; assert not ( not dram_data'STABLE(1 ns) ) report "?write data setup-time less than 1 ns." severity ERROR; end if; if( dram_data'EVENT ) then last_data_event := NOW ; if( (dras_l='0') and (dcas_l='0') and (dwe_l='0') ) then assert not ( (NOW - last_wenb_event) < 10 ns ) report "?write data hold-time less than 10 ns." severity ERROR; end if; end if; end if; end process d_monitor; end simulate;