diff --git a/litedram/core/bankmachine.py b/litedram/core/bankmachine.py index 5df27e0..5f56954 100644 --- a/litedram/core/bankmachine.py +++ b/litedram/core/bankmachine.py @@ -31,8 +31,8 @@ class BankMachine(Module): self.req = req = Record(cmd_layout(aw)) self.refresh_req = Signal() self.refresh_gnt = Signal() + self.ras_allowed = ras_allowed = Signal() self.cas_allowed = cas_allowed = Signal() - self.activate_allowed = activate_allowed = Signal() a = settings.geom.addressbits ba = settings.geom.bankbits self.cmd = cmd = stream.Endpoint(cmd_request_rw_layout(a, ba)) @@ -132,9 +132,9 @@ class BankMachine(Module): fsm.act("ACTIVATE", sel_row_adr.eq(1), track_open.eq(1), - cmd.valid.eq(activate_allowed), + cmd.valid.eq(ras_allowed), cmd.is_cmd.eq(1), - If(cmd.ready & activate_allowed, + If(cmd.ready & ras_allowed, NextState("TRCD") ), cmd.ras.eq(1) diff --git a/litedram/core/multiplexer.py b/litedram/core/multiplexer.py index b38961c..5e908a8 100644 --- a/litedram/core/multiplexer.py +++ b/litedram/core/multiplexer.py @@ -20,6 +20,7 @@ class _CommandChooser(Module): a = len(requests[0].a) ba = len(requests[0].ba) + # cas/ras/we are 0 when valid is inactive self.cmd = cmd = stream.Endpoint(cmd_request_rw_layout(a, ba)) @@ -63,6 +64,19 @@ class _CommandChooser(Module): ) self.comb += arbiter.ce.eq(cmd.ready) + # helpers + def accept(self): + return self.cmd.valid & self.cmd.ready + + def activate(self): + return self.cmd.ras & ~self.cmd.cas & ~self.cmd.we + + def write(self): + return self.cmd.is_write + + def read(self): + return self.cmd.is_read + class _Steerer(Module): def __init__(self, commands, dfi): @@ -102,6 +116,42 @@ class _Steerer(Module): ] +class tXXDController(Module): + def __init__(self, txxd): + self.valid = Signal() + self.ready = Signal(reset=1) + + # # # + + if txxd is not None: + count = Signal(max=txxd+1) + self.sync += \ + If(self.valid, + count.eq(txxd-1) + ).Elif(~self.ready, + count.eq(count-1) + ) + self.comb += self.ready.eq(count == 0) + + +class tFAWController(Module): + def __init__(self, tfaw): + self.valid = Signal() + self.ready = Signal(reset=1) + + # # # + + if tfaw is not None: + count = Signal(max=tfaw) + window = Signal(tfaw) + self.sync += window.eq(Cat(self.valid, window)) + for i in range(tfaw): + next_count = Signal(max=tfaw) + self.comb += next_count.eq(count + window[i]) + count = next_count + self.comb += If(count >= 4, self.ready.eq(0)) + + class Multiplexer(Module, AutoCSR): def __init__(self, settings, @@ -112,8 +162,8 @@ class Multiplexer(Module, AutoCSR): with_bandwidth=False): assert(settings.phy.nphases == len(dfi.phases)) - # Forward Declares - activate_allowed = Signal(reset=1) + ras_allowed = Signal(reset=1) + cas_allowed = Signal(reset=1) # Command choosing requests = [bm.cmd for bm in bank_machines] @@ -122,7 +172,7 @@ class Multiplexer(Module, AutoCSR): if settings.phy.nphases == 1: self.comb += [ choose_cmd.want_cmds.eq(1), - choose_cmd.want_activates.eq(activate_allowed), + choose_cmd.want_activates.eq(ras_allowed), choose_req.want_cmds.eq(1) ] @@ -135,66 +185,32 @@ class Multiplexer(Module, AutoCSR): steerer = _Steerer(commands, dfi) self.submodules += steerer - # tRRD Command Timing - trrd = settings.timing.tRRD - trrd_allowed = Signal(reset=1) - is_act_cmd = Signal() - self.comb += is_act_cmd.eq(choose_cmd.cmd.ras & ~choose_cmd.cmd.cas & ~choose_cmd.cmd.we) - if trrd is not None: - trrd_count = Signal(max=trrd+1) - self.sync += \ - If(choose_cmd.cmd.ready & choose_cmd.cmd.valid & is_act_cmd, - trrd_count.eq(trrd-1) - ).Elif(~activate_allowed, - trrd_count.eq(trrd_count-1) - ) - self.comb += trrd_allowed.eq(trrd_count == 0) + # tRRD timing (Row to Row delay) + self.trrdcon = trrdcon = tXXDController(settings.timing.tRRD) + self.comb += trrdcon.valid.eq(choose_cmd.accept() & choose_cmd.activate()) - # tFAW Command Timing - tfaw = settings.timing.tFAW - tfaw_allowed = Signal(reset=1) - if tfaw is not None: - tfaw_count = Signal(max=tfaw) - tfaw_window = Signal(tfaw) - self.sync += tfaw_window.eq(Cat((is_act_cmd & choose_cmd.cmd.ready & choose_cmd.cmd.valid), tfaw_window)) - for i in range(tfaw): - next_tfaw_count = Signal(max=tfaw) - self.comb += next_tfaw_count.eq(tfaw_count + tfaw_window[i]) - tfaw_count = next_tfaw_count - self.comb += If(tfaw_count >=4, tfaw_allowed.eq(0)) + # tFAW timing (Four Activate Window) + self.tfawcon = tfawcon = tFAWController(settings.timing.tFAW) + self.comb += tfawcon.valid.eq(choose_cmd.accept() & choose_cmd.activate()) - self.comb += activate_allowed.eq(trrd_allowed & tfaw_allowed) - self.comb += [bm.activate_allowed.eq(activate_allowed) for bm in bank_machines] + # RAS control + self.comb += ras_allowed.eq(trrdcon.ready & tfawcon.ready) + self.comb += [bm.ras_allowed.eq(ras_allowed) for bm in bank_machines] - # CAS to CAS - cas = choose_req.cmd.valid & choose_req.cmd.ready & (choose_req.cmd.is_read | choose_req.cmd.is_write) - cas_allowed = Signal(reset=1) - tccd = settings.timing.tCCD - if tccd is not None: - cas_count = Signal(max=tccd+1) - self.sync += \ - If(cas, - cas_count.eq(tccd-1) - ).Elif(~cas_allowed, - cas_count.eq(cas_count-1) - ) - self.comb += cas_allowed.eq(cas_count == 0) + # tCCD timing (Column to Column delay) + self.tccdcon = tccdcon = tXXDController(settings.timing.tCCD) + self.comb += tccdcon.valid.eq(choose_cmd.accept() & (choose_cmd.write() | choose_cmd.read())) + + # CAS control + self.comb += cas_allowed.eq(tccdcon.ready) self.comb += [bm.cas_allowed.eq(cas_allowed) for bm in bank_machines] - # Write to Read - wtr_allowed = Signal(reset=1) - twtr = settings.timing.tWTR - if tccd is not None: - twtr += settings.timing.tCCD # tWTR begins after the transfer is complete, tCCD accounts for this - wtr_count = Signal(max=twtr+1) - self.sync += [ - If(choose_req.cmd.ready & choose_req.cmd.valid & choose_req.cmd.is_write, - wtr_count.eq(twtr-1) - ).Elif(wtr_count != 0, - wtr_count.eq(wtr_count-1) - ) - ] - self.comb += wtr_allowed.eq(wtr_count == 0) + # tWTR timing (Write to Read delay) + self.twtrcon = twtrcon = tXXDController( + settings.timing.tWTR + + # tCCD must be added since tWTR begins after the transfer is complete + settings.timing.tCCD if settings.timing.tCCD is not None else 0) + self.comb += twtrcon.valid.eq(choose_req.accept() & choose_req.write()) # Read/write turnaround read_available = Signal() @@ -265,8 +281,8 @@ class Multiplexer(Module, AutoCSR): fsm.act("READ", read_time_en.eq(1), choose_req.want_reads.eq(1), - choose_cmd.want_activates.eq(activate_allowed), - choose_cmd.cmd.ready.eq(~is_act_cmd | activate_allowed), + choose_cmd.want_activates.eq(ras_allowed), + choose_cmd.cmd.ready.eq(~choose_cmd.activate() | ras_allowed), choose_req.cmd.ready.eq(1), steerer_sel(steerer, "read"), If(write_available, @@ -282,8 +298,8 @@ class Multiplexer(Module, AutoCSR): fsm.act("WRITE", write_time_en.eq(1), choose_req.want_writes.eq(1), - choose_cmd.want_activates.eq(activate_allowed), - choose_cmd.cmd.ready.eq(~is_act_cmd | activate_allowed), + choose_cmd.want_activates.eq(ras_allowed), + choose_cmd.cmd.ready.eq(~choose_cmd.activate() | ras_allowed), choose_req.cmd.ready.eq(1), steerer_sel(steerer, "write"), If(read_available, @@ -303,7 +319,7 @@ class Multiplexer(Module, AutoCSR): ) ) fsm.act("WTR", - If(wtr_allowed, + If(twtrcon.ready, NextState("READ") ) )