An RV32IMF implementation w/ migen/LiteX
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
Risq5/risq5_wo_vex.py

354 lines
17 KiB

#!/usr/bin/env python3
#
# risq5.py
#
# This file has been derived from LiteX-Boards/colorlight_5b_75x.py
# Copyright (c) 2020 Florent Kermarrec <florent@enjoy-digital.fr>
# SPDX-License-Identifier: BSD-2-Clause
#
# Disclaimer: Still a proof of concept with large timings violations on the IP/UDP and
# Etherbone stack that need to be optimized. It was initially just used to validate the reversed
# pinout but happens to work on hardware...
#
# History:
# --------
# 22.12.20/KQ Inital version
#
# Build/Use ----------------------------------------------------------------------------------------
# - 'python3 risq5.py --build --revision=7.0 --uart-name=crossover --with-etherbone --ip-address=192.168.1.20 --csr-csv=build/csr.csv'
# to generate
# - 'python3 risq5.py --load' to download to FPGA
# - 'ping 192.168.1.20' to verify ethernet connection - via LEFT(!) RJ45 port
# - 'wishbone-tool --ethernet-host 192.168.1.20 --server terminal --csr-csv build/csr.csv'
# You should see the LiteX BIOS and be able to interact with it
# - To load a file to RAM (@0x40000000 len=0x400000) use:
# wishbone-tool --ethernet-host 192.168.1.20 --server load-file --csr-csv build/csr.csv
# --load-address 0x40000000
# --load-name build/colorlight_5a_75b/software/<filename>
# To disassemble raw file:
# ../fpga/litex/riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14/bin/riscv64-unknown-elf-objdump
# -D -b binary ./build/colorlight_5a_75b/software/bios/bios.bin -m riscv
#
import os
import argparse
import sys
import time
from migen import *
from migen.genlib.misc import WaitTimer
from migen.genlib.resetsync import AsyncResetSynchronizer
from litex.build.io import DDROutput
from litex_boards.platforms import colorlight_5a_75b
from litex.build.lattice.trellis import trellis_args, trellis_argdict
from litex.soc.cores.clock import *
from litex.soc.cores.spi_flash import ECP5SPIFlash
from litex.soc.cores.gpio import GPIOOut
from litex.soc.integration.soc_core import *
from litex.soc.integration.builder import *
from litex.soc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage, CSRField
from litex.soc.interconnect.stream import SyncFIFO
from litedram.modules import M12L16161A, M12L64322A
from litedram.phy import GENSDRPHY, HalfRateGENSDRPHY
from litedram.frontend.dma import LiteDRAMDMAReader, LiteDRAMDMAWriter
from liteeth.phy.ecp5rgmii import LiteEthPHYRGMII
from litex.build.generic_platform import *
import litex.soc.doc as lxsocdoc
from libmodules.dramtransfer import DRAMTransfer, FPGA2DRAM
from libmodules.systime import Clint, SysTime
from libmodules.core import Risq5Core
from helpers.prepare_firmware import copyjob
# ISA extensions info: --ZY XWVU TSRQ PONM LKJI HGFE DCBA
# I (base integer) 0000 0000 0000 0000 0001 0000 0000
# IM (+ multiply) 0000 0000 0000 0001 0001 0000 0000
isa_extensions = 0x40000100 # Base integer ISA by default
# CRG ----------------------------------------------------------------------------------------------
class _CRG(Module):
def __init__(self, platform, sys_clk_freq, use_internal_osc=False, with_usb_pll=False, with_rst=True, sdram_rate="1:1"):
self.clock_domains.cd_sys = ClockDomain()
if sdram_rate == "1:2":
self.clock_domains.cd_sys2x = ClockDomain()
self.clock_domains.cd_sys2x_ps = ClockDomain(reset_less=True)
else:
self.clock_domains.cd_sys_ps = ClockDomain(reset_less=True)
# # #
# Clk / Rst
if not use_internal_osc:
clk = platform.request("clk25")
clk_freq = 25e6
else:
clk = Signal()
div = 5
self.specials += Instance("OSCG",
p_DIV = div,
o_OSC = clk)
clk_freq = 310e6/div
rst_n = 1 if not with_rst else platform.request("user_btn_n", 0)
# PLL
self.submodules.pll = pll = ECP5PLL()
self.comb += pll.reset.eq(~rst_n)
pll.register_clkin(clk, clk_freq)
pll.create_clkout(self.cd_sys, sys_clk_freq)
if sdram_rate == "1:2":
pll.create_clkout(self.cd_sys2x, 2*sys_clk_freq)
pll.create_clkout(self.cd_sys2x_ps, 2*sys_clk_freq, phase=180) # Idealy 90° but needs to be increased.
else:
pll.create_clkout(self.cd_sys_ps, sys_clk_freq, phase=180) # Idealy 90° but needs to be increased.
# USB PLL
if with_usb_pll:
self.submodules.usb_pll = usb_pll = ECP5PLL()
self.comb += usb_pll.reset.eq(~rst_n)
usb_pll.register_clkin(clk, clk_freq)
self.clock_domains.cd_usb_12 = ClockDomain()
self.clock_domains.cd_usb_48 = ClockDomain()
usb_pll.create_clkout(self.cd_usb_12, 12e6, margin=0)
usb_pll.create_clkout(self.cd_usb_48, 48e6, margin=0)
# SDRAM clock
sdram_clk = ClockSignal("sys2x_ps" if sdram_rate == "1:2" else "sys_ps")
self.specials += DDROutput(1, 0, platform.request("sdram_clock"), sdram_clk)
# BaseSoC ------------------------------------------------------------------------------------------
class BaseSoC(SoCCore):
def __init__(self, board, revision, with_ethernet=False, with_etherbone=False, eth_phy=0, ip_address=None, mac_address=None, sys_clk_freq=60e6, use_internal_osc=False, sdram_rate="1:1", with_bios_for_none=False, **kwargs):
platform = colorlight_5a_75b.Platform(revision="7.0")
# SoCCore ----------------------------------------------------------------------------------
# Memory map update for 'None' cpu removed (CSR area remains!)
SoCCore.__init__(self, platform, int(sys_clk_freq),
ident = "LiteX SoC on Colorlight " + board.upper(),
ident_version = True,
with_bios_for_none = with_bios_for_none,
**kwargs)
# CRG --------------------------------------------------------------------------------------
with_rst = kwargs["uart_name"] not in ["serial", "bridge"] # serial_rx shared with user_btn_n.
with_usb_pll = kwargs.get("uart_name", None) == "usb_acm"
self.submodules.crg = _CRG(platform, sys_clk_freq, use_internal_osc=use_internal_osc, with_usb_pll=with_usb_pll,with_rst=with_rst, sdram_rate=sdram_rate)
# SDR SDRAM --------------------------------------------------------------------------------
if not self.integrated_main_ram_size:
sdrphy_cls = HalfRateGENSDRPHY if sdram_rate == "1:2" else GENSDRPHY
self.submodules.sdrphy = sdrphy_cls(platform.request("sdram"))
if board == "5a-75e" and revision == "6.0":
sdram_cls = M12L64322A
sdram_size = 0x80000000
else:
sdram_cls = M12L16161A
sdram_size = 0x40000000
self.add_sdram("sdram",
phy = self.sdrphy,
module = sdram_cls(sys_clk_freq, sdram_rate),
origin = self.mem_map["main_ram"],
size = kwargs.get("max_sdram_size", sdram_size),
l2_cache_size = kwargs.get("l2_size", 8192),
l2_cache_min_data_width = kwargs.get("min_l2_data_width", 128),
l2_cache_reverse = True
)
# Ethernet / Etherbone ---------------------------------------------------------------------
if with_ethernet or with_etherbone:
self.submodules.ethphy = LiteEthPHYRGMII(
clock_pads = self.platform.request("eth_clocks", eth_phy),
pads = self.platform.request("eth", eth_phy))
self.add_csr("ethphy")
if with_ethernet:
self.add_ethernet(phy=self.ethphy)
if with_etherbone:
self.add_etherbone(
phy=self.ethphy,
ip_address = ip_address,
mac_address = mac_address,
)
""" FIXME: Added 2nd master to csr (after master0)
##from litex.soc.interconnect import wishbone
##self.csr.add_master(name="cpu2csr", master=wishbone.Interface(8,14))
#from litex.soc.interconnect import wishbone
#self.cpu2csr = cpu2csr = wishbone.Interface()
# Signals: ack, adr(14), bte(2), cti(3), cyc, dat_r(8), dat_w(8), err, sel, stb, we
# _do_transaction, data_width8, adr_width=14
# But for (etherbone) 'bridge' only these are used:
# Signals: adr(14), dat_r(8), dat_w(8), we
# data_width8, adr_width=14
#self.add_wb_master(self.etherbone.bus) # add_csr_bridge(self.mem_map["csr"], False) # fails
"""
# System time (count)
self.submodules.systime = systime = SysTime(comparecount=0x0000EA90)
self.add_csr("systime")
# Use ctrl.scratch.storage 32-bit (=0x12345678) for testing!
# DRAM test
MAXWORDS = 32 # Transfer length 32 x 32-bit, FIFO depth (511 L1 cache currently possible = 9-bit!)
MAXMEMWORDS = 4 # Transfer length 4 x 32-bit = 16 byte (LU cache)
MAXWRITEWORDS = 1 # Transfer length 1 x 32-bit = 4 byte maximum (SU)
# Instruction memory access
self.submodules.mm2s = mm2s = LiteDRAMDMAReader(self.sdram.crossbar.get_port(), fifo_depth=MAXWORDS, fifo_buffered=True)
mm2s.add_csr()
self.add_csr("mm2s")
# Load unit memory access
self.submodules.mm2s2 = mm2s2 = LiteDRAMDMAReader(self.sdram.crossbar.get_port(), fifo_depth=MAXMEMWORDS, fifo_buffered=True)
mm2s2.add_csr()
self.add_csr("mm2s2")
# Store unit memory access
self.submodules.s2mm2 = s2mm2 = LiteDRAMDMAWriter(self.sdram.crossbar.get_port(), fifo_depth=MAXWRITEWORDS, fifo_buffered=True)
s2mm2.add_csr()
self.add_csr("s2mm2")
# Instruction cache transfer
# 32 * 32bit = 32 * 4 bytes => 128 bytes (to fill queue)
self.submodules.sync_fifo = sync_fifo = SyncFIFO([("data", 32)], MAXWORDS, True)
# Queue is temporary/loop storage 'on' FPGA: DRAM->DMA-Reader->FIFO(in)|FIFO(out)->DMA-Writer->DRAM
self.comb += mm2s.source.connect(sync_fifo.sink) # Connect DMA-Reader.source -> FIFO.sink
# Load unit (LU) transfer
# 4 * 32bit = 4 * 4 bytes => 16 bytes (to fill queue)
self.submodules.sync_fifo2 = sync_fifo2 = SyncFIFO([("data", 32)], MAXMEMWORDS, True)
# Queue is temporary/loop storage 'on' FPGA: DRAM->DMA-Reader->FIFO(in)|FIFO(out)->DMA-Writer->DRAM
self.comb += mm2s2.source.connect(sync_fifo2.sink) # Connect DMA-Reader.source -> FIFO.sink
# Store unit (SU) transfer
self.submodules.sync_fifo3 = sync_fifo3 = SyncFIFO([("data", 32)], MAXWRITEWORDS, True)
self.comb += sync_fifo3.source.connect(s2mm2.sink) # Connect FIFO.source -> DMA-Writer.sink
# Instruction transfer
self.submodules.dramtransfer = dramtransfer = DRAMTransfer(maxwords=MAXWORDS, dma_reader=mm2s, sync_fifo=sync_fifo)
self.add_csr("dramtransfer")
# Load unit (LU) transfer
self.submodules.dramtransfer2 = dramtransfer2 = DRAMTransfer(maxwords=MAXMEMWORDS, dma_reader=mm2s2, sync_fifo=sync_fifo2)
self.add_csr("dramtransfer2")
# Store unit (SU) transfer
self.submodules.fpga2dram = fpga2dram = FPGA2DRAM(dma_writer=s2mm2, sync_fifo=sync_fifo3)
self.add_csr("fpga2dram")
# CLINT mtime/mtimecmp memory mapped 64-bit timers (acc. to RISC-V spec.)
self.submodules.clint = clint = Clint(comparecount=0x0000EA90)
self.add_csr("clint")
# Integrate my RISC-V cpu clone
global isa_extensions
RAMWAITTIME=128 # 32-bit max. for testing ... (TODO: 8-bit 128 seems to work as well)
LUWAITTIME=24
self.submodules.risq5ext = risq5ext = Risq5Core(
RAMWaitTime=RAMWAITTIME,
LU_CacheWait=LUWAITTIME,
L1CacheSize=MAXWORDS,
L1Cache=dramtransfer,
LUCacheSize=MAXMEMWORDS,
LUCache=dramtransfer2,
SU_Unit=fpga2dram,
clint=clint,
isa_extensions=isa_extensions # Custom value from global var
)
self.add_csr("risq5ext")
# USERLED (on-board LED) used for debugging
if kwargs["uart_name"] not in ["serial", "bridge"]: # only w/ uart-name=crossover option:
self.comb += platform.request("user_led_n").eq(~(risq5ext.b32mode.storage[31]))
# Build --------------------------------------------------------------------------------------------
def main():
parser = argparse.ArgumentParser(description="LiteX SoC on Colorlight 5A-75X")
builder_args(parser)
soc_core_args(parser)
trellis_args(parser)
parser.add_argument("--build", action="store_true", help="Build bitstream")
parser.add_argument("--load", action="store_true", help="Load bitstream")
parser.add_argument("--board", default="5a-75b", help="Board type: 5a-75b (default) & don't change!")
parser.add_argument("--revision", default="7.0", type=str, help="Board revision 7.0 (default) & don't change!")
parser.add_argument("--with-ethernet", action="store_true", help="Enable Ethernet support")
parser.add_argument("--with-etherbone", action="store_true", help="Enable Etherbone support")
parser.add_argument("--eth-phy", default=0, type=int, help="Ethernet PHY 0 or 1 (default=0)")
parser.add_argument("--ip-address", default="192.168.1.50", help="Ethernet IP address of the board.")
parser.add_argument("--mac-address", default="0x726b895bc2e2", help="Ethernet MAC address of the board.")
parser.add_argument("--sys-clk-freq", default=60e6, type=float, help="System clock frequency (default=60MHz)")
parser.add_argument("--use-internal-osc", action="store_true", help="Use internal oscillator")
parser.add_argument("--sdram-rate", default="1:1", help="SDRAM Rate 1:1 Full Rate (default), 1:2 Half Rate")
parser.add_argument("--csr_csv", default="build/csr.csv", help="CSR list location")
parser.add_argument("--doc", action="store_true", help="Create doc files for sphinx generator")
parser.add_argument("--isa-extension-m", action="store_true", help="RISC-V extension M")
parser.add_argument("--with-bios-for-none", action="store_true", help="Create ROM w/ BIOS even w/o CPU")
parser.add_argument("--flash", action="store_true", help="Load bitstream to flash")
args = parser.parse_args()
# Custom attribute addition
if args.isa_extension_m:
global isa_extensions # RISC-V extensions
isa_extensions |= 0x00001000 # M extension
#assert not (args.with_ethernet and args.with_etherbone)
soc = BaseSoC(board=args.board, revision=args.revision,
with_ethernet = args.with_ethernet,
with_etherbone = args.with_etherbone,
eth_phy = args.eth_phy,
ip_address = args.ip_address,
mac_address = int(args.mac_address, 0),
sys_clk_freq = args.sys_clk_freq,
use_internal_osc = args.use_internal_osc,
sdram_rate = args.sdram_rate,
**soc_core_argdict(args))
# 32MBit SPIFlash ------------------------------------------------------------------------
flashbase = 0xc0000000
flashoffset = 0x100000 # Used to be zero (default)
soc.mem_map["spiflash"] = flashbase # Length: 0x01000000 ('til 0xc1000000 - 1)
# Boot at +1MB
soc.add_constant("FLASH_BOOT_ADDRESS", soc.mem_map["spiflash"] + 1024*1024) # 0xc0100000
soc.add_spi_flash(name="spiflash", mode="1x", dummy_cycles=8, clk_freq=5e6)
builder = Builder(soc, **builder_argdict(args))
# Now override boot address (used to be zero/default)
args.ecppack_bootaddr = flashbase + flashoffset # 0xC0100000
if not args.with_bios_for_none: # 26.02.21/KQ Added
builder.compile_software = False # Cut off BIOS integration
builder.build(**trellis_argdict(args), run=args.build) # Written here to (local) build tree
if args.doc:
print("Generating documentation for sphinx ...")
lxsocdoc.generate_docs(soc, "build/documentation/", project_name="neopixelar", author="KQ")
print("Generate via: 'sphinx-build -b html build/documentation build/documentation/html'")
if args.load:
prog = soc.platform.create_programmer()
prog.load_bitstream(os.path.join(builder.gateware_dir, soc.build_name + ".svf"))
return
if args.flash: # Convert Bit File to Jtag Write Flash command
name = os.path.join(builder.gateware_dir, soc.build_name)
print(f"Executing ./bit_to_flash.py {name}.bit {name}.svf.flash")
from helpers.bit_to_flash import convertBitToFlashFile
convertBitToFlashFile(name + ".bit", name + ".svf.flash", address=0)
from helpers.load_to_flash import load2flash
load2flash(name + ".svf.flash")
return
if __name__ == "__main__":
starttime = time.time()
copyjob() # Create backup if nec. & move our firmware to the correct location
main() # Create FPGA & load/flash
print("Time used: {0} min.".format(int((time.time() - starttime)/60.0)))