You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
264 lines
10 KiB
Python
264 lines
10 KiB
Python
3 years ago
|
#!/usr/bin/env python3
|
||
|
|
||
|
# Based on valentyusb/sim/generate_verilog.py , modified
|
||
|
# for Microwatt
|
||
|
|
||
|
# This variable defines all the external programs that this module
|
||
|
# relies on. lxbuildenv reads this variable in order to ensure
|
||
|
# the build will finish without exiting due to missing third-party
|
||
|
# programs.
|
||
|
LX_DEPENDENCIES = []
|
||
|
|
||
|
# Import lxbuildenv to integrate the deps/ directory
|
||
|
#import lxbuildenv
|
||
|
|
||
|
# Disable pylint's E1101, which breaks completely on migen
|
||
|
#pylint:disable=E1101
|
||
|
|
||
|
import argparse
|
||
|
import os
|
||
|
import yaml
|
||
|
|
||
|
#from migen import *
|
||
|
from migen import Module, Signal, Instance, ClockDomain, If
|
||
|
from migen.genlib.resetsync import AsyncResetSynchronizer
|
||
|
from migen.fhdl.specials import TSTriple
|
||
|
from migen.fhdl.bitcontainer import bits_for
|
||
|
from migen.fhdl.structure import ClockSignal, ResetSignal, Replicate, Cat
|
||
|
|
||
|
# from litex.build.sim.platform import SimPlatform
|
||
|
from litex.build.lattice import LatticePlatform
|
||
|
from litex.build.generic_platform import Pins, IOStandard, Misc, Subsignal
|
||
|
from litex.soc.integration.soc_core import SoCCore
|
||
|
from litex.soc.integration.builder import Builder
|
||
|
from litex.soc.interconnect import wishbone
|
||
|
from litex.soc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage
|
||
|
|
||
|
from valentyusb import usbcore
|
||
|
from valentyusb.usbcore import io as usbio
|
||
|
from valentyusb.usbcore.cpu import dummyusb, cdc_eptri, eptri, epfifo
|
||
|
from valentyusb.usbcore.endpoint import EndpointType
|
||
|
|
||
|
_connectors = []
|
||
|
|
||
|
class _CRG(Module):
|
||
|
def __init__(self, platform):
|
||
|
clk = platform.request("clk")
|
||
|
rst = platform.request("reset")
|
||
|
|
||
|
clk12 = Signal()
|
||
|
|
||
|
self.clock_domains.cd_sys = ClockDomain()
|
||
|
self.clock_domains.cd_usb_12 = ClockDomain()
|
||
|
self.clock_domains.cd_usb_48 = ClockDomain()
|
||
|
self.clock_domains.cd_usb_48_to_12 = ClockDomain()
|
||
|
|
||
|
clk48 = clk.clk48
|
||
|
|
||
|
self.comb += self.cd_usb_48.clk.eq(clk48)
|
||
|
self.comb += self.cd_usb_48_to_12.clk.eq(clk48)
|
||
|
|
||
|
clk12_counter = Signal(2)
|
||
|
self.sync.usb_48_to_12 += clk12_counter.eq(clk12_counter + 1)
|
||
|
|
||
|
self.comb += clk12.eq(clk12_counter[1])
|
||
|
|
||
|
self.comb += self.cd_sys.clk.eq(clk.clksys)
|
||
|
self.comb += self.cd_usb_12.clk.eq(clk12)
|
||
|
|
||
|
self.comb += [
|
||
|
ResetSignal("sys").eq(rst),
|
||
|
ResetSignal("usb_12").eq(rst),
|
||
|
ResetSignal("usb_48").eq(rst),
|
||
|
]
|
||
|
|
||
|
class BaseSoC(SoCCore):
|
||
|
|
||
|
def __init__(self, platform, io, sys_freq, output_dir="build", usb_variant='dummy', **kwargs):
|
||
|
# Disable integrated RAM as we'll add it later
|
||
|
self.integrated_sram_size = 0
|
||
|
|
||
|
self.output_dir = output_dir
|
||
|
|
||
|
platform.add_extension(io)
|
||
|
|
||
|
self.submodules.crg = _CRG(platform)
|
||
|
|
||
|
# prior to SocCore.__init__
|
||
|
self.csr_map = {
|
||
|
"uart": 0, # microwatt soc will remap addresses to 0
|
||
|
}
|
||
|
|
||
|
SoCCore.__init__(self, platform, sys_freq,
|
||
|
cpu_type=None,
|
||
|
integrated_rom_size=0x0,
|
||
|
integrated_sram_size=0x0,
|
||
|
integrated_main_ram_size=0x0,
|
||
|
csr_address_width=14, csr_data_width=32,
|
||
|
with_uart=False, with_timer=False)
|
||
|
|
||
|
# Add USB pads
|
||
|
usb_pads = platform.request("usb")
|
||
|
usb_iobuf = usbio.IoBuf(usb_pads.d_p, usb_pads.d_n, usb_pads.pullup)
|
||
|
self.comb += usb_pads.tx_en.eq(usb_iobuf.usb_tx_en)
|
||
|
if usb_variant == 'eptri':
|
||
|
self.submodules.usb = eptri.TriEndpointInterface(usb_iobuf, debug=True)
|
||
|
elif usb_variant == 'epfifo':
|
||
|
self.submodules.usb = epfifo.PerEndpointFifoInterface(usb_iobuf, debug=True)
|
||
|
elif usb_variant == 'cdc_eptri':
|
||
|
extra_args = {}
|
||
|
passthrough = ['product', 'manufacturer']
|
||
|
for p in passthrough:
|
||
|
try:
|
||
|
extra_args[p] = kwargs[p]
|
||
|
except KeyError:
|
||
|
pass
|
||
|
self.submodules.uart = cdc_eptri.CDCUsb(usb_iobuf, debug=True, **extra_args)
|
||
|
elif usb_variant == 'dummy':
|
||
|
self.submodules.usb = dummyusb.DummyUsb(usb_iobuf, debug=True)
|
||
|
else:
|
||
|
raise ValueError('Invalid endpoints value. It is currently \'eptri\' and \'dummy\'')
|
||
|
try:
|
||
|
self.add_wb_master(self.usb.debug_bridge.wishbone)
|
||
|
except AttributeError:
|
||
|
pass
|
||
|
|
||
|
if self.uart:
|
||
|
self.comb += self.platform.request("interrupt").eq(self.uart.ev.irq)
|
||
|
|
||
|
wb_ctrl = wishbone.Interface()
|
||
|
self.add_wb_master(wb_ctrl)
|
||
|
platform.add_extension(wb_ctrl.get_ios("wb_ctrl"))
|
||
|
self.comb += wb_ctrl.connect_to_pads(self.platform.request("wishbone"), mode="slave")
|
||
|
|
||
|
def add_fsm_state_names():
|
||
|
"""Hack the FSM module to add state names to the output"""
|
||
|
from migen.fhdl.visit import NodeTransformer
|
||
|
from migen.genlib.fsm import NextState, NextValue, _target_eq
|
||
|
from migen.fhdl.bitcontainer import value_bits_sign
|
||
|
|
||
|
class My_LowerNext(NodeTransformer):
|
||
|
def __init__(self, next_state_signal, next_state_name_signal, encoding, aliases):
|
||
|
self.next_state_signal = next_state_signal
|
||
|
self.next_state_name_signal = next_state_name_signal
|
||
|
self.encoding = encoding
|
||
|
self.aliases = aliases
|
||
|
# (target, next_value_ce, next_value)
|
||
|
self.registers = []
|
||
|
|
||
|
def _get_register_control(self, target):
|
||
|
for x in self.registers:
|
||
|
if _target_eq(target, x[0]):
|
||
|
return x[1], x[2]
|
||
|
raise KeyError
|
||
|
|
||
|
def visit_unknown(self, node):
|
||
|
if isinstance(node, NextState):
|
||
|
try:
|
||
|
actual_state = self.aliases[node.state]
|
||
|
except KeyError:
|
||
|
actual_state = node.state
|
||
|
return [
|
||
|
self.next_state_signal.eq(self.encoding[actual_state]),
|
||
|
self.next_state_name_signal.eq(int.from_bytes(actual_state.encode(), byteorder="big"))
|
||
|
]
|
||
|
elif isinstance(node, NextValue):
|
||
|
try:
|
||
|
next_value_ce, next_value = self._get_register_control(node.target)
|
||
|
except KeyError:
|
||
|
related = node.target if isinstance(node.target, Signal) else None
|
||
|
next_value = Signal(bits_sign=value_bits_sign(node.target), related=related)
|
||
|
next_value_ce = Signal(related=related)
|
||
|
self.registers.append((node.target, next_value_ce, next_value))
|
||
|
return next_value.eq(node.value), next_value_ce.eq(1)
|
||
|
else:
|
||
|
return node
|
||
|
import migen.genlib.fsm as fsm
|
||
|
def my_lower_controls(self):
|
||
|
self.state_name = Signal(len(max(self.encoding,key=len))*8, reset=int.from_bytes(self.reset_state.encode(), byteorder="big"))
|
||
|
self.next_state_name = Signal(len(max(self.encoding,key=len))*8, reset=int.from_bytes(self.reset_state.encode(), byteorder="big"))
|
||
|
self.comb += self.next_state_name.eq(self.state_name)
|
||
|
self.sync += self.state_name.eq(self.next_state_name)
|
||
|
return My_LowerNext(self.next_state, self.next_state_name, self.encoding, self.state_aliases)
|
||
|
fsm.FSM._lower_controls = my_lower_controls
|
||
|
|
||
|
|
||
|
_io = [
|
||
|
# Wishbone
|
||
|
("wishbone", 0,
|
||
|
Subsignal("adr", Pins(30)),
|
||
|
Subsignal("dat_r", Pins(32)),
|
||
|
Subsignal("dat_w", Pins(32)),
|
||
|
Subsignal("sel", Pins(4)),
|
||
|
Subsignal("cyc", Pins(1)),
|
||
|
Subsignal("stb", Pins(1)),
|
||
|
Subsignal("ack", Pins(1)),
|
||
|
Subsignal("we", Pins(1)),
|
||
|
Subsignal("cti", Pins(3)),
|
||
|
Subsignal("bte", Pins(2)),
|
||
|
Subsignal("err", Pins(1))
|
||
|
),
|
||
|
("usb", 0,
|
||
|
Subsignal("d_p", Pins(1)),
|
||
|
Subsignal("d_n", Pins(1)),
|
||
|
Subsignal("pullup", Pins(1)),
|
||
|
Subsignal("tx_en", Pins(1)),
|
||
|
),
|
||
|
("clk", 0,
|
||
|
Subsignal("clk48", Pins(1)),
|
||
|
Subsignal("clksys", Pins(1)),
|
||
|
),
|
||
|
("interrupt", 0, Pins(1)),
|
||
|
("reset", 0, Pins(1)),
|
||
|
]
|
||
|
|
||
|
def generate(core_config, output_dir, csr_csv):
|
||
|
|
||
|
toolchain = core_config["toolchain"]
|
||
|
if toolchain == "trellis":
|
||
|
platform = LatticePlatform(core_config["device"], [], toolchain=toolchain)
|
||
|
else:
|
||
|
raise ValueError(f"Unknown config toolchain {toolchain}")
|
||
|
|
||
|
soc = BaseSoC(platform, _io, core_config["sys_freq"],
|
||
|
usb_variant=core_config["usb_variant"],
|
||
|
cpu_type=None, cpu_variant=None,
|
||
|
output_dir=output_dir,
|
||
|
product=core_config["product"],
|
||
|
manufacturer="Microwatt")
|
||
|
builder = Builder(soc, output_dir=output_dir,
|
||
|
csr_csv=csr_csv,
|
||
|
compile_software=False)
|
||
|
vns = builder.build(run=False, build_name='valentyusb')
|
||
|
soc.do_exit(vns)
|
||
|
|
||
|
def main():
|
||
|
parser = argparse.ArgumentParser(description="Build standalone ValentyUSB verilog output")
|
||
|
# parser.add_argument('variant', metavar='VARIANT',
|
||
|
# choices=['dummy', 'cdc_eptri', 'eptri', 'epfifo'],
|
||
|
# default='dummy',
|
||
|
# help='USB variant. Choices: [%(choices)s] (default: %(default)s)' )
|
||
|
parser.add_argument('--dir', metavar='DIRECTORY',
|
||
|
default='build',
|
||
|
help='Output directory (default: %(default)s)' )
|
||
|
parser.add_argument('--csr', metavar='CSR',
|
||
|
default='csr.csv',
|
||
|
help='csr file (default: %(default)s)')
|
||
|
parser.add_argument('config', type=argparse.FileType('r'),
|
||
|
help='Input platform config file')
|
||
|
args = parser.parse_args()
|
||
|
|
||
|
core_config = yaml.load(args.config.read(), Loader=yaml.Loader)
|
||
|
# XXX matt - not sure if this needed, maybe only for sim target?
|
||
|
# add_fsm_state_names()
|
||
|
output_dir = args.dir
|
||
|
generate(core_config, output_dir, args.csr)
|
||
|
|
||
|
print(
|
||
|
"""Build complete. Output files:
|
||
|
{}/gateware/valentyusb.v Source Verilog file.
|
||
|
""".format(output_dir))
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
main()
|