├── .gitignore ├── README.md ├── clk_driver.py ├── conversion.py ├── host_intf.py ├── sd_intf.py ├── sdram.py ├── sdram_cntl.py ├── test_controller.py └── test_sdram.py /.gitignore: -------------------------------------------------------------------------------- 1 | env.source 2 | *.pyc 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SDRAM_Controller 2 | 3 | This repository is created to host the work I done for Google Summer of Code 2015. It contains a Sdram controller and a model that can be used to test the controller. 4 | All the designs are written in MyHDL which is a HDL written in Python. Details and manual to use MyHDL can be found on http://myhdl.org/ 5 | 6 | ## Prerequistes ## 7 | 8 | * Python 2 9 | * MyHDL 10 | 11 | ## Instructions ## 12 | 13 | To run the tests myhdl and SDRAM_Controller should be included in the PYTHONPATH 14 | ```bash 15 | export PYTHONPATH = $PYTHONPATH:: 16 | 17 | python /test_sdram.py 18 | 19 | python /test_controller.py 20 | ``` 21 | ## Simulator ## 22 | 23 | This folder contains a sdram modal. It has a similar interface to real sdram which is defined in the file sd_intf.py 24 | The modal is closely simulating the sdram behaviour with the timing delays so that an sdram controller can be tested for functionality. 25 | 26 | Simulator is simply a python dictionary wrapped with an interface similar to sdram hardware. Writing to sdram is equivalent to adding (addr,value) pair to the dictionary. This is a good example of using the power of python to write models that can verify the functionality of RTL designs. 27 | 28 | Simulator also has some built-in checks to detect any illegal commands or sequence of commands. If such a violation has happened simulator output will have messages in the form "SDRAM : [ERROR] error-message". If such messages are present in the output, the controller is not working properly. The error message can be used to detect and correct the faults in the controller. 29 | 30 | ### Simulator Output ### 31 | 32 | This simulator prints several types of messages to the console. 33 | 34 | | Output | Description | 35 | |------------------------------------------------|-----------------------------------------------------------------------------------------| 36 | |SDRAM : [COMMAND] command-name | In every possitive edge of the clock cycle sdram will print the current command issued. (This would appear only if show_command is set to True. Default is False) | 37 | |STATE : [CHANGE] old-state -> new-state @ time | Each bank can be in different state. This would print the state transition and the time | 38 | |DATA : [WRITE] Addr: addr Data: value | This print happens at the moment when data is written to the memory. There is a few cycle delay between the time write command appear in the pins and the time when actual data is written to the memory | 39 | |STATE : [READ] Data Ready @ time | This is a very important print. It appears when sdram start driving the data bus with the read value. Since the sdram will only drive the bus for a limited time the controller should extract the data right after this time | 40 | |SDRAM : [ERROR] error-message | There are several self tests in the sdram. If this type of message appear in the output, controller is not functioning properly. error-message will give more information about the error. | 41 | 42 | ### Simulator Test ### 43 | 44 | Simulator test is availiable in the file test_sdram.py . This test can be run using the following command. 45 | ```bash 46 | python test_sdram.py 47 | ``` 48 | The test uses the transactors defined in sd_intf.py file to do the read and write. It manually drives the input ports of the sdram and read the value from inout port. 49 | 50 | ## Controller ## 51 | 52 | The controller is written reffering the VHDL designed by xesscorp which can be found on https://github.com/xesscorp/VHDL_Lib/blob/master/SdramCntl.vhd 53 | 54 | Sdram controller make it easy to access the sdram. The host logic can use the sdram more likely an sram because of the controller. It takes care of row refreshes and gives an easy to use interface for the host logic to access the sdram. Host side interface of the controller is availiable in the file host_intf.py 55 | 56 | ![alt tag](http://1.bp.blogspot.com/-7NdtaBXzLTk/VcwHf4cldVI/AAAAAAAAAbg/9GWwOfUk1lc/s1600/controller.png) 57 | 58 | ### Instructions ### 59 | 60 | * WRITE : To perform write operation write the address to addr and the data to data_i and drive write_i high. Hold the values until done_o goes high. 61 | * READ : To perform read operation write the read address to addr and wait until don_o goes high. Read the value of data_o as soon as done_o goes high. 62 | 63 | * MyHDL allows to write transactors in the host_intf.py file where the host side interface is defined. Transactors further simplifies usage of the controller. With transactors read and write looks like follows 64 | 65 | ```python 66 | yield host_intf.write(120,23) 67 | yield host_intf.done_o.posedge 68 | 69 | yield host_intf.read(120) 70 | yield host_intf.done_o.posedge 71 | print "Data Value : ",host_intf.data_o 72 | ``` 73 | 74 | ### Controller Test ### 75 | 76 | Controller test is availiable in the file test_controller.py. This test can be run by the following command. 77 | ```bash 78 | python test_controller.py 79 | ``` 80 | The test uses the transactors defined in host_intf.py file to do the read and write. It manually drives the input ports of the controller and read the value from the output port. 81 | 82 | ### Convertion to Verilog and VHDL ### 83 | 84 | MyHDL offeres a very simple interface to perform the conversion from MyHDL to either Verilog or VHDL. The file Converion.py uses these interfaces to generate both Verilog and VHDL convertions of the SdramCntl.py. Following command will generate MySdramCntl.v, MySdramCntl.vhd and pck_myhdl_10.vhd files. 85 | ```bash 86 | python Conversion.py 87 | ``` 88 | pck_myhdl_10.vhd is a VHDL library file which is required by MySdramCntl.vhd (The name of the file may change depends on the MyHDL version being used.) 89 | 90 | The converted VHDL design has been verified on Xula2 board. Detailed step followed for the hardware verification can be found at http://design4hardware.blogspot.com/2015/08/detailed-steps-for-hardware.html 91 | 92 | ### How to Use ### 93 | 94 | The converted output (MySdramCntl.v or MySdramCntl.vhd and pck_myhdl_10.vhd) can be used in any RTL design just as a regular verilog/VHDL file. Simply add the converted files to an existing project and instantiate a MySdramCntl module. 95 | 96 | ### Limitations ### 97 | 98 | Sdram can be programmed to used in several different modes. However this controller does not allow to set up the sdram user mode. Instead it uses a fixed mode where burst length is one. 99 | -------------------------------------------------------------------------------- /clk_driver.py: -------------------------------------------------------------------------------- 1 | from myhdl import * 2 | 3 | 4 | def clk_driver(clk): 5 | 6 | half_period = delay(1) 7 | 8 | @always(half_period) 9 | def drive_clk(): 10 | clk.next = not clk 11 | 12 | return drive_clk 13 | -------------------------------------------------------------------------------- /conversion.py: -------------------------------------------------------------------------------- 1 | from clk_driver import clk_driver 2 | from sdram_cntl import * 3 | 4 | clk_i = Signal(bool(0)) 5 | rst_i = ResetSignal(0, active=1, async=True) 6 | 7 | clkDriver_Inst = clk_driver(clk_i) 8 | sd_intf_Inst = SdramIntf() 9 | host_intf_Inst = HostIntf() 10 | 11 | sdramCntl_Inst = sdram_cntl(clk_i, host_intf_Inst, sd_intf_Inst) 12 | 13 | toVerilog(sdram_cntl, clk_i, host_intf_Inst, sd_intf_Inst) 14 | toVHDL(sdram_cntl, clk_i, host_intf_Inst, sd_intf_Inst) 15 | -------------------------------------------------------------------------------- /host_intf.py: -------------------------------------------------------------------------------- 1 | from myhdl import * 2 | 3 | 4 | class HostIntf(object): 5 | 6 | def __init__(self): 7 | # Host side signals 8 | self.rst_i = ResetSignal(0, active=1, async=True) 9 | self.rd_i = Signal(bool(0)) 10 | self.wr_i = Signal(bool(0)) 11 | self.addr_i = Signal(intbv(0)[24:]) # host side address = sdram side row + col + bank 12 | self.data_i = Signal(intbv(0)[16:]) 13 | self.data_o = Signal(intbv(0)[16:]) 14 | self.done_o = Signal(bool(0)) 15 | self.rdPending_o = Signal(bool(0)) 16 | 17 | def read(self, addr): 18 | self.addr_i.next = addr 19 | self.rd_i.next = 1 20 | yield delay(2) 21 | self.rd_i.next = 0 22 | 23 | def write(self, addr, data): 24 | self.addr_i.next = addr 25 | self.data_i.next = data 26 | yield delay(5) 27 | self.wr_i.next = 1 28 | 29 | def nop(self): 30 | self.rd_i.next = 0 31 | self.wr_i.next = 0 32 | 33 | def wait_until_done(self): 34 | yield self.done_o.posedge 35 | 36 | def read_data(self): 37 | return self.data_o.val 38 | -------------------------------------------------------------------------------- /sd_intf.py: -------------------------------------------------------------------------------- 1 | from myhdl import * 2 | from math import log 3 | 4 | 5 | class SdramIntf(object): 6 | 7 | addr_width = 13 8 | data_width = 16 9 | # constant for sdram 10 | SDRAM_NROWS_C = 8192 # Number of rows in SDRAM array. 11 | SDRAM_NCOLS_C = 512 # Number of columns in SDRAM array. 12 | SDRAM_DATA_WIDTH_C = 16 # Host & SDRAM data width. 13 | SDRAM_HADDR_WIDTH_C = 24 # Host-side address width. 14 | SDRAM_SADDR_WIDTH_C = 13 # SDRAM-side address width. 15 | SDRAM_T_INIT_C = 20000.0 # 200000.0 # Min initialization interval (ns). 16 | SDRAM_T_RAS_C = 45.0 # Min interval between active to precharge commands (ns). 17 | SDRAM_T_RCD_C = 20.0 # Min interval between active and R/W commands (ns). 18 | SDRAM_T_REF_C = 64000000.0 # Maximum refresh interval (ns). 19 | SDRAM_T_RFC_C = 65.0 # Duration of refresh operation (ns). 20 | SDRAM_T_RP_C = 20.0 # Min precharge command duration (ns). 21 | SDRAM_T_XSR_C = 75.0 # Exit self-refresh time (ns). 22 | 23 | SDRAM_FREQ_C = 100.0 # Operating frequency in MHz. 24 | SDRAM_IN_PHASE_C = True # SDRAM and controller XESS on same or opposite clock edge. 25 | SDRAM_PIPE_EN_C = False # If true, enable pipelined read operations. 26 | SDRAM_ENABLE_REFRESH_C = True # If true, row refreshes are automatically inserted. 27 | SDRAM_MULTIPLE_ACTIVE_ROWS_C = False # If true, allow an active row in each bank. 28 | SDRAM_MAX_NOP_C = 10000 # Number of NOPs before entering self-refresh. 29 | SDRAM_BEG_ADDR_C = 16 # 00_0000#; -- Beginning SDRAM address. 30 | SDRAM_END_ADDR_C = 16 # FF_FFFF#; -- Ending SDRAM address. 31 | 32 | SDRAM_NOP_CMD_C = intbv("0111")[4:] # 0,1,1,1,0,0 33 | SDRAM_ACTIVE_CMD_C = intbv("0011")[4:] # 0,0,1,1,0,0 34 | SDRAM_READ_CMD_C = intbv("0101")[4:] # 0,1,0,1,0,0 35 | SDRAM_WRITE_CMD_C = intbv("0100")[4:] # 0,1,0,0,0,0 36 | SDRAM_PCHG_CMD_C = intbv("0010")[4:] # 0,0,1,0,0,0 37 | SDRAM_MODE_CMD_C = intbv("0000")[4:] # 0,0,0,0,0,0 38 | SDRAM_RFSH_CMD_C = intbv("0001")[4:] # 0,0,0,1,0,0 39 | SDRAM_MODE_C = intbv("00_0_00_011_0_000")[12:] # mode command for set_mode command 40 | 41 | SDRAM_ALL_BANKS_C = intbv("001000000000")[12:] # value of CMDBIT to select all banks 42 | SDRAM_ONE_BANK_C = intbv("000000000000")[12:] 43 | 44 | timing = { # timing details refer data sheet 45 | 'init': 100, # min init interval 46 | 'ras': 10, # min interval between active prechargs 47 | 'rcd': 10, # min interval between active R/W 48 | 'cas': 20, 49 | 'ref': 64000000, # max refresh interval 50 | 'rfc': 65, # refresh opertaion 51 | 'rp': 20, # min precharge 52 | 'xsr': 75, # exit self-refresh time 53 | 'wr': 55, # @todo ... 54 | } 55 | 56 | def __init__(self): 57 | 58 | self.cke = Signal(bool(0)) 59 | self.cs = Signal(bool(0)) 60 | self.cas = Signal(bool(0)) 61 | self.ras = Signal(bool(0)) 62 | self.we = Signal(bool(0)) 63 | self.bs = Signal(intbv(0)[2:]) 64 | self.addr = Signal(intbv(0)[self.addr_width:]) 65 | self.dqml = Signal(bool(0)) 66 | self.dqmh = Signal(bool(0)) 67 | self.dq = TristateSignal(intbv(0)[self.data_width:]) 68 | 69 | # Written below are transactors for passing commands to sdram 70 | 71 | def nop(self, clk): 72 | # [NOP] cs ras cas we : L H H H 73 | self.cs.next, self.ras.next, self.cas.next, self.we.next = 0, 1, 1, 1 74 | yield clk.posedge 75 | 76 | def activate(self, clk, row_addr, bank_id=0): 77 | self.bs.next = bank_id 78 | self.addr.next = row_addr 79 | # [ACTIVE] cs ras cas we : L L H H 80 | self.cs.next, self.ras.next, self.cas.next, self.we.next = 0, 0, 1, 1 81 | yield clk.posedge 82 | 83 | def load_mode(self, clk, mode='burst', cas=3, burst=1): 84 | addr = 0 85 | if mode.lower() == 'single': 86 | addr += 2**9 87 | addr += cas*(2**4) 88 | addr += int(log(burst, 2)) 89 | self.addr.next = addr 90 | # [LOAD_MODE] cs ras cas we dqm : L L L L X 91 | self.cs.next, self.ras.next, self.cas.next, self.we.next = 0, 0, 0, 0 92 | yield clk.posedge 93 | yield clk.posedge 94 | 95 | def precharge(self, clk, bank_id=None): 96 | if not bank_id: # precharge all banks 97 | self.addr.next = 2**10 # A10 is high 98 | else: 99 | self.addr.next = 0 100 | self.bs.next = bank_id 101 | # [PRECHARGE] cs ras cas we : L L H L 102 | self.cs.next, self.ras.next, self.cas.next, self.we.next = 0, 0, 1, 0 103 | yield clk.posedge 104 | 105 | def read(self, clk, addr, bank_id=0): 106 | self.bs.next = bank_id 107 | self.addr.next = addr 108 | # [READ] # cs ras cas we dqm : L H L H X 109 | self.cs.next, self.ras.next, self.cas.next, self.we.next = 0, 1, 0, 1 110 | yield clk.posedge 111 | yield clk.posedge 112 | 113 | def write(self, clk, driver, addr, value, bank_id=0): 114 | self.bs.next = bank_id 115 | self.addr.next = addr 116 | driver.next = value 117 | # [WRITE] # cs ras cas we dqm : L H L L X 118 | self.cs.next, self.ras.next, self.cas.next, self.we.next = 0, 1, 0, 0 119 | yield clk.posedge 120 | yield clk.posedge 121 | driver.next = None 122 | 123 | def get_driver(self): 124 | return self.dq.driver() 125 | -------------------------------------------------------------------------------- /sdram.py: -------------------------------------------------------------------------------- 1 | from myhdl import * 2 | from sd_intf import SdramIntf 3 | from math import ceil 4 | 5 | commands = enum("COM_INHIBIT", "NOP", "ACTIVE", "READ", "WRITE", "BURST_TERM", 6 | "PRECHARGE", "AUTO_REFRESH", "LOAD_MODE", "OUTPUT_EN", "OUTPUT_Z", "INVALID") 7 | 8 | states = enum("Uninitialized", "Initialized", "Idle", "Activating", "Active", 9 | "Read", "Reading", "Read_rdy", "Write", "Writing") 10 | 11 | # generic parameters 12 | FREQ_GHZ_G = SdramIntf.SDRAM_FREQ_C / 1000 13 | ENABLE_REFRESH_G = True 14 | NROWS_G = SdramIntf.SDRAM_NROWS_C 15 | T_INIT_G = SdramIntf.SDRAM_T_INIT_C # min initialization interval (ns). 16 | T_RAS_G = SdramIntf.SDRAM_T_RAS_C # min interval between active to precharge commands (ns). 17 | T_RCD_G = SdramIntf.SDRAM_T_RCD_C # min interval between active and R/W commands (ns). 18 | T_REF_G = SdramIntf.SDRAM_T_REF_C # maximum refresh interval (ns). 19 | T_RFC_G = SdramIntf.SDRAM_T_RFC_C # duration of refresh operation (ns). 20 | T_RP_G = SdramIntf.SDRAM_T_RP_C # min precharge command duration (ns). 21 | T_XSR_G = SdramIntf.SDRAM_T_XSR_C # exit self-refresh time (ns). 22 | 23 | # delay constants 24 | INIT_CYCLES_C = int(ceil(T_INIT_G * FREQ_GHZ_G)) 25 | RP_CYCLES_C = int(ceil(T_RP_G * FREQ_GHZ_G)) 26 | RFC_CYCLES_C = int(ceil(T_RFC_G * FREQ_GHZ_G)) 27 | REF_CYCLES_C = int(ceil(T_REF_G * FREQ_GHZ_G / NROWS_G)) 28 | RCD_CYCLES_C = int(ceil(T_RCD_G * FREQ_GHZ_G/10)) 29 | RAS_CYCLES_C = int(ceil(T_RAS_G * FREQ_GHZ_G)) 30 | MODE_CYCLES_C = 2 31 | CAS_CYCLES_C = 3 32 | WR_CYCLES_C = 2 33 | RFSH_OPS_C = 8 # number of refresh operations needed to init SDRAM. 34 | 35 | 36 | # show_state and show_command are variables to show/hide log messages 37 | def sdram(clk, sd_intf, show_command=False): 38 | 39 | curr_command = Signal(commands.INVALID) 40 | control_logic_inst = control_logic(curr_command, sd_intf) 41 | 42 | curr_state = [State(0, sd_intf), State(1, sd_intf), State(2, sd_intf), State(3, sd_intf)] # state of eah bank 43 | 44 | # refresh variables 45 | ref_cycles_c = int(sd_intf.timing['ref'] / sd_intf.SDRAM_FREQ_C) 46 | rfsh_count_c = sd_intf.SDRAM_NROWS_C 47 | rfsh_timer = Signal(modbv(1, min=0, max=ref_cycles_c)) 48 | rfsh_count = Signal(intbv(0, min=0, max=rfsh_count_c)) 49 | 50 | @always(clk.posedge) 51 | def main_function(): 52 | if sd_intf.cke == 1: 53 | if show_command: 54 | print " SDRAM : [COMMAND] ", curr_command 55 | 56 | for bank_state in curr_state: 57 | bank_state.next_state(curr_command) 58 | 59 | if curr_command == commands.INVALID: 60 | print " SDRAM : [ERROR] Invalid command is given" 61 | elif curr_command == commands.LOAD_MODE: 62 | load_mode(sd_intf.bs, sd_intf.addr) 63 | elif curr_command == commands.ACTIVE: 64 | activate(sd_intf.bs, sd_intf.addr) 65 | elif curr_command == commands.READ: 66 | read(sd_intf.bs, sd_intf.addr) 67 | elif curr_command == commands.WRITE: 68 | write(sd_intf.bs, sd_intf.addr) 69 | elif curr_command == commands.PRECHARGE: 70 | precharge(sd_intf.bs, sd_intf.addr) 71 | elif curr_command == commands.AUTO_REFRESH: 72 | rfsh_count.next = rfsh_count + 1 if rfsh_timer != 0 else 0 73 | 74 | rfsh_timer.next = (rfsh_timer + 1) 75 | if rfsh_timer == 0: 76 | if rfsh_count < rfsh_count_c: 77 | print " SDRAM : [ERROR] Refresh requirement is not met" 78 | 79 | @always(clk.negedge) 80 | def read_function(): 81 | bank_state = curr_state[sd_intf.bs.val] 82 | if bank_state.state == states.Read_rdy or bank_state.state == states.Reading: 83 | bank_state.driver.next = bank_state.data 84 | else: 85 | bank_state.driver.next = None 86 | 87 | def load_mode(bs, addr): 88 | 89 | cas = int(addr[7:4]) 90 | burst = 2**int(addr[3:0]) 91 | if addr[9] == 1: 92 | mode = "Single " 93 | else: 94 | mode = "Burst " 95 | print "Bank :", bs 96 | print "--------------------------" 97 | print " Mode | CAS | Burst " 98 | print "--------|-------|---------" 99 | print " %s| %i | %i " % (mode, cas, burst) 100 | print "--------------------------" 101 | 102 | def activate(bs, addr): 103 | if curr_state[bs.val].active_row: 104 | print " SDRAM : [ERROR] A row is already activated. Bank should be precharged first" 105 | return None 106 | if curr_state[bs.val].get_state() == states.Uninitialized: 107 | print " SDRAM : [ERROR] Bank is not in a good state. Too bad for you" 108 | return None 109 | curr_state[bs.val].active_row = addr.val 110 | 111 | def read(bs, addr): 112 | if not curr_state[bs.val].active_row: 113 | print " SDRAM : [ERROR] A row should be activated before trying to read" 114 | else: 115 | print " SDRAM : [READ]", addr, " Commnad registered " 116 | 117 | def write(bs, addr): 118 | if not curr_state[bs.val].active_row: 119 | print " SDRAM : [ERROR] A row should be activated before trying to write", addr 120 | 121 | def precharge(bs, addr): 122 | if addr.val[10] == 1: # Precharge all banks command 123 | for bank in curr_state: 124 | bank.active_row = None 125 | else: # Precharge selected bank 126 | curr_state[bs.val].active_row = None 127 | 128 | return instances() 129 | 130 | 131 | def control_logic(curr_command, sd_intf): 132 | 133 | @always_comb 134 | def decode(): 135 | # detect the registered command 136 | if sd_intf.cs == 1: 137 | # cs ras cas we dqm : H X X X X 138 | curr_command.next = commands.COM_INHIBIT 139 | else: 140 | if sd_intf.ras == 1: 141 | if sd_intf.cas == 1: 142 | if sd_intf.we == 1: 143 | # cs ras cas we dqm : L H H H X 144 | curr_command.next = commands.NOP 145 | else: 146 | # cs ras cas we dqm : L H H L X 147 | curr_command.next = commands.BURST_TERM 148 | else: 149 | if sd_intf.we == 1: 150 | # cs ras cas we dqm : L H L H X 151 | curr_command.next = commands.READ 152 | else: 153 | # cs ras cas we dqm : L H L L X 154 | curr_command.next = commands.WRITE 155 | else: 156 | if sd_intf.cas == 1: 157 | if sd_intf.we == 1: 158 | # cs ras cas we dqm : L L H H X 159 | curr_command.next = commands.ACTIVE 160 | else: 161 | # cs ras cas we dqm : L L H L X 162 | curr_command.next = commands.PRECHARGE 163 | else: 164 | if sd_intf.we == 1: 165 | # cs ras cas we dqm : L L L H X 166 | curr_command.next = commands.AUTO_REFRESH 167 | else: 168 | # cs ras cas we dqm : L L L L X 169 | curr_command.next = commands.LOAD_MODE 170 | 171 | return decode 172 | 173 | 174 | class State: 175 | 176 | def __init__(self, bank_id, sd_intf): 177 | self.state = states.Uninitialized 178 | self.init_time = now() 179 | self.wait = 0 180 | self.bank_id = bank_id 181 | self.memory = {} 182 | self.sd_intf = sd_intf 183 | self.driver = sd_intf.dq.driver() 184 | self.active_row = None 185 | self.addr = None 186 | self.data = None 187 | self.tick = 0 188 | 189 | def next_state(self, curr_command): 190 | self.wait += 1 191 | if self.state == states.Uninitialized: 192 | if self.wait >= INIT_CYCLES_C: 193 | print " BANK", self.bank_id, "STATE : [CHANGE] Uninitialized -> Initialized @ ", now() 194 | self.state = states.Initialized 195 | self.wait = 0 196 | 197 | elif self.state == states.Idle or self.state == states.Initialized: 198 | self.data = 0 199 | # Reading command 200 | if curr_command == commands.READ and self.bank_id == self.sd_intf.bs.val: 201 | self.state = states.Reading 202 | self.wait = 0 203 | if self.sd_intf: 204 | self.addr = self.sd_intf.addr.val 205 | # Writing command 206 | elif curr_command == commands.WRITE and self.bank_id == self.sd_intf.bs.val: 207 | self.state = states.Writing 208 | self.wait = 0 209 | if self.sd_intf: 210 | self.addr = self.sd_intf.addr.val 211 | self.data = self.sd_intf.dq.val 212 | 213 | elif self.state == states.Reading: 214 | if self.wait >= CAS_CYCLES_C - 1: 215 | self.state = states.Read_rdy 216 | self.wait = 0 217 | if self.active_row: 218 | self.data = self.memory[self.active_row * 10000 + self.addr] 219 | print " STATE : [READ] Data Ready @ ", now(), " value : ", self.data 220 | 221 | elif self.state == states.Read_rdy: 222 | self.state = states.Idle 223 | self.wait = 0 224 | self.driver.next = None 225 | 226 | elif self.state == states.Writing: 227 | if self.wait >= RCD_CYCLES_C: 228 | self.state = states.Idle 229 | self.wait = 0 230 | if self.active_row: 231 | print " DATA : [WRITE] Addr:", self.addr, " Data:", self.data 232 | self.memory[self.active_row * 10000 + self.addr] = self.data 233 | 234 | def get_state(self): 235 | return self.state 236 | 237 | def get_data(self): 238 | return self.data 239 | -------------------------------------------------------------------------------- /sdram_cntl.py: -------------------------------------------------------------------------------- 1 | from math import ceil 2 | from sd_intf import * 3 | from host_intf import * 4 | 5 | 6 | def sdram_cntl(clk_i, host_intf, sd_intf): 7 | 8 | # commands to SDRAM ce ras cas we dqml dqmh 9 | nop_cmd_c = int(sd_intf.SDRAM_NOP_CMD_C) # intbv("011100")[6:] #0,1,1,1,0,0 10 | active_cmd_c = int(sd_intf.SDRAM_ACTIVE_CMD_C) # intbv("001100")[6:] #0,0,1,1,0,0 11 | read_cmd_c = int(sd_intf.SDRAM_READ_CMD_C) # intbv("010100")[6:] # 0,1,0,1,0,0 12 | write_cmd_c = int(sd_intf.SDRAM_WRITE_CMD_C) # intbv("010000")[6:] # 0,1,0,0,0,0 13 | pchg_cmd_c = int(sd_intf.SDRAM_PCHG_CMD_C) # intbv("001000")[6:] # 0,0,1,0,0,0 14 | mode_cmd_c = int(sd_intf.SDRAM_MODE_CMD_C) # intbv("000000")[6:] # 0,0,0,0,0,0 15 | rfsh_cmd_c = int(sd_intf.SDRAM_RFSH_CMD_C) # intbv("000100")[6:] # 0,0,0,1,0,0 16 | mode_c = int(sd_intf.SDRAM_MODE_C) # intbv("00_0_00_011_0_000")[12:] mode command for set_mode command 17 | 18 | # generic parameters 19 | freq_ghz_g = sd_intf.SDRAM_FREQ_C / 1000 20 | # ENABLE_REFRESH_G = True 21 | nrows_g = sd_intf.SDRAM_NROWS_C 22 | t_ref_g = sd_intf.SDRAM_T_REF_C 23 | t_init_g = sd_intf.SDRAM_T_INIT_C # min initialization interval (ns). 24 | t_ras_g = sd_intf.SDRAM_T_RAS_C # min interval between active to precharge commands (ns). 25 | t_rcd_g = sd_intf.SDRAM_T_RCD_C # min interval between active and R/W commands (ns). 26 | t_ref_g = sd_intf.SDRAM_T_REF_C # maximum refresh interval (ns). 27 | t_rfc_g = sd_intf.SDRAM_T_RFC_C # duration of refresh operation (ns). 28 | t_rp_g = sd_intf.SDRAM_T_RP_C # min precharge command duration (ns). 29 | t_xsr_g = sd_intf.SDRAM_T_XSR_C # exit self-refresh time (ns). 30 | 31 | # delay constants 32 | init_cycles_c = int(ceil(t_init_g * freq_ghz_g)) 33 | rp_cycles_c = int(ceil(t_rp_g * freq_ghz_g)) 34 | rfc_cycles_c = int(ceil(t_rfc_g * freq_ghz_g)) 35 | ref_cycles_c = int(ceil(t_ref_g * freq_ghz_g / nrows_g)) 36 | rcd_cycles_c = int(ceil(t_rcd_g * freq_ghz_g)) 37 | ras_cycles_c = int(ceil(t_ras_g * freq_ghz_g)) 38 | mode_cycles_c = 2 39 | cas_cycles_c = 3 40 | wr_cycles_c = 2 41 | rfsh_ops_c = 8 # number of refresh operations needed to init SDRAM. 42 | 43 | # constant values 44 | all_banks_c = int(sd_intf.SDRAM_ALL_BANKS_C) # value of CMDBIT to select all banks 45 | one_bank_c = int(sd_intf.SDRAM_ONE_BANK_C) 46 | input_c = bool(0) # sDataDir_r bit 0 for INPUT 47 | output_c = bool(1) # sDataDir_r bit 1 for OUTPUT 48 | nop_c = bool(0) 49 | read_c = bool(1) 50 | write_c = bool(1) 51 | ba_len_c = 2 52 | col_len_c = int(log(sd_intf.SDRAM_NCOLS_C, 2)) 53 | row_len_c = int(log(sd_intf.SDRAM_NROWS_C, 2)) 54 | 55 | # states of the SDRAM controller state machine 56 | cntlstatetype = enum( 57 | 'INITWAIT', # initialization - waiting for power-on initialization to complete. 58 | 'INITPCHG', # initialization - initial precharge of SDRAM banks. 59 | 'INITSETMODE', # initialization - set SDRAM mode. 60 | 'INITRFSH', # initialization - do initial refreshes. 61 | 'RW', # read/write/refresh the SDRAM. 62 | 'ACTIVATE', # open a row of the SDRAM for reading/writing. 63 | 'REFRESHROW', # refresh a row of the SDRAM. 64 | 'SELFREFRESH' # keep SDRAM in self-refresh mode with CKE low. 65 | ) 66 | 67 | # state register and next state 68 | state_r = Signal(cntlstatetype.INITWAIT) 69 | state_x = Signal(cntlstatetype.INITWAIT) 70 | 71 | # timer registers 72 | timer_r = Signal(intbv(0, min=0, max=init_cycles_c+1)) # current sdram opt time 73 | timer_x = Signal(intbv(0, min=0, max=init_cycles_c+1)) 74 | 75 | reftimer_r = Signal(intbv(ref_cycles_c, min=0, max=ref_cycles_c+1)) # time between row refreshes 76 | reftimer_x = Signal(intbv(ref_cycles_c, min=0, max=ref_cycles_c+1)) 77 | 78 | rastimer_r = Signal(intbv(0, min=0, max=ras_cycles_c+1)) # active to precharge time 79 | rastimer_x = Signal(intbv(0, min=0, max=ras_cycles_c+1)) 80 | 81 | wrtimer_r = Signal(intbv(0, min=0, max=wr_cycles_c+1)) # write to precharge time 82 | wrtimer_x = Signal(intbv(0, min=0, max=wr_cycles_c+1)) 83 | 84 | rfshcntr_r = Signal(intbv(0, min=0, max=nrows_g+1)) # count refreshes that are needed 85 | rfshcntr_x = Signal(intbv(0, min=0, max=nrows_g+1)) 86 | 87 | # status signals 88 | activate_in_progress_s = Signal(bool(0)) 89 | rd_in_progress_s = Signal(bool(0)) 90 | wr_in_progress_s = Signal(bool(0)) 91 | 92 | # command assignment 93 | cmd_r = Signal(intbv(nop_cmd_c)[3:0]) # this should be [6:] last two digits and first digit removed because zero 94 | cmd_x = Signal(intbv(nop_cmd_c)[3:0]) 95 | 96 | saddr_r = Signal(intbv(0)[row_len_c:]) # ideally this should be sd_intf.addr_width but we dont use upper two bits 97 | saddr_x = Signal(intbv(0)[row_len_c:]) 98 | 99 | sdata_r = Signal(intbv(0)[sd_intf.data_width:]) 100 | sdata_x = Signal(intbv(0)[sd_intf.data_width:]) 101 | 102 | sdriver = sd_intf.dq.driver() 103 | 104 | sdramdata_r = Signal(intbv(0)[sd_intf.data_width:]) 105 | sdramdata_x = Signal(intbv(0)[sd_intf.data_width:]) 106 | 107 | sdatadir_r = Signal(input_c) 108 | sdatadir_x = Signal(input_c) 109 | 110 | activerow_r = [Signal(intbv(0)[row_len_c:]) for _ in range(2**ba_len_c)] # each bank will have a active row 111 | activerow_x = [Signal(intbv(0)[row_len_c:]) for _ in range(2**ba_len_c)] 112 | activeflag_r = [Signal(bool(0)) for _ in range(2**ba_len_c)] 113 | activeflag_x = [Signal(bool(0)) for _ in range(2**ba_len_c)] 114 | activebank_r = Signal(intbv(0)[2:]) 115 | activebank_x = Signal(intbv(0)[2:]) # banks with active rows 116 | doactivate_s = Signal(bool(0)) # request row activation if a new row is needed to activate 117 | 118 | rdpipeline_r = Signal(intbv(0)[cas_cycles_c+2:]) 119 | rdpipeline_x = Signal(intbv(0)[cas_cycles_c+2:]) 120 | 121 | wrpipeline_r = Signal(intbv(0)[cas_cycles_c+2:]) 122 | wrpipeline_x = Signal(intbv(0)[cas_cycles_c+2:]) 123 | 124 | ba_r = Signal(intbv(0)[ba_len_c:]) 125 | ba_x = Signal(intbv(0)[ba_len_c:]) 126 | 127 | bank_s = Signal(intbv(0)[ba_len_c:]) 128 | row_s = Signal(intbv(0)[row_len_c:]) 129 | col_s = Signal(intbv(0)[col_len_c:]) 130 | 131 | # pin assignment for SDRAM 132 | @always_comb 133 | def sdram_pin_map(): 134 | sd_intf.cke.next = 1 135 | sd_intf.cs.next = 0 # cmd_r[3] 136 | sd_intf.ras.next = cmd_r[2] 137 | sd_intf.cas.next = cmd_r[1] 138 | sd_intf.we.next = cmd_r[0] 139 | sd_intf.bs.next = bank_s 140 | sd_intf.addr.next = saddr_r 141 | # sd_intf.driver.next = sData_r if sDataDir_r == OUTPUT_C else None 142 | if sdatadir_r == output_c: 143 | sdriver.next = sdata_r 144 | else: 145 | sdriver.next = None 146 | sd_intf.dqml.next = 0 147 | sd_intf.dqmh.next = 0 148 | 149 | # pin assignment for HOST SIDE 150 | @always_comb 151 | def host_pin_map(): 152 | host_intf.done_o.next = rdpipeline_r[0] or wrpipeline_r[0] 153 | host_intf.data_o.next = sdramdata_r 154 | host_intf.rdPending_o.next = rd_in_progress_s 155 | sdata_x.next = host_intf.data_i 156 | 157 | # extract bank, row and column from controller address 158 | @always_comb 159 | def extract_addr(): 160 | # extract bank 161 | # Multiple active rows logic has been removed for now 162 | bank_s.next = host_intf.addr_i[ba_len_c+row_len_c+col_len_c:row_len_c+col_len_c] 163 | ba_x.next = host_intf.addr_i[ba_len_c+row_len_c+col_len_c:row_len_c+col_len_c] 164 | 165 | # extract row 166 | row_s.next = host_intf.addr_i[row_len_c+col_len_c:col_len_c] 167 | # extract column 168 | col_s.next = host_intf.addr_i[col_len_c:] 169 | 170 | @always_comb 171 | def do_active(): 172 | if bank_s != activebank_r or row_s != activerow_r[bank_s.val] or not activeflag_r[bank_s.val]: 173 | doactivate_s.next = True 174 | else: 175 | doactivate_s.next = False 176 | 177 | if rdpipeline_r[1] == read_c: 178 | sdramdata_x.next = sd_intf.dq 179 | else: 180 | sdramdata_x.next = sdramdata_r 181 | 182 | # update status signals 183 | # activateInProgress_s.next = True if rasTimer_r != 0 else False 184 | if rastimer_r != 0: 185 | activate_in_progress_s.next = True 186 | else: 187 | activate_in_progress_s.next = False 188 | 189 | # writeInProgress_s.next = True if wrTimer_r != 0 else False 190 | if wrtimer_r != 0: 191 | wr_in_progress_s.next = True 192 | else: 193 | wr_in_progress_s.next = False 194 | 195 | # rdInProgress_s.next = True if rdPipeline_r[CAS_CYCLES_C+2:1] != 0 else False 196 | if rdpipeline_r[cas_cycles_c+2:1] != 0: 197 | rd_in_progress_s.next = True 198 | else: 199 | rd_in_progress_s.next = False 200 | 201 | @always_comb 202 | def comb_func(): 203 | 204 | rdpipeline_x.next = concat(nop_c, rdpipeline_r[cas_cycles_c+2:1]) 205 | wrpipeline_x.next = intbv(nop_c)[cas_cycles_c+2:] 206 | 207 | # #################### Update the timers ########################### 208 | 209 | # row activation timer 210 | # rasTimer_x.next = rasTimer_r - 1 if rasTimer_r != 0 else rasTimer_r 211 | if rastimer_r != 0: 212 | rastimer_x.next = rastimer_r - 1 213 | else: 214 | rastimer_x.next = rastimer_r 215 | 216 | # write operation timer 217 | # wrTimer_x.next = wrTimer_r - 1 if wrTimer_r != 0 else wrTimer_r 218 | if wrtimer_r != 0: 219 | wrtimer_x.next = wrtimer_r - 1 220 | else: 221 | wrtimer_x.next = wrtimer_r 222 | 223 | # refresh timer 224 | # refTimer_x.next = refTimer_r - 1 if refTimer_r != 0 else REF_CYCLES_C 225 | if reftimer_r != 0: 226 | reftimer_x.next = reftimer_r - 1 227 | rfshcntr_x.next = rfshcntr_r 228 | else: 229 | reftimer_x.next = ref_cycles_c 230 | # if refTimer_r == 0: 231 | # on timeout, reload the timer with the interval between row refreshes 232 | # and increment the counter for the number of row refreshes that are needed 233 | # rfshCntr_x.next = rfshCntr_r + 1 if ENABLE_REFRESH_G else 0 234 | rfshcntr_x.next = rfshcntr_r + 1 235 | 236 | ###################################################################### 237 | 238 | # ################## code to remove latches ########################### 239 | cmd_x.next = cmd_r 240 | state_x.next = state_r 241 | saddr_x.next = saddr_r 242 | activebank_x.next = activebank_r 243 | sdatadir_x.next = sdatadir_r 244 | for index in range(2**ba_len_c): 245 | activeflag_x[index].next = activeflag_r[index] 246 | activerow_x[index].next = activerow_r[index] 247 | ###################################################################### 248 | 249 | if timer_r != 0: 250 | timer_x.next = timer_r - 1 251 | cmd_x.next = nop_cmd_c 252 | else: 253 | timer_x.next = timer_r 254 | 255 | if state_r == cntlstatetype.INITWAIT: 256 | # wait for SDRAM power-on initialization once the clock is stable 257 | timer_x.next = init_cycles_c # set timer for initialization duration 258 | state_x.next = cntlstatetype.INITPCHG 259 | 260 | elif state_r == cntlstatetype.INITPCHG: 261 | # all banks should be precharged after initialization 262 | cmd_x.next = pchg_cmd_c 263 | timer_x.next = rp_cycles_c # set timer for precharge operation duration 264 | state_x.next = cntlstatetype.INITRFSH 265 | saddr_x.next = all_banks_c # select all banks precharge 266 | rfshcntr_x.next = rfsh_ops_c 267 | # ## tempory line should be change ##### 268 | # state_x.next = CntlStateType.RW 269 | # ###################################### 270 | 271 | elif state_r == cntlstatetype.INITRFSH: 272 | # refreshing state 273 | cmd_x.next = rfsh_cmd_c 274 | timer_x.next = rfc_cycles_c 275 | rfshcntr_x.next = rfshcntr_r - 1 276 | if rfshcntr_r == 1: 277 | state_x.next = cntlstatetype.INITSETMODE 278 | 279 | elif state_r == cntlstatetype.INITSETMODE: 280 | cmd_x.next = mode_cmd_c 281 | timer_x.next = mode_cycles_c 282 | state_x.next = cntlstatetype.RW 283 | saddr_x.next = mode_c 284 | 285 | elif state_r == cntlstatetype.RW: 286 | 287 | if rfshcntr_r != 0: 288 | # wait for any activation, read or write before precharge 289 | if not activate_in_progress_s and not wr_in_progress_s and not rd_in_progress_s: 290 | cmd_x.next = pchg_cmd_c 291 | timer_x.next = rp_cycles_c 292 | state_x.next = cntlstatetype.REFRESHROW 293 | saddr_x.next = all_banks_c 294 | for index in range(2**ba_len_c): 295 | activeflag_x[index].next = False 296 | 297 | # for now leave row refresh need.. IT SHOULD COME HERE 298 | elif host_intf.rd_i: 299 | if ba_x == ba_r: 300 | if doactivate_s: # A new row need to be activated. PRECHARGE The bank 301 | # activate new row only if all previous activations, writes, reads are done 302 | if not activate_in_progress_s and not wr_in_progress_s and not rd_in_progress_s: 303 | cmd_x.next = pchg_cmd_c 304 | timer_x.next = rp_cycles_c 305 | state_x.next = cntlstatetype.ACTIVATE 306 | saddr_x.next = one_bank_c 307 | activeflag_x[bank_s].next = False 308 | elif not rd_in_progress_s: 309 | cmd_x.next = read_cmd_c 310 | sdatadir_x.next = input_c 311 | saddr_x.next = col_s 312 | rdpipeline_x.next = concat(read_c, rdpipeline_r[cas_cycles_c+2:1]) 313 | 314 | elif host_intf.wr_i: 315 | if ba_x == ba_r: 316 | if doactivate_s: 317 | # activate new row only if all previous activations, writes, reads are done 318 | if not activate_in_progress_s and not wr_in_progress_s and not rd_in_progress_s: 319 | cmd_x.next = pchg_cmd_c 320 | timer_x.next = rp_cycles_c 321 | state_x.next = cntlstatetype.ACTIVATE 322 | saddr_x.next = one_bank_c 323 | activeflag_x[bank_s].next = False 324 | 325 | elif not rd_in_progress_s: 326 | cmd_x.next = write_cmd_c 327 | sdatadir_x.next = output_c 328 | saddr_x.next = col_s 329 | wrpipeline_x.next = intbv(1)[cas_cycles_c+2:] 330 | wrtimer_x.next = wr_cycles_c 331 | 332 | else: 333 | cmd_x.next = nop_cmd_c 334 | state_x.next = cntlstatetype.RW 335 | 336 | elif state_r == cntlstatetype.ACTIVATE: 337 | cmd_x.next = active_cmd_c 338 | timer_x.next = rcd_cycles_c 339 | state_x.next = cntlstatetype.RW 340 | rastimer_x.next = ras_cycles_c 341 | saddr_x.next = row_s 342 | activebank_x.next = bank_s 343 | activerow_x[bank_s].next = row_s 344 | activeflag_x[bank_s].next = True 345 | 346 | elif state_r == cntlstatetype.REFRESHROW: 347 | cmd_x.next = rfsh_cmd_c 348 | timer_x.next = rfc_cycles_c 349 | state_x.next = cntlstatetype.RW 350 | rfshcntr_x.next = rfshcntr_r - 1 351 | 352 | else: 353 | state_x.next = cntlstatetype.INITWAIT 354 | 355 | @always_seq(clk_i.posedge, host_intf.rst_i) 356 | def seq_func(): 357 | 358 | state_r.next = state_x 359 | cmd_r.next = cmd_x 360 | 361 | saddr_r.next = saddr_x 362 | sdata_r.next = sdata_x 363 | sdatadir_r.next = sdatadir_x 364 | activebank_r.next = activebank_x 365 | sdramdata_r.next = sdramdata_x 366 | wrpipeline_r.next = wrpipeline_x 367 | rdpipeline_r.next = rdpipeline_x 368 | ba_r.next = ba_x 369 | # timers 370 | timer_r.next = timer_x 371 | rastimer_r.next = rastimer_x 372 | reftimer_r.next = reftimer_x 373 | wrtimer_r.next = wrtimer_x 374 | rfshcntr_r.next = rfshcntr_x 375 | for index in range(2**ba_len_c): 376 | activerow_r[index].next = activerow_x[index] 377 | activeflag_r[index].next = activeflag_x[index] 378 | 379 | return comb_func, seq_func, sdram_pin_map, host_pin_map, extract_addr, do_active 380 | -------------------------------------------------------------------------------- /test_controller.py: -------------------------------------------------------------------------------- 1 | from clk_driver import clk_driver 2 | from sdram import * 3 | from sdram_cntl import sdram_cntl 4 | from host_intf import HostIntf 5 | from sd_intf import * 6 | 7 | 8 | def test_readwrite(host_intf): 9 | 10 | @instance 11 | def test(): 12 | yield delay(140) 13 | yield host_intf.write(120, 23) 14 | yield host_intf.done_o.posedge 15 | yield host_intf.nop() 16 | yield delay(5) 17 | yield host_intf.read(120) 18 | yield host_intf.done_o.posedge 19 | 20 | print "Data Value : ", host_intf.data_o, " clk : ", now() 21 | return test 22 | 23 | clk_i = Signal(bool(0)) 24 | rst_i = ResetSignal(0, active=1, async=True) 25 | 26 | clkDriver_Inst = clk_driver(clk_i) 27 | sd_intf_Inst = SdramIntf() 28 | host_intf_Inst = HostIntf() 29 | 30 | sdram_Inst = sdram(clk_i, sd_intf_Inst, show_command=False) 31 | sdramCntl_Inst = sdram_cntl(clk_i, host_intf_Inst, sd_intf_Inst) 32 | # sdramCntl_Inst = traceSignals(MySdramCntl,host_intf_Inst,sd_intf_Inst) 33 | 34 | test_readWrite_Inst = test_readwrite(host_intf_Inst) 35 | 36 | sim = Simulation(clkDriver_Inst, sdram_Inst, sdramCntl_Inst, test_readWrite_Inst) 37 | sim.run(7500) 38 | -------------------------------------------------------------------------------- /test_sdram.py: -------------------------------------------------------------------------------- 1 | from myhdl import * 2 | from clk_driver import clk_driver 3 | from sd_intf import SdramIntf 4 | from sdram import sdram 5 | 6 | 7 | def test_readwrite(clk, sd_intf): 8 | 9 | driver = sd_intf.get_driver() 10 | 11 | @instance 12 | def test(): 13 | sd_intf.cke.next = 1 14 | yield sd_intf.nop(clk) 15 | yield delay(10000) 16 | yield sd_intf.load_mode(clk) 17 | yield sd_intf.nop(clk) 18 | yield sd_intf.activate(clk, 17) 19 | yield sd_intf.nop(clk) 20 | yield delay(10000) 21 | 22 | yield sd_intf.write(clk, driver, 20, 31) 23 | 24 | yield sd_intf.nop(clk) 25 | yield delay(100) 26 | yield sd_intf.read(clk, 20) 27 | 28 | yield sd_intf.nop(clk) 29 | yield delay(4) 30 | print "sd_intf dq = ", sd_intf.dq.val, " @ ", now() 31 | 32 | return test 33 | 34 | clk = Signal(bool(0)) 35 | 36 | clk_driver_Inst = clk_driver(clk) 37 | sd_intf_Inst = SdramIntf() 38 | sdram_Inst = sdram(clk, sd_intf_Inst) 39 | test_readWrite_Inst = test_readwrite(clk, sd_intf_Inst) 40 | 41 | sim = Simulation(clk_driver_Inst, sdram_Inst, test_readWrite_Inst) 42 | sim.run(25000) 43 | --------------------------------------------------------------------------------