Initial commit

This commit is contained in:
muellerlu
2026-05-29 10:19:13 +02:00
commit fedfd92270
726 changed files with 542654 additions and 0 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,23 @@
TEX=hpdmc.tex
DVI=$(TEX:.tex=.dvi)
PS=$(TEX:.tex=.ps)
PDF=$(TEX:.tex=.pdf)
AUX=$(TEX:.tex=.aux)
LOG=$(TEX:.tex=.log)
all: $(PDF)
%.dvi: %.tex
latex $<
%.ps: %.dvi
dvips $<
%.pdf: %.ps
ps2pdf $<
clean:
rm -f $(DVI) $(PS) $(PDF) $(AUX) $(LOG)
.PHONY: clean
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,232 @@
\documentclass[a4paper,11pt]{article}
\usepackage{fullpage}
\usepackage[latin1]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[normalem]{ulem}
\usepackage[english]{babel}
\usepackage{listings,babel}
\lstset{breaklines=true,basicstyle=\ttfamily}
\usepackage{graphicx}
\usepackage{moreverb}
\usepackage{url}
\usepackage{float}
\usepackage{tabularx}
\title{High Performance Dynamic Memory Controller}
\author{S\'ebastien Bourdeauducq}
\date{December 2009}
\begin{document}
\setlength{\parindent}{0pt}
\setlength{\parskip}{5pt}
\maketitle{}
\section{Specifications}
This controller is targeted at high bandwidth applications such as live video processing.
It is designed to drive 32-bit DDR SDRAM memory (which can be physically made up of two 16-bit chips).
The memory contents are accessed through a 64-bit FML (Fast Memory Link) bus with a burst length of 4. FML is a burst-oriented bus designed to ease the design of DRAM controllers. Its signaling resembles WISHBONE, but basically removes all corner cases with burst modes to save on logic resources and aspirin.
HPDMC provides high flexibility and savings on hardware by implementing a bypass mode which gives the CPU low-level access to the SDRAM command interface (address pins, bank address pins, and CKE, CS, WE, CAS and RAS). The SDRAM initialization sequence is assigned to the CPU, which should use this mode to implement it. Timing parameters are also configurable at runtime. These control interfaces are accessed on a 32-bit CSR bus, separate from the data bus. The CSR bus is a proprietary bus designed for Milkymist that helps reduce the FPGA resource usage and avoid failed timing paths on the system bus.
Due to the use of \verb!IDDR!, \verb!ODDR! and \verb!IDELAY! primitives, HPDMC currently only supports the Virtex-4 FPGAs.
\section{Architecture}
\begin{figure}[H]
\centering
\includegraphics[height=100mm]{blockdiagram.eps}
\caption{Block diagram of the HPDMC architecture.}\label{fig:blockdiagram}
\end{figure}
\subsection{Control interface}
The control interface provides a register bank on a low-speed dedicated CSR bus, which is used to control the operating mode of the core, set timings, and initialize the SDRAM.
The interface can access directly the SDRAM address and command bus in the so-called \textit{bypass mode}. In this mode, the memory controller is disabled and the CPU can control each pin of the SDRAM control bus through the bypass register.
This mode should be used at system boot-up to perform the SDRAM initialization sequence. HPDMC does not provide a hardware state machine that does such initialization.
The mapped registers are the following (addresses are in bytes to match the addresses seen by the CPU when the CSR bus is bridged to Wishbone) :
\subsubsection{System register, offset 0x00}
\begin{tabularx}{\textwidth}{|l|l|l|X|}
\hline
\bf Bits & \bf Access & \bf Default & \bf Description \\
\hline
0 & RW & 1 & Bypass mode enable. Setting this bit transfers control of the SDRAM command and address bus from HPDMC to the system CPU. This bit should be set during the SDRAM initialization sequence and cleared during normal memory access. \\
\hline
1 & RW & 1 & Reset. This bit should be cleared during normal operation and set while reconfiguring the memory subsystem. \\
\hline
2 & RW & 0 & CKE control. This bit directly drives the CKE pin of the SDRAM and should be always set except during the first stage of the initialization sequence. The core does not support SDRAM power-down modes, so clearing this bit during normal operation results in undefined behaviour. \\
\hline
31 -- 3 & --- & 0 & Reserved. \\
\hline
\end{tabularx}
\subsubsection{Bypass register, offset 0x04}
The bypass register gives the system CPU low-level access to the SDRAM. It must be used at system power-up to initialize the SDRAM, as the controller does not provide this initialization. Such software initialization of the SDRAM provides greater flexibility and saves valuable hardware resources.
Writing once to this register issues \textbf{one} transaction to the SDRAM command bus, ie. the values written to the CS, WE, RAS and CAS bits are only taken into account for one clock cycle, and then the signals go back to their default inactive state.
The values written to this register have an effect on the SDRAM only if the controller is put in bypass mode using the system register.\\
\begin{tabularx}{\textwidth}{|l|l|l|X|}
\hline
\bf Bits & \bf Access & \bf Default & \bf Description \\
\hline
0 & W & 0 & CS control. Setting this bit activates the CS line of the SDRAM during the command transaction that results from writing to the bypass register. As the SDRAM control bus is active low, setting this bit actually puts a '0' logic level to the CS line. \\
\hline
1 & W & 0 & WE control (same as above). \\
\hline
2 & W & 0 & CAS control (same as above). \\
\hline
3 & W & 0 & RAS control (same as above). \\
\hline
16 -- 4 & RW & 0 & Address. Defines the current state of the address pins. \\
\hline
18 -- 17 & RW & 0 & Bank address. Defines the current state of the bank address pins. \\
\hline
31 -- 19 & --- & 0 & Reserved. \\
\hline
\end{tabularx}\\
\textit{NB. When this register is written, the address pins change synchronously at the same time as the command pins, so there is no need to pre-position the address bits before issuing a command. Commands like loading the mode register can therefore be performed with a single write to this register.}
\subsubsection{Timing register, offset 0x08}
This register allows the CPU to tune the behaviour of HPDMC so that it meets SDRAM timing requirements while avoiding unnecessary wait cycles.
The controller must be held in reset using the system register when the timing register is modified.\\
\begin{tabularx}{\textwidth}{|l|l|l|X|}
\hline
\bf Bits & \bf Access & \bf Default & \bf Description \\
\hline
2 -- 0 & RW & 2 & Number of clock cycles the scheduler must wait following a Precharge command. Usually referred to as $t_{RP}$ in SDRAM datasheets. \\
\hline
5 -- 3 & RW & 2 & Number of clock cycles the scheduler must wait following an Activate command. Usually referred to as $t_{RCD}$ in SDRAM datasheets. \\
\hline
6 & RW & 0 & CAS latency : 0 = CL2, 1 = CL3. CL2.5 is not supported. \\
\hline
17 -- 7 & RW & 740 & Autorefresh period, in clock cycles. This is the time between \textbf{each} Auto Refresh command that is issued to the SDRAM, not the delay between two consecutive refreshes of a particular row. Usually referred to as $t_{REFI}$ in SDRAM datasheets, which is often 7.8$\mu$s (64ms is an improbable value for this field). \\
\hline
21 -- 18 & RW & 8 & Number of clock cycles the controller must wait following an Auto Refresh command. Usually referred to as $t_{RFC}$ in SDRAM datasheets. \\
\hline
23 -- 22 & RW & 1 & Number of clock cycles the controller must wait following the last data sent to the SDRAM during a write. Usually referred to as $t_{WR}$ in SDRAM datasheets. \\
\hline
31 -- 24 & --- & 0 & Reserved. \\
\hline
\end{tabularx}\\
\textit{NB. The default values are example only, and must be adapted to your particular setup.}
\subsubsection{Delay register, offset 0x0C}
This register controls the amount of delay that is introduced on the data lines when reading from memory. It directly controls the \verb!IDELAY! elements that are inserted between the pins and the \verb!IDDR! registers.
Writing once to the register toggles the \verb!IDELAY! control signals \textbf{once}, that is to say, the signals will be active for one clock cycle and then go back to their default zero state.
This register also controls the amount of phase shift that is introduced between the system clock and DQS (typically 90 degrees). HPDMC provides three signals, \verb!dqs_psen!, \verb!dqs_psincdec! and \verb!dqs_psdone! that should be connected to the DCM used to generate the DQS clock which is controlled by this register.
The enable and incrementation bits work the same as for \verb!IDELAY!. They should only be used when the ready bit (5) is set.\\
\begin{tabularx}{\textwidth}{|l|l|l|X|}
\hline
\bf Bits & \bf Access & \bf Default & \bf Description \\
\hline
0 & W & 0 & Resets delay to 0. If this bit is set, the others are ignored. \\
\hline
1 & W & 0 & Increments or decrements delay by one tap (typically 78ps). If the bit 2 is set at the same time this bit is written, the tap delay is incremented. Otherwise, it is decremented. \\
\hline
2 & W & 0 & Selects between incrementation and decrementation of the input tap delay. \\
\hline
3 & W & 0 & Increments or decrements the phase shift on DQS. If the bit 4 is set at the same time this bit is written, the phase shift is incremented. Otherwise, it is decremented. The phase shift is typically between -255 and 255 and is expressed in 1/256ths of the clock period. \\
\hline
4 & W & 0 & Selects between incrementation and decrementation of the DQS phase shift. \\
\hline
5 & R & 0 & When this bit is set, the DCM used to generate DQS is ready for phase shift. \\
\hline
7--6 & R & 0 & Retreives the values of the pll\_stat inputs of the core. These inputs are internally double-latched so that they can directly accept asynchronous signals. They are intended to monitor the lock status of the DCMs used to generate the SDRAM and DQS clocks. \\
\hline
31--8 & --- & 0 & Reserved. \\
\hline
\end{tabularx}
This register can be written to at any time.
\subsection{SDRAM management unit}
The SDRAM management unit is a state machine which controls sequentially the SDRAM address and command bus. Unless the core is in bypass mode, the management unit has full control over the SDRAM bus.
This unit is responsible for precharging banks, activating rows, periodically refreshing the DRAM, and sending read and write commands to the SDRAM.
It has inputs connected to the control interface registers to retreive the $t_{RP}$, $t_{RCD}$, $t_{REFI}$ and $t_{RFC}$ timing values, as well as the row idle time.
It handles read and write requests through a port made up of four elements :
\begin{itemize}
\item a strobe input
\item a write enable input (which tells if the command to send to the SDRAM should be a read or a write)
\item an address input
\item an acknowledgement output
\end{itemize}
The protocol used on these signals is close to the one employed in Wishbone. The strobe signal indicates when a new command should be completed, and remains asserted (with other signals kept constant) until the acknowledgement signal is asserted. At the next clock cycle, a new command should be presented, or the strobe signal should be de-asserted.
In HPDMC, those signals are driven by the bus interface.
The management unit also signals the data path when it has sent a read or a write command into the SDRAM. The signal is asserted exactly at the same time as the command is asserted.
It receives \verb!read_safe!, \verb!write_safe! and \verb!precharge_safe! signals from the data path, whose meanings are explained below.
\subsection{Data path controller}
The data path controller is responsible for :
\begin{itemize}
\item deciding the direction of the DQ and DQS pins
\item delaying read, write and precharge commands from the management unit that would create conflicts
\end{itemize}
The delaying of the commands is acheived through the use of three signals :
\begin{itemize}
\item \verb!read_safe! : when this signal is asserted, it is safe to send a Read command to the SDRAM. This is used to prevent conflicts on the data bus : this signal is asserted when, taking into account the CAS latency and the burst length, the resulting burst would not overlap the currently running one.
\item \verb!write_safe! : same thing, for the Write command.
\item \verb!concerned_bank[3..0]! : when the management unit issues a Read or Write command, it must inform the data path controller about the bank which the transfer takes place in, using this one-hot encoded signal.
\item \verb!precharge_safe[3..0]! : when a bit in this signal is asserted, it is safe to precharge the corresponding bank. The management unit must use this signal so as not to precharge a bank interrupting a read burst or causing a write-to-precharge violation.
\end{itemize}
The data path controller is also connected to the control interface, to retreive $t_{WR}$ and the CAS latency.
\subsection{Data path}
Data is captured from or sent to the SDRAM using \verb!IDDR! and \verb!ODDR! primitives, in order to limit timing nightmares with ISE.
When writing to the DDRAM, the \verb!ODDR! primitive puts out data synchronously to the rising and falling edges of the system clock. This was chosen to ease timing between the FML (which is clocked by the system clock) and the I/O elements without introducing additional latency cycles. The data should therefore be strobed by DQS after a short time following each system clock edge. A delay corresponding to a 90 degrees phase shift gives the best margins, and can be controlled using the delay register.
When reading from the DDRAM, the \verb!IDDR! element is also clocked by the system clock for the same reason. The data must therefore be delayed by typically one quarter of the clock cycle so that it becomes center-aligned with the system clock edges. \verb!IDELAY! primitives are used for this purpose. DQS lines are not used for reading.
\verb!ODDR!, \verb!IDDR! and \verb!IDELAY! are only supported on Virtex-4 FPGAs, but have equivalents in other families.
\subsection{Bus interface}
The bus interface is responsible for sending commands to the SDRAM management unit according to the request coming from the FML, and acknowledging bus cycles at the appropriate time.
\section{Using the core}
\subsection{Connecting}
The differential clock going to the SDRAM chips should be generated using a dedicated FPGA clocking resource, such as a DCM. It is bad practice to simply add an inverter on the negative clock line, as the inverter will also add a delay.
This DCM can also introduce a 90 degree delay on the clock and the resulting signal be used to generate DQS by connecting it to the \verb!dqs_clk! input of the HPDMC top-level.
HPDMC uses \verb!IDELAY! elements internally, but does not include the required \verb!IDELAYCTRL! primitive. You must instantiate an \verb!IDELAYCTRL! in your design, generate the 200MHz reference clock and connect it to the \verb!IDELAYCTRL! through a \verb!BUFG!. The other signals of \verb!IDELAYCTRL! can be left unused.
\subsection{Programming}
When the system is powered up, HPDMC comes up in bypass mode and the SDRAM initialization sequence should be performed from then, by controlling the pins at a low level using the bypass register.
The SDRAM must be programmed to use a fixed burst length of 8\footnote{It might seem surprising that the burst length of the SDRAM and FML are not the same. This is because DDR SDRAM counts the words on both clock edges. Here, a burst of 8 32-bit words sent at double data rate on the SDRAM side corresponds to a burst of 4 64-bit words at single data rate on the FML side.}, and a CAS latency of 2 (preferred) or 3. CAS latency 2.5 is not supported.
HPDMC's timing registers may also have to be reprogrammed to match the memory chip's parameters. If a DIMM is used, it is possible to read those parameters from the serial presence detect (SPD) EEPROM and program HPDMC accordingly.
Once the SDRAM is initialized and the timing registers are programmed, the controller can be brought up by clearing the bypass and reset bits from the system register.
You may also need to tune the data capture delay. Reset the tap count to 0 by writing bit 0 to the delay register, then increment the delay to the desired value by repeatedly writing bits 1 and 2 simultaneously.
The DQS phase shift may also be adjusted. The procedure is the same, except that the delay cannot be reset and that the ready bit should be set when writing the enable and incrementation bits. The memory is now ready to be accessed over the FML interface.
\section*{Copyright notice}
Copyright \copyright 2007-2009 S\'ebastien Bourdeauducq. \\
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the LICENSE.FDL file at the root of the Milkymist source distribution.
\end{document}
@@ -0,0 +1,298 @@
/*
* Milkymist VJ SoC
* Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
*
* This program 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, version 3 of the License.
*
* This program 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/>.
*/
module hpdmc #(
parameter csr_addr = 4'h0,
/*
* The depth of the SDRAM array, in bytes.
* Capacity (in bytes) is 2^sdram_depth.
*/
parameter sdram_depth = 26,
/*
* The number of column address bits of the SDRAM.
*/
parameter sdram_columndepth = 9
) (
input sys_clk,
input sys_clk_n,
/*
* Clock used to generate DQS.
* Typically sys_clk phased out by 90 degrees,
* as data is sent synchronously to sys_clk.
*/
input dqs_clk,
input dqs_clk_n,
input sys_rst,
/* Control interface */
input [13:0] csr_a,
input csr_we,
input [31:0] csr_di,
output [31:0] csr_do,
/* Simple FML 4x64 interface to the memory contents */
input [sdram_depth-1:0] fml_adr,
input fml_stb,
input fml_we,
output fml_ack,
input [7:0] fml_sel,
input [63:0] fml_di,
output [63:0] fml_do,
/* SDRAM interface.
* The SDRAM clock should be driven synchronously to the system clock.
* It is not generated inside this core so you can take advantage of
* architecture-dependent clocking resources to generate a clean
* differential clock.
*/
output reg sdram_cke,
output reg sdram_cs_n,
output reg sdram_we_n,
output reg sdram_cas_n,
output reg sdram_ras_n,
output reg [12:0] sdram_adr,
output reg [1:0] sdram_ba,
output [3:0] sdram_dm,
inout [31:0] sdram_dq,
inout [3:0] sdram_dqs,
/* Interface to the DCM generating DQS */
output dqs_psen,
output dqs_psincdec,
input dqs_psdone,
input [1:0] pll_stat
);
/* Register all control signals, leaving the possibility to use IOB registers */
wire sdram_cke_r;
wire sdram_cs_n_r;
wire sdram_we_n_r;
wire sdram_cas_n_r;
wire sdram_ras_n_r;
wire [12:0] sdram_adr_r;
wire [1:0] sdram_ba_r;
always @(posedge sys_clk) begin
sdram_cke <= sdram_cke_r;
sdram_cs_n <= sdram_cs_n_r;
sdram_we_n <= sdram_we_n_r;
sdram_cas_n <= sdram_cas_n_r;
sdram_ras_n <= sdram_ras_n_r;
sdram_ba <= sdram_ba_r;
sdram_adr <= sdram_adr_r;
end
/* Mux the control signals according to the "bypass" selection.
* CKE always comes from the control interface.
*/
wire bypass;
wire sdram_cs_n_bypass;
wire sdram_we_n_bypass;
wire sdram_cas_n_bypass;
wire sdram_ras_n_bypass;
wire [12:0] sdram_adr_bypass;
wire [1:0] sdram_ba_bypass;
wire sdram_cs_n_mgmt;
wire sdram_we_n_mgmt;
wire sdram_cas_n_mgmt;
wire sdram_ras_n_mgmt;
wire [12:0] sdram_adr_mgmt;
wire [1:0] sdram_ba_mgmt;
assign sdram_cs_n_r = bypass ? sdram_cs_n_bypass : sdram_cs_n_mgmt;
assign sdram_we_n_r = bypass ? sdram_we_n_bypass : sdram_we_n_mgmt;
assign sdram_cas_n_r = bypass ? sdram_cas_n_bypass : sdram_cas_n_mgmt;
assign sdram_ras_n_r = bypass ? sdram_ras_n_bypass : sdram_ras_n_mgmt;
assign sdram_adr_r = bypass ? sdram_adr_bypass : sdram_adr_mgmt;
assign sdram_ba_r = bypass ? sdram_ba_bypass : sdram_ba_mgmt;
/* Control interface */
wire sdram_rst;
wire [2:0] tim_rp;
wire [2:0] tim_rcd;
wire tim_cas;
wire [10:0] tim_refi;
wire [3:0] tim_rfc;
wire [1:0] tim_wr;
wire idelay_rst;
wire idelay_ce;
wire idelay_inc;
hpdmc_ctlif #(
.csr_addr(csr_addr)
) ctlif (
.sys_clk(sys_clk),
.sys_rst(sys_rst),
.csr_a(csr_a),
.csr_we(csr_we),
.csr_di(csr_di),
.csr_do(csr_do),
.bypass(bypass),
.sdram_rst(sdram_rst),
.sdram_cke(sdram_cke_r),
.sdram_cs_n(sdram_cs_n_bypass),
.sdram_we_n(sdram_we_n_bypass),
.sdram_cas_n(sdram_cas_n_bypass),
.sdram_ras_n(sdram_ras_n_bypass),
.sdram_adr(sdram_adr_bypass),
.sdram_ba(sdram_ba_bypass),
.tim_rp(tim_rp),
.tim_rcd(tim_rcd),
.tim_cas(tim_cas),
.tim_refi(tim_refi),
.tim_rfc(tim_rfc),
.tim_wr(tim_wr),
.idelay_rst(idelay_rst),
.idelay_ce(idelay_ce),
.idelay_inc(idelay_inc),
.dqs_psen(dqs_psen),
.dqs_psincdec(dqs_psincdec),
.dqs_psdone(dqs_psdone),
.pll_stat(pll_stat)
);
/* SDRAM management unit */
wire mgmt_stb;
wire mgmt_we;
wire [sdram_depth-3-1:0] mgmt_address;
wire mgmt_ack;
wire read;
wire write;
wire [3:0] concerned_bank;
wire read_safe;
wire write_safe;
wire [3:0] precharge_safe;
hpdmc_mgmt #(
.sdram_depth(sdram_depth),
.sdram_columndepth(sdram_columndepth)
) mgmt (
.sys_clk(sys_clk),
.sdram_rst(sdram_rst),
.tim_rp(tim_rp),
.tim_rcd(tim_rcd),
.tim_refi(tim_refi),
.tim_rfc(tim_rfc),
.stb(mgmt_stb),
.we(mgmt_we),
.address(mgmt_address),
.ack(mgmt_ack),
.read(read),
.write(write),
.concerned_bank(concerned_bank),
.read_safe(read_safe),
.write_safe(write_safe),
.precharge_safe(precharge_safe),
.sdram_cs_n(sdram_cs_n_mgmt),
.sdram_we_n(sdram_we_n_mgmt),
.sdram_cas_n(sdram_cas_n_mgmt),
.sdram_ras_n(sdram_ras_n_mgmt),
.sdram_adr(sdram_adr_mgmt),
.sdram_ba(sdram_ba_mgmt)
);
/* Bus interface */
wire data_ack;
hpdmc_busif #(
.sdram_depth(sdram_depth)
) busif (
.sys_clk(sys_clk),
.sdram_rst(sdram_rst),
.fml_adr(fml_adr),
.fml_stb(fml_stb),
.fml_we(fml_we),
.fml_ack(fml_ack),
.mgmt_stb(mgmt_stb),
.mgmt_we(mgmt_we),
.mgmt_address(mgmt_address),
.mgmt_ack(mgmt_ack),
.data_ack(data_ack)
);
/* Data path controller */
wire direction;
wire direction_r;
hpdmc_datactl datactl(
.sys_clk(sys_clk),
.sdram_rst(sdram_rst),
.read(read),
.write(write),
.concerned_bank(concerned_bank),
.read_safe(read_safe),
.write_safe(write_safe),
.precharge_safe(precharge_safe),
.ack(data_ack),
.direction(direction),
.direction_r(direction_r),
.tim_cas(tim_cas),
.tim_wr(tim_wr)
);
/* Data path */
hpdmc_ddrio ddrio(
.sys_clk(sys_clk),
.sys_clk_n(sys_clk_n),
.dqs_clk(dqs_clk),
.dqs_clk_n(dqs_clk_n),
.direction(direction),
.direction_r(direction_r),
/* Bit meaning is the opposite between
* the FML selection signal and SDRAM DM pins.
*/
.mo(~fml_sel),
.do(fml_di),
.di(fml_do),
.sdram_dm(sdram_dm),
.sdram_dq(sdram_dq),
.sdram_dqs(sdram_dqs),
.idelay_rst(idelay_rst),
.idelay_ce(idelay_ce),
.idelay_inc(idelay_inc)
);
endmodule
@@ -0,0 +1,57 @@
/*
* Milkymist VJ SoC
* Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
*
* This program 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, version 3 of the License.
*
* This program 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/>.
*/
module hpdmc_banktimer(
input sys_clk,
input sdram_rst,
input tim_cas,
input [1:0] tim_wr,
input read,
input write,
output reg precharge_safe
);
reg [2:0] counter;
always @(posedge sys_clk) begin
if(sdram_rst) begin
counter <= 3'd0;
precharge_safe <= 1'b1;
end else begin
if(read) begin
/* see p.26 of datasheet :
* "A Read burst may be followed by, or truncated with, a Precharge command
* to the same bank. The Precharge command should be issued x cycles after
* the Read command, where x equals the number of desired data element
* pairs"
*/
counter <= 3'd4;
precharge_safe <= 1'b0;
end else if(write) begin
counter <= {1'b1, tim_wr};
precharge_safe <= 1'b0;
end else begin
if(counter == 3'b1)
precharge_safe <= 1'b1;
if(~precharge_safe)
counter <= counter - 3'b1;
end
end
end
endmodule
@@ -0,0 +1,58 @@
/*
* Milkymist VJ SoC
* Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
*
* This program 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, version 3 of the License.
*
* This program 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/>.
*/
/* Simple FML interface for HPDMC */
module hpdmc_busif #(
parameter sdram_depth = 26
) (
input sys_clk,
input sdram_rst,
input [sdram_depth-1:0] fml_adr,
input fml_stb,
input fml_we,
output fml_ack,
output mgmt_stb,
output mgmt_we,
output [sdram_depth-3-1:0] mgmt_address, /* in 64-bit words */
input mgmt_ack,
input data_ack
);
reg mgmt_stb_en;
assign mgmt_stb = fml_stb & mgmt_stb_en;
assign mgmt_we = fml_we;
assign mgmt_address = fml_adr[sdram_depth-1:3];
assign fml_ack = data_ack;
always @(posedge sys_clk) begin
if(sdram_rst)
mgmt_stb_en = 1'b1;
else begin
if(mgmt_ack)
mgmt_stb_en = 1'b0;
if(data_ack)
mgmt_stb_en = 1'b1;
end
end
endmodule
@@ -0,0 +1,157 @@
/*
* Milkymist VJ SoC
* Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
*
* This program 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, version 3 of the License.
*
* This program 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/>.
*/
module hpdmc_ctlif #(
parameter csr_addr = 4'h0
) (
input sys_clk,
input sys_rst,
input [13:0] csr_a,
input csr_we,
input [31:0] csr_di,
output reg [31:0] csr_do,
output reg bypass,
output reg sdram_rst,
output reg sdram_cke,
output reg sdram_cs_n,
output reg sdram_we_n,
output reg sdram_cas_n,
output reg sdram_ras_n,
output reg [12:0] sdram_adr,
output reg [1:0] sdram_ba,
/* Clocks we must wait following a PRECHARGE command (usually tRP). */
output reg [2:0] tim_rp,
/* Clocks we must wait following an ACTIVATE command (usually tRCD). */
output reg [2:0] tim_rcd,
/* CAS latency, 0 = 2 */
output reg tim_cas,
/* Auto-refresh period (usually tREFI). */
output reg [10:0] tim_refi,
/* Clocks we must wait following an AUTO REFRESH command (usually tRFC). */
output reg [3:0] tim_rfc,
/* Clocks we must wait following the last word written to the SDRAM (usually tWR). */
output reg [1:0] tim_wr,
output reg idelay_rst,
output reg idelay_ce,
output reg idelay_inc,
output reg dqs_psen,
output reg dqs_psincdec,
input dqs_psdone,
input [1:0] pll_stat
);
reg psready;
always @(posedge sys_clk) begin
if(dqs_psdone)
psready <= 1'b1;
else if(dqs_psen)
psready <= 1'b0;
end
wire csr_selected = csr_a[13:10] == csr_addr;
/* Double-latching on pll_stat (asynchronous) */
reg [1:0] pll_stat1;
reg [1:0] pll_stat2;
always @(posedge sys_clk) begin
pll_stat1 <= pll_stat;
pll_stat2 <= pll_stat1;
end
always @(posedge sys_clk) begin
if(sys_rst) begin
csr_do <= 32'd0;
bypass <= 1'b1;
sdram_rst <= 1'b1;
sdram_cke <= 1'b0;
sdram_adr <= 13'd0;
sdram_ba <= 2'd0;
tim_rp <= 3'd2;
tim_rcd <= 3'd2;
tim_cas <= 1'b0;
tim_refi <= 11'd740;
tim_rfc <= 4'd8;
tim_wr <= 2'd2;
end else begin
sdram_cs_n <= 1'b1;
sdram_we_n <= 1'b1;
sdram_cas_n <= 1'b1;
sdram_ras_n <= 1'b1;
idelay_rst <= 1'b0;
idelay_ce <= 1'b0;
idelay_inc <= 1'b0;
dqs_psen <= 1'b0;
dqs_psincdec <= 1'b0;
csr_do <= 32'd0;
if(csr_selected) begin
if(csr_we) begin
case(csr_a[1:0])
2'b00: begin
bypass <= csr_di[0];
sdram_rst <= csr_di[1];
sdram_cke <= csr_di[2];
end
2'b01: begin
sdram_cs_n <= ~csr_di[0];
sdram_we_n <= ~csr_di[1];
sdram_cas_n <= ~csr_di[2];
sdram_ras_n <= ~csr_di[3];
sdram_adr <= csr_di[16:4];
sdram_ba <= csr_di[18:17];
end
2'b10: begin
tim_rp <= csr_di[2:0];
tim_rcd <= csr_di[5:3];
tim_cas <= csr_di[6];
tim_refi <= csr_di[17:7];
tim_rfc <= csr_di[21:18];
tim_wr <= csr_di[23:22];
end
2'b11: begin
idelay_rst <= csr_di[0];
idelay_ce <= csr_di[1];
idelay_inc <= csr_di[2];
dqs_psen <= csr_di[3];
dqs_psincdec <= csr_di[4];
end
endcase
end
case(csr_a[1:0])
2'b00: csr_do <= {sdram_cke, sdram_rst, bypass};
2'b01: csr_do <= {sdram_ba, sdram_adr, 4'h0};
2'b10: csr_do <= {tim_wr, tim_rfc, tim_refi, tim_cas, tim_rcd, tim_rp};
2'b11: csr_do <= {pll_stat2, psready, 5'd0};
endcase
end
end
end
endmodule
@@ -0,0 +1,216 @@
/*
* Milkymist VJ SoC
* Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
*
* This program 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, version 3 of the License.
*
* This program 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/>.
*/
module hpdmc_datactl(
input sys_clk,
input sdram_rst,
input read,
input write,
input [3:0] concerned_bank,
output reg read_safe,
output reg write_safe,
output [3:0] precharge_safe,
output reg ack,
output reg direction,
output direction_r,
input tim_cas,
input [1:0] tim_wr
);
/*
* read_safe: whether it is safe to register a Read command
* into the SDRAM at the next cycle.
*/
reg [2:0] read_safe_counter;
always @(posedge sys_clk) begin
if(sdram_rst) begin
read_safe_counter <= 3'd0;
read_safe <= 1'b1;
end else begin
if(read) begin
read_safe_counter <= 3'd4;
read_safe <= 1'b0;
end else if(write) begin
/* after a write, read is unsafe for 5 cycles (4 transfers + tWTR=1) */
read_safe_counter <= 3'd5;
read_safe <= 1'b0;
end else begin
if(read_safe_counter == 3'd1)
read_safe <= 1'b1;
if(~read_safe)
read_safe_counter <= read_safe_counter - 3'd1;
end
end
end
/*
* write_safe: whether it is safe to register a Write command
* into the SDRAM at the next cycle.
*/
reg [2:0] write_safe_counter;
always @(posedge sys_clk) begin
if(sdram_rst) begin
write_safe_counter <= 3'd0;
write_safe <= 1'b1;
end else begin
if(read) begin
write_safe_counter <= {1'b1, tim_cas, ~tim_cas};
write_safe <= 1'b0;
end else if(write) begin
write_safe_counter <= 3'd3;
write_safe <= 1'b0;
end else begin
if(write_safe_counter == 3'd1)
write_safe <= 1'b1;
if(~write_safe)
write_safe_counter <= write_safe_counter - 3'd1;
end
end
end
/* Generate ack signal.
* After write is asserted, it should pulse after 2 cycles.
* After read is asserted, it should pulse after CL+3 cycles, that is
* 5 cycles when tim_cas = 0
* 6 cycles when tim_cas = 1
*/
reg ack_read3;
reg ack_read2;
reg ack_read1;
reg ack_read0;
always @(posedge sys_clk) begin
if(sdram_rst) begin
ack_read3 <= 1'b0;
ack_read2 <= 1'b0;
ack_read1 <= 1'b0;
ack_read0 <= 1'b0;
end else begin
if(tim_cas) begin
ack_read3 <= read;
ack_read2 <= ack_read3;
ack_read1 <= ack_read2;
ack_read0 <= ack_read1;
end else begin
ack_read2 <= read;
ack_read1 <= ack_read2;
ack_read0 <= ack_read1;
end
end
end
reg ack0;
always @(posedge sys_clk) begin
if(sdram_rst) begin
ack0 <= 1'b0;
ack <= 1'b0;
end else begin
ack0 <= ack_read0|write;
ack <= ack0;
end
end
/* during a 4-word write, we drive the pins for 5 cycles
* and 1 cycle in advance (first word is invalid)
* so that we remove glitches on DQS without resorting
* to asynchronous logic.
*/
/* direction must be glitch-free, as it directly drives the
* tri-state enable for DQ and DQS.
*/
reg write_d;
reg [2:0] counter_writedirection;
always @(posedge sys_clk) begin
if(sdram_rst) begin
counter_writedirection <= 3'd0;
direction <= 1'b0;
end else begin
if(write_d) begin
counter_writedirection <= 3'b101;
direction <= 1'b1;
end else begin
if(counter_writedirection == 3'b001)
direction <= 1'b0;
if(direction)
counter_writedirection <= counter_writedirection - 3'd1;
end
end
end
assign direction_r = write_d|(|counter_writedirection);
always @(posedge sys_clk) begin
if(sdram_rst)
write_d <= 1'b0;
else
write_d <= write;
end
/* Counters that prevent a busy bank from being precharged */
hpdmc_banktimer banktimer0(
.sys_clk(sys_clk),
.sdram_rst(sdram_rst),
.tim_cas(tim_cas),
.tim_wr(tim_wr),
.read(read & concerned_bank[0]),
.write(write & concerned_bank[0]),
.precharge_safe(precharge_safe[0])
);
hpdmc_banktimer banktimer1(
.sys_clk(sys_clk),
.sdram_rst(sdram_rst),
.tim_cas(tim_cas),
.tim_wr(tim_wr),
.read(read & concerned_bank[1]),
.write(write & concerned_bank[1]),
.precharge_safe(precharge_safe[1])
);
hpdmc_banktimer banktimer2(
.sys_clk(sys_clk),
.sdram_rst(sdram_rst),
.tim_cas(tim_cas),
.tim_wr(tim_wr),
.read(read & concerned_bank[2]),
.write(write & concerned_bank[2]),
.precharge_safe(precharge_safe[2])
);
hpdmc_banktimer banktimer3(
.sys_clk(sys_clk),
.sdram_rst(sdram_rst),
.tim_cas(tim_cas),
.tim_wr(tim_wr),
.read(read & concerned_bank[3]),
.write(write & concerned_bank[3]),
.precharge_safe(precharge_safe[3])
);
endmodule
@@ -0,0 +1,371 @@
/*
* Milkymist VJ SoC
* Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
*
* This program 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, version 3 of the License.
*
* This program 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/>.
*/
module hpdmc_mgmt #(
parameter sdram_depth = 26,
parameter sdram_columndepth = 9
) (
input sys_clk,
input sdram_rst,
input [2:0] tim_rp,
input [2:0] tim_rcd,
input [10:0] tim_refi,
input [3:0] tim_rfc,
input stb,
input we,
input [sdram_depth-3-1:0] address, /* in 64-bit words */
output reg ack,
output reg read,
output reg write,
output [3:0] concerned_bank,
input read_safe,
input write_safe,
input [3:0] precharge_safe,
output sdram_cs_n,
output sdram_we_n,
output sdram_cas_n,
output sdram_ras_n,
output [12:0] sdram_adr,
output [1:0] sdram_ba
);
/*
* Address Mapping :
* | ROW ADDRESS | BANK NUMBER | COL ADDRESS | for 32-bit words
* |depth-1 coldepth+2|coldepth+1 coldepth|coldepth-1 0|
* (depth for 32-bit words, which is sdram_depth-2)
*/
parameter rowdepth = sdram_depth-2-1-(sdram_columndepth+2)+1;
wire [sdram_depth-2-1:0] address32 = {address, 1'b0};
wire [sdram_columndepth-1:0] col_address = address32[sdram_columndepth-1:0];
wire [1:0] bank_address = address32[sdram_columndepth+1:sdram_columndepth];
wire [rowdepth-1:0] row_address = address32[sdram_depth-2-1:sdram_columndepth+2];
reg [3:0] bank_address_onehot;
always @(*) begin
case(bank_address)
2'b00: bank_address_onehot <= 4'b0001;
2'b01: bank_address_onehot <= 4'b0010;
2'b10: bank_address_onehot <= 4'b0100;
2'b11: bank_address_onehot <= 4'b1000;
endcase
end
/* Track open rows */
reg [3:0] has_openrow;
reg [rowdepth-1:0] openrows[0:3];
reg [3:0] track_close;
reg [3:0] track_open;
always @(posedge sys_clk) begin
if(sdram_rst) begin
has_openrow = 4'h0;
end else begin
has_openrow = (has_openrow | track_open) & ~track_close;
if(track_open[0]) openrows[0] <= row_address;
if(track_open[1]) openrows[1] <= row_address;
if(track_open[2]) openrows[2] <= row_address;
if(track_open[3]) openrows[3] <= row_address;
end
end
/* Bank precharge safety */
assign concerned_bank = bank_address_onehot;
wire current_precharge_safe =
(precharge_safe[0] | ~bank_address_onehot[0])
&(precharge_safe[1] | ~bank_address_onehot[1])
&(precharge_safe[2] | ~bank_address_onehot[2])
&(precharge_safe[3] | ~bank_address_onehot[3]);
/* Check for page hits */
wire bank_open = has_openrow[bank_address];
wire page_hit = bank_open & (openrows[bank_address] == row_address);
/* Address drivers */
reg sdram_adr_loadrow;
reg sdram_adr_loadcol;
reg sdram_adr_loadA10;
assign sdram_adr =
({13{sdram_adr_loadrow}} & row_address)
|({13{sdram_adr_loadcol}} & col_address)
|({13{sdram_adr_loadA10}} & 13'd1024);
assign sdram_ba = bank_address;
/* Command drivers */
reg sdram_cs;
reg sdram_we;
reg sdram_cas;
reg sdram_ras;
assign sdram_cs_n = ~sdram_cs;
assign sdram_we_n = ~sdram_we;
assign sdram_cas_n = ~sdram_cas;
assign sdram_ras_n = ~sdram_ras;
/* Timing counters */
/* The number of clocks we must wait following a PRECHARGE command (usually tRP). */
reg [2:0] precharge_counter;
reg reload_precharge_counter;
wire precharge_done = (precharge_counter == 3'd0);
always @(posedge sys_clk) begin
if(reload_precharge_counter)
precharge_counter <= tim_rp;
else if(~precharge_done)
precharge_counter <= precharge_counter - 3'd1;
end
/* The number of clocks we must wait following an ACTIVATE command (usually tRCD). */
reg [2:0] activate_counter;
reg reload_activate_counter;
wire activate_done = (activate_counter == 3'd0);
always @(posedge sys_clk) begin
if(reload_activate_counter)
activate_counter <= tim_rcd;
else if(~activate_done)
activate_counter <= activate_counter - 3'd1;
end
/* The number of clocks we have left before we must refresh one row in the SDRAM array (usually tREFI). */
reg [10:0] refresh_counter;
reg reload_refresh_counter;
wire must_refresh = refresh_counter == 11'd0;
always @(posedge sys_clk) begin
if(sdram_rst)
refresh_counter <= 11'd0;
else begin
if(reload_refresh_counter)
refresh_counter <= tim_refi;
else if(~must_refresh)
refresh_counter <= refresh_counter - 11'd1;
end
end
/* The number of clocks we must wait following an AUTO REFRESH command (usually tRFC). */
reg [3:0] autorefresh_counter;
reg reload_autorefresh_counter;
wire autorefresh_done = (autorefresh_counter == 4'd0);
always @(posedge sys_clk) begin
if(reload_autorefresh_counter)
autorefresh_counter <= tim_rfc;
else if(~autorefresh_done)
autorefresh_counter <= autorefresh_counter - 4'd1;
end
/* FSM that pushes commands into the SDRAM */
reg [3:0] state;
reg [3:0] next_state;
parameter IDLE = 4'd0;
parameter ACTIVATE = 4'd1;
parameter READ = 4'd2;
parameter WRITE = 4'd3;
parameter PRECHARGEALL = 4'd4;
parameter AUTOREFRESH = 4'd5;
parameter AUTOREFRESH_WAIT = 4'd6;
always @(posedge sys_clk) begin
if(sdram_rst)
state <= IDLE;
else begin
//$display("state: %d -> %d", state, next_state);
state <= next_state;
end
end
always @(*) begin
next_state = state;
reload_precharge_counter = 1'b0;
reload_activate_counter = 1'b0;
reload_refresh_counter = 1'b0;
reload_autorefresh_counter = 1'b0;
sdram_cs = 1'b0;
sdram_we = 1'b0;
sdram_cas = 1'b0;
sdram_ras = 1'b0;
sdram_adr_loadrow = 1'b0;
sdram_adr_loadcol = 1'b0;
sdram_adr_loadA10 = 1'b0;
track_close = 4'b0000;
track_open = 4'b0000;
read = 1'b0;
write = 1'b0;
ack = 1'b0;
case(state)
IDLE: begin
if(must_refresh)
next_state = PRECHARGEALL;
else begin
if(stb) begin
if(page_hit) begin
if(we) begin
if(write_safe) begin
/* Write */
sdram_cs = 1'b1;
sdram_ras = 1'b0;
sdram_cas = 1'b1;
sdram_we = 1'b1;
sdram_adr_loadcol = 1'b1;
write = 1'b1;
ack = 1'b1;
end
end else begin
if(read_safe) begin
/* Read */
sdram_cs = 1'b1;
sdram_ras = 1'b0;
sdram_cas = 1'b1;
sdram_we = 1'b0;
sdram_adr_loadcol = 1'b1;
read = 1'b1;
ack = 1'b1;
end
end
end else begin
if(bank_open) begin
if(current_precharge_safe) begin
/* Precharge Bank */
sdram_cs = 1'b1;
sdram_ras = 1'b1;
sdram_cas = 1'b0;
sdram_we = 1'b1;
track_close = bank_address_onehot;
reload_precharge_counter = 1'b1;
next_state = ACTIVATE;
end
end else begin
/* Activate */
sdram_cs = 1'b1;
sdram_ras = 1'b1;
sdram_cas = 1'b0;
sdram_we = 1'b0;
sdram_adr_loadrow = 1'b1;
track_open = bank_address_onehot;
reload_activate_counter = 1'b1;
if(we)
next_state = WRITE;
else
next_state = READ;
end
end
end
end
end
ACTIVATE: begin
if(precharge_done) begin
sdram_cs = 1'b1;
sdram_ras = 1'b1;
sdram_cas = 1'b0;
sdram_we = 1'b0;
sdram_adr_loadrow = 1'b1;
track_open = bank_address_onehot;
reload_activate_counter = 1'b1;
if(we)
next_state = WRITE;
else
next_state = READ;
end
end
READ: begin
if(activate_done) begin
if(read_safe) begin
sdram_cs = 1'b1;
sdram_ras = 1'b0;
sdram_cas = 1'b1;
sdram_we = 1'b0;
sdram_adr_loadcol = 1'b1;
read = 1'b1;
ack = 1'b1;
next_state = IDLE;
end
end
end
WRITE: begin
if(activate_done) begin
if(write_safe) begin
sdram_cs = 1'b1;
sdram_ras = 1'b0;
sdram_cas = 1'b1;
sdram_we = 1'b1;
sdram_adr_loadcol = 1'b1;
write = 1'b1;
ack = 1'b1;
next_state = IDLE;
end
end
end
PRECHARGEALL: begin
if(precharge_safe == 4'b1111) begin
sdram_cs = 1'b1;
sdram_ras = 1'b1;
sdram_cas = 1'b0;
sdram_we = 1'b1;
sdram_adr_loadA10 = 1'b1;
reload_precharge_counter = 1'b1;
track_close = 4'b1111;
next_state = AUTOREFRESH;
end
end
AUTOREFRESH: begin
if(precharge_done) begin
sdram_cs = 1'b1;
sdram_ras = 1'b1;
sdram_cas = 1'b1;
sdram_we = 1'b0;
reload_refresh_counter = 1'b1;
reload_autorefresh_counter = 1'b1;
next_state = AUTOREFRESH_WAIT;
end
end
AUTOREFRESH_WAIT: begin
if(autorefresh_done)
next_state = IDLE;
end
endcase
end
endmodule
@@ -0,0 +1,137 @@
/*
* Milkymist VJ SoC
* Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
*
* This program 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, version 3 of the License.
*
* This program 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/>.
*/
module hpdmc_ddrio(
input sys_clk,
input sys_clk_n,
input dqs_clk,
input dqs_clk_n,
input direction,
input direction_r,
input [7:0] mo,
input [63:0] do,
output [63:0] di,
output [3:0] sdram_dm,
inout [31:0] sdram_dq,
inout [3:0] sdram_dqs,
input idelay_rst,
input idelay_ce,
input idelay_inc
);
/******/
/* DQ */
/******/
wire [31:0] sdram_dq_t;
wire [31:0] sdram_dq_out;
wire [31:0] sdram_dq_in;
hpdmc_iobuf32 iobuf_dq(
.T(sdram_dq_t),
.I(sdram_dq_out),
.O(sdram_dq_in),
.IO(sdram_dq)
);
hpdmc_oddr32 oddr_dq_t(
.Q(sdram_dq_t),
.C0(sys_clk),
.C1(sys_clk_n),
.CE(1'b1),
.D0({32{~direction_r}}),
.D1({32{~direction_r}}),
.R(1'b0),
.S(1'b0)
);
hpdmc_oddr32 oddr_dq(
.Q(sdram_dq_out),
.C0(sys_clk),
.C1(sys_clk_n),
.CE(1'b1),
.D0(do[63:32]),
.D1(do[31:0]),
.R(1'b0),
.S(1'b0)
);
hpdmc_iddr32 iddr_dq(
.Q0(di[31:0]),
.Q1(di[63:32]),
.C0(sys_clk),
.C1(sys_clk_n),
.CE(1'b1),
.D(sdram_dq_in),
.R(1'b0),
.S(1'b0)
);
/*******/
/* DM */
/*******/
hpdmc_oddr4 oddr_dm(
.Q(sdram_dm),
.C0(sys_clk),
.C1(sys_clk_n),
.CE(1'b1),
.D0(mo[7:4]),
.D1(mo[3:0]),
.R(1'b0),
.S(1'b0)
);
/*******/
/* DQS */
/*******/
wire [3:0] sdram_dqs_t;
wire [3:0] sdram_dqs_out;
hpdmc_obuft4 obuft_dqs(
.T(sdram_dqs_t),
.I(sdram_dqs_out),
.O(sdram_dqs)
);
hpdmc_oddr4 oddr_dqs_t(
.Q(sdram_dqs_t),
.C0(dqs_clk),
.C1(dqs_clk_n),
.CE(1'b1),
.D0({4{~direction_r}}),
.D1({4{~direction_r}}),
.R(1'b0),
.S(1'b0)
);
hpdmc_oddr4 oddr_dqs(
.Q(sdram_dqs_out),
.C0(dqs_clk),
.C1(dqs_clk_n),
.CE(1'b1),
.D0(4'hf),
.D1(4'h0),
.R(1'b0),
.S(1'b0)
);
endmodule
@@ -0,0 +1,522 @@
/*
* Milkymist VJ SoC
* Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
*
* This program 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, version 3 of the License.
*
* This program 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/>.
*/
/*
* Verilog code that really should be replaced with a generate
* statement, but it does not work with some free simulators.
* So I put it in a module so as not to make other code unreadable,
* and keep compatibility with as many simulators as possible.
*/
module hpdmc_iddr32 #(
parameter DDR_ALIGNMENT = "C0",
parameter INIT_Q0 = 1'b0,
parameter INIT_Q1 = 1'b0,
parameter SRTYPE = "ASYNC"
) (
output [31:0] Q0,
output [31:0] Q1,
input C0,
input C1,
input CE,
input [31:0] D,
input R,
input S
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr0 (
.Q0(Q0[0]),
.Q1(Q1[0]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[0]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr1 (
.Q0(Q0[1]),
.Q1(Q1[1]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[1]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr2 (
.Q0(Q0[2]),
.Q1(Q1[2]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[2]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr3 (
.Q0(Q0[3]),
.Q1(Q1[3]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[3]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr4 (
.Q0(Q0[4]),
.Q1(Q1[4]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[4]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr5 (
.Q0(Q0[5]),
.Q1(Q1[5]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[5]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr6 (
.Q0(Q0[6]),
.Q1(Q1[6]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[6]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr7 (
.Q0(Q0[7]),
.Q1(Q1[7]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[7]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr8 (
.Q0(Q0[8]),
.Q1(Q1[8]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[8]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr9 (
.Q0(Q0[9]),
.Q1(Q1[9]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[9]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr10 (
.Q0(Q0[10]),
.Q1(Q1[10]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[10]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr11 (
.Q0(Q0[11]),
.Q1(Q1[11]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[11]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr12 (
.Q0(Q0[12]),
.Q1(Q1[12]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[12]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr13 (
.Q0(Q0[13]),
.Q1(Q1[13]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[13]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr14 (
.Q0(Q0[14]),
.Q1(Q1[14]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[14]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr15 (
.Q0(Q0[15]),
.Q1(Q1[15]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[15]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr16 (
.Q0(Q0[16]),
.Q1(Q1[16]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[16]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr17 (
.Q0(Q0[17]),
.Q1(Q1[17]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[17]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr18 (
.Q0(Q0[18]),
.Q1(Q1[18]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[18]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr19 (
.Q0(Q0[19]),
.Q1(Q1[19]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[19]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr20 (
.Q0(Q0[20]),
.Q1(Q1[20]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[20]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr21 (
.Q0(Q0[21]),
.Q1(Q1[21]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[21]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr22 (
.Q0(Q0[22]),
.Q1(Q1[22]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[22]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr23 (
.Q0(Q0[23]),
.Q1(Q1[23]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[23]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr24 (
.Q0(Q0[24]),
.Q1(Q1[24]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[24]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr25 (
.Q0(Q0[25]),
.Q1(Q1[25]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[25]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr26 (
.Q0(Q0[26]),
.Q1(Q1[26]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[26]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr27 (
.Q0(Q0[27]),
.Q1(Q1[27]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[27]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr28 (
.Q0(Q0[28]),
.Q1(Q1[28]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[28]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr29 (
.Q0(Q0[29]),
.Q1(Q1[29]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[29]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr30 (
.Q0(Q0[30]),
.Q1(Q1[30]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[30]),
.R(R),
.S(S)
);
IDDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT_Q0(INIT_Q0),
.INIT_Q1(INIT_Q1),
.SRTYPE(SRTYPE)
) iddr31 (
.Q0(Q0[31]),
.Q1(Q1[31]),
.C0(C0),
.C1(C1),
.CE(CE),
.D(D[31]),
.R(R),
.S(S)
);
endmodule
@@ -0,0 +1,225 @@
/*
* Milkymist VJ SoC
* Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
*
* This program 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, version 3 of the License.
*
* This program 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/>.
*/
/*
* Verilog code that really should be replaced with a generate
* statement, but it does not work with some free simulators.
* So I put it in a module so as not to make other code unreadable,
* and keep compatibility with as many simulators as possible.
*/
module hpdmc_iobuf32(
input [31:0] T,
input [31:0] I,
output [31:0] O,
inout [31:0] IO
);
IOBUF iobuf0(
.T(T[0]),
.I(I[0]),
.O(O[0]),
.IO(IO[0])
);
IOBUF iobuf1(
.T(T[1]),
.I(I[1]),
.O(O[1]),
.IO(IO[1])
);
IOBUF iobuf2(
.T(T[2]),
.I(I[2]),
.O(O[2]),
.IO(IO[2])
);
IOBUF iobuf3(
.T(T[3]),
.I(I[3]),
.O(O[3]),
.IO(IO[3])
);
IOBUF iobuf4(
.T(T[4]),
.I(I[4]),
.O(O[4]),
.IO(IO[4])
);
IOBUF iobuf5(
.T(T[5]),
.I(I[5]),
.O(O[5]),
.IO(IO[5])
);
IOBUF iobuf6(
.T(T[6]),
.I(I[6]),
.O(O[6]),
.IO(IO[6])
);
IOBUF iobuf7(
.T(T[7]),
.I(I[7]),
.O(O[7]),
.IO(IO[7])
);
IOBUF iobuf8(
.T(T[8]),
.I(I[8]),
.O(O[8]),
.IO(IO[8])
);
IOBUF iobuf9(
.T(T[9]),
.I(I[9]),
.O(O[9]),
.IO(IO[9])
);
IOBUF iobuf10(
.T(T[10]),
.I(I[10]),
.O(O[10]),
.IO(IO[10])
);
IOBUF iobuf11(
.T(T[11]),
.I(I[11]),
.O(O[11]),
.IO(IO[11])
);
IOBUF iobuf12(
.T(T[12]),
.I(I[12]),
.O(O[12]),
.IO(IO[12])
);
IOBUF iobuf13(
.T(T[13]),
.I(I[13]),
.O(O[13]),
.IO(IO[13])
);
IOBUF iobuf14(
.T(T[14]),
.I(I[14]),
.O(O[14]),
.IO(IO[14])
);
IOBUF iobuf15(
.T(T[15]),
.I(I[15]),
.O(O[15]),
.IO(IO[15])
);
IOBUF iobuf16(
.T(T[16]),
.I(I[16]),
.O(O[16]),
.IO(IO[16])
);
IOBUF iobuf17(
.T(T[17]),
.I(I[17]),
.O(O[17]),
.IO(IO[17])
);
IOBUF iobuf18(
.T(T[18]),
.I(I[18]),
.O(O[18]),
.IO(IO[18])
);
IOBUF iobuf19(
.T(T[19]),
.I(I[19]),
.O(O[19]),
.IO(IO[19])
);
IOBUF iobuf20(
.T(T[20]),
.I(I[20]),
.O(O[20]),
.IO(IO[20])
);
IOBUF iobuf21(
.T(T[21]),
.I(I[21]),
.O(O[21]),
.IO(IO[21])
);
IOBUF iobuf22(
.T(T[22]),
.I(I[22]),
.O(O[22]),
.IO(IO[22])
);
IOBUF iobuf23(
.T(T[23]),
.I(I[23]),
.O(O[23]),
.IO(IO[23])
);
IOBUF iobuf24(
.T(T[24]),
.I(I[24]),
.O(O[24]),
.IO(IO[24])
);
IOBUF iobuf25(
.T(T[25]),
.I(I[25]),
.O(O[25]),
.IO(IO[25])
);
IOBUF iobuf26(
.T(T[26]),
.I(I[26]),
.O(O[26]),
.IO(IO[26])
);
IOBUF iobuf27(
.T(T[27]),
.I(I[27]),
.O(O[27]),
.IO(IO[27])
);
IOBUF iobuf28(
.T(T[28]),
.I(I[28]),
.O(O[28]),
.IO(IO[28])
);
IOBUF iobuf29(
.T(T[29]),
.I(I[29]),
.O(O[29]),
.IO(IO[29])
);
IOBUF iobuf30(
.T(T[30]),
.I(I[30]),
.O(O[30]),
.IO(IO[30])
);
IOBUF iobuf31(
.T(T[31]),
.I(I[31]),
.O(O[31]),
.IO(IO[31])
);
endmodule
@@ -0,0 +1,52 @@
/*
* Milkymist VJ SoC
* Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
*
* This program 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, version 3 of the License.
*
* This program 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/>.
*/
/*
* Verilog code that really should be replaced with a generate
* statement, but it does not work with some free simulators.
* So I put it in a module so as not to make other code unreadable,
* and keep compatibility with as many simulators as possible.
*/
module hpdmc_obuft4(
input [3:0] T,
input [3:0] I,
output [3:0] O
);
OBUFT obuft0(
.T(T[0]),
.I(I[0]),
.O(O[0])
);
OBUFT obuft1(
.T(T[1]),
.I(I[1]),
.O(O[1])
);
OBUFT obuft2(
.T(T[2]),
.I(I[2]),
.O(O[2])
);
OBUFT obuft3(
.T(T[3]),
.I(I[3]),
.O(O[3])
);
endmodule
@@ -0,0 +1,489 @@
/*
* Milkymist VJ SoC
* Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
*
* This program 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, version 3 of the License.
*
* This program 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/>.
*/
/*
* Verilog code that really should be replaced with a generate
* statement, but it does not work with some free simulators.
* So I put it in a module so as not to make other code unreadable,
* and keep compatibility with as many simulators as possible.
*/
module hpdmc_oddr32 #(
parameter DDR_ALIGNMENT = "C0",
parameter INIT = 1'b0,
parameter SRTYPE = "ASYNC"
) (
output [31:0] Q,
input C0,
input C1,
input CE,
input [31:0] D0,
input [31:0] D1,
input R,
input S
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr0 (
.Q(Q[0]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[0]),
.D1(D1[0]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr1 (
.Q(Q[1]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[1]),
.D1(D1[1]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr2 (
.Q(Q[2]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[2]),
.D1(D1[2]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr3 (
.Q(Q[3]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[3]),
.D1(D1[3]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr4 (
.Q(Q[4]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[4]),
.D1(D1[4]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr5 (
.Q(Q[5]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[5]),
.D1(D1[5]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr6 (
.Q(Q[6]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[6]),
.D1(D1[6]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr7 (
.Q(Q[7]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[7]),
.D1(D1[7]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr8 (
.Q(Q[8]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[8]),
.D1(D1[8]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr9 (
.Q(Q[9]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[9]),
.D1(D1[9]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr10 (
.Q(Q[10]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[10]),
.D1(D1[10]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr11 (
.Q(Q[11]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[11]),
.D1(D1[11]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr12 (
.Q(Q[12]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[12]),
.D1(D1[12]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr13 (
.Q(Q[13]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[13]),
.D1(D1[13]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr14 (
.Q(Q[14]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[14]),
.D1(D1[14]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr15 (
.Q(Q[15]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[15]),
.D1(D1[15]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr16 (
.Q(Q[16]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[16]),
.D1(D1[16]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr17 (
.Q(Q[17]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[17]),
.D1(D1[17]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr18 (
.Q(Q[18]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[18]),
.D1(D1[18]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr19 (
.Q(Q[19]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[19]),
.D1(D1[19]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr20 (
.Q(Q[20]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[20]),
.D1(D1[20]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr21 (
.Q(Q[21]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[21]),
.D1(D1[21]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr22 (
.Q(Q[22]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[22]),
.D1(D1[22]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr23 (
.Q(Q[23]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[23]),
.D1(D1[23]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr24 (
.Q(Q[24]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[24]),
.D1(D1[24]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr25 (
.Q(Q[25]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[25]),
.D1(D1[25]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr26 (
.Q(Q[26]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[26]),
.D1(D1[26]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr27 (
.Q(Q[27]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[27]),
.D1(D1[27]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr28 (
.Q(Q[28]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[28]),
.D1(D1[28]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr29 (
.Q(Q[29]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[29]),
.D1(D1[29]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr30 (
.Q(Q[30]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[30]),
.D1(D1[30]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr31 (
.Q(Q[31]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[31]),
.D1(D1[31]),
.R(R),
.S(S)
);
endmodule
@@ -0,0 +1,97 @@
/*
* Milkymist VJ SoC
* Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
*
* This program 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, version 3 of the License.
*
* This program 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/>.
*/
/*
* Verilog code that really should be replaced with a generate
* statement, but it does not work with some free simulators.
* So I put it in a module so as not to make other code unreadable,
* and keep compatibility with as many simulators as possible.
*/
module hpdmc_oddr4 #(
parameter DDR_ALIGNMENT = "C0",
parameter INIT = 1'b0,
parameter SRTYPE = "ASYNC"
) (
output [3:0] Q,
input C0,
input C1,
input CE,
input [3:0] D0,
input [3:0] D1,
input R,
input S
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr0 (
.Q(Q[0]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[0]),
.D1(D1[0]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr1 (
.Q(Q[1]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[1]),
.D1(D1[1]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr2 (
.Q(Q[2]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[2]),
.D1(D1[2]),
.R(R),
.S(S)
);
ODDR2 #(
.DDR_ALIGNMENT(DDR_ALIGNMENT),
.INIT(INIT),
.SRTYPE(SRTYPE)
) oddr3 (
.Q(Q[3]),
.C0(C0),
.C1(C1),
.CE(CE),
.D0(D0[3]),
.D1(D1[3]),
.R(R),
.S(S)
);
endmodule
@@ -0,0 +1,3 @@
SOURCES_HPDMC=tb_hpdmc.v ddr.v oddr2.v iddr2.v obuft.v iobuf.v $(wildcard ../rtl/*.v) $(wildcard ../rtl/spartan6/*.v)
include common.mak
@@ -0,0 +1,14 @@
SOURCES_MODEL=tb_model.v ddr.v
all: hpdmc
model: $(SOURCES_MODEL)
cver $(SOURCES_MODEL)
hpdmc: $(SOURCES)
cver $(SOURCES_HPDMC)
clean:
rm -f verilog.log hpdmc.vcd
.PHONY: clean model hpdmc
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,143 @@
/****************************************************************************************
*
* Disclaimer This software code and all associated documentation, comments or other
* of Warranty: information (collectively "Software") is provided "AS IS" without
* warranty of any kind. MICRON TECHNOLOGY, INC. ("MTI") EXPRESSLY
* DISCLAIMS ALL WARRANTIES EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO, NONINFRINGEMENT OF THIRD PARTY RIGHTS, AND ANY IMPLIED WARRANTIES
* OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE. MTI DOES NOT
* WARRANT THAT THE SOFTWARE WILL MEET YOUR REQUIREMENTS, OR THAT THE
* OPERATION OF THE SOFTWARE WILL BE UNINTERRUPTED OR ERROR-FREE.
* FURTHERMORE, MTI DOES NOT MAKE ANY REPRESENTATIONS REGARDING THE USE OR
* THE RESULTS OF THE USE OF THE SOFTWARE IN TERMS OF ITS CORRECTNESS,
* ACCURACY, RELIABILITY, OR OTHERWISE. THE ENTIRE RISK ARISING OUT OF USE
* OR PERFORMANCE OF THE SOFTWARE REMAINS WITH YOU. IN NO EVENT SHALL MTI,
* ITS AFFILIATED COMPANIES OR THEIR SUPPLIERS BE LIABLE FOR ANY DIRECT,
* INDIRECT, CONSEQUENTIAL, INCIDENTAL, OR SPECIAL DAMAGES (INCLUDING,
* WITHOUT LIMITATION, DAMAGES FOR LOSS OF PROFITS, BUSINESS INTERRUPTION,
* OR LOSS OF INFORMATION) ARISING OUT OF YOUR USE OF OR INABILITY TO USE
* THE SOFTWARE, EVEN IF MTI HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGES. Because some jurisdictions prohibit the exclusion or
* limitation of liability for consequential or incidental damages, the
* above limitation may not apply to you.
*
* Copyright 2003 Micron Technology, Inc. All rights reserved.
*
****************************************************************************************/
`define sg75E
`define x16
// Timing parameters based on Speed Grade 04/07
// SYMBOL UNITS DESCRIPTION
// ------ ----- -----------
`ifdef sg5B // Timing Parameters for -5B (CL = 3)
parameter tCK = 5.0; // tCK ns Nominal Clock Cycle Time
parameter tDQSQ = 0.4; // tDQSQ ns DQS-DQ skew, DQS to last DQ valid, per group, per access
parameter tMRD = 10.0; // tMRD ns Load Mode Register command cycle time
parameter tRAP = 15.0; // tRAP ns ACTIVE to READ with Auto precharge command
parameter tRAS = 40.0; // tRAS ns Active to Precharge command time
parameter tRC = 55.0; // tRC ns Active to Active/Auto Refresh command time
parameter tRFC = 70.0; // tRFC ns Refresh to Refresh Command interval time
parameter tRCD = 15.0; // tRCD ns Active to Read/Write command time
parameter tRP = 15.0; // tRP ns Precharge command period
parameter tRRD = 10.0; // tRRD ns Active bank a to Active bank b command time
parameter tWR = 15.0; // tWR ns Write recovery time
`endif
`ifdef sg6T // Timing Parameters for -6T (CL = 2.5)
parameter tCK = 6.0; // tCK ns Nominal Clock Cycle Time
parameter tDQSQ = 0.45; // tDQSQ ns DQS-DQ skew, DQS to last DQ valid, per group, per access
parameter tMRD = 12.0; // tMRD ns Load Mode Register command cycle time
parameter tRAP = 15.0; // tRAP ns ACTIVE to READ with Auto precharge command
parameter tRAS = 42.0; // tRAS ns Active to Precharge command time
parameter tRC = 60.0; // tRC ns Active to Active/Auto Refresh command time
parameter tRFC = 72.0; // tRFC ns Refresh to Refresh Command interval time
parameter tRCD = 15.0; // tRCD ns Active to Read/Write command time
parameter tRP = 15.0; // tRP ns Precharge command period
parameter tRRD = 12.0; // tRRD ns Active bank a to Active bank b command time
parameter tWR = 15.0; // tWR ns Write recovery time
`endif
`ifdef sg6 // Timing Parameters for -6 (CL = 2.5)
parameter tCK = 6.0; // tCK ns Nominal Clock Cycle Time
parameter tDQSQ = 0.4; // tDQSQ ns DQS-DQ skew, DQS to last DQ valid, per group, per access
parameter tMRD = 12.0; // tMRD ns Load Mode Register command cycle time
parameter tRAP = 15.0; // tRAP ns ACTIVE to READ with Auto precharge command
parameter tRAS = 42.0; // tRAS ns Active to Precharge command time
parameter tRC = 60.0; // tRC ns Active to Active/Auto Refresh command time
parameter tRFC = 72.0; // tRFC ns Refresh to Refresh Command interval time
parameter tRCD = 15.0; // tRCD ns Active to Read/Write command time
parameter tRP = 15.0; // tRP ns Precharge command period
parameter tRRD = 12.0; // tRRD ns Active bank a to Active bank b command time
parameter tWR = 15.0; // tWR ns Write recovery time
`endif
`ifdef sg75E // Timing Parameters for -75E (CL = 2)
parameter tCK = 7.5; // tCK ns Nominal Clock Cycle Time
parameter tDQSQ = 0.5; // tDQSQ ns DQS-DQ skew, DQS to last DQ valid, per group, per access
parameter tMRD = 15.0; // tMRD ns Load Mode Register command cycle time
parameter tRAP = 15.0; // tRAP ns ACTIVE to READ with Auto precharge command
parameter tRAS = 40.0; // tRAS ns Active to Precharge command time
parameter tRC = 60.0; // tRC ns Active to Active/Auto Refresh command time
parameter tRFC = 75.0; // tRFC ns Refresh to Refresh Command interval time
parameter tRCD = 15.0; // tRCD ns Active to Read/Write command time
parameter tRP = 15.0; // tRP ns Precharge command period
parameter tRRD = 15.0; // tRRD ns Active bank a to Active bank b command time
parameter tWR = 15.0; // tWR ns Write recovery time
`endif
`ifdef sg75Z // Timing Parameters for -75Z (CL = 2)
parameter tCK = 7.5; // tCK ns Nominal Clock Cycle Time
parameter tDQSQ = 0.5; // tDQSQ ns DQS-DQ skew, DQS to last DQ valid, per group, per access
parameter tMRD = 15.0; // tMRD ns Load Mode Register command cycle time
parameter tRAP = 20.0; // tRAP ns ACTIVE to READ with Auto precharge command
parameter tRAS = 40.0; // tRAS ns Active to Precharge command time
parameter tRC = 65.0; // tRC ns Active to Active/Auto Refresh command time
parameter tRFC = 75.0; // tRFC ns Refresh to Refresh Command interval time
parameter tRCD = 20.0; // tRCD ns Active to Read/Write command time
parameter tRP = 20.0; // tRP ns Precharge command period
parameter tRRD = 15.0; // tRRD ns Active bank a to Active bank b command time
parameter tWR = 15.0; // tWR ns Write recovery time
`endif
`ifdef sg75 // Timing Parameters for -75 (CL = 2.5)
parameter tCK = 7.5; // tCK ns Nominal Clock Cycle Time
parameter tDQSQ = 0.5; // tDQSQ ns DQS-DQ skew, DQS to last DQ valid, per group, per access
parameter tMRD = 15.0; // tMRD ns Load Mode Register command cycle time
parameter tRAP = 20.0; // tRAP ns ACTIVE to READ with Auto precharge command
parameter tRAS = 40.0; // tRAS ns Active to Precharge command time
parameter tRC = 65.0; // tRC ns Active to Active/Auto Refresh command time
parameter tRFC = 75.0; // tRFC ns Refresh to Refresh Command interval time
parameter tRCD = 20.0; // tRCD ns Active to Read/Write command time
parameter tRP = 20.0; // tRP ns Precharge command period
parameter tRRD = 15.0; // tRRD ns Active bank a to Active bank b command time
parameter tWR = 15.0; // tWR ns Write recovery time
`endif
// Size Parameters based on Part Width
`ifdef x4
parameter ADDR_BITS = 13; // Set this parameter to control how many Address bits are used
parameter DQ_BITS = 4; // Set this parameter to control how many Data bits are used
parameter DQS_BITS = 1; // Set this parameter to control how many DQS bits are used
parameter DM_BITS = 1; // Set this parameter to control how many DM bits are used
parameter COL_BITS = 11; // Set this parameter to control how many Column bits are used
`endif
`ifdef x8
parameter ADDR_BITS = 13; // Set this parameter to control how many Address bits are used
parameter DQ_BITS = 8; // Set this parameter to control how many Data bits are used
parameter DQS_BITS = 1; // Set this parameter to control how many DQS bits are used
parameter DM_BITS = 1; // Set this parameter to control how many DM bits are used
parameter COL_BITS = 10; // Set this parameter to control how many Column bits are used
`endif
`ifdef x16
parameter ADDR_BITS = 13; // Set this parameter to control how many Address bits are used
parameter DQ_BITS = 16; // Set this parameter to control how many Data bits are used
parameter DQS_BITS = 2; // Set this parameter to control how many DQS bits are used
parameter DM_BITS = 2; // Set this parameter to control how many DM bits are used
parameter COL_BITS = 9; // Set this parameter to control how many Column bits are used
`endif
parameter BA_BITS = 2; // Set this parmaeter to control how many Bank Address bits are used
parameter full_mem_bits = BA_BITS+ADDR_BITS+COL_BITS; // Set this parameter to control how many unique addresses are used
parameter part_mem_bits = 10; // Set this parameter to control how many unique addresses are used
parameter no_halt = 0; // If set to 1, the model won't halt on command sequence/major errors
parameter DEBUG = 1; // Turn on DEBUG message
`define FULL_MEM
@@ -0,0 +1,135 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 1995/2005 Xilinx, Inc.
// All Right Reserved.
///////////////////////////////////////////////////////////////////////////////
// Modified for HPDMC simulation, based on Xilinx 05/29/07 revision
///////////////////////////////////////////////////////////////////////////////
module IDDR #(
parameter DDR_CLK_EDGE = "OPPOSITE_EDGE",
parameter INIT_Q1 = 1'b0,
parameter INIT_Q2 = 1'b0,
parameter SRTYPE = "SYNC"
) (
output Q1,
output Q2,
input C,
input CE,
input D,
input R,
input S
);
reg q1_out = INIT_Q1, q2_out = INIT_Q2;
reg q1_out_int, q2_out_int;
reg q1_out_pipelined, q2_out_same_edge_int;
wire c_in;
wire ce_in;
wire d_in;
wire gsr_in;
wire r_in;
wire s_in;
buf buf_c(c_in, C);
buf buf_ce(ce_in, CE);
buf buf_d(d_in, D);
buf buf_q1(Q1, q1_out);
buf buf_q2(Q2, q2_out);
buf buf_r(r_in, R);
buf buf_s(s_in, S);
initial begin
if((INIT_Q1 != 0) && (INIT_Q1 != 1)) begin
$display("Attribute Syntax Error : The attribute INIT_Q1 on IDDR instance %m is set to %d. Legal values for this attribute are 0 or 1.", INIT_Q1);
$finish;
end
if((INIT_Q2 != 0) && (INIT_Q2 != 1)) begin
$display("Attribute Syntax Error : The attribute INIT_Q1 on IDDR instance %m is set to %d. Legal values for this attribute are 0 or 1.", INIT_Q2);
$finish;
end
if((DDR_CLK_EDGE != "OPPOSITE_EDGE") && (DDR_CLK_EDGE != "SAME_EDGE") && (DDR_CLK_EDGE != "SAME_EDGE_PIPELINED")) begin
$display("Attribute Syntax Error : The attribute DDR_CLK_EDGE on IDDR instance %m is set to %s. Legal values for this attribute are OPPOSITE_EDGE, SAME_EDGE or SAME_EDGE_PIPELINED.", DDR_CLK_EDGE);
$finish;
end
if((SRTYPE != "ASYNC") && (SRTYPE != "SYNC")) begin
$display("Attribute Syntax Error : The attribute SRTYPE on IDDR instance %m is set to %s. Legal values for this attribute are ASYNC or SYNC.", SRTYPE);
$finish;
end
end
always @(r_in, s_in) begin
if(r_in == 1'b1 && SRTYPE == "ASYNC") begin
assign q1_out_int = 1'b0;
assign q1_out_pipelined = 1'b0;
assign q2_out_same_edge_int = 1'b0;
assign q2_out_int = 1'b0;
end else if(r_in == 1'b0 && s_in == 1'b1 && SRTYPE == "ASYNC") begin
assign q1_out_int = 1'b1;
assign q1_out_pipelined = 1'b1;
assign q2_out_same_edge_int = 1'b1;
assign q2_out_int = 1'b1;
end else if((r_in == 1'b1 || s_in == 1'b1) && SRTYPE == "SYNC") begin
deassign q1_out_int;
deassign q1_out_pipelined;
deassign q2_out_same_edge_int;
deassign q2_out_int;
end else if(r_in == 1'b0 && s_in == 1'b0) begin
deassign q1_out_int;
deassign q1_out_pipelined;
deassign q2_out_same_edge_int;
deassign q2_out_int;
end
end
always @(posedge c_in) begin
if(r_in == 1'b1) begin
q1_out_int <= 1'b0;
q1_out_pipelined <= 1'b0;
q2_out_same_edge_int <= 1'b0;
end else if(r_in == 1'b0 && s_in == 1'b1) begin
q1_out_int <= 1'b1;
q1_out_pipelined <= 1'b1;
q2_out_same_edge_int <= 1'b1;
end else if(ce_in == 1'b1 && r_in == 1'b0 && s_in == 1'b0) begin
q1_out_int <= d_in;
q1_out_pipelined <= q1_out_int;
q2_out_same_edge_int <= q2_out_int;
end
end
always @(negedge c_in) begin
if(r_in == 1'b1)
q2_out_int <= 1'b0;
else if(r_in == 1'b0 && s_in == 1'b1)
q2_out_int <= 1'b1;
else if(ce_in == 1'b1 && r_in == 1'b0 && s_in == 1'b0)
q2_out_int <= d_in;
end
always @(c_in, q1_out_int, q2_out_int, q2_out_same_edge_int, q1_out_pipelined) begin
case(DDR_CLK_EDGE)
"OPPOSITE_EDGE" : begin
q1_out <= q1_out_int;
q2_out <= q2_out_int;
end
"SAME_EDGE" : begin
q1_out <= q1_out_int;
q2_out <= q2_out_same_edge_int;
end
"SAME_EDGE_PIPELINED" : begin
q1_out <= q1_out_pipelined;
q2_out <= q2_out_same_edge_int;
end
default: begin
$display("Attribute Syntax Error : The attribute DDR_CLK_EDGE on IDDR instance %m is set to %s. Legal values for this attribute are OPPOSITE_EDGE, SAME_EDGE or SAME_EDGE_PIPELINED.", DDR_CLK_EDGE);
$finish;
end
endcase
end
endmodule
@@ -0,0 +1,161 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 1995/2004 Xilinx, Inc.
// All Right Reserved.
///////////////////////////////////////////////////////////////////////////////
// Modified for HPDMC simulation, based on Xilinx 04/08/09 revision
///////////////////////////////////////////////////////////////////////////////
`timescale 1 ps / 1 ps
module IDDR2 (Q0, Q1, C0, C1, CE, D, R, S);
output Q0;
output Q1;
input C0;
input C1;
input CE;
input D;
input R;
input S;
parameter DDR_ALIGNMENT = "NONE";
parameter INIT_Q0 = 1'b0;
parameter INIT_Q1 = 1'b0;
parameter SRTYPE = "SYNC";
pullup P1 (CE);
pulldown P2 (R);
pulldown P3 (S);
reg q0_out, q1_out;
reg q0_out_int, q1_out_int;
reg q0_c0_out_int, q1_c0_out_int;
wire PC0, PC1;
buf buf_q0 (Q0, q0_out);
buf buf_q1 (Q1, q1_out);
initial begin
if ((INIT_Q0 != 1'b0) && (INIT_Q0 != 1'b1)) begin
$display("Attribute Syntax Error : The attribute INIT_Q0 on IDDR2 instance %m is set to %d. Legal values for this attribute are 0 or 1.", INIT_Q0);
$finish;
end
if ((INIT_Q1 != 1'b0) && (INIT_Q1 != 1'b1)) begin
$display("Attribute Syntax Error : The attribute INIT_Q0 on IDDR2 instance %m is set to %d. Legal values for this attribute are 0 or 1.", INIT_Q1);
$finish;
end
if ((DDR_ALIGNMENT != "C1") && (DDR_ALIGNMENT != "C0") && (DDR_ALIGNMENT != "NONE")) begin
$display("Attribute Syntax Error : The attribute DDR_ALIGNMENT on IDDR2 instance %m is set to %s. Legal values for this attribute are C0, C1 or NONE.", DDR_ALIGNMENT);
$finish;
end
if ((SRTYPE != "ASYNC") && (SRTYPE != "SYNC")) begin
$display("Attribute Syntax Error : The attribute SRTYPE on IDDR2 instance %m is set to %s. Legal values for this attribute are ASYNC or SYNC.", SRTYPE);
$finish;
end
end // initial begin
assign PC0 = ((DDR_ALIGNMENT== "C0") || (DDR_ALIGNMENT== "NONE"))? C0 : C1;
assign PC1 = ((DDR_ALIGNMENT== "C0") || (DDR_ALIGNMENT== "NONE"))? C1 : C0;
initial begin
assign q0_out_int = INIT_Q0;
assign q1_out_int = INIT_Q1;
assign q0_c0_out_int = INIT_Q0;
assign q1_c0_out_int = INIT_Q1;
end
always @(R or S) begin
deassign q0_out_int;
deassign q1_out_int;
deassign q0_c0_out_int;
deassign q1_c0_out_int;
if (SRTYPE == "ASYNC") begin
if (R == 1) begin
assign q0_out_int = 0;
assign q1_out_int = 0;
assign q0_c0_out_int = 0;
assign q1_c0_out_int = 0;
end
else if (R == 0 && S == 1) begin
assign q0_out_int = 1;
assign q1_out_int = 1;
end
end // if (SRTYPE == "ASYNC")
end // always @ (GSR or R or S)
always @(posedge PC0) begin
if (R == 1 && SRTYPE == "SYNC") begin
q0_out_int <= 0;
q0_c0_out_int <= 0;
q1_c0_out_int <= 0;
end
else if (R == 0 && S == 1 && SRTYPE == "SYNC") begin
q0_out_int <= 1;
end
else if (CE == 1 && R == 0 && S == 0) begin
q0_out_int <= D;
q0_c0_out_int <= q0_out_int;
q1_c0_out_int <= q1_out_int;
end
end // always @ (posedge PC0)
always @(posedge PC1) begin
if (R == 1 && SRTYPE == "SYNC") begin
q1_out_int <= 0;
end
else if (R == 0 && S == 1 && SRTYPE == "SYNC") begin
q1_out_int <= 1;
end
else if (CE == 1 && R == 0 && S == 0) begin
q1_out_int <= D;
end
end // always @ (posedge PC1)
always @(q0_out_int or q1_out_int or q1_c0_out_int or q0_c0_out_int) begin
case (DDR_ALIGNMENT)
"NONE" : begin
q0_out <= q0_out_int;
q1_out <= q1_out_int;
end
"C0" : begin
q0_out <= q0_out_int;
q1_out <= q1_c0_out_int;
end
"C1" : begin
q0_out <= q0_out_int;
q1_out <= q1_c0_out_int;
end
endcase // case(DDR_ALIGNMENT)
end // always @ (q0_out_int or q1_out_int or q1_c0_out_int or q0_c0_out_int)
specify
if (C0) (C0 => Q0) = (100, 100);
if (C0) (C0 => Q1) = (100, 100);
if (C1) (C1 => Q1) = (100, 100);
if (C1) (C1 => Q0) = (100, 100);
specparam PATHPULSE$ = 0;
endspecify
endmodule // IDDR2
@@ -0,0 +1,23 @@
/*
* Simplified IDELAY model.
* Only fixed delay type is implemented and assumed.
*/
`timescale 1ns / 1ps
module IDELAY #(
parameter IOBDELAY_TYPE = "DEFAULT",
parameter IOBDELAY_VALUE = 0
) (
input C,
input CE,
input I,
input INC,
input RST,
output reg O
);
always @(I)
# (IOBDELAY_VALUE*0.078) O = I;
endmodule
@@ -0,0 +1,74 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 1995/2004 Xilinx, Inc.
// All Right Reserved.
///////////////////////////////////////////////////////////////////////////////
// Modified for HPDMC simulation, based on Xilinx 04/22/09 revision
///////////////////////////////////////////////////////////////////////////////
`timescale 1 ps / 1 ps
module IOBUF (O, IO, I, T);
parameter CAPACITANCE = "DONT_CARE";
parameter integer DRIVE = 12;
parameter IBUF_DELAY_VALUE = "0";
parameter IBUF_LOW_PWR = "TRUE";
parameter IFD_DELAY_VALUE = "AUTO";
parameter IOSTANDARD = "DEFAULT";
parameter SLEW = "SLOW";
output O;
inout IO;
input I, T;
bufif0 T1 (IO, I, T);
buf B1 (O, IO);
initial begin
case (CAPACITANCE)
"LOW", "NORMAL", "DONT_CARE" : ;
default : begin
$display("Attribute Syntax Error : The attribute CAPACITANCE on IOBUF instance %m is set to %s. Legal values for this attribute are DONT_CARE, LOW or NORMAL.", CAPACITANCE);
$finish;
end
endcase
case (IBUF_DELAY_VALUE)
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16" : ;
default : begin
$display("Attribute Syntax Error : The attribute IBUF_DELAY_VALUE on IOBUF instance %m is set to %s. Legal values for this attribute are 0, 1, 2, ... or 16.", IBUF_DELAY_VALUE);
$finish;
end
endcase
case (IBUF_LOW_PWR)
"FALSE", "TRUE" : ;
default : begin
$display("Attribute Syntax Error : The attribute IBUF_LOW_PWR on IBUF instance %m is set to %s. Legal values for this attribute are TRUE or FALSE.", IBUF_LOW_PWR);
$finish;
end
endcase
case (IFD_DELAY_VALUE)
"AUTO", "0", "1", "2", "3", "4", "5", "6", "7", "8" : ;
default : begin
$display("Attribute Syntax Error : The attribute IFD_DELAY_VALUE on IOBUF instance %m is set to %s. Legal values for this attribute are AUTO, 0, 1, 2, ... or 8.", IFD_DELAY_VALUE);
$finish;
end
endcase
end // initial begin
endmodule
@@ -0,0 +1,38 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 1995/2004 Xilinx, Inc.
// All Right Reserved.
///////////////////////////////////////////////////////////////////////////////
// Modified for HPDMC simulation, based on Xilinx 05/23/07 revision
///////////////////////////////////////////////////////////////////////////////
`timescale 1 ps / 1 ps
module OBUFT (O, I, T);
parameter CAPACITANCE = "DONT_CARE";
parameter integer DRIVE = 12;
parameter IOSTANDARD = "DEFAULT";
parameter SLEW = "SLOW";
output O;
input I, T;
bufif0 T1 (O, I, T);
initial begin
case (CAPACITANCE)
"LOW", "NORMAL", "DONT_CARE" : ;
default : begin
$display("Attribute Syntax Error : The attribute CAPACITANCE on OBUFT instance %m is set to %s. Legal values for this attribute are DONT_CARE, LOW or NORMAL.", CAPACITANCE);
$finish;
end
endcase
end
endmodule
@@ -0,0 +1,99 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 1995/2005 Xilinx, Inc.
// All Right Reserved.
///////////////////////////////////////////////////////////////////////////////
// Modified for HPDMC simulation, based on Xilinx 05/29/07 revision
///////////////////////////////////////////////////////////////////////////////
module ODDR #(
parameter DDR_CLK_EDGE = "OPPOSITE_EDGE",
parameter INIT = 1'b0,
parameter SRTYPE = "SYNC"
) (
output Q,
input C,
input CE,
input D1,
input D2,
input R,
input S
);
reg q_out = INIT, qd2_posedge_int;
wire c_in;
wire ce_in;
wire d1_in;
wire d2_in;
wire gsr_in;
wire r_in;
wire s_in;
buf buf_c(c_in, C);
buf buf_ce(ce_in, CE);
buf buf_d1(d1_in, D1);
buf buf_d2(d2_in, D2);
buf buf_q(Q, q_out);
buf buf_r(r_in, R);
buf buf_s(s_in, S);
initial begin
if((INIT != 0) && (INIT != 1)) begin
$display("Attribute Syntax Error : The attribute INIT on ODDR instance %m is set to %d. Legal values for this attribute are 0 or 1.", INIT);
$finish;
end
if((DDR_CLK_EDGE != "OPPOSITE_EDGE") && (DDR_CLK_EDGE != "SAME_EDGE")) begin
$display("Attribute Syntax Error : The attribute DDR_CLK_EDGE on ODDR instance %m is set to %s. Legal values for this attribute are OPPOSITE_EDGE or SAME_EDGE.", DDR_CLK_EDGE);
$finish;
end
if((SRTYPE != "ASYNC") && (SRTYPE != "SYNC")) begin
$display("Attribute Syntax Error : The attribute SRTYPE on ODDR instance %m is set to %s. Legal values for this attribute are ASYNC or SYNC.", SRTYPE);
$finish;
end
end
always @(r_in, s_in) begin
if(r_in == 1'b1 && SRTYPE == "ASYNC") begin
assign q_out = 1'b0;
assign qd2_posedge_int = 1'b0;
end else if(r_in == 1'b0 && s_in == 1'b1 && SRTYPE == "ASYNC") begin
assign q_out = 1'b1;
assign qd2_posedge_int = 1'b1;
end else if((r_in == 1'b1 || s_in == 1'b1) && SRTYPE == "SYNC") begin
deassign q_out;
deassign qd2_posedge_int;
end else if(r_in == 1'b0 && s_in == 1'b0) begin
deassign q_out;
deassign qd2_posedge_int;
end
end
always @(posedge c_in) begin
if(r_in == 1'b1) begin
q_out <= 1'b0;
qd2_posedge_int <= 1'b0;
end else if(r_in == 1'b0 && s_in == 1'b1) begin
q_out <= 1'b1;
qd2_posedge_int <= 1'b1;
end else if(ce_in == 1'b1 && r_in == 1'b0 && s_in == 1'b0) begin
q_out <= d1_in;
qd2_posedge_int <= d2_in;
end
end
always @(negedge c_in) begin
if(r_in == 1'b1)
q_out <= 1'b0;
else if(r_in == 1'b0 && s_in == 1'b1)
q_out <= 1'b1;
else if(ce_in == 1'b1 && r_in == 1'b0 && s_in == 1'b0) begin
if(DDR_CLK_EDGE == "SAME_EDGE")
q_out <= qd2_posedge_int;
else if(DDR_CLK_EDGE == "OPPOSITE_EDGE")
q_out <= d2_in;
end
end
endmodule
@@ -0,0 +1,131 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 1995/2004 Xilinx, Inc.
// All Right Reserved.
///////////////////////////////////////////////////////////////////////////////
// Modified for HPDMC simulation, based on Xilinx 01/12/09 revision
///////////////////////////////////////////////////////////////////////////////
`timescale 1 ps / 1 ps
module ODDR2 (Q, C0, C1, CE, D0, D1, R, S);
output Q;
input C0;
input C1;
input CE;
input D0;
input D1;
input R;
input S;
parameter DDR_ALIGNMENT = "NONE";
parameter INIT = 1'b0;
parameter SRTYPE = "SYNC";
pullup P1 (CE);
pulldown P2 (R);
pulldown P3 (S);
reg q_out, q_d1_c0_out_int;
wire PC0, PC1;
buf buf_q (Q, q_out);
initial begin
if ((INIT != 1'b0) && (INIT != 1'b1)) begin
$display("Attribute Syntax Error : The attribute INIT on ODDR2 instance %m is set to %d. Legal values for this attribute are 0 or 1.", INIT);
$finish;
end
if ((DDR_ALIGNMENT != "NONE") && (DDR_ALIGNMENT != "C0") && (DDR_ALIGNMENT != "C1")) begin
$display("Attribute Syntax Error : The attribute DDR_ALIGNMENT on ODDR2 instance %m is set to %s. Legal values for this attribute are NONE, C0 or C1.", DDR_ALIGNMENT);
$finish;
end
if ((SRTYPE != "ASYNC") && (SRTYPE != "SYNC")) begin
$display("Attribute Syntax Error : The attribute SRTYPE on ODDR2 instance %m is set to %s. Legal values for this attribute are ASYNC or SYNC.", SRTYPE);
$finish;
end
end // initial begin
initial begin
assign q_out = INIT;
assign q_d1_c0_out_int = INIT;
end
always @(R or S) begin
deassign q_out;
deassign q_d1_c0_out_int;
if (SRTYPE == "ASYNC") begin
if (R == 1) begin
assign q_out = 0;
assign q_d1_c0_out_int = 0;
end
else if (R == 0 && S == 1) begin
assign q_out = 1;
assign q_d1_c0_out_int = 1;
end
end // if (SRTYPE == "ASYNC")
end // always @ (GSR or R or S)
assign PC0 = ((DDR_ALIGNMENT== "C0") || (DDR_ALIGNMENT== "NONE"))? C0 : C1;
assign PC1 = ((DDR_ALIGNMENT== "C0") || (DDR_ALIGNMENT== "NONE"))? C1 : C0;
always @(posedge PC0) begin
if (R == 1 && SRTYPE == "SYNC") begin
q_out <= 0;
q_d1_c0_out_int <= 0;
end
else if (R == 0 && S == 1 && SRTYPE == "SYNC") begin
q_out <= 1;
q_d1_c0_out_int <= 1;
end
else if (CE == 1 && R == 0 && S == 0) begin
q_out <= D0;
q_d1_c0_out_int <= D1 ;
end // if (CE == 1 && R == 0 && S == 0)
end // always @ (posedge C0)
always @(posedge PC1) begin
if (R == 1 && SRTYPE == "SYNC") begin
q_out <= 0;
end
else if (R == 0 && S == 1 && SRTYPE == "SYNC") begin
q_out <= 1;
end
else if (CE == 1 && R == 0 && S == 0) begin
if (DDR_ALIGNMENT == "NONE")
q_out <= D1;
else
q_out <= q_d1_c0_out_int;
end // if (CE == 1 && R == 0 && S == 0)
end // always @ (negedge c_in)
specify
if (C0) (C0 => Q) = (100, 100);
if (C1) (C1 => Q) = (100, 100);
specparam PATHPULSE$ = 0;
endspecify
endmodule // ODDR2
@@ -0,0 +1,236 @@
initial begin : test
cke <= 1'b0;
cs_n <= 1'b1;
ras_n <= 1'b1;
cas_n <= 1'b1;
we_n <= 1'b1;
ba <= {BA_BITS{1'bz}};
a <= {ADDR_BITS{1'bz}};
dq_en <= 1'b0;
dqs_en <= 1'b0;
cke <= 1'b1;
power_up;
$display("Powerup complete");
precharge('h00000000, 1);
nop(trp);
load_mode('h1, 'h00002000);
nop(tmrd-1);
load_mode('h0, 'h0000013A);
nop(tmrd-1);
precharge('h00000000, 1);
nop(trp);
refresh;
nop(trfc);
refresh;
nop(trfc);
load_mode('h0, 'h0000003A);
nop(tmrd-1);
nop('h000000C8);
activate('h00000000, 'h00000000);
nop(trcd-1);
write('h00000000, 'h00000000, 1, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'h30003000, 32'h20002000, 32'h10001000, 32'h0});
nop(BL/2+twr);
activate('h00000001, 'h00000000);
nop(trcd-1);
write('h00000001, 'h00000000, 1, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'h30013001, 32'h20012001, 32'h10011001, 32'h10001});
nop(BL/2+twr);
activate('h00000002, 'h00000000);
nop(trcd-1);
write('h00000002, 'h00000000, 1, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'h30023002, 32'h20022002, 32'h10021002, 32'h20002});
nop(BL/2+twr);
activate('h00000003, 'h00000000);
nop(trcd-1);
write('h00000003, 'h00000000, 1, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'h30033003, 32'h20032003, 32'h10031003, 32'h30003});
nop(BL/2+twr);
activate('h00000000, 'h00000000);
nop(trrd-1);
activate('h00000001, 'h00000000);
nop(trrd-1);
activate('h00000002, 'h00000000);
nop(trrd-1);
activate('h00000003, 'h00000000);
read('h00000000, 'h00000000, 1);
nop(BL/2-1);
read('h00000001, 'h00000000, 1);
nop(BL/2-1);
read('h00000002, 'h00000000, 1);
nop(BL/2-1);
read('h00000003, 'h00000000, 1);
nop(BL/2+twr-2);
activate('h00000001, 'h00000000);
nop(trrd-1);
activate('h00000000, 'h00000000);
nop(trcd-1);
$display("%m At time %t: WRITE Burst", $time);write('h00000000, 'h00000004, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'h30403040, 32'h20402040, 32'h10401040, 32'h400040});
nop(BL/2+4);
$display("%m At time %t: Consecutive WRITE to WRITE", $time);write('h00000000, 'h00000008, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'h30803080, 32'h20802080, 32'h10801080, 32'h800080});
nop(BL/2-1);
write('h00000000, 'h0000000C, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'h31203120, 32'h21202120, 32'h11201120, 32'h1200120});
nop(BL/2-1);
$display("%m At time %t: Nonconsecutive WRITE to WRITE", $time);write('h00000000, 'h00000010, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'h31603160, 32'h21602160, 32'h11601160, 32'h1600160});
nop(BL/2+4);
write('h00000000, 'h00000014, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'h32003200, 32'h22002200, 32'h12001200, 32'h2000200});
nop(BL/2+twr+4);
$display("%m At time %t: Random WRITE Cycles", $time);write('h00000000, 'h00000018, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'h32403240, 32'h22402240, 32'h12401240, 32'h2400240});
nop(BL/2-1);
write('h00000000, 'h0000001C, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'h32803280, 32'h22802280, 32'h12801280, 32'h2800280});
nop(BL/2-1);
write('h00000000, 'h00000020, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'h33203320, 32'h23202320, 32'h13201320, 32'h3200320});
nop(BL/2-1);
write('h00000000, 'h00000024, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'h33603360, 32'h23602360, 32'h13601360, 32'h3600360});
nop(BL/2-1);
$display("%m At time %t: WRITE to READ - Uninterrupting", $time);write('h00000000, 'h00000028, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'h34003400, 32'h24002400, 32'h14001400, 32'h4000400});
nop(BL/2+1);
read('h00000000, 'h00000028, 0);
nop(CL+BL/2-1);
$display("%m At time %t: WRITE to READ - Interrupting", $time);write('h00000000, 'h0000002C, 0, { 4'h1, 4'h1, 4'h0, 4'h0}, { 32'h34403440, 32'h24402440, 32'h14401440, 32'h4400440});
nop(BL/2+1);
read('h00000000, 'h0000002C, 0);
nop(CL+BL/2-1);
$display("%m At time %t: WRITE to READ - Odd Number of Data, Interrupting", $time);write('h00000000, 'h00000030, 0, { 4'h1, 4'h1, 4'h1, 4'h0}, { 32'h34803480, 32'h24802480, 32'h14801480, 32'h4800480});
nop(BL/2+1);
read('h00000000, 'h00000030, 0);
nop(CL+BL/2-1);
$display("%m At time %t: WRITE to PRECHARGE - Uninterrupting", $time);write('h00000000, 'h00000034, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'h35203520, 32'h25202520, 32'h15201520, 32'h5200520});
nop(BL/2+twr);
precharge('h00000000, 0);
nop(trp-1);
$display("%m At time %t: WRITE with AUTO PRECHARGE", $time);activate('h00000000, 'h00000000);
nop(trcd-1);
write('h00000000, 'h00000040, 1, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'h36603660, 32'h26602660, 32'h16601660, 32'h6600660});
nop(BL/2+twr+trp);
activate('h00000000, 'h00000000);
nop(trcd-1);
$display("%m At time %t: READ Burst", $time);read('h00000000, 'h00000000, 0);
nop(BL/2-1);
$display("%m At time %t: Consecutive READ Bursts", $time);read('h00000000, 'h00000004, 0);
nop(BL/2-2);
read('h00000000, 'h00000008, 0);
nop(BL/2-1);
$display("%m At time %t: Nonconsecutive READ Bursts", $time);read('h00000000, 'h0000000C, 0);
nop(BL/2);
read('h00000000, 'h00000010, 0);
nop(BL/2);
$display("%m At time %t: Random READ Accesses", $time);read('h00000000, 'h00000014, 0);
read('h00000000, 'h00000018, 0);
read('h00000000, 'h0000001C, 0);
read('h00000000, 'h00000020, 0);
nop(BL/2);
$display("%m At time %t: Terminating a READ Burst", $time);read('h00000000, 'h00000024, 0);
burst_term;
nop(BL/2-2);
$display("%m At time %t: READ to WRITE", $time);read('h00000000, 'h00000028, 0);
burst_term;
nop(CL);
write('h00000000, 'h0000002C, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'h34C034C0, 32'h24C024C0, 32'h14C014C0, 32'h4C004C0});
nop(BL/2+1);
$display("%m At time %t: READ to PRECHARGE", $time);read('h00000000, 'h00000030, 0);
nop('h00000001);
precharge('h00000000, 0);
nop(trp-1);
$display("%m At time %t: READ with AUTO PRECHARGE", $time);activate('h00000000, 'h00000000);
nop(trcd-1);
read('h00000000, 'h00000034, 1);
nop(CL+BL/2+twr);
$display("%m At time %t: WRITE to READ - Mask byte 0 of Burst 0", $time);activate('h00000000, 'h00000000);
nop(trcd-1);
write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF});
nop(BL/2);
write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h0, 4'h1}, { 32'h33333333, 32'h22222222, 32'h11111111, 32'h0});
nop(BL/2+1);
read('h00000000, 'h00000064, 0);
nop(CL+BL/2-1);
$display("%m At time %t: WRITE to READ - Mask byte 1 of Burst 0", $time);write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF});
nop(BL/2);
write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h0, 4'h2}, { 32'h33333333, 32'h22222222, 32'h11111111, 32'h0});
nop(BL/2+1);
read('h00000000, 'h00000064, 0);
nop(CL+BL/2-1);
$display("%m At time %t: WRITE to READ - Mask byte 2 of Burst 0", $time);write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF});
nop(BL/2);
write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h0, 4'h4}, { 32'h33333333, 32'h22222222, 32'h11111111, 32'h0});
nop(BL/2+1);
read('h00000000, 'h00000064, 0);
nop(CL+BL/2-1);
$display("%m At time %t: WRITE to READ - Mask byte 3 of Burst 0", $time);write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF});
nop(BL/2);
write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h0, 4'h8}, { 32'h33333333, 32'h22222222, 32'h11111111, 32'h0});
nop(BL/2+1);
read('h00000000, 'h00000064, 0);
nop(CL+BL/2-1);
$display("%m At time %t: WRITE to READ - Mask byte 0 of Burst 1", $time);write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF});
nop(BL/2);
write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h1, 4'h0}, { 32'h33333333, 32'h22222222, 32'h11111111, 32'h0});
nop(BL/2+1);
read('h00000000, 'h00000064, 0);
nop(CL+BL/2-1);
$display("%m At time %t: WRITE to READ - Mask byte 1 of Burst 1", $time);write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF});
nop(BL/2);
write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h2, 4'h0}, { 32'h33333333, 32'h22222222, 32'h11111111, 32'h0});
nop(BL/2+1);
read('h00000000, 'h00000064, 0);
nop(CL+BL/2-1);
$display("%m At time %t: WRITE to READ - Mask byte 2 of Burst 1", $time);write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF});
nop(BL/2);
write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h4, 4'h0}, { 32'h33333333, 32'h22222222, 32'h11111111, 32'h0});
nop(BL/2+1);
read('h00000000, 'h00000064, 0);
nop(CL+BL/2-1);
$display("%m At time %t: WRITE to READ - Mask byte 3 of Burst 1", $time);write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF});
nop(BL/2);
write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h8, 4'h0}, { 32'h33333333, 32'h22222222, 32'h11111111, 32'h0});
nop(BL/2+1);
read('h00000000, 'h00000064, 0);
nop(CL+BL/2-1);
$display("%m At time %t: WRITE to READ - Mask byte 0 of Burst 2", $time);write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF});
nop(BL/2);
write('h00000000, 'h00000064, 0, { 4'h0, 4'h1, 4'h0, 4'h0}, { 32'h33333333, 32'h22222222, 32'h11111111, 32'h0});
nop(BL/2+1);
read('h00000000, 'h00000064, 0);
nop(CL+BL/2-1);
$display("%m At time %t: WRITE to READ - Mask byte 1 of Burst 2", $time);write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF});
nop(BL/2);
write('h00000000, 'h00000064, 0, { 4'h0, 4'h2, 4'h0, 4'h0}, { 32'h33333333, 32'h22222222, 32'h11111111, 32'h0});
nop(BL/2+1);
read('h00000000, 'h00000064, 0);
nop(CL+BL/2-1);
$display("%m At time %t: WRITE to READ - Mask byte 2 of Burst 2", $time);write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF});
nop(BL/2);
write('h00000000, 'h00000064, 0, { 4'h0, 4'h4, 4'h0, 4'h0}, { 32'h33333333, 32'h22222222, 32'h11111111, 32'h0});
nop(BL/2+1);
read('h00000000, 'h00000064, 0);
nop(CL+BL/2-1);
$display("%m At time %t: WRITE to READ - Mask byte 3 of Burst 2", $time);write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF});
nop(BL/2);
write('h00000000, 'h00000064, 0, { 4'h0, 4'h8, 4'h0, 4'h0}, { 32'h33333333, 32'h22222222, 32'h11111111, 32'h0});
nop(BL/2+1);
read('h00000000, 'h00000064, 0);
nop(CL+BL/2-1);
$display("%m At time %t: WRITE to READ - Mask byte 0 of Burst 3", $time);write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF});
nop(BL/2);
write('h00000000, 'h00000064, 0, { 4'h1, 4'h0, 4'h0, 4'h0}, { 32'h33333333, 32'h22222222, 32'h11111111, 32'h0});
nop(BL/2+1);
read('h00000000, 'h00000064, 0);
nop(CL+BL/2-1);
$display("%m At time %t: WRITE to READ - Mask byte 1 of Burst 3", $time);write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF});
nop(BL/2);
write('h00000000, 'h00000064, 0, { 4'h2, 4'h0, 4'h0, 4'h0}, { 32'h33333333, 32'h22222222, 32'h11111111, 32'h0});
nop(BL/2+1);
read('h00000000, 'h00000064, 0);
nop(CL+BL/2-1);
$display("%m At time %t: WRITE to READ - Mask byte 2 of Burst 3", $time);write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF});
nop(BL/2);
write('h00000000, 'h00000064, 0, { 4'h4, 4'h0, 4'h0, 4'h0}, { 32'h33333333, 32'h22222222, 32'h11111111, 32'h0});
nop(BL/2+1);
read('h00000000, 'h00000064, 0);
nop(CL+BL/2-1);
$display("%m At time %t: WRITE to READ - Mask byte 3 of Burst 3", $time);write('h00000000, 'h00000064, 0, { 4'h0, 4'h0, 4'h0, 4'h0}, { 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF, 32'hFFFFFFFF});
nop(BL/2);
write('h00000000, 'h00000064, 0, { 4'h8, 4'h0, 4'h0, 4'h0}, { 32'h33333333, 32'h22222222, 32'h11111111, 32'h0});
nop(BL/2+1);
read('h00000000, 'h00000064, 1);
nop(CL+BL/2-1);
test_done = 1;
end
@@ -0,0 +1,390 @@
/*
* Milkymist VJ SoC
* Copyright (C) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq
*
* This program 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, version 3 of the License.
*
* This program 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/>.
*/
`timescale 1ns / 1ps
//`define ENABLE_VCD
`define TEST_SOMETRANSFERS
//`define TEST_RANDOMTRANSFERS
module tb_hpdmc();
/* 100MHz system clock */
reg clk;
initial clk = 1'b0;
always #5 clk = ~clk;
/* DQS clock is phased out by 90 degrees, resulting in 2.5ns delay */
reg dqs_clk;
always @(clk) #2.5 dqs_clk = clk;
wire sdram_cke;
wire sdram_cs_n;
wire sdram_we_n;
wire sdram_cas_n;
wire sdram_ras_n;
wire [3:0] sdram_dm;
wire [12:0] sdram_adr;
wire [1:0] sdram_ba;
wire [31:0] sdram_dq;
wire [3:0] sdram_dqs;
ddr sdram1(
.Addr(sdram_adr),
.Ba(sdram_ba),
.Clk(clk),
.Clk_n(~clk),
.Cke(sdram_cke),
.Cs_n(sdram_cs_n),
.Ras_n(sdram_ras_n),
.Cas_n(sdram_cas_n),
.We_n(sdram_we_n),
.Dm(sdram_dm[3:2]),
.Dqs(sdram_dqs[3:2]),
.Dq(sdram_dq[31:16])
);
ddr sdram0(
.Addr(sdram_adr),
.Ba(sdram_ba),
.Clk(clk),
.Clk_n(~clk),
.Cke(sdram_cke),
.Cs_n(sdram_cs_n),
.Ras_n(sdram_ras_n),
.Cas_n(sdram_cas_n),
.We_n(sdram_we_n),
.Dm(sdram_dm[1:0]),
.Dqs(sdram_dqs[1:0]),
.Dq(sdram_dq[15:0])
);
reg rst;
reg [13:0] csr_a;
reg csr_we;
reg [31:0] csr_di;
wire [31:0] csr_do;
reg [25:0] fml_adr;
reg fml_stb;
reg fml_we;
wire fml_ack;
reg [7:0] fml_sel;
reg [63:0] fml_di;
wire [63:0] fml_do;
hpdmc dut(
.sys_clk(clk),
.sys_clk_n(~clk),
.dqs_clk(dqs_clk),
.dqs_clk_n(~dqs_clk),
.sys_rst(rst),
.csr_a(csr_a),
.csr_we(csr_we),
.csr_di(csr_di),
.csr_do(csr_do),
.fml_adr(fml_adr),
.fml_stb(fml_stb),
.fml_we(fml_we),
.fml_ack(fml_ack),
.fml_sel(fml_sel),
.fml_di(fml_di),
.fml_do(fml_do),
.sdram_cke(sdram_cke),
.sdram_cs_n(sdram_cs_n),
.sdram_we_n(sdram_we_n),
.sdram_cas_n(sdram_cas_n),
.sdram_ras_n(sdram_ras_n),
.sdram_dm(sdram_dm),
.sdram_adr(sdram_adr),
.sdram_ba(sdram_ba),
.sdram_dq(sdram_dq),
.sdram_dqs(sdram_dqs),
.dqs_psen(),
.dqs_psincdec(),
.dqs_psdone(1'b1)
);
task waitclock;
begin
@(posedge clk);
#1;
end
endtask
task waitnclock;
input [15:0] n;
integer i;
begin
for(i=0;i<n;i=i+1)
waitclock;
end
endtask
task csrwrite;
input [31:0] address;
input [31:0] data;
begin
csr_a = address[16:2];
csr_di = data;
csr_we = 1'b1;
waitclock;
$display("Configuration Write: %x=%x", address, data);
csr_we = 1'b0;
end
endtask
task csrread;
input [31:0] address;
begin
csr_a = address[16:2];
waitclock;
$display("Configuration Read : %x=%x", address, csr_do);
end
endtask
real reads;
real read_clocks;
task readburst;
input [31:0] address;
integer i;
begin
$display("READ [%x]", address);
fml_adr = address;
fml_stb = 1'b1;
fml_we = 1'b0;
i = 0;
while(~fml_ack) begin
i = i+1;
waitclock;
end
$display("%t: Memory Read : %x=%x acked in %d clocks", $time, address, fml_do, i);
fml_stb = 1'b0;
reads = reads + 1;
read_clocks = read_clocks + i;
for(i=0;i<3;i=i+1) begin
waitclock;
$display("%t: (R burst continuing) %x", $time, fml_do);
end
waitclock;
end
endtask
real writes;
real write_clocks;
task writeburst;
input [31:0] address;
integer i;
begin
$display("WRITE [%x]", address);
fml_adr = address;
fml_stb = 1'b1;
fml_we = 1'b1;
fml_sel = 8'hff;
fml_di = {$random, $random};
i = 0;
while(~fml_ack) begin
i = i+1;
waitclock;
end
$display("%t: Memory Write : %x=%x acked in %d clocks", $time, address, fml_di, i);
fml_stb = 1'b0;
writes = writes + 1;
write_clocks = write_clocks + i;
for(i=0;i<3;i=i+1) begin
waitclock;
fml_di = {$random, $random};
$display("%t: (W burst continuing) %x", $time, fml_di);
end
waitclock;
end
endtask
integer n, addr;
always begin
`ifdef ENABLE_VCD
$dumpfile("hpdmc.vcd");
`endif
/* Reset / Initialize our logic */
rst = 1'b1;
csr_a = 14'd0;
csr_di = 32'd0;
csr_we = 1'b0;
fml_adr = 26'd0;
fml_di = 64'd0;
fml_sel = 8'd0;
fml_stb = 1'b0;
fml_we = 1'b0;
waitclock;
rst = 1'b0;
waitclock;
/* SDRAM initialization sequence. */
/* The controller already comes up in Bypass mode with CKE disabled. */
/* Wait 200us */
#200000;
/* Bring CKE high */
csrwrite(32'h00, 32'h07);
/* Precharge All:
* CS=1
* WE=1
* CAS=0
* RAS=1
* A=A10
* BA=Don't Care
*/
csrwrite(32'h04, 32'b00_0010000000000_1011);
waitnclock(2);
/* Load Extended Mode Register:
* CS=1
* WE=1
* CAS=1
* RAS=1
* A=Value
* BA=01
*
* Extended mode register encoding :
* A12-A2 reserved, must be 0
* A1 weak drive strength
* A0 DLL disable
*/
csrwrite(32'h04, 32'b01_0000000000000_1111);
waitnclock(2);
/* Load Mode Register, DLL in Reset:
* CS=1
* WE=1
* CAS=1
* RAS=1
* A=Value
* BA=00
*
* Mode register encoding :
* A12-A7 = 000000 Normal operation w/o DLL reset
* 000010 Normal operation in DLL reset
* A6-A4 = 010 CL2
* A3 = 0 Sequential burst
* A2-A0 = 011 Burst length = 8
*/
csrwrite(32'h04, 32'b00__000010_010_0_011__1111);
waitnclock(200);
/* Precharge All */
csrwrite(32'h04, 32'b00_0010000000000_1011);
waitnclock(2);
/* Auto Refresh
* CS=1
* WE=0
* CAS=1
* RAS=1
* A=Don't Care
* BA=Don't Care
*/
csrwrite(32'h04, 32'b00_0000000000000_1101);
waitnclock(8);
/* Auto Refresh */
csrwrite(32'h04, 32'b00_0000000000000_1101);
waitnclock(8);
/* Load Mode Register, DLL enabled */
csrwrite(32'h04, 32'b00__000000_010_0_011__1111);
waitnclock(200);
/* SDRAM initialization completed */
`ifdef ENABLE_VCD
/* Now, we want to know what the controller will send to the SDRAM chips */
$dumpvars(0, dut);
`endif
/* Bring up the controller ! */
csrwrite(32'h00, 32'h04);
`ifdef TEST_SOMETRANSFERS
/*
* Try some transfers.
*/
writeburst(32'h00);
writeburst(32'h20);
//writeburst(32'h40);
readburst(32'h00);
readburst(32'h20);
/*readburst(32'h40);
writeburst(32'h40);
readburst(32'h40);*/
`endif
`ifdef TEST_RANDOMTRANSFERS
writes = 0;
write_clocks = 0;
reads = 0;
read_clocks = 0;
for(n=0;n<500;n=n+1) begin
addr = $random;
if($random > 32'h80000000) begin
writeburst(addr);
//writeburst(addr+32'h20);
//writeburst(addr+32'h40);
end else begin
readburst(addr);
//readburst(addr+32'h20);
//readburst(addr+32'h40);
end
end
$display("");
$display("=======================================================");
$display(" Tested: %.0f reads, %.0f writes ", reads, writes);
$display("=======================================================");
$display(" Average read latency: %f cycles", read_clocks/reads);
$display(" Average write latency: %f cycles", write_clocks/writes);
$display("=======================================================");
$display(" Average read bandwidth: %f MBit/s @ 100MHz", (4/(4+read_clocks/reads))*64*100);
$display(" Average write bandwidth: %f MBit/s @ 100MHz", (4/(4+write_clocks/writes))*64*100);
$display("=======================================================");
`endif
$finish;
end
endmodule
@@ -0,0 +1,556 @@
/****************************************************************************************
*
* File Name: tb.v
* Version: 5.7
* Model: BUS Functional
*
* Dependencies: ddr.v, ddr_parameters.v
*
* Description: Micron SDRAM DDR (Double Data Rate) test bench
*
* Note: - Set simulator resolution to "ps" accuracy
* - Set Debug = 0 to disable $display messages
*
* Disclaimer This software code and all associated documentation, comments or other
* of Warranty: information (collectively "Software") is provided "AS IS" without
* warranty of any kind. MICRON TECHNOLOGY, INC. ("MTI") EXPRESSLY
* DISCLAIMS ALL WARRANTIES EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO, NONINFRINGEMENT OF THIRD PARTY RIGHTS, AND ANY IMPLIED WARRANTIES
* OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE. MTI DOES NOT
* WARRANT THAT THE SOFTWARE WILL MEET YOUR REQUIREMENTS, OR THAT THE
* OPERATION OF THE SOFTWARE WILL BE UNINTERRUPTED OR ERROR-FREE.
* FURTHERMORE, MTI DOES NOT MAKE ANY REPRESENTATIONS REGARDING THE USE OR
* THE RESULTS OF THE USE OF THE SOFTWARE IN TERMS OF ITS CORRECTNESS,
* ACCURACY, RELIABILITY, OR OTHERWISE. THE ENTIRE RISK ARISING OUT OF USE
* OR PERFORMANCE OF THE SOFTWARE REMAINS WITH YOU. IN NO EVENT SHALL MTI,
* ITS AFFILIATED COMPANIES OR THEIR SUPPLIERS BE LIABLE FOR ANY DIRECT,
* INDIRECT, CONSEQUENTIAL, INCIDENTAL, OR SPECIAL DAMAGES (INCLUDING,
* WITHOUT LIMITATION, DAMAGES FOR LOSS OF PROFITS, BUSINESS INTERRUPTION,
* OR LOSS OF INFORMATION) ARISING OUT OF YOUR USE OF OR INABILITY TO USE
* THE SOFTWARE, EVEN IF MTI HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGES. Because some jurisdictions prohibit the exclusion or
* limitation of liability for consequential or incidental damages, the
* above limitation may not apply to you.
*
* Copyright 2003 Micron Technology, Inc. All rights reserved.
*
* Rev Author Date Changes
* --------------------------------------------------------------------------------
* 2.1 SPH 03/19/2002 - Second Release
* - Fix tWR and several incompatability
* between different simulators
* 3.0 TFK 02/18/2003 - Added tDSS and tDSH timing checks.
* - Added tDQSH and tDQSL timing checks.
* 3.1 CAH 05/28/2003 - update all models to release version 3.1
* (no changes to this model)
* 3.2 JMK 06/16/2003 - updated all DDR400 models to support CAS Latency 3
* 3.3 JMK 09/11/2003 - Added initialization sequence checks.
* 4.0 JMK 12/01/2003 - Grouped parameters into "ddr_parameters.v"
* - Fixed tWTR check
* 4.1 JMK 01/14/2001 - Grouped specify parameters by speed grade
* - Fixed mem_sizes parameter
* 4.2 JMK 03/19/2004 - Fixed pulse width checking on Dqs
* 4.3 JMK 04/27/2004 - Changed BL wire size in tb module
* - Changed Dq_buf size to [15:0]
* 5.0 JMK 06/16/2004 - Added read to write checking.
* - Added read with precharge truncation to write checking.
* - Added associative memory array to reduce memory consumption.
* - Added checking for required DQS edges during write.
* 5.1 JMK 08/16/2004 - Fixed checking for required DQS edges during write.
* - Fixed wdqs_valid window.
* 5.2 JMK 09/24/2004 - Read or Write without activate will be ignored.
* 5.3 JMK 10/27/2004 - Added tMRD checking during Auto Refresh and Activate.
* - Added tRFC checking during Load Mode and Precharge.
* 5.4 JMK 12/13/2004 - The model will not respond to illegal command sequences.
* 5.5 SPH 01/13/2005 - The model will issue a halt on illegal command sequences.
* JMK 02/11/2005 - Changed the display format for numbers to hex.
* 5.6 JMK 04/22/2005 - Fixed Write with auto precharge calculation.
* 5.7 JMK 08/05/2005 - Changed conditions for read with precharge truncation error.
* - Renamed parameters file with .vh extension.
* 5.8 BAS 12/26/2006 - Added parameters for T46A part - 256Mb
* - Added x32 functionality
* 6.0 BAS 05/31/2007 - Added read_verify command
****************************************************************************************/
`timescale 1ns / 1ps
module tb;
`include "ddr_parameters.vh"
reg clk ;
reg clk_n ;
reg cke ;
reg cs_n ;
reg ras_n ;
reg cas_n ;
reg we_n ;
reg [BA_BITS - 1 : 0] ba ;
reg [ADDR_BITS - 1 : 0] a ;
reg dq_en ;
reg [DM_BITS - 1 : 0] dm_out ;
reg [DQ_BITS - 1 : 0] dq_out ;
reg [DM_BITS-1 : 0] dm_fifo [0 : 13];
reg [DQ_BITS-1 : 0] dq_fifo [0 : 13];
reg [DQ_BITS-1 : 0] dq_in_pos ;
reg [DQ_BITS-1 : 0] dq_in_neg ;
reg dqs_en ;
reg [DQS_BITS - 1 : 0] dqs_out ;
reg [12 : 0] mode_reg ; //Mode Register
reg [12 : 0] ext_mode_reg; //Extended Mode Register
wire BO = mode_reg[3]; //Burst Order
wire [7 : 0] BL = (1<<mode_reg[2:0]); //Burst Length
// XXX modification by lekernel - removed CL2.5 support which crashes free simulators
// can be rewritten to make it work, but as CL2.5 is not used by Milkymist I'm lazy :)
// was wire [2 : 0] CL = (mode_reg[6:4] == 3'b110) ? 2.5 : mode_reg[6:4]; //CAS Latency
wire [2 : 0] CL = mode_reg[6:4]; //CAS Latency
wire dqs_n_en = ~ext_mode_reg[10]; //dqs# Enable
wire [2 : 0] AL = ext_mode_reg[5:3]; //Additive Latency
wire [3 : 0] RL = CL ; //Read Latency
wire [3 : 0] WL = 1 ; //Write Latency
wire [DM_BITS - 1 : 0] dm = dq_en ? dm_out : {DM_BITS{1'bz}};
wire [DQ_BITS - 1 : 0] dq = dq_en ? dq_out : {DQ_BITS{1'bz}};
wire [DQS_BITS - 1 : 0] dqs = dqs_en ? dqs_out : {DQS_BITS{1'bz}};
wire [DQS_BITS - 1 : 0] dqs_n = (dqs_en & dqs_n_en) ? ~dqs_out : {DQS_BITS{1'bz}};
wire [DQS_BITS - 1 : 0] rdqs_n = {DM_BITS{1'bz}};
wire [15 : 0] dqs_in = dqs;
wire [63 : 0] dq_in = dq;
ddr sdramddr (
clk ,
clk_n ,
cke ,
cs_n ,
ras_n ,
cas_n ,
we_n ,
ba ,
a ,
dm ,
dq ,
dqs
);
// timing definition in tCK units
real tck ;
integer tmrd ;
integer trap ;
integer tras ;
integer trc ;
integer trfc ;
integer trcd ;
integer trp ;
integer trrd ;
integer twr ;
initial begin
`ifdef period
tck = `period ;
`else
tck = tCK;
`endif
tmrd = ciel(tMRD/tck);
trap = ciel(tRAP/tck);
tras = ciel(tRAS/tck);
trc = ciel(tRC/tck);
trfc = ciel(tRFC/tck);
trcd = ciel(tRCD/tck);
trp = ciel(tRP/tck);
trrd = ciel(tRRD/tck);
twr = ciel(tWR/tck);
end
initial clk <= 1'b1;
initial clk_n <= 1'b0;
always @(posedge clk) begin
clk <= #(tck/2) 1'b0;
clk_n <= #(tck/2) 1'b1;
clk <= #(tck) 1'b1;
clk_n <= #(tck) 1'b0;
end
function integer ciel;
input number;
real number;
if (number > $rtoi(number))
ciel = $rtoi(number) + 1;
else
ciel = number;
endfunction
task power_up;
begin
cke <= 1'b0;
repeat(10) @(negedge clk);
$display ("%m at time %t TB: A 200 us delay is required before CKE can be brought high.", $time);
@ (negedge clk) cke = 1'b1;
nop (400/tck+1);
end
endtask
task load_mode;
input [BA_BITS - 1 : 0] bank;
input [ADDR_BITS - 1 : 0] addr;
begin
case (bank)
0: mode_reg = addr;
1: ext_mode_reg = addr;
endcase
cke = 1'b1;
cs_n = 1'b0;
ras_n = 1'b0;
cas_n = 1'b0;
we_n = 1'b0;
ba = bank;
a = addr;
@(negedge clk);
end
endtask
task refresh;
begin
cke = 1'b1;
cs_n = 1'b0;
ras_n = 1'b0;
cas_n = 1'b0;
we_n = 1'b1;
@(negedge clk);
end
endtask
task burst_term;
integer i;
begin
cke = 1'b1;
cs_n = 1'b0;
ras_n = 1'b1;
cas_n = 1'b1;
we_n = 1'b0;
@(negedge clk);
for (i=0; i<BL; i=i+1) begin
dm_fifo[2*RL + i] = {DM_BITS{1'bz}} ;
dq_fifo[2*RL + i] = {DQ_BITS{1'bz}} ;
end
end
endtask
task self_refresh;
input count;
integer count;
begin
cke = 1'b0;
cs_n = 1'b0;
ras_n = 1'b0;
cas_n = 1'b0;
we_n = 1'b1;
repeat(count) @(negedge clk);
end
endtask
task precharge;
input [BA_BITS - 1 : 0] bank;
input ap; //precharge all
begin
cke = 1'b1;
cs_n = 1'b0;
ras_n = 1'b0;
cas_n = 1'b1;
we_n = 1'b0;
ba = bank;
a = (ap<<10);
@(negedge clk);
end
endtask
task activate;
input [BA_BITS - 1 : 0] bank;
input [ADDR_BITS - 1 : 0] row;
begin
cke = 1'b1;
cs_n = 1'b0;
ras_n = 1'b0;
cas_n = 1'b1;
we_n = 1'b1;
ba = bank;
a = row;
@(negedge clk);
end
endtask
//write task supports burst lengths <= 16
task write;
input [BA_BITS - 1 : 0] bank;
input [COL_BITS - 1 : 0] col;
input ap; //Auto Precharge
input [16*DM_BITS - 1 : 0] dm;
input [16*DQ_BITS - 1 : 0] dq;
reg [ADDR_BITS - 1 : 0] atemp [1:0];
reg [DQ_BITS/DM_BITS - 1 : 0] dm_temp;
integer i,j;
begin
cke = 1'b1;
cs_n = 1'b0;
ras_n = 1'b1;
cas_n = 1'b0;
we_n = 1'b0;
ba = bank;
atemp[0] = col & 10'h3ff; //ADDR[ 9: 0] = COL[ 9: 0]
atemp[1] = (col>>10)<<11; //ADDR[ N:11] = COL[ N:10]
a = atemp[0] | atemp[1] | (ap<<10);
for (i=0; i<=BL; i=i+1) begin
dqs_en <= #(WL*tck + i*tck/2) 1'b1;
if (i%2 === 0) begin
dqs_out <= #(WL*tck + i*tck/2) {DQS_BITS{1'b0}};
end else begin
dqs_out <= #(WL*tck + i*tck/2) {DQS_BITS{1'b1}};
end
dq_en <= #(WL*tck + i*tck/2 + tck/4) 1'b1;
for (j=0; j<DM_BITS; j=j+1) begin
dm_temp = dm>>((i*DM_BITS + j)*DQ_BITS/DM_BITS);
dm_out[j] <= #(WL*tck + i*tck/2 + tck/4) &dm_temp;
end
dq_out <= #(WL*tck + i*tck/2 + tck/4) dq>>i*DQ_BITS;
case (i)
15: dm_out <= #(WL*tck + i*tck/2 + tck/4) dm[16*DM_BITS-1 : 15*DM_BITS];
14: dm_out <= #(WL*tck + i*tck/2 + tck/4) dm[15*DM_BITS-1 : 14*DM_BITS];
13: dm_out <= #(WL*tck + i*tck/2 + tck/4) dm[14*DM_BITS-1 : 13*DM_BITS];
12: dm_out <= #(WL*tck + i*tck/2 + tck/4) dm[13*DM_BITS-1 : 12*DM_BITS];
11: dm_out <= #(WL*tck + i*tck/2 + tck/4) dm[12*DM_BITS-1 : 11*DM_BITS];
10: dm_out <= #(WL*tck + i*tck/2 + tck/4) dm[11*DM_BITS-1 : 10*DM_BITS];
9: dm_out <= #(WL*tck + i*tck/2 + tck/4) dm[10*DM_BITS-1 : 9*DM_BITS];
8: dm_out <= #(WL*tck + i*tck/2 + tck/4) dm[ 9*DM_BITS-1 : 8*DM_BITS];
7: dm_out <= #(WL*tck + i*tck/2 + tck/4) dm[ 8*DM_BITS-1 : 7*DM_BITS];
6: dm_out <= #(WL*tck + i*tck/2 + tck/4) dm[ 7*DM_BITS-1 : 6*DM_BITS];
5: dm_out <= #(WL*tck + i*tck/2 + tck/4) dm[ 6*DM_BITS-1 : 5*DM_BITS];
4: dm_out <= #(WL*tck + i*tck/2 + tck/4) dm[ 5*DM_BITS-1 : 4*DM_BITS];
3: dm_out <= #(WL*tck + i*tck/2 + tck/4) dm[ 4*DM_BITS-1 : 3*DM_BITS];
2: dm_out <= #(WL*tck + i*tck/2 + tck/4) dm[ 3*DM_BITS-1 : 2*DM_BITS];
1: dm_out <= #(WL*tck + i*tck/2 + tck/4) dm[ 2*DM_BITS-1 : 1*DM_BITS];
0: dm_out <= #(WL*tck + i*tck/2 + tck/4) dm[ 1*DM_BITS-1 : 0*DM_BITS];
endcase
case (i)
15: dq_out <= #(WL*tck + i*tck/2 + tck/4) dq[16*DQ_BITS-1 : 15*DQ_BITS];
14: dq_out <= #(WL*tck + i*tck/2 + tck/4) dq[15*DQ_BITS-1 : 14*DQ_BITS];
13: dq_out <= #(WL*tck + i*tck/2 + tck/4) dq[14*DQ_BITS-1 : 13*DQ_BITS];
12: dq_out <= #(WL*tck + i*tck/2 + tck/4) dq[13*DQ_BITS-1 : 12*DQ_BITS];
11: dq_out <= #(WL*tck + i*tck/2 + tck/4) dq[12*DQ_BITS-1 : 11*DQ_BITS];
10: dq_out <= #(WL*tck + i*tck/2 + tck/4) dq[11*DQ_BITS-1 : 10*DQ_BITS];
9: dq_out <= #(WL*tck + i*tck/2 + tck/4) dq[10*DQ_BITS-1 : 9*DQ_BITS];
8: dq_out <= #(WL*tck + i*tck/2 + tck/4) dq[ 9*DQ_BITS-1 : 8*DQ_BITS];
7: dq_out <= #(WL*tck + i*tck/2 + tck/4) dq[ 8*DQ_BITS-1 : 7*DQ_BITS];
6: dq_out <= #(WL*tck + i*tck/2 + tck/4) dq[ 7*DQ_BITS-1 : 6*DQ_BITS];
5: dq_out <= #(WL*tck + i*tck/2 + tck/4) dq[ 6*DQ_BITS-1 : 5*DQ_BITS];
4: dq_out <= #(WL*tck + i*tck/2 + tck/4) dq[ 5*DQ_BITS-1 : 4*DQ_BITS];
3: dq_out <= #(WL*tck + i*tck/2 + tck/4) dq[ 4*DQ_BITS-1 : 3*DQ_BITS];
2: dq_out <= #(WL*tck + i*tck/2 + tck/4) dq[ 3*DQ_BITS-1 : 2*DQ_BITS];
1: dq_out <= #(WL*tck + i*tck/2 + tck/4) dq[ 2*DQ_BITS-1 : 1*DQ_BITS];
0: dq_out <= #(WL*tck + i*tck/2 + tck/4) dq[ 1*DQ_BITS-1 : 0*DQ_BITS];
endcase
dq_en <= #(WL*tck + i*tck/2 + tck/4) 1'b1;
end
dqs_en <= #(WL*tck + BL*tck/2 + tck/2) 1'b0;
dq_en <= #(WL*tck + BL*tck/2 + tck/4) 1'b0;
@(negedge clk);
end
endtask
task read;
input [BA_BITS - 1 : 0]bank;
input [COL_BITS - 1 : 0] col;
input ap; //Auto Precharge
reg [ADDR_BITS - 1 : 0] atemp [1:0];
begin
cke = 1'b1;
cs_n = 1'b0;
ras_n = 1'b1;
cas_n = 1'b0;
we_n = 1'b1;
ba = bank;
atemp[0] = col & 10'h3ff; //ADDR[ 9: 0] = COL[ 9: 0]
atemp[1] = (col>>10)<<11; //ADDR[ N:11] = COL[ N:10]
a = atemp[0] | atemp[1] | (ap<<10);
@(negedge clk);
end
endtask
// read with data verification
task read_verify;
input [BA_BITS - 1 : 0] bank;
input [COL_BITS - 1 : 0] col;
input ap; //Auto Precharge
input [16*DM_BITS - 1 : 0] dm; //Expected Data Mask
input [16*DQ_BITS - 1 : 0] dq; //Expected Data
integer i;
reg [2:0] brst_col;
begin
read (bank, col, ap);
for (i=0; i<BL; i=i+1) begin
// perform burst ordering
brst_col = col ^ i;
if (!BO) begin
brst_col = col + i;
end
if (BL == 4) begin
brst_col[2] = 1'b0 ;
end else if (BL == 2) begin
brst_col[2:1] = 2'b00 ;
end
dm_fifo[2*RL + i] = dm >> (i*DM_BITS);
dq_fifo[2*RL + i] = dq >> (i*DQ_BITS);
end
end
endtask
task nop;
input count;
integer count;
begin
cke = 1'b1;
cs_n = 1'b0;
ras_n = 1'b1;
cas_n = 1'b1;
we_n = 1'b1;
repeat(count) @(negedge clk);
end
endtask
task deselect;
input count;
integer count;
begin
cke = 1'b1;
cs_n = 1'b1;
ras_n = 1'b1;
cas_n = 1'b1;
we_n = 1'b1;
repeat(count) @(negedge clk);
end
endtask
task power_down;
input count;
integer count;
begin
cke = 1'b0;
cs_n = 1'b1;
ras_n = 1'b1;
cas_n = 1'b1;
we_n = 1'b1;
repeat(count) @(negedge clk);
end
endtask
function [16*DQ_BITS - 1 : 0] sort_data;
input [16*DQ_BITS - 1 : 0] dq;
input [2:0] col;
integer i;
reg [2:0] brst_col;
reg [DQ_BITS - 1 :0] burst;
begin
sort_data = 0;
for (i=0; i<BL; i=i+1) begin
// perform burst ordering
brst_col = col ^ i;
if (!BO) begin
brst_col[1:0] = col + i;
end
burst = dq >> (brst_col*DQ_BITS);
sort_data = sort_data | burst<<(i*DQ_BITS);
end
end
endfunction
// receiver(s) for data_verify process
always @(dqs_in[0]) begin #(tDQSQ); dqs_receiver(0); end
always @(dqs_in[1]) begin #(tDQSQ); dqs_receiver(1); end
always @(dqs_in[2]) begin #(tDQSQ); dqs_receiver(2); end
always @(dqs_in[3]) begin #(tDQSQ); dqs_receiver(3); end
always @(dqs_in[4]) begin #(tDQSQ); dqs_receiver(4); end
always @(dqs_in[5]) begin #(tDQSQ); dqs_receiver(5); end
always @(dqs_in[6]) begin #(tDQSQ); dqs_receiver(6); end
always @(dqs_in[7]) begin #(tDQSQ); dqs_receiver(7); end
task dqs_receiver;
input i;
integer i;
begin
if (dqs_in[i]) begin
case (i)
0: dq_in_pos[ 7: 0] <= dq_in[ 7: 0];
1: dq_in_pos[15: 8] <= dq_in[15: 8];
/* 2: dq_in_pos[23:16] <= dq_in[23:16];
3: dq_in_pos[31:24] <= dq_in[31:24];
4: dq_in_pos[39:32] <= dq_in[39:32];
5: dq_in_pos[47:40] <= dq_in[47:40];
6: dq_in_pos[55:48] <= dq_in[55:48];
7: dq_in_pos[63:56] <= dq_in[63:56];*/
endcase
end else if (!dqs_in[i]) begin
case (i)
0: dq_in_neg[ 7: 0] <= dq_in[ 7: 0];
1: dq_in_neg[15: 8] <= dq_in[15: 8];
/* 2: dq_in_neg[23:16] <= dq_in[23:16];
3: dq_in_neg[31:24] <= dq_in[31:24];
4: dq_in_pos[39:32] <= dq_in[39:32];
5: dq_in_pos[47:40] <= dq_in[47:40];
6: dq_in_pos[55:48] <= dq_in[55:48];
7: dq_in_pos[63:56] <= dq_in[63:56];*/
endcase
end
end
endtask
// perform data verification as a result of read_verify task call
always @(clk) begin : data_verify
integer i;
reg [DM_BITS-1 : 0] data_mask;
reg [8*DM_BITS-1 : 0] bit_mask;
for (i=0; i<=14; i=i+1) begin
dm_fifo[i] = dm_fifo[i+1];
dq_fifo[i] = dq_fifo[i+1];
end
dm_fifo[13] = 'bz;
dq_fifo[13] = 'bz;
// dm_fifo[30] = 0;
// dq_fifo[30] = 0;
data_mask = dm_fifo[0];
data_mask = dm_fifo[0];
for (i=0; i<DM_BITS; i=i+1) begin
bit_mask = {bit_mask, {8{~data_mask[i]}}};
end
if (clk) begin
if ((dq_in_neg & bit_mask) != (dq_fifo[0] & bit_mask))
$display ("%m at time %t: ERROR: Read data miscompare: Expected = %h, Actual = %h, Mask = %h", $time, dq_fifo[0], dq_in_neg, bit_mask);
end else begin
if ((dq_in_pos & bit_mask) != (dq_fifo[0] & bit_mask))
$display ("%m at time %t: ERROR: Read data miscompare: Expected = %h, Actual = %h, Mask = %h", $time, dq_fifo[0], dq_in_pos, bit_mask);
end
end
reg test_done;
initial test_done = 0;
// End-of-test triggered in 'subtest.vh'
always @(test_done) begin : all_done
if (test_done == 1) begin
#5000
$display ("Simulation is Complete");
$stop(0);
$finish;
end
end
// Test included from external file
`include "subtest.vh"
endmodule