Merge FPGA repository back into UHD repository
The FPGA codebase was removed from the UHD repository in 2014 to reduce the size of the repository. However, over the last half-decade, the split between the repositories has proven more burdensome than it has been helpful. By merging the FPGA code back, it will be possible to create atomic commits that touch both FPGA and UHD codebases. Continuous integration testing is also simplified by merging the repositories, because it was previously difficult to automatically derive the correct UHD branch when testing a feature branch on the FPGA repository. This commit also updates the license files and paths therein. We are therefore merging the repositories again. Future development for FPGA code will happen in the same repository as the UHD host code and MPM code. == Original Codebase and Rebasing == The original FPGA repository will be hosted for the foreseeable future at its original local location: https://github.com/EttusResearch/fpga/ It can be used for bisecting, reference, and a more detailed history. The final commit from said repository to be merged here is 05003794e2da61cabf64dd278c45685a7abad7ec. This commit is tagged as v4.0.0.0-pre-uhd-merge. If you have changes in the FPGA repository that you want to rebase onto the UHD repository, simply run the following commands: - Create a directory to store patches (this should be an empty directory): mkdir ~/patches - Now make sure that your FPGA codebase is based on the same state as the code that was merged: cd src/fpga # Or wherever your FPGA code is stored git rebase v4.0.0.0-pre-uhd-merge Note: The rebase command may look slightly different depending on what exactly you're trying to rebase. - Create a patch set for your changes versus v4.0.0.0-pre-uhd-merge: git format-patch v4.0.0.0-pre-uhd-merge -o ~/patches Note: Make sure that only patches are stored in your output directory. It should otherwise be empty. Make sure that you picked the correct range of commits, and only commits you wanted to rebase were exported as patch files. - Go to the UHD repository and apply the patches: cd src/uhd # Or wherever your UHD repository is stored git am --directory fpga ~/patches/* rm -rf ~/patches # This is for cleanup == Contributors == The following people have contributed mainly to these files (this list is not complete): Co-authored-by: Alex Williams <alex.williams@ni.com> Co-authored-by: Andrej Rode <andrej.rode@ettus.com> Co-authored-by: Ashish Chaudhari <ashish@ettus.com> Co-authored-by: Ben Hilburn <ben.hilburn@ettus.com> Co-authored-by: Ciro Nishiguchi <ciro.nishiguchi@ni.com> Co-authored-by: Daniel Jepson <daniel.jepson@ni.com> Co-authored-by: Derek Kozel <derek.kozel@ettus.com> Co-authored-by: EJ Kreinar <ej@he360.com> Co-authored-by: Humberto Jimenez <humberto.jimenez@ni.com> Co-authored-by: Ian Buckley <ian.buckley@gmail.com> Co-authored-by: Jörg Hofrichter <joerg.hofrichter@ni.com> Co-authored-by: Jon Kiser <jon.kiser@ni.com> Co-authored-by: Josh Blum <josh@joshknows.com> Co-authored-by: Jonathon Pendlum <jonathan.pendlum@ettus.com> Co-authored-by: Martin Braun <martin.braun@ettus.com> Co-authored-by: Matt Ettus <matt@ettus.com> Co-authored-by: Michael West <michael.west@ettus.com> Co-authored-by: Moritz Fischer <moritz.fischer@ettus.com> Co-authored-by: Nick Foster <nick@ettus.com> Co-authored-by: Nicolas Cuervo <nicolas.cuervo@ettus.com> Co-authored-by: Paul Butler <paul.butler@ni.com> Co-authored-by: Paul David <paul.david@ettus.com> Co-authored-by: Ryan Marlow <ryan.marlow@ettus.com> Co-authored-by: Sugandha Gupta <sugandha.gupta@ettus.com> Co-authored-by: Sylvain Munaut <tnt@246tNt.com> Co-authored-by: Trung Tran <trung.tran@ettus.com> Co-authored-by: Vidush Vishwanath <vidush.vishwanath@ettus.com> Co-authored-by: Wade Fife <wade.fife@ettus.com> Original-commit: bafa9d95453387814ef25e6b6256ba8db2df612f
This commit is contained in:
co-authored by
Alex Williams
Andrej Rode
Ashish Chaudhari
Ben Hilburn
Ciro Nishiguchi
Daniel Jepson
Derek Kozel
EJ Kreinar
Humberto Jimenez
Ian Buckley
Jörg Hofrichter
Jon Kiser
Josh Blum
Jonathon Pendlum
Matt Ettus
Michael West
Moritz Fischer
Nick Foster
Nicolas Cuervo
Paul Butler
Paul David
Ryan Marlow
Sugandha Gupta
Sylvain Munaut
Trung Tran
Vidush Vishwanath
Wade Fife
parent
74893643ca
commit
6b67702ad7
@@ -0,0 +1,8 @@
|
||||
xsim_proj
|
||||
modelsim_proj
|
||||
build-ip
|
||||
.ip_user_files
|
||||
.Xil
|
||||
xsim.dir
|
||||
work/
|
||||
xvlog.pb
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
# Ettus Research USRP3 FPGA Repository: Licenses
|
||||
|
||||
All code written by Ettus Research within this usrp3 subdirectory of the FPGA
|
||||
repository is licensed as LGPLv3. This repository also contains code from other
|
||||
sources, which is marked appropriately and may be licensed differently.
|
||||
|
||||
If you wish to obtain an alternative license on the components owned by Ettus
|
||||
Research / National Instruments, please contact info@ettus.com.
|
||||
|
||||
## LGPL License Text
|
||||
|
||||
Source: https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
@@ -0,0 +1,170 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2016 Ettus Research LLC
|
||||
#
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
import logging
|
||||
import re
|
||||
import json
|
||||
|
||||
_LIB_DIR = os.path.join("lib")
|
||||
_RFNOC_DIR = os.path.join("lib", "rfnoc")
|
||||
_SIM_DIR = os.path.join("lib", "sim")
|
||||
_BASE_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
_SEARCH_BASE = [_RFNOC_DIR, _SIM_DIR]
|
||||
_LOG = logging.getLogger(os.path.basename(__file__))
|
||||
_LOG.setLevel(logging.INFO)
|
||||
_STDOUT = logging.StreamHandler()
|
||||
_LOG.addHandler(_STDOUT)
|
||||
_FORMATTER = logging.Formatter('[%(name)s] - %(levelname)s - %(message)s')
|
||||
_STDOUT.setFormatter(_FORMATTER)
|
||||
|
||||
|
||||
def match_file(expr, path):
|
||||
""" Match regex expr on all lines of path and return list of matches """
|
||||
matches = []
|
||||
with open(path, 'rb') as my_file:
|
||||
for line in my_file:
|
||||
match = expr.match(line)
|
||||
if match:
|
||||
matches.append(match)
|
||||
return matches
|
||||
|
||||
|
||||
def create_index(paths):
|
||||
""" Create an index of modules in .v/.vhd and dump it to modules.json """
|
||||
hdl_expressions = {
|
||||
re.compile(r".*\.v$"):
|
||||
re.compile(b"module (?P<mod_name>[\\w]+) *$", re.IGNORECASE),
|
||||
re.compile(r".*\.vhd$"):
|
||||
re.compile(b"entity (?P<mod_name>[\\w]+) is *$", re.IGNORECASE)
|
||||
}
|
||||
ignore_dirs = ["build-ip", "sim"]
|
||||
|
||||
modules = {}
|
||||
for path in paths:
|
||||
for root, dirs, files in os.walk(os.path.join(_BASE_DIR, path)):
|
||||
ignore = [my_dir in dirs for my_dir in ignore_dirs]
|
||||
if any(ignore):
|
||||
for index, ignore_me in enumerate(ignore):
|
||||
if not ignore_me:
|
||||
continue
|
||||
dirs.pop(dirs.index(ignore_dirs[index]))
|
||||
for my_file in files:
|
||||
matches = []
|
||||
for key, value in hdl_expressions.items():
|
||||
if key.match(my_file):
|
||||
matches = match_file(value,
|
||||
os.path.join(root, my_file))
|
||||
break
|
||||
|
||||
for match in matches:
|
||||
if match.group("mod_name") in modules:
|
||||
_LOG.error("%s is already in modules",
|
||||
match.group("mod_name"))
|
||||
_LOG.error("Old Path: %s",
|
||||
modules[match.group("mod_name")])
|
||||
_LOG.error("New Path: %s", os.path.join(root, my_file))
|
||||
else:
|
||||
modules.update({
|
||||
match.group("mod_name").decode('utf-8'):
|
||||
os.path.join(root, my_file)
|
||||
})
|
||||
with open("modules.json", "w") as my_file:
|
||||
json.dump(
|
||||
modules, my_file, sort_keys=True, indent=4, separators=(',', ': '))
|
||||
|
||||
|
||||
def call_xsim(path):
|
||||
""" Call make xsim with default environment at path """
|
||||
os.chdir(os.path.join(_BASE_DIR, path))
|
||||
env = os.environ
|
||||
env["REPO_BASE_PATH"] = _BASE_DIR
|
||||
env["DISPLAY_NAME"] = "USRP-XSIM"
|
||||
env["VIVADO_VER"] = "2017.4"
|
||||
env["PRODUCT_ID_MAP"] = "foo/foo/bar/bar"
|
||||
setup_env = os.path.join(_BASE_DIR, "tools", "scripts", "setupenv_base.sh")
|
||||
result = subprocess.Popen(
|
||||
". {setup}; make xsim".format(setup=setup_env), env=env,
|
||||
shell=True).wait()
|
||||
return result
|
||||
|
||||
|
||||
def find_xsims():
|
||||
""" Find testbenches in lib/sim (dirs with Makefile) """
|
||||
sims = {}
|
||||
for basedir in _SEARCH_BASE:
|
||||
for root, _, files in os.walk(os.path.join(_BASE_DIR, basedir)):
|
||||
if "Makefile" in files:
|
||||
sims.update({os.path.basename(root): root})
|
||||
return sims
|
||||
|
||||
|
||||
def run_xsim(args):
|
||||
""" Run xsim for all specified modules """
|
||||
sims = find_xsims()
|
||||
result_all = 0
|
||||
if not isinstance(args.target, list):
|
||||
args.target = [args.target]
|
||||
if "cleanall" in args.target:
|
||||
env = os.environ
|
||||
env["REPO_BASE_PATH"] = _BASE_DIR
|
||||
for name, path in sims.iteritems():
|
||||
_LOG.info("Cleaning %s", name)
|
||||
os.chdir(os.path.join(_BASE_DIR, path))
|
||||
subprocess.Popen("make cleanall", env=env, shell=True).wait()
|
||||
elif "all" in args.target:
|
||||
for name, path in sims.iteritems():
|
||||
_LOG.info("Running %s xsim", name)
|
||||
result = call_xsim(path)
|
||||
if result:
|
||||
result_all = result
|
||||
else:
|
||||
for target in args.target:
|
||||
_LOG.info("Running %s xsim", target)
|
||||
result = call_xsim(sims[target])
|
||||
if result:
|
||||
result_all = result
|
||||
return result_all
|
||||
|
||||
|
||||
def parse_args():
|
||||
""" Parse cmdline arguments"""
|
||||
test_benches = find_xsims()
|
||||
parser = argparse.ArgumentParser()
|
||||
subparser = parser.add_subparsers(dest="command", metavar="")
|
||||
xsim_parser = subparser.add_parser(
|
||||
"xsim", help="Run available testbenches")
|
||||
xsim_parser.add_argument(
|
||||
"target",
|
||||
nargs="+",
|
||||
choices=list(test_benches.keys()) + ["all", "cleanall"],
|
||||
help="Space separated simulation target(s) or all. Available targets: "
|
||||
+ ", ".join(list(test_benches.keys()) + ["all", "cleanall"]),
|
||||
metavar="")
|
||||
index_parser = subparser.add_parser(
|
||||
"index", help="Index available HDL modules")
|
||||
index_parser.add_argument(
|
||||
"create",
|
||||
help="Create a modules.json of available HDL modules in lib/")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
"""Main logic"""
|
||||
args = parse_args()
|
||||
result = 0
|
||||
if args.command == "xsim":
|
||||
result = run_xsim(args)
|
||||
elif args.command == "index":
|
||||
create_index([_LIB_DIR])
|
||||
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(not main())
|
||||
@@ -0,0 +1,71 @@
|
||||
#
|
||||
# Copyright 2015 Ettus Research LLC
|
||||
#
|
||||
|
||||
# Define BASE_DIR to point to the "top" dir
|
||||
BASE_DIR = $(abspath ../../../top)
|
||||
# Include viv_sim_preample after defining BASE_DIR
|
||||
include $(BASE_DIR)/../tools/make/viv_design_builder.mak
|
||||
|
||||
#-------------------------------------------------
|
||||
# Design Specific
|
||||
#-------------------------------------------------
|
||||
ifndef PRODUCT
|
||||
PRODUCT=X310
|
||||
endif
|
||||
|
||||
ARCH=$(XIL_ARCH_${PRODUCT})
|
||||
PART_ID=$(XIL_PART_ID_${PRODUCT})
|
||||
|
||||
# Include makefiles and sources for the DUT and its dependencies
|
||||
include $(LIB_DIR)/control/Makefile.srcs
|
||||
include $(LIB_DIR)/fifo/Makefile.srcs
|
||||
include $(LIB_DIR)/timing/Makefile.srcs
|
||||
include $(LIB_DIR)/packet_proc/Makefile.srcs
|
||||
include $(LIB_DIR)/vita/Makefile.srcs
|
||||
include $(LIB_DIR)/dsp/Makefile.srcs
|
||||
include $(LIB_DIR)/axi/Makefile.srcs
|
||||
include $(LIB_DIR)/radio/Makefile.srcs
|
||||
include $(BASE_DIR)/x300/coregen_dsp/Makefile.srcs
|
||||
|
||||
DESIGN_SRCS = $(abspath \
|
||||
$(FIFO_SRCS) \
|
||||
$(CONTROL_LIB_SRCS) \
|
||||
$(TIMING_SRCS) \
|
||||
$(PACKET_PROC_SRCS) \
|
||||
$(VITA_SRCS) \
|
||||
$(DSP_SRCS) \
|
||||
$(AXI_SRCS) \
|
||||
$(RADIO_SRCS) \
|
||||
$(COREGEN_DSP_SRCS) \
|
||||
)
|
||||
|
||||
TOP_MODULE = radio
|
||||
|
||||
#-------------------------------------------------
|
||||
# IP Specific
|
||||
#-------------------------------------------------
|
||||
# If simulation contains IP, define the IP_DIR and point
|
||||
# it to the base level IP directory
|
||||
IP_DIR = $(BASE_DIR)/x300/ip
|
||||
|
||||
# Include makefiles and sources for all IP components
|
||||
# *after* defining the IP_DIR
|
||||
include $(IP_DIR)/fifo_short_2clk/Makefile.inc
|
||||
include $(IP_DIR)/fifo_4k_2clk/Makefile.inc
|
||||
|
||||
DESIGN_SRCS += $(abspath \
|
||||
$(IP_FIFO_4K_2CLK_SRCS) \
|
||||
$(IP_FIFO_SHORT_2CLK_SRCS) \
|
||||
)
|
||||
|
||||
# DESIGN_SRCS and VERILOG_DEFS must be defined
|
||||
.DEFAULT_GOAL := netlist
|
||||
netlist: .prereqs $(DESIGN_SRCS)
|
||||
$(call BUILD_VIVADO_DESIGN,$(abspath ./build.tcl),$(TOP_MODULE),$(PART_ID))
|
||||
|
||||
clean:
|
||||
@rm -rf build
|
||||
@rm -rf build-ip
|
||||
|
||||
.PHONY: netlist clean
|
||||
@@ -0,0 +1,8 @@
|
||||
# Copyright 2015 Ettus Research
|
||||
|
||||
source $::env(VIV_TOOLS_DIR)/scripts/viv_utils.tcl
|
||||
|
||||
vivado_utils::initialize_project
|
||||
vivado_utils::synthesize_design
|
||||
vivado_utils::write_netlist_outputs
|
||||
vivado_utils::close_batch_project
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
VIVADO_VER=CMDLINE_ARG
|
||||
DISPLAY_NAME="USRP Radio"
|
||||
REPO_BASE_PATH=$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)
|
||||
|
||||
declare -A PRODUCT_ID_MAP
|
||||
PRODUCT_ID_MAP["X300"]="kintex7/xc7k325t/ffg900/-2"
|
||||
PRODUCT_ID_MAP["X310"]="kintex7/xc7k410t/ffg900/-2"
|
||||
|
||||
source $REPO_BASE_PATH/tools/scripts/setupenv_base.sh
|
||||
@@ -0,0 +1,36 @@
|
||||
#
|
||||
# Copyright 2012-2013 Ettus Research LLC
|
||||
# Copyright 2014 Ettus Research, a National Instruments Company
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
#
|
||||
|
||||
##################################################
|
||||
# FIFO Sources
|
||||
##################################################
|
||||
AXI_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/axi/, \
|
||||
axi_chdr_header_trigger.v \
|
||||
axi_chdr_test_pattern.v \
|
||||
axi_defs.v \
|
||||
axi_dma_fifo.v \
|
||||
axi_dma_master.v \
|
||||
axi_replay.v \
|
||||
axi_embed_tlast.v \
|
||||
axi_extract_tlast.v \
|
||||
axi_fast_extract_tlast.v \
|
||||
axi_embed_tlast_tkeep.v \
|
||||
axi_extract_tlast_tkeep.v \
|
||||
axi_fast_fifo.v \
|
||||
axi_to_strobed.v \
|
||||
axis_data_swap.v \
|
||||
axi_dummy.v \
|
||||
strobed_to_axi.v \
|
||||
axi_add_preamble.v \
|
||||
axi_strip_preamble.v \
|
||||
crc_xnor.v \
|
||||
axis_packet_flush.v \
|
||||
axis_shift_register.v \
|
||||
axis_upsizer.v \
|
||||
axis_downsizer.v \
|
||||
axis_width_conv.v \
|
||||
))
|
||||
@@ -0,0 +1,157 @@
|
||||
//
|
||||
// Copyright 2016 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Adds preamble, EOP, and CRC/num_words check
|
||||
// <preamble> <packet> <EOP> [control_chksum,word_count,payload_chksum]
|
||||
// <preamble> = 64'h9E6774129E677412
|
||||
// <EOP> = 64'h2A1D632F2A1D632F
|
||||
|
||||
module axi_add_preamble #(
|
||||
parameter WIDTH=64
|
||||
) (
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
//
|
||||
input [WIDTH-1:0] i_tdata,
|
||||
input i_tlast,
|
||||
input i_tvalid,
|
||||
output i_tready,
|
||||
//
|
||||
output reg [WIDTH-1:0] o_tdata,
|
||||
output o_tvalid,
|
||||
input o_tready
|
||||
);
|
||||
|
||||
function [0:0] cvita_get_has_time;
|
||||
input [63:0] header;
|
||||
cvita_get_has_time = header[61];
|
||||
endfunction
|
||||
|
||||
//States
|
||||
localparam IDLE = 0;
|
||||
localparam PREAMBLE = 1;
|
||||
localparam PASS = 3;
|
||||
localparam EOP = 4;
|
||||
localparam CRC = 5;
|
||||
|
||||
localparam PAYLOAD_WORDCOUNT_WIDTH = 16;
|
||||
localparam PAYLOAD_CHKSUM_WIDTH = 32;
|
||||
localparam CONTROL_CHKSUM_WIDTH = 16;
|
||||
|
||||
reg [2:0] state, next_state;
|
||||
|
||||
reg [PAYLOAD_WORDCOUNT_WIDTH-1:0] word_count;
|
||||
reg [PAYLOAD_WORDCOUNT_WIDTH-1:0] cntrl_length = 16'd2;
|
||||
wire [PAYLOAD_CHKSUM_WIDTH-1:0] payload_chksum;
|
||||
wire [CONTROL_CHKSUM_WIDTH-1:0] control_chksum;
|
||||
|
||||
// Payload LFSR
|
||||
crc_xnor #(.INPUT_WIDTH(WIDTH), .OUTPUT_WIDTH(PAYLOAD_CHKSUM_WIDTH)) payload_chksum_gen (
|
||||
.clk(clk), .rst(word_count<=cntrl_length), .hold(~(i_tready && i_tvalid)),
|
||||
.input_data(i_tdata), .crc_out(payload_chksum)
|
||||
);
|
||||
|
||||
// Control LFSR
|
||||
crc_xnor #(.INPUT_WIDTH(WIDTH), .OUTPUT_WIDTH(CONTROL_CHKSUM_WIDTH)) control_chksum_gen (
|
||||
.clk(clk), .rst(word_count=='d0), .hold(~(i_tready && i_tvalid) || word_count>=cntrl_length),
|
||||
.input_data(i_tdata), .crc_out(control_chksum)
|
||||
);
|
||||
|
||||
//Update control length so control checksum is correct
|
||||
always @(posedge clk) begin
|
||||
if (state == IDLE && i_tvalid)
|
||||
cntrl_length <= cvita_get_has_time(i_tdata) ? 16'd2 : 16'd1;
|
||||
end
|
||||
|
||||
//Note that word_count includes EOP
|
||||
always @(posedge clk) begin
|
||||
if (state == IDLE) begin
|
||||
word_count <= 0;
|
||||
end else if (i_tready && i_tvalid || (o_tready && state == EOP)) begin
|
||||
word_count <= word_count+1;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
state <= IDLE;
|
||||
end else begin
|
||||
state <= next_state;
|
||||
end
|
||||
|
||||
always @(*) begin
|
||||
case(state)
|
||||
IDLE: begin
|
||||
if (i_tvalid) begin
|
||||
next_state = PREAMBLE;
|
||||
end else begin
|
||||
next_state = IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
PREAMBLE: begin
|
||||
if(o_tready) begin
|
||||
next_state = PASS;
|
||||
end else begin
|
||||
next_state = PREAMBLE;
|
||||
end
|
||||
end
|
||||
|
||||
PASS: begin
|
||||
if(i_tready && i_tvalid && i_tlast) begin
|
||||
next_state = EOP;
|
||||
end else begin
|
||||
next_state = PASS;
|
||||
end
|
||||
end
|
||||
|
||||
EOP: begin
|
||||
if(o_tready) begin
|
||||
next_state = CRC;
|
||||
end else begin
|
||||
next_state = EOP;
|
||||
end
|
||||
end
|
||||
|
||||
CRC: begin
|
||||
if(o_tready) begin
|
||||
next_state = IDLE;
|
||||
end else begin
|
||||
next_state = CRC;
|
||||
end
|
||||
end
|
||||
|
||||
default: begin
|
||||
next_state = IDLE;
|
||||
end
|
||||
|
||||
endcase
|
||||
end
|
||||
|
||||
//
|
||||
// Muxes
|
||||
//
|
||||
always @*
|
||||
begin
|
||||
case(state)
|
||||
IDLE: o_tdata = 0;
|
||||
PASS: o_tdata = i_tdata;
|
||||
PREAMBLE: o_tdata = 64'h9E6774129E677412;
|
||||
EOP: o_tdata = 64'h2A1D632F2A1D632F;
|
||||
CRC: o_tdata = {control_chksum,word_count,payload_chksum};
|
||||
default: o_tdata = 0;
|
||||
|
||||
endcase
|
||||
end
|
||||
|
||||
assign o_tvalid = (state == PASS) ? i_tvalid : (state != IDLE);
|
||||
assign i_tready = (state == PASS) ? o_tready : 1'b0;
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
|
||||
// Copyright 2014 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
|
||||
module axi_chdr_header_trigger
|
||||
#(
|
||||
parameter WIDTH=64,
|
||||
parameter SID=0
|
||||
)
|
||||
(input clk, input reset, input clear,
|
||||
input [WIDTH-1:0] i_tdata, input i_tlast, input i_tvalid, input i_tready,
|
||||
output trigger
|
||||
);
|
||||
|
||||
|
||||
reg state;
|
||||
localparam IDLE = 0;
|
||||
localparam RUN = 1;
|
||||
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset | clear)
|
||||
state <= IDLE;
|
||||
else
|
||||
case (state)
|
||||
IDLE :
|
||||
if(i_tvalid && i_tready)
|
||||
state <= RUN;
|
||||
|
||||
RUN :
|
||||
if(i_tready && i_tvalid && i_tlast)
|
||||
state <= IDLE;
|
||||
|
||||
default :
|
||||
state <= IDLE;
|
||||
endcase // case (state)
|
||||
|
||||
assign trigger = i_tvalid && i_tready && (state == IDLE) && (i_tdata[15:0] != SID);
|
||||
|
||||
endmodule // axi_chdr_header_trigger
|
||||
@@ -0,0 +1,505 @@
|
||||
//
|
||||
// Copyright 2014 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
//
|
||||
// Synthesizable test pattern generator and checker
|
||||
// for AXI-Stream that can be used to test transparent blocks
|
||||
// (FIFOs, switches, etc)
|
||||
//
|
||||
|
||||
module axi_chdr_test_pattern #(
|
||||
parameter SR_BASE = 8'h0, //Base address for settings in this module
|
||||
parameter DELAY_MODE = "DYNAMIC", //Are delays configurable at runtime {STATIC, DYNAMIC}
|
||||
parameter SID_MODE = "DYNAMIC", //Is the SID configurable at runtime {STATIC, DYNAMIC}
|
||||
parameter STATIC_SID = 32'h0, //SID Value if it is static
|
||||
parameter BW_COUNTER = 1 //Instantiate counters to measure bandwidth (Cycles of Data Xfer / Total cycles)
|
||||
) (
|
||||
input clk,
|
||||
input reset,
|
||||
|
||||
// AXI stream to hook up to input of DUT
|
||||
output reg [63:0] i_tdata,
|
||||
output reg i_tlast,
|
||||
output reg i_tvalid,
|
||||
input i_tready,
|
||||
|
||||
// AXI stream to hook up to output of DUT
|
||||
input [63:0] o_tdata,
|
||||
input o_tlast,
|
||||
input o_tvalid,
|
||||
output reg o_tready,
|
||||
|
||||
//Settings bus interface
|
||||
input set_stb,
|
||||
input [7:0] set_addr,
|
||||
input [31:0] set_data,
|
||||
|
||||
// Test flags
|
||||
output reg running, //Test is currently in progress
|
||||
output reg done, //(Sticky) Test has finished executing
|
||||
output reg [1:0] error, //Error code from last test execution
|
||||
|
||||
output [127:0] status_vtr, //More information about test failure.
|
||||
output [95:0] bw_ratio //Bandwidth counter info
|
||||
);
|
||||
|
||||
//
|
||||
// Error Codes
|
||||
//
|
||||
localparam ERR_SUCCESS = 0;
|
||||
localparam ERR_DATA_MISMATCH = 1;
|
||||
localparam ERR_SIZE_MISMATCH_TOO_LONG = 2;
|
||||
localparam ERR_SIZE_MISMATCH_TOO_SHORT = 3;
|
||||
|
||||
localparam ERR_TIMEOUT_LOG2 = 10;
|
||||
|
||||
//
|
||||
// Settings
|
||||
//
|
||||
wire bist_size_ramp;
|
||||
wire [1:0] bist_test_patt;
|
||||
wire [12:0] bist_max_pkt_size;
|
||||
wire bist_go, bist_cont, bist_ctrl_wr;
|
||||
wire [1:0] bist_ctrl_reserved;
|
||||
wire [17:0] bist_max_pkts;
|
||||
wire [15:0] bist_tx_pkt_delay;
|
||||
wire [7:0] bist_rx_samp_delay;
|
||||
wire [31:0] bist_cvita_sid;
|
||||
|
||||
localparam TEST_PATT_ZERO_ONE = 2'd0;
|
||||
localparam TEST_PATT_CHECKERBOARD = 2'd1;
|
||||
localparam TEST_PATT_COUNT = 2'd2;
|
||||
localparam TEST_PATT_COUNT_INV = 2'd3;
|
||||
|
||||
// SETTING: Test Control Register
|
||||
// Fields:
|
||||
// - [0] : (Strobe) Start the test if 1, otherwise stop a running test.
|
||||
// If no test is running then reset the status. (Reseting a
|
||||
// continuously running test requires two writes to this reg)
|
||||
// - [1] : Start the test in continuous mode. (Run until reset or failure)
|
||||
// - [3:2] : <Unused>
|
||||
// - [5:4] : Test pattern:
|
||||
// * 00 = Zeros and Ones (0x0000000000000000 <-> 0xFFFFFFFFFFFFFFFF)
|
||||
// * 01 = Checkerboard (0x5555555555555555 <-> 0xAAAAAAAAAAAAAAAA)
|
||||
// * 10 = Counter (Each byte will count up)
|
||||
// * 11 = Invert Counter (Each byte will count up and invert)
|
||||
setting_reg #(
|
||||
.my_addr(SR_BASE + 0), .width(6), .at_reset(3'b0)
|
||||
) reg_ctrl (
|
||||
.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out({bist_test_patt, bist_ctrl_reserved, bist_cont, bist_go}),.changed(bist_ctrl_wr)
|
||||
);
|
||||
|
||||
wire bist_start = bist_ctrl_wr & bist_go;
|
||||
wire bist_clear = bist_ctrl_wr & ~bist_go;
|
||||
|
||||
// SETTING: Test Packet Configuration Register
|
||||
// Fields:
|
||||
// - [17:0] : Number of packets to transfer per BIST execution
|
||||
// - [30:18] : Max number of bytes of payload per packet
|
||||
// - [31] : Send variable (ramping) sized packets
|
||||
setting_reg #(
|
||||
.my_addr(SR_BASE + 1), .width(32), .at_reset(32'b0)
|
||||
) reg_pkt_config (
|
||||
.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out({bist_size_ramp, bist_max_pkt_size, bist_max_pkts}),.changed()
|
||||
);
|
||||
|
||||
generate if (DELAY_MODE == "DYNAMIC") begin
|
||||
// SETTING: Delay Register
|
||||
// Fields:
|
||||
// - [15:0] : Number of cycles to wait between generating consecutive *packets*
|
||||
// - [23:16] : Number of cycles to wait between consuming consecutive *samples*
|
||||
setting_reg #(
|
||||
.my_addr(SR_BASE + 2), .width(24), .at_reset(24'b0)
|
||||
) reg_delay (
|
||||
.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out({bist_rx_samp_delay, bist_tx_pkt_delay}),.changed()
|
||||
);
|
||||
end else begin
|
||||
assign {bist_rx_samp_delay, bist_tx_pkt_delay} = 24'h0;
|
||||
end endgenerate
|
||||
|
||||
generate if (SID_MODE == "DYNAMIC") begin
|
||||
// SETTING: CHDR Stream ID Register
|
||||
// Fields:
|
||||
// - [31:0] : Stream ID to attach to CHDR packets
|
||||
setting_reg #(
|
||||
.my_addr(SR_BASE + 3), .width(32), .at_reset(32'b0)
|
||||
) reg_sid (
|
||||
.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(bist_cvita_sid),.changed()
|
||||
);
|
||||
end else begin
|
||||
assign bist_cvita_sid = STATIC_SID;
|
||||
end endgenerate
|
||||
|
||||
//
|
||||
// State
|
||||
//
|
||||
localparam TX_IDLE = 3'd0;
|
||||
localparam TX_START = 3'd1;
|
||||
localparam TX_ACTIVE = 3'd2;
|
||||
localparam TX_GAP = 3'd3;
|
||||
localparam TX_DONE = 3'd4;
|
||||
localparam TX_WAIT = 3'd5;
|
||||
|
||||
localparam RX_IDLE = 3'd0;
|
||||
localparam RX_ACTIVE = 3'd1;
|
||||
localparam RX_FAIL = 3'd2;
|
||||
localparam RX_DONE = 3'd3;
|
||||
localparam RX_WAIT = 3'd4;
|
||||
|
||||
reg [2:0] tx_state, rx_state;
|
||||
reg [ERR_TIMEOUT_LOG2-1:0] err_timeout;
|
||||
reg [1:0] test_pattern;
|
||||
reg rearm_test;
|
||||
|
||||
reg [17:0] tx_pkt_cnt, rx_pkt_cnt;
|
||||
reg [13:0] tx_byte_cnt, rx_byte_cnt;
|
||||
reg [23:0] test_run_cnt;
|
||||
reg [15:0] tx_delay;
|
||||
reg [7:0] rx_delay;
|
||||
wire [63:0] tx_cvita_hdr, rx_cvita_hdr;
|
||||
|
||||
wire tx_next_pkt_cond, rx_next_pkt_cond;
|
||||
assign tx_next_pkt_cond = (tx_byte_cnt[12:3] == bist_max_pkt_size[12:3]) || //Packet size reaches max OR
|
||||
(bist_size_ramp && ({7'h0, tx_byte_cnt[13:3]} == tx_pkt_cnt)); //Packet size / 8 == Packet Count
|
||||
assign rx_next_pkt_cond = (rx_byte_cnt[12:3] == bist_max_pkt_size[12:3]) ||
|
||||
(bist_size_ramp && ({7'h0, rx_byte_cnt[13:3]} == rx_pkt_cnt));
|
||||
|
||||
wire tx_test_done_cond, rx_test_done_cond;
|
||||
assign tx_test_done_cond = (tx_pkt_cnt == bist_max_pkts);
|
||||
assign rx_test_done_cond = (rx_pkt_cnt == bist_max_pkts);
|
||||
|
||||
reg [63:0] tx_data_next, rx_data_exp;
|
||||
always @(*) begin
|
||||
case (test_pattern)
|
||||
TEST_PATT_ZERO_ONE: begin
|
||||
tx_data_next <= {8{tx_byte_cnt[3] ? 8'h00 : 8'hFF}};
|
||||
rx_data_exp <= {8{rx_byte_cnt[3] ? 8'h00 : 8'hFF}};
|
||||
end
|
||||
TEST_PATT_CHECKERBOARD: begin
|
||||
tx_data_next <= {32{tx_byte_cnt[3] ? 2'b01 : 2'b10}};
|
||||
rx_data_exp <= {32{rx_byte_cnt[3] ? 2'b01 : 2'b10}};
|
||||
end
|
||||
TEST_PATT_COUNT: begin
|
||||
tx_data_next <= {8{tx_byte_cnt[10:3]}};
|
||||
rx_data_exp <= {8{rx_byte_cnt[10:3]}};
|
||||
end
|
||||
TEST_PATT_COUNT_INV: begin
|
||||
tx_data_next <= {8{(tx_byte_cnt[3] ? 8'hFF : 8'h00) ^ tx_byte_cnt[10:3]}};
|
||||
rx_data_exp <= {8{(rx_byte_cnt[3] ? 8'hFF : 8'h00) ^ rx_byte_cnt[10:3]}};
|
||||
end
|
||||
default: begin
|
||||
tx_data_next <= 64'd0;
|
||||
rx_data_exp <= 64'd0;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
//NOTE: We always attach the max size in the packet header for simplicity.
|
||||
// This will not work with state machines that validate the packet length in the
|
||||
// header with the tlast position.
|
||||
assign tx_cvita_hdr = {4'h0, tx_pkt_cnt[11:0], 2'b00, bist_max_pkt_size, bist_cvita_sid};
|
||||
assign rx_cvita_hdr = {4'h0, rx_pkt_cnt[11:0], 2'b00, bist_max_pkt_size, bist_cvita_sid};
|
||||
|
||||
reg [63:0] o_tdata_fail;
|
||||
assign status_vtr = { //Status at the time of failure
|
||||
o_tdata_fail, //[127:64]
|
||||
test_run_cnt, //[63:40]
|
||||
rx_data_exp[7:0], //[39:32]
|
||||
rx_pkt_cnt, //[31:14]
|
||||
rx_byte_cnt //[13:0]
|
||||
};
|
||||
|
||||
//-------------------------------------------------------
|
||||
// Transmitter
|
||||
//-------------------------------------------------------
|
||||
always @(posedge clk) begin
|
||||
if (reset | (bist_clear & ~rearm_test)) begin
|
||||
tx_delay <= 0;
|
||||
tx_pkt_cnt <= 0;
|
||||
tx_byte_cnt <= 0;
|
||||
i_tdata <= 64'h0;
|
||||
i_tlast <= 1'b0;
|
||||
i_tvalid <= 1'b0;
|
||||
tx_state <= TX_IDLE;
|
||||
end else begin
|
||||
case(tx_state)
|
||||
TX_IDLE: begin
|
||||
tx_delay <= 0;
|
||||
tx_pkt_cnt <= 1;
|
||||
tx_byte_cnt <= 0;
|
||||
i_tdata <= 64'h0;
|
||||
i_tlast <= 1'b0;
|
||||
i_tvalid <= 1'b0;
|
||||
// Run when bist_start asserted.
|
||||
if (bist_start | rearm_test) begin
|
||||
tx_state <= TX_START;
|
||||
test_pattern <= bist_test_patt;
|
||||
end
|
||||
end // case: TX_IDLE
|
||||
|
||||
// START signal is asserted.
|
||||
// Now need to start transmiting a packet.
|
||||
TX_START: begin
|
||||
// At the next clock edge drive first beat of new packet onto HDR bus.
|
||||
i_tlast <= 1'b0;
|
||||
i_tvalid <= 1'b1;
|
||||
tx_byte_cnt <= tx_byte_cnt + 8;
|
||||
i_tdata <= tx_cvita_hdr;
|
||||
tx_state <= TX_ACTIVE;
|
||||
end
|
||||
|
||||
// Valid data is (already) being driven onto the CHDR bus.
|
||||
// i_tlast may also be driven asserted if current data count has reached EOP.
|
||||
// Watch i_tready to see when it's consumed.
|
||||
// When packets are consumed increment data counter or transition state if
|
||||
// EOP has sucsesfully concluded.
|
||||
TX_ACTIVE: begin
|
||||
i_tvalid <= 1'b1; // Always assert tvalid
|
||||
if (i_tready) begin
|
||||
i_tdata <= tx_data_next;
|
||||
// Will this next beat be the last in a packet?
|
||||
if (tx_next_pkt_cond) begin
|
||||
tx_byte_cnt <= 0;
|
||||
i_tlast <= 1'b1;
|
||||
tx_state <= TX_GAP;
|
||||
end else begin
|
||||
tx_byte_cnt <= tx_byte_cnt + 8;
|
||||
i_tlast <= 1'b0;
|
||||
tx_state <= TX_ACTIVE;
|
||||
end
|
||||
end else begin
|
||||
//Keep driving all CHDR bus signals as-is until i_tready is asserted.
|
||||
tx_state <= TX_ACTIVE;
|
||||
end
|
||||
end // case: TX_ACTIVE
|
||||
|
||||
// Force an inter-packet gap between packets in a BIST sequence where tvalid is driven low.
|
||||
// As we leave this state check if all packets in BIST sequence have been generated yet,
|
||||
// and if so go to done state.
|
||||
TX_GAP: begin
|
||||
if (i_tready) begin
|
||||
i_tvalid <= 1'b0;
|
||||
i_tdata <= 64'h0;
|
||||
i_tlast <= 1'b0;
|
||||
tx_pkt_cnt <= tx_pkt_cnt + 1;
|
||||
|
||||
if (tx_test_done_cond) begin
|
||||
tx_state <= TX_DONE;
|
||||
end else begin
|
||||
tx_state <= TX_WAIT;
|
||||
tx_delay <= bist_tx_pkt_delay;
|
||||
end
|
||||
end else begin // if (i_tready)
|
||||
tx_state <= TX_GAP;
|
||||
end
|
||||
end // case: TX_GAP
|
||||
|
||||
// Simulate inter packet gap in real UHD system
|
||||
TX_WAIT: begin
|
||||
if (tx_delay == 0)
|
||||
tx_state <= TX_START;
|
||||
else begin
|
||||
tx_delay <= tx_delay - 1;
|
||||
tx_state <= TX_WAIT;
|
||||
end
|
||||
end
|
||||
|
||||
// Complete test pattern BIST sequence has been transmitted.
|
||||
// Sit in this state until the RX side consumes all packets except
|
||||
// for when the test is running in continuous mode.
|
||||
TX_DONE: begin
|
||||
i_tvalid <= 1'b0;
|
||||
i_tlast <= 1'b0;
|
||||
i_tdata <= 64'd0;
|
||||
|
||||
if (running & ~rearm_test) begin
|
||||
tx_state <= TX_DONE;
|
||||
end else begin
|
||||
tx_state <= TX_IDLE;
|
||||
end
|
||||
end
|
||||
endcase // case (tx_state)
|
||||
end
|
||||
end
|
||||
|
||||
//-------------------------------------------------------
|
||||
// Receiver
|
||||
//-------------------------------------------------------
|
||||
always @(posedge clk) begin
|
||||
if (reset | (bist_clear & ~rearm_test)) begin
|
||||
rx_delay <= 0;
|
||||
rx_pkt_cnt <= 0;
|
||||
rx_byte_cnt <= 0;
|
||||
o_tdata_fail <= 64'h0;
|
||||
o_tready <= 1'b0;
|
||||
error <= ERR_SUCCESS;
|
||||
done <= 1'b0;
|
||||
rx_state <= RX_IDLE;
|
||||
err_timeout <= {ERR_TIMEOUT_LOG2{1'b0}};
|
||||
test_run_cnt <= 0;
|
||||
end else begin
|
||||
case(rx_state)
|
||||
RX_IDLE: begin
|
||||
rx_delay <= 0;
|
||||
rx_pkt_cnt <= 1;
|
||||
rx_byte_cnt <= 0;
|
||||
o_tdata_fail <= 64'h0;
|
||||
o_tready <= 1'b0;
|
||||
error <= ERR_SUCCESS;
|
||||
done <= 1'b0;
|
||||
err_timeout <= {ERR_TIMEOUT_LOG2{1'b0}};
|
||||
// Not accepting data whilst Idle,
|
||||
// switch to active when packet arrives
|
||||
if (o_tvalid) begin
|
||||
o_tready <= 1'b1;
|
||||
rx_state <= RX_ACTIVE;
|
||||
end else begin
|
||||
rx_state <= RX_IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
RX_ACTIVE: begin
|
||||
o_tready <= 1'b1;
|
||||
if (o_tvalid) begin
|
||||
if (o_tdata != (rx_byte_cnt == 0 ? rx_cvita_hdr : rx_data_exp)) begin
|
||||
$display("axis_test_pattern: o_tdata: %x != expected: %x @ time: %d", o_tdata, rx_data_exp, $time);
|
||||
error <= ERR_DATA_MISMATCH;
|
||||
rx_state <= RX_FAIL;
|
||||
o_tdata_fail <= o_tdata;
|
||||
end else if (rx_next_pkt_cond) begin
|
||||
// Last not asserted when it should be!
|
||||
if (~(o_tlast === 1)) begin
|
||||
$display("axis_test_pattern: o_tlast not asserted when it should be @ time: %d", $time);
|
||||
error <= ERR_SIZE_MISMATCH_TOO_LONG;
|
||||
rx_state <= RX_FAIL;
|
||||
end else begin
|
||||
// End of packet, set up to RX next
|
||||
rx_byte_cnt <= 0;
|
||||
rx_pkt_cnt <= rx_pkt_cnt + 1;
|
||||
rx_delay <= bist_rx_samp_delay;
|
||||
if (rx_test_done_cond) begin
|
||||
rx_state <= rearm_test ? RX_IDLE : RX_DONE;
|
||||
error <= ERR_SUCCESS;
|
||||
test_run_cnt <= test_run_cnt + 1;
|
||||
end else begin
|
||||
rx_state <= RX_WAIT;
|
||||
end
|
||||
o_tready <= 1'b0;
|
||||
end
|
||||
end else begin
|
||||
// ...last asserted when it should not be!
|
||||
if (~(o_tlast === 0)) begin
|
||||
$display("axis_test_pattern: o_tlast asserted when it should not be @ time: %d", $time);
|
||||
error <= ERR_SIZE_MISMATCH_TOO_SHORT;
|
||||
rx_state <= RX_FAIL;
|
||||
end else begin
|
||||
// Still in packet body
|
||||
rx_byte_cnt <= rx_byte_cnt + 8;
|
||||
rx_delay <= bist_rx_samp_delay;
|
||||
if (bist_rx_samp_delay == 0) begin
|
||||
rx_state <= RX_ACTIVE;
|
||||
end else begin
|
||||
rx_state <= RX_WAIT;
|
||||
o_tready <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
// Nothing to do this cycle
|
||||
rx_state <= RX_ACTIVE;
|
||||
end
|
||||
end // case: RX_ACTIVE
|
||||
|
||||
// To simulate the radio consuming samples at a steady rate set by the decimation
|
||||
// have a programable delay here
|
||||
RX_WAIT: begin
|
||||
if (rx_delay == 0) begin
|
||||
rx_state <= RX_ACTIVE;
|
||||
o_tready <= 1'b1;
|
||||
end else begin
|
||||
rx_delay <= rx_delay - 1;
|
||||
rx_state <= RX_WAIT;
|
||||
end
|
||||
end
|
||||
|
||||
RX_FAIL: begin
|
||||
//The test has failed but the sender still has packets en route
|
||||
//Consume all of them before asserting done. Packets could be
|
||||
//malformed so just blindly consume lines and count cycles of
|
||||
//gaps. If non-valid cycles are more than 2^ERR_TIMEOUT_LOG2 then stop.
|
||||
o_tready <= 1'b1;
|
||||
if (~o_tvalid) begin
|
||||
if (err_timeout == {ERR_TIMEOUT_LOG2{1'b1}}) begin
|
||||
rx_state <= RX_DONE;
|
||||
end
|
||||
err_timeout <= err_timeout + 1;
|
||||
end
|
||||
end
|
||||
|
||||
RX_DONE: begin
|
||||
o_tready <= 1'b0;
|
||||
done <= 1'b1;
|
||||
//The only way to exit this state is by asserting bist_clear
|
||||
end
|
||||
endcase // case (rx_state)
|
||||
end
|
||||
end
|
||||
|
||||
//-------------------------------------------------------
|
||||
// Status Monitor
|
||||
//-------------------------------------------------------
|
||||
always @(posedge clk) begin
|
||||
if (reset)
|
||||
running <= 1'b0;
|
||||
else if (tx_state == TX_START)
|
||||
running <= 1'b1;
|
||||
else if (rx_state == RX_DONE)
|
||||
running <= 1'b0;
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (reset | bist_clear)
|
||||
rearm_test <= 1'b0;
|
||||
else if (bist_start & bist_cont)
|
||||
rearm_test <= 1'b1;
|
||||
else if (rx_state == RX_FAIL)
|
||||
rearm_test <= 1'b0;
|
||||
end
|
||||
|
||||
//-------------------------------------------------------
|
||||
// Bandwidth Counter
|
||||
//-------------------------------------------------------
|
||||
generate if (BW_COUNTER) begin
|
||||
reg [47:0] word_count, cyc_count;
|
||||
assign bw_ratio = {word_count, cyc_count};
|
||||
|
||||
//Count number of lines transferred
|
||||
always @(posedge clk) begin
|
||||
if (reset| (bist_clear & ~rearm_test) | bist_start)
|
||||
word_count <= 48'd0;
|
||||
else if (o_tvalid && rx_state == RX_ACTIVE)
|
||||
word_count <= word_count + 48'd1;
|
||||
end
|
||||
|
||||
//Count cycles as long as test is running
|
||||
always @(posedge clk) begin
|
||||
if (reset| (bist_clear & ~rearm_test) | bist_start)
|
||||
cyc_count <= 48'd0;
|
||||
else if (rx_state == RX_ACTIVE || rx_state == RX_WAIT)
|
||||
cyc_count <= cyc_count + 48'd1;
|
||||
end
|
||||
end else begin
|
||||
assign bw_ratio = 96'h0;
|
||||
end endgenerate
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// Copyright 2014 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
//
|
||||
// AXI4 Burst enumeration
|
||||
//
|
||||
`define AXI4_BURST_FIXED 2'b00
|
||||
`define AXI4_BURST_INCR 2'b01
|
||||
`define AXI4_BURST_WRAP 2'b10
|
||||
`define AXI4_BURST_RSVD 2'b11
|
||||
//
|
||||
// AXI4 response code enumeration
|
||||
//
|
||||
`define AXI4_RESP_OKAY 2'b00
|
||||
`define AXI4_RESP_EXOKAY 2'b01
|
||||
`define AXI4_RESP_SLVERR 2'b10
|
||||
`define AXI4_RESP_DECERR 2'b11
|
||||
//
|
||||
// AXI4 lock enumeration
|
||||
//
|
||||
`define AXI4_LOCK_NORMAL 1'b0
|
||||
`define AXI4_LOCK_EXCLUSIVE 1'b1
|
||||
//
|
||||
// AXI4 memory attrubutes
|
||||
//
|
||||
`define AXI4_CACHE_ALLOCATE 4'h8
|
||||
`define AXI4_CACHE_OTHER_ALLOCATE 4'h4
|
||||
`define AXI4_CACHE_MODIFIABLE 4'h2
|
||||
`define AXI4_CACHE_BUFFERABLE 4'h1
|
||||
//
|
||||
// AXI4 PROT attributes
|
||||
//
|
||||
`define AXI4_PROT_PRIVILEDGED 3'h1
|
||||
`define AXI4_PROT_NON_SECURE 3'h2
|
||||
`define AXI4_PROT_INSTRUCTION 3'h4
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,548 @@
|
||||
//
|
||||
// Copyright 2014 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
`include "axi_defs.v"
|
||||
|
||||
`define DEBUG if (0)
|
||||
|
||||
module axi_dma_master #(
|
||||
parameter AWIDTH = 32,
|
||||
parameter DWIDTH = 64
|
||||
) (
|
||||
input aclk, // Global AXI clock
|
||||
input areset, // Global AXI reset
|
||||
//
|
||||
// AXI Write address channel
|
||||
//
|
||||
output [0 : 0] m_axi_awid, // Write address ID. This signal is the identification tag for the write address signals
|
||||
output reg [AWIDTH-1 : 0] m_axi_awaddr, // Write address. The write address gives the address of the first transfer in a write burst
|
||||
output reg [7 : 0] m_axi_awlen, // Burst length. The burst length gives the exact number of transfers in a burst.
|
||||
output [2 : 0] m_axi_awsize, // Burst size. This signal indicates the size of each transfer in the burst.
|
||||
output [1 : 0] m_axi_awburst, // Burst type. The burst type and the size information, determine how the address is calculated
|
||||
output [0 : 0] m_axi_awlock, // Lock type. Provides additional information about the atomic characteristics of the transfer.
|
||||
output [3 : 0] m_axi_awcache, // Memory type. This signal indicates how transactions are required to progress
|
||||
output [2 : 0] m_axi_awprot, // Protection type. This signal indicates the privilege and security level of the transaction
|
||||
output [3 : 0] m_axi_awqos, // Quality of Service, QoS. The QoS identifier sent for each write transaction
|
||||
output [3 : 0] m_axi_awregion, // Region identifier. Permits a single physical interface on a slave to be re-used.
|
||||
output [0 : 0] m_axi_awuser, // User signal. Optional User-defined signal in the write address channel.
|
||||
output reg m_axi_awvalid, // Write address valid. This signal indicates that the channel is signaling valid write addr
|
||||
input m_axi_awready, // Write address ready. This signal indicates that the slave is ready to accept an address
|
||||
//
|
||||
// AXI Write data channel.
|
||||
//
|
||||
output [DWIDTH-1 : 0] m_axi_wdata, // Write data
|
||||
output [DWIDTH/8-1 : 0] m_axi_wstrb, // Write strobes. This signal indicates which byte lanes hold valid data.
|
||||
output reg m_axi_wlast, // Write last. This signal indicates the last transfer in a write burst
|
||||
output m_axi_wuser, // User signal. Optional User-defined signal in the write data channel.
|
||||
output m_axi_wvalid, // Write valid. This signal indicates that valid write data and strobes are available.
|
||||
input m_axi_wready, // Write ready. This signal indicates that the slave can accept the write data.
|
||||
//
|
||||
// AXI Write response channel signals
|
||||
//
|
||||
input [0 : 0] m_axi_bid, // Response ID tag. This signal is the ID tag of the write response.
|
||||
input [1 : 0] m_axi_bresp, // Write response. This signal indicates the status of the write transaction.
|
||||
input [0 : 0] m_axi_buser, // User signal. Optional User-defined signal in the write response channel.
|
||||
input m_axi_bvalid, // Write response valid. This signal indicates that the channel is signaling a valid response
|
||||
output reg m_axi_bready, // Response ready. This signal indicates that the master can accept a write response
|
||||
//
|
||||
// AXI Read address channel
|
||||
//
|
||||
output [0 : 0] m_axi_arid, // Read address ID. This signal is the identification tag for the read address group of signals
|
||||
output reg [AWIDTH-1 : 0] m_axi_araddr, // Read address. The read address gives the address of the first transfer in a read burst
|
||||
output reg [7 : 0] m_axi_arlen, // Burst length. This signal indicates the exact number of transfers in a burst.
|
||||
output [2 : 0] m_axi_arsize, // Burst size. This signal indicates the size of each transfer in the burst.
|
||||
output [1 : 0] m_axi_arburst, // Burst type. The burst type and the size information determine how the address for each transfer
|
||||
output [0 : 0] m_axi_arlock, // Lock type. This signal provides additional information about the atomic characteristics
|
||||
output [3 : 0] m_axi_arcache, // Memory type. This signal indicates how transactions are required to progress
|
||||
output [2 : 0] m_axi_arprot, // Protection type. This signal indicates the privilege and security level of the transaction
|
||||
output [3 : 0] m_axi_arqos, // Quality of Service, QoS. QoS identifier sent for each read transaction.
|
||||
output [3 : 0] m_axi_arregion, // Region identifier. Permits a single physical interface on a slave to be re-used
|
||||
output [0 : 0] m_axi_aruser, // User signal. Optional User-defined signal in the read address channel.
|
||||
output reg m_axi_arvalid, // Read address valid. This signal indicates that the channel is signaling valid read addr
|
||||
input m_axi_arready, // Read address ready. This signal indicates that the slave is ready to accept an address
|
||||
//
|
||||
// AXI Read data channel
|
||||
//
|
||||
input [0 : 0] m_axi_rid, // Read ID tag. This signal is the identification tag for the read data group of signals
|
||||
input [DWIDTH-1 : 0] m_axi_rdata, // Read data.
|
||||
input [1 : 0] m_axi_rresp, // Read response. This signal indicates the status of the read transfer
|
||||
input m_axi_rlast, // Read last. This signal indicates the last transfer in a read burst.
|
||||
input [0 : 0] m_axi_ruser, // User signal. Optional User-defined signal in the read data channel.
|
||||
input m_axi_rvalid, // Read valid. This signal indicates that the channel is signaling the required read data.
|
||||
output m_axi_rready, // Read ready. This signal indicates that the master can accept the read data and response
|
||||
//
|
||||
// DMA interface for Write transaction
|
||||
//
|
||||
input [AWIDTH-1:0] write_addr, // Byte address for start of write transaction (should be 64bit alligned)
|
||||
input [7:0] write_count, // Count of 64bit words to write. (minus one)
|
||||
input write_ctrl_valid,
|
||||
output reg write_ctrl_ready,
|
||||
input [DWIDTH-1:0] write_data,
|
||||
input write_data_valid,
|
||||
output write_data_ready,
|
||||
//
|
||||
// DMA interface for Read
|
||||
//
|
||||
input [AWIDTH-1:0] read_addr, // Byte address for start of read transaction (should be 64bit alligned)
|
||||
input [7:0] read_count, // Count of 64bit words to read.
|
||||
input read_ctrl_valid,
|
||||
output reg read_ctrl_ready,
|
||||
output [DWIDTH-1:0] read_data,
|
||||
output read_data_valid,
|
||||
input read_data_ready,
|
||||
//
|
||||
// Debug Bus
|
||||
//
|
||||
output [31:0] debug
|
||||
|
||||
);
|
||||
|
||||
|
||||
localparam AW_IDLE = 0;
|
||||
localparam WAIT_AWREADY = 1;
|
||||
localparam WAIT_BVALID = 2;
|
||||
localparam AW_ERROR = 3;
|
||||
|
||||
reg [1:0] write_addr_state;
|
||||
reg [7:0] write_data_count; // Count write transfers.
|
||||
reg enable_data_write;
|
||||
|
||||
localparam DW_IDLE = 0;
|
||||
localparam DW_RUN = 1;
|
||||
localparam DW_LAST = 2;
|
||||
|
||||
reg [1:0] write_data_state;
|
||||
|
||||
localparam AR_IDLE = 0;
|
||||
localparam WAIT_ARREADY = 1;
|
||||
localparam WAIT_READ_DONE = 2;
|
||||
localparam AR_ERROR = 3;
|
||||
|
||||
reg [1:0] read_addr_state;
|
||||
|
||||
localparam DR_IDLE = 0;
|
||||
localparam DR_RUN = 1;
|
||||
localparam DR_WAIT_ERROR = 2;
|
||||
localparam DR_ERROR = 3;
|
||||
|
||||
reg [1:0] read_data_state;
|
||||
reg [7:0] read_data_count;
|
||||
reg enable_data_read;
|
||||
|
||||
///////////////////////////
|
||||
// DEBUG
|
||||
///////////////////////////
|
||||
assign debug= {24'h0,write_addr_state[1:0],write_data_state[1:0],read_addr_state[1:0],read_data_state[1:0]};
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// AXI Write address channel
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
assign m_axi_awid = 1'b0;
|
||||
assign m_axi_awsize = $clog2(DWIDTH/8);
|
||||
assign m_axi_awburst = `AXI4_BURST_INCR;
|
||||
assign m_axi_awlock = `AXI4_LOCK_NORMAL;
|
||||
assign m_axi_awcache = `AXI4_CACHE_ALLOCATE | `AXI4_CACHE_OTHER_ALLOCATE | `AXI4_CACHE_MODIFIABLE | `AXI4_CACHE_BUFFERABLE;
|
||||
assign m_axi_awprot = `AXI4_PROT_NON_SECURE;
|
||||
assign m_axi_awqos = 4'h0;
|
||||
assign m_axi_awregion = 4'h0;
|
||||
assign m_axi_awuser = 1'b0;
|
||||
|
||||
|
||||
//
|
||||
// AXI Write address state machine
|
||||
//
|
||||
always @(posedge aclk)
|
||||
if (areset) begin
|
||||
write_ctrl_ready <= 1'b0;
|
||||
write_addr_state <= AW_IDLE;
|
||||
m_axi_awaddr <= {AWIDTH{1'b0}};
|
||||
m_axi_awlen[7:0] <= 8'h0;
|
||||
m_axi_awvalid <= 1'b0;
|
||||
m_axi_bready <= 1'b0;
|
||||
end else
|
||||
case (write_addr_state)
|
||||
//
|
||||
// AW_IDLE
|
||||
// We are ready to accept a new write transaction.
|
||||
//
|
||||
AW_IDLE: begin
|
||||
// Premptively accept new write transaction since we are idle.
|
||||
write_ctrl_ready <= 1'b1;
|
||||
// No need to be waiting for a response while idle.
|
||||
m_axi_bready <= 1'b0;
|
||||
// If we are offered a new transaction then.....
|
||||
if (write_ctrl_valid) begin
|
||||
// Drive all the relevent AXI4 write address channel signals next cycle.
|
||||
m_axi_awaddr <= write_addr;
|
||||
m_axi_awlen[7:0] <= {write_count};
|
||||
m_axi_awvalid <= 1'b1;
|
||||
// If the AXI4 write channel is pre-emptively accepting the transaction...
|
||||
if (m_axi_awready == 1'b1) begin
|
||||
// ...go straight to looking for a transaction response...
|
||||
`DEBUG $display("WRITE TRANSACTION: ADDR: %x LEN: %x @ time %d",write_addr,write_count,$time);
|
||||
write_addr_state <= WAIT_BVALID;
|
||||
m_axi_bready <= 1'b1;
|
||||
end else begin
|
||||
// ...otherwise wait to get the transaction accepted.
|
||||
write_addr_state <= WAIT_AWREADY;
|
||||
end
|
||||
end
|
||||
end
|
||||
//
|
||||
// WAIT_AWREADY
|
||||
// Waiting for AXI4 slave to accept new write transaction.
|
||||
//
|
||||
WAIT_AWREADY: begin
|
||||
write_ctrl_ready <= 1'b0;
|
||||
// If the AXI4 write channel is accepting the transaction...
|
||||
if (m_axi_awready == 1'b1) begin
|
||||
// ...go to looking for a transaction response...
|
||||
write_addr_state <= WAIT_BVALID;
|
||||
m_axi_awvalid <= 1'b0;
|
||||
m_axi_bready <= 1'b1;
|
||||
`DEBUG $display("WRITE TRANSACTION: ADDR: %x LEN: %x @ time %d",m_axi_awaddr,m_axi_awlen[7:0],$time);
|
||||
end else begin
|
||||
// ...otherwise wait to get the trasaction accepted.
|
||||
write_addr_state <= WAIT_AWREADY;
|
||||
end
|
||||
end // case: WAIT_AWREADY
|
||||
//
|
||||
// WAIT_BVALID
|
||||
// Write transaction has been accepted, now waiting for a response to signal it's sucsesful.
|
||||
// Ignoring ID tag for the moment
|
||||
//
|
||||
WAIT_BVALID: begin
|
||||
write_ctrl_ready <= 1'b0;
|
||||
m_axi_awvalid <= 1'b0;
|
||||
// Wait for response channel to signal how write transaction went down....
|
||||
if (m_axi_bvalid == 1'b1) begin
|
||||
if ((m_axi_bresp == `AXI4_RESP_OKAY) || (m_axi_bresp == `AXI4_RESP_EXOKAY)) begin
|
||||
// ....it went well, we are ready to start something new.
|
||||
write_addr_state <= AW_IDLE;
|
||||
m_axi_bready <= 1'b0;
|
||||
write_ctrl_ready <= 1'b1; // Ready to run again as soon as we hit idle.
|
||||
end else if ((m_axi_bresp == `AXI4_RESP_SLVERR) || (m_axi_bresp == `AXI4_RESP_DECERR)) begin
|
||||
// ....things got ugly, retreat to an error stat and wait for intervention.
|
||||
write_addr_state <= AW_ERROR;
|
||||
m_axi_bready <= 1'b0;
|
||||
end
|
||||
end else begin
|
||||
write_addr_state <= WAIT_BVALID;
|
||||
m_axi_bready <= 1'b1;
|
||||
end
|
||||
end // case: WAIT_BVALID
|
||||
//
|
||||
// AW_ERROR
|
||||
// Something bad happened, going to need external intervention to restore a safe state.
|
||||
//
|
||||
AW_ERROR: begin
|
||||
write_ctrl_ready <= 1'b0;
|
||||
write_addr_state <= AW_ERROR;
|
||||
m_axi_awaddr <= {AWIDTH{1'b0}};
|
||||
m_axi_awlen[7:0] <= 8'h0;
|
||||
m_axi_awvalid <= 1'b0;
|
||||
m_axi_bready <= 1'b0;
|
||||
end
|
||||
endcase // case(write_addr_state)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// AXI Write data channel
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
assign m_axi_wstrb = {DWIDTH/8{1'b1}};
|
||||
assign m_axi_wuser = 1'b0;
|
||||
|
||||
//
|
||||
// AXI Write data state machine
|
||||
//
|
||||
always @(posedge aclk)
|
||||
if (areset) begin
|
||||
write_data_state <= AW_IDLE;
|
||||
write_data_count <= 1;
|
||||
enable_data_write <= 1'b0;
|
||||
m_axi_wlast <= 1'b0;
|
||||
|
||||
end else
|
||||
case (write_data_state)
|
||||
//
|
||||
// DW_IDLE
|
||||
// Sit in this state until presented with the control details of a new write transaction.
|
||||
//
|
||||
DW_IDLE: begin
|
||||
write_data_count <= 1;
|
||||
m_axi_wlast <= 1'b0;
|
||||
|
||||
if (write_ctrl_valid && write_ctrl_ready) begin
|
||||
enable_data_write <= 1'b1;
|
||||
if (write_count[7:0] == 8'h0) begin
|
||||
// Single transfer transaction
|
||||
write_data_state <= DW_LAST;
|
||||
m_axi_wlast <= 1'b1;
|
||||
end else begin
|
||||
write_data_state <= DW_RUN;
|
||||
end
|
||||
end else begin
|
||||
write_data_state <= DW_IDLE;
|
||||
end
|
||||
end
|
||||
//
|
||||
// DW_RUN
|
||||
//
|
||||
DW_RUN : begin
|
||||
enable_data_write <= 1'b1;
|
||||
m_axi_wlast <= 1'b0;
|
||||
|
||||
if (write_data_valid && m_axi_wready) begin
|
||||
// Single write transfer
|
||||
write_data_count <= write_data_count + 1;
|
||||
|
||||
if (write_data_count == m_axi_awlen[7:0]) begin
|
||||
write_data_state <= DW_LAST;
|
||||
m_axi_wlast <= 1'b1;
|
||||
end else begin
|
||||
write_data_state <= DW_RUN;
|
||||
end
|
||||
end else begin
|
||||
write_data_state <= DW_RUN;
|
||||
end
|
||||
end
|
||||
//
|
||||
// DW_LAST
|
||||
//
|
||||
DW_LAST: begin
|
||||
if (write_data_valid && m_axi_wready) begin
|
||||
enable_data_write <= 1'b0;
|
||||
write_data_state <= DW_IDLE;
|
||||
m_axi_wlast <= 1'b0;
|
||||
end else begin
|
||||
enable_data_write <= 1'b1;
|
||||
write_data_state <= DW_LAST;
|
||||
m_axi_wlast <= 1'b1;
|
||||
end
|
||||
end // case: DW_LAST
|
||||
//
|
||||
default:
|
||||
write_data_state <= DW_IDLE;
|
||||
|
||||
endcase // case(write_data_state)
|
||||
|
||||
|
||||
assign m_axi_wdata = write_data;
|
||||
assign m_axi_wvalid = enable_data_write && write_data_valid;
|
||||
assign write_data_ready = enable_data_write && m_axi_wready;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// AXI Read address channel
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
assign m_axi_arid = 1'b0;
|
||||
assign m_axi_arsize = $clog2(DWIDTH/8);
|
||||
assign m_axi_arburst = `AXI4_BURST_INCR;
|
||||
assign m_axi_arlock = `AXI4_LOCK_NORMAL;
|
||||
assign m_axi_arcache = `AXI4_CACHE_ALLOCATE | `AXI4_CACHE_OTHER_ALLOCATE | `AXI4_CACHE_MODIFIABLE | `AXI4_CACHE_BUFFERABLE;
|
||||
assign m_axi_arprot = `AXI4_PROT_NON_SECURE;
|
||||
assign m_axi_arqos = 4'h0;
|
||||
assign m_axi_arregion = 4'h0;
|
||||
assign m_axi_aruser = 1'b0;
|
||||
|
||||
|
||||
//
|
||||
// AXI Read address state machine
|
||||
//
|
||||
always @(posedge aclk)
|
||||
if (areset) begin
|
||||
read_ctrl_ready <= 1'b0;
|
||||
read_addr_state <= AR_IDLE;
|
||||
m_axi_araddr <= {AWIDTH{1'b0}};
|
||||
m_axi_arlen[7:0] <= 8'h0;
|
||||
m_axi_arvalid <= 1'b0;
|
||||
end else
|
||||
case (read_addr_state)
|
||||
//
|
||||
// AR_IDLE
|
||||
// We are ready to accept a new read transaction.
|
||||
//
|
||||
AR_IDLE: begin
|
||||
// Premptively accept new read transaction since we are idle.
|
||||
read_ctrl_ready <= 1'b1;
|
||||
// If we are offered a new transaction then.....
|
||||
if (read_ctrl_valid) begin
|
||||
// Drive all the relevent AXI4 read address channel signals next cycle.
|
||||
m_axi_araddr <= read_addr;
|
||||
m_axi_arlen[7:0] <= {read_count};
|
||||
m_axi_arvalid <= 1'b1;
|
||||
// If the AXI4 read channel is pre-emptively accepting the transaction...
|
||||
if (m_axi_arready == 1'b1) begin
|
||||
// ...go straight to looking for the transaction to complete
|
||||
`DEBUG $display("READ TRANSACTION: ADDR: %x LEN: %x @ time %d",read_addr,read_count,$time);
|
||||
read_addr_state <= WAIT_READ_DONE;
|
||||
end else begin
|
||||
// ...otherwise wait to get the transaction accepted.
|
||||
read_addr_state <= WAIT_ARREADY;
|
||||
end
|
||||
end
|
||||
end
|
||||
//
|
||||
// WAIT_ARREADY
|
||||
// Waiting for AXI4 slave to accept new read transaction.
|
||||
//
|
||||
WAIT_ARREADY: begin
|
||||
read_ctrl_ready <= 1'b0;
|
||||
// If the AXI4 read channel is accepting the transaction...
|
||||
if (m_axi_arready == 1'b1) begin
|
||||
// ...go to looking for the transaction to complete...
|
||||
read_addr_state <= WAIT_READ_DONE;
|
||||
m_axi_arvalid <= 1'b0;
|
||||
`DEBUG $display("READ TRANSACTION: ADDR: %x LEN: %x @ time %d",m_axi_araddr,m_axi_arlen[7:0],$time);
|
||||
end else begin
|
||||
// ...otherwise wait to get the trasaction accepted.
|
||||
read_addr_state <= WAIT_ARREADY;
|
||||
end
|
||||
end // case: WAIT_ARREADY
|
||||
//
|
||||
// WAIT_READ_DONE
|
||||
// Read transaction has been accepted, now waiting for the data transfer to complete
|
||||
// Ignoring ID tag for the moment
|
||||
//
|
||||
WAIT_READ_DONE: begin
|
||||
read_ctrl_ready <= 1'b0;
|
||||
m_axi_arvalid <= 1'b0;
|
||||
// Wait for read transaction to complete
|
||||
if (read_data_state == DR_IDLE) begin
|
||||
// ....it went well, we are ready to start something new.
|
||||
read_addr_state <= AR_IDLE;
|
||||
read_ctrl_ready <= 1'b1; // Ready to run again as soon as we hit idle.
|
||||
end else if (read_data_state == DR_ERROR) begin
|
||||
// ....things got ugly, retreat to an error stat and wait for intervention.
|
||||
read_addr_state <= AR_ERROR;
|
||||
end else begin
|
||||
read_addr_state <= WAIT_READ_DONE;
|
||||
end
|
||||
end // case: WAIT_BVALID
|
||||
//
|
||||
// AR_ERROR
|
||||
// Something bad happened, going to need external intervention to restore a safe state.
|
||||
//
|
||||
AR_ERROR: begin
|
||||
read_ctrl_ready <= 1'b0;
|
||||
read_addr_state <= AR_ERROR;
|
||||
m_axi_araddr <= {AWIDTH{1'b0}};
|
||||
m_axi_arlen[7:0] <= 8'h0;
|
||||
m_axi_arvalid <= 1'b0;
|
||||
end
|
||||
endcase // case(read_addr_state)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// AXI Read data channel
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
//
|
||||
// AXI Read data state machine
|
||||
//
|
||||
always @(posedge aclk)
|
||||
if (areset) begin
|
||||
read_data_state <= AR_IDLE;
|
||||
read_data_count <= 0;
|
||||
enable_data_read <= 1'b0;
|
||||
|
||||
end else
|
||||
case (read_data_state)
|
||||
//
|
||||
// DR_IDLE
|
||||
// Sit in this state until presented with the control details of a new read transaction.
|
||||
//
|
||||
DR_IDLE: begin
|
||||
read_data_count <= 0;
|
||||
|
||||
if (read_ctrl_valid && read_ctrl_ready) begin
|
||||
enable_data_read <= 1'b1;
|
||||
read_data_state <= DR_RUN;
|
||||
end else begin
|
||||
read_data_state <= DR_IDLE;
|
||||
end
|
||||
end
|
||||
//
|
||||
// DR_RUN
|
||||
// Sit here counting read transfers. If any have error's shift to error state.
|
||||
//
|
||||
DR_RUN : begin
|
||||
enable_data_read <= 1'b1;
|
||||
|
||||
if (read_data_ready && m_axi_rvalid) begin
|
||||
// Single read transfer
|
||||
read_data_count <= read_data_count + 1;
|
||||
if ((m_axi_rresp == `AXI4_RESP_SLVERR) || (m_axi_rresp == `AXI4_RESP_DECERR)) begin
|
||||
if (m_axi_rlast) begin
|
||||
read_data_state <= DR_ERROR;
|
||||
end else begin
|
||||
read_data_state <= DR_WAIT_ERROR;
|
||||
end
|
||||
end else if (m_axi_rlast) begin // Implicitly good response signalled this transfer.
|
||||
if (read_data_count == m_axi_arlen[7:0]) begin
|
||||
read_data_state <= DR_IDLE;
|
||||
end else begin
|
||||
read_data_state <= DR_ERROR;
|
||||
end
|
||||
end else begin
|
||||
read_data_state <= DR_RUN;
|
||||
end
|
||||
end else begin
|
||||
read_data_state <= DR_RUN;
|
||||
end
|
||||
end
|
||||
//
|
||||
// DR_WAIT_ERROR
|
||||
// Something bad happened, wait for last signalled in this burst
|
||||
//
|
||||
DR_WAIT_ERROR: begin
|
||||
if (read_data_ready && m_axi_rvalid && m_axi_rlast) begin
|
||||
enable_data_read <= 1'b0;
|
||||
read_data_state <= DR_ERROR;
|
||||
end else begin
|
||||
enable_data_read <= 1'b1;
|
||||
read_data_state <= DR_WAIT_ERROR;
|
||||
end
|
||||
end // case: DR_WAIT_ERROR
|
||||
//
|
||||
// DR_ERROR
|
||||
// Something bad happened, going to need external intervention to restore a safe state.
|
||||
//
|
||||
DR_ERROR: begin
|
||||
enable_data_read <= 1'b0;
|
||||
read_data_state <= DR_ERROR;
|
||||
end // case: DR_ERROR
|
||||
|
||||
|
||||
endcase // case(read_data_state)
|
||||
|
||||
|
||||
assign read_data = m_axi_rdata;
|
||||
assign m_axi_rready = enable_data_read && read_data_ready;
|
||||
assign read_data_valid = enable_data_read && m_axi_rvalid;
|
||||
|
||||
endmodule // axi_dma_master
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
//
|
||||
// Copyright 2015 Ettus Research
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
module axi_dummy
|
||||
(
|
||||
// sys connect
|
||||
input s_axi_aclk,
|
||||
input s_axi_areset,
|
||||
|
||||
// axi4 lite slave port
|
||||
input [31:0] s_axi_awaddr,
|
||||
input s_axi_awvalid,
|
||||
output s_axi_awready,
|
||||
|
||||
input [31:0] s_axi_wdata,
|
||||
input [3:0] s_axi_wstrb,
|
||||
input s_axi_wvalid,
|
||||
output s_axi_wready,
|
||||
|
||||
output [1:0] s_axi_bresp,
|
||||
output s_axi_bvalid,
|
||||
input s_axi_bready,
|
||||
|
||||
input [31:0] s_axi_araddr,
|
||||
input s_axi_arvalid,
|
||||
output s_axi_arready,
|
||||
|
||||
output [31:0] s_axi_rdata,
|
||||
output [1:0] s_axi_rresp,
|
||||
output s_axi_rvalid,
|
||||
input s_axi_rready
|
||||
);
|
||||
parameter DEC_ERR = 1'b1;
|
||||
|
||||
localparam IDLE = 3'b001;
|
||||
localparam READ_IN_PROGRESS = 3'b010;
|
||||
localparam WRITE_IN_PROGRESS = 3'b100;
|
||||
|
||||
reg [2:0] state;
|
||||
|
||||
always @ (posedge s_axi_aclk) begin
|
||||
if (s_axi_areset) begin
|
||||
state <= IDLE;
|
||||
end
|
||||
else case (state)
|
||||
|
||||
IDLE: begin
|
||||
if (s_axi_arvalid)
|
||||
state <= READ_IN_PROGRESS;
|
||||
else if (s_axi_awvalid)
|
||||
state <= WRITE_IN_PROGRESS;
|
||||
end
|
||||
|
||||
READ_IN_PROGRESS: begin
|
||||
if (s_axi_rready)
|
||||
state <= IDLE;
|
||||
end
|
||||
|
||||
WRITE_IN_PROGRESS: begin
|
||||
if (s_axi_bready)
|
||||
state <= IDLE;
|
||||
end
|
||||
|
||||
default: begin
|
||||
state <= IDLE;
|
||||
end
|
||||
|
||||
endcase
|
||||
end
|
||||
|
||||
assign s_axi_awready = (state == IDLE);
|
||||
assign s_axi_wready = (state == WRITE_IN_PROGRESS);
|
||||
assign s_axi_bvalid = (state == WRITE_IN_PROGRESS);
|
||||
|
||||
assign s_axi_arready = (state == IDLE);
|
||||
assign s_axi_rdata = 32'hdead_ba5e;
|
||||
assign s_axi_rvalid = (state == READ_IN_PROGRESS);
|
||||
assign s_axi_rresp = DEC_ERR ? 2'b11 : 2'b00;
|
||||
assign s_axi_bresp = DEC_ERR ? 2'b11 : 2'b00;
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,132 @@
|
||||
//
|
||||
// Copyright 2014 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
//
|
||||
// AXI stream neds N+1 bits to transmit packets of N bits so that the LAST bit can be represented.
|
||||
// LAST occurs relatively infrequently and can be synthesized by using an in-band ESC code to generate
|
||||
// a multi-word sequence to encode it (and the escape character when it appears as data input).
|
||||
//
|
||||
// 0x1234567887654321 with last becomes
|
||||
// 0xDEADBEEFFEEDCAFE 0x0000000000000001 0x1234567887654321
|
||||
//
|
||||
// 0xDEADBEEFFEEDCAFE with last becomes
|
||||
// 0xDEADBEEFFEEDCAFE 0x0000000000000001 0xDEADBEEFFEEDCAFE
|
||||
//
|
||||
// 0xDEADBEEFFEEDCAFE without last becomes
|
||||
// 0xDEADBEEFFEEDCAFE 0x0000000000000000 0xDEADBEEFFEEDCAFE
|
||||
//
|
||||
|
||||
module axi_embed_tlast #(
|
||||
parameter WIDTH=64,
|
||||
parameter ADD_CHECKSUM=0
|
||||
) (
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
//
|
||||
input [WIDTH-1:0] i_tdata,
|
||||
input i_tlast,
|
||||
input i_tvalid,
|
||||
output i_tready,
|
||||
//
|
||||
output reg [WIDTH-1:0] o_tdata,
|
||||
output o_tvalid,
|
||||
input o_tready
|
||||
);
|
||||
|
||||
localparam PASS = 0;
|
||||
localparam ZERO = 1;
|
||||
localparam ONE = 2;
|
||||
localparam ESCAPE = 3;
|
||||
|
||||
localparam IDLE = 0;
|
||||
localparam LAST = 1;
|
||||
localparam ESC = 2;
|
||||
localparam FINISH = 3;
|
||||
|
||||
reg [1:0] state, next_state;
|
||||
|
||||
reg [1:0] select;
|
||||
|
||||
wire [31:0] checksum;
|
||||
generate if (ADD_CHECKSUM == 1) begin
|
||||
reg [31:0] checksum_reg;
|
||||
always @(posedge clk) begin
|
||||
if (reset | clear) begin
|
||||
checksum_reg <= 0;
|
||||
end else if (i_tready && i_tvalid && i_tlast) begin
|
||||
checksum_reg <= 0;
|
||||
end else if (i_tready && i_tvalid) begin
|
||||
checksum_reg <= checksum_reg ^ i_tdata[31:0] ^ i_tdata[63:32];
|
||||
end
|
||||
end
|
||||
assign checksum = checksum_reg;
|
||||
end else begin
|
||||
assign checksum = 32'h0;
|
||||
end endgenerate
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
state <= IDLE;
|
||||
end else begin if (o_tready)
|
||||
state <= next_state;
|
||||
end
|
||||
|
||||
always @(*) begin
|
||||
case(state)
|
||||
IDLE: begin
|
||||
if (i_tlast && i_tvalid) begin
|
||||
next_state = LAST;
|
||||
select = ESCAPE;
|
||||
end else if ((i_tdata == 64'hDEADBEEFFEEDCAFE) && i_tvalid) begin
|
||||
next_state = ESC;
|
||||
select = ESCAPE;
|
||||
end else begin
|
||||
next_state = IDLE;
|
||||
select = PASS;
|
||||
end
|
||||
end // case: IDLE
|
||||
|
||||
LAST: begin
|
||||
select = ONE;
|
||||
next_state = FINISH;
|
||||
end
|
||||
|
||||
ESC: begin
|
||||
select = ZERO;
|
||||
next_state = FINISH;
|
||||
end
|
||||
|
||||
FINISH: begin
|
||||
select = PASS;
|
||||
if (i_tvalid)
|
||||
next_state = IDLE;
|
||||
else
|
||||
next_state = FINISH;
|
||||
end
|
||||
endcase // case(state)
|
||||
end // always @ (*)
|
||||
|
||||
//
|
||||
// Muxes
|
||||
//
|
||||
always @*
|
||||
begin
|
||||
case(select)
|
||||
PASS: o_tdata = i_tdata;
|
||||
ZERO: o_tdata = 0;
|
||||
ONE: o_tdata = {checksum[31:0],32'h1};
|
||||
ESCAPE: o_tdata = 64'hDEADBEEFFEEDCAFE;
|
||||
endcase // case(select)
|
||||
end
|
||||
|
||||
assign o_tvalid = (select == PASS) ? i_tvalid : 1'b1;
|
||||
assign i_tready = (select == PASS) ? o_tready : 1'b0;
|
||||
|
||||
endmodule // axi_embed_tlast
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
//
|
||||
// Copyright 2019 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: axi_embed_tlast_tkeep
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// This module takes the TLAST and TKEEP values of an AXI-Stream interface
|
||||
// and embeds them into the data stream. This allows a data pipe to be used
|
||||
// that isn't wide enough for the TDATA, TLAST,and TKEEP to be passed through
|
||||
// in parallel. Since TLAST and TKEEP are only usually needed for one word
|
||||
// per packet, this also reduces the amount of memory required to store a
|
||||
// packet. Note that this module only supports TKEEP at the end of a packet
|
||||
// when TLAST is asserted. See also axi_extract_tlast_tkeep.
|
||||
//
|
||||
// This embedding is accomplished by using an escape sequence using the word
|
||||
// 0xDEADBEEF as the escape code. If TLAST and TKEEP are both 0 (the usual
|
||||
// case) then no escape sequence is used. Any word that has "DEADBEEF" in the
|
||||
// most significant position is considered an escape word. The least
|
||||
// significant bits of the escape word contain the TKEEP and TLAST bits. The
|
||||
// word following the escape word is the normal data word associated with
|
||||
// those TLAST and TKEEP values.
|
||||
//
|
||||
// Here are some examples for the case where DATA_W = 64
|
||||
//
|
||||
// 0x1234567887654321 with TLAST=0 and TKEEP=0 becomes
|
||||
// 0x1234567887654321
|
||||
//
|
||||
// 0x1234567887654321 with TLAST=1 and TKEEP=0 becomes
|
||||
// 0xDEADBEEF00000001 0x1234567887654321
|
||||
//
|
||||
// 0x1234567887654321 with TLAST=1 and TKEEP=2 becomes
|
||||
// 0xDEADBEEF00000005 0x1234567887654321
|
||||
//
|
||||
// 0x1234567887654321 with TLAST=0 and TKEEP=1 becomes
|
||||
// 0x1234567887654321 (because TKEEP is ignored when TLAST=0)
|
||||
//
|
||||
// 0xDEADBEEFFEEDCAFE without TLAST=0 and TKEEP=0 becomes
|
||||
// 0xDEADBEEF00000000 0xDEADBEEFFEEDCAFE
|
||||
//
|
||||
// 0xDEADBEEFFEEDCAFE with TLAST=0 and TKEEP=1 becomes
|
||||
// 0xDEADBEEF00000002 0xDEADBEEFFEEDCAFE
|
||||
//
|
||||
|
||||
module axi_embed_tlast_tkeep #(
|
||||
parameter DATA_W = 64,
|
||||
parameter KEEP_W = DATA_W/8
|
||||
) (
|
||||
input clk,
|
||||
input rst,
|
||||
|
||||
// Input AXI-Stream
|
||||
input [DATA_W-1:0] i_tdata,
|
||||
input [KEEP_W-1:0] i_tkeep,
|
||||
input i_tlast,
|
||||
input i_tvalid,
|
||||
output i_tready,
|
||||
|
||||
// Output AXI-Stream
|
||||
output reg [DATA_W-1:0] o_tdata,
|
||||
output o_tvalid,
|
||||
input o_tready
|
||||
);
|
||||
|
||||
localparam ESC_WORD_W = 32;
|
||||
localparam [ESC_WORD_W-1:0] ESC_WORD = 'hDEADBEEF;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Parameter Checking
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
if (DATA_W < ESC_WORD_W+KEEP_W+1) begin : gen_assertion
|
||||
// Cause an error if DATA_W is not large enough.
|
||||
DATA_W_is_not_large_enough_to_store_escape_code_TKEEP_and_TLAST();
|
||||
end
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// State Machine
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
localparam PASS = 0;
|
||||
localparam ESCAPE = 1;
|
||||
|
||||
localparam ST_IDLE = 0;
|
||||
localparam ST_DATA = 1;
|
||||
|
||||
reg [0:0] state = ST_IDLE;
|
||||
reg [0:0] next_state;
|
||||
|
||||
reg [0:0] select;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state <= ST_IDLE;
|
||||
end else begin if (o_tready)
|
||||
state <= next_state;
|
||||
end
|
||||
end
|
||||
|
||||
always @(*) begin
|
||||
case(state)
|
||||
ST_IDLE: begin
|
||||
if (i_tlast && i_tvalid) begin
|
||||
next_state = ST_DATA;
|
||||
select = ESCAPE;
|
||||
end else if ((i_tdata[DATA_W-1 -: ESC_WORD_W] == ESC_WORD) && i_tvalid) begin
|
||||
next_state = ST_DATA;
|
||||
select = ESCAPE;
|
||||
end else begin
|
||||
next_state = ST_IDLE;
|
||||
select = PASS;
|
||||
end
|
||||
end
|
||||
|
||||
ST_DATA: begin
|
||||
select = PASS;
|
||||
if (i_tvalid) begin
|
||||
next_state = ST_IDLE;
|
||||
end else begin
|
||||
next_state = ST_DATA;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Output Multiplexers
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
always @(*) begin
|
||||
case(select)
|
||||
PASS : begin
|
||||
o_tdata = i_tdata;
|
||||
end
|
||||
ESCAPE : begin
|
||||
o_tdata = {DATA_W{1'b0}};
|
||||
o_tdata[DATA_W-1 -: ESC_WORD_W] = ESC_WORD;
|
||||
o_tdata[ 1 +: KEEP_W] = i_tkeep;
|
||||
o_tdata[ 0 +: 1] = i_tlast;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
assign o_tvalid = (select == PASS) ? i_tvalid : 1'b1;
|
||||
assign i_tready = (select == PASS) ? o_tready : 1'b0;
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,147 @@
|
||||
//
|
||||
// Copyright 2014 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
//
|
||||
// AXI stream neds N+1 bits to transmit packets of N bits so that the LAST bit can be represented.
|
||||
// LAST occurs relatively infrequently and can be synthesized by using an in-band ESC code to generate
|
||||
// a multi-word sequence to encode it (and the escape character when it appears as data input).
|
||||
//
|
||||
// 0x1234567887654321 with last becomes
|
||||
// 0xDEADBEEFFEEDCAFE 0x0000000000000001 0x1234567887654321
|
||||
//
|
||||
// 0xDEADBEEFFEEDCAFE with last becomes
|
||||
// 0xDEADBEEFFEEDCAFE 0x0000000000000001 0xDEADBEEFFEEDCAFE
|
||||
//
|
||||
// 0xDEADBEEFFEEDCAFE without last becomes
|
||||
// 0xDEADBEEFFEEDCAFE 0x0000000000000000 0xDEADBEEFFEEDCAFE
|
||||
//
|
||||
|
||||
module axi_extract_tlast #(
|
||||
parameter WIDTH=64,
|
||||
parameter VALIDATE_CHECKSUM=0
|
||||
) (
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
//
|
||||
input [WIDTH-1:0] i_tdata,
|
||||
input i_tvalid,
|
||||
output reg i_tready,
|
||||
//
|
||||
output [WIDTH-1:0] o_tdata,
|
||||
output reg o_tlast,
|
||||
output reg o_tvalid,
|
||||
input o_tready,
|
||||
//
|
||||
output reg checksum_error
|
||||
);
|
||||
|
||||
reg [1:0] state, next_state;
|
||||
|
||||
localparam IDLE = 0;
|
||||
localparam EXTRACT1 = 1;
|
||||
localparam EXTRACT2 = 2;
|
||||
localparam EXTRACT3 = 3;
|
||||
|
||||
assign o_tdata = i_tdata;
|
||||
|
||||
reg checksum_error_pre;
|
||||
reg [31:0] checksum, old_checksum;
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
checksum <= 0;
|
||||
old_checksum <= 0;
|
||||
end else if (VALIDATE_CHECKSUM && o_tready && i_tvalid && o_tlast) begin
|
||||
checksum <= 0;
|
||||
old_checksum <= 0;
|
||||
end else if (VALIDATE_CHECKSUM && i_tready && i_tvalid && (state == IDLE)) begin
|
||||
checksum <= checksum ^ i_tdata[31:0] ^ i_tdata[63:32];
|
||||
old_checksum <= checksum;
|
||||
end
|
||||
|
||||
always @(posedge clk)
|
||||
checksum_error <= checksum_error_pre;
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
state <= IDLE;
|
||||
end else begin
|
||||
state <= next_state;
|
||||
end
|
||||
|
||||
always @(*) begin
|
||||
checksum_error_pre = 0;
|
||||
case(state)
|
||||
//
|
||||
// Search for Escape sequence "0xDEADBEEFFEEDCAFE"
|
||||
// If ESC found don't pass data downstream but transition to next state.
|
||||
// else pass data downstream.
|
||||
//
|
||||
IDLE: begin
|
||||
o_tlast = 1'b0;
|
||||
if ((i_tdata == 64'hDEADBEEFFEEDCAFE) && i_tvalid) begin
|
||||
next_state = EXTRACT1;
|
||||
o_tvalid = 1'b0;
|
||||
i_tready = 1'b1;
|
||||
end else begin
|
||||
next_state = IDLE;
|
||||
o_tvalid = i_tvalid;
|
||||
i_tready = o_tready;
|
||||
end // else: !if((i_tdata == 'hDEADBEEFFEEDCAFE) && i_tvalid)
|
||||
end // case: IDLE
|
||||
//
|
||||
// Look at next data. If it's a 0x1 then o_tlast should be asserted with next data word.
|
||||
// if it's 0x0 then it signals emulation of the Escape code in the original data stream
|
||||
// and we should just pass the next data word through unchanged with no o_tlast indication.
|
||||
//
|
||||
EXTRACT1: begin
|
||||
o_tvalid = 1'b0;
|
||||
i_tready = 1'b1;
|
||||
o_tlast = 1'b0;
|
||||
if (i_tvalid) begin
|
||||
if (i_tdata[31:0] == 'h1) begin
|
||||
if (VALIDATE_CHECKSUM && (old_checksum != i_tdata[63:32]))
|
||||
checksum_error_pre = 1'b1;
|
||||
next_state = EXTRACT2;
|
||||
end else begin
|
||||
// We assume emulation and don't look for illegal codes.
|
||||
next_state = EXTRACT3;
|
||||
end // else: !if(i_tdata == 'h1)
|
||||
end else begin // if (i_tvalid)
|
||||
next_state = EXTRACT1;
|
||||
end // else: !if(i_tvalid)
|
||||
end // case: EXTRACT1
|
||||
//
|
||||
// Assert o_tlast with data word.
|
||||
//
|
||||
EXTRACT2: begin
|
||||
o_tvalid = i_tvalid;
|
||||
i_tready = o_tready;
|
||||
o_tlast = 1'b1;
|
||||
if (i_tvalid & o_tready)
|
||||
next_state = IDLE;
|
||||
else
|
||||
next_state = EXTRACT2;
|
||||
end
|
||||
//
|
||||
// Emulation, don't assert o_tlast with dataword.
|
||||
//
|
||||
EXTRACT3: begin
|
||||
o_tvalid = i_tvalid;
|
||||
i_tready = o_tready;
|
||||
o_tlast = 1'b0;
|
||||
if (i_tvalid & o_tready)
|
||||
next_state = IDLE;
|
||||
else
|
||||
next_state = EXTRACT2;
|
||||
end
|
||||
endcase // case(state)
|
||||
end
|
||||
|
||||
endmodule // axi_extract_tlast
|
||||
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
//
|
||||
// Copyright 2019 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: axi_extract_tlast_tkeep
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// This module extracts the TLAST and TKEEP values that were embedded by the
|
||||
// axi_embed_tlast_tkeep module. See axi_embed_tlast_tkeep for a description
|
||||
// of how the data is encoded.
|
||||
//
|
||||
// Here are some extraction examples for DATA_W = 64.
|
||||
//
|
||||
// 0x1234567887654321 becomes
|
||||
// 0x1234567887654321 (no changes)
|
||||
//
|
||||
// 0xDEADBEEF00000001 0x1234567887654321 becomes
|
||||
// 0x1234567887654321 with TLAST=1 and TKEEP=0
|
||||
//
|
||||
// 0xDEADBEEF00000005 0x1234567887654321 becomes
|
||||
// 0x1234567887654321 with TLAST=1 and TKEEP=2
|
||||
//
|
||||
// 0xDEADBEEF00000000 0xDEADBEEFFEEDCAFE
|
||||
// 0xDEADBEEFFEEDCAFE without TLAST=0 and TKEEP=0 becomes
|
||||
//
|
||||
// 0xDEADBEEF00000002 0xDEADBEEFFEEDCAFE
|
||||
// 0xDEADBEEFFEEDCAFE with TLAST=0 and TKEEP=1 becomes
|
||||
//
|
||||
|
||||
module axi_extract_tlast_tkeep #(
|
||||
parameter DATA_W = 64,
|
||||
parameter KEEP_W = DATA_W /8
|
||||
) (
|
||||
input clk,
|
||||
input rst,
|
||||
|
||||
// Input AXI-Stream
|
||||
input [DATA_W-1:0] i_tdata,
|
||||
input i_tvalid,
|
||||
output reg i_tready,
|
||||
|
||||
// Output AXI-Stream
|
||||
output reg [DATA_W-1:0] o_tdata,
|
||||
output reg [KEEP_W-1:0] o_tkeep,
|
||||
output reg o_tlast,
|
||||
output reg o_tvalid,
|
||||
input o_tready
|
||||
);
|
||||
|
||||
localparam ESC_WORD_W = 32;
|
||||
localparam [ESC_WORD_W-1:0] ESC_WORD = 'hDEADBEEF;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// TKEEP and TLAST Holding Register
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
reg save_flags;
|
||||
reg tlast_saved;
|
||||
reg [KEEP_W-1:0] tkeep_saved;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (save_flags) begin
|
||||
// Save the TLAST and TKEEP values embedded in the escape word
|
||||
tlast_saved <= i_tdata[0];
|
||||
tkeep_saved <= i_tdata[1 +: KEEP_W];
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// State Machine
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
localparam ST_IDLE = 0;
|
||||
localparam ST_DATA = 1;
|
||||
|
||||
reg [0:0] state = ST_IDLE;
|
||||
reg [0:0] next_state;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state <= ST_IDLE;
|
||||
end else begin
|
||||
state <= next_state;
|
||||
end
|
||||
end
|
||||
|
||||
always @(*) begin
|
||||
// Default assignments (pass through)
|
||||
o_tdata = i_tdata;
|
||||
o_tlast = 1'b0;
|
||||
o_tkeep = {KEEP_W{1'b1}};
|
||||
save_flags = 1'b0;
|
||||
next_state = state;
|
||||
o_tvalid = i_tvalid;
|
||||
i_tready = o_tready;
|
||||
|
||||
case(state)
|
||||
//
|
||||
// Search for escape code. If found don't pass data downstream but
|
||||
// transition to next state. Otherwise, pass data downstream.
|
||||
//
|
||||
ST_IDLE: begin
|
||||
if ((i_tdata[DATA_W-1 -: ESC_WORD_W] == ESC_WORD) && i_tvalid) begin
|
||||
save_flags = 1'b1;
|
||||
next_state = ST_DATA;
|
||||
o_tvalid = 1'b0;
|
||||
i_tready = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
//
|
||||
// Output data word with the saved TLAST and TKEEP values
|
||||
//
|
||||
ST_DATA: begin
|
||||
o_tlast = tlast_saved;
|
||||
o_tkeep = tkeep_saved;
|
||||
if (i_tvalid & o_tready) begin
|
||||
next_state = ST_IDLE;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,193 @@
|
||||
//
|
||||
// Copyright 2014 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
//
|
||||
// Ultra fast critical path FIFO.
|
||||
// Only 2 entrys but no combinatorial feed through paths
|
||||
//
|
||||
|
||||
|
||||
module axi_fast_extract_tlast
|
||||
#(parameter WIDTH=64)
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
//
|
||||
input [WIDTH-1:0] i_tdata,
|
||||
input i_tvalid,
|
||||
output reg i_tready,
|
||||
//
|
||||
output [WIDTH-1:0] o_tdata,
|
||||
output o_tlast,
|
||||
output reg o_tvalid,
|
||||
input o_tready
|
||||
);
|
||||
|
||||
reg [WIDTH:0] data_reg1, data_reg2;
|
||||
|
||||
reg [1:0] fifo_state;
|
||||
|
||||
localparam EMPTY = 0;
|
||||
localparam HALF = 1;
|
||||
localparam FULL = 2;
|
||||
|
||||
reg [1:0] extract_state;
|
||||
|
||||
localparam IDLE = 0;
|
||||
localparam EXTRACT1 = 1;
|
||||
localparam EXTRACT2 = 2;
|
||||
localparam EXTRACT3 = 3;
|
||||
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
fifo_state <= EMPTY;
|
||||
end else begin
|
||||
case (fifo_state)
|
||||
// Nothing in either register.
|
||||
// Upstream can always push data to us.
|
||||
// Downstream has nothing to take from us.
|
||||
EMPTY: begin
|
||||
if ((extract_state == IDLE) && (i_tdata == 64'hDEADBEEFFEEDCAFE) && i_tvalid) begin
|
||||
// Embeded escpae code received.
|
||||
extract_state <= EXTRACT1;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b0;
|
||||
fifo_state <= EMPTY;
|
||||
end else if ((extract_state == EXTRACT1) && i_tvalid) begin
|
||||
// Now work out if its a genuine embeded tlast or emulation.
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b0;
|
||||
fifo_state <= EMPTY;
|
||||
if (i_tdata[31:0] == 'h1) begin
|
||||
extract_state <= EXTRACT2;
|
||||
end else begin
|
||||
extract_state <= EXTRACT3;
|
||||
end
|
||||
end else if ((extract_state == EXTRACT2) && i_tvalid) begin
|
||||
// Extract tlast.
|
||||
data_reg1 <= {1'b1,i_tdata};
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
fifo_state <= HALF;
|
||||
extract_state <= IDLE;
|
||||
end else if (i_tvalid) begin
|
||||
// Get here both for normal data and for EXTRACT3 emulation data.
|
||||
data_reg1 <= {1'b0,i_tdata};
|
||||
fifo_state <= HALF;
|
||||
extract_state <= IDLE;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
end else begin
|
||||
// Nothing to do.
|
||||
fifo_state <= EMPTY;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b0;
|
||||
end
|
||||
end
|
||||
// First Register Full.
|
||||
// Upstream can always push data to us.
|
||||
// Downstream can always read from us.
|
||||
HALF: begin
|
||||
if ((extract_state == IDLE) && (i_tdata == 64'hDEADBEEFFEEDCAFE) && i_tvalid) begin
|
||||
// Embeded escpae code received.
|
||||
extract_state <= EXTRACT1;
|
||||
if (o_tready) begin
|
||||
// If meanwhile we get read then go empty...
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b0;
|
||||
fifo_state <= EMPTY;
|
||||
end else begin
|
||||
// ...else stay half full.
|
||||
fifo_state <= HALF;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
end
|
||||
end else if ((extract_state == EXTRACT1) && i_tvalid) begin
|
||||
// Now work out if its a genuine embeded tlast or emulation.
|
||||
if (i_tdata[31:0] == 'h1) begin
|
||||
extract_state <= EXTRACT2;
|
||||
end else begin
|
||||
extract_state <= EXTRACT3;
|
||||
end
|
||||
if (o_tready) begin
|
||||
// If meanwhile we get read then go empty...
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b0;
|
||||
fifo_state <= EMPTY;
|
||||
end else begin
|
||||
// ...else stay half full.
|
||||
fifo_state <= HALF;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
end
|
||||
end else if ((extract_state == EXTRACT2) && i_tvalid) begin
|
||||
// Extract tlast.
|
||||
data_reg1 <= {1'b1,i_tdata};
|
||||
extract_state <= IDLE;
|
||||
if (o_tready) begin
|
||||
// We get read and writen same cycle...
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
fifo_state <= HALF;
|
||||
end else begin
|
||||
// ...or we get written and go full.
|
||||
data_reg2 <= data_reg1;
|
||||
i_tready <= 1'b0;
|
||||
o_tvalid <= 1'b1;
|
||||
fifo_state <= FULL;
|
||||
end
|
||||
end else if (i_tvalid) begin
|
||||
// Get here both for normal data and for EXTRACT3 emulation data.
|
||||
data_reg1 <= {1'b0,i_tdata};
|
||||
extract_state <= IDLE;
|
||||
if (o_tready) begin
|
||||
// We get read and writen same cycle...
|
||||
fifo_state <= HALF;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
end else begin
|
||||
// ...or we get written and go full.
|
||||
data_reg2 <= data_reg1;
|
||||
i_tready <= 1'b0;
|
||||
o_tvalid <= 1'b1;
|
||||
fifo_state <= FULL;
|
||||
end
|
||||
end else if (o_tready) begin // if (i_tvalid)
|
||||
// Only getting read this cycle so go empty
|
||||
fifo_state <= EMPTY;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b0;
|
||||
end else begin
|
||||
// Absolutley nothing happens, everything stays the same.
|
||||
fifo_state <= HALF;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
end
|
||||
end // case: HALF
|
||||
// Both Registers Full.
|
||||
// Upstream can not push to us in this fifo_state.
|
||||
// Downstream can always read from us.
|
||||
FULL: begin
|
||||
if (o_tready) begin
|
||||
fifo_state <= HALF;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
end
|
||||
else begin
|
||||
fifo_state <= FULL;
|
||||
i_tready <= 1'b0;
|
||||
o_tvalid <= 1'b1;
|
||||
end
|
||||
end
|
||||
endcase // case(fifo_state)
|
||||
end // else: !if(reset | clear)
|
||||
|
||||
assign {o_tlast,o_tdata} = (fifo_state == FULL) ? data_reg2 : data_reg1;
|
||||
|
||||
|
||||
endmodule // axi_fast_extract_tlast
|
||||
@@ -0,0 +1,108 @@
|
||||
//
|
||||
// Copyright 2014 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
//
|
||||
// Ultra fast critical path FIFO.
|
||||
// Only 2 entrys but no combinatorial feed through paths
|
||||
//
|
||||
|
||||
|
||||
module axi_fast_fifo
|
||||
#(parameter WIDTH=64)
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
//
|
||||
input [WIDTH-1:0] i_tdata,
|
||||
input i_tvalid,
|
||||
output reg i_tready,
|
||||
//
|
||||
output [WIDTH-1:0] o_tdata,
|
||||
output reg o_tvalid,
|
||||
input o_tready
|
||||
);
|
||||
|
||||
reg [WIDTH-1:0] data_reg1, data_reg2;
|
||||
|
||||
reg [1:0] state;
|
||||
|
||||
localparam EMPTY = 0;
|
||||
localparam HALF = 1;
|
||||
localparam FULL = 2;
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
state <= EMPTY;
|
||||
data_reg1 <= 0;
|
||||
data_reg2 <= 0;
|
||||
o_tvalid <= 1'b0;
|
||||
i_tready <= 1'b0;
|
||||
|
||||
end else begin
|
||||
case (state)
|
||||
// Nothing in either register.
|
||||
// Upstream can always push data to us.
|
||||
// Downstream has nothing to take from us.
|
||||
EMPTY: begin
|
||||
if (i_tvalid) begin
|
||||
data_reg1 <= i_tdata;
|
||||
state <= HALF;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
end else begin
|
||||
state <= EMPTY;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b0;
|
||||
end
|
||||
end
|
||||
// First Register Full.
|
||||
// Upstream can always push data to us.
|
||||
// Downstream can always read from us.
|
||||
HALF: begin
|
||||
if (i_tvalid && o_tready) begin
|
||||
data_reg1 <= i_tdata;
|
||||
state <= HALF;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
end else if (i_tvalid) begin
|
||||
data_reg1 <= i_tdata;
|
||||
data_reg2 <= data_reg1;
|
||||
state <= FULL;
|
||||
i_tready <= 1'b0;
|
||||
o_tvalid <= 1'b1;
|
||||
end else if (o_tready) begin
|
||||
state <= EMPTY;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b0;
|
||||
end else begin
|
||||
state <= HALF;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
end
|
||||
end // case: HALF
|
||||
// Both Registers Full.
|
||||
// Upstream can not push to us in this state.
|
||||
// Downstream can always read from us.
|
||||
FULL: begin
|
||||
if (o_tready) begin
|
||||
state <= HALF;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
end
|
||||
else begin
|
||||
state <= FULL;
|
||||
i_tready <= 1'b0;
|
||||
o_tvalid <= 1'b1;
|
||||
end
|
||||
end
|
||||
endcase // case(state)
|
||||
end // else: !if(reset | clear)
|
||||
|
||||
assign o_tdata = (state == FULL) ? data_reg2 : data_reg1;
|
||||
|
||||
|
||||
endmodule // axi_fast_fifo
|
||||
@@ -0,0 +1,867 @@
|
||||
//
|
||||
// Copyright 2017 Ettus Research, A National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0
|
||||
//
|
||||
// Module: axi_replay.v
|
||||
// Description:
|
||||
//
|
||||
// This block implements the state machine and control logic for recording and
|
||||
// playback of AXI-Stream data, using a DMA-accessible memory as a buffer.
|
||||
|
||||
|
||||
module axi_replay #(
|
||||
parameter DATA_WIDTH = 64,
|
||||
parameter ADDR_WIDTH = 32, // Byte address width used by DMA master
|
||||
parameter COUNT_WIDTH = 8 // Length of counters used to connect to the DMA
|
||||
// master's read and write interfaces.
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst, // Synchronous to clk
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Settings Bus
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
input wire set_stb,
|
||||
input wire [ 7:0] set_addr,
|
||||
input wire [31:0] set_data,
|
||||
output reg [31:0] rb_data,
|
||||
input wire [ 7:0] rb_addr,
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// AXI Stream Interface
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// Input
|
||||
input wire [DATA_WIDTH-1:0] i_tdata,
|
||||
input wire i_tvalid,
|
||||
input wire i_tlast,
|
||||
output wire i_tready,
|
||||
|
||||
// Output
|
||||
output wire [DATA_WIDTH-1:0] o_tdata,
|
||||
output wire o_tvalid,
|
||||
output wire o_tlast,
|
||||
input wire o_tready,
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// DMA Interface
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// Write interface
|
||||
output reg [ ADDR_WIDTH-1:0] write_addr, // Byte address for start of write
|
||||
// transaction (64-bit aligned).
|
||||
output reg [COUNT_WIDTH-1:0] write_count, // Count of 64-bit words to write, minus 1.
|
||||
output reg write_ctrl_valid,
|
||||
input wire write_ctrl_ready,
|
||||
output wire [ DATA_WIDTH-1:0] write_data,
|
||||
output wire write_data_valid,
|
||||
input wire write_data_ready,
|
||||
|
||||
// Read interface
|
||||
output reg [ ADDR_WIDTH-1:0] read_addr, // Byte address for start of read
|
||||
// transaction (64-bit aligned).
|
||||
output reg [COUNT_WIDTH-1:0] read_count, // Count of 64-bit words to read, minus 1.
|
||||
output reg read_ctrl_valid,
|
||||
input wire read_ctrl_ready,
|
||||
input wire [ DATA_WIDTH-1:0] read_data,
|
||||
input wire read_data_valid,
|
||||
output wire read_data_ready
|
||||
);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Constants
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// Size constants
|
||||
localparam CMD_WIDTH = 32; // Command width
|
||||
localparam LINES_WIDTH = 28; // Width of cmd_num_lines
|
||||
localparam WORD_SIZE = DATA_WIDTH/8; // Size of DATA_WIDTH in bytes
|
||||
|
||||
// Register offsets
|
||||
localparam [7:0] SR_REC_BASE_ADDR = 128;
|
||||
localparam [7:0] SR_REC_BUFFER_SIZE = 129;
|
||||
localparam [7:0] SR_REC_RESTART = 130;
|
||||
localparam [7:0] SR_REC_FULLNESS = 131;
|
||||
localparam [7:0] SR_PLAY_BASE_ADDR = 132;
|
||||
localparam [7:0] SR_PLAY_BUFFER_SIZE = 133;
|
||||
localparam [7:0] SR_RX_CTRL_COMMAND = 152; // Same offset as radio
|
||||
localparam [7:0] SR_RX_CTRL_HALT = 155; // Same offset as radio
|
||||
localparam [7:0] SR_RX_CTRL_MAXLEN = 156; // Same offset as radio
|
||||
|
||||
|
||||
// Memory buffering parameters:
|
||||
//
|
||||
// Log base 2 of the depth of the input and output FIFOs to use. The FIFOs
|
||||
// should be large enough to store more than a complete burst
|
||||
// (MEM_BURST_SIZE). A size of 9 (512 64-bit words) is one 36-kbit BRAM.
|
||||
localparam REC_FIFO_ADDR_WIDTH = 9; // Log2 of input/record FIFO size
|
||||
localparam PLAY_FIFO_ADDR_WIDTH = 9; // Log2 of output/playback FIFO size
|
||||
//
|
||||
// Amount of data to buffer before writing to RAM. This should be a power of
|
||||
// two so that it evenly divides the AXI_ALIGNMENT requirement. It also must
|
||||
// not exceed 2**COUNT_WIDTH (the maximum count allowed by DMA master).
|
||||
localparam MEM_BURST_SIZE = 2**COUNT_WIDTH; // Size in DATA_WIDTH-sized words
|
||||
//
|
||||
// AXI alignment requirement (4096 bytes) in DATA_WIDTH-bit words
|
||||
localparam AXI_ALIGNMENT = 4096 / WORD_SIZE;
|
||||
//
|
||||
// Clock cycles to wait before writing something less than MEM_BURST_SIZE
|
||||
// to memory.
|
||||
localparam DATA_WAIT_TIMEOUT = 31;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Signals
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// Command wires
|
||||
wire cmd_send_imm_cf, cmd_chain_cf, cmd_reload_cf, cmd_stop_cf;
|
||||
wire [LINES_WIDTH-1:0] cmd_num_lines_cf;
|
||||
|
||||
// Settings registers signals
|
||||
wire [ ADDR_WIDTH-1:0] rec_base_addr_sr; // Byte address
|
||||
wire [ ADDR_WIDTH-1:0] rec_buffer_size_sr; // Size in bytes
|
||||
wire [ ADDR_WIDTH-1:0] play_base_addr_sr; // Byte address
|
||||
wire [ ADDR_WIDTH-1:0] play_buffer_size_sr; // Size in bytes
|
||||
reg rec_restart;
|
||||
reg rec_restart_clear;
|
||||
wire [ CMD_WIDTH-1:0] command;
|
||||
wire command_valid;
|
||||
reg play_halt;
|
||||
reg play_halt_clear;
|
||||
wire [COUNT_WIDTH:0] play_max_len_sr;
|
||||
|
||||
// Command FIFO
|
||||
wire cmd_fifo_valid;
|
||||
reg cmd_fifo_ready;
|
||||
|
||||
// Record Data FIFO (Input)
|
||||
wire [DATA_WIDTH-1:0] rec_fifo_o_tdata;
|
||||
wire rec_fifo_o_tvalid;
|
||||
wire rec_fifo_o_tready;
|
||||
wire [ 15:0] rec_fifo_occupied;
|
||||
|
||||
// Playback Data FIFO (Output)
|
||||
wire [DATA_WIDTH-1:0] play_fifo_i_tdata;
|
||||
wire play_fifo_i_tvalid;
|
||||
wire play_fifo_i_tready;
|
||||
wire [ 15:0] play_fifo_space; // Free space in play_axi_fifo
|
||||
|
||||
// Buffer usage registers
|
||||
reg [ADDR_WIDTH-1:0] rec_buffer_avail; // Amount of free buffer space in words
|
||||
reg [ADDR_WIDTH-1:0] rec_buffer_used; // Amount of occupied buffer space in words
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Registers
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// Record Base Address Register. Address is a byte address. This must be a
|
||||
// multiple of 8 bytes.
|
||||
setting_reg #(
|
||||
.my_addr (SR_REC_BASE_ADDR),
|
||||
.width (ADDR_WIDTH)
|
||||
) sr_rec_base_addr (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.strobe (set_stb),
|
||||
.addr (set_addr),
|
||||
.in (set_data),
|
||||
.out (rec_base_addr_sr),
|
||||
.changed ()
|
||||
);
|
||||
|
||||
|
||||
// Record Buffer Size Register. This indicates the portion of the RAM
|
||||
// allocated to the record buffer, in bytes. This should be a multiple of 8
|
||||
// bytes.
|
||||
setting_reg #(
|
||||
.my_addr (SR_REC_BUFFER_SIZE),
|
||||
.width (ADDR_WIDTH)
|
||||
) sr_rec_buffer_size (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.strobe (set_stb),
|
||||
.addr (set_addr),
|
||||
.in (set_data),
|
||||
.out (rec_buffer_size_sr),
|
||||
.changed ()
|
||||
);
|
||||
|
||||
|
||||
// Playback Base Address Register. Address is a byte address. This must be a
|
||||
// multiple of the 8 bytes.
|
||||
setting_reg #(
|
||||
.my_addr (SR_PLAY_BASE_ADDR),
|
||||
.width (ADDR_WIDTH)
|
||||
) sr_play_base_addr (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.strobe (set_stb),
|
||||
.addr (set_addr),
|
||||
.in (set_data),
|
||||
.out (play_base_addr_sr),
|
||||
.changed ()
|
||||
);
|
||||
|
||||
|
||||
// Playback Buffer Size Register. This indicates the portion of the RAM
|
||||
// allocated to the record buffer, in bytes. This should be a multiple of 8
|
||||
// bytes.
|
||||
setting_reg #(
|
||||
.my_addr (SR_PLAY_BUFFER_SIZE),
|
||||
.width (ADDR_WIDTH)
|
||||
) sr_play_buffer_size (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.strobe (set_stb),
|
||||
.addr (set_addr),
|
||||
.in (set_data),
|
||||
.out (play_buffer_size_sr),
|
||||
.changed ()
|
||||
);
|
||||
|
||||
|
||||
// Record Buffer Restart Register. Software must write to this register after
|
||||
// updating the base address or buffer size. A write to this register means
|
||||
// we need to stop any recording in progress and reset the record buffers
|
||||
// according to the current buffer base address and size registers.
|
||||
always @(posedge clk)
|
||||
begin : sr_restart
|
||||
if(rst) begin
|
||||
rec_restart <= 1'b0;
|
||||
end else begin
|
||||
if(set_stb & (set_addr == SR_REC_RESTART)) begin
|
||||
rec_restart <= 1'b1;
|
||||
end else if (rec_restart_clear) begin
|
||||
rec_restart <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
// Halt Register. A write to this register stops any replay operation as soon
|
||||
// as the current DRAM transaction completes.
|
||||
always @(posedge clk)
|
||||
begin : sr_halt
|
||||
if(rst) begin
|
||||
play_halt <= 1'b0;
|
||||
end else begin
|
||||
if(set_stb & (set_addr == SR_RX_CTRL_HALT)) begin
|
||||
play_halt <= 1'b1;
|
||||
end else if (play_halt_clear) begin
|
||||
play_halt <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
// Play Command Register
|
||||
//
|
||||
// This register mirrors the behavior of the RFNoC RX radio block. All
|
||||
// commands are queued up in the replay command FIFO. The fields are as
|
||||
// follows.
|
||||
//
|
||||
// send_imm [31] Send command immediately (don't use time).
|
||||
//
|
||||
// chain [30] When done with num_lines, immediately run next command.
|
||||
//
|
||||
// reload [29] When done with num_lines, rerun the same command if
|
||||
// cmd_chain is set and no new command is available.
|
||||
//
|
||||
// stop [28] When done with num_lines, stop transferring if
|
||||
// cmd_chain is set.
|
||||
//
|
||||
// num_lines [27:0] Number of samples to transfer to/from block.
|
||||
//
|
||||
setting_reg #(
|
||||
.my_addr (SR_RX_CTRL_COMMAND),
|
||||
.width (CMD_WIDTH)
|
||||
) sr_command (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.strobe (set_stb),
|
||||
.addr (set_addr),
|
||||
.in (set_data),
|
||||
.out (command),
|
||||
.changed (command_valid)
|
||||
);
|
||||
|
||||
|
||||
// Max Length Register. This register sets the number of words for the
|
||||
// maximum packet size.
|
||||
setting_reg #(
|
||||
.my_addr (SR_RX_CTRL_MAXLEN),
|
||||
.width (COUNT_WIDTH+1),
|
||||
.at_reset({1'b1, {COUNT_WIDTH{1'b0}}})
|
||||
) sr_max_len (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.strobe (set_stb),
|
||||
.addr (set_addr),
|
||||
.in (set_data),
|
||||
.out (play_max_len_sr),
|
||||
.changed ()
|
||||
);
|
||||
|
||||
|
||||
// Implement register read
|
||||
always @(*) begin
|
||||
case (rb_addr)
|
||||
SR_REC_BASE_ADDR : rb_data = rec_base_addr_sr;
|
||||
SR_REC_BUFFER_SIZE : rb_data = rec_buffer_size_sr;
|
||||
SR_REC_FULLNESS : rb_data = rec_buffer_used * WORD_SIZE;
|
||||
SR_PLAY_BASE_ADDR : rb_data = play_base_addr_sr;
|
||||
SR_PLAY_BUFFER_SIZE : rb_data = play_buffer_size_sr;
|
||||
SR_RX_CTRL_MAXLEN : rb_data = play_max_len_sr;
|
||||
default : rb_data = 32'h0;
|
||||
endcase
|
||||
end
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Playback Command FIFO
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// This block queues up commands for playback control.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
axi_fifo_short #(
|
||||
.WIDTH (CMD_WIDTH)
|
||||
) command_fifo (
|
||||
.clk (clk),
|
||||
.reset (rst),
|
||||
.clear (play_halt_clear),
|
||||
.i_tdata (command),
|
||||
.i_tvalid (command_valid),
|
||||
.i_tready (),
|
||||
.o_tdata ({cmd_send_imm_cf, cmd_chain_cf, cmd_reload_cf, cmd_stop_cf, cmd_num_lines_cf}),
|
||||
.o_tvalid (cmd_fifo_valid),
|
||||
.o_tready (cmd_fifo_ready),
|
||||
.occupied (),
|
||||
.space ()
|
||||
);
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Record Input Data FIFO
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// This FIFO stores data to be recording into the RAM buffer.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
axi_fifo #(
|
||||
.WIDTH (DATA_WIDTH),
|
||||
.SIZE (REC_FIFO_ADDR_WIDTH)
|
||||
) rec_axi_fifo (
|
||||
.clk (clk),
|
||||
.reset (rst),
|
||||
.clear (1'b0),
|
||||
//
|
||||
.i_tdata (i_tdata),
|
||||
.i_tvalid (i_tvalid),
|
||||
.i_tready (i_tready),
|
||||
//
|
||||
.o_tdata (rec_fifo_o_tdata),
|
||||
.o_tvalid (rec_fifo_o_tvalid),
|
||||
.o_tready (rec_fifo_o_tready),
|
||||
//
|
||||
.space (),
|
||||
.occupied (rec_fifo_occupied)
|
||||
);
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Record State Machine
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// FSM States
|
||||
localparam REC_WAIT_FIFO = 0;
|
||||
localparam REC_CHECK_ALIGN = 1;
|
||||
localparam REC_DMA_REQ = 2;
|
||||
localparam REC_WAIT_DMA_START = 3;
|
||||
localparam REC_WAIT_DMA_COMMIT = 4;
|
||||
|
||||
// State Signals
|
||||
reg [2:0] rec_state;
|
||||
|
||||
// Registers
|
||||
reg [ADDR_WIDTH-1:0] rec_base_addr; // Last base address pulled from settings register
|
||||
reg [ADDR_WIDTH-1:0] rec_buffer_size; // Last buffer size pulled from settings register
|
||||
reg [ADDR_WIDTH-1:0] rec_addr; // Current offset into record buffer
|
||||
reg [ADDR_WIDTH-1:0] rec_size; // Number of words to transfer next
|
||||
reg [ADDR_WIDTH-1:0] rec_size_0; // Pipeline stage for computation of rec_size
|
||||
|
||||
reg signed [ADDR_WIDTH:0] rec_size_aligned; // rec_size reduced to not cross 4k boundary
|
||||
|
||||
// Timer to count how many cycles we've been waiting for new data
|
||||
reg [$clog2(DATA_WAIT_TIMEOUT+1)-1:0] rec_wait_timer;
|
||||
reg rec_wait_timeout;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
rec_state <= REC_WAIT_FIFO;
|
||||
rec_addr <= 0;
|
||||
write_ctrl_valid <= 1'b0;
|
||||
|
||||
rec_buffer_avail <= 0;
|
||||
rec_buffer_used <= 0;
|
||||
rec_wait_timer <= 0;
|
||||
rec_wait_timeout <= 0;
|
||||
|
||||
end else begin
|
||||
|
||||
// Default assignments
|
||||
rec_restart_clear <= 1'b0;
|
||||
|
||||
// Update wait timer
|
||||
if (i_tvalid || !rec_fifo_occupied) begin
|
||||
// If a new word is presented to the input FIFO, or the FIFO is empty,
|
||||
// then reset the timer.
|
||||
rec_wait_timer <= 0;
|
||||
rec_wait_timeout <= 1'b0;
|
||||
end else if (rec_fifo_occupied) begin
|
||||
// If no new word is written, but there's data in the FIFO, update the
|
||||
// timer. Latch timeout condition when we reach out limit.
|
||||
rec_wait_timer <= rec_wait_timer + 1;
|
||||
|
||||
if (rec_wait_timer == DATA_WAIT_TIMEOUT) begin
|
||||
rec_wait_timeout <= 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
// Pre-calculate the aligned size
|
||||
rec_size_aligned <= $signed(AXI_ALIGNMENT) - $signed(rec_addr & (AXI_ALIGNMENT-1));
|
||||
|
||||
//
|
||||
// State logic
|
||||
//
|
||||
case (rec_state)
|
||||
|
||||
REC_WAIT_FIFO : begin
|
||||
// Wait until there's enough data to initiate a transfer from the
|
||||
// FIFO to the RAM.
|
||||
|
||||
// Check if a restart was requested on the record interface
|
||||
if (rec_restart) begin
|
||||
rec_restart_clear <= 1'b1;
|
||||
|
||||
// Latch the new register values. We don't want them to change
|
||||
// while we're running.
|
||||
rec_base_addr <= rec_base_addr_sr;
|
||||
rec_buffer_size <= rec_buffer_size_sr / WORD_SIZE; // Store size in words
|
||||
|
||||
// Reset counters and address any time we update the buffer size or
|
||||
// base address.
|
||||
rec_buffer_avail <= rec_buffer_size_sr / WORD_SIZE; // Store size in words
|
||||
rec_buffer_used <= 0;
|
||||
rec_addr <= rec_base_addr_sr;
|
||||
|
||||
// Check if there's room left in the record RAM buffer
|
||||
end else if (rec_buffer_used < rec_buffer_size) begin
|
||||
// See if we can transfer a full burst
|
||||
if (rec_fifo_occupied >= MEM_BURST_SIZE && rec_buffer_avail >= MEM_BURST_SIZE) begin
|
||||
rec_size_0 <= MEM_BURST_SIZE;
|
||||
rec_state <= REC_CHECK_ALIGN;
|
||||
|
||||
// Otherwise, if we've been waiting a long time, see if we can
|
||||
// transfer less than a burst.
|
||||
end else if (rec_fifo_occupied > 0 && rec_wait_timeout) begin
|
||||
rec_size_0 <= (rec_fifo_occupied <= rec_buffer_avail) ?
|
||||
rec_fifo_occupied : rec_buffer_avail;
|
||||
rec_state <= REC_CHECK_ALIGN;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
REC_CHECK_ALIGN : begin
|
||||
// Check the address alignment, since AXI requires that an access not
|
||||
// cross 4k boundaries (boo), and the axi_dma_master doesn't handle
|
||||
// this automatically (boo again).
|
||||
rec_size <= ($signed({1'b0,rec_size_0}) > rec_size_aligned) ?
|
||||
rec_size_aligned : rec_size_0;
|
||||
|
||||
// DMA interface is ready, so transaction will begin
|
||||
rec_state <= REC_DMA_REQ;
|
||||
end
|
||||
|
||||
REC_DMA_REQ : begin
|
||||
// The write count written to the DMA engine should be 1 less than
|
||||
// the number of words you want to write (not the number of bytes).
|
||||
write_count <= rec_size - 1;
|
||||
|
||||
// Create the physical RAM byte address by combining the address and
|
||||
// base address.
|
||||
write_addr <= rec_addr;
|
||||
|
||||
// Once the interface is ready, make the DMA request
|
||||
if (write_ctrl_ready) begin
|
||||
// Request the write transaction
|
||||
write_ctrl_valid <= 1'b1;
|
||||
rec_state <= REC_WAIT_DMA_START;
|
||||
end
|
||||
end
|
||||
|
||||
REC_WAIT_DMA_START : begin
|
||||
// Wait until DMA interface deasserts ready, indicating it has
|
||||
// started on the request.
|
||||
write_ctrl_valid <= 1'b0;
|
||||
if (!write_ctrl_ready) begin
|
||||
rec_state <= REC_WAIT_DMA_COMMIT;
|
||||
end
|
||||
end
|
||||
|
||||
REC_WAIT_DMA_COMMIT : begin
|
||||
// Wait for the DMA interface to reassert write_ctrl_ready, which
|
||||
// signals that the DMA engine has received a response for the whole
|
||||
// write transaction and (we assume) it has been committed to RAM.
|
||||
// After this, we can update the write address and start the next
|
||||
// transaction.
|
||||
if (write_ctrl_ready) begin
|
||||
rec_addr <= rec_addr + (rec_size * WORD_SIZE);
|
||||
rec_buffer_used <= rec_buffer_used + rec_size;
|
||||
rec_buffer_avail <= rec_buffer_avail - rec_size;
|
||||
rec_state <= REC_WAIT_FIFO;
|
||||
end
|
||||
end
|
||||
|
||||
default : begin
|
||||
rec_state <= REC_WAIT_FIFO;
|
||||
end
|
||||
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
// Connect output of record FIFO to input of DMA write interface
|
||||
assign write_data = rec_fifo_o_tdata;
|
||||
assign write_data_valid = rec_fifo_o_tvalid;
|
||||
assign rec_fifo_o_tready = write_data_ready;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Playback State Machine
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// FSM States
|
||||
localparam PLAY_IDLE = 0;
|
||||
localparam PLAY_WAIT_DATA_READY = 1;
|
||||
localparam PLAY_SIZE_CALC = 2;
|
||||
localparam PLAY_DMA_REQ = 3;
|
||||
localparam PLAY_WAIT_DMA_START = 4;
|
||||
localparam PLAY_WAIT_DMA_COMMIT = 5;
|
||||
localparam PLAY_DONE_CHECK = 6;
|
||||
|
||||
// State Signals
|
||||
reg [2:0] play_state;
|
||||
|
||||
// Registers
|
||||
reg [ADDR_WIDTH-1:0] play_base_addr; // Last base address pulled from settings register
|
||||
reg [ADDR_WIDTH-1:0] play_buffer_size; // Last buffer size pulled from settings register
|
||||
reg [ADDR_WIDTH-1:0] play_addr; // Current byte offset into record buffer
|
||||
reg [ADDR_WIDTH-1:0] play_addr_0; // Pipeline stage for computing play_addr
|
||||
reg [ADDR_WIDTH-1:0] play_addr_1; // Pipeline stage for computing play_addr
|
||||
reg [ADDR_WIDTH-1:0] play_buffer_end; // Address of location after end of buffer
|
||||
reg [ADDR_WIDTH-1:0] max_dma_size; // Maximum size of next transfer, in words
|
||||
//
|
||||
reg [LINES_WIDTH-1:0] cmd_num_lines; // Copy of cmd_num_lines from last command
|
||||
reg [LINES_WIDTH-1:0] play_words_remaining; // Number of lines left to read for command
|
||||
reg cmd_chain; // Copy of cmd_chain from last command
|
||||
reg cmd_reload; // Copy of cmd_reload from last command
|
||||
|
||||
reg play_full_burst_avail; // True if we there's a full burst to read
|
||||
reg play_buffer_avail_nonzero; // True if > 0
|
||||
reg cmd_num_lines_cf_nonzero; // True if > 0
|
||||
reg max_dma_size_ok; // True if it's OK to read max_dma_size
|
||||
|
||||
reg [ADDR_WIDTH-1:0] max_dma_size_m1; // max_dma_size - 1
|
||||
reg [ADDR_WIDTH-1:0] play_words_remaining_m1; // play_words_remaining - 1
|
||||
|
||||
reg [ADDR_WIDTH-1:0] play_buffer_avail; // Number of words left to read in record buffer
|
||||
reg [ADDR_WIDTH-1:0] play_buffer_avail_0; // Pipeline stage for computing play_buffer_avail
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if (rst) begin
|
||||
play_state <= PLAY_IDLE;
|
||||
cmd_fifo_ready <= 1'b0;
|
||||
|
||||
end else begin
|
||||
|
||||
// Calculate how many words are left to read from the record buffer
|
||||
play_full_burst_avail <= (play_buffer_avail >= MEM_BURST_SIZE);
|
||||
play_buffer_avail_nonzero <= (play_buffer_avail > 0);
|
||||
cmd_num_lines_cf_nonzero <= (cmd_num_lines_cf > 0);
|
||||
play_buffer_end <= play_base_addr_sr + play_buffer_size_sr;
|
||||
|
||||
// Default values
|
||||
cmd_fifo_ready <= 1'b0;
|
||||
read_ctrl_valid <= 1'b0;
|
||||
play_halt_clear <= 1'b0;
|
||||
|
||||
//
|
||||
// State logic
|
||||
//
|
||||
case (play_state)
|
||||
PLAY_IDLE : begin
|
||||
// Always start reading at the start of the record buffer
|
||||
play_addr <= play_base_addr_sr;
|
||||
|
||||
// Save off command info, in case we need to repeat the command
|
||||
cmd_num_lines <= cmd_num_lines_cf;
|
||||
cmd_reload <= cmd_reload_cf;
|
||||
cmd_chain <= cmd_chain_cf;
|
||||
|
||||
// Save the buffer info so it doesn't update during playback
|
||||
play_base_addr <= play_base_addr_sr;
|
||||
play_buffer_size <= play_buffer_size_sr;
|
||||
play_buffer_avail <= play_buffer_size_sr / WORD_SIZE;
|
||||
|
||||
// Wait until we receive a command and we have enough data recorded
|
||||
// to honor it.
|
||||
if (cmd_fifo_valid && ~play_halt_clear) begin
|
||||
// Load the number of word remaining to complete this command
|
||||
play_words_remaining <= cmd_num_lines_cf;
|
||||
|
||||
// We don't support time yet, so we require send_imm to do
|
||||
// anything. Also, we can't do anything until we have data recorded.
|
||||
if (cmd_stop_cf) begin
|
||||
// Do nothing, except clear command from the FIFO
|
||||
cmd_fifo_ready <= 1'b1;
|
||||
end else if (cmd_send_imm_cf
|
||||
&& play_buffer_avail_nonzero
|
||||
&& cmd_num_lines_cf_nonzero) begin
|
||||
// Dequeue the command from the FIFO
|
||||
cmd_fifo_ready <= 1'b1;
|
||||
|
||||
play_state <= PLAY_WAIT_DATA_READY;
|
||||
end
|
||||
end else if (play_halt) begin
|
||||
// In case we get a HALT after a command has finished
|
||||
play_halt_clear <= 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
PLAY_WAIT_DATA_READY : begin
|
||||
// Save the maximum size we can read from RAM
|
||||
max_dma_size <= play_full_burst_avail ? MEM_BURST_SIZE : play_buffer_avail;
|
||||
|
||||
// Check if we got a halt command while waiting
|
||||
if (play_halt) begin
|
||||
play_halt_clear <= 1'b1;
|
||||
play_state <= PLAY_IDLE;
|
||||
|
||||
// Wait for output FIFO to empty sufficiently so we can read an
|
||||
// entire burst at once. This may be more space than needed, but we
|
||||
// won't know the exact size until the next state.
|
||||
end else if (play_fifo_space >= MEM_BURST_SIZE) begin
|
||||
play_state <= PLAY_SIZE_CALC;
|
||||
end
|
||||
end
|
||||
|
||||
PLAY_SIZE_CALC : begin
|
||||
// Do some intermediate calculations to determine what the read_count
|
||||
// should be.
|
||||
play_words_remaining_m1 <= play_words_remaining-1;
|
||||
max_dma_size_m1 <= max_dma_size-1;
|
||||
max_dma_size_ok <= play_words_remaining >= max_dma_size;
|
||||
play_state <= PLAY_DMA_REQ;
|
||||
end
|
||||
|
||||
PLAY_DMA_REQ : begin
|
||||
// Load the size of the next read into a register. We try to read the
|
||||
// max amount available (up to the burst size) or however many words
|
||||
// are needed to reach the end of the RAM buffer.
|
||||
//
|
||||
// The read count written to the DMA engine should be 1 less than the
|
||||
// number of words you want to read (not the number of bytes).
|
||||
read_count <= max_dma_size_ok ? max_dma_size_m1 : play_words_remaining_m1;
|
||||
|
||||
// Load the address to read. Note that we don't do an alignment check
|
||||
// since we assume that multiples of MEM_BURST_SIZE meet the
|
||||
// AXI_ALIGNMENT requirement.
|
||||
read_addr <= play_addr;
|
||||
|
||||
// Request the read transaction as soon as DMA interface is ready
|
||||
if (read_ctrl_ready) begin
|
||||
read_ctrl_valid <= 1'b1;
|
||||
play_state <= PLAY_WAIT_DMA_START;
|
||||
end
|
||||
end
|
||||
|
||||
PLAY_WAIT_DMA_START : begin
|
||||
// Wait until DMA interface deasserts ready, indicating it has
|
||||
// started on the request.
|
||||
read_ctrl_valid <= 1'b0;
|
||||
if (!read_ctrl_ready) begin
|
||||
// Update values for next transaction
|
||||
play_addr_0 <= play_addr + ({{(ADDR_WIDTH-COUNT_WIDTH){1'b0}}, read_count} + 1) * WORD_SIZE;
|
||||
play_words_remaining <= play_words_remaining - ({1'b0, read_count} + 1);
|
||||
play_buffer_avail_0 <= play_buffer_avail - ({1'b0, read_count} + 1);
|
||||
|
||||
play_state <= PLAY_WAIT_DMA_COMMIT;
|
||||
end
|
||||
end
|
||||
|
||||
PLAY_WAIT_DMA_COMMIT : begin
|
||||
// Wait for the DMA interface to reassert read_ctrl_ready, which
|
||||
// signals that the DMA engine has received a response for the whole
|
||||
// read transaction.
|
||||
if (read_ctrl_ready) begin
|
||||
// Check if we need to wrap the address for the next transaction
|
||||
if (play_addr_0 >= play_buffer_end) begin
|
||||
play_addr_1 <= play_base_addr_sr;
|
||||
play_buffer_avail <= play_buffer_size_sr / WORD_SIZE;
|
||||
end else begin
|
||||
play_addr_1 <= play_addr_0;
|
||||
play_buffer_avail <= play_buffer_avail_0;
|
||||
end
|
||||
|
||||
play_state <= PLAY_DONE_CHECK;
|
||||
end
|
||||
end
|
||||
|
||||
PLAY_DONE_CHECK : begin
|
||||
play_addr <= play_addr_1;
|
||||
|
||||
// Check if we have more data to transfer for this command
|
||||
if (play_words_remaining) begin
|
||||
play_state <= PLAY_WAIT_DATA_READY;
|
||||
|
||||
// Check if we're chaining
|
||||
end else if (cmd_chain) begin
|
||||
// Check if there's a new command waiting
|
||||
if (cmd_fifo_valid) begin
|
||||
// Load the next command. Note that we don't reset the playback
|
||||
// address when commands are chained together.
|
||||
play_words_remaining <= cmd_num_lines_cf;
|
||||
cmd_num_lines <= cmd_num_lines_cf;
|
||||
cmd_reload <= cmd_reload_cf;
|
||||
cmd_chain <= cmd_chain_cf;
|
||||
|
||||
// Dequeue the command from the FIFO
|
||||
cmd_fifo_ready <= 1'b1;
|
||||
|
||||
// Stop if it's a stop command, otherwise restart
|
||||
if (cmd_stop_cf) begin
|
||||
play_state <= PLAY_IDLE;
|
||||
end else begin
|
||||
play_state <= PLAY_WAIT_DATA_READY;
|
||||
end
|
||||
|
||||
// Check if we need to restart the previous command
|
||||
end else if (cmd_reload) begin
|
||||
play_words_remaining <= cmd_num_lines;
|
||||
play_state <= PLAY_WAIT_DATA_READY;
|
||||
end
|
||||
// Nothing left to do
|
||||
end else begin
|
||||
play_state <= PLAY_IDLE;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
// Connect output of DMA master to playback data FIFO
|
||||
assign play_fifo_i_tdata = read_data;
|
||||
assign play_fifo_i_tvalid = read_data_valid;
|
||||
assign read_data_ready = play_fifo_i_tready;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// TLAST Generation
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// This block monitors the signals to/from the DMA master and generates the
|
||||
// TLAST signal. We assert TLAST at the end of every read transaction and
|
||||
// after every play_max_len_sr words, so that no packets are longer than the
|
||||
// length indicated by the max_len register.
|
||||
//
|
||||
// The timing of this block relies on the fact that read_ctrl_ready is not
|
||||
// reasserted by the DMA master until after TLAST gets asserted.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
reg [COUNT_WIDTH-1:0] read_counter;
|
||||
reg [COUNT_WIDTH-1:0] length_counter;
|
||||
reg play_fifo_i_tlast;
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if (rst) begin
|
||||
play_fifo_i_tlast <= 1'b0;
|
||||
end else begin
|
||||
// Check if we're requesting a read transaction
|
||||
if (read_ctrl_valid && read_ctrl_ready) begin
|
||||
// Initialize read_counter for new transaction
|
||||
read_counter <= read_count;
|
||||
length_counter <= play_max_len_sr;
|
||||
|
||||
// If read_count is 0, then the first word is also the last word
|
||||
if (read_count == 0) begin
|
||||
play_fifo_i_tlast <= 1'b1;
|
||||
end
|
||||
|
||||
// Track the number of words read out by DMA master
|
||||
end else if (read_data_valid && read_data_ready) begin
|
||||
read_counter <= read_counter - 1;
|
||||
length_counter <= length_counter - 1;
|
||||
|
||||
// Check if the word currently being output is the last word of a
|
||||
// packet, which means we need to clear tlast.
|
||||
if (play_fifo_i_tlast) begin
|
||||
// But make sure that the next word isn't also the last of a DMA
|
||||
// burst, for which we will need to keep tlast asserted.
|
||||
if (read_counter != 1) begin
|
||||
play_fifo_i_tlast <= 1'b0;
|
||||
end
|
||||
|
||||
// Restart length counter
|
||||
length_counter <= play_max_len_sr;
|
||||
|
||||
// Check if the next word to be output should be the last of a packet.
|
||||
end else if (read_counter == 1 || length_counter == 2) begin
|
||||
play_fifo_i_tlast <= 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Playback Output Data FIFO
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// This FIFO buffers data that has been read out of RAM as part of a playback
|
||||
// operation.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
axi_fifo #(
|
||||
.WIDTH (DATA_WIDTH+1),
|
||||
.SIZE (PLAY_FIFO_ADDR_WIDTH)
|
||||
) play_axi_fifo (
|
||||
.clk (clk),
|
||||
.reset (rst),
|
||||
.clear (1'b0),
|
||||
//
|
||||
.i_tdata ({play_fifo_i_tlast, play_fifo_i_tdata}),
|
||||
.i_tvalid (play_fifo_i_tvalid),
|
||||
.i_tready (play_fifo_i_tready),
|
||||
//
|
||||
.o_tdata ({o_tlast, o_tdata}),
|
||||
.o_tvalid (o_tvalid),
|
||||
.o_tready (o_tready),
|
||||
//
|
||||
.space (play_fifo_space),
|
||||
.occupied ()
|
||||
);
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,296 @@
|
||||
//
|
||||
// Copyright 2016 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Strips preamble, EOP, and CRC/num_words check
|
||||
// <preamble> <packet> <EOP> [control_chksum,word_count,payload_chksum]
|
||||
// <preamble> = 64'h9E6774129E677412
|
||||
// <EOP> = 64'h2A1D632F2A1D632F
|
||||
|
||||
module axi_strip_preamble #(
|
||||
parameter WIDTH=64,
|
||||
parameter MAX_PKT_SIZE=512 //Set to 128 in sim to fill up buffers faster to help try and trigger more fail cases.
|
||||
) (
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
//
|
||||
input [WIDTH-1:0] i_tdata,
|
||||
input i_tvalid,
|
||||
output i_tready,
|
||||
//
|
||||
output [WIDTH-1:0] o_tdata,
|
||||
output o_tlast,
|
||||
output o_tvalid,
|
||||
input o_tready,
|
||||
//
|
||||
output pkt_dropped,
|
||||
output crc_err,
|
||||
output crit_error
|
||||
);
|
||||
|
||||
function [0:0] cvita_get_has_time;
|
||||
input [63:0] header;
|
||||
cvita_get_has_time = header[61];
|
||||
endfunction
|
||||
|
||||
//State machine info
|
||||
reg [1:0] state, next_state;
|
||||
|
||||
localparam IDLE = 0;
|
||||
localparam CHECK_HDR = 1;
|
||||
localparam PASS = 2;
|
||||
localparam CHECK_CRC = 3;
|
||||
|
||||
localparam PAYLOAD_WORDCOUNT_WIDTH = 16;
|
||||
localparam PAYLOAD_CHKSUM_WIDTH = 32;
|
||||
localparam CONTROL_CHKSUM_WIDTH = 16;
|
||||
|
||||
//Note that held_word is required when EOP is detected
|
||||
//so that we can rewrite into memory the last word + last bit
|
||||
reg [WIDTH-1:0] held_word;
|
||||
reg [WIDTH-1:0] held_word_r;
|
||||
always @(posedge clk) begin
|
||||
if(i_tvalid && i_tready) begin
|
||||
held_word <= i_tdata;
|
||||
held_word_r <= held_word;
|
||||
end
|
||||
end
|
||||
|
||||
//Look for next word that specifies if frame has timestamp
|
||||
reg [PAYLOAD_WORDCOUNT_WIDTH-1:0] cntrl_length = 16'd2;
|
||||
always @(posedge clk) begin
|
||||
if ((next_state == CHECK_HDR || state == CHECK_HDR) && i_tvalid)
|
||||
cntrl_length <= cvita_get_has_time(i_tdata) ? 16'd2 : 16'd1;
|
||||
end
|
||||
|
||||
reg [PAYLOAD_WORDCOUNT_WIDTH-1:0] word_count;
|
||||
wire det_preamble = (i_tdata == 64'h9E6774129E677412);
|
||||
wire det_eop = (i_tdata == 64'h2A1D632F2A1D632F);
|
||||
|
||||
wire [PAYLOAD_CHKSUM_WIDTH-1:0] payload_chksum;
|
||||
wire [CONTROL_CHKSUM_WIDTH-1:0] control_chksum;
|
||||
|
||||
// Payload LFSR. Must hold LFSR once detected EOP so checksum does not keep updating after EOP
|
||||
// Note the payload LFSR also includes the EOP in its checksum
|
||||
crc_xnor #(.INPUT_WIDTH(WIDTH), .OUTPUT_WIDTH(PAYLOAD_CHKSUM_WIDTH)) payload_chksum_gen (
|
||||
.clk(clk), .rst(word_count<=cntrl_length), .hold(~(i_tready && i_tvalid) || det_eop || state == CHECK_CRC),
|
||||
.input_data(i_tdata), .crc_out(payload_chksum)
|
||||
);
|
||||
|
||||
// Control LFSR. Varies in size based on whether the control information includes a timestamp
|
||||
// Hold the LFSR once the control word(s) have been parsed
|
||||
crc_xnor #(.INPUT_WIDTH(WIDTH), .OUTPUT_WIDTH(CONTROL_CHKSUM_WIDTH)) control_chksum_gen (
|
||||
.clk(clk), .rst(word_count=='d0), .hold(~(i_tready && i_tvalid) || word_count>=cntrl_length),
|
||||
.input_data(i_tdata), .crc_out(control_chksum)
|
||||
);
|
||||
|
||||
//Good frame is when the word_count is correct and the control checksum passes.
|
||||
//Allows passthrough of payloads with bit errors to reduce overall dropped frame rate
|
||||
wire frame_good = (word_count == i_tdata[47:32]) && (control_chksum == i_tdata[63:48]) && state == CHECK_CRC;
|
||||
|
||||
//CRC error only increments when the state machine makes it to CHECK_CRC state
|
||||
//It will not increment if a preamble or eop is detected outside of IDLE
|
||||
wire payload_crc_check = (payload_chksum == i_tdata[31:0]) && state == CHECK_CRC;
|
||||
assign crc_err = (~frame_good || ~payload_crc_check) && state == CHECK_CRC && i_tvalid;
|
||||
|
||||
//Increment word_count for payload and EOP
|
||||
always @(posedge clk) begin
|
||||
if (state == IDLE || pkt_dropped) begin
|
||||
word_count <= 0;
|
||||
end else if ((state == PASS || state == CHECK_HDR) && i_tready && i_tvalid) begin
|
||||
word_count <= word_count+1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (reset | clear) begin
|
||||
state <= IDLE;
|
||||
end else begin
|
||||
state <= next_state;
|
||||
end
|
||||
end
|
||||
|
||||
//Only drop packet if preamble detected outside of idle or bad frame was detected during CRC check
|
||||
assign pkt_dropped = ((state != IDLE) && det_preamble && i_tvalid) || ((state == CHECK_CRC) && ~frame_good && i_tvalid);
|
||||
|
||||
//When preamble is missing or has bit error, state machine stays in IDLE
|
||||
//When EOP is missing or has bit error, either the next preamble is detected and resets logic
|
||||
//or state machine exits on next EOP and fails CRC check.
|
||||
//For cables with very high BER its possible for the write buffer to fill up which causes a critical error and resets everything
|
||||
always @(*) begin
|
||||
case(state)
|
||||
IDLE: begin
|
||||
if (det_preamble && i_tvalid) //Preamble detected so check to see if timestamp is part of header
|
||||
next_state = CHECK_HDR;
|
||||
else
|
||||
next_state = IDLE;
|
||||
end
|
||||
|
||||
//Check incoming word to see if frame will have timestamp
|
||||
CHECK_HDR: begin
|
||||
if(crit_error) begin //Critical error so reset SM
|
||||
next_state = IDLE;
|
||||
end else if(~det_preamble && i_tvalid && i_tready) begin //Found control word so go to normal pass state
|
||||
next_state = PASS;
|
||||
end else begin
|
||||
next_state = CHECK_HDR;
|
||||
end
|
||||
end
|
||||
|
||||
//Note if early preamble is detected in PASS state everything is reset for the next frame
|
||||
PASS: begin
|
||||
if(crit_error) begin //Critical error so reset SM
|
||||
next_state = IDLE;
|
||||
end else if(det_preamble && i_tvalid) begin //Saw preamble so drop packet and start over
|
||||
next_state = CHECK_HDR;
|
||||
end else if(det_eop && i_tvalid && i_tready) begin //Saw EOP so check for crc on next word
|
||||
next_state = CHECK_CRC;
|
||||
end else begin
|
||||
next_state = PASS;
|
||||
end
|
||||
end
|
||||
|
||||
//Check for crc and go to idle or go back to pass if another preamble is detected
|
||||
CHECK_CRC: begin
|
||||
if(crit_error) begin //Critical error so reset SM
|
||||
next_state = IDLE;
|
||||
end else if(det_preamble && i_tvalid) begin //Saw preamble so drop packet and start over
|
||||
next_state = CHECK_HDR;
|
||||
end else if(i_tvalid) begin //Got word which should've been the CRC
|
||||
next_state = IDLE;
|
||||
end else begin
|
||||
next_state = CHECK_CRC;
|
||||
end
|
||||
end
|
||||
|
||||
default: begin
|
||||
next_state = IDLE;
|
||||
end
|
||||
|
||||
endcase
|
||||
end
|
||||
|
||||
wire [WIDTH-1:0] buf_tdata;
|
||||
wire buf_tlast, buf_tvalid, buf_tready, buf_empty;
|
||||
reg buf_full = 1'b0;
|
||||
wire [$clog2(MAX_PKT_SIZE)-1:0] valid_rd_addr;
|
||||
reg buf_empty_r;
|
||||
|
||||
assign mem_tvalid = (state == PASS || state == CHECK_HDR) ? (i_tvalid && ~pkt_dropped) : 1'b0;
|
||||
assign i_tready = (state == PASS || state == CHECK_HDR) ? buf_tready : 1'b1;
|
||||
|
||||
assign crit_error = buf_full && buf_empty; //This should never happen, if it does that indicates poor BER over Aurora or packet size too large
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
//Fifo to store incoming packets
|
||||
//The write pntr rewinds whenever an error occurs
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
wire int_tready;
|
||||
|
||||
reg [$clog2(MAX_PKT_SIZE)-1:0] wr_addr, prev_wr_addr, rd_addr, old_rd_addr;
|
||||
reg [$clog2(MAX_PKT_SIZE):0] in_pkt_cnt, out_pkt_cnt;
|
||||
wire read = ~buf_empty && (int_tready || buf_empty_r); //Read from buffer if its no longer empty to prime output reg
|
||||
wire almost_full = (wr_addr == valid_rd_addr-1'b1); //We need to look at the masked rd_addr in case its 1 ahead
|
||||
|
||||
assign buf_tready = ~buf_full;
|
||||
wire write = mem_tvalid && buf_tready && ~det_eop;
|
||||
|
||||
//If frame was good we need to go back and rewrite the last word and set the last bit
|
||||
wire [WIDTH:0] int_write_data = (frame_good) ? {1'b1,held_word_r} : {1'b0,i_tdata};
|
||||
wire [$clog2(MAX_PKT_SIZE)-1:0] int_wr_addr = (frame_good) ? wr_addr-1 : wr_addr;
|
||||
|
||||
//BRAM inferred
|
||||
wire [WIDTH:0] buf_data;
|
||||
ram_2port #(.DWIDTH(WIDTH+1), .AWIDTH($clog2(MAX_PKT_SIZE))) pkt_buf
|
||||
(.clka(clk), .ena(1'b1), .wea(1'b1), .addra(int_wr_addr),
|
||||
.dia(int_write_data), .doa(),
|
||||
.clkb(clk), .enb(read), .web(1'b0), .addrb(rd_addr), .dib(),
|
||||
.dob(buf_data));
|
||||
|
||||
// Write logic
|
||||
always @(posedge clk) begin
|
||||
|
||||
// Rewind logic
|
||||
if(pkt_dropped || crit_error)
|
||||
wr_addr <= prev_wr_addr;
|
||||
else if(write)
|
||||
wr_addr <= wr_addr + 1'b1;
|
||||
|
||||
if (almost_full) begin
|
||||
if (write && ~read) begin
|
||||
buf_full <= 1'b1;
|
||||
end
|
||||
end else begin
|
||||
if (~write && read) begin
|
||||
buf_full <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
if (frame_good) begin
|
||||
in_pkt_cnt <= in_pkt_cnt + 1'b1;
|
||||
prev_wr_addr <= wr_addr;
|
||||
end
|
||||
|
||||
if (reset || clear) begin
|
||||
wr_addr <= 0;
|
||||
prev_wr_addr <= 0;
|
||||
in_pkt_cnt <= 0;
|
||||
end
|
||||
|
||||
if(reset || clear || crit_error) begin
|
||||
buf_full <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
// Read logic. Hold data if pkt_count is equal
|
||||
assign buf_empty = in_pkt_cnt == out_pkt_cnt;
|
||||
reg last_word;
|
||||
|
||||
//Use current read addr only if read is enabled
|
||||
assign valid_rd_addr = (read) ? rd_addr : old_rd_addr;
|
||||
|
||||
assign buf_tvalid = ~buf_empty_r && ~(last_word && buf_empty);
|
||||
|
||||
assign buf_tdata = buf_data[WIDTH-1:0];
|
||||
assign buf_tlast = buf_data[WIDTH];
|
||||
|
||||
always @(posedge clk) begin
|
||||
buf_empty_r <= buf_empty;
|
||||
|
||||
if (read) old_rd_addr <= rd_addr; //Keeps track of last valid rd_addr
|
||||
|
||||
//Last word has two possibilities
|
||||
//If buffer empty then we need to rewind rd_addr and mask reading from buffer
|
||||
//If buffer is not empty continue with rd_addr and continue reading from buffer
|
||||
last_word <= buf_tvalid && int_tready && buf_tlast;
|
||||
|
||||
//Need to rewind rd_addr since it incremented one too far
|
||||
//This means there will be one cycle where rd_addr is ahead of where it should be
|
||||
//Other logic that uses rd_addr will have it masked for that cycle
|
||||
if (last_word && buf_empty) rd_addr <= rd_addr - 1;
|
||||
else if (read) rd_addr <= rd_addr + 1;
|
||||
|
||||
// Prevent output until we have a full packet
|
||||
if (buf_tvalid && int_tready && buf_tlast) begin
|
||||
out_pkt_cnt <= out_pkt_cnt + 1'b1;
|
||||
end
|
||||
|
||||
if (reset || clear) begin
|
||||
old_rd_addr <= 0;
|
||||
rd_addr <= 0;
|
||||
out_pkt_cnt <= 0;
|
||||
end
|
||||
end
|
||||
|
||||
assign o_tlast = buf_tlast;
|
||||
assign o_tdata = buf_tdata;
|
||||
assign o_tvalid = buf_tvalid;
|
||||
assign int_tready = o_tready;
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// Copyright 2016 Ettus Research
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Convert AXI Stream to a strobed interface.
|
||||
// Note: Not especially useful if simply wanting to set
|
||||
//
|
||||
|
||||
module axi_to_strobed #(
|
||||
parameter WIDTH = 32,
|
||||
parameter FIFO_SIZE = 1,
|
||||
parameter MIN_RATE = 256
|
||||
)(
|
||||
input clk, input reset, input clear,
|
||||
input [$clog2(MIN_RATE):0] out_rate, // Number of clock cycles between strobes
|
||||
input ready,
|
||||
output error, // Output strobe but no data
|
||||
input [WIDTH-1:0] i_tdata, input i_tvalid, input i_tlast, output i_tready,
|
||||
output out_stb, output out_last, output [WIDTH-1:0] out_data
|
||||
);
|
||||
|
||||
reg strobe;
|
||||
wire valid;
|
||||
reg [$clog2(MIN_RATE):0] counter = 1;
|
||||
always @(posedge clk) begin
|
||||
if (reset | clear) begin
|
||||
strobe <= 1'b0;
|
||||
counter <= 1;
|
||||
end else if (ready) begin
|
||||
if (counter >= out_rate) begin
|
||||
strobe <= 1'b1;
|
||||
counter <= 1;
|
||||
end else begin
|
||||
strobe <= 1'b0;
|
||||
counter <= counter + 1'b1;
|
||||
end
|
||||
end else begin
|
||||
strobe <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
axi_fifo #(.WIDTH(WIDTH+1), .SIZE(FIFO_SIZE)) axi_fifo (
|
||||
.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata({i_tlast,i_tdata}), .i_tvalid(i_tvalid), .i_tready(i_tready),
|
||||
.o_tdata({out_last,out_data}), .o_tvalid(valid), .o_tready(strobe),
|
||||
.space(), .occupied());
|
||||
|
||||
assign out_stb = valid & strobe;
|
||||
assign error = ~valid & strobe;
|
||||
endmodule
|
||||
@@ -0,0 +1,125 @@
|
||||
//
|
||||
// Copyright 2019 Ettus Research, A National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: axis_data_swap
|
||||
// Description:
|
||||
// A generic data swapper module for AXI-Stream. The contents of
|
||||
// tdata are swapped based on the tswap signal. For each bit 'i'
|
||||
// in tswap, adjacent words of width 2^i are swapped if tswap[i]
|
||||
// is high. For example, if tswap[3] = 1, then each byte in tdata
|
||||
// will be swapped with its adjacent neighbor. It is permissible
|
||||
// for tswap to change for each transfer in an AXIS packet.
|
||||
// Swapping can also be configured to be static (zero logic) by
|
||||
// setting DYNAMIC = 0. To reduce area, certain swap stages can
|
||||
// even be disabled. For example, if STAGES_EN[2:0] is set to 0
|
||||
// then the lowest granularity for swaps will be a byte.
|
||||
//
|
||||
// Parameters:
|
||||
// - DATA_W: Width of the tdata bus in bits
|
||||
// - USER_W: Width of the tuser bus in bits
|
||||
// - STAGES_EN: Which swap stages are enabled.
|
||||
// - DYNAMIC: Dynamic swapping enabled (use tswap)
|
||||
//
|
||||
// Signals:
|
||||
// - s_axis_*: The input AXI stream
|
||||
// - m_axis_*: The output AXI stream
|
||||
//
|
||||
|
||||
module axis_data_swap #(
|
||||
parameter integer DATA_W = 256,
|
||||
parameter integer USER_W = 1,
|
||||
parameter [$clog2(DATA_W)-1:0] STAGES_EN = 'hFFFFFFFF, //@HACK: Vivado does not allow $clog2 in value of this expr
|
||||
parameter [0:0] DYNAMIC = 1
|
||||
)(
|
||||
// Clock and Reset
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
// Input AXIS
|
||||
input wire [DATA_W-1:0] s_axis_tdata,
|
||||
input wire [$clog2(DATA_W)-2:0] s_axis_tswap,
|
||||
input wire [USER_W-1:0] s_axis_tuser,
|
||||
input wire s_axis_tlast,
|
||||
input wire s_axis_tvalid,
|
||||
output wire s_axis_tready,
|
||||
// Output AXIS
|
||||
output wire [DATA_W-1:0] m_axis_tdata,
|
||||
output wire [USER_W-1:0] m_axis_tuser,
|
||||
output wire m_axis_tlast,
|
||||
output wire m_axis_tvalid,
|
||||
input wire m_axis_tready
|
||||
);
|
||||
|
||||
parameter SWAP_STAGES = $clog2(DATA_W);
|
||||
parameter SWAP_W = $clog2(DATA_W)-1;
|
||||
genvar s, w;
|
||||
|
||||
wire [DATA_W-1:0] stg_tdata [0:SWAP_STAGES], stg_tdata_swp[0:SWAP_STAGES], stg_tdata_res[0:SWAP_STAGES];
|
||||
wire [SWAP_W-1:0] stg_tswap [0:SWAP_STAGES];
|
||||
wire [USER_W-1:0] stg_tuser [0:SWAP_STAGES];
|
||||
wire stg_tlast [0:SWAP_STAGES];
|
||||
wire stg_tvalid[0:SWAP_STAGES];
|
||||
wire stg_tready[0:SWAP_STAGES];
|
||||
|
||||
// Connect input and output to stage wires
|
||||
generate
|
||||
assign stg_tdata [0] = s_axis_tdata;
|
||||
assign stg_tswap [0] = s_axis_tswap;
|
||||
assign stg_tuser [0] = s_axis_tuser;
|
||||
assign stg_tlast [0] = s_axis_tlast;
|
||||
assign stg_tvalid[0] = s_axis_tvalid;
|
||||
assign s_axis_tready = stg_tready[0];
|
||||
|
||||
assign m_axis_tdata = stg_tdata [SWAP_STAGES];
|
||||
assign m_axis_tuser = stg_tuser [SWAP_STAGES];
|
||||
assign m_axis_tlast = stg_tlast [SWAP_STAGES];
|
||||
assign m_axis_tvalid = stg_tvalid[SWAP_STAGES];
|
||||
assign stg_tready[SWAP_STAGES] = m_axis_tready;
|
||||
endgenerate
|
||||
|
||||
// Instantiate AXIS flip-flops for each stage
|
||||
generate
|
||||
for (s = 0; s < SWAP_STAGES; s=s+1) begin
|
||||
if (STAGES_EN[SWAP_STAGES-s-1]) begin
|
||||
// Swap Logic
|
||||
for (w = 0; w < (1<<s); w=w+1) begin
|
||||
assign stg_tdata_swp[s][(w*(DATA_W/(1<<s)))+:(DATA_W/(1<<s))] =
|
||||
stg_tdata[s][(((1<<s)-w-1)*(DATA_W/(1<<s)))+:(DATA_W/(1<<s))];
|
||||
end
|
||||
if (DYNAMIC) begin
|
||||
// Honor tswap in DYNAMIC mode.
|
||||
// Also add a flip_flop to break the long start-to-end critical path
|
||||
assign stg_tdata_res[s] = (s > 0 && stg_tswap[s][SWAP_W-s]) ?
|
||||
stg_tdata_swp[s] : stg_tdata[s];
|
||||
// Flip-flop
|
||||
axi_fifo_flop #(.WIDTH(DATA_W+SWAP_W+USER_W+1)) reg_i (
|
||||
.clk(clk), .reset(rst), .clear(1'b0),
|
||||
.i_tdata({stg_tlast[s], stg_tuser[s], stg_tswap[s], stg_tdata_res[s]}),
|
||||
.i_tvalid(stg_tvalid[s]), .i_tready(stg_tready[s]),
|
||||
.o_tdata({stg_tlast[s+1], stg_tuser[s+1], stg_tswap[s+1], stg_tdata[s+1]}),
|
||||
.o_tvalid(stg_tvalid[s+1]), .o_tready(stg_tready[s+1]),
|
||||
.occupied(), .space()
|
||||
);
|
||||
end else begin
|
||||
// Static swapping logic
|
||||
assign stg_tdata [s+1] = stg_tdata_swp[s];
|
||||
assign stg_tswap [s+1] = stg_tswap [s];
|
||||
assign stg_tuser [s+1] = stg_tuser [s];
|
||||
assign stg_tlast [s+1] = stg_tlast [s];
|
||||
assign stg_tvalid[s+1] = stg_tvalid [s];
|
||||
assign stg_tready[s] = stg_tready [s+1];
|
||||
end
|
||||
end else begin
|
||||
// Skip this stage
|
||||
assign stg_tdata [s+1] = stg_tdata [s];
|
||||
assign stg_tswap [s+1] = stg_tswap [s];
|
||||
assign stg_tuser [s+1] = stg_tuser [s];
|
||||
assign stg_tlast [s+1] = stg_tlast [s];
|
||||
assign stg_tvalid[s+1] = stg_tvalid[s];
|
||||
assign stg_tready[s] = stg_tready[s+1];
|
||||
end
|
||||
end
|
||||
endgenerate
|
||||
|
||||
endmodule // axis_data_swap
|
||||
@@ -0,0 +1,96 @@
|
||||
//
|
||||
// Copyright 2018 Ettus Research, A National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: axis_downsizer
|
||||
// Description:
|
||||
// An AXI-Stream width conversion module that narrows the input
|
||||
// sample with by a factor of RATIO.
|
||||
// NOTE: This module has end-to-end combanitorial paths. For a
|
||||
// pipelined version, please use axis_width_conv
|
||||
//
|
||||
// Parameters:
|
||||
// - OUT_DATA_W: The bitwidth of the output data bus. The width of the
|
||||
// input data bus is OUT_DATA_W*RATIO
|
||||
// - OUT_USER_W: The bitwidth of the output user bus. The width of the
|
||||
// input user bus is OUT_USER_W*RATIO
|
||||
// - RATIO: The downsizing ratio
|
||||
//
|
||||
// Signals:
|
||||
// - s_axis_* : Input sample stream (AXI-Stream)
|
||||
// - m_axis_* : Output sample stream (AXI-Stream)
|
||||
|
||||
module axis_downsizer #(
|
||||
parameter OUT_DATA_W = 32,
|
||||
parameter OUT_USER_W = 1,
|
||||
parameter RATIO = 4
|
||||
)(
|
||||
// Clock, reset and settings
|
||||
input wire clk, // Clock
|
||||
input wire reset, // Reset
|
||||
// Data In (AXI-Stream)
|
||||
input wire [(OUT_DATA_W*RATIO)-1:0] s_axis_tdata, // Input stream tdata
|
||||
input wire [(OUT_USER_W*RATIO)-1:0] s_axis_tuser, // Input stream tuser
|
||||
input wire [RATIO-1:0] s_axis_tkeep, // Input stream tkeep
|
||||
input wire s_axis_tlast, // Input stream tlast
|
||||
input wire s_axis_tvalid, // Input stream tvalid
|
||||
output wire s_axis_tready, // Input stream tready
|
||||
// Data Out (AXI-Stream)
|
||||
output wire [OUT_DATA_W-1:0] m_axis_tdata, // Output stream tdata
|
||||
output wire [OUT_USER_W-1:0] m_axis_tuser, // Output stream tuser
|
||||
output wire m_axis_tlast, // Output stream tlast
|
||||
output wire m_axis_tvalid, // Output stream tvalid
|
||||
input wire m_axis_tready // Output stream tready
|
||||
);
|
||||
|
||||
genvar i;
|
||||
generate if (RATIO != 1) begin
|
||||
// Constants
|
||||
localparam [$clog2(RATIO)-1:0] SEL_FIRST = 'd0;
|
||||
localparam [$clog2(RATIO)-1:0] SEL_LAST = RATIO-1;
|
||||
localparam [RATIO-1:0] KEEP_FIRST = {{(RATIO-1){1'b0}}, 1'b1};
|
||||
localparam [RATIO-1:0] KEEP_ALL = {(RATIO){1'b1}};
|
||||
|
||||
// Keep a binary-coded and one-hot version of the current
|
||||
// section of the input that is being processed.
|
||||
reg [$clog2(RATIO)-1:0] select = SEL_FIRST;
|
||||
reg [RATIO-1:0] keep = KEEP_FIRST;
|
||||
|
||||
// State machine to drive the select bits for the
|
||||
// input selection MUX.
|
||||
always @(posedge clk) begin
|
||||
if (reset) begin
|
||||
select <= SEL_FIRST;
|
||||
keep <= KEEP_FIRST;
|
||||
end else if (m_axis_tvalid & m_axis_tready) begin
|
||||
select <= (select == SEL_LAST || m_axis_tlast) ? SEL_FIRST : (select + 'd1);
|
||||
keep <= (keep == KEEP_ALL || m_axis_tlast) ? KEEP_FIRST : {keep[RATIO-2:0], 1'b1};
|
||||
end
|
||||
end
|
||||
|
||||
// The input selection MUX
|
||||
wire [OUT_DATA_W-1:0] in_data[0:RATIO-1];
|
||||
wire [OUT_USER_W-1:0] in_user[0:RATIO-1];
|
||||
for (i = 0; i < RATIO; i=i+1) begin
|
||||
assign in_data[i] = s_axis_tdata[i*OUT_DATA_W+:OUT_DATA_W];
|
||||
assign in_user[i] = s_axis_tuser[i*OUT_USER_W+:OUT_USER_W];
|
||||
end
|
||||
assign m_axis_tdata = in_data[select];
|
||||
assign m_axis_tuser = in_user[select];
|
||||
assign m_axis_tlast = s_axis_tlast && (keep == s_axis_tkeep);
|
||||
assign m_axis_tvalid = s_axis_tvalid;
|
||||
assign s_axis_tready = m_axis_tvalid && m_axis_tready && ((keep == KEEP_ALL) || m_axis_tlast);
|
||||
|
||||
end else begin // if (RATIO != 1)
|
||||
|
||||
// Passthrough
|
||||
assign m_axis_tdata = s_axis_tdata;
|
||||
assign m_axis_tuser = s_axis_tuser;
|
||||
assign m_axis_tlast = s_axis_tlast;
|
||||
assign m_axis_tvalid = s_axis_tvalid;
|
||||
assign s_axis_tready = m_axis_tready;
|
||||
|
||||
end endgenerate
|
||||
|
||||
endmodule // axis_downsizer
|
||||
@@ -0,0 +1,148 @@
|
||||
//
|
||||
// Copyright 2018-2019 Ettus Research, A National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: axis_packet_flush
|
||||
// Description:
|
||||
// When this module is inserted in an AXI-Stream link, it allows
|
||||
// the client to flip a bit to make the stream lossy. When enable=1
|
||||
// all data coming through the input is dropped. This module can
|
||||
// start and stop flushing at packet boundaries to ensure no partial
|
||||
// packets are introduces into the stream. Set FLUSH_PARTIAL_PKTS = 1
|
||||
// to disable that behavior. An optional timeout can be set to
|
||||
// determine if flushing was done (without turning it off).
|
||||
//
|
||||
// Parameters:
|
||||
// - WIDTH: The bitwidth of the AXI-Stream bus
|
||||
// - TIMEOUT_W: Width of the timeout counter
|
||||
// - FLUSH_PARTIAL_PKTS: Start flusing immediately even if a packet is in flight
|
||||
// - PIPELINE: Which ports to pipeline? {NONE, IN, OUT, INOUT}
|
||||
//
|
||||
// Signals:
|
||||
// - s_axis_* : Input AXI-Stream
|
||||
// - m_axis_* : Output AXI-Stream
|
||||
// - enable : Enable flush mode
|
||||
// - timeout : Flush timeout (# of cycles of inactivity until done)
|
||||
// - flushing : The module is currently flushing
|
||||
// - done : Finished flushing (but is still active)
|
||||
|
||||
module axis_packet_flush #(
|
||||
parameter WIDTH = 64,
|
||||
parameter TIMEOUT_W = 32,
|
||||
parameter FLUSH_PARTIAL_PKTS = 0,
|
||||
parameter PIPELINE = "NONE"
|
||||
)(
|
||||
// Clock and reset
|
||||
input wire clk,
|
||||
input wire reset,
|
||||
// Control and status
|
||||
input wire enable,
|
||||
input wire [TIMEOUT_W-1:0] timeout,
|
||||
output wire flushing,
|
||||
output reg done = 1'b0,
|
||||
// Input stream
|
||||
input wire [WIDTH-1:0] s_axis_tdata,
|
||||
input wire s_axis_tlast,
|
||||
input wire s_axis_tvalid,
|
||||
output wire s_axis_tready,
|
||||
// Output stream
|
||||
output wire [WIDTH-1:0] m_axis_tdata,
|
||||
output wire m_axis_tlast,
|
||||
output wire m_axis_tvalid,
|
||||
input wire m_axis_tready
|
||||
);
|
||||
|
||||
//----------------------------------------------
|
||||
// Pipeline Logic
|
||||
//----------------------------------------------
|
||||
|
||||
wire [WIDTH-1:0] i_pipe_tdata, o_pipe_tdata;
|
||||
wire i_pipe_tlast, o_pipe_tlast;
|
||||
wire i_pipe_tvalid, o_pipe_tvalid;
|
||||
wire i_pipe_tready, o_pipe_tready;
|
||||
|
||||
generate
|
||||
if (PIPELINE == "IN" || PIPELINE == "INOUT") begin
|
||||
axi_fifo_flop2 #(.WIDTH(WIDTH+1)) in_pipe_i (
|
||||
.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({s_axis_tlast, s_axis_tdata}), .i_tvalid(s_axis_tvalid), .i_tready(s_axis_tready),
|
||||
.o_tdata({i_pipe_tlast, i_pipe_tdata}), .o_tvalid(i_pipe_tvalid), .o_tready(i_pipe_tready),
|
||||
.space(), .occupied()
|
||||
);
|
||||
end else begin
|
||||
assign {i_pipe_tlast, i_pipe_tdata, i_pipe_tvalid} = {s_axis_tlast, s_axis_tdata, s_axis_tvalid};
|
||||
assign s_axis_tready = i_pipe_tready;
|
||||
end
|
||||
|
||||
if (PIPELINE == "OUT" || PIPELINE == "INOUT") begin
|
||||
axi_fifo_flop2 #(.WIDTH(WIDTH+1)) out_pipe_i (
|
||||
.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({o_pipe_tlast, o_pipe_tdata}), .i_tvalid(o_pipe_tvalid), .i_tready(o_pipe_tready),
|
||||
.o_tdata({m_axis_tlast, m_axis_tdata}), .o_tvalid(m_axis_tvalid), .o_tready(m_axis_tready),
|
||||
.space(), .occupied()
|
||||
);
|
||||
end else begin
|
||||
assign {m_axis_tlast, m_axis_tdata, m_axis_tvalid} = {o_pipe_tlast, o_pipe_tdata, o_pipe_tvalid};
|
||||
assign o_pipe_tready = m_axis_tready;
|
||||
end
|
||||
endgenerate
|
||||
|
||||
//----------------------------------------------
|
||||
// Flushing Logic
|
||||
//----------------------------------------------
|
||||
|
||||
// Shortcuts
|
||||
wire xfer_stb = i_pipe_tvalid & i_pipe_tready;
|
||||
wire pkt_stb = xfer_stb & i_pipe_tlast;
|
||||
|
||||
// Packet boundary detector
|
||||
reg mid_pkt = 1'b0;
|
||||
always @(posedge clk) begin
|
||||
if (reset) begin
|
||||
mid_pkt <= 1'b0;
|
||||
end else if (xfer_stb) begin
|
||||
mid_pkt <= ~pkt_stb;
|
||||
end
|
||||
end
|
||||
|
||||
// Flush startup state machine
|
||||
reg active = 1'b0;
|
||||
always @(posedge clk) begin
|
||||
if (reset) begin
|
||||
active <= 1'b0;
|
||||
end else begin
|
||||
if (enable & (pkt_stb | (~mid_pkt & ~xfer_stb))) begin
|
||||
active <= 1'b1;
|
||||
end else if (~enable) begin
|
||||
active <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
assign flushing = (FLUSH_PARTIAL_PKTS == 0) ? active : enable;
|
||||
|
||||
// Flush done detector based on timeout
|
||||
reg [TIMEOUT_W-1:0] cyc_to_go = {TIMEOUT_W{1'b1}};
|
||||
wire done_tmp = (cyc_to_go == {TIMEOUT_W{1'b0}});
|
||||
always @(posedge clk) begin
|
||||
if (reset | ~enable) begin
|
||||
cyc_to_go <= {TIMEOUT_W{1'b1}};
|
||||
done <= 1'b0;
|
||||
end else if (enable & ~active) begin
|
||||
cyc_to_go <= timeout;
|
||||
end else begin
|
||||
if (~done_tmp) begin
|
||||
cyc_to_go <= xfer_stb ? timeout : (cyc_to_go - 1'b1);
|
||||
end
|
||||
done <= done_tmp;
|
||||
end
|
||||
end
|
||||
|
||||
// When flushing, drop all input data and quiet output data
|
||||
// When no flushing, pass data without interruption
|
||||
assign o_pipe_tdata = i_pipe_tdata;
|
||||
assign o_pipe_tlast = i_pipe_tlast;
|
||||
assign o_pipe_tvalid = flushing ? 1'b0 : i_pipe_tvalid;
|
||||
assign i_pipe_tready = flushing ? 1'b1 : o_pipe_tready;
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,209 @@
|
||||
//
|
||||
// Copyright 2018 Ettus Research, A National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: axis_shift_register
|
||||
// Description:
|
||||
// This module implements a chain of flip-flops in connected
|
||||
// using AXI-Stream. It can be used in the following ways:
|
||||
// * As a AXI-Stream shift register. The tready path is
|
||||
// combinatorial from the output to the input so backpressure
|
||||
// is immediate. The same behavior makes this module non-ideal
|
||||
// to actually break timing critical paths.
|
||||
// * An AXI-Stream wrapper module for a multi-cycle operation
|
||||
// with clock-enables. This can most commonly be used with DSP
|
||||
// operations like filters. Enable the sideband datapath to
|
||||
// let the module handle handshaking while processing samples
|
||||
// outside it.
|
||||
//
|
||||
// Parameters:
|
||||
// - WIDTH: The bitwidth of a sample on the data bus.
|
||||
// - NSPC: The number of parallel samples per cycle to process. The
|
||||
// total width of the data bus will be WIDTH*NSPC.
|
||||
// - LATENCY: Number of stages in the shift register
|
||||
// - SIDEBAND_DATAPATH: If SIDEBAND_DATAPATH==1 then tdata is managed
|
||||
// outside this module and imported from s_sideband_data.
|
||||
// If SIDEBAND_DATAPATH=0, then tdata is managed internally and
|
||||
// the sideband signals are unused.
|
||||
// Useful when using this module to manage a DSP pipeline where the
|
||||
// data could be changing in each stage.
|
||||
// - GAPLESS: After the shift register has filled up, should gaps be
|
||||
// allowed? If set to 1, then if s_axis_tvalid goes low then the
|
||||
// pipeline will stall and all bits in stage_stb will immediately go low
|
||||
// to ensure all stages in the shift register have valid data.
|
||||
// NOTE: This GAPLESS=1 will not allow the final "LATENCY" samples
|
||||
// to exit the shift register.
|
||||
// - PIPELINE: Which ports to pipeline? {NONE, IN, OUT, INOUT}
|
||||
//
|
||||
// Signals:
|
||||
// - s_axis_* : Input sample stream (AXI-Stream)
|
||||
// - m_axis_* : Output sample stream (AXI-Stream)
|
||||
// - stage_stb : Transfer strobe for each stage
|
||||
// - stage_eop : Transfer end-of-packet out. bit[i] = stage[i]
|
||||
// - m_sideband_data : Sideband data out for external consumer
|
||||
// - m_sideband_keep : Sideband keep signal out for external consumer
|
||||
// - s_sideband_data : Sideband data in from external producer
|
||||
|
||||
module axis_shift_register #(
|
||||
parameter WIDTH = 32,
|
||||
parameter NSPC = 1,
|
||||
parameter LATENCY = 3,
|
||||
parameter SIDEBAND_DATAPATH = 0,
|
||||
parameter GAPLESS = 0,
|
||||
parameter PIPELINE = "NONE"
|
||||
)(
|
||||
// Clock, reset and settings
|
||||
input wire clk, // Clock
|
||||
input wire reset, // Reset
|
||||
// Serial Data In (AXI-Stream)
|
||||
input wire [(WIDTH*NSPC)-1:0] s_axis_tdata, // Input stream tdata
|
||||
input wire [NSPC-1:0] s_axis_tkeep, // Input stream tkeep (used as a sample qualifier)
|
||||
input wire s_axis_tlast, // Input stream tlast
|
||||
input wire s_axis_tvalid, // Input stream tvalid
|
||||
output wire s_axis_tready, // Input stream tready
|
||||
// Serial Data Out (AXI-Stream)
|
||||
output wire [(WIDTH*NSPC)-1:0] m_axis_tdata, // Output stream tdata
|
||||
output wire [NSPC-1:0] m_axis_tkeep, // Output stream tkeep (used as a sample qualifier)
|
||||
output wire m_axis_tlast, // Output stream tlast
|
||||
output wire m_axis_tvalid, // Output stream tvalid
|
||||
input wire m_axis_tready, // Output stream tready
|
||||
// Signals for the sideband data path
|
||||
output wire [LATENCY-1:0] stage_stb, // Transfer strobe out. bit[i] = stage[i]
|
||||
output wire [LATENCY-1:0] stage_eop, // Transfer end-of-packet out. bit[i] = stage[i]
|
||||
output wire [(WIDTH*NSPC)-1:0] m_sideband_data, // Sideband data out for external consumer
|
||||
output wire [NSPC-1:0] m_sideband_keep, // Sideband keep signal out for external consumer
|
||||
input wire [(WIDTH*NSPC)-1:0] s_sideband_data // Sideband data in from external producer
|
||||
);
|
||||
// Shift register width depends on whether the datapath is internal
|
||||
localparam SHREG_WIDTH = SIDEBAND_DATAPATH[0] ? (NSPC + 1) : ((WIDTH*NSPC) + NSPC + 1);
|
||||
localparam SHREG_TLAST_LOC = SHREG_WIDTH-1;
|
||||
localparam SHREG_TKEEP_HI = SHREG_WIDTH-2;
|
||||
localparam SHREG_TKEEP_LO = SHREG_WIDTH-NSPC-1;
|
||||
|
||||
//----------------------------------------------
|
||||
// Pipeline Logic
|
||||
// (fifo_flop2 is used because it breaks timing
|
||||
// path going both ways: valid and ready)
|
||||
//----------------------------------------------
|
||||
wire [(WIDTH*NSPC)-1:0] i_tdata, o_tdata;
|
||||
wire [NSPC-1:0] i_tkeep, o_tkeep;
|
||||
wire i_tlast, o_tlast;
|
||||
wire i_tvalid, o_tvalid;
|
||||
wire i_tready, o_tready;
|
||||
|
||||
generate
|
||||
// Input pipeline register if requested
|
||||
if (PIPELINE == "IN" || PIPELINE == "INOUT") begin
|
||||
axi_fifo_flop2 #(.WIDTH((WIDTH*NSPC) + NSPC + 1)) in_pipe_i (
|
||||
.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({s_axis_tlast, s_axis_tkeep, s_axis_tdata}),
|
||||
.i_tvalid(s_axis_tvalid), .i_tready(s_axis_tready),
|
||||
.o_tdata({i_tlast, i_tkeep, i_tdata}), .o_tvalid(i_tvalid), .o_tready(i_tready),
|
||||
.space(), .occupied()
|
||||
);
|
||||
end else begin
|
||||
assign {i_tlast, i_tkeep, i_tdata} = {s_axis_tlast, s_axis_tkeep, s_axis_tdata};
|
||||
assign i_tvalid = s_axis_tvalid;
|
||||
assign s_axis_tready = i_tready;
|
||||
end
|
||||
|
||||
// Output pipeline register if requested
|
||||
if (PIPELINE == "OUT" || PIPELINE == "INOUT") begin
|
||||
axi_fifo_flop2 #(.WIDTH((WIDTH*NSPC) + NSPC + 1)) out_pipe_i (
|
||||
.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({o_tlast, o_tkeep, o_tdata}), .i_tvalid(o_tvalid), .i_tready(o_tready),
|
||||
.o_tdata({m_axis_tlast, m_axis_tkeep, m_axis_tdata}),
|
||||
.o_tvalid(m_axis_tvalid), .o_tready(m_axis_tready),
|
||||
.space(), .occupied()
|
||||
);
|
||||
end else begin
|
||||
assign {m_axis_tlast, m_axis_tkeep, m_axis_tdata} = {o_tlast, o_tkeep, o_tdata};
|
||||
assign m_axis_tvalid = o_tvalid;
|
||||
assign o_tready = m_axis_tready;
|
||||
end
|
||||
endgenerate
|
||||
|
||||
assign m_sideband_data = i_tdata;
|
||||
assign m_sideband_keep = i_tkeep;
|
||||
|
||||
//----------------------------------------------
|
||||
// Shift register stages
|
||||
//----------------------------------------------
|
||||
genvar i;
|
||||
generate
|
||||
if (GAPLESS == 0) begin
|
||||
// Individual stage wires
|
||||
wire [SHREG_WIDTH-1:0] stg_tdata [0:LATENCY];
|
||||
wire stg_tvalid[0:LATENCY];
|
||||
wire stg_tready[0:LATENCY];
|
||||
// Shift register input
|
||||
assign stg_tdata[0] = SIDEBAND_DATAPATH[0] ? {i_tlast, i_tkeep} : {i_tlast, i_tkeep, i_tdata};
|
||||
assign stg_tvalid[0] = i_tvalid;
|
||||
assign i_tready = stg_tready[0];
|
||||
// Shift register output
|
||||
assign o_tlast = stg_tdata[LATENCY][SHREG_TLAST_LOC];
|
||||
assign o_tkeep = stg_tdata[LATENCY][SHREG_TKEEP_HI:SHREG_TKEEP_LO];
|
||||
assign o_tdata = SIDEBAND_DATAPATH[0] ? s_sideband_data : stg_tdata[LATENCY][(WIDTH*NSPC)-1:0];
|
||||
assign o_tvalid = stg_tvalid[LATENCY];
|
||||
assign stg_tready[LATENCY] = o_tready;
|
||||
|
||||
for (i = 0; i < LATENCY; i=i+1) begin: stages
|
||||
axi_fifo_flop #(.WIDTH(SHREG_WIDTH)) reg_i (
|
||||
.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata(stg_tdata[i ]), .i_tvalid(stg_tvalid[i ]), .i_tready(stg_tready[i ]),
|
||||
.o_tdata(stg_tdata[i+1]), .o_tvalid(stg_tvalid[i+1]), .o_tready(stg_tready[i+1]),
|
||||
.occupied(), .space()
|
||||
);
|
||||
assign stage_stb[i] = stg_tvalid[i] & stg_tready[i];
|
||||
assign stage_eop[i] = stage_stb[i] & stg_tdata[i][SHREG_TLAST_LOC];
|
||||
end
|
||||
end else begin // if (GAPLESS == 0)
|
||||
wire [(WIDTH*NSPC)-1:0] o_tdata_fifo;
|
||||
wire [NSPC-1:0] o_tkeep_fifo;
|
||||
wire o_tlast_fifo, o_tvalid_fifo, o_tready_fifo;
|
||||
|
||||
// Shift register to hold valids
|
||||
reg [LATENCY-1:0] stage_valid = {LATENCY{1'b0}};
|
||||
// Shift register to hold data/last
|
||||
reg [SHREG_WIDTH-1:0] stage_shreg[0:LATENCY-1];
|
||||
wire [SHREG_WIDTH-1:0] shreg_input = SIDEBAND_DATAPATH[0] ? {i_tlast, i_tkeep} : {i_tlast, i_tkeep, i_tdata};
|
||||
wire shreg_ce = i_tready & i_tvalid;
|
||||
|
||||
assign i_tready = o_tready_fifo;
|
||||
assign o_tvalid_fifo = stage_valid[LATENCY-1] & shreg_ce;
|
||||
assign o_tlast_fifo = stage_shreg[LATENCY-1][SHREG_TLAST_LOC];
|
||||
assign o_tkeep_fifo = stage_shreg[LATENCY-1][SHREG_TKEEP_HI:SHREG_TKEEP_LO];
|
||||
assign o_tdata_fifo = SIDEBAND_DATAPATH[0] ? s_sideband_data : stage_shreg[LATENCY-1][(WIDTH*NSPC)-1:0];
|
||||
|
||||
for (i = 0; i < LATENCY; i=i+1) begin
|
||||
// Initialize shift register
|
||||
initial begin
|
||||
stage_shreg[i] <= {SHREG_WIDTH{1'b0}};
|
||||
end
|
||||
// Shift register logic
|
||||
always @(posedge clk) begin
|
||||
if (reset) begin
|
||||
stage_shreg[i] <= {SHREG_WIDTH{1'b0}};
|
||||
stage_valid[i] <= 1'b0;
|
||||
end else if (shreg_ce) begin
|
||||
stage_shreg[i] <= (i == 0) ? shreg_input : stage_shreg[i-1];
|
||||
stage_valid[i] <= (i == 0) ? 1'b1 : stage_valid[i-1];
|
||||
end
|
||||
end
|
||||
// Outputs
|
||||
assign stage_stb[i] = ((i == 0) ? 1'b1 : stage_valid[i-1]) & shreg_ce;
|
||||
assign stage_eop[i] = stage_stb[i] & ((i == 0) ? i_tlast : stage_shreg[i-1][SHREG_TLAST_LOC]);
|
||||
end
|
||||
|
||||
// The "gapless" logic violates AXI-Stream by having an o_tready -> o_tvalid dependency,
|
||||
// so we add a FIFO downstream to prevent deadlocks.
|
||||
axi_fifo #(.WIDTH((WIDTH*NSPC) + NSPC + 1), .SIZE($clog2(LATENCY))) out_fifo_i (
|
||||
.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({o_tlast_fifo, o_tkeep_fifo, o_tdata_fifo}), .i_tvalid(o_tvalid_fifo), .i_tready(o_tready_fifo),
|
||||
.o_tdata({o_tlast, o_tkeep, o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready),
|
||||
.space(), .occupied()
|
||||
);
|
||||
end
|
||||
endgenerate
|
||||
endmodule // axis_shift_register
|
||||
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// Copyright 2018 Ettus Research, A National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: axis_upsizer
|
||||
// Description:
|
||||
// An AXI-Stream width conversion module that widens the input
|
||||
// sample with by a factor of RATIO.
|
||||
// NOTE: This module has end-to-end combanitorial paths. For a
|
||||
// pipelined version, please use axis_width_conv
|
||||
//
|
||||
// Parameters:
|
||||
// - IN_DATA_W: The bitwidth of the input data bus. The width of the
|
||||
// output data bus is IN_DATA_W*RATIO
|
||||
// - IN_USER_W: The bitwidth of the input user bus. The width of the
|
||||
// output user bus is IN_USER_W*RATIO
|
||||
// - RATIO: The upsizing ratio
|
||||
//
|
||||
// Signals:
|
||||
// - s_axis_* : Input sample stream (AXI-Stream)
|
||||
// - m_axis_* : Output sample stream (AXI-Stream)
|
||||
|
||||
module axis_upsizer #(
|
||||
parameter IN_DATA_W = 32,
|
||||
parameter IN_USER_W = 1,
|
||||
parameter RATIO = 4
|
||||
)(
|
||||
// Clock, reset and settings
|
||||
input wire clk, // Clock
|
||||
input wire reset, // Reset
|
||||
// Data In (AXI-Stream)
|
||||
input wire [IN_DATA_W-1:0] s_axis_tdata, // Input stream tdata
|
||||
input wire [IN_USER_W-1:0] s_axis_tuser, // Input stream tuser
|
||||
input wire s_axis_tlast, // Input stream tlast
|
||||
input wire s_axis_tvalid, // Input stream tvalid
|
||||
output wire s_axis_tready, // Input stream tready
|
||||
// Data Out (AXI-Stream)
|
||||
output wire [(IN_DATA_W*RATIO)-1:0] m_axis_tdata, // Output stream tdata
|
||||
output wire [(IN_USER_W*RATIO)-1:0] m_axis_tuser, // Output stream tuser
|
||||
output wire [RATIO-1:0] m_axis_tkeep, // Output stream tkeep
|
||||
output wire m_axis_tlast, // Output stream tlast
|
||||
output wire m_axis_tvalid, // Output stream tvalid
|
||||
input wire m_axis_tready // Output stream tready
|
||||
);
|
||||
|
||||
genvar i;
|
||||
generate if (RATIO != 1) begin
|
||||
// Constants
|
||||
localparam [$clog2(RATIO)-1:0] SEL_FIRST = 'd0;
|
||||
localparam [$clog2(RATIO)-1:0] SEL_LAST = RATIO-1;
|
||||
localparam [RATIO-1:0] KEEP_FIRST = {{(RATIO-1){1'b0}}, 1'b1};
|
||||
localparam [RATIO-1:0] KEEP_ALL = {(RATIO){1'b1}};
|
||||
|
||||
// Keep a binary-coded and one-hot version of the current
|
||||
// section of the output that is being processed.
|
||||
reg [$clog2(RATIO)-1:0] select = SEL_FIRST;
|
||||
reg [RATIO-1:0] keep = KEEP_FIRST;
|
||||
// Cached data. Incomplete output word.
|
||||
reg [IN_DATA_W-1:0] cached_data[0:RATIO-2];
|
||||
reg [IN_USER_W-1:0] cached_user[0:RATIO-2];
|
||||
|
||||
// State machine to drive the select bits for the
|
||||
// output DEMUX.
|
||||
always @(posedge clk) begin
|
||||
if (reset) begin
|
||||
select <= SEL_FIRST;
|
||||
keep <= KEEP_FIRST;
|
||||
end else if (s_axis_tvalid & s_axis_tready) begin
|
||||
select <= (select == SEL_LAST || s_axis_tlast) ? SEL_FIRST : (select + 'd1);
|
||||
keep <= (keep == KEEP_ALL || s_axis_tlast) ? KEEP_FIRST : {keep[RATIO-2:0], 1'b1};
|
||||
cached_data[select] <= s_axis_tdata;
|
||||
cached_user[select] <= s_axis_tuser;
|
||||
end
|
||||
end
|
||||
|
||||
// The output DEMUX
|
||||
for (i = 0; i < RATIO; i=i+1) begin
|
||||
if (i == SEL_LAST) begin
|
||||
assign m_axis_tdata[(i*IN_DATA_W)+:IN_DATA_W] = s_axis_tdata;
|
||||
assign m_axis_tuser[(i*IN_USER_W)+:IN_USER_W] = s_axis_tuser;
|
||||
end else begin
|
||||
assign m_axis_tdata[(i*IN_DATA_W)+:IN_DATA_W] = keep[i+1] ? cached_data[i] : s_axis_tdata;
|
||||
assign m_axis_tuser[(i*IN_USER_W)+:IN_USER_W] = keep[i+1] ? cached_user[i] : s_axis_tuser;
|
||||
end
|
||||
end
|
||||
assign m_axis_tkeep = keep;
|
||||
assign m_axis_tlast = s_axis_tlast;
|
||||
assign m_axis_tvalid = s_axis_tvalid & ((keep == KEEP_ALL) | s_axis_tlast);
|
||||
assign s_axis_tready = m_axis_tvalid ? m_axis_tready : s_axis_tvalid;
|
||||
|
||||
end else begin // if (RATIO != 1)
|
||||
|
||||
// Passthrough
|
||||
assign m_axis_tdata = s_axis_tdata;
|
||||
assign m_axis_tuser = s_axis_tuser;
|
||||
assign m_axis_tkeep = 1'b1;
|
||||
assign m_axis_tlast = s_axis_tlast;
|
||||
assign m_axis_tvalid = s_axis_tvalid;
|
||||
assign s_axis_tready = m_axis_tready;
|
||||
|
||||
end endgenerate
|
||||
|
||||
endmodule // axis_upsizer
|
||||
@@ -0,0 +1,232 @@
|
||||
//
|
||||
// Copyright 2018 Ettus Research, A National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: axis_width_conv
|
||||
// Description:
|
||||
// An AXI-Stream width conversion module that can convert from
|
||||
// an arbitrary input width to an arbitrary output width. The
|
||||
// module also supports an optional clock crossing. Data bits
|
||||
// are grouped into words which will be rearranged by this module.
|
||||
// The contents of a word are not rearranged.
|
||||
// Example (WORD_W=4, IN_WORDS=4, OUT_WORDS=6):
|
||||
// Input : 3_1_2_0, x_6_5_4 (comma-delimited packets)
|
||||
// Output : 5_4_3_2_1_0, x_x_x_x_x_6 (comma-delimited packets)
|
||||
// NOTE: The use of tkeep in this module is a slight deviation from
|
||||
// the AXI standard where the bits are "byte qualifiers". In
|
||||
// this module, tkeep is a "word qualifier" where the width
|
||||
// of a word can be arbitrary. If WORD_W = 8, the behavior
|
||||
// of this module is identical to an AXI width converter.
|
||||
//
|
||||
// Parameters:
|
||||
// - WORD_W: Bitwidth of a word
|
||||
// - IN_WORDS: Number of words in the input stream
|
||||
// - OUT_WORDS: Number of words in the output stream
|
||||
// - SYNC_CLKS: Are s_axis_aclk and m_axis_aclk synchronous to each other?
|
||||
// - PIPELINE: Which ports to pipeline? {NONE, IN, OUT, INOUT}
|
||||
//
|
||||
// Signals:
|
||||
// - s_axis_* : Input sample stream (AXI-Stream)
|
||||
// - m_axis_* : Output sample stream (AXI-Stream)
|
||||
|
||||
module axis_width_conv #(
|
||||
parameter WORD_W = 8,
|
||||
parameter IN_WORDS = 4,
|
||||
parameter OUT_WORDS = 6,
|
||||
parameter SYNC_CLKS = 0,
|
||||
parameter PIPELINE = "NONE"
|
||||
)(
|
||||
// Data In (AXI-Stream)
|
||||
input wire s_axis_aclk, // Input stream Clock
|
||||
input wire s_axis_rst, // Input stream Reset
|
||||
input wire [(IN_WORDS*WORD_W)-1:0] s_axis_tdata, // Input stream tdata
|
||||
input wire [IN_WORDS-1:0] s_axis_tkeep, // Input stream tkeep
|
||||
input wire s_axis_tlast, // Input stream tlast
|
||||
input wire s_axis_tvalid, // Input stream tvalid
|
||||
output wire s_axis_tready, // Input stream tready
|
||||
// Data Out (AXI-Stream)
|
||||
input wire m_axis_aclk, // Output stream Clock
|
||||
input wire m_axis_rst, // Output stream Reset
|
||||
output wire [(OUT_WORDS*WORD_W)-1:0] m_axis_tdata, // Output stream tdata
|
||||
output wire [OUT_WORDS-1:0] m_axis_tkeep, // Output stream tkeep
|
||||
output wire m_axis_tlast, // Output stream tlast
|
||||
output wire m_axis_tvalid, // Output stream tvalid
|
||||
input wire m_axis_tready // Output stream tready
|
||||
);
|
||||
|
||||
//----------------------------------------------
|
||||
// Pipeline Logic
|
||||
//----------------------------------------------
|
||||
// Add optional input and output pipeline stages
|
||||
|
||||
wire [(IN_WORDS*WORD_W)-1:0] i_tdata;
|
||||
wire [IN_WORDS-1:0] i_tkeep;
|
||||
wire i_tlast, i_tvalid, i_tready;
|
||||
wire [(OUT_WORDS*WORD_W)-1:0] o_tdata;
|
||||
wire [OUT_WORDS-1:0] o_tkeep;
|
||||
wire o_tlast, o_tvalid, o_tready;
|
||||
|
||||
generate
|
||||
if (PIPELINE == "IN" || PIPELINE == "INOUT") begin
|
||||
axi_fifo_flop2 #(.WIDTH((IN_WORDS*(WORD_W+1))+1)) in_pipe_i (
|
||||
.clk(s_axis_aclk), .reset(s_axis_rst), .clear(1'b0),
|
||||
.i_tdata({s_axis_tlast, s_axis_tkeep, s_axis_tdata}),
|
||||
.i_tvalid(s_axis_tvalid), .i_tready(s_axis_tready),
|
||||
.o_tdata({i_tlast, i_tkeep, i_tdata}),
|
||||
.o_tvalid(i_tvalid), .o_tready(i_tready),
|
||||
.space(), .occupied()
|
||||
);
|
||||
end else begin
|
||||
assign {i_tlast, i_tkeep, i_tdata} = {s_axis_tlast, s_axis_tkeep, s_axis_tdata};
|
||||
assign i_tvalid = s_axis_tvalid;
|
||||
assign s_axis_tready = i_tready;
|
||||
end
|
||||
|
||||
if (PIPELINE == "OUT" || PIPELINE == "INOUT") begin
|
||||
axi_fifo_flop2 #(.WIDTH((OUT_WORDS*(WORD_W+1))+1)) out_pipe_i (
|
||||
.clk(m_axis_aclk), .reset(m_axis_rst), .clear(1'b0),
|
||||
.i_tdata({o_tlast, o_tkeep, o_tdata}),
|
||||
.i_tvalid(o_tvalid), .i_tready(o_tready),
|
||||
.o_tdata({m_axis_tlast, m_axis_tkeep, m_axis_tdata}),
|
||||
.o_tvalid(m_axis_tvalid), .o_tready(m_axis_tready),
|
||||
.space(), .occupied()
|
||||
);
|
||||
end else begin
|
||||
assign {m_axis_tlast, m_axis_tkeep, m_axis_tdata} = {o_tlast, o_tkeep, o_tdata};
|
||||
assign m_axis_tvalid = o_tvalid;
|
||||
assign o_tready = m_axis_tready;
|
||||
end
|
||||
endgenerate
|
||||
|
||||
//----------------------------------------------
|
||||
// Intermediate Data Bus
|
||||
//----------------------------------------------
|
||||
// To perform an M to N width conversion, we first
|
||||
// convert from M to LCM(M, N), then to N
|
||||
|
||||
// Function to compute the least common multiple
|
||||
// of two numbers (parameters or localparams only)
|
||||
function integer lcm;
|
||||
input integer a;
|
||||
input integer b;
|
||||
integer x, y, swap;
|
||||
reg done;
|
||||
begin
|
||||
done = 1'b0;
|
||||
x = a;
|
||||
y = b;
|
||||
while (!done) begin
|
||||
if (x < y) begin
|
||||
swap = x;
|
||||
x = y;
|
||||
y = swap;
|
||||
end else if (y != 0) begin
|
||||
x = x - y;
|
||||
end else begin
|
||||
done = 1'b1;
|
||||
end
|
||||
end
|
||||
// x is the greatest common divisor
|
||||
// LCM = (a*b)/GCD
|
||||
lcm = (a*b)/x;
|
||||
end
|
||||
endfunction
|
||||
|
||||
// Intermediate bus parameters
|
||||
localparam integer INT_KEEP_W = lcm(IN_WORDS, OUT_WORDS);
|
||||
localparam integer INT_DATA_W = INT_KEEP_W * WORD_W;
|
||||
localparam integer UPSIZE_RATIO = INT_KEEP_W / IN_WORDS;
|
||||
localparam integer DOWNSIZE_RATIO = INT_KEEP_W / OUT_WORDS;
|
||||
|
||||
wire [INT_DATA_W-1:0] fifo_i_tdata, fifo_o_tdata;
|
||||
wire [INT_KEEP_W-1:0] fifo_i_tkeep, fifo_o_tkeep;
|
||||
wire fifo_i_tlast, fifo_i_tvalid, fifo_i_tready;
|
||||
wire fifo_o_tlast, fifo_o_tvalid, fifo_o_tready;
|
||||
|
||||
// Skip the intermediate FIFO if
|
||||
// - The input and output clocks are the same
|
||||
// - The upsizer is effectively a passthrough and input registering is requested
|
||||
// - The downsizer is effectively a passthrough and output registering is requested
|
||||
localparam [0:0] SKIP_FIFO = (SYNC_CLKS == 1) && (
|
||||
((PIPELINE == "IN" || PIPELINE == "INOUT") && (UPSIZE_RATIO == 1)) ||
|
||||
((PIPELINE == "OUT" || PIPELINE == "INOUT") && (DOWNSIZE_RATIO == 1))
|
||||
);
|
||||
localparam FIFO_SIZE = 1;
|
||||
|
||||
//----------------------------------------------
|
||||
// In => Upsizer => FIFO => Downsizer => Out
|
||||
//----------------------------------------------
|
||||
|
||||
wire [INT_KEEP_W-1:0] up_keep_flat;
|
||||
wire [UPSIZE_RATIO-1:0] up_keep_keep;
|
||||
wire [DOWNSIZE_RATIO-1:0] down_keep_keep;
|
||||
|
||||
axis_upsizer #(
|
||||
.IN_DATA_W(IN_WORDS*WORD_W), .IN_USER_W(IN_WORDS),
|
||||
.RATIO(UPSIZE_RATIO)
|
||||
) upsizer_i (
|
||||
.clk(s_axis_aclk), .reset(s_axis_rst),
|
||||
.s_axis_tdata(i_tdata), .s_axis_tuser(i_tkeep),
|
||||
.s_axis_tlast(i_tlast), .s_axis_tvalid(i_tvalid), .s_axis_tready(i_tready),
|
||||
.m_axis_tdata(fifo_i_tdata), .m_axis_tuser(up_keep_flat), .m_axis_tkeep(up_keep_keep),
|
||||
.m_axis_tlast(fifo_i_tlast), .m_axis_tvalid(fifo_i_tvalid), .m_axis_tready(fifo_i_tready)
|
||||
);
|
||||
|
||||
// tkeep unmasking logic after upsizer
|
||||
genvar i;
|
||||
generate for (i = 0; i < INT_KEEP_W; i = i + 1) begin
|
||||
// tkeep is assumed to be valid only when tlast is asserted
|
||||
// otherwise it is 1
|
||||
assign fifo_i_tkeep[i] = ~fifo_i_tlast |
|
||||
(up_keep_keep[i/IN_WORDS] ? up_keep_flat[i] : 1'b0);
|
||||
end endgenerate
|
||||
|
||||
generate
|
||||
if (SKIP_FIFO) begin
|
||||
assign fifo_o_tdata = fifo_i_tdata;
|
||||
assign fifo_o_tkeep = fifo_i_tkeep;
|
||||
assign fifo_o_tlast = fifo_i_tlast;
|
||||
assign fifo_o_tvalid = fifo_i_tvalid;
|
||||
assign fifo_i_tready = fifo_o_tready;
|
||||
end else begin
|
||||
if (SYNC_CLKS) begin
|
||||
axi_fifo #(.WIDTH(INT_DATA_W+INT_KEEP_W+1), .SIZE(FIFO_SIZE)) fifo_i (
|
||||
.clk(s_axis_aclk), .reset(s_axis_rst), .clear(1'b0),
|
||||
.i_tdata({fifo_i_tlast, fifo_i_tkeep, fifo_i_tdata}),
|
||||
.i_tvalid(fifo_i_tvalid), .i_tready(fifo_i_tready),
|
||||
.o_tdata({fifo_o_tlast, fifo_o_tkeep, fifo_o_tdata}),
|
||||
.o_tvalid(fifo_o_tvalid), .o_tready(fifo_o_tready),
|
||||
.space(), .occupied()
|
||||
);
|
||||
end else begin
|
||||
axi_fifo_2clk #(.WIDTH(INT_DATA_W+INT_KEEP_W+1), .SIZE(FIFO_SIZE)) fifo_i (
|
||||
.reset(s_axis_rst),
|
||||
.i_aclk(s_axis_aclk),
|
||||
.i_tdata({fifo_i_tlast, fifo_i_tkeep, fifo_i_tdata}),
|
||||
.i_tvalid(fifo_i_tvalid), .i_tready(fifo_i_tready),
|
||||
.o_aclk(m_axis_aclk),
|
||||
.o_tdata({fifo_o_tlast, fifo_o_tkeep, fifo_o_tdata}),
|
||||
.o_tvalid(fifo_o_tvalid), .o_tready(fifo_o_tready)
|
||||
);
|
||||
end
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// tkeep masking logic after downsizer
|
||||
generate for (i = 0; i < DOWNSIZE_RATIO; i = i + 1) begin
|
||||
assign down_keep_keep[i] = |fifo_o_tkeep[i*OUT_WORDS+:OUT_WORDS];
|
||||
end endgenerate
|
||||
|
||||
axis_downsizer #(
|
||||
.OUT_DATA_W(OUT_WORDS*WORD_W), .OUT_USER_W(OUT_WORDS),
|
||||
.RATIO(DOWNSIZE_RATIO)
|
||||
) downsizer_i (
|
||||
.clk(m_axis_aclk), .reset(m_axis_rst),
|
||||
.s_axis_tdata(fifo_o_tdata), .s_axis_tuser(fifo_o_tkeep), .s_axis_tkeep(down_keep_keep),
|
||||
.s_axis_tlast(fifo_o_tlast), .s_axis_tvalid(fifo_o_tvalid), .s_axis_tready(fifo_o_tready),
|
||||
.m_axis_tdata(o_tdata), .m_axis_tuser(o_tkeep),
|
||||
.m_axis_tlast(o_tlast), .m_axis_tvalid(o_tvalid), .m_axis_tready(o_tready)
|
||||
);
|
||||
|
||||
endmodule // axis_width_conv
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// Copyright 2017 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Generates an LFSR based on a given seed value
|
||||
// Note that not all length LFSRs are supported in the file
|
||||
// For xnor LSFR equations please refer to following link:
|
||||
// https://www.xilinx.com/support/documentation/application_notes/xapp210.pdf
|
||||
|
||||
// All indexing will be from 1 to match indexing used in table from app note above
|
||||
|
||||
|
||||
module crc_xnor #(
|
||||
parameter INPUT_WIDTH=64,
|
||||
parameter OUTPUT_WIDTH=8
|
||||
) (
|
||||
input clk,
|
||||
input [INPUT_WIDTH:1] input_data,
|
||||
input rst,
|
||||
input hold,
|
||||
output [OUTPUT_WIDTH:1] crc_out
|
||||
);
|
||||
|
||||
wire [INPUT_WIDTH:1] current_lfsr;
|
||||
reg [INPUT_WIDTH:1] current_lfsr_r;
|
||||
|
||||
// LFSR based on table given by Xilinx
|
||||
generate if (INPUT_WIDTH == 64) begin
|
||||
assign current_lfsr[1] = current_lfsr_r[64] ^ current_lfsr_r[63] ^ current_lfsr_r[61] ^ current_lfsr_r[60];
|
||||
assign current_lfsr[INPUT_WIDTH:2] = current_lfsr_r[INPUT_WIDTH-1:1];
|
||||
end else begin
|
||||
fake_error_thrower invalid_width_parameter();
|
||||
end endgenerate
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
current_lfsr_r <= input_data;
|
||||
end else if(~hold) begin
|
||||
current_lfsr_r <= current_lfsr ^ input_data;
|
||||
end
|
||||
end
|
||||
|
||||
// Sum reduce based on output width
|
||||
generate if(INPUT_WIDTH == 64 && OUTPUT_WIDTH == 16) begin
|
||||
assign crc_out = current_lfsr_r[INPUT_WIDTH:INPUT_WIDTH/4*3+1]+current_lfsr_r[INPUT_WIDTH/4*3:INPUT_WIDTH/4*2+1]+
|
||||
current_lfsr_r[INPUT_WIDTH/4*2:INPUT_WIDTH/4+1]+current_lfsr_r[INPUT_WIDTH/4:1];
|
||||
end else if(INPUT_WIDTH == 64 && OUTPUT_WIDTH == 32) begin
|
||||
assign crc_out = current_lfsr_r[INPUT_WIDTH:INPUT_WIDTH/2+1]+current_lfsr_r[INPUT_WIDTH/2:1];
|
||||
end else begin
|
||||
fake_error_thrower invalid_width_parameter();
|
||||
end endgenerate
|
||||
|
||||
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Copyright 2016 Ettus Research
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
module strobed_to_axi #(
|
||||
parameter WIDTH = 32,
|
||||
parameter FIFO_SIZE = 1
|
||||
)(
|
||||
input clk, input reset, input clear,
|
||||
input in_stb, input [WIDTH-1:0] in_data, input in_last,
|
||||
output [WIDTH-1:0] o_tdata, output o_tlast, output o_tvalid, input o_tready
|
||||
);
|
||||
|
||||
axi_fifo #(.WIDTH(WIDTH+1), .SIZE(FIFO_SIZE)) axi_fifo (
|
||||
.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata({in_last,in_data}), .i_tvalid(in_stb), .i_tready(),
|
||||
.o_tdata({o_tlast,o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready),
|
||||
.space(), .occupied());
|
||||
endmodule
|
||||
@@ -0,0 +1,61 @@
|
||||
#
|
||||
# Copyright 2013 Ettus Research LLC
|
||||
# Copyright 2017 Ettus Research, a National Instruments Company
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
#
|
||||
|
||||
##################################################
|
||||
# Control Lib Sources
|
||||
##################################################
|
||||
CONTROL_LIB_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/control/, \
|
||||
ad5662_auto_spi.v \
|
||||
arb_qualify_master.v \
|
||||
axi_crossbar.v \
|
||||
axi_crossbar_regport.v \
|
||||
axi_fifo_header.v \
|
||||
axi_forwarding_cam.v \
|
||||
axi_setting_reg.v \
|
||||
axi_slave_mux.v \
|
||||
axi_test_vfifo.v \
|
||||
bin2gray.v \
|
||||
binary_encoder.v \
|
||||
db_control.v \
|
||||
fe_control.v \
|
||||
filter_bad_sid.v \
|
||||
gpio_atr_io.v \
|
||||
gpio_atr.v \
|
||||
gray2bin.v \
|
||||
por_gen.v \
|
||||
priority_encoder_one_hot.v \
|
||||
priority_encoder.v \
|
||||
ram_2port_impl.vh \
|
||||
ram_2port.v \
|
||||
reset_sync.v \
|
||||
s7_icap_wb.v \
|
||||
serial_to_settings.v \
|
||||
setting_reg.v \
|
||||
settings_bus_mux.v \
|
||||
settings_bus_timed_2clk.v \
|
||||
simple_i2c_core.v \
|
||||
simple_spi_core.v \
|
||||
synchronizer_impl.v \
|
||||
synchronizer.v \
|
||||
pulse_synchronizer.v \
|
||||
user_settings.v \
|
||||
axil_regport_master.v \
|
||||
axil_to_ni_regport.v \
|
||||
regport_resp_mux.v \
|
||||
regport_to_xbar_settingsbus.v \
|
||||
regport_to_settingsbus.v \
|
||||
pulse_stretch.v \
|
||||
pulse_stretch_min.v \
|
||||
mdio_master.v \
|
||||
map/cam_priority_encoder.v \
|
||||
map/cam_bram.v \
|
||||
map/cam_srl.v \
|
||||
map/cam.v \
|
||||
map/kv_map.v \
|
||||
map/axis_muxed_kv_map.v \
|
||||
axil_ctrlport_master.v\
|
||||
))
|
||||
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// Copyright 2015 Ettus Research
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// The AD5662 DAC serial interface uses 24-bit transfers to encode 16-bits
|
||||
// of actual data, two bits for power-down mode, and six pad bits. This
|
||||
// module stores a copy of the last-programmed value, and will generate a
|
||||
// serial stream if ever the input word (dat) changes. It will ignore
|
||||
// changes to (dat) while it is busy with a serial update.
|
||||
//
|
||||
module ad5662_auto_spi
|
||||
(
|
||||
input clk,
|
||||
input [15:0] dat,
|
||||
output reg sclk,
|
||||
output reg mosi,
|
||||
output reg sync_n
|
||||
);
|
||||
// initialize ldat to 0, thus forcing
|
||||
// a reload on init.
|
||||
reg [15:0] ldat = 16'd0;
|
||||
wire upd = (dat != ldat); // new data present, need to update hw
|
||||
|
||||
reg [23:0] shft=24'b0;
|
||||
wire [23:0] nxt_shft;
|
||||
|
||||
// clock cycle counter to throttle generated spi cycles
|
||||
// allowing one spi clock cycle every 16 cycles of clk, with clk at 200 MHz
|
||||
// gives a spi clock rate of 12 MHz. This can be made more sophisticated
|
||||
// or parameterized, if more flexibility in clk is needed, of course.
|
||||
reg [3:0] ccnt=4'b0;
|
||||
wire [3:0] nxt_ccnt = ccnt + 1'b1;
|
||||
wire half = ccnt==4'b1000;
|
||||
wire full = ccnt==4'b1111;
|
||||
reg sena, hena;
|
||||
wire cena;
|
||||
always @(posedge clk) if (cena) ccnt <= nxt_ccnt;
|
||||
always @(posedge clk) sena <= full; // state updates and rising sclk
|
||||
always @(posedge clk) hena <= half; // for falling sclk
|
||||
|
||||
// transfer state counter
|
||||
reg [4:0] scnt = 5'b0;
|
||||
reg [4:0] nxt_scnt;
|
||||
always @(posedge clk) begin
|
||||
if (sena) begin
|
||||
scnt <= nxt_scnt;
|
||||
shft <= nxt_shft;
|
||||
mosi <= shft[23];
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
// 32 possible states - more than enough to shift-out 24 bits and manage
|
||||
// the sync_n line
|
||||
|
||||
// particular scnt values of interest
|
||||
localparam READY=5'b00000; // waiting for new data
|
||||
localparam DCAPT=5'b00001; // new data transfers into ldat
|
||||
localparam SYNCL=5'b00010; // assert sync_n low
|
||||
localparam SYNCH=5'b11011; // return sync_n high
|
||||
|
||||
assign cena = upd | scnt != READY;
|
||||
|
||||
always @(scnt or upd)
|
||||
begin
|
||||
case (scnt)
|
||||
READY:
|
||||
nxt_scnt = upd ? DCAPT : READY;
|
||||
SYNCH:
|
||||
nxt_scnt = READY;
|
||||
default:
|
||||
nxt_scnt = scnt + 1'b1;
|
||||
endcase
|
||||
end
|
||||
|
||||
// note: defining the power-down mode bits to 00 for "normal operation"
|
||||
assign nxt_shft = (scnt == SYNCL) ? { 8'b000000_00, ldat } : { shft[22:0], 1'b0 };
|
||||
|
||||
// Update ldat when dat has changed, but only if READY.
|
||||
// Changes to dat arriving faster than can be kept up with here are ignored
|
||||
// until the cycle-in-progress is completed.
|
||||
wire ldat_ena = sena & (scnt == DCAPT);
|
||||
always @(posedge clk) begin
|
||||
if (ldat_ena) ldat <= dat;
|
||||
end
|
||||
|
||||
// keep the sync_n line low when idle to minimize power consumption
|
||||
// it gets brought high just before beginning each transaction
|
||||
wire nxt_sync_n = (scnt==SYNCL) | (scnt==SYNCH);
|
||||
always @(posedge clk) if (sena) sync_n <= nxt_sync_n;
|
||||
|
||||
reg sclk_go;
|
||||
always @(posedge clk) sclk_go <= (scnt > SYNCL);
|
||||
wire nxt_sclk = ~sclk_go ? 1'b1 : ~sclk;
|
||||
always @(posedge clk) if (sena | hena) sclk <= nxt_sclk;
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,91 @@
|
||||
//
|
||||
// Copyright 2012 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// This module forms the qualification engine for a single master as
|
||||
// part of a larger arbitration engine for a slave. It would typically
|
||||
// be instantiated from arb_select_master.v to form a complete arbitor solution.
|
||||
//
|
||||
|
||||
module arb_qualify_master
|
||||
#(
|
||||
parameter WIDTH=16 // Bit width of destination field.
|
||||
)
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
// Header signals
|
||||
input [WIDTH-1:0] header,
|
||||
input header_valid,
|
||||
// Slave Confg Signals
|
||||
input [WIDTH-1:0] slave_addr,
|
||||
input [WIDTH-1:0] slave_mask,
|
||||
input slave_valid,
|
||||
// Arbitration flags
|
||||
output reg master_valid,
|
||||
input master_ack
|
||||
);
|
||||
|
||||
localparam WAIT_HEADER_VALID = 0;
|
||||
localparam MATCH = 1;
|
||||
localparam WAIT_HEADER_NOT_VALID = 2;
|
||||
|
||||
|
||||
reg [1:0] state, next_state;
|
||||
|
||||
|
||||
// Does masked slave address match header field for dest from master?
|
||||
assign header_match = ((header & slave_mask) == (slave_addr & slave_mask)) && slave_valid;
|
||||
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
state <= WAIT_HEADER_VALID;
|
||||
master_valid <= 0;
|
||||
end else
|
||||
begin
|
||||
case(state)
|
||||
//
|
||||
// Wait here until Masters FIFO presents a valid header word.
|
||||
//
|
||||
WAIT_HEADER_VALID: begin
|
||||
if (header_valid)
|
||||
if (header_match) begin
|
||||
state <= MATCH;
|
||||
master_valid <= 1;
|
||||
end else
|
||||
next_state <= WAIT_HEADER_NOT_VALID;
|
||||
end
|
||||
//
|
||||
// There should only ever be one match across various arbitors
|
||||
// if they are configured correctly and since the backing FIFO in the
|
||||
// master should not start to drain until the arbitration is won
|
||||
// by that master, master_ack should always preceed de-assertion of
|
||||
// header_valid so we don't check for the other order of deassertion.
|
||||
//
|
||||
MATCH: begin
|
||||
if (master_ack) begin
|
||||
master_valid <= 0;
|
||||
state <= WAIT_HEADER_NOT_VALID;
|
||||
end
|
||||
end
|
||||
//
|
||||
// Wait here until this master starts to drain this packet from his FIFO.
|
||||
//
|
||||
WAIT_HEADER_NOT_VALID: begin
|
||||
if (!header_valid) begin
|
||||
state <= WAIT_HEADER_VALID;
|
||||
end
|
||||
end
|
||||
endcase // case(state)
|
||||
end // else: !if(reset | clear)
|
||||
|
||||
endmodule // arb_qualify_master
|
||||
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2012 Ettus Research, A National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: axi_crossbar
|
||||
// Description:
|
||||
// - Control Registers
|
||||
// - CAM to setup routing between RFNoC blocks
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
module axi_crossbar
|
||||
#(
|
||||
parameter BASE = 0, // settings bus base address
|
||||
parameter FIFO_WIDTH = 64, // AXI4-STREAM data bus width
|
||||
parameter DST_WIDTH = 16, // Width of DST field we are routing on.
|
||||
parameter NUM_INPUTS = 2, // number of input AXI4-STREAM buses
|
||||
parameter NUM_OUTPUTS = 2 // number of output AXI4-STREAM buses
|
||||
)
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
input [7:0] local_addr,
|
||||
// Inputs
|
||||
input [(FIFO_WIDTH*NUM_INPUTS)-1:0] i_tdata,
|
||||
input [NUM_INPUTS-1:0] i_tvalid,
|
||||
input [NUM_INPUTS-1:0] i_tlast,
|
||||
output [NUM_INPUTS-1:0] i_tready,
|
||||
input [NUM_INPUTS-1:0] pkt_present,
|
||||
// Setting Bus
|
||||
input set_stb,
|
||||
input [15:0] set_addr,
|
||||
input [31:0] set_data,
|
||||
// Output
|
||||
output [(FIFO_WIDTH*NUM_OUTPUTS)-1:0] o_tdata,
|
||||
output [NUM_OUTPUTS-1:0] o_tvalid,
|
||||
output [NUM_OUTPUTS-1:0] o_tlast,
|
||||
input [NUM_OUTPUTS-1:0] o_tready,
|
||||
// readback bus
|
||||
input rb_rd_stb,
|
||||
input [$clog2(NUM_OUTPUTS)+$clog2(NUM_INPUTS)-1:0] rb_addr,
|
||||
output reg [31:0] rb_data
|
||||
);
|
||||
|
||||
genvar m,n;
|
||||
|
||||
wire [(NUM_INPUTS*NUM_OUTPUTS)-1:0] forward_valid_in;
|
||||
wire [(NUM_INPUTS*NUM_OUTPUTS)-1:0] forward_ack_in;
|
||||
wire [(NUM_INPUTS*NUM_OUTPUTS)-1:0] forward_valid_out;
|
||||
wire [(NUM_INPUTS*NUM_OUTPUTS)-1:0] forward_ack_out;
|
||||
|
||||
wire [NUM_INPUTS-1:0] i_tready_slave [0:NUM_OUTPUTS-1];
|
||||
|
||||
//
|
||||
// Instantiate an axi_slave_mux for every slave/output of the Crossbar switch.
|
||||
// Each axi_slave_mux contains logic to maux and resolve arbitration
|
||||
// for this particular slave/output.
|
||||
//
|
||||
|
||||
generate
|
||||
for (m = 0; m < NUM_OUTPUTS; m = m + 1) begin: instantiate_slave_mux
|
||||
|
||||
wire [NUM_INPUTS-1:0] i_tready_tmp;
|
||||
|
||||
axi_slave_mux
|
||||
#(
|
||||
.FIFO_WIDTH(FIFO_WIDTH), // AXI4-STREAM data bus width
|
||||
.DST_WIDTH(DST_WIDTH), // Width of DST field we are routing on.
|
||||
.NUM_INPUTS(NUM_INPUTS) // number of input AXI buses
|
||||
) axi_slave_mux_i
|
||||
(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
// Inputs
|
||||
.i_tdata(i_tdata),
|
||||
.i_tvalid(i_tvalid),
|
||||
.i_tlast(i_tlast),
|
||||
.i_tready(i_tready_tmp),
|
||||
// Forwarding flags (One from each Input/Master)
|
||||
.forward_valid(forward_valid_in[(m+1)*NUM_INPUTS-1:m*NUM_INPUTS]),
|
||||
.forward_ack(forward_ack_out[(m+1)*NUM_INPUTS-1:m*NUM_INPUTS]),
|
||||
// Output
|
||||
.o_tdata(o_tdata[(m*FIFO_WIDTH)+FIFO_WIDTH-1:m*FIFO_WIDTH]),
|
||||
.o_tvalid(o_tvalid[m]),
|
||||
.o_tlast(o_tlast[m]),
|
||||
.o_tready(o_tready[m])
|
||||
);
|
||||
|
||||
if (m==0)
|
||||
assign i_tready_slave[0] = i_tready_tmp;
|
||||
else
|
||||
assign i_tready_slave[m] = i_tready_tmp | i_tready_slave[m-1] ;
|
||||
|
||||
end // block: instantiate_slave_mux
|
||||
endgenerate
|
||||
|
||||
assign i_tready = i_tready_slave[NUM_OUTPUTS-1];
|
||||
|
||||
//
|
||||
// Permute the forwarding flag buses
|
||||
//
|
||||
|
||||
generate
|
||||
for (m = 0; m < NUM_OUTPUTS; m = m + 1) begin: permute_outer
|
||||
for (n = 0; n < NUM_INPUTS; n = n + 1) begin: permute_inner
|
||||
assign forward_valid_in[n*NUM_OUTPUTS+m] = forward_valid_out[n+m*NUM_INPUTS];
|
||||
assign forward_ack_in[n+m*NUM_INPUTS] = forward_ack_out[n*NUM_OUTPUTS+m];
|
||||
end
|
||||
end
|
||||
|
||||
endgenerate
|
||||
|
||||
|
||||
//
|
||||
// Instantiate an axi_forwarding_cam for every Input/Master of the Crossbar switch.
|
||||
// Each contains a TCAM like lookup that allocates an egress port.
|
||||
//
|
||||
|
||||
wire [31:0] rb_data_mux[0:NUM_INPUTS-1];
|
||||
|
||||
generate
|
||||
for (m = 0; m < NUM_INPUTS; m = m + 1) begin: instantiate_cam
|
||||
axi_forwarding_cam
|
||||
#(
|
||||
.BASE(BASE),
|
||||
.WIDTH(FIFO_WIDTH), // Bit width of FIFO word.
|
||||
.NUM_OUTPUTS(NUM_OUTPUTS)
|
||||
) axi_forwarding_cam_i
|
||||
(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
// Monitored FIFO signals
|
||||
.o_tdata(i_tdata[(m*FIFO_WIDTH)+FIFO_WIDTH-1:m*FIFO_WIDTH]),
|
||||
.o_tvalid(i_tvalid[m]),
|
||||
.o_tready(i_tready[m]),
|
||||
.o_tlast(i_tlast[m]),
|
||||
.pkt_present(pkt_present[m]),
|
||||
// Configuration
|
||||
.local_addr(local_addr),
|
||||
// Setting Bus
|
||||
.set_stb(set_stb),
|
||||
.set_addr(set_addr),
|
||||
.set_data(set_data),
|
||||
// Header signals
|
||||
.forward_valid(forward_valid_out[(m+1)*NUM_OUTPUTS-1:m*NUM_OUTPUTS]),
|
||||
.forward_ack(forward_ack_in[(m+1)*NUM_OUTPUTS-1:m*NUM_OUTPUTS]),
|
||||
// Readback bus
|
||||
.rb_rd_stb(rb_rd_stb && (rb_addr[$clog2(NUM_OUTPUTS)+$clog2(NUM_INPUTS)-1:$clog2(NUM_OUTPUTS)] == m)),
|
||||
.rb_addr(rb_addr[$clog2(NUM_OUTPUTS)-1:0]),
|
||||
.rb_data(rb_data_mux[m])
|
||||
);
|
||||
end // block: instantiate_fifo_header
|
||||
endgenerate
|
||||
|
||||
// Pipeline readback data to alleviate timing issues
|
||||
always @(posedge clk) rb_data <= rb_data_mux[rb_addr[$clog2(NUM_OUTPUTS)+$clog2(NUM_INPUTS)-1:$clog2(NUM_OUTPUTS)]];
|
||||
|
||||
|
||||
endmodule // axi_crossbar
|
||||
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// Copyright 2015 Ettus Research LLC
|
||||
// Copyright 2017 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Wraps AXI crossbar and exposes cvita_stream_t and settings_bus_t interfaces
|
||||
|
||||
`include "sim_cvita_lib.svh"
|
||||
`include "sim_set_rb_lib.svh"
|
||||
|
||||
module axi_crossbar_intf
|
||||
#(
|
||||
parameter BASE = 0, // settings bus base address
|
||||
parameter FIFO_WIDTH = 64, // AXI4-STREAM data bus width
|
||||
parameter DST_WIDTH = 16, // Width of DST field we are routing on.
|
||||
parameter NUM_PORTS = 2 // number of cvita busses
|
||||
)(
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
input [7:0] local_addr,
|
||||
axis_t.slave s_cvita[0:NUM_PORTS-1],
|
||||
axis_t.master m_cvita[0:NUM_PORTS-1],
|
||||
settings_bus_t.slave set_bus,
|
||||
readback_bus_t.master rb_bus
|
||||
);
|
||||
|
||||
wire [NUM_PORTS*64-1:0] flat_i_tdata;
|
||||
wire [NUM_PORTS-1:0] i_tlast, i_tvalid, i_tready;
|
||||
wire [NUM_PORTS*64-1:0] flat_o_tdata;
|
||||
wire [NUM_PORTS-1:0] o_tlast, o_tvalid, o_tready;
|
||||
|
||||
// Flattern CE tdata arrays
|
||||
genvar i;
|
||||
generate
|
||||
for (i = 0; i < NUM_PORTS; i = i + 1) begin
|
||||
assign flat_i_tdata[i*FIFO_WIDTH+FIFO_WIDTH-1:i*FIFO_WIDTH] = s_cvita[i].tdata;
|
||||
assign i_tlast[i] = s_cvita[i].tlast;
|
||||
assign i_tvalid[i] = s_cvita[i].tvalid;
|
||||
assign s_cvita[i].tready = i_tready[i];
|
||||
end
|
||||
for (i = 0; i < NUM_PORTS; i = i + 1) begin
|
||||
assign m_cvita[i].tdata = flat_o_tdata[i*FIFO_WIDTH+FIFO_WIDTH-1:i*FIFO_WIDTH];
|
||||
assign m_cvita[i].tlast = o_tlast[i];
|
||||
assign m_cvita[i].tvalid = o_tvalid[i];
|
||||
assign o_tready[i] = m_cvita[i].tready;
|
||||
end
|
||||
endgenerate
|
||||
|
||||
wire set_stb = set_bus.stb;
|
||||
wire [15:0] set_addr = set_bus.addr;
|
||||
wire [31:0] set_data = set_bus.data;
|
||||
wire rb_rd_stb = rb_bus.stb;
|
||||
wire [2*$clog2(NUM_PORTS):0] rb_addr = rb_bus.addr[2*$clog2(NUM_PORTS):0];
|
||||
wire [31:0] rb_data;
|
||||
assign rb_bus.data = rb_data;
|
||||
|
||||
axi_crossbar #(
|
||||
.BASE(BASE),
|
||||
.FIFO_WIDTH(FIFO_WIDTH),
|
||||
.DST_WIDTH(DST_WIDTH),
|
||||
.NUM_INPUTS(NUM_PORTS),
|
||||
.NUM_OUTPUTS(NUM_PORTS))
|
||||
inst_axi_crossbar (
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(clear),
|
||||
.local_addr(local_addr),
|
||||
.i_tdata(flat_i_tdata),
|
||||
.i_tvalid(i_tvalid),
|
||||
.i_tlast(i_tlast),
|
||||
.i_tready(i_tready),
|
||||
.pkt_present(i_tvalid),
|
||||
.set_stb(set_stb),
|
||||
.set_addr(set_addr),
|
||||
.set_data(set_data),
|
||||
.o_tdata(flat_o_tdata),
|
||||
.o_tvalid(o_tvalid),
|
||||
.o_tlast(o_tlast),
|
||||
.o_tready(o_tready),
|
||||
.rb_rd_stb(rb_rd_stb),
|
||||
.rb_addr(rb_addr),
|
||||
.rb_data(rb_data));
|
||||
|
||||
endmodule
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2017 Ettus Research, A National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: axi_crossbar_regport
|
||||
// Description:
|
||||
// - axi_crossbar with regport interface for register/CAM access
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
module axi_crossbar_regport #(
|
||||
parameter REG_BASE = 0, // settings bus base address
|
||||
parameter FIFO_WIDTH = 64, // AXI4-STREAM data bus width
|
||||
parameter DST_WIDTH = 16, // Width of DST field we are routing on.
|
||||
parameter NUM_INPUTS = 2, // number of input AXI4-STREAM buses
|
||||
parameter NUM_OUTPUTS = 2, // number of output AXI4-STREAM buses
|
||||
parameter REG_DWIDTH = 32, // Width of the AXI4-Lite data bus (must be 32 or 64)
|
||||
parameter REG_AWIDTH = 14 // Width of the address bus
|
||||
)(
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
|
||||
input reg_wr_req,
|
||||
input [REG_AWIDTH-1:0] reg_wr_addr,
|
||||
input [REG_DWIDTH-1:0] reg_wr_data,
|
||||
|
||||
input reg_rd_req,
|
||||
input [REG_AWIDTH-1:0] reg_rd_addr,
|
||||
output [REG_DWIDTH-1:0] reg_rd_data,
|
||||
output reg_rd_resp,
|
||||
|
||||
// Inputs
|
||||
input [(FIFO_WIDTH*NUM_INPUTS)-1:0] i_tdata,
|
||||
input [NUM_INPUTS-1:0] i_tvalid,
|
||||
input [NUM_INPUTS-1:0] i_tlast,
|
||||
output [NUM_INPUTS-1:0] i_tready,
|
||||
input [NUM_INPUTS-1:0] pkt_present,
|
||||
|
||||
// Output
|
||||
output [(FIFO_WIDTH*NUM_OUTPUTS)-1:0] o_tdata,
|
||||
output [NUM_OUTPUTS-1:0] o_tvalid,
|
||||
output [NUM_OUTPUTS-1:0] o_tlast,
|
||||
input [NUM_OUTPUTS-1:0] o_tready
|
||||
);
|
||||
|
||||
localparam XBAR_VERSION = 32'b1;
|
||||
localparam XBAR_NUM_PORTS = NUM_INPUTS; //or NUM_OUTPUTS
|
||||
|
||||
localparam REG_XBAR_VERSION = REG_BASE + 14'h10;
|
||||
localparam REG_XBAR_NUM_PORTS = REG_BASE + 14'h14;
|
||||
localparam REG_XBAR_LOCAL_ADDR = REG_BASE + 14'h18;
|
||||
localparam REG_BASE_XBAR_SETTING_REG = REG_BASE + 14'h20;
|
||||
localparam REG_END_ADDR_XBAR_SETTING_REG = REG_BASE + 14'h1000;
|
||||
|
||||
// Settings bus address width
|
||||
localparam SR_AWIDTH = 12;
|
||||
|
||||
wire xbar_set_stb;
|
||||
wire [REG_DWIDTH-1:0] xbar_set_data;
|
||||
wire [SR_AWIDTH-1:0] xbar_set_addr;
|
||||
|
||||
wire xbar_rb_stb;
|
||||
wire [SR_AWIDTH-1:0] xbar_rb_addr;
|
||||
wire [REG_DWIDTH-1:0] xbar_rb_data;
|
||||
|
||||
reg [31:0] local_addr_reg;
|
||||
reg reg_rd_resp_glob;
|
||||
reg [REG_DWIDTH-1:0] reg_rd_data_glob;
|
||||
|
||||
wire [REG_DWIDTH-1:0] reg_rd_data_xbar;
|
||||
wire reg_rd_resp_xbar;
|
||||
|
||||
regport_resp_mux #(
|
||||
.WIDTH(REG_DWIDTH),
|
||||
.NUM_SLAVES(2)
|
||||
) inst_regport_resp_mux_xbar (
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.sla_rd_resp({reg_rd_resp_glob, reg_rd_resp_xbar}),
|
||||
.sla_rd_data({reg_rd_data_glob, reg_rd_data_xbar}),
|
||||
.mst_rd_resp(reg_rd_resp),
|
||||
.mst_rd_data(reg_rd_data)
|
||||
);
|
||||
|
||||
// Read Registers
|
||||
always @ (posedge clk) begin
|
||||
if (reset) begin
|
||||
local_addr_reg <= 32'h0;
|
||||
end
|
||||
else begin
|
||||
if (reg_wr_req)
|
||||
case (reg_wr_addr)
|
||||
REG_XBAR_LOCAL_ADDR:
|
||||
local_addr_reg <= reg_wr_data;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
// Write Registers
|
||||
always @ (posedge clk) begin
|
||||
if (reset)
|
||||
reg_rd_resp_glob <= 1'b0;
|
||||
|
||||
else begin
|
||||
if (reg_rd_req) begin
|
||||
reg_rd_resp_glob <= 1'b1;
|
||||
|
||||
case (reg_rd_addr)
|
||||
REG_XBAR_VERSION:
|
||||
reg_rd_data_glob <= XBAR_VERSION;
|
||||
|
||||
REG_XBAR_NUM_PORTS:
|
||||
reg_rd_data_glob <= XBAR_NUM_PORTS;
|
||||
|
||||
REG_XBAR_LOCAL_ADDR:
|
||||
reg_rd_data_glob <= local_addr_reg;
|
||||
|
||||
default:
|
||||
reg_rd_resp_glob <= 1'b0;
|
||||
endcase
|
||||
end
|
||||
else if (reg_rd_resp_glob) begin
|
||||
reg_rd_resp_glob <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
regport_to_xbar_settingsbus #(
|
||||
.BASE(REG_BASE_XBAR_SETTING_REG),
|
||||
.END_ADDR(REG_END_ADDR_XBAR_SETTING_REG),
|
||||
.DWIDTH(REG_DWIDTH),
|
||||
.AWIDTH(REG_AWIDTH),
|
||||
.SR_AWIDTH(SR_AWIDTH),
|
||||
.ADDRESSING("WORD")
|
||||
) inst_regport_to_xbar_settingsbus (
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
|
||||
.reg_wr_req(reg_wr_req),
|
||||
.reg_wr_addr(reg_wr_addr),
|
||||
.reg_wr_data(reg_wr_data),
|
||||
.reg_rd_req(reg_rd_req),
|
||||
.reg_rd_addr(reg_rd_addr),
|
||||
.reg_rd_data(reg_rd_data_xbar),
|
||||
.reg_rd_resp(reg_rd_resp_xbar),
|
||||
|
||||
.set_stb(xbar_set_stb),
|
||||
.set_addr(xbar_set_addr),
|
||||
.set_data(xbar_set_data),
|
||||
.rb_stb(xbar_rb_stb),
|
||||
.rb_addr(xbar_rb_addr),
|
||||
.rb_data(xbar_rb_data)
|
||||
);
|
||||
|
||||
axi_crossbar #(
|
||||
.BASE(0), // Set to 0 as logic for other values has not been tested
|
||||
.FIFO_WIDTH(FIFO_WIDTH),
|
||||
.DST_WIDTH(DST_WIDTH),
|
||||
.NUM_INPUTS(NUM_INPUTS),
|
||||
.NUM_OUTPUTS(NUM_OUTPUTS)
|
||||
) axi_crossbar (
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.clear(1'b0),
|
||||
.local_addr(local_addr_reg),
|
||||
|
||||
// settings bus for config
|
||||
.set_stb(xbar_set_stb),
|
||||
.set_addr({4'b0000,xbar_set_addr}),
|
||||
.set_data(xbar_set_data),
|
||||
.rb_rd_stb(xbar_rb_stb),
|
||||
.rb_addr(xbar_rb_addr[$clog2(NUM_INPUTS)+$clog2(NUM_OUTPUTS)-1:0]),
|
||||
.rb_data(xbar_rb_data),
|
||||
|
||||
// inputs, real men flatten busses
|
||||
.i_tdata(i_tdata),
|
||||
.i_tlast(i_tlast),
|
||||
.i_tvalid(i_tvalid),
|
||||
.i_tready(i_tready),
|
||||
|
||||
// outputs, real men flatten busses
|
||||
.o_tdata(o_tdata),
|
||||
.o_tlast(o_tlast),
|
||||
.o_tvalid(o_tvalid),
|
||||
.o_tready(o_tready),
|
||||
.pkt_present(pkt_present)
|
||||
);
|
||||
|
||||
endmodule // axi_crossbar_regport
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// Copyright 2012 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// This module is connected to the output port of an AXI4-STREAM FIFO that is used to move packetized data.
|
||||
// It extracts and indicates the header (first word) of a packet in the FIFO. The header and flag are pipelined
|
||||
// for timing closure.
|
||||
//
|
||||
|
||||
module axi_fifo_header
|
||||
#(
|
||||
parameter WIDTH=64 // Bit width of FIFO word.
|
||||
)
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
// Monitored FIFO signals
|
||||
input [WIDTH-1:0] o_tdata,
|
||||
input o_tvalid,
|
||||
input o_tready,
|
||||
input o_tlast,
|
||||
input pkt_present,
|
||||
// Header signals
|
||||
output reg [WIDTH-1:0] header,
|
||||
output reg header_valid
|
||||
);
|
||||
|
||||
localparam WAIT_SOF = 0;
|
||||
localparam WAIT_EOF = 1;
|
||||
|
||||
reg out_state;
|
||||
|
||||
|
||||
//
|
||||
// Monitor packets leaving FIFO
|
||||
//
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
out_state <= WAIT_SOF;
|
||||
end else
|
||||
case(out_state)
|
||||
//
|
||||
// After RESET or the EOF of previous packet, the first cycle with
|
||||
// output valid asserted is the SOF and presents the Header word.
|
||||
// The cycle following the concurrent presentation of asserted output
|
||||
// valid and output ready presents the word following the header.
|
||||
//
|
||||
WAIT_SOF:
|
||||
if (o_tvalid && o_tready) begin
|
||||
out_state <= WAIT_EOF;
|
||||
end else begin
|
||||
out_state <= WAIT_SOF;
|
||||
end
|
||||
//
|
||||
// EOF is signalled by o_tlast asserted whilst output valid and ready asserted.
|
||||
//
|
||||
WAIT_EOF:
|
||||
if (o_tlast && o_tvalid && o_tready) begin
|
||||
out_state <= WAIT_SOF;
|
||||
end else begin
|
||||
out_state <= WAIT_EOF;
|
||||
end
|
||||
endcase // case(in_state)
|
||||
|
||||
//
|
||||
// Pipeline Header signals
|
||||
//
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
header <= 0;
|
||||
header_valid <= 0;
|
||||
end else if (o_tvalid && (out_state == WAIT_SOF) && pkt_present) begin
|
||||
// Header will remian valid until o_tready is asserted as this will cause a state transition.
|
||||
header <= o_tdata;
|
||||
header_valid <= 1;
|
||||
end else begin
|
||||
header_valid <= 0;
|
||||
end
|
||||
|
||||
|
||||
endmodule // axi_fifo_header
|
||||
@@ -0,0 +1,232 @@
|
||||
//
|
||||
// Copyright 2013 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// This module implements a highly customized content-addressable memory (CAM)
|
||||
// that enables forwarding decisions to be made on a 16 bit field from a stream ID (SID) field.
|
||||
// The forwarding is generic in the sense that a SID's host destination can map to any endpoint / crossbar port.
|
||||
//
|
||||
// The 16 bits are allocated by convention as 8 bits of Network address (addresses USRP's / AXI crossbars) and
|
||||
// 8 bits of Host address (addresses endpoints / crossbar ports in a USRP).
|
||||
//
|
||||
// By definition if the destination field in the SID addresses a different
|
||||
// USRP / crossbar than this one then we don't care about the Host field, only the Network field.
|
||||
// We only look at the Host field when the Network field addresses us.
|
||||
// Thus we need a CAM of 256+256 entries with Log2(N) bits, where N is the number of
|
||||
// slave(output) ports on the crossbar switch.
|
||||
//
|
||||
// SID format:
|
||||
//
|
||||
// |---------|---------|---------|---------|
|
||||
// | SRC | SRC | DST | DST |
|
||||
// | NETWORK | HOST | NETWORK | HOST |
|
||||
// |---------|---------|---------|---------|
|
||||
// 8 8 8 8
|
||||
|
||||
module axi_forwarding_cam
|
||||
#(
|
||||
parameter BASE = 0, // BASE address for setting registers in this block. (512 addrs used)
|
||||
parameter WIDTH=64, // Bit width of FIFO word.
|
||||
parameter NUM_OUTPUTS=2 // Number of outputs (destinations) in crossbar.
|
||||
)
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
// Monitored FIFO signals
|
||||
input [WIDTH-1:0] o_tdata,
|
||||
input o_tvalid,
|
||||
input o_tready,
|
||||
input o_tlast,
|
||||
input pkt_present,
|
||||
// Configuration
|
||||
input [7:0] local_addr,
|
||||
// Setting Bus
|
||||
input set_stb,
|
||||
input [15:0] set_addr,
|
||||
input [31:0] set_data,
|
||||
|
||||
output reg [NUM_OUTPUTS-1:0] forward_valid,
|
||||
input [NUM_OUTPUTS-1:0] forward_ack,
|
||||
|
||||
input rb_rd_stb,
|
||||
input [$clog2(NUM_OUTPUTS)-1:0] rb_addr,
|
||||
output [31:0] rb_data
|
||||
);
|
||||
|
||||
|
||||
localparam WAIT_SOF = 0;
|
||||
localparam WAIT_EOF = 1;
|
||||
reg state;
|
||||
|
||||
localparam IDLE = 0;
|
||||
localparam FORWARD = 1;
|
||||
localparam WAIT = 2;
|
||||
|
||||
reg [1:0] demux_state;
|
||||
|
||||
reg [15:0] dst;
|
||||
reg dst_valid, dst_valid_reg;
|
||||
wire local_dst;
|
||||
wire [8:0] read_addr;
|
||||
|
||||
//
|
||||
// Monitor packets leaving FIFO
|
||||
//
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
state <= WAIT_SOF;
|
||||
end else
|
||||
case(state)
|
||||
//
|
||||
// After RESET or the EOF of previous packet, the first cycle with
|
||||
// output valid asserted is the SOF and presents the Header word.
|
||||
// The cycle following the concurrent presentation of asserted output
|
||||
// valid and output ready presents the word following the header.
|
||||
//
|
||||
WAIT_SOF:
|
||||
if (o_tvalid && o_tready) begin
|
||||
state <= WAIT_EOF;
|
||||
end else begin
|
||||
state <= WAIT_SOF;
|
||||
end
|
||||
//
|
||||
// EOF is signalled by o_tlast asserted whilst output valid and ready asserted.
|
||||
//
|
||||
WAIT_EOF:
|
||||
if (o_tlast && o_tvalid && o_tready) begin
|
||||
state <= WAIT_SOF;
|
||||
end else begin
|
||||
state <= WAIT_EOF;
|
||||
end
|
||||
endcase // case(in_state)
|
||||
|
||||
//
|
||||
// Extract Destination fields(s) from SID
|
||||
//
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
dst <= 0;
|
||||
dst_valid <= 0;
|
||||
dst_valid_reg <= 0;
|
||||
end else if (o_tvalid && (state == WAIT_SOF) && pkt_present) begin
|
||||
// SID will remain valid until o_tready is asserted as this will cause a state transition.
|
||||
dst <= o_tdata[15:0];
|
||||
dst_valid <= 1;
|
||||
dst_valid_reg <= dst_valid;
|
||||
end else begin
|
||||
dst_valid <= 0;
|
||||
dst_valid_reg <= dst_valid;
|
||||
end
|
||||
|
||||
//
|
||||
// Is Network field in DST our local address?
|
||||
//
|
||||
assign local_dst = (dst[15:8] == local_addr) && dst_valid;
|
||||
|
||||
|
||||
//
|
||||
// Mux address to RAM so that it searches CAM for Network field or Host field.
|
||||
// Network addresses are stored in the lower 256 locations, host addresses the upper 256.
|
||||
//
|
||||
assign read_addr = {local_dst,(local_dst ? dst[7:0] : dst[15:8])};
|
||||
|
||||
//
|
||||
// Implement CAM as block RAM here, 512xCeil(Log2(NUM_OUTPUTS))
|
||||
//
|
||||
//synthesis attribute ram_style of mem is block
|
||||
reg [$clog2(NUM_OUTPUTS)-1 : 0] mem [0:511];
|
||||
|
||||
// Initialize the CAM's local address forwarding decisions with sensible defaults by
|
||||
// assuming dst[7:4] = crossbar port, dst[3:0] = block port. Setup a one-to-one mapping
|
||||
// for crossbar ports and always map same crossbar port regardless of block port.
|
||||
// i.e.
|
||||
// dst 8'h00 => forward to crossbar port 0
|
||||
// dst 8'h01 => forward to crossbar port 0
|
||||
// dst 8'h10 => forward to crossbar port 1
|
||||
// etc.
|
||||
integer xbar_port;
|
||||
integer block_port;
|
||||
initial begin
|
||||
for (xbar_port = 0; xbar_port < NUM_OUTPUTS; xbar_port = xbar_port + 1) begin
|
||||
for (block_port = 0; block_port < 16; block_port = block_port + 1) begin
|
||||
mem[256+(xbar_port << 4)+block_port] = xbar_port;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
reg [8:0] read_addr_reg;
|
||||
wire write;
|
||||
wire [$clog2(NUM_OUTPUTS)-1:0] read_data;
|
||||
|
||||
assign write = (set_addr[15:9] == (BASE >>9)) && set_stb; // Addr decode.
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
read_addr_reg <= read_addr;
|
||||
|
||||
if (write) begin
|
||||
mem[set_addr[8:0]] <= set_data[$clog2(NUM_OUTPUTS)-1:0];
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
assign read_data = mem[read_addr_reg];
|
||||
|
||||
|
||||
//
|
||||
// State machine to manage forwarding flags.
|
||||
//
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
forward_valid <= {NUM_OUTPUTS{1'b0}};
|
||||
demux_state <= IDLE;
|
||||
end else
|
||||
case(demux_state)
|
||||
|
||||
// Wait for Valid DST which indicates a new packet lookup in the CAM.
|
||||
IDLE: begin
|
||||
if (dst_valid_reg == 1) begin
|
||||
forward_valid <= 1'b1 << read_data;
|
||||
demux_state <= FORWARD;
|
||||
end
|
||||
end
|
||||
// When Slave/Output thats forwarding ACK's the forward flag, clear request and wait for packet to be transfered
|
||||
FORWARD: begin
|
||||
if ((forward_ack & forward_valid) != 0) begin
|
||||
forward_valid <= {NUM_OUTPUTS{1'b0}};
|
||||
demux_state <= WAIT;
|
||||
end
|
||||
end
|
||||
// When packet transfered go back to idle.
|
||||
WAIT: begin
|
||||
if (forward_ack == 0)
|
||||
demux_state <= IDLE;
|
||||
end
|
||||
|
||||
endcase // case (demux_state)
|
||||
|
||||
//
|
||||
// Compile forwarding statistics
|
||||
// (This uses a lot of registers!)
|
||||
//
|
||||
genvar m;
|
||||
reg [31:0] statistics [0:NUM_OUTPUTS-1];
|
||||
|
||||
generate
|
||||
for (m = 0; m < NUM_OUTPUTS; m = m + 1) begin: generate_stats
|
||||
always @(posedge clk) begin
|
||||
if (reset | clear) begin
|
||||
statistics[m] <= 0;
|
||||
end else if (forward_ack[m] & forward_valid[m]) begin
|
||||
statistics[m] <= statistics[m] + 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
endgenerate
|
||||
|
||||
assign rb_data = statistics[rb_addr];
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,97 @@
|
||||
//
|
||||
// Copyright 2016 Ettus Research
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Settings register with AXI stream output.
|
||||
//
|
||||
// Parameters / common use cases:
|
||||
// USE_ADDR_LAST & ADDR_LAST User wants additional address that when written to asserts tlast.
|
||||
// Useful for the last word in a packet.
|
||||
// USE_FIFO & FIFO_SIZE Downstream block can throttle and a FIFO is needed to handle that case.
|
||||
// STROBE_LAST User always wants to assert tlast on writes. More efficient than USE_ADDR_LAST
|
||||
// since only one address is used instead of two.
|
||||
// REPEATS Keep tvalid asserted after initial write.
|
||||
// STROBE_LAST & REPEATS tlast is asserted on the initial write then deasserted for repeating output.
|
||||
// MSB_ALIGN Left justify data versus right justify.
|
||||
|
||||
module axi_setting_reg #(
|
||||
parameter ADDR = 0,
|
||||
parameter USE_ADDR_LAST = 0,
|
||||
parameter ADDR_LAST = ADDR+1,
|
||||
parameter AWIDTH = 8,
|
||||
parameter WIDTH = 32,
|
||||
parameter USE_FIFO = 0,
|
||||
parameter FIFO_SIZE = 5,
|
||||
parameter DATA_AT_RESET = 0,
|
||||
parameter VALID_AT_RESET = 0,
|
||||
parameter LAST_AT_RESET = 0,
|
||||
parameter STROBE_LAST = 0,
|
||||
parameter REPEATS = 0,
|
||||
parameter MSB_ALIGN = 0
|
||||
)
|
||||
(
|
||||
input clk, input reset, output reg error_stb,
|
||||
input set_stb, input [AWIDTH-1:0] set_addr, input [31:0] set_data,
|
||||
output [WIDTH-1:0] o_tdata, output o_tlast, output o_tvalid, input o_tready
|
||||
);
|
||||
|
||||
reg init;
|
||||
|
||||
reg [WIDTH-1:0] o_tdata_int;
|
||||
reg o_tlast_int, o_tvalid_int;
|
||||
wire o_tready_int;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (reset) begin
|
||||
o_tdata_int <= DATA_AT_RESET;
|
||||
o_tvalid_int <= VALID_AT_RESET;
|
||||
o_tlast_int <= LAST_AT_RESET;
|
||||
init <= 1'b0;
|
||||
error_stb <= 1'b0;
|
||||
end else begin
|
||||
error_stb <= 1'b0;
|
||||
if (o_tvalid_int & o_tready_int) begin
|
||||
// Deassert tvalid / tlast only if not repeating the output
|
||||
if (REPEATS == 0) begin
|
||||
o_tvalid_int <= 1'b0;
|
||||
end
|
||||
if ((REPEATS == 0) | (STROBE_LAST == 1)) begin
|
||||
o_tlast_int <= 1'b0;
|
||||
end
|
||||
end
|
||||
if (set_stb & ((ADDR[AWIDTH-1:0] == set_addr) | (USE_ADDR_LAST & (ADDR_LAST[AWIDTH-1:0] == set_addr)))) begin
|
||||
init <= 1'b1;
|
||||
o_tdata_int <= (MSB_ALIGN == 0) ? set_data[WIDTH-1:0] : set_data[31:32-WIDTH];
|
||||
o_tvalid_int <= 1'b1;
|
||||
if (set_stb & (STROBE_LAST | (USE_ADDR_LAST & (ADDR_LAST[AWIDTH-1:0] == set_addr)))) begin
|
||||
o_tlast_int <= 1'b1;
|
||||
end else begin
|
||||
o_tlast_int <= 1'b0;
|
||||
end
|
||||
if (~o_tready_int) begin
|
||||
error_stb <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
generate
|
||||
if (USE_FIFO) begin
|
||||
axi_fifo #(
|
||||
.WIDTH(WIDTH+1), .SIZE(FIFO_SIZE))
|
||||
axi_fifo (
|
||||
.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({o_tlast_int,o_tdata_int}), .i_tvalid(o_tvalid_int), .i_tready(o_tready_int),
|
||||
.o_tdata({o_tlast,o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready),
|
||||
.space(), .occupied());
|
||||
end else begin
|
||||
assign o_tdata = o_tdata_int;
|
||||
assign o_tlast = o_tlast_int;
|
||||
assign o_tvalid = o_tvalid_int;
|
||||
assign o_tready_int = o_tready;
|
||||
end
|
||||
endgenerate
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,122 @@
|
||||
//
|
||||
// Copyright 2012 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
`ifndef LOG2
|
||||
`define LOG2(N) (\
|
||||
N < 2 ? 0 : \
|
||||
N < 4 ? 1 : \
|
||||
N < 8 ? 2 : \
|
||||
N < 16 ? 3 : \
|
||||
N < 32 ? 4 : \
|
||||
N < 64 ? 5 : \
|
||||
N < 128 ? 6 : \
|
||||
N < 256 ? 7 : \
|
||||
N < 512 ? 8 : \
|
||||
N < 1024 ? 9 : 10)
|
||||
`endif
|
||||
|
||||
module axi_slave_mux
|
||||
#(
|
||||
parameter FIFO_WIDTH = 64, // AXI4-STREAM data bus width
|
||||
parameter DST_WIDTH = 16, // Width of DST field we are routing on.
|
||||
parameter NUM_INPUTS = 2 // number of input AXI buses
|
||||
)
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
// Inputs
|
||||
input [(FIFO_WIDTH*NUM_INPUTS)-1:0] i_tdata,
|
||||
input [NUM_INPUTS-1:0] i_tvalid,
|
||||
input [NUM_INPUTS-1:0] i_tlast,
|
||||
output [NUM_INPUTS-1:0] i_tready,
|
||||
// Forwarding Flags
|
||||
input [NUM_INPUTS-1:0] forward_valid,
|
||||
output reg [NUM_INPUTS-1:0] forward_ack,
|
||||
// Output
|
||||
output [FIFO_WIDTH-1:0] o_tdata,
|
||||
output o_tvalid,
|
||||
output o_tlast,
|
||||
input o_tready
|
||||
);
|
||||
|
||||
wire [FIFO_WIDTH-1:0] i_tdata_array [0:NUM_INPUTS-1];
|
||||
|
||||
reg [`LOG2(NUM_INPUTS):0] select;
|
||||
reg enable;
|
||||
|
||||
|
||||
reg state;
|
||||
|
||||
localparam CHECK_THIS_INPUT = 0;
|
||||
localparam WAIT_LAST = 1;
|
||||
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
state <= CHECK_THIS_INPUT;
|
||||
select <= 0;
|
||||
enable <= 0;
|
||||
forward_ack <= 0;
|
||||
end else begin
|
||||
case(state)
|
||||
// Is the currently selected input addressing this slave with a ready packet?
|
||||
CHECK_THIS_INPUT: begin
|
||||
if (forward_valid[select]) begin
|
||||
enable <= 1;
|
||||
forward_ack[select] <= 1;
|
||||
state <= WAIT_LAST;
|
||||
end else if (select == NUM_INPUTS - 1 ) begin
|
||||
select <= 0;
|
||||
end else begin
|
||||
select <= select + 1;
|
||||
end
|
||||
end
|
||||
// Assert ACK immediately to forwarding logic and then wait for end of packet.
|
||||
WAIT_LAST: begin
|
||||
|
||||
if (i_tlast[select] && i_tvalid[select] && o_tready) begin
|
||||
if (select == NUM_INPUTS - 1 ) begin
|
||||
select <= 0;
|
||||
end else begin
|
||||
select <= select + 1;
|
||||
end
|
||||
state <= CHECK_THIS_INPUT;
|
||||
forward_ack <= 0;
|
||||
enable <= 0;
|
||||
end else begin
|
||||
forward_ack[select] <= 1;
|
||||
enable <= 1;
|
||||
end
|
||||
end
|
||||
endcase // case(state)
|
||||
end
|
||||
|
||||
//
|
||||
// Combinatorial mux
|
||||
//
|
||||
genvar m;
|
||||
|
||||
generate
|
||||
for (m = 0; m < NUM_INPUTS; m = m + 1) begin: form_buses
|
||||
assign i_tdata_array[m] = i_tdata[(m*FIFO_WIDTH)+FIFO_WIDTH-1:m*FIFO_WIDTH];
|
||||
end
|
||||
endgenerate
|
||||
|
||||
assign o_tdata = i_tdata_array[select];
|
||||
assign o_tvalid = enable && i_tvalid[select];
|
||||
assign o_tlast = enable && i_tlast[select];
|
||||
// assign i_tready = {NUM_INPUTS{o_tready}} & (enable << select);
|
||||
|
||||
generate
|
||||
for (m = 0; m < NUM_INPUTS; m = m + 1) begin: form_ready
|
||||
assign i_tready[m] = o_tready && enable && (select == m);
|
||||
end
|
||||
endgenerate
|
||||
|
||||
|
||||
endmodule // axi_slave_mux
|
||||
@@ -0,0 +1,145 @@
|
||||
//
|
||||
// Copyright 2014 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
//
|
||||
// Test Virtual FIFO's by streaming modulo 2^32 counter (replicated in upper
|
||||
// and lower 32bits). Test result by tracking count on receive and using
|
||||
// sticky flag for error indication.
|
||||
// Also provide signal from MSB of 32bit count to blink LED.
|
||||
//
|
||||
|
||||
module axi_test_vfifo
|
||||
#(parameter PACKET_SIZE = 128)
|
||||
(
|
||||
input aclk,
|
||||
input aresetn,
|
||||
input enable,
|
||||
// AXI Stream Out
|
||||
output reg out_axis_tvalid,
|
||||
input out_axis_tready,
|
||||
output [63 : 0] out_axis_tdata,
|
||||
output reg [7 : 0] out_axis_tstrb,
|
||||
output reg [7 : 0] out_axis_tkeep,
|
||||
output reg out_axis_tlast,
|
||||
output reg [0 : 0] out_axis_tid,
|
||||
output reg [0 : 0] out_axis_tdest,
|
||||
input vfifo_full,
|
||||
// AXI Stream In
|
||||
input in_axis_tvalid,
|
||||
output reg in_axis_tready,
|
||||
input [63 : 0] in_axis_tdata,
|
||||
input [7 : 0] in_axis_tstrb,
|
||||
input [7 : 0] in_axis_tkeep,
|
||||
input in_axis_tlast,
|
||||
input [0 : 0] in_axis_tid,
|
||||
input [0 : 0] in_axis_tdest,
|
||||
// Flags
|
||||
output reg flag_error,
|
||||
output heartbeat_in,
|
||||
output heartbeat_out,
|
||||
output [31:0] expected_count
|
||||
);
|
||||
|
||||
|
||||
reg [31:0] out_count;
|
||||
reg [31:0] in_count;
|
||||
reg [63:0] in_axis_tdata_reg;
|
||||
reg in_data_valid;
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Output
|
||||
//
|
||||
always @(posedge aclk)
|
||||
if (!aresetn) begin
|
||||
out_count <= 0;
|
||||
out_axis_tvalid <= 0;
|
||||
out_axis_tid <= 0; // Don't care.
|
||||
out_axis_tdest <= 0; // Only use port 0 of VFIFO.
|
||||
out_axis_tstrb <= 0; // Unused in VFIFO
|
||||
out_axis_tkeep <= 8'hFF; // Always use every byte of data
|
||||
out_axis_tlast <= 1'b0;
|
||||
end else if (enable) begin
|
||||
if (~vfifo_full) begin
|
||||
// Always ready to output new count value.
|
||||
out_axis_tvalid <= 1;
|
||||
if (out_axis_tready)
|
||||
out_count <= out_count + 1;
|
||||
// Assert TLAST every PACKET_SIZE beats.
|
||||
if (out_count[15:0] == PACKET_SIZE)
|
||||
out_axis_tlast <= 1'b1;
|
||||
else
|
||||
out_axis_tlast <= 1'b0;
|
||||
end else begin
|
||||
out_axis_tvalid <= 0;
|
||||
end
|
||||
end else begin
|
||||
out_axis_tlast <= 1'b0;
|
||||
out_axis_tvalid <= 0;
|
||||
end
|
||||
|
||||
assign out_axis_tdata = {out_count,out_count};
|
||||
|
||||
assign heartbeat_out = out_count[28];
|
||||
|
||||
|
||||
//
|
||||
// Input (Ignore TLAST signal)
|
||||
//
|
||||
always @(posedge aclk)
|
||||
if (!aresetn) begin
|
||||
in_axis_tready <= 0;
|
||||
in_axis_tdata_reg <= 0;
|
||||
in_data_valid <= 0;
|
||||
|
||||
end else if (enable) begin
|
||||
in_axis_tready <= 1;
|
||||
in_axis_tdata_reg <= in_axis_tdata;
|
||||
if (in_axis_tvalid)
|
||||
in_data_valid <= 1;
|
||||
else
|
||||
in_data_valid <= 0;
|
||||
end else begin
|
||||
in_data_valid <= 0;
|
||||
in_axis_tready <= 0;
|
||||
end // else: !if(enable)
|
||||
|
||||
|
||||
assign heartbeat_in = in_count[28];
|
||||
|
||||
//
|
||||
// Input Checker
|
||||
//
|
||||
always @(posedge aclk)
|
||||
if (!aresetn) begin
|
||||
in_count <= 0;
|
||||
flag_error <= 0;
|
||||
end else if (enable) begin
|
||||
if (in_data_valid) begin
|
||||
|
||||
if ((in_axis_tdata_reg[63:32] != in_count) || (in_axis_tdata_reg[31:0] != in_count))
|
||||
begin
|
||||
flag_error <= 1;
|
||||
in_count <= in_axis_tdata_reg[63:32] + 1;
|
||||
end
|
||||
else
|
||||
begin
|
||||
flag_error <= 0;
|
||||
in_count <= in_count + 1;
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
assign expected_count = in_count;
|
||||
|
||||
|
||||
endmodule // axi_test_vfifo
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
//
|
||||
// Copyright 2019 Ettus Research, A National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: axil_ctrlport_master
|
||||
// Description:
|
||||
// An AXI4-Lite read/write control port adapter
|
||||
//
|
||||
// Converts AXI4-Lite transactions into control port requests.
|
||||
// Converts all AXI requests to control port by only forwarding the
|
||||
// CTRLPORT_AWIDTH LSBs of the address.
|
||||
//
|
||||
// Limitation:
|
||||
// The control port interface will only use address, data, byte enable and
|
||||
// wr/rd flags. All other signals are tied to 0.
|
||||
|
||||
|
||||
module axil_ctrlport_master #(
|
||||
parameter TIMEOUT = 10, // log2(timeout). Control port will timeout after 2^TIMEOUT AXI clock cycles
|
||||
parameter AXI_AWIDTH = 17, // Width of the AXI bus. Aliasing occurs of AXI_AWIDTH > CTRLPORT_AWIDTH
|
||||
parameter CTRLPORT_AWIDTH = 17 // Number of address LSBs forwarded to m_ctrlport_req_addr
|
||||
)(
|
||||
//Clock and reset
|
||||
input wire s_axi_aclk,
|
||||
input wire s_axi_aresetn,
|
||||
// AXI4-Lite: Write address port (domain: s_axi_aclk)
|
||||
input wire [AXI_AWIDTH-1:0] s_axi_awaddr,
|
||||
input wire s_axi_awvalid,
|
||||
output reg s_axi_awready,
|
||||
// AXI4-Lite: Write data port (domain: s_axi_aclk)
|
||||
input wire [31:0] s_axi_wdata,
|
||||
input wire [ 3:0] s_axi_wstrb,
|
||||
input wire s_axi_wvalid,
|
||||
output reg s_axi_wready,
|
||||
// AXI4-Lite: Write response port (domain: s_axi_aclk)
|
||||
output reg [ 1:0] s_axi_bresp = 0,
|
||||
output reg s_axi_bvalid,
|
||||
input wire s_axi_bready,
|
||||
// AXI4-Lite: Read address port (domain: s_axi_aclk)
|
||||
input wire [AXI_AWIDTH-1:0] s_axi_araddr,
|
||||
input wire s_axi_arvalid,
|
||||
output reg s_axi_arready,
|
||||
// AXI4-Lite: Read data port (domain: s_axi_aclk)
|
||||
output reg [31:0] s_axi_rdata = 0,
|
||||
output reg [ 1:0] s_axi_rresp = 0,
|
||||
output reg s_axi_rvalid,
|
||||
input wire s_axi_rready,
|
||||
// Control port master request interface
|
||||
output reg m_ctrlport_req_wr,
|
||||
output reg m_ctrlport_req_rd,
|
||||
output reg [19:0] m_ctrlport_req_addr = 0,
|
||||
output wire [ 9:0] m_ctrlport_req_portid,
|
||||
output wire [15:0] m_ctrlport_req_rem_epid,
|
||||
output wire [ 9:0] m_ctrlport_req_rem_portid,
|
||||
output reg [31:0] m_ctrlport_req_data = 0,
|
||||
output reg [ 3:0] m_ctrlport_req_byte_en = 0,
|
||||
output wire m_ctrlport_req_has_time,
|
||||
output wire [63:0] m_ctrlport_req_time,
|
||||
// Control port master response interface
|
||||
input wire m_ctrlport_resp_ack,
|
||||
input wire [ 1:0] m_ctrlport_resp_status,
|
||||
input wire [31:0] m_ctrlport_resp_data
|
||||
);
|
||||
|
||||
`include "../axi/axi_defs.v"
|
||||
`include "../rfnoc/core/ctrlport.vh"
|
||||
|
||||
//----------------------------------------------------------
|
||||
// unused ctrlport outputs
|
||||
//----------------------------------------------------------
|
||||
assign m_ctrlport_req_portid = 10'b0;
|
||||
assign m_ctrlport_req_rem_epid = 16'b0;
|
||||
assign m_ctrlport_req_rem_portid = 10'b0;
|
||||
assign m_ctrlport_req_has_time = 1'b0;
|
||||
assign m_ctrlport_req_time = 64'b0;
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Address calculation
|
||||
//----------------------------------------------------------
|
||||
// define configuration for the address calculation
|
||||
localparam [CTRLPORT_ADDR_W-1:0] ADDRESS_MASK = {CTRLPORT_ADDR_W {1'b0}} | {CTRLPORT_AWIDTH {1'b1}};
|
||||
|
||||
// bits to extract from AXI address
|
||||
localparam AXI_ADDR_BITS_TO_FORWARD = (AXI_AWIDTH < CTRLPORT_ADDR_W) ? AXI_AWIDTH : CTRLPORT_ADDR_W;
|
||||
|
||||
//----------------------------------------------------------
|
||||
// State machine for read and write
|
||||
//----------------------------------------------------------
|
||||
localparam IDLE = 4'd0;
|
||||
localparam READ_INIT = 4'd1;
|
||||
localparam WRITE_INIT = 4'd2;
|
||||
localparam READ_TRANSFER = 4'd3;
|
||||
localparam WRITE_TRANSFER = 4'd4;
|
||||
localparam READ_IN_PROGRESS = 4'd5;
|
||||
localparam WRITE_IN_PROGRESS = 4'd6;
|
||||
localparam WRITE_DONE = 4'd7;
|
||||
localparam READ_DONE = 4'd8;
|
||||
|
||||
reg [3:0] state;
|
||||
reg [TIMEOUT-1:0] timeout_counter;
|
||||
|
||||
always @ (posedge s_axi_aclk) begin
|
||||
if (~s_axi_aresetn) begin
|
||||
state <= IDLE;
|
||||
|
||||
// clear AXI feedback paths and controlport requests
|
||||
s_axi_awready <= 1'b0;
|
||||
s_axi_wready <= 1'b0;
|
||||
s_axi_bvalid <= 1'b0;
|
||||
s_axi_arready <= 1'b0;
|
||||
s_axi_rvalid <= 1'b0;
|
||||
m_ctrlport_req_rd <= 1'b0;
|
||||
m_ctrlport_req_wr <= 1'b0;
|
||||
end else begin
|
||||
case (state)
|
||||
// decide whether a read or write should be handled
|
||||
IDLE: begin
|
||||
timeout_counter <= {TIMEOUT {1'b1}};
|
||||
|
||||
if (s_axi_arvalid) begin
|
||||
state <= READ_INIT;
|
||||
end
|
||||
else if (s_axi_awvalid) begin
|
||||
state <= WRITE_INIT;
|
||||
end
|
||||
end
|
||||
|
||||
// wait for FIFO to get read to assign valid
|
||||
READ_INIT: begin
|
||||
// signal ready to upstream module
|
||||
s_axi_arready <= 1'b1;
|
||||
|
||||
state <= READ_TRANSFER;
|
||||
end
|
||||
|
||||
// transfer data to FIFO
|
||||
READ_TRANSFER: begin
|
||||
// clear ready flag from READ_INIT state
|
||||
s_axi_arready <= 1'b0;
|
||||
// transfer data to controlport
|
||||
m_ctrlport_req_rd <= 1'b1;
|
||||
m_ctrlport_req_addr <= s_axi_araddr[AXI_ADDR_BITS_TO_FORWARD-1:0] & ADDRESS_MASK;
|
||||
m_ctrlport_req_byte_en <= 4'b1111;
|
||||
|
||||
state <= READ_IN_PROGRESS;
|
||||
end
|
||||
|
||||
// wait for controlport response is available
|
||||
READ_IN_PROGRESS: begin
|
||||
// clear read flag from previous state
|
||||
m_ctrlport_req_rd <= 1'b0;
|
||||
|
||||
//decrement timeout
|
||||
timeout_counter <= timeout_counter - 1;
|
||||
|
||||
if (m_ctrlport_resp_ack == 1'b1 || timeout_counter == 0) begin
|
||||
s_axi_rvalid <= 1'b1;
|
||||
s_axi_rdata <= m_ctrlport_resp_data;
|
||||
s_axi_rresp <= `AXI4_RESP_OKAY;
|
||||
|
||||
// use AXI DECERR to inform about failed transaction
|
||||
if (timeout_counter == 0) begin
|
||||
s_axi_rresp <= `AXI4_RESP_DECERR;
|
||||
end else begin
|
||||
// if controlport response is not OKAY use AXI SLVERR to propagate error
|
||||
if (m_ctrlport_resp_status != CTRL_STS_OKAY) begin
|
||||
s_axi_rresp <= `AXI4_RESP_SLVERR;
|
||||
end
|
||||
end
|
||||
|
||||
state <= READ_DONE;
|
||||
end
|
||||
end
|
||||
|
||||
// wait until read response is transferred
|
||||
READ_DONE: begin
|
||||
if (s_axi_rready) begin
|
||||
s_axi_rvalid <= 1'b0;
|
||||
state <= IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
//wait for FIFO and data to process
|
||||
WRITE_INIT: begin
|
||||
if (s_axi_wvalid) begin
|
||||
s_axi_awready <= 1'b1;
|
||||
s_axi_wready <= 1'b1;
|
||||
state <= WRITE_TRANSFER;
|
||||
end
|
||||
end
|
||||
|
||||
// transfer data to FIFO
|
||||
WRITE_TRANSFER: begin
|
||||
// clear ready flags from READ_INIT state
|
||||
s_axi_awready <= 1'b0;
|
||||
s_axi_wready <= 1'b0;
|
||||
// transfer data to controlport
|
||||
m_ctrlport_req_wr <= 1'b1;
|
||||
m_ctrlport_req_addr <= s_axi_awaddr[AXI_ADDR_BITS_TO_FORWARD-1:0] & ADDRESS_MASK;
|
||||
m_ctrlport_req_data <= s_axi_wdata;
|
||||
m_ctrlport_req_byte_en <= s_axi_wstrb;
|
||||
|
||||
state <= WRITE_IN_PROGRESS;
|
||||
end
|
||||
|
||||
// wait for write to complete
|
||||
WRITE_IN_PROGRESS: begin
|
||||
// clear write flag from previous state
|
||||
m_ctrlport_req_wr <= 1'b0;
|
||||
|
||||
//decrement timeout
|
||||
timeout_counter <= timeout_counter - 1;
|
||||
|
||||
if (m_ctrlport_resp_ack == 1'b1 || timeout_counter == 0) begin
|
||||
s_axi_bvalid <= 1'b1;
|
||||
s_axi_rdata <= 32'b0;
|
||||
s_axi_bresp <= `AXI4_RESP_OKAY;
|
||||
|
||||
// use AXI DECERR to inform about failed transaction
|
||||
if (timeout_counter == 0) begin
|
||||
s_axi_bresp <= `AXI4_RESP_DECERR;
|
||||
end else begin
|
||||
// if controlport response is not OKAY use AXI SLVERR to propagate error
|
||||
if (m_ctrlport_resp_status != CTRL_STS_OKAY) begin
|
||||
s_axi_bresp <= `AXI4_RESP_SLVERR;
|
||||
end
|
||||
end
|
||||
|
||||
state <= WRITE_DONE;
|
||||
end
|
||||
end
|
||||
|
||||
WRITE_DONE: begin
|
||||
if (s_axi_bready) begin
|
||||
state <= IDLE;
|
||||
s_axi_bvalid <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
default: begin
|
||||
state <= IDLE;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,228 @@
|
||||
//
|
||||
// Copyright 2016-2017 Ettus Research
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
// An AXI4-Lite read/write register port adapter
|
||||
//
|
||||
// Converts memory mapped flow controlled AXI4-Lite transactions into a much
|
||||
// simpler non flow controlled write and read register bus.
|
||||
//
|
||||
// WRITE Transaction:
|
||||
// - Transaction completes in one cycle
|
||||
// - Valid, Strobe, Address and Data asserted in same cycle
|
||||
// __ __ __ __
|
||||
// clk __| |__| |__| |__| |__
|
||||
// _____
|
||||
// reg_wr_req ________| |___________
|
||||
// _____
|
||||
// reg_wr_keep XXXXXXXX|_____|XXXXXXXXXXX
|
||||
// _____
|
||||
// reg_wr_addr XXXXXXXX|_____|XXXXXXXXXXX
|
||||
// _____
|
||||
// reg_wr_data XXXXXXXX|_____|XXXXXXXXXXX
|
||||
//;
|
||||
// READ Transaction:
|
||||
// - Transaction request completes in one cycle, with valid and address assertion
|
||||
// - Transaction response must complete in at least one cycle with resp and data
|
||||
// - resp must be asserted between 1 and pow(2, TIMEOUT) cycles otherwise the read will timeout
|
||||
// __ __ __ __ __
|
||||
// clk __| |__| |__| |__| |__| |__
|
||||
// _____
|
||||
// reg_rd_req ________| |_________________
|
||||
// _____
|
||||
// reg_rd_addr XXXXXXXX|_____|XXXXXXXXXXXXXXXXX
|
||||
// _____
|
||||
// reg_rd_resp ____________________| |_____
|
||||
// _____
|
||||
// reg_rd_data XXXXXXXXXXXXXXXXXXXX|_____|XXXXX
|
||||
|
||||
|
||||
module axil_regport_master #(
|
||||
parameter DWIDTH = 32, // Width of the AXI4-Lite data bus (must be 32 or 64)
|
||||
parameter AWIDTH = 32, // Width of the address bus
|
||||
parameter WRBASE = 32'h0, // Write address base
|
||||
parameter RDBASE = 32'h0, // Read address base
|
||||
parameter TIMEOUT = 10 // log2(timeout). Read will timeout after (2^TIMEOUT - 1) cycles
|
||||
)(
|
||||
// Clock and reset
|
||||
input s_axi_aclk,
|
||||
input s_axi_aresetn,
|
||||
input reg_clk,
|
||||
// AXI4-Lite: Write address port (domain: s_axi_aclk)
|
||||
input [AWIDTH-1:0] s_axi_awaddr,
|
||||
input s_axi_awvalid,
|
||||
output reg s_axi_awready,
|
||||
// AXI4-Lite: Write data port (domain: s_axi_aclk)
|
||||
input [DWIDTH-1:0] s_axi_wdata,
|
||||
input [DWIDTH/8-1:0] s_axi_wstrb,
|
||||
input s_axi_wvalid,
|
||||
output reg s_axi_wready,
|
||||
// AXI4-Lite: Write response port (domain: s_axi_aclk)
|
||||
output reg [1:0] s_axi_bresp,
|
||||
output reg s_axi_bvalid,
|
||||
input s_axi_bready,
|
||||
// AXI4-Lite: Read address port (domain: s_axi_aclk)
|
||||
input [AWIDTH-1:0] s_axi_araddr,
|
||||
input s_axi_arvalid,
|
||||
output reg s_axi_arready,
|
||||
// AXI4-Lite: Read data port (domain: s_axi_aclk)
|
||||
output reg [DWIDTH-1:0] s_axi_rdata,
|
||||
output reg [1:0] s_axi_rresp,
|
||||
output reg s_axi_rvalid,
|
||||
input s_axi_rready,
|
||||
// Register port: Write port (domain: reg_clk)
|
||||
output reg_wr_req,
|
||||
output [AWIDTH-1:0] reg_wr_addr,
|
||||
output [DWIDTH-1:0] reg_wr_data,
|
||||
output [DWIDTH/8-1:0] reg_wr_keep,
|
||||
// Register port: Read port (domain: reg_clk)
|
||||
output reg_rd_req,
|
||||
output [AWIDTH-1:0] reg_rd_addr,
|
||||
input reg_rd_resp,
|
||||
input [DWIDTH-1:0] reg_rd_data
|
||||
);
|
||||
|
||||
//NOTE: clog2 only works when assigned to a parameter
|
||||
// localparam does not work
|
||||
parameter ADDR_LSB = $clog2(DWIDTH/8); //Do not modify
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Write state machine
|
||||
//----------------------------------------------------------
|
||||
reg [AWIDTH-1:0] wr_addr_cache;
|
||||
wire wr_fifo_valid, wr_fifo_ready;
|
||||
wire [AWIDTH-1:0] wr_addr_rel = (s_axi_awaddr - WRBASE);
|
||||
|
||||
// Generate s_axi_awready and latch write address
|
||||
always @(posedge s_axi_aclk) begin
|
||||
if (!s_axi_aresetn) begin
|
||||
s_axi_awready <= 1'b0;
|
||||
wr_addr_cache <= {AWIDTH{1'b0}};
|
||||
end else begin
|
||||
if (~s_axi_awready && s_axi_awvalid && s_axi_wvalid && wr_fifo_ready) begin
|
||||
s_axi_awready <= 1'b1;
|
||||
wr_addr_cache <= {wr_addr_rel[AWIDTH-1:ADDR_LSB], {ADDR_LSB{1'b0}}};
|
||||
end else begin
|
||||
s_axi_awready <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Generate s_axi_wready
|
||||
always @(posedge s_axi_aclk) begin
|
||||
if (!s_axi_aresetn) begin
|
||||
s_axi_wready <= 1'b0;
|
||||
end else begin
|
||||
if (~s_axi_wready && s_axi_wvalid && s_axi_awvalid)
|
||||
s_axi_wready <= 1'b1;
|
||||
else
|
||||
s_axi_wready <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
// Generate write response
|
||||
assign wr_fifo_valid = s_axi_awready && s_axi_awvalid && s_axi_wready && s_axi_wvalid && ~s_axi_bvalid;
|
||||
|
||||
always @(posedge s_axi_aclk) begin
|
||||
if (!s_axi_aresetn) begin
|
||||
s_axi_bvalid <= 1'b0;
|
||||
s_axi_bresp <= 2'b0;
|
||||
end else begin
|
||||
if (wr_fifo_valid && wr_fifo_ready) begin
|
||||
// indicates a valid write response is available
|
||||
s_axi_bvalid <= 1'b1;
|
||||
s_axi_bresp <= 2'b0; // 'OKAY' response
|
||||
end else begin
|
||||
if (s_axi_bready && s_axi_bvalid)
|
||||
s_axi_bvalid <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
axi_fifo_2clk #( .WIDTH(DWIDTH/8 + AWIDTH + DWIDTH), .SIZE(0) ) wr_fifo_2clk_i (
|
||||
.reset(~s_axi_aresetn), .i_aclk(s_axi_aclk),
|
||||
.i_tdata({s_axi_wstrb, wr_addr_cache, s_axi_wdata}),
|
||||
.i_tvalid(wr_fifo_valid), .i_tready(wr_fifo_ready),
|
||||
.o_aclk(reg_clk),
|
||||
.o_tdata({reg_wr_keep, reg_wr_addr, reg_wr_data}),
|
||||
.o_tvalid(reg_wr_req), .o_tready(1'b1)
|
||||
);
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Read state machine
|
||||
//----------------------------------------------------------
|
||||
reg [TIMEOUT-1:0] read_pending_ctr = {TIMEOUT{1'b0}};
|
||||
wire read_timed_out = (read_pending_ctr == {{(TIMEOUT-1){1'b0}}, 1'b1});
|
||||
wire read_pending = (read_pending_ctr != {TIMEOUT{1'b0}});
|
||||
wire [AWIDTH-1:0] rd_addr_rel = (s_axi_araddr - RDBASE);
|
||||
|
||||
wire rdreq_fifo_ready, rdresp_fifo_valid;
|
||||
wire [DWIDTH-1:0] rdresp_fifo_data;
|
||||
|
||||
// Generate s_axi_arready and latch read address
|
||||
always @(posedge s_axi_aclk) begin
|
||||
if (!s_axi_aresetn) begin
|
||||
s_axi_arready <= 1'b0;
|
||||
read_pending_ctr <= {TIMEOUT{1'b0}};
|
||||
end else begin
|
||||
if (~s_axi_arready && s_axi_arvalid && rdreq_fifo_ready) begin
|
||||
s_axi_arready <= 1'b1;
|
||||
read_pending_ctr <= {TIMEOUT{1'b1}};
|
||||
end else begin
|
||||
s_axi_arready <= 1'b0;
|
||||
end
|
||||
if (read_pending) begin
|
||||
if (rdresp_fifo_valid && ~s_axi_rvalid)
|
||||
read_pending_ctr <= {TIMEOUT{1'b0}};
|
||||
else
|
||||
read_pending_ctr <= read_pending_ctr - 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Perform read transaction
|
||||
always @(posedge s_axi_aclk) begin
|
||||
if (!s_axi_aresetn) begin
|
||||
s_axi_rvalid <= 1'b0;
|
||||
s_axi_rresp <= 2'b00;
|
||||
s_axi_rdata <= 0;
|
||||
end else begin
|
||||
if (read_pending && rdresp_fifo_valid && ~s_axi_rvalid) begin
|
||||
// Valid read data is available at the read data bus
|
||||
s_axi_rvalid <= 1'b1;
|
||||
s_axi_rresp <= 2'b00; // 'OKAY' response
|
||||
s_axi_rdata <= rdresp_fifo_data;
|
||||
end else if (read_pending && read_timed_out && ~s_axi_rvalid) begin
|
||||
// Read timed out. Assert error.
|
||||
s_axi_rvalid <= 1'b1;
|
||||
s_axi_rresp <= 2'b10; // 'SLVERR' response
|
||||
s_axi_rdata <= {DWIDTH{1'b1}};
|
||||
end else if (s_axi_rvalid && s_axi_rready) begin
|
||||
// Read data is accepted by the master
|
||||
s_axi_rvalid <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
axi_fifo_2clk #( .WIDTH(AWIDTH), .SIZE(0) ) readreq_fifo_2clk_i (
|
||||
.reset(~s_axi_aresetn), .i_aclk(s_axi_aclk),
|
||||
.i_tdata({rd_addr_rel[AWIDTH-1:ADDR_LSB], {ADDR_LSB{1'b0}}}),
|
||||
.i_tvalid(s_axi_arready && s_axi_arvalid), .i_tready(rdreq_fifo_ready),
|
||||
.o_aclk(reg_clk),
|
||||
.o_tdata(reg_rd_addr),
|
||||
.o_tvalid(reg_rd_req), .o_tready(1'b1)
|
||||
);
|
||||
|
||||
axi_fifo_2clk #( .WIDTH(DWIDTH), .SIZE(0) ) rdresp_fifo_2clk_i (
|
||||
.reset(~s_axi_aresetn), .i_aclk(reg_clk),
|
||||
.i_tdata(reg_rd_data),
|
||||
.i_tvalid(reg_rd_resp), .i_tready(/* lossy */),
|
||||
.o_aclk(s_axi_aclk),
|
||||
.o_tdata(rdresp_fifo_data),
|
||||
.o_tvalid(rdresp_fifo_valid), .o_tready(~read_pending || (s_axi_rvalid && (s_axi_rresp == 2'b00)))
|
||||
);
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,164 @@
|
||||
//
|
||||
// Copyright 2016 Ettus Research
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// AXI4lite to NI Register Port interface
|
||||
//
|
||||
|
||||
module axil_to_ni_regport #(
|
||||
parameter RP_AWIDTH = 16,
|
||||
parameter RP_DWIDTH = 32,
|
||||
parameter TIMEOUT = 512
|
||||
)(
|
||||
input s_axi_aclk,
|
||||
input s_axi_areset,
|
||||
|
||||
// AXI4lite interface
|
||||
input [31:0] s_axi_awaddr,
|
||||
input s_axi_awvalid,
|
||||
output s_axi_awready,
|
||||
input [31:0] s_axi_wdata,
|
||||
input [3:0] s_axi_wstrb,
|
||||
input s_axi_wvalid,
|
||||
output s_axi_wready,
|
||||
output [1:0] s_axi_bresp,
|
||||
output s_axi_bvalid,
|
||||
input s_axi_bready,
|
||||
input [31:0] s_axi_araddr,
|
||||
input s_axi_arvalid,
|
||||
output s_axi_arready,
|
||||
output [31:0] s_axi_rdata,
|
||||
output [1:0] s_axi_rresp,
|
||||
output s_axi_rvalid,
|
||||
input s_axi_rready,
|
||||
|
||||
// RegPort interface, the out vs in
|
||||
// is seen from the slave device
|
||||
// hooked up to the regport
|
||||
output reg_port_in_rd,
|
||||
output reg_port_in_wt,
|
||||
output [RP_AWIDTH-1:0] reg_port_in_addr,
|
||||
output [RP_DWIDTH-1:0] reg_port_in_data,
|
||||
input [RP_DWIDTH-1:0] reg_port_out_data,
|
||||
input reg_port_out_ready
|
||||
);
|
||||
|
||||
localparam IDLE = 3'd0;
|
||||
localparam READ_INIT = 3'd1;
|
||||
localparam WRITE_INIT = 3'd2;
|
||||
localparam READ_IN_PROGRESS = 3'd3;
|
||||
localparam WRITE_IN_PROGRESS = 3'd4;
|
||||
localparam WRITE_DONE = 3'd5;
|
||||
localparam READ_DONE = 3'd6;
|
||||
|
||||
reg [RP_AWIDTH-1:0] addr;
|
||||
reg [RP_DWIDTH-1:0] rb_data;
|
||||
reg [RP_DWIDTH-1:0] wr_data;
|
||||
reg [2:0] state;
|
||||
reg [9:0] count;
|
||||
reg [1:0] rresp;
|
||||
reg [1:0] bresp;
|
||||
|
||||
always @ (posedge s_axi_aclk) begin
|
||||
if (s_axi_areset) begin
|
||||
state <= IDLE;
|
||||
addr <= 'd0;
|
||||
rb_data <= 'd0;
|
||||
wr_data <= 'd0;
|
||||
|
||||
count <= 10'd0;
|
||||
rresp <= 2'd0;
|
||||
bresp <= 2'd0;
|
||||
end
|
||||
else case (state)
|
||||
|
||||
IDLE: begin
|
||||
if (s_axi_arvalid) begin
|
||||
state <= READ_INIT;
|
||||
addr <= s_axi_araddr[RP_AWIDTH-1:0];
|
||||
end
|
||||
else if (s_axi_awvalid) begin
|
||||
state <= WRITE_INIT;
|
||||
addr <= s_axi_awaddr[RP_AWIDTH-1:0];
|
||||
end
|
||||
end
|
||||
|
||||
READ_INIT: begin
|
||||
state <= READ_IN_PROGRESS;
|
||||
count <= 10'd0;
|
||||
rresp <= 2'b00;
|
||||
end
|
||||
|
||||
READ_IN_PROGRESS: begin
|
||||
if (reg_port_out_ready) begin
|
||||
rb_data <= reg_port_out_data;
|
||||
state <= READ_DONE;
|
||||
end
|
||||
else if (count >= TIMEOUT) begin
|
||||
state <= READ_DONE;
|
||||
rresp <= 2'b10;
|
||||
end
|
||||
else begin
|
||||
count <= count + 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
READ_DONE: begin
|
||||
if (s_axi_rready) begin
|
||||
state <= IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
WRITE_INIT: begin
|
||||
if (s_axi_wvalid) begin
|
||||
wr_data <= s_axi_wdata[RP_DWIDTH-1:0];
|
||||
state <= WRITE_IN_PROGRESS;
|
||||
count <= 10'd0;
|
||||
bresp <= 2'b00;
|
||||
end
|
||||
end
|
||||
|
||||
WRITE_IN_PROGRESS: begin
|
||||
if (reg_port_out_ready) begin
|
||||
state <= WRITE_DONE;
|
||||
end
|
||||
else if (count >= TIMEOUT) begin
|
||||
state <= READ_DONE;
|
||||
bresp <= 2'b10;
|
||||
end
|
||||
else begin
|
||||
count <= count + 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
WRITE_DONE: begin
|
||||
if (s_axi_bready)
|
||||
state <= IDLE;
|
||||
end
|
||||
|
||||
default: begin
|
||||
state <= IDLE;
|
||||
end
|
||||
|
||||
endcase
|
||||
end
|
||||
|
||||
assign s_axi_awready = (state == IDLE);
|
||||
assign s_axi_wready = (state == WRITE_INIT);
|
||||
assign s_axi_bvalid = (state == WRITE_DONE);
|
||||
assign s_axi_bresp = bresp;
|
||||
|
||||
assign s_axi_arready = (state == IDLE);
|
||||
assign s_axi_rdata = rb_data;
|
||||
assign s_axi_rvalid = (state == READ_DONE);
|
||||
assign s_axi_rresp = rresp;
|
||||
|
||||
assign reg_port_in_wt = (state == WRITE_INIT) & s_axi_wvalid;
|
||||
assign reg_port_in_data = (state == WRITE_INIT) ? s_axi_wdata : wr_data;
|
||||
assign reg_port_in_addr = addr;
|
||||
|
||||
assign reg_port_in_rd = (state == READ_INIT);
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
|
||||
|
||||
module bin2gray
|
||||
#(parameter WIDTH=8)
|
||||
(input [WIDTH-1:0] bin,
|
||||
output [WIDTH-1:0] gray);
|
||||
|
||||
assign gray = (bin >> 1) ^ bin;
|
||||
|
||||
endmodule // bin2gray
|
||||
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// Copyright 2013 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
`define log2(N) ( N < 2 ? 0 : \
|
||||
N < 4 ? 1 : \
|
||||
N < 8 ? 2 : \
|
||||
N < 16 ? 3 : \
|
||||
N < 32 ? 4 : \
|
||||
N < 64 ? 5 : \
|
||||
N < 128 ? 6 : \
|
||||
N < 256 ? 7 : \
|
||||
N < 512 ? 8 : \
|
||||
N < 1024 ? 9 : \
|
||||
10 \
|
||||
)
|
||||
|
||||
module binary_encoder
|
||||
#(
|
||||
parameter SIZE = 16
|
||||
)
|
||||
(
|
||||
input [SIZE-1:0] in,
|
||||
output [`log2(SIZE)-1:0] out
|
||||
);
|
||||
|
||||
genvar m,n;
|
||||
|
||||
generate
|
||||
// Loop enough times to represent the total number of input bits as an encoded value
|
||||
for (m = 0; m <= `log2(SIZE-1); m = m + 1) begin: expand_or_tree
|
||||
wire [SIZE-1:0] encoding;
|
||||
// Build enable mask by iterating through every input bit.
|
||||
for (n = 0; n < SIZE ; n = n + 1) begin: encode_this_bit
|
||||
assign encoding[n] = n[m];
|
||||
end
|
||||
// OR tree for this output bit with appropriate bits enabled.
|
||||
assign out[m] = |(encoding & in);
|
||||
end
|
||||
endgenerate
|
||||
|
||||
endmodule // binary_encoder
|
||||
@@ -0,0 +1,148 @@
|
||||
//
|
||||
// Copyright 2016 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
module db_control #(
|
||||
// Drive SPI core with input spi_clk instead of ce_clk. This is useful if ce_clk is very slow which
|
||||
// would cause spi transactions to take a long time. WARNING: This adds a clock crossing FIFO!
|
||||
parameter USE_SPI_CLK = 0,
|
||||
parameter SR_BASE = 160,
|
||||
parameter RB_BASE = 16,
|
||||
parameter NUM_SPI_SEN = 8
|
||||
)(
|
||||
// Commands from Radio Core
|
||||
input clk, input reset,
|
||||
input set_stb, input [7:0] set_addr, input [31:0] set_data,
|
||||
output reg rb_stb, input [7:0] rb_addr, output reg [63:0] rb_data,
|
||||
input run_rx, input run_tx,
|
||||
// Frontend / Daughterboard I/O
|
||||
input [31:0] misc_ins, output [31:0] misc_outs,
|
||||
input [31:0] fp_gpio_in, output [31:0] fp_gpio_out, output [31:0] fp_gpio_ddr, input [31:0] fp_gpio_fab,
|
||||
input [31:0] db_gpio_in, output [31:0] db_gpio_out, output [31:0] db_gpio_ddr, input [31:0] db_gpio_fab,
|
||||
output [31:0] leds,
|
||||
input spi_clk, input spi_rst, output [NUM_SPI_SEN-1:0] sen, output sclk, output mosi, input miso
|
||||
);
|
||||
|
||||
localparam [7:0] SR_MISC_OUTS = SR_BASE + 8'd0;
|
||||
localparam [7:0] SR_SPI = SR_BASE + 8'd8;
|
||||
localparam [7:0] SR_LEDS = SR_BASE + 8'd16;
|
||||
localparam [7:0] SR_FP_GPIO = SR_BASE + 8'd24;
|
||||
localparam [7:0] SR_DB_GPIO = SR_BASE + 8'd32;
|
||||
|
||||
localparam [7:0] RB_MISC_IO = RB_BASE + 0;
|
||||
localparam [7:0] RB_SPI = RB_BASE + 1;
|
||||
localparam [7:0] RB_LEDS = RB_BASE + 2;
|
||||
localparam [7:0] RB_DB_GPIO = RB_BASE + 3;
|
||||
localparam [7:0] RB_FP_GPIO = RB_BASE + 4;
|
||||
|
||||
/********************************************************
|
||||
** Settings registers
|
||||
********************************************************/
|
||||
setting_reg #(.my_addr(SR_MISC_OUTS), .width(32)) sr_misc_outs (
|
||||
.clk(clk), .rst(reset),
|
||||
.strobe(set_stb), .addr(set_addr), .in(set_data),
|
||||
.out(misc_outs), .changed());
|
||||
|
||||
// Readback
|
||||
reg spi_readback_stb_hold;
|
||||
reg [31:0] spi_readback_hold;
|
||||
wire [31:0] spi_readback_sync;
|
||||
wire [31:0] fp_gpio_readback, db_gpio_readback;
|
||||
always @* begin
|
||||
case(rb_addr)
|
||||
// Use a latched spi readback stobe so additional readbacks after a SPI transaction will work
|
||||
RB_MISC_IO : {rb_stb, rb_data} <= {spi_readback_stb_hold, {misc_ins, misc_outs}};
|
||||
RB_SPI : {rb_stb, rb_data} <= {spi_readback_stb_hold, {32'd0, spi_readback_hold}};
|
||||
RB_LEDS : {rb_stb, rb_data} <= {spi_readback_stb_hold, {32'd0, leds}};
|
||||
RB_DB_GPIO : {rb_stb, rb_data} <= {spi_readback_stb_hold, {32'd0, db_gpio_readback}};
|
||||
RB_FP_GPIO : {rb_stb, rb_data} <= {spi_readback_stb_hold, {32'd0, fp_gpio_readback}};
|
||||
default : {rb_stb, rb_data} <= {spi_readback_stb_hold, {64'h0BADC0DE0BADC0DE}};
|
||||
endcase
|
||||
end
|
||||
|
||||
/********************************************************
|
||||
** GPIO
|
||||
********************************************************/
|
||||
gpio_atr #(.BASE(SR_LEDS), .WIDTH(32), .FAB_CTRL_EN(0), .DEFAULT_DDR(32'hFFFF_FFFF), .DEFAULT_IDLE(32'd0)) leds_gpio_atr (
|
||||
.clk(clk), .reset(reset),
|
||||
.set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
|
||||
.rx(run_rx), .tx(run_tx),
|
||||
.gpio_in(32'd0), .gpio_out(leds), .gpio_ddr(/*unused, assumed output only*/),
|
||||
.gpio_out_fab(32'h00000000 /*LEDs don't have fabric control*/), .gpio_sw_rb());
|
||||
|
||||
gpio_atr #(.BASE(SR_FP_GPIO), .WIDTH(32), .FAB_CTRL_EN(1), .DEFAULT_DDR(32'hFFFF_FFFF), .DEFAULT_IDLE(32'd0)) fp_gpio_atr (
|
||||
.clk(clk), .reset(reset),
|
||||
.set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
|
||||
.rx(run_rx), .tx(run_tx),
|
||||
.gpio_in(fp_gpio_in), .gpio_out(fp_gpio_out), .gpio_ddr(fp_gpio_ddr),
|
||||
.gpio_out_fab(fp_gpio_fab), .gpio_sw_rb(fp_gpio_readback));
|
||||
|
||||
gpio_atr #(.BASE(SR_DB_GPIO), .WIDTH(32), .FAB_CTRL_EN(1), .DEFAULT_DDR(32'hFFFF_FFFF), .DEFAULT_IDLE(32'd0)) db_gpio_atr (
|
||||
.clk(clk), .reset(reset),
|
||||
.set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
|
||||
.rx(run_rx), .tx(run_tx),
|
||||
.gpio_in(db_gpio_in), .gpio_out(db_gpio_out), .gpio_ddr(db_gpio_ddr),
|
||||
.gpio_out_fab(db_gpio_fab), .gpio_sw_rb(db_gpio_readback));
|
||||
|
||||
/********************************************************
|
||||
** SPI
|
||||
********************************************************/
|
||||
wire spi_set_stb;
|
||||
wire [7:0] spi_set_addr;
|
||||
wire [31:0] spi_set_data;
|
||||
wire spi_readback_stb, spi_readback_stb_sync;
|
||||
wire [31:0] spi_readback;
|
||||
wire spi_clk_int, spi_rst_int;
|
||||
generate
|
||||
if (USE_SPI_CLK) begin
|
||||
axi_fifo_2clk #(.WIDTH(8 + 32), .SIZE(0)) set_2clk_i (
|
||||
.reset(reset),
|
||||
.i_aclk(clk), .i_tdata({set_addr, set_data}), .i_tvalid(set_stb), .i_tready(),
|
||||
.o_aclk(spi_clk), .o_tdata({spi_set_addr, spi_set_data}), .o_tvalid(spi_set_stb), .o_tready(spi_set_stb));
|
||||
|
||||
axi_fifo_2clk #(.WIDTH(32), .SIZE(0)) rb_2clk_i (
|
||||
.reset(reset),
|
||||
.i_aclk(spi_clk), .i_tdata(spi_readback), .i_tvalid(spi_readback_stb), .i_tready(),
|
||||
.o_aclk(clk), .o_tdata(spi_readback_sync), .o_tvalid(spi_readback_stb_sync), .o_tready(spi_readback_stb_sync));
|
||||
|
||||
assign spi_clk_int = spi_clk;
|
||||
assign spi_rst_int = spi_rst;
|
||||
end else begin
|
||||
assign spi_set_stb = set_stb;
|
||||
assign spi_set_addr = set_addr;
|
||||
assign spi_set_data = set_data;
|
||||
assign spi_readback_stb_sync = spi_readback_stb;
|
||||
assign spi_readback_sync = spi_readback;
|
||||
assign spi_clk_int = clk;
|
||||
assign spi_rst_int = reset;
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// Need to latch spi_readback_stb in case of additional readbacks
|
||||
// after the initial spi transaction.
|
||||
always @(posedge clk) begin
|
||||
if (reset) begin
|
||||
spi_readback_stb_hold <= 1'b1;
|
||||
end else begin
|
||||
if (set_stb & (set_addr == SR_SPI+2 /* Trigger address */)) begin
|
||||
spi_readback_stb_hold <= 1'b0;
|
||||
end else if (spi_readback_stb_sync) begin
|
||||
spi_readback_hold <= spi_readback_sync;
|
||||
spi_readback_stb_hold <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// SPI Core instantiation
|
||||
// Note: We don't use "ready" because we use readback_stb to backpressure the settings bus
|
||||
simple_spi_core #(.BASE(SR_SPI), .WIDTH(NUM_SPI_SEN), .CLK_IDLE(0), .SEN_IDLE(8'hFF)) simple_spi_core (
|
||||
.clock(spi_clk_int), .reset(spi_rst_int),
|
||||
.set_stb(spi_set_stb), .set_addr(spi_set_addr), .set_data(spi_set_data),
|
||||
.readback(spi_readback), .readback_stb(spi_readback_stb), .ready(/* Unused */),
|
||||
.sen(sen), .sclk(sclk), .mosi(mosi), .miso(miso),
|
||||
.debug());
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,70 @@
|
||||
//
|
||||
// Copyright 2019 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: fe_control
|
||||
//
|
||||
// Description: Handle the front end control from the radio settings bus.
|
||||
// The module gets generated NUM_CHANNELS times to give independent control to
|
||||
// the individual channels.
|
||||
//
|
||||
|
||||
module fe_control #(
|
||||
parameter NUM_CHANNELS = 2,
|
||||
parameter [7:0] SR_FE_CHAN_OFFSET = 16,
|
||||
parameter [7:0] SR_TX_FE_BASE = 192,
|
||||
parameter [7:0] SR_RX_FE_BASE = 200
|
||||
)(
|
||||
input clk, input reset,
|
||||
// Commands from Radio Core
|
||||
input set_stb, input [7:0] set_addr, input [31:0] set_data,
|
||||
input time_sync,
|
||||
// Radio datapath
|
||||
input [NUM_CHANNELS-1:0] tx_stb, input [32*NUM_CHANNELS-1:0] tx_data_in, output [32*NUM_CHANNELS-1:0] tx_data_out,
|
||||
output [NUM_CHANNELS-1:0] rx_stb, input [32*NUM_CHANNELS-1:0] rx_data_in, output [32*NUM_CHANNELS-1:0] rx_data_out
|
||||
);
|
||||
|
||||
genvar i;
|
||||
generate for (i = 0; i < NUM_CHANNELS; i = i + 1)
|
||||
begin
|
||||
localparam SR_TX_OFFSET_I = SR_TX_FE_BASE + SR_FE_CHAN_OFFSET*i + 0;
|
||||
localparam SR_TX_OFFSET_Q = SR_TX_FE_BASE + SR_FE_CHAN_OFFSET*i + 1;
|
||||
localparam SR_TX_MAG_CORRECTION = SR_TX_FE_BASE + SR_FE_CHAN_OFFSET*i + 2;
|
||||
localparam SR_TX_PHASE_CORRECTION = SR_TX_FE_BASE + SR_FE_CHAN_OFFSET*i + 3;
|
||||
localparam SR_TX_MUX = SR_TX_FE_BASE + SR_FE_CHAN_OFFSET*i + 4;
|
||||
|
||||
tx_frontend_gen3 #(
|
||||
.SR_OFFSET_I(SR_TX_OFFSET_I), .SR_OFFSET_Q(SR_TX_OFFSET_Q),.SR_MAG_CORRECTION(SR_TX_MAG_CORRECTION),
|
||||
.SR_PHASE_CORRECTION(SR_TX_PHASE_CORRECTION), .SR_MUX(SR_TX_MUX),
|
||||
.BYPASS_DC_OFFSET_CORR(0), .BYPASS_IQ_COMP(0),
|
||||
.DEVICE("7SERIES")
|
||||
) tx_fe_corr_i (
|
||||
.clk(clk), .reset(reset),
|
||||
.set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
|
||||
.tx_stb(tx_stb[i]), .tx_i(tx_data_in[32+(32*i)-1:16+(32*i)]), .tx_q(tx_data_in[16+(32*i)-1:(32*i)]),
|
||||
.dac_stb(), .dac_i(tx_data_out[32+(32*i)-1:16+(32*i)]), .dac_q(tx_data_out[16+(32*i)-1:(32*i)])
|
||||
);
|
||||
|
||||
localparam SR_RX_MAG_CORRECTION = SR_RX_FE_BASE + SR_FE_CHAN_OFFSET*i + 0;
|
||||
localparam SR_RX_PHASE_CORRECTION = SR_RX_FE_BASE + SR_FE_CHAN_OFFSET*i + 1;
|
||||
localparam SR_RX_OFFSET_I = SR_RX_FE_BASE + SR_FE_CHAN_OFFSET*i + 2;
|
||||
localparam SR_RX_OFFSET_Q = SR_RX_FE_BASE + SR_FE_CHAN_OFFSET*i + 3;
|
||||
localparam SR_RX_IQ_MAPPING = SR_RX_FE_BASE + SR_FE_CHAN_OFFSET*i + 4;
|
||||
localparam SR_RX_HET_PHASE_INCR = SR_RX_FE_BASE + SR_FE_CHAN_OFFSET*i + 5;
|
||||
|
||||
rx_frontend_gen3 #(
|
||||
.SR_MAG_CORRECTION(SR_RX_MAG_CORRECTION), .SR_PHASE_CORRECTION(SR_RX_PHASE_CORRECTION), .SR_OFFSET_I(SR_RX_OFFSET_I),
|
||||
.SR_OFFSET_Q(SR_RX_OFFSET_Q), .SR_IQ_MAPPING(SR_RX_IQ_MAPPING), .SR_HET_PHASE_INCR(SR_RX_HET_PHASE_INCR),
|
||||
.BYPASS_DC_OFFSET_CORR(0), .BYPASS_IQ_COMP(0), .BYPASS_REALMODE_DSP(0),
|
||||
.DEVICE("7SERIES")
|
||||
) rx_fe_corr_i (
|
||||
.clk(clk), .reset(reset), .sync_in(time_sync),
|
||||
.set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
|
||||
.adc_stb(1'b1), .adc_i(rx_data_in[32+(32*i)-1:16+(32*i)]), .adc_q(rx_data_in[16+(32*i)-1:(32*i)]),
|
||||
.rx_stb(rx_stb[i]), .rx_i(rx_data_out[32+(32*i)-1:16+(32*i)]), .rx_q(rx_data_out[16+(32*i)-1:(32*i)])
|
||||
);
|
||||
end
|
||||
endgenerate
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// Copyright 2014 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Discard silently packets which don't match this SID
|
||||
|
||||
module filter_bad_sid
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
//
|
||||
input [64:0] i_tdata,
|
||||
input i_tvalid,
|
||||
output i_tready,
|
||||
//
|
||||
output [64:0] o_tdata,
|
||||
output o_tvalid,
|
||||
input o_tready,
|
||||
//
|
||||
output reg [15:0] count
|
||||
);
|
||||
|
||||
reg [1:0] state;
|
||||
wire good_sid;
|
||||
wire qualify_i_tvalid;
|
||||
|
||||
localparam IDLE = 0;
|
||||
localparam ACCEPT = 1;
|
||||
localparam DISCARD = 2;
|
||||
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
state <= IDLE;
|
||||
count <= 0;
|
||||
end else
|
||||
case(state)
|
||||
//
|
||||
IDLE: begin
|
||||
if (i_tvalid && i_tready)
|
||||
if (good_sid)
|
||||
state <= ACCEPT;
|
||||
else begin
|
||||
count <= count + 1;
|
||||
state <= DISCARD;
|
||||
end
|
||||
end
|
||||
//
|
||||
ACCEPT: begin
|
||||
if (i_tvalid && i_tready && i_tdata[64])
|
||||
state <= IDLE;
|
||||
end
|
||||
//
|
||||
DISCARD: begin
|
||||
if (i_tvalid && i_tready && i_tdata[64])
|
||||
state <= IDLE;
|
||||
end
|
||||
endcase // case(state)
|
||||
|
||||
assign good_sid = ((i_tdata[15:0] == 16'h00A0) || (i_tdata[15:0] == 16'h00B0));
|
||||
|
||||
assign qualify_i_tvalid = (state == IDLE) ? good_sid : ((state == DISCARD) ? 1'b0 : 1'b1);
|
||||
|
||||
//
|
||||
// Buffer output, break combinatorial timing paths
|
||||
//
|
||||
axi_fifo_short #(.WIDTH(65)) fifo_short
|
||||
(
|
||||
.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata(i_tdata), .i_tvalid(i_tvalid && qualify_i_tvalid), .i_tready(i_tready),
|
||||
.o_tdata(o_tdata), .o_tvalid(o_tvalid), .o_tready(o_tready),
|
||||
.space(), .occupied()
|
||||
);
|
||||
|
||||
endmodule // filter_bad_sid
|
||||
@@ -0,0 +1,98 @@
|
||||
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
module gpio_atr #(
|
||||
parameter BASE = 0,
|
||||
parameter WIDTH = 32,
|
||||
parameter FAB_CTRL_EN = 0,
|
||||
parameter DEFAULT_DDR = 0,
|
||||
parameter DEFAULT_IDLE = 0
|
||||
) (
|
||||
input clk, input reset, //Clock and reset
|
||||
input set_stb, input [7:0] set_addr, input [31:0] set_data, //Settings control interface
|
||||
input rx, input tx, //Run signals that indicate tx and rx operation
|
||||
input [WIDTH-1:0] gpio_in, //GPIO input state
|
||||
output reg [WIDTH-1:0] gpio_out, //GPIO output state
|
||||
output reg [WIDTH-1:0] gpio_ddr, //GPIO direction (0=input, 1=output)
|
||||
input [WIDTH-1:0] gpio_out_fab, //GPIO driver bus from fabric
|
||||
output reg [WIDTH-1:0] gpio_sw_rb //Readback value for software
|
||||
);
|
||||
genvar i;
|
||||
|
||||
wire [WIDTH-1:0] in_idle, in_tx, in_rx, in_fdx, ddr_reg, atr_disable, fabric_ctrl;
|
||||
reg [WIDTH-1:0] ogpio, igpio;
|
||||
|
||||
setting_reg #(.my_addr(BASE+0), .width(WIDTH), .at_reset(DEFAULT_IDLE)) reg_idle (
|
||||
.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
|
||||
.out(in_idle),.changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+1), .width(WIDTH)) reg_rx (
|
||||
.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
|
||||
.out(in_rx),.changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+2), .width(WIDTH)) reg_tx (
|
||||
.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
|
||||
.out(in_tx),.changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+3), .width(WIDTH)) reg_fdx (
|
||||
.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
|
||||
.out(in_fdx),.changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+4), .width(WIDTH), .at_reset(DEFAULT_DDR)) reg_ddr (
|
||||
.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
|
||||
.out(ddr_reg),.changed());
|
||||
|
||||
setting_reg #(.my_addr(BASE+5), .width(WIDTH)) reg_atr_disable (
|
||||
.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
|
||||
.out(atr_disable),.changed());
|
||||
|
||||
generate if (FAB_CTRL_EN == 1) begin
|
||||
setting_reg #(.my_addr(BASE+6), .width(WIDTH)) reg_fabric_ctrl (
|
||||
.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), .in(set_data),
|
||||
.out(fabric_ctrl),.changed());
|
||||
end else begin
|
||||
assign fabric_ctrl = {WIDTH{1'b0}};
|
||||
end endgenerate
|
||||
|
||||
//Pipeline rx and tx signals for easier timing closure
|
||||
reg rx_d, tx_d;
|
||||
always @(posedge clk)
|
||||
{rx_d, tx_d} <= {rx, tx};
|
||||
|
||||
generate for (i=0; i<WIDTH; i=i+1) begin: gpio_mux_gen
|
||||
//ATR selection MUX
|
||||
always @(posedge clk) begin
|
||||
case({atr_disable[i], tx_d, rx_d})
|
||||
3'b000: ogpio[i] <= in_idle[i];
|
||||
3'b001: ogpio[i] <= in_rx[i];
|
||||
3'b010: ogpio[i] <= in_tx[i];
|
||||
3'b011: ogpio[i] <= in_fdx[i];
|
||||
default: ogpio[i] <= in_idle[i]; //If ATR mode is disabled, always use IDLE value
|
||||
endcase
|
||||
end
|
||||
|
||||
//Pipeline input, output and direction
|
||||
//For fabric access, insert MUX as close to the IO as possible
|
||||
always @(posedge clk) begin
|
||||
gpio_out[i] <= fabric_ctrl[i] ? gpio_out_fab[i] : ogpio[i];
|
||||
end
|
||||
end endgenerate
|
||||
|
||||
always @(posedge clk)
|
||||
igpio <= gpio_in;
|
||||
|
||||
always @(posedge clk)
|
||||
gpio_ddr <= ddr_reg;
|
||||
|
||||
//Generate software readback state
|
||||
generate for (i=0; i<WIDTH; i=i+1) begin: gpio_rb_gen
|
||||
always @(posedge clk)
|
||||
gpio_sw_rb[i] <= gpio_ddr[i] ? gpio_out[i] : igpio[i];
|
||||
end endgenerate
|
||||
|
||||
endmodule // gpio_atr
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
//
|
||||
// Copyright 2015 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
module gpio_atr_io #(
|
||||
parameter WIDTH = 32
|
||||
) (
|
||||
input clk,
|
||||
input [WIDTH-1:0] gpio_ddr,
|
||||
input [WIDTH-1:0] gpio_out,
|
||||
output [WIDTH-1:0] gpio_in,
|
||||
inout [WIDTH-1:0] gpio_pins
|
||||
);
|
||||
|
||||
//Instantiate registers in the IOB
|
||||
(* IOB = "true" *) reg [WIDTH-1:0] gpio_in_iob, gpio_out_iob;
|
||||
always @(posedge clk) begin
|
||||
gpio_in_iob <= gpio_pins;
|
||||
gpio_out_iob <= gpio_out;
|
||||
end
|
||||
assign gpio_in = gpio_in_iob;
|
||||
|
||||
//Pipeline the data direction bus
|
||||
reg [WIDTH-1:0] gpio_ddr_reg;
|
||||
always @(posedge clk)
|
||||
gpio_ddr_reg <= gpio_ddr;
|
||||
|
||||
//Tristate buffers
|
||||
genvar i;
|
||||
generate for (i=0; i<WIDTH; i=i+1) begin: io_tristate_gen
|
||||
assign gpio_pins[i] = gpio_ddr_reg[i] ? gpio_out_iob[i] : 1'bz;
|
||||
end endgenerate
|
||||
|
||||
endmodule // gpio_atr_io
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Copyright 2016 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
//
|
||||
// Gray: {a,b,c,d}
|
||||
// Bits: {a,a^b,a^b^c,a^b^c^d}
|
||||
//
|
||||
module gray2bin #(
|
||||
parameter WIDTH = 8)
|
||||
(
|
||||
input [WIDTH-1:0] gray,
|
||||
output reg [WIDTH-1:0] bin
|
||||
);
|
||||
|
||||
integer i;
|
||||
always @(*) begin
|
||||
bin[WIDTH-1] = gray[WIDTH-1];
|
||||
for (i = WIDTH-2; i >= 0; i = i - 1) begin
|
||||
bin[i] = bin[i+1] ^ gray[i];
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,3 @@
|
||||
Contributions from:
|
||||
Ettus Research, A National Instruments Company
|
||||
Alex Forencich <alex@alexforencich.com>
|
||||
@@ -0,0 +1,206 @@
|
||||
//
|
||||
// Copyright 2018 Ettus Research, A National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: axis_muxed_kv_map
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// This module implements a memory that stores key and value (KV) pairs such
|
||||
// that the value can be looked up using the key (e.g., for a routing table).
|
||||
// This implementation uses AXI stream for both inserting key-value pairs and
|
||||
// for looking up a value by its key. It also supports multiple find/result
|
||||
// AXI streams, which share the same KV map internally.
|
||||
//
|
||||
// Values are inserted into the KV map using the axis_insert_* AXI stream. A
|
||||
// value can be looked up by its key using the axis_find_* AXI stream, in
|
||||
// which case the resulting value is output on the axis_result_* AXI stream.
|
||||
//
|
||||
// Ports:
|
||||
//
|
||||
// axis_insert_tdest : Key to insert into the KV map
|
||||
// axis_insert_tdata : Value to associate with the key in TDEST
|
||||
// axis_insert_tvalid : Standard AXI stream TVALID
|
||||
// axis_insert_tready : Standard AXI stream TREADY
|
||||
//
|
||||
// axis_find_tdata : Key to look up in the KV map
|
||||
// axis_find_tvalid : Standard AXI stream TVALID
|
||||
// axis_find_tready : Standard AXI stream TREADY
|
||||
//
|
||||
// axis_result_tdata : Value associated with key that was input on axis_find
|
||||
// axis_result_tkeep : Indicates if TDATA contains a valid value (i.e.,
|
||||
// TKEEP is 0 if the lookup fails to find a match)
|
||||
// axis_result_tvalid : Standard AXI stream TVALID
|
||||
// axis_result_tready : Standard AXI stream TREADY
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// KEY_WIDTH : Width of the key (axis_insert_tdest, axis_find_tdata)
|
||||
// VAL_WIDTH : Width of the value (axis_insert_tdata, axis_result_tdata)
|
||||
// SIZE : Size of the KV map (i.e., 2**SIZE key-value pairs)
|
||||
// NUM_PORTS : Number of AXI-Stream ports for the find and result interfaces
|
||||
//
|
||||
|
||||
module axis_muxed_kv_map #(
|
||||
parameter KEY_WIDTH = 16,
|
||||
parameter VAL_WIDTH = 32,
|
||||
parameter SIZE = 6,
|
||||
parameter NUM_PORTS = 4
|
||||
) (
|
||||
input wire clk,
|
||||
input wire reset,
|
||||
|
||||
input wire [KEY_WIDTH-1:0] axis_insert_tdest,
|
||||
input wire [VAL_WIDTH-1:0] axis_insert_tdata,
|
||||
input wire axis_insert_tvalid,
|
||||
output wire axis_insert_tready,
|
||||
|
||||
input wire [(KEY_WIDTH*NUM_PORTS)-1:0] axis_find_tdata,
|
||||
input wire [NUM_PORTS-1:0] axis_find_tvalid,
|
||||
output wire [NUM_PORTS-1:0] axis_find_tready,
|
||||
|
||||
output wire [(VAL_WIDTH*NUM_PORTS)-1:0] axis_result_tdata,
|
||||
output wire [NUM_PORTS-1:0] axis_result_tkeep,
|
||||
output wire [NUM_PORTS-1:0] axis_result_tvalid,
|
||||
input wire [NUM_PORTS-1:0] axis_result_tready
|
||||
);
|
||||
|
||||
localparam MUX_W = $clog2(NUM_PORTS) + KEY_WIDTH;
|
||||
localparam DEMUX_W = $clog2(NUM_PORTS) + VAL_WIDTH + 1;
|
||||
genvar i;
|
||||
|
||||
localparam [1:0] ST_IDLE = 2'd0;
|
||||
localparam [1:0] ST_REQUEST = 2'd1;
|
||||
localparam [1:0] ST_PENDING = 2'd2;
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Demux find ports
|
||||
//---------------------------------------------------------
|
||||
wire [KEY_WIDTH-1:0] find_key, find_key_reg;
|
||||
wire find_key_stb;
|
||||
wire [$clog2(NUM_PORTS)-1:0] find_dest, find_dest_reg;
|
||||
wire find_key_valid, find_key_valid_reg;
|
||||
wire find_ready;
|
||||
reg find_in_progress = 1'b0;
|
||||
wire insert_stb;
|
||||
wire insert_busy;
|
||||
wire find_res_stb;
|
||||
wire [VAL_WIDTH-1:0] find_res_val;
|
||||
wire find_res_match, find_res_ready;
|
||||
|
||||
wire [(MUX_W*NUM_PORTS)-1:0] mux_tdata;
|
||||
generate for (i = 0; i < NUM_PORTS; i = i + 1) begin : gen_mux_input
|
||||
assign mux_tdata[(MUX_W*i)+KEY_WIDTH-1:MUX_W*i] = axis_find_tdata[(KEY_WIDTH*i)+:KEY_WIDTH];
|
||||
assign mux_tdata[(MUX_W*(i+1))-1:(MUX_W*i)+KEY_WIDTH] = i;
|
||||
end endgenerate
|
||||
|
||||
axi_mux #(
|
||||
.WIDTH(KEY_WIDTH+$clog2(NUM_PORTS)), .SIZE(NUM_PORTS),
|
||||
.PRE_FIFO_SIZE(0), .POST_FIFO_SIZE($clog2(NUM_PORTS))
|
||||
) mux_i (
|
||||
.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata(mux_tdata), .i_tlast({NUM_PORTS{1'b1}}),
|
||||
.i_tvalid(axis_find_tvalid), .i_tready(axis_find_tready),
|
||||
.o_tdata({find_dest_reg, find_key_reg}), .o_tlast(),
|
||||
.o_tvalid(find_key_valid_reg), .o_tready(find_ready)
|
||||
);
|
||||
|
||||
axi_fifo #(
|
||||
.WIDTH(KEY_WIDTH+$clog2(NUM_PORTS)), .SIZE(1)
|
||||
) mux_reg_i (
|
||||
.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({find_dest_reg, find_key_reg}),
|
||||
.i_tvalid(find_key_valid_reg), .i_tready(find_ready),
|
||||
.o_tdata({find_dest, find_key}),
|
||||
.o_tvalid(find_key_valid), .o_tready(find_res_stb),
|
||||
.space(), .occupied()
|
||||
);
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (reset) begin
|
||||
find_in_progress <= 1'b0;
|
||||
end else begin
|
||||
if (find_key_stb) begin
|
||||
find_in_progress <= 1'b1;
|
||||
end else if (find_res_stb) begin
|
||||
find_in_progress <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// find_key_stb indicates when to begin a new KV map lookup. We must wait
|
||||
// until the output mux is ready before starting a lookup.
|
||||
assign find_key_stb = find_key_valid & find_res_ready & ~find_in_progress;
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Insert logic
|
||||
//---------------------------------------------------------
|
||||
reg [1:0] ins_state = ST_IDLE;
|
||||
always @(posedge clk) begin
|
||||
if (reset) begin
|
||||
ins_state <= ST_IDLE;
|
||||
end else begin
|
||||
case (ins_state)
|
||||
ST_IDLE:
|
||||
if (axis_insert_tvalid & ~insert_busy)
|
||||
ins_state <= ST_REQUEST;
|
||||
ST_REQUEST:
|
||||
ins_state <= ST_PENDING;
|
||||
ST_PENDING:
|
||||
if (~insert_busy)
|
||||
ins_state <= ST_IDLE;
|
||||
default:
|
||||
ins_state <= ST_IDLE;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
assign axis_insert_tready = axis_insert_tvalid & (ins_state == ST_PENDING) & ~insert_busy;
|
||||
assign insert_stb = axis_insert_tvalid & (ins_state == ST_REQUEST);
|
||||
|
||||
//---------------------------------------------------------
|
||||
// KV map instantiation
|
||||
//---------------------------------------------------------
|
||||
kv_map #(
|
||||
.KEY_WIDTH (KEY_WIDTH),
|
||||
.VAL_WIDTH (VAL_WIDTH),
|
||||
.SIZE (SIZE)
|
||||
) map_i (
|
||||
.clk (clk),
|
||||
.reset (reset),
|
||||
.insert_stb (insert_stb),
|
||||
.insert_key (axis_insert_tdest),
|
||||
.insert_val (axis_insert_tdata),
|
||||
.insert_busy (insert_busy),
|
||||
.find_key_stb (find_key_stb),
|
||||
.find_key (find_key),
|
||||
.find_res_stb (find_res_stb),
|
||||
.find_res_match (find_res_match),
|
||||
.find_res_val (find_res_val),
|
||||
.count (/* unused */)
|
||||
);
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Mux results port
|
||||
//---------------------------------------------------------
|
||||
wire [(DEMUX_W*NUM_PORTS)-1:0] demux_tdata;
|
||||
wire [DEMUX_W-1:0] hdr;
|
||||
axi_demux #(
|
||||
.WIDTH(DEMUX_W), .SIZE(NUM_PORTS),
|
||||
.PRE_FIFO_SIZE(1), .POST_FIFO_SIZE(0)
|
||||
) demux_i (
|
||||
.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.header(hdr), .dest(hdr[DEMUX_W-1:VAL_WIDTH+1]),
|
||||
.i_tdata({find_dest, find_res_match, find_res_val}), .i_tlast(1'b1),
|
||||
.i_tvalid(find_res_stb), .i_tready(find_res_ready),
|
||||
.o_tdata(demux_tdata), .o_tlast(),
|
||||
.o_tvalid(axis_result_tvalid), .o_tready(axis_result_tready)
|
||||
);
|
||||
|
||||
generate for (i = 0; i < NUM_PORTS; i = i + 1) begin : gen_result_output
|
||||
assign axis_result_tdata[(VAL_WIDTH*i)+:VAL_WIDTH] = demux_tdata[(DEMUX_W*i)+VAL_WIDTH-1:DEMUX_W*i];
|
||||
assign axis_result_tkeep[i] = demux_tdata[(DEMUX_W*i)+VAL_WIDTH];
|
||||
end endgenerate
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2015-2016 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* Content Addressable Memory
|
||||
*/
|
||||
module cam #(
|
||||
// search data bus width
|
||||
parameter DATA_WIDTH = 64,
|
||||
// memory size in log2(words)
|
||||
parameter ADDR_WIDTH = 5,
|
||||
// CAM style (SRL, BRAM)
|
||||
parameter CAM_STYLE = "SRL",
|
||||
// width of data bus slices
|
||||
parameter SLICE_WIDTH = 4
|
||||
)
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
input wire [ADDR_WIDTH-1:0] write_addr,
|
||||
input wire [DATA_WIDTH-1:0] write_data,
|
||||
input wire write_delete,
|
||||
input wire write_enable,
|
||||
output wire write_busy,
|
||||
|
||||
input wire [DATA_WIDTH-1:0] compare_data,
|
||||
output wire [2**ADDR_WIDTH-1:0] match_many,
|
||||
output wire [2**ADDR_WIDTH-1:0] match_single,
|
||||
output wire [ADDR_WIDTH-1:0] match_addr,
|
||||
output wire match
|
||||
);
|
||||
|
||||
generate
|
||||
if (CAM_STYLE == "SRL") begin
|
||||
cam_srl #(
|
||||
.DATA_WIDTH(DATA_WIDTH),
|
||||
.ADDR_WIDTH(ADDR_WIDTH),
|
||||
.SLICE_WIDTH(SLICE_WIDTH)
|
||||
)
|
||||
cam_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.write_addr(write_addr),
|
||||
.write_data(write_data),
|
||||
.write_delete(write_delete),
|
||||
.write_enable(write_enable),
|
||||
.write_busy(write_busy),
|
||||
.compare_data(compare_data),
|
||||
.match_many(match_many),
|
||||
.match_single(match_single),
|
||||
.match_addr(match_addr),
|
||||
.match(match)
|
||||
);
|
||||
end else if (CAM_STYLE == "BRAM") begin
|
||||
cam_bram #(
|
||||
.DATA_WIDTH(DATA_WIDTH),
|
||||
.ADDR_WIDTH(ADDR_WIDTH),
|
||||
.SLICE_WIDTH(SLICE_WIDTH)
|
||||
)
|
||||
cam_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.write_addr(write_addr),
|
||||
.write_data(write_data),
|
||||
.write_delete(write_delete),
|
||||
.write_enable(write_enable),
|
||||
.write_busy(write_busy),
|
||||
.compare_data(compare_data),
|
||||
.match_many(match_many),
|
||||
.match_single(match_single),
|
||||
.match_addr(match_addr),
|
||||
.match(match)
|
||||
);
|
||||
end
|
||||
endgenerate
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2015-2016 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* Content Addressable Memory (block RAM based)
|
||||
*/
|
||||
module cam_bram #(
|
||||
// search data bus width
|
||||
parameter DATA_WIDTH = 64,
|
||||
// memory size in log2(words)
|
||||
parameter ADDR_WIDTH = 5,
|
||||
// width of data bus slices
|
||||
parameter SLICE_WIDTH = 9
|
||||
)
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
input wire [ADDR_WIDTH-1:0] write_addr,
|
||||
input wire [DATA_WIDTH-1:0] write_data,
|
||||
input wire write_delete,
|
||||
input wire write_enable,
|
||||
output wire write_busy,
|
||||
|
||||
input wire [DATA_WIDTH-1:0] compare_data,
|
||||
output wire [2**ADDR_WIDTH-1:0] match_many,
|
||||
output wire [2**ADDR_WIDTH-1:0] match_single,
|
||||
output wire [ADDR_WIDTH-1:0] match_addr,
|
||||
output wire match
|
||||
);
|
||||
|
||||
// total number of slices (enough to cover DATA_WIDTH with address inputs)
|
||||
localparam SLICE_COUNT = (DATA_WIDTH + SLICE_WIDTH - 1) / SLICE_WIDTH;
|
||||
// depth of RAMs
|
||||
localparam RAM_DEPTH = 2**ADDR_WIDTH;
|
||||
|
||||
localparam [2:0]
|
||||
STATE_INIT = 3'd0,
|
||||
STATE_IDLE = 3'd1,
|
||||
STATE_DELETE_1 = 3'd2,
|
||||
STATE_DELETE_2 = 3'd3,
|
||||
STATE_WRITE_1 = 3'd4,
|
||||
STATE_WRITE_2 = 3'd5;
|
||||
|
||||
reg [2:0] state_reg = STATE_INIT, state_next;
|
||||
|
||||
wire [SLICE_COUNT*SLICE_WIDTH-1:0] compare_data_padded = {{SLICE_COUNT*SLICE_WIDTH-DATA_WIDTH{1'b0}}, compare_data};
|
||||
wire [SLICE_COUNT*SLICE_WIDTH-1:0] write_data_padded = {{SLICE_COUNT*SLICE_WIDTH-DATA_WIDTH{1'b0}}, write_data};
|
||||
|
||||
reg [SLICE_WIDTH-1:0] count_reg = {SLICE_WIDTH{1'b1}}, count_next;
|
||||
|
||||
reg [SLICE_COUNT*SLICE_WIDTH-1:0] ram_addr = {SLICE_COUNT*SLICE_WIDTH{1'b0}};
|
||||
reg [RAM_DEPTH-1:0] set_bit;
|
||||
reg [RAM_DEPTH-1:0] clear_bit;
|
||||
reg wr_en;
|
||||
|
||||
reg [ADDR_WIDTH-1:0] write_addr_reg = {ADDR_WIDTH{1'b0}}, write_addr_next;
|
||||
reg [SLICE_COUNT*SLICE_WIDTH-1:0] write_data_padded_reg = {SLICE_COUNT*SLICE_WIDTH{1'b0}}, write_data_padded_next;
|
||||
reg write_delete_reg = 1'b0, write_delete_next;
|
||||
|
||||
reg write_busy_reg = 1'b1;
|
||||
|
||||
assign write_busy = write_busy_reg;
|
||||
|
||||
reg [RAM_DEPTH-1:0] match_raw_out[SLICE_COUNT-1:0];
|
||||
reg [RAM_DEPTH-1:0] match_many_raw;
|
||||
|
||||
assign match_many = match_many_raw;
|
||||
|
||||
reg [DATA_WIDTH-1:0] erase_ram [RAM_DEPTH-1:0];
|
||||
reg [DATA_WIDTH-1:0] erase_data = {DATA_WIDTH{1'b0}};
|
||||
reg erase_ram_wr_en;
|
||||
|
||||
integer i;
|
||||
|
||||
initial begin
|
||||
for (i = 0; i < RAM_DEPTH; i = i + 1) begin
|
||||
erase_ram[i] = {SLICE_COUNT*SLICE_WIDTH{1'b0}};
|
||||
end
|
||||
end
|
||||
|
||||
integer k;
|
||||
|
||||
always @* begin
|
||||
match_many_raw = {RAM_DEPTH{1'b1}};
|
||||
for (k = 0; k < SLICE_COUNT; k = k + 1) begin
|
||||
match_many_raw = match_many_raw & match_raw_out[k];
|
||||
end
|
||||
end
|
||||
|
||||
cam_priority_encoder #(
|
||||
.WIDTH(RAM_DEPTH),
|
||||
.LSB_PRIORITY("HIGH")
|
||||
)
|
||||
priority_encoder_inst (
|
||||
.input_unencoded(match_many_raw),
|
||||
.output_valid(match),
|
||||
.output_encoded(match_addr),
|
||||
.output_unencoded(match_single)
|
||||
);
|
||||
|
||||
// BRAMs
|
||||
genvar slice_ind;
|
||||
generate
|
||||
for (slice_ind = 0; slice_ind < SLICE_COUNT; slice_ind = slice_ind + 1) begin : slice
|
||||
localparam W = slice_ind == SLICE_COUNT-1 ? DATA_WIDTH-SLICE_WIDTH*slice_ind : SLICE_WIDTH;
|
||||
|
||||
wire [RAM_DEPTH-1:0] match_data;
|
||||
wire [RAM_DEPTH-1:0] ram_data;
|
||||
|
||||
ram_2port #(
|
||||
.DWIDTH(RAM_DEPTH),
|
||||
.AWIDTH(W)
|
||||
)
|
||||
ram_inst
|
||||
(
|
||||
.clka(clk),
|
||||
.ena(1'b1),
|
||||
.wea(1'b0),
|
||||
.addra(compare_data[SLICE_WIDTH * slice_ind +: W]),
|
||||
.dia({RAM_DEPTH{1'b0}}),
|
||||
.doa(match_data),
|
||||
.clkb(clk),
|
||||
.enb(1'b1),
|
||||
.web(wr_en),
|
||||
.addrb(ram_addr[SLICE_WIDTH * slice_ind +: W]),
|
||||
.dib((ram_data & ~clear_bit) | set_bit),
|
||||
.dob(ram_data)
|
||||
);
|
||||
|
||||
always @* begin
|
||||
match_raw_out[slice_ind] <= match_data;
|
||||
end
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// erase
|
||||
always @(posedge clk) begin
|
||||
erase_data <= erase_ram[write_addr_next];
|
||||
if (erase_ram_wr_en) begin
|
||||
erase_data <= write_data_padded_reg;
|
||||
erase_ram[write_addr_next] <= write_data_padded_reg;
|
||||
end
|
||||
end
|
||||
|
||||
// write
|
||||
always @* begin
|
||||
state_next = STATE_IDLE;
|
||||
|
||||
count_next = count_reg;
|
||||
ram_addr = erase_data;
|
||||
set_bit = {RAM_DEPTH{1'b0}};
|
||||
clear_bit = {RAM_DEPTH{1'b0}};
|
||||
wr_en = 1'b0;
|
||||
|
||||
erase_ram_wr_en = 1'b0;
|
||||
|
||||
write_addr_next = write_addr_reg;
|
||||
write_data_padded_next = write_data_padded_reg;
|
||||
write_delete_next = write_delete_reg;
|
||||
|
||||
case (state_reg)
|
||||
STATE_INIT: begin
|
||||
// zero out RAMs
|
||||
ram_addr = {SLICE_COUNT{count_reg}} & {{SLICE_COUNT*SLICE_WIDTH-DATA_WIDTH{1'b0}}, {DATA_WIDTH{1'b1}}};
|
||||
set_bit = {RAM_DEPTH{1'b0}};
|
||||
clear_bit = {RAM_DEPTH{1'b1}};
|
||||
wr_en = 1'b1;
|
||||
|
||||
if (count_reg == 0) begin
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
count_next = count_reg - 1;
|
||||
state_next = STATE_INIT;
|
||||
end
|
||||
end
|
||||
STATE_IDLE: begin
|
||||
// idle state
|
||||
write_addr_next = write_addr;
|
||||
write_data_padded_next = write_data_padded;
|
||||
write_delete_next = write_delete;
|
||||
|
||||
if (write_enable) begin
|
||||
// wait for read from erase_ram
|
||||
state_next = STATE_DELETE_1;
|
||||
end else begin
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end
|
||||
STATE_DELETE_1: begin
|
||||
// wait for read
|
||||
state_next = STATE_DELETE_2;
|
||||
end
|
||||
STATE_DELETE_2: begin
|
||||
// clear bit and write back
|
||||
clear_bit = 1'b1 << write_addr;
|
||||
wr_en = 1'b1;
|
||||
if (write_delete_reg) begin
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
erase_ram_wr_en = 1'b1;
|
||||
state_next = STATE_WRITE_1;
|
||||
end
|
||||
end
|
||||
STATE_WRITE_1: begin
|
||||
// wait for read
|
||||
state_next = STATE_WRITE_2;
|
||||
end
|
||||
STATE_WRITE_2: begin
|
||||
// set bit and write back
|
||||
set_bit = 1'b1 << write_addr;
|
||||
wr_en = 1'b1;
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state_reg <= STATE_INIT;
|
||||
count_reg <= {SLICE_WIDTH{1'b1}};
|
||||
write_busy_reg <= 1'b1;
|
||||
end else begin
|
||||
state_reg <= state_next;
|
||||
count_reg <= count_next;
|
||||
write_busy_reg <= state_next != STATE_IDLE;
|
||||
end
|
||||
|
||||
write_addr_reg <= write_addr_next;
|
||||
write_data_padded_reg <= write_data_padded_next;
|
||||
write_delete_reg <= write_delete_next;
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014-2016 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* Priority encoder module
|
||||
*/
|
||||
module cam_priority_encoder #
|
||||
(
|
||||
parameter WIDTH = 4,
|
||||
// LSB priority: "LOW", "HIGH"
|
||||
parameter LSB_PRIORITY = "LOW"
|
||||
)
|
||||
(
|
||||
input wire [WIDTH-1:0] input_unencoded,
|
||||
output wire output_valid,
|
||||
output wire [$clog2(WIDTH)-1:0] output_encoded,
|
||||
output wire [WIDTH-1:0] output_unencoded
|
||||
);
|
||||
|
||||
// power-of-two width
|
||||
parameter W1 = 2**$clog2(WIDTH);
|
||||
parameter W2 = W1/2;
|
||||
|
||||
generate
|
||||
if (WIDTH == 2) begin
|
||||
// two inputs - just an OR gate
|
||||
assign output_valid = |input_unencoded;
|
||||
if (LSB_PRIORITY == "LOW") begin
|
||||
assign output_encoded = input_unencoded[1];
|
||||
end else begin
|
||||
assign output_encoded = ~input_unencoded[0];
|
||||
end
|
||||
end else begin
|
||||
// more than two inputs - split into two parts and recurse
|
||||
// also pad input to correct power-of-two width
|
||||
wire [$clog2(W2)-1:0] out1, out2;
|
||||
wire valid1, valid2;
|
||||
cam_priority_encoder #(
|
||||
.WIDTH(W2),
|
||||
.LSB_PRIORITY(LSB_PRIORITY)
|
||||
)
|
||||
priority_encoder_inst1 (
|
||||
.input_unencoded(input_unencoded[W2-1:0]),
|
||||
.output_valid(valid1),
|
||||
.output_encoded(out1)
|
||||
);
|
||||
cam_priority_encoder #(
|
||||
.WIDTH(W2),
|
||||
.LSB_PRIORITY(LSB_PRIORITY)
|
||||
)
|
||||
priority_encoder_inst2 (
|
||||
.input_unencoded({{W1-WIDTH{1'b0}}, input_unencoded[WIDTH-1:W2]}),
|
||||
.output_valid(valid2),
|
||||
.output_encoded(out2)
|
||||
);
|
||||
// multiplexer to select part
|
||||
assign output_valid = valid1 | valid2;
|
||||
if (LSB_PRIORITY == "LOW") begin
|
||||
assign output_encoded = valid2 ? {1'b1, out2} : {1'b0, out1};
|
||||
end else begin
|
||||
assign output_encoded = valid1 ? {1'b0, out1} : {1'b1, out2};
|
||||
end
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// unencoded output
|
||||
assign output_unencoded = 1 << output_encoded;
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2015-2016 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* Content Addressable Memory (shift register based)
|
||||
*/
|
||||
module cam_srl #(
|
||||
// search data bus width
|
||||
parameter DATA_WIDTH = 64,
|
||||
// memory size in log2(words)
|
||||
parameter ADDR_WIDTH = 5,
|
||||
// width of data bus slices (4 for SRL16, 5 for SRL32)
|
||||
parameter SLICE_WIDTH = 4
|
||||
)
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
input wire [ADDR_WIDTH-1:0] write_addr,
|
||||
input wire [DATA_WIDTH-1:0] write_data,
|
||||
input wire write_delete,
|
||||
input wire write_enable,
|
||||
output wire write_busy,
|
||||
|
||||
input wire [DATA_WIDTH-1:0] compare_data,
|
||||
output wire [2**ADDR_WIDTH-1:0] match_many,
|
||||
output wire [2**ADDR_WIDTH-1:0] match_single,
|
||||
output wire [ADDR_WIDTH-1:0] match_addr,
|
||||
output wire match
|
||||
);
|
||||
|
||||
// total number of slices (enough to cover DATA_WIDTH with address inputs)
|
||||
localparam SLICE_COUNT = (DATA_WIDTH + SLICE_WIDTH - 1) / SLICE_WIDTH;
|
||||
// depth of RAMs
|
||||
localparam RAM_DEPTH = 2**ADDR_WIDTH;
|
||||
|
||||
localparam [1:0]
|
||||
STATE_INIT = 2'd0,
|
||||
STATE_IDLE = 2'd1,
|
||||
STATE_WRITE = 2'd2,
|
||||
STATE_DELETE = 2'd3;
|
||||
|
||||
reg [1:0] state_reg = STATE_INIT, state_next;
|
||||
|
||||
wire [SLICE_COUNT*SLICE_WIDTH-1:0] compare_data_padded = {{SLICE_COUNT*SLICE_WIDTH-DATA_WIDTH{1'b0}}, compare_data};
|
||||
wire [SLICE_COUNT*SLICE_WIDTH-1:0] write_data_padded = {{SLICE_COUNT*SLICE_WIDTH-DATA_WIDTH{1'b0}}, write_data};
|
||||
|
||||
reg [SLICE_WIDTH-1:0] count_reg = {SLICE_WIDTH{1'b1}}, count_next;
|
||||
|
||||
reg [SLICE_COUNT-1:0] shift_data;
|
||||
reg [RAM_DEPTH-1:0] shift_en;
|
||||
|
||||
reg [ADDR_WIDTH-1:0] write_addr_reg = {ADDR_WIDTH{1'b0}}, write_addr_next;
|
||||
reg [SLICE_COUNT*SLICE_WIDTH-1:0] write_data_padded_reg = {SLICE_COUNT*SLICE_WIDTH{1'b0}}, write_data_padded_next;
|
||||
|
||||
reg write_busy_reg = 1'b1;
|
||||
|
||||
assign write_busy = write_busy_reg;
|
||||
|
||||
reg [RAM_DEPTH-1:0] match_raw_out[SLICE_COUNT-1:0];
|
||||
reg [RAM_DEPTH-1:0] match_many_raw;
|
||||
reg [RAM_DEPTH-1:0] match_many_reg = {RAM_DEPTH{1'b0}};
|
||||
|
||||
assign match_many = match_many_reg;
|
||||
|
||||
integer k;
|
||||
|
||||
always @* begin
|
||||
match_many_raw = ~shift_en;
|
||||
for (k = 0; k < SLICE_COUNT; k = k + 1) begin
|
||||
match_many_raw = match_many_raw & match_raw_out[k];
|
||||
end
|
||||
end
|
||||
|
||||
cam_priority_encoder #(
|
||||
.WIDTH(RAM_DEPTH),
|
||||
.LSB_PRIORITY("HIGH")
|
||||
)
|
||||
priority_encoder_inst (
|
||||
.input_unencoded(match_many_reg),
|
||||
.output_valid(match),
|
||||
.output_encoded(match_addr),
|
||||
.output_unencoded(match_single)
|
||||
);
|
||||
|
||||
integer i;
|
||||
|
||||
// SRLs
|
||||
genvar row_ind, slice_ind;
|
||||
generate
|
||||
for (row_ind = 0; row_ind < RAM_DEPTH; row_ind = row_ind + 1) begin : row
|
||||
for (slice_ind = 0; slice_ind < SLICE_COUNT; slice_ind = slice_ind + 1) begin : slice
|
||||
reg [2**SLICE_WIDTH-1:0] srl_mem = {2**SLICE_WIDTH{1'b0}};
|
||||
|
||||
// match
|
||||
always @* begin
|
||||
match_raw_out[slice_ind][row_ind] = srl_mem[compare_data_padded[SLICE_WIDTH * slice_ind +: SLICE_WIDTH]];
|
||||
end
|
||||
|
||||
// write
|
||||
always @(posedge clk) begin
|
||||
if (shift_en[row_ind]) begin
|
||||
srl_mem <= {srl_mem[2**SLICE_WIDTH-2:0], shift_data[slice_ind]};
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// match
|
||||
always @(posedge clk) begin
|
||||
match_many_reg <= match_many_raw;
|
||||
end
|
||||
|
||||
// write
|
||||
always @* begin
|
||||
state_next = STATE_IDLE;
|
||||
|
||||
count_next = count_reg;
|
||||
shift_data = {SLICE_COUNT{1'b0}};
|
||||
shift_en = {RAM_DEPTH{1'b0}};
|
||||
|
||||
write_addr_next = write_addr_reg;
|
||||
write_data_padded_next = write_data_padded_reg;
|
||||
|
||||
case (state_reg)
|
||||
STATE_INIT: begin
|
||||
// zero out shift registers
|
||||
shift_en = {RAM_DEPTH{1'b1}};
|
||||
shift_data = {SLICE_COUNT{1'b0}};
|
||||
|
||||
if (count_reg == 0) begin
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
count_next = count_reg - 1;
|
||||
state_next = STATE_INIT;
|
||||
end
|
||||
end
|
||||
STATE_IDLE: begin
|
||||
if (write_enable) begin
|
||||
write_addr_next = write_addr;
|
||||
write_data_padded_next = write_data_padded;
|
||||
count_next = {SLICE_WIDTH{1'b1}};
|
||||
if (write_delete) begin
|
||||
state_next = STATE_DELETE;
|
||||
end else begin
|
||||
state_next = STATE_WRITE;
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end
|
||||
STATE_WRITE: begin
|
||||
// write entry
|
||||
shift_en = 1'b1 << write_addr;
|
||||
|
||||
for (i = 0; i < SLICE_COUNT; i = i + 1) begin
|
||||
shift_data[i] = count_reg == write_data_padded_reg[SLICE_WIDTH * i +: SLICE_WIDTH];
|
||||
end
|
||||
|
||||
if (count_reg == 0) begin
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
count_next = count_reg - 1;
|
||||
state_next = STATE_WRITE;
|
||||
end
|
||||
end
|
||||
STATE_DELETE: begin
|
||||
// delete entry
|
||||
shift_en = 1'b1 << write_addr;
|
||||
shift_data = {SLICE_COUNT{1'b0}};
|
||||
|
||||
if (count_reg == 0) begin
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
count_next = count_reg - 1;
|
||||
state_next = STATE_DELETE;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state_reg <= STATE_INIT;
|
||||
count_reg <= {SLICE_WIDTH{1'b1}};
|
||||
write_busy_reg <= 1'b1;
|
||||
end else begin
|
||||
state_reg <= state_next;
|
||||
count_reg <= count_next;
|
||||
write_busy_reg <= state_next != STATE_IDLE;
|
||||
end
|
||||
|
||||
write_addr_reg <= write_addr_next;
|
||||
write_data_padded_reg <= write_data_padded_next;
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,253 @@
|
||||
//
|
||||
// Copyright 2018 Ettus Research, A National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: kv_map
|
||||
|
||||
module kv_map #(
|
||||
parameter KEY_WIDTH = 16,
|
||||
parameter VAL_WIDTH = 32,
|
||||
parameter SIZE = 6
|
||||
) (
|
||||
// Clock and reset
|
||||
input wire clk,
|
||||
input wire reset,
|
||||
// Insert port
|
||||
input wire insert_stb,
|
||||
input wire [KEY_WIDTH-1:0] insert_key,
|
||||
input wire [VAL_WIDTH-1:0] insert_val,
|
||||
output wire insert_busy,
|
||||
// Find port
|
||||
input wire find_key_stb,
|
||||
input wire [KEY_WIDTH-1:0] find_key,
|
||||
output wire find_res_stb,
|
||||
output wire find_res_match,
|
||||
output wire [VAL_WIDTH-1:0] find_res_val,
|
||||
// Count
|
||||
output reg [SIZE-1:0] count = {SIZE{1'b0}}
|
||||
);
|
||||
|
||||
//-------------------------------------------------
|
||||
// Instantiate a CAM and a RAM
|
||||
//-------------------------------------------------
|
||||
// The CAM serves as a "set" and the RAM serves as a
|
||||
// random addressable "array". Using thse two data structures
|
||||
// we can build a map. The role of the CAM is to compress
|
||||
// the key to an address that can be used to lookup data
|
||||
// stored in the RAM
|
||||
|
||||
wire cam_wr_en, cam_wr_busy, cam_rd_match;
|
||||
wire [SIZE-1:0] cam_wr_addr, cam_rd_addr;
|
||||
wire [KEY_WIDTH-1:0] cam_wr_data, cam_rd_key;
|
||||
|
||||
wire ram_wr_en;
|
||||
wire [SIZE-1:0] ram_wr_addr;
|
||||
reg [SIZE-1:0] ram_rd_addr;
|
||||
wire [VAL_WIDTH-1:0] ram_wr_data, ram_rd_data;
|
||||
|
||||
cam #(
|
||||
.DATA_WIDTH (KEY_WIDTH),
|
||||
.ADDR_WIDTH (SIZE),
|
||||
.CAM_STYLE (SIZE > 8 ? "BRAM" : "SRL"),
|
||||
.SLICE_WIDTH (SIZE > 8 ? 9 : 5)
|
||||
) cam_i (
|
||||
.clk (clk),
|
||||
.rst (reset),
|
||||
.write_addr (cam_wr_addr),
|
||||
.write_data (cam_wr_data),
|
||||
.write_delete(1'b0),
|
||||
.write_enable(cam_wr_en),
|
||||
.write_busy (cam_wr_busy),
|
||||
.compare_data(cam_rd_key),
|
||||
.match_addr (cam_rd_addr),
|
||||
.match (cam_rd_match),
|
||||
.match_many (),
|
||||
.match_single()
|
||||
);
|
||||
|
||||
ram_2port #(
|
||||
.DWIDTH(VAL_WIDTH),
|
||||
.AWIDTH(SIZE)
|
||||
) mem_i (
|
||||
.clka (clk),
|
||||
.ena (ram_wr_en),
|
||||
.wea (1'b1),
|
||||
.addra (ram_wr_addr),
|
||||
.dia (ram_wr_data),
|
||||
.doa (/* Write port only */),
|
||||
.clkb (clk),
|
||||
.enb (1'b1),
|
||||
.web (1'b0),
|
||||
.addrb (ram_rd_addr),
|
||||
.dib (/* Read port only */),
|
||||
.dob (ram_rd_data)
|
||||
);
|
||||
|
||||
// Pipeline read address into RAM
|
||||
always @(posedge clk)
|
||||
ram_rd_addr <= cam_rd_addr;
|
||||
|
||||
//-------------------------------------------------
|
||||
// Find state machine
|
||||
//-------------------------------------------------
|
||||
// The lookup process has three cycles of latency
|
||||
// - CAM lookup has a 1 cycle latency
|
||||
// - The lookup address into the RAM is delayed by 1 cycle for timing
|
||||
// - The RAM takes 1 cycle to produce data
|
||||
|
||||
localparam FIND_CYC = 3;
|
||||
|
||||
reg [FIND_CYC-1:0] find_key_stb_shreg = {FIND_CYC{1'b0}};
|
||||
reg [FIND_CYC-2:0] find_match_shreg = {(FIND_CYC-1){1'b0}};
|
||||
reg find_pending = 1'b0;
|
||||
|
||||
wire find_busy = find_pending | find_key_stb;
|
||||
|
||||
// Delay the find valid signal to account for the latency
|
||||
// of the CAM and RAM
|
||||
always @(posedge clk) begin
|
||||
find_key_stb_shreg <= reset ? {FIND_CYC{1'b0}} :
|
||||
{find_key_stb_shreg[FIND_CYC-2:0], find_key_stb};
|
||||
end
|
||||
assign find_res_stb = find_key_stb_shreg[FIND_CYC-1];
|
||||
|
||||
// Latch the find signal to compute pending
|
||||
always @(posedge clk) begin
|
||||
if (find_key_stb)
|
||||
find_pending <= 1'b1;
|
||||
else if (find_pending)
|
||||
find_pending <= ~find_res_stb;
|
||||
end
|
||||
|
||||
// Delay the match signal to account for the latency of the RAM
|
||||
always @(posedge clk) begin
|
||||
find_match_shreg <= reset ? {(FIND_CYC-1){1'b0}} :
|
||||
{find_match_shreg[FIND_CYC-3:0], cam_rd_match};
|
||||
end
|
||||
assign find_res_match = find_match_shreg[FIND_CYC-2];
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// Insert state machine
|
||||
//-------------------------------------------------
|
||||
|
||||
localparam [2:0] ST_IDLE = 3'd0;
|
||||
localparam [2:0] ST_WAIT_FIND = 3'd1;
|
||||
localparam [2:0] ST_CAM_READ = 3'd2;
|
||||
localparam [2:0] ST_CAM_CHECK_MATCH = 3'd3;
|
||||
localparam [2:0] ST_CAM_RAM_WRITE = 3'd4;
|
||||
localparam [2:0] ST_CAM_WRITE_WAIT = 3'd5;
|
||||
localparam [2:0] ST_RAM_WRITE = 3'd6;
|
||||
|
||||
reg [2:0] ins_state = ST_IDLE;
|
||||
|
||||
reg [KEY_WIDTH-1:0] ins_key_cached;
|
||||
reg [VAL_WIDTH-1:0] ins_val_cached;
|
||||
reg [SIZE-1:0] write_addr = {SIZE{1'b0}};
|
||||
reg [SIZE-1:0] next_addr = {SIZE{1'b0}};
|
||||
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (reset) begin
|
||||
ins_state <= ST_IDLE;
|
||||
next_addr <= {SIZE{1'b0}};
|
||||
end else begin
|
||||
case (ins_state)
|
||||
|
||||
// Idle and waiting for an insert transaction
|
||||
//
|
||||
ST_IDLE: begin
|
||||
// Cache insertion parameters
|
||||
if (insert_stb) begin
|
||||
ins_key_cached <= insert_key;
|
||||
ins_val_cached <= insert_val;
|
||||
// Wait for find to finish
|
||||
ins_state <= find_busy ? ST_WAIT_FIND : ST_CAM_READ;
|
||||
end
|
||||
end
|
||||
|
||||
// Wait for a find transaction to finish
|
||||
//
|
||||
ST_WAIT_FIND: begin
|
||||
// Wait for find to finish
|
||||
if (~find_busy)
|
||||
ins_state <= ST_CAM_READ;
|
||||
end
|
||||
|
||||
// Read the CAM to check if the key to insert already exists
|
||||
//
|
||||
ST_CAM_READ: begin
|
||||
// Ensure that find always has priority
|
||||
if (~find_key_stb)
|
||||
ins_state <= ST_CAM_CHECK_MATCH;
|
||||
end
|
||||
|
||||
// Look at the CAM match signal to evaluate if we skip writing the CAM
|
||||
//
|
||||
ST_CAM_CHECK_MATCH: begin
|
||||
// If the CAM already has this key, then overwrite it
|
||||
if (cam_rd_match) begin
|
||||
ins_state <= ST_RAM_WRITE;
|
||||
write_addr <= cam_rd_addr;
|
||||
end else if (~cam_wr_busy) begin
|
||||
ins_state <= ST_CAM_RAM_WRITE;
|
||||
write_addr <= next_addr;
|
||||
next_addr <= next_addr + 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
// Write the specified key to the CAM and value to the RAM
|
||||
//
|
||||
ST_CAM_RAM_WRITE: begin
|
||||
ins_state <= ST_CAM_WRITE_WAIT;
|
||||
end
|
||||
|
||||
// Wait for CAM write to finish
|
||||
//
|
||||
ST_CAM_WRITE_WAIT: begin
|
||||
if (~cam_wr_busy) begin
|
||||
ins_state <= ST_IDLE;
|
||||
count <= next_addr;
|
||||
end
|
||||
end
|
||||
|
||||
// Write the specified value to the RAM
|
||||
//
|
||||
ST_RAM_WRITE: begin
|
||||
ins_state <= ST_IDLE;
|
||||
count <= next_addr;
|
||||
end
|
||||
|
||||
default: begin
|
||||
// We should not get here
|
||||
ins_state <= ST_IDLE;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
// CAM Read Port:
|
||||
// - Find has priority so it can interrupt an insert
|
||||
assign cam_rd_key =
|
||||
(ins_state != ST_CAM_READ || find_key_stb) ? find_key : ins_key_cached;
|
||||
|
||||
// RAM Write Port:
|
||||
// - The RAM write enable is held high for 1 cycle
|
||||
// - The address may come from a CAM lookup or could generated
|
||||
assign ram_wr_en = (ins_state == ST_RAM_WRITE || ins_state == ST_CAM_RAM_WRITE);
|
||||
assign ram_wr_addr = write_addr;
|
||||
assign ram_wr_data = ins_val_cached;
|
||||
|
||||
// CAM Write Port:
|
||||
// - The CAM write enable is held high for 1 cycle
|
||||
// - The address may come from a CAM lookup or could generated (same as RAM)
|
||||
assign cam_wr_en = (ins_state == ST_CAM_RAM_WRITE);
|
||||
assign cam_wr_addr = write_addr;
|
||||
assign cam_wr_data = ins_key_cached;
|
||||
|
||||
// Outputs
|
||||
assign insert_busy = (ins_state != ST_IDLE);
|
||||
assign find_res_val = ram_rd_data;
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,772 @@
|
||||
//
|
||||
// Copyright 2016 Ettus Research
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
module mdio_master #(
|
||||
parameter REG_AWIDTH = 32,
|
||||
parameter REG_BASE = 'h0,
|
||||
parameter [7:0] MDC_DIVIDER = 8'd200
|
||||
) (
|
||||
// Clock and reset
|
||||
input clk,
|
||||
input rst,
|
||||
// MDIO ports
|
||||
output reg mdc,
|
||||
output reg mdio_out,
|
||||
output reg mdio_tri, // Assert to tristate driver.
|
||||
input mdio_in,
|
||||
// Register ports
|
||||
input reg_wr_req,
|
||||
input [REG_AWIDTH-1:0] reg_wr_addr,
|
||||
input [31:0] reg_wr_data,
|
||||
input reg_rd_req,
|
||||
input [REG_AWIDTH-1:0] reg_rd_addr,
|
||||
output reg reg_rd_resp,
|
||||
output reg [31:0] reg_rd_data
|
||||
);
|
||||
|
||||
localparam [7:0]
|
||||
IDLE = 0,
|
||||
PREAMBLE1 = 1,
|
||||
PREAMBLE2 = 2,
|
||||
PREAMBLE3 = 3,
|
||||
PREAMBLE4 = 4,
|
||||
PREAMBLE5 = 5,
|
||||
PREAMBLE6 = 6,
|
||||
PREAMBLE7 = 7,
|
||||
PREAMBLE8 = 8,
|
||||
PREAMBLE9 = 9,
|
||||
PREAMBLE10 = 10,
|
||||
PREAMBLE11 = 11,
|
||||
PREAMBLE12 = 12,
|
||||
PREAMBLE13 = 13,
|
||||
PREAMBLE14 = 14,
|
||||
PREAMBLE15 = 15,
|
||||
PREAMBLE16 = 16,
|
||||
PREAMBLE17 = 17,
|
||||
PREAMBLE18 = 18,
|
||||
PREAMBLE19 = 19,
|
||||
PREAMBLE20 = 20,
|
||||
PREAMBLE21 = 21,
|
||||
PREAMBLE22 = 22,
|
||||
PREAMBLE23 = 23,
|
||||
PREAMBLE24 = 24,
|
||||
PREAMBLE25 = 25,
|
||||
PREAMBLE26 = 26,
|
||||
PREAMBLE27 = 27,
|
||||
PREAMBLE28 = 28,
|
||||
PREAMBLE29 = 29,
|
||||
PREAMBLE30 = 30,
|
||||
PREAMBLE31 = 31,
|
||||
PREAMBLE32 = 32,
|
||||
START1 = 33,
|
||||
C22_START2 = 34,
|
||||
C45_START2 = 35,
|
||||
OP1 = 36,
|
||||
OP2 = 37,
|
||||
PRTAD1 = 38,
|
||||
PRTAD2 = 39,
|
||||
PRTAD3 = 40,
|
||||
PRTAD4 = 41,
|
||||
PRTAD5 = 42,
|
||||
DEVAD1 = 43,
|
||||
DEVAD2 = 44,
|
||||
DEVAD3 = 45,
|
||||
DEVAD4 = 46,
|
||||
DEVAD5 = 47,
|
||||
TA1 = 48,
|
||||
TA2 = 49,
|
||||
TA3 = 50,
|
||||
READ1 = 51,
|
||||
READ2 = 52,
|
||||
READ3 = 53,
|
||||
READ4 = 54,
|
||||
READ5 = 55,
|
||||
READ6 = 56,
|
||||
READ7 = 57,
|
||||
READ8 = 58,
|
||||
READ9 = 59,
|
||||
READ10 = 60,
|
||||
READ11 = 61,
|
||||
READ12 = 62,
|
||||
READ13 = 63,
|
||||
READ14 = 64,
|
||||
READ15 = 65,
|
||||
READ16 = 66,
|
||||
WRITE1 = 67,
|
||||
WRITE2 = 68,
|
||||
WRITE3 = 69,
|
||||
WRITE4 = 70,
|
||||
WRITE5 = 71,
|
||||
WRITE6 = 72,
|
||||
WRITE7 = 73,
|
||||
WRITE8 = 74,
|
||||
WRITE9 = 75,
|
||||
WRITE10 = 76,
|
||||
WRITE11 = 77,
|
||||
WRITE12 = 78,
|
||||
WRITE13 = 79,
|
||||
WRITE14 = 80,
|
||||
WRITE15 = 81,
|
||||
WRITE16 = 82,
|
||||
C45_ADDR1 = 83,
|
||||
C45_ADDR2 = 84,
|
||||
C45_ADDR3 = 85,
|
||||
C45_ADDR4 = 86,
|
||||
C45_ADDR5 = 87,
|
||||
C45_ADDR6 = 88,
|
||||
C45_ADDR7 = 89,
|
||||
C45_ADDR8 = 90,
|
||||
C45_ADDR9 = 91,
|
||||
C45_ADDR10 = 92,
|
||||
C45_ADDR11 = 93,
|
||||
C45_ADDR12 = 94,
|
||||
C45_ADDR13 = 95,
|
||||
C45_ADDR14 = 96,
|
||||
C45_ADDR15 = 97,
|
||||
C45_ADDR16 = 98,
|
||||
PREIDLE = 99
|
||||
;
|
||||
|
||||
localparam REG_MDIO_DATA = REG_BASE + 'h0;
|
||||
localparam REG_MDIO_ADDR = REG_BASE + 'h4;
|
||||
localparam REG_MDIO_OP = REG_BASE + 'h8;
|
||||
localparam REG_MDIO_CTRL_STATUS = REG_BASE + 'hC;
|
||||
|
||||
reg [15:0] mdio_read_data, mdio_write_data;
|
||||
reg [15:0] mdio_address;
|
||||
reg [12:0] mdio_operation;
|
||||
reg [7:0] mdc_clk_count;
|
||||
reg mdc_falling_edge;
|
||||
reg mdio_running;
|
||||
reg mdio_done;
|
||||
reg [7:0] state;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
mdio_write_data <= 16'h0;
|
||||
mdio_address <= 16'h0;
|
||||
mdio_operation <= 13'h0;
|
||||
mdio_running <= 1'b0;
|
||||
end else begin
|
||||
// Handshake to MDIO state machine to reset running flag in status.
|
||||
// Wait for falling MDC edge to prevent S/W race condition occuring
|
||||
// where done flag still asserted but running flag now cleared (repeatedly).
|
||||
if (mdio_done && mdc_falling_edge)
|
||||
mdio_running <= 1'b0;
|
||||
|
||||
// Readable registers
|
||||
if (reg_rd_req) begin
|
||||
reg_rd_resp <= 1'b1;
|
||||
case (reg_rd_addr)
|
||||
REG_MDIO_DATA:
|
||||
reg_rd_data <= {16'h0, mdio_read_data};
|
||||
REG_MDIO_ADDR:
|
||||
reg_rd_data <= {16'h0, mdio_address};
|
||||
REG_MDIO_OP:
|
||||
reg_rd_data <= {16'h0, mdio_operation};
|
||||
REG_MDIO_CTRL_STATUS:
|
||||
reg_rd_data <= {31'b0, mdio_running};
|
||||
default:
|
||||
reg_rd_resp <= 1'b0;
|
||||
endcase
|
||||
end else if (reg_rd_resp) begin
|
||||
reg_rd_resp <= 1'b0;
|
||||
end
|
||||
|
||||
// Writable registers
|
||||
if (reg_wr_req) begin
|
||||
case(reg_wr_addr)
|
||||
REG_MDIO_DATA:
|
||||
mdio_write_data <= reg_wr_data[15:0];
|
||||
REG_MDIO_ADDR:
|
||||
mdio_address <= reg_wr_data[15:0];
|
||||
REG_MDIO_OP:
|
||||
mdio_operation <= reg_wr_data[12:0];
|
||||
REG_MDIO_CTRL_STATUS:
|
||||
if (reg_wr_data[0])
|
||||
mdio_running <= 1'b1; // Trigger mdio operation here. Cleared by state machine at end of bus transaction.
|
||||
endcase
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
//
|
||||
// Produce mdc clock as a signal synchronously from Wishbone clock.
|
||||
//
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
mdc_clk_count <= 8'd1;
|
||||
mdc <= 1'b0;
|
||||
mdc_falling_edge <= 1'b0;
|
||||
end else if (mdc_clk_count == (MDC_DIVIDER/2)) begin
|
||||
mdc_clk_count <= 8'd1;
|
||||
mdc <= ~mdc;
|
||||
mdc_falling_edge <= mdc;
|
||||
end else begin
|
||||
mdc_clk_count <= mdc_clk_count + 8'd1;
|
||||
mdc_falling_edge <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
//
|
||||
// MDIO state machine
|
||||
//
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
mdio_tri <= 1'b1;
|
||||
mdio_out <= 1'b0;
|
||||
mdio_done <= 1'b0;
|
||||
mdio_read_data <= 16'b0;
|
||||
state <= IDLE;
|
||||
end else if (mdc_falling_edge) begin
|
||||
// This is the MDIO bus controller. Use falling edge of MDC.
|
||||
mdio_tri <= 1'b1;
|
||||
mdio_out <= 1'b0;
|
||||
mdio_done <= 1'b0;
|
||||
case(state)
|
||||
// IDLE.
|
||||
// In Clause 22 & 45 the master of the MDIO bus is tristate during idle.
|
||||
IDLE: begin
|
||||
mdio_tri <= 1'b1;
|
||||
mdio_out <= 1'b0;
|
||||
if (mdio_running)
|
||||
state <= PREAMBLE1;
|
||||
end
|
||||
// Preamble. All MDIO transactions begin witrh 32bits of 1 bits as a preamble.
|
||||
PREAMBLE1: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE2;
|
||||
end
|
||||
PREAMBLE2: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE3;
|
||||
end
|
||||
PREAMBLE3: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE4;
|
||||
end
|
||||
PREAMBLE4: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE5;
|
||||
end
|
||||
PREAMBLE5: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE6;
|
||||
end
|
||||
PREAMBLE6: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE7;
|
||||
end
|
||||
PREAMBLE7: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE8;
|
||||
end
|
||||
PREAMBLE8: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE9;
|
||||
end
|
||||
PREAMBLE9: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE10;
|
||||
end
|
||||
PREAMBLE10: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE11;
|
||||
end
|
||||
PREAMBLE11: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE12;
|
||||
end
|
||||
PREAMBLE12: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE13;
|
||||
end
|
||||
PREAMBLE13: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE14;
|
||||
end
|
||||
PREAMBLE14: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE15;
|
||||
end
|
||||
PREAMBLE15: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE16;
|
||||
end
|
||||
PREAMBLE16: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE17;
|
||||
end
|
||||
PREAMBLE17: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE18;
|
||||
end
|
||||
PREAMBLE18: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE19;
|
||||
end
|
||||
PREAMBLE19: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE20;
|
||||
end
|
||||
PREAMBLE20: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE21;
|
||||
end
|
||||
PREAMBLE21: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE22;
|
||||
end
|
||||
PREAMBLE22: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE23;
|
||||
end
|
||||
PREAMBLE23: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE24;
|
||||
end
|
||||
PREAMBLE24: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE25;
|
||||
end
|
||||
PREAMBLE25: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE26;
|
||||
end
|
||||
PREAMBLE26: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE27;
|
||||
end
|
||||
PREAMBLE27: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE28;
|
||||
end
|
||||
PREAMBLE28: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE29;
|
||||
end
|
||||
PREAMBLE29: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE30;
|
||||
end
|
||||
PREAMBLE30: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE31;
|
||||
end
|
||||
PREAMBLE31: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= PREAMBLE32;
|
||||
end
|
||||
PREAMBLE32: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= START1;
|
||||
end
|
||||
// Start code for Clause 22 is 01 and Clause 45 is 00
|
||||
START1: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b0;
|
||||
if (mdio_operation[12])
|
||||
// Clause 45 bit set.
|
||||
state <= C45_START2;
|
||||
else
|
||||
state <= C22_START2;
|
||||
end
|
||||
// 2nd Clause 22 start bit is a 1
|
||||
C22_START2: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= OP1;
|
||||
end
|
||||
// 2nd Clause 45 start bit is a 0
|
||||
C45_START2: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b0;
|
||||
state <= OP1;
|
||||
end
|
||||
// Both Clause 22 & 45 use 2 bits for operation and are compatable.
|
||||
// Note we don't screen here for illegal Clause 22 ops.
|
||||
OP1: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_operation[11];
|
||||
state <= OP2;
|
||||
end
|
||||
OP2: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_operation[10];
|
||||
state <= PRTAD1;
|
||||
end
|
||||
// Both Clause 22 & 45 use 2 sucsessive 5 bit fields to form a hierarchical address
|
||||
// though it's used slightly different between the 2 standards.
|
||||
PRTAD1: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_operation[9];
|
||||
state <= PRTAD2;
|
||||
end
|
||||
PRTAD2: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_operation[8];
|
||||
state <= PRTAD3;
|
||||
end
|
||||
PRTAD3: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_operation[7];
|
||||
state <= PRTAD4;
|
||||
end
|
||||
PRTAD4: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_operation[6];
|
||||
state <= PRTAD5;
|
||||
end
|
||||
PRTAD5: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_operation[5];
|
||||
state <= DEVAD1;
|
||||
end
|
||||
DEVAD1: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_operation[4];
|
||||
state <= DEVAD2;
|
||||
end
|
||||
DEVAD2: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_operation[3];
|
||||
state <= DEVAD3;
|
||||
end
|
||||
DEVAD3: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_operation[2];
|
||||
state <= DEVAD4;
|
||||
end
|
||||
DEVAD4: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_operation[1];
|
||||
state <= DEVAD5;
|
||||
end
|
||||
DEVAD5: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_operation[0];
|
||||
state <= TA1;
|
||||
end
|
||||
// Both Clause 22 & Clause 45 use the same turn around on the bus.
|
||||
// Reads have Z as the first bit and 0 driven by the slave for the 2nd bit.
|
||||
// Note that slaves drive the bus on the rising edge of MDC.
|
||||
// Writes and Address cycles have 10 driven by the master.
|
||||
TA1: begin
|
||||
if (mdio_operation[11] == 1'b0) // Write/Address
|
||||
begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b1;
|
||||
state <= TA2;
|
||||
end
|
||||
else // Read
|
||||
begin
|
||||
mdio_tri <= 1'b1;
|
||||
state <= TA3;
|
||||
end
|
||||
end
|
||||
TA2: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= 1'b0;
|
||||
if ( !mdio_operation[12]) // Clause 22 Write
|
||||
state <= WRITE1;
|
||||
else if (mdio_operation[10]) // Clause 45 Write
|
||||
state <= WRITE1;
|
||||
else // Clause 45 ADDRESS
|
||||
state <= C45_ADDR1;
|
||||
end
|
||||
TA3: begin
|
||||
mdio_tri <= 1'b1;
|
||||
state <= READ1;
|
||||
end
|
||||
// Clause 22 Reads and both forms of clause 45 Reads have the same bus transaction from here out.
|
||||
READ1: begin
|
||||
mdio_tri <= 1'b1;
|
||||
mdio_read_data[15] <= mdio_in;
|
||||
state <= READ2;
|
||||
end
|
||||
READ2: begin
|
||||
mdio_tri <= 1'b1;
|
||||
mdio_read_data[14] <= mdio_in;
|
||||
state <= READ3;
|
||||
end
|
||||
READ3: begin
|
||||
mdio_tri <= 1'b1;
|
||||
mdio_read_data[13] <= mdio_in;
|
||||
state <= READ4;
|
||||
end
|
||||
READ4: begin
|
||||
mdio_tri <= 1'b1;
|
||||
mdio_read_data[12] <= mdio_in;
|
||||
state <= READ5;
|
||||
end
|
||||
READ5: begin
|
||||
mdio_tri <= 1'b1;
|
||||
mdio_read_data[11] <= mdio_in;
|
||||
state <= READ6;
|
||||
end
|
||||
READ6: begin
|
||||
mdio_tri <= 1'b1;
|
||||
mdio_read_data[10] <= mdio_in;
|
||||
state <= READ7;
|
||||
end
|
||||
READ7: begin
|
||||
mdio_tri <= 1'b1;
|
||||
mdio_read_data[9] <= mdio_in;
|
||||
state <= READ8;
|
||||
end
|
||||
READ8: begin
|
||||
mdio_tri <= 1'b1;
|
||||
mdio_read_data[8] <= mdio_in;
|
||||
state <= READ9;
|
||||
end
|
||||
READ9: begin
|
||||
mdio_tri <= 1'b1;
|
||||
mdio_read_data[7] <= mdio_in;
|
||||
state <= READ10;
|
||||
end
|
||||
READ10: begin
|
||||
mdio_tri <= 1'b1;
|
||||
mdio_read_data[6] <= mdio_in;
|
||||
state <= READ11;
|
||||
end
|
||||
READ11: begin
|
||||
mdio_tri <= 1'b1;
|
||||
mdio_read_data[5] <= mdio_in;
|
||||
state <= READ12;
|
||||
end
|
||||
READ12: begin
|
||||
mdio_tri <= 1'b1;
|
||||
mdio_read_data[4] <= mdio_in;
|
||||
state <= READ13;
|
||||
end
|
||||
READ13: begin
|
||||
mdio_tri <= 1'b1;
|
||||
mdio_read_data[3] <= mdio_in;
|
||||
state <= READ14;
|
||||
end
|
||||
READ14: begin
|
||||
mdio_tri <= 1'b1;
|
||||
mdio_read_data[2] <= mdio_in;
|
||||
state <= READ15;
|
||||
end
|
||||
READ15: begin
|
||||
mdio_tri <= 1'b1;
|
||||
mdio_read_data[1] <= mdio_in;
|
||||
state <= READ16;
|
||||
end
|
||||
READ16: begin
|
||||
mdio_tri <= 1'b1;
|
||||
mdio_read_data[0] <= mdio_in;
|
||||
state <= PREIDLE;
|
||||
mdio_done <= 1'b1;
|
||||
end
|
||||
// Write 16bits of data for all types of Write.
|
||||
WRITE1: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_write_data[15];
|
||||
state <= WRITE2;
|
||||
end
|
||||
WRITE2: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_write_data[14];
|
||||
state <= WRITE3;
|
||||
end
|
||||
WRITE3: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_write_data[13];
|
||||
state <= WRITE4;
|
||||
end
|
||||
WRITE4: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_write_data[12];
|
||||
state <= WRITE5;
|
||||
end
|
||||
WRITE5: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_write_data[11];
|
||||
state <= WRITE6;
|
||||
end
|
||||
WRITE6: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_write_data[10];
|
||||
state <= WRITE7;
|
||||
end
|
||||
WRITE7: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_write_data[9];
|
||||
state <= WRITE8;
|
||||
end
|
||||
WRITE8: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_write_data[8];
|
||||
state <= WRITE9;
|
||||
end
|
||||
WRITE9: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_write_data[7];
|
||||
state <= WRITE10;
|
||||
end
|
||||
WRITE10: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_write_data[6];
|
||||
state <= WRITE11;
|
||||
end
|
||||
WRITE11: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_write_data[5];
|
||||
state <= WRITE12;
|
||||
end
|
||||
WRITE12: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_write_data[4];
|
||||
state <= WRITE13;
|
||||
end
|
||||
WRITE13: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_write_data[3];
|
||||
state <= WRITE14;
|
||||
end
|
||||
WRITE14: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_write_data[2];
|
||||
state <= WRITE15;
|
||||
end
|
||||
WRITE15: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_write_data[1];
|
||||
state <= WRITE16;
|
||||
end
|
||||
WRITE16: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_write_data[0];
|
||||
state <= PREIDLE;
|
||||
mdio_done <= 1'b1;
|
||||
end
|
||||
// Write 16bits of address for a Clause 45 Address transaction
|
||||
C45_ADDR1: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_address[15];
|
||||
state <= C45_ADDR2;
|
||||
end
|
||||
C45_ADDR2: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_address[14];
|
||||
state <= C45_ADDR3;
|
||||
end
|
||||
C45_ADDR3: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_address[13];
|
||||
state <= C45_ADDR4;
|
||||
end
|
||||
C45_ADDR4: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_address[12];
|
||||
state <= C45_ADDR5;
|
||||
end
|
||||
C45_ADDR5: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_address[11];
|
||||
state <= C45_ADDR6;
|
||||
end
|
||||
C45_ADDR6: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_address[10];
|
||||
state <= C45_ADDR7;
|
||||
end
|
||||
C45_ADDR7: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_address[9];
|
||||
state <= C45_ADDR8;
|
||||
end
|
||||
C45_ADDR8: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_address[8];
|
||||
state <= C45_ADDR9;
|
||||
end
|
||||
C45_ADDR9: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_address[7];
|
||||
state <= C45_ADDR10;
|
||||
end
|
||||
C45_ADDR10: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_address[6];
|
||||
state <= C45_ADDR11;
|
||||
end
|
||||
C45_ADDR11: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_address[5];
|
||||
state <= C45_ADDR12;
|
||||
end
|
||||
C45_ADDR12: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_address[4];
|
||||
state <= C45_ADDR13;
|
||||
end
|
||||
C45_ADDR13: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_address[3];
|
||||
state <= C45_ADDR14;
|
||||
end
|
||||
C45_ADDR14: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_address[2];
|
||||
state <= C45_ADDR15;
|
||||
end
|
||||
C45_ADDR15: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_address[1];
|
||||
state <= C45_ADDR16;
|
||||
end
|
||||
C45_ADDR16: begin
|
||||
mdio_tri <= 1'b0;
|
||||
mdio_out <= mdio_address[0];
|
||||
state <= PREIDLE;
|
||||
mdio_done <= 1'b1;
|
||||
end
|
||||
// PREIDLE allows the mdio_running bit to reset.
|
||||
PREIDLE: begin
|
||||
state <= IDLE;
|
||||
end
|
||||
endcase // case(state)
|
||||
end // if (mdc_falling_edge)
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Copyright 2013 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
|
||||
|
||||
module por_gen
|
||||
(input clk,
|
||||
output reset_out);
|
||||
|
||||
reg por_rst;
|
||||
reg [7:0] por_counter = 8'h0;
|
||||
|
||||
always @(posedge clk)
|
||||
if (por_counter != 8'h55)
|
||||
begin
|
||||
por_counter <= por_counter + 8'h1;
|
||||
por_rst <= 1'b1;
|
||||
end
|
||||
else
|
||||
por_rst <= 1'b0;
|
||||
|
||||
assign reset_out = por_rst;
|
||||
|
||||
endmodule // por_gen
|
||||
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// Copyright 2013 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
`define log2(N) ( N < 2 ? 0 : \
|
||||
N < 4 ? 1 : \
|
||||
N < 8 ? 2 : \
|
||||
N < 16 ? 3 : \
|
||||
N < 32 ? 4 : \
|
||||
N < 64 ? 5 : \
|
||||
N < 128 ? 6 : \
|
||||
N < 256 ? 7 : \
|
||||
N < 512 ? 8 : \
|
||||
N < 1024 ? 9 : \
|
||||
10 \
|
||||
)
|
||||
|
||||
module priority_encoder
|
||||
#(
|
||||
parameter WIDTH = 16
|
||||
)
|
||||
(
|
||||
input [WIDTH-1:0] in,
|
||||
output [`log2(WIDTH)-1:0] out
|
||||
);
|
||||
|
||||
wire [WIDTH-1:0] one_hot;
|
||||
|
||||
// the priority encoder spits out the position
|
||||
// of the leading bit as one hot coding
|
||||
priority_encoder_one_hot #
|
||||
(
|
||||
.WIDTH(WIDTH)
|
||||
)
|
||||
prio_one_hot0
|
||||
(
|
||||
.in(in),
|
||||
.out(one_hot)
|
||||
);
|
||||
|
||||
// binary encoder turns the one hot coding
|
||||
// into binary encoding
|
||||
binary_encoder #
|
||||
(
|
||||
.WIDTH(WIDTH)
|
||||
)
|
||||
binary_enc0
|
||||
(
|
||||
.in(one_hot),
|
||||
.out(out)
|
||||
);
|
||||
endmodule
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// Copyright 2013 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
module priority_encoder_one_hot
|
||||
#(
|
||||
parameter WIDTH = 16
|
||||
)
|
||||
(
|
||||
input [WIDTH-1:0] in,
|
||||
output [WIDTH-1:0] out
|
||||
);
|
||||
|
||||
wire [WIDTH-1:0] in_rev;
|
||||
wire [WIDTH-1:0] in_rev_inv_po = ~in_rev + 1;
|
||||
|
||||
wire [WIDTH-1:0] mask;
|
||||
|
||||
generate
|
||||
genvar i,j;
|
||||
|
||||
for (i=0; i<WIDTH; i=i+1)
|
||||
assign in_rev[i] = in[WIDTH-1-i];
|
||||
|
||||
for (j=0; j<WIDTH; j=j+1)
|
||||
assign mask[j] = in_rev_inv_po[WIDTH-1-j];
|
||||
endgenerate
|
||||
|
||||
assign out = in & mask;
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// Copyright 2017 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: pulse_stretch
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// Pulse stretcher. Takes any input pulse that is SCALE+2 clock cycles or
|
||||
// less and outputs a pulse that is SCALE+1 clock cycles. However, if an
|
||||
// input pulse is longer than SCALE+2 clock cycles then the output pulse
|
||||
// repeats. If more than one pulse is input within SCALE+2 clock cycles then
|
||||
// additional pulses will be ignored.
|
||||
//
|
||||
// Examples (SCALE = 2):
|
||||
//
|
||||
// Clock _/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_
|
||||
//
|
||||
//
|
||||
// pulse _/‾‾‾\_______________/‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\___________________
|
||||
//
|
||||
// pulse_stretched _____/‾‾‾‾‾‾‾‾‾‾‾\_______/‾‾‾‾‾‾‾‾‾‾‾\___________________
|
||||
//
|
||||
//
|
||||
// pulse _/‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\___________________________________
|
||||
//
|
||||
// pulse_stretched _____/‾‾‾‾‾‾‾‾‾‾‾\___/‾‾‾‾‾‾‾‾‾‾‾\_______________________
|
||||
//
|
||||
//
|
||||
// pulse _/‾‾‾\_______/‾‾‾\___/‾‾‾\_______/‾‾‾‾‾‾‾\_______________
|
||||
//
|
||||
// pulse_stretched _____/‾‾‾‾‾‾‾‾‾‾‾\_______/‾‾‾‾‾‾‾‾‾‾‾\___/‾‾‾‾‾‾‾‾‾‾‾\___
|
||||
|
||||
// Parameters:
|
||||
//
|
||||
// SCALE : The number of clock cycles to add to a single cycle pulse. Or, the
|
||||
// number of clock cycles, minus 1, for the output pulse.
|
||||
//
|
||||
|
||||
module pulse_stretch #(
|
||||
parameter SCALE = 64'd12_500_000
|
||||
)(
|
||||
input clk,
|
||||
input rst,
|
||||
input pulse,
|
||||
output pulse_stretched
|
||||
);
|
||||
|
||||
reg [$clog2(SCALE+1)-1:0] count = 'd0;
|
||||
reg state = 1'b0;
|
||||
|
||||
always @ (posedge clk)
|
||||
if (rst) begin
|
||||
state <= 1'b0;
|
||||
count <= 'd0;
|
||||
end
|
||||
else begin
|
||||
case (state)
|
||||
|
||||
1'b0: begin
|
||||
if (pulse) begin
|
||||
state <= 1'b1;
|
||||
count <= 'd0;
|
||||
end
|
||||
end
|
||||
|
||||
1'b1: begin
|
||||
if (count == SCALE)
|
||||
state <= 1'b0;
|
||||
else
|
||||
count <= count + 1'b1;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
assign pulse_stretched = (state == 1'b1);
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// Copyright 2017 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: pulse_stretch_min
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// Pulse stretcher, to guarantee a minimum pulse width. Takes a short input
|
||||
// pulse and outputs a pulse that is LENGTH clock cycles long. If the input
|
||||
// pulse is longer than LENGTH then the output pulse will be the same length
|
||||
// as the input pulse. The output is registered so the output is delayed by
|
||||
// one clock cycle relative to the input. If more than one pulse is input
|
||||
// within LENGTH+1 clock cycles, then the extra input pulses will not
|
||||
// generate output pulses.
|
||||
//
|
||||
// Examples: (LENGTH = 3)
|
||||
//
|
||||
// Clock _/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_
|
||||
//
|
||||
//
|
||||
// pulse_in _/‾‾‾\_______________/‾‾‾‾‾‾‾\___________/‾‾‾‾‾‾‾‾‾‾‾\_______
|
||||
//
|
||||
// pulse_out _____/‾‾‾‾‾‾‾‾‾‾‾\_______/‾‾‾‾‾‾‾‾‾‾‾\_______/‾‾‾‾‾‾‾‾‾‾‾\___
|
||||
//
|
||||
//
|
||||
// pulse_in _/‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\______________/‾‾‾\_______/‾‾‾\______
|
||||
//
|
||||
// pulse_out ______/‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\______________/‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\__
|
||||
//
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// LENGTH : Length of the minimum pulse to output, in clock cycles.
|
||||
//
|
||||
|
||||
module pulse_stretch_min #(
|
||||
parameter LENGTH = 4
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
input wire pulse_in,
|
||||
output reg pulse_out = 0
|
||||
);
|
||||
|
||||
reg [$clog2(LENGTH)-1:0] count = 0;
|
||||
reg state = 0;
|
||||
|
||||
always @ (posedge clk)
|
||||
if (rst) begin
|
||||
state <= 0;
|
||||
count <= 0;
|
||||
pulse_out <= 0;
|
||||
end
|
||||
else begin
|
||||
case (state)
|
||||
|
||||
1'b0: begin
|
||||
if (pulse_in) begin
|
||||
state <= 1;
|
||||
pulse_out <= 1;
|
||||
count <= 0;
|
||||
end
|
||||
end
|
||||
|
||||
1'b1: begin
|
||||
if (count == LENGTH-1) begin
|
||||
if (!pulse_in) begin
|
||||
state <= 0;
|
||||
pulse_out <= 0;
|
||||
end
|
||||
end else
|
||||
count <= count + 1;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,70 @@
|
||||
//
|
||||
// Copyright 2018 Ettus Research, A National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: pulse_synchronizer
|
||||
// Description:
|
||||
// - Synchronizes a single-cycle pulse or an edge from one
|
||||
// clock domain to another
|
||||
// - Clocks A and B can be asynchronous
|
||||
//
|
||||
|
||||
module pulse_synchronizer #(
|
||||
parameter MODE = "PULSE", // Capture mode {PULSE, POSEDGE, NEGEDGE}
|
||||
parameter STAGES = 2 // Number of synchronizer stages
|
||||
) (
|
||||
input clk_a, // Clock A
|
||||
input rst_a, // Reset in clock domain A
|
||||
input pulse_a, // Pulse in clock domain A to synchronize
|
||||
output busy_a, // Synchronizer is busy (pulse_a ignored when asserted)
|
||||
input clk_b, // Clock B
|
||||
output pulse_b // Pulse in clock domain B
|
||||
);
|
||||
// Trigger logic based on the capture mode
|
||||
wire trigger;
|
||||
generate if (MODE == "POSEDGE") begin
|
||||
reg pulse_a_del_pe = 1'b0;
|
||||
always @ (posedge clk_a)
|
||||
pulse_a_del_pe <= rst_a ? 1'b0 : pulse_a;
|
||||
assign trigger = pulse_a & ~pulse_a_del_pe;
|
||||
end else if (MODE == "NEGEDGE") begin
|
||||
reg pulse_a_del_ne = 1'b1;
|
||||
always @ (posedge clk_a)
|
||||
pulse_a_del_ne <= rst_a ? 1'b1 : pulse_a;
|
||||
assign trigger = ~pulse_a & pulse_a_del_ne;
|
||||
end else begin
|
||||
assign trigger = pulse_a;
|
||||
end endgenerate
|
||||
|
||||
// Translate pulse/edge to a level and synchronize that into the B domain
|
||||
reg pulse_toggle_a = 1'b0;
|
||||
always @(posedge clk_a) begin
|
||||
pulse_toggle_a <= rst_a ? 1'b0 : (pulse_toggle_a ^ (trigger & ~busy_a));
|
||||
end
|
||||
|
||||
wire pulse_toggle_b;
|
||||
reg pulse_toggle_b_del = 1'b0;
|
||||
wire handshake_toggle_a;
|
||||
|
||||
synchronizer #(
|
||||
.STAGES(STAGES), .INITIAL_VAL(0)
|
||||
) toggle_sync_i (
|
||||
.clk(clk_b), .rst(1'b0), .in(pulse_toggle_a), .out(pulse_toggle_b)
|
||||
);
|
||||
|
||||
// Handshake toggle signal back into the A domain to deassert busy
|
||||
synchronizer #(
|
||||
.STAGES(STAGES), .INITIAL_VAL(0)
|
||||
) handshake_sync_i (
|
||||
.clk(clk_a), .rst(1'b0), .in(pulse_toggle_b_del), .out(handshake_toggle_a)
|
||||
);
|
||||
|
||||
always @(posedge clk_b) begin
|
||||
pulse_toggle_b_del <= pulse_toggle_b;
|
||||
end
|
||||
|
||||
assign pulse_b = pulse_toggle_b_del ^ pulse_toggle_b;
|
||||
assign busy_a = pulse_toggle_a ^ handshake_toggle_a;
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,120 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Description
|
||||
// This code implements a parameterizable true dual port memory
|
||||
// (both ports can read and write). If an enable is not necessary
|
||||
// it may be tied off.
|
||||
//
|
||||
// Note
|
||||
// This module requires the ram_2port_impl.vh header file. The
|
||||
// header is included multiple times with different values of
|
||||
// the RAM_DIRECTIVE macro to create different implementations of the
|
||||
// RAM. An implementation is chosen in ram_2port based on the
|
||||
// user parameter for RAM_TYPE.
|
||||
|
||||
// Mode: AUTOMATIC
|
||||
`define RAM_DIRECTIVE
|
||||
`define RAM_MOD_NAME ram_2port_impl_auto
|
||||
`include "ram_2port_impl.vh"
|
||||
`undef RAM_MOD_NAME
|
||||
`undef RAM_DIRECTIVE
|
||||
|
||||
// Mode: REG
|
||||
`define RAM_DIRECTIVE (* ram_style = "registers" *)
|
||||
`define RAM_MOD_NAME ram_2port_impl_reg
|
||||
`include "ram_2port_impl.vh"
|
||||
`undef RAM_MOD_NAME
|
||||
`undef RAM_DIRECTIVE
|
||||
|
||||
// Mode: LUTRAM
|
||||
`define RAM_DIRECTIVE (* ram_style = "distributed" *)
|
||||
`define RAM_MOD_NAME ram_2port_impl_lutram
|
||||
`include "ram_2port_impl.vh"
|
||||
`undef RAM_MOD_NAME
|
||||
`undef RAM_DIRECTIVE
|
||||
|
||||
// Mode: BRAM
|
||||
`define RAM_DIRECTIVE (* ram_style = "block" *)
|
||||
`define RAM_MOD_NAME ram_2port_impl_bram
|
||||
`include "ram_2port_impl.vh"
|
||||
`undef RAM_MOD_NAME
|
||||
`undef RAM_DIRECTIVE
|
||||
|
||||
// Mode: URAM
|
||||
`define RAM_DIRECTIVE (* ram_style = "ultra" *)
|
||||
`define RAM_MOD_NAME ram_2port_impl_uram
|
||||
`include "ram_2port_impl.vh"
|
||||
`undef RAM_MOD_NAME
|
||||
`undef RAM_DIRECTIVE
|
||||
|
||||
module ram_2port #(
|
||||
parameter DWIDTH = 32, // Width of the memory block
|
||||
parameter AWIDTH = 9, // log2 of the depth of the memory block
|
||||
parameter RW_MODE = "READ-FIRST", // Read-write mode {READ-FIRST, WRITE-FIRST, NO-CHANGE}
|
||||
parameter RAM_TYPE = "AUTOMATIC", // Type of RAM to infer {AUTOMATIC, REG, LUTRAM, BRAM, URAM}
|
||||
parameter OUT_REG = 0, // Instantiate an output register? (+1 cycle of read latency)
|
||||
parameter INIT_FILE = "" // Optionally initialize memory with this file
|
||||
) (
|
||||
input wire clka,
|
||||
input wire ena,
|
||||
input wire wea,
|
||||
input wire [AWIDTH-1:0] addra,
|
||||
input wire [DWIDTH-1:0] dia,
|
||||
output wire [DWIDTH-1:0] doa,
|
||||
|
||||
input wire clkb,
|
||||
input wire enb,
|
||||
input wire web,
|
||||
input wire [AWIDTH-1:0] addrb,
|
||||
input wire [DWIDTH-1:0] dib,
|
||||
output wire [DWIDTH-1:0] dob
|
||||
);
|
||||
|
||||
generate
|
||||
if (RAM_TYPE == "URAM")
|
||||
ram_2port_impl_uram #(
|
||||
.DWIDTH(DWIDTH), .AWIDTH(AWIDTH), .RW_MODE(RW_MODE),
|
||||
.OUT_REG(OUT_REG), .INIT_FILE(INIT_FILE)
|
||||
) impl (
|
||||
.clka(clka), .ena(ena), .wea(wea), .addra(addra), .dia(dia), .doa(doa),
|
||||
.clkb(clkb), .enb(enb), .web(web), .addrb(addrb), .dib(dib), .dob(dob)
|
||||
);
|
||||
else if (RAM_TYPE == "BRAM")
|
||||
ram_2port_impl_bram #(
|
||||
.DWIDTH(DWIDTH), .AWIDTH(AWIDTH), .RW_MODE(RW_MODE),
|
||||
.OUT_REG(OUT_REG), .INIT_FILE(INIT_FILE)
|
||||
) impl (
|
||||
.clka(clka), .ena(ena), .wea(wea), .addra(addra), .dia(dia), .doa(doa),
|
||||
.clkb(clkb), .enb(enb), .web(web), .addrb(addrb), .dib(dib), .dob(dob)
|
||||
);
|
||||
else if (RAM_TYPE == "LUTRAM")
|
||||
ram_2port_impl_lutram #(
|
||||
.DWIDTH(DWIDTH), .AWIDTH(AWIDTH), .RW_MODE(RW_MODE),
|
||||
.OUT_REG(OUT_REG), .INIT_FILE(INIT_FILE)
|
||||
) impl (
|
||||
.clka(clka), .ena(ena), .wea(wea), .addra(addra), .dia(dia), .doa(doa),
|
||||
.clkb(clkb), .enb(enb), .web(web), .addrb(addrb), .dib(dib), .dob(dob)
|
||||
);
|
||||
else if (RAM_TYPE == "REG")
|
||||
ram_2port_impl_reg #(
|
||||
.DWIDTH(DWIDTH), .AWIDTH(AWIDTH), .RW_MODE(RW_MODE),
|
||||
.OUT_REG(OUT_REG), .INIT_FILE(INIT_FILE)
|
||||
) impl (
|
||||
.clka(clka), .ena(ena), .wea(wea), .addra(addra), .dia(dia), .doa(doa),
|
||||
.clkb(clkb), .enb(enb), .web(web), .addrb(addrb), .dib(dib), .dob(dob)
|
||||
);
|
||||
else
|
||||
ram_2port_impl_auto #(
|
||||
.DWIDTH(DWIDTH), .AWIDTH(AWIDTH), .RW_MODE(RW_MODE),
|
||||
.OUT_REG(OUT_REG), .INIT_FILE(INIT_FILE)
|
||||
) impl (
|
||||
.clka(clka), .ena(ena), .wea(wea), .addra(addra), .dia(dia), .doa(doa),
|
||||
.clkb(clkb), .enb(enb), .web(web), .addrb(addrb), .dib(dib), .dob(dob)
|
||||
);
|
||||
endgenerate
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,129 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Used by ram_2port.v
|
||||
// Requires `RAM_MOD_NAME and `RAM_DIRECTIVE to be defined
|
||||
|
||||
module `RAM_MOD_NAME #(
|
||||
parameter DWIDTH = 32, // Width of the memory block
|
||||
parameter AWIDTH = 9, // log2 of the depth of the memory block
|
||||
parameter RW_MODE = "READ-FIRST", // Read-write mode {READ-FIRST, WRITE-FIRST, NO-CHANGE}
|
||||
parameter OUT_REG = 0, // Instantiate an output register? (+1 cycle of read latency)
|
||||
parameter INIT_FILE = "" // Optionally initialize memory with this file
|
||||
) (
|
||||
input wire clka,
|
||||
input wire ena,
|
||||
input wire wea,
|
||||
input wire [AWIDTH-1:0] addra,
|
||||
input wire [DWIDTH-1:0] dia,
|
||||
output wire [DWIDTH-1:0] doa,
|
||||
|
||||
input wire clkb,
|
||||
input wire enb,
|
||||
input wire web,
|
||||
input wire [AWIDTH-1:0] addrb,
|
||||
input wire [DWIDTH-1:0] dib,
|
||||
output wire [DWIDTH-1:0] dob
|
||||
);
|
||||
|
||||
`RAM_DIRECTIVE reg [DWIDTH-1:0] ram [(1<<AWIDTH)-1:0];
|
||||
|
||||
// Initialize ram to a specified file or to all zeros to match hardware
|
||||
generate if (INIT_FILE != "") begin
|
||||
initial
|
||||
$readmemh(INIT_FILE, ram, 0, (1<<AWIDTH)-1);
|
||||
end else begin
|
||||
integer i;
|
||||
initial
|
||||
for (i = 0; i < (1<<AWIDTH); i = i + 1)
|
||||
ram[i] = {DWIDTH{1'b0}};
|
||||
end endgenerate
|
||||
|
||||
reg [DWIDTH-1:0] doa_r = 'h0, dob_r = 'h0;
|
||||
generate if (OUT_REG == 1) begin
|
||||
// A 2 clock cycle read latency with improve clock-to-out timing
|
||||
reg [DWIDTH-1:0] doa_rr = 'h0, dob_rr = 'h0;
|
||||
|
||||
always @(posedge clka)
|
||||
if (ena)
|
||||
doa_rr <= doa_r;
|
||||
|
||||
always @(posedge clkb)
|
||||
if (enb)
|
||||
dob_rr <= dob_r;
|
||||
|
||||
assign doa = doa_rr;
|
||||
assign dob = dob_rr;
|
||||
end else begin
|
||||
// A 1 clock cycle read latency at the cost of a longer clock-to-out timing
|
||||
assign doa = doa_r;
|
||||
assign dob = dob_r;
|
||||
end endgenerate
|
||||
|
||||
generate if (RW_MODE == "READ-FIRST") begin
|
||||
// When data is written, the prior memory contents at the write
|
||||
// address are presented on the output port.
|
||||
always @(posedge clka) begin
|
||||
if (ena) begin
|
||||
if (wea)
|
||||
ram[addra] <= dia;
|
||||
doa_r <= ram[addra];
|
||||
end
|
||||
end
|
||||
always @(posedge clkb) begin
|
||||
if (enb) begin
|
||||
if (web)
|
||||
ram[addrb] <= dib;
|
||||
dob_r <= ram[addrb];
|
||||
end
|
||||
end
|
||||
|
||||
end else if (RW_MODE == "WRITE-FIRST") begin
|
||||
// The data being written to the RAM also resides on the output port.
|
||||
always @(posedge clka) begin
|
||||
if (ena) begin
|
||||
if (wea) begin
|
||||
ram[addra] <= dia;
|
||||
doa_r <= dia;
|
||||
end else begin
|
||||
doa_r <= ram[addra];
|
||||
end
|
||||
end
|
||||
end
|
||||
always @(posedge clkb) begin
|
||||
if (enb) begin
|
||||
if (web) begin
|
||||
ram[addrb] <= dib;
|
||||
dob_r <= dib;
|
||||
end else begin
|
||||
dob_r <= ram[addrb];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end else begin
|
||||
// This is a no change RAM which retains the last read value on the output during writes
|
||||
// which is the most power efficient mode.
|
||||
always @(posedge clka) begin
|
||||
if (ena) begin
|
||||
if (wea)
|
||||
ram[addra] <= dia;
|
||||
else
|
||||
doa_r <= ram[addra];
|
||||
end
|
||||
end
|
||||
always @(posedge clkb) begin
|
||||
if (enb) begin
|
||||
if (web)
|
||||
ram[addrb] <= dib;
|
||||
else
|
||||
dob_r <= ram[addrb];
|
||||
end
|
||||
end
|
||||
|
||||
end endgenerate
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// Copyright 2016 Ettus Research
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
module regport_resp_mux #(
|
||||
parameter WIDTH = 32,
|
||||
parameter NUM_SLAVES = 2
|
||||
)(
|
||||
input clk,
|
||||
input reset,
|
||||
|
||||
input [NUM_SLAVES-1:0] sla_rd_resp,
|
||||
input [(NUM_SLAVES*WIDTH)-1:0] sla_rd_data,
|
||||
|
||||
output reg mst_rd_resp,
|
||||
output reg [WIDTH-1:0] mst_rd_data
|
||||
);
|
||||
// Treat sla_rd_resp as a one-hot bus.
|
||||
// If multiple resp lines are asserted at the same time, then
|
||||
// it is a violation of the register port protocol
|
||||
|
||||
wire [NUM_SLAVES-1:0] bit_options[0:WIDTH-1];
|
||||
wire [WIDTH-1:0] data_out;
|
||||
|
||||
genvar i, b;
|
||||
generate
|
||||
for (b = 0; b < WIDTH; b = b + 1) begin
|
||||
for (i = 0; i < NUM_SLAVES; i = i + 1) begin
|
||||
assign bit_options[b][i] = sla_rd_data[(i*WIDTH)+b];
|
||||
end
|
||||
assign data_out[b] = |(bit_options[b] & sla_rd_resp);
|
||||
end
|
||||
endgenerate
|
||||
|
||||
always @(posedge clk) begin
|
||||
mst_rd_data <= data_out;
|
||||
if (reset)
|
||||
mst_rd_resp <= 1'b0;
|
||||
else
|
||||
mst_rd_resp <= |(sla_rd_resp);
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,63 @@
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2017 Ettus Research, A National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: regport_to_settingsbus
|
||||
// Description:
|
||||
// Converts regport write bus to the a setting bus
|
||||
// ADDRESSING: Set to "WORD" in case of settings bus. The settings bus
|
||||
// uses word addressing and hence the address needs to be shifted by
|
||||
// to convert to set_addr.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
module regport_to_settingsbus #(
|
||||
parameter BASE = 14'h0,
|
||||
parameter END_ADDR = 14'h3FFF,
|
||||
parameter DWIDTH = 32,
|
||||
parameter AWIDTH = 14,
|
||||
parameter SR_AWIDTH = 12,
|
||||
// Dealign for settings bus by shifting by 2
|
||||
parameter ADDRESSING = "WORD",
|
||||
parameter SHIFT = $clog2(DWIDTH/8)
|
||||
)(
|
||||
input reset,
|
||||
input clk,
|
||||
input reg_wr_req,
|
||||
input [AWIDTH-1:0] reg_wr_addr,
|
||||
input [DWIDTH-1:0] reg_wr_data,
|
||||
|
||||
output reg set_stb,
|
||||
output reg [SR_AWIDTH-1:0] set_addr,
|
||||
output reg [DWIDTH-1:0] set_data
|
||||
);
|
||||
|
||||
wire set_stb_int;
|
||||
wire [DWIDTH-1:0] set_data_int;
|
||||
wire [SR_AWIDTH-1:0] set_addr_base;
|
||||
wire [SR_AWIDTH-1:0] set_addr_int;
|
||||
|
||||
// Strobe asserted only when address is between BASE and END ADDR
|
||||
assign set_stb_int = reg_wr_req && (reg_wr_addr >= BASE) && (reg_wr_addr <= END_ADDR);
|
||||
assign set_addr_base = reg_wr_addr - BASE;
|
||||
// Shift by 2 in case of setting bus
|
||||
assign set_addr_int = (ADDRESSING == "WORD") ? {{SHIFT{1'b0}}, set_addr_base[SR_AWIDTH-1:SHIFT]}
|
||||
: set_addr_base[SR_AWIDTH-1:0];
|
||||
assign set_data_int = reg_wr_data;
|
||||
|
||||
// Adding a pipeline stage
|
||||
always @(posedge clk) begin
|
||||
if (reset) begin
|
||||
set_stb <= 'b0;
|
||||
set_addr <= 'h0;
|
||||
set_data <= 'h0;
|
||||
end else begin
|
||||
set_stb <= set_stb_int;
|
||||
set_addr <= set_addr_int;
|
||||
set_data <= set_data_int;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule // regport_to_settingsbus
|
||||
@@ -0,0 +1,113 @@
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2017 Ettus Research, A National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: regport_to_xbar_settingsbus
|
||||
// Description:
|
||||
// Converts regport to xbar setting bus.
|
||||
// The module is designed only for the crossbar. The readback bus for the
|
||||
// rfnoc crossbar reads from the same address as it writes to. Also
|
||||
// there is an extra cycle delay in read data in the crossbar, which is
|
||||
// why the rb_stb needs to be delayed by a cycle.
|
||||
//
|
||||
// ADDRESSING: Set to "WORD" in case of settings bus. The settings bus
|
||||
// uses word addressing and hence the address needs to be shifted by
|
||||
// to convert to set_addr.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
module regport_to_xbar_settingsbus #(
|
||||
parameter BASE = 14'h0,
|
||||
parameter END_ADDR = 14'h3FFF,
|
||||
parameter DWIDTH = 32,
|
||||
parameter AWIDTH = 14,
|
||||
parameter SR_AWIDTH = 12,
|
||||
// Dealign for settings bus by shifting by 2
|
||||
parameter ADDRESSING = "WORD",
|
||||
parameter SHIFT = $clog2(DWIDTH/8)
|
||||
)(
|
||||
input clk,
|
||||
input reset,
|
||||
|
||||
input reg_wr_req,
|
||||
input [AWIDTH-1:0] reg_wr_addr,
|
||||
input [DWIDTH-1:0] reg_wr_data,
|
||||
|
||||
input reg_rd_req,
|
||||
input [AWIDTH-1:0] reg_rd_addr,
|
||||
output [DWIDTH-1:0] reg_rd_data,
|
||||
output reg_rd_resp,
|
||||
|
||||
output set_stb,
|
||||
output [SR_AWIDTH-1:0] set_addr,
|
||||
output [DWIDTH-1:0] set_data,
|
||||
|
||||
output rb_stb,
|
||||
output [SR_AWIDTH-1:0] rb_addr,
|
||||
input [DWIDTH-1:0] rb_data
|
||||
);
|
||||
|
||||
reg reg_rd_req_delay;
|
||||
reg reg_rd_req_delay2;
|
||||
wire [AWIDTH-1:0] set_addr_int;
|
||||
reg [AWIDTH-1:0] rb_addr_int;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (reset) begin
|
||||
reg_rd_req_delay <= 1'b0;
|
||||
reg_rd_req_delay2 <= 1'b0;
|
||||
rb_addr_int <= 'd0;
|
||||
end
|
||||
else if (reg_rd_req) begin
|
||||
rb_addr_int <= reg_rd_addr - BASE;
|
||||
reg_rd_req_delay <= 1'b1;
|
||||
end
|
||||
else if (reg_rd_req_delay) begin
|
||||
reg_rd_req_delay2 <= 1'b1;
|
||||
reg_rd_req_delay <= 1'b0;
|
||||
end
|
||||
// Deassert after two clock cycles
|
||||
else if (reg_rd_req_delay2) begin
|
||||
reg_rd_req_delay <= 1'b0;
|
||||
reg_rd_req_delay2 <= 1'b0;
|
||||
rb_addr_int <= 'd0;
|
||||
end
|
||||
else begin
|
||||
reg_rd_req_delay <= 1'b0;
|
||||
reg_rd_req_delay2 <= 1'b0;
|
||||
rb_addr_int <= 'd0;
|
||||
end
|
||||
end
|
||||
|
||||
// Write mode of settings bus
|
||||
regport_to_settingsbus #(
|
||||
.BASE(BASE),
|
||||
.END_ADDR(END_ADDR),
|
||||
.DWIDTH(DWIDTH),
|
||||
.AWIDTH(AWIDTH),
|
||||
.SR_AWIDTH(SR_AWIDTH),
|
||||
.ADDRESSING(ADDRESSING)
|
||||
) xbar_write_settings_bus (
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.reg_wr_req(reg_wr_req),
|
||||
.reg_wr_addr(reg_wr_addr),
|
||||
.reg_wr_data(reg_wr_data),
|
||||
|
||||
.set_stb(set_stb),
|
||||
.set_addr(set_addr),
|
||||
.set_data(set_data)
|
||||
);
|
||||
|
||||
assign rb_addr = (ADDRESSING == "WORD") ? {{SHIFT{1'b0}}, rb_addr_int[SR_AWIDTH-1:SHIFT]}
|
||||
: rb_addr_int[SR_AWIDTH-1:0];
|
||||
// Strobe asserted two cycle after read request only when address is between BASE and END ADDR
|
||||
// This is specific to the xbar as the xbar delays read data by an extra clock
|
||||
// cycle to relax timing.
|
||||
assign rb_stb = reg_rd_req_delay2 && (reg_rd_addr >= BASE) && (reg_rd_addr <= END_ADDR);
|
||||
assign reg_rd_resp = rb_stb;
|
||||
assign reg_rd_data = rb_data;
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
// Copyright 2018-2019 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// The purpose of this module is to synchronize a reset from one clock domain
|
||||
// to another. The reset_in signal must be driven by a glitch-free source.
|
||||
//
|
||||
|
||||
module reset_sync (
|
||||
// clock for the output reset
|
||||
input clk,
|
||||
// glitch-free input reset
|
||||
input reset_in,
|
||||
// output reset in the clk domain
|
||||
output reg reset_out);
|
||||
|
||||
wire reset_c;
|
||||
|
||||
synchronizer #(
|
||||
// The input reset is async to the output clk domain... so timing should not be
|
||||
// analyzed here!
|
||||
.FALSE_PATH_TO_IN(1),
|
||||
// Assert reset_out by default. When clk starts toggling the downstream logic will
|
||||
// be in reset for at least 10 clk cycles. This allows the clock to settle (if needed)
|
||||
// and the reset to propagate fully to all logic.
|
||||
.INITIAL_VAL(1),
|
||||
.STAGES(10)
|
||||
) reset_double_sync (
|
||||
.clk(clk), .rst(1'b0), .in(reset_in), .out(reset_c)
|
||||
);
|
||||
|
||||
always @(posedge clk)
|
||||
reset_out <= reset_c;
|
||||
|
||||
endmodule // reset_sync
|
||||
@@ -0,0 +1,144 @@
|
||||
//
|
||||
// Copyright 2011-2014 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
//
|
||||
// Refer to SelectMAP and ICAP docs in UG470
|
||||
//
|
||||
|
||||
module s7_icap_wb
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
input cyc_i,
|
||||
input stb_i,
|
||||
input we_i,
|
||||
output ack_o,
|
||||
input [31:0] dat_i,
|
||||
output [31:0] dat_o
|
||||
);
|
||||
|
||||
reg rdwrb, csib;
|
||||
|
||||
|
||||
reg [2:0] icap_state;
|
||||
localparam ICAP_IDLE = 0;
|
||||
localparam ICAP_WR0 = 1;
|
||||
localparam ICAP_WR1 = 2;
|
||||
localparam ICAP_RD0 = 3;
|
||||
localparam ICAP_RD1 = 4;
|
||||
|
||||
localparam IDLE = 1'b1;
|
||||
localparam ACTIVE = 1'b0;
|
||||
localparam READ = 1'b1;
|
||||
localparam WRITE = 1'b0;
|
||||
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset) begin
|
||||
rdwrb <= READ;
|
||||
csib <= IDLE;
|
||||
icap_state <= ICAP_IDLE;
|
||||
end
|
||||
else
|
||||
case(icap_state)
|
||||
//
|
||||
// In IDLE state waiting for a READ or WRITE to be signalled from the WB bus.
|
||||
// (In this state rdwrb can flip state without effect because ICAP is not selected)
|
||||
//
|
||||
ICAP_IDLE :
|
||||
begin
|
||||
if(stb_i & cyc_i) begin
|
||||
if(we_i) begin
|
||||
// Start WRITE, assert RDWR_B LOW whilst CSI_B remains HIGH.
|
||||
rdwrb <= WRITE;
|
||||
csib <= IDLE;
|
||||
icap_state <= ICAP_WR0;
|
||||
end else begin
|
||||
// Start READ
|
||||
rdwrb <= READ;
|
||||
csib <= IDLE;
|
||||
icap_state <= ICAP_RD0;
|
||||
end
|
||||
end else begin
|
||||
// Stay IDLE
|
||||
rdwrb <= READ;
|
||||
csib <= IDLE;
|
||||
icap_state <= ICAP_IDLE;
|
||||
end
|
||||
end // case: ICAP_IDLE
|
||||
//
|
||||
// First cycle of WRITE.
|
||||
// Next cycle assert RDWR_B LOW and assert CSI_B LOW.
|
||||
//
|
||||
ICAP_WR0 : begin
|
||||
rdwrb <= WRITE;
|
||||
csib <= ACTIVE;
|
||||
icap_state <= ICAP_WR1;
|
||||
end
|
||||
//
|
||||
// Second cycle of WRITE.
|
||||
// Next cycle assert RDWR_B LOW and assert CSI_B HIGH whilst transitioning to IDLE state
|
||||
//
|
||||
ICAP_WR1 : begin
|
||||
rdwrb <= WRITE;
|
||||
csib <= IDLE;
|
||||
icap_state <= ICAP_IDLE;
|
||||
end
|
||||
//
|
||||
// First cycle of READ.
|
||||
// Next cycle assert RDWR_B HIGH and assert CSI_B LOW.
|
||||
//
|
||||
ICAP_RD0 : begin
|
||||
rdwrb <= READ;
|
||||
csib <= ACTIVE;
|
||||
icap_state <= ICAP_WR1;
|
||||
end
|
||||
//
|
||||
// Second cycle of READ.
|
||||
// Next cycle assert RDWR_B HIGH and assert CSI_B HIGH whilst transitioning to IDLE state
|
||||
//
|
||||
ICAP_RD1 : begin
|
||||
rdwrb <= READ;
|
||||
csib <= IDLE;
|
||||
icap_state <= ICAP_IDLE;
|
||||
end
|
||||
|
||||
endcase // case (icap_state)
|
||||
|
||||
assign ack_o = (icap_state == ICAP_WR1) | (icap_state == ICAP_RD1);
|
||||
//assign debug_out = {17'd0, BUSY, dat_i[7:0], ~CE, ICAPCLK, ~WRITE, icap_state};
|
||||
|
||||
|
||||
ICAPE2 #(
|
||||
.DEVICE_ID(32'h03651093),
|
||||
.ICAP_WIDTH("X32"),
|
||||
.SIM_CFG_FILE_NAME("NONE")
|
||||
)
|
||||
ICAPE2_inst (
|
||||
.O(/*dat_o[31:0]*/),
|
||||
.CLK(clk), // Rising edge referenced for both reads and writes.
|
||||
.CSIB(csib), // CSIB = 0 to select ICAP
|
||||
.I(dat_i[31:0]), // Bitswaped as per SELECTMAP (See UG470 page 40)
|
||||
.RDWRB(rdwrb) // RDWB = 0 for WRITE, = 1 for READ
|
||||
);
|
||||
|
||||
assign dat_0 = 32'h0;
|
||||
|
||||
endmodule // s3a_icap_wb
|
||||
@@ -0,0 +1,126 @@
|
||||
//
|
||||
// Copyright 2014 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
module serial_to_settings
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
// Serial signals (async)
|
||||
input scl,
|
||||
input sda,
|
||||
// Settngs bus out
|
||||
output reg set_stb,
|
||||
output reg [7:0] set_addr,
|
||||
output reg [31:0] set_data,
|
||||
// Debug
|
||||
output [31:0] debug
|
||||
);
|
||||
|
||||
reg [2:0] state;
|
||||
|
||||
localparam SEARCH = 3'h0;
|
||||
localparam ADDRESS = 3'h1;
|
||||
localparam DATA = 3'h2;
|
||||
localparam STOP1 = 3'h3;
|
||||
localparam STOP2 = 3'h4;
|
||||
|
||||
reg scl_pre_reg, scl_reg, scl_reg2;
|
||||
reg sda_pre_reg, sda_reg, sda_reg2;
|
||||
reg [4:0] counter;
|
||||
|
||||
|
||||
always @(posedge clk) begin
|
||||
scl_reg2 <= scl_reg;
|
||||
scl_reg <= scl_pre_reg;
|
||||
scl_pre_reg <= scl;
|
||||
sda_reg2 <= sda_reg;
|
||||
sda_reg <= sda_pre_reg;
|
||||
sda_pre_reg <= sda;
|
||||
end
|
||||
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset) begin
|
||||
state <= SEARCH;
|
||||
counter <= 0;
|
||||
set_addr <= 0;
|
||||
set_data <= 0;
|
||||
set_stb <= 0;
|
||||
end else begin
|
||||
case(state)
|
||||
//
|
||||
// Search for I2C like start indication: SDA goes low whilst clock is high.
|
||||
//
|
||||
SEARCH: begin
|
||||
set_stb <= 0;
|
||||
// Look for START.
|
||||
if (scl_reg && scl_reg2 && !sda_reg && sda_reg2) begin
|
||||
state <= ADDRESS;
|
||||
counter <= 0;
|
||||
end
|
||||
end
|
||||
//
|
||||
// Count 8 Address bits.
|
||||
// Master changes SDA on falling edge of SCL, we sample on the rising edge.
|
||||
//
|
||||
ADDRESS: begin
|
||||
if (scl_reg && !scl_reg2) begin
|
||||
set_addr[7:0] <= {set_addr[6:0],sda_reg};
|
||||
if (counter == 7) begin
|
||||
state <= DATA;
|
||||
counter <= 0;
|
||||
end else
|
||||
counter <= counter + 1;
|
||||
end
|
||||
end
|
||||
//
|
||||
// Count 32 data bits.
|
||||
// Master changes SDA on falling edge of SCL, we sample on the rising edge.
|
||||
//
|
||||
DATA: begin
|
||||
if (scl_reg && !scl_reg2) begin
|
||||
set_data[31:0] <= {set_data[30:0],sda_reg};
|
||||
if (counter == 31) begin
|
||||
state <= STOP1;
|
||||
counter <= 0;
|
||||
end else
|
||||
counter <= counter + 1;
|
||||
end
|
||||
end
|
||||
//
|
||||
// Looks for rising SCL edge before STOP bit.
|
||||
//
|
||||
STOP1: begin
|
||||
if (scl_reg && !scl_reg2) begin
|
||||
state <= STOP2;
|
||||
end
|
||||
end
|
||||
//
|
||||
// Looks for STOP bit
|
||||
//
|
||||
STOP2: begin
|
||||
if (scl_reg && scl_reg2 && sda_reg && !sda_reg2) begin
|
||||
state <= SEARCH;
|
||||
counter <= 0;
|
||||
set_stb <= 1;
|
||||
end
|
||||
end
|
||||
|
||||
endcase // case(state)
|
||||
end // else: !if(reset)
|
||||
|
||||
assign debug =
|
||||
{
|
||||
counter[4:0],
|
||||
state[2:0],
|
||||
scl_reg,
|
||||
sda_reg
|
||||
};
|
||||
|
||||
|
||||
|
||||
endmodule // serial_to_settings
|
||||
@@ -0,0 +1,86 @@
|
||||
//
|
||||
// Copyright 2014 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
module serial_to_settings_tb();
|
||||
|
||||
|
||||
|
||||
reg clk;
|
||||
reg reset;
|
||||
|
||||
wire scl;
|
||||
wire sda;
|
||||
wire set_stb;
|
||||
wire [7:0] set_addr;
|
||||
wire [31:0] set_data;
|
||||
|
||||
//
|
||||
// These registers optionaly used
|
||||
// to drive nets through procedural assignments in test bench.
|
||||
// These drivers default to tri-stated.
|
||||
//
|
||||
reg scl_r;
|
||||
reg sda_r;
|
||||
|
||||
assign scl = scl_r;
|
||||
assign sda = sda_r;
|
||||
|
||||
initial
|
||||
begin
|
||||
scl_r <= 1'bz;
|
||||
sda_r <= 1'bz;
|
||||
end
|
||||
|
||||
|
||||
|
||||
serial_to_settings serial_to_settings_i
|
||||
(
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
// Serial signals (async)
|
||||
.scl(scl),
|
||||
.sda(sda),
|
||||
// Settngs bus out
|
||||
.set_stb(set_stb),
|
||||
.set_addr(set_addr),
|
||||
.set_data(set_data)
|
||||
);
|
||||
|
||||
// Nasty HAck to convert settings to wishbone crudely.
|
||||
reg wb_stb;
|
||||
wire wb_ack_o;
|
||||
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset)
|
||||
wb_stb <= 0;
|
||||
else
|
||||
wb_stb <= set_stb ? 1 : ((wb_ack_o) ? 0 : wb_stb);
|
||||
|
||||
simple_uart debug_uart
|
||||
(
|
||||
.clk_i(clk),
|
||||
.rst_i(reset),
|
||||
.we_i(wb_stb),
|
||||
.stb_i(wb_stb),
|
||||
.cyc_i(wb_stb),
|
||||
.ack_o(wb_ack_o),
|
||||
.adr_i(set_addr[2:0]),
|
||||
.dat_i(set_data[31:0]),
|
||||
.dat_o(),
|
||||
.rx_int_o(),
|
||||
.tx_int_o(),
|
||||
.tx_o(txd),
|
||||
.rx_i(rxd),
|
||||
.baud_o()
|
||||
);
|
||||
|
||||
//
|
||||
// Bring in a simulation script here
|
||||
//
|
||||
`include "simulation_script.v"
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// Copyright 2011-2012 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
//-- A settings register is a peripheral for the settings register bus.
|
||||
//-- When the settings register sees strobe abd a matching address,
|
||||
//-- the outputs will be become registered to the given input bus.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
module setting_reg
|
||||
#(parameter my_addr = 0,
|
||||
parameter awidth = 8,
|
||||
parameter width = 32,
|
||||
parameter at_reset=0)
|
||||
(input clk, input rst, input strobe, input wire [awidth-1:0] addr,
|
||||
input wire [31:0] in, output reg [width-1:0] out, output reg changed);
|
||||
|
||||
always @(posedge clk)
|
||||
if(rst)
|
||||
begin
|
||||
out <= at_reset;
|
||||
changed <= 1'b0;
|
||||
end
|
||||
else
|
||||
if(strobe & (my_addr==addr))
|
||||
begin
|
||||
out <= in[width-1:0];
|
||||
changed <= 1'b1;
|
||||
end
|
||||
else
|
||||
changed <= 1'b0;
|
||||
|
||||
endmodule // setting_reg
|
||||
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// Copyright 2016 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Mux multiple settings buses
|
||||
|
||||
module settings_bus_mux #(
|
||||
parameter PRIO=0, // 0 = Round robin, 1 = Lower ports get priority (see axi_mux)
|
||||
parameter AWIDTH=8,
|
||||
parameter DWIDTH=32,
|
||||
parameter FIFO_SIZE=1,
|
||||
parameter NUM_BUSES=2)
|
||||
(
|
||||
input clk, input reset, input clear,
|
||||
input [NUM_BUSES-1:0] in_set_stb, input [NUM_BUSES*AWIDTH-1:0] in_set_addr, input [NUM_BUSES*DWIDTH-1:0] in_set_data,
|
||||
output out_set_stb, output [AWIDTH-1:0] out_set_addr, output [DWIDTH-1:0] out_set_data, input ready
|
||||
);
|
||||
|
||||
wire [NUM_BUSES*(AWIDTH+DWIDTH)-1:0] i_tdata;
|
||||
generate
|
||||
if(NUM_BUSES <= 1) begin
|
||||
assign out_set_stb = in_set_stb;
|
||||
assign out_set_addr = in_set_addr;
|
||||
assign out_set_data = in_set_data;
|
||||
end else begin
|
||||
genvar i;
|
||||
for (i = 0; i < NUM_BUSES; i = i + 1) begin
|
||||
assign i_tdata[(i+1)*(AWIDTH+DWIDTH)-1:i*(AWIDTH+DWIDTH)] = {in_set_addr[(i+1)*AWIDTH-1:i*AWIDTH],in_set_data[(i+1)*DWIDTH-1:i*DWIDTH]};
|
||||
end
|
||||
axi_mux #(
|
||||
.PRIO(PRIO),
|
||||
.WIDTH(AWIDTH+DWIDTH),
|
||||
.PRE_FIFO_SIZE($clog2(NUM_BUSES)),
|
||||
.POST_FIFO_SIZE(FIFO_SIZE),
|
||||
.SIZE(NUM_BUSES))
|
||||
axi_mux (
|
||||
.clk(clk), .reset(reset), .clear(clear),
|
||||
.i_tdata(i_tdata), .i_tlast({NUM_BUSES{1'b1}}), .i_tvalid(in_set_stb), .i_tready(),
|
||||
.o_tdata({out_set_addr,out_set_data}), .o_tlast(), .o_tvalid(out_set_stb), .o_tready(ready));
|
||||
end
|
||||
endgenerate
|
||||
endmodule
|
||||
@@ -0,0 +1,170 @@
|
||||
//
|
||||
// Copyright 2018 Ettus Research, A National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: settings_bus_timed_2clk
|
||||
// Description:
|
||||
// - Stores settings bus transaction in a FIFO and
|
||||
// releases them based on VITA time input
|
||||
// - Also moves the settings bus to the timebase
|
||||
// clock domain
|
||||
//
|
||||
|
||||
module settings_bus_timed_2clk #(
|
||||
parameter SR_AWIDTH = 8,
|
||||
parameter SR_DWIDTH = 32,
|
||||
parameter RB_AWIDTH = 8,
|
||||
parameter RB_DWIDTH = 64,
|
||||
parameter TIMED_CMDS_EN = 0
|
||||
) (
|
||||
input sb_clk, // Settings bus clock
|
||||
input sb_rst, // Reset (sb_clk)
|
||||
input tb_clk, // Timebase clock
|
||||
input tb_rst, // Reset (tb_clk)
|
||||
|
||||
input [63:0] vita_time, // Current timebase time
|
||||
|
||||
input s_set_stb, // Settings bus strobe
|
||||
input [SR_AWIDTH-1:0] s_set_addr, // Settings address
|
||||
input [SR_DWIDTH-1:0] s_set_data, // Settings data
|
||||
input s_set_has_time, // Is this a timed command?
|
||||
input [63:0] s_set_time, // Command time
|
||||
output s_set_pending, // Is settings transaction pending?
|
||||
input [RB_AWIDTH-1:0] s_rb_addr, // Readback address
|
||||
output s_rb_stb, // Readback data strobe
|
||||
output [RB_DWIDTH-1:0] s_rb_data, // Readback data value
|
||||
|
||||
output m_set_stb, // Settings bus strobe
|
||||
output [SR_AWIDTH-1:0] m_set_addr, // Settings address
|
||||
output [SR_DWIDTH-1:0] m_set_data, // Settings data
|
||||
output m_set_has_time, // Is this a timed command?
|
||||
output [63:0] m_set_time, // Command time
|
||||
input m_set_pending, // Is settings transaction pending?
|
||||
output [RB_AWIDTH-1:0] m_rb_addr, // Readback address
|
||||
input m_rb_stb, // Readback data strobe
|
||||
input [RB_DWIDTH-1:0] m_rb_data // Readback data value
|
||||
);
|
||||
|
||||
// States for input and output state machines
|
||||
localparam [2:0] ST_IDLE = 3'd0; // Nothing is happening on the bus
|
||||
localparam [2:0] ST_SET_ISSUED = 3'd1; // A settings transaction has been issued
|
||||
localparam [2:0] ST_SET_PENDING = 3'd2; // A settings transaction is pending
|
||||
localparam [2:0] ST_RB_PENDING = 3'd3; // Waiting for readback data
|
||||
localparam [2:0] ST_RB_DONE = 3'd4; // Readback data is valid
|
||||
|
||||
wire rb_valid;
|
||||
|
||||
// Input state machine
|
||||
reg [2:0] in_state = ST_IDLE;
|
||||
always @(posedge sb_clk) begin
|
||||
if (sb_rst) begin
|
||||
in_state <= ST_IDLE;
|
||||
end else begin
|
||||
case (in_state)
|
||||
ST_IDLE: begin
|
||||
if (s_set_stb) begin
|
||||
in_state <= ST_SET_PENDING;
|
||||
end
|
||||
end
|
||||
ST_SET_PENDING: begin
|
||||
if (rb_valid) begin
|
||||
in_state <= ST_RB_DONE;
|
||||
end
|
||||
end
|
||||
ST_RB_DONE: begin
|
||||
in_state <= ST_IDLE;
|
||||
end
|
||||
default: begin
|
||||
in_state <= ST_IDLE;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
assign s_set_pending = (in_state == ST_SET_PENDING);
|
||||
assign s_rb_stb = (in_state == ST_RB_DONE);
|
||||
|
||||
// Clock crossing FIFO (settings)
|
||||
// TODO: Look into a more efficient implementation for a single element
|
||||
// clock crossing FIFO.
|
||||
wire set_pending, set_finished;
|
||||
axi_fifo_2clk #(
|
||||
.WIDTH(SR_AWIDTH+SR_DWIDTH+1+64+RB_AWIDTH), .SIZE(0)
|
||||
) sb_2clk_fifo_i (
|
||||
.i_aclk(sb_clk), .reset(sb_rst),
|
||||
.i_tdata({s_set_addr, s_set_data, s_set_has_time, s_set_time, s_rb_addr}),
|
||||
.i_tvalid(s_set_stb), .i_tready(/* Ignored: FIFO may not have an exact size*/),
|
||||
.o_aclk(tb_clk),
|
||||
.o_tdata({m_set_addr, m_set_data, m_set_has_time, m_set_time, m_rb_addr}),
|
||||
.o_tvalid(set_pending), .o_tready(set_finished)
|
||||
);
|
||||
|
||||
// Time compare logic
|
||||
// If ~has_time then pass the transaction through, otherwise wait for time
|
||||
// to tick up to command time
|
||||
wire now, late;
|
||||
wire go = ((TIMED_CMDS_EN == 1) && m_set_has_time) ? (now | late) : 1'b1;
|
||||
|
||||
// If this is a timed command then vita_time == m_set_time one cycle before
|
||||
// strobe is asserted i.e. timed strobe assertion has a one cycle latency
|
||||
time_compare time_compare (
|
||||
.clk(tb_clk), .reset(tb_rst),
|
||||
.time_now(vita_time), .trigger_time(m_set_time),
|
||||
.now(now), .early(), .late(late), .too_early()
|
||||
);
|
||||
|
||||
// Clock crossing FIFO (readback)
|
||||
reg [RB_DWIDTH-1:0] cached_rb_data;
|
||||
axi_fifo_2clk #(
|
||||
.WIDTH(RB_DWIDTH), .SIZE(0)
|
||||
) rbdata_2clk_fifo_i (
|
||||
.reset(tb_rst),
|
||||
.i_aclk(tb_clk), .i_tdata(cached_rb_data), .i_tvalid(set_finished), .i_tready(),
|
||||
.o_aclk(sb_clk), .o_tdata(s_rb_data), .o_tvalid(rb_valid), .o_tready(s_rb_stb)
|
||||
);
|
||||
|
||||
// Output state machine
|
||||
reg [2:0] out_state = ST_IDLE;
|
||||
always @(posedge tb_clk) begin
|
||||
if (tb_rst) begin
|
||||
out_state <= ST_IDLE;
|
||||
end else begin
|
||||
case (out_state)
|
||||
ST_IDLE: begin
|
||||
if (go & set_pending) begin
|
||||
out_state <= ST_SET_ISSUED;
|
||||
end
|
||||
end
|
||||
ST_SET_ISSUED: begin
|
||||
out_state <= ST_SET_PENDING;
|
||||
end
|
||||
ST_SET_PENDING: begin
|
||||
if (~m_set_pending) begin
|
||||
if (m_rb_stb) begin
|
||||
out_state <= ST_RB_DONE;
|
||||
cached_rb_data <= m_rb_data;
|
||||
end else begin
|
||||
out_state <= ST_RB_PENDING;
|
||||
end
|
||||
end
|
||||
end
|
||||
ST_RB_PENDING: begin
|
||||
if (m_rb_stb) begin
|
||||
out_state <= ST_RB_DONE;
|
||||
cached_rb_data <= m_rb_data;
|
||||
end
|
||||
end
|
||||
ST_RB_DONE: begin
|
||||
out_state <= ST_IDLE;
|
||||
end
|
||||
default: begin
|
||||
out_state <= ST_IDLE;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
assign m_set_stb = (out_state == ST_SET_ISSUED);
|
||||
assign set_finished = (out_state == ST_RB_DONE);
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,107 @@
|
||||
//
|
||||
// Copyright 2012 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
|
||||
// Simple I2C core
|
||||
|
||||
// Settings reg map:
|
||||
//
|
||||
// BASE+0 control register
|
||||
// byte0 - control bits, data byte, or command bits, prescaler
|
||||
// byte1 - what to do? (documented in cpp file)
|
||||
// write prescaler lo
|
||||
// write prescaler hi
|
||||
// write control
|
||||
// write data
|
||||
// write command
|
||||
// read data
|
||||
// read status
|
||||
//
|
||||
|
||||
// Readback:
|
||||
//
|
||||
// byte0 has readback value based on the last read command
|
||||
//
|
||||
|
||||
module simple_i2c_core
|
||||
#(
|
||||
//settings register base address
|
||||
parameter BASE = 0,
|
||||
|
||||
//i2c line level at reset
|
||||
parameter ARST_LVL = 1
|
||||
)
|
||||
(
|
||||
//clock and synchronous reset
|
||||
input clock, input reset,
|
||||
|
||||
//32-bit settings bus inputs
|
||||
input set_stb, input [7:0] set_addr, input [31:0] set_data,
|
||||
|
||||
//32-bit data readback
|
||||
output reg [31:0] readback,
|
||||
|
||||
//read is high when i2c core can begin another transaction
|
||||
output reg ready,
|
||||
|
||||
// I2C signals
|
||||
// i2c clock line
|
||||
input scl_pad_i, // SCL-line input
|
||||
output scl_pad_o, // SCL-line output (always 1'b0)
|
||||
output scl_padoen_o, // SCL-line output enable (active low)
|
||||
|
||||
// i2c data line
|
||||
input sda_pad_i, // SDA-line input
|
||||
output sda_pad_o, // SDA-line output (always 1'b0)
|
||||
output sda_padoen_o, // SDA-line output enable (active low)
|
||||
|
||||
//optional debug output
|
||||
output [31:0] debug
|
||||
);
|
||||
|
||||
//declare command settings register
|
||||
wire [7:0] sr_what, sr_data;
|
||||
wire sr_changed;
|
||||
setting_reg #(.my_addr(BASE+0),.width(16)) i2c_cmd_sr(
|
||||
.clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
|
||||
.out({sr_what, sr_data}),.changed(sr_changed));
|
||||
|
||||
//declare wb interface signals
|
||||
wire [2:0] wb_addr;
|
||||
wire [7:0] wb_data_mosi;
|
||||
wire [7:0] wb_data_miso;
|
||||
wire wb_we, wb_stb, wb_cyc;
|
||||
wire wb_ack;
|
||||
|
||||
//create wishbone-based i2c core
|
||||
i2c_master_top #(.ARST_LVL(ARST_LVL)) i2c
|
||||
(.wb_clk_i(clock),.wb_rst_i(reset),.arst_i(1'b0),
|
||||
.wb_adr_i(wb_addr),.wb_dat_i(wb_data_mosi),.wb_dat_o(wb_data_miso),
|
||||
.wb_we_i(wb_we),.wb_stb_i(wb_stb),.wb_cyc_i(wb_cyc),
|
||||
.wb_ack_o(wb_ack),.wb_inta_o(),
|
||||
.scl_pad_i(scl_pad_i),.scl_pad_o(scl_pad_o),.scl_padoen_o(scl_padoen_o),
|
||||
.sda_pad_i(sda_pad_i),.sda_pad_o(sda_pad_o),.sda_padoen_o(sda_padoen_o) );
|
||||
|
||||
//not ready between setting register and wishbone ack
|
||||
always @(posedge clock) begin
|
||||
if (reset || wb_ack) ready <= 1;
|
||||
else if (sr_changed) ready <= 0;
|
||||
end
|
||||
|
||||
//register wishbone data on every ack
|
||||
always @(posedge clock) begin
|
||||
if (wb_ack) readback <= {24'b0, wb_data_miso};
|
||||
end
|
||||
|
||||
//assign wishbone signals
|
||||
assign wb_addr = sr_what[2:0];
|
||||
assign wb_stb = sr_changed;
|
||||
assign wb_we = wb_stb && sr_what[3];
|
||||
assign wb_cyc = wb_stb;
|
||||
assign wb_data_mosi = sr_data;
|
||||
|
||||
endmodule //simple_i2c_core
|
||||
@@ -0,0 +1,229 @@
|
||||
//
|
||||
// Copyright 2012 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
|
||||
// Simple SPI core, the simplest, yet complete spi core I can think of
|
||||
|
||||
// Settings register controlled.
|
||||
// 2 settings regs, control and data
|
||||
// 1 32-bit readback and status signal
|
||||
|
||||
// Settings reg map:
|
||||
//
|
||||
// BASE+0 divider setting
|
||||
// bits [15:0] spi clock divider
|
||||
//
|
||||
// BASE+1 configuration input
|
||||
// bits [23:0] slave select, bit0 = slave0 enabled
|
||||
// bits [29:24] num bits (1 through 32)
|
||||
// bit [30] data input edge = in data bit latched on rising edge of clock
|
||||
// bit [31] data output edge = out data bit latched on rising edge of clock
|
||||
//
|
||||
// BASE+2 input data
|
||||
// Writing this register begins a spi transaction.
|
||||
// Bits are latched out from bit 0.
|
||||
// Therefore, load this register in reverse.
|
||||
//
|
||||
// Readback
|
||||
// Bits are latched into bit 0.
|
||||
// Therefore, data will be in-order.
|
||||
|
||||
module simple_spi_core
|
||||
#(
|
||||
//settings register base address
|
||||
parameter BASE = 0,
|
||||
|
||||
//width of serial enables (up to 24 is possible)
|
||||
parameter WIDTH = 8,
|
||||
|
||||
//idle state of the spi clock
|
||||
parameter CLK_IDLE = 0,
|
||||
|
||||
//idle state of the serial enables
|
||||
parameter SEN_IDLE = 24'hffffff
|
||||
)
|
||||
(
|
||||
//clock and synchronous reset
|
||||
input clock, input reset,
|
||||
|
||||
//32-bit settings bus inputs
|
||||
input set_stb, input [7:0] set_addr, input [31:0] set_data,
|
||||
|
||||
//32-bit data readback
|
||||
output [31:0] readback,
|
||||
output reg readback_stb,
|
||||
|
||||
//read is high when spi core can begin another transaction
|
||||
output ready,
|
||||
|
||||
//spi interface, slave selects, clock, data in, data out
|
||||
output [WIDTH-1:0] sen,
|
||||
output sclk,
|
||||
output reg mosi,
|
||||
input miso,
|
||||
|
||||
//optional debug output
|
||||
output [31:0] debug
|
||||
);
|
||||
|
||||
wire [15:0] sclk_divider;
|
||||
setting_reg #(.my_addr(BASE+0),.width(16)) divider_sr(
|
||||
.clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
|
||||
.out(sclk_divider),.changed());
|
||||
|
||||
wire [23:0] slave_select;
|
||||
wire [5:0] num_bits;
|
||||
wire datain_edge, dataout_edge;
|
||||
setting_reg #(.my_addr(BASE+1),.width(32)) ctrl_sr(
|
||||
.clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
|
||||
.out({dataout_edge, datain_edge, num_bits, slave_select}),.changed());
|
||||
|
||||
wire [31:0] mosi_data;
|
||||
wire trigger_spi;
|
||||
setting_reg #(.my_addr(BASE+2),.width(32)) data_sr(
|
||||
.clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
|
||||
.out(mosi_data),.changed(trigger_spi));
|
||||
|
||||
localparam WAIT_TRIG = 0;
|
||||
localparam PRE_IDLE = 1;
|
||||
localparam CLK_REG = 2;
|
||||
localparam CLK_INV = 3;
|
||||
localparam POST_IDLE = 4;
|
||||
localparam IDLE_SEN = 5;
|
||||
|
||||
reg [2:0] state;
|
||||
|
||||
reg ready_reg;
|
||||
assign ready = ready_reg && ~trigger_spi;
|
||||
|
||||
//serial clock either idles or is in one of two clock states
|
||||
reg sclk_reg;
|
||||
assign sclk = sclk_reg;
|
||||
|
||||
//serial enables either idle or enabled based on state
|
||||
// IJB. One pipeline stage to break critical path from register in I/O pads.
|
||||
wire sen_is_idle = (state == WAIT_TRIG) || (state == IDLE_SEN);
|
||||
wire [23:0] sen24 = (sen_is_idle)? SEN_IDLE : (SEN_IDLE ^ slave_select);
|
||||
reg [WIDTH-1:0] sen_reg = SEN_IDLE;
|
||||
always @(posedge clock) begin
|
||||
if (reset) begin
|
||||
sen_reg <= SEN_IDLE;
|
||||
end else begin
|
||||
sen_reg <= sen24[WIDTH-1:0];
|
||||
end
|
||||
end
|
||||
assign sen = sen_reg;
|
||||
|
||||
//data output shift register
|
||||
// IJB. One pipeline stage to break critical path from register in I/O pads.
|
||||
reg [31:0] dataout_reg;
|
||||
wire [31:0] dataout_next = {dataout_reg[30:0], 1'b0};
|
||||
|
||||
always @(posedge clock)
|
||||
mosi <= dataout_reg[31];
|
||||
|
||||
//data input shift register
|
||||
// IJB. Two pipeline stages to break critical path from register in I/O pads.
|
||||
reg miso_pipe, miso_pipe2;
|
||||
always @(posedge clock) begin
|
||||
miso_pipe2 <= miso;
|
||||
miso_pipe <= miso_pipe2;
|
||||
end
|
||||
|
||||
reg [31:0] datain_reg;
|
||||
wire [31:0] datain_next = {datain_reg[30:0], miso_pipe};
|
||||
assign readback = datain_reg;
|
||||
|
||||
//counter for spi clock
|
||||
reg [15:0] sclk_counter;
|
||||
wire sclk_counter_done = (sclk_counter == sclk_divider);
|
||||
wire [15:0] sclk_counter_next = (sclk_counter_done)? 0 : sclk_counter + 1;
|
||||
|
||||
//counter for latching bits miso/mosi
|
||||
reg [6:0] bit_counter;
|
||||
wire [6:0] bit_counter_next = bit_counter + 1;
|
||||
wire bit_counter_done = (bit_counter_next == num_bits);
|
||||
|
||||
always @(posedge clock) begin
|
||||
if (reset) begin
|
||||
state <= WAIT_TRIG;
|
||||
sclk_reg <= CLK_IDLE;
|
||||
ready_reg <= 0;
|
||||
readback_stb <= 1'b0;
|
||||
end
|
||||
else begin
|
||||
case (state)
|
||||
|
||||
WAIT_TRIG: begin
|
||||
if (trigger_spi) state <= PRE_IDLE;
|
||||
readback_stb <= 1'b0;
|
||||
ready_reg <= ~trigger_spi;
|
||||
dataout_reg <= mosi_data;
|
||||
sclk_counter <= 0;
|
||||
bit_counter <= 0;
|
||||
sclk_reg <= CLK_IDLE;
|
||||
end
|
||||
|
||||
PRE_IDLE: begin
|
||||
if (sclk_counter_done) state <= CLK_REG;
|
||||
sclk_counter <= sclk_counter_next;
|
||||
sclk_reg <= CLK_IDLE;
|
||||
end
|
||||
|
||||
CLK_REG: begin
|
||||
if (sclk_counter_done) begin
|
||||
state <= CLK_INV;
|
||||
if (datain_edge != CLK_IDLE) datain_reg <= datain_next;
|
||||
if (dataout_edge != CLK_IDLE && bit_counter != 0) dataout_reg <= dataout_next;
|
||||
sclk_reg <= ~CLK_IDLE; //transition to rising when CLK_IDLE == 0
|
||||
end
|
||||
sclk_counter <= sclk_counter_next;
|
||||
end
|
||||
|
||||
CLK_INV: begin
|
||||
if (sclk_counter_done) begin
|
||||
state <= (bit_counter_done)? POST_IDLE : CLK_REG;
|
||||
bit_counter <= bit_counter_next;
|
||||
if (datain_edge == CLK_IDLE) datain_reg <= datain_next;
|
||||
if (dataout_edge == CLK_IDLE && ~bit_counter_done) dataout_reg <= dataout_next;
|
||||
sclk_reg <= CLK_IDLE; //transition to falling when CLK_IDLE == 0
|
||||
end
|
||||
sclk_counter <= sclk_counter_next;
|
||||
end
|
||||
|
||||
POST_IDLE: begin
|
||||
if (sclk_counter_done) state <= IDLE_SEN;
|
||||
sclk_counter <= sclk_counter_next;
|
||||
sclk_reg <= CLK_IDLE;
|
||||
end
|
||||
|
||||
IDLE_SEN: begin
|
||||
if (sclk_counter_done) begin
|
||||
ready_reg <= 1'b1;
|
||||
readback_stb <= 1'b1;
|
||||
state <= WAIT_TRIG;
|
||||
end
|
||||
sclk_counter <= sclk_counter_next;
|
||||
sclk_reg <= CLK_IDLE;
|
||||
end
|
||||
|
||||
default: state <= WAIT_TRIG;
|
||||
|
||||
endcase //state
|
||||
end
|
||||
end
|
||||
|
||||
assign debug = {
|
||||
trigger_spi, state, //4
|
||||
sclk, mosi, miso, ready, //4
|
||||
//sen[7:0], //8
|
||||
1'b0, bit_counter[6:0], //8
|
||||
sclk_counter_done, bit_counter_done, //2
|
||||
sclk_counter[5:0] //6
|
||||
};
|
||||
|
||||
endmodule //simple_spi_core
|
||||
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// Copyright 2014 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
module synchronizer #(
|
||||
parameter WIDTH = 1,
|
||||
parameter STAGES = 2,
|
||||
parameter INITIAL_VAL = 0,
|
||||
parameter FALSE_PATH_TO_IN = 1
|
||||
)(
|
||||
input clk,
|
||||
input rst,
|
||||
input [WIDTH-1:0] in,
|
||||
output [WIDTH-1:0] out
|
||||
);
|
||||
|
||||
//Q: Why do we have a separate impl and instantiate
|
||||
//it with a different instance name based on this
|
||||
//arbitrary parameter FALSE_PATH_TO_IN?
|
||||
//A: To make constraining these synchronizers easier.
|
||||
//We would like to write a single false path constraint
|
||||
//for all synchronizers when the input is truly async.
|
||||
//However other cases might require constraining the input
|
||||
//of this module.
|
||||
//To enable this, all clients that hook up async signals to
|
||||
//the "in" port can set FALSE_PATH_TO_IN=1 (or use the default)
|
||||
//and all clients that want the "in" delay to be constrained can
|
||||
//set FALSE_PATH_TO_IN=0.
|
||||
//In the XDC we can write the following async constraint:
|
||||
//set_false_path -to [get_pins */synchronizer_false_path/stages[0].value_reg[0]/D]
|
||||
//and this will take care of all instances of this module with FALSE_PATH_TO_IN==1
|
||||
|
||||
generate if (FALSE_PATH_TO_IN == 1) begin
|
||||
synchronizer_impl #(
|
||||
.WIDTH(WIDTH), .STAGES(STAGES), .INITIAL_VAL(INITIAL_VAL)
|
||||
) synchronizer_false_path (
|
||||
.clk(clk), .rst(rst), .in(in), .out(out)
|
||||
);
|
||||
end else begin
|
||||
synchronizer_impl #(
|
||||
.WIDTH(WIDTH), .STAGES(STAGES), .INITIAL_VAL(INITIAL_VAL)
|
||||
) synchronizer_constrained (
|
||||
.clk(clk), .rst(rst), .in(in), .out(out)
|
||||
);
|
||||
end endgenerate
|
||||
|
||||
endmodule //synchronizer
|
||||
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// Copyright 2014 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
module synchronizer_impl #(
|
||||
parameter WIDTH = 1,
|
||||
parameter STAGES = 2,
|
||||
parameter INITIAL_VAL = 0
|
||||
)(
|
||||
input clk,
|
||||
input rst,
|
||||
input [WIDTH-1:0] in,
|
||||
output [WIDTH-1:0] out
|
||||
);
|
||||
|
||||
(* ASYNC_REG = "TRUE" *) reg [WIDTH-1:0] value[0:STAGES-1];
|
||||
|
||||
integer k;
|
||||
initial begin
|
||||
for (k = 0; k < STAGES; k = k + 1) begin
|
||||
value[k] = INITIAL_VAL;
|
||||
end
|
||||
end
|
||||
|
||||
genvar i;
|
||||
generate
|
||||
for (i=0; i<STAGES; i=i+1) begin: stages
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
value[i] <= INITIAL_VAL;
|
||||
end else begin
|
||||
if (i == 0) begin
|
||||
value[i] <= in;
|
||||
end else begin
|
||||
value[i] <= value[i-1];
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
endgenerate
|
||||
|
||||
assign out = value[STAGES-1];
|
||||
|
||||
endmodule //synchronizer_impl
|
||||
@@ -0,0 +1,66 @@
|
||||
//
|
||||
// Copyright 2012 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
// User settings bus
|
||||
//
|
||||
// Provides 8-bit address, 32-bit data write only bus for user settings, consumes to addresses in
|
||||
// normal settings bus.
|
||||
//
|
||||
// Write user address to BASE
|
||||
// Write user data to BASE+1
|
||||
//
|
||||
// The user_set_stb will strobe after data write, must write new address even if same as previous one.
|
||||
|
||||
module user_settings
|
||||
#(parameter BASE=0)
|
||||
(input clk,
|
||||
input rst,
|
||||
|
||||
input set_stb,
|
||||
input [7:0] set_addr,
|
||||
input [31:0] set_data,
|
||||
|
||||
output set_stb_user,
|
||||
output [7:0] set_addr_user,
|
||||
output [31:0] set_data_user
|
||||
);
|
||||
|
||||
wire addr_changed, data_changed;
|
||||
reg stb_int;
|
||||
|
||||
setting_reg #(.my_addr(BASE+0),.width(8)) sr_0
|
||||
(.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out(set_addr_user),.changed(addr_changed) );
|
||||
|
||||
setting_reg #(.my_addr(BASE+1)) sr_1
|
||||
(.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr),
|
||||
.in(set_data),.out(set_data_user),.changed(data_changed) );
|
||||
|
||||
always @(posedge clk)
|
||||
if (rst|set_stb_user)
|
||||
stb_int <= 0;
|
||||
else
|
||||
if (addr_changed)
|
||||
stb_int <= 1;
|
||||
|
||||
assign set_stb_user = stb_int & data_changed;
|
||||
|
||||
endmodule // user_settings
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#
|
||||
# Copyright 2013 Ettus Research LLC
|
||||
# Copyright 2016 Ettus Research, a National Instruments Company
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
#
|
||||
|
||||
##################################################
|
||||
# Control Lib Sources (2xx devices)
|
||||
##################################################
|
||||
CONTROL_LIB_200_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/control_200/, \
|
||||
cvita_uart.v \
|
||||
radio_ctrl_proc.v \
|
||||
))
|
||||
@@ -0,0 +1,167 @@
|
||||
|
||||
//
|
||||
// Copyright 2013 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
|
||||
//create a compressed vita based uart data interface
|
||||
|
||||
module cvita_uart
|
||||
#(
|
||||
parameter SIZE = 0
|
||||
)
|
||||
(
|
||||
//clocking interface
|
||||
input clk, input rst,
|
||||
|
||||
//uart interface
|
||||
input rxd, output txd,
|
||||
|
||||
//chdr fifo input
|
||||
input [63:0] i_tdata,
|
||||
input i_tlast,
|
||||
input i_tvalid,
|
||||
output i_tready,
|
||||
|
||||
//chdr fifo output
|
||||
output [63:0] o_tdata,
|
||||
output o_tlast,
|
||||
output o_tvalid,
|
||||
input o_tready
|
||||
);
|
||||
|
||||
reg [31:0] sid;
|
||||
|
||||
//baud clock divider
|
||||
reg [15:0] clkdiv;
|
||||
|
||||
//hold rx in disable until a tx event
|
||||
reg rxd_enable;
|
||||
|
||||
//==================================================================
|
||||
//== RXD capture and packet generation interface
|
||||
//==================================================================
|
||||
wire [7:0] rx_char;
|
||||
wire fifo_empty;
|
||||
wire fifo_read;
|
||||
reg [11:0] seqnum;
|
||||
wire pgen_trigger;
|
||||
wire pgen_done;
|
||||
|
||||
//rx uart capture
|
||||
simple_uart_rx #(.SIZE(SIZE)) simple_uart_rx
|
||||
(
|
||||
.clk(clk), .rst(rst | ~rxd_enable),
|
||||
.fifo_out(rx_char), .fifo_read(fifo_read), .fifo_level(), .fifo_empty(fifo_empty),
|
||||
.clkdiv(clkdiv), .rx(rxd)
|
||||
);
|
||||
|
||||
//packet generation - holds rx character
|
||||
context_packet_gen context_packet_gen
|
||||
(
|
||||
.clk(clk), .reset(rst), .clear(1'b0),
|
||||
.trigger(pgen_trigger),
|
||||
.seqnum(seqnum),
|
||||
.sid({sid[15:0], sid[31:16]}),
|
||||
.body({56'b0, rx_char}),
|
||||
.vita_time(64'b0),
|
||||
|
||||
.done(pgen_done),
|
||||
.o_tdata(o_tdata), .o_tlast(o_tlast), .o_tvalid(o_tvalid), .o_tready(o_tready)
|
||||
);
|
||||
|
||||
//state machine to manage pgen and rx uart
|
||||
reg [1:0] rxd_state;
|
||||
localparam RXD_STATE_RECV_CHAR = 0;
|
||||
localparam RXD_STATE_PGEN_TRIG = 1;
|
||||
localparam RXD_STATE_WAIT_DONE = 2;
|
||||
localparam RXD_STATE_READ_FIFO = 3;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
seqnum <= 12'b0;
|
||||
rxd_state <= RXD_STATE_RECV_CHAR;
|
||||
end
|
||||
else case (rxd_state)
|
||||
|
||||
RXD_STATE_RECV_CHAR: begin
|
||||
if (!fifo_empty && rxd_enable) rxd_state <= RXD_STATE_PGEN_TRIG;
|
||||
end
|
||||
|
||||
RXD_STATE_PGEN_TRIG: begin
|
||||
rxd_state <= RXD_STATE_WAIT_DONE;
|
||||
end
|
||||
|
||||
RXD_STATE_WAIT_DONE: begin
|
||||
if (pgen_done) rxd_state <= RXD_STATE_READ_FIFO;
|
||||
end
|
||||
|
||||
RXD_STATE_READ_FIFO: begin
|
||||
rxd_state <= RXD_STATE_RECV_CHAR;
|
||||
seqnum <= seqnum + 1'b1;
|
||||
end
|
||||
|
||||
endcase //rxd_state
|
||||
end
|
||||
|
||||
assign fifo_read = (rxd_state == RXD_STATE_READ_FIFO) || (!rxd_enable);
|
||||
assign pgen_trigger = (rxd_state == RXD_STATE_PGEN_TRIG);
|
||||
|
||||
//==================================================================
|
||||
//== TXD generation and packet control interface
|
||||
//==================================================================
|
||||
wire [7:0] tx_char;
|
||||
wire fifo_write;
|
||||
wire fifo_full;
|
||||
|
||||
simple_uart_tx #(.SIZE(SIZE)) simple_uart_tx
|
||||
(
|
||||
.clk(clk), .rst(rst),
|
||||
.fifo_in(tx_char), .fifo_write(fifo_write), .fifo_level(), .fifo_full(fifo_full),
|
||||
.clkdiv(clkdiv), .baudclk(), .tx(txd)
|
||||
);
|
||||
|
||||
//state machine to manage control and tx uart
|
||||
reg [1:0] txd_state;
|
||||
localparam TXD_STATE_RECV_CHDR = 0;
|
||||
localparam TXD_STATE_RECV_TIME = 1;
|
||||
localparam TXD_STATE_RECV_BODY = 2;
|
||||
localparam TXD_STATE_DROP_FIFO = 3;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin;
|
||||
txd_state <= TXD_STATE_RECV_CHDR;
|
||||
rxd_enable <= 1'b0;
|
||||
end
|
||||
if (i_tvalid && i_tready) case (txd_state)
|
||||
|
||||
TXD_STATE_RECV_CHDR: begin
|
||||
txd_state <= (i_tdata[61])? TXD_STATE_RECV_TIME : TXD_STATE_RECV_BODY;
|
||||
sid <= i_tdata[31:0];
|
||||
end
|
||||
|
||||
TXD_STATE_RECV_TIME: begin
|
||||
txd_state <= TXD_STATE_RECV_BODY;
|
||||
end
|
||||
|
||||
TXD_STATE_RECV_BODY: begin
|
||||
txd_state <= (i_tlast)? TXD_STATE_RECV_CHDR : TXD_STATE_DROP_FIFO;
|
||||
clkdiv <= i_tdata[47:32];
|
||||
rxd_enable <= 1'b1;
|
||||
end
|
||||
|
||||
TXD_STATE_DROP_FIFO: begin
|
||||
if (i_tlast) txd_state <= TXD_STATE_RECV_CHDR;
|
||||
end
|
||||
|
||||
endcase //txd_state
|
||||
end
|
||||
|
||||
assign tx_char = i_tdata[7:0];
|
||||
assign fifo_write = (txd_state == TXD_STATE_RECV_BODY) && i_tvalid && i_tready;
|
||||
assign i_tready = !fifo_full;
|
||||
|
||||
endmodule // cvita_uart
|
||||
@@ -0,0 +1,151 @@
|
||||
//
|
||||
// Copyright 2014 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
// Radio Control Processor
|
||||
// Accepts compressed vita extension context packets of the following form:
|
||||
// { VITA Compressed Header, Stream ID }
|
||||
// { Optional 64 bit time }
|
||||
// { 16'h0, setting bus address [15:0], setting [31:0] }
|
||||
//
|
||||
// If there is a timestamp, packet is held until that time comes.
|
||||
// Goes immediately if there is no timestamp or if time has passed.
|
||||
// Sends out setting to setting bus, and then generates a response packet
|
||||
// with the same sequence number, the src/dest swapped streamid, and the actual time
|
||||
// the setting was sent.
|
||||
//
|
||||
// Note -- if t0 is the requested time, the actual send time on the setting bus is t0 + 1 cycle.
|
||||
|
||||
module radio_ctrl_proc
|
||||
(input clk, input reset, input clear,
|
||||
|
||||
input [63:0] ctrl_tdata, input ctrl_tlast, input ctrl_tvalid, output reg ctrl_tready,
|
||||
output reg [63:0] resp_tdata, output reg resp_tlast, output resp_tvalid, input resp_tready,
|
||||
|
||||
input [63:0] vita_time,
|
||||
|
||||
output set_stb, output [7:0] set_addr, output [31:0] set_data,
|
||||
input ready,
|
||||
|
||||
input [63:0] readback,
|
||||
|
||||
output [31:0] debug);
|
||||
|
||||
localparam RC_HEAD = 4'd0;
|
||||
localparam RC_TIME = 4'd1;
|
||||
localparam RC_DATA = 4'd2;
|
||||
localparam RC_DUMP = 4'd3;
|
||||
localparam RC_RESP_HEAD = 4'd4;
|
||||
localparam RC_RESP_TIME = 4'd5;
|
||||
localparam RC_RESP_DATA = 4'd6;
|
||||
|
||||
wire IS_EC = ctrl_tdata[63];
|
||||
wire HAS_TIME = ctrl_tdata[61];
|
||||
reg HAS_TIME_reg;
|
||||
|
||||
reg [3:0] rc_state;
|
||||
reg [63:0] cmd_time;
|
||||
|
||||
wire now, late, go;
|
||||
reg [11:0] seqnum;
|
||||
reg [31:0] sid;
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset)
|
||||
begin
|
||||
rc_state <= RC_HEAD;
|
||||
HAS_TIME_reg <= 1'b0;
|
||||
sid <= 32'd0;
|
||||
seqnum <= 12'd0;
|
||||
end
|
||||
else
|
||||
case(rc_state)
|
||||
RC_HEAD :
|
||||
if(ctrl_tvalid)
|
||||
begin
|
||||
sid <= ctrl_tdata[31:0];
|
||||
seqnum <= ctrl_tdata[59:48];
|
||||
HAS_TIME_reg <= HAS_TIME;
|
||||
if(IS_EC)
|
||||
if(HAS_TIME)
|
||||
rc_state <= RC_TIME;
|
||||
else
|
||||
rc_state <= RC_DATA;
|
||||
else
|
||||
if(~ctrl_tlast)
|
||||
rc_state <= RC_DUMP;
|
||||
end
|
||||
|
||||
RC_TIME :
|
||||
if(ctrl_tvalid)
|
||||
if(ctrl_tlast)
|
||||
rc_state <= RC_RESP_HEAD;
|
||||
else if(go)
|
||||
rc_state <= RC_DATA;
|
||||
|
||||
RC_DATA :
|
||||
if(ctrl_tvalid)
|
||||
if(ready)
|
||||
if(ctrl_tlast)
|
||||
rc_state <= RC_RESP_HEAD;
|
||||
else
|
||||
rc_state <= RC_DUMP;
|
||||
|
||||
RC_DUMP :
|
||||
if(ctrl_tvalid)
|
||||
if(ctrl_tlast)
|
||||
rc_state <= RC_RESP_HEAD;
|
||||
|
||||
RC_RESP_HEAD :
|
||||
if(resp_tready)
|
||||
rc_state <= RC_RESP_TIME;
|
||||
|
||||
RC_RESP_TIME :
|
||||
if(resp_tready)
|
||||
rc_state <= RC_RESP_DATA;
|
||||
|
||||
RC_RESP_DATA:
|
||||
if(resp_tready)
|
||||
rc_state <= RC_HEAD;
|
||||
|
||||
default :
|
||||
rc_state <= RC_HEAD;
|
||||
endcase // case (rc_state)
|
||||
|
||||
always @*
|
||||
case (rc_state)
|
||||
RC_HEAD : ctrl_tready <= 1'b1;
|
||||
RC_TIME : ctrl_tready <= ctrl_tlast | go;
|
||||
RC_DATA : ctrl_tready <= ready;
|
||||
RC_DUMP : ctrl_tready <= 1'b1;
|
||||
default : ctrl_tready <= 1'b0;
|
||||
endcase // case (rc_state)
|
||||
|
||||
time_compare time_compare
|
||||
(.clk(clk), .reset(reset), .time_now(vita_time), .trigger_time(ctrl_tdata), .now(now), .early(), .late(late), .too_early());
|
||||
|
||||
assign go = now | late;
|
||||
|
||||
assign set_stb = (rc_state == RC_DATA) & ready & ctrl_tvalid;
|
||||
assign set_addr = ctrl_tdata[39:32];
|
||||
assign set_data = ctrl_tdata[31:0];
|
||||
|
||||
always @(posedge clk)
|
||||
if (set_stb)
|
||||
cmd_time <= vita_time;
|
||||
|
||||
always @*
|
||||
case (rc_state)
|
||||
RC_RESP_HEAD : { resp_tlast, resp_tdata } <= {1'b0, 4'hA, seqnum, 16'd24, sid[15:0], sid[31:16] };
|
||||
RC_RESP_TIME : { resp_tlast, resp_tdata } <= {1'b0, cmd_time};
|
||||
RC_RESP_DATA : { resp_tlast, resp_tdata } <= {1'b1, readback};
|
||||
default : { resp_tlast, resp_tdata } <= 65'h0;
|
||||
endcase // case (rc_state)
|
||||
|
||||
assign resp_tvalid = (rc_state == RC_RESP_HEAD) | (rc_state == RC_RESP_TIME) | (rc_state == RC_RESP_DATA);
|
||||
|
||||
endmodule // radio_ctrl_proc
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
//
|
||||
// Copyright 2014 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module radio_ctrl_proc_tb();
|
||||
|
||||
reg clk = 0;
|
||||
reg reset = 1;
|
||||
|
||||
always #10 clk = ~clk;
|
||||
|
||||
initial $dumpfile("radio_ctrl_proc_tb.vcd");
|
||||
initial $dumpvars(0,radio_ctrl_proc_tb);
|
||||
|
||||
initial
|
||||
begin
|
||||
#1000 reset = 0;
|
||||
#20000;
|
||||
$finish;
|
||||
end
|
||||
|
||||
reg [63:0] vita_time = 64'd0;
|
||||
always @(posedge clk)
|
||||
if(reset) vita_time <= 64'd0;
|
||||
else vita_time <= vita_time + 64'd1;
|
||||
|
||||
reg [63:0] tdata;
|
||||
wire [63:0] tdata_int;
|
||||
reg tlast;
|
||||
wire tlast_int;
|
||||
reg tvalid = 1'b0;
|
||||
wire tvalid_int;
|
||||
wire tready, tready_int;
|
||||
|
||||
wire [7:0] set_addr;
|
||||
wire [31:0] set_data;
|
||||
wire set_stb;
|
||||
wire ready = 1'b1;
|
||||
|
||||
task send_packet;
|
||||
input ec;
|
||||
input timed;
|
||||
input [11:0] seqnum;
|
||||
input [31:0] sid;
|
||||
input [63:0] vtime;
|
||||
input [15:0] addr;
|
||||
input [31:0] data;
|
||||
|
||||
begin
|
||||
// Send a packet
|
||||
@(posedge clk);
|
||||
tlast <= 1'b0;
|
||||
tdata <= { ec, 1'b0, timed, 1'b0, seqnum, timed ? 16'd6 : 16'd4, sid };
|
||||
tvalid <= 1;
|
||||
@(posedge clk);
|
||||
if(timed)
|
||||
begin
|
||||
tdata <= vtime;
|
||||
@(posedge clk);
|
||||
end
|
||||
tlast <= 1'b1;
|
||||
tdata <= { 16'h0, addr, data };
|
||||
@(posedge clk);
|
||||
tvalid <= 0;
|
||||
@(posedge clk);
|
||||
end
|
||||
endtask // send_packet
|
||||
|
||||
initial
|
||||
begin
|
||||
tvalid <= 1'b0;
|
||||
while(reset)
|
||||
@(posedge clk);
|
||||
send_packet(1'b1,1'b0,12'h5,32'hDEAD_BEEF,64'h0,16'hB,32'hF00D_1234);
|
||||
send_packet(1'b1,1'b1,12'h6,32'hDEAD_6789,64'h20,16'hC,32'hABCD_4321);
|
||||
send_packet(1'b1,1'b1,12'h7,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321);
|
||||
//send_packet(.ec(1), .timed(0), .seqnum(5), .sid(32'hDEAD_BEEF), .vtime(0), .addr(16'hB), .data(32'hF00D_1234));
|
||||
end
|
||||
|
||||
axi_fifo_short #(.WIDTH(65)) axi_fifo_short
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.i_tdata({tlast,tdata}), .i_tvalid(tvalid), .i_tready(tready),
|
||||
.o_tdata({tlast_int,tdata_int}), .o_tvalid(tvalid_int), .o_tready(tready_int));
|
||||
|
||||
wire [63:0] resp_tdata;
|
||||
wire resp_tlast, resp_tvalid, resp_tready;
|
||||
|
||||
radio_ctrl_proc radio_ctrl_proc
|
||||
(.clk(clk), .reset(reset), .clear(1'b0),
|
||||
.ctrl_tdata(tdata_int), .ctrl_tlast(tlast_int), .ctrl_tvalid(tvalid_int), .ctrl_tready(tready_int),
|
||||
.resp_tdata(resp_tdata), .resp_tlast(resp_tlast), .resp_tvalid(resp_tvalid), .resp_tready(resp_tready),
|
||||
.vita_time(vita_time), .ready(ready),
|
||||
.set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
|
||||
.debug()
|
||||
);
|
||||
|
||||
assign resp_tready = 1'b1;
|
||||
|
||||
always @(posedge clk)
|
||||
if(resp_tvalid & resp_tready)
|
||||
begin
|
||||
$display("%x",resp_tdata);
|
||||
if(resp_tlast)
|
||||
$display("TLAST");
|
||||
end
|
||||
endmodule // radio_ctrl_proc_tb
|
||||
@@ -0,0 +1,47 @@
|
||||
#
|
||||
# Copyright 2013 Ettus Research LLC
|
||||
# Copyright 2016 Ettus Research, a National Instruments Company
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
#
|
||||
|
||||
##################################################
|
||||
# DSP Sources
|
||||
##################################################
|
||||
DSP_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/dsp/, \
|
||||
acc.v \
|
||||
add2_and_clip_reg.v \
|
||||
add2_and_clip.v \
|
||||
add2_and_round_reg.v \
|
||||
add2_and_round.v \
|
||||
add2_reg.v \
|
||||
add2.v \
|
||||
add_then_mac.v \
|
||||
cic_decim.v \
|
||||
cic_dec_shifter.v \
|
||||
cic_interp.v \
|
||||
cic_int_shifter.v \
|
||||
cic_strober.v \
|
||||
clip_reg.v \
|
||||
clip.v \
|
||||
cordic_stage.v \
|
||||
cordic_z24.v \
|
||||
ddc_chain.v \
|
||||
duc_chain.v \
|
||||
hb47_int.v \
|
||||
hb_dec.v \
|
||||
hb_interp.v \
|
||||
Makefile.srcs \
|
||||
mult_add_clip.v \
|
||||
round_reg.v \
|
||||
round_sd.v \
|
||||
round.v \
|
||||
rx_dcoffset.v \
|
||||
rx_frontend.v \
|
||||
sign_extend.v \
|
||||
small_hb_dec.v \
|
||||
small_hb_int.v \
|
||||
srl.v \
|
||||
tx_frontend.v \
|
||||
variable_delay_line.v \
|
||||
))
|
||||
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
|
||||
|
||||
module acc
|
||||
#(parameter IWIDTH=16, OWIDTH=30)
|
||||
(input clk,
|
||||
input clear,
|
||||
input acc,
|
||||
input [IWIDTH-1:0] in,
|
||||
output reg [OWIDTH-1:0] out);
|
||||
|
||||
wire [OWIDTH-1:0] in_signext;
|
||||
sign_extend #(.bits_in(IWIDTH),.bits_out(OWIDTH))
|
||||
acc_signext (.in(in),.out(in_signext));
|
||||
|
||||
// CLEAR & ~ACC --> clears the accumulator
|
||||
// CLEAR & ACC --> loads the accumulator
|
||||
// ~CLEAR & ACC --> accumulates
|
||||
// ~CLEAR & ~ACC --> hold
|
||||
|
||||
wire [OWIDTH-1:0] addend1 = clear ? 0 : out;
|
||||
wire [OWIDTH-1:0] addend2 = ~acc ? 0 : in_signext;
|
||||
wire [OWIDTH-1:0] sum_int = addend1 + addend2;
|
||||
|
||||
always @(posedge clk)
|
||||
out <= sum_int;
|
||||
|
||||
endmodule // acc
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
|
||||
|
||||
module add2
|
||||
#(parameter WIDTH=16)
|
||||
(input [WIDTH-1:0] in1,
|
||||
input [WIDTH-1:0] in2,
|
||||
output [WIDTH-1:0] sum);
|
||||
|
||||
wire [WIDTH:0] sum_int = {in1[WIDTH-1],in1} + {in2[WIDTH-1],in2};
|
||||
assign sum = sum_int[WIDTH:1]; // Note -- will have some bias
|
||||
|
||||
endmodule // add2
|
||||
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// Copyright 2014 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
module add2_and_clip
|
||||
#(parameter WIDTH=16)
|
||||
(input [WIDTH-1:0] in1,
|
||||
input [WIDTH-1:0] in2,
|
||||
output [WIDTH-1:0] sum);
|
||||
|
||||
wire [WIDTH:0] sum_int = {in1[WIDTH-1],in1} + {in2[WIDTH-1],in2};
|
||||
clip #(.bits_in(WIDTH+1),.bits_out(WIDTH)) clip
|
||||
(.in(sum_int),.out(sum));
|
||||
|
||||
endmodule // add2_and_clip
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Copyright 2014 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
module add2_and_clip_reg
|
||||
#(parameter WIDTH=16)
|
||||
(input clk,
|
||||
input rst,
|
||||
input [WIDTH-1:0] in1,
|
||||
input [WIDTH-1:0] in2,
|
||||
input strobe_in,
|
||||
output reg [WIDTH-1:0] sum,
|
||||
output reg strobe_out);
|
||||
|
||||
wire [WIDTH-1:0] sum_int;
|
||||
|
||||
add2_and_clip #(.WIDTH(WIDTH)) add2_and_clip (.in1(in1),.in2(in2),.sum(sum_int));
|
||||
|
||||
always @(posedge clk)
|
||||
if(rst)
|
||||
sum <= 0;
|
||||
else if(strobe_in)
|
||||
sum <= sum_int;
|
||||
|
||||
always @(posedge clk) strobe_out <= rst ? 1'b0 : strobe_in;
|
||||
|
||||
endmodule // add2_and_clip_reg
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
|
||||
|
||||
module add2_and_round
|
||||
#(parameter WIDTH=16)
|
||||
(input [WIDTH-1:0] in1,
|
||||
input [WIDTH-1:0] in2,
|
||||
output [WIDTH-1:0] sum);
|
||||
|
||||
wire [WIDTH:0] sum_int = {in1[WIDTH-1],in1} + {in2[WIDTH-1],in2};
|
||||
assign sum = sum_int[WIDTH:1] + (sum_int[WIDTH] & sum_int[0]);
|
||||
|
||||
endmodule // add2_and_round
|
||||
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
|
||||
|
||||
module add2_and_round_reg
|
||||
#(parameter WIDTH=16)
|
||||
(input clk,
|
||||
input [WIDTH-1:0] in1,
|
||||
input [WIDTH-1:0] in2,
|
||||
output reg [WIDTH-1:0] sum);
|
||||
|
||||
wire [WIDTH-1:0] sum_int;
|
||||
|
||||
add2_and_round #(.WIDTH(WIDTH)) add2_n_rnd (.in1(in1),.in2(in2),.sum(sum_int));
|
||||
|
||||
always @(posedge clk)
|
||||
sum <= sum_int;
|
||||
|
||||
endmodule // add2_and_round_reg
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Copyright 2011 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
|
||||
|
||||
module add2_reg
|
||||
#(parameter WIDTH=16)
|
||||
(input clk,
|
||||
input [WIDTH-1:0] in1,
|
||||
input [WIDTH-1:0] in2,
|
||||
output reg [WIDTH-1:0] sum);
|
||||
|
||||
wire [WIDTH-1:0] sum_int;
|
||||
|
||||
add2 #(.WIDTH(WIDTH)) add2 (.in1(in1),.in2(in2),.sum(sum_int));
|
||||
|
||||
always @(posedge clk)
|
||||
sum <= sum_int;
|
||||
|
||||
endmodule // add2_reg
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
//
|
||||
// Copyright 2015 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
`timescale 1 ps / 1 ps
|
||||
|
||||
//
|
||||
// Implements acc=((a+d)*b)+c or acc=((a+d)*b)+acc'
|
||||
//
|
||||
|
||||
module add_then_mac
|
||||
#(parameter DEVICE = "VIRTEX6")
|
||||
(
|
||||
// Output ports
|
||||
output [47:0] acc,
|
||||
|
||||
// Input ports
|
||||
input carryin,
|
||||
input ce,
|
||||
input clk,
|
||||
input [17:0] b,
|
||||
input load,
|
||||
input [47:0] c,
|
||||
input [17:0] a,
|
||||
input [17:0] d,
|
||||
input rst
|
||||
);
|
||||
|
||||
|
||||
wire [24:0] a_in;
|
||||
wire [24:0] d_in;
|
||||
|
||||
localparam AREG_IN = 1;
|
||||
localparam BREG_IN = 1;
|
||||
localparam MREG_IN = 1;
|
||||
localparam PREG_IN = 1;
|
||||
localparam A1REG_IN = 1;
|
||||
localparam A0REG_IN = 0;
|
||||
localparam B1REG_IN = 1;
|
||||
localparam B0REG_IN = 1;
|
||||
|
||||
// Sign extend inputs
|
||||
assign a_in = (a[17] == 1'b1) ? {7'hff, a} : {7'h00, a};
|
||||
assign d_in = (d[17] == 1'b1) ? {7'hff, d} : {7'h00, d};
|
||||
|
||||
generate
|
||||
case(DEVICE)
|
||||
// begin generate virtex6
|
||||
"VIRTEX6", "7SERIES" :
|
||||
begin
|
||||
DSP48E1 #(
|
||||
.ACASCREG(AREG_IN),
|
||||
.AREG(AREG_IN),
|
||||
.BCASCREG(BREG_IN),
|
||||
.BREG(BREG_IN),
|
||||
.MREG(MREG_IN),
|
||||
.PREG(PREG_IN),
|
||||
.USE_DPORT("TRUE")
|
||||
)
|
||||
DSP48E_BL (
|
||||
.ACOUT(),
|
||||
.BCOUT(),
|
||||
.CARRYCASCOUT(),
|
||||
.CARRYOUT(),
|
||||
.MULTSIGNOUT(),
|
||||
.OVERFLOW(),
|
||||
.P(acc),
|
||||
.PATTERNBDETECT(),
|
||||
.PATTERNDETECT(),
|
||||
.PCOUT(),
|
||||
.UNDERFLOW(),
|
||||
.A({5'b0, a_in[24:0]}),
|
||||
.ACIN(30'b0),
|
||||
.ALUMODE(4'b0000),
|
||||
.B(b[17:0]),
|
||||
.BCIN(18'b0),
|
||||
.C(c),
|
||||
.CARRYCASCIN(1'b0),
|
||||
.CARRYIN(carryin),
|
||||
.CARRYINSEL(3'b0),
|
||||
.CEA1(1'b0),
|
||||
.CEA2(ce),
|
||||
.CEAD(ce),
|
||||
.CEALUMODE(ce),
|
||||
.CEB1(1'b0),
|
||||
.CEB2(ce),
|
||||
.CEC(ce),
|
||||
.CECARRYIN(ce),
|
||||
.CECTRL(ce),
|
||||
.CED(ce),
|
||||
.CEINMODE(ce),
|
||||
.CEM(ce),
|
||||
.CEP(ce),
|
||||
.CLK(clk),
|
||||
.D(d_in[24:0]),
|
||||
.INMODE(5'b00100),
|
||||
.MULTSIGNIN(1'b0),
|
||||
.OPMODE({2'b01,load,4'b0101}),
|
||||
.PCIN(48'b0),
|
||||
.RSTA(rst),
|
||||
.RSTALLCARRYIN(rst),
|
||||
.RSTALUMODE(rst),
|
||||
.RSTB(rst),
|
||||
.RSTC(rst),
|
||||
.RSTCTRL(rst),
|
||||
.RSTD(rst),
|
||||
.RSTINMODE(rst),
|
||||
.RSTM(rst),
|
||||
.RSTP(rst)
|
||||
);
|
||||
end // end generate virtex6
|
||||
// begin generate spartan6
|
||||
"SPARTAN6" :
|
||||
begin
|
||||
// DSP48A1 has 18b+18b=18b pre-adder, must discard LSB of A and D and compensate by shifting ACC.
|
||||
wire discard;;
|
||||
assign acc[0] = 1'b0;
|
||||
|
||||
DSP48A1 #(
|
||||
.A0REG(A0REG_IN),
|
||||
.A1REG(A1REG_IN),
|
||||
.B0REG(B0REG_IN),
|
||||
.B1REG(B1REG_IN),
|
||||
.MREG(MREG_IN),
|
||||
.PREG(PREG_IN)
|
||||
)
|
||||
DSP48AST (
|
||||
.BCOUT(),
|
||||
.CARRYOUT(),
|
||||
.CARRYOUTF(),
|
||||
.M(),
|
||||
.P({discard,acc[47:1]}),
|
||||
.PCOUT(),
|
||||
.A(b[17:0]),
|
||||
.B({a_in[17],a_in[17:1]}),
|
||||
.C(c),
|
||||
.CARRYIN(carryin),
|
||||
.CEA(ce),
|
||||
.CEB(ce),
|
||||
.CEC(ce),
|
||||
.CECARRYIN(ce),
|
||||
.CED(ce),
|
||||
.CEM(ce),
|
||||
.CEOPMODE(ce),
|
||||
.CEP(ce),
|
||||
.CLK(clk),
|
||||
.D({d_in[17],d_in[17:1]}),
|
||||
.OPMODE({5'b00011,load, 2'b01}),
|
||||
.PCIN(48'b0),
|
||||
.RSTA(rst),
|
||||
.RSTB(rst),
|
||||
.RSTC(rst),
|
||||
.RSTCARRYIN(rst),
|
||||
.RSTD(rst),
|
||||
.RSTM(rst),
|
||||
.RSTOPMODE(rst),
|
||||
.RSTP(rst)
|
||||
);
|
||||
end // end generate spartan6
|
||||
endcase
|
||||
endgenerate
|
||||
endmodule
|
||||
@@ -0,0 +1,96 @@
|
||||
// -*- verilog -*-
|
||||
//
|
||||
// USRP - Universal Software Radio Peripheral
|
||||
//
|
||||
// Copyright (C) 2003 Matt Ettus
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
|
||||
// NOTE This only works for N=4, max decim rate of 128
|
||||
// NOTE signal "rate" is EQUAL TO the actual rate, no more -1 BS
|
||||
|
||||
module cic_dec_shifter(rate,signal_in,signal_out);
|
||||
parameter bw = 16;
|
||||
parameter maxbitgain = 28;
|
||||
|
||||
input [7:0] rate;
|
||||
input wire [bw+maxbitgain-1:0] signal_in;
|
||||
output reg [bw-1:0] signal_out;
|
||||
|
||||
function [4:0] bitgain;
|
||||
input [7:0] rate;
|
||||
case(rate)
|
||||
// Exact Cases -- N*log2(rate)
|
||||
8'd1 : bitgain = 0;
|
||||
8'd2 : bitgain = 4;
|
||||
8'd4 : bitgain = 8;
|
||||
8'd8 : bitgain = 12;
|
||||
8'd16 : bitgain = 16;
|
||||
8'd32 : bitgain = 20;
|
||||
8'd64 : bitgain = 24;
|
||||
8'd128 : bitgain = 28;
|
||||
|
||||
// Nearest without overflow -- ceil(N*log2(rate))
|
||||
8'd3 : bitgain = 7;
|
||||
8'd5 : bitgain = 10;
|
||||
8'd6 : bitgain = 11;
|
||||
8'd7 : bitgain = 12;
|
||||
8'd9 : bitgain = 13;
|
||||
8'd10,8'd11 : bitgain = 14;
|
||||
8'd12,8'd13 : bitgain = 15;
|
||||
8'd14,8'd15 : bitgain = 16;
|
||||
8'd17,8'd18,8'd19 : bitgain = 17;
|
||||
8'd20,8'd21,8'd22 : bitgain = 18;
|
||||
8'd23,8'd24,8'd25,8'd26 : bitgain = 19;
|
||||
8'd27,8'd28,8'd29,8'd30,8'd31 : bitgain = 20;
|
||||
8'd33,8'd34,8'd35,8'd36,8'd37,8'd38 : bitgain = 21;
|
||||
8'd39,8'd40,8'd41,8'd42,8'd43,8'd44,8'd45 : bitgain = 22;
|
||||
8'd46,8'd47,8'd48,8'd49,8'd50,8'd51,8'd52,8'd53 : bitgain = 23;
|
||||
8'd54,8'd55,8'd56,8'd57,8'd58,8'd59,8'd60,8'd61,8'd62,8'd63 : bitgain = 24;
|
||||
8'd65,8'd66,8'd67,8'd68,8'd69,8'd70,8'd71,8'd72,8'd73,8'd74,8'd75,8'd76 : bitgain = 25;
|
||||
8'd77,8'd78,8'd79,8'd80,8'd81,8'd82,8'd83,8'd84,8'd85,8'd86,8'd87,8'd88,8'd89,8'd90 : bitgain = 26;
|
||||
8'd91,8'd92,8'd93,8'd94,8'd95,8'd96,8'd97,8'd98,8'd99,8'd100,8'd101,8'd102,8'd103,8'd104,8'd105,8'd106,8'd107 : bitgain = 27;
|
||||
default : bitgain = 28;
|
||||
endcase // case(rate)
|
||||
endfunction // bitgain
|
||||
|
||||
wire [4:0] shift = bitgain(rate);
|
||||
|
||||
// We should be able to do this, but can't ....
|
||||
// assign signal_out = signal_in[shift+bw-1:shift];
|
||||
|
||||
always @*
|
||||
case(shift)
|
||||
5'd0 : signal_out = signal_in[0+bw-1:0];
|
||||
5'd4 : signal_out = signal_in[4+bw-1:4];
|
||||
5'd7 : signal_out = signal_in[7+bw-1:7];
|
||||
5'd8 : signal_out = signal_in[8+bw-1:8];
|
||||
5'd10 : signal_out = signal_in[10+bw-1:10];
|
||||
5'd11 : signal_out = signal_in[11+bw-1:11];
|
||||
5'd12 : signal_out = signal_in[12+bw-1:12];
|
||||
5'd13 : signal_out = signal_in[13+bw-1:13];
|
||||
5'd14 : signal_out = signal_in[14+bw-1:14];
|
||||
5'd15 : signal_out = signal_in[15+bw-1:15];
|
||||
5'd16 : signal_out = signal_in[16+bw-1:16];
|
||||
5'd17 : signal_out = signal_in[17+bw-1:17];
|
||||
5'd18 : signal_out = signal_in[18+bw-1:18];
|
||||
5'd19 : signal_out = signal_in[19+bw-1:19];
|
||||
5'd20 : signal_out = signal_in[20+bw-1:20];
|
||||
5'd21 : signal_out = signal_in[21+bw-1:21];
|
||||
5'd22 : signal_out = signal_in[22+bw-1:22];
|
||||
5'd23 : signal_out = signal_in[23+bw-1:23];
|
||||
5'd24 : signal_out = signal_in[24+bw-1:24];
|
||||
5'd25 : signal_out = signal_in[25+bw-1:25];
|
||||
5'd26 : signal_out = signal_in[26+bw-1:26];
|
||||
5'd27 : signal_out = signal_in[27+bw-1:27];
|
||||
5'd28 : signal_out = signal_in[28+bw-1:28];
|
||||
|
||||
default : signal_out = signal_in[28+bw-1:28];
|
||||
endcase // case(shift)
|
||||
|
||||
endmodule // cic_dec_shifter
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
// -*- verilog -*-
|
||||
//
|
||||
// USRP - Universal Software Radio Peripheral
|
||||
//
|
||||
// Copyright (C) 2003 Matt Ettus
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
|
||||
module cic_decim
|
||||
#(parameter bw = 16, parameter N = 4, parameter log2_of_max_rate = 7)
|
||||
(input clock,
|
||||
input reset,
|
||||
input enable,
|
||||
input [7:0] rate,
|
||||
input strobe_in,
|
||||
input strobe_out,
|
||||
input [bw-1:0] signal_in,
|
||||
output reg [bw-1:0] signal_out);
|
||||
|
||||
localparam maxbitgain = N * log2_of_max_rate;
|
||||
|
||||
wire [bw+maxbitgain-1:0] signal_in_ext;
|
||||
reg [bw+maxbitgain-1:0] integrator [0:N-1];
|
||||
reg [bw+maxbitgain-1:0] differentiator [0:N-1];
|
||||
reg [bw+maxbitgain-1:0] pipeline [0:N-1];
|
||||
reg [bw+maxbitgain-1:0] sampler;
|
||||
|
||||
integer i;
|
||||
|
||||
sign_extend #(bw,bw+maxbitgain)
|
||||
ext_input (.in(signal_in),.out(signal_in_ext));
|
||||
|
||||
always @(posedge clock)
|
||||
if(~enable)
|
||||
for(i=0;i<N;i=i+1)
|
||||
integrator[i] <= 0;
|
||||
else if (strobe_in)
|
||||
begin
|
||||
integrator[0] <= integrator[0] + signal_in_ext;
|
||||
for(i=1;i<N;i=i+1)
|
||||
integrator[i] <= integrator[i] + integrator[i-1];
|
||||
end
|
||||
|
||||
always @(posedge clock)
|
||||
if(~enable)
|
||||
begin
|
||||
sampler <= 0;
|
||||
for(i=0;i<N;i=i+1)
|
||||
begin
|
||||
pipeline[i] <= 0;
|
||||
differentiator[i] <= 0;
|
||||
end
|
||||
end
|
||||
else if (strobe_out)
|
||||
begin
|
||||
sampler <= integrator[N-1];
|
||||
differentiator[0] <= sampler;
|
||||
pipeline[0] <= sampler - differentiator[0];
|
||||
for(i=1;i<N;i=i+1)
|
||||
begin
|
||||
differentiator[i] <= pipeline[i-1];
|
||||
pipeline[i] <= pipeline[i-1] - differentiator[i];
|
||||
end
|
||||
end // if (enable && strobe_out)
|
||||
|
||||
wire [bw-1:0] signal_out_unreg;
|
||||
|
||||
cic_dec_shifter #(bw)
|
||||
cic_dec_shifter(rate,pipeline[N-1],signal_out_unreg);
|
||||
|
||||
always @(posedge clock)
|
||||
signal_out <= signal_out_unreg;
|
||||
|
||||
endmodule // cic_decim
|
||||
@@ -0,0 +1,90 @@
|
||||
// -*- verilog -*-
|
||||
//
|
||||
// USRP - Universal Software Radio Peripheral
|
||||
//
|
||||
// Copyright (C) 2003 Matt Ettus
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
|
||||
// NOTE This only works for N=4, max interp rate of 128
|
||||
// NOTE signal "rate" is EQUAL TO the actual rate (no more -1 BS)
|
||||
|
||||
module cic_int_shifter(rate,signal_in,signal_out);
|
||||
parameter bw = 16;
|
||||
parameter maxbitgain = 21;
|
||||
|
||||
input [7:0] rate;
|
||||
input wire [bw+maxbitgain-1:0] signal_in;
|
||||
output reg [bw-1:0] signal_out;
|
||||
|
||||
function [4:0] bitgain;
|
||||
input [7:0] rate;
|
||||
case(rate)
|
||||
// Exact Cases
|
||||
8'd1 : bitgain = 0;
|
||||
8'd2 : bitgain = 3;
|
||||
8'd4 : bitgain = 6;
|
||||
8'd8 : bitgain = 9;
|
||||
8'd16 : bitgain = 12;
|
||||
8'd32 : bitgain = 15;
|
||||
8'd64 : bitgain = 18;
|
||||
8'd128 : bitgain = 21;
|
||||
|
||||
// Nearest without overflow
|
||||
8'd3 : bitgain = 5;
|
||||
8'd5 : bitgain = 7;
|
||||
8'd6 : bitgain = 8;
|
||||
8'd7 : bitgain = 9;
|
||||
8'd9,8'd10 : bitgain = 10;
|
||||
8'd11,8'd12 : bitgain = 11;
|
||||
8'd13,8'd14,8'd15 : bitgain = 12;
|
||||
8'd17,8'd18,8'd19,8'd20 : bitgain = 13;
|
||||
8'd21,8'd22,8'd23,8'd24,8'd25 : bitgain = 14;
|
||||
8'd26,8'd27,8'd28,8'd29,8'd30,8'd31 : bitgain = 15;
|
||||
8'd33,8'd34,8'd35,8'd36,8'd37,8'd38,8'd39,8'd40 : bitgain = 16;
|
||||
8'd41,8'd42,8'd43,8'd44,8'd45,8'd46,8'd47,8'd48,8'd49,8'd50 : bitgain = 17;
|
||||
8'd51,8'd52,8'd53,8'd54,8'd55,8'd56,8'd57,8'd58,8'd59,8'd60,8'd61,8'd62,8'd63 : bitgain = 18;
|
||||
8'd65,8'd66,8'd67,8'd68,8'd69,8'd70,8'd71,8'd72,8'd73,8'd74,8'd75,8'd76,8'd77,8'd78,8'd79,8'd80 : bitgain = 19;
|
||||
8'd81,8'd82,8'd83,8'd84,8'd85,8'd86,8'd87,8'd88,8'd89,8'd90,8'd91,8'd92,8'd93,8'd94,8'd95,8'd96,8'd97,8'd98,8'd99,8'd100,8'd101 : bitgain = 20;
|
||||
|
||||
default : bitgain = 21;
|
||||
endcase // case(rate)
|
||||
endfunction // bitgain
|
||||
|
||||
wire [4:0] shift = bitgain(rate);
|
||||
|
||||
// We should be able to do this, but can't ....
|
||||
// assign signal_out = signal_in[shift+bw-1:shift];
|
||||
|
||||
always @*
|
||||
case(shift)
|
||||
5'd0 : signal_out = signal_in[0+bw-1:0];
|
||||
5'd3 : signal_out = signal_in[3+bw-1:3];
|
||||
5'd6 : signal_out = signal_in[6+bw-1:6];
|
||||
5'd9 : signal_out = signal_in[9+bw-1:9];
|
||||
5'd12 : signal_out = signal_in[12+bw-1:12];
|
||||
5'd15 : signal_out = signal_in[15+bw-1:15];
|
||||
5'd18 : signal_out = signal_in[18+bw-1:18];
|
||||
5'd21 : signal_out = signal_in[21+bw-1:21];
|
||||
|
||||
5'd5 : signal_out = signal_in[5+bw-1:5];
|
||||
5'd7 : signal_out = signal_in[7+bw-1:7];
|
||||
5'd8 : signal_out = signal_in[8+bw-1:8];
|
||||
5'd10 : signal_out = signal_in[10+bw-1:10];
|
||||
5'd11 : signal_out = signal_in[11+bw-1:11];
|
||||
5'd13 : signal_out = signal_in[13+bw-1:13];
|
||||
5'd14 : signal_out = signal_in[14+bw-1:14];
|
||||
5'd16 : signal_out = signal_in[16+bw-1:16];
|
||||
5'd17 : signal_out = signal_in[17+bw-1:17];
|
||||
5'd19 : signal_out = signal_in[19+bw-1:19];
|
||||
5'd20 : signal_out = signal_in[20+bw-1:20];
|
||||
|
||||
default : signal_out = signal_in[21+bw-1:21];
|
||||
endcase // case(shift)
|
||||
|
||||
endmodule // cic_int_shifter
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user