def __init__(self, ulpi_reg): ReadAddress = Signal(6) write_fsm = FSM() self.submodules += write_fsm def delay_clocks(v, d): for i in range(d): n = Signal() self.sync += n.eq(v) v = n return v ulpi_reg_wack = delay_clocks(ulpi_reg.wack, 2) ulpi_reg_rack = delay_clocks(ulpi_reg.rack, 2) write_fsm.delayed_enter("RESET", "WRITE_HS_SNOOP", 16) write_fsm.act("WRITE_HS_SNOOP", ulpi_reg.waddr.eq(0x4), ulpi_reg.wdata.eq(0x48), ulpi_reg.wreq.eq(1), If(ulpi_reg_wack, NextState("WRITE_IDLE"))) write_fsm.act("WRITE_IDLE", ulpi_reg.wreq.eq(0)) read_fsm = FSM() self.submodules += read_fsm read_fsm.delayed_enter("RESET", "READ_REG", 16) read_fsm.act("READ_REG", ulpi_reg.raddr.eq(ReadAddress), ulpi_reg.rreq.eq(1), If(ulpi_reg_rack, NextState("READ_ACK"))) self.sync += If(ulpi_reg_rack & ulpi_reg.rreq, ReadAddress.eq(ReadAddress + 1)) read_fsm.act("READ_ACK", ulpi_reg.rreq.eq(0), If(~ulpi_reg_rack, NextState("READ_WAIT"))) read_fsm.delayed_enter("READ_WAIT", "READ_REG", 16)
def __init__(self, phy_settings, geom_settings, timing_settings, bank_machines, refresher, dfi, lasmic): assert(phy_settings.nphases == len(dfi.phases)) # Command choosing requests = [bm.cmd for bm in bank_machines] choose_cmd = _CommandChooser(requests) choose_req = _CommandChooser(requests) self.comb += [ choose_cmd.want_reads.eq(0), choose_cmd.want_writes.eq(0) ] if phy_settings.nphases == 1: self.comb += [ choose_cmd.want_cmds.eq(1), choose_req.want_cmds.eq(1) ] self.submodules += choose_cmd, choose_req # Command steering nop = CommandRequest(geom_settings.mux_a, geom_settings.bank_a) commands = [nop, choose_cmd.cmd, choose_req.cmd, refresher.cmd] # nop must be 1st (STEER_NOP, STEER_CMD, STEER_REQ, STEER_REFRESH) = range(4) steerer = _Steerer(commands, dfi) self.submodules += steerer # Read/write turnaround read_available = Signal() write_available = Signal() self.comb += [ read_available.eq(optree("|", [req.stb & req.is_read for req in requests])), write_available.eq(optree("|", [req.stb & req.is_write for req in requests])) ] def anti_starvation(timeout): en = Signal() max_time = Signal() if timeout: t = timeout - 1 time = Signal(max=t+1) self.comb += max_time.eq(time == 0) self.sync += If(~en, time.eq(t) ).Elif(~max_time, time.eq(time - 1) ) else: self.comb += max_time.eq(0) return en, max_time read_time_en, max_read_time = anti_starvation(timing_settings.read_time) write_time_en, max_write_time = anti_starvation(timing_settings.write_time) # Refresh self.comb += [bm.refresh_req.eq(refresher.req) for bm in bank_machines] go_to_refresh = Signal() self.comb += go_to_refresh.eq(optree("&", [bm.refresh_gnt for bm in bank_machines])) # Datapath all_rddata = [p.rddata for p in dfi.phases] all_wrdata = [p.wrdata for p in dfi.phases] all_wrdata_mask = [p.wrdata_mask for p in dfi.phases] self.comb += [ lasmic.dat_r.eq(Cat(*all_rddata)), Cat(*all_wrdata).eq(lasmic.dat_w), Cat(*all_wrdata_mask).eq(~lasmic.dat_we) ] # Control FSM fsm = FSM() self.submodules += fsm def steerer_sel(steerer, phy_settings, r_w_n): r = [] for i in range(phy_settings.nphases): s = steerer.sel[i].eq(STEER_NOP) if r_w_n == "read": if i == phy_settings.rdphase: s = steerer.sel[i].eq(STEER_REQ) elif i == phy_settings.rdcmdphase: s = steerer.sel[i].eq(STEER_CMD) elif r_w_n == "write": if i == phy_settings.wrphase: s = steerer.sel[i].eq(STEER_REQ) elif i == phy_settings.wrcmdphase: s = steerer.sel[i].eq(STEER_CMD) else: raise ValueError r.append(s) return r fsm.act("READ", read_time_en.eq(1), choose_req.want_reads.eq(1), choose_cmd.cmd.ack.eq(1), choose_req.cmd.ack.eq(1), steerer_sel(steerer, phy_settings, "read"), If(write_available, # TODO: switch only after several cycles of ~read_available? If(~read_available | max_read_time, NextState("RTW")) ), If(go_to_refresh, NextState("REFRESH")) ) fsm.act("WRITE", write_time_en.eq(1), choose_req.want_writes.eq(1), choose_cmd.cmd.ack.eq(1), choose_req.cmd.ack.eq(1), steerer_sel(steerer, phy_settings, "write"), If(read_available, If(~write_available | max_write_time, NextState("WTR")) ), If(go_to_refresh, NextState("REFRESH")) ) fsm.act("REFRESH", steerer.sel[0].eq(STEER_REFRESH), If(~refresher.req, NextState("READ")) ) fsm.delayed_enter("RTW", "WRITE", phy_settings.read_latency-1) # FIXME: reduce this, actual limit is around (cl+1)/nphases fsm.delayed_enter("WTR", "READ", timing_settings.tWTR-1) # FIXME: workaround for zero-delay loop simulation problem with Icarus Verilog fsm.finalize() self.comb += refresher.ack.eq(fsm.state == fsm.encoding["REFRESH"]) self.submodules.bandwidth = Bandwidth(choose_req.cmd)
def __init__(self, geom_settings, timing_settings, address_align, bankn, req): self.refresh_req = Signal() self.refresh_gnt = Signal() self.cmd = CommandRequestRW(geom_settings.mux_a, geom_settings.bank_a) ### # Request FIFO self.submodules.req_fifo = SyncFIFO([("we", 1), ("adr", flen(req.adr))], timing_settings.req_queue_size) self.comb += [ self.req_fifo.din.we.eq(req.we), self.req_fifo.din.adr.eq(req.adr), self.req_fifo.we.eq(req.stb), req.req_ack.eq(self.req_fifo.writable), self.req_fifo.re.eq(req.dat_ack), req.lock.eq(self.req_fifo.readable), ] reqf = self.req_fifo.dout slicer = _AddressSlicer(geom_settings.col_a, address_align) # Row tracking has_openrow = Signal() openrow = Signal(geom_settings.row_a) hit = Signal() self.comb += hit.eq(openrow == slicer.row(reqf.adr)) track_open = Signal() track_close = Signal() self.sync += [ If(track_open, has_openrow.eq(1), openrow.eq(slicer.row(reqf.adr))), If(track_close, has_openrow.eq(0)), ] # Address generation s_row_adr = Signal() self.comb += [ self.cmd.ba.eq(bankn), If(s_row_adr, self.cmd.a.eq(slicer.row(reqf.adr))).Else(self.cmd.a.eq(slicer.col(reqf.adr))), ] # Respect write-to-precharge specification precharge_ok = Signal() t_unsafe_precharge = 2 + timing_settings.tWR - 1 unsafe_precharge_count = Signal(max=t_unsafe_precharge + 1) self.comb += precharge_ok.eq(unsafe_precharge_count == 0) self.sync += [ If(self.cmd.stb & self.cmd.ack & self.cmd.is_write, unsafe_precharge_count.eq(t_unsafe_precharge)).Elif( ~precharge_ok, unsafe_precharge_count.eq(unsafe_precharge_count - 1) ) ] # Control and command generation FSM fsm = FSM() self.submodules += fsm fsm.act( "REGULAR", If(self.refresh_req, NextState("REFRESH")).Elif( self.req_fifo.readable, If( has_openrow, If( hit, # NB: write-to-read specification is enforced by multiplexer self.cmd.stb.eq(1), req.dat_ack.eq(self.cmd.ack), self.cmd.is_read.eq(~reqf.we), self.cmd.is_write.eq(reqf.we), self.cmd.cas_n.eq(0), self.cmd.we_n.eq(~reqf.we), ).Else(NextState("PRECHARGE")), ).Else(NextState("ACTIVATE")), ), ) fsm.act( "PRECHARGE", # Notes: # 1. we are presenting the column address, A10 is always low # 2. since we always go to the ACTIVATE state, we do not need # to assert track_close. If( precharge_ok, self.cmd.stb.eq(1), If(self.cmd.ack, NextState("TRP")), self.cmd.ras_n.eq(0), self.cmd.we_n.eq(0), self.cmd.is_cmd.eq(1), ), ) fsm.act( "ACTIVATE", s_row_adr.eq(1), track_open.eq(1), self.cmd.stb.eq(1), self.cmd.is_cmd.eq(1), If(self.cmd.ack, NextState("TRCD")), self.cmd.ras_n.eq(0), ) fsm.act( "REFRESH", self.refresh_gnt.eq(precharge_ok), track_close.eq(1), self.cmd.is_cmd.eq(1), If(~self.refresh_req, NextState("REGULAR")), ) fsm.delayed_enter("TRP", "ACTIVATE", timing_settings.tRP - 1) fsm.delayed_enter("TRCD", "REGULAR", timing_settings.tRCD - 1)
def __init__(self, cachesize, lasmim): self.wishbone = wishbone.Interface() ### data_width = flen(self.wishbone.dat_r) if lasmim.dw > data_width and (lasmim.dw % data_width) != 0: raise ValueError("LASMI data width must be a multiple of {dw}".format(dw=data_width)) if lasmim.dw < data_width and (data_width % lasmim.dw) != 0: raise ValueError("WISHBONE data width must be a multiple of {dw}".format(dw=lasmim.dw)) # Split address: # TAG | LINE NUMBER | LINE OFFSET offsetbits = log2_int(max(lasmim.dw//data_width, 1)) addressbits = lasmim.aw + offsetbits linebits = log2_int(cachesize) - offsetbits tagbits = addressbits - linebits wordbits = data_width//lasmim.dw adr_offset, adr_line, adr_tag = split(self.wishbone.adr, offsetbits, linebits, tagbits) word = Signal(wordbits) if wordbits else None # Data memory data_mem = Memory(lasmim.dw*2**wordbits, 2**linebits) data_port = data_mem.get_port(write_capable=True, we_granularity=8) self.specials += data_mem, data_port write_from_lasmi = Signal() write_to_lasmi = Signal() if adr_offset is None: adr_offset_r = None else: adr_offset_r = Signal(offsetbits) self.sync += adr_offset_r.eq(adr_offset) self.comb += [ data_port.adr.eq(adr_line), If(write_from_lasmi, displacer(lasmim.dat_r, word, data_port.dat_w), displacer(Replicate(1, lasmim.dw//8), word, data_port.we) ).Else( data_port.dat_w.eq(Replicate(self.wishbone.dat_w, max(lasmim.dw//data_width, 1))), If(self.wishbone.cyc & self.wishbone.stb & self.wishbone.we & self.wishbone.ack, displacer(self.wishbone.sel, adr_offset, data_port.we, 2**offsetbits, reverse=True) ) ), If(write_to_lasmi, chooser(data_port.dat_r, word, lasmim.dat_w), lasmim.dat_we.eq(2**(lasmim.dw//8)-1) ), chooser(data_port.dat_r, adr_offset_r, self.wishbone.dat_r, reverse=True) ] # Tag memory tag_layout = [("tag", tagbits), ("dirty", 1)] tag_mem = Memory(layout_len(tag_layout), 2**linebits) tag_port = tag_mem.get_port(write_capable=True) self.specials += tag_mem, tag_port tag_do = Record(tag_layout) tag_di = Record(tag_layout) self.comb += [ tag_do.raw_bits().eq(tag_port.dat_r), tag_port.dat_w.eq(tag_di.raw_bits()) ] self.comb += [ tag_port.adr.eq(adr_line), tag_di.tag.eq(adr_tag) ] if word is not None: self.comb += lasmim.adr.eq(Cat(word, adr_line, tag_do.tag)) else: self.comb += lasmim.adr.eq(Cat(adr_line, tag_do.tag)) # Lasmim word computation, word_clr and word_inc will be simplified # at synthesis when wordbits=0 word_clr = Signal() word_inc = Signal() if word is not None: self.sync += \ If(word_clr, word.eq(0), ).Elif(word_inc, word.eq(word+1) ) def word_is_last(word): if word is not None: return word == 2**wordbits-1 else: return 1 # Control FSM assert(lasmim.write_latency >= 1 and lasmim.read_latency >= 1) fsm = FSM(reset_state="IDLE") self.submodules += fsm fsm.delayed_enter("EVICT_DATAD", "EVICT_DATA", lasmim.write_latency-1) fsm.delayed_enter("REFILL_DATAD", "REFILL_DATA", lasmim.read_latency-1) fsm.act("IDLE", If(self.wishbone.cyc & self.wishbone.stb, NextState("TEST_HIT")) ) fsm.act("TEST_HIT", word_clr.eq(1), If(tag_do.tag == adr_tag, self.wishbone.ack.eq(1), If(self.wishbone.we, tag_di.dirty.eq(1), tag_port.we.eq(1) ), NextState("IDLE") ).Else( If(tag_do.dirty, NextState("EVICT_REQUEST") ).Else( NextState("REFILL_WRTAG") ) ) ) fsm.act("EVICT_REQUEST", lasmim.stb.eq(1), lasmim.we.eq(1), If(lasmim.req_ack, NextState("EVICT_WAIT_DATA_ACK")) ) fsm.act("EVICT_WAIT_DATA_ACK", If(lasmim.dat_ack, NextState("EVICT_DATAD")) ) fsm.act("EVICT_DATA", write_to_lasmi.eq(1), word_inc.eq(1), If(word_is_last(word), NextState("REFILL_WRTAG"), ).Else( NextState("EVICT_REQUEST") ) ) fsm.act("REFILL_WRTAG", # Write the tag first to set the LASMI address tag_port.we.eq(1), word_clr.eq(1), NextState("REFILL_REQUEST") ) fsm.act("REFILL_REQUEST", lasmim.stb.eq(1), If(lasmim.req_ack, NextState("REFILL_WAIT_DATA_ACK")) ) fsm.act("REFILL_WAIT_DATA_ACK", If(lasmim.dat_ack, NextState("REFILL_DATAD")) ) fsm.act("REFILL_DATA", write_from_lasmi.eq(1), word_inc.eq(1), If(word_is_last(word), NextState("TEST_HIT"), ).Else( NextState("REFILL_REQUEST") ) )
def __init__(self, cachesize, lasmim): self.wishbone = wishbone.Interface() ### data_width = flen(self.wishbone.dat_r) if lasmim.dw > data_width and (lasmim.dw % data_width) != 0: raise ValueError( "LASMI data width must be a multiple of {dw}".format( dw=data_width)) if lasmim.dw < data_width and (data_width % lasmim.dw) != 0: raise ValueError( "WISHBONE data width must be a multiple of {dw}".format( dw=lasmim.dw)) # Split address: # TAG | LINE NUMBER | LINE OFFSET offsetbits = log2_int(max(lasmim.dw // data_width, 1)) addressbits = lasmim.aw + offsetbits linebits = log2_int(cachesize) - offsetbits tagbits = addressbits - linebits wordbits = log2_int(max(data_width // lasmim.dw, 1)) adr_offset, adr_line, adr_tag = split(self.wishbone.adr, offsetbits, linebits, tagbits) word = Signal(wordbits) if wordbits else None # Data memory data_mem = Memory(lasmim.dw * 2**wordbits, 2**linebits) data_port = data_mem.get_port(write_capable=True, we_granularity=8) self.specials += data_mem, data_port write_from_lasmi = Signal() write_to_lasmi = Signal() if adr_offset is None: adr_offset_r = None else: adr_offset_r = Signal(offsetbits) self.sync += adr_offset_r.eq(adr_offset) self.comb += [ data_port.adr.eq(adr_line), If(write_from_lasmi, displacer(lasmim.dat_r, word, data_port.dat_w), displacer(Replicate(1, lasmim.dw // 8), word, data_port.we)).Else( data_port.dat_w.eq( Replicate(self.wishbone.dat_w, max(lasmim.dw // data_width, 1))), If( self.wishbone.cyc & self.wishbone.stb & self.wishbone.we & self.wishbone.ack, displacer(self.wishbone.sel, adr_offset, data_port.we, 2**offsetbits, reverse=True))), If(write_to_lasmi, chooser(data_port.dat_r, word, lasmim.dat_w), lasmim.dat_we.eq(2**(lasmim.dw // 8) - 1)), chooser(data_port.dat_r, adr_offset_r, self.wishbone.dat_r, reverse=True) ] # Tag memory tag_layout = [("tag", tagbits), ("dirty", 1)] tag_mem = Memory(layout_len(tag_layout), 2**linebits) tag_port = tag_mem.get_port(write_capable=True) self.specials += tag_mem, tag_port tag_do = Record(tag_layout) tag_di = Record(tag_layout) self.comb += [ tag_do.raw_bits().eq(tag_port.dat_r), tag_port.dat_w.eq(tag_di.raw_bits()) ] self.comb += [tag_port.adr.eq(adr_line), tag_di.tag.eq(adr_tag)] if word is not None: self.comb += lasmim.adr.eq(Cat(word, adr_line, tag_do.tag)) else: self.comb += lasmim.adr.eq(Cat(adr_line, tag_do.tag)) # Lasmim word computation, word_clr and word_inc will be simplified # at synthesis when wordbits=0 word_clr = Signal() word_inc = Signal() if word is not None: self.sync += \ If(word_clr, word.eq(0), ).Elif(word_inc, word.eq(word+1) ) def word_is_last(word): if word is not None: return word == 2**wordbits - 1 else: return 1 # Control FSM assert (lasmim.write_latency >= 1 and lasmim.read_latency >= 1) fsm = FSM(reset_state="IDLE") self.submodules += fsm fsm.delayed_enter("EVICT_DATAD", "EVICT_DATA", lasmim.write_latency - 1) fsm.delayed_enter("REFILL_DATAD", "REFILL_DATA", lasmim.read_latency - 1) fsm.act( "IDLE", If(self.wishbone.cyc & self.wishbone.stb, NextState("TEST_HIT"))) fsm.act( "TEST_HIT", word_clr.eq(1), If(tag_do.tag == adr_tag, self.wishbone.ack.eq(1), If(self.wishbone.we, tag_di.dirty.eq(1), tag_port.we.eq(1)), NextState("IDLE")).Else( If(tag_do.dirty, NextState("EVICT_REQUEST")).Else( NextState("REFILL_WRTAG")))) fsm.act("EVICT_REQUEST", lasmim.stb.eq(1), lasmim.we.eq(1), If(lasmim.req_ack, NextState("EVICT_WAIT_DATA_ACK"))) fsm.act("EVICT_WAIT_DATA_ACK", If(lasmim.dat_ack, NextState("EVICT_DATAD"))) fsm.act( "EVICT_DATA", write_to_lasmi.eq(1), word_inc.eq(1), If( word_is_last(word), NextState("REFILL_WRTAG"), ).Else(NextState("EVICT_REQUEST"))) fsm.act( "REFILL_WRTAG", # Write the tag first to set the LASMI address tag_port.we.eq(1), word_clr.eq(1), NextState("REFILL_REQUEST")) fsm.act("REFILL_REQUEST", lasmim.stb.eq(1), If(lasmim.req_ack, NextState("REFILL_WAIT_DATA_ACK"))) fsm.act("REFILL_WAIT_DATA_ACK", If(lasmim.dat_ack, NextState("REFILL_DATAD"))) fsm.act( "REFILL_DATA", write_from_lasmi.eq(1), word_inc.eq(1), If( word_is_last(word), NextState("TEST_HIT"), ).Else(NextState("REFILL_REQUEST")))
def __init__(self, geom_settings, timing_settings, controller_settings, address_align, bankn, req): self.refresh_req = Signal() self.refresh_gnt = Signal() self.cmd = CommandRequestRW(geom_settings.addressbits, geom_settings.bankbits) ### # Request FIFO layout = [("we", 1), ("adr", len(req.adr))] req_in = Record(layout) reqf = Record(layout) self.submodules.req_fifo = SyncFIFO(layout_len(layout), controller_settings.req_queue_size) self.comb += [ self.req_fifo.din.eq(req_in.raw_bits()), reqf.raw_bits().eq(self.req_fifo.dout) ] self.comb += [ req_in.we.eq(req.we), req_in.adr.eq(req.adr), self.req_fifo.we.eq(req.stb), req.req_ack.eq(self.req_fifo.writable), self.req_fifo.re.eq(req.dat_w_ack | req.dat_r_ack), req.lock.eq(self.req_fifo.readable) ] slicer = _AddressSlicer(geom_settings.colbits, address_align) # Row tracking has_openrow = Signal() openrow = Signal(geom_settings.rowbits) hit = Signal() self.comb += hit.eq(openrow == slicer.row(reqf.adr)) track_open = Signal() track_close = Signal() self.sync += [ If(track_open, has_openrow.eq(1), openrow.eq(slicer.row(reqf.adr))), If(track_close, has_openrow.eq(0)) ] # Address generation s_row_adr = Signal() self.comb += [ self.cmd.ba.eq(bankn), If(s_row_adr, self.cmd.a.eq(slicer.row(reqf.adr))).Else( self.cmd.a.eq(slicer.col(reqf.adr))) ] # Respect write-to-precharge specification precharge_ok = Signal() t_unsafe_precharge = 2 + timing_settings.tWR - 1 unsafe_precharge_count = Signal(max=t_unsafe_precharge + 1) self.comb += precharge_ok.eq(unsafe_precharge_count == 0) self.sync += [ If(self.cmd.stb & self.cmd.ack & self.cmd.is_write, unsafe_precharge_count.eq(t_unsafe_precharge)).Elif( ~precharge_ok, unsafe_precharge_count.eq(unsafe_precharge_count - 1)) ] # Control and command generation FSM fsm = FSM() self.submodules += fsm fsm.act( "REGULAR", If(self.refresh_req, NextState("REFRESH")).Elif( self.req_fifo.readable, If( has_openrow, If( hit, # NB: write-to-read specification is enforced by multiplexer self.cmd.stb.eq(1), req.dat_w_ack.eq(self.cmd.ack & reqf.we), req.dat_r_ack.eq(self.cmd.ack & ~reqf.we), self.cmd.is_read.eq(~reqf.we), self.cmd.is_write.eq(reqf.we), self.cmd.cas_n.eq(0), self.cmd.we_n.eq(~reqf.we)).Else( NextState("PRECHARGE"))).Else( NextState("ACTIVATE")))) fsm.act( "PRECHARGE", # Notes: # 1. we are presenting the column address, A10 is always low # 2. since we always go to the ACTIVATE state, we do not need # to assert track_close. If(precharge_ok, self.cmd.stb.eq(1), If(self.cmd.ack, NextState("TRP")), self.cmd.ras_n.eq(0), self.cmd.we_n.eq(0), self.cmd.is_cmd.eq(1))) fsm.act("ACTIVATE", s_row_adr.eq(1), track_open.eq(1), self.cmd.stb.eq(1), self.cmd.is_cmd.eq(1), If(self.cmd.ack, NextState("TRCD")), self.cmd.ras_n.eq(0)) fsm.act("REFRESH", self.refresh_gnt.eq(precharge_ok), track_close.eq(1), self.cmd.is_cmd.eq(1), If(~self.refresh_req, NextState("REGULAR"))) fsm.delayed_enter("TRP", "ACTIVATE", timing_settings.tRP - 1) fsm.delayed_enter("TRCD", "REGULAR", timing_settings.tRCD - 1)
def __init__(self, phy_settings, geom_settings, timing_settings): if phy_settings.memtype in ["SDR"]: burst_length = phy_settings.nphases*1 # command multiplication*SDR elif phy_settings.memtype in ["DDR", "LPDDR", "DDR2", "DDR3"]: burst_length = phy_settings.nphases*2 # command multiplication*DDR address_align = log2_int(burst_length) nbanks = range(2**geom_settings.bank_a) A10_ENABLED = 0 COLUMN = 1 ROW = 2 rdphase = phy_settings.rdphase wrphase = phy_settings.wrphase self.dfi = dfi = dfibus.Interface(geom_settings.mux_a, geom_settings.bank_a, phy_settings.dfi_d, phy_settings.nphases) self.bus = bus = wishbone.Interface(data_width=phy_settings.nphases*flen(dfi.phases[rdphase].rddata)) slicer = _AddressSlicer(geom_settings.col_a, geom_settings.bank_a, geom_settings.row_a, address_align) refresh_req = Signal() refresh_ack = Signal() refresh_counter = Signal(max=timing_settings.tREFI+1) hit = Signal() row_open = Signal() row_closeall = Signal() addr_sel = Signal(max=3, reset=A10_ENABLED) has_curbank_openrow = Signal() # Extra bit means row is active when asserted self.openrow = openrow = Array(Signal(geom_settings.row_a + 1) for b in nbanks) self.comb += [ hit.eq(openrow[slicer.bank(bus.adr)] == Cat(slicer.row(bus.adr), 1)), has_curbank_openrow.eq(openrow[slicer.bank(bus.adr)][-1]), bus.dat_r.eq(Cat(phase.rddata for phase in dfi.phases)), Cat(phase.wrdata for phase in dfi.phases).eq(bus.dat_w), Cat(phase.wrdata_mask for phase in dfi.phases).eq(~bus.sel), ] for phase in dfi.phases: self.comb += [ phase.cke.eq(1), phase.cs_n.eq(0), phase.address.eq(Array([2**10, slicer.col(bus.adr), slicer.row(bus.adr)])[addr_sel]), phase.bank.eq(slicer.bank(bus.adr)) ] for b in nbanks: self.sync += [ If(row_open & (b == slicer.bank(bus.adr)), openrow[b].eq(Cat(slicer.row(bus.adr), 1)), ), If(row_closeall, openrow[b][-1].eq(0) ) ] self.sync += [ If(refresh_ack, refresh_req.eq(0) ), If(refresh_counter == 0, refresh_counter.eq(timing_settings.tREFI), refresh_req.eq(1) ).Else( refresh_counter.eq(refresh_counter - 1) ) ] fsm = FSM() self.submodules += fsm fsm.act("IDLE", If(refresh_req, NextState("PRECHARGEALL") ).Elif(bus.stb & bus.cyc, If(hit & bus.we, NextState("WRITE") ), If(hit & ~bus.we, NextState("READ") ), If(has_curbank_openrow & ~hit, NextState("PRECHARGE") ), If(~has_curbank_openrow, NextState("ACTIVATE") ), ) ) fsm.act("READ", # We output Column bits at address pins so A10 is 0 # to disable row Auto-Precharge dfi.phases[rdphase].ras_n.eq(1), dfi.phases[rdphase].cas_n.eq(0), dfi.phases[rdphase].we_n.eq(1), dfi.phases[rdphase].rddata_en.eq(1), addr_sel.eq(COLUMN), NextState("READ-WAIT-ACK"), ) fsm.act("READ-WAIT-ACK", If(dfi.phases[rdphase].rddata_valid, NextState("IDLE"), bus.ack.eq(1) ).Else( NextState("READ-WAIT-ACK") ) ) fsm.act("WRITE", dfi.phases[wrphase].ras_n.eq(1), dfi.phases[wrphase].cas_n.eq(0), dfi.phases[wrphase].we_n.eq(0), dfi.phases[wrphase].wrdata_en.eq(1), addr_sel.eq(COLUMN), bus.ack.eq(1), NextState("IDLE") ) fsm.act("PRECHARGEALL", row_closeall.eq(1), dfi.phases[rdphase].ras_n.eq(0), dfi.phases[rdphase].cas_n.eq(1), dfi.phases[rdphase].we_n.eq(0), addr_sel.eq(A10_ENABLED), NextState("PRE-REFRESH") ) fsm.act("PRECHARGE", # Notes: # 1. we are presenting the column address so that A10 is low # 2. since we always go to the ACTIVATE state, we do not need # to assert row_close because it will be reopen right after. NextState("TRP"), addr_sel.eq(COLUMN), dfi.phases[rdphase].ras_n.eq(0), dfi.phases[rdphase].cas_n.eq(1), dfi.phases[rdphase].we_n.eq(0) ) fsm.act("ACTIVATE", row_open.eq(1), NextState("TRCD"), dfi.phases[rdphase].ras_n.eq(0), dfi.phases[rdphase].cas_n.eq(1), dfi.phases[rdphase].we_n.eq(1), addr_sel.eq(ROW) ) fsm.act("REFRESH", refresh_ack.eq(1), dfi.phases[rdphase].ras_n.eq(0), dfi.phases[rdphase].cas_n.eq(0), dfi.phases[rdphase].we_n.eq(1), NextState("POST-REFRESH") ) fsm.delayed_enter("TRP", "ACTIVATE", timing_settings.tRP-1) fsm.delayed_enter("PRE-REFRESH", "REFRESH", timing_settings.tRP-1) fsm.delayed_enter("TRCD", "IDLE", timing_settings.tRCD-1) fsm.delayed_enter("POST-REFRESH", "IDLE", timing_settings.tRFC-1)
def __init__(self, phy_settings, geom_settings, timing_settings, controller_settings, bank_machines, refresher, dfi, lasmic): assert(phy_settings.nphases == len(dfi.phases)) self.phy_settings = phy_settings # Command choosing requests = [bm.cmd for bm in bank_machines] self.submodules.choose_cmd = choose_cmd = _CommandChooser(requests) self.submodules.choose_req = choose_req = _CommandChooser(requests) self.comb += [ choose_cmd.want_reads.eq(0), choose_cmd.want_writes.eq(0) ] if phy_settings.nphases == 1: self.comb += [ choose_cmd.want_cmds.eq(1), choose_req.want_cmds.eq(1) ] # Command steering nop = CommandRequest(geom_settings.addressbits, geom_settings.bankbits) commands = [nop, choose_cmd.cmd, choose_req.cmd, refresher.cmd] # nop must be 1st (STEER_NOP, STEER_CMD, STEER_REQ, STEER_REFRESH) = range(4) steerer = _Steerer(commands, dfi) self.submodules += steerer # Read/write turnaround read_available = Signal() write_available = Signal() self.comb += [ read_available.eq(reduce(or_, [req.stb & req.is_read for req in requests])), write_available.eq(reduce(or_, [req.stb & req.is_write for req in requests])) ] def anti_starvation(timeout): en = Signal() max_time = Signal() if timeout: t = timeout - 1 time = Signal(max=t+1) self.comb += max_time.eq(time == 0) self.sync += If(~en, time.eq(t) ).Elif(~max_time, time.eq(time - 1) ) else: self.comb += max_time.eq(0) return en, max_time read_time_en, max_read_time = anti_starvation(controller_settings.read_time) write_time_en, max_write_time = anti_starvation(controller_settings.write_time) # Refresh self.comb += [bm.refresh_req.eq(refresher.req) for bm in bank_machines] go_to_refresh = Signal() self.comb += go_to_refresh.eq(reduce(and_, [bm.refresh_gnt for bm in bank_machines])) # Datapath all_rddata = [p.rddata for p in dfi.phases] all_wrdata = [p.wrdata for p in dfi.phases] all_wrdata_mask = [p.wrdata_mask for p in dfi.phases] self.comb += [ lasmic.dat_r.eq(Cat(*all_rddata)), Cat(*all_wrdata).eq(lasmic.dat_w), Cat(*all_wrdata_mask).eq(~lasmic.dat_we) ] # Control FSM fsm = FSM() self.submodules += fsm def steerer_sel(steerer, phy_settings, r_w_n): r = [] for i in range(phy_settings.nphases): s = steerer.sel[i].eq(STEER_NOP) if r_w_n == "read": if i == phy_settings.rdphase: s = steerer.sel[i].eq(STEER_REQ) elif i == phy_settings.rdcmdphase: s = steerer.sel[i].eq(STEER_CMD) elif r_w_n == "write": if i == phy_settings.wrphase: s = steerer.sel[i].eq(STEER_REQ) elif i == phy_settings.wrcmdphase: s = steerer.sel[i].eq(STEER_CMD) else: raise ValueError r.append(s) return r fsm.act("READ", read_time_en.eq(1), choose_req.want_reads.eq(1), choose_cmd.cmd.ack.eq(1), choose_req.cmd.ack.eq(1), steerer_sel(steerer, phy_settings, "read"), If(write_available, # TODO: switch only after several cycles of ~read_available? If(~read_available | max_read_time, NextState("RTW")) ), If(go_to_refresh, NextState("REFRESH")) ) fsm.act("WRITE", write_time_en.eq(1), choose_req.want_writes.eq(1), choose_cmd.cmd.ack.eq(1), choose_req.cmd.ack.eq(1), steerer_sel(steerer, phy_settings, "write"), If(read_available, If(~write_available | max_write_time, NextState("WTR")) ), If(go_to_refresh, NextState("REFRESH")) ) fsm.act("REFRESH", steerer.sel[0].eq(STEER_REFRESH), refresher.ack.eq(1), If(~refresher.req, NextState("READ")) ) fsm.delayed_enter("RTW", "WRITE", phy_settings.read_latency-1) # FIXME: reduce this, actual limit is around (cl+1)/nphases fsm.delayed_enter("WTR", "READ", timing_settings.tWTR-1)
def __init__(self, cachesize, lasmim, wbm=None): if wbm is None: wbm = wishbone.Interface() self.wishbone = wbm ### data_width = flen(self.wishbone.dat_r) if lasmim.dw < data_width: raise ValueError("LASMI data width must be >= {dw}".format(dw=data_width)) if (lasmim.dw % data_width) != 0: raise ValueError("LASMI data width must be a multiple of {dw}".format(dw=data_width)) # Split address: # TAG | LINE NUMBER | LINE OFFSET offsetbits = log2_int(lasmim.dw//data_width) addressbits = lasmim.aw + offsetbits linebits = log2_int(cachesize) - offsetbits tagbits = addressbits - linebits adr_offset, adr_line, adr_tag = split(self.wishbone.adr, offsetbits, linebits, tagbits) # Data memory data_mem = Memory(lasmim.dw, 2**linebits) data_port = data_mem.get_port(write_capable=True, we_granularity=8) self.specials += data_mem, data_port write_from_lasmi = Signal() write_to_lasmi = Signal() if adr_offset is None: adr_offset_r = None else: adr_offset_r = Signal(offsetbits) self.sync += adr_offset_r.eq(adr_offset) self.comb += [ data_port.adr.eq(adr_line), If(write_from_lasmi, data_port.dat_w.eq(lasmim.dat_r), data_port.we.eq(Replicate(1, lasmim.dw//8)) ).Else( data_port.dat_w.eq(Replicate(self.wishbone.dat_w, lasmim.dw//data_width)), If(self.wishbone.cyc & self.wishbone.stb & self.wishbone.we & self.wishbone.ack, displacer(self.wishbone.sel, adr_offset, data_port.we, 2**offsetbits, reverse=True) ) ), If(write_to_lasmi, lasmim.dat_w.eq(data_port.dat_r), lasmim.dat_we.eq(2**(lasmim.dw//8)-1) ), chooser(data_port.dat_r, adr_offset_r, self.wishbone.dat_r, reverse=True) ] # Tag memory tag_layout = [("tag", tagbits), ("dirty", 1)] tag_mem = Memory(layout_len(tag_layout), 2**linebits) tag_port = tag_mem.get_port(write_capable=True) self.specials += tag_mem, tag_port tag_do = Record(tag_layout) tag_di = Record(tag_layout) self.comb += [ tag_do.raw_bits().eq(tag_port.dat_r), tag_port.dat_w.eq(tag_di.raw_bits()) ] self.comb += [ tag_port.adr.eq(adr_line), tag_di.tag.eq(adr_tag), lasmim.adr.eq(Cat(adr_line, tag_do.tag)) ] # Control FSM assert(lasmim.write_latency >= 1 and lasmim.read_latency >= 1) fsm = FSM() self.submodules += fsm fsm.delayed_enter("EVICT_DATAD", "EVICT_DATA", lasmim.write_latency-1) fsm.delayed_enter("REFILL_DATAD", "REFILL_DATA", lasmim.read_latency-1) fsm.act("IDLE", If(self.wishbone.cyc & self.wishbone.stb, NextState("TEST_HIT")) ) fsm.act("TEST_HIT", If(tag_do.tag == adr_tag, self.wishbone.ack.eq(1), If(self.wishbone.we, tag_di.dirty.eq(1), tag_port.we.eq(1) ), NextState("IDLE") ).Else( If(tag_do.dirty, NextState("EVICT_REQUEST") ).Else( NextState("REFILL_WRTAG") ) ) ) fsm.act("EVICT_REQUEST", lasmim.stb.eq(1), lasmim.we.eq(1), If(lasmim.req_ack, NextState("EVICT_WAIT_DATA_ACK")) ) fsm.act("EVICT_WAIT_DATA_ACK", If(lasmim.dat_ack, NextState("EVICT_DATAD")) ) fsm.act("EVICT_DATA", write_to_lasmi.eq(1), NextState("REFILL_WRTAG") ) fsm.act("REFILL_WRTAG", # Write the tag first to set the LASMI address tag_port.we.eq(1), NextState("REFILL_REQUEST") ) fsm.act("REFILL_REQUEST", lasmim.stb.eq(1), If(lasmim.req_ack, NextState("REFILL_WAIT_DATA_ACK")) ) fsm.act("REFILL_WAIT_DATA_ACK", If(lasmim.dat_ack, NextState("REFILL_DATAD")) ) fsm.act("REFILL_DATA", write_from_lasmi.eq(1), NextState("TEST_HIT") )