Nothing worx no more!

master
kaqu 2 years ago
parent 8891ac6964
commit 0cdd27d264
  1. 14
      debugger/risq5dbg.py
  2. 10
      helpers/prepare_firmware.py
  3. 46
      instruction_decode.vcd
  4. 15
      libmodules/core.py
  5. 4
      libmodules/register_file.py
  6. 1427
      litex/litex/soc/integration/soc.py
  7. 1426
      litex/litex/soc/integration/soc.py.original
  8. 348
      litex/litex/soc/integration/soc_core.py
  9. 348
      litex/litex/soc/integration/soc_core.py.original
  10. 11
      risq5.py
  11. 8
      software/ramcreate.sh
  12. 19
      software/source/testtimerinterrupt.c

@ -24,14 +24,14 @@ def risq5dbg(csr_csv):
wb.open() # To remote client ... (requires server up & running, s.a.)
print("RISQ5 debugger started ...")
print("Entering single step mode")
wb.regs.risq5_b32mode.write(1) # Enable run (but will halt before 1. instruction!)
#print("Entering single step mode")
#wb.regs.risq5_b32mode.write(1) # Enable run (but will halt before 1. instruction!)
print("PC adjusted to 0x40190078")
wb.regs.risq5_b32_next_pc.write(0x40190078) # Provide a new PC address
mode = wb.regs.risq5_b32mode.read() # Pick up current mode setting
wb.regs.risq5_b32mode.write(mode | 16) # Adjust PC, will load L1 2nd time now
wb.regs.risq5_b1_wb_reg_we.write(0) # Reset write requests (if any pending)
#print("PC adjusted to 0x40190078")
#wb.regs.risq5_b32_next_pc.write(0x40190078) # Provide a new PC address
#mode = wb.regs.risq5_b32mode.read() # Pick up current mode setting
#wb.regs.risq5_b32mode.write(mode | 16) # Adjust PC, will load L1 2nd time now
#wb.regs.risq5_b1_wb_reg_we.write(0) # Reset write requests (if any pending)
run_Qt5_GUI(wb) # Now switch to GUI (will never return!)

@ -43,6 +43,16 @@ def copyjob():
- standard with IDEs
If run solely from helpers directory will work as well ...
"""
copyjob_actual(
os.path.dirname(litex.soc.integration.__file__) + "/",
os.environ["PWD"] + relpath + "/litex/litex/soc/integration/",
"soc.py"
)
copyjob_actual(
os.path.dirname(litex.soc.integration.__file__) + "/",
os.environ["PWD"] + relpath + "/litex/litex/soc/integration/",
"soc_core.py"
)
copyjob_actual(
os.path.dirname(litedram.frontend.dma.__file__) + "/",
os.environ["PWD"] + relpath + "/litex/litedram/frontend/",

@ -1,46 +0,0 @@
$var wire 32 ! test_opcode $end
$var wire 32 " test_x $end
$var wire 7 # op $end
$var wire 5 $ rd $end
$var wire 3 % f3 $end
$var wire 5 & rs1 $end
$var wire 5 ' rs2 $end
$var wire 7 ( f7 $end
$var wire 12 ) imm_i $end
$var wire 12 * imm_s $end
$var wire 12 + imm_b $end
$var wire 20 , imm_u $end
$var wire 19 - imm_j $end
$var wire 1 . sys_clk $end
$dumpvars
$end
#0
b00000000000000000000000000000000 !
b00000000000000000000000000000000 "
b0000000 #
b00000 $
b000 %
b00000 &
b00000 '
b0000000 (
b000000000000 )
b000000000000 *
b000000000000 +
b00000000000000000000 ,
b0000000000000000000 -
0.
#8
b00000000000000000000000000000000 !
b00000000000000000000000000000000 "
b0000000 #
b00000 $
b000 %
b00000 &
b00000 '
b0000000 (
b000000000000 )
b000000000000 *
b000000000000 +
b00000000000000000000 ,
b0000000000000000000 -
0.

@ -59,7 +59,7 @@ class Risq5Core(Module, AutoCSR, AutoDoc, ModuleDoc):
:b32_FSMs: FSM states
"""
def __init__(self, L1CacheSize=8, L1Cache=None, LUCacheSize=4, LUCache=None, SU_Unit=None, DRAMbase=0x40000000, DRAMLen=0x400000, clint=None):
def __init__(self, RAMWaitTime=128, L1CacheSize=8, L1Cache=None, LUCacheSize=4, LUCache=None, SU_Unit=None, clint=None):
# Inputs
self.b32mode = CSRStorage(32, reset_less=True,
@ -168,7 +168,7 @@ class Risq5Core(Module, AutoCSR, AutoDoc, ModuleDoc):
# Local vars.
# Register file ----------------------------------------------------------------------------
self.submodules.regs = regs = Risq5RegisterFile(DRAMbase=DRAMbase)
self.submodules.regs = regs = Risq5RegisterFile()
self.sync += [ # External register file write port (debugger)
regs.write_ext_index.eq(self.b5_wb_reg_no.storage), # Let write index follow
regs.ext_wrport.dat_w.eq(self.b32_wb_reg_value_w.storage), # & value as well ...
@ -263,7 +263,7 @@ class Risq5Core(Module, AutoCSR, AutoDoc, ModuleDoc):
self.submodules += ALU_fsm
self.ALU_state = Signal(4, reset_less=True) # Debugging support
self.ALU_loaddelay = Signal(8, reset_less=True) # 1st opcode load delay
self.ALU_loaddelay = Signal(32, reset_less=True) # 1st opcode load delay
ALU_fsm.act("ALU_INIT", # Initialize static registers (TODO: Run after reset?!)
NextValue(regs.csr[risq5defs.CSR_misa], 0x40000100), # misa: 32 bit/I 'extension'
@ -283,7 +283,7 @@ class Risq5Core(Module, AutoCSR, AutoDoc, ModuleDoc):
)
ALU_fsm.act("ALU_DELAY",
# ------------------------------ MODIFIED LAST -------------------------------------------
If(self.ALU_loaddelay > 128, # Interrupt calls execute w/ full performance (safely) (TODO: Try to lower ...)
If(self.ALU_loaddelay > RAMWaitTime, # Interrupt calls execute w/ full performance (safely) (TODO: Try to lower ...)
NextState("ALU_EVAL1") # Load delay for opcode cache is next
).Else( # Time not yet elapsed ...
NextValue(self.ALU_loaddelay, self.ALU_loaddelay + 1),
@ -292,10 +292,15 @@ class Risq5Core(Module, AutoCSR, AutoDoc, ModuleDoc):
#----- LOOP ENTRY CACHED DATA ---------------------------------
ALU_fsm.act("ALU_EVAL1",
NextValue(self.ALU_loaddelay, 0),
NextState("ALU_EVAL2") # Load delay nec. for opcode indirect loading
)
ALU_fsm.act("ALU_EVAL2",
NextState("ALU_EVAL3") # Load delay nec. for opcode indirect loading
If(self.ALU_loaddelay > RAMWaitTime, # Interrupt calls execute w/ full performance (safely) (TODO: Try to lower ...)
NextState("ALU_EVAL3") # Load delay nec. for opcode indirect loading
).Else( # Time not yet elapsed ...
NextValue(self.ALU_loaddelay, self.ALU_loaddelay + 1),
)
)
ALU_fsm.act("ALU_EVAL3",
NextValue(regs.opcode, L1Cache.b32Data.storage), # Load opcode (depends upon L1Cache.b9Offset)

@ -22,11 +22,11 @@ class Risq5RegisterFile(Module):
"""
Risq5 The RISC-V register file
"""
def __init__(self, DRAMbase=0x40000000):
def __init__(self):
# Register file ----------------------------------------------------------------------------
WORDSIZE = 32
self.pc = Signal(WORDSIZE, reset=DRAMbase) # Program counter (TODO: Start @0!!!)
self.pc = Signal(WORDSIZE, reset=0) # Program counter (RISC-V start @0!)
self.opcode = Signal(WORDSIZE, reset_less=True) # Loaded from instruction fetch
# Instruction decode, 32 bit opcode parts --------------------------------------------------

@ -0,0 +1,1427 @@
#
# This file is part of LiteX.
#
# This file is Copyright (c) 2014-2020 Florent Kermarrec <florent@enjoy-digital.fr>
# This file is Copyright (c) 2013-2014 Sebastien Bourdeauducq <sb@m-labs.hk>
# This file is Copyright (c) 2019 Gabriel L. Somlo <somlo@cmu.edu>
# SPDX-License-Identifier: BSD-2-Clause
import logging
import time
import datetime
from math import log2, ceil
from migen import *
from litex.soc.cores import cpu
from litex.soc.cores.identifier import Identifier
from litex.soc.cores.timer import Timer
from litex.soc.cores.spi_flash import SpiFlash
from litex.soc.cores.spi import SPIMaster
from litex.soc.interconnect.csr import *
from litex.soc.interconnect import csr_bus
from litex.soc.interconnect import stream
from litex.soc.interconnect import wishbone
from litex.soc.interconnect import axi
logging.basicConfig(level=logging.INFO)
# Helpers ------------------------------------------------------------------------------------------
def auto_int(x):
return int(x, 0)
def colorer(s, color="bright"):
header = {
"bright": "\x1b[1m",
"green": "\x1b[32m",
"cyan": "\x1b[36m",
"red": "\x1b[31m",
"yellow": "\x1b[33m",
"underline": "\x1b[4m"}[color]
trailer = "\x1b[0m"
return header + str(s) + trailer
def build_time(with_time=True):
fmt = "%Y-%m-%d %H:%M:%S" if with_time else "%Y-%m-%d"
return datetime.datetime.fromtimestamp(time.time()).strftime(fmt)
# SoCConstant --------------------------------------------------------------------------------------
def SoCConstant(value):
return value
# SoCRegion ----------------------------------------------------------------------------------------
class SoCRegion:
# 14.02.21/KQ Cache disabled (use own) def __init__(self, origin=None, size=None, mode="rw", cached=True, linker=False):
def __init__(self, origin=None, size=None, mode="rw", cached=False, linker=False):
self.logger = logging.getLogger("SoCRegion")
self.origin = origin
self.size = size
if size != 2**log2_int(size, False):
self.logger.info("Region size {} internally from {} to {}.".format(
colorer("rounded", color="cyan"),
colorer("0x{:08x}".format(size)),
colorer("0x{:08x}".format(2**log2_int(size, False)))))
self.size_pow2 = 2**log2_int(size, False)
self.mode = mode
self.cached = cached
self.linker = linker
def decoder(self, bus):
origin = self.origin
size = self.size_pow2
if (origin & (size - 1)) != 0:
self.logger.error("Origin needs to be aligned on size:")
self.logger.error(self)
raise
if (origin == 0) and (size == 2**bus.address_width):
return lambda a : True
origin >>= int(log2(bus.data_width//8)) # bytes to words aligned
size >>= int(log2(bus.data_width//8)) # bytes to words aligned
return lambda a: (a[log2_int(size):] == (origin >> log2_int(size)))
def __str__(self):
r = ""
if self.origin is not None:
r += "Origin: {}, ".format(colorer("0x{:08x}".format(self.origin)))
if self.size is not None:
r += "Size: {}, ".format(colorer("0x{:08x}".format(self.size)))
r += "Mode: {}, ".format(colorer(self.mode.upper()))
r += "Cached: {} ".format(colorer(self.cached))
r += "Linker: {}".format(colorer(self.linker))
return r
class SoCIORegion(SoCRegion): pass
# SoCCSRRegion -------------------------------------------------------------------------------------
class SoCCSRRegion:
def __init__(self, origin, busword, obj):
self.origin = origin
self.busword = busword
self.obj = obj
# SoCBusHandler ------------------------------------------------------------------------------------
class SoCBusHandler(Module):
supported_standard = ["wishbone", "axi-lite"]
supported_data_width = [32, 64]
supported_address_width = [32]
# Creation -------------------------------------------------------------------------------------
def __init__(self, name="SoCBusHandler", standard="wishbone", data_width=32, address_width=32, timeout=1e6, reserved_regions={}):
self.logger = logging.getLogger(name)
self.logger.info("Creating Bus Handler...")
# Check Standard
if standard not in self.supported_standard:
self.logger.error("Unsupported {} {}, supporteds: {:s}".format(
colorer("Bus standard", color="red"),
colorer(standard),
colorer(", ".join(self.supported_standard))))
raise
# Check Data Width
if data_width not in self.supported_data_width:
self.logger.error("Unsupported {} {}, supporteds: {:s}".format(
colorer("Data Width", color="red"),
colorer(data_width),
colorer(", ".join(str(x) for x in self.supported_data_width))))
raise
# Check Address Width
if address_width not in self.supported_address_width:
self.logger.error("Unsupported {} {}, supporteds: {:s}".format(
colorer("Address Width", color="red"),
colorer(address_width),
colorer(", ".join(str(x) for x in self.supported_address_width))))
raise
# Create Bus
self.standard = standard
self.data_width = data_width
self.address_width = address_width
self.masters = {}
self.slaves = {}
self.regions = {}
self.io_regions = {}
self.timeout = timeout
self.logger.info("{}-bit {} Bus, {}GiB Address Space.".format(
colorer(data_width), colorer(standard), colorer(2**address_width/2**30)))
# Adding reserved regions
self.logger.info("Adding {} Bus Regions...".format(colorer("reserved", color="cyan")))
for name, region in reserved_regions.items():
if isinstance(region, int):
region = SoCRegion(origin=region, size=0x1000000)
self.add_region(name, region)
self.logger.info("Bus Handler {}.".format(colorer("created", color="green")))
# Add/Allog/Check Regions ----------------------------------------------------------------------
def add_region(self, name, region):
allocated = False
if name in self.regions.keys() or name in self.io_regions.keys():
self.logger.error("{} already declared as Region:".format(colorer(name, color="red")))
self.logger.error(self)
raise
# Check if SoCIORegion
if isinstance(region, SoCIORegion):
self.io_regions[name] = region
overlap = self.check_regions_overlap(self.io_regions)
if overlap is not None:
self.logger.error("IO Region {} between {} and {}:".format(
colorer("overlap", color="red"),
colorer(overlap[0]),
colorer(overlap[1])))
self.logger.error(str(self.io_regions[overlap[0]]))
self.logger.error(str(self.io_regions[overlap[1]]))
raise
self.logger.info("{} Region {} at {}.".format(
colorer(name, color="underline"),
colorer("added", color="green"),
str(region)))
# Check if SoCRegion
elif isinstance(region, SoCRegion):
# If no origin specified, allocate region.
if region.origin is None:
allocated = True
region = self.alloc_region(name, region.size, region.cached)
self.regions[name] = region
# Else add region and check for overlaps.
else:
if not region.cached:
if not self.check_region_is_io(region):
self.logger.error("{} Region {}: {}.".format(
colorer(name),
colorer("not in IO region", color="red"),
str(region)))
self.logger.error(self)
raise
self.regions[name] = region
overlap = self.check_regions_overlap(self.regions)
if overlap is not None:
self.logger.error("Region {} between {} and {}:".format(
colorer("overlap", color="red"),
colorer(overlap[0]),
colorer(overlap[1])))
self.logger.error(str(self.regions[overlap[0]]))
self.logger.error(str(self.regions[overlap[1]]))
raise
self.logger.info("{} Region {} at {}.".format(
colorer(name, color="underline"),
colorer("allocated" if allocated else "added", color="cyan" if allocated else "green"),
str(region)))
else:
self.logger.error("{} is not a supported Region.".format(colorer(name, color="red")))
raise
def alloc_region(self, name, size, cached=True):
self.logger.info("Allocating {} Region of size {}...".format(
colorer("Cached" if cached else "IO"),
colorer("0x{:08x}".format(size))))
# Limit Search Regions
if cached == False:
search_regions = self.io_regions
else:
search_regions = {"main": SoCRegion(origin=0x00000000, size=2**self.address_width-1)}
# Iterate on Search_Regions to find a Candidate
for _, search_region in search_regions.items():
origin = search_region.origin
while (origin + size) < (search_region.origin + search_region.size_pow2):
# Create a Candicate.
candidate = SoCRegion(origin=origin, size=size, cached=cached)
overlap = False
# Check Candidate does not overlap with allocated existing regions
for _, allocated in self.regions.items():
if self.check_regions_overlap({"0": allocated, "1": candidate}) is not None:
origin = allocated.origin + allocated.size_pow2
overlap = True
break
if not overlap:
# If no overlap, the Candidate is selected
return candidate
self.logger.error("Not enough Address Space to allocate Region.")
raise
def check_regions_overlap(self, regions, check_linker=False):
i = 0
while i < len(regions):
n0 = list(regions.keys())[i]
r0 = regions[n0]
for n1 in list(regions.keys())[i+1:]:
r1 = regions[n1]
if r0.linker or r1.linker:
if not check_linker:
continue
if r0.origin >= (r1.origin + r1.size_pow2):
continue
if r1.origin >= (r0.origin + r0.size_pow2):
continue
return (n0, n1)
i += 1
return None
def check_region_is_in(self, region, container):
is_in = True
if not (region.origin >= container.origin):
is_in = False
if not ((region.origin + region.size) < (container.origin + container.size)):
is_in = False
return is_in
def check_region_is_io(self, region):
is_io = False
for _, io_region in self.io_regions.items():
if self.check_region_is_in(region, io_region):
is_io = True
return is_io
# Add Master/Slave -----------------------------------------------------------------------------
def add_adapter(self, name, interface, direction="m2s"):
assert direction in ["m2s", "s2m"]
# Data width conversion
if interface.data_width != self.data_width:
interface_cls = type(interface)
converter_cls = {
wishbone.Interface: wishbone.Converter,
axi.AXILiteInterface: axi.AXILiteConverter,
}[interface_cls]
converted_interface = interface_cls(data_width=self.data_width)
if direction == "m2s":
master, slave = interface, converted_interface
elif direction == "s2m":
master, slave = converted_interface, interface
converter = converter_cls(master=master, slave=slave)
self.submodules += converter
else:
converted_interface = interface
# Wishbone <-> AXILite bridging
main_bus_cls = {
"wishbone": wishbone.Interface,
"axi-lite": axi.AXILiteInterface,
}[self.standard]
if isinstance(converted_interface, main_bus_cls):
bridged_interface = converted_interface
else:
bridged_interface = main_bus_cls(data_width=self.data_width)
if direction == "m2s":
master, slave = converted_interface, bridged_interface
elif direction == "s2m":
master, slave = bridged_interface, converted_interface
bridge_cls = {
(wishbone.Interface, axi.AXILiteInterface): axi.Wishbone2AXILite,
(axi.AXILiteInterface, wishbone.Interface): axi.AXILite2Wishbone,
}[type(master), type(slave)]
bridge = bridge_cls(master, slave)
self.submodules += bridge
if type(interface) != type(bridged_interface) or interface.data_width != bridged_interface.data_width:
fmt = "{name} Bus {converted} from {frombus} {frombits}-bit to {tobus} {tobits}-bit."
bus_names = {
wishbone.Interface: "Wishbone",
axi.AXILiteInterface: "AXI Lite",
}
self.logger.info(fmt.format(
name = colorer(name),
converted = colorer("converted", color="cyan"),
frombus = colorer(bus_names[type(interface)]),
frombits = colorer(interface.data_width),
tobus = colorer(bus_names[type(bridged_interface)]),
tobits = colorer(bridged_interface.data_width)))
return bridged_interface
def add_master(self, name=None, master=None):
if name is None:
name = "master{:d}".format(len(self.masters))
if name in self.masters.keys():
self.logger.error("{} {} as Bus Master:".format(
colorer(name),
colorer("already declared", color="red")))
self.logger.error(self)
raise
master = self.add_adapter(name, master, "m2s")
self.masters[name] = master
self.logger.info("{} {} as Bus Master.".format(
colorer(name, color="underline"),
colorer("added", color="green")))
def add_slave(self, name=None, slave=None, region=None):
no_name = name is None
no_region = region is None
if no_name and no_region:
self.logger.error("Please {} {} or/and {} of Bus Slave.".format(
colorer("specify", color="red"),
colorer("name"),
colorer("region")))
raise
if no_name:
name = "slave{:d}".format(len(self.slaves))
if no_region:
region = self.regions.get(name, None)
if region is None:
self.logger.error("{} Region {}.".format(
colorer(name),
colorer("not found", color="red")))
raise
else:
self.add_region(name, region)
if name in self.slaves.keys():
self.logger.error("{} {} as Bus Slave:".format(
colorer(name),
colorer("already declared", color="red")))
self.logger.error(self)
raise
slave = self.add_adapter(name, slave, "s2m")
self.slaves[name] = slave
self.logger.info("{} {} as Bus Slave.".format(
colorer(name, color="underline"),
colorer("added", color="green")))
# Str ------------------------------------------------------------------------------------------
def __str__(self):
r = "{}-bit {} Bus, {}GiB Address Space.\n".format(
colorer(self.data_width), colorer(self.standard), colorer(2**self.address_width/2**30))
r += "IO Regions: ({})\n".format(len(self.io_regions.keys())) if len(self.io_regions.keys()) else ""
io_regions = {k: v for k, v in sorted(self.io_regions.items(), key=lambda item: item[1].origin)}
for name, region in io_regions.items():
r += colorer(name, color="underline") + " "*(20-len(name)) + ": " + str(region) + "\n"
r += "Bus Regions: ({})\n".format(len(self.regions.keys())) if len(self.regions.keys()) else ""
regions = {k: v for k, v in sorted(self.regions.items(), key=lambda item: item[1].origin)}
for name, region in regions.items():
r += colorer(name, color="underline") + " "*(20-len(name)) + ": " + str(region) + "\n"
r += "Bus Masters: ({})\n".format(len(self.masters.keys())) if len(self.masters.keys()) else ""
for name in self.masters.keys():
r += "- {}\n".format(colorer(name, color="underline"))
r += "Bus Slaves: ({})\n".format(len(self.slaves.keys())) if len(self.slaves.keys()) else ""
for name in self.slaves.keys():
r += "- {}\n".format(colorer(name, color="underline"))
r = r[:-1]
return r
# SoCLocHandler --------------------------------------------------------------------------------------
class SoCLocHandler(Module):
# Creation -------------------------------------------------------------------------------------
def __init__(self, name, n_locs):
self.name = name
self.locs = {}
self.n_locs = n_locs
# Add ------------------------------------------------------------------------------------------
def add(self, name, n=None, use_loc_if_exists=False):
allocated = False
if not (use_loc_if_exists and name in self.locs.keys()):
if name in self.locs.keys():
self.logger.error("{} {} name {}.".format(
colorer(name), self.name, colorer("already used", color="red")))
self.logger.error(self)
raise
if n in self.locs.values():
self.logger.error("{} {} Location {}.".format(
colorer(n), self.name, colorer("already used", color="red")))
self.logger.error(self)
raise
if n is None:
allocated = True
n = self.alloc(name)
else:
if n < 0:
self.logger.error("{} {} Location should be {}.".format(
colorer(n),
self.name,
colorer("positive", color="red")))
raise
if n > self.n_locs:
self.logger.error("{} {} Location {} than maximum: {}.".format(
colorer(n),
self.name,
colorer("higher", color="red"),
colorer(self.n_locs)))
raise
self.locs[name] = n
else:
n = self.locs[name]
self.logger.info("{} {} {} at Location {}.".format(
colorer(name, color="underline"),
self.name,
colorer("allocated" if allocated else "added", color="cyan" if allocated else "green"),
colorer(n)))
# Alloc ----------------------------------------------------------------------------------------
def alloc(self, name):
for n in range(self.n_locs):
if n not in self.locs.values():
return n
self.logger.error("Not enough Locations.")
self.logger.error(self)
raise
# Str ------------------------------------------------------------------------------------------
def __str__(self):
r = "{} Locations: ({})\n".format(self.name, len(self.locs.keys())) if len(self.locs.keys()) else ""
locs = {k: v for k, v in sorted(self.locs.items(), key=lambda item: item[1])}
length = 0
for name in locs.keys():
if len(name) > length: length = len(name)
for name in locs.keys():
r += "- {}{}: {}\n".format(colorer(name, color="underline"), " "*(length + 1 - len(name)), colorer(self.locs[name]))
return r
# SoCCSRHandler ------------------------------------------------------------------------------------
class SoCCSRHandler(SoCLocHandler):
supported_data_width = [8, 32]
supported_address_width = [14+i for i in range(4)]
supported_alignment = [32]
supported_paging = [0x800*2**i for i in range(4)]
supported_ordering = ["big", "little"]
# Creation -------------------------------------------------------------------------------------
def __init__(self, data_width=32, address_width=14, alignment=32, paging=0x800, ordering="big", reserved_csrs={}):
SoCLocHandler.__init__(self, "CSR", n_locs=alignment//8*(2**address_width)//paging)
self.logger = logging.getLogger("SoCCSRHandler")
self.logger.info("Creating CSR Handler...")
# Check Data Width
if data_width not in self.supported_data_width:
self.logger.error("Unsupported {} {}, supporteds: {:s}".format(
colorer("Data Width", color="red"),
colorer(data_width),
colorer(", ".join(str(x) for x in self.supported_data_width))))
raise
# Check Address Width
if address_width not in self.supported_address_width:
self.logger.error("Unsupported {} {} supporteds: {:s}".format(
colorer("Address Width", color="red"),
colorer(address_width),
colorer(", ".join(str(x) for x in self.supported_address_width))))
raise
# Check Alignment
if alignment not in self.supported_alignment:
self.logger.error("Unsupported {}: {} supporteds: {:s}".format(
colorer("Alignment", color="red"),
colorer(alignment),
colorer(", ".join(str(x) for x in self.supported_alignment))))
raise
if data_width > alignment:
self.logger.error("Alignment ({}) {} Data Width ({})".format(
colorer(alignment),
colorer("should be >=", color="red"),
colorer(data_width)))
raise
# Check Paging
if paging not in self.supported_paging:
self.logger.error("Unsupported {} 0x{}, supporteds: {:s}".format(
colorer("Paging", color="red"),
colorer("{:x}".format(paging)),
colorer(", ".join("0x{:x}".format(x) for x in self.supported_paging))))
raise
# Check Ordering
if ordering not in self.supported_ordering:
self.logger.error("Unsupported {} {}, supporteds: {:s}".format(
colorer("Ordering", color="red"),
colorer("{}".format(paging)),
colorer(", ".join("{}".format(x) for x in self.supported_ordering))))
raise
# Create CSR Handler
self.data_width = data_width
self.address_width = address_width
self.alignment = alignment
self.paging = paging
self.ordering = ordering
self.masters = {}
self.regions = {}
self.logger.info("{}-bit CSR Bus, {}-bit Aligned, {}KiB Address Space, {}B Paging, {} Ordering (Up to {} Locations).".format(
colorer(self.data_width),
colorer(self.alignment),
colorer(2**self.address_width/2**10),
colorer(self.paging),
colorer(self.ordering),
colorer(self.n_locs)))
# Adding reserved CSRs
self.logger.info("Adding {} CSRs...".format(colorer("reserved", color="cyan")))
for name, n in reserved_csrs.items():
self.add(name, n)
self.logger.info("CSR Handler {}.".format(colorer("created", color="green")))
# Add Master -----------------------------------------------------------------------------------
def add_master(self, name=None, master=None):
if name is None:
name = "master{:d}".format(len(self.masters))
if name in self.masters.keys():
self.logger.error("{} {} as CSR Master:".format(
colorer(name),
colorer("already declared", color="red")))
self.logger.error(self)
raise
if master.data_width != self.data_width:
self.logger.error("{} Master/Handler Data Width {} ({} vs {}).".format(
colorer(name),
colorer("missmatch", color="red"),
colorer(master.data_width),
colorer(self.data_width)))
raise
self.masters[name] = master
self.logger.info("{} {} as CSR Master.".format(
colorer(name, color="underline"),
colorer("added", color="green")))
# Add Region -----------------------------------------------------------------------------------
def add_region(self, name, region):
# FIXME: add checks
self.regions[name] = region
# Address map ----------------------------------------------------------------------------------
def address_map(self, name, memory):
if memory is not None:
name = name + "_" + memory.name_override
if self.locs.get(name, None) is None:
self.logger.error("CSR {} {}.".format(
colorer(name),
colorer("not found", color="red")))
self.logger.error(self)
raise
return self.locs[name]
# Str ------------------------------------------------------------------------------------------
def __str__(self):
r = "{}-bit CSR Bus, {}-bit Aligned, {}KiB Address Space, {}B Paging, {} Ordering (Up to {} Locations).\n".format(
colorer(self.data_width),
colorer(self.alignment),
colorer(2**self.address_width/2**10),
colorer(self.paging),
colorer(self.ordering),
colorer(self.n_locs))
r += SoCLocHandler.__str__(self)
r = r[:-1]
return r
# SoCIRQHandler ------------------------------------------------------------------------------------
class SoCIRQHandler(SoCLocHandler):
# Creation -------------------------------------------------------------------------------------
def __init__(self, n_irqs=32, reserved_irqs={}):
SoCLocHandler.__init__(self, "IRQ", n_locs=n_irqs)
self.logger = logging.getLogger("SoCIRQHandler")
self.logger.info("Creating IRQ Handler...")
# Check IRQ Number
if n_irqs > 32:
self.logger.error("Unsupported IRQs number: {} supporteds: {:s}".format(
colorer(n, color="red"), colorer("Up to 32", color="green")))
raise
# Create IRQ Handler
self.logger.info("IRQ Handler (up to {} Locations).".format(colorer(n_irqs)))
# Adding reserved IRQs
self.logger.info("Adding {} IRQs...".format(colorer("reserved", color="cyan")))
for name, n in reserved_irqs.items():
self.add(name, n)
self.logger.info("IRQ Handler {}.".format(colorer("created", color="green")))
# Str ------------------------------------------------------------------------------------------
def __str__(self):
r ="IRQ Handler (up to {} Locations).\n".format(colorer(self.n_locs))
r += SoCLocHandler.__str__(self)
r = r[:-1]
return r
# SoCController ------------------------------------------------------------------------------------
class SoCController(Module, AutoCSR):
def __init__(self,
with_reset = True,
with_scratch = True,
with_errors = True):
if with_reset:
self._reset = CSRStorage(1, description="""Write a ``1`` to this register to reset the SoC.""")
if with_scratch:
self._scratch = CSRStorage(32, reset=0x12345678, description="""
Use this register as a scratch space to verify that software read/write accesses
to the Wishbone/CSR bus are working correctly. The initial reset value of 0x1234578
can be used to verify endianness.""")
if with_errors:
self._bus_errors = CSRStatus(32, description="Total number of Wishbone bus errors (timeouts) since start.")
# # #
# Reset
if with_reset:
self.reset = Signal()
self.comb += self.reset.eq(self._reset.re)
# Errors
if with_errors:
self.bus_error = Signal()
bus_errors = Signal(32)
self.sync += [
If(bus_errors != (2**len(bus_errors)-1),
If(self.bus_error, bus_errors.eq(bus_errors + 1))
)
]
self.comb += self._bus_errors.status.eq(bus_errors)
# SoC ----------------------------------------------------------------------------------------------
class SoC(Module):
mem_map = {}
def __init__(self, platform, sys_clk_freq,
bus_standard = "wishbone",
bus_data_width = 32,
bus_address_width = 32,
bus_timeout = 1e6,
bus_reserved_regions = {},
csr_data_width = 32,
csr_address_width = 14,
csr_paging = 0x800,
csr_ordering = "big",
csr_reserved_csrs = {},
irq_n_irqs = 32,
irq_reserved_irqs = {},
):
self.logger = logging.getLogger("SoC")
self.logger.info(colorer(" __ _ __ _ __ ", color="bright"))
self.logger.info(colorer(" / / (_) /____ | |/_/ ", color="bright"))
self.logger.info(colorer(" / /__/ / __/ -_)> < ", color="bright"))
self.logger.info(colorer(" /____/_/\\__/\\__/_/|_| ", color="bright"))
self.logger.info(colorer(" Build your hardware, easily!", color="bright"))
self.logger.info(colorer("-"*80, color="bright"))
self.logger.info(colorer("Creating SoC... ({})".format(build_time())))
self.logger.info(colorer("-"*80, color="bright"))
self.logger.info("FPGA device : {}.".format(platform.device))
self.logger.info("System clock: {:3.2f}MHz.".format(sys_clk_freq/1e6))
# SoC attributes ---------------------------------------------------------------------------
self.platform = platform
self.sys_clk_freq = sys_clk_freq
self.constants = {}
self.csr_regions = {}
# SoC Bus Handler --------------------------------------------------------------------------
self.submodules.bus = SoCBusHandler(
standard = bus_standard,
data_width = bus_data_width,
address_width = bus_address_width,
timeout = bus_timeout,
reserved_regions = bus_reserved_regions,
)
# SoC Bus Handler --------------------------------------------------------------------------
self.submodules.csr = SoCCSRHandler(
data_width = csr_data_width,
address_width = csr_address_width,
alignment = 32,
paging = csr_paging,
ordering = csr_ordering,
reserved_csrs = csr_reserved_csrs,
)
# SoC IRQ Handler --------------------------------------------------------------------------
self.submodules.irq = SoCIRQHandler(
n_irqs = irq_n_irqs,
reserved_irqs = irq_reserved_irqs
)
self.logger.info(colorer("-"*80, color="bright"))
self.logger.info(colorer("Initial SoC:"))
self.logger.info(colorer("-"*80, color="bright"))
self.logger.info(self.bus)
self.logger.info(self.csr)
self.logger.info(self.irq)
self.logger.info(colorer("-"*80, color="bright"))
self.add_config("CLOCK_FREQUENCY", int(sys_clk_freq))
# SoC Helpers ----------------------------------------------------------------------------------
def check_if_exists(self, name):
if hasattr(self, name):
self.logger.error("{} SubModule already {}.".format(
colorer(name),
colorer("declared", color="red")))
raise
def add_constant(self, name, value=None):
name = name.upper()
if name in self.constants.keys():
self.logger.error("{} Constant already {}.".format(
colorer(name),
colorer("declared", color="red")))
raise
self.constants[name] = SoCConstant(value)
def add_config(self, name, value=None):
name = "CONFIG_" + name
if isinstance(value, str):
self.add_constant(name + "_" + value)
else:
self.add_constant(name, value)
# SoC Main Components --------------------------------------------------------------------------
def add_controller(self, name="ctrl", **kwargs):
self.check_if_exists(name)
setattr(self.submodules, name, SoCController(**kwargs))
self.csr.add(name, use_loc_if_exists=True)
def add_ram(self, name, origin, size, contents=[], mode="rw"):
ram_cls = {
"wishbone": wishbone.SRAM,
"axi-lite": axi.AXILiteSRAM,
}[self.bus.standard]
interface_cls = {
"wishbone": wishbone.Interface,
"axi-lite": axi.AXILiteInterface,
}[self.bus.standard]
ram_bus = interface_cls(data_width=self.bus.data_width)
ram = ram_cls(size, bus=ram_bus, init=contents, read_only=(mode == "r"))
self.bus.add_slave(name, ram.bus, SoCRegion(origin=origin, size=size, mode=mode))
self.check_if_exists(name)
self.logger.info("RAM {} {} {}.".format(
colorer(name),
colorer("added", color="green"),
self.bus.regions[name]))
setattr(self.submodules, name, ram)
def add_rom(self, name, origin, size, contents=[], mode="r"):
self.add_ram(name, origin, size, contents, mode=mode)
def add_csr_bridge(self, origin, register=False):
csr_bridge_cls = {
"wishbone": wishbone.Wishbone2CSR,
"axi-lite": axi.AXILite2CSR,
}[self.bus.standard]
self.submodules.csr_bridge = csr_bridge_cls(
bus_csr = csr_bus.Interface(
address_width = self.csr.address_width,
data_width = self.csr.data_width),
register = register)
csr_size = 2**(self.csr.address_width + 2)
csr_region = SoCRegion(origin=origin, size=csr_size, cached=False)
bus = getattr(self.csr_bridge, self.bus.standard.replace('-', '_'))
self.bus.add_slave("csr", bus, csr_region)
self.csr.add_master(name="bridge", master=self.csr_bridge.csr)
self.add_config("CSR_DATA_WIDTH", self.csr.data_width)
self.add_config("CSR_ALIGNMENT", self.csr.alignment)
def add_cpu(self, name="vexriscv", variant="standard", cls=None, reset_address=None):
if name not in cpu.CPUS.keys():
self.logger.error("{} CPU {}, supporteds: {}.".format(
colorer(name),
colorer("not supported", color="red"),
colorer(", ".join(cpu.CPUS.keys()))))
raise
# Add CPU
if name == "external" and cls is None:
self.logger.error("{} CPU requires {} to be specified.".format(
colorer(name),
colorer("cpu_cls", color="red")))
raise
cpu_cls = cls if cls is not None else cpu.CPUS[name]
if variant not in cpu_cls.variants:
self.logger.error("{} CPU variant {}, supporteds: {}.".format(
colorer(variant),
colorer("not supported", color="red"),
colorer(", ".join(cpu_cls.variants))))
raise
self.submodules.cpu = cpu_cls(self.platform, variant)
# Update SoC with CPU constraints
for n, (origin, size) in enumerate(self.cpu.io_regions.items()):
self.bus.add_region("io{}".format(n), SoCIORegion(origin=origin, size=size, cached=False))
# 14.02.21/KQ removed self.mem_map.update(self.cpu.mem_map) # FIXME
# Add Bus Masters/CSR/IRQs
if not isinstance(self.cpu, (cpu.CPUNone, cpu.Zynq7000)):
if reset_address is None:
reset_address = self.mem_map["rom"]
self.cpu.set_reset_address(reset_address)
for n, cpu_bus in enumerate(self.cpu.periph_buses):
self.bus.add_master(name="cpu_bus{}".format(n), master=cpu_bus)
self.csr.add("cpu", use_loc_if_exists=True)
if hasattr(self.cpu, "interrupt"):
for name, loc in self.cpu.interrupts.items():
self.irq.add(name, loc)
self.add_config("CPU_HAS_INTERRUPT")
# Create optional DMA Bus (for Cache Coherence)
if hasattr(self.cpu, "dma_bus"):
self.submodules.dma_bus = SoCBusHandler(
name = "SoCDMABusHandler",
standard = "wishbone",
data_width = self.bus.data_width,
address_width = self.bus.address_width,
)
dma_bus = wishbone.Interface(data_width=self.bus.data_width)
self.dma_bus.add_slave("dma", slave=dma_bus, region=SoCRegion(origin=0x00000000, size=0x100000000)) # FIXME: covers lower 4GB only
self.submodules += wishbone.Converter(dma_bus, self.cpu.dma_bus)
# Connect SoCController's reset to CPU reset
if hasattr(self, "ctrl"):
if hasattr(self.ctrl, "reset"):
self.comb += self.cpu.reset.eq(self.ctrl.reset)
self.add_config("CPU_RESET_ADDR", reset_address)
# Add constants
self.add_config("CPU_TYPE", str(name))
self.add_config("CPU_VARIANT", str(variant.split('+')[0]))
self.add_constant("CONFIG_CPU_HUMAN_NAME", getattr(self.cpu, "human_name", "Unknown"))
if hasattr(self.cpu, "nop"):
self.add_constant("CONFIG_CPU_NOP", self.cpu.nop)
def add_timer(self, name="timer0"):
self.check_if_exists(name)
setattr(self.submodules, name, Timer())
self.csr.add(name, use_loc_if_exists=True)
if hasattr(self.cpu, "interrupt"):
self.irq.add(name, use_loc_if_exists=True)
# SoC finalization -----------------------------------------------------------------------------
def do_finalize(self):
self.logger.info(colorer("-"*80, color="bright"))
self.logger.info(colorer("Finalized SoC:"))
self.logger.info(colorer("-"*80, color="bright"))
self.logger.info(self.bus)
if hasattr(self, "dma_bus"):
self.logger.info(self.dma_bus)
self.logger.info(self.csr)
self.logger.info(self.irq)
self.logger.info(colorer("-"*80, color="bright"))
interconnect_p2p_cls = {
"wishbone": wishbone.InterconnectPointToPoint,
"axi-lite": axi.AXILiteInterconnectPointToPoint,
}[self.bus.standard]
interconnect_shared_cls = {
"wishbone": wishbone.InterconnectShared,
"axi-lite": axi.AXILiteInterconnectShared,
}[self.bus.standard]
# SoC CSR bridge ---------------------------------------------------------------------------
# FIXME: for now, use registered CSR bridge when SDRAM is present; find the best compromise.
self.add_csr_bridge(self.mem_map["csr"], register=hasattr(self, "sdram"))
# SoC Bus Interconnect ---------------------------------------------------------------------
if len(self.bus.masters) and len(self.bus.slaves):
# If 1 bus_master, 1 bus_slave and no address translation, use InterconnectPointToPoint.
if ((len(self.bus.masters) == 1) and
(len(self.bus.slaves) == 1) and
(next(iter(self.bus.regions.values())).origin == 0)):
self.submodules.bus_interconnect = interconnect_p2p_cls(
master = next(iter(self.bus.masters.values())),
slave = next(iter(self.bus.slaves.values())))
# Otherwise, use InterconnectShared.
else:
self.submodules.bus_interconnect = interconnect_shared_cls(
masters = self.bus.masters.values(),
slaves = [(self.bus.regions[n].decoder(self.bus), s) for n, s in self.bus.slaves.items()],
register = True,
timeout_cycles = self.bus.timeout)
if hasattr(self, "ctrl") and self.bus.timeout is not None:
if hasattr(self.ctrl, "bus_error"):
self.comb += self.ctrl.bus_error.eq(self.bus_interconnect.timeout.error)
self.bus.logger.info("Interconnect: {} ({} <-> {}).".format(
colorer(self.bus_interconnect.__class__.__name__),
colorer(len(self.bus.masters)),
colorer(len(self.bus.slaves))))
self.add_constant("CONFIG_BUS_STANDARD", self.bus.standard.upper())
self.add_constant("CONFIG_BUS_DATA_WIDTH", self.bus.data_width)
self.add_constant("CONFIG_BUS_ADDRESS_WIDTH", self.bus.address_width)
# SoC DMA Bus Interconnect (Cache Coherence) -----------------------------------------------
if hasattr(self, "dma_bus"):
if len(self.dma_bus.masters) and len(self.dma_bus.slaves):
# If 1 bus_master, 1 bus_slave and no address translation, use InterconnectPointToPoint.
if ((len(self.dma_bus.masters) == 1) and
(len(self.dma_bus.slaves) == 1) and
(next(iter(self.dma_bus.regions.values())).origin == 0)):
self.submodules.dma_bus_interconnect = wishbone.InterconnectPointToPoint(
master = next(iter(self.dma_bus.masters.values())),
slave = next(iter(self.dma_bus.slaves.values())))
# Otherwise, use InterconnectShared.
else:
self.submodules.dma_bus_interconnect = wishbone.InterconnectShared(
masters = self.dma_bus.masters.values(),
slaves = [(self.dma_bus.regions[n].decoder(self.dma_bus), s) for n, s in self.dma_bus.slaves.items()],
register = True)
self.bus.logger.info("DMA Interconnect: {} ({} <-> {}).".format(
colorer(self.dma_bus_interconnect.__class__.__name__),
colorer(len(self.dma_bus.masters)),
colorer(len(self.dma_bus.slaves))))
self.add_constant("CONFIG_CPU_HAS_DMA_BUS")
# SoC CSR Interconnect ---------------------------------------------------------------------
self.submodules.csr_bankarray = csr_bus.CSRBankArray(self,
address_map = self.csr.address_map,
data_width = self.csr.data_width,
address_width = self.csr.address_width,
alignment = self.csr.alignment,
paging = self.csr.paging,
ordering = self.csr.ordering,
soc_bus_data_width = self.bus.data_width)
if len(self.csr.masters):
self.submodules.csr_interconnect = csr_bus.InterconnectShared(
masters = list(self.csr.masters.values()),
slaves = self.csr_bankarray.get_buses())
# Add CSRs regions
for name, csrs, mapaddr, rmap in self.csr_bankarray.banks:
self.csr.add_region(name, SoCCSRRegion(
origin = (self.bus.regions["csr"].origin + self.csr.paging*mapaddr),
busword = self.csr.data_width,
obj = csrs))
# Add Memory regions
for name, memory, mapaddr, mmap in self.csr_bankarray.srams:
self.csr.add_region(name + "_" + memory.name_override, SoCCSRRegion(
origin = (self.bus.regions["csr"].origin + self.csr.paging*mapaddr),
busword = self.csr.data_width,
obj = memory))
# Sort CSR regions by origin
self.csr.regions = {k: v for k, v in sorted(self.csr.regions.items(), key=lambda item: item[1].origin)}
# Add CSRs / Config items to constants
for name, constant in self.csr_bankarray.constants:
self.add_constant(name + "_" + constant.name, constant.value.value)
# SoC CPU Check ----------------------------------------------------------------------------
if not isinstance(self.cpu, (cpu.CPUNone, cpu.Zynq7000)):
if "sram" not in self.bus.regions.keys():
self.logger.error("CPU needs {} Region to be {} as Bus or Linker Region.".format(
colorer("sram"),
colorer("defined", color="red")))
self.logger.error(self.bus)
raise
cpu_reset_address_valid = False
for name, container in self.bus.regions.items():
if self.bus.check_region_is_in(
region = SoCRegion(origin=self.cpu.reset_address, size=self.bus.data_width//8),
container = container):
cpu_reset_address_valid = True
if name == "rom":
self.cpu.use_rom = True
if not cpu_reset_address_valid:
self.logger.error("CPU needs {} to be in a {} Region.".format(
colorer("reset address 0x{:08x}".format(self.cpu.reset_address)),
colorer("defined", color="red")))
self.logger.error(self.bus)
raise
# SoC IRQ Interconnect ---------------------------------------------------------------------
if hasattr(self, "cpu"):
if hasattr(self.cpu, "interrupt"):
for name, loc in sorted(self.irq.locs.items()):
if name in self.cpu.interrupts.keys():
continue
if hasattr(self, name):
module = getattr(self, name)
if not hasattr(module, "ev"):
self.logger.error("EventManager {} in {} SubModule.".format(
colorer("not found", color="red"),
colorer(name)))
raise
self.comb += self.cpu.interrupt[loc].eq(module.ev.irq)
self.add_constant(name + "_INTERRUPT", loc)
# SoC build ------------------------------------------------------------------------------------
def build(self, *args, **kwargs):
self.build_name = kwargs.pop("build_name", self.platform.name)
kwargs.update({"build_name": self.build_name})
return self.platform.build(self, *args, **kwargs)
# LiteXSoC -----------------------------------------------------------------------------------------
class LiteXSoC(SoC):
# Add Identifier -------------------------------------------------------------------------------
def add_identifier(self, name="identifier", identifier="LiteX SoC", with_build_time=True):
self.check_if_exists(name)
if with_build_time:
identifier += " " + build_time()
setattr(self.submodules, name, Identifier(identifier))
self.csr.add(name + "_mem", use_loc_if_exists=True)
# Add UART -------------------------------------------------------------------------------------
def add_uart(self, name, baudrate=115200, fifo_depth=16):
from litex.soc.cores import uart
# Stub / Stream
if name in ["stub", "stream"]:
self.submodules.uart = uart.UART(tx_fifo_depth=0, rx_fifo_depth=0)
if name == "stub":
self.comb += self.uart.sink.ready.eq(1)
# UARTBone / Bridge
elif name in ["uartbone", "bridge"]: