Last active
July 22, 2021 20:28
-
-
Save enjoy-digital/529a4d9994f0cc95e45382e4eb253b09 to your computer and use it in GitHub Desktop.
DRAM initialization through Wishbone FSM.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
from migen import * | |
from wb_master import * | |
from wb_master import _WRITE_CMD, _WAIT_CMD, _DONE_CMD | |
dfii_control_sel = 0x01 | |
dfii_control_cke = 0x02 | |
dfii_control_odt = 0x04 | |
dfii_control_reset_n = 0x08 | |
dfii_command_cs = 0x01 | |
dfii_command_we = 0x02 | |
dfii_command_cas = 0x04 | |
dfii_command_ras = 0x08 | |
dfii_command_wrdata = 0x10 | |
dfii_command_rddata = 0x20 | |
# /!\ keep up to date with csr /!\ | |
sdram_dfii_control = 0xe0004000 | |
sdram_dfii_pi0_address = 0xe000400c | |
sdram_dfii_pi0_baddress = 0xe0004010 | |
sdram_dfii_pi0_command = 0xe0004004 | |
sdram_dfii_pi0_command_issue = 0xe0004008 | |
ddrphy_dly_sel = 0xe0008000 | |
ddrphy_rdly_dq_rst = 0xe0008004 | |
ddrphy_rdly_dq_inc = 0xe0008008 | |
ddrphy_rdly_dq_bitslip = 0xe000800c | |
def period_to_cycles(sys_clk_freq, period): | |
return int(period*sys_clk_freq) | |
def sdram_init_instructions(sys_clk_freq): | |
return [ | |
_WAIT_CMD | period_to_cycles(sys_clk_freq, 0.1), | |
# software control | |
_WRITE_CMD, sdram_dfii_control, 0, | |
# release reset | |
_WRITE_CMD, sdram_dfii_pi0_address, 0x0, | |
_WRITE_CMD, sdram_dfii_pi0_baddress, 0, | |
_WRITE_CMD, sdram_dfii_control, dfii_control_odt|dfii_control_reset_n, | |
_WAIT_CMD | period_to_cycles(sys_clk_freq, 0.1), | |
# bring cke high | |
_WRITE_CMD, sdram_dfii_pi0_address, 0x0, | |
_WRITE_CMD, sdram_dfii_pi0_baddress, 0, | |
_WRITE_CMD, sdram_dfii_control, dfii_control_cke|dfii_control_odt|dfii_control_reset_n, | |
_WAIT_CMD | period_to_cycles(sys_clk_freq, 0.1), | |
# load mode register 2 | |
_WRITE_CMD, sdram_dfii_pi0_address, 0x408, | |
_WRITE_CMD, sdram_dfii_pi0_baddress, 2, | |
_WRITE_CMD, sdram_dfii_pi0_command, dfii_command_ras|dfii_command_cas|dfii_command_we|dfii_command_cs, | |
_WRITE_CMD, sdram_dfii_pi0_command_issue, 1, | |
# load mode register 3 | |
_WRITE_CMD, sdram_dfii_pi0_address, 0x0, | |
_WRITE_CMD, sdram_dfii_pi0_baddress, 3, | |
_WRITE_CMD, sdram_dfii_pi0_command, dfii_command_ras|dfii_command_cas|dfii_command_we|dfii_command_cs, | |
_WRITE_CMD, sdram_dfii_pi0_command_issue, 1, | |
# load mode register 1 | |
_WRITE_CMD, sdram_dfii_pi0_address, 0x6, | |
_WRITE_CMD, sdram_dfii_pi0_baddress, 1, | |
_WRITE_CMD, sdram_dfii_pi0_command, dfii_command_ras|dfii_command_cas|dfii_command_we|dfii_command_cs, | |
_WRITE_CMD, sdram_dfii_pi0_command_issue, 1, | |
# load mode register 0, cl=7, bl=8 | |
_WRITE_CMD, sdram_dfii_pi0_address, 0x930, | |
_WRITE_CMD, sdram_dfii_pi0_baddress, 0, | |
_WRITE_CMD, sdram_dfii_pi0_command, dfii_command_ras|dfii_command_cas|dfii_command_we|dfii_command_cs, | |
_WRITE_CMD, sdram_dfii_pi0_command_issue, 1, | |
_WAIT_CMD | period_to_cycles(sys_clk_freq, 0.1), | |
# zq calibration | |
_WRITE_CMD, sdram_dfii_pi0_address, 0x400, | |
_WRITE_CMD, sdram_dfii_pi0_baddress, 0, | |
_WRITE_CMD, sdram_dfii_pi0_command, dfii_command_we|dfii_command_cs, | |
_WRITE_CMD, sdram_dfii_pi0_command_issue, 1, | |
_WAIT_CMD | period_to_cycles(sys_clk_freq, 0.1), | |
# hardware control | |
_WRITE_CMD, sdram_dfii_control, dfii_control_sel, | |
] | |
def sdram_config_instructions(bitslip, delay): | |
r = [] | |
for module in range(2): | |
r += [_WRITE_CMD, ddrphy_dly_sel, 1<<module] | |
r += [_WRITE_CMD, ddrphy_rdly_dq_rst, 1] | |
for i in range(bitslip): | |
# 7-series SERDES in DDR mode needs 3 pulses for 1 bitslip | |
for j in range(3): | |
r += [_WRITE_CMD, ddrphy_rdly_dq_bitslip, 1] | |
for i in range(delay): | |
r += [_WRITE_CMD, ddrphy_rdly_dq_inc, 1] | |
return r | |
class SDRAMInit(WishboneMaster): | |
def __init__(self, sys_clk_freq, bitslip, delay): | |
WishboneMaster.__init__(self, | |
sdram_init_instructions(sys_clk_freq) + | |
sdram_config_instructions(bitslip, delay) + | |
[_DONE_CMD]) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# SDRAM Initialization (to integrate in LiteX SoC) | |
sdram_init = SDRAMInit(sys_clk_freq, bitslip=3, delay=14) | |
self.submodules += sdram_init | |
self.bus.add_master(sdram_init.bus) | |
self.comb += [ | |
platform.request("init_done").eq(sdram_init.done), | |
platform.request("init_error").eq(sdram_init.error) | |
] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import sys | |
import time | |
from litex import RemoteClient | |
from sdram_init import * | |
wb = RemoteClient(debug=False) | |
wb.open() | |
# # # | |
# software control | |
wb.regs.sdram_dfii_control.write(0) | |
# sdram initialization | |
for i, (comment, a, ba, cmd, delay) in enumerate(init_sequence): | |
print(comment) | |
wb.regs.sdram_dfii_pi0_address.write(a) | |
wb.regs.sdram_dfii_pi0_baddress.write(ba) | |
if i < 2: | |
wb.regs.sdram_dfii_control.write(cmd) | |
else: | |
wb.regs.sdram_dfii_pi0_command.write(cmd) | |
wb.regs.sdram_dfii_pi0_command_issue.write(1) | |
# hardware control | |
wb.regs.sdram_dfii_control.write(dfii_control_sel) | |
def seed_to_data(seed, random=True): | |
if random: | |
return (1664525*seed + 1013904223) & 0xffffffff | |
else: | |
return seed | |
def write_pattern(length): | |
for i in range(length): | |
wb.write(wb.mems.main_ram.base + 4*i, seed_to_data(i)) | |
def check_pattern(length, debug=False): | |
errors = 0 | |
for i in range(length): | |
error = 0 | |
if wb.read(wb.mems.main_ram.base + 4*i) != seed_to_data(i): | |
error = 1 | |
if debug: | |
print("{}: 0x{:08x}, 0x{:08x} KO".format(i, wb.read(wb.mems.main_ram.base + 4*i), seed_to_data(i))) | |
else: | |
if debug: | |
print("{}: 0x{:08x}, 0x{:08x} OK".format(i, wb.read(wb.mems.main_ram.base + 4*i), seed_to_data(i))) | |
errors += error | |
return errors | |
# find working bitslips and delays | |
nbitslips = 8 | |
ndelays = 32 | |
nmodules = 2 | |
nwords = 16 | |
for bitslip in range(nbitslips): | |
print("bitslip {:d}: |".format(bitslip), end="") | |
for delay in range(ndelays): | |
for module in range(nmodules): | |
wb.regs.ddrphy_dly_sel.write(1<<module) | |
wb.regs.ddrphy_rdly_dq_rst.write(1) | |
wb.regs.ddrphy_rdly_dq_bitslip_rst.write(1) | |
for i in range(bitslip): | |
wb.regs.ddrphy_rdly_dq_bitslip.write(1) | |
for i in range(delay): | |
wb.regs.ddrphy_rdly_dq_inc.write(1) | |
write_pattern(nwords) | |
errors = check_pattern(nwords) | |
if errors: | |
print("..|", end="") | |
else: | |
print("{:02d}|".format(delay), end="") | |
sys.stdout.flush() | |
print("") | |
# # # | |
wb.close() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
from migen import * | |
from litex.soc.interconnect import wishbone | |
_WRITE_CMD = 0x10000000 | |
_WAIT_CMD = 0x20000000 | |
_DONE_CMD = 0x30000000 | |
def cmd_decoder(instruction, cmd): | |
return instruction[28:] == (cmd >> 28) | |
class WishboneMaster(Module): | |
def __init__(self, instructions): | |
self.bus = bus = wishbone.Interface() | |
self.run = Signal() | |
self.done = Signal() | |
self.error = Signal() | |
# # # | |
mem = Memory(32, len(instructions), init=instructions) | |
port = mem.get_port(async_read=True) | |
self.specials += mem, port | |
wait_counter = Signal(32) | |
fsm = FSM(reset_state="IDLE") | |
self.submodules += fsm | |
fsm.act("IDLE", | |
self.run.eq(1), | |
NextState("CMD") | |
) | |
fsm.act("CMD", | |
self.run.eq(1), | |
If(cmd_decoder(port.dat_r, _WRITE_CMD), | |
NextValue(port.adr, port.adr + 1), | |
NextState("WRITE_ADR") | |
).Elif(cmd_decoder(port.dat_r, _WAIT_CMD), | |
NextValue(wait_counter, port.dat_r[:28]), | |
NextState("WAIT") | |
).Elif(cmd_decoder(port.dat_r, _DONE_CMD), | |
NextState("DONE") | |
).Else( | |
NextState("ERROR") | |
) | |
) | |
fsm.act("WAIT", | |
self.run.eq(1), | |
NextValue(wait_counter, wait_counter - 1), | |
If(wait_counter == 0, | |
NextValue(port.adr, port.adr + 1), | |
NextState("CMD") | |
) | |
) | |
fsm.act("WRITE_ADR", | |
self.run.eq(1), | |
NextValue(bus.adr, port.dat_r[2:]), | |
NextValue(port.adr, port.adr + 1), | |
NextState("WRITE_DATA") | |
) | |
fsm.act("WRITE_DATA", | |
self.run.eq(1), | |
NextValue(bus.dat_w, port.dat_r), | |
NextValue(port.adr, port.adr + 1), | |
NextState("WRITE") | |
) | |
fsm.act("WRITE", | |
self.run.eq(1), | |
bus.stb.eq(1), | |
bus.cyc.eq(1), | |
bus.we.eq(1), | |
bus.sel.eq(0xf), | |
If(bus.ack, | |
If(bus.err, | |
NextState("ERROR"), | |
).Else( | |
NextState("CMD") | |
) | |
) | |
) | |
fsm.act("ERROR", self.error.eq(1)) | |
fsm.act("DONE", self.done.eq(1)) | |
if __name__ == "__main__": | |
instructions = [ | |
_WRITE_CMD, | |
0x12340000, | |
0x0000A5A5, | |
_WAIT_CMD | 0x20, | |
_WRITE_CMD, | |
0x00001234, | |
0xDEADBEEF, | |
_DONE_CMD | |
] | |
dut = WishboneMaster(instructions) | |
def dut_tb(dut): | |
yield dut.bus.ack.eq(1) | |
for i in range(1024): | |
yield | |
run_simulation(dut, dut_tb(dut), vcd_name="wb_master.vcd") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment