This design example shows how to interface Intel's PCI target MegaCore® functions to a synchronous memory similar to altsyncram. The example assumes that you are familiar with the PCI specifications and the functionality of the PCI MegaCore functions.
Download the files used in this example:
- targ_mem32_verilog.zip -32-bit version
- targ_mem32_vhdl.zip -32-bit version
- targ_mem64_verilog.zip -64-bit version
- targ_mem64_vhdl.zip -64-bit version
The following describes implementation for a 64-bit target interface. The implementations for 64-bit and 32-bit are almost identical except for the datapath width. The 64-bit design example instantiates two 32-bit wide altsyncrams. For a 32-bit implementation, only one instance of the 32-bit altsyncram is required. Figure 1 shows the implementation used in this design example.
Figure 1. 64-bit Target Memory Interface
The following sections describe the various blocks in more detail.
During burst transfers (more than 1 transfer), the user logic must keep track of the address for the current transfer since only the starting address is supplied by the PCI bus and the PCI MegaCore. For the 32-bit implementation, the PCI addresses are always DWORD-aligned, and the lower 2 bits of the address are always ‘0’ and, hence, can be omitted. For the 64-bit implementation, the PCI address are always QWORD-aligned and the lower 3 bits of the address are always ‘0’ and, hence, can be ignored.
The address counter is loaded with the starting address (l_adro) at the start of a PCI transfer. The signal cycle_start is generated by the user logic and logic to indicate the start of a target transaction. This signal can be generated by detecting a falling edge of the signal lt_framen.
For writes, the counter is enabled by lt_dxfrn. For reads, the signal lt_ackn is used. The signal targ_wr_rdn indicates the direction of the transfer and is generated from the signal l_cmdo.
wire mem_wr_inc = (!lt_dxfrn & targ_wr_rdn ); wire mem_rd_inc = (!lt_ackn &!targ_wr_rdn ); wire mem_inc = (mem_wr_inc | mem_rd_inc) & !l_hdat_ackn
signal mem_wr_inc : std_logic; signal mem_rd_inc : std_logic; signal mem_inc : std_logic; mem_wr_inc <= ((not(lt_dxfrn)) and targ_wr_rdn and bar0_hit); mem_rd_inc <= ((not(lt_ackn)) and (not(targ_wr_rdn)) and bar0_hit) ; mem_inc <= (mem_wr_inc or mem_rd_inc) and (not(l_hdat_ackn));
The signal l_hdat_ackn indicates that the high-DWORD is currently being transferred on the local side and is not required for a 32-bit only implementation.
Data Steering Logic
The data steering logic is not required for a 32-bit only implementation. Special considerations are required for a 32-bit PCI request to a 64-bit local side. During a 32-bit PCI target write to a 64-bit PCI MegaCore, the transfer width on the local side is 32 bits only and only the lower DWORD (l_dato[31:0]) is active. So, the local side must add appropriate logic to steer this data when the high DWORD is being addressed. The signals l_hdat_ackn and l_ldat_ackn indicate whether the upper DWORD or lower DWORD are currently being transferred on the local side. The signal lt_tsr indicates if a 64-bit transfer is in progress.
The following example shows the data steering logic and is the multiplexer in Figure 1.
wire [31:0] h_dword; // high DWORD write data assign h_dword = (!lt_tsr & !l_hdat_ackn) ? l_dato[31:0] : l_dato[63:32];
signal h_dword : std_logic_vector(31 downto 0); process(lt_tsr,l_hdat_ackn) begin if(((not(lt_tsr(7))) and (not(l_hdat_ackn)))='1') then h_dword <= l_dato(31 downto 0); else h_dword <= l_dato(63 downto 32); end if; end process;
During a 32-bit PCI target read to a 64-bit local side, the transfer width is always 64 bits and both the DWORDs of the bus l_adi[63:0] are active
The signal lt_dxfrn indicates a successful transfer on the local bus. For writes, this signal indicates that valid data is available on the l_dato bus. So, the memory write enable signal is simply logical and derived from the signals lt_dxfrn, targ_wr_rdn.
wire mem_wr_enable = !lt_dxfrn & targ_wr_rdn;
signal mem_wr_enable : std_logic; mem_wr_enable <= (not(lt_dxfrn)) and targ_wr_rdn and bar0_hit;
The data port of the memory can now be mapped to the signal l_dato[31:0] and h_dword.
Memory Clock Enable Logic
Special consideration is required for the case when the master inserts a wait state by de-asserting irdyn. The clock enable is required during PCI reads and will hold the data on the output of the memory when the master inserts a wait state.
wire clken = (mem_wr_enable | mem_rd_inc);
signal clken : std_logic; clken <= mem_wr_enable or mem_rd_inc;