From e446c063397079bbf558601d7bb2deddb501a620 Mon Sep 17 00:00:00 2001 From: Hans Baier Date: Wed, 31 May 2023 13:14:52 +0700 Subject: [PATCH] frontend/avalon: properly implement bursts (#340) frontend/avalon: properly implement bursts --- litedram/frontend/avalon.py | 193 +++++++++++++++++++++++------------- test/test_avalon.py | 87 +++++++++++++++- 2 files changed, 207 insertions(+), 73 deletions(-) diff --git a/litedram/frontend/avalon.py b/litedram/frontend/avalon.py index d50d26e..6878ffe 100644 --- a/litedram/frontend/avalon.py +++ b/litedram/frontend/avalon.py @@ -11,15 +11,14 @@ from math import log2 from migen import * from litex.soc.interconnect import stream - from litedram.common import LiteDRAMNativePort from litedram.frontend.adapter import LiteDRAMNativePortConverter -# LiteDRAMAvalongMM2Native -------------------------------------------------------------------------- +# LiteDRAMAvalonMM2Native -------------------------------------------------------------------------- class LiteDRAMAvalonMM2Native(Module): - def __init__(self, avalon, port, base_address=0x00000000, burst_increment=1): + def __init__(self, avalon, port, *, max_burst_length=16, base_address=0x00000000, burst_increment=1): avalon_data_width = len(avalon.writedata) port_data_width = 2**int(log2(len(port.wdata.data))) # Round to lowest power 2 ratio = avalon_data_width/port_data_width @@ -44,14 +43,25 @@ class LiteDRAMAvalonMM2Native(Module): offset = base_address >> log2_int(port.data_width//8) burstcounter = Signal(9) + start_burst = Signal() active_burst = Signal() - address = Signal(avalon_data_width) + address = Signal.like(port.cmd.addr) byteenable = Signal.like(avalon.byteenable) writedata = Signal(avalon_data_width) start_transaction = Signal() cmd_ready_seen = Signal() + cmd_ready_counter = Signal.like(burstcounter) - self.comb += active_burst.eq(2 <= burstcounter) + cmd_layout = [("address", len(address))] + wdata_layout = [ + ("data", avalon_data_width), + ("byteenable", len(avalon.byteenable)) + ] + + self.comb += [ + start_burst .eq(2 <= avalon.burstcount), + active_burst.eq(1 <= burstcounter) + ] self.sync += [ If(start_transaction, byteenable.eq(avalon.byteenable), @@ -59,42 +69,48 @@ class LiteDRAMAvalonMM2Native(Module): address.eq(avalon.address - offset)) ] - start_condition = start_transaction if downconvert else (start_transaction & port.cmd.ready) + start_condition = start_transaction if downconvert else (start_transaction & (start_burst | port.cmd.ready)) self.submodules.fsm = fsm = FSM(reset_state="START") fsm.act("START", avalon.waitrequest.eq(1), - port.cmd.addr.eq(avalon.address - offset), - port.cmd.we.eq(avalon.write), - port.cmd.valid .eq(avalon.read | avalon.write), + If (~start_burst, + port.cmd.addr.eq(avalon.address - offset), + port.cmd.we.eq(avalon.write), + port.cmd.valid.eq(avalon.read | avalon.write) + ), + start_transaction.eq(avalon.read | avalon.write), If(start_condition, - [] if downconvert else [avalon.waitrequest.eq(0)], + [] if downconvert else [If (~start_burst, avalon.waitrequest.eq(0))], If (avalon.write, - [ - port.wdata.data.eq(avalon.writedata), - port.wdata.valid.eq(1), - port.wdata.we.eq(avalon.byteenable), - ] if downconvert else [], - NextValue(writedata, avalon.writedata), - [] if downconvert else [NextValue(port.cmd.last, 0)], - NextState("WRITE_DATA") + If (start_burst, + NextState("BURST_WRITE") + ).Else( + [ + port.wdata.data.eq(avalon.writedata), + port.wdata.valid.eq(1), + port.wdata.we.eq(avalon.byteenable), + ] if downconvert else [], + NextValue(writedata, avalon.writedata), + port.cmd.last.eq(1), + NextState("SINGLE_WRITE") + ) ).Elif(avalon.read, - NextState("READ_DATA"))) + If (start_burst, + avalon.waitrequest.eq(0), + NextValue(cmd_ready_counter, avalon.burstcount), + NextState("BURST_READ") + ).Else( + port.cmd.last.eq(1), + NextState("SINGLE_READ") + ) ) + ) + ) - fsm.act("WRITE_CMD", - avalon.waitrequest.eq(1), - port.rdata.ready.eq(0), - - port.cmd.addr.eq(address), - port.cmd.we.eq(1), - port.cmd.valid.eq(1), - - If(port.cmd.ready, NextState("WRITE_DATA"))) - - fsm.act("WRITE_DATA", + fsm.act("SINGLE_WRITE", avalon.waitrequest.eq(1), port.rdata.ready.eq(0), @@ -115,36 +131,17 @@ class LiteDRAMAvalonMM2Native(Module): port.wdata.we.eq(byteenable), If(port.wdata.ready, - avalon.waitrequest.eq(active_burst if downconvert else ~active_burst), + avalon.waitrequest.eq(0 if downconvert else 1), NextValue(writedata, avalon.writedata), - If(~active_burst, - port.flush.eq(1), - NextValue(cmd_ready_seen, 0) if downconvert else NextValue(port.cmd.last, 1), - NextValue(burstcounter, 0), - NextValue(byteenable, 0), - # this marks the end of a write cycle - NextState("START") - ).Else( - NextValue(address, address + burst_increment), - NextValue(burstcounter, burstcounter - 1), - NextState("WRITE_CMD"))) - ) + port.flush.eq(1), + NextValue(cmd_ready_seen, 0) if downconvert else NextValue(port.cmd.last, 1), + NextValue(byteenable, 0), + NextState("START") + ) + ) - if downconvert: - self.comb += port.cmd.last.eq(~(fsm.ongoing("WRITE_CMD") | fsm.ongoing("WRITE_DATA") | avalon.write)) - - fsm.act("READ_CMD", - avalon.waitrequest.eq(1), - port.rdata.ready.eq(1), - port.cmd.addr.eq(address), - port.cmd.we.eq(0), - port.cmd.valid.eq(1), - - If(port.cmd.ready, - NextState("READ_DATA"))) - - fsm.act("READ_DATA", + fsm.act("SINGLE_READ", avalon.waitrequest.eq(1), port.rdata.ready.eq(1), @@ -153,9 +150,7 @@ class LiteDRAMAvalonMM2Native(Module): port.cmd.we.eq(0), port.cmd.valid.eq(1), - If(port.cmd.ready, - NextValue(cmd_ready_seen, 1) - ), + If(port.cmd.ready, NextValue(cmd_ready_seen, 1)), If(cmd_ready_seen, port.cmd.valid.eq(0), port.cmd.we.eq(0) @@ -166,17 +161,73 @@ class LiteDRAMAvalonMM2Native(Module): avalon.readdata.eq(port.rdata.data), avalon.readdatavalid.eq(1), - If(~active_burst, - [ - port.cmd.valid.eq(0), - avalon.waitrequest.eq(0), - NextValue(cmd_ready_seen, 0), - ] if downconvert else [], - NextValue(burstcounter, 0), - NextState("START") + [ + port.cmd.valid.eq(0), + avalon.waitrequest.eq(0), + NextValue(cmd_ready_seen, 0), + ] if downconvert else [], + NextState("START") + ) + ) - ).Else( - NextValue(address, address + burst_increment), + self.submodules.cmd_fifo = cmd_fifo = stream.SyncFIFO(cmd_layout, max_burst_length) + self.submodules.wdata_fifo = wdata_fifo = stream.SyncFIFO(wdata_layout, max_burst_length) + + fsm.act("BURST_WRITE", + # FIFO producer + avalon.waitrequest.eq(~(cmd_fifo.sink.ready & wdata_fifo.sink.ready)), + cmd_fifo.sink.payload.address.eq(address), + cmd_fifo.sink.valid.eq(avalon.write & ~avalon.waitrequest), + + wdata_fifo.sink.payload.data.eq(avalon.writedata), + wdata_fifo.sink.payload.byteenable.eq(avalon.byteenable), + wdata_fifo.sink.valid.eq(avalon.write & ~avalon.waitrequest), + + If (avalon.write & active_burst, + If (cmd_fifo.sink.ready & cmd_fifo.sink.valid, NextValue(burstcounter, burstcounter - 1), - NextState("READ_CMD"))) + NextValue(address, address + burst_increment)) + ).Else( + avalon.waitrequest.eq(1), + # wait for the FIFO to be empty + If ((cmd_fifo .level == 0) & + (wdata_fifo.level == 1) & port.wdata.ready, + NextState("START") ) + ), + + # FIFO consumer + port.cmd.addr.eq(cmd_fifo.source.payload.address), + port.cmd.we.eq(port.cmd.valid), + port.cmd.valid.eq(cmd_fifo.source.valid & (0 < wdata_fifo.level)), + cmd_fifo.source.ready.eq(port.cmd.ready), + + port.wdata.data.eq(wdata_fifo.source.payload.data), + port.wdata.we.eq(wdata_fifo.source.payload.byteenable), + port.wdata.valid.eq(wdata_fifo.source.valid), + wdata_fifo.source.ready.eq(port.wdata.ready), + ) + + fsm.act("BURST_READ", + avalon.waitrequest.eq(1), + port.cmd.addr.eq(address), + port.cmd.we.eq(0), + port.cmd.valid.eq(~cmd_ready_seen), + + port.rdata.ready.eq(1), + avalon.readdata.eq(port.rdata.data), + avalon.readdatavalid.eq(port.rdata.valid), + + If (port.cmd.ready, + If (cmd_ready_counter == 1, NextValue(cmd_ready_seen, 1)), + NextValue(cmd_ready_counter, cmd_ready_counter - 1), + NextValue(address, address + burst_increment) + ), + + If (port.rdata.valid, + If (burstcounter == 1, + NextValue(cmd_ready_seen, 0), + NextState("START")), + NextValue(burstcounter, burstcounter - 1) + ) + ) diff --git a/test/test_avalon.py b/test/test_avalon.py index 90cb816..8a1ba09 100644 --- a/test/test_avalon.py +++ b/test/test_avalon.py @@ -162,7 +162,7 @@ class TestAvalon(MemoryTestDataMixin, unittest.TestCase): avl = avalon.AvalonMMInterface(adr_width=30, data_width=32) port = LiteDRAMNativePort("both", address_width=30, data_width=32) - dut = DUT(port, avl, base_address=0x0, mem_expected=data) + dut = DUT(port, avl, base_address=0x0, mem_expected=[0x4567, 0x0123, 0xcdef, 0x89ab, 0xbeef, 0xdead, 0xee00, 0xc0ff, 0x3210, 0x7654]) generators = [ main_generator(dut), dut.mem.write_handler(dut.port), @@ -170,4 +170,87 @@ class TestAvalon(MemoryTestDataMixin, unittest.TestCase): ] run_simulation(dut, generators, vcd_name='sim.vcd') - self.assertEqual(dut.mem.mem, data) + self.assertEqual(dut.mem.mem, data + [0,0,0,0,0]) + + def test_avalon_burst_downconvert(self): + data = [0x01234567, 0x89abcdef, 0xdeadbeef, 0xc0ffee00, 0x76543210, 0xfedcba98] + + def main_generator(dut): + yield from dut.avalon.bus_write(0x0, data) + yield + self.assertEqual((yield from dut.avalon.bus_read(0x0000, burstcount=6)), 0x01234567) + self.assertEqual((yield dut.avalon.readdatavalid), 1) + self.assertEqual((yield from dut.avalon.continue_read_burst()), 0x89abcdef) + self.assertEqual((yield dut.avalon.readdatavalid), 1) + self.assertEqual((yield from dut.avalon.continue_read_burst()), 0xdeadbeef) + self.assertEqual((yield dut.avalon.readdatavalid), 1) + self.assertEqual((yield from dut.avalon.continue_read_burst()), 0xc0ffee00) + self.assertEqual((yield dut.avalon.readdatavalid), 1) + self.assertEqual((yield from dut.avalon.continue_read_burst()), 0x76543210) + self.assertEqual((yield dut.avalon.readdatavalid), 1) + self.assertEqual((yield from dut.avalon.continue_read_burst()), 0xfedcba98) + yield + yield + yield + yield + self.assertEqual((yield from dut.avalon.bus_read(0x0000)), 0x01234567) + self.assertEqual((yield from dut.avalon.bus_read(0x0001)), 0x89abcdef) + self.assertEqual((yield from dut.avalon.bus_read(0x0002)), 0xdeadbeef) + self.assertEqual((yield from dut.avalon.bus_read(0x0003)), 0xc0ffee00) + self.assertEqual((yield from dut.avalon.bus_read(0x0004)), 0x76543210) + yield + yield + + avl = avalon.AvalonMMInterface(adr_width=30, data_width=32) + port = LiteDRAMNativePort("both", address_width=32, data_width=16) + dut = DUT(port, avl, base_address=0x0, mem_expected=data + 6 * [0]) + generators = [ + main_generator(dut), + dut.mem.write_handler(dut.port), + dut.mem.read_handler(dut.port), + ] + + run_simulation(dut, generators, vcd_name="avalon_" + self._testMethodName + ".vcd") + self.assertEqual(dut.mem.mem, [0x4567, 0x0123, 0xcdef, 0x89ab, 0xbeef, 0xdead, 0xee00, 0xc0ff, 0x3210, 0x7654, 0xba98, 0xfedc]) + + def test_avalon_burst_upconvert(self): + data = [0x01234567, 0x89abcdef, 0xdeadbeef, 0xc0ffee00, 0x76543210, 0xfedcba98] + + def main_generator(dut): + yield from dut.avalon.bus_write(0x0, data) + yield + self.assertEqual((yield from dut.avalon.bus_read(0x0000, burstcount=6)), 0x01234567) + self.assertEqual((yield dut.avalon.readdatavalid), 1) + self.assertEqual((yield from dut.avalon.continue_read_burst()), 0x89abcdef) + self.assertEqual((yield dut.avalon.readdatavalid), 1) + self.assertEqual((yield from dut.avalon.continue_read_burst()), 0xdeadbeef) + self.assertEqual((yield dut.avalon.readdatavalid), 1) + self.assertEqual((yield from dut.avalon.continue_read_burst()), 0xc0ffee00) + self.assertEqual((yield dut.avalon.readdatavalid), 1) + self.assertEqual((yield from dut.avalon.continue_read_burst()), 0x76543210) + self.assertEqual((yield dut.avalon.readdatavalid), 1) + self.assertEqual((yield from dut.avalon.continue_read_burst()), 0xfedcba98) + yield + yield + yield + yield + self.assertEqual((yield from dut.avalon.bus_read(0x0000)), 0x01234567) + self.assertEqual((yield from dut.avalon.bus_read(0x0001)), 0x89abcdef) + self.assertEqual((yield from dut.avalon.bus_read(0x0002)), 0xdeadbeef) + self.assertEqual((yield from dut.avalon.bus_read(0x0003)), 0xc0ffee00) + self.assertEqual((yield from dut.avalon.bus_read(0x0004)), 0x76543210) + self.assertEqual((yield from dut.avalon.bus_read(0x0005)), 0xfedcba98) + yield + yield + + avl = avalon.AvalonMMInterface(adr_width=30, data_width=32) + port = LiteDRAMNativePort("both", address_width=30, data_width=64) + dut = DUT(port, avl, base_address=0x0, mem_expected=data) + generators = [ + main_generator(dut), + dut.mem.write_handler(dut.port), + dut.mem.read_handler(dut.port), + ] + + run_simulation(dut, generators, vcd_name="avalon_" + self._testMethodName + ".vcd") + self.assertEqual(dut.mem.mem, [0x89abcdef01234567, 0xc0ffee00deadbeef, 0xfedcba9876543210, 0, 0, 0])