URL
https://opencores.org/ocsvn/openarty/openarty/trunk
Subversion Repositories openarty
Compare Revisions
- This comparison shows the changes necessary to convert path
/openarty/trunk/rtl
- from Rev 21 to Rev 24
- ↔ Reverse comparison
Rev 21 → Rev 24
/wbddrsdram.v
2,9 → 2,16
// |
// Filename: wbddrsdram.v |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// Project: A wishbone controlled DDR3 SDRAM memory controller. |
// Used in: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: |
// Purpose: To control a DDR3-1333 (9-9-9) memory from a wishbone bus. |
// In our particular implementation, there will be two command |
// clocks (2.5 ns) per FPGA clock (i_clk) at 5 ns, and 64-bits transferred |
// per FPGA clock. However, since the memory is focused around 128-bit |
// word transfers, attempts to transfer other than adjacent 64-bit words |
// will (of necessity) suffer stalls. Please see the documentation for |
// more details of how this controller works. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
70,133 → 77,62
`define DDR_BABITS 3 // BABITS are really from 18:16, they are 3 bits |
`define DDR_ADDR_BITS 14 |
// |
|
// |
module wbddrsdram(i_clk, i_reset, |
// Wishbone inputs |
i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, |
o_wb_ack, o_wb_stall, o_wb_data, |
o_ddr_reset_n, o_ddr_cke, |
o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n, o_ddr_we_n, |
o_ddr_dqs, o_ddr_dm, o_ddr_odt, o_ddr_bus_oe, |
o_ddr_addr, o_ddr_ba, o_ddr_data, i_ddr_data); |
parameter CKRBITS = 13, // Bits in CKREFI4 |
CKREFI = 13'd1560, // 4 * 7.8us at 200 MHz clock |
CKRFC = 320, |
CKWR = 3, |
CKRP = 11, // (=tRTP)Time from precharge to open command |
CKCAS = 11, // CAS Latency, tCL |
CKXPR = CKRFC+5+2, // Clocks per tXPR timeout |
BUSREG= 2+CKCAS, |
BUSNOW= 3+CKCAS; |
input i_clk, i_reset; |
i_wb_sel, |
// Wishbone outputs |
o_wb_ack, o_wb_stall, o_wb_data, |
// Memory command wires |
o_ddr_reset_n, o_ddr_cke, o_ddr_bus_oe, |
o_ddr_cmd_a, o_ddr_cmd_b, |
// And the data wires to go with them .... |
o_ddr_data, i_ddr_data); |
// These parameters are not really meant for adjusting from the |
// top level. These are more internal variables, recorded here |
// so that things can be automatically adjusted without much |
// problem. |
parameter CKRP = 3; |
parameter BUSNOW = 4, BUSREG = BUSNOW-1; |
// The commands (above) include (in this order): |
// o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n, o_ddr_we_n, |
// o_ddr_dqs, o_ddr_dm, o_ddr_odt |
input i_clk, // *MUST* be at 200 MHz for this to work |
i_reset; |
// Wishbone inputs |
input i_wb_cyc, i_wb_stb, i_wb_we; |
input [25:0] i_wb_addr; |
input [31:0] i_wb_data; |
// Wishbone outputs |
output reg o_wb_ack; |
output reg o_wb_stall; |
output reg [31:0] o_wb_data; |
// DDR3 RAM Controller |
output reg o_ddr_reset_n, o_ddr_cke; |
// Control outputs |
output wire o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n,o_ddr_we_n; |
// DQS outputs:set to 3'b010 when data is active, 3'b100 (i.e. 2'bzz) ow |
output wire o_ddr_dqs; |
output reg o_ddr_dm; |
output reg o_ddr_odt; |
output wire o_ddr_bus_oe; |
// Address outputs |
output wire [13:0] o_ddr_addr; |
output wire [2:0] o_ddr_ba; |
// And the data inputs and outputs |
output reg [31:0] o_ddr_data; |
input [31:0] i_ddr_data; |
input i_wb_cyc, i_wb_stb, i_wb_we; |
input [24:0] i_wb_addr; // Identifies a 64-bit word of interest |
input [63:0] i_wb_data; |
input [7:0] i_wb_sel; |
// Wishbone responses/outputs |
output reg o_wb_ack, o_wb_stall; |
output reg [63:0] o_wb_data; |
// DDR memory command wires |
output reg o_ddr_reset_n, o_ddr_cke, o_ddr_bus_oe; |
// CMDs are: |
// 4 bits of CS, RAS, CAS, WE |
// 3 bits of bank |
// 14 bits of Address |
// 1 bit of DQS (strobe active, or not) |
// 4 bits of mask (one per byte) |
// 1 bit of ODT |
// ---- |
// 27 bits total |
output wire [26:0] o_ddr_cmd_a, o_ddr_cmd_b; |
output reg [63:0] o_ddr_data; |
input [63:0] i_ddr_data; |
|
reg drive_dqs; |
|
// The pending transaction |
reg [31:0] r_data; |
reg r_pending, r_we; |
reg [25:0] r_addr; |
reg [13:0] r_row; |
reg [2:0] r_bank; |
reg [9:0] r_col; |
reg [1:0] r_sub; |
reg r_move; // It was accepted, and can move to next stage |
|
// The pending transaction, one further into the pipeline. This is |
// the stage where the read/write command is actually given to the |
// interface if we haven't stalled. |
reg [31:0] s_data; |
reg s_pending, s_we; // , s_match; |
reg [25:0] s_addr; |
reg [13:0] s_row, s_nxt_row; |
reg [2:0] s_bank, s_nxt_bank; |
reg [9:0] s_col; |
reg [1:0] s_sub; |
|
// Can the pending transaction be satisfied with the current (ongoing) |
// transaction? |
reg m_move, m_match, m_pending, m_we; |
reg [25:0] m_addr; |
reg [13:0] m_row; |
reg [2:0] m_bank; |
reg [9:0] m_col; |
reg [1:0] m_sub; |
|
// Can we preload the next bank? |
reg [13:0] r_nxt_row; |
reg [2:0] r_nxt_bank; |
|
reg need_close_bank, need_close_this_bank, |
last_close_bank, maybe_close_next_bank, |
last_maybe_close, |
need_open_bank, last_open_bank, maybe_open_next_bank, |
last_maybe_open, |
valid_bank, last_valid_bank; |
reg [(`DDR_CMDLEN-1):0] close_bank_cmd, activate_bank_cmd, |
maybe_close_cmd, maybe_open_cmd, rw_cmd; |
reg [1:0] rw_sub; |
reg rw_we; |
|
wire w_this_closing_bank, w_this_opening_bank, |
w_this_maybe_close, w_this_maybe_open, |
w_this_rw_move; |
reg last_closing_bank, last_opening_bank; |
wire w_need_close_this_bank, w_need_open_bank, |
w_r_valid, w_s_valid, w_s_match; |
////////// |
// |
// tWTR = 7.5 |
// tRRD = 7.5 |
// tREFI= 7.8 |
// tFAW = 45 |
// tRTP = 7.5 |
// tCKE = 5.625 |
// tRFC = 160 |
// tRP = 13.5 |
// tRAS = 36 |
// tRCD = 13.5 |
// |
// RESET: |
// 1. Hold o_reset_n = 1'b0; for 200 us, or 40,000 clocks (65536 perhaps?) |
// Hold cke low during this time as well |
// The clock should be free running into the chip during this time |
// Leave command in NOOP state: {cs,ras,cas,we} = 4'h7; |
// ODT must be held low |
// 2. Hold cke low for another 500us, or 100,000 clocks |
// 3. Raise CKE, continue outputting a NOOP for |
// tXPR, tDLLk, and tZQInit |
// 4. Load MRS2, wait tMRD |
// 4. Load MRS3, wait tMRD |
// 4. Load MRS1, wait tMOD |
// Before using the SDRAM, we'll need to program at least 3 of the mode |
// registers, if not all 4. |
// tMOD clocks are required to program the mode registers, during which |
// time the RAM must be idle. |
// Reset Logic |
// |
// NOOP: CS low, RAS, CAS, and WE high |
|
// |
////////// |
// |
// |
// Reset logic should be simple, and is given as follows: |
// note that it depends upon a ROM memory, reset_mem, and an address into that |
// memory: reset_address. Each memory location provides either a "command" to |
205,7 → 141,7
// be set to idle, or whether the command is instead left as it was. |
reg reset_override, reset_ztimer, maintenance_override; |
reg [4:0] reset_address; |
reg [(`DDR_CMDLEN-1):0] reset_cmd, cmd, refresh_cmd, |
reg [(`DDR_CMDLEN-1):0] reset_cmd, cmd_a, cmd_b, refresh_cmd, |
maintenance_cmd; |
reg [24:0] reset_instruction; |
reg [16:0] reset_timer; |
240,51 → 176,15
reset_timer <= reset_instruction[16:0]; |
end |
|
wire [16:0] w_ckXPR, w_ckRST, w_ckRP, |
w_ckRFC_first; |
wire [2:0] w_ckCAS_MR2, w_ckCAS_MR0, w_ckCAS, w_ckFIVE; |
wire [16:0] w_ckXPR, w_ckRFC_first; |
wire [13:0] w_MR0, w_MR1, w_MR2; |
assign w_ckXPR = CKXPR; |
assign w_ckRST = 4; |
assign w_ckRP = CKRP-2; |
/* verilator lint_off WIDTH */ |
assign w_ckCAS = CKCAS; |
/* verilator lint_on WIDTH */ |
assign w_ckRFC_first = CKRFC-2-9+8; |
assign w_ckFIVE = 3'h5; |
assign w_ckCAS_MR2 = w_ckFIVE-3'h5; // w_ckCAS-3'h5; |
assign w_ckCAS_MR0 = w_ckFIVE-3'h4; // w_ckCAS-3'h4; |
assign w_MR2 = { 3'h0, 2'b00, 1'b0, 1'b0, 1'b1, w_ckCAS_MR2, 3'b0 }; |
assign w_MR1 = { |
1'h0, // Reserved for Future Use (RFU) |
1'b0, // Qoff - output buffer enabled |
1'b1, // TDQS ... enabled |
1'b0, // RFU |
1'b0, // High order bit, Rtt_Nom (3'b011) |
1'b0, // RFU |
// |
1'b0, // Disable write-leveling |
1'b1, // Mid order bit of Rtt_Nom |
1'b0, // High order bit of Output Drvr Impedence Ctrl |
2'b0, // Additive latency = 0 |
1'b1, // Low order bit of Rtt_Nom |
1'b0, // DIC set to 2'b00 |
1'b0 // MRS1, DLL enable |
}; |
assign w_MR0 = { |
1'b0, // Reserved for future use |
1'b0, // PPD control, (slow exit(DLL off)) |
3'b1, // Write recovery for auto precharge |
1'b0, // DLL Reset (No) |
// |
1'b0, // TM mode normal |
w_ckCAS_MR0, // High 3-bits, CAS latency (=4'b1110=4'd5,tCL=11) |
// |
1'b0, // Read burst type = nibble sequential |
(CKCAS>11)? 1'b1:1'b0, // Low bit of cas latency |
2'b0 // Burst length = 8 (Fixed) |
}; |
assign w_MR0 = 14'h0420; |
assign w_MR1 = 14'h0044; |
assign w_MR2 = 14'h0040; |
assign w_ckXPR = 17'd68; // Table 68, p186 |
assign w_ckRFC_first = 17'd30; // i.e. 64 nCK, or ckREFI |
always @(posedge i_clk) |
// DONE, TIMER, RESET, CKE, |
if (i_reset) |
reset_instruction <= { 4'h4, `DDR_NOOP, 17'd40_000 }; |
else if (reset_ztimer) case(reset_address) // RSTDONE, TIMER, CKE, ?? |
294,41 → 194,36
5'h1: reset_instruction <= { 4'h6, `DDR_NOOP, 17'd100_000 }; |
// 3. Assert CKE, wait minimum of Reset CKE Exit time |
5'h2: reset_instruction <= { 4'h7, `DDR_NOOP, w_ckXPR }; |
// 4. Look MR2. (1CK, no TIMER) |
5'h3: reset_instruction <= { 4'h3, `DDR_NOOP, 3'h2, w_MR2 }; |
5'h4: reset_instruction <= { 4'h3, `DDR_MRSET, 3'h2, w_MR2 }; |
5'h5: reset_instruction <= { 4'h3, `DDR_NOOP, 3'h2, w_MR2 }; |
// 3. Wait 4 clocks (tMRD) |
5'h6: reset_instruction <= { 4'h7, `DDR_NOOP, 17'h02 }; |
// 5. Set MR1 |
5'h7: reset_instruction <= { 4'h3, `DDR_NOOP, 3'h1, w_MR1 }; |
5'h8: reset_instruction <= { 4'h3, `DDR_MRSET, 3'h1, w_MR1 }; |
5'h9: reset_instruction <= { 4'h3, `DDR_NOOP, 3'h1, w_MR1 }; |
// 7. Wait another 4 clocks |
5'ha: reset_instruction <= { 4'h7, `DDR_NOOP, 17'h02 }; |
// 8. Send MRS0 |
5'hb: reset_instruction <= { 4'h3, `DDR_NOOP, 3'h0, w_MR0 }; |
5'hc: reset_instruction <= { 4'h3, `DDR_MRSET, 3'h0, w_MR0 }; |
5'hd: reset_instruction <= { 4'h3, `DDR_NOOP, 3'h0, w_MR0 }; |
// 9. Wait tMOD, is max(12 clocks, 15ns) |
5'he: reset_instruction <= { 4'h7, `DDR_NOOP, 17'h0a }; |
// 10. Issue a ZQCL command to start ZQ calibration, A10 is high |
5'hf: reset_instruction <= { 4'h3, `DDR_ZQS, 6'h0, 1'b1, 10'h0}; |
//11.Wait for both tDLLK and tZQinit completed, both are 512 cks |
5'h10: reset_instruction <= { 4'h7, `DDR_NOOP, 17'd512 }; |
// 4. Set MR2. (4 nCK, no TIMER, but needs a NOOP cycle) |
5'h3: reset_instruction <= { 4'h3, `DDR_MRSET, 3'h2, w_MR2 }; |
5'h4: reset_instruction <= { 4'h3, `DDR_NOOP, 17'h00 }; |
// 5. Set MR1. (4 nCK, no TIMER, but needs a NOOP cycle) |
5'h5: reset_instruction <= { 4'h3, `DDR_MRSET, 3'h1, w_MR1 }; |
5'h6: reset_instruction <= { 4'h3, `DDR_NOOP, 17'h00 }; |
// 6. Set MR0 |
5'h7: reset_instruction <= { 4'h3, `DDR_MRSET, 3'h0, w_MR0 }; |
// 7. Wait 12 clocks |
5'h8: reset_instruction <= { 4'h7, `DDR_NOOP, 17'd10 }; |
// 8. Issue a ZQCL command to start ZQ calibration, A10 is high |
5'h9: reset_instruction <= { 4'h3, `DDR_ZQS, 6'h0, 1'b1, 10'h0}; |
//11.Wait for both tDLLK and tZQinit completed, both are |
// 512 cks. Of course, since every one of these commands takes |
// two clocks, we wait for half as many clocks (minus two for |
// our timer logic) |
5'ha: reset_instruction <= { 4'h7, `DDR_NOOP, 17'd254 }; |
// 12. Precharge all command |
5'h11: reset_instruction <= { 4'h3, `DDR_PRECHARGE, 6'h0, 1'b1, 10'h0 }; |
// 13. Wait for the precharge to complete |
5'h12: reset_instruction <= { 4'h7, `DDR_NOOP, w_ckRP }; |
5'hb: reset_instruction <= { 4'h3, `DDR_PRECHARGE, 6'h0, 1'b1, 10'h0 }; |
// 13. Wait for the precharge to complete. A count of one, |
// will have us waiting (1+2)*2 or 6 clocks, so we should be |
// good here. |
5'hc: reset_instruction <= { 4'h7, `DDR_NOOP, 17'd1 }; |
// 14. A single Auto Refresh commands |
5'h13: reset_instruction <= { 4'h3, `DDR_REFRESH, 17'h00 }; |
5'hd: reset_instruction <= { 4'h3, `DDR_REFRESH, 17'h00 }; |
// 15. Wait for the auto refresh to complete |
5'h14: reset_instruction <= { 4'h7, `DDR_NOOP, w_ckRFC_first }; |
// Two Auto Refresh commands |
5'he: reset_instruction <= { 4'h7, `DDR_NOOP, w_ckRFC_first }; |
default: |
reset_instruction <={4'hb, `DDR_NOOP, 17'd00_000 }; |
endcase |
// reset_instruction <= reset_mem[reset_address]; |
|
initial reset_address = 5'h0; |
always @(posedge i_clk) |
336,36 → 231,216
reset_address <= 5'h1; |
else if ((reset_ztimer)&&(reset_override)) |
reset_address <= reset_address + 5'h1; |
|
////////// |
// |
// initial reset_mem = |
// 0. !DONE, TIMER,RESET_N=0, CKE=0, CMD = NOOP, TIMER = 200us ( 40,000) |
// 1. !DONE, TIMER,RESET_N=1, CKE=0, CMD = NOOP, TIMER = 500us (100,000) |
// 2. !DONE, TIMER,RESET_N=1, CKE=1, CMD = NOOP, TIMER = (Look me up) |
// 3. !DONE,!TIMER,RESET_N=1, CKE=1, CMD = MODE, MRS |
// 4. !DONE,!TIMER,RESET_N=1, CKE=1, CMD = NOOP, TIMER = tMRS |
// 5. !DONE,!TIMER,RESET_N=1, CKE=1, CMD = MODE, MRS3 |
// 6. !DONE,!TIMER,RESET_N=1, CKE=1, CMD = NOOP, TIMER = tMRS |
// 7. !DONE,!TIMER,RESET_N=1, CKE=1, CMD = MODE, MRS1 |
// 8. !DONE,!TIMER,RESET_N=1, CKE=1, CMD = NOOP, TIMER = tMRS |
// 9. !DONE,!TIMER,RESET_N=1, CKE=1, CMD = MODE, MRS1 |
// 10. !DONE,!TIMER,RESET_N=1, CKE=1, CMD = NOOP, TIMER = tMOD |
// 11. !DONE,!TIMER,RESET_N=1, CKE=1, (Pre-charge all) |
// 12. !DONE,!TIMER,RESET_N=1, CKE=1, (wait) |
// 13. !DONE,!TIMER,RESET_N=1, CKE=1, (Auto-refresh) |
// 14. !DONE,!TIMER,RESET_N=1, CKE=1, (Auto-refresh) |
// 15. !DONE,!TIMER,RESET_N=1, CKE=1, (wait) |
// |
// Refresh Logic |
// |
// |
////////// |
// |
// |
// |
// Okay, let's investigate when we need to do a refresh. Our plan will be to |
// do a single refreshes every tREFI seconds. We will not push off refreshes, |
// nor pull them in--for simplicity. tREFI = 7.8us, but it is a parameter |
// in the number of clocks. In our case, 7.8us / 5ns = 1560 clocks (not nCK!) |
// |
// Note that 160ns are needed between refresh commands (JEDEC, p172), or |
// 32 clocks @200MHz. After this time, no more refreshes will be needed for |
// (1560-32) clocks (@ 200 MHz). |
// |
// This logic is very similar to the refresh logic, both use a memory as a |
// script. |
// |
reg need_refresh; |
reg refresh_ztimer; |
reg [16:0] refresh_counter; |
reg [2:0] refresh_addr; |
reg [23:0] refresh_instruction; |
always @(posedge i_clk) |
if (reset_override) |
refresh_addr <= 3'hf; |
else if (refresh_ztimer) |
refresh_addr <= refresh_addr + 3'h1; |
else if (refresh_instruction[`DDR_RFBEGIN]) |
refresh_addr <= 3'h0; |
|
always @(posedge i_clk) |
if (reset_override) |
begin |
refresh_ztimer <= 1'b1; |
refresh_counter <= 17'd0; |
end else if (!refresh_ztimer) |
begin |
refresh_ztimer <= (refresh_counter == 17'h1); |
refresh_counter <= (refresh_counter - 17'h1); |
end else if (refresh_instruction[`DDR_RFTIMER]) |
begin |
refresh_ztimer <= 1'b0; |
refresh_counter <= refresh_instruction[16:0]; |
end |
|
wire [16:0] w_ckREFI; |
assign w_ckREFI = 17'd1560; // == 6240/4 |
|
wire [16:0] w_ckREFI_left, w_ckRFC_nxt, w_wait_for_idle, |
w_precharge_to_refresh; |
|
// We need to wait for the bus to become idle from whatever state |
// it is in. The difficult time for this measurement is assuming |
// a write was just given. In that case, we need to wait for the |
// write to complete, and then to wait an additional tWR (write |
// recovery time) or 6 nCK clocks from the end of the write. This |
// works out to seven idle bus cycles from the time of the write |
// command, or a count of 5 (7-2). |
assign w_wait_for_idle = 17'd5; // |
assign w_precharge_to_refresh = 17'd1; // = 3-2 |
assign w_ckREFI_left[16:0] = 17'd1560 // The full interval |
-17'd32 // Min what we've already waited |
-w_wait_for_idle |
-w_precharge_to_refresh-17'd12; |
assign w_ckRFC_nxt[16:0] = 17'd32-17'd2; |
|
always @(posedge i_clk) |
if (refresh_ztimer) |
case(refresh_addr)//NEED-REFRESH, HAVE-TIMER, BEGIN(start-over) |
// First, a number of clocks needing no refresh |
3'h0: refresh_instruction <= { 3'h2, `DDR_NOOP, w_ckREFI_left }; |
// Then, we take command of the bus and wait for it to be |
// guaranteed idle |
3'h1: refresh_instruction <= { 3'h6, `DDR_NOOP, w_wait_for_idle }; |
// Once the bus is idle, all commands complete, and a minimum |
// recovery time given, we can issue a precharge all command |
3'h2: refresh_instruction <= { 3'h4, `DDR_PRECHARGE, 17'h0400 }; |
// Now we need to wait tRP = 3 clocks (6 nCK) |
3'h3: refresh_instruction <= { 3'h6, `DDR_NOOP, w_precharge_to_refresh }; |
3'h4: refresh_instruction <= { 3'h4, `DDR_REFRESH, 17'h00 }; |
3'h5: refresh_instruction <= { 3'h6, `DDR_NOOP, w_ckRFC_nxt }; |
default: |
refresh_instruction <= { 3'h1, `DDR_NOOP, 17'h00 }; |
endcase |
|
// Note that we don't need to check if (reset_override) here since |
// refresh_ztimer will always be true if (reset_override)--in other |
// words, it will be true for many, many, clocks--enough for this |
// logic to settle out. |
always @(posedge i_clk) |
if (refresh_ztimer) |
refresh_cmd <= refresh_instruction[20:0]; |
always @(posedge i_clk) |
if (refresh_ztimer) |
need_refresh <= refresh_instruction[`DDR_NEEDREFRESH]; |
|
|
/* |
input i_clk, i_reset; |
// Wishbone inputs |
input i_wb_cyc, i_wb_stb, i_wb_we; |
input [25:0] i_wb_addr; |
input [31:0] i_wb_data; |
// Wishbone outputs |
output reg o_wb_ack; |
output reg o_wb_stall; |
output reg [31:0] o_wb_data; |
// DDR3 RAM Controller |
output reg o_ddr_reset_n, o_ddr_cke; |
// Control outputs |
output wire o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n,o_ddr_we_n; |
// DQS outputs:set to 3'b010 when data is active, 3'b100 (i.e. 2'bzz) ow |
output wire o_ddr_dqs; |
output reg o_ddr_odt; |
output wire o_ddr_bus_oe; |
// Address outputs |
output wire [13:0] o_ddr_addr; |
output wire [2:0] o_ddr_ba; |
// And the data inputs and outputs |
output reg [31:0] o_ddr_data; |
input [31:0] i_ddr_data; |
*/ |
|
|
reg [1:0] drive_dqs; |
// Our chosen timing doesn't require any more resolution than one |
// bus clock for ODT. (Of course, this really isn't necessary, since |
// we aren't using ODT as per the MRx registers ... but we keep it |
// around in case we change our minds later.) |
reg ddr_odt; |
reg [7:0] ddr_dm; |
|
// The pending transaction |
reg [63:0] r_data; |
reg r_pending, r_we; |
reg [24:0] r_addr; |
reg [13:0] r_row; |
reg [2:0] r_bank; |
reg [9:0] r_col; |
reg r_sub; |
reg [7:0] r_sel; |
|
// The pending transaction, one further into the pipeline. This is |
// the stage where the read/write command is actually given to the |
// interface if we haven't stalled. |
reg [63:0] s_data; |
reg s_pending, s_we; // , s_match; |
reg [24:0] s_addr; |
reg [13:0] s_row, s_nxt_row; |
reg [2:0] s_bank, s_nxt_bank; |
reg [9:0] s_col; |
reg s_sub; |
reg [7:0] s_sel; |
|
// Can the pending transaction be satisfied with the current (ongoing) |
// transaction? |
reg m_move, m_match, m_pending, m_we; |
reg [24:0] m_addr; |
reg [13:0] m_row; |
reg [2:0] m_bank; |
reg [9:0] m_col; |
reg [1:0] m_sub; |
|
// Can we preload the next bank? |
reg [13:0] r_nxt_row; |
reg [2:0] r_nxt_bank; |
|
reg need_close_bank, need_close_this_bank, |
last_close_bank, maybe_close_next_bank, |
last_maybe_close, |
need_open_bank, last_open_bank, maybe_open_next_bank, |
last_maybe_open, |
valid_bank; |
reg [(`DDR_CMDLEN-1):0] close_bank_cmd, activate_bank_cmd, |
maybe_close_cmd, maybe_open_cmd, rw_cmd; |
reg rw_sub; |
reg rw_we; |
|
wire w_this_closing_bank, w_this_opening_bank, |
w_this_maybe_close, w_this_maybe_open, |
w_this_rw_move; |
reg last_closing_bank, last_opening_bank; |
wire w_need_close_this_bank, w_need_open_bank, |
w_r_valid, w_s_valid, w_s_match; |
|
////////// |
// |
// |
// Open Banks |
// |
// |
////////// |
// |
// |
// |
// Let's keep track of any open banks. There are 8 of them to keep track of. |
// |
// A precharge requires 3 clocks at 200MHz to complete, 2 clocks at 100MHz. |
// |
// A precharge requires 3 clocks at 200MHz to complete. |
// An activate also requires 3 clocks at 200MHz to complete. |
// Precharges are not allowed until the maximum of: |
// 2 clocks (200 MHz) after a read command |
// 8 clocks after a write command |
// |
// |
reg need_refresh; |
|
wire w_precharge_all; |
reg [CKRP:0] bank_status [0:7]; |
reg [13:0] bank_address [0:7]; |
375,7 → 450,7
reg [7:0] bank_closed; |
|
wire [3:0] write_recycle_clocks; |
assign write_recycle_clocks = CKWR+4+4; |
assign write_recycle_clocks = 4'h8; |
|
initial bank_open = 0; |
initial bank_closed = 8'hff; |
447,15 → 522,15
bank_status[close_bank_cmd[16:14]] |
<= { bank_status[close_bank_cmd[16:14]][(CKRP-1):0], 1'b0 }; |
bank_open[close_bank_cmd[16:14]] <= 1'b0; |
// bank_status[close_bank_cmd[16:14]][0] <= 1'b0; |
end else if (need_open_bank) |
begin |
bank_status[activate_bank_cmd[16:14]] |
<= { bank_status[activate_bank_cmd[16:14]][(CKRP-1):0], 1'b1 }; |
// bank_status[activate_bank_cmd[16:14]][0] <= 1'b1; |
bank_closed[activate_bank_cmd[16:14]] <= 1'b0; |
end else if (valid_bank) |
; |
; // Read/write command was issued. This neither opens |
// nor closes any banks, and hence it needs no logic |
// here |
else if (maybe_close_next_bank) |
begin |
bank_status[maybe_close_cmd[16:14]] |
465,183 → 540,66
begin |
bank_status[maybe_open_cmd[16:14]] |
<= { bank_status[maybe_open_cmd[16:14]][(CKRP-1):0], 1'b1 }; |
// bank_status[activate_bank_cmd[16:14]][0] <= 1'b1; |
bank_closed[maybe_open_cmd[16:14]] <= 1'b0; |
end |
end |
|
always @(posedge i_clk) |
// if (cmd[22:19] == `DDR_ACTIVATE) |
if (w_this_opening_bank) |
bank_address[activate_bank_cmd[16:14]] |
<= activate_bank_cmd[13:0]; |
else if (!w_this_maybe_open) |
else if (w_this_maybe_open) |
bank_address[maybe_open_cmd[16:14]] |
<= maybe_open_cmd[13:0]; |
|
|
////////// |
// |
// |
// Okay, let's investigate when we need to do a refresh. Our plan will be to |
// do 4 refreshes every tREFI*4 seconds. tREFI = 7.8us, but its a parameter |
// in the number of clocks so that we can handle both 100MHz and 200MHz clocks. |
// Data BUS information |
// |
// Note that 160ns are needed between refresh commands (JEDEC, p172), or |
// 320 clocks @200MHz, or equivalently 160 clocks @100MHz. Thus to issue 4 |
// of these refresh cycles will require 4*320=1280 clocks@200 MHz. After this |
// time, no more refreshes will be needed for 6240 clocks. |
// |
// Let's think this through: |
// REFRESH_COST = (n*(320)+24)/(n*1560) |
// |
////////// |
// |
// |
reg refresh_ztimer; |
reg [16:0] refresh_counter; |
reg [2:0] refresh_addr; |
reg [23:0] refresh_instruction; |
always @(posedge i_clk) |
if (reset_override) |
refresh_addr <= 3'hf; |
else if (refresh_ztimer) |
refresh_addr <= refresh_addr + 3'h1; |
else if (refresh_instruction[`DDR_RFBEGIN]) |
refresh_addr <= 3'h0; |
|
always @(posedge i_clk) |
if (reset_override) |
begin |
refresh_ztimer <= 1'b1; |
refresh_counter <= 17'd0; |
end else if (!refresh_ztimer) |
begin |
refresh_ztimer <= (refresh_counter == 17'h1); |
refresh_counter <= (refresh_counter - 17'h1); |
end else if (refresh_instruction[`DDR_RFTIMER]) |
begin |
refresh_ztimer <= 1'b0; |
refresh_counter <= refresh_instruction[16:0]; |
end |
|
`ifdef QUADRUPLE_REFRESH |
// REFI4 = 13'd6240 |
wire [16:0] w_ckREFIn, w_ckREFRst, w_wait_for_idle, |
w_precharge_to_refresh; |
assign w_wait_for_idle = 5+CKCAS; |
assign w_precharge_to_refresh = CKRP-1; |
assign w_ckREFIn[(CKRBITS-1): 0] = CKREFI4-5*CKRFC-2-10; |
assign w_ckREFIn[ 16:(CKRBITS)] = 0; |
assign w_ckREFRst = CKRFC-2-12; |
|
always @(posedge i_clk) |
if (refresh_ztimer) |
case(refresh_addr)//NEED-RFC, HAVE-TIMER, |
4'h0: refresh_instruction <= { 3'h2, `DDR_NOOP, w_ckREFIn }; |
// 17'd10 = time to complete write, plus write recovery time |
// minus two (cause we can't count zero or one) |
// = WL+4+tWR-2 = 10 |
// = 5+4+3-2 = 10 |
4'h1: refresh_instruction <= { 3'h6, `DDR_NOOP, w_wait_for_idle }; |
4'h2: refresh_instruction <= { 3'h4, `DDR_PRECHARGE, 17'h0400 }; |
4'h3: refresh_instruction <= { 3'h6, `DDR_NOOP, w_precharge_to_refresh }; |
4'h4: refresh_instruction <= { 3'h4, `DDR_REFRESH, 17'h00 }; |
4'h5: refresh_instruction <= { 3'h6, `DDR_NOOP, w_ckRFC }; |
4'h6: refresh_instruction <= { 3'h4, `DDR_REFRESH, 17'h00 }; |
4'h7: refresh_instruction <= { 3'h6, `DDR_NOOP, w_ckRFC }; |
4'h8: refresh_instruction <= { 3'h4, `DDR_REFRESH, 17'h00 }; |
4'h9: refresh_instruction <= { 3'h6, `DDR_NOOP, w_ckRFC }; |
4'ha: refresh_instruction <= { 3'h4, `DDR_REFRESH, 17'h00 }; |
4'hb: refresh_instruction <= { 3'h6, `DDR_NOOP, w_ckRFC }; |
4'hc: refresh_instruction <= { 3'h2, `DDR_NOOP, w_ckREFRst }; |
default: |
refresh_instruction <= { 3'h1, `DDR_NOOP, 17'h00 }; |
endcase |
`else |
wire [16:0] w_ckREFI_left, w_ckRFC_nxt, w_wait_for_idle, |
w_precharge_to_refresh; |
assign w_wait_for_idle = 5+CKCAS; |
assign w_precharge_to_refresh = CKRP-2; |
assign w_ckREFI_left[16:0] = { 4'h0, CKREFI }-CKRFC-9 |
-w_wait_for_idle |
-w_precharge_to_refresh; |
// assign w_ckREFI_left[16:13] = 0; |
assign w_ckRFC_nxt[8:0] = CKRFC+9'h2; |
assign w_ckRFC_nxt[16:9] = 0; |
|
always @(posedge i_clk) |
if (refresh_ztimer) |
case(refresh_addr)//NEED-REFRESH, HAVE-TIMER, BEGIN(start-over) |
3'h0: refresh_instruction <= { 3'h2, `DDR_NOOP, w_ckREFI_left }; |
3'h1: refresh_instruction <= { 3'h6, `DDR_NOOP, w_wait_for_idle }; |
3'h2: refresh_instruction <= { 3'h4, `DDR_PRECHARGE, 17'h0400 }; |
3'h3: refresh_instruction <= { 3'h6, `DDR_NOOP, w_precharge_to_refresh }; |
3'h4: refresh_instruction <= { 3'h4, `DDR_REFRESH, 17'h00 }; |
3'h5: refresh_instruction <= { 3'h6, `DDR_NOOP, w_ckRFC_nxt }; |
default: |
refresh_instruction <= { 3'h1, `DDR_NOOP, 17'h00 }; |
endcase |
`endif |
|
always @(posedge i_clk) |
if (reset_override) |
refresh_cmd <= { `DDR_NOOP, w_ckREFI_left }; |
else if (refresh_ztimer) |
refresh_cmd <= refresh_instruction[20:0]; |
always @(posedge i_clk) |
if (reset_override) |
need_refresh <= 1'b0; |
else if (refresh_ztimer) |
need_refresh <= refresh_instruction[`DDR_NEEDREFRESH]; |
|
|
// Our purpose here is to keep track of when the data bus will be |
// active. This is separate from the FIFO which will contain the |
// data to be placed on the bus (when so placed), in that this is |
// a group of shift registers--every position has a location in time, |
// and time always moves forward. The FIFO, on the other hand, only |
// moves forward when data moves onto the bus. |
// |
// |
// Let's track: when will our bus be active? When will we be reading or |
// writing? |
// |
// |
|
reg [BUSNOW:0] bus_active, bus_read, bus_new, bus_ack; |
reg [1:0] bus_subaddr [BUSNOW:0]; |
reg [BUSNOW:0] bus_subaddr, bus_odt; |
initial bus_active = 0; |
initial bus_ack = 0; |
always @(posedge i_clk) |
begin |
bus_active[BUSNOW:0] <= { bus_active[(BUSNOW-1):0], 1'b0 }; |
bus_read[BUSNOW:0] <= { bus_read[(BUSNOW-1):0], 1'b0 }; // Drive the d-bus? |
// Drive the d-bus? |
bus_read[BUSNOW:0] <= { bus_read[(BUSNOW-1):0], 1'b0 }; |
// Is this a new command? i.e., the start of a transaction? |
bus_new[BUSNOW:0] <= { bus_new[(BUSNOW-1):0], 1'b0 }; |
bus_odt[BUSNOW:0] <= { bus_odt[(BUSNOW-1):0], 1'b0 }; |
// Will this position on the bus get a wishbone acknowledgement? |
bus_ack[BUSNOW:0] <= { bus_ack[(BUSNOW-1):0], 1'b0 }; |
//bus_mask[8:0] <= { bus_mask[7:0], 1'b1 }; // Write this value? |
bus_subaddr[8] <= bus_subaddr[7]; |
bus_subaddr[7] <= bus_subaddr[6]; |
bus_subaddr[6] <= bus_subaddr[5]; |
bus_subaddr[5] <= bus_subaddr[4]; |
bus_subaddr[4] <= bus_subaddr[3]; |
bus_subaddr[3] <= bus_subaddr[2]; |
bus_subaddr[2] <= bus_subaddr[1]; |
bus_subaddr[1] <= bus_subaddr[0]; |
bus_subaddr[0] <= 2'h3; |
// |
bus_subaddr[BUSNOW:0] <= { bus_subaddr[(BUSNOW-1):0], 1'b1 }; |
|
bus_ack[5] <= (bus_ack[4])&& |
((bus_subaddr[5] != bus_subaddr[4]) |
||(bus_new[4])); |
if (w_this_rw_move) |
begin |
bus_active[3:0]<= 4'hf; // Once per clock |
bus_subaddr[3] <= 2'h0; |
bus_subaddr[2] <= 2'h1; |
bus_subaddr[1] <= 2'h2; |
bus_active[1:0]<= 2'h3; // Data transfers in two clocks |
bus_subaddr[1] <= 1'h0; |
bus_new[{ 2'b0, rw_sub }] <= 1'b1; |
bus_ack[3:0] <= 4'h0; |
bus_ack[1:0] <= 2'h0; |
bus_ack[{ 2'b0, rw_sub }] <= 1'b1; |
|
bus_read[3:0] <= (rw_we)? 4'h0:4'hf; |
bus_read[1:0] <= (rw_we)? 2'h0:2'h3; |
bus_odt[3:0]<= (rw_we)? 4'he:4'h0; // Data transfers in 2 clks |
end else if ((s_pending)&&(!pipe_stall)) |
begin |
if (bus_subaddr[3] == s_sub) |
bus_ack[4] <= 1'b1; |
if (bus_subaddr[2] == s_sub) |
bus_ack[3] <= 1'b1; |
if (bus_subaddr[1] == s_sub) |
bus_ack[2] <= 1'b1; |
if (bus_subaddr[0] == s_sub) |
651,8 → 609,12
|
// Need to set o_wb_dqs high one clock prior to any read. |
always @(posedge i_clk) |
drive_dqs <= (|bus_active[BUSREG:(BUSREG-1)]) |
&&(~(|bus_read[BUSREG:(BUSREG-1)])); |
begin |
drive_dqs[1] <= (bus_active[(BUSREG)]) |
&&(!bus_read[(BUSREG)]); |
drive_dqs[0] <= (bus_active[BUSREG:(BUSREG-1)] != 2'b00) |
&&(bus_read[BUSREG:(BUSREG-1)] == 2'b00); |
end |
|
// |
// |
665,10 → 627,6
pre_valid <= 1'b0; |
else if (need_refresh) |
pre_valid <= 1'b0; |
else if (w_this_rw_move) |
pre_valid <= 1'b0; |
else if (bus_active[0]) |
pre_valid <= 1'b0; |
else |
pre_valid <= 1'b1; |
|
683,7 → 641,7
assign w_s_match = (s_pending)&&(r_pending)&&(r_we == s_we) |
&&(r_row == s_row)&&(r_bank == s_bank) |
&&(r_col == s_col) |
&&(r_sub > s_sub); |
&&(r_sub)&&(!s_sub); |
|
reg pipe_stall; |
always @(posedge i_clk) |
697,8 → 655,8
pipe_stall <= (r_pending)&&(((!w_r_valid)||(valid_bank))&&(!w_s_match)); |
o_wb_stall <= (r_pending)&&(((!w_r_valid)||(valid_bank))&&(!w_s_match)); |
end else begin // if (pipe_stall) |
pipe_stall <= (s_pending)&&((!w_s_valid)||(valid_bank)||(r_move)||(last_valid_bank)); |
o_wb_stall <= (s_pending)&&((!w_s_valid)||(valid_bank)||(r_move)||(last_valid_bank)); |
pipe_stall <= (s_pending)&&((!w_s_valid)||(valid_bank)); |
o_wb_stall <= (s_pending)&&((!w_s_valid)||(valid_bank)); |
end |
if (need_refresh) |
o_wb_stall <= 1'b1; |
708,14 → 666,22
r_we <= i_wb_we; |
r_addr <= i_wb_addr; |
r_data <= i_wb_data; |
r_row <= i_wb_addr[25:12]; |
r_bank <= i_wb_addr[11:9]; |
r_col <= { i_wb_addr[8:2], 3'b000 }; // 9:2 |
r_sub <= i_wb_addr[1:0]; |
r_row <= i_wb_addr[24:11]; // 14 bits row address |
r_bank <= i_wb_addr[10:8]; |
r_col <= { i_wb_addr[7:1], 3'b000 }; // 10 bits Caddr |
r_sub <= i_wb_addr[0]; // Select which 64-bit word |
r_sel <= i_wb_sel; |
|
// i_wb_addr[0] is the 8-bit byte selector of 16-bits (ignored) |
// i_wb_addr[1] is the 16-bit half-word selector of 32-bits (ignored) |
// i_wb_addr[2] is the 32-bit word selector of 64-bits (ignored) |
// i_wb_addr[3] is the 64-bit long word selector of 128-bits |
|
// pre-emptive work |
r_nxt_row <= (i_wb_addr[11:9]==3'h7)?i_wb_addr[25:12]+14'h1:i_wb_addr[25:12]; |
r_nxt_bank <= i_wb_addr[11:9]+3'h1; |
r_nxt_row <= (i_wb_addr[10:8]==3'h7) |
? (i_wb_addr[24:11]+14'h1) |
: i_wb_addr[24:11]; |
r_nxt_bank <= i_wb_addr[10:8]+3'h1; |
end |
|
if (~pipe_stall) |
728,12 → 694,11
s_bank <= r_bank; |
s_col <= r_col; |
s_sub <= r_sub; |
s_sel <= (r_we)?(~r_sel):8'h00; |
|
// pre-emptive work |
s_nxt_row <= r_nxt_row; |
s_nxt_bank <= r_nxt_bank; |
|
// s_match <= w_s_match; |
end |
end |
|
779,9 → 744,8
|
|
valid_bank <= ((w_r_valid)||((pipe_stall)&&(w_s_valid))) |
&&(!last_valid_bank)&&(!r_move) |
// &&(!last_valid_bank)&&(!r_move) |
&&(!w_this_rw_move); |
last_valid_bank <= r_move; |
|
if ((s_pending)&&(pipe_stall)) |
rw_cmd[`DDR_CSBIT:`DDR_WEBIT] <= (s_we)?`DDR_WRITE:`DDR_READ; |
794,9 → 758,9
else |
rw_cmd[`DDR_WEBIT-1:0] <= { r_bank, 3'h0, 1'b0, r_col }; |
if ((s_pending)&&(pipe_stall)) |
rw_sub <= 2'b11 - s_sub; |
rw_sub <= 1'b1 - s_sub; |
else |
rw_sub <= 2'b11 - r_sub; |
rw_sub <= 1'b1 - r_sub; |
if ((s_pending)&&(pipe_stall)) |
rw_we <= s_we; |
else |
854,38 → 818,49
last_closing_bank <= 1'b0; |
last_maybe_open <= 1'b0; |
last_maybe_close <= 1'b0; |
r_move <= 1'b0; |
if (maintenance_override) // Command from either reset or |
cmd <= maintenance_cmd; // refresh logic |
else if (need_close_bank) |
cmd_a <= { `DDR_NOOP, 17'h00 }; |
cmd_b <= { `DDR_NOOP, rw_cmd[(`DDR_WEBIT-1):0] }; |
|
if (maintenance_override) |
begin // Command from either reset or refresh logic |
cmd_a <= maintenance_cmd; |
// cmd_b <= { `DDR_NOOP, ... |
end else if (need_close_bank) |
begin |
cmd <= close_bank_cmd; |
cmd_a <= close_bank_cmd; |
// cmd_b <= { `DDR_NOOP, ...} |
last_closing_bank <= 1'b1; |
end else if (need_open_bank) |
begin |
cmd <= activate_bank_cmd; |
cmd_a <= activate_bank_cmd; |
// cmd_b <={`DDR_NOOP, ...} |
last_opening_bank <= 1'b1; |
end else if (valid_bank) |
begin |
cmd <= rw_cmd; |
r_move <= 1'b1; |
cmd_a <= {(rw_cmd[(`DDR_WEBIT)])?`DDR_READ:`DDR_NOOP, |
rw_cmd[(`DDR_WEBIT-1):0] }; |
cmd_b <= {(rw_cmd[(`DDR_WEBIT)])?`DDR_NOOP:`DDR_WRITE, |
rw_cmd[(`DDR_WEBIT-1):0] }; |
end else if (maybe_close_next_bank) |
begin |
cmd <= maybe_close_cmd; |
cmd_a <= maybe_close_cmd; |
// cmd_b <= {`DDR_NOOP, ... } |
last_maybe_close <= 1'b1; |
end else if (maybe_open_next_bank) |
begin |
cmd <= maybe_open_cmd; |
cmd_a <= maybe_open_cmd; |
// cmd_b <= {`DDR_NOOP, ... } |
last_maybe_open <= 1'b1; |
end else |
cmd <= { `DDR_NOOP, rw_cmd[(`DDR_WEBIT-1):0] }; |
cmd_a <= { `DDR_NOOP, rw_cmd[(`DDR_WEBIT-1):0] }; |
end |
|
`define LGFIFOLN 4 |
`define FIFOLEN 16 |
reg [(`LGFIFOLN-1):0] bus_fifo_head, bus_fifo_tail; |
reg [31:0] bus_fifo_data [0:(`FIFOLEN-1)]; |
reg [1:0] bus_fifo_sub [0:(`FIFOLEN-1)]; |
reg [63:0] bus_fifo_data [0:(`FIFOLEN-1)]; |
reg [7:0] bus_fifo_sel [0:(`FIFOLEN-1)]; |
reg bus_fifo_sub [0:(`FIFOLEN-1)]; |
reg bus_fifo_new [0:(`FIFOLEN-1)]; |
reg pre_ack; |
|
895,68 → 870,47
always @(posedge i_clk) |
begin |
pre_ack <= 1'b0; |
o_ddr_dm <= 1'b0; |
if (reset_override) |
begin |
bus_fifo_head <= {(`LGFIFOLN){1'b0}}; |
bus_fifo_tail <= {(`LGFIFOLN){1'b0}}; |
o_ddr_dm <= 1'b0; |
end else begin |
if ((s_pending)&&(!pipe_stall)) |
bus_fifo_head <= bus_fifo_head + 1'b1; |
|
o_ddr_dm <= (bus_active[BUSREG])&&(!bus_read[BUSREG]); |
if (w_bus_fifo_read_next_transaction) |
begin |
bus_fifo_tail <= bus_fifo_tail + 1'b1; |
pre_ack <= 1'b1; |
o_ddr_dm <= 1'b0; |
end |
end |
bus_fifo_data[bus_fifo_head] <= s_data; |
bus_fifo_sub[bus_fifo_head] <= s_sub; |
bus_fifo_new[bus_fifo_head] <= w_this_rw_move; |
|
// |
// if ((s_pending)&&(!pipe_stall)&&(!nxt_valid)) |
// nxt_fifo_data <= s_data; |
// nxt_fifo_sub <= s_sub; |
// nxt_fifo_new <= w_this_rw_move; |
// nxt_valid <= 1'b1; |
// bus_fifo_head <= bus_fifo_head+1; |
// bus_fifo_tail <= bus_fifo_tail+1; |
// else if (w_bus_fifo_read_next_transaction) |
// nxt_fifo_data <= bus_fifo_data[bus_fifo_tail] |
// nxt_fifo_sub <= bus_fifo_data[bus_fifo_tail] |
// nxt_fifo_new <= bus_fifo_data[bus_fifo_tail] |
// nxt_valid <= (bus_fifo_tail+1 == bus_fifo_head); |
// |
// if ((!valid)||(w_bus_fifo_next_read_transaction)) |
// nxt_ <= bus_fifo_x |
bus_fifo_sel[bus_fifo_head] <= s_sel; |
end |
|
|
assign o_ddr_cs_n = cmd[`DDR_CSBIT]; |
assign o_ddr_ras_n = cmd[`DDR_RASBIT]; |
assign o_ddr_cas_n = cmd[`DDR_CASBIT]; |
assign o_ddr_we_n = cmd[`DDR_WEBIT]; |
assign o_ddr_dqs = drive_dqs; |
assign o_ddr_addr = cmd[(`DDR_ADDR_BITS-1):0]; |
assign o_ddr_ba = cmd[(`DDR_BABITS+`DDR_ADDR_BITS-1):`DDR_ADDR_BITS]; |
always @(posedge i_clk) |
o_ddr_data <= bus_fifo_data[bus_fifo_tail]; |
assign w_precharge_all = (cmd[`DDR_CSBIT:`DDR_WEBIT]==`DDR_PRECHARGE) |
&&(o_ddr_addr[10]); // 5 bits |
always @(posedge i_clk) |
ddr_dm <= (bus_ack[BUSREG])? bus_fifo_sel[bus_fifo_tail] |
: ((!bus_read[BUSREG])? 8'hff: 8'h00); |
always @(posedge i_clk) |
o_ddr_bus_oe <= (bus_active[BUSREG])&&(!bus_read[BUSREG]); |
|
assign o_ddr_bus_oe = drive_dqs; // ~bus_read[BUSNOW]; |
// First, or left, command |
assign o_ddr_cmd_a = { cmd_a, drive_dqs[1], ddr_dm[7:4], ddr_odt }; |
// Second, or right, command of two |
assign o_ddr_cmd_b = { cmd_b, drive_dqs[0], ddr_dm[3:0], ddr_odt }; |
|
assign w_precharge_all = (cmd_a[`DDR_CSBIT:`DDR_WEBIT]==`DDR_PRECHARGE) |
&&(cmd_a[10]); |
|
// ODT must be in high impedence while reset_n=0, then it can be set |
// to low or high. As per spec, ODT = 0 during reads |
always @(posedge i_clk) |
o_ddr_odt <= (bus_active[BUSREG-3])&&(!bus_read[BUSREG-3]) |
||(bus_active[BUSREG-4])&&(!bus_read[BUSREG-4]) |
||((w_this_rw_move)&&(rw_we)&&(CKCAS<4)) |
||(CKCAS>3)&&(bus_active[BUSREG-5])&&(!bus_read[BUSREG-5]); |
ddr_odt <= bus_odt[BUSREG]; |
|
always @(posedge i_clk) |
o_wb_ack <= pre_ack; |
/xioddrserdes.v
0,0 → 1,134
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: xioddrserdes.v |
// |
// Project: A wishbone controlled DDR3 SDRAM memory controller. |
// |
// Purpose: |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY 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. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
module xioddrserdes(i_clk_fast, i_clk_fast_inv, i_clk_slow, i_reset, |
i_oe, i_data, o_data, io_pin); |
input i_clk_fast, i_clk_fast_inv, i_clk_slow, i_reset; |
input i_oe; |
input [7:0] i_data; |
output wire [7:0] o_data; |
inout io_pin; |
|
wire feedback; |
wire oe_for_fabric__unconnected; |
wire [1:0] local_shiftout__unconnected; |
wire local_tbyte_out__unconnected; |
wire send_to_iob; |
wire oe_for_iob; |
wire [1:0] iserdes_shiftout__unconnected; |
|
OSERDESE2 #( |
.DATA_RATE_OQ("DDR"), |
.DATA_RATE_TQ("BUF"), |
.DATA_WIDTH(8), // 8 data wires sent per clkdiv |
.INIT_OQ(1'b1), |
.SERDES_MODE("MASTER"), |
// |
.TRISTATE_WIDTH(1), |
.INIT_TQ(1'b1), |
.TBYTE_CTL("FALSE"), |
.TBYTE_SRC("FALSE") |
) oserdes_i( |
.CLK(i_clk_fast), |
.CLKDIV(i_clk_slow), |
.OCE(1'b1), |
.OFB(feedback), |
.OQ(send_to_iob), |
.RST(i_reset), |
// |
.TCE(1'b1), |
.TQ(oe_for_iob), |
.TFB(oe_for_fabric__unconnected), |
.T1(~i_oe), .T2(~i_oe), .T3(~i_oe), .T4(~i_oe), |
// |
.SHIFTOUT1(local_shiftout__unconnected[0]), |
.SHIFTOUT2(local_shiftout__unconnected[1]), |
.SHIFTIN1(1'b0), |
.SHIFTIN2(1'b0), |
.TBYTEIN(1'b0), |
.TBYTEOUT(local_tbyte_out__unconnected), |
// |
// And now for the actual data we wish to send |
// |
.D1(i_data[0]), .D2(i_data[1]), |
.D3(i_data[2]), .D4(i_data[3]), |
.D5(i_data[4]), .D6(i_data[5]), |
.D7(i_data[6]), .D8(i_data[7]) |
); |
|
IOBUF iobuf_i( |
.T(oe_for_iob), |
.I(send_to_iob), |
.IO(io_pin), |
.O(input_from_iobuf)); |
|
ISERDESE2 #( |
.DATA_RATE("DDR"), |
.DATA_WIDTH(8), // 8 data wires sent per clkdiv |
.INTERFACE_TYPE("OVERSAMPLE"), |
.IOBDELAY("NONE"), |
.NUM_CE(1), |
.OFB_USED("FALSE"), |
.SERDES_MODE("MASTER") |
) iserdes_i( |
.BITSLIP(1'b0), |
.CE1(1'b1), |
.CE2(1'b1), |
.CLK(i_clk_fast), |
.CLKB(i_clk_fast_inv), |
.CLKDIV(i_clk_slow), |
.CLKDIVP(1'b0), // Only used in MEMORY_DDR3 mode? |
.D(input_from_iobuf), |
.DDLY(1'b0), |
.DYNCLKDIVSEL(1'b0), |
.DYNCLKSEL(1'b0), |
.O(iserdes_unconnected_output), |
.OCLK(i_clk_fast), |
.OCLKB(i_clk_fast_inv), |
.OFB(feedback), |
.Q1(o_data[0]), .Q2(o_data[1]), |
.Q3(o_data[2]), .Q4(o_data[3]), |
.Q5(o_data[4]), .Q6(o_data[5]), |
.Q7(o_data[6]), .Q8(o_data[7]), |
.RST(i_reset), |
.SHIFTIN1(1'b0), .SHIFTIN2(1'b0), |
.SHIFTOUT1(iserdes_shiftout__unconnected[0]), |
.SHIFTOUT2(iserdes_shiftout__unconnected[1]) |
); |
|
endmodule |
|
/fastmaster.v
90,10 → 90,8
// The Quad SPI Flash |
o_qspi_cs_n, o_qspi_sck, o_qspi_dat, i_qspi_dat, o_qspi_mod, |
// The DDR3 SDRAM |
o_ddr_reset_n, o_ddr_cke, |
o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n, o_ddr_we_n, |
o_ddr_dqs, o_ddr_dm, o_ddr_odt, o_ddr_bus_oe, |
o_ddr_addr, o_ddr_ba, o_ddr_data, i_ddr_data, |
o_ddr_reset_n, o_ddr_cke, o_ddr_bus_oe, |
o_ddr_cmd_a, o_ddr_cmd_b, o_ddr_data, i_ddr_data, |
// The SD Card |
o_sd_sck, o_sd_cmd, o_sd_data, i_sd_cmd, i_sd_data, i_sd_detect, |
// Ethernet control (MDIO) lines |
128,14 → 126,10
input [3:0] i_qspi_dat; |
output wire [1:0] o_qspi_mod; |
// DDR3 RAM controller |
output wire o_ddr_reset_n, o_ddr_cke, |
o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n,o_ddr_we_n; |
output wire o_ddr_dqs; |
output wire o_ddr_dm, o_ddr_odt, o_ddr_bus_oe; |
output wire [13:0] o_ddr_addr; |
output wire [2:0] o_ddr_ba; |
output wire [31:0] o_ddr_data; |
input [31:0] i_ddr_data; |
output wire o_ddr_reset_n, o_ddr_cke, o_ddr_bus_oe; |
output wire [26:0] o_ddr_cmd_a, o_ddr_cmd_b; |
output wire [63:0] o_ddr_data; |
input [63:0] i_ddr_data; |
// The SD Card |
output wire o_sd_sck; |
output wire o_sd_cmd; |
915,13 → 909,16
// |
// |
`ifdef SDRAM_ACCESS |
wire [63:0] w_ram_wide_data; |
wbddrsdram #(13,13'd1520) rami(i_clk, i_rst, |
wb_cyc, (wb_stb)&&(ram_sel), wb_we, wb_addr[25:0], wb_data, |
ram_ack, ram_stall, ram_data, |
o_ddr_reset_n, o_ddr_cke, |
o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n, o_ddr_we_n, |
o_ddr_dqs, o_ddr_dm, o_ddr_odt, o_ddr_bus_oe, |
o_ddr_addr, o_ddr_ba, o_ddr_data, i_ddr_data); |
wb_cyc, (wb_stb)&&(ram_sel), wb_we, wb_addr[24:0], |
{ wb_data, wb_data }, (wb_addr[25])? 8'hf0:8'h0f, |
ram_ack, ram_stall, w_ram_wide_data, |
o_ddr_reset_n, o_ddr_cke, o_ddr_bus_oe, |
o_ddr_cmd_a, o_ddr_cmd_b, o_ddr_data, i_ddr_data); |
|
// assign ram_data = (wb_addr[25])?w_ram_wide_data[63:32]:w_ram_wide_data[ |
assign ram_data = w_ram_wide_data[31:0]; |
`else |
assign ram_data = 32'h00; |
assign ram_stall = 1'b0; |
/fasttop.v
110,21 → 110,22
|
`define FULLCLOCK |
// Build our master clock |
wire i_clk, clk_for_ddr, clk2_unused, enet_clk, clk_analyzer, |
clk_feedback, clk_locked, clk_analyzer_b; |
wire i_clk, clk_for_ddr, mem_serial_clk, mem_serial_clk_inv, |
enet_clk, clk_halfspeed, clk_feedback, clk_locked, clk_unused; |
PLLE2_BASE #( |
.BANDWIDTH("OPTIMIZED"), // OPTIMIZED, HIGH, LOW |
.CLKFBOUT_PHASE(0.0), // Phase offset in degrees of CLKFB, (-360-360) |
.CLKIN1_PERIOD(10.0), // Input clock period in ns to ps resolution |
.CLKFBOUT_PHASE(0.0), // Phase off. in deg of CLKFB,(-360-360) |
.CLKIN1_PERIOD(10.0), // Input clock period in ns resolution |
`ifdef FULLCLOCK |
// CLKOUT0_DIVIDE - CLKOUT5_DIVIDE: divide amount for each CLKOUT(1-128) |
// CLKOUT0_DIVIDE - CLKOUT5_DIVIDE: |
// divide amount for each CLKOUT(1-128) |
.CLKFBOUT_MULT(8), // Multiply value for all CLKOUT (2-64) |
.CLKOUT0_DIVIDE(4), // 200 MHz |
.CLKOUT1_DIVIDE(4), // 200 MHz clock for DDR memory |
.CLKOUT2_DIVIDE(8), // 100 MHz |
.CLKOUT3_DIVIDE(32), // 25 MHz |
.CLKOUT4_DIVIDE(1), // 800 MHz |
.CLKOUT5_DIVIDE(1), |
.CLKOUT1_DIVIDE(1), // 800 MHz clock for DDR memory |
.CLKOUT2_DIVIDE(1), // 800 MHz clock to run DDR I/O |
.CLKOUT3_DIVIDE(1), // 800MHz clk inv to run DDR I/O |
.CLKOUT4_DIVIDE(8), // 100 MHz |
.CLKOUT5_DIVIDE(32), // 25 MHz |
`else |
// 100*64/40 = 160 -- the fastest speed where the UART will |
// still work at 4MBaud. Others will still support 115200 |
135,10 → 136,10
.CLKFBOUT_MULT(8), // Multiply value for all CLKOUT (2-64) |
.CLKOUT0_DIVIDE(5), // 160 MHz |
.CLKOUT1_DIVIDE(5), // 160 MHz //Clock too slow for DDR mem |
.CLKOUT2_DIVIDE(10), // 80 MHz |
.CLKOUT3_DIVIDE(40), // 20 MHz |
.CLKOUT4_DIVIDE(1), // 40 MHz |
.CLKOUT5_DIVIDE(1), |
.CLKOUT2_DIVIDE(5), // 160 MHz // Clock too slow for DDR |
.CLKOUT3_DIVIDE(5), // 160 MHz // Clock too slow for DDR |
.CLKOUT4_DIVIDE(20), // 40 MHz |
.CLKOUT5_DIVIDE(5), |
`endif |
// CLKOUT0_DUTY_CYCLE -- Duty cycle for each CLKOUT |
.CLKOUT0_DUTY_CYCLE(0.5), |
151,20 → 152,20
.CLKOUT0_PHASE(0.0), |
.CLKOUT1_PHASE(270.0), |
.CLKOUT2_PHASE(0.0), |
.CLKOUT3_PHASE(0.0), |
.CLKOUT3_PHASE(180.0), |
.CLKOUT4_PHASE(0.0), |
.CLKOUT5_PHASE(180.0), |
.CLKOUT5_PHASE(0.0), |
.DIVCLK_DIVIDE(1), // Master division value , (1-56) |
.REF_JITTER1(0.0), // Reference input jitter in UI (0.000-0.999) |
.STARTUP_WAIT("FALSE") // Delayu DONE until PLL Locks, ("TRUE"/"FALSE") |
.REF_JITTER1(0.0), // Ref. input jitter in UI (0.000-0.999) |
.STARTUP_WAIT("TRUE") // Delay DONE until PLL Locks, ("TRUE"/"FALSE") |
) genclock( |
// Clock outputs: 1-bit (each) output |
.CLKOUT0(i_clk), |
.CLKOUT1(clk_for_ddr), |
.CLKOUT2(clk2_unused), // Reserved for flash, should we need it |
.CLKOUT3(enet_clk), |
.CLKOUT4(clk_analyzer), |
.CLKOUT5(clk_analyzer_b), |
.CLKOUT2(mem_serial_clk), |
.CLKOUT3(mem_serial_clk_inv), |
.CLKOUT4(clk_unused), |
.CLKOUT5(enet_clk), |
.CLKFBOUT(clk_feedback), // 1-bit output, feedback clock |
.LOCKED(clk_locked), |
.CLKIN1(i_clk_100mhz), |
186,11 → 187,27
wire rx_break, rx_parity_err, rx_frame_err, rx_stb; |
wire tx_stb, tx_busy; |
|
// |
// RESET LOGIC |
// |
// Okay, so this looks bad at a first read--but it's not really that |
// bad. If you look close, there are two parts to the reset logic. |
// The first is the "PRE"-reset. This is a wire, set from the external |
// reset button. In good old-fashioned asynch-logic to synchronous |
// logic fashion, we synchronize this wire by registering it first |
// to pre_reset, and then to pwr_reset (the actual reset wire). |
// |
reg pwr_reset, pre_reset; |
initial pwr_reset = 1'b1; |
// |
// Logic description starts with the PRE-reset, so as to make certain |
// we include the reset button |
initial pre_reset = 1'b0; |
always @(posedge i_clk) |
pre_reset <= ~i_reset_btn; |
// |
// and then continues with the actual reset, now that we've |
// synchronized our reset button wire. |
initial pwr_reset = 1'b1; |
always @(posedge i_clk) |
pwr_reset <= pre_reset; |
|
204,8 → 221,173
|
|
|
`ifdef SDRAM_ACCESS |
/// |
/// |
/// The following lines are included from ddr3insert.v. |
/// |
wire w_ddr_reset_n, w_ddr_cke, w_ddr_bus_oe; |
wire [26:0] w_ddr_cmd_a, w_ddr_cmd_b; |
wire [63:0] wi_ddr_data, wo_ddr_data; |
wire [127:0] wide_ddr_data; |
|
// |
// |
// Wires for setting up the DDR3 memory |
// |
// |
|
// First, let's set up the clock(s) |
xoddrserdesb ddrclk(mem_serial_clk, i_clk, pwr_reset, 8'h66, |
o_ddr_ck_p, o_ddr_ck_n); |
|
wire [7:0] w_udqs_in, w_ldqs_in; |
|
xioddrserdesb ddrudqs(mem_serial_clk, mem_serial_clk_inv, i_clk, |
~w_ddr_reset_n, w_ddr_cmd_a[0], |
(w_ddr_cmd_b[0])? 8'h66 : 8'h06, |
w_udqs_in, |
io_ddr_dqs_p[1], io_ddr_dqs_n[1]); |
|
xioddrserdesb ddrldqs(mem_serial_clk, mem_serial_clk_inv, i_clk, |
~w_ddr_reset_n, w_ddr_cmd_a[0], |
(w_ddr_cmd_b[0])? 8'h66 : 8'h06, |
w_ldqs_in, |
io_ddr_dqs_p[0], io_ddr_dqs_n[0]); |
|
// The command wires: CS_N, RAS_N, CAS_N, and WE_N |
xoddrserdes ddrcsn(mem_serial_clk, i_clk, ~w_ddr_reset_n, |
{ w_ddr_cmd_a[26], w_ddr_cmd_a[26], |
w_ddr_cmd_a[26], w_ddr_cmd_a[26], |
w_ddr_cmd_b[26], w_ddr_cmd_b[26], |
w_ddr_cmd_b[26], w_ddr_cmd_b[26] }, o_ddr_cs_n); |
|
xoddrserdes ddrrasn(mem_serial_clk, i_clk, ~w_ddr_reset_n, |
{ w_ddr_cmd_a[25], w_ddr_cmd_a[25], |
w_ddr_cmd_a[25], w_ddr_cmd_a[25], |
w_ddr_cmd_b[25], w_ddr_cmd_b[25], |
w_ddr_cmd_b[25], w_ddr_cmd_b[25] }, o_ddr_ras_n); |
|
xoddrserdes ddrcasn(mem_serial_clk, i_clk, ~w_ddr_reset_n, |
{ w_ddr_cmd_a[24], w_ddr_cmd_a[24], |
w_ddr_cmd_a[24], w_ddr_cmd_a[24], |
w_ddr_cmd_b[24], w_ddr_cmd_b[24], |
w_ddr_cmd_b[24], w_ddr_cmd_b[24] }, o_ddr_cas_n); |
|
xoddrserdes ddrwen(mem_serial_clk, i_clk, ~w_ddr_reset_n, |
{ w_ddr_cmd_a[23], w_ddr_cmd_a[23], |
w_ddr_cmd_a[23], w_ddr_cmd_a[23], |
w_ddr_cmd_b[23], w_ddr_cmd_b[23], |
w_ddr_cmd_b[23], w_ddr_cmd_b[23] }, o_ddr_we_n); |
|
// Data mask wires, first the upper byte |
xoddrserdes ddrudm(mem_serial_clk, i_clk, ~w_ddr_reset_n, |
{ w_ddr_cmd_a[4], w_ddr_cmd_a[4], |
w_ddr_cmd_a[2], w_ddr_cmd_a[2], |
w_ddr_cmd_b[4], w_ddr_cmd_b[4], |
w_ddr_cmd_b[2], w_ddr_cmd_b[2] }, o_ddr_dm[1]); |
// then the lower byte |
xoddrserdes ddrldm(mem_serial_clk, i_clk, ~w_ddr_reset_n, |
{ w_ddr_cmd_a[3], w_ddr_cmd_a[3], |
w_ddr_cmd_a[1], w_ddr_cmd_a[1], |
w_ddr_cmd_b[3], w_ddr_cmd_b[3], |
w_ddr_cmd_b[1], w_ddr_cmd_b[1] }, o_ddr_dm[0]); |
|
// and the On-Die termination wire |
xoddrserdes ddrodt(mem_serial_clk, i_clk, ~w_ddr_reset_n, |
{ w_ddr_cmd_a[0], w_ddr_cmd_a[0], |
w_ddr_cmd_a[0], w_ddr_cmd_a[0], |
w_ddr_cmd_b[0], w_ddr_cmd_b[0], |
w_ddr_cmd_b[0], w_ddr_cmd_b[0] }, o_ddr_odt); |
|
// |
// Now for the data, bank, and address wires |
// |
genvar k; |
generate begin |
// |
for(k=0; k<16; k=k+1) |
xioddrserdes ddrdata(mem_serial_clk, mem_serial_clk_inv, i_clk, ~w_ddr_reset_n, |
w_ddr_bus_oe, |
{ wo_ddr_data[48+k], wo_ddr_data[48+k], |
wo_ddr_data[32+k], wo_ddr_data[32+k], |
wo_ddr_data[16+k], wo_ddr_data[16+k], |
wo_ddr_data[ k], wo_ddr_data[ k] }, |
{ wide_ddr_data[112+k], wide_ddr_data[96+k], |
wide_ddr_data[ 80+k], wide_ddr_data[64+k], |
wide_ddr_data[ 48+k], wide_ddr_data[32+k], |
wide_ddr_data[ 16+k], wide_ddr_data[ k] }, |
io_ddr_data[k]); |
// |
for(k=0; k<3; k=k+1) |
xoddrserdes ddrbank(mem_serial_clk, i_clk, ~w_ddr_reset_n, |
{ w_ddr_cmd_a[20+k], w_ddr_cmd_a[20+k], |
w_ddr_cmd_a[20+k], w_ddr_cmd_a[20+k], |
w_ddr_cmd_b[20+k], w_ddr_cmd_b[20+k], |
w_ddr_cmd_b[20+k], w_ddr_cmd_b[20+k] }, |
o_ddr_ba[k]); |
// |
for(k=0; k<14; k=k+1) |
xoddrserdes ddraddr(mem_serial_clk, i_clk, ~w_ddr_reset_n, |
{ w_ddr_cmd_a[ 6+k], w_ddr_cmd_a[ 6+k], |
w_ddr_cmd_a[ 6+k], w_ddr_cmd_a[ 6+k], |
w_ddr_cmd_b[ 6+k], w_ddr_cmd_b[ 6+k], |
w_ddr_cmd_b[ 6+k], w_ddr_cmd_b[ 6+k] }, |
o_ddr_addr[k]); |
// |
|
for(k=0; k<64; k=k+1) |
assign wi_ddr_data[k] = (w_ddr_bus_oe) ? wide_ddr_data[2*k+1] |
: wide_ddr_data[2*k]; |
end endgenerate |
|
assign o_ddr_reset_n = w_ddr_reset_n; |
assign o_ddr_cke = w_ddr_cke; |
|
|
/// |
/// |
/// |
/// |
`else |
wire w_ddr_reset_n, w_ddr_cke, w_ddr_bus_oe; |
wire [26:0] w_ddr_cmd_a, w_ddr_cmd_b; |
wire [63:0] wi_ddr_data, wo_ddr_data; |
wire [127:0] wide_ddr_data; |
|
// |
// |
// Wires for setting up the DDR3 memory |
// |
// |
|
// Leave the SDRAM in a permanent state of reset |
assign o_ddr_reset_n = 1'b0; |
// Leave the SDRAM clock ... disabled |
assign o_ddr_cke = 1'b0; |
|
// Disable the clock(s) |
OBUFDS(.I(1'b0), .O(o_ddr_ck_p), .OB(o_ddr_ck_n)); |
// And the data strobe |
OBUFDS(.I(1'b0), .O(io_ddr_dqs_p[0]), .OB(io_ddr_dqs_n[0])); |
OBUFDS(.I(1'b0), .O(io_ddr_dqs_p[1]), .OB(io_ddr_dqs_n[1])); |
|
// Output ... something, anything, on the address lines |
assign o_ddr_cs_n = 1'b1; // Never enable any commands |
assign o_ddr_ras_n = 1'b0; |
assign o_ddr_cas_n = 1'b0; |
assign o_ddr_we_n = 1'b0; |
assign o_ddr_ba = 3'h0; |
assign o_ddr_addr = 14'h0; |
assign o_ddr_dm = 2'b00; |
assign o_ddr_odt = 1'b0; |
|
assign io_ddr_data = 16'bzzzz_zzzz_zzzz_zzzz; |
assign wi_ddr_data = io_ddr_data; |
|
`endif |
|
|
////// |
// |
// |
220,14 → 402,6
wire [3:0] i_qspi_dat; |
|
// |
wire [31:0] wo_ddr_data, wi_ddr_data; |
wire w_ddr_dqs, w_ddr_dm, w_ddr_bus_oe, w_ddr_odt; |
wire [2:0] w_ddr_ba; |
wire w_ddr_cs_n, w_ddr_ras_n, w_ddr_cas_n, w_ddr_we_n; |
wire [13:0] w_ddr_addr; |
reg [31:0] r_ddr_data; |
|
// |
wire w_mdio, w_mdwe; |
// |
wire w_sd_cmd; |
243,10 → 417,8
// Quad SPI flash |
w_qspi_cs_n, w_qspi_sck, qspi_dat, i_qspi_dat, qspi_bmod, |
// DDR3 SDRAM |
o_ddr_reset_n, o_ddr_cke, |
w_ddr_cs_n, w_ddr_ras_n, w_ddr_cas_n, w_ddr_we_n, |
w_ddr_dqs, w_ddr_dm, w_ddr_odt, w_ddr_bus_oe, |
w_ddr_addr, w_ddr_ba, wo_ddr_data, r_ddr_data, |
w_ddr_reset_n, w_ddr_cke, w_ddr_bus_oe, |
w_ddr_cmd_a, w_ddr_cmd_b, wo_ddr_data, wi_ddr_data, |
// SD Card |
o_sd_sck, w_sd_cmd, w_sd_data, io_sd_cmd, io_sd, i_sd_cs, |
// Ethernet control (MDIO) lines |
330,90 → 502,8
// Wires for setting up the DDR3 memory |
// |
// |
`ifdef SDRAM_ACCESS |
reg [15:0] bottom_half_data; |
always @(posedge i_clk) |
bottom_half_data <= wo_ddr_data[15:0]; |
xioddr p0(i_clk, w_ddr_bus_oe, { wo_ddr_data[16], wo_ddr_data[0] }, |
{ wi_ddr_data[16], wi_ddr_data[0] }, io_ddr_data[0]); |
|
xioddr p1(i_clk, w_ddr_bus_oe, { wo_ddr_data[17], wo_ddr_data[1] }, |
{ wi_ddr_data[17], wi_ddr_data[1] }, io_ddr_data[1]); |
|
xioddr p2(i_clk, w_ddr_bus_oe, { wo_ddr_data[18], wo_ddr_data[2] }, |
{ wi_ddr_data[18], wi_ddr_data[2] }, io_ddr_data[2]); |
|
xioddr p3(i_clk, w_ddr_bus_oe, { wo_ddr_data[19], wo_ddr_data[3] }, |
{ wi_ddr_data[19], wi_ddr_data[3] }, io_ddr_data[3]); |
|
xioddr p4(i_clk, w_ddr_bus_oe, { wo_ddr_data[20], wo_ddr_data[4] }, |
{ wi_ddr_data[20], wi_ddr_data[4] }, io_ddr_data[4]); |
|
xioddr p5(i_clk, w_ddr_bus_oe, { wo_ddr_data[21], wo_ddr_data[5] }, |
{ wi_ddr_data[21], wi_ddr_data[5] }, io_ddr_data[5]); |
|
xioddr p6(i_clk, w_ddr_bus_oe, { wo_ddr_data[22], wo_ddr_data[6] }, |
{ wi_ddr_data[22], wi_ddr_data[6] }, io_ddr_data[6]); |
|
xioddr p7(i_clk, w_ddr_bus_oe, { wo_ddr_data[23], wo_ddr_data[7] }, |
{ wi_ddr_data[23], wi_ddr_data[7] }, io_ddr_data[7]); |
|
xioddr p8(i_clk, w_ddr_bus_oe, { wo_ddr_data[24], wo_ddr_data[8] }, |
{ wi_ddr_data[24], wi_ddr_data[8] }, io_ddr_data[8]); |
|
xioddr p9(i_clk, w_ddr_bus_oe, { wo_ddr_data[25], wo_ddr_data[9] }, |
{ wi_ddr_data[25], wi_ddr_data[9] }, io_ddr_data[9]); |
|
xioddr pa(i_clk, w_ddr_bus_oe, { wo_ddr_data[26], wo_ddr_data[10] }, |
{ wi_ddr_data[26], wi_ddr_data[10] }, io_ddr_data[10]); |
|
xioddr pb(i_clk, w_ddr_bus_oe, { wo_ddr_data[27], wo_ddr_data[11] }, |
{ wi_ddr_data[27], wi_ddr_data[11] }, io_ddr_data[11]); |
|
xioddr pc(i_clk, w_ddr_bus_oe, { wo_ddr_data[28], wo_ddr_data[12] }, |
{ wi_ddr_data[28], wi_ddr_data[12] }, io_ddr_data[12]); |
|
xioddr pd(i_clk, w_ddr_bus_oe, { wo_ddr_data[29], wo_ddr_data[13] }, |
{ wi_ddr_data[29], wi_ddr_data[13] }, io_ddr_data[13]); |
|
xioddr pe(i_clk, w_ddr_bus_oe, { wo_ddr_data[30], wo_ddr_data[14] }, |
{ wi_ddr_data[30], wi_ddr_data[14] }, io_ddr_data[14]); |
|
xioddr pf(i_clk, w_ddr_bus_oe, { wo_ddr_data[31], wo_ddr_data[15] }, |
{ wi_ddr_data[31], wi_ddr_data[15] }, io_ddr_data[15]); |
always @(posedge i_clk) |
r_ddr_data <= wi_ddr_data; |
|
wire [7:0] w_dqs_ignore; |
xioddrds dqs0(clk_for_ddr, w_ddr_dqs, { 1'b0, 1'b1 }, |
{ w_dqs_ignore[0], w_dqs_ignore[1] }, |
io_ddr_dqs_p[0], io_ddr_dqs_n[0]); |
xioddrds dqs1(clk_for_ddr, w_ddr_dqs, { 1'b0, 1'b1 }, |
{ w_dqs_ignore[2], w_dqs_ignore[3] }, |
io_ddr_dqs_p[1], io_ddr_dqs_n[1]); |
|
xoddr xcs_n( i_clk, { w_ddr_cs_n, w_ddr_cs_n }, o_ddr_cs_n); |
xoddr xras_n(i_clk, { w_ddr_ras_n, w_ddr_ras_n }, o_ddr_ras_n); |
xoddr xcas_n(i_clk, { w_ddr_cas_n, w_ddr_cas_n }, o_ddr_cas_n); |
xoddr xwe_n( i_clk, { w_ddr_we_n, w_ddr_we_n }, o_ddr_we_n); |
xoddr xba0( i_clk, { w_ddr_ba[0], w_ddr_ba[0] }, o_ddr_ba[0]); |
xoddr xba1( i_clk, { w_ddr_ba[1], w_ddr_ba[1] }, o_ddr_ba[1]); |
xoddr xba2( i_clk, { w_ddr_ba[2], w_ddr_ba[2] }, o_ddr_ba[2]); |
xoddr xaddr0(i_clk, { w_ddr_addr[0], w_ddr_addr[0] }, o_ddr_addr[0]); |
xoddr xaddr1(i_clk, { w_ddr_addr[1], w_ddr_addr[1] }, o_ddr_addr[1]); |
xoddr xaddr2(i_clk, { w_ddr_addr[2], w_ddr_addr[2] }, o_ddr_addr[2]); |
xoddr xaddr3(i_clk, { w_ddr_addr[3], w_ddr_addr[3] }, o_ddr_addr[3]); |
xoddr xaddr4(i_clk, { w_ddr_addr[4], w_ddr_addr[4] }, o_ddr_addr[4]); |
xoddr xaddr5(i_clk, { w_ddr_addr[5], w_ddr_addr[5] }, o_ddr_addr[5]); |
xoddr xaddr6(i_clk, { w_ddr_addr[6], w_ddr_addr[6] }, o_ddr_addr[6]); |
xoddr xaddr7(i_clk, { w_ddr_addr[7], w_ddr_addr[7] }, o_ddr_addr[7]); |
xoddr xaddr8(i_clk, { w_ddr_addr[8], w_ddr_addr[8] }, o_ddr_addr[8]); |
xoddr xaddr9(i_clk, { w_ddr_addr[9], w_ddr_addr[9] }, o_ddr_addr[9]); |
xoddr xaddr10(i_clk,{ w_ddr_addr[10],w_ddr_addr[10]}, o_ddr_addr[10]); |
xoddr xaddr11(i_clk,{ w_ddr_addr[11],w_ddr_addr[11]}, o_ddr_addr[11]); |
xoddr xaddr12(i_clk,{ w_ddr_addr[12],w_ddr_addr[12]}, o_ddr_addr[12]); |
xoddr xaddr13(i_clk,{ w_ddr_addr[13],w_ddr_addr[13]}, o_ddr_addr[13]); |
|
/* |
wire w_clk_for_ddr; |
ODDR #(.DDR_CLK_EDGE("SAME_EDGE")) |
memclkddr(.Q(w_clk_for_ddr), .C(clk_for_ddr), .CE(1'b1), |
420,52 → 510,7
.D1(1'b0), .D2(1'b1), .R(1'b0), .S(1'b0)); |
OBUFDS #(.IOSTANDARD("DIFF_SSTL135"), .SLEW("FAST")) |
clkbuf(.O(o_ddr_ck_p), .OB(o_ddr_ck_n), .I(w_clk_for_ddr)); |
*/ |
|
// assign o_ddr_dm[0] = w_ddr_dm; |
// assign o_ddr_dm[1] = w_ddr_dm; |
xoddr xdm0(i_clk,{ w_ddr_dm, w_ddr_dm }, o_ddr_dm[0]); |
xoddr xdm1(i_clk,{ w_ddr_dm, w_ddr_dm }, o_ddr_dm[1]); |
|
assign o_ddr_odt = (~o_ddr_reset_n)? 1'bz : w_ddr_odt; |
|
// xlogicanalyzer ladata(i_clk, io_ddr_data[0], w_ddr_debug[3:0]); |
// xlogicanalyzer ladclk(clk_analyzer, clk_analyzer_b, |
// i_clk, o_ddr_ck_p, w_ddr_debug[7:4]); |
assign w_ddr_debug[7:4] = 4'h0; |
assign w_ddr_debug[3:0] = 4'h0; |
`else |
assign o_ddr_cs_n = w_ddr_cs_n; |
assign o_ddr_ras_n = w_ddr_ras_n; |
assign o_ddr_cas_n = w_ddr_cas_n; |
assign o_ddr_we_n = w_ddr_we_n; |
// |
assign o_ddr_ba = w_ddr_ba; |
assign o_ddr_addr = w_ddr_addr; |
// |
assign o_ddr_dm[1:0] = 2'b00; |
assign o_ddr_odt = 1'b0; |
// |
assign io_ddr_data = 16'bzzzz_zzzz_zzzz_zzzz; |
always @(posedge i_clk) |
r_ddr_data = 16'h0000; |
|
//wire w_clk_for_ddr; |
//ODDR #(.DDR_CLK_EDGE("SAME_EDGE")) |
//memclkddr(.Q(w_clk_for_ddr), .C(clk_for_ddr), .CE(1'b1), |
//.D1(1'b0), .D2(1'b1), .R(1'b0), .S(1'b0)); |
OBUFDS #(.IOSTANDARD("DIFF_SSTL135"), .SLEW("FAST")) |
clkbuf(.O(o_ddr_ck_p), .OB(o_ddr_ck_n), .I(1'b1)); |
|
wire [7:0] w_dqs_ignore; |
xioddrds dqs0(clk_for_ddr, w_ddr_dqs, { 1'b0, 1'b1 }, |
{ w_dqs_ignore[0], w_dqs_ignore[1] }, |
io_ddr_dqs_p[0], io_ddr_dqs_n[0]); |
xioddrds dqs1(clk_for_ddr, w_ddr_dqs, { 1'b0, 1'b1 }, |
{ w_dqs_ignore[2], w_dqs_ignore[3] }, |
io_ddr_dqs_p[1], io_ddr_dqs_n[1]); |
|
|
`endif |
|
endmodule |
|
/xioddrds.v
0,0 → 1,85
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: xioddrds.v |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: For the DDR3 SDRAM, this handles the Xilinx specific portions |
// of the IO necessary to make this happen for differential I/O |
// pins. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY 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. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
module xioddrds(i_clk, i_oe, i_v, o_v, io_p, io_n); |
input i_clk, i_oe; |
input [1:0] i_v; |
output [1:0] o_v; |
inout io_p, io_n; |
|
wire w_internal, w_read_path; |
|
ODDR #( |
.DDR_CLK_EDGE("SAME_EDGE"), |
.INIT(1'b0), |
.SRTYPE("SYNC") |
) ODDRi( |
.Q(w_internal), |
.C(i_clk), |
.CE(1'b1), |
.D1(i_v[0]), |
.D2(i_v[1]), |
.R(1'b0), |
.S(1'b0)); |
|
IDDR #( |
.DDR_CLK_EDGE("SAME_EDGE_PIPELINED"), |
.INIT_Q1(1'b0), |
.INIT_Q2(1'b0), |
.SRTYPE("SYNC") |
) IDDRi( |
.Q1(o_v[0]), |
.Q2(o_v[1]), |
.C(i_clk), |
.CE(1'b1), |
.D(w_read_path), |
.R(1'b0), |
.S(1'b0)); |
|
IOBUFDS #( |
.DIFF_TERM("FALSE"), |
.IBUF_LOW_PWR("TRUE"), |
.IOSTANDARD("DIFF_SSTL135"), |
.SLEW("FAST") |
) IOBUFDSi( |
.O(w_read_path), |
.IO(io_p),.IOB(io_n), |
.I(w_internal), |
.T(i_oe)); |
endmodule |
/ddr3insert.v
0,0 → 1,198
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: ddr3insert.v |
// |
// Project: A wishbone controlled DDR3 SDRAM memory controller. |
// |
// Purpose: Since the DDR3 RAM requires I/O resources not available |
// in Verilog alone, this insert (i.e. include file) is designed |
// to be included from a top-level block that would otherwise use the |
// DDR3 SDRAM. It is meant to encapsulate the I/O resource requirements |
// and connections required to make the DDR3 SDRAM work. |
// |
// |
// This file is not a module in its own right, but rather a file |
// included in a larger module. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY 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. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
|
// EXTERNAL PINS (BOTH INPUT AND OUTPUT): |
// o_ddr_reset_n, o_ddr_cke, o_ddr_ck_p, o_ddr_ck_n |
// o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n, o_ddr_we_n, |
// io_ddr_dqs_p, io_ddr_dqs_n, |
// o_ddr_addr, o_ddr_ba, |
// io_ddr_data, o_ddr_dm, o_ddr_odt |
// |
// INPUTS: (from elsewhere in the toplevel module) |
// pwr_reset True for one clock at power on |
// i_clk 200 MHz clock used in fabric |
// clk_for_ddr 800 MHz clock to send to memory clk pins |
// mem_serial_clk 800 MHz clock to drive serdes's |
// mem_serial_clk_inv The inverse of the 800 MHz clock ... |
// |
// |
// LOCAL VARIABLES PROVIDED: |
// (These come from the controller) |
// [3:0] w_ddr_cs_n, w_ddr_ras_n, w_ddr_cas_n, w_ddr_we_n |
// [11:0] w_ddr_ba, divided into |
// { w_ddr_ba[11:9], w_ddr_ba[8:6], w_ddr[5:3], w_ddr_ba[2:0] } |
// [55:0] w_ddr_addr |
// { w_ddr_addr[55:42], w_ddr_addr[41:28], w_ddr_addr[27:14], |
// w_ddr_addr[13:0] } |
// [7:0] w_ddr_odt // On-die termination |
// [7:0] w_ddr_odt // On-die termination |
// [15:0] w_ddr_dm // Data mask, headed to memory |
// [127:0] wo_ddr_data // Data going to the memory |
// [127:0] wi_ddr_data // The data returned by the memory |
// |
// |
|
// |
wire w_ddr_reset_n, w_ddr_cke, w_ddr_bus_oe; |
wire [26:0] w_ddr_cmd_a, w_ddr_cmd_b; |
wire [63:0] wi_ddr_data, wo_ddr_data; |
wire [127:0] wide_ddr_data; |
|
// |
// |
// Wires for setting up the DDR3 memory |
// |
// |
|
// First, let's set up the clock(s) |
xoddrserdesb ddrclk(mem_serial_clk, i_clk, pwr_reset, 8'h66, |
o_ddr_ck_p, o_ddr_ck_n); |
|
wire [7:0] w_udqs_in, w_ldqs_in; |
|
xioddrserdesb ddrudqs(mem_serial_clk, mem_serial_clk_inv, i_clk, |
~w_ddr_reset_n, w_ddr_cmd_a[0], |
(w_ddr_cmd_b[0])? 8'h66 : 8'h06, |
w_udqs_in, |
io_ddr_dqs_p[1], io_ddr_dqs_n[1]); |
|
xioddrserdesb ddrldqs(mem_serial_clk, mem_serial_clk_inv, i_clk, |
~w_ddr_reset_n, w_ddr_cmd_a[0], |
(w_ddr_cmd_b[0])? 8'h66 : 8'h06, |
w_ldqs_in, |
io_ddr_dqs_p[0], io_ddr_dqs_n[0]); |
|
// The command wires: CS_N, RAS_N, CAS_N, and WE_N |
xoddrserdes ddrcsn(mem_serial_clk, i_clk, ~w_ddr_reset_n, |
{ w_ddr_cmd_a[26], w_ddr_cmd_a[26], |
w_ddr_cmd_a[26], w_ddr_cmd_a[26], |
w_ddr_cmd_b[26], w_ddr_cmd_b[26], |
w_ddr_cmd_b[26], w_ddr_cmd_b[26] }, o_ddr_cs_n); |
|
xoddrserdes ddrrasn(mem_serial_clk, i_clk, ~w_ddr_reset_n, |
{ w_ddr_cmd_a[25], w_ddr_cmd_a[25], |
w_ddr_cmd_a[25], w_ddr_cmd_a[25], |
w_ddr_cmd_b[25], w_ddr_cmd_b[25], |
w_ddr_cmd_b[25], w_ddr_cmd_b[25] }, o_ddr_ras_n); |
|
xoddrserdes ddrcasn(mem_serial_clk, i_clk, ~w_ddr_reset_n, |
{ w_ddr_cmd_a[24], w_ddr_cmd_a[24], |
w_ddr_cmd_a[24], w_ddr_cmd_a[24], |
w_ddr_cmd_b[24], w_ddr_cmd_b[24], |
w_ddr_cmd_b[24], w_ddr_cmd_b[24] }, o_ddr_cas_n); |
|
xoddrserdes ddrwen(mem_serial_clk, i_clk, ~w_ddr_reset_n, |
{ w_ddr_cmd_a[23], w_ddr_cmd_a[23], |
w_ddr_cmd_a[23], w_ddr_cmd_a[23], |
w_ddr_cmd_b[23], w_ddr_cmd_b[23], |
w_ddr_cmd_b[23], w_ddr_cmd_b[23] }, o_ddr_we_n); |
|
// Data mask wires, first the upper byte |
xoddrserdes ddrudm(mem_serial_clk, i_clk, ~w_ddr_reset_n, |
{ w_ddr_cmd_a[4], w_ddr_cmd_a[4], |
w_ddr_cmd_a[2], w_ddr_cmd_a[2], |
w_ddr_cmd_b[4], w_ddr_cmd_b[4], |
w_ddr_cmd_b[2], w_ddr_cmd_b[2] }, o_ddr_dm[1]); |
// then the lower byte |
xoddrserdes ddrldm(mem_serial_clk, i_clk, ~w_ddr_reset_n, |
{ w_ddr_cmd_a[3], w_ddr_cmd_a[3], |
w_ddr_cmd_a[1], w_ddr_cmd_a[1], |
w_ddr_cmd_b[3], w_ddr_cmd_b[3], |
w_ddr_cmd_b[1], w_ddr_cmd_b[1] }, o_ddr_dm[0]); |
|
// and the On-Die termination wire |
xoddrserdes ddrodt(mem_serial_clk, i_clk, ~w_ddr_reset_n, |
{ w_ddr_cmd_a[0], w_ddr_cmd_a[0], |
w_ddr_cmd_a[0], w_ddr_cmd_a[0], |
w_ddr_cmd_b[0], w_ddr_cmd_b[0], |
w_ddr_cmd_b[0], w_ddr_cmd_b[0] }, o_ddr_odt); |
|
// |
// Now for the data, bank, and address wires |
// |
genvar k; |
generate begin |
// |
for(k=0; k<16; k=k+1) |
xioddrserdes ddrdata(mem_serial_clk, mem_serial_clk_inv, i_clk, ~w_ddr_reset_n, |
w_ddr_bus_oe, |
{ wo_ddr_data[48+k], wo_ddr_data[48+k], |
wo_ddr_data[32+k], wo_ddr_data[32+k], |
wo_ddr_data[16+k], wo_ddr_data[16+k], |
wo_ddr_data[ k], wo_ddr_data[ k] }, |
{ wide_ddr_data[112+k], wide_ddr_data[96+k], |
wide_ddr_data[ 80+k], wide_ddr_data[64+k], |
wide_ddr_data[ 48+k], wide_ddr_data[32+k], |
wide_ddr_data[ 16+k], wide_ddr_data[ k] }, |
io_ddr_data[k]); |
// |
for(k=0; k<3; k=k+1) |
xoddrserdes ddrbank(mem_serial_clk, i_clk, ~w_ddr_reset_n, |
{ w_ddr_cmd_a[20+k], w_ddr_cmd_a[20+k], |
w_ddr_cmd_a[20+k], w_ddr_cmd_a[20+k], |
w_ddr_cmd_b[20+k], w_ddr_cmd_b[20+k], |
w_ddr_cmd_b[20+k], w_ddr_cmd_b[20+k] }, |
o_ddr_ba[k]); |
// |
for(k=0; k<14; k=k+1) |
xoddrserdes ddraddr(mem_serial_clk, i_clk, ~w_ddr_reset_n, |
{ w_ddr_cmd_a[ 6+k], w_ddr_cmd_a[ 6+k], |
w_ddr_cmd_a[ 6+k], w_ddr_cmd_a[ 6+k], |
w_ddr_cmd_b[ 6+k], w_ddr_cmd_b[ 6+k], |
w_ddr_cmd_b[ 6+k], w_ddr_cmd_b[ 6+k] }, |
o_ddr_addr[k]); |
// |
|
for(k=0; k<64; k=k+1) |
assign wi_ddr_data[k] = (w_ddr_bus_oe) ? wide_ddr_data[2*k+1] |
: wide_ddr_data[2*k]; |
end endgenerate |
|
assign o_ddr_reset_n = w_ddr_reset_n; |
assign o_ddr_cke = w_ddr_cke; |
|
|
/xoddrserdes.v
0,0 → 1,94
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: xoddrserdes.v |
// |
// Project: A wishbone controlled DDR3 SDRAM memory controller. |
// |
// Purpose: Provide DDR outputs at 8x the clock rate (with a 4x clock given) |
// for such things as the memory clock output, and the data strobe |
// output (DQS) which needs to be synchronous with the memory clock output. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY 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. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
module xoddrserdes(i_clk_fast, i_clk_slow, i_reset, i_data, o_pin); |
input i_clk_fast, i_clk_slow, i_reset; |
input [7:0] i_data; |
output o_pin; |
|
wire local_monitor_pin__unconnected; |
wire oe_for_fabric__unconnected; |
wire [1:0] local_shiftout__unconnected; |
wire local_tbyte_out__unconnected; |
wire send_to_iob; |
wire oe_for_iob; |
|
OSERDESE2 #( |
.DATA_RATE_OQ("DDR"), |
.DATA_RATE_TQ("BUF"), |
.DATA_WIDTH(8), // 8 data wires sent per clkdiv |
.INIT_OQ(1'b1), |
.SERDES_MODE("MASTER"), |
// |
.TRISTATE_WIDTH(1), |
.INIT_TQ(1'b1), |
.TBYTE_CTL("FALSE"), |
.TBYTE_SRC("FALSE") |
) oserdes_i( |
.CLK(i_clk_fast), |
.CLKDIV(i_clk_slow), |
.OCE(1'b1), |
.OFB(local_monitor_pin__unconnected), |
.OQ(send_to_iob), |
.RST(i_reset), |
// |
.TCE(1'b1), |
.TQ(oe_for_iob), |
.TFB(oe_for_fabric__unconnected), |
.T1(1'b0), .T2(1'b0), .T3(1'b0), .T4(1'b0), |
// |
.SHIFTOUT1(local_shiftout__unconnected[0]), |
.SHIFTOUT2(local_shiftout__unconnected[1]), |
.SHIFTIN1(1'b0), |
.SHIFTIN2(1'b0), |
.TBYTEIN(1'b0), |
.TBYTEOUT(local_tbyte_out__unconnected), |
// |
// And now for the actual data we wish to send |
// |
.D1(i_data[0]), .D2(i_data[1]), |
.D3(i_data[2]), .D4(i_data[3]), |
.D5(i_data[4]), .D6(i_data[5]), |
.D7(i_data[6]), .D8(i_data[7]) |
); |
|
OBUF iobuf_i(.I(send_to_iob), .O(o_pin)); |
|
endmodule |
|
/xoddrserdesb.v
0,0 → 1,97
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: xoddrserdes.v |
// |
// Project: A wishbone controlled DDR3 SDRAM memory controller. |
// |
// Purpose: Provide DDR outputs at 8x the clock rate (with a 4x clock given) |
// for such things as the memory clock output, and the data strobe |
// output (DQS) which needs to be synchronous with the memory clock output. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY 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. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
module xoddrserdesb(i_clk_fast, i_clk_slow, i_reset, |
i_data, o_pin_p, o_pin_n); |
input i_clk_fast, i_clk_slow, i_reset; |
input [7:0] i_data; |
output wire o_pin_p, o_pin_n; |
|
wire local_monitor_pin__unconnected; |
wire oe_for_fabric__unconnected; |
wire [1:0] local_shiftout__unconnected; |
wire local_tbyte_out__unconnected; |
wire send_to_iob; |
wire oe_for_iob; |
|
OSERDESE2 #( |
.DATA_RATE_OQ("DDR"), |
.DATA_RATE_TQ("BUF"), |
.DATA_WIDTH(8), // 8 data wires sent per clkdiv |
.INIT_OQ(1'b1), |
.SERDES_MODE("MASTER"), |
// |
.TRISTATE_WIDTH(1), |
.INIT_TQ(1'b1), |
.TBYTE_CTL("FALSE"), |
.TBYTE_SRC("FALSE") |
) oserdes_i( |
.CLK(i_clk_fast), |
.CLKDIV(i_clk_slow), |
.OCE(1'b1), |
.OFB(local_monitor_pin__unconnected), |
.OQ(send_to_iob), |
.RST(i_reset), |
// |
.TCE(1'b1), |
.TQ(oe_for_iob), |
.TFB(oe_for_fabric__unconnected), |
.T1(1'b0), .T2(1'b0), .T3(1'b0), .T4(1'b0), |
// |
.SHIFTOUT1(local_shiftout__unconnected[0]), |
.SHIFTOUT2(local_shiftout__unconnected[1]), |
.SHIFTIN1(1'b0), |
.SHIFTIN2(1'b0), |
.TBYTEIN(1'b0), |
.TBYTEOUT(local_tbyte_out__unconnected), |
// |
// And now for the actual data we wish to send |
// |
.D1(i_data[0]), .D2(i_data[1]), |
.D3(i_data[2]), .D4(i_data[3]), |
.D5(i_data[4]), .D6(i_data[5]), |
.D7(i_data[6]), .D8(i_data[7]) |
); |
|
OBUFDS iobuf_i( |
.I(send_to_iob), |
.O(o_pin_p), .OB(o_pin_n)); |
|
endmodule |
|
/xioddrserdesb.v
0,0 → 1,134
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: xioddrserdesb.v |
// |
// Project: A wishbone controlled DDR3 SDRAM memory controller. |
// |
// Purpose: |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY 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. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
module xioddrserdesb(i_clk_fast, i_clk_fast_inv, i_clk_slow, i_reset, |
i_oe, i_data, o_data, io_pin_p, io_pin_n); |
input i_clk_fast, i_clk_fast_inv, i_clk_slow, i_reset; |
input i_oe; |
input [7:0] i_data; |
output [7:0] o_data; |
inout io_pin_p, io_pin_n; |
|
wire feedback; |
wire oe_for_fabric__unconnected; |
wire [1:0] local_shiftout__unconnected; |
wire local_tbyte_out__unconnected; |
wire send_to_iob; |
wire oe_for_iob; |
wire [1:0] iserdes_shiftout__unconnected; |
|
OSERDESE2 #( |
.DATA_RATE_OQ("DDR"), |
.DATA_RATE_TQ("BUF"), |
.DATA_WIDTH(8), // 8 data wires sent per clkdiv |
.INIT_OQ(1'b1), |
.SERDES_MODE("MASTER"), |
// |
.TRISTATE_WIDTH(1), |
.INIT_TQ(1'b1), |
.TBYTE_CTL("FALSE"), |
.TBYTE_SRC("FALSE") |
) oserdes_i( |
.CLK(i_clk_fast), |
.CLKDIV(i_clk_slow), |
.OCE(1'b1), |
.OFB(feedback), |
.OQ(send_to_iob), |
.RST(i_reset), |
// |
.TCE(1'b1), |
.TQ(oe_for_iob), |
.TFB(oe_for_fabric__unconnected), |
.T1(~i_oe), .T2(~i_oe), .T3(~i_oe), .T4(~i_oe), |
// |
.SHIFTOUT1(local_shiftout__unconnected[0]), |
.SHIFTOUT2(local_shiftout__unconnected[1]), |
.SHIFTIN1(1'b0), |
.SHIFTIN2(1'b0), |
.TBYTEIN(1'b0), |
.TBYTEOUT(local_tbyte_out__unconnected), |
// |
// And now for the actual data we wish to send |
// |
.D1(i_data[0]), .D2(i_data[1]), |
.D3(i_data[2]), .D4(i_data[3]), |
.D5(i_data[4]), .D6(i_data[5]), |
.D7(i_data[6]), .D8(i_data[7]) |
); |
|
IOBUFDS iobuf_i( |
.T(oe_for_iob), |
.I(send_to_iob), |
.IO(io_pin_p), .IOB(io_pin_n), |
.O(input_from_iobuf)); |
|
ISERDESE2 #( |
.DATA_RATE("DDR"), |
.DATA_WIDTH(8), // 8 data wires sent per clkdiv |
.INTERFACE_TYPE("OVERSAMPLE"), |
.IOBDELAY("NONE"), |
.NUM_CE(1), |
.OFB_USED("FALSE"), |
.SERDES_MODE("MASTER") |
) iserdes_i( |
.BITSLIP(1'b0), |
.CE1(1'b1), |
.CE2(1'b1), |
.CLK(i_clk_fast), |
.CLKB(i_clk_fast_inv), |
.CLKDIV(i_clk_slow), |
.CLKDIVP(1'b0), // Only used in MEMORY_DDR3 mode? |
.D(input_from_iobuf), |
.DDLY(1'b0), |
.DYNCLKDIVSEL(1'b0), |
.DYNCLKSEL(1'b0), |
.O(iserdes_unconnected_output), |
.OCLK(i_clk_fast), |
.OCLKB(i_clk_fast_inv), |
.OFB(feedback), |
.Q1(o_data[0]), .Q2(o_data[1]), |
.Q3(o_data[2]), .Q4(o_data[3]), |
.Q5(o_data[4]), .Q6(o_data[5]), |
.Q7(o_data[6]), .Q8(o_data[7]), |
.RST(i_reset), |
.SHIFTIN1(1'b0), .SHIFTIN2(1'b0), |
.SHIFTOUT1(iserdes_shiftout__unconnected[0]), |
.SHIFTOUT2(iserdes_shiftout__unconnected[1]) |
); |
|
endmodule |
|