From 147466beec25fb972a21548692d36c416b92e9b8 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 14 Aug 2018 11:05:09 +0200 Subject: [PATCH 1/4] multiplexer: create timing controllers module and simplify --- litedram/core/bankmachine.py | 6 +- litedram/core/multiplexer.py | 142 +++++++++++++++++++---------------- 2 files changed, 82 insertions(+), 66 deletions(-) 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") ) ) From c1b1b07b3cfed307a9dade0d1f38d4f2ecfe693d Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 14 Aug 2018 15:13:10 +0200 Subject: [PATCH 2/4] core/multiplexer: synchronize ready on tXXDController and tFAWcontroller to improve timings --- litedram/core/multiplexer.py | 45 ++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/litedram/core/multiplexer.py b/litedram/core/multiplexer.py index 5e908a8..b019304 100644 --- a/litedram/core/multiplexer.py +++ b/litedram/core/multiplexer.py @@ -1,5 +1,5 @@ from functools import reduce -from operator import or_, and_ +from operator import add, or_, and_ from migen import * from migen.genlib.roundrobin import * @@ -118,38 +118,49 @@ class _Steerer(Module): class tXXDController(Module): def __init__(self, txxd): - self.valid = Signal() - self.ready = Signal(reset=1) + self.valid = valid = Signal() + self.ready = ready = Signal(reset=1) + ready.attr.add("no_retiming") # # # if txxd is not None: - count = Signal(max=txxd+1) + count = Signal(max=txxd + 1) self.sync += \ - If(self.valid, - count.eq(txxd-1) - ).Elif(~self.ready, - count.eq(count-1) + If(valid, + count.eq(txxd - 1), + If((txxd - 1) == 0, + ready.eq(1) + ).Else( + ready.eq(0) + ) + ).Elif(~ready, + count.eq(count - 1), + If(count == 1, ready.eq(1)) ) - self.comb += self.ready.eq(count == 0) class tFAWController(Module): def __init__(self, tfaw): - self.valid = Signal() - self.ready = Signal(reset=1) + self.valid = valid = Signal() + self.ready = ready = Signal(reset=1) + ready.attr.add("no_retiming") # # # 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)) + self.sync += window.eq(Cat(valid, window)) + self.comb += reduce(add, [window[i] for i in range(tfaw)]) + self.sync += \ + If(count < 4, + If(count == 3, + ready.eq(~valid) + ).Else( + ready.eq(1) + ) + ) class Multiplexer(Module, AutoCSR): From b2f1f2938457bdb8c9ecc128296170b262535674 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 14 Aug 2018 15:13:33 +0200 Subject: [PATCH 3/4] core/bankmachine: update comments --- litedram/core/bankmachine.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/litedram/core/bankmachine.py b/litedram/core/bankmachine.py index 5f56954..ef542ea 100644 --- a/litedram/core/bankmachine.py +++ b/litedram/core/bankmachine.py @@ -87,6 +87,7 @@ class BankMachine(Module): cmd.is_write)) # Control and command generation FSM + # Note: tRRD, tFAW, tCCD, tWTR timings are enforced by the multiplexer self.submodules.fsm = fsm = FSM() fsm.act("REGULAR", If(self.refresh_req, @@ -95,8 +96,6 @@ class BankMachine(Module): If(has_openrow, If(hit, If(cas_allowed, - # Note: write-to-read specification is enforced by - # multiplexer cmd.valid.eq(1), If(cmd_buffer.source.we, req.wdata_ready.eq(cmd.ready), From 6620a91a224b7a9fbc4cf4089c80be39763eb9b8 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 14 Aug 2018 15:30:24 +0200 Subject: [PATCH 4/4] core/refresher: synchronize valid --- litedram/core/multiplexer.py | 2 +- litedram/core/refresher.py | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/litedram/core/multiplexer.py b/litedram/core/multiplexer.py index b019304..70c3738 100644 --- a/litedram/core/multiplexer.py +++ b/litedram/core/multiplexer.py @@ -325,7 +325,7 @@ class Multiplexer(Module, AutoCSR): fsm.act("REFRESH", steerer.sel[0].eq(STEER_REFRESH), refresher.cmd.ready.eq(1), - If(refresher.cmd.last, + If(~refresher.cmd.valid, NextState("READ") ) ) diff --git a/litedram/core/refresher.py b/litedram/core/refresher.py index 904d671..be58b2c 100644 --- a/litedram/core/refresher.py +++ b/litedram/core/refresher.py @@ -45,14 +45,16 @@ class Refresher(Module): self.comb += self.timer.wait.eq(settings.with_refresh & ~self.timer.done) # Control FSM + cmd_valid = Signal() self.submodules.fsm = fsm = FSM() fsm.act("IDLE", If(self.timer.done, + cmd_valid.eq(1), NextState("WAIT_GRANT") ) ) fsm.act("WAIT_GRANT", - cmd.valid.eq(1), + cmd_valid.eq(1), If(cmd.ready, seq_start.eq(1), NextState("WAIT_SEQ") @@ -60,9 +62,8 @@ class Refresher(Module): ) fsm.act("WAIT_SEQ", If(seq_done, - cmd.last.eq(1), + cmd_valid.eq(0), NextState("IDLE") - ).Else( - cmd.valid.eq(1) ) ) + self.sync += cmd.valid.eq(cmd_valid)