refactor csr2mp and docker Makefile
This commit is contained in:
parent
f30f6f1ad5
commit
130e1775ac
|
@ -5,6 +5,7 @@
|
||||||
# source distribution.
|
# source distribution.
|
||||||
|
|
||||||
.PHONY: images f4pga buildroot litex clone help attach hardware-image \
|
.PHONY: images f4pga buildroot litex clone help attach hardware-image \
|
||||||
|
install-software openFPGALoader pytftp \
|
||||||
buildroot-image upsilon-hardware.tar.gz upsilon-opensbi.tar.gz upsilon-buildroot.tar.gz
|
buildroot-image upsilon-hardware.tar.gz upsilon-opensbi.tar.gz upsilon-buildroot.tar.gz
|
||||||
|
|
||||||
###### Images
|
###### Images
|
||||||
|
@ -12,6 +13,15 @@
|
||||||
images: hardware-image buildroot-image opensbi-image
|
images: hardware-image buildroot-image opensbi-image
|
||||||
|
|
||||||
|
|
||||||
|
###### Install Software
|
||||||
|
|
||||||
|
install-software: openFPGALoader
|
||||||
|
|
||||||
|
openFPGALoader:
|
||||||
|
git clone https://github.com/trabucayre/openFPGALoader
|
||||||
|
mkdir openFPGALoader/build
|
||||||
|
cd openFPGALoader/build && cmake ..
|
||||||
|
cd openFPGALoader/build && cmake --build .
|
||||||
|
|
||||||
###### Containers
|
###### Containers
|
||||||
|
|
||||||
|
@ -38,6 +48,8 @@ hardware-get:
|
||||||
docker cp upsilon-hardware:/home/user/upsilon/gateware/build/digilent_arty/gateware/digilent_arty.bit ../boot/
|
docker cp upsilon-hardware:/home/user/upsilon/gateware/build/digilent_arty/gateware/digilent_arty.bit ../boot/
|
||||||
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_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
|
||||||
|
@ -112,6 +124,9 @@ buildroot-clean:
|
||||||
|
|
||||||
###### Execute
|
###### Execute
|
||||||
|
|
||||||
|
OPENFPGALOADER=./openFPGALoader/build/openFPGALoader
|
||||||
|
flash:
|
||||||
|
${OPENFPGALOADER} -c digilent ../boot/digilent_arty.bit
|
||||||
tftp:
|
tftp:
|
||||||
cd ../boot && py3tftp --host 192.168.1.100 -p 6969 -v
|
cd ../boot && py3tftp --host 192.168.1.100 -p 6969 -v
|
||||||
|
|
||||||
|
|
|
@ -6,22 +6,55 @@ For license terms, refer to the files in `doc/copying` in the Upsilon
|
||||||
source distribution.
|
source distribution.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pssh.clients import SSHClient
|
from pssh.clients import SSHClient # require parallel-ssh
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
def sign_extend(value, bits):
|
def sign_extend(value, bits):
|
||||||
|
"""
|
||||||
|
Interpret ``value`` as a twos-complement integer of ``bits`` length.
|
||||||
|
|
||||||
|
:param value: Twos-complement integer with finite bit width.
|
||||||
|
:param bits: Bit length of ``value``.
|
||||||
|
:return: ``value`` converted to a Python integer.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Check the sign bit of the integer.
|
||||||
is_signed = (value >> (bits - 1)) & 1 == 1
|
is_signed = (value >> (bits - 1)) & 1 == 1
|
||||||
|
# If not signed, just return the integer.
|
||||||
if not is_signed:
|
if not is_signed:
|
||||||
return value
|
return value
|
||||||
|
# Otherwise,
|
||||||
|
# 1. Do an explicit twos-complement negation
|
||||||
|
# 2. Mask all the non-sign bits
|
||||||
|
# This returns the positive value as a standard Python integer.
|
||||||
|
# Then the function negates the positive integer to get the negative
|
||||||
|
# one back.
|
||||||
return -((~value + 1) & ((1 << (bits - 1)) - 1))
|
return -((~value + 1) & ((1 << (bits - 1)) - 1))
|
||||||
|
|
||||||
|
###################
|
||||||
|
# Boilerplate
|
||||||
|
###################
|
||||||
|
|
||||||
|
# Start a SSH connection to the server.
|
||||||
client = SSHClient('192.168.1.50', user='root', pkey='~/.ssh/upsilon_key')
|
client = SSHClient('192.168.1.50', user='root', pkey='~/.ssh/upsilon_key')
|
||||||
|
# Upload the script.
|
||||||
client.scp_send('../linux/noise_test.py', '/root/noise_test.py')
|
client.scp_send('../linux/noise_test.py', '/root/noise_test.py')
|
||||||
|
# Run the script.
|
||||||
out = client.run_command('micropython noise_test.py')
|
out = client.run_command('micropython noise_test.py')
|
||||||
|
|
||||||
|
################
|
||||||
|
# Script Handler
|
||||||
|
################
|
||||||
|
"""
|
||||||
|
The ramp script outputs a list of lines, each with two values separated by one
|
||||||
|
space. The first value is the DAC setting, the second value is the ADC setting.
|
||||||
|
This script gets all of those values, averages them by DAC value, and plots
|
||||||
|
it.
|
||||||
|
"""
|
||||||
|
|
||||||
current_dac = None
|
current_dac = None
|
||||||
current_adc = []
|
current_adc = []
|
||||||
x_ax = []
|
x_ax = []
|
||||||
|
|
|
@ -11,17 +11,15 @@ Change directory to `build`.
|
||||||
|
|
||||||
## Installing OpenFPGALoader
|
## Installing OpenFPGALoader
|
||||||
|
|
||||||
Install [openFPGALoader][1]. This utility entered the Ubuntu repositories
|
Install [openFPGALoader][1]. If this program is not in your repositories,
|
||||||
in 23.04. Install and compile it if you do not have it. Install the udev rule
|
run `make openFPGALoader` to fetch and install the program.
|
||||||
so that admin access is not required to load FPGA bitstreams.
|
|
||||||
|
|
||||||
[1]: https://trabucayre.github.io/openFPGALoader/index.html
|
[1]: https://trabucayre.github.io/openFPGALoader/index.html
|
||||||
|
|
||||||
## Setup Rootless Docker
|
## Setup Rootless Docker
|
||||||
|
|
||||||
Docker allows you to run programs in containers, which are isolated
|
Docker allows you to run programs in containers, which are isolated
|
||||||
environments. Upsilon development (at the Maglab) uses Docker for
|
environments. Build environments can be set up automatically, and re-setup
|
||||||
reproducibility: the environment can be set up automatically, and re-setup
|
|
||||||
whenever needed.
|
whenever needed.
|
||||||
|
|
||||||
If you have issues with docker, try adding to `~/.config/docker/daemon.json`
|
If you have issues with docker, try adding to `~/.config/docker/daemon.json`
|
||||||
|
@ -33,7 +31,7 @@ If you have issues with docker, try adding to `~/.config/docker/daemon.json`
|
||||||
|
|
||||||
## Download and Install Python3
|
## Download and Install Python3
|
||||||
|
|
||||||
Install `python3-venv` (or `python3-virtualenv`) and `python3-pip`.
|
Install `python3` and `python3-pip`.
|
||||||
|
|
||||||
## Clone External Repositories
|
## Clone External Repositories
|
||||||
|
|
||||||
|
@ -46,7 +44,7 @@ Plug in your router/switch to an ethernet port on your computer. If your
|
||||||
computer is usually wired to the network, you will need another ethernet
|
computer is usually wired to the network, you will need another ethernet
|
||||||
port (a PCI card is ideal, but a USB-Ethernet port works).
|
port (a PCI card is ideal, but a USB-Ethernet port works).
|
||||||
|
|
||||||
Set the ethernet port to static ip `192.168.1.100/24`, netmask 255.255.255.0,
|
Set the ethernet port to static ip `192.168.1.100/24`, netmask `255.255.255.0`,
|
||||||
gateway `192.168.1.1`. Make sure this is not the default route. Make sure
|
gateway `192.168.1.1`. Make sure this is not the default route. Make sure
|
||||||
to adjust your firewall to allow traffic on the 192.168.1.0/24 range.
|
to adjust your firewall to allow traffic on the 192.168.1.0/24 range.
|
||||||
|
|
||||||
|
@ -82,9 +80,9 @@ launch the TFTP server. Keep this terminal open.
|
||||||
|
|
||||||
## Flash FPGA
|
## Flash FPGA
|
||||||
|
|
||||||
Plug in your FPGA into the USB slot. Then run
|
Plug in your FPGA into the USB slot. If you have installed openFPGALoader
|
||||||
|
by your package manager, run `make OPENFPGALOADER=openfpgaloader flash`.
|
||||||
openFPGALoader -c digilent upsilon/boot/digilent_arty.bit
|
If you installed it using `make openFPGALoader`, then just run `make flash`.
|
||||||
|
|
||||||
In a second you should see messages in the TFTP terminal. This means your
|
In a second you should see messages in the TFTP terminal. This means your
|
||||||
controller is sucessfully connected to your computer.
|
controller is sucessfully connected to your computer.
|
||||||
|
@ -113,9 +111,12 @@ Wait about a minute for Linux to boot.
|
||||||
If you cannot access the FPGA through SSH, you can launch a shell through
|
If you cannot access the FPGA through SSH, you can launch a shell through
|
||||||
UART.
|
UART.
|
||||||
|
|
||||||
|
You will need to install [LiteX](https://github.com/enjoy-digital/litex).
|
||||||
|
Download and run `litex_setup.py`.
|
||||||
|
|
||||||
Run `litex_term /dev/ttyUSB1`. You should get messages in the window with
|
Run `litex_term /dev/ttyUSB1`. You should get messages in the window with
|
||||||
the TFTP server that the FPGA has connected to the server. Eventually you
|
the TFTP server that the FPGA has connected to the server. Eventually you
|
||||||
will get a login prompt: you have sucessfully loaded Upsilon onto your FPGA.
|
will get a login prompt (username `root` password `upsilon`).
|
||||||
|
|
||||||
## Copy Library
|
## Copy Library
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#
|
#
|
||||||
# TODO: Devicetree?
|
# TODO: Devicetree?
|
||||||
|
|
||||||
|
import collections
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
|
@ -19,8 +20,8 @@ class MMIORegister:
|
||||||
def __init__(self, name, read_only=False, number=1, exntype=None):
|
def __init__(self, name, read_only=False, number=1, exntype=None):
|
||||||
"""
|
"""
|
||||||
Describes a MMIO register.
|
Describes a MMIO register.
|
||||||
:param name: The name of the MMIO register. This name must be the
|
:param name: The name of the MMIO register, excluding the prefix
|
||||||
same as the pin name used in ``csr.json``, except for any
|
defining its module (i.e. ``base_``) and excluding its
|
||||||
numerical suffix.
|
numerical suffix.
|
||||||
:param read_only: True if the register is read only. Defaults to
|
:param read_only: True if the register is read only. Defaults to
|
||||||
``False``.
|
``False``.
|
||||||
|
@ -33,121 +34,181 @@ class MMIORegister:
|
||||||
self.number = number
|
self.number = number
|
||||||
self.exntype = exntype
|
self.exntype = exntype
|
||||||
|
|
||||||
def mmio_factory(dac_num, 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):
|
def f(name, read_only=False):
|
||||||
return MMIORegister(name, read_only, numer=dac_num, exntype=exntype)
|
return MMIORegister(name, read_only, number=num, exntype=exntype)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
|
class CSRHandler:
|
||||||
class MicroPythonCSRGenerator:
|
|
||||||
def __init__(self, csrjson, bitwidthjson, registers, outf):
|
|
||||||
"""
|
"""
|
||||||
This class generates a MicroPython wrapper for MMIO registers.
|
Class that wraps the CSR file and fills in registers with information
|
||||||
|
from those files.
|
||||||
|
"""
|
||||||
|
def __init__(self, csrjson, bitwidthjson, registers):
|
||||||
|
"""
|
||||||
|
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 bitwidthjson: Filename of an Upsilon "bitwidthjson" file.
|
||||||
:param registers: A list of
|
:param registers: A list of :py:class:`MMIORegister`s.
|
||||||
|
: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))
|
self.bws = json.load(open(bitwidthjson))
|
||||||
self.file = outf
|
|
||||||
|
|
||||||
def get_reg(self, name, num=None):
|
def update_reg(self, reg):
|
||||||
""" Get the base address of the register. """
|
"""
|
||||||
if num is None:
|
Fill in size information from bitwidth json file.
|
||||||
regname = f"base_{name}"
|
|
||||||
|
:param reg: The register.
|
||||||
|
:raises Exception: When the bit width exceeds 64.
|
||||||
|
"""
|
||||||
|
b = self.bws[reg.name]
|
||||||
|
if b <= 8:
|
||||||
|
reg.size = 8
|
||||||
|
elif b <= 16:
|
||||||
|
reg.size = 16
|
||||||
|
elif b <= 32:
|
||||||
|
reg.size = 32
|
||||||
|
elif b <= 64:
|
||||||
|
reg.size = 64
|
||||||
else:
|
else:
|
||||||
regname = f"base_{name}_{num}"
|
raise Exception(f"unsupported width {b} in {reg.name}")
|
||||||
|
|
||||||
|
def get_reg_addr(self, reg, num=None):
|
||||||
|
"""
|
||||||
|
Get address of register.
|
||||||
|
|
||||||
|
:param reg: The register.
|
||||||
|
:param num: Select which register number. Registers without
|
||||||
|
numerical suffixes require ``None``.
|
||||||
|
:return: The address.
|
||||||
|
"""
|
||||||
|
if num is None:
|
||||||
|
regname = f"base_{reg.name}"
|
||||||
|
else:
|
||||||
|
regname = f"base_{reg.name}_{num}"
|
||||||
return self.csrs["csr_registers"][regname]["addr"]
|
return self.csrs["csr_registers"][regname]["addr"]
|
||||||
|
|
||||||
def get_accessor(self, name, num=None):
|
class InterfaceGenerator:
|
||||||
""" Return a list of Micropython machine.mem accesses that can
|
|
||||||
be used to read/write to a MMIO register.
|
|
||||||
|
|
||||||
Since the Micropython API only supports accesses up to the
|
|
||||||
natural word size of the processor, multiple accesses must be made
|
|
||||||
for 64 bit registers.
|
|
||||||
"""
|
"""
|
||||||
b = self.bws[name]
|
Interface for file generation. Implement the unimplemented functions
|
||||||
if b <= 8:
|
to generate a CSR interface for another language.
|
||||||
return [f'machine.mem8[{self.get_reg(name,num)}]']
|
"""
|
||||||
elif b <= 16:
|
|
||||||
return [f'machine.mem16[{self.get_reg(name,num)}]']
|
def __init__(self, csr, outf):
|
||||||
elif b <= 32:
|
"""
|
||||||
return [f'machine.mem32[{self.get_reg(name,num)}]']
|
:param CSRHandler csr:
|
||||||
elif b <= 64:
|
:param FileIO outf:
|
||||||
return [f'machine.mem32[{self.get_reg(name,num)}]',
|
"""
|
||||||
f'machine.mem32[{self.get_reg(name,num) + 4}]']
|
self.outf = outf
|
||||||
else:
|
self.csr = csr
|
||||||
raise Exception('unsupported width', b)
|
|
||||||
|
|
||||||
def print(self, *args):
|
def print(self, *args):
|
||||||
print(*args, end='', file=self.file)
|
|
||||||
|
|
||||||
def print_write_register(self, indent, varname, name, num):
|
|
||||||
acc = self.get_accessor(name,num)
|
|
||||||
if len(acc) == 1:
|
|
||||||
self.print(f'{indent}{acc[0]} = {varname}\n')
|
|
||||||
else:
|
|
||||||
assert len(acc) == 2
|
|
||||||
self.print(f'{indent}{acc[0]} = {varname} & 0xFFFFFFFF\n')
|
|
||||||
self.print(f'{indent}{acc[1]} = {varname} >> 32\n')
|
|
||||||
|
|
||||||
def print_read_register(self, indent, varname, name, num):
|
|
||||||
acc = self.get_accessor(name,num)
|
|
||||||
if len(acc) == 1:
|
|
||||||
self.print(f'{indent}return {acc[0]}\n')
|
|
||||||
else:
|
|
||||||
assert len(acc) == 2
|
|
||||||
self.print(f'{indent}return {acc[0]} | ({acc[1]} << 32)\n')
|
|
||||||
|
|
||||||
def print_fun(self, optype, reg, pfun):
|
|
||||||
"""Print out a read/write function for an MMIO register.
|
|
||||||
|
|
||||||
:param optype: is set to "read" or "write" (the string).
|
|
||||||
:param reg: is the dictionary containing the register info.
|
|
||||||
:param pfun: is set to {self.print_write_register} or {self.print_read_register}
|
|
||||||
"""
|
"""
|
||||||
name = reg['name']
|
Print to the file specified in the initializer and without
|
||||||
regnum = reg['total']
|
newlines.
|
||||||
exntype = reg['exntype]'
|
"""
|
||||||
|
print(*args, end='', file=self.outf)
|
||||||
|
|
||||||
self.print(f'def {optype}_{name}(')
|
def fun(self, reg, optype):
|
||||||
|
""" Print function for reads/writes to register. """
|
||||||
|
pass
|
||||||
|
def header(self):
|
||||||
|
""" Print header of file. """
|
||||||
|
pass
|
||||||
|
|
||||||
|
def print_file(self):
|
||||||
|
self.print(self.header())
|
||||||
|
for r in self.csr.registers:
|
||||||
|
self.print(self.fun(r, "read"))
|
||||||
|
if not r.read_only:
|
||||||
|
self.print(self.fun(r, "write"))
|
||||||
|
|
||||||
|
class MicropythonGenerator(InterfaceGenerator):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_accessor(self, reg, num):
|
||||||
|
addr = self.csr.get_reg_addr(reg, num)
|
||||||
|
if reg.size in [8, 16, 32]:
|
||||||
|
return [f"machine.mem{reg.size}[{addr}]"]
|
||||||
|
return [f"machine.mem32[{addr}]", f"machine.mem32[{addr + 4}]"]
|
||||||
|
|
||||||
|
def print_write_register(self, indent, varname, reg, num):
|
||||||
|
acc = self.get_accessor(reg, num)
|
||||||
|
if len(acc) == 1:
|
||||||
|
return f'{indent}{acc[0]} = {varname}\n'
|
||||||
|
else:
|
||||||
|
assert len(acc) == 2
|
||||||
|
return f'{indent}{acc[0]} = {varname} & 0xFFFFFFFF\n' + \
|
||||||
|
f'{indent}{acc[1]} = {varname} >> 32\n'
|
||||||
|
|
||||||
|
def print_read_register(self, indent, varname, reg, num):
|
||||||
|
acc = self.get_accessor(reg, num)
|
||||||
|
if len(acc) == 1:
|
||||||
|
return f'{indent}return {acc[0]}\n'
|
||||||
|
else:
|
||||||
|
assert len(acc) == 2
|
||||||
|
return f'{indent}return {acc[0]} | ({acc[1]} << 32)\n'
|
||||||
|
|
||||||
|
def fun(self, reg, optype):
|
||||||
|
rs = ""
|
||||||
|
def a(s):
|
||||||
|
nonlocal rs
|
||||||
|
rs = rs + s
|
||||||
|
a(f'def {optype}_{reg.name}(')
|
||||||
|
|
||||||
printed_argument = False
|
printed_argument = False
|
||||||
if optype == 'write':
|
if optype == 'write':
|
||||||
self.print('val')
|
a('val')
|
||||||
printed_argument = True
|
printed_argument = True
|
||||||
|
pfun = self.print_write_register
|
||||||
|
else:
|
||||||
|
pfun = self.print_read_register
|
||||||
|
|
||||||
if regnum != 1:
|
if reg.number != 1:
|
||||||
if printed_argument:
|
if printed_argument:
|
||||||
self.print(', ')
|
a(', ')
|
||||||
self.print('num')
|
a('num')
|
||||||
self.print('):\n')
|
a('):\n')
|
||||||
|
|
||||||
if regnum == 1:
|
if reg.number == 1:
|
||||||
pfun('\t', 'val', name, None)
|
a(pfun('\t', 'val', reg, None))
|
||||||
else:
|
else:
|
||||||
for i in range(0,regnum):
|
for i in range(0,reg.number):
|
||||||
if i == 0:
|
if i == 0:
|
||||||
self.print(f'\tif ')
|
a(f'\tif ')
|
||||||
else:
|
else:
|
||||||
self.print(f'\telif ')
|
a(f'\telif ')
|
||||||
self.print(f'num == {i}:\n')
|
a(f'num == {i}:\n')
|
||||||
pfun('\t\t', 'val', name, i)
|
a(pfun('\t\t', 'val', reg, i))
|
||||||
self.print(f'\telse:\n')
|
a(f'\telse:\n')
|
||||||
self.print(f'\t\traise {exntype}(regnum)\n')
|
a(f'\t\traise {r.exntype}(regnum)\n')
|
||||||
self.print('\n')
|
a('\n')
|
||||||
|
|
||||||
def print_file(self):
|
return rs
|
||||||
self.print('import machine\n')
|
|
||||||
self.print('class InvalidDACException(Exception):\n\tpass\n')
|
def header(self):
|
||||||
self.print('class InvalidADCException(Exception):\n\tpass\n')
|
return """import machine
|
||||||
for reg in self.registers:
|
class InvalidDACException(Exception):
|
||||||
self.print_fun('read', reg, self.print_read_register)
|
pass
|
||||||
if not reg['read_only']:
|
class InvalidADCException(Exception):
|
||||||
self.print_fun('write', reg, self.print_write_register)
|
pass
|
||||||
|
"""
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
dac_num = 8
|
dac_num = 8
|
||||||
|
@ -172,14 +233,8 @@ if __name__ == "__main__":
|
||||||
MMIORegister("cl_word_in"),
|
MMIORegister("cl_word_in"),
|
||||||
MMIORegister("cl_start_cmd"),
|
MMIORegister("cl_start_cmd"),
|
||||||
MMIORegister("cl_finish_cmd", read_only=True),
|
MMIORegister("cl_finish_cmd", read_only=True),
|
||||||
|
|
||||||
# {"read_only": False, "name": "wf_arm", "total": dac_num},
|
|
||||||
# {"read_only": False, "name": "wf_halt_on_finish", "total": dac_num},
|
|
||||||
# {"read_only": True, "name": "wf_finished", "total": dac_num},
|
|
||||||
# {"read_only": True, "name": "wf_running", "total": dac_num},
|
|
||||||
# {"read_only": False, "name": "wf_time_to_wait", "total": dac_num},
|
|
||||||
# {"read_only": False, "name": "wf_refresh_start", "total": dac_num},
|
|
||||||
# {"read_only": True, "name": "wf_refresh_finished", "total": dac_num},
|
|
||||||
# {"read_only": False, "name": "wf_start_addr", "total": dac_num},
|
|
||||||
]
|
]
|
||||||
MicroPythonCSRGenerator("csr.json", "csr_bitwidth.json", registers, sys.stdout).print_file()
|
csrh = CSRHandler(sys.argv[1], sys.argv[2], registers)
|
||||||
|
for r in registers:
|
||||||
|
csrh.update_reg(r)
|
||||||
|
MicropythonGenerator(csrh, sys.stdout).print_file()
|
||||||
|
|
Loading…
Reference in New Issue