From 6de7332eb270c96467baa38e2423362516b7b75e Mon Sep 17 00:00:00 2001 From: occheung Date: Thu, 10 Oct 2024 12:10:39 +0800 Subject: [PATCH 1/5] sdram: support burst memory access --- misoc/cores/minicon/core.py | 149 ++++++++++++++++++++++++++++++++---- 1 file changed, 133 insertions(+), 16 deletions(-) diff --git a/misoc/cores/minicon/core.py b/misoc/cores/minicon/core.py index a4105e2e..3970ec1e 100644 --- a/misoc/cores/minicon/core.py +++ b/misoc/cores/minicon/core.py @@ -123,21 +123,76 @@ def __init__(self, phy_settings, geom_settings, timing_settings, adr_width=30): self.submodules += write2precharge_timer self.comb += write2precharge_timer.wait.eq(~write) - refresh_timer = WaitTimer(timing_settings.tREFI) - self.submodules += refresh_timer - self.comb += refresh_timer.wait.eq(~refresh) + refi_cycles = Signal(bits_for(timing_settings.tREFI), reset=timing_settings.tREFI) + refresh_required = Signal() + refresh_required_burst = Signal() + + self.comb += [ + refresh_required.eq(refi_cycles == 0), + refresh_required_burst.eq( + refi_cycles[(geom_settings.colbits-address_align):] == 0), + ] + + self.sync += \ + If(refresh, + refi_cycles.eq(refi_cycles.reset), + ).Elif(~refresh_required, + refi_cycles.eq(refi_cycles - 1), + ) + + sdram_col = Signal.like(slicer.col(bus.adr)) + sdram_col_inc_next = Signal.like(slicer.col(bus.adr)) + sdram_swap_bank = Signal() + sdram_adr_inc = Signal() + + burst = Signal() + + self.comb += \ + Cat(sdram_col_inc_next, sdram_swap_bank).eq(sdram_col + (1 << address_align)) + + self.sync += \ + If(~burst, + sdram_col.eq(slicer.col(bus.adr)), + ).Else( + If(sdram_adr_inc, + sdram_col.eq(sdram_col_inc_next), + ) + ) + + rdvalid = dfi.phases[rdphase].rddata_valid + rdvalid_r = Signal.like(rdvalid) + self.sync += rdvalid_r.eq(rdvalid) + + read_ended = Signal() + self.comb += read_ended.eq(rdvalid_r & ~rdvalid) # Main FSM self.submodules.fsm = fsm = FSM() fsm.act("IDLE", - If(refresh_timer.done, + If(refresh_required, NextState("PRECHARGE-ALL") ).Elif(bus.stb & bus.cyc, If(bank_hit, If(bus.we, - NextState("WRITE") + If(bus.cti == 0b010, + If(refresh_required_burst, + NextState("PRECHARGE-ALL"), + ).Else( + NextState("BURST-WRITE"), + ), + ).Else( + NextState("WRITE") + ) ).Else( - NextState("READ") + If(bus.cti == 0b010, + If(refresh_required_burst, + NextState("PRECHARGE-ALL"), + ).Else( + NextState("BURST-READ"), + ), + ).Else( + NextState("READ") + ) ) ).Elif(~bank_idle, If(write2precharge_timer.done, @@ -157,10 +212,45 @@ def __init__(self, phy_settings, geom_settings, timing_settings, adr_width=30): NextState("WAIT-READ-DONE"), ) fsm.act("WAIT-READ-DONE", - If(dfi.phases[rdphase].rddata_valid, - bus.ack.eq(1), - NextState("IDLE") - ) + bus.ack.eq(dfi.phases[rdphase].rddata_valid), + If(read_ended, + NextState("IDLE"), + ), + ) + fsm.act("WAIT-BURST-READ-DONE", + bus.ack.eq(dfi.phases[rdphase].rddata_valid), + If(read_ended, + NextState("IDLE"), + ).Elif((bus.cti == 0b111) & bus.ack, + # Pending reads should be invalidated + NextState("INVALIDATE-READ"), + ), + ) + fsm.act("BURST-READ", + read.eq(1), + burst.eq(1), + 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), + bus.ack.eq(dfi.phases[rdphase].rddata_valid), + sdram_adr_inc.eq(1), + + # Stop the burst + If(bus.cti == 0b111, + # Wait out the overly anticipated reads + NextState("INVALIDATE-READ"), + ).Elif(sdram_swap_bank, + # Wait for the read to complete + NextState("WAIT-BURST-READ-DONE"), + ), + ) + fsm.act("INVALIDATE-READ", + # Invalidate responses by deasserting ACK + # The last legitimiate word was sent out by the end of BURST-READ + If(read_ended, + NextState("IDLE"), + ), ) fsm.act("WRITE", write.eq(1), @@ -170,6 +260,19 @@ def __init__(self, phy_settings, geom_settings, timing_settings, adr_width=30): dfi.phases[wrphase].wrdata_en.eq(1), NextState("WRITE-LATENCY") ) + fsm.act("BURST-WRITE", + write.eq(1), + burst.eq(1), + sdram_adr_inc.eq(1), + 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), + bus.ack.eq(1), + If((bus.cti == 0b111) | sdram_swap_bank, + NextState("BURST-WRITE-LATENCY"), + ), + ) fsm.act("WRITE-ACK", bus.ack.eq(1), NextState("IDLE") @@ -203,6 +306,7 @@ def __init__(self, phy_settings, geom_settings, timing_settings, adr_width=30): NextState("POST-REFRESH") ) fsm.delayed_enter("WRITE-LATENCY", "WRITE-ACK", phy_settings.write_latency-1) + fsm.delayed_enter("BURST-WRITE-LATENCY", "IDLE", phy_settings.write_latency) fsm.delayed_enter("TRP", "ACTIVATE", timing_settings.tRP-1) fsm.delayed_enter("TRCD", "IDLE", timing_settings.tRCD-1) fsm.delayed_enter("PRE-REFRESH", "REFRESH", timing_settings.tRP-1) @@ -221,15 +325,28 @@ def __init__(self, phy_settings, geom_settings, timing_settings, adr_width=30): If(precharge_all, phase.address.eq(2**10) ).Elif(activate, - phase.address.eq(slicer.row(bus.adr)) + phase.address.eq(slicer.row(bus.adr)) ).Elif(write | read, - phase.address.eq(slicer.col(bus.adr)) + phase.address.eq(sdram_col) ) ] - # DFI datapath + # DFI read datapath + self.comb += bus.dat_r.eq(Cat(phase.rddata for phase in dfi.phases)) + + # DFI write datapath + dfi_dat_w = Cat(phase.wrdata for phase in dfi.phases) + dfi_sel = Cat(phase.wrdata_mask for phase in dfi.phases) + + for _ in range(phy_settings.write_latency): + dfi_dat_w_r, dfi_dat_w = dfi_dat_w, Signal.like(dfi_dat_w) + dfi_sel_r, dfi_sel = dfi_sel, Signal.like(dfi_sel) + self.sync += [ + dfi_dat_w_r.eq(dfi_dat_w), + dfi_sel_r.eq(dfi_sel), + ] + self.comb += [ - 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), + dfi_dat_w.eq(bus.dat_w), + dfi_sel.eq(~bus.sel) ] From 87558a3297b17a6aa213a1f11f5e530381ba50a7 Mon Sep 17 00:00:00 2001 From: occheung Date: Thu, 10 Oct 2024 12:23:12 +0800 Subject: [PATCH 2/5] wishbone arbiter: holds grant while bursting --- misoc/interconnect/wishbone.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misoc/interconnect/wishbone.py b/misoc/interconnect/wishbone.py index aadcc8ce..5915dc4d 100644 --- a/misoc/interconnect/wishbone.py +++ b/misoc/interconnect/wishbone.py @@ -90,7 +90,7 @@ def __init__(self, masters, target): self.comb += dest.eq(source) # connect bus requests to round-robin selector - reqs = [m.cyc & ~m.ack for m in masters] + reqs = [m.cyc & ~(m.ack & (m.cti != 0b010)) for m in masters] self.comb += self.rr.request.eq(Cat(*reqs)) From f73f64aef8380ccbf369d04125e4d39595513950 Mon Sep 17 00:00:00 2001 From: occheung Date: Fri, 25 Oct 2024 15:19:32 +0800 Subject: [PATCH 3/5] sdram: allow refresh timer underflow --- misoc/cores/minicon/core.py | 37 +++++++++++++------------------------ 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/misoc/cores/minicon/core.py b/misoc/cores/minicon/core.py index 3970ec1e..14d3811d 100644 --- a/misoc/cores/minicon/core.py +++ b/misoc/cores/minicon/core.py @@ -123,22 +123,19 @@ def __init__(self, phy_settings, geom_settings, timing_settings, adr_width=30): self.submodules += write2precharge_timer self.comb += write2precharge_timer.wait.eq(~write) + pending_refresh = Signal() refi_cycles = Signal(bits_for(timing_settings.tREFI), reset=timing_settings.tREFI) - refresh_required = Signal() - refresh_required_burst = Signal() - - self.comb += [ - refresh_required.eq(refi_cycles == 0), - refresh_required_burst.eq( - refi_cycles[(geom_settings.colbits-address_align):] == 0), - ] - - self.sync += \ + self.sync += [ If(refresh, + pending_refresh.eq(0), + ), + If(refi_cycles == 0, + pending_refresh.eq(1), refi_cycles.eq(refi_cycles.reset), - ).Elif(~refresh_required, - refi_cycles.eq(refi_cycles - 1), - ) + ).Else( + refi_cycles.eq(refi_cycles - 1) + ), + ] sdram_col = Signal.like(slicer.col(bus.adr)) sdram_col_inc_next = Signal.like(slicer.col(bus.adr)) @@ -169,27 +166,19 @@ def __init__(self, phy_settings, geom_settings, timing_settings, adr_width=30): # Main FSM self.submodules.fsm = fsm = FSM() fsm.act("IDLE", - If(refresh_required, + If(pending_refresh, NextState("PRECHARGE-ALL") ).Elif(bus.stb & bus.cyc, If(bank_hit, If(bus.we, If(bus.cti == 0b010, - If(refresh_required_burst, - NextState("PRECHARGE-ALL"), - ).Else( - NextState("BURST-WRITE"), - ), + NextState("BURST-WRITE"), ).Else( NextState("WRITE") ) ).Else( If(bus.cti == 0b010, - If(refresh_required_burst, - NextState("PRECHARGE-ALL"), - ).Else( - NextState("BURST-READ"), - ), + NextState("BURST-READ"), ).Else( NextState("READ") ) From 4c8be416d77e1272b19506406b4a6d0993a0e1fa Mon Sep 17 00:00:00 2001 From: occheung Date: Fri, 25 Oct 2024 15:20:45 +0800 Subject: [PATCH 4/5] fix typo --- misoc/cores/minicon/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misoc/cores/minicon/core.py b/misoc/cores/minicon/core.py index 14d3811d..b20fd8f0 100644 --- a/misoc/cores/minicon/core.py +++ b/misoc/cores/minicon/core.py @@ -236,7 +236,7 @@ def __init__(self, phy_settings, geom_settings, timing_settings, adr_width=30): ) fsm.act("INVALIDATE-READ", # Invalidate responses by deasserting ACK - # The last legitimiate word was sent out by the end of BURST-READ + # The last legitimate word was sent out by the end of BURST-READ If(read_ended, NextState("IDLE"), ), From 36c00310884a5f2b03efa7870e6e3aa644b6a73b Mon Sep 17 00:00:00 2001 From: occheung Date: Fri, 25 Oct 2024 15:42:00 +0800 Subject: [PATCH 5/5] revert WAIT-READ-DONE exit condition Not touched by burst reads. The original method saves 1 cycle. --- misoc/cores/minicon/core.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/misoc/cores/minicon/core.py b/misoc/cores/minicon/core.py index b20fd8f0..b9a6a9e4 100644 --- a/misoc/cores/minicon/core.py +++ b/misoc/cores/minicon/core.py @@ -201,10 +201,10 @@ def __init__(self, phy_settings, geom_settings, timing_settings, adr_width=30): NextState("WAIT-READ-DONE"), ) fsm.act("WAIT-READ-DONE", - bus.ack.eq(dfi.phases[rdphase].rddata_valid), - If(read_ended, - NextState("IDLE"), - ), + If(dfi.phases[rdphase].rddata_valid, + bus.ack.eq(1), + NextState("IDLE") + ) ) fsm.act("WAIT-BURST-READ-DONE", bus.ack.eq(dfi.phases[rdphase].rddata_valid),