Browse Source

DRAM access integration preparation

master
kaqu 3 months ago
parent
commit
ab001bdc6d
16 changed files with 840 additions and 44 deletions
  1. +1
    -1
      .gitignore
  2. +2
    -2
      .vscode/launch.json
  3. +1
    -1
      README.md
  4. +258
    -0
      litex/litedram/frontend/dma.py
  5. +233
    -0
      modules/dramtransfer.py
  6. +61
    -0
      modules/systime.py
  7. +14
    -22
      neopixelar.py
  8. +18
    -0
      software/include/dramtransfer.h
  9. +16
    -0
      software/include/systime.h
  10. +11
    -5
      software/linker/ram1.ld
  11. +6
    -5
      software/linker/ram2.ld
  12. +23
    -6
      software/ramcreate.sh
  13. +2
    -1
      software/source/README.md
  14. +144
    -0
      software/source/dramtransfer.c
  15. +3
    -1
      software/source/main.c
  16. +47
    -0
      software/source/systime.c

+ 1
- 1
.gitignore View File

@ -2,5 +2,5 @@ build/*
__pycache__/*
backup/*
software/build/*
hd_errors/*
helpers/__pycache__/*
modules/__pycache__/*

+ 2
- 2
.vscode/launch.json View File

@ -10,8 +10,8 @@
"request": "launch",
"program": "${file}",
"args": ["--build",
//"--load", // May be used separately ...
"--flash", // May be used separately ...
"--load", // May be used separately ...
//"--flash", // May be used separately ...
"--revision=7.0",
"--uart-name=crossover",
//"--with-ethernet", // Not to be used together w/ etherbone! Won't TFTP ...


+ 1
- 1
README.md View File

@ -59,7 +59,7 @@ After installation of the relevant toolchains:
5. Press reset on the board, the LED chain should become illuminated
6. To program this base logic permanently, the board has to be improved, add a capacitor to stabilize the voltage for FPGA
programming (see Wolfgang's documentation on https://git.hacknology.de/wolfgang/colorlight )
7. Now run the same sequence as mentioned above, this time replacing the --load option with the --flash option
7. Now run the same sequence as mentioned above, replacing the --load option with the --flash option
8. This time it will take even longer ...
9. And hopefully complete without errors. If successful, the basic ROM s/w is now in place & operating. In case of errors try again - rule out EMI disturbances if nec.
10. Create the actual documentation for html via: 'sphinx-build -b html build/documentation build/documentation/html'


+ 258
- 0
litex/litedram/frontend/dma.py View File

@ -0,0 +1,258 @@
#
# This file is part of LiteDRAM.
#
# Copyright (c) 2015 Sebastien Bourdeauducq <sb@m-labs.hk>
# Copyright (c) 2016-2019 Florent Kermarrec <florent@enjoy-digital.fr>
# Copyright (c) 2018 John Sully <john@csquare.ca>
# Copyright (c) 2016 Tim 'mithro' Ansell <mithro@mithis.com>
# SPDX-License-Identifier: BSD-2-Clause
"""Direct Memory Access (DMA) reader and writer modules."""
from migen import *
from litex.soc.interconnect.csr import *
from litex.soc.interconnect import stream
from litedram.common import LiteDRAMNativePort
from litedram.frontend.axi import LiteDRAMAXIPort
# LiteDRAMDMAReader --------------------------------------------------------------------------------
class LiteDRAMDMAReader(Module, AutoCSR):
"""Read data from DRAM memory.
For every address written to the sink, one DRAM word will be produced on
the source.
Parameters
----------
port : port
Port on the DRAM memory controller to read from (Native or AXI).
fifo_depth : int
How many request results the output FIFO can contain (and thus how many
read requests can be outstanding at once).
fifo_buffered : bool
Implement FIFO in Block Ram.
Attributes
----------
sink : Record("address")
Sink for DRAM addresses to be read.
source : Record("data")
Source for DRAM word results from reading.
rsv_level: Signal()
FIFO reservation level counter
"""
def __init__(self, port, fifo_depth=16, fifo_buffered=False):
assert isinstance(port, (LiteDRAMNativePort, LiteDRAMAXIPort))
self.port = port
self.sink = sink = stream.Endpoint([("address", port.address_width)])
self.source = source = stream.Endpoint([("data", port.data_width)])
# # #
# Native / AXI selection
is_native = isinstance(port, LiteDRAMNativePort)
is_axi = isinstance(port, LiteDRAMAXIPort)
if is_native:
(cmd, rdata) = port.cmd, port.rdata
elif is_axi:
(cmd, rdata) = port.ar, port.r
else:
raise NotImplementedError
# Request issuance -------------------------------------------------------------------------
request_enable = Signal()
request_issued = Signal()
if is_native:
self.comb += cmd.we.eq(0)
self.comb += [
cmd.addr.eq(sink.address),
cmd.valid.eq(sink.valid & request_enable),
sink.ready.eq(cmd.ready & request_enable),
request_issued.eq(cmd.valid & cmd.ready)
]
# FIFO reservation level counter -----------------------------------------------------------
# incremented when data is planned to be queued
# decremented when data is dequeued
data_dequeued = Signal()
self.rsv_level = rsv_level = Signal(max=fifo_depth+1)
self.sync += [
If(request_issued,
If(~data_dequeued, rsv_level.eq(self.rsv_level + 1))
).Elif(data_dequeued,
rsv_level.eq(rsv_level - 1)
)
]
self.comb += request_enable.eq(rsv_level != fifo_depth)
# FIFO -------------------------------------------------------------------------------------
fifo = stream.SyncFIFO([("data", port.data_width)], fifo_depth, fifo_buffered)
self.submodules += fifo
self.comb += [
rdata.connect(fifo.sink, omit={"id", "resp"}),
fifo.source.connect(source),
data_dequeued.eq(source.valid & source.ready)
]
def add_csr(self):
self._base = CSRStorage(32)
self._length = CSRStorage(32)
self._start = CSR()
self._done = CSRStatus()
self._loop = CSRStorage()
self._localstart = Signal() # 29.12.20/KQ local starter
# # #
shift = log2_int(self.port.data_width//8)
base = Signal(self.port.address_width)
offset = Signal(self.port.address_width)
length = Signal(self.port.address_width)
self.comb += [
base.eq(self._base.storage[shift:]),
length.eq(self._length.storage[shift:]),
]
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
fsm.act("IDLE",
self._done.status.eq(1),
If(self._start.re | self._localstart, # 29.12.20/KQ local starter (#If(self._start.re,)
NextValue(offset, 0),
NextState("RUN"),
)
)
fsm.act("RUN",
self.sink.valid.eq(1),
self.sink.address.eq(base + offset),
If(self.sink.ready,
NextValue(offset, offset + 1),
If(offset == (length - 1),
If(self._loop.storage,
NextValue(offset, 0)
).Else(
NextState("IDLE")
)
)
)
)
# LiteDRAMDMAWriter --------------------------------------------------------------------------------
class LiteDRAMDMAWriter(Module, AutoCSR):
"""Write data to DRAM memory.
Parameters
----------
port : port
Port on the DRAM memory controller to write to (Native or AXI).
fifo_depth : int
How many requests the input FIFO can contain (and thus how many write
requests can be outstanding at once).
fifo_buffered : bool
Implement FIFO in Block Ram.
Attributes
----------
sink : Record("address", "data")
Sink for DRAM addresses and DRAM data word to be written too.
"""
def __init__(self, port, fifo_depth=16, fifo_buffered=False):
assert isinstance(port, (LiteDRAMNativePort, LiteDRAMAXIPort))
self.port = port
self.sink = sink = stream.Endpoint([("address", port.address_width),
("data", port.data_width)])
# # #
# Native / AXI selection -------------------------------------------------------------------
is_native = isinstance(port, LiteDRAMNativePort)
is_axi = isinstance(port, LiteDRAMAXIPort)
if is_native:
(cmd, wdata) = port.cmd, port.wdata
elif is_axi:
(cmd, wdata) = port.aw, port.w
else:
raise NotImplementedError
# FIFO -------------------------------------------------------------------------------------
fifo = stream.SyncFIFO([("data", port.data_width)], fifo_depth, fifo_buffered)
self.submodules += fifo
if is_native:
self.comb += cmd.we.eq(1)
self.comb += [
cmd.addr.eq(sink.address),
cmd.valid.eq(fifo.sink.ready & sink.valid),
sink.ready.eq(fifo.sink.ready & cmd.ready),
fifo.sink.valid.eq(sink.valid & cmd.ready),
fifo.sink.data.eq(sink.data)
]
if is_native:
self.comb += wdata.we.eq(2**(port.data_width//8)-1)
if is_axi:
self.comb += wdata.strb.eq(2**(port.data_width//8)-1)
self.comb += [
wdata.valid.eq(fifo.source.valid),
fifo.source.ready.eq(wdata.ready),
wdata.data.eq(fifo.source.data)
]
def add_csr(self):
self._sink = self.sink
self.sink = stream.Endpoint([("data", self.port.data_width)])
self._base = CSRStorage(32)
self._length = CSRStorage(32)
self._start = CSR()
self._done = CSRStatus()
self._loop = CSRStorage()
# # #
shift = log2_int(self.port.data_width//8)
base = Signal(self.port.address_width)
offset = Signal(self.port.address_width)
length = Signal(self.port.address_width)
self.comb += [
base.eq(self._base.storage[shift:]),
length.eq(self._length.storage[shift:]),
]
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
fsm.act("IDLE",
self._done.status.eq(1),
If(self._start.re,
NextValue(offset, 0),
NextState("RUN"),
)
)
fsm.act("RUN",
self._sink.valid.eq(self.sink.valid),
self._sink.data.eq(self.sink.data),
self._sink.address.eq(base + offset),
self.sink.ready.eq(self._sink.ready),
If(self.sink.valid & self.sink.ready,
NextValue(offset, offset + 1),
If(offset == (length - 1),
If(self._loop.storage,
NextValue(offset, 0)
).Else(
NextState("IDLE")
)
)
)
)

+ 233
- 0
modules/dramtransfer.py View File

@ -0,0 +1,233 @@
#!/usr/bin/env python3
#
# dramtransfer.py
#
# DRAM access
#
# History:
# --------
# 21.12.20/KQ Initial test
# 30.12.20/KQ Working (renamed) version
#
from migen import *
from migen.fhdl.specials import Memory
from litex.soc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage, CSRField, CSRAccess
from litex.soc.integration.doc import AutoDoc, ModuleDoc
from litex.soc.interconnect.csr import *
#from litedram.common import LiteDRAMNativePort
from litedram.core.crossbar import LiteDRAMCrossbar
from litedram.frontend import dma
class DRAMTransfer(Module, AutoCSR, AutoDoc, ModuleDoc):
"""
DRAMTransfer class provides the protocol logic to access DRAM values via LiteDRAM
Usage:
######
#. Load ``b32Address`` with base address of range to read from (DRAM: >= 0x40000000)
#. Indicate length of range to read by setting up ``b8Len`` (not used currently,
allways ``maxwords`` * 4 bytes will be loaded (words à 32-bit).
#. Finally, enable processing by setting ``bEnable`` to true (1).
#. Once ``bValid`` becomes true (1), FPGA local memory is loaded, deactivate ``bEnable``
#. To retrieve, load ``b9Offset`` with offset (from base adress) to read from (0 .. 511),
``b32Data`` will contain the 32-bit value (from local FPGA memory @offset)
Inputs:
#######
:b32Address: Base DRAM Address to load from
:b8Len: Length (0..255) of range to read from (i.e. # of bytes - not used currently - allways reading 32 bytes!)
:bEnable: To enable running (after initialization)
:b9Offset: Offset (0..511) into local FPGA memory to read from
Output:
#######
:bValid: Indicate validity of local FPGA memory, i.e. 'loaded'
:b32Data: Local FPGA memory at b9Offset
"""
def __init__(self, maxwords=8, dma_reader=None, sync_fifo=None):
# Inputs
self.b32Address = CSRStorage(32, reset_less=True,
fields=[CSRField("Address", size=32, description="*Field*: 32-Bit value")],
description="""
Base DRAM address, to load from
""")
self.b8Len = CSRStorage(8, reset_less=True,
fields=[CSRField("Len", size=8, description="*Field*: 8-Bit value (0..max)")],
description="""
Length of range to load, currently not used (allways 4 assumed)
""")
self.bEnable = CSRStorage(1, reset_less=True,
fields=[CSRField("Enable", size=1, description="*Field*: bit", values=[
("0", "DISABLED", "Loading enabled"),
("1", "ENABLED", "Loading disabled"),
])
],
description="""
Enable/disabling DRAM access
""")
self.b9Offset = CSRStorage(9, reset_less=True,
fields=[CSRField("Offset", size=12, description="*Field*: 9-Bit value (0..511)")],
description="""
Offset added to base address.
""")
# Outputs
self.bValid = CSRStorage(1, reset_less=True,
fields=[CSRField("Valid", size=1, description="*Field*: bit", values=[
("0", "INVALID", "Data output not available"),
("1", "VALID", "Data valid"),
])
],
description="""
Data valid indication
""")
self.b32Data = CSRStorage(32, reset_less=True,
fields=[CSRField("Data", size=32, description="*Field*: 32-Bit value")],
description="""
Actual value read
""")
self.b32RCount = CSRStorage(32, reset_less=True,
fields=[CSRField("RCount", size=32, description="*Field*: 32-Bit value")],
description="""
No. of FIFO entries read so far (only for testing purposes)
""")
# Local 'wire' data
self.b32MemPt = Signal(32) # WRITE: Local FPGA memory offset pointer
self.b2Address = Signal(3) # READ: Adress conversion helper
self.bData = Signal(32) # READ: Helper output data
storage = Memory(32, maxwords) # Local FPGA memory
self.specials += storage
# ---------------------- Local (FPGA) memory DMA filling from DRAM ---------------------------------------------
if (dma_reader != None) and (sync_fifo != None):
# FPGA local memory write port (driven by FSM, s.b.)
wrport = storage.get_port(write_capable=True)
self.specials += wrport
fsm = FSM(reset_state="IDLE") # FSM starts idling ...
self.submodules += fsm
# Prepare DRAM address to load from ...
self.sync += dma_reader._base.storage.eq(self.b32Address.storage) # Base DRAM adress to load from
self.sync += dma_reader._length.storage.eq(maxwords*4) # Fixed byte length of chunk to load from DRAM
fsm.act("IDLE",
If(self.bEnable.storage & ~self.bValid.storage, # Enabled & not busy already
NextValue(dma_reader._localstart, 1), # FPGA DMA self starter (special in litedram/frontend/dma.py)
NextValue(self.b32MemPt, 0), # Reset queue offset (counter)
NextState("DMAWAIT")
).Elif(~self.bEnable.storage, # Reset from external world first
NextValue(self.bValid.storage, 0) # to reset ... before possible re-enable
)
)
fsm.act("DMAWAIT",
NextValue(dma_reader._localstart, 0), # Reset FPGA DMA self starter
NextValue(self.bValid.storage, dma_reader._done.status), # Indicate DMA transfer finished to external ...
If(dma_reader._done.status, # Wait 'til DMA transfer finishes ...
NextState("FIFOREAD")
)
)
fsm.act("FIFOREAD",
If(sync_fifo.source.valid, # fifo.readable, # Data in queue waiting?
NextValue(self.b32RCount.storage, self.b32RCount.storage + 1), # Increment
If(self.b32MemPt < maxwords, # Legal address?
NextValue(wrport.adr, self.b32MemPt), # Local offset into memory
NextValue(wrport.dat_w, sync_fifo.source.payload.data), # Store current value -> memory
NextValue(wrport.we, 1) # Write enable
),
NextValue(sync_fifo.source.ready, 1), # fifo.re, 1), # ACK readable, request next FIFO entry
NextState("TESTFIFO")
)
)
fsm.act("TESTFIFO",
NextValue(wrport.we, 0), # Stop transfer to memory
If(self.b32MemPt >= (maxwords-1),
NextValue(self.b32MemPt, 0), # Reset counter
NextValue(self.bValid.storage, 1), # Indicate validity of results
NextState("IDLE")
).Else(
NextValue(self.b32MemPt, self.b32MemPt + 1), # Increment
NextState("FIFOREAD")
),
NextValue(sync_fifo.source.ready, 0) #fifo.re, 0), # Reset ACK
)
# --------------------------- Local (FPGA) memory retrieval access -----------------------------------------------
# FPGA local memory read port
rdport = storage.get_port()
self.specials += rdport
self.comb += [ # Read from (FPGA local) memory
self.b2Address.eq(self.b9Offset.storage[0:2]), # Filter bits 0..1 (range 0-3)
If(self.b9Offset.storage < maxwords,
#rdport.adr.eq(self.b9Offset.storage), # w/ translation!
If(self.b2Address == 0,
rdport.adr.eq(self.b9Offset.storage | 3) # 0->3
).Elif(self.b2Address == 1,
rdport.adr.eq((self.b9Offset.storage & 0x1FC) | 2) # 1->2
).Elif(self.b2Address == 2,
rdport.adr.eq((self.b9Offset.storage & 0x1FC) | 1) # 2->1
).Elif(self.b2Address == 3,
rdport.adr.eq(self.b9Offset.storage & 0x1FC) # 3->0
),
self.bData.eq(rdport.dat_r) # Assign to external var. ...
),
]
self.sync += self.b32Data.storage.eq(self.bData) # Assign to external var. ...
def DRAMTransfer_testbench(DRAMTransfer):
print("----- DRAM testbench -----")
yield DRAMTransfer.bEnable.storage.eq(False)
yield
yield DRAMTransfer.b32Address.storage.eq(0x40190000)
yield
iOffset = 0
yield DRAMTransfer.b9Offset.storage.eq(iOffset)
yield
yield DRAMTransfer.b8Len.storage.eq(4)
yield
yield DRAMTransfer.bEnable.storage.eq(True)
yield
#
for i in range(32): # Send the whole data & restart ...
print(i, ": ", sep="", end="")
bValid = (yield DRAMTransfer.bValid.storage)
if bValid == True:
print(hex((yield DRAMTransfer.b32Data.storage))) # Actual pin to move
yield DRAMTransfer.bEnable.storage.eq(False) # Reset request
else:
if (yield DRAMTransfer.bEnable.storage) == False:
print("New request")
iOffset = iOffset + 4
yield DRAMTransfer.b9Offset.storage.eq(iOffset)
yield DRAMTransfer.bEnable.storage.eq(True)
else:
print("-- invalid --")
yield
if __name__ == "__main__":
DRAMTransfer = DRAMTransfer(maxwords=8, sync_fifo=None)
run_simulation(DRAMTransfer, DRAMTransfer_testbench(DRAMTransfer), vcd_name="dramtransfer.vcd", clocks={"sys":16})

+ 61
- 0
modules/systime.py View File

@ -0,0 +1,61 @@
#!/usr/bin/env python3
#
# systime.py
#
# Clock (simulation)
#
# History:
# --------
# 31.12.20/KQ Initial version
#
from migen import *
from migen.fhdl.specials import Memory
from litex.soc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage, CSRField, CSRAccess
from litex.soc.integration.doc import AutoDoc, ModuleDoc
class SysTime(Module, AutoCSR, AutoDoc, ModuleDoc):
"""
SysTime class provides the missing system time
Usage:
######
#. Adjust clock by providing current Time to ``b32CurrentMSeconds``
#. Current Time may be read via ``b32CurrentMSeconds``
Inputs:
#######
:b32CurrentMSeconds: Current milliseconds within day to set
Output:
#######
:b32CurrentMSeconds: Current milliseconds within day since 00:00:00
"""
def __init__(self, comparecount=0x0000EA90):
# Input/Output
self.b32CurrentMSeconds = CSRStorage(32, reset_less=True,
fields=[CSRField("CurrentMSeconds", size=32, description="*Field*: 32-Bit value")],
description="""
Current milliseconds within day since 00:00:00.000
""")
# Local (internal)
self.b32Counter = Signal(32) # System frequency runner
# Simple time count function
self.sync += [
self.b32Counter.eq(self.b32Counter + 1), # Run system frequency counter
If(self.b32Counter == comparecount, # Approx. 1ms
self.b32Counter.eq(0),
self.b32CurrentMSeconds.storage.eq(self.b32CurrentMSeconds.storage + 1),
If(self.b32CurrentMSeconds.storage > 86399999, # Daily max. reached?
self.b32CurrentMSeconds.storage.eq(0) # Then: reset
)
)
]

+ 14
- 22
neopixelar.py View File

@ -18,6 +18,7 @@
# 19.09.20/KQ Project renamed 'NeoPixelar' (swedish plural for NeoPixel ...)
# 20.11.20/KQ Input provided: U28 -> J1 (1, 2, 3, [4=GND], 5, 6, 7), J2 (1, 2, [4=GND])
# (U15 -> J7 ([4=GND], 5, 7), J8 (1, 2, 3, [4=GND], 5, 6, 7))
# 02.01.21/KQ DMA DRAM access integration started
#
# Build/Use ----------------------------------------------------------------------------------------
# - 'python3 neopixelar.py --build --revision=7.0 --uart-name=crossover --with-etherbone --ip-address=192.168.1.20 --csr-csv=build/csr.csv'
@ -65,6 +66,10 @@ from litex.build.generic_platform import *
import litex.soc.doc as lxsocdoc
# KQ's helper modules ...
from modules.systime import SysTime
from modules.dramtransfer import DRAMTransfer
from neopixelengine import NeoPixelEngine
from helpers.prepare_firmware import copyjob
@ -335,8 +340,7 @@ class BaseSoC(SoCCore):
# Add platform specific I/O
platform.add_extension(_gpios) # General LED outputs
# OR values for LED signalling test
# For U28
# For U28 3.3V(!) inputs: (also see separate picture)
self.J1_1 = J1_1 = platform.request("gpio", 0) # Pins for logical evaluation
self.J1_2 = J1_2 = platform.request("gpio", 1)
self.J1_3 = J1_3 = platform.request("gpio", 2)
@ -346,23 +350,7 @@ class BaseSoC(SoCCore):
self.J2_1 = J2_1 = platform.request("gpio", 14)
self.J2_2 = J2_2 = platform.request("gpio", 15)
'''
J1_1 = Signal(1)
self.comb += J1_1.eq(platform.request("gpio", 0)) # Input data pins
J1_2 = Signal(1)
self.comb += J1_2.eq(platform.request("gpio", 1))
J1_3 = Signal(1)
self.comb += J1_3.eq(platform.request("gpio", 2))
J1_5 = Signal(1)
self.comb += J1_5.eq(platform.request("gpio", 3))
J1_6 = Signal(1)
self.comb += J1_6.eq(platform.request("gpio", 4))
J1_7 = Signal(1)
self.comb += J1_7.eq(platform.request("gpio", 5))
J2_1 = Signal(1)
self.comb += J2_1.eq(platform.request("gpio", 14))
J2_2 = Signal(1)
self.comb += J2_2.eq(platform.request("gpio", 15))
# For U15
# For U15 (not used currently)
J7_5 = Signal(1)
self.comb += J7_5.eq(platform.request("gpio", 87))
J7_7 = Signal(1)
@ -380,10 +368,10 @@ class BaseSoC(SoCCore):
J8_7 = Signal(1)
self.comb += J8_7.eq(platform.request("gpio", 103))
'''
# OR it all together
# OR it all together: Every input lights LED ...
self.led_logic1 = led_logic1 = Signal(1)
self.comb += led_logic1.eq(J1_1 | J1_2 | J1_3 | J1_5 | J1_6 | J1_7 | J2_1 | J2_2)
#led_logic2 = Signal(1)
#led_logic2 = Signal(1) # Not used currently
#self.comb += led_logic2.eq(J8_1 | J8_2 | J8_3 | J8_5 | J8_6 | J8_7 | J7_5 | J7_7)
# Base counter (used for clocking)
@ -393,8 +381,12 @@ class BaseSoC(SoCCore):
# USERLED blink (on-board LED)
# only w/ uart-name=crossover option:
if kwargs["uart_name"] not in ["serial", "bridge"]:
self.comb += platform.request("user_led_n").eq(~(counter[23] | led_logic1)) #| led_logic2) # ~2Hz
self.comb += platform.request("user_led_n").eq(~(counter[23] | led_logic1)) #| led_logic2) # ~2Hz | inputs
# System time (count)
self.submodules.systime = systime = SysTime(comparecount=0x0000EA90)
self.add_csr("systime")
# Adjust no. for your actual project ...
max_TABLES = 3 # 1..16
max_LEDS_per_chain = 27 # 1..256


+ 18
- 0
software/include/dramtransfer.h View File

@ -0,0 +1,18 @@
#ifndef DRAMTRANSFER_H
#define DRAMTRANSFER_H
//
// dramtransfer.h
//
// History:
// --------
// 29.12.20/KQ Initial version
//
#define FIFOSIZE 256 //128, 8
#define TIMEOUT 100
extern int load_FPGA(uint32_t iBaseAddress, int8_t bLen);
extern int32_t retrieve_FPGA(int iOffset);
extern void dramtest(void);
#endif

+ 16
- 0
software/include/systime.h View File

@ -0,0 +1,16 @@
#ifndef SYSTIME_H
#define SYSTIME_H
//
// systime.h
//
// History:
// --------
// 31.12.20/KQ Initial version
//
extern uint32_t systime(uint32_t setval);
extern void setsystime(int hh, int mm, int ss);
extern void getsystime(int *hh, int *mm, int *ss);
#endif

+ 11
- 5
software/linker/ram1.ld View File

@ -1,9 +1,12 @@
INCLUDE linker/output_format.ld
/* ENTRY(_start) */
ENTRY(illumination)
ENTRY(_start)
INCLUDE ../build/colorlight_5a_75b/software/include/generated/regions.ld
MEMORY {
main_sram : ORIGIN = 0x403FC000, LENGTH = 0x00002000
}
SECTIONS
{
.text :
@ -55,7 +58,8 @@ SECTIONS
FILL(0);
. = ALIGN(8);
_edata = .;
} > sram AT > main_ram
/* sram -> main_sram (DRAM) */
} > main_sram AT > main_ram
.bss :
{
@ -70,7 +74,8 @@ SECTIONS
. = ALIGN(8);
_ebss = .;
_end = .;
} > sram AT > main_ram
/* sram -> main_sram (DRAM) */
} > main_sram AT > main_ram
/DISCARD/ :
{
@ -79,7 +84,8 @@ SECTIONS
}
}
PROVIDE(_fstack = ORIGIN(sram) + LENGTH(sram) - 8);
/* STACK: sram -> main_sram (DRAM) */
PROVIDE(_fstack = ORIGIN(main_sram) + LENGTH(main_sram) - 8);
PROVIDE(_fdata_rom = LOADADDR(.data));
PROVIDE(_edata_rom = LOADADDR(.data) + SIZEOF(.data));

+ 6
- 5
software/linker/ram2.ld View File

@ -1,11 +1,11 @@
INCLUDE linker/output_format.ld
/* ENTRY(_start) */
ENTRY(illumination)
ENTRY(_start)
INCLUDE ../build/colorlight_5a_75b/software/include/generated/regions.ld
MEMORY {
main_ram2 : ORIGIN = 0x40200000, LENGTH = 0x00200000
main_sram : ORIGIN = 0x403FC000, LENGTH = 0x00002000
}
SECTIONS
@ -59,7 +59,7 @@ SECTIONS
FILL(0);
. = ALIGN(8);
_edata = .;
} > sram AT > main_ram2
} > main_sram AT > main_ram2
.bss :
{
@ -74,7 +74,7 @@ SECTIONS
. = ALIGN(8);
_ebss = .;
_end = .;
} > sram AT > main_ram2
} > main_sram AT > main_ram2
/DISCARD/ :
{
@ -83,7 +83,8 @@ SECTIONS
}
}
PROVIDE(_fstack = ORIGIN(sram) + LENGTH(sram) - 8);
/* STACK: sram -> main_sram (DRAM) */
PROVIDE(_fstack = ORIGIN(main_sram) + LENGTH(main_sram) - 8);
PROVIDE(_fdata_rom = LOADADDR(.data));
PROVIDE(_edata_rom = LOADADDR(.data) + SIZEOF(.data));

+ 23
- 6
software/ramcreate.sh View File

@ -1,12 +1,13 @@
#!/bin/bash
#
# ramcreate.sh
# A rather obbscure RAM s/w builder (needs transformation to actual makefile setup ;)
# A rather obscure RAM s/w builder (needs transformation to actual makefile setup ;)
#
# History:
# --------
# 10.10.20/KQ Initial (running) version
#
PROJECTNAME="neopixelar" # Adjust w/ local path
# Test for missing ('zero length') basefilenames
if [ -z $1 ] || [ -z $2 ] || [ -z $3 ]; then
@ -14,6 +15,15 @@ if [ -z $1 ] || [ -z $2 ] || [ -z $3 ]; then
echo "example: ./ramcreate.sh main illumination 1 (for RAM bank #1)"
exit
fi
echo "Creating $PROJECTNAME application ..."
# Additional user libs, not project specific
LIBSYSTIME="systime"
LIBPRINTF="my_vsnprintf"
# Additional user libs, not project specific
LIBSYSTIME="systime"
LIBPRINTF="my_vsnprintf"
# Adjust these paths according to your local installation !!!
MY_LOCAL_LITEX_PATH=$HOME"/fpga/litex"
@ -33,7 +43,7 @@ CPU_DIRECTORY="$MY_LOCAL_LITEX_PATH/litex/litex/soc/cores/cpu/vexriscv"
COMPILER_RT_DIRECTORY="$MY_LOCAL_LITEX_PATH/pythondata-software-compiler_rt/pythondata_software_compiler_rt/data"
SOC_DIRECTORY="$MY_LOCAL_LITEX_PATH/litex/litex/soc"
#export BUILDINC_DIRECTORY
BUILDINC_DIRECTORY="/mnt/a30054ad-3fe6-444a-8d93-16df937e448e/projects/fpga/neopixelar/build/colorlight_5a_75b/software/include"
BUILDINC_DIRECTORY="/mnt/a30054ad-3fe6-444a-8d93-16df937e448e/projects/fpga/$PROJECTNAME/build/colorlight_5a_75b/software/include"
LIBCOMPILER_RT_DIRECTORY="$MY_LOCAL_LITEX_PATH/litex/litex/soc/software/libcompiler_rt"
LIBBASE_DIRECTORY="$MY_LOCAL_LITEX_PATH/litex/litex/soc/software/libbase"
LIBLITEDRAM_DIRECTORY="$MY_LOCAL_LITEX_PATH/litex/litex/soc/software/liblitedram"
@ -42,7 +52,7 @@ LIBLITESPI_DIRECTORY="$MY_LOCAL_LITEX_PATH/litex/litex/soc/software/liblitespi"
LIBLITESDCARD_DIRECTORY="$MY_LOCAL_LITEX_PATH/litex/litex/soc/software/liblitesdcard"
BIOS_DIRECTORY="$MY_LOCAL_LITEX_PATH/litex/litex/soc/software/bios"
BIOSPATH="/mnt/a30054ad-3fe6-444a-8d93-16df937e448e/projects/fpga/neopixelar/build/colorlight_5a_75b/software/bios"
BIOSPATH="/mnt/a30054ad-3fe6-444a-8d93-16df937e448e/projects/fpga/$PROJECTNAME/build/colorlight_5a_75b/software/bios"
OBJECTS="$BIOSPATH/isr.o $BIOSPATH/boot-helper.o $BIOSPATH/boot.o $BIOSPATH/helpers.o $BIOSPATH/cmd_bios.o $BIOSPATH/cmd_mem.o $BIOSPATH/cmd_boot.o $BIOSPATH/cmd_i2c.o $BIOSPATH/cmd_spiflash.o $BIOSPATH/cmd_litedram.o $BIOSPATH/cmd_liteeth.o $BIOSPATH/cmd_litesdcard.o $BIOSPATH/complete.o $BIOSPATH/readline.o"
LXP="../build/colorlight_5a_75b/software"
@ -69,15 +79,21 @@ then
echo "*** gcc for $1.c failed!"
exit $?
fi
$MY_LOCAL_LITEX_PATH/riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14/bin/riscv64-unknown-elf-gcc -c $COMMONFLAGS $CFLAGS -Wa,-fPIC source/my_vsnprintf.c -o build/my_vsnprintf.o
$MY_LOCAL_LITEX_PATH/riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14/bin/riscv64-unknown-elf-gcc -c $COMMONFLAGS $CFLAGS -Wa,-fPIC source/$LIBPRINTF.c -o build/$LIBPRINTF.o
if [ $? -ne 0 ];
then
echo "*** gcc for $LIBPRINTF.c failed!"
exit $?
fi
$MY_LOCAL_LITEX_PATH/riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14/bin/riscv64-unknown-elf-gcc -c $COMMONFLAGS $CFLAGS -Wa,-fPIC source/$LIBSYSTIME.c -o build/$LIBSYSTIME.o
if [ $? -ne 0 ];
then
echo "*** gcc for my_vsnprintf.c failed!"
echo "*** gcc for $LIBSYSTIME.c failed!"
exit $?
fi
echo "Linking ..."
$MY_LOCAL_LITEX_PATH/riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14/bin/riscv64-unknown-elf-ld $LDFLAGS -T linker/ram$3.ld -N $CRT0 $OBJECTS $LXR build/$2.o build/$1.o build/my_vsnprintf.o lib/rv32ilibm.a lib/rv32ilibgcc.a -o build/$1.elf
$MY_LOCAL_LITEX_PATH/riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14/bin/riscv64-unknown-elf-ld $LDFLAGS -T linker/ram$3.ld -N $CRT0 $OBJECTS $LXR build/$2.o build/$1.o build/$LIBSYSTIME.o build/$LIBPRINTF.o lib/rv32ilibm.a lib/rv32ilibgcc.a -o build/$1.elf
if [ $? -ne 0 ];
then
echo "*** Link/Loader for build/$1.elf failed!"
@ -115,3 +131,4 @@ fi
# screen /dev/ttyUSB0 115200
# Press 'Ctrl-A k' to terminate ...
echo "Done (don't worry about the download 'error' in red!)."
echo "Loaded project $PROJECTNAME to RAM bank #$3 <---"

+ 2
- 1
software/source/README.md View File

@ -3,4 +3,5 @@ illumination.c - This is a sample application, demonstrating the use
of the 'neopixelengine' (the documentation can be found
under ./build/documentation/http/index.html)
my_vsnprintf.c - Some helpers. For float formatting use printf1() (really ugly!)
dramtransfer.c - DMA DRAM access loader
systime.c - (Daily) Time helper

+ 144
- 0
software/source/dramtransfer.c View File

@ -0,0 +1,144 @@
//
// dramtransfer.c
// DRAM transfer routines
//
// History:
// --------
// 21.12.20/KQ Initial version
//
#include <stdio.h>
#include <stdlib.h>
#include <console.h>
#include <string.h>
#include <uart.h>
#include <system.h>
#include <id.h>
#include <irq.h>
#include <crc.h>
#include "boot.h"
#include "readline.h"
#include "helpers.h"
#include "command.h"
#include "../../build/colorlight_5a_75b/software/include/generated/csr.h"
#include "../../build/colorlight_5a_75b/software/include/generated/soc.h"
#include "../../build/colorlight_5a_75b/software/include/generated/mem.h"
#include "../../build/colorlight_5a_75b/software/include/generated/git.h"
#include <spiflash.h>
#include <liblitedram/sdram.h>
#include <libliteeth/udp.h>
#include <libliteeth/mdio.h>
#include <liblitespi/spiflash.h>
#include <liblitesdcard/sdcard.h>
#include "../include/systime.h"
#include "../include/dramtransfer.h"
extern void busy_wait(unsigned int ms); // Worx!
// Load FPGA from DRAM
int load_FPGA(uint32_t iBaseAddress, int8_t bLen)
{
int i;
flush_l2_cache(); // Strictly nec. for longer transfers
if((iBaseAddress & 0xF) != 0) {
iBaseAddress &= 0xFFFFFFF0; // Enforce 16-byte alignment
printf("*** load_FPGA(): iBaseAddress alignment needs to be 16 Byte! Using %08Xh\n", iBaseAddress);
}
if((iBaseAddress < 0x40000000) || (iBaseAddress > 0x40400000)) {
printf("*** load_FPGA(): iBaseAddress not within DRAM range!\n");
return 0;
}
dramtransfer_bEnable_write(0); // For address change, turn off first!
dramtransfer_b32Address_write(iBaseAddress); // Provide a valid DRAM address
dramtransfer_bEnable_write(1); // Start DMA pickup & FIFO fill ...
for(i = 0; i < TIMEOUT; i++) {
if(dramtransfer_bValid_read()) // Wait 'til transfer done
break;
}
dramtransfer_bEnable_write(0); // Indicate termination of action
if(i>=TIMEOUT) { // Timing?
printf("*** TIMEOUT: bValid not set?\n");
return 0;
}
return 1; // Ok, FPGA loaded!
}
// Retrieve FPGA memory@offset
int32_t retrieve_FPGA(int iOffset)
{
if((iOffset < 0) || (iOffset >= FIFOSIZE)) {
printf("*** retrieve_FPGA(): Invalid offset?!\n");
iOffset = 0;
}
dramtransfer_b9Offset_write(iOffset); // ->memory[offset]
return dramtransfer_b32Data_read(); // memory[offset]
}
#define BASEADDRESS 0x40190000
#define MAXLOOPS 64 // 8x 0x400 (8x 256 * 4 = 8x 1024) => 0x2000
void dramtest(void)
{
uint32_t *TxPtr;
int i, j;
uint32_t iStart;
printf("---- DRAM transfer test ----\n");
iStart = systime(1); // Reset to defined value ...
int iSum = 0; // Time aggregate
for(j = 0; j < MAXLOOPS; j++) {
// Attention: int32_t Assignments require at least 4 bytes boundary alignment!
TxPtr = (uint32_t *)((BASEADDRESS & 0xFFFFFFFC) + j * (FIFOSIZE * sizeof(int32_t)));
for(i = 0; i < FIFOSIZE; i++) { // Fill source range data within DRAM
*(uint32_t *)(TxPtr+i) = j*FIFOSIZE + i+1;
}
iStart = systime(0); // Expect 0.70 ms/transfer ...
if(load_FPGA((uint32_t)TxPtr, 0)) { // Load <n> bytes from 0x40190000 ... (16 byte aligned!)
iSum += (systime(0) - iStart); // Time delta added
for(i=0;i<FIFOSIZE;i++) {
if((j*FIFOSIZE + i + 1) != retrieve_FPGA(i)) {
printf("%d: TxPtr -> %08X (%d 32-bit words) (Count before: %d) ", j, (uint32_t)TxPtr, FIFOSIZE, dramtransfer_b32RCount_read());
printf("\n*** FAIL mem[%03d] %d != %08Xh\n", i, j * FIFOSIZE + i + 1, retrieve_FPGA(i));
break;
}
}
}
else {
printf("*** load_FPGA(): FAILED?!\n");
break;
}
}
if(j >= MAXLOOPS) {
printf("PASS: %d bytes transferred in %dms w/ %d chuncks of %d bytes/each (0.%d ms/transfer), range %08Xh-%08Xh.\n",
MAXLOOPS * 4 * FIFOSIZE, iSum, MAXLOOPS, 4 * FIFOSIZE, iSum*100/MAXLOOPS, BASEADDRESS, BASEADDRESS+MAXLOOPS*4*FIFOSIZE-1);
}
#ifdef XXX
printf("\n---- System time test ----\n");
systime(86399999-1000);
printf("Pre: %d\n", systime(0));
busy_wait(2000);
printf("Post: %d\n", systime(0));
int hh, mm, ss; // ------------------ Time adjust test ----------------------
setsystime(13,55,0);
getsystime(&hh, &mm, &ss);
printf("Time now: %02d:%02d:%02d\nWait 60s, press [x] to terminate ...\n", hh, mm, ss);
while(!key_eval()) {
busy_wait(60000); // 60s
getsystime(&hh, &mm, &ss);
printf("Time now: %02d:%02d:%02d\nWait 60s, press [x] to terminate ...\n", hh, mm, ss);
}
#endif
}

+ 3
- 1
software/source/main.c View File

@ -1,11 +1,12 @@
//
// ramtest.c
// main.c
// RAM loadable (& executable!) object
//
// History:
// --------
// 07.10.20/KQ Initial version
// 18.10.20/KQ RAM version w/ command interpreter loop ready
// 02.01.21/KQ DRAM DMA access integrated
//
#include <stdio.h>
@ -40,6 +41,7 @@
#include <liblitesdcard/sdcard.h>
#include "../include/systime.h"
#include "../include/illumination.h"
extern void busy_wait(unsigned int ms); // Worx!


+ 47
- 0
software/source/systime.c View File

@ -0,0 +1,47 @@
//
// systime.c
// Little daily time helper routines
//
// History:
// --------
// 31.12.20/KQ Initial version
//
#include <stdio.h>
#include <stdlib.h>
#include <console.h>
#include <string.h>
#include <uart.h>
#include <system.h>
#include <id.h>
#include <irq.h>
#include <crc.h>
#include "../include/systime.h"
uint32_t systime(uint32_t setval)
{
if(setval != 0) // Read value?
systime_b32CurrentMSeconds_write(setval); // Nope!
else // Yap!
return systime_b32CurrentMSeconds_read();
return setval;
}
void setsystime(int hh, int mm, int ss)
{
systime((uint32_t)(hh * 3600 + mm * 60 + ss) * 1000);
}
void getsystime(int *hh, int *mm, int *ss)
{
uint32_t tVal = systime(0)/1000; // ms -> s
//
*hh = tVal / 3600;
tVal -= (*hh * 3600);
*mm = tVal/60;
tVal -= (*mm * 60);
*ss = tVal;
}

Loading…
Cancel
Save