refactor compiles

This commit is contained in:
Peter McGoron 2023-06-28 18:49:26 -04:00
parent 054609a459
commit cf95a0fd20
5 changed files with 207 additions and 270 deletions

View File

@ -49,7 +49,6 @@ hardware-get:
docker cp upsilon-hardware:/home/user/upsilon/gateware/arty.dtb ../boot/ docker cp upsilon-hardware:/home/user/upsilon/gateware/arty.dtb ../boot/
docker cp upsilon-hardware:/home/user/upsilon/gateware/mmio.py ../boot/ docker cp upsilon-hardware:/home/user/upsilon/gateware/mmio.py ../boot/
docker cp upsilon-hardware:/home/user/upsilon/gateware/csr.json ../boot/ docker cp upsilon-hardware:/home/user/upsilon/gateware/csr.json ../boot/
docker cp upsilon-hardware:/home/user/upsilon/gateware/csr_bitwidth.json ../boot/
hardware-clean: hardware-clean:
-docker container stop upsilon-hardware -docker container stop upsilon-hardware
-docker container rm upsilon-hardware -docker container rm upsilon-hardware

View File

@ -15,59 +15,23 @@ import collections
import argparse import argparse
import json import json
import sys import sys
import mmio_descr
class MMIORegister:
def __init__(self, name, read_only=False, number=1, exntype=None):
"""
Describes a MMIO register.
:param name: The name of the MMIO register, excluding the prefix
defining its module (i.e. ``base_``) and excluding its
numerical suffix.
:param read_only: True if the register is read only. Defaults to
``False``.
:param number: The number of MMIO registers with the same name
and number suffixes. The number suffixes must go from 0
to ``number - 1`` with no gaps.
"""
self.name = name
self.read_only = read_only
self.number = number
self.exntype = exntype
# These are filled in by the CSR file.
self.size = None
def mmio_factory(num, exntype):
"""
Return a function that simplifies the creation of instances of
:py:class:`MMIORegister` with the same number and exception type.
:param num: Number of registers with the same name (minus suffix).
:param exntype: MicroPython exception type.
:return: A function ``f(name, read_only=False)``. Each argument is
the same as the one in the initializer of py:class:`MMIORegister`.
"""
def f(name, read_only=False):
return MMIORegister(name, read_only, number=num, exntype=exntype)
return f
class CSRHandler: class CSRHandler:
""" """
Class that wraps the CSR file and fills in registers with information Class that wraps the CSR file and fills in registers with information
from those files. from those files.
""" """
def __init__(self, csrjson, bitwidthjson, registers): def __init__(self, csrjson, registers):
""" """
Reads in the CSR files. Reads in the CSR files.
:param csrjson: Filename of a LiteX "csr.json" file. :param csrjson: Filename of a LiteX "csr.json" file.
:param bitwidthjson: Filename of an Upsilon "bitwidthjson" file. :param registers: A list of ``mmio_descr`` ``Descr``s.
:param registers: A list of :py:class:`MMIORegister`s.
:param outf: Output file. :param outf: Output file.
""" """
self.registers = registers self.registers = registers
self.csrs = json.load(open(csrjson)) self.csrs = json.load(open(csrjson))
self.bws = json.load(open(bitwidthjson))
def update_reg(self, reg): def update_reg(self, reg):
""" """
@ -76,17 +40,19 @@ class CSRHandler:
:param reg: The register. :param reg: The register.
:raises Exception: When the bit width exceeds 64. :raises Exception: When the bit width exceeds 64.
""" """
b = self.bws[reg.name] regsize = None
b = reg.blen
if b <= 8: if b <= 8:
reg.size = 8 regsize = 8
elif b <= 16: elif b <= 16:
reg.size = 16 regsize = 16
elif b <= 32: elif b <= 32:
reg.size = 32 regsize = 32
elif b <= 64: elif b <= 64:
reg.size = 64 regsize = 64
else: else:
raise Exception(f"unsupported width {b} in {reg.name}") raise Exception(f"unsupported width {b} in {reg.name}")
setattr(reg, "regsize", regsize)
def get_reg_addr(self, reg, num=None): def get_reg_addr(self, reg, num=None):
""" """
@ -135,7 +101,7 @@ class InterfaceGenerator:
self.print(self.header()) self.print(self.header())
for r in self.csr.registers: for r in self.csr.registers:
self.print(self.fun(r, "read")) self.print(self.fun(r, "read"))
if not r.read_only: if not r.rwperm != "read-only":
self.print(self.fun(r, "write")) self.print(self.fun(r, "write"))
class MicropythonGenerator(InterfaceGenerator): class MicropythonGenerator(InterfaceGenerator):
@ -144,9 +110,9 @@ class MicropythonGenerator(InterfaceGenerator):
def get_accessor(self, reg, num): def get_accessor(self, reg, num):
addr = self.csr.get_reg_addr(reg, num) addr = self.csr.get_reg_addr(reg, num)
if reg.size in [8, 16, 32]: if reg.regsize in [8, 16, 32]:
return [f"machine.mem{reg.size}[{addr}]"] return [f"machine.mem{reg.regsize}[{addr}]"]
return [f"machine.mem32[{addr}]", f"machine.mem32[{addr + 4}]"] return [f"machine.mem32[{addr + 4}]", f"machine.mem32[{addr}]"]
def print_write_register(self, indent, varname, reg, num): def print_write_register(self, indent, varname, reg, num):
acc = self.get_accessor(reg, num) acc = self.get_accessor(reg, num)
@ -181,16 +147,16 @@ class MicropythonGenerator(InterfaceGenerator):
else: else:
pfun = self.print_read_register pfun = self.print_read_register
if reg.number != 1: if reg.num != 1:
if printed_argument: if printed_argument:
a(', ') a(', ')
a('num') a('num')
a('):\n') a('):\n')
if reg.number == 1: if reg.num == 1:
a(pfun('\t', 'val', reg, None)) a(pfun('\t', 'val', reg, None))
else: else:
for i in range(0,reg.number): for i in range(0,reg.num):
if i == 0: if i == 0:
a(f'\tif ') a(f'\tif ')
else: else:
@ -198,46 +164,16 @@ class MicropythonGenerator(InterfaceGenerator):
a(f'num == {i}:\n') a(f'num == {i}:\n')
a(pfun('\t\t', 'val', reg, i)) a(pfun('\t\t', 'val', reg, i))
a(f'\telse:\n') a(f'\telse:\n')
a(f'\t\traise {r.exntype}(regnum)\n') a(f'\t\traise Exception(regnum)\n')
a('\n') a('\n')
return rs return rs
def header(self): def header(self):
return """import machine return "import machine\n"
class InvalidDACException(Exception):
pass
class InvalidADCException(Exception):
pass
"""
if __name__ == "__main__": if __name__ == "__main__":
dac_num = 8 csrh = CSRHandler(sys.argv[1], mmio_descr.registers)
adc_num = 8 for r in mmio_descr.registers:
dac_reg = mmio_factory(dac_num, "InvalidDACException")
adc_reg = mmio_factory(adc_num, "InvalidADCException")
registers = [
dac_reg("dac_sel"),
dac_reg("dac_finished", read_only=True),
dac_reg("dac_arm"),
dac_reg("dac_recv_buf", read_only=True),
dac_reg("dac_send_buf"),
adc_reg("adc_finished", read_only=True),
adc_reg("adc_arm"),
adc_reg("adc_recv_buf", read_only=True),
adc_reg("adc_sel"),
MMIORegister("cl_in_loop", read_only=True),
MMIORegister("cl_cmd"),
MMIORegister("cl_word_in"),
MMIORegister("cl_word_out", read_only=True),
MMIORegister("cl_start_cmd"),
MMIORegister("cl_finish_cmd", read_only=True),
MMIORegister("cl_z_report", read_only=True),
]
csrh = CSRHandler(sys.argv[1], sys.argv[2], registers)
for r in registers:
csrh.update_reg(r) csrh.update_reg(r)
MicropythonGenerator(csrh, sys.stdout).print_file() MicropythonGenerator(csrh, sys.stdout).print_file()

View File

@ -11,15 +11,15 @@ class Descr:
""" """
self.name = name self.name = name
self.blen = blen self.blen = blen
self.doc = textwrap.deindent(descr) self.doc = textwrap.dedent(descr)
self.num =num self.num =num
self.read_only = read_only == "read-only" self.rwperm = rwperm
@classmethod @classmethod
def from_dict(cls, jsdict, name): def from_dict(cls, jsdict, name):
return cls(name, jsdict[name]["len"], jsdict[name]["ro"], jsdict[name]["num"], jsdict[name]["doc"]) return cls(name, jsdict[name]["len"], jsdict[name]["ro"], jsdict[name]["num"], jsdict[name]["doc"])
def store_to_dict(self, d): def store_to_dict(self, d):
d[self.name = { d[self.name] = {
"len": self.blen, "len": self.blen,
"doc": self.doc, "doc": self.doc,
"num": self.num, "num": self.num,
@ -27,7 +27,7 @@ class Descr:
} }
registers = [ registers = [
Descr("adc_sel", 3, "read-write", """\ Descr("adc_sel", 3, "read-write", 8, """\
Select which on-FPGA SPI master controls the ADC. Select which on-FPGA SPI master controls the ADC.
Valid settings: Valid settings:
@ -36,7 +36,7 @@ registers = [
* ``0b10``: ADC is controlled by MMIO registers, but conversion is * ``0b10``: ADC is controlled by MMIO registers, but conversion is
disabled. This is used to flush output from an out-of-sync ADC. disabled. This is used to flush output from an out-of-sync ADC.
* ``0b100``: ADC 0 only. ADC is controlled by control loop."""), * ``0b100``: ADC 0 only. ADC is controlled by control loop."""),
Descr("adc_finished", "read-only", """\ Descr("adc_finished", 1, "read-only", 8, """\
Signals that an ADC master has finished an SPI cycle. Signals that an ADC master has finished an SPI cycle.
Values: Values:
@ -47,9 +47,8 @@ registers = [
This flag is on only when ``adc_arm`` is high. The flag does not This flag is on only when ``adc_arm`` is high. The flag does not
mean that data has been received successfully, only that the master mean that data has been received successfully, only that the master
has finished it's SPI transfer. has finished it's SPI transfer."""),
"""), Descr("adc_arm", 1, "read-write", 8, """\
Descr("adc_arm", "read-write", """\
Start a DAC master SPI transfer. Start a DAC master SPI transfer.
If ``adc_arm`` is raised from and the master is currently not in a SPI If ``adc_arm`` is raised from and the master is currently not in a SPI
@ -84,7 +83,7 @@ registers = [
If ``adc_sel`` is not set to 0 then the transfer will proceed If ``adc_sel`` is not set to 0 then the transfer will proceed
as normal, but no data will be received from the ADC."""), as normal, but no data will be received from the ADC."""),
Descr("adc_recv_buf", "read-only", """\ Descr("adc_recv_buf", 18, "read-only", 8, """\
ADC Master receive buffer. ADC Master receive buffer.
This buffer is stable if there is no ADC transfer caused by ``adc_arm`` This buffer is stable if there is no ADC transfer caused by ``adc_arm``
@ -94,14 +93,14 @@ registers = [
registers. SPI transfers by other masters will not affect this register. registers. SPI transfers by other masters will not affect this register.
buffer."""), buffer."""),
Descr("dac_sel", 2, "read-write", """\ Descr("dac_sel", 2, "read-write", 8, """\
Select which on-FPGA SPI master controls the DAC. Select which on-FPGA SPI master controls the DAC.
Valid settings: Valid settings:
* ``0``: DAC is controlled by MMIO registers. * ``0``: DAC is controlled by MMIO registers.
* ``0b10``: DAC 0 only. DAC is controlled by control loop."""), * ``0b10``: DAC 0 only. DAC is controlled by control loop."""),
Descr("dac_finished", 1, "read-only", """\ Descr("dac_finished", 1, "read-only", 8, """\
Signals that the DAC master has finished transmitting data. Signals that the DAC master has finished transmitting data.
Values: Values:
@ -113,7 +112,7 @@ registers = [
This flag is on only when ``dac_arm`` is high. The flag does not This flag is on only when ``dac_arm`` is high. The flag does not
mean that data has been received or transmitted successfully, only that mean that data has been received or transmitted successfully, only that
the master has finished it's SPI transfer."""), the master has finished it's SPI transfer."""),
Descr("dac_arm", 1, "read-write", """\ Descr("dac_arm", 1, "read-write", 8, """\
Start a DAC master SPI transfer. Start a DAC master SPI transfer.
If ``dac_arm`` is raised from and the master is currently not in a SPI If ``dac_arm`` is raised from and the master is currently not in a SPI
@ -138,7 +137,7 @@ registers = [
If ``dac_sel`` is set to another master then the transfer will proceed If ``dac_sel`` is set to another master then the transfer will proceed
as normal, but no data will be sent to or received from the DAC."""), as normal, but no data will be sent to or received from the DAC."""),
Descr("dac_recv_buf", 24, "read-only", """\ Descr("dac_recv_buf", 24, "read-only", 8, """\
DAC master receive buffer. DAC master receive buffer.
This buffer is stable if there is no DAC transfer caused by ``dac_arm`` This buffer is stable if there is no DAC transfer caused by ``dac_arm``
@ -147,7 +146,7 @@ registers = [
This register only changes if an SPI transfer is triggered by the MMIO This register only changes if an SPI transfer is triggered by the MMIO
registers. SPI transfers by other masters will not affect this register. registers. SPI transfers by other masters will not affect this register.
buffer."""), buffer."""),
Descr("dac_send_buf, 24, "read-write", """\ Descr("dac_send_buf", 24, "read-write", 8, """\
DAC master send buffer. DAC master send buffer.
Fill this buffer with a 24 bit Analog Devices DAC command. Updating Fill this buffer with a 24 bit Analog Devices DAC command. Updating
@ -158,7 +157,7 @@ registers = [
Modifying this buffer during a transfer does not disrupt an in-process Modifying this buffer during a transfer does not disrupt an in-process
transfer."""), transfer."""),
Descr("cl_assert_change", 1, "read-write", """\ Descr("cl_assert_change", 1, "read-write", 1, """\
Flush parameter changes to control loop. Flush parameter changes to control loop.
When this bit is raised from low to high, this signals the control When this bit is raised from low to high, this signals the control
@ -168,21 +167,23 @@ registers = [
When this bit is raised from high to low before ``cl_change_made`` When this bit is raised from high to low before ``cl_change_made``
is asserted by the control loop, nothing happens."""), is asserted by the control loop, nothing happens."""),
Descr("cl_change_made", 1, "read-only", """\ Descr("cl_change_made", 1, "read-only", 1, """\
Signal from the control loop that the parameters have been applied. Signal from the control loop that the parameters have been applied.
This signal goes high only while ``cl_assert_change`` is high. No This signal goes high only while ``cl_assert_change`` is high. No
change will be applied afterwards while both are high."""), change will be applied afterwards while both are high."""),
Descr("cl_in_loop_in", 1, "read-only", """\ Descr("cl_in_loop", 1, "read-only", 1, """\
This bit is high if the control loop is running."""), This bit is high if the control loop is running."""),
Descr("cl_setpt_in", 18, "read-write", """\ Descr("cl_run_loop_in", 1, "read-write", 1, """\
Set this bit high to start the control loop."""),
Descr("cl_setpt_in", 18, "read-write", 1, """\
Setpoint of the control loop. Setpoint of the control loop.
This is a twos-complement number in ADC units. This is a twos-complement number in ADC units.
This is a parameter: see ``cl_assert_change``."""), This is a parameter: see ``cl_assert_change``."""),
Descr("cl_P_in", 64, "read-write", """\ Descr("cl_P_in", 64, "read-write", 1, """\
Proportional parameter of the control loop. Proportional parameter of the control loop.
This is a twos-complement fixed point number with 21 whole This is a twos-complement fixed point number with 21 whole
@ -190,7 +191,7 @@ registers = [
in DAC units. in DAC units.
This is a parameter: see ``cl_assert_change``."""), This is a parameter: see ``cl_assert_change``."""),
Descr("cl_I_in", 64, "read-write", """\ Descr("cl_I_in", 64, "read-write", 1, """\
Integral parameter of the control loop. Integral parameter of the control loop.
This is a twos-complement fixed point number with 21 whole This is a twos-complement fixed point number with 21 whole
@ -198,7 +199,7 @@ registers = [
in DAC units. in DAC units.
This is a parameter: see ``cl_assert_change``."""), This is a parameter: see ``cl_assert_change``."""),
Descr("cl_delay_in", 16, "read-write", """\ Descr("cl_delay_in", 16, "read-write", 1, """\
Delay parameter of the loop. Delay parameter of the loop.
This is an unsigned number denoting the number of cycles This is an unsigned number denoting the number of cycles
@ -206,15 +207,15 @@ registers = [
This is a parameter: see ``cl_assert_change``."""), This is a parameter: see ``cl_assert_change``."""),
Descr("cl_cycle_count", 18, "read-only", """\ Descr("cl_cycle_count", 18, "read-only", 1, """\
Delay parameter of the loop. Delay parameter of the loop.
This is an unsigned number denoting the number of cycles This is an unsigned number denoting the number of cycles
the loop should wait between loop executions."""), the loop should wait between loop executions."""),
Descr("cl_z_pos", 20, "read-only", """\ Descr("cl_z_pos", 20, "read-only", 1, """\
Control loop DAC Z position. Control loop DAC Z position.
"""), """),
Descr("cl_z_measured", 18, "read-only", """\ Descr("cl_z_measured", 18, "read-only", 1, """\
Control loop ADC Z position. Control loop ADC Z position.
"""), """),
] ]

View File

@ -292,6 +292,7 @@ m4_define(CL_DATA_WID, CL_CONSTS_WID)
input cl_assert_change, input cl_assert_change,
output cl_change_made, output cl_change_made,
output cl_in_loop,
input cl_run_loop_in, input cl_run_loop_in,
input [ADC_TYPE1_WID-1:0] cl_setpt_in, input [ADC_TYPE1_WID-1:0] cl_setpt_in,
@ -299,9 +300,9 @@ m4_define(CL_DATA_WID, CL_CONSTS_WID)
input [CL_DATA_WID-1:0] cl_I_in, input [CL_DATA_WID-1:0] cl_I_in,
input [CL_DELAY_WID-1:0] cl_delay_in, input [CL_DELAY_WID-1:0] cl_delay_in,
output [CYCLE_COUNT_WID-1:0] cl_cycle_count, output [CL_CYCLE_COUNT_WID-1:0] cl_cycle_count,
output [DAC_DATA_WID-1:0] cl_z_pos, output [DAC_DATA_WID-1:0] cl_z_pos,
output [ADC_WID-1:0] cl_z_measured output [ADC_TYPE1_WID-1:0] cl_z_measured
); );
assign set_low = 0; assign set_low = 0;

View File

@ -87,80 +87,80 @@ If there is more than one pin in the Pins string, the resulting
name will be a vector of pins. name will be a vector of pins.
""" """
io = [ io = [
("differntial_output_low", 0, Pins("J17 J18 K15 J15 U14 V14 T13 U13 B6 E5 A3"), IOStandard("LVCMOS33")), ("differntial_output_low", 0, Pins("J17 J18 K15 J15 U14 V14 T13 U13 B6 E5 A3"), IOStandard("LVCMOS33")),
("dac_ss_L", 0, Pins("G13 D13 E15 F5 U12 D7 D4 E2"), IOStandard("LVCMOS33")), ("dac_ss_L", 0, Pins("G13 D13 E15 F5 U12 D7 D4 E2"), IOStandard("LVCMOS33")),
("dac_mosi", 0, Pins("B11 B18 E16 D8 V12 D5 D3 D2"), IOStandard("LVCMOS33")), ("dac_mosi", 0, Pins("B11 B18 E16 D8 V12 D5 D3 D2"), IOStandard("LVCMOS33")),
("dac_miso", 0, Pins("A11 A18 D15 C7 V10 B7 F4 H2"), IOStandard("LVCMOS33")), ("dac_miso", 0, Pins("A11 A18 D15 C7 V10 B7 F4 H2"), IOStandard("LVCMOS33")),
("dac_sck", 0, Pins("D12 K16 C15 E7 V11 E6 F3 G2"), IOStandard("LVCMOS33")), ("dac_sck", 0, Pins("D12 K16 C15 E7 V11 E6 F3 G2"), IOStandard("LVCMOS33")),
("adc_conv", 0, Pins("V15 T11 N15 U18 U11 R10 R16 U17"), IOStandard("LVCMOS33")), ("adc_conv", 0, Pins("V15 T11 N15 U18 U11 R10 R16 U17"), IOStandard("LVCMOS33")),
("adc_sck", 0, Pins("U16 R12 M16 R17 V16 R11 N16 T18"), IOStandard("LVCMOS33")), ("adc_sck", 0, Pins("U16 R12 M16 R17 V16 R11 N16 T18"), IOStandard("LVCMOS33")),
("adc_sdo", 0, Pins("P14 T14 V17 P17 M13 R13 N14 R18"), IOStandard("LVCMOS33")), ("adc_sdo", 0, Pins("P14 T14 V17 P17 M13 R13 N14 R18"), IOStandard("LVCMOS33")),
("module_reset", 0, Pins("D9"), IOStandard("LVCMOS33")), ("module_reset", 0, Pins("D9"), IOStandard("LVCMOS33")),
("test_clock", 0, Pins("P18"), IOStandard("LVCMOS33")) ("test_clock", 0, Pins("P18"), IOStandard("LVCMOS33"))
] ]
# TODO: Assign widths to ADCs here using parameters # TODO: Assign widths to ADCs here using parameters
class Base(Module, AutoCSR): class Base(Module, AutoCSR):
""" The subclass AutoCSR will automatically make CSRs related """ The subclass AutoCSR will automatically make CSRs related
to this class when those CSRs are attributes (i.e. accessed by to this class when those CSRs are attributes (i.e. accessed by
`self.csr_name`) of instances of this class. (CSRs are MMIO, `self.csr_name`) of instances of this class. (CSRs are MMIO,
they are NOT RISC-V CSRs!) they are NOT RISC-V CSRs!)
Since there are a lot of input and output wires, the CSRs are Since there are a lot of input and output wires, the CSRs are
assigned using `setattr()`. assigned using `setattr()`.
CSRs are for input wires (`CSRStorage`) or output wires CSRs are for input wires (`CSRStorage`) or output wires
(`CSRStatus`). The first argument to the CSR constructor is (`CSRStatus`). The first argument to the CSR constructor is
the amount of bits the CSR takes. The `name` keyword argument the amount of bits the CSR takes. The `name` keyword argument
is required since the constructor needs the name of the attribute. is required since the constructor needs the name of the attribute.
The `description` keyword is used for documentation. The `description` keyword is used for documentation.
In LiteX, modules in separate Verilog files are instantiated as In LiteX, modules in separate Verilog files are instantiated as
self.specials += Instance( self.specials += Instance(
"module_name", "module_name",
PARAMETER_NAME=value, PARAMETER_NAME=value,
i_input = input_port, i_input = input_port,
o_output = output_port, o_output = output_port,
... ...
) )
Since the "base" module has a bunch of repeated input and output Since the "base" module has a bunch of repeated input and output
pins that have to be connected to CSRs, the LiteX wrapper uses pins that have to be connected to CSRs, the LiteX wrapper uses
keyword arguments to pass all the arguments. keyword arguments to pass all the arguments.
""" """
def _make_csr(self, reg, num=None): def _make_csr(self, reg, num=None):
""" Add a CSR for a pin `f"{name}_{num}".` """ Add a CSR for a pin `f"{name}_{num}".`
:param name: Name of the MMIO register without prefixes or numerical :param name: Name of the MMIO register without prefixes or numerical
suffix. suffix.
:param num: Numerical suffix of this MMIO register. This is the only :param num: Numerical suffix of this MMIO register. This is the only
parameter that should change when adding multiple CSRs of the same parameter that should change when adding multiple CSRs of the same
name. name.
""" """
name = reg.name name = reg.name
if num is not None: if num is not None:
name = f"{name}_{num}" name = f"{name}_{num}"
if self.ro == "read-only": if reg.rwperm == "read-only":
csrclass = CSRStatus csrclass = CSRStatus
else: else:
csrclass = CSRStorage csrclass = CSRStorage
csr = csrclass(reg.blen, name=name, description=None) csr = csrclass(reg.blen, name=name, description=None)
setattr(self, name, csr) setattr(self, name, csr)
if csrclass is CSRStorage: if csrclass is CSRStorage:
self.kwargs[f'i_{name}'] = csr.storage self.kwargs[f'i_{name}'] = csr.storage
elif csrclass is CSRStatus: elif csrclass is CSRStatus:
self.kwargs[f'o_{name}'] = csr.status self.kwargs[f'o_{name}'] = csr.status
else: else:
raise Exception(f"Unknown class {csrclass}") raise Exception(f"Unknown class {csrclass}")
def __init__(self, clk, sdram, platform): def __init__(self, clk, sdram, platform):
self.kwargs = {} self.kwargs = {}
for reg in mmio_descr.registers: for reg in mmio_descr.registers:
if reg.num > 1: if reg.num > 1:
@ -169,133 +169,133 @@ class Base(Module, AutoCSR):
else: else:
self._make_csr(reg) self._make_csr(reg)
self.kwargs["i_clk"] = clk self.kwargs["i_clk"] = clk
self.kwargs["i_rst_L"] = ~platform.request("module_reset") self.kwargs["i_rst_L"] = ~platform.request("module_reset")
self.kwargs["i_dac_miso"] = platform.request("dac_miso") self.kwargs["i_dac_miso"] = platform.request("dac_miso")
self.kwargs["o_dac_mosi"] = platform.request("dac_mosi") self.kwargs["o_dac_mosi"] = platform.request("dac_mosi")
self.kwargs["o_dac_sck"] = platform.request("dac_sck") self.kwargs["o_dac_sck"] = platform.request("dac_sck")
self.kwargs["o_dac_ss_L"] = platform.request("dac_ss_L") self.kwargs["o_dac_ss_L"] = platform.request("dac_ss_L")
self.kwargs["o_adc_conv"] = platform.request("adc_conv") self.kwargs["o_adc_conv"] = platform.request("adc_conv")
self.kwargs["i_adc_sdo"] = platform.request("adc_sdo") self.kwargs["i_adc_sdo"] = platform.request("adc_sdo")
self.kwargs["o_adc_sck"] = platform.request("adc_sck") self.kwargs["o_adc_sck"] = platform.request("adc_sck")
self.kwargs["o_set_low"] = platform.request("differntial_output_low") self.kwargs["o_set_low"] = platform.request("differntial_output_low")
self.specials += Instance("base", **self.kwargs) self.specials += Instance("base", **self.kwargs)
# Clock and Reset Generator # Clock and Reset Generator
# I don't know how this works, I only know that it does. # I don't know how this works, I only know that it does.
class _CRG(Module): class _CRG(Module):
def __init__(self, platform, sys_clk_freq, with_dram, rst_pin): def __init__(self, platform, sys_clk_freq, with_dram, rst_pin):
self.rst = Signal() self.rst = Signal()
self.clock_domains.cd_sys = ClockDomain() self.clock_domains.cd_sys = ClockDomain()
self.clock_domains.cd_eth = ClockDomain() self.clock_domains.cd_eth = ClockDomain()
if with_dram: if with_dram:
self.clock_domains.cd_sys4x = ClockDomain() self.clock_domains.cd_sys4x = ClockDomain()
self.clock_domains.cd_sys4x_dqs = ClockDomain() self.clock_domains.cd_sys4x_dqs = ClockDomain()
self.clock_domains.cd_idelay = ClockDomain() self.clock_domains.cd_idelay = ClockDomain()
# Clk/Rst. # Clk/Rst.
clk100 = platform.request("clk100") clk100 = platform.request("clk100")
rst = ~rst_pin if rst_pin is not None else 0 rst = ~rst_pin if rst_pin is not None else 0
# PLL. # PLL.
self.submodules.pll = pll = S7PLL(speedgrade=-1) self.submodules.pll = pll = S7PLL(speedgrade=-1)
self.comb += pll.reset.eq(rst | self.rst) self.comb += pll.reset.eq(rst | self.rst)
pll.register_clkin(clk100, 100e6) pll.register_clkin(clk100, 100e6)
pll.create_clkout(self.cd_sys, sys_clk_freq) pll.create_clkout(self.cd_sys, sys_clk_freq)
pll.create_clkout(self.cd_eth, 25e6) pll.create_clkout(self.cd_eth, 25e6)
self.comb += platform.request("eth_ref_clk").eq(self.cd_eth.clk) self.comb += platform.request("eth_ref_clk").eq(self.cd_eth.clk)
platform.add_false_path_constraints(self.cd_sys.clk, pll.clkin) # Ignore sys_clk to pll.clkin path created by SoC's rst. platform.add_false_path_constraints(self.cd_sys.clk, pll.clkin) # Ignore sys_clk to pll.clkin path created by SoC's rst.
if with_dram: if with_dram:
pll.create_clkout(self.cd_sys4x, 4*sys_clk_freq) pll.create_clkout(self.cd_sys4x, 4*sys_clk_freq)
pll.create_clkout(self.cd_sys4x_dqs, 4*sys_clk_freq, phase=90) pll.create_clkout(self.cd_sys4x_dqs, 4*sys_clk_freq, phase=90)
pll.create_clkout(self.cd_idelay, 200e6) pll.create_clkout(self.cd_idelay, 200e6)
# IdelayCtrl. # IdelayCtrl.
if with_dram: if with_dram:
self.submodules.idelayctrl = S7IDELAYCTRL(self.cd_idelay) self.submodules.idelayctrl = S7IDELAYCTRL(self.cd_idelay)
class UpsilonSoC(SoCCore): class UpsilonSoC(SoCCore):
def __init__(self, variant): def __init__(self, variant):
sys_clk_freq = int(100e6) sys_clk_freq = int(100e6)
platform = board_spec.Platform(variant=variant, toolchain="f4pga") platform = board_spec.Platform(variant=variant, toolchain="f4pga")
rst = platform.request("cpu_reset") rst = platform.request("cpu_reset")
self.submodules.crg = _CRG(platform, sys_clk_freq, True, rst) self.submodules.crg = _CRG(platform, sys_clk_freq, True, rst)
""" """
These source files need to be sorted so that modules These source files need to be sorted so that modules
that rely on another module come later. For instance, that rely on another module come later. For instance,
`control_loop` depends on `control_loop_math`, so `control_loop` depends on `control_loop_math`, so
control_loop_math.v comes before control_loop.v control_loop_math.v comes before control_loop.v
If you want to add a new verilog file to the design, look at the If you want to add a new verilog file to the design, look at the
modules that it refers to and place it the files with those modules. modules that it refers to and place it the files with those modules.
Since Yosys doesn't support modern Verilog, only put preprocessed Since Yosys doesn't support modern Verilog, only put preprocessed
(if applicable) files here. (if applicable) files here.
""" """
platform.add_source("rtl/spi/spi_switch_preprocessed.v") platform.add_source("rtl/spi/spi_switch_preprocessed.v")
platform.add_source("rtl/spi/spi_master_preprocessed.v") platform.add_source("rtl/spi/spi_master_preprocessed.v")
platform.add_source("rtl/spi/spi_master_no_write_preprocessed.v") platform.add_source("rtl/spi/spi_master_no_write_preprocessed.v")
platform.add_source("rtl/spi/spi_master_no_read_preprocessed.v") platform.add_source("rtl/spi/spi_master_no_read_preprocessed.v")
platform.add_source("rtl/spi/spi_master_ss_preprocessed.v") platform.add_source("rtl/spi/spi_master_ss_preprocessed.v")
platform.add_source("rtl/spi/spi_master_ss_no_write_preprocessed.v") platform.add_source("rtl/spi/spi_master_ss_no_write_preprocessed.v")
platform.add_source("rtl/spi/spi_master_ss_no_read_preprocessed.v") platform.add_source("rtl/spi/spi_master_ss_no_read_preprocessed.v")
platform.add_source("rtl/control_loop/sign_extend.v") platform.add_source("rtl/control_loop/sign_extend.v")
platform.add_source("rtl/control_loop/intsat.v") platform.add_source("rtl/control_loop/intsat.v")
platform.add_source("rtl/control_loop/boothmul_preprocessed.v") platform.add_source("rtl/control_loop/boothmul_preprocessed.v")
platform.add_source("rtl/control_loop/control_loop_math.v") platform.add_source("rtl/control_loop/control_loop_math.v")
platform.add_source("rtl/control_loop/control_loop.v") platform.add_source("rtl/control_loop/control_loop.v")
# platform.add_source("rtl/waveform/bram_interface_preprocessed.v") # platform.add_source("rtl/waveform/bram_interface_preprocessed.v")
# platform.add_source("rtl/waveform/waveform_preprocessed.v") # platform.add_source("rtl/waveform/waveform_preprocessed.v")
platform.add_source("rtl/base/base.v") platform.add_source("rtl/base/base.v")
# SoCCore does not have sane defaults (no integrated rom) # SoCCore does not have sane defaults (no integrated rom)
SoCCore.__init__(self, SoCCore.__init__(self,
clk_freq=sys_clk_freq, clk_freq=sys_clk_freq,
toolchain="symbiflow", toolchain="symbiflow",
platform = platform, platform = platform,
bus_standard = "wishbone", bus_standard = "wishbone",
ident = f"Arty-{variant} F4PGA LiteX VexRiscV Zephyr - Upsilon", ident = f"Arty-{variant} F4PGA LiteX VexRiscV Zephyr - Upsilon",
bus_data_width = 32, bus_data_width = 32,
bus_address_width = 32, bus_address_width = 32,
bus_timeout = int(1e6), bus_timeout = int(1e6),
cpu_type = "vexriscv_smp", cpu_type = "vexriscv_smp",
cpu_count = 1, cpu_count = 1,
cpu_variant="linux", cpu_variant="linux",
integrated_rom_size=0x20000, integrated_rom_size=0x20000,
integrated_sram_size = 0x2000, integrated_sram_size = 0x2000,
csr_data_width=32, csr_data_width=32,
csr_address_width=14, csr_address_width=14,
csr_paging=0x800, csr_paging=0x800,
csr_ordering="big", csr_ordering="big",
local_ip='192.168.1.50', local_ip='192.168.1.50',
remote_ip='192.168.1.100', remote_ip='192.168.1.100',
timer_uptime = True) timer_uptime = True)
# This initializes the connection to the physical DRAM interface. # This initializes the connection to the physical DRAM interface.
self.submodules.ddrphy = s7ddrphy.A7DDRPHY(platform.request("ddram"), self.submodules.ddrphy = s7ddrphy.A7DDRPHY(platform.request("ddram"),
memtype = "DDR3", memtype = "DDR3",
nphases = 4, nphases = 4,
sys_clk_freq = sys_clk_freq) sys_clk_freq = sys_clk_freq)
# Synchronous dynamic ram. This is what controls all access to RAM. # Synchronous dynamic ram. This is what controls all access to RAM.
# This houses the "crossbar", which negotiates all RAM accesses to different # This houses the "crossbar", which negotiates all RAM accesses to different
# modules, including the verilog interfaces (waveforms etc.) # modules, including the verilog interfaces (waveforms etc.)
self.add_sdram("sdram", self.add_sdram("sdram",
phy = self.ddrphy, phy = self.ddrphy,
module = MT41K128M16(sys_clk_freq, "1:4"), module = MT41K128M16(sys_clk_freq, "1:4"),
l2_cache_size = 8192 l2_cache_size = 8192
) )
self.submodules.ethphy = LiteEthPHYMII( self.submodules.ethphy = LiteEthPHYMII(
clock_pads = platform.request("eth_clocks"), clock_pads = platform.request("eth_clocks"),
pads = platform.request("eth")) pads = platform.request("eth"))
self.add_ethernet(phy=self.ethphy, dynamic_ip=True) self.add_ethernet(phy=self.ethphy, dynamic_ip=True)
platform.add_extension(io) platform.add_extension(io)
self.submodules.base = Base(ClockSignal(), self.sdram, platform) self.submodules.base = Base(ClockSignal(), self.sdram, platform)
def main(): def main():
soc =UpsilonSoC("a7-100") soc =UpsilonSoC("a7-100")
builder = Builder(soc, csr_json="csr.json", compile_software=True) builder = Builder(soc, csr_json="csr.json", compile_software=True)
builder.build() builder.build()
if __name__ == "__main__": if __name__ == "__main__":
main() main()